07070100000000000081A4000000000000000000000001682255050000456D000000000000000000000000000000000000001F00000000open-vm-tools-12.5.2/README.md    # General
## What is the open-vm-tools project?
open-vm-tools is a set of services and modules that enable several features in VMware products for better management of, and seamless user interactions with, guests. It includes kernel modules for enhancing the performance of virtual machines running Linux or other VMware supported Unix like guest operating systems. 
 
open-vm-tools enables the following features in VMware products:

- Graceful execution of power operations (reboot and shutdown) in the guest.
- Execution of built-in or user configured scripts in the guest during various power operations.
- Running programs, commands and file system operations in the guest to enhance guest automation.
- Authentication for guest operations.
- Generation of heartbeat from guest to host for vSphere HA solution to determine guest's availabilty.
- Clock synchronization between guest and host.
- Quiescing guest file systems to allow host to capture file-system-consistent guest snapshot.
- Execution of pre-freeze and post-thaw scripts while quiescing guest file systems.
- Customization of the guest immediately after power on.
- Periodic collection of network, disk, and memory usage information from the guest.
- Resizing the graphical desktop screen of the guest.
- Shared Folders operations between host and guest file systems on VMware Workstation and VMware Fusion.
- Copying and pasting text, graphics, and files between guest and host or client desktops.
- Dragging and dropping files between guest and host UI.
- Periodic collection of running applications, services, and containers in the guest.
- Accessing content from GuestStore.
- Publishing data to Guest Data Publisher.
- Managing Salt-Minion desired state specified in a guest variable.

## Can you provide more details on the actual code being released?
The following components have been released as open source software:
- Linux, Solaris and FreeBSD drivers for various devices and file system access.
- The PowerOps plugin to perform graceful power operation and run power scripts.
- The VIX plugin to run programs and commands, and perform file system operations in guest.
- The GuestInfo plugin to periodically collect various statistics from guest.
- The TimeSync plugin to perform time synchronization.
- The dndcp plugin to support drag and drop, and text and file copy/paste operations.
- The ResolutionSet plugin to adjust guest screen resolutions automatically based on window sizes.
- The vmbackup plugin to support quiesced snapshot operation.
- The GuestStore plugin to support GuestStore operation.
- The gdp plugin to support guest data publishing operation.
- The AppInfo plugin to periodically collect application information.
- The ServiceDiscovery plugin to periodically collect service information.
- The ContainerInfo plugin to periodically collect container information.
- The ComponentMgr plugin to handle desired state operations.
- The guest authentication service.
- The toolbox command to perform disk wiping and shrinking, manage power scripts, and time synchronization.
- The guest SDK libraries to provide information about virtual machine to guest.
- Client and server for shared folders support.
- Multiple monitor support.
- Other utilities.
 
## Is open-vm-tools available with Linux distributions?
Yes. open-vm-tools packages for user space components are available with new versions of major Linux distributions, and are installed as part of the OS installation in several cases. Please refer to VMware KB article http://kb.vmware.com/kb/2073803 for details. All leading Linux vendors support open-vm-tools and bundle it with their products. For information about OS compatibility for open-vm-tools, see the 
VMware Compatibility Guide at http://www.vmware.com/resources/compatibility
Automatic installation of open-vm-tools along with the OS installation eliminates the need to separately install open-vm-tools in guests. If open-vm-tools is not installed automatically, you may be able to manually install it from the guest OS vendor's public repository. Installing open-vm-tools from the Linux vendor's repository reduces virtual machine downtime because future updates to open-vm-tools are included with the OS maintenance patches and updates.
**NOTE**: Most of the Linux distributions ship two or more open-vm-tools packages. "open-vm-tools" is the core package without any dependencies on X libraries and "open-vm-tools-desktop" is an additional package with dependencies on "open-vm-tools" core package and X libraries. The "open-vm-tools-sdmp" package contains a plugin for Service Discovery. There may be additional packages, please refer to the documentation of the OS vendor. Note that the open-vm-tools packages available with Linux distributions do not include Linux drivers because Linux drivers are available as part of Linux kernel itself. Linux kernel versions 3.10 and later include all of the Linux drivers present in open-vm-tools except the vmhgfs driver. The vmhgfs driver was required for enabling shared folders feature, but is superseded by vmhgfs-fuse which does not require a kernel driver.

## Will there be continued support for VMware Tools and OSP? 
VMware Tools will continue to be available under a commercial license. It is recommended that open-vm-tools be used for the Linux distributions where open-vm-tools is available. VMware will not provide OSPs for operating systems where open-vm-tools is available.

## How does this benefit other open source projects?
Under the terms of the GPL, open source community members are able to use the open-vm-tools code to develop their own applications, extend it, and contribute to the community. They can also incorporate some or all of the code into their projects, provided they comply with the terms of the GPL.

# License Related
## What license is the code being released under?
The code is being released under GPL v2 and GPL v2 compatible licenses. To be more specific, the Linux kernel modules are being released under the GPL v2, while almost all of the user level components are being released under the LGPL v2.1. The SVGA and mouse drivers have been available under the X11 license for quite some time. There are certain third party components released under BSD style licenses, to which VMware has in some cases contributed, and will continue to distribute with open-vm-tools.
 
## Why did you choose these licenses?
We chose the GPL v2 for the kernel components to be consistent with the Linux kernel's license. We chose the LGPL v2.1 for the user level components because some of the code is implemented as shared libraries and we do not wish to restrict proprietary code from linking against those libraries. For consistency, we decided to license the rest of the userlevel code under the LGPL v2.1 as well.

## What are the obligations that the license(s) impose?
Each of these licenses have different obligations.
For questions about the GPL, LGPL licenses, the Free Software Foundation's GPL FAQ page provides lots of useful information. 
For questions about the other licenses like the X11, BSD licenses, the Open Source Initiative has numerous useful resources including mailing lists. 
The Software Freedom Law Center provides legal expertise and consulting for free and open source software (FOSS) developers.

## Can I use all or part of this code in my proprietary software? Do I have to release the source code if I do?
Different open source licenses have different requirements regarding the release of source code. Since the code is being released under various open source licenses, you will need to comply with the terms of the corresponding licenses.

## Am I required to contribute back any changes I make to the code?
No, you aren't required to contribute any changes that you make back to the open-vm-tools project. However, we encourage you to do so.

## Can I use all or part of this code in another open source package?
Yes, as long as you comply with the appropriate license(s).
 
## Can I package this for my favorite operating system?
Yes! Please do. 

## Will the commercial version (VMware Tools) differ from the open source version (open-vm-tools)? If so, how?
Our goal is to work towards making the open source version as close to the commercial version as possible. However, we do currently make use of certain components licensed from third parties as well as components from other VMware products which are only available in binary form.

## If I use the code from the open-vm-tools project in my project/product, can I call my project/product VMware Tools?
No, since your project/product is not a VMware project/product.

# Building open-vm-tools
## How do I build open-vm-tools?
open-vm-tools uses the GNU Automake tool for generating Makefiles to build all sources. More information about Automake can be found here: http://www.gnu.org/software/automake/
## Project build information:
The following steps will work on most recent Linux distributions:
```
autoreconf -i
./configure
make
sudo make install
sudo ldconfig
```

### Service Discovery (sdmp) plugin
To build the optional sdmp (Service Discovery) plugin use the `--enable-servicediscovery` option to invoke the configure script:
```
./configure --enable-servicediscovery
```

### The open-vm-tools 12.0.0 release introduces an optional setup script and two plugins (one optional)

 * Salt Minion Setup
 * Component Manager plugin
 * ContainerInfo plugin (optional)

### Salt Minion Setup
The Salt support on Linux consists of a single bash script to setup Salt Minion on VMware virtual machines.  The script requires the "curl" and "awk" commands to be available on the system.

Linux providers supplying open-vm-tools packages are recommended to provide Salt Minion support in a separate optional package - "open-vm-tools-salt-minion".

To include the Salt Minion Setup in the open-vm-tools build use the `--enable-salt-minion` option when invoking the configure script.
```
./configure --enable-salt-minion
```

### Component Manager (componentMgr) plugin
The component Manager manages a preconfigured set of components available from VMware that can be made available on the Linux guest.  Currently the only component that can be managed is the Salt Minion Setup.

### ContainerInfo (containerInfo) plugin
The optional containerInfo plugin retrieves a list of the containers running on a Linux guest and publishes the list to the guest variable "**guestinfo.vmtools.containerinfo**" in JSON format.  The containerInfo plugin communicates with the containerd daemon using gRPC to retrieve the desired information.  For containers that are managed by Docker, the plugin uses libcurl to communicate with the Docker daemon and get the names of the containers.

Since this plugin requires additional build and runtime dependencies, Linux vendors are recommended to release it in a separate, optional package - "open-vm-tools-containerinfo".  This avoids unnecessary dependencies for customers not using the feature.

#### Canonical, Debian, Ubuntu Linux
| Build Dependencies | Runtime |
|:------------------------:|:----------------:|
| `libcurl4-openssl-dev` | `curl` |
| `protobuf-compiler` | `protobuf` |
| `libprotobuf-dev` | `grpc++` |
| `protobuf-compiler-grpc` |
| `libgrpc++-dev` |
| `golang-github-containerd-containerd-dev` |
| `golang-github-gogo-protobuf-dev` |

#### Fedora, Red Hat Enterprise Linux, ...
| Build Dependencies | Runtime |
|:------------------------:|:----------------:|
| `libcurl-devel` | `curl` |
| `protobuf-compiler` | `protobuf` |
| `protobuf-devel` | `grpc-cpp` |
| `grpc-plugins` |
| `grpc-devel` |
| `containerd-devel` |


#### Configuring the build for the ContainerInfo plugin
The configure script defaults to building the ContainerInfo when all the needed dependencies are available.  ContainerInfo will not be built if there are missing dependencies.  Invoke the configure script with `--enable-containerinfo=no` to explicitly inhibit building the plugin.
```
./configure --enable-containerinfo=no
```
If the configure script is given the option `--enable-containerinfo=yes` and any necessary dependency is not available, the configure script will terminate with an error.
```
./configure --enable-containerinfo=yes
```

## Getting configure options and help
If you are looking for help or additional settings for the building of this project, the following configure command will display a list of help options:
```
./configure --help
```
When using configure in the steps above it is only necessary to call ./configure once unless there was a problem after the first invocation.

# Getting Involved
## How can I get involved today?
You can get involved today in several different ways:
- Start using open-vm-tools today and give us feedback.
- Suggest feature enhancements.
- Identify and submit bugs under issues section: https://github.com/vmware/open-vm-tools/issues
- Start porting the code to other operating systems.   Here is the list of operating systems with open-vm-tools:

  * Red Hat Enterprise Linux 7.0 and later releases
  * SUSE Linux Enterprise 12 and later releases
  * Ubuntu 14.04 and later releases
  * CentOS 7 and later releases
  * Debian 7.x and later releases
  * Oracle Linux 7 and later 
  * Fedora 19 and later releases
  * openSUSE 11.x and later releases
  * Flatcar Container Linux, all releases
  * Rocky 8 and later releases
  * AlmaLinux OS 8 and later releases
 
## Will external developers be allowed to become committers to the project?
Yes. Initially, VMware engineers will be the only committers. As we roll out our development infrastructure, we will be looking to add external committers to the project as well.

## How can I submit code changes like bug fixes, patches, new features to the project?
Initially, you can submit bug fixes, patches and new features to the project development mailing list as attachments to emails or bug reports. To contribute source code, you will need to fill out a contribution agreement form as part of the submission process. We will have more details on this process shortly.

## What is the governance model for managing this as an open source project?
The feature roadmap and schedules for the open-vm-tools project will continue to be defined by VMware. Initially, VMware engineers will be the only approved committers. We will review incoming submissions for suitability for merging into the project. We will be looking to add community committers to the project based on their demonstrated contributions to the project. Finally, we also plan to set up a process for enhancement proposals, establishing sub-projects and so on.

## Will you ship code that I contribute with VMware products? If so, will I get credit for my contributions?
Contributions that are accepted into the open-vm-tools project's main source tree will likely be a part of VMware Tools. We also recognize the value of attribution and value your contributions. Consequently, we will acknowledge contributions from the community that are distributed with VMware's products.

## Do I need to sign something before making a contribution?
Yes. We have a standard contribution agreement that covers all contributions made to the project. It gives VMware and you joint copyright interests in the code you are contributing. The agreement also gives VMware flexibility with licensing and also helps avoid any copyright/licensing related issues that may arise in the future. In order for us to include your contribution in our source tree, we ask that you send us a signed copy of the agreement. You can do this in one of two ways:
Fax to +1.650.427.5003, Attn: Product & Technology Law Group
Scan and email it to oss-queries_at_vmware.com
Agreement: http://open-vm-tools.sourceforge.net/files/vca.pdf

## My version of Linux is not recognized.  How do I add my Linux name to the known list?

The open-vm-tools source contains a table mapping the guest distro name to the officially recognized short name.  __Please do not submit pull requests altering this table and associated code.__  Any changes here must be accompanied by additional changes in the VMware host.  Values that are not recognized by the VMware host will be ignored. 


Use the appropriate generic Linux designation when configuring a VM for your Linux version.  The selection available will vary by virtual hardware version being used.
- Other 5.x or later Linux (64-bit)
- Other 5.x or later Linux (32-bit)
- Other 4.x Linux (64-bit)
- Other 4.x Linux (32-bit)
- Other 3.x Linux (64-bit)
- Other 3.x Linux (32-bit)
- Other Linux (64-bit)
- Other Linux (32-bit)

# Compatibilty

## What Operating Systems are supported for customization?
The [Guest OS Customization Support Matrix](http://partnerweb.vmware.com/programs/guestOS/guest-os-customization-matrix.pdf) provides details about the guest operating systems supported for customization.

## Which versions of open-vm-tools are compatible with other VMware products?

The [VMware Product Interoperability Matrix](http://partnerweb.vmware.com/comp_guide2/sim/interop_matrix.php) provides details about the compatibility of different versions of VMware Tools (includes open-vm-tools) and other VMware Products.

# Internationalization
## Which languages are supported?

open-vm-tools supports the following languages:
- English
- French
- German
- Spanish
- Italian
- Japanese
- Korean
- Simplified Chinese
- Traditional Chinese

# Other
## Mailing Lists
Please send an email to one of these mailing lists based on the nature of your question.
- Development related questions : open-vm-tools-devel@lists.sourceforge.net
- Miscellaneous questions: open-vm-tools-discuss@lists.sourceforge.net
- General project announcements: open-vm-tools-announce@lists.sourceforge.net

   07070100000001000081A4000000000000000000000001682255050000105A000000000000000000000000000000000000002500000000open-vm-tools-12.5.2/ReleaseNotes.md  #                      open-vm-tools 12.5.2 Release Notes

Updated on: 12 May 2025

open-vm-tools | 12 MAY 2025 | Build 24697584

Check back for additions and updates to these release notes.

## What's in the Release Notes

The release notes cover the following topics:

* [What's New](#whatsnew) 
* [Internationalization](#i18n) 
* [Product Support Notice](#suppnote)
* [Guest Operating System Customization Support](#guestop) 
* [Interoperability Matrix](#interop) 
* [Resolved Issues](#resolvedissues) 
* [Known Issues](#knownissues)

## <a id="whatsnew" name="whatsnew"></a>What's New


*   This release resolves [CVE-2025-22247](https://www.cve.org/CVERecord?id=CVE-2025-22247). For more information on this vulnerability and its impact on Broadcom products, see [VMSA-2025-0007](https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/25683)

    A patch to address CVE-2025-22247 on earlier open-vm-tools releases is provided to the Linux community at [CVE-2025-22247.patch](https://github.com/vmware/open-vm-tools/tree/CVE-2025-22247.patch).

*   A complete list of the granular changes in the open-vm-tools 12.5.2 release is available at:

    [open-vm-tools ChangeLog](https://github.com/vmware/open-vm-tools/blob/stable-12.5.2/open-vm-tools/ChangeLog)

## <a id="i18n" name="i18n"></a>Internationalization

open-vm-tools 12.5.2 is available in the following languages:

* English
* French
* German
* Spanish
* Italian
* Japanese
* Korean
* Simplified Chinese
* Traditional Chinese

## <a id="suppnote" name="suppnote"></a>Product Support Notice

Beginning with the next major release, we will be reducing the number of supported localization languages.  The three supported languages will be:
  * Japanese
  * Spanish
  * French

The following languages will no longer be supported:
  * Italian
  * German
  * Brazilian Portuguese
  * Traditional Chinese
  * Korean
  * Simplified Chinese

Impact:
  * Users who have been using the deprecated languages will no longer receive updates or support in these languages.
  * All user interfaces, message catalogs, help documentation, and customer support will be available only in English or in the three supported languages mentioned above.

## <a id="guestop" name="guestop"></a>Guest Operating System Customization Support

The [Guest OS Customization Support Matrix](http://partnerweb.vmware.com/programs/guestOS/guest-os-customization-matrix.pdf) provides details about the guest operating systems supported for customization.


## <a id="interop" name="interop"></a>Interoperability Matrix

The [VMware Product Interoperability Matrix](https://interopmatrix.broadcom.com/Interoperability) provides details about the compatibility of current and earlier versions of VMware Products. 

## <a id="resolvedissues" name ="resolvedissues"></a> Resolved Issues

*   **This release resolves CVE-2025-22247.**

    * For more information on this vulnerability and its impact on Broadcom products, see [VMSA-2025-0007](https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/25683)

    * A patch to address CVE-2025-22247 on earlier open-vm-tools releases is provided to the Linux community at [CVE-2025-22247.patch](https://github.com/vmware/open-vm-tools/tree/CVE-2025-22247.patch).

## <a id="knownissues" name="knownissues"></a>Known Issues

*   **Shared Folders mount is unavailable on Linux VM.**

    If the **Shared Folders** feature is enabled on a Linux VM while it is powered off, the shared folders mount is not available on restart.

    Note: This issue is applicable to open-vm-tools running on VMware Workstation and VMware Fusion.

    Workaround:

    If the VM is powered on, disable and enable the **Shared Folders** feature from the interface. For resolving the issue permanently, edit **/etc/fstab** and add an entry to mount the Shared Folders automatically on boot.  For example, add the line:

    <tt>vmhgfs-fuse   /mnt/hgfs    fuse    defaults,allow_other    0    0</tt>

    For more information on how to configure VMware Tools Shared Folders, see [KB 60262](https://kb.vmware.com/s/article/60262)
  07070100000002000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002300000000open-vm-tools-12.5.2/open-vm-tools    07070100000003000081A40000000000000000000000016822550500000428000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/.gitignore *~
*.swp
*.o
*.ko
 
# autoconf generated files
*.cache
.deps
.libs
INSTALL
Makefile
Makefile.in
aclocal.m4
config
!common-agent/etc/config
config.log
config.status
configure
libtool
m4
 
# project generated files
*.la
*.lo
checkvm/vmware-checkvm
docs/api/build
docs/api/doxygen.parsed.conf
docs/api/warnings.log
hgfsclient/vmware-hgfsclient
hgfsmounter/mount.vmhgfs
lib/guestRpc/nicinfo.h
lib/guestRpc/nicinfo_xdr.c
lib/include/guestrpc
libguestlib/guestlibIoctl.h
libguestlib/guestlibIoctl_xdr.c
libguestlib/guestlibV3.h
libguestlib/guestlibV3_xdr.c
libguestlib/vmguestlib.pc
rpctool/vmware-rpctool
scripts/build/rpcgen_wrapper.sh
scripts/poweroff-vm-default
scripts/poweron-vm-default
scripts/resume-vm-default
scripts/suspend-vm-default
services/plugins/vmbackup/vmBackupSignals.c
services/plugins/vmbackup/vmBackupSignals.h
services/vmtoolsd/svcSignals.c
services/vmtoolsd/svcSignals.h
services/vmtoolsd/vmtoolsd
toolbox/vmware-toolbox-cmd
vmware-user-suid-wrapper/vmware-user-suid-wrapper
vmware-user-suid-wrapper/vmware-user.desktop
xferlogs/vmware-xferlogs
07070100000004000081A400000000000000000000000168225505000011A3000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/AUTHORS    The VMware Guest Components Team

Contributors to open-vm-tools:

Steve Wills     Correct __IS_FREEBSD__ macro in vm_basic_defs.h (clang)
                - https://github.com/vmware/open-vm-tools/pull/136

Bernd Zeimetz   Fix gcc6 build issues in linuxDeployment.c file.
                - https://github.com/vmware/open-vm-tools/pull/107

Josh Paetzel    Add support for 64-bit inodes in FreeBSD 12
                - https://github.com/vmware/open-vm-tools/pull/190

Sebastian Parschauer    Add support to properly report SLES12-SAP
                - https://github.com/vmware/open-vm-tools/pull/123

Andrew Stormont Fix finding C++ compiler for cross-compiling
                - https://github.com/vmware/open-vm-tools/pull/206

Josh Paetzel    Fix compilation error in clang 6.0
                - https://github.com/vmware/open-vm-tools/pull/221

Mike Latimer    Restrict udev rules to disk devices only
                - https://github.com/vmware/open-vm-tools/pull/216

Thomas Mueller  Ignore ENXIO errors with SyncDriver
                - https://github.com/vmware/open-vm-tools/pull/218

Germán M. Bravo FreeBSD: Improper use of sysconf() for getpwent buffer size
                leads to vmtoolsd crash.
                - https://github.com/vmware/open-vm-tools/pull/238

Ed Schouten     Use standard SYSCTL_ADD_OID() macro to access the
                sysctl_add_oid() function across supported FreeBSD releases.
                - https://github.com/vmware/open-vm-tools/pull/125

Steve Wills     Fix vmmemctl.ko driver build for supported FreeBSD releases.
                - https://github.com/vmware/open-vm-tools/pull/140

John Eismeier   Propose fix some spelling.
                - https://github.com/vmware/open-vm-tools/pull/264

Josh Paetzel    Additional changes to vmmemctl.ko for FreeBSD 12.0 API changes.
                - https://github.com/vmware/open-vm-tools/pull/286

[Code]Ai        Highlighted a potential NULL pointer dereference and four
                pieces of dead code.
                - https://github.com/vmware/open-vm-tools/pull/247

Haruki Tsurumoto        Fix Asianux identification
                - https://github.com/vmware/open-vm-tools/pull/325

MilhouseVH      stop systemd-243 udev complaints
                - https://github.com/vmware/open-vm-tools/pull/371

Josh Paetzel    Changes to vmmemctl.ko and vmblock.ko for FreeBSD 13.0 API changes.
                - https://github.com/vmware/open-vm-tools/pull/398

Josh Paetzel    FreeBSD has removed some vnops flags that have never been used.
                - https://github.com/vmware/open-vm-tools/pull/403

Alexey Shabalin Add recognition of ALT Linux distributions
                - https://github.com/vmware/open-vm-tools/pull/431

Thom Leggett    Propagate new gdk-pixbuf-xlib include location
                - https://github.com/vmware/open-vm-tools/pull/438

Alexey Shabalin Adding vmtools library dependency to deploypkg library.
                - https://github.com/vmware/open-vm-tools/pull/432

Christian Ehrhardt   Build: fix propagation of libtirpc flags
                - https://github.com/vmware/open-vm-tools/pull/469

Vincent Milum Jr  Adding FreeBSD on ARM64 support to open-vm-tools.
                - https://github.com/vmware/open-vm-tools/pull/474

Miroslav Rezanina   Fix issues using GCC 11 with gtk >= 3.20 and glib >=2.66.3
                - https://github.com/vmware/open-vm-tools/pull/505

Marco Trevisan  Update open-vm-tools to build with either Fuse 3 or Fuse 2
                - https://github.com/vmware/open-vm-tools/pull/544

Bartosz Brachaczek Make HgfsConvertFromNtTimeNsec aware of 64-bit time_t on i386
                - https://github.com/vmware/open-vm-tools/pull/387

Bernd Zeimetz   Fix building containerinfo plugin on i386
                - https://github.com/vmware/open-vm-tools/pull/588

Dirk Mueller    Detect the proto files for containerd grpc client on SUSE like systems
                - https://github.com/vmware/open-vm-tools/pull/626

Jan Engelhardt  Fix build problems with grpc (at least) 1.54
                - https://github.com/vmware/open-vm-tools/pull/664

Yun Zheng Hu    Power Ops: Attempt to execute file path only
                - https://github.com/vmware/open-vm-tools/pull/689

Joseph Allen    Updated NetworkManager calls in suspend/resume scripts
                - https://github.com/vmware/open-vm-tools/pull/699

Brennan Kinney  Revise settings for vmware-user.desktop
                - https://github.com/vmware/open-vm-tools/pull/668

 07070100000005000081A40000000000000000000000016822550500006739000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000006000081A400000000000000000000000168225505000771F7000000000000000000000000000000000000002D00000000open-vm-tools-12.5.2/open-vm-tools/ChangeLog  commit 4ba99fabfecd7a54d3ebdb9d144f9ed6ec7faf22
Author: John Wolfe <john.wolfe@broadcom.com>
Date:   Mon May 12 13:00:43 2025 -0700

    Update the ReleaseNotes.md for the 12.5.2 open-vm-tools release.

commit d466ae89716856b2a4295ce622f1887a25575f25
Author: John Wolfe <john.wolfe@broadcom.com>
Date:   Sun May 11 09:17:41 2025 -0700

    Prepare for the open-vm-tools 12.5.2 release.
    
    - Update the tools version in the configure.ac.
    - Update the build numbers in the buldNumber.h.

commit f6e10ad22796353a8ed3bed876cdc41d7acc2fc0
Author: John Wolfe <john.wolfe@broadcom.com>
Date:   Fri May 9 11:51:44 2025 -0700

    Set the open-vm-tools product version for a 12.5.2 patch release

commit 6331ea0150b98316b3f41b4cdcff52ae9fc7d791
Author: John Wolfe <john.wolfe@broadcom.com>
Date:   Mon May 5 15:58:03 2025 -0700

    Validate user names and file paths
    
    Prevent usage of illegal characters in user names and file paths.
    Also, disallow unexpected symlinks in file paths.
    
    This patch contains changes to common source files not applicable
    to open-vm-tools.
    
    All files being updated should be consider to have the copyright to
    be updated to:
    
     * Copyright (c) XXXX-2025 Broadcom. All Rights Reserved.
     * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
    
    The 2025 Broadcom copyright information update is not part of this
    patch set to allow the patch to be easily applied to previous
    open-vm-tools source releases.

commit f2ca37ef3510543172657b82493d1eceefa9a134
Author: Kruti <kpendharkar@vmware.com>
Date:   Thu Oct 10 08:05:07 2024 -0700

    Update ChangeLog with the open-vm-tools 12.5.0 release marker.

commit 05afe0ae703d6027325059876528fe3b04fdf386
Author: Kruti <kpendharkar@vmware.com>
Date:   Thu Oct 10 06:12:07 2024 -0700

    =================================================
      open-vm-tools 12.5.0 released at this point.
    =================================================
    
    Update of the ChangeLog with the final changes in preparation for
    the open-vm-tools 12.5.0 release.

commit ef2095ffda17a766a65bf39d61f4cdb9c735fa62
Author: Kruti <kpendharkar@vmware.com>
Date:   Wed Oct 9 20:49:01 2024 -0700

    Update the ReleaseNotes.md for the 12.5.0 open-vm-tools release.

commit 96094bb1a59c67af99d31d5acfdc731f14109e47
Author: Kruti <kpendharkar@vmware.com>
Date:   Wed Oct 9 19:47:45 2024 -0700

    Prepare for the open-vm-tools 12.5.0 release.
    - Update the tools version in the configure.ac.
    - Update the build numbers in the buldNumber.h.

commit 412a4b1db38627062a8ebb955b0d483edee0a1ff
Author: Kruti <kpendharkar@vmware.com>
Date:   Thu Oct 3 23:34:31 2024 -0700

    Update ChangeLog with the granular push of Oct 03, 2024.
    - plus copyright update
    - plus ChangeLog update of Sep 24, 2024.

commit e54a2d61657e3c4c0fd4299cc5c830b4263e27bd
Author: Kruti <kpendharkar@vmware.com>
Date:   Thu Oct 3 23:17:34 2024 -0700

    Correct missed 2024 copyright update.

commit a2ff5f28720eca493c5369448b48d1ad595e368b
Author: Kruti <kpendharkar@vmware.com>
Date:   Thu Oct 3 22:57:50 2024 -0700

    open-vm-tools l10n message updates

commit a240f6a1d75c4fbacb1e9364ff48dfce304a23dc
Author: Kruti <kpendharkar@vmware.com>
Date:   Thu Oct 3 22:57:50 2024 -0700

    Ignore deprecated warning for LIBXML2 APIs xmlFileXXX.
    
    CVE-2024-40896 has been fixed in the following libxml2 releases.
      - 2.13.3
      - 2.12.9
      - 2.11.9
    
    The libxml2 version 2.13.0 has deprecated the xmlFileMatch, UserXmlFileOpen,
    xmlFileRead, xmlFileClose callback functions. This change suppresses a
    "deprecated-declaration" warning when these functions are invoked.

commit a167dff31e77d9599454667220b23dac304fed46
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 24 14:31:17 2024 -0700

    ====================================================================
    The "stable-12.5.x" branch was created from the "devel" branch here.
    ====================================================================
    
    Update the ChangeLog with the ChangeLog update of Aug 27, 2024.

commit 764f00f3c3393e8043ba87c2b63d1df670755c95
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:31:46 2024 -0700

    Update ChangeLog with the granular push of Aug 27, 2024.
    - plus Copyright pattern update of file tools.conf
    - plus README.md update of Aug 15, 2024.
    - plus ChangeLog update of July 15, 2024.

commit 25fe7db3739a021103b4dca64255531be02602cd
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:27:38 2024 -0700

    Update copyright pattern for tools.conf.

commit 21a843e74da24401049f40efeb313a858de53b07
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit 2197581c0da08a4a574703cf56eca39c90ad1a40
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 26ed84ded19e61801b752f3a606187ea2745e57a
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 5413628f892125da221bd180bfb861b3e773159c
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Annotate the os-release guest identification function HostinfoOsRelease()
    
    The HostinfoOsRelease() function examines the os-release standard file(s) to
    collect distro identification data of interest. This is open-source and users
    can legally modify the code changing the standard file priority order or
    accessing other files. Any such change breaks compliance with the os-release
    standard and may cause confusion in the field.
    The function header has been updated to remind users of this issue.

commit 86b308ab9eb8e4c3e154b30861cb66802c9e4624
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit c401bc0af27dd6928970cad4f6af5f22d48dc7a7
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 97a7efcc67eb37628181666a642a439cf4bc0bab
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common source file not applicable to open-vm-tools.

commit d9a88f25df7cab16374c7ed0c1bc2b4640df22c8
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 41d772c7ea21442f447872f52ec2fb7bd92d98b3
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 0a0b23a5080bf6ccb58c69b9b214e0c25c628173
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit 6e7206dbd9be10e1c0853a4ac8b9982e3d307a7e
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:11:00 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 159107b8027f122f8502adafbbc8ffc56b4ff7d7
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:10:59 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit bcc1be6f36d8a3d69c8a86f533d6deb27e6f2fda
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:10:59 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 649d27de9347ee507e3e863b43e861b48e5f0506
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:10:59 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 4bdc04df610cebde943967dc2042f90b6597d2ad
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:10:59 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit a5f761ab56eb7afbb885e4ec57062657cfe0f171
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue Aug 27 02:10:59 2024 -0700

    Change to common source file not applicable to open-vm-tools.

commit 582763347e3384a1fa1abb77a6b9c20d7c353753
Author: Ravindra Kumar <ravindrakumar@vmware.com>
Date:   Thu Aug 15 18:51:54 2024 -0700

    Update README.md
    
    Updated open-vm-tools functionality and plugin details

commit 88c4194d2c6fb1804159cd36dd7d68e984afc495
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 03:00:20 2024 -0700

    Update ChangeLog with the granular push of July 15, 2024.
    - plus ChangeLog update of May 28, 2024.

commit 567b94a40a5fe8f478dfe37b3b87fc9106028e45
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:39 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 8ab40432f52747946fe007bf2bd2afc60875ee0a
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 4a8e42d35fcafb5d3ce77e5ea0e42887307172da
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 0ae5e008d7f18600d424e3335da54ee3e09f77bd
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common source file not applicable to open-vm-tools at this time.

commit 0e8e439e9fa4f1b196fd40251db0e948cde52ce5
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit c22f4ebfe44a1798c2b5c445ca49cb96d2a2385f
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 95ba5d3d67b94f9f30aab6665728c7145e432a55
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common header files not applicable to open-vm-tools.

commit 621013de8d6e98ccb9e45b69ed26ede7b460500e
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit b4c81e8fb510f6b978973975246d1bba78a2361a
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 2ecb5a9bedcc6ddd6a19dd3b24ca577e463c6257
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:38 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit 0d6b03ce9ee20260998dabef3fcc7b484d2935a5
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common source files not applicable to open-vm-tools at this time.

commit abe5c72317031ad2eceb21038cca2e67431a4a79
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 2ad2337e413d65eb88db85740d5515505db16d86
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit aa9d798ac82e84bfdf2bb9101c689338cbfb6dfe
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit add970dae8e60ce4a3c98c5fd6b7c7c7d5138066
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit e44d22a272e99890f04ed14a55ec53facaabf7fb
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Update vmware-user.desktop.in and delete vmware-user.desktop.
    
    Update settings for vmware-user.desktop.in:
    - Remove Encoding since it's deprecated.
    - Uncomment NoDisplay=True since the bug related to it was fixed
    a while ago.
    
    Delete bora-vmsoft/install/common/vmware-user.desktop as it is no
    longer needed. In open-vm-tools, vmware-user.desktop is generated from
    vmware-user.desktop.in.
    
    Pull request: https://github.com/vmware/open-vm-tools/pull/668

commit 879e7e7600d219e6be04baae3a0903802265be38
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit 5cb816ddf7b4278f4fe7574180a7b1fbf5693e08
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 3bf397e2a039772cdc62d6a7248a38388d4a58d2
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit e86e129392012521937a8439ab146e79515c5ff6
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:37 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 9bc336c8457ddf1bedb5c6ef8d2fde4b2d4a9d59
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Jul 15 02:56:36 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 515e38c175e0a642865c663334932b2247414d87
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue May 28 02:35:23 2024 -0700

    Update ChangeLog with the granular push of May 26, 2024.
    - plus Copyright pattern update of file x86_basic_defs.h
    - plus ChangeLog update of May 20, 2024.

commit fa9c8fb804f47536cddcdca1365b79477dccff88
Author: Kruti <kpendharkar@vmware.com>
Date:   Tue May 28 02:00:20 2024 -0700

    Update Copyright pattern.

commit eedd5a4b61bfd2f54eaabffc2553cb9d7fcabbb5
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun May 26 23:55:32 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit 0d0b7bd3c8c2f63519c055236af843ecfd6d718f
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun May 26 23:55:32 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit bf2ac0396aedb12330050eeb63d0e3f2af65f847
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun May 26 23:55:32 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 5ab6868b2b993a18f3680416ad1257b4240c5f25
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun May 26 23:55:32 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit f026fcbdf5344d531954d049b05db12c3cf14429
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun May 26 23:55:32 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 8112f3d6d12a59d44f87776090643a2519b52f08
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 23:18:11 2024 -0700

    Update ChangeLog with the granular push of May 20, 2024.
    - plus Copyright update of file guestApp.c
    - plus ChangeLog update of May 3, 2024.

commit 9daa47c599c1fd85b60419b9375ef7d53d8d5c0f
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 23:10:14 2024 -0700

    Correct copyright year to 2024.

commit 8cd75607fbb7b5923f791c3cd8270d88b73fbef2
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:13 2024 -0700

    [Coverity]: Fixes for issues found from static application security testing
    
    Adding coverity escapes for false-positive issues.
    
    hgfsServerParameters.c -- 1 issue reported.
    issue: Overrunning array of 5 bytes at byte offset 5 by dereferencing
           pointer "newName".
    impact: False-Positive
    fix: suppress 'overrun-local'
    
    vmhgfs-fuse/file.c -- 2 issues reported.
    issue: Overrunning array of n bytes at byte offset n by dereferencing
           pointer "newNameP" (n is 17 and 5 respectively for those 2 locations
           where the issue occured).
    impact: False-Positive
    fix: suppress 'overrun-local'
    
    vmhgfs-fuse/link.c -- 2 issues reported.
    issue: Overrunning array of n bytes at byte offset n by dereferencing
           pointer "fileNameP" (n is 17 and 5 respectively for those 2 locations
           where the issue occured).
    impact: False-Positive
    fix: suppress 'overrun-local'
    
    vmhgfs-fuse/transport.c -- 1 issue reported.
    issue: uninit_use_in_call: Using uninitialized value "reply" while calling
           HgfsCompleteReq() function.
    impact: Bug
    fix: Remove function, it is unused/dead code (transport.h too).

commit b91770061d3000de3fbe0994480579d1c2ca62f8
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:13 2024 -0700

    Change to common source file not applicable to open-vm-tools.

commit 385e9b78195e96089353fbe2e945f069b245a831
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:13 2024 -0700

    Maintain compatibility with libxml2.12.5 and later.

commit 9364240246824f9a1daa591cc45af8a6714e046b
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:13 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit ebe97d7d8cb01e68826ada7766f0227d3c016508
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:13 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit ae0b62370b6ca926dbabe25bf5856c2a97cdcc5c
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:13 2024 -0700

    Verify the xmlsec version before passing the compiler flag "-DXMLSEC_NO_SIZE_T",
    as it has been deprecated since 1.3.3.
    
    The configure option "--enable-size-t" has been set to yes by default starting
    with 1.3.0.
    
    version < 1.3.0 :
      Add "-DXMLSEC_NO_SIZE_T" if size_t has a size other than 4 bytes.
      (There was no enable-size-t before 1.2.35 and the enable-size-t is
       "no" by default in [1.2.35, 1.3.0))
    version >= 1.3.0 :
      Do not add "-DXMLSEC_NO_SIZE_T" (as enable-size-t is "yes" by
      default in version [1.3.0, 1.3.3) and no need to consider this
      option since 1.3.3)

commit 3fb8fe48f108067c39077934497df465616ba518
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:13 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit b841140771ae6218f8875574de78dac1dd19fd90
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 084e3f4c6911d8036d157ed4f96dbda94f705142
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 1dad197a3419d900e8e4a7e2549abf8586a8b885
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 6196b065644d325d6345db2a42f8778a0fcd9751
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit 42ceedd78f9ccb39540c4ea12f40fba7c644a050
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit b3b985137250d9b82bb22367a1ce1e424830c21d
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit b8fbf56a86b2d7e3d937fba9858add64d9792e8b
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common source file not directly applicable to open-vm-tools.

commit 33aa649c612d2a41f850ace726047fb5d6f70475
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Remove unused header files from the open-vm-tools source bundle.

commit 2723eb37bad5d86683e67c6c6d09871da35e92e3
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Log customized error exit code from offspring processes.

commit 0ffadd28557203cd0c994e48ccb442f1405a85c8
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit ec5f27caeb454cf1c4142a7e95f98b9d2e001b3d
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Fix copyright years.

commit f9649775dafd0c2666f86357c1f72fb10e0d18c4
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    [Coverity]: UNINIT in resolutionSet finding from static application
                security testing (SAST)
    
    resolutionSet.c -- 1 issue reported in the file
    issue: capabilityArray not initialized or partially initialized when
           reaching the statement.
    impact: False-positive
    fix: suppress 'uninit_use_in_call'
    
    VMTools_WrapArray converts the capabilityArray to a GArray.
    The 'wrapped' array is allocated space for capabilityCount elements and
    only the first capabilityCount elements are COPIED from capabilityArray
    to the allocated GArray.  As such, the uninitialized elements of the
    capabilityArray are never used to generate the returned GArray.  While
    technically true (uninitialized) there is no reason to zero the array
    indexes from capabilityCount through to the end of the array as these
    elements are unused.

commit b437f6f4ae99b540820cb66dabc716eb43c5c71a
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 31894dfbfcd91efe5a2712c2f33ce9114d942e11
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Fix LOCK_EVASION issue found by Coverity scan.
    
    fileLogger.c -- 2 issues reported in file
    issue: MultiReader/SingleWriter lock race conditions between assign and
           check.
    fix: Mitigation more than fix.
    
    issue: Coverity seems confused by the MR/SW lock, but there is some data
           field assignment performed under the wrong lock to clean up.
           fix: Move assignment made under Read lock to Write lock.
    
    Moved setting the data->error status inside of writer lock block.
    
    Added re-checking the data->error status at reader -> writer and
    writer -> reader lock transitions.

commit 52596dea1e33e40ad8f518d445b53d7f0a4f34f1
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon May 20 22:58:12 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 9e85a0c132b8aa01e27bf958b61a802602352311
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:27:03 2024 -0700

    Update ChangeLog with the granular push of May 3, 2024.
    - plus Copyright modification of files vmxnet3_defs.h and
      get-connection-info.sh.
    - plus ChangeLog update of Apr 7, 2024.

commit a0ffd2a0bf2a1f22cce695adad374628c832555c
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:15:25 2024 -0700

    Copyright update.

commit 550b331d8c9b738065ea4c99daa07dcd1ab9235d
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 3abed5b9e3060208a02dd404749a3683b5aec79b
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    ProcManagerPosix.c:  Direct child process's logs to stdio.
    
    Mutexes in lib/libvmtools/vmtoolsLog.c and glib could have been locked
    at fork time.  The vmtoolsLog.c Debug(), Warning() and Panic()functions
    are not safe for child processes.
     - Direct the offspring process's logs to stdio.
     - Terminate the offspring process with _exit() or abort().

commit 12f3303f0559e74724b0656d192d32bd54957350
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Fix the Y2K38_SAFETY finding found by Coverity scan.
    
    Change the date type to 'long long' to make the size be 64 bits,
    same as that of time_t.

commit f9aaf02fd8c3e928bc1706fc9c692a6190a03a23
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Service Discovery data collection duration takes > 70 minutes when
    monitoring large setups.
    
    The issue happens on vrops node VMs that are part of large clusters
    because these kind of nodes typically have a large amount of tcp/udp
    connections.  The root cause of the issue is an enormous number of
    spaces generated by "ss -antup | grep -E $pattern" command in
    get-connection-info.sh. The SDMP plugin considers these spaces while
    calculating the chunk size for writing into NDB and writing about 200-400
    chunks for the get-connection-info key.  Processing of this amount of
    data slows down the discovery process on both the adapter and plugin sides
    and, as a result, the discovery exceeds the timing limit and fails.
    
    The solution is to remove unnecessary spaces from the end of the
    get-connection-info output lines.

commit fa6d75da8c15c7fb3a696839c9f4aa7b43344d31
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Change to common source file not applicable to open-vm-tools at this time.

commit 5681e42c30f9a8875abfcdecb8c12ee8c51416f1
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Record the use of version 12.4.5 in the vm_tools_version.h header

commit 200fa56ef5f0097df888c34e24cb52c9c947d287
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Fixes for issues found in Coverity scan.
    
    vgauth/serviceImpl/saml-xmlsec1.c
    issue: 'string_null' for strlen(pemCert)
    impact: False-positive
    fix: suppress 'string_null'
    issue: leaked_storage: certChain is not cleaned up on error.
    impact: Memory is leaked on the error path.
    fix: Add line before return to free certChain.
    
    vgauth/common/i18n.c
    issue: 'leaked_storage' for "name" variable
    impact: False-positive
    fix: suppress 'leaked_storage'
    
    lib/file/file.c
    issue: use_after_free for 'src' pointer
    impact: False-positive
    fix: suppress 'use_after_free'
    
    services/plugins/serviceDiscovery/serviceDiscovery.c
    issue: overrun-local: gdpErrMsgs array contains one less entry then there are
           enum defined.
    impact: Valid but the function never return the GDP_ERR_MAX enum.
    fix: in gdp.h, add an error entry for GDP_ERR_MAX this way gdpErrMsgs will
    generate all entries.
    
    lib/file/fileLockPosix.c
    issue: string_null for 'buffer' not being null terminated.
    impact: False-positive
    fix: suppress 'string_null'

commit 1963bfed9f388aa57c9a8fc5d0f17424c76cbe60
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 1d19761e81b9a601b6fdb10109c92331f0b11eef
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 5c10fab774d061230e8e747569d918272b182366
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    Fix the SHELLCHECK_WARNING findings from static application security testing
    (SAST)
    
    install/Linux/scripts/network: Multiple shellcheck issues found in the
    Linux "network" script with the warning "Remove backticks to avoid
    executing output (or use eval if intentional)."
    
    Removed the backticks from those corresponding lines of the script.

commit b892d59578b2a3d65f473d6f597df2f3c95997bf
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:45 2024 -0700

    [Coverity]: Fix the Y2K38_SAFETY findings from static application security
    testing (SAST)
    
    guestInfoServer.c -- 2 issues reported in file
    issue: casting time_t (64bits) to int (32bits) causing Y2K38_SAFETY.
    impact: delta is a time delta in seconds, overflow if delta >=
    (G_MAXINT/1000)+1
    fix: Remove cast on delta, cast both values as int64.
    
    issue: casting time_t to int for logging to a '%d'.
    impact: delta is a time delta in seconds, not expected to overflow a 32 bit
    int.
    fix: Remove cast on delta, change string to use '%"FMT64"d' format and cast
    the time_t to int64; time_t is defined as 'long int'.
    
    vixTools.c -- 7 issues reported in file
    issue: casting time_t to int for convertion to string (xml)
    impact: procStartTime is a time from epoch, it will overflow the int in Y2K38.
    fix: Remove the cast, change the string to use '%"FMT64"d"' and cast the
    time_t to int64; time_t is defined as 'long int'.
    
    issues: casting time_t to int in call to VixToolsPrintProcInfoEx.
    impact: The times used are time from epoch and will be impacted by Y2K38.
    fix: Change signature of VixToolsPrintProcInfoEx to take in time_t types.
    Change VixToolsPrintProcInfoEx to use '%"FMT64"d' in string conversions.
    and cast the time_t to int64; time_t is defined as 'long int'.

commit ae31ed20bfbf0e744701f60b7839f88aa73ae554
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:44 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit ffd52c7e4010098893a34c66b6d70b500d98711e
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:44 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 1b3e41d24086fed921982674daa89f70d3bd677e
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:44 2024 -0700

    Style correction in Broadcom copyright.

commit 8336d5d8ace540b356813c197794d5f14f2ae38b
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:44 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit e2d1f8c418c72f21f0ef44a83262d19e0f68185e
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:44 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit c48b6ac791d13313db4cb023b275bb6df90c565a
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:44 2024 -0700

    Change to common source files not applicable to open-vm-tools.

commit 98e9e31448b81ff4ba8f4ec29c893957aa9d07aa
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:44 2024 -0700

    Stop the glib based logging to avoid nested logging from RpcChannel error.

commit 7f68542609c84b573b0faee31acd4aa5dfffc94b
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri May 3 09:05:44 2024 -0700

    GuestOS: Explicitly identify Flatcar Linux
    
    We support a VMX guestOS string for Flatcar Linux ("flatcar-64").
    Update the guest identification code to report Flatcar, rather than
    generically (other linux <n> - 64).

commit 831d35dfed2fc94eb8dfa61cf16cfc7b9d1a2a36
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:49:24 2024 -0700

    Update ChangeLog with the granular push of Apr 7, 2024.
    - Plus ChangeLog update Mar 18, 2024.

commit 9026382b617114a4ea10593be3358d4f5470160a
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:32 2024 -0700

    Update NetworkManager calls in suspend/resume scripts.
    
    Revise the NetworkManager calls in the Linux network script to
    prefer using the Sleep method over the "Enable" method being used
    to work around a bug in version 0.9.0.
    
    Pull request: https://github.com/vmware/open-vm-tools/pull/699
    Issue: https://github.com/vmware/open-vm-tools/issues/426

commit 76177671b4dbe6795b30d7282750daa6bc44c3ce
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:32 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 160a2f6464f1f8a391e20445436aac828747944b
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:32 2024 -0700

    Change to common source file not applicable to open-vm-tools.

commit 16b2942637b4b4c8613c97910b0c926aa78f118e
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:32 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit a27e36f708ba532610bc80aa05c6f646aff2e221
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:32 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit d00cb18b872419d1fc9dba12d82a33249c9f8381
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:32 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit dc2afa499e80d09f198d056b9f65b34054934eb9
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:31 2024 -0700

    Linux network log file permissions fix: 0644 to 0600
    Since release 11.3.5, on linux guests, the vmware-network.log file has root
    default	file creation permissions (0644) rather than the expected 0600
    permissions.
    
    Fix:
    - Adding chmod 0600 on log file creation.
    - Adding file creation before first logging.
    - Adding handling of unset handler in case switch, default to file logging.
    - Adding logging of unknown or bad handler, and using file logging as default.
    - Default number of logfiles when network.maxOldLogFiles is set to 0.

commit 98d046ba12d3eb3e34ec2cf5752a9c35721946ba
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:31 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit ef456ec9073f2f223e56117077fd2555a3e3d4c1
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:31 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 9523e770f58e440e7359b3e495f2fe6b03da3447
Author: Kruti <kpendharkar@vmware.com>
Date:   Sun Apr 7 23:26:31 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit a02c7f8de02dd3108a5ebdd6158dfc9366051409
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:42:23 2024 -0700

    Update ChangeLog with the granular push of Mar 18, 2024.
    - plus Copyright update of file vmxnet3_defs.h
    - plus ChangeLog update of Feb 28, 2024.

commit c2056a39e9bf4fc91cd457c4ef12c29a3e9af3a7
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:30:49 2024 -0700

    Correct copyright year to 2024.

commit f510b78fe043392325ba0cede8066994baaa8276
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:49 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit acfd8ab650f10759b09a93f8adde71459a131392
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:49 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 3ad15e4e776738b993c8c2da8119f0c1a7bf0a91
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 652a17c1d06a0ced2fceecde700ad93623d311b6
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 9adc1653c0d40aef5e27e415bfa7cc111bda31bd
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 7884ab85eebacf7c864e1eecab600b94dc9dd844
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit d27143ceb08381f644fc93d8b685965685f87f1a
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 51036fade754381b95821d54ec5e01dd56bbd5dd
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit ad34c945344422fac091d7e7d5c1869edf4b0cdf
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit 96d97fdf1031b1c71656c7089e0ecfb7390af2eb
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit fc9c9503ee2d4a1a18aed73d001ef6340491d302
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    [GDP][GdpPlugin] Subscriber presence interrogation (ZeroData).
    
    This change allows empty/0 byte payload to be sent from GDP Plugin on guest
    to the gdp daemon on host when querying for subscriber presence,
    without publishing the data to the subscribers.

commit 66fb7a4816661670889d3f6f6cb74895cfb8c6fe
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common header file not applicable to open-vm-tools.

commit e3ca1f9beea77e7423c1c21d8f698b18d4ebe942
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Change to common source file not applicable to open-vm-tools.

commit 0eeef953ab3034bfd7034b5db7c064c9b287b791
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Mar 18 11:03:48 2024 -0700

    Bump the VMware Tools version to 12.5.0 on "devel" branch.
    
    Ear-marked "SOCKET_WRENCH" as the tools tag for the 12.4.x series of
    releases.

commit 11647ac4a337e7b768c98b3753acd4e814e58939
Author: Kruti <kpendharkar@vmware.com>
Date:   Wed Feb 28 10:01:36 2024 -0800

    Update Changelog with the granular push of Feb 28, 2024.
    - plus ChangeLog update of Feb 23, 2024.

commit a4b3c5996c1027cefc2e8dddcac24af2193c6284
Author: Kruti <kpendharkar@vmware.com>
Date:   Wed Feb 28 09:33:57 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit 3b63dd72bf283e575d86d1b691d1191f26df7c0e
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri Feb 23 08:06:32 2024 -0800

    Update Changelog with the granular push of Feb 23, 2024.
    - plus ChangeLog update of Feb 19, 2024.

commit 70684e40254279f8b8e16dbf51a33c0407d22703
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri Feb 23 02:25:10 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit f9769f7b4f90e5da0ee51b699b203e1e737e0b90
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri Feb 23 02:25:10 2024 -0800

    Changes to common source files not applicable to open-vm-tools.

commit fe771b09686ac3160886f685488a3fd7dede7831
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri Feb 23 02:25:10 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit c199ccedd3fd8417c0d58b4ca1d3675224d8ac48
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri Feb 23 02:25:10 2024 -0800

    Power Ops: Attempt to execute file path only
    
    Adding a check to verify that the path to execute
    is a file only and not a directory.
    
    Pull request: https://github.com/vmware/open-vm-tools/pull/689

commit c35063e1280f549342a993453cadff3cc12b8ca6
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri Feb 23 02:25:10 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit e4f0f9b0d72f583b5129fc8d2b19dd5bb4497d74
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri Feb 23 02:25:09 2024 -0800

    Change to common source files not applicable to open-vm-tools.

commit 190202266d0d198cef38c34420be6057bd75e3e2
Author: Kruti <kpendharkar@vmware.com>
Date:   Fri Feb 23 02:25:09 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit 56ecc8b15b195a66d1476360b0a51579324e3820
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 07:07:41 2024 -0800

    Update Changelog with the granular push of Feb 19, 2024.
    - plus ChangeLog update of Nov 21, 2023.

commit 9c0491a6654630397aad6a884e6aa04f1cd962e1
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    Changes to common source files not applicable to open-vm-tools at this time.

commit 6057078343bc803ff1614fc2039e31bec49bcf35
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit e8cbf5e4db323638b95999bb268a0d2cb2ab4ce5
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    Changes to common source files not applicable to open-vm-tools at this time.

commit 86b1b5bf4a688e9e986400249a8170d03f24bea9
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit aad5ad195a0a502982e7cc8d5269d13ea6e38a85
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit 3095d26c3d3f044911771d436c31b0cf51abea35
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit 60094472f75aefbbc957574faa8c1663ee0625d4
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    lib/file/file.c: Handle EACCES during File_CreateDirectoryHierarchyEx
    
    On DELL thinOS, while creating an existing dir in a path without write
    permission, mkdir returns EACCES. This breaks the directory
    hierarchy check.
    This patch handles the EACCES by checking the file with euidaccess
    after an EACCES failure.

commit 777dc80ddfcf8980c055ab33091200923a06ab67
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    Change to common header file not applicable to open-vm-tools.

commit c0cc78a3fa53c8f6ea82aafda981251e753bae4d
Author: Kruti <kpendharkar@vmware.com>
Date:   Mon Feb 19 06:32:44 2024 -0800

    Fix out-of-bound access issues reported by Coverity
    
    Do not pass PATH_MAX and FILENAME_MAX to Str_Strlen function call.

commit 4986a2b0cbb94e8d6e3a6e4414215246aa0cbbbd
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 21 12:23:20 2023 -0800

    Update Changelog with the granular push of Nov. 21, 2023.
    - plus ChangeLog update of Nov. 16, 2023.

commit e6c2e7a6426335e6b6cef87e223a687b4623ce8a
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 21 12:17:25 2023 -0800

    Change to common source file not applicable to open-vm-tools.

commit 76b60aa06da13f2c1e42e10a3bef7a62a51fc896
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 21 12:17:25 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit af68bed4751d1415b292d310e41f557d8c7ffcde
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 21 12:17:25 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 4bf9de66e43222f50d1280f7181bc542b059d02f
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 21 12:17:25 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 39e99567d4febf2987f22cc778c4cbefff40d307
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 21 12:17:25 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 5bc44d481b88ddf14e9c46d56b7bd8f0ebff6b6d
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 21 12:17:25 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 7994b3261aa968f69c0fa6eabac63e4d68423bbc
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:23:56 2023 -0800

    Update Changelog with the granular push of Nov. 16, 2023.
    - plus ChangeLog update of Nov. 13, 2023.

commit 7f7374c8ab89c546e4e32d3302442103c0a0ba5e
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Fix double free in guestInfoServer.c.
    
    Double free in "GuestInfoGather(gpointer data)" for osNameFullOverride.

commit db767e6d28188cbed8a13b2497877e05c5fd38ab
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Drop group privileges before dropping user privileges in vmtoolsd vmusr service.

commit d3bce5781e8c869fcdd398d2bb1fcceb4ef84076
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Send guest detailed data to VMX even when short-name is set in tools.conf.

commit 7c2e898997a5b5667ed0a932652124dc85b80560
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 1910ed7b5ac64c98d03b14532f8af7460fa0ebe0
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Changes to common source files not applicable to open-vm-tools.

commit f094b993b7be5583e47a0d3f10fcbabe1db49122
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Changes to common source files not applicable to open-vm-tools.

commit 5d5bbbecd32cbc054b81b05a5af4bdbf62aee3a7
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change in Guest Data Producer plugin API and Guest Data Producer
    protocol/handshake between the host and guest to support ‘no subscribers’
    error feedback.
    
    The Service Discovery plugin was updated for API compatibility.
    
    Changes in the GDP protocol/handshake introduce versioning of the protocol.
    The new version is “2” and introduces new attributes (‘version’ and
    ‘requireSubs’) in the guest request header sent to the host publisher service.
    The response from the host publisher service to the guest producer client is
    also versioned and changes more significantly: V2 response messages have new
    attributes (‘version’, ‘error-id’, and ‘error-text’) and have the ‘status’
    attributes type change from string (‘ok’, ‘bad’) to Boolean (‘true’, ‘false’).
    The ‘error-id’, when present, contains a string identifier for an error
    type/code and ‘error-text’, when present, contains textual details for the error.
    
    The host publisher supports all protocol versions up to the version it provides
    for backward and forward compatibility. The host publisher service responds with
    the same version of protocol as the incoming request or its highest protocol
    version when the incoming request version is higher than it supports.
    The guest producer client supports all response versions up to the version it
    provides for backward and forward compatibility. The guest producer client
    always sends request to the host publisher service using the highest protocol
    version it supports.

commit c7c88a4aacd05f7d6f232c11c5537f2d43934d6b
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 7800de416d88ba03db4f8bd818d7c99d51048bdf
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common source file not applicable to open-vm-tools.

commit 72effb954333e49657315835c4df87fc0417ad2a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 14ada379295c86b9eed8d407753c88610312a6e8
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 2683d53f2ef4e8e01c938b6ec81f87e46e187c3d
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 8f03cddae70d27fe10b2833d53995f1dd3985f37
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 77f6b260876aba27c497a96c833b26ec5d50578a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit f76fea45d2c4de6ebcc0d7f5ed40a6c24689abef
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Nov 16 09:21:20 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit f756d77a51c5e1c7d66b46e5e33774b5503e21de
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 13:14:45 2023 -0800

    Update Changelog with the granular push of Nov. 13, 2023.
    - plus ChangeLog update of Oct. 5, 2023.

commit 8647f0cf7a15f1c3a06e903cb15e053871d23c36
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit beb027f7eccea977d00ef2a91da29e19ccc0a6d8
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit e7c2898e1a2b61948efd7bfd582a36a0e9363b3b
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    Guest identification: Asianux Linux is now Miracle Linux
    
    The Asianux distro rebranded itself as Miracle Linux. Our
    infrastructure already knows about Asianux however the guest
    identification that runs in tools does not. Add the necessary
    aliasing code to the "in guest" code.

commit b7775db161d414bb1311a1eb120a7f8474d59aeb
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit e6937680a6ee85431799d956c521971113508dd2
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    Change to common source file not applicable to open-vm-tools.

commit 34035501b125caadae9e0a9598f1e68bbe56282e
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit f612000e31b788f3da054aae9d3f296447d00039
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    File descriptor vulnerability in the open-vm-tools vmware-user-suid-wrapper
    on Linux
    
    Move the privilege drop logic (dropping privilege to the real uid and
    gid of the process for the vmusr service) from suidWrapper to vmtoolsd code.
    Now vmtoolsd is not executed with dropped privileges (started as setuid
    program) and the dumpable attribute of the process is not reset.
    Unprivileged users will not have access to the privileged file descriptors
    in the vmtoolsd vmusr process.
    Also, set the FD_CLOEXEC flag for both uinputFd and blockFd preventing
    those file descriptors from being inherited any further from vmtoolsd.

commit fd4c58055253ba80d84a17778d9f2e93d117350e
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    Don't accept tokens with unrelated certs
    
    If a SAML token has a cert that's not a part of a chain,
    fail the token as invalid.

commit 0546037841f16e8fe4148c8434e2b02cb2b0a6dd
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:34 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit c10d8436ff4137dbd66b17d12ffe1be7e00a1f2d
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit cdbc0b2bee9d771ab3c3312abe737cabecf4bfc8
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit fa2b47d9a4d0f27be5a5172c5d0eb6f197fca596
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit e9cab85bc079a3b5b0cc9f154a0aa90946314c72
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit fdb122f3e196835e97d2828fd77580d4f7bedb1a
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:33 2023 -0800

    Suppress optional arg to backup scripts when empty string.
    
    Backup scripts can be called with an optional argument.
    Don't pass the optional arg to the script if it's an empty string.

commit e60562e9409f81541281c452af5f8426a1cc1df2
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit b4b5483324bd241a1895e080489383ed59335117
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 92cc832c4493c272cb9227a87f68a9ae0cf18fec
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Nov 13 12:07:33 2023 -0800

    Checking flag 'disable_vmware_customization' in more cloud-init config files
    
    Currently, the deployPkg plugin checks the existence of the flag
    'disable_vmware_customization: false' in the /etc/cloud/cloud.cfg file
    to determine whether VMware customization is enabled on cloud-init
    side when cloud-init is available in guest.  Instead, keep local settings,
    such as this flag, in config files under the /etc/cloud/cloud.cfg.d directory,
    for example: /etc/cloud/cloud.cfg.d/somefile.cfg
    
    This change implements the following adjustments to make sure this flag is
    handled the same way as cloud-init does in ds-identify and Datasource:
    
       1. Instead of regex matching flag 'disable_vmware_customization: false',
          check the value of flag 'disable_vmware_customization':
          If the value is 'false', it means VMware customization is enabled.
          If the value is 'true', it means VMware customization is disabled.
          If the flag is not set, by default VMware customization is disabled
          on cloud-init side.
       2. Besides cloud-init /etc/cloud/cloud.cfg file, also check all .cfg
          files under /etc/cloud/cloud.cfg.d directory.
       3. The value of flag 'disable_vmware_customization' in .cfg files under
          /etc/cloud/cloud.cfg.d directory will overwrite the one in the
          /etc/cloud/cloud.cfg file.
       4. The value of flag 'disable_vmware_customization' in a .cfg file listed
          further down in alphabetical order under the /etc/cloud/cloud.cfg.d
          directory will overwrite the value in a .cfg file listed earier.
       5. If a cloud-init config file contains more than one instance of this
          flag, the value of the later flag will overwrite the former one.
    
    Github Issue: https://github.com/vmware/open-vm-tools/issues/310

commit 2cb54143f98031d5ab42c08e4fb1a6c9133b44b0
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 15:53:07 2023 -0700

    Update Changelog with the granular push of Oct. 5, 2023.
    - plus ChangeLog update of Aug. 25, 2023.

commit d0605c5ba368a3f417ad1a24fc9dc28c0de5bef7
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 15:42:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 12c2e213b1de63349c58561612333fb9af8eb00e
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 15:42:41 2023 -0700

    Change to common source file not applicable to open-vm-tools at this time.

commit 5d08866eafbc2536732d9fce9d8f024532fe0364
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 15:42:41 2023 -0700

    Record the use of tools version 12.2.6 for an emergency patch.
    Also updating 12.3.0 as released and adding the scheduled 12.3.5
    update relase.

commit b7f4cf3d0dcd9de5df3340ee088bb701725b4597
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 15:42:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit e61042ddf3627af614a2cce12406ce7a5b5e7869
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 15:42:41 2023 -0700

    Enabling the open-vm-tools VGAuth Host Verification feature.
    
    The Host Verified SAML token work is complete. Adding the new code to the
    open-vm-tools source.

commit 058863ab5eaef42aa3511d4974c32db11a3cf9d7
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:50:32 2023 -0700

    Correct missed 2023 copyright update.

commit d442791014bde010be93f5b9ce588a88fcd68bc3
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 9201543b6e4e6d5bc4788433b8231fb8d8e678a9
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit ce61f2591ceed0b6acb4ad744a32d9362ca4b258
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit f8b150212afbf963241685406c42002b80b10a11
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit ad9066280a11bfc8466747e4fa4d9f60e2d29df4
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:28 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit fe7e5654fbb69baec6c8e6de5c367346580934bc
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Linux guest identification: Correct the misidentification of
    Big Cloud Enterprise Linux.

commit 045ccf2067bb1f9e29d20a0e8a8b9053da999cf6
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit aeb469eda96b721bb382e294f63342139c7ae694
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Remove support for building with xml-security-c and xerces-c.

commit 59bff291726794b85921edf206290cd43c2b3cc2
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 6e43c69c0ab3e84a6d63d39d9fb832fc02d6076c
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit f48cd857d9fd17f4587b1d6d7213f9d88e658b5d
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 6ab97407104bd86f63ef4ab30462c2234b72e47e
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 07c13237ef07e62927fc540c0f77cd67bb6b03b1
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 0c494e27261531aaa6bc5d431e11dcf43691be61
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Allow only X509 certs to verify the SAML token signature.

commit eba62c065fa21ee88d7c3e27533f1fbc4e959688
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Changes to common source files not applicable to open-vm-tools at this time.

commit 3ddae17a251d51c15a576fe3da7643574a96c061
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit f906dc45d324bf531219b14c5af0f405f801d5f4
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit af8cb38e92ac414539655f563f1f05fdaf532fe6
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:27 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit d90cefe349130cedc7af9722bac77198b4e0b891
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:26 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 60ede3ec06d81bf7033ca2ac68431148506baf1b
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:26 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 2138a3fd2c8599d1d5ce66a423e86f61ba4dfe69
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:26 2023 -0700

    Remove Glib usage from stringxx/ubstr_t.
    Replace Glib::RefPtr with std::shared_ptr.

commit e367c428ff4062517228171ea63359fdbe252e66
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:26 2023 -0700

    Update Glib header inclusion to address C++ compatibility issues.

commit a2f775b5c3e1d6c4859308bb87d9209018a28716
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:26 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 0633d24feca0d9aa28696b4156c6f1944d3e7644
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:26 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 126f05a2c2834ce20bc507b5e0f96b3b5929ed1a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:26 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 1930fbc010efcfb95196a999e6f24c9bcb19087f
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:26 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit e796234eb7a790ac1accc87534106e10140394fa
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit bacfb532b8f904c7d4141891ca95db7b4b6c0ac6
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Allow pre-Windows 2000 style user logon for Linux Guests.
    
    With this change, applications that require user/password type
    authentication/authorization (done by VGAuth service) for guestOps
    can now use pre-windows 2000 style user logon for Linux Guests too.
    
    Github Issue: https://github.com/vmware/open-vm-tools/issues/641

commit 6267410529bdc7ba4c4e89293232d72f7d824641
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 1536a14b450633ba8040f3fc57e922e39d9b3311
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit f2485a10afcbc3a81b9cfc4726e2492a338b729a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 97fd6909e336796e5eb3829d853754de9ac3b97a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 7a6e765d2a5f3946513abdec8fc61c00385dc120
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 2f1d2a0231db89c035b1dcd8a6266ea8ae49c9c4
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Bump the VMware Tools version to 12.4.0 in the "devel" branch.
    
    Setting the development VMware Tools version to 12.4.0.
    Adding the tag "HEDGE_TRIMMER" to the 12.3.0 version.

commit 5188bd21d33180eb70612f57d5cb2aef8188108a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Change to common source file not applicable to open-vm-tools at this time.

commit 093aef15e1e390a613237a3bdd7a8c794ab672cc
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Oct 5 10:35:25 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 75a751e2fda529a14de79fd32fb25ca97612400c
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Aug 25 12:11:31 2023 -0700

    Add missed 2023 copyright change.

commit 1335787e74fc86752984c81ea7b0bd1b0065ce89
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Aug 25 11:57:15 2023 -0700

    ChangeLog update of Aug. 25, 2023.

commit 518a77a39a30d39c4397d2533bae57880f44ec6e
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Aug 25 11:51:12 2023 -0700

    ====================================================================
    The "stable-12.3.x" branch was created from the "devel" branch here.
    ====================================================================
    
    Update ChangeLog with the granular push of Aug. 22, 2023.
    - plus ChangeLog update of Apr. 24, 2023.

commit 14dfd9c00b5321bafc012f678ef1cd2129e03a73
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 15:51:19 2023 -0700

    Fix build problems with grpc
    
    Updating Makefiles to put -l specifiers into LIBADD and not LDFLAGS.
    Use the grpc++ pkgconfig (.pc file) if it exists to retrieve flags
    and libraries for linking.
    
    Pull request: https://github.com/vmware/open-vm-tools/pull/664

commit 6584bcaa8c25da5ef0f20c7f6a17e6b173a25f2d
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 15:51:19 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit a1f318dfb992cfee9555a747207fce6721313e2c
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 15:51:19 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit f584dca10f4cc7e0a0733ac5dc8844aac65af7ae
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 15:37:45 2023 -0700

    Enable hostVerfied SAML token feature in Tools.

commit 4463b84006034879cb1d8cba8ae43f9509c6d0ac
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:43 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 6430649b834da00a81410f7c1d87ca4e67409ada
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:43 2023 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 6489b7c0bc4d8fb603cf11ff4605a5d2a975daa8
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:43 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 447f17dfb33a69a43a6d4ad1b5e486a6b20a6f77
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:43 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit dfc6ee8aa66b3bfcdefe8020639f2b2ef03c215d
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:43 2023 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 859849c55f34a777e6ac2a4e38aab30028b5207a
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 759613baeb1377708123772be1fe70da341b1707
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 972d10eab23dda4d219f919685fbcd2651a57165
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 6852459279251412d01b073de977c1848119918c
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 8a3103cfb7d68f7e501c16f8ddb4cc998a68ad20
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 8047bc8787d3bf04748fb3435c1731cc03f30405
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Fix VThreadBase_ForgetSelf using wrong format specifier for VThread_CurID.

commit c3eb0c034c53a80d92acbdc1cb8699c4d9a724df
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 1a27960f1761ad9f9e5bb7e5eb952f51e2fa0a60
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 901511c7de7a1d870725eeeb5f660f8728dd119d
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 8205b35f6b29c10a6e12616785f65c18b0c5204b
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 17543c6584bdd41ff75d1ea85b4cf61c7b39f65e
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 89831b1c249ba89d683d74b3624efe77ad3f3e53
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    svtminion scripts v1.6 fixes for salt-minion release 3006.0
    
    Fix output of getent group, so don't make noise on Linux.

commit eabbaf9bca1320b45fb7ad46cf48f5fbf49e958f
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 60c3a80ddc2b400366ed05169e16a6bed6501da2
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Provide alternate method to allow (expected) pre-frozen filesystems
    when taking a quiesced snapshot.
    
    Effective with open-vm-tools 12.2.0, Linux quiesced snapshots will fail if
    any filesystem(s) have been prefrozen by other than the vmtoolsd process.
    This has been done to assure that filesystems are inactive while the
    snapshots are being taken. Some existing prefreeze scripts may be freezing
    some filesystem(s). In these cases, the vmtoolsd process must be informed of
    anticipated pre-frozen filesystems by providing an "excludedFileSystem" list in
    the [vmbackup] section of the tools.conf file.
    
    This change provides a new switch in the tools.conf file to allow pre-frozen
    filesystems to be encountered and accepted when doing a quiesced snapshot
    operation. With the default value of "false", the "ignoreFrozenFileSystems"
    can be configured with a setting of "true" to notify the quiesced snapshot
    operation that pre-frozen filesystems are allowed.

commit baf812a3d942e9f1a6fe9774ddc5e514b976f059
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit b3c23d050ffab46ae24d6fc8e98851a07863dd2a
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 99ee819329e6b1b51affcf15673c2020bfe4e059
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit dcc090563d12ba436c7137bb3861b2966fc372cd
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit c0116f300bfe2d37919fb333d63fd401dc228e5b
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 3f4dd03d969efe60c6c77002a3b8c8ddfd325ab3
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 6aa05e8959408d0981c18ed8dfc29513b71c98a8
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:42 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit f6c27bbe691ba06f535d9b5d5dbabd4d4d5e3b1b
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 01d4788e191d1a846dbe7220442e25bba2cb545c
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 9d8054800e10cabeed6911a5b8ac85d612565fe6
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 0bc0f8e68339c1bae63e9d8734d4d10c8d4b5fa9
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 549f04fabbba57a33b8983e02064e216b8393485
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 508933ad4fb97cde6d19544d13005c88eaa9362f
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 9f1c97526ba6414bfbc342fcf0f8a83118dbee8d
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 016fa52e6bcbf868a08b5dbac0fdb8f19ab14c9a
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Removing appUtil library from open-vm-tools.
    
    Several Linux distributions have deprecated the use of gdk-pixbuf-xlib library
    and many may follow suit in future. Remove the appUtil library, which was
    required by Unity but is no longer supported on Linux guests.
    
    Github Issue: https://github.com/vmware/open-vm-tools/issues/658

commit 55d4f25e7ad27b77268bf5f2d61e75336890dacc
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 2a770fead82a86a8b67107c40fa8e5986fd39416
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit bea8dba7af41c6988a76bf6cbc47efd1d35761f3
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Remove some dead code.

commit 3a897d916ed6e69ebcede282ee36757808ee29af
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    svtminion scripts update for salt 3006 release
    
    Scripts Ver. 1.5, support for status 106 code feature.

commit 5cf3023cf9981a97cd65f3600d03dc4714b4cc5e
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 8b940e736ed25a7e7bad47a476f6e0db154620aa
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit eb771586d88bd46fc38853b4f63cc01262a168b1
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Introduce the new CAP_HOST_VERIFIED_SAML_TOKEN capability.

commit 2793302b6d4bcd427a6e4b70263b4ce8069fdd00
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 0b24b9b517c939059535aae8bfcd2ad70acaced1
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Aug 22 11:11:41 2023 -0700

    Modify the comment for the CONFNAME_DEPLOYPKG_PROCESSTIMEOUT value range.

commit a353e69b9438ca149a185a2249d076c951d945fe
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Jun 1 10:22:47 2023 -0700

    Copyright symbol updates.

commit a15a0bda093255eadb3768fb845b96a185ac6af9
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:46:20 2023 -0700

    Update ChangeLog with the granular push of Apr 24, 2023.
    - plus ChangeLog update of Apr 6, 2023.

commit b4ecba012795fd6e227a36f9da43ae390f8ac121
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit a5eb9f3ce90f3c9ac79673db7e21486fcc8b434d
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 5321737607c4779936ea7d1ca4eb7cc02f0ba452
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 68b465af17e333d04776fc606088fc39b2a3dcdc
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 68107d0bd5e7f900c2af5946ef560fdcd5454534
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 27ea45d66f2358c1dcc8400a600f5e4ed8a419a1
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 8c7bbf28197c254d6859ac6727ab88355b2cb7f8
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit c66fb5c9ec87167f82d8b72c611a2df35af514a9
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 0fb50371215f5a6924103309980e565eebff08a6
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common header file not applicable to open-vm-tools at this time.

commit a480533a0a2bbeb9701f305cf3f78278e1279347
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Apr 24 12:30:14 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 87eb9ad7ed9ded840ac577cad2c027c3eaf9b49f
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:36:37 2023 -0700

    Update ChangeLog with the granular push of Apr 6, 2023.
    - plus ChangeLog update of Mar 24, 2023.

commit 0c15d1214d340893719c9eaaee7a071318bd7399
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:33:31 2023 -0700

    Correct missed 2023 copyright update.

commit 41f7e48c7570673bf84938bdf243dd78ad66c608
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Update the AUTHORS file in open-vm-tools.

commit 377f82a668c6c02a3fa15ad3f2da3a5b7849f39a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Changes to common header files not applicable to open-vm-tools.

commit d65cdcb30a38f8de7f127f9b6c8bf78e8c987b98
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 06081ea162cb73754b9bcb6574db78cba6db5aa7
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 215d87b455bc630f33c22f67d362a6e530fdd1db
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 0365b80e3a5c67ecedd3dcc312e425ef4b2ec9dc
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 9c861f7feade19ef863a46f5ddd59c44d98a7b83
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 8ee7744f7716467185c2a7abda8776ea10387f5f
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 241b8eefe7090086a8fc8bab5b9504578731eaf1
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 3a16728979d86ed78009817f1940a3ddc40b9103
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Apr 6 11:27:41 2023 -0700

    Improve POSIX guest identification

    Many Linux distros do not provide LSB information.  Instead,
    they rely exclusively on the the os-release standard information.

    We previously used a ranking system that rates the LSB data
    (if present) against the os-release data (if present).
    With the LSB data install much more rare than before, perform
    a quick check for the LSB before attempting expensive operations.

    This is in response to these GitHub issues:

    https://github.com/vmware/open-vm-tools/issues/647
    https://github.com/vmware/open-vm-tools/issues/648

    Note that we cannot look just for the os-release data.  In older
    Linux releases, there are mistakes or differences between the
    os-release and LSB data.  We must continue to do what we do now
    or risk changing the identification of older releases.

    This optimization is very effective.

commit b9da6af8a561b911c6d59cfffb5b96cd5af3b6f7
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:48:17 2023 -0700

    Update ChangeLog with the granular push of Mar 24, 2023.
    - plus ChangeLog update of Mar 6, 2023.

commit dfe0aa7ef53ea6051d31cc69f406e746e8d0f80c
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Change to common source file not applicable to open-vm-tools.

commit 5e28e7e7577b35c585d4c45e053a162615bdbb1b
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 2a9a730985ff05bcb808e68e4526856bb77a4c17
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit acff74d7fa4e4f3bb70c575a287cac2762cfdaec
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Record the usage of VMware Tools version 12.2.5 in vm_tools_version.h
    
    Adding tools TOOLS_VERSION_BANDSAW_UPDATE1 for 12.2.5 into the
    vm_tools_version.h header file.

commit c30e8e66dcc41fd6e869c3a87d712ab29a90df8a
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Record the GA release of VMware Tools 12.2.0.
    
    Adding the "_RELEASE" suffix to the tools 12.2.0 tagname in vm_tools_version.h.

commit a1a29b2358c8ca022d5116ee199c02337ec6a4a9
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Add UNMANAGED InstallStatus value for components
    
    The UNMANAGED InstallStatus is returned from components operations to indicate
    that the component is installed (detected) but is not to be managed by the
    Component Manager.  The Component Manager will not take actions (except
    checkStatus) until the UNMANAGED status for the component gets resolved
    (externally).
    
    The Salt Minion (salt_minion) component returns code 106 ("ExternalInstall")
    is equivalent to UNMANAGED.

commit 62dc9fe959f8cc6fee64a716cc1de2dee0f0d0ef
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    lib/file: Fix timetools breakage to use memset
    
    GCC bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) wrongly
    complains about using the zero initializer in some circumstances.

commit f26fad0849a5107018bbcc0b4687b460432e6d3c
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Change to common source files not applicable to open-vm-tools.

commit ab7a609bd52b57e74946a58f9a989845128555d3
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 12cc8ad99a9cd8faaf990d03010d511d50f88ee2
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit 425df45b9551383ec393d44926782f3d64603d83
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Mar 24 13:29:28 2023 -0700

    Change to common header file not applicable to open-vm-tools.

commit e0cec2ba3f7fcf0958077d6209a58951f5a6d275
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Mar 6 10:42:40 2023 -0800

    Update Changelog with the granular push of Mar 6, 2023.
    - plus ChangeLog update of Feb 23, 2023.

commit 813d8531e739f6a12a4e3c92f18786e10f46eacc
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Mar 6 10:32:39 2023 -0800

    Add antrea and calico interface pattern to GUESTINFO_DEFAULT_IFACE_EXCLUDES.
    
    Since k8s are more popular nowadays and their CNI also has IPs for pods,
    we should also exclude the IP assigned by popular CNI.
    
    Fixes issue: https://github.com/vmware/open-vm-tools/issues/638
    
    Pull request: https://github.com/vmware/open-vm-tools/pull/639

commit b822932f2b0ea864fdc85005e6ed7bf34c4b2a75
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Mar 6 10:32:39 2023 -0800

    Change to common source files not applicable to open-vm-tools.

commit 11141b784b66a941a98cb8d781125837bbcb267b
Author: Katy Feng <fkaty@vmware.com>
Date:   Mon Mar 6 10:32:39 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 43cab44867c952b67255d92acfda43bb89ce7cbf
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:55:53 2023 -0800

    Update Changelog with the granular push of Feb 23, 2023.
    - plus ChangeLog update of Feb 3, 2023.

commit a9e42290898d7077f73cda7fceeab046f0164388
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:52:12 2023 -0800

    Correct missed 2023 copyright update.

commit e4d5478f95dc0e38b4e152278b30e49960784be9
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    lib/file: Improve File_SetTimes
    
    There is a new system call for setting times that removes race conditions.
    Use it when it is available.

commit 2e8ac3a7963a5ba98808c2b0748773140da6be86
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 7173a629c81bd6acf45cb07dbb169fda5ab73adf
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Change to common source file not applicable to open-vm-tools.

commit 202c4939446da2e251ded55a9566c54d0d9f655a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Inclusive terminology - "sanity check"
    Address uses of the term "sanity check" in some files used by open-vm-tools.

commit e530a014e677213e5660106c75eddb72d151016b
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 076aa3f0276010daaae95a95b2c3d4aea56b55bd
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Remove SSL_NewContext() declaration from sslDirect.h

commit 8d7402b5b309275a4ef0c609966afed086d91e13
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 7ab29ed5a2ec3b80f0d5e69a437e48e667bca8ec
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 6927da94f892ce3a9acae9cb297842c4f7852b16
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Change to common source file not applicable to open-vm-tools.

commit 355ea8ce3cd4b5038fb79e0a6ca10777ec4fdf79
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 62b3b42fa69fc7a7fc8fdda9d1a8cd158244c819
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Feb 23 11:27:13 2023 -0800

    Change to common source file not applicable to open-vm-tools.

commit 830e9031f64d614c7bfe0acab022a9c33da9da2a
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:57:46 2023 -0800

    ChangeLog update of Feb. 3, 2023.

commit 632a516d8c4b64025ed0f7b0ee890ebb36a99a15
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit d0346245ddb589e8d35e02650321682987c60f09
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit b2acb8fb61dd2b3888ee58e78313ee29581c41fb
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 33b542fff5ecb78a0f1da4a005b09d11853f7026
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common source file not applicable to open-vm-tools.

commit 007203173890d75171db2c450bcaf37eb23c6a1d
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit e470e09b81a417e9f0c123793e3b4f0bfc1cd0cc
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit b40e6808fd6bb6483cef741cd80c3ab58f4094c8
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 58580a5d5bd3f8a0661408031dca52ac5c70183a
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 82379efe344790ee4ab58927a194a55d6931bc3d
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Inclusive terminology - "sanity check"
    
    "sanity check" clean-up:
    
    - Rename functions:
     - ./scripts/linux/network -> Renamed "sanity_check" to "confidence_check".
     - ./vgauth/lib/proto.c -> Renamed "Proto_SanityCheckReply" to
                                         "Proto_ConfidenceCheckReply".
    
    - Replace "sanity check" with "confidence check" in comments.

commit 556f27f7011927d27283923b6b86dd446fd2ff17
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Bump the VMware Tools version to 12.3.0 in the "devel" branch.
    
    Setting the development VMware Tools version to 12.3.0.
    Adding the tag "BANDSAW" to the 12.2.0 version.

commit 3232ed80db1af22c9906d78d12804681dc23fbfd
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit afc98393dfefae36bb26c9018b9d6e2d149688ad
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 486ca6223aad2474e356b0651476db53d4917a49
Author: Katy Feng <fkaty@vmware.com>
Date:   Fri Feb 3 09:41:08 2023 -0800

    Changes to common header files not applicable to open-vm-tools.

commit 3bc128aa58770d400de28f899cd867804f8aefb3
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:32:04 2023 -0800

    ChangeLog update of Jan. 17, 2023.

commit 512af95b8788f5784126368462866eb0e4d76524
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:22:16 2023 -0800

    ====================================================================
    The "stable-12.2.x" branch was created from the "devel" branch here.
    ====================================================================

    Update ChangeLog with the granular push of Jan. 17, 2023.
    - plus ChangeLog update of Dec 31, 2022.

commit 891f1f654a6d2bffb270d6f3c7e69caec431d860
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:15:28 2023 -0800

    Correct missed 2023 copyright update.

commit 9d458c53a7a656d4d1ba3a28d090cce82ac4af0e
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:08:33 2023 -0800

    Track Linux filesystem id (FSID) for quiesced (frozen) filesystems
    
    Tracking the filesystem FSID along with each file descriptor (FD)
    as the ioctl FIFREEZE is done.  An EBUSY could be seen because of
    an attempt to freeze the same superblock more than once depending
    on the OS configuration (e.g. usage of bind mounts).  An EBUSY could
    also mean another process has locked or frozen that filesystem.
    
    When an EBUSY is received, the filesyste FSID is checked against the
    list of filesystems that have already be quiesced.  If not previously
    seen, a warning that the filesystem is controlled by another process
    is logged and the quiesced snapshot request will be rejected.

commit 193200d3ce31461f876f4779ced3ca5c85f32459
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:08:33 2023 -0800

    Salt-Minion 1.4 copyright and license updates.

commit c3d2d7a23ad0deac57bf53667ebe5d14c24d53c2
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:08:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit ebd466b5a4f55e68486cc0e747ee6b76a4f171f1
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:08:33 2023 -0800

    backdoor_def.h: Update the Arm specific comment to be arm64 only.

commit 998ff379939defc09834ae55fea450ee61f46ca9
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:08:33 2023 -0800

    FreeBSD: Support newer releases and code clean-up for earlier versions.
    
    Changes to allow open-vm-tools and drivers to be compiled for newer
    FreeBSD releases and clean-up/remove code for earlier FreeBSD
    releases that are no longer supported by the FreeBSD Foundation.
    - remove old FreeBSD version support.
    - trap SIGBUS while VmCheckSafe().
    - fix build on recent versions.
    - fix build after 1400043
    - fix build after 1400051.
    - replace deprecated NO_MAN by MK_MAN=no.
    
    Pull request: https://github.com/vmware/open-vm-tools/pull/584

commit ab1728ebe46703624f151d09a65a100ccd514891
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:08:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit aad49df92c53aa561c4a23507ef4617c09a449ba
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:08:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 739cc1b828c1e23daca46a61b105391e8f271bb2
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Jan 17 19:08:33 2023 -0800

    Change to common header file not applicable to open-vm-tools.

commit 0ab6ba968e040f0b72e564338c6ea2b52bcb9e7c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 31 22:42:11 2022 -0800

    Update Changelog with the granular push of Dec 31, 2022.
    - plus ChangeLog update of Dec 22, 2022.

commit 301cea5522870a746112cd6d634a04c465dcf7cc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 31 22:13:07 2022 -0800

    open-vm-tools SUSE: Detect the proto files for the containerd grpc client
    
    On SUSE systems, the Go src'es are installed in a different location,
    namely /usr/share/go/1.18/contrib/src.
    Extend the config checks to detect that location.
    
    OVT pull request: https://github.com/vmware/open-vm-tools/pull/626

commit f749bd5b0647c7bdf8aa46ca035e070b5def352e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 31 22:13:07 2022 -0800

    Add missing error codes for AsyncSocket_GetGenericError()
    
    AsyncSocket_GetGenericError() returns ETIMEDOUT or ECONNREFUSED
    errors when they are encountered.  Added cross-platform #defines that
    represent these codes.

commit df63ef8559535bc57dfb5cecff7944074aca33e4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 31 22:13:07 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 11d107a07440c8cd97f4a3c34b8664a2089812ac
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 31 22:13:07 2022 -0800

    Changes to common source files not directly applicable to open-vm-tools.

commit 47a5bd3bd87f00e9e39cd3641028940eecbb315a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:37:51 2022 -0800

    Update Changelog with the granular push of Dec 22, 2022.
    - plus ChangeLog update of Nov 29, 2022.

commit 082f7f784b28452058ac6a6529f289144b5694d9
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:51 2022 -0800

    Inclusive terminology - "sanity check"
    
    Partial "sanity check" clean-up:
    - Change comments from "sanity check" to "confidence check"

commit 63a35ed428b16baedc4cea0d666ac1b8d762de2e
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:51 2022 -0800

    Powerops plugin:  Enhance logging and response message when a previous
    powerops script is still running.

commit 91e8725d65565b4b3452085a2e9db6e3f0bb1dcf
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:51 2022 -0800

    lib/panic: Remove Panic_GetCoreFileName and Panic_SetCoreFileName.
    
    Panic_[GS]etCoreFileName have been dead code for a very long time.
    
    This change removes them, along with the underlying panicState.coreDumpFile
    field.  Also, do some reformatting and tidy a few comments.

commit f2d8ed1e924818d9611db104a5a259290e53f68f
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit bd63d8029852af847383f19cd13d62adcabaabaf
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Source file changes not directly applicable to open-vm-tools.
    
    Minor cleanup in a couple of Makefile.am files.

commit 4a2332747dc9e95e7b93d2b6d28b1d7fd474e3f2
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Linux guest identification: Make the code more robust
    
    Additional clean up.

commit 049e37fef87a515c69471e7682ed5c3274942cf0
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 264c9c4cd4d8fbd06abaa3dc9266e4e16d2380d2
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Inclusive terminology - "disabled"
    
    Partial "disabled" clean-up:
    - Change comments from "disable(d)" to "deactivate(d)"
    - Change comments from "enable" to "activate" for grammar consistency

commit 4f884c52df4d03246e40715d771a23a9af17436e
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Linux guest identification: Make the code more robust.
    
    The code to read and parse the os-release data isn't doing a good job
    of protecting the tools daemon. Fixed this.
     - do not depend on sscanf.
     - bound the size of parameters;
     - better checking for syntax errors.

commit 1f1a34edda47b37d3bd7040b9f2080f8dcb23275
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Silence ComponentMgr logs for a missing support file.
    
    Components managed by the componentMgr plugin are often optional.
    The missing component script file is a normal condition.
    Silenced the logs by changing the message level from g_info to g_debug.

commit 422205fcad59a9202a06b65fe28e9ced1d3b1a3b
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Limit the number of RPC vsocket connect() retries for ECONNRESET errors.
    
    If a guest VM has been configured with 'guest_rpc.rpci.usevsocket = "FALSE"'
    to work around a problem in ESXi 6.0 ot 6.5 (KB 2149941), that guest VM
    may experience high CPU usage on open-vm-tools 12.1.0 and 12.1.5.

commit 4e278d12ea1ad9b541de94791065bf4658ed781d
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit a7da31373c5bb21d647a22c240a204645fa5f9ef
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    [TimeInfo] Handle notifications and get updates
    
    TimeInfo, which is part of TimeSync plugin, can be
    used to query, set, subscribe, and receive updates for
    time-related information from the host when guest is
    using precisionclock to consume time from the host.
    Previous changes laid foundation to subscribe and
    unsubscribe for TimeInfo updates in open-vm-tools during
    init/shutdown.
    
    When open-vm-tools subscribes for TimeInfo updates, VMX
    will send a notification GuestRPC to tools if and
    when the timing properties change. This change
    adds support to handle such GuestRPCs from VMX.
    The handler for the GuestRPC is pretty straightforward
    for now: it queues an async task that simply gets
    all updates and logs them.

commit 4ab39d04ddde473a9698660a545c356aa5614dcb
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 0b2992f6718150d7c04ce4268e071033d4222e45
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Limit retry if the VMX RESETs a vsock connection
    
    If guest_rpc.rpci.usevsocket = "FALSE" is set, a vsock
    connect() will always fail with RESET. This confused code that
    thought it could only happen for secure sockets when they
    were quickly re-used.
    
    Limit retry, and only for secure connections.

commit 3cfb89bd45978bababef56404e33da7e645b0b5a
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    [TimeInfo] Subscribe/unsubscribe to notifications during init/shutdown
    
    This change adds support for subscribing to time info notifications
    when this feature is enabled in the tools. As a result VMX will
    send time info notifications to the tools when host timing
    properties change. The change adds support to perform subscribe/
    unsubscribe GuestRPCs from tools. Note that, handling of notification
    (received from VMX) will be implemented in the next change.

commit a23ac6dc590374f3b990f4d220ec59d1c3d82195
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 70c64efdedfc4b6772fe540f502113b8b58d6aad
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 11d8bddccab30dea8c33ab8fb895b599ea6b8419
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 4e5ff11a5927ebf711656d30166914725fd811f9
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    [TimeInfo] Introduce TimeInfo in TimeSync plugin
    
    This change lays a foundation for upcoming changes to
    support TimeInfo feature in open-vm-tools. TimeInfo feature
    is introduced as part of TimeSync plugin and can be
    used to query, set, subscribe, and receive updates for
    time-related information from the host when guest is
    using precisionclock to consume time from the host.
    
    This change simply adds a new file and basic init/shutdown
    routines which are called as part of TimeSync plugin
    load/unload. The change also introduces a config option
    to enable/disable this feature (default is off). The
    feature is Linux-only for now.
    
    Upcoming changes will add support for subscribing and
    receiving TimeInfo updates.

commit 042b463ba64d527345c0a6048915d1b9a6a3402c
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 6c4e9964f58d94b31fda5bcc54c976959dd4f339
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:50 2022 -0800

    Inclusive language - "disabled"
    
    Use “deactivated” instead of “disabled” in some files used by open-vm-tools.

commit e85f51de2849566e8dd08f035474bfc626a17d8d
Author: Katy Feng <fkaty@vmware.com>
Date:   Thu Dec 22 16:25:49 2022 -0800

    Enhance the guest identification code for Linux
    
    The Linux guest identification code is enhanced to return two
    additional fields (when a distro makes them available):
    
    1) The VERSION field
    
    This field sometimes contains additional information not
    found in other fields. For instance, on SLES, this provides
    the patch level information.
    
    2) The CPE_NAME field
    
    This is the NIST Common Platform Enumeration Specification
    string. If present, this may provide information in a
    standardized form.

commit 569c595128322339d1435d6a745243d2d7aec860
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 29 12:22:44 2022 -0800

    Update Changelog with the granular push of Nov 29, 2022.
     - plus ChangeLog update of Nov 4, 2022.

commit e058df3ebf6aa0b4cd1b15f1f55ac4d56f09c5ed
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 29 12:11:43 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 136de61dd7c2a473ca926e2df1800c6d76aa5da0
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 29 12:11:43 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 6208fe0c286f45c70c621c15bdf90e935a5a56ee
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 29 12:11:43 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 7a94ee4524da9c794a68d6b2e7106d719209d93e
Author: Katy Feng <fkaty@vmware.com>
Date:   Tue Nov 29 12:11:42 2022 -0800

    Change to common header file not applicable to open-vm-tools.

commit 5405d130aba6724ba671cf5d3ff8b250147d624c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 4 11:58:14 2022 -0700

    Update Changelog with the granular push of Nov 4, 2022.
     - plus ChangeLog update of Oct 21, 2022.

commit bf074c36924f28b3f36f88aa4bed0337a0b695be
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 4 11:45:05 2022 -0700

    Add explanatory comments for Coverity false positives.
    
    Add comments for two memory leak false positives reported by a
    Coverity scan of open-vm-tools.   Don't annotate since the
    annotations can't be made specified to the leaked variable, so
    that if an actual leak were introduced in the future an annotation
    would cause it to be reported as a false positive.
    
    For the same reason, replace a leaked storage annotation added
    previously with an explanatory comment.

commit b85ab7a63a97d9b7258df73638c76f9b7c149a4f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 4 11:45:05 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit da618bbbdaa6341c478d548a7d951250c571ad8e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 4 11:45:05 2022 -0700

    Changes to common source files not applicable to open-vm-tools.

commit f5eb126149dccb3172a90e772f6d5471a6c2c435
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 4 11:45:05 2022 -0700

    Change to common source file not applicable to open-vm-tools.

commit d88dcf561315a316c1e2efda6a96ec22e2183306
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 21 12:11:07 2022 -0700

    Update ChangeLog with the granular push of Oct 21, 2022.
     - plus ChangeLog update of Oct 12, 2022.

commit 3e1154b1e260982a47f66a9c807b7613ced95f59
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 21 11:39:49 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 6d934775155d5b1ee000056319290780e4e79b80
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 21 11:39:49 2022 -0700

    Fix a regression caused by the previous guestOps ListFiles() change.
    
    If maxResults is 0, return an empty file list plus
    the remaining number of files.

commit 3c7bcfc3a3ca1e8a4d80b2eded28cb36535717f0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 21 11:39:49 2022 -0700

    Fix printf format type found by Coverity scan.
    
    printf format should be using %zu to print a size_t value.

commit f56f1573158d0d5c1962202fba4a4aa8d0bfa35e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 21 11:39:49 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 40c74575b37430a2e30dbdd149b5de8c45978949
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 21 11:39:49 2022 -0700

    Avoid a vmtoolsd service error message if not on a VMware hypervisor.
    
    When open-vm-tools comes preinstalled in a base Linux release, the
    vmtoolsd services are started automatically at system start
    and desktop login.  If running on physical hardware or in a non-VMware
    hypervisor, the services will emit an error message to the systemd's
    logging service before stopping.
    
    This change removes the unwanted error message.

commit c1ba736f18d35d5b1e149aa43a902cb2a69927f3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 21 11:39:49 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 6224a7616e26fd816772a82e100674ffdd87cf9f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 21 11:39:49 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 6ec06e4a6482872421a87367814849f340bfb1a8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 13:21:25 2022 -0700

    Update ChangeLog with the granular push of Oct 12, 2022.
     - plus ChangeLog update of Sep 21, 2022.

commit 7909b89d2d847f416257932e3c6ef96085a69ad4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 13:04:00 2022 -0700

    Add missed 2022 copyright change.

commit cb762155088e5f56535d1f8990718d8880d165ff
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:37 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 51a5cca0c77ea4f246e5a411727221fcf70f240e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:37 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 70a10365ba86e871710551d54e565ba6b8b3f4eb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:37 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 7645e6cd7e3e8f13a862ce3489ea04bad3a26bf2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:37 2022 -0700

    Update the guestOps to handle some edge cases.
    
    When File_GetSize() fails or returns a -1 indicating the user does not
    have access permissions:
     1) Skip the file in the output of the ListFiles() request.
     2) Fail an InitiateFileTransferFromGuest operation.
    
    Properly handle the hostd request offset value returned in a ListFiles()
    guest operation when the results are truncated.

commit 2f759e610c5ceabe420df9f678d23474dd423df4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:37 2022 -0700

    Move deployPkgFormat.h to lib/include/deployPkg.

commit 90445a78a9d34da3b651fcc9ca7e967d8c397fda
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:37 2022 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 7ac0e26580dcc2e2e1311ab16fb937da2a2e4e72
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:36 2022 -0700

    Inclusive terminology - "rule of thumb"
    
    Update bora/lib/public/fileIO.h to use "general rule" instead
    of "rule of thumb".

commit 521eecf6eb591c7ef6106f9f44eee728c14a5795
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:36 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 83e3f82592ae15cc2d213591693d5684a8c88b5f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 12 12:40:36 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 2a7556c850fae76f830a1805f4dfbdf9f49e3899
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 11:18:00 2022 -0700

    Update ChangeLog with the granular push of Sep 21, 2022.
     - plus ChangeLog update of Sep 13, 2022.

commit ce6d6d1abd652c766de597120b5a19c65a9a2f3f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit c0002b9761a0fc18e363d1e414f9330d1acab950
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Add an example of a new deployPkg/wait-cloudinit-timeout setting to tools.conf.

commit e210b4f0b28f7411ea2e45d4304725d0679481d2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Inclusive terminology - "suffer"
    
    Update bora/public/backdoor_def.h to not use "suffer".

commit 045cc568c053096ca2d5e142787ab039c03ba654
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 1d13616f84f10e836cb589a1bab62d8ae3331c7d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 7d4360383274807b488fcdf16ca6844ad27891a8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 7ec8864176d51356ea5bdb6713496d1770aff2ba
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit e49ffc8d24257a4c1568e3a36d1f001a16b9d4cc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit e873df9b63d4b16239f15dd0a4abfc7c1375e936
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Sep 21 10:53:28 2022 -0700

    Change to common source file not applicable to open-vm-tools.

commit b5329468f4c2115d57ccfff54d0b4e9c703df33e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 11:04:06 2022 -0700

    Update ChangeLog with the granular push of Sep 13, 2022.
     - plus ChangeLog update of Sep 8, 2022.

commit ab5b5fa4453fd1e1998d8571cab30b2b590053ba
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:54:06 2022 -0700

    Add missed 2022 copyright.

commit 7de3c1c208596a6dfaa967d9bb3b58d7f77c8671
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:15 2022 -0700

    Record the usage of open-vm-tools version 12.1.5.
    
    Update bora/public/vm_tools_version.h with an entry that associates
    version 12.1.5 with the tag TOOLS_VERSION_MITER_SAW_UPDATE1.

commit abd63f3785f8e4ec5384f55021b070be22c7cf8f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:15 2022 -0700

    Add a null undo function to the vmbackup null provider.
    
    If a snapshot operation times out, vmbackup can attempt
    to undo quiescing.  Since no quiescing is done for the null
    backup provider, no undo function was provided.  If vmbackup
    attempts to call the undo function, it dereferences a garbage
    pointer resulting in a segfault.
    
    Rather than add null backup provider specific checks to vmbackup,
    this change adds a null undo function to provide vmbackup with a
    valid function pointer it can call.  The new undo function updates
    the vmbackup state machine state with a new currentOpName, but
    has no other effect.  currentOpName is set to the calling
    function name, e.g. __FUNCTION__.

commit 42437c1131ee990737986d3fd5248bd17ec3e5ff
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:15 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit f1e30c3cb3b698a91de4966206350df2e08be128
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:15 2022 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 484ab8d8443ac6dd42f2ba7aabae4a7b44eda2e3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:15 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 8b98bd41def988342c16a870a4e7e880885dc81d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:15 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 6d0cf2442fb9ec26be9528952e871c35019a1854
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:15 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 064033a0ea74599d93f2a9988e0331efbe8f981f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:14 2022 -0700

    [deployPkg] Increase the maximum timeout for cloud-init execution to complete.

commit f3979ace5779c481073004968d6d9787d24c1096
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:14 2022 -0700

    Record the use of VMware Tools version 10.3.26 in vm_tools_version.h
    
    Tracking the use of 10.3.26 for TOOLS_VERSION_JACKHAMMER_PATCH13.

commit 22f7f76e19c906890f5cd35bde1a43ae90e77f79
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:14 2022 -0700

    Make Linux perl based customization work with the cloud-init workflow.
    
    To resolve issues seen where users want to set a vm's networking
    and apply cloud-init userdata together before the vm is booted, the
    deployPkg plugin has been modified to wait for cloud-init
    execution to finish.  This allows cloud-init to finish execution
    completely before the customization process triggers a reboot
    of the guest.
    
    This change is solely in the deployPkg plugin side, so a user can get
    this change by upgrading their open-vm-tools in the guest/template.

commit 15e8c9828bcef3eb41e7655b0f18c575dc495b25
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:14 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit a2cb7be60dbaa2b632872b37e187570a92fe6b2a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:14 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 1dbbb6e91e5388a88fe063d61d0cbb9602fb75a1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 13 10:31:14 2022 -0700

    Address Coverity issues.
    
    base64.c
    - buffer overrun (out); false positive
    
    hashTable.c
    - expression always false; false positive

commit 587be32ec29a3665e30f7509d3d3c8f8ada505e7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:57:36 2022 -0700

    Update ChangeLog with the granular push of Sep 8, 2022.
      - plus ChangeLog update of Aug 21, 2022.

commit 29b92ada5842c2b9d1cde7dbc9dafb69d6a18b96
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    Change to common source file not applicable to open-vm-tools.

commit 775671dc79aa47d82df2ad43e63e18d215625510
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 95b78cf63ea7dc37b6d9bd5b0f37cd074359ed0f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 8fa7eef2d27971753d9d6d1dd4fd5bd1a7a3dd17
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    [dnd] Coverity Fixes
    
    dndCommon.c
    - truncating time value; false positive

commit 900e24d53bca579d8ad6b12687daf670fb7fe5a6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    [asyncsocket] Coverity Fixes
    
    asyncsocket.c
    - dereferencing null pointer (asock); false positive.

commit 8aa0bc98bf2986b47800dd7eaf544e7e800a075e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit d3a4f33d29db6050ca84f504c9d3a557484e4a7d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    Common header file change not applicable to open-vm=tools.

commit bdca47456a592040474b21ad0db40f820dcc87f9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 2875dd33aa494879103b9bf6a635781a99c1292a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    lib/rpcin/rpcin.c: Address issues reported by Coverity.

commit bbf4831bcfdb90428dae3aeee2304e5e16f7c7a9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    Change to common source file not applicable to open-vm-tools.

commit d379cf84e479c11f4c3b470130a27f22005ab56d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:40 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 8a8bcb4ad1773fd62ad5a8bee352bf6ed34946a6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Move HostinfoProcessQuery() from hostinfo.c into hostinfoPosix.c.

commit c6de70b61177943130040563243ba6f671a0556e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    L10n drop for open-vm-tools 12.1.0.

commit b6c4eca1f575a6f116a3ce9e1bbb6e99dd134d33
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 53f331ccbbbc57eeaf4dfc51c94ec6f3e9a31a65
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 662eb2af666ca75532777ac28a11e29df1a6b413
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    lib/file: Speed up FileSimpleRandom.
    
    Also remove potential lock rank violations.

commit db405bfe35567f1cafaff49c623d1f5568baea58
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Bump the open-vm-tools version to 12.2.0 in the "devel" branch.
    
    Setting the development open-vm-tools version to 12.2.0.
    Adding the tag name "MITER_SAW_RELEASE" to tools version 12.1.0

commit 366096f02bbaa95e057ed89a5de5c05a1168d23c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Record the use of VMware Tools version 12.0.6 for an express patch.
    
    Adding the use of version 12.0.6 to lib/include/vm_tools_version.h.

commit 948eb98761b2662bceda35791a3be420391ebec3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 7066a131959f212a71e678f7538159b5f421ffc1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit a2299e2022718f7fd833714a2252252c07249745
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    lib/file: Clarify why File_GetSize opens and closes the file.
    
    Spell out what's going on.

commit d1f0cb0906d3a479fd3a3320ea202d96134fb16d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 3f426fb27d85e71e10bb3264f622fdca0d1f8cd7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 7991ed9acfe0917bc8642a48e7db82db5c779277
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 365db99d442774c49234ae157fc4373d0bb6c4f8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Sep 8 14:51:39 2022 -0700

    Common source file changes not applicable to open-vm-tools.

commit 732bb7a3287ebdb2be079f364da4b0ea6a4c08bd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Aug 21 12:42:12 2022 -0700

    ChangeLog update of Aug. 21, 2020

commit 7fa57304ab41a26f1de972f4a024e7da3632df69
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Aug 21 09:37:23 2022 -0700

    ====================================================================
    The "stable-12.1.x" branch was created from the "devel" branch here.
    ====================================================================
    
    Update ChangeLog with the granular push of Aug. 21, 2022.
      - plus ChangeLog update of July 29, 2022.

commit 70a74758bfe0042c27f15ce590fb21a2bc54d745
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Aug 21 07:56:49 2022 -0700

    Properly check authorization on incoming guestOps requests.
    
    Fix public pipe request checks.  Only a SessionRequest type should
    be accepted on the public pipe.

commit 08cac55b2a37fac30adfb090748ca2b96c3256db
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Aug 21 07:56:49 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit a4f13c6a3427a2a9366945c817222c12c957f5af
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Aug 21 07:56:49 2022 -0700

    Address issues reported by Coverity.

commit d2b1bb8fd315124de4cdcceb5b82b2e5b2882f30
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Aug 21 07:56:49 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 581091addcbb6484047587f6d12038a04f2cc14a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Aug 21 07:56:49 2022 -0700

    Changes to common header files not directly applicable to open-vm-tools.
    
    ARM: native atomics

commit 37aefc9db67398e04f4bd9017af69edd16670080
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Aug 21 07:56:49 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit ff29587e22711489207d020794b8f588058dfe52
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 29 15:50:29 2022 -0700

    Update ChangeLog with the granular push of July 28 & 29, 2022.
      - plus ChangeLog update of July 12, 2022.

commit d5b73e64a5857d40905acee1242edaaac7ce9494
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 29 11:11:04 2022 -0700

    Correct missed 2022 copyright update.

commit 1bd202e0a819c520896c743b51f2c1365f7ae2b2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:43 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 306498ca0676354eb541b5817426191e615d0ef2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:43 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit b3f0be90d03a5da6d3bb695e9e1dd44f071814aa
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:43 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 5887bf59e95cba480568ad731f7b037894058fef
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:43 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 9982eee37884c819759aadcec987324d9fe5b41e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit b5997ab6ecdbc6981a8a35b2349b3bd98c746a19
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 24594a971f9807fbee2253ee7680846de6cd9a63
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit bc9ab15472ed53a3071902ca5ec41d60e2f00d88
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:42 2022 -0700

    lib/misc/hostinfoPosix.c: iopl(3) is not avaiable on ARM.

commit 6907610abb95f96c3e8a10389483bfe54efd3eee
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:42 2022 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit b76e3218ddb24e49a87ef35913f9bf1389166033
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 155a8d440df8ae889d93c9aaee9d15bec9d7640d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 7452011db95a49aaaa19c293fb1ff1ea0944d6db
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 28 20:03:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 386e493c3cd58723d183ecbd3f0594706778e423
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 10:49:28 2022 -0700

    Update ChangeLog with the granular push of July 12, 2022.
      - plus ChangeLog update of June 7, 2022.

commit eb48b7972c0c64735cb6fe7ca7ac3f505b80c7d4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 10:25:28 2022 -0700

    Correct missed 2022 copyright update.

commit ddfb569588d8b1a9cd31cb3bb5d33c58c72f7fbc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Fix a bug in CodeSet_JsonEscape.
    
    Fix a bug introduced in an earlier change in which CodeSet_JsonEscape
    erroneously tests the first character in the buffer when checking
    whether to escape the character currently being processed.

commit a816c2e149010bdca27a47006932aa10b686a895
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 6aa2e661a35b88f0f69e744b4918340437cd6485
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Fix a grammar error of c++11 scoped enumeration "enum class".

commit e5e9a9ba31791d4f10e7dd75a8dbab179926511f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Escape all control characters in JSON.
    
    Update CodeSet_JsonEscape to escape all control characters between
    U+0000 and U+001F as required by the JSON standard.  Delete
    CodeSet_Utf8Escape as it is no longer used.  Also update datasets
    in-guest API tests accordingly.

commit 47f47851a026cce6ee9738015759b45cf6931cc2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit bb2f24ed14f8d5102df6387253ba4ac4271bf4f5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Changes to common source files not applicable to open-vm-tools.

commit f45b8f435f2d9b3536f283098ddaf69218ae7456
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Add a comment with the bump of the Guest Msg Channels to 128.

commit f272162cad49f13dc2d86eafb9aec7f0c2c7c8b4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 523df70f7f1b0ef760fe753404a0682b79b095b8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Bump up the Guest Msg Channels to 128.

commit 9e371bd289f53585e67853a97328906c196af818
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit d5b28c0380fb6687fd106e6ebd65570473940878
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 42c136b5a3cddb077b5c39d933141e30d4c2c129
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 091d43b06052d742e321d3f61c8620284d56b959
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit df0099727e96292279398c3fbd7f3901f215e4f3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common source file changes not directly applicable to open-vm-tools.

commit 5317dfb778ee97c6fe83fbf23ee2e800fb608eaa
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 12 09:56:01 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 575f1ec1bee338c1a0ac6f14072429f926cf2f52
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 10:07:23 2022 -0700

    Update ChangeLog with the granular push of June 7, 2022.
      - plus the sync of the README.md with the stable-12.0.x branch.
      - plus ChangeLog update of May 17, 2022.

commit 95956d193464f2aa0547e6dbd4f51bc2052d5f82
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit dd2f50d4cc956d5d45638d31f68c449ba8afbca4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 9a38d3f49bad645826aad7f0cb778098ba6ca381
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:42 2022 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 0f78b84aeedab75e3614bfdda9fe5fd86268686e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:42 2022 -0700

    Log Facility: Improve wording of group level messages and filtering.
    
    Common header file change not directly applicable to open-vm-tools.

commit 41509dbb7223fa7e8905718c357fbd6f243f06e3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:42 2022 -0700

    Report consistent customization error status when custom script is disabled.

commit f83c890a91e29febbe54a7be0ffe96c1e14f8057
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:42 2022 -0700

    Guest OS: Prepare for Linux 6
    
    The basics are already there, update guest identification.

commit 0b6597b97ce0d1607cb0602731b9c2d4ca0c5162
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:42 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 9945bb6cc3aa6d811ef46d9ddf2c2ae462baa38d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:41 2022 -0700

    Common header file change not applicable to open-vm-tools at this time.

commit d669d0263887a3da2242d55bae7eab224981e075
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:41 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 7fa64a4e23e65224b1752e5037854fd6f9ea43cb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:41 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit cff50b62e0db5f5978c14bb09b0e2fe7c1c89fc3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jun 7 09:52:41 2022 -0700

    Log Facility: Enhance logging for grouped (named) log message.
    
    Common header file change not directly applicable to open-vm-tools.

commit 939545b3e0445858cf7be038c978e7df2d8547ff
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed May 25 05:55:52 2022 -0700

    Sync the README.md file with the version released with 12.0.5.

commit 4c620fd78b6281ca8a045f3dffdf2180dd9c480f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:47:08 2022 -0700

    Update ChangeLog with the granular push of May 17, 2022.
      - plus ChangeLog update of May 6, 2022.

commit 29ba7eb2a1e6c62357ad46bc67daff94b0fc0dc4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    Salt-minion plugin update:
    
    The Salt Minion installer script svtminion.sh has been updated.
      - version 1.3
      - configurable download source
      - requires the "wget" command

commit e1a6796a1aecd22a7b76f5b4871d215b82277f18
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    Changes to common header files not applicable to open-vm-tools.

commit b42ed315b7844660e9f9546c7bd85e1804938526
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 074cd660a718e817e7db0df57f71252107d23490
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit ce9459965a1900159b5f563c030385e554ba3e49
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit bac24ee80957feea0336f7ac597630aedf13ec86
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 784c520757e883c5e46e1d3a593e24d171cd280e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    Common header file change not applicable to open-vm-toole.

commit 32db37a3cf437578efcb64d954aae9d5cca1ff83
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 619f656f4444af598179345f8c35e056e3563de1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 18:36:21 2022 -0700

    vm-support: Capture the locale configuration of the Linux guest.
    
    To aid in debugging issues related to locale configuration, the vm-support
    command has been updated to collect the following:
    
     - The contents of the /etc/default/locale file if it exists.
     - The contents of the /etc/locale.conf file if it exists.
     - The output of the 'locale' command for the current environment.
     - The output of the 'locale -a' command for a list of available locales.

commit 0c675d3d53f9e859eaa396cabfbf68c85a9f5f72
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue May 17 12:57:40 2022 -0700

    Sync the README.md file with the version last released in the
    stable-12.0.x and master branches.

commit e7d6abcbd3134e01cd5a8602fe4edd01b9e60aec
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:40:22 2022 -0700

    Update ChangeLog with the granular push of May 6, 2022.
      - plus ChangeLog update of Apr. 19, 2022.

commit 605c30a33f6dedc07ded8e24ce1491fba162ce82
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Fix a compilation issue in the containerInfo plugin for i386 builds.
    
    The "gint64" type used for time values in the code is not a "long" on
    i386.  The fix using the glib "G_GINT64_FORMAT" macro was provided by
    the Debian OVT maintainer.
    
    github PR:
    https://github.com/vmware/open-vm-tools/pull/588

commit be5c99cbb69afc1586315195f40d0e31d4f742b1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit cf19772adf10706fa1da83aca21a9fc45977c387
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit fde902600d26f8b38f13a7c535d094963f989697
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 0073b6ae4aed8466a2e657fe853cd993a60abd1b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Changes to common source files not applicable to open-vm-tools.

commit f9da69911d845d36515f8c45f6d5840f934a1189
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit c613b3d554c49fe8bdc6103d90aa028e7bee854e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 9b9f221c486069d90ed6460edab72cb545ca0361
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 82e373eb02505791f34b0154a8c2762040034fd8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:28:00 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 6df59bd95ec5a1a23be67483ef6a4c34cf849506
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:27:59 2022 -0700

    Change to common header file not applicable to open-vm-tools.

commit 875d19750952f575f6f41b6ce4631ecdbe74b90a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 6 14:27:59 2022 -0700

    Service Discovery: Cassandra service version missed for a default Cassandra installation.
    
    Updated the get-versions.sh script to correctly report the version of the
    Cassandra service running in the guest.

commit 6452e34357b1dc258a038ea5137fd27d13f9f4ca
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:48:06 2022 -0700

    Update ChangeLog with the granular push of Mar. 23, 2022.
      - plus ChangeLog update of Apr. 4, 2022.

commit f1e56311c8f245d6aba2a94d37e5df7220398ed5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 911b68fef82a3dfb083daa3bf0f61ed793e2961e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Common header file change not directly applicable to open-vm-tools.

commit c8f345b0cc50417665b0eff6bf19f0af2526ff74
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit b3c2a0e00e3d40e979dacab1833a6711e0e5620a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 19e2a77e0a2b29b77866876a5d45ec2df8f96d4f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 93af2bbbe583e95598cfb46d042ac6025a6dcac8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Improve the "don't touch this" comments for Linux guest identification.
    
    Pull requests to add distro identification information are often submitted.
    Open-vm-tools does not own the guest identification code.  Such a change
    requires coordinated changes throughout the VMware product stack.
    
    Improve the text to reduce pull requests for changes to common source
    code that open-vm-tools does not own and cannot make.

commit 36eea633611e678f3ea17a913c0990f319135c48
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Make HgfsConvertFromNtTimeNsec aware of 64-bit time_t on i386.
    
    The change incorporates the support of 64 bit time epoch conversion
    from Windows NT time to Unix Epoch time on i386.
    
    Addresses pull request:
    https://github.com/vmware/open-vm-tools/pull/387

commit 472335e347018fbddfbbe45618193075e77fa836
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit bb8a37981bfb76d9314d797c3ab1b394ce431d1d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Changes to common header files not applicable to open-vm-tools.

commit fb011741deec7e277f34974e32f88c60826e0db3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit d232afd908f22104a632e3060c9bf4170ac18018
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 1b4db25a65153bd2c1c5a725303bf22a61ac931f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Added the following miscellaneous checks for the deploypkg plugin.
    
    1. Check if the plugin is running in a VMware VM.
    2. Check if the plugin is loaded by the main tools service.
    3. Check if the underlying hypervisor is ESXi.

commit df63c64bf5e12a5f5bc0ba9bda4b786a4295ae5b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:54 2022 -0700

    Inclusive language - "kill"
    
    Address uses of the term "kill" in some files used by open-vm-tools.

commit e057e41fc4d4e4e084cf46c9d16243fb05a7df19
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:53 2022 -0700

    Inclusive Language - "whitelist"
    
    Use allowlist instead of whitelist in some files used by open-vm-tools.

commit 532cf9f3bb9dd3011239fb193f7458fc78ae921e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:30:53 2022 -0700

    ContainerInfo Plugin: correct compiler warnings in containerInfo_grpc.cc
    
    - The local variable "containersAdded" should be an 'unsigned int' since
      it's compared with an 'unsigned int' parameter.
    - Remove the unreferenced local variable "numContainers".

commit 92a6422d03a932ab92f4b7c791673cf2f17bf3b9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Apr 19 14:29:06 2022 -0700

    Reverting previous bad patch erroneously comitted.

commit 36b7f58392fbbb9e7bb336c0cf07202cba4d92cc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 13:22:16 2022 -0700

    Update ChangeLog with the granular push of Apr. 4, 2022.
      - plus ChangeLog update of Mar. 23, 2022.

commit 37f5400a44d1fbe4fdeef0fe048e8903af9f1907
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 8665272146a9302faf16c98992f57d5cce6530bc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    Common header file change not directly related to open-vm-tools.

commit 66644fb9b74bcad603a2e175e99be80b756bd6c8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    VGAuth: Add secure VMX RPC support.
    
     - Added some error handling to work around VMX closing a
       port that is being reused.

commit eef3be312ad6f7ed5d96cafa15ffafc89bc43851
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    VGAuth: Update some error messages with certificate error text.

commit b53ae31eaa20ec80f2d97f85ac6b9a295e21c287
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit bd8be34a9a4e897d78297eb8ae970300e80253a0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    DynBuf:  Implement new function DynBuf_SafeInternalInsert().

commit d59878b05d57393770f1cf064141a17c9a8af3f4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    DynBuf:  Implement new function DynBuf_Insert.
    
     - DynBuf_Insert(): Insert data at a given offset within a dynamic buffer.

commit 70509b63b728b65adfeb62afa40488782cc7f5f1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    Record use of tools version 10.3.25.
    
    Adding version 10.3.25 to the list of releases or planned releases
    of VMware Tools.

commit 33b7eb9f3f3b8d58025f484a9c5fd30293f60622
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit e7b3c7180fdf8f62b9852af90cdad725e69480da
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit 936537f6234690f8ae1afba3ae8b0a6a89df6625
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 4 12:58:41 2022 -0700

    Generalize VMX RPC code in vgauthservice.
     - Rework the code so that it can used for other RPCs in the future.
     - Rework the code reading the RPC reply, since it can now be much larger.
     - Also remove the attempt to use the "guest.log.text" RPC since the VMX
       will just drop the logging on the floor with no error if the virtual
       HW version is too old.  A smart fail over is not possible.

commit 2fe3cccfc1c36e946f46fb57813dd50c6c7f89dd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Mar 23 13:00:26 2022 -0700

    Update ChangeLog with the granular push of Mar. 23, 2022.
      - plus ChangeLog update of Mar. 10, 2022.

commit 405c57fbcfe8772679fdaf594b368f5a68b31edf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Mar 23 12:04:31 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit b167d985e0632d6455334e09437905ab54fa1a60
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Mar 23 12:04:31 2022 -0700

    Record the use of tools version 12.0.5.

commit 577764a4adf5e6ff595e23fe54c30ccdce353977
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Mar 23 12:04:31 2022 -0700

    Change to common header files not applicable to open-vm-tools.

commit f0cb3eebe942fba3f5abca32303998985a756b90
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Mar 23 12:04:31 2022 -0700

    Common header file change not applicable to open-vm-tools.

commit b571bf4026b6f2dee31102a55dd7338c276646f9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Mar 23 12:04:31 2022 -0700

    VGAuth: Customize libxml2 to open local file with unescaped file name only.

commit c0408788b07ec42f8c1bcf3e7373032d8b9784b3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 10 12:06:56 2022 -0800

    Update ChangeLog with the granular push of Mar. 10, 2022.
      - plus README.md updates related to 12.0.0 OVT release.
      - plus ChangeLog update of Feb. 17, 2022.

commit b47748c330f1fa495f099db0d6fcc300f9e82240
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 10 11:36:24 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 6e16f34ac546f5cdf156cc484b29823b84603f76
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 10 11:36:24 2022 -0800

    Changes to common header files not directly related to open-vm-tools.

commit e57a78d2fa4afe47b1a5d2d12d4e0a4a71db1073
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 10 11:36:24 2022 -0800

    Service Discovery: Script correction to collect the version of Cassandra.

commit dfff48a686515a5046d8e33b8181f34f79fe6fa6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 10 11:36:23 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 092cc7135b6d0f5fc4dbbf8b14a3093dbee402d8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 10 11:36:23 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 68391545c1c7ac7804429f7dc73a158d74690b82
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 10 11:36:23 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 69ce810007b0390a369425bc3561ab7befdb040c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 10 11:36:23 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 97440ff3bc72428be0158cada3afc2513ead463a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Mar 2 16:49:49 2022 -0800

    Update the README.md document.
      - Adding configure and build information for new plugins and service(s)
        added in the 12.0.0 tools release.

commit 09ea13cfb0947137dd31cd1f278739e49aff95fc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Feb 22 16:52:02 2022 -0800

    Update the list of operating system with open-vm-tools.  Add:
     * Flatcar Container Linux, all releases
     * Rocky 8 and later releases
     * AlmaLinux OS 8 and later releases
    
    Addresses:
      https://github.com/vmware/open-vm-tools/pull/573
      https://github.com/vmware/open-vm-tools/pull/513

commit 43d17bd3f2e93566ee7b588494d29ab857c7a95f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 15:10:47 2022 -0800

    Update ChangeLog with the granular push of Feb. 17, 2022.
     - plus ChangeLog update of Feb. 7, 2022.

commit 2cf575e2fb0a6c83211e8e09339491889bdcc8fe
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 15:01:31 2022 -0800

    Add missing 2022 copyright.

commit 66b79830eccb5fd8c7b85f07b8090c763e12783c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 14:51:25 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 27bd6b5a84370ad488227614df3740ad6c2014db
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 14:51:25 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit bb597ad25dbf695b5d16ddea3c09080d1a715967
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 14:51:25 2022 -0800

    Backout previous changes to common header files not applicable to open-vm-tools.

commit 92254389f42b0a09ea140427afb4d0352a56cb4b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 14:51:25 2022 -0800

    Adding configuration information for the GlobalConf feature.
    
    As part of the GlobalConf feature, a "globalconf" section is introduced
    into tools.conf to provide custom configuration options for the feature.
    The configuration parameters are as follows:
     * enabled=false - Enable/disable the GlobalConf module.
     * poll-interval=3600 - Poll interval for the GlobalConf feature.
     * resource=<path> - Defines the location of the tools.conf in the
                         GuestStore.  There is a separate default for Windows
                         and Linux guests.

commit 1b5114ab87e342af85eb340237ba2d747b49fdad
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 14:51:25 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit f35987b39f2874f13b2f4facd1b1d3dde7ae2a63
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 14:51:25 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit de6d129476724668b8903e2a87654f50ba21b1b2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 14:51:25 2022 -0800

    asyncsocket.c: Use size_t in place of int type for array size and indexing.
    
    Glibc 2.35 with GCC 11 and 12 produces additional warnings about strings
    and array bounds.  Switching from "int" to "size_t" type for variable
    used for the array size and element indexing.
    
    GCC warned when an integer value is passed as the size of the
    struct pollfd array to poll().
    
    Fixes https://github.com/vmware/open-vm-tools/issues/570

commit 956fd221b6697242405bc3c1f8fe5c18236a9b68
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 17 14:51:25 2022 -0800

    Changes to common header files not applicable to open-vm-tools.

commit 760d6ca22de590ffcce82c43926d8b91474cf101
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 10:01:44 2022 -0800

    Update ChangeLog with the granular push of Feb. 7, 2022.
     - plus ChangeLog and late copyright update of Jan. 20, 2022.

commit 8406802f4bf9269f02b6850b10741a7fce1635bc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:52:43 2022 -0800

    Correct missed 2022 copyright update.

commit 4311a4a5d2952703cb808feb1f6372e28c2afc68
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:01 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit e9e485b9d6fd41acb5a777896b7c7efb32632722
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit a085da930a078f956192bb257de251a58e49a5f2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 5b0c9eada72482a7149a5188f11d470272e53087
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 8692a2327c0bcd4608fd40d8f7539da5d82e6b28
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Enable time step correction log in all builds.
    
    Enabled logging of the information in all builds of open-vm-tools.
    This is useful for auditing and for debugging.

commit 87389ed6975ed40bdc7883c64c2509da6650a435
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 05943d86fbc47821c3036eaa3b11b7b7a1eb2780
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit d8ad6f5c0d2237ed6eb3230720be9701547e2ed9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Service Discovery script update.
    
    Updated the get-version.sh script to retrieve the vRLI version.

commit a9e72b211bafb19ca0e674bf73351e1aa859d083
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 5a040d4c25705565e8397b2135506e93e320bd54
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common header file cahnge not applicable to open-vm-tools.

commit 627f748ab314c6bd2b6a5023d1f17684bd8eec0d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common source file change not applicable to open-vm-tools.

commit 2290690693f0590bb5ffbd592ebd4b8062d57513
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    AppVisibility: Skip terminated/stopped containers.
    
    Added code to skip terminated/stopped containers.  This is done
    by using TaskStub to retrieve the pid for the running container.
    If the pid cannot be retrieved, then that container is skipped.
    
    The previously suppressed "default" namespace has been restored.
    
    Fixed another minor issue with calculating the number of containers
    that were added.
    
    Note: This changeset introduces a dependency on Tasks.proto, and
    Tasks.proto depends on a few other proto files like mount, metrics,
    descriptor and task.  Modified the makefile to generate the necessary
    C++ files from the proto files.
    
    Additional notes:
     - .proto -> generates .pb.h, .pb.c and .grpc.pb.cc files.
     - For compiling and building the library (.so file), the .grpc.pb.{h,cc}
       and .pb.{h,cc} files are needed.
     - The protoc compiler generates .pb.h and .pb.c files when the --cpp_out
       option is specified.
     - The protoc compiler generates .grpc.pb.* when the --grpc_out option is
       specified.

commit 5e79bc1414f31a07a9fb74c624b8b435546e0212
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common source file change not applicable to open-vm-tools.

commit 75f3a3e2e6b4cfec157c02e8f06e5ac139ad98cc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 7 09:40:00 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 192667e6e65ad7d7592798ef567278dbe15c293d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 15:01:49 2022 -0800

    Last minute 2022 copyright updates to several files.

commit 99c00042476803def0d1aea4fc28cd59e271859d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:50:29 2022 -0800

    Update ChangeLog with the granular push of Jan. 20, 2022.
     - plus ChangeLog update of Jan 2, 2022.

commit bd0f9506e5ad3ee42c95cbd1aea0c9381e4abf59
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit ddd97d710b1b49a2d8b3679ac07c70feacb2aeb0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit b4a4387294cc9dc3cb890093398abad4d0800bc2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change.
    
    Prepare to recognize macOS 14, Debian GNU/Linux 12, Rocky Linux,
    and AlmaLinux.

commit cbc229407f734a7b44aeecb9e3a6a51975ace35e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    open-vm-tools-12.0.0 L10n drop.

commit e85e58885523f69b01fc8a49ee40732df6b6e2a6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    ContainerInfo: Remove 'default' namespace from the default allowed namespace list.

commit b9ebabe69f0d864bfaa28bf8773a4c8a51e29416
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Changes to common source files not applicable to open-vm-tools.

commit e2de0491601d6c14e5aa4b64467cced413ff28e6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 28dd7a83e35173ac7d1869906442ded5d1c632eb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 750eed3fc4dc6d0265bc3e28b845e76ca3acf944
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit b59acd69c499825b1e0cc451f3c3798b1bb18d1c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 0f084152e3e03d22dcaa5d80118f244dba3f1455
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Set tools builds on the "devel" branch to version 12.1.0.
    
    Also, add back the definitions of TOOLS_VERSION_NEXT* inadvertently
    removed in the previous change to vm_tools_version.h.

commit f48b8797c14d43b3786fddae8bd38b890e48cd9d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit 87396d0925299645ce77491ca8ad30ad1cb01c85
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Restrict Enable Salt Minion feature to x86_64 systems.
    
    Currently Salt Minion is available only for x86_64 systems.  Update
    configure.ac to exclude ENABLE_SALTMINION from ARM and 32-bit systems.

commit 9c171d9aede372e9647fe05f09a0e127df6d1dbd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jan 20 11:40:15 2022 -0800

    Common header file change not applicable to open-vm-tools.

commit bc9832a87009a1d41729ea6d07d1fae960026655
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Jan 2 02:20:56 2022 -0800

    Update ChangeLog with the granular changes through 12/31/21.
      - granular push of Jan. 2, 2022
      - plus ChangeLog update of Dec. 25.

commit 34415b186a26a563690025d50b582cb7a73aef77
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Jan 2 02:10:04 2022 -0800

    Set SABRE_SAW as the code name for the VMware Tools 12.0.0 release.

commit e52bbb643aebf0a414d00c519cbe748a378d1570
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sun Jan 2 02:10:04 2022 -0800

    The header added to the vmsvc log file is repeated at log file rotation.
    
    The log entry with the tools version, tools build number and guest OS
    details added at the start of the vmsvc log file is repeated when log
    file rotation occurs.

commit c4220766355a623901f3d562ad03ddd3745729fd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 25 10:10:54 2021 -0800

    ====================================================================
    The "stable-12.0.x" branch was created from the "devel" branch here.
    ====================================================================
    
    Update ChangeLog with the granular push of Dec. 25, 2021.
     - plus ChangeLog update of Dec. 21.

commit f6d49e0acd81353d8022b639e231bc517a69b166
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 25 09:51:13 2021 -0800

    Common source file changes not directly applicable to open-vm-tools.

commit ff6e8264fedfc3237ab66b91598d998bfa7eae39
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 25 09:51:13 2021 -0800

    Salt-minion and componentMgr plugin updates.
    
    Lastest version of the Salt Minion installer script.
    The poll-interval of the componentMgr can be set to a minimum value of
    five (5) seconds when compiled with -DVMX86_DEBUG.

commit f0b616649db5cb3c7a5251cd981401bbf74039c7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 25 09:51:13 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 3c3cfcc9a9d16e79a9ef886f2831a7be28e9e596
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 13:19:21 2021 -0800

    Update ChangeLog with the granular push of Dec. 21, 2021.
     - plus ChangeLog update of Dec. 4.

commit 7987a3440f0d05b18faa77b9f9f67ae55d8a1cf1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 13:07:55 2021 -0800

    Update missed 2021 copyright.

commit 4c66981405127910285d182930e049c4fb496a7a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Change to a common header file not applicable to open-vm-tools.

commit cc72edfdd60497b86b8e7f20495c85ce414d6106
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Changed the log domain of the containerInfo plugin to containerinfo.
    
    This will make both 'config group name' and 'logging domain' name the same.
    Did some code-reorg.

commit 97917fce5828ef0efdae59d7bb6e4dd7f0caeae0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Update open-vm-tools to build with either Fuse 3 or Fuse 2
    
    Vendors are requesting that open-vm-tools can be built with either
    Fuse 2 or Fuse 3.  While both Fuse 2 and Fuse 3 runtime can be
    installed on a Linux system, vendors would prefer to switch from
    Fuse 2 to Fuse 3 at the same time for all products to be available
    with the base OS.
    
    Updating the configure.ac file to check for the presence of the Fuse 3
    or Fuse 2 development packages in the build environment.  Providing
    configure options to allow users to control the version of Fuse to be
    used.
     --without-fuse       - vmblock-fuse and vmhgfs-fuse will be disabled.
     --with-fuse=fuse3|3  - use Fuse 3.x
     --with-fuse=fuse|2   - use Fuse 2.x
     --with-fuse=auto     - check for Fuse 3 or Fuse 2 availability; disable
                            vmblock-fuse and vmhgfs-fuse if unavailable.
     --with-fuse          - implicit "yes".
     --with-fuse=yes      - check for Fuse 3 or Fuse 2 availability; disable
                            vmblock-fuse and vmhgfs-fuse if unavailable.
    
    Pull request: https://github.com/vmware/open-vm-tools/pull/544
    Fixes issue:  https://github.com/vmware/open-vm-tools/issues/314
    
    The vmblock-fuse code is also used by WorkStation.  Configure defines
    are not available in internal builds.  Reworked preprocessor tests to
    use FUSE_MAJOR_VERSION from the fuse headers to determine API to be
    used during compilation.

commit d0571b23fe1513c0ec83e8b57bd8e91edd5b86a3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Changes to common source files not applicable to open-vm-tools.

commit 7776c845db0bc24c5103d80a8d8f76d73f79ec39
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Change to a common header file not applicable to open-vm-tools.

commit ec4040bfc25818e5979f8c681f13b4c0e3d9c38a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Changes to a common source file not applicable to open-vm-tools.

commit 3695f042018aedbca6d37751d3d1577bf1887aa1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Common source file change not applicable to open-vm-tools.

commit 7cb1006b6f362a1e19b6dbaea5ae30bd727d4f5b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Fix potential integer overflow when calling g_timeout_source_new() or
    g_timeout_source_new_seconds().

commit c69332a9252413b265b4a146c072bcae8b16a3d3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 1c916a09107b2b6c901488587711db569ade9223
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Changes to common header files not applicable to open-vm-tools.

commit 8296854e348014441368af01edc41c728b80de86
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Clean up of guest OS tables.
    
    Use official defines for Arm and guestOS strings.

commit d5a3e4b02c5515cde9f10e351b67dade9c073d6e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Remove the collection of GRUB files from the vm-support script
    
    GRUB configuration files have not been needed in the past. Therefore, as part
    of this change, removing the code from vm-support script which collects any
    grub configuration files.

commit 63abb4f4b0fe73205888bedca91385faf3f53bf7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Adding Alma Linux and Rocky Linux to tools guest ID code.

commit 89d3e8d9560708be308b0511bbacece903651790
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Refactored the componentMgr plugin code following standard practices.

commit d33c816998b8680b67b39a096f44e95d5019e1ab
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Adding Alma and Rocky Linux for x86 and Arm architectures.

commit dce3b03c433e4ff77df5703e97519e3b32c9cf14
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 32dd72b34033ba0f7eda8456a67bd45fee98c483
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Common source file changes not directly applicable to open-vm-tools.

commit 6b3e2e5ae06c77c122bc890a8ff8c3dede6368c9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    Changes to common header files not applicable to open-vm-tools.

commit ed84c0a175c0122555a6a7cc947590569d28becb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:50 2021 -0800

    containerInfo Plugin: list the Docker and Kubernetes containers in a guest VM.
    
    Added code for a new open-vm-tools plugin, containerInfo.
    - Added a new configure option --disable-containerinfo to disable building
      the containerinfo plugin.
       --disable-containerinfo : Will not check for any dependent packages and
                                 will not build the containerinfo plugin.
       --enable-containerinfo=no : Same as --disable-containerinfo
       --enable-containerinfo=auto : Checks for the dependent packages. If they
                                     are available, then the containerinfo plugin
                                     will be built. Otherwise, a warning is printeds
                                     and the containerinfo plugin will be skipped.
       --enable-containerinfo
       --enable-containerinfo=yes  : Checks for the dependent packages. If they are
                                     available, then the containerinfo plugin will
                                     be built.  Otherwise, the configure will
                                     terminate with an error.
    
    - Updated the sample tools.conf file with various settings related to
      the containerinfo plugin.
    - Due to an issue reported in https://github.com/protocolbuffers/protobuf/issues/9184,
      implemented a workaround by changing 'import weak ' to 'import ' in the
      .proto files while generating the header files.
    
    Build dependencies: (packages names may vary with Linux release).
                                    - or -
      - libcurl4-openssl-dev                      libcurl-devel
      - protobuf-compiler                         protobuf-compiler
      - libprotobuf-dev                           protobuf-devel
      - protobuf-compiler-grpc                    grpc-plugins
      - libgrpc++-dev                             grpc-devel
      - golang-github-containerd-containerd-dev   containerd-devel
      - golang-github-gogo-protobuf-dev
    
    Runtime requirements:
    
       - curl, protobug and grpc-cpp

commit b36b275bc8cd14c1e2e64f76e3b41307e24a165f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Set the "svtminion.sh" script version to '1.0'.

commit 53949064ee169cdba0925d99a1f9a75f63dbf39b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit b38a34acdf9ed3113e53f5e9f2c3cc0927df50eb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 308259f3e5e3630cd30bd04aa0fd0d4c952d7ff2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Added the componentMgr plugin to the vmtoolsd vmsvc process.
    
    The new componentMgr plugin manages adding/removing of components
    in the guest.

commit db3a1dc6c9cc771bf4c4d8a268b6747f167bcad7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Salt Stack Script Integration for Linux.
    
    Added the "svtminion.sh" script which will be packaged for Linux to
    support SaltStack integration and management from the componentMgr plugin.

commit 50500199ac9e36ee67deffe51ccd714a6a90ef46
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Activate the componentMgr plugin build and enablement of the Salt-Minion plugin.

commit 67383c5669650766fbdb18962ce05afafb6d0dca
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Prepare to enable Mac OS 14 (Darwin 23) as a new guest.

commit 3ca3a1d1ff4edb9ab6dbe5e30079167710ee5d06
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    filePosix.c: Increased the hash table size for better performance.
    
    Improved the performance of the functions File_ListDirectory() and
    File_WalkDirectoryStart().

commit 011d680184caa26fd4d4d6fe77f77a862aee0adb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Changes to common header files not applicable to open-vm-tools.

commit be8ec133fb755dec100de90d6f5d42dd2a56cf6c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 8b97e826ce53cbcd6ca5aa568906ffbe1b57f0d4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 21 12:48:49 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit de920bf8398bcf7a017a4b012e84cf98d67babab
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 4 23:09:31 2021 -0800

    Update ChangeLog with the granular push of Dec. 4, 2021.
     - plus ChangeLog update of Nov. 19.

commit 2cadef44dc8c156d8543111d62340212c501aa0d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 4 22:55:11 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 5fc921074bbf7eba8ee1827de7f12417769e30ca
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 4 22:55:11 2021 -0800

    Adding RHEL9_ARM and OTHERLINUX6_ARM to the ALLARM group.

commit b3cb33a91bb97a17b575b77fa9a622b1a9a74a74
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 4 22:55:11 2021 -0800

    Changes to common header files not applicable to open-vm-tools.

commit 8459c9bcda9b7901de0128d1cb594227b33eea1a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 4 22:55:11 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 611dce7461466fbde66466b08efed436c3c9b3cb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Sat Dec 4 22:55:11 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 9920a7e9141ae4910cc77b7891dd14607ee8a474
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 19 12:52:20 2021 -0800

    Update ChangeLog with the granular push of Nov. 19, 2021.
     - plus ChangeLog update of Nov. 8.

commit 28666842fb665b9039ea32a0de5602e6873ac442
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 19 11:40:50 2021 -0800

    [AsyncSocket] Avoid a potential NULL pointer dereference in a log message.
    
    A log message in AsyncTCPSocketListenerCreateImpl() accesses an error code
    through an optional parameter.  Introduce a local error variable to
    capture any error code from called functions and have it available for the
    log message.

commit 342fd81a270bf36c79baec5bb928a9a979e95af2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 19 11:40:50 2021 -0800

    Correct the guestFamily reported for RHEL 9.
    
    RHEL 9 had not been added to the ALLLINUX macro.  This has been
    corrected.

commit 36514e38f5e31a74ef2e38cb13cebd054a52179d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 19 11:40:50 2021 -0800

    Common source file change not directly applicable to open-vm-tools.

commit a91770b88b3d6f534944867f9c58f982b3f6c37a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 19 11:40:50 2021 -0800

    Change to common header file not applicable to open-vm-tools.

commit 9997bb0c311e5d931d53f1a4328ced07ec911b77
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 19 11:40:50 2021 -0800

    Change to common header file not applicable to open-vm-tools.

commit e4b6231aab74fcc51cf6a22848d9512605be8d28
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 19 11:40:50 2021 -0800

    Change to common header file not applicable to open-vm-tools.

commit 44f883c59b11912eccfc67df5a9723a2848433b1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 14:03:23 2021 -0800

    Update ChangeLog with the granular push of Nov. 8, 2021.
     - plus ChangeLog update of Oct. 18.

commit 819a20bd5fd2d4f84ae56183d2b2fa4fc88dd72b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:58 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 53a3387f9bb7f15fd504d6a44c4d8608c1b6ef56
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:58 2021 -0800

    Code clean up.
     - Fix assignments of pointer to constant data to a non-constant
       pointer type.
     - Remove code that has been commented out.
     - Updated treatment of pointers as a boolean expression to be
       a true boolean expression.
     - Fixed some formatting issues.

commit 04515ad7b6f3291af6236ef492de8bfb927d3151
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:58 2021 -0800

    Update open-vm-tools 12.0.0 to work with openssl 1.1.1 or 3.0.0.

commit 0186a8af01f1dd09090cfcd1ddd5487d60c1026e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:58 2021 -0800

    AppInfo Enhancement: Remove duplicate applications.
    
    Update appinfo to remove duplicate applications from guestVar.
    Add a "remove-duplicates" tools.conf key, which is enabled by default.
    Setting "remove-duplicates=false" disables the functionality.

commit 899063f29c498d1a0abd7ffce71ae05260a5555d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:57 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit aaaefaf7e0cc39bc7ec1c7f89069f5bf96baf5ca
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:57 2021 -0800

    ServiceDiscovery: Enhance version scripts to discover vRLi and
                      Cassandra services versions.

commit 8a4e38f66325092f84ba612e614f6fd16e135752
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:57 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit ffbbc2932e8fa348e7177f3cf91c1085641a295c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:57 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit e501f711f59b30ce4d402d5bbc2361cb5b29f541
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:57 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit df85d1e571414fdecabc98492da53fe2950a8e6b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:57 2021 -0800

    Changes to common source files not applicable to open-vm-tools.

commit d40c8b6d3086f95980b41a1012639ae796e80fbc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 8 13:33:57 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 16e24c8d789f8ff81a9fbb915858cb6af88f66a5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:44:47 2021 -0700

    Update ChangeLog with the granular push of Oct. 18, 2021.
      plus ChangeLog update of Oct. 6.

commit d3158f108ec8119d270d08d613545b61f4a6d50b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 8407406335a8f1bc25a23497cdb5b880fc2db6aa
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit e9907529c68df57dbf1baa5c5b0872098fb3bbeb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 1ec43b3ef88719d8d3ad206e7a427c479c496e0d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Remove <CR> characters from saml-schema-assertion-2.0.xsd.
    
    The file, created with Windows <CRLF> in 2011, has been changed
    to Unix encoding.

commit ae7d374ee335277a637ec99ada728df7545f2459
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit f9bfc57babb4cc18e8fcada6f44c26d7dfca90ce
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit a45ded9333305f71eea2e0ab4cc144fc2efad74f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 6ff8e802b6a2fdf1595d032a1ab94b9f83a2f61b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 8f2a9bb928e5d41c308684e68ed68a742dfd8222
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit c9639690ec6d7bce96a7c06d0a05546b811d6b1f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit a1aa6abc127d872966415e5fd01a9e35db0f2ef5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 88de2de0f48cb0334cb8b57bca5ad016133d1674
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:44 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit fe739dc30671ab0d6de67acd1dde374f88fe499c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 18 07:31:43 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit c5bc4c37b8f948e3d9bf8d7e2f85196f5099d311
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 6 23:52:03 2021 -0700

    Update ChangeLog with the granular push of Oct. 6, 2021.
      plus ChangeLog update of Sept. 20.

commit 4aecc2978e09dddc1f7a0c9af7e9b8ffa57227e2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 6 23:40:31 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 02b9fea09af950780fdf6e93141edf97de9075de
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 6 23:40:31 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit d3147cbb1cec5b5ac41c1d1955f28a20d2bedf5a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 6 23:40:31 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 8929bd9db5fc57a76491d3f1d8e314955c7b5823
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 6 23:40:31 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit db093d15c263262632a854342b447c6b67e72394
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 6 23:40:31 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 3b586cc541a0ead1bc60a6b11ec655e10e83c29c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Oct 6 23:40:31 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit daf6e0fb8ac80cc8bd718b6564b037fc9eb6b654
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Sep 20 08:51:19 2021 -0700

    Update ChangeLog with the granular push of Sept. 20, 2021.
      plus ChangeLog update of Sept. 7.

commit fe059f34e4ab93f21c41b4598da6846eafd9bc20
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Sep 20 08:07:36 2021 -0700

    Change to common header file not directly applicable to open-vm-tools.

commit ffa5d491194412213882cdcc808d621b439ae2a6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Sep 20 08:07:36 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 03b6ca66c23289da7b34d1e47f5769d063ee3ad9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Sep 20 08:07:36 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit a3e4fd4e4692e36b5b66b5feb4044faa41d46f22
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Sep 20 08:07:36 2021 -0700

    resolutionKMS: Add support for the svga3 device
    
    The resolution plugin was checking for only the svga2 device, which
    does not exist on ARM.  This makes the resolution plugin work on ARM.

commit 21f193b21e3d2be122a6ee9755a45536671cf4e9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Sep 20 08:07:36 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit fe8803ea205da66e8a5040f447835fd2292de89e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Sep 20 08:07:36 2021 -0700

    Log the guest OS name and full name.
    
    Added a log message for the guest OS name and full name that is sent as
    guestInfo.  Messages are logged only when the guestInfo is successfully
    updated to the VMX log.

commit 7b58f891b283c4eb42602bba07f4713815a508da
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 7 06:49:29 2021 -0700

    Update ChangeLog with the granular push of Sept. 7, 2021.
      plus ChangeLog update of Aug. 23.

commit 9a793fb5399b36cb473c57eb3a41570adfccc078
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 7 06:39:53 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit d09c3d5247e21f0a881406a020e6cdf87ab0ee2d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 7 06:39:53 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit b963fdd5363d1ecd347045408cf1b40e5ac27188
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 7 06:39:53 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit ee2fe611e1c6f27d0e918a361a967f13da10ba08
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 7 06:39:53 2021 -0700

    Common header file change not directly applicable to open-vm-tools.

commit e551b81709ee4f42e79f2f4aaa47d9443e24a2fa
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 7 06:39:53 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit adb9c0476a8dd9d15947857343b23c6ffe9b395c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 7 06:39:53 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 6336dec45c73d60d8bd100ddc71f3b125a2a9cc9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Aug 23 20:22:35 2021 -0700

    Update ChangeLog with the granular push of Aug. 22, 2021.
      plus ChangeLog update of Aug. 12.

commit a6923583d4519019e25860abcd16c2205d8a3fde
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Aug 23 20:13:37 2021 -0700

    Replace the use of appInfo's internal json escape function with
    libmisc's CodeSet_JsonEscape().

commit c7a5a3e189494e44c16ed7c21ab0b4fac28b26b8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Aug 23 20:13:37 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 3bae54175a7a72872920ba62842a5091a87ae11a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Aug 23 20:13:37 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 61331a189a0eeb76f014db28288b06c0323bc0b9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Aug 23 20:13:36 2021 -0700

    Removal of the hgfsmounter/mount.vmhgfs command from open-vm-tools.
    
    The hgfsmounter (mount.vmhgfs) command is no longer used in Linux
    open-vm-tools.  It has been replaced by hgfs-fuse.  Remove
    all references to the hgfsmounter in Linux builds.

commit f5fb3a9fb32ea465f965aa971bc40bdff8edc36f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Aug 23 20:13:36 2021 -0700

    Further refinement of the toolsDeployPkg.log file.
    
    Modified the build info description with the module name instead of
    "imgcust" or "sysimage".

commit 1f971148e368b2df87cb20c74568494b7b543ced
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:57:20 2021 -0700

    Update ChangeLog with the granular push of Aug. 12, 2021.
      plus ChangeLog update of July 27.

commit 38f8606a3f4faeb0b8ae1f9277c9c1b50ac19bbe
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:05 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 574cbbe55abdc716a6af03a752260699c3e22784
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:05 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 5003c79aaf398fbdc965c510c1b0be2350be7e64
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:05 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit f254eef58523fc9539e3836d825056b999ff192b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:05 2021 -0700

    Reduce the scope of a local variable.
    
    The 'Codacy' tool reported that the scope of a local variable can be
    reduced in the Debug() function of linuxDeployment.c.

commit 63d9fd68f5513b683aae855d30a2da4d4e4f72a7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:05 2021 -0700

    Common header file change not directly applicable to open-vm-tools.

commit f39d4c1fe303d94a079f0d0e9ed098cde1cb0316
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:05 2021 -0700

    VmCheck_IsVirtualWorld(): assume VMware hypervisor if USE_VALGRIND is defined.
    
    Updating the VmCheck_IsVirtualWorld() function to assume that if the
    open-vm-tools build was configured with "--enable-valgrind", the tools
    will be used on a VMware hypervisor for memory leak detection.  In this
    case, simply return TRUE and avoid touching the backdoor.
    
    The "backdoor" touch test cannot be handled by Valgrind.

commit 1c31dbfc3df970bd6f4e0ad297f8d79e105a2690
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:05 2021 -0700

    Update VMware Tools version to 12.0.0.
    
    The next major VMware Tools / open-vm-tools will be version 12.0.0.

commit dd78c6abbb4f376afea1db423acaf483c4f56f32
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:04 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit e76f136563a8e78676f9c1e069175dec835eaade
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Aug 12 12:46:04 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit beee0873cbf0d0399d52cb762b2b3e85054c5774
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:54:08 2021 -0700

    Update ChangeLog with the granular push of July 27, 2021.
      - plus ChangeLog update of July 8.

commit 26e7c2f3b5198621c8234c4890431dc16698bcf3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit d78011e325c6275bc9199b102983544057dfc0bc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 8db9ab140d15787064745c4ba0497fc7d162ab84
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 4eb519b107745cd01018194aa059a5d0b39c3cf4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit c387b16795b4a78a53c89f607a8ca31580138e05
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 8259e2e5238e7b90c7f1ddf396ec47fc9acabfe1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Add tools version and build info into the toolsDeployPkg.log file.

commit 2fe6126e92f4bd4dc9eb683e59df84000b312398
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 8c788af23d0855f1876e8e6de665d050a157c557
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit cb8741edd1fe30dd423cf8533d63a4dc8bb39d8a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Terminology cleanup in some comments.

commit b873cda6c5bf0228b0442d1c3be402feafadf8c1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    GuestOS: Support Fedora and Debian 10 for ARM
    
    Since we support Fedora, support ARM Fedora - 64 bit only.
    
    Remove VCPUHOTADD from ARM guests. There is no support for this.

commit 3a599078d238c12c26d3ca3e39c743f7d9c65555
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    guest_os.h: A bit of clean up
    
    Arrange things in a nicer way.

commit 61e3c68f029af528e61de5b22b215ef689870be2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 27 10:37:24 2021 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 3226203c1c49e8d0fca9aa6a8557ac757c91c3a8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:35:44 2021 -0700

    Update ChangeLog with the granular push of July 8, 2021.
      - plus ChangeLog update of June 30.

commit b033e5c2d77ee66e9426973005cf1f48a5b3cceb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Common header file changes not applicable to open-vm-tools.

commit c907662b1034bd783dab4728bec3af06892f6e4a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Changes to common header files not directly applicable to open-vm-tools.

commit e6ff175716602a91fa1385c30846961c57d089f2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Common header file change not directly applicable to open-vm-tools.

commit d21b1402ac2e1f59851560ac08516ee8ddaa13b0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 35c78d66e0202a5ce217193d03bd48a50ce87c3c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Added a configurable logging capability to the network script.
    
    The network script has been updated to:
      - use the vmware-toolbox-cmd to query any network logging configuration.
      - use 'vmtoolsd --cmd "log ..."' to log a message to the vmx logfile
        when the logginging handler is configured to "vmx" or when the logfile
        is full or is not writeable.
    Added an example configuration in the tools.conf example file.

commit b484ab6727bbfd8691e8dd4f8d6ae297e06a8672
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 102714ebe1d0253a7a2afb373f5d73d9d25687fb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Fixed the possible use of an uninitialized variable in Util_ExpandString.
    
    Additionally, several Boolean expressions were updated to be proper
    Boolean values, for readability and consistency.

commit 0ae179aa40ed68f3357faabbe6f101035206cd32
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Changes to common header files not applicable to open-vm-tools.
    
    Prepared the basic infrastructure for adding Windows 11 guests.

commit e1f850a1fa20a911abf9a3d7f2ca7ae6c2a44209
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Record the use of VMware Tools version 11.3.5 in vm_tools_version.h
    
    Add TOOLS_VERSION_JIGSAW_UPDATE1 for version 11.3.5 in the header
    file lib/include/vm_tools_version.h

commit d5605d3861c1dd8a5cf968b627b7adad2da98308
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 77196a4b4fe7edbdddab1adb402021979f224a0f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 38777a57a1e9befbb5d858934e8c032923fb2242
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 7caa442246e75e60e266f936538d68a0fa6045a8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 6e5c26b8bddbe7817d49b2f48dba6776e5d4f851
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Jul 8 20:10:23 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit c9bc765c083db8e3d4705eb14715792cdfba8d0c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:53:41 2021 -0700

    Update ChangeLog with the granular push of Jun 30, 2021.
      - plus ChangeLog update of June 7.

commit 4ac7706c44c4d29f38e8d1c7d2ac9cf45521cf4f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to a common header file not applicable to open-vm-tools.

commit d5f0c71fd909527fcfd03f858d277abc9fae9569
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Add page size defines for 64KB pages.

commit c1457815c1c5e579a3d26c99fff3f6b9e6ed0d41
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to a common header file not applicable to open-vm-tools.

commit 248830d868b7ade3ce199894fc0cb3c4771dd364
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to common source file not applicable to open-vm-tools.

commit bd5dede3536c30e2799fa8c788a9dc9b7f904d35
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Use os-release preferentially for guest identification.
    
    If the "score" of the Linux guest identification from os-release is
    the same as from the LSB score, use the os-release data. The os-release
    standard is well established and the LSB is deprecated.

commit 5fac74631a896e3e5b121a024a4293dcede5eaa1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to a common header file not applicable to open-vm-tools.

commit d9ff40dc4cf48b78cdebe9d05a393bd283429fa0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to a common header file not applicable to open-vm-tools.

commit 843e5851ac692c4bcaca1cfa262eda7e610417f0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to a common header file not directly applicable to open-vm-tools.
    
    This is part of a host-side fix for an intermittent DnD issue.

commit 19c38e8ed9be5272dd45cd07d362345456d9c3ed
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Fix an issue of re-running scripts.
    
    Fix an issue in the service discovery plugin where scripts can be re-run
    while the previously started one is still active.

commit 26b6e174f9a6ba780a052657cce3290f2174bcba
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to a common header file not applicable to open-vm-tools.

commit 03d3b6978b2c9d0e66d4b45b992724167ab14f03
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to a common header file not applicable to open-vm-tools.

commit 59fe3b61458659b0bdcce7d8ac0e4ae9e4ae7b3d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Changes to a common header file not applicable to open-vm-tools.

commit 81745623ae421c510fedba499ecfe0d916265a28
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Allow setting a custom poll interval in the service discovery plugin for debugging.

commit 08649561962e115cda98d6f1ef85a3559b12efe3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Change to common header file not applicable to open-vm-tools.

commit 185952f4b8c85d0fb04eecd08be0673f9e800f70
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Add preliminary guest OS support of RHEL-9 for Arm.

commit 176a0490fa001892e74237d1cf49607574bf8c4a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:27 2021 -0700

    Throttle the "Nic limit reached" log message from the guestInfo plugin.
    
    Implement a wrapper API for throttling vmtools log messages.
    Use the API to throttle the repetitive log message, "Nic limit reached",
    from the guestInfo plugin.
    
    This change addresses the log spew reported in open-vm-tools issue:
    https://github.com/vmware/open-vm-tools/issues/128

commit c3ad5e62cb9892e28486839aba19116aaadd76ef
Author: John Wolfe <jwolfe@vmware.com>
Date:   Wed Jun 30 11:37:26 2021 -0700

    Add a header to the vmsvc log file.
    
    Add a log entry at the start of the vmsvc log file that includes the tools
    version, build number, and guest OS details.

commit 1d25adfe20a5da7a3ed37c970e72699d487f3cca
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 10:53:14 2021 -0700

    Adding missed 2021 copyright updates in granular pushes.

commit a01a33d95d511a1019c6f9ab7a525635c9a61c61
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:09 2021 -0700

    Enhanced the VMware Tools SDMP plugin to support GDP.

commit 1751cd1e49edb5d88cb4d5175bdbb7ac5d86054c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:09 2021 -0700

    Add check that the packet size received is >= expected packet header size.
    
    DnD RpcV3: A corrupted packet received may result in an out of bounds (OOB)
    memory access if the length of the message received is less than the size
    of the expected packet header.

commit e3de1afdd4d802596bffdbbb63ec6708fafcf51a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:09 2021 -0700

    GOS support: Add Linux 6.x support.

commit bd151c59769f9bac14fc2df0d1076faa902734bf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:09 2021 -0700

    Change to a common source file not directly applicable to open-vm-tools.
    
    Adding a NULL pointer check.

commit d5938385597f4c13b6e9d265a399a67258c47f41
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:09 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 26b72e101e3d9a66e97e2179f142ce8961c7869f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:09 2021 -0700

    Update the tools version to 11.4.0 on the "devel" branch.

commit 5c1025a2f29f199a65cf703b93c1ae5294ff0053
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:09 2021 -0700

    Open-vm-tools 11.3.0 L10n updates.

commit 40e473ea1378ab1874f4cdf8aa38d39e11299fe9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:08 2021 -0700

    Customization: Retry the Linux reboot if telinit is a soft link to systemctl.
    
    Issues have been reported on some newer versions of Linux where the VM
    failed to reboot at the end of a traditional customization.  The command
    '/sbin/telinit 6' exited abnormally due to SIGTERM sent by systemd and
    where telinit is a symlink to systemctl.
    
    This fix checks if telinit is a soft link to systemctl and if yes, retries
    a system reboot until the telinit command succeeds or the reboot literally
    happens.

commit f8eedf7070b0c11c73f86e1374c004a1bc0212e1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:08 2021 -0700

    Change to common source file not directly applicable to open-vm-tools.

commit 5acc2a69256aa351e38d7abe1914b7785de6d6b2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:08 2021 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 9ff3c61c5d5ab58922c55db77704bab56bac75d6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:08 2021 -0700

    Common source file change not applicable to open-vm-tools.

commit dc39fc8c67180b3e7213d01449868482c17b36f4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Jun 7 08:25:08 2021 -0700

    Switch the current thread to "C" locale to parse /proc files.

commit 912fecf2f9e28a8083fee6e29b83a926c35b8661
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri May 21 11:57:16 2021 -0700

    Match the copyright style as in the main source repository.

commit b35069156e3d73b2149e78238eed38671481b0b4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:58:01 2021 -0700

    Update ChangeLog with the granular push of May 20, 2021
      - plus ChangeLog update of May 3.

commit dde0b8e906ea1ef1f5e752e51e9887be002b07f0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit b4a3a288b013202bfc01f99093c420b75051ffb4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Change to common source file not immediately applicable to open-vm-tools.

commit b2c8baeaa8ac365e1445f941cf1b80999ed89a9d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Remove unwanted "volatile" from static variable used with g_once_init_enter()
    
    The glib api documentation for g_once_init_enter() specifically states
    that "volatile" should NOT be used with the address passed as the
    argument.  Recent compilers (GCC-11 & clang 11) and recent versions of
    glib-2 will result in a warning that the "volatile" qualifier has been
    dropped if it has been used.
    
    Remove the unneeded and unwanted "volatile" qualifier from the definition
    of "inited" in pollGtk.c.
    
    Fixes: https://github.com/vmware/open-vm-tools/issues/509

commit 33ba0f01a3a15b245857e0f8ba2563b3177ced25
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Add backdoor support for host time of day in Arm
    
    The timeSync plugin makes backdoor calls to get host time of day.
    Update the time of day backdoor calls to function with the Arm
    backdoor implementation.  Also fix a bug where an error returned
    by the GETTIME backdoor handler is incorrectly treated as a time value.

commit 39b499fb2fcedc7b984510edf2ed0ea43fb0fe24
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 35dbe93510857ea9fa58bb93a959f26dea1bed7d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit c693dcf8212bcb01827cf131910e1d66132f50e1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Common source file change not directly applicable to open-vm-tools.
    
    Introduce peek() to the asyncsocket API, only supported by TCP vtable.
    
    Peeks are similar to recv(), except that they do not drain the socket
    after reading.  Subsequent peek/recv reads the same data back.  However
    since recv does SSL_Read, a recv() following a peek() may not get the same
    data as peek() after SSL is initialized.  This is not a problem when
    peeks are done before SSL setup.
    
    Implementation notes:
    
    - peek is a one-shot operation. The poll callback is unregistered
      once it fires (recv keeps the callback until recv is cancelled).
    - non-partial peek is not supported, so the peek callback will be fired when
      any amount of data less than or equal amount of the requested length is
      available in the socket buffer.
    - It is possible to invoke recv() or peek() recursively from within the peek()
      callback.  A peek is disallowed from within the recv() callback.

commit 95cbd990ec667a34b6b76c1d53182317c33e5330
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Fix an ASSERT in bora/lib/misc/timeutil.c.
    
    Fix a problem with TimeUtil_NtTimeToUnixTime on Arm that was encountered
    when running tools tests on Apple silicon.  The problem was the routine
    assumed that a variable was 32-bits if VM_X86_64 was not defined.  This
    may have been true in the past, but it is no longer true now that the code
    is also built for 64-bit Arm.

commit abfd599dea18f0f6a5acc35d539cec0322d194df
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:38 2021 -0700

    Additional changes for the network interface limit logging.
    
    1) Use the VM_SAFE_STR macro for string null check.
    2) Since free() is NULL safe, remove the "if" check before the free().
    3) Fix an alignment issue.

commit 78f19996702a14066d9f5424c522167780151f0e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:37 2021 -0700

    Update terms used in code that is distributed publicly in open-vm-tools.
    
    Use alternate terms wherever possible.  This does not address
    function names, structure elements, or macros.

commit d55475adf7617516fd8ec2bdc4403c1b7fdcf83f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu May 20 11:38:37 2021 -0700

    Common header file change not directly applicable to open-vm-tools.

commit bbcdb619382645179f2a8091818f55314c44291d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 20:01:43 2021 -0700

    Update ChangeLog with the granular push of May 3, 2021
      - plus ChangeLog update of April 19.

commit 16e2be2e2c67d49ed01032348368157ad9867920
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:41 2021 -0700

    VGAuth: Use GUESTRPCPKT_FIELD_FAST_CLOSE flag for log messages.
    
    VGauth is a single action service.  With the GUESTRPCPKT_FIELD_FAST_CLOSE
    flag added, VMX closes the vsocket as soon as the RPC response is sent.
    This cleans up the vsocket connections faster and minimizes the number
    of connect() failures in the guest.

commit 092a4cfd7b577b55abc71e636748dbbb25240171
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 8994d7c3a9c5164c5c88b5d882d32238a9964628
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Asyncsocket: Add comments about AsyncSocket_SetErrorFn usage.
    
    Document that AsyncSocket_SetErrorFn must be called before an internal
    asyncsocket callback can fire and trigger a call to the error handler.
    The error handler must be set immediately after the asyncsocket is created,
    either from the poll thread, which requires no additional synchronization,
    or while holding the asyncsocket lock, which is passed via pollParams.

commit 4abc2d2b6cf754914e6c0f08d245a2852f04e368
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit a3db034717c4bd77ffcf4f6ac2682096d360e2e0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Tools gdp plugin updates.

commit 037825f8b73cdc04e6f19e5868224e3e911c1acb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 62daa89681b48ad0fe33a92332a04a5c33c862e2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 306366fa79a390b632c89b4eab29fdd01659b688
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Format the VMCI port in a guest RPC connect request using "unsigned int" type.

commit 6c01c0ad957152d00cec28fb059a5781acb5a607
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit e0b4963730505f1bb746b1f93998b5e9a8a9af58
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 2693e19a54d93dac0a206bf2634f6e6b908fc8b9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    vmwgfxctrl: Refactor the 'for' loop index declarations for C89 compatibility.

commit 86fa9e400b3866407be4acfebd52f9093483f8b1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit d9dae1062ab61a15c54f02ccc8b74f69a1ead3f9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Fusion does not need to fork on certain file opens anymore.
    
    An NFS issue in MacOS 10.4 was worked around by forking and
    sending credentials to the child.  Removing the work-around
    since it is no longer needed.

commit b2078e875dfaa3257cb8a709a574b8a3cb8e8069
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit f59fe38baa986b5d5e1a8504ac70c5e3d4938cc6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Log messages when the network interface limit is hit.
    
    Whenever the maximum NIC limit is reached, log a message to both the
    VM guest.log file on the host and the vmsvc.log file inside the guest.
    
    Moved the logging api added for powerOps plugin to vmtoolslib so
    that it is available for all plugins.  Modified the powerOps code
    accordingly.

commit 09b035ad85475df08cd9bae8cc2d7aea6d8f0ffe
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon May 3 19:39:40 2021 -0700

    Common header file change not directly applicable to open-vm-tools.

commit cf65239bd6f5dede5394d1090615e6d70b06cd48
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 12:41:43 2021 -0700

    Update ChangeLog with the granular push of April 19, 2021
     - plus ChangeLog update of April 5.

commit b5ec5c1b60db89fe8fb4c223b204a8e50077851b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 12:32:41 2021 -0700

    Sync "(c)" vs. "(C)" usage in another header file.

commit e4e7aee59955326f1480fc27ffb8eef080fbbefc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 12:19:57 2021 -0700

    Update copyright date missed by developer.

commit b8058d49454e7c15fcd4806e51244803b08170b8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:16:36 2021 -0700

    Sync the (c) copyright symbol with the (C) in the Perforce repository; will
    avoid granular update problems when the copyright changes on these files.

commit 315b159733f55bd617391abf472a4ef2be22a624
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 1ebf8c871c8db79278fa6cd3f7d24edd4db7ce2d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common source file change not applicable to open-vm-tools.

commit 52523347adccc1ec3670488c6c61ed4c998372f3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit cd80433f04bb0f760759a4b64cc52d9950c07230
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 7417e628d777f598c75d832564478ec199e709d8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 5f982aadfcfbdb11d38978c764678c94575eb4b8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 48c0a778d94c99ebb0e51a284aa8ea1f6a8d6338
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common source file change not applicable to open-vm-tools.

commit fe2192f866a2faed6f9d27a1674838d56aa9f69f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit a183c79fbd8188a1d2aa1322469d19e71be7d676
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Unescape JSON in UTF-8.
    
    Add a routine to unescape JSON escape sequence in UTF-8 strings.

commit 865b31e49350e4aefa0858b6575051eae7def5e1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 5883b498a44c7d64025e6af761e784a7e2ce72aa
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit e834e17b698f56313e6b6a1f34c2b0c52665e3cd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 3827120a9edac868a1094cdd225b02920f952a0e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header files change to facilitate building with x86_64 and arm64.
    
    Simplify _DMB/_DSB.

commit 5e5494ef373917c706bc14c3fc5f90cdd07a0f4a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 65519ffb067661ecea1e0bae077dea5d3ce23197
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Correct a typo that rendered an if statment to always be true.

commit 27d9e866243ce119746a57d60690efb4c4502fb7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 964ecc057dd482efd53c809921a9f97b8efd3313
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:11 2021 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 57d9962108e0fdd56721ad03a7acec6abb7fdb86
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Adding thread ID to logging messages.
    
    As more threads (even short-lived worker threads) are added
    to vmtoolsd, add logging of the thread ID to simplify debugging.

commit 4dca880085556da2e7c3515067667a53acf43d54
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 0f6d89fc0bbf119e2284ba5195b7072d99a503f2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit a58b9c39768dd4582a2ccbd2483ae6c02d894528
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Check a previously unchecked return value.
    
    Fixes an "Unused value" issue reported from a Coverity scan of
    open-vm-tools.

commit b9c4441ed7316d1f807697c7145e2c09656f3e5e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 82931a1bcb39d5132910c7fb2ddc086c51d06662
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Fix issues using GCC 11 with gtk >= 3.20 and glib >=2.66.3
    
    With glib2.0 releases >= 2.66.3, glib header files inside an
    extern "C" block will encounter compilation errors.  This has
    impacted several OSS packages.  Consumers of newer versions of glib2.0
    must not include glib headers in an extern "C" block.
    
    GTK 3.20 has deprecated gdk_display_get_device_manager(); using the
    newer gdk_display_get_default_seat() when the GTK version is >= 3.20.
    
    The return value from read() must be used to avoid an unused result
    warning from the compiler.  This can be avoided by using dummy retyping
    in the case where the return value is not used or in this case, using
    the returned value in a debug log message.
    
    Pull Request: https://github.com/vmware/open-vm-tools/pull/505
    Addresses:    https://github.com/vmware/open-vm-tools/issues/500
    Addresses:    https://github.com/vmware/open-vm-tools/issues/509

commit d8ccbf4f386ca996778ed843073f6ad5743c8093
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit fc6368df777de8e862dcc7fac3dec5ccdc8cf817
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 50a9a23dd3abda3df63f96433821a79f74742bd1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 19 11:08:10 2021 -0700

    Common header file change not applicable to open-vm-tools.
    
    Add new definition for darwin22-64 (macOS 13)

commit 78631eb8a2e4ce12794b1e75a6232482f08d0c96
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:28:52 2021 -0700

    Update ChangeLog with the granular push of April 5, 2021
     - plus ChangeLog update of March 25.

commit b81140454253dfe3c836c03e221aa832da4ac9e8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:43 2021 -0700

    Check return value from VmCheck_GetVersion.
    
    Fixed an issue reported by Coverity scan of open-vm-tools.

commit f68f15d091fcda7ab393586498683d2061f9a85e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:43 2021 -0700

    L10n drop for updated wording in user visible messages to conform to guidelines.

commit d1d9f2fca56a40159b7039f2955410acc3de3489
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:43 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 585747b2d0b6091ffcc9ffce585ef89dd43ea9e8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:43 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 3256fd9e85d75768f9bcbeb39037a35e2b7c3908
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:43 2021 -0700

    Common header file change not directly applicable to open-vm-tools.

commit ba6744a874786f5a890e641fb94f2d048fc39e69
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:43 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit ec8b7d139c72287140e660ad4f43c083981b9a6d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:42 2021 -0700

    Fix dereference after null check reported by Coverity.
    
    Removed a NULL pointer test for gErr that causes Coverity to report
    a dereference after null check.

commit d19fe417deed03b2b8a9b8a22cbed1bcc836e0f3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:42 2021 -0700

    Tools gdp plugin updates.

commit 58fca5f585180c19311d084a536ed8014f5c102b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:42 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 08d82bad0db6d0d04b257748e8434a28f5a7c1c4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:42 2021 -0700

    JSMN: Miscellaneous log message fixes.
    
    * One log message in jsmn_parse_string function referred to a primitive
      instead of a string.
    
    * When the parsing fails, the log message specified the wrong position
      where the parsing failed.
    
    * Changed '%c' to '0x%02x' in the log messages to avoid any issues with
      printing the invalid/unprintable characters.
    
    * Added a new log message for better debugging in one specific error
      code path.

commit 5bf72167ec1870b75699e46df1b5e5e86520492a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:42 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit a5b6b94525ee86d4283ae1db3c424435c93dac61
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:42 2021 -0700

    lib/misc/hostinfoPosix.c:
    
    Correct an ifdef typo introduced in an earlier change.

commit 47df40cccede41beffa9658889b51ce1738c61bd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:42 2021 -0700

    lib/file/file.c:  File_UnlinkIfExists()
    
    Revised File_UnlinkIfExists() to return 0 for success and an errno for
    failure.  Previously, a failure was indicated by a -1 and the caller had
    to retrieve the errno.

commit 48f6c1cefc31cab095718ac4383a4fb7fd08d51b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:41 2021 -0700

    lib/file/file.c:  File_Unlink()
    
    Revised File_Unlink() to return 0 for success and an errno for failure.
    Previously, a failure was indicated by a -1 and the caller had to retrieve
    the errno.

commit 996626d977b3e5186d4664491b740a8afc6f45cd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:41 2021 -0700

    Common source file change not directly applicable to open-vm-tools.
    
    lib/file/file.c:
    
    Revised File_UnlinkRetry() and File_UnlinkNoFollow() to return 0 for
    success and an errno for failure.  Previously, a failure was indicated
    by a -1 and the caller had to retrieve the errno.

commit 8a98b8f5d3669812e1524cd692e1dd897e5f0ba9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:41 2021 -0700

    Change to common source files not directly applicable to open-vm-tools.
    
    Add a common function to detect if HyperV is present.

commit 761631d7c7d7c0210dafd6f4e4d5281a6d46a52a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:41 2021 -0700

    Don't follow symlinks in File_Rotate_ByRename().
    
    Delete the directory entries, not where they point to in the case of symlinks.
    
    Also, clean up some of the related and similar source code.

commit f9821bdf9031eda30bc515b4b869522119684393
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:41 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 5f14d310fa604a2d6802b76c8bbe48baac7e7b90
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Apr 5 09:01:41 2021 -0700

    Pre-enabled MacOS 13 (Darwin 22).

commit 0c88931b229a0099e6c70cb6eecc83b1b00846d3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:51:05 2021 -0700

    Update ChangeLog with the granular push of Mar. 25, 2021.
     - plus ChangeLog update of March 5.

commit 244e5bad740fa06eb40c48a7973ae8c519106019
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit c8c330b4df2878b6b152ac8be4f1bc999febe615
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Add Arm FreeBSD to the ALLARM set in guest_os.h.

commit 2df1c32e009649602f0f3179272107afa2c966b3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Clean up the classification information for Ubuntu, Debian, and FreeBSD.
    
    Some important information about these distros was not reflected in
    the macros in guest_os.h.  Fixed this.

commit 0ec0bb8ec20b45b7b1714701276d377db63acabc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 1a6546dfe7e0d4ed572434bd794d94f5228763b4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Fix an uninitialized variable issue reported by a Coverity scan of
    open-vm-tools.

commit f06d7cf7225a8c5e2a58fa2bf6a8f5d301964dfa
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Record the assignment of 11.2.6 for a VMware Tools emergency patch.

commit a95a56f95b01b3028c72e4da2af7c44fb38a4900
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit d26c6dfb4cf8dd0783df79589b3621f87c71071e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit f5a922bfd6268ee0690f0aabc03d88bdb781b867
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Disable NVMe quiesced snapshot by default.
    
    Open-vm-tools has no FSS.  Enable the feature only when it has been
    completely verified and FSS is enabled on the host side.

commit a58a79da6affce6fc993b0fa18b3d2b718a32d18
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:18 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit 004a5ddc3f79e126a1a14bac0ac5cf53630c833c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Add RHEL9 to the guest_os_tables.h header file.

commit c82f1d9ced8a9c1b13a19256da9d92b64c896906
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    FreeBSD now supports Arm
    
    Upgrade the guest identification code to handle this.

commit 4f7441d8cd20923e509ff819084693bbd8c928df
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Fix a memory leak reported by a partner from their Coverity scans.

commit 6cda6b456e88c570aac9c3eb8ee1765347e43a27
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Linux guest identification: Support multiple architectures.
    
    Allow machine architecture information to be present in the
    "short string" and the guestOS string.  This is done via a prefix,
    "<arch>-" (e.g. arm-ubuntu-64, riscv-debian11-64, arm-windows10-64)
    with the X86 architecture being implied (no explicit "<arch>-").
    
    Add the recognition of the Arm machine architecture to the Linux
    guest identification code.

commit 3694c7e9071771ccc6690a7f1473206cc21b3452
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Style change, alignment correction.

commit 6e980e3adeed221af11edc3ef66dcb382474642c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Clipboard logging improvement.
    
    Add a log instruction in CPClipboard_SetItem() to print the data size for
    every format.

commit c6b98a52e69488006fef87c63a3cc186ab3d9525
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Add machine architecture information to os.detailed.data.
    
    The detailed data now include information about the guest's running
    machine architecture.  A later change will deal with the guest
    "short name" (a.k.a. the guestOS string).

commit f387d5a2cc4a2221bb4802915e4e432adc1a945f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Tools gdp plugin updates.

commit 5689130bbc010843b718a0691ee3765624beca00
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Implement log redirection for the deployPkg plugin.
    
    On Linux, /var/log/vmware-imc/toolsDeployPkg.log is the default deployPkgs
    log file.  This change allows users to redirect that log to the vmware.log
    on the host or to another file located on the guest VM.  Redirection is
    controlled by settings in the "logging" section of the tools.conf file.
    
    See the examples in the sample tools.conf file provided in the distribution.

commit ce844c061dc2c8049d8c6f4c6499848cfa145728
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Changes to common header files.
    
    GuestOS: Begin recognizing Arm in VMware products.

commit 49ca4d0632ab5920c45e536883db5154cad2b8e4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Stylistic changes to the serviceDiscovery plugin Makefile.
    
    Replaced all occurrences of "$(libdir)/open-vm-tools" with "$(pkglibdir)".
    Split the makefile command lines to fit within 80 columns.

commit 807a1167e3bef3f070773ea1d8e2fd812f82cfd6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 25 20:21:17 2021 -0700

    Common header file change not applicable to open-vm-tools.

commit adaeae034bd8d4fa150700f96f617e40fc22edf1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Mar 5 11:28:17 2021 -0800

    Update the ChangeLog with Mar. 5 corrections
     - plus ChangeLog update of Mar. 4.

commit fdc50f3716bd23dd891eccf292741c3f48d342e1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Mar 5 11:19:30 2021 -0800

    Added complete LICENSE file to the jsmn.h and jsmn.c source files.

commit 7fb7dd20e9b879c4c75eb9349c761ca0f284f9d1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 16:41:38 2021 -0800

    Update ChangeLog with the granular push of Mar. 4, 2021.
     - plus Changelog update of Feb. 22.

commit 1a13bb349ab93d6861cdb57daf6cec451cca616a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 16:34:57 2021 -0800

    Correct some missed copyright dates for 2021 publication.

commit 30a9f295681983c778ea89eef379b8822518e524
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Configuring OVT with the --without-pam option will implicitly disable vgauth.
    
    When no vgauth option is given alongside -–without-pam, a warning
    is displayed with a message “Building without PAM; vgauth will be
    disabled.”.
    
    When -–disable-vgauth is supplied alongside –-without-pam, no
    warning or error message is displayed.
    
    When -–enable-vgauth is supplied alongside -–without-pam, an
    error will be thrown and the configure stage will be aborted with an
    error message “Cannot enable vgauth without PAM. Please configure
    without --without-pam or without --enable-vgauth.”
    
    Github Issue: https://github.com/vmware/open-vm-tools/issues/481

commit edeef6123b1c1edeac42acd4b45f5dab96a8c8d3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit f28bea2dde358e4a12f90f4436d276d9c5e3be77
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Common source file changes not applicable to open-vm-tools.

commit 65d92cd11edafe3e9ff7c96e83d824da6ecf4ae7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Tools gdp plugin updates.

commit c94ae5a4c7524f0d424ffd0a2cbc0aa685a6a710
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 662f85c3f7d356cfbb8e23dff0a7c4dfa6f74fba
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    tools/vmwgfxctrl: Add a command line tool for controlling vmwgfx.
    
    vmwgfxctrl is a small command line tool used to control various
    aspects of the vmwgfx kernel driver.  Currently it can both
    display and set current topology of the vmwgfx kernel driver.
    
    It should be distributed alongside other open-vm-tools binaries.
    It's incredibly useful when trying to set custom resolution on any
    recent distro (that includes multi-monitor setups).

commit b00709f8f9af26a1fb5803ef22c155b5983086ac
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Change not applicable to open-vm-tools.
    
    Windows: Add default setting to example tools.conf.

commit e239a31df9563c38ffbc0468deb1018b52334e02
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    stringxx: Replace usage of deprecated glibmm function
    
    Glibmm::RefPtr::clear() has been deprecated for a while and was removed in
    2.51. The reasoning is listed in the 2.44 header:
    
    "/// @deprecated Use reset() instead because this leads to confusion with
    clear() methods on the underlying class."
    
    reset() has been available since 2.16, so it is trivial to switch to it.

commit 44bfc5bc266ae7fc9c8c3df551220942c187c587
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit b2f9775cac95500b3ab19ac35bc9d40d48d41d2e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    PowerOps plugin logging of diagnostic messages on the host.
    
    To assist with diagnosis of VM power operations, have the powerOps plugin
    log messages about guest OS reboot, shutdown, power-on/power-off,
    script execution, and exit status to the VM's guest.log file on the host.

commit c3fad0b7d4f505f22e5606c6587833948f420c7e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 9bdfa71a8d86b7182c0ed2b8ea9e42f6aa0d33e8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Mar 4 13:48:46 2021 -0800

    Reduce or eliminate Linux dependency on the "net-tools" package.
    
    The "ifconfig" and "netstat" commands are deprecated in more recent
    releases of Linux.  Update the Linux vm-support script to use the "ip"
    and "ss" commands when available.
    
    If Available:         Fallback:
         ip                   ifconfig
         ip route             route
         ss                   netstat
    
    Addresses: https://github.com/vmware/open-vm-tools/issues/446

commit 9b2d46f1f36535e7a0838890a151cd63a853f198
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 10:13:53 2021 -0800

    Update ChangeLog with the granular push of Feb. 22, 2021.
      plus Changelog update of Feb. 4.

commit a7f9569f76f4ff05886dc3f4f9b5d3531d87c0ef
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:37:00 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit ff99f1169acc775d2e150f9cd4035c3bbd5eaf02
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:37:00 2021 -0800

    lib/file: Deleting a directory tree should not care about missing files
    
    Tolerate a directory entry disappearing while a directory tree is being
    deleted.

commit 533d1a6ee716635f0175584e62aaa6a1a115e00e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:37:00 2021 -0800

    Common header file changes: additional pre-gcc-4.4 clean up.

commit 433270c8dc916aeafd222a55a9bd2a301ac573f6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:37:00 2021 -0800

    Fix miscellaneous Codacy warnings in Tools and VGAuth code.
    
    Multiple warnings "The scope of the variable can be reduced" reported by
    the Codacy static analysis tool have been addressed.

commit d0b0527a75c4e265e5e9b21cfa87e9bc40733cb5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:37:00 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 5c27a31bbe54cdff6f53825a163d65e1c559a884
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:37:00 2021 -0800

    Adding FreeBSD on ARM64 support to open-vm-tools.
    
    Updating the FreeBSD specific sections of open-vm-tools to adjust
    where necessary for ARM64.   The FreeBSD vmballoon driver (vmmemctl.ko)
    will use the backdoorGcc64_arm64.c when built for ARM64.
    
    Pull request: https://github.com/vmware/open-vm-tools/pull/474

commit f3f8353eb2037f84d7f7ed6101086680ec58bdff
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:37:00 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 51328a83a99526be033774c32ef78934b0a4f6e2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit fb05d52910e9929ed3e23374a2d59bbbd96e4a8d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 4071ec7e4067f57d3747f2e95a04de23859d1735
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 08b6d1d81dc574a9ddbeead44ea8b72dcf5d4c00
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 05c7fa93f68be1c96badaa596d25c12a6654eed1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 619df79c8d3661d5609d68419c42a7e9e81463c5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit f200cb67cd75913c0850df7758a3adc5a462a688
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Clean up pre-gcc-4.4 macros
    
    Gcc is now "always" at least gcc-4.4 (checked in vm_basic_types.h),
    which means many conditionals for earlier gcc can be removed.

commit 6c116db015bf794f420019aa4210004ebff262fd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not directly applicable to open-vm-tools.

commit 4fa8c3dde624e209af5ecaaeb6ac720d9abfaa81
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Compiler minimums: bump to gcc-4.4

commit cbd0de661501194f0834ff0f8ff215344989ef38
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Fix miscellaneous Codacy warnings in deployPkg plugin code.
    
    Multiple warnings "The scope of the variable can be reduced" reported by
    the Codacy static analysis tool have been addressed.

commit 08e78ef8082830191d1be09a0b3a6806bd408c9e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 649dc72afb024063fc9c5871ee411bb1ad82f45d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    [open-vm-tools part] Support raw cloud-init data in VM customization.
    
    To support this feature, open-vm-tools needs to check whether cloud-init
    can support raw cloud-init data; then copy the data to the cloud-init
    cfg directory.

commit 39c9113caae0750342ef6049201f6c8976d8535b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Added 2 error codes for supporting raw cloud-init data in VM customization.
    
    Header file updated with errors detected when using raw cloud-init data
    in guest customization.
      - cloud-init version is too old to support raw cloud-init data.
      - cloud-init meta data format is invalid.

commit 5bf675afdc2c7d021bdac29f3bebe2d3bbb70c37
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit bd2c979437f44ecdc5a23a24b3e0afd301a21151
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Feb 22 09:36:59 2021 -0800

    Remove lib/include/vmware_pack_*.h header files.
    
    Previous changes have removed the last usages of these headers; now using
    '#pragma pack' 100% of the time.

commit 0da0823a61677c6140a71a8407acccdf5b4a3de8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 10:13:38 2021 -0800

    Update ChangeLog with the granular push of Feb. 4, 2021.
      - plus Changelog update of Jan. 22.

commit 38a44105dfc934aded80b38e4cb242daacc2bca5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not directly applicable to open-vm-tools.

commit 3d114fa0a5c050d1e84d6e73a9942d2e82aa8f00
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Drop usage of vmware_pack_*.h header files.
    
    Both gcc and clang support the Microsoft-style "pragma pack" syntax.

commit ff5eb5f448c78448b96f9c3db957d19f0288a9b6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    resolution: Fix kms autodetection
    
    Currently, the elf binary of the xorg driver is loaded to check for a
    string that was put in the .modinfo section in the driver.  Unfortunately
    there are two problems with this approach:
    
      1) Distros now ship without xorg, so the xorg .so doesn't exist and
         there's nothing to check.
      2) Distros (e.g. Fedora) do heavy optimizations and remove the .modinfo
         section from the .so, so the string cannot be found even though the
         driver exists.
    
    To fix both, stop depending on being able to parse the elf binary of the
    xorg driver.  Instead, let the plugin check for the existence of the drm
    driver with a sufficiently high version, and if it exists, use kms.
    This removes the dependency on X for kms.  Also increase the version of
    vmwgfx required to ensure atomic mode-setting plus relevant bug fixes (in
    the kernel since 2017) are available.

commit c5b510fded54592eae1c5a4a61f8119f69666a3f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Add a switch to the vm-support script to transfer the support bundle to the hypervisor
    
    The vm-support script unconditionally transferred the support bundle to
    to the VMware hypervisor, thereby filling vmware.log.
    This fix changes the default behavior of the Linux and Windows scripts
    to *not* transfer the logs to the host.
    
    Added an option '-x' to both scripts to let the user elect to
    transfer the guest support bundle to the host.

commit f7ddf98958097197468cd153227317cba205a19c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Add NVMe capability for vmbackup.
    
    The vmx/vmbackup logic checks if tools/vmbackup can support NVMe.  If it
    is supported, vmx/vmbackup can further go through the app quiesce process.
    Otherwise, vmx/vmbackup requests the filesystem quiesce process.

commit 61a691368b6e102d939173583e5e08c6ed1673dc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit b797923f9a5e960bb5c2971132d7cac592defc00
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit ce4ad3f984f4926d75888360b58b1b9b5637528d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit e2eb818a1d6d8947bc9c95801f714af202692963
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not directly applicable to open-vm-tools.

commit 056936aea88980f1474daaf34f20aacf34cc39c5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 2c10a53bc8c41baaff4f8817d8f35d2808291c3a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 4e9abe77c7ca725b3cbc70ed0659794a6e54f269
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 4ab1a9303926a9c50ec099bd9ed2778d02ed7fdf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Add tools version 10.3.24 to list of tools versions.
    
    Document use of 10.3.24 for the next possible release of VMware Tools
    (tartools) or OSPS for older releases of Linux.

commit 3d237b31774e58d637c88047567cf4394d3f8654
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Feb 4 09:57:03 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 7a830f9330fc9a5599a6d04ad07b0ac6cffcb3c3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:55:24 2021 -0800

    Added in the ChangeLog update comment from Dec 31.

commit 5ad6d8f3f627c050e2b58acc916166d09822f221
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:52:09 2021 -0800

    Update ChangeLog with the granular push of Jan. 22, 2021.

commit c777cf7c2d053948c18fda88bba8e2564c495518
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:39:50 2021 -0800

    Correct missing 2021 copyright dates.

commit c41a0d7d9f3d9e30b62eb64b30a8cba362edcd59
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 7a300e61857eeb16174f97eb8f95bb47f2499561
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 3e5288ecb310ad2cb8dcdce586aaee11abd080ba
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    File_Rotate: Optimize FileRotateByRenumber performance

commit f06ab994230b074a361edbd76e29e00a7b3160ef
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    Fix an issue where CustomizationUnknownFailure is generated multiple times
    on Linux.
    
    For Linux, sysimage sends a failure status to VMX when the deploy pkg
    failed.  The tools plugin then sends the failure notice again.  Changing the
    plugin to only send the failure notice for Windows guests where the
    failure has yet to be logged.

commit 61d8c3cfd2d1ed84c6527ae446f543324a341f38
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    Common header file change not directly applicable to open-vm-tools.

commit 41b2a9b1f07e4e1971f7523290c9443d0b251f29
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 4a9cdfb5190706a8471bf668247011a0725af09c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    Customization: Set log level to info, not error, for failed /sbin/telinit command
    
    The error message of a failed /sbin/telinit command is confusing for
    customers who might think customization failed.  On some Guest OSes,
    the repeatedly executing /sbin/telinit command can fail while a reboot
    is happening and the init daemon has been killed.
    This change sets log level to info, not error, for failed /sbin/telinit
    command.
    Note: The first /sbin/telinit commmand failure will fail customization
    with error 127; this is unchanged.

commit ecf07f0bf6cbab57c9e970efa32ca53e6c207aab
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    lib/file/file.c: Use Err_Errno instead of errno directly.

commit 741870477774566d0c065ae7f432ecda3e0caafc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    lib/file/file.c: Fix memory leak
    
    Don't continue; jump to ensure string free.

commit 5f8671ff14899d34f7abc07550812d5951e927b0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:41 2021 -0800

    Invalid file name causes the VMX to crash in log file rotation
    
    Parse the log file names without using sscanf.  This way no "%" in
    a file name can look like a valid scanf directive.

commit 2b2d31c783c551b25d6a5525c3227d2dc87e64d7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:40 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 68853a46e351f6d850973c01762b1188a685001a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:40 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit d37933efb2dacaadc11fd5df8de51d7537bd9306
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:40 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 69b5354e7df1ab0579f1c25fa2e0cf5414cec037
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:40 2021 -0800

    [resolution/x11] Preserve the rotation
    
    The screen orientation/rotation was being reset on each mode set.
    In general that is not a bad behavior.   But it does break with a
    fit to window and such as it will continuously reset the user
    requested orientation.
    This patch preserves the orientation/rotation on mode changes.

commit d65ad41e69517cbd2ef653b30ddcfca528b835c3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:40 2021 -0800

    Log file name becomes invalid after a rotation
    
    This is because the accounting is done unsigned but the printf used (in
    multiple places) was "%d".  Fix this by using "%u".
    
    As documented in the function header, the wrap around case was not handled
    properly, so this was fixed as well.  If the maximum rotation number hits
    MAX_UINT32, all of the files are renamed to pack the files as if this was
    the beginning of a rotation sequence.

commit 462c995f2deeaa578792b65c22eb082b9f487305
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:40 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit ba3483f293b56d696b962f691fa66fd888cc2964
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jan 22 12:25:40 2021 -0800

    Common header file change not applicable to open-vm-tools.

commit 937691d51dd93ee8043adc83c24f3b7fe4c25bcb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 31 10:16:50 2020 -0800

    Update ChangeLog with the granular push of Dec. 31.

commit 2ca1201b81d7513cc4d709ae766daf7eb4ae3612
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 31 10:12:25 2020 -0800

    Correcting missed copyright date updates.
    
    Adding 2020 to the copyright of a few files modified in this year.

commit 73491aa22b8892784b68ef2b7e285c6d95261fa6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 31 10:12:25 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 2d07080e644b2c6f12aae02006eb40adcac2a1ca
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 31 10:12:25 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 64ccc4db5bc5d2aac817af5041fe482e2982c7ed
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 13:01:04 2020 -0800

    Update some copyright dates for 2020 publication.

commit 2b6aac74cdc34ce88a7ca04aee3bfa4df888f391
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:04 2020 -0800

    Changes to common source files not immediately applicable to open-vm-tools.
    
    Staging source files and changes for a future feature.

commit af42d91f5881f1fcec0a902206386f546306dfe9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    OVT: Build only fuse-based or kernel module vmblocktest programs.
    
    The OVT configuration determines whether open-vm-tools will build
    a vmblock kernel module or a vmblock-fuse user level VMBlock
    implementation.  Both versions of the vmblocktest program(s) are not
    needed.   Linux OVT is only using the vmblock-fuse implementation.
    
    Select the version to build based on the HAVE_FUSE setting from the
    ./configure run.
    
    This fixes https://github.com/vmware/open-vm-tools/issues/467

commit 2a6f597bc91b8806601d186ab5be9d219ce01637
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 2a0984da06bd84dc85228368f4a449e53cf04b91
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 4c15a5dd7ea13adee0774298d837fc3e712be910
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 1d8243ee2d6b606f0be546fdaae494229c0714a6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common source file change not applicable to open-vm-tools

commit 2a8f1de3f7ff1d81f45c9a81f06beae95b74af07
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common header file change not directly applicable to open-vm-tools.

commit 9b2d29f394066714ac0131c9904c01a5430ce16d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Changes to a common source file not applicable to open-vm-tools.

commit 54d0128fc4ec2f2b256b88ad978b780c696b9c5d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Corrected a few typos and reworded sentences in tools.conf.
    
    Corrected a few typos, reworded a few sentences, added some
    details for quiescing scripts and removed some duplicates.

commit c80f0f3db5df3a055c70949e4c07326f98ac7045
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    MXUser Semaphores - nanosecond resolution wait times
    
    The graphics team requested the ability to have semaphore wait times
    less than a millisecond.  This would greatly improve some graphics
    operations.  Since POSIX platforms (i.e. ESXi, MacOS, Linux) support
    nanosecond resolution for semaphore wait times, a new timed semaphore
    wait routine with nanosecond resolution is introduced.

commit a2396202001370cf37d4cea3087b6a83bcb0cb61
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit bc9196b59469f9269c73d8cca605e7628d6b3d17
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Update copyright date for products to be released in 2021.

commit 6284afd17986fe3053fc6d69560d319dacb5299c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit dec004f0853468937b8e1e7b25f5062c58d131e2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 30874008335209a51531a50bc27d05b7fac513fe
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Dec 22 12:22:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 816f569db5207ffe321372edf75836eb4cff1992
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 197124856f619fb303f8bb5f91489c8f6753e0e4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Change to common source file not immediately applicable to open-vm-tools.
    
    GuestStore Upgrade: add settings to the tools.conf file
    
    This change adds the GuestStore Upgrade settings to the tools.conf
    file installed with Tools.
    
    Settings are:
    [gueststoreupgrade]
    
     # The guestStoreUpgrade plugin is only available for Windows.
    
     # The policy value is one of the settings listed below.
     # off         = no VMware Tools upgrade from GuestStore. Feature is
     #               disabled.
     # manual      = (Default) VMware Tools upgrade from GuestStore is
     #               manually started.
     # powercycle  = VMware Tools upgrade from GuestStore on system
     #               power on.
    
     #policy=manual
    
     # Time interval for periodically checking available VMware Tools package
     # version in the GuestStore.
     # User-defined poll interval in seconds. Set to 0 to disable polling.
     # Minimum valid value is 900 seconds (15 minutes)
     # Default value is 3600 seconds (60 minutes)
     #poll-interval=3600
    
     # VMware Tools package version metadata key to specify a VMware Tools
     # package version in the GuestStore.
     # User-defined key for VMware Tools package version.
     # Default value is "vmtools" which points to the latest version of
     # VMware Tools package in the GuestStore.
     #vmtools-version-key=vmtools

commit 36519ebbb0c6f14b0d2e56525223938ff4b65595
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit cad956d8cb6c71f5726fa630602085f90855d56a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Changes to common header files not applicable to open-vm-tools.

commit 6630aeda4ae52007744dbe789e59f0212a5cd818
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit d70f18262ccf9719f490671d1a2b7b3df57a5336
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit c504335ff4d11b93d6225a3f68f2faa88fe36f4a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    The next Windows Server OS is Windows Server 2022
    
    Originally guessed 2021 based on early data, but now Microsoft has
    provided the official name and date.  Update the internals appropriately.
    
    Add some TBD markers as a reminder to update the guest identification
    code once Windows Server 2022 has an official build number.

commit f5c38172e4d87342ffd8bf6f3f3e498d98705f5e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 4f55ff374583417645fcf22ceaad8dabbf049f1c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Update Mac OS related comments: 10.15->11, 10.16->12
    
    Apple changed their naming scheme.

commit d8db55524a278f05671bc60fe3691e9edafd85c3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Changes to common l10n files not applicable to open-vm-tools.

commit c38b77280bc273abc07dccdc46c01fd5b7386e18
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 7aef446280d018a738d66caafc932f3d5842e43e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Changes to toolbox-cmd not immediately applicable to open-vm-tools.

commit f2a099bb3402c5f15c3b2d23e27703fda32974b6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Backout previous header file change not applicable to open-vm-tools.

commit 36ded5ccac2c86e3accbed54e7930a4eb60723d4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 526156a867563020c90f681030dd697bbfa527a5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Thu Dec 10 19:34:56 2020 -0800

    Changes to common l10n files not applicable to open-vm-tools.

commit 735b9639a70429a3f0f50bfbd5bdc506999532c3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:31 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit fbd4f01c7b0e707aba1a2df045dc76fafac3d3a5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:31 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 9ea59704795a431c19fafe6e1b4ed4019b601c1c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:31 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 31fc9ee42b23fbcc482a787572a95d602afbec25
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:31 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 52bc97aa454ca2140ea62e6421f56c61d6954cf4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit c9fe171752a499a66d76e64c1333373cfa2bd5f4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Update wording of user visible messages to conform to guidelines.

commit b8d8c9f706558372d19edaa8c3a1ad4518960cd9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common source file change not applicable to open-vm-tools.

commit 2f4cc717102813ffabf8c534e59d690f4af7e669
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit decf8d536e55aa54ccfb64e39599151725cf4b58
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Record use of tools version 11.2.1 for an emergency patch.
    
    Update bora/public/vm_tools_version.h with the assignment of
    version 11.2.1 as a patch for 11.2.0.
    
    Also updating 11.2.0 as released and adding the scheduled 11.2.5
    update relase.

commit 2d51c8c2a7ce103417fb9a85204458099192fa1e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit de14e31cbd95c3fa847ef4d16dbaebdf97b0be87
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Resolving autoreconf warning caused by recently submitted xferlogs change.

commit 242478158e241275a284bcf5e87e26c3585031d7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 02b9f17acb8249571b4be15a00f0a7f82d695d4c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit e82101b0cd6cdaaba8109e6c689f8d6edeb252cd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Fix propagation of libtirpc flags into build of test source.
    
    Starting with glibc 2.32, the Linux libc<n>-dev no longer provides
    the /usr/include/rpc/rpc.h header.  The configure script will detect
    the availability of the libtirpc package and use the rpc.h header
    from /usr/include/tirpc/rpc/rpc.h.
    
    This fix extends the necessary compilation and linking options to the
    build of the open-vm-tools services test programs that utilize RPC.
    
    Fixes: https://github.com/vmware/open-vm-tools/issues/468
    Pull Request: https://github.com/vmware/open-vm-tools/pull/469

commit 3c20d288d6b1348ac5625c9b574db1edac16e8d9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 8efe182cdfeb85b7fb6b180a526c465714dd89f5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 0e83a002e2b0836634bfcbfcff5dd45168cd25c0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common source file change not applicable to open-vm-tools.

commit bf677ea09702cb9185409dbdd6ca94478dcc7227
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Replace g_key_file() usage in the Vix plugin with VMTools functions.

commit d8eb9171431f69f818430699bb9ece67b1d39c76
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 50b718b2462ef24483bc0e3895a752f784eb64a8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Adding localized messages for new toolbox-cmd subcommands in development.

commit 40c55773a03e552ff13bf0982d78962c7c9507a9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Remove the pam_securetty.so reference from the SUSE pam configuration file.
    
    The PAM config file for SUSE includes a library that is not necessary.
    This changeset removes the unnecessary library per KB 78521.

commit b40b15bce04fb65641fa469b49627f59fe874a69
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 4ed052eab5972e7c28ba40302c14e229c9e2cac7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Changes to common header files not directly applicable to open-vm-tools.

commit 24cb1ecb455d9d1e348a2eb2bc7ced692e9f4f6d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Nov 20 08:37:30 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit d37e545258fe051acba22d728aa3d2b2232e0710
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 4b09058fb66e07b2545d779e5aee80e18c02e6ed
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    JSON escape a UTF8 string, plus a general purpose routine.
    
    Provide a JSON escape routine working with UTF8 built on
    top of a general purpose escape routine.

commit 1ef815ffcbced6cb22cd7ecf84ee6ca23e20da2a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 381f8471434b84af40733a60163810734b437b4b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit e51d9ca883dc892c126ba231ece82d2cc87898e7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Misc cleanup of "xferlogs" utility.
    
    Update the xferlogs usage message to include a 'upd' option, and use
    glib to parse and print usage.  Remove the dependency on the vmtools
    library.

commit 330eb4025c26305e85fc5603dba88cefed261778
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 3cb3c6735d9f90b777765b93b93587ffe1db3a9a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not directly applicable to open-vm-tools.

commit 263fd9cf2c07cf20214a51427b6a9afb14ed0c2b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not appplicable to open-vm-tools.

commit 6cbd023665f07949526f7985d36d15739cf8ca90
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not directly applicable to open-vm-tools.

commit f775ee10e6be1714b7fc3cf79037fdca6916a689
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Service Discovrey: Fix a task thread and child process deadlock.
    
    Correct argument order in a warning message.

commit 4f127053603aeffe722a9441f51529d1819cd658
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    vm_assert.h: Document usage patterns where they're easy to find.
    
    This is a formatting and comment change.

commit 85004da3412c57a5ca40b6b4b4a087fe4a3e6591
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 807af42f5574a933d7e45c627e2537f1137c1e47
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common source file change not applicable to open-vm-tools.

commit dc029b6a3b3e269a13a3a52142411e38ed5e163e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not directly applicable to open-vm-tools.

commit 4dff6fd60f99d79c5fa8739e19c82c8bef21dcab
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit 5f7df88d6bd7d3621a241cb0267b6523d28f9311
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Changes to common header files not applicable to open-vm-tools.

commit 004f1603ef72cd4baeb713b1d10ec67490777d5f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Add a usage message for the "checkvm" utility.
    
    This change alters the functionality of the -h option to print a
    usage message by using glib options.  The previous functionality of -h
    was to print hardware version, which prints invalid results.  This
    change also removes a repeated header file and removes the -r option
    which prints invalid results.

commit 2614ce5bbf473c3df03f35d68cd8eceee8dbbf98
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit c27a6eac336305d36ee2fba7c192e5d0ea4bf0e4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Change the default Unicode error errno.
    
    In times long past, EINVAL was chosen for the errno returned when a
    Unicode problem was detected.  This overloads EINVAL, particularly when
    using POSIX system calls.  Unfortunately, the overloading makes it very
    difficult to recover from some errors as there is no way to know the
    difference between a "real" EINVAL and a Unicode problem EINVAL.
    
    Over 10 years of data shows that we *RARELY* encounter Unicode errors.
    Inspecting the source base shows the code segments that do check for
    errno after an error either:
    
    1) Post the errno value and die.
    2) Have switch handle cases but also have a default catcher.
    3) Have an if/else waterfall with a final else.
    
    Another observation is that Unicode routines are not involved with
    anything that would return a math related errno (e.g. ERANGE, EDOM).
    
    Changing the value of UNICODE_CONVERSION_ERRNO to ERANGE and avoid
    any overloading.

commit d35ef560247e7f0bd798ae45646eeaee38d3a12b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:03 2020 -0800

    Changes to common source files not applicable to open-vm-tools.

commit e47338fd721f3393823d778e487ee3c937594311
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Nov 9 12:29:02 2020 -0800

    Common header file change not applicable to open-vm-tools.

commit d6ba5ae8996e44e52c9ee967c6d44036fa2eba92
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit a4c1458c67a291c2a2d69d2a7f829daac00face1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:55 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 727a554c3d44ae615e23361a450ccfd793198019
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit e18e67f727d0354b08a55b685178fd05f542c6da
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Fix memory leaks.
    
    A Coverity scan of open-vm-tools reported a number of memory leaks
    on error code paths.  Fix seven reported leaks, and modify code
    to address two false positives in order to make the code clearer
    and/or keep Coverity from reporting the issues.  Also fix additional
    leaks found in the routine Proto_TextContents during code review.

commit bf3a15812b587bc46f79f8ca97253d7a867456d5
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 646eaa37a982d4f665443b6a017f8d420fc80546
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    tools: Fix Coverity errors in resolution plugin.
    
    Fix multiplication overflow and fgetc usage to avoid Coverity errors.

commit 1548fae0093271462d23bb5d66eb9c2e6521fece
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Fix a memory leak in HgfsOplockMonitorFileChange
    
    In the error case, "data" was not freed.

commit 2bb9aab991f787cf07941d6c76812c089ea830ab
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 90aeb31d98be541fdd2324e97160cef7abad93cf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Terminology cleanup

commit 23e7479ca368d392395499a3db192c97112a156c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 8bc0c0b8e3274cbfacb4ab1ace1a1fb644b66c64
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 26fc16779ffbce1833ea90d1d5efdba83708bb87
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Update vm_basic_types.h for apple silicon
    
    Apple silicon compilers will define __arm64__ as their
    name for their new CPU. But they also define __aarch64__ which
    existing 64-bit ARM CPUs use.  Add a little bit of logic
    to enforce and document this.

commit f73fa790abde3fe385cca37ed8583073eea36b7f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:54 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit b924451d819b646f2c25b873682c35937856dd1e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:53 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 75aabacdb096aa39252a14cf9ecbb11d7539369b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:53 2020 -0700

    [GlobalConf] Support for vmusr service to load Global Configuration.
    
    Currently, the vmsvc service periodically downloads the Global Configuration
    from the GuestStore and applies the downloaded configuration.  'vmusr'
    service doesn't have the support.  In this changeset, made the changes
    for both vmusr and vmsvc services to periodically check and apply
    the changes.
    
    To keep thing simple and easy, the new approach is check the
    modification time every time the regular tools conf is read and if
    changes are detected, the configuration is applied.  With this new approach,
    signalling mechanism is not needed and that code is removed.
    
    vmsvc:
    * Starts a background thread which periodically fetches the global
    configuration from the GuestStore.
    
    vmusr:
    * No background thread is created. But checks and loads the configuration
    everytime the regular tools.conf is checked.

commit 00531302d7e56bddaa037e5e4ae006135a34cca8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Mon Oct 26 17:29:53 2020 -0700

    Check in tools gdp plugin OVT installer code.

commit d3ed2aebcb5b33581ad49f85e41e6fa0eca98738
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Oct 16 21:03:23 2020 -0700

    Get the latest README.md from the stable-11.1.x branch

commit eae2394df5fc0856c4bc4133c569e13f5e327efb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:56 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit b76c75ae9a48e68e55412d0fd97f238a66409ea0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:56 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit b966cb3822b3a8cb9815af6f2da46f8f367b58cd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:56 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 99d5a9ea8605ad5ce085f109d4e3d378bf327db4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit fd75f811c8d09b662ee78f072d90ec6c915fcec6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit c1f88125233b074398bb6ba38d7b5c2ae19e7085
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit ece245c662d28b3880eb0ee40e8c427a2aafe4c8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:55 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 695d2fab5c8a036b8b7584877fb490bf505c178b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 84d87f789f8d93e5d265afe4288a6111c90341c4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 48e96c64a3a51b9bca2dd7614b1c7a04cc4dbb1d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 49103fbe71707b99323c996a23ec22fde5cb1438
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:55 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 08333afefd17ac7617ba5d4b1e3f087cccd97056
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:54 2020 -0700

    L10n drop for open-vm-tools 10.2.0.

commit 6cbee265af33370fe41a47db63c90cfd52ecdba4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:54 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit a869ed128c313d6335316fa10ebfbe15ace2be73
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:54 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit b6f81f00fcfbc602838b48feca9f85da2847cf59
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:54 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 66eb1b16e7cea8399c67d71eee45acd015e12a49
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Oct 6 14:30:54 2020 -0700

    Adding the vgauthImport utility to open-vm-tools.
    
    The vmware-alias-import utility is not currently packaged with the
    open-vm-tools bundle; this change adds the utility.  It is built
    only if vgauth is enabled.

commit a021b598a61aadccd7da931c7ab717312ca03c0f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Fix the exit code when toolbox-cmd disk shrink is canceled.
    
    The toolbox-cmd disk shrink operation was exiting with status 0
    when a SIGINT is received.  A non-zero exit status is expected.
    Change the exit code to 130, which is the appropriate value
    according to TLDP: https://tldp.org/LDP/abs/html/exitcodes.html

commit 985c9a0ea8e4ff00d5cfbadbb0397c12a8ec0240
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 81aff3a41e14b4aaae285bebb95cc6545a600c77
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Fix memory leaks in guestInfo/diskInfo.c.
    
    When checking for IDE, SATA and SAS disk drives, the glib GMatchInfo is
    passed to a g_regex_match() function inside a for loop.  It was not
    properly passed to g_match_info_free() before subsequently being reused.
    
    This addresses https://github.com/vmware/open-vm-tools/issues/452

commit 1c5d9dbbd3c19eb135633f9fefc47c654eacde76
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 30c87d05d1bb654b2bc37f482c779961bdb20506
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 457adfe51c0e03610705d32b0fb5cba55deb1304
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit cd6824eb5014508f479bb8deda86529bc5a21e79
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 9338c579fba6ee1f8d3a50df63f67cec7785d71b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 5ba9b23033f6500570f6a706aee18477c72039ac
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:07 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 77c30381461404f2b9468edfd6d010f36183f3ca
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 65fd0bf725a223d31d09c249675a5e1d1eb5dabb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 3267a1eb6964a4bb35806d1a117531b3a4119c9d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit cd53ce69a494ac0929523010f768a03e4cafec24
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit c86819a35d2a64a47bc846eeb8083a0dbd18b742
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit d6d96d1c7e53ea53affacb538342857d688a7aa1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Fix a minor memory leak in VIX Listfiles().
    
    The GError structure is not cleared in an error code path, which
    leads to a minor memory leak.  Explicitly call g_clear_error().

commit af4e12648002f9ac4fdcc50a3aa04dddbaf5398b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit c7b7ff477ca923833deebed645fec6b1612d4c5a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Changes to common header files not applicable to open-vm-tools.

commit ee09d506dac785a790e78b0e0dca0b99f447c694
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:06 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit bc811a0fcd6b80082fc7f513928e9c448e7bf46b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:05 2020 -0700

    hgfsServerOplockMonitor.c  HgfsOplockUnmonitorFileChange():
    
    There is no need to cancel the monitor action if the oplock monitor
    module has already been destroyed.   Avoid a possible NULL pointer
    dereference.

commit 49c898507badc4d19e17a5e3230116505aa58f93
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:05 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 2d3a1ce819c86360cf00c7a3352c501674daaae1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:05 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit ce5044577a89c0a66f4b427067c2843f4094044b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:05 2020 -0700

    Update the development tools version for the next major point release.

commit ea2e57b14f0b4b063556fda617829ad1320d8565
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:05 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 97582346292959b5b4d125485ab7cf9747c1c391
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 14:54:05 2020 -0700

    Fix a potential null pointer dereference in asyncsocket.

commit 9a3f9c925037fc7cba45761c6fcae762ded0668d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Sep 22 08:20:25 2020 -0700

    Captured the "devel" change log history since the start of the "stable-11.1.x"
    branch.

*****
    Created the "stable-11.2.x" branch at this point.
*****

commit 19d12e9a038a1835155ae26631b9a34d5a54cfcd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:06 2020 -0700

    Common source file changes not applicable to open-vm-tools.

commit 5e0117e5ac8620ee00906fb9f070e92ac6a15baf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:06 2020 -0700

    Changes to Common source files not applicable to open-vm-tools.

commit e71f137295a2aa94931369060a08a9fd6c1dde33
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:06 2020 -0700

    GuestOS: Add Flatcar Linux (64-bit only) as a new guest.

commit 53c5a3a8cb653031d3efa8009274b2b96c84ce25
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:06 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit d93e52f060d979baea5bcb63fac62a32a276becf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:06 2020 -0700

    Common source file change not applicable to open-vm-tools.

commit 6c40ef8aa76485edbc322711cae34cb75df56d2a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    hostinfo.h: stop including x86cpuid.h
    
    With the removal of Hostinfo_GetAllCpuid in an earlier change, hostinfo.h
    can stop including x86cpuid.h and only needs vendor IDs from x86vendor.h.
    
    Unfortunately, quite a few source files depended in hostinfo.h's automatic
    inclusion of x86cpuid.h. Fix them to include what they need.
    
    The lib/include/guestStats.h is a special case that happened to succeed because
    of a warning that was disabled in x86cpuid.h which also happens to have
    been included earlier than this header.  Re-disable the warning.

commit 1f66f283c20f7f5cd154ca34c33e3e145659916a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Ensuring vmtools utilities are only used in a VMware virtual environment.
    
    Several utilities do not check that their running environment is in a
    VMware hypervisor.  Add checks and generate error messages if the
    running environment is a physical machine.  Some makefiles were altered
    o resolve dependency issues.

commit 6f78dd5ce2733ea964fe969cb739daa55e329e70
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    VGAuth: vgauthd service fails if vgauth.conf samlSchemaDir has trailing whitespace
    
    When reading the vgauth.conf samlSchemaDir, remove any trailing whitespace.
    Also remove trailing whitespace when reading any preference string.

commit e6b3847d7bd9b811faff47126da72436d0e69ea2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Additional clean up of vmware_pack files.

commit 1cb3b54c545608fff271ca076b832c991a9316fb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Updating authors for OVT contributions
    
    https://github.com/vmware/open-vm-tools/pull/432

commit 098d213d1db4b4bb22823036cca7f3bc6f06af71
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Changing permissions of tools configuration example file.
    
    Also made a small change to another makefile for more consistency in style.

commit 6ef9d2fb20c127d633210b4537848021b1840013
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3b5f6264e905911ed87196cd49eec8387205da1e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 46c324e50081f63b12890cae9f0a9147805cdbee
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Additional clean up of vmware_pack files.

commit daa348acc12b0c24075276874c03d97acd2d247e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Additional clean up of vmware_pack files.

commit fdcc1ae11383e627ba624dc06c94d2ea0b62032f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Additional clean up of vmware_pack files.

commit 8cb221c581abe6a905ebd348d32cc3ecb6cabb93
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit c2553fbd4906f47984f01f11a208fea742683a5a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:05 2020 -0700

    Change to common header file not applicable to open-vm-tools.

commit c161dcb11d061c6bd4ef3c5aa563b15b13167bdd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 4da428cb1bbea7a5e1e9da2df40d410ca557f63f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Back out earlier common header file changes.

commit 7903a7eaee51685e70abfd7c227d1828c34e9373
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Backout the previous header file change.

commit ccba10c950c5a5a30c884c6ad3f5b2e71add5b09
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 27ef6c9deabdc9fe31de7b4f5da02a4408463410
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 2e5c40274f3f09d95084dafc9608123bf59f80b7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Replace a bogus URL provided in a vmcheck.c error message.
    
    Previously the URL "http://www.vmware.com/info?id=99" appeared in
    an error message in the vmware_checkvm binary to refer to VMware Tools
    updating information that may change from one release to the next.
    That information is now available from the single VMware Tools URL"
    https://docs.vmware.com/en/VMware-Tools/index.html.

commit fe7c6ebcd1fdf3e25246c7e1725063f8c4978db3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Get rid of more vmware_pack files.
    
    gcc supports the Microsoft-style "pragma pack" syntax.
    Standardize on it.  The conversion is somewhat non-trivial,
    as gcc requires "pragma" to be before or after a statement,
    not in the middle.

commit 055fed60e4f1a545e923c2aa0c14f5ecdb53b6ab
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Common source file change not applicable to open-vm-tools.

commit 3afc566751b7d151b2adb75703b5dfc10bff2237
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Log Facility: Infrastructure changes for module level filtering
    
    Additional changes to log.h and loglevel_userVars.h

commit 0516ef4a9a98f435aa3fbf20be43fc9581b34115
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3a10aafdeaac5c708d951e122bba816d9574b2be
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Remove PANIC() macro in favor of Panic() or VERIFY()

commit 54d36be9679eec0971a31d5b608bb51ecb07dd25
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:04 2020 -0700

    Record usage of VMware Tools version 11.1.6

commit 90ac21d93e9159582865955c83d47676db2ea34f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3df677d2ba3b4ddefc81c0ae0e381c782c81bbb6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 652aeafa2cee7893b66f73f3096077232e6dce93
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Adding null check for results passed to log function.
    
    In several files, replies/results from RPC functions can possibly be
    null if the function fails.  This changeset adds a function-like macro
    which does the null checks and is applied to the replies when passed
    into logging functions.

commit 31e14a2def83c8cf4450543c042c61a5e811eada
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Ensure the servicediscovery plugin isn't enabled in an open-vm-tools build for non Linux OS.
    
    The servicediscovery plugin is supported only on Linux platforms.  This
    change checks that this plugin is enabled only if the OS is Linux.
    Otherwise, an error message is printed.

commit be533613b3efdc8f78f6762aa31574173443e7e8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Tools consumes loglevel_user.h
    
    Adding lib/include/loglevel_userVars.h.

commit 5a9242dd13e229929e1c1dbe6a872b4c0e9e3361
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Split out the log level defs from the log level vars.
    
    This way the Log Facility can use the same name space as LOG for its
    domain specific level filtering by including loglevel_userVars.h directly.

commit 3fbb1d39f298f37248ec9690f08c550872846940
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Backout the previous log level defines change.

commit 34a64c375ee0a16db7e1f85f358c90d12815dac2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Split out the log level defs from the log level vars.

commit 606ed341010cec4a3c32233ba47e88aca9eb7b4d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Get rid of vmware_pack files (devices edition)
    
    gcc supports the Microsoft-style "pragma pack" syntax.
    Standardize on it.  The conversion is somewhat non-trivial,
    as gcc requires "pragma" to be before or after a statement,
    not in the middle.

commit 2174f3237b58391e9155e37895ef63c0043b0b35
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    vm_assert.h: Remove unused PANIC_BUG

commit 42af202affbf0cc2bb533dd430933bf5ee2ce247
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:03 2020 -0700

    Swap out Log+PANIC for Panic
    
    Just call Panic with an appropriate string instead of a Log+PANIC.

commit e61fbb2ebeb701063ec1e1362d7e1b19695ba183
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    LOG: Keep the LOG_LEVEL entries sorted
    
    This is the first in a series of changes to the Log Facility that will add
    the ability to have domain specific filtering (e.g. foobar domain can have
    log calls which are filtered by domain (name) and level).  This will make
    LOG like abilities usable in all build types, even a release build.
    
    The LOG_LEVEL entries are difficult to find since they are in a jumbled
    order.  The existing grouping isn't useful as many of the modules are shared.
    Create one big sorted list.

commit 16f81450b4820a4634dfefbaea222dda161e445c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit d2404fe3a2dfc88b118bd8dcf3655a960b52c822
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 31542efae6854d989d51b5d4c3333d3baa43dfc8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    Removing unnecessary code from rpc header file.
    
    Several macros related to VMDB are no longer needed in the guestrpc
    header file.  This change deletes those unused macros.

commit 0bb2d1599b4a29486aef2d4bced25992a99154cf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    Improve the logging for deployPkg in a few workflows
    
    When executing Perl script in Linux Guest OS customization, log an
    error if the execution fails.

commit 1c3925b3078008ff48b2c89c1ed080c583aba840
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    Add --cmdfile argument
    
    Some OSes (Windows) have a relatively small cmdline limit.
    Expose a cmdfile argument which works like --cmd, but
    reads the input from a file.  This allows RPCs which can
    have huge arguments (guestVars, namespaceDB, DataSets) to work.

commit 00d5ee23bdbbc01ac7eb326715ae7a98c0acdd06
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    Common source file change not applicable to open-vm-tools.

commit 248ef2c5361a0a8a7e4bed9cafec50598a313899
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    Fix a stack-use-after-scope issue in FileLockScanDirectory.

commit 504e2b8021b5c1e51c7f208c11acaac444531feb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Sep 11 12:11:02 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 79790f72cac52853dba8abba51e51226dad721f0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:46:26 2020 -0700

    Update copyright dates.

commit dd54c97c7aca69dda041e82fd1a62a836c20db4f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:12 2020 -0700

    Remove the guestApp.h header file that is not needed in conf.h
    
    The configuration header file conf.h includes the guestapp header file
    guestApp.h, but there are no related references to the guestapp header
    file.  This change removes the unnecessary header file.
    The guestApp.h header file is included in the few source files with
    a dependency on it.

commit 1b7f86e63a4a03170de159aa719c5e5be7e2dcea
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:12 2020 -0700

    Including appinfo and servicediscovery settings in configuration file
    
    Adding the default configuration settings to the tools.conf sample.

commit 45d0a2d16b5fb25ec11df8e9f75000917e279c22
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:12 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 093fcc60b980a6d48e9e3ed2156e79c6a6790f99
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:12 2020 -0700

    [HGFS Server] Add file attributes/symlink check status cache using oplock support
    
    An investigation found that while copying a group of small files from host
    to guest using the shared folder, the HGFS_OP_GETATTR_V3 message was handled
    in HGFS server 276,140 times over a period of ~35 seconds.
    
    Caching the file attributes in HGFS on the server-side can reduce the time
    significantly (from experimental results, 35s => 8s).
    
    It was also discovered that Posix_RealPath is called repeatedly to check
    whether a path is a symlink, since HgfsServerGetLocalNameInfo is called when
    handling a series of requests.
    
    Integrate a key-value cache to HGFS server to cache the symlink check results
    and file attributes, using the file path as the key.  This task will be divided
    into multiple change:
    
    1. Add oplock support
    2. Add a customized LRU cache in HGFS server for file attributes and
       symlink check results
    
    -  New files:
       hgfsCache.h/c: implements a customized LRU cache which is built by
                      combining two data structures: a doubly linked list and
                      a hash table.
    
    -  The cache will be allocated only when oplock is enabled.
    -  The cache relies on the file change monitor provided by oplock to ensure
       there is no stale data.  When adding an entry into the cache, also
       register the file change callback to oplock.  Upon receiving the file change
       callback, invalidate the cache.  Once the cache is full, remove the LRU
       entry and unregister the file change callback for it.

commit e860f4aaaac89aa0043a948ae79ac1b2a8dab6b4
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Change to common header files not applicable to open-vm-tools.

commit fac536ee236d87e779a323bc99e6f5c66550abf8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit f7ac1c4b756bc8a0b4b248299f725df773858695
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 07b065dbceadc6968f9b145dcef13a9fdfde1587
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Define "Unknown Command" Macro to replace hard coded strings in rpc files
    
    This changeset finds all instances of the hardcoded string "Unknown Command"
    in a few rpc files and replaces it with a macro defined in a shared header
    file.

commit 468fcf407bd71983cca6652d71547099772f37f6
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Changes to common header files not directly applicable to open-vm-tools.

commit bab170d03516705690ec347ba4c0ffbf5338f42a
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 2f11b89440dfb80a8b3a8ff1b343642367b2491b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Fix log recursion issue in DestroyRpcChannel()
    
    Avoid calling g_xxx() logging routines in any function directly or
    indirectly invoked from VmxGuestLog().  Change the g_debug() call in
    DestroyRpcChannel() to a Debug() call.

commit 46808762d89d92cdb2d2d382ff01227bdaf99f49
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 7ea7359bcac3bab10db4d568156ccc9c7849ef40
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Remove #include for headers that are not needed in serviceDiscovery.c file.

commit 697e20320ee71b8feb248582ccf0100b919f5b32
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit e70b82a46f92f8094d321a44f5402de1686f22b2
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:11 2020 -0700

    Removing windows header file which is not needed for OVT build
    
    Changeset deletes code which references a Windows specific
    header file and cleans up some whitespace.

commit 95f424ea52f3a674a3d3181759cb1fbf7315b315
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:10 2020 -0700

    Adding vmtools library dependency to deploypkg library
    
    Some functions are not found in shared libraries when linking,
    which generates several warnings.  Added a library with
    the needed functions to the list of dependencies.

commit cfbb787aa6c14ed71314b848fde915dc40fb8055
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:10 2020 -0700

    Backout the previous common souce file changes.
    
    Restore files to original state

commit 78871a8b52d44aa4211890e4e7dadac31a56fe97
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 18 07:14:10 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 98ac6d11bd48531c138f2fa82d70e71ec848a515
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Aug 11 21:26:23 2020 -0700

    Update the copyright

commit 32e653c935977b511470fafccb3cddcdb566df85
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:26 2020 -0700

    Backout the previous common souce file changes.

commit f08d6f6d9a2cefe2f5a24fbe6836362c6fddde23
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:26 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 09c15cc2d69d00799361b1ffc3b87550377b5f31
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:26 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 62896f05bb22a2217851dc5dda59d0605e76d2bf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:26 2020 -0700

    Address some Log spew for toolsdeployPkg.log
    
    1. Remove the extra '\n' at the tail of logging messages in imgcust.
    2. Update the log printting function, do not add '\n' if the original
       log message has already included it.

commit 9cbda48c6a5b778b30db123d621c358a15d215bf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:26 2020 -0700

    Add copyright header to service discovery scripts

commit 3aef7599dc3ea7468149be55d9ed5c3bbcb6061b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:26 2020 -0700

    common header file change not applicable to open-vm-tools.

commit 1eb8a82ac5e2f493ad23caf67e9bab8fd4cd3802
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:25 2020 -0700

    Common source file changes not directly applicable to open-vm-tools.

commit d2c7b255e902fcced1b60d30ff472d88595613ea
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:25 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit a65b1788534991fa3989a3a65942e944588df897
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:25 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 8dc74b22555398fbea5a7895914a910411909803
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:25 2020 -0700

    Updating the copyright date on a few files.

commit bddf9cdc61846cf9d6e8f36585dcdc0b39339beb
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:25 2020 -0700

    Resubmit the gcc baseline to 4.1 bump.

commit 563ab298de4a7d25355fd7768542b655660e22b9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:25 2020 -0700

    Backout the previous gcc-4.1 enforcement.

commit 57b3aa2a537fc1a7d5c6e04cdbbcb39e472f38af
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:25 2020 -0700

    Bump gcc baseline to 4.1

commit ac7098903f510f69b8aefdd9ae2625f691389ba8
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:25 2020 -0700

    [HGFS Server] Support oplock inside HGFS server for Windows host
    
    Missed two source files for previous HGFS Server changeset.

commit 31978d3150e9730e75266689e18404f49430ca44
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:24 2020 -0700

    [HGFS Server] Support oplock inside HGFS server for Windows host
    
    The motivation for this change is to cache file/directory related information
    in host side, for example the file/directory attributes.  To make the cache
    correct, we will use the oplock(Windows)/lease(Linux) to monitor the
    file/directory change event.  When the file/directory changes, the item in
    cache will be invalidated.
    
    In this change, two new functions are defined:
    - HgfsOplockMonitorFileChange
       This function is used to monitor the change event for a file/directory,
       and the callback will be called if file/directory is changed.
    - HgfsOplockUnmonitorFileChange
       This function is used to cancel the change event monitoring.
    
    This patch only implements the oplock support for Windows host, the support
    for Linux will be delivered in another patch.

commit 3b40aceb52c8fd2b001c0b5aa192018fad3fa2c1
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Aug 7 12:03:24 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 6757870e37708413ea005c5790f292f1115b12ec
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:35 2020 -0700

    The new table driven guest identification code did not handle Red Hat
    properly.  It needs to be checked for before Enterprise Linux.

commit 17f34b99fcde4d7f1a5dd26e766c502dc804a2db
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:35 2020 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 623f1d057fd552a2a20110c8f2ad2e00ec01b0ad
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:35 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 6578e6a740a027e8957b8af1442277feba022ef9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:35 2020 -0700

    Remove duplicate global definitions.
    
    Remove duplicate and unused global definitions for
    GDK_SELECTION_TYPE_TIMESTAMP and GDK_SELECTION_TYPE_UTF8_STRING.
    
    This fix will address https://github.com/vmware/open-vm-tools/issues/451

commit bd2b5b4a8dfa4f449cc0afa4262e28bb4db06714
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:35 2020 -0700

    Changes to common header files not applicable to open-vm-tools.

commit 012ae95c08eaba8c46152422a23a2a82ff330f0c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:35 2020 -0700

    serviceDiscovery: Double quoting script variables that might contain whitespaces.

commit 6908a9f51956ef77c5e7a45d42ab09c3aba2c818
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:35 2020 -0700

    Update to the tools.conf sample file not applicable to open-vm-tools.

commit 3d311af531f06e7de255b51534818dcf777c81b7
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 96daffa492eee5aa9e5ad03f1b67c17e5ccfae42
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit e6927bc3ac49fec1241bbf78cb35158553bab400
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 631280670bd5e0f50c2ece8ea43cc01c8586eb46
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    Changes to common header files not directly applicable to open-vm-tools.

commit c275d66d52a532ddae5ca0bfe4b1af7f71209524
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    [Coverity] Remove superfluous condition in if statement

commit 45b48c92746b4151b1eb118efdbfcb045cb4b97e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    Rewrite Linux guest shortname identification
    
    Replaced guest identification with easy to maintain tables
    as well as added general table search abstractions for easy
    maintainability.

commit 9e70e3db1038b2703b7c0e9d5e12afad1407e95c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    GOSC event doesn't report at once after customization process timer is reached
    
    When process timer is reached, the command process is killed and function
    ProcessRead is called to read all the output till EOF.  But EOF can't be
    reached immediately.  The reason is pre-customization script is launched
    by perl script, and killing perl script process doesn't kill the
    pre-customization script process.
    This code update includes:
    1. Do not read stdout/stderr when gosc command process exits abnormally
       to avoid the EOF blocking.
    2. Add READSTATUS_PENDING_TO_EOF in enum ReadStatus to avoid the confusion
       with READSTATUS_PENDING.
    3. Close the write ends of pipes (stdout[1]/stderr[1]) for child command
       process before it exists.
    4. In processPosix.c, the write ends of pipes have been closed in line 180,
       181, so the read ends should be closed in line 254, 255.
    5. Add explicit note for the beginning and the end of the perl script log.

commit 641874bfb831fc63e8ea289ee00d12d67734285e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    Common hesder file change not applicable to open-vm-tools.

commit 534f6bcbdda25bc9999c306cdd0d8e3e89f8f95c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:34 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit f1625fd8cad09739da8e67c952c27bdd425e096b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Fri Jul 31 13:36:33 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 44ad3675a33b804dd262bfab3279410aff442bcc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:01 2020 -0700

    Fix memory leak issue for "vmtoolsd" binary on receiving SIGINT or SIGUSR1.
    
    Valgrind complains of a memory leak issue on receiving SIGINT or SIGUSR1
    signal, because we lost call to RpcIn_Destruct(chan->in) in
    RpcChannelTeardown().  It just happens to be released only at service
    shutdown or on receiving SIGUSR1 signal; not a major concern.
    Cleaning up the code.

commit 2696fb719e9589dd82477f296c7cf81cc3cad617
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:01 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit f1330b36a316d38930aa3a654ea288a40f5a2a70
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:01 2020 -0700

    [CLI] "removeAll" missing in first line of vmware-vgauth-cmd help info

commit 511b4a8d729adef45964aceb8b3f1a7ef285c132
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:01 2020 -0700

    Remove unnecessary FreeBSD strings

commit c9a362419af2cdc312591c5dd63258a765d6f8f0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:01 2020 -0700

    Unknown Linux, later than 5 should report 5, not 4
    
    We want to report the highest version we know about in the short name.

commit 0265c8e2a43d45bfcbf9276168c55aa5fa69265e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:01 2020 -0700

    Improve the LSB distro identification table documentation
    
    Explain the LSB distro identification table and provide directions
    about when to use it.

commit 87128716f810df6140987db6de8c73905e8a3b8c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Fix a comment about POSIX/Linux guest identification
    
    The comment is not clear.  Fix this.

commit 0dabbdf36c4f2fdc248f75a5c5325b9d82728c59
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 99ccb81ebb4d0d085a25b9b531c86d43e646be6e
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3980498b245bf7a6a46a42d11dcdb725a326da70
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3b9521c96f0ec985d4965c73e9fd70e9718b3d53
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Common source file changes not directly applicable to open-vm-tools at this time.

commit 695611c22bb41a50abf71850ebc69de5ee415b09
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    hostinfoPosix: remove NOT_IMPLEMENTED() when NO_IOPL
    
    When running "vmtoolsd -b pidfile" on Linux arm64, we hit this error:
    
       [error] [vmsvc] NOT_IMPLEMENTED hostinfoPosix.c:2526
    
    Linux arm64 does not implement iopl() and the Hostinfo_ResetProcessState()
    is raising a NOT_IMPLEMENTED() because of that.  However if there's no iopl(),
    there is no reason to drop IO privileges, so we can just skip that code.
    
    This change also restores usage of getloadavg() for non Android arm64
    platforms: ESX vmx and tools for Linux arm64.

commit b1fdd4f15337cfb314652b66bbcc7be4bca100c3
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Resubmit - Move vgauth to gcc6 and openssl-1.1.1e.

commit 31d769f152106333e00902432a4bf49158defecd
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    ServiceDiscovery: Reducing scope of local variables in checkForWrite().
    
    Reducing scope of variables 'clientTimeStamp', 'clientInterval' and
    'currentTime' in function 'checkForWrite()'.

commit 0e6bd1e045daeb194d624bf09a768f60a199b223
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 48a140340865ca769d022d741f6fdd6c73384a5c
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit e0152be9ef541f359bf2cc4452b838825cefd581
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:53:00 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit de28bcaf8e4e85bdfcc2d67577bb2a3656f76d4f
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Backout the previous vgauth changes for openssl 1.1.1.

commit 8180c0dd6c2e07631173d00437934a369bf7e715
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Move vgauth to gcc6 and openssl-1.1.1.

commit 4f6cec30425e7e65405ca3e7191bfcd03ffe97d9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    vmware-toolsbox-cmd: Fix illegal read memory issue reported by Valgrind
    
    Replace the static function MsgUnescape() and its memmove() call that moves
    1 byte beyond the termination '\0' with glib's g_strcompress().

commit d5a24c41aa23c155a24ed65174cd986452fddd0d
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Backout the immediately previous changes to common files.

commit f078797c6b17ae91e3788da47a606313b141d0d9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Common source file changes not directly applicable to open-vm-tools at this time.

commit 7bd7bb9e062c7e93f94197b52211d09ef535b4e9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit 3e83452afcc6cc86b106b2c87527489c0126635b
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit a08df7a3eef47c1ed211ef034ae390a4ee898df9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 92022a07d524a16cccc71e44f4cbbbafdd1062bf
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    open-vm-tools: Propagate new gdk-pixbuf-xlib include location #438
    
    From github PR #438 (https://github.com/vmware/open-vm-tools/pull/438).

commit cffddcca7187adc043c82a24b5f15296fa6428c0
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit c0dfe4248701323394959b34814bd48d2b157bbc
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 27fe47c54402b6d3e941b2c45e611b98a6c862e9
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:59 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 29b3ab0fd346f411e65a39a3b89f56eb85d30b20
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:58 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 6ae818a5c9c261c16c630fc3f5814cbf8d080022
Author: John Wolfe <jwolfe@vmware.com>
Date:   Tue Jul 21 11:52:58 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit e5d0e1a2d33bd9060d843007d6bafebac677735d
Author: Oliver Kurth <okurth@vmware.com>
Date:   Thu Jun 11 20:43:21 2020 -0700

    Fix misc. issues in appinfo plugin source.
    
    Used the proper @param and @return statements in the
    function documentation for AppInfoServerSetOption.
    
    Re-organized an if code block.

commit d5517e5255770cd66eab3239c9d617147a3b0077
Author: Oliver Kurth <okurth@vmware.com>
Date:   Thu Jun 11 20:43:21 2020 -0700

    ServiceDiscovery: Replacing deprecated 'netstat' command with 'ss'
    
    Updating the Linux serviceDiscovery scripts to use "ss" in place of
    the deprecated "netstat" command.

commit 9afd238cddc0cb0511d8daa903b4f5c9a52b8dc3
Author: Oliver Kurth <okurth@vmware.com>
Date:   Thu Jun 11 20:43:21 2020 -0700

    Code cleanup to address a Coverity issue.
    
    Coverity reports a "dereference after NULL check" in
    BkdoorChannelStart.  However, at the point of dereference
    it's known that chan->inStarted is TRUE, which means chan->in
    is guaranteed to be non-NULL, so it's not a bug.
    
    Still, given that an input channel, if present, must have been
    started before calling BkdoorChannelStart, it's possible to do
    some code cleanup that will also get Coverity to stop reporting
    the issue.  Change what's currently a test into an ASSERT, test
    chan->in rather than chan->inStarted, and add comments to make
    it clearer what's going on.

commit 5e1388e10828cec3885ab0107711f483fe732d33
Author: Oliver Kurth <okurth@vmware.com>
Date:   Thu Jun 11 20:43:21 2020 -0700

    Common header file change not applicable to open-vm-toold.

commit e52ffacc9d189e4a4fd5880e3b1c9593cb6c32b6
Author: Oliver Kurth <okurth@vmware.com>
Date:   Thu Jun 11 20:43:21 2020 -0700

    Fix Coverity-reported dead code issue.
    
    The underlying problem was that retryCount was being zeroed on every
    iteration of the while loop.  Zero it before entering the loop instead.

commit e3a31877f56c1f0274a7e45b39d3be16a5f64c5f
Author: Oliver Kurth <okurth@vmware.com>
Date:   Thu Jun 11 20:43:20 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 1669fe0b0c0161884f19bcce257e0b10328c14c6
Author: Oliver Kurth <okurth@vmware.com>
Date:   Thu Jun 11 20:43:20 2020 -0700

    Correct an issue reported by Coverity.
    
    lib/asyncsocket/asyncsocket.c:
     - AsyncSocket_SetKeepAlive() calls AsyncSocket_GetFd() which may return
       a negative result.  If that should happen, simply return FALSE.

commit 013e0137786b28fef01bf3a09d79087d656e8f6e
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:46 2020 -0700

    open-vm-tools: add distribution specific pam config files
    
    Add distribution specific pam config files for Debian/Ubuntu,
    SuSE and Redhat/Fedora/CentOS.  Install a generic file by default,
    with comments to KB article.  The distribution files are intended
    to be used by OS vendors in their open-vm-tools packages.

commit 6ffc688caa7755febe912851d70de304d1ad447a
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:46 2020 -0700

    Update the AUTHORS file for OVT an contribution.
    
    https://github.com/vmware/open-vm-tools/pull/431

commit 384414d61846f5bd81ce691662efe9d8616a6c69
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:46 2020 -0700

    Appinfo.h:  Add define for APP_INFO_GUESTINFO_KEY

commit 13ac4d1bff79b0218675fcd1b64205f39cdbc768
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:46 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3d1d9c92f7ff88b8bd7dfaec674c30a7298b596e
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:46 2020 -0700

    Improve Linux guest identification function documentation.
    
    Make it clear that the "short name" of a Linux distro has a default
    value and that the default value should not be overwritten unless
    VMware does this.
    
    If someone cheats and sends down an unsupported "short name", the
    guestMapper will protect the software stack, mapping the unsupported
    "short name" to that of the guestOS config found in the VMX file.
    We know that is OK since the VM couldn't power on and run the guest
    unless it was valid.
    
    All older, supported ESXi have updates that contain the guestMapper.
    
    If the guestMapper is not in place - an older, unpatched release -
    the software stack can become confused causing problems and crashes.
    
    Change augments https://github.com/vmware/open-vm-tools/pull/431

commit 0cfda58aaa8bc0fea56f1211897e7f2237f6070a
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:46 2020 -0700

    Make peeking back into the stack work for back traces
    
    GCC 10 doesn't like peeking back before the end of an arrary (which
    is used to peek into the stack).  Fix this.
    
    https://github.com/vmware/open-vm-tools/issues/429

commit c7f4a5150d398184062b4d9ddba18d2c481c37f1
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    Pick up the LSB distro file for ALT Linux
    
    Improve the documentation of the Linux identification routine so others
    know that nothing needs to be changed in the field.
    Only VMware needs to add identification codes.
    
    https://github.com/vmware/open-vm-tools/pull/431

commit ae80317fcbf2026896b9fe5885992d919062d79c
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    SDMP plugin logs warning message every 5 minute if there is no
    Namespace DB instance created on a VM.  Changing the log level to
    debug to solve the problem.

commit 94fe542015b2764db1ea6b29c55cf53d9ce94f5a
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 0cbe4240689df8c7f6670bb2acf8d9c674063bb7
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    Clean up lib/file/file.c
    
    Consistency with the remainder of lib/file.

commit 070142eaf4742eff23d7bba2a20d07c0064f37d7
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 8c65a2773817208831d2c2db836a124fb6f6dd0a
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    Add a new definition for amazonlinux3_64

commit dca1937e894674fa41e7e0e2f622fb297ab74c08
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    Implement Set_option handler in appInfo plugin.
    
    * Added a handler for the Set_option for appInfo plugin.
      The poll loop will be immediately turned off when the feature
      is turned off at the host side.
      The poll loop will be immediately turned on when the feature
      is turned on at the host side.
    
    * Added the code to handle VM vmotion to an older host that
      doesn't have logic to send 'set_option'.

commit 3e3968f0b805d368b77e42902ec8283ba31dda7f
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    Enable recognition of other5xLinux and other5xLinux64 guests

commit f860921bce04cd0d7ad9f9eff977567d83b294a4
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:45 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3bd995723f1c11e44ea391c296ad41cf5bc5f151
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Directive argument is null from GCC 9.3.0
    
    There are paths that do not properly deal with NULL in FileMakeTempEx2Work.
    Fix this.
    
    https://github.com/vmware/open-vm-tools/issues/428

commit 88b016d68ec5bc3b03ae8255a47a9f2fb8630f3a
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Add new definition for rhel9_64 and clones.

commit 5612d4889d6dae21dfbb27ec4291807baa55ce7d
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Add recognition of FreeBSD 13.

commit 041028cd617331def7b3e6ef7cb98985c64cde38
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Enable recognition of SLES 16_64.

commit b48233cb1a996928cf13f6e48131fadabc6e31e9
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Add recognition of Windows Server 2021.

commit f40ebc8d757924b08af60956e290e7957274a14c
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Enable recognition of darwin20_64 (macOS 10.16) and darwin21_64 (macOS 10.17).

commit 10e5fe6eb5bd803fe0545371d820133813b3e7d5
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    guest_os_tables.h: Backout previous change for SLES-16.

commit 5978c6bcb8fdf6900c30e2e220f8478070f3bf12
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Enable recognization of SLES 16.

commit 0c7eb59720429fb8fa5872e432104731520e8799
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Move appInfo and serviceDiscovery plugin related header files.
    
    As part of an upcoming project, some appinfo and serviceDiscovery
    header files are needed at a different location in the common source
    tree.
    
    * Moved the appInfo.h and serviceDiscovery.h to a different location.
    
    * Made necessary code and OVT changes to refer to new file paths.
    
    * Did some cleanup related to the MACRO names for the keys and scripts.

commit ffc6f72e9babdf82010a96c7c76a0e99d5d9633e
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:44 2020 -0700

    Fix a Coverity-reported NULL pointer issue.
    
    Check whether a pointer is NULL before dereferencing it.  Also
    updated a stale comment, edited a couple of other comments for
    line length, and deleted some trailing white space.

commit 96c50d2068e45f2803b8ad8c1e186480d82f4411
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:43 2020 -0700

    Fix dereference after null check reported by Coverity.
    
    Remove a superfluous NULL pointer test that causes Coverity
    to report a dereference after null check.

commit 60d527ae5ea021a7c04a8df7437ef8e5914e5404
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:43 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3dfcedc9f383454baa79dcaa6508c676077177ca
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:43 2020 -0700

    Enable recognition of Amazone Linux 3.

commit 47c78f0e7ebbc80c9addeaa48fad88d507bcc759
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:43 2020 -0700

    Header file vm_verison.h not needed in lib/user/util.c.

commit cab6d8d915d86abd5558d00592e8b64386b50f22
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:43 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit d689c5de5349c48e3a84e72eaf7e2a6be52b8c08
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:43 2020 -0700

    Common source file change not directly applicable to open-vm-tools.

commit 8c0f47c895bd3cd901ddcc6df450a8d28fdf3578
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:43 2020 -0700

    Coverity reported issue: missing break
    
    Add the missing break.

commit bb11e038323893d2428eb2ce94f8742233b45546
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:43 2020 -0700

    Changes to common source file not applicable to open-vm-tools.

commit 421e77f5cbb0436bbafdb4d2ec7c39e637061838
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Jun 10 12:05:42 2020 -0700

    Common source file change not directly applicable to open-vm-tools.

commit 88836e5df83b215fed8821b3f31bb4a3091d9848
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:59 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 06ac81cfa3a6a31db045a719733a6b9f804063db
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:59 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 5a521ce9b164f0db7f860b154ab4baed553f5390
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    serviceDiscovery: Updating 'get-versions.sh' script
    
    The following changes have been made:
    
    1. Environment variable 'ALIVE_BASE' is used to construct the path of
       the file where vcops version is stored.  The variable is not avialable
       when running the script inside serviceDiscovery plugin process, so a
       hardcoded path is used.
    
    2. Added command to retrieve tcserver version

commit 20ad411ebeff55e6d7bfd15afc3fdd6fc3947511
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit c1832d6a60475850a25093d05ccf8c786dd89a50
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Deprecate VMGuestLib_GetHostMemKernOvhdMB GuestSDK API.
    
    * GUESTLIB_HOST_MEM_KERN_OVHD_MB metric was already deprecated in ESXi
      starting from 7.1.  It's time to deprecate the corresponding
      VMGuestLib_GetHostMemKernOvhdMB API in GuestSDK.  The documentation
      will be update denoting that this API is deprecated.
      After one or two releases, the API will be removed completely.
      Untill then, the API is modified to explicity return 0 for the metric.
    
    * Only a few functions listed in vmGuestLib.h are exposed in the vmGuestLibJava
      interface.  The remaining functions are encapsulated in '#ifndef SWIG'
      to hide them from SWIG utility which is used to generate the Java bindings.
    
    Note: The external customer facing documentation for this API will be
          documented later.
    Note: Once this changeset is submitted, the vmStatsProvider module
          will be updated to stop using the deprecated API.

commit 6a5123f2e020e6e35d2111777f99879c791088a2
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit a1bb9128e3087602ce5280f43d639cd87e5487cc
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 0682a7064f553a3d50a55ac881b0e5e6b78a257e
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Update to bora/public/vm_tools_version.h
    
    Add tools versions 11.1.1 and 11.1.5 to bora/public/vm_tools_version.h

commit d1ce2c08c47a82a4cfddae55a55c4b9619b08042
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 53473f83f7dfda6a0b592bbfdf1a5e6144b426e7
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Changes to common source files not applicable to open-vm-tools.

commit a99fcf43fbef64cc36473750a441cb2f6265a1dd
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Backout previous changes to common source files.

commit 8f68d75e3d6642e2f8d7d7ef9a4cbfcab052b3be
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:58 2020 -0700

    Changes to common source files not applicable to open-vm-tools.

commit 34d7c760709cec8938488d1c60831960bc30441d
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Common source file changes not directly applicable to open-vm-tools.

commit f3423aa7a1d2f0d864eaaa6d11b8b34eacb04c3c
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Common source file changes not directly applicable to open-vm-tools.

commit e25e233b02557a944babad5d8140472eb25c2210
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 5ed3bc8fe0cfee2b15b469b4f5e71c1fbb28bad9
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Backout previous common header file change - not applicable to open-vm-tools.

commit 2f6676c69e332677b7bd598505de02d6e78e1fbb
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 0128592fd7ec406824237af5e184f72726715f59
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Updating the bora/public/vm_tools_version.h history.
    
    Tracking version info for the 10.3.22 patch release and 10.2.23
    development version of tools for older Linux guests.

commit 146971ddbe43d24380891c4297493cce2d4a46c6
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 29c1c6743f5b1a2c9bf6d86f2eaacd10f2102aa2
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Common source file changes not directly applicable to open-vm-tools.

commit 25320e4af24664ebb6ab324db0a1cdfebf241f37
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Common source file changes not directly applicable to open-vm-tools.
    
    Pre-enable SUSE 16

commit ecdd9001cf809b2ce359f07f8b84d54cd436a573
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:57 2020 -0700

    Common source file changes not directly applicable to open-vm-tools
    
    Pre-enable RHEL 9, CentOS 9, Oracle 9, and Asianux 9 guests

commit 66e52b635938b8c4e5276944daaec1cddc5ae6f9
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    GuestSDK and vmtoolslib cleanup.
    
    * Removed the vmtools dependency for guestsdk.  This has been already
      done for tar tools build/target.  The same is being done for OVT
      builds.
    
    * Used various static libraries for building guestlib in OVT instead of
      depending on vmtoolslib.  Similar thing has already been done for
      appmonitor library in OVT.
    
    * GuestSDK_{Panic|Debug|Warning|Log} functions in vmtoolslib are not used
      anywhere and hence they are completely removed from the code.

commit 66b70db09241bef18694bc0e45bd94a1804b201d
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    Common header file change not directly applicable to open-vm-tools.

commit e4fdad51c47f5e2605a3ad9dd9e3451b25ba6fbd
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    Changes to common header files not applicable to open-vm-tools.

commit a760361f0b603f0dabe2e82cec1a728e5b3f796b
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    Pre-enablment: Recognition of FreeBSD 13

commit 80ce8edc04fcd3253174be476b0f09bdf5527bf5
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    Improve logging on signature verification
    
    If xmlsec and vgauth are built inconsistently, xmlSecSize
    can differ between the two, which results in a key datastructure
    being a different size, and vgauth ends up looking in the
    wrong place in memory for the signature check status.
    
    Log the status, so that if its not one of the two
    expected values, we have some ideas of what went wrong.

commit 4dbd9ff2e404957147ad1c22233388302b726d71
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    Changes to common header files not directly related to open-vm-tools.
    
    Recongition of MacOS 10.17 (darwin21-64) - pre-enablement on hosts.

commit f01d92c41b756f0167308ef7b5596b0554a661b8
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    lib/file: Fix a memory leak in FileLock_Lock.
    
    Valgrind complained about a minor leak within FileLock_Lock when running
    test-vmx Firmware.Efi.QuickBoot .  FileLockScanner builds a list of "active
    locks" and traverses that list repeatedly, but whenever it decides to remove
    an entry from the list, it would simply leak it.
    
    This change ensures that the list item is not leaked by Posix_Free()ing it
    once it is no longer needed.

commit c27281f24e043f64a9e627e2eaeccd9c196fc938
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    Hgfs Linux/OSX Server: fix write-only shares access check creating new files
    
    Linux and OS X Hgfs server has an incorrect failure status check when a
    user has a write-only share enabled.  In a write-only share any failure
    is mapped to EACESS unless the error is ENOENT when a new file is to be
    created.  The error check currently fails all errors when it should
    only allow creation of new files i.e., ENOENT with flags specifying
    O_CREAT.
    
    The check should be
       if (status == EACCES) goto exit

commit 5a300cffdb3c7069a104559b598f6315a6dbae00
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:56 2020 -0700

    Common source file changes not directly applicable to open-vm-tools.

commit e256c4eb87e602c981cd9b92ec4c52df861094f4
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:55 2020 -0700

    Common source file change not applicable to open-vm-tools.

commit d663f02db62cf44ee246956f50666f1c044cee2a
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:55 2020 -0700

    Common source file change not applicable to open-vm-tools.

commit c3d1c32369ec29653bd32beb3851d2d283dadd0c
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue May 26 15:32:55 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 215d376160a4c2f4e53666fd4275f7e8c7edc2da
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:13 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 3e5fd5d7976d9d047229b33f9a209c250f928384
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:13 2020 -0700

    [AppInfo] Tweak the gather loop only for a real config reload.
    
    The poll loop for the appInfo is being tweaked (destroyed and recreated)
    for every conf reload even when nothing related to appinfo changed.
    This may cause few scenarios where the 'application information'
    will never be collected inside the guest.  Fixed the code, to
    tweak the loop only when there is a real appinfo related config change
    in the tools.conf file.

commit c5f0d8d38bfedc576b9fec40b579a0d5f6c85688
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:13 2020 -0700

    Use RPCI_UNKNOWN_COMMAND macro instead of hardcoded 'Unknown command" string.
    
    At few places in the code, the result from the RPC channel send APIs
    is explicitly compared with a harcoded "Unknown command" string.  We already
    have a well defined macro for that.  Changed the code to re-use the macro
    wherever possible.

commit 7a981dcaa625a982e62e93ab63648dc02ac31595
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Clean up a few things in the random.h header file.

commit bdd112cee037f58b2788d7bc3f1dbc9fa5344d97
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Fix minor misc. issues.
    
    * In ServiceLoadFileContentsPosix() function, fd is always a positive
      value when close() is called.  The if check 'fd >= 0' is not really
      required.  Removed it.
    
    * Fixed the function name in the "procedure comment block" for the
      ParseShareName function.

commit 384b1e7b9e9a6dac67a285eef0acdd52f5c997e5
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Fix minor 'Comparison is always true' warning in linuxDeployment.c
    
    pkgProcessTimeout is uint16 and comparison is always true because
    pkgProcessTimeout <= 65535, so remove the check.

commit 4b57bd2d1c9fa37994a562a63be2232af6c4587d
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Deprecate GUESTLIB_HOST_MEM_KERN_OVHD_MB at ESXi level.
    
    Decided to deprecate GUESTLIB_HOST_MEM_KERN_OVHD_MB.  This changeset
    implements the necessary changes to deprecate the metric at the ESXi level.
    
    Will address the deprecation changes in GuestLib SDK/API in
    a separate changeset.

commit cb0a946b284d99f78da58e012a91c38eb81e492e
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit aa346e71e6bbc9a55f0a48d7d7b728e3128ae4bf
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Backout previous change to guestlibV3.x

commit 0f90e3fdd95d62294452b159c11a66368f8114e3
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Deprecate GUESTLIB_HOST_MEM_KERN_OVHD_MB at ESXi level.
    
    Decided to deprecate GUESTLIB_HOST_MEM_KERN_OVHD_MB. This changeset
    implements the necessary changes to deprecate the metric at the ESXi level.
    
    Will address the deprecation changes in GuestLib SDK/API in
    a separate changeset.

commit 91923b6c7d19edfedac6d7a36c4a9b795a7ef37c
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Common source file change not applicable to open-vm-tools.

commit 7058e42e6fa34dcf60438d27827d9b9ba20f5bd1
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Define macro for "Permission Denied" message returned from RpcChannel send APIs.
    
    In error cases, RpcChannel Send APIs (RpcChannel_SendOneRaw,
    RpcChannel_SendOneRawPriv return 'hard coded' "Permission Denied"
    error message.
    
    This changeset adds the MACRO for that error message.
    
    If there are any callers who compare the results (ex: ServiceDiscovery), they
    don't have to use 'hardcoded' messages and can reuse the MACROs.

commit 4c286c51e21e94e35b76a7f56c143a3b69e9a7e1
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:12 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit fde53d238b14ca984179aa821d7de95d6d6985ba
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:11 2020 -0700

    CodeSet: Fix copyright header in codeset.h

commit ced22726ccf583d54ebeaa83e5736fb249b730be
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:11 2020 -0700

    CodeSet: Add CodeSet_IsValidUTF8String() and more comments
    
    This change adds a new function CodeSet_IsValidUTF8String() to
    lib/misc/codesetUTF8.c, and adds comments for CodeSet_IsValidUTF8()
    and CodeSet_IsStringValidUTF8().

commit adcaf9cd9ad65a45459ee8058ebfc267844477d6
Author: Oliver Kurth <okurth@vmware.com>
Date:   Mon May 4 11:54:11 2020 -0700

    ServiceDiscovery: Deleting keys from NDB by chunks to reduce RPC calls.
    
    Setting chunk size to 25 keys at a time.

commit 619329e43a785831683f09d13f53f9d76615c559
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 2fb29808e01636ef6621ec6cbc815b024f2aed68
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 9e3875bc727141e69d7325af393e797353929833
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Fix a trivial typo in the VIX log message.
    
    * Fixed a trivial typo in the VIX log message.
    * Fixed an indentation issue.

commit 08218dcd4eb03aafe823ae3585d99a23f7bee3c0
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Use random poll-interval for appInfo on channel reset.
    
    In few workflows like instant clone, when a large number of VMs are
    cloned at the same time, if the appinfo plugin runs at the same time
    in all the VMs, the underlying ESXi may encounter heavy load.  To
    avoid these situations, a random poll interval should be used
    for the appinfo whenever applicable workflows are detected.
    
    Detecting a 'rpc channel reset' is a simple approach to detect.
    
    In this changeset, add the following changes:
    
    - Added a new callback function for the 'rpc channel reset'.  If
    the rpc channel is reset, a new random poll interval is calculated
    and poll timer is adjusted accordingly.  If the existing appinfo
    poll interval is greater than the minimum interval of 30 seconds,
    random interval will be generated between 30 and appinfopollinterval.
    If the existing poll interval is less than the minimum 30 seconds
    time, then random interval will not be changed.
    
    - Code refactoring for few functions.
    - Changed one global variable as static.
    - Added few debug/info log messages.

commit 9b79ed9bc118860518028828a6c7858af62757a8
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 9a9aab94168189302e74dd97939415ef1e07fb20
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit a8086de2c4317d4463b0bb1a3b602cda0833bd3e
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 8e2e15a2da54f3603a7ccdc09249aec0b58f6ed9
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Free old ununsed loggers when tools.conf changes.
    
    Existing unused loggers are leaked when there is a change
    in logging configuration.  Added a check to free the loggers
    that are not used after tools.conf is modified.

commit 1852a9e98e3b1c31542986b2d332c707e4a0b540
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Changes to common source files not directly applicable to open-vm-tools.

commit 86ca532794728e4cf3d6b5fd29b3a10ff4f36141
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Add option to vmware-vgauth-cmd to support remove alias by [username]
    and subject
    
    a. subject is mandatory
    b. if user only provide subject, will only remove subject matched mapped aliases
    c. if user provide username and subject, remove matched aliases

commit 3324db715f71d850f12f91e7941745792edacb63
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:46 2020 -0700

    Make Backdoor fallback temporary.
    
    When RpcOut falls to Backdoor, it stays with Backdoor
    permanently for the life of vmtoolsd service.  It is a
    long standing bug in the reset handling code.  Typically,
    channel type is not changed during reset.  Our reset
    handling code can either keep the channel type same or
    switch it from vsocket to Backdoor, but it can't do other
    way.  Though it is supposed to switch to vsocket on reset
    caused by events like vmtoolsd being restarted or VMX
    breaking the channel for some VM management operation.
    With this change when we start the channel, we always
    try vsocket first unless Backdoor is enforced by the
    caller.
    
    Using Backdoor for too long is not desirable because
    privileged RPCs can't be used on such channel.  So, we
    need to retry switching the channel back to vsocket
    periodically.  We don't want to try vsocket on every
    RpcChannel_Send call because that adds to overhead and
    increases the latency of RpcChannel_Send due to connection
    timeouts.  So, we retry vsocket with a backoff delay
    between 2sec-5min.
    
    As some RpcChannel callers intend to use Backdoor channel
    we need to differentiate between such usage from the
    callers that create vsocket channel and fallback to
    Backdoor.  Therefore, introduced a concept of mutable
    channel.  The vsocket channel is mutable as it can fallback
    to Backdoor and restore vsocket.  However, if a caller
    creates Backdoor channel, it will not be mutable and
    stay with Backdoor for its lifetime.
    
    As vmxLogger frequently connects and disconnects the
    channel for every log message and does not use any
    privileged RPC, so make it use Backdoor channel
    permanently to avoid frequent vsocket connections.
    
    Additionally, removed the redundant 'stopRpcOut' interface
    and renamed 'onStartErr' to 'destroy'.

commit 4a049ceec7e2e1fafbfece34e29c4f7b14962e7c
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    AppInfo updates.
    
    While most of the changes are only applicable to VMware Tools for Windows
    and are not applicable to open-vm-tools, the following changes do apply.
    
    - Modified few log messages from g_debug from g_warning.
    - Modified the default poll interval to 360 minutes (Once in six hours).
    - Modified log messages to log the filepath whose version is being retrieved.

commit 500eadfa9b5b8304ce57d9f881247ab67205d40b
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    Common header file changes not applicable to open-vm-tools.

commit 5f9bc344328dd144c5539dd44fe1508d0f7b8b6f
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    Common source file changes not applicable to open-vm-tools.
    
    Tools Windows: test plugin DLLs are the correct version

commit 4c1fed3a4676d664a049da5db6516a8baee1fbb9
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    Backout previous change for the vmware-vgauth-cmd.

commit f62afa8e4462c100105b8ac8d88f5379094e87cd
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    Add option to vmware-vgauth-cmd to support remove alias by [username]
    and subject
    
    a. subject is mandatory
    b. if user only provide subject, will only remove subject matched mapped aliases
    c. if user provide username and subject, remove matched aliases

commit bf687d16a380a2f94952a9d9ad84d492376d2895
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    Common source file changes not applicable to open-vm-tools.

commit f2b9d8028ad88583f9f3575d26ca68cf5664fef7
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit d1a02b45838f568d8a70d22f16e28bd2da8c447e
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    Common source file changes not applicable to open-vm-tools.

commit fa6fb17347de72a56d6c5a6ffcf713b97b9e73c1
Author: Oliver Kurth <okurth@vmware.com>
Date:   Tue Apr 21 14:43:45 2020 -0700

    Common source file changes not applicable to open-vm-tools.

commit 86053c524c617ba1faa744a04b1e18986ebb929f
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 8 12:05:18 2020 -0700

    update version

commit 32df621461ca3f7f7188763be0f3b269b71b87cb
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 8 12:04:15 2020 -0700

    sync tclodefs.h to source

commit 5b3b243dc5edae6d94e0bd376290605044a2bf89
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:38 2020 -0700

    serviceDiscovery: Adding sleep before 'RpcChannel_SendOneRawPriv' call.

commit a4560863d4fedc050b4917b3bc764c384b5c007f
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:38 2020 -0700

    Do a 100 msec delay for each vsock channel start retry.
    
    Move the 100 msec delay into the vsock RPC start retry loop.  The
    100 msec delay before each start retry is needed for errors that
    will return quickly.

commit ffe0dc255114856f123b6f9dd04dc714ab6f1eb9
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:38 2020 -0700

    Common source file change not directly applicable to open-vm-tools.

commit 8e221a1311bab1f526bef16d97d9d2798bf09e49
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    Fix localization issue on windows guests.
    
    Some fields of GOptionContext used in help are not public, so add 2
    arguments to Usage to get needed info.

commit 6fcc7bdd696cec8f04c748d3807bbce4dd3230b5
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    Add a retry loop to VSockChannelStart() to recover on start failure.
    
    On failure, a vsock RPC channel will eventually fallback to the
    backdoor channel.  Services that require or are limited to the
    priviledge RPC channel will fail.
    
    Adding a simple, limited loop in VSockChannelStart to retry the vsock
    channel start before ultimately switching to the backdoor channel.
    Retries are not done for "send once" operations.

commit 39ceb37ce4d6093cb1b64baf6eeddf2532b0b3a1
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    Destroy the dedicated RPCI channel set up for the Vmx Guest Logging
    when vmtoolsd process exits.

commit b911e5c94cd18b7985e920875249d2314029f0a6
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    Fixing some log messages
    
    1. PublishScriptOutputToNamespaceDB can fail for various reasons,
       caller can't know exact reason from return values, so just printing
       that the function failed in case return value is FALSE.  Exact cause
       must be inferred from the function's logs itself.
    2. Printing log for chunkCount only if we successfully wrote it in NDB

commit 6934e672378dbbdb505467682092d7bb905fe10e
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    ServiceDiscovery: Fixing failure of sending message via RPCChannel after
    it has failed to the backdoor.

commit 03f9392540cdfabd1220e220842ead210edfe7d2
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 04e99532df99d8a11fc160882a3a8455a2d7c23c
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit b4e6f4f8334be3bcc35c483dcf869de871ccac92
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit c556b1d10671366e1d5ddba14785c3e9b270db84
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    open-vm-tools: stage all *.vmsg files
    
    Only a subset of the *.vmsg files were installed. Fixing this by
    using a glob.

commit 9a3f8e4b57537e522dba8831eef3529a6dd6b9e1
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:37 2020 -0700

    Record the assignment of tools version 11.0.6.
    
    Add TOOLS_VERSION_SLEDGEHAMMER_PATCH2 for version 11.0.6 to the
    historical version information.

commit 2fa133ac0af2588f0295228795f427bc5cd44465
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 9cbdf3a6e2cb23a7673fe19b2a3f888714095019
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    Add a Coverity annotation.
    
    The Coverity scan of open-vm-tools reports an out-of-bounds access
    issue in Util_BacktraceWithFunc.  This is intentional, to compute
    the base pointer for a stack trace.

commit 7db9613b49a77e59c3cb1ed045905e2d0bbb2280
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit af4db666735d0af28ba013c63b92fef743bec125
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    Updating the tools version on the devel branch to 11.2.0.

commit ded2a20ac11ef4f9340847d19ae2a14431a4702f
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    lib/unicode/unicodeSimpleTypes.c: fix leak on exit
    
    The hashtable used for encoding name -> IANA table index
    lookups was not getting freed.
    
    Also, HashTable_AllocOnce() incorrectly instructs
    the hashtable to clear its clientData, which contains
    an integer index and not a pointer, using "free".

commit ef20d06e9b2e3fbe8e7f6f709c4707ebf7c57d72
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    Annotate a Coverity false positive.
    
    The open-vm-tools Coverity scan reports a NULL pointer dereference
    for the variable untrustedCerts.  However, untrustedCerts is NULL
    only if no untrusted certs are found, in which case the code that
    dereferences untrustedCerts isn't executed.

commit 3777c7008da506fe35b1b8003b3d1ae7067662ae
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    lib/mics/sha1.c: Include string.h unconditionally.
    
    string.h is part of POSIX, lets include it unconditionally

commit 8774b6383b6dcb024335dd115c8fcae2b00c4d16
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 0bbaf772c27a8df913ade55d7086d651652a2f17
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    vmtools: fix compiler errors in posix service discovery core plugin
    
    As we build the vmtools for arm (unfortunately not on main), we hit
    a compiler warnings "ISO C90 forbids mixed declarations and code"
    in this new code.

commit 65b6819ed3ba196f52639c5984be40475ce2fffc
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:36 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 5ef6682ae1038942f4b1d873cd0ca93d688f83e0
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:35 2020 -0700

    Common header file change not applicable to open-vm-tools.

commit 74351e2121e866ca0b28644a6fc545c97e8bfbda
Author: Oliver Kurth <okurth@vmware.com>
Date:   Wed Apr 1 11:31:35 2020 -0700

    Fix localization issue of vmware-vgauth-cmd
    1. default msg catalog folder is wrong, add correct msgCatalog in
       vgauth.conf
    2. rename vmsg file name to "VGAuthCli" since file name used in main
       is "VGAuthCli"
    3. Move I18n init up to fix variable localization issue

*****
    Created the "stable-11.1.x" branch at this point.
*****

2013.04.16  Dmitry Torokhov  <dtor@vmware.com>
	This release tag contains commits up to the following id:
	867ea989d5e6889abcac77b4c7dae7db2651df58


2012.12.26  Dmitry Torokhov  <dtor@vmware.com>
	This release tag contains commits up to the following id:
	530ef7f26ed041ab1b6655f2cdc79f3af97fc50b


2012.10.14  Dmitry Torokhov  <dtor@vmware.com>
	This release tag contains commits up to the following id:
	d85408321c66c927bffdb05106a26581244edd4a

2012.05.21  Dmitry Torokhov  <dtor@vmware.com>
	This release tag contains commits up to the following id:
	482332b8d4282df09838df5ea4c58df9cdb4faf3


2012.03.13  Dmitry Torokhov  <dtor@vmware.com>
	This release tag contains commits up to the following id:
	0114aabd54bf2db4556563b7149c4fbbdec3d87b


2011.12.20  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	dfca5cf9d1c2335c5d001deedd9266c84956b508


2011.11.20  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	6c197b8e5eccf32bfee0e2452d8926968182aada


2011.10.26  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	6271fde90248a4fd49e38aa61498db9e1dbf37c3


2011.09.23  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	05baddbed1c5e47e9789a44d30bb217d7184232a


2011.08.21  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	2b3d76ba776f55d06fb5b62499b189ebd6bc1c75


2011.07.19  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	5bed6f1369ca6e9c2c7fbaf4205d86e50f219c5f


2011.06.27  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	166bbe1d28da4dab763b9568f163c8dca99ced9c


2011.05.27  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	3793ddc9c9b5facf376a2625d4c2252aa9bd3daa


2011.04.25  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	3112c27981074deb53e86e30e1c168d55e42220c


2011.03.28  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	ec43520f5f3a50f5a980a73d22ae231380f97555


2011.02.23  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	96cf4718ac0aff1743e50a2165599306ba442fe1


2011.01.24  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	60c470337c8932a6d9564130dcaf06c7a1a3df53


2010.12.19  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	5aef2b20a519788613350752575bcba0ac71df79


2010.11.17  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	11c0273ed4269f6f7a92f82f6c822df7da4c8720


2010.10.18  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	2162c5d770cdac3b0e275907a1a5d22ece8ce23c


2010.09.19  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	c92a8bfbb406a906bcd2fb9ef6801f92c5b64d1f


2010.08.24  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	94e63742d734b41638d37580602de4232da5ece6


2010.07.25  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	b15cffc7961b97129d0b77643db42b4d4d8e3da7


2010.06.16  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	ec87703fccdd0f954a118640c0b097e383994391


2010.04.25  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	6fafd672e006208c1e479b297e19618170ff19bd


2010.03.20  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	7cdbb623125729b41bf54068568dfbcc2dd58733


2010.02.23  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	8baa8588d5fd4cf64efb17164cb70c86c758d0c6


2010.01.19  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	8ee82a5774ae7badeb98ecf4dc629c7e9aac7077


2009.12.16  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	0d28106da5684dc31ea52ebb5a2dc6a0af5c1d61


2009.11.16  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	6f4cdd0f38be020d722f2393c0b78d7cd13f04d2


2009.10.15  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	d2f1b83daab1d7882fd651ad1cc77c729bbd9760


2009.09.18  Marcelo Vanzin  <mvanzin@vmware.com>
	This release tag contains commits up to the following id:
	8bb94fbfbdf65b53b87279cf81529756dba7a2ca

	Other changes not captured in the git logs:

	* Resync with internal dev branch (2009.08.24)

	* hgfsmounter/hgfsmounter.c: allow hgfs users to set ttl to 0 at mount time.

	* lib/guestApp/guestApp.c, lib/include/conf.h, lib/include/netutil.h,
	  lib/include/procMgr.h, lib/include/system.h, lib/vixTools/vixTools.c,
	  lib/vmCheck/vmcheck.c: remove (now unused) Netware checks.

	* lib/message/*, modules/freebsd/vmhgfs/Makefile,
	  modules/linux/vmhgfs/Makefile.*, modules/solaris/vmhgfs/Makefile: remove
	  unused message transport abstraction. The rpcChannel library is now used to
	  do RPC abstraction.

	* modules/*/vmmemctl/*: refactor of the vmmemctl module as part of adding
	  support for Mac OS guests.

	* modules/linux/pvscsi/pvscsi.c: don't clobber RESERVATION_CONFLICT sdstat
	  up from linux pvscsi driver.

	* modules/linux/shared/*: VMCI changes unrelated to the guest driver.

	* modules/linux/vmhgfs/fsutil.c, modules/linux/vmhgfs/inode.c: fix the case
	  where two directories refer to the same inode.

	* scripts/linux/*: support older versions of NetworkManager.

2009-08-24	Marcelo Vanzin	 <mvanzin@vmware.com>
	* Resync with internal trunk (2009.08.19)

	* configure.ac: remove support for Linux kernels < 2.6.9.

	* lib/include/vmtools.h, libvmtools/vmtoolsLog.c, doc/api/services/util.txt:
	  add new log handled that allows appending to existing log files, and fix
	  an issue where old log files would be "rotated" whenever the config file
	  was reloaded.

	* lib/appUtil/appUtilX11.c, lib/ghIntegration/ghIntegrationX11.c,
	  lib/include/appUtil.h: fix compilation issues on FreeBSD when unity
	  was enabled.

	* lib/dnd/dndLinux.c, lib/include/vmblock_user.h, tests/testVmblock/*: add
	  vmblock tests and, as part of the change, do some refactoring of vmblock
	  related functions.

	* lib/guestInfo/guestInfo.c, lib/include/wiper.h, lib/wiper/*,
	  toolbox/toolboxcmd-shrink.c, toolbox/toolboxShrink.c: refactor the wiper
	  structures so that they behave the same on Win32 and other platforms, and
	  also reuse data structures already in use by other parts of the code. This
	  fixes an "use after free" issue that toolbox-cmd had on Win32.

	* lib/guestInfo/guestInfo.c, lib/guestInfo/guestInfoInt.h,
	  lib/guestInfo/guestInfoPosix.c, lib/guestRpc/nicinfo.x,
	  lib/include/guestInfo.h, lib/include/netutil.h, lib/include/slashProc.h,
	  lib/netutil/netUtilLinux.c, lib/slashProc/*,
	  services/plugins/guestInfo/guestInfoServer.c: add support for sending more
	  network-related information to the host, such as routing tables and name
	  servers.

	* lib/hgfsBd/hgfsBd.c: don't log RPC errors when HGFS is disabled.

	* lib/hgfsHelper/*, lib/include/hgfsHelper.h, lib/vixTools/vixTools.c: new
	  library with functions to query information about HGFS; expose some HGFS
	  properties through VIX.

	* lib/hgfsServer/*, lib/hgfsServerPolicyGuest/hgfsServerPolicyGuest.c,
	  lib/include/hgfsServerPolicy.h: fix checking of whether an object belongs
	  to a particular share; this was causing issues with invalid information
	  being returned in certain cases.

	* lib/hgfsServer/*, lib/include/hgfsProto.h: changes to support new VIX API
	  calls (mostly affecting Win32 only).

	* lib/include/guestCaps.h, lib/include/unityCommon.h: add boilerplate for new
	  RPCs for setting windows as sticky / non-sticky (not yet implemented).

	* lib/include/hgfsProto.h: new definitions for the next version of the HGFS
	  protocol.

	* lib/include/xdrutil.h: make XDRUTIL_ARRAYAPPEND() more type-safe when using
	  GCC.

	* lib/misc/codesetOld.c: fix some issues with UTF16 -> UTF8 conversion.

	* lib/rpcChannel/rpcChannel.c, libvmtools/signalSource.c,
	  libvmtools/vmtools.c, libvmtools/vmtoolsConfig.c: g_assert -> ASSERT.

	* lib/unityWindowTracker/unityWindowTracker.c: fix issue with z-ordering of
	  modal dialogs.

	* libvmtools/vmtoolsConfig.c: fix some old config translation issues.

	* modules/freebsd/shared/*, modules/freebsd/vmblock/*: make vmblock work on
	  FreeBSD 8.

	* modules/freebsd/vmmemctl/*, modules/linux/vmmemctl/*,
	  modules/solaris/vmmemctl/*, : refactoring and code changes to support the
	  driver on Mac OS X.

	* modules/linux/vmblock/*, modules/linux/vmci/*, modules/linux/vsock/linux/*:
	  remove compatibility code for older kernels.

	* modules/linux/vmhgfs/*: fix memory leak in HgfsAccessInt().

	* modules/linux/vmxnet3/*: fix kunmap usage in vmxnet3_shm, and reset the
	  shared pages when the char device is closed.

	* modules/linux/vsock/linux/af_vsock.{c,h}, modules/linux/vsock/linux/util.c:
	  add vsock protocol negotiation for notifyOn ops. This allows the driver to
	  negotiate with the remove end which version of the notification protocol to
	  use.

	* modules/linux/vsock/linux/notify.c, modules/linux/vsock/linux/notify.h,
	  modules/linux/vsock/linux/notifyQState.c,
	  modules/linux/vsock/linux/vsockPacket.h: add pktOn protocol. This new
	  protocol improves performance by detecting changes in the queue state
	  instead of sending WAITING_READ and WAITING_WRITE packets.

	* services/plugins/hgfsServer/hgfsPlugin.c,
	  services/plugins/resolutionSet/resolutionSet.c,
	  services/vmtoolsd/mainLoop.c, services/vmtoolsd/pluginMgr.c,
	  services/vmtoolsd/toolsRpc.c: load plugins even when an RPC channel cannot
	  be instantiated (i.e., when running outside of a virtual machine); this
	  allows plugins to perform actions (at load time) also when running outside
	  virtual machines (e.g., to undo some configuration done when the OS was run
	  in a VM).

	* services/vmtoolsd/mainLoop.c, services/vmtoolsd/mainPosix.c,
	  services/vmtoolsd/toolsCoreInt.h: handle SIGHUP differently; instead of
	  stopping the service, just re-load the config data. This should make it
	  easier to integrate the tools service with other tools such as logrotate.

	* toolbox/toolbox-cmd.c, toolbox/toolboxcmd-stat.c: remove memory info query,
	  which didn't really return useful information.

	* vmware-user/copyPasteUI.cpp: if the clipboard/primary with most recent
	  timestamp has no data on it, try the other before giving up during
	  copy/paste.

2009-07-22	Marcelo Vanzin	 <mvanzin@vmware.com>
	* Resync with internal trunk (2009.07.17)

	* configure.ac: fix detection of "dot" and its usage in the doxygen config
	  file.

	* configure.ac, libguestlib/Makefile.am, libguestlib/vmguestlib.pc.in:
	  install vmguestlib headers and add new pkgconfig script.

	* lib/dndGuest/dnd.{cc,hh}, lib/include/dndBase.h, vmware-user/dndUI.{cpp,h}:
	  fix an issue where the DnD code would prevent drops to other windows in
	  Unity mode, instead of just intercepting drops to the desktop.

	* lib/dndGuest/dnd.{cc,hh}, lib/dndGuest/dndRpc.hh, lib/dndGuest/dndRpcV3.cc,
	  lib/include/dndBase.h, vmware-user/dndUI.{cpp,h}: fix an issue where the
	  host Unity code would cause an undesired drop event.

	* lib/dndGuest/dnd.{cc,hh}, lib/dndGuest/dndRpc.hh, lib/dndGuest/dndRpcV3.cc,
	  lib/include/dndBase.h: cleanup mouse simulation code, and fix an issue
	  where a drag would be canceled if the contents were dragged out of a guest
	  and then back in.

	* lib/dndGuest/dndRpcV3.cc, vmware-user/copyPasteDnDWrapper.cpp,
	  vmware-user/dndUI.cpp, vmware-user/dndUI.h, vmware-user/dragDetWnd.cpp,
	  vmware-user/dragDetWnd.h: several DnD fixes; make the DnD code behave
	  more like other GTK applications (based on analysing the flow of signals
	  on a test widget), and get rid of one of the detection windows, merging
	  both the Unity and non-Unity windows. Also, some code refactoring.

	* lib/ghIntegration/*, lib/guestRpc/*, lib/include/guestCaps.h,
	  lib/include/unityCommon.h: add stubs for a few new GHI functions (setting
	  window focus, tray icon updates and enhanced app info) - currently only
	  implemented on Win32.

	* lib/ghIntegration/ghIntegrationX11.c: examine WM_CLASS when determining
	  which DE Tools are running in. The previous code was failing to show a
	  few entries set as "OnlyShowIn=xxx" because checking WM_NAME wasn't enough
	  to properly detect the desktop environment in certain cases.

	* lib/guestApp/guestAppPosixX11.c: use gconftool-2 to detect the user's
	  browser, since gnome-open fails to open "file://" URLs which contain query
	  strings. Also, use "kde-open" when running under KDE.

	* lib/guestInfo/guestInfo.c, lib/include/wiper.h, lib/wiper/wiperPosix.c:
	  allow shrinking LVM partitions.

	* lib/include/appUtil.h: partial refactoring of icon retrieval code.

	* lib/include/dbllnklst.h, lib/misc/dbllnklst.c: inline linked list
	  functions.

	* lib/include/guest_os.h, lib/misc/hostinfoPosix.c: add a couple of
	  distros to the known distro list.

	* lib/include/netutil.h, lib/netUtil/netUtilLinux.c: add Linux interface
	  name, index lookup routines.

	* lib/include/system.h, lib/system/systemLinux.c,
	  services/plugins/timeSync/timeSync.c: fixes a few bugs in the backwards
	  time sync code, mostly due to unit mismatches.

	* lib/include/unityWindowTracker.h, lib/unity/unity.c,
	  lib/unity/unityPlatform.h, lib/unity/unityPlatformX11Window.c,
	  lib/unityWindowTracker/unityWindowTracker.c: expose two new properties in
	  the window tracker to save an RPC to retrieve those properties.

	* lib/include/vmtoolsApp.h, services/vmtoolsd/mainLoop.c,
	  services/vmtoolsd/pluginMgr.c, services/vmtoolsd/toolsCoreInt.h: add
	  support for "application providers", which allow plugins to add support to
	  new kinds of application frameworks through vmtoolsd.

	* lib/unity/unityPlatformX11.c: only enable GIO channels when Unity is
	  actually active.

	* lib/unity/*: cleanup old GTK1 code.

	* libvmtools/vmtoolsConfig.c: don't complain if config file is empty.

	* modules/linux/dkms.conf, modules/linux/dkms.sh: fix dkms.conf, and provide
	  a script to create a dkms tree from open-vm-tools.

	* modules/linux/shared/vmci_queue_pair.h, modules/linux/vmci/vmciKernelIf.c:
	  remove two (now) unneeded functions.

	* modules/linux/vmhgfs/*: code cleanups, properly initialize the list head,
	  and allow receives to timeout so that hibernation works.

	* modules/linux/vmxnet/vmxnet.c, modules/linux/vmxnet3/vmxnet3_drv.c: fix
	  drivers for kernels 2.6.29+.

	* modules/linux/vmxnet3/*: add shared memory support for faster communication
	  between user space and the device backend; this doesn't affect the regular
	  driver functionality, but is used by some VMware code not part of Tools.

	* modules/linux/vsock/*: fix an issue where it was possible for users to send
	  VMCI datagrams directly to the hypervisor.

	* scripts/common/vmware-user.desktop: work around a bug in KDE where desktop
	  files with "NoDisplay" fail to autostart.

	* scripts/freebsd/suspend-vm-default: use netif to bring down interfaces,
	  since dhclient doesn't understand "-r" in the default install.

	* services/plugins/vmbackup/*: add some new code used by enhancements being
	  done in the Win32 version of the plugin.

	* services/vmtoolsd/mainPosix.c: fix running in background when executing
	  vmtoolsd while relying on PATH.

	* services/vmtoolsd/Makefile.am: point to the correct plugin install
	  directory.

	* toolbox/*: better command line handling in the CLI utility, plus some code
	  cleanup.

	* vmware-user/copyPaste.c, vmware-user/copyPasteDnDWrapper.cpp,
	  vmware-user/copyPasteDnDWrapper.h, vmware-user/dnd.c: properly unregister
	  RPCs to avoid multiple registrations (and the ensuing ASSERT) in certain
	  cases.

	* vmware-user/copyPasteUI.{cpp,h}: change way data is retrieved from the
	  clipboard, using the gtk+ clipboard wait APIs (which turn out to be more
	  reliable than the previous approach).

2009-06-18	Marcelo Vanzin	 <mvanzin@vmware.com>
	* Resync with internal trunk (2009.06.15)

	* docs/api/Makefile.am: replace all variables in the doxygen conf file with
	  the correct values.

	* lib/appUtil/appUtilX11.c: try alternate file extensions during icon search.
	  Sometimes a package lists its icon as icon-foo.png, but the package didn't
	  include icon-foo.png.	 Instead, it included icon-foo.xpm.	 Ex: Ubuntu
	  8.04's hwtest.

	* lib/guestApp/guestAppPosixX11.c: change detection of the default browser so
	  that the new help system works; the help files are not yet available in
	  open-vm-tools.

	* lib/hgfs/*, lib/include/cpName.h, lib/include/cpNameLite.h,
	  lib/include/hgfsUtil.h: avoid compiling certain parts of the source in
	  kernel modules.

	* lib/hgfsServer/*: return correct error when files / directories don't
	  exist.

	* lib/include/hgfsChannel.h, modules/linux/vmhgfs/*: added support for vsock
	  channel for HGFS.

	* lib/include/util.h, lib/misc/utilMem.c: un-inline a bunch of functions to
	  reduce the size of binaries.

	* lib/include/vm_tools_version.h: bumped Tools version.

	* lib/resolution/resolutionX11.c: fix for issue where the host would send
	  a "topology set" command with a top-left corner that was not (0, 0) and
	  cause the screen to be reconfigured in the wrong way.

	* lib/unity/unityPlatformX11Window.c: check for errors in calls to
	  XQueryTree.

	* lib/wiper/wiperPosix.c: check return of fgets() to avoid compiler warnings.

	* libvmtools/vmtools.c: fix initialization of wiper library on Win32.

	* modules/Makefile.am: install vsock header file on Linux.

	* modules/freebsd/vmhgfs/*, modules/linux/vmci/*, modules/linux/vsock/*:
	  changes related to 64-bit Mac drivers, don't affect either the FreeBSD
	  or Linux drivers.

	* modules/linux/vmhgfs/hgfs.h: removed duplicate file.

	* modules/linux/vmhgfs/fsutil.c, modules/linux/vmhgfs/inode.c,
	  modules/linux/vmhgfs/module.h: fix issue where two files would get the
	  same inode number in some situations.

	* modules/linux/vmxnet/vmxnet.c: re-probe vmxnet2 device features on resume
	  from hibernate, to cover the case where a VM is resumed on a platform with
	  a different version of the device backend (bug #2209565).

	* scripts/resume-vm-default, scripts/suspend-vm-default: use NetworkManager
	  to handle networking where it is available.

	* services/plugins/hgfsServer/Makefile.am, services/plugins/vix/Makefile.am:
	  fix installation of vmusr plugins that are shared with vmsvc.

	* services/plugins/timeSync/timeSync.c: fix backwards time sync.

	* services/vmtoolsd/cmdLine.c, toolbox/toolbox-cmd.c: print build number as
	  part of "-v" output.

	* toolbox/toolboxcmd-shrink.c: correctly ignore unsupported partitions when
	  shrinking.

	* toolbox/toolbox-gtk.c: changes for the new help system, not yet available
	  for open-vm-tools.

	* toolbox/toolboxInt.{c,h}, toolbox/toolboxScripts.c: some code refactoring.

	* vmware-user/Makefile.am: fix linking when compiling without gtkmm.

2009-05-22	Marcelo Vanzin	 <mvanzin@vmware.com>
	* Resync with internal trunk (2009.05.18)

	* configure.ac, m4/vmtools.m4: check for PAM and enable PAM support when
	  available.

	* configure.ac, services/plugins/*/Makefile.am, tests/*/Makefile.am: avoid
	  the "-rpath" hack to create plugins, using plugin_LTLIBRARIES instead (and
	  manually fixing things when necessary). Thanks to Dominique Leuenberger for
	  the suggestion (and sample patch).

	* docs/api/Makefile.am: fix doc target directories.

	* configure.ac, lib/Makefile.am, lib/deployPkg/*, lib/include/deployPkg.h:
	  remove the deployPkg code, which depends on some VMware code that is not
	  yet open source.

	* lib/backdoor/*, lib/hgfs/*, lib/hgfsBd/*, lib/include/*,
	  lib/include/compat/*, lib/message/*, lib/misc/dbllnklst.c,
	  lib/rpcOut/rpcout.c: as part of sharing code between user-level code and
	  kernel modules, some files have been tagged with more than one license.

	* lib/dndGuest/*, lib/include/dndBase.h, lib/include/dndMsg.h,
	  lib/include/unity*, lib/unity/unityPlatformX11.c: implement mouse movement
	  from within the guest; this avoids a dependency on "unofficial" mouse APIs
	  in the VMware host code, making DnD more reliable, and makes a few things
	  (such as cancelling an ongoing DnD operation) easier.

	* lib/file/filePosix.c: make File_FullPath()'s behavior consistent when the
	  input path starts with a '/'.

	* lib/ghIntegration/ghIntegration.c: send more info about the "start menu"
	  contents to the host (only usable for Windows guests).

	* lib/ghIntegration/ghIntegrationX11.c: prettify the category names of menu
	  items. This is a temporary solution before actually reading this
	  information from .desktop files.

	* lib/guestApp/guestApp.c, libguestlib/vmGuestLib.c, lib/include/guestApp.h,
	  toolbox/toolbox-gtk.c, vmware-user/foreignVM*, vmware-user/vmware-user.cpp,
	  configure.ac, lib/Makefile.am, lib/include/socketMgr.h, lib/socketMgr.*:
	  remove code related to "foreign VM" support, which was never really used.

	* lib/guestInfo/guestInfo.c, lib/include/wiper.h, lib/wiper/wiperPosix.c:
	  properly report disk info for LVM volumes.

	* lib/hgfsServer/hgfsDirNotify*, lib/hgfsServer/hgfsServer.c: add support
	  for directory / file monitoring. It's currently only implemented on
	  Mac OS X.

	* lib/hgfsServer/hgfsServer*: fix issue where it was possible to
	  create a file on a read-only share on Windows hosts.

	* lib/hgfsServer/hgfsServer*, lib/hgfsServerManagerGuest/*,
	  lib/include/hgfs*.h, services/plugins/hgfsServer/hgfsPlugin.c,
	  services/plugins/vix/foundryToolsDaemon.c: some refactoring caused by the
	  work to make HGFS support pluggable transports.

	* lib/include/procMgr.h, lib/procMgr/procMgrPosix.c:
	  remove ProcMgr_GetAsyncStatus().

	* lib/include/vmsupport.h, scripts/vm-support, xferlogs/xferlogs.c,
	  services/plugins/guestInfo/guestInfoServer.c: new feature to automatically
	  collect support data from the guest from the VMware UI.

	* lib/panicDefault/*, lib/user/*: change file names to avoid clash with
	  another file (Mac OS linker doesn't really like that).

	* lib/rpcChannel/rpcChannel.c: try to reinitialize the outbound channel on
	  failure.

	* lib/vixTools/vixTools.c, lib/include/vixCommands.h,
	  lib/include/vixOpenSource.h: add backend for new VIX API call to list the
	  guest's filesystems.

	* libvmtools/vmtoolsLog.c: lazily open the log file, instead of opening it
	  during configuration of the log system. This way two processes can use the
	  same conf file and not overwrite each other's log files (assuming the conf
	  data is sane).

	* modules/Makefile.am, modules/linux/vmci/Makefile.kernel,
	  modules/linux/vsock/Makefile.kernel: don't store VMCI module symbols in
	  /tmp during build; this avoids a possible symlink attack that could cause
	  data to be overwritten when building open-vm-tools.

	* modules/*/*, vmblock-fuse/*: remove a lot of duplicated files by either
	  moving them to a shared location or reusing files that were already
	  available elsewhere.

	* modules/freebsd/vmblock/subr.c, modules/freebsd/vmblock/vmblock_k.h,
	  modules/freebsd/vmblock/vnoops.c: fix a possible kernel crash caused by
	  trying to read / write to the mount point (something vmware-user does to
	  detect whether it's using fuse or non-fuse vmblock).

	* modules/linux/pvscsi/*: adds support for a generic msg framework that is
	  currently used to deliver hot-plug/unplug notifications; get rid of a few
	  divisions; fix a bug where pvscsi_probe could return 0 (aka success) if
	  a call to kmalloc() failed; remove a few unused fields.

	* modules/linux/shared/vmci*: mostly changes related to the host VMCI drivers
	  (removed user-level queue-pair daemon, added support for trusted VMCI
	  endpoints) to keep binary compatibility between the host and guest VMCI
	  drivers.

	* modules/linux/hgfs/inode.c: check host's access rights when checking file
	  permissions, so that permissions are correctly checked when the guest's
	  user id is different from the host's user id.

	* modules/linux/bdhandler.*, modules/linux/filesystem.c; modules/linux/tcp.*,
	  modules/linux/transport.*,: postpone opening the channels so that module
	  can load successfully even if shared folders are disabled on the host;
	  fix a synchronization problem between recv and close/open; allow
	  hibernation to work by timing out the recv thread; correctly handle
	  failures in recv, including injecting a dummy error to the pending
	  requests when the recv thread exits; move the recv code to the channel's
	  implementation so that it can be simpler.

	* modules/linux/vmxnet3/vmxnet3.c, modules/linux/shared/compat_pci.h: fix
	  Wake-On-LAN for 2.6.27 and newer Linux guests.

	* modules/linux/vsock/linux/*: changes to support trusted VMCI host apps;
	  pull out notification and stats logging code into their own files.

	* modules/solaris/vmhgfs/vnode.c: do not substitute errors returned by
	  uiomove() calls with EIO, propagate returned error code (which is EFAULT)
	  up the stack.

	* services/vmtoolsd/Makefile.am, scripts/common/linux/pam.d/vmtoolsd-x64,
	  scripts/common/linux/pam.d/64/vmtoolsd: install the 64-bit PAM config
	  file with the correct name.

	* services/plugins/powerOps/powerOps.c: fix running default scripts by
	  making sure the path to the script is absolute.

	* services/vmtoolsd/Makefile.am, services/vmtoolsd/pluginMgr.c: use info
	  from the configure step to define the plugin path; this avoids requiring
	  a symlink to be placed in /etc/vmware-tools for vmtoolsd to be able to
	  find the plugins.

	* services/vmtoolsd/toolsRpc.c: send the build information to the host so
	  it's logged in the VM's logs.

	* toolbox/toolbox-cmd.c, toolbox/toolboxcmd-*, toolbox/toolboxCmdInt.h:
	  make string comparison case-insensitive on Win32.

	* toolbox/toolboxcmd-shrink.c: properly finish the shrinking process by
	  sending the "disk.shrink" RPC to the host.

	* toolbox/toolboxScripts.c: fix showing default settings for power ops
	  scripts (should show default script enabled, not script disabled).

	* vmblock-fuse/Makefile.am: fix compilation on FreeBSD.

	* vmware-user/copyPasteUI.cpp: disallow copy-paste text of size greater than
	  4MB, instead of truncating the data.

	* vmware-user/dndUI.*, lib/dndGuest/dnd.cc, lib/include/dndBase.h: remove
	  drag timeout callback (which was not needed), and add new signal to
	  indicate that a GH DnD was cancelled.

	* vmware-user/Makefile.am: remove C++ link hack (not needed now since
	  vmware-user has C++ code already).

2009-04-23	Marcelo Vanzin	 <mvanzin@vmware.com>
	* Resync with internal trunk (2009.04.17)

	* configure.ac, Makefile.am, docs/*: build API docs for vmtools; the
	  documentation files are provided under a BSD-style license.

	* configure.ac, Makefile.am, guestd/*, lib/Makefile.am, lib/guestInfo/*,
	  lib/include/vmbackup_def.h, lib/include/vmBackup.h, lib/vmBackupLib/*,
	  libvmtools/Makefile.am, services/plugins/vmbackup/vmbackup_def.h: remove
	  guestd from open-vm-tools, and clean up code in other places that was only
	  there because of guestd.

	* configure.ac, modules/solaris/vmblock/*, modules/solaris/vmmemctl/*: add
	  Solaris vmblock and vmmemctl driver sources. The vmmemctl module also
	  includes a user-level daemon (vmmemctld.c).

	* lib/conf/conf.c, lib/include/conf.h, libvmtools/vmtoolsConfig.c: remove
	  unused config options.

	* lib/deployPkg/toolsDeployPkg.h: code refactoring.

	* lib/dnd/dndClipboard.c: if size of clipboard exceeds the maximum backdoor
	  packet size, only keep text data (dropping the RTF data).

	* lib/dnd/dndLinux.c, lib/include/dnd.h, lib/include/vmblock.h,
	  vmware-user/copyPaste.c, vmware-user/dnd.c, vmware-user/vmware-user.cpp,
	  vmware-user/vmwareuserint.h, vmware-user-suid-wrapper/main.c: detect
	  whether plain vmblock or vmblock-fuse is being used, allowing the same
	  executable to be used with either.

	* lib/dndGuest/*, vmware-user/copyPaseDnDWrapper.{cpp,h},
	  vmware-user/dndUI.{cpp,h}, vmware-user/dragDetWnd.{cpp,h}:
	  vmware-user/vmware-user.cpp: more DnD V3 protocol work.

	* lib/ghIntegration/*, lib/include/guestCaps.h, lib/include/unityCommon.h:
	  work related to mapping Outlook folders over HGFS and exposing the Windows
	  Recycle Bin to the host (doesn't really affect open-vm-tools).

	* lib/ghIntegration/ghIntegrationX11.c: restore the native environment when
	  launching external applications. This doesn't really affect open-vm-tools.

	* lib/guestRpc/*, vmware-user/copyPasteUI.{cpp,h}: implement RTF and file
	  contents copy & paste.

	* lib/include/circList.h, lib/include/vm_basic_math.h,
	  lib/include/vm_device_version.h, modules/linux/*, modules/Makefile.am:
	  changes to share files between the user space code and the kernel code,
	  instead of duplicating the same source files in different places.

	* lib/include/rpcChannel.h, lib/rpcChannel/*, tests/testDebug/testDebug.c,
	  test/vmrpcdbg/debugChannel.c: some code cleanup, and fix crash when dealing
	  with multiple reset messages.

	* lib/include/system.h, lib/system/systemLinux.c,
	  services/vmtoolsd/mainPosix.c: remove System_Daemon() (replaced with
	  Hostinfo_Daemonize()).

	* lib/include/unityCommon.h, lib/unity/*: ressurrect UNITY_RPC_WINDOW_SHOW
	  and UNITY_RPC_WINDOW_HIDE RPCs.

	* lib/procMgr/procMgrPosix.c: fix ProcMgr_IsProcessRunning().

	* lib/system/systemLinux.c: fix shutdown / reboot commands on Solaris; fix
	  rebuilding of native environment from variables set by VMware scripts (this
	  last one doesn't really affect open-vm-tools).

	* lib/unicode/unicodeSimpleTypes.c: speed up UnicodeIANALookup, and fix case
	  where C++ constructors could call UnicodeIANALookup before Unicode_Init()
	  was called by lazily creating the internal cache.

	* libguestlib/*: link libguestlib against libvmtools. This avoids having
	  two definitions of certain symbols (like Debug()) when an application
	  links to both libraries.

	* modules/linux/vmblock/linux/control.c: only set directory entry owner when
	  needed.

	* modules/linux/vmhgfs/bdhandler.{c,h}, modules/linux/vmhgfs/dir.c,
	  modules/linux/vmhgfs/file.c, modules/linux/vmhgfs/filesystem.c,
	  modules/linux/vmhgfs/fsutil.c, modules/linux/vmhgfs/inode.c,
	  modules/linux/vmhgfs/module.{c,h}, modules/linux/vmhgfs/page.c,
	  modules/linux/vmhgfs/request.{c,h}, modules/linux/vmhgfs/super.c,
	  modules/linux/vmhgfs/tcp.{c,h}, modules/linux/vmhgfs/transport.{c,h}:
	  cleanup use of atomic variables in HGFS; add a transport abstraction layer,
	  and add an initial version of a socket-based transport (not yet stable and
	  not yet supported by any released VMware product).

	* modules/linux/vmxnet3/vmxnet3.c: fix build on kernel 2.6.29.

	* modules/linux/vsock/af_vsock.c: export more functions to other kernel
	  modules; some changes to statistics gathering code.

	* modules/solaris/vmhgfs/filesystem.c: make module loadable on Solaris 9.

	* modules/solaris/vmhgfs/vnode.c: unify mapping of HGFS to Solaris error
	  codes.

	* scripts/*: restart network before running user scripts in resume scripts.

	* services/plugin/powerOps/powerOps.c: fix running default power scripts.

	* services/vmtoolsd/pluginMgr.c: better error logging.

	* toolbox/toolbox-cmd.c: fix help string.

	* vmblock-fuse/block.c: fix vmblock-fuse compilation on FreeBSD.

2009-03-18	Marcelo Vanzin	 <mvanzin@vmware.com>
	* Resync with internal trunk (2009.03.13)

	* configure.ac: check for FreeBSD kernel tree when building modules; warn
	  about which version of make to use when building kernel modules on FreeBSD
	  and Solaris; add compiler defines for identifying Solaris 9 and 11.

	* configure.ac, modules/Makefile.am: handle SYSDIR on FreeBSD.

	* guestd/main.c, modules/solaris/vmhgfs/Makefile: remove HGFS-related that
	  is now obsolete with the recent changes to the HGFS module on Solaris.

	* guestd/toolsDaemon.c: default to the configuration dir when the power
	  script path is not absolute.

	* guestd/toolsDaemon.c, lib/include/guestInfo.h, lib/netUtil/netUtilLinux.c:
	  handle case when all network interfaces have been disabled and send an
	  "unknown" IP address to the host.

	* guestd/toolsDaemon.c, services/vmtoolsd/toolsRpc.c: always send
	  TOOLS_VERSION_UNMANAGED from an open-vm-tools build, so there's no need for
	  a config file option anymore.

	* hgfsclient/*: make it link to libvmtools to avoid code duplication.

	* lib/appUtil/appUtil.c: update list of "skippable" apps when figuring out
	  an application's path.

	* lib/auth/authPosix.c, scripts/linux/pam.d/*, guestd/Makefile.am,
	  services/vmtoolsd/Makefile.am : change the name of the PAM application to
	  "vmtoolsd" to reflect the new service name.

	* lib/dnd/dndFileContentsUtil.h, lib/dnd/dndInt.h, lib/dndGuest/*.hh, and
	  corresponding files in lib/include: relocate private headers.

	* lib/ghIntegration/ghIntegration.c, lib/ghIntegration/ghIntegrationInt.h,
	  lib/ghIntegration/ghIntegrationX11.c, lib/include/unityCommon.h: glue code
	  for Outlook mirrored folder, which does not affect open-vm-tools.

	* lib/guestRpc/guestlibV3.x, lib/include/vmGuestLib.h,
	  libguestlib/vmGuestLib.c: add new guestlib counters.

	* lib/include/conf.h, toolbox/toolbox-gtk.c: remove the need for the
	  "helpdir" config option; this doesn't really affect open-vm-tools since the
	  help files are not yet included.

	* lib/include/guest_os.h, lib/misc/hostinfoPosix.c: more guest OS names; fix
	  name used to identify Solaris to match what VMware's host code expects.

	* lib/include/guestStats.h: documentation changes.

	* lib/include/hostinfo.h, lib/user/hostinfoPosix.c: add a new function that
	  behaves like daemon(3), but is more Mac OS-friendly.

	* lib/include/toolsLogger.h, lib/Makefile.am, lib/toolsLogger/*: removed
	  library, which is not used anymore.

	* lib/include/vm_basic_types.h, lib/misc/timeutil.c: fixes to compile under
	  (Open) Solaris 11.

	* lib/include/vmtoolsApp.h, services/plugins/vmbackup/stateMachine.c,
	  services/vmtoolsd/mainLoop.c, services/vmtoolsd/mainPosix.c,
	  services/vmtoolsd/serviceObj.c, services/vmtoolsd/toolsCoreInt.h: add new
	  signal handler to gather debugging information from a running vmtoolsd
	  instance.

	* lib/misc/posixPosix.c: fix off-by-one error.

	* lib/unity/unity.c, lib/unity/unityPlatform.h, lib/unity/unityPlatformX11.c,
	  lib/unity/unityPlatformX11Settings.c: always send Unity updates using RPCI;
	  this avoids a possible race between replying to an incoming RPC and sending
	  an Unity update from a different thread; also, API documentation updates.

	* lib/unity/unityPlatformX11.c: verify the DnD detection window was
	  initialized before actually using it.

	* lib/unity/unityPlatformX11Settings.c, lib/unity/unityPlatformX11Window.c:
	  reset _NET_WM_DESKTOP as necessary before exiting Unity; this could cause
	  guest taskbars to disappear when in Unity mode.

	* lib/unity/unityPlatformX11.c, lib/unity/unityPlatformX11Window.c,
	  lib/unity/unityX11.h: examine WM_CLIENT_LEADER when gathering application
	  information; certain applications use this property to define the window
	  where the WM_COMMAND property should be.

	* lib/vixTools/vixTools.c: do not follow symlinks when deleting files in the
	  guest using the VIX API.

	* libvmtools/vmtools.c, libvmtools/vmtoolsLog.c: allow the logging subsystem
	  to be re-configured, and clean up the logging data when unloading the
	  library; allow ${USER} and ${PID} to be used in log file paths.

	* modules/freebsd/vmblock/subr.c, modules/freebsd/vmblock/vnops.c: fix kernel
	  panic on FreeBSD 7.

	* modules/linux/*/Makefile: remove GCC version check.

	* modules/linux/*/compat_wait.h: fix COMPAT_DEFINE_WAIT for "vanilla" 2.4
	  kernels.

	* modules/linux/vmhgfs/Makefile.normal: fix build of HGFS module on 2.4
	  kernels.

	* modules/linux/vmxnet/*, modules/linux/vmxnet3/*: avoid using compat
	  functions when they're not needed; add compatibility functions for
	  newer Linux kernels.

	* modules/linux/vsock/linux/af_vsock.c: fix two races; one when the socket
	  state changed between calls to VSockVmciRecvStreamCB and
	  VSockVmciRecvPktWork, and another when trying to read from the socket after
	  a RST arrived after the socket got a detach notification.

	* modules/solaris/vmxnet3/*: add Solaris vmxnet3 driver.

	* rpctool/*: add "rpctool", a simple, stand-alone tool to send RPC commands
	  to the host software.

	* services/plugins/guestInfo/guestInfoServer.c: don't cache configuration
	  data.

	* services/plugins/guestInfo/perfMonLinux.c: fix problem with overwriting
	  flags after GuestInfoMonitorReadMeminfo() was called. (Same as fix to
	  lib/guestInfo on previous release.)

	* services/plugins/powerOps/powerOps.c: handle power ops-related options
	  sent from the host.

	* services/vmtoolsd/mainLoop.c: handle re-loading the configuration file.

	* services/vmtoolsd/mainPosix.c: exec after forking on Mac OS, since
	  CoreFoundation classes don't work after a fork.

	* services/vmtoolsd/pluginMgr.c: allow both 32 and 64 bit plugins to be
	  installed on Solaris by loading them from the appropriate directory;
	  add library loading code that is not really needed (nor used) in
	  open-vm-tools.

	* services/vmtoolsd/toolsRpc.c: send another "capability" the host expects
	  from Tools.

	* toolbox/toolbox-gtk.c: add F1 shortcut to invoke help.

	* toolbox/toolboxScripts.c: fix issue with freeing data that should not be
	  freed.

	* vmware-user/*: implement the new DnD protocol (V3).


2009-02-18	Marcelo Vanzin	 <mvanzin@vmware.com>
	* Resync with internal trunk (2009.02.13)

	* configure.ac, m4/vmtools.m4: clean up a lot of the library detection code.

	* configure.ac: remove support for gtk+ 1.2 (code already depended on it in
	  any case); enforce need for glib 2.6.0 or later due to new code being
	  added; add detection for gtkmm; check for C++ compiler when it's needed;
	  reorder the include path to avoid clashing with system headers in some
	  situations.

	* guestd/foundryToolsDaemon.*, vmware-user/foundryToolsDaemon.*,
	  guestd/Makefile.am, vmware-user/Makefile.am: moved shared source files to
	  a new place to avoid duplication.

	* hgfsmounter/hgfsmounter.c: add support for Solaris.

	* lib/appUtil/appUtilX11.c: fix loading of icons when the name has a period.

	* lib/dnd/dndClipboard.c, lib/dnd/dndInt.h, lib/dnd/dndMsg.c,
	  lib/dnd/Makefile.am, lib/dndGuest/*, lib/include/copyPasteBase.h,
	  lib/include/copyPaste.hh, lib/include/copyPasteRpc.hh,
	  lib/include/copyPasteRpcV3.hh, lib/include/dndBase.h,
	  lib/include/dndClipboard.h, lib/include/dndFileContentsUtil.h,
	  lib/include/dndFileList.hh, lib/include/dnd.h, lib/include/dnd.hh,
	  lib/include/dndInt.h, lib/include/dndMsg.h, lib/include/dndRpc.hh,
	  lib/include/dndRpcV3.hh, lib/include/dndTransportGuestRpc.hh,
	  lib/include/dndTransport.hh, lib/include/libExport.hh,
	  vmware-user/copyPaste.cpp, vmware-user/copyPasteUI.{cpp,h},
	  vmware-user/copyPasteV3.h, vmware-user/copyPasteWrapper.{cpp,h},
	  vmware-user/dnd.cpp, vmware-user/Makefile.am,
	  vmware-user/vmware-user.{c,cpp}, vmware-user/vmwareuserInt.h,
	  vmware-user/stringxx/string.{cc,hh}, vmware-user/stringxx/ubstr_t.hh: add
	  support for new version of the DnD protocol.

	* lib/guestInfo/guestInfoPerfMonLinux.c: fix problem with overwriting flags
	  after GuestInfoMonitorReadMeminfo() was called.

	* lib/guestRpc/guestlibV3.x, lib/include/vmGuestLib.h,
	  libguestlib/vmGuestLib.c: add new host stats.

	* lib/guestRpc/Makefile.am: fix a few compatibility issues with non-GNU
	  versions of make.

	* lib/hgfsServer/hgfsServer.c, lib/hgfsServer/hgfsServerInt.h,
	  lib/hgfsServer/hgfsServerLinux.c, */hgfsProto.h,
	  modules/freebsd/vmhgfs/vnops.c, modules/freebsd/vmhgfs/vnopscommon.{c,h}:
	  don't trust the local VFS layer, check the HGFS server to see if an
	  operation would succeed.

	* lib/include/rpcChannel.h, lib/rpcChannel/*: add new Guest RPC channel
	  abstraction library used by the new "core services" code.

	* lib/include/vmrpcdbg.h, tests/*: add test code from the "core services"
	  project.

	* lib/include/vmtoolsApp.h, lib/include/vmtoolsd_version.h,
	  services/vmtoolsd/*: add "vmtoolsd", the new service "shell" used in the
	  "core services" project.

	* lib/unity/unityPlatformX11Window.c: don't send initial "region" updates for
	  shaped windows. This works around an issue where resizing a shaped window
	  would not work as expected in Unity mode.

	* lib/wiper/wiperPosix.c: fix major number detection for disks on newer Linux
	  kernels.

	* libvmtools/Makefile.am: link more libraries needed by vmtoolsd and the new
	  plugins.

	* modules/linux/pvscsi/pvscsi.c, modules/linux/pvscsi/pvscsi_version.h: use
	  PCI-specific memory allocation functions and update driver version.

	* modules/linux/vmci/vmciKernelIf.c: disable queue pair support in the host
	  version of the driver on older Linux kernels. This doesn't affect the
	  guest driver.

	* modules/linux/vmci/vmci_queue_pair.h: implement MSG_PEEK support on
	  Linux driver.

	* modules/linux/vmhgfs/bdhandler.c, modules/linux/vmhgfs/compat_sched.h,
	  modules/linux/vmmemctl/os.c: fix issue with HGFS module interfering with
	  suspend / hibernation on newer Linux kernels (bug #2523263).

	* modules/linux/vmhgfs/compat_cred.h, modules/linux/vmhgfs/file.c,
	  modules/linux/vmhgfs/filesystem.c, modules/linux/vmhgfs/inode.c: changes
	  for compatibility with newer Linux kernels, where it's not possible to
	  change fsuid/capabilities directly anymore.

	* modules/linux/vmhgfs/compat_pagemap.h, modules/linux/vmhgfs/page.c:
	  fix warning, and compatibility changes for newer Linux kernels
	  (2.6.28.1 and newer; bug #2530616).

	* modules/linux/vmhgfs/inode.c: fix creation of symlinks (bug #2531303).

	* modules/linux/vmxnet/vmxnet.c: use PCI-specific memory allocation
	  functions.

	* modules/linux/vmxnet/vmxnet.c, modules/linux/vmxnet3/vmxnet3.c: add option
	  to disable LRO.

	* modules/linux/vsock/af_inet.{c,h}, modules/linux/vsock/util.{c,h}: add
	  MSG_PEEK support; remove ifdefs that were disabling support for queue
	  pairs on the host kernel module; fix compilation with certain versions
	  of gcc (bug #2531283).

	* modules/solaris/vmhgfs/*: move backdoor handling code to the HGFS driver;
	  this makes the user-level code to handle the driver (currently in
	  vmware-guestd) obsolete.

	* modules/solaris/vmxnet/*: add vmxnet driver for Solaris, under the CDDL.

	* services/plugins/*: add all currently available "core services" plugins.
	  The current set of plugins provide the functionality available in
	  vmware-guestd; there are a few plugins that replace functionality from
	  vmware-user, but not all features are ported yet (and vmtoolsd - with
	  vmware-user plugins - and vmware-user cannot run at the same time).

	* toolbox/toolboxAbout.c: fix the copyright character.

	* toolbox/toolbox-cmd.c: reword a few messages, fix typos.

	* toolbox/toolboxCmdInt.h, toolbox/toolboxcmd-record.c,
	  toolbox/toolbox-gtk.c, toolbox/toolboxRecord.c: remove the "record"
	  functionality from tools (due to internal request).

	* toolbox/toolboxInt.c, toolbox/toolboxScripts.c, toolbox/toolbox-scripts.c:
	  changes to use the new config file format used by the "core services" code.

	* */Makefile.am: make sure 'rm' and 'mv' are being used instead of $(RM) and
	  $(MV) (which are not defined by automake; bug #2492040).

2009-01-21	Marcelo Vanzin	 <mvanzin@vmware.com>
	* Resync with internal trunk (2009.01.19)

	* configure.ac: detect the presence of FUSE libraries.

	* configure.ac, Makefile.am: compile kernel modules for Solaris, and
	  vmblock-fuse module if FUSE is available.

	* lib/ghIntegration/ghIntegrationX11.c: retrieved localized application
	  names.

	* lib/guestInfo/guestInfo.c, lib/guestInfo/guestInfoPosix.c,
	  lib/include/hostinfo.h, lib/misc/hostinfo_misc.c, lib/misc/hostinfoPosic.c,
	  lib/vixTools/vixTools.c: refactoring to move code shared with other VMware
	  products to a common library.

	* lib/guestRpc/guestlibV3.x, lib/guestRpc/Makefile.am,
	  lib/include/vmGuestLib.h, libguestlib/*: add new iteration of the
	  "guestlib" protocol. This is a more extensible solution than the current
	  protocol, and should make it easier to add new information when needed.

	* lib/include/dynarray.h, lib/include/dynbuf.h, lib/misc/dynarray.c,
	  lib/misc/dynbuf.c: make some DynArray/DynBuf functions inline for speed.

	* lib/include/unityCommon.h: more documentation about the Unity protocol.

	* lib/unity/unityPlatformX11.c: fix Unity<->guest desktop ID mappings.

	* libvmtools/vmtoolsLog.c: change the way log domains are configured; now
	  sub-domains inherit the default handler for the app if no handler is
	  specified.

	* modules/freebsd/vmhgfs/state.c, modules/freebsd/vmhgfs/state.h,
	  modules/freebsd/vmhgfs/vnopscommon.{c,h}: implement support for mmap on
	  the Mac OS driver, which allows running executables from an HGFS
	  share. The FreeBSD module still does not support this functionality.

	* modules/freebsd/vmhgfs/transport.{c,h}: refactoring for sharing structure
	  definitions.

	* modules/linux/pvscsi/pvscsi.c, modules/linux/vmblock/linux/module.c,
	  modules/linux/vmci/vmci_drv.c, modules/linux/hgfs/module.c,
	  modules/linux/vmmemctl/os.c, modules/linux/vmsync/sync.c,
	  modules/linux/vmxnet/vmxnet.c, modules/linux/vmxnet3/vmxnet3.c: add support
	  for Novell's proprietary module info tag ("supported").

	* modules/linux/vmci/vmciKernelIf.c: add support for VMCI queue pairs on
	  the host. This does not affect the driver when it runs inside virtual
	  machines.

	* modules/linux/vmci/*.h, modules/linux/vsock/*.h: some changes in the common
	  code to support Mac OS X, and also queue pairs on the Solaris VMCI module.

	* modules/linux/vsock/af_vsock.{c,h}: add functions for registering with
	  the vsock driver from within the kernel.

	* modules/linux/*/Makefile: add $(LINUXINCLUDE) to the compiler flags; this
	  allows compiling the modules against recent kernels which removed that
	  from $(KBUILD_CPPFLAGS).

	* modules/Makefile.am: add support for compiling Solaris kernel modules.

	* modules/solaris/vmhgfs/*: initial release of the Solaris HGFS driver in
	  open-vm-tools. Driver is licensed under the CDDL 1.0.

	* vmblock-fuse/*: add the user-level implementation of the vmblock driver,
	  which is build on top of FUSE, to open-vm-tools. vmware-user hasn't yet
	  been modified to use this version of vmblock.

	* vmware-user/copyPaseV3.h: new header introduced during development of the
	  next version of copy paste / DnD for X11 platforms.


2008-11-23  Marcelo Vanzin  <mvanzin@vmware.com>
	* Resync with internal trunk (2008.12.19)

	* configure.ac, */Makefile.am: standardize on using libtool archives for
	  the libraries. This also means several makefiles were removed, since
	  there's no need to build two archives of the same library anymore.

	* configure.ac: add logic to detect glib 2.6; this is the minimum version
	  required by the "Core Services" project. Currently it's an optional
	  dependency.

	* configure.ac: disable Unity when user specifies --disable-multimon.

	* configure.ac: actually build the pvscsi modules if the detected kernel
	  version supports it.

	* configure.ac, Makefile.am, lib/guestInfo/Makefile.am,
	  lib/stubs/Makefile.am, libvmtools/*, lib/include/vmtools.h: add the
	  "vmtools" shared library used in the "Core Services" project. The library
	  is a collection of a lot of other libraries used by most VMware Tools
	  programs, and adds some functionality on top of glib that is used in the
	  "Core Services" project. Currently no other components from that project
	  are available as part of open-vm-tools.

	* lib/deployPkg/deployPkgLog.h, lib/deployPkg/toolsDeployPkg.h: moved private
	  headers out of the public header directory.

	* lib/guestRpc/Makefile.am, modules/Makefile.am: fix the makefiles so that
	  it's possible to build open-vm-tools from a directory different from the
	  source dir.

	* lib/hgfsServer/hgfsServer.c: changes to support aliases on Mac OS hosts.

	* lib/hgfsServer/hgfsServerLinux.c: changes to map Windows attributes to
	  Mac OS FileInfo.

	* lib/include/circList.h: removed unused file.

	* lib/include/unityCommon.h, lib/unity/unity.c: changes to add documentation
	  to various application RPCs used in Tools.

	* lib/misc/posixPosix.c, toolbox/toolboxScripts.c: fix include path for
	  syslimits.h on FreeBSD.

	* lib/unity/unityPlatformX11.c: better detection of the guest's work area
	  boundaries; fixes a problem when dragging a Window in Unity mode with
	  the host's task bar on the left/right of screen would result in weird
	  behavior.

	* lib/unity/unityPlatformX11Settings.c, lib/unity/unityPlatformX11Window.c,
	  lib/unity/unityX11.h: preserve the _NET_WM_DESKTOP setting when hiding
	  windows; this fixes a problem where hiding a panel and later restoring it
	  would result in the wrong _NET_WM_DESKTOP property being set for the panel,
	  causing it to only display on the first virtual desktop.

	* modules/linux/vmci/*: minor changes related to internal development of the
	  Mac OS drivers.

	* modules/linux/vmhgfs/page.c: fix HGFS driver for kernel 2.6.28.

	* modules/linux/vsock/linux/af_vsock.c: added code to gather some performance
	  statistics for the driver.

	* toolbox/toolbox-gtk.c: a few fixes to the help functionality.

	* vmware-user/vmware-user.c, vmware-user-suid-wrapper/main.c: change the
	  "blockFd" argument to have an extra dash so it follows the convention of
	  common command line parsing libraries.

	* Other bug fixes and changes in shared code that don't affect open-vm-tools.

2008-11-18  Marcelo Vanzin  <mvanzin@vmware.com>
	* Resync with internal trunk (2008.11.14)

	* lib/include/vm_version.h: Bumped TOOLS_VERSION.

	* guestd/toolsDaemon.c, lib/include/vm_app.h: changes related to host-side
	  configuration of power operations.

	* hgfsclient/hgfsclient.c, lib/dnd/dndCommon.c, lib/dnd/dndLinux.c,
	  lib/hgfs/*, lib/hgfsServer/*, lib/include/cpName.h, lib/include/dnd.h,
	  lib/include/hgfsEscape.h, lib/include/staticEscape.h, modules/*/vmhgfs/*,
	  vmware-user/dnd.c: refactor the HGFS character escaping code; escape
	  invalid characters when the sender allows characters in the file name that
	  the receiver does not allow.

   * lib/hgfsServer/hgfsServerLinux.c: return proper error code when the
     remote path points to a non-existent directory.

	* lib/deployPkg/deployPkg.c, lib/deployPkg/deployPkgLog.c,
	  lib/include/deployPkg.h, lib/include/rpcin.h, lib/rpcin/rpcin.c:
     refactoring for the Tools Core Services project.

	* lib/dnd/dndCommon.c: don't ASSERT if the staging directory is not
	  actually a directory.

	* lib/dynxdr/xdrutil.c, lib/include/xdrutil.h: more XDR-related utility
	  functions.

	* lib/file/file.c, lib/file/fileLockPrimitive.c, lib/include/vm_basic_defs.h:
	  replace use of strtok with strtok_r.

	* lib/guestApp/guestApp.c, lib/include/guestApp.h: more utility functions.

	* lib/guestApp/guestApp.c, lib/include/backdoor_def.h, vmware-user/pointer.c:
	  add the ability to detect whether the mouse hardware can be used in
     absolute mode, which allows for auto grab / auto ungrab to work.

	* lib/guestInfo/guestInfo.c, lib/guestInfo/guestInfoPosix.c,
     lib/guestRpc/nicinfo.x: provide the prefix length of an IPv6 address in a
     separate field of the NicInfo struct.

	* lib/guestInfo/guestInfoPerfMonLinux.c: reduce log spew.

	* lib/guestInfo/guestInfoServer.c, lib/include/guestInfo.h: changes related
	  to how the VMware code in the host handles Tools version information.

	* lib/include/unityCommon.h: changes related to documenting the Unity API.

	* lib/include/unity.h, lib/unity/*: remove a few unused RPCs, add a new
	  function used to notify the host of the status of Unity.

	* modules/freebsd/vmhgfs/state.c, modules/vmhgfs/freebsd/vnops.c,
     modules/vmhgfs/freebsd/vnopscommon.*: support symlinks on FreeBSD and
     Mac OS.

   * modules/freebsd/vmhgfs/worker.c: fix mutex not being unlocked in some
     error paths.

	* modules/linux/dkms.conf: add rudimentary dkms support.

	* modules/linux/pvscsi/*: add a driver for VMware's paravirtualized SCSI
	  device.

	* modules/linux/*/Makefile, modules/linux/vmci/Makefile.kernel,
	  modules/linux/vsock/Makefile.kernel: add support for exporing symbols,
	  needed for dependent modules to load if a kernel >= 2.6.26 is compiled
	  with CONFIG_MODVERSIONS. Make the vsock module reference the vmci module's
	  symbols.

	* modules/vmmemctl/freebsd/os.c: add sysctl to get driver status. Information
	  can be retrieved with "sysctl vm.vmmemctl".

   * modules/linux/vmci/vmci_defs.h, modules/linux/vmci/vmci_call_defs.h,
     modules/linux/vsock/vmciHostKernelAPI.h: changes related to VMCI work
     on VMware ESX.

	* modules/linux/vsock/linux/*: improve performance of some applications by
	  improving the poll behavior and sending less packets when waiting
	  for a response.

   * modules/linux/vmsnc/sync.c: fix panic on kernels < 2.6.20.

	* modules/linux/vmxnet3/vmxnet3.c, modules/linux/vmxnet3/vmxnet3_int.h,
     modules/linux/vmxnet3/vmxnet3_version.h: inherit net device features to
     avoid having the kernel setting the PSH flag on every TCP packet (among
     other issues).

	* modules/*/*/*: Reflected changes from elsewhere.

	* scripts/common/vmware-user.desktop: add missing "Type" information; fixes
	  auto-start of vmware-user on Ubuntu 8.10.

	* toolbox/*: fix a GTK+ warning caused when trying to unref a dialog
	  instance from a signal callback.

	* vmware-user/copyPaste.c: work around an issue when OpenOffice's "cut"
	  command is used; it sets the timestamp of both the clipboard and the
	  primary selection, but only puts data in the clipboard.

	* Other files: minor refactorings, and changes unrelated to open-vm-tools.

2008-10-13  Adar Dembo  <adar@vmware.com>
	* Resync with internal trunk (2008.10.09)

	* configure.ac, modules/Makefile.am: Added a command-line option to
	skip privileged operations during make install (requested by Dennis
	Leroy). Integrated new vmxnet3 kernel module.

	* configure.ac, lib/guestInfo/Makefile.am,
	lib/guestInfo/guestInfoPerfMonLinux.c, lib/include/vm_procps.h:
	Removed open-vm-tools dependency on libproc-dev by providing some procps
	bits and pieces in our own vm_procps.h (Sourceforge bug 1960947).

	* hgfsmounter/Makefile.am: Removed chown calls. Only call chmod
	if we're running as root (requested by Dennis Leroy).

	* */foreignVMToolsDaemon.c, */foundryToolsDaemon.[ch],
	lib/guestInfo/guestInfo.c, lib/guestInfo/guestInfoServer.c,
	lib/include/guestInfoServer.h, lib/include/vixTools.h,
	lib/rpcin/rpcin.c, lib/vixTools/vixTools.c, vmware-user/copyPaste.c,
	vmware-user/vmware-user.c: More refactoring from the Tools core
	services project.

	* */foundryToolsDaemon.c: Changed HGFS mounting behavior such that the
	global HGFS share is mounted at /mnt/hgfs instead of relying on
	"mount -a".

	* guestd/toolsDaemon.[ch], lib/include/backdoor_def.h,
	lib/include/system.h, lib/include/vm_app.h, lib/system/systemLinux.c:
	Added backwards time synchronization functionality. Moved Tools scripts
	checking from VMX to guestd.

	* hgfsmounter/hgfsmounter.c: Added handling for multiple "-o" flags on
	the command line.

	* lib/dynxdr/dynxdr.c: Fixed x_putint32 behavior on 64-bit Solaris.

	* lib/file/*, lib/include/codeset.h, lib/include/file.h,
	lib/include/fileIO.h, lib/include/hostinfo.h,
	lib/include/loglevel_user.h, lib/include/productState.h,
	lib/include/timeutil.h, lib/include/unicodeTypes.h,
	lib/include/vixCommands.h, lib/include/vix.h,
	lib/include/vm_basic_asm.h, lib/include/vm_basic_types.h,
	lib/include/vm_legal.h, lib/include/vm_product.h,
	lib/include/win32util.h, lib/include/x86cpuid.h, lib/misc/codeset.c,
	lib/misc/codesetOld.[ch], lib/misc/posixPosix.c, lib/misc/timeutil.c,
	lib/region/region.c, lib/string/bsd_vsnprintf.c, lib/unicode/*,
	lib/user/hostinfoPosix.c, modules/freebsd/vmhgfs/sha1.c,
	modules/*/*/vm_device_version.h, modules/linux/*/vmci_iocontrols.h,
	modules/linux/*/vmci_version.h, modules/linux/vmhgfs/hgfsEscape.h:
	Changes from work unrelated to open-vm-tools on internal trunk.

	* lib/ghIntegration/ghIntegrationX11.c: Fixed some bugs in menu-spec and
	desktop-entry-spec support in Linux guests.

	* lib/guestApp/guestApp.c, lib/include/guestApp.h,
	lib/include/statelogger_backdoor_def.h, toolbox/Makefile.am,
	toolbox/toolbox-cmd.c, toolbox/toolbox-gtk.c, toolbox/toolboxCmdInt.h,
	toolbox/toolboxGtkInt.h, toolbox/toolboxInt.h, toolbox/toolboxRecord.c,
	toolbox/toolboxcmd-record.c: Patches from Yiwen Zhang to add basic
	record/replay controls to the gtk and command-line toolbox apps.

	* lib/guestInfo/guestInfoPosix.c: Fixed a bug where we assumed the
	primary interface's addresses were all IPv4 addresses.

	* lib/guestInfo/guestInfoServer.c: Fixed a memory leak.

	* lib/guestRpc/Makefile.am, lib/guestRpc/unityActive.x,
	lib/include/guestCaps.h, lib/include/unityCommon.h, lib/unity/unity.c,
	vmware-user/Makefile.am: Added new unityActive xdr protocol, used for
	tracking whether Unity is enabled or not.

	* lib/hgfsServer/hgfsServer.c, lib/hgfsServer/hgfsServerLinux.c,
	lib/hgfsServerPolicyGuest/hgfsServerPolicyGuest.c,
	lib/include/hgfsProto.h: Fixed bug where we were invalidating HGFS
	handles on the share of entire drive ("/"). Added optional symlink
	following behavior to HGFS server. Fixed a UTF-8 validation issue.
	Added volume ID field to attribute fields.

	* lib/include/vm_tools_version.h: Bumped internal Tools backdoor
	version.

	* lib/include/vm_version.h: Bumped TOOLS_VERSION.

	* lib/procMgr/progMgrPosix.c: Fixed impersonation behavior so that
	uids are passed to setresuid instead of gids. Added alternate way to
	list processes in situations where a process lacks a command line.

	* lib/region/region.c: Reforked xorg miregion.c and apply open-vm-tools
	specific patches.

	* lib/unity/*, lib/unityWindowTracker/unityWindowTracker.c: Fixed an
	overflow that lead to a panic when a window title exceeded 1024 bytes.
	Fixed some initialization assumptions when using virtual desktops.

	* lib/unity/unityPlatformX11Window.c: Fixed an issue with restacking
	windows above non-existent windows. Other minor fixes.

	* modules/*/*/*: Reflected changes from elsewhere.

	* modules/linux/*/Makefile.kernel: Changed clean target to remove
	Module.markers and modules.order.

	* modules/linux/vsock/include/compat_sock.h,
	modules/linux/vsock/*: Fixed several issues in vsock.

2008-09-03  Adar Dembo  <adar@vmware.com>
	* Resync with internal trunk (2008.08.29)

	* Makefile.am, aclocal.m4, m4/*: Moved macros to 'm4' subdir.

	* compile, config.guess, config.sub, config/*, depcomp, install-sh,
	ltmain.sh, missing: Moved auxiliary build tools to 'config' subdir.

	* configure.ac: Moved macros and auxiliary build tools into separate
	subdirectories. Added command line option to force the use of gtk1 over
	gtk2. Cosmetic fixes. Reworked libicu detection. Switched over to
	libtool-2.2. Added library check for new gdk symbol. Added library
	check for libnotify. Reworked use of macros from AC_PATH_XTRA and
	some X11 library checks.

	* */foundryToolsDaemon.c, toolbox/toolbox-cmd.c, guestd/main.c,
	lib/guestInfo/guestInfoPerfMonLinux.c, lib/guestInfo/guestInfoPosix.c,
	lib/misc/posixPosix.c, lib/panic/panic.c, lib/system/systemLinux.c,
	modules/linux/vsock/linux/util.c, xferlogs/xferlogs.c: Added checks for
	return codes of certain functions and passed %s to formatted string
	functions where appropriate (needed to compile on Ubuntu Intrepid).

	* lib/appUtil/appUtilX11.c: Fixed command line skipping logic and added
	more icon paths. Removed unnecessary chdir(2) canonicalization logic.

	* lib/deployPkg/runDeployPkgPosix.c, lib/file/fileIO.c,
	lib/file/fileIOPosix.c, lib/file/fileLockPrimitive.c,
	lib/file/filePosix.c, lib/hgfsServer/hgfsServerLinux.c,
	lib/include/bsdfmt.h, lib/include/file.h, lib/include/fileIO.h,
	lib/include/iovector.h, lib/include/msgfmt.h, lib/include/str.h,
	lib/include/vm_basic_defs.h, lib/include/vm_basic_types.h,
	lib/misc/hostname.c, lib/misc/idLinux.c, lib/misc/posixPosix.c,
	lib/SLPv2Parser/*.c, lib/wiper/wiperPosix.c, toolbox/toolboxScripts.c:
	Added FreeBSD compatibility glue.

	* guestd/toolsDaemon.c, lib/file/file.c,
	lib/foundryMsg/foundryPropertyListCommon.c, lib/image/imageUtilPng.c,
	lib/include/appUtil.h, lib/include/backdoor_def.h, lib/include/conf.h,
	lib/include/cpuid_info.h, lib/include/guest_os.h,
	lib/include/hostinfo.h, lib/include/imageUtil.h,
	lib/include/imageUtilTypes.h, lib/include/log.h,
	lib/include/loglevel_user.h, lib/include/netutil.h,
	lib/include/posix.h, lib/include/timeutil.h, lib/include/util.h,
	lib/include/uuid.h, lib/include/vix.h, lib/include/vixOpenSource.h,
	lib/include/vm_atomic.h, lib/include/vm_legal.h,
	lib/include/vm_version.h, lib/include/x86cpuid.h,
	lib/misc/codesetOld.c, lib/misc/timeutil.c, lib/string/bsd_vsnprintf.c,
	lib/user/util.c, lib/user/utilPosix.c, lib/vixTools/vixTools.c,
	modules/linux/*/vmci_kernel_if.h, modules/linux/vmxnet/compat_timer.h,
	toolbox/toolboxCmdInt.h, toolbox/toolboxcmd-*.c:
	Changes from work unrelated to open-vm-tools on internal trunk.

	* lib/ghIntegration/ghIntegration.c,
	lib/guestRpc/ghiGetBinaryHandlers.x: Don't send oversized messages.
	Increased maximum number of binary handlers.

	* lib/ghIntegration/ghIntegrationX11.c, lib/guestApp/guestAppPosix.c,
	lib/include/guestApp.h, lib/include/system.h, lib/system/systemLinux.c,
	toolbox/toolbox-gtk.c: Improved "run program" functionality by
	restoring program environment and stripping any VMware wrapper script
	changes to LD_LIBRARY_PATH.

	* lib/guestApp/guestAppPosixX11.c: Now using glib to open URLs instead
	of system(3). Improved gnome and kde session detection.

	* lib/guestApp/Makefile.am: This library needed GTK_CPPFLAGS too.

	* lib/guestInfo/guestInfoInt.h, lib/guestInfo/guestInfoPosix.c,
	lib/guestInfo/guestInfoServer.c: Added logic to optionally convert
	subnet mask to an ASCII string.

	* lib/guestRpc/Makefile.am: Cleaned up generated xdr headers better.

	* lib/hgfsServer/hgfsServer.c, lib/hgfsServer/hgfsServerInt.h,
	lib/hgfsServer/hgfsServerLinux.c: Fixed	problems when packing V3
	replies.

	* lib/hgfsServer/hgfsServerLinux.c: Fixed UTF-8 normal form D/C
	conversions on the root directory.

	* lib/include/dndGuest.h: Changed preprocessor usage to allow gtk1 to
	access UnityDnD.

	* lib/include/dnd.h, vmware-user/copyPaste.c: Code motion.

	* lib/include/guestCaps.h: Resort the capabilities table.

	* lib/include/rpcin.h, lib/rpcIn/rpcin.c, : Beginnings of the Tools
	core services. This is a full-fledged refactoring of the Tools
	userlevel apps to a "service" vs. "plugin" programming model.

	* lib/include/vmblock.h, modules/*/vmblock/block.c,
	modules/*/vmblock/stubs.c, modules/*/vmblock/stubs.h: Changes needed
	to support the fuse-based implementation of vmblock (coming soon).

	* lib/include/vm_tools_version.h: Some Tools version bumps.
	
	* modules/*/*/*: Reflected changes from elsewhere.

	* modules/*/*/compat/compat_stdarg.h: Added compatibility wrappers for
	stdarg features.

	* modules/freebsd/vmhgfs/debug.*: Cosmetic fixes.

	* modules/freebsd/vmhgfs/*: Make driver compliant with HGFSv3.

	* modules/*/vmmemctl/vmballoon.c: Allow module to yield the processor
	when allocating many pages.

	* modules/linux/*/autoconf/cachector1.c,
	modules/linux/*/include/compat_sched.h,
	modules/linux/*/include/compat_semaphore.h,
	modules/linux/*/include/compat_slab.h,
	modules/linux/vmblock/linux/filesystem.c,
	modules/linux/*/Makefile.kernel,
	modules/linux/vmhgfs/bdhandler.c, modules/linux/vmhgfs/filesystem.c,
	modules/linux/vmhgfs/module.h, modules/linux/vmhgfs/request.c,
	modules/linux/vmsync/sync.c, modules/linux/vsock/linux/af_vsock.c: Fix
	modules for 2.6.27 kernels.

	* modules/linux/*/Makefile: Fixed DRIVER target.

	* modules/linux/vmci/vmci_drv.c: Moved interrupt registration to be
	after driver initialization.

	* modules/linux/vsock/linux/af_vsock.c,
	modules/linux/vsock/linux/af_vsock.c: Added optimized flow control
	protocol.

	* toolbox/toolboxScripts.c, toolbox/toolboxShrink.c: Cosmetic fixes.

	* vmware-user/copyPaste.c, vmware-user/dnd.c: Fixed edge case behavior
	with file copy paste and DnD.

	* vmware-user/modconfig.c, vmware-user/notify.c,
	vmware-user/vmware-user.c, vmware-user/vmwareuserInt.h: Added stubbed
	modconfig module out-of-date notification framework. Not useful for
	open-vm-tools, hence the stubs.

2008-08-08  Adar Dembo  <adar@vmware.com>
	* Resync with internal trunk (2008.07.24)

	* configure.ac, */Makefile.am: Landed support for command line Toolbox,
	Unity, vsock, and vmci. Refactored and reformatted a few things.
	Improved portability by using $(SED) and AC_PROG_SED instead of "sed",
	$(MKDIR_P) and AC_PROG_MKDIR_P instead of "mkdir -p", $(LN_S) and
	AC_PROG_LN_S instead of "ln -s". Changed icu feature detection and
	linking to rely on C++ linker instead of C linker. Fixed module
	compilation checks on FreeBSD. Fixed $(DESTDIR) handling (patch by Mike
	Auty). Refactored lib/strUtil into lib/misc. Changed hgfsmounter
	install hook to symlink mount.vmhgfs. Renamed libghIntegrationStub to
	libGhIntegrationStub. Fixed compilation of lib/guestApp when using
	--without-x (reported by Martin Preishuber). Renamed libunityStub to
	libUnityStub. Fix build on FreeBSD by using ":=" instead of "=" when
	exporting module directories. The vmware-user desktop link now executes
	vmware-user-suid-wrapper. Properly install vmware-user-suid-wrapper.

	* */foundryToolsDaemon.c, lib/vixTools/vixTools.c: Use a larger result
	packet when handling impersonated HGFS requests (since HGFSv3 uses
	larger packets).

	* guestd/main.c: Moved foreign VM check.

	* guestd/toolsDaemon.*: Added plumbing for HGFS usability library
	calls.

	* hgfsmounter/hgfsmounter.c: Added support for passing options to
	the MacOS HGFS driver.

	* lib/appUtil/*, lib/include/appUtil.h: New library for Unity support.

	* lib/auth/authPosix.c: Don't try using PAM from the Tools.
	
	* lib/dnd/dndCommon.c, lib/dnd/dndLinux.c, lib/file/file.c,
	lib/file/fileIOPosix.c, lib/file/filePosix.c, lib/include/dnd.h,
	lib/include/loglevel_user.h, lib/include/panic.h, lib/include/posix.h,
	lib/include/strutil.h, lib/unicode/unicodeBase.h,
	lib/include/unicodeOperations.h, lib/include/vix.h,
	lib/include/vm_app.h, lib/include/vm_assert.h,
	lib/include/vm_product.h, lib/include/x86cpuid.h, lib/misc/codeset.c,
	lib/misc/hashTable.c, lib/misc/strutil.c, lib/misc/timeutil.c,
	lib/panic/panic.c, lib/string/bsd_vsnprintf.c, lib/strUtil/*,
	lib/unicode/unicodeCommon.c, lib/unicode/unicodeSimpleBase.c,
	lib/unicode/unicodeStatic.c, lib/user/hostinfoPosix.c,
	lib/user/util.c, lib/user/utilPosix.c: Changes from work unrelated to
	open-vm-tools on the internal trunk.

	* lib/backdoor/backdoorInt.h, lib/deployPkg/runDeployPkgInt.h,
	lib/dnd/dndInt.h, lib/file/fileInt.h, lib/guestInfo/guestInfoInt.h,
	lib/hgfs/cpNameInt.h, lib/hgfsServer/hgfsServerInt.h,
	lib/impersonate/impersonateInt.h, lib/include/backdoorInt.h,
	lib/include/bsd_output_int.h, lib/include/cpNameInt.h,
	lib/include/dndInt.h, lib/include/fileInt.h,
	lib/include/guestInfoInt.h, lib/hgfsServer/hgfsServerInt.h,
	lib/include/impersonateInt.h, lib/include/runDeployPkgInt.h,
	lib/include/toolsLoggerInt.h, lib/include/unicodeInt.h,
	lib/string/bsd_output_int.h, lib/toolsLogger/toolsLoggerInt.h,
	lib/unicode/unicodeInt.h: Moved some internal header files out of
	the general include directory and into the appropriate libraries.

	* lib/ghIntegration/*: New library for Unity support.

	* lib/guestApp/guestAppPosixX11.c: Reset the value of LD_LIBRARY_PATH
	before running the web browser.

	* lib/guestInfo/guestInfoPosix.c, lib/include/guest_os.h: Fixed a typo
	in Mandriva guest detection. Added Asianux.

	* lib/guestInfo/guestInfoServer.c: Fixed behavior for sending nicInfo
	updates to the host (patch by Jason Lunz).

	* lib/guestRpc/ghi*.*: New xdr protocol for Unity support.

	* lib/guestRpc/nicinfo.x: Correctly applied LGPL to file.

	* lib/hgfs/cpNameLinux.c: Allow building for versions of Solaris newer
	than 10.

	* lib/hgfsServer/hgfsServer.c,
	lib/hgfsServerPolicyGuest/hgfsServerPolicyGuest.c,
	lib/include/hgfsServerPolicy.h: Provide an override setting for
	disabling case conversion during file lookups.

	* lib/hgfsServer/hgfsServerLinux.c: Only perform case insensitive
	file lookups if a case sensitive lookup fails.

	* lib/image/imageUtilPng.c, lib/include/imageUtil.h,
	lib/include/imageUtilTypes.h: New library for
	Unity support.

	* lib/include/conf.h, toolbox/toolbox-gtk.c: Robustified the help page
	discovery mechanism.

	* lib/include/dndGuest.h: Allow inclusion of header into source files
	without GTK2 support.

	* lib/unity/*, lib/include/guestCaps.h, lib/include/unityCommon.h: New
	library for Unity support.

	* lib/include/hgfsUtil.h: Fixed a precedence issue in a macro.

	* lib/raster/*, lib/include/rasterConv.h: New library for Unity support.

	* lib/region/*, lib/include/region.h: New library for Unity support.

	* lib/include/system.h: Added new OS type for WinXP 64-bit, reformatted
	enums.

	* lib/unityWindowTracker/*, lib/include/unityWindowTracker.h: New
	library for Unity support.

	* lib/include/vm_version.h: Bumped TOOLS_VERSION.

	* lib/wiper/wiperPosix.c: Replaced BSD_VERSION with __FreeBSD_version.

	* modules/*/*/*: Reflected changes from elsewhere.

	* modules/freebsd/vmhgfs/*: Reflected changes from MacOS HGFS work, and
	fixed file permissions so that they're not all owned by root.

	* modules/linux/vmblock/linux/dentry.c: Changed d_revalidate to
	properly invalidate negative dentries.

	* modules/linux/vmci/*: Landed the Virtual Machine Communication
	Interface guest module.

	* modules/linux/vmmemctl/os.c: Fixed vmmemctl to build on 2.6.26
	(reported by Pavol Rusnak).

	* modules/linux/vmsync/sync.c: Fixed vmsync to build on 2.6.26
	(reported by Pavol Rusnak).

	* modules/linux/vsock/*: Landed the VMCI sockets interface module.

	* modules/linux/vmxnet/vmxnet.c, modules/linux/vmxnet/vmxnet2_def.h,
	modules/linux/vmxnet/vmxnetInt.h: Increased rx ring size for enhanced
	vmxnet2.

	* toolbox/*: Refactored pieces of GTK Toolbox and landed the command
	line Toolbox. Fixed mnemonic collisions in the GTK Toolbox.

	* vmware-user/copyPaste.c: Fixed several bugs with file copy paste
	behavior.

	* vmware-user/notify.c, vmware-user/vmware-user.c,
	vmware-user/vmwareuserInt.h: Added stubs for notification framework.

	* vmware-user/pointer.c: Reverted fix for bug with clipboard retry
	behavior.

	* vmware-user/vmware-user.c: Fixed build with gtk 1.2 (reported by
	Stephen Duncan). Added signal handlers for SIGUSR1/SIGUSR2 used by
	VMware Tools installer to reload vmware-user cleanly during a Tools
	upgrader. Reload vmware-user on a fatal X I/O error. Don't panic if
	run outside of a VM. Don't leave Unity mode on a Tools reset.
	
2008-07-01  Adar Dembo  <adar@vmware.com>
	* Resync with internal trunk (2008.06.30)

	* configure.ac, lib/guestApp/*, toolbox/Makefile.am,
	vmware-user/Makefile.am: Split lib/guestApp into two libraries, one
	with X11 functionality, and one without. Improved detection of
	gnome-open.

	* guestd/*, lib/netUtil/netUtilLinux.c: guestd now compiles for MacOS
	guests.
	
	* guestd/main.c, lib/include/system.h, lib/system/systemLinux.c:
	Refactored GuestdWritePidfile into System_Daemon.

	* guestd/toolsDaemon.c: Fixed a backwards time synchronization issue.
	Thanks to Eric Castan for reporting the bug.

	* lib/conf/conf.c, lib/include/conf.h: Removed obsolete configuration
	keys and values.

	* lib/file/*, lib/dict/*, lib/foundryMsg/*, lib/include/backdoor_def.h,
	lib/include/codeset.h, lib/include/config.h,
	lib/include/file_extensions.h, lib/include/fileInt.h,
	lib/include/loglevel_user.h, lib/include/msg.h, lib/include/msgid.h,
	lib/include/posix.h, lib/include/preference.h, lib/include/unity.h,
	lib/include/vixCommands.h, lib/include/vix.h,
	lib/include/vmbackup_def.h, lib/include/vmBackup.h,
	lib/include/vm_basic_defs.h, lib/include/vm_basic_types.h,
	lib/include/vm_product.h, lib/include/win32util.h,
	lib/include/x86cpuid.h, lib/misc/codeset.c, lib/misc/codesetOld.c,
	lib/misc/codesetOld.h, lib/misc/posixPosix.c, lib/strUtil/strutil.c,
	lib/user/hostinfoPosix.c, lib/user/util.c,
	lib/vmBackupLib/stateMachine.c, modules/*/vmxnet/net.h: Changes from
	work unrelated to open-vm-tools on the internal trunk.

	* lib/guestRpc/Makefile.am: Added comment about misuse of CFLAGS.

	* lib/hgfsServer/hgfsServer.c: Corrected pointer arithmetic so that
	new node and search allocation works consistently in 64-bit apps.

	* lib/hgfsServer/hgfsServerLinux.c, lib/include/hgfsProto.h: Added
	HGFS_ATTR_HIDDEN_FORCED and set it when returning hidden files.
	
	* lib/hgfsServer/*, lib/hgfsServerPolicy/hgfsServerPolicyGuest.c,
	lib/include/hgfsServerInt.h, lib/include/hgfsServerPolicy.h:
	Refactored and cleaned up some code.

	* lib/include/resolution.h, lib/resolution/*,
	vmware-user/vmware-user.c: Refactored some functions.

	* lib/include/vm_legal.h: Added another patent to the patent string.

	* lib/include/vm_tools_version.h: Added a pair of Tools version macros.

	* lib/include/vm_version.h: Bumped Tools product version.

	* lib/Makefile.am: Included a fix for compilation --without-x. Thanks to
	Mark Foster for reporting the issue.

	* lib/misc/Makefile.am, lib/misc/shared/Makefile.am: Realphabetized
	some sources and added missing source files.

	* lib/misc/posixWin32.c: Removed unneeded file from tree.

	* lib/procMgr/procMgrPosix.c: Made safe for -fPIC and for MacOS.

	* modules/*/*/*: Reflected changes from elsewhere.

	* modules/freebsd/vmhgfs/*: Added some code to handle codeset
	conversions between UTF-8 precomposed and decomposed strings.

	* modules/linux/vmhgfs/*: Refactored string escaping/unescaping code.

	* toolbox/*: Added mnemonics for some buttons.

	* vmware-user/pointer.c: Fixed bug in clipboard retry behavior.

	* vmware-user/vmware-user.c: Added handlers for SIGUSR1 and SIGUSR2
	to facilitate smooth vmware-user upgrades with respect to the vmblock
	kernel module.
	
2008-06-20  Elliot Lee  <elliot@vmware.com>

	* Resync with internal trunk (2008.06.13)

	* FreeBSD 7/8 fixes from Martin Blapp.

	* Fix getpwnam_r etc. on FreeBSD & Solaris.

	* configure.ac: Add --without-kernel-modules,
	--with-linux-release, and --with-linuxdir (gissa).

	* configure.ac, lib/guestRpc/*, lib/guestInfo/guestInfo.c,
	lib/guestInfo/guestInfoServer.c, lib/dynxdr/*,
	{vmware-user,guestd}/foreignVMToolsNetworking.c, guestd/Makefile.am,
	{vmware-user,guestd}/foundryToolsDaemon.c, lib/include/dynxdr.h,
	lib/include/guestInfo.h, lib/include/vmxrpc.h,
	lib/include/xdrutil.h, lib/Makefile.am, lib/netUtil/*,
	lib/vixTools/Makefile.am: Add support for XDR encoding of RPC
	values, including the NicInfoV2 structure.

	* guestd/stub.c, hgfsclient/Makefile.am, hgfsclient/stub.c,
	hgfsclient/stub-user-util.c, hgfsmounter/Makefile.am,
	hgfsmounter/stub.c, lib/stubs/*, libguestlib/Makefile.am,
	libguestlib/stubs.c, toolbox/Makefile.am, toolbox/stub.c,
	vmware-user/stub.c: Centralize stubs.

	* lib/guestInfo/guestInfoPerfMonLinux.c: Convert ioInRate and
	ioOutRate to be in terms of KB instead of pages.

	* lib/hgfsBd/hgfsBd.c, lib/hgfsServer/hgfsServer.c,
	lib/hgfsServer/hgfsServerLinux.c: Large packet support, and
	additional case-insensitivity fixes.

	* lib/include/hgfsBd.h, lib/include/hgfs.h,
	lib/include/hgfsProto.h: Add HGFS error code.
	
	* lib/hgfs/hgfsUtil.c, lib/guestInfo/Makefile.am,
	lib/guestInfo/guestInfoPosix.c, lib/guestApp/guestApp.c,
	lib/foundryMsg/foundryMsg.c, lib/file/fileLockPrimitive.c,
	lib/file/fileIOPosix.c, lib/file/fileLockPosix.c,
	guestd/toolsDaemon.c, guestd/debugStdio.c, guestd/main.c,
	lib/hgfsServerManagerGuest/hgfsServerManagerGuest.c,
	lib/include/codeset.h, lib/include/cpuid_info.h,
	lib/include/dnd.h, lib/include/file_extensions.h,
	lib/include/fileInt.h, lib/include/ghIntegration.h,
	lib/include/guestApp.h, lib/include/guestStats.h,
	lib/include/hgfsServerInt.h, lib/include/hgfsUtil.h,
	lib/include/hostinfo.h, lib/include/loglevel_user.h,
	lib/include/netutil.h, lib/include/panic.h, lib/include/posix.h,
	lib/include/unicode*.h, lib/include/util.h, lib/include/vix.h,
	lib/include/vixTools.h, lib/include/vm_app.h,
	lib/include/vm_basic_defs.h, lib/include/vm_product.h,
	lib/include/vm_tools_version.h, lib/include/vm_version.h,
	lib/include/x86cpuid.h, lib/misc/codeset.c, lib/misc/codesetOld.c,
	lib/misc/codesetOld.h, lib/misc/hashTable.c, lib/misc/hostname.c,
	lib/misc/timeutil.c, lib/panic/panic.c, lib/string/str.c,
	lib/sync/syncMutex.c, lib/system/systemLinux.c, lib/unicode/*.c,
	lib/unityStub/*, lib/user/hostinfo.c, lib/user/hostinfoPosix.c,
	lib/vixTools/*, modules/linux/vmxnet/*, toolbox/debugStdio.c,
	vmware-user/debugStdio.c, vmware-user/dnd.c, vmware-user/main.c:
	Bug fixes.

	* modules/linux/vmxnet/*: Remove unused BPF code. Add ethtool
	callbacks to get & set driver settings.

	* lib/user/util.c: Add function for getting backtraces.

	* lib/resolution/*, vmware-user/*, lib/Makefile.am, configure.ac:
	Move resolution-changing code into separate library.

	* guestd/main.c, lib/include/tools.h: Allow disabling tools
	version reporting to the host, via config file.

	* lib/rpcIn/*, lib/include/rpcin.h, guestd/toolsDaemon.c,
	toolbox/toolbox-gtk.c: Updated RPC API

	* lib/include/dndGuest.h: Helper API for DnD code

	* modules/freebsd/vmhgfs/*, modules/freebsd/vmmemctl/*,
	modules/freebsd/vmblock/*, modules/linux/vmhgfs/*,
	modules/linux/vmmemctl/*: Reflect changes from main source tree.

	* vmware-user/copyPaste.c: Copy/paste cleanup.

	* vmware-user/vmware-user.c: Updated locking code to use X11
	display instead of lockfiles.

2008-06-03  Adar Dembo  <adar@vmware.com>
	* Resync with internal trunk (2008.05.28).

	* configure.ac, Makefile.am, */Makefile.am: Added rudimentary
	`make install` support. Fixes Sourceforge bug 1839981.

	* configure.ac, Makefile.am, vmware-user-suid-wrapper/*: Added
	vmware-user-suid-wrapper to help autostart vmware-user. Added some
	informational tags to AC_DEFINE macros.

	* */debugStdio.c: Fixed a format string vulnerability
	in Debug. Allocate fd on the stack in DebugToFile.

	* lib/auth/authPosix.c, lib/dnd/dndCommon.c, lib/dnd/dndLinux.c
	lib/impersonate/impersonate.c: Add inclusion of vmware.h and refactor
	some include statements.

	* lib/file/file.c, lib/include/file.h: Added File_UnlinkNoFollow
	function.

	* lib/file/fileIO.c, lib/file/fileLockPrimitive.c,
	lib/include/fileIO.h: Added error case for ENAMETOOLONG to FileIO_Lock.
	Constified 'buf' in FileIO_Pwrite.

	* lib/file/fileIOPosix.c: Removed coalescing and decoalescing code.
	Consolidated some Unicode calls.

	* lib/file/filePosix.c: Reworked some error handling logic.

	* lib/foundryMsg/foundryMsg.c: Refactored buffer encoding and decoding
	logic into a single pair of functions.

	* lib/foundryMsg/foundryThreads.c, lib/include/foundryThreads.h
	lib/include/util.h, lib/misc/util_misc.c: Changed generic thread type
	from uintptr_t to Util_ThreadID.

	* lib/hgfsServer/*, lib/hgfs/hgfsProto.h, modules/linux/vmhgfs/*:
	Additional HGFSv3 fixes and refactoring.

	* lib/include/dbllnklst.h, lib/misc/dbllnklst.c: Constified argument to
	DblLnkLst_IsLinked.

	* lib/include/dnd.h: Added support for DnD of RTF.

	* lib/include/fileInt.h: Removed prototype of FileLockFileSize.

	* lib/include/hashTable.h, lib/misc/hashTable.c: Cosmetic changes.
	Added HashTable_ReplaceIfEqual.

	* lib/include/loglevel_user.h: Added hpet loglevel.

	* lib/include/msg.h: Removed prototype of MsgSetPostStderrBlock.

	* lib/include/posix.h: Removed certain includeCheck allowances.

	* lib/include/productState.h: Added VDM client product.

	* lib/include/unicode*, lib/unicode/*: Ongoing i18n work.

	* lib/include/vixCommands.h: Added command to set snapshot information.

	* lib/include/vix.h: Added more errors and a new flag.

	* lib/include/vixOpenSource.h: Reworked asserts and added VIX_ASSERT.

	* lib/include/vm_app.h: Added Tools tray app.

	* lib/include/vm_product.h: Reworked VMRC product definitions and added
	VDM client product definitions.

	* lib/include/vm_tools_version.h: Added WS65 Tools version.

	* lib/include/vm_version.h: Bumped Tools version. Added logic for VMRC
	product.

	* lib/include/x86cpuid.h: Modified a flag and trimmed an unneeded macro.

	* lib/misc/codesetOld.c: Implement UTF-16 codest conversion to UTF-8
	for CURRENT_IS_UTF8.

	* lib/misc/dynbuf.c: Modified dynbuf growing behavior.

	* lib/misc/posixDlopen.c, lib/misc/posixInt.h, lib/misc/posixPosix.h:
	Refactored codeset conversion code into PosixConvertToCurrent.

	* lib/misc/posixWin32.c: Added some path checks.

	* lib/misc/timeutil.c: Win32-wrappified TimeUtil_GetTimeFormat.

	* lib/misc/vmstdio.c: Reduce virtual memory usage and add '\r' as a line
	ending in StdIO_ReadNextLine.

	* lib/rpcout/rpcout.c: Added comments.

	* lib/str/str.c: Cosmetic changes.

	* lib/vixTools/vixTools.c: Added unlink(2) logic to avoid deleting
	symlink targets. Cosmetic changes.

	* modules/*/*/*: Reflect changes from elsewhere in the source tree.

	* modules/linux/vmhgfs/super.c: Fix vmhgfs to properly report the
	available space on the host (Sourceforge bug 1924246).

	* vmware-user/vmware-user.c: Add advisory locking code to help maintain
	only one vmware-user instance per X session.

	* xferlogs/xferlogs.c: Fix a formatted string vulnerability.

2008-05-12  Elliot Lee  <elliot@vmware.com>

	* Resync with internal trunk (2008.05.08).

	* configure.ac, **/Makefile.am: Use CPPFLAGS instead of
	CFLAGS to eliminate warning about proc/sysinfo.h.

	* guestd/foreignVMToolsNetworking.c,
	vmware-user/foreignVMToolsNetworking.c
	lib/hgfsServer/hgfsServerLinux.c, lib/include/hgfsServerInt.h,
	modules/linux/vmhgfs/bdhandler.c, modules/linux/vmhgfs/dir.c,
	modules/linux/vmhgfs/file.c, modules/linux/vmhgfs/filesystem.h,
	modules/linux/vmhgfs/fsutil.h, modules/linux/vmhgfs/inode.c,
	modules/linux/vmhgfs/link.c, modules/linux/vmhgfs/module.h,
	modules/linux/vmhgfs/page.c, modules/linux/vmhgfs/request.c,
	modules/linux/vmhgfs/request.h: Whitespace cleanups.

	* guestd/main.c: Removed "blessed app" code for starting
	vmware-user. Hooray!

	* lib/deployPkg/deployPkg.c: Remove unneeded Utf8 conversion for
	Windows.

	* lib/file/filePosix.c: Use new Posix_RealPath implementation.

	* lib/guestApp/guestApp.c, lib/include/guestApp.h: Remove/cleanup
	UTF-8 related RPC functions.

	* lib/guestInfo/guestInfoPerfMonLinux.c,
	lib/guestInfo/guestInfoPosix.c, lib/include/guestInfo.h,
	lib/include/guestInfoInt.h, lib/include/guestStats.h: Rename
	structures to GuestMemInfo, GuestNicInfo, and GuestDiskInfo.

	* lib/guestInfo/guestInfoServer.c, lib/include/guest_msg_def.h: As
	above, and also GUESTMSG_MAX_IN_SIZE moved to guest_msg_def.h, and
	misc locking updates. Also add GuestInfoServer_Main(), and cleanup
	whitespace.

	* lib/hgfsServer/hgfsServer.c: Cleanup UTF-8 handling.

	* lib/include/codeset.h: Update defines that indicate whether the
	current platform is using UTF-8.

	* lib/include/dnd.h: Add prototypes for a couple of string
	conversion functions.

	* lib/include/file_extensions.h: Add OVF and Archived OVF file extensions.

	* lib/include/file.h: C++ guard thingies. Update a couple of
	function prototypes to work on file descriptors instead of
	filenames.

	* lib/include/hashTable.h, lib/include/guest_os.h,
	lib/include/loglevel_defs.h, lib/include/stats_user_defs.h,
	lib/include/stats_user_setup.h, lib/include/str.h,
	lib/include/unicodeTypes.h, lib/include/util.h: Allow inclusion in
	kernel modules...

	* lib/include/loglevel_user.h: As above, and add a couple of
	loglevel variables.

	* lib/include/util.h, lib/misc/util_misc.c: Allow inclusion in
	kernel modules as above, and add some utility functions on Windows
	for manipulating canonical paths.

	* lib/include/hgfsProto.h, lib/include/hgfsUtil.h: Move
	request/reply payload macros to hgfsProto.h.

	* lib/include/hgfsServerPolicy.h: Add ShareList management
	prototypes and structure members.

	* lib/include/msg.h: Add function prototypes for creating and
	posting lists of messages.

	* lib/include/system.h: Add types & functions related to desktop
	switch monitoring on Windows.

	* lib/include/unicodeOperations.h: Add/update inline unicode operations.

	* lib/include/vixCommands.h: Add VIX requests and events.

	* lib/include/vmbackup_def.h, lib/vmBackupLib/stateMachine.c: Move
	backup status enum to public header.

	* lib/include/vm_basic_asm_x86_64.h: Div643232 now also works on MSC.

	* lib/include/vm_basic_defs.h: Add debug output macros for Windows drivers.

	* lib/include/vm_basic_types.h: Update the FMTPD macro, add
	SCANF_DECL macro for arg checking on scanf-like functions.

	* lib/include/x86cpuid.h: Defines for AMD L2/L3 cache separately, and CPUID for Nehalem.

	* lib/misc/codesetOld.c: Bug fixes and general unicode handling updates. 

	* lib/system/systemLinux.c: Use Posix_Getenv/Posix_Setenv impls.

	* lib/vixTools/vixTools.c, lib/vmBackupLib/scriptOps.c: Bug fixes.

	* modules/freebsd/*, modules/linux/*: Updates to correspond to
	updates of files in main tree.

	* modules/freebsd/vmhgfs/hgfs_kernel.h: Bug fixes.

	* modules/freebsd/vmxnet/vm_device_version: Add
	SCSI_IDE_HOSTED_CHANNEL define, update SCSI_MAX_CHANNELS.

	* modules/freebsd/vmxnet/vmnet.def: Add capabilities for IPv6
	checksumming and TSO, and large packet TSO.

	* lib/include/vmblock.h, modules/linux/vmblock/linux/control.c,
	modules/linux/vmblock/linux/vmblockInt.h: Use a macro to better
	abstract the vmblock mount point & device.

	* vmware-user/vmware-user.c: Add SIGPIPE to the list of signals
	that vmware-user handles.
	
2008-05-02  Adar Dembo  <adar@vmware.com>

	* Resync with internal trunk (2008.04.19).

	* configure.ac, guestd/Makefile.am, hgfsclient/Makefile.am,
	lib/misc/*/Makefile.am, lib/string/*/Makefile.am, toolbox/Makefile.am,
	vmware-user/Makefile.am, xferlogs/Makefile.am: Added libicu support for
	codeset conversions. This includes some makefile logic as well as
	autoconf arguments for controlling libicu behavior at compile-time.

	* */foreignVMToolsNetworking.c, lib/vixTools/vixTools.c: Unicode fixes.

	* */foundryToolsDaemon.c, lib/foundryMsg/vixTranslateErrOpenSource.c,
	lib/panic/panic.c, lib/printer/printer.c: Added calls to Win32 Unicode
	wrappers.

	* guestd/main.c: Cleaned up guestInfo server when guestd shuts down.

	* guestd/toolsDaemon.c, vmware-user/resolution.c: Disabled multi-mon
	advertisement for Win2k.

	* lib/auth/authPosix.c, lib/dnd/dndLinux.c, lib/file/*,
	lib/impersonate/impersonatePosix.c, lib/include/mntinfo.h,
	lib/sync/syncWaitQPosix.c, lib/user/hostinfoPosix.c, lib/user/util.c,
	lib/user/utilPosix.c, lib/wiper/wiperPosix.c: Added calls to POSIX
	Unicode wrappers. 

	* lib/file/*: Replaced calls to string functions with calls to
	the "safe" family of string functions.

	* lib/dict/dictll.c, lib/include/dictll.h: Detect and tolerate UTF-8
	dictionary files that contain the UTF-8 BOM.

	* lib/err/*, lib/include/err.h, lib/include/msgfmt.h,
	lib/include/msg.h: Added support for localization of error strings.

	* lib/foundryMsg/foundryThreads.c, lib/include/foundryThreads.h,
	lib/misc/util_misc.c: Added opaque type for threads/process IDs.

	* lib/guestInfo/guestInfoServer.c: Removed separate thread context.

	* lib/hgfsServer/*, lib/include/hgfs*.h: Additional HGFSv3 cleanup.

	* lib/hgfsServer/hgfsServerLinux.c: Added calls to POSIX Unicode
	wrappers. Fixed some alias detection code for MacOS.

	* lib/include/backdoor_def.h: Added backdoor call for debugging events.

	* lib/include/bsdfmt.h, lib/string/bsd_vsnprintf.c,
	lib/string/bsd_vsnprintfw.c: Replaced BSDFmt_WCSonv with
	BSDFmt_WChartoUTF8.

	* lib/include/codeset.h, lib/include/codesetOld.h, lib/misc/codeset.c,
	lib/misc/codesetOld.c, lib/string/convertutf.h: Implemented
	libicu-backed codeset layer. When building without libicu, fallback on
	codesetOld.

	* lib/include/guestApp.h: Added wide versions of dictionary functions.

	* lib/include/loglevel_user.h: Added two new loglevels.

	* lib/include/posix.h, lib/misc/posixPosix.c: Added new POSIX wrappers.

	* lib/include/str.h: Clarified the use of some functions.

	* lib/include/syncMutex.h, lib/include/syncWaitQ.h: Removed unneeded
	macros.

	* lib/include/unicode*.h, lib/unicode/*: Ongoing Unicode work.

	* lib/include/util.h: Added Util_FreeStringList, removed Util_FreeList.

	* lib/include/uuid.h: Added new UUID creation scheme.

	* lib/include/vix*.h: Tweaked some VIX commands, errors, and properties.

	* lib/include/vmBackup.h, lib/vmBackupLib/scriptOps.c,
	lib/vmBackupLib/stateMachine.c: Moved disabled targets logic from
	library to Windows VSS provider.

	* lib/include/vm_basic_asm_x86*.h: Allow emitted FX functions to
	modify main memory as a side effect.

	* lib/include/vm_tools_version.h: Bump Tools version.

	* lib/include/vm_version.h: Added several product versions.

	* modules/linux/vmhgfs/*: Additional cleanup for HGFSv3. Use new kthread
	wrapper when possible. Bump module version.

	* modules/linux/vmmemctl/*: Use new kthread wrapper when possible.
	Remove dead delayed work code. Bump module version.

	* modules/linux/*/compat_kthread.c: Added kthread wrapper implementation
	for modules that use kernel threads.

	* modules/*/*/*: Reflect header file changes from elsewhere in the
	source code tree.

	* vmware-user/copyPaste.c, vmware-user/pointer.c,
	vmware-user/vmwareuserInt.h: Stop wastefully polling for pointer
	updates if the VMX is new enough.

	* xferlogs/xferlogs.c: Fixed a warning in the call to fwrite.
	(Thanks to Denis Leroy for reporting this bug.)
	
	
2008-04-14  Elliot Lee  <elliot@vmware.com>

	* Resync with internal trunk (2008.04.01).

	* Fixed legal header on all LGPL-licensed files.

	* vmware-user/resolution.c: Normalize the display topology that
	comes in from the host, and report 'global_offset' capability.

	* toolbox/Makefile.am, vmware-user/Makefile.am,
	lib/misc/Makefile.am, lib/misc/atomic.c, lib/Makefile.am,
	lib/atomic/*, hgfsclient/Makefile.am: Move libAtomic stuff into libmisc

	* vmware-user/foundryToolsDaemon.c, lib/vixTools/vixTools.c,
	lib/include/hgfsServerInt.h, guestd/toolsDaemon.c,
	guestd/foundryToolsDaemon.c: Remove WIN9XCOMPAT, and some
	SOCKET_MGR code.

	* vmware-user/copyPaste.c: Copy/paste fixes for cross-platform
	operation.

	* modules/linux/vmxnet/vmnet_def.h: Add SG_SPAN_PAGES capability.

	* modules/linux/vmxnet/vm_device_version.h: Update some device limits.

	* modules/linux/*/compat_sched.h: Add TASK_COMM_LEN define.

	* modules/linux/*/compat_kernel.h,
	modules/linux/*/kernelStubsLinux.c: Add vsnprintf define.

	* modules/linux/*/x86cpuid.h: Add new CPUs.

	* modules/linux/vmhgfs/vmhgfs_version.h: Bump HGFS version.

	* modules/linux/*/vm_basic_asm_x86.h,
	modules/linux/*/vm_basic_asm_x86_64.h,
	lib/include/vm_basic_asm_x86.h, lib/include/vm_basic_asm_x86_64.h:
	Formatting fixes, and change asm directives used.

	* modules/linux/vmhgfs/module.h,
	modules/linux/vmhgfs/filesystem.c,
	modules/linux/vmhgfs/bdhandler.c,
	modules/linux/*/compat_kthread.h: compat_kthread fixes.

	* modules/freebsd/vmxnet/net_compat.h,
	modules/freebsd/vmxnet/if_vxn.c: Updates for FreeBSD 7.0.
	(Thanks to Martin Blapp for contributing to these changes.)

	* lib/misc/util_misc.c, lib/include/loglevel_user.h,
	lib/user/hostinfoPosix.c, lib/misc/hostname.c: Bugfix.

	* lib/unityStub/unityStub.c, lib/include/unity.h: Add stub and enums
	related to DnD support.

	* lib/unicode/unicodeSimpleTypes.c,
	lib/unicode/unicodeSimpleTransforms.c,
	lib/unicode/unicodeSimpleBase.c, lib/unicode/unicodeCommon.c,
	lib/include/unicodeTypes.h,
	lib/include/unicodeTransforms.h,
	lib/include/unicodeBase.h, lib/include/unicodeCommon.h: Add
	additional Unicode-related functions.

	* lib/sync/syncMutex.c, lib/include/syncMutex.h: Add TryLock method.

	* lib/strUtil/strutil.c: Add int64-related functions.

	* lib/string/str.c: Compile fix

	* lib/string/bsd_output_shared.c: Better handling of floating
	point on Windows.

	* lib/include/progMgr.h, lib/procMgr/procMgrPosix.c: Clarify that
	the strings are in UTF-8, do conversion as needed.

	* lib/include/posix.h, lib/misc/posixPosix.c,
	lib/misc/posixWin32.c, lib/file/filePosix.c: Add new Posix_
	function implementations, and unicodify existing ones.

	* lib/misc/hashTable.c, lib/include/hashTable.h: Add lock-less hash
	table functions.

	* lib/misc/util_misc.c, lib/include/w32util.h: Add a couple of
	Win32 utility functions.

	* lib/include/vm_version.h: Add WS5 config version.

	* lib/include/vm_atomic.h: Add typecasts to atomic operations to
	make compilers stop complaining, and expand the AtomicUseFence option.

	* lib/include/vm_app.h: Add a couple of HGFS-related options.

	* lib/include/vix.h: Update a few errors and other macros.

	* lib/include/vixCommands.h, lib/foundry/foundryMsg.c: Change a
	bunch of structure members from int32 to uint32, and add a parsing
	function.

	* lib/include/msgfmt.h, lib/include/msg.h: Additional
	message-handling prototypes.

	* lib/include/guestInfoInt.h, lib/include/guestInfo.h,
	lib/guestInfo/Makefile.am, lib/guestInfo/guestInfoServer.c,
	lib/guestInfo/guestInfoPosix.c,
	lib/guestInfo/guestInfoPerfMonLinux.c: Add IPv6 support, and the
	ability to read mem stats on Linux.

	* lib/include/fileIO.h, lib/file/fileIOPosix.c: Add MacOS function
	related to Time Machine.

	* lib/guestApp/guestApp.c: Use Posix_ variants of functions.

	* lib/ghIntegrationStub/ghIntegrationStub.c: Add GHI capabilities
	stubs.

	* lib/dnd/dndCommon.c, lib/file/file.c: Use new Unicode_Format()
	function, bugfix.

	* guestd/main.c: Fix a security bug.

	* configure.ac: Allow calling libdnet 'dumbnet' for Debian
	systems. Detect libprocps.

2008-03-19  Adar Dembo  <adar@vmware.com>

	* Resync with internal trunk (2008.03.13).

	* vm_version.h: Updated Tools version.

	* configure.ac: Added dynamic dnet detection and --without-dnet flag.

	* guestd/debugStdio.c, lib/include/system.h, lib/system/systemLinux.c:
	Modified debugging to file behavior to prepend debug strings with human
	readable timestamps.

	* guestd/main.c, guestd/toolsDaemon.c, lib/conf/conf.c,
	lib/guestApp/guestApp.c, lib/include/guestApp.h: Internationalized
	GuestApp_GetInstallPath and GuestApp_GetconfPath.

	* lib/auth/authPosix.c, lib/dnd/dndLinux.c, lib/file/*,
	lib/impersonate/impersonatePosix.c, lib/include/fileInt.h,
	lib/include/posix.h, lib/misc/posix*.c: Refactored, extended, and made
	use of the set of POSIX internationalization-safe function wrappers.

	* lib/dnd/dndCommon.c, lib/include/dnd.h, lib/include/dndInt.h,
	vmware-user/copyPaste.c, vmware-user/dnd.c: Replaced some duplicated
	UTF-8 formatting code with calls to lib/unicode.

	* lib/guestInfo/guestInfoPosix.c: Replaced the old syscall-based
	implementation of nicinfo with a simpler implementation that uses dnet.

	* lib/guestInfo/guestInfoServer.c, lib/include/guestInfo.h,
	lib/include/guestInfoInt.h: Added Win32 implementation of
	meminfo. POSIX implementation to follow.

	* lib/hgfsServer/hgfsServerLinux.c: Replaced a direct readlink(3) call
	with a call to the POSIX wrapper for readlink(3). Relax an overeager
	ASSERT in symlink checking when using the special empty share.

	* lib/include/codeset.h, lib/string/bsd_vsnprintf.c, lib/string/str.c,
	lib/unicode/unicodeSimpleOperations.c, lib/unicode/unicodeSimpleUTF16.h:
	Refactored ICU routines from unicodeSimpleUtf16.h to codeset.h, which
	is now licensed under the ICU license (BSD variant).

	* lib/include/file.h, lib/file/file.c: Added function File_StripSlashes.

	* lib/include/hgfsProto.h: Removed an A acute from a comment to allow
	the file to be built on Windows systems where the default language isn't
	English.

	* lib/include/hostinfo.h, lib/include/util.h, lib/user/hostinfoPosix.c,
	lib/user/util.c, lib/user/utilPosix.c: More conversions to
	lib/unicode. Added Util_ZeroFreeStringW function for Windows in util.h.

	* lib/include/msg.h: Removed obsolete NO_MSGFMT macro.

	* lib/include/unicodeBase.h, lib/unicode/unicodeCommon.c,
	lib/unicode/unicodeSimpleBase.c: Added some more encoding functions.

	* lib/include/vixCommands.h, lib/include/vixOpenSource.h: Added another
	user credential type, some command flags, some command layouts, some
	error codes, some properties, and tweaked existing commands.

	* lib/include/vixTools.h: Added VixToolsUserIsMemberOfAdministratorGroup
	function.

	* lib/include/vm_assert.h, lib/include/vm_basic_defs.h: Move IMPLIES to
	vm_basic_defs.h. Removed some vprobes definitions.

	* lib/include/vmBackup.h, lib/vmBackupLib/scriptOps.c,
	lib/vmBackupLib/stateMachine.c: Added infrastructure to disable
	quiescing targets from a config file.

	* lib/include/vm_basic_asm.h: Changed __GET_CPUID2 handling for Windows.

	* lib/include/vm_produt.h: Added VDM product.

	* lib/include/vm_tools_version.h: Bumped internal Tools version.

	* lib/include/win32util.h, lib/misc/hostname.c, lib/misc/util_misc:
	Refactored functions to separate set of Win32 wrappers (next to the
	POSIX wrappers mentioned earlier).

	* lib/misc/codeset.c: Made CodeSetGetCurrentCodeSet non-static.

	* lib/misc/*/Makefile.am: Added POSIX wrappers to build system.

	* lib/strUtil/strutil.c: Fixed bug in StrUtil_EndsWith function.

	* lib/include/unicodeTypes.h, lib/unicode/unicodeSimpleTypes.c: Removed
	ISO-8859-11 encoding. Added cross-reference of IANA character set
	names, windows code pages, and ICU encodings.

	* lib/vixTools/vixTools.c: Impersonation tweaks.

	* modules/*/*/*: Reflect header file changes from elsewhere in the
	source code tree.

2008-03-11  Adar Dembo  <adar@vmware.com>

	* vm_version.h: Updated Tools version.

	* modules/vmblock/linux/*: Make vmblock build under 2.6.25-rc2.
	The dentry and mount objects have been moved out of struct
	nameidata and into the new struct path. Also, path_release() is
	now path_put().

	* modules/vmsync/linux/*: Make vmsync build under 2.6.25-rc2.
	The same changes were needed here as in vmblock above.

2008-03-10  Adar Dembo  <adar@vmware.com>

	* vm_version.h: Updated Tools version.

	* modules/vmhgfs/linux/*: Make vmhgfs build under 2.6.25-rc1.
	The iget() function has been removed and filesystems are now
	expected to implement it themselves using iget_locked().

2008-02-27  Elliot Lee  <elliot@vmware.com>

	* configure.ac, guestd/Makefile.am, toolbox/Makefile.am,
	vmware-user/Makefile.am: Allow passing custom LDFLAGS in to
	build process (patch by Mike Auty).

	* Resync with internal trunk (2008.02.27).

	* guestd/foundryToolsDaemon.c, lib/vixTools/vixTools.c,
	vmware-user/foundryToolsDaemon.c: Win9x compat changes.

	* guestd/toolsDaemon.c: Style fixes.

	* hgfsmounter/hgfsmounter.c: Bug fixes.

	* lib/dnd/dndLinux.c, lib/dnd/dndCommon.c: Move some code to the
	platform-independant file, some DnDv3 support.

	* lib/include/dnd.h, lib/include/dndInt.h: DnDv3 support.

	* lib/file/file.c, lib/file/fileIO.c, lib/file/fileIOPosix.c,
	lib/file/fileLockPrimitive.c, lib/file/filePosix.c,
	lib/include/file_extensions.h, lib/include/fileInt.h,
	lib/include/fileIO.h: Move functions around, Unicode fixes, misc
	fixes.

	* lib/foundryMsg/foundryPropertyListCommon.c: Error handling fixes.

	* lib/hgfsServer/*.c, lib/include/hgfs*.h,
	modules/freebsd/vmhgfs/*, modules/linux/vmhgfs/*: HGFS v3 support,
	updates to improve code re-use between the FreeBSD and MacOS X
	ports, and updates to make the Linux port build on 2.6.25-rc1 (but
	not rc2, yet).

	* lib/include/auth.h, lib/include/codeset.h,
	lib/include/hostinfo.h, lib/include/str.h, lib/include/unicode*.h,
	lib/include/vm_basic_types.h, lib/misc/hostname.c,
	lib/unicode/*.c, lib/user/hostinfoPosix.c: Unicode fixes.

	* lib/include/backdoor_def.h: Add a new command for use by the
	BIOS in checking the GuestOS against Darwin.

	* lib/include/dynarray.h, lib/misc/dynarray.c,
	lib/misc/Makefile.am, lib/misc/shared/Makefile.am: Add Dynarray
	implementation.

	* lib/include/bsdfmt.h, lib/include/bsd_output_int.h,
	lib/string/bsd_output_shared.c, lib/string/bs_vsnprintf.c,
	lib/string/bsd_vsnwprintf.c, lib/string/str.c: Rework built-in
	printf implementation, esp. for Unicode fixes.

	* lib/include/ghIntegration.h: Shuffle types around.

	* lib/include/loglevel_user.h, lib/include/unity.h,
	lib/syncDriver/syncDriverPosix.c, lib/user/util.c,
	toolbox/toolbox-gtk.c: Misc fixes.

	* lib/include/vmBackup.h, lib/vmBackupLib/scriptOps.c,
	lib/vmBackupLib/stateMachine.c, lib/vmBackupLib/vmBackupInt.h:
	Rework scripts for freeze & thaw operations.

	* lib/include/vm_product.h, lib/include/vm_version.h: Add new
	product defs (VMRC).

	* lib/include/vm_tools_version.h: Add ESX 3.5U1 product.

	* lib/include/vixCommands.h, lib/include/vix.h: Add new VIX
	commands and error code.

	* lib/include/win32util.h: Add misc Win32 utilities.

	* modules/*/*/*: Reflect header file changes from elsewhere in the
	source code tree.

2008-02-13  Adar Dembo  <adar@vmware.com>

	* Resync with internal trunk (2008.02.12).

	* configure.ac, lib/unityStub/*, lib/ghIntegrationStub/*,
	lib/Makefile.am, vmware-user/Makefile.am, vmware-user/vmware-user.c:
	Added lib/unityStub and lib/ghIntegrationStub. Unity and guest-host
	integration features for X11 guests are on the way.

	* configure.ac, guestd/Makefile.am, lib/fileUtf8/*,
	lib/vixTools/vixTools.c, vmare-user/Makefile.am: lib/file is now fully
	internationalized. Removed unneeded lib/fileUtf8.

	* foundryToolsDaemon.c: Fixed a leak of the sync driver handle.

	* guestd/toolsDaemon.c: Send guestd's "config directory" to the VMX for
	publishing.

	* hgfsmounter/hgfsmounter.c: Port to MacOS.

	* lib/dnd/*, lib/err/err.c, lib/file/*, lib/include/dnd*,
	lib/include/file*, lib/include/unicode*, lib/include/util.h,
	lib/unicode/*, lib/user/utilPosix.c: More Unicodification.

	* lib/file/file.c, lib/include/file.h: Added File_EnsureDirectory.

	* lib/foundryMsg/foundryMsg.c, lib/guestInfo/guestInfoServer.c,
	lib/misc/codeset.c, lib/misc/vmstdio.c,
	lib/SLPv2Parser/SLPv2MsgAssembler.c, lib/user/util.c: Removed some
	unneeded casts.

	* lib/foundryMsg/foundryThreads.c, lib/include/foundryThreads.h: Added
	FoundryThreads_Free.

	* lib/guestInfo/*, lib/include/guest_os.h, lib/include/guestInfo.h:
	Refactored GetSystemBitness. Removed osNames.h.

	* lib/hgfsServer/hgfsServerLinux.c: Modified MacOS alias resolution code
	so as not to mount volumes. Made HGFS query volume code more resilient
	to failures.

	* lib/include/backdoor_def.h: Added commands for VAssert.

	* lib/include/escape.h, lib/misc/escape.c: Escape_Do is no longer
	declared inline.

	* lib/include/hashTable.h, lib/misc/hashTable.c, lib/misc/Makefile.am,
	lib/misc/shared/Makefile.am: Renamed from hash.[ch].

	* lib/include/iovector.h, lib/include/vm_basic_types.h: Added
	SectorType definition.

	* lib/include/loglevel_user.h: Added additional log levels.

	* lib/include/msgfmt.h: Modified for use in VMKERNEL. Added
	MsgFmt_GetArgswithBuf.

	* lib/include/msg.h: Added Msg_AppendVob for ESX.

	* lib/include/stats_user*: Modified some preprocessing steps. Added
	SETUP_WANT_GETVAL to retrieve named stat counter values.

	* lib/include/str.h: Modified behavior Str_* family of functions for
	Windows.

	* lib/include/strutil.h, lib/strUtil/strutil.c: Removed Split, Grep,
	GrepFd, and GrepFree. Added EndsWith and DecimalStrToUint.

	* lib/include/syncWaitQ.h, lib/sync/*: Modified SyncWaitQ_Add and
	SyncWaitQ_Remove to use PollDevHandle fd types instead of int fd types.

	* lib/include/timeutil.h, lib/misc/timeutil.c: Added
	TimeUtil_GetLocalWindowsTimeZoneIndex and some helper functions.

	* lib/include/util.h, lib/user/utilPosix.c: Added Util_BumpNoFds.

	* lib/include/vixCommands.h: Added commands for device hotplug and
	remote debugging.

	* lib/include/vix.h, lib/include/vixOpenSource.h: Added some new errors
	and properties. Added more VM manipulation functions.

	* lib/include/vm_atomic.h: Comment cleanup and added VMKERNEL-specific
	calls for fencing.

	* lib/include/vm_basic_asm_x86_64.h: Added inline routines to save and
	restore ES1.

	* lib/include/vm_basic_types.h: Added some types and cleaned up a bit.

	* lib/include/vm_legal.h: Updated COPYRIGHT_YEARS.

	* lib/include/vm_product.h: Added hostd service name.

	* lib/include/x86cpuid.h: Cleaned up the file and added some definitions
	for Penryn processors.

	* lib/misc/codeset.c: Added new UTF-16 --> UTF-8 conversion routine.

	* lib/misc/util_misc.c, lib/user/util.c: Moved Util_GetCurrentThreadId
	and friends to util_misc.c.

	* lib/procMgr/procMgrPosix.c: Cleaned up some code and reworked
	asynchronous process execution so as to properly track the grandchild's
	pid instead of the child's pid.

	* lib/string/bsd*: Reorganized BSD formatter.

	* lib/string/str.c: Updated unit tests. Added some Windows corner case
	behavior for Str_Vsnwprintf.

	* lib/strUtil/strutil.c: Fixed some corner cases in existing functions
	that call strtoul.

	* lib/vixTools/vixTools.c: Changed signature of VixToolsImpersonateUser.
	Changed error code handling in a few places.

	* modules/freebsd/vmhgfs/*: Refactored a lot of code so that it can be
	safely reused within the MacOS vmhgfs module.

	* modules/*/*/kernelStubs*: Removed dead System_Uptime function.

	* modules/linux/*/compat_wait.h: Reworked VMW_HAVE_EPOLL macro. Added
	waitqueue helper macros for older kernels.

	* modules/linux/vmhgfs/file.c, modules/linux/vmhgfs/fsutil.*,
	modules/linux/vmhgfs/inode.c: Added HgfsSetUidGid function and used it
	to preserve uid/gid after creating a directory.

	* modules/linux/vmhgfs/vmhgfs_version.h: Bumped driver version.

	* modules/linux/vmsync/compat_workqueue.h: Basic implementation of
	work queues and delayed work queues (using taskqueues and timers) for
	older kernels.

	* modules/linux/vmsync/sync.c: Modified internal state to use new
	compatible work queue implementation.

	* modules/linux/vmxnet/compat_ioport.h,
	modules/linux/vmxnet/compat_netdevice.h,
	modules/linux/vmxnet/compat_pci.h,
	modules/linux/vmxnet/compat_skbuff.h,
	modules/linux/vmxnet/vmxnetInt.h: Added and refactored
	compatibility macros for use in vmxnet3 and vmci sockets modules.

	* modules/linux/vmxnet/vmxnet.c: Hide some kernel functions behind
	compatibility macros.

2008-01-23  Adar Dembo  <adar@vmware.com>

	* Resync with internal trunk (2008.01.08).

	* configure.ac, guestd/Makefile.am, hgfsclient/Makefile.am,
	lib/Makefile.am, toolbox/Makefile.am, vmware-user/Makefile.am:
	integrated lib/unicode for internationalizing strings.

	* guestd/main.c: Stopped using pgrep for finding existing instances
	of guestd. Removed ancient bandwidth test code.

	* guestd/toolsDaemon.c: Moved initial send of the guest's uptime from
	when guestd sends its version to when guestd registers its
	capabilities.

	* lib/file/*, lib/include/file*.h : Massive overhaul of lib/file to
	integrate the new unicode strings that are i18n-safe. Quite a bit of
	cleanup and refactoring as well.

	* lib/file/file.c: Addition of File_PrependToPath function.

	* lib/file/fileIOPosix.c: Addition of FileIO_SetExcludedFromTimeMachine
	and FileIO_PrivilegedPosixOpen functions.

	* lib/fileUTF8/fileUTF8Linux.c, lib/include/fileUTF8.h: Removal of some
	casts and addition of FileUTF8_GetSize function.

	* lib/foundryMsg/foundryMsg.c, lib/misc/vmstdio.c,
	lib/SLPv2Parser/SLPv2MsgAssembler.c: Addition of some casts.

	* lib/foundryMsg/foundryPropertyListCommon.c: Robustified some error
	cases.

	* lib/foundryMsg/vixTranslateErrOpenSource.c,
	lib/include/vixOpenSource.h: Added VIX_E_OUT_OF_MEMORY error code.
	Added Vix_TranslateCOMError function. ADded VIX_DEBUG macro.

	* lib/guestInfo/guestInfoServer.c, lib/include/guestInfo.h: Added some
	casts and refactored some functions. Also fixed a crash that hinders
	backwards compatibility.

	* lib/hgfs/cpNameUtil.c, lib/hgfs/cpNameUtilLinux.c,
	lib/hgfsBd/hgfsBd.c, lib/include/cpName.h, lib/include/cpNameLite.h,
	lib/include/escBitvector.h, lib/include/hgfsUtil.h,
	lib/message/messageBackdoor.c, lib/message/message.c,
	lib/message/messageStub.c, lib/rpcout/rpcout.c,
	modules/freebsd/vmhgfs/kernelStubs.h: Made safe for inclusion in MacOS
	kernel module code.

	* lib/include/backdoor.h: Refactored some type definitions.

	* lib/include/bsd_output_int.h, lib/include/safetime.h,
	lib/string/bsd_output_shared.c: Made safe for Win64 builds.

	* lib/include/dynbuf.h: Added DynBuf_AppendString function.

	* lib/include/err.h: Assorted cleanup.

	* lib/include/escape.h, lib/misc/escape.c: Converted Escape_Do to be
	inline. Some cleanup.

	* lib/include/guest_os.h: Assorted cleanup.

	* lib/include/hash.h, lib/misc/hash.c, lib/misc/Makefile.am,
	lib/misc/shared/Makefile.am: Added basic hash table implementation.

	* lib/include/hostinfo.h, lib/user/hostinfoPosix.c: Refactored and
	added several timekeeping functions.

	* lib/include/localconfig.h, lib/include/util_shared.h: Modified
	statements for include check.

	* lib/include/log.h: Changed the value of some macros when debugging.

	* lib/include/loglevel_defs.h: Refactoed some code, added macros for
	use in the VMM.

	* lib/include/loglevel_user.h: Added loglevels for some new components.

	* lib/include/msgfmt.h: Added new functions.

	* lib/include/msg.h: Added new Msg_LocalizeList function.

	* lib/include/netutil.h: Modified prototypes for two Windows-only
	functions.

	* lib/include/preference.h: Added new Preference_GetInt64 and
	Preference_SetFromString functions.

	* lib/include/strutil.h, lib/strUtil/strutil.c: Cleaned up and added
	some new functions.

	* lib/include/su.h: Cleanup.

	* lib/include/syncMutex.h, lib/sync/syncMutex.c: Added NetWare
	implementation of some synchronization primitives.

	* lib/include/unicode*, lib/unicode/*: New library for handling
	Unicode-aware strings.

	* lib/include/util.h, lib/user/util.c: Assorted refactoring and
	addition of some new functions, one related to backtracing.

	* lib/include/vixCommands.h: New commands for vprobes, replay, message
	dialogs, and others, plus cleanup of some existing commands.

	* lib/include/vm_assert.h: Added IMPLIES macro.

	* lib/include/vm_atomic.h, lib/include/vm_basic_asm.h: Refactored for
	safe Win64 builds.

	* lib/include/vm_basic_defs.h: Added compatibility code for __va_copy.

	* lib/include/vm_basic_types.h: Added FMTH for printing the value of
	handles. Set a new #pragma to ignore size_t truncation warnings on
	Windows. Added several other macros, as well as a ssize_t definition
	for some versions of BSD.

	* lib/include/vm_legal.h: Added more patents to the patent string.

	* lib/include/vm_product.h: Added new macros for some products.

	* lib/include/vm_tools_version.h: Added macros for certain older Tools
	versions and for PRODUCT_BUILD_NUMBER refactoring.

	* lib/include/vm_version.h: Tweaked some product expiration dates and
	versions. Refactored many uses of BUILD_NUMBER to PRODUCT_BUILD_NUMBER.

	* lib/include/x86cpuid.h: Tweaked definition of RDTSCP flag. Refactored
	BIT_MASK macro to VMW_BIT_MASK.

	* lib/misc/base64.c: Modified calling contract for Base64_EasyEncode.

	* lib/misc/codeset.c: Tweaked casts and preprocessor conditioning.

	* lib/misc/idLinux.c: Added IdAuthCreateWithFork and reworked several
	other functions to work around a bug in Apple's detection of GUI
	processes.

	* lib/misc/util_misc.c: Moved W32Util_GetLongPathName and
	W32UTil_LookupSidForAccount elsewhere.

	* lib/rpcin/rpcin.c: Addition of a ping GuestRPC callback.

	* lib/string/str.c: Removed a comment.

	* lib/sync/syncWaitQPosix.c: Added code to disable a workaround for a
	MacOS bug when appropriate (it was fixed in Leopard).

	* lib/vixTools/vixTools.c: Refactored some code, added code to modify
	the guest's networking configuration, added some casts, and added
	code to prevent renaming a file to itself.

	* modules/freebsd/*/Makefile, modules/linux/*/Makefile.normal: Set a
	make variable so the module file will be build in the parent directory.
	Removed some unused rules.

	* modules/freebsd/vmhgfs/kernelStubsBSD.c,
	modules/linux/vmhgfs/kernelStubsLinux.c: Removed unused function.

	* modules/linux/*/include/driver-config.h: Added check to prevent
	uintptr_t from being declared twice.

	* modules/linux/vmblock/linux/filesystem.c,
	modules/linux/vmblock/Makefile.kernel: Added check for newer kernels
	where the slab allocator's constructor function expects three
	arguments. Makes it work with 2.6.25-rc1 (but not rc2, yet).

	* modules/linux/vmblock/linux/vmblock_version.h: Bumped module version.

	* modules/linux/vmhgfs/filesystem.c, modules/linux/vmhgfs/inode.c,
	modules/linux/vmhgfs/module.h, modules/linux/vmhgfs/page.c: Added
	support for writeback caching in conformant kernels.

	* modules/linux/vmhgfs/vmhgfs_version.h: Bumped module version.

	* modules/linux/vmxnet/vmxnetInt.h: Renamed a type and removed the
	inclusion of unnecessary headers. Pruned said headers from codebase.

2007-11-15  Elliot Lee  <elliot@vmware.com>

	* Bandsaw release (2007.11.15).

	* configure.ac: Handle building modules for multiple OS's. Improve
	X detection to allow building --without-x. Improve Gtk+
	detection. Detect libdnet on Solaris. Detect which -Wwarning flags
	the compiler can handle.

	* vmware-user/foreignVMToolsNetworking.c, lib/vixTools/vixTools.c,
	guestd/foreignVMToolsNetworking.c, lib/include/netutil.h,
	lib/include/guestInfo.h, lib/netUtil/netUtilLinux.c,
	lib/include/guestInfoInt.h, lib/guestInfo/guestInfoPosix.c,
	lib/guestInfo/guestInfoServer.c: Move to new NicInfo structures.

	* vmware-user/foundryToolsDaemon.c, guestd/foundryToolsDaemon.c:
	Make sure requestMsg is not NULL before looking inside it.

	* guestd/main.c: Cleanup of HGFS pserver and mounting code. Check
	for some type of signal when sending an RPC.

	* guestd/toolsDaemon.c, vmware-user/resolution.c: Have the guest
	tell the host whether screen resolution changes should be sent,
	instead of having the host guess it based on the OS type set in
	the .vmx file.  Better timeout checking to avoid problems when
	host & guest time diverge.

	* hgfsmounter/hgfsmounter.c: FreeBSD support. Fixes to compile on
	old systems.

	* lib/backdoor/backdoor.c: Tweak for FreeBSD kernel modules.

	* lib/include/mntinfo.h, lib/dnd/dndLinux.c,
	lib/wiper/wiperPosix.c, lib/syncDriver/syncDriverPosix.c: Fixes to
	compile on new systems w/gcc 4.2.

	* lib/err/err.c, lib/err/errPosix.c, lib/err/Makefile.am: Move
	Err_Errno2String function into POSIX-specific source file.

	* lib/file/fileIOPosix.c: Handle EDQUOT if applicable. Fixes to
	compile on new systems where SYS__llseek may not be
	available. Better reporting of errors, by translating errno into
	FILEIO_* error codes.

	* lib/file/fileLockPosix.c: Fixes to compile on old systems. Add a
	bunch of functions to the FileLock* API.

	* lib/file/fileLockPrimitive.c, lib/include/fileInt.h: Bunch of
	file locking cleanups and bug fixes.

	* lib/file/filePosix.c: Bunch of MacOS-related fixes. Add
	File_GetTimes(), FilePosixGetParent(), FilePosixGetBlockDevice(),
	etc.

	* lib/fileUtf8/fileUTF8Linux.c: Add FileUTF8_GetTimes() function.

	* lib/foundry/foundryMsg.c, lib/include/vixCommands.h: Add
	VIX_USER_CREDENTIAL_HOST_CONFIG_HASHED_SECRET credential type, and
	a bunch of VIX commands relating to record-replay.

	* lib/foundryMsg/vixTranslateErrOpenSource.c: Translate a couple
	more error codes.

	* lib/guestInfo/guestInfoPosix.c, lib/guestInfo/Makefile.am: Use
	libdnet on Solaris to retrieve networking info.

	* lib/hgfs/cpNameUtil.c, lib/hgfs/cpNameUtilInt.h,
	lib/hgfs/cpNameUtilLinux.c: Couple more CPName <-> UTF8 conversion
	routines. Some MacOS changes as well.

	* lib/hgfs/hgfsUtil.c, lib/include/hgfs.h,
	modules/linux/vmhgfs/fsutil.c: Handle ENAMETOOLONG.

	* lib/hgfs/staticEscape.c, lib/hgfs/hgfsBd.c: Handle FreeBSD as
	well.

	* lib/hgfsServer/hgfsServer.c: Tie in the cpNameUtil UTF8 changes
	on MacOS.

	* lib/hgfsServer/hgfsServerLinux.c: Make the getdents() wrapper
	work on a wider range of Linux systems. Add "alias" resolution on
	MacOS, and tie in the cpNameUtil UTF8 changes on MacOS.

	* lib/hgfsServer/hgfsServerPolicyGuest.c: Handle FreeBSD.

	* lib/include/backdoor_def.h: Add BDOOR_CMD_LAZYTIMEREMULATION and
	BDOOR_CMD_BIOSBBS.

	* lib/include/str.h, lib/include/bsd_output.h,
	lib/include/bsd_output_int.h: include compat_stdarg.h, change
	vsnwprintf prototype, add HAVE_BSD_WPRINTF define, other compat
	fixups.

	* lib/include/cpNameUtil.h, lib/include/codeset.h,
	lib/misc/codeset.c: Changes to correspond to cpNameUtil UTF8
	changes.

	* lib/include/compat/compat_stdarg.h: New header for doing stdarg
	easily across platforms.

	* lib/include/cpName.h: FreeBSD fixes.

	* lib/include/dnd.h: Add Dnd_SetClipboard and Dnd_GetFileList().

	* lib/include/escBitvector.h: FreeBSD fixes.

	* lib/include/file.h, lib/include/fileUTF8.h: Add new MacOS
	routines and File_GetTimes/FileUTF8_GetTimes.

	* lib/include/hgfsProto.h: Explanation of the whole cpNameUtil and
	codeset UTF8 changes and how they tie in with HGFS.

	* lib/include/hgfsUtil.h: Random compatibility changes.

	* lib/include/loglevel_user.h: Add a few LOGLEVEL_VAR definitions.

	* lib/include/msg.h: s/USE_MSGFMT/NO_MSGFMT/

	* lib/include/osNames.h: Add Windows 2003 Datacenter Edition, and
	user-visible 64bit suffix macro.

	* lib/misc/random.c, lib/include/random.h: Add Random_Quick() and
	Random_QuickSeed() routines.

	* lib/misc/idLinux.c, lib/include/su.h: Add Id_AuthGetLocal() and
	Id_GetAuthExternal() routines, and compat fixes.

	* lib/misc/timeutil.c, lib/include/timeutil.h: Add
	TimeUtil_UnixTimeToNtTime() routine.

	* lib/include/util.h: Add a couple of MacOS routines.

	* lib/include/vmBackup.h, lib/vmBackupLib/stateMachine.c: add a
	couple of structure elements for Windows backup fixes.

	* lib/include/vm_basic_asm.h: fixes for reading TSC on 64-bit
	platforms.

	* lib/include/vm_basic_defs.h: Add other va_copy macros.

	* lib/include/vm_basic_types.h: Fixes for compiling on a wide
	range of systems.

	* lib/include/vm_legal.h: Change the PATENTS_STRING

	* lib/include/vm_product.h: Add "License Infrastructure" product.

	* lib/include/vm_tools_version.h: Change tools versions listed for
	various upcoming product releases.

	* lib/include/vm_version.h: Update the versions.

	* lib/include/x86cpuid.h: Define more CPU flags & fields, add new
	CPU models. Fixes for fully writable TSC detection.

	* lib/message/message.c, lib/message/messageBackdoor.c: Fixes for
	FreeBSD.

	* lib/misc/util_misc.c: Handle MacOS.

	* lib/rpcIn/rpcin.c: Fail a badly-formed RPC instead of
	ASSERT()'ing into oblivion.

	* lib/string/bsd_vsnprintf.c: Various fixes to synchronize with
	bsd_vsnwprintf.c.

	* lib/string/Makefile.am, lib/string/shared/Makefile.am,
	lib/string/str.c lib/string/bsd_vsnwprintf.c: New file to
	implement vsnwprintf() for compat purposes.

	* lib/vixTools/vixTools.c: New FileUTF8 routines.

	* Makefile.am, modules/Makefile.am: --without-x fixes, add
	xferlogs, move kernel module building into separate Makefile.am

	* modules/freebsd/*: Add FreeBSD kernel modules (vmblock, vmhgfs,
	vmmemctl, vmxnet).

	* modules/linux/*/include/compat_*.h,
	modules/linux/*/autoconf/cachector.c,
	modules/linux/*/autoconf/cachecreate.c,
	modules/linux/*/backdoor.c, modules/linux/vmhgfs/filesystem.c,
	modules/linux/vmhgfs/hgfsBd.c, lib/procMgr/procMgrPosix.c,
	lib/rpcOut/rpcout.c, lib/user/util.c, lib/vmCheck/vmcheck.c,
	libguestlib/Makefile.am, lib/deployPkg/runDeployPkgPosix.c,
	lib/include/vm_atomic.h: Compat fixes.

	* modules/linux/*/kernelStubs.h: Update for FreeBSD.

	* modules/linux/*/include/*.h, modules/linux/*/backdoor_def.h,
	modules/linux/*/cpName.h, modules/linux/*/hgfs.h,
	modules/linux/*/hgfsProto.h, modules/linux/*/hgfsUtil.[ch],
	modules/linux/*/kernelStubsLinux.c,
	modules/linux/*/messageBackdoor.c, modules/linux/*/message.c,
	modules/linux/*/rpcout.c, modules/linux/*/rpcin.c,
	modules/linux/*/staticEscape.c, modules/linux/*/vm_basic_asm.h,
	modules/linux/*/vm_basic_defs.h, modules/linux/*/vm_basic_types.h,
	modules/linux/*/x86cpuid.h, modules/linux/*/compat_*.h: Pull in
	updated files from main source tree.

	* modules/linux/*/Makefile.kernel: Remove CC_WARNINGS/CC_OPTS
	gunk.

	* modules/linux/*/README, modules/linux/*/Makefile.normal: Build
	foo.o driver by default on systems with VM_KBUILD=no.

	* modules/linux/vmhgfs/vmhgfs_version.h: Updated VMHGFS driver
	version.

	* modules/linux/vmmemctl/os.[ch],
	modules/linux/vmmemctl/vmballoon.c: Implement and use os_yield()
	to deprioritize the Balloon_Deallocate operation.

	* modules/linux/vmsync/*: New sync driver to make VM snapshots
	consistent.

	* modules/linux/vmxnet/bpf_meta.h: New file.

	* modules/linux/vmxnet/net_dist.h: Update NET_MAX_IMPL_PKT_OVHD
	value.

	* modules/linux/vmxnet/vm_device_version.h: Mention VMXNET3

	* modules/linux/vmxnet/vmkapi_status.h: Updated VMK_ERR codes.

	* modules/linux/vmxnet/vmkapi_types.h: Add VMK_CONST64(U) macros.

	* modules/linux/vmxnet/vmxnet2_def.h,
	modules/linux/vmxnet/vmnet_def.h,
	modules/linux/vmxnet/vmxnet_def.h,
	modules/linux/vmxnet/vmxnetInt.h, modules/linux/vmxnet/vmxnet.c:
	Add (optional) BPF support.

	* modules/linux/vmxnet/vmxnetInt.h, modules/linux/vmxnet/vmxnet.c:
	Add vmxnet_link_check to propagate device link status to netdev.

	* common/vm-support: New script to gather support info from a VM.

	* scripts/*/*-default: New poweron/poweroff/suspend/resume scripts
	for a VM. Add support for dropping user-provided scripts into a
	subdirectory.

	* toolbox/toolboxAbout.c: Eliminate warnings about unused
	variables.

	* toolbox/toolboxShrink.c: Update wording of message.

	* toolbox/copyPaste.c: Try cutting & pasting UTF8 text if we can.

	* xferlogs/*: New log transfer utility.

2007-10-26  Elliot Lee  <elliot@vmware.com>

	* Initial import of 2007.09.04-56574 code ("Axe" release).

	* Import 2007.10.08 snapshot, which includes patches to fix the
	--without-x flag, and compilation with gcc 4.2.
 07070100000007000081A4000000000000000000000001682255050000ACCA000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/LICENSE    LICENSE

Open-vm-tools v10.3.0

The Linux kernel modules are released under the GPL v2, a majority of the user level components are released under the LGPL v2.1, and the SVGA and mouse drivers are released under the X11 license.

Copyright  2007-2018 VMware, Inc.  All rights reserved.

=========================================================================
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.

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.

=========

GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999

Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.]

Preamble

The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.

This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.

When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things.

To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.

For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.

We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.

To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.

Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.

Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.

When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.

We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.

For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.

In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.

Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.

The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".

A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.

The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)

"Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library.

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.

1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library.
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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)

These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.

Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.

This option is useful when you wish to copy part of the code of the Library into a program that is not a library.

4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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.

If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.

5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.

However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.

When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.

If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)

Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.

6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.

You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.

For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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.

It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.

7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.

8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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.

9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.

10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License.

11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library.

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.

12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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.

13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.

14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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

15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

=========

X11 License

Copyright (c) <year> <copyright holders>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

========

Open-vm-tools v10.3.0 includes a number of subcomponents with separate copyright notices and license terms.  Your use of the source code for these subcomponents is subject to the terms and conditions of the following licenses.


SECTION 1: BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES

   >>> freebsd-base64-4.8
   >>> unicode-5.0


===================================================


--------------- SECTION 1:  BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES ----------

BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES are applicable to the following component(s).

>>> freebsd-base64-4.8

base64.c -- routines to encode/decode base64 data
$OpenLDAP: pkg/ldap/libraries/liblutil/base64.c,v 1.15 2006/01/03 22:12:11 kurt Exp $ /
This work is part of OpenLDAP Software <http://www.openldap.org/>.

Copyright 1998-2006 The OpenLDAP Foundation.
Portions Copyright 1998-2003 Kurt D. Zeilenga.
Portions Copyright 1995 IBM Corporation.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted only as authorized by the OpenLDAP
Public License.

A copy of this license is available in the file LICENSE in the
top-level directory of the distribution or, alternatively, at
<http://www.OpenLDAP.org/license.html>.
The OpenLDAP Public License
Version 2.8, 17 August 2003

Redistribution and use of this software and associated documentation
("Software"), with or without modification, are permitted provided
that the following conditions are met:

1. Redistributions in source form must retain copyright statements
and notices,

2. Redistributions in binary form must reproduce applicable copyright
statements and notices, this list of conditions, and the following
disclaimer in the documentation and/or other materials provided
with the distribution, and

3. Redistributions must contain a verbatim copy of this document.

The OpenLDAP Foundation may revise this license from time to time.
Each revision is distinguished by a version number.  You may use
this Software under terms of this license revision or under the
terms of any subsequent revision of the license.

THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT
SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

The names of the authors and copyright holders must not be used in
advertising or otherwise to promote the sale, use or other dealing
in this Software without specific, written prior permission.  Title
to copyright in this Software shall at all times remain with copyright
holders.

OpenLDAP is a registered trademark of the OpenLDAP Foundation.

Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
California, USA.  All Rights Reserved.  Permission to copy and
distribute verbatim copies of this document is granted.

ADDITIONAL LICENSE INFORMATION:

Portions Copyright (c) 1996, 1998 by Internet Software Consortium.

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

This work is based upon Base64 routines (developed by IBM) found
Berkeley Internet Name Daemon (BIND) as distributed by ISC.  They
were adapted for inclusion in OpenLDAP Software by Kurt D. Zeilenga.


>>> unicode-5.0

Copyright (c) 2008 VMware, Inc.  All rights reserved.

Copyright (c) 1991-2007 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html.

Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that (a) the above copyright notice(s) and this permission notice appear with all copies of the Data Files or Software, (b) both the above copyright notice(s) and this permission notice appear in associated documentation, and (c) there is clear notice in each modified Data File or in the Software as well as in the documentation associated with the Data File(s) or Software that the data or software has been modified.

THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.

Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder.

[OPENVMTOOLS1030GANV061518]

  07070100000008000081A40000000000000000000000016822550500000899000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/Makefile.am    ################################################################################
### Copyright (c) 2007-2024 Broadcom. All Rights Reserved.
### Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc.
### and/or its subsidiaries.
###
### Top-level Makefile for building the VMware OSS Tools.
###
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

# These flags get passed to aclocal when autoreconf calls it, and tell aclocal
# that all of our macros are in the 'm4' subdirectory.
ACLOCAL_AMFLAGS = -I m4

SUBDIRS =
SUBDIRS += lib
if LINUX
   SUBDIRS += libguestStoreClient
endif
SUBDIRS += libvmtools
SUBDIRS += libhgfs
SUBDIRS += hgfsclient
if ENABLE_VGAUTH
   SUBDIRS += vgauth
   SUBDIRS += vgauthImport
endif
SUBDIRS += checkvm
SUBDIRS += libguestlib
if ENABLE_DEPLOYPKG
   SUBDIRS += libDeployPkg
endif


SUBDIRS += rpctool
SUBDIRS += namespacetool
SUBDIRS += scripts
SUBDIRS += services
SUBDIRS += toolbox
if HAVE_X11
   SUBDIRS += vmware-user-suid-wrapper
endif
if HAVE_FUSE
   SUBDIRS += vmblock-fuse
   SUBDIRS += vmhgfs-fuse
endif
if !LINUX
   SUBDIRS += vmblockmounter
endif
SUBDIRS += xferlogs
if ENABLE_TESTS
   SUBDIRS += tests
endif
if WITH_KERNEL_MODULES
   SUBDIRS += modules
endif
SUBDIRS += docs
if ENABLE_LIBAPPMONITOR
   SUBDIRS += libappmonitor
endif

if HAVE_UDEV
   SUBDIRS += udev
   SUBDIRS += vmwgfxctrl
endif


install-data-local:
	$(INSTALL) -d $(DESTDIR)/etc/vmware-tools/
	$(INSTALL) -m 644 $(srcdir)/tools.conf $(DESTDIR)/etc/vmware-tools/tools.conf.example

   07070100000009000081A4000000000000000000000001682255050000599E000000000000000000000000000000000000002800000000open-vm-tools-12.5.2/open-vm-tools/NEWS   open-vm-tools 2013.04.16 changes:
	* VMCI/VMSOCK Linux kernel modules are disabled for kernels
	  3.9 and above.

	* HGFS compilation fixes, performance and other improvements.

	* Disable glib deprecation warnigns on newer Linux
	  distributions.

	* Configure script will automatically detect procps-ng.

	* Other fixes and cleanups.

open-vm-tools 2012.12.26 changes:
	* vmsync is not longer being compiled on newer (3.0+) Linux
	  kernels since they support FIFREEZE/FITHAW ioctls.

	* vmblock is not longer being compiled on newer (3.0+) Linux
	  kernels, such systems should use vmblock-fuse.

	* The draft SOCK_SEQPACKET code is removed from VSOCK driver.

	* Fixes for the vmballoon driver.

	* Other fixes and cleanups.

open-vm-tools 2012.10.14 changes:
	* Changes and cleanups to VMCI driver

	* Changes to HGFS to allow compiling on newer kernels

	* Fix MTU handling in vmxnet3 for Solaris driver.

	* Updates to the vmballon driver to handle new protocol.

	* Fix for FreeBSD version of vmblock driver to stop kernel
	  panics on unload on FreeBSD 9.0

	* Fix to guestinfo plugin causing vmtoolsd to crash when
	  guest had too many NICs.

	* Other fixes and cleanups.

open-vm-tools 2012.05.21 changes:
	* Updates for newer Linux kernel releases (3.4).

	* Fix for vmxnet driver and IPV6

	* VMCI updates and fixes.

	* Other fixes and cleanups.

open-vm-tools 2012.03.13 changes:
	* Updates for newer Linux kernel releases (3.3).

	* Updates for Solaris 11.

	* Updates for FreeBSD 9.0.

	* Translation updates.

	* Other fixes and cleanups.

open-vm-tools 2011.12.20 changes:
	* Updates for new Linux kernel releases, including some fixes for
	Fedora's re-versioned 3.x kernels.

	* VMCI sockets has some changes for a new socket type, targeted
	at replacing the "backdoor" communication used by the tools services.

	* HGFS has better session support.

	* Miscelaneous bug fixes and small enhancements in other parts
	of the code.

open-vm-tools 2011.11.20 changes:
	* Updates for new Linux kernel releases.

	* Better Unity and DnD compatibility with newer Linux distros.

	* Other minor fixes and cleanups.

open-vm-tools 2011.10.26 changes:
	* Mostly cleanups and bug fixes.

	* Code can now compile with uClibc, with some caveats.

open-vm-tools 2011.09.23 changes:
	* open-vm-tools now can use the built in Linux freeze / thaw
	support for block devices, making the vmsync driver unnecessary
	in kernels supporting that feature.

	* The VMCI driver has been simplified, removing VM-to-VM
	communication support (which is being removed from VMware products).

	* The Unity team decided to remove the Unity plugin from open-vm-tools,
	given its partial brokenness due to reliance on internally-modified
	version of GNOME libraries, among other reasons.

	* Other bug fixes and code cleanup.

open-vm-tools 2011.08.21 changes:
	* Enabled several VIX APIs on FreeBSD.

	* Minor bug fixes and code cleanup.

open-vm-tools 2011.07.19 changes:
	* Fix an issue in the HGFS driver that could lead to a kernel
	  panic.

	* Update some code to support new compiler and kernel versions.

	* Minor bug fixes and code cleanup.

open-vm-tools 2011.06.27 changes:
	* A few enhancements to Unity: XFCE support, better interaction
	  with "the other" (Ubuntu's) Unity and compositing window
	  managers, better X error handling, and a few bug fixes.

	* A few bug fixes in HGFS, and minor bug fixes in other components.

	* Otherwise, mostly code cleanup.

open-vm-tools 2011.05.27 changes:
	* Mostly cleanups and a few bug fixes.

open-vm-tools 2011.04.25 changes:
	* Mostly cleanups and small bug fixes in this release.

	* Logging is enabled by default in vmtoolsd, writing to syslog.
	  The default log level is not chatty, so few messages should
	  make it to syslog during normal operation.

	* The GUI version of the toolbox was removed.

open-vm-tools 2011.03.28 changes:
	* HGFS mounter and vmusr's suid wrapper were changed to avoid
	  issues with symlink attacks. A new mount utility for vmblock on
	  Solaris and FreeBSD was added.
  
	* The VMCI driver was thoroughly reworked so that it can serve as
	  both the host and guest VMCI driver. This is mostly targeted at
	  supporting nested VMs.
  
	* vmusr got better integration with X's session manager, including
	  proper cleanup during session teardown.
  
	* Unity has been enhanced to better handle some desktop environments.

	* Many small bug fixes in other areas, including updates for newer
	  Linux kernels.

open-vm-tools 2011.02.23 changes:
	* Some copy & paste issues with KDE were fixed.

	* Mostly cleanups and bug fixes, with a few build enhancements.

open-vm-tools 2011.01.24 changes:
	* Mostly cleanups and bug fixes.

	* Install code fixed to handle translation catalogs correctly.

open-vm-tools 2010.12.19 changes:
	* New version of DnD code lands in open-vm-tools. The host part
	of the new DnD protocol is not available yet in VMware products,
	though.

	* vmtoolsd gets some new functionality to support new features
	being developed internally.

	* vmxnet driver for FreeBSD fixed to allow changing the MAC address.

	* lots of bug fixes and other cleanups in various areas.

open-vm-tools 2010.11.17 changes:
	* Mostly cleanups and bug fixes.

	* vmxnet3 on Solaris now supports jumbo frames.

open-vm-tools 2010.10.18 changes:
	* The unity plugin has received some refactoring work, and the
	menu gathering code has been enhanced to be more DE-agnostic and
	support the new app icon code. It now needs glib 2.24 to compile.

	* Several bug fixes and enhancements to the VIX plugin.

	* Bug fixes to the guest info plugin so that it better supports
	VMs with several network interfaces.

	* Other minor enhancements and bug fixes in several areas, including
	vsock, vmmemctl and copy & paste.

open-vm-tools 2010.09.19 changes:
	* Mostly cleanups and minor bug fixes.

	* VIX plugin has been updated with lots of new commands being added
	to the next VIX API release.

open-vm-tools 2010.08.24 changes:
	* HGFS and VMCI kernel module bug fixes, and updates to compile
	in newer Linux kernels.

	* HGFS server interface refactoring was finished, now supports
	the transport abstraction available for the kernel interface.

	* VIX operations are now properly implemented on Solaris, plus
	new operations added to support features under development.

	* Other minor cleanups and bug fixes.

open-vm-tools 2010.07.25 changes:
	* Features previously provided by vmware-user are now provided
	as vmtoolsd plugins. vmware-user has been removed and code that
	interacted with it changed to start the correct vmtoolsd instance.

	* Lots of cleanup: removed old compatibility headers not needed
	anymore, removed dead code, consolidated logging code.

	* Time synchronization now works more like NTP.

	* New code for features not yet exposed by VMware host software
	is being added.

open-vm-tools 2010.06.16 changes:
	* VMCI moves towards unifying the guest and host driver APIs,
	and gets support for MSI/MSI-X.

	* More work on new VMCI-backed HGFS protocol.

	* vmmemctl: Linux driver removed (it's now upstream), plus
	cleanup and simplification of the FreeBSD and Solaris drivers.

	* some Linux kernel compatibility fixes.

	* Cleanup of old kernel support code.

	* Some cleanup of old VIX features.

	* toolbox-cmd was updated to use newer APIs, and got some i18n
	enhancements.

	* some bug fixes and enhancements to the logging code.

	* update detection and use of some needed libraries.

	* More progress in making libraries thread-safe.

open-vm-tools 2010.04.25 changes:
	* VMCI and HGFS get some initial work for new features.

	* vmbackup support has been extended to more systems, allowing its
	use even if the system lacks a proper "sync" implementation.

	* A few bug fixes in different areas: Unity/X11, vmware-user startup
	on upstart-based systems, and other minor fixes / thread safety fixes.

	* The pvscsi driver was removed from the open-vm-tools distribution (it
	is upstream as of Linux 2.6.33).

	* The vmmemctl driver will be upstream as of Linux 2.6.34, at which
	point it will be removed from open-vm-tools.

open-vm-tools 2010.03.20 changes:
	* New i18n support for Tools; it is based on VMware's internal tools
	(instead of gettext).

	* Logging infrastructure has been enhanced and now supports rotation
	(without the need for logrotate) and syslog.

	* Bug fixes in several areas (DnD, backup support, thread safety).

	* Updates in Linux kernel modules for compatibility with newer kernels.

	* New functionality in the Unity support code.

open-vm-tools 2010.02.23 changes:
	* Mostly bug fixes, cleanups and code refactoring.

open-vm-tools 2010.01.19 changes:
	* Linux modules have been updated to compile on newer kernels.

	* Solaris modules now should compile on OpenSolaris (tested on 2009.06).

	* Other than those, mostly bug fixes and minor refactoring.

open-vm-tools 2009.12.16 changes:
	* Some improvements to vmtoolsd, base libraries and and the plugin
	interface.
	
	* Some library refactoring: use new lock library, changes to support
	compilation of some code on ARM.
	
	* some fixes in configure.ac to try to correctly support newer distros.
	
	* vsock/vmci improvements.
	
	* bug fixes in the vmxnet / vmxnet3 drivers, and FreeBSD's vmblock driver.
	
	* vmxnet3 for Linux is now upstream (as of Linux 2.6.32), and will be
	removed from later releases of open-vm-tools.
	
	* pvscsi will be available upstream starting with Linux 2.6.33 and at that
	time will be removed from open-vm-tools.

open-vm-tools 2009.11.16 changes:
	* Lots of refactoring and cleanup in the code, mainly targeting the
	definition of a set of public APIs.

	* vmblock-fuse can now replace the vmblock kernel module for DnD
	operations.

	* Fix some memory leaks in the guestInfo module. Users of the 2009.10.15
	release are recommended to upgrade, or at least crossport cid 6a8d4279.

open-vm-tools 2009.10.15 changes:
	* The HGFS module got some performance enhancements.

	* Minor enhancements to vmtoolsd and the logging system.

	* Fix for a few issues reported on the sourceforge bug tracker.

	* Lots of code refactoring, and a few bug fixes.

open-vm-tools 2009.09.18 changes:
	* Mostly bug fixes and minor enhancements.

	* The pvscsi code was re-factored in preparation for upstreaming. The
	driver has been sent to the LKML for inclusion in the main Linux tree,
	and might be removed from open-vm-tools once it's accepted.

open-vm-tools 2009.08.24 changes:
	* Remove support for Linux kernels < 2.6.9.

	* The vsock now implements a new notification protocol that has better
	performance than the previous.

	* New infrastructure for sending more network config-related information
	about the virtual machine to the host.

	* Other bug fixes and minor improvements to the code.

open-vm-tools 2009.07.22 changes:
	* Better support for dkms by means of a script to help create a dkms tree.

	* "make install" now also installs header files for public libraries, plus
	a few fixes to incorrect install behavior.

	* Lots of improvements to the new DnD code.

	* This will be the last release with support for Linux kernels < 2.6.9.

open-vm-tools 2009.06.18 changes:
	* Mostly a bug fix release.

	* vmhgfs now is able to use vsock as a transport (although backend support
	for HGFS over vsock is not yet released in VMware products).

open-vm-tools 2009.05.22 changes:
	* Mostly a cleanup and bug fix release.

	* Fix a build issue where a symlink attack could cause the open-vm-tools
	build to overwrite data (by placing a symlink in /tmp).

	* Second (and last?) batch of changes to clean up duplicate files
	in the source tree, including Solaris and FreeBSD kernel modules and
	other module-specific shared files.

	* Plugins now are properly built using plugins_LTLIBRARIES so that
	"-rpath" doesn't need to be used (this would cause an RPATH entry to
	be added to the final binary, which some tools didn't like). Thanks to
	Dominique Leuenberger for the suggestion.

	* open-vm-tools now properly detects PAM and enables PAM support in the
	code.

open-vm-tools 2009.04.23 changes:
	* Implemented copy & paste support for RTF data and file contents.

	* guestd has been removed from open-vm-tools; vmtoolsd is now the only
	option for the main tools service.

	* Added vmblock and vmmemctl modules for Solaris (under the CDDL).

	* vmware-user can now work with both vmblock-fuse and vmblock.

	* Linux HGFS now has a stream-based (TCP, vsock) transport, still under
	development.

	* First batch of changes to cleanup duplicated files in the source tree.
	Most duplicated files in the Linux kernel modules have been cleaned up.

open-vm-tools 2009.03.18 changes:
	* Mostly a bug fix release.

	* Solaris vmxnet3 driver was added; open-vm-tools now should also compile on
	OpenSolaris (tested on 08.11), as long as the --without-gtkmm option is used.

	* The new DnD V3 protocol is now available in open-vm-tools.

	* Added "rpctool", a simple, stand-alone tool to send RPC commands to the
	host software.

	* vmtoolsd is now preferred in lieu of vmware-guestd; vmware-guestd will
	most probably be completely removed in the next release.

open-vm-tools 2009.02.18 changes:
	* open-vm-tools now depend on glib 2.6.0 as a minimum requirement.

	* Added vmxnet module for Solaris, and reworked the HGFS module so it
	works without help from vmware-guestd.

	* Added implementation of new DnD protocol, which adds a dependency on
	a C++ compiler and the gtkmm library to vmware-user.

	* The code from the "core services" has been added to open-vm-tools,
	including a few tests. vmtoolsd + plugins are now capable of replacing
	vmware-guestd (vmware-user still needs to have some features ported
	over), but this code is still not to be considered as stable as the
	legacy services, so vmware-guestd is still included.

	* A few fixes for compatibility with non-GNU toolchains, newer Linux
	kernels and old gcc compilers.

open-vm-tools 2009.01.21 changes:
	* First open source release of the HGFS module for Solaris, under the CDDL.
	Other modules are expected to be added in the upcoming releases.

	* Added an implementation of vmblock on top of FUSE; vmware-user still
	doesn't use this module even if it is available, though.

	* Linux modules now add the "supported" tag used by Novell in their
	SLES 10 SP2 release when loading modules.

	* Fix compilation of modules in newer Linux kernels which don't include
	$(LINUXINCLUDE) in the compiler flags anymore.

open-vm-tools 2008.12.23 changes:
	* Lots of makefile cleanup with the switch to using libtool archives.

	* Automatically disable Unity if multimon support is disabled.

	* Actually build the pvscsi modules.

	* First bits of the "Core Service" project are starting to show up; the
	base "vmtools" library was added to the package. It currently is mostly
	a collection of the existing libraries already shipped with open-vm-tools,
	plus some extra functionality build on top of glib. Currently no other
	code in open-vm-tools uses it, so it's optional.

	* The HGFS driver was fixed for the Linux 2.6.28 kernel.

open-vm-tools 2009.11.18 changes:
	* The pvscsi Linux kernel module has been added (for kernels >= 2.6.8).
	It provides access to VMware's new paravirtualized SCSI device.

	* The HGFS driver and user-level code has seen a lot of refactoring to
	enable more consistent name escaping. The FreeBSD driver now supports
	symlinks.

	* The Linux module makefiles now support exporting symbol version files,
	allowing modules with dependencies (such as vsock, which depends on vmci)
	to correctly build and load on Linux >= 2.6.26 with CONFIG_MODVERSIONS.

	* Rudimentary support for dkms.

	* Assortment of bug fixes.

open-vm-tools 2009.10.13 changes:
	* The vmxnet3 Linux kernel module has been added. This module provides
	better network performance for the guest. The corresponding virtual
	hardware is available beginning with Workstation 6.5, though
	performance benefits are unlikely to be realized until a later VMware
	product release. The module should work for all kernels beginning with
	2.6.

	* The open-vm-tools no longer depend on libproc-dev. Several people
	reported this issue (Sourceforge bug 1960947).

	* Added a command line argument to skip privileged operations during
	make install (--without-root-privileges).

	* Guestd now supports backwards time synchronization, though the
	corresponding hypervisor-side changes are not yet part of any shipping
	VMware products.
	
	* Assortment of bug fixes.

open-vm-tools 2009.09.03 changes:
	* Fixed an issue where open-vm-tools fails to configure when using
	--without-icu. Thanks to Timo Gurr for reporting the issue (Sourceforge
	bug 2046262).

	* Fixed failed build on Ubuntu Intrepid and Fedora 9. Thanks to Nathan
	Charles for reporting the issue (Sourceforge bug 2048423).

	* Fixed kernel module build issues on 2.6.27 pre-release kernels.
	Thanks to Dominique Leuenberger for reporting the issue (Sourceforge
	bug 2071170).

	* ...and other bug fixes.

open-vm-tools 2008.08.08 changes:
	* Unity for X11 guests has been added. Unity is implemented within
	vmware-user and requires no additional setup beyond setting up the
	vmware-user application itself. Unity should work with Fusion 1.x
	releases as well as with the upcoming Workstation 6.5 release. Our
	in-house testing was with Linux guests, and they should mostly work.
	There is very little standing in the way of FreeBSD/Solaris support,
	though we've never built or tested Unity for those platforms.

	* The VMCI Linux kernel module has been added. This module provides
	high-speed datagram and socket interfaces for guest<->guest and
	host<->guest communication. It should work for all kernels beginning
	with 2.4, and for VMware products beginning with Workstation 6.5.

	* The VMCI sockets Linux kernel module has been added. It provides
	both datagram and stream socket interfaces to userlevel for use with
	VMCI. As with VMCI, it should work for kernels 2.4 or later, and for
	VMware products beginning with Workstation 6.5.

	* The command-line Toolbox has been added. This application provides
	the same functionality as the GTK Toolbox, but with a scriptable
	command-line interface. It also has some statistic retrieval commands
	that aren't found in the GTK Toolbox.

	* Fixed compilation of vmsync and vmmemctl Linux kernel modules on
	2.6.26. Thanks to Pavol Rusnak for the report (Sourceforge bug 2032683).

	* Fixed an issue with guestd's nicInfo updating mechanism. Thanks to
	Jason Lunz for the patch (not tracked on Sourceforge).

	* Fixed handling of $(DESTDIR) in automake. Thanks to Mike Auty for
	the patch (Sourceforge bug 2018802).

	* Fixed build of vmware-user using gtk 1.2. Thanks to Stephen Duncan
	for the report (Sourceforge bug 2014338).

	* Fixed compilation of lib/guestApp when using --without-x. Thanks to
	Martin Preishuber for the report (Sourceforge bug 2013568).

	* As usual, other bug fixes.

open-vm-tools 2008.07.01 changes:
	* Fixed a backwards time synchronization issue (not tracked on
	Sourceforge). Thanks to Eric Castan for reporting it.

	* Fixed an issue where open-vm-tools configured via --without-x didn't
	compile (not tracked on Sourceforge). Thanks to Mark Foster for
	reporting the bug.

	* Other bug fixes.

open-vm-tools 2008.06.20 changes:
	* Fixed Sourceforge bug 1847750 (FreeBSD 7 & 8 builds) and Sourceforge
	bug 1981632 (build failure on Solaris). This should get open-vm-tools
	building and running on FreeBSD 7 & 8 and Solaris. Thanks to Martin
	Blapp for all the FreeBSD patches, and Jonathan Keatley for reporting
	the Solaris bug.

	* Fixed Sourceforge bug 1968416 (packet counting in FreeBSD vmxnet).
	Thanks to Shunsuke SHINOMIYA for reporting this bug.

	* Fixed Sourceforge bug 1983375 (Cannot specify kernel constraints).
	You can now pass --without-kernel-modules, --with-kernel-release, and
	--with-linuxdir to the ./configure script. Thanks to Craig Phillips for
	reporting this bug.

	* Other bug fixes.

open-vm-tools 2008.06.03 changes:
	* Added the vmware-user-suid-wrapper application, with implementations
	for Linux, FreeBSD, and Solaris. This app is needed to make correct use
	of vmware-user with the vmblock kernel module. It should have been in
	the previous code refresh, but we accidentally overlooked it.

	* Fixed Sourceforge bug 1924246: vmhgfs on Linux properly reports the
	available space on the host. Thanks to Mikhail Krivtsov for reporting
	the bug.

	* Fixed Sourceforge bug 1839981: we now have rudimentary `make install`
	support. On any platform, it should copy files and kernel modules to
	the location specified at build-time, and on Linux, it will additionally
	run `depmod -a` to make the kernel modules accessible to modprobe. This
	change also adds a "--with-pam-prefix" argument to the configure
	script, which controls the location of guestd's pam files.
	
	* Other bug fixes.

open-vm-tools 2008.05.15 changes:
	* guestd no longer starts vmware-user. Packagers will need to use
	the XDG autostart spec, Xsession drop-in scripts, or other appropriate
	mechanisms to make sure that vmware-user is started as part of
	X session initialization. Please see
	http://open-vm-tools.wiki.sourceforge.net/Packaging for more details.

	* Bug fixes as usual.

open-vm-tools 2008.05.02 changes:
	* Continued Unicode support.

	* open-vm-tools now depends on libicu for codeset conversions. If you
	wish to build open-vm-tools without libicu, pass "--without--icu" when
	configuring the package. Without libicu, codeset conversions will be
	done as before, via calls to iconv.
	
	* A few more bug fixes.

open-vm-tools 2008.04.14 changes:
	* Update the license stamp on all LGPL files.

	* Continued Unicode support.

	* Handle libdumbnet on Debian.

	* More bug fixes, including a security fix in guestd.

open-vm-tools 2008.03.19 changes:
	* Continued Unicode support.

	* A few bug fixes.

open-vm-tools 2008.03.03 changes:
	* Bug fixes (including the ability to specify custom LDFLAGS
	at build time, thanks to Mike Auty).

	* First cut of HGFSv3 implementation.

	* Beginnings of DnDv3 implementation.

	* Add Unicode support all over the code base.

open-vm-tools 2008.02.13 changes:
	* Some bug fixes.

open-vm-tools 2008.01.23 changes:

	* The Linux HGFS kernel module now supports writeback caching, which
	should yield better performance.

	* Added a generic Unicode-aware library to ease i18n and l10n work.

	* A bunch of bug fixes.

open-vm-tools 2007.11.15 "Bandsaw" changes:

	* Kernel modules for FreeBSD, including an experimental port of HGFS to
	FreeBSD.

	* Add the vmsync driver on Linux to make VM snapshots consistent.

	* Added the xferlogs utility, the *-vm-default scripts, and the
	vm-support script.

	* Build on a wider variety of systems.

	* Lots of smaller bug fixes throughout the code.

open-vm-tools 2007.09.04 "Axe" changes:
	* Initial release of open-vm-tools.
  0707010000000A000081A400000000000000000000000168225505000000C5000000000000000000000000000000000000002A00000000open-vm-tools-12.5.2/open-vm-tools/README This file is no longer distributed with open-vm-tools.

For current project information and release notes, please refer to
README.md and ReleaseNotes.md at https://github.com/vmware/open-vm-tools

   0707010000000B000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/checkvm    0707010000000C000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/checkvm/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   0707010000000D000081A400000000000000000000000168225505000005EC000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/checkvm/Makefile.am    ################################################################################
### Copyright (c) 2007-2016,2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-checkvm

vmware_checkvm_SOURCES =
vmware_checkvm_SOURCES += checkvm.c

vmware_checkvm_LDADD =
vmware_checkvm_LDADD += @GLIB2_LIBS@
vmware_checkvm_LDADD += @VMTOOLS_LIBS@

vmware_checkvm_CPPFLAGS =
vmware_checkvm_CPPFLAGS += @GLIB2_CPPFLAGS@

if HAVE_ICU
   vmware_checkvm_LDADD += @ICU_LIBS@
   vmware_checkvm_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS)     \
                            $(LIBTOOLFLAGS) --mode=link $(CXX)       \
                            $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
                            $(LDFLAGS) -o $@
else
   vmware_checkvm_LINK = $(LINK)
endif

0707010000000E000081A40000000000000000000000016822550500000C53000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/checkvm/checkvm.c  /*********************************************************
 * Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * checkvm.c --
 *
 *      Check if we are running in a VM or not
 */

#include <stdlib.h>
#include <stdio.h>
#include <glib.h>

#if !defined(_WIN32)
#include <unistd.h>
#endif

#include "vm_basic_types.h"
#include "vmcheck.h"
#if defined(_WIN32)
#include "getoptwin32.h"
#include "vmware/tools/win32util.h"
#endif

#include "checkvm_version.h"
#include "vm_version.h"
#include "embed_version.h"
VM_EMBED_VERSION(CHECKVM_VERSION_STRING);

/*
 *  Start of main program.  Check if we are in a VM, by reading
 *  a backdoor port.  Then process any other commands.
 */
int
main(int argc,
     char *argv[])
{
   uint32 version[2];
   gchar *gAppName;
   GError *gErr = NULL;
   gboolean product = FALSE;
   int success = 1;

   GOptionEntry options[] = {
      {"prod", 'p', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &product,
       "print VMware hypervisor product.", NULL},
      {NULL}
   };
   GOptionContext *optCtx;

#if defined(_WIN32)
   WinUtil_EnableSafePathSearching(TRUE);
#endif

   /*
    * set up glib options context.
    */
   gAppName = g_path_get_basename(argv[0]);

   g_set_prgname(gAppName);
   optCtx = g_option_context_new(NULL);
   g_option_context_add_main_entries(optCtx, options, NULL);

   if (!VmCheck_IsVirtualWorld()) {
      g_printerr("Error: %s must be run inside a virtual machine"
                 " on a VMware hypervisor product.\n", gAppName);
      goto out;
   }

   if (!VmCheck_GetVersion(&version[0], &version[1])) {
      g_printerr("%s: Couldn't get version\n", gAppName);
      goto out;
   }

   if (!g_option_context_parse(optCtx, &argc, &argv, &gErr)) {
      g_printerr("%s: %s\n", gAppName, gErr->message);
      g_error_free(gErr);
      goto out;
   }

   /*
    * product is true if 'p' option was passed to parser
    */
   if (product) {
      switch (version[1]) {
      case VMX_TYPE_SCALABLE_SERVER:
         g_print("ESX Server\n");
         break;

      case VMX_TYPE_WORKSTATION:
         g_print("Workstation\n");
         break;

      default:
         g_print("Unknown\n");
         break;
      }
      success = 0;
      goto out;
   }

   g_print("%s version %d (good)\n", PRODUCT_LINE_NAME, version[0]);
   success = 0;

out:
   g_option_context_free(optCtx);
   g_free(gAppName);
   return success;
}
 0707010000000F000081A4000000000000000000000001682255050000060D000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/checkvm/checkvm_version.h  /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * checkvm_version.h --
 *
 * Version definitions for the VM checking utility.
 */

#ifndef _CHECKVM_VERSION_H_
#define _CHECKVM_VERSION_H_

/*
 * This component's version is coupled with Tools versioning. The effect
 * is that the version increments with each build, and with each Tools
 * version bump. If and when it becomes necessary to version the component
 * manually, make sure that the version is bumped any time the component or
 * its dependencies are changed.
 */
#include "vm_tools_version.h"
#define CHECKVM_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define CHECKVM_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _CHECKVM_VERSION_H_ */
   07070100000010000081A4000000000000000000000001682255050000EC02000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/configure.ac   ################################################################################
### Copyright (c) 2007-2024 Broadcom. All Rights Reserved.
### The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
###
### Configure script for building the VMware OSS Tools.
###
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

################################################################################
# In addition to the usual environment variables and command line
# arguments that a configure script usually takes (CFLAGS, LDFLAGS,
# etc.), this script also accepts variables of the form:
#
#  CUSTOM_LIB_CPPFLAGS: compile-time flags for LIB
#  CUSTOM_LIB_LIBS: link-time flags for LIB
#  RPCGENFLAGS: extra flags to pass to rpcgen
#
# The following libraries are currently tested: DNET, FUSE, GLIB2, GMODULE,
# GOBJECT, GTHREAD, GTK, GTKMM, ICU, LIBPNG, PAM, URIPARSER, ZLIB
################################################################################

###
### Initialization
###

TOOLS_VERSION="12.5.2"
AC_INIT(
   [open-vm-tools],
   [12.5.2],
   [open-vm-tools-devel@lists.sourceforge.net])

# In order to make this configure script auto-detect situations where
# people have a 32-bit userland running with a 64-bit kernel, we try to ask
# the compiler (assumedly gcc) for its default Target:.
# We have to set up $TEST_CC manually, since AC_PROG_CC hasn't yet been run (and can't be until AC_CANONICAL_HOST & AC_CANONICAL_BUILD are run)
# The purpose of all this is to set up $host_alias/$build_alias in a more
# intelligent way than config.guess currently does.
TEST_CC="$CC_FOR_BUILD"
test -z "$TEST_CC" && TEST_CC="$HOST_CC"
test -z "$TEST_CC" && TEST_CC="$CC"
if test -n "$TEST_CC" -a -z "$host_alias"; then
   host_alias="`$TEST_CC -dumpmachine`"
   if test -z "$build_alias" -a -n "$host_alias"; then
      build_alias="$host_alias"
   fi
fi
unset TEST_CC

# checkvm/checkvm.c has no special significance - we just need to pass in a file that
# helps autoconf verify that it really has found the source tree.
AC_CONFIG_SRCDIR([checkvm/checkvm.c])

# Keep the top-level directory tidy by putting auxiliary build tools and local
# macros in separate subdirectories.
AC_CONFIG_AUX_DIR([config])
AC_CONFIG_MACRO_DIR([m4])

AC_CANONICAL_HOST
AC_CANONICAL_BUILD

# Quote the regular expressions
case "$host_cpu" in
   [i[3456]86])
      arch="x32"
      ;;
   [amd64|x86_64])
      arch="x64"
      ;;
   [aarch64])
      arch="arm64"
      ;;
   [*])
      AC_MSG_ERROR([Unknown architecture.])
      ;;
esac

# Operational arguments.
AC_ARG_WITH([root-privileges],
	    [AS_HELP_STRING([--without-root-privileges],
	       [does not perform any operations that require root privileges])],
	    [],
	    [with_root_privileges=yes])

# Kernel arguments.
# The kernel args have to go here otherwise the KERNEL_RELEASE won't be visible
# to getOsVersion()
AC_ARG_WITH([kernel-release],
	    [AS_HELP_STRING([--with-kernel-release],
		[specifies the kernel release you want to build against])],
	    [KERNEL_RELEASE="$withval"],
	    [KERNEL_RELEASE=`uname -r`])

AC_ARG_WITH([linuxdir],
	    [AS_HELP_STRING([--with-linuxdir],
		[specifies the Linux directory you want to use])],
	    [LINUXDIR="$withval"],
	    [LINUXDIR=/lib/modules/$KERNEL_RELEASE])

# Turn the uname output into something we can run comparisons on.
getOsVersion() {
   major_version="`echo $KERNEL_RELEASE | cut -f1 -d. | cut -f1 -d-`"
   minor_version="`echo $KERNEL_RELEASE | cut -f2 -d. | cut -f1 -d-`"
   micro_version="`echo $KERNEL_RELEASE | cut -f3 -d. | cut -f1 -d-`"
   printf '%02d%02d%03d' $major_version $minor_version $micro_version
}

case "$host_os" in
   [linux*])
      os="linux"
      ;;
   [freebsd*])
      os="freebsd"
      ;;
   [kfreebsd*-gnu])
      os="kfreebsd-gnu"
      ;;
   [solaris*])
      os="solaris"
      ;;
   [*])
      AC_MSG_WARN([This is an untested and unsupported Operating System. Proceed at your own peril.])
      ;;
esac
osVersion="`getOsVersion`"

AC_ARG_ENABLE(
   glibc-check,
   AS_HELP_STRING(
      [--disable-glibc-check],
      [Skip checking the glibc version on the system. (off by default)]),
   [enable_glibc_check=$enableval],
   [enable_glibc_check="yes"])

if test "x$enable_glibc_check" != "xno" ; then
   if test "x$os" = "xlinux" ; then
      libc_ver=`ldd --version 2>/dev/null | head -1 | sed 's/.* //;q'`

      if test "x$libc_ver" = "x"; then
         AC_MSG_ERROR(["Failed to detect glibc version installed in the system."])
      fi

      libc_ver_mjr=`expr "$libc_ver" : "\([[0-9]]*\)"`
      libc_ver_mnr=`expr "$libc_ver" : "[[0-9]]*\.\([[0-9]]*\)"`
      libc_ver_num=`expr $libc_ver_mjr \* 1000 + $libc_ver_mnr`

      if test "$libc_ver_num" -lt 2011; then
         AC_MSG_ERROR(["glibc version $libc_ver detected.
This version of open-vm-tools requires glibc version 2.11 or later.
Please use an older version of open-vm-tools for this system."])
      fi
   fi
fi

if test "x$os" = "xfreebsd" -a "$osVersion" -lt 1000000; then
   AC_MSG_ERROR(["This version of open-vm-tools requires FreeBSD version 10 or later.
Please use an older version of open-vm-tools for this system."])
fi

AC_ARG_WITH([kernel-modules],
	    [AS_HELP_STRING([--with-kernel-modules],
		[compile and install the kernel modules])],
	    [],
	    [if test "x$os" = "xlinux" ; then
	        with_kernel_modules=no
	     else
	        with_kernel_modules=yes
	     fi
	    ])

if test "$with_kernel_modules" = "yes"; then
   case "$os" in
      linux)
         AC_MSG_ERROR([Building kernel modules for Linux is no longer supported.])
         ;;
      freebsd)
         freebsd_sysdir=/usr/src/sys
         if test -n "$SYSDIR"; then
            freebsd_sysdir="$SYSDIR"
         fi
         if test ! -f "$freebsd_sysdir/conf/kmod.mk"; then
            AC_MSG_ERROR([FreeBSD kernel tree not found. Please install the kernel sources (or provide the location using SYSDIR) or configure using --without-kernel-modules.])
         fi
         ;;
   esac
fi

# Locates X includes and lib, adds --without-x option,
# and sets no_x.
AC_PATH_XTRA

AC_CHECK_HEADER([valgrind/valgrind.h],
                [],
                [have_valgrind=no],
                [])

AC_ARG_ENABLE(
   valgrind,
   AS_HELP_STRING(
      [--enable-valgrind],
      [enables building with valgrind]),
   [enable_valgrind="$enableval"],
   [enable_valgrind="no"])

if test "$enable_valgrind" = "yes" ; then
   if test "$have_valgrind" = "no" ; then
      AC_MSG_ERROR([valgrind/valgrind.h not found. Make sure you have the valgrind headers installed.]),
   fi
   CPPFLAGS="$CPPFLAGS -DUSE_VALGRIND=1"
fi

# Arguments for disabling individual open-vm-tools features or libraries.
AC_ARG_ENABLE(
   multimon,
   AS_HELP_STRING(
      [--disable-multimon],
      [disables multimon, enabled by default]),
   [enable_multimon="$enableval"],
   [enable_multimon="yes"])

AC_ARG_WITH(
   gtk3,
   AS_HELP_STRING(
      [--without-gtk3],
      [compiles without Gtk 3.0]),
   [with_gtk3="$withval"],
   [with_gtk3="auto"])

AC_ARG_WITH(
   gtk2,
   AS_HELP_STRING(
      [--without-gtk2],
      [compiles without Gtk 2.0]),
   [with_gtk2="$withval"],
   [with_gtk2="auto"])


if test "$no_x" = "yes" ; then
   with_gtk2="no"
   with_gtk3="no"
else
   if test "$with_gtk2" = "auto" ; then
      if test "$with_gtk3" = "auto" ; then
         with_gtk2="no"
         with_gtk3="yes"
      elif test "$with_gtk3" = "no" ; then
         with_gtk2="yes"
      elif test "$with_gtk3" = "yes" ; then
         with_gtk2="no"
      fi
   elif test "$with_gtk2" = "no" ; then
      if test "$with_gtk3" = "auto" ; then
         with_gtk3="yes"
      elif test "$with_gtk3" = "no" ; then
         AC_MSG_ERROR('need either gtk2 or gtk3 or build with --without-x to disable building desktop plugins')
      fi
   elif test "$with_gtk2" = "yes"  ; then
      if test "$with_gtk3" = "auto" ; then
         with_gtk3="no"
      elif test "$with_gtk3" = "yes" ; then
         AC_MSG_ERROR('cannot set both --with-gtk2 and --with-gtk3')
      fi
   fi
fi

if test "$with_gtk2" = "no" ; then
   with_gtkmm="no"
fi

if test "$with_gtk3" = "no" ; then
   with_gtkmm3="no"
fi

if test "$with_gtk3" = "yes"; then
   AC_ARG_WITH(
      gtkmm3,
      AS_HELP_STRING(
         [--without-gtkmm3],
         [compiles without Gtkmm 3, sigc++, and related libs]),
      [with_gtkmm3="$withval"],
      [with_gtkmm3="yes"])
      with_gtkmm="no"
elif test "$with_gtk2" = "yes"; then
   AC_ARG_WITH(
      gtkmm,
      AS_HELP_STRING(
         [--without-gtkmm],
         [compiles without Gtkmm, sigc++, and related libs]),
      [with_gtkmm="$withval"],
      [with_gtkmm="yes"])
      with_gtkmm3="no"
fi

AC_ARG_ENABLE(
   docs,
   AS_HELP_STRING(
      [--disable-docs],
      [disables generation of API documentation; by default, docs are built if doxygen is available.]),
   [enable_docs="$enableval"],
   [enable_docs="yes"])

AC_ARG_ENABLE(
   tests,
   AS_HELP_STRING(
      [--disable-tests],
      [disable compilation of test code.]),
   [enable_tests="$enableval"],
   [enable_tests="auto"])

AC_ARG_ENABLE(
   resolutionkms,
   AS_HELP_STRING(
      [--enable-resolutionkms],
      [build the linux / unix resolutionkms module.]),
   [],
   [enable_resolutionkms="auto"])


AC_ARG_ENABLE(
   vmwgfxctrl,
   AS_HELP_STRING(
      [--enable-vmwgfxctrl],
      [build the linux / unix vmwgfxctrl tool.]),
   [],
   [enable_vmwgfxctrl="auto"])

AM_INIT_AUTOMAKE

###
### Constants
###
# These need to be declared after initialization.

# Some of our macro call-sites require changes to
# CPPFLAGS/CFLAGS/LDFLAGS. In such places, we save the original value
# of CPPFLAGS/CFLAGS/LDFLAGS before the macro call and restore it when
# the call is done. We must perform this save at each macro site,
# because CPPFLAGS/CFLAGS/LDFLAGS may change over the course of
# configuration.
#
# CPPFLAGS is intended for preprocessor options (-D and -I mainly)
# CFLAGS is intended for compiler options (-O, -f, -W, and so forth)

CPPFLAGS="$CPPFLAGS -DUSING_AUTOCONF=1 -DOPEN_VM_TOOLS"

###
### Programs
###
# C preprocessor and compiler.
AC_PROG_CPP
AC_PROG_CC

# C++ compiler. Note that unlike AC_PROG_CC, this call does not trigger an
# error if no C++ compiler was found; it'll just set the variable CXX to 'g++'.
AC_PROG_CXX

# This allows features like per-target compiler flags.  I.e., you can compile
# one copy of the same sources twice with different flags.  (See lib/guestApp
# for an example.)
AM_PROG_CC_C_O

# Needed for the various install and uninstall hooks.
AC_PROG_INSTALL
AC_PROG_SED
AC_PROG_LN_S
AC_PROG_MKDIR_P

# Needed for creating the archives in lib/ and the shared libraries.
AC_PROG_LIBTOOL
if test "$ac_cv_prog_AR" = false; then
   AC_MSG_ERROR([The 'ar' utility was not found. Please put ar on the path.])
fi

# We use pkg-config to set up the cflags and libs for gtk.
AC_CHECK_TOOL(
   [PKG_CONFIG],
   [pkg-config],
   [not_found])

if test "$GCC" != "yes"; then
   AC_MSG_ERROR([Only GCC is currently supported. Please put gcc in the path.])
fi

###
### Libraries
###

#
# Check for libintl.h. When configuring using "--without-x", /usr/local/include
# may not be added to the include path, so code that use glib's i18n functions
# would fail to compile because it can't find libintl.h.
#
AC_CHECK_HEADER([libintl.h],
                [],
                [have_libintl=no],
                [])
if test "$have_libintl" = "no"; then
   unset ac_cv_header_libintl_h
   CPPFLAGS="$CPPFLAGS -I/usr/local/include"
   AC_CHECK_HEADER([libintl.h],
                   [],
                   [AC_MSG_ERROR([libintl.h not found. Make sure you have the gettext headers installed.])],
                   [])
fi

AC_ARG_ENABLE([deploypkg],
   [AS_HELP_STRING([--disable-deploypkg],
     [do not build deploypkg plugin.])],
   [],
   [
    if test "$os" = "linux"; then
       enable_deploypkg=yes
    else
       enable_deploypkg=no
    fi
   ])

if test "$enable_deploypkg" = "yes"; then
  AC_VMW_CHECK_LIB([mspack],
                   [MSPACK],
                   [libmspack],
                   [],
                   [0.0.20040308alpha],
                   [mspack.h],
                   [mspack_version],
                   [],
                   [AC_MSG_ERROR([mspack >= 0.0.20040308alpha is required.])])
fi

AC_ARG_ENABLE([libappmonitor],
   [AS_HELP_STRING([--enable-libappmonitor],
     [Build the libappmonitor library.])],
   [],
   [
    enable_libappmonitor=no
   ])

AC_ARG_ENABLE([servicediscovery],
   [AS_HELP_STRING([--enable-servicediscovery],
     [Build the Service Discovery Plugin.])],
   [
    enable_servicediscovery=$enableval
   ],
   [
    enable_servicediscovery=no
   ])

#
# Check that servicediscovery is enabled only on linux systems
#

if test "$enable_servicediscovery" = "yes"; then
   if test "$os" != "linux"; then
      AC_MSG_ERROR([The Service Discovery plugin is only supported for Linux
      platforms. Try configure without --enable-servicediscovery option.])
   fi
fi

AC_ARG_ENABLE([salt-minion],
   [AS_HELP_STRING([--enable-salt-minion],
     [Install salt_minion related script files.])],
   [
    enable_saltminion=$enableval
   ],
   [
    enable_saltminion=no
   ])

#
# Check that salt_minion is enabled only on linux systems
#

if test "$enable_saltminion" = "yes"; then
   if test "$os" != "linux"; then
      AC_MSG_ERROR([The salt_minion package is only supported for Linux
      platforms. Try configure without --enable-salt-minion option.])
   fi
fi

#
# Check for glib 2.34.0 or greater.
#
AC_VMW_CHECK_LIB([glib-2.0],
                 [GLIB2],
                 [glib-2.0],
                 [],
                 [2.34.0],
                 [glib.h],
                 [g_key_file_new],
                 [],
                 [AC_MSG_ERROR([glib2 >= 2.34.0 is required.])])

AC_VMW_CHECK_LIB([gmodule-2.0],
                 [GMODULE],
                 [gmodule-2.0],
                 [],
                 [2.34.0],
                 [],
                 [g_module_open],
                 [],
                 [AC_MSG_ERROR([gmodule >= 2.34.0 is required.])])
AC_VMW_CHECK_LIB([gobject-2.0],
                 [GOBJECT],
                 [gobject-2.0],
                 [],
                 [2.34.0],
                 [glib-object.h],
                 [g_object_new],
                 [],
                 [AC_MSG_ERROR([gobject >= 2.34.0 is required.])])
AC_VMW_CHECK_LIB([gthread-2.0],
                 [GTHREAD],
                 [gthread-2.0],
                 [],
                 [2.34.0],
                 [],
                 [g_thread_new],
                 [],
                 [AC_MSG_ERROR([gthread >= 2.34.0 is required.])])
AC_CHECK_PROG([have_genmarshal],
              [glib-genmarshal],
              [yes],
              [no])

if test "$have_genmarshal" != "yes"; then
   AC_MSG_ERROR([glib-genmarshal is required; make sure it is available in your path.])
fi

#
# Check for fuse.
#

AC_ARG_WITH([fuse],
   [AS_HELP_STRING([--without-fuse],
     [compiles without FUSE support (disables vmblock-fuse/vmhgfs-fuse).])],
   [with_fuse="$withval"],
   [with_fuse=auto])

case "$with_fuse" in
   fuse|2)
      with_fuse="fuse"
      ;;
   fuse3|3)
      with_fuse="fuse3"
      ;;
   auto)
      ;;
   yes)
      ;;
   no)
      have_fuse3=no;
      have_fuse=no;
      ;;
   *)
      AC_MSG_FAILURE([--with-fuse was given with an unsupported paramter $with_fuse])
      ;;
esac


if test "$with_fuse" = "auto" ||
   test "$with_fuse" = "fuse3" ||
   test "$with_fuse" = "yes"; then
   #
   # Check for fuse3.
   #
   # FUSE_USE_VERSION sets the version of the FUSE API that will be exported.
   AC_VMW_CHECK_LIB([fuse3],
                    [FUSE3],
                    [fuse3],
                    [],
                    [3.10.0],
                    [fuse3/fuse.h],
                    [fuse_main],
                    [have_fuse3=yes;
                     AC_DEFINE([HAVE_FUSE3], 1, [Define to 1 if using FUSE3.])
                     AC_DEFINE([FUSE_USE_VERSION], 35, [FUSE API version to use.])],
                    [have_fuse3=no])

   if test "$have_fuse3" = "no"; then
      if test "$with_fuse" = "auto" || test "$with_fuse" = "yes"; then
         AC_MSG_NOTICE([Fuse3 is missing, trying to use older Fuse library.])
      else
         AC_MSG_FAILURE([Fuse3 was requested but unavailable on the system.])
      fi
   fi
fi

if test "$with_fuse" = "fuse" ||
   ( ( test "$with_fuse" = "auto" || test "$with_fuse" = "yes" ) &&
     test "$have_fuse3" = "no" ); then
   #
   # Check for fuse.
   #
   # FUSE_USE_VERSION sets the version of the FUSE API that will be exported.
   AC_VMW_CHECK_LIB([fuse],
                    [FUSE],
                    [fuse],
                    [],
                    [],
                    [fuse.h],
                    [fuse_main],
                    [have_fuse=yes;
                     AC_DEFINE([HAVE_FUSE], 1, [Define to 1 if using FUSE.])
                     AC_DEFINE([FUSE_USE_VERSION], 29, [FUSE API version to use.])],
                    [have_fuse=no])

   if test "$have_fuse" = "no"; then
      if test "$with_fuse" = "auto" || test "$with_fuse" = "yes"; then
	 AC_MSG_NOTICE([Fuse is missing, vmblock-fuse/vmhgfs-fuse will be disabled.])
      else
         AC_MSG_FAILURE([Fuse2 was requested but unavailable on the system.])
      fi
   fi
fi

if test "$with_fuse" = "no"; then
   AC_MSG_WARN([Fuse or Fuse3 is suppressed, vmblock-fuse/vmhgfs-fuse will be disabled.])
fi

#
# Check for PAM.
#
AC_ARG_WITH([pam],
   [AS_HELP_STRING([--without-pam],
     [compiles without PAM support (disables vgauth).])],
   [with_pam="$withval"],
   [with_pam=yes])

if test "$with_pam" = "yes"; then
   AC_VMW_DEFAULT_FLAGS([PAM])
   AC_VMW_CHECK_LIB([pam],
                    [PAM],
                    [],
                    [],
                    [],
                    [security/pam_appl.h],
                    [pam_start],
                    [PAM_CPPFLAGS="$PAM_CPPFLAGS -DUSE_PAM"],
                    [AC_VMW_LIB_ERROR([PAM], [pam])])
fi

AC_ARG_ENABLE([containerinfo],
   [AS_HELP_STRING([--disable-containerinfo],
     [do not build containerinfo plugin.])],
   [
     enable_containerinfo=$enableval
   ],
   [
       if test "$os" = "linux"; then
          enable_containerinfo=auto
       else
          enable_containerinfo=no
       fi
   ])

#
# Check that containerinfo plugin is enabled only on linux systems.
#
if test "$os" != "linux"; then
   if test "$enable_containerinfo" = "yes"; then
      AC_MSG_ERROR([The containerinfo plugin is only supported for Linux
      platforms. Try configure with --disable-containerinfo option.])
   fi
fi

if test "$enable_containerinfo" = "yes" ||
   test "$enable_containerinfo" = "auto"; then

can_build_containerinfo=yes

#
# AC_VMW_CONTAINERINFO_MSG(library)
#
# Wrapper around AC_MSG_WARN to print a standard message about missing libraries.
#
#     library ($1): name of missing library / package.
#
AC_DEFUN([AC_VMW_CONTAINERINFO_MSG],[
   can_build_containerinfo=no
   AC_MSG_WARN(["$1 is missing which is required for building containerinfo plugin."])
])

   AC_VMW_DEFAULT_FLAGS([CURL])
   AC_VMW_CHECK_LIB([curl],
                    [CURL],
                    [],
                    [],
                    [],
                    [curl/curl.h],
                    [curl_easy_init],
                    [CURL_CPPFLAGS="$CURL_CPPFLAGS"],
                    [AC_VMW_CONTAINERINFO_MSG([CURL])])

   AC_VMW_CHECK_LIB([protobuf],
                    [PROTOBUF],
                    [protobuf],
                    [],
                    [3.0.0],
                    [],
                    [],
                    [],
                    [AC_VMW_CONTAINERINFO_MSG(["protobuf >= 3.0.0"])])

   AC_VMW_DEFAULT_FLAGS([GRPC])
   AC_VMW_CHECK_LIBXX([grpc++],
                      [GRPC],
                      [grpc++],
                      [],
                      [1.3.2],
                      [grpc++/grpc++.h],
                      [],
                      [PKG_CHECK_MODULES([grpcxx], [grpc++ >= 1.3.2])],
                      [AC_VMW_CONTAINERINFO_MSG(["grpc++ >= 1.3.2"])])

#
# proto files needed by containerd grpc client.
#
# Installation location varies between Linux vendors.
#   Canonical, Ubuntu and Debian: in /usr/share/gocode/src
#   openSUSE/SUSE: in /usr/share/go/<version>/contrib/src
#
   for d in /usr/share/gocode/src /usr/share/go/1.*/contrib/src; do
       if test -d "$d"/github.com; then
           src_prefix="$d"
           break
       fi
   done
   shared_prefix=$src_prefix/github.com
   AC_SUBST(TYPES_DIR, github.com/containerd/containerd/api/types)
   AC_SUBST(TASKS_PROTOPATH, $shared_prefix/containerd/containerd/api/services/tasks/v1)
   AC_SUBST(DEP_PROTOPATH, $src_prefix)
   AC_SUBST(CONTAINERD_PROTOPATH, $shared_prefix/containerd/containerd/api/services/containers/v1)
   AC_SUBST(GOGO_PROTOPATH, $shared_prefix/gogo/protobuf)
   AC_CHECK_FILE([${CONTAINERD_PROTOPATH}/containers.proto],
                 [],
                 [AC_VMW_CONTAINERINFO_MSG(["containerd package"])])
   AC_CHECK_FILE([${TASKS_PROTOPATH}/tasks.proto],
                 [],
                 [AC_VMW_CONTAINERINFO_MSG(["containerd package"])])
   AC_CHECK_FILE([${DEP_PROTOPATH}/${TYPES_DIR}/mount.proto],
                 [],
                 [AC_VMW_CONTAINERINFO_MSG(["containerd package"])])
   AC_CHECK_FILE([${DEP_PROTOPATH}/${TYPES_DIR}/metrics.proto],
                 [],
                 [AC_VMW_CONTAINERINFO_MSG(["containerd package"])])
   AC_CHECK_FILE([${DEP_PROTOPATH}/${TYPES_DIR}/descriptor.proto],
                 [],
                 [AC_VMW_CONTAINERINFO_MSG(["containerd package"])])
   AC_CHECK_FILE([${DEP_PROTOPATH}/${TYPES_DIR}/task/task.proto],
                 [],
                 [AC_VMW_CONTAINERINFO_MSG(["containerd package"])])
   AC_CHECK_FILE([${GOGO_PROTOPATH}/gogoproto/gogo.proto],
                 [],
                 [AC_VMW_CONTAINERINFO_MSG(["gogoproto package"])])

#
# Binaries needed to build for containerd grpc client.
#
   AC_CHECK_PROG([GRPC_CPP], [grpc_cpp_plugin], [grpc_cpp_plugin], [not found])

   if test "$GRPC_CPP" != "grpc_cpp_plugin"  ; then
      AC_VMW_CONTAINERINFO_MSG(["grpc_cpp_plugin binary"])
   fi

   AC_CHECK_PROG([PROTOC], [protoc], [protoc], [not found])

   if test "$PROTOC" != "protoc"  ; then
      AC_VMW_CONTAINERINFO_MSG(["protoc binary"])
   fi

   if test "$can_build_containerinfo" = "no" ; then
      if test "$enable_containerinfo" = "auto" ; then
         enable_containerinfo=no
         AC_MSG_WARN(["Cannot enable containerinfo plugin since one ore more required packages are missing."])
      else
         AC_MSG_ERROR(["Cannot enable containerinfo plugin since one or more required packages are missing. Please configure without containerinfo (using --disable-containerinfo), or install the necessary libraries and devel package(s)."])
      fi
   else
      enable_containerinfo=yes
   fi
fi

AC_ARG_ENABLE([vgauth],
   [AS_HELP_STRING([--disable-vgauth],
     [do not build vgauth])],
   [
    if test "$with_pam" = "no" -a "$enableval" = "yes"; then
       AC_MSG_ERROR([Cannot enable vgauth without PAM. Please configure
                     without --without-pam or without --enable-vgauth.])
    fi
    enable_vgauth="$enableval"
   ],
   [
    if test "$with_pam" = "no"; then
       enable_vgauth="no"
       AC_MSG_WARN("Building without PAM; vgauth will be disabled.")
    elif test "$os" = "linux"; then
       enable_vgauth=yes
    else
       enable_vgauth=no
    fi
   ])


#
# Check for openssl, xmlsec1, xml2
#
AC_ARG_WITH([ssl],
   [AS_HELP_STRING([--without-ssl],
     [compiles without openssl support (disables vgauth)])],
   [if test "$withval" = "no"; then enable_vgauth = "no"; fi],
   [with_ssl="yes"])

AC_ARG_WITH([xmlsec1],
   [AS_HELP_STRING([--without-xmlsec1],
     [compiles without xmlsec1 support (disables vgauth)])],
   [if test "$withval" = "no"; then enable_vgauth = "no"; fi],
   [with_xmlsec1="yes"])

AC_ARG_WITH([xml2],
   [AS_HELP_STRING([--without-xml2],
     [compiles without xml2 support (disables vgauth)])],
   [if test "$withval" = "no"; then enable_vgauth = "no"; fi],
   [with_xml2="yes"])

AC_ARG_WITH([tirpc],
   [AS_HELP_STRING([--without-tirpc],
     [compiles with/without libtirpc])],
   [],
   [with_tirpc=auto])

# Make sure we are building with openssl 1.0.1 and above so that
# we use only TLSv1_2.

if test "$enable_vgauth" = "yes" ; then
   AC_VMW_DEFAULT_FLAGS([SSL])
   AC_VMW_CHECK_LIB([ssl],
                    [SSL],
                    [openssl],
                    [],
                    [1.0.1],
                    [],
                    [BIO_new_file],
                    [],
                    [AC_VMW_LIB_ERROR([SSL], [ssl])])

   CPPFLAGS="$CPPFLAGS -DUSE_VGAUTH"
   AC_VMW_DEFAULT_FLAGS([XML2])
   AC_VMW_CHECK_LIB([xml2],
                     [XML2],
                     [],
                     [],
                     [],
                     [],
                     [],
                     [],
                     [AC_VMW_LIB_ERROR([XML2], [xml2])])

# Multiple distros built xmlsec1 with -DXMLSEC_NO_SIZE_T but
# their xmlssec1-config --cflags doesn't properly report it.
# Force it on following the xmlSecSize changelog in below link:
# https://www.aleksey.com/xmlsec/news.html
# The xmlsec configure flag "enable_size_t" is "yes" by default
# since 1.3.0, and both the configure flag and CPP flag has been
# deprecated since 1.3.3, which means the size_t is used by default
# and no need to add CPP flag -DXMLSEC_NO_SIZE_T since 1.3.0.
   AC_VMW_DEFAULT_FLAGS([XMLSEC1])
   AC_VMW_CHECK_LIB([xmlsec1],
                     [XMLSEC1],
                     [],
                     [xmlsec1-config],
                     [],
                     [xmlsec/xmlsec.h],
                     [xmlSecCheckVersionExt],
                     [XMLSEC1_VER=`pkg-config --modversion xmlsec1`
                      xmlsec1_major_version="`echo $XMLSEC1_VER | cut -f1 -d. | cut -f1 -d-`"
                      xmlsec1_minor_version="`echo $XMLSEC1_VER | cut -f2 -d. | cut -f1 -d-`"
                      xmlsec1_micro_version="`echo $XMLSEC1_VER | cut -f3 -d. | cut -f1 -d-`"
                      xmlsec1_version=`printf '%02d%02d%02d' $xmlsec1_major_version $xmlsec1_minor_version $xmlsec1_micro_version`
                      AC_CHECK_SIZEOF(size_t)
                      if test "$xmlsec1_version" -lt 010300 -a "$ac_cv_sizeof_size_t" -ne 4 ; then
                         XMLSEC1_CPPFLAGS="$XMLSEC1_CPPFLAGS -DXMLSEC_NO_SIZE_T"
                      fi
                     ],
                     [AC_VMW_LIB_ERROR([XMLSEC1], [xmlsec1])])
fi

#
# Check for CUnit and disable test code if not available.
#
if test "$enable_tests" = "auto" -o "$enable_tests" = "yes"; then
   AC_VMW_DEFAULT_FLAGS([CUNIT])
   AC_VMW_CHECK_LIB([cunit],
                    [CUNIT],
                    [],
                    [],
                    [],
                    [CUnit/CUnit.h],
                    [CU_initialize_registry],
                    [have_cunit=yes],
                    [have_cunit=no])
   if test "$have_cunit" = "no"; then
      if test "$enable_tests" = "yes"; then
         AC_VMW_LIB_ERROR([CUNIT], [cunit])
      else
         AC_MSG_WARN([CUnit not found, tests won't be compiled.])
      fi
   fi
fi

# If the user explicitly disables X11, then don't try to detect the X-related libraries
if test "$have_x" = "disabled"; then
   enable_multimon="no"
elif test "$have_x" != "yes"; then
   AC_MSG_ERROR(
      [The X11 libraries were not found. Please configure without X11 (using --without-x), or install the libX11 devel package(s).])
else
   CPPFLAGS="$CPPFLAGS $X_CFLAGS"
   COMMON_XLIBS="$X_PRE_LIBS $X_LIBS -lX11 $X_EXTRA_LIBS"

   AC_CHECK_LIB(
      [Xext],
      [XeviQueryVersion],
      [COMMON_XLIBS="-lXext $COMMON_XLIBS"],
      [AC_MSG_ERROR(
         [libXext not found. Please configure without X11 (using --without-x), or install the libXext devel package(s).])],
      [$COMMON_XLIBS])

   AC_CHECK_HEADER(
      [X11/extensions/extutil.h],
      [],
      [AC_MSG_ERROR(
         [X11/extensions/extutil.h header not found - you are probably on Solaris 10 or older. Please copy that header file onto your system manually, or configure without X11 (using --without-x).])],
      [#include <X11/Xlib.h>
       #include <X11/Xproto.h>])

   if test "$enable_multimon" != "no"; then
      AC_CHECK_LIB(
	 [Xinerama],
	 [XineramaQueryVersion],
	 [COMMON_XLIBS="-lXinerama $COMMON_XLIBS"],
	 [AC_MSG_ERROR(
	    [libXinerama not found. Please configure without multimon (using --disable-multimon), configure without X11 (using --without-x), or install the libXinerama devel package(s).])],
	 [$COMMON_XLIBS])
   fi

   AC_CHECK_LIB(
      [Xi],
      [XOpenDevice],
      [COMMON_XLIBS="-lXi $COMMON_XLIBS"],
      [AC_MSG_ERROR(
         [libXi not found. Please configure without X11 (using --without-x), or install the libXi devel package(s).])],
      [$COMMON_XLIBS])

   AC_CHECK_LIB(
      [Xrender],
      [XRenderQueryVersion],
      [COMMON_XLIBS="-lXrender $COMMON_XLIBS"],
      [AC_MSG_ERROR(
         [libXrender not found. Please configure without X11 (using --without-x), or install the libXrender devel package(s).])],
      [$COMMON_XLIBS])

   AC_CHECK_LIB(
      [Xrandr],
      [XRRQueryVersion],
      [COMMON_XLIBS="-lXrandr $COMMON_XLIBS"],
      [AC_MSG_ERROR(
	 [libXrandr not found. Please configure without X11 (using --without-x) or install the libXrandr devel package(s).])],
      [$COMMON_XLIBS])

   AC_CHECK_LIB(
      [Xtst],
      [XTestQueryExtension],
      [COMMON_XLIBS="-lXtst $COMMON_XLIBS"],
      [AC_MSG_ERROR(
	 [libXtst not found. Please configure without X11 (using --without-x) or install the libXtst devel package(s).])],
      [$COMMON_XLIBS])

   AC_CHECK_LIB(
      [SM],
      [SmcOpenConnection],
      [XSM_LIBS="-lSM -lICE" && have_xsm_lib="yes"],
      []
      [-lICE])

   AC_CHECK_HEADERS([X11/SM/SMlib.h X11/ICE/ICElib.h],
                    [have_xsm_header="yes"],
                    [],
                    [])
   if test "$have_xsm_lib" = "yes" -a "$have_xsm_header" = "yes"; then
      have_xsm="yes"
   fi

   AC_CHECK_LIB(
      [Xcomposite],
      [XCompositeQueryExtension],
      [XCOMPOSITE_LIBS="-lXcomposite"],
      [have_xcomposite="no"]
      [])
   AC_CHECK_HEADERS([X11/extensions/Xcomposite.h],
                    [],
                    [have_xcomposite="no"],
                    [])
   if test "$have_xcomposite" != "no"; then
      have_xcomposite="yes"
   fi

   # Check whether we have gtk+ 3.0.
   if test "$with_gtk3" != "no"; then
      # gdk_display_get_default_group (added in gtk+ 2.4.0) is function currently
      # needed by vmware-user.
      AC_VMW_CHECK_LIB([gtk-3],
                       [GTK],
                       [gtk+-3.0],
                       [],
                       [3.0.0],
                       [gtk/gtk.h],
                       [gdk_display_get_default_group],
                       [GTK_CPPFLAGS="$GTK_CPPFLAGS -DGTK3"],
                       [AC_MSG_ERROR([Gtk+ 3.0 library not found or too old. Please configure without Gtk+ support (using --without-gtk3) or install the Gtk+ 3.0 devel package.])])

   # Check whether we have gtk+ 2.0.
   elif test "$with_gtk2" != "no"; then
      AC_VMW_CHECK_LIB([gtk-x11-2.0],
                       [GTK],
                       [gtk+-2.0],
                       [],
                       [2.4.0],
                       [gtk/gtk.h],
                       [gdk_display_get_default_group],
                       [GTK_CPPFLAGS="$GTK_CPPFLAGS -DGTK2"],
                       [AC_MSG_ERROR([Gtk+ 2.0 library not found or too old. Please configure without Gtk+ support (using --without-gtk2) or install the Gtk+ 2.0 devel package.])])
   fi

   #
   # Check for gtkmm 2.4.0 or greater.
   #


   if test "$with_gtkmm" != "no" -o "$with_gtkmm3" != "no"; then
      if test "$with_gtkmm3" != "no"; then
         AC_VMW_CHECK_LIBXX([gtkmm-3.0],
                            [GTKMM],
                            [gtkmm-3.0],
                            [],
                            [3.0.0],
                            [gtkmm.h],
                            [],
                            [GTKMM_CPPFLAGS="$GTKMM_CPPFLAGS -DHAVE_GTKMM"],
                            [AC_MSG_ERROR([gtkmm3 library not found. Please install the libgtkmm devel package(s), or re-configure using --without-gtkmm3.])])

      elif test "$with_gtkmm" != "no"; then
         CUSTOM_GTKMM_CPPFLAGS="$CUSTOM_GTKMM_CPPFLAGS $GTK_CPPFLAGS"
         AC_VMW_CHECK_LIBXX([gtkmm-2.4],
                            [GTKMM],
                            [gtkmm-2.4],
                            [],
                            [2.4.0],
                            [gtkmm.h],
                            [],
                            [GTKMM_CPPFLAGS="$GTKMM_CPPFLAGS -DHAVE_GTKMM"],
                            [AC_MSG_ERROR([gtkmm library not found. Please install the libgtkmm devel package(s), or re-configure using --without-gtkmm.])])
      fi

      #
      # libsigc++-2.0 >= 2.5.1 requires C++11 support
      #
      # Calling AC_VMW_CHECK_LIBXX would duplicate the sigc++
      # flags we would have already got as part of gtkmm call
      # above. This is OK because we are not seeing any issues
      # because of this and calling AC_VMW_CHECK_LIBXX function
      # provides other benefits like picking custom flags.
      #
      # AC_VMW_CHECK_LIBXX sets CPPFLAGS and LIBS variables only,
      # so we have to use SIGCXX_CPPFLAGS instead of SIGCXX_CXXFLAGS.
      #
      AC_VMW_CHECK_LIBXX([sigc++-2.0],
                         [SIGCXX],
                         [sigc++-2.0],
                         [],
                         [2.5.1],
                         [sigc++.h],
                         [],
                         [SIGCXX_CPPFLAGS="$SIGCXX_CPPFLAGS -std=c++11"],
                         [SIGCXX_CPPFLAGS="$SIGCXX_CPPFLAGS"])
   fi
fi # End of checks for X libraries

AC_CHECK_LIB(
   [crypt],
   [crypt],
   [HAVE_CRYPT="yes"],
   [AC_MSG_ERROR(
      [libcrypt not found. Please install the libc/libcrypt devel package(s).])])

AC_CHECK_FUNCS(
   dlopen,
   ,
   [AC_CHECK_LIB(
      dl,
      dlopen,
      [VIX_LIBADD="$VIX_LIBADD -ldl"
       LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -ldl"
       VGAUTH_LIBADD="$VGAUTH_LIBADD -ldl"
       RESOLUTIONSET_LIBADD="$RESOLUTIONSET_LIBADD -ldl"
      ],
      [AC_MSG_ERROR(
         [dlopen was not found, but is required for open-vm-tools to function properly. Please contact your OS vendor.])])])

AC_CHECK_FUNCS([ecvt])
AC_CHECK_FUNCS([fcvt])

AC_CHECK_FUNC([mkdtemp], [have_mkdtemp=yes])

if test "$os" = "freebsd" -a "$osVersion" -ge 600000; then
   AC_CHECK_LIB(
      [thr],
      [pthread_mutex_init],
      [THREAD_LIBS=-lthr],
      [AC_MSG_ERROR(
         [Unable to locate required threading library libthr.])])
else
   AC_CHECK_LIB(
      [pthread],
      [pthread_mutex_init],
      [THREAD_LIBS=-lpthread],
      [AC_MSG_ERROR(
         [libpthread not found. Please install the libc/libpthread devel package(s).])])
fi

# PAM prefix
AC_ARG_WITH([pam-prefix],
	    [AS_HELP_STRING([--with-pam-prefix],
		[specifies where pam files go. Default is $(sysconfdir)])],
	    [PAM_PREFIX="$withval"],
	    [if test "$os" = "freebsd" ; then PAM_PREFIX='$(sysconfdir)'; else PAM_PREFIX='/etc'; fi])

if test "$os" = "linux"; then
   with_dnet_dflt="no"
else
   with_dnet_dflt="yes"
fi

AC_ARG_WITH([dnet],
	    [AS_HELP_STRING([--with-dnet],
	    [specifies whether to build with libdnet - the default is without for Linux, and with it otherwise])],
	    [],
	    [with_dnet="$with_dnet_dflt"])

have_dnet="no"
if test "$with_dnet" = "yes"; then
	# On Debian, dnet is installed via the libdumbnet package. We need to
	# detect this so that our source files include dumbnet.h instead of
	# dnet.h, which is part of a different package altogether.
   AC_VMW_CHECK_LIB([dumbnet],
                    [DNET],
                    [],
                    [dumbnet-config],
                    [],
                    [dumbnet.h],
                    [intf_open],
                    [have_dnet="yes";
                     AC_DEFINE([DNET_IS_DUMBNET], 1, [Define to 1 if substituting libdumbnet for libdnet in Debian.])],
                    [])

   if test $have_dnet = "no"; then
      AC_VMW_CHECK_LIB([dnet],
                       [DNET],
                       [],
                       [dnet-config],
                       [],
                       [dnet.h],
                       [intf_open],
                       [have_dnet="yes"],
                       [])
   fi

   if test $have_dnet = "no"; then
		AC_MSG_ERROR(
		   [dnet-config was not found on your PATH. Please configure without dnet or install dnet - http://libdnet.sourceforge.net])
   fi
fi

if test "$with_dnet" != "yes"; then
AC_DEFINE([NO_DNET], 1, [Define to 1 if building without libdnet.])
fi

AC_ARG_WITH([icu],
            [AS_HELP_STRING([--with-icu],
              [enables support for ICU])],
            [],
            [with_icu=no])

if test "$have_x" = "yes" -o "$with_icu" = "yes"; then
   # AC_CHECK_PROG fails to locate CXX if it's set to an absolute
   # path and it includes arguments, which is often the case when
   # cross compling.  Try to handle this case directly.
   case "$CXX" in
      [/*])
         AS_IF(test -x ${CXX%% *}, [have_cxx=yes], [have_cxx=no])
         ;;
      [*])
         AC_CHECK_PROG([have_cxx], [$CXX], [yes], [no])
         ;;
   esac
   if test "$have_cxx" = "no"; then
      AC_MSG_ERROR([C++ compiler not found. Make sure you have a C++ compiler installed or configure without X11 (using --without-x) and without ICU (using --without-icu).])
   fi
fi

if test "$with_icu" = "yes"; then
   AC_VMW_CHECK_LIBXX([icui18n],
                      [ICU],
                      [icu-i18n],
                      [icu-config],
                      [],
                      [unicode/utf.h],
                      [],
                      [ICU_CPPFLAGS="$ICU_CPPFLAGS -DUSE_ICU"],
                      [AC_MSG_ERROR([ICU library not found. Please configure without ICU (using --without-icu) or install ICU - http://www.icu-project.org])])

   # Check whether we have ICU >= 3.8.
   AC_LANG_PUSH([C++])
   AC_MSG_CHECKING([for ucasemap_utf8ToTitle in ICU])
   ORIGINAL_CPPFLAGS="$CPPFLAGS"
   CPPFLAGS="$CPPFLAGS $ICU_CPPFLAGS"
   AC_TRY_COMPILE([#include <unicode/ucasemap.h>],
                  [
                     (void) &ucasemap_utf8ToTitle;
                     return 0;
                  ],
                  [
                     ICU_CPPFLAGS="$ICU_CPPFLAGS -DHAVE_ICU_38"
                     AC_MSG_RESULT([yes])
                  ],
                  [AC_MSG_RESULT([no])])
   CPPFLAGS="$ORIGINAL_CPPFLAGS"
   AC_LANG_POP([C++])

   # Easier to give all modules the ICU defines/includes...
   CPPFLAGS="$CPPFLAGS $ICU_CPPFLAGS"
else
   CPPFLAGS="$CPPFLAGS -DNO_ICU"
fi


if test "$with_tirpc" != "no"; then
   AC_VMW_CHECK_LIB([libtirpc],
                    [TIRPC],
                    [libtirpc],
                    [],
                    [],
                    [],
                    [],
                    [have_tirpc="yes"],
                    [have_tirpc="no"])
fi

if test "$with_tirpc" != "yes"; then
   AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
      #include <rpc/types.h>
      #include <rpc/xdr.h>

      int main()
      {
         struct xdr_ops xdr;
#if defined(__GLIBC__)
         xdr.x_putint32(NULL, NULL);
#endif
         return 0;
      }
      ]])], [need_tirpc="no"], [need_tirpc="yes"])
   AC_MSG_NOTICE([tirpc is needed: $need_tirpc])
fi

if test "$with_tirpc" = "no"; then
   if test "$need_tirpc" = "yes"; then
      AC_MSG_ERROR([libtirpc is required but it is disabled.])
   fi
   use_tirpc="no"
elif test "$with_tirpc" = "auto"; then
   if test "$need_tirpc" = "yes" -a "$have_tirpc" = "no"; then
      AC_MSG_ERROR([cannot find libtirpc but it is required.])
   fi
   use_tirpc=$need_tirpc
elif test "$with_tirpc" = "yes"; then
   if test "$have_tirpc" = "no"; then
      AC_MSG_ERROR([cannot build with libtirpc because it cannot be found.])
   fi
   use_tirpc="yes"
fi

XDR_LIBS=
XDR_CPPFLAGS=
if test "$use_tirpc" = "yes"; then
   AC_MSG_NOTICE([building with libtirpc])
   XDR_LIBS="$TIRPC_LIBS"
   XDR_CPPFLAGS="-DUSE_TIRPC $TIRPC_CPPFLAGS"
else
   AC_MSG_NOTICE([building without libtirpc])
   # In Solaris, the XDR-related functions are not in libc like in Linux and
   # FreeBSD, so binaries need to be linked to some extra libraries.
   if test "$os" = "solaris"; then
      XDR_LIBS="-lnsl -lrpcsvc"
   fi
fi

AC_PATH_PROG(
   [RPCGEN],
   [rpcgen],
   [not_found])
if test "$RPCGEN" == "not_found"; then
   AC_MSG_ERROR([rpcgen not found.])
fi

###
### Headers
###

AC_CHECK_HEADERS([crypt.h])
AC_CHECK_HEADERS([inttypes.h])
AC_CHECK_HEADERS([stdint.h])
AC_CHECK_HEADERS([stdlib.h])
AC_CHECK_HEADERS([wchar.h])
AC_CHECK_HEADERS([sys/inttypes.h])
AC_CHECK_HEADERS([sys/io.h])
AC_CHECK_HEADERS([sys/param.h]) # Required to make the sys/user.h check work correctly on FreeBSD
AC_CHECK_HEADERS([sys/sysinfo.h])
AC_CHECK_HEADERS([sys/types.h])
AC_CHECK_HEADERS([sys/user.h],
   [],
   [],
   [
#ifdef HAVE_SYS_PARAM_H
#   include <sys/param.h>
#endif
   ])
AC_CHECK_HEADERS([sys/vfs.h])
AC_CHECK_HEADERS([syslimits.h])

# On Freebsd, the unwind.h header file is available in the libunwind
# package, but the necessary functions are only available if __GNU_SOURCE
# is defined to enable "all" GCC extensions.
if test "$os" = "freebsd"; then
   ac_cv_header_unwind_h="no"
fi
AC_CHECK_HEADERS([unwind.h])

AC_CHECK_HEADER(
   [wchar.h],
   [HAVE_WCHAR_H="yes"],
   [HAVE_WCHAR_H="no"])

if test "$os" = "linux"; then
   # Make sure kernel-headers package is installed.
   AC_CHECK_HEADER(
      [linux/unistd.h],
      [],
      [AC_MSG_ERROR(linux/unistd.h is not found. Please install kernel-headers/linux-userspace-headers/linux-libc-dev package.)])
fi

if test "$enable_multimon" != "no"; then
   AC_CHECK_HEADER(
      [X11/extensions/panoramiXproto.h],
      [],
      [AC_MSG_ERROR(
         [panoramiXproto.h not found. Please configure without multimon (using --disable-multimon) or install the libXinerama devel package(s).])],
      [#include <X11/X.h>
       #include <X11/Xmd.h>])
fi

###
### Typdefs, structs, and compiler quarks.
###
AC_HEADER_STDBOOL
AC_C_CONST
AC_TYPE_UID_T
AC_C_INLINE
AC_TYPE_MODE_T
AC_TYPE_OFF_T
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_CHECK_MEMBERS([struct stat.st_rdev])
AC_HEADER_TIME
AC_STRUCT_TM
AC_C_VOLATILE

###
### Specific features and OS/arch flags / actions
###

### General flags / actions
CFLAGS="$CFLAGS -Wall"
CFLAGS="$CFLAGS -Werror"

# -Wno-unknown-pragmas is due to gcc not understanding '#pragma ident'
# in Xlib.h on OpenSolaris.
# Glib2 keep changing mutex APIs so we also need to disable 'deprecated'
# warnings for now (-Wno-deprecated-declarations).
for TEST_CFLAG in -Wno-pointer-sign -Wno-unused-value -fno-strict-aliasing \
    -Wno-unknown-pragmas -Wno-uninitialized -Wno-deprecated-declarations \
    -Wno-unused-const-variable; do
    AC_MSG_CHECKING([for GCC flag $TEST_CFLAG])
    ORIGINAL_CFLAGS="$CFLAGS"
    CFLAGS="$CFLAGS $TEST_CFLAG"
    NEW_CFLAG=""
    AC_TRY_COMPILE(
       [],
       [
	return 0;
       ],
   [NEW_CFLAG=" $TEST_CFLAG"
    AC_MSG_RESULT(yes)],
   [AC_MSG_RESULT(no)])
   CFLAGS="$ORIGINAL_CFLAGS$NEW_CFLAG"
done
CPPFLAGS="$CPPFLAGS -DVMX86_TOOLS"
CPPFLAGS="$CPPFLAGS"

# -fvisibility is used by "core service" plugins, but not required.
ORIGINAL_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -fvisibility=hidden"
AC_MSG_CHECKING([for GCC flag -fvisibility])
AC_TRY_COMPILE([],
               [return 0;],
               [PLUGIN_CPPFLAGS="-fvisibility=hidden -DGCC_EXPLICIT_EXPORT";
                AC_MSG_RESULT(yes)],
               [AC_MSG_RESULT(no)])
CFLAGS="$ORIGINAL_CFLAGS"

# Detect "unused-but-set-variable" gcc warning and disable it.
ORIGINAL_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wno-unused-but-set-variable"
AC_MSG_CHECKING([for GCC flag -Wno-unused-but-set-variable])
AC_TRY_COMPILE([],
               [return 0;],
               [ORIGINAL_CFLAGS="$ORIGINAL_CFLAGS -Wno-unused-but-set-variable";
                AC_MSG_RESULT(yes)],
               [AC_MSG_RESULT(no)])
CFLAGS="$ORIGINAL_CFLAGS"


BUILDDIR="`pwd`"

INCLUDE_DIR="`cd $srcdir; pwd`/lib/include"
BLD_INCLUDE_DIR="$BUILDDIR/lib/include"
CPPFLAGS="-I$INCLUDE_DIR -I$BLD_INCLUDE_DIR $CPPFLAGS"

###
### Documentation.
###

if test "$enable_docs" = "yes"; then
   AC_CHECK_PROG([have_doxygen],
                 [doxygen],
                 [yes],
                 [no])
   if test "$have_doxygen" = "no"; then
      AC_MSG_WARN([doxygen not found; API documentation will not be generated.])
   else
      AC_PATH_PROG([DOT], [dot], [])
      if test "$DOT" = ""; then
         HAVE_DOT=NO
      else
         DOT=`dirname $DOT`
         HAVE_DOT=YES
      fi
      AC_SUBST([DOT])
      AC_SUBST([HAVE_DOT])

      AC_PATH_PROG([MSCGEN],
                   [mscgen],
                   [no])
      if test "$MSCGEN" != "no"; then
         MSCGEN_DIR="`dirname $MSCGEN`"
      else
         MSCGEN_DIR=
      fi
      AC_SUBST([MSCGEN_DIR])
   fi
fi

###
### OS/arch-specific flags / actions
###

MODULES=""
MODULES_OS="$os"
TARGET_OS="$os"
MODULES_DIR=""

if test "$os" = "linux"; then
   MODULES_DIR="$LINUXDIR/kernel/"

   CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=64"
   CPPFLAGS="$CPPFLAGS -D_LARGEFILE64_SOURCE"
   CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE=500"
   CPPFLAGS="$CPPFLAGS -D_BSD_SOURCE"
   CPPFLAGS="$CPPFLAGS -D_SVID_SOURCE"
   CPPFLAGS="$CPPFLAGS -D_DEFAULT_SOURCE"

   LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -lrt"

   # vmxnet is not supported for kernels 3.3.0 and newer
   if test "$osVersion" -lt 303000; then
      MODULES="$MODULES vmxnet"
   fi
   # See if we need vmhgfs module. Starting with 3.10.0 we use FUSE
   if test "$osVersion" -lt 310000; then
      MODULES="$MODULES vmhgfs"
   fi
   # See if we need vmci and vsock modules. Starting with 3.9 they made
   # their way into mainline kernel.
   if test "$osVersion" -lt 309000; then
      MODULES="$MODULES vmci vsock"
   fi
   if test "$osVersion" -lt 300000; then
      MODULES="$MODULES vmblock vmsync"
   fi
   if test "x$enable_resolutionkms" != "xno" && test "x$enable_vmwgfxctrl" != "xno"; then
      PKG_CHECK_MODULES(
         [LIBUDEV],
         libdrm libudev,
         [LIBUDEV_CFLAGS="$LIBUDEV_CFLAGS -DHAVE_LIBUDEV";
         enable_vmwgfxctrl="yes"],
         [AC_MSG_WARN(
            [Missing libdrm or libudev. vmwgfxctrl will be disabled and the resolutionKMS plugin will search for them at run-time.]);
            enable_vmwgfxctrl="no"
            ])
      enable_resolutionkms="yes"
   fi
fi

if test "$os" = "freebsd" || test "$os" = "kfreebsd-gnu"; then
   LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -lkvm"
   MODULES_DIR="/boot/modules"

   if test "$osVersion" -ge 302000; then
      MODULES="$MODULES vmmemctl"
   fi
   if test "$osVersion" -ge 600000; then
      MODULES="$MODULES vmblock"
   fi

   if test "$with_kernel_modules" = "yes"; then
      echo "****************************************************************"
      echo "   You are building FreeBSD kernel modules. Make sure you use   "
      echo "   'make' to build open-vm-tools, and not GNU make ('gmake').   "
      echo "****************************************************************"
   fi
fi

if test "$os" = "solaris"; then
   LIB_IMPERSONATE_CPPFLAGS="$LIB_IMPERSONATE_CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS"
   LIB_USER_CPPFLAGS="$LIB_USER_CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS"

   LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -lsocket"
   LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -lnsl"
   LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -lresolv"
   LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -lrpcsvc"
   LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -lrt"

   # Setup defines to identify the OS version.
   if test "$osVersion" -eq 509000; then
      CPPFLAGS="$CPPFLAGS -DSOL9"
   fi
   if test "$osVersion" -eq 510000; then
      CPPFLAGS="$CPPFLAGS -DSOL10"
   fi
   if test "$osVersion" -eq 511000; then
      CPPFLAGS="$CPPFLAGS -DSOL11"
   fi

   MODULES="$MODULES vmxnet vmmemctl"

   # HGFS and vmblock need Solaris 10 at least.
   if test "$osVersion" -ge 510000; then
      MODULES="$MODULES vmhgfs vmblock"
   fi

   # vmxnet3 is built on Solaris 10 / 11 only if GLDv3 is installed.
   if test "$osVersion" -gt 510000; then
      AC_CHECK_HEADER(
         [sys/mac.h],
         [MODULES="$MODULES vmxnet3"],
         [AC_MSG_WARN([GLDv3 (sys/mac.h) is not installed, vmxnet3 will not be compiled.])])
   fi

   if test "$with_kernel_modules" = "yes"; then
      echo "****************************************************************"
      echo "   You are building Solaris kernel modules. Make sure you use   "
      echo "   GNU make to build open-vm-tools.                             "
      echo "****************************************************************"
   fi
fi

if test "$os" = "linux"; then
    have_udev="yes"
    AC_ARG_WITH([udev-rules-dir],
                [AS_HELP_STRING([--with-udev-rules-dir=DIR],
                    [where to install udev rules])],
                [UDEVRULESDIR="$withval"],
                [
                    UDEVRULESDIR="/lib/udev/rules.d"
                    if test "$PKG_CONFIG" != "not_found"; then
                        udevdir=$($PKG_CONFIG udev --variable=udevdir)
                        if test "x$udevdir" != "x"; then
                            UDEVRULESDIR="$udevdir/rules.d"
                        fi
                    fi
                ])
else
    have_udev="no"
    UDEVRULESDIR=""
fi

if test "x$enable_resolutionkms" = "xauto"; then
   enable_resolutionkms="no"
fi

if test "x$enable_resolutionkms" = "xyes"; then
   CPPFLAGS="$CPPFLAGS -DENABLE_RESOLUTIONKMS"
fi

if test "x$enable_vmwgfxctrl" = "xauto"; then
   enable_vmwgfxctrl="no"
fi

AM_CONDITIONAL(LINUX, test "$os" = "linux")
AM_CONDITIONAL(SOLARIS, test "$os" = "solaris")
AM_CONDITIONAL(FREEBSD, test "$os" = "freebsd" -o "$os" = "kfreebsd-gnu")
AM_CONDITIONAL(FREEBSD_CUSTOM_SYSDIR,
               test \( "$os" = "freebsd" -o "$os" = "kfreebsd-gnu" \) -a -n "$SYSDIR")
AM_CONDITIONAL(ARCH_X32, test "$arch" = "x32")
AM_CONDITIONAL(ARCH_X64, test "$arch" = "x64")
AM_CONDITIONAL(ARCH_ARM64, test "$arch" = "arm64")
AM_CONDITIONAL(HAVE_X11, test "$have_x" = "yes")
AM_CONDITIONAL(HAVE_ICU, test "$with_icu" = "yes")
AM_CONDITIONAL(WITH_KERNEL_MODULES, test "$with_kernel_modules" = "yes")
AM_CONDITIONAL(HAVE_XSM, test "$have_xsm" = "yes")
AM_CONDITIONAL(HAVE_XCOMPOSITE, test "$have_xcomposite" = "yes")
AM_CONDITIONAL(ENABLE_TESTS, test "$have_cunit" = "yes")
AM_CONDITIONAL(WITH_ROOT_PRIVILEGES, test "$with_root_privileges" = "yes")
AM_CONDITIONAL(HAVE_DOXYGEN, test "$have_doxygen" = "yes")
AM_CONDITIONAL(HAVE_FUSE, test "$have_fuse" = "yes" || test "$have_fuse3" = "yes")
AM_CONDITIONAL(HAVE_FUSE3, test "$have_fuse3" = "yes")
AM_CONDITIONAL(HAVE_GNU_LD, test "$with_gnu_ld" = "yes")
AM_CONDITIONAL(HAVE_GTKMM, test "$have_x" = "yes" -a \( "$with_gtkmm" = "yes" -o "$with_gtkmm3" = "yes" \) )
AM_CONDITIONAL(HAVE_PAM, test "$with_pam" = "yes")
AM_CONDITIONAL(USE_SLASH_PROC, test "$os" = "linux")
AM_CONDITIONAL(ENABLE_CONTAINERINFO, test "$enable_containerinfo" = "yes")
AM_CONDITIONAL(ENABLE_DEPLOYPKG, test "$enable_deploypkg" = "yes")
AM_CONDITIONAL(ENABLE_VGAUTH, test "$enable_vgauth" = "yes")
AM_CONDITIONAL(HAVE_VSOCK, test "$os" = "linux")
AM_CONDITIONAL(HAVE_MKDTEMP, test "$have_mkdtemp" = "yes")
AM_CONDITIONAL(HAVE_UDEV, test "$have_udev" = "yes")
AM_CONDITIONAL(ENABLE_RESOLUTIONKMS, test "x$enable_resolutionkms" = "xyes")
AM_CONDITIONAL(ENABLE_VMWGFXCTRL, test "x$enable_vmwgfxctrl" = "xyes")
AM_CONDITIONAL(VGAUTH_USE_CXX, test "$with_icu" = "yes")
AM_CONDITIONAL(ENABLE_LIBAPPMONITOR, test "$enable_libappmonitor" = "yes")
AM_CONDITIONAL(ENABLE_SDMP, test "$enable_servicediscovery" = "yes")
AM_CONDITIONAL(ENABLE_SALTMINION, test "$enable_saltminion" = "yes" -a \( "$arch" = "x64" \) )

if test "$have_xsm" != "yes"; then
AC_DEFINE([NO_XSM], 1, [])
fi

if test "$have_xcomposite" != "yes"; then
   AC_DEFINE([NO_XCOMPOSITE])
fi

### Feature-specific flags / actions
# Combine where possible

# If control reaches this point and multimon is still enabled, then we know
# all of the tests for required components have passed and it's safe to allow
# multimon. Otherwise, it should be disabled.
if test "$enable_multimon" = "no"; then
   # XXX: For consistency, change this to ENABLE_MULTIMON. This will require
   # some additional code cleanup.
   AC_DEFINE([NO_MULTIMON], 1, [Define to 1 if building without multimon support.])
fi

LIB_AUTH_CPPFLAGS="$LIB_AUTH_CPPFLAGS $PAM_CPPFLAGS"
if test "$HAVE_CRYPT" = "yes"; then
   LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD -lcrypt"
   VIX_LIBADD="$VIX_LIBADD -lcrypt"
fi


LIBVMTOOLS_LIBADD="$LIBVMTOOLS_LIBADD $THREAD_LIBS"
VIX_LIBADD="$VIX_LIBADD $THREAD_LIBS"

### Core Services definitions.

HGFS_LIBS="$BUILDDIR/libhgfs/libhgfs.la"

VMTOOLS_LIBS="$BUILDDIR/libvmtools/libvmtools.la $GLIB2_LIBS"
VMTOOLS_CPPFLAGS="-DVMTOOLS_USE_GLIB $GLIB2_CPPFLAGS"

PLUGIN_CPPFLAGS="$VMTOOLS_CPPFLAGS $PLUGIN_CPPFLAGS"
PLUGIN_LDFLAGS="-Wl,-z,defs -Wl,-lc -Wl,--as-needed -shared -module -avoid-version"

# Installation directories for core services plugins.
TEST_PLUGIN_INSTALLDIR=$datadir/open-vm-tools/tests
COMMON_PLUGIN_INSTALLDIR=$libdir/open-vm-tools/plugins/common
VMSVC_PLUGIN_INSTALLDIR=$libdir/open-vm-tools/plugins/vmsvc
VMUSR_PLUGIN_INSTALLDIR=$libdir/open-vm-tools/plugins/vmusr


# General definitions
INSTVMSG='$(SHELL) $(top_srcdir)/scripts/build/instvmsg.sh'
RPCGEN_WRAPPER='$(SHELL) $(top_builddir)/scripts/build/rpcgen_wrapper.sh'

### General substs

AC_SUBST([HGFS_LIBS])
AC_SUBST([TOOLS_VERSION])
AC_SUBST([TARGET_OS])
AC_SUBST([KERNEL_RELEASE])
AC_SUBST([LINUXINCLUDE])
AC_SUBST([MODULES_OS])
AC_SUBST([MODULES_DIR])
AC_SUBST([MODULES])
AC_SUBST([COMMON_XLIBS])
AC_SUBST([XSM_LIBS])
AC_SUBST([XCOMPOSITE_LIBS])
AC_SUBST([PAM_PREFIX])
AC_SUBST([PLUGIN_CPPFLAGS])
AC_SUBST([PLUGIN_LDFLAGS])
AC_SUBST([VMTOOLS_CPPFLAGS])
AC_SUBST([VMTOOLS_LIBS])
AC_SUBST([THREAD_LIBS])
AC_SUBST([RPCGENFLAGS])
AC_SUBST([XDR_LIBS])
AC_SUBST([XDR_CPPFLAGS])
AC_SUBST([TEST_PLUGIN_INSTALLDIR])
AC_SUBST([COMMON_PLUGIN_INSTALLDIR])
AC_SUBST([VMSVC_PLUGIN_INSTALLDIR])
AC_SUBST([VMUSR_PLUGIN_INSTALLDIR])
if test "$os" = "freebsd" -a -n "$SYSDIR"; then
   # If SYSDIR is not defined, AC_SUBST expands to nothing, so we need something
   # inside this block.
   true
   AC_SUBST([SYSDIR])
fi
AC_SUBST([INSTVMSG])
AC_SUBST([RPCGEN_WRAPPER])

### Lib substs

AC_SUBST([LIB_AUTH_CPPFLAGS])
AC_SUBST([LIB_IMPERSONATE_CPPFLAGS])
AC_SUBST([LIB_USER_CPPFLAGS])
AC_SUBST([LIBVMTOOLS_LIBADD])
AC_SUBST([RESOLUTIONSET_LIBADD])

### Program substs

AC_SUBST([VIX_LIBADD])
AC_SUBST([VGAUTH_LIBADD])

AC_SUBST([UDEVRULESDIR])

###
### Create the Makefiles
###
AC_CONFIG_FILES([                      \
   Makefile                            \
   lib/Makefile                        \
   lib/auth/Makefile                   \
   lib/backdoor/Makefile               \
   lib/asyncsocket/Makefile            \
   lib/sslDirect/Makefile              \
   lib/pollGtk/Makefile                \
   lib/poll/Makefile                   \
   lib/dataMap/Makefile                \
   lib/hashMap/Makefile                \
   lib/dict/Makefile                   \
   lib/dynxdr/Makefile                 \
   lib/err/Makefile                    \
   lib/file/Makefile                   \
   lib/foundryMsg/Makefile             \
   lib/glibUtils/Makefile              \
   lib/globalConfig/Makefile           \
   lib/guestApp/Makefile               \
   lib/guestRpc/Makefile               \
   lib/guestStoreClientHelper/Makefile \
   lib/hgfs/Makefile                   \
   lib/hgfsBd/Makefile                 \
   lib/hgfsHelper/Makefile             \
   lib/hgfsServer/Makefile             \
   lib/hgfsServerManagerGuest/Makefile \
   lib/hgfsServerPolicyGuest/Makefile  \
   lib/hgfsUri/Makefile                \
   lib/impersonate/Makefile            \
   lib/lock/Makefile                   \
   lib/message/Makefile                \
   lib/misc/Makefile                   \
   lib/netUtil/Makefile                \
   lib/nicInfo/Makefile                \
   lib/panic/Makefile                  \
   lib/panicDefault/Makefile           \
   lib/procMgr/Makefile                \
   lib/rpcChannel/Makefile             \
   lib/rpcIn/Makefile                  \
   lib/rpcOut/Makefile                 \
   lib/rpcVmx/Makefile                 \
   lib/slashProc/Makefile              \
   lib/string/Makefile                 \
   lib/jsmn/Makefile                   \
   lib/stubs/Makefile                  \
   lib/syncDriver/Makefile             \
   lib/system/Makefile                 \
   lib/unicode/Makefile                \
   lib/user/Makefile                   \
   lib/vmCheck/Makefile                \
   lib/vmSignal/Makefile               \
   lib/wiper/Makefile                  \
   lib/xdg/Makefile                    \
   services/Makefile                   \
   services/vmtoolsd/Makefile          \
   services/plugins/Makefile           \
   services/plugins/gdp/Makefile       \
   services/plugins/appInfo/Makefile   \
   services/plugins/componentMgr/Makefile  \
   services/plugins/containerInfo/Makefile   \
   services/plugins/serviceDiscovery/Makefile   \
   services/plugins/desktopEvents/Makefile \
   services/plugins/dndcp/Makefile     \
   services/plugins/guestInfo/Makefile \
   services/plugins/guestStore/Makefile \
   services/plugins/hgfsServer/Makefile \
   services/plugins/powerOps/Makefile  \
   services/plugins/resolutionSet/Makefile \
   services/plugins/resolutionKMS/Makefile \
   services/plugins/timeSync/Makefile  \
   services/plugins/vix/Makefile       \
   services/plugins/vmbackup/Makefile  \
   services/plugins/deployPkg/Makefile  \
   vmware-user-suid-wrapper/Makefile   \
   toolbox/Makefile                    \
   hgfsclient/Makefile                 \
   checkvm/Makefile                    \
   vmwgfxctrl/Makefile                 \
   rpctool/Makefile                    \
   vgauth/Makefile                     \
   vgauth/lib/Makefile                 \
   vgauthImport/Makefile               \
   namespacetool/Makefile              \
   vgauth/cli/Makefile                 \
   vgauth/test/Makefile                 \
   vgauth/service/Makefile             \
   libguestlib/Makefile                \
   libguestlib/vmguestlib.pc           \
   libDeployPkg/Makefile               \
   libDeployPkg/libDeployPkg.pc        \
   libhgfs/Makefile                    \
   libguestStoreClient/Makefile        \
   libvmtools/Makefile                 \
   xferlogs/Makefile                   \
   modules/Makefile                    \
   vmblock-fuse/Makefile               \
   vmhgfs-fuse/Makefile                \
   vmblockmounter/Makefile             \
   tests/Makefile                      \
   tests/vmrpcdbg/Makefile             \
   tests/testDebug/Makefile            \
   tests/testPlugin/Makefile           \
   tests/testVmblock/Makefile          \
   docs/Makefile                       \
   docs/api/Makefile                   \
   scripts/Makefile                    \
   scripts/build/rpcgen_wrapper.sh     \
   udev/Makefile                       \
   libappmonitor/Makefile              \
   libappmonitor/appmonitor.pc         \
])









###
### Output
###
AC_OUTPUT
  07070100000011000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002800000000open-vm-tools-12.5.2/open-vm-tools/docs   07070100000012000081A400000000000000000000000168225505000003B5000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/docs/Makefile.am   ################################################################################
### Copyright (c) 2009-2016, 2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

if HAVE_DOXYGEN
SUBDIRS += api

endif
   07070100000013000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/docs/api   07070100000014000081A40000000000000000000000016822550500000AF9000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/docs/api/Makefile.am   ################################################################################
### Copyright (c) 2009-2016,2022-2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

DOXYGEN_INPUT =
DOXYGEN_INPUT += $(top_srcdir)/docs/api
DOXYGEN_INPUT += $(top_srcdir)/docs/api/services
DOXYGEN_INPUT += $(top_srcdir)/lib/include/vmware/tools
DOXYGEN_INPUT += $(top_srcdir)/libvmtools
DOXYGEN_INPUT += $(top_srcdir)/tests/vmrpcdbg


DOXYGEN_CPP_PREDEFINED =
DOXYGEN_CPP_PREDEFINED += G_LOCK_DEFINE_STATIC(x)
DOXYGEN_CPP_PREDEFINED += G_LOG_DOMAIN=

DOXYGEN_CPP_EXPAND =

DOXYGEN_EXTRA_PATTERNS =
DOXYGEN_EXTRA_PATTERNS += *.c
DOXYGEN_EXTRA_PATTERNS += *.doxy
DOXYGEN_EXTRA_PATTERNS += *.h
DOXYGEN_EXTRA_PATTERNS += *.txt

# This rule has no dependencies, so it's executed every time. Not optimal,
# but generation of the docs is pretty quick, and having the right dependencies
# here is very non-trivial.
build/index.html:
	mkdir -p build
	sed \
	   -e 's,##{BUILD_DIR}##,$(top_builddir),'                \
	   -e 's,##{BUILD_OUTPUT}##,build,'                       \
	   -e 's,##{CLIENT_ROOT}##,$(top_srcdir),'                \
	   -e 's,##{CPP_EXPAND}##,$(DOXYGEN_CPP_EXPAND),'         \
	   -e 's,##{CPP_PREDEFINED}##,$(DOXYGEN_CPP_PREDEFINED),' \
	   -e 's,##{DOT}##,@DOT@,'                                \
	   -e 's,##{FILE_PATTERNS}##,$(DOXYGEN_EXTRA_PATTERNS),'  \
	   -e 's,##{HAVE_DOT}##,@HAVE_DOT@,'                      \
	   -e 's,##{INCLUDE_PATHS}##,$(top_srcdir)/lib/include,'  \
	   -e 's,##{INPUT_PATHS}##,$(DOXYGEN_INPUT),'             \
	   -e 's,##{MSCGEN_DIR}##,@MSCGEN_DIR@,'                  \
	   -e 's,##{PERL}##,,'                                    \
	   -e 's,##{PROJECT_NAME}##,@PACKAGE_STRING@,'            \
	   -e 's,##{PROJECT_VERSION}##,@TOOLS_VERSION@,'          \
	   -e 's,##{WARNING_LOG}##,warnings.log,'                 \
	   $(top_srcdir)/docs/api/doxygen.conf > doxygen.parsed.conf
	doxygen doxygen.parsed.conf > /dev/null

all-local: build/index.html

install-data-local:
	mkdir -p $(DESTDIR)$(docdir)/api
	cp -r build/* $(DESTDIR)$(docdir)/api
   07070100000015000081A4000000000000000000000001682255050000126E000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/docs/api/doxygen.conf  ###
### profile-api.conf - "Public API" Doxygen profile.  Includes only non-static
###                    functions and non-internal documentation.
###

#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------

PROJECT_NAME           = "##{PROJECT_NAME}##"
PROJECT_NUMBER         = "##{PROJECT_VERSION}##"
OUTPUT_DIRECTORY       = ##{BUILD_OUTPUT}##
TAB_SIZE               = 3
OPTIMIZE_OUTPUT_FOR_C  = YES
FULL_PATH_NAMES        = YES
STRIP_FROM_PATH        = ##{CLIENT_ROOT}##

#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------

EXTRACT_LOCAL_CLASSES  = NO


#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------

WARNINGS               = YES
WARN_IF_UNDOCUMENTED   = YES
WARN_IF_DOC_ERROR      = YES
WARN_NO_PARAMDOC       = YES
WARN_FORMAT            = "$file:$line: $text"
WARN_LOGFILE           = ##{WARNING_LOG}##


#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------

INPUT                  = ##{INPUT_PATHS}##
FILE_PATTERNS          = ##{FILE_PATTERNS}##


#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------

GENERATE_HTML          = YES
HTML_OUTPUT            = html
HTML_FILE_EXTENSION    = .html


#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------

GENERATE_LATEX         = NO


#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------

GENERATE_RTF           = NO


#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------

GENERATE_MAN           = NO


#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------

GENERATE_XML           = NO


#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------

MACRO_EXPANSION        = YES
EXPAND_ONLY_PREDEF     = YES
INCLUDE_PATH           = ##{INCLUDE_PATHS}##

PREDEFINED             = ##{CPP_PREDEFINED}##
EXPAND_AS_DEFINED      = ##{CPP_EXPAND}##

#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------

TAGFILES               =
GENERATE_TAGFILE       =
ALLEXTERNALS           = NO
EXTERNAL_GROUPS        = YES
PERL_PATH              = ##{PERL}##

#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS         = YES
MSCGEN_PATH            = ##{MSCGEN_DIR}##
HIDE_UNDOC_RELATIONS   = YES
HAVE_DOT               = ##{HAVE_DOT}##
CLASS_GRAPH            = YES
COLLABORATION_GRAPH    = YES
GROUP_GRAPHS           = YES
UML_LOOK               = NO
TEMPLATE_RELATIONS     = NO
INCLUDE_GRAPH          = YES
INCLUDED_BY_GRAPH      = YES
CALL_GRAPH             = NO
CALLER_GRAPH           = NO
GRAPHICAL_HIERARCHY    = YES
DIRECTORY_GRAPH        = YES
DOT_IMAGE_FORMAT       = png
DOT_PATH               = ##{DOT}##
DOTFILE_DIRS           =
DOT_GRAPH_MAX_NODES    = 50
MAX_DOT_GRAPH_DEPTH    = 0
DOT_TRANSPARENT        = NO
DOT_MULTI_TARGETS      = NO
GENERATE_LEGEND        = YES
DOT_CLEANUP            = NO

#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE           = NO

  07070100000016000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/docs/api/services  07070100000017000081A40000000000000000000000016822550500000BF1000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/docs/api/services/debug.txt    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/**

@defgroup vmtools_debug Debugging Tools Plugins

@brief Defines functions and interfaces for debugging Tools plugins.

This module provides functions for writing "debug plugins" - plugins which
provide functionality for driving the Tools Services when they're running in
"debug mode". Debug plugins are dynamically loaded by providing specific
command-line options to the Tools service process.

@section vmtools_debug_how How debug plugins work

Currently, there's only support for GuestRPC-based debug plugins.

The debug library implements an RPC channel that can be driven by a debug
plugin, according to information provided by the plugin at load time.

The debug plugin can provide two types of information:

   - RPCs to be sent to the application: these are input to the application,
     and can be tied to a validation function to make sure the response from
     the application is correct.

   - Validation functions for "outgoing" RPCs: these are functions called in
     response to RPCs initiated by the applications; they can be mapped to
     specific RPC command strings, to make writing the plugin easier.

For more details, check the RpcDebugPlugin data structure.

Plugins that depend on events that are not only responses to RPC messages (such
as timer-based outgoing RPCs) should make sure they tell the library that it
should not stop running by incrementing its ref count (see RpcDebug_IncRef()).
When the test is complete, the code can then call RpcDebug_DecRef() and, when
the ref count reaches zero, the main application loop will be stopped.

*/

   07070100000018000081A40000000000000000000000016822550500000649000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/docs/api/services/guestrpc.txt /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/**

@defgroup vmtools_guestrpc GuestRPC functions

@brief Functions related to reading and writing data on a GuestRPC channel.

*/

   07070100000019000081A40000000000000000000000016822550500000801000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/docs/api/services/main.doxy    /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/**
@mainpage VMware Tools Core API

The Tools Core API provides functionality for extending Tools by building
plugins, and loading them as part of the existing Tools services or by creating
new plugin containers.

@section tc_main_plugins   Basic Plugin Functionality

   - @ref vmtools_plugins
   - @ref vmtools_guestrpc
   - @ref vmtools_vmcf

@section tc_main_utils     Miscelaneous Utilities

   - @ref vmtools_utils
   - @ref vmtools_logging
   - @ref vmtools_threads
   - @ref vmtools_i18n

@section tc_main_debug     Debugging Support

   - @ref vmtools_debug

*/

   0707010000001A000081A40000000000000000000000016822550500000860000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/docs/api/services/plugins.txt  /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/**

@defgroup vmtools_plugins VMware Tools Plugins

@brief Defines the API used by the Tools Services for dynamically loading plugins.

Plugins are dynamically loaded into the Tools service process, and may
provide applications that hook into the functionality provided by the
service, such as the glib main loop and the GuestRPC channel.

Plugins that want to remain loaded by the service should always return
registration data when their registration function (ToolsPluginOnLoad())
is called. The registration data, defined by the ToolsPluginData data structure,
provides information about what events the plugin is interested in (for example,
incoming RPCs).

*/

0707010000001B000081A40000000000000000000000016822550500000000000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/docs/api/services/utils.txt    0707010000001C000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/hgfsclient 0707010000001D000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/hgfsclient/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   0707010000001E000081A40000000000000000000000016822550500000637000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/hgfsclient/Makefile.am ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-hgfsclient

vmware_hgfsclient_LDADD =
vmware_hgfsclient_LDADD += ../lib/hgfsBd/libHgfsBd.la
vmware_hgfsclient_LDADD += @VMTOOLS_LIBS@
vmware_hgfsclient_LDADD += @HGFS_LIBS@

vmware_hgfsclient_CPPFLAGS =
vmware_hgfsclient_CPPFLAGS += @VMTOOLS_CPPFLAGS@

vmware_hgfsclient_SOURCES =
vmware_hgfsclient_SOURCES += hgfsclient.c

if HAVE_ICU
   vmware_hgfsclient_LDADD += @ICU_LIBS@
   vmware_hgfsclient_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
                            $(LIBTOOLFLAGS) --mode=link $(CXX) \
                            $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
                            $(LDFLAGS) -o $@
else
   vmware_hgfsclient_LINK = $(LINK)
endif
 0707010000001F000081A40000000000000000000000016822550500002B9C000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/hgfsclient/hgfsclient.c    /*********************************************************
 * Copyright (c) 2006-2019,2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsclient.c --
 *
 *      Userspace HGFS client. Will one day be as full featured as an HGFS
 *      filesystem driver.
 *
 */

#define G_LOG_DOMAIN "hgfsClient"
#include "vmware.h"
#include "guestApp.h"
#include "vmcheck.h"
#include "hgfsEscape.h"
#include "hgfs.h"
#include "hgfsBd.h"
#include "hgfsProto.h"
#include "conf.h"
#include "str.h"
#include "vmware/tools/log.h"
#include "vmware/tools/utils.h"
#ifdef _WIN32
#include "vmware/tools/win32util.h"
#endif

#include "hgfsclient_version.h"
#include "vm_version.h"
#include "embed_version.h"
VM_EMBED_VERSION(HGFSCLIENT_VERSION_STRING);

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>

RpcOut *gChannel = NULL;
char *gPacketBuffer = NULL;

static Bool HgfsClient_Open(HgfsHandle *rootHandle);
static HgfsFileName *HgfsClient_Read(HgfsHandle rootHandle,
                                     int offset);
static Bool HgfsClient_Close(HgfsHandle rootHandle);
static Bool HgfsClient_PrintShares(void);
static Bool HgfsClient_Init(void);
static Bool HgfsClient_Cleanup(void);


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsClient_Open --
 *
 *    Open the root directory on the host.
 *
 * Results:
 *    TRUE on success. FALSE otherwise. When TRUE, the root directory handle
 *    is returned as an argument.
 *
 * Side effects:
 *    The host has cached an open search for us.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsClient_Open(HgfsHandle *rootHandle) // OUT: Handle to root directory
{
   Bool success = FALSE;
   HgfsRequestSearchOpen *searchOpenReq;
   HgfsReplySearchOpen *searchOpenRep = NULL;
   int err;
   char const *replyPacket;
   size_t packetSize;

   /* Create a SearchOpen and send it. */
   searchOpenReq = (HgfsRequestSearchOpen *)gPacketBuffer;
   memset(searchOpenReq, 0, sizeof *searchOpenReq);
   searchOpenReq->header.id = 0;
   searchOpenReq->header.op = HGFS_OP_SEARCH_OPEN;
   searchOpenReq->dirName.length = 0;
   searchOpenReq->dirName.name[0] = 0;
   packetSize = sizeof *searchOpenReq;

   err = HgfsBd_Dispatch(gChannel, (char *)searchOpenReq, 
                         &packetSize, &replyPacket);
   if (err != 0) {
      Warning("Failed to send search open request.\n");
      goto out;
   }
   
   /* replyPacket has our search handle. */
   searchOpenRep = (HgfsReplySearchOpen *)replyPacket;
   if (searchOpenRep->header.status != HGFS_STATUS_SUCCESS) {
      Warning("Error in opening root directory.\n");
      goto out;
   }
   success = TRUE;

  out:
   /* We got the root handle. */
   if (success) {
      *rootHandle = searchOpenRep->search;
   }
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsClient_Read --
 *
 *    Read a share name from the host.
 *
 * Results:
 *    Pointer into the packet buffer where the caller can find the
 *    HgfsFileName struct. Since this is a pointer into the global
 *    packet buffer, the caller should not free it.
 *
 *    NULL if there was an error.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static HgfsFileName *
HgfsClient_Read(HgfsHandle rootHandle, // IN: Handle to root directory
                int offset)            // IN: Offset of dirent to read
{
   HgfsFileName *shareName = NULL;
   HgfsRequestSearchRead *searchReadReq;
   HgfsReplySearchRead *searchReadRep;
   int err;
   char const *replyPacket;
   size_t packetSize;

   /* Create searchRead and send it. */
   searchReadReq = (HgfsRequestSearchRead *)gPacketBuffer;
   memset(searchReadReq, 0, sizeof *searchReadReq);
   searchReadReq->header.id = 0;
   searchReadReq->header.op = HGFS_OP_SEARCH_READ;
   searchReadReq->search = rootHandle;
   searchReadReq->offset = offset;
   packetSize = sizeof *searchReadReq;

   err = HgfsBd_Dispatch(gChannel, (char *)searchReadReq, 
                         &packetSize, &replyPacket);
   if (err != 0) {
      Warning("Failed to send search read request.\n");
      goto out;
   }

   /* replyPacket has our share name. */
   searchReadRep = (HgfsReplySearchRead *)replyPacket;
   if (searchReadRep->header.status != HGFS_STATUS_SUCCESS) {
      Warning("Error in getting share name.\n");
      goto out;
   }

   /* We got the share name. */
   shareName = &searchReadRep->fileName;

  out:
   return shareName;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsClient_Close --
 *
 *    Closes the root directory on the host.
 *
 * Results:
 *    TRUE on success, FALSE otherwise.
 *
 * Side effects:
 *    Host releases state on our opened search.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsClient_Close(HgfsHandle rootHandle) // IN: Handle to root directory
{
   HgfsRequestSearchClose *searchCloseReq;
   HgfsReplySearchClose *searchCloseRep;
   int err;
   char const *replyPacket;
   size_t packetSize;

   /* Create a SearchClose and send it. */
   searchCloseReq = (HgfsRequestSearchClose *)gPacketBuffer;
   memset(searchCloseReq, 0, sizeof *searchCloseReq);
   searchCloseReq->header.id = 0;
   searchCloseReq->header.op = HGFS_OP_SEARCH_CLOSE;
   searchCloseReq->search = rootHandle;
   packetSize = sizeof *searchCloseReq;

   err = HgfsBd_Dispatch(gChannel, (char *)searchCloseReq, 
                         &packetSize, &replyPacket);
   if (err != 0) {
      Warning("Failed to send search close request.\n");
      return FALSE;
   }

   /* replyPacket has success/failure. */
   searchCloseRep = (HgfsReplySearchClose *)replyPacket;
   if (searchCloseRep->header.status != HGFS_STATUS_SUCCESS) {
      Warning("Error closing root directory.\n");
      return FALSE;
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsClient_PrintShares --
 *
 *    List all the shares available on the host.
 *
 * Results:
 *    TRUE if successful, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsClient_PrintShares(void)
{
   Bool success = FALSE;
   int offset = 0;
   char escapedName[PATH_MAX + 1];
   HgfsHandle rootHandle;

   if (!HgfsClient_Open(&rootHandle)) {
      return success;
   }

   while (TRUE) {
      HgfsFileName *fileName = HgfsClient_Read(rootHandle, offset++);

      if (fileName == NULL) {
         break;
      }

      /* Are we done? */
      if (fileName->length == 0) {
         success = TRUE;
         break;
      }

      /*
       * Escape this filename. If we get back a negative result, it means that
       * the escaped filename is too big, so skip this share.
       */
      if (HgfsEscape_Do(fileName->name, fileName->length,
                           sizeof escapedName, escapedName) < 0) {
        continue;
      }

      /* Skip "." and ".." which can be returned. */
      if (strcmp(".", escapedName) == 0 ||
          strcmp("..", escapedName) == 0) {
         continue;
      }
      printf("%s\n", escapedName);

   }

   if (!HgfsClient_Close(rootHandle)) {
      success = FALSE;
   }
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsClient_Init --
 *
 *    Do some initialization "stuff".
 *
 * Results:
 *    TRUE if initialization succeeded, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsClient_Init(void)
{
   Bool success = FALSE;
   GKeyFile *conf = NULL;

   VMTools_LoadConfig(NULL, G_KEY_FILE_NONE, &conf, NULL);
   VMTools_ConfigLogging("hgfsclient", conf, FALSE, FALSE);
   if (conf != NULL) {
      g_key_file_free(conf);
      conf = NULL;
   }

   if (!VmCheck_IsVirtualWorld()) {
      Warning("This application must be run in a Virtual Machine.\n");
      goto out;
   }

   /* Setup an HGFS channel and packet buffer. */   
   gChannel = HgfsBd_GetChannel();
   if (gChannel == NULL) {
      Warning("Failed to create RPC channel\n");
      goto out;
   }
   gPacketBuffer = HgfsBd_GetBuf();
   if (gPacketBuffer == NULL) {
      Warning("Failed to create packet buffer\n");
      goto out;
   }

   /* Find out if HGFS is enabled in the VMX. */
   if (!HgfsBd_Enabled(gChannel, gPacketBuffer)) {
      Warning("HGFS is disabled in the host\n");
      goto out;
   }
   success = TRUE;

  out:
   if (!success) {
      HgfsClient_Cleanup();
   }
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsClient_Cleanup --
 *
 *    Do some cleanup crap.
 *
 * Results:
 *    TRUE if cleanup succeeded, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsClient_Cleanup(void)
{
   Bool success = TRUE;

   if (gPacketBuffer != NULL) {
      HgfsBd_PutBuf(gPacketBuffer);
   }
   if (gChannel != NULL) {
      if (!HgfsBd_CloseChannel(gChannel)) {
         Warning("Failed to close RPC channel\n");
         success = FALSE;
      }
   }
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * main --
 *
 *    Main entry point. Calls into the host's HGFS server and prints out
 *    a list of the available shares.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

int
main(int argc,          // IN
     char *argv[])      // IN
{
#ifdef _WIN32
   WinUtil_EnableSafePathSearching(TRUE);
#   if defined(VMX86_RELEASE)
   WinUtil_VerifyExePathW();
#   endif
#endif
   if (argc == 2 &&
       (!strncmp(argv[1], "-h", 2) ||
        !strncmp(argv[1], "--help", 6))) {
      fprintf(stderr, "hgfsclient: lists any shared folders.\n");
      return 0;
   }

   if (!HgfsClient_Init()) {
      return EXIT_FAILURE;
   }
   if (!HgfsClient_PrintShares()) {
      return EXIT_FAILURE;
   }
   if (!HgfsClient_Cleanup()) {
      return EXIT_FAILURE;
   }
   return EXIT_SUCCESS;
}

07070100000020000081A4000000000000000000000001682255050000062D000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/hgfsclient/hgfsclient_version.h    /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsclient_version.h --
 *
 * Version definitions for the HGFS userspace client application.
 */

#ifndef _HGFSCLIENT_VERSION_H_
#define _HGFSCLIENT_VERSION_H_

/*
 * This component's version is coupled with Tools versioning. The effect
 * is that the version increments with each build, and with each Tools
 * version bump. If and when it becomes necessary to version the component
 * manually, make sure that the version is bumped any time the component or
 * its dependencies are changed.
 */
#include "vm_tools_version.h"
#define HGFSCLIENT_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define HGFSCLIENT_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _HGFSCLIENT_VERSION_H_ */
   07070100000021000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002700000000open-vm-tools-12.5.2/open-vm-tools/lib    07070100000022000081A40000000000000000000000016822550500006739000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000023000081A4000000000000000000000001682255050000081E000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/Makefile.am    ################################################################################
### Copyright (c) 2007-2024 Broadcom. All Rights Reserved.
### Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc.
### and/or its subsidiaries.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =
SUBDIRS += guestRpc
SUBDIRS += auth
SUBDIRS += backdoor
if HAVE_VSOCK
SUBDIRS += asyncsocket
endif
SUBDIRS += sslDirect
SUBDIRS += pollGtk
SUBDIRS += poll
SUBDIRS += dataMap
SUBDIRS += hashMap
SUBDIRS += dict
SUBDIRS += dynxdr
SUBDIRS += err
SUBDIRS += file
SUBDIRS += foundryMsg
SUBDIRS += glibUtils
SUBDIRS += guestApp
if LINUX
   SUBDIRS += guestStoreClientHelper
   SUBDIRS += globalConfig
endif
SUBDIRS += hgfs
SUBDIRS += hgfsBd
SUBDIRS += hgfsHelper
SUBDIRS += hgfsServer
SUBDIRS += hgfsServerManagerGuest
SUBDIRS += hgfsServerPolicyGuest
if HAVE_GTKMM
SUBDIRS += hgfsUri
endif
SUBDIRS += impersonate
SUBDIRS += lock
SUBDIRS += message
SUBDIRS += misc
SUBDIRS += netUtil
SUBDIRS += nicInfo
SUBDIRS += panic
SUBDIRS += panicDefault
SUBDIRS += procMgr
SUBDIRS += rpcChannel
SUBDIRS += rpcIn
SUBDIRS += rpcOut
SUBDIRS += rpcVmx
if USE_SLASH_PROC
   SUBDIRS += slashProc
endif
SUBDIRS += string
SUBDIRS += jsmn
SUBDIRS += stubs
SUBDIRS += syncDriver
SUBDIRS += system
SUBDIRS += unicode
SUBDIRS += user
SUBDIRS += vmCheck
SUBDIRS += vmSignal
SUBDIRS += wiper
SUBDIRS += xdg


  07070100000024000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/asyncsocket    07070100000025000081A4000000000000000000000001682255050000047B000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/asyncsocket/Makefile.am    ################################################################################
### Copyright (C) 2013-2017 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libAsyncSocket.la

libAsyncSocket_la_SOURCES =
libAsyncSocket_la_SOURCES += asyncsocket.c
libAsyncSocket_la_SOURCES += asyncSocketBase.c
libAsyncSocket_la_SOURCES += asyncSocketInterface.c

AM_CFLAGS =
AM_CFLAGS += -DUSE_SSL_DIRECT
 07070100000026000081A40000000000000000000000016822550500005AE4000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/asyncsocket/asyncSocketBase.c  /*********************************************************
 * Copyright (C) 2016-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * asyncSocketBase.c --
 *
 *      This exposes the public functions of the AsyncSocket library.
 *      This file itself just contains stubs which call the function
 *      pointers in the socket's virtual table.
 *
 */

#include "vmware.h"
#include "asyncsocket.h"
#include "asyncSocketBase.h"
#include "msg.h"
#include "log.h"

#define LOGLEVEL_MODULE asyncsocket
#include "loglevel_user.h"

/*
 * A version of ASOCKLOG() which is safe to call from inside IncRef,
 * DecRef or any of the other functions which the regular ASOCKLOG()
 * implicitly calls.  We don't log fd as that isn't available at the
 * base class level.
 */

/* gcc needs special syntax to handle zero-length variadic arguments */
#if defined(_MSC_VER)
#define ASOCKLOG_NORECURSION(_level, _asock, fmt, ...)               \
   do {                                                              \
      if (((_level) == 0) || DOLOG_BYNAME(asyncsocket, (_level))) {  \
         Log(ASOCKPREFIX "%d " fmt, (_asock)->id, __VA_ARGS__);      \
      }                                                              \
   } while(0)
#else
#define ASOCKLOG_NORECURSION(_level, _asock, fmt, ...)               \
   do {                                                              \
      if (((_level) == 0) || DOLOG_BYNAME(asyncsocket, (_level))) {  \
         Log(ASOCKPREFIX "%d " fmt, (_asock)->id, ##__VA_ARGS__);    \
      }                                                              \
   } while(0)
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocketInternalIncRef --
 *
 *    Increments reference count on AsyncSocket struct and optionally
 *    takes the lock.  This function is used to implement both Lock
 *    and AddRef.
 *
 * Results:
 *    New reference count.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
AsyncSocketInternalIncRef(AsyncSocket *asock,   // IN
                          Bool lock)            // IN
{
   if (lock && asock->pollParams.lock) {
      MXUser_AcquireRecLock(asock->pollParams.lock);
   }
   ASSERT(asock->refCount > 0);
   ++asock->refCount;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocketInternalDecRef --
 *
 *    Decrements reference count on AsyncSocket struct, freeing it when it
 *    reaches 0.  If "unlock" is TRUE, releases the lock after decrementing
 *    the count.
 *
 *    This function is used to implement both Unlock and DecRef.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    May free struct.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
AsyncSocketInternalDecRef(AsyncSocket *s, // IN
                          Bool unlock)    // IN
{
   int count = --s->refCount;

   if (unlock && s->pollParams.lock) {
      MXUser_ReleaseRecLock(s->pollParams.lock);
   }

   ASSERT(count >= 0);
   if (UNLIKELY(count == 0)) {
      ASOCKLOG_NORECURSION(1, s, "Final release; freeing asock struct\n");
      VT(s)->destroy(s);
   } else {
      ASOCKLOG_NORECURSION(1, s, "Release (count now %d)\n", count);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketLock --
 * AsyncSocketUnlock --
 *
 *      Acquire/Release the lock provided by the client when creating the
 *      AsyncSocket object.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
AsyncSocketLock(AsyncSocket *asock)   // IN:
{
   AsyncSocketInternalIncRef(asock, TRUE);
}

void
AsyncSocketUnlock(AsyncSocket *asock)   // IN:
{
   AsyncSocketInternalDecRef(asock, TRUE);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketIsLocked --
 *
 *      If a lock is associated with the socket, check whether the calling
 *      thread holds the lock.
 *
 * Results:
 *      TRUE if calling thread holds the lock, or if there is no assoicated
 *      lock.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
AsyncSocketIsLocked(AsyncSocket *asock)   // IN:
{
   if (asock->pollParams.lock && Poll_LockingEnabled()) {
      return MXUser_IsCurThreadHoldingRecLock(asock->pollParams.lock);
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocketAddRef --
 *
 *    Increments reference count on AsyncSocket struct.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
AsyncSocketAddRef(AsyncSocket *s)         // IN
{
   AsyncSocketInternalIncRef(s, FALSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocketRelease --
 *
 *    Decrements reference count on AsyncSocket struct, freeing it when it
 *    reaches 0.  If "unlock" is TRUE, releases the lock after decrementing
 *    the count.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    May free struct.
 *
 *-----------------------------------------------------------------------------
 */

void
AsyncSocketRelease(AsyncSocket *s)  // IN:
{
   AsyncSocketInternalDecRef(s, FALSE);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketGetState --
 *
 *      Accessor function for the state in the base class.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocketState
AsyncSocketGetState(AsyncSocket *asock)         // IN
{
   ASSERT(AsyncSocketIsLocked(asock));
   return asock->state;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketSetState --
 *
 *      Modifier function for the state in the base class.
 *
 *----------------------------------------------------------------------------
 */

void
AsyncSocketSetState(AsyncSocket *asock,         // IN/OUT
                    AsyncSocketState state)     // IN
{
   asock->state = state;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocketGetPollParams --
 *
 *    Accessor function for the pollParams struct in the base socket.
 *
 *-----------------------------------------------------------------------------
 */

AsyncSocketPollParams *
AsyncSocketGetPollParams(AsyncSocket *s)         // IN
{
   return &s->pollParams;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocketInitSocket --
 *
 *    Initialize the AsyncSocket base struct.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
AsyncSocketInitSocket(AsyncSocket *s,                          // IN/OUT
                      AsyncSocketPollParams *pollParams,       // IN
                      const AsyncSocketVTable *vtable)         // IN
{
   /*
    * The sockets each have a "unique" ID, which is just an
    * incrementing integer.
    */
   static Atomic_uint32 nextid = { 1 };

   s->id = Atomic_ReadInc32(&nextid);
   s->refCount = 1;
   s->vt = vtable;
   s->inited = TRUE;
   if (pollParams) {
      s->pollParams = *pollParams;
   } else {
      s->pollParams.pollClass = POLL_CS_MAIN;
      s->pollParams.flags = 0;
      s->pollParams.lock = NULL;
      s->pollParams.iPoll = NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocketTeardownSocket --
 *
 *    Tear down the AsyncSocket base struct.  Currently this just
 *    clears the inited flag and releases the initial (user) refcount.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
AsyncSocketTeardownSocket(AsyncSocket *asock)         // IN/OUT
{
   /*
    * Release the initial refcount created when we initialize the
    * socket struct.
    */
   ASSERT(AsyncSocketIsLocked(asock));
   ASSERT(asock->refCount >= 1);
   ASSERT(asock->inited);
   asock->inited = FALSE;
   AsyncSocketRelease(asock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_Init --
 *
 *      Initialize the various socket subsytems.  Currently just TCP, this
 *      will expand.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *      See subsystems.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_Init(void)
{
   return AsyncTCPSocket_Init();
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetID --
 *
 *      Returns a unique identifier for the asock.
 *
 * Results:
 *      Integer id or ASOCKERR_INVAL.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetID(AsyncSocket *asock)         // IN
{
   if (!asock) {
      return ASOCKERR_INVAL;    /* For some reason we return ID 5
                                   for null pointers! */
   } else {
      return asock->id;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SetErrorFn --
 *
 *      Sets the error handling function for the asock. The error function
 *      is invoked automatically on I/O errors. This should be done
 *      before an internal callback that may call the error handler can be
 *      fired. This usually means doing so immediately after the asyncsocket
 *      is created, either from the poll thread or with the asyncsocket lock
 *      (passed in pollParams) held throughout both calls.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_INVAL.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SetErrorFn(AsyncSocket *asock,           // IN/OUT
                       AsyncSocketErrorFn errorFn,   // IN
                       void *clientData)             // IN
{
   if (!asock) {
      return ASOCKERR_INVAL;
   } else {
      AsyncSocketLock(asock);
      asock->errorFn = errorFn;
      asock->errorClientData = clientData;
      AsyncSocketUnlock(asock);
      return ASOCKERR_SUCCESS;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketHandleError --
 *
 *      Internal error handling helper. Changes the socket's state to error,
 *      and calls the registered error handler or closes the socket.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Lots.
 *
 *----------------------------------------------------------------------------
 */

void
AsyncSocketHandleError(AsyncSocket *asock,   // IN
                       int asockErr)         // IN
{
   ASSERT(asock);
   asock->errorSeen = TRUE;
   if (asock->errorFn) {
      ASOCKLOG(3, asock, "firing error callback (%s)\n",
               AsyncSocket_Err2String(asockErr));
      asock->errorFn(asockErr, asock, asock->errorClientData);
   } else {
      ASOCKLOG(3, asock, "no error callback, closing socket (%s)\n",
               AsyncSocket_Err2String(asockErr));
      AsyncSocket_Close(asock);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketCheckAndDispatchRecv --
 *
 *      Check if the recv buffer is full and dispatch the client callback.
 *
 *      Handles the possibility that the client registers a new receive buffer
 *      or closes the socket in their callback.
 *
 * Results:
 *      TRUE if the socket was closed or the receive was cancelled,
 *      FALSE if the caller should continue to try to receive data.
 *
 * Side effects:
 *      Could fire recv completion or trigger socket destruction.
 *
 *----------------------------------------------------------------------------
 */

Bool
AsyncSocketCheckAndDispatchRecv(AsyncSocket *s,  // IN
                                int *result)     // OUT
{
   ASSERT(s);
   ASSERT(result);
   ASSERT(s->recvFn);
   ASSERT(s->recvBuf);
   ASSERT(s->recvLen > 0);
   ASSERT(s->recvPos > 0);
   ASSERT(s->recvPos <= s->recvLen);

   /*
    * The application may close the socket in this callback.  This
    * asserts that even if that happens, the socket will not be
    * immediately freed in the middle of our function.
    */
   ASSERT(s->refCount > 1);

   if (s->recvPos == s->recvLen || s->recvFireOnPartial) {
      void *recvBuf = s->recvBuf;
      ASOCKLOG(3, s, "recv buffer full, calling recvFn\n");

      /*
       * We do this dance in case the handler frees the buffer (so
       * that there's no possible window where there are dangling
       * references here.  Obviously if the handler frees the buffer,
       * but then fails to register a new one, we'll put back the
       * dangling reference in the automatic reset case below, but
       * there's currently a limit to how far we go to shield clients
       * who use our API in a broken way.
       */

      s->recvBuf = NULL;
      s->recvFn(recvBuf, s->recvPos, s, s->recvClientData);
      if (s->state == AsyncSocketClosed) {
         ASOCKLG0(s, "owner closed connection in recv callback\n");
         *result = ASOCKERR_CLOSED;
         return TRUE;
      } else if (s->recvFn == NULL && s->recvLen == 0) {
         /*
          * Further recv is cancelled from within the last recvFn, see
          * AsyncSocket_CancelRecv(). So exit from the loop.
          */
         *result = ASOCKERR_SUCCESS;
         return TRUE;
      } else if (s->recvPos > 0) {
         /*
          * Automatically reset keeping the current handler.  Checking
          * that recvPos is still non-zero implies that the
          * application has not called AsyncSocket_Recv or
          * _RecvPartial in the callback.
          */
         s->recvPos = 0;
         s->recvBuf = recvBuf;
         *result = ASOCKERR_SUCCESS;
         return FALSE;
      } else {
         *result = ASOCKERR_SUCCESS;
         return FALSE;
      }
   } else {
      *result = ASOCKERR_SUCCESS;
      return FALSE;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketSetRecvBuf --
 *
 *      Helper function to validate socket state and recvBuf
 *      parameters before setting the recvBuf values in the base
 *      class.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_*.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocketSetRecvBuf(AsyncSocket *asock,  // IN:
                      void *buf,           // IN:
                      int len,             // IN:
                      Bool fireOnPartial,  // IN:
                      void *cb,            // IN:
                      void *cbData)        // IN:
{
   ASSERT(AsyncSocketIsLocked(asock));

   if (!asock->errorFn) {
      ASOCKWARN(asock, "%s: no registered error handler!\n", __FUNCTION__);
      return ASOCKERR_INVAL;
   }

   if (!buf || !cb || len <= 0) {
      ASOCKWARN(asock, "Recv called with invalid arguments!\n");
      return ASOCKERR_INVAL;
   }

   if (AsyncSocketGetState(asock) != AsyncSocketConnected &&
       AsyncSocketGetState(asock) != AsyncSocketConnectedRdOnly) {
      ASOCKWARN(asock, "recv called but state is not connected!\n");
      return ASOCKERR_NOTCONNECTED;
   }

   if (asock->recvBuf && asock->recvPos != 0) {
      ASOCKWARN(asock, "Recv called -- partially read buffer discarded.\n");
   }

   asock->recvBuf = buf;
   asock->recvLen = len;
   asock->recvFireOnPartial = fireOnPartial;
   asock->recvFn = cb;
   asock->recvClientData = cbData;
   asock->recvPos = 0;

   return ASOCKERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocketCancelRecv --
 *
 *    Call this function if you know what you are doing. This should
 *    be called if you want to synchronously receive the outstanding
 *    data on the socket.  It returns number of partially read bytes
 *    (if any). A partially read response may exist as
 *    AsyncSocketRecvCallback calls the recv callback only when all
 *    the data has been received.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    Subsequent client call to AsyncSocket_Recv can reinstate async behaviour.
 *
 *-----------------------------------------------------------------------------
 */

void
AsyncSocketCancelRecv(AsyncSocket *asock,         // IN
                      int *partialRecvd,          // OUT
                      void **recvBuf,             // OUT
                      void **recvFn)              // IN
{
   if (partialRecvd) {
      *partialRecvd = asock->recvPos;
   }
   if (recvFn) {
      *recvFn = asock->recvFn;
   }
   if (recvBuf) {
      *recvBuf = asock->recvBuf;
   }

   asock->recvBuf = NULL;
   asock->recvFn = NULL;
   asock->recvPos = 0;
   asock->recvLen = 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_Err2String --
 *
 *      Returns the error string associated with error code.
 *
 * Results:
 *      Error string.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

const char *
AsyncSocket_Err2String(int err)  // IN
{
   return Msg_StripMSGID(AsyncSocket_MsgError(err));
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_MsgError --
 *
 *      Returns the message associated with error code.
 *
 * Results:
 *      Message string.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

const char *
AsyncSocket_MsgError(int asyncSockError)   // IN
{
   const char *result = NULL;
   switch (asyncSockError) {
   case ASOCKERR_SUCCESS:
      result = MSGID(asyncsocket.success) "Success";
      break;
   case ASOCKERR_GENERIC:
      result = MSGID(asyncsocket.generic) "Asyncsocket error";
      break;
   case ASOCKERR_INVAL:
      result = MSGID(asyncsocket.invalid) "Invalid parameters";
      break;
   case ASOCKERR_TIMEOUT:
      result = MSGID(asyncsocket.timeout) "Time-out error";
      break;
   case ASOCKERR_NOTCONNECTED:
      result = MSGID(asyncsocket.notconnected) "Local socket not connected";
      break;
   case ASOCKERR_REMOTE_DISCONNECT:
      result = MSGID(asyncsocket.remotedisconnect) "Remote disconnected";
      break;
   case ASOCKERR_CLOSED:
      result = MSGID(asyncsocket.closed) "Closed socket";
      break;
   case ASOCKERR_CONNECT:
      result = MSGID(asyncsocket.connect) "Connection error";
      break;
   case ASOCKERR_POLL:
      result = MSGID(asyncsocket.poll) "Poll registration error";
      break;
   case ASOCKERR_BIND:
      result = MSGID(asyncsocket.bind) "Socket bind error";
      break;
   case ASOCKERR_BINDADDRINUSE:
      result = MSGID(asyncsocket.bindaddrinuse) "Socket bind address already in use";
      break;
   case ASOCKERR_LISTEN:
      result = MSGID(asyncsocket.listen) "Socket listen error";
      break;
   case ASOCKERR_CONNECTSSL:
      result = MSGID(asyncsocket.connectssl) "Connection error: could not negotiate SSL";
      break;
   case ASOCKERR_NETUNREACH:
      result = MSGID(asyncsocket.netunreach) "Network unreachable";
      break;
   case ASOCKERR_ADDRUNRESV:
      result = MSGID(asyncsocket.addrunresv) "Address unresolvable";
      break;
   case ASOCKERR_BUSY:
      result = MSGID(asyncsocket.busy) "Concurrent operations on socket";
      break;
   case ASOCKERR_PROXY_NEEDS_AUTHENTICATION:
      result = MSGID(asyncsocket.proxyneedsauthentication)
                     "Proxy needs authentication";
      break;
   case ASOCKERR_PROXY_CONNECT_FAILED:
      result = MSGID(asyncsocket.proxyconnectfailed)
                     "Connection failed through proxy";
      break;
   case ASOCKERR_PROXY_INVALID_OR_NOT_SUPPORTED:
      result = MSGID(asyncsocket.proxyinvalidornotsupported)
                     "Invalid or not supported type proxy";
      break;
   case ASOCKERR_WEBSOCK_UPGRADE_NOT_FOUND:
      result = MSGID(asyncsocket.websocketupgradefailed)
                     "Upgrade to websocket error: NOT FOUND, status code 404";
      break;
   case ASOCKERR_WEBSOCK_TOO_MANY_CONNECTION:
      result = MSGID(asyncsocket.websockettoomanyconnection)
                     "The server-side WebSocket connection limit has been exceeded,"
                     " HTTP status code 429";
      break;
   }

   if (!result) {
      Warning("%s was passed bad code %d\n", __FUNCTION__, asyncSockError);
      result = MSGID(asyncsocket.unknown) "Unknown error";
   }
   return result;
}


/**
 *-----------------------------------------------------------------------------
 *
 * stristr --
 *
 *    Do you know strstr from <string.h>?
 *    So this one is the same, but without the case sensitivity.
 *
 * Results:
 *    return a pointer to the first occurrence of needle in haystack,
 *    or NULL if needle does not appear in haystack. If needle is zero
 *    length, the function returns haystack.
 *
 * Side effects:
 *    none
 *
 *-----------------------------------------------------------------------------
 */

const char *
stristr(const char *haystack,         // IN
        const char *needle)           // IN
{
   if (*needle) {
      int len = strlen(needle);
      for (; *haystack; haystack++) {
         if (strncasecmp(haystack, needle, len) == 0) {
            return haystack;
         }
      }
      return NULL;
   } else {
      return haystack;
   }
}
07070100000027000081A40000000000000000000000016822550500000C3A000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/asyncsocket/asyncSocketBase.h  /*********************************************************
 * Copyright (C) 2011,2014-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef __ASYNC_SOCKET_BASE_H__
#define __ASYNC_SOCKET_BASE_H__

#ifdef USE_SSL_DIRECT
#include "sslDirect.h"
#else
#include "ssl.h"
#endif

#include "asyncSocketVTable.h"

/*
 * The abstract base class for all asyncsocket implementations.
 */
struct AsyncSocket {
   uint32 id;
   uint32 refCount;
   AsyncSocketPollParams pollParams;
   AsyncSocketState state;

   Bool inited;
   Bool errorSeen;
   AsyncSocketErrorFn errorFn;
   void *errorClientData;

   void *recvBuf;
   int recvPos;
   int recvLen;
   AsyncSocketRecvFn recvFn;
   void *recvClientData;
   Bool recvFireOnPartial;

   const AsyncSocketVTable *vt;
};

void AsyncSocketInitSocket(AsyncSocket *asock,
                           AsyncSocketPollParams *params,
                           const AsyncSocketVTable *vtable);
void AsyncSocketTeardownSocket(AsyncSocket *s);

void AsyncSocketLock(AsyncSocket *asock);
void AsyncSocketUnlock(AsyncSocket *asock);
Bool AsyncSocketIsLocked(AsyncSocket *asock);
void AsyncSocketAddRef(AsyncSocket *asock);
void AsyncSocketRelease(AsyncSocket *s);
AsyncSocketState AsyncSocketGetState(AsyncSocket *sock);
void AsyncSocketSetState(AsyncSocket *sock, AsyncSocketState state);
int AsyncSocketSetRecvBuf(AsyncSocket *asock, void *buf, int len,
                           Bool fireOnPartial, void *cb, void *cbData);
Bool AsyncSocketCheckAndDispatchRecv(AsyncSocket *s, int *result);
AsyncSocketPollParams *AsyncSocketGetPollParams(AsyncSocket *s);
void AsyncSocketHandleError(AsyncSocket *asock, int asockErr);
void AsyncSocketCancelRecv(AsyncSocket *asock, int *partialRecvd,
                           void **recvBuf, void **recvFn);


int AsyncTCPSocket_Init(void);

#endif
  07070100000028000081A4000000000000000000000001682255050000E072000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/asyncsocket/asyncSocketInterface.c /*********************************************************
 * Copyright (C) 2016-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * asyncSocketInterface.c --
 *
 *      This exposes the public functions of the AsyncSocket library.
 *      This file itself just contains stubs which call the function
 *      pointers in the socket's virtual table.
 *
 * Which entrypoints are virtual and which are base functionality?
 * Guidelines:
 * - functions affecting the underlying transport (e.g. TCP timeouts)
 *   are backend-specific and generally ARE virtualized.
 * - functions with an immediate effect (e.g. queue bytes for send)
 *   generally ARE virtualized.
 * - functions affecting the socket abstraction (e.g. how it reports errors
 *   to the caller) are basic functionality and generally are NOT virtualized.
 * - functions affecting state which is queried later (e.g. close behavior)
 *   generally are NOT virtualized.
 */

#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#endif

#include "vmware.h"
#include "asyncsocket.h"
#include "asyncSocketBase.h"
#include "msg.h"
#include "log.h"

#define LOGLEVEL_MODULE asyncsocket
#include "loglevel_user.h"


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SetCloseOptions --
 *
 *      Enables optional behavior for AsyncSocket_Close():
 *
 *      - If flushEnabledMaxWaitMsec is non-zero, the output stream
 *        will be flushed synchronously before the socket is closed.
 *        (default is zero: close socket right away without flushing)
 *
 *      - If closeCb is set, the callback will be called asynchronously
 *        when the socket is actually destroyed.
 *        (default is NULL: no callback)
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_*.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SetCloseOptions(AsyncSocket *asock,           // IN
                            int flushEnabledMaxWaitMsec,  // IN
                            AsyncSocketCloseFn closeCb)   // IN
{
   int ret;
   if (VALID(asock, setCloseOptions)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->setCloseOptions(asock, flushEnabledMaxWaitMsec, closeCb);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetState --
 *
 *      Returns the state of the provided asock or ASOCKERR_INVAL.  Note that
 *      unless this is called from a callback function, the state should be
 *      treated as transient (except the state AsyncSocketClosed).
 *
 * Results:
 *      AsyncSocketState enum.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocketState
AsyncSocket_GetState(AsyncSocket *asock)         // IN
{
   AsyncSocketState ret;
   if (VALID(asock, getState)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getState(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetGenericErrno --
 *
 *      Used when an ASOCKERR_GENERIC is returned due to a system error.
 *      The errno that was returned by the system is stored in the asock
 *      struct and returned to the user in this function.
 *
 *      XXX: This function is not thread-safe.  The errno should be returned
 *      in a parameter to any function that can return ASOCKERR_GENERIC.
 *
 * Results:
 *      int error code
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetGenericErrno(AsyncSocket *asock)  // IN:
{
   int ret;
   if (VALID(asock, getGenericErrno)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getGenericErrno(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = -1;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetFd --
 *
 *      Returns the fd for this socket.
 *
 * Results:
 *      File descriptor.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetFd(AsyncSocket *asock)         // IN
{
   int ret;
   if (VALID(asock, getFd)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getFd(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = -1;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetRemoteIPStr --
 *
 *      Given an AsyncSocket object, returns the remote IP address associated
 *      with it, or an error if the request is meaningless for the underlying
 *      connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_INVAL.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetRemoteIPStr(AsyncSocket *asock,      // IN
                           const char **ipRetStr)   // OUT
{
   int ret;
   if (VALID(asock, getRemoteIPStr)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getRemoteIPStr(asock, ipRetStr);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetRemotePort --
 *
 *      Given an AsyncSocket object, returns the remote port associated
 *      with it, or an error if the request is meaningless for the underlying
 *      connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_INVAL.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetRemotePort(AsyncSocket *asock,  // IN
                          uint32 *port)        // OUT
{
   int ret;
   if (VALID(asock, getRemotePort)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getRemotePort(asock, port);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetINETIPStr --
 *
 *      Given an AsyncSocket object, returns the IP addresses associated with
 *      the requested address family's file descriptor if available.
 *
 *      Passing AF_UNSPEC to socketFamily will provide you with the first
 *      usable IP address found (if multiple are available), with a preference
 *      given to IPv6.
 *
 *      It is the caller's responsibility to free ipRetStr.
 *
 * Results:
 *      ASOCKERR_SUCCESS. ASOCKERR_INVAL if there is no socket associated with
 *      address family requested. ASOCKERR_GENERIC for all other errors.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetINETIPStr(AsyncSocket *asock,  // IN
                         int socketFamily,    // IN
                         char **ipRetStr)     // OUT
{
   int ret;
   if (VALID(asock, getINETIPStr)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getINETIPStr(asock, socketFamily, ipRetStr);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetPort --
 *
 *      Given an AsyncSocket object, returns the port number associated with
 *      the requested address family's file descriptor if available.
 *
 * Results:
 *      Port number in host byte order. MAX_UINT32 on error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

unsigned int
AsyncSocket_GetPort(AsyncSocket *asock)  // IN
{
   int ret;
   if (VALID(asock, getPort)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getPort(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = MAX_UINT32;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_UseNodelay --
 *
 *      THIS IS DEPRECATED in favor of AsyncSocket_SetOption(...TCP_NODELAY...).
 *      It exists for now to avoid having to change all existing calling code.
 *      TODO: Remove it fully and fix up all calling code accordingly.
 *
 *      Sets the setsockopt() value TCP_NODELAY.
 *      asyncSocket may be an AsyncTCPSocket itself
 *      or contain one on which the option will be set.
 *
 *      This fails if there is no applicable AsyncTCPSocket (asyncSocket or
 *      one inside it).
 *
 * Results:
 *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
 *      There being no applicable AsyncTCPSocket yields ASOCKERR_INVAL.
 *      OS error when setting value yields ASOCKERR_GENERIC.
 *
 * Side effects:
 *      Possibly increased bandwidth usage for short messages on this socket
 *      due to TCP overhead, in exchange for lower latency.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_UseNodelay(AsyncSocket *asyncSocket,  // IN/OUT
                       Bool noDelay)              // IN
{
   const int noDelayNative = noDelay ? 1 : 0;
   return AsyncSocket_SetOption(asyncSocket,
                                (AsyncSocketOpts_Layer)IPPROTO_TCP, TCP_NODELAY,
                                &noDelayNative, sizeof noDelayNative);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SetTCPTimeouts --
 *
 *      Sets setsockopt() TCP_KEEP{INTVL|IDLE|CNT} if available in the OS.
 *      asyncSocket may be an AsyncTCPSocket itself
 *      or contain one on which the option will be set.
 *
 *      This fails if there is no applicable AsyncTCPSocket (asyncSocket or
 *      one inside it).
 *
 * Results:
 *      ASOCKERR_SUCCESS if no error, or OS doesn't support options.
 *      There being no applicable AsyncTCPSocket yields ASOCKERR_INVAL.
 *      OS error when setting any one value yields ASOCKERR_GENERIC.
 *
 * Side effects:
 *      None.
 *      Note that in case of error ASOCKERR_GENERIC, 0, 1, or 2 of the values
 *      may have still been successfully set (the successful changes are
 *      not rolled back).
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SetTCPTimeouts(AsyncSocket *asyncSocket,  // IN/OUT
                           int keepIdleSec,           // IN
                           int keepIntvlSec,          // IN
                           int keepCnt)               // IN
{
   /*
    * This function is NOT deprecated like the nearby setOption()-wrapping
    * functions. It's valuable because it: enapsulates OS-dependent logic; and
    * performs one lock before settong all applicable options together.
    */

#if defined(__linux__) || defined(VMX86_SERVER)
   /*
    * Tempting to call AsyncSocket_SetOption() x 3 instead of worrying about
    * locking and VT() ourselves, but this way we can reduce amount of
    * locking/unlocking at the cost of code verbosity.
    *
    * Reason for bailing on first error instead of trying all three:
    * it's what the original code (that this adapts) did. TODO: Find out from
    * author, explain here.
    */

   int ret;
   if (VALID(asyncSocket, setOption)) {
      AsyncSocketLock(asyncSocket);

      ret = VT(asyncSocket)->setOption
               (asyncSocket,
                (AsyncSocketOpts_Layer)IPPROTO_TCP, TCP_KEEPIDLE,
                &keepIdleSec, sizeof keepIdleSec);
      if (ret == ASOCKERR_SUCCESS) {
         ret = VT(asyncSocket)->setOption
                  (asyncSocket,
                   (AsyncSocketOpts_Layer)IPPROTO_TCP, TCP_KEEPINTVL,
                   &keepIntvlSec, sizeof keepIntvlSec);
         if (ret == ASOCKERR_SUCCESS) {
            ret = VT(asyncSocket)->setOption
                     (asyncSocket,
                      (AsyncSocketOpts_Layer)IPPROTO_TCP, TCP_KEEPCNT,
                      &keepCnt, sizeof keepCnt);
         }
      }

      AsyncSocketUnlock(asyncSocket);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
#else // #ifndef __linux__
   return ASOCKERR_SUCCESS;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_EstablishMinBufferSizes --
 *
 *      Meant to be invoked around socket creation time, this tries to ensure
 *      that SO_{SND|RCV}BUF setsockopt() values are set to at least the values
 *      provided as arguments. That is, it sets the given buffer size but only
 *      if the current value reported by the OS is smaller.
 *
 *      This fails unless asyncSocket is of the "applicable socket type."
 *      Being of "applicable socket type" is defined as supporting the
 *      option:
 *
 *        layer = SOL_SOCKET,
 *        optID = SO_{SND|RCV}BUF.
 *
 *      As of this writing, only AsyncTCPSockets (or derivations thereof)
 *      are supported, but (for example) UDP sockets could be added over time.
 *
 * Results:
 *      TRUE: on success; FALSE: on failure.
 *      Determining that no setsockopt() is required is considered success.
 *
 * Side effects:
 *      None.
 *      Note that in case of a setsockopt() failing, 0 or 1 of the values
 *      may have still been successfully set (the successful changes are
 *      not rolled back).
 *
 *-----------------------------------------------------------------------------
 */

Bool
AsyncSocket_EstablishMinBufferSizes(AsyncSocket *asyncSocket,  // IN/OUT
                                    int sendSz,                // IN
                                    int recvSz)                // IN
{
   Bool ok;

   if (VALID(asyncSocket, setOption)) {
      int curSendSz;
      socklen_t curSendSzSz = sizeof curSendSz;
      int curRecvSz;
      socklen_t curRecvSzSz = sizeof curRecvSz;

      AsyncSocketLock(asyncSocket);

      /*
       * For each buffer size, see if the current reported size is already
       * at least as large (in which case we needn't do anything for that one).
       * Bail out the moment anything fails, but don't worry about undoing any
       * change already made (as advertised in doc comment).
       *
       * Reason for bailing on first error instead of trying everything:
       * it's what the original code (that this adapts) did. TODO: Find out from
       * author, explain here.
       *
       * Note that depending on the type of socket and the particular
       * implementation (e.g., the TCP stack), asking for buffer size N might
       * result in an even larger buffer, like a multiple 2N. It's not an exact
       * science.
       */

      ok = (VT(asyncSocket)->getOption(asyncSocket,
                                       SOL_SOCKET, SO_SNDBUF,
                                       &curSendSz, &curSendSzSz) ==
            ASOCKERR_SUCCESS) &&
           (VT(asyncSocket)->getOption(asyncSocket,
                                       SOL_SOCKET, SO_RCVBUF,
                                       &curRecvSz, &curRecvSzSz) ==
            ASOCKERR_SUCCESS);
      if (ok && (curSendSz < sendSz)) {
         ok = VT(asyncSocket)->setOption(asyncSocket,
                                         SOL_SOCKET, SO_SNDBUF,
                                         &sendSz, sizeof sendSz) ==
              ASOCKERR_SUCCESS;
      }
      if (ok && (curRecvSz < recvSz)) {
         ok = VT(asyncSocket)->setOption(asyncSocket,
                                         SOL_SOCKET, SO_RCVBUF,
                                         &recvSz, sizeof recvSz) ==
              ASOCKERR_SUCCESS;
      }

      AsyncSocketUnlock(asyncSocket);
   } else {
      ok = FALSE;
   }

   return ok;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SetSendLowLatencyMode --
 *
 *      THIS IS DEPRECATED in favor of
 *      AsyncSocket_SetOption(ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE).
 *      It exists for now to avoid having to change all existing calling code.
 *      TODO: Remove it fully and fix up all calling code accordingly.
 *
 *      Sets the aforementioned value. See doc comment on
 *      ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE for more info.
 *
 *      This fails unless asyncSocket is of the "applicable socket type."
 *      Being of "applicable socket type" is defined as supporting the
 *      option:
 *
 *        layer = ASYNC_SOCKET_OPTS_LAYER_BASE,
 *        optID = ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE.
 *
 * Results:
 *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
 *      asyncSocket being of inapplicable socket type yields ASOCKERR_INVAL.
 *
 * Side effects:
 *      See ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE doc comment.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SetSendLowLatencyMode(AsyncSocket *asyncSocket,  // IN
                                  Bool enable)               // IN
{
   int ret;
   if (VALID(asyncSocket, setOption)) {
      AsyncSocketLock(asyncSocket);
      ret = VT(asyncSocket)->setOption
               (asyncSocket, ASYNC_SOCKET_OPTS_LAYER_BASE,
                ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE,
                &enable, sizeof enable);
      AsyncSocketUnlock(asyncSocket);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SetOption --
 *
 *      Sets the value of the given socket option belonging to the given
 *      option layer to the given value. The socket options mechanism is
 *      discussed in more detail in asyncsocket.h.
 *
 *      The exact behavior and supported options are dependent on the socket
 *      type. See the doc header for the specific implementation for details.
 *      If ->setOption is NULL, all options are invalid for that socket.
 *      Setting an invalid layer+option results in a no-op + error result.
 *
 *      For native options, layer = setsockopt() level,
 *      optID = setsockopt() option_name.
 *
 *      For non-native options, optID is obtained as follows: it is converted
 *      from an enum option ID value for your socket type; for example,
 *      from ASYNC_TCP_SOCKET_OPT_ALLOW_DECREASING_BUFFER_SIZE,
 *      where the latter is of type AsyncTCPSocket_OptID.
 *
 *      The option's value must reside at the buffer valuePtr that is
 *      inBufLen long. If inBufLen does not match the expected size for
 *      the given option, behavior is undefined.
 *
 * Results:
 *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
 *      Invalid option+layer yields ASOCKERR_INVAL.
 *      Failure to set a native OS option yields ASOCKERR_GENERIC.
 *      inBufLen being wrong (for the given option) yields undefined behavior.
 *
 * Side effects:
 *      Depends on option.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SetOption(AsyncSocket *asyncSocket,     // IN/OUT
                      AsyncSocketOpts_Layer layer,  // IN
                      AsyncSocketOpts_ID optID,     // IN
                      const void *valuePtr,         // IN
                      socklen_t inBufLen)           // IN
{
   int ret;
   /*
    * Lacking a setOption() implementation is conceptually the same as
    * ->setOption() existing but determining layer+optID to be invalid
    * (ASOCKERR_INVAL results).
    */
   if (VALID(asyncSocket, setOption)) {
      AsyncSocketLock(asyncSocket);
      ret = VT(asyncSocket)->setOption(asyncSocket, layer, optID,
                                       valuePtr, inBufLen);
      AsyncSocketUnlock(asyncSocket);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetOption --
 *
 *      Gets the value of the given socket option belonging to the given
 *      option layer. The socket options mechanism is
 *      discussed in more detail in asyncsocket.h.
 *      This is generally symmetrical to ..._SetOption(); most comments applying
 *      to that function apply to this one in common-sense ways.
 *      In particular a layer+optID combo is supported here if and only if it
 *      is supported for ..._SetOption().
 *
 *      The length of the output buffer at valuePtr must reside at *outBufLen
 *      at entry to this function. If *outBufLen does not match or exceed the
 *      expected size for the given option, behavior is undefined.
 *      At successful return from function, *outBufLen will be set to the
 *      length of the value written to at valuePtr.
 *
 * Results:
 *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
 *      Invalid option+layer yields ASOCKERR_INVAL.
 *      Failure to get a native OS option yields ASOCKERR_GENERIC.
 *      *outBufLen being wrong (for the given option) yields undefined behavior.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetOption(AsyncSocket *asyncSocket,     // IN/OUT
                      AsyncSocketOpts_Layer layer,  // IN
                      AsyncSocketOpts_ID optID,     // IN
                      void *valuePtr,               // OUT
                      socklen_t *outBufLen)         // IN/OUT
{
   int ret;
   /*
    * Lacking a getOption() implementation is conceptually the same as
    * ->getOption() existing but determining layer+optID to be invalid
    * (ASOCKERR_INVAL results).
    */
   if (VALID(asyncSocket, getOption)) {
      AsyncSocketLock(asyncSocket);
      ret = VT(asyncSocket)->getOption(asyncSocket, layer, optID,
                                       valuePtr, outBufLen);
      AsyncSocketUnlock(asyncSocket);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_StartSslConnect --
 *
 *    Start an asynchronous SSL connect operation.
 *
 *    The supplied callback function is called when the operation is complete
 *    or an error occurs. The caller should only free the verifyParam argument
 *    after the sslConnectFn callback is called.
 *
 * Results:
 *    ASOCKERR_SUCCESS indicates we have started async connect.
 *    ASOCKERR_* indicates a failure to start the connect.
 *
 *    Errors during asynchronous processing is reported using the
 *    callback supplied. Detailed SSL verification error can be
 *    retrieved from verifyParam structure.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

int
AsyncSocket_StartSslConnect(AsyncSocket *asock,                   // IN
                            SSLVerifyParam *verifyParam,          // IN/OPT
                            const char *hostname,                 // IN/OPT
                            void *sslCtx,                         // IN
                            AsyncSocketSslConnectFn sslConnectFn, // IN
                            void *clientData)                     // IN
{
   int ret;
   if (VALID(asock, startSslConnect)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->startSslConnect(asock, verifyParam, hostname, sslCtx,
                                       sslConnectFn, clientData);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_ConnectSSL --
 *
 *    Initialize the socket's SSL object, by calling SSL_ConnectAndVerify.
 *    NOTE: This call is blocking.
 *
 * Results:
 *    TRUE if SSL_ConnectAndVerify succeeded, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
AsyncSocket_ConnectSSL(AsyncSocket *asock,          // IN
                       SSLVerifyParam *verifyParam, // IN/OPT
                       const char *hostname,        // IN/OPT
                       void *sslContext)            // IN/OPT
{
   Bool ret;
   if (VALID(asock, connectSSL)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->connectSSL(asock, verifyParam, hostname, sslContext);
      AsyncSocketUnlock(asock);
   } else {
      ret = FALSE;
   }
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_AcceptSSL --
 *
 *    Initialize the socket's SSL object, by calling SSL_Accept.
 *
 * Results:
 *    TRUE if SSL_Accept succeeded, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
AsyncSocket_AcceptSSL(AsyncSocket *asock,    // IN
                      void *sslCtx)          // IN: optional
{
   Bool ret;
   if (VALID(asock, acceptSSL)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->acceptSSL(asock, sslCtx);
      AsyncSocketUnlock(asock);
   } else {
      ret = FALSE;
   }
   return ret;
}

/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_StartSslAccept --
 *
 *    Start an asynchronous SSL accept operation.
 *
 *    The supplied callback function is called when the operation is complete
 *    or an error occurs.
 *
 * Results:
 *    ASOCKERR_SUCCESS indicates we have started async accept.
 *    ASOCKERR_* indicates a failure to start the accept.
 *
 *    Errors during asynchronous processing are reported using the
 *    callback supplied.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

int
AsyncSocket_StartSslAccept(AsyncSocket *asock,                 // IN
                           void *sslCtx,                       // IN
                           AsyncSocketSslAcceptFn sslAcceptFn, // IN
                           void *clientData)                   // IN
{
   int ret;
   if (VALID(asock, startSslAccept)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->startSslAccept(asock, sslCtx, sslAcceptFn, clientData);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_Flush --
 *
 *      Try to send any pending out buffers until we run out of buffers, or
 *      the timeout expires.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on system call
 *      failures, and ASOCKERR_TIMEOUT if we couldn't send enough data
 *      before the timeout expired.  ASOCKERR_INVAL on invalid
 *      parameters or operation not implemented on this socket.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_Flush(AsyncSocket *asock,  // IN
                  int timeoutMS)       // IN
{
   int ret;
   if (VALID(asock, flush)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->flush(asock, timeoutMS);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_Recv --
 * AsyncSocket_RecvPartial --
 *
 *      Registers a callback that will fire once the specified amount of data
 *      has been received on the socket.
 *
 *      In the case of AsyncSocket_RecvPartial, the callback is fired
 *      once all or part of the data has been received on the socket.
 *
 *      TCP usage:
 *      AsyncSocket_Recv(AsyncSocket *asock,
 *                       void *buf,
 *                       int len,
 *                       AsyncSocketRecvFn recvFn,
 *                       void *clientData)
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Could register poll callback.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_Recv(AsyncSocket *asock,         // IN
                 void *buf,                  // IN (buffer to fill)
                 int len,                    // IN
                 void *cb,                   // IN
                 void *cbData)               // IN
{
   int ret;
   if (VALID(asock, recv)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->recv(asock, buf, len, FALSE, cb, cbData);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}

int
AsyncSocket_RecvPartial(AsyncSocket *asock,         // IN
                        void *buf,                  // IN (buffer to fill)
                        int len,                    // IN
                        void *cb,                   // IN
                        void *cbData)               // IN
{
   int ret;
   if (VALID(asock, recv)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->recv(asock, buf, len, TRUE, cb, cbData);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_Peek --
 *
 *      Similar to AsyncSocket_RecvPartial, AsyncSocket_Peek reads the socket
 *      buffer contents into the provided buffer by registering a callback that
 *      will fire when data becomes available.
 *
 *      Due to underying poll implementation, peeks are always "partial" ie.
 *      callback returns when less than or equal amount of requested length is
 *      available to read. Peek callers may use recv() to drain smaller amounts
 *      notified by peek-callback and then peek for more data if that helps.
 *
 *      There are some noteworthy differences compared to Recv():
 *
 *      - By definition, Recv() drains the socket buffer while Peek() does not
 *
 *      - Asyncsocket Recv() is post-SSL since it internally calls SSL_Read()
 *        so application always gets decrypted data when entire SSL record is
 *        decrypted. Peek() on the other hand is SSL agnostic; it reads
 *        directly from the underlying host socket and makes no attempt to
 *        decrypt it or check for any data buffered within SSL. So asyncsocket
 *        user doing a recv() followed by peek() may get different results.
 *        That is why is it most safe to use peek() before SSL is setup on the
 *        TCP connection.
 *
 *      - Peek is one-shot in nature, meaning that peek callbacks are
 *        unregistered from poll once fired.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Could register poll callback.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_Peek(AsyncSocket *asock,         // IN
                 void *buf,                  // IN (buffer to fill)
                 int len,                    // IN
                 void *cb,                   // IN
                 void *cbData)               // IN
{
   int ret;
   if (VALID(asock, recv)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->peek(asock, buf, len, cb, cbData);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_RecvPassedFd --
 *
 *      See AsyncSocket_Recv.  Besides that it allows for receiving one
 *      file descriptor...
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Could register poll callback.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_RecvPassedFd(AsyncSocket *asock,  // IN/OUT: socket
                         void *buf,           // OUT: buffer with data
                         int len,             // IN: length
                         void *cb,            // IN: completion calback
                         void *cbData)        // IN: callback's data
{
   int ret;
   if (VALID(asock, recvPassedFd)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->recvPassedFd(asock, buf, len, cb, cbData);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_GetReceivedFd --
 *
 *    Retrieve received file descriptor from socket.
 *
 * Results:
 *    File descriptor.  Or -1 if none was received.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

int
AsyncSocket_GetReceivedFd(AsyncSocket *asock)      // IN
{
   int ret;
   if (VALID(asock, getReceivedFd)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getReceivedFd(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = -1;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_Send --
 *
 *      Queues the provided data for sending on the socket. If a send callback
 *      is provided, the callback is fired after the data has been written to
 *      the socket. Note that this only guarantees that the data has been
 *      copied to the transmit buffer, we make no promises about whether it
 *      has actually been transmitted, or received by the client, when the
 *      callback is fired.
 *
 *      Send callbacks should also be able to deal with being called if none
 *      or only some of the queued buffer has been transmitted, since the send
 *      callbacks for any remaining buffers are fired by AsyncSocket_Close().
 *      This condition can be detected by checking the len parameter passed to
 *      the send callback.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      May register poll callback or perform I/O.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_Send(AsyncSocket *asock,         // IN
                 void *buf,                  // IN
                 int len,                    // IN
                 AsyncSocketSendFn sendFn,   // IN
                 void *clientData)           // IN
{
   int ret;
   if (VALID(asock, send)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->send(asock, buf, len, sendFn, clientData);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_IsSendBufferFull --
 *
 *      Indicate if socket send buffer is full.  Note that unless this is
 *      called from a callback function, the return value should be treated
 *      as transient.
 *
 * Results:
 *      0: send space probably available,
 *      1: send has reached maximum,
 *      ASOCKERR_INVAL: null socket or operation not supported.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_IsSendBufferFull(AsyncSocket *asock)         // IN
{
   int ret;
   if (VALID(asock, isSendBufferFull)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->isSendBufferFull(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetNetworkStats --
 *
 *      Get network statistics from the active socket.
 *
 * Results:
 *      ASOCKERR_*
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetNetworkStats(AsyncSocket *asock,              // IN
                            AsyncSocketNetworkStats *stats)  // OUT
{
   int ret;
   if (VALID(asock, getNetworkStats)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getNetworkStats(asock, stats);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetSNIHostname --
 *
 *      Get SNI hostname from the active socket.
 *
 * Results:
 *      ASOCKERR_*
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetSNIHostname(AsyncSocket *asock,          // IN
                           const char **sniHostname)    // OUT
{
   int ret;
   if (VALID(asock, getSNIHostname)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getSNIHostname(asock, sniHostname);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_Close --
 *
 *      AsyncSocket destructor. The destructor should be safe to call at any
 *      time.  It's invoked automatically for I/O errors on slots that have no
 *      error handler set, and should be called manually by the error handler
 *      as necessary. It could also be called as part of the normal program
 *      flow.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Closes the socket fd, unregisters all Poll callbacks, and fires the
 *      send triggers for any remaining output buffers.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_Close(AsyncSocket *asock)         // IN
{
   int ret;
   if (VALID(asock, close)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->close(asock);
      ASSERT(!asock->inited);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_CloseWrite --
 *
 *      Close the write side of AsyncSocket, and leave the read side open. This
 *      can be used to implement a shutdown(SHUT_WR...) equivalent.
 *
 *      This function is meant to be called after the socket has been connected
 *      If no error, pending send is flushed the same way as AsyncSocket_Close,
 *      and then the socket enters half-closed state, disallowing further send.
 *
 *      Like AsyncSocket_Close, it's safe to call at any time, but it could err
 *      when the socket is not connected. Unlike Close, read side of the socket
 *      is left intact, and the the socket is not released.
 *
 *      If the socket is already closed, it returns success.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Send is not permitted after this call.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_CloseWrite(AsyncSocket *asock)         // IN
{
   int ret;
   if (VALID(asock, closeWrite)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->closeWrite(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_CancelRecv --
 * AsyncSocket_CancelRecvEx --
 *
 *    Call this function if you know what you are doing. This should be
 *    called if you want to synchronously receive the outstanding data on
 *    the socket. It removes the recv poll callback. It also returns number of
 *    partially read bytes (if any). A partially read response may exist as
 *    AsyncSocketRecvCallback calls the recv callback only when all the data
 *    has been received.
 *
 * Results:
 *    ASOCKERR_SUCCESS or ASOCKERR_INVAL.
 *
 * Side effects:
 *    Subsequent client call to AsyncSocket_Recv can reinstate async behaviour.
 *
 *-----------------------------------------------------------------------------
 */

int
AsyncSocket_CancelRecv(AsyncSocket *asock,         // IN
                       int *partialRecvd,          // OUT
                       void **recvBuf,             // OUT
                       void **recvFn)              // OUT
{
   return AsyncSocket_CancelRecvEx(asock, partialRecvd, recvBuf, recvFn, FALSE);
}

int
AsyncSocket_CancelRecvEx(AsyncSocket *asock,         // IN
                         int *partialRecvd,          // OUT
                         void **recvBuf,             // OUT
                         void **recvFn,              // OUT
                         Bool cancelOnSend)          // IN
{
   int ret;
   if (VALID(asock, cancelRecv)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->cancelRecv(asock, partialRecvd, recvBuf, recvFn,
                                  cancelOnSend);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_CancelCbForClose --
 *
 *      This is the external version of AsyncSocketCancelCbForCloseInt().  It
 *      takes care of acquiring any necessary lock before calling the internal
 *      function.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_CancelCbForClose(AsyncSocket *asock)  // IN:
{
   int ret;
   if (VALID(asock, cancelCbForClose)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->cancelCbForClose(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetLocalVMCIAddress --
 *
 *      Given an AsyncSocket object, returns the local VMCI context ID and
 *      port number associated with it, or an error if the request is
 *      meaningless for the underlying connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetLocalVMCIAddress(AsyncSocket *asock,  // IN
                                uint32 *cid,         // OUT: optional
                                uint32 *port)        // OUT: optional
{
   int ret;
   if (VALID(asock, getLocalVMCIAddress)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getLocalVMCIAddress(asock, cid, port);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetRemoteVMCIAddress --
 *
 *      Given an AsyncSocket object, returns the remote VMCI context ID and
 *      port number associated with it, or an error if the request is
 *      meaningless for the underlying connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetRemoteVMCIAddress(AsyncSocket *asock,  // IN
                                 uint32 *cid,         // OUT: optional
                                 uint32 *port)        // OUT: optional
{
   int ret;
   if (VALID(asock, getRemoteVMCIAddress)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getRemoteVMCIAddress(asock, cid, port);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetWebSocketError --
 *
 *      Return the HTTP error code supplied during a failed WebSocket
 *      upgrade negotiation.
 *
 * Results:
 *      Numeric HTTP error code, 0 if no error, or -1 on invalid arguments.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_GetWebSocketError(AsyncSocket *asock)    // IN
{
   int ret;
   if (VALID(asock, getWebSocketError)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getWebSocketError(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = -1;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetWebSocketURI --
 *
 *      Return the URI supplied during a WebSocket connection request.
 *
 * Results:
 *      URI or Null if no URI was specified.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

char *
AsyncSocket_GetWebSocketURI(AsyncSocket *asock)    // IN
{
   char *ret;
   if (VALID(asock, getWebSocketURI)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getWebSocketURI(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = NULL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetWebSocketCookie --
 *
 *      Return the Cookie field value supplied during a WebSocket
 *      connection request.
 *
 * Results:
 *      Cookie, if asock is WebSocket.
 *      NULL, if asock is not WebSocket.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

char *
AsyncSocket_GetWebSocketCookie(AsyncSocket *asock)    // IN
{
   char *ret;
   if (VALID(asock, getWebSocketCookie)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getWebSocketCookie(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = NULL;
   }
   return ret;
}

/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SetWebSocketCookie --
 *
 *      Insert Set-Cookie HTTP response header during WebSocket connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS if we finished the operation, ASOCKERR_* error codes
 *      otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SetWebSocketCookie(AsyncSocket *asock,      // IN
                               void *clientData,        // IN
                               const char *path,        // IN
                               const char *sessionId)   // IN
{
   int ret = ASOCKERR_INVAL;
   if (VALID(asock, setWebSocketCookie)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->setWebSocketCookie(asock, clientData, path, sessionId);
      AsyncSocketUnlock(asock);
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetWebSocketCloseStatus --
 *
 *      Retrieve the close status, if received, for a websocket connection.
 *
 * Results:
 *      Websocket close status code (>= 1000), or 0 if never received.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

uint16
AsyncSocket_GetWebSocketCloseStatus(AsyncSocket *asock)   // IN
{
   uint16 ret;
   if (VALID(asock, getWebSocketCloseStatus)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getWebSocketCloseStatus(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = 0;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_GetWebSocketProtocol --
 *
 *      Return the negotiated websocket protocol.  Only valid until asock is
 *      destroyed.
 *
 * Results:
 *      NULL, if asock is not WebSocket.
 *      AsyncWebSocketProtocol *, if asock is WebSocket.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

const char *
AsyncSocket_GetWebSocketProtocol(AsyncSocket *asock)  // IN
{
   const char *ret;
   if (VALID(asock, getWebSocketProtocol)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->getWebSocketProtocol(asock);
      AsyncSocketUnlock(asock);
   } else {
      ret = NULL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SetDelayWebSocketUpgradeResponse --
 *
 *      Set a flag for whether or not to not automatically send the websocket
 *      upgrade response upon receiving the websocket upgrade request.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SetDelayWebSocketUpgradeResponse(AsyncSocket *asock,                  // IN
                                             Bool delayWebSocketUpgradeResponse)  // IN
{
   int ret = ASOCKERR_INVAL;
   if (VALID(asock, setDelayWebSocketUpgradeResponse)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->setDelayWebSocketUpgradeResponse(asock,
                                                        delayWebSocketUpgradeResponse);
      AsyncSocketUnlock(asock);
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_RecvBlocking --
 *
 *      Implement "blocking + timeout" operations on the socket. These are
 *      simple wrappers around the AsyncTCPSocketBlockingWork function, which
 *      operates on the actual non-blocking socket, using poll to determine
 *      when it's ok to keep reading/writing. If we can't finish within the
 *      specified time, we give up and return the ASOCKERR_TIMEOUT error.
 *
 *      Note that if these are called from a callback and a lock is being
 *      used (pollParams.lock), the whole blocking operation takes place
 *      with that lock held.  Regardless, it is the caller's responsibility
 *      to make sure the synchronous and asynchronous operations do not mix.
 *
 * Results:
 *      ASOCKERR_SUCCESS if we finished the operation, ASOCKERR_* error codes
 *      otherwise.
 *
 * Side effects:
 *      Reads/writes the socket.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_RecvBlocking(AsyncSocket *asock,         // IN
                         void *buf,                  // OUT
                         int len,                    // IN
                         int *received,              // OUT
                         int timeoutMS)              // IN
{
   int ret;
   if (VALID(asock, recvBlocking)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->recvBlocking(asock, buf, len, received, timeoutMS);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_RecvPartialBlocking --
 *
 *      Implement "blocking + timeout" version of RecvPartial
 *
 * Results:
 *      ASOCKERR_SUCCESS if we finished the operation, ASOCKERR_* error codes
 *      otherwise.
 *
 * Side effects:
 *      Reads/writes the socket.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_RecvPartialBlocking(AsyncSocket *asock,         // IN
                                void *buf,                  // OUT
                                int len,                    // IN
                                int *received,              // OUT
                                int timeoutMS)              // IN
{
   int ret;
   if (VALID(asock, recvPartialBlocking)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->recvPartialBlocking(asock, buf, len, received,
                                           timeoutMS);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SendBlocking --
 *
 *      Implement "blocking + timeout" version of Send
 *
 * Results:
 *      ASOCKERR_SUCCESS if we finished the operation, ASOCKERR_* error codes
 *      otherwise.
 *
 * Side effects:
 *      Reads/writes the socket.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SendBlocking(AsyncSocket *asock,         // IN
                         void *buf,                  // IN
                         int len,                    // IN
                         int *sent,                  // OUT
                         int timeoutMS)              // IN
{
   int ret;
   if (VALID(asock, sendBlocking)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->sendBlocking(asock, buf, len, sent, timeoutMS);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_DoOneMsg --
 *
 *      Spins a socket until the specified amount of time has elapsed or
 *      data has arrived / been sent.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on system call
 *         failures
 *      ASOCKERR_TIMEOUT if nothing happened in the allotted time.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_DoOneMsg(AsyncSocket *asock,         // IN
                     Bool read,                  // IN
                     int timeoutMS)              // IN
{
   int ret;
   if (VALID(asock, doOneMsg)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->doOneMsg(asock, read, timeoutMS);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_WaitForConnection --
 *
 *      Spins a socket currently listening or connecting until the
 *      connection completes or the allowed time elapses.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on failures, and
 *      ASOCKERR_TIMEOUT if nothing happened in the allotted time.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_WaitForConnection(AsyncSocket *asock,         // IN
                              int timeoutMS)              // IN
{
   int ret;
   if (VALID(asock, waitForConnection)) {
      AsyncSocketLock(asock);
      ret = VT(asock)->waitForConnection(asock, timeoutMS);
      AsyncSocketUnlock(asock);
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_WaitForReadMultiple --
 *
 *      Waits on a list of sockets, returning when a socket becomes
 *      available for read, or when the allowed time elapses.
 *
 *      Note, if this function is called by two threads with overlapping
 *      sets of sockets, a deadlock can occur. The caller should guard
 *      against such scenarios from happening, or making sure that there
 *      is a consistent ordering to the lists of sockets.
 *
 *      The caller must also make sure synchronous and asynchronous
 *      operations do not mix, as this function does not hold locks
 *      for the entirety of the call.
 *
 * Results:
 *      ASOCKERR_SUCCESS if one of the sockets is ready for read,
 *      ASOCKERR_GENERIC on failures, and ASOCKERR_TIMEOUT if nothing
 *      happened in the allotted time.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_WaitForReadMultiple(AsyncSocket **asock,  // IN
                                int numSock,          // IN
                                int timeoutMS,        // IN
                                int *outIdx)          // OUT
{
   int ret;

   if (numSock > 0 && VALID(asock[0], waitForReadMultiple)) {
      int i;

      for (i = 0; i < numSock; i++) {
         AsyncSocketLock(asock[i]);
      }
      ret = VT(asock[0])->waitForReadMultiple(asock, numSock,
                                              timeoutMS, outIdx);
      for (i = numSock - 1; i >= 0; i--) {
         AsyncSocketUnlock(asock[i]);
      }
   } else {
      ret = ASOCKERR_INVAL;
   }
   return ret;
}
  07070100000029000081A40000000000000000000000016822550500001B1B000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/lib/asyncsocket/asyncSocketVTable.h    /*********************************************************
 * Copyright (c) 2011,2014-2017,2019-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef __ASYNC_SOCKET_VTABLE_H__
#define __ASYNC_SOCKET_VTABLE_H__

#ifdef USE_SSL_DIRECT
#include "sslDirect.h"
#else
#include "ssl.h"
#endif

/*
 * If we change the AsyncSocketVTable, we also need to change the follow files:
 * apps/asyncSocketProxy/asyncVvcSocket.c
 * lib/blastSockets/asyncBweSocket.c
 * lib/blastSockets/asyncProxySocket.c
 * lib/asyncsocket/asyncsocket.c
 * lib/asyncsocket/asyncWebSocket.c
 * lib/asyncsocket/asyncNamedPipe.c
 * lib/udpfec/fecAsyncSocket.c
 * lib/udpfec/fecAsyncSslSocket.c
 * devices/vsock/asyncVmciSocket.c
 */
typedef struct AsyncSocketVTable {
   AsyncSocketState (*getState)(AsyncSocket *sock);

   /*
    * The socket options mechanism is discussed in asyncsocket.h.
    * If you're considering adding a new virtual function table entry whose
    * effect is to call setsockopt() and/or save a value inside the socket
    * structure and/or forward such a call to a contained AsyncSocket,
    * strongly consider using this setOption() mechanism instead.
    * Your life is likely to be made easier by this.
    */
   int (*setOption)(AsyncSocket *asyncSocket,
                    AsyncSocketOpts_Layer layer,
                    AsyncSocketOpts_ID optID,
                    const void *valuePtr,
                    socklen_t inBufLen);
   /*
    * A setOption() implementation must have a symmetrical getOption()
    * counterpart.  The converse is not true -- a getOption()
    * implementation need not have a setOption() counterpart.  (One
    * way to look at this is that an option may be read-only, but it
    * must not be write-only.)
    */
   int (*getOption)(AsyncSocket *asyncSocket,
                    AsyncSocketOpts_Layer layer,
                    AsyncSocketOpts_ID optID,
                    void *valuePtr,
                    socklen_t *outBufLen);

   int (*getGenericErrno)(AsyncSocket *s);
   int (*getFd)(AsyncSocket *asock);
   int (*getRemoteIPStr)(AsyncSocket *asock, const char **ipStr);
   int (*getRemotePort)(AsyncSocket *asock, uint32 *port);
   int (*getINETIPStr)(AsyncSocket *asock, int socketFamily, char **ipRetStr);
   unsigned int (*getPort)(AsyncSocket *asock);
   int (*setCloseOptions)(AsyncSocket *asock, int flushEnabledMaxWaitMsec,
                           AsyncSocketCloseFn closeCb);
   Bool (*connectSSL)(AsyncSocket *asock, struct _SSLVerifyParam *verifyParam,
                      const char *hostname, void *sslContext);
   int (*startSslConnect)(AsyncSocket *asock,
                           struct _SSLVerifyParam *verifyParam,
                           const char *hostname, void *sslCtx,
                           AsyncSocketSslConnectFn sslConnectFn,
                           void *clientData);
   Bool (*acceptSSL)(AsyncSocket *asock, void *sslCtx);
   int (*startSslAccept)(AsyncSocket *asock, void *sslCtx,
                          AsyncSocketSslAcceptFn sslAcceptFn,
                          void *clientData);
   int (*flush)(AsyncSocket *asock, int timeoutMS);
   int (*recv)(AsyncSocket *asock, void *buf, int len, Bool partial, void *cb,
               void *cbData);
   int (*recvPassedFd)(AsyncSocket *asock, void *buf, int len, void *cb,
                       void *cbData);
   int (*getReceivedFd)(AsyncSocket *asock);
   int (*send)(AsyncSocket *asock, void *buf, int len,
               AsyncSocketSendFn sendFn, void *clientData);
   int (*isSendBufferFull)(AsyncSocket *asock);
   int (*getNetworkStats)(AsyncSocket *asock,
                          AsyncSocketNetworkStats *stats);
   int (*getSNIHostname)(AsyncSocket *asock,
                         const char **sniHostname);
   int (*close)(AsyncSocket *asock);
   int (*closeWrite)(AsyncSocket *asock);
   int (*cancelRecv)(AsyncSocket *asock, int *partialRecvd, void **recvBuf,
                     void **recvFn, Bool cancelOnSend);
   int (*cancelCbForClose)(AsyncSocket *asock);
   int (*getLocalVMCIAddress)(AsyncSocket *asock, uint32 *cid, uint32 *port);
   int (*getRemoteVMCIAddress)(AsyncSocket *asock, uint32 *cid, uint32 *port);
   int (*getWebSocketError)(AsyncSocket *asock);
   char *(*getWebSocketURI)(AsyncSocket *asock);
   char *(*getWebSocketCookie)(AsyncSocket *asock);
   uint16 (*getWebSocketCloseStatus)(AsyncSocket *asock);
   const char *(*getWebSocketProtocol)(AsyncSocket *asock);
   int (*setWebSocketCookie)(AsyncSocket *asock, void *clientData,
                             const char *path, const char *sessionId);
   int (*setDelayWebSocketUpgradeResponse)(AsyncSocket *asock,
                                           Bool delayWebSocketUpgradeResponse);
   int (*recvBlocking)(AsyncSocket *s, void *buf, int len, int *received,
                      int timeoutMS);
   int (*recvPartialBlocking)(AsyncSocket *s, void *buf, int len,
                              int *received, int timeoutMS);
   int (*sendBlocking)(AsyncSocket *s, void *buf, int len, int *sent,
                       int timeoutMS);
   int (*doOneMsg)(AsyncSocket *s, Bool read, int timeoutMS);
   int (*waitForConnection)(AsyncSocket *s, int timeoutMS);
   int (*waitForReadMultiple)(AsyncSocket **asock, size_t numSock,
                              int timeoutMS, int *outIdx);
   int (*peek)(AsyncSocket *asock, void *buf, int len, void *cb, void *cbData);

   /*
    * Internal function, called when refcount drops to zero:
    */
   void (*destroy)(AsyncSocket *asock);
} AsyncSocketVTable;


#define VT(x) ((x)->vt)
#define VALID(asock, x) LIKELY(asock && VT(asock)->x)


#endif
 0707010000002A000081A40000000000000000000000016822550500034348000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/asyncsocket/asyncsocket.c  /*********************************************************
 * Copyright (c) 2003-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * asyncsocket.c --
 *
 *      The AsyncTCPSocket object is a fairly simple wrapper around a basic TCP
 *      socket. It's potentially asynchronous for both read and write
 *      operations. Reads are "requested" by registering a receive function
 *      that is called once the requested amount of data has been read from
 *      the socket. Similarly, writes are queued along with a send function
 *      that is called once the data has been written. Errors are reported via
 *      a separate callback.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>

#ifdef _WIN32
/*
 * We redefine strcpy/strcat because the Windows SDK uses it for getaddrinfo().
 * When we upgrade SDKs, this redefinition can go away.
 * Note: Now we are checking if we have secure libs for string operations
 */
#if !(defined(__GOT_SECURE_LIB__) && __GOT_SECURE_LIB__ >= 200402L)
#define strcpy(dst,src) Str_Strcpy((dst), (src), 0x7FFFFFFF)
#define strcat(dst,src) Str_Strcat((dst), (src), 0x7FFFFFFF)
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <wspiapi.h>
#include <MSWSock.h>
#include <mstcpip.h>
#include <windows.h>
#if !(defined(__GOT_SECURE_LIB__) && __GOT_SECURE_LIB__ >= 200402L)
#undef strcpy
#undef strcat
#endif
#else
#include <stddef.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#endif

#include "vmware.h"
#include "str.h"
#include "random.h"
#include "asyncsocket.h"
#include "asyncSocketBase.h"
#include "poll.h"
#include "log.h"
#include "err.h"
#include "hostinfo.h"
#include "util.h"
#include "msg.h"
#include "posix.h"
#include "vm_basic_asm.h"
#include "vmci_sockets.h"
#ifndef VMX86_TOOLS
#include "vmdblib.h"
#endif


#ifdef _WIN32
#define ASOCK_LASTERROR()       WSAGetLastError()
#else
#define ASOCK_LASTERROR()       errno
#endif


#define LOGLEVEL_MODULE asyncsocket
#include "loglevel_user.h"

#ifdef VMX86_SERVER
#include "uwvmkAPI.h"
#endif

#ifdef __linux__
/*
 * Our toolchain does not support IPV6_V6ONLY, but the host we are running on
 * may support it. Since setsockopt will return a error that we treat as
 * non-fatal, it is fine to attempt it. See define in in6.h.
 */
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 26
#endif

/*
 * Linux versions can lack support for IPV6_V6ONLY while still supporting
 * V4MAPPED addresses. We check for a V4MAPPED address during accept to cover
 * this scenario. In case IN6_IS_ADDR_V4MAPPED is also not available, define it.
 */
#ifndef IN6_IS_ADDR_V4MAPPED
#define IN6_IS_ADDR_V4MAPPED(a)                                   \
   (*(const u_int32_t *)(const void *)(&(a)->s6_addr[0]) == 0 &&  \
    *(const u_int32_t *)(const void *)(&(a)->s6_addr[4]) == 0 &&  \
    *(const u_int32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)))
#endif
#endif

#define PORT_STRING_LEN 6 /* "12345\0" or ":12345" */

#define IN_IPOLL_RECV (1 << 0)
#define IN_IPOLL_SEND (1 << 1)

/*
 * INET6_ADDRSTRLEN allows for only 45 characters. If we somehow have a
 * non-recommended V4MAPPED address we can exceed 45 total characters in our
 * address string format. While this should not be the case it is possible.
 * Account for the possible:
 *    "[XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:AAA.BBB.CCC.DDD]:12345\0"
 *    (XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:AAA.BBB.CCC.DDD\0 + [] + :12345)
 */
#define ADDR_STRING_LEN (INET6_ADDRSTRLEN + 2 + PORT_STRING_LEN)

typedef enum {
   ASOCK_FLAG_PEEK = 1
} AsockFlags;

#define ASOCK_PEEK(a)   (a->flags & ASOCK_FLAG_PEEK)

/* Local types. */

/*
 * Output buffer list data type, for the queue of outgoing buffers
 */
typedef struct SendBufList {
   struct SendBufList   *next;
   void                 *buf;
   int                   len;
   int                   passFd;
   AsyncSocketSendFn     sendFn;
   void                 *clientData;
} SendBufList;


typedef struct AsyncTCPSocket {
   /*
    * The base class, which is just a vtable:
    */
   AsyncSocket base;

   /*
    * Everything for the TCP AsyncSocket implementation:
    */
   int fd;
   SSLSock sslSock;

   int genericErrno;

   struct sockaddr_storage localAddr;
   socklen_t localAddrLen;
   struct sockaddr_storage remoteAddr;
   socklen_t remoteAddrLen;

   AsyncSocketConnectFn connectFn;
   AsyncSocketSslAcceptFn sslAcceptFn;
   AsyncSocketSslConnectFn sslConnectFn;
   int sslPollFlags;       /* shared by sslAcceptFn, sslConnectFn */

   /* shared by connectFn, sslAcceptFn and sslConnectFn */
   void *clientData;

   PollerFunction internalConnectFn;
   PollerFunction internalSendFn;
   PollerFunction internalRecvFn;

   /* governs optional AsyncSocket_Close() behavior */
   int flushEnabledMaxWaitMsec;
   AsyncSocketCloseFn closeCb;
   void *closeCbData;

   Bool recvCb;
   Bool recvCbTimer;

   SendBufList *sendBufList;
   SendBufList **sendBufTail;
   int sendPos;
   Bool sendCb;
   Bool sendCbTimer;
   Bool sendCbRT;
   Bool sendBufFull;
   Bool sendLowLatency;
   int inLowLatencySendCb;

   Bool sslConnected;

   uint8 inIPollCb;
   Bool inRecvLoop;
   uint32 inBlockingRecv;

   struct AsyncTCPSocket *listenAsock4;
   struct AsyncTCPSocket *listenAsock6;

   struct {
      Bool expected;
      int fd;
   } passFd;

   AsockFlags flags;

} AsyncTCPSocket;



/*
 * Local Functions
 */
static AsyncTCPSocket *AsyncTCPSocketCreate(AsyncSocketPollParams *pollParams);
static void AsyncTCPSocketSendCallback(void *clientData);
static void AsyncTCPSocketRecvCallback(void *clientData);
static int AsyncTCPSocketResolveAddr(const char *hostname,
                                     unsigned int port,
                                     int family,
                                     Bool passive,
                                     struct sockaddr_storage *addr,
                                     socklen_t *addrLen,
                                     char **addrString);
static AsyncTCPSocket *AsyncTCPSocketAttachToFd(
   int fd, AsyncSocketPollParams *pollParams, int *outError);
static Bool AsyncTCPSocketHasDataPending(AsyncTCPSocket *asock);
static int AsyncTCPSocketMakeNonBlocking(int fd);
static void AsyncTCPSocketAcceptCallback(void *clientData);
static void AsyncTCPSocketConnectCallback(void *clientData);
static int AsyncTCPSocketBlockingWork(AsyncTCPSocket *asock, Bool read,
                                      void *buf, int len,
                                      int *completed, int timeoutMS,
                                      Bool partial);
static VMwareStatus AsyncTCPSocketPollAdd(AsyncTCPSocket *asock, Bool socket,
                                          int flags, PollerFunction callback,
                                          ...);
static Bool AsyncTCPSocketPollRemove(AsyncTCPSocket *asock, Bool socket,
                                     int flags, PollerFunction callback);
static unsigned int AsyncTCPSocketGetPortFromAddr(
   struct sockaddr_storage *addr);
static AsyncTCPSocket *AsyncTCPSocketConnect(struct sockaddr_storage *addr,
                                             socklen_t addrLen,
                                             int socketFd,
                                             AsyncSocketConnectFn connectFn,
                                             void *clientData,
                                             AsyncSocketConnectFlags flags,
                                             AsyncSocketPollParams *pollParams,
                                             int *outError);
static int AsyncTCPSocketConnectInternal(AsyncTCPSocket *s);

static VMwareStatus AsyncTCPSocketIPollAdd(AsyncTCPSocket *asock, Bool socket,
                                           int flags, PollerFunction callback,
                                           int info);
static Bool AsyncTCPSocketIPollRemove(AsyncTCPSocket *asock, Bool socket,
                                      int flags, PollerFunction callback);
static void AsyncTCPSocketIPollSendCallback(void *clientData);
static void AsyncTCPSocketIPollRecvCallback(void *clientData);
static Bool AsyncTCPSocketAddListenCb(AsyncTCPSocket *asock);
static void AsyncTCPSocketSslConnectCallback(void *clientData);
static void AsyncTCPSocketSslAcceptCallback(void *clientData);

static Bool AsyncTCPSocketBind(AsyncTCPSocket *asock,
                               struct sockaddr_storage *addr,
                               socklen_t addrLen,
                               int *outError);
static Bool AsyncTCPSocketListen(AsyncTCPSocket *asock,
                                 AsyncSocketConnectFn connectFn,
                                 void *clientData,
                                 int *outError);
static AsyncTCPSocket *AsyncTCPSocketInit(int socketFamily,
                                          AsyncSocketPollParams *pollParams,
                                          int *outError);

static void AsyncTCPSocketCancelListenCb(AsyncTCPSocket *asock);


static int AsyncTCPSocketRegisterRecvCb(AsyncTCPSocket *asock);
static Bool AsyncTCPSocketCancelCbForConnectingClose(AsyncTCPSocket *asock);

static int AsyncTCPSocketWaitForConnection(AsyncSocket *s, int timeoutMS);
static int AsyncTCPSocketGetGenericErrno(AsyncSocket *s);
static int AsyncTCPSocketGetFd(AsyncSocket *asock);
static int AsyncTCPSocketGetRemoteIPStr(AsyncSocket *asock, const char **ipStr);
static int AsyncTCPSocketGetRemotePort(AsyncSocket *asock, uint32 *port);
static int AsyncTCPSocketGetINETIPStr(AsyncSocket *asock, int socketFamily,
                                      char **ipRetStr);
static unsigned int AsyncTCPSocketGetPort(AsyncSocket *asock);
static Bool AsyncTCPSocketConnectSSL(AsyncSocket *asock,
                                     struct _SSLVerifyParam *verifyParam,
                                     const char *hostname,
                                     void *sslContext);
static int AsyncTCPSocketStartSslConnect(AsyncSocket *asock,
                                         SSLVerifyParam *verifyParam,
                                         const char *hostname,
                                         void *sslCtx,
                                         AsyncSocketSslConnectFn sslConnectFn,
                                         void *clientData);
static Bool AsyncTCPSocketAcceptSSL(AsyncSocket *asock, void *sslCtx);
static int AsyncTCPSocketStartSslAccept(AsyncSocket *asock, void *sslCtx,
                                        AsyncSocketSslAcceptFn sslAcceptFn,
                                        void *clientData);
static int AsyncTCPSocketFlush(AsyncSocket *asock, int timeoutMS);
static void AsyncTCPSocketCancelRecvCb(AsyncTCPSocket *asock);
static void AsyncTCPSocketCancelSendCb(AsyncTCPSocket *asock);

static int AsyncTCPSocketRecv(AsyncSocket *asock,
             void *buf, int len, Bool partial, void *cb, void *cbData);
static int AsyncTCPSocketPeek(AsyncSocket *asock, void *buf, int len, void *cb,
                              void *cbData);
static int AsyncTCPSocketRecvPassedFd(AsyncSocket *asock, void *buf, int len,
                     void *cb, void *cbData);
static int AsyncTCPSocketGetReceivedFd(AsyncSocket *asock);
static int AsyncTCPSocketSend(AsyncSocket *asock, void *buf, int len,
                              AsyncSocketSendFn sendFn, void *clientData);
static int AsyncTCPSocketSendWithFd(AsyncSocket *asock, void *buf, int len,
                                    int fd, AsyncSocketSendFn sendFn,
                                    void *clientData);
static int AsyncTCPSocketIsSendBufferFull(AsyncSocket *asock);
static int AsyncTCPSocketClose(AsyncSocket *asock);
static int AsyncTCPSocketCloseWrite(AsyncSocket *asock);
static int AsyncTCPSocketCancelRecv(AsyncSocket *asock, int *partialRecvd,
                                    void **recvBuf, void **recvFn,
                                    Bool cancelOnSend);
static int AsyncTCPSocketCancelCbForClose(AsyncSocket *asock);
static int AsyncTCPSocketGetLocalVMCIAddress(AsyncSocket *asock,
                            uint32 *cid, uint32 *port);
static int AsyncTCPSocketGetRemoteVMCIAddress(AsyncSocket *asock,
                             uint32 *cid, uint32 *port);
static int AsyncTCPSocketSetCloseOptions(AsyncSocket *asock,
                                          int flushEnabledMaxWaitMsec,
                                          AsyncSocketCloseFn closeCb);
static void AsyncTCPSocketDestroy(AsyncSocket *s);
static int AsyncTCPSocketRecvBlocking(AsyncSocket *s, void *buf, int len,
                                      int *received, int timeoutMS);
static int AsyncTCPSocketRecvPartialBlocking(AsyncSocket *s, void *buf, int len,
                                             int *received, int timeoutMS);
static int AsyncTCPSocketSendBlocking(AsyncSocket *s, void *buf, int len,
                                      int *sent, int timeoutMS);
static int AsyncTCPSocketDoOneMsg(AsyncSocket *s, Bool read, int timeoutMS);
static int AsyncTCPSocketWaitForReadMultiple(AsyncSocket **asock, size_t numSock,
                                             int timeoutMS, int *outIdx);
static int AsyncTCPSocketSetOption(AsyncSocket *asyncSocket,
                                   AsyncSocketOpts_Layer layer,
                                   AsyncSocketOpts_ID optID,
                                   const void *valuePtr,
                                   socklen_t inBufLen);
static int AsyncTCPSocketGetOption(AsyncSocket *asyncSocket,
                                   AsyncSocketOpts_Layer layer,
                                   AsyncSocketOpts_ID optID,
                                   void *valuePtr,
                                   socklen_t *outBufLen);
static void AsyncTCPSocketListenerError(int error,
                                        AsyncSocket *asock,
                                        void *clientData);


/* Local constants. */

static const AsyncSocketVTable asyncTCPSocketVTable = {
   AsyncSocketGetState,
   AsyncTCPSocketSetOption,
   AsyncTCPSocketGetOption,
   AsyncTCPSocketGetGenericErrno,
   AsyncTCPSocketGetFd,
   AsyncTCPSocketGetRemoteIPStr,
   AsyncTCPSocketGetRemotePort,
   AsyncTCPSocketGetINETIPStr,
   AsyncTCPSocketGetPort,
   AsyncTCPSocketSetCloseOptions,
   AsyncTCPSocketConnectSSL,
   AsyncTCPSocketStartSslConnect,
   AsyncTCPSocketAcceptSSL,
   AsyncTCPSocketStartSslAccept,
   AsyncTCPSocketFlush,
   AsyncTCPSocketRecv,
   AsyncTCPSocketRecvPassedFd,
   AsyncTCPSocketGetReceivedFd,
   AsyncTCPSocketSend,
   AsyncTCPSocketIsSendBufferFull,
   NULL,                        /* getNetworkStats */
   NULL,                        /* getSNIHostname */
   AsyncTCPSocketClose,
   AsyncTCPSocketCloseWrite,
   AsyncTCPSocketCancelRecv,
   AsyncTCPSocketCancelCbForClose,
   AsyncTCPSocketGetLocalVMCIAddress,
   AsyncTCPSocketGetRemoteVMCIAddress,
   NULL,                        /* getWebSocketError */
   NULL,                        /* getWebSocketURI */
   NULL,                        /* getWebSocketCookie */
   NULL,                        /* getWebSocketCloseStatus */
   NULL,                        /* getWebSocketProtocol */
   NULL,                        /* setWebSocketCookie */
   NULL,                        /* setDelayWebSocketUpgradeResponse */
   AsyncTCPSocketRecvBlocking,
   AsyncTCPSocketRecvPartialBlocking,
   AsyncTCPSocketSendBlocking,
   AsyncTCPSocketDoOneMsg,
   AsyncTCPSocketWaitForConnection,
   AsyncTCPSocketWaitForReadMultiple,
   AsyncTCPSocketPeek,
   AsyncTCPSocketDestroy
};


/* Function bodies. */

/*
 *----------------------------------------------------------------------
 *
 * BaseSocket --
 *
 *      Return a pointer to the tcp socket's base class.
 *
 *----------------------------------------------------------------------
 */

static INLINE AsyncSocket *
BaseSocket(AsyncTCPSocket *s)
{
   ASSERT((void *)s == (void *)&s->base);
   return &s->base;
}


/*
 *----------------------------------------------------------------------
 *
 * TCPSocket --
 *
 *      Cast a generic AsyncSocket pointer to AsyncTCPSocket, after
 *      asserting this is legal.
 *
 *----------------------------------------------------------------------
 */

static INLINE AsyncTCPSocket *
TCPSocket(AsyncSocket *s)
{
   ASSERT(s->vt == &asyncTCPSocketVTable);
   ASSERT(s == &((AsyncTCPSocket *)s)->base);
   return (AsyncTCPSocket *)s;
}


/*
 *----------------------------------------------------------------------
 *
 * TCPSocketLock --
 * TCPSocketUnlock --
 * TCPSocketIsLocked --
 * TCPSocketAddRef --
 * TCPSocketRelease --
 * TCPSocketPollParams --
 * TCPSocketGetState --
 * TCPSocketSetState --
 * TCPSocketHandleError --
 *
 *      AsyncTCPSocket versions of base class interfaces.  These
 *      simply invoke the corresponding function on the base class
 *      pointer.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
AsyncTCPSocketLock(AsyncTCPSocket *asock)
{
   AsyncSocketLock(BaseSocket(asock));
}

static INLINE void
AsyncTCPSocketUnlock(AsyncTCPSocket *asock)
{
   AsyncSocketUnlock(BaseSocket(asock));
}

static INLINE Bool
AsyncTCPSocketIsLocked(AsyncTCPSocket *asock)
{
   return AsyncSocketIsLocked(BaseSocket(asock));
}

static INLINE void
AsyncTCPSocketAddRef(AsyncTCPSocket *asock)
{
   AsyncSocketAddRef(BaseSocket(asock));
}

static INLINE void
AsyncTCPSocketRelease(AsyncTCPSocket *asock)
{
   AsyncSocketRelease(BaseSocket(asock));
}

static INLINE AsyncSocketPollParams *
AsyncTCPSocketPollParams(AsyncTCPSocket *asock)
{
   return AsyncSocketGetPollParams(BaseSocket(asock));
}

static INLINE AsyncSocketState
AsyncTCPSocketGetState(AsyncTCPSocket *asock)
{
   return AsyncSocketGetState(BaseSocket(asock));
}

static INLINE void
AsyncTCPSocketSetState(AsyncTCPSocket *asock, AsyncSocketState state)
{
   AsyncSocketSetState(BaseSocket(asock), state);
}

static INLINE void
AsyncTCPSocketHandleError(AsyncTCPSocket *asock, int error)
{
   AsyncSocketHandleError(BaseSocket(asock), error);
}


/*
 *----------------------------------------------------------------------
 *
 * TCPSOCKWARN --
 * TCPSOCKLOG --
 * TCPSOCKLG0 --
 *
 *      AsyncTCPSocket versions of base class logging macros.  These
 *      simply invoke the corresponding macro on the base class
 *      pointer.
 *
 *----------------------------------------------------------------------
 */

/* gcc needs special syntax to handle zero-length variadic arguments */
#if defined(_MSC_VER)
#define TCPSOCKWARN(a, fmt, ...) \
   ASOCKWARN(BaseSocket(a), fmt, __VA_ARGS__)
#define TCPSOCKLOG(_level, a, fmt, ...) \
   ASOCKLOG(_level, BaseSocket(a), fmt, __VA_ARGS__)
#define TCPSOCKLG0(a, fmt, ...) \
   ASOCKLG0(BaseSocket(a), fmt, __VA_ARGS__)
#else
#define TCPSOCKWARN(a, fmt, ...) \
   ASOCKWARN(BaseSocket(a), fmt, ##__VA_ARGS__)
#define TCPSOCKLOG(_level, a, fmt, ...) \
   ASOCKLOG(_level, BaseSocket(a), fmt, ##__VA_ARGS__)
#define TCPSOCKLG0(a, fmt, ...) \
   ASOCKLG0(BaseSocket(a), fmt, ##__VA_ARGS__)
#endif

/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocket_Init --
 *
 *      Initializes the host's socket library. NOP on Posix.
 *      On Windows, calls WSAStartup().
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *      On Windows, loads winsock library.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncTCPSocket_Init(void)
{
#ifdef _WIN32
   WSADATA wsaData;
   WORD versionRequested = MAKEWORD(2, 0);
   return WSAStartup(versionRequested, &wsaData) ?
             ASOCKERR_GENERIC : ASOCKERR_SUCCESS;
#endif
   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetFd --
 *
 *      Returns the fd for this socket.  If listening, return one of
 *      the asock6/asock4 fds.
 *
 * Results:
 *      File descriptor.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetFd(AsyncSocket *base)         // IN
{
   AsyncTCPSocket *asock = TCPSocket(base);

   if (asock->fd != -1) {
      return asock->fd;
   } else if (asock->listenAsock4 && asock->listenAsock4->fd != -1) {
      return asock->listenAsock4->fd;
   } else if (asock->listenAsock6 && asock->listenAsock6->fd != -1) {
      return asock->listenAsock6->fd;
   } else {
      return -1;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetAddr --
 *
 *      Given an AsyncTCPSocket object, return the sockaddr associated with the
 *      requested address family's file descriptor if available.
 *
 *      Passing AF_UNSPEC to socketFamily will provide you with the first
 *      usable sockaddr found (if multiple are available), with a preference
 *      given to IPv6.
 *
 * Results:
 *      ASOCKERR_SUCCESS. ASOCKERR_INVAL if there is no socket associated with
 *      address family requested. ASOCKERR_GENERIC for all other errors.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetAddr(AsyncTCPSocket *asock,             // IN
                      int socketFamily,                  // IN
                      struct sockaddr_storage *outAddr,  // OUT
                      socklen_t *outAddrLen)             // IN/OUT
{
   AsyncTCPSocket *tempAsock;
   int tempFd;
   struct sockaddr_storage addr;
   socklen_t addrLen = sizeof addr;

   if (asock->fd != -1) {
      tempAsock = asock;
   } else if ((socketFamily == AF_UNSPEC || socketFamily == AF_INET6) &&
              asock->listenAsock6 && asock->listenAsock6->fd != -1) {
      tempAsock = asock->listenAsock6;
   } else if ((socketFamily == AF_UNSPEC || socketFamily == AF_INET) &&
              asock->listenAsock4 && asock->listenAsock4->fd != -1) {
      tempAsock = asock->listenAsock4;
   } else {
      return ASOCKERR_INVAL;
   }

   ASSERT(AsyncTCPSocketIsLocked(tempAsock));
   tempFd = tempAsock->fd;

   if (getsockname(tempFd, (struct sockaddr*)&addr, &addrLen) == 0) {
      if (socketFamily != AF_UNSPEC && addr.ss_family != socketFamily) {
         return ASOCKERR_INVAL;
      }

      memcpy(outAddr, &addr, Min(*outAddrLen, addrLen));
      *outAddrLen = addrLen;
      return ASOCKERR_SUCCESS;
   } else {
      TCPSOCKWARN(tempAsock, "%s: could not locate socket.\n", __FUNCTION__);
      return ASOCKERR_GENERIC;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetRemoteIPStr --
 *
 *      Given an AsyncTCPSocket object, returns the remote IP address
 *      associated with it, or an error if the request is meaningless
 *      for the underlying connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetRemoteIPStr(AsyncSocket *base,      // IN
                             const char **ipRetStr)  // OUT
{
   AsyncTCPSocket *asock = TCPSocket(base);
   int ret = ASOCKERR_SUCCESS;

   ASSERT(asock);
   ASSERT(ipRetStr != NULL);

   if (ipRetStr == NULL || asock == NULL ||
       (AsyncTCPSocketGetState(asock) != AsyncSocketConnected &&
        AsyncTCPSocketGetState(asock) != AsyncSocketConnectedRdOnly) ||
       (asock->remoteAddrLen != sizeof (struct sockaddr_in) &&
        asock->remoteAddrLen != sizeof (struct sockaddr_in6))) {
      ret = ASOCKERR_GENERIC;
   } else {
      char addrBuf[NI_MAXHOST];

      if (Posix_GetNameInfo((struct sockaddr *)&asock->remoteAddr,
                            asock->remoteAddrLen, addrBuf,
                            sizeof addrBuf, NULL, 0, NI_NUMERICHOST) != 0) {
         ret = ASOCKERR_GENERIC;
      } else {
         *ipRetStr = Util_SafeStrdup(addrBuf);
      }
   }

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetRemotePort --
 *
 *      Given an AsyncTCPSocket object, returns the remote port
 *      associated with it, or an error if the request is meaningless
 *      for the underlying connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetRemotePort(AsyncSocket *base,  // IN
                            uint32 *port)       // OUT
{
   AsyncTCPSocket *asock = TCPSocket(base);
   int ret = ASOCKERR_SUCCESS;

   ASSERT(asock);

   if (asock == NULL ||
     (AsyncTCPSocketGetState(asock) != AsyncSocketConnected &&
      AsyncTCPSocketGetState(asock) != AsyncSocketConnectedRdOnly) ||
      (asock->remoteAddrLen != sizeof(struct sockaddr_in) &&
       asock->remoteAddrLen != sizeof(struct sockaddr_in6))) {
      ret = ASOCKERR_GENERIC;
   } else {
      *port = AsyncTCPSocketGetPortFromAddr(&asock->remoteAddr);
   }

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetINETIPStr --
 *
 *      Given an AsyncTCPSocket object, returns the IP addresses associated with
 *      the requested address family's file descriptor if available.
 *
 *      Passing AF_UNSPEC to socketFamily will provide you with the first
 *      usable IP address found (if multiple are available), with a preference
 *      given to IPv6.
 *
 *      It is the caller's responsibility to free ipRetStr.
 *
 * Results:
 *      ASOCKERR_SUCCESS. ASOCKERR_INVAL if there is no socket associated with
 *      address family requested. ASOCKERR_GENERIC for all other errors.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetINETIPStr(AsyncSocket *base,   // IN
                           int socketFamily,    // IN
                           char **ipRetStr)     // OUT
{
   AsyncTCPSocket *asock = TCPSocket(base);
   struct sockaddr_storage addr;
   socklen_t addrLen = sizeof addr;
   int ret;

   ASSERT(AsyncTCPSocketIsLocked(asock));

   ret = AsyncTCPSocketGetAddr(asock, socketFamily, &addr, &addrLen);
   if (ret == ASOCKERR_SUCCESS) {
      char addrBuf[NI_MAXHOST];

      if (ipRetStr == NULL) {
         TCPSOCKWARN(asock, "%s: Output string is not usable.\n", __FUNCTION__);
         ret = ASOCKERR_INVAL;
      } else if (Posix_GetNameInfo((struct sockaddr *)&addr, addrLen, addrBuf,
                                   sizeof addrBuf, NULL, 0,
                                   NI_NUMERICHOST) == 0) {
         *ipRetStr = Util_SafeStrdup(addrBuf);
      } else {
         TCPSOCKWARN(asock, "%s: could not find IP address.\n", __FUNCTION__);
         ret = ASOCKERR_GENERIC;
      }
   }

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetLocalVMCIAddress --
 *
 *      Given an AsyncTCPSocket object, returns the local VMCI context ID and
 *      port number associated with it, or an error if the request is
 *      meaningless for the underlying connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetLocalVMCIAddress(AsyncSocket *base,   // IN
                                  uint32 *cid,         // OUT: optional
                                  uint32 *port)        // OUT: optional
{
   AsyncTCPSocket *asock = TCPSocket(base);
   ASSERT(asock);

   if (asock->localAddrLen != sizeof(struct sockaddr_vm)) {
      return ASOCKERR_GENERIC;
   }

   if (cid != NULL) {
      *cid = ((struct sockaddr_vm *)&asock->localAddr)->svm_cid;
   }

   if (port != NULL) {
      *port = ((struct sockaddr_vm *)&asock->localAddr)->svm_port;
   }

   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetRemoteVMCIAddress --
 *
 *      Given an AsyncTCPSocket object, returns the remote VMCI context ID and
 *      port number associated with it, or an error if the request is
 *      meaningless for the underlying connection.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetRemoteVMCIAddress(AsyncSocket *base,   // IN
                                   uint32 *cid,         // OUT: optional
                                   uint32 *port)        // OUT: optional
{
   AsyncTCPSocket *asock = TCPSocket(base);
   ASSERT(asock);

   if (asock->remoteAddrLen != sizeof(struct sockaddr_vm)) {
      return ASOCKERR_GENERIC;
   }

   if (cid != NULL) {
      *cid = ((struct sockaddr_vm *)&asock->remoteAddr)->svm_cid;
   }

   if (port != NULL) {
      *port = ((struct sockaddr_vm *)&asock->remoteAddr)->svm_port;
   }

   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketListenImpl --
 *
 *      Initializes, binds, and listens on pre-populated address structure.
 *
 * Results:
 *      New AsyncTCPSocket in listening state or NULL on error.
 *
 * Side effects:
 *      Creates new socket, binds and listens.
 *
 *----------------------------------------------------------------------------
 */

static AsyncTCPSocket *
AsyncTCPSocketListenImpl(struct sockaddr_storage *addr,      // IN
                         socklen_t addrLen,                  // IN
                         AsyncSocketConnectFn connectFn,     // IN
                         void *clientData,                   // IN
                         AsyncSocketPollParams *pollParams,  // IN: optional
                         int *outError)                      // OUT: optional
{
   AsyncTCPSocket *asock = AsyncTCPSocketInit(addr->ss_family, pollParams,
                                              outError);

   if (asock != NULL) {
      if (AsyncTCPSocketBind(asock, addr, addrLen, outError) &&
          AsyncTCPSocketListen(asock, connectFn, clientData, outError)) {
         return asock;
      }
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketListenerCreateImpl --
 *
 *      Listens on specified address and/or port for resolved/requested socket
 *      family and accepts new connections. Fires the connect callback with
 *      new AsyncTCPSocket object for each connection.
 *
 * Results:
 *      New AsyncTCPSocket in listening state or NULL on error.
 *
 * Side effects:
 *      Creates new socket, binds and listens.
 *
 *----------------------------------------------------------------------------
 */

static AsyncTCPSocket *
AsyncTCPSocketListenerCreateImpl(
   const char *addrStr,                // IN: optional
   unsigned int port,                  // IN: optional
   int socketFamily,                   // IN
   AsyncSocketConnectFn connectFn,     // IN
   void *clientData,                   // IN
   AsyncSocketPollParams *pollParams,  // IN
   int *outError)                      // OUT: optional
{
   AsyncTCPSocket *asock = NULL;
   struct sockaddr_storage addr;
   socklen_t addrLen;
   char *ipString = NULL;
   int tempError = ASOCKERR_SUCCESS;
   int getaddrinfoError = AsyncTCPSocketResolveAddr(addrStr, port, socketFamily,
                                                    TRUE, &addr, &addrLen,
                                                    &ipString);

   if (getaddrinfoError == 0) {
      asock = AsyncTCPSocketListenImpl(&addr, addrLen, connectFn, clientData,
                                       pollParams,
                                       &tempError);

      if (asock) {
         TCPSOCKLG0(asock,
                    "Created new %s %s listener for (%s)\n",
                    addr.ss_family == AF_INET ? "IPv4" : "IPv6",
                    "socket", ipString);
      } else {
         Log(ASOCKPREFIX "Could not create %s listener socket, error %d: %s\n",
             addr.ss_family == AF_INET ? "IPv4" : "IPv6", tempError,
             AsyncSocket_Err2String(tempError));
      }
      free(ipString);
   } else {
      Log(ASOCKPREFIX "Could not resolve listener socket address.\n");
      tempError = ASOCKERR_ADDRUNRESV;
   }

   if (outError) {
      *outError = tempError;
   }

   return asock;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_Listen --
 *
 *      Listens on specified address and/or port for all resolved socket
 *      families and accepts new connections. Fires the connect callback with
 *      new AsyncTCPSocket object for each connection.
 *
 *      If address string is present and that string is not the "localhost"
 *      loopback, then we will listen on resolved address only.
 *
 *      If address string is NULL or is "localhost" we will listen on all
 *      address families that will resolve on the host.
 *
 *      If port requested is 0, we will let the system assign the first
 *      available port.
 *
 *      If address string is NULL and port requested is not 0, we will listen
 *      on any address for all resolved protocols for the port requested.
 *
 *      If address string is "localhost" and port is 0, we will use the first
 *      port we are given if the host supports multiple address families.
 *      If by chance we try to bind on a port that is available for one
 *      protocol and not the other, we will attempt a second time with the
 *      order of address families reversed.
 *
 *      If address string is NULL, port cannot be 0.
 *
 * Results:
 *      New AsyncTCPSocket in listening state or NULL on error.
 *
 * Side effects:
 *      Creates new socket/s, binds and listens.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_Listen(const char *addrStr,                // IN: optional
                   unsigned int port,                  // IN: optional
                   AsyncSocketConnectFn connectFn,     // IN
                   void *clientData,                   // IN
                   AsyncSocketPollParams *pollParams,  // IN
                   int *outError)                      // OUT: optional
{
   if (addrStr != NULL && *addrStr != '\0' &&
       Str_Strcmp(addrStr, "localhost")) {
      AsyncTCPSocket *asock;

      asock = AsyncTCPSocketListenerCreateImpl(addrStr, port, AF_UNSPEC,
                                               connectFn,
                                               clientData, pollParams,
                                               outError);
      return BaseSocket(asock);
   } else {
      Bool localhost = addrStr != NULL && !Str_Strcmp(addrStr, "localhost");
      unsigned int tempPort = port;
      AsyncTCPSocket *asock6 = NULL;
      AsyncTCPSocket *asock4 = NULL;
      int tempError4;
      int tempError6;

      asock6 = AsyncTCPSocketListenerCreateImpl(addrStr, port, AF_INET6,
                                                connectFn, clientData,
                                                pollParams,
                                                &tempError6);

      if (localhost && port == 0) {
         tempPort = AsyncSocket_GetPort(BaseSocket(asock6));
         if (tempPort == MAX_UINT32) {
            Log(ASOCKPREFIX
                "Could not resolve IPv6 listener socket port number.\n");
            tempPort = port;
         }
      }

      asock4 = AsyncTCPSocketListenerCreateImpl(addrStr, tempPort, AF_INET,
                                                connectFn, clientData,
                                                pollParams,
                                                &tempError4);

      if (localhost && port == 0 && tempError4 == ASOCKERR_BINDADDRINUSE) {
         Log(ASOCKPREFIX "Failed to reuse IPv6 localhost port number for IPv4 "
             "listener socket.\n");
         AsyncSocket_Close(BaseSocket(asock6));

         tempError4 = ASOCKERR_SUCCESS;
         asock4 = AsyncTCPSocketListenerCreateImpl(addrStr, port, AF_INET,
                                                   connectFn, clientData,
                                                   pollParams,
                                                   &tempError4);

         tempPort = AsyncSocket_GetPort(BaseSocket(asock4));
         if (tempPort == MAX_UINT32) {
            Log(ASOCKPREFIX
                "Could not resolve IPv4 listener socket port number.\n");
            tempPort = port;
         }

         tempError6 = ASOCKERR_SUCCESS;
         asock6 = AsyncTCPSocketListenerCreateImpl(addrStr, tempPort, AF_INET6,
                                                   connectFn, clientData,
                                                   pollParams,
                                                   &tempError6);

         if (!asock6 && tempError6 == ASOCKERR_BINDADDRINUSE) {
            Log(ASOCKPREFIX "Failed to reuse IPv4 localhost port number for "
                "IPv6 listener socket.\n");
            AsyncSocket_Close(BaseSocket(asock4));
         }
      }

      if (asock6 && asock4) {
         AsyncTCPSocket *asock;

         asock = AsyncTCPSocketCreate(pollParams);
         AsyncTCPSocketSetState(asock, AsyncSocketListening);
         asock->listenAsock6 = asock6;
         asock->listenAsock4 = asock4;
         AsyncSocket_SetErrorFn(BaseSocket(asock4),
                                AsyncTCPSocketListenerError,
                                asock);
         AsyncSocket_SetErrorFn(BaseSocket(asock6),
                                AsyncTCPSocketListenerError,
                                asock);

         return BaseSocket(asock);
      } else if (asock6) {
         return BaseSocket(asock6);
      } else if (asock4) {
         return BaseSocket(asock4);
      }

      if (outError) {
         /* Client only gets one error and the one for IPv6 is favored. */
         if (!asock6) {
            *outError = tempError6;
         } else if (!asock4) {
            *outError = tempError4;
         } else {
            *outError = ASOCKERR_LISTEN;
         }
      }

      return NULL;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketListenerCreateLoopback --
 *
 *      Listens on loopback interface and port for all resolved socket
 *      families and accepts new connections. Fires the connect callback with
 *      new AsyncTCPSocket object for each connection.
 *
 * Results:
 *      New AsyncTCPSocket in listening state or NULL on error.
 *
 * Side effects:
 *      Creates new socket/s, binds and listens.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_ListenLoopback(unsigned int port,                  // IN
                           AsyncSocketConnectFn connectFn,     // IN
                           void *clientData,                   // IN
                           AsyncSocketPollParams *pollParams,  // IN
                           int *outError)                      // OUT: optional
{
   AsyncTCPSocket *asock6 = NULL;
   AsyncTCPSocket *asock4 = NULL;
   int tempError4;
   int tempError6;

   /*
    * "localhost6" does not work on Windows. "localhost" does
    * not work for IPv6 on old Linux versions like 2.6.18. So,
    * using IP address for both the cases to be consistent.
    */
   asock6 = AsyncTCPSocketListenerCreateImpl("::1", port, AF_INET6,
                                             connectFn, clientData, pollParams,
                                             &tempError6);

   asock4 = AsyncTCPSocketListenerCreateImpl("127.0.0.1", port, AF_INET,
                                             connectFn, clientData, pollParams,
                                             &tempError4);

   if (asock6 && asock4) {
      AsyncTCPSocket *asock;

      asock = AsyncTCPSocketCreate(pollParams);
      AsyncTCPSocketSetState(asock, AsyncSocketListening);
      asock->listenAsock6 = asock6;
      asock->listenAsock4 = asock4;

      return BaseSocket(asock);
   } else if (asock6) {
      return BaseSocket(asock6);
   } else if (asock4) {
      return BaseSocket(asock4);
   }

   if (outError) {
      /* Client only gets one error and the one for IPv6 is favored. */
      if (!asock6) {
         *outError = tempError6;
      } else if (!asock4) {
         *outError = tempError4;
      } else {
         *outError = ASOCKERR_LISTEN;
      }
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocket_ListenVMCI --
 *
 *      Listens on the specified port and accepts new connections. Fires the
 *      connect callback with new AsyncTCPSocket object for each connection.
 *
 * Results:
 *      New AsyncTCPSocket in listening state or NULL on error.
 *
 * Side effects:
 *      Creates new socket, binds and listens.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_ListenVMCI(unsigned int cid,                  // IN
                       unsigned int port,                 // IN
                       AsyncSocketConnectFn connectFn,    // IN
                       void *clientData,                  // IN
                       AsyncSocketPollParams *pollParams, // IN
                       int *outError)                     // OUT: optional
{
   struct sockaddr_vm addr;
   AsyncTCPSocket *asock;
   int vsockDev = -1;
   int tempError = ASOCKERR_SUCCESS;

   memset(&addr, 0, sizeof addr);
   addr.svm_family = VMCISock_GetAFValueFd(&vsockDev);
   addr.svm_cid = cid;
   addr.svm_port = port;

   asock = AsyncTCPSocketListenImpl((struct sockaddr_storage *)&addr,
                                    sizeof addr,
                                    connectFn, clientData, pollParams,
                                    &tempError);
   if (outError) {
      *outError = tempError;
   }

   VMCISock_ReleaseAFValueFd(vsockDev);
   return BaseSocket(asock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketInit --
 *
 *      This is an internal routine that sets up a SOCK_STREAM (TCP) socket.
 *
 * Results:
 *      New AsyncTCPSocket or NULL on error.
 *
 * Side effects:
 *      Creates new socket.
 *
 *----------------------------------------------------------------------------
 */

static AsyncTCPSocket *
AsyncTCPSocketInit(int socketFamily,                  // IN
                   AsyncSocketPollParams *pollParams, // IN
                   int *outError)                     // OUT
{
   AsyncTCPSocket *asock = NULL;
   int error = ASOCKERR_GENERIC;
   int sysErr;
   int fd;

   /*
    * Create a new socket
    */

   if ((fd = socket(socketFamily, SOCK_STREAM, 0)) == -1) {
      sysErr = ASOCK_LASTERROR();
      Warning(ASOCKPREFIX "could not create new socket, error %d: %s\n",
              sysErr, Err_Errno2String(sysErr));
      goto errorNoFd;
   }

   /*
    * Wrap it with an asock object
    */

   if ((asock = AsyncTCPSocketAttachToFd(fd, pollParams, &error)) == NULL) {
      goto error;
   }

   return asock;

error:
   SSLGeneric_close(fd);

errorNoFd:
   if (outError) {
      *outError = error;
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetPortFromAddr --
 *
 *      This is an internal routine that gets a port given an address.  The
 *      address must be in either AF_INET, AF_INET6 or AF_VMCI format.
 *
 * Results:
 *      Port number (in host byte order for INET).
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static unsigned int
AsyncTCPSocketGetPortFromAddr(struct sockaddr_storage *addr)         // IN
{
   ASSERT(NULL != addr);

   if (AF_INET == addr->ss_family) {
      return ntohs(((struct sockaddr_in *)addr)->sin_port);
   } else if (AF_INET6 == addr->ss_family) {
      return ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
#ifndef _WIN32
   } else if (AF_UNIX == addr->ss_family) {
      return MAX_UINT32; // Not applicable
#endif
   } else {
#ifdef VMX86_DEBUG
      int vsockDev = -1;

      ASSERT(VMCISock_GetAFValueFd(&vsockDev) == addr->ss_family);
      VMCISock_ReleaseAFValueFd(vsockDev);

#endif
      return ((struct sockaddr_vm *)addr)->svm_port;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetPort --
 *
 *      Given an AsyncTCPSocket object, returns the port number associated with
 *      the requested address family's file descriptor if available.
 *
 * Results:
 *      Port number in host byte order. MAX_UINT32 on error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static unsigned int
AsyncTCPSocketGetPort(AsyncSocket *base)  // IN
{
   AsyncTCPSocket *asock = TCPSocket(base);
   AsyncTCPSocket *tempAsock;
   struct sockaddr_storage addr;
   socklen_t addrLen = sizeof addr;
   unsigned int ret = MAX_UINT32;

   if (asock->fd != -1) {
      tempAsock = asock;
   } else if (asock->listenAsock6 && asock->listenAsock6->fd != -1) {
      tempAsock = asock->listenAsock6;
   } else if (asock->listenAsock4 && asock->listenAsock4->fd != -1) {
      tempAsock = asock->listenAsock4;
   } else {
      return ret;
   }

   ASSERT(AsyncTCPSocketIsLocked(asock));
   ASSERT(AsyncTCPSocketIsLocked(tempAsock));

   if (AsyncTCPSocketGetAddr(tempAsock, AF_UNSPEC, &addr, &addrLen) ==
       ASOCKERR_SUCCESS) {
      return AsyncTCPSocketGetPortFromAddr(&addr);
   } else {
      return MAX_UINT32;
   }


   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketOSVersionSupportsV4Mapped --
 *
 *      Determine if runtime environment supports IPv4-mapped IPv6 addressed
 *      and all the functionality needed to deal with this scenario.
 *
 * Results:
 *      Returns TRUE if supported.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketOSVersionSupportsV4Mapped(void)
{
#if defined(_WIN32) && !defined(VM_WIN_UWP)
   OSVERSIONINFOW osvi = {sizeof(OSVERSIONINFOW)};

   /*
    * Starting with msvc-12.0 / SDK v8.1 GetVersionEx is deprecated.
    * Bug 1259185 tracks switching to VerifyVersionInfo.
    */

#pragma warning(suppress : 4996) // 'function': was declared deprecated
   GetVersionExW(&osvi);

   /* Windows version is at least Vista or higher */
   return osvi.dwMajorVersion >= 6;
#else
   return TRUE;
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketBind --
 *
 *      This is an internal routine that binds a socket to a port.
 *
 * Results:
 *      Returns TRUE upon success, FALSE upon failure.
 *
 * Side effects:
 *      Socket is bound to a particular port.
 *
 *----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketBind(AsyncTCPSocket *asock,          // IN
                   struct sockaddr_storage *addr,  // IN
                   socklen_t addrLen,              // IN
                   int *outError)                  // OUT
{
   int sysErr;
   unsigned int port;

   ASSERT(NULL != asock);
   ASSERT(NULL != asock->sslSock);
   ASSERT(NULL != addr);

   port = AsyncTCPSocketGetPortFromAddr(addr);
   TCPSOCKLG0(asock, "creating new listening socket on port %d\n", port);

#ifndef _WIN32
   /*
    * Don't ever use SO_REUSEADDR on Windows; it doesn't mean what you think
    * it means.
    */

   if (addr->ss_family == AF_INET || addr->ss_family == AF_INET6) {
      int reuse = port != 0;

      if (setsockopt(asock->fd, SOL_SOCKET, SO_REUSEADDR,
                     (const void *) &reuse, sizeof(reuse)) != 0) {
         sysErr = ASOCK_LASTERROR();
         Warning(ASOCKPREFIX "could not set SO_REUSEADDR, error %d: %s\n",
                 sysErr, Err_Errno2String(sysErr));
      }
   }
#else
   /*
    * Always set SO_EXCLUSIVEADDRUSE on Windows, to prevent other applications
    * from stealing this socket. (Yes, Windows is that stupid).
    */

   {
      int exclusive = 1;

      if (setsockopt(asock->fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
                     (const void *) &exclusive, sizeof(exclusive)) != 0) {
         sysErr = ASOCK_LASTERROR();
         Warning(ASOCKPREFIX "could not set SO_EXCLUSIVEADDRUSE, error %d: "
                 "%s\n", sysErr, Err_Errno2String(sysErr));
      }
   }
#endif

#if defined(IPV6_V6ONLY)
   /*
    * WINDOWS: By default V4MAPPED was not supported until Windows Vista.
    * IPV6_V6ONLY was disabled by default until Windows 7. So if we are binding
    * to a AF_INET6 socket and IPV6_V6ONLY existed, we need to turn it on no
    * matter what the setting is to disable V4 mapping.
    *
    * MAC OSX: Support for IPV6_V6ONLY can be found in 10.5+.
    *
    * LINUX: IPV6_V6ONLY was released after V4MAPPED was implemented. There is
    * no way to turn V4MAPPED off on those systems. The default behavior
    * differs from distro-to-distro so attempt to turn V4MAPPED off on all
    * systems that have IPV6_V6ONLY define. There is no good solution for the
    * case where we cannot enable IPV6_V6ONLY, if we error in this case and do
    * not have a IPv4 option then we render the application useless.
    * See AsyncTCPSocketAcceptInternal for the IN6_IS_ADDR_V4MAPPED validation
    * for incomming addresses to close this loophole.
    */

   if (addr->ss_family == AF_INET6 && AsyncTCPSocketOSVersionSupportsV4Mapped()) {
      int on = 1;

      if (setsockopt(asock->fd, IPPROTO_IPV6, IPV6_V6ONLY,
                     (const void *) &on, sizeof(on)) != 0) {
         Warning(ASOCKPREFIX "Cannot set IPV6_V6ONLY socket option.\n");
      }
   }
#else
#error No compiler definition for IPV6_V6ONLY
#endif

   /*
    * Bind to a port
    */

   if (bind(asock->fd, (struct sockaddr *)addr, addrLen) != 0) {
      sysErr = ASOCK_LASTERROR();
      if (sysErr == ASOCK_EADDRINUSE) {
         *outError = ASOCKERR_BINDADDRINUSE;
      }
      Warning(ASOCKPREFIX "Could not bind socket, error %d: %s\n", sysErr,
              Err_Errno2String(sysErr));
      goto error;
   }

   return TRUE;

error:
   SSL_Shutdown(asock->sslSock);
   free(asock);

   return FALSE;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketListen --
 *
 *      This is an internal routine that calls listen() on a socket.
 *
 * Results:
 *      Returns TRUE upon success, FALSE upon failure.
 *
 * Side effects:
 *      Socket is in listening state.
 *
 *----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketListen(AsyncTCPSocket *asock,             // IN
                     AsyncSocketConnectFn connectFn,    // IN
                     void *clientData,                  // IN
                     int *outError)                     // OUT
{
   VMwareStatus pollStatus;

   ASSERT(NULL != asock);
   ASSERT(NULL != asock->sslSock);

   if (!connectFn) {
      Warning(ASOCKPREFIX "invalid arguments to listen!\n");
      *outError = ASOCKERR_INVAL;
      goto error;
   }

   /*
    * Listen on the socket
    */

   if (listen(asock->fd, 5) != 0) {
      int sysErr = ASOCK_LASTERROR();
      Warning(ASOCKPREFIX "could not listen on socket, error %d: %s\n",
              sysErr, Err_Errno2String(sysErr));
      *outError = ASOCKERR_LISTEN;
      goto error;
   }

   /*
    * Register a read callback to fire each time the socket
    * is ready for accept.
    */

   AsyncTCPSocketLock(asock);
   pollStatus = AsyncTCPSocketPollAdd(asock, TRUE,
                                   POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                   AsyncTCPSocketAcceptCallback);

   if (pollStatus != VMWARE_STATUS_SUCCESS) {
      TCPSOCKWARN(asock, "could not register accept callback!\n");
      *outError = ASOCKERR_POLL;
      AsyncTCPSocketUnlock(asock);
      goto error;
   }
   AsyncTCPSocketSetState(asock, AsyncSocketListening);

   asock->connectFn = connectFn;
   asock->clientData = clientData;
   AsyncTCPSocketUnlock(asock);

   return TRUE;

error:
   SSL_Shutdown(asock->sslSock);
   free(asock);

   return FALSE;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketConnectImpl --
 *
 *      AsyncTCPSocket AF_INET/AF_INET6 connect.
 *
 *      NOTE: This function can block.
 *
 * Results:
 *      AsyncTCPSocket * on success and NULL on failure.
 *      On failure, error is returned in *outError.
 *
 * Side effects:
 *      Allocates an AsyncTCPSocket, registers a poll callback.
 *
 *----------------------------------------------------------------------------
 */

static AsyncTCPSocket *
AsyncTCPSocketConnectImpl(int socketFamily,                  // IN
                          const char *hostname,              // IN
                          unsigned int port,                 // IN
                          int tcpSocketFd,                   // IN
                          AsyncSocketConnectFn connectFn,    // IN
                          void *clientData,                  // IN
                          AsyncSocketConnectFlags flags,     // IN
                          AsyncSocketPollParams *pollParams, // IN
                          int *outError)                     // OUT: optional
{
   struct sockaddr_storage addr;
   int getaddrinfoError;
   int error;
   AsyncTCPSocket *asock;
   char *ipString = NULL;
   socklen_t addrLen;

   /*
    * Resolve the hostname.  Handles dotted decimal strings, too.
    */

   getaddrinfoError = AsyncTCPSocketResolveAddr(hostname, port, socketFamily,
                                                FALSE, &addr, &addrLen,
                                                &ipString);
   if (0 != getaddrinfoError) {
      Log(ASOCKPREFIX "Failed to resolve %s address '%s' and port %u\n",
          socketFamily == AF_INET ? "IPv4" : "IPv6", hostname, port);
      error = ASOCKERR_ADDRUNRESV;
      goto error;
   }

   Log(ASOCKPREFIX "creating new %s socket, connecting to %s (%s)\n",
       socketFamily == AF_INET ? "IPv4" : "IPv6", ipString, hostname);
   free(ipString);

   asock = AsyncTCPSocketConnect(&addr, addrLen, tcpSocketFd,
                                 connectFn, clientData,
                                 flags, pollParams, &error);
   if (!asock) {
      Warning(ASOCKPREFIX "%s connection attempt failed: %s\n",
              socketFamily == AF_INET ? "IPv4" : "IPv6",
              AsyncSocket_MsgError(error));
      goto error;
   }

   return asock;

error:
   if (outError) {
      *outError = error;
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocket_Connect --
 *
 *      AsyncTCPSocket connect. Connection is attempted with AF_INET socket
 *      family, when that fails AF_INET6 is attempted.
 *
 *      NOTE: This function can block.
 *
 * Results:
 *      AsyncTCPSocket * on success and NULL on failure.
 *      On failure, error is returned in *outError.
 *
 * Side effects:
 *      Allocates an AsyncTCPSocket, registers a poll callback.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_Connect(const char *hostname,                // IN
                    unsigned int port,                   // IN
                    AsyncSocketConnectFn connectFn,      // IN
                    void *clientData,                    // IN
                    AsyncSocketConnectFlags flags,       // IN
                    AsyncSocketPollParams *pollParams,   // IN
                    int *outError)                       // OUT: optional
{
   return AsyncSocket_ConnectWithFd(hostname,
                                    port,
                                    -1,
                                    connectFn,
                                    clientData,
                                    flags,
                                    pollParams,
                                    outError);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_ConnectWithFd --
 *
 *      AsyncTCPSocket connect using an existing socket descriptor.
 *      Connection is attempted with AF_INET socket
 *      family, when that fails AF_INET6 is attempted.
 *
 *      Limitation: The ConnectWithFd functionality is currently Windows only.
 *                  Non-Windows platforms & windows-UWP are not supported.
 *
 * Results:
 *      AsyncTCPSocket * on success and NULL on failure.
 *      On failure, error is returned in *outError.
 *
 * Side effects:
 *      Allocates an AsyncTCPSocket, registers a poll callback.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_ConnectWithFd(const char *hostname,                // IN
                          unsigned int port,                   // IN
                          int tcpSocketFd,                     // IN
                          AsyncSocketConnectFn connectFn,      // IN
                          void *clientData,                    // IN
                          AsyncSocketConnectFlags flags,       // IN
                          AsyncSocketPollParams *pollParams,   // IN
                          int *outError)                       // OUT: optional
{
   int error = ASOCKERR_CONNECT;
   AsyncTCPSocket *asock = NULL;

   if (!connectFn || !hostname) {
      error = ASOCKERR_INVAL;
      Warning(ASOCKPREFIX "invalid arguments to connect!\n");
      goto error;
   }

   asock = AsyncTCPSocketConnectImpl(AF_INET, hostname, port,
                                     tcpSocketFd, connectFn, clientData,
                                     flags, pollParams, &error);
   if (!asock) {
      asock = AsyncTCPSocketConnectImpl(AF_INET6, hostname, port,
                                        tcpSocketFd, connectFn, clientData,
                                        flags, pollParams, &error);
   }

error:
   if (!asock && outError) {
      *outError = error;
   }

   return BaseSocket(asock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocket_ConnectVMCI --
 *
 *      AsyncTCPSocket AF_VMCI constructor. Connects to the specified cid:port,
 *      and passes the caller a valid asock via the callback once the
 *      connection has been established.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *      Allocates an AsyncTCPSocket, registers a poll callback.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_ConnectVMCI(unsigned int cid,                  // IN
                        unsigned int port,                 // IN
                        AsyncSocketConnectFn connectFn,    // IN
                        void *clientData,                  // IN
                        AsyncSocketConnectFlags flags,     // IN
                        AsyncSocketPollParams *pollParams, // IN
                        int *outError)                     // OUT: optional
{
   int vsockDev = -1;
   struct sockaddr_vm addr;
   AsyncTCPSocket *asock;

   memset(&addr, 0, sizeof addr);
   addr.svm_family = VMCISock_GetAFValueFd(&vsockDev);
   addr.svm_cid = cid;
   addr.svm_port = port;

   Log(ASOCKPREFIX "creating new socket, connecting to %u:%u\n", cid, port);

   asock = AsyncTCPSocketConnect((struct sockaddr_storage *)&addr,
                                 sizeof addr, -1, connectFn, clientData,
                                 flags, pollParams, outError);

   VMCISock_ReleaseAFValueFd(vsockDev);
   return BaseSocket(asock);
}


#ifndef _WIN32
/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_ConnectUnixDomain --
 *
 *      AsyncTCPSocket AF_UNIX constructor. Connects to the specified unix socket,
 *      and passes the caller a valid asock via the callback once the
 *      connection has been established.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *      Allocates an AsyncTCPSocket, registers a poll callback.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_ConnectUnixDomain(const char *path,                  // IN
                              AsyncSocketConnectFn connectFn,    // IN
                              void *clientData,                  // IN
                              AsyncSocketConnectFlags flags,     // IN
                              AsyncSocketPollParams *pollParams, // IN
                              int *outError)                     // OUT
{
   struct sockaddr_un addr;
   AsyncTCPSocket *asock;

   memset(&addr, 0, sizeof addr);
   addr.sun_family = AF_UNIX;

   if (strlen(path) + 1 > sizeof addr.sun_path) {
      Warning(ASOCKPREFIX "Path '%s' is too long for a unix domain socket!\n", path);
      return NULL;
   }
   Str_Strcpy(addr.sun_path, path, sizeof addr.sun_path);

   Log(ASOCKPREFIX "creating new socket, connecting to %s\n", path);

   asock = AsyncTCPSocketConnect((struct sockaddr_storage *)&addr,
                              sizeof addr, -1, connectFn, clientData,
                              flags, pollParams, outError);
   return asock ? BaseSocket(asock) : NULL;
}
#endif


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketConnectErrorCheck --
 *
 *      Check for error on a connecting socket and fire the connect callback
 *      is any error is found.  This is only used on Windows.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketConnectErrorCheck(void *data)  // IN: AsyncTCPSocket *
{
   AsyncTCPSocket *asock = data;
   Bool removed;
   PollerFunction func = NULL;

   ASSERT(AsyncTCPSocketIsLocked(asock));

   if (AsyncTCPSocketGetState(asock) == AsyncSocketConnecting) {
      int sockErr = 0;
      int sockErrLen = sizeof sockErr;

      if (getsockopt(asock->fd, SOL_SOCKET, SO_ERROR, (void *)&sockErr,
                     (void *)&sockErrLen) == 0) {
         if (sockErr == 0) {
            /* There is no error; keep waiting. */
            return;
         }
         asock->genericErrno = sockErr;
      } else {
         asock->genericErrno = ASOCK_LASTERROR();
      }
      TCPSOCKLG0(asock, "Connection failed: %s\n",
                 Err_Errno2String(asock->genericErrno));
      /* Remove connect callback. */
      removed = AsyncTCPSocketPollRemove(asock, TRUE, POLL_FLAG_WRITE,
                                         asock->internalConnectFn);
      ASSERT(removed);
      func = asock->internalConnectFn;
   }

   /* Remove this callback. */
   removed = AsyncTCPSocketPollRemove(asock, FALSE, POLL_FLAG_PERIODIC,
                                      AsyncTCPSocketConnectErrorCheck);
   ASSERT(removed);
   asock->internalConnectFn = NULL;

   if (func) {
      func(asock);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * SocketProtocolAndTypeMatches --
 *
 *      Discover whether a given socket has the specified protocol family
 *      (PF_INET, PF_INET6, ...) and data transfer type (SOCK_STREAM,
 *      SOCK_DGRAM, ...).
 *
 *      For now, this is supported only on non-UWP Windows platforms.
 *      Other platforms always receive a FALSE result.
 *
 * Results:
 *      True if the socket has the specified family and type, false
 *      otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
SocketProtocolAndTypeMatches(int socketFd,   // IN
                             int protocol,   // IN
                             int type)       // IN
{
#if defined( _WIN32) && !defined(VM_WIN_UWP)
   int ret;
   WSAPROTOCOL_INFO protocolInfo;
   int protocolInfoLen = sizeof protocolInfo;

   ret = getsockopt(socketFd, SOL_SOCKET, SO_PROTOCOL_INFO,
                    (void*)&protocolInfo, &protocolInfoLen);
   if (ret != 0) {
      Warning(ASOCKPREFIX "SO_PROTOCOL_INFO failed on sockFd %d, ",
              "error 0x%x\n",
              socketFd, ASOCK_LASTERROR());
      return FALSE;
   }

   /*
    * Windows is confused about protocol families (the "domain" of the
    * socket, passed as the first argument to the socket() call) and
    * address families (specified in the xx_family member of a sockaddr_xx
    * argument passed to bind()).  The protocol family of the socket is
    * reported in the iAddressFamily of the WSAPROTOCOL_INFO structure.
    */
   return ((protocol == protocolInfo.iAddressFamily) &&
           (type == protocolInfo.iSocketType));
#else

   /*
    * If we need to implement this for other platforms then we can use
    * getsockopt(SO_TYPE) to retrieve the socket type, and on Linux we can
    * use getsockopt(SO_DOMAIN) to retrieve the protocol family, but other
    * platforms might not have SO_DOMAIN.  On those platforms we might be
    * able to infer the protocol family by attempting sockopt calls that
    * only work on certain families.
    *
    * BTW, Linux has thrown in the towel on the distinction between
    * protocol families and address families.  Its socket() man page shows
    * AF_* literals being used for the 'domain' argument instead of PF_*
    * literals.  This works because AF_XX is defined to have the same
    * numeric value as PF_XX for all values of XX.
    */
   Warning(ASOCKPREFIX "discovery of socket protocol and type is "
           "not implemented on this platform\n");
   NOT_IMPLEMENTED();
#endif // defined(_WIN32)
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketConnect --
 *
 *      Internal AsyncTCPSocket constructor.
 *
 * Results:
 *      ASOCKERR_SUCCESS or ASOCKERR_GENERIC.
 *
 * Side effects:
 *      Allocates an AsyncTCPSocket, registers a poll callback.
 *
 *----------------------------------------------------------------------------
 */

static AsyncTCPSocket *
AsyncTCPSocketConnect(struct sockaddr_storage *addr,         // IN
                      socklen_t addrLen,                     // IN
                      int socketFd,                          // IN: optional
                      AsyncSocketConnectFn connectFn,        // IN
                      void *clientData,                      // IN
                      AsyncSocketConnectFlags flags,         // IN
                      AsyncSocketPollParams *pollParams,     // IN
                      int *outError)                         // OUT
{
   int fd;
   VMwareStatus pollStatus;
   AsyncTCPSocket *asock = NULL;
   int error = ASOCKERR_GENERIC;
   int sysErr;

   ASSERT(addr);

   if (!connectFn) {
      error = ASOCKERR_INVAL;
      Warning(ASOCKPREFIX "invalid arguments to connect!\n");
      goto error;
   }

   /*
    * If we were given a socket, verify that it is of the required
    * protocol family and type before using it.  If no socket was given,
    * create a new socket of the appropriate family.  (For the sockets
    * we care about, the required protocol family is numerically the
    * same as the address family provided in the given destination
    * sockaddr, so we can use addr->ss_family whenever we need to
    * specify a protocol family.)
    *
    * For now, passing in a socket is supported only on non-UWP Windows
    * platforms.  The SocketProtocolAndTypeMatches() call will fail on
    * other platforms.
    */
   if (-1 != socketFd) {
      int protocolFamily = addr->ss_family;
      // XXX Logging here is excessive, remove after testing
      if (SocketProtocolAndTypeMatches(socketFd, protocolFamily,
                                       SOCK_STREAM)) {
         Warning(ASOCKPREFIX "using passed-in socket, family %d\n",
                 protocolFamily);
         fd = socketFd;
      } else {
         Warning(ASOCKPREFIX "rejecting passed-in socket, wanted family %d\n",
                 protocolFamily);
         error = ASOCKERR_INVAL;
         goto error;
      }
   } else if ((fd = socket(addr->ss_family, SOCK_STREAM, 0)) == -1) {
      sysErr = ASOCK_LASTERROR();
      Warning(ASOCKPREFIX "failed to create socket, error %d: %s\n",
              sysErr, Err_Errno2String(sysErr));
      error = ASOCKERR_CONNECT;
      goto error;
   }

   /*
    * Wrap it with an asock
    */

   if ((asock = AsyncTCPSocketAttachToFd(fd, pollParams, &error)) == NULL) {
      SSLGeneric_close(fd);
      goto error;
   }


   /*
    * Call connect(), which can either succeed immediately or return an error
    * indicating that the connection is in progress. In the latter case, we
    * can poll the fd for write to find out when the connection attempt
    * has succeeded (or failed). In either case, we want to invoke the
    * caller's connect callback from Poll rather than directly, so if the
    * connection succeeds immediately, we just schedule the connect callback
    * as a one-time (RTime) callback instead.
    */

   AsyncTCPSocketLock(asock);
   if (connect(asock->fd, (struct sockaddr *)addr, addrLen) != 0) {
      if (ASOCK_LASTERROR() == ASOCK_ECONNECTING) {
         TCPSOCKLOG(1, asock,
                    "registering write callback for socket connect\n");
         pollStatus = AsyncTCPSocketPollAdd(asock, TRUE, POLL_FLAG_WRITE,
                                            AsyncTCPSocketConnectCallback);
         if (vmx86_win32 && pollStatus == VMWARE_STATUS_SUCCESS &&
             AsyncTCPSocketPollParams(asock)->iPoll == NULL) {
            /*
             * Work around WSAPoll's bug of not reporting failed connection
             * by periodically (500 ms) checking for error.
             */
            pollStatus = AsyncTCPSocketPollAdd(asock, FALSE, POLL_FLAG_PERIODIC,
                                               AsyncTCPSocketConnectErrorCheck,
                                               500 * 1000);
            if (pollStatus == VMWARE_STATUS_SUCCESS) {
               asock->internalConnectFn = AsyncTCPSocketConnectCallback;
            } else {
               TCPSOCKLG0(asock, "failed to register periodic error check\n");
               AsyncTCPSocketPollRemove(asock, TRUE, POLL_FLAG_WRITE,
                                        AsyncTCPSocketConnectCallback);
            }
         }
      } else {
         sysErr = ASOCK_LASTERROR();
         Log(ASOCKPREFIX "connect failed, error %d: %s\n",
             sysErr, Err_Errno2String(sysErr));

         /*
          * If "network unreachable" or "No route to host"
          * errors happens, explicitly propogate
          * the error to trigger the reconnection if possible.
          */
         error = (sysErr == ASOCK_ENETUNREACH ||
                  sysErr == ASOCK_EHOSTUNREACH) ? ASOCKERR_NETUNREACH :
                                                  ASOCKERR_CONNECT;
         goto errorHaveAsock;
      }
   } else {
      TCPSOCKLOG(2, asock,
                 "socket connected, registering RTime callback for connect\n");
      pollStatus = AsyncTCPSocketPollAdd(asock, FALSE, 0,
                                         AsyncTCPSocketConnectCallback, 0);
   }

   if (pollStatus != VMWARE_STATUS_SUCCESS) {
      TCPSOCKWARN(asock, "failed to register callback in connect!\n");
      error = ASOCKERR_POLL;
      goto errorHaveAsock;
   }

   AsyncTCPSocketSetState(asock, AsyncSocketConnecting);
   asock->connectFn = connectFn;
   asock->clientData = clientData;

   /* Store a copy of the sockaddr_storage so we can look it up later. */
   memcpy(&(asock->remoteAddr), addr, addrLen);
   asock->remoteAddrLen = addrLen;

   AsyncTCPSocketUnlock(asock);

   return asock;

errorHaveAsock:
   SSL_Shutdown(asock->sslSock);
   AsyncTCPSocketUnlock(asock);
   free(asock);

error:
   if (outError) {
      *outError = error;
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketCreate --
 *
 *      AsyncSocket constructor for fields common to all TCP-based
 *      AsyncSocket types.
 *
 * Results:
 *      New AsyncSocket object.
 *
 * Side effects:
 *      Allocates memory.
 *
 *----------------------------------------------------------------------------
 */

static AsyncTCPSocket *
AsyncTCPSocketCreate(AsyncSocketPollParams *pollParams) // IN
{
   AsyncTCPSocket *s;

   s = Util_SafeCalloc(1, sizeof *s);

   AsyncSocketInitSocket(BaseSocket(s), pollParams, &asyncTCPSocketVTable);

   s->fd = -1;
   s->inRecvLoop = FALSE;
   s->sendBufFull = FALSE;
   s->sendBufTail = &(s->sendBufList);
   s->passFd.fd = -1;

   if (pollParams && pollParams->iPoll) {
      s->internalSendFn = AsyncTCPSocketIPollSendCallback;
      s->internalRecvFn = AsyncTCPSocketIPollRecvCallback;
   } else {
      s->internalSendFn = AsyncTCPSocketSendCallback;
      s->internalRecvFn = AsyncTCPSocketRecvCallback;
   }

   return s;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketAttachToSSLSock --
 *
 *      AsyncTCPSocket constructor. Wraps an existing SSLSock object with an
 *      AsyncTCPSocket and returns the latter.
 *
 * Results:
 *      New AsyncTCPSocket object or NULL on error.
 *
 * Side effects:
 *      Allocates memory, makes the underlying fd for the socket non-blocking.
 *
 *----------------------------------------------------------------------------
 */

static AsyncTCPSocket *
AsyncTCPSocketAttachToSSLSock(SSLSock sslSock,                   // IN
                              AsyncSocketPollParams *pollParams, // IN
                              int *outError)                     // OUT
{
   AsyncTCPSocket *s;
   int fd;
   int error;

   ASSERT(sslSock);

   fd = SSL_GetFd(sslSock);

   if ((AsyncTCPSocketMakeNonBlocking(fd)) != ASOCKERR_SUCCESS) {
      int sysErr = ASOCK_LASTERROR();
      Warning(ASOCKPREFIX "failed to make fd %d non-blocking!: %d, %s\n",
              fd, sysErr, Err_Errno2String(sysErr));
      error = ASOCKERR_GENERIC;
      goto error;
   }

   s = AsyncTCPSocketCreate(pollParams);
   AsyncTCPSocketSetState(s, AsyncSocketConnected);
   s->sslSock = sslSock;
   s->fd = fd;

   /* From now on socket is ours. */
   SSL_SetCloseOnShutdownFlag(sslSock);
   TCPSOCKLOG(1, s, "new asock id %u attached to fd %d\n", s->base.id, s->fd);

   return s;

error:
   if (outError) {
      *outError = error;
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketAttachToFd --
 *
 *      AsyncTCPSocket constructor. Wraps a valid socket fd with an
 *      AsyncTCPSocket object.
 *
 * Results:
 *      New AsyncTCPSocket or NULL on error.
 *
 * Side effects:
 *      If function succeeds, fd is owned by AsyncTCPSocket and should not be
 *      used (f.e. closed) anymore.
 *
 *----------------------------------------------------------------------------
 */

static AsyncTCPSocket *
AsyncTCPSocketAttachToFd(int fd,                             // IN
                         AsyncSocketPollParams *pollParams,  // IN
                         int *outError)                      // OUT
{
   SSLSock sslSock;
   AsyncTCPSocket *asock;

   /*
    * Create a new SSL socket object with the current socket
    */

   if (!(sslSock = SSL_New(fd, FALSE))) {
      if (outError) {
         *outError = ENOMEM;
      }
      LOG(0, ASOCKPREFIX "failed to create SSL socket object\n");

      return NULL;
   }
   asock = AsyncTCPSocketAttachToSSLSock(sslSock, pollParams, outError);
   if (asock) {
      return asock;
   }
   SSL_Shutdown(sslSock);

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_AttachToFd --
 *
 *      Wrap a pre-existing file descriptor in an AsyncSocket entity.
 *
 * Results:
 *      New AsyncSocket or NULL on error.
 *
 * Side effects:
 *      If function succeeds, fd is owned by AsyncSocket and should not be
 *      used (f.e. closed) anymore.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_AttachToFd(int fd,                            // IN
                       AsyncSocketPollParams *pollParams, // IN
                       int *outError)                     // OUT
{
   AsyncTCPSocket *asock;
   asock = AsyncTCPSocketAttachToFd(fd, pollParams, outError);
   return BaseSocket(asock);
}

/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_AttachToSSLSock --
 *
 *      Wrap a pre-existing SSLSock in an AsyncSocket entity.
 *
 * Results:
 *      New AsyncSocket or NULL on error.
 *
 * Side effects:
 *      If function succeeds, fd is owned by AsyncSocket and should not be
 *      used (f.e. closed) anymore.
 *
 *----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_AttachToSSLSock(SSLSock sslSock,                   // IN
                            AsyncSocketPollParams *pollParams, // IN
                            int *outError)                     // OUT
{
   AsyncTCPSocket *asock;
   asock = AsyncTCPSocketAttachToSSLSock(sslSock, pollParams, outError);
   return BaseSocket(asock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketRegisterRecvCb --
 *
 *      Register poll callbacks as required to be notified when data is ready
 *      following a AsyncTCPSocket_Recv call.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Could register poll callback.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketRegisterRecvCb(AsyncTCPSocket *asock) // IN:
{
   int retVal = ASOCKERR_SUCCESS;
   Bool peek = asock->flags & ASOCK_FLAG_PEEK;

   if (!asock->recvCb) {
      VMwareStatus pollStatus;

      /*
       * Register the Poll callback
       */

      TCPSOCKLOG(3, asock, "installing %s periodic poll callback\n",
                 peek ? "peek" : "recv");

      pollStatus = AsyncTCPSocketPollAdd(asock, TRUE,
                                      POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                      asock->internalRecvFn);

      if (pollStatus != VMWARE_STATUS_SUCCESS) {
         TCPSOCKWARN(asock, "failed to install %s callback!\n",
                     peek ? "peek" : "recv");
         retVal = ASOCKERR_POLL;
         goto out;
      }
      asock->recvCb = TRUE;
   }

   /*
    * !peek comes before other checks because SSL may not be initialized for
    * peeks, and also because peek ignores data buffered in SSL.
    */
   if (!peek && AsyncTCPSocketHasDataPending(asock) && !asock->inRecvLoop) {
      TCPSOCKLOG(0, asock, "installing recv RTime poll callback\n");
      if (AsyncTCPSocketPollAdd(asock, FALSE, 0, asock->internalRecvFn, 0) !=
          VMWARE_STATUS_SUCCESS) {
         retVal = ASOCKERR_POLL;
         goto out;
      }
      asock->recvCbTimer = TRUE;
   }

out:
   return retVal;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocket_Recv --
 *
 *      Registers a callback that will fire once the specified amount of data
 *      has been received on the socket.
 *
 *      In the case of AsyncTCPSocket_RecvPartial, the callback is fired
 *      once all or part of the data has been received on the socket.
 *
 *      Data that was not retrieved at the last call of SSL_read() could still
 *      be buffered inside the SSL layer and will be retrieved on the next
 *      call to SSL_read(). However poll/select might not mark the socket as
 *      for reading since there might not be any data in the underlying network
 *      socket layer. Hence in the read callback, we keep spinning until all
 *      all the data buffered inside the SSL layer is retrieved before
 *      returning to the poll loop (See AsyncTCPSocketFillRecvBuffer()).
 *
 *      However, we might not have come out of Poll in the first place, e.g.
 *      if this is the first call to AsyncTCPSocket_Recv() after creating a new
 *      connection. In this situation, if there is buffered SSL data pending,
 *      we have to schedule an RTTime callback to force retrieval of the data.
 *      This could also happen if the client calls AsyncTCPSocket_RecvBlocking,
 *      some data is left in the SSL layer, and the client then calls
 *      AsyncTCPSocket_Recv. We use the inRecvLoop variable to detect and handle
 *      this condition, i.e., if inRecvLoop is FALSE, we need to schedule the
 *      RTime callback.
 *
 *      TCP usage:
 *      AsyncTCPSocket_Recv(AsyncTCPSocket *asock,
 *                       void *buf,
 *                       int len,
 *                       AsyncSocketRecvFn recvFn,
 *                       void *clientData)
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Could register poll callback.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketRecv(AsyncSocket *base,   // IN:
                   void *buf,           // IN: unused
                   int len,             // IN: unused
                   Bool fireOnPartial,  // IN:
                   void *cb,            // IN:
                   void *cbData)        // IN:
{
   AsyncTCPSocket *asock = TCPSocket(base);
   int retVal;

   if (!asock->base.errorFn) {
      TCPSOCKWARN(asock, "%s: no registered error handler!\n", __FUNCTION__);
      return ASOCKERR_INVAL;
   }

   /*
    * XXX We might want to allow passing NULL for the recvFn, to indicate that
    *     the client is no longer interested in reading from the socket. This
    *     would be useful e.g. for HTTP, where the client sends a request and
    *     then the client->server half of the connection is closed.
    */

   if (!buf || !cb || len <= 0) {
      Warning(ASOCKPREFIX "Recv called with invalid arguments!\n");
      return ASOCKERR_INVAL;
   }

   ASSERT(AsyncTCPSocketIsLocked(asock));

   if (AsyncTCPSocketGetState(asock) != AsyncSocketConnected &&
       AsyncTCPSocketGetState(asock) != AsyncSocketConnectedRdOnly) {
      TCPSOCKWARN(asock, "recv called but state is not connected!\n");
      return ASOCKERR_NOTCONNECTED;
   }

   if (asock->inBlockingRecv && !asock->inRecvLoop) {
      TCPSOCKWARN(asock, "Recv called while a blocking recv is pending.\n");
      return ASOCKERR_INVAL;
   }

   /*
    * Reset peek flag which may be set from previous peek(), to stop peeking.
    * This is the only place we need to reset it.
    */
   asock->flags &= ~ASOCK_FLAG_PEEK;

   retVal = AsyncTCPSocketRegisterRecvCb(asock);
   if (retVal != ASOCKERR_SUCCESS) {
      return retVal;
   }

   AsyncSocketSetRecvBuf(BaseSocket(asock), buf, len, fireOnPartial,
                         cb, cbData);

   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketRecvPassedFd --
 *
 *      See AsyncTCPSocket_Recv.  Besides that it allows for receiving one
 *      file descriptor...
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Could register poll callback.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketRecvPassedFd(AsyncSocket *base,   // IN/OUT: socket
                           void *buf,           // OUT: buffer with data
                           int len,             // IN: length
                           void *cb,            // IN: completion calback
                           void *cbData)        // IN: callback's data
{
   AsyncTCPSocket *asock = TCPSocket(base);
   int err;

   if (!asock->base.errorFn) {
      TCPSOCKWARN(asock, "%s: no registered error handler!\n", __FUNCTION__);

      return ASOCKERR_INVAL;
   }

   ASSERT(AsyncTCPSocketIsLocked(asock));
   if (asock->passFd.fd != -1) {
      SSLGeneric_close(asock->passFd.fd);
      asock->passFd.fd = -1;
   }
   asock->passFd.expected = TRUE;

   err = AsyncTCPSocketRecv(BaseSocket(asock), buf, len, FALSE, cb, cbData);
   if (err != ASOCKERR_SUCCESS) {
      asock->passFd.expected = FALSE;
   }

   return err;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketPeek --
 *
 *      Peek into the socket buffer. Similar to AsyncTCPSocketRecv except that
 *      this routine does not assume SSL state is initialized for the socket.
 *      Unlike recv, peek does not consider data buffered in the SSL layer. So
 *      peek() is only useful pre-SSL. This is in contrast to tcp asyncsocket
 *      recv() that only does SSL_Read; hence recv() is only suitable post-SSL.
 *
 *      ASOCK_FLAG_PEEK flag is reset in recv() prior to callback registration.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Could register poll callback.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketPeek(AsyncSocket *base,   // IN:
                   void *buf,           // IN: unused
                   int len,             // IN: unused
                   void *cb,            // IN:
                   void *cbData)        // IN:
{
   AsyncTCPSocket *asock = TCPSocket(base);
   int retVal;

   ASSERT(AsyncTCPSocketIsLocked(asock));

   // Disallow peek with pending blocking recv
   if (UNLIKELY(asock->inBlockingRecv && !asock->inRecvLoop)) {
      TCPSOCKWARN(asock, "%s: Cannot peek due to blocking recv\n",
                  __FUNCTION__);
      return ASOCKERR_BUSY;
   }
   /*
    * Disallow peek while in recv callback. We can support this if needed in
    * future just like we allow the reverse today (recv from peek callback).
    */
   if (asock->inRecvLoop && !ASOCK_PEEK(asock)) {
      TCPSOCKWARN(asock, "%s: Cannot peek from recv callback\n", __FUNCTION__);
      return ASOCKERR_BUSY;
   }

   if (base->pollParams.iPoll != NULL) {
      Warning(ASOCKPREFIX "Peek not supported for IVmdbPoll!\n");
      return ASOCKERR_INVAL;
   }

   if (buf == NULL || cb == NULL || len <= 0) {
      Warning(ASOCKPREFIX "Peek called with invalid arguments!\n");
      return ASOCKERR_INVAL;
   }

   if (AsyncTCPSocketGetState(asock) != AsyncSocketConnected &&
       AsyncTCPSocketGetState(asock) != AsyncSocketConnectedRdOnly) {
      TCPSOCKWARN(asock, "peek called but state is not connected!\n");
      return ASOCKERR_NOTCONNECTED;
   }

   /*
    * Set ASOCK_FLAG_PEEK flag to differentiate peek from recv in the common
    * callback. The flag will be reset on next recv in AsyncTCPSocketRecv.
    */
   asock->flags |= ASOCK_FLAG_PEEK;

   retVal = AsyncTCPSocketRegisterRecvCb(asock);
   if (retVal == ASOCKERR_SUCCESS) {
      AsyncSocketSetRecvBuf(BaseSocket(asock), buf, len, TRUE, cb, cbData);
   } else {
      TCPSOCKWARN(asock, "%s: Peek failed with error %d: %s \n", __FUNCTION__,
                  retVal, Err_Errno2String(retVal));
      asock->flags &= ~ASOCK_FLAG_PEEK;
   }
   return retVal;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketPollWork --
 *
 *      Blocks on the specified sockets until there's data pending or a
 *      timeout occurs.
 *
 *      If the asyncsocket is a dual stack listener, parentSock will not be
 *      NULL, and the asock array will contain the IPv4 and v6 sockets.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on system call
 *        failures
 *      ASOCKERR_TIMEOUT if we just didn't receive enough data.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketPollWork(AsyncTCPSocket **asock,     // IN:
                       size_t numSock,             // IN:
                       void *p,                    // IN:
                       Bool read,                  // IN:
                       int timeoutMS,              // IN:
                       AsyncTCPSocket *parentSock, // IN:
                       AsyncTCPSocket **outAsock)  // OUT:
{
   AsyncTCPSocket *warnSock = parentSock ? parentSock : asock[0];
#ifndef _WIN32
   struct pollfd *pfd = (struct pollfd *)p;
#else
   /*
    * We use select() to do this on Windows, since there ain't no poll().
    * Fortunately, select() doesn't have the 1024 fd value limit.
    */

   struct timeval tv;
   struct fd_set rwfds;
   struct fd_set exceptfds;
#endif
   size_t i;
   int retval;

   ASSERT(outAsock != NULL && *outAsock == NULL && asock != NULL &&
          numSock != 0);

   for (i = 0; i < numSock; i++) {
      if (read && SSL_Pending(asock[i]->sslSock)) {
         *outAsock = asock[i];
         return ASOCKERR_SUCCESS;
      }
   }

   while (1) {
#ifndef _WIN32
      for (i = 0; i < numSock; i++) {
         pfd[i].fd = asock[i]->fd;
         pfd[i].events = read ? POLLIN : POLLOUT;
      }

      if (parentSock != NULL) {
         AsyncTCPSocketUnlock(parentSock);
         retval = poll(pfd, numSock, timeoutMS);
         AsyncTCPSocketLock(parentSock);
      } else {
         for (i = numSock; i-- > 0; ) {
            AsyncTCPSocketUnlock(asock[i]);
         }
         retval = poll(pfd, numSock, timeoutMS);
         for (i = 0; i < numSock; i++) {
            AsyncTCPSocketLock(asock[i]);
         }
      }
#else
      tv.tv_sec = timeoutMS / 1000;
      tv.tv_usec = (timeoutMS % 1000) * 1000;

      FD_ZERO(&rwfds);
      FD_ZERO(&exceptfds);

      for (i = 0; i < numSock; i++) {
         FD_SET(asock[i]->fd, &rwfds);
         FD_SET(asock[i]->fd, &exceptfds);
      }

      if (parentSock != NULL) {
         AsyncTCPSocketUnlock(parentSock);
         retval = select(1, read ? &rwfds : NULL, read ? NULL : &rwfds,
                         &exceptfds, timeoutMS >= 0 ? &tv : NULL);
         AsyncTCPSocketLock(parentSock);
      } else {
         for (i = numSock; i-- > 0; ) {
            AsyncTCPSocketUnlock(asock[i]);
         }
         retval = select(1, read ? &rwfds : NULL, read ? NULL : &rwfds,
                         &exceptfds, timeoutMS >= 0 ? &tv : NULL);
         for (i = 0; i < numSock; i++) {
            AsyncTCPSocketLock(asock[i]);
         }
      }
#endif

      switch (retval) {
      case 0:
         /*
          * No sockets were ready within the specified time.
          */
         TCPSOCKLG0(warnSock, "%s: Timeout waiting for a ready socket.\n",
                    __FUNCTION__);
         return ASOCKERR_TIMEOUT;

      case -1: {
         int sysErr = ASOCK_LASTERROR();

         if (sysErr == EINTR) {
            /*
             * We were somehow interrupted by signal. Let's loop and retry.
             * XXX: update the timeout by the amount we had previously waited.
             */

            TCPSOCKLG0(warnSock, "%s: Socket interrupted by a signal.\n",
                       __FUNCTION__);
            continue;
         }

         if (parentSock != NULL) {
            parentSock->genericErrno = sysErr;
         } else {
            for (i = 0; i < numSock; i++) {
               asock[i]->genericErrno = sysErr;
            }
         }

         TCPSOCKLG0(warnSock, "%s: Failed with error %d: %s\n", __FUNCTION__,
                    sysErr, Err_Errno2String(sysErr));
         return ASOCKERR_GENERIC;
      }
      default: {
         Bool failed = FALSE;

#ifndef _WIN32
         for (i = 0; i < numSock; i++) {
            if (pfd[i].revents & (POLLERR | POLLNVAL)) {
               failed = TRUE;
            }
         }
#else
         for (i = 0; i < numSock; i++) {
            if (FD_ISSET(asock[i]->fd, &exceptfds)) {
               failed = TRUE;
            }
         }
#endif

         if (failed) {
            int sockErr = 0;
            int sysErr;
            int sockErrLen = sizeof sockErr;

            for (i = 0; i < numSock; i++) {
               if (getsockopt(asock[i]->fd, SOL_SOCKET, SO_ERROR,
                              (void *) &sockErr, (void *) &sockErrLen) == 0) {
                  if (sockErr) {
                     asock[i]->genericErrno = sockErr;
                     TCPSOCKLG0(asock[i],
                                "%s: Socket error lookup returned %d: %s\n",
                                __FUNCTION__, sockErr,
                                Err_Errno2String(sockErr));
                  }
               } else {
                  sysErr = ASOCK_LASTERROR();
                  asock[i]->genericErrno = sysErr;
                  TCPSOCKLG0(asock[i], "%s: Last socket error %d: %s\n",
                             __FUNCTION__, sysErr, Err_Errno2String(sysErr));
               }
            }

            return ASOCKERR_GENERIC;
         }

         /*
          * If one socket is ready, and it wasn't in an exception state,
          * everything is ok. The socket is ready for reading/writing.
          */

#ifndef _WIN32
         for (i = 0; i < numSock; i++) {
            if (pfd[i].revents & (read ? POLLIN : POLLOUT)) {
               *outAsock = asock[i];
               return ASOCKERR_SUCCESS;
            }
         }
#else
         for (i = 0; i < numSock; i++) {
            if (FD_ISSET(asock[i]->fd, &rwfds)) {
               *outAsock = asock[i];
               return ASOCKERR_SUCCESS;
            }
         }
#endif

         TCPSOCKWARN(warnSock, "%s: Failed to return a ready socket.\n",
                     __FUNCTION__);
         return ASOCKERR_GENERIC;
      }
      }
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketPoll --
 *
 *      Blocks on the specified socket until there's data pending or a
 *      timeout occurs.
 *
 *      If the specified socket is a dual stack listener, we will poll on all
 *      listening sockets and will return when one is ready with data for a
 *      connection. If both socket families happen to race with connect data,
 *      we will favor IPv6 for the return.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on system call
 *        failures
 *      ASOCKERR_TIMEOUT if we just didn't receive enough data.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketPoll(AsyncTCPSocket *s,          // IN:
                   Bool read,                  // IN:
                   int timeoutMS,              // IN:
                   AsyncTCPSocket **outAsock)  // OUT:
{
   AsyncTCPSocket *asock[2];
#ifndef _WIN32
   struct pollfd p[2];
#else
   void *p = NULL;
#endif
   size_t numSock = 0;

   if (read && s->fd == -1) {
      if (!s->listenAsock4 && !s->listenAsock6) {
         TCPSOCKLG0(s, "%s: Failed to find listener socket.\n", __FUNCTION__);
         return ASOCKERR_GENERIC;
      }

      if (s->listenAsock6 && s->listenAsock6->fd != -1) {
         asock[numSock++] = s->listenAsock6;
      }
      if (s->listenAsock4 && s->listenAsock4->fd != -1) {
         asock[numSock++] = s->listenAsock4;
      }
   } else {
      asock[numSock++] = s;
   }

   return AsyncTCPSocketPollWork(asock, numSock, p, read, timeoutMS, s,
                                 outAsock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketWaitForReadMultiple --
 *
 *      Blocks on the list of sockets until there's data readable or a
 *      timeout occurs.
 *
 *      Please see the comment in asyncSocketInterface.c for more
 *      information about using this function.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on system call
 *        failures
 *      ASOCKERR_TIMEOUT if no sockets were ready with readable data.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketWaitForReadMultiple(AsyncSocket **asock,   // IN:
                                  size_t numSock,        // IN:
                                  int timeoutMS,         // IN:
                                  int *outIdx)           // OUT:
{
   size_t i;
   int err;
   AsyncTCPSocket *outAsock  = NULL;
#ifndef _WIN32
   struct pollfd *p          = Util_SafeCalloc(numSock, sizeof *p);
#else
   void *p                   = NULL;
#endif

   for (i = 0; i < numSock; i++) {
      ASSERT(AsyncTCPSocketIsLocked(TCPSocket(asock[i])));
   }
   err = AsyncTCPSocketPollWork((AsyncTCPSocket **)asock, numSock, p, TRUE,
                                timeoutMS, NULL, &outAsock);
   for (i = numSock; i-- > 0; ) {
      AsyncTCPSocket *tcpAsock = TCPSocket(asock[i]);
      if (outAsock == tcpAsock) {
         *outIdx = i;
      }
   }

   free(p);
   return err;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketRecvBlocking --
 * AsyncTCPSocketRecvPartialBlocking --
 * AsyncTCPSocketSendBlocking --
 *
 *      Implement "blocking + timeout" operations on the socket. These are
 *      simple wrappers around the AsyncTCPSocketBlockingWork function, which
 *      operates on the actual non-blocking socket, using poll to determine
 *      when it's ok to keep reading/writing. If we can't finish within the
 *      specified time, we give up and return the ASOCKERR_TIMEOUT error.
 *
 *      Note that if these are called from a callback and a lock is being
 *      used (pollParams.lock), the whole blocking operation takes place
 *      with that lock held.  Regardless, it is the caller's responsibility
 *      to make sure the synchronous and asynchronous operations do not mix.
 *
 * Results:
 *      ASOCKERR_SUCCESS if we finished the operation, ASOCKERR_* error codes
 *      otherwise.
 *
 * Side effects:
 *      Reads/writes the socket.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketRecvBlocking(AsyncSocket *base,     // IN
                           void *buf,             // OUT
                           int len,               // IN
                           int *received,         // OUT
                           int timeoutMS)         // IN
{
   AsyncTCPSocket *s = TCPSocket(base);
   return AsyncTCPSocketBlockingWork(s, TRUE, buf, len, received, timeoutMS,
                                     FALSE);
}

static int
AsyncTCPSocketRecvPartialBlocking(AsyncSocket *base,     // IN
                                  void *buf,             // OUT
                                  int len,               // IN
                                  int *received,         // OUT
                                  int timeoutMS)         // IN
{
   AsyncTCPSocket *s = TCPSocket(base);
   return AsyncTCPSocketBlockingWork(s, TRUE, buf, len, received, timeoutMS,
                                     TRUE);
}

static int
AsyncTCPSocketSendBlocking(AsyncSocket *base,         // IN
                           void *buf,                 // OUT
                           int len,                   // IN
                           int *sent,                 // OUT
                           int timeoutMS)             // IN
{
   AsyncTCPSocket *s = TCPSocket(base);
   return AsyncTCPSocketBlockingWork(s, FALSE, buf, len, sent, timeoutMS, FALSE);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketBlockingWork --
 *
 *      Try to complete the specified read/write operation within the
 *      specified time.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketBlockingWork(AsyncTCPSocket *s,  // IN:
                           Bool read,          // IN:
                           void *buf,          // IN/OUT:
                           int len,            // IN:
                           int *completed,     // OUT:
                           int timeoutMS,      // IN:
                           Bool partial)       // IN:
{
   VmTimeType now, done;
   int sysErr;

   if (s == NULL || buf == NULL || len <= 0) {
      Warning(ASOCKPREFIX "Recv called with invalid arguments!\n");

      return ASOCKERR_INVAL;
   }

   if (AsyncTCPSocketGetState(s) != AsyncSocketConnected &&
      /* Allow AsyncSocketConnectedRdOnly iff we are reading */
      (AsyncTCPSocketGetState(s) != AsyncSocketConnectedRdOnly || !read)) {
      TCPSOCKWARN(s, "recv called but state is not connected!\n");
      return ASOCKERR_NOTCONNECTED;
   }

   if (completed) {
      *completed = 0;
   }
   now = Hostinfo_SystemTimerUS() / 1000;
   done = now + timeoutMS;
   do {
      int numBytes, error;
      AsyncTCPSocket *asock = NULL;

      if ((numBytes = read ? SSL_Read(s->sslSock, buf, len)
                           : SSL_Write(s->sslSock, buf, len)) > 0) {
         if (completed) {
            *completed += numBytes;
         }
         len -= numBytes;
         if (len == 0 || partial) {
            return ASOCKERR_SUCCESS;
         }
         buf = (uint8*)buf + numBytes;
      } else if (numBytes == 0) {
         TCPSOCKLG0(s, "blocking %s detected peer closed connection\n",
                    read ? "recv" : "send");
         return ASOCKERR_REMOTE_DISCONNECT;
      } else if ((sysErr = ASOCK_LASTERROR()) != ASOCK_EWOULDBLOCK) {
         s->genericErrno = sysErr;
         TCPSOCKWARN(s, "blocking %s error %d: %s\n", read ? "recv" : "send",
                     sysErr, Err_Errno2String(sysErr));

         return ASOCKERR_GENERIC;
      }

      now = Hostinfo_SystemTimerUS() / 1000;
      if (now >= done && timeoutMS >= 0) {
         return ASOCKERR_TIMEOUT;
      }

      /*
       * Only call in to Poll if we weren't able to send/recv directly
       * off the socket.  But always make sure that the call to Poll()
       * is followed by a read/send.
       */
      error = AsyncTCPSocketPoll(s, read, done - now, &asock);
      if (error != ASOCKERR_SUCCESS) {
         return error;
      }
      ASSERT(asock == s);
   } while (TRUE);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketSend --
 *
 *      Queues the provided data for sending on the socket. If a send callback
 *      is provided, the callback is fired after the data has been written to
 *      the socket. Note that this only guarantees that the data has been
 *      copied to the transmit buffer, we make no promises about whether it
 *      has actually been transmitted, or received by the client, when the
 *      callback is fired.
 *
 *      Send callbacks should also be able to deal with being called if none
 *      or only some of the queued buffer has been transmitted, since the send
 *      callbacks for any remaining buffers are fired by AsyncSocket_Close().
 *      This condition can be detected by checking the len parameter passed to
 *      the send callback.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      May register poll callback or perform I/O.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketSend(AsyncSocket *asock,        // IN
                   void *buf,                 // IN
                   int len,                   // IN
                   AsyncSocketSendFn sendFn,  // IN
                   void *clientData)          // IN
{
   return AsyncTCPSocketSendWithFd(asock, buf, len, -1, sendFn, clientData);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_SendWithFd --
 *
 *      Like the regular AsyncSocket_Send, with the addition of passing a
 *      file descriptor to the recipient.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      May register poll callback or perform I/O.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_SendWithFd(AsyncSocket *asock,        // IN:
                       void *buf,                 // IN:
                       int len,                   // IN:
                       int passFd,                // IN:
                       AsyncSocketSendFn sendFn,  // IN:
                       void *clientData)          // IN:
{
   int ret;

   AsyncSocketLock(asock);
   ret = AsyncTCPSocketSendWithFd(asock, buf, len, passFd, sendFn, clientData);
   AsyncSocketUnlock(asock);

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketSendWithFd --
 *
 *      The meat of queueing a packet to send, optionally also passing a
 *      file descriptor.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      See callers.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketSendWithFd(AsyncSocket *base,         // IN:
                         void *buf,                 // IN:
                         int len,                   // IN:
                         int passFd,                // IN:
                         AsyncSocketSendFn sendFn,  // IN:
                         void *clientData)          // IN:
{
   AsyncTCPSocket *asock = TCPSocket(base);
   int retVal;
   Bool bufferListWasEmpty = FALSE;
   SendBufList **pcur;
   SendBufList *newBuf;

   ASSERT(base->vt == &asyncTCPSocketVTable);

   /*
    * Note: I think it should be fine to send with a length of zero and a
    * buffer of NULL or any other garbage value.  However the code
    * downstream of here is unprepared for it (silently misbehaves).  Hence
    * the <= zero check instead of just a < zero check.  --Jeremy.
    */

   if (!buf || len <= 0) {
      Warning(ASOCKPREFIX "Send called with invalid arguments!"
              "buffer: %p length: %d\n", buf, len);

      return ASOCKERR_INVAL;
   }

   LOG(2, "%s: sending %d bytes\n", __FUNCTION__, len);

   ASSERT(AsyncTCPSocketIsLocked(asock));

   /*
    * In low-latency mode, we want to guard against recursive calls to
    * Send from within the send callback, as these have the capacity
    * to blow up the stack.  However some operations generate implicit
    * sends (such as Close on a websocket) seem like they should be
    * legal from the send callback.  So, allow a small degree of
    * recursive use of the send callback to accomodate these internal
    * paths.
    */
   ASSERT(asock->inLowLatencySendCb < 2);

   if (AsyncTCPSocketGetState(asock) != AsyncSocketConnected) {
      TCPSOCKWARN(asock, "send called but state is not connected!\n");
      return ASOCKERR_NOTCONNECTED;
   }

   /*
    * Allocate and initialize new send buffer entry
    */
   newBuf = Util_SafeCalloc(1, sizeof *newBuf);
   newBuf->buf = buf;
   newBuf->len = len;
   newBuf->passFd = -1;
   if (passFd == -1) {
      newBuf->sendFn = sendFn;
      newBuf->clientData = clientData;
   } else {
      SendBufList *fdBuf = Util_SafeCalloc(1, sizeof *fdBuf);
      fdBuf->passFd = passFd;
      /* Fire callback after both are sent. */
      fdBuf->len = len;
      fdBuf->sendFn = sendFn;
      fdBuf->clientData = clientData;
      newBuf->next = fdBuf;
   }

   /*
    * Append new send buffer to the tail of list.
    */
   *asock->sendBufTail = newBuf;
   asock->sendBufTail = passFd == -1 ? &(newBuf->next) : &(newBuf->next->next);
   bufferListWasEmpty = (asock->sendBufList == newBuf);

   if (bufferListWasEmpty && !asock->sendCb) {
      if (asock->sendLowLatency) {
         /*
          * For low-latency sockets, call the callback directly from
          * this thread.  It is non-blocking and will schedule device
          * callbacks if necessary to complete the operation.
          *
          * Unfortunately we can't make this the default as current
          * consumers of asyncsocket are not expecting the completion
          * callback to be invoked prior to the call to
          * AsyncTCPSocket_Send() returning.
          *
          * Add and release asock reference around the send callback
          * since asock may be closed by a callback invoked during
          * the send workflow.
          */
         AsyncTCPSocketAddRef(asock);
         asock->inLowLatencySendCb++;
         asock->internalSendFn((void *)asock);
         /*
          * Coverity warns that asock->internalSendFn may free asock. This is a
          * false positive because this is a reference counted object and we've
          * added a reference a few lines above.
          */
         /* coverity[deref_after_free:FALSE] */
         asock->inLowLatencySendCb--;
         AsyncTCPSocketRelease(asock);
      } else {
#ifdef _WIN32
         /*
          * If the send buffer list was empty, we schedule a one-time
          * callback to "prime" the output. This is necessary to
          * support the FD_WRITE network event semantic for sockets on
          * Windows (see WSAEventSelect documentation). The event
          * won't signal unless a previous write() on the socket
          * failed with WSAEWOULDBLOCK, so we have to perform at least
          * one partial write before we can start polling for write.
          *
          * XXX: This can be a device callback once all poll
          * implementations know to get around this Windows quirk.
          * Both PollVMX and PollDefault already make 0-byte send() to
          * force WSAEWOULDBLOCK.
          */
         if (AsyncTCPSocketPollAdd(asock, FALSE, 0, asock->internalSendFn,
                                   AsyncTCPSocketPollParams(asock)->iPoll
                                   != NULL ? 1 : 0)
             != VMWARE_STATUS_SUCCESS) {
            retVal = ASOCKERR_POLL;
            TCPSOCKLOG(1, asock, "Failed to register poll callback for send\n");
            goto outUndoAppend;
         }
         asock->sendCbTimer = TRUE;
         asock->sendCb = TRUE;
#else
         if (AsyncTCPSocketPollAdd(asock, TRUE, POLL_FLAG_WRITE,
                                   asock->internalSendFn)
             != VMWARE_STATUS_SUCCESS) {
            retVal = ASOCKERR_POLL;
            TCPSOCKLOG(1, asock, "Failed to register poll callback for send\n");
            goto outUndoAppend;
         }
         asock->sendCb = TRUE;
#endif
      }
   }

   return ASOCKERR_SUCCESS;

outUndoAppend:
   /*
    * Remove the appended buffer from the sendBufList. We always append the
    * buffer to the tail of the list.
    */
   pcur = &asock->sendBufList;
   if (*pcur != NULL) {
      if (!bufferListWasEmpty) {
         do {
            pcur = &((*pcur)->next);
         } while ((*pcur)->next != NULL && (*pcur)->buf != buf);
      }

      if ((*pcur)->buf == buf) {
         if (passFd != -1) {
            /* Free the entry for the fd being passed. */
            free((*pcur)->next);
         }
         free(*pcur);
         *pcur = NULL;
         asock->sendBufTail = pcur;
      }
   }

   return retVal;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketResolveAddr --
 *
 *      Resolves a hostname and port.
 *
 * Results:
 *      Zero upon success.  This returns whatever getaddrinfo() returns.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketResolveAddr(const char *hostname,          // IN
                          unsigned int port,             // IN
                          int family,                    // IN
                          Bool passive,                  // IN
                          struct sockaddr_storage *addr, // OUT
                          socklen_t *addrLen,            // OUT
                          char **addrString)             // OUT
{
   struct addrinfo hints;
   struct addrinfo *aiTop = NULL;
   struct addrinfo *aiIterator = NULL;
   int getaddrinfoError = 0;
   char portString[PORT_STRING_LEN];

   ASSERT(NULL != addr);

   if (port > MAX_UINT16) {
      Log(ASOCKPREFIX "port number requested (%d) is out of range.\n", port);
      return EAI_SERVICE;
   }

   Str_Sprintf(portString, sizeof(portString), "%d", port);
   memset(&hints, 0, sizeof(hints));
   hints.ai_family = family;
   hints.ai_socktype = SOCK_STREAM;
   if (passive) {
      hints.ai_flags = AI_PASSIVE;
   }

   getaddrinfoError = Posix_GetAddrInfo(hostname, portString, &hints, &aiTop);
   if (0 != getaddrinfoError) {
      Log(ASOCKPREFIX "getaddrinfo failed for host %s: %s\n", hostname,
                      gai_strerror(getaddrinfoError));
      goto bye;
   }

   for (aiIterator = aiTop; NULL != aiIterator ; aiIterator =
                                                       aiIterator->ai_next) {
      if ((family == AF_UNSPEC && (aiIterator->ai_family == AF_INET ||
                                   aiIterator->ai_family == AF_INET6)) ||
          family == aiIterator->ai_family) {
         if (addrString != NULL) {
            char tempAddrString[ADDR_STRING_LEN];
            static char unknownAddr[] = "(Unknown)";
#if defined(_WIN32)
            DWORD len = ARRAYSIZE(tempAddrString);

            if (WSAAddressToStringA(aiIterator->ai_addr, aiIterator->ai_addrlen,
                                    NULL, tempAddrString, &len)) {
               *addrString = Util_SafeStrdup(unknownAddr);
            } else {
               *addrString = Util_SafeStrdup(tempAddrString);
            }
#else

            if (aiIterator->ai_family == AF_INET &&
                !inet_ntop(aiIterator->ai_family,
                     &(((struct sockaddr_in *)aiIterator->ai_addr)->sin_addr),
                     tempAddrString, INET6_ADDRSTRLEN)) {
               *addrString = Util_SafeStrdup(unknownAddr);
            } else if (aiIterator->ai_family == AF_INET6 &&
                       !inet_ntop(aiIterator->ai_family,
                  &(((struct sockaddr_in6 *)aiIterator->ai_addr)->sin6_addr),
                  tempAddrString, INET6_ADDRSTRLEN)) {
               *addrString = Util_SafeStrdup(unknownAddr);
            } else {
               *addrString = Str_SafeAsprintf(NULL, aiIterator->ai_family ==
                                                    AF_INET6 ? "[%s]:%u" :
                                                               "%s:%u",
                                              tempAddrString, port);
            }
#endif
         }

         memcpy(addr, aiIterator->ai_addr, aiIterator->ai_addrlen);
         *addrLen = aiIterator->ai_addrlen;

         break;
      }
   }

bye:
   if (NULL != aiTop) {
      Posix_FreeAddrInfo(aiTop);
   }

   return getaddrinfoError;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketFillRecvBuffer --
 *
 *      Called when an asock has data ready to be read via the poll callback.
 *
 * Results:
 *      ASOCKERR_SUCCESS if everything worked,
 *      ASOCKERR_REMOTE_DISCONNECT if peer closed connection gracefully,
 *      ASOCKERR_CLOSED if trying to read from a closed socket.
 *      ASOCKERR_GENERIC for other errors.
 *
 * Side effects:
 *      Reads data, could fire recv completion or trigger socket destruction.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketFillRecvBuffer(AsyncTCPSocket *s)         // IN
{
   int recvd;
   int needed;
   int sysErr = 0;
   int result;
   int pending = 0;

   ASSERT(AsyncTCPSocketIsLocked(s));
   ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketConnected ||
          AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly);

   /*
    * When a socket has received all its desired content and FillRecvBuffer is
    * called again for the same socket, just return ASOCKERR_SUCCESS. The
    * reason we need this hack is that if a client which registered a receive
    * callback asynchronously later changes its mind to do it synchronously,
    * (e.g. aioMgr wait function), then FillRecvBuffer can be potentially be
    * called twice for the same receive event.
    */

   needed = s->base.recvLen - s->base.recvPos;
   if (!s->base.recvBuf && needed == 0) {
      return ASOCKERR_SUCCESS;
   }

   ASSERT(needed > 0);

   AsyncTCPSocketAddRef(s);

   /*
    * See comment in AsyncTCPSocket_Recv
    */

   s->inRecvLoop = TRUE;

   do {
      /*
       * Try to read the remaining bytes to complete the current recv request.
       */

      if (s->passFd.expected) {
         int fd;

         recvd = SSL_RecvDataAndFd(s->sslSock,
                                   (uint8 *) s->base.recvBuf +
                                   s->base.recvPos,
                                   needed, &fd);
         if (fd != -1) {
            s->passFd.fd = fd;
            s->passFd.expected = FALSE;
         }
      } else {
         recvd = SSL_Read(s->sslSock,
                          (uint8 *) s->base.recvBuf +
                          s->base.recvPos,
                          needed);
      }
      /*
       * Do NOT make any system call directly or indirectly here
       * unless you can preserve the system error number
       */
      if (recvd > 0) {
         TCPSOCKLOG(3, s, "need\t%d\trecv\t%d\tremain\t%d\n", needed, recvd,
                    needed - recvd);
         s->sslConnected = TRUE;
         s->base.recvPos += recvd;
         if (AsyncSocketCheckAndDispatchRecv(&s->base, &result)) {
            goto exit;
         }
      } else if (recvd == 0) {
         TCPSOCKLG0(s, "recv detected remote shutdown\n");
         /*
          * We treat this as an error so that the owner can detect orderly
          * shutdown (or half-close) by peer (via the error handler callback).
          */
         result = ASOCKERR_REMOTE_DISCONNECT;
         goto exit;
      } else if ((sysErr = ASOCK_LASTERROR()) == ASOCK_EWOULDBLOCK) {
         TCPSOCKLOG(4, s, "recv would block\n");
         break;
      } else {
         TCPSOCKLG0(s, "recv error %d: %s\n", sysErr, Err_Errno2String(sysErr));
         s->genericErrno = sysErr;
         result = ASOCKERR_GENERIC;
         goto exit;
      }

      /*
       * At this point, s->recvFoo have been updated to point to the
       * next chained Recv buffer. By default we're done at this
       * point, but we may want to continue if the SSL socket has data
       * buffered in userspace already (SSL_Pending).
       */

      needed = s->base.recvLen - s->base.recvPos;
      ASSERT(needed > 0);

      pending = SSL_Pending(s->sslSock);
      needed = MIN(needed, pending);

   } while (needed);

   /*
    * Reach this point only when previous SSL_Pending returns 0 or
    * error is ASOCK_EWOULDBLOCK
    */

   ASSERT(pending == 0 || sysErr == ASOCK_EWOULDBLOCK);

   /*
    * Both a spurious wakeup and receiving any data even if it wasn't enough
    * to fire the callback are both success.  We were ready and now
    * presumably we aren't ready anymore.
    */

   result = ASOCKERR_SUCCESS;

exit:
   s->inRecvLoop = FALSE;
   AsyncTCPSocketRelease(s);

   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketFillPeekBuffer --
 *
 *      Called when asock has data ready to peek via the poll callback.
 *      Internally calls recv system call with MSG_PEEK flag.
 *
 *      Similar to AsyncTCPSocketFillRecvBuffer with key differences:
 *
 *      - peek does non-SSL (clear-text pre-SSL or encrypted) retrieval from the
 *        socket, whereas TCPSocketFillRecvBuffer does an SSL_Read. peek does
 *        not take into account any data buffered at the SSL layer, so only
 *        makes sense prior to SSL setup.
 *
 *      - peek reads from the head of socket buffer, but does not drain it so
 *        subsequent recv/peek will still read the same data.
 *
 *      - if peek and recv async requests overlap, the latest requests
 *        implicitly cancels the existing one.
 *
 *      - nested peeks of same or lesser length into the same buffer are
 *        pointless and dropped. Nested recv() transition to recv callback.
 *
 * Results:
 *      Same as AsyncTCPSocketFillRecvBuffer().
 *
 * Side effects:
 *      Reads data but does not remove it from the socket buffer, could fire
 *      recv completion or trigger socket destruction.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketFillPeekBuffer(AsyncTCPSocket *s,         // IN
                             Bool *retry)               // OUT
{
   int recvd;
   int needed;
   int result;
   int sysErr;
   int length;
   void *buffer;
   int loopCount = 0;

#define MAX_NESTED_PEEKS 8

   ASSERT(AsyncTCPSocketIsLocked(s));
   ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketConnected ||
          AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly);
   ASSERT(s->flags & ASOCK_FLAG_PEEK);

   needed = s->base.recvLen - s->base.recvPos;
   if (!s->base.recvBuf && needed == 0) {
      s->flags &= ~ASOCK_FLAG_PEEK;
      return ASOCKERR_SUCCESS;
   }

   ASSERT(needed > 0);

   AsyncTCPSocketAddRef(s);

   s->inRecvLoop = TRUE;

   // peek loop to service nested peeks
   do {
      TCPSOCKLOG(1, s, "peeking for %d bytes\n", needed);
      length = s->base.recvLen;
      buffer = s->base.recvBuf;

      // recv() with MSG_PEEK
      recvd = recv(SSL_GetFd(s->sslSock),
                   (uint8 *) s->base.recvBuf + s->base.recvPos, needed,
                   MSG_PEEK);
      TCPSOCKLOG(0, s, "peek fetched %d of %d bytes requested\n", recvd,
                 needed);

      if (recvd > 0) {
         s->base.recvPos += recvd;
         if (AsyncSocketCheckAndDispatchRecv(&s->base, &result)) {
            goto exit;
         }
         ASSERT(s->base.recvPos == 0);
      } else if (recvd == 0) {
         TCPSOCKLG0(s, "peek detected remote shutdown\n");
         /*
          * We treat this as an error so that the owner can detect orderly
          * shutdown (or half-close) by peer (via the error handler callback).
          */
         result = ASOCKERR_REMOTE_DISCONNECT;
         goto exit;
      } else if ((sysErr = ASOCK_LASTERROR()) == ASOCK_EWOULDBLOCK) {
         TCPSOCKLOG(4, s, "peek would block\n");
         result = ASOCKERR_SUCCESS;
         break;
      } else {
         TCPSOCKLG0(s, "peek error %d: %s\n", sysErr, Err_Errno2String(sysErr));
         s->genericErrno = sysErr;
         result = ASOCKERR_GENERIC;
         goto exit;
      }

      needed = s->base.recvLen - s->base.recvPos;

      // Handle recv from peek callback using retry loop of the caller
      if (!ASOCK_PEEK(s)) {
         TCPSOCKLOG(0, s, "recv during peek, retry recv callback");
         *retry = TRUE;
         break;
      }

      /*
       * Nested peeks for same or lesser lengths into the same buffer are
       * pointless and unexpected, so we use it as a terminating condition to
       * break the loop and also unregister peek callback.
       */
      if (s->base.recvLen <= length && s->base.recvBuf == buffer) {
         TCPSOCKLG0(s, "cancelling one-shot peek op");
         s->flags &= ~ASOCK_FLAG_PEEK;
         AsyncTCPSocketCancelRecvCb(s);
         break;
      }
   } while (needed && (++loopCount < MAX_NESTED_PEEKS));

   // flag heavy peek nesting
   ASSERT(loopCount < MAX_NESTED_PEEKS);

   result = ASOCKERR_SUCCESS;

exit:
   s->inRecvLoop = FALSE;
   AsyncTCPSocketRelease(s);
   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketDispatchSentBuffer --
 *
 *      Pop off the head of the send buffer list and call its callback.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketDispatchSentBuffer(AsyncTCPSocket *s)         // IN
{
   int result = ASOCKERR_SUCCESS;

   /*
    * We're done with the current buffer, so pop it off and nuke it.
    * We do the list management *first*, so that the list is in a
    * consistent state.
    */

   SendBufList *head = s->sendBufList;
   SendBufList tmp = *head;

   s->sendBufList = head->next;
   if (s->sendBufList == NULL) {
      s->sendBufTail = &(s->sendBufList);
   }
   s->sendPos = 0;
   free(head);

   if (tmp.sendFn) {
      /*
       * Firing the send completion cannot trigger immediate
       * destruction of the socket because we hold a refCount across
       * this and all other application callbacks.  If the socket is
       * closed, however, we need to bubble the information up to the
       * caller in the same way as we do in the Recv callback case.
       */
      ASSERT(s->base.refCount > 1);
      tmp.sendFn(tmp.buf, tmp.len, BaseSocket(s), tmp.clientData);
      if (AsyncTCPSocketGetState(s) == AsyncSocketClosed) {
         TCPSOCKLG0(s, "owner closed connection in send callback\n");
         result = ASOCKERR_CLOSED;
      }
   }

   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketPassFd --
 *
 *      Send a file descriptor on the TCP socket.
 *
 * Results:
 *      1 if successful, -1 if not.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketPassFd(int socket,  // IN:
                     int passFd)  // IN:
{
#ifndef _WIN32
   static unsigned char byte = 0;
   union {
      struct cmsghdr cmsghdr;
      char cbuf[CMSG_SPACE(sizeof passFd)];
   } cbuf_un;
   struct msghdr msghdr = {0};
   struct cmsghdr *cmsghdr;
   struct iovec iov[1];
   int ret;

   iov[0].iov_base = &byte;
   iov[0].iov_len = sizeof byte;
   msghdr.msg_control = cbuf_un.cbuf;
   msghdr.msg_controllen = sizeof cbuf_un.cbuf;
   msghdr.msg_iov = iov;
   msghdr.msg_iovlen = 1;
   cmsghdr = CMSG_FIRSTHDR(&msghdr);
   cmsghdr->cmsg_len = CMSG_LEN(sizeof passFd);
   cmsghdr->cmsg_level = SOL_SOCKET;
   cmsghdr->cmsg_type = SCM_RIGHTS;
   *(int *)CMSG_DATA(cmsghdr) = passFd;

   ret = sendmsg(socket, &msghdr, 0);
   return ret > 0 ? 1 : -1;
#else
   NOT_IMPLEMENTED();
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketWriteBuffers --
 *
 *      The meat of AsyncTCPSocket's sending functionality.  This function
 *      actually writes to the wire assuming there's space in the buffers
 *      for the socket.
 *
 * Results:
 *      ASOCKERR_SUCCESS if everything worked, else ASOCKERR_GENERIC.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketWriteBuffers(AsyncTCPSocket *s)  // IN
{
   int result;

   ASSERT(AsyncTCPSocketIsLocked(s));

   if (s->sendBufList == NULL) {
      return ASOCKERR_SUCCESS;     /* Vacuously true */
   }

   if (AsyncTCPSocketGetState(s) != AsyncSocketConnected) {
      TCPSOCKWARN(s, "write buffers on a disconnected or rdonly socket!\n");
      return ASOCKERR_GENERIC;
   }

   AsyncTCPSocketAddRef(s);

   while (s->sendBufList && AsyncTCPSocketGetState(s) == AsyncSocketConnected) {
      SendBufList *head = s->sendBufList;
      int error = 0;
      int sent = 0;
      int left = head->len - s->sendPos;
      int sizeToSend = head->len;

      if (head->passFd == -1) {
         sent = SSL_Write(s->sslSock,
                          (uint8 *) head->buf + s->sendPos, left);
      } else {
         sizeToSend = 1;
         sent = AsyncTCPSocketPassFd(s->fd, head->passFd);
      }
      /*
       * Do NOT make any system call directly or indirectly here
       * unless you can preserve the system error number
       */
      if (sent > 0) {
         TCPSOCKLOG(3, s, "left\t%d\tsent\t%d\tremain\t%d\n",
                    left, sent, left - sent);
         s->sendBufFull = FALSE;
         s->sslConnected = TRUE;
         if ((s->sendPos += sent) == sizeToSend) {
            result = AsyncTCPSocketDispatchSentBuffer(s);
            if (result != ASOCKERR_SUCCESS) {
               goto exit;
            }
         }
      } else if (sent == 0) {
         TCPSOCKLG0(s, "socket write() should never return 0.\n");
         NOT_REACHED();
      } else if ((error = ASOCK_LASTERROR()) != ASOCK_EWOULDBLOCK) {
         TCPSOCKLG0(s, "send error %d: %s\n", error, Err_Errno2String(error));
         s->genericErrno = error;
         if (error == ASOCK_EPIPE || error == ASOCK_ECONNRESET) {
            result = ASOCKERR_REMOTE_DISCONNECT;
         } else {
            result = ASOCKERR_GENERIC;
         }
         goto exit;
      } else {
         /*
          * Ran out of space to send. This is actually successful completion
          * (our contract obligates us to send as much data as space allows
          * and we fulfilled that).
          *
          * Indicate send buffer is full.
          */

         s->sendBufFull = TRUE;
         break;
      }
   }

   result = ASOCKERR_SUCCESS;

exit:
   AsyncTCPSocketRelease(s);

   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketAcceptInternal --
 *
 *      The meat of 'accept'.  This function can be invoked either via a
 *      poll callback or blocking. We call accept to get the new socket fd,
 *      create a new asock, and call the newFn callback previously supplied
 *      by the call to AsyncTCPSocket_Listen.
 *
 * Results:
 *      ASOCKERR_SUCCESS if everything works, else an error code.
 *      ASOCKERR_GENERIC is returned to hide accept() system call's
 *        nitty-gritty, it implies that we should try accept() again and not
 *        report error to client.
 *      ASOCKERR_ACCEPT to report accept operation's error to client.
 *
 * Side effects:
 *      Accepts on listening fd, creates new asock.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketAcceptInternal(AsyncTCPSocket *s)         // IN
{
   AsyncTCPSocket *newsock;
   int sysErr;
   int fd;
   struct sockaddr_storage remoteAddr;
   socklen_t remoteAddrLen = sizeof remoteAddr;

   ASSERT(AsyncTCPSocketIsLocked(s));
   ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketListening);

   if ((fd = accept(s->fd, (struct sockaddr *)&remoteAddr,
                    &remoteAddrLen)) == -1) {
      sysErr = ASOCK_LASTERROR();
      s->genericErrno = sysErr;
      if (sysErr == ASOCK_EWOULDBLOCK) {
         TCPSOCKWARN(s, "spurious accept notification\n");
#if TARGET_OS_IPHONE
         /*
          * For iOS, while the app is suspended and device's screen is locked,
          * system will reclaim resources from underneath socket(see Apple
          * Technical Note TN2277), the callback function AsyncTCPSocketAcceptCallback()
          * will be invoked repeatedly, to deal with this issue, we need to
          * handle error EWOULDBLOCK.
          */
         return ASOCKERR_ACCEPT;
#else
         return ASOCKERR_GENERIC;
#endif
#ifndef _WIN32
         /*
          * Linux accept() can return ECONNABORTED for connections
          * that closed before we got to actually call accept(), but Windows
          * just ignores this case. So we have to special case for Linux here.
          * We return ASOCKERR_GENERIC here because we still want to continue
          * accepting new connections.
          */

      } else if (sysErr == ECONNABORTED) {
         TCPSOCKLG0(s, "accept: new connection was canceled.\n");

         return ASOCKERR_GENERIC;
#endif
      } else {
         TCPSOCKWARN(s, "accept failed on fd %d, error %d: %s\n",
                     s->fd, sysErr, Err_Errno2String(sysErr));

         return ASOCKERR_ACCEPT;
      }
   }

   if (remoteAddr.ss_family == AF_INET6 &&
       AsyncTCPSocketOSVersionSupportsV4Mapped()) {
      struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&remoteAddr;

      /*
       * Remote address should not be a V4MAPPED address. Validate for the rare
       * case that IPV6_V6ONLY is not defined and V4MAPPED is enabled by
       * default when setting up socket listener.
       */

      if (IN6_IS_ADDR_V4MAPPED(&(addr6->sin6_addr))) {
         TCPSOCKWARN(s, "accept rejected on fd %d due to a IPv4-mapped IPv6 "
                     "remote connection address.\n", s->fd);
         SSLGeneric_close(fd);

         return ASOCKERR_ACCEPT;
      }
   }

   newsock = AsyncTCPSocketAttachToFd(fd, AsyncTCPSocketPollParams(s), NULL);
   if (!newsock) {
      SSLGeneric_close(fd);

      return ASOCKERR_ACCEPT;
   }

   newsock->remoteAddr = remoteAddr;
   newsock->remoteAddrLen = remoteAddrLen;
   AsyncTCPSocketSetState(newsock, AsyncSocketConnected);
   newsock->internalRecvFn = s->internalRecvFn;
   newsock->internalSendFn = s->internalSendFn;

   /*
    * Fire the connect callback:
    */
   s->connectFn(BaseSocket(newsock), s->clientData);

   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketConnectInternal --
 *
 *      The meat of connect.  This function is invoked either via a poll
 *      callback or the blocking API and verifies that connect() succeeded
 *      or reports is failure.  On success we call the registered 'new
 *      connection' function.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it all worked out or ASOCKERR_GENERIC.
 *
 * Side effects:
 *      Creates new asock, fires newFn callback.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketConnectInternal(AsyncTCPSocket *s)         // IN
{
   int optval = 0, optlen = sizeof optval, sysErr;

   ASSERT(AsyncTCPSocketIsLocked(s));
   ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketConnecting);

   /* Remove when bug 859728 is fixed */
   if (vmx86_server && s->remoteAddr.ss_family == AF_UNIX) {
      goto done;
   }

   if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR,
                  (void *) &optval, (void *)&optlen) != 0) {
      sysErr = ASOCK_LASTERROR();
      s->genericErrno = sysErr;
      Warning(ASOCKPREFIX "getsockopt for connect on fd %d failed with "
              "error %d : %s\n", s->fd, sysErr, Err_Errno2String(sysErr));

      return ASOCKERR_GENERIC;
   }

   if (optval != 0) {
      s->genericErrno = optval;
      TCPSOCKLOG(1, s, "connection SO_ERROR: %s\n", Err_Errno2String(optval));
      if (optval == ASOCK_ENETUNREACH || optval == ASOCK_EHOSTUNREACH) {
         return ASOCKERR_NETUNREACH;
      } else {
         return ASOCKERR_CONNECT;
      }
   }

   s->localAddrLen = sizeof s->localAddr;
   if (getsockname(s->fd, (struct sockaddr *)&s->localAddr,
                   &s->localAddrLen) != 0) {
      sysErr = ASOCK_LASTERROR();
      s->genericErrno = sysErr;
      Warning(ASOCKPREFIX "getsockname for connect on fd %d failed with "
              "error %d: %s\n", s->fd, sysErr, Err_Errno2String(sysErr));

      return ASOCKERR_GENERIC;
   }

done:
   AsyncTCPSocketSetState(s, AsyncSocketConnected);
   s->connectFn(BaseSocket(s), s->clientData);

   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetGenericErrno --
 *
 *      Used when an ASOCKERR_GENERIC is returned due to a system error.
 *      The errno that was returned by the system is stored in the asock
 *      struct and returned to the user in this function.
 *
 *      XXX: This function is not thread-safe.  The errno should be returned
 *      in a parameter to any function that can return ASOCKERR_GENERIC.
 *
 * Results:
 *      int error code
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetGenericErrno(AsyncSocket *base)  // IN:
{
   AsyncTCPSocket *asock = TCPSocket(base);
   ASSERT(asock);
   return asock->genericErrno;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketWaitForConnection --
 *
 *      Spins a socket currently listening or connecting until the
 *      connection completes or the allowed time elapses.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on failures, and
 *      ASOCKERR_TIMEOUT if nothing happened in the allotted time.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketWaitForConnection(AsyncSocket *base,  // IN:
                                int timeoutMS)      // IN:
{
   AsyncTCPSocket *s = TCPSocket(base);
   Bool read = FALSE;
   int error;
   VmTimeType now, done;
   Bool removed = FALSE;

   ASSERT(AsyncTCPSocketIsLocked(s));

   if (AsyncTCPSocketGetState(s) == AsyncSocketConnected ||
       AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly) {
      return ASOCKERR_SUCCESS;
   }

   if (AsyncTCPSocketGetState(s) != AsyncSocketListening &&
       AsyncTCPSocketGetState(s) != AsyncSocketConnecting) {
      return ASOCKERR_GENERIC;
   }

   read = AsyncTCPSocketGetState(s) == AsyncSocketListening;

   /*
    * For listening sockets, unregister AsyncTCPSocketAcceptCallback before
    * starting polling and re-register before returning.
    *
    * ConnectCallback() is either registered as a device or rtime callback
    * depending on the prior return value of connect(). So we try to remove it
    * from both.
    */
   if (read) {
      if (s->fd == -1) {
         if (s->listenAsock4) {
            ASSERT(AsyncTCPSocketIsLocked(s->listenAsock4));
            AsyncTCPSocketCancelListenCb(s->listenAsock4);
         }
         if (s->listenAsock6) {
            ASSERT(AsyncTCPSocketIsLocked(s->listenAsock6));
            AsyncTCPSocketCancelListenCb(s->listenAsock6);
         }
      } else {
         AsyncTCPSocketCancelListenCb(s);
      }

      removed = TRUE;
   } else {
      removed = (AsyncTCPSocketPollRemove(s, TRUE, POLL_FLAG_WRITE,
                                          AsyncTCPSocketConnectCallback) ||
                 AsyncTCPSocketPollRemove(s, FALSE, 0,
                                          AsyncTCPSocketConnectCallback));
      ASSERT(removed);
      if (s->internalConnectFn) {
         removed = AsyncTCPSocketPollRemove(s, FALSE, POLL_FLAG_PERIODIC,
                                            AsyncTCPSocketConnectErrorCheck);
         ASSERT(removed);
         s->internalConnectFn = NULL;
      }
   }

   now = Hostinfo_SystemTimerUS() / 1000;
   done = now + timeoutMS;

   do {
      AsyncTCPSocket *asock = NULL;

      error = AsyncTCPSocketPoll(s, read, done - now, &asock);
      if (error != ASOCKERR_SUCCESS) {
         goto out;
      }

      now = Hostinfo_SystemTimerUS() / 1000;

      if (read) {
         if (AsyncTCPSocketAcceptInternal(asock) != ASOCKERR_SUCCESS) {
            TCPSOCKLG0(s, "wait for connection: accept failed\n");

            /*
             * Just fall through, we'll loop and try again as long as we still
             * have time remaining.
             */

         } else {
            error = ASOCKERR_SUCCESS;
            goto out;
         }
      } else {
         error = AsyncTCPSocketConnectInternal(asock);
         goto out;
      }
   } while ((now < done && timeoutMS > 0) || (timeoutMS < 0));

   error = ASOCKERR_TIMEOUT;

out:
   if (read && removed) {
      if (s->fd == -1) {
         if (s->listenAsock4 &&
             AsyncTCPSocketGetState(s->listenAsock4) != AsyncSocketClosed) {
            if (!AsyncTCPSocketAddListenCb(s->listenAsock4)) {
               error = ASOCKERR_POLL;
            }
         }

         if (s->listenAsock6 &&
             AsyncTCPSocketGetState(s->listenAsock6) != AsyncSocketClosed) {
            if (!AsyncTCPSocketAddListenCb(s->listenAsock6)) {
               error = ASOCKERR_POLL;
            }
         }
      } else if (AsyncTCPSocketGetState(s) != AsyncSocketClosed) {
         if (!AsyncTCPSocketAddListenCb(s)) {
            error = ASOCKERR_POLL;
         }
      }
   }

   return error;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketDoOneMsg --
 *
 *      Spins a socket until the specified amount of time has elapsed or
 *      data has arrived / been sent.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on system call
 *         failures
 *      ASOCKERR_BUSY if another thread is in the read callback.
 *      ASOCKERR_TIMEOUT if nothing happened in the allotted time.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketDoOneMsg(AsyncSocket *base, // IN
                       Bool read,         // IN
                       int timeoutMS)     // IN
{
   AsyncTCPSocket *s = TCPSocket(base);
   AsyncTCPSocket *asock = NULL;
   int retVal;

   ASSERT(AsyncTCPSocketIsLocked(s));
   ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketConnected ||
         (AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly && read));

   if (read) {
      if (s->inRecvLoop) {
         /*
          * The recv loop would read the data if there is any and it is
          * not safe to proceed and race with the recv loop.
          */
         TCPSOCKLG0(s, "busy: another thread in recv loop\n");
         return ASOCKERR_BUSY;
      }

      /*
       * Bug 158571: There could other threads polling on the same asyncsocket.
       * If two threads land up polling  on the same socket at the same time,
       * the first thread to be scheduled reads the data from the socket,
       * while the second one blocks infinitely. This hangs the VM. To prevent
       * this, we temporarily remove the poll callback and then reinstate it
       * after reading the data.
       */

      ASSERT(s->recvCb); /* We are supposed to call someone... */
      AsyncTCPSocketAddRef(s);
      AsyncTCPSocketCancelRecvCb(s);
      s->recvCb = TRUE;  /* We need to know if the callback cancel recv. */

      s->inBlockingRecv++;
      retVal = AsyncTCPSocketPoll(s, read, timeoutMS, &asock);
      if (retVal != ASOCKERR_SUCCESS) {
         if (retVal == ASOCKERR_GENERIC) {
            TCPSOCKWARN(s, "%s: failed to poll on the socket during read.\n",
                        __FUNCTION__);
         }
      } else {
         ASSERT(asock == s);
         retVal = AsyncTCPSocketFillRecvBuffer(s);
      }
      s->inBlockingRecv--;

      /*
       * If socket got closed in AsyncTCPSocketFillRecvBuffer, we
       * cannot add poll callback - AsyncSocket_Close() would remove
       * it if we would not remove it above.
       */

      if (AsyncTCPSocketGetState(s) != AsyncSocketClosed && s->recvCb) {
         ASSERT(s->base.refCount > 1); /* We shouldn't be last user of socket. */
         ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketConnected ||
                AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly);
         /*
          * If AsyncTCPSocketPoll or AsyncTCPSocketFillRecvBuffer fails, do not
          * add the recv callback as it may never fire.
          */
         s->recvCb = FALSE;  /* For re-registering the poll callback. */
         if (retVal == ASOCKERR_SUCCESS || retVal == ASOCKERR_TIMEOUT) {
            int ret = AsyncTCPSocketRegisterRecvCb(s);
            Log("SOCKET reregister recvCb after DoOneMsg (ref %d)\n",
                BaseSocket(s)->refCount);
            if (ret != ASOCKERR_SUCCESS) {
               s->base.recvBuf = NULL;
               retVal = ret;
            }
         }
      }
      AsyncTCPSocketRelease(s);
   } else {
      AsyncTCPSocketAddRef(s);
      retVal = AsyncTCPSocketPoll(s, read, timeoutMS, &asock);
      if (retVal != ASOCKERR_SUCCESS) {
         if (retVal == ASOCKERR_GENERIC) {
            TCPSOCKWARN(s, "%s: failed to poll on the socket during write.\n",
                        __FUNCTION__);
         }
      } else {
         ASSERT(asock == s);
         retVal = AsyncTCPSocketWriteBuffers(s);
      }
      AsyncTCPSocketRelease(s);
   }

   return retVal;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocket_TCPDrainRecv --
 *
 *      This function can be used to drain all the messages from a socket
 *      disconnected on the remote end.  It spins a socket until the specified
 *      amount of time has elapsed or an error is encountered, with backoff
 *      between read attempts if there is a conflict with another thread.  The
 *      recv callback is restored at the end of this only if not all the
 *      messages have been read, the socket is still connected and recv callack
 *      has not been cancelled.
 *
 * Results:
 *      ASOCKERR_SUCCESS if all messages are have been read, or if the callback
 *      has canceled the recv, or if the socket is closed
 *      ASOCKERR_GENERIC on system call failures
 *      ASOCKERR_TIMEOUT if there may still be unread messages at the end of
 *      the speccified time.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
AsyncSocket_TCPDrainRecv(AsyncSocket *base, // IN
                         int timeoutMS)     // IN
{
   AsyncTCPSocket *s = TCPSocket(base);
   int retVal;
   Bool cbRemoved = FALSE;
   Bool releaseLock = FALSE;
   unsigned count = 0;
   VmTimeType startMS = Hostinfo_SystemTimerMS();
   VmTimeType nowMS;

   ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketConnected ||
          AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly);
   ASSERT(s->recvCb); /* We are supposed to call someone... */

   if (!AsyncTCPSocketIsLocked(s) || !Poll_LockingEnabled()) {
      AsyncTCPSocketLock(s);
      releaseLock = TRUE;
   }
   AsyncTCPSocketAddRef(s);

   while (TRUE) {
      AsyncTCPSocket *asock = NULL;

      count++;
      if (s->inRecvLoop) {
         /*
          * The recv loop would read the data if there is any and it is
          * not safe to proceed and race with the recv loop.
          */
         TCPSOCKLG0(s, "busy: another thread in recv loop\n");
         retVal = ASOCKERR_BUSY;
         /* Add a bit of backoff. */
         AsyncTCPSocketUnlock(s);
         Util_Usleep(MIN(100 << (mssb32(count) / 2), timeoutMS));
         AsyncTCPSocketLock(s);
         goto retry;
      }

      if (!cbRemoved) {
         /*
          * Cancel the recv callback, but pretend that it is still registered
          * so we know if the callback cancel recv.
          */
         AsyncTCPSocketCancelRecvCb(s);
         s->recvCb = TRUE;
         cbRemoved = TRUE;
      }

      s->inBlockingRecv++;
      retVal = AsyncTCPSocketPoll(s, TRUE, 0, &asock);
      if (retVal != ASOCKERR_SUCCESS) {
         if (retVal == ASOCKERR_GENERIC) {
            TCPSOCKWARN(s, "%s: failed to poll on the socket during read.\n",
                        __FUNCTION__);
         }
      } else if (AsyncTCPSocketGetState(s) == AsyncSocketConnected ||
                 AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly) {
         ASSERT(asock == s);
         retVal = AsyncTCPSocketFillRecvBuffer(s);
      }
      s->inBlockingRecv--;

retry:
      if (retVal == ASOCKERR_REMOTE_DISCONNECT ||
          AsyncTCPSocketGetState(s) == AsyncSocketClosed ||
          !s->recvCb) {
         /* No more messages to recv. */
         retVal = ASOCKERR_SUCCESS;
         break;
      }
      if (retVal == ASOCKERR_GENERIC) {
         break;
      }

      nowMS = Hostinfo_SystemTimerMS();
      if (nowMS >= startMS + timeoutMS) {
         retVal = ASOCKERR_TIMEOUT;
         break;
      }
      timeoutMS -= nowMS - startMS;
      startMS = nowMS;
      ASSERT((AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly ||
              AsyncTCPSocketGetState(s) == AsyncSocketConnected) && s->recvCb);
   }

   if (cbRemoved) {
      s->recvCb = FALSE;
      /*
       * If AsyncTCPSocketPoll or AsyncTCPSocketFillRecvBuffer fails, do not
       * add the recv callback as it may never fire.
       */
      if (retVal == ASOCKERR_TIMEOUT) {
         ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketConnected ||
                AsyncTCPSocketGetState(s) == AsyncSocketConnectedRdOnly);
         ASSERT(s->base.refCount > 1); /* We better not be the last user */
         retVal = AsyncTCPSocketRegisterRecvCb(s);
         Log("SOCKET reregister recvCb after DrainRecv (ref %d)\n",
             BaseSocket(s)->refCount);
      }
   }
   if (!s->recvCb) {
      s->base.recvBuf = NULL;
   }

   AsyncTCPSocketRelease(s);
   if (releaseLock) {
      AsyncTCPSocketUnlock(s);
   }
   return retVal;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketFlush --
 *
 *      Try to send any pending out buffers until we run out of buffers, or
 *      the timeout expires.
 *
 * Results:
 *      ASOCKERR_SUCCESS if it worked, ASOCKERR_GENERIC on system call
 *      failures, and ASOCKERR_TIMEOUT if we couldn't send enough data
 *      before the timeout expired.
 *
 * Side effects:
 *      None.
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketFlush(AsyncSocket *base,  // IN
                    int timeoutMS)      // IN
{
   AsyncTCPSocket *s = TCPSocket(base);
   VmTimeType now, done;
   int retVal;

   if (s == NULL) {
      Warning(ASOCKPREFIX "Flush called with invalid arguments!\n");

      return ASOCKERR_INVAL;
   }

   ASSERT(AsyncTCPSocketIsLocked(s));
   AsyncTCPSocketAddRef(s);

   if (AsyncTCPSocketGetState(s) != AsyncSocketConnected) {
      TCPSOCKWARN(s, "flush called but state is rdonly or disconnected!\n");
      retVal = ASOCKERR_INVAL;
      goto outHaveLock;
   }

   now = Hostinfo_SystemTimerUS() / 1000;
   done = now + timeoutMS;

   while (s->sendBufList) {
      AsyncTCPSocket *asock = NULL;

      retVal = AsyncTCPSocketPoll(s, FALSE, done - now, &asock);
      if (retVal != ASOCKERR_SUCCESS) {
         TCPSOCKWARN(s, "flush failed\n");
         goto outHaveLock;
      }

      ASSERT(asock == s);
      if ((retVal = AsyncTCPSocketWriteBuffers(s)) != ASOCKERR_SUCCESS) {
         goto outHaveLock;
      }
      ASSERT(AsyncTCPSocketGetState(s) == AsyncSocketConnected);

      /* Setting timeoutMS to -1 means never timeout. */
      if (timeoutMS >= 0) {
         now = Hostinfo_SystemTimerUS() / 1000;

         /* Don't timeout if you've sent everything */
         if (now > done && s->sendBufList) {
            TCPSOCKWARN(s, "flush timed out\n");
            retVal = ASOCKERR_TIMEOUT;
            goto outHaveLock;
         }
      }
   }

   retVal = ASOCKERR_SUCCESS;

outHaveLock:
   AsyncTCPSocketRelease(s);

   return retVal;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketCancelListenCbSocket --
 *
 *      Socket specific code for canceling callbacks for a listening socket.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketCancelListenCb(AsyncTCPSocket *asock)  // IN:
{
   Bool removed;

   ASSERT(AsyncTCPSocketIsLocked(asock));

   removed = AsyncTCPSocketPollRemove(asock, TRUE,
                                      POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                      AsyncTCPSocketAcceptCallback);
   ASSERT(removed);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketAddListenCb --
 *
 *      Socket specific code for adding callbacks for a listening socket.
 *
 * Results:
 *      TRUE if Poll callback successfully added.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketAddListenCb(AsyncTCPSocket *asock)  // IN:
{
   VMwareStatus pollStatus;

   ASSERT(AsyncTCPSocketIsLocked(asock));

   pollStatus = AsyncTCPSocketPollAdd(asock, TRUE,
                                      POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                      AsyncTCPSocketAcceptCallback);

   if (pollStatus != VMWARE_STATUS_SUCCESS) {
      TCPSOCKWARN(asock, "failed to install listen accept callback!\n");
   }

   return pollStatus == VMWARE_STATUS_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketCancelRecvCb --
 *
 *      Socket specific code for canceling callbacks when a receive
 *      request is being canceled.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketCancelRecvCb(AsyncTCPSocket *asock)  // IN:
{
   ASSERT(AsyncTCPSocketIsLocked(asock));

   /*
    * We could fire the current recv completion callback here, but in
    * practice clients won't want to know about partial reads since it just
    * complicates the common case (i.e. every read callback would need to
    * check the len parameter).
    */
   if (asock->recvCbTimer) {
      AsyncTCPSocketPollRemove(asock, FALSE, 0, asock->internalRecvFn);
      asock->recvCbTimer = FALSE;
   }
   if (asock->recvCb) {
      Bool removed;
      TCPSOCKLOG(1, asock,
                 "Removing poll recv callback while cancelling recv.\n");
      removed = AsyncTCPSocketPollRemove(asock, TRUE,
                                         POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                         asock->internalRecvFn);

      /*
       * A recv callback registered on a bad FD can be deleted by
       * PollHandleInvalidFd if POLL_FLAG_ACCEPT_INVALID_FDS flag
       * is added to asyncsocket.
       */
      ASSERT(removed || AsyncTCPSocketPollParams(asock)->iPoll ||
             (AsyncTCPSocketPollParams(asock)->flags &
              POLL_FLAG_ACCEPT_INVALID_FDS) != 0);
      asock->recvCb = FALSE;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketCancelSendCb --
 *
 *      Socket specific code for canceling callbacks when a send
 *      request is being canceled.
 *
 *      This function assumes the caller has done AsyncTCPSocketAddRef,
 *      since the sendFn callback registered might close the socket.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketCancelSendCb(AsyncTCPSocket *asock)  // IN:
{
   if (asock->sendCb) {
      Bool removed;

      TCPSOCKLOG(1, asock,
                 "sendBufList is non-NULL, removing send callback\n");

      /*
       * The send callback could be either a device or RTime callback, so
       * we check the latter if it wasn't the former.
       */

      if (asock->sendCbTimer) {
         removed = AsyncTCPSocketPollRemove(asock, FALSE, 0,
                                            asock->internalSendFn);
      } else {
         removed = AsyncTCPSocketPollRemove(asock, TRUE, POLL_FLAG_WRITE,
                                            asock->internalSendFn);
      }
      ASSERT(removed || AsyncTCPSocketPollParams(asock)->iPoll);
      asock->sendCb = FALSE;
      asock->sendCbTimer = FALSE;
   }

   /*
    * Go through any send buffers on the list and fire their
    * callbacks, reflecting back how much of each buffer has been
    * submitted to the kernel.  For the first buffer in the list that
    * may be non-zero, for subsequent buffers it will be zero.
    *
    * Unlike AsyncTCPSocketCancelRecvCb, the argument of firing callbacks
    * here is that the common case for writes is "fire and forget", e.g.
    * send this buffer and free it. Firing the triggers at close time
    * simplifies client code, since the clients aren't forced to keep track
    * of send buffers themselves. Clients can figure out how much data was
    * actually transmitted (if they care) by checking the len parameter
    * passed to the send callback.
    *
    * A modification suggested by Jeremy is to pass a list of unsent
    * buffers and their completion callbacks to the error handler if one is
    * registered, and only fire the callbacks here if there was no error
    * handler invoked.
    */

   while (asock->sendBufList) {
      /*
       * Pop each remaining buffer and fire its completion callback.
       */

      SendBufList *cur = asock->sendBufList;
      int pos = asock->sendPos;

      asock->sendBufList = asock->sendBufList->next;
      asock->sendPos = 0;

      if (cur->sendFn) {
         cur->sendFn(cur->buf, pos, BaseSocket(asock), cur->clientData);
      }
      free(cur);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketCancelCbForClose --
 *
 *      Cancel future asynchronous send and recv by unregistering
 *      their Poll callbacks, and change the socket state to
 *      AsyncTCPSocketCBCancelled if the socket state is AsyncTCPSocketConnected.
 *
 *      The function can be called in a send/recv error handler before
 *      actually closing the socket in a separate thread, to prevent other
 *      code calling AsyncTCPSocket_Send/Recv from re-registering the
 *      callbacks again. The next operation should be just AsyncSocket_Close().
 *      This helps to avoid unnecessary send/recv callbacks before the
 *      socket is closed.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Unregisters send/recv Poll callbacks, and fires the send
 *      triggers for any remaining output buffers. May also change
 *      the socket state.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketCancelCbForClose(AsyncSocket *base)  // IN:
{
   AsyncTCPSocket *asock = TCPSocket(base);
   Bool removed;

   ASSERT(AsyncTCPSocketIsLocked(asock));
   if (AsyncTCPSocketGetState(asock) == AsyncSocketConnected ||
       AsyncTCPSocketGetState(asock) == AsyncSocketConnectedRdOnly) {
      AsyncTCPSocketSetState(asock, AsyncSocketCBCancelled);
   }

   /* Remove the read callback. Similar to AsyncTCPSocketCancelRecvCb */

   ASSERT(!asock->base.recvBuf || asock->base.recvFn);

   if (asock->recvCbTimer) {
      AsyncTCPSocketPollRemove(asock, FALSE, 0, asock->internalRecvFn);
      asock->recvCbTimer = FALSE;
   }
   if (asock->recvCb) {
      TCPSOCKLOG(1, asock, "recvCb is non-NULL, removing recv callback\n");
      removed = AsyncTCPSocketPollRemove(asock, TRUE,
                                         POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                         asock->internalRecvFn);
      /* Callback might be temporarily removed in AsyncSocket_DoOneMsg. */
      ASSERT_NOT_TESTED(removed ||
                        asock->inBlockingRecv ||
                        AsyncTCPSocketPollParams(asock)->iPoll);

      asock->recvCb = FALSE;
      asock->base.recvBuf = NULL;
   }

   /* Remove the write callback. */

   AsyncTCPSocketAddRef(asock);
   AsyncTCPSocketCancelSendCb(asock);
   AsyncTCPSocketRelease(asock);
   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketCancelCbForConnectingClose --
 *
 *      Cancels outstanding connect requests for a socket that is going
 *      away.
 *
 * Results:
 *      TRUE on callback removed. FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketCancelCbForConnectingClose(AsyncTCPSocket *asock) // IN
{
   return (AsyncTCPSocketPollRemove(asock, TRUE, POLL_FLAG_WRITE,
                                    AsyncTCPSocketConnectCallback) ||
           AsyncTCPSocketPollRemove(asock, FALSE, 0,
                                    AsyncTCPSocketConnectCallback));
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketSetCloseOptions --
 *
 *      Enables optional behavior for AsyncSocket_Close():
 *
 *      - If flushEnabledMaxWaitMsec is non-zero, the output stream
 *        will be flushed synchronously before the socket is closed.
 *        (default is zero: close socket right away without flushing)
 *
 *      - If closeCb is set, the callback will be called asynchronously
 *        when the socket is actually destroyed.
 *        (default is NULL: no callback)
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketSetCloseOptions(AsyncSocket *base,           // IN
                              int flushEnabledMaxWaitMsec, // IN
                              AsyncSocketCloseFn closeCb)  // IN
{
   AsyncTCPSocket *asock = TCPSocket(base);
   asock->flushEnabledMaxWaitMsec = flushEnabledMaxWaitMsec;
   asock->closeCb = closeCb;
   VERIFY(closeCb == NULL);
   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketCloseWrite --
 *
 *      Close the write side of AsyncTCPSocket.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketCloseWrite(AsyncSocket *base)  // IN:
{
#ifdef VMX86_TOOLS
   /* For tools, don't support closeWrite for now */
   return ASOCKERR_INVAL;
#else
   AsyncTCPSocket *asock = TCPSocket(base);
   int ret;

   ASSERT(AsyncTCPSocketIsLocked(asock));

   if (AsyncTCPSocketGetState(asock) != AsyncSocketConnected) {
      return ASOCKERR_INVAL;
   }
   if (SSL_IsEncrypted(asock->sslSock)) {
      /* SSL doesn't support half-close */
      return ASOCKERR_INVAL;
   }
   ASSERT(asock->listenAsock4 == NULL);
   ASSERT(asock->listenAsock6 == NULL);

   if (asock->flushEnabledMaxWaitMsec && !asock->base.errorSeen) {
      int ret = AsyncTCPSocketFlush(BaseSocket(asock),
                                    asock->flushEnabledMaxWaitMsec);
      if (ret != ASOCKERR_SUCCESS) {
         TCPSOCKWARN(asock,
                     "AsyncTCPSocket_Flush failed: %s. (ignored).\n",
                     AsyncSocket_Err2String(ret));
      }
   }
   AsyncTCPSocketSetState(asock, AsyncSocketConnectedRdOnly);
   AsyncTCPSocketAddRef(asock);
   AsyncTCPSocketCancelSendCb(asock);

   ret = SSLGeneric_shutdown(SSL_GetFd(asock->sslSock));
   if (ret < 0) {
      asock->genericErrno = ASOCK_LASTERROR();
      TCPSOCKWARN(asock, "shutdown error %d: %s\n", asock->genericErrno,
                 Err_Errno2String(asock->genericErrno));
      ret = ASOCKERR_GENERIC;
      goto out;
   }
   ret = ASOCKERR_SUCCESS;

out:
   AsyncTCPSocketRelease(asock);
   return ret;
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketClose --
 *
 *      AsyncTCPSocket destructor. The destructor should be safe to call at any
 *      time.  It's invoked automatically for I/O errors on slots that have no
 *      error handler set, and should be called manually by the error handler
 *      as necessary. It could also be called as part of the normal program
 *      flow.
 *
 * Results:
 *      ASOCKERR_*.
 *
 * Side effects:
 *      Closes the socket fd, unregisters all Poll callbacks, and fires the
 *      send triggers for any remaining output buffers.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketClose(AsyncSocket *base)   // IN
{
   AsyncTCPSocket *asock = TCPSocket(base);

   ASSERT(AsyncTCPSocketIsLocked(asock));

   if (AsyncTCPSocketGetState(asock) == AsyncSocketClosed) {
      Warning("%s() called on already closed asock!\n", __FUNCTION__);
      return ASOCKERR_CLOSED;
   }

   if (asock->listenAsock4 || asock->listenAsock6) {
      if (asock->listenAsock4) {
         AsyncSocket_Close(BaseSocket(asock->listenAsock4));
      }
      if (asock->listenAsock6) {
         AsyncSocket_Close(BaseSocket(asock->listenAsock6));
      }
   } else {
      Bool removed;
      AsyncSocketState oldState;

      /* Flush output if requested via AsyncTCPSocket_SetCloseOptions(). */
      if (asock->flushEnabledMaxWaitMsec &&
          AsyncTCPSocketGetState(asock) == AsyncSocketConnected &&
          !asock->base.errorSeen) {
         int ret = AsyncTCPSocketFlush(base, asock->flushEnabledMaxWaitMsec);
         if (ret != ASOCKERR_SUCCESS) {
            TCPSOCKWARN(asock,
                        "AsyncTCPSocket_Flush failed: %s. Closing now.\n",
                        AsyncSocket_Err2String(ret));
         }
      }

      /*
       * Set the new state to closed, and then check the old state and do the
       * right thing accordingly
       */

      TCPSOCKLOG(1, asock, "closing socket\n");
      oldState = AsyncTCPSocketGetState(asock);
      AsyncTCPSocketSetState(asock, AsyncSocketClosed);

      switch(oldState) {
      case AsyncSocketListening:
         TCPSOCKLOG(1, asock,
                    "old state was listening, removing accept callback\n");
         AsyncTCPSocketCancelListenCb(asock);
         break;

      case AsyncSocketConnecting:
         TCPSOCKLOG(1, asock,
                    "old state was connecting, removing connect callback\n");
         removed = AsyncTCPSocketCancelCbForConnectingClose(asock);
         if (!removed) {
            TCPSOCKLOG(1, asock,
                       "connect callback is not present in the poll list.\n");
         }
         break;

      case AsyncSocketConnected:
      case AsyncSocketConnectedRdOnly:
         TCPSOCKLOG(1, asock, "old state was connected/rdonly\n");
         AsyncTCPSocketCancelCbForClose(BaseSocket(asock));
         break;

      case AsyncSocketCBCancelled:
         TCPSOCKLOG(1, asock, "old state was CB-cancelled\n");
         break;

      default:
         NOT_REACHED();
      }

      if (asock->internalConnectFn) {
         removed = AsyncTCPSocketPollRemove(asock, FALSE, POLL_FLAG_PERIODIC,
                                            AsyncTCPSocketConnectErrorCheck);
         ASSERT(removed);
         asock->internalConnectFn = NULL;
      }

      if (asock->sslConnectFn && asock->sslPollFlags > 0) {
         removed = AsyncTCPSocketPollRemove(asock, TRUE, asock->sslPollFlags,
                                            AsyncTCPSocketSslConnectCallback);
         ASSERT(removed);
      }

      if (asock->sslAcceptFn && asock->sslPollFlags > 0) {
         removed = AsyncTCPSocketPollRemove(asock, TRUE, asock->sslPollFlags,
                                            AsyncTCPSocketSslAcceptCallback);
         ASSERT(removed);
      }
      asock->sslPollFlags = 0;

      /*
       * Close the underlying SSL sockets.
       */
      SSL_Shutdown(asock->sslSock);

      if (asock->passFd.fd != -1) {
         SSLGeneric_close(asock->passFd.fd);
      }
   }

   AsyncSocketTeardownSocket(base);
   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketIsSendBufferFull --
 *
 *      Indicate if socket send buffer is full.  Note that unless this is
 *      called from a callback function, the return value should be treated
 *      as transient.
 *
 * Results:
 *      0: send space probably available,
 *      1: send has reached maximum,
 *      ASOCKERR_GENERIC: null socket.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketIsSendBufferFull(AsyncSocket *base)         // IN
{
   AsyncTCPSocket *asock = TCPSocket(base);
   return asock->sendBufFull;
}




/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketHasDataPending --
 *
 *      Determine if SSL has any pending/unread data.
 *
 * Results:
 *      TRUE if this socket has pending data.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketHasDataPending(AsyncTCPSocket *asock)   // IN:
{
   return SSL_Pending(asock->sslSock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketMakeNonBlocking --
 *
 *      Make the specified socket non-blocking if it isn't already.
 *
 * Results:
 *      ASOCKERR_SUCCESS if the operation succeeded, ASOCKERR_GENERIC otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketMakeNonBlocking(int fd)         // IN
{
#ifdef _WIN32
   int retval;
   u_long argp = 1; /* non-zero => enable non-blocking mode */

   retval = ioctlsocket(fd, FIONBIO, &argp);

   if (retval != 0) {
      ASSERT(retval == SOCKET_ERROR);

      return ASOCKERR_GENERIC;
   }
#elif defined(__APPLE__)
   int argp = 1;
   if (ioctl(fd, FIONBIO, &argp) < 0) {
      return ASOCKERR_GENERIC;
   }
#else
   int flags;

   if ((flags = fcntl(fd, F_GETFL)) < 0) {
      return ASOCKERR_GENERIC;
   }

   if (!(flags & O_NONBLOCK) && (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0))
   {
      return ASOCKERR_GENERIC;
   }
#endif

   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncSocketAcceptCallback --
 *
 *      Poll callback for listening fd waiting to complete an accept
 *      operation. We call accept to get the new socket fd, create a new
 *      asock, and call the newFn callback previously supplied by the call to
 *      AsyncTCPSocket_Listen.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Accepts on listening fd, creates new asock.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketAcceptCallback(void *clientData)         // IN
{
   AsyncTCPSocket *asock = clientData;
   int retval;

   ASSERT(asock);
   ASSERT(AsyncTCPSocketPollParams(asock)->iPoll == NULL);
   ASSERT(AsyncTCPSocketIsLocked(asock));

   AsyncTCPSocketAddRef(asock);
   retval = AsyncTCPSocketAcceptInternal(asock);

   /*
    * See comment for return value of AsyncTCPSocketAcceptInternal().
    */

   if (retval == ASOCKERR_ACCEPT) {
      AsyncTCPSocketHandleError(asock, retval);
   }
   AsyncTCPSocketRelease(asock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketConnectCallback --
 *
 *      Poll callback for connecting fd. Calls through to
 *      AsyncTCPSocketConnectInternal to do the real work.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Creates new asock, fires newFn callback.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketConnectCallback(void *clientData)         // IN
{
   AsyncTCPSocket *asock = clientData;
   int retval;

   ASSERT(asock);
   ASSERT(AsyncTCPSocketPollParams(asock)->iPoll == NULL);
   ASSERT(AsyncTCPSocketIsLocked(asock));

   AsyncTCPSocketAddRef(asock);
   retval = AsyncTCPSocketConnectInternal(asock);
   if (retval != ASOCKERR_SUCCESS) {
      ASSERT(retval == ASOCKERR_GENERIC ||
             retval == ASOCKERR_NETUNREACH ||
             retval == ASOCKERR_CONNECT);
      AsyncTCPSocketHandleError(asock, retval);
   }
   if (vmx86_win32 && asock->internalConnectFn != NULL) {
      Bool removed;

      removed = AsyncTCPSocketPollRemove(asock, FALSE, POLL_FLAG_PERIODIC,
                                         AsyncTCPSocketConnectErrorCheck);
      ASSERT(removed);
      asock->internalConnectFn = NULL;
   }
   AsyncTCPSocketRelease(asock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketRecvCallback --
 *
 *      Poll callback for input waiting on the socket. We try to pull off the
 *      remaining data requested by the current receive function.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Reads data, could fire recv completion or trigger socket destruction.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketRecvCallback(void *clientData)         // IN
{
   AsyncTCPSocket *asock = clientData;
   int error = ASOCKERR_SUCCESS;
   Bool recv = TRUE;

   ASSERT(asock);
   ASSERT(AsyncTCPSocketIsLocked(asock));

   AsyncTCPSocketAddRef(asock);

   if (UNLIKELY(ASOCK_PEEK(asock))) {
      recv = FALSE;
      error = AsyncTCPSocketFillPeekBuffer(asock, &recv);
   }

   if (LIKELY(recv)) {
      error = AsyncTCPSocketFillRecvBuffer(asock);
   }

   if (error == ASOCKERR_GENERIC || error == ASOCKERR_REMOTE_DISCONNECT) {
      AsyncTCPSocketHandleError(asock, error);
   }

   AsyncTCPSocketRelease(asock);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketIPollRecvCallback --
 *
 *      Poll callback for input waiting on the socket.  IVmdbPoll does not
 *      handle callback locks, so this function first locks the asyncsocket
 *      and verify that the recv callback has not been cancelled before
 *      calling AsyncTCPSocketFillRecvBuffer to do the real work.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Reads data, could fire recv completion or trigger socket destruction.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketIPollRecvCallback(void *clientData)  // IN:
{
#ifdef VMX86_TOOLS
   NOT_IMPLEMENTED();
#else
   AsyncTCPSocket *asock = clientData;
   MXUserRecLock *lock;

   ASSERT(asock);
   ASSERT(AsyncTCPSocketPollParams(asock)->lock == NULL ||
          !MXUser_IsCurThreadHoldingRecLock(
             AsyncTCPSocketPollParams(asock)->lock));

   /*
    * peek() has not been needed for IVmdbPoll so it is not tested/supported
    * in this callback, unlike the regular socket poll recv callback
    * (AsyncTCPSocketRecvCallback). ASSERT added for caution.
    */
   ASSERT(!(asock->flags & ASOCK_FLAG_PEEK));

   AsyncTCPSocketLock(asock);
   if (asock->recvCbTimer) {
      /* IVmdbPoll only has periodic timer callbacks. */
      AsyncTCPSocketIPollRemove(asock, FALSE, 0, asock->internalRecvFn);
      asock->recvCbTimer = FALSE;
   }
   lock = AsyncTCPSocketPollParams(asock)->lock;
   if (asock->recvCb && asock->inBlockingRecv == 0) {
      asock->inIPollCb |= IN_IPOLL_RECV;
      AsyncTCPSocketRecvCallback(clientData);
      asock->inIPollCb &= ~IN_IPOLL_RECV;
      /*
       * Re-register the callback if it has not been canceled.  Lock may have
       * been dropped to fire recv callback so re-check inBlockingRecv.
       */
      if (asock->recvCb && asock->inBlockingRecv == 0) {
         AsyncTCPSocketIPollAdd(asock, TRUE, POLL_FLAG_READ,
                                asock->internalRecvFn, asock->fd);
      }
   } else {
      TCPSOCKLG0(asock, "Skip recv because %s\n",
                 asock->recvCb ? "blocking recv is in progress"
                               : "recv callback is cancelled");
   }

   /* This is a one-shot callback so we always release the reference taken. */
   AsyncTCPSocketRelease(asock);
   AsyncTCPSocketUnlock(asock);
   if (lock != NULL) {
      MXUser_DecRefRecLock(lock);
   }
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketSendCallback --
 *
 *      Poll callback for output socket buffer space available (socket is
 *      writable). We iterate over all the remaining buffers in our queue,
 *      writing as much as we can until we fill the socket buffer again. If we
 *      don't finish, we register ourselves as a device write callback.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Writes data, could trigger write completion or socket destruction.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketSendCallback(void *clientData)         // IN
{
   AsyncTCPSocket *s = clientData;
   int retval;

   ASSERT(s);
   ASSERT(AsyncTCPSocketIsLocked(s));

   AsyncTCPSocketAddRef(s);
   s->sendCb = FALSE; /* AsyncTCPSocketSendCallback is never periodic */
   s->sendCbTimer = FALSE;
   retval = AsyncTCPSocketWriteBuffers(s);
   if (retval != ASOCKERR_SUCCESS &&
       retval != ASOCKERR_CLOSED) {
      AsyncTCPSocketHandleError(s, retval);
   } else if (s->sendBufList && !s->sendCb) {
      VMwareStatus pollStatus;

      /*
       * We didn't finish, so we need to reschedule the Poll callback (the
       * write callback is *not* periodic).
       */

#ifdef _WIN32
      /*
       * If any data has been sent out or read in from the sslSock,
       * SSL has finished the handshaking. Otherwise,
       * we have to schedule a realtime callback for write. See bug 37147
       */

      if (!s->sslConnected) {
         pollStatus = AsyncTCPSocketPollAdd(s, FALSE, 0,
                                         s->internalSendFn, 100000);
         VERIFY(pollStatus == VMWARE_STATUS_SUCCESS);
         s->sendCbTimer = TRUE;
      } else
#endif
      {
         pollStatus = AsyncTCPSocketPollAdd(s, TRUE, POLL_FLAG_WRITE,
                                         s->internalSendFn);
         VERIFY(pollStatus == VMWARE_STATUS_SUCCESS);
      }
      s->sendCb = TRUE;
   }
   AsyncTCPSocketRelease(s);
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketIPollSendCallback --
 *
 *      IVmdbPoll callback for output socket buffer space available.  IVmdbPoll
 *      does not handle callback locks, so this function first locks the
 *      asyncsocket and verify that the send callback has not been cancelled.
 *      IVmdbPoll only has periodic callbacks, so this function unregisters
 *      itself before calling AsyncTCPSocketSendCallback to do the real work.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Writes data, could trigger write completion or socket destruction.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketIPollSendCallback(void *clientData)  // IN:
{
#ifdef VMX86_TOOLS
   NOT_IMPLEMENTED();
#else
   AsyncTCPSocket *s = clientData;
   MXUserRecLock *lock;

   ASSERT(s);

   AsyncTCPSocketLock(s);
   s->inIPollCb |= IN_IPOLL_SEND;
   lock = AsyncTCPSocketPollParams(s)->lock;
   if (s->sendCbTimer) {
      /* IVmdbPoll only has periodic timer callback. */
      AsyncTCPSocketIPollRemove(s, FALSE, 0, AsyncTCPSocketIPollSendCallback);
      s->sendCbTimer = FALSE;
   }
   if (s->sendCb) {
      AsyncTCPSocketSendCallback(s);
   } else {
      TCPSOCKLG0(s, "cancelled send callback fired\n");
   }

   s->inIPollCb &= ~IN_IPOLL_SEND;
   AsyncTCPSocketRelease(s);
   AsyncTCPSocketUnlock(s);
   if (lock != NULL) {
      MXUser_DecRefRecLock(lock);
   }
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketPollAdd --
 *
 *    Add a poll callback.  Wrapper for Poll_Callback since we always call
 *    it in one of two basic forms.
 *
 *    If socket is FALSE, user has to pass in the timeout value
 *
 * Results:
 *    VMwareStatus result code from Poll_Callback
 *
 * Side effects:
 *    Only the obvious.
 *
 *-----------------------------------------------------------------------------
 */

static VMwareStatus
AsyncTCPSocketPollAdd(AsyncTCPSocket *asock,         // IN
                      Bool socket,                   // IN
                      int flags,                     // IN
                      PollerFunction callback,       // IN
                      ...)                           // IN
{
   int type, info;

   if (socket) {
      ASSERT(asock->fd != -1);
      type = POLL_DEVICE;
      flags |= POLL_FLAG_SOCKET;
      info = asock->fd;
   } else {
      va_list marker;
      va_start(marker, callback);

      type = POLL_REALTIME;
      info = va_arg(marker, int);

      va_end(marker);
   }

   if (AsyncTCPSocketPollParams(asock)->iPoll != NULL) {
      return AsyncTCPSocketIPollAdd(asock, socket, flags, callback, info);
   }

   return Poll_Callback(AsyncTCPSocketPollParams(asock)->pollClass,
                        flags | AsyncTCPSocketPollParams(asock)->flags,
                        callback, asock, type, info,
                        AsyncTCPSocketPollParams(asock)->lock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketPollRemove --
 *
 *    Remove a poll callback.  Wrapper for Poll_CallbackRemove since we
 *    always call it in one of two basic forms.
 *
 * Results:
 *    TRUE if removed, FALSE if not found.
 *
 * Side effects:
 *    Only the obvious.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketPollRemove(AsyncTCPSocket *asock,         // IN
                         Bool socket,                   // IN
                         int flags,                     // IN
                         PollerFunction callback)       // IN
{
   int type;

   if (AsyncTCPSocketPollParams(asock)->iPoll != NULL) {
      return AsyncTCPSocketIPollRemove(asock, socket, flags, callback);
   }

   if (socket) {
      ASSERT(asock->fd != -1);
      type = POLL_DEVICE;
      flags |= POLL_FLAG_SOCKET;
   } else {
      type = POLL_REALTIME;
   }

   return Poll_CallbackRemove(AsyncTCPSocketPollParams(asock)->pollClass,
                              flags | AsyncTCPSocketPollParams(asock)->flags,
                              callback, asock, type);
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketIPollAdd --
 *
 *    Add a poll callback.  Wrapper for IVmdbPoll.Register[Timer].
 *
 *    If socket is FALSE, user has to pass in the timeout value
 *
 * Results:
 *    VMwareStatus result code.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static VMwareStatus
AsyncTCPSocketIPollAdd(AsyncTCPSocket *asock,         // IN
                       Bool socket,                   // IN
                       int flags,                     // IN
                       PollerFunction callback,       // IN
                       int info)                      // IN
{
#ifdef VMX86_TOOLS
   return VMWARE_STATUS_ERROR;
#else
   VMwareStatus status = VMWARE_STATUS_SUCCESS;
   VmdbRet ret;
   IVmdbPoll *poll;

   ASSERT(AsyncTCPSocketPollParams(asock)->iPoll);
   ASSERT(AsyncTCPSocketIsLocked(asock));

   /* Protect asyncsocket and lock from disappearing */
   AsyncTCPSocketAddRef(asock);
   if (AsyncTCPSocketPollParams(asock)->lock != NULL) {
      MXUser_IncRefRecLock(AsyncTCPSocketPollParams(asock)->lock);
   }

   poll = AsyncTCPSocketPollParams(asock)->iPoll;

   if (socket) {
      int pollFlags = VMDB_PRF_ONE_SHOT |
                      ((flags & POLL_FLAG_READ) != 0 ? VMDB_PRF_READ
                                                     : VMDB_PRF_WRITE);

      ret = poll->Register(poll, pollFlags, callback, asock, info);
   } else {
      ret = poll->RegisterTimer(poll, callback, asock, info);
   }

   if (ret != VMDB_S_OK) {
      Log(ASOCKPREFIX "failed to register callback (%s %d): error %d\n",
          socket ? "socket" : "delay", info, ret);
      if (AsyncTCPSocketPollParams(asock)->lock != NULL) {
         MXUser_DecRefRecLock(AsyncTCPSocketPollParams(asock)->lock);
      }
      AsyncTCPSocketRelease(asock);
      status = VMWARE_STATUS_ERROR;
   }

   return status;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketIPollRemove --
 *
 *    Remove a poll callback.  Wrapper for IVmdbPoll.Unregister[Timer].
 *
 * Results:
 *    TRUE  if the callback was registered and has been cancelled successfully.
 *    FALSE if the callback was not registered, or the callback is already
 *          scheduled to fire (and is guaranteed to fire).
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketIPollRemove(AsyncTCPSocket *asock,         // IN
                          Bool socket,                   // IN
                          int flags,                     // IN
                          PollerFunction callback)       // IN
{
#ifdef VMX86_TOOLS
   return FALSE;
#else
   IVmdbPoll *poll;
   Bool ret;

   ASSERT(AsyncTCPSocketPollParams(asock)->iPoll);
   ASSERT(AsyncTCPSocketIsLocked(asock));

   poll = AsyncTCPSocketPollParams(asock)->iPoll;

   if (socket) {
      int pollFlags = VMDB_PRF_ONE_SHOT |
                      ((flags & POLL_FLAG_READ) != 0 ? VMDB_PRF_READ
                                                     : VMDB_PRF_WRITE);

      ret = poll->Unregister(poll, pollFlags, callback, asock);
   } else {
      ret = poll->UnregisterTimer(poll, callback, asock);
   }

   if (ret &&
       !((asock->inIPollCb & IN_IPOLL_RECV) != 0 &&
         callback == asock->internalRecvFn) &&
       !((asock->inIPollCb & IN_IPOLL_SEND) != 0 &&
         callback == asock->internalSendFn)) {
      MXUserRecLock *lock = AsyncTCPSocketPollParams(asock)->lock;

      /*
       * As the callback has been unregistered and we are not currently in
       * the callback being removed, we can safely release the reference taken
       * when registering the callback.
       */
      AsyncTCPSocketRelease(asock);
      if (lock != NULL) {
         MXUser_DecRefRecLock(lock);
      }
   }

   return ret;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketCancelRecv --
 *
 *    Call this function if you know what you are doing. This should be
 *    called if you want to synchronously receive the outstanding data on
 *    the socket. It removes the recv poll callback. It also returns number of
 *    partially read bytes (if any). A partially read response may exist as
 *    AsyncTCPSocketRecvCallback calls the recv callback only when all the data
 *    has been received.
 *
 * Results:
 *    ASOCKERR_SUCCESS or ASOCKERR_INVAL.
 *
 * Side effects:
 *    Subsequent client call to AsyncTCPSocket_Recv can reinstate async behaviour.
 *
 *-----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketCancelRecv(AsyncSocket *base,          // IN
                         int *partialRecvd,          // OUT
                         void **recvBuf,             // OUT
                         void **recvFn,              // OUT
                         Bool cancelOnSend)          // IN
{
   AsyncTCPSocket *asock = TCPSocket(base);

   ASSERT(AsyncTCPSocketIsLocked(asock));

   if (AsyncTCPSocketGetState(asock) != AsyncSocketConnected &&
       AsyncTCPSocketGetState(asock) != AsyncSocketConnectedRdOnly) {
      Warning(ASOCKPREFIX "Failed to cancel request on disconnected socket!\n");
      return ASOCKERR_INVAL;
   }

   if (asock->inBlockingRecv && !asock->inRecvLoop) {
      Warning(ASOCKPREFIX "Cannot cancel request while a blocking recv is "
                          "pending.\n");
      return ASOCKERR_INVAL;
   }

   if (!cancelOnSend && (asock->sendBufList || asock->sendCb)) {
      Warning(ASOCKPREFIX "Can't cancel request as socket has send operation "
              "pending.\n");
      return ASOCKERR_INVAL;
   }

   AsyncTCPSocketCancelRecvCb(asock);
   AsyncSocketCancelRecv(BaseSocket(asock), partialRecvd, recvBuf, recvFn);

   if (asock->passFd.fd != -1) {
      SSLGeneric_close(asock->passFd.fd);
      asock->passFd.fd = -1;
   }
   asock->passFd.expected = FALSE;

   return ASOCKERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetReceivedFd --
 *
 *    Retrieve received file descriptor from socket.
 *
 * Results:
 *    File descriptor.  Or -1 if none was received.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetReceivedFd(AsyncSocket *base)      // IN
{
   AsyncTCPSocket *asock = TCPSocket(base);
   int fd;

   ASSERT(AsyncTCPSocketIsLocked(asock));

   if (AsyncTCPSocketGetState(asock) != AsyncSocketConnected &&
       AsyncTCPSocketGetState(asock) != AsyncSocketConnectedRdOnly) {
      Warning(ASOCKPREFIX "Failed to receive fd on disconnected socket!\n");
      return -1;
   }
   fd = asock->passFd.fd;
   asock->passFd.fd = -1;
   asock->passFd.expected = FALSE;

   return fd;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketConnectSSL --
 *
 *    Initialize the socket's SSL object, by calling SSL_ConnectAndVerify.
 *    NOTE: This call is blocking.
 *
 * Results:
 *    TRUE if SSL_ConnectAndVerify succeeded, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketConnectSSL(AsyncSocket *base,           // IN
                         SSLVerifyParam *verifyParam, // IN/OPT
                         const char *hostname,        // IN/OPT
                         void *sslContext)            // IN/OPT
{
#ifndef USE_SSL_DIRECT
   AsyncTCPSocket *asock = TCPSocket(base);
   ASSERT(asock);

   if (sslContext == NULL) {
      sslContext = SSL_DefaultClientContext();
   }

   return SSL_ConnectAndVerifyWithContext(asock->sslSock, verifyParam,
                                          hostname, sslContext);
#else
   return FALSE;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketAcceptSSL --
 *
 *    Initialize the socket's SSL object, by calling SSL_Accept or
 *    SSL_AcceptWithContext.
 *
 * Results:
 *    TRUE if SSL_Accept/SSL_AcceptWithContext succeeded, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
AsyncTCPSocketAcceptSSL(AsyncSocket *base,  // IN
                        void *sslCtx)       // IN: optional
{
#ifndef USE_SSL_DIRECT
   AsyncTCPSocket *asock = TCPSocket(base);
   ASSERT(asock);

   if (sslCtx) {
      return SSL_AcceptWithContext(asock->sslSock, sslCtx);
   } else {
      return SSL_Accept(asock->sslSock);
   }
#else
   return FALSE;
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketSslConnectCallback --
 *
 *      Poll callback to redrive an outstanding ssl connect operation.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketSslConnectCallback(void *clientData)  // IN
{
#ifndef USE_SSL_DIRECT
   int sslOpCode;
   VMwareStatus pollStatus;
   AsyncTCPSocket *asock = clientData;

   ASSERT(asock);
   ASSERT(AsyncTCPSocketPollParams(asock)->iPoll == NULL);
   ASSERT(AsyncTCPSocketIsLocked(asock));

   AsyncTCPSocketAddRef(asock);

   /* Only set if poll callback is registered */
   asock->sslPollFlags = 0;

   sslOpCode = SSL_TryCompleteConnect(asock->sslSock);
   if (sslOpCode > 0) {
      (*asock->sslConnectFn)(TRUE, BaseSocket(asock), asock->clientData);
   } else if (sslOpCode < 0) {
      (*asock->sslConnectFn)(FALSE, BaseSocket(asock), asock->clientData);
   } else {
      asock->sslPollFlags = SSL_WantRead(asock->sslSock) ?
                            POLL_FLAG_READ : POLL_FLAG_WRITE;

      /* register the poll callback to redrive the SSL connect */
      pollStatus = AsyncTCPSocketPollAdd(asock, TRUE, asock->sslPollFlags,
                                      AsyncTCPSocketSslConnectCallback);

      if (pollStatus != VMWARE_STATUS_SUCCESS) {
         TCPSOCKWARN(asock, "failed to reinstall ssl connect callback!\n");
         asock->sslPollFlags = 0;
         (*asock->sslConnectFn)(FALSE, BaseSocket(asock), asock->clientData);
      }
   }

   AsyncTCPSocketRelease(asock);
#else
   NOT_IMPLEMENTED();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketStartSslConnect --
 *
 *    Start an asynchronous SSL connect operation.
 *
 *    The supplied callback function is called when the operation is complete
 *    or an error occurs.
 *
 *    Note: The client callback could be invoked from this function or
 *          from a poll callback. If there is any requirement to always
 *          invoke the client callback from outside this function, consider
 *          changing this code to use a poll timer callback with timeout
 *          set to zero.
 *
 * Results:
 *    ASOCKERR_SUCCESS or ASOCKERR_*.
 *    Errors during async processing are reported using the callback supplied.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketStartSslConnect(AsyncSocket *base,                    // IN
                              SSLVerifyParam *verifyParam,          // IN/OPT
                              const char *hostname,                 // IN/OPT
                              void *sslCtx,                         // IN
                              AsyncSocketSslConnectFn sslConnectFn, // IN
                              void *clientData)                     // IN
{
#ifndef USE_SSL_DIRECT
   AsyncTCPSocket *asock = TCPSocket(base);
   Bool ok;

   ASSERT(asock);
   ASSERT(sslConnectFn);

   ASSERT(AsyncTCPSocketIsLocked(asock));

   if (asock->sslConnectFn || asock->sslAcceptFn) {
      TCPSOCKWARN(asock, "An SSL operation was already initiated.\n");
      return ASOCKERR_GENERIC;
   }

   ok = SSL_SetupConnectAndVerifyWithContext(asock->sslSock, verifyParam,
                                             hostname, sslCtx);
   if (!ok) {
      /* Something went wrong already */
      (*sslConnectFn)(FALSE, BaseSocket(asock), clientData);
      return ASOCKERR_GENERIC;
   }

   asock->sslConnectFn = sslConnectFn;
   asock->clientData = clientData;

   AsyncTCPSocketSslConnectCallback(asock);
   return ASOCKERR_SUCCESS;
#else
   return ASOCKERR_INVAL;
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketSslAcceptCallback --
 *
 *      Poll callback for redrive an outstanding ssl accept operation
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketSslAcceptCallback(void *clientData)         // IN
{
   int sslOpCode;
   AsyncTCPSocket *asock = clientData;
   VMwareStatus pollStatus;

   ASSERT(asock);
   ASSERT(AsyncTCPSocketPollParams(asock)->iPoll == NULL);
   ASSERT(AsyncTCPSocketIsLocked(asock));

   AsyncTCPSocketAddRef(asock);

   /* Only set if poll callback is registered */
   asock->sslPollFlags = 0;

   sslOpCode = SSL_TryCompleteAccept(asock->sslSock);
   if (sslOpCode > 0) {
      (*asock->sslAcceptFn)(TRUE, BaseSocket(asock), asock->clientData);
   } else if (sslOpCode < 0) {
      (*asock->sslAcceptFn)(FALSE, BaseSocket(asock), asock->clientData);
   } else {
      asock->sslPollFlags = SSL_WantRead(asock->sslSock) ?
                            POLL_FLAG_READ : POLL_FLAG_WRITE;

      /* register the poll callback to redrive the SSL accept */
      pollStatus = AsyncTCPSocketPollAdd(asock, TRUE, asock->sslPollFlags,
                                      AsyncTCPSocketSslAcceptCallback);

      if (pollStatus != VMWARE_STATUS_SUCCESS) {
         TCPSOCKWARN(asock, "failed to reinstall ssl accept callback!\n");
         asock->sslPollFlags = 0;
         (*asock->sslAcceptFn)(FALSE, BaseSocket(asock), asock->clientData);
      }
   }

   AsyncTCPSocketRelease(asock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketStartSslAccept --
 *
 *    Start an asynchronous SSL accept operation.
 *
 *    The supplied callback function is called when the operation is complete
 *    or an error occurs.
 *
 *    Note: The client callback could be invoked from this function or
 *          from a poll callback. If there is any requirement to always
 *          invoke the client callback from outside this function, consider
 *          changing this code to use a poll timer callback with timeout
 *          set to zero.
 *
 *    Note: sslCtx is typed as void *, so that the async socket code does
 *          not have to include the openssl header. This is in sync with
 *          SSL_AcceptWithContext(), where the sslCtx param is typed as void *
 * Results:
 *    ASOCKERR_SUCCESS or ASOCKERR_*.
 *    Errors during async processing reported using the callback supplied.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketStartSslAccept(AsyncSocket *base,                  // IN
                             void *sslCtx,                       // IN
                             AsyncSocketSslAcceptFn sslAcceptFn, // IN
                             void *clientData)                   // IN
{
   AsyncTCPSocket *asock = TCPSocket(base);
   Bool ok;

   ASSERT(asock);
   ASSERT(sslAcceptFn);

   ASSERT(AsyncTCPSocketIsLocked(asock));

   if (asock->sslAcceptFn || asock->sslConnectFn) {
      TCPSOCKWARN(asock, "An SSL operation was already initiated.\n");
      return ASOCKERR_GENERIC;
   }

   ok = SSL_SetupAcceptWithContext(asock->sslSock, sslCtx);
   if (!ok) {
      /* Something went wrong already */
      (*sslAcceptFn)(FALSE, BaseSocket(asock), clientData);
      return ASOCKERR_GENERIC;
   }

   asock->sslAcceptFn = sslAcceptFn;
   asock->clientData = clientData;

   AsyncTCPSocketSslAcceptCallback(asock);
   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketSetOption --
 *
 *      This implementation of ->setOption() supports the following
 *      options. Exact behavior of each cited optID is documented in the
 *      comment header for that enum value declaration (for non-native options),
 *      or `man setsockopt`/equivalent (for native options).
 *
 *         - layer = SOL_SOCKET, optID =
 *           SO_SNDBUF, SO_RCVBUF.
 *
 *         - layer = IPPROTO_TCP, optID =
 *           TCP_NODELAY, TCP_KEEPINTVL, TCP_KEEPIDLE, TCP_KEEPCNT.
 *
 *         - layer = ASYNC_SOCKET_OPTS_LAYER_BASE, optID (type) =
 *           ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE (Bool).
 *
 * Results:
 *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
 *      Invalid option+layer yields ASOCKERR_INVAL.
 *      Failure to set a native OS option yields ASOCKERR_GENERIC.
 *      inBufLen being wrong (for the given option) yields undefined behavior.
 *
 * Side effects:
 *      Depends on option.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketSetOption(AsyncSocket *asyncSocket,     // IN/OUT
                        AsyncSocketOpts_Layer layer,  // IN
                        AsyncSocketOpts_ID optID,     // IN
                        const void *valuePtr,         // IN
                        socklen_t inBufLen)           // IN
{
   /* Maintenance: Keep this in sync with ...GetOption(). */

   AsyncTCPSocket *tcpSocket = TCPSocket(asyncSocket);
   Bool isSupported;

   switch ((int)layer)
   {
   case SOL_SOCKET:
   case IPPROTO_TCP:
   case ASYNC_SOCKET_OPTS_LAYER_BASE:
      break;
   default:
      TCPSOCKLG0(tcpSocket,
                 "%s: Option layer [%d] (option [%d]) is not "
                     "supported for TCP socket.\n",
                  __FUNCTION__, (int)layer, optID);
      return ASOCKERR_INVAL;
   }

   /*
    * layer is supported.
    * Handle non-native options first.
    */

   if (layer == ASYNC_SOCKET_OPTS_LAYER_BASE) {
      switch (optID) {
         case ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE:
            ASSERT(inBufLen == sizeof(Bool));
            tcpSocket->sendLowLatency = *((const Bool *)valuePtr);
            TCPSOCKLG0(tcpSocket, "%s: sendLowLatencyMode set to [%d].\n",
                       __FUNCTION__, (int)tcpSocket->sendLowLatency);
            return ASOCKERR_SUCCESS;
         default:
            TCPSOCKLG0(tcpSocket, "%s: could not set non-native option [%d]"
                       " for TCP socket -- option not supported.\n",
                       __FUNCTION__, (int)optID);
            return ASOCKERR_INVAL;
      }
   }

   /*
    * Handle native (setsockopt()) options from this point on.
    *
    * We need the level and option_name arguments for that call.
    * Our design dictates that, for native options, simply option_name=optID.
    * So just determine level from our layer enum (for native layers, the enum's
    * ordinal value is set to the corresponding int level value). Therefore,
    * level=layer.
    *
    * level and option_name are known. However, we only allow the setting of
    * certain specific options. Anything else is an error.
    */
   isSupported = FALSE;
   if (layer == SOL_SOCKET) {
      switch (optID) {
      case SO_SNDBUF:
      case SO_RCVBUF:
         isSupported = TRUE;
      }
   } else {
      ASSERT((int)layer == IPPROTO_TCP);

      switch (optID) {
         /*
          * Note: All but TCP_KEEPIDLE are available in Mac OS X (at least
          * 10.11). iOS and Android are TBD. For now, let's keep it simple and
          * make all these available in the two known OS where all 3 exist
          * together, as they're typically often set as a group.
          * TODO: Possibly enable for other OS in more fine-grained fashion.
          */
#if defined(__linux__) || defined(VMX86_SERVER)
      case TCP_KEEPIDLE:
      case TCP_KEEPINTVL:
      case TCP_KEEPCNT:
#endif
      case TCP_NODELAY:
         isSupported = TRUE;
      }
   }

   if (!isSupported) {
      TCPSOCKLG0(tcpSocket,
                 "%s: Option layer/level [%d], option/name [%d]: "
                     "could not set OS option for TCP socket; "
                     "option not supported.\n",
                  __FUNCTION__, (int)layer, optID);
      return ASOCKERR_INVAL;
   }

   /* All good. Ready to actually set the OS option. */

   if (setsockopt(tcpSocket->fd, layer, optID,
                  valuePtr, inBufLen) != 0) {
      tcpSocket->genericErrno = Err_Errno();
      TCPSOCKLG0(tcpSocket,
                 "%s: Option layer/level [%d], option/name [%d]: "
                     "could not set OS option for TCP socket; "
                     "error [%d: %s].\n",
                  __FUNCTION__, (int)layer, optID,
                  tcpSocket->genericErrno,
                  Err_Errno2String(tcpSocket->genericErrno));
      return ASOCKERR_GENERIC;
   }

   TCPSOCKLG0(tcpSocket,
              "%s: Option layer/level [%d], option/name [%d]: successfully "
                  "set OS option for TCP socket.\n",
               __FUNCTION__, (int)layer, optID);

   return ASOCKERR_SUCCESS;
}


/*
 *----------------------------------------------------------------------------
 *
 * AsyncTCPSocketGetOption --
 *
 *      This is the reverse of AsyncTCPSocketSetOption().
 *
 * Results:
 *      ASOCKERR_SUCCESS on success, ASOCKERR_* otherwise.
 *      Invalid option+layer yields ASOCKERR_INVAL.
 *      Failure to get a native OS option yields ASOCKERR_GENERIC.
 *      *outBufLen being wrong (for the given option) at entry to function
 *      yields undefined behavior.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
AsyncTCPSocketGetOption(AsyncSocket *asyncSocket,     // IN/OUT
                        AsyncSocketOpts_Layer layer,  // IN
                        AsyncSocketOpts_ID optID,     // IN
                        void *valuePtr,               // OUT
                        socklen_t *outBufLen)         // IN/OUT
{
   /*
    * Maintenance: Keep this in sync with ...GetOption().
    * Substantive comments are kept light to avoid redundancy (refer to the
    * other function).
    */

   AsyncTCPSocket *tcpSocket = TCPSocket(asyncSocket);
   Bool isSupported;

   switch ((int)layer) {
   case SOL_SOCKET:
   case IPPROTO_TCP:
   case ASYNC_SOCKET_OPTS_LAYER_BASE:
      break;
   default:
      TCPSOCKLG0(tcpSocket,
                 "%s: Option layer [%d] (option [%d]) is not "
                     "supported for TCP socket.\n",
                  __FUNCTION__, (int)layer, optID);
      return ASOCKERR_INVAL;
   }

   if ((layer == ASYNC_SOCKET_OPTS_LAYER_BASE) &&
       (optID == ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE)) {
      ASSERT(*outBufLen >= sizeof(Bool));
      *outBufLen = sizeof(Bool);
      *((Bool *)valuePtr) = tcpSocket->sendLowLatency;
      TCPSOCKLG0(tcpSocket,
                 "%s: sendLowLatencyMode is [%d].\n",
                  __FUNCTION__, (int)tcpSocket->sendLowLatency);
      return ASOCKERR_SUCCESS;
   }

   isSupported = FALSE;
   if (layer == SOL_SOCKET) {
      switch (optID) {
      case SO_SNDBUF:
      case SO_RCVBUF:
         isSupported = TRUE;
      }
   } else {
      ASSERT((int)layer == IPPROTO_TCP);

      switch (optID) {
#ifdef __linux__
      case TCP_KEEPIDLE:
      case TCP_KEEPINTVL:
      case TCP_KEEPCNT:
#endif
      case TCP_NODELAY:
         isSupported = TRUE;
      }
   }

   if (!isSupported) {
      TCPSOCKLG0(tcpSocket,
                 "%s: Option layer/level [%d], option/name [%d]: "
                     "could not get OS option for TCP socket; "
                     "option not supported.\n",
                  __FUNCTION__, (int)layer, optID);
      return ASOCKERR_INVAL;
   }

   if (getsockopt(tcpSocket->fd, layer, optID,
                  valuePtr, outBufLen) != 0) {
      tcpSocket->genericErrno = Err_Errno();
      TCPSOCKLG0(tcpSocket,
                 "%s: Option layer/level [%d], option/name [%d]: "
                     "could not get OS option for TCP socket; "
                     "error [%d: %s].\n",
                  __FUNCTION__, (int)layer, optID,
                  tcpSocket->genericErrno,
                  Err_Errno2String(tcpSocket->genericErrno));
      return ASOCKERR_GENERIC;
   }

   TCPSOCKLG0(tcpSocket,
              "%s: Option layer/level [%d], option/name [%d]: successfully "
                  "got OS option for TCP socket.\n",
               __FUNCTION__, (int)layer, optID);

   return ASOCKERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketDestroy --
 *
 *    Free the AsyncTCPSocket struct and all of its child storage.
 *
 * Result
 *    None
 *
 * Side-effects
 *    Releases memory.
 *
 *-----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketDestroy(AsyncSocket *base)         // IN/OUT
{
   free(base);
}


#ifndef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_ListenSocketUDS --
 *
 *      Listens on the specified unix domain socket, and accepts new
 *      socket connections. Fires the connect callback with new
 *      AsyncTCPSocket object for each connection.
 *
 * Results:
 *      New AsyncTCPSocket in listening state or NULL on error
 *
 * Side effects:
 *      Creates new Unix domain socket, binds and listens.
 *
 *-----------------------------------------------------------------------------
 */

AsyncSocket *
AsyncSocket_ListenSocketUDS(const char *pipeName,               // IN
                            AsyncSocketConnectFn connectFn,     // IN
                            void *clientData,                   // IN
                            AsyncSocketPollParams *pollParams,  // IN
                            int *outError)                      // OUT: optional
{
   struct sockaddr_un addr;
   AsyncTCPSocket *asock;
   int tempError = ASOCKERR_SUCCESS;

   memset(&addr, 0, sizeof addr);
   addr.sun_family = AF_UNIX;
   Str_Strcpy(addr.sun_path, pipeName, sizeof addr.sun_path);

   asock = AsyncTCPSocketListenImpl((struct sockaddr_storage *)&addr,
                                    sizeof addr, connectFn, clientData,
                                    pollParams, &tempError);
   if (outError) {
      *outError = tempError;
   }

   return BaseSocket(asock);
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncTCPSocketListenerError --
 *
 *    Call the error handler from parent AsyncSocket object. The passed in
 *    parameter clientData is the parent AsyncSocket object.
 *
 * Result
 *    None
 *
 * Side-effects
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
AsyncTCPSocketListenerError(int error,           // IN
                            AsyncSocket *asock,  // IN
                            void *clientData)    // IN
{
   AsyncSocket *s = clientData;
   ASSERT(s);

   AsyncSocketHandleError(s, error);
}


/*
 *-----------------------------------------------------------------------------
 *
 * AsyncSocket_SetKeepAlive --
 *
 *      Set keep-alive socket option.
 *
 * Results:
 *      TRUE if successful.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
AsyncSocket_SetKeepAlive(AsyncSocket *asock, // IN
                         int keepIdle)       // IN
{
   int fd;

   fd = AsyncSocket_GetFd(asock);
   if (fd < 0) {
      Log(ASOCKPREFIX "(%p) is not valid.\n", asock);
      return FALSE;
   }
#ifdef WIN32
   {
      struct tcp_keepalive keepalive = { 1, keepIdle * 1000, keepIdle * 10 };
      DWORD ret;

      if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &keepalive, sizeof keepalive,
                   NULL, 0, &ret, NULL, NULL)) {
         Err_Number sysErr = ASOCK_LASTERROR();
         ASOCKLG0(asock, "Could not set keepalive options, error %d: %s\n",
                  sysErr, Err_Errno2String(sysErr));
      }
   }
#else
   {
      static const int keepAlive = 1;
      if (keepIdle) {
#  ifdef TCP_KEEPIDLE
#     define VMTCP_KEEPIDLE TCP_KEEPIDLE
#  else
#     define VMTCP_KEEPIDLE TCP_KEEPALIVE
#  endif
         if (setsockopt(fd,
                        IPPROTO_TCP,
                        VMTCP_KEEPIDLE,
                        (const char *)&keepIdle, sizeof keepIdle) != 0) {
            Err_Number sysErr = ASOCK_LASTERROR();
            ASOCKLG0(asock, "Could not set TCP_KEEPIDLE, error %d: %s\n",
                     sysErr, Err_Errno2String(sysErr));
            return FALSE;
         }
#  ifndef __APPLE__
      {
         /*
          * Default TCP setting is 7200 sec idle, and 75 interval.  So let's
          * divide keepIdle by 100 to get an interval.  For our 300 seconds
          * default that is 3 seconds keepIdle.
          */
         int keepIntvl = keepIdle / 100;

         if (keepIntvl < 1) {
            keepIntvl = 1;
         }
         if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL,
                        (const char *)&keepIntvl, sizeof keepIntvl) != 0) {
            Err_Number sysErr = ASOCK_LASTERROR();
            ASOCKLG0(asock, "Could not set TCP_KEEPIDLE, error %d: %s\n",
                     sysErr, Err_Errno2String(sysErr));
            return FALSE;
         }
      }
#  endif /* __APPLE__ */
      }
      if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
                     (const char *)&keepAlive, sizeof keepAlive) != 0) {
         Err_Number sysErr = ASOCK_LASTERROR();
         ASOCKLG0(asock, "Could not set TCP_KEEPIDLE, error %d: %s\n",
                  sysErr, Err_Errno2String(sysErr));
         return FALSE;
      }
   }
#endif /* WIN32 */
   return TRUE;
}
0707010000002B000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/auth   0707010000002C000081A400000000000000000000000168225505000003F7000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/auth/Makefile.am   ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libAuth.la

libAuth_la_SOURCES =
libAuth_la_SOURCES += authPosix.c

AM_CFLAGS = @LIB_AUTH_CPPFLAGS@
 0707010000002D000081A40000000000000000000000016822550500003617000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/auth/authPosix.c   /*********************************************************
 * Copyright (C) 2003-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h> // for access, crypt, etc.
#if !defined USE_PAM && !defined __APPLE__
#include <shadow.h>
#endif

#include "vmware.h"
#include "vm_product.h"
#include "codeset.h"
#include "posix.h"
#include "auth.h"
#include "str.h"
#include "log.h"

#ifdef USE_PAM
#   include "file.h"
#   include "config.h"
#   include "localconfig.h"
#   include <security/pam_appl.h>
#   include <dlfcn.h>
#endif

#if defined(HAVE_CONFIG_H) || defined(sun)
#  include <crypt.h>
#endif

#define LOGLEVEL_MODULE auth
#include "loglevel_user.h"

typedef struct {
   struct passwd  pwd;      /* must be first member */
   size_t         bufSize;
   uint8          buf[];
} AuthTokenInternal;

#ifdef USE_PAM
#if defined(sun)
#define CURRENT_PAM_LIBRARY	"libpam.so.1"
#elif defined(__FreeBSD__)
#define CURRENT_PAM_LIBRARY	"libpam.so"
#elif defined(__APPLE__)
#define CURRENT_PAM_LIBRARY	"libpam.dylib"
#else
#define CURRENT_PAM_LIBRARY	"libpam.so.0"
#endif

static typeof(&pam_start) dlpam_start;
static typeof(&pam_end) dlpam_end;
static typeof(&pam_authenticate) dlpam_authenticate;
static typeof(&pam_setcred) dlpam_setcred;
static typeof(&pam_acct_mgmt) dlpam_acct_mgmt;
static typeof(&pam_strerror) dlpam_strerror;
#if 0  /* These three functions are not used yet */
static typeof(&pam_open_session) dlpam_open_session;
static typeof(&pam_close_session) dlpam_close_session;
static typeof(&pam_chauthtok) dlpam_chauthtok;
#endif

static struct {
   void       **procaddr;
   const char  *procname;
} authPAMImported[] = {
#define IMPORT_SYMBOL(x) { (void **)&dl##x, #x }
   IMPORT_SYMBOL(pam_start),
   IMPORT_SYMBOL(pam_end),
   IMPORT_SYMBOL(pam_authenticate),
   IMPORT_SYMBOL(pam_setcred),
   IMPORT_SYMBOL(pam_acct_mgmt),
   IMPORT_SYMBOL(pam_strerror),
#undef IMPORT_SYMBOL
};

static void *authPamLibraryHandle = NULL;


/*
 *----------------------------------------------------------------------
 *
 * AuthLoadPAM --
 *
 *      Attempt to load and initialize PAM library.
 *
 * Results:
 *      FALSE if load and/or initialization failed.
 *      TRUE  if initialization succeeded.
 *
 * Side effects:
 *      libpam loaded.  We never unload - some libpam modules use
 *      syslog() function, and glibc does not survive when arguments
 *      specified to openlog() are freeed from memory.
 *
 *----------------------------------------------------------------------
 */

static Bool
AuthLoadPAM(void)
{
   void *pam_library;
   int i;

   if (authPamLibraryHandle) {
      return TRUE;
   }
   pam_library = Posix_Dlopen(CURRENT_PAM_LIBRARY, RTLD_LAZY | RTLD_GLOBAL);
   if (!pam_library) {
#if defined(VMX86_TOOLS)
      /*
       * XXX do we even try to configure the pam libraries?
       * potential nightmare on all the possible guest OSes
       */

      Log("System PAM libraries are unusable: %s\n", dlerror());

      return FALSE;
#else
      char *liblocation;
      char *libdir;

      libdir = LocalConfig_GetPathName(DEFAULT_LIBDIRECTORY, CONFIG_VMWAREDIR);
      if (!libdir) {
         Log("System PAM library unusable and bundled one not found.\n");

         return FALSE;
      }
      liblocation = Str_SafeAsprintf(NULL, "%s/lib/%s/%s", libdir,
                                     CURRENT_PAM_LIBRARY, CURRENT_PAM_LIBRARY);
      free(libdir);

      pam_library = Posix_Dlopen(liblocation, RTLD_LAZY | RTLD_GLOBAL);
      if (!pam_library) {
         Log("Neither system nor bundled (%s) PAM libraries usable: %s\n",
             liblocation, dlerror());
         free(liblocation);

         return FALSE;
      }
      free(liblocation);
#endif
   }
   for (i = 0; i < ARRAYSIZE(authPAMImported); i++) {
      void *symbol = dlsym(pam_library, authPAMImported[i].procname);

      if (!symbol) {
         Log("PAM library does not contain required function: %s\n",
             dlerror());
         dlclose(pam_library);
         return FALSE;
      }

      *(authPAMImported[i].procaddr) = symbol;
   }

   authPamLibraryHandle = pam_library;
   Log("PAM up and running.\n");

   return TRUE;
}


static const char *PAM_username;
static const char *PAM_password;

#if defined(sun)
static int PAM_conv (int num_msg,                     // IN:
		     struct pam_message **msg,        // IN:
		     struct pam_response **resp,      // OUT:
		     void *appdata_ptr)               // IN:
#else
static int PAM_conv (int num_msg,                     // IN:
		     const struct pam_message **msg,  // IN:
		     struct pam_response **resp,      // OUT:
		     void *appdata_ptr)               // IN:
#endif
{
   int count;
   struct pam_response *reply = calloc(num_msg, sizeof *reply);

   if (!reply) {
      return PAM_CONV_ERR;
   }

   for (count = 0; count < num_msg; count++) {
      switch (msg[count]->msg_style) {
      case PAM_PROMPT_ECHO_ON:
         reply[count].resp_retcode = PAM_SUCCESS;
         reply[count].resp = PAM_username ? strdup(PAM_username) : NULL;
         /* PAM frees resp */
         break;
      case PAM_PROMPT_ECHO_OFF:
         reply[count].resp_retcode = PAM_SUCCESS;
         reply[count].resp = PAM_password ? strdup(PAM_password) : NULL;
         /* PAM frees resp */
         break;
      case PAM_TEXT_INFO:
         reply[count].resp_retcode = PAM_SUCCESS;
         reply[count].resp = NULL;
         /* ignore it... */
         break;
      case PAM_ERROR_MSG:
         reply[count].resp_retcode = PAM_SUCCESS;
         reply[count].resp = NULL;
         /* Must be an error of some sort... */
      default:
         while (--count >= 0) {
            free(reply[count].resp);
         }
         free(reply);

         return PAM_CONV_ERR;
      }
   }

   *resp = reply;

   return PAM_SUCCESS;
}

static struct pam_conv PAM_conversation = {
    &PAM_conv,
    NULL
};
#endif /* USE_PAM */


/*
 *----------------------------------------------------------------------
 *
 * AuthAllocateToken --
 *
 *      Allocates an AuthTokenInternal structure, plus helper buffer
 *      large enough for the Posix_Get*_r calls.
 *
 * Side effects:
 *      None.
 *
 * Results:
 *      An AuthTokenInternal pointer. Free with Auth_CloseToken.
 *
 *----------------------------------------------------------------------
 */

static AuthTokenInternal *
AuthAllocateToken(void)
{
   long bufSize;
   AuthTokenInternal *ati;

   /*
    * We need to get the maximum size buffer needed by getpwuid_r from
    * sysconf. Multiply by 4 to compensate for the conversion to UTF-8
    * by the Posix_Get*_r() wrappers.
    */

   errno = 0;
   bufSize = sysconf(_SC_GETPW_R_SIZE_MAX);
   if ((errno != 0) || (bufSize <= 0)) {
      bufSize = 16 * 1024;  // Unlimited; pick something reasonable
   }

   bufSize *= 4;

   ati = Util_SafeMalloc(sizeof *ati + (size_t) bufSize);
   ati->bufSize = bufSize;

   return ati;
}


/*
 *----------------------------------------------------------------------
 *
 * Auth_GetPwnam --
 *
 *      Wrapper aroung Posix_Getpwnam_r.
 *
 * Side effects:
 *      None.
 *
 * Results:
 *      An AuthToken. Free with Auth_CloseToken.
 *
 *----------------------------------------------------------------------
 */

AuthToken
Auth_GetPwnam(const char *user)  // IN
{
   AuthTokenInternal *ati;
   int res;
   struct passwd *ppwd;

   ASSERT(user);

   ati = AuthAllocateToken();
   res = Posix_Getpwnam_r(user, &ati->pwd, ati->buf, ati->bufSize, &ppwd);

   if ((0 != res) || (ppwd == NULL)) {
      Auth_CloseToken((AuthToken) ati);
      return NULL;
   }

   ASSERT(ppwd == &ati->pwd);

   return (AuthToken) ati;
}


/*
 *----------------------------------------------------------------------
 *
 * Auth_AuthenticateSelf --
 *
 *      Authenticate as the current user.
 *
 * Side effects:
 *      None.
 *
 * Results:
 *      An AuthToken. Free with Auth_CloseToken.
 *
 *----------------------------------------------------------------------
 */

AuthToken
Auth_AuthenticateSelf(void)  // IN
{
   AuthTokenInternal *ati;
   int res;
   struct passwd *ppwd;

   ati = AuthAllocateToken();
   res = Posix_Getpwuid_r(getuid(), &ati->pwd, ati->buf, ati->bufSize, &ppwd);

   if ((0 != res) || (ppwd == NULL)) {
      Auth_CloseToken((AuthToken) ati);
      return NULL;
   }

   ASSERT(ppwd == &ati->pwd);

   return (AuthToken) ati;
}


/*
 *----------------------------------------------------------------------
 *
 * Auth_AuthenticateUserPAM --
 *
 *      Accept username/password, and service and verfiy it with PAM
 *
 * Side effects:
 *      None.
 *
 * Results:
 *
 *      The vmauthToken for the authenticated user, or NULL if
 *      authentication failed.
 *
 *----------------------------------------------------------------------
 */

AuthToken
Auth_AuthenticateUserPAM(const char *user,     // IN:
                         const char *pass,     // IN:
                         const char *service)  // IN:
{
#ifndef USE_PAM
   return NULL;
#else
   pam_handle_t *pamh;
   int pam_error;

   Bool success = FALSE;
   AuthTokenInternal *ati = NULL;

   ASSERT(service);

   if (!CodeSet_Validate(user, strlen(user), "UTF-8")) {
      Log("User not in UTF-8\n");
      goto exit;
   }
   if (!CodeSet_Validate(pass, strlen(pass), "UTF-8")) {
      Log("Password not in UTF-8\n");
      goto exit;
   }

   if (!AuthLoadPAM()) {
      goto exit;
   }

   /*
    * XXX PAM can blow away our syslog level settings so we need
    * to call Log_InitEx() again before doing any more Log()s
    */

#define PAM_BAIL if (pam_error != PAM_SUCCESS) { \
                  Log_Error("%s:%d: PAM failure - %s (%d)\n", \
                            __FUNCTION__, __LINE__, \
                            dlpam_strerror(pamh, pam_error), pam_error); \
                  dlpam_end(pamh, pam_error); \
                  goto exit; \
                 }
   PAM_username = user;
   PAM_password = pass;


   pam_error = dlpam_start(service, PAM_username, &PAM_conversation,
                           &pamh);

   if (pam_error != PAM_SUCCESS) {
      Log("Failed to start PAM (error = %d).\n", pam_error);
      goto exit;
   }

   pam_error = dlpam_authenticate(pamh, 0);
   PAM_BAIL;
   pam_error = dlpam_acct_mgmt(pamh, 0);
   PAM_BAIL;
   pam_error = dlpam_setcred(pamh, PAM_ESTABLISH_CRED);
   PAM_BAIL;
   dlpam_end(pamh, PAM_SUCCESS);

#undef PAM_BAIL

   /* If this point is reached, the user has been authenticated. */
   ati = (AuthTokenInternal *) Auth_GetPwnam(user);
   success = TRUE;

exit:
   if (success) {
      return (AuthToken) ati;
   } else {
      Auth_CloseToken((AuthToken) ati);
      return NULL;
   }

#endif // USE_PAM
}


/*
 *----------------------------------------------------------------------
 *
 * Auth_AuthenticateUser --
 *
 *      Accept username/password And verfiy it
 *
 * Side effects:
 *      None.
 *
 * Results:
 *
 *      The vmauthToken for the authenticated user, or NULL if
 *      authentication failed.
 *
 *----------------------------------------------------------------------
 */

AuthToken
Auth_AuthenticateUser(const char *user,  // IN:
                      const char *pass)  // IN:
{

#ifdef USE_PAM

#if defined(VMX86_TOOLS)
   return Auth_AuthenticateUserPAM(user, pass, "vmtoolsd");
#else
   return Auth_AuthenticateUserPAM(user, pass, "vmware-authd");
#endif

#else /* !USE_PAM */
   Bool success = FALSE;
   AuthTokenInternal *ati = NULL;

   if (!CodeSet_Validate(user, strlen(user), "UTF-8")) {
      Log("User not in UTF-8\n");
      goto exit;
   }
   if (!CodeSet_Validate(pass, strlen(pass), "UTF-8")) {
      Log("Password not in UTF-8\n");
      goto exit;
   }

   /* All of the following issues are dealt with in the PAM configuration
      file, so put all authentication/priviledge checks before the
      corresponding #endif below. */

   ati = (AuthTokenInternal *) Auth_GetPwnam(user);

   if (ati == NULL) {
      goto exit;
   }

   if (*ati->pwd.pw_passwd != '\0') {
      const char *pw = ati->pwd.pw_passwd;
      const char *namep;

#if !defined __APPLE__
      // support shadow passwords:
      if (strcmp(pw, "x") == 0) {
         struct spwd *sp = getspnam(user);

         if (sp) {
            pw = sp->sp_pwdp;
         }
      }
#endif

      namep = crypt(pass, pw);
      if (namep == NULL || strcmp(namep, pw) != 0) {
         // Incorrect password
         goto exit;
      }

      // Clear out crypt()'s internal state, too.
      crypt("glurp", pw);
   }

   success = TRUE;

exit:
   if (success) {
      return (AuthToken) ati;
   } else {
      Auth_CloseToken((AuthToken) ati);
      return NULL;
   }
#endif /* !USE_PAM */
}


/*
 *----------------------------------------------------------------------
 *
 * Auth_CloseToken --
 *
 *      Free the token allocated in Auth_AuthenticateUser.
 *
 * Side effects:
 *      None
 *
 * Results:
 *      None
 *
 *----------------------------------------------------------------------
 */

void
Auth_CloseToken(AuthToken token)  // IN (OPT):
{
   free((void *) token);
}
 0707010000002E000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/lib/backdoor   0707010000002F000081A400000000000000000000000168225505000004AC000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/backdoor/Makefile.am   ################################################################################
### Copyright (C) 2007-2016,2019 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libBackdoor.la

libBackdoor_la_SOURCES =
libBackdoor_la_SOURCES += backdoor.c
if ARCH_X32
   libBackdoor_la_SOURCES += backdoorGcc32.c
endif
if ARCH_X64
   libBackdoor_la_SOURCES += backdoorGcc64.c
endif
if ARCH_ARM64
   libBackdoor_la_SOURCES += backdoorGcc64_arm64.c
endif

07070100000030000081A40000000000000000000000016822550500003269000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/backdoor/backdoor.c    /*********************************************************
 * Copyright (C) 1999-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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

#include "backdoor_def.h"
#include "backdoor.h"
#include "backdoorInt.h"

#if defined(USE_HYPERCALL)
#include "vm_assert.h"
#include "x86cpuid.h"
#include "x86cpuid_asm.h"
#endif

#ifdef USE_VALGRIND
/*
 * When running under valgrind, we need to ensure we have the correct register
 * state when poking the backdoor. The VALGRIND_NON_SIMD_CALLx macros are used
 * to escape from the valgrind emulated CPU to the physical CPU.
 */
#include "vm_valgrind.h"
#endif

#if defined(BACKDOOR_DEBUG) && defined(USERLEVEL)
#if defined(__KERNEL__) || defined(_KERNEL)
#else
#   include "debug.h"
#endif
#   include <stdio.h>
#   define BACKDOOR_LOG(...) Debug(__VA_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(...)
#   define BACKDOOR_LOG_PROTO_STRUCT(x)
#   define BACKDOOR_LOG_HB_PROTO_STRUCT(x)
#endif

#if defined(USE_HYPERCALL)
/* Setting 'backdoorInterface' is idempotent, no atomic access is required. */
static BackdoorInterface backdoorInterface = BACKDOOR_INTERFACE_NONE;

static BackdoorInterface
BackdoorGetInterface(void)
{
   if (UNLIKELY(backdoorInterface == BACKDOOR_INTERFACE_NONE)) {
      CPUIDRegs regs;

      /* Check whether we're on a VMware hypervisor that supports vmmcall. */
      __GET_CPUID(1, &regs);
      if (CPUID_ISSET(1, ECX, HYPERVISOR, regs.ecx)) {
         __GET_CPUID(CPUID_HYPERVISOR_LEVEL_0, &regs);
         if (CPUID_IsRawVendor(&regs, CPUID_VMWARE_HYPERVISOR_VENDOR_STRING)) {
            if (__GET_EAX_FROM_CPUID(CPUID_HYPERVISOR_LEVEL_0) >=
                                     CPUID_VMW_FEATURES) {
               uint32 features = __GET_ECX_FROM_CPUID(CPUID_VMW_FEATURES);
               if (CPUID_ISSET(CPUID_VMW_FEATURES, ECX,
                               VMCALL_BACKDOOR, features)) {
                  backdoorInterface = BACKDOOR_INTERFACE_VMCALL;
                  BACKDOOR_LOG("Backdoor interface: vmcall\n");
               } else if (CPUID_ISSET(CPUID_VMW_FEATURES, ECX,
                                      VMMCALL_BACKDOOR, features)) {
                  backdoorInterface = BACKDOOR_INTERFACE_VMMCALL;
                  BACKDOOR_LOG("Backdoor interface: vmmcall\n");
               }
            }
         }
      }
      if (backdoorInterface == BACKDOOR_INTERFACE_NONE) {
         backdoorInterface = BACKDOOR_INTERFACE_IO;
         BACKDOOR_LOG("Backdoor interface: I/O port\n");
      }
   }
   return backdoorInterface;
}
#else
static BackdoorInterface
BackdoorGetInterface(void) {
   return BACKDOOR_INTERFACE_IO;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Backdoor_ForceLegacy --
 *
 *     In some cases, it may be desirable to use the legacy IO interface to
 *     access the backdoor, even if CPUID reports support for the VMCALL/VMMCALL
 *     interface.
 *
 * Params:
 *     force  Set to TRUE to force the library to use the legacy IO interface
 *            for dispatching backdoor calls; set to FALSE to use the
 *            autodetected interface.
 *
 * Side-effects:
 *      Changes the interface used to access the backdoor.
 *
 *-----------------------------------------------------------------------------
 */
#if defined(USE_HYPERCALL)
void
Backdoor_ForceLegacy(Bool force)
{
   if (force) {
      backdoorInterface = BACKDOOR_INTERFACE_IO;
   } else {
      backdoorInterface = BACKDOOR_INTERFACE_NONE;
      BackdoorGetInterface();
   }
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Backdoor --
 *
 *      Send a low-bandwidth basic request (16 bytes) to vmware, and return its
 *      reply (24 bytes).
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#ifdef USE_VALGRIND
static void
BackdoorInOutValgrind(uint16 tid, Backdoor_proto *myBp)
{
    Backdoor_InOut(myBp);
}
static void
BackdoorHbInValgrind(uint16 tid, Backdoor_proto_hb *myBp)
{
    BackdoorHbIn(myBp);
}
static void
BackdoorHbOutValgrind(uint16 tid, Backdoor_proto_hb *myBp)
{
    BackdoorHbOut(myBp);
}
#if defined(USE_HYPERCALL)
static void
BackdoorVmcallValgrind(uint16 tid, Backdoor_proto *myBp)
{
    Backdoor_Vmcall(myBp);
}
static void
BackdoorVmmcallValgrind(uint16 tid, Backdoor_proto *myBp)
{
    Backdoor_Vmmcall(myBp);
}
static void
BackdoorHbVmcallValgrind(uint16 tid, Backdoor_proto_hb *myBp)
{
    BackdoorHbVmcall(myBp);
}
static void
BackdoorHbVmmcallValgrind(uint16 tid, Backdoor_proto_hb *myBp)
{
    BackdoorHbVmmcall(myBp);
}
#endif
#endif

void
Backdoor(Backdoor_proto *myBp) // IN/OUT
{
   BackdoorInterface interface = BackdoorGetInterface();
   ASSERT(myBp);

   myBp->in.ax.word = BDOOR_MAGIC;

   switch (interface) {
   case BACKDOOR_INTERFACE_IO:
      myBp->in.dx.halfs.low = BDOOR_PORT;
      break;
#if defined(USE_HYPERCALL)
   case BACKDOOR_INTERFACE_VMCALL:  // Fall through.
   case BACKDOOR_INTERFACE_VMMCALL:
      myBp->in.dx.halfs.low = BDOOR_FLAGS_LB | BDOOR_FLAGS_READ;
      break;
#endif
   default:
      ASSERT(FALSE);
      break;
   }

   BACKDOOR_LOG("Backdoor: before ");
   BACKDOOR_LOG_PROTO_STRUCT(myBp);

   switch (interface) {
   case BACKDOOR_INTERFACE_IO:
#ifdef USE_VALGRIND
      VALGRIND_NON_SIMD_CALL1(BackdoorInOutValgrind, myBp);
#else
      Backdoor_InOut(myBp);
#endif
      break;
#if defined(USE_HYPERCALL)
   case BACKDOOR_INTERFACE_VMCALL:
#ifdef USE_VALGRIND
      VALGRIND_NON_SIMD_CALL1(BackdoorVmcallValgrind, myBp);
#else
      Backdoor_Vmcall(myBp);
#endif
      break;
   case BACKDOOR_INTERFACE_VMMCALL:
#ifdef USE_VALGRIND
      VALGRIND_NON_SIMD_CALL1(BackdoorVmmcallValgrind, myBp);
#else
      Backdoor_Vmmcall(myBp);
#endif
      break;
#endif // defined(USE_HYPERCALL)
   default:
      ASSERT(FALSE);
      break;
   }

   BACKDOOR_LOG("Backdoor: after ");
   BACKDOOR_LOG_PROTO_STRUCT(myBp);
}


void
BackdoorHb(Backdoor_proto_hb *myBp, // IN/OUT
           Bool outbound)           // IN
{
   BackdoorInterface interface = BackdoorGetInterface();
   ASSERT(myBp);

   myBp->in.ax.word = BDOOR_MAGIC;

   switch (interface) {
   case BACKDOOR_INTERFACE_IO:
      myBp->in.dx.halfs.low = BDOORHB_PORT;
      break;
#if defined(USE_HYPERCALL)
   case BACKDOOR_INTERFACE_VMCALL:  // Fall through.
   case BACKDOOR_INTERFACE_VMMCALL:
      myBp->in.dx.halfs.low = BDOOR_FLAGS_HB;
      if (outbound) {
         myBp->in.dx.halfs.low |= BDOOR_FLAGS_WRITE;
      } else {
         myBp->in.dx.halfs.low |= BDOOR_FLAGS_READ;
      }
      break;
#endif
   default:
      ASSERT(FALSE);
      break;
   }

   BACKDOOR_LOG("BackdoorHb: before ");
   BACKDOOR_LOG_HB_PROTO_STRUCT(myBp);

   switch (interface) {
   case BACKDOOR_INTERFACE_IO:
      if (outbound) {
#ifdef USE_VALGRIND
         VALGRIND_NON_SIMD_CALL1(BackdoorHbOutValgrind, myBp);
#else
         BackdoorHbOut(myBp);
#endif
      } else {
#ifdef USE_VALGRIND
         VALGRIND_NON_SIMD_CALL1(BackdoorHbInValgrind, myBp);
#else
         BackdoorHbIn(myBp);
#endif
      }
      break;
#if defined(USE_HYPERCALL)
   case BACKDOOR_INTERFACE_VMCALL:
#ifdef USE_VALGRIND
      VALGRIND_NON_SIMD_CALL1(BackdoorHbVmcallValgrind, myBp);
#else
      BackdoorHbVmcall(myBp);
#endif
      break;
   case BACKDOOR_INTERFACE_VMMCALL:
#ifdef USE_VALGRIND
      VALGRIND_NON_SIMD_CALL1(BackdoorHbVmmcallValgrind, myBp);
#else
      BackdoorHbVmmcall(myBp);
#endif
      break;
#endif
   default:
      ASSERT(FALSE);
      break;
   }

   BACKDOOR_LOG("BackdoorHb: after ");
   BACKDOOR_LOG_HB_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);

   BackdoorHb(myBp, TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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);

   BackdoorHb(myBp, FALSE);
}

#ifdef __cplusplus
}
#endif
   07070100000031000081A40000000000000000000000016822550500002CDE000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/backdoor/backdoorGcc32.c   /*********************************************************
 * Copyright (C) 2005-2016, 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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"
   );
}

#if defined(USE_HYPERCALL)
void
Backdoor_Vmcall(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"
        "vmcall"                "\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"
   );
}


void
Backdoor_Vmmcall(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"
        "vmmcall"               "\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"
   );
}
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * 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"
   );
}


#if defined(USE_HYPERCALL)
void
BackdoorHbVmcall(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"
        "vmcall"                "\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
BackdoorHbVmmcall(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"
        "vmmcall"               "\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"
   );
}
#endif

#ifdef __cplusplus
}
#endif

  07070100000032000081A4000000000000000000000001682255050000339E000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/backdoor/backdoorGcc64.c   /*********************************************************
 * Copyright (C) 2005-2016, 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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__(
#ifdef __APPLE__
        /*
         * Save %rbx on the stack because the Mac OS GCC doesn't want us to
         * clobber it - it erroneously thinks %rbx is the PIC register.
         * (Radar bug 7304232)
         */
        "pushq %%rbx"           "\n\t"
#endif
        "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)" "\n\t"
#ifdef __APPLE__
        "popq %%rbx"            "\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 __APPLE__
      /* %rbx is unchanged at the end of the function on Mac OS. */
      "rbx",
#endif
      "rcx", "rdx", "rsi", "rdi", "memory"
   );
}


#if defined(USE_HYPERCALL)
void
Backdoor_Vmcall(Backdoor_proto *myBp) // IN/OUT
{
   uint64 dummy;

   __asm__ __volatile__(
#ifdef __APPLE__
        /*
         * Save %rbx on the stack because the Mac OS GCC doesn't want us to
         * clobber it - it erroneously thinks %rbx is the PIC register.
         * (Radar bug 7304232)
         */
        "pushq %%rbx"           "\n\t"
#endif
        "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"
        "vmcall"                "\n\t"
        "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)" "\n\t"
#ifdef __APPLE__
        "popq %%rbx"            "\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 __APPLE__
      /* %rbx is unchanged at the end of the function on Mac OS. */
      "rbx",
#endif
      "rcx", "rdx", "rsi", "rdi", "memory"
   );
}

void
Backdoor_Vmmcall(Backdoor_proto *myBp) // IN/OUT
{
   uint64 dummy;

   __asm__ __volatile__(
#ifdef __APPLE__
        /*
         * Save %rbx on the stack because the Mac OS GCC doesn't want us to
         * clobber it - it erroneously thinks %rbx is the PIC register.
         * (Radar bug 7304232)
         */
        "pushq %%rbx"           "\n\t"
#endif
        "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"
        "vmmcall"               "\n\t"
        "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)" "\n\t"
#ifdef __APPLE__
        "popq %%rbx"            "\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 __APPLE__
      /* %rbx is unchanged at the end of the function on Mac OS. */
      "rbx",
#endif
      "rcx", "rdx", "rsi", "rdi", "memory"
   );
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * 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
{
   uint64 dummy;

   __asm__ __volatile__(
        "pushq %%rbp"           "\n\t"
#ifdef __APPLE__
        /*
         * Save %rbx on the stack because the Mac OS GCC doesn't want us to
         * clobber it - it erroneously thinks %rbx is the PIC register.
         * (Radar bug 7304232)
         */
        "pushq %%rbx"           "\n\t"
#endif
        "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"
#ifdef __APPLE__
        "popq %%rbx"            "\n\t"
#endif
        "popq %%rbp"
      : "=a" (dummy)
      : "0" (myBp)
      /*
       * vmware can modify the whole VM state without the compiler knowing
       * it. --hpreg
       */
      :
#ifndef __APPLE__
      /* %rbx is unchanged at the end of the function on Mac OS. */
      "rbx",
#endif
      "rcx", "rdx", "rsi", "rdi", "memory", "cc"
   );
}


void
BackdoorHbOut(Backdoor_proto_hb *myBp) // IN/OUT
{
   uint64 dummy;

   __asm__ __volatile__(
        "pushq %%rbp"           "\n\t"
#ifdef __APPLE__
        /*
         * Save %rbx on the stack because the Mac OS GCC doesn't want us to
         * clobber it - it erroneously thinks %rbx is the PIC register.
         * (Radar bug 7304232)
         */
        "pushq %%rbx"           "\n\t"
#endif
        "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"
#ifdef __APPLE__
        "popq %%rbx"            "\n\t"
#endif
        "popq %%rbp"
      : "=a" (dummy)
      : "0" (myBp)
      :
#ifndef __APPLE__
      /* %rbx is unchanged at the end of the function on Mac OS. */
      "rbx",
#endif
      "rcx", "rdx", "rsi", "rdi", "memory", "cc"
   );
}

#if defined(USE_HYPERCALL)
void
BackdoorHbVmcall(Backdoor_proto_hb *myBp) // IN/OUT
{
   uint64 dummy;

   __asm__ __volatile__(
        "pushq %%rbp"           "\n\t"
#ifdef __APPLE__
        /*
         * Save %rbx on the stack because the Mac OS GCC doesn't want us to
         * clobber it - it erroneously thinks %rbx is the PIC register.
         * (Radar bug 7304232)
         */
        "pushq %%rbx"           "\n\t"
#endif
        "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"
        "vmcall"                "\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"
#ifdef __APPLE__
        "popq %%rbx"            "\n\t"
#endif
        "popq %%rbp"
      : "=a" (dummy)
      : "0" (myBp)
      /*
       * vmware can modify the whole VM state without the compiler knowing
       * it. --hpreg
       */
      :
#ifndef __APPLE__
      /* %rbx is unchanged at the end of the function on Mac OS. */
      "rbx",
#endif
      "rcx", "rdx", "rsi", "rdi", "memory", "cc"
   );
}


void
BackdoorHbVmmcall(Backdoor_proto_hb *myBp) // IN/OUT
{
   uint64 dummy;

   __asm__ __volatile__(
        "pushq %%rbp"           "\n\t"
#ifdef __APPLE__
        /*
         * Save %rbx on the stack because the Mac OS GCC doesn't want us to
         * clobber it - it erroneously thinks %rbx is the PIC register.
         * (Radar bug 7304232)
         */
        "pushq %%rbx"           "\n\t"
#endif
        "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"
        "vmmcall"               "\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"
#ifdef __APPLE__
        "popq %%rbx"            "\n\t"
#endif
        "popq %%rbp"
      : "=a" (dummy)
      : "0" (myBp)
      /*
       * vmware can modify the whole VM state without the compiler knowing
       * it. --hpreg
       */
      :
#ifndef __APPLE__
      /* %rbx is unchanged at the end of the function on Mac OS. */
      "rbx",
#endif
      "rcx", "rdx", "rsi", "rdi", "memory", "cc"
   );
}
#endif

#ifdef __cplusplus
}
#endif

  07070100000033000081A40000000000000000000000016822550500001743000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/lib/backdoor/backdoorGcc64_arm64.c /*********************************************************
 * Copyright (C) 2015-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * backdoorGcc64_arm64.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 backdoor_def.h for implementation details.
 *
 */
#ifdef __cplusplus
extern "C" {
#endif

#include "backdoor.h"
#include "backdoor_def.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
{
   /*
    * The low-bandwidth backdoor call has the following effects:
    * o The VM can modify the calling vCPU's registers x0, x1, x2, x3, x4
    *   and x5.
    * o The VM can modify arbitrary guest memory.
    * So far the VM does not modify the calling vCPU's conditional flags.
    */
   __asm__ __volatile__(
      "ldp x4, x5, [%0, 8 * 4] \n\t"
      "ldp x2, x3, [%0, 8 * 2] \n\t"
      "ldp x0, x1, [%0       ] \n\t"
      "mov x7, %1              \n\t"
      "movk x7, %2, lsl #32    \n\t"
      "mrs xzr, mdccsr_el0     \n\t"
      "stp x4, x5, [%0, 8 * 4] \n\t"
      "stp x2, x3, [%0, 8 * 2] \n\t"
      "stp x0, x1, [%0       ]     "
      :
      : "r" (myBp),
        "M" (X86_IO_W7_WITH | X86_IO_W7_DIR | 2 << X86_IO_W7_SIZE_SHIFT),
        "i" (X86_IO_MAGIC)
      : "x0", "x1", "x2", "x3", "x4", "x5", "x7", "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.
 *
 *-----------------------------------------------------------------------------
 */

#define _BACKDOOR_HB(myBp, w7dir)                                             \
   __asm__ __volatile__(                                                      \
      "ldp x5, x6, [%0, 8 * 5] \n\t"                                          \
      "ldp x3, x4, [%0, 8 * 3] \n\t"                                          \
      "ldp x1, x2, [%0, 8 * 1] \n\t"                                          \
      "ldr x0,     [%0       ] \n\t"                                          \
      "mov x7, %1              \n\t"                                          \
      "movk x7, %2, lsl #32    \n\t"                                          \
      "mrs xzr, mdccsr_el0     \n\t"                                          \
      "stp x5, x6, [%0, 8 * 5] \n\t"                                          \
      "stp x3, x4, [%0, 8 * 3] \n\t"                                          \
      "stp x1, x2, [%0, 8 * 1] \n\t"                                          \
      "str x0,     [%0       ]     "                                          \
      :                                                                       \
      : "r" (myBp),                                                           \
        "M" (X86_IO_W7_STR | X86_IO_W7_WITH | w7dir),                         \
        "i" (X86_IO_MAGIC)                                                    \
      : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "memory"              \
   )

void
BackdoorHbIn(Backdoor_proto_hb *myBp) // IN/OUT
{
   /*
    * The high-bandwidth backdoor call has the following effects:
    * o The VM can modify the calling vCPU's registers x0, x1, x2, x3, x4, x5
    *   and x6.
    * o The VM can modify arbitrary guest memory.
    * So far the VM does not modify the calling vCPU's conditional flags.
    */
   _BACKDOOR_HB(myBp, X86_IO_W7_DIR);
}

void
BackdoorHbOut(Backdoor_proto_hb *myBp) // IN/OUT
{
   /*
    * The high-bandwidth backdoor call has the following effects:
    * o The VM can modify the calling vCPU's registers x0, x1, x2, x3, x4, x5
    *   and x6.
    * o The VM can modify arbitrary guest memory.
    * So far the VM does not modify the calling vCPU's conditional flags.
    */
   _BACKDOOR_HB(myBp, 0);
}

#undef _BACKDOOR_HB


#ifdef __cplusplus
}
#endif
 07070100000034000081A40000000000000000000000016822550500000893000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/backdoor/backdoorInt.h /*********************************************************
 * Copyright (C) 2005-2016, 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * backdoorInt.h --
 *
 *      Internal function prototypes for the real backdoor work.
 */

void BackdoorHbIn(Backdoor_proto_hb *bp);
void BackdoorHbOut(Backdoor_proto_hb *bp);
void BackdoorHb(Backdoor_proto_hb *myBp, Bool outbound);

/*
 * Are vmcall/vmmcall hypercall instructions available in the assembler?
 * Use the compiler version as a proxy.
 */
#if defined(__linux__) && defined(__GNUC__)
#define GCC_VERSION (__GNUC__ * 10000 + \
                     __GNUC_MINOR__ * 100 + \
                     __GNUC_PATCHLEVEL__)
#if GCC_VERSION > 40803 && !defined(__aarch64__)
#define USE_HYPERCALL
#endif
#endif

#if defined(USE_HYPERCALL)
void BackdoorHbVmcall(Backdoor_proto_hb *bp);
void BackdoorHbVmmcall(Backdoor_proto_hb *bp);
#endif
 07070100000035000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/dataMap    07070100000036000081A400000000000000000000000168225505000003DD000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/dataMap/Makefile.am    ################################################################################
### Copyright (C) 2013-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libDataMap.la

libDataMap_la_SOURCES =
libDataMap_la_SOURCES += dataMap.c
   07070100000037000081A4000000000000000000000001682255050000F882000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/dataMap/dataMap.c  /*********************************************************
 * Copyright (c) 2012-2017,2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif

#include "vm_basic_types.h"
#include "str.h"
#include "dataMap.h"
#include "vm_ctype.h"

/*
 * A union for all kinds of data fields types.
 */
typedef union {
   struct {
      int64  val;
   } number;

   struct {
      int32 length;
      char *str;
   } string;

   struct {
      int32 length;
      int64 *numbers;
   } numList;

   struct {
      char **strings; /* a list of string pointers, the last elment is NULL */
      int32 *lengths; /* a list of lengths for strings in strings. */
   } strList;
} DMFieldValue;

typedef struct {
   DMFieldType type;
   DMFieldValue value;
} DataMapEntry;

/* structure used in hashMap iteration callback */
typedef struct {
   DataMap *map;
   ErrorCode result;   /* store the previous callback function result value */

   /* the followings are used during serialization, deserialization and
    * pretty print.
    */
   char *buffer;
   uint32 buffLen;        /* available buffer size */
   uint32 maxNumElems;    /* this limits the number of elements for list print. */
   uint32 maxStrLen;      /* max number of bytes to print for each string */
   FieldIdNameEntry *fieldIdList;   /* array for field ID to name mapping */
   uint32 fieldIdListLen; /* fieldIdList size */
} ClientData;

static const uint64 magic_cookie = 0x4d41474943ULL;   /* 'MAGIC' */

/*
 *-----------------------------------------------------------------------------
 *
 * AddEntry_Int64 --
 *
 *      - low level helper function to add a numeric type entry to the map
 *
 * Result:
 *      0 on success
 *      error code otherwise
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
AddEntry_Int64(DataMap *that,      // IN/OUT
               DMKeyType key,      // IN
               int64  value)       // IN
{
   DataMapEntry *entry = (DataMapEntry *)malloc(sizeof(DataMapEntry));

   if (entry == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }
   entry->type = DMFIELDTYPE_INT64;
   entry->value.number.val = value;
   if (!HashMap_Put(that->map, &key, &entry)) {
      return DMERR_INSUFFICIENT_MEM;
   }
   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AddEntry_String --
 *
 *      Low level helper function to add a string type entry to the map.
 *      - 'str': ownership of the str pointer is passed to the map on success.
 *
 * Result:
 *      0 on success
 *      error code otherwise
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
AddEntry_String(DataMap *that,      // IN/OUT
                DMKeyType  key,     // IN
                char *str,          // IN
                int32 strLen)       // IN
{
   DataMapEntry *entry = (DataMapEntry *)malloc(sizeof(DataMapEntry));

   if (entry == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }
   entry->type = DMFIELDTYPE_STRING;
   entry->value.string.str = str;
   entry->value.string.length = strLen;

   if (!HashMap_Put(that->map, &key, &entry)) {
      return DMERR_INSUFFICIENT_MEM;
   }
   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AddEntry_Int64List --
 *
 *      Low level helper function to add a list of numbers to the map
 *      - 'numbers': ownership of this pointer is passed to the map on success.
 *      - 'listLen': the number of integers in the list of 'numbers'.
 *
 * Result:
 *      0 on success
 *      error code otherwise
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
AddEntry_Int64List(DataMap *that,            // IN/OUT
                   DMKeyType key,            // IN
                   int64 *numbers,           // IN
                   int32 listLen)            // IN
{
   DataMapEntry *entry = (DataMapEntry *)malloc(sizeof(DataMapEntry));

   if (entry == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }
   entry->type = DMFIELDTYPE_INT64LIST;
   entry->value.numList.numbers = numbers;
   entry->value.numList.length = listLen;

   if (!HashMap_Put(that->map, &key, &entry)) {
      return DMERR_INSUFFICIENT_MEM;
   }
   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AddEntry_StringList --
 *
 *      Low level helper function to add a list of trings to the map
 *      - 'strList': this pointer points to an array of pointers which points to
 *        strings. Upon success, the ownership of this array is passed to the
 *        map, no copy of strings is made. The last element in this array
 *        pointer must be NULL.
 *      - 'strLens': this is an array of integers which indicating the length of
 *        cooresponding string in strList. the ownership is passed to the map
 *        as well on success.
 *
 * Result:
 *      0 on success
 *      error code otherwise
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
AddEntry_StringList(DataMap *that,            // IN/OUT
                    DMKeyType key,            // IN
                    char **strList,           // IN
                    int32 *strLens)           // IN
{
   DataMapEntry *entry = (DataMapEntry *)malloc(sizeof(DataMapEntry));

   if (entry == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }
   entry->type = DMFIELDTYPE_STRINGLIST;
   entry->value.strList.strings = strList;
   entry->value.strList.lengths = strLens;

   if (!HashMap_Put(that->map, &key, &entry)) {
      return DMERR_INSUFFICIENT_MEM;
   }
   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FreeStringList --
 *
 *      Low level helper function to free a list of strings.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
FreeStringList(char **strList,    // IN
               int32 *strLens)    // IN
{
   if (*strList != NULL) {
      char **ptr;
      for (ptr = strList; *ptr != NULL; ptr++) {
         free(*ptr);
      }
   }

   free(strLens);
   free(strList);
}


/*
 *-----------------------------------------------------------------------------
 *
 * FreeEntryPayload --
 *
 *      - low level helper function to free entry payload only
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
FreeEntryPayload(DataMapEntry *entry)    // IN
{
   if (entry == NULL) {
      return;
   }

   switch(entry->type) {
      case DMFIELDTYPE_INT64:
         break;
      case DMFIELDTYPE_STRING:
         free(entry->value.string.str);
         break;
      case DMFIELDTYPE_INT64LIST:
         free(entry->value.numList.numbers);
         break;
      case DMFIELDTYPE_STRINGLIST:
         FreeStringList(entry->value.strList.strings,
                        entry->value.strList.lengths);
         break;
      default:
         ASSERT(0);    /*  we do not expect this to happen */
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * EncodeInt32 --
 *
 *      Low level helper function to encode an int32 into a byte buffer.
 *      - 'buf': *buf points to the output buffer, *buf is advanced properly.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
EncodeInt32(char **buf,   // IN/OUT
            int32 num)    // IN
{
   uint32 netVal = htonl((uint32)num);
   *((uint32 *)(*buf)) = netVal;
   (*buf) += sizeof(int32);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DecodeInt32 --
 *
 *      Low level helper function to decode an int32 from a byte buffer
 *      - 'buf': *buf points to the input buffer. *buf is advanced accordingly
 *               on success.
 *      - 'left': indicates number of bytes left in the input buffer, *left is
 *                updated accordingly on success.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
DecodeInt32(char **buf,    // IN/OUT
            int32 *left,   // IN/OUT
            int32 *num)    // OUT
{
   uint32 val;

   if (*left < sizeof(int32)) {
      return DMERR_TRUNCATED_DATA;
   }

   val = ntohl(*((uint32 *)(*buf)));
   *num = (int32) val;

   *buf += sizeof(int32);
   *left -= sizeof(int32);

   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * EncodeInt64 --
 *
 *      Low level helper function to encode an int64 into a byte buffer
 *      - 'buf': *buf points to the output buffer, *buf is advanced properly.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
EncodeInt64(char **buf,   // IN/OUT
            int64 num)    // IN
{
   EncodeInt32(buf, (uint32)num);
   EncodeInt32(buf, (uint32)(num >> 32));
}


/*
 *-----------------------------------------------------------------------------
 *
 * DecodeInt64 --
 *
 *      Low level helper function to decode an int64 from a byte buffer
 *      - 'buf': *buf points to the input buffer. *buf is advanced accordingly
 *               on success.
 *      - 'left': indicates number of bytes left in the input buffer, *left is
 *                updated accordingly on success.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
DecodeInt64(char **buf,    // IN/OUT
            int32 *left,   // IN/OUT
            int64 *num)    // OUT
{
   ErrorCode res;
   uint32 low;
   uint32 high;

   res = DecodeInt32(buf, left, &low);
   if (res == DMERR_SUCCESS) {
      res = DecodeInt32(buf, left, &high);
      if (res == DMERR_SUCCESS) {
         *num = (int64)(((((uint64)high)<< 32) | low));
      }
   }

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * EncodeString --
 *
 *      Low level helper function to encode a string into a byte buffer
 *      - 'buf': *buf points to the output buffer, *buf is advanced properly.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
EncodeString(char **buf,         // IN/OUT
             const char *str,    // IN
             int32 strLen)       // IN
{
   EncodeInt32(buf, strLen);
   memcpy(*buf, str, strLen);
   (*buf) += strLen;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DecodeString --
 *
 *      - low level helper function to decode a string from  a byte buffer
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
DecodeString(char **buf,         // IN/OUT
             int32 *left,        // IN/OUT
             char **str,         // OUT
             int32 *strLen)      // OUT
{
   ErrorCode res;

   res = DecodeInt32(buf, left, strLen);

   if (res != DMERR_SUCCESS) {
      return res;
   }

   if (*strLen <= 0) {
      return DMERR_BAD_DATA;
   }

   if (*left < *strLen) {
      return DMERR_TRUNCATED_DATA;
   }

   *str = (char *)malloc(*strLen);
   if (*str == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }

   memcpy(*str, *buf, *strLen);
   *buf += *strLen;
   *left -= *strLen;

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * EncodeInt64List --
 *
 *      Low level helper function to encode an int64 list into a byte buffer
 *      - 'buf': *buf points to the output buffer, *buf is advanced properly.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
EncodeInt64List(char **buf,        // IN/OUT
                int64 *numList,    // IN
                int32 listLen)     // IN
{
   int32 i;

   EncodeInt32(buf, (uint32)listLen);

   for(i = 0; i< listLen; i++) {
      EncodeInt64(buf, numList[i]);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DecodeInt64List --
 *
 *      Low level helper function to decode an int64 list from a byte buffer
 *      - 'buf': *buf points to the input buffer. *buf is advanced accordingly
 *               on success.
 *      - 'left': indicates number of bytes left in the input buffer, *left is
 *                updated accordingly on success.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
DecodeInt64List(char **buf,        // IN/OUT
                int32 *left,       // IN/OUT
                DMKeyType fieldId, // IN
                DataMap *that)     // OUT
{
   int32 listLen;
   int64 *numList = NULL;
   ErrorCode res;
   int32 i;

   res = DecodeInt32(buf, left, &listLen);
   if (res != DMERR_SUCCESS) {
      return res;
   }

   if (listLen < 0 || listLen > *left / sizeof(int64)) {
      /* listLen can be zero to support an empty list */
      return DMERR_BAD_DATA;
   }

   if (listLen) {
      numList = (int64 *)malloc(sizeof(int64) * listLen);
      if (numList == NULL) {
         return DMERR_INSUFFICIENT_MEM;
      }

      for(i = 0; i< listLen; i++) {
         res = DecodeInt64(buf, left, numList + i);
         if (res != DMERR_SUCCESS) {
            break;
         }
      }
   }

   if (res == DMERR_SUCCESS) {
      res = AddEntry_Int64List(that, fieldId, numList, listLen);
   }

   if (res != DMERR_SUCCESS) {
      /* clean up memory */
      free(numList);
   }

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FreeEntry --
 *
 *      - low level helper function to free an entry.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
FreeEntry(DataMapEntry *entry)    // IN
{
   FreeEntryPayload(entry);
   free(entry);
}


/*
 *-----------------------------------------------------------------------------
 *
 * LookupEntry --
 *
 *      - helper function to lookup an entry in the data map.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static DataMapEntry *
LookupEntry(const DataMap *that,    // IN
            DMKeyType fieldId)      // IN
{
   if ((that != NULL) && (that->map != NULL)) {
      void *rv = HashMap_Get(that->map, &fieldId);
      if (rv == NULL) {
          return NULL;   /* key not found */
      }
      return *((DataMapEntry **)rv);
   } else {
      return NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_GetType --
 *
 *      Get the value type for a given fieldID.
 *
 * Result:
 *      - DMFIELDTYPE_EMPTY is returned if entry does not exist.
 *      - One of other values in DMFieldType otherwise.
 *
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

DMFieldType
DataMap_GetType(const DataMap *that,    // IN
                DMKeyType fieldId)      // IN
{
   DataMapEntry *entry;

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      return DMFIELDTYPE_EMPTY;
   } else {
      return entry->type;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_Create --
 *
 *      - Initialize an Empty DataMap using the DataMap storage pointed
 *        by that.
 *      - The memory pointed by that may be allocated from heap or stack.
 *      - This function may allocate additional memory from the heap to
 *        initialize the DataMap object.
 *
 * Result:
 *      0(DMERR_SUCCESS) on success.
 *      error code on failures.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_Create(DataMap *that)    // IN/OUT
{
   if (that == NULL) {
      return DMERR_INVALID_ARGS;
   }

   that->map = HashMap_AllocMap(16, sizeof(DMKeyType), sizeof(DataMapEntry *));

   if (that->map != NULL) {
      that->cookie = magic_cookie;
      return DMERR_SUCCESS;
   }

   return DMERR_INSUFFICIENT_MEM;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToBufferUpdate --
 *
 *      Update result, advance buffer pointer, and adjust buffer left size etc
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ToBufferUpdate(ClientData *clientData,   // IN/OUT
               uint32 len)
{
   if (len >= clientData->buffLen) {
      clientData->result = DMERR_BUFFER_TOO_SMALL;
      clientData->buffer += clientData->buffLen;
      clientData->buffLen = 0;
   } else {
      clientData->buffer += len;
      clientData->buffLen -= len;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToBufferString --
 *
 *      Copy a string to a buffer.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ToBufferString(ClientData *clientData,   // IN/OUT
               const char *str)          // IN
{
   int32 len;

   if (clientData->result != DMERR_SUCCESS) {
      /* an error occurred already, so stop. */
      return;
   }

   ASSERT(clientData->buffLen > 0);

   len = snprintf(clientData->buffer, clientData->buffLen, "%s", str);

   ToBufferUpdate(clientData, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToBufferStringN --
 *
 *      Copy N bytes from a string to a buffer.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ToBufferStringN(ClientData *clientData,   // IN/OUT
                const char *str,          // IN
                uint32 len)                // IN
{
   uint32 copyLen = len;

   if (clientData->result != DMERR_SUCCESS) {
      /* an error occurred already, so stop. */
      return;
   }

   ASSERT(clientData->buffLen > 0);

   if (copyLen >= clientData->buffLen) {
      copyLen = clientData->buffLen - 1;
   }
   memcpy(clientData->buffer, str, copyLen);
   clientData->buffer[copyLen] = '\0';

   ToBufferUpdate(clientData, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * GetLimit --
 *
 *      Get the limit from the given max and actual length.
 *
 * Result:
 *      Returns the limit
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static int32
GetLimit(int32 max,        // IN:  -1 means no limit
         int32 length)     // IN
{
   if (max < 0) {
      return length;
   }

   return  max < length ? max : length;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToBufferInt64 --
 *
 *      Copy an int64 to a buffer.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ToBufferInt64(ClientData *clientData,   // IN/OUT
              int64 num)                // IN
{
   uint32 len;

   if (clientData->result != DMERR_SUCCESS) {
      /* an error occurred already, so stop. */
      return;
   }

   ASSERT(clientData->buffLen > 0);

   len = snprintf(clientData->buffer, clientData->buffLen,
                  "%"FMT64"d", num);

   ToBufferUpdate(clientData, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToBufferIdType --
 *
 *      Convert the ID, name and type to a string.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ToBufferIdType(ClientData *clientData,   // IN/OUT
               const char *idName,       // IN
               DMKeyType fieldId,        // IN
               const char *type)         // IN
{
   uint32 len;

   if (clientData->result != DMERR_SUCCESS) {
      /* an error occurred already, so stop. */
      return;
   }

   ASSERT(clientData->buffLen > 0);

   len = snprintf(clientData->buffer, clientData->buffLen,
                  "--> FIELD_%s(%d, %s): [",
                  idName, fieldId, type);

   ToBufferUpdate(clientData, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToBufferEndLine --
 *
 *      Print to end the current line
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ToBufferEndLine(ClientData *clientData)   // IN/OUT
{
   ToBufferString(clientData, "]\n");
}


/*
 *-----------------------------------------------------------------------------
 *
 * IsPrintable --
 *
 *      Check is a string is printable or not.
 *      - 'len': if printable, *len is the printable length, '\0' is excluded.
 *
 * Result:
 *      TRUE if yes, FALSE otherwise
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
IsPrintable(const char *str,    // IN
            int32 strLen,       // IN
            int32 *len)         // OUT
{
   Bool printable = TRUE;
   int32 cc;

   for (cc = 0; cc < strLen; cc ++) {
      /* isprint crashes with negative value in windows debug mode */
      if ((!CType_IsPrint(str[cc])) && (!CType_IsSpace(str[cc]))) {
         printable = FALSE;
         break;
      }
   }

   if (printable) {
      *len = strLen;
   } else {
      /* if only the last char is not printable and is '\0',
       * make it printable
       */
      if ((cc == strLen - 1) && (str[cc] == '\0')) {
         printable = TRUE;
         *len = strLen - 1;
      }
   }

   return printable;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToBufferHexString --
 *
 *      Print a string into a buffer, the string may be in binary format.
 *      - 'strLen': is the length of str.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ToBufferHexString(ClientData *clientData,    // IN/OUT
                  const char *str,           // IN
                  uint32 strLen)              // IN
{
   Bool printable;
   int32 len; /* the number of printable chars */
   int32 maxLen = clientData->maxStrLen;  /* -1 means no limit */

   if (clientData->result != DMERR_SUCCESS) {
      /* an error occurred already, so stop. */
      return;
   }

   ASSERT(clientData->buffLen > 0);

   maxLen = GetLimit(maxLen, strLen);

   printable = IsPrintable(str, maxLen, &len);

   if (printable) {
      ToBufferString(clientData, "\"");
      ToBufferStringN(clientData, str, len);
      if (maxLen < strLen) {
         ToBufferString(clientData, "...");  /* to indicate partial print */
      }
      ToBufferString(clientData, "\"");
   } else {
      int i;
      /* print the string in hex */

      ToBufferString(clientData, "(");

      for (i = 0; i < maxLen; i++) {
         char hexStr[3];

         if (i) {
            ToBufferString(clientData, ",");  /* separator */
         }
         snprintf(hexStr, sizeof(hexStr), "%02x", (unsigned char)(str[i]));
         ToBufferString(clientData, hexStr);
         if (clientData->result != DMERR_SUCCESS) {
            break;
         }
      }

      if (maxLen < strLen) {
         /* "..." to indicate partial print*/
         ToBufferString(clientData, ",...");
      }

      ToBufferString(clientData, ")");
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HashMapToStringEntryCb --
 *
 *      Convert to an entry to a string.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HashMapToStringEntryCb(void *key,            // IN
                       void *data,           // IN
                       void *userData)       // IN/OUT
{
   DataMapEntry *entry = *((DataMapEntry **)data);
   ClientData *clientData = (ClientData *)userData;
   DMKeyType fieldId = *((DMKeyType *)key);

   const char *idName = NULL; /* field ID name */

   if (clientData->result != DMERR_SUCCESS) {
      /* A previous error has occurred, so stop. */
      return;
   }

   if (clientData->fieldIdList!= NULL) {
      int32 cc;
      for (cc = 0; cc < clientData->fieldIdListLen; cc ++) {
         if (fieldId == clientData->fieldIdList[cc].fieldId) {
            idName = clientData->fieldIdList[cc].fieldName;
            break;
         }
      }
   }

   if (idName == NULL) {
      idName = "";
   }

   switch(entry->type) {
      case DMFIELDTYPE_INT64:
         {
            ToBufferIdType(clientData, idName, fieldId, "int64");
            ToBufferInt64(clientData, entry->value.number.val);
            ToBufferEndLine(clientData);
            break;
         }
      case DMFIELDTYPE_STRING:
         {
            ToBufferIdType(clientData, idName, fieldId, "string");
            ToBufferHexString(clientData, entry->value.string.str,
                              entry->value.string.length);
            ToBufferEndLine(clientData);
            break;
         }
      case DMFIELDTYPE_INT64LIST:
         {
            int32 cc;
            int32 max = GetLimit(clientData->maxNumElems,
                                 entry->value.numList.length);

            ToBufferIdType(clientData, idName, fieldId, "int64List");
            for (cc = 0; cc < max; cc++) {
               if (cc != 0) {
                  ToBufferString(clientData, ",");  /* add a separator */
               }
               ToBufferInt64(clientData, entry->value.numList.numbers[cc]);
            }

            if (max < entry->value.numList.length) {
               /* to indicate partial print*/
               ToBufferString(clientData, ",...");
            }

            ToBufferEndLine(clientData);
            break;
         }
      case DMFIELDTYPE_STRINGLIST:
         {
            char **strPtr = entry->value.strList.strings;
            int32 *lenPtr = entry->value.strList.lengths;
            int32 cc = 0;
            int32 max = clientData->maxNumElems;

            ToBufferIdType(clientData, idName, fieldId, "stringList");

            for (; *strPtr != NULL; strPtr++, lenPtr++, cc++) {
               if ((max >= 0) && (cc >= max)) {
                  break;
               }
               if (cc > 0) {
                  ToBufferString(clientData, ","); /* add a separator */
               }
               ToBufferHexString(clientData, *strPtr, *lenPtr);

               if (clientData->result != DMERR_SUCCESS) {
                  return;
               }
            }

            if (*strPtr != NULL) {
               /* to indicate partial print*/
               ToBufferString(clientData, ",...");
            }
            ToBufferEndLine(clientData);
            break;
         }
      default:
         {
            clientData->result = DMERR_UNKNOWN_TYPE;
            return;
         }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HashMapCalcEntrySizeCb --
 *
 *      - calculate how much space is needed to encode an entry
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HashMapCalcEntrySizeCb(void *key,            // IN
                       void *data,           // IN
                       void *userData)       // IN/OUT
{
   DataMapEntry *entry = *((DataMapEntry **)data);
   ClientData *clientData = (ClientData *)userData;
   uint32 oldLen = clientData->buffLen;
   uint32 *buffLen = &(clientData->buffLen);

   if (clientData->result != DMERR_SUCCESS) {
      /* a previous error has occurred, so stop. */
      return;
   }

   switch(entry->type) {
      case DMFIELDTYPE_INT64:
         {
            *buffLen += sizeof(int32);        /* type */
            *buffLen += sizeof(DMKeyType);    /* fieldId */
            *buffLen += sizeof(int64);        /* int value */
            break;
         }
      case DMFIELDTYPE_STRING:
         {
            *buffLen += sizeof(int32);        /* type */
            *buffLen += sizeof(DMKeyType);    /* fieldId */
            *buffLen += sizeof(int32);        /* string length */
            *buffLen += entry->value.string.length; /* string payload */
            break;
         }
      case DMFIELDTYPE_INT64LIST:
         {
            *buffLen += sizeof(int32);        /* type */
            *buffLen += sizeof(DMKeyType);    /* fieldId */
            *buffLen += sizeof(int32);        /* list size */
            *buffLen += sizeof(int64) * entry->value.numList.length;
            break;
         }
      case DMFIELDTYPE_STRINGLIST:
         {
            char **strPtr = entry->value.strList.strings;
            int32 *lenPtr = entry->value.strList.lengths;

            *buffLen += sizeof(int32);        /* type */
            *buffLen += sizeof(DMKeyType);    /* fieldId */
            *buffLen += sizeof(int32);        /* list size */

            for (; *strPtr != NULL; strPtr++, lenPtr++) {
               if (*buffLen < oldLen) {
                  clientData->result = DMERR_INTEGER_OVERFLOW;
                  return;
               }

               *buffLen += sizeof(int32);   /* string length */
               *buffLen += *lenPtr;        /* string payload */
            }
            break;
         }
      default:
         {
            clientData->result = DMERR_UNKNOWN_TYPE;
            return;
         }
   }

   if (*buffLen < oldLen) {
      clientData->result = DMERR_INTEGER_OVERFLOW;
      return;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HashMapSerializeEntryCb --
 *
 *      - serialize each entry into a byte buffer
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HashMapSerializeEntryCb(void *key,            // IN
                        void *data,           // IN
                        void *userData)       // OUT
{
   DataMapEntry *entry = *((DataMapEntry **)data);
   ClientData *clientData = (ClientData *)userData;
   char **buffPtr = &(clientData->buffer);
   char *buffPtrOrig = clientData->buffer;

   EncodeInt32(buffPtr, entry->type);   /* encode type */
   EncodeInt32(buffPtr, *((DMKeyType *)key));   /* encode field id*/

   switch(entry->type) {
      case DMFIELDTYPE_INT64:
         EncodeInt64(buffPtr, entry->value.number.val);
         break;
      case DMFIELDTYPE_STRING:
         EncodeString(buffPtr, entry->value.string.str,
                      entry->value.string.length);
         break;
      case DMFIELDTYPE_INT64LIST:
         EncodeInt64List(buffPtr, entry->value.numList.numbers,
                         entry->value.numList.length);
         break;
      case DMFIELDTYPE_STRINGLIST:
         {
            char **strPtr = entry->value.strList.strings;
            int32 *lenPtr = entry->value.strList.lengths;
            int32 listSize = 0;
            char *listSizePtr = *buffPtr;

            /*reserve the space for list size, we will update later*/
            *buffPtr += sizeof(int32);

            for (; *strPtr != NULL; strPtr++, lenPtr++) {
               EncodeString(buffPtr, *strPtr, *lenPtr);
               listSize ++;
            }

            EncodeInt32(&listSizePtr, listSize);  /* now update the list size */

            break;
         }
      default:
         ASSERT(0);    /*  we do not expect this to happen */
   }

   /* Update left buffer size so we can do a confidence check at the end. */
   clientData->buffLen -= (clientData->buffer - buffPtrOrig);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DecodeStringList --
 *
 *      Decode a string list entry and add to the dataMap
 *      - 'buf': *buf points to the input buffer. *buf is advanced accordingly
 *               on success.
 *      - 'left': indicates number of bytes left in the input buffer, *left is
 *                updated accordingly on success.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
DecodeStringList(char **buf,           // IN
                 int32 *left,          // IN
                 DMKeyType fieldId,    // IN
                 DataMap *that)        // OUT
{
   ErrorCode res;
   int32 listSize;
   char **strList;
   int32 *strLens;
   int32 i;

   res = DecodeInt32(buf, left, &listSize);
   if (res != DMERR_SUCCESS) {
      return res;
   }

   if (listSize < 0 || listSize > *left / sizeof(int32)) {
      /* listSize can be zero to support an empty list */
      return DMERR_BAD_DATA;
   }

   strList = (char **)calloc(listSize + 1, sizeof(char *));
   if (strList == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }
   if (listSize) {
      strLens = (int32 *)malloc(sizeof(int32) * listSize);
      if (strLens == NULL) {
         FreeStringList(strList, strLens);
         return DMERR_INSUFFICIENT_MEM;
      }
   } else {
      strLens = NULL;
   }

   for (i = 0; i < listSize; i++) {
      res = DecodeString(buf, left, &strList[i], &strLens[i]);
      if (res != DMERR_SUCCESS) {
         break;
      }
   }

   if (res == DMERR_SUCCESS) {
      res = AddEntry_StringList(that, fieldId, strList, strLens);
   }

   if (res != DMERR_SUCCESS) {
      FreeStringList(strList, strLens);
   }

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyStringList --
 *
 *      - copy string list entry into another map
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static ErrorCode
CopyStringListEntry(DMKeyType fieldId,                  // IN
                    const DataMapEntry *entry,          // IN
                    DataMap *dst)                       // OUT
{
   char **oldList = entry->value.strList.strings;
   int32 *lenPtr = entry->value.strList.lengths;
   char **newList;
   int32 *newLens;
   int32 listSize = 0;
   char **ptr = oldList;
   ErrorCode res = DMERR_SUCCESS;
   int32 i;

   /* get the list Size */
   for (; *ptr != NULL; ptr++) {
      listSize ++;
   }

   newList = (char **)calloc(listSize + 1, sizeof(char *));
   if (newList == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }

   newLens = (int32 *)malloc(sizeof(int32) * listSize);

   if (newLens == NULL) {
      free(newList);
      return DMERR_INSUFFICIENT_MEM;
   }

   /* copy the length vector */
   memcpy(newLens, lenPtr, listSize * sizeof(int32));

   /* copy string one by one */
   for (i = 0; i < listSize; i++) {
      newList[i] = (char *)malloc(newLens[i]);
      if (newList[i] == NULL) {
         res = DMERR_INSUFFICIENT_MEM;
         break;
      }
      memcpy(newList[i], oldList[i], newLens[i]);
   }

   if (res == DMERR_SUCCESS) {
      res = AddEntry_StringList(dst, fieldId, newList, newLens);
   }

   if (res != DMERR_SUCCESS) {
      FreeStringList(newList, newLens);
   }

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HashMapCopyEntryCb --
 *
 *      - Call back function to copy a dataMap entry.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HashMapCopyEntryCb(void *key,              // IN
                   void *data,             // IN
                   void *userData)         // IN/OUT
{
   DMKeyType fieldId = *((DMKeyType *)key);
   DataMapEntry *entry = *((DataMapEntry **)data);
   ClientData *clientData = (ClientData *)userData;
   DataMap *dst = clientData->map;
   ErrorCode res = DMERR_SUCCESS;

   if (clientData->result != DMERR_SUCCESS) {
      /* previous  calls have encountered error, so stop */
      return;
   }

   switch(entry->type) {
      case DMFIELDTYPE_INT64:
         res = AddEntry_Int64(dst, fieldId, entry->value.number.val);
         break;
      case DMFIELDTYPE_STRING:
      {
         char *str = (char *)malloc(entry->value.string.length);
         if (str == NULL) {
            res = DMERR_INSUFFICIENT_MEM;
            break;
         }
         memcpy(str, entry->value.string.str, entry->value.string.length);
         res = AddEntry_String(dst, fieldId, str, entry->value.string.length);
         if (res != DMERR_SUCCESS) {
            free(str);
         }
         break;
      }
      case DMFIELDTYPE_INT64LIST:
      {
         int64 *numList;
            numList = (int64 *)malloc(sizeof(int64) *
                  (entry->value.numList.length));

         if (numList == NULL) {
            res = DMERR_INSUFFICIENT_MEM;
         } else {
            res = AddEntry_Int64List(dst, fieldId, numList,
                                     entry->value.numList.length);
            if (res != DMERR_SUCCESS) {
               free(numList);
            }
         }
         break;
      }
      case DMFIELDTYPE_STRINGLIST:
         res = CopyStringListEntry(fieldId, entry, dst);
         break;
      default:
         ASSERT(0);    /*  we do not expect this to happen */
         break;
   }

   clientData->result = res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HashMapFreeEntryCb --
 *
 *      - call back function is called to free all entries in the hashMap.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HashMapFreeEntryCb(void *key, void *data, void *userData)
{
   DataMapEntry *entry = *((DataMapEntry **)data);
   FreeEntry(entry);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_Destroy --
 *
 *     Destroy the DataMap object pointed by that. Frees all the internal
 *     pointers in the object. However the memory pointed by that is not
 *     freed.
 *
 * Result:
 *     0 on success.
 *     error code on failures.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_Destroy(DataMap *that)    // IN/OUT
{
   if (that == NULL) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   HashMap_Iterate(that->map, HashMapFreeEntryCb, TRUE, NULL);

   HashMap_DestroyMap(that->map);

   that->map = NULL;
   that->cookie = 0;

   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_Copy --
 *
 *     Copy a DataMap, a deep copy.
 *     - 'dst': dst should *NOT* be initialized via DataMap_Create.
 *       the caller *MUST* call DataMap_Destroy to detroy dst upon success.
 *
 * Result:
 *     0 on success.
 *     error code on failures.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_Copy(const DataMap *src,  // IN
             DataMap *dst)        // OUT
{
   ClientData clientData;
   ErrorCode res;

   if (src == NULL || dst == NULL) {
      return  DMERR_INVALID_ARGS;
   }

   ASSERT(src->map != NULL);
   ASSERT(src->cookie == magic_cookie);

   /* init dst map */
   res = DataMap_Create(dst);
   if (res != DMERR_SUCCESS) {
      return res;
   }

   clientData.map = dst;
   clientData.result = DMERR_SUCCESS;

   HashMap_Iterate(src->map, HashMapCopyEntryCb, FALSE, &clientData);

   if (clientData.result != DMERR_SUCCESS) {
      DataMap_Destroy(dst);
   }

   return clientData.result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_Serialize --
 *
 *     Serialize a DataMap to a buffer.
 *     - 'buf': on success, this points to the allocated serialize buffer.
 *       The caller *MUST* free this buffer to avoid memory leak.
 *     - 'bufLen': on success, this indicates the length of the allocated
 *       buffer.
 *
 * Result:
 *     0 on success
 *     error code on failures.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_Serialize(const DataMap *that,     // IN
                  char **buf,              // OUT
                  uint32 *bufLen)          // OUT
{
   ClientData clientData;

   if (that == NULL || buf == NULL || bufLen == NULL) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   /* get the buffer size first */
   memset(&clientData, 0, sizeof clientData);
   HashMap_Iterate(that->map, HashMapCalcEntrySizeCb, FALSE, &clientData);
   if (clientData.result != DMERR_SUCCESS) {
      return clientData.result;
   }

   /* 4 bytes is payload length */
   *bufLen = clientData.buffLen + sizeof(uint32);
   if (*bufLen < clientData.buffLen) {
      return DMERR_INTEGER_OVERFLOW;
   }

   *buf = (char *)malloc(*bufLen);

   if (*buf == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }

   /* now serialize the map into the buffer */
   clientData.map = (DataMap *)that;
   clientData.result = DMERR_SUCCESS;
   clientData.buffer = *buf;

   /* Encode the payload size */
   EncodeInt32(&(clientData.buffer), clientData.buffLen);

   HashMap_Iterate(that->map, HashMapSerializeEntryCb, FALSE, &clientData);

   /* confidence check, make sure the buffer size is just used up. */
   ASSERT(clientData.buffLen == 0);

   if (clientData.result != DMERR_SUCCESS) {
      free(*buf);
      *buf = NULL;
      *bufLen = 0;
   }
   return clientData.result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_Deserialize --
 *
 *      Initialize an empty DataMap from a buffer.
 *      - 'that': the given map should *NOT* be initialized by the caller.
 *        On success, the caller needs to call DataMap_Destropy on 'that' to
 *        avoid any memory leak.
 *
 * Result:
 *      - 0 on success
 *      - error code on failures.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_Deserialize(const char *bufIn ,    // IN
                    const int32 bufLen,    // IN
                    DataMap *that)         // OUT
{
   ErrorCode res;
   int32 left = bufLen;   /* number of bytes undecoded */
   int32 len;
   char *buf = (char *)bufIn;

   if (that == NULL || bufIn == NULL || bufLen < 0) {
      return DMERR_INVALID_ARGS;
   }

   /* decode the encoded buffer length */
   res = DecodeInt32(&buf, &left, &len);
   if (res != DMERR_SUCCESS) {
      return res;
   }

   if (len > bufLen - sizeof(int32)) {
      return DMERR_TRUNCATED_DATA;
   }

   left = len;

   return DataMap_DeserializeContent(buf, left, that);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_DeserializeContent --
 *
 *      Initialize an empty DataMap from the content of the data map buffer
 *      - 'that': the given map should *NOT* be initialized by the caller.
 *        On success, the caller needs to call DataMap_Destropy on 'that' to
 *        avoid any memory leak.
 *
 * Result:
 *      - 0 on success
 *      - error code on failures.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_DeserializeContent(const char *content,    // IN
                           const int32 contentLen, // IN
                           DataMap *that)          // OUT
{
   ErrorCode res;
   int32 left = contentLen;   /* number of bytes undecoded */
   char *buf = (char *)content;

   res = DataMap_Create(that);   /* init the map */
   if (res != DMERR_SUCCESS) {
      return res;
   }

   while ((left> 0) && (res == DMERR_SUCCESS)) {
      DMFieldType type;
      DMKeyType fieldId;
      int32 val;

      res = DecodeInt32(&buf, &left, &val);     /* decode entry type */
      if (res != DMERR_SUCCESS) {
         goto out;
      }

      if (val >= DMFIELDTYPE_MAX) {
         res = DMERR_UNKNOWN_TYPE;
         goto out;
      }

      type = (DMFieldType)val;

      res = DecodeInt32(&buf, &left, &fieldId);   /* decode filedID */
      if (res != DMERR_SUCCESS) {
         goto out;
      }

      if (LookupEntry(that, fieldId) != NULL) {
         res = DMERR_DUPLICATED_FIELD_IDS;
         goto out;
      }

      /* decode individual entry */
      switch(type) {
         case DMFIELDTYPE_INT64:
         {
            int64 val;
            res = DecodeInt64(&buf, &left, &val);
            if (res != DMERR_SUCCESS) {
               goto out;
            }
            res = AddEntry_Int64(that, fieldId, val);
            break;
         }
         case DMFIELDTYPE_STRING:
         {
            char *str;
            int32 strLen;
            res = DecodeString(&buf, &left, &str, &strLen);
            if (res != DMERR_SUCCESS) {
               goto out;
            }
            res = AddEntry_String(that, fieldId, str, strLen);
            if (res != DMERR_SUCCESS) {
               /* clean up memory */
               free(str);
            }
            break;
         }
         case DMFIELDTYPE_INT64LIST:
         {
            res = DecodeInt64List(&buf, &left, fieldId, that);
            break;
         }
         case DMFIELDTYPE_STRINGLIST:
         {
            res = DecodeStringList(&buf, &left, fieldId, that);
            break;
         }
         default:
            res = DMERR_UNKNOWN_TYPE;
            break;
      }
   }

   if (res != DMERR_SUCCESS) {
      goto out;
   }
   return DMERR_SUCCESS;

out:
   DataMap_Destroy(that);
   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_SetInt64 --
 *
 *      Set an integer value to the map by given field id.
 *      - 'replace': when an entry with the same fieldID exists, if replace is
 *        true, error will be returned, otherwise, the existing entry will be
 *        replaced.
 *
 * Result:
 *      0 on success
 *      Otherwise, corresponding error code is returned.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_SetInt64(DataMap *that,      // IN/OUT
                 DMKeyType fieldId,  // IN
                 int64 value,        // IN
                 Bool replace)       // IN
{
   DataMapEntry *entry;

   if (that == NULL) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      /* need to add a new entry */
      return AddEntry_Int64(that, fieldId, value);
   } else if (!replace){
      return DMERR_ALREADY_EXIST;
   } else {
      if ((entry->type != DMFIELDTYPE_INT64)) {
         FreeEntryPayload(entry);
         entry->type = DMFIELDTYPE_INT64;
      }

      /* simple update */
      entry->value.number.val = value;
      return DMERR_SUCCESS;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_SetString --
 *
 *      Set a string field to a map.
 *      - 'str': the ownership of buffer pointed by str is passed to the map
 *        on success.
 *      - 'strLen': length of str, -1 means str is null terminated.
 *      - 'replace': when an entry with the same fieldID exists, if replace is
 *        true, error will be returned, otherwise, the existing entry will be
 *        replaced.
 *
 * Result:
 *      0 on success
 *      error code otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_SetString(DataMap *that,       // IN/OUT
                  DMKeyType fieldId,   // IN
                  char *str,           // IN
                  int32 strLen,        // IN
                  Bool replace)        // IN
{
   DataMapEntry *entry;

   if (that == NULL || str == NULL || (strLen < 0 && strLen != -1)) {
      return DMERR_INVALID_ARGS;
   }

   if (strLen == -1) {
      strLen = strlen(str);
   }

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      return AddEntry_String(that, fieldId, str, strLen);
   } else if (!replace){
      return DMERR_ALREADY_EXIST;
   } else {
      FreeEntryPayload(entry);

      entry->type = DMFIELDTYPE_STRING;
      entry->value.string.str = str;
      entry->value.string.length = strLen;

      return DMERR_SUCCESS;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_SetInt64List --
 *
 *      Set an integer value array to the map by given field id.
 *      - 'numList': the ownership of this list will be passed to the map on
 *        success, no copy of the list numbers is made.
 *      - 'replace': when an entry with the same fieldID exists, if replace is
 *        true, error will be returned, otherwise, the existing entry will be
 *        replaced.
 *
 * Result:
 *      0 on success
 *      Otherwise, corresponding error code is returned.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_SetInt64List(DataMap *that,        // IN/OUT
                     DMKeyType fieldId,    // IN
                     int64 *numList,       // IN
                     int32 listLen,        // IN
                     Bool replace)         // IN
{
   DataMapEntry *entry;

   if (that == NULL || numList == NULL || listLen < 0) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      /* need to add a new entry */
      return AddEntry_Int64List(that, fieldId, numList, listLen);
   } else if (!replace){
      return DMERR_ALREADY_EXIST;
   } else {
      FreeEntryPayload(entry);

      entry->type = DMFIELDTYPE_INT64LIST;
      entry->value.numList.numbers = numList;
      entry->value.numList.length = listLen;

      return DMERR_SUCCESS;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_SetStringList --
 *
 *      Set a string list field to a map.
 *      - 'strList': this pointer points to an array of string pointers.
 *        Upon success, the ownership of this array is passed to the
 *        map, no copy of strings is made. The last element in this array
 *        pointer must be NULL.
 *      - 'strLens': this is an array of integers which indicating the length of
 *        cooresponding string in strList. the ownership is passed to the map
 *        as well on success.
 *      - 'replace': when an entry with the same fieldID exists, if replace is
 *        true, error will be returned, otherwise, the existing entry will be
 *        replaced.
 *
 * Result:
 *      0 on success
 *      error code otherwise.
 *
 * Side-effects:
 *     None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_SetStringList(DataMap *that,          // IN/OUT
                      DMKeyType fieldId,      // IN
                      char **strList,         // IN
                      int32 *strLens,         // IN
                      Bool replace)           // IN
{
   DataMapEntry *entry;

   if (that == NULL || strList == NULL || strLens == NULL) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      /* need to add a new entry */
      return AddEntry_StringList(that, fieldId, strList, strLens);
   } else if (!replace){
      return DMERR_ALREADY_EXIST;
   } else {
      FreeEntryPayload(entry);

      entry->type = DMFIELDTYPE_STRINGLIST;
      entry->value.strList.strings = strList;
      entry->value.strList.lengths = strLens;

      return DMERR_SUCCESS;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_GetInt64 --
 *
 *      - Get an integer value from the map by given field id.
 *
 * Result:
 *      - DMERR_SUCCESS: *value has the value.
 *      - DMERR_NOT_FOUND:  no data is found
 *      - DMERR_TYPE_MISMATCH:  the entry in the map has other type.
 *      - Otherwise, corresponding error code is returned.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_GetInt64(const DataMap *that,      // IN
                 DMKeyType fieldId,        // IN
                 int64 *value)             // OUT
{
   DataMapEntry *entry;

   if (that == NULL || value == NULL) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      return DMERR_NOT_FOUND;
   }

   if (entry->type != DMFIELDTYPE_INT64) {
      return DMERR_TYPE_MISMATCH;
   }

   *value = entry->value.number.val;
   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_GetString --
 *
 *      Get a string value from the map by given fieldId.
 *      - 'str': upon success, *str is a string pointer which points to internal
 *        map structures, and should not be modified.
 *      - 'strLen': upon success, *strLen indicates the length of str.
 *
 * Result:
 *      - DMERR_SUCCESS: *str points to the string field in the map, *strLen has
 *        the string length.
 *      - DMERR_NOT_FOUND:  no data is found
 *      - DMERR_TYPE_MISMATCH:  the entry in the map has other type.
 *      - Otherwise, corresponding error code is returned.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_GetString(const DataMap *that,      // IN
                  DMKeyType fieldId,        // IN
                  char **str,               // OUT
                  int32 *strLen)            // OUT
{
   DataMapEntry *entry;

   if (that == NULL || str == NULL || strLen == NULL) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      return DMERR_NOT_FOUND;
   }

   if (entry->type != DMFIELDTYPE_STRING) {
      return DMERR_TYPE_MISMATCH;
   }

   *str = entry->value.string.str;
   *strLen = entry->value.string.length;
   return DMERR_SUCCESS;

}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_GetInt64List --
 *
 *      Get an integer value list from the map by given field id.
 *      - 'numList': on success, *numList points data structures in the map,
 *        and should not be modified. *numList is an array of int64.
 *      - 'listLen': on success, *listLen indicates number of elements in
 *        numList.
 *
 * Result:
 *      - DMERR_SUCCESS: *numbers points to the list of numbers in the map,
 *        *listLen has the list length.
 *      - DMERR_NOT_FOUND:  no data is found
 *      - DMERR_TYPE_MISMATCH:  the entry in the map has other type.
 *      - Otherwise, corresponding error code is returned.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_GetInt64List(const DataMap *that,      // IN
                     DMKeyType fieldId,        // IN
                     int64 **numList,          // OUT
                     int32 *listLen)           // OUT
{
   DataMapEntry *entry;

   if (that == NULL || numList == NULL || listLen == NULL) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      return DMERR_NOT_FOUND;
   }

   if (entry->type != DMFIELDTYPE_INT64LIST) {
      return DMERR_TYPE_MISMATCH;
   }

   *numList = entry->value.numList.numbers;
   *listLen  = entry->value.numList.length;
   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_GetStringList --
 *
 *      Get a string list from the map by given fieldId.
 *      - 'strList': on success, *strList points to an array of strings which
 *        is owned by the map, and should not be modified.
 *        The last element in the array is NULL.
 *      - 'strLen': on success, *strLen is an array of numbers indicates the
 *        length of corresponding string in strList.
 *        This should not be modified either.
 *
 * Result:
 *      - DMERR_SUCCESS: *strList points to the list of strings in the map,
 *        *strLens has the length for each string.
 *      - DMERR_NOT_FOUND:  no data is found
 *      - DMERR_TYPE_MISMATCH:  the entry in the map has other type.
 *      - Otherwise, corresponding error code is returned.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_GetStringList(const DataMap *that,      // IN
                      DMKeyType fieldId,        // IN
                      char ***strList,          // OUT
                      int32 **strLens)          // OUT
{
   DataMapEntry *entry;

   if (that == NULL || strList == NULL || strLens == NULL) {
      return DMERR_INVALID_ARGS;
   }

   ASSERT(that->cookie == magic_cookie);

   entry = LookupEntry(that, fieldId);
   if (entry == NULL) {
      return DMERR_NOT_FOUND;
   }

   if (entry->type != DMFIELDTYPE_STRINGLIST) {
      return DMERR_TYPE_MISMATCH;
   }

   *strList = entry->value.strList.strings;
   *strLens = entry->value.strList.lengths;
   return DMERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DataMap_ToString --
 *
 *      Dump the content of the whole map into a user supplied buffer.
 *      - 'fieldIdList': this is an array of FieldIdNameEntry.
 *      - 'fieldIdListLen': the size of of feildIdList array.
 *      - 'maxNumElements': for list elements, this is the max number of
 *                          elemenents we print.  -1 means no limit.
 *      - 'maxStrLen': max number of bytes to print for a string, -1 no limit.
 *      - 'buf': *buf is a null terminated output string buffer, the caller
 *               *MUST* free this buffer later to avoid memory leak.
 *
 * Result:
 *      DMERR_SUCCESS on success.
 *      error code on failures.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ErrorCode
DataMap_ToString(const DataMap *that,               // IN
                 FieldIdNameEntry *fieldIdList,     // IN
                 int32 fieldIdListLen,              // IN
                 int32 maxNumElements,              // IN
                 int32 maxStrLen,                   // IN
                 char **buf)                        // OUT
{
   ClientData clientData;
   char *buffPtr;

   /* This API is for debugging only, so we use hard coded buffer size */
   const int32 maxBuffSize = 10 * 1024;

   if (that == NULL || buf == NULL ||
      (maxNumElements < 0 && maxNumElements != -1) ||
      (maxStrLen < 0 && maxStrLen != -1)) {
      return DMERR_INVALID_ARGS;
   }

   *buf = NULL;
   ASSERT(that->cookie == magic_cookie);

   /* get the buffer size first */
   memset(&clientData, 0, sizeof clientData);
   clientData.map = (DataMap *)that;
   clientData.fieldIdList = fieldIdList;
   clientData.fieldIdListLen = fieldIdListLen;
   clientData.maxNumElems = maxNumElements;
   clientData.maxStrLen = maxStrLen;
   clientData.buffLen = maxBuffSize;

   buffPtr = (char *)malloc(maxBuffSize);
   if (buffPtr == NULL) {
      return DMERR_INSUFFICIENT_MEM;
   }
   *buf = buffPtr;

   clientData.buffer = buffPtr;

   ToBufferString(&clientData, "--> Begin\n");

   /* output each entry in the map */
   HashMap_Iterate(that->map, HashMapToStringEntryCb, FALSE, &clientData);

   ToBufferString(&clientData, "--> End.\n");

   /* confidence check, make sure the buffer is not overflown. */
   ASSERT(clientData.buffLen >= 0);
   ASSERT(buffPtr + maxBuffSize >= clientData.buffer);

   if (clientData.result == DMERR_BUFFER_TOO_SMALL) {
      const char truncStr[] = " DATA TRUNCATED!!!\n";

      ASSERT(maxBuffSize > strlen(truncStr));
      Str_Strcpy(buffPtr + maxBuffSize - strlen(truncStr) - 1, truncStr,
	         strlen(truncStr) + 1);
      return DMERR_SUCCESS;
   } else if (clientData.result != DMERR_SUCCESS) {
      *buf = NULL;
      free(buffPtr);
      return clientData.result;
   } else {
      clientData.buffer[0] = '\0';
      return DMERR_SUCCESS;
   }
}

  07070100000038000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/dict   07070100000039000081A400000000000000000000000168225505000003D3000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/dict/Makefile.am   ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libDict.la

libDict_la_SOURCES =
libDict_la_SOURCES += dictll.c
 0707010000003A000081A40000000000000000000000016822550500003DD2000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/dict/dictll.c  /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dictll.c --
 *
 *    Low-level dictionary format --hpreg
 */


#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "vmware.h"
#include "vmstdio.h"
#include "escape.h"
#include "dictll.h"
#include "util.h"

#define UTF8_BOM "\xEF\xBB\xBF"


/* Duplicate a buffer --hpreg. The result is NUL-terminated */
static void *
BufDup(void const * const bufIn,  // IN: buffer to duplicate
       unsigned int const sizeIn) // IN: buffer size in bytes
{
   char *bufOut;

   ASSERT(bufIn);

   bufOut = Util_SafeMalloc(sizeIn + 1);
   memcpy(bufOut, bufIn, sizeIn);
   bufOut[sizeIn] = '\0';

   return bufOut;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Walk --
 *
 *    While 'bufIn' points to a byte in 'sentinel', increment it --hpreg
 *
 * Note:
 *    If your 'bufIn' is a NUL-terminated C string, you should rather make sure
 *    that the NUL byte is not in your 'sentinel'
 *
 * Results:
 *    The new 'buf'
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void const *
Walk(void const * const bufIn,   // IN
     int const * const sentinel) // IN
{
   char const *buf;

   buf = (char const *)bufIn;
   ASSERT(buf);

   /* Unsigned does matter --hpreg */
   while (sentinel[(unsigned char)*buf]) {
      buf++;
   }

   return buf;
}

/*
 * XXX Document the escaping/unescaping process: rationale for which chars we
 *     escape, and how we escape --hpreg
 *
 * The dictionary line format:
 *
 *    <ws> <name> <ws> = <ws> <value> <ws> <comment>
 * or
 *    <ws> <name> <ws> = <ws> " <quoted-value> " <ws> <comment>
 * or
 *    <ws> <name> <ws> = <ws> <comment> (Implied value of empty string)
 * or
 *    <ws> <comment>
 *
 * where
 *    <name> does not contain any whitespace or = or #
 *    <value> does not contain any double-quote or #
 *    <quoted-value> does not contain any double-quote
 *    <comment> begins with # and ends at the end of the line
 *    <ws> is a sequence spaces and/or tabs
 *    <comment> and <ws> are optional
 */


/*
 *-----------------------------------------------------------------------------
 *
 * DictLL_UnmarshalLine --
 *
 *      Reads a line from the bufSize-byte buffer buf, which holds one or more
 *      new-line delimited lines.  The buffer is not necessarily
 *      NUL-terminated.
 *
 * Results:
 *      The beginning of the next line if a line was successfully parsed.  In
 *      that case, '*line' is the allocated line.  If the line is well-formed,
 *      then '*name' and '*value' are allocated strings. Otherwise they are
 *      both NULL.
 *
 *      A null pointer at the end of the buffer, in which case *line, *name,
 *      and *value are set to null pointers.
 *
 * Side effects:
 *      Advances *buf to the beginning of the next line.
 *
 *-----------------------------------------------------------------------------
 */

const char *
DictLL_UnmarshalLine(const char *buf,   // IN: buffer to parse
                     size_t bufSize,    // IN: buffer size in bytes
                     char **line,       // OUT: malloc()'d entire line
                     char **name,       // OUT: malloc()'d name or NULL
                     char **value)      // OUT: malloc()'d value or NULL
{
   /* Space and tab --hpreg */
   static int const ws_in[] = {
      0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   };
   /* Everything but NUL, space, tab and pound --hpreg */
   static int const wsp_out[] = {
      0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
      0, 1, 1, 0, 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, 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,
   };
   /* Everything but NUL, space, tab, pound and equal --hpreg */
   static int const wspe_out[] = {
      0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
      0, 1, 1, 0, 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, 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, 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,
   };
   /* Everything but NUL and double quote --hpreg */
   static int const q_out[] = {
      0, 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, 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, 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,
   };
   char const *nBegin;
   char const *nEnd;
   char const *vBegin;
   char const *vEnd;
   char const *tmp;
   char *myLine;
   char *myName;
   char *myValue;
   const char *lineEnd;
   const char *nextLine;

   ASSERT(buf);
   ASSERT(line);
   ASSERT(name);
   ASSERT(value);

   /* Check for end of buffer. */
   if (bufSize == 0) {
      *line = NULL;
      *name = NULL;
      *value = NULL;
      return NULL;
   }

   /* Find end of this line, beginning of next. */
   lineEnd = memchr(buf, '\n', bufSize);
   if (lineEnd != NULL) {
      nextLine = lineEnd + 1;
   } else {
      nextLine = lineEnd = buf + bufSize;
   }

   /* Make local copy of line. */
   myLine = BufDup(buf, lineEnd - buf);

   /* Check if the line is well-formed --hpreg */
   nBegin = Walk(myLine, ws_in);
   nEnd = Walk(nBegin, wspe_out);
   tmp = Walk(nEnd, ws_in);
   if (nBegin == nEnd || *tmp != '=') {
      goto weird;
   }
   tmp++;
   tmp = Walk(tmp, ws_in);
   if (*tmp == '"') {
      tmp++;
      vBegin = tmp;
      vEnd = Walk(vBegin, q_out);
      tmp = vEnd;
      if (*tmp != '"') {
         goto weird;
      }
      tmp++;
   } else {
      vBegin = tmp;
      vEnd = Walk(vBegin, wsp_out);
      tmp = vEnd;
   }
   tmp = Walk(tmp, ws_in);
   if (*tmp != '\0' && *tmp != '#') {
      goto weird;
   }

   /* The line is well-formed. Extract the name and value --hpreg */

   myName = BufDup(nBegin, nEnd - nBegin);
   myValue = Escape_Undo('|', vBegin, vEnd - vBegin, NULL);
   VERIFY(myValue);

   *line = myLine;
   *name = myName;
   *value = myValue;

   return nextLine;

weird:
   /* The line is not well-formed. Let the upper layers handle it --hpreg */

   *line = myLine;
   *name = NULL;
   *value = NULL;

   return nextLine;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DictLL_ReadLine --
 *
 *    Read the next line from a dictionary file --hpreg
 *
 * Results:
 *    2 on success: '*line' is the allocated line
 *                  If the line is well-formed, then '*name' and '*value' are
 *                  allocated strings. Otherwise they are both NULL.
 *    1 if there is no next line (end of stream)
 *    0 on failure: errno is set accordingly
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */
 
int
DictLL_ReadLine(FILE *stream, // IN: stream to read
                char **line,  // OUT: malloc()'d line or null pointer
                char **name,  // OUT: malloc()'d name or null pointer
                char **value) // OUT: malloc()'d value or null pointer
{
   char *myLine;
   size_t myLineLen;

   ASSERT(stream);
   ASSERT(line);
   ASSERT(name);
   ASSERT(value);

   *line = NULL;
   *name = NULL;
   *value = NULL;

   switch (StdIO_ReadNextLine(stream, &myLine, 0, &myLineLen)) {
   case StdIO_Error:
      return 0;

   case StdIO_EOF:
      return 1;

   case StdIO_Success:
      if (DictLL_UnmarshalLine(myLine, myLineLen,
                               line, name, value) == NULL) {
         *line = BufDup("", 0);
      }
      free(myLine);
      return 2;

   default:
      NOT_IMPLEMENTED();
   }
   NOT_REACHED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * DictLL_MarshalLine --
 *
 *    Marshals a line, appending the data to a DynBuf.  If 'name' is NULL,
 *    '*value' contains the whole line to write verbatim.  Otherwise a proper
 *    name/value pair is written.
 *
 * Results:
 *    TRUE on success, FALSE on failure (caused by memory allocation failure).
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DictLL_MarshalLine(DynBuf *output,    // IN/OUT: output buffer
                   char const *name,  // IN/OPT: name to marshal
                   char const *value) // IN: value to marshal
{
   size_t size;

   if (name) {
      /*
       * Double quote, pipe, 0x7F, and all control characters but
       * tab --hpreg
       * 0x80 to 0xff are unescaped so characters in encodings
       * like UTF-8 will be displayed normally.
       */
      static int const toEscape[] = {
         1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      };
      char *evalue;

      /* Write a well-formed line --hpreg */

      evalue = Escape_Do('|', toEscape, value, (uint32)strlen(value), &size);
      if (   !DynBuf_Append(output, name, (uint32)strlen(name))
          || !DynBuf_Append(output, " = \"", 4)
          || (size && !DynBuf_Append(output, evalue, size))
          || !DynBuf_Append(output, "\"", 1)) {
         free(evalue);

         return FALSE;
      }
      free(evalue);
   } else {
      /* Write the line as passed from the upper layers --hpreg */

      size = (uint32)strlen(value);
      if (size && !DynBuf_Append(output, value, size)) {
         return FALSE;
      }
   }

   /*
    * Win32 takes care of adding the \r (XXX this assumes that the stream
    * is opened in ascii mode) --hpreg
    */
   if (!DynBuf_Append(output, "\n", 1)) {
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DictLL_WriteLine --
 *
 *    Marshals a line, writing the data to a file.  If 'name' is NULL, '*value'
 *    contains the whole line to write verbatim.  Otherwise a proper name/value
 *    pair is written.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure: errno is set accordingly
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DictLL_WriteLine(FILE *stream,      // IN: stream to write
                 char const *name,  // IN: name to write
                 char const *value) // IN: value to write
{
   DynBuf buf;

   DynBuf_Init(&buf);
   if (!DictLL_MarshalLine(&buf, name, value)) {
      DynBuf_Destroy(&buf);
      errno = ENOMEM;
      return FALSE;
   }
   if (fwrite(DynBuf_Get(&buf), DynBuf_GetSize(&buf), 1, stream) != 1) {
      DynBuf_Destroy(&buf);
      return FALSE;
   }
   DynBuf_Destroy(&buf);
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DictLL_ReadUTF8BOM --
 *
 *    Reads a UTF-8 BOM from a file.
 *
 * Results:
 *    If successful, returns TRUE and updates the file position.
 *    Returns FALSE if a UTF-8 BOM was not found.
 *
 * Side effects:
 *    Might clears the error indicator of the file stream.
 *
 *-----------------------------------------------------------------------------
 */

Bool
DictLL_ReadUTF8BOM(FILE *file) // IN/OUT
{
   Bool found;

   // sizeof on a string literal counts NUL.  Exclude it.
   char buf[sizeof UTF8_BOM - 1] = { 0 };

   if (file == stdin) {
      return FALSE;
   }

   found =    fread(buf, sizeof buf, 1, file) == 1
           && memcmp(UTF8_BOM, buf, sizeof buf) == 0;

   if (!found) {
      rewind(file);
   }

   return found;
}
  0707010000003B000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/lib/dynxdr 0707010000003C000081A40000000000000000000000016822550500000476000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/dynxdr/Makefile.am ################################################################################
### Copyright (C) 2008-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libDynxdr.la

libDynxdr_la_SOURCES =
libDynxdr_la_SOURCES += dynxdr.c
libDynxdr_la_SOURCES += xdrutil.c

libDynxdr_la_CPPFLAGS =
libDynxdr_la_CPPFLAGS += @XDR_CPPFLAGS@

libDynxdr_la_LIBADD =
libDynxdr_la_LIBADD += @XDR_LIBS@

  0707010000003D000081A40000000000000000000000016822550500002DD3000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/dynxdr/dynxdr.c    /*********************************************************
 * Copyright (C) 2008-2018, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include <stdlib.h>
#if defined(_WIN32)
#  include <winsock2.h>
#else
#  include <sys/param.h>
#  include <sys/types.h>
#  include <netinet/in.h>
#  include <arpa/inet.h>
#endif

#include "dynxdr.h"
#include "dynbuf.h"
#include "util.h"

/*
 * dynxdr.c --
 *
 *    Implements an XDR stream backed by a DynBuf.
 */


typedef struct DynXdrData {
   DynBuf   data;
   Bool     freeMe;
} DynXdrData;

/*
 * Solaris does not declare some parameters as "const".
 */
#if defined(sun)
#  define DYNXDR_CONST
#else
#  define DYNXDR_CONST const
#endif

/*
 * Mac OS X, FreeBSD and Solaris don't take a const parameter to the
 * "x_getpostn" function.
 */
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(sun) || defined(USE_TIRPC)
#  define DYNXDR_GETPOS_CONST
#else
#  define DYNXDR_GETPOS_CONST const
#endif

/*
 * Solaris defines the parameter to "x_putbytes" as an int.
 */
#if defined(sun)
#  define DYNXDR_SIZE_T int
#else
#  define DYNXDR_SIZE_T u_int
#endif

/*
 * Mac OS 64-bit defines the parameter to "x_putlong" as an int.
 */
#if defined __APPLE__ && defined __LP64__
#  define DYNXDR_LONG int
#else
#  define DYNXDR_LONG long
#endif

#if defined(sun)
#   define DYNXDR_INLINE_T rpc_inline_t
#   define DYNXDR_INLINE_LEN_T int
#else
#   define DYNXDR_INLINE_T int32_t
#   define DYNXDR_INLINE_LEN_T u_int
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdrPutBytes --
 *
 *    Writes a byte array into the XDR stream.
 *
 * Results:
 *    TRUE: all ok
 *    FALSE: failed to add data do dynbuf.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static bool_t
DynXdrPutBytes(XDR *xdrs,                 // IN/OUT
               DYNXDR_CONST char *data,   // IN
               DYNXDR_SIZE_T len)         // IN
{
   DynXdrData *priv = (DynXdrData *) xdrs->x_private;
   return DynBuf_Append(&priv->data, data, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdrGetPos --
 *
 *    Returns the current position of the buffer, which is the same as
 *    the current buffer size.
 *
 *    XXX: The Mac OS X headers don't expect a "const" argument.
 *
 * Results:
 *    The size of the underlying buffer.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static u_int
DynXdrGetPos(DYNXDR_GETPOS_CONST XDR *xdrs) // IN
{
   DynXdrData *priv = (DynXdrData *) xdrs->x_private;
   return (u_int) DynBuf_GetSize(&priv->data);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdrSetPos --
 *
 *    Sets the position of the XDR stream. The current data in the buffer is
 *    not affected, just the pointer to the current position.
 *
 * Results:
 *    TRUE if pos is within the bounds of the backing buffer.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static bool_t
DynXdrSetPos(XDR *xdrs, // IN
             u_int pos) // IN
{
   DynXdrData *priv = (DynXdrData *) xdrs->x_private;
   if (pos <= DynBuf_GetAllocatedSize(&priv->data)) {
      DynBuf_SetSize(&priv->data, (size_t) pos);
      return TRUE;
   }
   return FALSE;
}


#if !defined(USE_TIRPC) &&    \
     defined(__GLIBC__) ||    \
     (defined(sun) &&         \
        (defined(_LP64) ||    \
           defined(_KERNEL)))
/*
 *-----------------------------------------------------------------------------
 *
 * DynXdrPutInt32 --
 *
 *    Writes a 32-bit int to the XDR stream.
 *
 *    XXX: This seems to be a glibc-only extenstion. It's present since at
 *    least glibc 2.1, according to their CVS.
 *
 *    XXX: Investigate this further.  This XDR operation exists in Solaris
 *    since at least Solaris 9.
 *
 * Results:
 *    TRUE: all ok
 *    FALSE: failed to add data do dynbuf.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static bool_t
DynXdrPutInt32(XDR *xdrs,                       // IN/OUT
               DYNXDR_CONST int32_t *ip)        // IN
{
   int32_t out = htonl(*ip);
   DynXdrData *priv = (DynXdrData *) xdrs->x_private;
   return DynBuf_Append(&priv->data, &out, sizeof out);
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdrPutLong --
 *
 *    Writes a 32-bit int to the XDR stream.
 *
 * Results:
 *    TRUE: all ok
 *    FALSE: failed to add data do dynbuf.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static bool_t
DynXdrPutLong(XDR *xdrs,                    // IN/OUT
              DYNXDR_CONST DYNXDR_LONG *lp) // IN
{
   int32 out;
   DynXdrData *priv = (DynXdrData *) xdrs->x_private;

#ifdef __APPLE__
   ASSERT_ON_COMPILE(sizeof *lp <= sizeof (int32));
#endif
   out = htonl((int32)*lp);
   return DynBuf_Append(&priv->data, &out, sizeof out);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdrInline --
 *
 *    Return a pointer to a contiguous buffer of len bytes.  On XDR_ENCODE,
 *    is used to preallocate chunks of the backing buffer such that the caller
 *    may set bulk 4-byte members w/o reallocating each time.
 *
 * Results:
 *    Valid pointer on success, NULL on failure.
 *
 * Side effects:
 *    Backing DynBuf may be enlarged.
 *
 *-----------------------------------------------------------------------------
 */

static DYNXDR_INLINE_T *
DynXdrInline(XDR *xdrs,                 // IN/OUT
             DYNXDR_INLINE_LEN_T len)   // IN
{
   DynXdrData *priv = (DynXdrData *)xdrs->x_private;
   DynBuf *buf = &priv->data;
   DYNXDR_INLINE_T *retAddr;

   ASSERT(len >= 0);
   ASSERT(xdrs->x_op == XDR_ENCODE);

   if (len == 0) {
      return (DYNXDR_INLINE_T *)&buf->data[buf->size];
   }

   if (buf->allocated - buf->size < len) {
      /* DynBuf too small.  Grow it. */
      if (!DynBuf_Enlarge(buf, buf->size + len)) {
         return NULL;
      }
   }

   retAddr = (DYNXDR_INLINE_T *)&buf->data[buf->size];
   buf->size += len;

   return retAddr;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdr_Create --
 *
 *    Creates a new XDR struct backed by a DynBuf. The XDR stream is created
 *    in XDR_ENCODE mode. The "in" argument is optional - if NULL, a new XDR
 *    structure will be allocated.
 *
 * Results:
 *    The XDR struct, or NULL on failure.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

XDR *
DynXdr_Create(XDR *in)  // IN
{
   static struct xdr_ops dynXdrOps = {
      /*
       * Yes, these macros are a little redundant, but I figure it helps with
       * readability to group the sun/_KERNEL bits together.
       */
#if !defined(sun) || (defined(sun) && !defined(_KERNEL))
      NULL,             /* x_getlong */
      DynXdrPutLong,    /* x_putlong */
#endif
      NULL,             /* x_getbytes */
      DynXdrPutBytes,   /* x_putbytes */
      DynXdrGetPos,     /* x_getpostn */
      DynXdrSetPos,     /* x_setpostn */
      DynXdrInline,     /* x_inline */
      NULL,             /* x_destroy */
#if defined(__APPLE__) || defined(USE_TIRPC)
      NULL,             /* x_control */
#elif defined(__GLIBC__)
      NULL,             /* x_getint32 */
      DynXdrPutInt32,   /* x_putint32 */
#elif defined(sun) && (defined(_LP64) || defined(_KERNEL))
      NULL,             /* x_control */
      NULL,             /* x_getint32 */
      DynXdrPutInt32,   /* x_putint32 */
#endif
   };

   XDR *ret;
   DynXdrData *priv;

   if (in == NULL) {
      ret = malloc(sizeof *ret);
      if (ret == NULL) {
         goto error;
      }
   } else {
      ret = in;
   }

   priv = malloc(sizeof *priv);
   if (priv == NULL) {
      goto error;
   }

   priv->freeMe = (in == NULL);
   DynBuf_Init(&priv->data);

   ret->x_op = XDR_ENCODE;
   ret->x_public = NULL;
   ret->x_private = (char *) priv;
   ret->x_base = 0;
   ret->x_ops = &dynXdrOps;

   return ret;

error:
   if (in == NULL) {
      free(ret);
   }
   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdr_AppendRaw --
 *
 *    Appends some raw bytes to the XDR stream's internal dynbuf. This is
 *    useful when non-XDR data may need to be added to the buffer, avoiding
 *    the need to create another buffer and copying the existing data.
 *
 * Results:
 *    Whether appending the data was successful.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */


Bool
DynXdr_AppendRaw(XDR *xdrs,         // IN
                 const void *buf,   // IN
                 size_t len)        // IN
{
   DynBuf *intbuf = &((DynXdrData *) xdrs->x_private)->data;
   return DynBuf_Append(intbuf, buf, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdr_GetData --
 *
 *    Returns a copy of the current data in the XDR buffer. Caller is
 *    responsible for freeing the data.
 *
 * Results:
 *    The current data in the buffer, or NULL if there's no data, or failed
 *    to allocate data.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void *
DynXdr_AllocGet(XDR *xdrs) // IN
{
   DynBuf *buf = &((DynXdrData *) xdrs->x_private)->data;
   return Util_Memdup(DynBuf_Get(buf), DynBuf_GetSize(buf));
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdr_Get --
 *
 *    Returns the current data in the XDR buffer.
 *
 * Results:
 *    The current data in the buffer, or NULL if there's no data.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void *
DynXdr_Get(XDR *xdrs)  // IN
{
   DynBuf *buf = &((DynXdrData *) xdrs->x_private)->data;
   return DynBuf_Get(buf);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynXdr_Destroy --
 *
 *    Frees data in the XDR stream, optionally destroying the underlying
 *    DynBuf (if "release" is TRUE). If the XDR stream was dynamically
 *    allocated by DynXdr_Create(), it will be freed.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
DynXdr_Destroy(XDR *xdrs,     // IN
               Bool release)  // IN
{
   if (xdrs) {
      DynXdrData *priv = (DynXdrData *) xdrs->x_private;
      if (release) {
         DynBuf_Destroy(&priv->data);
      }
      if (priv->freeMe) {
         free(xdrs);
      }
      free(priv);
   }
}

 0707010000003E000081A40000000000000000000000016822550500000BE6000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/dynxdr/xdrutil.c   /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * xdrutil.c --
 *
 *    Utility functions for code that uses XDR to encode/decode data.
 */

#include <stdlib.h>
#include <string.h>
#include "vm_assert.h"
#include "vmxrpc.h"
#include "xdrutil.h"


/*
 *-----------------------------------------------------------------------------
 *
 * XdrUtil_ArrayAppend --
 *
 *    Appends 'cnt' new elements of size 'sz' at the end of the given array.
 *    If successful, len will contain the count of elements in the new
 *    array.
 *
 *    The newly allocated memory is zeroed out.
 *
 * Results:
 *    NULL on allocation failure, pointer to the first new element otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void *
XdrUtil_ArrayAppend(void **array,      // IN/OUT
                    u_int *arrayLen,   // IN/OUT
                    size_t elemSz,     // IN
                    u_int elemCnt)     // IN
{
   void *ret = NULL;
   void *newarray;

   newarray = realloc(*array, (*arrayLen + elemCnt) * elemSz);
   if (newarray != NULL) {
      ret = &((char *)newarray)[*arrayLen * elemSz];
      memset(ret, 0, elemSz * elemCnt);
      *array = newarray;
      *arrayLen = *arrayLen + elemCnt;
   }

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * XdrUtil_Deserialize --
 *
 *    Deserializes the given data into the provided destination, using the
 *    given XDR function.
 *
 * Results:
 *    Whether deserialization was successful.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
XdrUtil_Deserialize(const void *data,  // IN
                    size_t dataLen,    // IN
                    void *xdrProc,     // IN
                    void *dest)        // IN
{
   Bool ret;
   xdrproc_t proc = xdrProc;
   XDR xdrs;

   ASSERT(data != NULL);
   ASSERT(xdrProc != NULL);
   ASSERT(dest != NULL);

   xdrmem_create(&xdrs, (char *) data, dataLen, XDR_DECODE);
   ret = (Bool) proc(&xdrs, dest, 0);
   xdr_destroy(&xdrs);

   if (!ret) {
      VMX_XDR_FREE(proc, dest);
   }

   return ret;
}

  0707010000003F000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/lib/err    07070100000040000081A400000000000000000000000168225505000003ED000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/err/Makefile.am    ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libErr.la

libErr_la_SOURCES =
libErr_la_SOURCES += err.c
libErr_la_SOURCES += errPosix.c
   07070100000041000081A40000000000000000000000016822550500002201000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/lib/err/err.c  /*********************************************************
 * Copyright (C) 1998-2016,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * err.c --
 *
 *      General error handling library
 *
 */

#include "vmware.h"
#include "errInt.h"
#include "str.h"
#include "vm_atomic.h"
#include "hashTable.h"
#include "util.h"
#include "codeset.h"


/*
 * Constants
 */

#define HASHTABLE_SIZE 2048


/*
 * Types
 */

typedef struct ErrInfo {
   Err_Number number;
   char *string;
} ErrInfo;


/*
 * Variables
 */

/*
 * We statically link lib/err in several libraries. This means that a
 * single binary may have several copies of lib/err. These pointers are
 * not static so that we have one copy across the entire binary.
 */
Atomic_Ptr errNumTable;
Atomic_Ptr errPtrTable;
#if defined VMX86_DEBUG && defined __linux__
Atomic_Ptr errStrTable;
#endif

#define NUMTABLE() HashTable_AllocOnce(&errNumTable, HASHTABLE_SIZE, \
                                       HASH_INT_KEY | HASH_FLAG_ATOMIC, \
                                       ErrFreeErrInfo)
#define PTRTABLE() HashTable_AllocOnce(&errPtrTable, HASHTABLE_SIZE, \
                                       HASH_INT_KEY | HASH_FLAG_ATOMIC, NULL)
#if defined VMX86_DEBUG && defined __linux__
#define STRTABLE() HashTable_AllocOnce(&errStrTable, HASHTABLE_SIZE, \
                                       HASH_STRING_KEY | HASH_FLAG_ATOMIC, \
                                       NULL)
#endif


/*
 *----------------------------------------------------------------------
 *
 * ErrFreeErrInfo --
 *
 *      HashTableFreeEntryFn helper function to free ErrInfo struct.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
ErrFreeErrInfo(void *pErrInfo) // IN
{
   ErrInfo *errInfo = pErrInfo;
   if (errInfo) {
      free(errInfo->string);
      free(errInfo);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * Err_ErrString --
 *
 *      Returns a string that corresponds to the last error message.
 *      The error number used is that which is native to the platform,
 *      errno on POSIXen and GetLastError on Windows.
 *
 * Results:
 *      Error message string.
 *
 * Side effects:
 *      None.
 *	Current error number is preserved.
 *
 *----------------------------------------------------------------------
 */

const char *
Err_ErrString(void)
{
   return Err_Errno2String(Err_Errno());
}


/*
 *----------------------------------------------------------------------
 *
 * Err_Errno2String --
 *
 *      Return a string that corresponds to the passed error number.
 *      The error number used is that which is native to the platform,
 *      errno on POSIXen and GetLastError on Windows.
 *
 *	The string is in English in UTF-8, has indefinite lifetime,
 *	and need not be freed.
 *
 * Results:
 *      Error message string in UTF-8.
 *
 * Side effects:
 *      None.
 *	Current error number is preserved.
 *
 *----------------------------------------------------------------------
 */

const char *
Err_Errno2String(Err_Number errorNumber) // IN
{
   HashTable *numTable;
   HashTable *ptrTable;
   ErrInfo *info;
   ErrInfo *oldInfo;
   Err_Number oldErrno = Err_Errno();

   ASSERT(errorNumber != ERR_INVALID);

   /*
    * Look up the error in numTable.
    * Or insert it if it's not there.
    */

   numTable = NUMTABLE();
   if (!HashTable_Lookup(numTable, (void *) (uintptr_t) errorNumber,
			 (void **) &info)) {
      char buf[2048];
      const char *p;
      size_t n;

      /*
       * Convert number to string and build the info structure.
       */

      p = ErrErrno2String(errorNumber, buf, sizeof buf);

      info = Util_SafeMalloc(sizeof *info);
      info->number = errorNumber;
      info->string = Util_SafeStrdup(p);

      /*
       * To be safe, make sure the end of the string is at
       * a UTF-8 boundary, but we can only do this when the
       * string is in our buffer (it may not be).
       */

      n = strlen(info->string);
      n = CodeSet_Utf8FindCodePointBoundary(info->string, n);
      info->string[n] = '\0';

      /*
       * Try to insert new info into numTable.
       * If that fails, then we must have lost out to someone else.
       * Use theirs in that case.
       */

      oldInfo = HashTable_LookupOrInsert(numTable,
				         (void *) (uintptr_t) errorNumber,
				         info);
      if (oldInfo != info) {
	 ASSERT(oldInfo->number == info->number);
	 ASSERT(Str_Strcmp(oldInfo->string, info->string) == 0);
	 free(info->string);
	 free(info);
	 info = oldInfo;
      }
   }

   /*
    * Try to insert info into ptrTable.
    * We need to do it even if we didn't create this entry,
    * because we may get here before the other guy (who created
    * the entry and inserted it into numTable).
    */

   ptrTable = PTRTABLE();
   oldInfo = HashTable_LookupOrInsert(ptrTable, info->string, info);
   ASSERT(oldInfo == info);

#if defined VMX86_DEBUG && defined __linux__
   {
      HashTable *strTable = STRTABLE();
      ErrInfo *i = HashTable_LookupOrInsert(strTable, info->string, info);
      ASSERT(i == info);
   }
#endif

   Err_SetErrno(oldErrno);
   return info->string;
}


/*
 *----------------------------------------------------------------------
 *
 * Err_String2Errno --
 *
 *      Return an error number that corresponds to the passed string.
 *      The error number used is that which is native to the platform,
 *      errno on POSIXen and GetLastError on Windows.
 *
 *	To be recognized, the string must be one previously returned
 *	by Err_Errno2String.  Any other string (even a copy of
 *	a valid error string) returns ERR_INVALID.
 *
 * Results:
 *      Error number or ERR_INVALID.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Err_Number
Err_String2Errno(const char *string) // IN
{
   HashTable *ptrTable = PTRTABLE();
   ErrInfo *info;

   if (!HashTable_Lookup(ptrTable, string, (void **) &info)) {
      return ERR_INVALID;
   }

   ASSERT(info->string == string);
   ASSERT(info->number != ERR_INVALID);
   return info->number;
}


/*
 *----------------------------------------------------------------------
 *
 * Err_Exit --
 *
 *      Reclaim memory.  Useful for avoiding leaks at exit being
 *      reported by valgrind / Memory Validator.
 *
 *      Assumes that no other threads are calling into bora/lib/err.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Err_Exit(void) // IN
{
   HashTable *numTable = NUMTABLE();
   HashTable *ptrTable = PTRTABLE();
#if defined VMX86_DEBUG && defined __linux__
   HashTable *strTable = STRTABLE();

   HashTable_FreeUnsafe(strTable);
#endif
   HashTable_FreeUnsafe(ptrTable);
   HashTable_FreeUnsafe(numTable);
}


#ifdef VMX86_DEBUG
/*
 *----------------------------------------------------------------------
 *
 * Err_String2ErrnoDebug --
 *
 *      Return an error number that corresponds to the passed string.
 *
 *	This is the debug version that uses the whole string as key,
 *	instead of just the address.
 *
 * Results:
 *      Error number or ERR_INVALID.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Err_Number
Err_String2ErrnoDebug(const char *string) // IN
{
#ifdef __linux__
   HashTable *strTable = STRTABLE();
   ErrInfo *info;

   if (!HashTable_Lookup(strTable, string, (void **) &info)) {
      return ERR_INVALID;
   }

   ASSERT(Str_Strcmp(info->string, string) == 0);
   ASSERT(info->number != ERR_INVALID);
   if (info->string != string) {
      Log("%s: errno %d, string \"%s\" at %p, originally at %p.\n",
	  __FUNCTION__, info->number, string, string, info->string);
   }
   return info->number;
#else
   return ERR_INVALID;
#endif
}
#endif
   07070100000042000081A400000000000000000000000168225505000004CF000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/err/errInt.h   /*********************************************************
 * Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * errInt.h --
 *
 *	Internal definitions for the Err module.
 */

#ifndef _ERRINT_H_
#define _ERRINT_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "err.h"

const char *ErrErrno2String(Err_Number errorNumber,
                            char *buf,
                            size_t bufSize);

#endif // ifndef _ERRINT_H_
 07070100000043000081A40000000000000000000000016822550500000797000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/err/errPosix.c /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * errPosix.c --
 *
 *      Posix error handling library
 *
 */

#if defined __linux__
/* Force GNU strerror_r prototype instead of Posix prototype */
#  define _GNU_SOURCE
#endif

#include <errno.h>
#include <string.h>
#include <locale.h>

#include "vmware.h"
#include "errInt.h"
#include "util.h"
#include "str.h"


/*
 *----------------------------------------------------------------------
 *
 * ErrErrno2String --
 *
 *      Convert an error number to a string in English.
 *	The returned string may use the supplied buffer or may be
 *	a static string.
 *
 * Results:
 *      The error string.
 *      
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

const char *
ErrErrno2String(Err_Number errorNumber, // IN
                char *buf,		// OUT: return buffer
		size_t bufSize)		// IN: size of buffer
{
   char *p;

#if defined(__linux__) && !defined(__ANDROID__)
   p = strerror_r(errorNumber, buf, bufSize);
#else
   p = strerror(errorNumber);
#endif
   ASSERT(p != NULL);
   return p;
}
 07070100000044000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/file   07070100000045000081A400000000000000000000000168225505000004F5000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/file/Makefile.am   ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libFile.la

libFile_la_SOURCES = 
libFile_la_SOURCES += file.c
libFile_la_SOURCES += fileStandAlone.c
libFile_la_SOURCES += filePosix.c
libFile_la_SOURCES += fileIO.c
libFile_la_SOURCES += fileIOPosix.c
libFile_la_SOURCES += fileLockPrimitive.c
libFile_la_SOURCES += fileLockPosix.c
libFile_la_SOURCES += fileTempPosix.c
libFile_la_SOURCES += fileTemp.c
   07070100000046000081A40000000000000000000000016822550500011922000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/file/file.c    /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * file.c --
 *
 *        Interface to host file system.  See also filePosix.c,
 *        fileWin32.c, etc.
 *
 *        If a function can be implemented such that it has no dependencies
 *        outside of lib/misc, place the function in fileStandAlone.c, NOT
 *        here.
 */

#if defined(_WIN32)
#include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#if defined(_WIN32)
#include <io.h>
#define S_IXUSR    0100
#define S_IWUSR    0200
#else
#include <unistd.h>
#endif
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#include "vmware.h"
#include "util.h"
#include "str.h"
#include "msg.h"
#include "log.h"
#include "random.h"
#include "uuid.h"
#include "config.h"
#include "posix.h"
#include "file.h"
#include "fileIO.h"
#include "util.h"
#include "fileInt.h"
#include "dynbuf.h"
#include "base64.h"
#include "timeutil.h"
#include "hostinfo.h"
#include "hostType.h"
#include "vm_atomic.h"
#include "vm_basic_asm.h"
#include "fileLock.h"
#include "userlock.h"
#include "strutil.h"

#include "unicodeOperations.h"


/*
 *----------------------------------------------------------------------
 *
 * File_Exists --
 *
 *      Check if a file is accessible with the process' real user ID
 *
 *      XXX - This function invokes access(), which uses the real uid,
 *      not the effective uid, so it probably does not do what you
 *      expect.  Instead it should use Posix_EuidAccess(), which
 *      uses the effective uid, but it's too risky to fix right now.
 *      See PR 459242.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *      TRUE    file is accessible with the process' real uid
 *      FALSE   file doesn't exist or an error occured
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_Exists(const char *pathName)  // IN: May be NULL.
{
   return FileIO_IsSuccess(FileIO_Access(pathName, FILEIO_ACCESS_EXISTS));
}


/*
 *----------------------------------------------------------------------
 *
 * File_UnlinkIfExists --
 *
 *      If the given file exists, unlink it.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *        0  success
 *      > 0  failure (errno)
 *
 * Side effects:
 *      May unlink the file.
 *
 *----------------------------------------------------------------------
 */

int
File_UnlinkIfExists(const char *pathName)  // IN:
{
   errno = FileDeletion(pathName, TRUE);

   if (errno == ENOENT) {
      errno = 0;
   }

   return errno;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_SupportsMandatoryLock --
 *
 *      Determines if the underlying filesystem for a particular location
 *      can support mandatory locking. Mandatory locking is used within
 *      FileLock to make the advisory FileLock self-cleaning in the event
 *      of host failure.
 *
 * Results:
 *      TRUE if FILEIO_OPEN_EXCLUSIVE_LOCK will work, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_SupportsMandatoryLock(const char *pathName) // IN: file to be locked
{
   /*
    * For now, "know" that all ESX filesystems support mandatory locks
    * and no non-ESX filesystems support mandatory locks.
    */
   return HostType_OSIsVMK();
}


/*
 *----------------------------------------------------------------------
 *
 * File_IsDirectory --
 *
 *      Check if specified file is a directory or not.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *      TRUE    is a directory
 *      FALSE   is not a directory or an error occured
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_IsDirectory(const char *pathName)  // IN:
{
   FileData fileData;

   return (FileAttributes(pathName, &fileData) == 0) &&
           (fileData.fileType == FILE_TYPE_DIRECTORY);
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_GetFilePermissions --
 *
 *      Return the read / write / execute permissions of a file.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *      TRUE if success, FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_GetFilePermissions(const char *pathName,  // IN:
                        int *mode)             // OUT: file mode
{
   FileData fileData;

   ASSERT(mode != NULL);

   if (FileAttributes(pathName, &fileData) != 0) {
      return FALSE;
   }

   *mode = fileData.fileMode;

#if defined(_WIN32)
      /*
       * On Win32 implementation of FileAttributes does not return execution
       * bit.
       */

      if (FileIO_Access(pathName, FILEIO_ACCESS_EXEC) == FILEIO_SUCCESS) {
         *mode |= S_IXUSR;
      }
#endif

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * File_Unlink --
 *
 *      Unlink the file.
 *
 *      POSIX: If name is a symbolic link, then unlink the the file the link
 *      refers to as well as the link itself.  Only one level of links are
 *      followed.
 *      WINDOWS: No symbolic links so no link following.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *        0  success
 *      > 0  failure (errno)
 *
 * Side effects:
 *      The file is removed.
 *
 *----------------------------------------------------------------------
 */

int
File_Unlink(const char *pathName)  // IN:
{
   errno = FileDeletion(pathName, TRUE);

   return errno;
}


/*
 *----------------------------------------------------------------------
 *
 * File_UnlinkNoFollow --
 *
 *      Unlink the file (do not follow symbolic links).
 *      On Windows, there are no symbolic links so this is the same as
 *      File_Unlink
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *        0  success
 *      > 0  failure (errno)
 *
 * Side effects:
 *      The file is removed.
 *
 *----------------------------------------------------------------------
 */

int
File_UnlinkNoFollow(const char *pathName)  // IN:
{
   errno = FileDeletion(pathName, FALSE);

   return errno;
}


/*
 *----------------------------------------------------------------------
 *
 * File_UnlinkRetry --
 *
 *      Unlink the file, retrying on EBUSY on ESX, up to given timeout.
 *
 * Results:
 *        0  success
 *      > 0  failure (errno)
 *
 * Side effects:
 *      The file is removed.
 *
 *----------------------------------------------------------------------
 */

int
File_UnlinkRetry(const char *pathName,       // IN:
                 uint32 maxWaitTimeMilliSec) // IN:
{
   if (vmx86_server) {
      uint32 const unlinkWait = 300;
      uint32 waitMilliSec = 0;

      do {
         errno = FileDeletion(pathName, TRUE);

         if (errno != EBUSY || waitMilliSec >= maxWaitTimeMilliSec) {
            break;
         }

         Log(LGPFX" %s: %s after %u ms\n", __FUNCTION__, pathName, unlinkWait);

         Util_Usleep(unlinkWait * 1000);
         waitMilliSec += unlinkWait;
      } while (TRUE);
   } else {
      errno = FileDeletion(pathName, TRUE);
   }

   return errno;
}


/*
 *----------------------------------------------------------------------
 *
 * File_CreateDirectoryEx --
 *
 *      Creates the specified directory with the specified permissions.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *      TRUE   Directory was created
 *      FALSE  Directory creation failed.
 *             See File_EnsureDirectoryEx for dealing with directories that
 *             may exist.
 *
 * Side effects:
 *      Creates the directory on disk.
 *
 *----------------------------------------------------------------------
 */

Bool
File_CreateDirectoryEx(const char *pathName,  // IN:
                       int mode)              // IN:
{
   int err = FileCreateDirectory(pathName, mode);

   return err == 0;
}


/*
 *----------------------------------------------------------------------
 *
 * File_CreateDirectory --
 *
 *      Creates the specified directory with 0777 permissions.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *      TRUE   Directory was created
 *      FALSE  Directory creation failed.
 *             See File_EnsureDirectory for dealing with directories that
 *             may exist.
 *
 * Side effects:
 *      Creates the directory on disk.
 *
 *----------------------------------------------------------------------
 */

Bool
File_CreateDirectory(const char *pathName)  // IN:
{
   int err = FileCreateDirectory(pathName, 0777);

   return err == 0;
}


/*
 *----------------------------------------------------------------------
 *
 * File_EnsureDirectoryEx --
 *
 *      If the directory doesn't exist, creates it. If the directory
 *      already exists, do nothing and succeed.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *      See above.
 *
 * Side effects:
 *      May create a directory on disk.
 *
 *----------------------------------------------------------------------
 */

Bool
File_EnsureDirectoryEx(const char *pathName,  // IN:
                       int mode)              // IN:
{
   int err = FileCreateDirectory(pathName, mode);

   if (err == EEXIST) {
      FileData fileData;

      err = FileAttributes(pathName, &fileData);

      if (err == 0) {
         if (fileData.fileType != FILE_TYPE_DIRECTORY) {
            err = ENOTDIR;
            errno = ENOTDIR;

#if defined(_WIN32)
            SetLastError(ERROR_DIRECTORY);
#endif
         }
      }
   }

   return err == 0;
}


/*
 *----------------------------------------------------------------------
 *
 * File_EnsureDirectory --
 *
 *      If the directory doesn't exist, creates it. If the directory
 *      already exists, do nothing and succeed.
 *
 *      Errno/GetLastError is available upon failure.
 *
 * Results:
 *      See above.
 *
 * Side effects:
 *      May create a directory on disk.
 *
 *----------------------------------------------------------------------
 */

Bool
File_EnsureDirectory(const char *pathName)  // IN:
{
   return File_EnsureDirectoryEx(pathName, 0777);
}


/*
 *----------------------------------------------------------------------
 *
 * File_DeleteEmptyDirectory --
 *
 *      Deletes the specified directory if it is empty.
 *
 * Results:
 *      True if the directory is successfully deleted, false otherwise.
 *
 * Side effects:
 *      Deletes the directory from disk.
 *
 *----------------------------------------------------------------------
 */

Bool
File_DeleteEmptyDirectory(const char *pathName)  // IN:
{
   Bool returnValue = TRUE;

   if (FileRemoveDirectory(pathName) != 0) {
#if defined(_WIN32)
      /*
       * Directory may have read-only bit set. Unset the
       * read-only bit and try deleting one more time.
       */
      if (File_SetFilePermissions(pathName, S_IWUSR)) {
         if (FileRemoveDirectory(pathName) != 0) {
            returnValue = FALSE;
         }
      } else {
         returnValue = FALSE;
      }
#else
      returnValue =  FALSE;
#endif
   }

   return returnValue;
}


/*
 *----------------------------------------------------------------------
 *
 * GetOldMachineID --
 *
 *      Return the old machineID, the one based on Hostinfo_MachineID.
 *
 * Results:
 *      The machineID is returned. It should not be freed.
 *
 * Side effects:
 *      Memory allocated for the machineID is never freed, however the
 *      memory is cached - there is no memory leak.
 *
 *----------------------------------------------------------------------
 */

static const char *
GetOldMachineID(void)
{
   static Atomic_Ptr atomic; /* Implicitly initialized to NULL. --mbellon */
   const char *machineID;

   machineID = Atomic_ReadPtr(&atomic);

   if (machineID == NULL) {
      char *p;
      uint32 hashValue;
      uint64 hardwareID;
      char encodedMachineID[16 + 1];
      char rawMachineID[sizeof hashValue + sizeof hardwareID];

      Hostinfo_MachineID(&hashValue, &hardwareID);

      /* Build the raw machineID */
      memcpy(rawMachineID, &hashValue, sizeof hashValue);
      memcpy(&rawMachineID[sizeof hashValue], &hardwareID,
             sizeof hardwareID);

      /* Base 64 encode the binary data to obtain printable characters */
      /* coverity[check_return] */
      Base64_Encode(rawMachineID, sizeof rawMachineID, encodedMachineID,
                    sizeof encodedMachineID, NULL);

      /* remove '/' from the encoding; no problem using it for a file name */
      for (p = encodedMachineID; *p; p++) {
         if (*p == '/') {
            *p = '-';
         }
      }

      p = Util_SafeStrdup(encodedMachineID);

      if (Atomic_ReadIfEqualWritePtr(&atomic, NULL, p)) {
         Posix_Free(p);
      }

      machineID = Atomic_ReadPtr(&atomic);
      ASSERT(machineID != NULL);
   }

   return machineID;
}


/*
 *----------------------------------------------------------------------
 *
 * FileLockGetMachineID --
 *
 *      Return the machineID, a "universally unique" identification of
 *      of the system that calls this routine.
 *
 *      An attempt is first made to use the host machine's UUID. If that
 *      fails drop back to the older machineID method.
 *
 * Results:
 *      The machineID is returned. It should not be freed.
 *
 * Side effects:
 *      Memory allocated for the machineID is never freed, however the
 *      memory is cached - there is no memory leak.
 *
 *----------------------------------------------------------------------
 */

const char *
FileLockGetMachineID(void)
{
   static Atomic_Ptr atomic; /* Implicitly initialized to NULL. --mbellon */
   const char *machineID;

   machineID = Atomic_ReadPtr(&atomic);

   if (machineID == NULL) {
      char *p;
      char *q;

      /*
       * UUID_GetHostRealUUID is fine on Windows.
       *
       * UUID_GetHostUUID is fine on Macs because the UUID can't be found
       * in /dev/mem even if it can be accessed. Macs always use the MAC
       * address from en0 to provide a UUID.
       *
       * UUID_GetHostUUID is problematic on Linux so it is not acceptable for
       * locking purposes - it accesses /dev/mem to obtain the SMBIOS UUID
       * and that can fail when the calling process is not priviledged.
       *
       */

#if defined(_WIN32)
      q = UUID_GetRealHostUUID();
#elif defined(__APPLE__) || defined(VMX86_SERVER)
      q = UUID_GetHostUUID();
#else
      q = NULL;
#endif

      if (q == NULL) {
         p = Util_SafeStrdup(GetOldMachineID());
      } else {

         /*
          * Coverity flags this as dead code on Non-Windows, non-Apple
          * Platforms, since q will be NULL and this code not reached.
          */
         /* coverity[dead_error_begin] */
         p = Str_SafeAsprintf(NULL, "uuid=%s", q);
         Posix_Free(q);

         /* Surpress any whitespace. */
         for (q = p; *q; q++) {
            if (isspace((int) *q)) {
               *q = '-';
            }
         }
      }

      if (Atomic_ReadIfEqualWritePtr(&atomic, NULL, p)) {
         Posix_Free(p);
      }

      machineID = Atomic_ReadPtr(&atomic);
      ASSERT(machineID != NULL);
   }

   return machineID;
}


/*
 *-----------------------------------------------------------------------------
 *
 * OldMachineIDMatch --
 *
 *      Do the old-style MachineIDs match?
 *
 * Results:
 *      TRUE     Yes
 *      FALSE    No
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
OldMachineIDMatch(const char *first,   // IN:
                  const char *second)  // IN:
{
#if defined(__APPLE__) || defined(__linux__)
   /* Ignore the host name hash */
   char *p;
   char *q;
   size_t len;
   Bool result;
   uint8 rawMachineID_1[12];
   uint8 rawMachineID_2[12];

   for (p = Util_SafeStrdup(first), q = p; *p; p++) {
      if (*p == '-') {
         *p = '/';
      }
   }
   result = Base64_Decode(q, rawMachineID_1, sizeof rawMachineID_1, &len);
   Posix_Free(q);

   if ((result == FALSE) || (len != 12)) {
      Warning("%s: unexpected decode problem #1 (%s)\n", __FUNCTION__,
              first);

      return FALSE;
   }

   for (p = Util_SafeStrdup(second), q = p; *p; p++) {
      if (*p == '-') {
         *p = '/';
      }
   }
   result = Base64_Decode(q, rawMachineID_2, sizeof rawMachineID_2, &len);
   Posix_Free(q);

   if ((result == FALSE) || (len != 12)) {
      Warning("%s: unexpected decode problem #2 (%s)\n", __FUNCTION__,
              second);

      return FALSE;
   }

   return memcmp(&rawMachineID_1[4],
                 &rawMachineID_2[4], 8) == 0 ? TRUE : FALSE;
#else
   return strcmp(first, second) == 0 ? TRUE : FALSE;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockMachineIDMatch --
 *
 *      Do the MachineIDs match?
 *
 * Results:
 *      TRUE     Yes
 *      FALSE    No
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
FileLockMachineIDMatch(const char *hostMachineID,   // IN:
                       const char *otherMachineID)  // IN:
{
   if (strncmp(hostMachineID, "uuid=", 5) == 0) {
      if (strncmp(otherMachineID, "uuid=", 5) == 0) {
         return strcmp(hostMachineID + 5,
                       otherMachineID + 5) == 0 ? TRUE : FALSE;
      } else {
         return OldMachineIDMatch(GetOldMachineID(), otherMachineID);
      }
   } else {
      if (strncmp(otherMachineID, "uuid=", 5) == 0) {
         return FALSE;
      } else {
         return strcmp(hostMachineID, otherMachineID) == 0 ? TRUE : FALSE;
      }
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * File_IsEmptyDirectory --
 *
 *      Check if specified file is a directory and contains no files.
 *
 * Results:
 *      Bool - TRUE -> is an empty directory, FALSE -> not an empty directory
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

Bool
File_IsEmptyDirectory(const char *pathName)  // IN:
{
   int numFiles;

   if (!File_IsDirectory(pathName)) {
      return FALSE;
   }

   numFiles = File_ListDirectory(pathName, NULL);
   if (numFiles < 0) {
      return FALSE;
   }

   return numFiles == 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * File_IsOsfsVolumeEmpty --
 *
 *      Check if specified OSFS volume contains no files.
 *      This method ignore hidden .sf files. *.sf files are VMFS
 *      metadata files.
 *
 *      OSFS based volumes are considered empty even if they
 *      contain vmfs metadata files. This emptiness can not be
 *      checked by File_IsEmptyDirectory API (PR 1050328).
 *
 * Results:
 *      Bool - TRUE -> is vmfs empty directory, FALSE -> not an vmfs
 *      empty directory
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

Bool
File_IsOsfsVolumeEmpty(const char *pathName)  // IN:
{
   int i, numFiles;
   char **fileList = NULL;
   static const char vmfsSystemFilesuffix[] = ".sf";
   Bool onlyVmfsSystemFilesFound = TRUE;

   numFiles = File_ListDirectory(pathName, &fileList);
   if (numFiles == -1) {
      return FALSE;
   }

   for (i = 0; i < numFiles; i++) {
      if (!Unicode_EndsWith(fileList[i], vmfsSystemFilesuffix)) {
         onlyVmfsSystemFilesFound = FALSE;
         break;
      }
   }

   Util_FreeStringList(fileList, numFiles);

   return onlyVmfsSystemFilesFound;
}


/*
 *----------------------------------------------------------------------
 *
 * File_IsFile --
 *
 *      Check if specified file is a regular file.
 *
 * Results:
 *      TRUE    is a regular file
 *      FALSE   is not a regular file or an error occured.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_IsFile(const char *pathName)  // IN:
{
   FileData fileData;

   return (FileAttributes(pathName, &fileData) == 0) &&
           (fileData.fileType == FILE_TYPE_REGULAR);
}


/*
 *----------------------------------------------------------------------
 *
 * File_CopyFromFdToFd --
 *
 *      Write all data between the current position in the 'src' file and the
 *      end of the 'src' file to the current position in the 'dst' file
 *
 * Results:
 *      TRUE   success
 *      FALSE  failure
 *
 * Side effects:
 *      The current position in the 'src' file and the 'dst' file are modified
 *
 *----------------------------------------------------------------------
 */

Bool
File_CopyFromFdToFd(FileIODescriptor src,  // IN:
                    FileIODescriptor dst)  // IN:
{
   Err_Number err;
   FileIOResult fretR;

   do {
      unsigned char buf[8 * 1024];
      size_t actual;
      FileIOResult fretW;

      fretR = FileIO_Read(&src, buf, sizeof buf, &actual);
      if (!FileIO_IsSuccess(fretR) && (fretR != FILEIO_READ_ERROR_EOF)) {
         err = Err_Errno();

         Msg_Append(MSGID(File.CopyFromFdToFd.read.failure)
                               "Read error: %s.\n\n", FileIO_MsgError(fretR));

         Err_SetErrno(err);

         return FALSE;
      }

      fretW = FileIO_Write(&dst, buf, actual, NULL);
      if (!FileIO_IsSuccess(fretW)) {
         err = Err_Errno();

         Msg_Append(MSGID(File.CopyFromFdToFd.write.failure)
                              "Write error: %s.\n\n", FileIO_MsgError(fretW));

         Err_SetErrno(err);

         return FALSE;
      }
   } while (fretR != FILEIO_READ_ERROR_EOF);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileCopyTree --
 *
 *      Recursively copies all files from a source path to a destination,
 *      optionally overwriting any files. This does the actual work
 *      for File_CopyTree.
 *
 * Results:
 *      TRUE   Success.
 *      FALSE  Failure. Error messages appended
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
FileCopyTree(const char *srcName,     // IN:
             const char *dstName,     // IN:
             Bool overwriteExisting,  // IN:
             Bool followSymlinks)     // IN:
{
   int err;
   Bool success = TRUE;
   int numFiles;
   int i;
   char **fileList = NULL;

   numFiles = File_ListDirectory(srcName, &fileList);

   if (numFiles == -1) {
      err = Err_Errno();
      Msg_Append(MSGID(File.CopyTree.walk.failure)
                 "Unable to access '%s' when copying files.\n\n",
                 srcName);
      Err_SetErrno(err);

      return FALSE;
   }

   File_EnsureDirectory(dstName);

   for (i = 0; i < numFiles && success; i++) {
      struct stat sb;
      char *srcFilename;

      srcFilename = File_PathJoin(srcName, fileList[i]);

      if (followSymlinks) {
         success = (Posix_Stat(srcFilename, &sb) == 0);
      } else {
         success = (Posix_Lstat(srcFilename, &sb) == 0);
      }

      if (success) {
         char *dstFilename = File_PathJoin(dstName, fileList[i]);

         switch (sb.st_mode & S_IFMT) {
         case S_IFDIR:
            success = FileCopyTree(srcFilename, dstFilename, overwriteExisting,
                                   followSymlinks);
            break;

#if !defined(_WIN32)
         case S_IFLNK:
            if (Posix_Symlink(Posix_ReadLink(srcFilename), dstFilename) != 0) {
               err = Err_Errno();
               Msg_Append(MSGID(File.CopyTree.symlink.failure)
                          "Unable to symlink '%s' to '%s': %s\n\n",
                          Posix_ReadLink(srcFilename),
                          dstFilename,
                          Err_Errno2String(err));
               Err_SetErrno(err);
               success = FALSE;
            }
            break;
#endif

         default:
            if (!File_Copy(srcFilename, dstFilename, overwriteExisting)) {
               err = Err_Errno();
               Msg_Append(MSGID(File.CopyTree.copy.failure)
                          "Unable to copy '%s' to '%s': %s\n\n",
                          srcFilename, dstFilename,
                          Err_Errno2String(err));
               Err_SetErrno(err);
               success = FALSE;
            }

            break;
         }

         Posix_Free(dstFilename);
      } else {
         err = Err_Errno();
         Msg_Append(MSGID(File.CopyTree.stat.failure)
                    "Unable to get information on '%s' when copying files.\n\n",
                    srcFilename);
         Err_SetErrno(err);
      }

      Posix_Free(srcFilename);
   }

   Util_FreeStringList(fileList, numFiles);

   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_CopyTree --
 *
 *      Recursively copies all files from a source path to a destination,
 *      optionally overwriting any files.
 *
 * Results:
 *      TRUE   Success.
 *      FALSE  Failure. Error messages appended
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_CopyTree(const char *srcName,     // IN:
              const char *dstName,     // IN:
              Bool overwriteExisting,  // IN:
              Bool followSymlinks)     // IN:
{
   int err;

   ASSERT(srcName != NULL);
   ASSERT(dstName != NULL);

   if (!File_IsDirectory(srcName)) {
      err = Err_Errno();
      Msg_Append(MSGID(File.CopyTree.source.notDirectory)
                 "Source path '%s' is not a directory.",
                 srcName);
      Err_SetErrno(err);
      return FALSE;
   }

   if (!File_IsDirectory(dstName)) {
      err = Err_Errno();
      Msg_Append(MSGID(File.CopyTree.dest.notDirectory)
                 "Destination path '%s' is not a directory.",
                 dstName);
      Err_SetErrno(err);
      return FALSE;
   }

   return FileCopyTree(srcName, dstName, overwriteExisting, followSymlinks);
}


/*
 *----------------------------------------------------------------------
 *
 * File_CopyFromFd --
 *
 *      Copy the 'src' file to 'dstName'.
 *      If the 'dstName' file already exists, 'overwriteExisting'
 *      decides whether to overwrite the existing file or not.
 *
 * Results:
 *      TRUE   Success.
 *      FALSE  Failure. Error messages appended
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_CopyFromFd(FileIODescriptor src,    // IN:
                const char *dstName,     // IN:
                Bool overwriteExisting)  // IN:
{
   Bool success;
   Err_Number err;
   FileIOResult fret;
   FileIODescriptor dst;
   FileIOOpenAction action;

   ASSERT(dstName != NULL);

   FileIO_Invalidate(&dst);

   action = overwriteExisting ? FILEIO_OPEN_CREATE_EMPTY :
                                FILEIO_OPEN_CREATE_SAFE;

   fret = FileIO_Open(&dst, dstName, FILEIO_OPEN_ACCESS_WRITE, action);
   if (!FileIO_IsSuccess(fret)) {
      err = Err_Errno();

      Msg_Append(MSGID(File.CopyFromFdToName.create.failure)
                 "Unable to create a new '%s' file: %s.\n\n", dstName,
                 FileIO_MsgError(fret));

      Err_SetErrno(err);

      return FALSE;
   }

   success = File_CopyFromFdToFd(src, dst);

   err = Err_Errno();

   if (!FileIO_IsSuccess(FileIO_Close(&dst))) {
      if (success) {  // Report close failure when there isn't another error
         err =  Err_Errno();
      }

      Msg_Append(MSGID(File.CopyFromFdToName.close.failure)
                 "Unable to close the '%s' file: %s.\n\n", dstName,
                 Msg_ErrString());

      success = FALSE;
   }

   if (!success) {
      /* The copy failed: ensure the destination file is removed */
      File_Unlink(dstName);
   }

   Err_SetErrno(err);

   return success;
}


/*
 *----------------------------------------------------------------------
 *
 * File_Copy --
 *
 *      Copy the 'srcName' file to 'dstName'.
 *      If 'srcName' doesn't exist, an error is reported
 *      If the 'dstName' file already exists, 'overwriteExisting'
 *      decides whether to overwrite the existing file or not.
 *
 * Results:
 *      TRUE   Success.
 *      FALSE  Failure. Error messages appended
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_Copy(const char *srcName,     // IN:
          const char *dstName,     // IN:
          Bool overwriteExisting)  // IN:
{
   Bool success;
   Err_Number err;
   FileIOResult fret;
   FileIODescriptor src;

   ASSERT(srcName != NULL);
   ASSERT(dstName != NULL);

   FileIO_Invalidate(&src);

   fret = FileIO_Open(&src, srcName, FILEIO_OPEN_ACCESS_READ, FILEIO_OPEN);
   if (!FileIO_IsSuccess(fret)) {
      err = Err_Errno();

      Msg_Append(MSGID(File.Copy.open.failure)
                 "Unable to open the '%s' file for read access: %s.\n\n",
                 srcName, FileIO_MsgError(fret));

      Err_SetErrno(err);

      return FALSE;
   }

   success = File_CopyFromFd(src, dstName, overwriteExisting);

   err = Err_Errno();

   if (!FileIO_IsSuccess(FileIO_Close(&src))) {
      if (success) {  // Report close failure when there isn't another error
         err =  Err_Errno();
      }

      Msg_Append(MSGID(File.Copy.close.failure)
                 "Unable to close the '%s' file: %s.\n\n", srcName,
                 Msg_ErrString());

      success = FALSE;
   }

   Err_SetErrno(err);

   return success;
}


/*
 *----------------------------------------------------------------------
 *
 * File_Move --
 *
 *      Moves a file from one place to the other as efficiently as possible.
 *      This can be used to rename a file but, since file copying may be
 *      necessary, there is no assurance of atomicity. For efficiency
 *      purposes copying only results if the native rename ability fails.
 *
 * Results:
 *      TRUE   success
 *      FALSE  otherwise
 *
 * Side effects:
 *      src file is no more, but dst file exists
 *
 *----------------------------------------------------------------------
 */

Bool
File_Move(const char *oldFile,  // IN:
          const char *newFile,  // IN:
          Bool *asRename)       // OUT/OPT: result occurred due to rename/copy
{
   Bool ret;
   Bool duringRename;

   if (File_Rename(oldFile, newFile) == 0) {
      duringRename = TRUE;
      ret = TRUE;
      Err_SetErrno(0);
   } else {
      duringRename = FALSE;

      if (File_Copy(oldFile, newFile, TRUE)) {  // Allow overwrite
         File_Unlink(oldFile);  // Explicitly ignore errors
         ret = TRUE;
         Err_SetErrno(0);
      } else {
         ret = FALSE;
      }
   }

   if (asRename != NULL) {
      *asRename = duringRename;
   }

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_MoveTree --
 *
 *    Moves a directory from one place to the other.
 *     - If dstName indicates a path that does not exist a directory will be
 *       created with that path filled with the contents from srcName.
 *     - If dstName is an existing directory then the contents will be moved
 *       into that directory.
 *     - If dstName indicates a file then File_MoveTree fails.
 *
 *    First we'll attempt to rename the directory, failing that we copy the
 *    contents from src->destination and unlink the src.  If the copy is
 *    succesful then we will report success even if the unlink fails for some
 *    reason.  In that event we will append error messages.
 *
 * Results:
 *    TRUE   Success.
 *    FALSE  Failure. Error messages appended
 *
 * Side effects:
 *    - Deletes the originating directory
 *    - In the event of a failed copy we'll leave the new directory in an
 *      undefined state. Calling File_DeleteDirectoryContent would be a
 *      good idea.
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_MoveTree(const char *srcName,    // IN:
              const char *dstName,    // IN:
              Bool overwriteExisting, // IN:
              Bool *asMove)           // OUT/OPT:
{
   Bool ret = FALSE;
   Bool createdDir = FALSE;

   ASSERT(srcName != NULL);
   ASSERT(dstName != NULL);

   if (asMove != NULL) {
      *asMove = FALSE;
   }

   if (!File_IsDirectory(srcName)) {
      Msg_Append(MSGID(File.MoveTree.source.notDirectory)
                 "Source path '%s' is not a directory.",
                 srcName);

      return FALSE;
   }

   if (File_Rename(srcName, dstName) == 0) {
      if (asMove != NULL) {
         *asMove = TRUE;
      }

      ret = TRUE;
   } else {
      struct stat statbuf;

      if (Posix_Stat(dstName, &statbuf) == -1) {
         int err = Err_Errno();

         if (err == ENOENT) {
            if (!File_CreateDirectoryHierarchy(dstName, NULL)) {
               Msg_Append(MSGID(File.MoveTree.dst.couldntCreate)
                          "Could not create '%s'.\n\n", dstName);

               return FALSE;
            }

            createdDir = TRUE;
         } else {
            Msg_Append(MSGID(File.MoveTree.statFailed)
                       "%d:Failed to stat destination '%s'.\n\n",
                       err, dstName);

            return FALSE;
         }
      } else {
         if (!File_IsDirectory(dstName)) {
            Msg_Append(MSGID(File.MoveTree.dest.notDirectory)
                       "The destination path '%s' is not a directory.\n\n",
                       dstName);

            return FALSE;
         }
      }

#if !defined(__FreeBSD__) && !defined(sun)
      /*
       * File_GetFreeSpace is not defined for FreeBSD
       */
      if (createdDir) {
         /*
          * Check for free space on destination filesystem. We only check for
          * free space if the destination directory did not exist. In this
          * case, we will not be overwriting any existing paths, so we need as
          * much space as srcName.
          */
         int64 srcSize = File_GetSizeEx(srcName);
         int64 freeSpace = File_GetFreeSpace(dstName, TRUE);

         if (freeSpace < srcSize) {
            char *spaceStr = Msg_FormatSizeInBytes(srcSize);
            Msg_Append(MSGID(File.MoveTree.dst.insufficientSpace)
                  "There is not enough space in the file system to "
                  "move the directory tree. Free %s and try again.",
                  spaceStr);
            Posix_Free(spaceStr);
            return FALSE;
         }
      }
#endif

      if (File_CopyTree(srcName, dstName, overwriteExisting, FALSE)) {
         ret = TRUE;

         if (!File_DeleteDirectoryTree(srcName)) {
            Msg_Append(MSGID(File.MoveTree.cleanupFailed)
                       "Forced to copy '%s' into '%s' but unable to remove "
                       "source directory.\n\n",
                       srcName, dstName);
         }
      } else {
         ret = FALSE;
         Msg_Append(MSGID(File.MoveTree.copyFailed)
                    "Could not rename and failed to copy source directory "
                    "'%s'.\n\n",
                    srcName);
         if (createdDir) {
            /*
             * Only clean up if we created the directory.  Not attempting to
             * clean up partial failures.
             */
            /* coverity[check_return] */
            File_DeleteDirectoryTree(dstName);
         }
      }
   }

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetModTimeString --
 *
 *      Returns a human-readable string denoting the last modification
 *      time of a file.
 *      ctime() returns string terminated with newline, which we replace
 *      with a '\0'.
 *
 * Results:
 *      Last modification time string on success, NULL on error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

char *
File_GetModTimeString(const char *pathName)  // IN:
{
   int64 modTime = File_GetModTime(pathName);

   return (modTime == -1) ? NULL : TimeUtil_GetTimeFormat(modTime, TRUE, TRUE);
}


/*
 *----------------------------------------------------------------------------
 *
 * File_GetSize --
 *
 *      Get size of file. Try File_GetSizeEx to get size of directory/symlink.
 *
 *      For performance reasons, whenever a file grows, many file systems elect
 *      to not update the on-storage inode information until close or when
 *      forced to write a dirty page. This is done to avoid wasting I/O
 *      throughput.
 *
 *      The only way to determine the exact, up-to-date size of a file is to
 *      open it and query the file size.
 *
 * Results:
 *      Size of file or -1.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int64
File_GetSize(const char *pathName)  // IN:
{
   int64 ret;

   if (pathName == NULL) {
      ret = -1;
   } else {
      FileIODescriptor fd;
      FileIOResult res;

      FileIO_Invalidate(&fd);
      res = FileIO_Open(&fd, pathName, FILEIO_OPEN_ACCESS_READ, FILEIO_OPEN);

      if (FileIO_IsSuccess(res)) {
         ret = FileIO_GetSize(&fd);
         FileIO_Close(&fd);
      } else {
         ret = -1;
      }
   }

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * File_SupportsLargeFiles --
 *
 *      Check if the given file is on an FS that supports 4GB files.
 *      Require 4GB support so we rule out FAT filesystems, which
 *      support 4GB-1 on both Linux and Windows.
 *
 * Results:
 *      TRUE if FS supports files over 4GB.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_SupportsLargeFiles(const char *pathName)  // IN:
{
   return File_SupportsFileSize(pathName, CONST64U(0x100000000));
}


/*
 *----------------------------------------------------------------------------
 *
 * File_GetSizeEx --
 *
 *      Get size of file or directory or symlink. File_GetSize can only get
 *      size of file.
 *
 * Results:
 *      Size of file/directory/symlink or -1.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int64
File_GetSizeEx(const char *pathName)  // IN:
{
   int i;
   int numFiles;
   int64 totalSize = 0;
   char **fileList = NULL;

   if (pathName == NULL) {
      return -1;
   }

   if (!File_IsDirectory(pathName)) {
      return File_GetSize(pathName);
   }

   numFiles = File_ListDirectory(pathName, &fileList);
   if (numFiles == -1) {
      return -1;
   }

   for (i = 0; i < numFiles; i++) {
      char *fileName = File_PathJoin(pathName, fileList[i]);
      int64 fileSize = File_GetSizeEx(fileName);

      Posix_Free(fileName);

      if (fileSize != -1) {
         totalSize += fileSize;
      }
   }

   Util_FreeStringList(fileList, numFiles);

   return totalSize;
}


/*
 *----------------------------------------------------------------------------
 *
 * File_GetSizeByPath --
 *
 *      Get size of a file without opening it.
 *
 * Results:
 *      Size of file or -1.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int64
File_GetSizeByPath(const char *pathName)  // IN:
{
   return (pathName == NULL) ? -1 : FileIO_GetSizeByPath(pathName);
}


/*
 *----------------------------------------------------------------------
 *
 * FileFirstSlashIndex --
 *
 *      Finds the first pathname slash index in a path (both slashes count
 *      for Win32, only forward slash for Unix).
 *
 * Results:
 *      As described.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static UnicodeIndex
FileFirstSlashIndex(const char *pathName,     // IN:
                    UnicodeIndex startIndex)  // IN:
{
   UnicodeIndex firstFS;
#if defined(_WIN32)
   UnicodeIndex firstBS;
#endif

   ASSERT(pathName != NULL);

   firstFS = Unicode_FindSubstrInRange(pathName, startIndex, -1,
                                       "/", 0, 1);

#if defined(_WIN32)
   firstBS = Unicode_FindSubstrInRange(pathName, startIndex, -1,
                                       "\\", 0, 1);

   if ((firstFS != UNICODE_INDEX_NOT_FOUND) &&
       (firstBS != UNICODE_INDEX_NOT_FOUND)) {
      return MIN(firstFS, firstBS);
   } else {
     return (firstFS == UNICODE_INDEX_NOT_FOUND) ? firstBS : firstFS;
   }
#else
   return firstFS;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_CreateDirectoryHierarchyEx --
 *
 *      Create a directory including any parents that don't already exist.
 *      All the created directories are tagged with the specified permission.
 *      Returns the topmost directory which was created, to allow calling code
 *      to remove it after in case later operations fail.
 *
 * Results:
 *      TRUE   Success.
 *      FALSE  Failure.
 *
 *      If topmostCreated is not NULL, it returns the result of the hierarchy
 *      creation. If no directory was created, *topmostCreated is set to NULL.
 *      Otherwise *topmostCreated is set to the topmost directory which was
 *      created. *topmostCreated is set even in case of failure.
 *
 *      The caller most free the resulting string.
 *
 * Side effects:
 *      Only the obvious.
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_CreateDirectoryHierarchyEx(const char *pathName,   // IN:
                                int mode,               // IN:
                                char **topmostCreated)  // OUT/OPT:
{
   char *volume;
   UnicodeIndex index;
   UnicodeIndex length;

   if (topmostCreated != NULL) {
      *topmostCreated = NULL;
   }

   if (pathName == NULL) {
      return TRUE;
   }

   length = Unicode_LengthInCodePoints(pathName);

   if (length == 0) {
      return TRUE;
   }

   /*
    * Skip past any volume/share.
    */

   File_SplitName(pathName, &volume, NULL, NULL);

   index = Unicode_LengthInCodePoints(volume);

   Posix_Free(volume);

   if (index >= length) {
      return File_IsDirectory(pathName);
   }

   /*
    * Iterate directory path, creating directories as necessary.
    */

   while (TRUE) {
      int err;
      char *temp;

      index = FileFirstSlashIndex(pathName, index + 1);

      temp = Unicode_Substr(pathName,
                            0,
                            (index == UNICODE_INDEX_NOT_FOUND) ? -1 : index);

      /*
       * If we check if the directory already exists and then we create it,
       * there is a race between these two operations. Any failure can be
       * confusing. We avoid this by attempting to create the directory before
       * checking the type.
       */
      err = FileCreateDirectory(temp, mode);

      if (err == 0) {
         if (topmostCreated != NULL && *topmostCreated == NULL) {
            *topmostCreated = temp;
            temp = NULL;
         }
      } else {
         /*
          * For DELL thinOS, calling `mkdir' for an existing directory in a
          * path which we do not have write permission will return with EACCES
          * instead of EEXIST. Here we test again using `euidaccess' to work
          * around this.
          */
         if (err == EACCES && Posix_EuidAccess(temp, F_OK) == 0) {
            err = EEXIST;
         }

         if (err == EEXIST) {
            FileData fileData;

            err = FileAttributes(temp, &fileData);

            if (err == 0) {
               if (fileData.fileType != FILE_TYPE_DIRECTORY) {
                  err = ENOTDIR;
                  errno = err;

#if defined(_WIN32)
                  SetLastError(ERROR_DIRECTORY);
#endif
               }
            }
         }
      }

      if (err != 0) {
         Log(LGPFX" %s: Failure on '%s'. Error = %d\n",
             __FUNCTION__, temp, err);
      }

      Posix_Free(temp);

      if (err != 0) {
         return FALSE;
      }

      if (index == UNICODE_INDEX_NOT_FOUND) {
         break;
      }
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_CreateDirectoryHierarchy --
 *
 *      Create a directory including any parents that don't already exist.
 *      All the created directories are tagged with 0777 permissions.
 *      Returns the topmost directory which was created, to allow calling code
 *      to remove it after in case later operations fail.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 *      If topmostCreated is not NULL, it returns the result of the hierarchy
 *      creation. If no directory was created, *topmostCreated is set to NULL.
 *      Otherwise *topmostCreated is set to the topmost directory which was
 *      created. *topmostCreated is set even in case of failure.
 *
 *      The caller most free the resulting string.
 *
 * Side effects:
 *      Only the obvious.
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_CreateDirectoryHierarchy(const char *pathName,   // IN:
                              char **topmostCreated)  // OUT/OPT:
{
   return File_CreateDirectoryHierarchyEx(pathName, 0777, topmostCreated);
}


/*
 *----------------------------------------------------------------------
 *
 * FileDeleteDirectoryTree --
 *
 *      Deletes the specified directory tree. If filesystem errors are
 *      encountered along the way, the function will continue to delete what
 *      it can but will return FALSE. If contentOnly is TRUE it does not
 *      delete the directory itself.
 *
 * Results:
 *      TRUE   the entire tree was deleted or didn't exist
 *      FALSE  otherwise.
 *
 * Side effects:
 *      Deletes the directory tree from disk.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
FileDeleteDirectoryTree(const char *pathName,  // IN: directory to delete
                        Bool contentOnly)      // IN: Content only or not
{
   int i;
   int numFiles;
   int err = 0;
   char *base;

   char **fileList = NULL;
   Err_Number fileError = 0;

   if (Posix_EuidAccess(pathName, F_OK) != 0) {
      /*
       * If Posix_EuidAccess failed with errno == ENOSYS, then fall back
       * to FileAttributes.
       */
      if (errno == ENOSYS) {
         /* FileAttributes returns the error code instead of setting errno. */
         err = FileAttributes(pathName, NULL);
      } else {
         /* Use the error value that was set by Posix_EuidAccess. */
         err = errno;
      }
   }

   switch (err) {
      case ENOENT:
      case ENOTDIR:
         /* path does not exist or is inaccessible */
         return TRUE;
      default:
         break;
   }

   /* get list of files in current directory */
   numFiles = File_ListDirectory(pathName, &fileList);

   if (numFiles == -1) {
      return FALSE;
   }

   /* delete everything in the directory */
   base = Unicode_Append(pathName, DIRSEPS);

   for (i = 0; i < numFiles; i++) {
      char *curPath;
      struct stat statbuf;

      curPath = Unicode_Append(base, fileList[i]);

      if (Posix_Lstat(curPath, &statbuf) == 0) {
         switch (statbuf.st_mode & S_IFMT) {
         case S_IFDIR:
            /* Directory, recurse */
            if (!FileDeleteDirectoryTree(curPath, FALSE)) {
               fileError = Err_Errno();
            }
            break;

#if !defined(_WIN32)
         case S_IFLNK:
            /* Delete symlink, not what it points to */
            err = FileDeletion(curPath, FALSE);

            if ((err != 0) && (err != ENOENT)) {
               fileError = Err_Errno();
            }
            break;
#endif

         default:
            err = FileDeletion(curPath, FALSE);

            if ((err != 0) && (err != ENOENT)) {
#if defined(_WIN32)
               if (File_SetFilePermissions(curPath, S_IWUSR)) {
                  err = FileDeletion(curPath, FALSE);

                  if ((err != 0) && (err != ENOENT)) {
                     fileError = Err_Errno();
                  }
               } else {
                  fileError = Err_Errno();
               }
#else
               fileError = Err_Errno();
#endif
            }
            break;
         }
      } else {
         if (errno != ENOENT) {
            fileError = Err_Errno();
            Log(LGPFX" %s: Lstat of '%s' failed, errno = %d\n",
                __FUNCTION__, curPath, errno);
         }
      }

      Posix_Free(curPath);
   }

   Posix_Free(base);

   if (!contentOnly) {
      /*
       * Call File_DeleteEmptyDirectory() only if there is no prior error
       * while deleting the children.
       */
      if (fileError == 0 && !File_DeleteEmptyDirectory(pathName)) {
         fileError = Err_Errno();
      }
   }

   Util_FreeStringList(fileList, numFiles);

   Err_SetErrno(fileError);

   return fileError == 0;
}


/*
 *----------------------------------------------------------------------
 *
 * File_DeleteDirectoryContent --
 *
 *      Deletes the specified directory content. If filesystem errors are
 *      encountered along the way, the function will continue to delete what
 *      it can but will return FALSE.
 *
 * Results:
 *      TRUE   the entire contents were deleted or there were no files and the
 *             directory was empty
 *      FALSE  otherwise
 *
 * Side effects:
 *      Deletes the directory content from disk.
 *
 *----------------------------------------------------------------------
 */

Bool
File_DeleteDirectoryContent(const char *pathName)  // IN: directory to delete
{
   return FileDeleteDirectoryTree(pathName, TRUE);
}


/*
 *----------------------------------------------------------------------
 *
 * File_DeleteDirectoryTree --
 *
 *      Deletes the specified directory tree. If filesystem errors are
 *      encountered along the way, the function will continue to delete what
 *      it can but will return FALSE.
 *
 * Results:
 *      TRUE   the entire tree was deleted or didn't exist
 *      FALSE  otherwise.
 *
 * Side effects:
 *      Deletes the directory tree from disk.
 *
 *----------------------------------------------------------------------
 */

Bool
File_DeleteDirectoryTree(const char *pathName)  // IN: directory to delete
{
   return FileDeleteDirectoryTree(pathName, FALSE);
}

/*
 *-----------------------------------------------------------------------------
 *
 * File_FindFileInSearchPath --
 *
 *      Search all the directories in searchPath for a filename.
 *      If searchPath has a relative path take it with respect to cwd.
 *      searchPath must be ';' delimited.
 *
 * Results:
 *      TRUE   file was found
 *      FALSE  otherwise.
 *
 * Side effects:
 *      If result is non Null allocate a string for the filename found.
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_FindFileInSearchPath(const char *fileIn,      // IN:
                          const char *searchPath,  // IN:
                          const char *cwd,         // IN:
                          char **result)           // OUT/OPT:
{
   char *cur;
   char *tok;
   Bool found;
   Bool full;
   char *saveptr = NULL;
   char *sp = NULL;
   char *dir = NULL;
   char *file = NULL;

   ASSERT(fileIn != NULL);
   ASSERT(searchPath != NULL);
   ASSERT(cwd != NULL);

   /*
    * First check the usual places - the fullpath or the cwd.
    */

   full = File_IsFullPath(fileIn);
   if (full) {
      cur = Util_SafeStrdup(fileIn);
   } else {
      cur = Str_SafeAsprintf(NULL, "%s%s%s", cwd, DIRSEPS, fileIn);
   }

   if (Posix_EuidAccess(cur, F_OK) == 0) {
      goto done;
   }
   if (errno == ENOSYS && FileAttributes(cur, NULL) == 0) {
      goto done;
   }

   Posix_Free(cur);
   cur = NULL;

   if (full) {
      goto done;
   }

   File_GetPathName(fileIn, &dir, &file);

   /*
    * Search path applies only if filename is simple basename.
    */
   if (Unicode_LengthInCodePoints(dir) != 0) {
      goto done;
   }

   /*
    * Didn't find it in the usual places so strip it to its bare minimum and
    * start searching.
    */

   sp = Util_SafeStrdup(searchPath);
   tok = strtok_r(sp, FILE_SEARCHPATHTOKEN, &saveptr);

   while (tok != NULL) {
      if (File_IsFullPath(tok)) {
         /* Fully Qualified Path. Use it. */
         cur = Str_SafeAsprintf(NULL, "%s%s%s", tok, DIRSEPS, file);
      } else {
         /* Relative Path.  Prepend the cwd. */
         if (Str_Strcasecmp(tok, ".") == 0) {
            /* Don't append "." */
            cur = Str_SafeAsprintf(NULL, "%s%s%s", cwd, DIRSEPS, file);
         } else {
            cur = Str_SafeAsprintf(NULL, "%s%s%s%s%s", cwd, DIRSEPS, tok,
                                   DIRSEPS, file);
         }
      }

      if (Posix_EuidAccess(cur, F_OK) == 0) {
         break;
      }

      if ((errno == ENOSYS) && (FileAttributes(cur, NULL) == 0)) {
         break;
      }

      Posix_Free(cur);
      cur = NULL;

      tok = strtok_r(NULL, FILE_SEARCHPATHTOKEN, &saveptr);
   }

done:
   if (cur != NULL) {
      found = TRUE;

      if (result) {
         *result = File_FullPath(cur);

         if (*result == NULL) {
            found = FALSE;
         }
      }

      Posix_Free(cur);
   } else {
      found = FALSE;
   }

   Posix_Free(sp);
   Posix_Free(dir);
   Posix_Free(file);

   return found;
}


/*
 *----------------------------------------------------------------------
 *
 * File_ExpandAndCheckDir --
 *
 *      Expand any environment variables in the given path and check that
 *      the named directory is writeable.
 *
 * Results:
 *      NULL error
 *     !NULL the expanded path otherwise.
 *
 * Side effects:
 *      The result is allocated.
 *
 *----------------------------------------------------------------------
 */

char *
File_ExpandAndCheckDir(const char *dirName)  // IN:
{
   if (dirName != NULL) {
      char *edirName = Util_ExpandString(dirName);

      if ((edirName != NULL) && FileIsWritableDir(edirName)) {
         size_t len = strlen(edirName) - 1;

         if (edirName[len] == DIRSEPC) {
            edirName[len] = '\0';
         }

         return edirName;
      }
      free(edirName);
   }

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileSimpleRandom --
 *
 *      Return a random number in the range of 0 and 2^32-1.
 *
 * Results:
 *      Random number is returned.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

uint32
FileSimpleRandom(void)
{
   uint32 result;
   static rqContext *context = NULL;
   static Atomic_uint32 spinLock = { 0 };

   /*
    * Use a spin lock here since:
    *
    *   The chance we'll spin in tiny.
    *   The time spent under the spin lock is miniscule.
    *   The time spent under the spin lock is not highly variable.
    *   We can't get stuck under the spin lock.
    *   The overhead of a mutex is larger than the time spent under the spin lock.
    *   It uses much less memory than a mutex.
    */

   while (Atomic_ReadWrite(&spinLock, 1)) {
      PAUSE();
   }

   if (UNLIKELY(context == NULL)) {
      uint32 value;

#if defined(_WIN32)
      value = GetCurrentProcessId();
#else
      value = getpid();
#endif

      context = Random_QuickSeed(value);
      ASSERT(context != NULL);
   }

   result = Random_Quick(context);

   Atomic_Write(&spinLock, 0);

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * FileSleeper
 *
 *      Sleep for a random amount of time, no less than the specified minimum
 *      and no more than the specified maximum. This often proves useful to
 *      "jitter" retries such that multiple threads don't easily get into
 *      resonance performing necessary actions (e.g. retries).
 *
 * Results:
 *      Somnambulistic behavior; the amount of time slept is returned.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

uint32
FileSleeper(uint32 minSleepTimeMsec,  // IN:
            uint32 maxSleepTimeMsec)  // IN:
{
   uint32 variance;
   uint32 actualSleepTimeMsec;
#if defined(_WIN32)
   uint32 totalSleepTimeMsec;
#endif

   ASSERT(minSleepTimeMsec <= maxSleepTimeMsec);

   variance = maxSleepTimeMsec - minSleepTimeMsec;

   if (variance == 0) {
      actualSleepTimeMsec = minSleepTimeMsec;
   } else {
      float fpRand = ((float) FileSimpleRandom()) / ((float) ~((uint32) 0));

      actualSleepTimeMsec = minSleepTimeMsec + (uint32) (fpRand * variance);
   }

#if defined(_WIN32)
   /* Clamp individual sleeps to avoid Windows issues */
   totalSleepTimeMsec = actualSleepTimeMsec;

   while (totalSleepTimeMsec > 0) {
      uint32 sleepTimeMsec = (totalSleepTimeMsec > 900) ? 900 :
                                                          totalSleepTimeMsec;

      Util_Usleep(1000 * sleepTimeMsec);

      totalSleepTimeMsec -= sleepTimeMsec;
   }
#else
   Util_Usleep(1000 * actualSleepTimeMsec);
#endif

   return actualSleepTimeMsec;
}


/*
 *----------------------------------------------------------------------
 *
 * FileRotateByRename --
 *
 *      The oldest indexed file should be removed so that the consequent
 *      rename succeeds.
 *
 *      The last dst is 'fileName' and should not be deleted.
 *
 * Results:
 *      If newFileName is non-NULL: the new path is returned to *newFileName
 *      if the rotation succeeded, otherwise NULL is returned in *newFileName.
 *      The caller is responsible for freeing the string returned in
 *      *newFileName.
 *
 * Side effects:
 *      Rename backup old files kept so far.
 *
 *----------------------------------------------------------------------
 */

static void
FileRotateByRename(const char *fileName,  // IN: full path to file
                   const char *baseName,  // IN: filename w/o extension.
                   const char *ext,       // IN: extension
                   int n,                 // IN: number of old files to keep
                   char **newFileName)    // OUT/OPT: new path to file
{
   char *src = NULL;
   char *dst = NULL;
   int i;
   int result;

   if (newFileName != NULL) {
      *newFileName = NULL;
   }

   for (i = n; i >= 0; i--) {
      src = (i == 0) ? (char *) fileName :
                       Str_SafeAsprintf(NULL, "%s-%d%s", baseName, i - 1, ext);

      if (dst == NULL) {
         result = FileDeletion(src, FALSE);  // Don't follow a symlink!

         if ((result != 0) && (result != ENOENT)) {
            Log(LGPFX" %s: failed to remove %s: %s\n", __FUNCTION__,
                src, Err_Errno2String(Err_Errno()));
         }
      } else {
         result = File_Rename(src, dst);

         if ((result != 0) && (result != ENOENT)) {
            Log(LGPFX" %s: rename of %s -> %s failed: %s\n", src, dst,
                __FUNCTION__, Err_Errno2String(Err_Errno()));
         }
      }

      if ((src == fileName) && (newFileName != NULL) && (result == 0)) {
         *newFileName = Util_SafeStrdup(dst);
      }

      ASSERT(dst != fileName);
      Posix_Free(dst);
      /* coverity[use_after_free] */
      dst = src;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * FileNumberCompare --
 *
 *      Helper function for comparing the contents of two
 *      uint32 pointers a and b, suitable for use by qsort
 *      to order an array of file numbers.
 *
 * Results:
 *      The contents of 'a' minus the contents of 'b'.
 *
 * Side effects:
 *      None.
 */

static int
FileNumberCompare(const void *a,  // IN:
                  const void *b)  // IN:
{
   return *(uint32 *) a - *(uint32 *) b;
}


/*
 *----------------------------------------------------------------------
 *
 * FileRotateByRenumber --
 *
 *      File rotation scheme optimized for vmfs:
 *        1) Find highest numbered file (maxNr)
 *        2) Rename <base>.<ext> to <base>-<maxNr + 1>.<ext>
 *           Should maxNr hit MAX_UINT32, file names are "fixed up".
 *        3) Delete (nFound - numToKeep) lowest numbered files.
 *
 * Results:
 *      If newFilePath is non-NULL: the new path is returned to *newFilePath
 *      if the rotation succeeded, otherwise NULL is returned in *newFilePath.
 *      The caller is responsible for freeing the string returned in
 *      *newFilePath.
 *
 * Side effects:
 *      Files renamed / deleted.
 *
 *----------------------------------------------------------------------
 */

static void
FileRotateByRenumber(const char *filePath,       // IN: full path to file
                     const char *filePathNoExt,  // IN: filename w/o extension.
                     const char *ext,            // IN: extension
                     int n,                      // IN: number old files to keep
                     char **newFilePath)         // OUT/OPT: new path to file
{
   uint32 i;
   char *tmp;
   int result;
   int nrFiles;
   size_t extLen;
   size_t baseNameLen;
   uint32 maxNr = 0;
   uint32 nFound = 0;
   char *baseDir = NULL;
   char *baseName = NULL;
   char **fileList = NULL;
   char *fullPathNoExt = NULL;
   uint32 *fileNumbers = NULL;

   if (newFilePath != NULL) {
      *newFilePath = NULL;
   }

   fullPathNoExt = File_FullPath(filePathNoExt);
   if (fullPathNoExt == NULL) {
      Log(LGPFX" %s: failed to get full path for '%s'.\n", __FUNCTION__,
          filePathNoExt);
      goto cleanup;
   }

   File_GetPathName(fullPathNoExt, &baseDir, &baseName);

   if ((baseDir == NULL) || (*baseDir == '\0')) {
      free(baseDir);
      baseDir = Unicode_Duplicate(DIRSEPS);
   }

   if ((baseName == NULL) || (*baseName == '\0')) {
      Log(LGPFX" %s: failed to get base name for path '%s'.\n", __FUNCTION__,
          filePathNoExt);
      goto cleanup;
   }

   baseNameLen = strlen(baseName);

   nrFiles = File_ListDirectory(baseDir, &fileList);
   if (nrFiles == -1) {
      Log(LGPFX" %s: failed to read the directory '%s'.\n", __FUNCTION__,
          baseDir);
      goto cleanup;
   }

   fileNumbers = Util_SafeCalloc(nrFiles, sizeof(uint32));

   /*
    * Make sure the whole file name precisely matches what we expect before
    * including in the list to be considered.
    */

   extLen = strlen(ext);

   for (i = 0; i < nrFiles; i++) {
      size_t fileNameLen = strlen(fileList[i]);

      if ((fileNameLen >= (baseNameLen + 1 /* dash */ + 1 /* digit */ + extLen)) &&
          (memcmp(fileList[i], baseName, baseNameLen) == 0) &&
          (fileList[i][baseNameLen] == '-') &&
          (memcmp(fileList[i] + fileNameLen - extLen, ext, extLen) == 0)) {
         const char *nr = fileList[i] + baseNameLen + 1;

         /* No leading zeros; zero is invalid; must be a valid ASCII digit */
         if ((nr[0] >= '1') && (nr[0] <= '9')) {
            uint32 curNr;
            char *endNr = NULL;

            errno = 0;
            curNr = strtoul(nr, &endNr, 10);
            if ((errno == 0) &&
                (endNr == fileList[i] + fileNameLen - extLen) &&
                (curNr <= MAX_UINT32)) {
               fileNumbers[nFound++] = curNr;
            }
         }
      }

      Posix_Free(fileList[i]);
   }

   if (nFound > 0) {
      qsort(fileNumbers, nFound, sizeof(uint32), FileNumberCompare);
      maxNr = fileNumbers[nFound - 1];

      /*
       * If the maximum file number maxes out the uint32 used, rename all of the
       * files, packing them down to the beginning of the rotation sequence.
       *
       * After MAX_UINT32 file rotations we can afford some extra time and I/O
       * operations to handle the wrapping case nicely.
       */

      if (maxNr == MAX_UINT32) {
         for (i = 0; i < nFound; i++) {
            char *to = Str_SafeAsprintf(NULL, "%s/%s-%u%s", baseDir, baseName,
                             i + 1, ext);
            char *from = Str_SafeAsprintf(NULL, "%s/%s-%u%s", baseDir, baseName,
                             fileNumbers[i], ext);

            result = File_Rename(from, to);

            if (result != 0) {
               Log(LGPFX" %s: rename of %s -> %s failed: %s\n", __FUNCTION__,
                   from, to, Err_Errno2String(Err_Errno()));
            }

            free(to);
            free(from);

            fileNumbers[i] = i + 1;
         }

         maxNr = nFound;
      }
   }

   /* Rename the existing file to the next number */
   tmp = Str_SafeAsprintf(NULL, "%s/%s-%u%s", baseDir, baseName, maxNr + 1, ext);

   result = File_Rename(filePath, tmp);

   if ((result != 0) && (result != ENOENT)) {
      Log(LGPFX" %s: rename of %s -> %s failed: %s\n", __FUNCTION__,
          filePath, tmp, Err_Errno2String(Err_Errno()));
   }

   if (newFilePath == NULL || result != 0) {
      Posix_Free(tmp);
   } else {
      *newFilePath = tmp;
   }

   if (nFound >= n) {
      /* Delete the extra files. */
      for (i = 0; i <= nFound - n; i++) {
         tmp = Str_SafeAsprintf(NULL, "%s/%s-%u%s", baseDir, baseName,
                                fileNumbers[i], ext);

         result = FileDeletion(tmp, FALSE);  // Don't follow a symlink!

         if (result != 0) {
            Log(LGPFX" %s: failed to remove %s: %s\n", __FUNCTION__,
                tmp, Err_Errno2String(Err_Errno()));
         }

         Posix_Free(tmp);
      }
   }

  cleanup:
   Posix_Free(fileNumbers);
   Posix_Free(fileList);
   Posix_Free(baseDir);
   Posix_Free(baseName);
   Posix_Free(fullPathNoExt);
}


/*
 *----------------------------------------------------------------------
 *
 * File_Rotate --
 *
 *      Rotate old files. The 'noRename' option is useful for filesystems
 *      where rename is hideously expensive (*cough* vmfs).
 *
 * Results:
 *      If newFileName is non-NULL: the new path is returned to
 *      *newFileName if the rotation succeeded, otherwise NULL
 *      is returned in *newFileName.  The caller is responsible
 *      for freeing the string returned in *newFileName.
 *
 * Side effects:
 *      Files are renamed / deleted.
 *
 *----------------------------------------------------------------------
 */

void
File_Rotate(const char *fileName,  // IN: original file
            int n,                 // IN: number of backup files
            Bool noRename,         // IN: don't rename all files
            char **newFileName)    // OUT/OPT: new path to file
{
   const char *ext;
   size_t baseLen;
   char *baseName;

   ASSERT(fileName != NULL);

   if ((ext = Str_Strrchr(fileName, '.')) == NULL) {
      ext = fileName + strlen(fileName);
   }
   baseLen = ext - fileName;

   /*
    * Backup base of file name.
    *
    * Since the Str_Asprintf(...) doesn't like format of %.*s and crashes
    * in Windows 2000. (Daniel Liu)
    */

   baseName = Util_SafeStrdup(fileName);
   baseName[baseLen] = '\0';

   if (noRename) {
      FileRotateByRenumber(fileName, baseName, ext, n, newFileName);
   } else {
      FileRotateByRename(fileName, baseName, ext, n, newFileName);
   }

   Posix_Free(baseName);
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_GetFSMountInfo --
 *
 *      Platform-independent wrapper around File_GetVMFSMountInfo
 *
 * Results:
 *      On failure return -1.  Otherwise, return fsType, version,
 *      remoteIP, remoteMountPoint, and localMountPoint.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
File_GetFSMountInfo(const char *pathName,
                    char **fsType,
                    uint32 *version,
                    char **remoteIP,
                    char **remoteMountPoint,
                    char **localMountPoint)
{
#if defined VMX86_SERVER
   return  File_GetVMFSMountInfo(pathName, fsType, version,
                                 remoteIP, remoteMountPoint,
                                 localMountPoint);
#else
   return -1;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * File_ContainSymLink --
 *
 *      Check if the specified file path contains symbolic link.
 *
 * Results:
 *      TRUE   pathName contains a symlink,
 *      FALSE  pathName is not a symlink nor contains a symlink, or error.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_ContainSymLink(const char *pathName)  // IN:
{
   char *path = NULL;
   char *base = NULL;
   Bool retValue = FALSE;

   if (File_IsSymLink(pathName)) {
      return TRUE;
   }

   File_GetPathName(pathName, &path, &base);

   if ((path != NULL) &&
       (base != NULL) &&
       (strcmp(path, "") != 0) &&
       (strcmp(base, "") != 0)) {
      if (File_ContainSymLink(path)) {
         retValue = TRUE;
      }
   }

   Posix_Free(path);
   Posix_Free(base);

   return retValue;
}


/*
 *----------------------------------------------------------------------------
 *
 * File_IsSubPathOf --
 *
 *    Check if the argument path is a sub path for argument base.
 *    The argument path will be converted to canonical path which doesn't
 *    contain ".." and then check if this canonical path is a sub path for
 *    argument base.
 *    So, this function can correctly recognize that a path like
 *    "/tmp/dir1/dir2/../../../bin/" is not a sub path for "/tmp/".
 *
 * Results:
 *    True if the argument path is a sub path for argument base.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
File_IsSubPathOf(const char *base, // IN: the base path to test against.
                 const char *path) // IN: the possible subpath to test.
{
   char *fullBase = File_FullPath(base);
   char *fullPath = File_FullPath(path);
   Bool isSubPath = TRUE;

   ASSERT(fullBase != NULL);
   ASSERT(fullPath != NULL);

   if (fullPath == NULL ||
       fullBase == NULL ||
       strncmp(fullPath, fullBase, strlen(fullBase)) != 0) {
      isSubPath = FALSE;
   }

   free(fullBase);
   free(fullPath);

   return isSubPath;
}


#if !defined(VMX86_SERVER)

/*
 *---------------------------------------------------------------------------
 *
 * File_DoesVolumeSupportConvertBlocks --
 *
 *     Does the volume support the new convert block allocation
 *     IOCTL? (Always FALSE for now on non-VMFS.)
 *
 * Results:
 *     TRUE   Yes
 *     FALSE  No
 *
 * Side effects:
 *     None
 *
 *---------------------------------------------------------------------------
 */

Bool
File_DoesVolumeSupportConvertBlocks(const char *pathName)  // IN:
{
   UNUSED_VARIABLE(pathName);
   return FALSE;
}

#endif
  07070100000047000081A4000000000000000000000001682255050000791D000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/file/fileIO.c  /*********************************************************
 * Copyright (C) 1998-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fileIO.c --
 *
 *    Basic (non internationalized) implementation of error messages for the
 *    Files library.
 *
 *    File locking/unlocking routines.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include "vmware.h"
#include "util.h"
#include "fileIO.h"
#include "fileLock.h"
#include "fileInt.h"
#include "msg.h"
#include "unicodeOperations.h"
#include "hostType.h"
#if defined(_WIN32)
#include <io.h>
#else
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#if defined(VMX86_SERVER)
#include "config.h"
#include "fs_public.h"
#endif


/*
 *----------------------------------------------------------------------
 *
 * FileIO_ErrorEnglish --
 *
 *      Return the message associated with a status code
 *
 * Results:
 *      The message
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

const char *
FileIO_ErrorEnglish(FileIOResult status)  // IN:
{
   return Msg_StripMSGID(FileIO_MsgError(status));
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_MsgError --
 *
 *      Return the message associated with a status code
 *
 * Results:
 *      The message.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

const char *
FileIO_MsgError(FileIOResult status)  // IN:
{
   const char *result = NULL;

   switch (status) {
   case FILEIO_SUCCESS:
      /*
       * Most of the time, you don't call this function with this value
       * because there is no error
       */
      result = MSGID(fileio.success) "Success";
      break;

   case FILEIO_CANCELLED:
      /*
       * Most of the time, you don't call this function with this value
       * because you don't want to display error messages after a user has
       * cancelled an operation.
       */
      result = MSGID(fileio.cancel) "The operation was cancelled by the user";
      break;

   case FILEIO_ERROR:
      /*
       * Most of the time, you don't call this function with this value
       * because you can call your native function to retrieve a more
       * accurate message.
       */
      result = MSGID(fileio.generic) "Error";
      break;

   case FILEIO_OPEN_ERROR_EXIST:
      result = MSGID(fileio.exists) "The file already exists";
      break;

   case FILEIO_LOCK_FAILED:
      result = MSGID(fileio.lock) "Failed to lock the file";
      break;

   case FILEIO_READ_ERROR_EOF:
      result = MSGID(fileio.eof) "Tried to read beyond the end of the file";
      break;

   case FILEIO_FILE_NOT_FOUND:
      result = MSGID(fileio.notfound) "Could not find the file";
      break;

   case FILEIO_NO_PERMISSION:
      result = MSGID(fileio.noPerm) "Insufficient permission to access the file";
      break;

   case FILEIO_FILE_NAME_TOO_LONG:
      result = MSGID(fileio.namelong) "The file name is too long";
      break;

   case FILEIO_WRITE_ERROR_FBIG:
      result = MSGID(fileio.fBig) "The file is too large";
      break;

   case FILEIO_WRITE_ERROR_NOSPC:
      result = MSGID(fileio.noSpc) "There is no space left on the device";
      break;

   case FILEIO_WRITE_ERROR_DQUOT:
      result = MSGID(fileio.dQuot) "There is no space left on the device";
      break;

   case FILEIO_ERROR_LAST:
      NOT_IMPLEMENTED();
      break;

      /*
       * We do not provide a default case on purpose, so that the compiler can
       * detect changes in the error set and reminds us to implement the
       * associated messages --hpreg
       */
   }

   if (!result) {
      Warning("%s: bad code %d\n", __FUNCTION__, status);
      ASSERT(0);
      result = MSGID(fileio.unknown) "Unknown error";
   }

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Init --
 *
 *      Initialize invalid FileIODescriptor.  Expects that caller
 *	prepared structure with FileIO_Invalidate.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

void
FileIO_Init(FileIODescriptor *fd,  // IN/OUT:
            const char *pathName)  // IN:
{
   ASSERT(fd != NULL);
   ASSERT(pathName != NULL);

   fd->fileName = Unicode_Duplicate(pathName);
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Cleanup --
 *
 *      Undo resource allocation done by FileIO_Init.  You do not want to
 *	call this function directly, you most probably want FileIO_Close.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

void
FileIO_Cleanup(FileIODescriptor *fd)  // IN/OUT:
{
   ASSERT(fd != NULL);

   if (fd->fileName) {
      Posix_Free(fd->fileName);
      fd->fileName = NULL;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * FileIOResolveLockBits --
 *
 *      Resolve the multitude of lock bits from historical public names
 *      to newer internal names.
 *
 *      Input flags: FILEIO_OPEN_LOCKED a.k.a. FILEIO_OPEN_LOCK_BEST,
 *                   FILEIO_OPEN_EXCLUSIVE_LOCK
 *      Output flags: FILEIO_OPEN_LOCK_MANDATORY, FILEIO_OPEN_LOCK_ADVISORY
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Only output flags are set in *access.
 *
 *----------------------------------------------------------------------
 */

void
FileIOResolveLockBits(int *access)  // IN/OUT: FILEIO_OPEN_* bits
{
   /*
    * Lock types:
    *    none: no locking at all
    *    advisory: open() ignores lock, FileIO_ respects lock.
    *    mandatory: open() and FileIO_ respect lock.
    *    "best": downgrades to advisory or mandatory based on OS support
    */
   if ((*access & FILEIO_OPEN_EXCLUSIVE_LOCK) != 0) {
      *access &= ~FILEIO_OPEN_EXCLUSIVE_LOCK;
      *access |= FILEIO_OPEN_LOCK_MANDATORY;
   }
   if ((*access & FILEIO_OPEN_LOCK_BEST) != 0) {
      /* "Best effort" bit: mandatory if OS supports, advisory otherwise */
      *access &= ~FILEIO_OPEN_LOCK_BEST;
      if (HostType_OSIsVMK()) {
         *access |= FILEIO_OPEN_LOCK_MANDATORY;
      } else {
         *access |= FILEIO_OPEN_LOCK_ADVISORY;
      }
   }

   /* Only one lock type (or none at all) allowed */
   ASSERT(((*access & FILEIO_OPEN_LOCK_ADVISORY) == 0) ||
          ((*access & FILEIO_OPEN_LOCK_MANDATORY) == 0));
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Lock --
 *
 *      Call the FileLock module to lock the given file.
 *
 * Results:
 *      FILEIO_ERROR               A serious error occured.
 *      FILEIO_SUCCESS             All is well
 *      FILEIO_LOCK_FAILED         Requested lock on file was not acquired
 *      FILEIO_FILE_NOT_FOUND      Unable to find the specified file
 *      FILEIO_NO_PERMISSION       Permissions issues
 *      FILEIO_FILE_NAME_TOO_LONG  The path name is too long
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Lock(FileIODescriptor *file,  // IN/OUT:
            int access)              // IN:
{
   FileIOResult ret = FILEIO_SUCCESS;

   /*
    * Lock the file if necessary.
    */

   ASSERT(file != NULL);
   ASSERT(file->lockToken == NULL);

   FileIOResolveLockBits(&access);
   ASSERT((access & FILEIO_OPEN_LOCKED) == 0);

#if !defined(__FreeBSD__) && !defined(sun)
   if ((access & FILEIO_OPEN_LOCK_MANDATORY) != 0) {
      /* Mandatory file locks are available only when opening a file */
      ret = FILEIO_LOCK_FAILED;
   } else if ((access & FILEIO_OPEN_LOCK_ADVISORY) != 0) {
      int err = 0;

      file->lockToken = FileLock_Lock(file->fileName,
                                      (access & FILEIO_OPEN_ACCESS_WRITE) == 0,
                                      FILELOCK_DEFAULT_WAIT,
                                      &err,
                                      NULL);

      if (file->lockToken == NULL) {
         /* Describe the lock not acquired situation in detail */
         Warning(LGPFX" %s on '%s' failed: %s\n",
                 __FUNCTION__, file->fileName,
                 (err == 0) ? "Lock timed out" : Err_Errno2String(err));

         /* Return a serious failure status if the locking code did */
         switch (err) {
         case 0:             // File is currently locked
         case EROFS:         // Attempt to lock for write on RO FS
            ret = FILEIO_LOCK_FAILED;
            break;
         case ENAMETOOLONG:  // Path is too long
            ret = FILEIO_FILE_NAME_TOO_LONG;
            break;
         case ENOENT:        // No such file or directory
            ret = FILEIO_FILE_NOT_FOUND;
            break;
         case EACCES:       // Permissions issues
            ret = FILEIO_NO_PERMISSION;
            break;
         default:            // Some sort of locking error
            ret = FILEIO_ERROR;
         }
      }
   }
#endif // !__FreeBSD__ && !sun

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_UnLock --
 *
 *      Call the FileLock module to unlock the given file.
 *
 * Results:
 *      FILEIO_SUCCESS  All is well
 *      FILEIO_ERROR    A serious error occured.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Unlock(FileIODescriptor *file)  // IN/OUT:
{
   FileIOResult ret = FILEIO_SUCCESS;

   ASSERT(file != NULL);

#if !defined(__FreeBSD__) && !defined(sun)
   if (file->lockToken != NULL) {
      int err = 0;

      if (!FileLock_Unlock(file->lockToken, &err, NULL)) {
         Warning(LGPFX" %s on '%s' failed: %s\n",
                 __FUNCTION__, file->fileName, Err_Errno2String(err));

         ret = FILEIO_ERROR;
      }

      file->lockToken = NULL;
   }
#else
   ASSERT(file->lockToken == NULL);
#endif // !__FreeBSD__ && !sun

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_GetSize --
 *
 *      Get size of file.
 *
 * Results:
 *      Size of file or -1.
 *
 * Side effects:
 *      errno is set on error.
 *
 *----------------------------------------------------------------------
 */

int64
FileIO_GetSize(const FileIODescriptor *fd)  // IN:
{
   int64 logicalBytes;

   return (FileIO_GetAllocSize(fd, &logicalBytes, NULL) != FILEIO_SUCCESS) ?
      -1 : logicalBytes;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_GetSizeByPath --
 *
 *      Get size of a file specified by path.
 *
 * Results:
 *      Size of file or -1.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int64
FileIO_GetSizeByPath(const char *pathName)  // IN:
{
   int64 logicalBytes;

   return (FileIO_GetAllocSizeByPath(pathName, &logicalBytes, NULL) !=
      FILEIO_SUCCESS) ? -1 : logicalBytes;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Filename --
 *
 *      Returns the filename that was used to open a FileIODescriptor
 *
 * Results:
 *      Filename. You DON'T own the memory - use Unicode_Duplicate if
 *      you want to keep it for yourself. In particular, if the file
 *      gets closed the string will almost certainly become invalid.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

const char *
FileIO_Filename(FileIODescriptor *fd)  // IN:
{
   ASSERT(fd != NULL);

   return fd->fileName;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_CloseAndUnlink
 *
 *      Closes and unlinks the file associated with a FileIODescriptor.
 *
 * Results:
 *      FILEIO_SUCCESS: The file was closed and unlinked. The FileIODescriptor
 *                      is no longer valid.
 *      FILEIO_ERROR: An error occurred.
 *
 * Side effects:
 *      File is probably closed and unlinked.
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_CloseAndUnlink(FileIODescriptor *fd)  // IN:
{
   char *path;
   FileIOResult ret;

   ASSERT(fd != NULL);
   ASSERT(FileIO_IsValid(fd));

   path = Unicode_Duplicate(fd->fileName);

   ret = FileIO_Close(fd);
   if ((File_UnlinkIfExists(path) != 0) && FileIO_IsSuccess(ret)) {
      ret = FILEIO_ERROR;
   }

   Posix_Free(path);

   return ret;
}


#if defined(_WIN32) || defined(__linux__) || defined(__APPLE__) || \
    defined(__FreeBSD__) || defined(sun)
/*
 *----------------------------------------------------------------------
 *
 * FileIO_Pread --
 *
 *      Reads from a file starting at a specified offset.
 *
 *      Note: This function may update the file pointer so you will need to
 *      call FileIO_Seek before calling FileIO_Read/Write afterwards.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Pread(FileIODescriptor *fd,  // IN: File descriptor
             void *buf,             // IN: Buffer to read into
             size_t len,            // IN: Length of the buffer
             uint64 offset)         // IN: Offset to start reading
{
   struct iovec iov;

   ASSERT(fd != NULL);

   iov.iov_base = buf;
   iov.iov_len = len;

   return FileIO_Preadv(fd, &iov, 1, offset, len, NULL);
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Pwrite --
 *
 *      Writes to a file starting at a specified offset.
 *
 *      Note: This function may update the file pointer so you will need to
 *      call FileIO_Seek before calling FileIO_Read/Write afterwards.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Pwrite(FileIODescriptor *fd,  // IN: File descriptor
              void const *buf,       // IN: Buffer to write from
              size_t len,            // IN: Length of the buffer
              uint64 offset)         // IN: Offset to start writing
{
   struct iovec iov;

   ASSERT(fd != NULL);

   /* The cast is safe because FileIO_Pwritev() will not write to '*buf'. */
   iov.iov_base = (void *)buf;
   iov.iov_len = len;

   return FileIO_Pwritev(fd, &iov, 1, offset, len, NULL);
}
#endif


#if defined(sun) && __GNUC__ < 3
/*
 *-----------------------------------------------------------------------------
 *
 * FileIO_IsSuccess --
 *
 *      XXX: See comment in fileIO.h.  For reasonable compilers, this
 *      function is implemented as "static inline" in fileIO.h; for
 *      unreasonable compilers, it can't be static so we implement it here.
 *
 * Results:
 *      TRUE if the input indicates success.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
FileIO_IsSuccess(FileIOResult res)  // IN:
{
   return res == FILEIO_SUCCESS;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * FileIO_AtomicTempPath
 *
 *      Return a temp path name in the same directory as the argument path.
 *      The path is the full path of the source file with a '~' appended.
 *      The caller must free the path when done.
 *
 * Results:
 *      UTF8 path if successful, NULL on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
FileIO_AtomicTempPath(const char *path)  // IN:
{
   char *srcPath;
   char *retPath;

   srcPath = File_FullPath(path);
   if (srcPath == NULL) {
      Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__, path);
      return NULL;
   }
   retPath = Unicode_Join(srcPath, "~", NULL);
   Posix_Free(srcPath);

   return retPath;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileIO_AtomicTempFile
 *
 *      Create a temp file in the same directory as the argument file.
 *      On non-Windows attempts to create the temp file with the same
 *      permissions and owner/group as the argument file.
 *
 * Results:
 *      FileIOResult of call that failed or FILEIO_SUCCESS
 *
 * Side effects:
 *      Creates a new file.
 *
 *-----------------------------------------------------------------------------
 */

FileIOResult
FileIO_AtomicTempFile(FileIODescriptor *fileFD,  // IN:
                      FileIODescriptor *tempFD)  // OUT:
{
   char *tempPath;
   int permissions;
   FileIOResult status;
#if !defined(_WIN32)
   int ret;
   struct stat stbuf;
#endif

   ASSERT(FileIO_IsValid(fileFD));
   ASSERT(tempFD && !FileIO_IsValid(tempFD));

   tempPath = FileIO_AtomicTempPath(FileIO_Filename(fileFD));
   if (tempPath == NULL) {
      status = FILEIO_ERROR;
      goto bail;
   }

#if defined(_WIN32)
   permissions = 0;
   File_UnlinkIfExists(tempPath);
#else
   if (fstat(fileFD->posix, &stbuf)) {
      Log("%s: Failed to fstat '%s', errno: %d.\n", __FUNCTION__,
          FileIO_Filename(fileFD), errno);
      status = FILEIO_ERROR;
      goto bail;
   }
   permissions = stbuf.st_mode;

   /* Clean up a previously created temp file; if one exists. */
   ret = Posix_Unlink(tempPath);
   if (ret != 0 && errno != ENOENT) {
      Log("%s: Failed to unlink temporary file, errno: %d\n",
          __FUNCTION__, errno);
      /* Fall through; FileIO_Create will report the actual error. */
   }
#endif

   status = FileIO_Create(tempFD, tempPath,
                          FILEIO_ACCESS_READ | FILEIO_ACCESS_WRITE,
                          FILEIO_OPEN_CREATE_SAFE, permissions);
   if (!FileIO_IsSuccess(status)) {
      Log("%s: Failed to create temporary file, %s (%d). errno: %d\n",
          __FUNCTION__, FileIO_ErrorEnglish(status), status, Err_Errno());
      goto bail;
   }

#if !defined(_WIN32)
   /*
    * On ESX we always use the vmkernel atomic file swap primitive, so
    * there's no need to set the permissions and owner of the temp file.
    *
    * XXX this comment is not true for NFS on ESX -- we use rename rather
    * than "vmkernel atomic file swap primitive" -- but we do not care
    * because files are always owned by root.  Sigh.  Bug 839283.
    */

   if (!HostType_OSIsVMK()) {
      if (fchmod(tempFD->posix, stbuf.st_mode)) {
         Log("%s: Failed to chmod temporary file, errno: %d\n",
             __FUNCTION__, errno);
         status = FILEIO_ERROR;
         goto bail;
      }
      if (fchown(tempFD->posix, stbuf.st_uid, stbuf.st_gid)) {
         Log("%s: Failed to chown temporary file, errno: %d\n",
             __FUNCTION__, errno);
         status = FILEIO_ERROR;
         goto bail;
      }
   }
#endif

   Posix_Free(tempPath);
   return FILEIO_SUCCESS;

bail:
   ASSERT(!FileIO_IsSuccess(status));
   if (FileIO_IsValid(tempFD)) {
      FileIO_Close(tempFD);
#if defined(_WIN32)
      File_UnlinkIfExists(tempPath);
#else
      ret = Posix_Unlink(tempPath);
      if (ret != 0) {
         Log("%s: Failed to clean up temporary file, errno: %d\n",
             __FUNCTION__, errno);
      }
      ASSERT(ret == 0);
#endif
   }
   Posix_Free(tempPath);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileIO_AtomicUpdateEx --
 *
 *      On ESX when the target files reside on vmfs, exchanges the contents
 *      of two files using code modeled from VmkfsLib_SwapFiles.  Both "curr"
 *      and "new" are left open.
 *
 *      On hosted products, uses rename to swap files, so "new" becomes "curr",
 *      and path to "new" no longer exists on success.
 *
 *      On ESX on NFS:
 *
 *      If renameOnNFS is TRUE, use rename, like on hosted.
 *
 *      If renameOnNFS is FALSE, returns -1 rather than trying to use
 *      rename, to avoid various bugs in the vmkernel client... (PR
 *      839283, PR 1671787, etc).
 *
 *      On success the caller must call FileIO_IsValid on newFD to verify it
 *      is still open before using it again.
 *
 * Results:
 *      1 if successful, 0 on failure, -1 if not supported on this filesystem.
 *      errno is preserved.
 *
 * Side effects:
 *      Disk I/O.
 *
 *-----------------------------------------------------------------------------
 */

int
FileIO_AtomicUpdateEx(FileIODescriptor *newFD,   // IN/OUT: file IO descriptor
                      FileIODescriptor *currFD,  // IN/OUT: file IO descriptor
                      Bool renameOnNFS)          // IN: fall back to rename on NFS
{
   char *currPath = NULL;
   char *newPath = NULL;
#if defined(_WIN32)
   uint32 currAccess;
   uint32 newAccess;
   FileIOResult status;
   FileIODescriptor tmpFD;
#endif
   int savedErrno = 0;
   int ret = 0;

   ASSERT(FileIO_IsValid(newFD));
   ASSERT(FileIO_IsValid(currFD));

   if (HostType_OSIsVMK()) {
#if defined(VMX86_SERVER)
      FS_SwapFilesArgsUW args = { 0 };
      char *dirName = NULL;
      char *fileName = NULL;
      char *dstDirName = NULL;
      char *dstFileName = NULL;
      Bool isSame;

      currPath = File_FullPath(FileIO_Filename(currFD));
      if (currPath == NULL) {
         savedErrno = errno;
         Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__,
             FileIO_Filename(currFD));
         goto swapdone;
      }

      newPath = File_FullPath(FileIO_Filename(newFD));
      if (newPath == NULL) {
         savedErrno = errno;
         Log("%s: File_FullPath of '%s' failed.\n", __FUNCTION__,
             FileIO_Filename(newFD));
         goto swapdone;
      }

      File_GetPathName(newPath, &dirName, &fileName);
      File_GetPathName(currPath, &dstDirName, &dstFileName);

      ASSERT(dirName != NULL);
      ASSERT(fileName != NULL && *fileName != '\0');
      ASSERT(dstDirName != NULL);
      ASSERT(dstFileName != NULL && *dstFileName != '\0');

      errno = 0;
      isSame = File_IsSameFile(dirName, dstDirName);
      if (errno == 0) {
         ASSERT(isSame);
      } else {
         savedErrno = errno;
         Log("%s: File_IsSameFile of ('%s', '%s') failed: %d\n", __FUNCTION__,
             dirName, dstDirName, errno);
         goto swapdone;
      }

      args.fd = currFD->posix;
      if (ioctl(newFD->posix, IOCTLCMD_VMFS_SWAP_FILES, &args) != 0) {
         savedErrno = errno;
         if (errno != ENOSYS && errno != ENOTTY) {
            Log("%s: ioctl failed %d.\n", __FUNCTION__, errno);
            ASSERT(errno != EBUSY);   /* #615124. */
         }
      } else {
         ret = 1;
      }

      /*
       * Did we fail because we are on a file system that does not
       * support the IOCTLCMD_VMFS_SWAP_FILES ioctl? If so fallback to
       * using rename.
       *
       * Check for both ENOSYS and ENOTTY. PR 957695
       */
      if (savedErrno == ENOSYS || savedErrno == ENOTTY) {
         if (renameOnNFS) {
            int fd;

            /*
             * NFS allows renames of locked files, even if both files
             * are locked.  The file lock follows the file handle, not
             * the name, so after the rename we can swap the underlying
             * file descriptors instead of closing and reopening the
             * target file.
             *
             * This is different than the hosted path below because
             * ESX uses native file locks and hosted does not.
             *
             * We assume that all ESX file systems that support rename
             * have the same file lock semantics as NFS.
             */

            if (File_Rename(newPath, currPath)) {
               Log("%s: rename of '%s' to '%s' failed %d.\n",
                   __FUNCTION__, newPath, currPath, errno);
               savedErrno = errno;
               goto swapdone;
            }
            ret = 1;
            fd = newFD->posix;
            newFD->posix = currFD->posix;
            currFD->posix = fd;
            FileIO_Close(newFD);
         } else {
            ret = -1;
         }
      }

swapdone:
      Posix_Free(dirName);
      Posix_Free(fileName);
      Posix_Free(dstDirName);
      Posix_Free(dstFileName);
      Posix_Free(currPath);
      Posix_Free(newPath);

      errno = savedErrno;
      return ret;
#else
      NOT_REACHED();
#endif
   }
#if defined(_WIN32)
   currPath = Unicode_Duplicate(FileIO_Filename(currFD));
   newPath = Unicode_Duplicate(FileIO_Filename(newFD));

   newAccess = newFD->flags;
   currAccess = currFD->flags;

   FileIO_Close(newFD);

   /*
    * The current file needs to be closed and reopened,
    * but we don't want to drop the file lock by calling
    * FileIO_Close() on it.  Instead, use native close primitives.
    * We'll reopen it later with FileIO_Open.  Set the
    * descriptor/handle to an invalid value while we're in the
    * middle of transferring ownership.
    */

   CloseHandle(currFD->win32);
   currFD->win32 = INVALID_HANDLE_VALUE;
   if (File_RenameRetry(newPath, currPath, 10) == 0) {
      ret = TRUE;
   } else {
      savedErrno = errno;
      ASSERT(!ret);
   }

   FileIO_Invalidate(&tmpFD);

   /*
    * Clear the locking bits from the requested access so that reopening
    * the file ignores the advisory lock.
    */

   ASSERT((currAccess & FILEIO_OPEN_LOCK_MANDATORY) == 0);
   currAccess &= ~(FILEIO_OPEN_LOCK_MANDATORY | FILEIO_OPEN_LOCK_ADVISORY |
                   FILEIO_OPEN_LOCK_BEST | FILEIO_OPEN_LOCKED);
   status = FileIO_Open(&tmpFD, currPath, currAccess, FILEIO_OPEN);
   if (!FileIO_IsSuccess(status)) {
      Panic("Failed to reopen dictionary after renaming "
            "\"%s\" to \"%s\": %s (%d)\n", newPath, currPath,
            FileIO_ErrorEnglish(status), status);
   }
   ASSERT(tmpFD.lockToken == NULL);

   currFD->win32 = tmpFD.win32;

   FileIO_Cleanup(&tmpFD);
   Posix_Free(currPath);
   Posix_Free(newPath);
   errno = savedErrno;

   return ret;
#else
   currPath = (char *)FileIO_Filename(currFD);
   newPath = (char *)FileIO_Filename(newFD);

   if (File_Rename(newPath, currPath)) {
      Log("%s: rename of '%s' to '%s' failed %d.\n",
          __FUNCTION__, newPath, currPath, errno);
          savedErrno = errno;
   } else {
      int fd;

      ret = TRUE;
      fd = newFD->posix;
      newFD->posix = currFD->posix;
      currFD->posix = fd;
      FileIO_Close(newFD);
   }

   errno = savedErrno;

   return ret;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileIO_AtomicUpdate --
 *
 *      Wrapper around FileIO_AtomicUpdateEx that defaults 'renameOnNFS' to
 *      TRUE.
 *
 * Results:
 *      TRUE if successful, FALSE otherwise.
 *
 * Side effects:
 *      See FileIO_AtomicUpdateEx.
 *
 *-----------------------------------------------------------------------------
 */

Bool
FileIO_AtomicUpdate(FileIODescriptor *newFD,   // IN/OUT: file IO descriptor
                    FileIODescriptor *currFD)  // IN/OUT: file IO descriptor
{
   return FileIO_AtomicUpdateEx(newFD, currFD, TRUE) == 1;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileIOGetChoiceOnConfig --
 *
 *      Get the choice based on the "answer.msg.%s.file.open" in config file.
 *      If it's not set, will return the default 0x20.
 *
 * Results:
 *      The first character of the value or 0x20 by default.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char
FileIOGetChoiceOnConfig(const char *device,   // IN: device name
                        const char *pathName) // IN: file path
{
   char choice = 0x20;

#if defined(VMX86_SERVER)
   char *answer = Config_GetString(NULL, "answer.msg.%s.file.open", device);

   if (answer == NULL) {
      Log("Appending %s output to %s. Use answer.msg.%s.file.open=\"replace\" "
          "to replace file instead.", device, pathName, device);
   } else {
      const char *opt = answer;

      while (*opt == '_') {
         opt++;
      }
      choice = *opt | 0x20; // tolower() for ascii
      free(answer);
   }
#endif
   return choice;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileIO_CreateDeviceFileNoPrompt --
 *
 *      If file does not exist, new file is created. If file does exist,
 *      configuration option answer.msg.<device>.file.open is consulted to
 *      decide whether to truncate file (replace), append data at the end of
 *      the file (append, the default), or fail open (cancel).
 *
 * Results:
 *      FileIOResult of call: FILEIO_SUCCESS or FILEIO_CANCELLED or other value.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

FileIOResult
FileIO_CreateDeviceFileNoPrompt(FileIODescriptor *fd,   // IN: file IO description
                                const char *pathName,   // IN: file path
                                int openMode,           // IN:
                                FileIOOpenAction action,// IN:
                                int perms,              // IN:
                                const char *device)     // IN:
{
   FileIOResult fret;
   char choice = '\0';
   int time = 0;

   ASSERT(fd != NULL);
   ASSERT(pathName != NULL);
   ASSERT(device != NULL);

   while ((fret = FileIO_Create(fd, pathName, openMode, action, perms))
           == FILEIO_OPEN_ERROR_EXIST) {
      if (choice == '\0') {
         choice = FileIOGetChoiceOnConfig(device, pathName);

         switch (choice) {
            case 'c':     // Cancel
               fret = FILEIO_CANCELLED;
               break;
            case 'r':     // Replace
            case 'o':     // Overwrite
               action = FILEIO_OPEN_CREATE_EMPTY;
               break;
            default:      // Append, nothing, empty string
               action = FILEIO_OPEN_CREATE;
               break;
         }
      }

      if (fret == FILEIO_CANCELLED || time++ == 3) {
         break;
      }
   }

   return fret;
}
   07070100000048000081A40000000000000000000000016822550500012B7C000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/file/fileIOPosix.c /*********************************************************
 * Copyright (C) 1998-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fileIOPosix.c --
 *
 *      Implementation of the file library host specific functions for linux.
 */


#if defined(__linux__)
#  if !defined(VMX86_TOOLS) && !defined(__ANDROID__)
#     define FILEIO_SUPPORT_ODIRECT
#     define _GNU_SOURCE
#  endif
#  include <features.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#if defined __ANDROID__
#include <asm/unistd.h> // for __NR_SYSCALL_BASE
#endif
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#if defined(__linux__)
#ifdef __ANDROID__
#   include <sys/syscall.h>
#else
#   include <syscall.h>
#endif
#endif
#include <sys/stat.h>
#include "su.h"

#if defined(__APPLE__)
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/kauth.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <CoreFoundation/CoreFoundation.h>
#include <dlfcn.h>
#include <sys/xattr.h>
#else
#if defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/mount.h>
#else
#include <sys/statfs.h>
#if !defined(__sun__)
#include <mntent.h>
#include <dlfcn.h>
#endif
#endif
#endif

/* Check for non-matching prototypes */
#include "vmware.h"
#include "str.h"
#include "err.h"
#include "posix.h"
#include "file.h"
#include "fileIO.h"
#include "fileInt.h"
#include "config.h"
#include "util.h"
#include "iovector.h"
#include "hostType.h"

#include "unicodeOperations.h"
#include "memaligned.h"
#include "userlock.h"

#include "hostinfo.h"

#if defined(__APPLE__)
#include <sys/sysctl.h>
#include <sys/fcntl.h>
#endif

#ifdef VMX86_SERVER
#include "fs_public.h"
#include <sys/ioctl.h>
#endif

/*
 * fallocate() is only supported since the glibc-2.8 and
 * linux kernel-2.6.23. Presently the glibc in our toolchain is 2.3.
 */
#if defined(__linux__)
   #if !defined(SYS_fallocate)
      #if defined(__i386__)
         #define SYS_fallocate 324
      #elif __x86_64__
         #define SYS_fallocate 285
      #elif __arm__
         #define SYS_fallocate (__NR_SYSCALL_BASE+352) // newer glibc value
      #endif
   #endif
   #if !defined(FALLOC_FL_KEEP_SIZE)
      #define FALLOC_FL_KEEP_SIZE 1
   #endif
#endif

static const unsigned int FileIO_SeekOrigins[] = {
   SEEK_SET,
   SEEK_CUR,
   SEEK_END,
};

static const int FileIO_OpenActions[] = {
   0,
   O_TRUNC,
   O_CREAT,
   O_CREAT | O_EXCL,
   O_CREAT | O_TRUNC,
};

#ifdef __APPLE__
static FileIOPrivilegedOpener *privilegedOpenerFunc = NULL;
#endif

/*
 * Options for FileCoalescing performance optimization
 */
typedef struct FilePosixOptions {
   Bool initialized;
   Bool aligned;
   Bool enabled;
   int countThreshold;
   int sizeThreshold;
   int aioNumThreads;
   ssize_t maxIOVec;
} FilePosixOptions;


static FilePosixOptions filePosixOptions;

/*
 * Data structures for FileIOAligned_* functions; only used on
 * hosted (see fileInt.h for rationale).
 */
#if !defined(VMX86_TOOLS) && !defined(VMX86_SERVER)
#define ALIGNEDPOOL_FREELIST_SIZE 30
#define ALIGNEDPOOL_BUFSZ         (1024 * 1024)
#define ALIGNEDPOOL_OLD_AGE       ((VmTimeType)1000 * 1000 * 1000) /* nanoseconds */

typedef struct AlignedPool {
   MXUserExclLock *lock;

   /*
    * list: Array of allocated buffers.
    *        0 .. numBusy-1 : busy buffers (in use by a caller).
    * numBusy .. numAlloc-1 : allocated but not busy.
    *    numAlloc .. SIZE-1 : unused.
    */
   void           *list[ALIGNEDPOOL_FREELIST_SIZE];

   /*
    * timestamp: Array of release timestamps.
    *        0 .. numBusy-1 : unused.
    * numBusy .. numAlloc-1 : last time we had N buffers outstanding.
    *    numAlloc .. SIZE-1 : unused.
    */
   VmTimeType      timestamp[ALIGNEDPOOL_FREELIST_SIZE];

   /* invariant: 0 <= numBusy <= numAlloc <= ALIGNEDPOOL_FREELIST_SIZE */
   unsigned        numAlloc;
   unsigned        numBusy;
} AlignedPool;

static Atomic_Ptr alignedPoolLockStorage;
static AlignedPool alignedPool;
#endif

/*
 * Although, support for preadv()/pwrite() first appeared in Linux 2.6.30,
 * library support was added in glibc 2.10. Hence these functions
 * are not available in any header file.
 */

#if defined(__linux__) && !defined(__ANDROID__)
   #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
      /*
       * We want preadv/pwritev. But due to FOB=64, the symbols are -64.
       * TODO: when the baseline bumps to XOPEN=700, link directly to
       * the symbols (and anyone building XOPEN<700 gets nothing).
       */
      extern ssize_t preadv64(int fd, const struct iovec *iov, int iovcnt,
                          __off64_t offset) __attribute__ ((weak));

      extern ssize_t pwritev64(int fd, const struct iovec *iov, int iovcnt,
                          __off64_t offset) __attribute__ ((weak));
   #else
      #error "Large file support is unavailable."
   #endif
#endif /* defined(__linux__) */

/*
 *-----------------------------------------------------------------------------
 *
 * FileIOErrno2Result --
 *
 *      Convert a POSIX errno to a FileIOResult code.
 *
 * Results:
 *      The FileIOResult corresponding to the errno, FILEIO_ERROR by default.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static FileIOResult
FileIOErrno2Result(int error)  // IN: errno to convert
{
   switch (error) {
   case EIO:
      return FILEIO_ERROR;
   case EEXIST:
      return FILEIO_OPEN_ERROR_EXIST;
   case ENOENT:
      return FILEIO_FILE_NOT_FOUND;
   case EACCES:
      return FILEIO_NO_PERMISSION;
   case ENAMETOOLONG:
      return FILEIO_FILE_NAME_TOO_LONG;
   case ENOSPC:
      return FILEIO_WRITE_ERROR_NOSPC;
   case EFBIG:
      return FILEIO_WRITE_ERROR_FBIG;
#if defined(VMX86_SERVER)
   case EBUSY:
      return FILEIO_LOCK_FAILED;
#endif
#if defined(EDQUOT)
   case EDQUOT:
      return FILEIO_WRITE_ERROR_DQUOT;
#endif
   default:
      return FILEIO_ERROR;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_OptionalSafeInitialize --
 *
 *      Initialize global state. If this module is called from a
 *      thread other than the VMX or VCPU threads, like an aioGeneric worker
 *      thread, then we cannot do things like call config. Do that sort
 *      of initialization here, which is called from a safe thread.
 *
 *      This routine is OPTIONAL if you do not call this module from a
 *      worker thread. The same initialization can be done lazily when
 *      a read/write routine is called.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

INLINE void
FileIO_OptionalSafeInitialize(void)
{
   if (!filePosixOptions.initialized) {
      filePosixOptions.enabled =
                            Config_GetBool(TRUE, "filePosix.coalesce.enable");

      /*
       * Aligned malloc starts failing to allocate memory during heavy I/O on
       * Linux. We're not sure why -- maybe we are running out of mmaps?
       * Turn it off by default for now.
       */

      filePosixOptions.aligned =
                           Config_GetBool(FALSE, "filePosix.coalesce.aligned");

      filePosixOptions.countThreshold =
                           Config_GetLong(5, "filePosix.coalesce.count");

      filePosixOptions.sizeThreshold =
                           Config_GetLong(16*1024, "filePosix.coalesce.size");

      filePosixOptions.aioNumThreads =
                           Config_GetLong(0, "aiomgr.numThreads");
#if defined(__linux__)
      filePosixOptions.maxIOVec = sysconf(_SC_IOV_MAX);

      /* Assume unlimited unless sysconf says otherwise. */
      if (filePosixOptions.maxIOVec < 0) {
         filePosixOptions.maxIOVec = MAX_INT32;
      }
#elif defined(__APPLE__)
      /*
       * There appears to be no way to determine the iovec size limit at
       * runtime.  If Apple ever changes this, we lose binary compatibility.
       * On the bright side, Apple has not changed this value for at least as
       * long as they've produced Intel Macs.
       */

      filePosixOptions.maxIOVec = 1024;
#else
      filePosixOptions.maxIOVec = MAX_INT32;
#endif

      filePosixOptions.initialized = TRUE;
      FileIOAligned_PoolInit();
   }
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Invalidate --
 *
 *      Initialize a FileIODescriptor with an invalid value
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

void
FileIO_Invalidate(FileIODescriptor *fd)  // OUT:
{
   ASSERT(fd != NULL);

   (memset)(fd, 0, sizeof *fd);
   fd->posix = -1;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_IsValid --
 *
 *      Check whether a FileIODescriptor is valid.
 *
 * Results:
 *      True if valid.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
FileIO_IsValid(const FileIODescriptor *fd)  // IN:
{
   ASSERT(fd != NULL);

   return fd->posix != -1;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_CreateFDPosix --
 *
 *      This function is for specific needs: for example, when you need
 *      to create a FileIODescriptor from an already open fd. Use only
 *      FileIO_* library functions on the FileIODescriptor from that point on.
 *
 *      Because FileIODescriptor struct is different on two platforms,
 *      this function is the only one in the file library that's
 *      platform-specific.
 *
 * Results:
 *      FileIODescriptor
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIODescriptor
FileIO_CreateFDPosix(int posix,  // IN: UNIX file descriptor
                     int flags)  // IN: UNIX access flags
{
   FileIODescriptor fd;

   FileIO_Invalidate(&fd);

   switch (flags & O_ACCMODE) {
   case O_RDWR:
      fd.flags |= (FILEIO_OPEN_ACCESS_READ | FILEIO_OPEN_ACCESS_WRITE);
      break;
   case O_WRONLY:
      fd.flags |= FILEIO_OPEN_ACCESS_WRITE;
      break;
   default:
      ASSERT(FALSE);
      /* FALLTHRU */
   case O_RDONLY:
      fd.flags |= FILEIO_OPEN_ACCESS_READ;
      break;
   }

#if defined(O_SYNC) // Not available in FreeBSD tools build
   if (flags & O_SYNC) {
      fd.flags |= FILEIO_OPEN_SYNC;
   }
#endif
   if (flags & O_APPEND) {
      fd.flags |= FILEIO_OPEN_APPEND;
   }

#if defined(__linux__) && defined(O_CLOEXEC)
   if (flags & O_CLOEXEC) {
      fd.flags |= FILEIO_OPEN_CLOSE_ON_EXEC;
   }
#endif

   fd.posix = posix;

   return fd;
}


#if defined(__APPLE__)
/*
 *----------------------------------------------------------------------
 *
 * FileIO_SetPrivilegedOpener --
 *
 *      Set the function to be used when opening files with privilege,
 *      overriding the default behavior. See FileIO_PrivilegedPosixOpen.
 *
 *      Setting the privileged opener to NULL will restore default
 *      behavior.
 *
 *      This function is not thread safe.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

void
FileIO_SetPrivilegedOpener(FileIOPrivilegedOpener *opener) // IN
{
   ASSERT(privilegedOpenerFunc == NULL || opener == NULL);
   privilegedOpenerFunc = opener;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * FileIOCreateRetry --
 *
 *      Open/create a file; specify creation mode
 *      May perform retries to deal with certain OS conditions
 *
 * Results:
 *      FILEIO_SUCCESS on success: 'file' is set
 *      FILEIO_OPEN_ERROR_EXIST if the file already exists
 *      FILEIO_FILE_NOT_FOUND if the file is not present
 *      FILEIO_ERROR for other errors
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIOCreateRetry(FileIODescriptor *file,   // OUT:
                  const char *pathName,     // IN:
                  int access,               // IN:
                  FileIOOpenAction action,  // IN:
                  int mode,                 // IN: mode_t for creation
                  uint32 maxWaitTimeMsec)   // IN: Ignored
{
   int fd = -1;
   int flags = 0;
   int error;
   FileIOResult ret;

   ASSERT(file != NULL);

   if (pathName == NULL) {
      errno = EFAULT;

      return FILEIO_ERROR;
   }

   ASSERT(!FileIO_IsValid(file));
   ASSERT(file->lockToken == NULL);
   ASSERT_ON_COMPILE(FILEIO_ERROR_LAST < 16); /* See comment in fileIO.h */

   FileIOResolveLockBits(&access);
   ASSERT((access & FILEIO_OPEN_LOCKED) == 0 &&
          (access & FILEIO_OPEN_EXCLUSIVE_LOCK) == 0);
   /* Only ESX implements mandatory locking */
   ASSERT((access & FILEIO_OPEN_LOCK_MANDATORY) == 0 ||
          File_SupportsMandatoryLock(pathName));

#if defined(__APPLE__)
   if (access & FILEIO_OPEN_EXCLUSIVE_LOCK_MACOS) {
      flags |= O_EXLOCK;
   }
#elif defined(__linux__)
   if (HostType_OSIsVMK()) {
      if ((access & FILEIO_OPEN_SWMR_LOCK) != 0) {
         flags |= O_SWMR_LOCK;
      } else if ((access & FILEIO_OPEN_MULTIWRITER_LOCK) != 0) {
         flags |= O_MULTIWRITER_LOCK;
      } else if ((access & FILEIO_OPEN_LOCK_MANDATORY) != 0) {
         flags |= O_EXCLUSIVE_LOCK;
      } else if ((access & FILEIO_OPEN_OPTIMISTIC_LOCK) != 0) {
         flags |= O_OPTIMISTIC_LOCK;
      }
   }
#endif

   /*
    * Locking implementation note: this can be recursive. On ESX:
    * FileIOCreateRetry("foo", ...ADVISORY...)
    *  -> FileIO_Lock("foo", ...ADVISORY...)
    *     -> FileLock_Lock("foo", ...ADVISORY...)
    *        -> FileIOCreateRetry("foo.lck", ...MANDATORY...)
    *           -> open("foo.lck", ...O_EXCLUSIVE_LOCK...)
    */

   FileIO_Init(file, pathName);
   /* Mandatory file locks are only available at open() itself */
   if ((access & FILEIO_OPEN_LOCK_ADVISORY) != 0) {
      ret = FileIO_Lock(file, access);
      if (!FileIO_IsSuccess(ret)) {
         goto error;
      }
   }

   if ((access & (FILEIO_OPEN_ACCESS_READ | FILEIO_OPEN_ACCESS_WRITE)) ==
       (FILEIO_OPEN_ACCESS_READ | FILEIO_OPEN_ACCESS_WRITE)) {
      flags |= O_RDWR;
   } else if (access & FILEIO_OPEN_ACCESS_WRITE) {
      flags |= O_WRONLY;
   } else if (access & FILEIO_OPEN_ACCESS_READ) {
      flags |= O_RDONLY;
   }

   if (access & FILEIO_OPEN_EXCLUSIVE_READ &&
       access & FILEIO_OPEN_EXCLUSIVE_WRITE) {
      flags |= O_EXCL;
   }

   if (access & FILEIO_OPEN_UNBUFFERED) {
#if defined(FILEIO_SUPPORT_ODIRECT)
      flags |= O_DIRECT;
#elif !defined(__APPLE__) // Mac hosts need this access flag after opening.
      access &= ~FILEIO_OPEN_UNBUFFERED;
      LOG_ONCE(LGPFX" %s reverting to buffered IO on %s.\n",
               __FUNCTION__, pathName);
#endif
   }

   if (access & FILEIO_OPEN_NONBLOCK) {
      flags |= O_NONBLOCK;
   }

   if (access & FILEIO_OPEN_APPEND) {
      flags |= O_APPEND;
   }

#if defined(O_NOFOLLOW)
   if (access & FILEIO_OPEN_ACCESS_NOFOLLOW) {
      flags |= O_NOFOLLOW;
   }
#endif

#if defined(__linux__)
   if (access & FILEIO_OPEN_SYNC) {
      flags |= O_SYNC;
   }
#endif

#if defined(O_NOFOLLOW)
   if (access & FILEIO_OPEN_ACCESS_NOFOLLOW) {
      flags |= O_NOFOLLOW;
   }
#endif

#if defined(__linux__) && defined(O_CLOEXEC)
   if (access & FILEIO_OPEN_CLOSE_ON_EXEC) {
      flags |= O_CLOEXEC;
   }
#endif

   flags |= FileIO_OpenActions[action];

   file->flags = access;

#if defined(__APPLE__)
   if (access & FILEIO_OPEN_PRIVILEGED) {
      // We only support privileged opens, not creates or truncations.
      if ((flags & (O_CREAT | O_TRUNC)) != 0) {
         fd = -1;
         errno = EACCES;
      } else {
         fd = FileIO_PrivilegedPosixOpen(pathName, flags);
      }
   } else {
      fd = Posix_Open(pathName, flags, mode);
   }
#else
   {
      uid_t uid = -1;

      if (access & FILEIO_OPEN_PRIVILEGED) {
         uid = Id_BeginSuperUser();
      }

      fd = Posix_Open(pathName, flags, mode);

      error = errno;

      if (access & FILEIO_OPEN_PRIVILEGED) {
         Id_EndSuperUser(uid);
      }

      errno = error;
   }
#endif

   if (fd == -1) {
      ret = FileIOErrno2Result(errno);
      if (ret == FILEIO_ERROR) {
         Log(LGPFX "open error on %s: %s\n", pathName,
             Err_Errno2String(errno));
      }
      goto error;
   }

#if defined(__APPLE__)
   if (access & (FILEIO_OPEN_UNBUFFERED | FILEIO_OPEN_SYNC)) {
      error = fcntl(fd, F_NOCACHE, 1);
      if (error == -1) {
         ret = FileIOErrno2Result(errno);
         if (ret == FILEIO_ERROR) {
            Log(LGPFX "fcntl error on %s: %s\n", pathName,
                Err_Errno2String(errno));
         }
         goto error;
      }

      if (!(access & FILEIO_OPEN_SYNC)) {
         error = fcntl(fd, F_NODIRECT, 1);
         if (error == -1) {
            ret = FileIOErrno2Result(errno);
            if (ret == FILEIO_ERROR) {
               Log(LGPFX "fcntl error on %s: %s\n", pathName,
                   Err_Errno2String(errno));
            }
            goto error;
         }
      }
   }
#endif

   if (access & FILEIO_OPEN_DELETE_ASAP) {
      /*
       * Remove the name from the name space. The file remains laid out on the
       * disk and accessible through the file descriptor until it is closed.
       */

      if (Posix_Unlink(pathName) == -1) {
         ret = FileIOErrno2Result(errno);
         if (ret == FILEIO_ERROR) {
            Log(LGPFX "unlink error on %s: %s\n", pathName,
                Err_Errno2String(errno));
         }
         goto error;
      }
   }

   file->posix = fd;

   return FILEIO_SUCCESS;

error:
   error = errno;

   if (fd != -1) {
      close(fd);
   }
   FileIO_Unlock(file);
   FileIO_Cleanup(file);
   FileIO_Invalidate(file);
   errno = error;

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_CreateRetry --
 *
 *      Open/create a file; specify creation mode
 *
 * Results:
 *      FILEIO_SUCCESS on success: 'file' is set
 *      FILEIO_OPEN_ERROR_EXIST if the file already exists
 *      FILEIO_FILE_NOT_FOUND if the file is not present
 *      FILEIO_ERROR for other errors
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_CreateRetry(FileIODescriptor *file,   // OUT:
                   const char *pathName,     // IN:
                   int access,               // IN:
                   FileIOOpenAction action,  // IN:
                   int mode,                 // IN: mode_t for creation
                   uint32 maxWaitTimeMsec)   // IN:
{
   return FileIOCreateRetry(file, pathName, access, action, mode,
                            maxWaitTimeMsec);
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Create --
 *
 *      Open/create a file; specify creation mode
 *
 * Results:
 *      FILEIO_SUCCESS on success: 'file' is set
 *      FILEIO_OPEN_ERROR_EXIST if the file already exists
 *      FILEIO_FILE_NOT_FOUND if the file is not present
 *      FILEIO_ERROR for other errors
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Create(FileIODescriptor *file,   // OUT:
              const char *pathName,     // IN:
              int access,               // IN:
              FileIOOpenAction action,  // IN:
              int mode)                 // IN: mode_t for creation
{
   return FileIOCreateRetry(file, pathName, access, action, mode, 0);
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_OpenRetry --
 *
 *      Open/create a file.
 *      May perform retries to deal with certain OS conditions.
 *
 * Results:
 *      FILEIO_SUCCESS on success: 'file' is set
 *      FILEIO_OPEN_ERROR_EXIST if the file already exists
 *      FILEIO_FILE_NOT_FOUND if the file is not present
 *      FILEIO_ERROR for other errors
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_OpenRetry(FileIODescriptor *file,   // OUT:
                 const char *pathName,     // IN:
                 int access,               // IN:
                 FileIOOpenAction action,  // IN:
                 uint32 maxWaitTimeMsec)   // IN:
{
#if defined(VMX86_SERVER)
   FileIOResult res;
   uint32 waitTimeMsec = 0;
   uint32 maxLoopTimeMsec = 3000;  // 3 seconds

   /*
    * Workaround the ESX NFS client bug as seen in PR 1341775.
    * Since ESX NFS client can sometimes *wrongly* return ESTALE for a
    * legitimate file open case, we retry for some time in hopes that the
    * problem will resolve itself.
    */

   while (TRUE) {
      res = FileIOCreateRetry(file, pathName, access, action,
                              S_IRUSR | S_IWUSR, maxWaitTimeMsec);

      if (res == FILEIO_ERROR && Err_Errno() == ESTALE &&
          waitTimeMsec < maxLoopTimeMsec) {
         Log(LGPFX "FileIOCreateRetry (%s) failed with ESTALE, retrying.\n",
             pathName);

         waitTimeMsec += FileSleeper(100, 300);
      } else {
         break;
      }
   }

   return res;
#else
   return FileIOCreateRetry(file, pathName, access, action,
                            S_IRUSR | S_IWUSR, maxWaitTimeMsec);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Open --
 *
 *      Open/create a file.
 *
 * Results:
 *      FILEIO_SUCCESS on success: 'file' is set
 *      FILEIO_OPEN_ERROR_EXIST if the file already exists
 *      FILEIO_FILE_NOT_FOUND if the file is not present
 *      FILEIO_ERROR for other errors
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Open(FileIODescriptor *file,   // OUT:
            const char *pathName,     // IN:
            int access,               // IN:
            FileIOOpenAction action)  // IN:
{
   return FileIO_OpenRetry(file, pathName, access, action, 0);
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Seek --
 *
 *      Change the current position in a file
 *
 * Results:
 *      On success: the new current position in bytes from the beginning of
 *                  the file
 *
 *      On failure: -1
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

uint64
FileIO_Seek(const FileIODescriptor *file,  // IN:
            int64 distance,                // IN:
            FileIOSeekOrigin origin)       // IN:
{
   ASSERT(file != NULL);

#if defined(__ANDROID__)
   /*
    * Android doesn't implement _FILE_OFFSET_BITS=64, but always has lseek64.
    */
   return lseek64(file->posix, distance, FileIO_SeekOrigins[origin]);
#else
   /*
    * Require 64-bit file API support via _FILE_OFFSET_BITS=64 or
    * operating system default.
    */
   ASSERT_ON_COMPILE(sizeof(off_t) == 8);

   return lseek(file->posix, distance, FileIO_SeekOrigins[origin]);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Write --
 *
 *      Write to a file
 *
 * Results:
 *      FILEIO_SUCCESS on success: '*actual_count' = 'requested' bytes have
 *       been written.
 *      FILEIO_WRITE_ERROR_FBIG for the attempt to write file that exceeds
 *       maximum file size.
 *      FILEIO_WRITE_ERROR_NOSPC when the device containing the file has no
 *       room for the data.
 *      FILEIO_WRITE_ERROR_DQUOT for attempts to write file that exceeds
 *       user's disk quota.
 *      FILEIO_ERROR for other errors: only '*actual_count' bytes have been
 *       written for sure.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Write(FileIODescriptor *fd,  // IN:
             const void *bufIn,     // IN:
             size_t requested,      // IN:
             size_t *actual)        // OUT:
{
   const uint8 *buf = (const uint8 *)bufIn;
   size_t initial_requested;
   FileIOResult fret = FILEIO_SUCCESS;

   ASSERT(fd != NULL);

   VERIFY(requested < 0x80000000);

   initial_requested = requested;
   while (requested > 0) {
      ssize_t res;

      res = write(fd->posix, buf, requested);

      if (res == -1) {
         int error = errno;

         if (error == EINTR) {
            continue;
         }
         fret = FileIOErrno2Result(error);
         break;
      }

      buf += res;
      requested -= res;
   }

   if (actual) {
      *actual = initial_requested - requested;
   }
   return fret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Read --
 *
 *      Read from a file
 *
 * Results:
 *      FILEIO_SUCCESS on success: '*actual_count' = 'requested' bytes have
 *       been read.
 *      FILEIO_READ_ERROR_EOF if the end of the file was reached: only
 *       '*actual_count' bytes have been read for sure.
 *      FILEIO_ERROR for other errors: only '*actual_count' bytes have been
 *       read for sure.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Read(FileIODescriptor *fd,  // IN:
            void *bufIn,           // OUT:
            size_t requested,      // IN:
            size_t *actual)        // OUT:
{
   uint8 *buf = (uint8 *) bufIn;
   size_t initial_requested;
   FileIOResult fret = FILEIO_SUCCESS;

   ASSERT(fd != NULL);

   VERIFY(requested < 0x80000000);

   initial_requested = requested;
   while (requested > 0) {
      ssize_t res;

      res = read(fd->posix, buf, requested);
      if (res == -1) {
         if (errno == EINTR) {
            continue;
         }
         fret = FileIOErrno2Result(errno);
         break;
      }

      if (res == 0) {
         fret = FILEIO_READ_ERROR_EOF;
         break;
      }

      buf += res;
      requested -= res;
   }

   if (actual) {
      *actual = initial_requested - requested;
   }
   return fret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Truncate --
 *
 *      Truncates file to a given length
 *
 * Results:
 *      Bool - TRUE on success, FALSE on failure
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
FileIO_Truncate(FileIODescriptor *file,  // IN:
                uint64 newLength)        // IN:
{
   ASSERT(file != NULL);

   return ftruncate(file->posix, newLength) == 0;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Close --
 *
 *      Close a file
 *
 * Results:
 *      FILEIO_SUCCESS: The file was closed and unlinked. The FileIODescriptor
 *                      is no longer valid.
 *      FILEIO_ERROR: An error occurred.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Close(FileIODescriptor *file)  // IN:
{
   int err;

   ASSERT(file != NULL);

   err = (close(file->posix) == -1) ? errno : 0;

   /* Unlock the file if it was locked */
   FileIO_Unlock(file);
   FileIO_Cleanup(file);
   FileIO_Invalidate(file);

   if (err) {
      errno = err;
   }

   return (err == 0) ? FILEIO_SUCCESS : FILEIO_ERROR;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Sync --
 *
 *      Synchronize the disk state of a file with its memory state
 *
 * Results:
 *      On success: FILEIO_SUCCESS
 *      On failure: FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Sync(const FileIODescriptor *file)  // IN:
{
   ASSERT(file != NULL);

   return (fsync(file->posix) == -1) ? FILEIO_ERROR : FILEIO_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileIOCoalesce --
 *
 *      Linux 2.2 does a fairly braindead thing with ioVec's.  It simply issues
 *      reads and writes internal to the kernel in serial
 *      (linux/fs/read_write.c:do_readv_writev()).  We optimize here for the
 *      case of many small chunks.  The cost of the extra copy in this case
 *      is made up for by the decreased number of separate I/Os the kernel
 *      issues internally. Note that linux 2.4 seems to be smarter with respect
 *      to this problem.
 *
 * Results:
 *      Bool - Whether or not coalescing was done.  If it was done,
 *             FileIODecoalesce *MUST* be called.
 *
 * Side effects:
 *      FileIOCoalesce will malloc *outVec if coalescing is performed
 *
 *-----------------------------------------------------------------------------
 */

static Bool
FileIOCoalesce(
           struct iovec const *inVec, // IN:  Vector to coalesce from
           int inCount,               // IN:  count for inVec
           size_t inTotalSize,        // IN:  totalSize (bytes) in inVec
           Bool isWrite,              // IN:  coalesce for writing (or reading)
           Bool forceCoalesce,        // IN:  if TRUE always coalesce
           int flags,                 // IN: fileIO open flags
           struct iovec *outVec)      // OUT: Coalesced (1-entry) iovec
{
   uint8 *cBuf;

   ASSERT(inVec != NULL);
   ASSERT(outVec != NULL);

   FileIO_OptionalSafeInitialize();

   /* simple case: no need to coalesce */
   if (inCount == 1) {
      return FALSE;
   }

   /*
    * Only coalesce when the number of entries is above our count threshold
    * and the average size of an entry is less than our size threshold
    */

   if (!forceCoalesce &&
       (!filePosixOptions.enabled ||
       inCount <= filePosixOptions.countThreshold ||
       inTotalSize / inCount >= filePosixOptions.sizeThreshold)) {
      return FALSE;
   }

   // XXX: Wouldn't it be nice if we could log from here!
   //LOG(5, "FILE: Coalescing %s of %d elements and %d size\n",
   //    isWrite ? "write" : "read", inCount, inTotalSize);

   if (filePosixOptions.aligned || flags & FILEIO_OPEN_UNBUFFERED) {
      cBuf = FileIOAligned_Malloc(sizeof(uint8) * inTotalSize);
   } else {
      cBuf = Util_SafeMalloc(sizeof(uint8) * inTotalSize);
   }
   if (!cBuf) {
      return FALSE;
   }

  if (isWrite) {
      IOV_WriteIovToBuf(inVec, inCount, cBuf, inTotalSize);
   }

   outVec->iov_base = cBuf;
   outVec->iov_len = inTotalSize;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileIODecoalesce --
 *
 *      Inverse of the coalesce optimization.  For writes, its a NOOP, but
 *      for reads, it copies the data back into the original buffer.
 *      It also frees the memory allocated by FileIOCoalesce.
 *
 * Results:
 *      void
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
FileIODecoalesce(
        struct iovec *coVec,         // IN: Coalesced (1-entry) vector
        struct iovec const *origVec, // IN: Original vector
        int origVecCount,            // IN: count for origVec
        size_t actualSize,           // IN: # bytes to transfer back to origVec
        Bool isWrite,                // IN: decoalesce for writing (or reading)
        int flags)                   // IN: fileIO open flags
{
   ASSERT(coVec != NULL);
   ASSERT(origVec != NULL);

   ASSERT(actualSize <= coVec->iov_len);
   ASSERT_NOT_TESTED(actualSize == coVec->iov_len);

   if (!isWrite) {
      IOV_WriteBufToIov(coVec->iov_base, actualSize, origVec, origVecCount);
   }

   if (filePosixOptions.aligned || flags & FILEIO_OPEN_UNBUFFERED) {
      FileIOAligned_Free(coVec->iov_base);
   } else {
      Posix_Free(coVec->iov_base);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Readv --
 *
 *      Wrapper for readv. In linux, we can issue a readv directly.
 *      But the readv is not atomic, i.e, the read can succeed
 *      on the first N vectors, and return a positive value in spite
 *      of the fact that there was an error on the N+1st vector. There
 *      is no way to query the exact error that happened. So, we retry
 *      in a loop (for a max of MAX_RWV_RETRIES).
 *      XXX: If we retried MAX_RWV_RETRIES times and gave up, we will
 *      return FILEIO_ERROR even if errno is undefined.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR, FILEIO_READ_ERROR_EOF
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Readv(FileIODescriptor *fd,  // IN:
             struct iovec const *v, // IN:
             int numEntries,        // IN:
             size_t totalSize,      // IN:
             size_t *actual)        // OUT:
{
   size_t bytesRead = 0, sum = 0;
   FileIOResult fret = FILEIO_ERROR;
   int nRetries = 0, maxRetries = numEntries;
   struct iovec coV;
   struct iovec const *vPtr;
   Bool didCoalesce;
   int numVec;

   ASSERT(fd != NULL);

   didCoalesce = FileIOCoalesce(v, numEntries, totalSize, FALSE,
                                FALSE, fd->flags, &coV);

   VERIFY(totalSize < 0x80000000);

   numVec = didCoalesce ? 1 : numEntries;
   vPtr = didCoalesce ? &coV : v;

   while (nRetries < maxRetries) {
      ssize_t retval;
      int tempVec = MIN(filePosixOptions.maxIOVec, numVec);

      ASSERT(tempVec > 0);
      retval = readv(fd->posix, vPtr, tempVec);

      if (retval == -1) {
         if (errno == EINTR) {
            continue;
         }
         fret = FileIOErrno2Result(errno);
         break;
      }
      bytesRead += retval;
      if (bytesRead == totalSize) {
         fret = FILEIO_SUCCESS;
         break;
      }
      if (retval == 0) {
         fret = FILEIO_READ_ERROR_EOF;
         break;
      }

      /*
       * Ambigous case. Stupid Linux. If the bytesRead matches an
       * exact iovector boundary, we need to retry from the next
       * iovec. 2) If it does not match, EOF is the only error possible.
       * NOTE: If Linux Readv implementation changes, this
       * ambiguity handling may need to change.
       * --Ganesh, 08/15/2001.
       */

      for (; sum < bytesRead; vPtr++, numVec--) {
         sum += vPtr->iov_len;

         /*
          * In each syscall, we will process atleast one iovec
          * or get an error back. We will therefore retry atmost
          * count times. If multiple iovecs were processed before
          * an error hit, we will retry a lesser number of times.
          */

         nRetries++;
      }
      if (sum > bytesRead) {
         // A partially filled iovec can ONLY mean EOF
         fret = FILEIO_READ_ERROR_EOF;
         break;
      }
   }

   if (didCoalesce) {
      FileIODecoalesce(&coV, v, numEntries, bytesRead, FALSE, fd->flags);
   }

   if (actual) {
      *actual = bytesRead;
   }

   return fret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Writev --
 *
 *      Wrapper for writev. In linux, we can issue a writev directly.
 *      But the writev is not atomic, i.e, the write can succeed
 *      on the first N vectors, and return a positive value in spite
 *      of the fact that there was an error on the N+1st vector. There
 *      is no way to query the exact error that happened. So, we retry
 *      in a loop (for a max of MAX_RWV_RETRIES).
 *      XXX: If we retried MAX_RWV_RETRIES times and gave up, we will
 *      return FILEIO_ERROR even if errno is undefined.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Writev(FileIODescriptor *fd,  // IN:
              struct iovec const *v, // IN:
              int numEntries,        // IN:
              size_t totalSize,      // IN:
              size_t *actual)        // OUT:
{
   size_t bytesWritten = 0, sum = 0;
   FileIOResult fret = FILEIO_ERROR;
   int nRetries = 0, maxRetries = numEntries;
   struct iovec coV;
   struct iovec const *vPtr;
   Bool didCoalesce;
   int numVec;

   ASSERT(fd != NULL);

   didCoalesce = FileIOCoalesce(v, numEntries, totalSize, TRUE,
                                FALSE, fd->flags, &coV);

   VERIFY(totalSize < 0x80000000);

   numVec = didCoalesce ? 1 : numEntries;
   vPtr = didCoalesce ? &coV : v;

   while (nRetries < maxRetries) {
      ssize_t retval;
      int tempVec = MIN(filePosixOptions.maxIOVec, numVec);

      ASSERT(tempVec > 0);
      retval = writev(fd->posix, vPtr, tempVec);

      if (retval == -1) {
         if (errno == EINTR) {
            continue;
         }
         fret = FileIOErrno2Result(errno);
         break;
      }

      bytesWritten += retval;
      if (bytesWritten == totalSize) {
         fret = FILEIO_SUCCESS;
         break;
      }
      for (; sum < bytesWritten; vPtr++, numVec--) {
         sum += vPtr->iov_len;
         nRetries++;
      }

      /*
       * writev only seems to produce a partial iovec when the disk is
       * out of space.  Just call it an error. --probin
       */

      if (sum != bytesWritten) {
         fret = FILEIO_WRITE_ERROR_NOSPC;
         break;
      }
   }

   if (didCoalesce) {
      FileIODecoalesce(&coV, v, numEntries, bytesWritten, TRUE, fd->flags);
   }

   if (actual) {
      *actual = bytesWritten;
   }

   return fret;
}


#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) ||\
    defined(__sun__)

/*
 *----------------------------------------------------------------------
 *
 * FileIOPreadvCoalesced --
 *
 *      This function implements vector pread for platforms that do not
 *      support the preadv system call. The incoming vectors are
 *      coalesced to a single buffer to issue only one pread()
 *      system call which reads from a specified offset. The
 *      vectors are then decoalesced before return.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static FileIOResult
FileIOPreadvCoalesced(
                FileIODescriptor *fd,        // IN: File descriptor
                struct iovec const *entries, // IN: Vector to read into
                int numEntries,              // IN: Number of vector entries
                uint64 offset,               // IN: Offset to start reading
                size_t totalSize,            // IN: totalSize(bytes) in entries
                size_t *actual)              // OUT: number of bytes read
{
   struct iovec const *vPtr;
   struct iovec coV;
   int count;
   uint64 fileOffset;
   FileIOResult fret;
   Bool didCoalesce;
   size_t sum = 0;

   didCoalesce = FileIOCoalesce(entries, numEntries, totalSize, FALSE,
                                TRUE /* force coalescing */, fd->flags, &coV);

   count = didCoalesce ? 1 : numEntries;
   vPtr = didCoalesce ? &coV : entries;

   fileOffset = offset;
   while (count > 0) {
      size_t leftToRead = vPtr->iov_len;
      uint8 *buf = (uint8 *) vPtr->iov_base;

      while (leftToRead > 0) {
         ssize_t retval = pread(fd->posix, buf, leftToRead, fileOffset);

         if (retval == -1) {
            if (errno == EINTR) {
               continue;
            }
            fret = FileIOErrno2Result(errno);
            goto exit;
         }

         if (retval == 0) {
            fret = FILEIO_READ_ERROR_EOF;
            goto exit;
         }

         buf += retval;
         leftToRead -= retval;
         sum += retval;
         fileOffset += retval;
      }

      count--;
      vPtr++;
   }
   fret = FILEIO_SUCCESS;

exit:
   if (didCoalesce) {
      FileIODecoalesce(&coV, entries, numEntries, sum, FALSE, fd->flags);
   }
   if (actual) {
      *actual = sum;
   }

   return fret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIOPwritevCoalesced --
 *
 *      This function implements vector pwrite for platforms that do not
 *      support the pwritev system call. The incoming vectors are
 *      coalesced to a single buffer to issue only one pwrite()
 *      system call which writes from a specified offset. The
 *      vectors are then decoalesced before return.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static FileIOResult
FileIOPwritevCoalesced(
                   FileIODescriptor *fd,        // IN: File descriptor
                   struct iovec const *entries, // IN: Vector to write from
                   int numEntries,              // IN: Number of vector entries
                   uint64 offset,               // IN: Offset to start writing
                   size_t totalSize,            // IN: Total size(bytes)
                   size_t *actual)              // OUT: number of bytes written
{
   struct iovec coV;
   Bool didCoalesce;
   struct iovec const *vPtr;
   int count;
   uint64 fileOffset;
   FileIOResult fret;
   size_t sum = 0;

   didCoalesce = FileIOCoalesce(entries, numEntries, totalSize, TRUE,
                                TRUE /* force coalescing */, fd->flags, &coV);

   count = didCoalesce ? 1 : numEntries;
   vPtr = didCoalesce ? &coV : entries;

   fileOffset = offset;
   while (count > 0) {
      size_t leftToWrite = vPtr->iov_len;
      uint8 *buf = (uint8 *)vPtr->iov_base;

      while (leftToWrite > 0) {
         ssize_t retval = pwrite(fd->posix, buf, leftToWrite, fileOffset);

         if (retval == -1) {
            if (errno == EINTR) {
               continue;
            }
            fret = FileIOErrno2Result(errno);
            goto exit;
         }
         if (retval == 0) {
            NOT_TESTED();
            fret = FILEIO_WRITE_ERROR_NOSPC;
            goto exit;
         }
         if (retval < leftToWrite) {
            /*
             * Using %zd on Android generated a warning about
             * expecting a "signed size_t" argument; casting retval to
             * "signed size_t" generated an error, though. We've
             * already checked for retval == -1 above, so the cast
             * below should be OK. Refer to bug 817761.
             */
            LOG_ONCE(LGPFX" %s wrote %"FMTSZ"u out of %"FMTSZ"u bytes.\n",
                     __FUNCTION__, (size_t)retval, leftToWrite);
         }

         buf += retval;
         leftToWrite -= retval;
         sum += retval;
         fileOffset += retval;
      }

      count--;
      vPtr++;
   }

   fret = FILEIO_SUCCESS;
exit:
   if (didCoalesce) {
      FileIODecoalesce(&coV, entries, numEntries, sum, TRUE, fd->flags);
   }
   if (actual) {
      *actual = sum;
   }
   return fret;
}

#endif /* defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) ||
          defined(__sun__) */


#if defined(__linux__) && !defined(__ANDROID__)

/*
 *----------------------------------------------------------------------
 *
 * FileIOPreadvInternal --
 *
 *      This function implements vector pread for linux builds. Although,
 *      support for preadv() first appeared in Linux 2.6.30,
 *      library support was added in glibc 2.10.
 *      Hence using weak linkage technique, we try to call the more
 *      optimized preadv system call. If the system does not support
 *      this, we fall back to earlier unoptimized technique.
 *
 *      Note that in linux, preadv can succeed on the first N vectors,
 *      and return a positive value in spite of the fact that there was
 *      an error on the N+1st vector. There is no way to query the exact
 *      error that happened. So, we retry in a loop (for a max of
 *      MAX_RWV_RETRIES), same as FileIO_Readv().
 *
 *      XXX: If we retried MAX_RWV_RETRIES times and gave up, we will
 *      return FILEIO_ERROR even if errno is undefined.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static FileIOResult
FileIOPreadvInternal(
                FileIODescriptor *fd,        // IN: File descriptor
                struct iovec const *entries, // IN: Vector to read into
                int numEntries,              // IN: Number of vector entries
                uint64 offset,               // IN: Offset to start reading
                size_t totalSize,            // IN: totalSize(bytes) in entries
                size_t *actual)              // OUT: number of bytes read
{
   struct iovec const *vPtr;
   int numVec;
   size_t partialBytes = 0;
   size_t bytesRead = 0;
   size_t sum = 0;
   int nRetries = 0;
   int maxRetries = numEntries;
   FileIOResult fret = FILEIO_ERROR;

   FileIO_OptionalSafeInitialize();
   numVec = numEntries;
   vPtr = entries;

   while (nRetries < maxRetries) {
      ssize_t retval = 0;

      ASSERT(numVec > 0);

      /*
       * This is needed to deal with old libraries.  Once we're over
       * the library horizon this can go away.
       */
      /* coverity[func_conv] */
      if (preadv64 == NULL) {
         fret = FileIOPreadvCoalesced(fd, entries, numEntries, offset,
                                      totalSize, &bytesRead);
         break;
      } else {
         int tempVec = MIN(filePosixOptions.maxIOVec, numVec);
         retval = preadv64(fd->posix, vPtr, tempVec, offset);
      }
      if (retval == -1) {
         if (errno == EINTR) {
            continue;
         }
         if (errno == ENOSYS || errno == EINVAL || errno == ENOMEM) {
            /*
             * ENOSYS is returned when the kernel does not support preadv and
             * will be returned the first time we ever call preadv. So, we
             * can assume that we are not reading partial requests here.
             * ENOMEM can be due to system call failure and EINVAL is when file
             * was opened with the O_DIRECT flag and memory is not suitably
             * aligned. Let's try to read the remaining vectors using the
             * unoptimized function and hope we don't encounter another error.
             */
            size_t remSize = totalSize - bytesRead;
            uint64 tempOffset =  offset + bytesRead;
            partialBytes = 0;
            fret = FileIOPreadvCoalesced(fd, vPtr, numVec, tempOffset, remSize,
                                         &partialBytes);
            break;
         }
         fret = FileIOErrno2Result(errno);
         break;
      }
      bytesRead += retval;
      if (bytesRead == totalSize) {
         fret = FILEIO_SUCCESS;
         break;
      }
      if (retval == 0) {
         fret = FILEIO_READ_ERROR_EOF;
         break;
      }

      /*
       * This is an ambiguous case in linux preadv implementation.
       * If the bytesRead matches an exact iovector boundary, we need
       * to retry from the next iovec. However, if it does not match,
       * EOF is the only error possible. Linux 3.4.4 continues to have
       * this behaviour.
       * NOTE: If Linux preadv implementation changes, this
       * ambiguity handling may need to change.
       */

      for (; sum < bytesRead; vPtr++, numVec--) {
         sum += vPtr->iov_len;
         offset += vPtr->iov_len;

         /*
          * In each syscall, we will process atleast one iovec
          * or get an error back. We will therefore retry at most
          * count times. If multiple iovecs were processed before
          * an error hit, we will retry a lesser number of times.
          */

         nRetries++;
      }
      if (sum > bytesRead) {
         // A partially filled iovec can ONLY mean EOF
         fret = FILEIO_READ_ERROR_EOF;
         break;
      }
   }
   if (actual) {
      *actual = bytesRead + partialBytes;
   }

   return fret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIOPwritevInternal --
 *
 *      This function implements vector pwrite for linux builds. Although,
 *      support for pwritev() first appeared in Linux 2.6.30, library
 *      support was added in glibc 2.10.
 *      Hence using weak linkage technique, we try to call the more
 *      optimized pwritev system call. If the system does not support
 *      this, we fall back to earlier unoptimized technique.
 *
 *      Note that in linux, pwritev can succeed on the first N vectors,
 *      and return a positive value in spite of the fact that there was
 *      an error on the N+1st vector. There is no way to query the exact
 *      error that happened. So, we retry in a loop (for a max of
 *      MAX_RWV_RETRIES), same as FileIO_Writev().
 *
 *      XXX: If we retried MAX_RWV_RETRIES times and gave up, we will
 *      return FILEIO_ERROR even if errno is undefined.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static FileIOResult
FileIOPwritevInternal(
                FileIODescriptor *fd,        // IN: File descriptor
                struct iovec const *entries, // IN: Vector to write from
                int numEntries,              // IN: Number of vector entries
                uint64 offset,               // IN: Offset to start writing
                size_t totalSize,            // IN: Total size(bytes)in entries
                size_t *actual)              // OUT: number of bytes written
{
   struct iovec const *vPtr;
   int numVec;
   size_t partialBytes = 0;
   size_t bytesWritten = 0;
   size_t sum = 0;
   int nRetries = 0;
   int maxRetries = numEntries;
   FileIOResult fret = FILEIO_ERROR;

   FileIO_OptionalSafeInitialize();
   numVec = numEntries;
   vPtr = entries;

   while (nRetries < maxRetries) {
      ssize_t retval = 0;

      ASSERT(numVec > 0);

      /*
       * This is needed to deal with old libraries.  Once we're over
       * the library horizon this can go away.
       */
      /* coverity[func_conv] */
      if (pwritev64 == NULL) {
         fret = FileIOPwritevCoalesced(fd, entries, numEntries, offset,
                                       totalSize, &bytesWritten);
         break;
      } else {
         int tempVec = MIN(filePosixOptions.maxIOVec, numVec);
         retval = pwritev64(fd->posix, vPtr, tempVec, offset);
      }
      if (retval == -1) {
         if (errno == EINTR) {
            continue;
         }
         if (errno == ENOSYS || errno == EINVAL || errno == ENOMEM) {
            /*
             * ENOSYS is returned when the kernel does not support pwritev and
             * will be returned the first time we ever call pwritev. So, we
             * can assume that we are not writing partial requests here.
             * ENOMEM can be due to system call failure and EINVAL is when file
             * was opened with the O_DIRECT flag and memory is not suitably
             * aligned. Let's try writing the remaining vectors using the
             * unoptimized function and hope we don't encounter another error.
             */
            size_t remSize = totalSize - bytesWritten;
            uint64 tempOffset =  offset + bytesWritten;
            partialBytes = 0;
            fret = FileIOPwritevCoalesced(fd, vPtr, numVec, tempOffset, remSize,
                                          &partialBytes);
            break;
         }

         fret = FileIOErrno2Result(errno);
         break;
      }

      bytesWritten += retval;
      if (bytesWritten == totalSize) {
         fret = FILEIO_SUCCESS;
         break;
      }
      for (; sum < bytesWritten; vPtr++, numVec--) {
         sum += vPtr->iov_len;
         offset += vPtr->iov_len;
         nRetries++;
      }

      /*
       * pwritev produces a partial iovec when the disk is
       * out of space.  Just call it an error.
       */

      if (sum != bytesWritten) {
         fret = FILEIO_WRITE_ERROR_NOSPC;
         break;
      }
   }
   if (actual) {
      *actual = bytesWritten + partialBytes;
   }
   return fret;
}

#endif /* defined(__linux__) && !defined(__ANDROID__) */


#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) ||\
    defined(__sun__)

/*
 *----------------------------------------------------------------------
 *
 * FileIO_Preadv --
 *
 *      Implementation of vector pread. The function checks for the support
 *      of system call preadv with the version of glibc and calls the
 *      optimized system call. If the system call is not supported,
 *      we fall back to the earlier technique of coalescing the vectors
 *      and calling a single pread and decoalescing again.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Preadv(FileIODescriptor *fd,        // IN: File descriptor
              struct iovec const *entries, // IN: Vector to read into
              int numEntries,              // IN: Number of vector entries
              uint64 offset,               // IN: Offset to start reading
              size_t totalSize,            // IN: totalSize (bytes) in entries
              size_t *actual)              // OUT: number of bytes read
{
   FileIOResult fret;

   ASSERT(fd != NULL);
   ASSERT(entries != NULL);
   ASSERT(!(fd->flags & FILEIO_ASYNCHRONOUS));
   VERIFY(totalSize < 0x80000000);

#if defined(__linux__) && !defined(__ANDROID__)
   fret = FileIOPreadvInternal(fd, entries, numEntries, offset, totalSize,
                               actual);
#else
   fret = FileIOPreadvCoalesced(fd, entries, numEntries, offset, totalSize,
                                actual);
#endif /* defined(__linux__ ) && !defined(__ANDROID__) */
   return fret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Pwritev --
 *
 *      Implementation of vector pwrite. The function checks for the support
 *      of system call pwritev with the version of glibc and calls the
 *      optimized system call. If the system call is not supported,
 *      we fall back to the earlier technique of coalescing the vectors and
 *      calling a single pread and decoalescing again.
 *
 * Results:
 *      FILEIO_SUCCESS, FILEIO_ERROR
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Pwritev(FileIODescriptor *fd,        // IN: File descriptor
               struct iovec const *entries, // IN: Vector to write from
               int numEntries,              // IN: Number of vector entries
               uint64 offset,               // IN: Offset to start writing
               size_t totalSize,            // IN: Total size (bytes) in entries
               size_t *actual)              // OUT: number of bytes written
{
   FileIOResult fret;

   ASSERT(fd != NULL);
   ASSERT(entries != NULL);
   ASSERT(!(fd->flags & FILEIO_ASYNCHRONOUS));
   VERIFY(totalSize < 0x80000000);

#if defined(__linux__) && !defined(__ANDROID__)
   fret = FileIOPwritevInternal(fd, entries, numEntries, offset, totalSize,
                                actual);
#else
   fret = FileIOPwritevCoalesced(fd, entries, numEntries, offset, totalSize,
                                 actual);
#endif /* defined(__linux__ ) && !defined(__ANDROID__) */
   return fret;
}

#endif /* defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) ||
          defined(__sun__) */


/*
 *----------------------------------------------------------------------
 *
 * FileIO_GetAllocSize --
 *
 *      Get logcial and alloced size of a file.
 *
 * Results:
 *      FileIOResult.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_GetAllocSize(const FileIODescriptor *fd,  // IN:
                    uint64 *logicalBytes,        // OUT:
                    uint64 *allocedBytes)        // OUT:
{
   struct stat statBuf;

   ASSERT(fd != NULL);

   if (fstat(fd->posix, &statBuf) == -1) {
      return FileIOErrno2Result(errno);
   }

   if (logicalBytes) {
      *logicalBytes = statBuf.st_size;
   }

   if (allocedBytes) {
     /*
      * If you don't like the magic number 512, yell at the people
      * who wrote sys/stat.h and tell them to add a #define for it.
      */
      *allocedBytes = statBuf.st_blocks * 512;
   }

   return FILEIO_SUCCESS;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_SetAllocSize --
 *
 *      Set allocated size of file, allocating new blocks if needed.
 *      It is an error for size to be less than the current size.
 *
 * Results:
 *      TRUE on success.  Sets errno on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
FileIO_SetAllocSize(const FileIODescriptor *fd,  // IN:
                    uint64 size)                 // IN:
{

#if defined(__APPLE__) || defined(__linux__)
   FileIOResult fret;
   uint64 curSize;
   uint64 preallocLen;
#if defined(__APPLE__)
   fstore_t prealloc;
#endif

   fret = FileIO_GetAllocSize(fd, NULL, &curSize);
   if (!FileIO_IsSuccess(fret)) {
      return FALSE;
   }

   if (curSize > size) {
      errno = EINVAL;

      return FALSE;
   }
   preallocLen = size - curSize;

#if defined(__APPLE__)
   prealloc.fst_flags = 0;
   prealloc.fst_posmode = F_PEOFPOSMODE;
   prealloc.fst_offset = 0;
   prealloc.fst_length = preallocLen;
   prealloc.fst_bytesalloc = 0;

   return fcntl(fd->posix, F_PREALLOCATE, &prealloc) != -1;
#elif __linux__
   return syscall(SYS_fallocate, fd->posix, FALLOC_FL_KEEP_SIZE,
                  curSize, preallocLen) == 0;
#endif

#else
   errno = ENOSYS;

   return FALSE;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_GetAllocSizeByPath --
 *
 *      Get the logcial and alloced size of a file specified by path.
 *
 * Results:
 *      FileIOResult.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_GetAllocSizeByPath(const char *pathName,  // IN:
                          uint64 *logicalBytes,  // OUT:
                          uint64 *allocedBytes)  // OUT:
{
   struct stat statBuf;

   if (Posix_Stat(pathName, &statBuf) == -1) {
      return FileIOErrno2Result(errno);
   }

   if (logicalBytes) {
      *logicalBytes = statBuf.st_size;
   }

   if (allocedBytes) {
     /*
      * If you don't like the magic number 512, yell at the people
      * who wrote sys/stat.h and tell them to add a #define for it.
      */
      *allocedBytes = statBuf.st_blocks * 512;
   }

   return FILEIO_SUCCESS;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_Access --
 *
 *      Wrapper for access syscall. We return FILEIO_SUCCESS if the file
 *      is accessible with the specified mode. If not, we will return
 *      FILEIO_ERROR.
 *
 * Results:
 *      FILEIO_SUCCESS or FILEIO_ERROR.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

FileIOResult
FileIO_Access(const char *pathName,  // IN: Path name. May be NULL.
              int accessMode)        // IN: Access modes to be asserted
{
   int mode = 0;

   if (pathName == NULL) {
      errno = EFAULT;

      return FILEIO_ERROR;
   }

   if (accessMode & FILEIO_ACCESS_READ) {
      mode |= R_OK;
   }
   if (accessMode & FILEIO_ACCESS_WRITE) {
      mode |= W_OK;
   }
   if (accessMode & FILEIO_ACCESS_EXEC) {
      mode |= X_OK;
   }
   if (accessMode & FILEIO_ACCESS_EXISTS) {
      mode |= F_OK;
   }

   return (Posix_Access(pathName, mode) == -1) ? FILEIO_ERROR : FILEIO_SUCCESS;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_GetFlags --
 *
 *      Accessor for fd->flags;
 *
 * Results:
 *      fd->flags
 *
 * Side Effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

uint32
FileIO_GetFlags(FileIODescriptor *fd)  // IN:
{
   ASSERT(fd != NULL);
   ASSERT(FileIO_IsValid(fd));

   return fd->flags;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_SupportsFileSize --
 *
 *      Test whether underlying filesystem supports specified file size.
 *
 * Results:
 *      Return TRUE if such file size is supported, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
FileIO_SupportsFileSize(const FileIODescriptor *fd,  // IN:
                        uint64 requestedSize)        // IN:
{
#ifdef VMX86_SERVER
   // PR 93215: Use VMkernel-specific logic.
   uint64 maxFileSize;
   if (ioctl(fd->posix, IOCTLCMD_VMFS_GET_MAX_FILE_SIZE, &maxFileSize) == -1) {
      Log(LGPFX" %s: Could not get max file size for fd: %d, error: %s\n",
          __func__, fd->posix, Err_Errno2String(errno));
      return FALSE;
   }
   return requestedSize <= maxFileSize;
#elif defined(__linux__)
   /*
    * Linux makes test on seek(), so we can do simple non-intrusive test.
    * Verified to work on 2.2.x, 2.4.x and 2.6.x, with ext2, ext3, smbfs,
    * cifs, nfs and ncpfs.  Always got some reasonable value.
    */
   Bool supported = FALSE;
   uint64 oldPos;

   ASSERT(FileIO_IsValid(fd));

   oldPos = FileIO_Seek(fd, 0, FILEIO_SEEK_CURRENT);
   if (oldPos != (uint64)-1) {
      uint64 newPos;

      if (FileIO_Seek(fd, requestedSize, FILEIO_SEEK_BEGIN) == requestedSize) {
         supported = TRUE;
      }
      newPos = FileIO_Seek(fd, oldPos, FILEIO_SEEK_BEGIN);
      VERIFY(oldPos == newPos);
   }

   return supported;
#elif defined(__APPLE__)
   struct statfs buf;

   if (fstatfs(fd->posix, &buf) == -1) {
      Log(LGPFX" %s fstatfs failure: %s\n", __FUNCTION__,
          Err_Errno2String(errno));
      /* Be optimistic despite failure */
      return TRUE;
   }

   /* Check for FAT and UFS file systems */
   if ((Str_Strcasecmp(buf.f_fstypename, "msdos") == 0) ||
       (Str_Strcasecmp(buf.f_fstypename, "ufs") == 0)) {
      /* 4 GB limit */
      return requestedSize > CONST64U(0xFFFFFFFF) ? FALSE : TRUE;
   }

   /* Be optimistic... */
   return TRUE;
#else
   /* Be optimistic on FreeBSD and Solaris... */
   return TRUE;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_GetModTime --
 *
 *      Retrieve last modification time.
 *
 * Results:
 *      Return POSIX epoch time or -1 on error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int64
FileIO_GetModTime(const FileIODescriptor *fd)  // IN:
{
   struct stat statbuf;

   if (fstat(fd->posix, &statbuf) == 0) {
      return statbuf.st_mtime;
   } else {
      return -1;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * FileIO_PrivilegedPosixOpen --
 *
 *      Opens file with elevated privileges.
 *
 * Results:
 *      Opened file descriptor, or -1 on failure (errno contains error code).
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
FileIO_PrivilegedPosixOpen(const char *pathName,  // IN:
                           int flags)             // IN:
{
   int fd;

   if (pathName == NULL) {
      errno = EFAULT;

      return -1;
   }

   /*
    * I've said *opens*.  I want you really think twice before creating files
    * with elevated privileges, so for them you have to use Id_BeginSuperUser()
    * yourself.
    */

   ASSERT((flags & (O_CREAT | O_TRUNC)) == 0);

#if defined(__APPLE__)
   if (privilegedOpenerFunc != NULL) {
      fd = (*privilegedOpenerFunc)(pathName, flags);
   } else
#endif
   {
      Bool suDone;
      uid_t uid = -1;

      if (Id_IsSuperUser()) {
         suDone = FALSE;
      } else {
         uid = Id_BeginSuperUser();
         suDone = TRUE;
      }

      fd = Posix_Open(pathName, flags, 0);

      if (suDone) {
         int error = errno;

         Id_EndSuperUser(uid);
         errno = error;
      }
   }

   return fd;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileIO_DescriptorToStream
 *
 *      Return a FILE * stream equivalent to the given FileIODescriptor.
 *      This is the logical equivalent of Posix dup() then fdopen().
 *
 *      Caller should fclose the returned descriptor when finished.
 *
 * Results:
 *      !NULL  A FILE * associated with the file associated with the fd
 *      NULL   Failure
 *
 * Side effects:
 *      New fd allocated.
 *
 *-----------------------------------------------------------------------------
 */

FILE *
FileIO_DescriptorToStream(FileIODescriptor *fdesc,  // IN:
                          Bool textMode)            // IN: unused
{
   int dupFD;
   const char *mode;
   int tmpFlags;
   FILE *stream;

   dupFD = dup(fdesc->posix);
   if (dupFD == -1) {
      return NULL;
   }

   /* The file you pass us should be valid and opened for *something* */
   ASSERT(FileIO_IsValid(fdesc));
   ASSERT((fdesc->flags & (FILEIO_OPEN_ACCESS_READ | FILEIO_OPEN_ACCESS_WRITE)) != 0);
   tmpFlags = fdesc->flags & (FILEIO_OPEN_ACCESS_READ | FILEIO_OPEN_ACCESS_WRITE);

   if (tmpFlags == (FILEIO_OPEN_ACCESS_READ | FILEIO_OPEN_ACCESS_WRITE)) {
      mode = "r+";
   } else if (tmpFlags == FILEIO_OPEN_ACCESS_WRITE) {
      mode = "w";
   } else {  /* therefore (tmpFlags == FILEIO_OPEN_ACCESS_READ) */
      mode = "r";
   }

   stream = fdopen(dupFD, mode);

   if (stream == NULL) {
      close(dupFD);
   }

   return stream;
}


#if defined(__APPLE__)
/*
 *----------------------------------------------------------------------
 *
 * HostSupportsPrealloc --
 *
 *      Returns TRUE if the host OS is new enough to support F_PREALLOCATE
 *      without data loss bugs.  On OSX, this has been verified fixed
 *      on 10.6 build with kern.osrelease 10.0.0d6.
 *
 * Results:
 *      TRUE if the current host OS is new enough.
 *      FALSE if it is not or we can't tell because of an error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
HostSupportsPrealloc(void)
{
   static Atomic_uint32 supported = { 0 };
   enum { PREALLOC_UNKNOWN = 0, PREALLOC_YES, PREALLOC_NO } val;
   char curRel[32];
   char type;
   unsigned static const int req[] = { 10, 0, 0, 6 };
   unsigned int cur[4], i;
   int num;
   size_t len = sizeof curRel;
   Bool ret = FALSE;

   val = Atomic_Read(&supported);
   if (val != PREALLOC_UNKNOWN) {
      return val == PREALLOC_YES;
   }

   if (sysctlbyname("kern.osrelease", (void *) &curRel, &len, NULL, 0) == -1) {
      goto exit;
   }

   curRel[31] = '\0';
   Log("Current OS Release is %s\n", curRel);

   /*
    * Apple's osversion is in the format X.Y.Z which maps to the public
    * OSX version 10.X-4.Y, and Z is incremented for each publicly
    * released build.  The Z part is of the form A<type>B, where a and
    * B are version numbers and <type> is either d (devel), a (alpha),
    * b (beta), rc, or fc.  If the <type>B is missing, then its a GA build.
    *
    * Since we're checking for 10.0.0d6, we can just say anything without
    * a type or with a type other than d is higher.  For d, we compare
    * the last number.
    */

   num = sscanf(curRel, "%u.%u.%u%c%u", &cur[0], &cur[1], &cur[2], &type,
                &cur[3]);

   if (num < 3) {
      goto exit;
   }

   for (i = 0; i < 3; i++) {
      if (req[i] > cur[i]) {
         goto exit;
      } else if (req[i] < cur[i]) {
         ret = TRUE;
         goto exit;
      }
   }
   if (num == 5 && type == 'd') {
      ret = req[3] <= cur[3];
      goto exit;
   }

   /*
    * If we get a type with no letter (num == 4), thats odd.
    * Consider it mal-formatted and fail.
    */

   ret = num != 4;

exit:
   if (!ret && filePosixOptions.initialized &&
       filePosixOptions.aioNumThreads == 1) {
      ret = TRUE;
   }

   Atomic_Write(&supported, ret ? PREALLOC_YES : PREALLOC_NO);

   return ret;
}

#else

/*
 *----------------------------------------------------------------------
 *
 * HostSupportsPrealloc --
 *
 *      fallocate() is supported for ext4 and xfs since 2.6.23 kernels
 *
 * Results:
 *      TRUE if the current host is linux and kernel is >= 2.6.23.
 *      FALSE if it is not .
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
HostSupportsPrealloc(void)
{
#if  (defined(__linux__ ) && !defined(VMX86_SERVER))
    if (Hostinfo_OSVersion(0) >=2 && Hostinfo_OSVersion(1) >=6 &&
        Hostinfo_OSVersion(2) >=23) {
       return TRUE;
    }
#endif
    return FALSE;
}

#endif


/*
 *----------------------------------------------------------------------------
 *
 * FileIO_SupportsPrealloc --
 *
 *      Checks if the HostOS/filesystem supports preallocation.
 *
 * Results:
 *      TRUE if supported by the Host OS/filesystem.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
FileIO_SupportsPrealloc(const char *pathName,  // IN:
                        Bool fsCheck)          // IN:
{
   Bool ret = TRUE;

   if (!HostSupportsPrealloc()) {
      return FALSE;
   }

   if (!fsCheck) {
      return ret;
   }

#if (defined( __linux__) && !defined(VMX86_SERVER))
   {
      struct statfs statBuf;
      char *fullPath;

      ret = FALSE;
      if (!pathName) {
         return ret;
      }

      fullPath = File_FullPath(pathName);
      if (!fullPath) {
         return ret;
      }

      if (Posix_Statfs(fullPath, &statBuf) == 0 &&
         statBuf.f_type == EXT4_SUPER_MAGIC) {
         ret = TRUE;
      }
      Posix_Free(fullPath);
   }
#endif

   return ret;
}


/*
 * The FileIOAligned_* functions are only used on
 * hosted (see fileInt.h for rationale).
 */
#if !defined(VMX86_TOOLS) && !defined(VMX86_SERVER)

/*
 *---------------------------------------------------------------------------
 *
 * FileIOAligned_PoolInit --
 *
 *      Initialize alignedPool. Must be called before FileIOAligned_PoolMalloc.
 *
 * Result:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
FileIOAligned_PoolInit(void)
{
   alignedPool.lock = MXUser_CreateSingletonExclLock(&alignedPoolLockStorage,
                                                     "alignedPoolLock",
                                                     RANK_LEAF);
}


/*
 *---------------------------------------------------------------------------
 *
 * FileIOAligned_PoolExit --
 *
 *      Tear down alignedPool.  Afterwards, PoolInit can be called again if
 *      desired.
 *
 * Result:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
FileIOAligned_PoolExit(void)
{
   if (!alignedPool.lock) {
      LOG_ONCE("%s called without FileIOAligned_Pool lock\n", __FUNCTION__);
      return;
   }

   MXUser_AcquireExclLock(alignedPool.lock);

   if (alignedPool.numBusy > 0) {
      LOG_ONCE("%s: %d busy buffers!  Proceeding with trepidation.\n",
               __FUNCTION__, alignedPool.numBusy);
   }
   while (alignedPool.numAlloc > 0) {
      alignedPool.numAlloc--;
      Aligned_Free(alignedPool.list[alignedPool.numAlloc]);
   }

   MXUser_ReleaseExclLock(alignedPool.lock);
   MXUser_DestroyExclLock(alignedPool.lock);

   memset(&alignedPool, 0, sizeof alignedPool);
}


/*
 *---------------------------------------------------------------------------
 *
 * FileIOAligned_PoolMalloc --
 *
 *      Alloc a chunk of memory aligned on a page boundary using a memory
 *      pool.  Result needs to be freed with FileIOAligned_PoolFree.  Returns
 *      NULL if the pool is full or the requested size cannot be satisfied from
 *      the pool.
 *
 * Result:
 *      A pointer.  NULL if requested size is too large, or on out of memory
 *      condition.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void *
FileIOAligned_PoolMalloc(size_t size)  // IN:
{
   void *buf = NULL;

   if (!alignedPool.lock) {
      LOG_ONCE("%s called without FileIOAligned_Pool lock\n", __FUNCTION__);
      return NULL;
   }

   if (size > ALIGNEDPOOL_BUFSZ) {
      return NULL;
   }

   MXUser_AcquireExclLock(alignedPool.lock);

   ASSERT(alignedPool.numBusy <= ARRAYSIZE(alignedPool.list));
   ASSERT(alignedPool.numAlloc <= ARRAYSIZE(alignedPool.list));
   ASSERT(alignedPool.numBusy <= alignedPool.numAlloc);

   if (alignedPool.numBusy == ARRAYSIZE(alignedPool.list)) {
      goto done;
   }
   if (alignedPool.numBusy == alignedPool.numAlloc) {
      buf = Aligned_UnsafeMalloc(ALIGNEDPOOL_BUFSZ);
      /* If allocation fails, just bail. */
      if (buf) {
         alignedPool.list[alignedPool.numAlloc] = buf;
         alignedPool.numBusy = ++alignedPool.numAlloc;
      }
      goto done;
   }
   buf = alignedPool.list[alignedPool.numBusy];
   alignedPool.numBusy++;

done:
   MXUser_ReleaseExclLock(alignedPool.lock);

   return buf;
}


/*
 *---------------------------------------------------------------------------
 *
 * FileIOAligned_PoolFree --
 *
 *      Test if a pointer was allocated from alignedPool, and if so, free it.
 *
 * Result:
 *      TRUE if ptr was allocated from alignedPool.  ptr is returned to pool.
 *      FALSE otherwise.
 *
 * Side effects:
 *      Might Aligned_Free() some entries from alignedPool if the timestamp[]
 *      entries indicate that we have not needed them for a while.
 *
 *---------------------------------------------------------------------------
 */

Bool
FileIOAligned_PoolFree(void *ptr)  // IN:
{
   unsigned i;
   Bool ret = FALSE;
   VmTimeType now;

   if (!alignedPool.lock) {
      LOG_ONCE("%s called without FileIOAligned_Pool lock\n", __FUNCTION__);

      return FALSE;
   }

   MXUser_AcquireExclLock(alignedPool.lock);

   ASSERT(alignedPool.numBusy <= ARRAYSIZE(alignedPool.list));
   ASSERT(alignedPool.numAlloc <= ARRAYSIZE(alignedPool.list));
   ASSERT(alignedPool.numBusy <= alignedPool.numAlloc);

   for (i = 0; i < alignedPool.numBusy; i++) {
      if (alignedPool.list[i] == ptr) {
         break;
      }
   }
   if (i == alignedPool.numBusy) {
      /* The pointer wasn't allocated from our pool. */
      goto done;
   }

   alignedPool.numBusy--;

   /*
    * At this point either i points to the "top" busy item, or i points to an
    * earlier busy item.  If i points to the top, we're done, and the following
    * "swap" is a noop.  If i points somewhere further down the busy list, we
    * can simply move the newly freed item to the top of the free list by
    * swapping its place with the not-freed item at list[numBusy].
    */
   alignedPool.list[i] = alignedPool.list[alignedPool.numBusy];
   alignedPool.list[alignedPool.numBusy] = ptr;

   now = Hostinfo_SystemTimerNS();
   alignedPool.timestamp[alignedPool.numBusy] = now;

   while (alignedPool.numAlloc > alignedPool.numBusy &&
          now - alignedPool.timestamp[alignedPool.numAlloc - 1] > ALIGNEDPOOL_OLD_AGE) {
      alignedPool.numAlloc--;
      Aligned_Free(alignedPool.list[alignedPool.numAlloc]);
      alignedPool.list[alignedPool.numAlloc] = NULL;
   }

   ret = TRUE;

done:
   MXUser_ReleaseExclLock(alignedPool.lock);

   return ret;
}

#endif
07070100000049000081A400000000000000000000000168225505000026A2000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/file/fileInt.h /*********************************************************
 * Copyright (C) 2007-2019, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fileInt.h --
 *
 *     Things internal to the file library.
 */

#if !defined(__FILE_INTERNAL_H__)
#define __FILE_INTERNAL_H__

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"
#include "err.h"
#include "posix.h"
#include "file.h"
#include "fileIO.h"
#include "fileLock.h"
#include "unicodeTypes.h"
#include "memaligned.h"

/*
 * Max supported file size is 64 TB.
 */
#define MAX_SUPPORTED_FILE_SIZE CONST64U(0x400000000000)

#if defined __linux__
/*
 * These magic constants are used only for parsing Linux statfs data.
 * So they make sense only for Linux build. If you need them on other OSes,
 * think once more.
 */
#define HFSPLUS_SUPER_MAGIC   0x482B
#if !defined(__ANDROID__)
#define ADFS_SUPER_MAGIC      0xadf5
#define AFFS_SUPER_MAGIC      0xADFF
#define EXT_SUPER_MAGIC       0x137D
#define EXT2_OLD_SUPER_MAGIC  0xEF51
#define EXT2_SUPER_MAGIC      0xEF53
#define EXT3_SUPER_MAGIC      0xEF53
#define EXT4_SUPER_MAGIC      0xEF53
#define NFS_SUPER_MAGIC       0x6969
#define SMB_SUPER_MAGIC       0x517B
#define ISOFS_SUPER_MAGIC     0x9660
#define JFFS2_SUPER_MAGIC     0x72b6
#define PROC_SUPER_MAGIC      0x9fa0
#define OPENPROM_SUPER_MAGIC  0x9fa1
#define USBDEVICE_SUPER_MAGIC 0x9fa2
#define AUTOFS_SUPER_MAGIC    0x0187
#endif

#if !defined(MSDOS_SUPER_MAGIC)
#define MSDOS_SUPER_MAGIC     0x4D44
#endif

#define XENIX_SUPER_MAGIC     0x012FF7B4
#define SYSV4_SUPER_MAGIC     0x012FF7B5
#define SYSV2_SUPER_MAGIC     0x012FF7B6
#define COH_SUPER_MAGIC       0x012FF7B7
#define UFS_SUPER_MAGIC       0x00011954
#define XFS_SUPER_MAGIC       0x58465342
#define VMFS_SUPER_MAGIC      0x2fABF15E
#define TMPFS_SUPER_MAGIC     0x01021994
#define JFS_SUPER_MAGIC       0x3153464a
#define AFS_SUPER_MAGIC       0x5346414F
#define CIFS_SUPER_MAGIC      0xFF534D42

#if !defined(REISERFS_SUPER_MAGIC)
#define REISERFS_SUPER_MAGIC  0x52654973
#endif
#endif // linux

#define LGPFX "FILE:"

#define FILE_TYPE_REGULAR      0
#define FILE_TYPE_DIRECTORY    1
#define FILE_TYPE_BLOCKDEVICE  2
#define FILE_TYPE_CHARDEVICE   3
#define FILE_TYPE_SYMLINK      4
#define FILE_TYPE_FIFO         5
#define FILE_TYPE_SOCKET       6
#define FILE_TYPE_UNCERTAIN    7

typedef struct FileData {
   uint64 fileAccessTime;
   uint64 fileCreationTime;
   uint64 fileModificationTime;
   uint64 fileSize;
   int    fileType;
   int    fileMode;
   int    fileOwner;
   int    fileGroup;
} FileData;

#define FILE_MAX_WAIT_TIME_MS 2000  // maximum wait time in milliseconds

void FileIOResolveLockBits(int *access);

#if defined(_WIN32)
int FileMapErrorToErrno(const char *functionName,
                        Err_Number status);

Bool FileRetryThisError(DWORD error,
                        uint32 numCodes,
                        DWORD *codes);

int FileAttributesRetry(const char *pathName,
                        uint32 maxWaitTimeMsec,
                        FileData *fileData);

int FileDeletionRetry(const char *pathName,
                      Bool handleLink,
                      uint32 maxWaitTimeMsec);

int FileCreateDirectoryRetry(const char *pathName,
                             int mask,
                             uint32 maxWaitTimeMsec);

int FileRemoveDirectoryRetry(const char *pathName,
                             uint32 maxWaitTimeMsec);

int FileListDirectoryRetry(const char *pathName,
                           uint32 maxWaitTimeMsec,
                           char ***ids);

#define FileAttributes(a, b)       FileAttributesRetry((a), 0, (b))
#define FileDeletion(a, b)         FileDeletionRetry((a), (b), 0)
#define FileCreateDirectory(a, b)  FileCreateDirectoryRetry((a), (b), 0)
#define FileRemoveDirectory(a)     FileRemoveDirectoryRetry((a), 0)

#define FileListDirectoryRobust(a, b) \
                    FileListDirectoryRetry((a), FILE_MAX_WAIT_TIME_MS, (b))
#define FileAttributesRobust(a, b) \
                    FileAttributesRetry((a), FILE_MAX_WAIT_TIME_MS, (b))
#define FileRenameRobust(a, b) \
                    File_RenameRetry((a), (b), FILE_MAX_WAIT_TIME_MS)
#define FileDeletionRobust(a, b) \
                    FileDeletionRetry((a), (b), FILE_MAX_WAIT_TIME_MS)
#define FileCreateDirectoryRobust(a, b) \
                    FileCreateDirectoryRetry((a), (b), FILE_MAX_WAIT_TIME_MS)
#define FileRemoveDirectoryRobust(a) \
                    FileRemoveDirectoryRetry((a), FILE_MAX_WAIT_TIME_MS)
#else
static INLINE int
FileMapErrorToErrno(const char *functionName,
                    Err_Number status)
{
   return status;
}

char *FilePosixGetBlockDevice(char const *path);

int FileAttributes(const char *pathName,
                   FileData *fileData);

int FileDeletion(const char *pathName,
                 Bool handleLink);

int FileCreateDirectory(const char *pathName,
                       int mask);

int FileRemoveDirectory(const char *pathName);

#define FileListDirectoryRobust(a, b)    File_ListDirectory((a), (b))
#define FileAttributesRobust(a, b)       FileAttributes((a), (b))
#define FileRenameRobust(a, b)           File_Rename((a), (b))
#define FileDeletionRobust(a, b)         FileDeletion((a), (b))
#define FileCreateDirectoryRobust(a, b)  FileCreateDirectory((a), (b))
#define FileRemoveDirectoryRobust(a)     FileRemoveDirectory((a))
#endif

typedef struct active_lock
{
  struct active_lock *next;
  uint32              age;
  Bool                marked;
  char               *dirName;
} ActiveLock;

typedef struct lock_values
{
   char         *machineID;
   char         *executionID;
   const char   *lockType;
   char         *locationChecksum;
   char         *memberName;
   unsigned int  lamportNumber;
   Bool          exclusivity;
   VmTimeType    startTimeMsec;
   uint32        maxWaitTimeMsec;
   ActiveLock   *lockList;
} LockValues;

#include "file_extensions.h"

#define FILELOCK_SUFFIX "." LOCK_FILE_EXTENSION

#define FILELOCK_DATA_SIZE 512

uint32 FileSleeper(uint32 minSleepTimeMsec,
                   uint32 maxSleepTimeMsec);

uint32 FileSimpleRandom(void);

const char *FileLockGetMachineID(void);

char *FileLockGetExecutionID(void);

Bool FileLockMachineIDMatch(const char *host,
                            const char *second);

int FileLockMemberValues(const char *lockDir,
                         const char *fileName,
                         char *buffer,
                         size_t size,
                         LockValues *memberValues);

FileLockToken *FileLockIntrinsic(const char *filePathName,
                                 Bool exclusivity,
                                 uint32 maxWaitTimeMsec,
                                 int *err);

int FileUnlockIntrinsic(FileLockToken *tokenPtr);

Bool FileLockIsLocked(const char *filePath,
                      int *err);

Bool FileLockValidExecutionID(const char *executionID);

Bool FileLockValidName(const char *fileName);

void FileLockAppendMessage(MsgList **msgs,
                           int err);

Bool FileIsWritableDir(const char *dirName);

FileIOResult
FileIOCreateRetry(FileIODescriptor *fd,
                  const char *pathName,
                  int access,
                  FileIOOpenAction action,
                  int mode,
                  uint32 maxWaitTimeMsec);


/*
 * FileIOAligned_* are useful on hosted platforms where malloc/memalign/valloc
 * for "large buffers" (in the range 64 KiB - 1 MiB) will generally fall
 * through to mmap/munmap, which is expensive due to page table modifications
 * and the attendant TLB flushing (which requires IPIs and possibly world
 * switches) on other threads running in the same address space.  In particular,
 * on Mac OS 10.6.6 on a Westmere-class Mac Pro, mmap + memcpy + munmap adds
 * around a millisecond of CPU time and a hundred IPIs to a 512 KiB write.  See
 * PR#685845.
 *
 * This isn't applicable to ESX because
 * 1. we don't use this path for disk IO
 * 2. we don't want to do extra large allocations
 * 3. we don't have the same alignment constraints
 * so simply define it away to nothing.
 *
 * Tools is another case, we can use this path for IO but we don't want to add
 * MXUserExclLock dependencies.
 */

#if defined(VMX86_TOOLS) || defined(VMX86_SERVER)
#define FileIOAligned_PoolInit()     /* nothing */
#define FileIOAligned_PoolExit()     /* nothing */
#define FileIOAligned_PoolMalloc(sz) NULL
#define FileIOAligned_PoolFree(ptr)  FALSE
#else
void FileIOAligned_PoolInit(void);
void FileIOAligned_PoolExit(void);
void *FileIOAligned_PoolMalloc(size_t);
Bool FileIOAligned_PoolFree(void *);
#endif

static INLINE void *
FileIOAligned_Malloc(size_t sz)  // IN:
{
   void *buf = FileIOAligned_PoolMalloc(sz);

   if (!buf) {
      buf = Aligned_Malloc(sz);
   }

   return buf;
}

static INLINE void
FileIOAligned_Free(void *ptr)  // IN:
{
   if (!FileIOAligned_PoolFree(ptr)) {
      Aligned_Free(ptr);
   }
}

#if defined(__APPLE__)
int PosixFileOpener(const char *pathName,
                    int flags,
                    mode_t mode);
#else
#define PosixFileOpener(a, b, c) Posix_Open(a, b, c);
#endif

#endif
  0707010000004A000081A40000000000000000000000016822550500004ABF000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/file/fileLockPosix.c   /*********************************************************
 * Copyright (c) 2006-2019,2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fileLockPosix.c --
 *
 *      Interface to host-specific file locking functions for POSIX hosts.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#if defined(__APPLE__)
#include <sys/sysctl.h>
#endif

#include "vmware.h"
#include "posix.h"
#include "file.h"
#include "fileLock.h"
#include "fileInt.h"
#include "util.h"
#include "str.h"
#include "err.h"
#include "hostinfo.h"

#include "unicodeOperations.h"

#define LOGLEVEL_MODULE main
#include "loglevel_user.h"

#define LOG_MAX_PROC_NAME  64


/*
 * XXX
 * Most of these warnings must be turned back into Msg_Appends, or the
 * equivalent. They were changed from Msg_Appends to Warnings to facilitate
 * integration in the disklib library, but many of the warnings are very
 * important, and should be presented directly to the user, not go silently
 * into the log file.
 */

/*
 *----------------------------------------------------------------------
 *
 * FileLockIsValidProcess --
 *
 *      Determine if the process, via its pid, is valid (alive).
 *
 * Results:
 *      TRUE    Yes
 *      FALSE   No
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
FileLockIsValidProcess(int pid)  // IN:
{
   HostinfoProcessQuery value = Hostinfo_QueryProcessExistence(pid);

   if (value == HOSTINFO_PROCESS_QUERY_UNKNOWN) {
      return TRUE;  // Err on the side of caution
   }

   return (value == HOSTINFO_PROCESS_QUERY_ALIVE) ? TRUE : FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * FileLockAppendMessage --
 *
 *      Append a detailed error message to the MsgList.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      Memory is allocated
 *
 *----------------------------------------------------------------------
 */

void
FileLockAppendMessage(MsgList **msgs,  // IN/OUT/OPT:
                      int err)         // IN: errno
{
#if defined(VMX86_TOOLS)
   Log(LGPFX "A file locking error (%d) has occurred: %s.",
       err, Err_Errno2String(err));
#else
   MsgList_Append(msgs, MSGID(fileLock.posix)
                  "A file locking error (%d) has occurred: %s.",
                  err, Err_Errno2String(err));
#endif
}


#if defined(__linux__)
/*
 *----------------------------------------------------------------------
 *
 * FileReadSlashProc --
 *
 *      Read the data in a /proc file
 *
 * Results:
 *      0    Data is available
 *      !0   Error (errno)
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
FileReadSlashProc(const char *procPath,  // IN:
                  char *buffer,          // OUT:
                  size_t bufferSize)     // IN:
{
   int fd;
   int err;
   char *p;
   size_t len;

   ASSERT(procPath != NULL);
   ASSERT(buffer != NULL);
   ASSERT(bufferSize > 0);

   fd = Posix_Open(procPath, O_RDONLY, 0);

   if (fd == -1) {
      return errno;
   }

   len = read(fd, buffer, bufferSize - 1);
   err = errno;

   close(fd);

   if (len == -1) {
      return err;
   }

   buffer[len] = '\0';

   p = strchr(buffer, '\n');

   if (p != NULL) {
      *p = '\0';
   }

   return 0;
}


/*
 *---------------------------------------------------------------------------
 *
 * FileLockProcessDescriptor --
 *
 *      Returns the process descriptor of the specified process.
 *
 *      The format of a process descriptor is as follows:
 *
 *      processID-processCreationTime(processName)
 *
 *      where the processName and processCreationTime information
 *      may be independently optional.
 *
 * Results:
 *      NULL The process does not exist.
 *     !NULL The process descriptor is returned. It is the callers
 *           responsibility to free the dynamically allocated memory.
 *
 * Side Effects:
 *     None
 *
 *---------------------------------------------------------------------------
 */

static char *
FileLockProcessDescriptor(pid_t pid)  // IN:
{
   char path[64];
   char buffer[1024];
   char *descriptor = NULL;

   if (!FileLockIsValidProcess(pid)) {
      return NULL;
   }

   Str_Sprintf(path, sizeof path, "/proc/%d/stat", pid);

   if (FileReadSlashProc(path, buffer, sizeof buffer) == 0) {
      char *p;
      char *q;
      char *rest;
      uint32 argc;
      char *argv[22];
      char *savePtr = NULL;

      /*
       * You are in a maze of twisty little fields, (virtually) all alike...
       *
       * The process creation time, in 64-bit jiffies is "out there".
       *
       * A "man 5 proc" will provide illumination concerning all of the
       * fields found on this line of text. We code for the worst case
       * and ensure that file names containing spaces or parens are
       * properly handled.
       */

      /* coverity[string_null] */
      p = strchr(buffer, '(');

      if ((p == NULL) || (p == buffer) || (*(p - 1) != ' ')) {
         goto bail;
      }

      *(p - 1) = '\0';

      q = strrchr(p + 1, ')');
      if (q == NULL) {
         goto bail;
      }

      rest = q + 1;
      if (*rest != ' ') {
         goto bail;
      }

      *rest++ = '\0';

      argv[0] = strtok_r(buffer, " ", &savePtr);  // ensure no trailing spaces
      argv[1] = p;

      /* Map spaces in the process name to something benign */
      q = p;

      while ((q = strchr(q, ' ')) != NULL) {
         *q = '_';
      }

      if (strlen(p) > LOG_MAX_PROC_NAME) {
         p[LOG_MAX_PROC_NAME - 1] = ')';
         p[LOG_MAX_PROC_NAME] = '\0';
      }

      for (argc = 2; argc < 22; argc++) {
         argv[argc] = strtok_r((argc == 2) ? rest : NULL, " ", &savePtr);

         if (argv[argc] == NULL) {
            break;
         }
      }

      if (argc == 22) {
         descriptor = Str_SafeAsprintf(NULL, "%s-%s%s", argv[0], argv[21],
                                       argv[1]);
      }
   }

bail:

   if (descriptor == NULL) {
      /*
       * Accessing /proc failed in some way. Emit a valid string that also
       * provides a clue that there is/was a problem.
       */

      descriptor = Str_SafeAsprintf(NULL, "%d-0", pid);
   }

   return descriptor;
}
#elif defined(__APPLE__)
/*
 *---------------------------------------------------------------------------
 *
 * FileLockProcessCreationTime --
 *
 *      Returns the process creation time of the specified process.
 *
 * Results:
 *      TRUE  Done!
 *      FALSE Process doesn't exist
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

static Bool
FileLockProcessCreationTime(pid_t pid,                 // IN:
                            uint64 *procCreationTime)  // OUT:
{
   int err;
   int mib[4];
   size_t size;
   struct kinfo_proc info;

   ASSERT(procCreationTime != NULL);

   /* Request information about the specified process */
   mib[0] = CTL_KERN;
   mib[1] = KERN_PROC;
   mib[2] = KERN_PROC_PID;
   mib[3] = pid;

   memset(&info, 0, sizeof info);
   size = sizeof info;
   err = sysctl(mib, ARRAYSIZE(mib), &info, &size, NULL, 0);

   if (err == -1) {
      return FALSE;
   }

   *procCreationTime = (info.kp_proc.p_starttime.tv_sec * CONST64U(1000000)) +
                        info.kp_proc.p_starttime.tv_usec;

   return TRUE;
}


/*
 *---------------------------------------------------------------------------
 *
 * FileLockProcessDescriptor --
 *
 *      Returns the process descriptor of the specified process.
 *
 *      The format of a process descriptor is as follows:
 *
 *      processID-processCreationTime(processName)
 *
 *      where the processName and processCreationTime information
 *      may be independently optional.
 *
 * Results:
 *      NULL The process does not exist.
 *     !NULL The process descriptor is returned. It is the callers
 *           responsibility to free the dynamically allocated memory.
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

static char *
FileLockProcessDescriptor(pid_t pid)  // IN:
{
   uint64 procCreationTime;

   if (!FileLockIsValidProcess(pid)) {
      return NULL;
   }

   if (!FileLockProcessCreationTime(pid, &procCreationTime)) {
      return NULL;
   }

   return Str_SafeAsprintf(NULL, "%d-%"FMT64"u", pid, procCreationTime);
}
#else
/*
 *---------------------------------------------------------------------------
 *
 * FileLockProcessDescriptor --
 *
 *      Returns the process descriptor of the specified process.
 *
 *      The format of a process descriptor is as follows:
 *
 *      processID-processCreationTime(processName)
 *
 *      where the processName and processCreationTime information
 *      may be independently optional.
 *
 * Results:
 *      NULL The process does not exist.
 *     !NULL The process descriptor is returned. It is the callers
 *           responsibility to free the dynamically allocated memory.
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

static char *
FileLockProcessDescriptor(pid_t pid)  // IN:
{
   char *value;

   if (FileLockIsValidProcess(pid)) {
      value = Str_SafeAsprintf(NULL, "%u-0", (uint32) pid);
   } else {
      value = NULL;
   }

   return value;
}
#endif


/*
 *---------------------------------------------------------------------------
 *
 * FileLockGetExecutionID --
 *
 *      Returns the executionID of the caller.
 *
 * Results:
 *      The executionID of the caller. This is a dynamically allocated string;
 *      the caller is responsible for its disposal.
 *
 * Side effects:
 *      The executionID of the caller is not thread safe. Locking is currently
 *      done at the process level - all threads of a process are treated
 *      identically.
 *
 *---------------------------------------------------------------------------
 */

char *
FileLockGetExecutionID(void)
{
   char *descriptor = FileLockProcessDescriptor(getpid());

   ASSERT(descriptor != NULL);  // Must be able to describe ourselves!

   return descriptor;
}


/*
 *---------------------------------------------------------------------------
 *
 * FileLockParseProcessDescriptor --
 *
 *      Attempt to parse the specified process descriptor. Return the
 *      pieces requested.
 *
 * Results:
 *      TRUE  Process descriptor is valid.
 *      FALSE Process descriptor is invalid.
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

static Bool
FileLockParseProcessDescriptor(const char *procDescriptor,  // IN:
                               pid_t *pid,                  // OUT:
                               uint64 *procCreationTime)    // OUT:
{
   uint32 tmp;

   ASSERT(procDescriptor != NULL);
   ASSERT(pid != NULL);
   ASSERT(procCreationTime != NULL);

   if (sscanf(procDescriptor, "%u-%"FMT64"u", &tmp, procCreationTime) != 2) {
      if (sscanf(procDescriptor, "%d", &tmp) == 1) {
         *procCreationTime = 0ULL;
      } else {
         return FALSE;
      }
   }

   *pid = tmp;

   return *pid >= 0;
}


/*
 *----------------------------------------------------------------------
 *
 * FileLockValidExecutionID --
 *
 *      Validate the execution ID.
 *
 * Results:
 *      TRUE    Yes
 *      FALSE   No
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
FileLockValidExecutionID(const char *executionID)  // IN:
{
   pid_t filePID;
   pid_t procPID;
   Bool gotFileData;
   Bool gotProcData;
   char *procDescriptor;
   uint64 fileCreationTime;
   uint64 procCreationTime;

   gotFileData = FileLockParseProcessDescriptor(executionID, &filePID,
                                                &fileCreationTime);

   if (!gotFileData) {
      Warning(LGPFX" %s parse error on '%s'. Assuming valid.\n",
              __FUNCTION__, executionID);

      return TRUE;  // Assume TRUE - preserve a lock - on parse error
   }

   procDescriptor = FileLockProcessDescriptor(filePID);

   if (procDescriptor == NULL) {
      return FALSE;  // process doesn't exist
   }

   gotProcData = FileLockParseProcessDescriptor(procDescriptor, &procPID,
                                                &procCreationTime);

   ASSERT(gotProcData);         // We built it; it had better be good
   ASSERT(procPID == filePID);  // This better match what we started with...

   Posix_Free(procDescriptor);

   if ((fileCreationTime != 0) &&
       (procCreationTime != 0) &&
       (fileCreationTime != procCreationTime)) {
      return FALSE;  // The process no longer exists
   } else {
      return TRUE;  // Looks valid...
   }
}


/*
 *---------------------------------------------------------------------------
 *
 * FileLockNormalizePath
 *
 *      Normalize the path of the file being locked. Locking a symbolic
 *      link should place the lock next to the link, not where the link
 *      points to.
 *
 * Results:
 *      The normalized path or NULL on error
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

static char *
FileLockNormalizePath(const char *filePath)  // IN:
{
   char *result;

   char *dirName = NULL;
   char *fileName = NULL;

   /*
    * If the file to be locked is a symbolic link the lock file belongs next
    * to the symbolic link, not "off" where the symbolic link points to.
    * Translation: Don't "full path" the entire path of the file to be locked;
    * "full path" the dirName of the path, leaving the fileName alone.
    */

   File_GetPathName(filePath, &dirName, &fileName);

   /*
    * Handle filePath - "xxx", "./xxx", "/xxx", and "/a/b/c".
    */

   if (*dirName == '\0') {
      if (File_IsFullPath(filePath)) {
         result = Unicode_Join(DIRSEPS, fileName, NULL);
      } else {
         result = Unicode_Join(".", DIRSEPS, fileName, NULL);
      }
   } else {
      char *fullPath = File_FullPath(dirName);

      result = (fullPath == NULL) ? NULL : Unicode_Join(fullPath, DIRSEPS,
                                                        fileName, NULL);

      Posix_Free(fullPath);
   }

   Posix_Free(dirName);
   Posix_Free(fileName);

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * FileLock_Lock --
 *
 *      Obtain a lock on a file; shared or exclusive access. Also specify
 *      how long to wait on lock acquisition - maxWaitTimeMsec
 *
 *      maxWaitTimeMsec specifies the maximum amount of time, in
 *      milliseconds, to wait for the lock before returning the "not
 *      acquired" status. A value of FILELOCK_TRYLOCK_WAIT is the
 *      equivalent of a "try lock" - the lock will be acquired only if
 *      there is no contention. A value of FILELOCK_INFINITE_WAIT
 *      specifies "waiting forever" to acquire the lock.
 *
 * Results:
 *      NULL    Lock not acquired
 *              errno to *err, msg to **msgs - when appropriate
 *      !NULL   Lock Acquired. This is the "lockToken" for an unlock.
 *
 * Side effects:
 *      Changes the host file system.
 *
 *----------------------------------------------------------------------
 */

FileLockToken *
FileLock_Lock(const char *filePath,          // IN:
              const Bool readOnly,           // IN:
              const uint32 maxWaitTimeMsec,  // IN:
              int *err,                      // OUT/OPT: returns errno
              MsgList **msgs)                // IN/OUT/OPT: add error message
{
   int res = 0;
   char *normalizedPath;
   FileLockToken *tokenPtr;

   ASSERT(filePath != NULL);

   normalizedPath = FileLockNormalizePath(filePath);

   if (normalizedPath == NULL) {
      res = EINVAL;

      tokenPtr = NULL;
   } else {
      tokenPtr = FileLockIntrinsic(normalizedPath, !readOnly, maxWaitTimeMsec,
                                   &res);

      Posix_Free(normalizedPath);
   }

   if (tokenPtr == NULL) {
      if (res == 0) {
         res = EAGAIN;  // Thank you for playing; try again
         /* Failed to acquire the lock; another has possession of it */
      }

      FileLockAppendMessage(msgs, res);
   }

   if (err != NULL) {
      *err = res;
   }

   return tokenPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * FileLock_IsLocked --
 *
 *      Is a file currently locked (at the time of the call)?
 *
 * Results:
 *      TRUE   YES
 *      FALSE  Failure (errno to *err, msg to **msgs - when appropriate)
 *
 *----------------------------------------------------------------------
 */

Bool
FileLock_IsLocked(const char *filePath,  // IN:
                  int *err,              // OUT/OPT: returns errno
                  MsgList **msgs)        // IN/OUT/OPT: add error message
{
   int res = 0;
   Bool isLocked;
   char *normalizedPath;

   ASSERT(filePath != NULL);

   normalizedPath = FileLockNormalizePath(filePath);

   if (normalizedPath == NULL) {
      res = EINVAL;

      isLocked = FALSE;
   } else {
      isLocked = FileLockIsLocked(normalizedPath, &res);

      Posix_Free(normalizedPath);
   }

   if (err != NULL) {
      *err = res;
   }

   if (res != 0) {
      FileLockAppendMessage(msgs, res);
   }

   return isLocked;
}


/*
 *----------------------------------------------------------------------
 *
 * FileLock_Unlock --
 *
 *      Release the lock held on the specified file.
 *
 * Results:
 *      TRUE   Success
 *      FALSE  Failure (errno to *err, msg to **msgs - when appropriate)
 *
 * Side effects:
 *      Changes the host file system.
 *
 *----------------------------------------------------------------------
 */

Bool
FileLock_Unlock(const FileLockToken *lockToken,  // IN:
                int *err,                        // OUT/OPT: returns errno
                MsgList **msgs)                  // IN/OUT/OPT: error messages
{
   int res;

   ASSERT(lockToken != NULL);

   res = FileUnlockIntrinsic((FileLockToken *) lockToken);

   if (err != NULL) {
      *err = res;
   }

   if (res != 0) {
      FileLockAppendMessage(msgs, res);
   }

   return (res == 0);
}
 0707010000004B000081A4000000000000000000000001682255050000E03F000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/file/fileLockPrimitive.c   /*********************************************************
 * Copyright (c) 2007-2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fileLockPrimitive.c --
 *
 *      Portable file locking via Lamport's Bakery algorithm.
 *
 *      This implementation relies upon a remove directory operation failing
 *      if the directory contains any files.
 */

#define _GNU_SOURCE /* For O_NOFOLLOW */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if defined(_WIN32)
#include <windows.h>
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <sys/param.h>
#endif
#include "vmware.h"
#include "hostinfo.h"
#include "util.h"
#include "err.h"
#include "log.h"
#include "str.h"
#include "fileIO.h"
#include "fileLock.h"
#include "fileInt.h"
#include "random.h"
#include "vm_atomic.h"
#include "util.h"
#include "hostType.h"

#include "unicodeOperations.h"

#define LOGLEVEL_MODULE main
#include "loglevel_user.h"

#define LOCK_SHARED     "S"
#define LOCK_EXCLUSIVE  "X"
#define FILELOCK_PROGRESS_DEARTH 8000 // Dearth of progress time in milliseconds
#define FILELOCK_PROGRESS_SAMPLE 200  // Progress sampling time in milliseconds

static char implicitReadToken;

#define PARSE_TABLE_UINT   0
#define PARSE_TABLE_STRING 1

typedef struct parse_table
{
   int         type;
   const char *name;
   void       *valuePtr;
} ParseTable;

/*
 * The lock token. This is returned by the lock operation and must be sent
 * to the unlock operation.
 */

#define FILELOCK_TOKEN_SIGNATURE 0x4B434C46  // 'FLCK' in memory

struct FileLockToken
{
   uint32  signature;
   Bool    portable;
   char   *pathName;
   union {
      struct {
         FileIODescriptor lockFd;
      } mandatory;
      struct {
         char *lockFilePath;  // &implicitReadToken for implicit read locks
      } portable;
   } u;
};


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockSleeper --
 *
 *      Have the calling thread sleep "for a while". The duration of the
 *      sleep is determined by the count that is passed in. Checks are
 *      also done for exceeding the maximum wait time.
 *
 * Results:
 *      0       slept
 *      EAGAIN  maximum sleep time exceeded
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockSleeper(LockValues *myValues)  // IN/OUT:
{
   VmTimeType ageMsec;
   uint32 maxSleepTimeMsec;

   if (myValues->maxWaitTimeMsec == FILELOCK_TRYLOCK_WAIT) {
      return EAGAIN;
   }

   ageMsec = Hostinfo_SystemTimerMS() - myValues->startTimeMsec;

   if ((myValues->maxWaitTimeMsec != FILELOCK_INFINITE_WAIT) &&
       (ageMsec >= myValues->maxWaitTimeMsec)) {
      return EAGAIN;
   }

   if (ageMsec <= 2000) {
      /* Most locks are "short" */
      maxSleepTimeMsec = 100;
   } else {
      /*
       * The lock has been around a while; use a continuously increasing back
       * off with an upper bound.
       */

      maxSleepTimeMsec = MIN(ageMsec / 10, 2000);
   }

   /*
    * Randomize the time slept. This will prevent any potential cadence issues
    * (thundering herds).
    */

   (void) FileSleeper(maxSleepTimeMsec / 2, maxSleepTimeMsec);

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockRemoveLockingFile --
 *
 *      Remove the specified file.
 *
 * Results:
 *      0       success
 *      > 0     failure (errno)
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockRemoveLockingFile(const char *lockDir,   // IN:
                          const char *fileName)  // IN:
{
   int err;
   char *path;

   ASSERT(lockDir != NULL);
   ASSERT(fileName != NULL);

   path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL);

   err = FileDeletionRobust(path, FALSE);

   if (err != 0) {
      if (err == ENOENT) {
         /* Not there anymore; locker unlocked or timed out */
         err = 0;
      } else {
         Warning(LGPFX" %s of '%s' failed: %s\n", __FUNCTION__,
                 path, Err_Errno2String(err));
      }
   }

   Posix_Free(path);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockParseArgs --
 *
 *      Parse the property list arguments of a lock file. The ParseTable
 *      contains names of properies that are interesting to the caller;
 *      only those values associated with the interesting names will be
 *      extracted, the others will be ignored.
 *
 * Results:
 *      TRUE    An error was detected
 *      FALSE   All is well
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
FileLockParseArgs(char *argv[],       // IN:
                  uint32 argCount,    // IN:
                  ParseTable *table,  // IN:
                  uint32 tableSize)   // IN:
{
   uint32 argPos = 5;  // The property list always starts with this argument

   while (argCount) {
      uint32 i;
      char *p = strchr(argv[argPos], '=');

      /* Validate the "name=value" form */
      if ((p == NULL) || (p == argv[argPos]) || (p[1] == '\0')) {
         return TRUE;
      }

      *p = '\0';

      /* Unknown names are ignored without error */
      for (i = 0; i < tableSize; i++) {
         if (strcmp(argv[argPos], table[i].name) == 0) {
            switch (table[i].type) {
            case PARSE_TABLE_UINT:
               if (sscanf(&p[1], "%u", (uint32 *) table[i].valuePtr) != 1) {
                  return TRUE;
               }
               break;

            case PARSE_TABLE_STRING:
               *((char **) table[i].valuePtr) = &p[1];
               break;
            }
         }
      }

      *p = '=';

      argPos++;
      argCount--;
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockMemberValues --
 *
 *      Returns the values associated with lock directory file.
 *
 * Results:
 *      0       Valid lock file; values have been returned
 *      > 0     Lock file problem (errno); values have not been returned
 *
 * Side effects:
 *      The lock file may be deleted if it is invalid
 *
 *-----------------------------------------------------------------------------
 */

#define FL_MAX_ARGS 16

int
FileLockMemberValues(const char *lockDir,       // IN:
                     const char *fileName,      // IN:
                     char *buffer,              // OUT:
                     size_t requiredSize,       // IN:
                     LockValues *memberValues)  // OUT:
{
   size_t len;
   int access;
   char *path;
   FileData fileData;
   FileIOResult result;
   FileIODescriptor desc;
   char *argv[FL_MAX_ARGS];

   int err = 0;
   uint32 argc = 0;
   char *saveptr = NULL;

   ParseTable table[] = {
                           { PARSE_TABLE_STRING,
                             "lc",
                             (void *) &memberValues->locationChecksum
                           }
                        };

   ASSERT(lockDir != NULL);
   ASSERT(fileName != NULL);

   path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL);

   FileIO_Invalidate(&desc);

   access = FILEIO_OPEN_ACCESS_READ;

#if defined(_WIN32)
   access |= FILEIO_OPEN_SHARE_DELETE;
#endif

   result = FileIOCreateRetry(&desc, path, access, FILEIO_OPEN, 0444,
                              FILE_MAX_WAIT_TIME_MS);

   if (!FileIO_IsSuccess(result)) {
      err = FileMapErrorToErrno(__FUNCTION__, Err_Errno());

      /*
       * A member file may "disappear" if is deleted (unlinked on POSIXen)
       * due to an unlock immediately after a directory scan but before the
       * scan is processed. Since this is a "normal" thing, ENOENT will be
       * suppressed.
       */

      if (err != ENOENT) {
         Warning(LGPFX" %s open failure on '%s': %s\n", __FUNCTION__,
                 path, Err_Errno2String(err));
      }

      goto bail;
   }

   /* Attempt to obtain the lock file attributes now that it is opened */
   err = FileAttributesRobust(path, &fileData);

   if (err != 0) {
      /*
       * A member file may "disappear" if is deleted (unlinked on POSIXen)
       * due to an unlock immediately after a directory scan but before the
       * scan is processed. Since this is a "normal" thing, ENOENT will be
       * suppressed.
       */

      if (err != ENOENT) {
         Warning(LGPFX" %s file size failure on '%s': %s\n", __FUNCTION__,
                 path, Err_Errno2String(err));
      }

      FileIO_Close(&desc);

      goto bail;
   }

   /* Complain if the lock file is not the proper size */
   if (fileData.fileSize != requiredSize) {
      Warning(LGPFX" %s file '%s': size %"FMT64"u, required size %"FMTSZ"d\n",
              __FUNCTION__, path, fileData.fileSize, requiredSize);

      FileIO_Close(&desc);

      goto corrupt;
   }

   /* Attempt to read the lock file data and validate how much was read. */
   result = FileIO_Read(&desc, buffer, requiredSize, &len);

   FileIO_Close(&desc);

   if (!FileIO_IsSuccess(result)) {
      err = FileMapErrorToErrno(__FUNCTION__, Err_Errno());

      Warning(LGPFX" %s read failure on '%s': %s\n",
              __FUNCTION__, path, Err_Errno2String(err));

      goto bail;
   }

   if (len != requiredSize) {
      Warning(LGPFX" %s read length issue on '%s': %"FMTSZ"d and %"FMTSZ"d\n",
              __FUNCTION__, path, len, requiredSize);

      err = EIO;
      goto bail;
   }

fixedUp:

   /* Extract and validate the lock file data. */
   for (argc = 0; argc < FL_MAX_ARGS; argc++) {
      argv[argc] = strtok_r((argc == 0) ? buffer : NULL, " ", &saveptr);

      if (argv[argc] == NULL) {
         break;
      }
   }

   /*
    * Lock file arguments are space separated. There is a minimum of 5
    * arguments - machineID, executionID, Lamport number, lock type
    * and process creation time. The maximum number of arguments is
    * FL_MAX_ARGS.
    *
    * Additional arguments, if present, form a property list - one or more
    * "name=value" pairs.
    *
    * Here is picture of valid forms:
    *
    * 0 1 2 3 4 5 6   Comment
    *-------------------------
    * A B C D E       No property list
    * A B C D E x     One property
    * A B C D E x y   Two properties
    */

   memberValues->locationChecksum = NULL;

   if ((argc < 5) || ((argc == FL_MAX_ARGS) &&
                       (strtok_r(NULL, " ", &saveptr) != NULL))) {
      goto corrupt;
   }

   if ((argc > 5) && FileLockParseArgs(argv, argc - 5,
                                       table, ARRAYSIZE(table))) {
      goto corrupt;
   }

   /*
    * Check for an old style lock file; if found, upgrade it (internally).
    *
    * The new style lock always has an executionID that is minimally
    * processID-processCreationTime (the '-' is the critical difference).
    */

   if ((strchr(argv[1], '-') == NULL) &&
       (strchr(argv[1], '(') == NULL) &&
       (strchr(argv[1], ')') == NULL) &&
       (argc == 6) &&
       !FileLockParseArgs(argv, argc - 5, table, ARRAYSIZE(table))) {
         char *newBuffer;

         newBuffer = Str_SafeAsprintf(NULL, "%s %s-%s %s %s %s %s",
                                      argv[0], argv[1], argv[4], argv[2],
                                      argv[3], argv[4], argv[5]);

        Str_Strcpy(buffer, newBuffer, requiredSize);

        Posix_Free(newBuffer);

        goto fixedUp;
  }

   if (sscanf(argv[2], "%u", &memberValues->lamportNumber) != 1) {
      goto corrupt;
   }

   if ((strcmp(argv[3], LOCK_SHARED) != 0) &&
       (strcmp(argv[3], LOCK_EXCLUSIVE) != 0)) {
      goto corrupt;
   }

   memberValues->machineID = argv[0];
   memberValues->executionID = argv[1];
   memberValues->lockType = argv[3];
   memberValues->memberName = Unicode_Duplicate(fileName);

   Posix_Free(path);

   return 0;

corrupt:
   Warning(LGPFX" %s removing problematic lock file '%s'\n", __FUNCTION__,
           path);

   if (argc) {
      uint32 i;

      Log(LGPFX" %s '%s' contents are:\n", __FUNCTION__, fileName);

      for (i = 0; i < argc; i++) {
         Log(LGPFX" %s %s argv[%u]: '%s'\n", __FUNCTION__, fileName,
             i, argv[i]);
      }
   }

   /* Remove the lock file and behave like it has disappeared */
   err = FileDeletionRobust(path, FALSE);

   if (err == 0) {
      err = ENOENT;
   }

bail:
   Posix_Free(path);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockValidName --
 *
 *      Validate the format of the file name.
 *
 * Results:
 *      TRUE    Yes
 *      FALSE   No
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
FileLockValidName(const char *fileName)  // IN:
{
   uint32 i;

   ASSERT(fileName != NULL);

   /* The fileName must start with the ASCII character, 'M', 'D' or 'E' */
   if (Unicode_FindSubstrInRange("MDE", 0, -1, fileName, 0,
                                 1) == UNICODE_INDEX_NOT_FOUND) {
      return FALSE;
   }

   /* The fileName must contain 5 ASCII digits after the initial character */
   for (i = 0; i < 5; i++) {
      if (Unicode_FindSubstrInRange("0123456789", 0, -1, fileName, i + 1,
                                    1) == UNICODE_INDEX_NOT_FOUND) {
         return FALSE;
      }
   }

   /* The fileName must terminate with the appropriate suffix string */
   return Unicode_EndsWith(fileName, FILELOCK_SUFFIX);
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockActivateList
 *
 *      Insure a lock list entry exists for the lock directory.
 *
 * Results:
 *     0        success
 *     > 0      error (errno)
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockActivateList(const char *dirName,   // IN:
                     LockValues *myValues)  // IN:
{
   ActiveLock   *ptr;

   ASSERT(dirName != NULL);
   ASSERT(*dirName == 'D');

   /* Search the list for a matching entry */
   for (ptr = myValues->lockList; ptr != NULL; ptr = ptr->next) {
      if (Unicode_Compare(ptr->dirName, dirName) == 0) {
         break;
      }
   }

   /* No entry? Attempt to add one. */
   if (ptr == NULL) {
      ptr = Util_SafeMalloc(sizeof *ptr);

      ptr->next = myValues->lockList;
      myValues->lockList = ptr;

      ptr->age = 0;
      ptr->dirName = Unicode_Duplicate(dirName);
   }

   /* Mark the entry (exists) */
   ptr->marked = TRUE;

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockLocationChecksum --
 *
 *      Compute the location checksum of the argument path.
 *
 * Results:
 *      The location checksum as dynamically allocated string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char *
FileLockLocationChecksum(const char *path)  // IN:
{
   int c;
   uint32 hash = 5381;

#if defined(_WIN32)
   char *p;
   char *value = Unicode_Duplicate(path);

   /* Don't get fooled by mixed case; "normalize" */
   Str_ToLower(value);
   p = value;
#else
   char *p = (char *) path;
#endif

   /* DBJ2 hash... good enough? */
   while ((c = *p++)) {
      hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
   }

#if defined(_WIN32)
   Posix_Free(value);
#endif

   return Str_SafeAsprintf(NULL, "%u", hash);
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockScanDirectory --
 *
 *      Call the specified function for each member file found in the
 *      specified directory.
 *
 * Results:
 *      0       success
 *      > 0     failure
 *
 * Side effects:
 *     Anything that this not a valid locking file is deleted.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockScanDirectory(const char *lockDir,      // IN:
                      int (*func)(              // IN:
                             const char *lockDir,
                             const char *fileName,
                             LockValues *memberValues,
                             LockValues *myValues
                           ),
                      LockValues *myValues,    // IN:
                      Bool cleanUp)            // IN:
{
   uint32 i;
   int err;
   int numEntries;

   char **fileList = NULL;
   char *myExecutionID = NULL;
   char *locationChecksum = NULL;

   ASSERT(lockDir != NULL);

   numEntries = FileListDirectoryRobust(lockDir, &fileList);

   if (numEntries == -1) {
      Log(LGPFX" %s: Could not read the directory '%s': %d\n",
          __FUNCTION__, lockDir, Err_Errno());

      return EDOM;  // out of my domain
   }

   /* Pass 1: Validate entries and handle any 'D' entries */
   for (i = 0, err = 0; i < numEntries; i++) {
      /* Remove any non-locking files */
      if (!FileLockValidName(fileList[i])) {
         Log(LGPFX" %s discarding %s from %s'; invalid file name.\n",
             __FUNCTION__, fileList[i], lockDir);

         err = FileLockRemoveLockingFile(lockDir, fileList[i]);
         if (err != 0) {
            goto bail;
         }

        Posix_Free(fileList[i]);
        fileList[i] = NULL;

        continue;
      }

      /*
       * Any lockers appear to be entering?
       *
       * This should be rather rare. If a locker dies while entering
       * this will cleaned-up.
       */

      if (*fileList[i] == 'D') {
         if (cleanUp) {
            err = FileLockActivateList(fileList[i], myValues);
            if (err != 0) {
               goto bail;
            }
        }

        Posix_Free(fileList[i]);
        fileList[i] = NULL;
      }
   }

   if (myValues->lockList != NULL) {
      goto bail;
   }

   myExecutionID = FileLockGetExecutionID();
   locationChecksum = FileLockLocationChecksum(lockDir);

   /* Pass 2: Handle the 'M' entries */
   for (i = 0, err = 0; i < numEntries; i++) {
      LockValues *ptr;
      Bool myLockFile;
      LockValues memberValues;
      char buffer[FILELOCK_DATA_SIZE];  // Must be near memberValues,
                                        // as it will be pointed by it

      if ((fileList[i] == NULL) || (*fileList[i] == 'E')) {
         continue;
      }

      myLockFile = (Unicode_Compare(fileList[i],
                          myValues->memberName) == 0) ? TRUE : FALSE;

      if (myLockFile) {
         /* It's me! No need to read or validate anything. */
         ptr = myValues;
      } else {
         /* It's not me! Attempt to extract the member values. */
         err = FileLockMemberValues(lockDir, fileList[i], buffer,
                                    FILELOCK_DATA_SIZE, &memberValues);

         if (err != 0) {
            if (err == ENOENT) {
               err = 0;
               /* Not there anymore; locker unlocked or timed out */
               continue;
            }

            break;
         }

         /* Remove any stale locking files */
         if (FileLockMachineIDMatch(myValues->machineID,
                                    memberValues.machineID)) {
            char *dispose = NULL;

            if (FileLockValidExecutionID(memberValues.executionID)) {
               /* If it's mine it better still be where I put it! */
               if ((strcmp(myExecutionID, memberValues.executionID) == 0) &&
                   ((memberValues.locationChecksum != NULL) &&
                    (strcmp(memberValues.locationChecksum,
                            locationChecksum) != 0))) {
                  dispose = Unicode_Duplicate("lock file has been moved.");
               }
            } else {
               dispose = Str_SafeAsprintf(NULL, "invalid executionID %s.",
                                          memberValues.executionID);
            }

            if (dispose) {
               Log(LGPFX" %s discarding %s from %s': %s\n",
                   __FUNCTION__, fileList[i], lockDir, dispose);

               Posix_Free(dispose);
               Posix_Free(memberValues.memberName);

               err = FileLockRemoveLockingFile(lockDir, fileList[i]);
               if (err != 0) {
                  break;
               }

               continue;
            }
         }

         ptr = &memberValues;
      }

      /* Locking file looks good; see what happens */
      err = (*func)(lockDir, fileList[i], ptr, myValues);

      if (ptr == &memberValues) {
         Posix_Free(memberValues.memberName);
      }

      if (err != 0) {
         break;
      }
   }

bail:

   Util_FreeStringList(fileList, numEntries);

   Posix_Free(locationChecksum);
   Posix_Free(myExecutionID);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockScanner --
 *
 *      Call the specified function for each member file found in the
 *      specified directory. If a rescan is necessary check the list
 *      of outstanding locks and handle removing stale locks.
 *
 * Results:
 *     0        success
 *     > 0      failure
 *
 * Side effects:
 *     None
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockScanner(const char *lockDir,     // IN:
                int (*func)(             // IN:
                       const char *lockDir,
                       const char *fileName,
                       LockValues *memberValues,
                       LockValues *myValues
                    ),
                LockValues *myValues,    // IN:
                Bool cleanUp)            // IN:
{
   int        err;
   ActiveLock *ptr;

   ASSERT(lockDir != NULL);

   myValues->lockList = NULL;

   while (TRUE) {
      ActiveLock *prev;

      err = FileLockScanDirectory(lockDir, func, myValues, cleanUp);
      if ((err > 0) || ((err == 0) && (myValues->lockList == NULL))) {
         break;
      }

      prev = NULL;
      ptr = myValues->lockList;

      /*
       * Some 'D' entries have persisted. Age them and remove those that
       * have not progressed. Remove those that have disappeared.
       */

      while (ptr != NULL) {
         Bool remove;

         if (ptr->marked) {
            if (ptr->age > FILELOCK_PROGRESS_DEARTH) {
               char *temp;
               char *path;
               UnicodeIndex index;

               ASSERT(*ptr->dirName == 'D');

               Log(LGPFX" %s discarding %s data from '%s'.\n",
                   __FUNCTION__, ptr->dirName, lockDir);

               path = Unicode_Join(lockDir, DIRSEPS, ptr->dirName, NULL);

               index = Unicode_FindLast(path, "D");
               ASSERT(index != UNICODE_INDEX_NOT_FOUND);

               temp = Unicode_Replace(path, index, 1, "M");
               FileDeletionRobust(temp, FALSE);
               Posix_Free(temp);

               temp = Unicode_Replace(path, index, 1, "E");
               FileDeletionRobust(temp, FALSE);
               Posix_Free(temp);

               FileRemoveDirectoryRobust(path);

               Posix_Free(path);

               remove = TRUE;
            } else {
               ptr->marked = FALSE;
               ptr->age += FILELOCK_PROGRESS_SAMPLE;

               remove = FALSE;
            }
         } else {
            remove = TRUE;
         }

         if (remove) {
            ActiveLock *temp = ptr;

            ptr = ptr->next;
            Posix_Free(temp->dirName);
            Posix_Free(temp);

            if (prev == NULL) {
               myValues->lockList = ptr;
            } else {
               prev->next = ptr;
            }
         } else {
            prev = ptr;
            ptr = ptr->next;
         }
      }

      FileSleeper(FILELOCK_PROGRESS_SAMPLE,
                  FILELOCK_PROGRESS_SAMPLE); // relax
   }

   /* Clean up anything still on the list; they are no longer important */
   while (myValues->lockList != NULL) {
      ptr = myValues->lockList;
      myValues->lockList = ptr->next;

      Posix_Free(ptr->dirName);

      Posix_Free(ptr);
   }

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileUnlockIntrinsic --
 *
 *      Release a lock on a file.
 *
 * Results:
 *      0       unlocked
 *      > 0     errno
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
FileUnlockIntrinsic(FileLockToken *tokenPtr)  // IN:
{
   int err = 0;

   ASSERT(tokenPtr && (tokenPtr->signature == FILELOCK_TOKEN_SIGNATURE));

   LOG(1, "Requesting unlock on %s\n", tokenPtr->pathName);

   if (tokenPtr->portable) {
      /*
       * If the lockFilePath (a pointer) is the fixed-address token representing
       * an implicit read lock, there is no lock file and the token can simply
       * be discarded.
       */

      if (tokenPtr->u.portable.lockFilePath != &implicitReadToken) {
         char *lockDir;

         /* The lock directory path */
         lockDir = Unicode_Append(tokenPtr->pathName, FILELOCK_SUFFIX);

         /*
          * TODO: under vmx86_debug validate the contents of the lock file as
          *       matching the machineID and executionID.
          */

         err = FileDeletionRobust(tokenPtr->u.portable.lockFilePath, FALSE);

         FileRemoveDirectoryRobust(lockDir); // just in case we can clean up

         if (err && vmx86_debug) {
            Log(LGPFX" %s failed for '%s': %s\n", __FUNCTION__,
                tokenPtr->u.portable.lockFilePath, Err_Errno2String(err));
         }
         Posix_Free(lockDir);
         Posix_Free(tokenPtr->u.portable.lockFilePath);
      }

      tokenPtr->u.portable.lockFilePath = NULL;  // Just in case...
   } else {
      ASSERT(FileIO_IsValid(&tokenPtr->u.mandatory.lockFd));

      if (!FileIO_IsSuccess(FileIO_CloseAndUnlink(&tokenPtr->u.mandatory.lockFd))) {
         /*
          * Should succeed, but there is an unavoidable race:
          * close() must precede unlink(), but another thread could touch
          * the file between close() and unlink(). We only worry about other
          * FileLock-like manipulations; the advisory lock file should not
          * experience any name collisions. Treat races as success.
          * Specific errors:
          *    EBUSY: other locked file
          *    ENOENT: other locked + unlocked (w/ implicit unlink) file
          */
         if (Err_Errno() == EBUSY || Err_Errno() == ENOENT) {
            LOG(0, "Tolerating %s on unlink of advisory lock at %s\n",
                Err_Errno() == EBUSY ? "EBUSY" : "ENOENT", tokenPtr->pathName);
         } else {
            err = Err_Errno();
            if (vmx86_debug) {
               Log(LGPFX" %s failed for advisory lock '%s': %s\n", __FUNCTION__,
                   tokenPtr->pathName, Err_Errno2String(err));
            }
         }
      }
   }

   Posix_Free(tokenPtr->pathName);
   tokenPtr->signature = 0;        // Just in case...
   tokenPtr->pathName = NULL;      // Just in case...
   Posix_Free(tokenPtr);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockWaitForPossession --
 *
 *      Wait until the caller has a higher priority towards taking
 *      possession of a lock than the specified file.
 *
 * Results:
 *     0        success
 *     > 0      error (errno)
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockWaitForPossession(const char *lockDir,       // IN:
                          const char *fileName,      // IN:
                          LockValues *memberValues,  // IN:
                          LockValues *myValues)      // IN:
{
   int err = 0;

   ASSERT(lockDir != NULL);
   ASSERT(fileName != NULL);

   /* "Win" or wait? */
   if (((memberValues->lamportNumber < myValues->lamportNumber) ||
       ((memberValues->lamportNumber == myValues->lamportNumber) &&
          (Unicode_Compare(memberValues->memberName,
                           myValues->memberName) < 0))) &&
        ((strcmp(memberValues->lockType, LOCK_EXCLUSIVE) == 0) ||
         (strcmp(myValues->lockType, LOCK_EXCLUSIVE) == 0))) {
      Bool thisMachine = FileLockMachineIDMatch(myValues->machineID,
                                                memberValues->machineID);
      char *path = Unicode_Join(lockDir, DIRSEPS, fileName, NULL);

      while ((err = FileLockSleeper(myValues)) == 0) {
         /* still there? */
         err = FileAttributesRobust(path, NULL);
         if (err != 0) {
            if (err == ENOENT) {
               /* Not there anymore; locker unlocked or timed out */
               err = 0;
            }

            break;
         }

         /* still valid? */
         if (thisMachine &&
             !FileLockValidExecutionID(memberValues->executionID)) {
            /* Invalid Execution ID; remove the member file */
            Warning(LGPFX" %s discarding file '%s'; invalid executionID.\n",
                    __FUNCTION__, path);

            err = FileLockRemoveLockingFile(lockDir, fileName);
            break;
         }
      }

      /*
       * Log the disposition of each timeout for all non "try lock" locking
       * attempts. This can assist in debugging locking problems.
       */

      if ((myValues->maxWaitTimeMsec != FILELOCK_TRYLOCK_WAIT) &&
          (err == EAGAIN)) {
         if (thisMachine) {
            Log(LGPFX" %s timeout on '%s' due to a local process '%s'\n",
                __FUNCTION__, path, memberValues->executionID);
         } else {
            Log(LGPFX" %s timeout on '%s' due to another machine '%s'\n",
                __FUNCTION__, path, memberValues->machineID);
         }
      }

      Posix_Free(path);
   }

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockNumberScan --
 *
 *      Determine the maxmimum number value within the current locking set.
 *
 * Results:
 *     0        success
 *     > 0      failure (errno)
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockNumberScan(const char *lockDir,       // IN:
                   const char *fileName,      // IN:
                   LockValues *memberValues,  // IN:
                   LockValues *myValues)      // IN/OUT:
{
   ASSERT(lockDir != NULL);
   ASSERT(fileName != NULL);

   if (memberValues->lamportNumber > myValues->lamportNumber) {
      myValues->lamportNumber = memberValues->lamportNumber;
   }

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockMakeDirectory --
 *
 *      Create a directory.
 *
 * Results:
 *      0       success
 *      > 0     failure (errno)
 *
 * Side Effects:
 *      File system may be modified.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockMakeDirectory(const char *pathName)  // IN:
{
   int err;

#if !defined(_WIN32)
   mode_t save;

   save = umask(0);
#endif

   ASSERT(pathName != NULL);

   err = FileCreateDirectoryRobust(pathName, 0777);

#if !defined(_WIN32)
   umask(save);
#endif

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockCreateEntryDirectory --
 *
 *      Create an entry directory in the specified locking directory.
 *
 *      Due to FileLock_Unlock() attempting to remove the locking
 *      directory on an unlock operation (to "clean up" and remove the
 *      locking directory when it is no longer needed), this routine
 *      must carefully handle a number of race conditions to insure the
 *      the locking directory exists and the entry directory is created
 *      within.
 *
 * Results:
 *      0       success
 *      > 0     failure (errno)
 *
 * Side Effects:
 *      On success returns the number identifying the entry directory and
 *      the entry directory path name.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockCreateEntryDirectory(const char *lockDir,    // IN:
                             char **entryDirectory,  // OUT:
                             char **entryFilePath,   // OUT:
                             char **memberFilePath,  // OUT:
                             char **memberName)      // OUT:
{
   int err;
   VmTimeType startTimeMsec;

   ASSERT(lockDir != NULL);

   *entryDirectory = NULL;
   *entryFilePath = NULL;
   *memberFilePath = NULL;
   *memberName = NULL;

   /* Fun at the races */
   startTimeMsec = Hostinfo_SystemTimerMS();

   while (TRUE) {
      char *temp;
      FileData fileData;
      VmTimeType ageMsec;
      uint32 randomNumber;

      err = FileAttributesRobust(lockDir, &fileData);
      if (err == 0) {
        /* The name exists. Deal with it... */

        if (fileData.fileType != FILE_TYPE_DIRECTORY) {
           /*
            * Locks are implemented via directories. Ancient hosted locks and
            * lock files imported alongsize an ESXi VM are implemented as
            * files. It's safe to remove these. The ESXi lock files are
            * meaningless and the ancient hosted lock files have been dead
            * for over a decade (at the time of writing this comment).
            */

           err = FileDeletionRobust(lockDir, FALSE);
           if (err == 0) {
              Warning(LGPFX" %s: '%s' is not a directory. Removed.\n",
                      __FUNCTION__, lockDir);
           } else {
              Warning(LGPFX" %s: an attempt to remove '%s' failed: %s\n",
                      __FUNCTION__, lockDir, Err_Errno2String(err));
              break;
           }

           continue;
        }
      } else {
         if (err == ENOENT) {
            /* Not there anymore; locker unlocked or timed out */
            err = FileLockMakeDirectory(lockDir);

            if ((err != 0) && (err != EEXIST)) {
               Warning(LGPFX" %s creation failure on '%s': %s\n",
                       __FUNCTION__, lockDir, Err_Errno2String(err));

               break;
            }
         } else {
            Warning(LGPFX" %s stat failure on '%s': %s\n",
                    __FUNCTION__, lockDir, Err_Errno2String(err));

            break;
         }
      }

      /* There is a small chance of collision/failure; grab stings now */
      randomNumber = (FileSimpleRandom() >> 8) & 0xFFFF;

      *memberName = Unicode_Format("M%05u%s", randomNumber, FILELOCK_SUFFIX);

      temp = Unicode_Format("D%05u%s", randomNumber, FILELOCK_SUFFIX);
      *entryDirectory = Unicode_Join(lockDir, DIRSEPS, temp, NULL);
      Posix_Free(temp);

      temp = Unicode_Format("E%05u%s", randomNumber, FILELOCK_SUFFIX);
      *entryFilePath = Unicode_Join(lockDir, DIRSEPS, temp, NULL);
      Posix_Free(temp);

      *memberFilePath = Unicode_Join(lockDir, DIRSEPS, *memberName, NULL);

      err = FileLockMakeDirectory(*entryDirectory);

      if (err == 0) {
         /*
          * The entry directory was safely created. See if a member file
          * is in use (the entry directory is removed once the member file
          * is created). If a member file is in use, choose another number,
          * otherwise the use of the this number is OK.
          *
          * Err on the side of caution... don't want to trash perfectly
          * good member files.
          */

         err = FileAttributesRobust(*memberFilePath, NULL);

         if (err != 0) {
            if (err == ENOENT) {
               err = 0;
               break;
            }

            if (vmx86_debug) {
               Log(LGPFX" %s stat failure on '%s': %s\n",
                   __FUNCTION__, *memberFilePath, Err_Errno2String(err));
             }
         }

         err = FileRemoveDirectoryRobust(*entryDirectory);

         if (err != 0) {
            Warning(LGPFX" %s unable to remove '%s': %s\n",
                    __FUNCTION__, *entryDirectory, Err_Errno2String(err));

            break;
         }
      } else {
          if ((err != EEXIST) &&  // Another process/thread created it...
              (err != ENOENT)) {  // lockDir is gone...
             Warning(LGPFX" %s creation failure on '%s': %s\n",
                     __FUNCTION__, *entryDirectory, Err_Errno2String(err));

             break;
          }
      }

      Posix_Free(*entryDirectory);
      Posix_Free(*entryFilePath);
      Posix_Free(*memberFilePath);
      Posix_Free(*memberName);

      *entryDirectory = NULL;
      *entryFilePath = NULL;
      *memberFilePath = NULL;
      *memberName = NULL;

      /*
       * If we've been trying to get the locking started for a unacceptable
       * amount of time, bail. Something is seriously wrong, probably the
       * file system or networking. Nothing we can do about it.
       */

      ageMsec = Hostinfo_SystemTimerMS() - startTimeMsec;

      if (ageMsec > FILELOCK_PROGRESS_DEARTH) {
         Warning(LGPFX" %s lack of progress on '%s'\n", __FUNCTION__, lockDir);

         err = EBUSY;
         break;
      }
   }

   if (err != 0) {
      Posix_Free(*entryDirectory);
      Posix_Free(*entryFilePath);
      Posix_Free(*memberFilePath);
      Posix_Free(*memberName);

      *entryDirectory = NULL;
      *entryFilePath = NULL;
      *memberFilePath = NULL;
      *memberName = NULL;
   }

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockCreateMemberFile --
 *
 *      Create the member file.
 *
 * Results:
 *     0        success
 *     > 0      failure (errno)
 *
 * Side Effects:
 *     None
 *
 *-----------------------------------------------------------------------------
 */

static int
FileLockCreateMemberFile(FileIODescriptor *desc,       // IN:
                         const LockValues *myValues,   // IN:
                         const char *entryFilePath,    // IN:
                         const char *memberFilePath)   // IN:
{
   int cnt;
   int pid;
   size_t len;
   FileIOResult result;
   uint64 processCreationTime;

   int err = 0;
   char buffer[FILELOCK_DATA_SIZE] = { 0 };

   ASSERT(entryFilePath != NULL);
   ASSERT(memberFilePath != NULL);

   /*
    * Populate the buffer with appropriate data
    *
    * Lock file arguments are space separated. There is a minimum of 5
    * arguments - machineID, executionID, Lamport number, lock type
    * and process creation time. The maximum number of arguments is
    * FL_MAX_ARGS.
    *
    * Additional arguments, if present, form a property list - one or more
    * "name=value" pairs.
    *
    * Yes, the process creation time is redundently encoded. This is necessary
    * to maintain backwards compatibility. Should an older code pick up a
    * newer lock file and there is lock contention, the older code will log
    * the name of the process causing the contention - it's also encoded
    * into the executionID.
    */

   cnt = sscanf(myValues->executionID, "%d-%"FMT64"u", &pid,
                &processCreationTime);

   ASSERT(cnt == 2);  // ensure new format executionID

   Str_Sprintf(buffer, sizeof buffer, "%s %s %u %s %"FMT64"u lc=%s",
               myValues->machineID,
               myValues->executionID,
               myValues->lamportNumber,
               myValues->lockType,
               processCreationTime,
               myValues->locationChecksum);

   /* Attempt to write the data */
   result = FileIO_Write(desc, buffer, sizeof buffer, &len);

   if (!FileIO_IsSuccess(result)) {
      err = FileMapErrorToErrno(__FUNCTION__, Err_Errno());

      Warning(LGPFX" %s write of '%s' failed: %s\n", __FUNCTION__,
              entryFilePath, Err_Errno2String(err));

      FileIO_Close(desc);

      return err;
   }

   if (!FileIO_IsSuccess(FileIO_Close(desc))) {
      err = FileMapErrorToErrno(__FUNCTION__, Err_Errno());

      Warning(LGPFX" %s close of '%s' failed: %s\n", __FUNCTION__,
              entryFilePath, Err_Errno2String(err));

      return err;
   }

   if (len != sizeof buffer) {
      Warning(LGPFX" %s write length issue on '%s': %"FMTSZ"d and %"FMTSZ"d\n",
              __FUNCTION__, entryFilePath, len, sizeof buffer);

      return EIO;
   }

   err = File_Rename(entryFilePath, memberFilePath);

   if (err != 0) {
      Warning(LGPFX" %s FileRename of '%s' to '%s' failed: %s\n",
              __FUNCTION__, entryFilePath, memberFilePath,
              Err_Errno2String(err));

      if (vmx86_debug) {
         Log(LGPFX" %s FileLockFileType() of '%s': %s\n",
             __FUNCTION__, entryFilePath,
            Err_Errno2String(FileAttributesRobust(entryFilePath, NULL)));

         Log(LGPFX" %s FileLockFileType() of '%s': %s\n",
             __FUNCTION__, memberFilePath,
            Err_Errno2String(FileAttributesRobust(memberFilePath, NULL)));
      }

      return err;
   }

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockIntrinsicMandatory --
 *
 *      Obtain a lock on a file; shared or exclusive access.
 *
 *      This implementation uses the FILEIO_OPEN_LOCK_MANDATORY flag,
 *      which requires kernel support for mandatory locking. Such locks
 *      are automatically broken if the host holding the lock fails.
 *
 *      maxWaitTimeMsec specifies the maximum amount of time, in
 *      milliseconds, to wait for the lock before returning the "not
 *      acquired" status. A value of FILELOCK_TRYLOCK_WAIT is the
 *      equivalent of a "try lock" - the lock will be acquired only if
 *      there is no contention. A value of FILELOCK_INFINITE_WAIT
 *      specifies "waiting forever" to acquire the lock.
 *
 * Results:
 *      NULL    Lock not acquired. Check err.
 *              err     0       Lock Timed Out
 *              err     > 0     errno
 *      !NULL   Lock Acquired. This is the "lockToken" for an unlock.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static FileLockToken *
FileLockIntrinsicMandatory(const char *pathName,   // IN:
                           const char *lockFile,   // IN:
                           LockValues *myValues,   // IN/OUT:
                           int *err)               // OUT:
{
   int access;
   int errnum;
   FileIOResult result;
   FileLockToken *tokenPtr = Util_SafeMalloc(sizeof *tokenPtr);

   tokenPtr->signature = FILELOCK_TOKEN_SIGNATURE;
   tokenPtr->portable = FALSE;
   tokenPtr->pathName = Unicode_Duplicate(pathName);
   FileIO_Invalidate(&tokenPtr->u.mandatory.lockFd);

   access = myValues->exclusivity ? FILEIO_OPEN_ACCESS_WRITE :
                                    FILEIO_OPEN_ACCESS_READ;
   access |= FILEIO_OPEN_EXCLUSIVE_LOCK;

   do {
      result = FileIOCreateRetry(&tokenPtr->u.mandatory.lockFd,
                                 lockFile, access,
                                 FILEIO_OPEN_CREATE, 0600,
                                 0);
      errnum = Err_Errno();
      if (result != FILEIO_LOCK_FAILED) {
         break;
      }
   } while (FileLockSleeper(myValues) == 0);

   if (FileIO_IsSuccess(result)) {
      ASSERT(FileIO_IsValid(&tokenPtr->u.mandatory.lockFd));
      *err = 0;

      return tokenPtr;
   } else {
      if (result == FILEIO_LOCK_FAILED) {
         *err = 0;
      } else {
         *err = FileMapErrorToErrno(__FUNCTION__, errnum);
      }
      Posix_Free(tokenPtr->pathName);
      ASSERT(!FileIO_IsValid(&tokenPtr->u.mandatory.lockFd));
      Posix_Free(tokenPtr);

      return NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockIntrinsicPortable --
 *
 *      Obtain a lock on a file; shared or exclusive access.
 *
 *      This implementation uses a HIGHLY portable directory-namespace +
 *      Lamport bakery scheme that works on all filesystems that provide
 *      atomicity of the directory namespace (That is, all known filesystems).
 *      The various files involved are hidden within a "pathName.lck/"
 *      subdirectory.
 *
 *      The lock can be broken by removing the subdirectory. The lock
 *      is self-cleaning on the same host (e.g. will detect a dead process
 *      and will break the lock), but NOT self-cleaning across hosts. The
 *      lock does not require any sort of time-based leases or heartbeats.
 *
 * Results:
 *      NULL    Lock not acquired. Check err.
 *              err     0       Lock Timed Out
 *              err     > 0     errno
 *      !NULL   Lock Acquired. This is the "lockToken" for an unlock.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static FileLockToken *
FileLockIntrinsicPortable(const char *pathName,   // IN:
                          const char *lockDir,    // IN:
                          LockValues *myValues,   // IN/OUT:
                          int *err)               // OUT:
{
   int access;
   FileIOResult result;
   FileIODescriptor desc;
   FileLockToken *tokenPtr;

   char *entryFilePath = NULL;
   char *memberFilePath = NULL;
   char *entryDirectory = NULL;

   ASSERT(pathName != NULL);
   ASSERT(err != NULL);

   /*
    * Attempt to create the locking and entry directories; obtain the
    * entry and member path names.
    */

   *err = FileLockCreateEntryDirectory(lockDir, &entryDirectory,
                                       &entryFilePath, &memberFilePath,
                                       &myValues->memberName);

   switch (*err) {
   case 0:
      break;

   case EROFS:
      /* FALL THROUGH */
   case EACCES:
      if (!myValues->exclusivity) {
         /*
          * Lock is for read/shared access however the lock directory could
          * not be created. Grant an implicit read lock whenever possible.
          * The address of a private variable will be used for the lock token.
          */

         Warning(LGPFX" %s implicit %s lock succeeded on '%s'.\n",
                 __FUNCTION__, LOCK_SHARED, pathName);

         *err = 0;
         memberFilePath = &implicitReadToken;
      }

      /* FALL THROUGH */
   default:
      goto bail;
   }

   ASSERT(Unicode_LengthInCodeUnits(memberFilePath) -
          Unicode_LengthInCodeUnits(pathName) <= FILELOCK_OVERHEAD);

   /* Attempt to create the entry file */
   access = FILEIO_OPEN_ACCESS_WRITE;

#if defined(_WIN32)
   access |= FILEIO_OPEN_SHARE_DELETE;
#else
   access |= FILEIO_OPEN_ACCESS_NOFOLLOW;
#endif

   FileIO_Invalidate(&desc);

   result = FileIOCreateRetry(&desc, entryFilePath, access,
                              FILEIO_OPEN_CREATE_SAFE, 0644,
                              FILE_MAX_WAIT_TIME_MS);

   if (!FileIO_IsSuccess(result)) {
      *err = FileMapErrorToErrno(__FUNCTION__, Err_Errno());

      /* clean up */
      FileRemoveDirectoryRobust(entryDirectory);
      FileRemoveDirectoryRobust(lockDir);

      goto bail;
   }

   /* What is max(Number[1]... Number[all lockers])? */
   *err = FileLockScanner(lockDir, FileLockNumberScan, myValues, FALSE);

   if (*err != 0) {
      /* clean up */
      FileIO_Close(&desc);
      FileDeletionRobust(entryFilePath, FALSE);
      FileRemoveDirectoryRobust(entryDirectory);
      FileRemoveDirectoryRobust(lockDir);

      goto bail;
   }

   /* Number[i] = 1 + max([Number[1]... Number[all lockers]) */
   myValues->lamportNumber++;

   /* Attempt to create the member file */
   *err = FileLockCreateMemberFile(&desc, myValues, entryFilePath,
                                   memberFilePath);

   /* Remove entry directory; it has done its job */
   if (*err == 0) {
      *err = FileRemoveDirectoryRobust(entryDirectory);
   }

   if (*err != 0) {
      /* clean up */
      FileDeletionRobust(entryFilePath, FALSE);
      FileDeletionRobust(memberFilePath, FALSE);
      FileRemoveDirectoryRobust(lockDir);

      goto bail;
   }

   /* Attempt to acquire the lock */
   *err = FileLockScanner(lockDir, FileLockWaitForPossession, myValues, TRUE);

   switch (*err) {
   case 0:
      break;

   case EAGAIN:
      /* clean up */
      FileDeletionRobust(memberFilePath, FALSE);
      FileRemoveDirectoryRobust(lockDir);

      /* FALL THROUGH */
   default:
      break;
   }

bail:

   Posix_Free(entryDirectory);
   Posix_Free(entryFilePath);

   if (*err == 0) {
      tokenPtr = Util_SafeMalloc(sizeof *tokenPtr);

      tokenPtr->signature = FILELOCK_TOKEN_SIGNATURE;
      tokenPtr->portable = TRUE;
      tokenPtr->pathName = Unicode_Duplicate(pathName);
      tokenPtr->u.portable.lockFilePath = memberFilePath;
   } else {
      Posix_Free(memberFilePath);
      tokenPtr = NULL;

      if (*err == EAGAIN) {
         *err = 0; // lock not acquired
      }
   }

   return tokenPtr;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockIntrinsic --
 *
 *      Obtain a lock on a file; shared or exclusive access.
 *
 *      All FileLock_-based locks are advisory locks (i.e. the
 *      lock is maintained separately from the file so only FileLock_
 *      callers experience locking). Advisory locks have an inherent problem
 *      that they are difficult to break in the event one of the cooperating
 *      entities fails, particularly across distributed filesystems.
 *
 *      This wrapper function will adaptively switch between a scheme
 *      implemented via mandatory locks and a more portable scheme depending
 *      on host OS support.
 *
 *      maxWaitTimeMsec specifies the maximum amount of time, in
 *      milliseconds, to wait for the lock before returning the "not
 *      acquired" status. A value of FILELOCK_TRYLOCK_WAIT is the
 *      equivalent of a "try lock" - the lock will be acquired only if
 *      there is no contention. A value of FILELOCK_INFINITE_WAIT
 *      specifies "waiting forever" to acquire the lock.
 *
 * Results:
 *      NULL    Lock not acquired. Check err.
 *              err     0       Lock Timed Out
 *              err     > 0     errno
 *      !NULL   Lock Acquired. This is the "lockToken" for an unlock.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

FileLockToken *
FileLockIntrinsic(const char *pathName,    // IN:
                  Bool exclusivity,        // IN:
                  uint32 maxWaitTimeMsec,  // IN:
                  int *err)                // OUT:
{
   char *lockBase;
   LockValues myValues = { 0 };
   FileLockToken *tokenPtr;

   /* Construct the locking directory path */
   lockBase = Unicode_Append(pathName, FILELOCK_SUFFIX);

   myValues.lockType = exclusivity ? LOCK_EXCLUSIVE : LOCK_SHARED;
   myValues.exclusivity = exclusivity;
   myValues.startTimeMsec = Hostinfo_SystemTimerMS();
   myValues.maxWaitTimeMsec = maxWaitTimeMsec;

   if (File_SupportsMandatoryLock(pathName)) {
      LOG(1, "Requesting %s lock on %s (mandatory, %u).\n",
          myValues.lockType, pathName, myValues.maxWaitTimeMsec);

      tokenPtr = FileLockIntrinsicMandatory(pathName, lockBase, &myValues, err);
   } else {
      myValues.machineID = (char *) FileLockGetMachineID(); // don't free this!
      myValues.executionID = FileLockGetExecutionID();      // free this!
      myValues.lamportNumber = 0;
      myValues.locationChecksum = FileLockLocationChecksum(lockBase); // free this!
      myValues.memberName = NULL;

      LOG(1, "Requesting %s lock on %s (%s, %s, %u).\n",
          myValues.lockType, pathName, myValues.machineID,
          myValues.executionID, myValues.maxWaitTimeMsec);

      tokenPtr = FileLockIntrinsicPortable(pathName, lockBase, &myValues, err);

      Posix_Free(myValues.memberName);
      Posix_Free(myValues.locationChecksum);
      Posix_Free(myValues.executionID);
   }

   Posix_Free(lockBase);

   return tokenPtr;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockIsLockedMandatory --
 *
 *      Is a file currently locked (at the time of the call)?
 *
 *      The only way to check for a mandatory lock is to try opening
 *      the file (and quickly closing it again). If the lock is held,
 *      attempting to open the file will return FILEIO_LOCK_FAILED.
 *
 * Results:
 *      TRUE    YES
 *      FALSE   NO; if err is not NULL may check *err for an error
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
FileLockIsLockedMandatory(const char *lockFile,  // IN:
                          int *err)              // OUT/OPT:
{
   int access;
   FileIOResult result;
   FileIODescriptor desc;

   FileIO_Invalidate(&desc);

   /*
    * Check for lock by actually locking file, and dropping
    * lock quickly if open was successful.
    */

   access = FILEIO_OPEN_ACCESS_READ | FILEIO_OPEN_ACCESS_WRITE |
            FILEIO_OPEN_EXCLUSIVE_LOCK;

   result = FileIOCreateRetry(&desc, lockFile, access, FILEIO_OPEN, 0644, 0);

   if (FileIO_IsSuccess(result)) {
      Bool success;

      success = FileIO_IsSuccess(FileIO_Close(&desc));

      ASSERT(success);
      return FALSE;
   } else if (result == FILEIO_LOCK_FAILED) {
      return TRUE;   // locked
   } else if (result == FILEIO_FILE_NOT_FOUND) {
      return FALSE;  // no lock file means unlocked
   } else {
      if (err != NULL) {
         *err = FileMapErrorToErrno(__FUNCTION__, Err_Errno());
      }

      return FALSE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockIsLockedPortable --
 *
 *      Is a file currently locked (at the time of the call)?
 *
 *      The "portable" lock is held if the lock directory exists and
 *      there are any "M" entries (representing held locks).
 *
 *      FileLocks implemented via mandatory locking are reported
 *      as held locks (errno == ENOTDIR).
 *
 * Results:
 *      TRUE    YES
 *      FALSE   NO; if err is not NULL may check *err for an error
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
FileLockIsLockedPortable(const char *lockDir,  // IN:
                         int *err)             // OUT/OPT:
{
   uint32 i;
   int numEntries;
   Bool isLocked = FALSE;
   char **fileList = NULL;

   numEntries = FileListDirectoryRobust(lockDir, &fileList);

   if (numEntries == -1) {
      /*
       * If the lock directory doesn't exist, we should not count this
       * as an error.  This is expected if the file isn't locked.
       */

      if (err != NULL) {
         *err = (errno == ENOENT) ? 0 : errno;
      }

      return FALSE;
   }

   for (i = 0; i < numEntries; i++) {
      if (*fileList[i] == 'M') {
         isLocked = TRUE;
         break;
      }
   }

   Util_FreeStringList(fileList, numEntries);

   return isLocked;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileLockIsLocked --
 *
 *      Is a file currently locked (at the time of the call)?
 *
 * Results:
 *      TRUE    YES
 *      FALSE   NO; if err is not NULL may check *err for an error
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
FileLockIsLocked(const char *pathName,  // IN:
                 int *err)              // OUT/OPT:
{
   Bool isLocked;
   char *lockBase;

   ASSERT(pathName != NULL);

   lockBase = Unicode_Append(pathName, FILELOCK_SUFFIX);

   if (File_SupportsMandatoryLock(pathName)) {
      isLocked = FileLockIsLockedMandatory(lockBase, err);
   } else {
      isLocked = FileLockIsLockedPortable(lockBase, err);
   }

   Posix_Free(lockBase);

   return isLocked;
}


/*
 *----------------------------------------------------------------------
 *
 * FileLock_TokenPathName --
 *
 *      Return the path name associated with a lock (token). The path name
 *      is returned as a dynamically allocated string the caller is
 *      responsible for.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

char *
FileLock_TokenPathName(const FileLockToken *lockToken)  // IN:
{
   ASSERT(lockToken && (lockToken->signature == FILELOCK_TOKEN_SIGNATURE));

   return Unicode_Duplicate(lockToken->pathName);
}
 0707010000004C000081A40000000000000000000000016822550500016D78000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/file/filePosix.c   /*********************************************************
 * Copyright (c) 2006-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * filePosix.c --
 *
 *      Interface to Posix-specific file functions.
 */

#include <sys/types.h> /* Needed before sys/vfs.h with glibc 2.0 --hpreg */

#if defined(__FreeBSD__)
# include <sys/param.h>
# include <sys/mount.h>
#else
# if !defined(__APPLE__)
#  include <sys/vfs.h>
# endif
# include <limits.h>
# include <stdio.h>      /* Needed before sys/mnttab.h in Solaris */
# if defined(sun)
#  include <sys/mnttab.h>
# elif __APPLE__
#  include <sys/mount.h>
# else
#  include <mntent.h>
# endif
#include <signal.h>
#endif
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#if !defined(__USE_ATFILE)
#define __USE_ATFILE
#endif
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
#if defined(__linux__)
#   include <pwd.h>
#endif
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif

#include "vmware.h"
#include "posix.h"
#include "codeset.h"
#include "file.h"
#include "fileInt.h"
#include "msg.h"
#include "util.h"
#include "str.h"
#include "util.h"
#include "timeutil.h"
#include "dynbuf.h"
#include "hostType.h"
#include "vmfs.h"
#include "hashTable.h"
#include "hostinfo.h"
#include "log.h"

#ifdef VMX86_SERVER
#include "fs_public.h"
#include "fs3Layout.h"
#endif

#define LOGLEVEL_MODULE main
#include "loglevel_user.h"

#include "unicodeOperations.h"

#if !defined(__FreeBSD__) && !defined(sun)
#if !defined(__APPLE__)
static char *FilePosixLookupMountPoint(char const *canPath, Bool *bind);
#endif
static char *FilePosixNearestExistingAncestor(char const *path);

#if defined(VMX86_SERVER)
#define VMFS3CONST 256
#include "hostType.h"
/* Needed for VMFS implementation of File_GetFreeSpace() */
#  include <sys/ioctl.h>
# endif
#endif

#if defined(VMX86_SERVER)
#include "fs_user.h"
#endif

struct WalkDirContextImpl {
   char       *dirName;
   DIR        *dir;
   HashTable  *hash;
};


/* A string for NFS on ESX file system type */
#define FS_NFS_PREFIX_LEN 3
#define FS_NFS_ON_ESX "NFS"
/* A string for VMFS on ESX file system type */
#define FS_VMFS_ON_ESX "VMFS"
/* A string for vsanD on ESX file system type */
#define FS_VSAND_ON_ESX "vsanD"
#define FS_VSAN_URI_PREFIX      "vsan:"

#if defined __ANDROID__
/*
 * Android doesn't support setmntent(), endmntent() or MOUNTED.
 */
#define NO_SETMNTENT
#define NO_ENDMNTENT
#endif

/* Long path chunk growth size */
#define FILE_PATH_GROW_SIZE 1024


/*
 *-----------------------------------------------------------------------------
 *
 * FileRemoveDirectory --
 *
 *      Delete a directory.
 *
 * Results:
 *      0 success
 *    > 0 failure (errno)
 *
 * Side effects:
 *      May change the host file system.
 *
 *-----------------------------------------------------------------------------
 */

int
FileRemoveDirectory(const char *pathName)  // IN:
{
   return (Posix_Rmdir(pathName) == -1) ? errno : 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_Rename --
 *
 *      Rename a file.
 *
 * Results:
 *        0  success
 *      > 0  failure (errno)
 *
 * Side effects:
 *      May change the host file system.
 *
 *-----------------------------------------------------------------------------
 */

int
File_Rename(const char *oldName,  // IN:
            const char *newName)  // IN:
{
   return (Posix_Rename(oldName, newName) == -1) ? errno : 0;
}


int
File_RenameRetry(const char *oldFile,     // IN:
                 const char *newFile,     // IN:
                 uint32 maxWaitTimeMsec)  // IN: Unused.
{
   return File_Rename(oldFile, newFile);
}


/*
 *----------------------------------------------------------------------
 *
 *  FileDeletion --
 *      Delete the specified file.  A NULL pathName will result in an error
 *      and errno will be set to EFAULT.
 *
 * Results:
 *        0  success
 *      > 0  failure (errno)
 *
 * Side effects:
 *      May change the host file system.  errno may be set.
 *
 *----------------------------------------------------------------------
 */

int
FileDeletion(const char *pathName,   // IN:
             const Bool handleLink)  // IN:
{
   int err;

   if (pathName == NULL) {
      errno = EFAULT;

      return errno;
   }

   if (handleLink) {
      char *linkPath = Posix_ReadLink(pathName);

      if (linkPath == NULL) {
         /* If there is no link involved, continue */
         err = errno;

         if (err != EINVAL) {
            goto bail;
         }
      } else {
         err = (Posix_Unlink(linkPath) == -1) ? errno : 0;

         Posix_Free(linkPath);

         /* Ignore a file that has already disappeared */
         if (err != ENOENT) {
            goto bail;
         }
      }
   }

   err = (Posix_Unlink(pathName) == -1) ? errno : 0;

bail:

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_UnlinkDelayed --
 *
 *      Same as File_Unlink for POSIX systems since we can unlink anytime.
 *
 * Results:
 *      Return 0 if the unlink is successful.   Otherwise, returns -1.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
File_UnlinkDelayed(const char *pathName)  // IN:
{
   return (FileDeletion(pathName, TRUE) == 0) ? 0 : -1;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileAttributes --
 *
 *      Return the attributes of a file. Time units are in OS native time.
 *
 * Results:
 *      0    success
 *    > 0  failure (errno)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
FileAttributes(const char *pathName,  // IN:
               FileData *fileData)    // OUT:
{
   int err;
   struct stat statbuf;

   if (Posix_Stat(pathName, &statbuf) == -1) {
      err = errno;
   } else {
      if (fileData != NULL) {
         fileData->fileCreationTime = statbuf.st_ctime;
         fileData->fileModificationTime = statbuf.st_mtime;
         fileData->fileAccessTime = statbuf.st_atime;
         fileData->fileSize = statbuf.st_size;

         switch (statbuf.st_mode & S_IFMT) {
         case S_IFREG:
            fileData->fileType = FILE_TYPE_REGULAR;
            break;

         case S_IFDIR:
            fileData->fileType = FILE_TYPE_DIRECTORY;
            break;

         case S_IFBLK:
            fileData->fileType = FILE_TYPE_BLOCKDEVICE;
            break;

         case S_IFCHR:
            fileData->fileType = FILE_TYPE_CHARDEVICE;
            break;

         case S_IFLNK:
            fileData->fileType = FILE_TYPE_SYMLINK;
            break;

         default:
            fileData->fileType = FILE_TYPE_UNCERTAIN;
            break;
         }

         fileData->fileMode = statbuf.st_mode;
         fileData->fileOwner = statbuf.st_uid;
         fileData->fileGroup = statbuf.st_gid;
      }

      err = 0;
   }

   return err;
}


/*
 *----------------------------------------------------------------------
 *
 * File_IsRemote --
 *
 *      Determine whether a file is on a remote filesystem.
 *
 *      On ESX all files are treated as local files, as all
 *      callers of this function wants to do is to post message
 *      that performance will be degraded on remote filesystems.
 *      On ESX (a) performance should be acceptable with remote
 *      files, and (b) even if it is not, we should not ask users
 *      whether they are aware that it is poor.  ESX has
 *      performance monitoring which can notify user if something
 *      is wrong.
 *
 *      On hosted platform we report remote files as faithfully
 *      as we can because having mainmem file on NFS is known
 *      to badly affect VM consistency when NFS filesystem gets
 *      reconnected.  Due to that we are conservative, and report
 *      filesystem as remote if there was some problem with
 *      determining file remoteness.
 *
 * Results:
 *      The answer.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

#if !defined(__FreeBSD__) && !defined(sun)
Bool
File_IsRemote(const char *pathName)  // IN: Path name
{
   if (HostType_OSIsVMK()) {
      /*
       * All files and file systems are treated as "directly attached"
       * on ESX.  See bug 158284.
       */

      return FALSE;
   } else {
      struct statfs sfbuf;

      if (Posix_Statfs(pathName, &sfbuf) == -1) {
         Log(LGPFX" %s: statfs(%s) failed: %s\n", __func__, pathName,
             Err_Errno2String(errno));

         return TRUE;
      }
#if defined(__APPLE__)
      return sfbuf.f_flags & MNT_LOCAL ? FALSE : TRUE;
#else
      if (NFS_SUPER_MAGIC == sfbuf.f_type) {
         return TRUE;
      }

      if (SMB_SUPER_MAGIC == sfbuf.f_type) {
         return TRUE;
      }

      if (CIFS_SUPER_MAGIC == sfbuf.f_type) {
         return TRUE;
      }

      return FALSE;
#endif
   }
}
#endif /* !FreeBSD && !sun */


/*
 *----------------------------------------------------------------------
 *
 * File_IsSymLink --
 *
 *      Check if the specified file is a symbolic link or not
 *
 * Results:
 *      Bool - TRUE -> is a symlink, FALSE -> not a symlink or error
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_IsSymLink(const char *pathName)  // IN:
{
   struct stat statbuf;

   return (Posix_Lstat(pathName, &statbuf) == 0) &&
           S_ISLNK(statbuf.st_mode);
}


/*
 *----------------------------------------------------------------------
 *
 * File_Cwd --
 *
 *      Find the current directory on drive DRIVE. DRIVE is either NULL
 *      (current drive) or a string starting with [A-Za-z].
 *
 * Results:
 *      NULL if error.
 *
 * Side effects:
 *      The result is allocated
 *
 *----------------------------------------------------------------------
 */

char *
File_Cwd(const char *drive)  // IN:
{
   size_t size;
   char *buffer;
   char *path;

   if ((drive != NULL) && !Unicode_IsEmpty(drive)) {
      Warning(LGPFX" %s: Drive letter %s on Linux?\n", __FUNCTION__,
              drive);
   }

   size = FILE_PATH_GROW_SIZE;
   buffer = Util_SafeMalloc(size);

   while (TRUE) {
      if (getcwd(buffer, size) != NULL) {
         break;
      }

      Posix_Free(buffer);
      buffer = NULL;

      if (errno != ERANGE) {
         break;
      }

      size += FILE_PATH_GROW_SIZE;
      buffer = Util_SafeMalloc(size);
   }

   if (buffer == NULL) {
      Msg_Append(MSGID(filePosix.getcwd)
                 "Unable to retrieve the current working directory: %s. "
                 "Check if the directory has been deleted or unmounted.\n",
                 Msg_ErrString());

      Warning(LGPFX" %s: getcwd() failed: %s\n", __FUNCTION__,
              Msg_ErrString());

      return NULL;
   }

   path = Unicode_Alloc(buffer, STRING_ENCODING_DEFAULT);

   Posix_Free(buffer);

   return path;
}


/*
 *----------------------------------------------------------------------
 *
 * File_StripFwdSlashes --
 *
 *      Returns a new string with the extraneous forward slashes ("/") removed.
 *
 * Results:
 *      As documented.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

char *
File_StripFwdSlashes(const char *pathName)  // IN:
{
   char *ptr;
   char *path;
   char *cptr;
   char *prev;
   char *result;

   ASSERT(pathName != NULL);

   path = Unicode_GetAllocBytes(pathName, STRING_ENCODING_UTF8);
   ASSERT(path != NULL);

   ptr = path;
   cptr = path;
   prev = NULL;

   /*
    * Copy over if not DIRSEPC. If yes, copy over only if previous
    * character was not DIRSEPC.
    */

   while (*ptr != '\0') {
      if (*ptr == DIRSEPC) {
         if (prev != ptr - 1) {
            *cptr++ = *ptr;
         }
         prev = ptr;
      } else {
         *cptr++ = *ptr;
      }
      ptr++;
   }

   *cptr = '\0';

   result = Unicode_AllocWithUTF8(path);

   Posix_Free(path);

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * File_FullPath --
 *
 *      This routine computes the canonical path from a supplied path.
 *      The supplied path could be an absolute path name or a relative
 *      one, with our without symlinks and /./ /../ separators. A
 *      canonical representation of a path is defined as an absolute
 *      path without symlinks and /./ /../ separators. The canonical
 *      path of "." is the current working directory, ".." is parent
 *      directory and so on. If the path is NULL or "", this routine
 *      returns the current working directory.
 *
 *      On FreeBSD and Sun platforms, this routine will only work if
 *      the path exists, or when we are about to create a child in an
 *      existing parent directory. This is because on these platforms,
 *      we cannot rely on finding existing ancestor and such because
 *      those functions are not compiled.
 *
 * Results:
 *      NULL if error (reported to the user)
 *
 * Side effects:
 *      The result is allocated
 *
 *----------------------------------------------------------------------
 */

char *
File_FullPath(const char *pathName)  // IN:
{
   char *cwd;
   char *ret;

   if ((pathName != NULL) && File_IsFullPath(pathName)) {
      cwd = NULL;
   } else {
      cwd = File_Cwd(NULL);
      if (cwd == NULL) {
         return NULL;
      }
   }

   if ((pathName == NULL) || Unicode_IsEmpty(pathName)) {
      ret = Unicode_Duplicate(cwd);
   } else {
      char *path;

      if (File_IsFullPath(pathName)) {
         path = Unicode_Duplicate(pathName);
      } else {
         path = Unicode_Join(cwd, DIRSEPS, pathName, NULL);
      }

      ret = Posix_RealPath(path);
      if (ret == NULL) {
         char *dir;
         char *file;
#if defined(__FreeBSD__) || defined(sun)
         char *realDir;
#else
         char *ancestorPath;
         char *ancestorRealPath;
#endif

         File_GetPathName(path, &dir, &file);
#if defined(__FreeBSD__) || defined(sun)
         realDir = Posix_RealPath(dir);
         if (realDir == NULL) {
            realDir = File_StripFwdSlashes(dir);
         }

         ret = Unicode_Join(realDir, DIRSEPS, file, NULL);
         Posix_Free(realDir);
#else
         ancestorPath = FilePosixNearestExistingAncestor(dir);
         ancestorRealPath = Posix_RealPath(ancestorPath);

         /*
          * Check if the ancestor was deleted before we could compute its
          * realPath
          */
         if (ancestorRealPath == NULL) {
            ret = File_StripFwdSlashes(path);
         } else {
            ret = File_PathJoin(ancestorRealPath, path + strlen(ancestorPath));
            Posix_Free(ancestorRealPath);
         }

         Posix_Free(ancestorPath);
#endif
         Posix_Free(dir);
         Posix_Free(file);
      }

      Posix_Free(path);
   }

   Posix_Free(cwd);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * File_IsFullPath --
 *
 *      Is this a full path?
 *
 * Results:
 *      TRUE if full path.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
File_IsFullPath(const char *pathName)  // IN:
{
   /* start with a slash? */
   return pathName != NULL && pathName[0] == DIRSEPC;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetTimes --
 *
 *      Get the date and time that a file was created, last accessed,
 *      last modified and last attribute changed.
 *
 * Results:
 *      TRUE if succeed or FALSE if error.
 *
 * Side effects:
 *      If a particular time is not available, -1 will be returned for
 *      that time.
 *
 *----------------------------------------------------------------------
 */

Bool
File_GetTimes(const char *pathName,        // IN:
              VmTimeType *createTime,      // OUT: Windows NT time format
              VmTimeType *accessTime,      // OUT: Windows NT time format
              VmTimeType *writeTime,       // OUT: Windows NT time format
              VmTimeType *attrChangeTime)  // OUT: Windows NT time format
{
   struct stat statBuf;

   ASSERT(createTime && accessTime && writeTime && attrChangeTime);

   *createTime     = -1;
   *accessTime     = -1;
   *writeTime      = -1;
   *attrChangeTime = -1;

   if (Posix_Lstat(pathName, &statBuf) == -1) {
      Log(LGPFX" %s: error stating file \"%s\": %s\n", __FUNCTION__,
          pathName, Err_Errno2String(errno));

      return FALSE;
   }

   /*
    * XXX We should probably use the MIN of all Unix times for the creation
    *     time, so that at least times are never inconsistent in the
    *     cross-platform format. Maybe atime is always that MIN. We should
    *     check and change the code if it is not.
    *
    * XXX atime is almost always MAX.
    */

#if defined(__FreeBSD__)
   /*
    * FreeBSD: All supported versions have timestamps with nanosecond
    * resolution.
    */
   *createTime     = TimeUtil_UnixTimeToNtTime(statBuf.st_birthtimespec);
   *accessTime     = TimeUtil_UnixTimeToNtTime(statBuf.st_atimespec);
   *writeTime      = TimeUtil_UnixTimeToNtTime(statBuf.st_mtimespec);
   *attrChangeTime = TimeUtil_UnixTimeToNtTime(statBuf.st_ctimespec);
#elif defined(__linux__)
   /*
    * Linux: Glibc 2.3+ has st_Xtim.  Glibc 2.1/2.2 has st_Xtime/__unusedX on
    *        same place (see below).  We do not support Glibc 2.0 or older.
    */

#   if (__GLIBC__ == 2) && (__GLIBC_MINOR__ < 3) && !defined(__UCLIBC__)
   {
      /*
       * stat structure is same between glibc 2.3 and older glibcs, just
       * these __unused fields are always zero. If we'll use __unused*
       * instead of zeroes, we get automatically nanosecond timestamps
       * when running on host which provides them.
       */

      struct timespec timeBuf;

      timeBuf.tv_sec  = statBuf.st_atime;
      timeBuf.tv_nsec = statBuf.__unused1;
      *accessTime     = TimeUtil_UnixTimeToNtTime(timeBuf);


      timeBuf.tv_sec  = statBuf.st_mtime;
      timeBuf.tv_nsec = statBuf.__unused2;
      *writeTime      = TimeUtil_UnixTimeToNtTime(timeBuf);

      timeBuf.tv_sec  = statBuf.st_ctime;
      timeBuf.tv_nsec = statBuf.__unused3;
      *attrChangeTime = TimeUtil_UnixTimeToNtTime(timeBuf);
   }
#   elif defined(__ANDROID__)
   {
      struct timespec timeBuf;

      timeBuf.tv_sec  = statBuf.st_atime;
      timeBuf.tv_nsec = statBuf.st_atime_nsec;
      *accessTime     = TimeUtil_UnixTimeToNtTime(timeBuf);


      timeBuf.tv_sec  = statBuf.st_mtime;
      timeBuf.tv_nsec = statBuf.st_mtime_nsec;
      *writeTime      = TimeUtil_UnixTimeToNtTime(timeBuf);

      timeBuf.tv_sec  = statBuf.st_ctime;
      timeBuf.tv_nsec = statBuf.st_ctime_nsec;
      *attrChangeTime = TimeUtil_UnixTimeToNtTime(timeBuf);
   }
#   else
   *accessTime     = TimeUtil_UnixTimeToNtTime(statBuf.st_atim);
   *writeTime      = TimeUtil_UnixTimeToNtTime(statBuf.st_mtim);
   *attrChangeTime = TimeUtil_UnixTimeToNtTime(statBuf.st_ctim);
#   endif
#elif defined(__APPLE__)
   /* Mac: No file create timestamp. */
   *accessTime     = TimeUtil_UnixTimeToNtTime(statBuf.st_atimespec);
   *writeTime      = TimeUtil_UnixTimeToNtTime(statBuf.st_mtimespec);
   *attrChangeTime = TimeUtil_UnixTimeToNtTime(statBuf.st_ctimespec);
#else
   {
      /* Solaris: No nanosecond timestamps, no file create timestamp. */
      struct timespec timeBuf;

      timeBuf.tv_nsec = 0;

      timeBuf.tv_sec  = statBuf.st_atime;
      *accessTime     = TimeUtil_UnixTimeToNtTime(timeBuf);

      timeBuf.tv_sec  = statBuf.st_mtime;
      *writeTime      = TimeUtil_UnixTimeToNtTime(timeBuf);

      timeBuf.tv_sec  = statBuf.st_ctime;
      *attrChangeTime = TimeUtil_UnixTimeToNtTime(timeBuf);
   }
#endif

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * FileSetTimes --
 *
 *      Set the date and time that a file was last accessed or last
 *      modified.
 *
 * Results:
 *      TRUE  Success
 *      FALSE Failure
 *
 * Side effects:
 *      If fileName is a symlink, target's timestamps will be updated.
 *      Symlink itself's timestamps will not be changed.
 *
 *----------------------------------------------------------------------
 */

static Bool
FileSetTimes(const char *path,       // IN:
             VmTimeType accessTime,  // IN: Windows NT time format
             VmTimeType writeTime)   // IN: Windows NT time format
#if defined(UTIME_NOW) && defined(UTIME_OMIT)
{
   struct timespec times[2];

   if (accessTime > 0) {
      TimeUtil_NtTimeToUnixTime(&times[0], accessTime);
   } else {
      times[0].tv_sec  = 0;
      times[0].tv_nsec = UTIME_OMIT;
   }

   if (writeTime > 0) {
      TimeUtil_NtTimeToUnixTime(&times[1], writeTime);
   } else {
      times[1].tv_sec  = 0;
      times[1].tv_nsec  = UTIME_OMIT;
   }

   return utimensat(0, path, times, 0) == 0 ? TRUE : FALSE;
}
#else
{
   struct stat statBuf;
   struct timeval times[2];
   struct timeval *aTime, *wTime;
   int err = (lstat(path, &statBuf) == -1) ? errno : 0;

   if (err != 0) {
      Log(LGPFX" %s: error stating file \"%s\": %s\n", __FUNCTION__,
          path, Err_Errno2String(err));

      return FALSE;
   }

   aTime = &times[0];
   wTime = &times[1];

   /*
    * Preserve old times if new time <= 0.
    * XXX Need a better implementation to preserve tv_usec.
    */

   aTime->tv_sec = statBuf.st_atime;
   aTime->tv_usec = 0;
   wTime->tv_sec = statBuf.st_mtime;
   wTime->tv_usec = 0;

   if (accessTime > 0) {
      struct timespec ts;

      TimeUtil_NtTimeToUnixTime(&ts, accessTime);

      aTime->tv_sec = ts.tv_sec;
      aTime->tv_usec = ts.tv_nsec / 1000;
   }

   if (writeTime > 0) {
      struct timespec ts;

      TimeUtil_NtTimeToUnixTime(&ts, writeTime);

      wTime->tv_sec = ts.tv_sec;
      wTime->tv_usec = ts.tv_nsec / 1000;
   }

   err = (utimes(path, times) == -1) ? errno : 0;

   if (err != 0) {
      Log(LGPFX" %s: utimes error on file \"%s\": %s\n", __FUNCTION__,
          path, Err_Errno2String(err));

      return FALSE;
   }

   return TRUE;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * File_SetTimes --
 *
 *      Set the date and time that a file was last accessed or last
 *      modified.
 *
 * Results:
 *      TRUE  Success
 *      FALSE Failure
 *
 * Side effects:
 *      If fileName is a symlink, target's timestamps will be updated.
 *      Symlink itself's timestamps will not be changed.
 *
 *----------------------------------------------------------------------
 */

Bool
File_SetTimes(const char *pathName,       // IN:
              VmTimeType createTime,      // IN: ignored
              VmTimeType accessTime,      // IN: Windows NT time format
              VmTimeType writeTime,       // IN: Windows NT time format
              VmTimeType attrChangeTime)  // IN: ignored
{
   char *path;
   Bool success;
   char *fullPath;

   if (pathName == NULL) {
      errno = EINVAL;  // Invalid parameter
      return FALSE;
   }

   if ((accessTime == 0) && (writeTime == 0)) {
      return TRUE;
   }

   fullPath = File_FullPath(pathName);

   if (fullPath == NULL) {
      return FALSE;
   }

   path = Unicode_GetAllocBytes(fullPath, STRING_ENCODING_DEFAULT);

   Posix_Free(fullPath);

   if (path == NULL) {
      Log(LGPFX" %s: failed to convert \"%s\" to current encoding\n",
          __FUNCTION__, pathName);

      return FALSE;
   }

   success = FileSetTimes(path, accessTime, writeTime);

   Posix_Free(path);

   return success;
}


/*
 *----------------------------------------------------------------------
 *
 * File_SetFilePermissions --
 *
 *      Set file permissions.
 *
 * Results:
 *      TRUE if succeed or FALSE if error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
File_SetFilePermissions(const char *pathName,  // IN:
                        int perms)             // IN: permissions
{
   ASSERT(pathName != NULL);

   if (Posix_Chmod(pathName, perms) == -1) {
      /* The error is not critical, just log it. */
      Log(LGPFX" %s: failed to change permissions on file \"%s\": %s\n",
          __FUNCTION__, pathName, Err_Errno2String(errno));

      return FALSE;
   }

   return TRUE;
}


#if !defined(__FreeBSD__) && !defined(sun)
/*
 *-----------------------------------------------------------------------------
 *
 * FilePosixGetParent --
 *
 *      The input buffer is a canonical path name. Change it in place to the
 *      canonical path name of its parent directory.
 *
 *      Although this code is quite simple, we encapsulate it in a function
 *      because it is easy to get it wrong.
 *
 * Results:
 *      TRUE  The input buffer was (and remains) the root directory.
 *      FALSE The input buffer was not the root directory and was changed in
 *            place to its parent directory.
 *
 *      Example: "/foo/bar" -> "/foo" FALSE
 *               "/foo"     -> "/"    FALSE
 *               "/"        -> "/"    TRUE
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
FilePosixGetParent(char **canPath)  // IN/OUT: Canonical file path
{
   char *pathName;
   char *baseName;

   ASSERT(canPath != NULL);
   ASSERT(File_IsFullPath(*canPath));

   if (Unicode_Compare(*canPath, DIRSEPS) == 0) {
      return TRUE;
   }

   File_GetPathName(*canPath, &pathName, &baseName);

   Posix_Free(*canPath);

   if (Unicode_IsEmpty(pathName)) {
      /* empty string which denotes "/" */
      Posix_Free(pathName);
      *canPath = Unicode_Duplicate("/");
   } else {
      if (Unicode_IsEmpty(baseName)) {  // Directory
         File_GetPathName(pathName, canPath, NULL);
         Posix_Free(pathName);
      } else {                          // File
         *canPath = pathName;
      }
   }

   Posix_Free(baseName);

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_GetParent --
 *
 *      The input buffer is a canonical path name. Change it in place to the
 *      canonical path name of its parent directory.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_GetParent(char **canPath)  // IN/OUT: Canonical file path
{
   return FilePosixGetParent(canPath);
}


#if !defined(__APPLE__) || TARGET_OS_IPHONE
/*
 *----------------------------------------------------------------------
 *
 * FileGetStats --
 *
 *      Calls statfs on a full path (eg. something returned from File_FullPath).
 *      If doNotAscend is FALSE, climb up the directory chain and call statfs
 *      on each level until it succeeds.
 *
 * Results:
 *      TRUE    statfs succeeded
 *      FALSE   unable to statfs anything along the path
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
FileGetStats(const char *pathName,       // IN:
             Bool doNotAscend,           // IN:
             struct statfs *pstatfsbuf)  // OUT:
{
   Bool retval = TRUE;
   char *dupPath = NULL;

   while (Posix_Statfs(dupPath ? dupPath : pathName, pstatfsbuf) == -1) {
      if (errno != ENOENT || doNotAscend) {
         retval = FALSE;
         break;
      }

      if (dupPath == NULL) {
         /* Dup fullPath, so as not to modify input parameters */
         dupPath = Unicode_Duplicate(pathName);
      }

      FilePosixGetParent(&dupPath);
   }

   Posix_Free(dupPath);

   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetFreeSpace --
 *
 *      Return the free space (in bytes) available to the user on a disk where
 *      a file is or would be.  If doNotAscend is FALSE, the helper function
 *      ascends the directory chain on system call errors in order to obtain
 *      the file system information.
 *
 * Results:
 *      -1 if error (reported to the user)
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

uint64
File_GetFreeSpace(const char *pathName,  // IN: File name
                  Bool doNotAscend)      // IN: Do not ascend dir chain
{
   uint64 ret;
   char *fullPath;
   struct statfs statfsbuf;

   fullPath = File_FullPath(pathName);
   if (fullPath == NULL) {
      return -1;
   }

   if (FileGetStats(fullPath, doNotAscend, &statfsbuf)) {
      ret = (uint64) statfsbuf.f_bavail * statfsbuf.f_bsize;  // available space
   } else {
      Warning("%s: Couldn't statfs %s\n", __func__, fullPath);
      ret = -1;
   }

   Posix_Free(fullPath);

   return ret;
}
#endif


#if defined(VMX86_SERVER)
/*
 *----------------------------------------------------------------------
 *
 * File_GetVMFSAttributes --
 *
 *      Acquire the attributes for a given file or directory on a VMFS volume.
 *
 * Results:
 *      Integer return value and populated FS_PartitionListResult
 *
 * Side effects:
 *      Will fail if file is not on VMFS or not enough memory for partition
 *      query results
 *
 *----------------------------------------------------------------------
 */

int
File_GetVMFSAttributes(const char *pathName,              // IN: File/dir to test
                       FS_PartitionListResult **fsAttrs)  // IN/OUT: VMFS Info
{
   int fd;
   int ret;
   char *fullPath;
   char *directory = NULL;

   fullPath = File_FullPath(pathName);
   if (fullPath == NULL) {
      ret = -1;
      goto bail;
   }

   if (File_IsDirectory(fullPath)) {
      directory = Unicode_Duplicate(fullPath);
   } else {
      File_SplitName(fullPath, NULL, &directory, NULL);
   }

   if (!HostType_OSIsVMK()) {
      Log(LGPFX" %s: File %s not on VMFS volume\n", __func__, pathName);
      ret = -1;
      goto bail;
   }

   *fsAttrs = Util_SafeMalloc(FS_PARTITION_ARR_SIZE(FS_PLIST_DEF_MAX_PARTITIONS));

   memset(*fsAttrs, 0, FS_PARTITION_ARR_SIZE(FS_PLIST_DEF_MAX_PARTITIONS));

   (*fsAttrs)->ioctlAttr.maxPartitions = FS_PLIST_DEF_MAX_PARTITIONS;
   (*fsAttrs)->ioctlAttr.getAttrSpec = FS_ATTR_SPEC_BASIC;

   fd = Posix_Open(directory, O_RDONLY, 0);

   if (fd == -1) {
      Log(LGPFX" %s: could not open %s: %s\n", __func__, pathName,
          Err_Errno2String(errno));
      ret = -1;
      Posix_Free(*fsAttrs);
      *fsAttrs = NULL;
      goto bail;
   }

   ret = ioctl(fd, IOCTLCMD_VMFS_FS_GET_ATTR, (char *) *fsAttrs);
   if (ret == -1) {
      Log(LGPFX" %s: Could not get volume attributes (ret = %d): %s\n",
          __func__, ret, Err_Errno2String(errno));
      Posix_Free(*fsAttrs);
      *fsAttrs = NULL;
   }

   close(fd);

bail:
   Posix_Free(fullPath);
   Posix_Free(directory);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetVMFSFSType --
 *
 *      Get the filesystem type number of the file system on which the
 *      given file/directory resides.
 *
 *      Callers can specify either a pathname, or an already opened fd,
 *      of the file/dir whose filesystem they want to determine.
 *      'fd' takes precedence over 'pathName', so 'pathName' is used only
 *      if 'fd' is -1.
 *
 * Results:
 *      On success : return value  0 and file type number in 'fsTypeNum'.
 *      On failure : return value -1 (errno will be set appropriately).
 *
 * Side effects:
 *      On failure errno will be set.
 *
 *----------------------------------------------------------------------
 */

int
File_GetVMFSFSType(const char *pathName,  // IN:  File name to test
                   int fd,                // IN:  fd of an already opened file
                   uint16 *fsTypeNum)     // OUT: Filesystem type number
{
   int ret, savedErrno;
   Bool fdArg = (fd >= 0);  /* fd or pathname ? */

   if (!fsTypeNum || (!fdArg && !pathName)) {
      savedErrno = EINVAL;
      goto exit;
   }

   if (!fdArg) {
      fd = Posix_Open(pathName, O_RDONLY, 0);
      if (fd < 0) {
         savedErrno = errno;
         Log(LGPFX" %s : Could not open %s : %s\n", __func__, pathName,
             Err_Errno2String(savedErrno));
         goto exit;
      }
   }

   ret = ioctl(fd, IOCTLCMD_VMFS_GET_FSTYPE, fsTypeNum);
   /*
    * Save errno to avoid close() affecting it.
    */
   savedErrno = errno;
   if (!fdArg) {
      close(fd);
   }

   if (ret == -1) {
      Log(LGPFX" %s : Could not get filesystem type for %s (fd %d) : %s\n",
          __func__, (!fdArg ? pathName : "__na__"), fd,
          Err_Errno2String(savedErrno));
      goto exit;
   }

   return 0;

exit:
   errno = savedErrno;
   ASSERT(errno != 0);
   return -1;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetVMFSVersion --
 *
 *      Get the version number of the VMFS file system on which the
 *      given file resides.
 *
 * Results:
 *      Integer return value and version number.
 *
 * Side effects:
 *      Will fail if file is not on VMFS or not enough memory for partition
 *      query results.
 *
 *----------------------------------------------------------------------
 */

int
File_GetVMFSVersion(const char *pathName,  // IN: File name to test
                    uint32 *versionNum)    // OUT: Version number
{
   int ret = -1;
   FS_PartitionListResult *fsAttrs = NULL;

   if (versionNum == NULL) {
      errno = EINVAL;
      goto exit;
   }

   ret = File_GetVMFSAttributes(pathName, &fsAttrs);

   if (ret < 0) {
      Log(LGPFX" %s: File_GetVMFSAttributes failed\n", __func__);
   } else {
      *versionNum = fsAttrs->versionNumber;
   }

   if (fsAttrs) {
      Posix_Free(fsAttrs);
   }

exit:
   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetVMFSBlockSize --
 *
 *      Acquire the blocksize for a given file on a VMFS file system.
 *
 * Results:
 *      Integer return value and block size
 *
 * Side effects:
 *      Will fail if file is not on VMFS or not enough memory for partition
 *      query results
 *
 *----------------------------------------------------------------------
 */

int
File_GetVMFSBlockSize(const char *pathName,  // IN: File name to test
                      uint32 *blockSize)     // IN/OUT: VMFS block size
{
   int ret = -1;
   FS_PartitionListResult *fsAttrs = NULL;

   if (blockSize == NULL) {
      errno = EINVAL;
      goto exit;
   }

   ret = File_GetVMFSAttributes(pathName, &fsAttrs);

   if (ret < 0) {
      Log(LGPFX" %s: File_GetVMFSAttributes failed\n", __func__);
   } else {
      *blockSize = fsAttrs->fileBlockSize;
   }

   if (fsAttrs) {
      Posix_Free(fsAttrs);
   }

exit:
   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetVMFSMountInfo --
 *
 *      Acquire the FS mount point info such as fsType, major version,
 *      local mount point (/vmfs/volumes/xyz), and for NFS,
 *      remote IP and remote mount point for a given file.
 *
 * Results:
 *      Integer return value and allocated data
 *
 * Side effects:
 *      Only implemented on ESX. Will fail on other platforms.
 *      remoteIP and remoteMountPoint are only populated for files on NFS.
 *
 *----------------------------------------------------------------------
 */

int
File_GetVMFSMountInfo(const char *pathName,    // IN:
                      char **fsType,           // OUT:
                      uint32 *version,         // OUT:
                      char **remoteIP,         // OUT:
                      char **remoteMountPoint, // OUT:
                      char **localMountPoint)  // OUT:
{
   int ret;
   FS_PartitionListResult *fsAttrs = NULL;

   *localMountPoint = File_GetUniqueFileSystemID(pathName);

   if (*localMountPoint == NULL) {
      return -1;
   }

   /* Get file IP and mount point */
   ret = File_GetVMFSAttributes(pathName, &fsAttrs);
   if (ret >= 0 && fsAttrs) {
      *version = fsAttrs->versionNumber;
      *fsType = Util_SafeStrdup(fsAttrs->fsType);

      /*
       * We only compare the first 3 characters 'NFS'xx.
       * This will cover both NFSv3 and NFSv4.1.
       */
      if (strncmp(fsAttrs->fsType, FS_NFS_ON_ESX, FS_NFS_PREFIX_LEN) == 0) {
         char *sep = strchr(fsAttrs->logicalDevice, ' ');

         if (sep) {
            *sep++ = 0;
            *remoteIP = Util_SafeStrdup(fsAttrs->logicalDevice);
            *remoteMountPoint = Util_SafeStrdup(sep);
         } else {
            *remoteIP = NULL;
            *remoteMountPoint = NULL;
         }
      } else {
         *remoteIP = NULL;
         *remoteMountPoint = NULL;
      }

      Posix_Free(fsAttrs);
   }

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetVMFSLockInfo --
 *
 *      Get lock information about a file that has probably caused a
 *      locking conflict.
 *
 *      If the file is locked by a process local to the current host,
 *      FS open flags, world ID, and world name will be returned (or
 *      0/NULL if not).
 *
 *      If the file is on VMFS, the owner MAC address and lock mode of
 *      the on-disk lock will be provided (or 0/NULL if not).
 *
 *      (It's possible for a file to be on a non-VMFS shared datastore
 *      like NFS/VVol/VSAN and be locked by another host. In that
 *      case, this function might not return any information.)
 *
 * Results:
 *      0 on success (and see above), -1 otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
File_GetVMFSLockInfo(const char *path,         // IN
                     uint32 *outOpenFlags,     // OUT:
                     uint32 *outWorldID,       // OUT:
                     char **outWorldName,      // OUT:
                     char **outVMFSMacAddr,    // OUT:
                     uint32 *outVMFSLockMode)  // OUT:
{
   int ret = -1;
   int ioctlRet;
   int fd = -1;
   FS_GetFileLockInfoArgs lockArgs;
   FS_DumpFDData dumpArgs;
   char *dir = NULL;
   char *fileName = NULL;

   *outOpenFlags = 0;
   *outWorldID = INVALID_WORLD_ID;
   *outWorldName = NULL;
   *outVMFSMacAddr = NULL;
   *outVMFSLockMode = 0;

   memset(&lockArgs, 0, sizeof lockArgs);
   memset(&dumpArgs, 0, sizeof dumpArgs);
   File_SplitName(path, NULL, &dir, &fileName);

   fd = Posix_Open(dir, O_RDONLY, 0);
   if (fd == -1) {
      Log(LGPFX" %s: could not open directory \"%s\": %s\n", __FUNCTION__, dir,
          Err_Errno2String(errno));
      goto exit;
   }

   Str_Strncpy(lockArgs.fileName, sizeof lockArgs.fileName, fileName,
               strlen(fileName));
   ioctlRet = ioctl(fd, IOCTLCMD_GETFILELOCKINFO, (char *)&lockArgs);
   /* If this fails, it might be open only on another machine. */
   if (ioctlRet != -1) {
      *outOpenFlags = lockArgs.openFlags;
      *outWorldID = lockArgs.worldID;
      *outWorldName = Util_SafeStrdup(lockArgs.worldName);
   }

   Str_Strncpy(dumpArgs.args.fileName, sizeof dumpArgs.args.fileName,
               fileName, strlen(fileName));
   ioctlRet = ioctl(fd, IOCTLCMD_VMFS_DUMP_METADATA, (char *)&dumpArgs);
   /* If this fails, it probably isn't on VMFS. */
   if (ioctlRet != -1) {
      FS3_FileDescriptor *fileDesc =
         (FS3_FileDescriptor *)&dumpArgs.result.descriptor;
      FS3_DiskLock *diskLock = FS3_DISKLOCK(&fileDesc->lockBlock);
      const UUID *theOwner;

      if (dumpArgs.result.descriptorLength < sizeof(FS3_FileDescriptor)) {
         /* This should not happen. */
         Log(LGPFX" %s: VMFS file descriptor size %u too small (need "
             "%"FMTSZ"u) for file \"%s\"\n", __FUNCTION__,
             dumpArgs.result.descriptorLength, sizeof(FS3_FileDescriptor),
             path);
         goto exit;
      }

      *outVMFSLockMode = diskLock->mode;

      if (diskLock->mode != FS3_LC_FREE &&
          diskLock->mode != FS3_LC_EXCLUSIVE) {
         /*
          * This is a non-exclusive lock. Thus it can have multiple holders.
          * For now, return only the first.
          */
         if (diskLock->numHolders > FS3_MAX_LOCK_HOLDERS) {
            Log(LGPFX" %s: Corrupt VMFS disk lock found for file \"%s\", "
                "invalid number of holders %u.\n", __FUNCTION__, path,
                diskLock->numHolders);
            goto exit;
         }

         if (diskLock->numHolders == 0) {
            theOwner = &diskLock->owner;
         } else {
            theOwner = &diskLock->holders[0].uid;
         }
      } else {
         /* Exclusive lock, so there is only one owner. */
         theOwner = &diskLock->owner;
      }

      *outVMFSMacAddr = Str_SafeAsprintf(NULL,
                                         "%02x:%02x:%02x:%02x:%02x:%02x",
                                         theOwner->macAddr[0],
                                         theOwner->macAddr[1],
                                         theOwner->macAddr[2],
                                         theOwner->macAddr[3],
                                         theOwner->macAddr[4],
                                         theOwner->macAddr[5]);
   }

   ret = 0;

  exit:
   if (fd != -1) {
      close(fd);
   }
   free(dir);
   free(fileName);
   return ret;
}


/*
 *---------------------------------------------------------------------------
 *
 * File_DoesVolumeSupportConvertBlocks --
 *
 *     Does the volume support the new convert block allocation
 *     IOCTL? (Always FALSE for now on non-VMFS.)
 *
 * Results:
 *     TRUE   Yes
 *     FALSE  No
 *
 * Side effects:
 *     None
 *
 *---------------------------------------------------------------------------
 */

Bool
File_DoesVolumeSupportConvertBlocks(const char *pathName)  // IN:
{
   Bool supports;
   FS_PartitionListResult *fsAttrs = NULL;
   int ret = File_GetVMFSAttributes(pathName, &fsAttrs);

   if (ret < 0) {
      /* Probably not VMFS. */
      return FALSE;
   }

   /*
    * Only regular VMFS (no VMFS-L, VMFSOS, or other oddities) and
    * only version 6 and above. Also, no VMFS on VSAN/VVol objects.
    */
   supports = Str_Strcmp(fsAttrs->fsType, FS_VMFS_ON_ESX) == 0 &&
      fsAttrs->versionNumber >= 6 &&
      strcmp(fsAttrs->driverType, "vsan") != 0 &&
      strcmp(fsAttrs->driverType, "vvol") != 0;

   Posix_Free(fsAttrs);
   return supports;
}

#endif // VMX86_SERVER


/*
 *----------------------------------------------------------------------
 *
 * FileIsVMFS --
 *
 *      Is the given file on a filesystem that supports vmfs-specific
 *      features like zeroed-thick and multiwriter files?
 *
 * Results:
 *      TRUE if we're on VMFS.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static Bool
FileIsVMFS(const char *pathName)  // IN:
{
   Bool result = FALSE;

#if defined(VMX86_SERVER)
   /* Right now only VMFS supports zeroedThick and multiWriter. */
   FS_PartitionListResult *fsAttrs = NULL;

   if (File_GetVMFSAttributes(pathName, &fsAttrs) >= 0) {
      /* We want to match anything that starts with VMFS */
      result = strncmp(fsAttrs->fsType, FS_VMFS_ON_ESX,
                       strlen(FS_VMFS_ON_ESX)) == 0;
      if (!result) {
         result = strncmp(fsAttrs->fsType, FS_VSAND_ON_ESX,
                          strlen(FS_VSAND_ON_ESX)) == 0;
      }
   } else {
      Log(LGPFX" %s: File_GetVMFSAttributes failed\n", __func__);
   }

   if (fsAttrs) {
      Posix_Free(fsAttrs);
   }
#endif

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * File_SupportsZeroedThick --
 *
 *      Check if the given file is on an FS supports creation of
 *      the zeroed-thick files.
 *      Currently only VMFS on ESX does support zeroed-thick files, but
 *      this may change in the future.
 *
 * Results:
 *      TRUE if FS supports creation of the zeroed-thick files.
 *
 * Side effects:
 *       None
 *
 *----------------------------------------------------------------------
 */

Bool
File_SupportsZeroedThick(const char *pathName)  // IN:
{
   return FileIsVMFS(pathName);
}


/*
 *----------------------------------------------------------------------
 *
 * File_SupportsMultiWriter --
 *
 *      Check if the given file is on an FS supports opening files
 *      in multi-writer mode.
 *      Currently only VMFS on ESX supports multi-writer mode, but
 *      this may change in the future.
 *
 * Results:
 *      TRUE if FS supports opening files in multi-writer mode.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_SupportsMultiWriter(const char *pathName)  // IN:
{
   return FileIsVMFS(pathName);
}


/*
 *----------------------------------------------------------------------
 *
 * File_SupportsOptimisticLock --
 *
 *      Return TRUE if the given file is on an FS that supports the
 *      FILEIO_OPEN_OPTIMISTIC_LOCK flag (only VMFS).
 *
 * Results:
 *      See above.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
File_SupportsOptimisticLock(const char *pathName)  // IN:
{
#ifdef VMX86_SERVER
   uint16 fsTypeNum;
   char *dir;
   char *tempPath = NULL;
   const char *fullPath;
   int res;

   /*
    * File_GetVMFSFSType works much faster on directories, so get the
    * directory.
    */

   if (File_IsFullPath(pathName)) {
      fullPath = pathName;
   } else {
      tempPath = File_FullPath(pathName);
      fullPath = tempPath;
   }
   File_GetPathName(fullPath, &dir, NULL);
   res = File_GetVMFSFSType(dir, -1, &fsTypeNum);
   Posix_Free(tempPath);
   Posix_Free(dir);

   return (res == 0) ? IS_VMFS_FSTYPENUM(fsTypeNum) : FALSE;
#else
   return FALSE;
#endif
}


#if !defined(__APPLE__) || TARGET_OS_IPHONE
/*
 *----------------------------------------------------------------------
 *
 * File_GetCapacity --
 *
 *      Return the total capacity (in bytes) of the file system that the
 *      specified file resides on. This is not be confused with the
 *      amount of free space available in the file system the specified
 *      file resides on.
 *
 * Results:
 *      -1 if error (reported to the user)
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

uint64
File_GetCapacity(const char *pathName)  // IN: Path name
{
   uint64 ret;
   char *fullPath;
   struct statfs statfsbuf;

   fullPath = File_FullPath(pathName);
   if (fullPath == NULL) {
      return -1;
   }

   if (FileGetStats(fullPath, FALSE, &statfsbuf)) {
      ret = (uint64) statfsbuf.f_blocks * statfsbuf.f_bsize; // FS size
   } else {
      Warning(LGPFX" %s: Couldn't statfs\n", __func__);
      ret = -1;
   }

   Posix_Free(fullPath);

   return ret;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * File_GetUniqueFileSystemID --
 *
 *      Returns a string which uniquely identifies the underlying filesystem
 *      for a given path.
 *
 *      'path' can be relative (including empty) or absolute, and any number
 *      of non-existing components at the end of 'path' are simply ignored.
 *
 *      XXX: On POSIX systems, we choose the underlying device's name as the
 *           unique ID. I make no claim that this is 100% unique so if you
 *           need this functionality to be 100% perfect, I suggest you think
 *           about it more deeply than I did. -meccleston
 *
 * Results:
 *      On success: Allocated and NUL-terminated filesystem ID.
 *      On failure: NULL.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
File_GetUniqueFileSystemID(char const *path)  // IN: File path
{
#ifdef VMX86_SERVER
   char vmfsVolumeName[FILE_MAXPATH];
   char *existPath;
   char *canPath;

   existPath = FilePosixNearestExistingAncestor(path);
   canPath = Posix_RealPath(existPath);

   /*
    * Returns "/vmfs/devices" for DEVFS. Since /vmfs/devices is symbol linker,
    * We don't use realpath here.
    */
   if (strncmp(existPath, DEVFS_MOUNT_POINT, strlen(DEVFS_MOUNT_POINT)) == 0) {
      char devfsName[FILE_MAXPATH];

      if (sscanf(existPath, DEVFS_MOUNT_PATH "%[^/]%*s", devfsName) == 1) {
         Posix_Free(existPath);
         Posix_Free(canPath);
         return Str_SafeAsprintf(NULL, "%s/%s", DEVFS_MOUNT_POINT, devfsName);
      }
   }

   Posix_Free(existPath);

   if (canPath == NULL) {
      return NULL;
   }

   /*
    * VCFS doesn't have real mount points, so the mount point lookup below
    * returns "/vmfs", instead of the VCFS mount point.
    *
    * See bug 61646 for why we care.
    */
   if (strncmp(canPath, VCFS_MOUNT_POINT, strlen(VCFS_MOUNT_POINT)) != 0 ||
       sscanf(canPath, VCFS_MOUNT_PATH "%[^/]%*s", vmfsVolumeName) != 1) {
      Posix_Free(canPath);
      goto exit;
   }

   /*
    * If the path points to a file or directory that is on a vsan datastore,
    * we have to determine which namespace object is involved.
    */
   if (strncmp(vmfsVolumeName, FS_VSAN_URI_PREFIX,
               strlen(FS_VSAN_URI_PREFIX)) == 0) {
      FS_PartitionListResult *fsAttrs = NULL;
      int res;

      res = File_GetVMFSAttributes(canPath, &fsAttrs);

      if (res >= 0 && fsAttrs != NULL &&
          strncmp(fsAttrs->fsType, FS_VMFS_ON_ESX,
                  strlen(FS_VMFS_ON_ESX)) == 0) {
         char *unique;
         unique = Str_SafeAsprintf(NULL, "%s/%s/%s",
                                   VCFS_MOUNT_POINT, vmfsVolumeName,
                                   fsAttrs->name);
         Posix_Free(fsAttrs);
         Posix_Free(canPath);
         return unique;
      }
      Posix_Free(fsAttrs);
   }
   Posix_Free(canPath);

   return Str_SafeAsprintf(NULL, "%s/%s", VCFS_MOUNT_POINT,
                           vmfsVolumeName);
exit:
#endif
   return FilePosixGetBlockDevice(path);
}


#if !defined(__APPLE__)
/*
 *-----------------------------------------------------------------------------
 *
 * FilePosixLookupMountPoint --
 *
 *      Looks up passed in canonical file path in list of mount points.
 *      If there is a match, it returns the underlying device name of the
 *      mount point along with a flag indicating whether the mount point is
 *      mounted with the "--[r]bind" option.
 *
 * Results:
 *      On success: The allocated, NUL-terminated mounted "device".
 *      On failure: NULL.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char *
FilePosixLookupMountPoint(char const *canPath,  // IN: Canonical file path
                          Bool *bind)           // OUT: Mounted with --[r]bind?
{
#if defined NO_SETMNTENT || defined NO_ENDMNTENT
   NOT_IMPLEMENTED();
   errno = ENOSYS;
   return NULL;
#else
   FILE *f;
   struct mntent mnt;
   char *buf;
   size_t size;
   size_t used;
   char *ret = NULL;

   ASSERT(canPath != NULL);
   ASSERT(bind != NULL);

   size = 4 * FILE_MAXPATH;  // Should suffice for most locales

retry:
   f = setmntent(MOUNTED, "r");
   if (f == NULL) {
      return NULL;
   }

   buf = Util_SafeMalloc(size);

   while (Posix_Getmntent_r(f, &mnt, buf, size) != NULL) {

      /*
       * Our Posix_Getmntent_r graciously sets errno when the buffer
       * is too small, but on UTF-8 based platforms Posix_Getmntent_r
       * is #defined to the system's getmntent_r, which can simply
       * truncate the strings with no other indication.  See how much
       * space it used and increase the buffer size if needed.  Note
       * that if some of the strings are empty, they may share a
       * common nul in the buffer, and the resulting size calculation
       * will be a little over-zealous.
       */

      used = 0;
      if (mnt.mnt_fsname) {
         used += strlen(mnt.mnt_fsname) + 1;
      }
      if (mnt.mnt_dir) {
         used += strlen(mnt.mnt_dir) + 1;
      }
      if (mnt.mnt_type) {
         used += strlen(mnt.mnt_type) + 1;
      }
      if (mnt.mnt_opts) {
         used += strlen(mnt.mnt_opts) + 1;
      }
      if (used >= size || !mnt.mnt_fsname || !mnt.mnt_dir ||
          !mnt.mnt_type || !mnt.mnt_opts) {
         size += 4 * FILE_MAXPATH;
         ASSERT(size <= 32 * FILE_MAXPATH);
         Posix_Free(buf);
         endmntent(f);
         goto retry;
      }

      /*
       * NB: A call to realpath is not needed as getmntent() already
       *     returns it in canonical form.  Additionally, it is bad
       *     to call realpath() as often a mount point is down, and
       *     realpath calls stat which can block trying to stat
       *     a filesystem that the caller of the function is not at
       *     all expecting.
       */

      if (strcmp(mnt.mnt_dir, canPath) == 0) {
         /*
          * The --bind and --rbind options behave differently. See
          * FilePosixGetBlockDevice() for details.
          *
          * Sadly (I blame a bug in 'mount'), there is no way to tell them
          * apart in /etc/mtab: the option recorded there is, in both cases,
          * always "bind".
          */

         *bind = strstr(mnt.mnt_opts, "bind") != NULL;

         ret = Util_SafeStrdup(mnt.mnt_fsname);

         break;
      }
   }

   // 'canPath' is not a mount point.
   endmntent(f);

   Posix_Free(buf);

   return ret;
#endif
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * FilePosixGetBlockDevice --
 *
 *      Retrieve the block device that backs file path 'path'.
 *
 *      'path' can be relative (including empty) or absolute, and any number of
 *      non-existing components at the end of 'path' are simply ignored.
 *
 * Results:
 *      On success: The allocated, NUL-terminated block device absolute path.
 *      On failure: NULL.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
FilePosixGetBlockDevice(char const *path)  // IN: File path
{
   char *existPath;
   Bool failed;
#if defined(__APPLE__)
   struct statfs buf;
#else
   char canPath[FILE_MAXPATH];
   char canPath2[FILE_MAXPATH];
   unsigned int retries = 0;
   char *realPath;
#endif

   existPath = FilePosixNearestExistingAncestor(path);

#if defined(__APPLE__)
   failed = statfs(existPath, &buf) == -1;
   Posix_Free(existPath);
   if (failed) {
      return NULL;
   }

   return Util_SafeStrdup(buf.f_mntfromname);
#else
   realPath = Posix_RealPath(existPath);
   Posix_Free(existPath);

   if (realPath == NULL) {
      return NULL;
   }
   Str_Strcpy(canPath, realPath, sizeof canPath);
   Posix_Free(realPath);

retry:
   Str_Strcpy(canPath2, canPath, sizeof canPath2);

   /* Find the nearest ancestor of 'canPath' that is a mount point. */
   for (;;) {
      char *x;
      Bool bind = FALSE;
      char *ptr;

      ptr = FilePosixLookupMountPoint(canPath, &bind);
      if (ptr) {

         if (bind) {
            /*
             * 'canPath' is a mount point mounted with --[r]bind. This is the
             * mount equivalent of a hard link. Follow the rabbit...
             *
             * --bind and --rbind behave differently. Consider this mount
             * table:
             *
             *    /dev/sda1              /             ext3
             *    exit14:/vol/vol0/home  /exit14/home  nfs
             *    /                      /bind         (mounted with --bind)
             *    /                      /rbind        (mounted with --rbind)
             *
             * then what we _should_ return for these paths is:
             *
             *    /bind/exit14/home -> /dev/sda1
             *    /rbind/exit14/home -> exit14:/vol/vol0/home
             *
             * XXX but currently because we cannot easily tell the difference,
             *     we always assume --rbind and we return:
             *
             *    /bind/exit14/home -> exit14:/vol/vol0/home
             *    /rbind/exit14/home -> exit14:/vol/vol0/home
             */

            Bool rbind = TRUE;

            if (rbind) {
               /*
                * Compute 'canPath = ptr + (canPath2 - canPath)' using and
                * preserving the structural properties of all canonical
                * paths involved in the expression.
                */

               size_t canPathLen = strlen(canPath);
               char const *diff = canPath2 + (canPathLen > 1 ? canPathLen : 0);

               if (*diff != '\0') {
                  Str_Sprintf(canPath, sizeof canPath, "%s%s",
                              strlen(ptr) > 1 ? ptr : "", diff);
               } else {
                  Str_Strcpy(canPath, ptr, sizeof canPath);
               }
            } else {

               /*
                * This is coded as it is in order to document clearly that
                * the function does not handle bind mount correctly (it
                * always assumes rbind).
                */
               /* coverity[dead_error_line] */
               Str_Strcpy(canPath, ptr, sizeof canPath);
            }

            Posix_Free(ptr);

            /*
             * There could be a series of these chained together.  It is
             * possible for the mounts to get into a loop, so limit the total
             * number of retries to something reasonable like 10.
             */

            retries++;
            if (retries > 10) {
               Warning(LGPFX" %s: The --[r]bind mount count exceeds %u. Giving "
                       "up.\n", __func__, 10);
               return NULL;
            }

            goto retry;
         }

         return ptr;
      }

/* XXX temporary work-around until this function is Unicoded. */
      x = Util_SafeStrdup(canPath);
      failed = FilePosixGetParent(&x);
      Str_Strcpy(canPath, x, sizeof canPath);
      Posix_Free(x);

      /*
       * Prevent an infinite loop in case FilePosixLookupMountPoint() even
       * fails on "/".
       */

      if (failed) {
         return NULL;
      }
   }
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * FilePosixNearestExistingAncestor --
 *
 *      Find the nearest existing ancestor of 'path'.
 *
 *      'path' can be relative (including empty) or absolute, and 'path' can
 *      have any number of non-existing components at its end.
 *
 * Results:
 *      The allocated, NUL-terminated, non-empty path of the
 *      nearest existing ancestor.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char *
FilePosixNearestExistingAncestor(char const *path)  // IN: File path
{
   size_t resultSize;
   char *result;
   struct stat statbuf;

   resultSize = MAX(strlen(path), 1) + 1;
   result = Util_SafeMalloc(resultSize);
   Str_Strcpy(result, path, resultSize);

   for (;;) {
      char *ptr;

      if (*result == '\0') {
         Str_Strcpy(result, *path == DIRSEPC ? "/" : ".", resultSize);
         break;
      }

      if (Posix_Stat(result, &statbuf) == 0) {
         break;
      }

      ptr = strrchr(result, DIRSEPC);
      if (ptr == NULL) {
         ptr = result;
      }
      *ptr = '\0';
   }

   return result;
}


#endif /* !FreeBSD && !sun */


/*
 *----------------------------------------------------------------------------
 *
 * File_IsSameFile --
 *
 *      Determine whether both paths point to the same file.
 *
 *      Caveats - While local files are matched based on inode and device
 *      ID, some older versions of NFS return buggy device IDs, so the
 *      determination cannot be done with 100% confidence across NFS.
 *      Paths that traverse NFS mounts are matched based on device, inode
 *      and all of the fields of the stat structure except for times.
 *      This introduces a race condition in that if the target files are not
 *      locked, they can change out from underneath this function yielding
 *      false negative results.  Cloned files sytems mounted across an old
 *      version of NFS may yield a false positive.
 *
 * Results:
 *      TRUE if both paths point to the same file, FALSE otherwise.
 *
 * Side effects:
 *      Changes errno, maybe.
 *
 *----------------------------------------------------------------------------
 */

Bool
File_IsSameFile(const char *path1,  // IN:
                const char *path2)  // IN:
{
   struct stat st1;
   struct stat st2;
#if !defined(sun)  // Solaris does not have statfs
   struct statfs stfs1;
   struct statfs stfs2;
#endif

   ASSERT(path1 != NULL);
   ASSERT(path2 != NULL);

   /*
    * First take care of the easy checks.  If the paths are identical, or if
    * the inode numbers or resident devices don't match, we're done.
    */

   if (Unicode_Compare(path1, path2) == 0) {
      return TRUE;
   }

   if (Posix_Stat(path1, &st1) == -1) {
      return FALSE;
   }

   if (Posix_Stat(path2, &st2) == -1) {
      return FALSE;
   }

   if (st1.st_ino != st2.st_ino) {
      return FALSE;
   }

   if (st1.st_dev != st2.st_dev) {
      return FALSE;
   }

   if (HostType_OSIsVMK()) {
      /*
       * On ESX, post change 1074635 the st_dev field of the stat structure
       * is valid and differentiates between resident devices or NFS file
       * systems - no need to use statfs to obtain file system information.
       */

      return TRUE;
   }

#if !defined(sun)  // Solaris does not have statfs
   if (Posix_Statfs(path1, &stfs1) != 0) {
      return FALSE;
   }

   if (Posix_Statfs(path2, &stfs2) != 0) {
      return FALSE;
   }

#if defined(__APPLE__) || defined(__FreeBSD__)
   if ((stfs1.f_flags & MNT_LOCAL) && (stfs2.f_flags & MNT_LOCAL)) {
      return TRUE;
   }
#else
   if ((stfs1.f_type != NFS_SUPER_MAGIC) &&
       (stfs2.f_type != NFS_SUPER_MAGIC)) {
      return TRUE;
   }
#endif
#endif

   /*
    * At least one of the paths traverses NFS and some older NFS
    * implementations can set st_dev incorrectly. Do some extra checks of the
    * stat structure to increase our confidence. Since the st_ino numbers had
    * to match to get this far, the overwhelming odds are the two files are
    * the same.
    *
    * If another process was actively writing or otherwise modifying the file
    * while we stat'd it, then the following test could fail and we could
    * return a false negative.  On the other hand, if NFS lies about st_dev
    * and the paths point to a cloned file system, then the we will return a
    * false positive.
    */

   if ((st1.st_mode == st2.st_mode) &&
       (st1.st_nlink == st2.st_nlink) &&
       (st1.st_uid == st2.st_uid) &&
       (st1.st_gid == st2.st_gid) &&
       (st1.st_rdev == st2.st_rdev) &&
       (st1.st_size == st2.st_size) &&
       (st1.st_blksize == st2.st_blksize) &&
       (st1.st_blocks == st2.st_blocks)) {
      return TRUE;
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_Replace --
 *
 *      Replace old file (destination) with new file (source), and attempt to
 *      reproduce file permissions.  A NULL value for either the oldName or
 *      newName will result in failure and errno will be set to EFAULT.
 *
 * Results:
 *      TRUE on success.
 *
 * Side effects:
 *      errno may be set.
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_Replace(const char *oldName,  // IN: old file
             const char *newName)  // IN: new file
{
   int status = 0;
   Bool result = FALSE;
   char *newPath = NULL;
   char *oldPath = NULL;
   struct stat st;

   if (newName == NULL) {
      status = EFAULT;
      goto bail;
   } else if ((newPath = Unicode_GetAllocBytes(newName,
                           STRING_ENCODING_DEFAULT)) == NULL) {
      status = UNICODE_CONVERSION_ERRNO;
      Msg_Append(MSGID(filePosix.replaceConversionFailed)
                 "Failed to convert file path \"%s\" to current encoding\n",
                 newName);
      goto bail;
   }
   if (oldName == NULL) {
      status = EFAULT;
      goto bail;
   } else if ((oldPath = Unicode_GetAllocBytes(oldName,
                           STRING_ENCODING_DEFAULT)) == NULL) {
      status = UNICODE_CONVERSION_ERRNO;
      Msg_Append(MSGID(filePosix.replaceConversionFailed)
                 "Failed to convert file path \"%s\" to current encoding\n",
                 oldName);
      goto bail;
   }

   if ((stat(oldPath, &st) == 0) && (chmod(newPath, st.st_mode) == -1)) {
      status = errno;
      Msg_Append(MSGID(filePosix.replaceChmodFailed)
                 "Failed to duplicate file permissions from "
                 "\"%s\" to \"%s\": %s\n",
                 oldName, newName, Msg_ErrString());
      goto bail;
   }


   if (rename(newPath, oldPath) < 0) {
      status = errno;
      Msg_Append(MSGID(filePosix.replaceRenameFailed)
                 "Failed to rename \"%s\" to \"%s\": %s\n",
                 newName, oldName, Msg_ErrString());
      goto bail;
   }

   result = TRUE;

bail:
   Posix_Free(newPath);
   Posix_Free(oldPath);

   errno = status;

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * FilePosixGetMaxOrSupportsFileSize --
 *
 *      Given a file descriptor to a file on a volume, either find out the
 *      max file size for the volume on which the file is located or check
 *      if the volume supports the given file size.
 *      If getMaxFileSize is set then find out the max file size and store it
 *      in *maxFileSize on success, otherwise figure out if *fileSize is
 *      supported.
 *
 * Results:
 *      If getMaxFileSize was set:
 *        TRUE with max file size stored in *fileSize.
 *      Otherwise:
 *        TRUE fileSize is supported.
 *        FALSE fileSize not supported or could not figure out the answer.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
FilePosixGetMaxOrSupportsFileSize(FileIODescriptor *fd,  // IN:
                                  uint64 *fileSize,      // IN/OUT:
                                  Bool getMaxFileSize)   // IN:
{
   uint64 value = 0;
   uint64 mask;

   ASSERT(fd != NULL);
   ASSERT(fileSize != NULL);

   if (!getMaxFileSize) {
      return FileIO_SupportsFileSize(fd, *fileSize);
   }

   /*
    * Try to do a binary search and figure out the max supported file size.
    */
   for (mask = (1ULL << 62); mask != 0; mask >>= 1) {
      if (FileIO_SupportsFileSize(fd, value | mask)) {
         value |= mask;
      }
   }
   *fileSize = value;
   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * FilePosixCreateTestGetMaxOrSupportsFileSize --
 *
 *      Given a path to a dir on a volume, either find out the max file size
 *      for the volume on which the dir is located or check if the volume
 *      supports the given file size.
 *      If getMaxFileSize is set then find out the max file size and store it
 *      in *maxFileSize on success, otherwise figure out if *fileSize is
 *      supported.
 *
 * Results:
 *      If getMaxFileSize was set:
 *        TRUE if figured out the max file size.
 *        FALSE failed to figure out the max file size.
 *      Otherwise:
 *        TRUE fileSize is supported.
 *        FALSE fileSize not supported or could not figure out the answer.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
FilePosixCreateTestGetMaxOrSupportsFileSize(const char *dirName, // IN: test dir
                                            uint64 *fileSize,    // IN/OUT:
                                            Bool getMaxFileSize) // IN:
{
   Bool retVal;
   int posixFD;
   char *temp;
   char *path;
   FileIODescriptor fd;

   ASSERT(fileSize != NULL);

   temp = Unicode_Append(dirName, "/.vmBigFileTest");
   posixFD = File_MakeSafeTemp(temp, &path);
   Posix_Free(temp);

   if (posixFD == -1) {
      Log(LGPFX" %s: Failed to create temporary file in dir: %s\n", __func__,
          dirName);

      return FALSE;
   }

   fd = FileIO_CreateFDPosix(posixFD, O_RDWR);

   retVal = FilePosixGetMaxOrSupportsFileSize(&fd, fileSize,
                                              getMaxFileSize);
   /* Eventually perform destructive tests here... */

   FileIO_Close(&fd);
   File_Unlink(path);
   Posix_Free(path);

   return retVal;
}


#ifdef VMX86_SERVER
/*
 *----------------------------------------------------------------------
 *
 * FileVMKGetMaxFileSize --
 *
 *      Given a path to a file on a volume, find out the max file size for
 *      the volume on which the file is located.
 *      Max file size gets stored in *maxFileSize on success.
 *
 * Results:
 *      TRUE if figured out the max file size.
 *      FALSE failed to figure out the max file size.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static Bool
FileVMKGetMaxFileSize(const char *pathName,  // IN:
                      uint64 *maxFileSize)   // OUT:
{
   int fd;
   Bool retval = TRUE;
   char *fullPath;

   char *dirPath = NULL;

   ASSERT(maxFileSize != NULL);

   fullPath = File_FullPath(pathName);
   if (fullPath == NULL) {
      Log(LGPFX" %s: Failed to get the full path for %s\n", __func__,
          pathName);
      retval = FALSE;
      goto bail;
   }

   if (File_IsDirectory(fullPath)) {
      dirPath = Unicode_Duplicate(fullPath);
   } else {
      dirPath = NULL;
      File_SplitName(fullPath, NULL, &dirPath, NULL);
   }

   /*
    * We always try to open the dir in order to avoid any contention on VMDK
    * descriptor file with those threads which already have descriptor file
    * opened for writing.
    */
   fd = Posix_Open(dirPath, O_RDONLY, 0);
   if (fd == -1) {
      Log(LGPFX" %s: could not open %s: %s\n", __func__, dirPath,
          Err_Errno2String(errno));
      retval = FALSE;
      goto bail;
   }

   if (ioctl(fd, IOCTLCMD_VMFS_GET_MAX_FILE_SIZE, maxFileSize) == -1) {
      Log(LGPFX" %s: Could not get max file size for path: %s, error: %s\n",
          __func__, pathName, Err_Errno2String(errno));
      retval = FALSE;
   }
   close(fd);

bail:
   Posix_Free(fullPath);
   Posix_Free(dirPath);

   return retval;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * FileVMKGetMaxOrSupportsFileSize --
 *
 *      Given a path to a file on a volume, either find out the max file size
 *      for the volume on which the file is located or check if the volume
 *      supports the given file size.
 *      If getMaxFileSize is set then find out the max file size and store it
 *      in *maxFileSize on success, otherwise figure out if *fileSize is
 *      supported.
 *
 * Results:
 *      If getMaxFileSize was set:
 *        TRUE if figured out the max file size.
 *        FALSE failed to figure out the max file size.
 *      Otherwise:
 *        TRUE fileSize is supported.
 *        FALSE fileSize not supported or could not figure out the answer.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static Bool
FileVMKGetMaxOrSupportsFileSize(const char *pathName,  // IN:
                                uint64 *fileSize,      // IN/OUT:
                                Bool getMaxFileSize)   // IN:
{
#if defined(VMX86_SERVER)
   FS_PartitionListResult *fsAttrs = NULL;
   uint64 maxFileSize;

   /*
    * Let's first try IOCTL to figure out max file size.
    */
   if (FileVMKGetMaxFileSize(pathName, &maxFileSize)) {
      if (getMaxFileSize) {
         *fileSize = maxFileSize;

         return TRUE;
      }
      return (*fileSize <= maxFileSize);
   }

   /*
    * Try the old way if IOCTL failed.
    */
   LOG(0, LGPFX" %s: Failed to figure out max file size via "
       "IOCTLCMD_VMFS_GET_MAX_FILE_SIZE. Falling back to old method.\n",
       __func__);
   maxFileSize = -1;

   if (File_GetVMFSAttributes(pathName, &fsAttrs) < 0) {
      Log(LGPFX" %s: File_GetVMFSAttributes Failed\n", __func__);

      return FALSE;
   }

   if (strcmp(fsAttrs->fsType, FS_VMFS_ON_ESX) == 0) {
      if (fsAttrs->versionNumber == 3) {
         maxFileSize = (VMFS3CONST * (uint64) fsAttrs->fileBlockSize * 1024);
      } else if (fsAttrs->versionNumber >= 5) {
         /* Get ready for 64 TB on VMFS5 and perform confidence check on version */
         maxFileSize = (uint64) 0x400000000000ULL;
      } else {
         Log(LGPFX" %s: Unsupported filesystem version, %u\n", __func__,
             fsAttrs->versionNumber);
         Posix_Free(fsAttrs);

         return FALSE;
      }

      Posix_Free(fsAttrs);
      if (maxFileSize == -1) {
         Log(LGPFX" %s: Failed to figure out the max file size for %s\n",
             __func__, pathName);
         return FALSE;
      }

      if (getMaxFileSize) {
         *fileSize = maxFileSize;
         return TRUE;
      } else {
         return *fileSize <= maxFileSize;
      }
   } else {
      char *fullPath;
      char *parentPath;
      Bool supported;

      Log(LGPFX" %s: Trying create file and seek approach.\n", __func__);
      fullPath = File_FullPath(pathName);

      if (fullPath == NULL) {
         Log(LGPFX" %s: Error acquiring full path\n", __func__);
         Posix_Free(fsAttrs);

         return FALSE;
      }

      File_GetPathName(fullPath, &parentPath, NULL);

      supported = FilePosixCreateTestGetMaxOrSupportsFileSize(parentPath,
                                                              fileSize,
                                                              getMaxFileSize);

      Posix_Free(fsAttrs);
      Posix_Free(fullPath);
      Posix_Free(parentPath);

      return supported;
   }

#endif
   Log(LGPFX" %s: did not execute properly\n", __func__);

   return FALSE; /* happy compiler */
}


/*
 *----------------------------------------------------------------------
 *
 * FileGetMaxOrSupportsFileSize --
 *
 *      Given a path to a file on a volume, either find out the max file size
 *      for the volume on which the file is located or check if the volume
 *      supports the given file size.
 *      If getMaxFileSize is set then find out the max file size and store it
 *      in *maxFileSize on success, otherwise figure out if *fileSize is
 *      supported.
 *
 * Results:
 *      If getMaxFileSize was set:
 *        TRUE if figured out the max file size.
 *        FALSE failed to figure out the max file size.
 *      Otherwise:
 *        TRUE fileSize is supported.
 *        FALSE fileSize not supported or could not figure out the answer.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
FileGetMaxOrSupportsFileSize(const char *pathName,  // IN:
                             uint64 *fileSize,      // IN/OUT:
                             Bool getMaxFileSize)   // IN:
{
   char *fullPath;
   char *folderPath;
   Bool retval = FALSE;

   ASSERT(pathName != NULL);
   ASSERT(fileSize != NULL);

   /*
    * We acquire the full path name for testing in
    * FilePosixCreateTestGetMaxOrSupportsFileSize().  This is also done in the
    * event that a user tries to create a virtual disk in the directory that
    * they want a vmdk created in (setting filePath only to the disk name,
    * not the entire path.).
    */

   fullPath = File_FullPath(pathName);
   if (fullPath == NULL) {
      Log(LGPFX" %s: Error acquiring full path for path: %s.\n", __func__,
          pathName);
      goto out;
   }

   if (HostType_OSIsVMK()) {
      retval = FileVMKGetMaxOrSupportsFileSize(fullPath, fileSize,
                                               getMaxFileSize);
      goto out;
   }

   if (File_IsFile(fullPath)) {
      FileIOResult res;
      FileIODescriptor fd;

      FileIO_Invalidate(&fd);
      res = FileIO_Open(&fd, fullPath, FILEIO_OPEN_ACCESS_READ, FILEIO_OPEN);
      if (FileIO_IsSuccess(res)) {
         retval = FilePosixGetMaxOrSupportsFileSize(&fd, fileSize,
                                                    getMaxFileSize);
         FileIO_Close(&fd);
         goto out;
      }
   }

   /*
    * On unknown filesystems create a temporary file in the argument file's
    * parent directory and use it as a test.
    */

   if (File_IsDirectory(pathName)) {
      folderPath = Unicode_Duplicate(fullPath);
   } else {
      folderPath = NULL;
      File_SplitName(fullPath, NULL, &folderPath, NULL);
   }

   retval = FilePosixCreateTestGetMaxOrSupportsFileSize(folderPath, fileSize,
                                                        getMaxFileSize);
   Posix_Free(folderPath);

out:
   Posix_Free(fullPath);

   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * File_GetMaxFileSize --
 *
 *      Given a path to a file on a volume, return the max file size for that
 *      volume. The max file size is stored on *maxFileSize on success.
 *      The function caps the max file size to be MAX_SUPPORTED_FILE_SIZE on
 *      any type of FS.
 *
 * Results:
 *      TRUE on success.
 *      FALSE failed to figure out max file size due to some reasons.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_GetMaxFileSize(const char *pathName,  // IN:
                    uint64 *maxFileSize)   // OUT:
{
   Bool result;

   if (maxFileSize == NULL) {
      Log(LGPFX" %s: maxFileSize passed as NULL.\n", __func__);

      return FALSE;
   }

   result = FileGetMaxOrSupportsFileSize(pathName, maxFileSize, TRUE);
   if (result) {
      /*
       * Cap the max supported file size at MAX_SUPPORTED_FILE_SIZE.
       */
      if (*maxFileSize > MAX_SUPPORTED_FILE_SIZE) {
         *maxFileSize = MAX_SUPPORTED_FILE_SIZE;
      }
   }
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * File_SupportsFileSize --
 *
 *      Check if the given file is on an FS that supports such file size.
 *      The function caps the max supported file size to be
 *      MAX_SUPPORTED_FILE_SIZE on any type of FS.
 *
 * Results:
 *      TRUE if FS supports such file size.
 *      FALSE otherwise (file size not supported, invalid path, read-only, ...)
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
File_SupportsFileSize(const char *pathName,  // IN:
                      uint64 fileSize)       // IN:
{
   /*
    * All supported filesystems can hold at least 2GB-1 bytes files.
    */
   if (fileSize <= 0x7FFFFFFF) {
      return TRUE;
   }

   /*
    * Cap the max supported file size at MAX_SUPPORTED_FILE_SIZE.
    */
   if (fileSize > MAX_SUPPORTED_FILE_SIZE) {
      return FALSE;
   }

   return FileGetMaxOrSupportsFileSize(pathName, &fileSize, FALSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileCreateDirectory --
 *
 *      Create a directory. The umask is honored.
 *
 * Results:
 *      0    success
 *    > 0  failure (errno)
 *
 * Side effects:
 *      May change the host file system.
 *
 *-----------------------------------------------------------------------------
 */

int
FileCreateDirectory(const char *pathName,  // IN:
                    int mask)              // IN:
{
   int err;

   if (pathName == NULL) {
      err = errno = EFAULT;
   } else {
      err = (Posix_Mkdir(pathName, mask) == -1) ? errno : 0;
   }

   return err;
}


/*
 *----------------------------------------------------------------------
 *
 * File_ListDirectory --
 *
 *      Gets the list of files (and directories) in a directory.
 *
 * Results:
 *      Returns the number of files returned or -1 on failure.
 *
 * Side effects:
 *      If ids is provided and the function succeeds, memory is
 *      allocated for both the unicode strings and the array itself
 *      and must be freed.  (See Util_FreeStringList.)
 *      The memory allocated for the array may be larger than necessary.
 *      The caller may trim it with realloc() if it cares.
 *
 *      A file name that cannot be represented in the default encoding
 *      will appear as a string of three UTF8 substitution characters.
 *
 *----------------------------------------------------------------------
 */

typedef struct {
   char   **fileNames;
   uint32   pos;
} ListParams;

static int
FileNameArray(const char *key,   // IN:
              void *value,       // IN:
              void *clientData)  // IN/OUT: ListParams pointer
{
   ListParams *params = clientData;

   ASSERT(value == NULL);

   params->fileNames[params->pos++] = Util_SafeStrdup(key);

   return 0;
}

int
File_ListDirectory(const char *dirName,  // IN:
                   char ***ids)          // OUT: relative paths
{
   int count = -1;
   WalkDirContext context = File_WalkDirectoryStart(dirName);

   if (context != NULL) {
      int err;

      while (File_WalkDirectoryNext(context, NULL))
         ;

      err = errno;

      if (err == 0) {
         count = HashTable_GetNumElements(context->hash);

         if (ids) {
            if (count == 0) {
               *ids = NULL;
            } else {
               ListParams params;

               params.fileNames = Util_SafeCalloc(count, sizeof(char *));
               params.pos = 0;

               HashTable_ForEach(context->hash, FileNameArray, &params);
               *ids = params.fileNames;
            }
         }
      }

      File_WalkDirectoryEnd(context);

      errno = err;
   }

   return count;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_WalkDirectoryEnd --
 *
 *      End the directory traversal.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      The context is now invalid.
 *
 *-----------------------------------------------------------------------------
 */

static int
FileKeyDispose(const char *key,   // IN:
               void *value,       // IN:
               void *clientData)  // IN:
{
   Posix_Free((void *) key);

   return 0;
}

void
File_WalkDirectoryEnd(WalkDirContext context)  // IN:
{
   if (context != NULL) {
      if (context->dir != NULL) {
         closedir(context->dir);
      }

      HashTable_ForEach(context->hash, FileKeyDispose, NULL);

      HashTable_Free(context->hash);

      Posix_Free(context->dirName);
      Posix_Free(context);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_WalkDirectoryStart --
 *
 *      Start a directory tree walk at 'parentPath'.
 *
 *      To read each entry, repeatedly pass the returned context to
 *      File_WalkDirectoryNext() until that function returns FALSE.
 *
 *      We assume no thread will change the working directory between the calls
 *      to File_WalkDirectoryStart and File_WalkDirectoryEnd.
 *
 * Results:
 *      A context used in subsequent calls to File_WalkDirectoryNext() or NULL
 *      if an error is encountered.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

WalkDirContext
File_WalkDirectoryStart(const char *dirName)  // IN:
{
   WalkDirContextImpl *context;

   ASSERT(dirName != NULL);

   context = Util_SafeMalloc(sizeof *context);

   context->dirName = Util_SafeStrdup(dirName);
   context->hash = HashTable_Alloc(2048, HASH_STRING_KEY, NULL);
   context->dir = Posix_OpenDir(dirName);

   if (context->dir == NULL) {
      int err = errno;

      File_WalkDirectoryEnd(context);

      errno = err;
      context = NULL;
   }

   return context;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_WalkDirectoryNext --
 *
 *      Get the next file name during a directory traversal started with
 *      File_WalkDirectoryStart.
 *
 * Results:
 *      TRUE   Traversal hasn't completed yet. If *fileName is not NULL,
 *             an allocated string of the file name found is returned.
 *             The caller must free the string.
 *      FALSE  The traversal has completed. Check errno; if it is zero (0),
 *             the walk completed sucessfully.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_WalkDirectoryNext(WalkDirContext context,  // IN:
                       char **fileName)         // OUT/OPT:
{
   int err = 0;
   Bool callAgain = FALSE;

   ASSERT(context != NULL);

   while (TRUE) {
      char *allocName;
      struct dirent *entry;

      errno = 0;
      entry = readdir(context->dir);
      if (entry == NULL) {
         err = errno;
         break;
      }

      /* Strip out undesirable paths. No one ever cares about these. */
      if ((strcmp(entry->d_name, ".") == 0) ||
          (strcmp(entry->d_name, "..") == 0)) {
         continue;
      }

      /*
       * It is possible for a directory operation to change the contents
       * of a directory while this routine is running. If the OS decides
       * to physically rearrange the contents of the directory it is
       * possible for readdir to report a file more than once. Only add
       * a file to the return data if it is unique within the return
       * data.
       */

#if defined(VMX86_SERVER) || defined(__APPLE__)  // UTF8 native platforms
      if (CodeSet_IsStringValidUTF8(entry->d_name)) {
         allocName = Util_SafeStrdup(entry->d_name);
#else
      if (Unicode_IsBufferValid(entry->d_name, -1, STRING_ENCODING_DEFAULT)) {
         allocName = Unicode_Alloc(entry->d_name, STRING_ENCODING_DEFAULT);
#endif
      } else {
         char *id = Unicode_EscapeBuffer(entry->d_name, -1,
                                         STRING_ENCODING_DEFAULT);

         Warning("%s: file '%s' in directory '%s' cannot be converted to "
                 "UTF8\n", __FUNCTION__, context->dirName, id);

         Posix_Free(id);

         allocName = Unicode_Duplicate(UNICODE_SUBSTITUTION_CHAR
                                       UNICODE_SUBSTITUTION_CHAR
                                       UNICODE_SUBSTITUTION_CHAR);
      }

      if (HashTable_Insert(context->hash, allocName, NULL)) {  // Unique - good
         if (fileName != NULL) {
            *fileName = Util_SafeStrdup(allocName);
         }

         callAgain = TRUE;
         break;
      } else {  // Duplicate - ignore
         free(allocName);
         continue;
      }
   }

   errno = err;

   return callAgain;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIsGroupsMember --
 *
 *      Determine if a gid is in the gid list of the current process
 *
 * Results:
 *      FALSE if error (reported to the user)
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static Bool
FileIsGroupsMember(gid_t gid)  // IN:
{
   int nr_members;
   gid_t *members;
   int res;
   int ret;

   members = NULL;
   nr_members = 0;

   for (;;) {
      gid_t *new;

      res = getgroups(nr_members, members);
      if (res == -1) {
         Warning(LGPFX" %s: Couldn't getgroups\n", __FUNCTION__);
         ret = FALSE;
         goto end;
      }

      if (res == nr_members) {
         break;
      }

      /* Was bug 17760 --hpreg */
      new = realloc(members, res * sizeof *members);
      if (new == NULL) {
         Warning(LGPFX" %s: Couldn't realloc\n", __FUNCTION__);
         ret = FALSE;
         goto end;
      }

      members = new;
      nr_members = res;
   }

   for (res = 0; res < nr_members; res++) {
      if (members[res] == gid) {
         ret = TRUE;
         goto end;
      }
   }
   ret = FALSE;

end:
   Posix_Free(members);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * FileIsWritableDir --
 *
 *      Determine in a non-intrusive way if the user can create a file in a
 *      directory
 *
 * Results:
 *      FALSE if error (reported to the user)
 *
 * Side effects:
 *      None
 *
 * Bug:
 *      It would be cleaner to use the POSIX access(2), which deals well
 *      with read-only filesystems. Unfortunately, access(2) doesn't deal with
 *      the effective [u|g]ids.
 *
 *----------------------------------------------------------------------
 */

Bool
FileIsWritableDir(const char *dirName)  // IN:
{
   int err;
   uid_t euid;
   FileData fileData;

   err = FileAttributes(dirName, &fileData);

   if ((err != 0) || (fileData.fileType != FILE_TYPE_DIRECTORY)) {
      return FALSE;
   }

   euid = geteuid();
   if (euid == 0) {
      /* Root can read or write any file. Well... This is not completely true
         because of read-only filesystems and NFS root squashing... What a
         nightmare --hpreg */

      return TRUE;
   }

   if (fileData.fileOwner == euid) {
      fileData.fileMode >>= 6;
   } else if (FileIsGroupsMember(fileData.fileGroup)) {
      fileData.fileMode >>= 3;
   }

   /* Check for Read and Execute permissions */
   return (fileData.fileMode & 3) == 3;
}


/*
 *----------------------------------------------------------------------
 *
 * File_MakeCfgFileExecutable --
 *
 *	Make a .vmx file executable. This is sometimes necessary
 *      to enable MKS access to the VM.
 *
 *      Owner always gets rwx.  Group/other get x where r is set.
 *
 * Results:
 *      FALSE if error
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

Bool
File_MakeCfgFileExecutable(const char *pathName)  // IN:
{
   struct stat s;

   if (Posix_Stat(pathName, &s) == 0) {
      mode_t newMode = s.st_mode;

      newMode |= S_IRUSR | S_IWUSR | S_IXUSR;

      ASSERT_ON_COMPILE(S_IRGRP >> 2 == S_IXGRP && S_IROTH >> 2 == S_IXOTH);
      newMode |= ((newMode & (S_IRGRP | S_IROTH)) >> 2);

      return newMode == s.st_mode || Posix_Chmod(pathName, newMode);
   }
   return FALSE;
}


/*
 *----------------------------------------------------------------------------
 *
 * File_GetSizeAlternate --
 *
 *      An alternate way to determine the filesize. Useful for finding
 *      problems with files on remote fileservers, such as described in bug
 *      19036. However, in Linux we do not have an alternate way, yet, to
 *      determine the problem, so we call back into the regular getSize
 *      function.
 *
 * Results:
 *      Size of file or -1.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

int64
File_GetSizeAlternate(const char *pathName)  // IN:
{
   return File_GetSize(pathName);
}


/*
 *----------------------------------------------------------------------------
 *
 * File_IsCharDevice --
 *
 *      This function checks whether the given file is a char device
 *      and return TRUE in such case. This is often useful on Windows
 *      where files like COM?, LPT? must be differentiated from "normal"
 *      disk files.
 *
 * Results:
 *      TRUE    is a character device
 *      FALSE   is not a character device or error
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

Bool
File_IsCharDevice(const char *pathName)  // IN:
{
   FileData fileData;

   return (FileAttributes(pathName, &fileData) == 0) &&
           (fileData.fileType == FILE_TYPE_CHARDEVICE);
}


/*
 *----------------------------------------------------------------------------
 *
 * File_GetMountPath --
 *
 *      This function translates the path for a symlink to the physical path.
 *      If checkEntirePath is TRUE, this function will try to translate
 *      every parent directory to physical path.
 *      Caller must free the returned buffer if valid path is returned.
 *
 * Results:
 *      return valid physical path if successfully.
 *      return NULL if error.
 *
 * Side effects:
 *      The result is allocated.
 *
 *----------------------------------------------------------------------------
 */

char *
File_GetMountPath(const char *pathName,  // IN:
                  Bool checkEntirePath)  // IN:
{
   char *mountPath;

   if (pathName == NULL) {
      return NULL;
   }

   if (checkEntirePath) {
      return Posix_RealPath(pathName);
   }

   mountPath = Posix_ReadLink(pathName);
   if (mountPath != NULL) {
      return mountPath;
   }

   if (!Posix_Access(pathName, F_OK)) {
      return Util_SafeStrdup(pathName);
   }

   return NULL;
}
0707010000004D000081A40000000000000000000000016822550500004CCD000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/file/fileStandAlone.c  /*********************************************************
 * Copyright (C) 1998-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fileStandAlone.c --
 *
 * This file contains lib/file routines which are unentangled - they do
 * not depend on other libraries besides lib/misc and its dependencies.
 */

#if defined(_WIN32)
#include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#if !defined(_WIN32)
#include <unistd.h>
#endif
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#include "vmware.h"
#include "util.h"
#include "str.h"
#include "strutil.h"
#include "posix.h"
#include "file.h"

#include "unicodeOperations.h"


/*
 *----------------------------------------------------------------------
 *
 * File_GetModTime --
 *
 *      Get the last modification time of a file and return it. The time
 *      unit is seconds since the POSIX/UNIX/Linux epoch.
 *
 * Results:
 *      Last modification time of file or -1 if error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int64
File_GetModTime(const char *pathName)  // IN:
{
   int64 theTime;
   struct stat statbuf;

   if (Posix_Stat(pathName, &statbuf) == 0) {
      theTime = statbuf.st_mtime;
   } else {
      theTime = -1;
   }

   return theTime;
}


#if defined(_WIN32)
/*
 *---------------------------------------------------------------------------
 *
 * FileFindFirstDirsep --
 *
 *      Return a pointer to the first directory separator.
 *
 * Results:
 *      NULL  No directory separator found
 *     !NULL  Pointer to the last directory separator
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

static char *
FileFindFirstDirsep(const char *pathName)  // IN:
{
   char *p;

   ASSERT(pathName != NULL);

   p = (char *) pathName;

   while (*p != '\0') {
      if (File_IsDirsep(*p)) {
         return p;
      }

      p++;
   }

   return NULL;
}
#endif


/*
 *---------------------------------------------------------------------------
 *
 * FileFindLastDirsep --
 *
 *      Return a pointer to the last directory separator.
 *
 * Results:
 *      NULL  No directory separator found
 *     !NULL  Pointer to the last directory separator
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

static char *
FileFindLastDirsep(const char *pathName,  // IN:
                   size_t len)            // IN:
{
   char *p;

   ASSERT(pathName != NULL);

   p = (char *) pathName + len;

   while (p-- != pathName) {
      if (File_IsDirsep(*p)) {
         return p;
      }
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * File_SplitName --
 *
 *      Split a file name into three components: VOLUME, DIRECTORY,
 *      BASE.  The return values must be freed.
 *
 *      VOLUME is empty for an empty string or a UNIX-style path, the
 *      drive letter and colon for a Win32 drive-letter path, or the
 *      construction "\\server\share" for a Win32 UNC path.
 *
 *      BASE is the longest string at the end that begins after the
 *      volume string and after the last directory separator.
 *
 *      DIRECTORY is everything in-between VOLUME and BASE.
 *
 *      The concatenation of VOLUME, DIRECTORY, and BASE produces the
 *      original string, so any of those strings may be empty.
 *
 *      A NULL pointer may be passed for one or more OUT parameters, in
 *      which case that parameter is not returned.
 *
 *      Able to handle both UNC and drive-letter paths on Windows.
 *
 * Results:
 *      As described.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
File_SplitName(const char *pathName,  // IN:
               char **volume,         // OUT/OPT:
               char **directory,      // OUT/OPT:
               char **base)           // OUT/OPT:
{
   char *vol;
   char *dir;
   char *bas;
   size_t len;
   char *baseBegin;
   char *volEnd;
   int volLen, dirLen;

   ASSERT(pathName != NULL);

   len = strlen(pathName);

   /*
    * Get volume.
    */

   volEnd = (char *) pathName;

#ifdef WIN32
   if ((len > 2) &&
       (!Str_Strncmp("\\\\", pathName, 2) ||
        !Str_Strncmp("//", pathName, 2))) {
      /* UNC path */
      volEnd = FileFindFirstDirsep(volEnd + 2);

      if (volEnd != NULL) {
         volEnd = FileFindFirstDirsep(volEnd + 1);

         if (volEnd == NULL) {
            /* We have \\foo\bar, which is legal */
            volEnd = (char *) pathName + len;
         }

      } else {
         /* We have \\foo, which is just bogus */
         volEnd = (char *) pathName;
      }
   } else if ((len >= 2) && (pathName[1] == ':')) {
      // drive-letter path
      volEnd = (char *) pathName + 2;
   }
#endif /* WIN32 */

   volLen = volEnd - pathName;
   vol = Util_SafeMalloc(volLen + 1);
   memcpy(vol, pathName, volLen);
   vol[volLen] = '\0';

   /*
    * Get base.
    */

   baseBegin = FileFindLastDirsep(pathName, len);
   baseBegin = (baseBegin == NULL) ? (char *) pathName : baseBegin + 1;

   if (baseBegin < volEnd) {
      baseBegin = (char *) pathName + len;
   }

   bas = Util_SafeStrdup(baseBegin);

   /*
    * Get dir.
    */

   dirLen = baseBegin - volEnd;
   dir = Util_SafeMalloc(dirLen + 1);
   memcpy(dir, volEnd, dirLen);
   dir[dirLen] = '\0';

   /*
    * Return what needs to be returned.
    */

   if (volume == NULL) {
      free(vol);
   } else {
      *volume = vol;
   }

   if (directory == NULL) {
      free(dir);
   } else {
      *directory = dir;
   }

   if (base == NULL) {
      free(bas);
   } else {
      *base = bas;
   }
}


/*
 *---------------------------------------------------------------------------
 *
 * File_PathJoin --
 *
 *      Join the dirName and baseName together to create a (full) path.
 *
 *      This code concatenates two strings together and omits a redundant
 *      directory separator between the two.
 *
 *      On Windows, the 'baseName' argument may not be a fully qualified path.
 *      That is, it may not be an absolute path containing a drive letter nor
 *      may it be a UNC path.
 *
 * Examples:
 *      File_PathJoin("", "b")            -> "/b"
 *      File_PathJoin("/", "b")           -> "/b"
 *      File_PathJoin("a", "b")           -> "a/b"
 *      File_PathJoin("a/", "b")          -> "a/b"
 *      File_PathJoin("a/////", "b")      -> "a/b"
 *      File_PathJoin("a", "")            -> "a/"
 *      File_PathJoin("a", "/")           -> "a/"
 *      File_PathJoin("a", "/b")          -> "a/b"
 *      File_PathJoin("a", "/////b")      -> "a/b" (only posix)
 *      File_PathJoin("a/", "/b")         -> "a/b"
 *      File_PathJoin("a/////", "/////b") -> "a/b" (only posix)
 *
 * Results:
 *      The constructed path which must be freed by the caller.
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

char *
File_PathJoin(const char *dirName,   // IN:
              const char *baseName)  // IN: See above.
{
   char *result;
   char *newDir = NULL;

   ASSERT(dirName);
   ASSERT(baseName);

   /*
    * Remove ALL directory separators from baseName begin.
    */
#if defined(_WIN32)
   {
      const char *oldBaseName = baseName;

      /*
       * Reject drive letters in baseName.
       */
      ASSERT(Unicode_LengthInCodePoints(baseName) < 2 ||
             Unicode_FindSubstrInRange(baseName, 1, 1, ":", 0, 1) ==
             UNICODE_INDEX_NOT_FOUND);

      while (*baseName == '/' || *baseName == '\\') {
         baseName++;
      }

      /*
       * Reject UNC paths for baseName.
       */
      ASSERT(baseName - oldBaseName < 2);
   }
#else
   while (*baseName == '/') {
      baseName++;
   }
#endif

   /*
    * Remove ALL directory separators from dirName end.
    */
   newDir = File_StripSlashes(dirName);

   result = Unicode_Join(newDir, DIRSEPS, baseName, NULL);
   Posix_Free(newDir);

   return result;
}


/*
 *---------------------------------------------------------------------------
 *
 * File_GetPathName --
 *
 *      Behaves like File_SplitName by splitting the fullpath into
 *      pathname & filename components.
 *
 *      The trailing directory separator [\|/] is stripped off the
 *      pathname component. This in turn means that on Linux the root
 *      directory will be returned as the empty string "". On Windows
 *      it will be returned as X: where X is the drive letter. It is
 *      important that callers of this functions are aware that the ""
 *      on Linux means root "/".
 *
 *      A NULL pointer may be passed for one or more OUT parameters,
 *      in which case that parameter is not returned.
 *
 * Results:
 *      As described.
 *
 * Side effects:
 *      The return values must be freed.
 *
 *---------------------------------------------------------------------------
 */

void
File_GetPathName(const char *fullPath,  // IN:
                 char **pathName,       // OUT/OPT:
                 char **baseName)       // OUT/OPT:
{
   char *p;
   char *pName;
   char *bName;
   ASSERT(fullPath);

   p = FileFindLastDirsep(fullPath, strlen(fullPath));

   if (p == NULL) {
      pName = Util_SafeStrdup("");
      bName = Util_SafeStrdup(fullPath);
   } else {
      bName = Util_SafeStrdup(&fullPath[p - fullPath + 1]);
      pName = Util_SafeStrdup(fullPath);
      pName[p - fullPath] = '\0';

      p = &pName[p - fullPath];

      while (p-- != pName) {
         if (File_IsDirsep(*p)) {
            *p = '\0';
         } else {
            break;
         }
      }
   }

   if (pathName == NULL) {
      free(pName);
   } else {
      *pathName = pName;
   }

   if (baseName == NULL) {
      free(bName);
   } else {
      *baseName = bName;
   }
}


/*
 *----------------------------------------------------------------------
 *
 *  File_StripSlashes --
 *
 *      Strip trailing slashes from the end of a path.
 *
 * Results:
 *      The stripped filename.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

char *
File_StripSlashes(const char *path)  // IN:
{
   char *result;
   char *volume;
   char *dir;
   char *base;

   /*
    * SplitName handles all drive letter/UNC/whatever cases, all we
    * have to do is make sure the dir part is stripped of slashes if
    * there isn't a base part.
    */

   File_SplitName(path, &volume, &dir, &base);

   if (!Unicode_IsEmpty(dir) && Unicode_IsEmpty(base)) {
      char *dir2 = Unicode_GetAllocBytes(dir, STRING_ENCODING_UTF8);
      size_t i = strlen(dir2);

      /*
       * Don't strip first slash on Windows, since we want at least
       * one slash to trail a drive letter/colon or UNC specifier.
       */

#if defined(_WIN32)
      while ((i > 1) && File_IsDirsep(dir2[i - 1])) {
#else
      while ((i > 0) && File_IsDirsep(dir2[i - 1])) {
#endif
         i--;
      }

      Posix_Free(dir);
      dir = Unicode_AllocWithLength(dir2, i, STRING_ENCODING_UTF8);
      Posix_Free(dir2);
   }

   result = Unicode_Join(volume, dir, base, NULL);

   Posix_Free(volume);
   Posix_Free(dir);
   Posix_Free(base);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_MapPathPrefix --
 *
 *      Given a path and a newPrefix -> oldPrefix mapping, transform
 *      oldPath according to the mapping.
 *
 * Results:
 *      The new path, or NULL if there is no mapping.
 *
 * Side effects:
 *      The returned string is allocated, free it.
 *
 *-----------------------------------------------------------------------------
 */

char *
File_MapPathPrefix(const char *oldPath,       // IN:
                   const char **oldPrefixes,  // IN:
                   const char **newPrefixes,  // IN:
                   size_t numPrefixes)        // IN:
{
   int i;
   size_t oldPathLen = strlen(oldPath);

   for (i = 0; i < numPrefixes; i++) {
      char *oldPrefix;
      char *newPrefix;
      size_t oldPrefixLen;

      oldPrefix = File_StripSlashes(oldPrefixes[i]);
      newPrefix = File_StripSlashes(newPrefixes[i]);
      oldPrefixLen = strlen(oldPrefix);

      /*
       * If the prefix matches on a DIRSEPS boundary, or the prefix is the
       * whole string, replace it.
       *
       * If we don't insist on matching a whole directory name, we could
       * mess things of if one directory is a substring of another.
       *
       * Perform a case-insensitive compare on Windows. (There are
       * case-insensitive filesystems on MacOS also, but the problem
       * is more acute with Windows because of frequent drive-letter
       * case mismatches. So in lieu of actually asking the
       * filesystem, let's just go with a simple ifdef for now.)
       */

      if ((oldPathLen >= oldPrefixLen) &&
#ifdef _WIN32
          (Str_Strncasecmp(oldPath, oldPrefix, oldPrefixLen) == 0) &&
#else
          (Str_Strncmp(oldPath, oldPrefix, oldPrefixLen) == 0) &&
#endif
          (strchr(VALID_DIRSEPS, oldPath[oldPrefixLen]) ||
              (oldPath[oldPrefixLen] == '\0'))) {
         size_t newPrefixLen = strlen(newPrefix);
         size_t newPathLen = (oldPathLen - oldPrefixLen) + newPrefixLen;
         char *newPath;

         ASSERT(newPathLen > 0);
         ASSERT(oldPathLen >= oldPrefixLen);

         newPath = Util_SafeMalloc((newPathLen + 1) * sizeof(char));
         memcpy(newPath, newPrefix, newPrefixLen);
         memcpy(newPath + newPrefixLen, oldPath + oldPrefixLen,
                oldPathLen - oldPrefixLen + 1);
         /*
          * It should only match once.  Weird self-referencing mappings
          * aren't allowed.
          */

         Posix_Free(oldPrefix);
         Posix_Free(newPrefix);

         return newPath;
      }
      Posix_Free(oldPrefix);
      Posix_Free(newPrefix);
   }

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_PrependToPath --
 *
 *      This function checks if the elem is already present in the
 *      searchPath, if it is then it is moved forward in the search path.
 *      Otherwise it is prepended to the searchPath.
 *
 * Results:
 *      Return file search path with elem in front.
 *
 * Side effects:
 *      Caller must free returned string.
 *
 *-----------------------------------------------------------------------------
 */

char *
File_PrependToPath(const char *searchPath,  // IN:
                   const char *elem)        // IN:
{
   const char sep = FILE_SEARCHPATHTOKEN[0];
   char *newPath;
   char *path;
   size_t n;

   ASSERT(searchPath);
   ASSERT(elem);

   newPath = Str_SafeAsprintf(NULL, "%s%s%s", elem, FILE_SEARCHPATHTOKEN,
                              searchPath);

   n = strlen(elem);
   path = newPath + n + 1;

   for (;;) {
      char *next = Str_Strchr(path, sep);
      size_t len = next ? next - path : strlen(path);

      if ((len == n) && (Str_Strncmp(path, elem, len) == 0)) {
         if (next) {
            memmove(path, next + 1, strlen(next + 1) + 1);
         } else {
            *--path = '\0';
         }
         break;
      }

      if (!next) {
         break;
      }
      path = next + 1;
   }

   return newPath;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_ReplaceExtension --
 *
 *      Replaces the extension in input with newExtension.
 *
 *      If the old extension exists in the list of extensions specified in ...,
 *      truncate it before appending the new extension.
 *
 *      If the extension is not found in the list, the newExtension is
 *      just appended.
 *
 *      If there isn't a list of extensions specified (numExtensions == 0),
 *      truncate the old extension unconditionally.
 *
 *      NB: newExtension and the extension list must have .'s.
 *
 * Results:
 *      The name with newExtension added to it. The caller is responsible to
 *      free it when they are done with it.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
File_ReplaceExtension(const char *pathName,      // IN:
                      const char *newExtension,  // IN:
                      uint32 numExtensions,      // IN:
                      ...)                       // IN:
{
   char *p;
   char *place;
   char *result;
   size_t newExtLen;
   size_t resultLen;
   size_t pathNameLen;

   ASSERT(pathName);
   ASSERT(newExtension);
   ASSERT(*newExtension == '.');

   pathNameLen = strlen(pathName);
   newExtLen = strlen(newExtension);
   resultLen = pathNameLen + newExtLen + 1;
   result = Util_SafeMalloc(resultLen);

   memcpy(result, pathName, pathNameLen + 1);

   p = FileFindLastDirsep(result, pathNameLen);
   if (p == NULL) {
       p = strrchr(result, '.');
   } else {
       p = strrchr(p, '.');
   }

   if (p == NULL) {
      /* No extension... just append */
      place = &result[pathNameLen];  // The NUL
   } else if (numExtensions == 0) {
      /* Always truncate the old extension if extension list is empty. */
      place = p;  // The '.'
   } else {
      uint32 i;
      va_list arguments;

      /*
       * Only truncate the old extension if it exists in the valid
       * extensions list.
       */

      place = &result[pathNameLen];  // The NUL

      va_start(arguments, numExtensions);

      for (i = 0; i < numExtensions ; i++) {
         const char *oldExtension = va_arg(arguments, const char *);

         ASSERT(*oldExtension == '.');

         if (strcmp(p, oldExtension) == 0) {
            place = p;  // The '.'
            break;
         }
      }

      va_end(arguments);
   }

   /* Add the new extension - in the appropriate place - to pathName */
   memcpy(place, newExtension, newExtLen + 1);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_RemoveExtension --
 *
 *      Return a copy of the given path name with the extension
 *      removed. We ASSERT that the given path does have an extension.
 *
 * Results:
 *      A newly allocated buffer with the modified string. The caller
 *      is responsible to free it when they are done with it.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
File_RemoveExtension(const char *pathName)  // IN:
{
   char *p;
   char *result;

   ASSERT(pathName != NULL);

   result = Util_SafeStrdup(pathName);

   p = FileFindLastDirsep(result, strlen(pathName));
   if (p == NULL) {
       p = strrchr(result, '.');
   } else {
       p = strrchr(p, '.');
   }

   ASSERT(p != NULL);

   if (p != NULL) {
      *p = '\0';
   }

   return result;
}
   0707010000004E000081A40000000000000000000000016822550500002F55000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/file/fileTemp.c    /*********************************************************
 * Copyright (C) 2011-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#if defined(_WIN32)
#include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#include "vmware.h"
#include "log.h"
#include "file.h"
#include "fileInt.h"
#include "util.h"
#include "unicodeOperations.h"
#include "posix.h"

#if !defined(O_BINARY)
#define O_BINARY 0
#endif


/*
 *----------------------------------------------------------------------
 *
 *  FileTempNum --
 *
 *      Compute a number to be used as an attachment to a base name.
 *      In order to avoid race conditions, files and directories are
 *      kept separate via enforced odd (file) and even (directory)
 *      numberings.
 *
 *      Regardless of the input value of *var, the output value will
 *      be even or odd as determined by createTempFile.
 *
 * Results:
 *      An odd number if createTempFile is TRUE.
 *      An even number if createTempFile is FALSE.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static void
FileTempNum(Bool createTempFile,  // IN:
            uint32 *var)          // IN/OUT:
{
   ASSERT(var != NULL);

   *var += (FileSimpleRandom() >> 8) & 0xFF;
   *var = (*var & ~0x1) | (createTempFile ? 1 : 0);
}


/*
 *----------------------------------------------------------------------
 *
 *  FileMakeTempEx2Work --
 *
 *      Create a temporary file or a directory.
 *
 *      If a temporary file is created successfully, then return an open file
 *      descriptor to that file.
 *
 *      'dir' specifies the directory in which to create the object. It
 *      must not end in a slash.
 *
 *      'createTempFile', if TRUE, then a temporary file will be created. If
 *      FALSE, then a temporary directory will be created.
 *
 *      'makeSubdirSafe', if TRUE, then if a directory is requested,
 *      the directory will be made "safe". This also requires that
 *      'dir' already be safe (the code will check this).
 *
 *      'createNameFunc' specifies the user-specified callback function that
 *      will be called to construct a fileName. 'createNameFuncData' will be
 *      passed everytime 'createNameFunc' is called. 'createNameFunc'
 *      should return the dynamically allocated, proper fileName.
 *
 *      Check the documentation for File_MakeTempHelperFunc.
 *
 * Results:
 *      if a temporary file is created, then Open file descriptor or -1;
 *      if a temporary directory is created, then 0 or -1;
 *      If successful then presult points to a dynamically allocated
 *      string with the pathname of the temp object created.
 *
 * Side effects:
 *      Creates the requested object when successful. Errno is set on error
 *
 *      Files and directories are effectively in separate name spaces;
 *      the numerical value attached via createNameFunc is odd for files
 *      and even for directories.
 *
 *----------------------------------------------------------------------
 */

static int
FileMakeTempEx2Work(const char *dir,                              // IN:
                    Bool createTempFile,                          // IN:
                    Bool makeSubdirSafe,                          // IN:
                    File_MakeTempCreateNameFunc *createNameFunc,  // IN:
                    void *createNameFuncData,                     // IN:
                    char **presult)                               // OUT:
{
   uint32 i;

   int fd;
   uint32 var = 0;

   ASSERT(presult != NULL);

   if ((dir == NULL) || (createNameFunc == NULL)) {
      errno = EFAULT;

      return -1;
   }

   *presult = NULL;

   for (i = 0; i < (MAX_INT32 / 2); i++) {
      char *objName;
      char *pathName;

      /*
       * Files and directories are kept separate (odd and even respectfully).
       * This way the available exclusion mechanisms work properly - O_EXCL
       * on files, mkdir on directories - and races are avoided.
       *
       * Not attempting an open on a directory is a good thing...
       */

      FileTempNum(createTempFile, &var);

      objName = (*createNameFunc)(var, createNameFuncData);
      ASSERT(objName != NULL);

      if (createTempFile) {
         pathName = File_PathJoin(dir, objName);
         fd = Posix_Open(pathName, O_CREAT | O_EXCL | O_BINARY | O_RDWR, 0600);
      } else {
         if (makeSubdirSafe) {
            pathName = File_MakeSafeTempSubdir(dir, objName);
            fd = (pathName == NULL) ? -1 : 0;
         } else {
            pathName = File_PathJoin(dir, objName);
            fd = Posix_Mkdir(pathName, 0700);
         }
      }

      if (fd != -1) {
         *presult = pathName;

         Posix_Free(objName);
         break;
      }

      Posix_Free(pathName);

      if (errno != EEXIST) {
         Log(LGPFX" Failed to create temporary %s; dir \"%s\", "
             "objName \"%s\", errno %d\n",
             createTempFile ? "file" : "directory",
             dir, objName, errno);

         Posix_Free(objName);
         goto exit;
      }

      Posix_Free(objName);
   }

   if (fd == -1) {
      Warning(LGPFX" Failed to create temporary %s: "
              "The name space is full.\n",
              createTempFile ? "file" : "directory");

      errno = EAGAIN;
   }

  exit:

   return fd;
}


/*
 *----------------------------------------------------------------------
 *
 *  File_MakeTempEx2 --
 *
 *      Same as FileMakeTempEx2Int, defaulting 'makeSubdirSafe' to
 *      FALSE.
 *
 * Results:
 *      See FileMakeTempEx2Int.
 *
 * Side effects:
 *      See FileMakeTempEx2Int.
 *
 *----------------------------------------------------------------------
 */

int
File_MakeTempEx2(const char *dir,                              // IN:
                 Bool createTempFile,                          // IN:
                 File_MakeTempCreateNameFunc *createNameFunc,  // IN:
                 void *createNameFuncData,                     // IN:
                 char **presult)                               // OUT:
{
   return FileMakeTempEx2Work(dir, createTempFile, FALSE, createNameFunc,
                              createNameFuncData, presult);
}


/*
 *----------------------------------------------------------------------------
 *
 * FileMakeTempExCreateNameFunc --
 *
 *      This is a helper function designed for File_MakeTempEx function.
 *      Everytime this function is called, this creates a fileName with the
 *      format <num><fileName> and returns back to the caller.
 *
 *      'num' specifies the nth time this function is called.
 *
 *      'data' specifies the payload that is specified when File_MakeTempEx2()
 *      function is called. This points to a UTF8 string.
 *
 * Results:
 *      if successful, a dynamically allocated string with the basename of
 *      the temp file. NULL otherwise.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

static char *
FileMakeTempExCreateNameFunc(uint32 num,  // IN:
                             void *data)  // IN:
{
   if (data == NULL) {
      return NULL;
   }

   return Unicode_Format("%s%u", (char *) data, num);
}


/*
 *----------------------------------------------------------------------
 *
 *  File_MakeTempEx --
 *
 *      Create a temporary file and, if successful, return an open file
 *      descriptor to that file.
 *
 *      'dir' specifies the directory in which to create the file. It
 *      must not end in a slash.
 *
 *      'fileName' specifies the base filename of the created file.
 *
 * Results:
 *      Open file descriptor or -1; if successful then presult points
 *      to a dynamically allocated string with the pathname of the temp
 *      file.
 *
 * Side effects:
 *      Creates a file if successful. Errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
File_MakeTempEx(const char *dir,       // IN:
                const char *fileName,  // IN:
                char **presult)        // OUT:
{
   return File_MakeTempEx2(dir, TRUE, FileMakeTempExCreateNameFunc,
                           (void *) fileName, presult);
}


/*
 *----------------------------------------------------------------------
 *
 *  File_MakeSafeTempDir --
 *
 *      Create a temporary directory in a safe area.
 *
 *      Optional argument 'prefix' specifies the name prefix of the
 *      created directory. When not provided a default will be provided.
 *
 * Results:
 *      NULL  Failure
 *     !NULL  Path name of created directory
 *
 * Side effects:
 *      Creates a directory if successful. Errno is set on error
 *
 *----------------------------------------------------------------------
 */

char *
File_MakeSafeTempDir(const char *prefix)  // IN:
{
   char *result = NULL;
   char *dir = File_GetSafeTmpDir(TRUE);

   if (dir != NULL) {
      const char *effectivePrefix = (prefix == NULL) ? "safeDir" : prefix;

      FileMakeTempEx2Work(dir, FALSE, TRUE, FileMakeTempExCreateNameFunc,
                          (void *) effectivePrefix, &result);

      Posix_Free(dir);
   }

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * File_MakeSafeTemp --
 *
 *      Exactly the same as File_MakeTempEx except uses a safe directory
 *      as the default temporary directory.
 *
 * Results:
 *      Open file descriptor or -1
 *
 * Side effects:
 *      Creates a file if successful.
 *
 *----------------------------------------------------------------------
 */

int
File_MakeSafeTemp(const char *tag,  // IN (OPT):
                  char **presult)   // OUT:
{
   int fd;
   char *dir = NULL;
   char *fileName = NULL;

   ASSERT(presult);

   *presult = NULL;

   if (tag && File_IsFullPath(tag)) {
      File_GetPathName(tag, &dir, &fileName);
   } else {
      dir = File_GetSafeTmpDir(TRUE);
      fileName = Unicode_Duplicate(tag ? tag : "vmware");
   }

   fd = File_MakeTempEx(dir, fileName, presult);

   Posix_Free(dir);
   Posix_Free(fileName);

   return fd;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_DoesVolumeSupportAcls --
 *
 *    Determines if the volume that the pathname resides on supports
 *    ACLs.
 *
 * Results:
 *    TRUE   it does
 *    FALSE  it doesn't
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
File_DoesVolumeSupportAcls(const char *path)  // IN:
{
   Bool succeeded = FALSE;

#if defined(_WIN32)
   Bool res;
   char *vol;
   char *vol2;
   const utf16_t *vol2W;
   DWORD fsFlags;

   ASSERT(path);

   File_SplitName(path, &vol, NULL, NULL);
   vol2 = Unicode_Append(vol, DIRSEPS);

   vol2W = UNICODE_GET_UTF16(vol2);
   res = GetVolumeInformationW(vol2W, NULL, 0, NULL, NULL, &fsFlags, NULL, 0);
   UNICODE_RELEASE_UTF16(vol2W);

   if (res) {
      if ((fsFlags & FS_PERSISTENT_ACLS) == 0) {
         goto exit;
      }
   } else {
      Log(LGPFX" %s: GetVolumeInformation failed: %d\n", __FUNCTION__,
          GetLastError());
      goto exit;
   }

   succeeded = TRUE;

  exit:
   Posix_Free(vol);
   Posix_Free(vol2);
#endif

   return succeeded;
}
   0707010000004F000081A40000000000000000000000016822550500004F99000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/file/fileTempPosix.c   /*********************************************************
 * Copyright (C) 2004-2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include <string.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <ctype.h>

#if !defined(__FreeBSD__) && !defined(sun)
#   include <pwd.h>
#endif

#if defined(__APPLE__) && defined(TARGET_MAC_OS)
#include <libproc.h>
#endif

#include "vmware.h"
#include "file.h"
#include "fileInt.h"
#include "util.h"
#include "su.h"
#include "vm_atomic.h"
#include "str.h"
#include "vm_product.h"
#include "random.h"
#include "userlock.h"
#include "unicodeOperations.h"
#include "err.h"
#include "posix.h"
#include "mutexRankLib.h"
#include "hostType.h"
#include "localconfig.h"

#define LOGLEVEL_MODULE util
#include "loglevel_user.h"


/*
 *----------------------------------------------------------------------
 *
 * FileTryDir --
 *
 *      Check to see if the specified directory is actually a directory
 *      and is writable by us.
 *
 * Results:
 *     !NULL  The expanded directory name (must be freed)
 *      NULL  Failure
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static char *
FileTryDir(const char *dirName)  // IN: Is this a writable directory?
{
   if (dirName != NULL) {
      char *edirName = Util_ExpandString(dirName);

      if ((edirName != NULL) && FileIsWritableDir(edirName)) {
         return edirName;
      }

      Posix_Free(edirName);
   }

   return NULL;
}


#if defined(__APPLE__) && defined(TARGET_MAC_OS)
/*
 *----------------------------------------------------------------------
 *
 * FileExecutablePath --
 *
 *      Return the path of the executable running this code.
 *
 * Results:
 *     !NULL  Success. The executable path. Must be freed by caller.
 *      NULL  Failure
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static char *
FileExecutablePath(void)
{
   char *path = Util_SafeMalloc((size_t) PROC_PIDPATHINFO_MAXSIZE);

   if (proc_pidpath(getpid(), path, PROC_PIDPATHINFO_MAXSIZE) <= 0) {
      Warning("%s: proc_pidpath failure %d\n", __FUNCTION__, errno);

      free(path);
      path = NULL;
   }

   return path;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * FileTmpDirOk --
 *
 *      Is it OK to query the TMPDIR environment variable?
 *
 * Results:
 *     TRUE   Yes
 *     FALSE  No
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static Bool
FileTmpDirOk(void)
{
#if defined(__APPLE__) && defined(TARGET_MAC_OS)
   /* No setuid on IOS, MacOS only */
   int err;
   Bool ok;
   struct stat statBuf;
   char *path = FileExecutablePath();

   if (path == NULL) {
      return FALSE;  // Err on the side of caution
   }

   LOG_ONCE("%s: executable path '%s'\n", __FUNCTION__, path);

   err = Posix_Stat(path, &statBuf);

   free(path);

   if (err == -1) {
      Warning("%s: Stat failed on '%s'\n", __FUNCTION__, path);

      return FALSE;  // Err on the side of caution
   }

   ok = ((statBuf.st_mode & S_ISUID) == 0);  // Only when setuid not present

   LOG_ONCE("%s: TMPDIR ok %d\n", __FUNCTION__, ok);

   return ok;
#else
   return TRUE;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * FileGetTmpDir --
 *
 *      Determine the best temporary directory. Unsafe since the returned
 *      directory is generally going to be 0777, thus all sorts of denial
 *      of service or symlink attacks are possible.
 *
 * Results:
 *     !NULL  The temp directory name (must be freed)
 *      NULL  Failure (reported to the user).
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static char *
FileGetTmpDir(Bool useConf)  // IN: Use the config file?
{
   char *dirName;
   char *edirName;

   /* Make several attempts to find a good temporary directory candidate */

   if (useConf) {
      dirName = (char *) LocalConfig_GetString(NULL, "tmpDirectory");
      edirName = FileTryDir(dirName);
      Posix_Free(dirName);
      if (edirName != NULL) {
         return edirName;
      }
   }

   if (FileTmpDirOk()) {
      /* Posix_Getenv string must _not_ be freed */
      edirName = FileTryDir(Posix_Getenv("TMPDIR"));
      if (edirName != NULL) {
         return edirName;
      }
   }

   /* P_tmpdir is usually defined in <stdio.h> */
   edirName = FileTryDir(P_tmpdir);
   if (edirName != NULL) {
      return edirName;
   }

   edirName = FileTryDir("/tmp");
   if (edirName != NULL) {
      return edirName;
   }

   edirName = FileTryDir("~");
   if (edirName != NULL) {
      return edirName;
   }

   dirName = File_Cwd(NULL);

   if (dirName != NULL) {
      edirName = FileTryDir(dirName);
      Posix_Free(dirName);
      if (edirName != NULL) {
         return edirName;
      }
   }

   edirName = FileTryDir("/");
   if (edirName != NULL) {
      return edirName;
   }

   Warning("%s: Couldn't get a temporary directory\n", __FUNCTION__);
   return NULL;
}

#undef HOSTINFO_TRYDIR


#if !defined(__FreeBSD__) && !defined(sun)
/*
 *-----------------------------------------------------------------------------
 *
 * FileGetUserName --
 *
 *      Retrieve the name associated with the specified UID. Thread-safe
 *      version. --hpreg
 *
 * Results:
 *     !NULL  The user name (must be freed)
 *      NULL  Failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char *
FileGetUserName(uid_t uid)  // IN:
{
   char *memPool;
   long memPoolSize;
   struct passwd pw;
   struct passwd *pw_p;
   char *userName = NULL;

#if defined(__APPLE__)
   memPoolSize = _PASSWORD_LEN;
#else
   errno = 0;
   memPoolSize = sysconf(_SC_GETPW_R_SIZE_MAX);

   if ((errno != 0) || (memPoolSize == 0)) {
      Warning("%s: sysconf(_SC_GETPW_R_SIZE_MAX) failed.\n", __FUNCTION__);

      return NULL;
   }

   if (memPoolSize == -1) {  // Unlimited; pick something reasonable
      memPoolSize = 16 * 1024;
   }
#endif

   memPool = Util_SafeMalloc(memPoolSize);

   if ((Posix_Getpwuid_r(uid, &pw, memPool, memPoolSize, &pw_p) != 0) ||
       pw_p == NULL) {
      Warning("%s: Unable to retrieve the user name associated with UID %u.\n",
             __FUNCTION__, uid);
   } else {
      userName = Util_SafeStrdup(pw_p->pw_name);
   }

   Posix_Free(memPool);

   return userName;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileGetUserIdentifier --
 *
 *      Attempt to obtain an user identification string.
 *
 * Results:
 *     A dynamically allocated string containing the user identifier.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char *
FileGetUserIdentifier(uid_t uid,    // IN:
                      Bool addPid)  // IN:
{
   char *userName = FileGetUserName(uid);

   if (userName == NULL) {
      Warning("%s: Failed to get user name, using UID.\n", __FUNCTION__);

      /* Fallback on just using the uid as the user name. */
      userName = Str_SafeAsprintf(NULL, "uid_%u", uid);
   }

   if (addPid) {
      char *pidToo = Str_SafeAsprintf(NULL, "%s_%u", userName, getpid());

      Posix_Free(userName);
      userName = pidToo;
   }

   return userName;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileAcceptableSafeTmpDir --
 *
 *      Determines if the specified path is acceptable as a safe temp
 *      directory. The directory must either be creatable with the appropriate
 *      permissions and UID or it must already exist with those settings.
 *
 * Results:
 *      TRUE   Path is acceptible
 *      FALSE  Otherwise
 *
 * Side effects:
 *      Directory may be created
 *
 *-----------------------------------------------------------------------------
 */

static Bool
FileAcceptableSafeTmpDir(const char *dirName,  // IN:
                         uid_t uid)            // IN:
{
   Bool acceptable;
   static const mode_t mode = 0700;

   acceptable = (Posix_Mkdir(dirName, mode) == 0);

   if (!acceptable) {
      int error = errno;

      if (EEXIST == error) {
         struct stat st;

         /*
          * The name already exists. Check that it is what we want: a
          * directory owned by the current effective user with permissions
          * 'mode'. It is crucial to use lstat() instead of stat() here,
          * because we do not want the name to be a symlink (created by
          * another user) pointing to a directory owned by the current
          * effective user with permissions 'mode'.
          */

         if (Posix_Lstat(dirName, &st) == 0) {
            /*
             * Our directory inherited S_ISGID if its parent had it. So it is
             * important to ignore that bit, and it is safe to do so because
             * that bit does not affect the owner's permissions.
             */

            if (S_ISDIR(st.st_mode) &&
                (st.st_uid == uid) &&
                ((st.st_mode & 05777) == mode)) {
               acceptable = TRUE;
            }
         }
      }
   }

   return acceptable;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileFindExistingSafeTmpDir --
 *
 *      Searches baseTmpDir to see if any subdirectories are suitable to use
 *      as a safe temp directory. The safe temp directory must have the correct
 *      permissions and UID.
 *
 * Results:
 *     !NULL  Path to discovered safe temp directory (must be freed)
 *      NULL  No suitable directory was found
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char *
FileFindExistingSafeTmpDir(const char *baseTmpDir,  // IN:
                           const char *userName,    // IN:
                           uid_t uid)               // IN:
{
   int i;
   int numFiles;
   char *pattern;
   char *tmpDir = NULL;
   char **fileList = NULL;

   /*
    * We always use the pattern PRODUCT-USER-xxxx when creating alternative
    * safe temp directories, so check for ones with those names and the
    * appropriate permissions.
    */

   pattern = Unicode_Format("%s-%s-", PRODUCT_GENERIC_NAME_LOWER, userName);
   if (pattern == NULL) {
      return NULL;
   }

   numFiles = File_ListDirectory(baseTmpDir, &fileList);

   if (numFiles == -1) {
      Posix_Free(pattern);

      return NULL;
   }

   for (i = 0; i < numFiles; i++) {
       if (Unicode_StartsWith(fileList[i], pattern)) {
          char *path = Unicode_Join(baseTmpDir, DIRSEPS, fileList[i], NULL);

          if (File_IsDirectory(path) &&
              FileAcceptableSafeTmpDir(path, uid)) {
             tmpDir = path;
             break;
          }

          Posix_Free(path);
       }
   }

   Util_FreeStringList(fileList, numFiles);
   Posix_Free(pattern);

   return tmpDir;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FileCreateSafeTmpDir --
 *
 *      Creates a new directory within baseTmpDir with the correct permissions
 *      and UID to ensure it is safe from symlink attacks.
 *
 * Results:
 *     !NULL  Path to created safe temp directory (must be freed)
 *      NULL  No suitable directory was found
 *
 * Side effects:
 *      Directory may be created.
 *
 *-----------------------------------------------------------------------------
 */

static char *
FileCreateSafeTmpDir(const char *baseTmpDir,  // IN:
                     const char *userName,    // IN:
                     uid_t uid)               // IN:
{
   int curDirIter = 0;
   char *tmpDir = NULL;
   static const int MAX_DIR_ITERS = 250;

   while (TRUE) {
      /*
       * We use a random number that makes it more likely that we will create
       * an unused name than if we had simply tried suffixes in numeric order.
       */

      tmpDir = Str_SafeAsprintf(NULL, "%s%s%s-%s-%u", baseTmpDir, DIRSEPS,
                                PRODUCT_GENERIC_NAME_LOWER, userName,
                                FileSimpleRandom());

      if (FileAcceptableSafeTmpDir(tmpDir, uid)) {
         break;
      }

      if (++curDirIter > MAX_DIR_ITERS) {
         Warning("%s: Failed to create a safe temporary directory, path "
                 "\"%s\". The maximum number of attempts was exceeded.\n",
                 __FUNCTION__, tmpDir);
         Posix_Free(tmpDir);
         tmpDir = NULL;
         break;
      }

      Posix_Free(tmpDir);
   }

   return tmpDir;
}
#endif // __linux__


/*
 *-----------------------------------------------------------------------------
 *
 * FileGetSafeTmpDir --
 *
 *      Return a safe temporary directory (i.e. a temporary directory which
 *      is not prone to symlink attacks, because it is only writable with the
 *      current set of credentials (EUID).
 *
 *      Guaranteed to return the same directory for any EUID every time it is
 *      called during the lifetime of the current process. (Barring the user
 *      manually deleting or renaming the directory.)
 *
 *      Optionally, add the PID to the user identifier for the cases where
 *      the EUID may change during the lifetime of the calling process.
 *
 * Results:
 *     !NULL  Path to safe temp directory (must be freed)
 *      NULL  No suitable directory was found
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static char *
FileGetSafeTmpDir(Bool useConf,  // IN: Use configuration variables?
                  Bool addPid)   // IN: Add PID to userName?
{
   char *tmpDir = NULL;

#if defined(__FreeBSD__) || defined(sun)
   tmpDir = FileGetTmpDir(useConf);
#else
   static Atomic_Ptr lckStorage;
   static char *cachedDir;
   static uid_t cachedEuid;
   static char *cachedPidDir;
   uid_t euid;
   char *testSafeDir;
   MXUserExclLock *lck;
   char *userName = NULL;
   char *baseTmpDir = NULL;

   /* Get and take lock for our safe dir. */
   lck = MXUser_CreateSingletonExclLock(&lckStorage, "getSafeTmpDirLock",
                                        RANK_getSafeTmpDirLock);

   MXUser_AcquireExclLock(lck);

   /*
    * If a suitable temporary directory was cached for this EUID, use it...
    * as long as it is still acceptable.
    */

   euid = geteuid();

   testSafeDir = addPid ? cachedPidDir : cachedDir;

   /*
    * Detecting an EUID change without resorting to I/Os is an nice performance
    * improvement... particularly on ESXi where the operations are expensive.
    */

   if ((euid == cachedEuid) &&
       (testSafeDir != NULL) &&
       FileAcceptableSafeTmpDir(testSafeDir, euid)) {
      tmpDir = Util_SafeStrdup(testSafeDir);
      goto exit;
   }

   /* We don't have a useable temporary dir, create one. */
   baseTmpDir = FileGetTmpDir(useConf);

   if (baseTmpDir == NULL) {
      goto exit;
   }

   userName = FileGetUserIdentifier(euid, addPid);

   tmpDir = Str_SafeAsprintf(NULL, "%s%s%s-%s", baseTmpDir, DIRSEPS,
                             PRODUCT_GENERIC_NAME_LOWER, userName);

   if (addPid || !FileAcceptableSafeTmpDir(tmpDir, euid)) {
      /*
       * Either we want a truely random temp directory or we didn't get our
       * first choice for the safe temp directory. Search through the unsafe
       * tmp directory to see if there is an acceptable one to use.
       */

      Posix_Free(tmpDir);

      tmpDir = FileFindExistingSafeTmpDir(baseTmpDir, userName, euid);

      if (tmpDir == NULL) {
         /*
          * We didn't find any usable directories, so try to create one now.
          */

         tmpDir = FileCreateSafeTmpDir(baseTmpDir, userName, euid);
      }
   }

   if (tmpDir != NULL) {
      char *newDir = Util_SafeStrdup(tmpDir);

      if (euid == cachedEuid) {
         if (addPid) {
            Posix_Free(cachedPidDir);
            cachedPidDir = newDir;
         } else {
            Posix_Free(cachedDir);
            cachedDir = newDir;
         }
      } else {
         Posix_Free(cachedPidDir);
         Posix_Free(cachedDir);

         if (addPid) {
            cachedPidDir = newDir;
            cachedDir = NULL;
         } else {
            cachedDir = newDir;
            cachedPidDir = NULL;
         }

         cachedEuid = euid;
      }
   }

exit:

   MXUser_ReleaseExclLock(lck);
   Posix_Free(baseTmpDir);
   Posix_Free(userName);
#endif

   return tmpDir;
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_GetSafeTmpDir --
 *
 *      Return a safe temporary directory (i.e. a temporary directory which
 *      is not prone to symlink attacks, because it is only writable with the
 *      current set of credentials (EUID).
 *
 *      Guaranteed to return the same directory for any EUID every time it is
 *      called during the lifetime of the current process. (Barring the user
 *      manually deleting or renaming the directory.)
 *
 * Results:
 *     !NULL  Path to safe temp directory (must be freed)
 *      NULL  No suitable directory was found
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
File_GetSafeTmpDir(Bool useConf)  // IN:
{
   return FileGetSafeTmpDir(useConf, FALSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_GetSafeRandomTmpDir --
 *
 *      Return a safe, random temporary directory (i.e. a temporary directory
 *      is not prone to symlink attacks, because it is only writable with the
 *      current set of credentials (EUID).
 *
 *      Guaranteed to return the same directory for any EUID every time it is
 *      called during the lifetime of the current process. (Barring the user
 *      manually deleting or renaming the directory.)
 *
 * Results:
 *     !NULL  Path to safe temp directory (must be freed).
 *      NULL  No suitable directory was found.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
File_GetSafeRandomTmpDir(Bool useConf)  // IN:
{
   return FileGetSafeTmpDir(useConf, TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * File_MakeSafeTempSubdir --
 *
 *      Given an existing safe directory, create a safe subdir of
 *      the specified name in that directory.
 *
 * Results:
 *      The allocated subdir path on success.
 *      NULL on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
File_MakeSafeTempSubdir(const char *safeDir,     // IN
                        const char *subdirName)  // IN
{
#if defined(__FreeBSD__) || defined(sun)
   if (!File_Exists(safeDir)) {
      return NULL;
   }

   return File_PathJoin(safeDir, subdirName);
#else
   uid_t userId = geteuid();
   char *fullSafeSubdir;

   if (!File_Exists(safeDir) ||
       !FileAcceptableSafeTmpDir(safeDir, userId)) {
      return NULL;
   }

   fullSafeSubdir = File_PathJoin(safeDir, subdirName);
   if (!FileAcceptableSafeTmpDir(fullSafeSubdir, userId)) {
      free(fullSafeSubdir);
      return NULL;
   }

   return fullSafeSubdir;
#endif
}
   07070100000050000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/lib/foundryMsg 07070100000051000081A40000000000000000000000016822550500000459000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/foundryMsg/Makefile.am ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libFoundryMsg.la

libFoundryMsg_la_SOURCES =
libFoundryMsg_la_SOURCES += foundryMsg.c
libFoundryMsg_la_SOURCES += foundryPropertyListCommon.c
libFoundryMsg_la_SOURCES += vixTranslateErrOpenSource.c
   07070100000052000081A40000000000000000000000016822550500013A8E000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/foundryMsg/foundryMsg.c    /*********************************************************
 * Copyright (c) 2004-2016, 2019, 2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * foundryMsg.c --
 *
 * This is a library for formatting and parsing the messages sent
 * between a foundry client and the VMX. It is a stand-alone library
 * so it can be used by the VMX tree without also linking in the
 * entire foundry client-side library.
 */

#include "vmware.h"
#include "util.h"
#include "str.h"
#include "base64.h"

#include "vixOpenSource.h"
#include "vixCommands.h"
#include "unicodeBase.h"

static char PlainToObfuscatedCharMap[256];
static char ObfuscatedToPlainCharMap[256];


/*
 * An entry in the command info table. There is one VixCommandInfo per op
 * code, and each entry contains a description of the op code plus security-
 * related metadata.
 */
typedef struct VixCommandInfo {
   int                         opCode;
   const char                  *commandName;
   VixCommandSecurityCategory  category;
   Bool                        used;     // Is there an opcode for this entry?
} VixCommandInfo;

#define VIX_DEFINE_COMMAND_INFO(x, category) { x, #x, category, TRUE }
#define VIX_DEFINE_UNUSED_COMMAND  { 0, NULL, VIX_COMMAND_CATEGORY_UNKNOWN, FALSE }

/*
 * Contains the information for every VIX command op code. This table is
 * organized to allow for direct look up, so it must be complete. Any index
 * that does not correspond to a valid VIX op code must be marked with
 * VIX_DEFINE_UNUSED_COMMAND.
 *
 * When you add or remove a command to vixCommands.h, this table needs to
 * be updated as well. When adding a new command, you need to give it a
 * security category. There are descriptions of the categories in vixCommands.h
 * where they are defined, but in general, if the command affects the host or
 * a VM (but not the guest), then the command should be CATEGORY_PRIVILEGED.
 * If the command is a guest command (a command the runs inside the guest
 * OS) than it should be CATEGORY_ALWAYS_ALLOWED. Also, if a command is
 * required to establish a connection with the VMX, it needs to be
 * CATEGORY_ALWAYS_ALLOWED.
 */

static const VixCommandInfo vixCommandInfoTable[] = {
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_UNKNOWN,
                           VIX_COMMAND_CATEGORY_UNKNOWN),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VM_POWERON,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VM_POWEROFF,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VM_RESET,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VM_SUSPEND,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_RUN_PROGRAM,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_KEYSTROKES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_READ_REGISTRY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_WRITE_REGISTRY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_COPY_FILE_FROM_GUEST_TO_HOST,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_COPY_FILE_FROM_HOST_TO_GUEST,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_SNAPSHOT,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_REMOVE_SNAPSHOT,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_REVERT_TO_SNAPSHOT,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VM_CLONE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_GUEST_FILE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GUEST_FILE_EXISTS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_FIND_VM,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CALL_PROCEDURE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_REGISTRY_KEY_EXISTS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_WIN32_WINDOW_MESSAGE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CONSOLIDATE_SNAPSHOTS,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_INSTALL_TOOLS,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CANCEL_INSTALL_TOOLS,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_UPGRADE_VIRTUAL_HARDWARE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_RELOAD_VM,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_VM,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_WAIT_FOR_TOOLS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_RUNNING_VM_SNAPSHOT,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CONSOLIDATE_RUNNING_VM_SNAPSHOT,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_NUM_SHARED_FOLDERS,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_SHARED_FOLDER_STATE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_EDIT_SHARED_FOLDER_STATE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_REMOVE_SHARED_FOLDER,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_ADD_SHARED_FOLDER,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_RUN_SCRIPT_IN_GUEST,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_OPEN_VM,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   /* GET_HANDLE_STATE is needed for the initial handshake */
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_HANDLE_STATE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_WORKING_COPY,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DISCARD_WORKING_COPY,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SAVE_WORKING_COPY,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CAPTURE_SCREEN,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_TOOLS_STATE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CHANGE_SCREEN_RESOLUTION,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DIRECTORY_EXISTS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_GUEST_REGISTRY_KEY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_GUEST_DIRECTORY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_GUEST_EMPTY_DIRECTORY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_TEMPORARY_FILE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_PROCESSES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_MOVE_GUEST_FILE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_DIRECTORY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CHECK_USER_ACCOUNT,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_DIRECTORY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_REGISTER_VM,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_UNREGISTER_VM,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   /* CREATE_SESSION_KEY is needed for the initial handshake */
   VIX_DEFINE_COMMAND_INFO(VIX_CREATE_SESSION_KEY_COMMAND,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VMXI_HGFS_SEND_PACKET_COMMAND,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_KILL_PROCESS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LOGOUT_IN_GUEST,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_READ_VARIABLE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_WRITE_VARIABLE,
                           VIX_COMMAND_CATEGORY_MIXED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CONNECT_DEVICE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_IS_DEVICE_CONNECTED,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_FILE_INFO,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SET_FILE_INFO,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_MOUSE_EVENTS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_OPEN_TEAM,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_ANSWER_MESSAGE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_ENABLE_SHARED_FOLDERS,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_MOUNT_HGFS_FOLDERS,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_HOT_EXTEND_DISK,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,

   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CONNECT_HOST,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_LINKED_CLONE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   /*
    * HOWTO: Adding a new Vix Command. Step 2b.
    * Take the command you added to vixCommands.h, and add it to this
    * table. The command needs to go in the index that matches the command
    * ID as specified in the enum in vixCommands.h.
    */
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SAMPLE_COMMAND,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_GUEST_NETWORKING_CONFIG,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SET_GUEST_NETWORKING_CONFIG,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VM_PAUSE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VM_UNPAUSE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_PERFORMANCE_DATA,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_SNAPSHOT_SCREENSHOT,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_WAIT_FOR_USER_ACTION_IN_GUEST,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CHANGE_VIRTUAL_HARDWARE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_HOT_PLUG_CPU,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_HOT_PLUG_MEMORY,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_HOT_ADD_DEVICE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_HOT_REMOVE_DEVICE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   /* GET_VMX_DEVICE_STATE is needed for the initial handshake. */
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GET_VMX_DEVICE_STATE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SET_SNAPSHOT_INFO,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SNAPSHOT_SET_MRU,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LOGOUT_HOST,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_HOT_PLUG_BEGIN_BATCH,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_HOT_PLUG_COMMIT_BATCH,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_TRANSFER_CONNECTION,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_TRANSFER_REQUEST,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_TRANSFER_FINAL_DATA,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),

   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,

   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,
   VIX_DEFINE_UNUSED_COMMAND,

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_FILESYSTEMS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CHANGE_DISPLAY_TOPOLOGY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SUSPEND_AND_RESUME,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_REMOVE_BULK_SNAPSHOT,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_COPY_FILE_FROM_READER_TO_GUEST,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_GENERATE_NONCE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CHANGE_DISPLAY_TOPOLOGY_MODES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_QUERY_CHILDREN,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_FILES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_DIRECTORY_EX,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_MOVE_GUEST_FILE_EX,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_MOVE_GUEST_DIRECTORY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_TEMPORARY_FILE_EX,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_TEMPORARY_DIRECTORY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SET_GUEST_FILE_ATTRIBUTES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_COPY_FILE_FROM_GUEST_TO_READER,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_START_PROGRAM,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_PROCESSES_EX,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_READ_ENV_VARIABLES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_INITIATE_FILE_TRANSFER_TO_GUEST,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_ACQUIRE_CREDENTIALS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_RELEASE_CREDENTIALS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_VALIDATE_CREDENTIALS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_TERMINATE_PROCESS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_GUEST_FILE_EX,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_GUEST_DIRECTORY_EX,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_HOT_CHANGE_MONITOR_TYPE,
                           VIX_COMMAND_CATEGORY_PRIVILEGED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_ADD_AUTH_ALIAS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_REMOVE_AUTH_ALIAS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_AUTH_PROVIDER_ALIASES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_AUTH_MAPPED_ALIASES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_CREATE_REGISTRY_KEY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_REGISTRY_KEYS,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_REGISTRY_KEY,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_SET_REGISTRY_VALUE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_LIST_REGISTRY_VALUES,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_DELETE_REGISTRY_VALUE,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),

   VIX_DEFINE_COMMAND_INFO(VIX_COMMAND_REMOVE_AUTH_ALIAS_BY_CERT,
                           VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED),
};


static const VixCommandInfo *VixGetCommandInfoForOpCode(int opCode);

static void VixMsgInitializeObfuscationMapping(void);

static VixError VixMsgEncodeBuffer(const uint8 *buffer,
                                   size_t bufferLength,
                                   Bool includeEncodingId,
                                   char **result);

static VixError VixMsgDecodeBuffer(const char *str,
                                   Bool nullTerminateResult,
                                   char **result,
                                   size_t *bufferLength);

static VixError VMAutomationMsgParserInit(const char *caller,
                                          unsigned int line,
                                          VMAutomationMsgParser *state,
                                          const VixMsgHeader *msg,
                                          size_t headerLength,
                                          size_t fixedLength,
                                          size_t miscDataLength,
                                          const char *packetType);


/*
 *----------------------------------------------------------------------------
 *
 * VixMsg_AllocResponseMsg --
 *
 *      Allocate and initialize a response message.
 *
 * Results:
 *      The message, with the headers properly initialized.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

VixCommandResponseHeader *
VixMsg_AllocResponseMsg(const VixCommandRequestHeader *requestHeader, // IN
                        VixError error,                               // IN
                        uint32 additionalError,                       // IN
                        size_t responseBodyLength,                    // IN
                        const void *responseBody,                     // IN
                        size_t *responseMsgLength)                    // OUT
{
   char *responseBuffer = NULL;
   VixCommandResponseHeader *responseHeader;
   size_t totalMessageSize;

   ASSERT((NULL != responseBody) || (0 == responseBodyLength));

   /*
    * We don't have scatter/gather, so copy everything into one buffer.
    */
   totalMessageSize = sizeof(VixCommandResponseHeader) + responseBodyLength;
   if (totalMessageSize > VIX_COMMAND_MAX_SIZE) {
      /*
       * We don't want to allocate any responses larger than
       * VIX_COMMAND_MAX_SIZE, since the VMX will ignore them.
       * If we hit this ASSERT, we will need to either revise this
       * value, or start packetizing certain commands.
       */
      ASSERT(0);
      return NULL;
   }

   responseBuffer = Util_SafeMalloc(totalMessageSize);
   responseHeader = (VixCommandResponseHeader *) responseBuffer;

   VixMsg_InitResponseMsg(responseHeader,
                          requestHeader,
                          error,
                          additionalError,
                          totalMessageSize);

   if ((responseBodyLength > 0) && (responseBody)) {
      memcpy(responseBuffer + sizeof(VixCommandResponseHeader),
             responseBody,
             responseBodyLength);
   }

   if (NULL != responseMsgLength) {
      *responseMsgLength = totalMessageSize;
   }

   return responseHeader;
} // VixMsg_AllocResponseMsg


/*
 *----------------------------------------------------------------------------
 *
 * VixMsg_InitResponseMsg --
 *
 *      Initialize a response message.
 *
 * Results:
 *      The message, with the headers properly initialized.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
VixMsg_InitResponseMsg(VixCommandResponseHeader *responseHeader,     // IN
                       const VixCommandRequestHeader *requestHeader, // IN
                       VixError error,                               // IN
                       uint32 additionalError,                       // IN
                       size_t totalMessageSize)                      // IN
{
   size_t responseBodyLength;

   ASSERT(NULL != responseHeader);
   ASSERT(totalMessageSize >= sizeof(VixCommandResponseHeader));

   responseBodyLength = totalMessageSize - sizeof(VixCommandResponseHeader);

   /*
    * Fill in the response header.
    */
   responseHeader->commonHeader.magic = VIX_COMMAND_MAGIC_WORD;
   responseHeader->commonHeader.messageVersion = VIX_COMMAND_MESSAGE_VERSION;
   responseHeader->commonHeader.totalMessageLength = totalMessageSize;
   responseHeader->commonHeader.headerLength = sizeof(VixCommandResponseHeader);
   responseHeader->commonHeader.bodyLength = responseBodyLength;
   responseHeader->commonHeader.credentialLength = 0;
   responseHeader->commonHeader.commonFlags = 0;
   if (NULL != requestHeader) {
      responseHeader->requestCookie = requestHeader->cookie;
   } else {
      responseHeader->requestCookie = 0;
   }
   responseHeader->responseFlags = 0;
   responseHeader->duration = 0xFFFFFFFF;
   responseHeader->error = error;
   responseHeader->additionalError = additionalError;
   responseHeader->errorDataLength = 0;
} // VixMsg_InitResponseMsg


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_AllocRequestMsg --
 *
 *      Allocate and initialize a request message.
 *
 * Results:
 *      The message, with the headers properly initialized.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixCommandRequestHeader *
VixMsg_AllocRequestMsg(size_t msgHeaderAndBodyLength,    // IN
                       int opCode,                       // IN
                       uint64 cookie,                    // IN
                       int credentialType,               // IN
                       const char *credential)           // IN
{
   size_t totalMessageSize;
   VixCommandRequestHeader *commandRequest = NULL;
   size_t providedCredentialLength = 0;
   size_t totalCredentialLength = 0;

   if ((VIX_USER_CREDENTIAL_NAME_PASSWORD == credentialType)
      || (VIX_USER_CREDENTIAL_HOST_CONFIG_SECRET == credentialType)
      || (VIX_USER_CREDENTIAL_HOST_CONFIG_HASHED_SECRET == credentialType)
      || (VIX_USER_CREDENTIAL_TICKETED_SESSION == credentialType)
      || (VIX_USER_CREDENTIAL_SSPI == credentialType)
      || (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN == credentialType)
      || (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN_HOST_VERIFIED == credentialType)) {
      /*
       * All of these are optional.
       */
      if (NULL != credential) {
         providedCredentialLength = strlen(credential);
         totalCredentialLength += providedCredentialLength;
      }
      /*
       * Add 1 to each string to include '\0' for the end of the string.
       */
      totalCredentialLength += 1;
   } else {
      totalCredentialLength = 0;
   }

   totalMessageSize = msgHeaderAndBodyLength + totalCredentialLength;
   if (totalMessageSize > VIX_COMMAND_MAX_REQUEST_SIZE) {
      /*
       * We don't want to allocate any requests larger than
       * VIX_COMMAND_MAX_REQUEST_SIZE, since the VMX will ignore them.
       * If we hit this ASSERT, we will need to either revise this
       * value, or start packetizing certain commands.
       */
      ASSERT(0);
      return NULL;
   }

   commandRequest = (VixCommandRequestHeader *)
                        Util_SafeCalloc(1, totalMessageSize);

   commandRequest->commonHeader.magic = VIX_COMMAND_MAGIC_WORD;
   commandRequest->commonHeader.messageVersion = VIX_COMMAND_MESSAGE_VERSION;
   commandRequest->commonHeader.totalMessageLength =
      msgHeaderAndBodyLength + totalCredentialLength;
   commandRequest->commonHeader.headerLength = sizeof(VixCommandRequestHeader);
   commandRequest->commonHeader.bodyLength = msgHeaderAndBodyLength -
      sizeof(VixCommandRequestHeader);
   commandRequest->commonHeader.credentialLength = totalCredentialLength;
   commandRequest->commonHeader.commonFlags = VIX_COMMAND_REQUEST;

   commandRequest->opCode = opCode;
   commandRequest->cookie = cookie;
   commandRequest->timeOut = 0xFFFFFFFF;
   commandRequest->requestFlags = 0;

   commandRequest->userCredentialType = credentialType;

   if ((VIX_USER_CREDENTIAL_NAME_PASSWORD == credentialType)
         || (VIX_USER_CREDENTIAL_HOST_CONFIG_SECRET == credentialType)
         || (VIX_USER_CREDENTIAL_HOST_CONFIG_HASHED_SECRET == credentialType)
         || (VIX_USER_CREDENTIAL_TICKETED_SESSION == credentialType)
         || (VIX_USER_CREDENTIAL_SSPI == credentialType)
         || (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN == credentialType)
         || (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN_HOST_VERIFIED == credentialType)) {
      char *destPtr = (char *) commandRequest;

      destPtr += commandRequest->commonHeader.headerLength;
      destPtr += commandRequest->commonHeader.bodyLength;
      if (NULL != credential) {
         Str_Strcpy(destPtr, credential, providedCredentialLength + 1);
         destPtr += providedCredentialLength;
      }
      *(destPtr++) = 0;
   }

   return commandRequest;
} // VixMsg_AllocRequestMsg


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_ValidateMessage --
 *
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_ValidateMessage(const void *vMsg, // IN
                       size_t msgLength) // IN
{
   const VixMsgHeader *message;

   if ((NULL == vMsg) || (msgLength < sizeof *message)) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   /*
    * Confidence check the header.
    * Some basic rules: All the length values in the VixMsgHeader
    * struct are uint32. The headerLength must be large enough to
    * accomodate the base header: VixMsgHeader. The bodyLength and
    * the credentialLength can be 0.
    *
    * We cannot compare message->totalMessageLength and msgLength.
    * When we first read just the header, message->totalMessageLength
    * is > msgLength. When we have read the whole message, then
    * message->totalMessageLength <= msgLength. So, it depends on
    * when we call this function. Instead, we just make sure the message
    * is internally consistent, and then rely on the higher level code to
    * decide how much to read and when it has read the whole message.
    */
   message = vMsg;
   if ((VIX_COMMAND_MAGIC_WORD != message->magic)
         || (message->headerLength < sizeof(VixMsgHeader))
         || (message->totalMessageLength
               < ((uint64)message->headerLength + message->bodyLength + message->credentialLength))
         || (message->totalMessageLength > VIX_COMMAND_MAX_SIZE)
         || (VIX_COMMAND_MESSAGE_VERSION != message->messageVersion)) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   return VIX_OK;
} // VixMsg_ValidateMessage


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_ValidateRequestMsg --
 *
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_ValidateRequestMsg(const void *vMsg, // IN
                          size_t msgLength) // IN
{
   VixError err;
   const VixCommandRequestHeader *message;

   err = VixMsg_ValidateMessage(vMsg, msgLength);
   if (VIX_OK != err) {
      return(err);
   }

   /*
    * Confidence check the parts of the header that are specific to requests.
    */
   message = vMsg;
   if (message->commonHeader.headerLength < sizeof(VixCommandRequestHeader)) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   if (message->commonHeader.totalMessageLength > VIX_COMMAND_MAX_REQUEST_SIZE) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   if (!(VIX_COMMAND_REQUEST & message->commonHeader.commonFlags)) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   if ((VIX_REQUESTMSG_INCLUDES_AUTH_DATA_V1 & message->requestFlags) &&
       (message->commonHeader.totalMessageLength <
          (uint64)message->commonHeader.headerLength +
          message->commonHeader.bodyLength +
          message->commonHeader.credentialLength +
          sizeof (VixMsgAuthDataV1))) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   return VIX_OK;
} // VixMsg_ValidateRequestMsg


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_ValidateResponseMsg --
 *
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_ValidateResponseMsg(const void *vMsg, // IN
                           size_t msgLength) // IN
{
   VixError err;
   const VixCommandResponseHeader *message;

   if ((NULL == vMsg) || (msgLength < sizeof *message)) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   err = VixMsg_ValidateMessage(vMsg, msgLength);
   if (VIX_OK != err) {
      return(err);
   }

   /*
    * Confidence check the parts of the header that are specific to responses.
    */
   message = vMsg;
   if (message->commonHeader.headerLength < sizeof(VixCommandResponseHeader)) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   if (VIX_COMMAND_REQUEST & message->commonHeader.commonFlags) {
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   return VIX_OK;
} // VixMsg_ValidateResponseMsg


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_ParseWriteVariableRequest --
 *
 *      Extract the value's name and the value itself from the request
 *      message, while validating message.
 *
 *      The strings returned from this function just point to memory in
 *      the message itself, so they must not be free()'d.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_ParseWriteVariableRequest(VixMsgWriteVariableRequest *msg,   // IN
                                 char **valueName,                  // OUT
                                 char **value)                      // OUT
{
   VixError err;
   char *valueNameLocal = NULL;
   char *valueLocal = NULL;
   uint64 headerAndBodyLength;

   if ((NULL == msg) || (NULL == valueName) || (NULL == value)) {
      ASSERT(0);
      err = VIX_E_FAIL;
      goto quit;
   }

   *valueName = NULL;
   *value = NULL;

   /*
    * In most cases we will have already called VixMsg_ValidateResponseMsg()
    * on this request before, but call it here so that this function will
    * always be sufficient to validate the request.
    */
   err = VixMsg_ValidateRequestMsg(msg,
                                   msg->header.commonHeader.totalMessageLength);
   if (VIX_OK != err) {
      goto quit;
   }

   if (msg->header.commonHeader.totalMessageLength < sizeof *msg) {
      err = VIX_E_INVALID_MESSAGE_BODY;
      goto quit;
   }

   headerAndBodyLength = (uint64) msg->header.commonHeader.headerLength
                            + msg->header.commonHeader.bodyLength;

   if (headerAndBodyLength < ((uint64) sizeof *msg
                                 + msg->nameLength + 1
                                 + msg->valueLength + 1)) {
      err = VIX_E_INVALID_MESSAGE_BODY;
      goto quit;
   }

   valueNameLocal = ((char *) msg) + sizeof(*msg);
   if ('\0' != valueNameLocal[msg->nameLength]) {
      err = VIX_E_INVALID_MESSAGE_BODY;
      goto quit;
   }

   valueLocal = valueNameLocal + msg->nameLength + 1;
   if ('\0' != valueLocal[msg->valueLength]) {
      err = VIX_E_INVALID_MESSAGE_BODY;
      goto quit;
   }

   *valueName = valueNameLocal;
   *value = valueLocal;
   err = VIX_OK;

quit:

   return err;
} // VixMsg_ParseWriteVariableRequest


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsgInitializeObfuscationMapping --
 *
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
VixMsgInitializeObfuscationMapping(void)
{
   size_t charIndex;
   static Bool initializedTable = FALSE;

   if (initializedTable) {
      return;
   }

   for (charIndex = 0; charIndex < sizeof(PlainToObfuscatedCharMap); charIndex++) {
      PlainToObfuscatedCharMap[charIndex] = 0;
      ObfuscatedToPlainCharMap[charIndex] = 0;
   }

   PlainToObfuscatedCharMap['\\'] = '1';
   PlainToObfuscatedCharMap['\''] = '2';
   PlainToObfuscatedCharMap['\"'] = '3';
   PlainToObfuscatedCharMap[' '] = '4';
   PlainToObfuscatedCharMap['\r'] = '5';
   PlainToObfuscatedCharMap['\n'] = '6';
   PlainToObfuscatedCharMap['\t'] = '7';

   ObfuscatedToPlainCharMap['1'] = '\\';
   ObfuscatedToPlainCharMap['2'] = '\'';
   ObfuscatedToPlainCharMap['3'] = '\"';
   ObfuscatedToPlainCharMap['4'] = ' ';
   ObfuscatedToPlainCharMap['5'] = '\r';
   ObfuscatedToPlainCharMap['6'] = '\n';
   ObfuscatedToPlainCharMap['7'] = '\t';

   initializedTable = TRUE;
} // VixMsgInitializeObfuscationMapping


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_ObfuscateNamePassword --
 *
 *       This is NOT ENCRYPTION.
 *
 *       This function does 2 things:
 *          * It removes spaces, quotes and other characters that may make
 *             parsing params in a string difficult. The name and password is
 *             passed from the VMX to the tools through the backdoor as a
 *             string containing quoted parameters.
 *
 *          * It means that somebody doing a trivial string search on
 *             host memory won't see a name/password.
 *
 *          This is used ONLY between the VMX and guest through the backdoor.
 *          This is NOT secure.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_ObfuscateNamePassword(const char *userName,      // IN
                             const char *password,      // IN
                             char **result)             // OUT
{
   VixError err = VIX_OK;
   char *packedBuffer = NULL;
   char *resultString = NULL;
   char *destPtr;
   size_t packedBufferLength = 0;
   size_t nameLength = 0;
   size_t passwordLength = 0;

   if (NULL != userName) {
      nameLength = strlen(userName);
   }
   if (NULL != password) {
      passwordLength = strlen(password);
   }
   /*
    * Leave space for null terminating characters.
    */
   packedBufferLength = nameLength + 1 + passwordLength + 1;
   packedBuffer = VixMsg_MallocClientData(packedBufferLength);
   if (packedBuffer == NULL) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   destPtr = packedBuffer;
   if (NULL != userName) {
      Str_Strcpy(destPtr, userName, nameLength + 1);
      destPtr += nameLength;
   }
   *(destPtr++) = 0;
   if (NULL != password) {
      Str_Strcpy(destPtr, password, passwordLength + 1);
      destPtr += passwordLength;
   }
   *(destPtr++) = 0;

   err = VixMsgEncodeBuffer(packedBuffer, packedBufferLength, FALSE,
                            &resultString);
   if (err != VIX_OK) {
      goto quit;
   }

quit:
   Util_ZeroFree(packedBuffer, packedBufferLength);

   if (err == VIX_OK) {
      *result = resultString;
   }

   return err;
} // VixMsg_ObfuscateNamePassword


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_DeObfuscateNamePassword --
 *
 *      This reverses VixMsg_ObfuscateNamePassword.
 *      See the notes for that procedure.
 *
 * Results:
 *      VixError. VIX_OK if successful.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_DeObfuscateNamePassword(const char *packagedName,   // IN
                               char **userNameResult,      // OUT
                               char **passwordResult)      // OUT
{
   VixError err;
   char *packedString = NULL;
   char *srcPtr;
   size_t packedStringLength;
   char *userName = NULL;
   char *passwd = NULL;

   err = VixMsgDecodeBuffer(packagedName, FALSE,
                            &packedString, &packedStringLength);
   if (err != VIX_OK) {
      goto quit;
   }

   srcPtr = packedString;
   if (NULL != userNameResult) {
      Bool allocateFailed;
      userName = VixMsg_StrdupClientData(srcPtr, &allocateFailed);
      if (allocateFailed) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
   }
   srcPtr = srcPtr + strlen(srcPtr);
   srcPtr++;
   if (NULL != passwordResult) {
      Bool allocateFailed;
      passwd = VixMsg_StrdupClientData(srcPtr, &allocateFailed);
      if (allocateFailed) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
   }

   if (NULL != userNameResult) {
      *userNameResult = userName;
      userName = NULL;
   }
   if (NULL != passwordResult) {
      *passwordResult = passwd;
      passwd = NULL;
   }

quit:
   Util_ZeroFree(packedString, packedStringLength);
   Util_ZeroFreeString(userName);
   Util_ZeroFreeString(passwd);

   return err;
} // VixMsg_DeObfuscateNamePassword


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_EncodeString --
 *
 *       This makes a string safe to pass over a backdoor Tclo command as a
 *       string. It base64 encodes a string, which removes quote, space,
 *       backslash, and other characters. This will also allow us to pass
 *       UTF-8 strings.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_EncodeString(const char *str,  // IN
                    char **result)    // OUT
{
   if (NULL == str) {
      str = "";
   }

   /*
    * Coverity flags this as a buffer overrun in the case where str is
    * assigned the empty string above, claiming that the underlying
    * Base64_Encode function directly indexes the array str at index 2;
    * however, that indexing is only done if the string length is greater
    * than 2, and clearly strlen("") is 0.
    */
   /* coverity[overrun-buffer-val] */
   return VixMsgEncodeBuffer(str, strlen(str), TRUE, result);
} // VixMsg_EncodeString


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsgEncodeBuffer --
 *
 *       This makes a string safe to pass over a backdoor Tclo command as a
 *       string. It base64 encodes a string, which removes quote, space,
 *       backslash, and other characters. This will also allow us to pass
 *       UTF-8 strings.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsgEncodeBuffer(const uint8 *buffer,     // IN
                   size_t bufferLength,     // IN
                   Bool includeEncodingId,  // IN: Add 'a' (ASCII) at start of output
                   char ** result)          // OUT
{
   VixError err = VIX_OK;
   char *base64String = NULL;
   char *resultString = NULL;
   size_t resultBufferLength = 0;
   char *srcPtr;
   char *endSrcPtr;
   char *destPtr;
   size_t base64Length;

   base64Length = Base64_EncodedLength((uint8 const *) buffer,
                                       bufferLength);
   base64String = VixMsg_MallocClientData(base64Length);
   if (base64String == NULL) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   if (!(Base64_Encode((uint8 const *) buffer,
                       bufferLength,
                       base64String,
                       base64Length,
                       &base64Length))) {
      err = VIX_E_FAIL;
      goto quit;
   }

   VixMsgInitializeObfuscationMapping();

   /*
    * Expand it to make space for escaping some characters.
    */
   resultBufferLength = base64Length * 2;
   if (includeEncodingId) {
      resultBufferLength++;
   }

   resultString = VixMsg_MallocClientData(resultBufferLength + 1);
   if (resultString == NULL) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   destPtr = resultString;
   srcPtr = base64String;
   endSrcPtr = base64String + base64Length;

   if (includeEncodingId) {
      /*
       * Start with the character-set type.
       *   'a' means ASCII.
       */
      *(destPtr++) = 'a';
   }

   /*
    * Now, escape problematic characters.
    */
   while (srcPtr < endSrcPtr)
   {
      if (PlainToObfuscatedCharMap[(unsigned int) (*srcPtr)]) {
         *(destPtr++) = '\\';
         *(destPtr++) = PlainToObfuscatedCharMap[(unsigned int) (*srcPtr)];
      } else {
         *(destPtr++) = *srcPtr;
      }

      srcPtr++;
   }

   VERIFY((destPtr - resultString) <= resultBufferLength);
   *destPtr = 0;

quit:
   free(base64String);

   if (err == VIX_OK) {
      *result = resultString;
   }

   return err;
} // VixMsgEncodeBuffer

/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_DecodeString --
 *
 *       This reverses VixMsg_EncodeString.
 *       See the notes for that procedure.
 *
 * Results:
 *      VixError. VIX_OK if successful.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_DecodeString(const char *str,   // IN
                    char **result)     // OUT
{
   /*
    * Check the character set.
    *   'a' means ASCII.
    */
   if ((NULL == str) || ('a' != *str)) {
      *result = NULL;
      return VIX_E_INVALID_ARG;
   }

   return VixMsgDecodeBuffer(str + 1, TRUE, result, NULL);
} // VixMsg_DecodeString


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsgDecodeBuffer --
 *
 *      This reverses VixMsgEncodeBuffer.
 *      See the notes for that procedure.
 *
 * Results:
 *      VixError. VIX_OK if successful.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsgDecodeBuffer(const char *str,           // IN
                   Bool nullTerminateResult,  // OUT
                   char **result,             // OUT
                   size_t *bufferLength)      // OUT: Optional
{
   VixError err = VIX_OK;
   char *base64String = NULL;
   char *resultStr = NULL;
   char *srcPtr;
   char *destPtr;
   size_t resultStrAllocatedLength;
   size_t resultStrLogicalLength;
   Bool allocateFailed;

   if (NULL != bufferLength) {
      *bufferLength = 0;
   }

   /*
    * Remove escaped special characters.
    * Do this in a private copy because we will change the string in place.
    */
   VixMsgInitializeObfuscationMapping();
   base64String = VixMsg_StrdupClientData(str, &allocateFailed);
   if (allocateFailed) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }
   destPtr = base64String;
   srcPtr = base64String;

   while (*srcPtr) {
      if ('\\' == *srcPtr) {
         srcPtr++;
         /*
          * There should never be a null byte as part of an escape character or
          * an escape character than translates into a null byte.
          */
         if ((0 == *srcPtr)
                || (0 == ObfuscatedToPlainCharMap[(unsigned int) (*srcPtr)])) {
            goto quit;
         }
         *(destPtr++) = ObfuscatedToPlainCharMap[(unsigned int) (*srcPtr)];
      } else {
         *(destPtr++) = *srcPtr;
      }
      srcPtr++;
   }
   *destPtr = 0;

   /*
    * Add 1 to the Base64_DecodedLength(), since we base64 encoded the string
    * without the NUL terminator and need to add one.
    */
   resultStrAllocatedLength = Base64_DecodedLength(base64String,
                                                   destPtr - base64String);
   if (nullTerminateResult) {
      resultStrAllocatedLength += 1;
   }

   resultStr = Util_SafeMalloc(resultStrAllocatedLength);
   if (!Base64_Decode(base64String,
                      resultStr,
                      resultStrAllocatedLength,
                      &resultStrLogicalLength)
          || (resultStrLogicalLength > resultStrAllocatedLength)) {
      free(resultStr);
      resultStr = NULL;
      goto quit;
   }

   if (nullTerminateResult) {
      VERIFY(resultStrLogicalLength < resultStrAllocatedLength);
      resultStr[resultStrLogicalLength] = 0;
   }

   if (NULL != bufferLength) {
      *bufferLength = resultStrLogicalLength;
   }

quit:
   free(base64String);

   if (err == VIX_OK) {
      *result = resultStr;
   }

   return err;
} // VixMsgDecodeBuffer


/*
 *-----------------------------------------------------------------------------
 *
 * VixAsyncOp_ValidateCommandInfoTable --
 *
 *      Checks that the command info table is generally well-formed.
 *      Makes sure that the table is big enough to contain all the
 *      command op codes and that they are present in the right order.
 *
 * Results:
 *      Bool
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
VixMsg_ValidateCommandInfoTable(void)
{
   int i;

   /*
    * Check at compile time that there is as many entries in the
    * command info table as there are commands. We need the +1 since
    * VIX_COMMAND_UNKNOWN is in the table and its opcode is -1.
    *
    * If this has failed for you, you've probably added a new command to VIX
    * without adding it to the command info table above.
    */
   ASSERT_ON_COMPILE(ARRAYSIZE(vixCommandInfoTable)
                        == (VIX_COMMAND_LAST_NORMAL_COMMAND + 1));

   /*
    * Iterated over all the elements in the command info table to make
    * sure that op code matches the index (they are shifted by one because
    * of VIX_COMMAND_UNKNOWN) and that every used entry has a non-NULL name.
    */
   for (i = 0; i < ARRAYSIZE(vixCommandInfoTable); i++) {
      if (vixCommandInfoTable[i].used &&
          ((vixCommandInfoTable[i].opCode != (i - 1)) ||
           (NULL == vixCommandInfoTable[i].commandName))) {
         Warning("%s: Mismatch or NULL in command with op code %d at "
                 "index %d.\n",
                 __FUNCTION__, vixCommandInfoTable[i].opCode, i);
         return FALSE;
      }
   }

   return TRUE;
} // VixMsg_ValidateCommandInfoTable


/*
 *-----------------------------------------------------------------------------
 *
 * VixAsyncOp_GetDebugStrForOpCode --
 *
 *      Get a human readable string representing the given op code, or
 *      "Unrecognized op" if the op code is invalid.
 *
 * Results:
 *      const char *
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

const char *
VixAsyncOp_GetDebugStrForOpCode(int opCode)  // IN
{
   const char *opName = "Unrecognized op";
   const VixCommandInfo *commandInfo;

   commandInfo = VixGetCommandInfoForOpCode(opCode);
   if (NULL != commandInfo) {
      opName = commandInfo->commandName;
      ASSERT(NULL != opName);
   }

   return opName;
} // VixAsyncOp_GetDebugStrForOpCode


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_GetCommandSecurityCategory --
 *
 *      Get the security category asociated with the given op code.
 *
 * Results:
 *      VixCommandSecurityCategory: the security category for the op code,
 *      or VIX_COMMAND_CATEGORY_UNKNOWN is the op code is invalid.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VixCommandSecurityCategory
VixMsg_GetCommandSecurityCategory(int opCode)  // IN
{
   VixCommandSecurityCategory category = VIX_COMMAND_CATEGORY_UNKNOWN;
   const VixCommandInfo *commandInfo;

   commandInfo = VixGetCommandInfoForOpCode(opCode);
   if (NULL != commandInfo) {
      category = commandInfo->category;
   }

   return category;
} // VixMsg_GetCommandSecurityCategory


/*
 *-----------------------------------------------------------------------------
 *
 * VixGetCommandInfoForOpCode --
 *
 *      Looks up the information for an opcode from the global op code table.
 *
 * Results:
 *      A const pointer to the command info struct for the opCode, or NULL
 *      if the op code is invalid.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static const VixCommandInfo *
VixGetCommandInfoForOpCode(int opCode)  // IN
{
   const VixCommandInfo *commandInfo = NULL;

   if ((opCode >= VIX_COMMAND_UNKNOWN) &&
       (opCode < VIX_COMMAND_LAST_NORMAL_COMMAND)) {
      /* Add 1 to the op code, since VIX_COMMAND_UNKNOWN is -1 */
      if (vixCommandInfoTable[opCode + 1].used) {
         commandInfo = &vixCommandInfoTable[opCode + 1];
      }
   }

   return commandInfo;
} // VixGetCommandInfoForOpCode


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_AllocGenericRequestMsg --
 *
 *      Allocate and initialize a generic request message.
 *
 *      Assumes the caller holds the lock to 'propertyList'.
 *
 * Results:
 *      Returns VixError.
 *      Upon retrun, *request will contain either the message with the
 *      headers properly initialized or NULL.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_AllocGenericRequestMsg(int opCode,                         // IN
                              uint64 cookie,                      // IN
                              int credentialType,                 // IN
                              const char *userNamePassword,       // IN
                              int options,                        // IN
                              VixPropertyListImpl *propertyList,  // IN
                              VixCommandGenericRequest **request) // OUT
{
   VixError err;
   VixCommandGenericRequest *requestLocal = NULL;
   size_t msgHeaderAndBodyLength;
   char *serializedBufferBody = NULL;
   size_t serializedBufferLength = 0;

   if (NULL == request) {
      ASSERT(0);
      err = VIX_E_FAIL;
      goto quit;
   }

   *request = NULL;

   if (NULL != propertyList) {
      err = VixPropertyList_Serialize(propertyList,
                                      FALSE,
                                      &serializedBufferLength,
                                      &serializedBufferBody);
      if (VIX_OK != err) {
         goto quit;
      }
   }

   msgHeaderAndBodyLength = sizeof(*requestLocal) + serializedBufferLength;
   requestLocal = (VixCommandGenericRequest *)
      VixMsg_AllocRequestMsg(msgHeaderAndBodyLength,
                             opCode,
                             cookie,
                             credentialType,
                             userNamePassword);
   if (NULL == requestLocal) {
      err = VIX_E_FAIL;
      goto quit;
   }

   requestLocal->options = options;
   requestLocal->propertyListSize = serializedBufferLength;

   if (NULL != serializedBufferBody) {
      char *dst = (char *)request + sizeof(*request);
      memcpy(dst, serializedBufferBody, serializedBufferLength);
   }

   *request = requestLocal;
   err = VIX_OK;

 quit:
   free(serializedBufferBody);

   return err;
}  // VixMsg_AllocGenericRequestMsg


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_ParseGenericRequestMsg --
 *
 *      Extract the options and property list from the request
 *      message, while validating message.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_ParseGenericRequestMsg(const VixCommandGenericRequest *request,  // IN
                              int *options,                             // OUT
                              VixPropertyListImpl *propertyList)        // OUT
{
   VixError err;
   uint64 headerAndBodyLength;

   if ((NULL == request) || (NULL == options) || (NULL == propertyList)) {
      ASSERT(0);
      err = VIX_E_FAIL;
      goto quit;
   }

   *options = 0;
   VixPropertyList_Initialize(propertyList);

   /*
    * In most cases we will have already called VixMsg_ValidateResponseMsg()
    * on this request before, but call it here so that this function will
    * always be sufficient to validate the request.
    */
   err = VixMsg_ValidateRequestMsg(request,
                                   request->header.commonHeader.totalMessageLength);
   if (VIX_OK != err) {
      goto quit;
   }

   if (request->header.commonHeader.totalMessageLength < sizeof *request) {
      err = VIX_E_INVALID_MESSAGE_BODY;
      goto quit;
   }

   headerAndBodyLength = (uint64) request->header.commonHeader.headerLength
      + request->header.commonHeader.bodyLength;

   if (headerAndBodyLength < ((uint64) sizeof *request
                              + request->propertyListSize)) {
      err = VIX_E_INVALID_MESSAGE_BODY;
      goto quit;
   }

   if (request->propertyListSize > 0) {
      const char *serializedBuffer = (const char *) request + sizeof(*request);

      err = VixPropertyList_Deserialize(propertyList,
                                        serializedBuffer,
                                        request->propertyListSize,
                                        VIX_PROPERTY_LIST_BAD_ENCODING_ERROR);
      if (VIX_OK != err) {
         goto quit;
      }
   }

   *options = request->options;
   err = VIX_OK;

 quit:

   return err;
} // VixMsg_ParseGenericRequestMsg



/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_ParseSimpleResponseWithString --
 *
 *      Takes a response packet that consists of a VixCommandResponseHeader
 *      followed by a string containing the response data, validates
 *      the packet, and then passes out a pointer to that string.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixMsg_ParseSimpleResponseWithString(const VixCommandResponseHeader *response,  // IN
                                     const char **result)                       // OUT
{
   VixError err;
   VMAutomationMsgParser parser;

   err = VMAutomationMsgParserInitResponse(&parser, response, sizeof *response);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VMAutomationMsgParserGetOptionalString(&parser,
                                                response->commonHeader.bodyLength,
                                                result);

quit:
   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_MallocClientData --
 *
 *      Allocates the memory needed to copy from a client-provided buffer.
 *
 * Results:
 *      Pointer to allocated memory
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void *
VixMsg_MallocClientData(size_t size)  // IN
{
   return malloc(size);
} // VixMsg_MallocClientData


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_ReallocClientData --
 *
 *      Reallocates the memory needed to copy from a client-provided buffer.
 *
 * Results:
 *      Pointer to allocated memory
 *
 * Side effects:
 *      Frees memory pointed to by ptr.
 *
 *-----------------------------------------------------------------------------
 */

void *
VixMsg_ReallocClientData(void *ptr,   // IN
                         size_t size) // IN
{
   return realloc(ptr, size);
} // VixMsg_ReallocClientData


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg_StrdupClientData --
 *
 *      Allocates memory and copies client-provided string.
 *
 * Results:
 *      Pointer to allocated string
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
VixMsg_StrdupClientData(const char *s,          // IN
                        Bool *allocateFailed)   // OUT
{
   char* newString = NULL;

   ASSERT(allocateFailed);
   if (NULL == allocateFailed) {
      goto quit;
   }

   *allocateFailed = FALSE;

   if (NULL != s) {
#if defined(_WIN32)
         newString = _strdup(s);
#else
         newString = strdup(s);
#endif
      if (NULL == newString) {
         *allocateFailed = TRUE;
      }
   }

quit:
   return newString;
} // VixMsg_StrdupClientData


/*
 *-----------------------------------------------------------------------------
 *
 * __VMAutomationValidateString --
 *
 *      Verifies that string at specified address is NUL terminated within
 *      specified number of bytes, and is valid UTF-8.
 *
 * Results:
 *      VixError.  VIX_OK on success.  Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static VixError
__VMAutomationValidateString(const char  *caller,              // IN
                             unsigned int line,                // IN
                             const char  *buffer,              // IN
                             size_t       available)           // IN
{
   size_t stringLength;

   /*
    * NUL terminated string needs at least one byte - NUL one.
    */
   if (available < 1) {
      Log("%s(%u): Message body too short to contain string.\n", caller, line);
      return VIX_E_INVALID_MESSAGE_BODY;
   }

   /*
    * Reject message if there is no NUL before request end.  There must
    * be one...
    */

   stringLength = Str_Strlen(buffer, available);
   if (stringLength >= available) {
      Log("%s(%u): Variable string is not NUL terminated "
          "before message end.\n", caller, line);
      return VIX_E_INVALID_MESSAGE_BODY;
   }

   /*
    * If string is shorter than expected, complain.  Maybe it is too strict,
    * but clients seems to not send malformed messages, so keep doing this.
    */

   if (stringLength + 1 != available) {
      Log("%s(%u): Retrieved fixed string \"%s\" with "
          "trailing garbage.\n", caller, line, buffer);
      return VIX_E_INVALID_MESSAGE_BODY;
   }

   /*
    * If string is not UTF-8, reject it.  We do not want to pass non-UTF-8
    * strings through vmx bowels - they could hit some ASSERT somewhere...
    */

   if (!Unicode_IsBufferValid(buffer, stringLength, STRING_ENCODING_UTF8)) {
      Log("%s(%u): Variable string is not an UTF8 string.\n", caller, line);
      return VIX_E_INVALID_UTF8_STRING;
   }

   return VIX_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * __VMAutomationValidateStringInBuffer --
 *
 *      Verifies that string at specified address is NUL terminated within
 *      specified number of bytes, and is valid UTF-8.
 *      String does not have to occupy the entire buffer.
 *
 * Results:
 *      VixError.  VIX_OK on success.
 *      Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static VixError
__VMAutomationValidateStringInBuffer(const char  *caller,              // IN
                                     unsigned int line,                // IN
                                     const char  *buffer,              // IN
                                     size_t       available,           // IN
                                     size_t      *strLen)              // IN
{
   size_t stringLength;

   /*
    * NUL terminated string needs at least one byte - NUL one.
    */
   if (available < 1) {
      Log("%s(%u): Message body too short to contain string.\n", caller, line);
      return VIX_E_INVALID_MESSAGE_BODY;
   }

   /*
    * Reject message if there is no NUL before request end.  There must
    * be one...
    */

   stringLength = Str_Strlen(buffer, available);
   *strLen = stringLength;

   if (stringLength >= available) {
      Log("%s(%u): Variable string is not NUL terminated "
          "before message end.\n", caller, line);
      return VIX_E_INVALID_MESSAGE_BODY;
   }

   /*
    * If string is not UTF-8, reject it.  We do not want to pass non-UTF-8
    * strings through vmx bowels - they could hit some ASSERT somewhere...
    */

   if (!Unicode_IsBufferValid(buffer, stringLength, STRING_ENCODING_UTF8)) {
      Log("%s(%u): Variable string is not an UTF8 string.\n", caller, line);
      return VIX_E_INVALID_UTF8_STRING;
   }

   return VIX_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * __VMAutomationMsgParserInitRequest --
 * VMAutomationMsgParserInitRequest --
 *
 *      Initializes request parser, and performs basic message validation
 *      not performed elsewhere.
 *
 * Results:
 *      VixError.  VIX_OK on success.  Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
__VMAutomationMsgParserInitRequest(const char *caller,                  // IN
                                   unsigned int line,                   // IN
                                   VMAutomationMsgParser *state,        // OUT (opt)
                                   const VixCommandRequestHeader *msg,  // IN
                                   size_t fixedLength)                  // IN
{
   size_t miscDataLength = 0;

   /*
    * If the VM is encrypted, there is additional data factored into
    * the total message size that needs to be accounted for.
    */

   if (VIX_REQUESTMSG_INCLUDES_AUTH_DATA_V1 & msg->requestFlags) {
      miscDataLength = sizeof(VixMsgAuthDataV1);
   } else {
      miscDataLength = 0;
   }

   return VMAutomationMsgParserInit(caller, line, state, &msg->commonHeader,
                                    sizeof *msg, fixedLength, miscDataLength, "request");
}


/*
 *-----------------------------------------------------------------------------
 *
 * __VMAutomationMsgParserInitResponse --
 * VMAutomationMsgParserInitResponse --
 *
 *      Initializes response parser, and performs basic message validation
 *      not performed elsewhere.
 *
 * Results:
 *      VixError.  VIX_OK on success.  Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
__VMAutomationMsgParserInitResponse(const char *caller,                  // IN
                                    unsigned int line,                   // IN
                                    VMAutomationMsgParser *state,        // OUT (opt)
                                    const VixCommandResponseHeader *msg, // IN
                                    size_t fixedLength)                  // IN
{
   return VMAutomationMsgParserInit(caller, line, state, &msg->commonHeader,
                                    sizeof *msg, fixedLength, 0, "response");
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMAutomationMsgParserInit --
 *
 *      Initializes message parser, and performs basic message validation
 *      not performed elsewhere.
 *
 * Results:
 *      VixError. VIX_OK on success. Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VMAutomationMsgParserInit(const char *caller,              // IN
                          unsigned int line,               // IN
                          VMAutomationMsgParser *state,    // OUT (opt)
                          const VixMsgHeader *msg,         // IN
                          size_t headerLength,             // IN
                          size_t fixedLength,              // IN
                          size_t miscDataLength,           // IN
                          const char *packetType)          // IN
{
   uint32 headerAndBodyLength;
   // use int64 to prevent overflow
   int64 computedTotalLength = (int64)msg->headerLength +
      (int64)msg->bodyLength + (int64)msg->credentialLength +
      (int64)miscDataLength;

   int64 extBodySize = (int64)msg->headerLength + (int64)msg->bodyLength -
      (int64)fixedLength;

   if (computedTotalLength != (int64)msg->totalMessageLength) {
      Log("%s:%d, header information mismatch.\n", __FILE__, __LINE__);
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   if (extBodySize < 0) {
      Log("%s:%d, %s too short.\n", __FILE__, __LINE__, packetType);
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   /*
    * Protocol allows for headerLength expansion, but predefined structures
    * do not anticipate that even a bit. So give up if header length is
    * incompatible with our structures.
    */

   if (msg->headerLength != headerLength) {
      Log("%s(%u): %s header length %u is not supported "
          "(%"FMTSZ"u is required).\n",
          caller, line, packetType, msg->headerLength, headerLength);
      return VIX_E_INVALID_MESSAGE_HEADER;
   }

   /*
    * Message looks reasonable.  Skip over fixed part.
    */

   headerAndBodyLength = msg->headerLength + msg->bodyLength;

   if (state) {
      state->currentPtr = (const char *)msg + fixedLength;
      state->endPtr = (const char *)msg + headerAndBodyLength;
   }
   return VIX_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMAutomation_VerifyRequestLength --
 *
 *      Ensures that request contains at least fixedLength bytes in
 *      header and body.
 *
 * Results:
 *      VixError.  VIX_OK on success.  Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VMAutomation_VerifyRequestLength(const VixCommandRequestHeader *request, // IN
                                 size_t fixedLength)                     // IN
{
   return VMAutomationMsgParserInitRequest(NULL, request, fixedLength);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMAutomationMsgParserGetRemainingData --
 *
 *      Fetches all data remaining in the request.
 *
 * Results:
 *      Pointer to the data.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

const void *
VMAutomationMsgParserGetRemainingData(VMAutomationMsgParser *state,   // IN/OUT
                                      size_t *length)                 // OUT
{
   const void *data;

   *length = state->endPtr - state->currentPtr;
   data = state->currentPtr;
   state->currentPtr = state->endPtr;

   return data;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMAutomationMsgParserGetData --
 * __VMAutomationMsgParserGetData --
 *
 *      Fetches specified number of bytes.
 *
 * Results:
 *      VixError.  VIX_OK on success.  Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
__VMAutomationMsgParserGetData(const char *caller,             // IN
                               unsigned int line,              // IN
                               VMAutomationMsgParser *state,   // IN/OUT
                               size_t length,                  // IN
                               const char **result)            // OUT (opt)
{
   size_t available;

   available = state->endPtr - state->currentPtr;

   /* If message is too short, return an error. */
   if (available < length) {
      Log("%s(%u): Message has only %"FMTSZ"u bytes available when "
          "looking for %"FMTSZ"u bytes od data.\n",
          caller, line, available, length);
      return VIX_E_INVALID_MESSAGE_BODY;
   }

   if (result) {
      *result = state->currentPtr;
   }
   state->currentPtr += length;
   return VIX_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMAutomationMsgParserGetOptionalString --
 * __VMAutomationMsgParserGetOptionalString --
 *
 *      Fetches string of specified length from the request.  Length includes
 *      terminating NUL byte, which must be present.  Length of zero results
 *      in NULL being returned.
 *
 * Results:
 *      VixError.  VIX_OK on success.  Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
__VMAutomationMsgParserGetOptionalString(const char *caller,           // IN
                                         unsigned int line,            // IN
                                         VMAutomationMsgParser *state, // IN/OUT
                                         size_t length,                // IN
                                         const char **result)          // OUT
{
   if (length) {
      VixError err;
      const char *string;

      err = __VMAutomationMsgParserGetData(caller, line, state, length,
                                           &string);
      if (VIX_OK != err) {
         return err;
      }
      err = __VMAutomationValidateString(caller, line, string, length);
      if (VIX_OK != err) {
         return err;
      }
      *result = string;
   } else {
      *result = NULL;
   }
   return VIX_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMAutomationMsgParserGetOptionalStrings --
 * __VMAutomationMsgParserGetOptionalStrings --
 *
 *      Fetches an array of strings from the request.  Length includes the
 *      terminating NUL byte of each string.
 *
 * Results:
 *      VixError.  VIX_OK on success.
 *      Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
__VMAutomationMsgParserGetOptionalStrings(const char *caller,   // IN
                                          unsigned int line,    // IN
                                VMAutomationMsgParser *state,   // IN/OUT
                                          uint32 count,         // IN
                                          size_t length,        // IN
                                          const char **result) // OUT
{
   VixError err = VIX_OK;
   const char *buffer;
   const char *theResult;
   int i;
   size_t strLen;

   if (0 == count) {
      *result = NULL;
      goto quit;
   }

   err = __VMAutomationMsgParserGetData(caller, line, state, length,
                                        &buffer);
   if (VIX_OK != err) {
      return err;
   }

   theResult = buffer;

   for (i = 0; i < count; ++i) {
      err = __VMAutomationValidateStringInBuffer(caller, line,
                                                 buffer, length, &strLen);
      if (VIX_OK != err) {
         return err;
      }
      ASSERT(strLen < length);
      buffer += (strLen + 1);
      length -= (strLen + 1);
   }

   /*
    * If string is shorter than expected, complain.  Maybe it is too strict,
    * but clients seems to not send malformed messages, so keep doing this.
    */

   if (length != 0) {
      Log("%s(%u): Retrieved an array of string with trailing garbage.\n",
          caller, line);
      return VIX_E_INVALID_MESSAGE_BODY;
   }

   *result = theResult;

quit:

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMAutomationMsgParserGetString --
 * __VMAutomationMsgParserGetString --
 *
 *      Fetches string of specified length from the request.  Length of
 *      string is specified in number of usable characters: function consumes
 *      length + 1 bytes from request, and first length bytes must be non-NUL,
 *      while length+1st byte must be NUL.
 *
 * Results:
 *      VixError.  VIX_OK on success.  Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
__VMAutomationMsgParserGetString(const char *caller,            // IN
                                 unsigned int line,             // IN
                                 VMAutomationMsgParser *state,  // IN/OUT
                                 size_t length,                 // IN
                                 const char **result)           // OUT
{
   VixError err;
   const char *string;

   length++;
   if (!length) {
      Log("%s(%u): String is too long.\n", caller, line);
      return VIX_E_INVALID_ARG;
   }
   err = __VMAutomationMsgParserGetData(caller, line, state, length,
                                        &string);
   if (VIX_OK != err) {
      return err;
   }
   err = __VMAutomationValidateString(caller, line, string, length);
   if (VIX_OK != err) {
      return err;
   }

   *result = string;
   return VIX_OK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMAutomationMsgParserGetPropertyList --
 * __VMAutomationMsgParserGetPropertyList --
 *
 *      Fetches specified number of bytes.
 *
 * Results:
 *      VixError.  VIX_OK on success.  Some other VIX_* code if message is malformed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
__VMAutomationMsgParserGetPropertyList(const char *caller,            // IN
                                       unsigned int line,             // IN
                                       VMAutomationMsgParser *state,  // IN/OUT
                                       size_t length,                 // IN
                                       VixPropertyListImpl *propList) // IN/OUT
{
   VixError err;

   err = VIX_OK;
   if (length) {
      const char *data;

      err = __VMAutomationMsgParserGetData(caller, line, state, length,
                                           &data);
      if (VIX_OK == err) {
         err = VixPropertyList_Deserialize(propList, data, length,
                                           VIX_PROPERTY_LIST_BAD_ENCODING_ERROR);
      }
   }

   return err;
}
  07070100000053000081A4000000000000000000000001682255050000D723000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/lib/foundryMsg/foundryPropertyListCommon.c /*********************************************************
 * Copyright (C) 2004-2016, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * foundryPropertyListCommon.c --
 *
 * Some utility functions for manipulating property lists. Property
 * lists are now used in both the client and the VMX. The VMX uses them
 * as part of the socket protocol with the client. As a result, these
 * functions have been factored out into the stand-alone message library 
 * so it can be used by the VMX tree without also linking in the 
 * entire foundry client-side library.
 */

#include "vmware.h"
#include "util.h"
#include "str.h"
#include "unicode.h"
#include "vixCommands.h"

#include "vixOpenSource.h"

/*
 * The length of the 'size' field is 4 bytes -- avoid the confusion
 * of size_t on 32 vs 64 bit platforms.
 */
#define  PROPERTY_LENGTH_SIZE 4

/*
 * Lets not trust sizeof()
 */
#define  PROPERTY_SIZE_INT32     4
#define  PROPERTY_SIZE_INT64     8
#define  PROPERTY_SIZE_BOOL      1
// The size may be different on different machines.
// To be safe, we always use 8 bytes.
#define  PROPERTY_SIZE_POINTER   8

static VixError
VixPropertyListDeserializeImpl(VixPropertyListImpl *propList,
                               const char *buffer,
                               size_t bufferSize,
                               Bool clobber,
                               VixPropertyListBadEncodingAction action);


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_Initialize --
 *
 *       Initialize a list to be empty. This is an internal function
 *       that is used both when we allocate a property list that wil be passed
 *       to the client as a handle, and when we allocate an internal property
 *       list that was not allocated as a handle.
 *
 * Results:
 *       None.
 *
 * Side effects:
 *
 *-----------------------------------------------------------------------------
 */

void 
VixPropertyList_Initialize(VixPropertyListImpl *propList)  // IN
{
   ASSERT(propList);
   propList->properties = NULL;
} // VixPropertyList_Initialize


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_RemoveAllWithoutHandles --
 *
 *       Delete all properties in a list. This is an internal procedure
 *       that takes a VixPropertyListImpl as a parameter.
 *
 * Results:
 *       None
 *
 * Side effects:
 *       The property list is empty.
 *
 *-----------------------------------------------------------------------------
 */

void 
VixPropertyList_RemoveAllWithoutHandles(VixPropertyListImpl *propList)   // IN
{
   VixPropertyValue *property;

   if (NULL == propList) {
      return;
   }

   while (NULL != propList->properties) {
      property = propList->properties;
      propList->properties = property->next;

      if (VIX_PROPERTYTYPE_STRING == property->type) {
         if (property->isSensitive) {
            Util_ZeroString(property->value.strValue);
         }
         free(property->value.strValue);
      } else if (VIX_PROPERTYTYPE_BLOB == property->type) {
         if (property->isSensitive) {
            Util_Zero(property->value.blobValue.blobContents,
                      property->value.blobValue.blobSize);
         }
         free(property->value.blobValue.blobContents);
      }
    
      free(property);
   }
} // VixPropertyList_RemoveAllWithoutHandles


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_MarkAllSensitive --
 *
 *       Mark all properties in a list sensitive.
 *
 * Results:
 *       As above
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

void
VixPropertyList_MarkAllSensitive(VixPropertyListImpl *propList)  // IN/OUT:
{
   if (NULL != propList) {
      VixPropertyValue *property = propList->properties;

      while (NULL != property) {
         property->isSensitive = TRUE;

         property = property->next;
      }
   }
} // VixPropertyList_MarkAllSensitive


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_Serialize --
 *
 *       Serialize a property list to a buffer. The buffer is allocated by
 *       this routine to be of the required size and should be freed by caller.
 *
 *       This function should be modified to deal with the case of 
 *       properties of type VIX_PROPERTYTYPE_HANDLE.
 *     
 *
 * Results:
 *      VixError.
 *
 * Side effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_Serialize(VixPropertyListImpl *propList,  // IN:
                          Bool dirtyOnly,                 // IN:
                          size_t *resultSize,             // OUT:
                          char **resultBuffer)            // OUT:
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;
   char *serializeBuffer = NULL;
   int valueLength;
   size_t headerSize;
   size_t propertyIDSize;
   size_t propertyTypeSize;
   size_t propertyValueLengthSize;
   size_t bufferSize = 0;
   size_t pos = 0;
 
   ASSERT_ON_COMPILE(PROPERTY_LENGTH_SIZE == sizeof valueLength);

   if ((NULL == propList) ||
       (NULL == resultSize) ||
       (NULL == resultBuffer)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
 
   propertyIDSize = sizeof(property->propertyID);
   propertyTypeSize = sizeof(property->type);
   propertyValueLengthSize = PROPERTY_LENGTH_SIZE;

   headerSize = propertyIDSize + propertyTypeSize + propertyValueLengthSize;

   /*
    * Walk the property list to determine size of the needed buffer
    */
   property = propList->properties;
   while (NULL != property) {
      /*
       * If only the dirty properties need to be serialized
       * then skip the unchanged ones.
       */
      if (dirtyOnly && (!property->isDirty)) {
         property = property->next;
         continue;
      }

      bufferSize += headerSize;
    
      switch (property->type) {
         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_INTEGER:
            bufferSize += PROPERTY_SIZE_INT32;
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_STRING:
            if (property->value.strValue) {
               valueLength = strlen(property->value.strValue) + 1;
               /*
                * The deserialization code rejects all non-UTF-8 strings.
                * There should not be any non-UTF-8 strings passing
                * through our code since we should have either converted
                * non-UTF-8 strings from system APIs to UTF-8, or validated
                * that any client-provided strings were UTF-8. But this
                * if we've missed something, this should hopefully catch the
                * offending code close to the act.
                */
               if (!Unicode_IsBufferValid(property->value.strValue,
                                          valueLength,
                                          STRING_ENCODING_UTF8)) {
                  Log("%s: attempted to send a non-UTF-8 string for "
                      "property %d.\n",
                      __FUNCTION__, property->propertyID);
                  ASSERT(0);
                  err = VIX_E_INVALID_UTF8_STRING;
               }
               bufferSize += valueLength;
            } else {
               err = VIX_E_INVALID_ARG;
               goto quit;
            }
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_BOOL:
            bufferSize += PROPERTY_SIZE_BOOL;
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_INT64:
            bufferSize += PROPERTY_SIZE_INT64;
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_BLOB:
            bufferSize += property->value.blobValue.blobSize;
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_POINTER:
            /*
             * We should not serialize any pointer.
             * Catch such programming errors.
             */
            err = VIX_E_INVALID_ARG;
            Log("%s:%d, pointer properties cannot be serialized.\n",
                __FUNCTION__, __LINE__);
            goto quit;

         ////////////////////////////////////////////////////////
         default:
            err = VIX_E_UNRECOGNIZED_PROPERTY;
            goto quit;   
      }

      property = property->next;
   }

   *resultBuffer = (char*) VixMsg_MallocClientData(bufferSize);
   if (NULL == *resultBuffer) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }
   serializeBuffer = *resultBuffer;

   pos = 0;
   property = propList->properties;

   /*
    * Write out the properties to the buffer in the following format:
    * PropertyID | PropertyType | DataLength | Data
    */

   while (NULL != property) { 
      /*
       * If only the dirty properties need to be serialized
       * then skip the unchanged ones.
       */
      if (dirtyOnly && (!property->isDirty)) {
         property = property->next;
         continue;
      }

      memcpy(&(serializeBuffer[pos]), &(property->propertyID), propertyIDSize);
      pos += propertyIDSize;
      memcpy(&(serializeBuffer[pos]), &(property->type), propertyTypeSize);
      pos += propertyTypeSize;

      switch (property->type) {
         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_INTEGER:
             valueLength = PROPERTY_SIZE_INT32;
             memcpy(&(serializeBuffer[pos]), &valueLength, propertyValueLengthSize);
             pos += propertyValueLengthSize;
             memcpy(&(serializeBuffer[pos]), &(property->value.intValue), valueLength);
             break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_STRING:
             valueLength = (int) strlen(property->value.strValue) + 1;
             memcpy(&(serializeBuffer[pos]), &valueLength, propertyValueLengthSize);
             pos += propertyValueLengthSize;
             Str_Strcpy(&(serializeBuffer[pos]), property->value.strValue, valueLength);
             break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_BOOL:
             valueLength = PROPERTY_SIZE_BOOL;
             memcpy(&(serializeBuffer[pos]), &valueLength, propertyValueLengthSize);
             pos += propertyValueLengthSize;
             memcpy(&(serializeBuffer[pos]), &(property->value.boolValue), valueLength);
             break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_INT64:
             valueLength = PROPERTY_SIZE_INT64;
             memcpy(&(serializeBuffer[pos]), &valueLength, propertyValueLengthSize);
             pos += propertyValueLengthSize;
             memcpy(&(serializeBuffer[pos]), &(property->value.int64Value), valueLength);
             break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_BLOB:
            if (property->value.blobValue.blobContents) {
               valueLength = property->value.blobValue.blobSize;
               memcpy(&(serializeBuffer[pos]), &valueLength, propertyValueLengthSize);
               pos += propertyValueLengthSize;
               memcpy(&(serializeBuffer[pos]), 
                      property->value.blobValue.blobContents, 
                      valueLength);
            } else {
               err = VIX_E_INVALID_ARG;
               goto quit;
            }
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_POINTER:
            NOT_IMPLEMENTED();

         ////////////////////////////////////////////////////////
         default:
             err = VIX_E_UNRECOGNIZED_PROPERTY;
             goto quit;   
      }
    
      pos += valueLength;
      property = property->next;
   }

   ASSERT(pos == bufferSize);
   *resultSize = bufferSize;
 
quit:
   if (VIX_OK != err) {
      free(serializeBuffer);
      if (NULL != resultBuffer) {
         *resultBuffer = NULL;
      }
      if (NULL != resultSize) {
         *resultSize = 0;
      }
   }

   return err;
} // FoundryPropertList_Serialize


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_Deserialize --
 *
 *       Deserialize a property list from a buffer. Repeated properties
 *       are clobbered.
 *
 * Results:
 *      VixError.
 *
 * Side effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_Deserialize(VixPropertyListImpl *propList,            // IN
                            const char *buffer,                       // IN
                            size_t bufferSize,                        // IN
                            VixPropertyListBadEncodingAction action)  // IN
{
   return VixPropertyListDeserializeImpl(propList,
                                         buffer,
                                         bufferSize,
                                         TRUE, // clobber
                                         action);
} // VixPropertyList_Deserialize


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_DeserializeNoClobber --
 *
 *       Deserialize a property list from a buffer. Repeated properties
 *       are preserved.
 *
 * Results:
 *      VixError.
 *
 * Side effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_DeserializeNoClobber(VixPropertyListImpl *propList,     // IN
                                     const char *buffer,                // IN
                                     size_t bufferSize,                 // IN
                                     VixPropertyListBadEncodingAction action) // IN
{
   return VixPropertyListDeserializeImpl(propList,
                                         buffer,
                                         bufferSize,
                                         FALSE, // clobber
                                         action);
} // VixPropertyList_DeserializeNoClobber


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyListDeserializeImpl --
 *
 *       Deserialize a property list from a buffer. 
 *
 *       This function should be modified to deal with the case of 
 *       properties of type VIX_PROPERTYTYPE_HANDLE.
 *
 * Results:
 *      VixError.
 *
 * Side effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyListDeserializeImpl(VixPropertyListImpl *propList,            // IN
                               const char *buffer,                       // IN
                               size_t bufferSize,                        // IN
                               Bool clobber,                             // IN
                               VixPropertyListBadEncodingAction action)  // IN
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;
   size_t pos = 0;
   char *strPtr;
   int *intPtr;
   Bool *boolPtr;
   int64 *int64Ptr;
   unsigned char* blobPtr;
   int *propertyIDPtr;
   int *lengthPtr;
   size_t propertyIDSize;
   size_t propertyTypeSize;
   size_t propertyValueLengthSize;
   size_t headerSize;
   VixPropertyType *propertyTypePtr;
   Bool allocateFailed;
   Bool needToEscape;

   if ((NULL == propList)
       || (NULL == buffer)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   propertyIDSize = sizeof(*propertyIDPtr);
   propertyTypeSize = sizeof(*propertyTypePtr);
   propertyValueLengthSize = PROPERTY_LENGTH_SIZE;
   headerSize = propertyIDSize + propertyTypeSize + propertyValueLengthSize;

   /*
    * Read properties from the buffer and add them to the property list.
    */
   while ((pos+headerSize) < bufferSize) {
      propertyIDPtr = (int*) &(buffer[pos]);
      pos += propertyIDSize;
      propertyTypePtr = (VixPropertyType*) &(buffer[pos]);
      pos += propertyTypeSize;
      lengthPtr = (int*) &(buffer[pos]);
      pos += propertyValueLengthSize;

      /*
       * Do not allow lengths of 0 or fewer bytes. Those do not make sense,
       * unless you can pass a NULL blob, which Serialize() does not allow.
       * Also, make sure the value is contained within the bounds of the buffer.
       */
      if ((*lengthPtr < 1) || ((*lengthPtr + pos) > bufferSize)) {
         err = VIX_E_INVALID_SERIALIZED_DATA;
         goto quit;
      }

      /*
       * Create the property if missing
       */
      if (clobber) {
         err = VixPropertyList_FindProperty(propList,
                                            *propertyIDPtr,
                                            *propertyTypePtr,
                                            0, // index
                                            TRUE, //createIfMissing
                                            &property);
      } else {
         err = VixPropertyListAppendProperty(propList,
                                             *propertyIDPtr,
                                             *propertyTypePtr,
                                             &property);
      }

      if (VIX_OK != err) {
         goto quit;
      }

      /*
       * Initialize the property to the received value
       */
      switch (*propertyTypePtr) {
         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_INTEGER:
            if (PROPERTY_SIZE_INT32 != *lengthPtr) {
               err = VIX_E_INVALID_SERIALIZED_DATA;
               goto quit;
            }
            intPtr = (int*) &(buffer[pos]);
            property->value.intValue = *intPtr;
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_STRING:
            strPtr = (char*) &(buffer[pos]);
            /*
             * The length that Serialize() generates includes the terminating
             * NUL character.
             */
            if (strPtr[*lengthPtr - 1] != '\0') {
               err = VIX_E_INVALID_SERIALIZED_DATA;
               goto quit;
            }

            needToEscape = FALSE;

            /*
             * Make sure the string is valid UTF-8 before copying it. We
             * expect all strings stored in the process to be UTF-8.
             */
            if (!Unicode_IsBufferValid(strPtr, *lengthPtr,
                                       STRING_ENCODING_UTF8)) {
               Log("%s: non-UTF-8 string received for property %d.\n",
                   __FUNCTION__, *propertyIDPtr);
               switch (action) {
               case VIX_PROPERTY_LIST_BAD_ENCODING_ERROR:
                  err = VIX_E_INVALID_UTF8_STRING;
                  goto quit;
               case VIX_PROPERTY_LIST_BAD_ENCODING_ESCAPE:
                  needToEscape = TRUE;
               }

            }
            free(property->value.strValue);

            if (needToEscape) {
               property->value.strValue =
                  Unicode_EscapeBuffer(strPtr, *lengthPtr,
                                       STRING_ENCODING_UTF8);
               if (NULL == property->value.strValue) {
                  err = VIX_E_OUT_OF_MEMORY;
                  goto quit;
               }
            } else {
               property->value.strValue =
                  VixMsg_StrdupClientData(strPtr, &allocateFailed);
               if (allocateFailed) {
                  err = VIX_E_OUT_OF_MEMORY;
                  goto quit;
               }
            }
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_BOOL:
            if (PROPERTY_SIZE_BOOL != *lengthPtr) {
               err = VIX_E_INVALID_SERIALIZED_DATA;
               goto quit;
            }
            boolPtr = (Bool*) &(buffer[pos]);
            property->value.boolValue = *boolPtr;
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_INT64:
            if (PROPERTY_SIZE_INT64 != *lengthPtr) {
               err = VIX_E_INVALID_SERIALIZED_DATA;
               goto quit;
            }
            int64Ptr = (int64*) &(buffer[pos]);
            property->value.int64Value = *int64Ptr;
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_BLOB:
            blobPtr = (unsigned char*) &(buffer[pos]);
            property->value.blobValue.blobSize = *lengthPtr;
            /*
             * Use regular malloc() when allocating amounts specified by another
             * process. Admittedly we've already bounds checked it, but this is
             * pretty easy to handle.
             */
            free(property->value.blobValue.blobContents);
            property->value.blobValue.blobContents =
               VixMsg_MallocClientData(*lengthPtr);
            if (NULL == property->value.blobValue.blobContents) {
               err = VIX_E_OUT_OF_MEMORY;
               goto quit;
            }
            memcpy(property->value.blobValue.blobContents, blobPtr, *lengthPtr);
            break;

         ////////////////////////////////////////////////////////
         case VIX_PROPERTYTYPE_POINTER:
            /*
             * Deserialize an pointer property should not be allowed.
             * An evil peer could send us such data.
             */
            err = VIX_E_INVALID_SERIALIZED_DATA;
            Log("%s:%d, pointer properties cannot be serialized.\n",
                __FUNCTION__, __LINE__);
            goto quit;

         ////////////////////////////////////////////////////////
         default:
            err = VIX_E_UNRECOGNIZED_PROPERTY;
            goto quit;
      }

      pos += *lengthPtr;
   }

quit:
   if ((VIX_OK != err) && (NULL != propList)) {
      VixPropertyList_RemoveAllWithoutHandles(propList);
   }

   return err;
} // VixPropertyList_Deserialize


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_FindProperty --
 *
 *       This is an internal routine that finds a property in the list.
 *
 *       If the property is found, then this also checks that the property
 *       has an expected type; if the types mismatch thenit returns an error.
 *
 *       It optionally creates a property if it is missing.
 *
 * Results:
 *       VixError
 *
 * Side effects:
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_FindProperty(VixPropertyListImpl *propList,    // IN
                             int propertyID,                   // IN
                             VixPropertyType type,             // IN
                             int index,                        // IN
                             Bool createIfMissing,             // IN
                             VixPropertyValue **resultEntry)   // OUT
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == resultEntry) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
   *resultEntry = NULL;


   property = propList->properties;
   while (NULL != property) {
      if (propertyID == property->propertyID) {
         if (index > 0) {
            index--;
         } else {
            if ((VIX_PROPERTYTYPE_ANY != type) 
                  && (type != property->type)) {
               err = VIX_E_TYPE_MISMATCH;
            }
            *resultEntry = property;
            goto quit;
         }
      } // (propertyID == property->propertyID)

      property = property->next;
   } // while (NULL != property)

   /*
    * If we get to here, then the property doesn't exist.
    * Either create it or return an error.
    */
   if (!createIfMissing) {
      err = VIX_E_UNRECOGNIZED_PROPERTY;
      goto quit;
   }

   err = VixPropertyListAppendProperty(propList,
                                       propertyID,
                                       type,
                                       resultEntry);

quit:
   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyListAppendProperty --
 *
 *       This is an internal routine that creates a property for the
 *       append routines.
 *
 * Results:
 *       VixError
 *
 * Side effects:
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyListAppendProperty(VixPropertyListImpl *propList,   // IN
                              int propertyID,                  // IN
                              VixPropertyType type,            // IN
                              VixPropertyValue **resultEntry)  // OUT
{
   VixError err = VIX_OK;
   VixPropertyValue *lastProperty;
   VixPropertyValue *property = NULL;

   if (NULL == resultEntry) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
   *resultEntry = NULL;

   property = (VixPropertyValue *) 
      Util_SafeCalloc(1, sizeof(VixPropertyValue));

   property->type = type;
   property->propertyID = propertyID;
   property->isDirty = TRUE;
   property->isSensitive = FALSE;

   /*
    * We only have to initialize the values that we release, so
    * we don't try to release an invalid reference.
    */
   if (VIX_PROPERTYTYPE_STRING == property->type) {
      property->value.strValue = NULL;
   } else if (VIX_PROPERTYTYPE_BLOB == property->type) {
      property->value.blobValue.blobContents = NULL;
   } else if (VIX_PROPERTYTYPE_HANDLE == property->type) {
      property->value.handleValue = VIX_INVALID_HANDLE;
   }

   /*
    * Put the new property on the end of the list. Some property lists,
    * like a list of VMs or snapshots, assume the order is meaningful and 
    * so it should be preserved.
    */
   lastProperty = propList->properties;
   while ((NULL != lastProperty) && (NULL != lastProperty->next)) {
      lastProperty = lastProperty->next;
   }

 
   if (NULL == lastProperty) {
      propList->properties = property;
   } else {
      lastProperty->next = property;
   }
   property->next = NULL;


   *resultEntry = property;

quit:
   return err;
} // VixPropertyListAppendProperty



/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_GetString --
 *
 *       Return a copy of a string property value. The value is identified
 *       by the integer property ID.
 *
 *       This fails if the value is not present, or if it is a different
 *       type, or if the caller did not pass a valid out parameter to
 *       receive the value.
 *
 * Results:
 *       VixError. VIX_OK if the property was found.
 *                 VIX_E_UNRECOGNIZED_PROPERTY if the property was not found.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_GetString(VixPropertyListImpl *propList,       // IN
                          int propertyID,                      // IN
                          int index,                           // IN
                          char **resultValue)                  // OUT
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if ((NULL == propList) || (NULL == resultValue)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
   *resultValue = NULL;

   err = VixPropertyList_FindProperty(propList, 
                                      propertyID, 
                                      VIX_PROPERTYTYPE_STRING, 
                                      index,
                                      FALSE,
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL != property->value.strValue) {
      *resultValue = Util_SafeStrdup(property->value.strValue);
   }

quit:
   return err;
} // VixPropertyList_GetString


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyListSetStringImpl --
 *
 *       Saves a copy of a string property value. Sets sensitivity.
 *
 * Results:
 *       As above
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

static void
VixPropertyListSetStringImpl(VixPropertyValue *property,  // IN:
                             const char *value,           // IN:
                             Bool isSensitive)            // IN:
{
   if (NULL != property->value.strValue) {
      if (property->isSensitive) {
         Util_ZeroString(property->value.strValue);
      }
      free(property->value.strValue);
      property->value.strValue = NULL;
   }
   if (NULL != value) {
      property->value.strValue = Util_SafeStrdup(value);
   }
   property->isDirty = TRUE;
   property->isSensitive = isSensitive;
} // VixPropertyListSetStringImpl


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_SetString --
 *
 *       Saves a copy of a string property value. The value is identified
 *       by the integer property ID.
 *
 *       Value names are unique within a single property list.
 *       If a previous value with the same propertyID value already
 *       existed in this property list, then it is replaced with the new
 *       value. Otherwise, a new value is added.
 *
 *       This fails if the value is present but has a different type.
 *
 * Results:
 *       VixError
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_SetString(VixPropertyListImpl *propList,  // IN:
                          int propertyID,                 // IN:
                          const char *value)              // IN:
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == propList) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   /*
    * Find or create an entry for this property.
    */
   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_STRING,
                                      0,
                                      TRUE,
                                      &property);
   if (VIX_OK == err) {
      VixPropertyListSetStringImpl(property, value, property->isSensitive);
   }

quit:

   return err;
} // VixPropertyList_SetString


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_SetStringSensitive --
 *
 *       Saves a copy of a string property value. The value is identified
 *       by the integer property ID. Mark sensitive.
 *
 *       Value names are unique within a single property list.
 *       If a previous value with the same propertyID value already
 *       existed in this property list, then it is replaced with the new
 *       value. Otherwise, a new value is added.
 *
 *       This fails if the value is present but has a different type.
 *
 * Results:
 *       VixError
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_SetStringSensitive(VixPropertyListImpl *propList,  // IN:
                                   int propertyID,                 // IN:
                                   const char *value)              // IN:
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == propList) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   /*
    * Find or create an entry for this property.
    */
   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_STRING,
                                      0,
                                      TRUE,
                                      &property);

   if (VIX_OK == err) {
      VixPropertyListSetStringImpl(property, value, TRUE);
   }

quit:

   return err;
} // VixPropertyList_SetString


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_GetInteger --
 *
 *       Return a copy of a integer property value. The value is identified
 *       by the integer property ID.
 *
 *       This fails if the value is not present, or if it is a different
 *       type, or if the caller did not pass a valid out parameter to
 *       receive the value.
 *
 * Results:
 *       VixError. VIX_OK if the property was found.
 *                 VIX_E_UNRECOGNIZED_PROPERTY if the property was not found.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_GetInteger(VixPropertyListImpl *propList,      // IN
                           int propertyID,                     // IN
                           int index,                          // IN
                           int *resultValue)                   // OUT
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if ((NULL == resultValue) || (NULL == propList)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
 
   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_INTEGER, 
                                      index,
                                      FALSE, 
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   *resultValue = property->value.intValue;

quit: 
   return err;
} // VixPropertyList_GetInteger


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_SetInteger --
 *
 *       Saves a copy of a integer property value. The value is identified
 *       by the integer property ID.
 *
 *       Value names are unique within a single property list.
 *       If a previous value with the same propertyID value already
 *       existed in this property list, then it is replaced with the new
 *       value. Otherwise, a new value is added.
 *
 *       This fails if the value is present but has a different type.
 *
 * Results:
 *       VixError.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_SetInteger(VixPropertyListImpl *propList,      // IN
                           int propertyID,                     // IN
                           int value)                          // IN
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == propList) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
 
   /*
    * Find or create an entry for this property.
    */
   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_INTEGER, 
                                      0,
                                      TRUE, 
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   property->value.intValue = value;
   property->isDirty = TRUE;

quit:
   return err;
} // VixPropertyList_SetInteger


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_GetBool --
 *
 *       Return a copy of a boolean property value. The value is identified
 *       by the integer property ID.
 *
 *       This fails if the value is not present, or if it is a different
 *       type, or if the caller did not pass a valid out parameter to
 *       receive the value.
 *
 * Results:
 *       VixError. VIX_OK if the property was found.
 *                 VIX_E_UNRECOGNIZED_PROPERTY if the property was not found.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_GetBool(VixPropertyListImpl *propList,      // IN
                        int propertyID,                     // IN
                        int index,                          // IN
                        Bool *resultValue)                  // OUT
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if ((NULL == resultValue) || (NULL == propList)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_BOOL, 
                                      index,
                                      FALSE, 
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL == property) {
      goto quit;
   }

   *resultValue = property->value.boolValue;

quit: 
   return err;
} // VixPropertyList_GetBool


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_SetBool --
 *
 *       Saves a copy of a Bool property value. The value is identified
 *       by the integer property ID.
 *
 *       Value names are unique within a single property list.
 *       If a previous value with the same propertyID value already
 *       existed in this property list, then it is replaced with the new
 *       value. Otherwise, a new value is added.
 *
 *       This fails if the value is present but has a different type.
 *
 * Results:
 *       VixError.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_SetBool(VixPropertyListImpl *propList,      // IN
                        int propertyID,                     // IN
                        Bool value)                         // IN
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == propList) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   /*
    * Find or create an entry for this property.
    */
   err = VixPropertyList_FindProperty(propList,
                                      propertyID, 
                                      VIX_PROPERTYTYPE_BOOL, 
                                      0,
                                      TRUE, 
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   property->value.boolValue = value;
   property->isDirty = TRUE;

quit:
   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_GetInt64 --
 *
 *       Return a copy of a Int64 property value. The value is identified
 *       by the integer property ID.
 *
 *       This fails if the value is not present, or if it is a different
 *       type, or if the caller did not pass a valid out parameter to
 *       receive the value.
 *
 * Results:
 *       VixError. VIX_OK if the property was found.
 *                 VIX_E_UNRECOGNIZED_PROPERTY if the property was not found.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_GetInt64(VixPropertyListImpl *propList,     // IN
                         int propertyID,                    // IN
                         int index,                         // IN
                         int64 *resultValue)                // OUT
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if ((NULL == resultValue) || (NULL == propList)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_INT64, 
                                      index,
                                      FALSE, 
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   *resultValue = property->value.int64Value;

quit: 
   return err;
} // VixPropertyList_GetInt64


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_SetInt64 --
 *
 *       Saves a copy of a int64 property value. The value is identified
 *       by the integer property ID.
 *
 *       Value names are unique within a single property list.
 *       If a previous value with the same propertyID value already
 *       existed in this property list, then it is replaced with the new
 *       value. Otherwise, a new value is added.
 *
 *       This fails if the value is present but has a different type.
 *
 * Results:
 *       VixError.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_SetInt64(VixPropertyListImpl *propList,     // IN
                         int propertyID,                    // IN
                         int64 value)                       // IN
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == propList) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
 
   /*
    * Find or create an entry for this property.
    */
   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_INT64, 
                                      0,
                                      TRUE, 
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   property->value.int64Value = value;
   property->isDirty = TRUE;

quit:
   return err;
} // VixPropertyList_SetInt64


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_GetBlob --
 *
 *       Return a copy of a Blob property value. The value is identified
 *       by the integer property ID.
 *
 *       This fails if the value is not present, or if it is a different
 *       type, or if the caller did not pass a valid out parameter to
 *       receive the value.
 *
 * Results:
 *       VixError. VIX_OK if the property was found.
 *                 VIX_E_UNRECOGNIZED_PROPERTY if the property was not found.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_GetBlob(VixPropertyListImpl *propList,      // IN
                        int propertyID,                     // IN
                        int index,                          // IN
                        int *resultSize,                    // OUT
                        unsigned char **resultValue)        // OUT
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if ((NULL == propList) || (NULL == resultSize) || (NULL == resultValue)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
   *resultSize = 0;
   *resultValue = NULL;
 
   err = VixPropertyList_FindProperty(propList, 
                                      propertyID, 
                                      VIX_PROPERTYTYPE_BLOB, 
                                      index,
                                      FALSE,
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   if ((property->value.blobValue.blobSize > 0) 
         && (NULL != property->value.blobValue.blobContents)) {
      *resultSize = property->value.blobValue.blobSize;
    
      *resultValue = Util_SafeMalloc(property->value.blobValue.blobSize);
      memcpy(*resultValue, 
             property->value.blobValue.blobContents, 
             property->value.blobValue.blobSize);
   }

quit:
   return err;
} // VixPropertyList_GetBlob


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyListSetBlobImpl --
 *
 *       Saves a copy of a blob property value. Set sensitivity.
 *
 * Results:
 *       As above.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

static void
VixPropertyListSetBlobImpl(VixPropertyValue *property,  // IN:
                           int blobSize,                // IN:
                           const unsigned char *value,  // IN:
                           Bool isSensitive)            // IN:
{
   if (NULL != property->value.blobValue.blobContents) {
      if (property->isSensitive) {
         Util_Zero(property->value.blobValue.blobContents,
                   property->value.blobValue.blobSize);
      }

      free(property->value.blobValue.blobContents);
      property->value.blobValue.blobContents = NULL;
   }

   property->value.blobValue.blobSize = blobSize;
   if ((NULL != value) && (blobSize > 0)) {
      property->value.blobValue.blobContents = Util_SafeMalloc(blobSize);
      memcpy(property->value.blobValue.blobContents, value, blobSize);
   }

   property->isDirty = TRUE;
   property->isSensitive = isSensitive;
} // VixPropertyListSetBlobImpl


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_SetBlob --
 *
 *       Saves a copy of a blob property value. The value is identified
 *       by the integer property ID.
 *
 *       Value names are unique within a single property list.
 *       If a previous value with the same propertyID value already
 *       existed in this property list, then it is replaced with the new
 *       value. Otherwise, a new value is added.
 *
 *       This fails if the value is present but has a different type.
 *
 * Results:
 *       VixError.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_SetBlob(VixPropertyListImpl *propList,  // IN:
                        int propertyID,                 // IN:
                        int blobSize,                   // IN:
                        const unsigned char *value)     // IN:
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == propList) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   /*
    * Find or create an entry for this property.
    */
   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_BLOB,
                                      0,
                                      TRUE,
                                      &property);

   if (VIX_OK == err) {
      VixPropertyListSetBlobImpl(property, blobSize, value,
                                 property->isSensitive);
   }

quit:
   return err;
} // VixPropertyList_SetBlob


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_SetBlobSensitive --
 *
 *       Saves a copy of a blob property value. The value is identified
 *       by the integer property ID. Set sentivity.
 *
 *       Value names are unique within a single property list.
 *       If a previous value with the same propertyID value already
 *       existed in this property list, then it is replaced with the new
 *       value. Otherwise, a new value is added.
 *
 *       This fails if the value is present but has a different type.
 *
 * Results:
 *       VixError.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_SetBlobSensitive(VixPropertyListImpl *propList,  // IN:
                                 int propertyID,                 // IN:
                                 int blobSize,                   // IN:
                                 const unsigned char *value)     // IN:
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == propList) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   /*
    * Find or create an entry for this property.
    */
   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_BLOB,
                                      0,
                                      TRUE,
                                      &property);

   if (VIX_OK == err) {
      VixPropertyListSetBlobImpl(property, blobSize, value, TRUE);
   }

quit:
   return err;
} // VixPropertyList_SetBlob


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_GetPtr --
 *
 *       Return a copy of a void* property value. The value is identified
 *       by the integer property ID.
 *
 *       This is a SHALLOW copy. It only copies the pointer, not what the
 *       pointer references.
 *
 *       This fails if the value is not present, or if it is a different
 *       type, or if the caller did not pass a valid out parameter to
 *       receive the value.
 *
 * Results:
 *       VixError. VIX_OK if the property was found.
 *                 VIX_E_UNRECOGNIZED_PROPERTY if the property was not found.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_GetPtr(VixPropertyListImpl *propList,     // IN
                       int propertyID,                    // IN
                       int index,                         // IN
                       void **resultValue)                // OUT
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if ((NULL == resultValue) || (NULL == propList)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_POINTER, 
                                      index,
                                      FALSE, 
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   *resultValue = property->value.ptrValue;

quit: 
   return err;
} // VixPropertyList_GetPtr


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_SetPtr --
 *
 *       Saves a copy of a ptr property value. The value is identified
 *       by the integer property ID.
 *
 *       This is a SHALLOW copy. It only copies the pointer, not what the
 *       pointer references.
 *
 *       Value names are unique within a single property list.
 *       If a previous value with the same propertyID value already
 *       existed in this property list, then it is replaced with the new
 *       value. Otherwise, a new value is added.
 *
 *       This fails if the value is present but has a different type.
 *
 * Results:
 *       VixError.
 *
 * Side effects:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixPropertyList_SetPtr(VixPropertyListImpl *propList,     // IN
                       int propertyID,                    // IN
                       void *value)                       // IN
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;

   if (NULL == propList) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
 
   /*
    * Find or create an entry for this property.
    */
   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      VIX_PROPERTYTYPE_POINTER, 
                                      0,
                                      TRUE, 
                                      &property);
   if (VIX_OK != err) {
      goto quit;
   }

   property->value.ptrValue = value;
   property->isDirty = TRUE;

quit:
   return err;
} // VixPropertyList_SetPtr


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_PropertyExists --
 *
 *
 * Results:
 *       Bool
 *
 * Side effects:
 *
 *-----------------------------------------------------------------------------
 */

Bool
VixPropertyList_PropertyExists(VixPropertyListImpl *propList,     // IN
                               int propertyID,                    // IN
                               VixPropertyType type)              // IN
{
   VixError err = VIX_OK;
   VixPropertyValue *property = NULL;
   Bool foundIt = FALSE;

   err = VixPropertyList_FindProperty(propList,
                                      propertyID,
                                      type,
                                      0, // index
                                      FALSE, // createIfMissing
                                      &property);
   if ((VIX_OK == err) && (NULL != property)) {
      foundIt = TRUE;
   }

   return foundIt;
} // VixPropertyList_PropertyExists


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_NumItems --
 *
 *       Returns a count of the properties in the list.
 *
 * Results:
 *       int - Number of properties in property list.
 *
 * Side effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

int
VixPropertyList_NumItems(VixPropertyListImpl *propList)     // IN
{
   VixPropertyValue *prop;
   int count = 0;

   if (propList == NULL) {
      return 0;
   }

   for (prop = propList->properties; prop != NULL; prop = prop->next) {
      ++count;
   }

   return count;
} // VixPropertyList_NumItems


/*
 *-----------------------------------------------------------------------------
 *
 * VixPropertyList_Empty --
 *
 *       Returns whether the property list has no properties.
 *
 * Results:
 *       Bool - True iff property list has no properties.
 *
 * Side effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
VixPropertyList_Empty(VixPropertyListImpl *propList)     // IN
{
   return (propList == NULL || propList->properties == NULL);
} // VixPropertyList_Empty


 07070100000054000081A400000000000000000000000168225505000027D6000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/lib/foundryMsg/vixTranslateErrOpenSource.c /*********************************************************
 * Copyright (C) 2003-2017,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vixTranslateErrorOpenSource.c --
 *
 * Routines which translate between various other error code systems
 * into foundry errors.
 *
 * This is the minimal functions needed to build the tools for open source.
 * Most of the error translation functions are in foundryTranslateError.c,
 * which is NOT being released as open source. We do not want to include
 * any unnecessary error functions, since those use lots of different
 * error code definitions, and that would drag in a lot of headers from
 * bora/lib/public.
 */

#include "vmware.h"
#include "vixOpenSource.h"
#include <errno.h>
#include <string.h>

#ifdef _WIN32
#include "windowsu.h"
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Vix_TranslateGuestRegistryError --
 *
 *     Translate a guest windows registry error to a Foundry error.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VixError
Vix_TranslateGuestRegistryError(int systemError) // IN
{
   VixError err = VIX_E_FAIL;
#ifdef _WIN32
   char *msg;

   switch (systemError) {
   case ERROR_INVALID_PARAMETER:
   case ERROR_FILE_NOT_FOUND:
      err = VIX_E_REG_KEY_INVALID;
      break;
   case ERROR_ACCESS_DENIED:
      err = VIX_E_GUEST_USER_PERMISSIONS;
      break;
   case ERROR_CHILD_MUST_BE_VOLATILE:
      err = VIX_E_REG_KEY_PARENT_VOLATILE;
      break;
   default:
      return Vix_TranslateSystemError(systemError);
   }
   msg = Win32U_FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                              NULL, systemError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                              NULL);

   Log("Foundry operation failed with guest windows registry error: %s (%d), translated to %"FMT64"d\n",
       msg, systemError, err);
   free(msg);

#endif
   return err;
} // Vix_TranslateGuestRegistryError


/*
 *-----------------------------------------------------------------------------
 *
 * Vix_TranslateSystemError --
 *
 *     Translate a System error to a Foundry error.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *
 *-----------------------------------------------------------------------------
 */

VixError
Vix_TranslateSystemError(int systemError) // IN
{
   VixError err;
#ifdef _WIN32
   char *msg;

   switch (systemError) {
   case ERROR_ACCESS_DENIED:
      err = VIX_E_FILE_ACCESS_ERROR;
      break;
   case ERROR_INVALID_NAME:
      err = VIX_E_FILE_NAME_INVALID;
      break;
   case ERROR_FILENAME_EXCED_RANGE:
      err = VIX_E_FILE_NAME_TOO_LONG;
      break;
   case ERROR_FILE_NOT_FOUND:
   case ERROR_PATH_NOT_FOUND:
   case ERROR_BAD_PATHNAME:
   case ERROR_DIRECTORY:
   case ERROR_BUFFER_OVERFLOW:
      err = VIX_E_FILE_NOT_FOUND;
      break;
   case ERROR_DIR_NOT_EMPTY:
      err = VIX_E_DIRECTORY_NOT_EMPTY;
      break;
   case ERROR_TOO_MANY_OPEN_FILES:
   case ERROR_NO_MORE_FILES:
   case ERROR_WRITE_PROTECT:
   case ERROR_WRITE_FAULT:
   case ERROR_READ_FAULT:
   case ERROR_SHARING_VIOLATION:
   case ERROR_SEEK:
   case ERROR_CANNOT_MAKE:
      Log("%s: system error = %d\n", __FUNCTION__,
          systemError);
      err = VIX_E_FILE_ERROR;
      break;
   case ERROR_HANDLE_DISK_FULL:
   case ERROR_DISK_FULL:
      err = VIX_E_DISK_FULL;
      break;
   case ERROR_FILE_EXISTS:
   case ERROR_ALREADY_EXISTS:
      err = VIX_E_FILE_ALREADY_EXISTS;
      break;
   case ERROR_BUSY:
   case ERROR_PATH_BUSY:
      err = VIX_E_OBJECT_IS_BUSY;
      break;
   case ERROR_INVALID_PARAMETER:
      err = VIX_E_INVALID_ARG;
      break;
   case ERROR_NOT_SUPPORTED:
      err = VIX_E_NOT_SUPPORTED;
      break;
   case ERROR_NO_DATA:
   case ERROR_INVALID_DATA:
      err = VIX_E_NOT_FOUND;
      break;
   case ERROR_NOT_ENOUGH_MEMORY:
      err = VIX_E_OUT_OF_MEMORY;
      break;
   default:
      err = VIX_E_FAIL;
   }
   msg = Win32U_FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
                              NULL, systemError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                              NULL);

   Log("Foundry operation failed with system error: %s (%d), translated to %"FMT64"d\n",
       msg, systemError, err);
   free(msg);

#else // linux, other *nix
   err = Vix_TranslateErrno(systemError);
#endif

   return err;
} // Vix_TranslateSystemError


/*
 *-----------------------------------------------------------------------------
 *
 * Vix_TranslateCOMError --
 *
 *     Translate a COM (Windows) error to a Foundry error.
 *
 * Results:
 *     VixError.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

#ifdef _WIN32
VixError
Vix_TranslateCOMError(HRESULT hrError) // IN
{
   VixError err = VIX_E_FAIL;

   switch (hrError) {
   case E_ACCESSDENIED:
      err = VIX_E_FILE_ACCESS_ERROR;
      break;

   case STG_E_PATHNOTFOUND:
   case STG_E_FILENOTFOUND:
      err = VIX_E_FILE_NOT_FOUND;
      break;

   case STG_E_MEDIUMFULL:
      err = VIX_E_DISK_FULL;
      break;

   case STG_E_FILEALREADYEXISTS:
      err = VIX_E_FILE_ALREADY_EXISTS;
      break;

   case E_INVALIDARG:
   case E_POINTER:
      err = VIX_E_INVALID_ARG;
      break;

   case E_NOTIMPL:
   case E_NOINTERFACE:
      err = VIX_E_NOT_SUPPORTED;
      break;

   case E_OUTOFMEMORY:
      err = VIX_E_OUT_OF_MEMORY;
      break;

   case E_FAIL:
   default:
      err = VIX_E_FAIL;
   }

   return err;
} // Vix_TranslateCOMError
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Vix_TranslateCryptoError --
 *
 *     Translate a Crypto error to a Foundry error.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *
 *-----------------------------------------------------------------------------
 */

VixError
Vix_TranslateCryptoError(CryptoError cryptoError) // IN
{
   if (CRYPTO_ERROR_SUCCESS == cryptoError) {
      return VIX_OK;
   } else if (CRYPTO_ERROR_OPERATION_FAILED == cryptoError) {
      return VIX_E_GUEST_USER_PERMISSIONS;
   } else if (CRYPTO_ERROR_UNKNOWN_ALGORITHM == cryptoError) {
      return VIX_E_CRYPTO_UNKNOWN_ALGORITHM;
   } else if (CRYPTO_ERROR_BAD_BUFFER_SIZE == cryptoError) {
      return VIX_E_CRYPTO_BAD_BUFFER_SIZE;
   } else if (CRYPTO_ERROR_INVALID_OPERATION == cryptoError) {
      return VIX_E_CRYPTO_INVALID_OPERATION;
   } else if (CRYPTO_ERROR_NOMEM == cryptoError) {
      return VIX_E_OUT_OF_MEMORY;
   } else if (CRYPTO_ERROR_NEED_PASSWORD == cryptoError) {
      return VIX_E_CRYPTO_NEED_PASSWORD;
   } else if (CRYPTO_ERROR_BAD_PASSWORD == cryptoError) {
      return VIX_E_CRYPTO_BAD_PASSWORD;
   } else if (CRYPTO_ERROR_IO_ERROR == cryptoError) {
      Log("%s: crypto error = %d\n", __FUNCTION__,
          (int)cryptoError);
      return VIX_E_FILE_ERROR;
   } else if (CRYPTO_ERROR_UNKNOWN_ERROR == cryptoError) {
      return VIX_E_FAIL;
   } else if (CRYPTO_ERROR_NAME_NOT_FOUND == cryptoError) {
      return VIX_E_CRYPTO_NOT_IN_DICTIONARY;
   } else if (CRYPTO_ERROR_NO_CRYPTO == cryptoError) {
      return VIX_E_CRYPTO_NO_CRYPTO;
   }

   return VIX_E_FAIL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Vix_TranslateErrno --
 *
 *     Translate a Posix errno to a Foundry error.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VixError
Vix_TranslateErrno(int systemError)             // IN
{
   VixError err = VIX_E_FAIL;

   /*
    * Be careful while adding new error code translations. This function is
    * complied for both Windows and POSIX guests. Few errors i.e. ETIMEDOUT,
    * ENOBUFS are defined only for POSIX guests. When a new error code
    * translation is added, make sure you build a sandbox job and it is
    * successful.
    */

   switch (systemError) {
   case EPERM:
   case EACCES:
      err = VIX_E_FILE_ACCESS_ERROR;
      break;
   case EAGAIN:
   case EBUSY:
      err = VIX_E_OBJECT_IS_BUSY;
      break;
   case EEXIST:
      err = VIX_E_FILE_ALREADY_EXISTS;
      break;
   case EFBIG:
      err = VIX_E_FILE_TOO_BIG;
      break;
   case ENOTEMPTY:
      err = VIX_E_DIRECTORY_NOT_EMPTY;
      break;
   case ENOTDIR:
      err = VIX_E_NOT_A_DIRECTORY;
      break;
#ifndef _WIN32
   case ETIMEDOUT:
   case ENOBUFS:
#endif
   case EIO:
   case EMFILE:
   case ENFILE:
   case EMLINK:
   case EROFS:
      Log("%s: errno = %d\n", __FUNCTION__,
                              systemError);
      err = VIX_E_FILE_ERROR;
      break;
   case ENODEV:
   case ENOENT:
      err = VIX_E_FILE_NOT_FOUND;
      break;
   case ENOSPC:
      err = VIX_E_DISK_FULL;
      break;
   case EISDIR:
      err = VIX_E_NOT_A_FILE;
      break;
   case ESRCH:
      err = VIX_E_NO_SUCH_PROCESS;
      break;
   case ENAMETOOLONG:
      err = VIX_E_FILE_NAME_TOO_LONG;
      break;
#ifndef _WIN32
   case EMSGSIZE:
#endif
   case EINVAL:
      err = VIX_E_INVALID_ARG;
      break;
#ifndef _WIN32
   case ELOOP:
#endif
   case ENOMEM:
      err = VIX_E_OUT_OF_MEMORY;
      break;
   default:
      err = VIX_E_FAIL;
   }
   Log("Foundry operation failed with system error: %s (%d), translated to %"FMT64"d\n",
       strerror(systemError), systemError, err);

   return err;
} // Vix_TranslateErrno

  07070100000055000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/lib/glibUtils  07070100000056000081A4000000000000000000000001682255050000047D000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/glibUtils/Makefile.am  ################################################################################
### Copyright (C) 2011-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libGlibUtils.la

libGlibUtils_la_CPPFLAGS =
libGlibUtils_la_CPPFLAGS += @GLIB2_CPPFLAGS@

libGlibUtils_la_SOURCES =
libGlibUtils_la_SOURCES += fileLogger.c
libGlibUtils_la_SOURCES += stdLogger.c
libGlibUtils_la_SOURCES += sysLogger.c
   07070100000057000081A4000000000000000000000001682255050000372F000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/glibUtils/fileLogger.c /*********************************************************
 * Copyright (C) 2010-2019, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file fileLogger.c
 *
 * Logger that uses file streams and provides optional log rotation.
 */

#include "glibUtils.h"
#include <stdio.h>
#include <string.h>
#include <glib/gstdio.h>
#if defined(G_PLATFORM_WIN32)
#  include <process.h>
#  include <windows.h>
#  include "win32Access.h"
#else
#  include <fcntl.h>
#  include <unistd.h>
#endif


typedef struct FileLogger {
   GlibLogger     handler;
   GIOChannel    *file;
   gchar         *path;
   gint           logSize;
   guint64        maxSize;
   guint          maxFiles;
   gboolean       append;
   gboolean       error;
   GMutex         lock;
} FileLogger;


#if !defined(_WIN32)
/*
 *******************************************************************************
 * FileLoggerIsValid --                                                   */ /**
 *
 * Checks that the file descriptor backing this logger is still valid.
 *
 * This is a racy workaround for an issue with glib code; or, rather, two
 * issues. The first issue is that we can't intercept G_LOG_FLAG_RECURSION,
 * and glib just quits when that happens (see gnome bug 618956). The second
 * is that if a GIOChannel channel write fails, that calls
 * g_io_channel_error_from_errno, which helpfully logs stuff, causing recursion.
 * Don't get me started on why that's, well, at least questionable.
 *
 * This is racy because between the check and the actual GIOChannel operation,
 * the state of the FD may have changed. In reality, since the bug this is
 * fixing happens in very special situations where code outside this file is
 * doing weird things like closing random fds, it should be OK.
 *
 * We may still get other write errors from the GIOChannel than EBADF, but
 * those would be harder to work around. Hopefully this handles the most usual
 * cases.
 *
 * See bug 783999 for some details about what triggers the bug.
 *
 * @param[in] logger The logger instance.
 *
 * @return TRUE if the I/O channel is still valid.
 *
 *******************************************************************************
 */

static gboolean
FileLoggerIsValid(FileLogger *logger)
{
   if (logger->file != NULL) {
      int fd = g_io_channel_unix_get_fd(logger->file);
      return fcntl(fd, F_GETFD) >= 0;
   }

   return FALSE;
}

#else

#define FileLoggerIsValid(logger) TRUE

#endif


/*
 * This function is a temporary workaround for a broken glib version
 * 2.42.2 that is missing g_get_user_name_utf8 export. After glib
 * is fixed this needs to be removed and call to GlibGetUserName
 * replaced by g_get_user_name. See bug 1434059 for details.
 */
const char* GlibGetUserName()
{
#if !defined(_WIN32)
   return g_get_user_name();
#else
   wchar_t buffer[256] = { 0 };
   DWORD len = ARRAYSIZE(buffer);
   static char* user_name_utf8 = NULL;
   if (!user_name_utf8) {
      if (GetUserNameW(buffer, &len)) {
         user_name_utf8 = g_utf16_to_utf8(buffer, -1, NULL, NULL, NULL);
      }
   }
   return user_name_utf8;
#endif
}


/*
 *******************************************************************************
 * FileLoggerGetPath --                                                   */ /**
 *
 * Parses the given template file name and expands embedded variables, and
 * places the log index information at the right position.
 *
 * The following variables are expanded:
 *
 *    - ${USER}:  user's login name.
 *    - ${PID}:   current process's pid.
 *    - ${IDX}:   index of the log file (for rotation).
 *
 * @param[in] data         Log handler data.
 * @param[in] index        Index of the log file.
 *
 * @return The expanded log file path.
 *
 ******************************************************************************
 */

static gchar *
FileLoggerGetPath(FileLogger *data,
                  gint index)
{
   gboolean hasIndex = FALSE;
   gchar indexStr[11];
   gchar *logpath;
   gchar *vars[] = {
      "${USER}",  NULL,
      "${PID}",   NULL,
      "${IDX}",   indexStr,
   };
   gchar *tmp;
   size_t i;

   logpath = g_strdup(data->path);
   vars[1] = (char *) GlibGetUserName();
   vars[3] = g_strdup_printf("%u", (unsigned int) getpid());
   g_snprintf(indexStr, sizeof indexStr, "%d", index);

   for (i = 0; i < G_N_ELEMENTS(vars); i += 2) {
      char *last = logpath;
      char *start;
      while ((start = strstr(last, vars[i])) != NULL) {
         gchar *tmp;
         char *end = start + strlen(vars[i]);
         size_t offset = (start - last) + strlen(vars[i+1]);

         *start = '\0';
         tmp = g_strdup_printf("%s%s%s", logpath, vars[i+1], end);
         g_free(logpath);
         logpath = tmp;
         last = logpath + offset;

         /* XXX: ugly, but well... */
         if (i == 4) {
            hasIndex = TRUE;
         }
      }
   }

   g_free(vars[3]);

   /*
    * Always make sure we add the index if it's not 0, since that's used for
    * backing up old log files.
    */
   if (index != 0 && !hasIndex) {
      char *sep = strrchr(logpath, '.');
      char *pathsep = strrchr(logpath, '/');

      if (pathsep == NULL) {
         pathsep = strrchr(logpath, '\\');
      }

      if (sep != NULL && sep > pathsep) {
         *sep = '\0';
         sep++;
         tmp = g_strdup_printf("%s.%d.%s", logpath, index, sep);
      } else {
         tmp = g_strdup_printf("%s.%d", logpath, index);
      }
      g_free(logpath);
      logpath = tmp;
   }

   return logpath;
}


/*
 *******************************************************************************
 * FileLoggerOpen --                                                      */ /**
 *
 * Opens a log file for writing, backing up the existing log file if one is
 * present. Only one old log file is preserved.
 *
 * @note Make sure this function is called with the write lock held.
 *
 * @param[in] data   Log handler data.
 *
 * @return Log file pointer (NULL on error).
 *
 *******************************************************************************
 */

static GIOChannel *
FileLoggerOpen(FileLogger *data)
{
   GIOChannel *logfile = NULL;
   gchar *path;

   g_return_val_if_fail(data != NULL, NULL);
   path = FileLoggerGetPath(data, 0);

   if (g_file_test(path, G_FILE_TEST_EXISTS)) {
      /* GStatBuf was added in 2.26. */
#if GLIB_CHECK_VERSION(2, 26, 0)
      GStatBuf fstats;
#else
      struct stat fstats;
#endif

      /*
       * In order to determine whether we should rotate the logs,
       * we are calling the system call stat() to get the existing log file
       * size.
       * The time of check vs. time of use issue does not apply to this use
       * case, as even the file size is increasing, it will not affect the log
       * rotation decision. So Suppress the fs_check_call coverity warning.
       */
      /* coverity[fs_check_call] */
      if (g_stat(path, &fstats) > -1) {
         data->logSize = (gint) fstats.st_size;
      }

      if (!data->append || data->logSize >= data->maxSize) {
         /*
          * Find the last log file and iterate back, changing the indices as we go,
          * so that the oldest log file has the highest index (the new log file
          * will always be index "0"). When not rotating, "maxFiles" is 1, so we
          * always keep one backup.
          */
         guint id;
         GPtrArray *logfiles = g_ptr_array_new();

         /*
          * Find the id of the last log file. The pointer array will hold
          * the names of all existing log files + the name of the last log
          * file, which may or may not exist.
          */
         for (id = 0; id < data->maxFiles; id++) {
            gchar *log = FileLoggerGetPath(data, id);

            g_ptr_array_add(logfiles, log);
            if (!g_file_test(log, G_FILE_TEST_IS_REGULAR)) {
               break;
            }
         }

         /* Rename the existing log files, increasing their index by 1. */
         for (id = logfiles->len - 1; id > 0; id--) {
            gchar *dest = g_ptr_array_index(logfiles, id);
            gchar *src = g_ptr_array_index(logfiles, id - 1);

            if (!g_file_test(dest, G_FILE_TEST_IS_DIR) &&
                (!g_file_test(dest, G_FILE_TEST_EXISTS) ||
                 g_unlink(dest) == 0)) {
               /*
                * We should ignore an unlikely rename() system call failure,
                * as we should keep our service running with non-critical errors.
                * We cannot log the error because we are already in the log
                * handler context to avoid crash or recursive logging loop.
                */
               /* coverity[check_return] */
               g_rename(src, dest);
            } else {
               g_unlink(src);
            }
         }

         /* Cleanup. */
         for (id = 0; id < logfiles->len; id++) {
            g_free(g_ptr_array_index(logfiles, id));
         }
         g_ptr_array_free(logfiles, TRUE);
         data->logSize = 0;
         data->append = FALSE;
      }
   }

   logfile = g_io_channel_new_file(path, data->append ? "a" : "w", NULL);

   if (logfile != NULL) {
      g_io_channel_set_encoding(logfile, NULL, NULL);
#ifdef VMX86_TOOLS
      /*
       * Make the logfile readable only by user and root/administrator.
       * Can't do anything if it fails, so ignore return.
       */
#ifdef _WIN32
      (void) Win32Access_SetFileOwnerRW(path);
#else
      /* coverity[toctou] */
      (void) chmod(path, 0600);
#endif
#endif // VMX86_TOOLS
   }
   g_free(path);

   return logfile;
}


/*
 *******************************************************************************
 * FileLoggerLog --                                                       */ /**
 *
 * Logs a message to the configured destination file. Also opens the file for
 * writing if it hasn't been done yet.
 *
 * @param[in] domain    Log domain.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 * @param[in] data      File logger.
 *
 *******************************************************************************
 */

static void
FileLoggerLog(const gchar *domain,
              GLogLevelFlags level,
              const gchar *message,
              gpointer data)
{
   FileLogger *logger = data;
   gsize written;

   g_mutex_lock(&logger->lock);

   if (logger->error) {
      goto exit;
   }

   if (logger->file == NULL) {
      if (logger->file == NULL) {
         logger->file = FileLoggerOpen(data);
      }
      if (logger->file == NULL) {
         logger->error = TRUE;
         goto exit;
      }
   }

   if (!FileLoggerIsValid(logger)) {
      logger->error = TRUE;
      goto exit;
   }

   /* Write the log file and do log rotation accounting. */
   if (g_io_channel_write_chars(logger->file, message, -1, &written, NULL) ==
       G_IO_STATUS_NORMAL) {
      if (logger->maxSize > 0) {
         logger->logSize += (gint) written;
         if (logger->logSize >= logger->maxSize) {
            g_io_channel_unref(logger->file);
            logger->append = FALSE;
            logger->file = FileLoggerOpen(logger);
            logger->handler.logHeader = TRUE;
         } else {
            g_io_channel_flush(logger->file, NULL);
         }
      } else {
         g_io_channel_flush(logger->file, NULL);
      }
   }

exit:
   g_mutex_unlock(&logger->lock);
}


/*
 ******************************************************************************
 * FileLoggerDestroy --                                               */ /**
 *
 * Cleans up the internal state of a file logger.
 *
 * @param[in] _data     File logger data.
 *
 ******************************************************************************
 */

static void
FileLoggerDestroy(gpointer data)
{
   FileLogger *logger = data;
   if (logger->file != NULL) {
      g_io_channel_unref(logger->file);
   }
   g_mutex_clear(&logger->lock);
   g_free(logger->path);
   g_free(logger);
}


/*
 *******************************************************************************
 * GlibUtils_CreateFileLogger --                                          */ /**
 *
 * @brief Creates a new file logger based on the given configuration.
 *
 * @param[in] path      Path to log file.
 * @param[in] append    Whether to append to existing log file.
 * @param[in] maxSize   Maximum log file size (in MB, 0 = no limit).
 * @param[in] maxFiles  Maximum number of old files to be kept.
 *
 * @return A new logger, or NULL on error.
 *
 *******************************************************************************
 */

GlibLogger *
GlibUtils_CreateFileLogger(const char *path,
                           gboolean append,
                           guint maxSize,
                           guint maxFiles)
{
   FileLogger *data = NULL;

   g_return_val_if_fail(path != NULL, NULL);

   data = g_new0(FileLogger, 1);
   data->handler.addsTimestamp = FALSE;
   data->handler.shared = FALSE;
   data->handler.logfn = FileLoggerLog;
   data->handler.dtor = FileLoggerDestroy;
   data->handler.logHeader = TRUE;

   data->path = g_filename_from_utf8(path, -1, NULL, NULL, NULL);
   if (data->path == NULL) {
      g_free(data);
      return NULL;
   }

   data->append = append;
   data->maxSize = maxSize * 1024 * 1024;
   data->maxFiles = maxFiles + 1; /* To account for the active log file. */
   g_mutex_init(&data->lock);

   return &data->handler;
}

 07070100000058000081A400000000000000000000000168225505000019EB000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/glibUtils/stdLogger.c  /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file stdLogger.c
 *
 * A very simplified version of a file logger that uses the standard output
 * streams (stdout / stderr).
 */

#include "glibUtils.h"
#include <stdio.h>

#if defined(_WIN32)
static GMutex gConsoleLock;
static gint gRefCount = 0;
#endif

typedef struct StdLogger {
   GlibLogger  handler;
#if defined(_WIN32)
   gboolean    attached;
#endif
} StdLogger;


/*
 *******************************************************************************
 * StdLoggerLog --                                                        */ /**
 *
 * Logs a message to stdout or stderr depending on its severity.
 *
 * @param[in] domain    Unused.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 * @param[in] data      Logger data.
 *
 *******************************************************************************
 */

static void
StdLoggerLog(const gchar *domain,
             GLogLevelFlags level,
             const gchar *message,
             gpointer data)
{
   gchar *local;
   FILE *dest = (level < G_LOG_LEVEL_MESSAGE) ? stderr : stdout;

#if defined(_WIN32)
   StdLogger *sdata = data;

   if (!sdata->attached) {
      g_mutex_lock(&gConsoleLock);
      if (gRefCount != 0 || GlibUtils_AttachConsole()) {
         gRefCount++;
         sdata->attached = TRUE;
      }
      g_mutex_unlock(&gConsoleLock);
   }

   if (!sdata->attached) {
      return;
   }
#endif

   local = g_locale_from_utf8(message, -1, NULL, NULL, NULL);
   if (local != NULL) {
      fputs(local, dest);
      g_free(local);
   } else {
      fputs(message, dest);
   }
}


/*
 *******************************************************************************
 * StdLoggerDestroy --                                                    */ /**
 *
 * Cleans up the internal state of the logger.
 *
 * @param[in] data   Logger data.
 *
 *******************************************************************************
 */

static void
StdLoggerDestroy(gpointer data)
{
#if defined(_WIN32)
   StdLogger *sdata = data;
   g_mutex_lock(&gConsoleLock);
   if (sdata->attached && --gRefCount == 0) {
      FreeConsole();
   }
   g_mutex_unlock(&gConsoleLock);
#endif
   g_free(data);
}


#if defined(_WIN32)
/*
 *******************************************************************************
 * GlibUtilsIsRedirected --                                               */ /**
 *
 * Checks whether given standard device (standard input, standard output,
 * or standard error) has been redirected to an on-disk file/pipe.
 * Win32-only.
 *
 * @param[in] nStdHandle          The standard device number.
 *
 * @return TRUE if device redirected to a file/pipe.
 *
 *******************************************************************************
 */

static gboolean
GlibUtilsIsRedirected(DWORD nStdHandle)
{
   HANDLE handle = GetStdHandle(nStdHandle);
   DWORD type = handle ? GetFileType(handle) : FILE_TYPE_UNKNOWN;

   return type == FILE_TYPE_DISK || type == FILE_TYPE_PIPE;
}


/*
 *******************************************************************************
 * GlibUtils_AttachConsole --                                             */ /**
 *
 * Attaches a console to the current process. If the parent process already has
 * a console open, reuse it. Otherwise, create a new console for the current
 * process. Win32-only.
 *
 * It's safe to call this function multiple times (it won't do anything if
 * the process already has a console).
 *
 * @note Attaching to the parent process's console is only available on XP and
 * later.
 *
 * @return Whether the process is attached to a console.
 *
 *******************************************************************************
 */

gboolean
GlibUtils_AttachConsole(void)
{
   typedef BOOL (WINAPI *AttachConsoleFn)(DWORD);
   gboolean ret = TRUE;
   AttachConsoleFn _AttachConsole;
   BOOL reopenStdout;
   BOOL reopenStderr;

   if (GetConsoleWindow() != NULL) {
      goto exit;
   }

   reopenStdout = !GlibUtilsIsRedirected(STD_OUTPUT_HANDLE);
   reopenStderr = !GlibUtilsIsRedirected(STD_ERROR_HANDLE);
   if (!reopenStdout && !reopenStderr) {
      goto exit;
   }

   _AttachConsole = (AttachConsoleFn) GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
                                                     "AttachConsole");
   if ((_AttachConsole != NULL && _AttachConsole(ATTACH_PARENT_PROCESS)) ||
       AllocConsole()) {
      FILE *fptr;

      if (reopenStdout) {
         fptr = _wfreopen(L"CONOUT$", L"a", stdout);
         if (fptr == NULL) {
            g_warning("_wfreopen failed for stdout/CONOUT$: %d (%s)",
                      errno, strerror(errno));
            ret = FALSE;
         }
      }

      if (reopenStderr) {
         fptr = _wfreopen(L"CONOUT$", L"a", stderr);
         if (fptr == NULL) {
            g_warning("_wfreopen failed for stderr/CONOUT$: %d (%s)",
                      errno, strerror(errno));
            ret = FALSE;
         } else {
            setvbuf(fptr, NULL, _IONBF, 0);
         }
      }
   } else {
      ret = FALSE;
   }

exit:
   if (!ret) {
      g_warning("Console redirection unavailable.");
   }
   return ret;
}
#endif


/*
 *******************************************************************************
 * GlibUtils_CreateStdLogger --                                           */ /**
 *
 * @brief Configures a new std logger.
 *
 * @return A new logger instance.
 *
 *******************************************************************************
 */

GlibLogger *
GlibUtils_CreateStdLogger(void)
{
   StdLogger *data = g_new0(StdLogger, 1);
   data->handler.logfn = StdLoggerLog;
   data->handler.addsTimestamp = FALSE;
   data->handler.shared = FALSE;
   data->handler.dtor = StdLoggerDestroy;
   return &data->handler;
}

 07070100000059000081A400000000000000000000000168225505000017A3000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/glibUtils/sysLogger.c  /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file sysLogger.c
 *
 * Logger that writes to syslog(3). Since there's only one "syslog connection"
 * for the whole application, this code does reference counting to allow
 * different domains to be configured with a "syslog" handler, and still be
 * able to call closelog(3) when appropriate.
 */

#include "glibUtils.h"
#include <stdio.h>
#include <string.h>
#include <syslog.h>

typedef struct SysLogger {
   GlibLogger  handler;
   gchar      *domain;
   gint        refcount;
} SysLogger;


static SysLogger *gSysLogger;
static GMutex gSysLoggerLock;


/*
 *******************************************************************************
 * SysLoggerLog --                                                        */ /**
 *
 * @brief Sends the given log message to syslog.
 *
 * @param[in] domain     Unused.
 * @param[in] level      Log level.
 * @param[in] message    Message to log.
 * @param[in] data       Unused.
 *
 ******************************************************************************
 */

static void
SysLoggerLog(const gchar *domain,
             GLogLevelFlags level,
             const gchar *message,
             gpointer data)
{
   gchar *local;
   int priority;

   /* glib and syslog disagree about critical / error. */
   if (level & G_LOG_LEVEL_ERROR) {
      priority = LOG_CRIT;
   } else if (level & G_LOG_LEVEL_CRITICAL) {
      priority = LOG_ERR;
   } else if (level & G_LOG_LEVEL_WARNING) {
      priority = LOG_WARNING;
   } else if (level & G_LOG_LEVEL_MESSAGE) {
      priority = LOG_NOTICE;
   } else if (level & G_LOG_LEVEL_INFO) {
      priority = LOG_INFO;
   } else {
      priority = LOG_DEBUG;
   }

   local = g_locale_from_utf8(message, -1, NULL, NULL, NULL);
   if (local != NULL) {
      syslog(priority, "%s", local);
      g_free(local);
   } else {
      syslog(priority, "%s", message);
   }
}


/*
 *******************************************************************************
 * SysLoggerUnref --                                                      */ /**
 *
 * @brief Decreases the ref count and closes syslog if it reaches 0.
 *
 * @param[in] data   Unused.
 *
 *******************************************************************************
 */

static void
SysLoggerUnref(gpointer data)
{
   g_return_if_fail(data == gSysLogger);
   g_return_if_fail(gSysLogger->refcount > 0);
   g_mutex_lock(&gSysLoggerLock);
   gSysLogger->refcount -= 1;
   if (gSysLogger->refcount == 0) {
      closelog();
      g_free(gSysLogger->domain);
      g_free(gSysLogger);
      gSysLogger = NULL;
   }
   g_mutex_unlock(&gSysLoggerLock);
}


/*
 *******************************************************************************
 * GlibUtils_CreateSysLogger --                                           */ /**
 *
 * @brief Initializes syslog if it hasn't been done yet.
 *
 * Since syslog is shared, it's not recommended to change the default domain
 * during the lifetime of the application, since that may not reflect on the
 * syslogs (and, when it does, it might be confusing).
 *
 * @param[in] domain    Application name, used as the syslog identity.
 * @param[in] facility  Facility to use. One of: "daemon", "local[0-7]",
                        "user" (default).
 *
 * @return Syslog logger data.
 *
 *******************************************************************************
 */

GlibLogger *
GlibUtils_CreateSysLogger(const char *domain,
                          const char *facility)
{
   g_mutex_lock(&gSysLoggerLock);
   if (gSysLogger == NULL) {
      int facid = LOG_USER;

      if (facility != NULL) {
         int idx;
         if (strcmp(facility, "daemon") == 0) {
            facid = LOG_DAEMON;
         } else if (sscanf(facility, "local%d", &idx) == 1) {
            switch (idx) {
            case 0:
               facid = LOG_LOCAL0;
               break;

            case 1:
               facid = LOG_LOCAL1;
               break;

            case 2:
               facid = LOG_LOCAL2;
               break;

            case 3:
               facid = LOG_LOCAL3;
               break;

            case 4:
               facid = LOG_LOCAL4;
               break;

            case 5:
               facid = LOG_LOCAL5;
               break;

            case 6:
               facid = LOG_LOCAL6;
               break;

            case 7:
               facid = LOG_LOCAL7;
               break;

            default:
               g_message("Invalid local facility for %s: %s\n", domain, facility);
               break;
            }
         } else if (strcmp(facility, "user") != 0) {
            g_message("Invalid syslog facility for %s: %s\n", domain, facility);
         }
      }

      gSysLogger = g_new0(SysLogger, 1);
      gSysLogger->handler.addsTimestamp = TRUE;
      gSysLogger->handler.shared = FALSE;
      gSysLogger->handler.logfn = SysLoggerLog;
      gSysLogger->handler.dtor = SysLoggerUnref;

      gSysLogger->domain = g_strdup(domain);
      gSysLogger->refcount = 1;
      openlog(gSysLogger->domain, LOG_CONS | LOG_PID, facid);
   } else {
      gSysLogger->refcount += 1;
   }
   g_mutex_unlock(&gSysLoggerLock);
   return &gSysLogger->handler;
}

 0707010000005A000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/globalConfig   0707010000005B000081A4000000000000000000000001682255050000043B000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/globalConfig/Makefile.am   ################################################################################
### Copyright (c) 2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libGlobalConfig.la

libGlobalConfig_la_SOURCES =
libGlobalConfig_la_SOURCES += globalConfig.c

libGlobalConfig_la_CPPFLAGS =
libGlobalConfig_la_CPPFLAGS += @GLIB2_CPPFLAGS@
 0707010000005C000081A4000000000000000000000001682255050000772E000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/globalConfig/globalConfig.c    /*********************************************************
 * Copyright (c) 2020-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file globalConfig.c
 *
 *    Implementation of the module that downloads the configuration from the
 *    GuestStore.
 */

/* Need this header in the beginning for GLOBALCONFIG_SUPPORTED definition. */
#include "globalConfig.h"

#if !defined(GLOBALCONFIG_SUPPORTED)
#   error This file should not be compiled
#endif

#include <errno.h>
#include "conf.h"
#include "file.h"
#include "guestApp.h"
#include <string.h>
#include <glib/gstdio.h>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include "str.h"
#include "vmware/tools/guestStore.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/threadPool.h"
#include "guestStoreClient.h"

/**
 * Default value for CONFNAME_GLOBALCONF_ENABLED setting in tools.conf.
 *
 * TRUE will enable the module. GlobalConf module is disabled by default.
 */
#define GLOBALCONF_DEFAULT_ENABLED FALSE

/**
 * Default value for CONFNAME_GLOBALCONF_POLL_INTERVAL setting in tools.conf.
 */
#define GLOBALCONF_DEFAULT_POLL_INTERVAL (60 * 60)

/**
 * Minimum poll interval for fetching the global configuration from GuestStore.
 */
#ifdef VMX86_DEBUG
#define GLOBALCONF_MIN_POLL_INTERVAL (2 * 60)
#else
#define GLOBALCONF_MIN_POLL_INTERVAL (30 * 60)
#endif

/**
 * Default value for CONFNAME_GLOBALCONF_RESOURCE setting in tools.conf.
 */
#if defined(_WIN32)
#define GLOBALCONF_DEFAULT_RESOURCE \
        "/vmware/configurations/vmtools/windows/tools.conf"
#else
#define GLOBALCONF_DEFAULT_RESOURCE \
        "/vmware/configurations/vmtools/linux/tools.conf"
#endif

/**
 * Name of the local file populated with the global tools configuration.
 */
#define GLOBALCONF_LOCAL_FILENAME "tools-global.conf"

/**
 * Name of the local temp file populated with the global tools configuration.
 */
#define GLOBALCONF_LOCAL_TEMP_FILENAME "temp-global.conf"

/**
 * Macro to get the resource path from the config dictionary.
 */
#define GLOBALCONF_GET_RESOURCE_PATH(cfg) \
           VMTools_ConfigGetString(cfg, CONFGROUPNAME_GLOBALCONF, \
                                   CONFNAME_GLOBALCONF_RESOURCE, \
                                   GLOBALCONF_DEFAULT_RESOURCE)


typedef struct GlobalConfigInfo {
   gchar *localConfPath;
   gchar *localTempPath;
   gchar *guestStoreResource;
   guint pollInterval;
} GlobalConfigInfo;

typedef struct GlobalConfigThreadState {
   /*
    * 'mutex' protect concurrent accesses to terminate, cond, guestStoreEnabled
    * and useRandomInterval.
    */
   GMutex mutex;
   GCond cond;
   gboolean terminate;
   gboolean guestStoreEnabled;
   gboolean useRandomInterval;
   GlobalConfigInfo *configInfo;
} GlobalConfigThreadState;

static GlobalConfigThreadState globalConfigThreadState;


/*
 **************************************************************************
 * GuestStoreStateChanged --                                         */ /**
 *
 * GuestStore access state has changed, now update the download thread
 * accordingly.
 *
 * @param[in] src               Unused
 * @param[in] guestStoreEnabled GuestStore state enabled if TRUE
 * @param[in] unused            Unused
 *
 **************************************************************************
 */

static void
GuestStoreStateChanged(gpointer src,
                       gboolean guestStoreEnabled,
                       gpointer unused)
{
   GlobalConfigThreadState *threadState = &globalConfigThreadState;

   g_mutex_lock(&threadState->mutex);

   g_debug("%s: GuestStore old state: %d and new state: %d\n",
           __FUNCTION__, threadState->guestStoreEnabled, guestStoreEnabled);

   if (threadState->guestStoreEnabled != guestStoreEnabled) {
      threadState->guestStoreEnabled = guestStoreEnabled;
      g_debug("%s: Signalling the change in the GuestStore state.\n",
              __FUNCTION__);
      g_cond_signal(&threadState->cond);
   }

   g_mutex_unlock(&threadState->mutex);
}


/*
 **************************************************************************
 * GlobalConfigGetPollInterval --                                    */ /**
 *
 * Parses the configuration and returns the poll-interval.
 *
 * @param[in] cfg   VMTools configuration dictionary.
 *
 * @return unsigned integer representing the poll interval.
 *         0 if the globalconfig module is disabled.
 *         GLOBALCONF_DEFAULT_POLL_INTERVAL if an invalid value is specified.
 *
 **************************************************************************
 */

static guint
GlobalConfigGetPollInterval(GKeyFile *cfg)
{
   gint pollInterval = 0;
   gboolean enabled = GlobalConfig_GetEnabled(cfg);

   if (!enabled) {
      g_info("%s: global config module is disabled.", __FUNCTION__);
      return pollInterval;
   }

   pollInterval = VMTools_ConfigGetInteger(cfg, CONFGROUPNAME_GLOBALCONF,
                                           CONFNAME_GLOBALCONF_POLL_INTERVAL,
                                           GLOBALCONF_DEFAULT_POLL_INTERVAL);
   if (pollInterval < GLOBALCONF_MIN_POLL_INTERVAL) {
      g_warning("%s: Invalid value %d specified for '%s'. Using default %us",
                __FUNCTION__, pollInterval, CONFNAME_GLOBALCONF_POLL_INTERVAL,
                GLOBALCONF_DEFAULT_POLL_INTERVAL);
      pollInterval = GLOBALCONF_DEFAULT_POLL_INTERVAL;
   }

   return pollInterval;
}


/*
 **************************************************************************
 * GenerateRandomInterval --                                         */ /**
 *
 * Generate a random value between MIN_RAND_WAIT_INTERVAL and
 * MAX_RAND_WAIT_INTERVAL.
 *
 * @return a random generated unsigned integer value.
 *
 **************************************************************************
 */

static guint
GenerateRandomInterval(void)
{
   GRand *gRand = g_rand_new();

   /*
    * The following min and max values are taken randomly.
    */
#define MIN_RAND_WAIT_INTERVAL 30
#define MAX_RAND_WAIT_INTERVAL 300

   guint randomInterval = g_rand_int_range(gRand,
                                           MIN_RAND_WAIT_INTERVAL,
                                           MAX_RAND_WAIT_INTERVAL);
   g_rand_free(gRand);

#undef MIN_RAND_WAIT_INTERVAL
#undef MAX_RAND_WAIT_INTERVAL

   g_info("%s: Using random interval: %u.\n", __FUNCTION__, randomInterval);

   return randomInterval;
}


/*
 **************************************************************************
 * VMToolsChannelReset --                                            */ /**
 *
 * Callback function that gets called when the VMTools channel gets reset.
 *
 * @param[in] src       Unused
 * @param[in] ctx       Unused
 * @param[in] unused    Unused
 *
 **************************************************************************
 */

static void
VMToolsChannelReset(gpointer src,
                    ToolsAppCtx *ctx,
                    gpointer unused)
{
   GlobalConfigThreadState *threadState = &globalConfigThreadState;

   g_debug("%s: VMTools channel got reset.\n", __FUNCTION__);

   g_mutex_lock(&threadState->mutex);

   if (threadState->configInfo->pollInterval > 0) {
      /*
       * The RPC channel may get reset due to various conditions like
       * snapshotting the VM, vmotion the VM, instant cloning of the VM.
       * In order to avoid potential load spikes in case of instant clones,
       * wait for a randomized interval.
       */
      threadState->useRandomInterval = TRUE;
   }

   g_mutex_unlock(&threadState->mutex);
}


/*
 **************************************************************************
 * GlobalConfigToolsConfReload --                                    */ /**
 *
 * Callback function that gets called when the VMTools configuration gets
 * reloaded.
 *
 * @param[in] src       Unused
 * @param[in] ctx       Application Context
 * @param[in] unused    Unused
 *
 **************************************************************************
 */

static void
GlobalConfigToolsConfReload(gpointer src,
                            ToolsAppCtx *ctx,
                            gpointer unused)
{
   guint pollInterval;
   GlobalConfigThreadState *threadState = &globalConfigThreadState;
   gchar *newResourcePath;

   g_debug("%s: VMTools configuration got reloaded.\n", __FUNCTION__);

   g_mutex_lock(&threadState->mutex);

   newResourcePath = GLOBALCONF_GET_RESOURCE_PATH(ctx->config);
   if (g_strcmp0(newResourcePath,
                 threadState->configInfo->guestStoreResource)) {
      g_info("%s: '%s' changed. Old: %s, New: %s\n",
             __FUNCTION__, CONFNAME_GLOBALCONF_RESOURCE,
             threadState->configInfo->guestStoreResource,
             newResourcePath);
      g_free(threadState->configInfo->guestStoreResource);
      threadState->configInfo->guestStoreResource = newResourcePath;
   } else {
      g_free(newResourcePath);
      newResourcePath = NULL;
   }

   pollInterval = GlobalConfigGetPollInterval(ctx->config);
   if (pollInterval != threadState->configInfo->pollInterval) {
      g_info("%s: '%s' changed. Old: '%u', New: '%u' "
             "Signalling the change in the globalConfing configuration.\n",
             __FUNCTION__, CONFNAME_GLOBALCONF_POLL_INTERVAL,
             threadState->configInfo->pollInterval,
             pollInterval);
      threadState->configInfo->pollInterval = pollInterval;
      g_cond_signal(&threadState->cond);
   } else if (pollInterval == 0) {
      /*
       * Delete the stale config files (if any).
       */
      GlobalConfig_DeleteConfig();
   }

   g_mutex_unlock(&threadState->mutex);
}


/*
 **************************************************************************
 * GlobalConfGetConfPath --                                          */ /**
 *
 * Returns the path to the global configuration file.
 *
 * @return Path of the global configuration file. The caller shouldn't
 *         free the memory. Returns NULL if the path cannot be computed.
 *
 **************************************************************************
 */

static const gchar *
GlobalConfGetConfPath(void)
{
   static gchar *globalConfPath = NULL;

   if (globalConfPath == NULL) {
      char *guestAppConfPath = GuestApp_GetConfPath();
      if (guestAppConfPath == NULL) {
         g_warning("%s: Failed to get configuration directory.\n",
                   __FUNCTION__);
      } else {
         globalConfPath = g_build_filename(guestAppConfPath,
                                           GLOBALCONF_LOCAL_FILENAME,
                                           NULL);
      }
      free(guestAppConfPath);
   }

   return globalConfPath;
}


/*
 **************************************************************************
 * GlobalConfigThreadStateInit --                                    */ /**
 *
 * Reads the key/value pairs related to globalconf module from the user
 * specified configuration dictionary and intializes the globaconf module.
 *
 * @param[in] cfg    Tools configuration dictionary.
 *
 * @return TRUE if the globalconf module is initialized.
 *         FALSE if any error happened while initializing.
 *
 **************************************************************************
 */

static gboolean
GlobalConfigThreadStateInit(GKeyFile *cfg)
{
   gchar *guestAppConfPath;
   GlobalConfigInfo *configInfo;
   GlobalConfigThreadState *threadState = &globalConfigThreadState;

   ASSERT(cfg != NULL);

   guestAppConfPath = GuestApp_GetConfPath();
   if (guestAppConfPath == NULL) {
      g_warning("%s: Failed to get tools install path.\n", __FUNCTION__);
      return FALSE;
   }

   ASSERT(threadState->configInfo == NULL);

   configInfo = g_malloc0(sizeof(*configInfo));
   configInfo->localConfPath = g_build_filename(guestAppConfPath,
                                                GLOBALCONF_LOCAL_FILENAME,
                                                NULL);

   configInfo->localTempPath = g_build_filename(guestAppConfPath,
                                                GLOBALCONF_LOCAL_TEMP_FILENAME,
                                                NULL);

   vm_free(guestAppConfPath);

   configInfo->pollInterval = GlobalConfigGetPollInterval(cfg);

   g_debug("%s: %s: %d", __FUNCTION__, CONFNAME_GLOBALCONF_POLL_INTERVAL,
           configInfo->pollInterval);

   configInfo->guestStoreResource = GLOBALCONF_GET_RESOURCE_PATH(cfg);

   g_debug("%s: Configuration Resource path in GuestStore: %s",
           __FUNCTION__, configInfo->guestStoreResource);

   threadState->configInfo = configInfo;
   return TRUE;
}


/*
 **************************************************************************
 * GlobalConfigInfoFree --                                           */ /**
 *
 * Frees up memory allocated for GlobalConfigInfo structure.
 *
 * @param[in] data   GlobalConfigInfo structure that needs to be freed.
 *
 **************************************************************************
 */

static void
GlobalConfigInfoFree(GlobalConfigInfo *configInfo)
{
   if (configInfo != NULL) {
      g_free(configInfo->localConfPath);
      g_free(configInfo->localTempPath);
      g_free(configInfo->guestStoreResource);
   }

   g_free(configInfo);
}


/*
 **************************************************************************
 * GlobalConfigThreadStateFree --                                    */ /**
 *
 * Frees up memory allocated for GlobalConfigThread structure.
 *
 * @param[in] unused Callback data. Not used.
 *
 **************************************************************************
 */

static void
GlobalConfigThreadStateFree(gpointer unused)
{
   GlobalConfigThreadState *threadState = &globalConfigThreadState;

   GlobalConfigInfoFree(threadState->configInfo);
   threadState->configInfo = NULL;
}


/*
 **************************************************************************
 * GlobalConfigThreadTerminate --                                    */ /**
 *
 * Signals the 'global config' thread to exit.
 *
 * @param[in] ctx    Application context
 * @param[in] data   Pointer to GlobalConfigThreadState sturcture.
 *
 **************************************************************************
 */

static void
GlobalConfigThreadTerminate(ToolsAppCtx *ctx,
                            gpointer data)
{
   GlobalConfigThreadState *threadState = &globalConfigThreadState;

   g_mutex_lock(&threadState->mutex);

   threadState->terminate = TRUE;
   threadState->guestStoreEnabled = FALSE;

   g_cond_signal(&threadState->cond);

   g_mutex_unlock(&threadState->mutex);
}


/*
 **************************************************************************
 * LoadConfigFile --                                                 */ /**
 *
 * Loads the specified config file.
 *
 * @param[in] confPath Path to the configuration file.
 * @param[in|out] config  Configuration dictionary that is loaded with the
 *                        contents from the specified configuration file. When
 *                        loading, the old content is destroyed. Before
 *                        invoking this function the first time for a specific
 *                        confPath, the config must be initialized to NULL.
 * @param[in|out] mtime   Last known modification time of the config file.
 *                        When the function succeeds, will contain the new
 *                        modification time read from the file. If NULL (or 0),
 *                        the configuration dictionary is always loaded.
 *
 * @return TRUE Whether a new configuration dictionary was loaded.
 *
 **************************************************************************
 */

static gboolean
LoadConfigFile(const gchar *confPath,
               GKeyFile **config,
               time_t *mtime)
{
   gboolean configUpdated = FALSE;
   GStatBuf confStat;
   GError *err = NULL;
   GKeyFile *cfg;

   if (config == NULL || confPath == NULL ) {
      g_debug("%s: Invalid arguments specified.\n", __FUNCTION__);
      goto exit;
   }

  if (g_stat(confPath, &confStat) == -1) {
      /*
       * If the file doesn't exist, it's not an error.
       */
      if (errno != ENOENT) {
         g_warning("%s: Failed to stat conf file: %s, Error: '%s'\n",
                   __FUNCTION__, confPath, strerror(errno));
      } else {
         /*
          * If we used to have a file, set the config to NULL.
          */
         if (*config != NULL) {
            g_key_file_free(*config);
            *config = NULL;
            configUpdated = TRUE;
         }
      }
      goto exit;
   }

   /* Check if we really need to load the data. */
   if (mtime != NULL && confStat.st_mtime <= *mtime) {
      goto exit;
   }

   cfg = g_key_file_new();

   /* On error, 'err' will be set, null otherwise. */
   /* coverity[check_return] */
   g_key_file_load_from_file(cfg, confPath,
                             G_KEY_FILE_NONE, &err);
   if (err != NULL) {
      g_warning("%s: Failed to load the configuration from '%s'. Error: '%s'",
                __FUNCTION__, confPath, err->message);
      g_clear_error(&err);
      g_key_file_free(cfg);
      cfg = NULL;
      goto exit;
   }

   if (*config != NULL) {
      g_key_file_free(*config);
   }
   *config = cfg;
   configUpdated = TRUE;

   if (mtime != NULL) {
      *mtime = confStat.st_mtime;
   }

   g_debug("%s: Loaded the configuration from %s.\n", __FUNCTION__, confPath);

exit:
   return configUpdated;
}


/*
 **************************************************************************
 * DownloadConfig --                                                 */ /**
 *
 * Downloads the tools.conf from the GuestStore.
 *
 * @param[in] guestStoreResource Resource path in the GuestStore.
 * @param[in|opt] localTempPath  File path to be used for temporarily download
 *                               of the resource from the GuestStore. If this is
 *                               NULL, then a random file path is used.
 *
 * @return GuestStoreClientError.
 **************************************************************************
 */

static GuestStoreClientError
DownloadConfig(const char *guestStoreResource,
               const char *localTempPath)
{
   GuestStoreClientError status = GSLIBERR_GENERIC;
   const gchar *localConfPath = GlobalConfGetConfPath();
   gchar *randomLocalTempPath = NULL;

   if (localConfPath == NULL) {
      g_warning("%s: Failed to get the configuration file path.\n",
                __FUNCTION__);
      return status;
   }

   if (localTempPath == NULL) {
      int fd = File_MakeSafeTemp(NULL, &randomLocalTempPath);
      if (fd != -1) {
         close(fd);
      } else {
         g_warning("%s: Failed to get the random temporary file.\n",
                   __FUNCTION__);
         return status;
      }
      localTempPath = randomLocalTempPath;
   }

   g_debug("%s: Downloading the configuration to %s\n", __FUNCTION__,
           localTempPath);

   status = GuestStoreClient_GetContent(guestStoreResource,
                                        localTempPath,
                                        NULL, NULL);
   if (status == GSLIBERR_SUCCESS) {
      GKeyFile *newGlobalCfg = NULL;

      g_debug("%s: Successfully downloaded the configuration from GuestStore.",
              __FUNCTION__);

      LoadConfigFile(localTempPath, &newGlobalCfg, NULL);

      if (newGlobalCfg != NULL) {
         GKeyFile *existingGlobalCfg = NULL;

         LoadConfigFile(localConfPath, &existingGlobalCfg, NULL);
         if (!VMTools_CompareConfig(existingGlobalCfg, newGlobalCfg)) {
            /*
             * Write the config to the filesystem using VMTools_WriteConfig and
             * the content will be normalized.
             */
            VMTools_WriteConfig(localConfPath,
                                newGlobalCfg,
                                NULL);
         }

         if (existingGlobalCfg != NULL) {
            g_key_file_free(existingGlobalCfg);
         }

         g_key_file_free(newGlobalCfg);
      }
   } else {
      g_debug("%s: Failed to download the configuration "
              "from GuestStore. Error: %d",
              __FUNCTION__, status);
      /*
       * If the global configuration is not available in the GuestStore or
       * VM is not allowed to access it, then delete the local copy of global
       * configuration downloaded previously.
       */
      if (status == GSLIBERR_CONTENT_NOT_FOUND ||
          status == GSLIBERR_CONTENT_FORBIDDEN) {
         File_UnlinkIfExists(localConfPath);
      }
   }

   File_UnlinkIfExists(localTempPath);
   g_free(randomLocalTempPath);
   return status;
}


/*
 **************************************************************************
 * GlobalConfigThreadStart --                                        */ /**
 *
 * Entry function for the thread to download the global configuration from
 * the GuestStore.
 *
 * @param[in] ctx    Application context
 * @param[in] unused Callback data. Not used.
 *
 **************************************************************************
 */

static void
GlobalConfigThreadStart(ToolsAppCtx *ctx,
                        gpointer unused)
{
   GlobalConfigThreadState *threadState = &globalConfigThreadState;
   gboolean waitBeforeDownload = FALSE;

   g_mutex_lock(&threadState->mutex);

   while (!threadState->terminate) {
      GlobalConfigInfo *configInfo = threadState->configInfo;
      if (threadState->guestStoreEnabled && configInfo->pollInterval > 0) {
         gint64 endTime;

         if (waitBeforeDownload) {
            waitBeforeDownload = FALSE;
            endTime = g_get_monotonic_time() +
                     (configInfo->pollInterval * G_TIME_SPAN_SECOND);
            g_cond_wait_until(&threadState->cond, &threadState->mutex, endTime);
            continue;
         }  else if (threadState->useRandomInterval) {
            threadState->useRandomInterval = FALSE;
            endTime = g_get_monotonic_time() +
                      (GenerateRandomInterval() * G_TIME_SPAN_SECOND);
            g_cond_wait_until(&threadState->cond,
                              &threadState->mutex, endTime);
            continue;
         }

         g_mutex_unlock(&threadState->mutex);

         DownloadConfig(configInfo->guestStoreResource,
                        configInfo->localTempPath);

         g_mutex_lock(&threadState->mutex);

         waitBeforeDownload = TRUE;
      } else {
         if (configInfo->pollInterval == 0) {
            GlobalConfig_DeleteConfig();
         }
         g_cond_wait(&threadState->cond, &threadState->mutex);
         waitBeforeDownload = FALSE;
      }
   }

   g_mutex_unlock(&threadState->mutex);
}


/*
 **************************************************************************
 * GlobalConfig_Start --                                             */ /**
 *
 * Initializes the global config module. If the feature is not enabled in
 * tools.conf file, the module is not enabled. If this function is called
 * in the context of the Tools main service, a thread is started in the
 * background to periodically download the global configuration from the
 * GuestStore.
 *
 * @param[in] ctx                   Application context.
 *
 * @return TRUE if the download config module is successfully started
 *
 **************************************************************************
 */

gboolean
GlobalConfig_Start(ToolsAppCtx *ctx)
{
   gboolean ret = FALSE;
   GKeyFile *cfg;

   ASSERT(ctx != NULL);
   cfg = ctx->config;
   ASSERT(cfg != NULL);

   if (!GlobalConfigThreadStateInit(cfg)) {
      g_warning("%s: Failed to initialize global config module.",
                  __FUNCTION__);
      goto exit;
   }

   if (TOOLS_IS_MAIN_SERVICE(ctx)) {
      /*
       * Start the background thread only when this module is started by
       * 'vmsvc' service.
       */
      ret = ToolsCorePool_StartThread(ctx,
                                      "toolsGlobalConfig",
                                      GlobalConfigThreadStart,
                                      GlobalConfigThreadTerminate,
                                      NULL,
                                      GlobalConfigThreadStateFree);
      if (!ret) {
         g_info("%s: Unable to start the GuestStore download config thread",
                __FUNCTION__);
         GlobalConfigThreadStateFree(NULL);
         goto exit;
      }

      if (g_signal_lookup(TOOLS_CORE_SIG_GUESTSTORE_STATE,
                          G_OBJECT_TYPE(ctx->serviceObj)) != 0) {
         g_signal_connect(ctx->serviceObj,
                          TOOLS_CORE_SIG_GUESTSTORE_STATE,
                          G_CALLBACK(GuestStoreStateChanged),
                          NULL);
      }

      if (g_signal_lookup(TOOLS_CORE_SIG_RESET,
                          G_OBJECT_TYPE(ctx->serviceObj)) != 0) {
         g_signal_connect(ctx->serviceObj,
                          TOOLS_CORE_SIG_RESET,
                          G_CALLBACK(VMToolsChannelReset),
                          NULL);
      }
   } else {
      ret = TRUE;
   }

   if (g_signal_lookup(TOOLS_CORE_SIG_CONF_RELOAD,
                       G_OBJECT_TYPE(ctx->serviceObj)) != 0) {
      g_signal_connect(ctx->serviceObj,
                       TOOLS_CORE_SIG_CONF_RELOAD,
                       G_CALLBACK(GlobalConfigToolsConfReload),
                       NULL);
   }

exit:
   return ret;
}


/*
 **************************************************************************
 * GlobalConfig_LoadConfig --                                        */ /**
 *
 * Loads the Global configuration downloaded from the GuestStore. The
 * modification time of the configuration file is checked and it's loaded only
 * if it has been updated since the caller specified modification time.
 *
 * @param[in|out] config  Configuration dictionary that is loaded with the
 *                        contents downloaded from the GuestStore. When
 *                        loading, the old content is destroyed. The caller
 *                        must initialize this to NULL before the first
 *                        invocation of this function.
 * @param[in|out] mtime   Last known modification time of the config file.
 *                        When the function succeeds, will contain the new
 *                        modification time read from the file. If NULL (or 0),
 *                        the configuration dictionary is always loaded.
 *
 * @return TRUE Whether a new configuration dictionary was loaded.
 *
 **************************************************************************
 */

gboolean
GlobalConfig_LoadConfig(GKeyFile **config,
                        time_t *mtime)
{
   static const gchar *confPath = NULL;

   if (confPath == NULL) {
      confPath = GlobalConfGetConfPath();
   }

   if (confPath != NULL) {
      return LoadConfigFile(confPath, config, mtime);
   } else {
      return FALSE;
   }
}


/*
 **************************************************************************
 * GlobalConfig_GetEnabled --                                        */ /**
 *
 * Query the given configuration dictionary and returns the status of
 * globaconf module.
 *
 * @param[in] config  Configuration dictionary that needs to be queried.
 *
 * @return TRUE if the globalconf module is enabled. FALSE otherwise.
 *
 **************************************************************************
 */

gboolean
GlobalConfig_GetEnabled(GKeyFile *config)
{
   return VMTools_ConfigGetBoolean(config,
                                   CONFGROUPNAME_GLOBALCONF,
                                   CONFNAME_GLOBALCONF_ENABLED,
                                   GLOBALCONF_DEFAULT_ENABLED);
}


/*
 **************************************************************************
 * GlobalConfig_SetEnabled --                                        */ /**
 *
 * Changes the 'enabled' status of globalconf module in the specified
 * configuration dictionary.
 *
 * @param[in]     enabled Desired state of the globalconf module.
 * @param[in|out] config  Configuration dictionary that needs to be updated.
 *
 **************************************************************************
 */

void
GlobalConfig_SetEnabled(gboolean enabled,
                        GKeyFile *config)
{
   if (config != NULL) {
      g_key_file_set_boolean(config, CONFGROUPNAME_GLOBALCONF,
                             CONFNAME_GLOBALCONF_ENABLED, enabled);
   }
}


/*
 **************************************************************************
 * GlobalConfig_DeleteConfig --                                      */ /**
 *
 * Delete the global configuration downloaded from the GuestStore.
 *
 * @return TRUE if the global configuration is successfully deleted.
 *         FALSE if any error happens.
 *
 **************************************************************************
 */

gboolean
GlobalConfig_DeleteConfig(void) {
   const gchar *confPath = GlobalConfGetConfPath();

   return confPath != NULL && File_UnlinkIfExists(confPath) == 0;
}


/*
 **************************************************************************
 * GlobalConfig_DownloadConfig --                                    */ /**
 *
 * Download the global configuration from the GuestStore.
 *
 * @param[in] config  Configuration dictionary.
 *
 * @return GuestStoreClientError
 *
 **************************************************************************
 */

GuestStoreClientError
GlobalConfig_DownloadConfig(GKeyFile *config)
{
   GuestStoreClientError status = GSLIBERR_GENERIC;
   char *guestStoreResource;

   if (config == NULL) {
      g_warning("%s: Invalid arguments specified.\n", __FUNCTION__);
      return status;
   }

   guestStoreResource = GLOBALCONF_GET_RESOURCE_PATH(config);

   status = DownloadConfig(guestStoreResource, NULL);

   g_free(guestStoreResource);

   return status;
}
  0707010000005D000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/lib/guestApp   0707010000005E000081A400000000000000000000000168225505000003E3000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/guestApp/Makefile.am   ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################


noinst_LTLIBRARIES = libGuestApp.la

libGuestApp_la_SOURCES =
libGuestApp_la_SOURCES += guestApp.c

 0707010000005F000081A40000000000000000000000016822550500005123000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/guestApp/guestApp.c    /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * guestApp.c --
 *
 *    Utility functions common to all guest applications
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef _MSC_VER
#include <windows.h>
#include <shlobj.h>
#include "productState.h"
#include "conf.h" // for tools install path regkey
#include "winregistry.h"
#include "windowsUtil.h"
#endif

#include "vmware.h"
#include "vm_version.h"
#include "vm_tools_version.h"
#include "guestApp.h"
#include "backdoor.h"
#include "backdoor_def.h"
#include "conf.h"
#include "rpcout.h"
#include "debug.h"
#include "strutil.h"
#include "str.h"
#include "msg.h"
#include "file.h"
#include "posix.h"
#include "vmware/guestrpc/tclodefs.h"

/*
 * For Netware/Linux/BSD/Solaris, the install path
 * is the hardcoded value below. For Windows, it is
 * determined dynamically in GuestApp_GetInstallPath(),
 * so the empty string here is just for completeness.
 */

#if defined _WIN32
#   define GUESTAPP_TOOLS_INSTALL_PATH ""
#elif defined __APPLE__
#   define GUESTAPP_TOOLS_INSTALL_PATH "/Library/Application Support/VMware Tools"
#else
#   define GUESTAPP_TOOLS_INSTALL_PATH "/etc/vmware-tools"
#endif

#if defined _WIN32
static char *GuestAppGetOrCreateConfPath(void);
static DWORD GuestAppCreateConfPathSecInfo(PSECURITY_ATTRIBUTES *confPathSecAttrs,
                                           PACL *confPathAcl);
static VOID GuestAppDestroyConfPathSecInfo(PSECURITY_ATTRIBUTES confPathSecAttrs,
                                           PACL confPathAcl);
static DWORD GuestAppCreateConfPathSD(PSECURITY_DESCRIPTOR *confPathSD,
                                      PACL *confPathAcl);
static VOID GuestAppDestroyConfPathSD(PSECURITY_DESCRIPTOR confPathSd,
                                      PACL confPathAcl);
static DWORD GuestAppCreateConfPathAcl(PACL *newAcl);
static VOID GuestAppDestroyConfPathAcl(PACL confPathAcl);
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * GuestApp_GetDefaultScript --
 *
 *    Returns the default power script for the given configuration option.
 *
 * Results:
 *    Script name on success, NULL of the option is not recognized.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

const char *
GuestApp_GetDefaultScript(const char *confName) // IN
{
   const char *value = NULL;
   if (strcmp(confName, CONFNAME_SUSPENDSCRIPT) == 0) {
      value = CONFVAL_SUSPENDSCRIPT_DEFAULT;
   } else if (strcmp(confName, CONFNAME_RESUMESCRIPT) == 0) {
      value = CONFVAL_RESUMESCRIPT_DEFAULT;
   } else if (strcmp(confName, CONFNAME_POWEROFFSCRIPT) == 0) {
      value = CONFVAL_POWEROFFSCRIPT_DEFAULT;
   } else if (strcmp(confName, CONFNAME_POWERONSCRIPT) == 0) {
      value = CONFVAL_POWERONSCRIPT_DEFAULT;
   }
   return value;
}

#if defined _WIN32

/*
 *------------------------------------------------------------------------------
 *
 * GuestApp_GetInstallPathW --
 *
 *    Returns the tools installation path as a UTF-16 encoded string, or NULL on
 *    error. The caller must deallocate the returned string using free.
 *
 * Results:
 *    See above.
 *
 * Side effects:
 *    None.
 *------------------------------------------------------------------------------
 */

LPWSTR
GuestApp_GetInstallPathW(void)
{
   HKEY   key    = NULL;
   LONG   rc;
   DWORD  cbData = 0;
   DWORD  temp   = 0;
   PWCHAR data   = NULL;
   REGSAM samDesired = KEY_READ;

   rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, CONF_VMWARE_TOOLS_REGKEY_W, 0,
                      samDesired, &key);
   if (ERROR_SUCCESS != rc) {
      Debug("%s: Couldn't open key \"%S\".\n", __FUNCTION__,
            CONF_VMWARE_TOOLS_REGKEY_W);
      Debug("%s: RegOpenKeyExW error 0x%x.\n", __FUNCTION__, GetLastError());
      goto exit;
   }

   rc = RegQueryValueExW(key, CONF_VMWARE_TOOLS_INSTPATH_KEY_W, 0, NULL, NULL,
                         &cbData);
   if (ERROR_SUCCESS != rc) {
      Debug("%s: Couldn't get length of value \"%S\".\n", __FUNCTION__,
            CONF_VMWARE_TOOLS_INSTPATH_KEY_W);
      Debug("%s: RegQueryValueExW error 0x%x.\n", __FUNCTION__, GetLastError());
      goto exit;
   }

   /*
    * The data in the registry may not be null terminated. So allocate enough
    * space for one extra WCHAR and use that space to write our own NULL.
    */
   data = (LPWSTR) malloc(cbData + sizeof(WCHAR));
   if (NULL == data) {
      Debug("%s: Couldn't allocate %d bytes.\n", __FUNCTION__, cbData);
      goto exit;
   }

   temp = cbData;
   rc = RegQueryValueExW(key, CONF_VMWARE_TOOLS_INSTPATH_KEY_W, 0, NULL, (LPBYTE) data,
                         &temp);
   if (ERROR_SUCCESS != rc) {
      Debug("%s: Couldn't get data for value \"%S\".\n", __FUNCTION__,
            CONF_VMWARE_TOOLS_INSTPATH_KEY_W);
      Debug("%s: RegQueryValueExW error 0x%x.\n", __FUNCTION__, GetLastError());
      goto exit;
   }

   data[cbData / sizeof(WCHAR)] = L'\0';

exit:
   if (NULL != key) {
      RegCloseKey(key);
   }

   return data;
}

#endif

/*
 *----------------------------------------------------------------------
 *
 * GuestApp_GetInstallPath --
 *
 *      Get the tools installation path. The caller is responsible for
 *      freeing the memory allocated for the path.
 *
 * Results:
 *      The path in UTF-8 if successful.
 *      NULL otherwise.
 *
 * Side effects:
 *      Allocates memory.
 *
 *----------------------------------------------------------------------
 */

char *
GuestApp_GetInstallPath(void)
{
   char *pathUtf8 = NULL;

#if defined _WIN32
   size_t pathLen = 0;

   if (WinReg_GetSZ(HKEY_LOCAL_MACHINE,
                    CONF_VMWARE_TOOLS_REGKEY,
                    CONF_VMWARE_TOOLS_INSTPATH_KEY,
                    &pathUtf8) != ERROR_SUCCESS) {
      Warning("%s: Unable to retrieve install path: %s\n",
               __FUNCTION__, Msg_ErrString());
      return NULL;
   }

   /* Strip off the trailing backslash, if present */

   pathLen = strlen(pathUtf8);
   if (pathLen > 0) {
      if (pathUtf8[pathLen - 1] == '\\') {
         pathUtf8[pathLen - 1] = '\0';
      }
   }

#else
   pathUtf8 = Str_Asprintf(NULL, "%s", GUESTAPP_TOOLS_INSTALL_PATH);
#endif

   return pathUtf8;
}


/*
 *----------------------------------------------------------------------
 *
 * GuestApp_GetConfPath --
 *
 *      Get the path to the Tools configuration file.
 *
 *      The return conf path is a dynamically allocated UTF-8 encoded
 *      string that should be freed by the caller.
 *
 *      However, the function will also return NULL if we fail to create
 *      a "VMware/VMware Tools" directory. This can occur if we're not running
 *      as Administrator, which VMwareUser doesn't. But I believe that
 *      VMwareService will always come up before VMwareUser, so by the time
 *      a non-root user process calls this function, the directory exists.
 *
 * Results:
 *      The path in UTF-8, or NULL on failure.
 *
 * Side effects:
 *      Allocates memory.
 *
 *----------------------------------------------------------------------
 */

char *
GuestApp_GetConfPath(void)
{
   static char *confPath = NULL;

   if (confPath == NULL) {
#if defined _WIN32
      confPath = GuestAppGetOrCreateConfPath();
#else
      /* Just call into GuestApp_GetInstallPath. */
      confPath = GuestApp_GetInstallPath();
#endif
   }

   /*
    * Return a copy of the cached confPath.
    */
   return confPath ? Util_SafeStrdup(confPath) : NULL;
}

#if defined _WIN32

/*
 *----------------------------------------------------------------------
 *
 * GuestAppGetOrCreateConfPath --
 *
 *      Get the path to the Tools configuration file.
 *
 *      The return conf path is a dynamically allocated UTF-8 encoded
 *      string that should be freed by the caller.
 *      The directory will be created if it doesn't exist.
 *
 *      However, the function will also return NULL if we fail to create
 *      a "VMware/VMware Tools" directory. This can occur if we're not running
 *      as Administrator, which VMwareUser doesn't. But I believe that
 *      VMwareService will always come up before VMwareUser, so by the time
 *      a non-root user process calls this function, the directory exists.
 *
 * Results:
 *      The path in UTF-8, or NULL on failure.
 *
 * Side effects:
 *      Allocates memory and creates the directory if it does not exist.
 *
 *----------------------------------------------------------------------
 */

static char *
GuestAppGetOrCreateConfPath(void)
{
   PSECURITY_ATTRIBUTES confPathSecAttrs = NULL;
   PACL confPathAcl = NULL;
   char *path;
   char *tmp;
   DWORD status = ERROR_SUCCESS;

   path = W32Util_GetVmwareCommonAppDataFilePath(NULL);
   if (path == NULL) {
      goto exit;
   }

   tmp = Str_SafeAsprintf(NULL, "%s%c%s", path, DIRSEPC, ProductState_GetName());
   free(path);
   path = tmp;

   if (File_Exists(path)) {
      goto exit;
   }

   status = GuestAppCreateConfPathSecInfo(&confPathSecAttrs, &confPathAcl);
   if (ERROR_SUCCESS != status) {
      Warning("%s: Error: Get security info failed %u\n", __FUNCTION__, status);
      goto exit;
   }

   if (!CreateDirectoryA(path, confPathSecAttrs)) {
      status = GetLastError();
      if (ERROR_ALREADY_EXISTS == status) {
         Debug("%s: Error: CreateDirectory path exists %u\n", __FUNCTION__, status);
         status = ERROR_SUCCESS;
      } else {
         Warning("%s: Error: CreateDirectory failed %u\n", __FUNCTION__, status);
         goto exit;
      }
   }

exit:
   GuestAppDestroyConfPathSecInfo(confPathSecAttrs, confPathAcl);

   if (ERROR_SUCCESS != status) {
      free(path);
      path = NULL;
   }

   return path;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAppCreateConfPathSecInfo --
 *
 *      Creates the user access security attributes.
 *
 * Results:
 *      ERROR_SUCCESS on success or appropriate failure code.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static DWORD
GuestAppCreateConfPathSecInfo(PSECURITY_ATTRIBUTES *confPathSecAttrs,   // OUT: security attrs
                              PACL *confPathAcl)                        // OUT: acl
{
   PSECURITY_ATTRIBUTES newSA = NULL;
   PSECURITY_DESCRIPTOR newSD = NULL;
   PACL newAcl = NULL;
   DWORD status = ERROR_SUCCESS;

   Debug("%s: entered\n", __FUNCTION__);

   ASSERT(NULL != confPathSecAttrs);
   ASSERT(NULL != confPathAcl);

   /* Initialize a security descriptor. */
   newSA = malloc(sizeof *newSA);
   if (NULL == newSA) {
      status = ERROR_NOT_ENOUGH_MEMORY;
      Warning("%s: Error: malloc failed %u\n", __FUNCTION__, status);
      goto exit;
   }
   memset(newSA, 0, sizeof *newSA);

   status = GuestAppCreateConfPathSD(&newSD, &newAcl);
   if (ERROR_SUCCESS != status) {
      Warning("%s: Error: SD creation failed %u\n", __FUNCTION__, status);
      goto exit;
   }

   newSA->nLength = sizeof *newSA;
   newSA->bInheritHandle = FALSE;
   newSA->lpSecurityDescriptor = newSD;

exit:
   if (ERROR_SUCCESS != status) {
      GuestAppDestroyConfPathSecInfo(newSA, newAcl);
      newSA = NULL;
      newAcl = NULL;
   }
   *confPathSecAttrs = newSA;
   *confPathAcl = newAcl;
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAppDestroyConfPathSecInfo --
 *
 *      Destroys the security attributes and ACL.
 *
 * Results:
 *      None.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static VOID
GuestAppDestroyConfPathSecInfo(PSECURITY_ATTRIBUTES confPathSecAttrs,  // IN/OUT: security attrs
                               PACL confPathAcl)                       // IN/OUT: acl
{
   if (NULL != confPathSecAttrs) {
      GuestAppDestroyConfPathSD(confPathSecAttrs->lpSecurityDescriptor,
                                confPathAcl);
      free(confPathSecAttrs);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAppCreateConfPathSD --
 *
 *      Creates a security descriptor for the configuration path.
 *
 * Results:
 *      ERROR_SUCCESS on success or appropriate failure code.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static DWORD
GuestAppCreateConfPathSD(PSECURITY_DESCRIPTOR *confPathSD,   // OUT: security
                         PACL *confPathAcl)                  // OUT: acl
{
   DWORD status = ERROR_SUCCESS;
   PACL newConfPathAcl = NULL;
   PSECURITY_DESCRIPTOR newConfPathSD = NULL;

   ASSERT(NULL != confPathSD);
   ASSERT(NULL != confPathAcl);

   status = GuestAppCreateConfPathAcl(&newConfPathAcl);
   if (ERROR_SUCCESS != status) {
      Warning("%s: Error: Get Acl failed %u\n", __FUNCTION__, status);
      goto exit;
   }

   /* Initialize a security descriptor. */
   newConfPathSD = malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
   if (NULL == newConfPathSD) {
      status = ERROR_NOT_ENOUGH_MEMORY;
      Warning("%s: Error: malloc failed %u\n", __FUNCTION__, status);
      goto exit;
   }

    if (!InitializeSecurityDescriptor(newConfPathSD, SECURITY_DESCRIPTOR_REVISION)) {
      status = GetLastError();
      Warning("%s: Error: InitializeSecurityDescriptor failed %u\n", __FUNCTION__, status);
      goto exit;
    }

   /* Add the ACL to the security descriptor. */
   if (!SetSecurityDescriptorDacl(newConfPathSD,
                                  TRUE,
                                  newConfPathAcl,
                                  FALSE)) {
      status = GetLastError();
      Warning("%s: Error: SetSecurityDescriptorDacl failed %u\n", __FUNCTION__, status);
      goto exit;
   }

exit:
   if (ERROR_SUCCESS != status) {
      GuestAppDestroyConfPathSD(newConfPathSD, newConfPathAcl);
      newConfPathSD = NULL;
      newConfPathAcl = NULL;
   }
   *confPathSD = newConfPathSD;
   *confPathAcl = newConfPathAcl;
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAppDestroyConfPathSD --
 *
 *      Destroys the security descriptor and access control list.
 *
 * Results:
 *      None.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static VOID
GuestAppDestroyConfPathSD(PSECURITY_DESCRIPTOR confPathSd,   // IN/OUT: security
                          PACL confPathAcl)                  // IN/OUT: acl
{
   free(confPathSd);
   GuestAppDestroyConfPathAcl(confPathAcl);
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAppCreateConfPathAcl --
 *
 *      Create a new user access ACL.
 *
 * Results:
 *      ERROR_SUCCESS on success or appropriate failure code.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static DWORD
GuestAppCreateConfPathAcl(PACL *confPathAcl) // OUT: ACL
{
   PACL newAcl = NULL;
   PSID systemSID = NULL;
   PSID adminsGrpSID = NULL;
   PSID everyoneSID = NULL;
   SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
   SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
   DWORD newAclSize;
   DWORD status = ERROR_SUCCESS;

   ASSERT(NULL != confPathAcl);

   /* Create a well-known SID for the Everyone group. */
   if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
                                 SECURITY_WORLD_RID,
                                 0, 0, 0, 0, 0, 0, 0,
                                 &everyoneSID)) {
      status = GetLastError();
      Warning("%s: Error: AllocateAndInitializeSid Error %u\n", __FUNCTION__, status);
      goto exit;
   }

   /* Create a well-known SID for the Administrators group. */
   if (!AllocateAndInitializeSid(&SIDAuthNT, 2,
                                 SECURITY_BUILTIN_DOMAIN_RID,
                                 DOMAIN_ALIAS_RID_ADMINS,
                                 0, 0, 0, 0, 0, 0,
                                 &adminsGrpSID)) {
      status = GetLastError();
      Warning("%s: Error: AllocateAndInitializeSid Error %u\n", __FUNCTION__, status);
      goto exit;
   }

   /* Create a well-known SID for the System user. */
   if (!AllocateAndInitializeSid(&SIDAuthNT, 1,
                                 SECURITY_LOCAL_SYSTEM_RID,
                                 0, 0, 0, 0, 0, 0, 0,
                                 &systemSID)) {
      status = GetLastError();
      Warning("%s: Error: AllocateAndInitializeSid Error %u\n", __FUNCTION__, status);
      goto exit;
   }


   /*
    * Get the size of the new ACL. See the following link for this calculation.
    * http://msdn.microsoft.com/en-us/library/windows/desktop/aa378853(v=vs.85).aspx?
    */
   newAclSize = sizeof *newAcl;
   newAclSize += GetLengthSid(systemSID) + sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD);
   newAclSize += GetLengthSid(adminsGrpSID) + sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD);
   newAclSize += GetLengthSid(everyoneSID) + sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD);

   /* Align newAclSize to a DWORD. */
   newAclSize = (newAclSize + (sizeof (DWORD) - 1)) & 0xfffffffc;

   /* Allocate the new ACL. */
   newAcl = malloc(newAclSize);
   if (NULL == newAcl) {
      status = ERROR_NOT_ENOUGH_MEMORY;
      Warning("%s: Error: malloc Acl failed %u\n", __FUNCTION__, status);
      goto exit;
   }

   /* Initialize the new DACL. */
   if (!InitializeAcl(newAcl, newAclSize, ACL_REVISION)) {
      status = GetLastError();
      Warning("%s: Error: Init Acl failed %u\n", __FUNCTION__, status);
      goto exit;
   }

   /* Add the ACE to the DACL for the sid. Note: no inheritence. */
   if (!AddAccessAllowedAceEx(newAcl,
                              ACL_REVISION,
                              CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
                              GENERIC_READ | GENERIC_EXECUTE,
                              everyoneSID)) {
      status = GetLastError();
      Warning("%s: Error: Add Everyone Ace to Acl failed %u\n", __FUNCTION__, status);
      goto exit;
   }

   /* Add the ACE to the ACL for the sid. Note: no inheritence. */
   if (!AddAccessAllowedAceEx(newAcl,
                              ACL_REVISION,
                              CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
                              GENERIC_ALL,
                              adminsGrpSID)) {
      status = GetLastError();
      Warning("%s: Error: Add admins Grp Ace to Acl failed %u\n", __FUNCTION__, status);
      goto exit;
   }

   /* Add the ACE to the ACL for the sid. Note: no inheritence. */
   if (!AddAccessAllowedAceEx(newAcl,
                              ACL_REVISION,
                              CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
                              GENERIC_ALL,
                              systemSID)) {
      status = GetLastError();
      Warning("%s: Error: Add system Ace to Acl failed %u\n", __FUNCTION__, status);
      goto exit;
   }

exit:
   /* Free the allocated SIDs. */
   if (NULL != everyoneSID) {
      FreeSid(everyoneSID);;
   }

   if (NULL != adminsGrpSID) {
      FreeSid(adminsGrpSID);
   }

   if (NULL != systemSID) {
      FreeSid(systemSID);
   }

   if (ERROR_SUCCESS != status) {
      GuestAppDestroyConfPathAcl(newAcl);
      newAcl = NULL;
   }
   *confPathAcl = newAcl;

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAppDestroyConfPathAcl --
 *
 *      Destroys the ACL.
 *
 * Results:
 *      None.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static VOID
GuestAppDestroyConfPathAcl(PACL confPathAcl)     // IN/OUT: security
{
   free(confPathAcl);
}
#endif

 07070100000060000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/lib/guestRpc   07070100000061000081A4000000000000000000000001682255050000073F000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/guestRpc/Makefile.am   ################################################################################
### Copyright (C) 2008-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libGuestRpc.la

libGuestRpc_la_SOURCES =
libGuestRpc_la_SOURCES += nicinfo_xdr.c

# XXX: Autoreconf complains about this and recommends using AM_CFLAGS instead.
# Problem is, $(CFLAGS) is appended to the compiler command line after AM_CFLAGS
# and after libGuestRpc_la_CFLAGS, so "-Wall -Werror" will override this flag.
CFLAGS += -Wno-unused

CLEANFILES =
CLEANFILES += nicinfo.h
CLEANFILES += nicinfo_xdr.c

EXTRA_DIST =
EXTRA_DIST += nicinfo.x


# Rules to invoke rpcgen. rpcgen will generate funny paths in the generated
# files if not invoked in the same directory as the source file, so we need
# to copy the sources to the build dir before compiling them.

nicinfo.h: nicinfo.x
	@RPCGEN_WRAPPER@ lib/guestRpc/nicinfo.x $@

nicinfo_xdr.c: nicinfo.x nicinfo.h
	@RPCGEN_WRAPPER@ lib/guestRpc/nicinfo.x $@

libGuestRpc_la_CPPFLAGS =
libGuestRpc_la_CPPFLAGS += @XDR_CPPFLAGS@

libGuestRpc_la_LIBADD =
libGuestRpc_la_LIBADD += @XDR_LIBS@

 07070100000062000081A40000000000000000000000016822550500001E7C000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/guestRpc/nicinfo.x /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * nicinfo.x --
 *
 *    Definition of the data structures used in the GuestRpc commands to
 *    provide information about the guest NICs.
 */

/*
 * Enumerates the different versions of the messages. Starting at 2, since
 * version one is legacy code we can't change.
 */
enum NicInfoVersion {
   NIC_INFO_V1 = 1,     /* XXX Not represented here. */
   NIC_INFO_V2 = 2,
   NIC_INFO_V3 = 3
};

/*
 * These are arbitrary limits to avoid possible DoS attacks.
 * The IP limit is large enough to hold an IP address (either v4 or v6).
 */
const NICINFO_MAX_IP_LEN   = 64;
const NICINFO_MAX_IPS      = 2048;
const NICINFO_MAX_NICS     = 16;

/* MAC Addresses are "AA:BB:CC:DD:EE:FF" = 18 bytes. */
const NICINFO_MAC_LEN      = 18;

/*
 * Corresponds to public/guestInfo.h::GuestInfoIPAddressFamilyType.
 */
enum NicInfoAddrType {
   NICINFO_ADDR_IPV4 = 0,
   NICINFO_ADDR_IPV6 = 1
};

struct VmIpAddress {
   NicInfoAddrType addressFamily;
   Bool        dhcpEnabled;
   char        ipAddress[NICINFO_MAX_IP_LEN];
   /*
    * For IPv4, may be either a hexadecimal mask ("0xffffff00") or CIDR-style
    * prefix length ("24").  For IPv6, will only be a prefix length.
    */
   char        subnetMask[NICINFO_MAX_IP_LEN];
};

struct GuestNic {
   char                 macAddress[NICINFO_MAC_LEN];
   struct VmIpAddress   ips<NICINFO_MAX_IPS>;
};

/*
 * This structure is not entirely necessary, but it makes the generated
 * code nicer to code to.
 */
struct GuestNicList {
   struct GuestNic nics<NICINFO_MAX_NICS>;
};


/*
 *-----------------------------------------------------------------------------
 *
 * NIC Info version 3.
 *
 *      V3 adds routing, DNS, and WINS information to the NIC list.
 *
 *-----------------------------------------------------------------------------
 */


/*
 * IP v4 and v6 addressing.
 *
 * These types were redefined primarily to allow much more efficient wire
 * encoding.
 */


/*
 * See RFC 4001, InetAddress.
 */
const INET_ADDRESS_MAX_LEN = 255;
typedef opaque InetAddress<INET_ADDRESS_MAX_LEN>;


/*
 * See RFC 4001, InetAddressType.
 */
enum InetAddressType {
   IAT_UNKNOWN  = 0,
   IAT_IPV4     = 1,
   IAT_IPV6     = 2,
   IAT_IPV4Z    = 3,
   IAT_IPV6Z    = 4,
   IAT_DNS      = 16
};


/*
 * See RFC 4001, InetAddressPrefixLength.
 */
typedef unsigned int InetAddressPrefixLength;


/*
 * See RFC 4293, IpAddressOriginTC.
 */
enum IpAddressOrigin {
   IAO_OTHER            = 1,
   IAO_MANUAL           = 2,
   IAO_DHCP             = 4,
   IAO_LINKLAYER        = 5,
   IAO_RANDOM           = 6
};


/*
 * See RFC 4293, IpAddressStatusTC.
 *
 * "The status of an address.  Most of the states correspond to states
 * from the IPv6 Stateless Address Autoconfiguration protocol.
 *    ...
 * "In the absence of other information, an IPv4 address is always
 * preferred(1)." 
 */
enum IpAddressStatus {
   IAS_PREFERRED        = 1,
   IAS_DEPRECATED       = 2,
   IAS_INVALID          = 3,
   IAS_INACCESSIBLE     = 4,
   IAS_UNKNOWN          = 5,
   IAS_TENTATIVE        = 6,
   IAS_DUPLICATE        = 7,
   IAS_OPTIMISTIC       = 8
};


/*
 * Convenient type for lists of addresses.
 */
struct TypedIpAddress {
   InetAddressType      ipAddressAddrType;
   InetAddress          ipAddressAddr;
};


/*
 * See RFC 4293, IpAddressEntry.
 *
 * "An address mapping for a particular interface."
 *
 * We deviate from the RFC in a few places:
 *  - The prefix length is listed explicitly here rather than reference
 *    a separate prefix table.
 *  - Interface indices aren't included as this structure is dependent
 *    upon/encapsulated by a GuestNicV3 structure.
 */
struct IpAddressEntry {
   TypedIpAddress               ipAddressAddr;
   InetAddressPrefixLength      ipAddressPrefixLength;
   IpAddressOrigin              *ipAddressOrigin;
   IpAddressStatus              *ipAddressStatus;
};


/*
 * Runtime DNS resolver state.
 */


/*
 * Quoth RFC 2181 §11 (Name Syntax):
 *    "The length of any one label is limited to between 1 and 63 octets.  A
 *    full domain name is limited to 255 octets (including the separators)."
 */
const DNSINFO_MAX_ADDRLEN = 255;
typedef string DnsHostname<DNSINFO_MAX_ADDRLEN>;


/*
 * Arbitrary limits.
 */
const DNSINFO_MAX_SUFFIXES = 10;
const DNSINFO_MAX_SERVERS = 16;

struct DnsConfigInfo {
   DnsHostname  *hostName;
   DnsHostname  *domainName;
   TypedIpAddress serverList<DNSINFO_MAX_SERVERS>;
   DnsHostname  searchSuffixes<DNSINFO_MAX_SUFFIXES>;
};


/*
 * Runtime WINS resolver state.  Addresses are assumed to be IPv4 only.
 */

struct WinsConfigInfo {
   TypedIpAddress       primary;
   TypedIpAddress       secondary;
};


/*
 * Runtime routing tables.
 */


/*
 * Arbitrary limit.
 */
const NICINFO_MAX_ROUTES = 100;


/*
 * See RFC 4292 for details.
 */
enum InetCidrRouteType {
   ICRT_OTHER   = 1,
   ICRT_REJECT  = 2,
   ICRT_LOCAL   = 3,
   ICRT_REMOTE  = 4
};

struct InetCidrRouteEntry {
   TypedIpAddress               inetCidrRouteDest;
   InetAddressPrefixLength      inetCidrRoutePfxLen;

   /*
    * Next hop isn't mandatory.
    */
   TypedIpAddress               *inetCidrRouteNextHop;

   /*
    * inetCidrRouteProto is omitted as we're concerned only with static/
    * netmgmt routes.
    */

   /* This is an array index into GuestNicListV3::nics. */
   uint32                       inetCidrRouteIfIndex;

   /* XXX Do we really need this? */
   InetCidrRouteType            inetCidrRouteType;

   /* -1 == unused. */
   uint32                       inetCidrRouteMetric;
};


/*
 * Fun with DHCP
 */

const DHCP_MAX_CONFIG_SIZE = 16384;

struct DhcpConfigInfo {
   bool         enabled;
   string       dhcpSettings<DHCP_MAX_CONFIG_SIZE>;
};


/*
 * Top-level containers.
 */


/*
 * Describes a single NIC.
 */

struct GuestNicV3 {
   string               macAddress<NICINFO_MAC_LEN>;
   IpAddressEntry       ips<NICINFO_MAX_IPS>;
   DnsConfigInfo        *dnsConfigInfo;
   WinsConfigInfo       *winsConfigInfo;
   DhcpConfigInfo       *dhcpConfigInfov4;
   DhcpConfigInfo       *dhcpConfigInfov6;
};


/*
 * Attempts to model general network state.
 */

struct NicInfoV3 {
   GuestNicV3           nics<NICINFO_MAX_NICS>;
   InetCidrRouteEntry   routes<NICINFO_MAX_ROUTES>;
   DnsConfigInfo        *dnsConfigInfo;
   WinsConfigInfo       *winsConfigInfo;
   DhcpConfigInfo       *dhcpConfigInfov4;
   DhcpConfigInfo       *dhcpConfigInfov6;
};


/*
 * This defines the protocol for a "nic info" message. The union allows
 * us to create new versions of the protocol later by creating new values
 * in the NicInfoVersion enumeration, without having to change much of
 * the code calling the (de)serialization functions.
 *
 * Since the union doesn't have a default case, de-serialization will fail
 * if an unknown version is provided on the wire.
 */
union GuestNicProto switch (NicInfoVersion ver) {
case NIC_INFO_V2:
   struct GuestNicList *nicsV2;
case NIC_INFO_V3:
   struct NicInfoV3 *nicInfoV3;
};
07070100000063000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/guestStoreClientHelper 07070100000064000081A40000000000000000000000016822550500000470000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/guestStoreClientHelper/Makefile.am ################################################################################
### Copyright (c) 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libGuestStoreClientHelper.la

libGuestStoreClientHelper_la_SOURCES =
libGuestStoreClientHelper_la_SOURCES += guestStoreClient.c

libGuestStoreClientHelper_la_CPPFLAGS =
libGuestStoreClientHelper_la_CPPFLAGS += @GLIB2_CPPFLAGS@07070100000065000081A40000000000000000000000016822550500002967000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/lib/guestStoreClientHelper/guestStoreClient.c  /*********************************************************
 * Copyright (c) 2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * guestStoreClient.c --
 *
 *     Wrapper functions to load/unload and get content from GuestStore.
 */

#define G_LOG_DOMAIN          "guestStoreClient"

#include <glib-object.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>

#if defined(_WIN32)
#include <windows.h>
#else
#include <unistd.h>
#include <dlfcn.h>
#endif

#include "vm_assert.h"
#include "vm_basic_defs.h"
#include "guestStoreClient.h"


#if defined(_WIN32)
#define GUESTSTORE_CLIENTLIB_DLL   WSTR("guestStoreClient.dll")
#else
#define GUESTSTORE_CLIENTLIB_DLL   "libguestStoreClient.so.0"
#endif

/*
 * Track whether the library has been initialized or not.
 */
static gboolean gsClientInit = FALSE;

/*
 * Module handle of GuestStore client library.
 */
#if defined(_WIN32)
static HMODULE gsClientLibModule = NULL;
#else
static void *gsClientLibModule = NULL;
#endif


/*
 * Function pointer types for GuestStore client library exports.
 */
typedef GuestStoreLibError (*GuestStoreLibInit)(void);
typedef GuestStoreLibError (*GuestStoreLibDeInit)(void);
typedef GuestStoreLibError (*GuestStoreLibGetContent)(const char* contentPath,
                                                      const char* outputPath,
                                                      GuestStore_Logger logger,
                                                      GuestStore_Panic panic,
                                                      GuestStore_GetContentCallback getContentCb,
                                                      void* clientData);

/*
 * Function pointer definitions for GuestStore client library exports.
 */

static GuestStoreLibInit       GuestStoreLib_Init;
static GuestStoreLibDeInit     GuestStoreLib_DeInit;
static GuestStoreLibGetContent GuestStoreLib_GetContent;

/*
 * Macro to get the export function address from GuestStore client library.
 */
#if defined(_WIN32)

#define GET_GUESTSTORELIB_FUNC_ADDR(funcname)                              \
   do {                                                                    \
      (FARPROC) XXCONC(GuestStoreLib_,funcname) = GetProcAddress(gsClientLibModule,  \
         "GuestStore_" #funcname);                                         \
      if (XXCONC(GuestStoreLib_,funcname) == NULL) {                       \
         error = GetLastError();                                           \
         g_critical("GetProcAddress failed for \'%s\': error=%u.\n",       \
                    "GuestStore_" #funcname, error);                       \
         return FALSE;                                                     \
      }                                                                    \
   } while (0)

#else

#define GET_GUESTSTORELIB_FUNC_ADDR(funcname)                        \
   do {                                                              \
      dlerror();                                                     \
      *(void **)(&XXCONC(GuestStoreLib_,funcname)) = dlsym(gsClientLibModule,  \
         "GuestStore_" #funcname);                                   \
      if ((dlErrStr = dlerror()) != NULL) {                          \
         g_critical("dlsym failed for \'%s\': %s\n",                 \
                    "GuestStore_" #funcname, dlErrStr);              \
         return FALSE;                                               \
      }                                                              \
   } while (0)

#endif


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreGetLibExportFunctions --
 *
 *      Get the export function addresses from GuestStore client library.
 *
 * Results:
 *      TRUE on success.
 *      FALSE on failure.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
GuestStoreGetLibExportFunctions(void)
{

#if defined(_WIN32)
   DWORD error;

   g_debug("Entering %s.\n", __FUNCTION__);

   gsClientLibModule = LoadLibraryW(GUESTSTORE_CLIENTLIB_DLL);
   if (gsClientLibModule == NULL) {
      error = GetLastError();
      g_critical("%s: LoadLibrary failed: error=%u.\n", __FUNCTION__, error);
      return FALSE;
   }
#else
   char const *dlErrStr;

   g_debug("Entering %s.\n", __FUNCTION__);

   gsClientLibModule = dlopen(GUESTSTORE_CLIENTLIB_DLL, RTLD_NOW);
   if (gsClientLibModule == NULL) {
      dlErrStr = dlerror();
      g_critical("%s: dlopen failed: %s\n", __FUNCTION__, dlErrStr);
      return FALSE;
   }
#endif

   GET_GUESTSTORELIB_FUNC_ADDR(Init);        // For GuestStore_Init
   GET_GUESTSTORELIB_FUNC_ADDR(GetContent);  // For GuestStore_GetContent
   GET_GUESTSTORELIB_FUNC_ADDR(DeInit);      // For GuestStore_DeInit

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreClientLogger --
 *
 *      Log messages from GuestStore client library.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
GuestStoreClientLogger(GuestStoreLibLogLevel level,  // IN
                       const char *message,          // IN
                       void *clientData)             // IN
{
   switch (level) {
      case GSLIBLOGLEVEL_ERROR:
         g_critical("%s: Error: %s\n", __FUNCTION__, message);
         break;

      case GSLIBLOGLEVEL_WARNING:
         g_warning("%s: Warning: %s\n", __FUNCTION__, message);
         break;

      case GSLIBLOGLEVEL_INFO:
         g_info("%s: Info: %s\n", __FUNCTION__, message);
         break;

      case GSLIBLOGLEVEL_DEBUG:
         g_debug("%s: Debug: %s\n", __FUNCTION__, message);
         break;

      default:
         ASSERT(0);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreClientPanic --
 *
 *      Panic handler for GuestStore client library.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Process crashes?
 *
 *-----------------------------------------------------------------------------
 */

static void
GuestStoreClientPanic(const char *message,  // IN
                      void *clientData)     // IN
{
   g_critical("%s: %s\n", __FUNCTION__, message);
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreClient_Init --
 *
 *      Initialize the guest store client library access
 *
 * Results:
 *      TRUE on success., FALSE otherwise
 *
 *      Error code from GuestStore client library or
 *      general process error exit code.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
GuestStoreClient_Init(void)
{
   GuestStoreLibError libErr;

   g_debug("Entering %s.\n", __FUNCTION__);

   if (!GuestStoreGetLibExportFunctions()) {
      goto exit;
   }

   libErr = GuestStoreLib_Init();
   if (libErr != GSLIBERR_SUCCESS) {
      g_critical("%s: GuestStoreLib_Init failed: error=%d.\n",
                 __FUNCTION__, libErr);
      goto exit;
   }
   gsClientInit = TRUE;

exit:
   g_debug("%s: Exit -> %d.\n", __FUNCTION__, gsClientInit);
   return gsClientInit;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreClient_DeInit --
 *
 *      Deinitialize the guest store client library access
 *
 * Results:
 *      TRUE on success, FALSE otherwise
 *
 *      Error code from GuestStore client library or
 *      general process error exit code.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
GuestStoreClient_DeInit(void)
{
   GuestStoreLibError libErr;

   g_debug("Entering %s.\n", __FUNCTION__);

   if (!gsClientInit) {
      return gsClientInit;
   }

   libErr = GuestStoreLib_DeInit();
   if (libErr != GSLIBERR_SUCCESS) {
      g_critical("%s: GuestStore_DeInit failed: error=%d.\n",
                 __FUNCTION__, libErr);
   }

   if (gsClientLibModule != NULL) {
#if defined(_WIN32)
      if (!FreeLibrary(gsClientLibModule)) {
         g_critical("%s: FreeLibrary failed: error=%d.\n",
                    __FUNCTION__, GetLastError());
      }
#else
      if (dlclose(gsClientLibModule) != 0) {
         g_critical("%s: dlclose failed with error: %s\n",
                    __FUNCTION__, dlerror());
      }
#endif

      gsClientLibModule = NULL;
   }

   g_debug("Exiting %s.\n", __FUNCTION__);

   gsClientInit = FALSE;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreClient_GetContent --
 *
 *      Handle and parse gueststore command.
 *
 * Results:
 *      Error code from GuestStore client library or
 *      general process error exit code.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

GuestStoreClientError
GuestStoreClient_GetContent(const char *contentPath,                     // IN: content file path
                            const char *outputPath,                      // IN: output file path
                            GuestStoreClient_GetContentCb getContentCb,  // IN: OPTIONAL callback
                            void *clientCbData)                          // IN: OPTIONAL callback data
{
   g_debug("Entering %s.\n", __FUNCTION__);

   if (!gsClientInit) {
      return GSLIBERR_NOT_INITIALIZED;
   }

   return GuestStoreLib_GetContent(contentPath, outputPath,
                                   GuestStoreClientLogger,
                                   GuestStoreClientPanic,
                                   getContentCb, clientCbData);
}
 07070100000066000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/hashMap    07070100000067000081A400000000000000000000000168225505000003DD000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/hashMap/Makefile.am    ################################################################################
### Copyright (C) 2013-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libHashMap.la

libHashMap_la_SOURCES =
libHashMap_la_SOURCES += hashMap.c
   07070100000068000081A40000000000000000000000016822550500006D86000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/hashMap/hashMap.c  /*********************************************************
 * Copyright (C) 2009-2018,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#include "vm_basic_types.h"
#include "vm_assert.h"

#include <stdlib.h>
#include <string.h>

#include "hashMap.h"
#include "clamped.h"
#include "util.h"
#ifdef VMX86_SERVER
#include "aioMgr.h"
#include "iovector.h"
#endif

/*
 * hashMap.c --
 *
 *    This is a map data structure that can store a sparsely indexed data set.
 *    It is not intended to be thread safe nor should it be used in a way that
 *    this may cause problems.
 *
 *    Entries are stored based on a simple hash of their key.  Memory
 *    allocations are kept to a minimum to ensure that this is appropriate for
 *    use in a kernel mode driver.  Linear probing is used to resolve hash
 *    collisions.
 *
 *    This implementation only supports static length keys.  It might be
 *    possible to store the keys outside the table and thus support keys of
 *    variable length but this isn't currently planned.  This would allow for
 *    string keys for example.
 *
 *    Callers should not store pointers to objects stored in the map as they may
 *    become invalid as a result of a resize.  If you need to share objects
 *    stored as values in the map, then store pointers to the objects instead.
 *
 *    All objects are copied into the map and must be freed as appropriate by
 *    the caller.
 *
 *    This particular HashMap has some important differences from the hash table
 *    implementation in bora/lib/misc
 *    - HashMap supports key removal
 *    - HashMap only supports fixed size keys and values while hashTable
 *      supports string and insensitive string keys and only supports pointer
 *      data.  This means its possible to store entire data structures in
 *      HashMap.
 *    - HashMap uses linear probing to resolve collisions while hashTable uses
 *      chaining.  HashMap will dynamically resize itself as necessary.
 *    - Pointers to HashMap values will be invalidated if the internal structure
 *      is resized.  If this is a problem, you should store the pointer in the
 *      HashMap rather than the object itself.
 *    - HashMap will ensure that a maximum load factor is not exceeded.
 *
 *    hashMap is now restrictd to userworld applications on ESX builds ONLY.
 *    See PR 817760 which has an attached patchfile to remove this limitation
 *    if in future that is felt to be desireable.
 */

#define HASHMAP_DEFAULT_ALPHA 2

struct HashMap {
   uint8 *entries;
   uint32 numEntries;
   uint32 count;
   uint32 alpha;

   size_t keySize;
   size_t dataSize;
   size_t entrySize;

   size_t keyOffset;
   size_t dataOffset;
};

#ifdef VMX86_SERVER
#pragma pack(push, 1)
typedef struct HashMapOnDisk {
   uint32 numEntries;
   uint32 count;
   uint32 alpha;

   uint64 keySize;
   uint64 dataSize;
   uint64 entrySize;

   uint64 keyOffset;
   uint64 dataOffset;
} HashMapOnDisk;
#pragma pack(pop)
#endif

typedef enum {
   HashMapState_EMPTY  = 0,
   HashMapState_FILLED,
   HashMapState_DELETED,
} HashMapEntryState;

typedef struct {
   uint32 state;
   uint32 hash;
} HashMapEntryHeader;

#define NO_FREE_INDEX ((uint32) -1)

static Bool InitMap(struct HashMap *map, uint32 numEntries, uint32 alpha,
                    size_t keySize, size_t dataSize);
static void CalculateEntrySize(struct HashMap *map);
static void GetEntry(struct HashMap *map, uint32 index, HashMapEntryHeader **header, void **key, void **data);
static uint32 ComputeHash(struct HashMap *map, const void *key);
static Bool LookupKey(struct HashMap* map, const void *key, Bool constTimeLookup, HashMapEntryHeader **header, void **data, uint32 *freeIndex);
static Bool CompareKeys(struct HashMap *map, const void *key, const void *compare);
static Bool ConstTimeCompareKeys(struct HashMap *map, const void *key, const void *compare);
static Bool NeedsResize(struct HashMap *map);
static void Resize(struct HashMap *map);
INLINE void EnsureSanity(HashMap *map);

/*
 * ----------------------------------------------------------------------------
 *
 * CheckSanity --
 *
 *   Same code as EnsureSanity except return Bool instead of ASSERTing
 *
 * Results:
 *    TRUE is sane.
 *
 * Side Effects:
 *    None.
 *
 * ----------------------------------------------------------------------------
 */

#ifdef VMX86_DEBUG
static INLINE Bool
CheckSanity(HashMap *map)
{
   uint32 i, cnt = 0;

   ASSERT(map);
   for (i = 0; i < map->numEntries; i++) {
      HashMapEntryHeader *header = NULL;
      void *key, *data;

      GetEntry(map, i, &header, &key, &data);
      ASSERT(header);
      ASSERT(header->state == HashMapState_FILLED ||
             header->state == HashMapState_EMPTY ||
             header->state == HashMapState_DELETED);
      if (header->state == HashMapState_FILLED) {
         cnt++;
         if (header->hash != ComputeHash(map, key)) {
            return FALSE;
         }
      }
   }

   if (cnt != map->count) {
      return FALSE;
   }

   if (!map->numEntries) {
      return FALSE;
   }
   return TRUE;
}
#endif

/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_AllocMap --
 *
 *    Allocate a map and the space for the entries.
 *
 * Results:
 *    Returns a pointer to a HashMap or NULL on failure.
 *
 * Side Effects:
 *    Allocates memory.
 *
 * ----------------------------------------------------------------------------
 */

struct HashMap*
HashMap_AllocMap(uint32 numEntries,  // IN
                 size_t keySize,     // IN
                 size_t dataSize)    // IN
{
   return HashMap_AllocMapAlpha(numEntries, HASHMAP_DEFAULT_ALPHA, keySize, dataSize);
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_AllocMapAlpha --
 *
 *    Allocate a map and the space for the entries.  The value of alpha is
 *    treated as a denominator for the maximum allowable load factor.  I.e. an
 *    alpha value of 2 would correspond to a maximum load factor of 0.5.  The
 *    map will be enlarged when elements are added in order to maintain this
 *    load factor.
 *
 * Results:
 *    Returns a pointer to a HashMap or NULL on failure.
 *
 * Side Effects:
 *    Allocates memory.
 *
 * ----------------------------------------------------------------------------
 */

struct HashMap*
HashMap_AllocMapAlpha(uint32 numEntries,  // IN
                      uint32 alpha,       // IN
                      size_t keySize,     // IN
                      size_t dataSize)    // IN
{
   struct HashMap *map;
   map = calloc(1, sizeof *map);

   ASSERT(alpha);

   if (map) {
      if (!InitMap(map, numEntries, alpha, keySize, dataSize)) {
         HashMap_DestroyMap(map);
         return NULL;
      }
   }

   return map;
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_DestroyMap --
 *
 *    Destroy a HashMap, clear out all the entries and free the memory.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Frees memory.
 *
 * ----------------------------------------------------------------------------
 */

void
HashMap_DestroyMap(struct HashMap *map)  // IN
{
   if (map) {
      free(map->entries);
   }
   free(map);
}


/*
 * ----------------------------------------------------------------------------
 *
 * InitMap --
 *
 *    Initializes a map's internal structure.  The value of alpha is treated
 *    as a denominator for the maximum allowable load factor.  I.e. an alpha
 *    value of 2 would correspond to a maximum load factor of 0.5.  The map
 *    will be enlarged when elements are added in order to maintain this load
 *    factor.
 *
 * Results:
 *    Returns TRUE on success or FALSE if the memory allocation failed.
 *
 * Side Effects:
 *    Allocates memory.
 *
 * ----------------------------------------------------------------------------
 */

static Bool
InitMap(struct HashMap *map,     // IN
        uint32 numEntries,       // IN
        uint32 alpha,            // IN
        size_t keySize,          // IN
        size_t dataSize)         // IN
{
   ASSERT(map);
   ASSERT(alpha);
   ASSERT(numEntries);

   /*
    * Ensure that the entries map is at least large enough to hold all of the
    * entries that were requested taking into account the alpha factor.
    */
   numEntries *= alpha;

   map->numEntries = numEntries;
   map->alpha = alpha;
   map->keySize = keySize;
   map->dataSize = dataSize;

   CalculateEntrySize(map);
   map->entries = calloc(numEntries, map->entrySize);

   if (map->entries) {
      EnsureSanity(map);
   }

   return map->entries != NULL;
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_Put --
 *
 *    Put the value at data against the key.  This will replace any existing
 *    data that is in the table without warning.
 *
 * Results:
 *    TRUE if the put operation is successful
 *    FALSE otherwise
 *
 * Side Effects:
 *    The value in data is copied to the table and can be referenced in the
 *    future by the value in key.
 *
 * ----------------------------------------------------------------------------
 */

Bool
HashMap_Put(struct HashMap *map,    // IN
            const void *key,        // IN
            const void *data)       // IN
{
   uint32 freeIndex;
   HashMapEntryHeader *header;
   void *tableData;

   if (!LookupKey(map, key, FALSE, &header, &tableData, &freeIndex)) {
      uint32 hash = ComputeHash(map, key);
      void *tableKey;

      if (NeedsResize(map)) {
         Resize(map);
         if (LookupKey(map, key, FALSE, &header, &tableData, &freeIndex)) {
            /*
             * Somehow our key appeared after resizing the table.
             */
            ASSERT(FALSE);
         }
         if (freeIndex == NO_FREE_INDEX) {
            /*
             * The resize must have failed.
             */
            return FALSE;
         }
      }

      map->count++;
      GetEntry(map, freeIndex, &header, &tableKey, &tableData);
      ASSERT(header);

      header->state = HashMapState_FILLED;
      header->hash = hash;
      memcpy(tableKey, key, map->keySize);
   }

   ASSERT(data || map->dataSize == 0);
   memcpy(tableData, data, map->dataSize);

   EnsureSanity(map);
   return TRUE;
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_Get --
 *
 *    Get the value corresponding to the given key.
 *
 * Results:
 *    Returns a pointer to the data that was previously stored by HashMap_Put or
 *    NULL if the key wasn't found.
 *
 * Side Effects:
 *    None.
 *
 * ----------------------------------------------------------------------------
 */

void *
HashMap_Get(struct HashMap *map,    // IN
            const void *key)        // IN
{
   void *data;
   uint32 freeIndex;
   HashMapEntryHeader *header;

   if (LookupKey(map, key, FALSE, &header, &data, &freeIndex)) {
      return data;
   }

   return NULL;
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_ConstTimeGet --
 *
 *    Timing attack safe version of HashMap_Get. This will call LookupKey with
 *    the constTime flag set to 1 which will do the memory comparison with
 *    Util_ConstTimeMemDiff instead of memcmp. Note that there is a bit of a
 *    time penalty associated with this so only use this if you are looking up
 *    sensitive information.
 *
 * Results:
 *    Returns a pointer to the data that was previously stored by HashMap_Put or
 *    NULL if the key wasn't found.
 *
 * Side Effects:
 *    None.
 *
 * ----------------------------------------------------------------------------
 */

void *
HashMap_ConstTimeGet(struct HashMap *map,    // IN
                     const void *key)        // IN
{
   void *data;
   uint32 freeIndex;
   HashMapEntryHeader *header;

   if (LookupKey(map, key, TRUE, &header, &data, &freeIndex)) {
      return data;
   }

   return NULL;
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_Clear --
 *
 *    Remove all entries from the HashMap.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    All entries in the map are removed.
 *
 * ----------------------------------------------------------------------------
 */

void
HashMap_Clear(struct HashMap *map) // IN
{
   int i = 0;
   HashMapEntryHeader *header;
   void *key, *data;

   ASSERT(map);
   for (i = 0; i < map->numEntries; i++) {
      GetEntry(map, i, &header, &key, &data);
      ASSERT(header);
      header->state = HashMapState_EMPTY;
   }
   map->count = 0;
   EnsureSanity(map);
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_Remove --
 *
 *    Remove an entry from the map.
 *
 * Results:
 *    Returns TRUE if the entry was in the map, FALSE if the entry was not in
 *    the map.
 *
 * Side Effects:
 *    The entry is removed from the map.
 *
 * ----------------------------------------------------------------------------
 */

Bool
HashMap_Remove(struct HashMap *map,   // IN
               const void *key)       // IN
{
   uint32 freeIndex;
   HashMapEntryHeader *header;
   void *tableData;

   if (!LookupKey(map, key, FALSE, &header, &tableData, &freeIndex)) {
      return FALSE;
   }

   /*
    * XXX: This could be made slightly smarter.  We could check the next entry
    * to see if it's EMPTY and then mark this one as empty as well.
    */
   map->count--;
   header->state = HashMapState_DELETED;

   EnsureSanity(map);

   return TRUE;
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_Count --
 *
 *    Returns the current count of entries in the map.
 *
 * Results:
 *    The current count of entries in the map.
 *
 * Side Effects:
 *    None.
 *
 * ----------------------------------------------------------------------------
 */

uint32
HashMap_Count(struct HashMap *map) // IN
{
   return map->count;
}


/*
 * ----------------------------------------------------------------------------
 *
 * CalculateEntrySize --
 *
 *    Calculate the size of the entry and the offsets to the key and data.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    The map structure is adjusted to contain the correct sizes.
 *
 * ----------------------------------------------------------------------------
 */

void
CalculateEntrySize(struct HashMap *map) // IN
{
   size_t alignKeySize, alignDataSize;
   size_t alignKeyOffset, alignDataOffset;

   ASSERT(map);

   alignKeySize = ROUNDUP(map->keySize, 4);
   alignDataSize = ROUNDUP(map->dataSize, 4);

   alignKeyOffset = sizeof (HashMapEntryHeader);
   alignDataOffset = ROUNDUP(alignKeyOffset + alignKeySize, 4);

   map->entrySize = sizeof (HashMapEntryHeader) + alignKeySize + alignDataSize;
   map->keyOffset = alignKeyOffset;
   map->dataOffset = alignDataOffset;
}


/*
 * ----------------------------------------------------------------------------
 *
 * LookupKey --
 *
 *    Use linear probing to find a free space in the table or the data that
 *    we're interested in.
 *
 *    Call this function with constTimeLookup = TRUE to use a timing attack
 *    safe version of memcmp while comparing keys.
 *
 * Returns:
 *    - TRUE if the key was found in the table, FALSE otherwise.
 *    - Returns the entry header on header, data pointer on data and the first
 *    non-filled index that was encountered on freeIndex
 *
 * Side Effects:
 *    - Header and Data are changed.  They should only be considered valid if
 *    the key was found.
 *    - FreeIndex will be updated to point to the first non-filled index.
 *
 * ----------------------------------------------------------------------------
 */

Bool
LookupKey(struct HashMap* map,          // IN
          const void *key,              // IN
          Bool constTimeLookup,         // IN
          HashMapEntryHeader **header,  // OUT
          void **data,                  // OUT
          uint32 *freeIndex)            // OUT
{
   uint32 hash = ComputeHash(map, key);
   uint32 index = hash % map->numEntries;
   uint32 probe = 0;

   Bool done = FALSE, found = FALSE;
   void *tableKey;

   ASSERT(map);
   ASSERT(key);
   ASSERT(data);
   ASSERT(freeIndex);

   *freeIndex = NO_FREE_INDEX;

   while (!done && probe < map->numEntries + 1) {
      uint32 currentIndex = (index + probe) % map->numEntries;

      GetEntry(map, currentIndex, header, &tableKey, data);
      ASSERT(header);

      switch ((*header)->state) {
      case HashMapState_EMPTY:
         done = TRUE;
         /* FALL THROUGH */
      case HashMapState_DELETED:
         /*
          * We're not done if we've found a deleted space.  This may just mean
          * that the entry was deleted but that the target entry may appear
          * later.
          */
         if (*freeIndex == NO_FREE_INDEX) {
            *freeIndex = currentIndex;
         }
         break;
      case HashMapState_FILLED:
         if ((*header)->hash == hash) {
            /*
             * There is some performance penalty to doing a constant time
             * comparison, so only use that version if it's been explicitly
             * asked for.
             */
            if (constTimeLookup) {
               found = ConstTimeCompareKeys(map, key, tableKey);
            } else {
               found = CompareKeys(map, key, tableKey);
            }
            if (found) {
               done = TRUE;
            }
         }
         break;
      default:
         NOT_REACHED();
      }
      probe++;
   }

   ASSERT(found || *freeIndex != NO_FREE_INDEX || map->count == map->numEntries);

   return found;
}


/*
 * ----------------------------------------------------------------------------
 *
 * GetEntry --
 *
 *    Get a specific entry from the entries list.  This does not perform any
 *    hash compare, it's a simple index based lookup.
 *
 * Results:
 *    The header is stored in header, key in key and the data in data.  These
 *    are all direct pointers into the table.
 *
 * Side Effects:
 *    The header, key and data pointers are modified.
 *
 * ----------------------------------------------------------------------------
 */

void
GetEntry(struct HashMap *map,          // IN
         uint32 index,                 // IN
         HashMapEntryHeader **header,  // OUT
         void **key,                   // OUT
         void **data)                  // OUT
{
   uint8 *entry;

   ASSERT(map);
   ASSERT(header);
   ASSERT(key);
   ASSERT(data);
   ASSERT(index < map->numEntries);

   entry = ((uint8 *)map->entries) + (map->entrySize * index);
   ASSERT(entry);
   *header = (HashMapEntryHeader *) (entry);
   *key = (void*) (entry + map->keyOffset);
   *data = (void*) (entry + map->dataOffset);
}


/*
 * ----------------------------------------------------------------------------
 *
 * ComputeHash --
 *
 *    Compute the hash of the given key.
 *
 * Results:
 *    An opaque hash value based on the given key.  Given the same key in the
 *    same map, this function will always return the same value.
 *
 * Side Effects:
 *    None.
 *
 * ----------------------------------------------------------------------------
 */

uint32
ComputeHash(struct HashMap *map,  // IN
            const void *key)      // IN
{
   /*
    * djb2, with n == 33. See http://www.cse.yorku.ca/~oz/hash.html.
    *
    * This hash function is largely borrowed from the emmet library in bora/lib
    * This hash table implementation does a hash compare before comparing the
    * keys so it's inappropriate for the hash function to take the modulo before
    * returning.
    */
   uint32 h = 5381;
   const uint8 *keyByte;
   size_t i = 0;

   for (keyByte = key, i = 0; i < map->keySize; keyByte++, i++) {
      h *= 33;
      h += *keyByte;
   }
   return h;
}


/*
 * ----------------------------------------------------------------------------
 *
 * CompareKeys --
 *
 *    Compare two keys to one another.
 *
 * Results:
 *    Returns TRUE if the two keys are binary equal over the length specified
 *    in the map.  FALSE if the two keys are different.
 *
 * Side Effects:
 *    None.
 *
 * ----------------------------------------------------------------------------
 */

Bool
CompareKeys(struct HashMap *map,    // IN
            const void *key,        // IN
            const void *compare)    // IN
{
   return memcmp(key, compare, map->keySize) == 0;
}


/*
 * ----------------------------------------------------------------------------
 *
 * ConstTimeCompareKeys --
 *
 *    Timing attack safe version of CompareKeys. Instead of calling memcmp,
 *    which will return after the first character that doesn't match, this
 *    calls a constant time memory comparison function.
 *
 * Results:
 *    Returns TRUE if the two keys are binary equal over the length specified
 *    in the map.  FALSE if the two keys are different.
 *
 * Side Effects:
 *    None.
 *
 * ----------------------------------------------------------------------------
 */

Bool
ConstTimeCompareKeys(struct HashMap *map,    // IN
                     const void *key,        // IN
                     const void *compare)    // IN
{
   return Util_ConstTimeMemDiff(key, compare, map->keySize) == 0;
}


/*
 * ----------------------------------------------------------------------------
 *
 * NeedsResize --
 *
 *    Determine if adding another element to the map will require that the map
 *    be resized.  This takes into account the maximum load factor that is
 *    allowed for this map.
 *
 * Results:
 *    Returns TRUE if the map should be resized.
 *
 * Side Effects:
 *    None.
 *
 * ----------------------------------------------------------------------------
 */

Bool
NeedsResize(struct HashMap *map)
{
   uint32 required;

   Clamped_UMul32(&required, map->count, map->alpha);

   return required >= map->numEntries;
}

/*
 * ----------------------------------------------------------------------------
 *
 * Resize --
 *
 *    Doubles the size of the entries array until it is at least large enough
 *    to ensure the maximum load factor is not exceeded.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    The entries list is doubled in size and the entries are copied into the
 *    appropriate location.  Callers should not assume that the locations that
 *    were valid before this was called are still valid as all entries may
 *    appear at different locations after this function completes.
 *
 * ----------------------------------------------------------------------------
 */

void
Resize(struct HashMap *map)   // IN
{
   struct HashMap oldHashMap = *map;
   int i;

   if (map->numEntries == MAX_UINT32) {
      if (map->count < MAX_UINT32) {
         /*
          * We're already at the maximum size of the array and there's still
          * room for the new entry, don't bother resizing.
          */
         return;
      } else {
         /*
          * This situation is fatal, though we're unlikely to ever hit this with
          * realistic usage.
          */
         Panic("Ran out of room in the hashtable\n");
      }
   }

   /*
    * We might, at some point, want to look at making this grow geometrically
    * until we hit some threshold and then grow arithmetically after that.  To
    * keep it simple for now, however, we'll just grow geometrically all the
    * time.
    */
   map->entries = calloc(oldHashMap.numEntries * 2, oldHashMap.entrySize);
   if (!map->entries) {
      map->entries = oldHashMap.entries;
      return;
   }

   do {
      if (!Clamped_UMul32(&map->numEntries, map->numEntries, 2)) {
         /* Prevent overflow and */
         break;
      }
   } while (NeedsResize(map));

   map->count = 0;

   for (i = 0; i < oldHashMap.numEntries; i++) {
      HashMapEntryHeader *oldHeader;
      HashMapEntryHeader *newHeader;
      void *oldKey;
      void *oldData;
      void *newKey;
      void *newData;
      uint32 freeIndex;

      GetEntry(&oldHashMap, i, &oldHeader, &oldKey, &oldData);
      switch (oldHeader->state) {
      case HashMapState_EMPTY:
      case HashMapState_DELETED:
         continue;
      case HashMapState_FILLED:
         if (!LookupKey(map, oldKey, FALSE, &newHeader, &newData, &freeIndex)) {
            GetEntry(map, freeIndex, &newHeader, &newKey, &newData);

            newHeader->hash = oldHeader->hash;
            newHeader->state = HashMapState_FILLED;
            memcpy(newKey, oldKey, map->keySize);
            memcpy(newData, oldData, map->dataSize);

            map->count++;
         } else {
            /*
             * There is only one way that this could happen; the key is in the
             * old map twice.  That's clearly an error.
             */
            ASSERT(FALSE);
            continue;
         }
      }
   }

   ASSERT(oldHashMap.count == map->count);
   free(oldHashMap.entries);
   EnsureSanity(map);
}


/*
 * ----------------------------------------------------------------------------
 *
 * HashMap_Iterate --
 *
 *    Iterate over the contents of the map, optionally clearing each entry as
 *    it's passed.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    itFn is called for each entry in the hashMap until.  If clear is TRUE,
 *    each entry will be removed from the map after it has been seen by the
 *    iterator function.
 *
 * ----------------------------------------------------------------------------
 */

void
HashMap_Iterate(HashMap *map,             // IN
                HashMapIteratorFn itFn,   // IN
                Bool clear,               // IN
                void *userData)           // IN/OUT
{
   int i = 0;
   HashMapEntryHeader *header;
   void *key, *data;

   ASSERT(map);
   ASSERT(itFn);

   for (i = 0; i < map->numEntries; i++) {
      GetEntry(map, i, &header, &key, &data);
      if (header->state == HashMapState_FILLED) {
         itFn(key, data, userData);
         if (clear) {
            map->count--;
         }
      }
      if (clear) {
         header->state = HashMapState_EMPTY;
      }
   }

   ASSERT(map->count == 0 || !clear);
}


/*
 * ----------------------------------------------------------------------------
 *
 * EnsureSanity --
 *
 *    Ensure that the HashMap contents are still sane.  That is, each entry has
 *    a correctly computed hash, the count is correct and the header states are
 *    valid for all entries.  This should be called at the end of every
 *    function which modifies the map.
 *
 * Results:
 *    None.
 *
 * Side Effects:
 *    Fails an assert if the contents are incorrect.
 *
 * ----------------------------------------------------------------------------
 */

void
EnsureSanity(HashMap *map)
{
   ASSERT(CheckSanity(map) == TRUE);
}



  07070100000069000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs   0707010000006A000081A400000000000000000000000168225505000004A9000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/Makefile.am   ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libHgfs.la

libHgfs_la_SOURCES =
libHgfs_la_SOURCES += cpName.c
libHgfs_la_SOURCES += cpNameLite.c
libHgfs_la_SOURCES += cpNameLinux.c
libHgfs_la_SOURCES += cpNameUtil.c
libHgfs_la_SOURCES += cpNameUtilLinux.c
libHgfs_la_SOURCES += hgfsUtil.c
libHgfs_la_SOURCES += hgfsEscape.c
   0707010000006B000081A40000000000000000000000016822550500003634000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/cpName.c  /*********************************************************
 * Copyright (c) 1998-2016,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * cpName.c --
 *
 *    Shared portions of cross-platform name conversion routines used
 *    by hgfs. [bac]
 *
 */

#ifdef sun
#include <string.h>
#endif

#include "cpName.h"
#include "cpNameInt.h"
#include "vm_assert.h"
#include "hgfsEscape.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
{
   char const *walk;
   char const *myNext;
   size_t len;

   ASSERT(begin);
   ASSERT(end);
   ASSERT(next);
   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("%s: error: first char can't be NUL\n", __FUNCTION__);
            return -1;
         }

         myNext = walk + 1;
         /* Skip consecutive path delimiters. */
         while ((*myNext == '\0') && (myNext != end)) {
            myNext++;
         }
         if (myNext == end) {
            /* Last character in the buffer is not allowed to be NUL */
            Log("%s: error: last char can't be NUL\n", __FUNCTION__);
            return -1;
         }

         break;
      }
   }

   len = walk - begin;

   *next = myNext;
   return (int) len;
}


/*
 *----------------------------------------------------------------------
 *
 * CPNameEscapeAndConvertFrom --
 *
 *    Converts a cross-platform name representation into a string for
 *    use in the local filesystem.
 *    Escapes illegal characters as a part of convertion.
 *    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
CPNameEscapeAndConvertFrom(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
{
   int result;
   int inputSize;
   inputSize = HgfsEscape_GetSize(*bufIn, *inSize);
   if (inputSize < 0) {
      result = -1;
   } else if (inputSize != 0) {
      char *savedBufOut = *bufOut;
      char const *savedOutConst = savedBufOut;
      size_t savedOutSize = *outSize;
      if (inputSize > *outSize) {
         Log("%s: error: not enough room for escaping\n", __FUNCTION__);
         return -1;
      }

      /* Leaving space for the leading path separator, thus output to savedBufOut + 1. */
      result = HgfsEscape_Do(*bufIn, *inSize, savedOutSize - 1, savedBufOut + 1);
      if (result < 0) {
         Log("%s: error: not enough room to perform escape: %d\n",
             __FUNCTION__, result);
          return -1;
      }
      *inSize = (size_t) result;

      result = CPNameConvertFrom(&savedOutConst, inSize, outSize, bufOut, pathSep);
      *bufIn += *inSize;
      *inSize = 0;
   } else {
      result = CPNameConvertFrom(bufIn, inSize, outSize, bufOut, pathSep);
   }
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * 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;
   Bool inPlaceConvertion = (*bufIn == *bufOut);

   ASSERT(bufIn);
   ASSERT(inSize);
   ASSERT(outSize);
   ASSERT(bufOut);

   in = *bufIn;
   inEnd = in + *inSize;
   if (inPlaceConvertion) {
      in++; // Skip place for the leading path separator.
   }
   myOutSize = *outSize;
   out = *bufOut;

   for (;;) {
      char const *next;
      int len;
      int newLen;

      len = CPName_GetComponent(in, inEnd, &next);
      if (len < 0) {
         Log("%s: error: get next component failed\n", __FUNCTION__);
         return len;
      }

      /* Bug 27926 - preventing escaping from shared folder. */
      if ((len == 1 && *in == '.') ||
          (len == 2 && in[0] == '.' && in[1] == '.')) {
         Log("%s: error: found dot/dotdot\n", __FUNCTION__);
         return -1;
      }

      if (len == 0) {
         /* No more component */
         break;
      }

      newLen = ((int) myOutSize) - len - 1;
      if (newLen < 0) {
         Log("%s: error: not enough room\n", __FUNCTION__);
         return -1;
      }
      myOutSize = (size_t) newLen;

      *out++ = pathSep;
      if (!inPlaceConvertion) {
         memcpy(out, in, len);
      }
      out += len;

      in = next;
   }

   /* NUL terminate */
   if (myOutSize < 1) {
      Log("%s: error: not enough room\n", __FUNCTION__);
      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, '/');
}


/*
 *----------------------------------------------------------------------------
 *
 * 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.
 *    HGFS convention is to echange names between guest and host in uescaped form.
 *    Both ends perform necessary name escaping according to its own rules
 *    to avoid presenitng invalid file names to OS. Thus the name needs to be unescaped
 *    as a part of conversion to host-independent format.
 *
 * 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 *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'.
     * Collapse multiple consecutive path separators into a single one since
     * CPName_GetComponent can't handle consecutive path separators.
     */
   while (*nameIn != '\0' && bufOut < endOut) {
      if (*nameIn == pathSep) {
         *bufOut = '\0';
         do {
            nameIn++;
         } while (*nameIn == pathSep);
      } else {
         *bufOut = *nameIn;
         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--;
   }
   cpNameLength = HgfsEscape_Undo(origOut, cpNameLength);

   /* Return number of bytes used */
   return (int) cpNameLength;
}
0707010000006C000081A40000000000000000000000016822550500000A88000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/cpNameInt.h   /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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

int
CPNameEscapeAndConvertFrom(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

#endif /* __CP_NAME_INT_H__ */
0707010000006D000081A400000000000000000000000168225505000015EE000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/cpNameLinux.c /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * cpNameLinux.c --
 *
 *    Linux implementation of cross-platform name conversion
 *    routines used by hgfs. [bac]
 *
 */

#if defined sun && !defined SOL9
#include <memory.h>
#endif

#include "cpName.h"
#include "cpNameInt.h"
#include "vm_assert.h"
#include "hgfsEscape.h"


/*
 *----------------------------------------------------------------------
 *
 * 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 CPNameEscapeAndConvertFrom(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("%s: get first component failed\n", __FUNCTION__);
      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.
 *    If the name being converter may be a result a of name escaping the
 *    function performs unescaping. HGFS convention is to always exchange
 *    unescaped names between guest and host and to perform necessary
 *    name escaping on both ends.
 *
 * 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
{
   int result;
   result = CPName_LinuxConvertTo(nameIn, bufOutSize, bufOut);
   return result;
}
  0707010000006E000081A40000000000000000000000016822550500000CA2000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/cpNameLite.c  /*********************************************************
 * Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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;
      }
   }
}
  0707010000006F000081A40000000000000000000000016822550500002B5C000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/cpNameUtil.c  /*********************************************************
 * Copyright (C) 2005-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * cpNameUtil.c
 *
 *    Common implementations of CPName utility functions.
 */


/* Some of the headers below cannot be included in driver code */
#if !defined __KERNEL__ && !defined _KERNEL && !defined KERNEL

#include "cpNameUtil.h"
#include "hgfsServerPolicy.h"
#include "hgfsVirtualDir.h"
#include "util.h"
#include "vm_assert.h"
#include "str.h"
#include "cpNameUtilInt.h"

#define WIN_DIRSEPC     '\\'
#define WIN_DIRSEPS     "\\"


/*
 *----------------------------------------------------------------------------
 *
 * CPNameUtil_Strrchr --
 *
 *    Performs strrchr(3) on a CPName path.
 *
 * Results:
 *    Pointer to last occurrence of searchChar in cpNameIn if found, NULL if
 *    not found.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

char *
CPNameUtil_Strrchr(char const *cpNameIn,       // IN: CPName path to search
                   size_t cpNameInSize,        // IN: Size of CPName path
                   char searchChar)            // IN: Character to search for
{
   ssize_t index;

   ASSERT(cpNameIn);
   ASSERT(cpNameInSize > 0);

   for (index = cpNameInSize - 1;
        cpNameIn[index] != searchChar && index >= 0;
        index--);

   return (index < 0) ? NULL : (char *)(cpNameIn + index);
}


/*
 *----------------------------------------------------------------------------
 *
 * CPNameUtil_LinuxConvertToRoot --
 *
 *    Performs CPName conversion and such that the result can be converted back
 *    to an absolute path (in the "root" share) by a Linux hgfs server.
 *
 *    Note that nameIn must contain an absolute path.
 *
 * Results:
 *    Size of the output buffer on success, negative value on error
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
CPNameUtil_LinuxConvertToRoot(char const *nameIn, // IN:  buf to convert
                              size_t bufOutSize,  // IN:  size of the output buffer
                              char *bufOut)       // OUT: output buffer
{
   const size_t shareNameSize = HGFS_STR_LEN(HGFS_SERVER_POLICY_ROOT_SHARE_NAME);

   int result;

   ASSERT(nameIn);
   ASSERT(bufOut);

   if (bufOutSize <= shareNameSize) {
      return -1;
   }

   /* Prepend the name of the "root" share directly in the output buffer */
   memcpy(bufOut, HGFS_SERVER_POLICY_ROOT_SHARE_NAME, shareNameSize);
   bufOut[shareNameSize] = '\0';

   result = CPName_LinuxConvertTo(nameIn, bufOutSize - shareNameSize - 1,
                                  bufOut + shareNameSize + 1);

   /* Return either the same error code or the correct size */
   return (result < 0) ? result : (int)(result + shareNameSize + 1);
}


/*
 *----------------------------------------------------------------------------
 *
 * CPNameUtil_WindowsConvertToRoot --
 *
 *    Performs CPName conversion and appends necessary strings ("root" and
 *    "drive"|"unc") so that the result can be converted back to an absolute
 *    path (in the "root" share) by a Windows hgfs server.
 *
 *    Note that nameIn must contain an absolute path.
 *
 * Results:
 *    Size of the output buffer on success, negative value on error
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
CPNameUtil_WindowsConvertToRoot(char const *nameIn, // IN:  buf to convert
                                size_t bufOutSize,  // IN:  size of the output buffer
                                char *bufOut)       // OUT: output buffer
{
   const char partialName[] = HGFS_SERVER_POLICY_ROOT_SHARE_NAME;
   const size_t partialNameLen = HGFS_STR_LEN(HGFS_SERVER_POLICY_ROOT_SHARE_NAME);
   const char *partialNameSuffix = "";
   size_t partialNameSuffixLen;
   char *fullName;
   size_t fullNameLen;
   size_t nameLen;
   int result;

   ASSERT(nameIn);
   ASSERT(bufOut);

   /*
    * Create the full name. Note that Str_Asprintf should not be
    * used here as it uses FormatMessages which interprets 'data', a UTF-8
    * string, as a string in the current locale giving wrong results.
    */

   /*
    * Is this file path a UNC path?
    */
   if (nameIn[0] == WIN_DIRSEPC && nameIn[1] == WIN_DIRSEPC) {
      partialNameSuffix    = WIN_DIRSEPS HGFS_UNC_DIR_NAME WIN_DIRSEPS;
      partialNameSuffixLen = HGFS_STR_LEN(WIN_DIRSEPS) +
                             HGFS_STR_LEN(HGFS_UNC_DIR_NAME) +
                             HGFS_STR_LEN(WIN_DIRSEPS);
   } else {
      partialNameSuffix    = WIN_DIRSEPS HGFS_DRIVE_DIR_NAME WIN_DIRSEPS;
      partialNameSuffixLen = HGFS_STR_LEN(WIN_DIRSEPS) +
                             HGFS_STR_LEN(HGFS_DRIVE_DIR_NAME) +
                             HGFS_STR_LEN(WIN_DIRSEPS);
   }

   /* Skip any path separators at the beginning of the input string */
   while (*nameIn == WIN_DIRSEPC) {
      nameIn++;
   }

   nameLen = strlen(nameIn);
   fullNameLen = partialNameLen + partialNameSuffixLen + nameLen;
   fullName = (char *)Util_SafeMalloc(fullNameLen + 1);

   memcpy(fullName, partialName, partialNameLen);

   memcpy(fullName + partialNameLen, partialNameSuffix, partialNameSuffixLen);
   if (nameIn[1] == ':') {
      /*
       * If the name is in format "<drive letter>:" strip out ':' from it
       * because the rest of the code assumes that driver letter in a 
       * platform independent name is represented by a single character without colon.
       */
      fullName[partialNameLen + partialNameSuffixLen] = nameIn[0];
      memcpy(fullName + partialNameLen + partialNameSuffixLen + 1, nameIn + 2, nameLen - 2);
      fullNameLen--;
   } else {
      memcpy(fullName + partialNameLen + partialNameSuffixLen, nameIn, nameLen);
   }
   fullName[fullNameLen] = '\0';

   /* CPName_ConvertTo strips out the ':' character */
   result = CPName_WindowsConvertTo(fullName, bufOutSize, bufOut);
   free(fullName);

   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPNameUtil_Utf8FormHostToUtf8FormC --
 *
 *    Convert CPname to form C (precomposed) which is used by the HGFS
 *    protocol from host preferred format. On Mac hosts the current format
 *    is unicode form D, so conversion is required, others the current
 *    format is the same.
 *
 *    Input/output name lengths include the nul-terminator so that the 
 *    conversion routine will include the final character when breaking
 *    up the CPName into it's individual components.
 *
 *
 * Results:
 *    TRUE if success result string is in form C format, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPNameUtil_Utf8FormHostToUtf8FormC(const char *cpNameToConvert,   // IN:
                                   size_t cpNameToConvertLen,     // IN: includes nul
                                   char **cpUtf8FormCName,        // OUT:
                                   size_t *cpUtf8FormCNameLen)    // OUT: includes nul
{
   return CPNameUtilConvertUtf8FormCAndD(cpNameToConvert,
                                         cpNameToConvertLen,
                                         TRUE,
                                         cpUtf8FormCName,
                                         cpUtf8FormCNameLen);
}


/*
 *----------------------------------------------------------------------------
 *
 * CPNameUtil_Utf8FormCToUtf8FormHost --
 *
 *    Convert from CP unicode form C (decomposed) name  used by the HGFS
 *    protocol to the host preferred format. On Mac OS is unicode form D 
 *    (precomposed), everyone else this stays as form C (precomposed).
 *
 *    Input/output name lengths include the nul-terminator so that the 
 *    conversion routine will include the final character when breaking
 *    up the CPName into it's individual components.
 *
 * Results:
 *    TRUE if success result string is in CP format, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPNameUtil_Utf8FormCToUtf8FormHost(const char *cpUtf8FormCName,   // IN:
                                   size_t cpUtf8FormCNameLen,     // IN: includes nul 
                                   char **cpConvertedName,        // OUT:
                                   size_t *cpConvertedNameLen)    // OUT: includes nul
{
   return CPNameUtilConvertUtf8FormCAndD(cpUtf8FormCName,
                                         cpUtf8FormCNameLen,
                                         FALSE,
                                         cpConvertedName,
                                         cpConvertedNameLen);
}


/*
 *----------------------------------------------------------------------------
 *
 * CPNameUitl_CharReplace --
 *
 *    A simple function to replace all oldChar's with newChar's in a binary
 *    buffer. This is used for either replacing NULL with local DIRSPEC to
 *    convert from relative cross-platform name to local relative name, or
 *    replacing local DIRSEPC with NULL to convert from local relative name
 *    to relative cross-platform file name 
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
CPNameUtil_CharReplace(char *buf,      // IN/OUT
                       size_t bufSize, // IN
                       char oldChar,   // IN
                       char newChar)   // IN

{
   size_t i;

   ASSERT(buf);

   for (i = 0; i < bufSize; i++) {
      if (buf[i] == oldChar) {
         buf[i] = newChar;
      }
   }
}


#endif /* __KERNEL__ */
07070100000070000081A400000000000000000000000168225505000007FD000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/cpNameUtilInt.h   /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * cpNameUtilInt.h --
 *
 *    Cross-platform name format used by hgfs.
 *
 */


#ifndef __CP_NAME_UTIL_INT_H__
#define __CP_NAME_UTIL_INT_H__


#include "vm_basic_types.h"

/*
 * Used by CPNameUtil_Utf8FormHostToUtf8FormC and 
 * CPNameUtil_Utf8FormCToUtf8FormHost.
 */
Bool 
CPNameUtilConvertUtf8FormCAndD(const char *cpNameToConvert,
                               size_t cpNameToConvertLen,
                               Bool convertToFormC,
                               char **cpConvertedName,
                               size_t *cpConvertedNameLen);

#endif /* __CP_NAME_UTIL_INT_H__ */
   07070100000071000081A40000000000000000000000016822550500001810000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/cpNameUtilLinux.c /*********************************************************
 * Copyright (C) 2005-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * cpNameUtilLinux.c
 *
 *    Linux implementation of CPName utility functions.  These are not included
 *    in the main CPName API since they may require other calls on the
 *    resulting CPName to be from the same OS-type.
 */

/* Some of the headers in cpNameUtil.c cannot be included in driver code */
#if !defined __KERNEL__ && !defined _KERNEL && !defined KERNEL

#include "cpNameUtil.h"
#include "cpNameUtilInt.h"

#if defined __APPLE__
#include "codeset.h"
#endif /* defined __APPLE__ */
#include "util.h"

/*
 *----------------------------------------------------------------------------
 *
 * CPNameUtil_ConvertToRoot --
 *
 *    Pass through function that calls Linux version of _ConvertToRoot().
 *
 *    Performs CPName conversion and such that the result can be converted back
 *    to an absolute path (in the "root" share) by a Linux hgfs server.
 *
 *    Note that nameIn must contain an absolute path.
 *
 * Results:
 *    Size of the output buffer on success, negative value on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
CPNameUtil_ConvertToRoot(char const *nameIn, // IN:  buf to convert
                         size_t bufOutSize,  // IN:  size of the output buffer
                         char *bufOut)       // OUT: output buffer
{
   return CPNameUtil_LinuxConvertToRoot(nameIn, bufOutSize, bufOut);
}


/*
 *----------------------------------------------------------------------------
 *
 * CPNameUtilConvertUtf8FormCAndD --
 *
 *    Helper conversion routine to convert between a CP format name 
 *    in unicode form C (precomposed) format which is used by the HGFS
 *    protocol requests and the unicode form D (decomposed) format,
 *    which is used on Mac OS host (everyone else uses form C).
 *
 * Results:
 *    TRUE if success result string is converted, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPNameUtilConvertUtf8FormCAndD(const char *cpNameToConvert,   // IN:
                               size_t cpNameToConvertLen,     // IN: includes nul 
                               Bool convertToFormC,           // IN:
                               char **cpConvertedName,        // OUT:
                               size_t *cpConvertedNameLen)    // OUT: includes nul
{
   Bool status = TRUE;
#if defined __APPLE__
   const char *begin;
   const char *end;
   const char *next;
   char *newData = NULL;
   char *convertedName = NULL;
   size_t convertedNameLen;
   uint32 newDataLen = 0;
   int len;

   ASSERT(cpNameToConvert);
   ASSERT(cpConvertedName);
   ASSERT(cpConvertedNameLen);

   /* 
    * Get first component. We bypass the higher level CPName_GetComponent
    * function so we'll have more control over the illegal characters.
    */
   begin = cpNameToConvert;
   end = cpNameToConvert + cpNameToConvertLen - 1;
   /* Get the length of this component, and a pointer to the next. */
   while ((len = CPName_GetComponent(begin, end, &next)) != 0) {
      uint32 origNewDataLen = newDataLen;

      if (len < 0) {
         status = FALSE;
         goto exit;
      }

      if (convertToFormC) {
         status = CodeSet_Utf8FormDToUtf8FormC(begin, 
                                               len, 
                                               &convertedName, 
                                               &convertedNameLen);
      } else {
         status = CodeSet_Utf8FormCToUtf8FormD(begin, 
                                               len, 
                                               &convertedName, 
                                               &convertedNameLen);
      }

      if (!status) {
         goto exit;
      }

      /*
       * Append this component to our list: allocate one more for NUL.
       */
      newDataLen += convertedNameLen + 1;
      newData = (char *)Util_SafeRealloc(newData, newDataLen);

      memcpy(newData + origNewDataLen, convertedName, convertedNameLen);
      newData[newDataLen - 1] = '\0';

      free(convertedName);
      convertedName = NULL;
      begin = next;
   }

   *cpConvertedName = newData;
   /* Including nul terminator */
   *cpConvertedNameLen = newDataLen;
exit:
   if (!status) {
      if (newData != NULL) {
         free(newData);
      }
   }
#else /* defined __APPLE__ */
   /* No conversion required return a copy of what is received. */
   *cpConvertedName = Util_SafeCalloc(1, cpNameToConvertLen);
   memcpy(*cpConvertedName, cpNameToConvert, cpNameToConvertLen);
   *cpConvertedNameLen = cpNameToConvertLen;
#endif /* defined __APPLE__ */
   return status;
}

#endif /* __KERNEL__ */
07070100000072000081A40000000000000000000000016822550500007D02000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/hgfsEscape.c  /*********************************************************
 * Copyright (c) 2008-2019,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * hgfsEscape.c --
 *
 *    Escape and unescape illegal filenames for different platforms.
 *
 */

#ifdef __KERNEL__
#  include "driver-config.h"
#  include <linux/string.h>
#elif defined __FreeBSD__
#   if defined _KERNEL
#      include <sys/libkern.h>
#      define strchr(s,c)       index(s,c)
#   else
#      include <string.h>
#   endif
#   define memmove(s1,s2,n) bcopy(s2,s1,n)
#elif defined __APPLE__ && defined KERNEL
#  include <string.h>
#elif !defined sun
#  include <stdlib.h>
#  include <string.h>
#else
#  include <string.h>
#endif

#include "vmware.h"
#include "hgfsEscape.h"
#include "cpName.h"

#ifdef _WIN32

#define UNREFERENCED_PARAMETER(P) (P)

/* These characters are illegal in Windows file names. */
const char* HGFS_ILLEGAL_CHARS = "/\\*?:\"<>|";
const char* HGFS_SUBSTITUTE_CHARS = "!@#$^&(){";
/* Last character of a file name in Windows can be neither dot nor space. */
const char* HGFS_ILLEGAL_LAST_CHARS = ". ";

/* http://msdn.microsoft.com/en-us/library/aa365247.aspx */
char *HgfsReservedNames[] = {"CON", "PRN", "AUX", "NUL"};
char *HgfsReservedNamesWithNumber[] = {"COM", "LPT"};

#define HGFS_RESERVED_NAME_CHARS_LENGTH 3
#define HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH (HGFS_RESERVED_NAME_CHARS_LENGTH + 1)
/* Check for special escaping cases - reserved names and illegal last characters. */
#define IS_SPECIAL_CASE_ESCAPE(b,o,l) HgfsIsSpecialCaseEscape(b,o,l)
/* Process Windows reserved names. */
#define PROCESS_RESERVED_NAME(b,s,p,o,c) \
if (!HgfsProcessReservedName(b,s,p,o,c)) \
{ \
 return FALSE; \
}
/* Process Windows reserved names. */
#define PROCESS_LAST_CHARACTER(b,s,p,c) \
if (!HgfsProcessLastCharacter(b,s,p,c)) \
{ \
 return FALSE; \
}

#else // _WIN32

#define UNREFERENCED_PARAMETER(P)
/* There is no special escape sequences on other than Windows platforms. */
#define IS_SPECIAL_CASE_ESCAPE(b,o,l) FALSE
/* There is no reserved names on other then Windows platforms. */
#define PROCESS_RESERVED_NAME(b,s,p,o,c)
/* There is no special processing for the last character on non-Windows platforms. */
#define PROCESS_LAST_CHARACTER(b,s,p,c)

#if defined __APPLE__
/* These characters are illegal in MAC OS file names. */
const char* HGFS_ILLEGAL_CHARS = "/:";
const char* HGFS_SUBSTITUTE_CHARS = "!&";
#else   // __APPLE__
/* These characters are illegal in Linux file names. */
const char* HGFS_ILLEGAL_CHARS = "/";
const char* HGFS_SUBSTITUTE_CHARS = "!";
#endif  // __APPLE__

#endif  // _WIN32

#define HGFS_ESCAPE_CHAR '%'
#define HGFS_ESCAPE_SUBSTITUE_CHAR ']'

typedef enum {
   HGFS_ESCAPE_ILLEGAL_CHARACTER,
   HGFS_ESCAPE_RESERVED_NAME,
   HGFS_ESCAPE_ILLEGAL_LAST_CHARACTER,
   HGFS_ESCAPE_ESCAPE_SEQUENCE,
   HGFS_ESCAPE_COMPLETE
} HgfsEscapeReason;


typedef Bool (*HgfsEnumCallback)(char const *bufIn,
                                 uint32 offset,
                                 HgfsEscapeReason reason,
                                 void* context);

/*
 * The structure is used by HgfsAddEscapeCharacter to keep context information between
 * invocations
 * All offsets defined in this structure are in characters, not bytes
 */
typedef  struct {
   uint32   processedOffset;     // Offset of the first unprocessed input character
   uint32   outputBufferLength;  // Number of characters in the output buffer
   uint32   outputOffset;        // Number of characters that are already in the output
   char    *outputBuffer;        // Pointer to the output buffer
} HgfsEscapeContext;

static void HgfsEscapeUndoComponent(char *bufIn, uint32 *totalLength);
static int HgfsEscapeGetComponentSize(char const *bufIn, uint32 sizeIn);
static int HgfsEscapeDoComponent(char const *bufIn, uint32 sizeIn, uint32 sizeBufOut,
                                 char *bufOut);

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAddEscapeCharacter --
 *
 *    Callback function that is called by HgfsEnumerate to insert an escape sequence
 *    into the input name.
 *
 * Results:
 *    TRUE if successful, FALSE if there is an error like the output buffer is
 *    too small.
 *
 * Side effects:
 *    Updates the output buffer pointer (stored in the context variable).
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsAddEscapeCharacter(char const * bufIn,      // IN: input name
                       uint32 offset,           // IN: offset that requires escaping
                       HgfsEscapeReason reason, // IN: reason for esaping
                       void *context)           // IN/OUT: convertion context
{
   HgfsEscapeContext *escapeContext = (HgfsEscapeContext *)context;
   uint32 charactersToCopy;
   uint32 outputSpace;
   char* illegal;
   Bool result = TRUE;

   ASSERT(offset >= escapeContext->processedOffset); // Scanning forward
   charactersToCopy = offset - escapeContext->processedOffset;

   if (escapeContext->outputOffset + charactersToCopy >
       escapeContext->outputBufferLength) {
      return FALSE;
   }

   memcpy(escapeContext->outputBuffer + escapeContext->outputOffset,
          bufIn + escapeContext->processedOffset, charactersToCopy * sizeof *bufIn);
   escapeContext->outputOffset += charactersToCopy;
   escapeContext->processedOffset += charactersToCopy;

   outputSpace = escapeContext->outputBufferLength - escapeContext->outputOffset;

   switch(reason) {
   case HGFS_ESCAPE_ILLEGAL_CHARACTER:
      if (outputSpace < 2) {
         return FALSE;
      }
      illegal = strchr(HGFS_ILLEGAL_CHARS, bufIn[escapeContext->processedOffset]);
      escapeContext->processedOffset++;  // Skip illegal input character
      ASSERT(illegal != NULL);
      escapeContext->outputBuffer[escapeContext->outputOffset] =
         HGFS_SUBSTITUTE_CHARS[illegal - HGFS_ILLEGAL_CHARS];
      escapeContext->outputOffset++;
      escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_CHAR;
      escapeContext->outputOffset++;
      break;

   case HGFS_ESCAPE_RESERVED_NAME:
      if (outputSpace < 1) {
         return FALSE;
      }
      escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_CHAR;
      escapeContext->outputOffset++;
      break;

   case HGFS_ESCAPE_ILLEGAL_LAST_CHARACTER:
      if (outputSpace < 1) {
         return FALSE;
      }
      escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_CHAR;
      escapeContext->outputOffset++;
      break;

   case HGFS_ESCAPE_ESCAPE_SEQUENCE:
      if (outputSpace < 2) {
         return FALSE;
      }
      escapeContext->processedOffset++; // Skip input esape character
      escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_SUBSTITUE_CHAR;
      escapeContext->outputOffset++;
      escapeContext->outputBuffer[escapeContext->outputOffset] = HGFS_ESCAPE_CHAR;
      escapeContext->outputOffset++;
      break;

   case HGFS_ESCAPE_COMPLETE:
      if (outputSpace < 1) {
         return FALSE;
      }
      escapeContext->outputBuffer[escapeContext->outputOffset] = '\0';
      break;

   default:
      result = FALSE;
      ASSERT(FALSE);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCountEscapeChars --
 *
 *    Callback function that is called by HgfsEnumerate to count additional characters
 *    that need to be inserted in the input name.
 *
 * Results:
 *    TRUE since it never fails.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsCountEscapeChars(char const *bufIn,       // IN: input name
                     uint32 offset,           // IN: offset where escape is needed
                     HgfsEscapeReason reason, // IN: reason for escaping
                     void *context)           // IN/OUT: context info
{
   UNREFERENCED_PARAMETER(bufIn);
   UNREFERENCED_PARAMETER(offset);
   if (reason != HGFS_ESCAPE_COMPLETE) {
      uint32 *counter = (uint32*)context;
      (*counter)++;
   }
   return TRUE;
}


#ifdef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * HgfsLetterToUpper --
 *
 *    Converts lowercase English letters to uppercase.
 *    If the symbol is not a lowercase English letter returns the original character.
 *
 * Results:
 *    Converted character.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static char
HgfsLetterToUpper(char letter)
{
   if (letter >= 'a' && letter <= 'z') {
      return letter - ('a' - 'A');
   }
   return letter;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsIsEqualPrefix --
 *
 *    Verifies if the string prefix is equal to the given prefix.
 *    It assumes that the prefix includes only uppercase English letters or numbers
 *    and it does not have any international characters.
 *    The string must be either NULL terminated or not shorter then the prefix.
 *
 * Results:
 *    TRUE if the uppcased string starts with the given prefix. False otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsIsEqualPrefix(char const *prefix,  // IN: prefix to check
                  char const *string,  // IN: input string
                  uint32 prefixLength) // IN: length of the prefix in characters
{
   int i;
   for (i = 0; i < prefixLength; i++) {
      ASSERT(prefix[i] > 0 && (prefix[i] < 'a' || prefix[i] > 'z' ));
      if (prefix[i] != HgfsLetterToUpper(string[i])) {
         return FALSE;
      }
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsIsReservedPrefix --
 *
 *    Verifies if the name's prefix is one of the reserved names.
 *
 * Results:
 *    TRUE if the name's prefix is one of the reserved names.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsIsReservedPrefix(char const *bufIn)  // IN: input name
{
   uint32 i;
   for (i = 0; i < ARRAYSIZE(HgfsReservedNames); i++) {
      if (HgfsIsEqualPrefix(HgfsReservedNames[i], bufIn,
                            HGFS_RESERVED_NAME_CHARS_LENGTH)) {
         return TRUE;
      }
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsIsReservedPrefixWithNumber --
 *
 *    Verifies if the name's prefix is one of the reserved names with number:
 *    COM1-9 or LPT1-9.
 *
 * Results:
 *    TRUE if the name's prefix is one of the reserved names with number.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsIsReservedPrefixWithNumber(char const *bufIn)   // IN: input name
{
   uint32 i;
   for (i = 0; i < ARRAYSIZE(HgfsReservedNamesWithNumber); i++) {
      if (HgfsIsEqualPrefix(HgfsReservedNamesWithNumber[i], bufIn,
                            HGFS_RESERVED_NAME_CHARS_LENGTH) &&
          bufIn[HGFS_RESERVED_NAME_CHARS_LENGTH] >= '1' &&
          bufIn[HGFS_RESERVED_NAME_CHARS_LENGTH] <= '9') {
         return TRUE;
      }
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsIsSpecialCaseEscape --
 *
 *    Verifies if the escape character is a part of special case escape sequence
 *    that exists only in Windows - escaped reserved name or escaped illegal last
 *    character.
 *
 * Results:
 *    TRUE if the name's prefix is one of the reserved names with number.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsIsSpecialCaseEscape(char const *bufIn,   // IN: input name
                        uint32 offset,       // IN: offset of the escape character
                        uint32 length)       // IN: length of the name in characters
{
   if (offset + 1 == length &&
      strchr(HGFS_ILLEGAL_LAST_CHARS, bufIn[offset - 1]) != NULL) {
      return TRUE;
   }
   if (offset == HGFS_RESERVED_NAME_CHARS_LENGTH &&
      (length == HGFS_RESERVED_NAME_CHARS_LENGTH + 1 || bufIn[offset+1] == '.')) {
      return HgfsIsReservedPrefix(bufIn);
   }
   if (offset == HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH &&
      (length == HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH + 1 ||
      bufIn[offset+1] == '.')) {
      return HgfsIsReservedPrefixWithNumber(bufIn);
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsProcessReservedName --
 *
 *    Verifies if the name is one of reserved Windows file names.
 *    If it is a reserved name invokes callback that performs required
 *    processing.
 *
 * Results:
 *    TRUE if no processing is required of if processing succeeded,
 *    FALSE if processing failed.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsProcessReservedName(char const *bufIn,              // IN:  Unescaped input buffer
                        uint32 sizeIn,                  // IN:  Length of the input
                        HgfsEnumCallback processEscape, // IN:  Callack that is invoked
                                                        //      if input is reserved name
                        uint32 *offset,                 // OUT: New offset in the input
                        void *context)                  // IN/OUT: Context for callback
{
   /*  Look reserved names: CON, PRN, AUX, NUL. */
   if (sizeIn >= HGFS_RESERVED_NAME_CHARS_LENGTH && HgfsIsReservedPrefix(bufIn)) {
      if (HGFS_RESERVED_NAME_CHARS_LENGTH == sizeIn ||
         bufIn[HGFS_RESERVED_NAME_CHARS_LENGTH] == '.') {
         if (!processEscape(bufIn, HGFS_RESERVED_NAME_CHARS_LENGTH,
                            HGFS_ESCAPE_RESERVED_NAME, context)) {
            return FALSE;
         }
         *offset = HGFS_RESERVED_NAME_CHARS_LENGTH;
      }
   }

   /*  Look reserved names with numbers: COM1-9 and LPT1-9. */
   if (sizeIn >= HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH &&
       HgfsIsReservedPrefixWithNumber(bufIn)) {
      if (HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH == sizeIn ||
         bufIn[HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH] == '.') {
         if (!processEscape(bufIn, HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH,
                            HGFS_ESCAPE_RESERVED_NAME, context)) {
            return FALSE;
         }
         *offset = HGFS_RESERVED_NAME_WITH_NUMBER_CHARS_LENGTH;
      }
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsProcessLastCharacter --
 *
 *    Verifies if the trailing character in the name is a valid last character.
 *    In Windows it is illegal to have a file name that ends with dot ('.') or
 *    space (' '). The only exception is "." and ".." directory names.
 *    If the last character is invalid the function invokes a callback to process it.
 *
 * Results:
 *    TRUE if no processing is required of if processing succeeded,
 *    FALSE if processing failed.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsProcessLastCharacter(char const *bufIn,              // IN:  Unescaped input buffer
                         uint32 sizeIn,                  // IN:  Length of the input
                         HgfsEnumCallback processEscape, // IN:  Callack that is invoked
                                                         //      when escaping is needed
                         void *context)                  // IN/OUT: Callback context
{

   /* If the filename is '.' or '..' we shouldn't escape it. */
   if ((sizeIn == 1 && bufIn[0] == '.') ||
       (sizeIn == 2 && bufIn[0] == '.' && bufIn[1] == '.')) {
      return TRUE;
   }

   /* Invoke the callback if the last character is illegal. */
   if (strchr(HGFS_ILLEGAL_LAST_CHARS, bufIn[sizeIn - 1]) != NULL) {
      if (!processEscape(bufIn, sizeIn, HGFS_ESCAPE_ILLEGAL_LAST_CHARACTER, context)) {
         return FALSE;
      }
   }
   return TRUE;
}

#endif //  WIN32


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsIsEscapeSequence --
 *
 *    Verifies if input buffer has an escape sequence at the position
 *    defined by offset.
 *
 * Results:
 *    TRUE if there is an escape sequence at the position defined by offset.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsIsEscapeSequence(char const *bufIn,   // IN: input name
                     uint32 offset,       // IN: offset of the escape character
                     uint32 length)       // IN: length of the name in characters
{
   if (bufIn[offset] == HGFS_ESCAPE_CHAR && offset > 0) {
      char *substitute;
      if (bufIn[offset - 1] == HGFS_ESCAPE_SUBSTITUE_CHAR && offset > 1) {
         /*
          * Possibly a valid sequence, check it must be preceded with a substitute
          * character or another escape-escape character. Otherwise, HGFS did
          * not generate this sequence and should leave it alone.
          */
         if (bufIn[offset - 2] == HGFS_ESCAPE_SUBSTITUE_CHAR) {
            return TRUE;
         }
         substitute = strchr(HGFS_SUBSTITUTE_CHARS, bufIn[offset - 2]);
         if (substitute != NULL) {
            return TRUE;
         }
      }
      substitute = strchr(HGFS_SUBSTITUTE_CHARS, bufIn[offset - 1]);
      if (substitute != NULL) {
         return TRUE;
      }
      return IS_SPECIAL_CASE_ESCAPE(bufIn,offset,length);
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsEscapeEnumerate --
 *
 *    The function scans the input buffer and calls processEscape callback for every
 *    place in the input buffer which require escaping.
 *
 *    Callback does the required processing. There are two different callbacks -
 *    one counts extra symbols that are needed for escaping and another produces
 *    escaped output name based on input name.
 *
 *    1. The first function calculates number of extra characters. It just increments
 *    a counter which is passed to it in context variable every time it is called
 *    for the reason different from "complete processing" assuming that
 *    exactly one extra  character is required to escape any invalid input.
 *
 *    2. The second function produces output name by copying everything from input
 *    name into the output name up to the place which require escaping and
 *    then inserts appropriate escape sequence into the output. It keeps track of its
 *    progress and keeps pointer to the output buffer in the context variable.
 *    HgfsEscapeEnumerate calls calback function one more time at the end of the
 *    input buffer to let callback finish processing of the input (for example copy
 *    the rest of the name after the last escape sequence from input buffer to
 *    output buffer).
 *
 * Results:
 *    TRUE if the input has been processed successfully by the callback, false otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsEscapeEnumerate(char const *bufIn,              // IN:  Buffer with unescaped input
                    uint32 sizeIn,                  // IN:  Number of input *characters*
                    HgfsEnumCallback processEscape, // IN: Callack that is invoked every
                                                    //     time escaping is required
                    void *context)                  // IN/OUT: Context for processEscape
{
   /*  First look for invalid characters in the input name. */
   uint32 i, offset = 0;
   if (sizeIn == 0) {
      return TRUE;
   }

   ASSERT(processEscape);

   PROCESS_RESERVED_NAME(bufIn, sizeIn, processEscape, &offset, context);

   for (i = offset; i < sizeIn; i++) {
      if (strchr(HGFS_ILLEGAL_CHARS, bufIn[i]) != NULL) {
         if (!processEscape(bufIn, i, HGFS_ESCAPE_ILLEGAL_CHARACTER, context)) {
            return FALSE;
         }
      } else if (HgfsIsEscapeSequence(bufIn, i, sizeIn)) {
         if (!processEscape(bufIn, i, HGFS_ESCAPE_ESCAPE_SEQUENCE, context)) {
            return FALSE;
         }
      }
   }

   PROCESS_LAST_CHARACTER(bufIn, sizeIn, processEscape, context);

   if (!processEscape(bufIn, sizeIn, HGFS_ESCAPE_COMPLETE, context)) {
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsEscape_Do --
 *
 *    Escape any characters that are not legal in a windows filename.
 *    Escape reserved file names that can't be used in Windows.
 *    We also of course have to escape the escape character, which is "%",
 *    when it is part of a character sequence that would require unescaping
 *
 *    sizeBufOut must account for the NUL terminator.
 *
 * 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
HgfsEscape_Do(char const *bufIn, // IN:  Buffer with unescaped input
              uint32 sizeIn,     // IN:  Size of input buffer
              uint32 sizeBufOut, // IN:  Size of output buffer
              char *bufOut)      // OUT: Buffer for escaped output
{
   const char *currentComponent = bufIn;
   uint32 sizeLeft = sizeBufOut;
   char *outPointer = bufOut;
   const char *end = bufIn + sizeIn;
   const char *next;
   ASSERT(sizeIn > 0);
   if (bufIn[sizeIn - 1] == '\0') {
      /*
       * In some cases a NUL terminated string is passed to HgfsEscape_Do
       * so it make sense to support such input even if CPName_GetComponent
       * does not. Detect this case and make the input compliant with
       * CPName_GetComponent by removing terminating NUL.
       */
      end--;
      sizeIn--;
   }
   /*
    * Absolute symbolic link name starts with the '\0'. HgfsEscapeDo needs to work
    * with such names. Leading NULL symbols should be skipped here since
    * CPName_GetComponent does not support such names.
    */
   while (*currentComponent == '\0' && currentComponent - bufIn < sizeIn) {
      currentComponent++;
      sizeLeft--;
      *outPointer++ = '\0';
   }
   while (currentComponent - bufIn < sizeIn) {
      int escapedLength;
      int componentSize = CPName_GetComponent(currentComponent, end, &next);
      if (componentSize < 0) {
         return componentSize;
      }

      escapedLength = HgfsEscapeDoComponent(currentComponent, componentSize,
                                            sizeLeft, outPointer);
      if (escapedLength < 0) {
         return escapedLength;
      }
      currentComponent = next;
      sizeLeft -= escapedLength + 1;
      outPointer += escapedLength + 1;
   }
   return (int) (outPointer - bufOut) - 1; // Do not count the last NUL terminator
}

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsEscape_GetSize --
 *
 *    Calculates required size in bytes for the buffer that is needed to hold escaped
 *    cross platform path name. Returns 0 if no escaping is required.
 *
 * Results:
 *    On success, the size (excluding the NUL terminator) of the
 *    escaped, NUL terminated buffer.
 *    Returns 0 if the name is a valid Windows file name.
 *    Returns -1 if the name is not a valid file name.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

int
HgfsEscape_GetSize(char const *bufIn,    // IN:  Buffer with unescaped input
                   uint32 sizeIn)        // IN:  Size of the input buffer
{
   uint32 result = 0;
   const char *currentComponent = bufIn;
   const char *end = bufIn + sizeIn;
   const char *next;

   if (sizeIn == 0) { // No need to escape an empty name.
      return 0;
   }
   if (bufIn[sizeIn - 1] == '\0') {
      /*
       * In some cases, a NUL-terminated string is passed to HgfsEscape_GetSize,
       * so it makes sense to support such input even if CPName_GetComponent
       * does not. Detect this case and make the input compliant with
       * CPName_GetComponent by removing the terminating NUL.
       */
      end--;
      sizeIn--;
   }
   /* Skip leading NULs to keep CPName_GetComponent happy. */
   while (*currentComponent == '\0' && currentComponent - bufIn < sizeIn) {
      currentComponent++;
   }
   while (currentComponent - bufIn < sizeIn) {
      int componentSize = CPName_GetComponent(currentComponent, end, &next);
      if (componentSize < 0) {
         Log("%s: failed to calculate escaped name size - name is invalid\n", __FUNCTION__);
         return -1;
      }
      result += HgfsEscapeGetComponentSize(currentComponent, componentSize);
      currentComponent = next;
   }
   return (result == 0) ? 0 : result + sizeIn;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsEscape_Undo --
 *
 *    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
 *
 *-----------------------------------------------------------------------------
 */

uint32
HgfsEscape_Undo(char *bufIn,       // IN: Characters to be unescaped
                uint32 sizeIn)     // IN: Number of characters in bufIn
{
   uint32 componentSize;
   uint32 unprocessedSize = sizeIn + 1;
   uint32 result = 0;
   char *currentComponent = bufIn;

   ASSERT(bufIn != NULL);

   while (currentComponent != NULL) {
      HgfsEscapeUndoComponent(currentComponent, &unprocessedSize);
      componentSize = strlen(currentComponent) + 1; // Unescaped size
      result += componentSize;
      if (unprocessedSize > 1) {
         currentComponent = currentComponent + componentSize;
      } else {
         currentComponent = NULL;
      }
   }
   return result - 1;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsEscapeUndoComponent --
 *
 *    Unescape a buffer that was escaped using HgfsEscapeBuffer.
 *
 *    The unescaping is done in place in the input buffer, and
 *    can not fail.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsEscapeUndoComponent(char   *bufIn,             // IN: Characters to be unescaped
                        uint32 *unprocessedLength) // IN: Unprocessed characters
                                                   //     in the whole name
{
   size_t sizeIn;
   char* curOutBuffer;
   char* escapePointer;

   ASSERT(bufIn != NULL);

   curOutBuffer = bufIn;
   sizeIn = strlen(curOutBuffer);
   escapePointer = strchr(curOutBuffer, HGFS_ESCAPE_CHAR);
   while (escapePointer != NULL) {
      size_t offset = escapePointer - bufIn;

      if (HgfsIsEscapeSequence(bufIn, offset, sizeIn)) {
         char* substitute = strchr(HGFS_SUBSTITUTE_CHARS, bufIn[offset - 1]);
         if (substitute != NULL) {
            bufIn[offset - 1] = HGFS_ILLEGAL_CHARS[substitute - HGFS_SUBSTITUTE_CHARS];
         } else if (bufIn[offset - 1] == HGFS_ESCAPE_SUBSTITUE_CHAR) {
            bufIn[offset - 1] = HGFS_ESCAPE_CHAR;
         }
         memmove(escapePointer, escapePointer + 1, (*unprocessedLength) - offset - 1);
         (*unprocessedLength)--;
         sizeIn--;
         if (sizeIn > 0) {
            escapePointer = strchr(escapePointer, HGFS_ESCAPE_CHAR);
         } else {
            escapePointer = NULL;
         }
      } else {
         escapePointer = strchr(escapePointer + 1, HGFS_ESCAPE_CHAR);
      }
   }
   ASSERT((*unprocessedLength) > sizeIn);
   (*unprocessedLength) -= sizeIn + 1;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsEscapeDoComponent --
 *
 *    Escape any characters that are not legal in a windows filename.
 *    Escape reserved file names that can't be used in Windows.
 *    We also of course have to escape the escape character, which is "%",
 *    when it is part of a character sequence that would require unescaping
 *
 *    sizeBufOut must account for the NUL terminator.
 *
 * 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
HgfsEscapeDoComponent(char const *bufIn, // IN:  Buffer with unescaped input
	                   uint32 sizeIn,     // IN:  Size of input buffer
	                   uint32 sizeBufOut, // IN:  Size of output buffer
	                   char *bufOut)      // OUT: Buffer for escaped output
{
   HgfsEscapeContext conversionContext;
   conversionContext.processedOffset = 0;
   conversionContext.outputBufferLength = sizeBufOut / sizeof *bufOut;
   conversionContext.outputOffset = 0;
   conversionContext.outputBuffer = bufOut;

   if (!HgfsEscapeEnumerate(bufIn, sizeIn, HgfsAddEscapeCharacter, &conversionContext)) {
      return -1;
   }
   return conversionContext.outputOffset * sizeof *bufOut;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsEscapeGetComponentSize --
 *
 *    Calculates number of addtitional characters that are needed to escape
 *    name for one NUL terminated component of the path.
 *
 * Results:
 *    Number of additional escape characters needed to escape the name.
 *    Returns 0 if no escaping is required.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

int
HgfsEscapeGetComponentSize(char const *bufIn, // IN:  Buffer with unescaped input
                           uint32 sizeIn)     // IN:  Size of the in input buffer
{
   int result = 0;
   HgfsEscapeEnumerate(bufIn, sizeIn, HgfsCountEscapeChars, &result);
   return result;
}
  07070100000073000081A40000000000000000000000016822550500001FA6000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfs/hgfsUtil.c    /*********************************************************
 * Copyright (c) 1998-2016,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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
{
   ASSERT(unixTime);

   if (ntTime < UNIX_EPOCH) {
      unixTime->tv_sec = 0;
      unixTime->tv_nsec = 0;
      return -1;
   }

#ifdef __i386__
   if (sizeof unixTime->tv_sec == 4) {
      uint32 sec,nsec;

      /* 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;
      }

      Div643232(ntTime - UNIX_EPOCH, 10000000, &sec, &nsec);
      unixTime->tv_sec = sec;
      unixTime->tv_nsec = nsec * 100;

      return 0;
   }
#endif

   unixTime->tv_sec = (ntTime - UNIX_EPOCH) / 10000000;
   unixTime->tv_nsec = ((ntTime - UNIX_EPOCH) % 10000000) * 100;

   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 ERROR_INVALID_PARAMETER:
      return HGFS_STATUS_INVALID_PARAMETER;
   case ERROR_NOT_SAME_DEVICE:
      return HGFS_STATUS_NOT_SAME_DEVICE;
   case ERROR_FILENAME_EXCED_RANGE:
      return HGFS_STATUS_NAME_TOO_LONG;
   case ERROR_CONNECTION_INVALID:  // HGFS_ERROR_STALE_SESSION
      return HGFS_STATUS_STALE_SESSION;
   case ERROR_MAX_SESSIONS_REACHED:
      return HGFS_STATUS_TOO_MANY_SESSIONS;
   case ERROR_INTERNAL_ERROR:
   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 EISDIR:
   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 EPARAMETERNOTSUPPORTED:
      return HGFS_STATUS_INVALID_PARAMETER;
   case EXDEV:
      return HGFS_STATUS_NOT_SAME_DEVICE;
   case ENETRESET:  // HGFS_ERROR_STALE_SESSION
      return HGFS_STATUS_STALE_SESSION;
   case ECONNREFUSED:
      return HGFS_STATUS_TOO_MANY_SESSIONS;
   case EINTERNAL:
   case HGFS_INTERNAL_STATUS_ERROR:
   default:
      return HGFS_STATUS_GENERIC_ERROR;
   }
}
#endif
  07070100000074000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsBd 07070100000075000081A400000000000000000000000168225505000003D9000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsBd/Makefile.am ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libHgfsBd.la

libHgfsBd_la_SOURCES =
libHgfsBd_la_SOURCES += hgfsBd.c
   07070100000076000081A40000000000000000000000016822550500002990000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsBd/hgfsBd.c    /*********************************************************
 * Copyright (C) 1998-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * hgfsBd.c --
 *
 *    Backdoor calls used by hgfs pserver. [bac]
 */

#if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL)
#   include "kernelStubs.h"
#else
#   include <stdio.h>
#   include <stdlib.h>
#   include <string.h>
#   include <errno.h>
#   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(HgfsLargePacketMax(FALSE));
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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;
   Bool rpcStatus;
   char const *reply;
   size_t replyLen;
   char *bdPacket = packetIn - HGFS_SYNC_REQREP_CLIENT_CMD_LEN;

   ASSERT(out);
   ASSERT(packetIn);
   ASSERT(packetSize);
   ASSERT(packetOut);

   memcpy(bdPacket, HGFS_SYNC_REQREP_CLIENT_CMD, HGFS_SYNC_REQREP_CLIENT_CMD_LEN);

   success = RpcOut_send(out, bdPacket, *packetSize + HGFS_CLIENT_CMD_LEN,
                         &rpcStatus, &reply, &replyLen);
   if (!success || !rpcStatus) {
      Debug("HgfsBd_Dispatch: RpcOut_send returned failure\n");
      return -1;
   }

   ASSERT(replyLen <= HgfsLargePacketMax(TRUE));
   *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 replyLen;
   Bool success;
   Bool rpcStatus;

   /*
    * 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).
    */
   success = RpcOut_send(out, requestPacket - HGFS_CLIENT_CMD_LEN,
                         HGFS_CLIENT_CMD_LEN,
                         &rpcStatus, &replyPacket, &replyLen);
   if (success && rpcStatus) {
      ASSERT(replyLen <= HgfsLargePacketMax(TRUE));
   }

   return success && rpcStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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;
}
07070100000077000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsHelper 07070100000078000081A400000000000000000000000168225505000003EF000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsHelper/Makefile.am ################################################################################
### Copyright (C) 2009-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libHgfsHelper.la

libHgfsHelper_la_SOURCES =
libHgfsHelper_la_SOURCES += hgfsHelperPosix.c

 07070100000079000081A40000000000000000000000016822550500000AEF000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsHelper/hgfsHelperPosix.c   /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsHelperPosix.c --
 *
 *    Provides a posix helper library for guest applications to access
 *    the HGFS file system.
 *
 */

#if !defined __linux__ && !defined __FreeBSD__ && !defined sun && !defined __APPLE__
#   error This file should not be compiled
#endif

#include "vmware.h"
#include "debug.h"

#include "hgfsHelper.h"

#if defined __linux__
#define HGFSHLPR_DEFAULT_MOUNT_PATH      "/mnt/hgfs"
#elif defined sun
#define HGFSHLPR_DEFAULT_MOUNT_PATH      "/hgfs"
#elif defined __APPLE__
#define HGFSHLPR_DEFAULT_MOUNT_PATH      "/Volumes/VMware Shared Folders"
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHlpr_QuerySharesDefaultRootPath --
 *
 *      Queries the driver for its share's root paths.
 *      Currently only one is expected to be supported
 *      and returned, although later versions may not.
 *      E.g. "/mnt/hgfs" is the root path to
 *      the HGFS shares.
 *
 * Results:
 *      TRUE always.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHlpr_QuerySharesDefaultRootPath(char **hgfsRootPath)
{
#if defined __FreeBSD__
   return FALSE;
#else
   ASSERT(hgfsRootPath != NULL);

   *hgfsRootPath = Unicode_AllocWithUTF8(HGFSHLPR_DEFAULT_MOUNT_PATH);

   Debug("%s: HGFS shares root path name \"%s\"\n",
         __FUNCTION__, *hgfsRootPath);

   return TRUE;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHlpr_FreeSharesRootPath --
 *
 *      Frees the share's root paths previously returned
 *      to the caller from the HgfsHlpr_QuerySharesRootPath.
 *
 * Results:
 *      None.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsHlpr_FreeSharesRootPath(char *hgfsRootPath)
{
   free(hgfsRootPath);
}
 0707010000007A000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer 0707010000007B000081A400000000000000000000000168225505000005F5000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/Makefile.am ################################################################################
### Copyright (C) 2007-2016, 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libHgfsServer.la

libHgfsServer_la_SOURCES =
libHgfsServer_la_SOURCES += hgfsCacheStub.c
libHgfsServer_la_SOURCES += hgfsServer.c
libHgfsServer_la_SOURCES += hgfsServerLinux.c
libHgfsServer_la_SOURCES += hgfsServerPacketUtil.c
libHgfsServer_la_SOURCES += hgfsDirNotifyStub.c
libHgfsServer_la_SOURCES += hgfsServerParameters.c
libHgfsServer_la_SOURCES += hgfsServerOplock.c
libHgfsServer_la_SOURCES += hgfsServerOplockMonitor.c
libHgfsServer_la_SOURCES += hgfsServerOplockLinux.c
libHgfsServer_la_SOURCES += hgfsThreadpoolStub.c

AM_CFLAGS =
AM_CFLAGS += -DVMTOOLS_USE_GLIB
AM_CFLAGS += @GLIB2_CPPFLAGS@

   0707010000007C000081A40000000000000000000000016822550500000681000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsCache.h /*********************************************************
 * Copyright (C) 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsCache.h --
 *
 *    A customized LRU cache which is built by combining two data structures:
 *    a doubly linked list and a hash table.
 */

#ifndef _HGFS_CACHE_H_
#define _HGFS_CACHE_H_

#include "dbllnklst.h"
#include "userlock.h"

typedef void(*HgfsCacheRemoveLRUCallback)(void *data);

typedef struct HgfsCache {
   void *hashTable;
   DblLnkLst_Links links;
   MXUserExclLock *lock;
   HgfsCacheRemoveLRUCallback callback;
} HgfsCache;

HgfsCache *HgfsCache_Alloc(HgfsCacheRemoveLRUCallback callback);
void HgfsCache_Destroy(HgfsCache *cache);
void HgfsCache_Put(HgfsCache *cache, const char *key, void *data);
Bool HgfsCache_Get(HgfsCache *cache, const char *key, void **data);
Bool HgfsCache_Invalidate(HgfsCache *cache, const char *key);

#endif // ifndef _HGFS_CACHE_H_
   0707010000007D000081A40000000000000000000000016822550500000CB9000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsCacheStub.c /*********************************************************
 * Copyright (C) 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsCacheStub.c --
 *
 *    This file contains the stub implementation for hgfs cache.
 */

#include "hgfsCache.h"
#include "hgfsServerInt.h"


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCache_Alloc --
 *
 *      Create a cache and the corresponding hash table/doubly linked list/lock.
 *
 * Results:
 *      Always return NULL.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

HgfsCache *
HgfsCache_Alloc(HgfsCacheRemoveLRUCallback callback) // IN
{
   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCache_Destroy --
 *
 *      Destroy a cache and the corresponding hash table/doubly linked list/lock.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsCache_Destroy(HgfsCache *cache)                    // IN
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCache_Put --
 *
 *      Put an entry into a cache.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsCache_Put(HgfsCache *cache,                    // IN
              const char *key,                     // IN
              void *data)                          // IN
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCache_Get --
 *
 *      Get an entry in a cache.
 *
 * Results:
 *      Always return FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsCache_Get(HgfsCache *cache, // IN
              const char *key,  // IN
              void **data)      // OUT
{
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCache_Invalidate --
 *
 *      Remove an entry from a cache.
 *
 * Results:
 *      Always return FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsCache_Invalidate(HgfsCache *cache, // IN
                     const char *key)  // IN
{
   return FALSE;
}
   0707010000007E000081A40000000000000000000000016822550500000DDB000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsDirNotify.h /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _HGFS_DIRNOTIFY_H
#define _HGFS_DIRNOTIFY_H

/*
 * hgfsDirNotify.h --
 *
 *	Function definitions for directory change notification.
 */

#include "hgfsServer.h" // for HgfsSharedFolderHandle
#include "hgfsProto.h"  // for HgfsSubscriberHandle
#include "hgfsUtil.h"   // for HgfsInternalStatus

struct HgfsSessionInfo;
/*
 * Activate and deactivate reason.
 * Currently, there are two scenarios:
 * 1) HGFS server is check point synchronizing: the file system event
 * generation is deactivated at the start and activated at the end.
 * 2) The client has added the first subscriber or removed the last
 * subscriber. The file system event generation is activated on the
 * addition of the first subscriber and deactivated on removal of
 * the last one.
 *
 * Note, in case 1 above, if there are no subscribers even at the end
 * of the HGFS server check point syncing, the activation will not
 * activate the file system events.
 */
typedef enum {
   HGFS_NOTIFY_REASON_SERVER_SYNC,
   HGFS_NOTIFY_REASON_SUBSCRIBERS,
} HgfsNotifyActivateReason;

/* These are the callbacks that are implemented in hgfsServer.c */
typedef void (*HgfsNotifyEventReceiveCb)(HgfsSharedFolderHandle sharedFolder,
                                         HgfsSubscriberHandle subscriber,
                                         char *name,
                                         uint32 mask,
                                         struct HgfsSessionInfo *session);

typedef struct HgfsServerNotifyCallbacks {
   HgfsNotifyEventReceiveCb       eventReceive;
} HgfsServerNotifyCallbacks;

HgfsInternalStatus HgfsNotify_Init(const HgfsServerNotifyCallbacks *serverCbData);
void HgfsNotify_Exit(void);
void HgfsNotify_Deactivate(HgfsNotifyActivateReason mode,
                           struct HgfsSessionInfo *session);
void HgfsNotify_Activate(HgfsNotifyActivateReason mode,
                         struct HgfsSessionInfo *session);

HgfsSharedFolderHandle HgfsNotify_AddSharedFolder(const char *path,
                                                  const char *shareName);
HgfsSubscriberHandle HgfsNotify_AddSubscriber(HgfsSharedFolderHandle sharedFolder,
                                              const char *path,
                                              uint32 eventFilter,
                                              uint32 recursive,
                                              struct HgfsSessionInfo *session);

Bool HgfsNotify_RemoveSharedFolder(HgfsSharedFolderHandle sharedFolder);
Bool HgfsNotify_RemoveSubscriber(HgfsSubscriberHandle subscriber);
void HgfsNotify_RemoveSessionSubscribers(struct HgfsSessionInfo *session);

#endif // _HGFS_DIRNOTIFY_H
 0707010000007F000081A400000000000000000000000168225505000017C7000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsDirNotifyStub.c /*********************************************************
 * Copyright (C) 2009-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsDirNotifyStub.c --
 *
 *	Stubs for directory notification support, used to build guest components.
 */

#include <stdio.h>

#include "vmware.h"
#include "vm_basic_types.h"

#include "hgfsProto.h"
#include "hgfsServer.h"
#include "hgfsUtil.h"
#include "hgfsDirNotify.h"


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_Init --
 *
 *    Initialization for the notification component.
 *
 * Results:
 *    Invalid value error.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsNotify_Init(const HgfsServerNotifyCallbacks *serverCbData) // IN: serverCbData unused
{
   return HGFS_ERROR_NOT_SUPPORTED;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_Exit --
 *
 *    Exit for the notification component.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsNotify_Exit(void)
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_Activate --
 *
 *    Activates generating file system change notifications.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsNotify_Activate(HgfsNotifyActivateReason reason, // IN: reason
                    struct HgfsSessionInfo *session) // IN: session
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_Deactivate --
 *
 *    Deactivates generating file system change notifications.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsNotify_Deactivate(HgfsNotifyActivateReason reason, // IN: reason
                      struct HgfsSessionInfo *session) // IN: session
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_AddSharedFolder --
 *
 *    Allocates memory and initializes new shared folder structure.
 *
 * Results:
 *    Opaque subscriber handle for the new subscriber or HGFS_INVALID_FOLDER_HANDLE
 *    if adding shared folder fails.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsSharedFolderHandle
HgfsNotify_AddSharedFolder(const char *path,       // IN: path in the host
                           const char *shareName)  // IN: name of the shared folder
{
   return HGFS_INVALID_FOLDER_HANDLE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_AddSubscriber --
 *
 *    Allocates memory and initializes new subscriber structure.
 *    Inserts allocated subscriber into corrspondent array.
 *
 * Results:
 *    Opaque subscriber handle for the new subscriber or HGFS_INVALID_SUBSCRIBER_HANDLE
 *    if adding subscriber fails.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsSubscriberHandle
HgfsNotify_AddSubscriber(HgfsSharedFolderHandle sharedFolder, // IN: shared folder handle
                         const char *path,                    // IN: relative path
                         uint32 eventFilter,                  // IN: event filter
                         uint32 recursive,                    // IN: look in subfolders
                         struct HgfsSessionInfo *session)     // IN: server context
{
   return HGFS_INVALID_SUBSCRIBER_HANDLE;
}

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_RemoveSharedFolder --
 *
 *    Deallcates memory used by shared folder and performs necessary cleanup.
 *    Also deletes all subscribers that are defined for the shared folder.
 *
 * Results:
 *    FALSE.
 *
 * Side effects:
 *    Removes all subscribers that correspond to the shared folder and invalidates
 *    thier handles.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsNotify_RemoveSharedFolder(HgfsSharedFolderHandle sharedFolder) // IN
{
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_RemoveSubscriber --
 *
 *    Deallcates memory used by NotificationSubscriber and performs necessary cleanup.
 *
 * Results:
 *    FALSE.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsNotify_RemoveSubscriber(HgfsSubscriberHandle subscriber) // IN
{
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsNotify_RemoveSessionSubscribers --
 *
 *    Removes all entries that are related to a particular session.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsNotify_RemoveSessionSubscribers(struct HgfsSessionInfo *session) // IN
{
}
 07070100000080000081A4000000000000000000000001682255050004B5DB000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServer.c    /*********************************************************
 * Copyright (c) 1998-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#if defined(__APPLE__)
/*
 * DirectoryEntry type that is used in common code is
 * defined as dirent for Mac OS.
 * _DARWIN_USE_64_BIT_INODE definition is needed to make dirent
 * structure definitions in this file and in HgfsServerLinux.c
 * consistent.
 */
#define _DARWIN_USE_64_BIT_INODE
#endif

#include <string.h>
#include <stdlib.h>

#include "vmware.h"
#include "str.h"
#include "cpName.h"
#include "cpNameLite.h"
#include "hashTable.h"
#include "hgfsServerInt.h"
#include "hgfsServerPolicy.h"
#include "hgfsUtil.h"
#include "hgfsVirtualDir.h"
#include "codeset.h"
#include "dbllnklst.h"
#include "file.h"
#include "util.h"
#include "wiper.h"
#include "hgfsServer.h"
#include "hgfsServerParameters.h"
#include "hgfsServerOplock.h"
#include "hgfsServerOplockMonitor.h"
#include "hgfsDirNotify.h"
#include "hgfsThreadpool.h"
#include "userlock.h"
#include "poll.h"
#include "mutexRankLib.h"
#include "vm_basic_asm.h"
#include "unicodeOperations.h"

#ifndef VM_X86_ANY
#include "random.h"
#endif

#if defined(_WIN32)
#include <io.h>
#define HGFS_PARENT_DIR "..\\"
#else
#include <unistd.h>
#define stricmp strcasecmp
#define HGFS_PARENT_DIR "../"
#endif // _WIN32
#define HGFS_PARENT_DIR_LEN 3


/*
 * Define this to enable an ASSERT on HGFS_STATUS_PROTOCOL_ERROR.
 * This is useful if client is to be guaranteed to work with the server
 * without falling back to older protocol versions and to ensure that
 * clients don't send op value greater than HGFS_OP_MAX.
 *
 * NOTE: This flag is only meant to be used while testing. This should
 *       _always_ be undefined when checking code in.
 */
#if 0
#define HGFS_ASSERT_CLIENT(op) \
   do { \
   LOG(4, "%s: op: %u.\n", __FUNCTION__, op); \
   ASSERT(status != HGFS_STATUS_PROTOCOL_ERROR); \
   } while(0)
#else
#define HGFS_ASSERT_CLIENT(op)
#endif

#define HGFS_ASSERT_INPUT(input) \
   ASSERT(input && input->packet && input->request && \
          ((!input->sessionEnabled && input->session) || \
          (input->sessionEnabled && \
          (input->op == HGFS_OP_CREATE_SESSION_V4 || input->session))) && \
          (!input->payloadSize || input->payload))

/*
 * Define this to enable an ASSERT if server gets an op lower than
 * this value. This is useful if client is to be guaranteed to work with
 * the server without falling back to older protocol versions.
 *
 * NOTE: This flag is only meant to be used while testing. This should
 *       _always_ be undefined when checking code in.
 */
#if 0
#define HGFS_ASSERT_MINIMUM_OP(op) \
   do { \
      LOG(4, "%s: op received - %u.\n", __FUNCTION__, op); \
      ASSERT(op >= HGFS_OP_OPEN_V3); \
   } while(0)
#else
#define HGFS_ASSERT_MINIMUM_OP(op)
#endif

/*
 * This ensures that the hgfs name conversion code never fails on long
 * filenames by using a buffer that is too small. If anything, we will
 * fail first elsewhere because the name is too big to fit in one hgfs
 * packet. [bac]
 */
#define HGFS_PATH_MAX HGFS_PACKET_MAX

/*
 * Array of FileNodes for opening files.
 */
#define NUM_FILE_NODES 100
#define NUM_SEARCHES 100

/* Default maximun number of open nodes that have server locks. */
#define MAX_LOCKED_FILENODES 10


struct HgfsTransportSessionInfo {
   /* Default session id. */
   uint64 defaultSessionId;

   /* Lock to manipulate the list of sessions */
   MXUserExclLock *sessionArrayLock;

   /* List of sessions */
   DblLnkLst_Links sessionArray;

   /* Max packet size that is supported by both client and server. */
   uint32 maxPacketSize;

   /* Total number of sessions present this transport session*/
   uint32 numSessions;

   /* Transport session context. */
   void *transportData;

   /* Current state of the session. */
   HgfsSessionInfoState state;

   /* Session is dynamic or internal. */
   HgfsSessionInfoType type;

   /* Function callbacks into Hgfs Channels. */
   HgfsServerChannelCallbacks *channelCbTable;

   Atomic_uint32 refCount;    /* Reference count for session. */

   HgfsServerChannelData channelCapabilities;
};

/* The input request parameters object. */
typedef struct HgfsInputParam {
   const void *request;          /* Hgfs header followed by operation request */
   size_t requestSize;           /* Size of Hgfs header and operation request */
   HgfsSessionInfo *session;     /* Hgfs session data */
   HgfsTransportSessionInfo *transportSession;
   HgfsPacket *packet;           /* Public (server/transport) Hgfs packet */
   const void *payload;          /* Hgfs operation request */
   uint32 payloadOffset;         /* Offset to start of Hgfs operation request */
   size_t payloadSize;           /* Hgfs operation request size */
   HgfsOp op;                    /* Hgfs operation command code */
   uint32 id;                    /* Request ID to be matched with the reply */
   Bool sessionEnabled;          /* Requests have session enabled headers */
} HgfsInputParam;

/*
 * The HGFS server configurable settings.
 * (Note: the guest sets these to all defaults only modifiable from the VMX.)
 */
static HgfsServerConfig gHgfsCfgSettings = {
   (HGFS_CONFIG_NOTIFY_ENABLED | HGFS_CONFIG_VOL_INFO_MIN),
   HGFS_MAX_CACHED_FILENODES
};

/*
 * Monotonically increasing handle counter used to dish out HgfsHandles.
 * This value is checkpointed.
 */
static Atomic_uint32 hgfsHandleCounter = {0};

static HgfsServerMgrCallbacks *gHgfsMgrData = NULL;


/*
 * Session usage and locking.
 *
 * The channel will serialize callbacks to connect, disconnect, close
 * and invalidate objects for sessions.
 * The receives will also be serialized with the above when received through
 * the backdoor channel.
 * However, when requests are received from a socket, they will be from a
 * worker thread. It is the responsibility of the socket channel to keep
 * the session alive when processing the receive request which it does by
 * an additional reference for the session. This means even if a disconnect
 * occurs and the socket is closed, the channel will not call the session
 * close until the hgfs server returns from the receive processing. Thus
 * the hgfs server session data will remain valid.
 * When the hgfs server processes requests asynchronously, or returns from
 * receive request prior to sending the reply to be done at a later time,
 * a reference on the session is taken out while processing the message,
 * and not removed until the reply is processed. This reference will ensure
 * the session is not torndown until the final reference is removed, even
 * if the close session is called from the channel.
 */

/* Session related callbacks. */
static void HgfsServerSessionReceive(HgfsPacket *packet,
                                     void *clientData);
static Bool HgfsServerSessionConnect(void *transportData,
                                     HgfsServerChannelCallbacks *channelCbTable,
                                     HgfsServerChannelData *channelCapabililies,
                                     void **clientData);
static void HgfsServerSessionDisconnect(void *clientData);
static void HgfsServerSessionClose(void *clientData);
static void HgfsServerSessionInvalidateObjects(void *clientData,
                                               DblLnkLst_Links *shares);
static uint32 HgfsServerSessionInvalidateInactiveSessions(void *clientData);
static void HgfsServerSessionSendComplete(HgfsPacket *packet, void *clientData);
static void HgfsServerSessionQuiesce(void * clientData, HgfsQuiesceOp quiesceOp);

/*
 * Callback table passed to transport and any channels.
 */
static const HgfsServerCallbacks gHgfsServerCBTable = {
   {
      HgfsServerSessionConnect,
      HgfsServerSessionDisconnect,
      HgfsServerSessionClose,
      HgfsServerSessionReceive,
      HgfsServerSessionInvalidateObjects,
      HgfsServerSessionInvalidateInactiveSessions,
      HgfsServerSessionSendComplete,
      HgfsServerSessionQuiesce,
   },
};


static void HgfsServerNotifyReceiveEventCb(HgfsSharedFolderHandle sharedFolder,
                                           HgfsSubscriberHandle subscriber,
                                           char* fileName,
                                           uint32 mask,
                                           struct HgfsSessionInfo *session);

/*
 * Callback table passed to the directory change notification component.
 */
static const HgfsServerNotifyCallbacks gHgfsServerNotifyCBTable = {
   HgfsServerNotifyReceiveEventCb,
};

/* Lock that protects shared folders list. */
static MXUserExclLock *gHgfsSharedFoldersLock = NULL;

/* List of shared folders nodes. */
static DblLnkLst_Links gHgfsSharedFoldersList;

/*
 * Number of active sessions that support change directory notification. HGFS server
 * needs to maintain up-to-date shared folders list when there is
 * at least one such session.
 */
static Bool gHgfsDirNotifyActive = FALSE;

/*
 * Indicates if the threadpool is active. If so then all the asynchronous IO will be
 * handled by worker thread in threadpool. If threadpool is not active, all the
 * asynchronous IO will be handled by poll.
 */
static Bool gHgfsThreadpoolActive = FALSE;

typedef struct HgfsSharedFolderProperties {
   DblLnkLst_Links links;
   char *name;                                /* Name of the share. */
   HgfsSharedFolderHandle notificationHandle; /* Directory notification handle. */
} HgfsSharedFolderProperties;


/* Allocate/Add sessions helper functions. */
#ifndef VMX86_TOOLS
static void
HgfsServerAsyncInfoIncCount(HgfsAsyncRequestInfo *info);
#endif

static Bool
HgfsServerAllocateSession(HgfsTransportSessionInfo *transportSession,
                          HgfsCreateSessionInfo createSessionInfo,
                          HgfsSessionInfo **sessionData);
static HgfsInternalStatus
HgfsServerTransportAddSessionToList(HgfsTransportSessionInfo *transportSession,
                                    HgfsSessionInfo *sessionInfo);
static void
HgfsServerTransportRemoveSessionFromList(HgfsTransportSessionInfo *transportSession,
                                         HgfsSessionInfo *sessionInfo);
static HgfsSessionInfo *
HgfsServerTransportGetSessionInfo(HgfsTransportSessionInfo *transportSession,
                                  uint64 sessionId);
static HgfsTransportSessionInfo *
HgfsServerTransportInit(void *transportData,
                        HgfsServerChannelCallbacks *channelCbTable,
                        HgfsServerChannelData *channelCapabilities);
static void
HgfsServerTransportExit(HgfsTransportSessionInfo *transportSession);

/* Local functions. */
static void HgfsInvalidateSessionObjects(DblLnkLst_Links *shares,
                                         HgfsSessionInfo *session);
static Bool HgfsAddToCacheInternal(HgfsHandle handle,
                                   HgfsSessionInfo *session);
static Bool HgfsIsCachedInternal(HgfsHandle handle,
                                 HgfsSessionInfo *session);
static Bool HgfsRemoveLruNode(HgfsSessionInfo *session);
static Bool HgfsRemoveFromCacheInternal(HgfsHandle handle,
                                        HgfsSessionInfo *session);
static void HgfsRemoveSearchInternal(HgfsSearch *search,
                                     HgfsSessionInfo *session);
static HgfsSearch *HgfsSearchHandle2Search(HgfsHandle handle,
                                           HgfsSessionInfo *session);
static HgfsHandle HgfsSearch2SearchHandle(HgfsSearch const *search);
static HgfsSearch *HgfsAddNewSearch(char const *utf8Dir,
                                    DirectorySearchType type,
                                    char const *utf8ShareName,
                                    char const *rootDir,
                                    HgfsSessionInfo *session);
static void HgfsDumpAllSearches(HgfsSessionInfo *session);
static void HgfsDumpAllNodes(HgfsSessionInfo *session);
static void HgfsFreeFileNode(HgfsHandle handle,
                             HgfsSessionInfo *session);
static void HgfsFreeFileNodeInternal(HgfsHandle handle,
                                     HgfsSessionInfo *session);
static HgfsFileNode *HgfsAddNewFileNode(HgfsFileOpenInfo *openInfo,
                                        HgfsLocalId const *localId,
                                        fileDesc fileDesc,
                                        Bool append,
                                        size_t shareNameLen,
                                        char const *shareName,
                                        Bool sharedFolderOpen,
                                        HgfsSessionInfo *session);
static void HgfsRemoveFileNode(HgfsFileNode *node,
                               HgfsSessionInfo *session);
static HgfsFileNode *HgfsGetNewNode(HgfsSessionInfo *session);
static HgfsHandle HgfsFileNode2Handle(HgfsFileNode const *fileNode);
static HgfsFileNode *HgfsHandle2FileNode(HgfsHandle handle,
                                         HgfsSessionInfo *session);
static void HgfsServerExitSessionInternal(HgfsSessionInfo *session);
static void HgfsServerCompleteRequest(HgfsInternalStatus status,
                                      size_t replyPayloadSize,
                                      HgfsInputParam *input);
static Bool HgfsHandle2NotifyInfo(HgfsHandle handle,
                                  HgfsSessionInfo *session,
                                  char **fileName,
                                  size_t *fileNameSize,
                                  HgfsSharedFolderHandle *folderHandle);
static void HgfsFreeSearchDirents(HgfsSearch *search);

static HgfsInternalStatus
HgfsServerTransportGetDefaultSession(HgfsTransportSessionInfo *transportSession,
                                     HgfsSessionInfo **session);
static Bool HgfsPacketSend(HgfsPacket *packet,
                           HgfsTransportSessionInfo *transportSession,
                           HgfsSessionInfo *session,
                           HgfsSendFlags flags);

static void HgfsCacheRemoveLRUCb(void *data);

/*
 * Opcode handlers
 */

static void HgfsServerOpen(HgfsInputParam *input);
static void HgfsServerRead(HgfsInputParam *input);
static void HgfsServerWrite(HgfsInputParam *input);
static void HgfsServerSearchOpen(HgfsInputParam *input);
static void HgfsServerSearchRead(HgfsInputParam *input);
static void HgfsServerGetattr(HgfsInputParam *input);
static void HgfsServerSetattr(HgfsInputParam *input);
static void HgfsServerCreateDir(HgfsInputParam *input);
static void HgfsServerDeleteFile(HgfsInputParam *input);
static void HgfsServerDeleteDir(HgfsInputParam *input);
static void HgfsServerRename(HgfsInputParam *input);
static void HgfsServerQueryVolume(HgfsInputParam *input);
static void HgfsServerSymlinkCreate(HgfsInputParam *input);
static void HgfsServerServerLockChange(HgfsInputParam *input);
static void HgfsServerWriteWin32Stream(HgfsInputParam *input);
static void HgfsServerCreateSession(HgfsInputParam *input);
static void HgfsServerDestroySession(HgfsInputParam *input);
static void HgfsServerClose(HgfsInputParam *input);
static void HgfsServerSearchClose(HgfsInputParam *input);
static void HgfsServerSetDirNotifyWatch(HgfsInputParam *input);
static void HgfsServerRemoveDirNotifyWatch(HgfsInputParam *input);


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerSessionGet --
 *
 *      Increment session reference count.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsServerSessionGet(HgfsSessionInfo *session)   // IN: session context
{
   ASSERT(session && Atomic_Read(&session->refCount) != 0);
   Atomic_Inc(&session->refCount);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerSessionPut --
 *
 *      Decrement session reference count.
 *
 *      Free session info data if no reference.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsServerSessionPut(HgfsSessionInfo *session)   // IN: session context
{
   ASSERT(session);

   if (Atomic_ReadDec32(&session->refCount) == 1) {
      HgfsServerExitSessionInternal(session);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerTransportSessionGet --
 *
 *      Increment transport session reference count.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsServerTransportSessionGet(HgfsTransportSessionInfo *transportSession)   // IN: session context
{
   ASSERT(transportSession);
   Atomic_Inc(&transportSession->refCount);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerTransportSessionPut --
 *
 *      Decrement transport session reference count.
 *
 *      Free session info data if no reference.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsServerTransportSessionPut(HgfsTransportSessionInfo *transportSession)   // IN: transport session context
{
   ASSERT(transportSession);
   if (Atomic_ReadDec32(&transportSession->refCount) == 1) {
      HgfsServerTransportExit(transportSession);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerInitHandleCounter --
 *
 *    Initialize the file handle counter to the new value passed.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerInitHandleCounter(uint32 newHandleCounter)
{
   Atomic_Write(&hgfsHandleCounter, newHandleCounter);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetHandleCounter --
 *
 *    Return file handle counter. This is used by the checkpointing code to
 *    checkpoint this value so we avoid the risk of handle collision.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static uint32
HgfsServerGetHandleCounter(void)
{
   return Atomic_Read(&hgfsHandleCounter);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetNextHandleCounter --
 *
 *    Return file handle counter. This is used by the checkpointing code to
 *    checkpoint this value so we avoid the risk of handle collision.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static uint32
HgfsServerGetNextHandleCounter(void)
{
   return Atomic_ReadInc32(&hgfsHandleCounter);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2FileNode --
 *
 *    Retrieve the file node a handle refers to.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    The file node if the handle is valid (i.e. it refers to an existing file
 *    node that is currently in use).
 *    NULL if the handle is invalid.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static HgfsFileNode *
HgfsHandle2FileNode(HgfsHandle handle,        // IN: Hgfs file handle
                    HgfsSessionInfo *session) // IN: Session info
{
   unsigned int i;
   HgfsFileNode *fileNode = NULL;

   ASSERT(session);
   ASSERT(session->nodeArray);

   /* XXX: This O(n) lookup can and should be optimized. */
   for (i = 0; i < session->numNodes; i++) {
      if (session->nodeArray[i].state != FILENODE_STATE_UNUSED &&
          session->nodeArray[i].handle == handle) {
         fileNode = &session->nodeArray[i];
         break;
      }
   }

   return fileNode;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsFileNode2Handle --
 *
 *    Retrieve the handle that represents a file node outside of the server.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    The handle
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsHandle
HgfsFileNode2Handle(HgfsFileNode const *fileNode) // IN
{
   ASSERT(fileNode);

   return fileNode->handle;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsDumpAllNodes --
 *
 *    Debugging routine; print all nodes in the nodeArray.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsDumpAllNodes(HgfsSessionInfo *session)  // IN: session info
{
   unsigned int i;

   ASSERT(session);
   ASSERT(session->nodeArray);

   Log("Dumping all nodes\n");
   for (i = 0; i < session->numNodes; i++) {
      Log("handle %u, name \"%s\", localdev %"FMT64"u, localInum %"FMT64"u %u\n",
          session->nodeArray[i].handle,
          session->nodeArray[i].utf8Name ? session->nodeArray[i].utf8Name : "NULL",
          session->nodeArray[i].localId.volumeId,
          session->nodeArray[i].localId.fileId,
          session->nodeArray[i].fileDesc);
   }
   Log("Done\n");
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2FileDesc --
 *
 *    Retrieve the file descriptor (host OS file handle) based on the hgfs
 *    handle.
 *
 * Results:
 *    TRUE if the handle is valid and the file desc was retrieved successfully.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHandle2FileDesc(HgfsHandle handle,        // IN: Hgfs file handle
                    HgfsSessionInfo *session, // IN: Session info
                    fileDesc *fd,             // OUT: OS handle (file descriptor)
                    void **fileCtx)           // OUT: OS file context
{
   Bool found = FALSE;
   HgfsFileNode *fileNode = NULL;

   MXUser_AcquireExclLock(session->nodeArrayLock);
   fileNode = HgfsHandle2FileNode(handle, session);
   if (fileNode == NULL) {
      goto exit;
   }

   *fd = fileNode->fileDesc;
   if (fileCtx) {
      *fileCtx = fileNode->fileCtx;
   }
   found = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2AppendFlag --
 *
 *    Retrieve the append flag for the file node that corresponds to
 *    the specified hgfs handle.
 *
 * Results:
 *    TRUE if the handle is valid and append flag was retrieved successfully.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHandle2AppendFlag(HgfsHandle handle,        // IN: Hgfs file handle
                      HgfsSessionInfo *session, // IN: Session info
                      Bool *appendFlag)         // OUT: append flag
{
   Bool found = FALSE;
   HgfsFileNode *fileNode = NULL;

   MXUser_AcquireExclLock(session->nodeArrayLock);
   fileNode = HgfsHandle2FileNode(handle, session);
   if (fileNode == NULL) {
      goto exit;
   }

   *appendFlag = fileNode->flags & HGFS_FILE_NODE_APPEND_FL;
   found = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2LocalId --
 *
 *    Retrieve the local id for the file node that corresponds to
 *    the specified hgfs handle.
 *
 * Results:
 *    TRUE if the hgfs handle is valid and local id was retrieved successfully.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHandle2LocalId(HgfsHandle handle,        // IN: Hgfs file handle
                   HgfsSessionInfo *session, // IN: Session info
                   HgfsLocalId *localId)     // OUT: local id info
{
   Bool found = FALSE;
   HgfsFileNode *fileNode = NULL;

   ASSERT(localId);

   MXUser_AcquireExclLock(session->nodeArrayLock);
   fileNode = HgfsHandle2FileNode(handle, session);
   if (fileNode == NULL) {
      goto exit;
   }

   localId->volumeId = fileNode->localId.volumeId;
   localId->fileId = fileNode->localId.fileId;

   found = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsFileDesc2Handle --
 *
 *    Given an OS handle/fd, return file's hgfs handle.
 *
 * Results:
 *    TRUE if the node was found.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsFileDesc2Handle(fileDesc fd,              // IN: OS handle (file descriptor)
                    HgfsSessionInfo *session, // IN: Session info
                    HgfsHandle *handle)       // OUT: Hgfs file handle
{
   unsigned int i;
   Bool found = FALSE;
   HgfsFileNode *existingFileNode = NULL;

   ASSERT(session);
   ASSERT(session->nodeArray);

   MXUser_AcquireExclLock(session->nodeArrayLock);

   for (i = 0; i < session->numNodes; i++) {
      existingFileNode = &session->nodeArray[i];
      if ((existingFileNode->state == FILENODE_STATE_IN_USE_CACHED) &&
          (existingFileNode->fileDesc == fd)) {
         *handle = HgfsFileNode2Handle(existingFileNode);
         found = TRUE;
         break;
      }
   }

   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2ShareMode --
 *
 *    Given an OS handle/fd, return the share access mode.
 *
 * Results:
 *    TRUE if the node was found.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHandle2ShareMode(HgfsHandle handle,         // IN: Hgfs file handle
                     HgfsSessionInfo *session,  // IN: Session info
                     HgfsOpenMode *shareMode)   // OUT:share access mode
{
   Bool found = FALSE;
   HgfsFileNode *existingFileNode = NULL;
   HgfsNameStatus nameStatus;

   if (shareMode == NULL) {
      return found;
   }

   MXUser_AcquireExclLock(session->nodeArrayLock);

   existingFileNode = HgfsHandle2FileNode(handle, session);
   if (existingFileNode == NULL) {
      goto exit_unlock;
   }

   nameStatus = HgfsServerPolicy_GetShareMode(existingFileNode->shareName,
                                              existingFileNode->shareNameLen,
                                              shareMode);
   found = (nameStatus == HGFS_NAME_STATUS_COMPLETE);

exit_unlock:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2FileName --
 *
 *    Given an OS handle/fd, return file's hgfs name.
 *
 * Results:
 *    TRUE if the node was found.
 *    FALSE otherwise.
 *
 * Side effects:
 *    Allocates memory and makes a copy of the file name.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHandle2FileName(HgfsHandle handle,       // IN: Hgfs file handle
                    HgfsSessionInfo *session,// IN: Session info
                    char **fileName,         // OUT: UTF8 file name
                    size_t *fileNameSize)    // OUT: UTF8 file name size
{
   Bool unused1, unused2;
   return HgfsHandle2FileNameMode(handle, session, &unused1, &unused2, fileName,
                                  fileNameSize);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2FileNameMode --
 *
 *    Given an OS handle/fd, return file's hgfs name and permissions
 *    associated with the corresponding shared folder.
 *
 * Results:
 *    TRUE if the node was found.
 *    FALSE otherwise.
 *
 * Side effects:
 *    Allocates memory and makes a copy of the file name.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHandle2FileNameMode(HgfsHandle handle,       // IN: Hgfs file handle
                        HgfsSessionInfo *session,// IN: Session info
                        Bool *readPermissions,   // OUT: shared folder permissions
                        Bool *writePermissions,  // OUT: shared folder permissions
                        char **fileName,         // OUT: UTF8 file name
                        size_t *fileNameSize)    // OUT: UTF8 file name size
{
   Bool found = FALSE;
   HgfsFileNode *existingFileNode = NULL;
   char *name = NULL;
   size_t nameSize = 0;

   if ((fileName == NULL) || (fileNameSize == NULL)) {
      return found;
   }

   MXUser_AcquireExclLock(session->nodeArrayLock);

   existingFileNode = HgfsHandle2FileNode(handle, session);
   if (existingFileNode == NULL) {
      goto exit_unlock;
   }

   name = malloc(existingFileNode->utf8NameLen + 1);
   if (name == NULL) {
      goto exit_unlock;
   }
   *readPermissions = existingFileNode->shareInfo.readPermissions;
   *writePermissions = existingFileNode->shareInfo.writePermissions;
   nameSize = existingFileNode->utf8NameLen;
   memcpy(name, existingFileNode->utf8Name, nameSize);
   name[nameSize] = '\0';
   found = TRUE;

exit_unlock:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   *fileName = name;
   *fileNameSize = nameSize;

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2FileNameMode --
 *
 *    Given an OS handle/fd, return information needed for directory
 *    notification package: relative to the root share file name and
 *    shared folder notification handle.
 *
 * Results:
 *    TRUE if the node was found.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHandle2NotifyInfo(HgfsHandle handle,                    // IN: Hgfs file handle
                      HgfsSessionInfo *session,             // IN: Session info
                      char **fileName,                      // OUT: UTF8 file name
                      size_t *fileNameSize,                 // OUT: UTF8 file name size
                      HgfsSharedFolderHandle *folderHandle) // OUT: shared folder handle
{
   Bool found = FALSE;
   HgfsFileNode *existingFileNode;
   char *name;
   size_t nameSize;

   ASSERT(fileName != NULL && fileNameSize != NULL);
   MXUser_AcquireExclLock(session->nodeArrayLock);

   existingFileNode = HgfsHandle2FileNode(handle, session);
   if (NULL != existingFileNode) {
      nameSize = existingFileNode->utf8NameLen - existingFileNode->shareInfo.rootDirLen;
      name = Util_SafeMalloc(nameSize + 1);
      *folderHandle = existingFileNode->shareInfo.handle;
      memcpy(name, existingFileNode->utf8Name, nameSize);
      name[nameSize] = '\0';
      *fileName = name;
      *fileNameSize = nameSize;
      found = TRUE;
   }

   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetNodeCopy --
 *
 *    Make a copy of the node. The node should not be kept around for long, as
 *    the data might become stale. This is mostly a convenience function to get
 *    node fields more efficiently.
 *
 * Results:
 *    TRUE if the hgfs handle is valid and the copy was successful.
 *    FALSE otherwise.
 *
 * Side effects:
 *    Allocates memory for node.utf8Name if copyName was set to TRUE.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsGetNodeCopy(HgfsHandle handle,        // IN: Hgfs file handle
                HgfsSessionInfo *session, // IN: Session info
                Bool copyName,            // IN: Should we copy the name?
                HgfsFileNode *copy)       // IN/OUT: Copy of the node
{
   HgfsFileNode *original = NULL;
   Bool found = FALSE;

   ASSERT(copy);

   MXUser_AcquireExclLock(session->nodeArrayLock);

   original = HgfsHandle2FileNode(handle, session);
   if (original == NULL) {
      goto exit;
   }

   if (copyName) {
      copy->utf8Name = malloc(original->utf8NameLen + 1);
      if (copy->utf8Name == NULL) {
         goto exit;
      }
      copy->utf8NameLen = original->utf8NameLen;
      memcpy(copy->utf8Name, original->utf8Name, copy->utf8NameLen);
      copy->utf8Name[copy->utf8NameLen] = '\0';
   } else {
      copy->utf8Name = NULL;
      copy->utf8NameLen = 0;
   }

   copy->localId = original->localId;
   copy->fileDesc = original->fileDesc;
   copy->mode = original->mode;
   copy->shareAccess = original->shareAccess;
   copy->flags = original->flags;
   copy->state = original->state;
   copy->handle = original->handle;
   copy->fileCtx = original->fileCtx;
   found = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsHandleIsSequentialOpen --
 *
 *    Get the Hgfs open mode this handle was originally opened with.
 *
 * Results:
 *    TRUE on success, FALSE on failure.  sequentialOpen is filled in on
 *    success.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
HgfsHandleIsSequentialOpen(HgfsHandle handle,        // IN:  Hgfs file handle
                           HgfsSessionInfo *session, // IN: Session info
                           Bool *sequentialOpen)     // OUT: If open was sequential
{
   HgfsFileNode *node;
   Bool success = FALSE;

   ASSERT(sequentialOpen);

   MXUser_AcquireExclLock(session->nodeArrayLock);

   node = HgfsHandle2FileNode(handle, session);
   if (node == NULL) {
      goto exit;
   }

   *sequentialOpen = node->flags & HGFS_FILE_NODE_SEQUENTIAL_FL;
   success = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return success;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsHandleIsSharedFolderOpen --
 *
 *    Find if this is a shared folder open.
 *
 * Results:
 *    TRUE on success, FALSE on failure.  sharedFolderOpen is filled in on
 *    success.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
HgfsHandleIsSharedFolderOpen(HgfsHandle handle,        // IN:  Hgfs file handle
                             HgfsSessionInfo *session, // IN: Session info
                             Bool *sharedFolderOpen)   // OUT: If shared folder
{
   HgfsFileNode *node;
   Bool success = FALSE;

   ASSERT(sharedFolderOpen);

   MXUser_AcquireExclLock(session->nodeArrayLock);

   node = HgfsHandle2FileNode(handle, session);
   if (node == NULL) {
      goto exit;
   }

   *sharedFolderOpen = node->flags & HGFS_FILE_NODE_SHARED_FOLDER_OPEN_FL;
   success = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUpdateNodeFileDesc --
 *
 *    Given a hgfs file handle, update the node with the new file desc (OS
 *    handle) information.
 *
 * Results:
 *    TRUE if the update is successful.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUpdateNodeFileDesc(HgfsHandle handle,        // IN: Hgfs file handle
                       HgfsSessionInfo *session, // IN: Session info
                       fileDesc fd,              // IN: OS handle (file desc)
                       void *fileCtx)            // IN: OS file context
{
   HgfsFileNode *node;
   Bool updated = FALSE;

   MXUser_AcquireExclLock(session->nodeArrayLock);

   node = HgfsHandle2FileNode(handle, session);
   if (node == NULL) {
      goto exit;
   }

   node->fileDesc = fd;
   node->fileCtx = fileCtx;
   updated = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return updated;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUpdateNodeServerLock --
 *
 *    Given a file desc (OS handle), update the node with the new oplock
 *    information.
 *
 * Results:
 *    TRUE if the update is successful.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUpdateNodeServerLock(fileDesc fd,                // IN: OS handle
                         HgfsSessionInfo *session,   // IN: Session info
                         HgfsLockType serverLock)    // IN: new oplock
{
   unsigned int i;
   HgfsFileNode *existingFileNode = NULL;
   Bool updated = FALSE;

   ASSERT(session);
   ASSERT(session->nodeArray);

   MXUser_AcquireExclLock(session->nodeArrayLock);

   for (i = 0; i < session->numNodes; i++) {
      existingFileNode = &session->nodeArray[i];
      if (existingFileNode->state != FILENODE_STATE_UNUSED) {
         if (existingFileNode->fileDesc == fd) {
            existingFileNode->serverLock = serverLock;
            updated = TRUE;
            break;
         }
      }
   }

   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return updated;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUpdateNodeAppendFlag --
 *
 *    Given a hgfs file handle, update the node with the append flag info.
 *
 * Results:
 *    TRUE if the update is successful.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUpdateNodeAppendFlag(HgfsHandle handle,        // IN: Hgfs file handle
                         HgfsSessionInfo *session, // IN: Session info
                         Bool appendFlag)          // OUT: Append flag
{
   HgfsFileNode *node;
   Bool updated = FALSE;

   MXUser_AcquireExclLock(session->nodeArrayLock);

   node = HgfsHandle2FileNode(handle, session);
   if (node == NULL) {
      goto exit;
   }

   if (appendFlag) {
      node->flags |= HGFS_FILE_NODE_APPEND_FL;
   }
   updated = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return updated;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerCheckOpenFlagsForShare --
 *
 *    Given an open mode check this is compatible with the mode for
 *    the share upon which the open file resides.
 *
 *    If the share is read only and mode is HGFS_OPEN_CREATE we remap
 *    it to HGFS_OPEN which is allowed if the file exists.
 *
 * Results:
 *    TRUE if the mode is compatible.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerCheckOpenFlagsForShare(HgfsFileOpenInfo *openInfo,// IN: Hgfs file handle
                                 HgfsOpenFlags *flags)      // IN/OUT: open mode
{
   Bool status = TRUE;
   HgfsNameStatus nameStatus;
   HgfsOpenMode shareMode;
   char const *inEnd;
   char const *next;
   int len;

   ASSERT(openInfo);
   ASSERT(flags);

   inEnd = openInfo->cpName + openInfo->cpNameSize;

   /* The share name is the first component of the cross-platform name. */
   len = CPName_GetComponent(openInfo->cpName, inEnd, &next);
   if (len < 0) {
      LOG(4, "%s: get first component failed\n", __FUNCTION__);
      status = FALSE;
      goto exit;
   }

   nameStatus = HgfsServerPolicy_GetShareMode(openInfo->cpName, len,
                                              &shareMode);
   if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
      status = FALSE;
      goto exit;
   }

   if (shareMode == HGFS_OPEN_MODE_READ_ONLY) {
      /* Read only share we may have work to do. */
      if (*flags != HGFS_OPEN && *flags != HGFS_OPEN_CREATE) {
         status = FALSE;
         goto exit;
      }
      if (*flags == HGFS_OPEN_CREATE) {
         /*
          * Map open or create, to just open, which will fail if
          * if the file does not exist, which it is okay, as creating
          * a new file is not allowed and should be failed.
          */
         *flags = HGFS_OPEN;
      }
   }

exit:
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsDumpAllSearches --
 *
 *    Debugging routine; print all searches in the searchArray.
 *
 *    Caller should hold the session's searchArrayLock.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsDumpAllSearches(HgfsSessionInfo *session)   // IN: session info
{
   unsigned int i;

   ASSERT(session);
   ASSERT(session->searchArray);

   Log("Dumping all searches\n");
   for (i = 0; i < session->numSearches; i++) {
      Log("handle %u, baseDir \"%s\"\n",
          session->searchArray[i].handle,
          session->searchArray[i].utf8Dir ?
          session->searchArray[i].utf8Dir : "(NULL)");
   }
   Log("Done\n");
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetNewNode --
 *
 *    Remove a node from the free list and return it. Nodes on
 *    the free list should already be initialized.
 *
 *    If the free list is empty, reallocates more memory,
 *    initializes it appropriately, adds the new entries to the
 *    free list, and then returns one off the free list.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    An unused file node on success
 *    NULL on failure
 *
 * Side effects:
 *    Memory allocation (potentially).
 *
 *-----------------------------------------------------------------------------
 */

static HgfsFileNode *
HgfsGetNewNode(HgfsSessionInfo *session)  // IN: session info
{
   HgfsFileNode *node;
   HgfsFileNode *newMem;
   unsigned int newNumNodes;
   unsigned int i;

   ASSERT(session);
   ASSERT(session->nodeArray);

   LOG(4, "%s: entered\n", __FUNCTION__);

   if (!DblLnkLst_IsLinked(&session->nodeFreeList)) {
      /*
       * This has to be unsigned and with maximum bit length. This is
       * required to take care of "negative" differences as well.
       */
      uintptr_t ptrDiff;

      if (DOLOG(4)) {
         Log("Dumping nodes before realloc\n");
         HgfsDumpAllNodes(session);
      }

      /* Try to get twice as much memory as we had */
      newNumNodes = 2 * session->numNodes;
      newMem = (HgfsFileNode *)realloc(session->nodeArray,
                                       newNumNodes * sizeof *(session->nodeArray));
      if (!newMem) {
         LOG(4, "%s: can't realloc more nodes\n", __FUNCTION__);

         return NULL;
      }

      ptrDiff = (char *)newMem - (char *)session->nodeArray;
      if (ptrDiff) {
         size_t const oldSize = session->numNodes * sizeof *(session->nodeArray);

         /*
          * The portion of memory that contains all our file nodes moved.
          * All pointers that pointed inside the previous portion of memory
          * must be updated to point to the new portion of memory.
          *
          * We'll need to lock this if we multithread.
          */

         LOG(4, "Rebasing pointers, diff is %"FMTSZ"u, sizeof node is "
             "%"FMTSZ"u\n", ptrDiff, sizeof(HgfsFileNode));
         LOG(4, "old: %p new: %p\n", session->nodeArray, newMem);
         ASSERT(newMem == (HgfsFileNode *)((char*)session->nodeArray + ptrDiff));

#define HgfsServerRebase(_ptr, _type)                                   \
   if ((size_t)((char *)_ptr - (char *)session->nodeArray) < oldSize) { \
      _ptr = (_type *)((char *)_ptr + ptrDiff);                         \
   }

         /*
          * Rebase the links of all file nodes
          */
         for (i = 0; i < session->numNodes; i++) {
            HgfsServerRebase(newMem[i].links.prev, DblLnkLst_Links)
            HgfsServerRebase(newMem[i].links.next, DblLnkLst_Links)
         }

         /*
          * There is no need to rebase the anchor of the file node free list
          * because if we are here, it is empty.
          */

         /* Rebase the anchor of the cached file nodes list. */
         HgfsServerRebase(session->nodeCachedList.prev, DblLnkLst_Links)
         HgfsServerRebase(session->nodeCachedList.next, DblLnkLst_Links)

#undef HgfsServerRebase
      }

      /* Initialize the new nodes */
      LOG(4, "numNodes was %u, now is %u\n", session->numNodes, newNumNodes);
      for (i = session->numNodes; i < newNumNodes; i++) {
         DblLnkLst_Init(&newMem[i].links);

         newMem[i].state = FILENODE_STATE_UNUSED;
         newMem[i].utf8Name = NULL;
         newMem[i].utf8NameLen = 0;
         newMem[i].fileCtx = NULL;

         /* Append at the end of the list */
         DblLnkLst_LinkLast(&session->nodeFreeList, &newMem[i].links);
      }
      session->nodeArray = newMem;
      session->numNodes = newNumNodes;

      if (DOLOG(4)) {
         Log("Dumping nodes after pointer changes\n");
         HgfsDumpAllNodes(session);
      }
   }

   /* Remove the first item from the list */
   node = DblLnkLst_Container(session->nodeFreeList.next, HgfsFileNode, links);
   DblLnkLst_Unlink1(&node->links);

   return node;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsRemoveFileNode --
 *
 *    Free its localname, clear its fields, return it to the free list.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    node->utf8Name is freed.
 *    node->state is set to FILENODE_STATE_UNUSED.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsRemoveFileNode(HgfsFileNode *node,        // IN: file node
                   HgfsSessionInfo *session)  // IN: session info
{
   ASSERT(node);

   LOG(4, "%s: handle %u, name %s, fileId %"FMT64"u\n", __FUNCTION__,
       HgfsFileNode2Handle(node), node->utf8Name, node->localId.fileId);

   if (node->shareName) {
      free(node->shareName);
      node->shareName = NULL;
   }

   if (node->utf8Name) {
      free(node->utf8Name);
      node->utf8Name = NULL;
   }

   node->state = FILENODE_STATE_UNUSED;
   ASSERT(node->fileCtx == NULL);
   node->fileCtx = NULL;

   if (node->shareInfo.rootDir) {
      free((void*)node->shareInfo.rootDir);
      node->shareInfo.rootDir = NULL;
   }

   /* Prepend at the beginning of the list */
   DblLnkLst_LinkFirst(&session->nodeFreeList, &node->links);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsFreeFileNodeInternal --
 *
 *    Free its localname, clear its fields, return it to the free list.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    node->utf8Name is freed.
 *    node->state is set to FILENODE_STATE_UNUSED.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsFreeFileNodeInternal(HgfsHandle handle,        // IN: Handle to free
                         HgfsSessionInfo *session) // IN: Session info
{
   HgfsFileNode *node = HgfsHandle2FileNode(handle, session);
   ASSERT(node);
   HgfsRemoveFileNode(node, session);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsFreeFileNode --
 *
 *    Free its localname, clear its fields, return it to the free list.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    node->utf8Name is freed.
 *    node->state is set to FILENODE_STATE_UNUSED.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsFreeFileNode(HgfsHandle handle,         // IN: Handle to free
                 HgfsSessionInfo *session)  // IN: Session info
{
   MXUser_AcquireExclLock(session->nodeArrayLock);
   HgfsFreeFileNodeInternal(handle, session);
   MXUser_ReleaseExclLock(session->nodeArrayLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAddNewFileNode --
 *
 *    Gets a free node off the free list, sets its name, localId info,
 *    file descriptor and permissions.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    A pointer to the newly added node on success
 *    NULL on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsFileNode *
HgfsAddNewFileNode(HgfsFileOpenInfo *openInfo,  // IN: open info struct
                   HgfsLocalId const *localId,  // IN: Local unique file ID
                   fileDesc fileDesc,           // IN: File Handle
                   Bool append,                 // IN: open with append flag
                   size_t shareNameLen,         // IN: share name byte length
                   char const *shareName,       // IN: share name
                   Bool sharedFolderOpen,       // IN: shared folder only open
                   HgfsSessionInfo *session)    // IN: session info
{
   HgfsFileNode *newNode;
   char* rootDir;

   ASSERT(openInfo);
   ASSERT(localId);
   ASSERT(session);

   /* This was already verified in HgfsUnpackOpenRequest... */
   ASSERT(openInfo->mask & HGFS_OPEN_VALID_FILE_NAME);

   /* Get an unused node */
   newNode = HgfsGetNewNode(session);
   if (!newNode) {
      LOG(4, "%s: out of memory\n", __FUNCTION__);

      return NULL;
   }

   /* Set new node's fields */
   if (!HgfsServerGetOpenMode(openInfo, &newNode->mode)) {
      HgfsRemoveFileNode(newNode, session);
      return NULL;
   }

   /*
    * Save a copy of the share name so we can look up its
    * access mode at various times over the node's lifecycle.
    */
   newNode->shareName = malloc(shareNameLen + 1);
   if (newNode->shareName == NULL) {
      LOG(4, "%s: out of memory\n", __FUNCTION__);
      HgfsRemoveFileNode(newNode, session);
      return NULL;
   }
   memcpy(newNode->shareName, shareName, shareNameLen);
   newNode->shareName[shareNameLen] = '\0';
   newNode->shareNameLen = shareNameLen;

   newNode->utf8NameLen = strlen(openInfo->utf8Name);
   newNode->utf8Name = malloc(newNode->utf8NameLen + 1);
   if (newNode->utf8Name == NULL) {
      LOG(4, "%s: out of memory\n", __FUNCTION__);
      HgfsRemoveFileNode(newNode, session);
      return NULL;
   }
   memcpy(newNode->utf8Name, openInfo->utf8Name, newNode->utf8NameLen);
   newNode->utf8Name[newNode->utf8NameLen] = '\0';

   newNode->shareInfo.rootDirLen = strlen(openInfo->shareInfo.rootDir);
   rootDir = malloc(newNode->shareInfo.rootDirLen + 1);
   if (rootDir == NULL) {
      LOG(4, "HgfsAddNewFileNode: out of memory\n");
      HgfsRemoveFileNode(newNode, session);
      return NULL;
   }
   memcpy(rootDir, openInfo->shareInfo.rootDir, newNode->shareInfo.rootDirLen);
   rootDir[newNode->shareInfo.rootDirLen] = '\0';
   newNode->shareInfo.rootDir = rootDir;

   newNode->handle = HgfsServerGetNextHandleCounter();
   newNode->localId = *localId;
   newNode->fileDesc = fileDesc;
   newNode->shareAccess = (openInfo->mask & HGFS_OPEN_VALID_SHARE_ACCESS) ?
      openInfo->shareAccess : HGFS_DEFAULT_SHARE_ACCESS;
   newNode->flags = 0;

   if (append) {
      newNode->flags |= HGFS_FILE_NODE_APPEND_FL;
   }
   if (sharedFolderOpen) {
      newNode->flags |= HGFS_FILE_NODE_SHARED_FOLDER_OPEN_FL;
   }
   if (HGFS_OPEN_MODE_FLAGS(openInfo->mode) & HGFS_OPEN_SEQUENTIAL) {
      newNode->flags |= HGFS_FILE_NODE_SEQUENTIAL_FL;
   }

   newNode->serverLock = openInfo->acquiredLock;
   newNode->state = FILENODE_STATE_IN_USE_NOT_CACHED;
   newNode->shareInfo.readPermissions = openInfo->shareInfo.readPermissions;
   newNode->shareInfo.writePermissions = openInfo->shareInfo.writePermissions;
   newNode->shareInfo.handle = openInfo->shareInfo.handle;

   LOG(4, "%s: got new node, handle %u\n", __FUNCTION__,
       HgfsFileNode2Handle(newNode));
   return newNode;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAddToCacheInternal --
 *
 *    Adds the node to cache. If the number of nodes in the cache exceed
 *    the maximum number of entries then the first node is removed. The
 *    first node should be the least recently used.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsAddToCacheInternal(HgfsHandle handle,         // IN: HGFS file handle
                       HgfsSessionInfo *session)  // IN: Session info
{
   HgfsFileNode *node;

   /* Check if the node is already cached. */
   if (HgfsIsCachedInternal(handle, session)) {
      ASSERT((node = HgfsHandle2FileNode(handle, session)) &&
             node->state == FILENODE_STATE_IN_USE_CACHED);
      return TRUE;
   }

   /* Remove the LRU node if the list is full. */
   if (session->numCachedOpenNodes == gHgfsCfgSettings.maxCachedOpenNodes) {
      if (!HgfsRemoveLruNode(session)) {
         LOG(4, "%s: Unable to remove LRU node from cache.\n", __FUNCTION__);

         return FALSE;
      }
   }

   ASSERT(session->numCachedOpenNodes < gHgfsCfgSettings.maxCachedOpenNodes);

   node = HgfsHandle2FileNode(handle, session);
   ASSERT(node);
   /* Append at the end of the list. */
   DblLnkLst_LinkLast(&session->nodeCachedList, &node->links);

   node->state = FILENODE_STATE_IN_USE_CACHED;
   session->numCachedOpenNodes++;

   /*
    * Keep track of how many open nodes we have with
    * server locks on them. The locked file should
    * always be present in the node cache. So we keep
    * the number of the files that have locks on them
    * limited, and smaller than the number of maximum
    * nodes in the cache.
    */

   if (node->serverLock != HGFS_LOCK_NONE) {
      session->numCachedLockedNodes++;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsRemoveFromCacheInternal --
 *
 *    Remove the specified node from the cache and close the associated
 *    file descriptor. If the node was not already in the cache then nothing
 *    is done.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsRemoveFromCacheInternal(HgfsHandle handle,        // IN: Hgfs handle to the node
                            HgfsSessionInfo *session) // IN: Session info
{
   HgfsFileNode *node;

   ASSERT(session);

   node = HgfsHandle2FileNode(handle, session);
   if (node == NULL) {
      LOG(4, "%s: invalid handle.\n", __FUNCTION__);

      return FALSE;
   }

   if (node->state == FILENODE_STATE_IN_USE_CACHED) {
      /* Unlink the node from the list of cached fileNodes. */
      DblLnkLst_Unlink1(&node->links);
      node->state = FILENODE_STATE_IN_USE_NOT_CACHED;
      session->numCachedOpenNodes--;
      LOG(4, "%s: cache entries %u remove node %s id %"FMT64"u fd %u .\n",
          __FUNCTION__, session->numCachedOpenNodes, node->utf8Name,
          node->localId.fileId, node->fileDesc);

      /*
       * XXX: From this point and up in the call chain (i.e. this function and
       * all callers), Bool is returned instead of the HgfsInternalStatus.
       * HgfsPlatformCloseFile returns HgfsInternalStatus, which is far more granular,
       * but modifying this stack to use HgfsInternalStatus instead of Bool is
       * not worth it, as we'd have to #define per-platform error codes for
       * things like "ran out of memory", "bad file handle", etc.
       *
       * Instead, we'll just await the lobotomization of the node cache to
       * really fix this.
       */
      if (HgfsPlatformCloseFile(node->fileDesc, node->fileCtx)) {
         LOG(4, "%s: Could not close fd %u\n", __FUNCTION__, node->fileDesc);

         return FALSE;
      }
      node->fileCtx = NULL;

     /*
      * If we have just removed the node then the number of used nodes better
      * be less than the max. If we didn't remove a node, it means the
      * node we tried to remove was not in the cache to begin with, and
      * we have a problem (see bug 36244).
      */

      ASSERT(session->numCachedOpenNodes < gHgfsCfgSettings.maxCachedOpenNodes);
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsIsCachedInternal --
 *
 *    Check if the node exists in the cache. If the node is found in
 *    the cache then move it to the end of the list. Most recently
 *    used nodes move towards the end of the list.
 *
 *    The session nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    TRUE if the node is found in the cache.
 *    FALSE if the node is not in the cache.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsIsCachedInternal(HgfsHandle handle,         // IN: Structure representing file node
                     HgfsSessionInfo *session)  // IN: Session info
{
   HgfsFileNode *node;

   ASSERT(session);

   node = HgfsHandle2FileNode(handle, session);
   if (node == NULL) {
      LOG(4, "%s: invalid handle.\n", __FUNCTION__);

      return FALSE;
   }

   if (node->state == FILENODE_STATE_IN_USE_CACHED) {
      /*
       * Move this node to the end of the list.
       */
      DblLnkLst_Unlink1(&node->links);
      DblLnkLst_LinkLast(&session->nodeCachedList, &node->links);

      return TRUE;
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsIsServerLockAllowed --
 *
 *    Check if there's room for another file node with the server lock.
 *    If there's no room in the cache for the file with the server lock,
 *    then the file will be opened without the lock even if the client
 *    asked for the lock.
 *
 *
 * Results:
 *    TRUE if the node is found in the cache.
 *    FALSE if the node is not in the cache.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsIsServerLockAllowed(HgfsSessionInfo *session)  // IN: session info
{
   Bool allowed;

   MXUser_AcquireExclLock(session->nodeArrayLock);
   allowed = session->numCachedLockedNodes < MAX_LOCKED_FILENODES;
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return allowed;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetNewSearch --
 *
 *    Remove a search from the free list and return it. Searches on
 *    the free list should already be initialized.
 *
 *    If the free list is empty, reallocates more memory,
 *    initializes it appropriately, adds the new entries to the
 *    free list, and then returns one off the free list.
 *
 *    Caller should hold the session's searchArrayLock.
 *
 * Results:
 *    An unused search on success
 *    NULL on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsSearch *
HgfsGetNewSearch(HgfsSessionInfo *session)  // IN: session info
{
   HgfsSearch *search;
   HgfsSearch *newMem;
   unsigned int newNumSearches;
   unsigned int i;

   ASSERT(session);
   ASSERT(session->searchArray);

   LOG(4, "%s: entered\n", __FUNCTION__);

   if (!DblLnkLst_IsLinked(&session->searchFreeList)) {
      /*
       * This has to be unsigned and with maximum bit length. This is
       * required to take care of "negative" differences as well.
       */

      uintptr_t ptrDiff;

      if (DOLOG(4)) {
         Log("Dumping searches before realloc\n");
         HgfsDumpAllSearches(session);
      }

      /* Try to get twice as much memory as we had */
      newNumSearches = 2 * session->numSearches;
      newMem = (HgfsSearch *)realloc(session->searchArray,
                                     newNumSearches * sizeof *(session->searchArray));
      if (!newMem) {
         LOG(4, "%s: can't realloc more searches\n", __FUNCTION__);

         return NULL;
      }

      ptrDiff = (char *)newMem - (char *)session->searchArray;
      if (ptrDiff) {
         size_t const oldSize = session->numSearches * sizeof *(session->searchArray);

         /*
          * The portion of memory that contains all our searches moved.
          * All pointers that pointed inside the previous portion of memory
          * must be updated to point to the new portion of memory.
          */

         LOG(4, "Rebasing pointers, diff is %"FMTSZ"u, sizeof search is "
             "%"FMTSZ"u\n", ptrDiff, sizeof(HgfsSearch));
         LOG(4, "old: %p new: %p\n", session->searchArray, newMem);
         ASSERT(newMem == (HgfsSearch*)((char*)session->searchArray + ptrDiff));

#define HgfsServerRebase(_ptr, _type)                                     \
   if ((size_t)((char *)_ptr - (char *)session->searchArray) < oldSize) { \
      _ptr = (_type *)((char *)_ptr + ptrDiff);                           \
   }

         /*
          * Rebase the links of all searches
          */

         for (i = 0; i < session->numSearches; i++) {
            HgfsServerRebase(newMem[i].links.prev, DblLnkLst_Links)
            HgfsServerRebase(newMem[i].links.next, DblLnkLst_Links)
         }

         /*
          * There is no need to rebase the links of the search free list
          * because if we are here, it is empty
          */

#undef HgfsServerRebase
      }

      /* Initialize the new searches */
      LOG(4, "numSearches was %u, now is %u\n", session->numSearches,
          newNumSearches);

      for (i = session->numSearches; i < newNumSearches; i++) {
         DblLnkLst_Init(&newMem[i].links);
         newMem[i].utf8Dir = NULL;
         newMem[i].utf8DirLen = 0;
         newMem[i].utf8ShareName = NULL;
         newMem[i].utf8ShareNameLen = 0;
         newMem[i].shareInfo.rootDir = NULL;
         newMem[i].shareInfo.rootDirLen = 0;
         newMem[i].dents = NULL;
         newMem[i].numDents = 0;

         /* Append at the end of the list */
         DblLnkLst_LinkLast(&session->searchFreeList, &newMem[i].links);
      }
      session->searchArray = newMem;
      session->numSearches = newNumSearches;

      if (DOLOG(4)) {
         Log("Dumping searches after pointer changes\n");
         HgfsDumpAllSearches(session);
      }
   }

   /* Remove the first item from the list */
   search = DblLnkLst_Container(session->searchFreeList.next, HgfsSearch, links);
   DblLnkLst_Unlink1(&search->links);

   return search;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSearch2SearchHandle --
 *
 *    Retrieve the handle that represents a search outside of the server.
 *
 *    Caller should hold the session's searchArrayLock.
 *
 * Results:
 *    The handle
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsHandle
HgfsSearch2SearchHandle(HgfsSearch const *search) // IN
{
   ASSERT(search);

   return search->handle;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSearchIsBaseNameSpace --
 *
 *    Check if the search is the base of our name space, i.e. the dirents are
 *    the shares themselves.
 *
 * Results:
 *    TRUE if the search is the base of the name space, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsSearchIsBaseNameSpace(HgfsSearch const *search) // IN
{
   ASSERT(search);

   return search->type == DIRECTORY_SEARCH_TYPE_BASE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetSearchCopy --
 *
 *    Make a copy of the search. It should not be kept around for long, as the
 *    data might become stale. This is mostly a convenience function to get
 *    search fields more efficiently.
 *
 *    Note that unlike HgfsGetNodeCopy, we always copy the name, and we never
 *    copy the dents.
 *
 * Results:
 *    TRUE if the hgfs handle is valid and the copy was successful.
 *    FALSE otherwise.
 *
 * Side effects:
 *    Allocates memory for search.utf8Dir
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsGetSearchCopy(HgfsHandle handle,        // IN: Hgfs search handle
                  HgfsSessionInfo *session, // IN: Session info
                  HgfsSearch *copy)         // IN/OUT: Copy of the search
{
   HgfsSearch *original = NULL;
   Bool found = FALSE;

   ASSERT(copy);

   MXUser_AcquireExclLock(session->searchArrayLock);
   original = HgfsSearchHandle2Search(handle, session);
   if (original == NULL) {
      goto exit;
   }

   copy->utf8Dir = malloc(original->utf8DirLen + 1);
   if (copy->utf8Dir == NULL) {
      goto exit;
   }
   copy->utf8DirLen = original->utf8DirLen;
   memcpy(copy->utf8Dir, original->utf8Dir, copy->utf8DirLen);
   copy->utf8Dir[copy->utf8DirLen] = '\0';

   copy->utf8ShareName = malloc(original->utf8ShareNameLen + 1);
   if (copy->utf8ShareName == NULL) {
      goto exit;
   }
   copy->utf8ShareNameLen = original->utf8ShareNameLen;
   memcpy(copy->utf8ShareName, original->utf8ShareName, copy->utf8ShareNameLen);
   copy->utf8ShareName[copy->utf8ShareNameLen] = '\0';

   /* No dents for the copy, they consume too much memory and aren't needed. */
   copy->dents = NULL;
   copy->numDents = 0;

   copy->handle = original->handle;
   copy->type = original->type;
   found = TRUE;

exit:
   MXUser_ReleaseExclLock(session->searchArrayLock);

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAddNewSearch --
 *
 *    Gets a free search off the free list, sets its base directory, dents,
 *    and type.
 *
 *    Caller should hold the session's searchArrayLock.
 *
 * Results:
 *    A pointer to the newly added search on success
 *    NULL on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsSearch *
HgfsAddNewSearch(char const *utf8Dir,       // IN: UTF8 name of dir to search in
                 DirectorySearchType type,  // IN: What kind of search is this?
                 char const *utf8ShareName, // IN: Share name containing the directory
                 char const *rootDir,       // IN: Root directory for the share
                 HgfsSessionInfo *session)  // IN: Session info
{
   HgfsSearch *newSearch;

   ASSERT(utf8Dir);

   /* Get an unused search */
   newSearch = HgfsGetNewSearch(session);
   if (!newSearch) {
      LOG(4, "%s: out of memory\n", __FUNCTION__);

      return NULL;
   }

   newSearch->dents = NULL;
   newSearch->numDents = 0;
   newSearch->flags = 0;
   newSearch->type = type;
   newSearch->handle = HgfsServerGetNextHandleCounter();

   newSearch->utf8DirLen = strlen(utf8Dir);
   newSearch->utf8Dir = Util_SafeStrdup(utf8Dir);

   newSearch->utf8ShareNameLen = strlen(utf8ShareName);
   newSearch->utf8ShareName = Util_SafeStrdup(utf8ShareName);

   newSearch->shareInfo.rootDirLen = strlen(rootDir);
   newSearch->shareInfo.rootDir = Util_SafeStrdup(rootDir);

   LOG(4, "%s: got new search, handle %u\n", __FUNCTION__,
       HgfsSearch2SearchHandle(newSearch));
   return newSearch;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsFreeSearchDirents --
 *
 *    Frees all dirents and dirents pointer array.
 *
 *    Caller should hold the session's searchArrayLock.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsFreeSearchDirents(HgfsSearch *search)       // IN/OUT: search
{
   unsigned int i;

   if (NULL != search->dents) {
      for (i = 0; i < search->numDents; i++) {
         free(search->dents[i]);
         search->dents[i] = NULL;
      }
      free(search->dents);
      search->dents = NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsRemoveSearchInternal --
 *
 *    Destroy a search object and recycle it to the free list
 *
 *    Caller should hold the session's searchArrayLock.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsRemoveSearchInternal(HgfsSearch *search,       // IN: search
                         HgfsSessionInfo *session) // IN: session info
{
   ASSERT(search);
   ASSERT(session);

   LOG(4, "%s: handle %u, dir %s\n", __FUNCTION__,
       HgfsSearch2SearchHandle(search), search->utf8Dir);

   HgfsFreeSearchDirents(search);
   free(search->utf8Dir);
   free(search->utf8ShareName);
   free((char*)search->shareInfo.rootDir);
   search->utf8DirLen = 0;
   search->utf8Dir = NULL;
   search->utf8ShareNameLen = 0;
   search->utf8ShareName = NULL;
   search->shareInfo.rootDirLen = 0;
   search->shareInfo.rootDir = NULL;

   /* Prepend at the beginning of the list */
   DblLnkLst_LinkFirst(&session->searchFreeList, &search->links);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsRemoveSearch --
 *
 *    Wrapper around HgfsRemoveSearchInternal that first takes the lock and
 *    converts the handle to the search itself.
 *
 * Results:
 *    TRUE if the search was freed successfully.
 *    FALSE if the search could not be found.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsRemoveSearch(HgfsHandle handle,        // IN: search
                 HgfsSessionInfo *session) // IN: session info
{
   HgfsSearch *search;
   Bool success = FALSE;

   MXUser_AcquireExclLock(session->searchArrayLock);

   search = HgfsSearchHandle2Search(handle, session);
   if (search != NULL) {
      HgfsRemoveSearchInternal(search, session);
      success = TRUE;
   }

   MXUser_ReleaseExclLock(session->searchArrayLock);

   return success;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSearchHasReadAllEntries --
 *
 *    Return whether the client has read all the search entries or not.
 *
 * Results:
 *    TRUE on success, FALSE on failure.  readAllEntries is filled in on
 *    success.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
HgfsSearchHasReadAllEntries(HgfsHandle handle,        // IN:  Hgfs file handle
                            HgfsSessionInfo *session, // IN: Session info
                            Bool *readAllEntries)     // OUT: If open was sequential
{
   HgfsSearch *search;
   Bool success = FALSE;

   ASSERT(NULL != readAllEntries);

   MXUser_AcquireExclLock(session->searchArrayLock);

   search = HgfsSearchHandle2Search(handle, session);
   if (NULL == search) {
      goto exit;
   }

   *readAllEntries = search->flags & HGFS_SEARCH_FLAG_READ_ALL_ENTRIES;
   success = TRUE;

exit:
   MXUser_ReleaseExclLock(session->searchArrayLock);

   return success;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSearchSetReadAllEntries --
 *
 *    Set the flag to indicate the client has read all the search entries.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsSearchSetReadAllEntries(HgfsHandle handle,        // IN:  Hgfs file handle
                            HgfsSessionInfo *session) // IN: Session info
{
   HgfsSearch *search;

   MXUser_AcquireExclLock(session->searchArrayLock);

   search = HgfsSearchHandle2Search(handle, session);
   if (NULL == search) {
      goto exit;
   }

   search->flags |= HGFS_SEARCH_FLAG_READ_ALL_ENTRIES;

exit:
   MXUser_ReleaseExclLock(session->searchArrayLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetDirEntry --
 *
 *    Returns a copy of the directory entry at the given index. If remove is set
 *    to TRUE, the existing result is also pruned and the remaining results
 *    are shifted up in the result array.
 *
 * Results:
 *    NULL if there was an error or no search results were left.
 *    Non-NULL if result was found. Caller must free it.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsServerGetDirEntry(HgfsHandle handle,                // IN: Handle to search
                      HgfsSessionInfo *session,         // IN: Session info
                      uint32 index,                     // IN: index to retrieve at
                      Bool remove,                      // IN: If true, removes the result
                      struct DirectoryEntry **dirEntry) // OUT: directory entry
{
   HgfsSearch *search;
   struct DirectoryEntry *dent = NULL;
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;

   MXUser_AcquireExclLock(session->searchArrayLock);

   search = HgfsSearchHandle2Search(handle, session);
   if (search == NULL) {
      status = HGFS_ERROR_INVALID_HANDLE;
      goto out;
   }

   /* No more entries or none. */
   if (search->dents == NULL) {
      goto out;
   }

   if (HGFS_SEARCH_LAST_ENTRY_INDEX == index) {
      /* Set the index to the final entry. */
      index = search->numDents - 1;
   }

   status = HgfsPlatformGetDirEntry(search,
                                    session,
                                    index,
                                    remove,
                                    &dent);
out:
   MXUser_ReleaseExclLock(session->searchArrayLock);
   *dirEntry = dent;

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSearchHandle2Search --
 *
 *    Retrieve the search a handle refers to.
 *
 * Results:
 *    The search if the handle is valid (i.e. it refers to an existing search
 *     that is currently in use)
 *    NULL if the handle is invalid
 *
 *    Caller should hold the session's searchArrayLock.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsSearch *
HgfsSearchHandle2Search(HgfsHandle handle,         // IN: handle
                        HgfsSessionInfo *session)  // IN: session info
{
   unsigned int i;
   HgfsSearch *search = NULL;

   ASSERT(session);
   ASSERT(session->searchArray);

   /* XXX: This O(n) lookup can and should be optimized. */
   for (i = 0; i < session->numSearches; i++) {
      if (!DblLnkLst_IsLinked(&session->searchArray[i].links) &&
          session->searchArray[i].handle == handle) {
         search = &session->searchArray[i];
         break;
      }
   }

   return search;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUpdateNodeNames --
 *
 *    Walk the node array and update all nodes that have the old file name to
 *    store the new file name.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    If there isn't enough memory to accommodate the new names, those file nodes
 *    that couldn't be updated are deleted.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsUpdateNodeNames(const char *oldLocalName,  // IN: Name of file to look for
                    const char *newLocalName,  // IN: Name to replace with
                    HgfsSessionInfo *session)  // IN: Session info
{
   HgfsFileNode *fileNode;
   unsigned int i;
   char *newBuffer;
   size_t newBufferLen;

   ASSERT(oldLocalName);
   ASSERT(newLocalName);
   ASSERT(session);
   ASSERT(session->nodeArray);

   newBufferLen = strlen(newLocalName);

   MXUser_AcquireExclLock(session->nodeArrayLock);

   for (i = 0; i < session->numNodes; i++) {
      fileNode = &session->nodeArray[i];

      /* If the node is on the free list, skip it. */
      if (fileNode->state == FILENODE_STATE_UNUSED) {
         continue;
      }

      if (strcmp(fileNode->utf8Name, oldLocalName) == 0) {
         newBuffer = malloc(newBufferLen + 1);
         if (!newBuffer) {
            LOG(4, "%s: Failed to update a node name.\n", __FUNCTION__);
            continue;
         }
         memcpy(newBuffer, newLocalName, newBufferLen);
         newBuffer[newBufferLen] = '\0';

         /* Update this name to the new name. */
         free(fileNode->utf8Name);
         fileNode->utf8Name = newBuffer;
         fileNode->utf8NameLen = newBufferLen;
      }
   }

   MXUser_ReleaseExclLock(session->nodeArrayLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerClose --
 *
 *    Handle a Close request.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerClose(HgfsInputParam *input)  // IN: Input params
{
   HgfsHandle file;
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   size_t replyPayloadSize = 0;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackCloseRequest(input->payload, input->payloadSize,
                              input->op, &file)) {
      LOG(4, "%s: close fh %u\n", __FUNCTION__, file);

      if (!HgfsRemoveFromCache(file, input->session)) {
         LOG(4, "%s: Could not remove the node from cache.\n", __FUNCTION__);
         status = HGFS_ERROR_INVALID_HANDLE;
      } else {
         HgfsFreeFileNode(file, input->session);
         if (!HgfsPackCloseReply(input->packet, input->request, input->op,
                                 &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   } else {
      status = HGFS_ERROR_INTERNAL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSearchClose --
 *
 *    Handle a "Search Close" request.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSearchClose(HgfsInputParam *input)  // IN: Input params
{
   HgfsHandle search;
   HgfsInternalStatus status;
   size_t replyPayloadSize = 0;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackSearchCloseRequest(input->payload, input->payloadSize,
                                    input->op, &search)) {
      LOG(4, "%s: close search #%u\n", __FUNCTION__, search);

      if (HgfsRemoveSearch(search, input->session)) {
         if (HgfsPackSearchCloseReply(input->packet, input->request,
                                      input->op,
                                      &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_SUCCESS;
         } else {
            status = HGFS_ERROR_INTERNAL;
         }
      } else {
         /* Invalid handle */
         LOG(4, "%s: invalid handle %u\n", __FUNCTION__, search);
         status = HGFS_ERROR_INVALID_HANDLE;
      }
   } else {
      status = HGFS_ERROR_INTERNAL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


#define HGFS_SIZEOF_OP(type) (sizeof (type) + sizeof (HgfsRequest))

/* Opcode handlers, indexed by opcode */
static struct {
   void (*handler)(HgfsInputParam *input);
   /* Minimal size of the request packet */
   unsigned int minReqSize;

   /* How do you process the request {sync, async} ? */
   RequestHint reqType;

} const handlers[] = {
   { HgfsServerOpen,             sizeof (HgfsRequestOpen),              REQ_SYNC },
   { HgfsServerRead,             sizeof (HgfsRequestRead),              REQ_SYNC },
   { HgfsServerWrite,            sizeof (HgfsRequestWrite),             REQ_SYNC },
   { HgfsServerClose,            sizeof (HgfsRequestClose),             REQ_SYNC },
   { HgfsServerSearchOpen,       sizeof (HgfsRequestSearchOpen),        REQ_SYNC },
   { HgfsServerSearchRead,       sizeof (HgfsRequestSearchRead),        REQ_SYNC },
   { HgfsServerSearchClose,      sizeof (HgfsRequestSearchClose),       REQ_SYNC },
   { HgfsServerGetattr,          sizeof (HgfsRequestGetattr),           REQ_SYNC },
   { HgfsServerSetattr,          sizeof (HgfsRequestSetattr),           REQ_SYNC },
   { HgfsServerCreateDir,        sizeof (HgfsRequestCreateDir),         REQ_SYNC },
   { HgfsServerDeleteFile,       sizeof (HgfsRequestDelete),            REQ_SYNC },
   { HgfsServerDeleteDir,        sizeof (HgfsRequestDelete),            REQ_SYNC },
   { HgfsServerRename,           sizeof (HgfsRequestRename),            REQ_SYNC },
   { HgfsServerQueryVolume,      sizeof (HgfsRequestQueryVolume),       REQ_SYNC },

   { HgfsServerOpen,             sizeof (HgfsRequestOpenV2),            REQ_SYNC },
   { HgfsServerGetattr,          sizeof (HgfsRequestGetattrV2),         REQ_SYNC },
   { HgfsServerSetattr,          sizeof (HgfsRequestSetattrV2),         REQ_SYNC },
   { HgfsServerSearchRead,       sizeof (HgfsRequestSearchReadV2),      REQ_SYNC },
   { HgfsServerSymlinkCreate,    sizeof (HgfsRequestSymlinkCreate),     REQ_SYNC },
   { HgfsServerServerLockChange, sizeof (HgfsRequestServerLockChange),  REQ_SYNC },
   { HgfsServerCreateDir,        sizeof (HgfsRequestCreateDirV2),       REQ_SYNC },
   { HgfsServerDeleteFile,       sizeof (HgfsRequestDeleteV2),          REQ_SYNC },
   { HgfsServerDeleteDir,        sizeof (HgfsRequestDeleteV2),          REQ_SYNC },
   { HgfsServerRename,           sizeof (HgfsRequestRenameV2),          REQ_SYNC },

   { HgfsServerOpen,             HGFS_SIZEOF_OP(HgfsRequestOpenV3),             REQ_SYNC },
   { HgfsServerRead,             HGFS_SIZEOF_OP(HgfsRequestReadV3),             REQ_SYNC },
   { HgfsServerWrite,            HGFS_SIZEOF_OP(HgfsRequestWriteV3),            REQ_SYNC },
   { HgfsServerClose,            HGFS_SIZEOF_OP(HgfsRequestCloseV3),            REQ_SYNC },
   { HgfsServerSearchOpen,       HGFS_SIZEOF_OP(HgfsRequestSearchOpenV3),       REQ_SYNC },
   { HgfsServerSearchRead,       HGFS_SIZEOF_OP(HgfsRequestSearchReadV3),       REQ_SYNC },
   { HgfsServerSearchClose,      HGFS_SIZEOF_OP(HgfsRequestSearchCloseV3),      REQ_SYNC },
   { HgfsServerGetattr,          HGFS_SIZEOF_OP(HgfsRequestGetattrV3),          REQ_SYNC },
   { HgfsServerSetattr,          HGFS_SIZEOF_OP(HgfsRequestSetattrV3),          REQ_SYNC },
   { HgfsServerCreateDir,        HGFS_SIZEOF_OP(HgfsRequestCreateDirV3),        REQ_SYNC },
   { HgfsServerDeleteFile,       HGFS_SIZEOF_OP(HgfsRequestDeleteV3),           REQ_SYNC },
   { HgfsServerDeleteDir,        HGFS_SIZEOF_OP(HgfsRequestDeleteV3),           REQ_SYNC },
   { HgfsServerRename,           HGFS_SIZEOF_OP(HgfsRequestRenameV3),           REQ_SYNC },
   { HgfsServerQueryVolume,      HGFS_SIZEOF_OP(HgfsRequestQueryVolumeV3),      REQ_SYNC },
   { HgfsServerSymlinkCreate,    HGFS_SIZEOF_OP(HgfsRequestSymlinkCreateV3),    REQ_SYNC },
   { HgfsServerServerLockChange, sizeof (HgfsRequestServerLockChange),          REQ_SYNC },
   { HgfsServerWriteWin32Stream, HGFS_SIZEOF_OP(HgfsRequestWriteWin32StreamV3), REQ_SYNC },
   /*
    * Starting from HGFS_OP_CREATE_SESSION_V4 (all V4 commands and above) the
    * second field is the minimum size for actual HGFS operational request
    * and not the minimum size of operational request with a header.
    */
   { HgfsServerCreateSession,    sizeof (HgfsRequestCreateSessionV4),              REQ_SYNC},
   { HgfsServerDestroySession,   sizeof (HgfsRequestDestroySessionV4),             REQ_SYNC},
   { HgfsServerRead,             sizeof (HgfsRequestReadV3),                       REQ_ASYNC},
   { HgfsServerWrite,            sizeof (HgfsRequestWriteV3),                      REQ_ASYNC},
   { HgfsServerSetDirNotifyWatch,    sizeof (HgfsRequestSetWatchV4),               REQ_SYNC},
   { HgfsServerRemoveDirNotifyWatch, sizeof (HgfsRequestRemoveWatchV4),            REQ_SYNC},
   { NULL,                       0,                                                REQ_SYNC}, // No Op notify
   { HgfsServerSearchRead,       sizeof (HgfsRequestSearchReadV4),                 REQ_SYNC},

};


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerInputAllocInit --
 *
 *    Allocates and initializes the input params object with the operation parameters.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerInputAllocInit(HgfsPacket *packet,                        // IN: packet
                         HgfsTransportSessionInfo *transportSession,// IN: session
                         HgfsSessionInfo *session,                  // IN: session Id
                         const void *request,                       // IN: HGFS packet
                         size_t requestSize,                        // IN: request packet size
                         Bool sessionEnabled,                       // IN: session enabled request
                         uint32 requestId,                          // IN: unique request id
                         HgfsOp requestOp,                          // IN: op
                         size_t requestOpArgsSize,                  // IN: op args size
                         const void *requestOpArgs,                 // IN: op args
                         HgfsInputParam **params)                   // OUT: parameters
{
   HgfsInputParam *localParams;

   localParams = Util_SafeCalloc(1, sizeof *localParams);

   localParams->packet = packet;
   localParams->request = request;
   localParams->requestSize = requestSize;
   localParams->transportSession = transportSession;
   localParams->session = session;
   localParams->id = requestId;
   localParams->sessionEnabled = sessionEnabled;
   localParams->op = requestOp;
   localParams->payload = requestOpArgs;
   localParams->payloadSize = requestOpArgsSize;

   if (NULL != localParams->payload) {
      localParams->payloadOffset = (char *)localParams->payload -
                                   (char *)localParams->request;
   }
   *params = localParams;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerInputExit --
 *
 *    Tearsdown and frees the input params object with the operation parameters.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerInputExit(HgfsInputParam *params)                        // IN: packet
{
   if (NULL != params->session) {
      HgfsServerSessionPut(params->session);
   }
   HgfsServerTransportSessionPut(params->transportSession);
   free(params);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetRequest --
 *
 *    Takes the Hgfs packet and extracts the operation parameters.
 *    This validates the incoming packet as part of the processing.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS if all the request parameters are successfully extracted.
 *    HGFS_ERROR_INTERNAL if an error occurs without sufficient request data to be
 *    able to send a reply to the client.
 *    Any other appropriate error if the incoming packet has errors and there is
 *    sufficient information to send a response.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerGetRequest(HgfsPacket *packet,                        // IN: packet
                     HgfsTransportSessionInfo *transportSession,// IN: session
                     HgfsInputParam **input)                    // OUT: parameters
{
   HgfsSessionInfo *session = NULL;
   uint64 sessionId = HGFS_INVALID_SESSION_ID;
   Bool sessionEnabled = FALSE;
   uint32 requestId = 0;
   HgfsOp opcode;
   const void *request;
   size_t requestSize;
   const void *requestOpArgs;
   size_t requestOpArgsSize;
   HgfsInternalStatus parseStatus = HGFS_ERROR_SUCCESS;

   request = HSPU_GetMetaPacket(packet, &requestSize, transportSession->channelCbTable);

   if (NULL == request) {
      /*
       * How can I return error back to the client, clearly the client is either broken or
       * malicious? We cannot continue from here.
       */
      parseStatus = HGFS_ERROR_INTERNAL;
      goto exit;
   }

   parseStatus = HgfsUnpackPacketParams(request,
                                        requestSize,
                                        &sessionEnabled,
                                        &sessionId,
                                        &requestId,
                                        &opcode,
                                        &requestOpArgsSize,
                                        &requestOpArgs);
   if (HGFS_ERROR_INTERNAL == parseStatus) {
      /* The packet was malformed and we cannot reply. */
      goto exit;
   }

   /*
    * Every request must be processed within an HGFS session, except create session.
    * If we don't already have an HGFS session for processing this request,
    * then use or create the default session.
    */
   if (sessionEnabled) {
      if (opcode != HGFS_OP_CREATE_SESSION_V4) {
         session = HgfsServerTransportGetSessionInfo(transportSession,
                                                     sessionId);
         if (NULL == session || session->state != HGFS_SESSION_STATE_OPEN) {
            LOG(4, "%s: HGFS packet with invalid session id!\n", __FUNCTION__);
            parseStatus = HGFS_ERROR_STALE_SESSION;
         }
      }
   } else {
      parseStatus = HgfsServerTransportGetDefaultSession(transportSession,
                                                         &session);
   }

   if (NULL != session) {
      session->isInactive = FALSE;
   }

   HgfsServerInputAllocInit(packet,
                            transportSession,
                            session,
                            request,
                            requestSize,
                            sessionEnabled,
                            requestId,
                            opcode,
                            requestOpArgsSize,
                            requestOpArgs,
                            input);

exit:
   return parseStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetHeaderSize --
 *
 *    Takes the Hgfs input and finds the size of the header component.
 *
 * Results:
 *    Size of the HGFS protocol header used by this request or reply.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerGetHeaderSize(Bool sessionEnabled,     // IN: session based request
                        HgfsOp op,               // IN: operation
                        Bool request)            // IN: TRUE for request, FALSE for reply
{
   size_t headerSize;

   /*
    * If the HGFS request is session enabled we must have the new header.
    * Any V4 operation always must have the new header too.
    * Otherwise, starting from HGFS V3 the header is not included in the
    * request itself, so we must return the size of the separate header
    * structure, for requests this will be HgfsRequest and replies will be HgfsReply.
    * Prior to V3 (so V1 and V2) there was no separate header from the request
    * or reply structure for any given operation, so a zero size is returned for these.
    */
   if (sessionEnabled) {
      headerSize = sizeof (HgfsHeader);
   } else if (op < HGFS_OP_CREATE_SESSION_V4 &&
              op >= HGFS_OP_OPEN_V3) {
      headerSize = (request ? sizeof (HgfsRequest) : sizeof (HgfsReply));
   } else {
      headerSize = 0;
   }
   return headerSize;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetRequestHeaderSize --
 *
 *    Takes the Hgfs request input and finds the size of the header component.
 *
 * Results:
 *    Size of the HGFS protocol header used by this request and reply.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerGetRequestHeaderSize(Bool sessionEnabled,     // IN: session based request
                               HgfsOp op)               // IN: operation
{
   return HgfsServerGetHeaderSize(sessionEnabled, op, TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetReplyHeaderSize --
 *
 *    Takes the Hgfs reply input and finds the size of the header component.
 *
 * Results:
 *    Size of the HGFS protocol header used by this reply.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerGetReplyHeaderSize(Bool sessionEnabled,     // IN: session based request
                             HgfsOp op)               // IN: operation
{
   return HgfsServerGetHeaderSize(sessionEnabled, op, FALSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerCompleteRequest --
 *
 *    Performs all necessary action which needed for completing HGFS request:
 *       1. Sends reply to the guest.
 *       2. Release allocated objects, mapped guest memory.
 *       3. Dereference objects that were referenced.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Reference to Session is dropped.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerCompleteRequest(HgfsInternalStatus status,   // IN: Status of the request
                          size_t replyPayloadSize,     // IN: sizeof the reply payload
                          HgfsInputParam *input)       // IN/OUT: request context
{
   void *reply;
   size_t replySize;
   size_t replyTotalSize;
   size_t replyHeaderSize;
   uint64 replySessionId;

   if (HGFS_ERROR_SUCCESS == status) {
      HGFS_ASSERT_INPUT(input);
   } else {
      ASSERT(input);
   }

   replySessionId =  (NULL != input->session) ? input->session->sessionId
                                              : HGFS_INVALID_SESSION_ID;
   replyHeaderSize = HgfsServerGetReplyHeaderSize(input->sessionEnabled,
                                                  input->op);

   if (replyHeaderSize != 0) {
      replySize = replyHeaderSize + replyPayloadSize;
   } else {
      /*
       * For pre-V3 header is included in the payload size.
       * If we want to send just an error result then HgfsReply
       * only size is required.
       *
       * XXX - all callers should be verified that the reply payload
       * size for V1 and V2 should be correct (mininum HgfsReply size).
       */
      replySize = MAX(replyPayloadSize, sizeof (HgfsReply));
   }

   reply = HSPU_GetReplyPacket(input->packet,
                               input->transportSession->channelCbTable,
                               replySize,
                               &replyTotalSize);

   ASSERT(reply && (replySize <= replyTotalSize));
   if (!HgfsPackReplyHeader(status, replyPayloadSize, input->sessionEnabled, replySessionId,
                           input->id, input->op, HGFS_PACKET_FLAG_REPLY, replyTotalSize,
                           reply)) {
      Log("%s: Error packing header!\n", __FUNCTION__);
      goto exit;
   }

   if (!HgfsPacketSend(input->packet,
                       input->transportSession,
                       input->session,
                       0)) {
      /* Send failed. Drop the reply. */
      Log("%s: Error sending reply\n", __FUNCTION__);
   }

exit:
   HgfsServerInputExit(input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerProcessRequest --
 *
 *    Dispatch an incoming packet (in packetIn) to a handler function.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerProcessRequest(void *context)
{
   HgfsInputParam *input = (HgfsInputParam *)context;
   if (!input->request) {
      input->request = HSPU_GetMetaPacket(input->packet,
                                          &input->requestSize,
                                          input->transportSession->channelCbTable);
   }

   input->payload = (char *)input->request + input->payloadOffset;
   (*handlers[input->op].handler)(input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSessionReceive --
 *
 *    Dispatch an incoming packet (in packetIn) to a handler function.
 *
 *    This function cannot fail; if something goes wrong, it returns
 *    a packet containing only a reply header with error code.
 *
 *    The handler function can send the reply packet either using
 *    HgfsPacketSend helper functions. This function would return error
 *    as a reply if the op handler do not return HGFS_ERROR_SUCCESS.
 *
 *    NOTE: If any op handler needs to keep packetIn around for sending replies
 *    at a later point (possibly in a different thread context), it should
 *    make a copy of it. The validity of packetIn for the HGFS server is only
 *    within the scope of this function.
 *
 *    Definitions of Meta Packet, Data packet can be looked up in
 *    hgfsChannelVmci.c
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSessionReceive(HgfsPacket *packet,      // IN: Hgfs Packet
                         void *clientData)        // IN: session info
{
   HgfsTransportSessionInfo *transportSession = clientData;
   HgfsInternalStatus status;
   HgfsInputParam *input = NULL;

   ASSERT(transportSession);

   if (transportSession->state == HGFS_SESSION_STATE_CLOSED) {
      LOG(4, "%s: %d: Received packet after disconnected.\n", __FUNCTION__,
          __LINE__);
      return;
   }

   HgfsServerTransportSessionGet(transportSession);

   status = HgfsServerGetRequest(packet, transportSession, &input);
   if (HGFS_ERROR_INTERNAL == status) {
      LOG(4, "%s: %d: Error: packet invalid and cannot reply %d.\n ",
          __FUNCTION__, __LINE__, status);
      HgfsServerTransportSessionPut(transportSession);
      return;
   }

   HGFS_ASSERT_MINIMUM_OP(input->op);
   HGFS_ASSERT_CLIENT(input->op);

   if (HGFS_ERROR_SUCCESS == status) {
      HGFS_ASSERT_INPUT(input);
      if ((input->op < ARRAYSIZE(handlers)) &&
          (handlers[input->op].handler != NULL) &&
          (input->requestSize >= handlers[input->op].minReqSize)) {
         /* Initial validation passed, process the client request now. */
         /*
          * Server will only handle the request asynchronously when both the
          * server and client support asynchronous IO, this is indicated by
          * bit HGFS_SESSION_ASYNC_IO_ENABLED in input->session->flags which
          * is negotiated when the session is created.
          */
         if ((handlers[input->op].reqType == REQ_ASYNC) &&
             (transportSession->channelCapabilities.flags & HGFS_CHANNEL_ASYNC) &&
             (input->session->flags & HGFS_SESSION_ASYNC_IO_ENABLED)) {
             packet->state |= HGFS_STATE_ASYNC_REQUEST;
         }
         if (0 != (packet->state & HGFS_STATE_ASYNC_REQUEST)) {
            LOG(4, "%s: %d: @@Async\n", __FUNCTION__, __LINE__);
#ifndef VMX86_TOOLS
            /*
             * Asynchronous processing is supported by the transport.
             * We can release mappings here and reacquire when needed.
             */
            HSPU_PutMetaPacket(packet, transportSession->channelCbTable);
            input->request = NULL;
            HgfsServerAsyncInfoIncCount(&input->session->asyncRequestsInfo);

            if (gHgfsThreadpoolActive) {
               if (!HgfsThreadpool_QueueWorkItem(HgfsServerProcessRequest, input)) {
                  LOG(4, "%s: %d: failed to queue item.\n", __FUNCTION__, __LINE__);
                  HgfsServerProcessRequest(input);
               }
            } else {
                /* Remove pending requests during poweroff. */
                Poll_Callback(POLL_CS_MAIN,
                              POLL_FLAG_REMOVE_AT_POWEROFF,
                              HgfsServerProcessRequest,
                              input,
                              POLL_REALTIME,
                              1000,
                              NULL);
            }
#else
            /* Tools code should never process request async. */
            ASSERT(0);
#endif
         } else {
            LOG(4, "%s: %d: ##Sync\n", __FUNCTION__, __LINE__);
            HgfsServerProcessRequest(input);
         }
      } else {
         /*
          * The input packet is smaller than the minimal size needed for the
          * operation.
          */
         status = HGFS_ERROR_PROTOCOL;
         LOG(4, "%s: %d: Possible BUG! Malformed packet.\n", __FUNCTION__,
             __LINE__);
      }
   }

   /* Send error if we fail to process the op. */
   if (HGFS_ERROR_SUCCESS != status) {
      LOG(4, "Error %d occurred parsing the packet\n", (uint32)status);
      HgfsServerCompleteRequest(status, 0, input);
   }

   /*
    * Contrary to Coverity analysis, storage pointed to by the variable
    * "input" is not leaked; it is freed either by HgfsServerProcessRequest
    * at the end of request processing or by HgfsServerCompleteRequest if
    * there is a protocol error.  However, no Coverity annotation for
    * leaked_storage is added here because such an annotation cannot be
    * made specific to input; as a result, if any actual memory leaks were
    * to be introduced by a future change, the leaked_storage annotation
    * would cause such new leaks to be flagged as false positives.
    *
    * XXX - can something be done with Coverity function models so that
    * Coverity stops reporting this?
    */
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerTransportGetSessionInfo --
 *
 *   Scans the list of sessions and return the session with the specified
 *   session id.
 *
 * Results:
 *    A valid pointer to HgfsSessionInfo if there is a session with the
 *    specified session id. NULL, otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsSessionInfo *
HgfsServerTransportGetSessionInfo(HgfsTransportSessionInfo *transportSession,       // IN: transport session info
                                  uint64 sessionId)                                 // IN: session id
{
   DblLnkLst_Links *curr;
   HgfsSessionInfo *session = NULL;

   ASSERT(transportSession);

   if (HGFS_INVALID_SESSION_ID == sessionId) {
      return NULL;
   }

   MXUser_AcquireExclLock(transportSession->sessionArrayLock);

   DblLnkLst_ForEach(curr, &transportSession->sessionArray) {
      session = DblLnkLst_Container(curr, HgfsSessionInfo, links);
      if (session->sessionId == sessionId) {
         HgfsServerSessionGet(session);
         break;
      }
      session = NULL;
   }

   MXUser_ReleaseExclLock(transportSession->sessionArrayLock);

   return session;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerTransportGetDefaultSession --
 *
 *    Returns default session if there is one, otherwise creates it.
 *    XXX - this function should be moved to the HgfsServer file.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS and the session if found or created successfully
 *    or an appropriate error if no memory or cannot add to the list of sessions.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerTransportGetDefaultSession(HgfsTransportSessionInfo *transportSession, // IN: transport
                                     HgfsSessionInfo **session)                  // OUT: session
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   HgfsSessionInfo *defaultSession;
   HgfsCreateSessionInfo info = { 0 };
   defaultSession = HgfsServerTransportGetSessionInfo(transportSession,
                                                      transportSession->defaultSessionId);
   if (NULL != defaultSession) {
      /* The default session already exists, we are done. */
      goto exit;
   }

   /*
    * Create a new session if the default session doesn't exist.
    */
   if (!HgfsServerAllocateSession(transportSession,
                                  info,
                                  &defaultSession)) {
      status = HGFS_ERROR_NOT_ENOUGH_MEMORY;
      goto exit;
   }

   status = HgfsServerTransportAddSessionToList(transportSession,
                                                defaultSession);
   if (HGFS_ERROR_SUCCESS != status) {
      LOG(4, "%s: Could not add session to the list.\n", __FUNCTION__);
      HgfsServerSessionPut(defaultSession);
      defaultSession = NULL;
      goto exit;
   }

   transportSession->defaultSessionId = defaultSession->sessionId;
   HgfsServerSessionGet(defaultSession);

exit:
   *session = defaultSession;
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerTransportRemoveSessionFromList --
 *
 *   Unlinks the specified session info from the list.
 *
 *   Note: The caller must acquire the sessionArrayLock in transportSession
 *   before calling this function.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServerTransportRemoveSessionFromList(HgfsTransportSessionInfo *transportSession,   // IN: transport session info
                                         HgfsSessionInfo *session)                     // IN: session info
{
   ASSERT(transportSession);
   ASSERT(session);

   DblLnkLst_Unlink1(&session->links);
   transportSession->numSessions--;
   HgfsServerSessionPut(session);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerTransportAddSessionToList --
 *
 *    Links the specified session info to the list.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS if the session is successfully added to the list,
 *    HGFS_ERROR_TOO_MANY_SESSIONS if maximum number of sessions were already
 *                                 added to the list.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerTransportAddSessionToList(HgfsTransportSessionInfo *transportSession,       // IN: transport session info
                                    HgfsSessionInfo *session)                         // IN: session info
{
   HgfsInternalStatus status = HGFS_ERROR_TOO_MANY_SESSIONS;

   ASSERT(transportSession);
   ASSERT(session);

   MXUser_AcquireExclLock(transportSession->sessionArrayLock);

   if (transportSession->numSessions == MAX_SESSION_COUNT) {
      goto quit;
   }

   DblLnkLst_LinkLast(&transportSession->sessionArray, &session->links);
   transportSession->numSessions++;
   HgfsServerSessionGet(session);
   status = HGFS_ERROR_SUCCESS;

quit:
   MXUser_ReleaseExclLock(transportSession->sessionArrayLock);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSharesDeleteStale --
 *
 *    This function iterates through all shared folders and removes all
 *    deleted shared folders, removes them from notification package and
 *    from the folders list.
 *
 *    Note: this assumes that the list lock is already acquired.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSharesDeleteStale(DblLnkLst_Links *newSharesList)  // IN: new list of shares
{
   DblLnkLst_Links *link, *nextElem;

   DblLnkLst_ForEachSafe(link, nextElem, &gHgfsSharedFoldersList) {
      HgfsSharedFolderProperties *currentShare =
         DblLnkLst_Container(link, HgfsSharedFolderProperties, links);
      Bool staleShare = TRUE;
      DblLnkLst_Links *linkNewShare, *nextNewShare;

      DblLnkLst_ForEachSafe(linkNewShare, nextNewShare, newSharesList) {
         HgfsSharedFolder *newShare =
            DblLnkLst_Container(linkNewShare, HgfsSharedFolder, links);

         ASSERT(newShare);

         if (strcmp(currentShare->name, newShare->name) == 0) {
            LOG(4, "%s: %s is still valid\n", __FUNCTION__, newShare->name);
            staleShare = FALSE;
            break;
         }
      }

      if (staleShare) {
         LOG(8, "%s: removing shared folder handle %#x\n",
             __FUNCTION__, currentShare->notificationHandle);
         if (!HgfsNotify_RemoveSharedFolder(currentShare->notificationHandle)) {
            LOG(4, "%s: Error: removing %d shared folder handle\n",
                __FUNCTION__, currentShare->notificationHandle);
         }
         DblLnkLst_Unlink1(link);
         free(currentShare->name);
         free(currentShare);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerShareAddInternal --
 *
 *    Add a new shared folder property entry if it is not in the list of shares.
 *
 *    Note: this assumes that the list lock is already acquired.
 *
 * Results:
 *    The share handle if entry was found HGFS_INVALID_FOLDER_HANDLE if not.
 *
 * Side effects:
 *    May add a shared folders entry for change notifications.
 *
 *-----------------------------------------------------------------------------
 */

static HgfsSharedFolderHandle
HgfsServerShareAddInternal(const char *shareName,   // IN: shared folder name
                           const char *sharePath)   // IN: shared folder path
{
   HgfsSharedFolderHandle handle = HGFS_INVALID_FOLDER_HANDLE;
   DblLnkLst_Links *link, *nextElem;

   DblLnkLst_ForEachSafe(link, nextElem, &gHgfsSharedFoldersList) {
      HgfsSharedFolderProperties *currentShare =
         DblLnkLst_Container(link, HgfsSharedFolderProperties, links);

      ASSERT(currentShare);

      if (strcmp(currentShare->name, shareName) == 0) {
         LOG(8, "%s: Share is not new\n", __FUNCTION__);
         handle = currentShare->notificationHandle;
         break;
      }
   }

   /* If the share is new then add it to the notification shares. */
   if (handle == HGFS_INVALID_FOLDER_HANDLE) {
      handle = HgfsNotify_AddSharedFolder(sharePath, shareName);
      if (HGFS_INVALID_FOLDER_HANDLE != handle) {
         HgfsSharedFolderProperties *shareProps =
            (HgfsSharedFolderProperties *)Util_SafeMalloc(sizeof *shareProps);

         shareProps->notificationHandle = handle;
         shareProps->name = Util_SafeStrdup(shareName);
         DblLnkLst_Init(&shareProps->links);
         DblLnkLst_LinkLast(&gHgfsSharedFoldersList, &shareProps->links);
      }

      LOG(8, "%s: %s, %s, add hnd %#x\n",__FUNCTION__,
          (shareName ? shareName : "NULL"),
          (sharePath ? sharePath : "NULL"),
          handle);
   }
   return handle;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerShareAdd --
 *
 *    Add a new shared folder property entry if it is not in the list of shares.
 *
 *    Note: this assumes that the list lock is already acquired.
 *
 * Results:
 *    The shares handle if found or added. HGFS_INVALID_FOLDER_HANDLE otherwise.
 *
 * Side effects:
 *    May add a shared folders entry for change notifications.
 *
 *-----------------------------------------------------------------------------
 */

static HgfsSharedFolderHandle
HgfsServerShareAdd(const char *shareName,   // IN: shared folder name
                   const char *sharePath)   // IN: shared folder path
{
   HgfsSharedFolderHandle handle;

   LOG(8, "%s: entered\n", __FUNCTION__);

   if (!gHgfsDirNotifyActive) {
      LOG(8, "%s: notification disabled\n", __FUNCTION__);
      return HGFS_INVALID_FOLDER_HANDLE;
   }

   MXUser_AcquireExclLock(gHgfsSharedFoldersLock);
   handle = HgfsServerShareAddInternal(shareName, sharePath);
   MXUser_ReleaseExclLock(gHgfsSharedFoldersLock);

   LOG(8, "%s: exit(%#x)\n", __FUNCTION__, handle);
   return handle;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSharesReset --
 *
 *    This is a callback function which is invoked by hgfsServerManagement
 *    for every shared folder when something changed in shared folders
 *    configuration.
 *    The function any entries now not present as stale and removes them.
 *    Any entries on the new list of shares not on the list of list of shares
 *    will have new entries created and added to the list.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    May add an entry to known shared folders list.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSharesReset(DblLnkLst_Links *newSharesList)  // IN: List of new shares
{
   DblLnkLst_Links *linkNewShare, *nextNewShare;

   LOG(8, "%s: entered\n", __FUNCTION__);

   if (!gHgfsDirNotifyActive) {
      LOG(8, "%s: notification disabled\n", __FUNCTION__);
      return;
   }

   MXUser_AcquireExclLock(gHgfsSharedFoldersLock);

   /*
    * Now we go through the shares properties list to
    * remove and delete those shares that are stale.
    */
   HgfsServerSharesDeleteStale(newSharesList);

   /*
    * Iterate over the new shares and check for any not in the updated shares properties
    * list, as those will need a new share property created and added to the list.
    */
   DblLnkLst_ForEachSafe(linkNewShare, nextNewShare, newSharesList) {
      HgfsSharedFolder *newShare =
         DblLnkLst_Container(linkNewShare, HgfsSharedFolder, links);

      ASSERT(newShare);

      HgfsServerShareAddInternal(newShare->name, newShare->path);
   }

   MXUser_ReleaseExclLock(gHgfsSharedFoldersLock);
   LOG(8, "%s: exit\n", __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetShareHandle --
 *
 *    The function returns shared folder notification handle for the specified
 *    shared folder.
 *
 * Results:
 *    HgfsSharedFolderHandle that corresponds to the shared folder.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static HgfsSharedFolderHandle
HgfsServerGetShareHandle(const char *shareName)  // IN: name of the shared folder
{
   DblLnkLst_Links *link;
   HgfsSharedFolderHandle result = HGFS_INVALID_FOLDER_HANDLE;

   if (!gHgfsDirNotifyActive) {
      return HGFS_INVALID_FOLDER_HANDLE;
   }

   MXUser_AcquireExclLock(gHgfsSharedFoldersLock);

   DblLnkLst_ForEach(link, &gHgfsSharedFoldersList) {
      HgfsSharedFolderProperties *folder =
         DblLnkLst_Container(link, HgfsSharedFolderProperties, links);
      if (strcmp(folder->name, shareName) == 0) {
         result = folder->notificationHandle;
         break;
      }
   }
   MXUser_ReleaseExclLock(gHgfsSharedFoldersLock);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetShareName --
 *
 *    Get the share name for a shared folder handle by looking at the
 *    requested handle, finding the matching share (if any), and returning
 *    the share's name.
 *
 * Results:
 *    An Bool value indicating if the result is returned.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsServerGetShareName(HgfsSharedFolderHandle sharedFolder, // IN: Notify handle
                       size_t *shareNameLen,                // OUT: Name length
                       char **shareName)                    // OUT: Share name
{
   Bool result = FALSE;
   DblLnkLst_Links *link;

   if (!gHgfsDirNotifyActive) {
      return FALSE;
   }

   MXUser_AcquireExclLock(gHgfsSharedFoldersLock);

   DblLnkLst_ForEach(link, &gHgfsSharedFoldersList) {
      HgfsSharedFolderProperties *folder =
         DblLnkLst_Container(link, HgfsSharedFolderProperties, links);
      if (folder->notificationHandle == sharedFolder) {
         *shareName = Util_SafeStrdup(folder->name);
         result = TRUE;
         *shareNameLen = strlen(*shareName);
         break;
      }
   }
   MXUser_ReleaseExclLock(gHgfsSharedFoldersLock);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServer_InitState --
 *
 *    Initialize the global server state
 *
 * Results:
 *    TRUE if succeeded, FALSE if failed.
 *
 * Side effects:
 *    Memory allocation.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServer_InitState(const HgfsServerCallbacks **callbackTable,   // IN/OUT: our callbacks
                     HgfsServerConfig *serverCfgData,             // IN: configurable settings
                     HgfsServerMgrCallbacks *serverMgrData)       // IN: mgr callback
{
   Bool result = TRUE;

   ASSERT(callbackTable);

   /* Save any server manager data for logging state updates.*/
   gHgfsMgrData = serverMgrData;

   if (NULL != serverCfgData) {
      gHgfsCfgSettings = *serverCfgData;
   }

   /*
    * Initialize the globals for handling the active shared folders.
    */

   DblLnkLst_Init(&gHgfsSharedFoldersList);
   gHgfsSharedFoldersLock = MXUser_CreateExclLock("sharedFoldersLock",
                                                  RANK_hgfsSharedFolders);

   if (!HgfsPlatformInit()) {
      LOG(4, "Could not initialize server platform specific \n");
      result = FALSE;
   }

   if (result) {
      *callbackTable = &gHgfsServerCBTable;

      if (0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_NOTIFY_ENABLED)) {
         gHgfsDirNotifyActive = HgfsNotify_Init(&gHgfsServerNotifyCBTable) == HGFS_STATUS_SUCCESS;
         Log("%s: initialized notification %s.\n", __FUNCTION__,
             (gHgfsDirNotifyActive ? "active" : "inactive"));
      }
      if (0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_OPLOCK_MONITOR_ENABLED)) {
         if (!HgfsServerOplockInit()) {
            Log("%s: failed to init oplock module.\n", __FUNCTION__);
            HgfsServerOplockDestroy();
            gHgfsCfgSettings.flags &= ~HGFS_CONFIG_OPLOCK_ENABLED;
            gHgfsCfgSettings.flags &= ~HGFS_CONFIG_OPLOCK_MONITOR_ENABLED;
         }
      }
      if (0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_THREADPOOL_ENABLED)) {
         gHgfsThreadpoolActive =
            HgfsThreadpool_Init() == HGFS_STATUS_SUCCESS;
         Log("%s: initialized threadpool %s.\n", __FUNCTION__,
             (gHgfsThreadpoolActive ? "active" : "inactive"));
      }
      if (0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_OPLOCK_MONITOR_ENABLED)) {
         if (!HgfsOplockMonitorInit()) {
            Log("%s: failed to init oplock monitor module.\n", __FUNCTION__);
            gHgfsCfgSettings.flags &= ~HGFS_CONFIG_OPLOCK_MONITOR_ENABLED;
         }
      }
   } else {
      HgfsServer_ExitState(); // Cleanup partially initialized state
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServer_ExitState --
 *
 *    Cleanup the global server state.
 *
 *    This function should be called when all other HGFS threads stopped
 *    running. Otherwise we'll be in trouble because this is where we delete
 *    the node array lock.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServer_ExitState(void)
{
   if (0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_OPLOCK_MONITOR_ENABLED)) {
      HgfsOplockMonitorDestroy();
   }
   if (   0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_OPLOCK_ENABLED)
       || 0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_OPLOCK_MONITOR_ENABLED)) {
      HgfsServerOplockDestroy();
   }
   if (gHgfsDirNotifyActive) {
      DblLnkLst_Links emptySharesList;
      DblLnkLst_Init(&emptySharesList);

      /* Make all existing shared folders stale and delete them. */
      HgfsServerSharesReset(&emptySharesList);
      HgfsNotify_Exit();
      gHgfsDirNotifyActive = FALSE;
      Log("%s: exit notification - inactive.\n", __FUNCTION__);
   }

   if (NULL != gHgfsSharedFoldersLock) {
      MXUser_DestroyExclLock(gHgfsSharedFoldersLock);
      gHgfsSharedFoldersLock = NULL;
   }

   if (gHgfsThreadpoolActive) {
      HgfsThreadpool_Exit();
      gHgfsThreadpoolActive = FALSE;
      Log("%s: exit threadpool - inactive.\n", __FUNCTION__);
   }

   HgfsPlatformDestroy();

   /*
    * Reset the server manager callbacks.
    */
   gHgfsMgrData = NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServer_ShareAccessCheck --
 *
 *    Checks if the requested mode may be granted depending on read/write
 *    permissions.
 *
 * Results:
 *    An HgfsNameStatus value indicating the result is returned.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServer_ShareAccessCheck(HgfsOpenMode accessMode,  // IN: open mode to check
                            Bool shareWriteable,      // IN: share is writable
                            Bool shareReadable)       // IN: share is readable
{
   /*
    * See if access is allowed in the requested mode.
    *
    * XXX We should be using bits instead of an enum for HgfsOpenMode.
    * Add it to the todo list. [bac]
    */

   switch (HGFS_OPEN_MODE_ACCMODE(accessMode)) {
   case HGFS_OPEN_MODE_READ_ONLY:
      if (!shareReadable) {
         LOG(4, "%s: Read access denied\n", __FUNCTION__);

         return FALSE;
      }
      break;

   case HGFS_OPEN_MODE_WRITE_ONLY:
      if (!shareWriteable) {
         LOG(4, "%s: Write access denied\n", __FUNCTION__);

         return FALSE;
      }
      break;

   case HGFS_OPEN_MODE_READ_WRITE:
      if (!shareReadable || !shareWriteable) {
         LOG(4, "%s: Read/write access denied\n", __FUNCTION__);

         return FALSE;
      }
      break;

   default:
      LOG(0, "%s: Invalid mode %d\n", __FUNCTION__, accessMode);
      ASSERT(FALSE);

      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGenerateSessionId --
 *
 *    Generates unique session id.
 *
 * Results:
 *    Unique 64-bit value.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static uint64
HgfsGenerateSessionId(void)
{
#ifdef VM_X86_ANY
   return RDTSC();
#else
   uint64 sessionId;
   rqContext *rCtx = Random_QuickSeed((uint32)time(NULL));
   sessionId = (uint64)Random_Quick(rCtx) << 32;
   sessionId |= Random_Quick(rCtx);
   free(rCtx);
   return sessionId;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSetSessionCapability --
 *
 *    Sets session capability for a specific operation code.
 *
 * Results:
 *    TRUE is the capability for the operation has been changed.
 *    FALSE if the operation is not represented in the capabilities array.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerSetSessionCapability(HgfsOp op,                  // IN: operation code
                               HgfsOpCapFlags flags,       // IN: flags that describe level of support
                               HgfsSessionInfo *session)   // IN/OUT: session to update
{
   int i;
   Bool result = FALSE;

   for ( i = 0; i < ARRAYSIZE(session->hgfsSessionCapabilities); i++) {
      if (session->hgfsSessionCapabilities[i].op == op) {
         session->hgfsSessionCapabilities[i].flags = flags;
         result = TRUE;
      }
   }
   LOG(4, "%s: Setting capability flags %x for op code %d %s\n",
       __FUNCTION__, flags, op, result ? "succeeded" : "failed");

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerResEnumInit --
 *
 *    Initialize an enumeration of all existing resources.
 *
 * Results:
 *    The enumeration state object.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void *
HgfsServerResEnumInit(void)
{
   void *enumState = NULL;

    if (gHgfsMgrData != NULL &&
       gHgfsMgrData->enumResources.init != NULL) {
      enumState = gHgfsMgrData->enumResources.init();
   }

   return enumState;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerResEnumGet --
 *
 *    Enumerates the next resource associated with the enumeration state.
 *
 * Results:
 *    TRUE on success and .
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerResEnumGet(void *enumState,           // IN/OUT: enumeration state
                     char const **enumResName,  // OUT: enumerated resource name
                     size_t *enumResNameLen,    // OUT: enumerated resource name len
                     Bool *enumResDone)         // OUT: enumerated resources done
{
   Bool success = FALSE;

    if (gHgfsMgrData != NULL &&
        gHgfsMgrData->enumResources.get != NULL) {
      success = gHgfsMgrData->enumResources.get(enumState,
                                                enumResName,
                                                enumResNameLen,
                                                enumResDone);
   }

   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerResEnumExit --
 *
 *    Exit the enumeration of all existing resources.
 *
 * Results:
 *    TRUE on success, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerResEnumExit(void *enumState)           // IN/OUT: enumeration state
{
   Bool success = FALSE;

   if (gHgfsMgrData != NULL &&
       gHgfsMgrData->enumResources.exit != NULL) {
       success = gHgfsMgrData->enumResources.exit(enumState);
   }

   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerEnumerateSharedFolders --
 *
 *    Enumerates all existing shared folders and registers shared folders with
 *    directory notification package.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsServerEnumerateSharedFolders(void)
{
   void *state;
   Bool success = FALSE;

   LOG(8, "%s: entered\n", __FUNCTION__);
   state = HgfsServerResEnumInit();
   if (NULL != state) {
      Bool done;

      do {
         char const *shareName;
         size_t len;

         success = HgfsServerResEnumGet(state, &shareName, &len, &done);
         if (success && !done) {
            HgfsSharedFolderHandle handle;
            char const *sharePath;
            size_t sharePathLen;
            HgfsNameStatus nameStatus;

            nameStatus = HgfsServerPolicy_GetSharePath(shareName, len,
                                                       HGFS_OPEN_MODE_READ_ONLY,
                                                       &sharePathLen, &sharePath);
            if (HGFS_NAME_STATUS_COMPLETE == nameStatus) {
               LOG(8, "%s: registering share %s path %s\n", __FUNCTION__,
                   shareName, sharePath);
               handle = HgfsServerShareAdd(shareName, sharePath);
               success = handle != HGFS_INVALID_FOLDER_HANDLE;
               LOG(8, "%s: registering share %s hnd %#x\n", __FUNCTION__,
                   shareName, handle);
            }
         }
      } while (!done && success);

      HgfsServerResEnumExit(state);
   }
   LOG(8, "%s: exit %d\n", __FUNCTION__, success);
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSessionConnect --
 *
 *    Initialize a new client session.
 *
 *    Allocate HgfsTransportSessionInfo and initialize it.
 *
 * Results:
 *    TRUE always.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsServerSessionConnect(void *transportData,                         // IN: transport session context
                         HgfsServerChannelCallbacks *channelCbTable,  // IN: Channel callbacks
                         HgfsServerChannelData *channelCapabilities,  // IN: channel capabilities
                         void **transportSessionData)                 // OUT: server session context
{
   ASSERT(transportSessionData);

   LOG(4, "%s: initting.\n", __FUNCTION__);

   *transportSessionData = HgfsServerTransportInit(transportData,
                                                   channelCbTable,
                                                   channelCapabilities);
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerTransportInit --
 *
 *      Init a transport session.
 *
 *      Allocated and initialize the transport session.
 *
 * Results:
 *      The initialized transport session object.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static HgfsTransportSessionInfo *
HgfsServerTransportInit(void *transportData,                         // IN: transport session context
                        HgfsServerChannelCallbacks *channelCbTable,  // IN: Channel callbacks
                        HgfsServerChannelData *channelCapabilities)  // IN: channel capabilities
{
   HgfsTransportSessionInfo *transportSession;

   transportSession = Util_SafeCalloc(1, sizeof *transportSession);
   transportSession->transportData = transportData;
   transportSession->channelCbTable = channelCbTable;
   transportSession->type = HGFS_SESSION_TYPE_REGULAR;
   transportSession->state = HGFS_SESSION_STATE_OPEN;
   transportSession->channelCapabilities = *channelCapabilities;
   transportSession->numSessions = 0;

   transportSession->sessionArrayLock =
         MXUser_CreateExclLock("HgfsSessionArrayLock",
                               RANK_hgfsSessionArrayLock);

   DblLnkLst_Init(&transportSession->sessionArray);

   transportSession->defaultSessionId = HGFS_INVALID_SESSION_ID;

   Atomic_Write(&transportSession->refCount, 0);

   /* Give our session a reference to hold while we are open. */
   HgfsServerTransportSessionGet(transportSession);
   return transportSession;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerTransportExit --
 *
 *      Exit by destroying the transport session.
 *
 *      Free session info data if no reference.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsServerTransportExit(HgfsTransportSessionInfo *transportSession)   // IN: transport session context
{
   DblLnkLst_Links *curr, *next;

   ASSERT(Atomic_Read(&transportSession->refCount) == 0);

   DblLnkLst_ForEachSafe(curr, next,  &transportSession->sessionArray) {
      HgfsSessionInfo *session = DblLnkLst_Container(curr, HgfsSessionInfo, links);
      HgfsServerTransportRemoveSessionFromList(transportSession, session);
      HgfsServerSessionPut(session);
   }

   MXUser_DestroyExclLock(transportSession->sessionArrayLock);
   free(transportSession);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerAsyncInfoInit --
 *
 *    Initialize the async request info for a session.
 *
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerAsyncInfoInit(HgfsAsyncRequestInfo *info) // IN/OUT: info
{
   Atomic_Write(&info->requestCount, 0);
   info->lock  = MXUser_CreateExclLock("asyncLock",
                                       RANK_hgfsSharedFolders);
   info->requestCountIsZero = MXUser_CreateCondVarExclLock(info->lock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerAsyncInfoExit --
 *
 *    Destroy the async request info for a session session.
 *
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerAsyncInfoExit(HgfsAsyncRequestInfo *info) // IN/OUT: info
{
   ASSERT(Atomic_Read(&info->requestCount) == 0);
   if (NULL != info->lock) {
      MXUser_DestroyExclLock(info->lock);
      info->lock = NULL;
   }

   if (NULL != info->requestCountIsZero) {
      MXUser_DestroyCondVar(info->requestCountIsZero);
      info->requestCountIsZero = NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerAsyncWaitForAllRequestsDone --
 *
 *    Wait for all the async info requests to be done.
 *
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerAsyncWaitForAllRequestsDone(HgfsAsyncRequestInfo *info) // IN: info
{
   MXUser_AcquireExclLock(info->lock);
   while (Atomic_Read(&info->requestCount)) {
      MXUser_WaitCondVarExclLock(info->lock, info->requestCountIsZero);
   }
   MXUser_ReleaseExclLock(info->lock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerAsyncSignalAllRequestsDone --
 *
 *    Signal that all the async info requests are done.
 *
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerAsyncSignalAllRequestsDone(HgfsAsyncRequestInfo *info) // IN: info
{
   MXUser_AcquireExclLock(info->lock);
   MXUser_BroadcastCondVar(info->requestCountIsZero);
   MXUser_ReleaseExclLock(info->lock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerAsyncInfoDecCount --
 *
 *    Decrement the async info request count for a session.
 *
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerAsyncInfoDecCount(HgfsAsyncRequestInfo *info) // IN/OUT: info
{
   if (Atomic_ReadDec32(&info->requestCount) == 1) {
      HgfsServerAsyncSignalAllRequestsDone(info);
   }
}


#ifndef VMX86_TOOLS
/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerAsyncInfoIncCount --
 *
 *    Increment the async info request count for a session.
 *
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerAsyncInfoIncCount(HgfsAsyncRequestInfo *info) // IN/OUT: info
{
   Atomic_Inc(&info->requestCount);
}
#endif // VMX86_TOOLS


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerAllocateSession --
 *
 *    Initialize a new Hgfs session.
 *
 *    Allocate HgfsSessionInfo and initialize it. Create the nodeArray and
 *    searchArray for the session.
 *
 * Results:
 *    TRUE on success, FALSE otherwise.
 *
 * Side effects:
 *    Allocates and initializes new session info.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsServerAllocateSession(HgfsTransportSessionInfo *transportSession, // IN:
                          HgfsCreateSessionInfo createSessionInfo,    // IN:
                          HgfsSessionInfo **sessionData)              // OUT:
{
   int i;
   HgfsSessionInfo *session;
   HgfsOpCapFlags flags;

   LOG(8, "%s: entered\n", __FUNCTION__);

   ASSERT(transportSession);

   session = Util_SafeCalloc(1, sizeof *session);

   /*
    * Initialize all our locks first as these can fail.
    */

   session->fileIOLock = MXUser_CreateExclLock("HgfsFileIOLock",
                                               RANK_hgfsFileIOLock);

   session->nodeArrayLock = MXUser_CreateExclLock("HgfsNodeArrayLock",
                                                  RANK_hgfsNodeArrayLock);

   session->searchArrayLock = MXUser_CreateExclLock("HgfsSearchArrayLock",
                                                    RANK_hgfsSearchArrayLock);

   session->sessionId = HgfsGenerateSessionId();
   session->state = HGFS_SESSION_STATE_OPEN;
   DblLnkLst_Init(&session->links);
   session->isInactive = TRUE;
   session->transportSession = transportSession;
   session->numInvalidationAttempts = 0;

   if (  createSessionInfo.maxPacketSize
       < transportSession->channelCapabilities.maxPacketSize) {
      session->maxPacketSize = createSessionInfo.maxPacketSize;
   } else {
      session->maxPacketSize = transportSession->channelCapabilities.maxPacketSize;
   }
   session->flags |= HGFS_SESSION_MAXPACKETSIZE_VALID;

   /*
    * If the server is enabled for processing oplocks and the client
    * is requesting to use them, then report back to the client oplocks
    * are enabled by propagating the session flag.
    */
   if ((0 != (createSessionInfo.flags & HGFS_SESSION_OPLOCK_ENABLED)) &&
       (0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_OPLOCK_ENABLED))) {
      session->flags |= HGFS_SESSION_OPLOCK_ENABLED;
   }

   /*
    * Initialize the node handling components.
    */

   DblLnkLst_Init(&session->nodeFreeList);
   DblLnkLst_Init(&session->nodeCachedList);

   /* Allocate array of FileNodes and add them to free list. */
   session->numNodes = NUM_FILE_NODES;
   session->nodeArray = Util_SafeCalloc(session->numNodes,
                                        sizeof (HgfsFileNode));
   session->numCachedOpenNodes = 0;
   session->numCachedLockedNodes = 0;

   for (i = 0; i < session->numNodes; i++) {
      DblLnkLst_Init(&session->nodeArray[i].links);
      /* Append at the end of the list. */
      DblLnkLst_LinkLast(&session->nodeFreeList, &session->nodeArray[i].links);
   }

   /*
    * Initialize the search handling components.
    */

   /* Initialize search freelist. */
   DblLnkLst_Init(&session->searchFreeList);

   /* Give our session a reference to hold while we are open. */
   Atomic_Write(&session->refCount, 1);

   /* Allocate array of searches and add them to free list. */
   session->numSearches = NUM_SEARCHES;
   session->searchArray = Util_SafeCalloc(session->numSearches,
                                          sizeof (HgfsSearch));

   for (i = 0; i < session->numSearches; i++) {
      DblLnkLst_Init(&session->searchArray[i].links);
      /* Append at the end of the list. */
      DblLnkLst_LinkLast(&session->searchFreeList,
                         &session->searchArray[i].links);
   }
   /* Initialize the async request info.*/
   HgfsServerAsyncInfoInit(&session->asyncRequestsInfo);

   /* Get common to all sessions capabiities. */
   HgfsServerGetDefaultCapabilities(session->hgfsSessionCapabilities,
                                    &session->numberOfCapabilities);

   if (transportSession->channelCapabilities.flags & HGFS_CHANNEL_SHARED_MEM) {
      flags = HGFS_OP_CAPFLAG_IS_SUPPORTED;
      if (   (0 != (createSessionInfo.flags & HGFS_SESSION_ASYNC_IO_ENABLED))
          && gHgfsThreadpoolActive) {
         if (HgfsThreadpool_Activate()) {
            session->flags |= HGFS_SESSION_ASYNC_IO_ENABLED;
            flags |= HGFS_SESSION_ASYNC_IO_ENABLED;
            LOG(8, "%s: threadpool is enabled\n", __FUNCTION__);
         } else {
            HgfsThreadpool_Exit();
            gHgfsThreadpoolActive = FALSE;
            Log("%s: failed to activate the threadpool\n", __FUNCTION__);
         }
      }
      HgfsServerSetSessionCapability(HGFS_OP_READ_FAST_V4,
                                     flags,
                                     session);
      HgfsServerSetSessionCapability(HGFS_OP_WRITE_FAST_V4,
                                     flags,
                                     session);

      if (gHgfsDirNotifyActive) {
         LOG(8, "%s: notify is enabled\n", __FUNCTION__);
         if (HgfsServerEnumerateSharedFolders()) {
            HgfsServerSetSessionCapability(HGFS_OP_SET_WATCH_V4,
                                           HGFS_OP_CAPFLAG_IS_SUPPORTED, session);
            HgfsServerSetSessionCapability(HGFS_OP_REMOVE_WATCH_V4,
                                           HGFS_OP_CAPFLAG_IS_SUPPORTED, session);
            session->flags |= HGFS_SESSION_CHANGENOTIFY_ENABLED;
         } else {
            HgfsServerSetSessionCapability(HGFS_OP_SET_WATCH_V4,
                                           HGFS_OP_CAPFLAG_NOT_SUPPORTED, session);
            HgfsServerSetSessionCapability(HGFS_OP_REMOVE_WATCH_V4,
                                           HGFS_OP_CAPFLAG_NOT_SUPPORTED, session);
         }
         LOG(8, "%s: session notify capability is %s\n", __FUNCTION__,
             (session->flags & HGFS_SESSION_CHANGENOTIFY_ENABLED ? "enabled" :
                                                                   "disabled"));
      }

      HgfsServerSetSessionCapability(HGFS_OP_SEARCH_READ_V4,
                                     HGFS_OP_CAPFLAG_IS_SUPPORTED, session);
   }

   if (0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_OPLOCK_MONITOR_ENABLED)) {
      /* Allocate symlink check status cache. */
      session->symlinkCache = HgfsCache_Alloc(HgfsCacheRemoveLRUCb);

      /* Allocate file attributes cache. */
      session->fileAttrCache = HgfsCache_Alloc(HgfsCacheRemoveLRUCb);
   }

   *sessionData = session;

   Log("%s: init session %p id %"FMT64"x\n", __FUNCTION__, session, session->sessionId);
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsDisconnectSessionInt --
 *
 *    Disconnect a client session.
 *
 *    Mark the session as closed as we are in the process of teardown
 *    of the session. No more new requests should be processed. We would
 *    start draining any outstanding pending operations at this point.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsDisconnectSessionInt(HgfsSessionInfo *session)    // IN: session context
{
   LOG(8, "%s: entered\n", __FUNCTION__);

   ASSERT(session);
   ASSERT(session->nodeArray);
   ASSERT(session->searchArray);

   session->state = HGFS_SESSION_STATE_CLOSED;
   LOG(8, "%s: exit\n", __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSessionDisconnect --
 *
 *    Disconnect a client session.
 *
 *    Mark the session as closed as we are in the process of teardown
 *    of the session. No more new requests should be processed. We would
 *    start draining any outstanding pending operations at this point.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSessionDisconnect(void *clientData)    // IN: session context
{
   HgfsTransportSessionInfo *transportSession = clientData;
   DblLnkLst_Links *curr, *next;

   LOG(8, "%s: entered\n", __FUNCTION__);

   ASSERT(transportSession);

   MXUser_AcquireExclLock(transportSession->sessionArrayLock);

   DblLnkLst_ForEachSafe(curr, next, &transportSession->sessionArray) {
      HgfsSessionInfo *session = DblLnkLst_Container(curr, HgfsSessionInfo, links);

      HgfsDisconnectSessionInt(session);
   }

   MXUser_ReleaseExclLock(transportSession->sessionArrayLock);

   transportSession->state = HGFS_SESSION_STATE_CLOSED;
   LOG(8, "%s: exit\n", __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSessionClose --
 *
 *    Closes a client session.
 *
 *    Remvoing the final reference will free the session's nodeArray
 *    and seachArrary, and finally free the session object.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSessionClose(void *clientData)    // IN: session context
{
   HgfsTransportSessionInfo *transportSession = clientData;

   LOG(8, "%s: entered\n", __FUNCTION__);

   ASSERT(transportSession);
   ASSERT(transportSession->state == HGFS_SESSION_STATE_CLOSED);

   /* Remove, typically, the last reference, will teardown everything. */
   HgfsServerTransportSessionPut(transportSession);
   LOG(8, "%s: exit\n", __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerExitSessionInternal --
 *
 *    Destroys a session.
 *
 *    Free the session's nodeArray and seachArrary. Free the session.
 *
 *    The caller must have previously acquired the global sessions lock.
 *
 * Results:
 *    TRUE on success, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerExitSessionInternal(HgfsSessionInfo *session)    // IN: session context
{
   int i;

   ASSERT(session);
   ASSERT(session->nodeArray);
   ASSERT(session->searchArray);

   ASSERT(session->state == HGFS_SESSION_STATE_CLOSED);

   /* Check and remove any notification handles we have for this session. */
   if (session->flags & HGFS_SESSION_CHANGENOTIFY_ENABLED) {
      LOG(8, "%s: calling notify component to disconnect\n", __FUNCTION__);
      /*
       * This routine will synchronize itself with notification generator.
       * Therefore, it will remove subscribers and prevent the event generator
       * from generating any new events while it locks the subscribers lists.
       * New events will continue once more but with the updated subscriber list
       * that will not contain this session.
       */
      HgfsNotify_RemoveSessionSubscribers(session);
   }

   MXUser_AcquireExclLock(session->nodeArrayLock);

   Log("%s: teardown session %p id 0x%"FMT64"x\n", __FUNCTION__, session, session->sessionId);

   /* Recycle all nodes that are still in use, then destroy the node pool. */
   for (i = 0; i < session->numNodes; i++) {
      HgfsHandle handle;

      if (session->nodeArray[i].state == FILENODE_STATE_UNUSED) {
         continue;
      }

      handle = HgfsFileNode2Handle(&session->nodeArray[i]);
      HgfsRemoveFromCacheInternal(handle, session);
      HgfsFreeFileNodeInternal(handle, session);
   }
   free(session->nodeArray);
   session->nodeArray = NULL;

   MXUser_ReleaseExclLock(session->nodeArrayLock);

   /*
    * Recycle all searches that are still in use, then destroy the
    * search pool.
    */

   MXUser_AcquireExclLock(session->searchArrayLock);

   for (i = 0; i < session->numSearches; i++) {
      if (DblLnkLst_IsLinked(&session->searchArray[i].links)) {
         continue;
      }
      HgfsRemoveSearchInternal(&session->searchArray[i], session);
   }
   free(session->searchArray);
   session->searchArray = NULL;

   MXUser_ReleaseExclLock(session->searchArrayLock);

   if (gHgfsThreadpoolActive) {
      HgfsThreadpool_Deactivate();
   }

   /* Teardown the locks for the sessions and destroy itself. */
   MXUser_DestroyExclLock(session->nodeArrayLock);
   MXUser_DestroyExclLock(session->searchArrayLock);
   MXUser_DestroyExclLock(session->fileIOLock);

   /* Teardown the async request info.*/
   HgfsServerAsyncInfoExit(&session->asyncRequestsInfo);

   free(session);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServer_GetHandleCounter --
 *
 *    Return file handle counter. This is used by the checkpointing code to
 *    checkpoint this value so we avoid the risk of handle collision.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

uint32
HgfsServer_GetHandleCounter(void)
{
   return HgfsServerGetHandleCounter();
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServer_SetHandleCounter --
 *
 *    Set the file handle counter. This is used by the checkpointing code to
 *    restore this value so we avoid the risk of handle collision.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServer_SetHandleCounter(uint32 newHandleCounter)
{
   HgfsServerInitHandleCounter(newHandleCounter);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerSessionSendComplete --
 *
 *    This is called by the Transport when it is done sending the packet.
 *    Free the buffer. If we allocate buffers per session we have the session
 *    that the buffer belongs too.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Frees the packet buffer.
 *
 *---------------------------------------------------------------------------
 */

void
HgfsServerSessionSendComplete(HgfsPacket *packet,   // IN/OUT: Hgfs packet
                              void *clientData)     // IN: session info
{
   HgfsTransportSessionInfo *transportSession = clientData;

   if (0 != (packet->state & HGFS_STATE_CLIENT_REQUEST)) {
      HSPU_PutMetaPacket(packet, transportSession->channelCbTable);
      HSPU_PutReplyPacket(packet, transportSession->channelCbTable);
      HSPU_PutDataPacketBuf(packet, transportSession->channelCbTable);
   } else {
      if (packet->metaPacketIsAllocated) {
         free(packet->metaPacket);
      }
      free(packet);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerSessionQuiesce --
 *
 *    The function is called when VM is about to take a snapshot and
 *    when creation of the snapshot completed. When the freeze is TRUE the
 *    function quiesces all asynchronous and background activity to prevent
 *    interactions with snapshots and waits until there is no such activity.
 *    When freeze is FALSE the function restarts background activity that
 *    has been suspended previously.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsServerSessionQuiesce(void *clientData,         // IN: transport and session
                         HgfsQuiesceOp quiesceOp)  // IN: operation
{
   HgfsTransportSessionInfo *transportSession = clientData;
   DblLnkLst_Links *curr;

   LOG(4, "%s: Beginning\n", __FUNCTION__);

   MXUser_AcquireExclLock(transportSession->sessionArrayLock);

   DblLnkLst_ForEach(curr, &transportSession->sessionArray) {
      HgfsSessionInfo *session = DblLnkLst_Container(curr, HgfsSessionInfo, links);

      switch(quiesceOp) {
      case HGFS_QUIESCE_CHANNEL_FREEZE:
         /*
          * The channel is still alive now, it's the right time to
          * finish all asynchronous IO.
          */
         if (gHgfsThreadpoolActive) {
            HgfsThreadpool_Deactivate();
         }
         break;
      case HGFS_QUIESCE_FREEZE:
         /* Suspend background activity. */
         LOG(8, "%s: Halt file system activity for session %p\n",
             __FUNCTION__, session);

         if (gHgfsDirNotifyActive) {
            HgfsNotify_Deactivate(HGFS_NOTIFY_REASON_SERVER_SYNC, session);
         }

         if (gHgfsThreadpoolActive) {
            HgfsThreadpool_Deactivate();
         }

         HgfsServerAsyncWaitForAllRequestsDone(&session->asyncRequestsInfo);
         break;

      case HGFS_QUIESCE_THAW:
         /* Resume background activity. */
         LOG(8, "%s: Resume file system activity for session %p\n",
             __FUNCTION__, session);

         if (gHgfsDirNotifyActive) {
            HgfsNotify_Activate(HGFS_NOTIFY_REASON_SERVER_SYNC, session);
         }

         if (gHgfsThreadpoolActive) {
            if (!HgfsThreadpool_Activate()) {
               HgfsThreadpool_Exit();
               gHgfsThreadpoolActive = FALSE;
               Log("%s: failed to resume the threadpool\n", __FUNCTION__);
            }
         }
         break;

      default:
         NOT_REACHED();
      }

   }
   MXUser_ReleaseExclLock(transportSession->sessionArrayLock);
   LOG(4, "%s: Ending\n", __FUNCTION__);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsPacketSend --
 *
 *    Send the packet.
 *
 * Results:
 *    TRUE on success, FALSE on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
HgfsPacketSend(HgfsPacket *packet,                         // IN/OUT: Hgfs Packet
               HgfsTransportSessionInfo *transportSession, // IN: transport
               HgfsSessionInfo *session,                   // IN: session info
               HgfsSendFlags flags)                        // IN: send flags
{
   Bool result = FALSE;
   Bool asyncClientRequest = (0 != (packet->state & HGFS_STATE_CLIENT_REQUEST) &&
                              0 != (packet->state & HGFS_STATE_ASYNC_REQUEST));

   ASSERT(packet);
   ASSERT(transportSession);

   if (transportSession->state == HGFS_SESSION_STATE_OPEN) {
      ASSERT(transportSession->type == HGFS_SESSION_TYPE_REGULAR);
      result = transportSession->channelCbTable->send(transportSession->transportData,
                                                      packet,
                                                      flags);
   }

   if (asyncClientRequest) {
      ASSERT(session);
      HgfsServerAsyncInfoDecCount(&session->asyncRequestsInfo);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsInvalidateSessionObjects --
 *
 *      Iterates over all nodes and searches, invalidating and removing those
 *      that are no longer within a share.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsInvalidateSessionObjects(DblLnkLst_Links *shares,  // IN: List of new shares
                             HgfsSessionInfo *session) // IN: Session info
{
   unsigned int i;

   ASSERT(shares);
   ASSERT(session);
   ASSERT(session->nodeArray);
   ASSERT(session->searchArray);
   LOG(4, "%s: Beginning\n", __FUNCTION__);

   MXUser_AcquireExclLock(session->nodeArrayLock);

   /*
    * Iterate over each node, skipping those that are unused. For each node,
    * if its filename is no longer within a share, remove it.
    */
   for (i = 0; i < session->numNodes; i++) {
      HgfsHandle handle;
      DblLnkLst_Links *l;

      if (session->nodeArray[i].state == FILENODE_STATE_UNUSED) {
         continue;
      }

      handle = HgfsFileNode2Handle(&session->nodeArray[i]);
      LOG(4, "%s: Examining node with fd %d (%s)\n", __FUNCTION__,
          handle, session->nodeArray[i].utf8Name);

      /* For each share, is the node within the share? */
      for (l = shares->next; l != shares; l = l->next) {
         HgfsSharedFolder *share;

         share = DblLnkLst_Container(l, HgfsSharedFolder, links);
         ASSERT(share);
         if (strcmp(session->nodeArray[i].shareInfo.rootDir, share->path) == 0) {
            LOG(4, "%s: Node is still valid\n", __FUNCTION__);
            break;
         }
      }

      /* If the node wasn't found in any share, remove it. */
      if (l == shares) {
         LOG(4, "%s: Node is invalid, removing\n", __FUNCTION__);
         if (!HgfsRemoveFromCacheInternal(handle, session)) {
            LOG(4, "%s: Could not remove node with "
                "fh %d from the cache.\n", __FUNCTION__, handle);
         } else {
            HgfsFreeFileNodeInternal(handle, session);
         }
      }
   }

   MXUser_ReleaseExclLock(session->nodeArrayLock);

   MXUser_AcquireExclLock(session->searchArrayLock);

   /*
    * Iterate over each search, skipping those that are on the free list. For
    * each search, if its base name is no longer within a share, remove it.
    */
   for (i = 0; i < session->numSearches; i++) {
      DblLnkLst_Links *l;

      if (DblLnkLst_IsLinked(&session->searchArray[i].links)) {
         continue;
      }

      if (HgfsSearchIsBaseNameSpace(&session->searchArray[i])) {
         /* Skip search of the base name space. Maybe stale but it is okay. */
         continue;
      }

      LOG(4, "%s: Examining search (%s)\n", __FUNCTION__,
          session->searchArray[i].utf8Dir);

      /* For each share, is the search within the share? */
      for (l = shares->next; l != shares; l = l->next) {
         HgfsSharedFolder *share;

         share = DblLnkLst_Container(l, HgfsSharedFolder, links);
         ASSERT(share);
         if (strcmp(session->searchArray[i].shareInfo.rootDir, share->path) == 0) {
            LOG(4, "%s: Search is still valid\n", __FUNCTION__);
            break;
         }
      }

      /* If the node wasn't found in any share, remove it. */
      if (l == shares) {
         LOG(4, "%s: Search is invalid, removing\n", __FUNCTION__);
         HgfsRemoveSearchInternal(&session->searchArray[i], session);
      }
   }

   MXUser_ReleaseExclLock(session->searchArrayLock);

   HgfsCache* caches[] = {session->symlinkCache, session->fileAttrCache };
   for (i = 0; i < sizeof(caches) / sizeof(caches[0]); i++) {
      const void **keys = NULL;
      size_t nkeys;
      int keyIdx;

      if (!caches[i]) {
         continue;
      }
      MXUser_AcquireExclLock(caches[i]->lock);
      HashTable_KeyArray(caches[i]->hashTable, &keys, &nkeys);
      MXUser_ReleaseExclLock(caches[i]->lock);
      for (keyIdx = 0; keyIdx < nkeys; keyIdx++) {
         DblLnkLst_Links *l;
         const char *name = (const char *)keys[keyIdx];
         for (l = shares->next; l != shares; l = l->next) {
            HgfsSharedFolder *share;

            share = DblLnkLst_Container(l, HgfsSharedFolder, links);
            if (strncmp(name, share->path, strlen(name)) == 0) {
               break;
            }
         }

         if (l == shares) {
            LOG(4, "%s: Remove %s from cache\n", __FUNCTION__, name);
            HgfsCache_Invalidate(caches[i], name);

         }
      }
      free((void *)keys);
   }

   LOG(4, "%s: Ending\n", __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSessionInvalidateObjects --
 *
 *      Iterates over all sessions and invalidate session objects for the shares
 *      removed.
 *
 *      Caller guarantees that the sessions won't go away under us, so no locks
 *      needed.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServerSessionInvalidateObjects(void *clientData,         // IN:
                                   DblLnkLst_Links *shares)  // IN: List of new shares
{
   HgfsTransportSessionInfo *transportSession = clientData;
   DblLnkLst_Links *curr;

   LOG(4, "%s: Beginning\n", __FUNCTION__);

   ASSERT(transportSession);
   MXUser_AcquireExclLock(transportSession->sessionArrayLock);

   DblLnkLst_ForEach(curr, &transportSession->sessionArray) {
      HgfsSessionInfo *session = DblLnkLst_Container(curr, HgfsSessionInfo, links);
      HgfsServerSessionGet(session);
      HgfsInvalidateSessionObjects(shares, session);
      HgfsServerSessionPut(session);
   }

   MXUser_ReleaseExclLock(transportSession->sessionArrayLock);

   /* Now invalidate any stale shares and add any new ones. */
   HgfsServerSharesReset(shares);
   LOG(4, "%s: Ending\n", __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSessionInvalidateInactiveSessions --
 *
 *      Iterates over all sessions and invalidate all inactive session objects.
 *
 *      Following clock algorithm is used to determine whether the session object
 *      is inactive or not.
 *
 *      When this function is called, the HGFS server manager will iterate
 *      over all the sessions belonging to this manager. Each session is marked
 *      as inactive. Whenever a message is processed for a session, that
 *      session is marked as active. When this function is called the next time,
 *      any sessions that are still inactive will be invalidated.
 *
 *      Caller guarantees that the sessions won't go away under us, so no locks
 *      needed.
 *
 * Results:
 *      Number of active sessions remaining inside the HGFS server.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

uint32
HgfsServerSessionInvalidateInactiveSessions(void *clientData)         // IN:
{
   HgfsTransportSessionInfo *transportSession = clientData;
   uint32 numActiveSessionsLeft = 0;
   DblLnkLst_Links shares, *curr, *next;

   ASSERT(transportSession);
   MXUser_AcquireExclLock(transportSession->sessionArrayLock);

   DblLnkLst_Init(&shares);

   DblLnkLst_ForEachSafe(curr, next,  &transportSession->sessionArray) {
      HgfsSessionInfo *session = DblLnkLst_Container(curr, HgfsSessionInfo, links);
      HgfsServerSessionGet(session);

      session->numInvalidationAttempts++;
      numActiveSessionsLeft++;

      /*
       * Check if the session is inactive. If the session is inactive, then
       * invalidate the session objects.
       */
      if (session->isInactive) {

         if (session->numInvalidationAttempts == MAX_SESSION_INVALIDATION_ATTEMPTS) {
            LOG(4, "%s: closing inactive session %"FMT64"x\n", __FUNCTION__,
                session->sessionId);
            session->state = HGFS_SESSION_STATE_CLOSED;
            HgfsServerTransportRemoveSessionFromList(transportSession,
                                                     session);
            /*
             * We need to reduce the refcount by 1 since we want to
             * destroy the session.
             */
            numActiveSessionsLeft--;
            HgfsServerSessionPut(session);
         } else {
            HgfsInvalidateSessionObjects(&shares, session);
         }
      } else {
         session->isInactive = TRUE;
         session->numInvalidationAttempts = 0;
      }

      HgfsServerSessionPut(session);
   }

   MXUser_ReleaseExclLock(transportSession->sessionArrayLock);

   return numActiveSessionsLeft;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerStatFs --
 *
 *      Calls on the wiper library to return the number of free bytes and
 *      total bytes on the filesystem underlying the given pathname.
 *
 * Results:
 *      TRUE if successful: freeBytes and totalBytes have been written to.
 *      FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerStatFs(const char *pathName, // IN: Path we're interested in
                 size_t pathLength,    // IN: Length of path
                 uint64 *freeBytes,    // OUT: Free bytes on volume
                 uint64 *totalBytes)   // OUT: Total bytes on volume
{
   WiperPartition p;
   unsigned char *wiperError;

   ASSERT(pathName);
   ASSERT(freeBytes);
   ASSERT(totalBytes);

   Wiper_Init(NULL);

   /*
    * Confidence checks. If length is good, assume well-formed drive path
    * (i.e. "C:\..." or "\\abc..."). Note that we throw out shares that
    * exactly equal p.mountPoint's size because we won't have room for a null
    * delimiter on copy. Allow 0 length drives so that hidden feature "" can
    * work.
    */
   if (pathLength >= sizeof p.mountPoint) {
      LOG(4, "%s: could not get the volume name\n", __FUNCTION__);

      return FALSE;
   }

   /* Now call the wiper lib to get space information. */
   Str_Strcpy(p.mountPoint, pathName, sizeof p.mountPoint);
   wiperError = WiperSinglePartition_GetSpace(&p, NULL, freeBytes, totalBytes);
   if (strlen(wiperError) > 0) {
      LOG(4, "%s: error using wiper lib: %s\n", __FUNCTION__, wiperError);

      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsOplockFileChangeCb --
 *
 *    The callback is invoked by the oplock when detecting file/directory is
 *    changed.
 *    This simply calls back to the cache's invalidate function.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsOplockFileChangeCb(HgfsSessionInfo *session, // IN: Session info
                       void *data)               // IN: Path
{
   // There is no need to explicitly invoke HgfsOplockUnmonitorFileChange.
   if (NULL != session->symlinkCache) {
      HgfsCache_Invalidate(session->symlinkCache, data);
   }
   if (NULL != session->fileAttrCache) {
      HgfsCache_Invalidate(session->fileAttrCache, data);
   }
   free(data);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCacheRemoveLRUCb --
 *
 *    The callback is invoked by the cache when removing the LRU entry.
 *    This simply calls back to the oplock to unmonitor the file/directory
 *    change event.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsCacheRemoveLRUCb(void *data) // IN
{
   HgfsOplockUnmonitorFileChange(((HOM_HANDLE *)data)[0]);
}

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetLocalNameInfo --
 *
 *    Construct local name based on the crossplatform CPName for the file and the
 *    share information.
 *
 *    The name returned is allocated and must be freed by the caller.
 *    The name length is optionally returned.
 *
 * Results:
 *    A status code indicating either success (correspondent share exists) or
 *    a failure status.
 *
 * Side effects:
 *    Memory allocation in the success case
 *
 *-----------------------------------------------------------------------------
 */

static HgfsNameStatus
HgfsServerGetLocalNameInfo(const char *cpName,      // IN:  Cross-platform filename to check
                           size_t cpNameSize,       // IN:  Size of name cpName
                           uint32 caseFlags,        // IN:  Case-sensitivity flags
                           HgfsSessionInfo *session,// IN:  Session info
                           HgfsShareInfo *shareInfo,// OUT: properties of the shared folder
                           char **bufOut,           // OUT: File name in local fs
                           size_t *outLen)          // OUT: Length of name out optional
{
   HgfsNameStatus nameStatus;
   const char *inEnd;
   const char *next;
   char *myBufOut;
   char *convertedMyBufOut;
   char *out;
   size_t outSize;
   size_t myBufOutLen;
   size_t convertedMyBufOutLen;
   int len;
   uint32 pathNameLen;
   char tempBuf[HGFS_PATH_MAX];
   size_t tempSize;
   char *tempPtr;
   uint32 startIndex = 0;
   HgfsShareOptions shareOptions;
   HgfsSymlinkCacheEntry *entry;

   ASSERT(cpName);
   ASSERT(bufOut);

   inEnd = cpName + cpNameSize;

   if (!Unicode_IsBufferValid(cpName, cpNameSize, STRING_ENCODING_UTF8)) {
      LOG(4, "%s: invalid UTF8 string @ %p\n", __FUNCTION__, cpName);
      return HGFS_NAME_STATUS_FAILURE;
   }

   /*
    * Get first component.
    */
   len = CPName_GetComponent(cpName, inEnd, &next);
   if (len < 0) {
      LOG(4, "%s: get first component failed\n", __FUNCTION__);

      return HGFS_NAME_STATUS_FAILURE;
   }

   /* See if we are dealing with the base of the namespace */
   if (!len) {
      return HGFS_NAME_STATUS_INCOMPLETE_BASE;
   }

   /* Check permission on the share and get the share path */
   nameStatus = HgfsServerPolicy_ProcessCPName(cpName,
                                               len,
                                               &shareInfo->readPermissions,
                                               &shareInfo->writePermissions,
                                               &shareInfo->handle, // XXX: to be deleted.
                                               &shareInfo->rootDir);
   if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
      LOG(4, "%s: No such share (%s)\n", __FUNCTION__, cpName);
      return nameStatus;
   }
   shareInfo->rootDirLen = strlen(shareInfo->rootDir);
   /*
    * XXX: The handle is now NOT propagated back and held in the policy but only in the
    * table of share properties.
    *
    * Get shareInfo handle returns a valid handle only if we have change
    * notification active.
    * Note: cpName begins with the share name.
    */
   shareInfo->handle = HgfsServerGetShareHandle(cpName);

   /* Get the config options. */
   nameStatus = HgfsServerPolicy_GetShareOptions(cpName, len, &shareOptions);
   if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
      LOG(4, "%s: no matching share: %s.\n", __FUNCTION__, cpName);
      return nameStatus;
   }

   /* Point to the next component, if any */
   cpNameSize -= next - cpName;
   cpName = next;

   /*
    * Allocate space for the string. We trim the unused space later.
    */

   outSize = HGFS_PATH_MAX;
   myBufOut = (char *) malloc(outSize * sizeof *myBufOut);
   if (!myBufOut) {
      LOG(4, "%s: out of memory allocating string\n", __FUNCTION__);

      return HGFS_NAME_STATUS_OUT_OF_MEMORY;
   }

   out = myBufOut;

   /*
    * See if we are dealing with a "root" share or regular share
    */
   if (shareInfo->rootDirLen == 0) {
      size_t prefixLen;

      /* Are root shares allowed? If not, we exit with an error. */
      if (0 == (gHgfsCfgSettings.flags & HGFS_CONFIG_SHARE_ALL_HOST_DRIVES_ENABLED)) {
         LOG(4, "%s: Root share being used\n", __FUNCTION__);
         nameStatus = HGFS_NAME_STATUS_ACCESS_DENIED;
         goto error;
      }

      /*
       * This is a "root" share. Interpret the input appropriately as
       * either a drive letter or UNC name and append it to the output
       * buffer (for Win32) or simply get the prefix for root (for
       * linux).
       */
      tempSize = sizeof tempBuf;
      tempPtr = tempBuf;
      nameStatus = CPName_ConvertFromRoot(&cpName,
                                          &cpNameSize, &tempSize, &tempPtr);
      if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
         LOG(4, "%s: ConvertFromRoot not complete\n", __FUNCTION__);
         goto error;
      }

      prefixLen = tempPtr - tempBuf;

      /* Copy the UTF8 prefix to the output buffer. */
      if (prefixLen >= HGFS_PATH_MAX) {
         Log("%s: error: prefix too long\n", __FUNCTION__);
         nameStatus = HGFS_NAME_STATUS_TOO_LONG;
         goto error;
      }

      memcpy(out, tempBuf, prefixLen);
      out += prefixLen;
      *out = 0;
      outSize -= prefixLen;
   } else {
      /*
       * This is a regular share. Append the path to the out buffer.
       */
      if (outSize < shareInfo->rootDirLen + 1) {
         LOG(4, "%s: share path too big\n", __FUNCTION__);
         nameStatus = HGFS_NAME_STATUS_TOO_LONG;
         goto error;
      }

      memcpy(out, shareInfo->rootDir, shareInfo->rootDirLen + 1);
      out += shareInfo->rootDirLen;
      outSize -= shareInfo->rootDirLen;
   }

   /* Convert the rest of the input name (if any) to a local name */
   tempSize = sizeof tempBuf;
   tempPtr = tempBuf;


   if (CPName_ConvertFrom(&cpName, &cpNameSize, &tempSize,
                          &tempPtr) < 0) {
      LOG(4, "%s: CP name conversion failed\n", __FUNCTION__);
      nameStatus = HGFS_NAME_STATUS_FAILURE;
      goto error;
   }

   /*
    * For volume root directory shares the prefix will have a trailing
    * separator and since our remaining paths start with a separator, we
    * will skip over the second separator for this case. Bug 166755.
    */

   if ((out != myBufOut) && (*(out - 1) == DIRSEPC) && (tempBuf[0] == DIRSEPC)) {
      startIndex++;
   }
   pathNameLen = tempPtr - &tempBuf[startIndex];

   /* Copy UTF8 to the output buffer. */
   if (pathNameLen >= outSize) {
      LOG(4, "%s: pathname too long\n", __FUNCTION__);
      nameStatus = HGFS_NAME_STATUS_TOO_LONG;
      goto error;
   }

   memcpy(out, &tempBuf[startIndex], pathNameLen);
   outSize -= pathNameLen;
   out += pathNameLen;
   *out = 0;
   myBufOutLen = out - myBufOut;

#if defined(__APPLE__)
   {
      size_t nameLen;
      /*
       * For Mac hosts the unicode format is decomposed (form D)
       * so there is a need to convert the incoming name from HGFS clients
       * which is assumed to be in the normalized form C (precomposed).
       */

      if (!CodeSet_Utf8FormCToUtf8FormD(myBufOut, myBufOutLen, &tempPtr,
                                        &nameLen)) {
         LOG(4, "%s: unicode conversion to form D failed.\n", __FUNCTION__);
         nameStatus = HGFS_NAME_STATUS_FAILURE;
         goto error;
      }

      free(myBufOut);
      LOG(4, "%s: name is \"%s\"\n", __FUNCTION__, tempPtr);

      /* Save returned pointers, update buffer length. */
      myBufOut = tempPtr;
      out = tempPtr + nameLen;
      myBufOutLen = nameLen;
   }
#endif /* defined(__APPLE__) */

   /*
    * Look up the file name using the proper case if the config option is not set
    * to use the host default and lookup is supported for this platform.
    */

   if (!HgfsServerPolicy_IsShareOptionSet(shareOptions,
                                          HGFS_SHARE_HOST_DEFAULT_CASE) &&
       HgfsPlatformDoFilenameLookup()) {
      nameStatus = HgfsPlatformFilenameLookup(shareInfo->rootDir, shareInfo->rootDirLen,
                                              myBufOut, myBufOutLen, caseFlags,
                                              &convertedMyBufOut,
                                              &convertedMyBufOutLen);

      /*
       * On successful lookup, use the found matching file name for further operations.
       */

      if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
         LOG(4, "%s: HgfsPlatformFilenameLookup failed.\n", __FUNCTION__);
         goto error;
      }

      free(myBufOut);
      myBufOut = convertedMyBufOut;
      myBufOutLen = convertedMyBufOutLen;
      ASSERT(myBufOut);
   }

   /* Check for symlinks if the followSymlinks option is not set. */
   if (!HgfsServerPolicy_IsShareOptionSet(shareOptions,
                                          HGFS_SHARE_FOLLOW_SYMLINKS)) {
      if (NULL != session->symlinkCache &&
          HgfsCache_Get(session->symlinkCache, myBufOut, (void **)&entry)) {
         nameStatus = entry->nameStatus;
      } else {
         /*
          * Verify that either the path is same as share path or the path until
          * the parent directory is within the share.
          *
          * XXX: Symlink check could become susceptible to
          * TOCTOU (time-of-check, time-of-use) attack when we move to
          * asynchrounous HGFS operations.
          * We should use the resolved file path for further file system
          * operations, instead of using the one passed from the client.
          */
         nameStatus = HgfsPlatformPathHasSymlink(myBufOut, myBufOutLen,
                                                 shareInfo->rootDir,
                                                 shareInfo->rootDirLen);
         if (NULL != session->symlinkCache) {
            HOM_HANDLE handle =
               HgfsOplockMonitorFileChange(myBufOut, session,
                                           HgfsOplockFileChangeCb,
                                           Util_SafeStrdup(myBufOut));
            if (handle != HGFS_OPLOCK_INVALID_MONITOR_HANDLE) {
               entry = Util_SafeCalloc(1, sizeof *entry);
               entry->handle = handle;
               entry->nameStatus = nameStatus;
               HgfsCache_Put(session->symlinkCache, myBufOut,
                             entry);
            }
         }
      }

      if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
         LOG(4, "%s: parent path failed to be resolved: %d\n",
             __FUNCTION__, nameStatus);
         goto error;
      }
   }

   {
      char *p;

      /* Trim unused memory */

      /* Enough space for resulting string + NUL termination */
      p = realloc(myBufOut, (myBufOutLen + 1) * sizeof *p);
      if (!p) {
         LOG(4, "%s: failed to trim memory\n", __FUNCTION__);
      } else {
         myBufOut = p;
      }

      if (outLen) {
         *outLen = myBufOutLen;
      }
   }

   LOG(4, "%s: name is \"%s\"\n", __FUNCTION__, myBufOut);

   *bufOut = myBufOut;

   /*
    * Contrary to Coverity analysis, storage pointed to by the variable
    * "entry" is not leaked; HgfsCache_Put stores a pointer to it in the
    * symlink cache.  However, no Coverity annotation for leaked_storage
    * is added here because such an annotation cannot be made specific to
    * entry; as a result, if any actual memory leaks were to be introduced
    * by a future change, the leaked_storage annotation would cause such
    * new leaks to be flagged as false positives.
    *
    * XXX - can something be done with Coverity function models so that
    * Coverity stops reporting this?
    */
   return HGFS_NAME_STATUS_COMPLETE;

error:
   free(myBufOut);

   return nameStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerIsSharedFolderOnly --
 *
 *    Test a name if it is a shared folder only or not
 *
 *    This function assumes that CPName_GetComponent() will always succeed
 *    with a size greater than 0, so it must ONLY be called after a call to
 *    HgfsServerGetLocalNameInfo() that returns HGFS_NAME_STATUS_COMPLETE.
 *
 * Results:
 *    True if it is a shared folder only, otherwise false
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerIsSharedFolderOnly(char const *cpName,// IN:  Cross-platform filename to check
                             size_t cpNameSize) // IN:  Size of name cpName
{
   char const *inEnd;
   char const *next;
   int len;

   ASSERT(cpName);

   inEnd = cpName + cpNameSize;
   len = CPName_GetComponent(cpName, inEnd, &next);

   ASSERT(len > 0);
   (void) len; /* Shuts up gcc's -Werror=unused-but-set-variable. */

   return (next == inEnd);
}


#ifdef VMX86_LOG
/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerDirDumpDents --
 *
 *    Dump a set of directory entries (debugging code)
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServerDirDumpDents(HgfsHandle searchHandle,  // IN: Handle to dump dents from
                       HgfsSessionInfo *session) // IN: Session info
{
   HgfsSearch *search;

   MXUser_AcquireExclLock(session->searchArrayLock);

   search = HgfsSearchHandle2Search(searchHandle, session);
   if (search != NULL) {
      HgfsPlatformDirDumpDents(search);
   }

   MXUser_ReleaseExclLock(session->searchArrayLock);
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSearchRealDir --
 *
 *    Handle a search on a real directory. Takes a pointer to an enumerator
 *    for the directory's contents and returns a handle to a search that is
 *    correctly set up with the real directory's entries.
 *
 *    The casual reader will notice that the "type" of this search is obviously
 *    always DIRECTORY_SEARCH_TYPE_DIR, but the caller is nonetheless required
 *    to pass it in, for completeness' sake with respect to
 *    HgfsServerSearchVirtualDir.
 *
 * Results:
 *    Zero on success, returns a handle to the created search.
 *    Non-zero on failure.
 *
 * Side effects:
 *    Memory allocation on success
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsServerSearchRealDir(char const *baseDir,      // IN: Directory to search
                        size_t baseDirLen,        // IN: Length of directory
                        char const *shareName,    // IN: Share name containing the directory
                        char const *rootDir,      // IN: Shared folder root directory
                        HgfsSessionInfo *session, // IN: Session info
                        HgfsHandle *handle)       // OUT: Search handle
{
   HgfsSearch *search = NULL;
   HgfsInternalStatus status = 0;
   HgfsNameStatus nameStatus;
   Bool followSymlinks;
   HgfsShareOptions configOptions;

   ASSERT(baseDir);
   ASSERT(handle);
   ASSERT(shareName);

   MXUser_AcquireExclLock(session->searchArrayLock);

   search = HgfsAddNewSearch(baseDir, DIRECTORY_SEARCH_TYPE_DIR, shareName,
                             rootDir, session);
   if (!search) {
      LOG(4, "%s: failed to get new search\n", __FUNCTION__);
      status = HGFS_ERROR_INTERNAL;
      goto out;
   }

   /* Get the config options. */
   nameStatus = HgfsServerPolicy_GetShareOptions(shareName, strlen(shareName),
                                                 &configOptions);
   if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
      LOG(4, "%s: no matching share: %s.\n", __FUNCTION__, shareName);
      status = HGFS_ERROR_INTERNAL;
      HgfsRemoveSearchInternal(search, session);
      goto out;
   }

   followSymlinks = HgfsServerPolicy_IsShareOptionSet(configOptions,
                                                      HGFS_SHARE_FOLLOW_SYMLINKS);

   status = HgfsPlatformScandir(baseDir, baseDirLen, followSymlinks,
                                &search->dents, &search->numDents);
   if (HGFS_ERROR_SUCCESS != status) {
      LOG(4, "%s: couldn't scandir\n", __FUNCTION__);
      HgfsRemoveSearchInternal(search, session);
      goto out;
   }

   *handle = HgfsSearch2SearchHandle(search);

  out:
   MXUser_ReleaseExclLock(session->searchArrayLock);

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSearchVirtualDir --
 *
 *    Handle a search on a virtual directory (i.e. one that does not
 *    really exist on the server). Takes a pointer to an enumerator
 *    for the directory's contents and returns a handle to a search that is
 *    correctly set up with the virtual directory's entries.
 *
 * Results:
 *    Zero on success, returns a handle to the created search.
 *    Non-zero on failure.
 *
 * Side effects:
 *    Memory allocation on success
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsServerSearchVirtualDir(HgfsServerResEnumGetFunc getName,      // IN: Name enumerator
                           HgfsServerResEnumInitFunc initName,    // IN: Init function
                           HgfsServerResEnumExitFunc cleanupName, // IN: Cleanup function
                           DirectorySearchType type,              // IN: Kind of search
                           HgfsSessionInfo *session,              // IN: Session info
                           HgfsHandle *handle)                    // OUT: Search handle
{
   HgfsInternalStatus status = 0;
   HgfsSearch *search = NULL;

   ASSERT(getName);
   ASSERT(initName);
   ASSERT(cleanupName);
   ASSERT(handle);

   MXUser_AcquireExclLock(session->searchArrayLock);

   search = HgfsAddNewSearch("", type, "", "", session);
   if (!search) {
      LOG(4, "%s: failed to get new search\n", __FUNCTION__);
      status = HGFS_ERROR_INTERNAL;
      goto out;
   }

   status = HgfsPlatformScanvdir(getName,
                                 initName,
                                 cleanupName,
                                 type,
                                 &search->dents,
                                 &search->numDents);
   if (HGFS_ERROR_SUCCESS != status) {
      LOG(4, "%s: couldn't get dents\n", __FUNCTION__);
      HgfsRemoveSearchInternal(search, session);
      goto out;
   }

   *handle = HgfsSearch2SearchHandle(search);

  out:
   MXUser_ReleaseExclLock(session->searchArrayLock);

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerRestartSearchVirtualDir --
 *
 *    Restart a search on a virtual directory (i.e. one that does not
 *    really exist on the server). Takes a pointer to an enumerator
 *    for the directory's contents and returns a handle to a search that is
 *    correctly set up with the virtual directory's entries.
 *
 * Results:
 *    Zero on success, returns a handle to the created search.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsServerRestartSearchVirtualDir(HgfsServerResEnumGetFunc getName,      // IN: Name enumerator
                                  HgfsServerResEnumInitFunc initName,    // IN: Init function
                                  HgfsServerResEnumExitFunc cleanupName, // IN: Cleanup function
                                  HgfsSessionInfo *session,              // IN: Session info
                                  HgfsHandle searchHandle)               // IN: search to restart
{
   HgfsInternalStatus status = 0;
   HgfsSearch *vdirSearch;

   ASSERT(getName);
   ASSERT(initName);
   ASSERT(cleanupName);
   ASSERT(searchHandle);

   MXUser_AcquireExclLock(session->searchArrayLock);

   vdirSearch = HgfsSearchHandle2Search(searchHandle, session);
   if (NULL == vdirSearch) {
      status = HGFS_ERROR_INVALID_HANDLE;
      goto exit;
   }

   /* Release the virtual directory's old set of entries. */
   HgfsFreeSearchDirents(vdirSearch);

   /* Restart by rescanning the virtual directory. */
   status = HgfsPlatformScanvdir(getName,
                                 initName,
                                 cleanupName,
                                 vdirSearch->type,
                                 &vdirSearch->dents,
                                 &vdirSearch->numDents);
   if (HGFS_ERROR_SUCCESS != status) {
      LOG(4, "%s: couldn't get root dents %u\n", __FUNCTION__, status);
      goto exit;
   }

   /* Clear the flag to indicate that the client has read the entries. */
   vdirSearch->flags &= ~HGFS_SEARCH_FLAG_READ_ALL_ENTRIES;

exit:
   MXUser_ReleaseExclLock(session->searchArrayLock);

   LOG(4, "%s: refreshing dents return %d\n", __FUNCTION__, status);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsRemoveFromCache --
 *
 *    Grab a node cache lock and call HgfsRemoveFromCacheInternal.
 *
 *    If the node was not already in the cache then nothing is done.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsRemoveFromCache(HgfsHandle handle,        // IN: Hgfs handle to the node
                    HgfsSessionInfo *session) // IN: Session info
{
   Bool removed = FALSE;

   MXUser_AcquireExclLock(session->nodeArrayLock);
   removed = HgfsRemoveFromCacheInternal(handle, session);
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return removed;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsIsCached --
 *
 *    Grab a lock and call HgfsIsCachedInternal.
 *
 * Results:
 *    TRUE if the node is found in the cache.
 *    FALSE if the node is not in the cache.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsIsCached(HgfsHandle handle,         // IN: Structure representing file node
             HgfsSessionInfo *session)  // IN: Session info
{
   Bool cached = FALSE;

   MXUser_AcquireExclLock(session->nodeArrayLock);
   cached = HgfsIsCachedInternal(handle, session);
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return cached;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsRemoveLruNode--
 *
 *    Removes the least recently used node in the cache. The first node is
 *    removed since most recently used nodes are moved to the end of the
 *    list.
 *
 *    XXX: Right now we do not remove nodes that have server locks on them
 *         This is not correct and should be fixed before the release.
 *         Instead we should cancel the server lock (by calling IoCancel)
 *         notify client of the lock break, and close the file.
 *
 *    Assumes that there is at least one node in the cache.
 *
 *    The session's nodeArrayLock should be acquired prior to calling this
 *    function.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsRemoveLruNode(HgfsSessionInfo *session)   // IN: session info
{
   HgfsFileNode *lruNode = NULL;
   HgfsHandle handle;
   Bool found = FALSE;
   uint32 numOpenNodes = session->numCachedOpenNodes;

   ASSERT(session);
   ASSERT(session->numCachedOpenNodes > 0);

   /*
    * Remove the first item from the list that does not have a server lock,
    * file context or is open in sequential mode.
    */
   while (!found && (numOpenNodes-- > 0)) {
      lruNode = DblLnkLst_Container(session->nodeCachedList.next,
                                    HgfsFileNode, links);

      ASSERT(lruNode->state == FILENODE_STATE_IN_USE_CACHED);
      if (lruNode->serverLock != HGFS_LOCK_NONE || lruNode->fileCtx != NULL
          || (lruNode->flags & HGFS_FILE_NODE_SEQUENTIAL_FL) != 0) {
         /*
	  * Move this node with the server lock to the beginning of the list.
	  * Also, prevent files opened in HGFS_FILE_NODE_SEQUENTIAL_FL mode
	  * from being closed. -- On some platforms, this mode does not
	  * allow files to be closed/re-opened (eg: When restoring a file
	  * into a Windows guest you cannot use BackupWrite, then close and
	  * re-open the file and continue to use BackupWrite.
	  */
         DblLnkLst_Unlink1(&lruNode->links);
         DblLnkLst_LinkLast(&session->nodeCachedList, &lruNode->links);
      } else {
         found = TRUE;
      }
   }
   if (found) {
      handle = HgfsFileNode2Handle(lruNode);
      if (!HgfsRemoveFromCacheInternal(handle, session)) {
         LOG(4, "%s: Could not remove the node from cache.\n", __FUNCTION__);
         return FALSE;
      }
   } else {
      LOG(4, "%s: Could not find a node to remove from cache.\n", __FUNCTION__);
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAddToCache --
 *
 *    Grabs the cache lock and calls HgfsAddToCacheInternal.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsAddToCache(HgfsHandle handle,        // IN: HGFS file handle
               HgfsSessionInfo *session) // IN: Session info
{
   Bool added = FALSE;

   MXUser_AcquireExclLock(session->nodeArrayLock);
   added = HgfsAddToCacheInternal(handle, session);
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return added;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCreateAndCacheFileNode --
 *
 *    Get a node from the free node list and cache it.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsCreateAndCacheFileNode(HgfsFileOpenInfo *openInfo, // IN: Open info struct
                           HgfsLocalId const *localId, // IN: Local unique file ID
                           fileDesc fileDesc,          // IN: Handle to the fileopenInfo,
                           Bool append,                // IN: flag to append
                           HgfsSessionInfo *session)   // IN: session info
{
   HgfsHandle handle;
   HgfsFileNode *node = NULL;
   char const *inEnd;
   char const *next;
   int len;
   Bool sharedFolderOpen = FALSE;

   ASSERT(openInfo);
   ASSERT(localId);
   ASSERT(session);

   inEnd = openInfo->cpName + openInfo->cpNameSize;

   /*
    * Get first component.
    */

   len = CPName_GetComponent(openInfo->cpName, inEnd, &next);
   if (len < 0) {
      LOG(4, "%s: get first component failed\n", __FUNCTION__);
      HgfsPlatformCloseFile(fileDesc, NULL);
      return FALSE;
   }

   /* See if we are dealing with the base of the namespace */
   if (!len) {
      HgfsPlatformCloseFile(fileDesc, NULL);
      return FALSE;
   }

   if (!next) {
      sharedFolderOpen = TRUE;
   }

   MXUser_AcquireExclLock(session->nodeArrayLock);

   node = HgfsAddNewFileNode(openInfo, localId, fileDesc, append, len,
                             openInfo->cpName, sharedFolderOpen, session);

   if (node == NULL) {
      LOG(4, "%s: Failed to add new node.\n", __FUNCTION__);
      MXUser_ReleaseExclLock(session->nodeArrayLock);

      HgfsPlatformCloseFile(fileDesc, NULL);
      return FALSE;
   }
   handle = HgfsFileNode2Handle(node);

   if (!HgfsAddToCacheInternal(handle, session)) {
      HgfsFreeFileNodeInternal(handle, session);
      HgfsPlatformCloseFile(fileDesc, NULL);

      LOG(4, "%s: Failed to add node to the cache.\n", __FUNCTION__);
      MXUser_ReleaseExclLock(session->nodeArrayLock);

      return FALSE;
   }

   MXUser_ReleaseExclLock(session->nodeArrayLock);

   /* Only after everything is successful, save the handle in the open info. */
   openInfo->file = handle;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAllocInitReply --
 *
 *    Retrieves the hgfs protocol reply data buffer that follows the reply header.
 *
 * Results:
 *    Cannot fail, returns the protocol reply data buffer for the corresponding
 *    processed protocol request.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void *
HgfsAllocInitReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                   const void *packetHeader,     // IN: packet header
                   size_t replyDataSize,         // IN: replyDataSize size
                   HgfsSessionInfo *session)     // IN: Session Info
{
   const HgfsRequest *request = packetHeader;
   size_t replyPacketSize;
   size_t headerSize = 0; /* Replies prior to V3 do not have a header. */
   void *replyHeader;
   void *replyData;

   /*
    * XXX - this should be modified to use the common HgfsServerGetReplyHeaderSize
    * so that all requests and replies are handled consistently.
    */
   if (HGFS_OP_NEW_HEADER == request->op) {
      headerSize = sizeof(HgfsHeader);
   } else if (request->op < HGFS_OP_CREATE_SESSION_V4 &&
              request->op > HGFS_OP_RENAME_V2) {
      headerSize = sizeof(HgfsReply);
   }
   replyHeader = HSPU_GetReplyPacket(packet,
                                     session->transportSession->channelCbTable,
                                     headerSize + replyDataSize,
                                     &replyPacketSize);

   ASSERT(replyHeader && (replyPacketSize >= headerSize + replyDataSize));

   memset(replyHeader, 0, headerSize + replyDataSize);
   if (replyDataSize > 0) {
      replyData = (char *)replyHeader + headerSize;
   } else {
      replyData = NULL;
      ASSERT(FALSE);
   }

   return replyData;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerValidateRead --
 *
 *    Validate a Read request's arguments.
 *
 *    Note, the readOffset is ignored here but is checked in the platform specific
 *    read handler.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS on success.
 *    HGFS error code on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerValidateRead(HgfsInputParam *input,  // IN: Input params
                       HgfsHandle readHandle,  // IN: read Handle
                       uint32 readSize,        // IN: size to read
                       uint64 readOffset,      // IN: offset to read unused
                       fileDesc *readfd,       // OUT: read file descriptor
                       size_t *readReplySize,  // OUT: read reply size
                       size_t *readDataSize)   // OUT: read data size
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   size_t replyReadHeaderSize;
   size_t replyReadResultSize = 0;
   size_t replyReadResultDataSize = 0;
   size_t replyReadDataSize = 0;
   fileDesc readFileDesc = 0;
   Bool useMappedBuffer;

   useMappedBuffer = (input->transportSession->channelCbTable->getWriteVa != NULL);
   replyReadHeaderSize = HgfsServerGetReplyHeaderSize(input->sessionEnabled,
                                                      input->op);
   switch (input->op) {
   case HGFS_OP_READ_FAST_V4:
      /* Data is packed into a separate buffer from the read results. */
      replyReadResultSize = sizeof (HgfsReplyReadV3);
      replyReadResultDataSize = 0;
      replyReadDataSize = readSize;
      break;
   case HGFS_OP_READ_V3:
      /* Data is packed as a part of the read results. */
      replyReadResultSize = sizeof (HgfsReplyReadV3);
      replyReadResultDataSize = readSize;
      replyReadDataSize = 0;
      break;
   case HGFS_OP_READ:
      /* Data is packed as a part of the read results. */
      replyReadResultSize = sizeof (HgfsReplyRead);
      replyReadResultDataSize = readSize;
      replyReadDataSize = 0;
      break;
   default:
      status = HGFS_ERROR_PROTOCOL;
      LOG(4, "%s: Unsupported protocol version passed %d -> PROTOCOL_ERROR.\n",
          __FUNCTION__, input->op);
      NOT_IMPLEMENTED();
      goto exit;
   }

   if (!HSPU_ValidateDataPacketSize(input->packet, replyReadDataSize) ||
       !HSPU_ValidateReplyPacketSize(input->packet,
                                     replyReadHeaderSize,
                                     replyReadResultSize,
                                     replyReadResultDataSize,
                                     useMappedBuffer)) {
      status = HGFS_ERROR_INVALID_PARAMETER;
      LOG(4, "%s: Error: arg validation read size -> %d.\n",
          __FUNCTION__, status);
      goto exit;
   }

   /* Validate the file handle by retrieving it possibly from the cache. */
   status = HgfsPlatformGetFd(readHandle, input->session, FALSE, &readFileDesc);
   if (status != HGFS_ERROR_SUCCESS) {
      LOG(4, "%s: Error: arg validation handle -> %d.\n",
          __FUNCTION__, status);
      goto exit;
   }

exit:
   *readDataSize = replyReadDataSize;
   *readReplySize = replyReadResultSize + replyReadResultDataSize;
   *readfd = readFileDesc;
   LOG(4, "%s: arg validation check return (%"FMTSZ"u) %d.\n",
       __FUNCTION__, replyReadDataSize, status);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerRead --
 *
 *    Handle a Read request.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS on success.
 *    HGFS error code on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerRead(HgfsInputParam *input)  // IN: Input params
{
   HgfsInternalStatus status;
   HgfsHandle file;
   fileDesc readFd;
   uint64 offset;
   uint32 requiredSize;
   size_t replyPayloadSize = 0;
   size_t replyReadSize = 0;
   size_t replyReadDataSize = 0;
   void *replyRead;

   HGFS_ASSERT_INPUT(input);

   if (!HgfsUnpackReadRequest(input->payload, input->payloadSize, input->op, &file,
                              &offset, &requiredSize)) {
      LOG(4, "%s: Failed to unpack a valid packet -> PROTOCOL_ERROR.\n",
          __FUNCTION__);
      status = HGFS_ERROR_PROTOCOL;
      goto exit;
   }

   /*
    * Validate the read arguments with the data and reply buffers to ensure
    * there isn't a malformed request or we read more data than the buffer can
    * hold.
    */
   status = HgfsServerValidateRead(input,
                                   file,
                                   requiredSize,
                                   offset,
                                   &readFd,
                                   &replyReadSize,
                                   &replyReadDataSize);
   if (status != HGFS_ERROR_SUCCESS) {
      LOG(4, "%s: Error: validate args %u.\n", __FUNCTION__, status);
      goto exit;
   }

   replyRead = HgfsAllocInitReply(input->packet,
                                  input->request,
                                  replyReadSize,
                                  input->session);

   switch (input->op) {
   case HGFS_OP_READ_FAST_V4:
   case HGFS_OP_READ_V3: {
         HgfsReplyReadV3 *reply = replyRead;
         void *payload;
         Bool readUseDataBuffer = replyReadDataSize != 0;

         /*
          * The read data size holds the size of the data to read which will be read
          * into the separate data packet buffer. Zero indicates data is read into the
          * same buffer as the reply arguments.
          */
         if (readUseDataBuffer) {
            payload = HSPU_GetDataPacketBuf(input->packet, BUF_WRITEABLE,
                                            input->transportSession->channelCbTable);
         } else {
            payload = &reply->payload[0];
         }
         if (payload) {
            uint32 actualSize = 0;
            status = HgfsPlatformReadFile(readFd, input->session, offset,
                                          requiredSize, payload,
                                          &actualSize);
            if (HGFS_ERROR_SUCCESS == status) {
               reply->reserved = 0;
               reply->actualSize = actualSize;
               replyPayloadSize = sizeof *reply;

               if (readUseDataBuffer) {
                  HSPU_SetDataPacketSize(input->packet, reply->actualSize);
               } else {
                  replyPayloadSize += reply->actualSize;
               }
            }
         } else {
            status = HGFS_ERROR_PROTOCOL;
            LOG(4, "%s: V3/V4 Failed to get payload -> PROTOCOL_ERROR.\n",
                __FUNCTION__);
         }
         break;
      }
   case HGFS_OP_READ: {
         uint32 actualSize = 0;
         HgfsReplyRead *reply = replyRead;

         status = HgfsPlatformReadFile(readFd, input->session, offset, requiredSize,
                                       reply->payload, &actualSize);
         if (HGFS_ERROR_SUCCESS == status) {
            reply->actualSize = actualSize;
            replyPayloadSize = sizeof *reply + reply->actualSize;
         } else {
            LOG(4, "%s: V1 Failed to read-> %d.\n", __FUNCTION__, status);
         }
         break;
      }
   default:
      NOT_REACHED();
      break;
   }

exit:
   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerValidateWrite --
 *
 *    Validate a write request's arguments.
 *
 *    The HGFS Packet stored in the following possible formats as follows:
 *
 *    Protocol V4 versions:
 *    In the HgfsPacket metaPacket buffer
 *    [HgfsHeader][HgfsRequestWriteV3]
 *    In the HgfsPacket dataPacket buffer
 *    [Variable length data to write]
 *
 *    Protocol V3 versions:
 *    In the HgfsPacket metaPacket buffer
 *    [HgfsHeader][HgfsRequestWriteV3][Variable length data to write]
 *    [HgfsRequest][HgfsRequestWriteV3][Variable length data to write]
 *
 *    Protocol V2 versions:
 *    Does not exist
 *
 *    Protocol V1 versions:
 *    In the HgfsPacket metaPacket buffer
 *    [HgfsRequestWrite][Variable length data to write]
 *    (Note, the HgfsRequestWrite contains an HgfsRequest within it.)
 *
 *    Note, the writeOffset is ignored here but is checked in the platform specific
 *    write handler.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS on success.
 *    HGFS error code on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerValidateWrite(HgfsInputParam *input,     // IN: Input params
                        HgfsHandle writeHandle,    // IN: write Handle
                        uint64 writeOffset,        // IN: write offset of file
                        uint32 writeSize,          // IN: size to write
                        HgfsWriteFlags flags,      // IN: write flags
                        fileDesc *writefd,         // OUT: write file descriptor
                        Bool *writeSequential,     // OUT: write is sequential
                        Bool *writeAppend)         // OUT: write is append
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   size_t requestWriteHeaderSize;
   size_t requestWritePacketSize = 0;
   size_t requestWritePacketDataSize = 0;
   size_t requestWriteDataSize = 0;
   fileDesc writeFileDesc = 0;
   Bool sequentialHandle = FALSE;
   Bool appendHandle = FALSE;

   requestWriteHeaderSize = HgfsServerGetRequestHeaderSize(input->sessionEnabled,
                                                           input->op);
   switch (input->op) {
   case HGFS_OP_WRITE_FAST_V4:
      /*
       * For this the operation data is in the shared memory,
       * which depends on the mapping functions from the transport.
       */
      ASSERT(input->transportSession->channelCbTable->getReadVa != NULL);
      /*
       * The write data is packed in a separate buffer to the write request.
       * Note, for size we **include** the 1 byte placeholder payload that was not
       * counted in earlier versions of the write request. Sigh. See below.
       */
      requestWritePacketSize = sizeof (HgfsRequestWriteV3);
      requestWritePacketDataSize = 0;
      requestWriteDataSize = writeSize;
      break;
   case HGFS_OP_WRITE_V3:
      /*
       * Data is packed as a part of the write request.
       * Note, for size we remove the 1 byte placeholder payload
       * so it isn't counted twice.
       */
      requestWritePacketSize = sizeof (HgfsRequestWriteV3) - 1;
      requestWritePacketDataSize = writeSize;
      requestWriteDataSize = 0;
      break;
   case HGFS_OP_WRITE:
      /*
       * Data is packed as a part of the write request.
       * Note, for size we remove the 1 byte placeholder payload
       * so it isn't counted twice.
       */
      requestWritePacketSize = sizeof (HgfsRequestWrite) - 1;
      requestWritePacketDataSize = writeSize;
      requestWriteDataSize = 0;
      break;
   default:
      status = HGFS_ERROR_PROTOCOL;
      LOG(4, "%s: Unsupported protocol version passed %d -> PROTOCOL_ERROR.\n",
          __FUNCTION__, input->op);
      NOT_IMPLEMENTED();
      goto exit;
   }

   /*
    * Validate the packet size with the header, write request and write data.
    */
   if (!HSPU_ValidateDataPacketSize(input->packet, requestWriteDataSize) ||
       !HSPU_ValidateRequestPacketSize(input->packet,
                                       requestWriteHeaderSize,
                                       requestWritePacketSize,
                                       requestWritePacketDataSize)) {
      status = HGFS_ERROR_INVALID_PARAMETER;
      LOG(4, "%s: Error: write data size pkt %"FMTSZ"u data %"FMTSZ"u\n",
          __FUNCTION__, requestWritePacketDataSize, requestWriteDataSize);
      goto exit;
   }

   /*
    * Now map the file handle, and extract the details of the write e.g. writing
    * sequentially or appending
    *
    * Validate the file handle by retrieving it possibly from the cache.
    */
   status = HgfsPlatformGetFd(writeHandle, input->session,
                              ((flags & HGFS_WRITE_APPEND) ? TRUE : FALSE),
                              &writeFileDesc);
   if (status != HGFS_ERROR_SUCCESS) {
      LOG(4, "%s: Error: arg validation handle -> %d.\n", __FUNCTION__, status);
      goto exit;
   }

   if (!HgfsHandleIsSequentialOpen(writeHandle, input->session, &sequentialHandle)) {
      status = HGFS_ERROR_INVALID_HANDLE;
      LOG(4, "%s: Could not get sequential open status\n", __FUNCTION__);
      goto exit;
   }

#if defined(__APPLE__)
   if (!HgfsHandle2AppendFlag(writeHandle, input->session, &appendHandle)) {
      status = HGFS_ERROR_INVALID_HANDLE;
      LOG(4, "%s: Could not get append mode\n", __FUNCTION__);
      goto exit;
   }
#endif

exit:
   *writefd = writeFileDesc;
   *writeSequential = sequentialHandle;
   *writeAppend = appendHandle;
   LOG(4, "%s: arg validation check return (file %u data size %u) %u.\n",
       __FUNCTION__, writeHandle, writeSize, status);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerWrite --
 *
 *    Handle a Write request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerWrite(HgfsInputParam *input)  // IN: Input params
{
   uint64 writeOffset;
   uint32 writeSize;
   uint32 writtenSize = 0;
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   HgfsWriteFlags writeFlags;
   const void *writeData;
   size_t writeReplySize = 0;
   HgfsHandle writeFile;
   fileDesc writeFd;
   Bool writeSequential;
   Bool writeAppend;

   HGFS_ASSERT_INPUT(input);

   if (!HgfsUnpackWriteRequest(input->payload, input->payloadSize, input->op,
                               &writeFile, &writeOffset, &writeSize, &writeFlags,
                               &writeData)) {
      LOG(4, "%s: Error: Op %d unpack write request arguments\n", __FUNCTION__,
          input->op);
      status = HGFS_ERROR_PROTOCOL;
      goto exit;
   }

   /*
    * Validate the write arguments with the data and request buffers to ensure
    * there isn't a malformed request or we try to write more data than is in the buffer.
    */
   status = HgfsServerValidateWrite(input,
                                    writeFile,
                                    writeOffset,
                                    writeSize,
                                    writeFlags,
                                    &writeFd,
                                    &writeSequential,
                                    &writeAppend);
   if (status != HGFS_ERROR_SUCCESS) {
      LOG(4, "%s: Error: validate args %u.\n", __FUNCTION__, status);
      goto exit;
   }

   if (writeSize > 0) {
      if (NULL == writeData) {
         /* No inline data to write, get it from the transport shared memory. */
         HSPU_SetDataPacketSize(input->packet, writeSize);
         writeData = HSPU_GetDataPacketBuf(input->packet, BUF_READABLE,
                                           input->transportSession->channelCbTable);
         if (NULL == writeData) {
            LOG(4, "%s: Error: Op %d mapping write data buffer\n",
                __FUNCTION__, input->op);
            status = HGFS_ERROR_PROTOCOL;
            goto exit;
         }
      }

      status = HgfsPlatformWriteFile(writeFd,
                                     input->session,
                                     writeOffset,
                                     writeSize,
                                     writeFlags,
                                     writeSequential,
                                     writeAppend,
                                     writeData,
                                     &writtenSize);
      if (HGFS_ERROR_SUCCESS != status) {
         goto exit;
      }
   }

   if (!HgfsPackWriteReply(input->packet, input->request, input->op,
                           writtenSize, &writeReplySize, input->session)) {
      status = HGFS_ERROR_INTERNAL;
   }

exit:
   HgfsServerCompleteRequest(status, writeReplySize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerQueryVolInt --
 *
 *    Internal function to query the volume's free space and capacity.
 *    The volume queried can be:
 *    - real taken from the file path of a real file or folder
 *    - virtual taken from one of the HGFS virtual folders which can span
 *      multiple volumes.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *
 *-----------------------------------------------------------------------------
 */
static HgfsInternalStatus
HgfsServerQueryVolInt(HgfsSessionInfo *session,   // IN: session info
                      const char *fileName,       // IN: cpName for the volume
                      size_t fileNameLength,      // IN: cpName length
                      uint32 caseFlags,           // IN: case sensitive/insensitive name
                      uint64 *freeBytes,          // OUT: free space in bytes
                      uint64 *totalBytes)         // OUT: capacity in bytes
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   uint64 outFreeBytes = 0;
   uint64 outTotalBytes = 0;
   char *utf8Name = NULL;
   size_t utf8NameLen;
   HgfsNameStatus nameStatus;
   HgfsShareInfo shareInfo;
   VolumeInfoType infoType;

   /*
    * XXX - make the filename const!
    * It is now safe to read the file name field.
    */
   nameStatus = HgfsServerGetLocalNameInfo(fileName,
                                           fileNameLength,
                                           caseFlags,
                                           session,
                                           &shareInfo,
                                           &utf8Name,
                                           &utf8NameLen);

   /* Check if we have a real path and if so handle it here. */
   if (nameStatus == HGFS_NAME_STATUS_COMPLETE) {
      Bool success;

      ASSERT(utf8Name);
      LOG(4, "%s: querying path %s\n", __FUNCTION__, utf8Name);
      success = HgfsServerStatFs(utf8Name, utf8NameLen,
                                 &outFreeBytes, &outTotalBytes);
      free(utf8Name);
      if (!success) {
         LOG(4, "%s: error getting volume information\n", __FUNCTION__);
         status = HGFS_ERROR_IO;
      }
      goto exit;
   }

    /*
     * If we're outside the Tools, find out if we're to compute the minimum
     * values across all shares, or the maximum values.
     */
   infoType = VOLUME_INFO_TYPE_MIN;
   /* We have a virtual folder path and if so pass it over to the platform code. */
   if (0 == (gHgfsCfgSettings.flags & HGFS_CONFIG_VOL_INFO_MIN)) {
      /* Using the maximum volume size and space values. */
      infoType = VOLUME_INFO_TYPE_MAX;
   }

   status = HgfsPlatformVDirStatsFs(session,
                                    nameStatus,
                                    infoType,
                                    &outFreeBytes,
                                    &outTotalBytes);

exit:
   *freeBytes  = outFreeBytes;
   *totalBytes = outTotalBytes;
   LOG(4, "%s: return %"FMT64"u bytes Free %"FMT64"u bytes\n", __FUNCTION__,
       outTotalBytes, outFreeBytes);

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerQueryVolume --
 *
 *    Handle a Query Volume request.
 *
 *    Right now we only handle the volume space request. Call Wiper library
 *    to get the volume information.
 *    It is possible that shared folders can belong to different volumes on
 *    the server. If this is the case, default to return the space information
 *    of the volume that has the least amount of the available space, but it's
 *    configurable with a config option (tools.hgfs.volumeInfoType). 2 possible
 *    options, min and max.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerQueryVolume(HgfsInputParam *input)  // IN: Input params
{
   HgfsInternalStatus status;
   size_t replyPayloadSize = 0;
   HgfsHandle file;
   const char *fileName;
   size_t fileNameLength;
   uint32 caseFlags;
   Bool useHandle;
   uint64 freeBytes;
   uint64 totalBytes;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackQueryVolumeRequest(input->payload, input->payloadSize, input->op,
                                    &useHandle, &fileName,
                                    &fileNameLength, &caseFlags, &file)) {
      /*
       * We don't yet support file handle for this operation.
       * Clients should retry using the file name.
       */
      if (useHandle) {
         LOG(4, "%s: Doesn't support file handle.\n", __FUNCTION__);
         status = HGFS_ERROR_INVALID_PARAMETER;
      } else {
         status = HgfsServerQueryVolInt(input->session,
                                        fileName,
                                        fileNameLength,
                                        caseFlags,
                                        &freeBytes,
                                        &totalBytes);
         if (HGFS_ERROR_SUCCESS == status) {
            if (!HgfsPackQueryVolumeReply(input->packet, input->request,
                                          input->op, freeBytes, totalBytes,
                                          &replyPayloadSize, input->session)) {
               status = HGFS_ERROR_INTERNAL;
            }
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSymlinkCreate --
 *
 *    Platform independent function that verifies whether symbolic link creation
 *    is allowed for the specific shared folder and then calls platform specific
 *    HgfsPlatformSymlinkCreate to do the actual job.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsSymlinkCreate(HgfsSessionInfo *session, // IN: session info,
                  const char *srcFileName,  // IN: symbolic link file name
                  uint32 srcFileNameLength, // IN: symbolic link name length
                  uint32 srcCaseFlags,      // IN: symlink case flags
                  const char *trgFileName,  // IN: symbolic link target name
                  uint32 trgFileNameLength, // IN: target name length
                  uint32 trgCaseFlags)      // IN: target case flags
{
   HgfsShareInfo shareInfo;
   HgfsInternalStatus status = 0;
   HgfsNameStatus nameStatus;
   HgfsShareOptions configOptions;
   char *localSymlinkName = NULL;
   size_t localSymlinkNameLen;
   char localTargetName[HGFS_PACKET_MAX];

   /*
    * It is now safe to read the symlink file name and the
    * "targetName" field
    */

   nameStatus = HgfsServerGetLocalNameInfo(srcFileName,
                                           srcFileNameLength,
                                           srcCaseFlags,
                                           session,
                                           &shareInfo,
                                           &localSymlinkName,
                                           &localSymlinkNameLen);
   if (nameStatus == HGFS_NAME_STATUS_COMPLETE) {
      if (shareInfo.writePermissions ) {
         /* Get the config options. */
         nameStatus = HgfsServerPolicy_GetShareOptions(srcFileName, srcFileNameLength,
                                                       &configOptions);
         if (nameStatus == HGFS_NAME_STATUS_COMPLETE) {
            /* Prohibit symlink ceation if symlink following is enabled. */
            if (HgfsServerPolicy_IsShareOptionSet(configOptions, HGFS_SHARE_FOLLOW_SYMLINKS)) {
               status = HGFS_ERROR_ACCESS_DENIED;
            }
         } else {
            LOG(4, "%s: no matching share: %s.\n", __FUNCTION__, srcFileName);
            status = HgfsPlatformConvertFromNameStatus(nameStatus);
         }
      } else {
         status = HgfsPlatformFileExists(localSymlinkName);
         if (status != 0) {
            if (status == HGFS_ERROR_FILE_NOT_FOUND) {
               status = HGFS_ERROR_ACCESS_DENIED;
            }
         } else {
            status = HGFS_ERROR_FILE_EXIST;
         }
         LOG(4, "%s: failed access check, error %d\n", __FUNCTION__, status);
      }
   } else {
      LOG(4, "%s: symlink name access check failed\n", __FUNCTION__);
      status = HgfsPlatformConvertFromNameStatus(nameStatus);
   }
   if (HGFS_ERROR_SUCCESS == status) {
      /* Convert from CPName-lite to normal and NUL-terminate. */
      memcpy(localTargetName, trgFileName, trgFileNameLength);
      CPNameLite_ConvertFrom(localTargetName, trgFileNameLength, DIRSEPC);
      localTargetName[trgFileNameLength] = '\0';

      status = HgfsPlatformSymlinkCreate(localSymlinkName, localTargetName);
   }

   free(localSymlinkName);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSymlinkCreate --
 *
 *    Handle a SymlinkCreate request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSymlinkCreate(HgfsInputParam *input)  // IN: Input params
{
   HgfsInternalStatus status;
   HgfsHandle srcFile;
   const char *srcFileName;
   size_t srcFileNameLength;
   uint32 srcCaseFlags;
   Bool srcUseHandle;
   HgfsHandle trgFile;
   const char *trgFileName;
   size_t trgFileNameLength;
   uint32 trgCaseFlags;
   Bool trgUseHandle;
   size_t replyPayloadSize = 0;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackSymlinkCreateRequest(input->payload, input->payloadSize, input->op,
                                      &srcUseHandle, &srcFileName,
                                      &srcFileNameLength, &srcCaseFlags, &srcFile,
                                      &trgUseHandle, &trgFileName,
                                      &trgFileNameLength, &trgCaseFlags, &trgFile)) {
      /*
       * We don't yet support file handle for this operation.
       * Clients should retry using the file name.
       */
      if (srcUseHandle || trgUseHandle) {
         LOG(4, "%s: Doesn't support file handle.\n", __FUNCTION__);
         status = HGFS_ERROR_INVALID_PARAMETER;
      } else {
         status = HgfsSymlinkCreate(input->session, srcFileName, srcFileNameLength,
                                    srcCaseFlags, trgFileName, trgFileNameLength,
                                    trgCaseFlags);
         if (HGFS_ERROR_SUCCESS == status) {
            if (!HgfsPackSymlinkCreateReply(input->packet, input->request, input->op,
                                            &replyPayloadSize, input->session)) {
               status = HGFS_ERROR_INTERNAL;
            }
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSearchOpen --
 *
 *    Handle a search open request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSearchOpen(HgfsInputParam *input)  // IN: Input params
{
   HgfsInternalStatus status;
   size_t replyPayloadSize = 0;
   const char *dirName;
   size_t dirNameLength;
   uint32 caseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
   HgfsHandle search;
   HgfsNameStatus nameStatus;
   HgfsShareInfo shareInfo;
   char *baseDir = NULL;
   size_t baseDirLen;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackSearchOpenRequest(input->payload, input->payloadSize, input->op,
                                   &dirName, &dirNameLength, &caseFlags)) {
      nameStatus = HgfsServerGetLocalNameInfo(dirName, dirNameLength, caseFlags,
                                              input->session, &shareInfo,
                                              &baseDir, &baseDirLen);
      status = HgfsPlatformSearchDir(nameStatus, dirName, dirNameLength, caseFlags,
                                     &shareInfo, baseDir, baseDirLen,
                                     input->session, &search);
      if (HGFS_ERROR_SUCCESS == status) {
         if (!HgfsPackSearchOpenReply(input->packet, input->request, input->op, search,
                                      &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
   free(baseDir);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsValidateRenameFile --
 *
 *    Validates if the file can can participate in rename process either as
 *    as a source or as a target.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS if rename operation is allowed.
 *    Appropriate error code otherwise.
 *
 * Side effects:
 *    Allcates locaFileName which must be freed by the caller.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsValidateRenameFile(Bool useHandle,            // IN:
                       HgfsHandle fileHandle,     // IN:
                       const char *cpName,        // IN:
                       size_t cpNameLength,       // IN:
                       uint32 caseFlags,          // IN:
                       HgfsSessionInfo *session,  // IN: Session info
                       fileDesc* descr,           // OUT:
                       HgfsShareInfo *shareInfo,  // OUT:
                       char **localFileName,      // OUT:
                       size_t *localNameLength)   // OUT:
{
   HgfsInternalStatus status;
   Bool sharedFolderOpen = FALSE;
   HgfsLockType serverLock = HGFS_LOCK_NONE;
   HgfsNameStatus nameStatus;


   if (useHandle) {
      status = HgfsPlatformGetFd(fileHandle, session, FALSE, descr);

      if (HGFS_ERROR_SUCCESS != status) {
         LOG(4, "%s: could not map cached handle %d, error %u\n",
             __FUNCTION__, fileHandle, status);
      } else if (!HgfsHandle2FileNameMode(fileHandle, session, &shareInfo->readPermissions,
                                          &shareInfo->writePermissions, localFileName,
                                          localNameLength)) {
         /*
          * HgfsPlatformRename requires valid source file name even when file handle
          * is specified.
          * Also the name will be required to update the nodes on a successful
          * rename operation.
          */
        LOG(4, "%s: could not get file name for fd %d\n", __FUNCTION__, *descr);
        status = HGFS_ERROR_INVALID_HANDLE;
      } else if (HgfsHandleIsSharedFolderOpen(fileHandle, session, &sharedFolderOpen) &&
                                              sharedFolderOpen) {
         LOG(4, "%s: Cannot rename shared folder\n", __FUNCTION__);
         status = HGFS_ERROR_ACCESS_DENIED;
      }
   } else {
      nameStatus = HgfsServerGetLocalNameInfo(cpName,
                                              cpNameLength,
                                              caseFlags,
                                              session,
                                              shareInfo,
                                              localFileName,
                                              localNameLength);
      if (HGFS_NAME_STATUS_COMPLETE != nameStatus) {
         LOG(4, "%s: access check failed\n", __FUNCTION__);
         status = HgfsPlatformConvertFromNameStatus(nameStatus);
      } else if (HgfsServerIsSharedFolderOnly(cpName, cpNameLength)) {
         /* Guest OS is not allowed to rename shared folder. */
         LOG(4, "%s: Cannot rename shared folder\n", __FUNCTION__);
         status = HGFS_ERROR_ACCESS_DENIED;
      } else {
         status = HGFS_ERROR_SUCCESS;
      }
   }

   ASSERT(*localFileName != NULL || HGFS_ERROR_SUCCESS != status);

   if (HGFS_ERROR_SUCCESS == status) {
      if (HgfsFileHasServerLock(*localFileName, session, &serverLock, descr)) {
         /*
          * XXX: Before renaming the file, check to see if we are holding
          * an oplock on both the old and new files. If one of them is oplocked, and
          * we commence with the rename, we'll trigger an oplock break that'll
          * deadlock us. The client should be smart enough to break necessary oplocks
          * on the source and target files before calling rename, so we'll return
          * an error.
          */

         LOG (4, "%s: File has an outstanding oplock. Client "
            "should remove this oplock and try again.\n", __FUNCTION__);
         status = HGFS_ERROR_PATH_BUSY;
      }
   }

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerRename --
 *
 *    Handle a Rename request.
 *
 *    Simply converts the new and old names to local filenames, calls
 *    platform specific function to rename/move the file, and returns an
 *    appropriate response to the driver.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerRename(HgfsInputParam *input)  // IN: Input params
{
   char *utf8OldName = NULL;
   size_t utf8OldNameLen;
   char *utf8NewName = NULL;
   size_t utf8NewNameLen;
   const char *cpOldName;
   size_t cpOldNameLen;
   const char *cpNewName;
   size_t cpNewNameLen;
   HgfsInternalStatus status;
#ifdef _WIN32
   fileDesc srcFileDesc = INVALID_HANDLE_VALUE;
   fileDesc targetFileDesc = INVALID_HANDLE_VALUE;
#else
   fileDesc srcFileDesc = -1;
   fileDesc targetFileDesc = -1;
#endif
   HgfsHandle srcFile;
   HgfsHandle targetFile;
   HgfsRenameHint hints;
   uint32 oldCaseFlags;
   uint32 newCaseFlags;
   HgfsShareInfo shareInfo;
   size_t replyPayloadSize = 0;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackRenameRequest(input->payload, input->payloadSize, input->op, &cpOldName,
                               &cpOldNameLen, &cpNewName, &cpNewNameLen,
                               &hints, &srcFile, &targetFile, &oldCaseFlags,
                               &newCaseFlags)) {
      status = HgfsValidateRenameFile((hints & HGFS_RENAME_HINT_USE_SRCFILE_DESC) != 0,
                                      srcFile,
                                      cpOldName,
                                      cpOldNameLen,
                                      oldCaseFlags,
                                      input->session,
                                      &srcFileDesc,
                                      &shareInfo,
                                      &utf8OldName,
                                      &utf8OldNameLen);
      if (HGFS_ERROR_SUCCESS == status) {
         /*
          * Renaming a file requires both read and write permssions for the
          * original file.
          * However the error code must be different depending on the existence
          * of the file with the same name.
          */
         if (!shareInfo.writePermissions || !shareInfo.readPermissions) {
            status = HgfsPlatformFileExists(utf8OldName);
            if (HGFS_ERROR_SUCCESS == status) {
               status = HGFS_ERROR_ACCESS_DENIED;
            }
            LOG(4, "HgfsServerRename: failed access check, error %d\n", status);
         } else {
            status =
               HgfsValidateRenameFile((hints & HGFS_RENAME_HINT_USE_TARGETFILE_DESC) != 0,
                                      targetFile,
                                      cpNewName,
                                      cpNewNameLen,
                                      newCaseFlags,
                                      input->session,
                                      &targetFileDesc,
                                      &shareInfo,
                                      &utf8NewName,
                                      &utf8NewNameLen);
            if (HGFS_ERROR_SUCCESS == status) {
               /*
                * Renaming a file requires both read and write permssions for
                * the target directory.
                * However the error code must be different depending on the existence
                * of the target directory - if the destination directory exists then
                * ERROR_ACCESS_DENIED should be returned regardless if the destination
                * file exists.
                */
               if (!shareInfo.writePermissions || !shareInfo.readPermissions) {
                  status = HgfsPlatformFileExists(utf8NewName);
                  if (HGFS_ERROR_SUCCESS == status ||
                      HGFS_ERROR_FILE_NOT_FOUND == status) {
                     status = HGFS_ERROR_ACCESS_DENIED;
                  }
                  LOG(4, "HgfsServerRename: failed access check, error %d\n", status);
               }
            }
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   /* If all pre-conditions are met go ahead with actual rename. */
   if (HGFS_ERROR_SUCCESS == status) {
      status = HgfsPlatformRename(utf8OldName, srcFileDesc, utf8NewName,
         targetFileDesc, hints);
      if (HGFS_ERROR_SUCCESS == status) {
         /* Update all file nodes that refer to this file to contain the new name. */
         HgfsUpdateNodeNames(utf8OldName, utf8NewName, input->session);
         if (!HgfsPackRenameReply(input->packet, input->request, input->op,
                                  &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   }

   free(utf8OldName);
   free(utf8NewName);

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerCreateDir --
 *
 *    Handle a CreateDir request.
 *
 *    Simply converts to the local filename, calls platform specific
 *    code to create a directory, and returns an appropriate response to the driver.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerCreateDir(HgfsInputParam *input)  // IN: Input params
{
   HgfsInternalStatus status;
   HgfsNameStatus nameStatus;
   HgfsCreateDirInfo info;
   char *utf8Name = NULL;
   size_t utf8NameLen;
   size_t replyPayloadSize = 0;
   HgfsShareInfo shareInfo;

   HGFS_ASSERT_INPUT(input);

   if (!HgfsUnpackCreateDirRequest(input->payload, input->payloadSize,
                                   input->op, &info)) {
      status = HGFS_ERROR_PROTOCOL;
      goto exit;
   }

   nameStatus = HgfsServerGetLocalNameInfo(info.cpName, info.cpNameSize,
                                           info.caseFlags, input->session,
                                           &shareInfo, &utf8Name, &utf8NameLen);
   if (HGFS_NAME_STATUS_COMPLETE == nameStatus) {
      ASSERT(utf8Name);

      /*
       * Check if the guest is attempting to create a directory as a new
       * share in our virtual root folder. If so, then it exists as we have found
       * the share. This should error as a file exists whereas access denied is for
       * new folders that do not collide.
       * The virtual root folder is read-only for guests and new shares can only be
       * created from the host.
       */
      if (HgfsServerIsSharedFolderOnly(info.cpName, info.cpNameSize)) {
         /* Disallow creating a subfolder matching the share in the virtual folder. */
         LOG(4, "%s: Collision: cannot create a folder which is a share\n",
             __FUNCTION__);
         status = HGFS_ERROR_FILE_EXIST;
         goto exit;
      }
      /*
       * For read-only shares we must never attempt to create a directory.
       * However the error code must be different depending on the existence
       * of the file with the same name.
       */
      if (shareInfo.writePermissions) {
         status = HgfsPlatformCreateDir(&info, utf8Name);
         if (HGFS_ERROR_SUCCESS == status) {
            if (!HgfsPackCreateDirReply(input->packet, input->request, info.requestType,
                                        &replyPayloadSize, input->session)) {
               status = HGFS_ERROR_PROTOCOL;
            }
         }
      } else {
         status = HgfsPlatformFileExists(utf8Name);
         if (HGFS_ERROR_SUCCESS == status) {
            status = HGFS_ERROR_FILE_EXIST;
         } else if (HGFS_ERROR_FILE_NOT_FOUND == status) {
            status = HGFS_ERROR_ACCESS_DENIED;
         }
      }
   } else {
      /*
       * Check if the name does not exist - the share was not found.
       * Then it could one of two things: the share was removed/disabled;
       * or we could be in the root share itself and have a new name.
       * To return the correct error, if we are in the root share,
       * we must check the open mode too - creation of new files/folders
       * should fail access denied, for anything else "not found" is acceptable.
       */
      if (nameStatus == HGFS_NAME_STATUS_DOES_NOT_EXIST) {
         if (HgfsServerIsSharedFolderOnly(info.cpName,
                                          info.cpNameSize)) {
            nameStatus = HGFS_NAME_STATUS_ACCESS_DENIED;
            LOG(4, "%s: disallow new folder creation in virtual share root.\n",
                __FUNCTION__);
         } else {
            LOG(4, "%s: Shared folder not found\n", __FUNCTION__);
         }
      } else {
         LOG(4, "%s: Shared folder access error %u\n", __FUNCTION__, nameStatus);
      }

      status = HgfsPlatformConvertFromNameStatus(nameStatus);
   }

exit:
   HgfsServerCompleteRequest(status, replyPayloadSize, input);
   free(utf8Name);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerDeleteFile --
 *
 *    Handle a Delete File request.
 *
 *    Simply converts to the local filename, calls DeleteFile on the
 *    file or calls the Windows native API Delete, and returns an
 *    appropriate response to the driver.
 *
 *    Enforcing read-only shares restrictions
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerDeleteFile(HgfsInputParam *input)  // IN: Input params
{
   const char *cpName;
   size_t cpNameSize;
   HgfsLockType serverLock = HGFS_LOCK_NONE;
   fileDesc fileDesc;
   HgfsHandle file;
   HgfsDeleteHint hints = 0;
   HgfsInternalStatus status;
   HgfsNameStatus nameStatus;
   uint32 caseFlags;
   size_t replyPayloadSize = 0;
   HgfsShareInfo shareInfo;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackDeleteRequest(input->payload, input->payloadSize, input->op, &cpName,
                               &cpNameSize, &hints, &file, &caseFlags)) {
      if (hints & HGFS_DELETE_HINT_USE_FILE_DESC) {
         status = HgfsPlatformDeleteFileByHandle(file, input->session);
      } else {
         char *utf8Name = NULL;
         size_t utf8NameLen;

         nameStatus = HgfsServerGetLocalNameInfo(cpName, cpNameSize, caseFlags,
                                                 input->session, &shareInfo,
                                                 &utf8Name, &utf8NameLen);
         if (nameStatus == HGFS_NAME_STATUS_COMPLETE) {
            /*
             * Deleting a file needs both read and write permssions.
             * However the error code must be different depending on the existence
             * of the file with the same name.
             */
            if (!shareInfo.writePermissions || !shareInfo.readPermissions) {
               status = HgfsPlatformFileExists(utf8Name);
               if (HGFS_ERROR_SUCCESS == status) {
                  status = HGFS_ERROR_ACCESS_DENIED;
               }
               LOG(4, "HgfsServerDeleteFile: failed access check, error %d\n", status);
            } else if (HgfsFileHasServerLock(utf8Name, input->session, &serverLock,
                       &fileDesc)) {
               /*
                * XXX: If the file has an oplock, the client should have broken it on
                * its own by now. Sorry!
                */
               LOG (4, "%s: File has an outstanding oplock. Client should "
                  "remove this oplock and try again.\n", __FUNCTION__);
               status = HGFS_ERROR_PATH_BUSY;
            } else {
               LOG(4, "%s: deleting \"%s\"\n", __FUNCTION__, utf8Name);
               status = HgfsPlatformDeleteFileByName(utf8Name);
            }
            free(utf8Name);
         } else {
            LOG(4, "%s: Shared folder does not exist.\n", __FUNCTION__);
            status = HgfsPlatformConvertFromNameStatus(nameStatus);
         }
      }
      if (HGFS_ERROR_SUCCESS == status) {
         if (!HgfsPackDeleteReply(input->packet, input->request, input->op,
                                  &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerDeleteDir --
 *
 *    Handle a Delete Dir request.
 *
 *    Simply converts to the local filename, calls RemoveDirectory on the
 *    directory or Windows native API delete if we have a valid handle,
 *    and returns an appropriate response to the driver.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerDeleteDir(HgfsInputParam *input)  // IN: Input params
{
   const char *cpName;
   size_t cpNameSize;
   HgfsInternalStatus status;
   HgfsNameStatus nameStatus;
   HgfsHandle file;
   HgfsDeleteHint hints = 0;
   fileDesc fileDesc;
   Bool sharedFolderOpen = FALSE;
   uint32 caseFlags;
   size_t replyPayloadSize = 0;
   HgfsShareInfo shareInfo;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackDeleteRequest(input->payload, input->payloadSize, input->op, &cpName,
                               &cpNameSize, &hints, &file, &caseFlags)) {
      if (hints & HGFS_DELETE_HINT_USE_FILE_DESC) {

         status = HgfsPlatformGetFd(file, input->session, FALSE, &fileDesc);

         if (HGFS_ERROR_SUCCESS == status) {
            if (HgfsHandleIsSharedFolderOpen(file, input->session, &sharedFolderOpen) &&
               sharedFolderOpen) {
               LOG(4, "%s: Cannot delete shared folder\n", __FUNCTION__);
               status = HGFS_ERROR_ACCESS_DENIED;
            } else {
               status = HgfsPlatformDeleteDirByHandle(file, input->session);
               if (HGFS_ERROR_SUCCESS != status) {
                  LOG(4, "%s: error deleting directory %d: %d\n", __FUNCTION__,
                     file, status);
               }
            }
         } else {
            LOG(4, "%s: could not map cached handle %u, error %u\n",
               __FUNCTION__, file, status);
         }
      } else {
         char *utf8Name = NULL;
         size_t utf8NameLen;

         nameStatus = HgfsServerGetLocalNameInfo(cpName, cpNameSize, caseFlags,
                                                 input->session, &shareInfo,
                                                 &utf8Name, &utf8NameLen);
         if (HGFS_NAME_STATUS_COMPLETE == nameStatus) {
            ASSERT(utf8Name);
            /* Guest OS is not allowed to delete shared folder. */
            if (HgfsServerIsSharedFolderOnly(cpName, cpNameSize)){
               LOG(4, "%s: Cannot delete shared folder\n", __FUNCTION__);
               status = HGFS_ERROR_ACCESS_DENIED;
            } else if (!shareInfo.writePermissions || !shareInfo.readPermissions) {
               /*
                * Deleting a directory requires both read and write permissions.
                * However the error code must be different depending on the existence
                * of the file with the same name.
                */
               status = HgfsPlatformFileExists(utf8Name);
               if (HGFS_ERROR_SUCCESS == status) {
                  status = HGFS_ERROR_ACCESS_DENIED;
               }
               LOG(4, "HgfsServerDeleteDir: failed access check, error %d\n", status);
            } else {
               LOG(4, "%s: removing \"%s\"\n", __FUNCTION__, utf8Name);
               status = HgfsPlatformDeleteDirByName(utf8Name);
            }
            free(utf8Name);
         } else {
            LOG(4, "%s: access check failed\n", __FUNCTION__);
            status = HgfsPlatformConvertFromNameStatus(nameStatus);
         }
      }
      if (HGFS_ERROR_SUCCESS == status) {
         if (!HgfsPackDeleteReply(input->packet, input->request, input->op,
                                  &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerServerLockChange --
 *
 *    Called by the client when it wants to either acquire an oplock on a file
 *    that was previously opened, or when it wants to release/downgrade an
 *    oplock on a file that was previously oplocked.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerServerLockChange(HgfsInputParam *input)  // IN: Input params
{

   HGFS_ASSERT_INPUT(input);

   HgfsServerCompleteRequest(HGFS_ERROR_NOT_SUPPORTED, 0, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerWriteWin32Stream --
 *
 *    Handle a write request in the WIN32_STREAM_ID format.
 *
 * Results:
 *    ERROR_SUCCESS or an appropriate Win32 error code.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerWriteWin32Stream(HgfsInputParam *input)  // IN: Input params
{
   uint32  actualSize;
   HgfsInternalStatus status;
   HgfsHandle file;
   const char *dataToWrite;
   Bool doSecurity;
   size_t replyPayloadSize = 0;
   size_t requiredSize;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackWriteWin32StreamRequest(input->payload, input->payloadSize, input->op, &file,
                                         &dataToWrite, &requiredSize, &doSecurity)) {
      status = HgfsPlatformWriteWin32Stream(file, (char *)dataToWrite, (uint32)requiredSize,
                                            doSecurity, &actualSize, input->session);
      if (HGFS_ERROR_SUCCESS == status) {
         if (!HgfsPackWriteWin32StreamReply(input->packet, input->request, input->op,
                                            actualSize, &replyPayloadSize,
                                            input->session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSetDirWatchByHandle --
 *
 *    Sets directory notification watch request using directory handle.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerSetDirWatchByHandle(HgfsInputParam *input,         // IN: Input params
                              HgfsHandle dir,                // IN: directory handle
                              uint32 events,                 // IN: event types to report
                              Bool watchTree,                // IN: recursive watch
                              HgfsSubscriberHandle *watchId) // OUT: watch id
{
   HgfsInternalStatus status;
   char *fileName = NULL;
   size_t fileNameSize;
   HgfsSharedFolderHandle sharedFolder = HGFS_INVALID_FOLDER_HANDLE;

   LOG(8, "%s: entered\n", __FUNCTION__);

   ASSERT(watchId != NULL);

   if (HgfsHandle2NotifyInfo(dir, input->session, &fileName, &fileNameSize,
                             &sharedFolder)) {
      LOG(4, "%s: adding a subscriber on shared folder handle %#x\n",
          __FUNCTION__, sharedFolder);
      *watchId = HgfsNotify_AddSubscriber(sharedFolder, fileName, events, watchTree,
                                          input->session);
      status = (HGFS_INVALID_SUBSCRIBER_HANDLE == *watchId) ? HGFS_ERROR_INTERNAL :
                                                              HGFS_ERROR_SUCCESS;
      LOG(4, "%s: result of add subscriber id %"FMT64"x status %u\n",
          __FUNCTION__, *watchId, status);
   } else {
      status = HGFS_ERROR_INTERNAL;
   }
   free(fileName);
   LOG(8, "%s: exit %u\n", __FUNCTION__, status);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSetDirWatchByName --
 *
 *    Sets directory notification watch request using directory name.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerSetDirWatchByName(HgfsInputParam *input,         // IN: Input params
                            const char *cpName,            // IN: directory name
                            uint32 cpNameSize,             // IN: directory name length
                            uint32 caseFlags,              // IN: case flags
                            uint32 events,                 // IN: event types to report
                            Bool watchTree,                // IN: recursive watch
                            HgfsSubscriberHandle *watchId) // OUT: watch id
{
   HgfsInternalStatus status;
   HgfsNameStatus nameStatus;
   char *utf8Name = NULL;
   size_t utf8NameLen;
   HgfsShareInfo shareInfo;
   HgfsSharedFolderHandle sharedFolder = HGFS_INVALID_FOLDER_HANDLE;

   ASSERT(cpName != NULL);
   ASSERT(watchId != NULL);

   LOG(8, "%s: entered\n",__FUNCTION__);

   nameStatus = HgfsServerGetLocalNameInfo(cpName, cpNameSize, caseFlags,
                                           input->session, &shareInfo,
                                           &utf8Name, &utf8NameLen);
   if (HGFS_NAME_STATUS_COMPLETE == nameStatus) {
      char const *inEnd = cpName + cpNameSize;
      char const *next;
      int len;

      ASSERT(utf8Name);
      /*
       * Get first component.
       */
      len = CPName_GetComponent(cpName, inEnd, (char const **) &next);
      if (len < 0) {
         LOG(4, "%s: get first component failed\n", __FUNCTION__);
         nameStatus = HGFS_NAME_STATUS_FAILURE;
      } else if (0 == len) {
         /* See if we are dealing with the base of the namespace */
         nameStatus = HGFS_NAME_STATUS_INCOMPLETE_BASE;
      } else {
         sharedFolder = shareInfo.handle;
      }

      if (HGFS_NAME_STATUS_COMPLETE == nameStatus &&
          HGFS_INVALID_FOLDER_HANDLE != sharedFolder) {
         if (cpNameSize > len + 1) {
            size_t nameSize = cpNameSize - len - 1;
            char tempBuf[HGFS_PATH_MAX];
            char *tempPtr = tempBuf;
            size_t tempSize = sizeof tempBuf;

            nameStatus = CPName_ConvertFrom((char const **) &next, &nameSize,
                                            &tempSize, &tempPtr);
            if (HGFS_NAME_STATUS_COMPLETE == nameStatus) {
               LOG(8, "%s: session %p id %"FMT64"x on share hnd %#x\n",
                   __FUNCTION__, input->session, input->session->sessionId,
                   sharedFolder);
               *watchId = HgfsNotify_AddSubscriber(sharedFolder, tempBuf, events,
                                                   watchTree,
                                                   input->session);
               status = (HGFS_INVALID_SUBSCRIBER_HANDLE == *watchId) ?
                        HGFS_ERROR_INTERNAL : HGFS_ERROR_SUCCESS;
               LOG(8, "%s: watchId %"FMT64"x result %u\n", __FUNCTION__,
                   *watchId, status);
            } else {
               LOG(4, "%s: Conversion to platform specific name failed\n",
                   __FUNCTION__);
               status = HgfsPlatformConvertFromNameStatus(nameStatus);
            }
         } else {
            LOG(8, "%s: adding subscriber on share hnd %#x\n", __FUNCTION__,
                sharedFolder);
            *watchId = HgfsNotify_AddSubscriber(sharedFolder, "", events, watchTree,
                                                input->session);
            status = (HGFS_INVALID_SUBSCRIBER_HANDLE == *watchId) ? HGFS_ERROR_INTERNAL :
                                                                    HGFS_ERROR_SUCCESS;
            LOG(8, "%s: adding subscriber on share hnd %#x watchId %"FMT64"x result %u\n",
                __FUNCTION__, sharedFolder, *watchId, status);
         }
      } else if (HGFS_NAME_STATUS_INCOMPLETE_BASE == nameStatus) {
         LOG(4, "%s: Notification for root share is not supported yet\n",
             __FUNCTION__);
         status = HGFS_ERROR_INVALID_PARAMETER;
      } else {
         LOG(4, "%s: file not found.\n", __FUNCTION__);
         status = HgfsPlatformConvertFromNameStatus(nameStatus);
      }
   } else {
      LOG(4, "%s: file not found.\n", __FUNCTION__);
      status = HgfsPlatformConvertFromNameStatus(nameStatus);
   }
   free(utf8Name);
   LOG(8, "%s: exit %u\n",__FUNCTION__, status);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSetDirNotifyWatch --
 *
 *    Handle a set directory notification watch request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSetDirNotifyWatch(HgfsInputParam *input)  // IN: Input params
{
   const char *cpName;
   size_t cpNameSize;
   HgfsInternalStatus status;
   HgfsHandle dir;
   uint32 caseFlags;
   size_t replyPayloadSize = 0;
   uint32 flags;
   uint32 events;
   HgfsSubscriberHandle watchId = HGFS_INVALID_SUBSCRIBER_HANDLE;
   Bool useHandle;

   HGFS_ASSERT_INPUT(input);

   LOG(8, "%s: entered\n", __FUNCTION__);

   /*
    * If the active session does not support directory change notification - bail out
    * with an error immediately.
    * Clients are expected to check the session capabilities and flags but a malicious
    * or broken client could still issue this to us.
    */
   if (0 == (input->session->flags & HGFS_SESSION_CHANGENOTIFY_ENABLED)) {
      HgfsServerCompleteRequest(HGFS_ERROR_PROTOCOL, 0, input);
      return;
   }

   if (HgfsUnpackSetWatchRequest(input->payload, input->payloadSize, input->op,
                                 &useHandle, &cpName, &cpNameSize, &flags, &events,
                                 &dir, &caseFlags)) {
      Bool watchTree = 0 != (flags & HGFS_NOTIFY_FLAG_WATCH_TREE);
      if (useHandle) {
         status = HgfsServerSetDirWatchByHandle(input, dir, events, watchTree, &watchId);
      } else {
         status = HgfsServerSetDirWatchByName(input, cpName, cpNameSize, caseFlags,
                                              events, watchTree, &watchId);
      }
      if (HGFS_ERROR_SUCCESS == status) {
         if (!HgfsPackSetWatchReply(input->packet, input->request, input->op,
                                    watchId, &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
   LOG(8, "%s: exit %u\n", __FUNCTION__, status);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerRemoveDirNotifyWatch --
 *
 *    Handle a remove directory notification watch request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerRemoveDirNotifyWatch(HgfsInputParam *input)  // IN: Input params
{
   HgfsSubscriberHandle watchId;
   HgfsInternalStatus status;
   size_t replyPayloadSize = 0;

   LOG(8, "%s: entered\n", __FUNCTION__);
   HGFS_ASSERT_INPUT(input);

   /*
    * If the active session does not support directory change notification - bail out
    * with an error immediately.
    * Clients are expected to check the session capabilities and flags but a malicious
    * or broken client could still issue this to us.
    */
   if (0 == (input->session->flags & HGFS_SESSION_CHANGENOTIFY_ENABLED)) {
      HgfsServerCompleteRequest(HGFS_ERROR_PROTOCOL, 0, input);
      return;
   }

   if (HgfsUnpackRemoveWatchRequest(input->payload, input->payloadSize, input->op,
                                    &watchId)) {
      LOG(8, "%s: remove subscriber on subscr id %"FMT64"x\n", __FUNCTION__,
          watchId);
      if (HgfsNotify_RemoveSubscriber(watchId)) {
         status = HGFS_ERROR_SUCCESS;
      } else {
         status = HGFS_ERROR_INTERNAL;
      }
      LOG(8, "%s: remove subscriber on subscr id %"FMT64"x result %u\n",
          __FUNCTION__, watchId, status);
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }
   if (HGFS_ERROR_SUCCESS == status) {
      if (!HgfsPackRemoveWatchReply(input->packet, input->request, input->op,
         &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_INTERNAL;
      }
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
   LOG(8, "%s: exit result %u\n", __FUNCTION__, status);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetattr --
 *
 *    Handle a Getattr request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerGetattr(HgfsInputParam *input)  // IN: Input params
{
   char *localName = NULL;
   HgfsAttrHint hints = 0;
   HgfsFileAttrInfo attr;
   HgfsInternalStatus status = 0;
   HgfsNameStatus nameStatus;
   const char *cpName;
   size_t cpNameSize;
   char *targetName = NULL;
   uint32 targetNameLen = 0;
   HgfsHandle file; /* file handle from driver */
   uint32 caseFlags = 0;
   HgfsShareOptions configOptions;
   size_t localNameLen;
   HgfsShareInfo shareInfo;
   size_t replyPayloadSize = 0;
   HgfsSessionInfo *session;
   HgfsFileAttrCacheEntry *entry;

   HGFS_ASSERT_INPUT(input);

   session = input->session;

   if (HgfsUnpackGetattrRequest(input->payload, input->payloadSize, input->op, &attr,
                                &hints, &cpName, &cpNameSize, &file, &caseFlags)) {
      /* Client wants us to reuse an existing handle. */
      if (hints & HGFS_ATTR_HINT_USE_FILE_DESC) {
         fileDesc fd;
         HgfsFileNode node;
         Bool found;

         memset(&node, 0, sizeof node);
         found = HgfsGetNodeCopy(file, session, TRUE, &node);

         if (found && NULL != session->fileAttrCache &&
             HgfsCache_Get(session->fileAttrCache, node.utf8Name,
                           (void **)&entry)) {
            attr = entry->attr;
            status = HGFS_ERROR_SUCCESS;
         } else {
            targetNameLen = 0;
            status = HgfsPlatformGetFd(file, session, FALSE, &fd);
            if (HGFS_ERROR_SUCCESS == status) {
               status = HgfsPlatformGetattrFromFd(fd, session, &attr);
               if (found && HGFS_ERROR_SUCCESS == status &&
                   NULL != session->fileAttrCache) {
                  HOM_HANDLE handle =
                     HgfsOplockMonitorFileChange(node.utf8Name, session,
                                                 HgfsOplockFileChangeCb,
                                                 Util_SafeStrdup(node.utf8Name));
                  if (handle != HGFS_OPLOCK_INVALID_MONITOR_HANDLE) {
                     entry = Util_SafeCalloc(1, sizeof *entry);
                     entry->handle = handle;
                     entry->attr = attr;
                     HgfsCache_Put(session->fileAttrCache, node.utf8Name,
                                   entry);
                  }
               }
            } else {
               LOG(4, "%s: Could not get file descriptor\n", __FUNCTION__);
            }
         }
         if (found) {
            free(node.utf8Name);
         }

      } else {
         /*
          * Depending on whether this file/dir is real or virtual, either
          * forge its attributes or look them up in the actual filesystem.
          */
         nameStatus = HgfsServerGetLocalNameInfo(cpName, cpNameSize, caseFlags,
                                                 session, &shareInfo,
                                                 &localName, &localNameLen);
         switch (nameStatus) {
         case HGFS_NAME_STATUS_INCOMPLETE_BASE:
            /*
             * This is the base of our namespace; make up fake status for
             * this directory.
             */

            LOG(4, "%s: getting attrs for base dir\n", __FUNCTION__);
            HgfsPlatformGetDefaultDirAttrs(&attr);
            break;

         case HGFS_NAME_STATUS_COMPLETE:
            /* This is a regular lookup; proceed as usual */
            ASSERT(localName);

            if (NULL != session->fileAttrCache &&
                HgfsCache_Get(session->fileAttrCache, localName,
                              (void **)&entry)) {
               attr = entry->attr;
               status = HGFS_ERROR_SUCCESS;
            } else {
               /* Get the config options. */
               nameStatus = HgfsServerPolicy_GetShareOptions(cpName, cpNameSize,
                                                             &configOptions);
               if (HGFS_NAME_STATUS_COMPLETE == nameStatus) {
                  status = HgfsPlatformGetattrFromName(localName, configOptions,
                                                       (char *)cpName, &attr,
                                                       &targetName);
                  if (HGFS_ERROR_SUCCESS == status &&
                      NULL != session->fileAttrCache) {
                     HOM_HANDLE handle =
                        HgfsOplockMonitorFileChange(localName, session,
                                                    HgfsOplockFileChangeCb,
                                                    Util_SafeStrdup(localName));
                     if (handle != HGFS_OPLOCK_INVALID_MONITOR_HANDLE) {
                        entry = Util_SafeCalloc(1, sizeof *entry);
                        entry->handle = handle;
                        entry->attr = attr;
                        HgfsCache_Put(session->fileAttrCache, localName,
                                      entry);
                     }
                  }
               } else {
                  LOG(4, "%s: no matching share: %s.\n", __FUNCTION__, cpName);
                  status = HGFS_ERROR_FILE_NOT_FOUND;
               }

               if (HGFS_ERROR_SUCCESS == status &&
                   !HgfsServer_ShareAccessCheck(HGFS_OPEN_MODE_READ_ONLY,
                                                shareInfo.writePermissions,
                                                shareInfo.readPermissions)) {
                      status = HGFS_ERROR_ACCESS_DENIED;
               } else if (status != HGFS_ERROR_SUCCESS) {
                  /*
                   * If it is a dangling share server should not return
                   * HGFS_ERROR_FILE_NOT_FOUND
                   * to the client because it causes confusion: a name that is returned
                   * by directory enumeration should not produce "name not found"
                   * error.
                   * Replace it with a more appropriate error code: no such device.
                   */
                  if (status == HGFS_ERROR_FILE_NOT_FOUND &&
                      HgfsServerIsSharedFolderOnly(cpName, cpNameSize)) {
                     status = HGFS_ERROR_IO;
                  }
               }
            }
            break;

         default:
            status = HgfsPlatformHandleIncompleteName(nameStatus, &attr);
         }
         targetNameLen = targetName ? strlen(targetName) : 0;

      }
      if (HGFS_ERROR_SUCCESS == status) {
         if (!HgfsPackGetattrReply(input->packet, input->request, &attr, targetName,
                                   targetNameLen, &replyPayloadSize, session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   free(targetName);
   free(localName);

   HgfsServerCompleteRequest(status, replyPayloadSize, input);

   /*
    * Contrary to Coverity analysis, storage pointed to by the variable
    * "entry" is not leaked; HgfsCache_Put stores a pointer to it in the
    * symlink cache.  However, no Coverity annotation for leaked_storage
    * is added here because such an annotation cannot be made specific to
    * entry; as a result, if any actual memory leaks were to be introduced
    * by a future change, the leaked_storage annotation would cause such
    * new leaks to be flagged as false positives.
    *
    * XXX - can something be done with Coverity function models so that
    * Coverity stops reporting this?
    */
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSetattr --
 *
 *    Handle a Setattr request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSetattr(HgfsInputParam *input)  // IN: Input params
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   HgfsNameStatus nameStatus;
   HgfsFileAttrInfo attr;
   const char *cpName;
   size_t cpNameSize = 0;
   HgfsAttrHint hints = 0;
   HgfsOpenMode shareMode;
   uint32 caseFlags = 0;
   HgfsShareInfo shareInfo;
   HgfsHandle file;
   size_t replyPayloadSize = 0;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackSetattrRequest(input->payload, input->payloadSize, input->op, &attr,
                                &hints, &cpName, &cpNameSize, &file, &caseFlags)) {
      Bool useHostTime = 0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_USE_HOST_TIME);

      /* Client wants us to reuse an existing handle. */
      if (hints & HGFS_ATTR_HINT_USE_FILE_DESC) {
         if (HgfsHandle2ShareMode(file, input->session, &shareMode)) {
            if (HGFS_OPEN_MODE_READ_ONLY != shareMode) {
               status = HgfsPlatformSetattrFromFd(file,
                                                  input->session,
                                                  &attr,
                                                  hints,
                                                  useHostTime);
            } else {
               status = HGFS_ERROR_ACCESS_DENIED;
            }
         } else {
            LOG(4, "%s: could not get share mode fd %d\n", __FUNCTION__, file);
            status = HGFS_ERROR_INVALID_HANDLE;
         }
      } else { /* Client wants us to open a new handle for this operation. */
         char *utf8Name = NULL;
         size_t utf8NameLen;

         nameStatus = HgfsServerGetLocalNameInfo(cpName,
                                                 cpNameSize,
                                                 caseFlags,
                                                 input->session,
                                                 &shareInfo,
                                                 &utf8Name,
                                                 &utf8NameLen);
         if (HGFS_NAME_STATUS_COMPLETE == nameStatus) {
            fileDesc hFile;
            HgfsLockType serverLock = HGFS_LOCK_NONE;
            HgfsShareOptions configOptions;

            /*
             * XXX: If the client has an oplock on this file, it must reuse the
             * handle for the oplocked node (or break the oplock) prior to making
             * a setattr request. Fail this request.
             */
            if (!HgfsServer_ShareAccessCheck(HGFS_OPEN_MODE_WRITE_ONLY,
                                             shareInfo.writePermissions,
                                             shareInfo.readPermissions)) {
               status = HGFS_ERROR_ACCESS_DENIED;
            } else if (HGFS_NAME_STATUS_COMPLETE !=
                       HgfsServerPolicy_GetShareOptions(cpName, cpNameSize,
                       &configOptions)) {
               LOG(4, "%s: no matching share: %s.\n", __FUNCTION__, cpName);
               status = HGFS_ERROR_FILE_NOT_FOUND;
            } else if (HgfsFileHasServerLock(utf8Name, input->session, &serverLock, &hFile)) {
               LOG(4, "%s: An open, oplocked handle exists for "
                  "this file. The client should retry with that handle\n",
                  __FUNCTION__);
               status = HGFS_ERROR_PATH_BUSY;
            } else {
               status = HgfsPlatformSetattrFromName(utf8Name,
                                                    &attr,
                                                    configOptions,
                                                    hints,
                                                    useHostTime);
            }
            free(utf8Name);
         } else {
            LOG(4, "%s: file not found.\n", __FUNCTION__);
            status = HgfsPlatformConvertFromNameStatus(nameStatus);
         }
      }

      if (HGFS_ERROR_SUCCESS == status) {
         if (!HgfsPackSetattrReply(input->packet, input->request, attr.requestType,
                                   &replyPayloadSize, input->session)) {
            status = HGFS_ERROR_INTERNAL;
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerValidateOpenParameters --
 *
 *    Performs confidence check of the input parameters.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS if the parameters are valid.
 *    Appropriate error code otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsServerValidateOpenParameters(HgfsFileOpenInfo *openInfo, // IN/OUT: openfile info
                                 HgfsSessionInfo *session,   // IN: Session info
                                 Bool *denyCreatingFile,     // OUT: No new files
                                 int *followSymlinks)        // OUT: Host resolves link
{
   size_t utf8NameLen;
   HgfsInternalStatus status;

   *followSymlinks = 0;
   *denyCreatingFile = FALSE;

   if ((openInfo->mask & HGFS_OPEN_VALID_MODE)) {
      HgfsNameStatus nameStatus;
      /* It is now safe to read the file name. */
      nameStatus = HgfsServerGetLocalNameInfo(openInfo->cpName,
                                              openInfo->cpNameSize,
                                              openInfo->caseFlags,
                                              session,
                                              &openInfo->shareInfo,
                                              &openInfo->utf8Name,
                                              &utf8NameLen);
      if (HGFS_NAME_STATUS_COMPLETE == nameStatus) {
         if (openInfo->mask & HGFS_OPEN_VALID_FLAGS) {
            HgfsOpenFlags savedOpenFlags = openInfo->flags;

            if (HgfsServerCheckOpenFlagsForShare(openInfo, &openInfo->flags)) {
               HgfsShareOptions configOptions;

               /* Get the config options. */
               nameStatus = HgfsServerPolicy_GetShareOptions(openInfo->cpName,
                                                             openInfo->cpNameSize,
                                                             &configOptions);
               if (nameStatus == HGFS_NAME_STATUS_COMPLETE) {
                  *followSymlinks =
                     HgfsServerPolicy_IsShareOptionSet(configOptions,
                                                       HGFS_SHARE_FOLLOW_SYMLINKS);
                  *denyCreatingFile = savedOpenFlags != openInfo->flags;
                  status = HGFS_ERROR_SUCCESS;
               } else {
                  LOG(4, "%s: no matching share: %s.\n", __FUNCTION__,
                      openInfo->cpName);
                  *denyCreatingFile = TRUE;
                  status = HGFS_ERROR_FILE_NOT_FOUND;
               }
            } else {
               /* Incompatible open mode with share mode. */
               status = HGFS_STATUS_ACCESS_DENIED;
            }
         } else {
            status = HGFS_ERROR_PROTOCOL;
         }
      } else {
         /*
          * Check if the name does not exist - the share was not found.
          * Then it could one of two things: the share was removed/disabled;
          * or we could be in the root share itself and have a new name.
          * To return the correct error, if we are in the root share,
          * we must check the open mode too - creation of new files/folders
          * should fail access denied, for anything else "not found" is acceptable.
          */
         if (nameStatus == HGFS_NAME_STATUS_DOES_NOT_EXIST) {
            if ((openInfo->mask & HGFS_OPEN_VALID_FLAGS &&
                 (openInfo->flags == HGFS_OPEN_CREATE ||
                  openInfo->flags == HGFS_OPEN_CREATE_SAFE ||
                  openInfo->flags == HGFS_OPEN_CREATE_EMPTY)) &&
                HgfsServerIsSharedFolderOnly(openInfo->cpName,
                                             openInfo->cpNameSize)) {
               nameStatus = HGFS_NAME_STATUS_ACCESS_DENIED;
               LOG(4, "%s: New file creation in share root not allowed\n",
                   __FUNCTION__);
            } else {
               LOG(4, "%s: Shared folder not found\n", __FUNCTION__);
            }
         } else {
            LOG(4, "%s: Shared folder access error %u\n", __FUNCTION__,
                nameStatus);
         }
         status = HgfsPlatformConvertFromNameStatus(nameStatus);
      }
   } else {
      LOG(4, "%s: filename or mode not provided\n", __FUNCTION__);
      status = HGFS_ERROR_PROTOCOL;
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerOpen --
 *
 *    Handle an Open request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerOpen(HgfsInputParam *input)  // IN: Input params
{
   HgfsInternalStatus status;
   fileDesc newHandle;
   HgfsLocalId localId;
   HgfsFileOpenInfo openInfo;
   fileDesc fileDesc;
   HgfsLockType serverLock = HGFS_LOCK_NONE;
   size_t replyPayloadSize = 0;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackOpenRequest(input->payload, input->payloadSize, input->op, &openInfo)) {
      int followSymlinks;
      Bool denyCreatingFile;

      status = HgfsServerValidateOpenParameters(&openInfo, input->session,
                                                &denyCreatingFile,
                                                &followSymlinks);
      if (HGFS_ERROR_SUCCESS == status) {
         ASSERT(openInfo.utf8Name);
         LOG(4, "%s: opening \"%s\", mode %u, flags %u, perms %u%u%u%u attr %u\n",
             __FUNCTION__, openInfo.utf8Name, openInfo.mode,
             openInfo.mask & HGFS_OPEN_VALID_FLAGS       ? openInfo.flags      : 0,
             (openInfo.mask & HGFS_OPEN_VALID_SPECIAL_PERMS) ?
             openInfo.specialPerms : 0,
             (openInfo.mask & HGFS_OPEN_VALID_OWNER_PERMS) ?
             openInfo.ownerPerms : 0,
             (openInfo.mask & HGFS_OPEN_VALID_GROUP_PERMS) ?
             openInfo.groupPerms : 0,
             (openInfo.mask & HGFS_OPEN_VALID_OTHER_PERMS) ?
             openInfo.otherPerms : 0,
             openInfo.mask & HGFS_OPEN_VALID_FILE_ATTR   ? (uint32)openInfo.attr : 0);
         /*
          * XXX: Before opening the file, see if we already have this file opened on
          * the server with an oplock on it. If we do, we must fail the new open
          * request, otherwise we will trigger an oplock break that the guest cannot
          * handle at this time (since the HGFS server is running in the context of
          * the vcpu thread), and we'll deadlock.
          *
          * Until we overcome this limitation via Crosstalk, we will be extra smart
          * in the client drivers so as to prevent open requests on handles that
          * already have an oplock. And the server will protect itself like so.
          *
          * XXX: With some extra effort, we could allow a second open for read here,
          * since that won't break a shared oplock, but the clients should already
          * realize that the second open can be avoided via sharing handles, too.
          */
         if (!HgfsFileHasServerLock(openInfo.utf8Name, input->session, &serverLock,
                                    &fileDesc)) {
            /* See if the name is valid, and if so add it and return the handle. */
            status = HgfsPlatformValidateOpen(&openInfo, followSymlinks, input->session,
                                              &localId, &newHandle);
            if (status == HGFS_ERROR_SUCCESS) {
               ASSERT(newHandle >= 0);

               /*
                * Open succeeded, so make new node and return its handle. If we fail,
                * it's almost certainly an internal server error.
                */

               if (HgfsCreateAndCacheFileNode(&openInfo, &localId, newHandle,
                                              FALSE, input->session)) {
                  if (!HgfsPackOpenReply(input->packet, input->request, &openInfo,
                                         &replyPayloadSize, input->session)) {
                     status = HGFS_ERROR_INTERNAL;
                  }
               }
            } else if (denyCreatingFile && HGFS_ERROR_FILE_NOT_FOUND == status) {
               status = HGFS_ERROR_ACCESS_DENIED;
            }
         } else {
            status = HGFS_ERROR_PATH_BUSY;
         }
         free(openInfo.utf8Name);
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSearchReadAttrToMask --
 *
 *    Sets a search read information mask from the retrieved attribute
 *    information.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSearchReadAttrToMask(HgfsFileAttrInfo *attr,     // IN/OUT: attributes for entry
                               HgfsSearchReadMask *mask)   // IN/OUT: what info is required/returned
{
   if (0 != (attr->mask & HGFS_ATTR_VALID_TYPE)) {
      *mask |= (HGFS_SEARCH_READ_FILE_NODE_TYPE);
   }
   if (0 != (attr->mask & HGFS_ATTR_VALID_SIZE)) {
      *mask |= (HGFS_SEARCH_READ_FILE_SIZE);
   }
   if (0 != (attr->mask & HGFS_ATTR_VALID_ALLOCATION_SIZE)) {
      *mask |= (HGFS_SEARCH_READ_ALLOCATION_SIZE);
   }
   if (0 != (attr->mask & (HGFS_ATTR_VALID_CREATE_TIME |
                           HGFS_ATTR_VALID_ACCESS_TIME |
                           HGFS_ATTR_VALID_WRITE_TIME |
                           HGFS_ATTR_VALID_CHANGE_TIME))) {
      *mask |= (HGFS_SEARCH_READ_TIME_STAMP);
   }
   if (0 != (attr->mask & (HGFS_ATTR_VALID_FLAGS |
                           HGFS_ATTR_VALID_OWNER_PERMS |
                           HGFS_ATTR_VALID_GROUP_PERMS |
                           HGFS_ATTR_VALID_OTHER_PERMS))) {
      Bool isReadOnly = TRUE;

      *mask |= (HGFS_SEARCH_READ_FILE_ATTRIBUTES);
      /*
       * For V4 we don't return the permissions as they are really not
       * used. Only used to see if the entry is read only. So set the
       * attribute flag if the entry is read only.
       */
      if (attr->mask & HGFS_ATTR_VALID_OWNER_PERMS &&
          attr->ownerPerms & HGFS_PERM_WRITE) {
          isReadOnly = FALSE;
      }
      if (attr->mask & HGFS_ATTR_VALID_GROUP_PERMS &&
          attr->groupPerms & HGFS_PERM_WRITE) {
          isReadOnly = FALSE;
      }
      if (attr->mask & HGFS_ATTR_VALID_OTHER_PERMS &&
          attr->otherPerms & HGFS_PERM_WRITE) {
          isReadOnly = FALSE;
      }
      if (isReadOnly) {
         attr->flags |= HGFS_ATTR_READONLY;
         attr->mask |= HGFS_ATTR_VALID_FLAGS;
      }
   }
   if (0 != (attr->mask & (HGFS_ATTR_VALID_FILEID |
                           HGFS_ATTR_VALID_NON_STATIC_FILEID))) {
      *mask |= (HGFS_SEARCH_READ_FILE_ID);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetDirEntry --
 *
 *    Gets a directory entry at specified index.
 *
 * Results:
 *    A platform specific error or success.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsGetDirEntry(HgfsHandle hgfsSearchHandle,     // IN: ID for search data
                HgfsSearch *search,              // IN: search data
                HgfsShareOptions configOptions,  // IN: share configuration settings
                HgfsSessionInfo *session,        // IN: session we are called in
                HgfsSearchReadInfo *info,        // IN/OUT: request details
                HgfsSearchReadEntry *entry,      // OUT: directory entry
                Bool *moreEntries)               // OUT: any more entries
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   struct DirectoryEntry *dent;
   HgfsSearchReadMask infoRetrieved;
   HgfsSearchReadMask infoRequested;
   HgfsFileAttrInfo *entryAttr;
   char **entryName;
   uint32 *entryNameLength;
   Bool getAttrs;
   uint32 requestedIndex;

   infoRequested = info->requestedMask;

   entryAttr = &entry->attr;
   entryName = &entry->name;
   entryNameLength = &entry->nameLength;

   requestedIndex = info->currentIndex;

   getAttrs = (0 != (infoRequested & (HGFS_SEARCH_READ_FILE_SIZE |
                                      HGFS_SEARCH_READ_ALLOCATION_SIZE |
                                      HGFS_SEARCH_READ_TIME_STAMP |
                                      HGFS_SEARCH_READ_FILE_ATTRIBUTES |
                                      HGFS_SEARCH_READ_FILE_ID |
                                      HGFS_SEARCH_READ_FILE_NODE_TYPE)));

   /* Clear out what we will return. */
   infoRetrieved = 0;
   memset(entryAttr, 0, sizeof *entryAttr);
   *moreEntries = FALSE;
   *entryName = NULL;
   *entryNameLength = 0;

   status = HgfsServerGetDirEntry(hgfsSearchHandle, session, requestedIndex, FALSE, &dent);
   if (HGFS_ERROR_SUCCESS != status) {
      goto exit;
   }

   if (NULL == dent) {
      /* End of directory entries marker. */
      info->replyFlags |= HGFS_SEARCH_READ_REPLY_FINAL_ENTRY;
      HgfsSearchSetReadAllEntries(hgfsSearchHandle, session);
      goto exit;
   }

   status = HgfsPlatformSetDirEntry(search,
                                    configOptions,
                                    session,
                                    dent,
                                    getAttrs,
                                    entryAttr,
                                    entryName,
                                    entryNameLength);
   if (HGFS_ERROR_SUCCESS != status) {
      goto exit;
   }

   if (getAttrs) {
      /*
       * Update the search read mask for the attributes information.
       */
      HgfsServerSearchReadAttrToMask(entryAttr, &infoRetrieved);
   }

   infoRetrieved |= HGFS_SEARCH_READ_NAME;
   /* Update the entry fields for valid data and index for the dent. */
   entry->mask = infoRetrieved;
   entry->fileIndex = requestedIndex;
   *moreEntries = TRUE;

exit:
   free(dent);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsDoSearchRead --
 *
 *    Gets all the directory entries that remain or as many that will
 *    fit into the reply buffer from the specified index. Fill in the
 *    reply with the records and complete the reply details.
 *
 * Results:
 *    A platform specific error or success.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsDoSearchRead(HgfsHandle hgfsSearchHandle,     // IN: ID for search data
                 HgfsSearch *search,              // IN: search data
                 HgfsShareOptions configOptions,  // IN: share configuration settings
                 HgfsSessionInfo *session,        // IN: session we are called in
                 HgfsSearchReadInfo *info,        // IN/OUT: request details
                 size_t *replyHeaderSize,         // OUT: reply info written size
                 size_t *replyDirentSize)         // OUT: reply dirent written size
{
   HgfsSearchReadEntry entry;
   size_t bytesWritten = 0;
   size_t bytesRemaining = 0;
   char *currentSearchReadRecord = NULL;
   char *lastSearchReadRecord = NULL;
   Bool moreEntries = TRUE;
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;

   info->currentIndex = info->startIndex;
   *replyHeaderSize = 0;
   *replyDirentSize = 0;


   while (moreEntries) {
      size_t offsetInBuffer = ROUNDUP(*replyDirentSize, sizeof (uint64));

      if (info->payloadSize <= offsetInBuffer) {
         break;
      }

      memset(&entry, 0, sizeof entry);

      currentSearchReadRecord = (char*)info->replyPayload + offsetInBuffer;
      bytesRemaining = info->payloadSize - offsetInBuffer;
      bytesWritten = 0;

      status = HgfsGetDirEntry(hgfsSearchHandle,
                               search,
                               configOptions,
                               session,
                               info,
                               &entry,
                               &moreEntries);
      if (HGFS_ERROR_SUCCESS != status) {
         /* Failed to retrieve an entry record, bail. */
         break;
      }

      if (!HgfsPackSearchReadReplyRecord(info->requestType,
                                         &entry,
                                         bytesRemaining,
                                         lastSearchReadRecord,
                                         currentSearchReadRecord,
                                         &bytesWritten)) {
         /*
          * The current entry is too large to be contained in the reply.
          * If this is the first entry returned then we have an error.
          * Otherwise, we return success for what is already in the reply.
          */
         if (0 == info->numberRecordsWritten) {
            status = HGFS_ERROR_INTERNAL;
         }
         moreEntries = FALSE;
      }

      if (NULL != entry.name) {
         free(entry.name);
      }

      if (HGFS_ERROR_SUCCESS != status) {
         /* Failed to pack any entry records, bail. */
         break;
      }

      /*
       * Only count records actually written to the reply.
       * (The final, empty record is not written for all protocol versions.)
       */
      if (0 < bytesWritten) {

         if (0 != (info->flags & HGFS_SEARCH_READ_SINGLE_ENTRY)) {
            moreEntries = FALSE;
         }

         *replyDirentSize = ROUNDUP(*replyDirentSize, sizeof (uint64)) + bytesWritten;
         lastSearchReadRecord = currentSearchReadRecord;
         info->currentIndex++;
         info->numberRecordsWritten++;
      }
   }

   /* Now pack the search read reply common reply part. */
   if (HgfsPackSearchReadReplyHeader(info,
                                     &bytesWritten)) {
      /* The search read reply common reply part size was already done so should be 0. */
      *replyHeaderSize = bytesWritten;
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSearchRead --
 *
 *    Handle a "Search Read" request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSearchRead(HgfsInputParam *input)  // IN: Input params
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   HgfsNameStatus nameStatus;
   HgfsHandle hgfsSearchHandle;
   HgfsSearch search;
   HgfsShareOptions configOptions = 0;
   size_t replyInfoSize = 0;
   size_t replyDirentSize = 0;
   size_t replyPayloadSize = 0;
   size_t inlineDataSize = 0;
   size_t baseReplySize;
   HgfsSearchReadInfo info;

   HGFS_ASSERT_INPUT(input);

   memset(&info, 0, sizeof info);

   /*
    * For search read V4 we use the whole packet buffer available to pack
    * as many replies as can fit into that size. For all previous versions
    * only one record is going to be returned, so we allow the old packet
    * max for the reply.
    */
   if (HgfsUnpackSearchReadRequest(input->payload, input->payloadSize, input->op,
                                   &info, &baseReplySize, &inlineDataSize,
                                   &hgfsSearchHandle)) {

      LOG(4, "%s: read search #%u, offset %u\n", __FUNCTION__,
          hgfsSearchHandle, info.startIndex);

      info.reply = HgfsAllocInitReply(input->packet, input->request,
                                      baseReplySize + inlineDataSize,
                                      input->session);

      if (inlineDataSize == 0) {
         info.replyPayload = HSPU_GetDataPacketBuf(input->packet, BUF_WRITEABLE,
                                                   input->transportSession->channelCbTable);
      } else {
         info.replyPayload = (char *)info.reply + baseReplySize;
      }

      if (info.replyPayload == NULL) {
         LOG(4, "%s: Op %d reply buffer failure\n", __FUNCTION__, input->op);
         status = HGFS_ERROR_PROTOCOL;
      } else {

         if (HgfsGetSearchCopy(hgfsSearchHandle, input->session, &search)) {
            /* Get the config options. */
            if (search.utf8ShareNameLen != 0) {
               nameStatus = HgfsServerPolicy_GetShareOptions(search.utf8ShareName,
                                                             search.utf8ShareNameLen,
                                                             &configOptions);
               if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
                  LOG(4, "%s: no matching share: %s.\n", __FUNCTION__,
                      search.utf8ShareName);
                  status = HGFS_ERROR_FILE_NOT_FOUND;
               }
            } else if (0 == info.startIndex) {
               Bool readAllEntries = FALSE;

               /*
                * Reading the first entry, we check if this is a second scan
                * of the directory. If so, in some cases we restart the scan
                * by refreshing the entries first.
                 */
               if (!HgfsSearchHasReadAllEntries(hgfsSearchHandle,
                                                input->session,
                                                &readAllEntries)) {
                  status = HGFS_ERROR_INTERNAL;
               }

               if (readAllEntries) {
                  /*
                   * XXX - a hack that is now required until Fusion 5.0 end
                   * of lifes see bug 710697.
                   * The coder modified the server instead of the OS X client
                   * for the shares directory refresh needed by OS X clients in
                   * order to work around handles remaining open by Finder.
                   * This was fixed CLN 1988575 in the OS X client for 5.0.2.
                   * However, Fusion 4.0 and Fusion 5.0 tools will rely on this hack.
                   * At least now it works correctly without breaking everything
                   * else.
                   */
                  status = HgfsPlatformRestartSearchDir(hgfsSearchHandle,
                                                        input->session,
                                                        search.type);
               }
            }

            if (HGFS_ERROR_SUCCESS == status) {
               status = HgfsDoSearchRead(hgfsSearchHandle,
                                          &search,
                                          configOptions,
                                          input->session,
                                          &info,
                                          &replyInfoSize,
                                          &replyDirentSize);
            }

            if (HGFS_ERROR_SUCCESS == status) {
               replyPayloadSize = replyInfoSize +
                                    ((inlineDataSize == 0) ? 0 : replyDirentSize);
               if (0 == inlineDataSize) {
                  HSPU_SetDataPacketSize(input->packet, replyDirentSize);
               }
            }

            free(search.utf8Dir);
            free(search.utf8ShareName);

         } else {
            LOG(4, "%s: handle %u is invalid\n", __FUNCTION__,
                hgfsSearchHandle);
            status = HGFS_ERROR_INVALID_HANDLE;
         }
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerCreateSession --
 *
 *    Handle a "Create session" request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerCreateSession(HgfsInputParam *input)  // IN: Input params
{
   size_t replyPayloadSize = 0;
   HgfsCreateSessionInfo info;
   HgfsInternalStatus status;

   HGFS_ASSERT_INPUT(input);

   if (HgfsUnpackCreateSessionRequest(input->payload, input->payloadSize,
                                      input->op, &info)) {
      HgfsSessionInfo *session;
      LOG(4, "%s: create session\n", __FUNCTION__);

      if (!HgfsServerAllocateSession(input->transportSession,
                                     info,
                                     &session)) {
         status = HGFS_ERROR_NOT_ENOUGH_MEMORY;
         goto quit;
      } else {
         status = HgfsServerTransportAddSessionToList(input->transportSession,
                                                      session);
         if (HGFS_ERROR_SUCCESS != status) {
            LOG(4, "%s: Could not add session to the list.\n", __FUNCTION__);
            HgfsServerSessionPut(session);
            goto quit;
         }
      }

      if (HgfsPackCreateSessionReply(input->packet, input->request,
                                     &replyPayloadSize, session)) {
         status = HGFS_ERROR_SUCCESS;
      } else {
         status = HGFS_ERROR_INTERNAL;
      }
   } else {
      status = HGFS_ERROR_PROTOCOL;
   }

quit:
   HgfsServerCompleteRequest(status, replyPayloadSize, input);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerDestroySession --
 *
 *    Handle a "Destroy session" request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerDestroySession(HgfsInputParam *input)  // IN: Input params
{
   HgfsTransportSessionInfo *transportSession;
   HgfsSessionInfo *session;
   size_t replyPayloadSize = 0;
   HgfsInternalStatus status;

   HGFS_ASSERT_INPUT(input);

   transportSession = input->transportSession;
   session = input->session;

   session->state = HGFS_SESSION_STATE_CLOSED;

   if (session->sessionId == transportSession->defaultSessionId) {
      transportSession->defaultSessionId = HGFS_INVALID_SESSION_ID;
   }

   if (0 != (gHgfsCfgSettings.flags & HGFS_CONFIG_OPLOCK_MONITOR_ENABLED)) {
      HgfsCache_Destroy(session->symlinkCache);
      session->symlinkCache = NULL;
      HgfsCache_Destroy(session->fileAttrCache);
      session->fileAttrCache = NULL;
   }

   /*
    * Remove the session from the list. By doing that, the refcount of
    * the session will be decremented. Later, we will be invoking
    * HgfsServerCompleteRequest which will decrement the session's
    * refcount and cleanup the session
    */
   MXUser_AcquireExclLock(transportSession->sessionArrayLock);
   HgfsServerTransportRemoveSessionFromList(transportSession, session);
   MXUser_ReleaseExclLock(transportSession->sessionArrayLock);
   if (HgfsPackDestroySessionReply(input->packet,
                                   input->request,
                                   &replyPayloadSize,
                                   session)) {
      status = HGFS_ERROR_SUCCESS;
   } else {
      status = HGFS_ERROR_INTERNAL;
   }
   HgfsServerCompleteRequest(status, replyPayloadSize, input);
   HgfsServerSessionPut(session);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetTargetRelativePath --
 *
 *    Generates relative file path which need to be used a symbolic link
 *    target which would generate target name defined in "target" if the path
 *    to symbolic link file defined in the "source".
 *    Both source and target parameters represent absolute paths.
 *
 * Results:
 *    Allocated path that caller must free.
 *    NULL if there is a low memory condition.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

char*
HgfsServerGetTargetRelativePath(const char* source,    // IN: source file name
                                const char* target)    // IN: target file name
{
   const char *relativeSource = source;
   const char *relativeTarget = target;
   const char* sourceSep;
   const char* targetSep;
   int level = 0;
   size_t targetSize;
   char *result;
   char *currentPosition;

   /*
    * First remove the part of the path which is common between source and
    * target
    */

   while (*relativeSource != '\0' && *relativeTarget != '\0') {
      sourceSep = strchr(relativeSource, DIRSEPC);
      targetSep = strchr(relativeTarget, DIRSEPC);
      if (sourceSep == NULL || targetSep == NULL) {
         break;
      }
      if ((sourceSep - relativeSource) != (targetSep - relativeTarget)) {
         break;
      }
      if (strncmp(relativeSource, relativeTarget,
                  (targetSep - relativeTarget)) != 0) {
         break;
      }
      relativeSource = sourceSep + 1;
      relativeTarget = targetSep + 1;
   };

   /*
    * Find out how many directories deep the source file is from the common
    * part of the  path.
    */
   while(*relativeSource != '\0') {
      sourceSep = strchr(relativeSource, DIRSEPC);
      if (sourceSep != NULL) {
         /* Several consecutive separators mean only one level. */
         while (*sourceSep == DIRSEPC) {
            sourceSep++;
         }
         if (*sourceSep != '\0') {
            level++;
            relativeSource = sourceSep;
         } else {
            break;
         }
      } else {
         break;
      }
   }

   /*
    * Consruct relative path by adding level number of "../"
    * to the relative target path.
    */

   targetSize = level * HGFS_PARENT_DIR_LEN + strlen(relativeTarget) + sizeof '\0';
   result = malloc(targetSize);
   currentPosition = result;
   if (result != NULL) {
      while (level != 0) {
         memcpy(currentPosition, HGFS_PARENT_DIR, HGFS_PARENT_DIR_LEN);
         level--;
         currentPosition += HGFS_PARENT_DIR_LEN;
      }
      memcpy(currentPosition, relativeTarget, strlen(relativeTarget) + sizeof '\0');
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerNotifyReceiveEventCb --
 *
 *    The callback is invoked by the file system change notification component
 *    in response to a change event when the client has set at least one watch
 *    on a directory.
 *
 *    The function builds directory notification packet and queues it to be sent
 *    to the client. It processes one notification at a time. Any consolidation of
 *    packets is expected to occur at the transport layer.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerNotifyReceiveEventCb(HgfsSharedFolderHandle sharedFolder, // IN: shared folder
                               HgfsSubscriberHandle subscriber,     // IN: subsciber
                               char* fileName,                      // IN: name of the file
                               uint32 mask,                         // IN: event type
                               struct HgfsSessionInfo *session)     // IN: session info
{
   HgfsPacket *packet = NULL;
   HgfsHeader *packetHeader = NULL;
   char *shareName = NULL;
   size_t shareNameLen;
   size_t sizeNeeded;
   uint32 notifyFlags;

   LOG(4, "%s: Entered shr hnd %u hnd %"FMT64"x file %s mask %u\n",
         __FUNCTION__, sharedFolder, subscriber, fileName, mask);

   if (session->state == HGFS_SESSION_STATE_CLOSED) {
      LOG(4, "%s: session has been closed drop the notification %"FMT64"x\n",
          __FUNCTION__, session->sessionId);
      goto exit;
   }

   if (!HgfsServerGetShareName(sharedFolder, &shareNameLen, &shareName)) {
      LOG(4, "%s: failed to find shared folder for a handle %x\n",
          __FUNCTION__, sharedFolder);
      goto exit;
   }

   sizeNeeded = HgfsPackCalculateNotificationSize(shareName, fileName);

   /*
    * Use a single buffer zero'd out, as the packet and metapacket have the same
    * lifespan which is ended at the send complete callback (HgfsServerSessionSendComplete).
    */
   packet = Util_SafeCalloc(1, sizeof *packet + sizeNeeded);
   packetHeader = (HgfsHeader *)((char *)packet + sizeof *packet);
   /*
    * The buffer is zero'd out, so no need to set the following explicitly:
    * packet->metaPacketIsAllocated = FALSE;
    * packet->state &= ~HGFS_STATE_CLIENT_REQUEST;
    */
   packet->metaPacketSize = sizeNeeded;
   packet->metaPacketDataSize = packet->metaPacketSize;
   packet->metaPacket = packetHeader;
   notifyFlags = 0;
   if (mask & HGFS_NOTIFY_EVENTS_DROPPED) {
      notifyFlags |= HGFS_NOTIFY_FLAG_OVERFLOW;
   }

   if (!HgfsPackChangeNotificationRequest(packetHeader, subscriber, shareName, fileName, mask,
                                          notifyFlags, session, &sizeNeeded)) {
      LOG(4, "%s: failed to pack notification request\n", __FUNCTION__);
      goto exit;
   }

   if (!HgfsPacketSend(packet,
                       session->transportSession,
                       session,
                       0)) {
      LOG(4, "%s: failed to send notification to the host\n", __FUNCTION__);
      goto exit;
   }

   /* The transport will call the server send complete callback to release the packets. */
   packet = NULL;

   LOG(4, "%s: Sent notify for: %u index: %"FMT64"u file name %s mask %x\n",
       __FUNCTION__, sharedFolder, subscriber, fileName, mask);

exit:
   if (shareName) {
      free(shareName);
   }
   if (packet) {
      free(packet);
   }
}


/*
 * more testing
 */
#if 0
void
TestNodeFreeList(void)
{
   HgfsHandle array[10 * NUM_FILE_NODES];
   HgfsFileNode *node;
   unsigned int i;

   printf("%s: begin >>>>>>>>>>>>>>>>>>>>>>>>>>>\n", __FUNCTION__);

   for (i = 0; i < sizeof array / sizeof array[0]; i++) {
      char tempName[20];
      HgfsLocalId localId;

      Str_Sprintf(tempName, sizeof tempName, "name%u", i);
      printf("\nadding node with name: %s\n", tempName);
      localId.volumeId = 0;
      localId.fileId = i + 1000;
      node = HgfsAddNewFileNode(Util_SafeStrdup(tempName), &localId);
      array[i] = HgfsFileNode2Handle(node);
   }

   HgfsDumpAllNodes();

   printf("done getting nodes, now freeing\n");

   for (i = 0; i < sizeof array / sizeof array[0]; i++) {
      printf("removing node #%u\n", i);
      HgfsRemoveFileNode(&nodeArray[array[i]]);
   }

   HgfsDumpAllNodes();
   printf("%s: end <<<<<<<<<<<<<<<<<<<<<<<<<< \n", __FUNCTION__);
}


void
TestSearchFreeList(void)
{
   HgfsHandle array[10 * NUM_SEARCHES];
   HgfsSearch *search;
   unsigned int i;

   printf("%s: begin >>>>>>>>>>>>>>>>>>>>>>>>>>>\n", __FUNCTION__);

   for (i = 0; i < sizeof array / sizeof array[0]; i++) {
      char tempName[20];

      Str_Sprintf(tempName, sizeof tempName, "baseDir%u", i);
      printf("\nadding search with baseDir: \"%s\"\n", tempName);
      search = HgfsAddNewSearch(Util_SafeStrdup(tempName));
      array[i] = HgfsSearch2SearchHandle(search);
   }

   HgfsDumpAllSearches();

   printf("done getting searches, now freeing\n");

   for (i = 0; i < sizeof array / sizeof array[0]; i++) {
      printf("removing search #%u\n", i);
      HgfsRemoveSearch(&searchArray[array[i]]);
   }

   HgfsDumpAllSearches();
   printf("%s: end <<<<<<<<<<<<<<<<<<<<<<<<<< \n", __FUNCTION__);
}
#endif
 07070100000081000081A4000000000000000000000001682255050000A0CE000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerInt.h /*********************************************************
 * Copyright (C) 1998-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


#ifndef __HGFS_SERVER_INT_H__
#   define __HGFS_SERVER_INT_H__

#include "vm_basic_types.h"

struct DirectoryEntry;

#ifndef _WIN32
   typedef int fileDesc;
#else
#  include <windows.h>
   typedef HANDLE fileDesc;
#endif

#include "dbllnklst.h"
#include "cpName.h"     // for HgfsNameStatus
#include "hgfsCache.h"
#include "hgfsProto.h"
#include "hgfsServer.h" // for the server public types
#include "hgfsServerPolicy.h"
#include "hgfsUtil.h"   // for HgfsInternalStatus
#include "userlock.h"
#include "vm_atomic.h"


#ifndef VMX86_TOOLS

#define LOGLEVEL_MODULE hgfsServer
#include "loglevel_user.h"

#else // VMX86_TOOLS

#undef DOLOG
#undef LOG

/*
 * Map all LOG statements to a Debug or g_debug tools log.
 * Set the level to a default log level of 10 so that we will
 * capture everything if tools logging is set to debug.
 *
 * Note, for future work would be to go through the log
 * statements and set the levels correctly so that we can
 * map to info, error and warnings.
*/
#define LGLEVEL         (10)
#define LGPFX_FMT       "%s:%s:"
#define LGPFX           "hgfsServer"

#if defined VMTOOLS_USE_GLIB
#define Debug                 g_debug
#define Warning               g_warning

#define G_LOG_DOMAIN    LGPFX

#include "vmware/tools/utils.h"
#include "vmware/tools/log.h"

#else // VMTOOLS_USE_GLIB

#include "debug.h"

#endif // VMTOOLS_USE_GLIB

#define DOLOG(_min)     ((_min) <= LGLEVEL)

/* gcc needs special syntax to handle zero-length variadic arguments */
#if defined(_MSC_VER)
#define LOG(_level, fmt, ...)                                     \
   do {                                                           \
      if (DOLOG(_level)) {                                        \
         Debug(LGPFX_FMT fmt, LGPFX , __FUNCTION__, __VA_ARGS__); \
      }                                                           \
   } while (0)
#else
#define LOG(_level, fmt, ...)                                      \
   do {                                                            \
      if (DOLOG(_level)) {                                         \
         Debug(LGPFX_FMT fmt, LGPFX, __FUNCTION__, ##__VA_ARGS__); \
      }                                                            \
   } while (0)
#endif

#endif // VNMX86_TOOLS

#define HGFS_DEBUG_ASYNC   (0)

typedef uintptr_t HOM_HANDLE;

typedef struct HgfsTransportSessionInfo HgfsTransportSessionInfo;

/* Identifier for a local file */
typedef struct HgfsLocalId {
   uint64 volumeId;
   uint64 fileId;
} HgfsLocalId;

typedef enum {
   REQ_ASYNC,    /* Hint that request should be processed Async. */
   REQ_SYNC,     /*               "                       Sync.  */
} RequestHint;


/* Three possible filenode states */
typedef enum {
   FILENODE_STATE_UNUSED,              /* Linked on the free list */
   FILENODE_STATE_IN_USE_CACHED,       /* Linked on the cached nodes list */
   FILENODE_STATE_IN_USE_NOT_CACHED,   /* Not linked on any list */
} FileNodeState;

/* Three possible search types */
typedef enum {
   DIRECTORY_SEARCH_TYPE_DIR,       /* Objects are files and subdirectories */
   DIRECTORY_SEARCH_TYPE_BASE,      /* Objects are shares */
   DIRECTORY_SEARCH_TYPE_OTHER,     /* Objects are the contents of
                                       "root/drive" or contents of "root" */
} DirectorySearchType;

#define HGFS_SEARCH_LAST_ENTRY_INDEX         ((uint32)~((uint32)0))


/* Two possible volume info type */
typedef enum {
   VOLUME_INFO_TYPE_MIN,
   VOLUME_INFO_TYPE_MAX,
} VolumeInfoType;

/*
 * The "default" share access is used in cross-platform code, so it's helpful
 * to have a single macro for accessing it.
 */
#ifdef _WIN32
#  define HGFS_DEFAULT_SHARE_ACCESS (FILE_SHARE_READ | FILE_SHARE_WRITE | \
                                     FILE_SHARE_DELETE)
#else
#  define HGFS_DEFAULT_SHARE_ACCESS 0
#endif // _WIN32

typedef struct HgfsShareInfo {
   /* Filename of the root directory for the shared folder */
   const char *rootDir;

   /* Length of the root directory filename (does not include nul terminator) */
   size_t rootDirLen;

   /* Read permissions for the shared folder, needed for handle => name conversions. */
   Bool readPermissions;

   /* Write permissions for the shared folder, needed for handle => name conversions. */
   Bool writePermissions;

   /*
    *  Shared folder handle used by change directory notification code to identify
    *  shared folder.
    */
   HgfsSharedFolderHandle handle;
} HgfsShareInfo;

/*
 * This struct represents a file on the local filesystem that has been
 * opened by a remote client. We store the name of the local file and
 * enough state to keep track of whether the file has changed locally
 * between remote accesses. None of the fields contain cross-platform
 * types; everything has been converted for the local filesystem.
 *
 * A file node object can only be in 1 of these 3 states:
 * 1) FILENODE_STATE_UNUSED: linked on the free list
 * 2) FILENODE_STATE_IN_USE_CACHED: Linked on the cached nodes list
 * 3) FILENODE_STATE_IN_USE_NOT_CACHED: Linked on neither of the above two lists.
 */
typedef struct HgfsFileNode {
   /* Links to place the object on various lists */
   DblLnkLst_Links links;

   /* HGFS handle uniquely identifying this node. */
   HgfsHandle handle;

   /* Local filename (in UTF8) */
   char *utf8Name;

   /* Length of filename (does not include nul terminator) */
   size_t utf8NameLen;

   /* share name */
   char *shareName;

   /* Length of share name (does not include nul terminator) */
   size_t shareNameLen;

   /* ID of file in local filesystem */
   HgfsLocalId localId;

   /* File descriptor */
   fileDesc fileDesc;

   /* On POSIX, access mode. On Windows, desired access */
   uint32 mode;

   /* Share access to open with (Windows only) */
   uint32 shareAccess;

   /* The server lock that the node currently has. */
   HgfsLockType serverLock;

   /* File node state on lists */
   FileNodeState state;

   /* File flags - see below. */
   uint32 flags;

   /*
    * Context as required by some file operations. Eg: BackupWrite on
    * Windows: BackupWrite requires the caller to hold on to a pointer
    * to a Windows internal data structure between subsequent calls to
    * BackupWrite while restoring a file.
    */
   void *fileCtx;

   /* Parameters associated with the share. */
   HgfsShareInfo shareInfo;
} HgfsFileNode;


/* HgfsFileNode flags. */

/* TRUE if opened in append mode */
#define HGFS_FILE_NODE_APPEND_FL               (1 << 0)
/* Whether this file was opened in sequential mode. */
#define HGFS_FILE_NODE_SEQUENTIAL_FL           (1 << 1)
/* Whether this a shared folder open. */
#define HGFS_FILE_NODE_SHARED_FOLDER_OPEN_FL   (1 << 2)

/*
 * This struct represents a file search that a client initiated.
 *
 * A search object can only be in 1 of these 2 states:
 * 1) Unused: linked on the free list
 * 2) In use: unlinked
 */
typedef struct HgfsSearch {
   /* Links to place the object on various lists */
   DblLnkLst_Links links;

   /* Flags to track state and information: see below. */
   uint32 flags;

   /* HGFS handle uniquely identifying this search. */
   HgfsHandle handle;

   /* Local directory name (in UTF8) */
   char *utf8Dir;

   /* Length of directory name (does not include nul terminator) */
   size_t utf8DirLen;

   /* Share name. */
   char *utf8ShareName;

   /* Share name length. */
   size_t utf8ShareNameLen;

   /* Directory entries for this search */
   struct DirectoryEntry **dents;

   /* Number of dents */
   uint32 numDents;

   /*
    * What type of search is this (what objects does it track)? This is
    * important to know so we can do the right kind of stat operation later
    * when we want to retrieve the attributes for each dent.
    */
   DirectorySearchType type;

   /* Parameters associated with the share. */
   HgfsShareInfo shareInfo;
} HgfsSearch;

/* HgfsSearch flags. */

/* TRUE if opened in append mode */
#define HGFS_SEARCH_FLAG_READ_ALL_ENTRIES      (1 << 0)

/* HgfsSessionInfo flags. */
typedef enum {
   HGFS_SESSION_TYPE_REGULAR,      /* Dynamic session, created by the HgfsTransport. */
   HGFS_SESSION_TYPE_INTERNAL,     /* This is a static session. */
} HgfsSessionInfoType;

/* HgfsSessionState, used for session status. */
typedef enum {
   HGFS_SESSION_STATE_OPEN,
   HGFS_SESSION_STATE_CLOSED,
} HgfsSessionInfoState;

typedef struct HgfsAsyncRequestInfo {
   /* Asynchronous request handling. */
   Atomic_uint32   requestCount;
   MXUserExclLock *lock;
   MXUserCondVar  *requestCountIsZero;
} HgfsAsyncRequestInfo;

typedef struct HgfsSessionInfo {

   DblLnkLst_Links links;

   Bool isInactive;

   /* The sessions state and capabilities. */
   HgfsSessionFlags flags;

   /* Unique session id. */
   uint64 sessionId;

   /* Max packet size that is supported by both client and server. */
   uint32 maxPacketSize;

   /* Transport session context. */
   HgfsTransportSessionInfo *transportSession;

   /* Current state of the session. */
   HgfsSessionInfoState state;

   /* Lock to ensure some fileIO requests are atomic for a handle. */
   MXUserExclLock *fileIOLock;

   int numInvalidationAttempts;

   Atomic_uint32 refCount;    /* Reference count for session. */

   /*
    ** START NODE ARRAY **************************************************
    *
    * Lock for the following 6 fields: the node array,
    * counters and lists for this session.
    */
   MXUserExclLock *nodeArrayLock;

   /* Open file nodes of this session. */
   HgfsFileNode *nodeArray;

   /* Number of nodes in the nodeArray. */
   uint32 numNodes;

   /* Free list of file nodes. LIFO to be cache-friendly. */
   DblLnkLst_Links nodeFreeList;

   /* List of cached open nodes. */
   DblLnkLst_Links nodeCachedList;

   /* Current number of open nodes. */
   unsigned int numCachedOpenNodes;

   /* Number of open nodes having server locks. */
   unsigned int numCachedLockedNodes;
   /** END NODE ARRAY ****************************************************/

   /*
    ** START SEARCH ARRAY ************************************************
    *
    * Lock for the following three fields: for the search array
    * and it's counter and list, for this session.
    */
   MXUserExclLock *searchArrayLock;

   /* Directory entry cache for this session. */
   HgfsSearch *searchArray;

   /* Number of entries in searchArray. */
   uint32 numSearches;

   /* Free list of searches. LIFO. */
   DblLnkLst_Links searchFreeList;
   /** END SEARCH ARRAY ****************************************************/

   /* Array of session specific capabiities. */
   HgfsOpCapability hgfsSessionCapabilities[HGFS_OP_MAX];

   uint32 numberOfCapabilities;

   /* Asynchronous request handling. */
   HgfsAsyncRequestInfo asyncRequestsInfo;

   /* Cache for symlink check status. */
   HgfsCache *symlinkCache;

   /* Cache for file attributes. */
   HgfsCache *fileAttrCache;
} HgfsSessionInfo;

/*
 * This represents the maximum number of HGFS sessions that can be
 * created in a HGFS transport session. We picked a random value
 * for this variable. There is no specific reason behind picking
 * this value.
 */
#define MAX_SESSION_COUNT 1024

/*
 * This represents the maximum number attempts made by the HGFS
 * invalidator before completely destroying the HGFS session. We
 * picked a random value and there is no specific reason behind
 * the value 4 for thie variable.
 */
#define MAX_SESSION_INVALIDATION_ATTEMPTS 4

/*
 * These structs represent information about file open requests, file
 * attributes, and directory creation requests.
 *
 * The main reason for these structs is data abstraction -- we pass
 * a struct around instead of the individual parameters. This way
 * as more parameters are implemented, we don't have to add more
 * parameters to the functions, instead just extend the structs.
 */

typedef struct HgfsFileOpenInfo {
   HgfsOp requestType;
   HgfsHandle file;                  /* Opaque file ID used by the server */
   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 */
   HgfsLockType desiredLock;         /* The type of lock desired by the client */
   HgfsLockType acquiredLock;        /* The type of lock acquired by the server */
   uint32 cpNameSize;
   const char *cpName;
   char *utf8Name;
   uint32 caseFlags;                 /* Case-sensitivity flags. */
   HgfsShareInfo shareInfo;          /* Parameters associated with the share. */
} HgfsFileOpenInfo;

typedef struct HgfsFileAttrInfo {
   HgfsOp requestType;
   HgfsAttrValid mask;
   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. 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 */
   uint32 volumeId;              /* Volume Id of the volune on which the file resides */
   uint32 effectivePerms;        /* Permissions in effect for the current user */
   uint32 eaSize;                /* Extended attribute data size */
   uint32 reparseTag;            /* Windows reparse point tag, valid by attr flag */
   HgfsShortFileName shortName;  /* Windows DOS 8 dot 3 name for long names */
} HgfsFileAttrInfo;

typedef struct HgfsSearchReadEntry {
   HgfsSearchReadMask mask;      /* Info returned mask */
   HgfsFileAttrInfo attr;        /* Attributes of entry */
   uint32 fileIndex;             /* Entry directory index */
   char *name;                   /* Name */
   uint32 nameLength;            /* Name byte length */
} HgfsSearchReadEntry;

typedef struct HgfsSearchReadInfo {
   HgfsOp requestType;           /* HGFS request version */
   HgfsSearchReadMask requestedMask; /* Entry info requested mask */
   HgfsSearchReadFlags flags;    /* Request specific flags */
   HgfsSearchReadFlags replyFlags;   /* Reply specific flags */
   char *searchPattern;          /* Search pattern to match entries with */
   uint32 searchPatternLength;   /* Byte length of search pattern */
   uint32 startIndex;            /* Starting index for entries */
   uint32 currentIndex;          /* Current index for entries */
   uint32 numberRecordsWritten;  /* Number of entries written */
   void *reply;                  /* Fixed part of search read reply */
   void *replyPayload;           /* Variable part (dirent records) of reply */
   size_t payloadSize;           /* Remaining bytes in reply payload. */
} HgfsSearchReadInfo;

typedef struct HgfsCreateDirInfo {
   HgfsOp requestType;
   HgfsCreateDirValid mask;
   HgfsPermissions specialPerms; /* Special permissions bits. Ignored by Windows */
   HgfsPermissions ownerPerms;   /* Owner permissions bits */
   HgfsPermissions groupPerms;   /* Group permissions bits. Ignored by Windows */
   HgfsPermissions otherPerms;   /* Other permissions bits. Ignored by Windows */
   uint32 cpNameSize;
   const char *cpName;
   uint32 caseFlags;             /* Case-sensitivity flags. */
   HgfsAttrFlags fileAttr;       /* Various flags and Windows 'attributes' */
} HgfsCreateDirInfo;

typedef struct HgfsCreateSessionInfo {
   uint32 maxPacketSize;
   HgfsSessionFlags flags;       /* Session capability flags. */
} HgfsCreateSessionInfo;

typedef struct HgfsSymlinkCacheEntry {
   HOM_HANDLE handle;            /* File handle. */
   HgfsNameStatus nameStatus;    /* Symlink check status. */
} HgfsSymlinkCacheEntry;

typedef struct HgfsFileAttrCacheEntry {
   HOM_HANDLE handle;            /* File handle. */
   HgfsFileAttrInfo attr;        /* Attributes of entry. */
} HgfsFileAttrCacheEntry;

Bool
HgfsCreateAndCacheFileNode(HgfsFileOpenInfo *openInfo, // IN: Open info struct
                           HgfsLocalId const *localId, // IN: Local unique file ID
                           fileDesc fileDesc,          // IN: OS file handle
                           Bool append,                // IN: Open with append flag
                           HgfsSessionInfo *session);  // IN: Session info

Bool
HgfsSearchHandle2FileName(HgfsHandle handle,       // IN: Hgfs search handle
                          char **fileName,         // OUT: cp file name
                          uint32 *fileNameSize);   // OUT: cp file name size

void
HgfsUpdateNodeNames(const char *oldLocalName,  // IN: Name of file to look for
                    const char *newLocalName,  // IN: Name to replace with
                    HgfsSessionInfo *session); // IN: Session info

Bool
HgfsRemoveSearch(HgfsHandle searchHandle,
                 HgfsSessionInfo *session);

#ifdef VMX86_LOG
#define HGFS_SERVER_DIR_DUMP_DENTS(_searchHandle, _session) do {    \
      if (DOLOG(4)) {                                               \
         HgfsServerDirDumpDents(_searchHandle, _session);           \
      }                                                             \
   } while (0)

void
HgfsServerDirDumpDents(HgfsHandle searchHandle,   // IN: Handle to dump dents from
                       HgfsSessionInfo *session); // IN: Session info
#else
#define HGFS_SERVER_DIR_DUMP_DENTS(_searchHandle, _session) do {} while (0)
#endif


struct DirectoryEntry *
HgfsGetSearchResult(HgfsHandle handle,        // IN: Handle to search
                    HgfsSessionInfo *session, // IN: Session info
                    uint32 offset,            // IN: Offset to retrieve at
                    Bool remove);             // IN: If true, removes the result

Bool
HgfsServerStatFs(const char *pathName, // IN: Path we're interested in
                 size_t pathLength,    // IN: Length of path
                 uint64 *freeBytes,    // OUT: Free bytes on volume
                 uint64 *totalBytes);  // OUT: Total bytes on volume

HgfsNameStatus
HgfsServerGetAccess(char *in,                    // IN:  CP filename to check
                    size_t inSize,               // IN:  Size of name in
                    HgfsOpenMode mode,           // IN:  Requested access mode
                    uint32 caseFlags,            // IN:  Case-sensitivity flags
                    char **bufOut,               // OUT: File name in local fs
                    size_t *outLen);             // OUT: Length of name out

Bool
HgfsServerIsSharedFolderOnly(char const *in,  // IN:  CP filename to check
                             size_t inSize);  // IN:  Size of name in

void *
HgfsServerResEnumInit(void);

Bool
HgfsServerResEnumGet(void *enumState,
                     char const **enumResName,
                     size_t *enumResNameLen,
                     Bool *enumResDone);

Bool
HgfsServerResEnumExit(void *enumState);

HgfsInternalStatus
HgfsServerGetDirEntry(HgfsHandle handle,                // IN: Handle to search
                      HgfsSessionInfo *session,         // IN: Session info
                      uint32 index,                     // IN: index to retrieve at
                      Bool remove,                      // IN: If true, removes the result
                      struct DirectoryEntry **dirEntry);// OUT: directory entry

HgfsInternalStatus
HgfsServerSearchRealDir(char const *baseDir,      // IN: Directory to search
                        size_t baseDirLen,        // IN: Length of directory
                        char const *shareName,    // IN: Share name
                        char const *rootDir,      // IN: Root directory for the share
                        HgfsSessionInfo *session, // IN: Session info
                        HgfsHandle *handle);      // OUT: Search handle

HgfsInternalStatus
HgfsServerSearchVirtualDir(HgfsServerResEnumGetFunc getName,      // IN: Name enumerator
                           HgfsServerResEnumInitFunc initName,    // IN: Init function
                           HgfsServerResEnumExitFunc cleanupName, // IN: Cleanup function
                           DirectorySearchType type,              // IN: Kind of search
                           HgfsSessionInfo *session,              // IN: Session info
                           HgfsHandle *handle);                   // OUT: Search handle

HgfsInternalStatus
HgfsServerRestartSearchVirtualDir(HgfsServerResEnumGetFunc getName,      // IN: Name enumerator
                                  HgfsServerResEnumInitFunc initName,    // IN: Init function
                                  HgfsServerResEnumExitFunc cleanupName, // IN: Cleanup function
                                  HgfsSessionInfo *session,              // IN: Session info
                                  HgfsHandle searchHandle);              // IN: search to restart


void *
HgfsAllocInitReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                   const void *packetHeader,     // IN: incoming packet header
                   size_t replyDataSize,         // IN: payload size
                   HgfsSessionInfo *session);    // IN: Session Info

/* Node cache functions. */

Bool
HgfsRemoveFromCache(HgfsHandle handle,         // IN: Hgfs handle of the node
                    HgfsSessionInfo *session); // IN: Session info

Bool
HgfsAddToCache(HgfsHandle handle,         // IN: Hgfs handle of the node
               HgfsSessionInfo *session); // IN: Session info

Bool
HgfsIsCached(HgfsHandle handle,         // IN: Hgfs handle of the node
             HgfsSessionInfo *session); // IN: Session info

Bool
HgfsIsServerLockAllowed(HgfsSessionInfo *session);  // IN: session info

Bool
HgfsHandle2FileDesc(HgfsHandle handle,        // IN: Hgfs file handle
                    HgfsSessionInfo *session, // IN: session info
                    fileDesc *fd,             // OUT: OS handle (file descriptor)
                    void **fileCtx);          // OUT: OS file context

Bool
HgfsFileDesc2Handle(fileDesc fd,              // IN: OS handle (file descriptor)
                    HgfsSessionInfo *session, // IN: session info
                    HgfsHandle *handle);      // OUT: Hgfs file handle

Bool
HgfsHandle2ShareMode(HgfsHandle handle,         // IN: Hgfs file handle
                     HgfsSessionInfo *session,  // IN: session info
                     HgfsOpenMode *shareMode);  // OUT: UTF8 file name size

Bool
HgfsHandle2FileName(HgfsHandle handle,        // IN: Hgfs file handle
                    HgfsSessionInfo *session, // IN: session info
                    char **fileName,          // OUT: CP file name
                    size_t *fileNameSize);    // OUT: CP file name size
Bool
HgfsHandle2FileNameMode(HgfsHandle handle,       // IN: Hgfs file handle
                        HgfsSessionInfo *session,// IN: Session info
                        Bool *readPermissions,   // OUT: shared folder permissions
                        Bool *writePermissions,  // OUT: shared folder permissions
                        char **fileName,         // OUT: UTF8 file name
                        size_t *fileNameSize);   // OUT: UTF8 file name size
Bool
HgfsHandle2AppendFlag(HgfsHandle handle,        // IN: Hgfs file handle
                      HgfsSessionInfo *session, // IN: session info
                      Bool *appendFlag);        // OUT: Append flag

Bool
HgfsHandle2LocalId(HgfsHandle handle,        // IN: Hgfs file handle
                   HgfsSessionInfo *session, // IN: session info
                   HgfsLocalId *localId);    // OUT: Local id info


Bool
HgfsUpdateNodeFileDesc(HgfsHandle handle,        // IN: Hgfs file handle
                       HgfsSessionInfo *session, // IN: session info
                       fileDesc fd,              // IN: OS handle (file desc
                       void *fileCtx);           // IN: OS file context

Bool
HgfsUpdateNodeServerLock(fileDesc fd,                // IN: OS handle
                         HgfsSessionInfo *session,   // IN: session info
                         HgfsLockType serverLock);   // IN: new oplock

Bool
HgfsUpdateNodeAppendFlag(HgfsHandle handle,        // IN: Hgfs file handle
                         HgfsSessionInfo *session, // IN: session info
                         Bool appendFlag);         // OUT: Append flag

Bool
HgfsGetNodeCopy(HgfsHandle handle,        // IN: Hgfs file handle
                HgfsSessionInfo *session, // IN: session info
                Bool copyName,            // IN: Should we copy the name?
                HgfsFileNode *copy);      // IN/OUT: Copy of the node

Bool
HgfsHandleIsSequentialOpen(HgfsHandle handle,        // IN:  Hgfs file handle
                           HgfsSessionInfo *session, // IN: session info
                           Bool *sequentialOpen);    // OUT: If open was sequential

Bool
HgfsHandleIsSharedFolderOpen(HgfsHandle handle,        // IN:  Hgfs file handle
                             HgfsSessionInfo *session, // IN: session info
                             Bool *sharedFolderOpen);  // OUT: If shared folder

Bool
HgfsGetSearchCopy(HgfsHandle handle,        // IN: Hgfs search handle
                  HgfsSessionInfo *session, // IN: Session info
                  HgfsSearch *copy);        // IN/OUT: Copy of the search

Bool
HgfsServerGetOpenMode(HgfsFileOpenInfo *openInfo, // IN:  Open info to examine
                      uint32 *modeOut);           // OUT: Local mode


char*
HgfsServerGetTargetRelativePath(const char* source,    // IN: source file name
                                const char* target);   // IN: target file name

Bool
HgfsServerCheckOpenFlagsForShare(HgfsFileOpenInfo *openInfo, // IN: Hgfs file handle
                                 HgfsOpenFlags *flags);      // IN/OUT: open mode

/* Platform specific exports. */
Bool
HgfsPlatformInit(void);
void
HgfsPlatformDestroy(void);
HgfsInternalStatus
HgfsPlatformCloseFile(fileDesc fileDesc,            // IN: OS handle of the file
                      void *fileCtx);               // IN: file context
Bool
HgfsPlatformDoFilenameLookup(void);
HgfsNameStatus
HgfsPlatformFilenameLookup(const char *sharePath,             // IN: share path in question
                           size_t sharePathLength,            // IN
                           char *fileName,                    // IN: filename to be looked up
                           size_t fileNameLength,             // IN
                           uint32 caseFlags,                  // IN: case-sensitivity flags
                           char **convertedFileName,          // OUT: case-converted filename
                           size_t *convertedFileNameLength);  // OUT
HgfsInternalStatus
HgfsPlatformConvertFromNameStatus(HgfsNameStatus status);  // IN: name status
HgfsNameStatus
HgfsPlatformPathHasSymlink(const char *fileName,      // IN: fileName to be checked
                           size_t fileNameLength,     // IN
                           const char *sharePath,     // IN: share path in question
                           size_t sharePathLen);      // IN
HgfsInternalStatus
HgfsPlatformSymlinkCreate(char *localSymlinkName,   // IN: symbolic link file name
                          char *localTargetName);   // IN: symlink target name
HgfsInternalStatus
HgfsPlatformGetattrFromName(char *fileName,                 // IN: file name
                            HgfsShareOptions configOptions, // IN: configuration options
                            char *shareName,                // IN: share name
                            HgfsFileAttrInfo *attr,         // OUT: file attributes
                            char **targetName);             // OUT: Symlink target
HgfsInternalStatus
HgfsPlatformGetDirEntry(HgfsSearch *search,                // IN: search
                        HgfsSessionInfo *session,          // IN: Session info
                        uint32 offset,                     // IN: Offset to retrieve at
                        Bool remove,                       // IN: If true, removes the result
                        struct DirectoryEntry **dirEntry); // OUT: dirent
HgfsInternalStatus
HgfsPlatformSetDirEntry(HgfsSearch *search,              // IN: search
                        HgfsShareOptions configOptions,  // IN: share configuration settings
                        HgfsSessionInfo *session,        // IN: session info
                        struct DirectoryEntry *dirEntry, // IN: the indexed dirent
                        Bool getAttr,                    // IN: get the entry attributes
                        HgfsFileAttrInfo *entryAttr,     // OUT: entry attributes, optional
                        char **entryName,                // OUT: entry name
                        uint32 *entryNameLength);        // OUT: entry name length
HgfsInternalStatus
HgfsPlatformScandir(char const *baseDir,             // IN: Directory to search in
                    size_t baseDirLen,               // IN: Length of directory
                    Bool followSymlinks,             // IN: followSymlinks config option
                    struct DirectoryEntry ***dents,  // OUT: Array of DirectoryEntrys
                    int *numDents);                  // OUT: Number of DirectoryEntrys
HgfsInternalStatus
HgfsPlatformScanvdir(HgfsServerResEnumGetFunc enumNamesGet,   // IN: Function to get name
                     HgfsServerResEnumInitFunc enumNamesInit, // IN: Setup function
                     HgfsServerResEnumExitFunc enumNamesExit, // IN: Cleanup function
                     DirectorySearchType type,                // IN: Kind of search
                     struct DirectoryEntry ***dents,          // OUT: Array of DirectoryEntrys
                     uint32 *numDents);                       // OUT: total number of directory entrys
HgfsInternalStatus
HgfsPlatformSearchDir(HgfsNameStatus nameStatus,       // IN: name status
                      const char *dirName,             // IN: relative directory name
                      size_t dirNameLength,            // IN: length of dirName
                      uint32 caseFlags,                // IN: case flags
                      HgfsShareInfo *shareInfo,        // IN: sharfed folder information
                      char *baseDir,                   // IN: name of the shared directory
                      uint32 baseDirLen,               // IN: length of the baseDir
                      HgfsSessionInfo *session,        // IN: session info
                      HgfsHandle *handle);             // OUT: search handle
HgfsInternalStatus
HgfsPlatformRestartSearchDir(HgfsHandle handle,               // IN: search handle
                             HgfsSessionInfo *session,        // IN: session info
                             DirectorySearchType searchType); // IN: Kind of search
#ifdef VMX86_LOG
void
HgfsPlatformDirDumpDents(HgfsSearch *search);         // IN: search
#endif

HgfsInternalStatus
HgfsPlatformReadFile(fileDesc readFile,           // IN: file descriptor
                     HgfsSessionInfo *session,    // IN: session info
                     uint64 offset,               // IN: file offset to read from
                     uint32 requiredSize,         // IN: length of data to read
                     void* payload,               // OUT: buffer for the read data
                     uint32 *actualSize);         // OUT: actual length read
HgfsInternalStatus
HgfsPlatformWriteFile(fileDesc writeFile,          // IN: file descriptor
                      HgfsSessionInfo *session,    // IN: session info
                      uint64 writeOffset,          // IN: file offset to write to
                      uint32 writeDataSize,        // IN: length of data to write
                      HgfsWriteFlags writeFlags,   // IN: write flags
                      Bool writeSequential,        // IN: write is sequential
                      Bool writeAppend,            // IN: write is appended
                      const void *writeData,       // IN: data to be written
                      uint32 *writtenSize);        // OUT: byte length written
HgfsInternalStatus
HgfsPlatformWriteWin32Stream(HgfsHandle file,           // IN: packet header
                             char *dataToWrite,         // IN: data to write
                             size_t requiredSize,       // IN: data size
                             Bool doSecurity,           // IN: write ACL
                             uint32  *actualSize,       // OUT: written data size
                             HgfsSessionInfo *session); // IN: session info
HgfsInternalStatus
HgfsPlatformVDirStatsFs(HgfsSessionInfo *session,  // IN: session info
                        HgfsNameStatus nameStatus, // IN:
                        VolumeInfoType infoType,   // IN:
                        uint64 *outFreeBytes,      // OUT:
                        uint64 *outTotalBytes);    // OUT:

HgfsInternalStatus
HgfsPlatformGetFd(HgfsHandle hgfsHandle,    // IN:  HGFS file handle
                  HgfsSessionInfo *session, // IN:  Session info
                  Bool append,              // IN:  Open with append flag
                  fileDesc *fd);            // OUT: Opened file descriptor
HgfsInternalStatus
HgfsPlatformFileExists(char *utf8LocalName); // IN: Full file path utf8 encoding

/*
 * NOTE.
 * This function requires valid localSrcName and localTargetName even when
 * srcFile and targetFile are specified.
 * Depending on some various conditions it may fall back on using file names
 * instead of file handles.
 */
HgfsInternalStatus
HgfsPlatformRename(char *localSrcName,     // IN: local path to source file
                   fileDesc srcFile,       // IN: source file handle
                   char *localTargetName,  // IN: local path to target file
                   fileDesc targetFile,    // IN: target file handle
                   HgfsRenameHint hints);  // IN: rename hints
HgfsInternalStatus
HgfsPlatformCreateDir(HgfsCreateDirInfo *info,  // IN: direcotry properties
                      char *utf8Name);          // IN: full path for the new directory
HgfsInternalStatus
HgfsPlatformDeleteFileByHandle(HgfsHandle file,           // IN: file being deleted
                               HgfsSessionInfo *session); // IN: session info
HgfsInternalStatus
HgfsPlatformDeleteFileByName(char const *utf8Name); // IN: full file path in utf8 encoding
HgfsInternalStatus
HgfsPlatformDeleteDirByHandle(HgfsHandle dir,            // IN: directory being deleted
                              HgfsSessionInfo *session); // IN: session info
HgfsInternalStatus
HgfsPlatformDeleteDirByName(char const *utf8Name); // IN: full file path in utf8 encoding
HgfsInternalStatus
HgfsPlatformHandleIncompleteName(HgfsNameStatus nameStatus,  // IN: name status
                                 HgfsFileAttrInfo *attr);    // OUT: attributes
void
HgfsPlatformGetDefaultDirAttrs(HgfsFileAttrInfo *attr); // OUT: attributes
HgfsInternalStatus
HgfsPlatformGetattrFromFd(fileDesc fileDesc,        // IN: file descriptor to query
                          HgfsSessionInfo *session, // IN: session info
                          HgfsFileAttrInfo *attr);  // OUT: file attributes
HgfsInternalStatus
HgfsPlatformSetattrFromFd(HgfsHandle file,          // IN: file descriptor
                          HgfsSessionInfo *session, // IN: session info
                          HgfsFileAttrInfo *attr,   // IN: attrs to set
                          HgfsAttrHint hints,       // IN: attr hints
                          Bool useHostTime);        // IN: use current host time
HgfsInternalStatus
HgfsPlatformSetattrFromName(char *utf8Name,                 // IN: local file path
                            HgfsFileAttrInfo *attr,         // IN: attrs to set
                            HgfsShareOptions configOptions, // IN: share options
                            HgfsAttrHint hints,             // IN: attr hints
                            Bool useHostTime);              // IN: use current host time
HgfsInternalStatus
HgfsPlatformValidateOpen(HgfsFileOpenInfo *openInfo, // IN: Open info struct
                         Bool followLinks,           // IN: follow symlinks on the host
                         HgfsSessionInfo *session,   // IN: Session info
                         HgfsLocalId *localId,       // OUT: Local unique file ID
                         fileDesc *newHandle);       // OUT: Handle to the file

void *
HSPU_GetMetaPacket(HgfsPacket *packet,                   // IN/OUT: Hgfs Packet
                   size_t *metaPacketSize,               // OUT: Size of metaPacket
                   HgfsServerChannelCallbacks *chanCb);  // IN: Channel callbacks

Bool
HSPU_ValidateDataPacketSize(HgfsPacket *packet,     // IN: Hgfs Packet
                            size_t dataSize);       // IN: data size

void *
HSPU_GetDataPacketBuf(HgfsPacket *packet,                   // IN/OUT: Hgfs Packet
                      MappingType mappingType,              // IN: Readable/ Writeable ?
                      HgfsServerChannelCallbacks *chanCb);  // IN: Channel callbacks

void
HSPU_SetDataPacketSize(HgfsPacket *packet,            // IN/OUT: Hgfs Packet
                       size_t dataSize);              // IN: data size

void
HSPU_PutDataPacketBuf(HgfsPacket *packet,                   // IN/OUT: Hgfs Packet
                      HgfsServerChannelCallbacks *chanCb);  // IN: Channel callbacks

void
HSPU_PutMetaPacket(HgfsPacket *packet,                   // IN/OUT: Hgfs Packet
                   HgfsServerChannelCallbacks *chanCb);  // IN: Channel callbacks

Bool
HSPU_ValidateRequestPacketSize(HgfsPacket *packet,        // IN: Hgfs Packet
                               size_t requestHeaderSize,  // IN: request header size
                               size_t requestOpSize,      // IN: request packet size
                               size_t requestOpDataSize); // IN: request packet data size

Bool
HSPU_ValidateReplyPacketSize(HgfsPacket *packet,         // IN: Hgfs Packet
                             size_t replyHeaderSize,     // IN: reply header size
                             size_t replyResultSize,     // IN: reply result size
                             size_t replyResultDataSize, // IN: reply result data size
                             Bool useMappedMetaPacket);    // IN: using meta buffer

void *
HSPU_GetReplyPacket(HgfsPacket *packet,                  // IN/OUT: Hgfs Packet
                    HgfsServerChannelCallbacks *chanCb,  // IN: Channel callbacks
                    size_t replyDataSize,                // IN: Size of reply data
                    size_t *replyPacketSize);            // OUT: Size of reply Packet

void
HSPU_PutReplyPacket(HgfsPacket *packet,                  // IN/OUT: Hgfs Packet
                    HgfsServerChannelCallbacks *chanCb); // IN: Channel callbacks
#endif /* __HGFS_SERVER_INT_H__ */
  07070100000082000081A40000000000000000000000016822550500028DDD000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerLinux.c   /*********************************************************
 * Copyright (C) 1998-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerLinux.c --
 *
 *      This file contains the linux implementation of the server half
 *      of the Host/Guest File System (hgfs), a.k.a. "Shared Folder".
 *
 *      The hgfs server carries out filesystem requests that it receives
 *      over the backdoor from a driver in the other world.
 */

#define _GNU_SOURCE // for O_NOFOLLOW

#if defined(__APPLE__)
#define _DARWIN_USE_64_BIT_INODE
#endif

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>  // for utimes(2)
#include <sys/syscall.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/resource.h> // for getrlimit

#if defined(__FreeBSD__)
#   include <sys/param.h>
#else
#   include <wchar.h>
#   include <wctype.h>
#endif

#include "vmware.h"
#include "hgfsServerPolicy.h" // for security policy
#include "hgfsServerInt.h"
#include "hgfsServerOplock.h"
#include "hgfsEscape.h"
#include "err.h"
#include "str.h"
#include "cpNameLite.h"
#include "hgfsUtil.h"  // for cross-platform time conversion
#include "posix.h"
#include "file.h"
#include "util.h"
#include "su.h"
#include "codeset.h"
#include "unicodeOperations.h"
#include "userlock.h"

#if defined(__linux__) && !defined(SYS_getdents64)
/* For DT_UNKNOWN */
#   include <dirent.h>
#endif

#ifndef VMX86_TOOLS
#   include "config.h"
#endif

#if defined(__APPLE__)
#include <CoreServices/CoreServices.h> // for the alias manager
#include <CoreFoundation/CoreFoundation.h> // for CFString and CFURL
#include <sys/attr.h>       // for getattrlist
#include <sys/vnode.h>      // for VERG / VDIR
#endif

#ifdef __linux__
typedef struct DirectoryEntry {
   uint64 d_ino;
   uint64 d_off;
   uint16 d_reclen;
   uint8  d_type;
   char   d_name[256];
} DirectoryEntry;
#else
#include <dirent.h>
typedef struct DirectoryEntry {
   uint64 d_ino;
   uint16 d_reclen;
   uint16 d_namlen;
   uint8  d_type;
   char   d_name[1024];
} DirectoryEntry;
#endif

/*
 * ALLPERMS (mode 07777) and ACCESSPERMS (mode 0777) are not defined in the
 * Solaris version of <sys/stat.h>.
 */
#ifdef sun
#   define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO)
#   define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
#endif


/*
 * On Linux, we must wrap getdents64, as glibc does not wrap it for us. We use getdents64
 * (rather than getdents) because with the latter, we'll get 64-bit offsets and inode
 * numbers. Note that getdents64 isn't supported everywhere, in particular, kernels older
 * than 2.4.1 do not implement it. On those older guests, we try using getdents(), and
 * translating the results to our DirectoryEntry structure...
 *
 * On FreeBSD and Mac platforms, getdents is implemented as getdirentries, and takes an
 * additional parameter which returns the position of the block read, which we don't care
 * about.
 */
#if defined(__linux__)
static INLINE int
getdents_linux(unsigned int fd,
               DirectoryEntry *dirp,
               unsigned int count)
{
#   if defined(SYS_getdents64)
   return syscall(SYS_getdents64, fd, dirp, count);
#   else
   /*
    * Fall back to regular getdents on older Linux systems that don't have
    * getdents64. Because glibc does translation between the kernel's "struct dirent" and
    * the libc "struct dirent", this structure matches the one in linux/dirent.h, rather
    * than us using the libc 'struct dirent' directly
    */
   struct linux_dirent {
      long d_ino;
      long d_off; /* __kernel_off_t expands to long on RHL 6.2 */
      unsigned short d_reclen;
      char d_name[NAME_MAX];
   } *dirp_temp;
   int retval;

   dirp_temp = alloca((sizeof *dirp_temp) * count);

   retval = syscall(SYS_getdents, fd, dirp_temp, count);

   if (retval > 0) {
      int i;

      /*
       * Translate from the Linux 'struct dirent' to the hgfs DirectoryEntry, since
       * they're not always the same layout.
       */
      for (i = 0; i < count; i++) {
         dirp[i].d_ino = dirp_temp[i].d_ino;
         dirp[i].d_off = dirp_temp[i].d_off;
         dirp[i].d_reclen = dirp_temp[i].d_reclen;
         dirp[i].d_type = DT_UNKNOWN;
         memset(dirp[i].d_name, 0, sizeof dirp->d_name);
         memcpy(dirp[i].d_name, dirp_temp[i].d_name,
                ((sizeof dirp->d_name) < (sizeof dirp_temp->d_name))
                ? (sizeof dirp->d_name)
                : (sizeof dirp_temp->d_name));
      }
   }

   return retval;
#   endif
}
#      define getdents getdents_linux
#elif defined(__FreeBSD__)
#if defined(__INO64)
typedef off_t BASEPTYPE;
#else
typedef long BASEPTYPE;
#endif
#define getdents_freebsd(fd, dirp, count)                                     \
({                                                                            \
   BASEPTYPE basep;                                                           \
   getdirentries(fd, dirp, count, &basep);                                    \
})
#      define getdents getdents_freebsd
#elif defined(__APPLE__)
static INLINE int
getdents_apple(DIR *fd,               // IN
               DirectoryEntry *dirp,  // OUT
               unsigned int count)    // IN: ignored
{
   int res = 0;
   struct dirent *dirEntry;
   dirEntry = readdir(fd);
   if (NULL != dirEntry) {
      /*
       * Assert that the hgfs DirectoryEntry version and system dirent
       * name fields are of the same size. Since that is where it was taken from.
       */
      ASSERT_ON_COMPILE(sizeof dirp->d_name == sizeof dirEntry->d_name);

      dirp->d_ino    = dirEntry->d_ino;
      dirp->d_type   = dirEntry->d_type;
      dirp->d_namlen = dirEntry->d_namlen;
      memcpy(dirp->d_name, dirEntry->d_name, dirEntry->d_namlen + 1);
      dirp->d_reclen = offsetof(DirectoryEntry, d_name) + dirp->d_namlen + 1;
      res = dirp->d_reclen;
   }
   return res;
}
#      define getdents getdents_apple
#endif

/*
 * O_DIRECTORY is only relevant on Linux. For other platforms, we'll hope that
 * the kernel is smart enough to deny getdents(2) (or getdirentries(2)) on
 * files which aren't directories.
 *
 * Likewise, O_NOFOLLOW doesn't exist on Solaris 9. Oh well.
 */
#ifndef O_DIRECTORY
#define O_DIRECTORY 0
#endif

#ifndef O_NOFOLLOW
#define O_NOFOLLOW 0
#endif


#if defined(sun) || defined(__linux__)
/*
 * Implements futimes(), which was introduced in glibc 2.3.3.
 * For now the only caller to futimes() is HgfsServerSetattr which doesn't get
 * invoked at all in the HGFS server which runs in the Tools.
 */
#define PROC_SELF_FD "/proc/self/fd/"
#define STRLEN_OF_MAXINT_AS_STRING 10
int
futimes(int fd, const struct timeval times[2])
{
#ifndef sun
   /*
    * Hack to allow the timevals in futimes() to be const even when utimes()
    * expects non-const timevals. This is the case on glibc up to 2.3 or
    * thereabouts.
    */
   struct timeval mytimes[2];

   /* Maximum size of "/proc/self/fd/<fd>" as a string. Accounts for nul. */
   char nameBuffer[sizeof PROC_SELF_FD + STRLEN_OF_MAXINT_AS_STRING];

   mytimes[0] = times[0];
   mytimes[1] = times[1];
   if (snprintf(nameBuffer, sizeof nameBuffer, PROC_SELF_FD "%d", fd) < 0) {
      return -1;
   }
   return Posix_Utimes(nameBuffer, mytimes);
#else /* !sun */
   return futimesat(fd, NULL, times);
#endif /* !sun */
}
#undef PROC_SELF_FD
#undef STRLEN_OF_MAXINT_AS_STRING
#endif

#if defined(__APPLE__)
struct FInfoAttrBuf {
   uint32 length;
   fsobj_type_t objType;
   char finderInfo[32];
};
#endif

/*
 * Taken from WinNT.h.
 * For verifying the Windows client which can ask for delete access as well as the
 * standard read, write, execute permissions.
 * XXX - should probably be moved into a header file and may need to be expanded if
 * Posix looks at the access mode more thoroughly or we expand the set of cross-platform
 * access mode flags.
 */
#define DELETE                           (0x00010000L)

/*
 * Server open flags, indexed by HgfsOpenFlags. Stolen from
 * lib/fileIOPosix.c
 *
 * Using O_NOFOLLOW will allow us to forgo a (racy) symlink check just
 * before opening the file.
 *
 * Using O_NONBLOCK will prevent us from blocking the HGFS server if
 * we open a FIFO.
 */
static const int HgfsServerOpenFlags[] = {
   O_NONBLOCK | O_NOFOLLOW,
   O_NONBLOCK | O_NOFOLLOW | O_TRUNC,
   O_NONBLOCK | O_NOFOLLOW | O_CREAT,
   O_NONBLOCK | O_NOFOLLOW | O_CREAT | O_EXCL,
   O_NONBLOCK | O_NOFOLLOW | O_CREAT | O_TRUNC,
};


/*
 * Server open mode, indexed by HgfsOpenMode.
 */
static const int HgfsServerOpenMode[] = {
   O_RDONLY,
   O_WRONLY,
   O_RDWR,
};

/* Local functions. */
static HgfsInternalStatus HgfsGetattrResolveAlias(char const *fileName,
                                                  char **targetName);

static void HgfsStatToFileAttr(struct stat *stats,
                               uint64 *creationTime,
                               HgfsFileAttrInfo *attr);
static int HgfsStat(const char* fileName,
                    Bool followLink,
                    struct stat *stats,
                    uint64 *creationTime);
static int HgfsFStat(int fd,
                     struct stat *stats,
                     uint64 *creationTime);

static void HgfsGetSequentialOnlyFlagFromName(const char *fileName,
                                              Bool followSymlinks,
                                              HgfsFileAttrInfo *attr);

static void HgfsGetSequentialOnlyFlagFromFd(int fd,
                                            HgfsFileAttrInfo *attr);

static int HgfsConvertComponentCase(char *currentComponent,
                                    const char *dirPath,
                                    const char **convertedComponent,
                                    size_t *convertedComponentSize);

static int HgfsConstructConvertedPath(char **path,
                                      size_t *pathSize,
                                      char *convertedPath,
                                      size_t convertedPathSize);

static int HgfsCaseInsensitiveLookup(const char *sharePath,
                                     size_t sharePathLength,
                                     char *fileName,
                                     size_t fileNameLength,
                                     char **convertedFileName,
                                     size_t *convertedFileNameLength);

static Bool HgfsSetattrMode(struct stat *statBuf,
                            HgfsFileAttrInfo *attr,
                            mode_t *newPermissions);

static Bool HgfsSetattrOwnership(HgfsFileAttrInfo *attr,
                                 uid_t *newUid,
                                 gid_t *newGid);

static HgfsInternalStatus HgfsSetattrTimes(struct stat *statBuf,
                                           HgfsFileAttrInfo *attr,
                                           HgfsAttrHint hints,
                                           Bool useHostTime,
                                           struct timeval *accessTime,
                                           struct timeval *modTime,
                                           Bool *timesChanged);

static HgfsInternalStatus HgfsGetHiddenXAttr(char const *fileName, Bool *attribute);
static HgfsInternalStatus HgfsSetHiddenXAttr(char const *fileName,
                                             Bool value,
                                             mode_t permissions);
static HgfsInternalStatus HgfsEffectivePermissions(char *fileName,
                                                   Bool readOnlyShare,
                                                   uint32 *permissions);
static uint64 HgfsGetCreationTime(const struct stat *stats);

#if !defined(sun)
static HgfsInternalStatus HgfsWriteCheckIORange(off_t offset,
                                                uint32 bytesToWrite);
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformConvertFromNameStatus --
 *
 *    This function converts between a status code used in processing a cross
 *    platform filename, and a platform-specific status code.
 *
 *    Because the two status codes never go down the wire, there is no danger
 *    of backwards compatibility here, and we should ASSERT if we encounter
 *    an status code that we're not familiar with.
 *
 * Results:
 *    Converted status code.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformConvertFromNameStatus(HgfsNameStatus status) // IN
{
   switch(status) {
   case HGFS_NAME_STATUS_COMPLETE:
      return 0;
   case HGFS_NAME_STATUS_FAILURE:
   case HGFS_NAME_STATUS_INCOMPLETE_BASE:
   case HGFS_NAME_STATUS_INCOMPLETE_ROOT:
   case HGFS_NAME_STATUS_INCOMPLETE_DRIVE:
   case HGFS_NAME_STATUS_INCOMPLETE_UNC:
   case HGFS_NAME_STATUS_INCOMPLETE_UNC_MACH:
      return EINVAL;
   case HGFS_NAME_STATUS_DOES_NOT_EXIST:
      return ENOENT;
   case HGFS_NAME_STATUS_ACCESS_DENIED:
      return EACCES;
   case HGFS_NAME_STATUS_SYMBOLIC_LINK:
      return ELOOP;
   case HGFS_NAME_STATUS_OUT_OF_MEMORY:
      return ENOMEM;
   case HGFS_NAME_STATUS_TOO_LONG:
      return ENAMETOOLONG;
   case HGFS_NAME_STATUS_NOT_A_DIRECTORY:
      return ENOTDIR;
   default:
      NOT_IMPLEMENTED();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformGetDefaultDirAttrs --
 *
 *    Get default directory attributes. Permissions are Read and
 *    Execute permission only.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsPlatformGetDefaultDirAttrs(HgfsFileAttrInfo *attr) // OUT
{
   struct timeval tv;
   uint64 hgfsTime;

   ASSERT(attr);

   attr->type = HGFS_FILE_TYPE_DIRECTORY;
   attr->size = 4192;

   /*
    * Linux and friends are OK with receiving timestamps of 0, but just
    * for consistency with the Windows server, we'll pass back the
    * host's time in a virtual directory's timestamps.
    */
   if (gettimeofday(&tv, NULL) != 0) {
      hgfsTime = 0;
   } else {
      hgfsTime = HgfsConvertToNtTime(tv.tv_sec, tv.tv_usec * 1000);
   }
   attr->creationTime = hgfsTime;
   attr->accessTime = hgfsTime;
   attr->writeTime = hgfsTime;
   attr->attrChangeTime = hgfsTime;
   attr->specialPerms = 0;
   attr->ownerPerms = HGFS_PERM_READ | HGFS_PERM_EXEC;
   attr->groupPerms = HGFS_PERM_READ | HGFS_PERM_EXEC;
   attr->otherPerms = HGFS_PERM_READ | HGFS_PERM_EXEC;

   attr->mask = HGFS_ATTR_VALID_TYPE |
      HGFS_ATTR_VALID_SIZE |
      HGFS_ATTR_VALID_CREATE_TIME |
      HGFS_ATTR_VALID_ACCESS_TIME |
      HGFS_ATTR_VALID_WRITE_TIME |
      HGFS_ATTR_VALID_CHANGE_TIME |
      HGFS_ATTR_VALID_SPECIAL_PERMS |
      HGFS_ATTR_VALID_OWNER_PERMS |
      HGFS_ATTR_VALID_GROUP_PERMS |
      HGFS_ATTR_VALID_OTHER_PERMS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetOpenFlags --
 *
 *      Retrieve system open flags from HgfsOpenFlags.
 *
 *      Does the correct bounds checking on the HgfsOpenFlags before
 *      indexing into the array of flags to use. See bug 54429.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsServerGetOpenFlags(HgfsOpenFlags flagsIn, // IN
                       int *flagsOut)         // OUT
{
   unsigned int arraySize;

   ASSERT(flagsOut);

   arraySize = ARRAYSIZE(HgfsServerOpenFlags);

   if ((unsigned int)flagsIn >= arraySize) {
      Log("%s: Invalid HgfsOpenFlags %d\n", __FUNCTION__, flagsIn);

      return FALSE;
   }

   *flagsOut = HgfsServerOpenFlags[flagsIn];

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformInit --
 *
 *      Set up any state needed to start Linux HGFS server.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPlatformInit(void)
{
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformDestroy --
 *
 *      Tear down any state used for Linux HGFS server.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsPlatformDestroy(void)
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetOpenMode --
 *
 *      Retrieve system open mode from HgfsOpenMode.
 *
 *      Does the correct bounds checking on the HgfsOpenMode before
 *      indexing into the array of modes to use. See bug 54429.
 *
 *      This is just the POSIX implementation; the Windows implementation is
 *      more complicated, hence the need for the HgfsFileOpenInfo as an
 *      argument.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerGetOpenMode(HgfsFileOpenInfo *openInfo, // IN:  Open info to examine
                      uint32 *modeOut)            // OUT: Local mode
{
   ASSERT(modeOut);

   /*
    * If we didn't get the mode in the open request, we'll return a mode of 0.
    * This has the effect of failing the call to open(2) later, which is
    * exactly what we want.
    */
   if ((openInfo->mask & HGFS_OPEN_VALID_MODE) == 0) {
      *modeOut = 0;
      return TRUE;
   }

   if (!HGFS_OPEN_MODE_IS_VALID_MODE(openInfo->mode)) {
      Log("%s: Invalid HgfsOpenMode %d\n", __FUNCTION__, openInfo->mode);

      return FALSE;
   }

   *modeOut = HgfsServerOpenMode[HGFS_OPEN_MODE_ACCMODE(openInfo->mode)];

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformCloseFile --
 *
 *    Closes the file descriptor and release the file context.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformCloseFile(fileDesc fileDesc, // IN: File descriptor
                      void *fileCtx)     // IN: File context
{
   if (close(fileDesc) != 0) {
      int error = errno;

      LOG(4, "%s: Could not close fd %d: %s\n", __FUNCTION__, fileDesc,
          Err_Errno2String(error));
      return error;
   }

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCheckFileNode --
 *
 *    Check if a file node is still valid (i.e. if the file name stored in the
 *    file node still refers to the same file)
 *
 * Results:
 *    Zero if the file node is valid
 *    Non-zero if the file node is stale
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsCheckFileNode(char const *localName,      // IN
                  HgfsLocalId const *localId) // IN
{
   struct stat nodeStat;

   ASSERT(localName);
   ASSERT(localId);

   /*
    * A file is uniquely identified by a (device; inode) pair. Check that the
    * file name still refers to the same pair
    */

#if defined(__APPLE__)
   /*
    *  Can't use Posix_Stat because of inconsistent definition
    *  of _DARWIN_USE_64_BIT_INODE in this file and in other libraries.
    */
   if (stat(localName, &nodeStat) < 0) {
#else
   if (Posix_Stat(localName, &nodeStat) < 0) {
#endif
      int error = errno;

      LOG(4, "%s: couldn't stat local file \"%s\": %s\n", __FUNCTION__,
          localName, Err_Errno2String(error));
      return error;
   }

   if (nodeStat.st_dev != localId->volumeId ||
       nodeStat.st_ino != localId->fileId) {
      LOG(4, "%s: local Id mismatch\n", __FUNCTION__);

      return ENOENT;
   }

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformGetFd --
 *
 *    Returns the file descriptor associated with the node. If the node is
 *    cached then it just returns the cached file descriptor (checking for
 *    correct write flags). Otherwise, it opens a new file, caches the node
 *    and returns the file desriptor.
 *
 * Results:
 *    Zero on success. fd contains the opened file descriptor.
 *    Non-zero on error.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformGetFd(HgfsHandle hgfsHandle,    // IN:  HGFS file handle
                  HgfsSessionInfo *session, // IN:  Session info
                  Bool append,              // IN:  Open with append flag
                  fileDesc *fd)             // OUT: Opened file descriptor
{
   int newFd = -1, openFlags = 0;
   HgfsFileNode node;
   HgfsInternalStatus status = 0;

   ASSERT(fd);
   ASSERT(session);

   /*
    * Use node copy convenience function to get the node information.
    * Note that we shouldn't keep this node around for too long because
    * the information can become stale. However, it's ok to get all the
    * fields in one step, instead of getting them all separate.
    *
    * XXX: It would be better if we didn't do this node copy on the fast
    * path. Unfortuntely, even the fast path may need to look at the node's
    * append flag.
    */
   node.utf8Name = NULL;
   if (!HgfsGetNodeCopy(hgfsHandle, session, TRUE, &node)) {
      /* XXX: Technically, this can also fail if we're out of memory. */
      LOG(4, "%s: Invalid hgfs handle.\n", __FUNCTION__);
      status = EBADF;
      goto exit;
   }

   /* If the node is found in the cache */
   if (HgfsIsCached(hgfsHandle, session)) {
      /*
       * If the append flag is set check to see if the file was opened
       * in append mode. If not, close the file and reopen it in append
       * mode.
       */
      if (append && !(node.flags & HGFS_FILE_NODE_APPEND_FL)) {
         status = HgfsPlatformCloseFile(node.fileDesc, node.fileCtx);
         if (status != 0) {
            LOG(4, "%s: Couldn't close file \"%s\" for reopening\n",
                __FUNCTION__, node.utf8Name);
            goto exit;
         }

         /*
          * Update the node in the cache with the new value of the append
          * flag.
          */
         if (!HgfsUpdateNodeAppendFlag(hgfsHandle, session, TRUE)) {
            LOG(4, "%s: Could not update the node in the cache\n",
                __FUNCTION__);
            status = EBADF;
            goto exit;
         }
      } else {
         newFd = node.fileDesc;
         goto exit;
      }
   }

   /*
    * If we got here then the file was either not in the cache or needs
    * reopening. This means we need to open a file. But first, verify
    * that the file we intend to open isn't stale.
    */
   status = HgfsCheckFileNode(node.utf8Name, &node.localId);
   if (status != 0) {
      goto exit;
   }

   /*
    * We're not interested in creating a new file. So let's just get the
    * flags for a simple open request. This really should always work.
    */
   HgfsServerGetOpenFlags(0, &openFlags);

   /*
    * We don't need to specify open permissions here because we're only
    * reopening an existing file, not creating a new one.
    *
    * XXX: We should use O_LARGEFILE, see lib/file/fileIOPosix.c --hpreg
    */
   newFd = Posix_Open(node.utf8Name,
		node.mode | openFlags | (append ? O_APPEND : 0));

   if (newFd < 0) {
      int error = errno;

      LOG(4, "%s: Couldn't open file \"%s\": %s\n", __FUNCTION__,
          node.utf8Name, Err_Errno2String(errno));
      status = error;
      goto exit;
   }

   /*
    * Update the original node with the new value of the file desc.
    * This call might fail if the node is not used anymore.
    */
   if (!HgfsUpdateNodeFileDesc(hgfsHandle, session, newFd, NULL)) {
      LOG(4, "%s: Could not update the node -- node is not used.\n",
          __FUNCTION__);
      status = EBADF;
      goto exit;
   }

   /* Add the node to the cache. */
   if (!HgfsAddToCache(hgfsHandle, session)) {
      LOG(4, "%s: Could not add node to the cache\n", __FUNCTION__);
      status = EBADF;
      goto exit;
   }

  exit:
   if (status == 0) {
      *fd = newFd;
   }
   free(node.utf8Name);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformValidateOpen --
 *
 *    Verify that the file with the given local name exists in the
 *    local filesystem by trying to open the file with the requested
 *    mode and permissions. If the open succeeds we stat the file
 *    and fill in the volumeId and fileId with the file's local
 *    filesystem device and inode number, respectively.
 *
 * Results:
 *    Zero on success
 *    Non-zero on failure.
 *
 * Side effects:
 *    File with name "localName" may be created or truncated.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformValidateOpen(HgfsFileOpenInfo *openInfo, // IN: Open info struct
                         Bool followSymlinks,        // IN: followSymlinks config option
                         HgfsSessionInfo *session,   // IN: session info
                         HgfsLocalId *localId,       // OUT: Local unique file ID
                         fileDesc *fileDesc)         // OUT: Handle to the file
{
   struct stat fileStat;
   int fd;
   int openMode = 0, openFlags = 0;
   mode_t openPerms;
   HgfsLockType serverLock;
   HgfsInternalStatus status = 0;
   Bool needToSetAttribute = FALSE;

   ASSERT(openInfo);
   ASSERT(localId);
   ASSERT(fileDesc);
   ASSERT(session);

   /*
    * Get correct system flags and mode from the HgfsOpenFlags and
    * HgfsOpenMode. This is related to bug 54429.
    */
   if (!HgfsServerGetOpenFlags(openInfo->mask & HGFS_OPEN_VALID_FLAGS ?
                                 openInfo->flags : 0,
                               &openFlags) ||
       !HgfsServerGetOpenMode(openInfo, &openMode)) {
      status = EPROTO;
      goto exit;
   }

   /*
    * Create mode_t for use in open(). If owner permissions are missing, use
    * read/write for the owner permissions. If group or other permissions
    * are missing, use the owner permissions.
    *
    * This sort of makes sense. If the Windows driver wants to make a file
    * read-only, it probably intended for the file to be 555. Since creating
    * a file requires a valid mode, it's highly unlikely that we'll ever
    * be creating a file without owner permissions.
    */
   openPerms = 0;
   openPerms |= openInfo->mask & HGFS_OPEN_VALID_SPECIAL_PERMS ?
                  openInfo->specialPerms << 9 : 0;
   openPerms |= openInfo->mask & HGFS_OPEN_VALID_OWNER_PERMS ?
                  openInfo->ownerPerms << 6 : S_IWUSR | S_IRUSR;
   openPerms |= openInfo->mask & HGFS_OPEN_VALID_GROUP_PERMS ?
                  openInfo->groupPerms << 3 : (openPerms & S_IRWXU) >> 3;
   openPerms |= openInfo->mask & HGFS_OPEN_VALID_OTHER_PERMS ?
                  openInfo->otherPerms : (openPerms & S_IRWXU) >> 6;

   /*
    * By default we don't follow symlinks, O_NOFOLLOW is always set.
    * Unset it if followSymlinks config option is specified.
    */
   if (followSymlinks) {
      openFlags &= ~O_NOFOLLOW;
   }

   /*
    * Need to validate that open does not change the file for read
    * only shared folders.
    */
   status = 0;
   if (!openInfo->shareInfo.writePermissions) {
      Bool deleteAccess = FALSE;
      /*
       * If a valid desiredAccess field specified by the Windows client, we use that
       * as the desiredAccess field has more data such as delete than is contained
       * in the mode.
       */
      if ((0 != (openInfo->mask & HGFS_OPEN_VALID_DESIRED_ACCESS)) &&
          (0 != (openInfo->desiredAccess & DELETE))) {
         deleteAccess = TRUE;
      }

      if ((openFlags & (O_APPEND | O_CREAT | O_TRUNC)) ||
          (openMode & (O_WRONLY | O_RDWR)) ||
          deleteAccess) {
         status = Posix_Access(openInfo->utf8Name, F_OK);
         if (status < 0) {
            status = errno;
            if (status == ENOENT && (openFlags & O_CREAT) != 0) {
               status = EACCES;
            }
         } else {
            /*
             * Handle the case when the file already exists:
             * If there is an attempt to createa new file, fail with "EEXIST"
             * error, otherwise set error to "EACCES".
             */
            if ((openFlags & O_CREAT) && (openFlags & O_EXCL)) {
               status = EEXIST;
            } else  {
               status = EACCES;
            }
         }
      }
      if (status != 0) {
         LOG(4, "%s: Error: Unwritable share mode %u flags %u file \"%s\": %d %s\n",
             __FUNCTION__, openMode, openFlags, openInfo->utf8Name,
             status, Err_Errno2String(status));
         goto exit;
      }
   }

   if (!openInfo->shareInfo.readPermissions) {
      /*
       * "Drop Box" / "FTP incoming" type of shared folders.
       * Only allow creating a new file.
       * Any access to an existing file requires read so fail EACCES.
       */
      int accessStatus = Posix_Access(openInfo->utf8Name, F_OK);

      if (accessStatus < 0) {
         accessStatus = errno;
         /* Not creating a new file, then fail. */
         if (!(accessStatus == ENOENT && (openFlags & O_CREAT))) {
            status = EACCES;
         }
      } else {
         /*An existing file, then fail */
         status = EACCES;
      }
      if (status == EACCES) {
         LOG(4, "%s: Error: Unreadable share flags %u file \"%s\": %d %s\n",
             __FUNCTION__, openFlags, openInfo->utf8Name, status,
             Err_Errno2String(status));
         goto exit;
      }
   }

   /*
    * Determine if hidden attribute needs to be updated.
    * It needs to be updated if a new file is created or an existing file is truncated.
    * Since Posix_Open does not tell us if a new file has been created when O_CREAT is
    * specified we need to find out if the file exists before an open that may create
    * it.
    */
   if (openInfo->mask & HGFS_OPEN_VALID_FILE_ATTR) {
      if ((openFlags & O_TRUNC) ||
          ((openFlags & O_CREAT) && (openFlags & O_EXCL))) {
         needToSetAttribute = TRUE;
      } else if (openFlags & O_CREAT) {
         int err = Posix_Access(openInfo->utf8Name, F_OK);
         needToSetAttribute = (err != 0) && (errno == ENOENT);
      }
   }

   /*
    * Try to open the file with the requested mode, flags and permissions.
    */
   fd = Posix_Open(openInfo->utf8Name,
                   openMode | openFlags,
                   openPerms);
   if (fd < 0) {
      status = errno;
      if (status == EAGAIN) {
         /*
          * We have tried opening with O_NONBLOCK but looks like an incompatible
          * lease may be held on the file. Tell the client that this access mode
          * is not allowed currently.
          */
         status = EACCES;
      }
      LOG(4, "%s: Error: open file \"%s\": %d %s\n", __FUNCTION__,
          openInfo->utf8Name, status, Err_Errno2String(status));
      goto exit;
   }

   /* Stat file to get its volume and file info */
   if (fstat(fd, &fileStat) < 0) {
      status = errno;
      LOG(4, "%s: Error: stat file\"%s\": %d %s\n", __FUNCTION__,
          openInfo->utf8Name, status, Err_Errno2String(status));
      close(fd);
      goto exit;
   }

   /* Set the rest of the Windows specific attributes if necessary. */
   if (needToSetAttribute) {
      HgfsSetHiddenXAttr(openInfo->utf8Name,
                         (openInfo->attr & HGFS_ATTR_HIDDEN) != 0,
                         fileStat.st_mode);
   }

   /* Try to acquire an oplock. */
   if (openInfo->mask & HGFS_OPEN_VALID_SERVER_LOCK) {
      serverLock = openInfo->desiredLock;
      if (!HgfsAcquireServerLock(fd, session, &serverLock)) {
         openInfo->acquiredLock = HGFS_LOCK_NONE;
      } else {
         openInfo->acquiredLock = serverLock;
      }
   } else {
      openInfo->acquiredLock = HGFS_LOCK_NONE;
   }

   *fileDesc = fd;

   /* Set volume and file ids from stat results */
   localId->volumeId = fileStat.st_dev;
   localId->fileId = fileStat.st_ino;

  exit:
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetattrResolveAlias --
 *
 *    Mac OS defines a special file type known as an alias which behaves like a
 *    symlink when viewed through the Finder, but is actually a regular file
 *    otherwise.
 *
 *    If the given filename is (or contains) an alias, this function will
 *    resolve it completely and set targetName to something non-NULL.
 *
 * Results:
 *    Zero on success. targetName is allocated if the file was an alias, and
 *    NULL otherwise.
 *    Non-zero on failure. targetName is unmodified.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsGetattrResolveAlias(char const *fileName,       // IN:  Input filename
                        char **targetName)          // OUT: Target filename
{
#ifndef __APPLE__
   *targetName = NULL;
   return 0;
#else // __APPLE__
   HgfsInternalStatus status = HGFS_INTERNAL_STATUS_ERROR;
   Boolean success;
   CFURLRef resolvedURL = NULL;
   CFStringRef resolvedString = NULL;
   CFIndex maxPath;

   *targetName = NULL;

   if (CFURLCreateBookmarkDataFromFile != NULL) {
      /* We are running on Mac OS 10.6 or later. */

      CFURLRef fileURL;
      CFBooleanRef isAlias = NULL;
      CFDataRef bookmarkData = NULL;
      CFURLBookmarkResolutionOptions resolutionOptions;
      Boolean isStale;

      fileURL = CFURLCreateFromFileSystemRepresentation(NULL, fileName,
                                                        strlen(fileName),
                                                        FALSE);
      if (!fileURL) {
         Log("%s: could not create CFURL for file.\n",
                 __FUNCTION__);
         goto newExit;
      }

      success = CFURLCopyResourcePropertyForKey(fileURL, kCFURLIsAliasFileKey,
                                                &isAlias, NULL);
      if (!success) {
         Log("%s: could not copy IsAlias property key for file.\n",
                 __FUNCTION__);
         goto newExit;
      }
      if (!CFBooleanGetValue(isAlias)) {
         status = 0;
         LOG(4, "%s: file was not an alias\n", __FUNCTION__);
         goto newExit;
      }

      LOG(4, "%s: file was an alias\n", __FUNCTION__);

      bookmarkData = CFURLCreateBookmarkDataFromFile(NULL, fileURL, NULL);
      if (!bookmarkData) {
         Log("%s: could not retrieve bookmark data for file.\n",
                 __FUNCTION__);
         goto newExit;
      }

      /*
       * Don't show any UI during alias resolution and don't mount volumes
       * containing the alias target. This avoids blocking the current process
       * and/or Finder while trying to mount unreachable hosts (bug 1396411).
       */
      resolutionOptions = kCFBookmarkResolutionWithoutUIMask |
                          kCFBookmarkResolutionWithoutMountingMask;

      resolvedURL = CFURLCreateByResolvingBookmarkData(NULL, bookmarkData,
                                                       resolutionOptions,
                                                       NULL, NULL, &isStale,
                                                       NULL);
      if (!resolvedURL) {
         Log("%s: could not resolve bookmark data for file.\n",
                 __FUNCTION__);
         goto newExit;
      }

newExit:
      if (fileURL) {
         CFRelease(fileURL);
      }
      if (isAlias) {
         CFRelease(isAlias);
      }
      if (bookmarkData) {
         CFRelease(bookmarkData);
      }

   } else {
      /* We are running on Mac OS 10.5 or earlier. */

#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1060
      /*
       * Mac OS 10.5 used a type of alias which appears on disk as a 0-byte
       * file but stores its linking data in a resource fork. The APIs for
       * interacting with this type of alias are deprecated when targetting
       * 10.8+.
       */
      FSRef fileRef;
      Boolean isAlias;
      Boolean targetIsFolder;
      OSStatus osStatus;

      ASSERT_ON_COMPILE(sizeof osStatus == sizeof (int32));

      osStatus = FSPathMakeRef(fileName, &fileRef, NULL);
      if (osStatus != noErr) {
         Log("%s: could not create file reference: error %d\n",
                 __FUNCTION__, (int32)osStatus);
         goto oldExit;
      }

      osStatus = FSIsAliasFile(&fileRef, &isAlias, &targetIsFolder);
      if (osStatus != noErr) {
         Log("%s: could not detect if file is an old style alias: error %d\n",
                 __FUNCTION__, (int32)osStatus);
         goto oldExit;
      }

      if (!isAlias) {
         status = 0;
         LOG(4, "%s: file was not an alias\n", __FUNCTION__);
         goto oldExit;
      }

      LOG(4, "%s: file was an alias\n", __FUNCTION__);

      /*
       * Create and resolve an FSRef of the desired path. We pass FALSE to
       * resolveAliasChains because aliases to aliases should behave as
       * symlinks to symlinks.
       *
       * If alias points to an unmounted volume, the volume needs to be
       * explicitly mounted. Mount flag kResolveAliasFileNoUI prevents the user
       * from being prompted about mounting.
       *
       * Caution: Mac OS 10.10.2 will attempt to mount volumes silently,
       * unlike the earlier behavior of only resolving within existing mounts.
       * The new CFURLCreateByResolvingBookmarkData API must be used to avoid
       * mounting the alias target.
       *
       * XXX: This function returns fnfErr (file not found) if it encounters a
       * broken alias. Perhaps we should make that look like a dangling symlink
       * instead of returning an error?
       */
      osStatus = FSResolveAliasFileWithMountFlags(&fileRef, FALSE,
                                                  &targetIsFolder, &isAlias,
                                                  kResolveAliasFileNoUI);
      if (osStatus != noErr) {
         Log("%s: could not resolve reference: error %d\n",
                 __FUNCTION__, (int32)osStatus);
         goto oldExit;
      }

      resolvedURL = CFURLCreateFromFSRef(NULL, &fileRef);
      if (!resolvedURL) {
         Log("%s: could not create resolved URL reference from "
                 "resolved filesystem reference\n", __FUNCTION__);
         goto oldExit;
      }

oldExit:
      (void)0; // Need a statement for the label.
#else // __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
      // We are running on 10.5 but the build was targetting 10.6+. Eject!
      NOT_IMPLEMENTED();
#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
   }

   if (resolvedURL) {
      /*
       * This is somewhat convoluted. We want to call
       * CFURLGetFileSystemRepresentation() to get a UTF-8 string representing
       * the target of the alias. But to call
       * CFStringGetMaximumSizeOfFileSystemRepresentation(), we need a CFString,
       * so we make one from the CFURL. Once we've got the max number of bytes
       * for a filename on the filesystem, we allocate some memory and convert
       * the CFURL to a basic UTF-8 string using a call to
       * CFURLGetFileSystemRepresentation().
       */
      resolvedString = CFURLGetString(resolvedURL);
      if (!resolvedString) {
         Log("%s: could not create resolved string reference from "
                 "resolved URL reference\n", __FUNCTION__);
         goto exit;
      }

      maxPath = CFStringGetMaximumSizeOfFileSystemRepresentation(resolvedString);
      *targetName = malloc(maxPath);
      if (*targetName == NULL) {
         Log("%s: could not allocate %"FMTSZ"d bytes of memory for "
                 "target name storage\n", __FUNCTION__, maxPath);
         goto exit;
      }

      success = CFURLGetFileSystemRepresentation(resolvedURL, FALSE,
                                                 *targetName, maxPath);
      if (!success) {
         Log("%s: could not convert and copy resolved URL reference "
                 "into allocated buffer\n", __FUNCTION__);
         goto exit;
      }

      status = 0;
   }

exit:
   if (resolvedURL) {
      CFRelease(resolvedURL);
   }
   if (status != 0) {
      free(*targetName);
      *targetName = NULL;
   }

   return status;

#endif // __APPLE__
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetHiddenAttr --
 *
 *    For Mac hosts and Linux hosts, if a guest is Windows we force the "dot",
 *    files to be treated as hidden too in the Windows client by always setting
 *    the hidden attribute flag.
 *    Currently, this flag cannot be removed by Windows clients.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsGetHiddenAttr(char const *fileName,         // IN:  Input filename
                  HgfsFileAttrInfo *attr)       // OUT: Struct to copy into
{
   char *baseName;

   ASSERT(fileName);
   ASSERT(attr);

   baseName = strrchr(fileName, DIRSEPC);

   if ((baseName != NULL) &&
       (baseName[1] == '.') &&
       (strcmp(&baseName[1], ".") != 0) &&
       (strcmp(&baseName[1], "..") != 0)) {
      attr->mask |= HGFS_ATTR_VALID_FLAGS;
      attr->flags |= HGFS_ATTR_HIDDEN;
      /*
       * The request sets the forced flag so the client knows it is simulated
       * and is not a real attribute, which can only happen on a Windows server.
       * This allows the client to enforce some checks correctly if the flag
       * is real or not.
       * This replicates SMB behavior see bug 292189.
       */
      attr->flags |= HGFS_ATTR_HIDDEN_FORCED;
   } else {
      Bool isHidden = FALSE;
      /*
       * Do not propagate any error returned from HgfsGetHiddenXAttr.
       * Consider that the file is not hidden if can't get hidden attribute for
       * whatever reason; most likely it fails because hidden attribute is not supported
       * by the OS or file system.
       */
      HgfsGetHiddenXAttr(fileName, &isHidden);
      if (isHidden) {
         attr->mask |= HGFS_ATTR_VALID_FLAGS;
         attr->flags |= HGFS_ATTR_HIDDEN;
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsConvertComponentCase --
 *
 *    Do a case insensitive search of a directory for the specified entry. If
 *    a matching entry is found, return it in the convertedComponent argument.
 *
 * Results:
 *    On Success:
 *    Returns 0 and the converted component name in the argument convertedComponent.
 *    The length for the convertedComponent is returned in convertedComponentSize.
 *
 *    On Failure:
 *    Non-zero errno return, with convertedComponent and convertedComponentSize
 *    set to NULL and 0 respectively.
 *
 * Side effects:
 *    On success, allocated memory is returned in convertedComponent and needs
 *    to be freed.
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsConvertComponentCase(char *currentComponent,           // IN
                         const char *dirPath,              // IN
                         const char **convertedComponent,  // OUT
                         size_t *convertedComponentSize)   // OUT
{
   struct dirent *dirent;
   DIR *dir = NULL;
   char *dentryName;
   size_t dentryNameLen;
   char *myConvertedComponent = NULL;
   size_t myConvertedComponentSize;
   int ret;

   ASSERT(currentComponent);
   ASSERT(dirPath);
   ASSERT(convertedComponent);
   ASSERT(convertedComponentSize);

   /* Open the specified directory. */
   dir = Posix_OpenDir(dirPath);
   if (!dir) {
      ret = errno;
      goto exit;
   }

   /*
    * Unicode_CompareIgnoreCase crashes with invalid unicode strings,
    * validate it before passing it to Unicode_* functions.
    */
   if (!Unicode_IsBufferValid(currentComponent, -1, STRING_ENCODING_UTF8)) {
      /* Invalid unicode string, return failure. */
      ret = EINVAL;
      goto exit;
   }

   /*
    * Read all of the directory entries. For each one, convert the name
    * to lower case and then compare it to the lower case component.
    */
   while ((dirent = readdir(dir))) {
      char *dentryNameU;
      int cmpResult;

      dentryName = dirent->d_name;
      dentryNameLen = strlen(dentryName);

      /*
       * Unicode_CompareIgnoreCase crashes with invalid unicode strings,
       * validate and convert it appropriately before passing it to Unicode_*
       * functions.
       */

      if (!Unicode_IsBufferValid(dentryName, dentryNameLen,
                                 STRING_ENCODING_DEFAULT)) {
         /* Invalid unicode string, skip the entry. */
         continue;
      }

      dentryNameU = Unicode_Alloc(dentryName, STRING_ENCODING_DEFAULT);

      cmpResult = Unicode_CompareIgnoreCase(currentComponent, dentryNameU);
      free(dentryNameU);

      if (cmpResult == 0) {
         /*
          * The current directory entry is a case insensitive match to
          * the specified component. Malloc and copy the current directory entry.
          */
         myConvertedComponentSize = dentryNameLen + 1;
         myConvertedComponent = malloc(myConvertedComponentSize);
         if (myConvertedComponent == NULL) {
            ret = errno;
            LOG(4, "%s: failed to malloc myConvertedComponent.\n",
                __FUNCTION__);
            goto exit;
         }
         Str_Strcpy(myConvertedComponent, dentryName, myConvertedComponentSize);

         /* Success. Cleanup and exit. */
         ret = 0;
         *convertedComponentSize = myConvertedComponentSize;
         *convertedComponent = myConvertedComponent;
         goto exit;
      }
   }

   /* We didn't find a match. Failure. */
   ret = ENOENT;

exit:
   if (dir) {
      closedir(dir);
   }
   if (ret) {
      *convertedComponent = NULL;
      *convertedComponentSize = 0;
   }
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsConstructConvertedPath --
 *
 *    Expand the passed string and append the converted path.
 *
 * Results:
 *    Returns 0 if successful, errno on failure. Note that this
 *    function cannot return ENOENT.
 *
 * Side effects:
 *    Reallocs the path.
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsConstructConvertedPath(char **path,                 // IN/OUT
                           size_t *pathSize,            // IN/OUT
                           char *convertedPath,         // IN
                           size_t convertedPathSize)    // IN
{
   char *p;
   size_t convertedPathLen = convertedPathSize - 1;

   ASSERT(path);
   ASSERT(*path);
   ASSERT(convertedPath);
   ASSERT(pathSize);

   p = realloc(*path, *pathSize + convertedPathLen + sizeof (DIRSEPC));
   if (!p) {
      int error = errno;
      LOG(4, "%s: failed to realloc.\n", __FUNCTION__);
      return error;
   }

   *path = p;
   *pathSize += convertedPathLen + sizeof (DIRSEPC);

   /* Copy out the converted component to curDir, and free it. */
   Str_Strncat(p, *pathSize, DIRSEPS, sizeof (DIRSEPS));
   Str_Strncat(p, *pathSize, convertedPath, convertedPathLen);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsCaseInsensitiveLookup --
 *
 *    Do a case insensitive lookup for fileName. Each component past sharePath is
 *    looked-up case-insensitively. Expensive!
 *
 *    NOTE:
 *    shareName is always expected to be a prefix of fileName.
 *
 * Results:
 *    Returns 0 if successful and resolved path for fileName is returned in
 *    convertedFileName and its length (without nul) in convertedFileNameLength.
 *    Otherwise returns non-zero errno with convertedFileName and
 *    convertedFileNameLength set to NULL and 0 respectively.
 *
 * Side effects:
 *    On success, allocated memory is returned in convertedFileName and needs
 *    to be freed.
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsCaseInsensitiveLookup(const char *sharePath,           // IN
                          size_t sharePathLength,          // IN
                          char *fileName,                  // IN
                          size_t fileNameLength,           // IN
                          char **convertedFileName,        // OUT
                          size_t *convertedFileNameLength) // OUT
{
   char *currentComponent;
   char *nextComponent;
   char *curDir;
   size_t curDirSize;
   char *convertedComponent = NULL;
   size_t convertedComponentSize = 0;
   int error = 0;

   ASSERT(sharePath);
   ASSERT(fileName);
   ASSERT(convertedFileName);
   ASSERT(fileNameLength >= sharePathLength);

   curDirSize = sharePathLength + 1;
   curDir = malloc(curDirSize);
   if (!curDir) {
      error = errno;
      LOG(4, "%s: failed to allocate for curDir\n", __FUNCTION__);
      goto exit;
   }
   Str_Strcpy(curDir, sharePath, curDirSize);

   currentComponent = fileName + sharePathLength;
   /* Check there is something beyond the share name. */
   if (*currentComponent == '\0') {
      /*
       * The fileName is the same as sharePath. Nothing else to do.
       * Return the duplicated sharePath string and return.
       */
      goto exit;
   }

   /* Skip a component separator if not in the share path. */
   if (*currentComponent == DIRSEPC) {
      currentComponent += 1;
   }

   while (TRUE) {
      /* Get the next component. */
      nextComponent = strchr(currentComponent, DIRSEPC);
      if (nextComponent != NULL) {
         *nextComponent = '\0';
      }

      /*
       * Try to match the current component against the one in curDir.
       * HgfsConvertComponentCase may return ENOENT. In that case return
       * the path case-converted uptil now (curDir) and append to it the
       * rest of the unconverted path.
       */
      error = HgfsConvertComponentCase(currentComponent, curDir,
                                       (const char **)&convertedComponent,
                                       &convertedComponentSize);
      /* Restore the path separator if we removed it earlier. */
      if (nextComponent != NULL) {
         *nextComponent = DIRSEPC;
      }

      if (error) {
         if (error == ENOENT) {
            /*
             * We could not find the current component so no need to convert it.
             * So it most likely a new path is to be created or an ENOENT genuine error.
             * Copy out the components starting from currentComponent. We do this
             * after replacing DIRSEPC, so all the components following
             * currentComponent gets copied.
             */
            error = HgfsConstructConvertedPath(&curDir, &curDirSize, currentComponent,
                                               strlen(currentComponent) + 1);
         }
         break;
      }

      /* Expand curDir and copy out the converted component. */
      error = HgfsConstructConvertedPath(&curDir, &curDirSize, convertedComponent,
                                         convertedComponentSize);
      if (error) {
         break;
      }

      /* Free the converted component. */
      free(convertedComponent);
      convertedComponent = NULL;

      /* If there is no component after the current one then we are done. */
      if (nextComponent == NULL) {
         /* Set success. */
         error = 0;
         break;
      }

      /*
       * Set the current component pointer to point at the start of the next
       * component.
       */
      currentComponent = nextComponent + 1;
   }

exit:
   /*
    * If the conversion was successful, return the result.
    * The length does NOT include the nul terminator.
    */
   if (error == 0) {
      *convertedFileName = curDir;
      *convertedFileNameLength = curDirSize - 1;
   } else {
      *convertedFileName = NULL;
      *convertedFileNameLength = 0;
      free(curDir);
   }
   free(convertedComponent);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformFilenameLookup --
 *
 *    Perform a fileName lookup of the fileName if requested.
 *
 *    The type of lookup depends on the flags passed. Currently,
 *    case insensitive is checked and if set we lookup the file name.
 *    Otherwise this function assumes the file system is the default
 *    of case sensitive and returns a copy of the passed name.
 *
 * Results:
 *    Returns HGFS_NAME_STATUS_COMPLETE if successful and converted
 *    path for fileName is returned in convertedFileName and it length in
 *    convertedFileNameLength.
 *
 *    Otherwise returns non-zero integer without affecting fileName with
 *    convertedFileName and convertedFileNameLength set to NULL and 0
 *    respectively.
 *
 * Side effects:
 *    On success, allocated memory is returned in convertedFileName and needs
 *    to be freed.
 *
 *-----------------------------------------------------------------------------
 */

HgfsNameStatus
HgfsPlatformFilenameLookup(const char *sharePath,              // IN
                           size_t sharePathLength,             // IN
                           char *fileName,                     // IN
                           size_t fileNameLength,              // IN
                           uint32 caseFlags,                   // IN
                           char **convertedFileName,           // OUT
                           size_t *convertedFileNameLength)    // OUT
{
   int error = 0;
   HgfsNameStatus nameStatus = HGFS_NAME_STATUS_COMPLETE;

   ASSERT(sharePath);
   ASSERT(fileName);
   ASSERT(convertedFileName);
   ASSERT(convertedFileNameLength);

   *convertedFileName = NULL;
   *convertedFileNameLength = 0;

   /*
    * Case-insensitive lookup is expensive, do it only if the flag is set
    * and file is inaccessible using the case passed to us. We use access(2)
    * call to check if the passed case of the file name is correct.
    */

   if (caseFlags == HGFS_FILE_NAME_CASE_INSENSITIVE &&
       Posix_Access(fileName, F_OK) == -1) {
      LOG(4, "%s: Case insensitive lookup, fileName: %s, flags: %u.\n",
          __FUNCTION__, fileName, caseFlags);
      error = HgfsCaseInsensitiveLookup(sharePath, sharePathLength,
                                        fileName, fileNameLength,
                                        convertedFileName,
                                        convertedFileNameLength);

      /*
       * Map the success or an error code.
       */
      switch (error) {
         /*
          * 0 means that HgfsCaseInsensitiveLookup completed
          * successfully built the converted name thus we return
          * HGFS_NAME_STATUS_COMPLETE in this case.
          */
         case 0:
            nameStatus = HGFS_NAME_STATUS_COMPLETE;
            break;
         case ENOTDIR:
            nameStatus = HGFS_NAME_STATUS_NOT_A_DIRECTORY;
            break;
         default:
            nameStatus = HGFS_NAME_STATUS_FAILURE;
            break;
      }
      return nameStatus;
   }

   *convertedFileName = strdup(fileName);
   if (!*convertedFileName) {
      nameStatus = HGFS_NAME_STATUS_OUT_OF_MEMORY;
      LOG(4, "%s: strdup on fileName failed.\n", __FUNCTION__);
   } else {
      *convertedFileNameLength = fileNameLength;
   }
   return nameStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformDoFilenameLookup --
 *
 *   Determines if the file name lookup depending on case flags is required.
 *
 * Results:
 *   TRUE on Linux / Apple.
 *
 * Side effects:
 *   None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPlatformDoFilenameLookup(void)
{
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsEffectivePermissions --
 *
 *    Get permissions that are in efffect for the current user.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsEffectivePermissions(char *fileName,          // IN: Input filename
                         Bool readOnlyShare,      // IN: Share name
                         uint32 *permissions)     // OUT: Effective permissions
{
   *permissions = 0;
   if (Posix_Access(fileName, R_OK) == 0) {
      *permissions |= HGFS_PERM_READ;
   }
   if (Posix_Access(fileName, X_OK) == 0) {
      *permissions |= HGFS_PERM_EXEC;
   }
   if (!readOnlyShare && (Posix_Access(fileName, W_OK) == 0)) {
      *permissions |= HGFS_PERM_WRITE;
   }
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetCreationTime --
 *
 *    Calculates actual or emulated file creation time from stat structure.
 *    Definition of stat structure are different on diferent platforms.
 *    This function hides away all these differences and produces 64 bit value
 *    which should be reported to the client.
 *
 * Results:
 *    Value that should be used as a file creation time stamp.
 *    The resulting timestamp is in platform independent HGFS format.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static uint64
HgfsGetCreationTime(const struct stat *stats)
{
   uint64 creationTime;
   /*
    * Linux doesn't know about creation time; we just use the time
    * of last data modification for the creation time.
    * FreeBSD 5+ supports file creation time.
    *
    * Using mtime when creation time is unavailable to be consistent with SAMBA.
    */

#ifdef __FreeBSD__
   /*
    * FreeBSD: All supported versions have timestamps with nanosecond resolution
    *          and have file creation time.
    */
   creationTime   = HgfsConvertTimeSpecToNtTime(&stats->st_birthtimespec);
#elif defined(__linux__)
   /*
    * Linux: Glibc 2.3+ has st_Xtim.  Glibc 2.1/2.2 has st_Xtime/__unusedX on
    *        same place (see below).  We do not support Glibc 2.0 or older.
    */
#   if (__GLIBC__ == 2) && (__GLIBC_MINOR__ < 3) && !defined(__UCLIBC__)
   /*
    * stat structure is same between glibc 2.3 and older glibcs, just
    * these __unused fields are always zero. If we'll use __unused*
    * instead of zeroes, we get automatically nanosecond timestamps
    * when running on host which provides them.
    */
   creationTime   = HgfsConvertToNtTime(stats->st_mtime, stats->__unused2);
#   else
   creationTime   = HgfsConvertTimeSpecToNtTime(&stats->st_mtim);
#   endif
#elif defined(__APPLE__)
   creationTime   = HgfsConvertTimeSpecToNtTime(&stats->st_birthtimespec);
#else
   /*
    * Solaris: No nanosecond timestamps, no file create timestamp.
    */
   creationTime   = HgfsConvertToNtTime(stats->st_mtime, 0);
#endif
   return creationTime;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsStat --
 *
 *    Wrapper function that invokes stat on Mac OS and on Linux.
 *
 *    Returns filled stat structure and a file creation time. File creation time is
 *    the birthday time for Mac OS and last write time for Linux (which does not support
 *    file creation time).
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsStat(const char* fileName,   // IN: file name
         Bool followLink,        // IN: If true then follow symlink
         struct stat *stats,     // OUT: file attributes
         uint64 *creationTime)   // OUT: file creation time
{
   int error;
#if defined(__APPLE__)
   if (followLink) {
      error = stat(fileName, stats);
   } else {
      error = lstat(fileName, stats);
   }
#else
   if (followLink) {
      error = Posix_Stat(fileName, stats);
   } else {
      error = Posix_Lstat(fileName, stats);
   }
#endif
   *creationTime = HgfsGetCreationTime(stats);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsFStat --
 *
 *    Wrapper function that invokes fstat.
 *
 *    Returns filled stat structure and a file creation time. File creation time is
 *    the birthday time for Mac OS and last write time for Linux (which does not support
 *    file creation time).
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsFStat(int fd,                 // IN: file descriptor
          struct stat *stats,     // OUT: file attributes
          uint64 *creationTime)   // OUT: file creation time
{
   int error = 0;
   if (fstat(fd, stats) < 0) {
      error = errno;
   }
   *creationTime = HgfsGetCreationTime(stats);
   return error;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetSequentialOnlyFlagFromName --
 *
 *    Certain files like 'kallsyms' residing in /proc/ filesystem can be
 *    copied only if they are opened in sequential mode. Check for such files
 *    and set the 'sequential only' flag. This is done by trying to read the file
 *    content using 'pread'. If 'pread' fails with ESPIPE then they are
 *    tagged as 'sequential only' files.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsGetSequentialOnlyFlagFromName(const char *fileName,        // IN
                                  Bool followSymlinks,         // IN: If true then follow symlink
                                  HgfsFileAttrInfo *attr)      // IN/OUT
{
#if defined(__linux__) || defined(__APPLE__)
   int fd;
   int openFlags;

   if ((NULL == fileName) || (NULL == attr)) {
      return;
   }

   /*
    * We're not interested in creating a new file. So let's just get the
    * flags for a simple open request. This really should always work.
    */
   HgfsServerGetOpenFlags(0, &openFlags);

   /*
    * By default we don't follow symlinks, O_NOFOLLOW is always set.
    * Unset it if followSymlinks config option is specified.
    */
   if (followSymlinks) {
      openFlags &= ~O_NOFOLLOW;
   }

   /*
    * Checking for a FIFO we open in nonblocking mode. In this case, opening for
    * read only will succeed even if no-one has opened on the write side yet,
    * opening for write only will fail with ENXIO (no such device or address)
    * unless the other end has already been opened.
    * Note, Under Linux, opening a FIFO for read and write will succeed both
    * in blocking and nonblocking mode. POSIX leaves this behavior undefined.
    */
   fd = Posix_Open(fileName, openFlags | O_RDONLY);
   if (fd < 0) {
      LOG(4, "%s: Couldn't open the file \"%s\"\n", __FUNCTION__, fileName);
      return;
   }
   HgfsGetSequentialOnlyFlagFromFd(fd, attr);
   close(fd);
   return;
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetSequentialOnlyFlagFromFd --
 *
 *    Certain files like 'kallsyms' residing in /proc/ filesystem can be
 *    copied only if they are opened in sequential mode. Check for such files
 *    and set the 'sequential only' flag. This is done by trying to read the file
 *    content using 'pread'. If 'pread' fails with ESPIPE then they are
 *    tagged as 'sequential only' files.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsGetSequentialOnlyFlagFromFd(int fd,                     // IN
                                HgfsFileAttrInfo *attr)     // IN/OUT
{
#if defined(__linux__) || defined(__APPLE__)
   int error;
   char buffer[2];
   struct stat stats;

   if (NULL == attr) {
      return;
   }

   if (fstat(fd, &stats) < 0) {
      return;
   }

   if (S_ISDIR(stats.st_mode) || S_ISLNK(stats.st_mode)) {
      return;
   }

   /*
    * At this point in the code, we are not reading any amount of data from the
    * file. We just want to check the behavior of pread. Since we are not
    * reading any data, we can call pread with size specified as 0.
    */
   error = pread(fd, buffer, 0, 0);
   LOG(4, "%s: pread returned %d, errno %d\n", __FUNCTION__, error, errno);
   if ((-1 == error) && (ESPIPE == errno)) {
      LOG(4, "%s: Marking the file as 'Sequential only' file\n", __FUNCTION__);
      attr->flags |= HGFS_ATTR_SEQUENTIAL_ONLY;
   }

   return;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformGetattrFromName --
 *
 *    Performs a stat operation on the given filename, and, if it is a symlink,
 *    allocates the target filename on behalf of the caller and performs a
 *    readlink to get it. If not a symlink, the targetName argument is
 *    untouched. Does necessary translation between Unix file stats and the
 *    HgfsFileAttrInfo formats.
 *    NOTE: The function is different from HgfsGetAttrFromId: this function returns
 *    effectve permissions while HgfsGetAttrFromId does not.
 *    The reason for this asymmetry is that effective permissions are needed
 *    to get a new handle. If the file is already opened then
 *    getting effective permissions does not have any value. However getting
 *    effective permissions would hurt perfomance and should be avoided.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformGetattrFromName(char *fileName,                 // IN/OUT:  Input filename
                            HgfsShareOptions configOptions, // IN: Share config options
                            char *shareName,                // IN: Share name
                            HgfsFileAttrInfo *attr,         // OUT: Struct to copy into
                            char **targetName)              // OUT: Symlink target
{
   HgfsInternalStatus status = 0;
   struct stat stats;
   int error;
   char *myTargetName = NULL;
   uint64 creationTime;
   Bool followSymlinks;

   ASSERT(fileName);
   ASSERT(attr);

   LOG(4, "%s: getting attrs for \"%s\"\n", __FUNCTION__, fileName);
   followSymlinks = HgfsServerPolicy_IsShareOptionSet(configOptions,
                                                      HGFS_SHARE_FOLLOW_SYMLINKS),

   error = HgfsStat(fileName,
                    followSymlinks,
                    &stats,
                    &creationTime);
   if (error) {
      status = errno;
      LOG(4, "%s: error stating file: %s\n", __FUNCTION__,
          Err_Errno2String(status));
      goto exit;
   }

   /*
    * Deal with the file type returned from lstat(2). We currently support
    * regular files, directories, and symlinks. On Mac OS, we'll additionally
    * treat finder aliases as symlinks.
    */
   if (S_ISDIR(stats.st_mode)) {
      attr->type = HGFS_FILE_TYPE_DIRECTORY;
      LOG(4, "%s: is a directory\n", __FUNCTION__);
   } else if (S_ISLNK(stats.st_mode)) {
      attr->type = HGFS_FILE_TYPE_SYMLINK;
      LOG(4, "%s: is a symlink\n", __FUNCTION__);

      /*
       * In the case of a symlink, we should populate targetName if the
       * caller asked. Use st_size and readlink() to do so.
       */
      if (targetName != NULL) {

         myTargetName = Posix_ReadLink(fileName);
         if (myTargetName == NULL) {
            error = errno;
            LOG(4, "%s: readlink returned wrong size\n", __FUNCTION__);

            /*
             * Because of an unavoidable race between the lstat(2) and the
             * readlink(2), the symlink target may have lengthened and we may
             * not have read the entire link. If that happens, just return
             * "out of memory".
             */
            status = error ? error : ENOMEM;
            goto exit;
         }
      }
   } else {
      /*
       * Now is a good time to check if the file was an alias. If so, we'll
       * treat it as a symlink.
       *
       * XXX: If HgfsGetattrResolveAlias fails, we'll treat the file as a
       * regular file. This isn't completely correct (the function may have
       * failed because we're out of memory), but it's better than having to
       * call LScopyItemInfoForRef for each file, which may negatively affect
       * performance. See:
       *
       * http://lists.apple.com/archives/carbon-development/2001/Nov/msg00007.html
       */

      LOG(4, "%s: NOT a directory or symlink\n", __FUNCTION__);
      if (HgfsGetattrResolveAlias(fileName, &myTargetName)) {
         LOG(4, "%s: could not resolve file aliases\n", __FUNCTION__);
      }
      attr->type = HGFS_FILE_TYPE_REGULAR;
      if (myTargetName != NULL) {
         /*
          * At this point the alias target has been successfully resolved. If
          * the alias target is inside the same shared folder then convert it
          * to relative path. Converting to a relative path produces a symlink
          * that points to the target file in the guest OS. If the target lies
          * outside the shared folder then treat it the same way as if alias
          * has not been resolved - we drop the error and treat as a regular file!
          */
         HgfsNameStatus nameStatus;
         size_t sharePathLen;
         const char* sharePath;
         nameStatus = HgfsServerPolicy_GetSharePath(shareName,
                                                    strlen(shareName),
                                                    HGFS_OPEN_MODE_READ_ONLY,
                                                    &sharePathLen,
                                                    &sharePath);
         if (nameStatus == HGFS_NAME_STATUS_COMPLETE &&
             sharePathLen < strlen(myTargetName) &&
             Str_Strncmp(sharePath, myTargetName, sharePathLen) == 0) {
            char *relativeName;

            relativeName = HgfsServerGetTargetRelativePath(fileName, myTargetName);
            free(myTargetName);
            myTargetName = relativeName;

            if (myTargetName != NULL) {
               /*
                * Let's mangle the permissions and size of the file so that
                * it more closely resembles a symlink. The size should be
                * the length of the target name (not including the
                * nul-terminator), and the permissions should be 777.
                */
               stats.st_size = strlen(myTargetName);
               stats.st_mode |= ACCESSPERMS;
               attr->type = HGFS_FILE_TYPE_SYMLINK;
            } else {
               LOG(4, "%s: out of memory\n", __FUNCTION__);
            }
         } else {
             LOG(4, "%s: alias target is outside shared folder\n",
                 __FUNCTION__);
         }
      }
   }

   if (myTargetName != NULL && targetName != NULL) {
#if defined(__APPLE__)
      /*
       * HGFS clients will expect filenames in unicode normal form C
       * (precomposed) so Mac hosts must convert from normal form D
       * (decomposed).
       */

      if (!CodeSet_Utf8FormDToUtf8FormC(myTargetName, strlen(myTargetName),
                                        targetName, NULL)) {
         LOG(4, "%s: Unable to normalize form C \"%s\"\n",
             __FUNCTION__, myTargetName);
         status = HgfsPlatformConvertFromNameStatus(HGFS_NAME_STATUS_FAILURE);
         goto exit;
      }
#else
      *targetName = myTargetName;
      myTargetName = NULL;
#endif
      LOG(4, "%s: symlink target \"%s\"\n", __FUNCTION__, *targetName);
   }

   HgfsStatToFileAttr(&stats, &creationTime, attr);

   /*
    * In the case we have a Windows client, force the hidden flag.
    * This will be ignored by Linux, Solaris clients.
    */
   HgfsGetHiddenAttr(fileName, attr);

   HgfsGetSequentialOnlyFlagFromName(fileName, followSymlinks, attr);

   /* Get effective permissions if we can */
   if (!(S_ISLNK(stats.st_mode))) {
      HgfsOpenMode shareMode;
      uint32 permissions;
      HgfsNameStatus nameStatus;
      nameStatus = HgfsServerPolicy_GetShareMode(shareName, strlen(shareName),
                                                 &shareMode);
      if (nameStatus == HGFS_NAME_STATUS_COMPLETE &&
          HgfsEffectivePermissions(fileName,
                                   shareMode == HGFS_OPEN_MODE_READ_ONLY,
                                   &permissions) == 0) {
         attr->mask |= HGFS_ATTR_VALID_EFFECTIVE_PERMS;
         attr->effectivePerms = permissions;
      }
   }

exit:
   free(myTargetName);
   return status;
}

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformGetattrFromFd --
 *
 *    Performs a stat operation on the given file desc.
 *    Does necessary translation between Unix file stats and the
 *    HgfsFileAttrInfo formats.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformGetattrFromFd(fileDesc fileDesc,        // IN:  file descriptor
                          HgfsSessionInfo *session, // IN:  session info
                          HgfsFileAttrInfo *attr)   // OUT: FileAttrInfo to copy into
{
   HgfsInternalStatus status = 0;
   struct stat stats;
   int error;
   HgfsOpenMode shareMode;
   HgfsHandle handle = HGFS_INVALID_HANDLE;
   char *fileName = NULL;
   size_t fileNameLen;
   uint64 creationTime;

   ASSERT(attr);
   ASSERT(session);

   LOG(4, "%s: getting attrs for %u\n", __FUNCTION__, fileDesc);

   error = HgfsFStat(fileDesc, &stats, &creationTime);
   if (error) {
      LOG(4, "%s: error stating file: %s\n", __FUNCTION__,
          Err_Errno2String(error));
      status = error;
      goto exit;
   }

   /*
    * For now, everything that isn't a directory or symlink is a regular
    * file.
    */
   if (S_ISDIR(stats.st_mode)) {
      attr->type = HGFS_FILE_TYPE_DIRECTORY;
      LOG(4, "%s: is a directory\n", __FUNCTION__);
   } else if (S_ISLNK(stats.st_mode)) {
      attr->type = HGFS_FILE_TYPE_SYMLINK;
      LOG(4, "%s: is a symlink\n", __FUNCTION__);

   } else {
      attr->type = HGFS_FILE_TYPE_REGULAR;
      LOG(4, "%s: NOT a directory or symlink\n", __FUNCTION__);
   }

   HgfsStatToFileAttr(&stats, &creationTime, attr);

   /*
    * XXX - Correct share mode checking should be fully implemented.
    *
    * For now, we must ensure that the client only sees read only
    * attributes when the share is read only. This allows the client
    * to make decisions to fail write/delete operations.
    * It is required by clients who use file handles that
    * are cached, for setting attributes, renaming and deletion.
    */

   if (!HgfsFileDesc2Handle(fileDesc, session, &handle)) {
      LOG(4, "%s: could not get HGFS handle for fd %u\n", __FUNCTION__,
          fileDesc);
      status = EBADF;
      goto exit;
   }

   if (!HgfsHandle2ShareMode(handle, session, &shareMode)) {
      LOG(4, "%s: could not get share mode fd %u\n", __FUNCTION__, fileDesc);
      status = EBADF;
      goto exit;
   }

   if (!HgfsHandle2FileName(handle, session, &fileName, &fileNameLen)) {
      LOG(4, "%s: could not map cached target file handle %u\n",
          __FUNCTION__, handle);
      status = EBADF;
      goto exit;
   }

   /*
    * In the case we have a Windows client, force the hidden flag.
    * This will be ignored by Linux, Solaris clients.
    */
   HgfsGetHiddenAttr(fileName, attr);

   HgfsGetSequentialOnlyFlagFromFd(fileDesc, attr);

   if (shareMode == HGFS_OPEN_MODE_READ_ONLY) {
      /*
       * Share does not allow write, so tell the client
       * everything is read only.
       */
      if (attr->mask & HGFS_ATTR_VALID_OWNER_PERMS) {
         attr->ownerPerms &= ~HGFS_PERM_WRITE;
      }
      if (attr->mask & HGFS_ATTR_VALID_GROUP_PERMS) {
         attr->groupPerms &= ~HGFS_PERM_WRITE;
      }
      if (attr->mask & HGFS_ATTR_VALID_OTHER_PERMS) {
         attr->otherPerms &= ~HGFS_PERM_WRITE;
      }
   }

exit:
   free(fileName);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsStatToFileAttr --
 *
 *    Does necessary translation between Unix file stats and the
 *    HgfsFileAttrInfo formats.
 *    It expects creationTime to be in platform-independent HGFS format and
 *    stats in a platform-specific stat format.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsStatToFileAttr(struct stat *stats,       // IN: stat information
                   uint64 *creationTime,     // IN: file creation time
                   HgfsFileAttrInfo *attr)   // OUT: FileAttrInfo to copy into
{
   attr->size           = stats->st_size;
   attr->allocationSize = stats->st_blocks * 512;
   attr->creationTime   = *creationTime;

#ifdef __FreeBSD__
   /*
    * FreeBSD: All supported versions have timestamps with nanosecond resolution
    *          and have file creation time.
    */
   attr->accessTime     = HgfsConvertTimeSpecToNtTime(&stats->st_atimespec);
   attr->writeTime      = HgfsConvertTimeSpecToNtTime(&stats->st_mtimespec);
   attr->attrChangeTime = HgfsConvertTimeSpecToNtTime(&stats->st_ctimespec);
#elif defined(__linux__)
   /*
    * Linux: Glibc 2.3+ has st_Xtim.  Glibc 2.1/2.2 has st_Xtime/__unusedX on
    *        same place (see below).  We do not support Glibc 2.0 or older.
    */
#   if (__GLIBC__ == 2) && (__GLIBC_MINOR__ < 3) && !defined(__UCLIBC__)
   /*
    * stat structure is same between glibc 2.3 and older glibcs, just
    * these __unused fields are always zero. If we'll use __unused*
    * instead of zeroes, we get automatically nanosecond timestamps
    * when running on host which provides them.
    */
   attr->accessTime     = HgfsConvertToNtTime(stats->st_atime, stats->__unused1);
   attr->writeTime      = HgfsConvertToNtTime(stats->st_mtime, stats->__unused2);
   attr->attrChangeTime = HgfsConvertToNtTime(stats->st_ctime, stats->__unused3);
#   else
   attr->accessTime     = HgfsConvertTimeSpecToNtTime(&stats->st_atim);
   attr->writeTime      = HgfsConvertTimeSpecToNtTime(&stats->st_mtim);
   attr->attrChangeTime = HgfsConvertTimeSpecToNtTime(&stats->st_ctim);
#   endif
#else
   /*
    * Solaris, Mac OS: No nanosecond timestamps.
    */
   attr->accessTime     = HgfsConvertToNtTime(stats->st_atime, 0);
   attr->writeTime      = HgfsConvertToNtTime(stats->st_mtime, 0);
   attr->attrChangeTime = HgfsConvertToNtTime(stats->st_ctime, 0);
#endif

   attr->specialPerms   = (stats->st_mode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9;
   attr->ownerPerms     = (stats->st_mode & S_IRWXU) >> 6;
   attr->groupPerms     = (stats->st_mode & S_IRWXG) >> 3;
   attr->otherPerms     = stats->st_mode & S_IRWXO;
   LOG(4, "%s: done, permissions %o%o%o%o, size %"FMT64"u\n", __FUNCTION__,
       attr->specialPerms, attr->ownerPerms, attr->groupPerms,
       attr->otherPerms, attr->size);
#ifdef __FreeBSD__
#   if !defined(VM_X86_64) && !defined(VM_ARM_64)
#      define FMTTIMET ""
#   else
#      define FMTTIMET "l"
#   endif
#else
#   define FMTTIMET "l"
#endif
   LOG(4, "access: %"FMTTIMET"d/%"FMT64"u \nwrite: %"FMTTIMET"d/%"FMT64"u \n"
       "attr: %"FMTTIMET"d/%"FMT64"u\n",
       stats->st_atime, attr->accessTime, stats->st_mtime, attr->writeTime,
       stats->st_ctime, attr->attrChangeTime);
#undef FMTTIMET

   attr->userId = stats->st_uid;
   attr->groupId = stats->st_gid;
   attr->hostFileId = stats->st_ino;
   attr->volumeId = stats->st_dev;
   attr->mask = HGFS_ATTR_VALID_TYPE |
      HGFS_ATTR_VALID_SIZE |
      HGFS_ATTR_VALID_ALLOCATION_SIZE |
      HGFS_ATTR_VALID_CREATE_TIME |
      HGFS_ATTR_VALID_ACCESS_TIME |
      HGFS_ATTR_VALID_WRITE_TIME |
      HGFS_ATTR_VALID_CHANGE_TIME |
      HGFS_ATTR_VALID_SPECIAL_PERMS |
      HGFS_ATTR_VALID_OWNER_PERMS |
      HGFS_ATTR_VALID_GROUP_PERMS |
      HGFS_ATTR_VALID_OTHER_PERMS |
      HGFS_ATTR_VALID_USERID |
      HGFS_ATTR_VALID_GROUPID |
      HGFS_ATTR_VALID_FILEID |
      HGFS_ATTR_VALID_VOLID;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSetattrMode --
 *
 *    Set the permissions based on stat and attributes.
 *
 * Results:
 *    TRUE if permissions have changed.
 *    FALSE otherwise.
 *
 *    Note that newPermissions is always set.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsSetattrMode(struct stat *statBuf,       // IN: stat info
                HgfsFileAttrInfo *attr,     // IN: attrs to set
                mode_t *newPermissions)     // OUT: new perms
{
   Bool permsChanged = FALSE;

   ASSERT(statBuf);
   ASSERT(attr);
   ASSERT(newPermissions);

   *newPermissions = 0;
   if (attr->mask & HGFS_ATTR_VALID_SPECIAL_PERMS) {
      *newPermissions |= attr->specialPerms << 9;
      permsChanged = TRUE;
   } else {
      *newPermissions |= statBuf->st_mode & (S_ISUID | S_ISGID | S_ISVTX);
   }
   if (attr->mask & HGFS_ATTR_VALID_OWNER_PERMS) {
      *newPermissions |= attr->ownerPerms << 6;
      permsChanged = TRUE;
   } else {
      *newPermissions |= statBuf->st_mode & S_IRWXU;
   }
   if (attr->mask & HGFS_ATTR_VALID_GROUP_PERMS) {
      *newPermissions |= attr->groupPerms << 3;
      permsChanged = TRUE;
   } else {
      *newPermissions |= statBuf->st_mode & S_IRWXG;
   }
   if (attr->mask & HGFS_ATTR_VALID_OTHER_PERMS) {
      *newPermissions |= attr->otherPerms;
      permsChanged = TRUE;
   } else {
      *newPermissions |= statBuf->st_mode & S_IRWXO;
   }
   return permsChanged;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSetattrOwnership --
 *
 *    Set the user and group ID based the attributes.
 *
 * Results:
 *    TRUE if ownership has changed.
 *    FALSE otherwise.
 *
 *    Note that newUid/newGid are always set.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsSetattrOwnership(HgfsFileAttrInfo *attr,     // IN: attrs to set
                     uid_t *newUid,              // OUT: new user ID
                     gid_t *newGid)              // OUT: new group ID
{
   Bool idChanged = FALSE;

   ASSERT(attr);
   ASSERT(newUid);
   ASSERT(newGid);

   *newUid = *newGid = -1;

   if (attr->mask & HGFS_ATTR_VALID_USERID) {
      *newUid = attr->userId;
      idChanged = TRUE;
   }

   if (attr->mask & HGFS_ATTR_VALID_GROUPID) {
      *newGid = attr->groupId;
      idChanged = TRUE;
   }

   return idChanged;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSetattrTimes --
 *
 *    Set the time stamps based on stat and attributes.
 *
 * Results:
 *    Zero on success. accessTime/modTime contain new times.
 *    Non-zero on failure.
 *
 *    Note that timesChanged is always set.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsSetattrTimes(struct stat *statBuf,       // IN: stat info
                 HgfsFileAttrInfo *attr,     // IN: attrs to set
                 HgfsAttrHint hints,         // IN: attr hints
                 Bool useHostTime,           // IN: use the current host time
                 struct timeval *accessTime, // OUT: access time
                 struct timeval *modTime,    // OUT: modification time
                 Bool *timesChanged)         // OUT: times changed
{
   HgfsInternalStatus status = 0;
   int error;

   ASSERT(statBuf);
   ASSERT(attr);
   ASSERT(accessTime);
   ASSERT(modTime);
   ASSERT(timesChanged);

   *timesChanged = FALSE;

   if (attr->mask & (HGFS_ATTR_VALID_ACCESS_TIME |
                    HGFS_ATTR_VALID_WRITE_TIME)) {

      /*
       * utime(2) only lets you update both atime and mtime at once, so
       * if either one needs updating, first we get the current times
       * and call utime with some combination of the current and new
       * times. This is a bit racy because someone else could update
       * one of them in between, but this seems to be how "touch" does
       * things, so we'll go with it. [bac]
       */

      if ((attr->mask & (HGFS_ATTR_VALID_ACCESS_TIME |
                        HGFS_ATTR_VALID_WRITE_TIME))
          != (HGFS_ATTR_VALID_ACCESS_TIME | HGFS_ATTR_VALID_WRITE_TIME)) {

         /*
          * XXX Set also usec from nsec stat fields.
          */

         accessTime->tv_sec = statBuf->st_atime;
         accessTime->tv_usec = 0;
         modTime->tv_sec = statBuf->st_mtime;
         modTime->tv_usec = 0;
      }

      /*
       * If times need updating, we either use the guest-provided time or the
       * host time.  HGFS_ATTR_HINT_SET_x_TIME_ will be set if we should use
       * the guest time, and useHostTime will be TRUE if the config
       * option to always use host time is set.
       */
      if (attr->mask & HGFS_ATTR_VALID_ACCESS_TIME) {
         if (!useHostTime && (hints & HGFS_ATTR_HINT_SET_ACCESS_TIME)) {
            /* Use the guest-provided time */
            struct timespec ts;

            HgfsConvertFromNtTimeNsec(&ts, attr->accessTime);
            accessTime->tv_sec = ts.tv_sec;
            accessTime->tv_usec = ts.tv_nsec / 1000;
         } else {
            /* Use the host's time */
            struct timeval tv;

            if (gettimeofday(&tv, NULL) != 0) {
               error = errno;
               LOG(4, "%s: gettimeofday error: %s\n", __FUNCTION__,
                   Err_Errno2String(error));
               status = error;
               goto exit;
            }

            accessTime->tv_sec = tv.tv_sec;
            accessTime->tv_usec = tv.tv_usec;
         }
      }

      if (attr->mask & HGFS_ATTR_VALID_WRITE_TIME) {
         if (!useHostTime && (hints & HGFS_ATTR_HINT_SET_WRITE_TIME)) {
            struct timespec ts;

            HgfsConvertFromNtTimeNsec(&ts, attr->writeTime);
            modTime->tv_sec = ts.tv_sec;
            modTime->tv_usec = ts.tv_nsec / 1000;
         } else {
            struct timeval tv;

            if (gettimeofday(&tv, NULL) != 0) {
               error = errno;
               LOG(4, "%s: gettimeofday error: %s\n", __FUNCTION__,
                   Err_Errno2String(error));
               status = error;
               goto exit;
            }

            modTime->tv_sec = tv.tv_sec;
            modTime->tv_usec = tv.tv_usec;
         }
      }
      *timesChanged = TRUE;
   }

exit:
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformSetattrFromFd --
 *
 *    Handle a Setattr request by file descriptor.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformSetattrFromFd(HgfsHandle file,          // IN: file descriptor
                          HgfsSessionInfo *session, // IN: session info
                          HgfsFileAttrInfo *attr,   // OUT: attrs to set
                          HgfsAttrHint hints,       // IN: attr hints
                          Bool useHostTime)         // IN: use current host time
{
   HgfsInternalStatus status = 0, timesStatus;
   int error;
   struct stat statBuf;
   struct timeval times[2];
   mode_t newPermissions;
   uid_t newUid = -1;
   gid_t newGid = -1;
   Bool permsChanged = FALSE;
   Bool timesChanged = FALSE;
   Bool idChanged = FALSE;
   int fd;
   HgfsLockType serverLock;

   ASSERT(session);
   ASSERT(file != HGFS_INVALID_HANDLE);

   status = HgfsPlatformGetFd(file, session, FALSE, &fd);
   if (status != 0) {
      LOG(4, "%s: Could not get file descriptor\n", __FUNCTION__);
      goto exit;
   }

   /* We need the old stats so that we can preserve times. */
   if (fstat(fd, &statBuf) == -1) {
      error = errno;
      LOG(4, "%s: error stating file %u: %s\n", __FUNCTION__,
          fd, Err_Errno2String(error));
      status = error;
      goto exit;
   }

   /*
    * Try to make each requested attribute change. In the event that
    * one operation fails, we still attempt to perform any other
    * operations that the driver requested. We return success only
    * if all operations succeeded.
    */

   idChanged = HgfsSetattrOwnership(attr, &newUid, &newGid);
   if (idChanged) {
      LOG(4, "%s: set uid %"FMTUID" and gid %"FMTUID"\n", __FUNCTION__,
          newUid, newGid);
      if (fchown(fd, newUid, newGid) < 0) {
         error = errno;
         LOG(4, "%s: error chowning file %u: %s\n", __FUNCTION__,
             fd, Err_Errno2String(error));
         status = error;
      }
   }

   /*
    * Set permissions based on what we got in the packet. If we didn't get
    * a particular bit, use the existing permissions. In that case we don't
    * toggle permsChanged since it should not influence our decision of
    * whether to actually call chmod or not.
    *
    * NOTE: Setting ownership clears SUID and SGID bits, therefore set the
    * file permissions after setting ownership.
    */
   permsChanged = HgfsSetattrMode(&statBuf, attr, &newPermissions);
   if (permsChanged) {
      LOG(4, "%s: set mode %o\n", __FUNCTION__, (unsigned)newPermissions);

      if (fchmod(fd, newPermissions) < 0) {
         error = errno;
         LOG(4, "%s: error chmoding file %u: %s\n", __FUNCTION__,
             fd, Err_Errno2String(error));
         status = error;
      }
   }

   if (attr->mask & HGFS_ATTR_VALID_SIZE) {
      /*
       * XXX: Truncating the file will trigger an oplock break. The client
       * should have predicted this and removed the oplock prior to sending
       * the truncate request. At this point, the server must safeguard itself
       * against deadlock.
       */
      if (!HgfsHandle2ServerLock(file, session, &serverLock)) {
         LOG(4, "%s: File handle is no longer valid.\n", __FUNCTION__);
         status = EBADF;
      } else if (serverLock != HGFS_LOCK_NONE) {
         LOG(4, "%s: Client attempted to truncate an oplocked file\n",
             __FUNCTION__);
         status = EBUSY;
      } else if (ftruncate(fd, attr->size) < 0) {
         error = errno;
         LOG(4, "%s: error truncating file %u: %s\n", __FUNCTION__,
             fd, Err_Errno2String(error));
         status = error;
      } else {
         LOG(4, "%s: set size %"FMT64"u\n", __FUNCTION__, attr->size);
      }
   }

   /* Setting hidden attribute for symlink itself is not supported. */
   if ((attr->mask & HGFS_ATTR_VALID_FLAGS) && !S_ISLNK(statBuf.st_mode)) {
       char *localName;
       size_t localNameSize;
       if (HgfsHandle2FileName(file, session, &localName, &localNameSize)) {
          status = HgfsSetHiddenXAttr(localName,
                                      (attr->flags & HGFS_ATTR_HIDDEN) != 0,
                                      newPermissions);
          free(localName);
       }
   }

   timesStatus = HgfsSetattrTimes(&statBuf, attr, hints, useHostTime,
                                  &times[0], &times[1], &timesChanged);
   if (timesStatus == 0 && timesChanged) {
      uid_t uid = (uid_t)-1;
      Bool switchToSuperUser = FALSE;

      LOG(4, "%s: setting new times\n", __FUNCTION__);

      /*
       * If the VMX is neither the file owner nor running as root, return an error.
       * Otherwise if we are not the file owner switch to superuser briefly
       * to set the files times using futimes.
       */

      if (geteuid() != statBuf.st_uid) {
         /* We are not the file owner. Check if we are running as root. */
         if (!Id_IsSuperUser()) {
            LOG(4, "%s: only owner of file %u or root can call futimes\n",
                __FUNCTION__, fd);
            /* XXX: Linux kernel says both EPERM and EACCES are valid here. */
            status = EPERM;
            goto exit;
         }
         uid = Id_BeginSuperUser();
         switchToSuperUser = TRUE;
      }
      /*
       * XXX Newer glibc provide also lutimes() and futimes()
       *     when we politely ask with -D_GNU_SOURCE -D_BSD_SOURCE
       */

      if (futimes(fd, times) < 0) {
         if (!switchToSuperUser) {
            /*
             * Check bug 718252. If futimes() fails, switch to
             * superuser briefly and try futimes() one more time.
             */
            uid = Id_BeginSuperUser();
            switchToSuperUser = TRUE;
            if (futimes(fd, times) < 0) {
               error = errno;
               LOG(4, "%s: Executing futimes as owner on file: %u "
                   "failed with error: %s\n", __FUNCTION__,
                   fd, Err_Errno2String(error));
               status = error;
            }
         } else {
            error = errno;
            LOG(4, "%s: Executing futimes as superuser on file: %u "
                "failed with error: %s\n", __FUNCTION__,
                fd, Err_Errno2String(error));
            status = error;
         }
      }
      if (switchToSuperUser) {
         Id_EndSuperUser(uid);
      }
   } else if (timesStatus != 0) {
      status = timesStatus;
   }

exit:
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformSetattrFromName --
 *
 *    Handle a Setattr request by name.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */
HgfsInternalStatus
HgfsPlatformSetattrFromName(char *localName,                // IN: Name
                            HgfsFileAttrInfo *attr,         // IN: attrs to set
                            HgfsShareOptions configOptions, // IN: share options
                            HgfsAttrHint hints,             // IN: attr hints
                            Bool useHostTime)               // IN: use current host time
{
   HgfsInternalStatus status = 0, timesStatus;
   struct stat statBuf;
   struct timeval times[2];
   mode_t newPermissions;
   uid_t newUid = -1;
   gid_t newGid = -1;
   Bool permsChanged = FALSE;
   Bool timesChanged = FALSE;
   Bool idChanged = FALSE;
   int error;

   ASSERT(localName);


   if (!HgfsServerPolicy_IsShareOptionSet(configOptions,
                                          HGFS_SHARE_FOLLOW_SYMLINKS)) {
      /*
       * If followSymlink option is not set, verify that the pathname isn't a
       * symlink. Some of the following syscalls (chmod, for example) will
       * follow a link. So we need to verify the final component too. The
       * parent has already been verified in HgfsServerGetAccess.
       *
       * XXX: This is racy. But clients interested in preventing a race should
       * have sent us a Setattr packet with a valid HGFS handle.
       */
      if (File_IsSymLink(localName)) {
         LOG(4, "%s: pathname contains a symlink\n", __FUNCTION__);
         status = EINVAL;
         goto exit;
      }
   }

   LOG(4, "%s: setting attrs for \"%s\"\n", __FUNCTION__, localName);

   /* We need the old stats so that we can preserve times. */
   if (Posix_Lstat(localName, &statBuf) == -1) {
      error = errno;
      LOG(4, "%s: error stating file \"%s\": %s\n", __FUNCTION__,
          localName, Err_Errno2String(error));
      status = error;
      goto exit;
   }

   /*
    * Try to make each requested attribute change. In the event that
    * one operation fails, we still attempt to perform any other
    * operations that the driver requested. We return success only
    * if all operations succeeded.
    */

   idChanged = HgfsSetattrOwnership(attr, &newUid, &newGid);
   /*
    * Chown changes the uid and gid together. If one of them should
    * not be changed, we pass in -1.
    */
   if (idChanged) {
      if (Posix_Lchown(localName, newUid, newGid) < 0) {
         error = errno;
         LOG(4, "%s: error chowning file \"%s\": %s\n", __FUNCTION__,
             localName, Err_Errno2String(error));
         status = error;
      }
   }

   /*
    * Set permissions based on what we got in the packet. If we didn't get
    * a particular bit, use the existing permissions. In that case we don't
    * toggle permsChanged since it should not influence our decision of
    * whether to actually call chmod or not.
    *
    * NOTE: Setting ownership clears SUID and SGID bits, therefore set the
    * file permissions after setting ownership.
    */
   permsChanged = HgfsSetattrMode(&statBuf, attr, &newPermissions);
   if (permsChanged) {
      LOG(4, "%s: set mode %o\n", __FUNCTION__, (unsigned)newPermissions);

      if (Posix_Chmod(localName, newPermissions) < 0) {
         error = errno;
         LOG(4, "%s: error chmoding file \"%s\": %s\n", __FUNCTION__,
             localName, Err_Errno2String(error));
         status = error;
      }
   }

   if (attr->mask & HGFS_ATTR_VALID_SIZE) {
      if (Posix_Truncate(localName, attr->size) < 0) {
         error = errno;
         LOG(4, "%s: error truncating file \"%s\": %s\n", __FUNCTION__,
             localName, Err_Errno2String(error));
         status = error;
      } else {
         LOG(4, "%s: set size %"FMT64"u\n", __FUNCTION__, attr->size);
      }
   }

   if (attr->mask & HGFS_ATTR_VALID_FLAGS) {
      status = HgfsSetHiddenXAttr(localName,
                                  (attr->flags & HGFS_ATTR_HIDDEN) != 0,
                                  newPermissions);
   }

   timesStatus = HgfsSetattrTimes(&statBuf, attr, hints, useHostTime,
                                  &times[0], &times[1], &timesChanged);
   if (timesStatus == 0 && timesChanged) {
      /*
       * XXX Newer glibc provide also lutimes() and futimes()
       *     when we politely ask with -D_GNU_SOURCE -D_BSD_SOURCE
       */

      if (Posix_Utimes(localName, times) < 0) {
         error = errno;
         LOG(4, "%s: utimes error on file \"%s\": %s\n", __FUNCTION__,
             localName, Err_Errno2String(error));
         status = error;
      }
   } else if (timesStatus != 0) {
      status = timesStatus;
   }

exit:
   return status;
}


HgfsInternalStatus
HgfsPlatformWriteWin32Stream(HgfsHandle file,        // IN: packet header
                             char *dataToWrite,      // IN: request type
                             size_t requiredSize,
                             Bool doSecurity,
                             uint32  *actualSize,
                             HgfsSessionInfo *session)
{
   return EPROTO;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformVDirStatsFs --
 *
 *    Handle a statfs (query volume information) request for a virtual folder.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS or an appropriate error code.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformVDirStatsFs(HgfsSessionInfo *session,  // IN: session info
                        HgfsNameStatus nameStatus, // IN:
                        VolumeInfoType infoType,   // IN:
                        uint64 *outFreeBytes,      // OUT:
                        uint64 *outTotalBytes)     // OUT:
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   HgfsInternalStatus firstErr = HGFS_ERROR_SUCCESS;
   Bool firstShare = TRUE;
   size_t failed = 0;
   size_t shares = 0;
   DirectoryEntry *dent;
   HgfsHandle handle;

   ASSERT(nameStatus != HGFS_NAME_STATUS_COMPLETE);

   switch (nameStatus) {
   case HGFS_NAME_STATUS_INCOMPLETE_BASE:
      /*
       * This is the base of our namespace. Clients can request a
       * QueryVolumeInfo on it, on individual shares, or on just about
       * any pathname.
       */

      LOG(4, "%s: opened search on base\n", __FUNCTION__);
      status = HgfsServerSearchVirtualDir(HgfsServerResEnumGet,
                                          HgfsServerResEnumInit,
                                          HgfsServerResEnumExit,
                                          DIRECTORY_SEARCH_TYPE_BASE,
                                          session,
                                          &handle);
      if (status != HGFS_ERROR_SUCCESS) {
         break;
      }

      /*
       * Now go through all shares and get share paths on the server.
       * Then retrieve space info for each share's volume.
       */
      while ((status = HgfsServerGetDirEntry(handle, session, HGFS_SEARCH_LAST_ENTRY_INDEX,
                                             TRUE, &dent)) == HGFS_ERROR_SUCCESS) {
         char const *sharePath;
         size_t sharePathLen;
         uint64 currentFreeBytes  = 0;
         uint64 currentTotalBytes = 0;
         size_t length;

         if (NULL == dent) {
            break;
         }

         length = strlen(dent->d_name);

         /*
          * Now that the server is passing '.' and ".." around as dents, we
          * need to make sure to handle them properly. In particular, they
          * should be ignored within QueryVolume, as they're not real shares.
          */
         if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) {
            LOG(4, "%s: Skipping fake share %s\n", __FUNCTION__, dent->d_name);
            free(dent);
            continue;
         }

         /*
          * The above check ignores '.' and '..' so we do not include them in
          * the share count here.
          */
         shares++;

         /*
          * Check permission on the share and get the share path.  It is not
          * fatal if these do not succeed.  Instead we ignore the failures
          * (apart from logging them) until we have processed all shares.  Only
          * then do we check if there were any failures; if all shares failed
          * to process then we bail out with an error code.
          */

         nameStatus = HgfsServerPolicy_GetSharePath(dent->d_name, length,
                                                    HGFS_OPEN_MODE_READ_ONLY,
                                                    &sharePathLen,
                                                    &sharePath);
         free(dent);
         if (nameStatus != HGFS_NAME_STATUS_COMPLETE) {
            LOG(4, "%s: No such share or access denied\n", __FUNCTION__);
            if (0 == firstErr) {
               firstErr = HgfsPlatformConvertFromNameStatus(nameStatus);
            }
            failed++;
            continue;
         }

         /*
          * Pick the drive with amount of space available and return that
          * according to different volume info type.
          */


         if (!HgfsServerStatFs(sharePath, sharePathLen,
                               &currentFreeBytes, &currentTotalBytes)) {
            LOG(4, "%s: error getting volume information\n",
                __FUNCTION__);
            if (0 == firstErr) {
               firstErr = HGFS_ERROR_IO;
            }
            failed++;
            continue;
         }

         /*
          * Pick the drive with amount of space available and return that
          * according to different volume info type.
          */
         switch (infoType) {
         case VOLUME_INFO_TYPE_MIN:
            if ((*outFreeBytes > currentFreeBytes) || firstShare) {
               firstShare = FALSE;
               *outFreeBytes  = currentFreeBytes;
               *outTotalBytes = currentTotalBytes;
            }
            break;
         case VOLUME_INFO_TYPE_MAX:
            if ((*outFreeBytes < currentFreeBytes)) {
               *outFreeBytes  = currentFreeBytes;
               *outTotalBytes = currentTotalBytes;
            }
            break;
         default:
            NOT_IMPLEMENTED();
         }
      }
      if (!HgfsRemoveSearch(handle, session)) {
         LOG(4, "%s: could not close search on base\n", __FUNCTION__);
      }
      if (shares == failed) {
         if (firstErr != 0) {
            /*
             * We failed to query any of the shares.  We return the error]
             * from the first share failure.
             */
            status = firstErr;
         }
         /* No shares but no error, return zero for sizes and success. */
      }
      break;
   default:
      LOG(4, "%s: file access check failed\n", __FUNCTION__);
      status = HgfsPlatformConvertFromNameStatus(nameStatus);
   }

   return status;
}


#ifdef VMX86_LOG
/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformDirDumpDents --
 *
 *    Dump a set of directory entries (debugging code).
 *    Note: this must be called with the session search lock acquired.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsPlatformDirDumpDents(HgfsSearch *search)         // IN: search
{
   unsigned int i;

   ASSERT(search != NULL);

   Log("%s: %u dents in \"%s\"\n", __FUNCTION__, search->numDents, search->utf8Dir);

   for (i = 0; i < search->numDents; i++) {
      Log("\"%s\"\n", search->dents[i]->d_name);
   }
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsConvertToUtf8FormC --
 *
 *    Converts file name coming from OS to Utf8 form C.
 *    The function NOOP on Linux where the name is already in correct
 *    encoding.
 *    On Mac OS the default encoding is Utf8 form D thus a convertion to
 *    Utf8 for C is required.
 *
 * Results:
 *    TRUE on success. Buffer has name in Utf8 form C encoding.
 *    FALSE on error.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsConvertToUtf8FormC(char *buffer,         // IN/OUT: name to normalize
                       size_t bufferSize)    // IN: size of the name buffer
{
#if defined(__APPLE__)
   size_t entryNameLen;
   char *entryName = NULL;
   Bool result;

   /*
    * HGFS clients receive names in unicode normal form C,
    * (precomposed) so Mac hosts must convert from normal form D
    * (decomposed).
    */

   if (CodeSet_Utf8FormDToUtf8FormC(buffer, bufferSize, &entryName, &entryNameLen)) {
      result = entryNameLen < bufferSize;
      if (result) {
         memcpy(buffer, entryName, entryNameLen + 1);
      }
      free(entryName);
   } else {
      LOG(4, "%s: Unable to normalize form C \"%s\"\n", __FUNCTION__, buffer);
      result = FALSE;
   }

   return result;
#else
   size_t size;
   /*
    * Buffer may contain invalid data after the null terminating character.
    * We need to check the validity of the buffer only till the null
    * terminating character (if any). Calculate the real size of the
    * string before calling Unicode_IsBufferValid().
    */
   for (size = 0; size < bufferSize ; size++) {
      if ('\0' == buffer[size]) {
         break;
      }
   }

   return Unicode_IsBufferValid(buffer, size, STRING_ENCODING_UTF8);
#endif /* defined(__APPLE__) */
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformGetDirEntry --
 *
 *    Returns the directory entry (or a copy) at the given index. If remove is set
 *    to TRUE, the existing result is also pruned and the remaining results
 *    are shifted up in the result array.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS or an appropriate error code.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformGetDirEntry(HgfsSearch *search,               // IN: search
                        HgfsSessionInfo *session,         // IN: Session info
                        uint32 index,                     // IN: Offset to retrieve at
                        Bool remove,                      // IN: If true, removes the result
                        struct DirectoryEntry **dirEntry) // OUT: dirent
{
   DirectoryEntry *dent = NULL;
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;

   if (index >= search->numDents) {
      goto out;
   }

   /* If we're not removing the result, we need to make a copy of it. */
   if (remove) {
      /*
       * We're going to shift the dents array, overwriting the dent pointer at
       * offset, so first we need to save said pointer so that we can return it
       * later to the caller.
       */
      dent = search->dents[index];

      if (index < search->numDents - 1) {
         /* Shift up the remaining results */
         memmove(&search->dents[index], &search->dents[index + 1],
                 (search->numDents - (index + 1)) * sizeof search->dents[0]);
      }

      /* Decrement the number of results */
      search->numDents--;
   } else {
      DirectoryEntry *originalDent;
      size_t nameLen;

      originalDent = search->dents[index];
      ASSERT(originalDent);

      nameLen = strlen(originalDent->d_name);
      /*
       * Make sure the name will not overrun the d_name buffer, the end of
       * which is also the end of the DirectoryEntry.
       */
      ASSERT(offsetof(DirectoryEntry, d_name) + nameLen < originalDent->d_reclen);

      dent = malloc(originalDent->d_reclen);
      if (dent == NULL) {
         status = HGFS_ERROR_NOT_ENOUGH_MEMORY;
         goto out;
      }

      /*
       * Yes, there are more members than this in a dirent. But if you look
       * at the top of hgfsServerInt.h, you'll see that on Windows we only
       * define d_reclen and d_name, as those are the only fields we need.
       */
      dent->d_reclen = originalDent->d_reclen;
      memcpy(dent->d_name, originalDent->d_name, nameLen);
      dent->d_name[nameLen] = 0;
   }

out:
   if (status == HGFS_ERROR_SUCCESS) {
      *dirEntry = dent;
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformSetDirEntry --
 *
 *    Sets the directory entry into the search read information.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS or an appropriate error code.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformSetDirEntry(HgfsSearch *search,              // IN: partially valid search
                        HgfsShareOptions configOptions,  // IN: share configuration settings
                        HgfsSessionInfo *session,        // IN: session info
                        struct DirectoryEntry *dirEntry, // IN: the indexed dirent
                        Bool getAttr,                    // IN: get the entry attributes
                        HgfsFileAttrInfo *entryAttr,     // OUT: entry attributes, optional
                        char **entryName,                // OUT: entry name
                        uint32 *entryNameLength)         // OUT: entry name length
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   unsigned int length;
   char *fullName;
   char *sharePath;
   size_t sharePathLen;
   size_t fullNameLen;
   HgfsLockType serverLock = HGFS_LOCK_NONE;
   fileDesc fileDesc;
   Bool unescapeName = TRUE;

   length = strlen(dirEntry->d_name);

   /* Each type of search gets a dent's attributes in a different way. */
   switch (search->type) {
   case DIRECTORY_SEARCH_TYPE_DIR:

      /*
       * Construct the UTF8 version of the full path to the file, and call
       * HgfsGetattrFromName to get the attributes of the file.
       */
      fullNameLen = search->utf8DirLen + 1 + length;
      fullName = (char *)malloc(fullNameLen + 1);
      if (fullName) {
         memcpy(fullName, search->utf8Dir, search->utf8DirLen);
         fullName[search->utf8DirLen] = DIRSEPC;
         memcpy(&fullName[search->utf8DirLen + 1], dirEntry->d_name, length + 1);

         LOG(4, "%s: about to stat \"%s\"\n", __FUNCTION__, fullName);

         /* Do we need to query the attributes information? */
         if (getAttr) {
            /*
             * XXX: It is unreasonable to make the caller either 1) pass existing
             * handles for directory objects as part of the SearchRead, or 2)
             * prior to calling SearchRead on a directory, break all oplocks on
             * that directory's objects.
             *
             * To compensate for that, if we detect that this directory object
             * has an oplock, we'll quietly reuse the handle. Note that this
             * requires clients who take out an exclusive oplock to open a
             * handle with read as well as write access, otherwise we'll fail
             * further down in HgfsStat.
             *
             * XXX: We could open a new handle safely if its a shared oplock.
             * But isn't this handle sharing always desirable?
             */
            if (HgfsFileHasServerLock(fullName, session, &serverLock, &fileDesc)) {
               LOG(4, "%s: Reusing existing oplocked handle "
                   "to avoid oplock break deadlock\n", __FUNCTION__);
               status = HgfsPlatformGetattrFromFd(fileDesc, session, entryAttr);
            } else {
               status = HgfsPlatformGetattrFromName(fullName, configOptions,
                                                    search->utf8ShareName,
                                                    entryAttr, NULL);
            }

            if (HGFS_ERROR_SUCCESS != status) {
               HgfsOp savedOp = entryAttr->requestType;
               LOG(4, "%s: stat FAILED %s (%d)\n", __FUNCTION__, fullName,
                   status);
               memset(entryAttr, 0, sizeof *entryAttr);
               entryAttr->requestType = savedOp;
               entryAttr->type = HGFS_FILE_TYPE_REGULAR;
               entryAttr->mask = HGFS_ATTR_VALID_TYPE;
               status = HGFS_ERROR_SUCCESS;
            }
         }

         free(fullName);
      } else {
         LOG(4, "%s: could not allocate space for \"%s\\%s\"\n",
             __FUNCTION__, search->utf8Dir, dirEntry->d_name);
         status = HGFS_ERROR_NOT_ENOUGH_MEMORY;
      }
      break;

   case DIRECTORY_SEARCH_TYPE_BASE:

      /*
       * We only want to unescape names that we could have escaped.
       * This cannot apply to our shares since they are created by the user.
       * The client will take care of escaping anything it requires.
       */
      unescapeName = FALSE;
      if (getAttr) {
         /*
          * For a search enumerating all shares, give the default attributes
          * for '.' and ".." (which aren't really shares anyway). Each real
          * share gets resolved into its full path, and gets its attributes
          * via HgfsGetattrFromName.
          */
         if (strcmp(dirEntry->d_name, ".") == 0 ||
               strcmp(dirEntry->d_name, "..") == 0) {
            LOG(4, "%s: assigning %s default attributes\n",
                __FUNCTION__, dirEntry->d_name);
            HgfsPlatformGetDefaultDirAttrs(entryAttr);
         } else {
            HgfsNameStatus nameStatus;

            /* Check permission on the share and get the share path */
            nameStatus =
               HgfsServerPolicy_GetSharePath(dirEntry->d_name, length,
                                             HGFS_OPEN_MODE_READ_ONLY,
                                             &sharePathLen,
                                             (char const **)&sharePath);
            if (nameStatus == HGFS_NAME_STATUS_COMPLETE) {

               /*
                * Server needs to produce list of shares that is consistent with
                * the list defined in UI. If a share can't be accessed because of
                * problems on the host, the server still enumerates it and
                * returns to the client.
                * XXX: We will open a new handle for this, but it should be safe
                * from oplock-induced deadlock because these are all directories,
                * and thus cannot have oplocks placed on them.
                */
               status = HgfsPlatformGetattrFromName(sharePath, configOptions,
                                                      dirEntry->d_name, entryAttr,
                                                      NULL);


               if (HGFS_ERROR_SUCCESS != status) {
                  /*
                   * The dent no longer exists. Log the event.
                   */

                  LOG(4, "%s: stat FAILED\n", __FUNCTION__);
                  status = HGFS_ERROR_SUCCESS;
               }
            } else {
               LOG(4, "%s: No such share or access denied\n", __FUNCTION__);
               status = HgfsPlatformConvertFromNameStatus(nameStatus);
            }
         }
      }
      break;
   case DIRECTORY_SEARCH_TYPE_OTHER:
   default:
      NOT_IMPLEMENTED();
      break;
   }

   /*
    * We need to unescape the name before sending it back to the client
    */
   if (HGFS_ERROR_SUCCESS == status) {
      *entryName = Util_SafeStrdup(dirEntry->d_name);
      if (unescapeName) {
         *entryNameLength = HgfsEscape_Undo(*entryName, length + 1);
      } else {
         *entryNameLength = length;
      }
      LOG(4, "%s: dent name is \"%s\" len = %u\n", __FUNCTION__,
          *entryName, *entryNameLength);
   } else {
      *entryName = NULL;
      *entryNameLength = 0;
      LOG(4, "%s: error %d getting dent\n", __FUNCTION__, status);
   }

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformScandir --
 *
 *    The cross-platform HGFS server code will call into this function
 *    in order to populate a list of dents. In the Linux case, we want to avoid
 *    using scandir(3) because it makes no provisions for not following
 *    symlinks. Instead, we'll open(2) the directory with O_DIRECTORY and
 *    O_NOFOLLOW, call getdents(2) directly, then close(2) the directory.
 *
 *    On Mac OS getdirentries became deprecated starting from 10.6 and
 *    there is no similar API available. Thus on Mac OS readdir is used that
 *    returns one directory entry at a time.
 *
 * Results:
 *    Zero on success. numDents contains the number of directory entries found.
 *    Non-zero on error.
 *
 * Side effects:
 *    Memory allocation.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformScandir(char const *baseDir,            // IN: Directory to search in
                    size_t baseDirLen,              // IN: Ignored
                    Bool followSymlinks,            // IN: followSymlinks config option
                    struct DirectoryEntry ***dents, // OUT: Array of DirectoryEntrys
                    int *numDents)                  // OUT: Number of DirectoryEntrys
{
#if defined(__APPLE__)
   DIR *fd = NULL;
#else
   int fd = -1;
   int openFlags = O_NONBLOCK | O_RDONLY | O_DIRECTORY | O_NOFOLLOW;
#endif
   int result;
   DirectoryEntry **myDents = NULL;
   int myNumDents = 0;
   HgfsInternalStatus status = 0;

   /*
    * XXX: glibc uses 8192 (BUFSIZ) when it can't get st_blksize from a stat.
    * Should we follow its lead and use stat to get st_blksize?
    */
   char buffer[8192];

#if defined(__APPLE__)
   /*
    * Since opendir does not support O_NOFOLLOW flag need to explicitly verify
    * that we are not dealing with symlink if follow symlinks is
    * not allowed.
    */
   if (!followSymlinks) {
      struct stat st;
      if (lstat(baseDir, &st) == -1) {
         status = errno;
         LOG(4, "%s: error in lstat: %d (%s)\n", __FUNCTION__, status,
             Err_Errno2String(status));
         goto exit;
      }
      if (S_ISLNK(st.st_mode)) {
         status = EACCES;
         LOG(4, "%s: do not follow symlink\n", __FUNCTION__);
         goto exit;
      }
   }
   fd = Posix_OpenDir(baseDir);
   if (NULL ==  fd) {
      status = errno;
      LOG(4, "%s: error in opendir: %d (%s)\n", __FUNCTION__, status,
          Err_Errno2String(status));
      goto exit;
   }
#else
   /* Follow symlinks if config option is set. */
   if (followSymlinks) {
      openFlags &= ~O_NOFOLLOW;
   }

   /* We want a directory. No FIFOs. Symlinks only if config option is set. */
   result = Posix_Open(baseDir, openFlags);
   if (result < 0) {
      status = errno;
      LOG(4, "%s: error in open: %d (%s)\n", __FUNCTION__, status,
          Err_Errno2String(status));
      goto exit;
   }
   fd = result;
#endif

   /*
    * Rather than read a single dent at a time, batch up multiple dents
    * in each call by using a buffer substantially larger than one dent.
    */
   while ((result = getdents(fd, (void *)buffer, sizeof buffer)) > 0) {
      size_t offset = 0;
      while (offset < result) {
         DirectoryEntry *newDent, **newDents;

         newDent = (DirectoryEntry *)(buffer + offset);

         /* This dent had better fit in the actual space we've got left. */
         ASSERT(newDent->d_reclen <= result - offset);

         /* Add another dent pointer to the dents array. */
         newDents = realloc(myDents, sizeof *myDents * (myNumDents + 1));
         if (newDents == NULL) {
            status = ENOMEM;
            goto exit;
         }
         myDents = newDents;

         /*
          * Allocate the new dent and set it up. We do a straight memcpy of
          * the entire record to avoid dealing with platform-specific fields.
          */
         myDents[myNumDents] = malloc(newDent->d_reclen);
         if (myDents[myNumDents] == NULL) {
            status = ENOMEM;
            goto exit;
         }

         if (HgfsConvertToUtf8FormC(newDent->d_name,
                                    newDent->d_reclen - offsetof(DirectoryEntry, d_name))) {
            memcpy(myDents[myNumDents], newDent, newDent->d_reclen);
            /*
             * Dent is done. Bump the offset to the batched buffer to process the
             * next dent within it.
             */
            myNumDents++;
         } else {
            /*
             * XXX:
             *    HGFS discards all file names that can't be converted to utf8.
             *    It is not desirable since it causes many problems like
             *    failure to delete directories which contain such files.
             *    Need to change this to a more reasonable behavior, similar
             *    to name escaping which is used to deal with illegal file names.
             */
            free(myDents[myNumDents]);
         }
         offset += newDent->d_reclen;
      }
   }

   if (result == -1) {
      status = errno;
      LOG(4, "%s: error in getdents: %d (%s)\n", __FUNCTION__, status,
          Err_Errno2String(status));
      goto exit;
   }

  exit:
#if defined(__APPLE__)
   if (NULL != fd && closedir(fd) < 0) {
#else
   if (fd != -1 && close(fd) < 0) {
#endif
      status = errno;
      LOG(4, "%s: error in close: %d (%s)\n", __FUNCTION__, status,
          Err_Errno2String(status));
   }

   /*
    * On error, free all allocated dents. On success, set the dents pointer
    * given to us by the client.
    */
   if (status != 0) {
      size_t i;
      for (i = 0; i < myNumDents; i++) {
         free(myDents[i]);
      }
      free(myDents);
   } else {
      *dents = myDents;
      *numDents = myNumDents;
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformScanvdir --
 *
 *    Perform a scandir on our virtual directory.
 *
 *    Get directory entry names from the given callback function, and
 *    build an array of DirectoryEntrys of all the names. Somewhat similar to
 *    scandir(3) on linux, but more general.
 *
 * Results:
 *    On success, the number of directory entries found.
 *    On failure, negative error.
 *
 * Side effects:
 *    Memory allocation.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformScanvdir(HgfsServerResEnumGetFunc enumNamesGet,   // IN: Function to get name
                     HgfsServerResEnumInitFunc enumNamesInit, // IN: Setup function
                     HgfsServerResEnumExitFunc enumNamesExit, // IN: Cleanup function
                     DirectorySearchType type,                // IN: Kind of search - unused
                     struct DirectoryEntry ***dents,          // OUT: Array of DirectoryEntrys
                     uint32 *numDents)                        // OUT: total number of directory entrys
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;
   uint32 totalDents = 0;   // Number of allocated dents
   uint32 myNumDents = 0;     // Current actual number of dents
   DirectoryEntry **myDents = NULL; // So realloc is happy w/ zero myNumDents
   void *enumNamesHandle;

   ASSERT(NULL != enumNamesInit);
   ASSERT(NULL != enumNamesGet);
   ASSERT(NULL != enumNamesExit);

   enumNamesHandle = enumNamesInit();
   if (NULL == enumNamesHandle) {
      status = HGFS_ERROR_NOT_ENOUGH_MEMORY;
      LOG(4, "%s: Error: init state ret %u\n", __FUNCTION__, status);
      goto exit;
   }

   for (;;) {
      DirectoryEntry *currentEntry;
      char const *currentEntryName;
      size_t currentEntryNameLen;
      size_t currentEntryLen;
      size_t maxNameLen;
      Bool done = FALSE;

      /* Add '.' and ".." as the first dents. */
      if (myNumDents == 0) {
         currentEntryName = ".";
         currentEntryNameLen = 1;
      } else if (myNumDents == 1) {
         currentEntryName = "..";
         currentEntryNameLen = 2;
      } else {
         if (!enumNamesGet(enumNamesHandle, &currentEntryName, &currentEntryNameLen, &done)) {
            status = HGFS_ERROR_INVALID_PARAMETER;
            LOG(4, "%s: Error: get next entry name ret %u\n", __FUNCTION__,
                status);
            goto exit;
         }
      }

      if (done) {
         LOG(4, "%s: No more names\n", __FUNCTION__);
         break;
      }

#if defined(sun)
      /*
       * Solaris lacks a single definition of NAME_MAX and using pathconf(), to
       * determine NAME_MAX for the current directory, is too cumbersome for
       * our purposes, so we use PATH_MAX as a reasonable upper bound on the
       * length of the name.
       */
      maxNameLen = PATH_MAX;
#else
      maxNameLen = sizeof currentEntry->d_name;
#endif
      if (currentEntryNameLen >= maxNameLen) {
         Log("%s: Error: Name \"%s\" is too long.\n", __FUNCTION__, currentEntryName);
         continue;
      }

      /* See if we need to allocate more memory */
      if (myNumDents == totalDents) {
         void *p;

         if (totalDents != 0) {
            totalDents *= 2;
         } else {
            totalDents = 100;
         }
         p = realloc(myDents, totalDents * sizeof *myDents);
         if (NULL == p) {
            status = HGFS_ERROR_NOT_ENOUGH_MEMORY;
            LOG(4, "%s:  Error: realloc growing array memory ret %u\n",
                __FUNCTION__, status);
            goto exit;
         }
         myDents = p;
      }

      /* This file/directory can be added to the list. */
      LOG(4, "%s: Nextfilename = \"%s\"\n", __FUNCTION__, currentEntryName);

      /*
       * Start with the size of the DirectoryEntry struct, subtract the static
       * length of the d_name buffer (256 in Linux, 1 in Solaris, etc) and add
       * back just enough space for the UTF-8 name and nul terminator.
       */

      currentEntryLen = offsetof(DirectoryEntry, d_name) + currentEntryNameLen + 1;
      currentEntry = malloc(currentEntryLen);
      if (NULL == currentEntry) {
         status = HGFS_ERROR_NOT_ENOUGH_MEMORY;
         LOG(4, "%s:  Error: allocate dentry memory ret %u\n", __FUNCTION__,
             status);
         goto exit;
      }
      currentEntry->d_reclen = (unsigned short)currentEntryLen;
      memcpy(currentEntry->d_name, currentEntryName, currentEntryNameLen);
      currentEntry->d_name[currentEntryNameLen] = 0;

      myDents[myNumDents] = currentEntry;
      myNumDents++;
   }

   /* Trim extra memory off of dents */
   {
      void *p;

      p = realloc(myDents, myNumDents * sizeof *myDents);
      if (NULL != p) {
         myDents = p;
      } else {
         LOG(4, "%s: Error: realloc trimming array memory\n", __FUNCTION__);
      }
   }

   *dents = myDents;
   *numDents = myNumDents;

exit:
   if (NULL != enumNamesHandle) {
      /* Call the exit callback to teardown any state. */
      if (!enumNamesExit(enumNamesHandle)) {
         LOG(4, "%s: Error cleanup failed\n", __FUNCTION__);
      }
   }

   if (HGFS_ERROR_SUCCESS != status) {
      unsigned int i;

      /* Free whatever has been allocated so far */
      for (i = 0; i < myNumDents; i++) {
         free(myDents[i]);
      }

      free(myDents);
   }

   return status;
}


/*
 *----------------------------------------------------------------------
 *
 * Request Handler Functions
 * -------------------------
 *
 * The functions that follow are all of the same type: they take a
 * request packet which came from the driver, process it, and fill out
 * a reply packet which is then sent back to the driver. They are
 * called by DispatchPacket, which dispatches an incoming packet to
 * the correct handler function based on the packet's opcode.
 *
 * These functions all take the following as input:
 *
 * - A pointer to a buffer containing the incoming request packet,
 * - A pointer to a buffer big enough to hold the outgoing reply packet,
 * - A pointer to the size of the incoming packet, packetSize.
 *
 * After processing the request, the handler functions write the reply
 * packet into the output buffer and set the packetSize to be the size
 * of the OUTGOING reply packet. The ServerLoop function uses the size
 * to send the reply back to the driver.
 *
 * Note that it is potentially okay for the caller to use the same
 * buffer for both input and output; handler functions should make
 * sure they are safe w.r.t. this possibility by storing any state
 * from the input buffer before they clobber it by potentially writing
 * output into the same buffer.
 *
 * Handler functions should return zero if they successfully processed
 * the request, or a negative error if an unrecoverable error
 * occurred. Normal errors (e.g. a poorly formed request packet)
 * should be handled by sending an error packet back to the driver,
 * NOT by returning an error code to the caller, because errors
 * returned by handler functions cause the server to terminate.
 *
 * [bac]
 *
 *----------------------------------------------------------------------
 */


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformReadFile --
 *
 *    Reads data from a file.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformReadFile(fileDesc file,               // IN: file descriptor
                     HgfsSessionInfo *session,    // IN: session info
                     uint64 offset,               // IN: file offset to read from
                     uint32 requiredSize,         // IN: length of data to read
                     void* payload,               // OUT: buffer for the read data
                     uint32 *actualSize)          // OUT: actual length read
{
   int error;
   HgfsInternalStatus status = 0;
   HgfsHandle handle;
   Bool sequentialOpen;

   ASSERT(session);


   LOG(4, "%s: read fh %u, offset %"FMT64"u, count %u\n", __FUNCTION__,
       file, offset, requiredSize);

   if (!HgfsFileDesc2Handle(file, session, &handle)) {
      LOG(4, "%s: Could not get file handle\n", __FUNCTION__);
      return EBADF;
   }

   if (!HgfsHandleIsSequentialOpen(handle, session, &sequentialOpen)) {
      LOG(4, "%s: Could not get sequenial open status\n", __FUNCTION__);
      return EBADF;
   }

#if defined(__linux__) || defined(__APPLE__)
   /* Read from the file. */
   if (sequentialOpen) {
      error = read(file, payload, requiredSize);
   } else {
      error = pread(file, payload, requiredSize, offset);
   }
#else
   /*
    * Seek to the offset and read from the file. Grab the IO lock to make
    * this and the subsequent read atomic.
    */

   MXUser_AcquireExclLock(session->fileIOLock);

   if (sequentialOpen) {
      error = 0; // No error from seek
   } else {
#   ifdef __linux__
      {
         uint64 res;
#      if !defined(VM_X86_64)
         error = _llseek(file, offset >> 32, offset & 0xFFFFFFFF, &res, 0);
#      else
         error = llseek(file, offset >> 32, offset & 0xFFFFFFFF, &res, 0);
#      endif
      }
#   else
      error = lseek(file, offset, 0);
#   endif
   }

   if (error >= 0) {
      error = read(file, payload, requiredSize);
   } else {
      LOG(4, "%s: could not seek to %"FMT64"u: %s\n", __FUNCTION__,
         offset, Err_Errno2String(status));
   }

   MXUser_ReleaseExclLock(session->fileIOLock);
#endif
   if (error < 0) {
      status = errno;
      LOG(4, "%s: error reading from file: %s\n", __FUNCTION__,
          Err_Errno2String(status));
   } else {
      LOG(4, "%s: read %d bytes\n", __FUNCTION__, error);
      *actualSize = error;
   }

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformWriteFile --
 *
 *    Performs actual writing data to a file.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformWriteFile(fileDesc writeFd,            // IN: file descriptor
                      HgfsSessionInfo *session,    // IN: session info
                      uint64 writeOffset,          // IN: file offset to write to
                      uint32 writeDataSize,        // IN: length of data to write
                      HgfsWriteFlags writeFlags,   // IN: write flags
                      Bool writeSequential,        // IN: write is sequential
                      Bool writeAppend,            // IN: write is appended
                      const void *writeData,       // IN: data to be written
                      uint32 *writtenSize)         // OUT: actual length written
{
   HgfsInternalStatus status = 0;
   int error = 0;

   LOG(4, "%s: write fh %u offset %"FMT64"u, count %u\n",
       __FUNCTION__, writeFd, writeOffset, writeDataSize);

#if !defined(sun)
   if (!writeSequential) {
      status = HgfsWriteCheckIORange(writeOffset, writeDataSize);
      if (status != 0) {
         return status;
      }
   }
#endif

#if defined(__linux__)
   /* Write to the file. */
   if (writeSequential) {
      error = write(writeFd, writeData, writeDataSize);
   } else {
      error = pwrite(writeFd, writeData, writeDataSize, writeOffset);
   }
#elif defined(__APPLE__)
   {
      /* Write to the file. */
      if (writeSequential || writeAppend) {
         error = write(writeFd, writeData, writeDataSize);
      } else {
         error = pwrite(writeFd, writeData, writeDataSize, writeOffset);
      }
   }
#else
   /*
    * Seek to the offset and write from the file. Grab the IO lock to make
    * this and the subsequent write atomic.
    */

   MXUser_AcquireExclLock(session->fileIOLock);
   if (!writeSequential) {
#   ifdef __linux__
      {
         uint64 res;
#      if !defined(VM_X86_64)
         error = _llseek(writeFd, writeOffset >> 32, writeOffset & 0xFFFFFFFF, &res, 0);
#      else
         error = llseek(writeFd, writeOffset >> 32, writeOffset & 0xFFFFFFFF, &res, 0);
#      endif
      }
#   else
      error = lseek(writeFd, writeOffset, 0);
#   endif

   }

   if (error < 0) {
      LOG(4, "%s: could not seek to %"FMT64"u: %s\n", __FUNCTION__,
          writeOffset, Err_Errno2String(errno));
   } else {
      error = write(writeFd, writeData, writeDataSize);
   }
   {
      int savedErr = errno;
      MXUser_ReleaseExclLock(session->fileIOLock);
      errno = savedErr;
   }
#endif

   if (error < 0) {
      status = errno;
      LOG(4, "%s: error writing to file: %s\n", __FUNCTION__,
         Err_Errno2String(status));
   } else {
      *writtenSize = error;
      LOG(4, "%s: wrote %d bytes\n", __FUNCTION__, *writtenSize);
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformSearchDir --
 *
 *    Handle platform specific logic needed to perform search open request.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformSearchDir(HgfsNameStatus nameStatus,       // IN: name status
                      const char *dirName,             // IN: relative directory name
                      size_t dirNameLength,            // IN: length of dirName
                      uint32 caseFlags,                // IN: case flags
                      HgfsShareInfo *shareInfo,        // IN: sharfed folder information
                      char *baseDir,                   // IN: name of the shared directory
                      uint32 baseDirLen,               // IN: length of the baseDir
                      HgfsSessionInfo *session,        // IN: session info
                      HgfsHandle *handle)              // OUT: search handle
{
   HgfsInternalStatus status = 0;
   switch (nameStatus) {
   case HGFS_NAME_STATUS_COMPLETE:
   {
      const char *inEnd;
      const char *next;
      int len;

      ASSERT(baseDir);
      LOG(4, "%s: searching in \"%s\", %s.\n", __FUNCTION__, baseDir, dirName);

      inEnd = dirName + dirNameLength;

      /* Get the first component. */
      len = CPName_GetComponent(dirName, inEnd, &next);
      if (len >= 0) {
         if (*inEnd != '\0') {
            LOG(4, "%s: dir name not nul-terminated!\n", __FUNCTION__);
            /*
             * NT4 clients can send the name without a nul-terminator.
             * The space for the nul is included and tested for in the size
             * calculations above. Size of structure (includes a single
             * character of the name) and the full dirname length.
             */
            *(char *)inEnd = '\0';
         }

         LOG(4, "%s: dirName: %s.\n", __FUNCTION__, dirName);
         status = HgfsServerSearchRealDir(baseDir,
                                          baseDirLen,
                                          dirName,
                                          shareInfo->rootDir,
                                          session,
                                          handle);
      } else {
         LOG(4, "%s: get first component failed\n", __FUNCTION__);
         status = ENOENT;
      }
      /*
       * If the directory exists but shared folder is write only
       * then return access denied, otherwise preserve the original
       * error code.
       */
      if (!shareInfo->readPermissions && HGFS_NAME_STATUS_COMPLETE == status) {
         status = HGFS_NAME_STATUS_ACCESS_DENIED;
      }
      if (status != 0) {
         LOG(4, "%s: couldn't scandir\n", __FUNCTION__);
      }
      break;
   }

   case HGFS_NAME_STATUS_INCOMPLETE_BASE:
      /*
       * This is the base of our namespace, so enumerate all
       * shares. [bac]
       */

      LOG(4, "%s: opened search on base\n", __FUNCTION__);
      status = HgfsServerSearchVirtualDir(HgfsServerResEnumGet,
                                          HgfsServerResEnumInit,
                                          HgfsServerResEnumExit,
                                          DIRECTORY_SEARCH_TYPE_BASE,
                                          session,
                                          handle);
      if (status != 0) {
         LOG(4, "%s: couldn't enumerate shares\n", __FUNCTION__);
      }
      break;

   default:
      LOG(4, "%s: access check failed\n", __FUNCTION__);
      status = HgfsPlatformConvertFromNameStatus(nameStatus);
   }

   if (status == 0) {
      HGFS_SERVER_DIR_DUMP_DENTS(*handle, session);
   }

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformRestartSearchDir --
 *
 *    Handle platform specific restarting of a directory search.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS or an appropriate error code.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformRestartSearchDir(HgfsHandle handle,               // IN: search handle
                             HgfsSessionInfo *session,        // IN: session info
                             DirectorySearchType searchType)  // IN: Kind of search
{
   HgfsInternalStatus status;

   switch (searchType) {
   case DIRECTORY_SEARCH_TYPE_BASE:
      /* Entries are shares */
      status = HgfsServerRestartSearchVirtualDir(HgfsServerResEnumGet,
                                                 HgfsServerResEnumInit,
                                                 HgfsServerResEnumExit,
                                                 session,
                                                 handle);
      break;
   case DIRECTORY_SEARCH_TYPE_OTHER:
      /* Entries of this type are unknown and not supported for this platform. */
   case DIRECTORY_SEARCH_TYPE_DIR:
      /* Entries are files and subdirectories: currently not implemented! */
   default:
      status = EINVAL;
      break;
   }

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformHandleIncompleteName --
 *
 *   Returns platform error that matches HgfsNameStatus.
 *
 * Results:
 *    Non-zero error code.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformHandleIncompleteName(HgfsNameStatus nameStatus,  // IN: name status
                                 HgfsFileAttrInfo *attr)     // OUT: unused
{
   return HgfsPlatformConvertFromNameStatus(nameStatus);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformDeleteFileByName --
 *
 *    POSIX specific implementation of a delete file request which accepts
 *    utf8 file path as a parameter.
 *
 *    Simply calls Posix_Unlink.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformDeleteFileByName(char const *utf8Name) // IN: full file path in uf8 encoding
{
   HgfsInternalStatus status;

   LOG(4, "%s: unlinking \"%s\"\n", __FUNCTION__, utf8Name);
   status = Posix_Unlink(utf8Name);
   if (status) {
      status = errno;
      LOG(4, "%s: error: %s\n", __FUNCTION__, Err_Errno2String(status));
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformDeleteFileByHandle --
 *
 *    POSIX specific implementation of a delete file request which accepts
 *    HgfsHandle as a parameter.
 *
 *    File handle must have appropriate access mode to allow file deletion.
 *    Shared folder restrictions are enforced here as well.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformDeleteFileByHandle(HgfsHandle file,          // IN: File being deleted
                               HgfsSessionInfo *session) // IN: session info
{
   HgfsInternalStatus status;
   Bool readPermissions;
   Bool writePermissions;
   char *localName;
   size_t localNameSize;

   if (HgfsHandle2FileNameMode(file, session, &readPermissions, &writePermissions,
                               &localName, &localNameSize)) {
      if (writePermissions && readPermissions) {
         status = HgfsPlatformDeleteFileByName(localName);
      } else {
         status = EPERM;
      }
      free(localName);
   } else {
      LOG(4, "%s: could not map cached file handle %u\n", __FUNCTION__, file);
      status = EBADF;
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformDeleteDirByName --
 *
 *    POSIX specific implementation of a delete directory request which accepts
 *    utf8 file path as a parameter.
 *
 *    Simply calls Posix_Rmdir.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformDeleteDirByName(char const *utf8Name) // IN: full file path in uf8 encoding
{
   HgfsInternalStatus status;

   LOG(4, "%s: removing \"%s\"\n", __FUNCTION__, utf8Name);
   status = Posix_Rmdir(utf8Name);
   if (status) {
      status = errno;
      LOG(4, "%s: error: %s\n", __FUNCTION__, Err_Errno2String(status));
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformDeleteDirByHandle --
 *
 *    POSIX specific implementation of a Delete directory request which accepts
 *    HgfsHandle as a parameter.
 *
 *    File handle must have appropriate access mode to allow file deletion.
 *    Shared folder restrictions are enforced here as well.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformDeleteDirByHandle(HgfsHandle file,          // IN: File being deleted
                              HgfsSessionInfo *session) // IN: session info
{
   HgfsInternalStatus status;
   Bool readPermissions;
   Bool writePermissions;
   char *localName;
   size_t localNameSize;

   if (HgfsHandle2FileNameMode(file, session, &readPermissions, &writePermissions,
                               &localName, &localNameSize)) {
      if (writePermissions && readPermissions) {
         status = HgfsPlatformDeleteDirByName(localName);
      } else {
         status = EPERM;
      }
      free(localName);
   } else {
      LOG(4, "%s: could not map cached file handle %u\n", __FUNCTION__, file);
      status = EBADF;
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformFileExists  --
 *
 *    Platform specific function that that verifies if a file or directory exists.
 *
 * Results:
 *    0 if user has permissions to traverse the parent directory and
 *    the file exists, POSIX error code otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformFileExists(char *localTargetName) // IN: Full file path utf8 encoding
{
   int err;
   err = Posix_Access(localTargetName, F_OK);
   if (-1 == err) {
      err = errno;
   }
   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformRename  --
 *
 *    POSIX version of the function that renames a file or directory.
 *
 * Results:
 *    0 on success, POSIX error code otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformRename(char *localSrcName,     // IN: local path to source file
                   fileDesc srcFile,       // IN: source file handle
                   char *localTargetName,  // IN: local path to target file
                   fileDesc targetFile,    // IN: target file handle
                   HgfsRenameHint hints)   // IN: rename hints
{
   HgfsInternalStatus status = 0;

   if (hints & HGFS_RENAME_HINT_NO_REPLACE_EXISTING) {
      if (0 == HgfsPlatformFileExists(localTargetName)) {
         status = EEXIST;
         goto exit;
      }
   }

   LOG(4, "%s: renaming \"%s\" to \"%s\"\n", __FUNCTION__,
       localSrcName, localTargetName);
   status = Posix_Rename(localSrcName, localTargetName);
   if (status) {
      status = errno;
      LOG(4, "%s: error: %s\n", __FUNCTION__, Err_Errno2String(status));
   }

exit:
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformCreateDir --
 *
 *    POSIX specific code that implements create directory request.
 *
 *    It invokes POSIX to create the directory and then assigns
 *    file attributes to the new directory if attributes are specified
 *    by the guest.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformCreateDir(HgfsCreateDirInfo *info,  // IN: direcotry properties
                      char *utf8Name)           // IN: full path for the new directory
{
   mode_t permissions;
   HgfsInternalStatus status;

   /*
    * Create mode_t for use in mkdir(). If owner permissions are missing, use
    * read/write/execute for the owner permissions. If group or other
    * permissions are missing, use the owner permissions.
    *
    * This sort of makes sense. If the Windows driver wants to make a dir
    * read-only, it probably intended for the dir to be 666. Since creating
    * a directory requires a valid mode, it's highly unlikely that we'll ever
    * be creating a directory without owner permissions.
    */
   permissions = 0;
   permissions |= info->mask & HGFS_CREATE_DIR_VALID_SPECIAL_PERMS ?
                  info->specialPerms << 9 : 0;
   permissions |= info->mask & HGFS_CREATE_DIR_VALID_OWNER_PERMS ?
                  info->ownerPerms << 6 : S_IRWXU;
   permissions |= info->mask & HGFS_CREATE_DIR_VALID_GROUP_PERMS ?
                  info->groupPerms << 3 : (permissions & S_IRWXU) >> 3;
   permissions |= info->mask & HGFS_CREATE_DIR_VALID_OTHER_PERMS ?
                  info->otherPerms : (permissions & S_IRWXU) >> 6;

   LOG(4, "%s: making dir \"%s\", mode %"FMTMODE"\n", __FUNCTION__,
       utf8Name, permissions);

   status = Posix_Mkdir(utf8Name, permissions);
   if ((info->mask & HGFS_CREATE_DIR_VALID_FILE_ATTR) &&
       (info->fileAttr & HGFS_ATTR_HIDDEN) && 0 == status) {
      /*
       *  Set hidden attribute when requested.
       *  Do not fail directory creation if setting hidden attribute fails.
       */
      HgfsSetHiddenXAttr(utf8Name, TRUE, permissions);
   }

   if (status) {
      status = errno;
      LOG(4, "%s: error: %s\n", __FUNCTION__, Err_Errno2String(status));
   }
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformSymlinkCreate --
 *
 *    Platform specific function that actually creates the symbolic link.
 *
 * Results:
 *    Zero on success.
 *    Non-zero on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsPlatformSymlinkCreate(char *localSymlinkName,   // IN: symbolic link file name
                          char *localTargetName)    // IN: symlink target name
{
   HgfsInternalStatus status = 0;
   int error;

   LOG(4, "%s: %s -> %s\n", __FUNCTION__, localSymlinkName, localTargetName);

   /* XXX: Should make use of targetNameP->flags? */
   error = Posix_Symlink(localTargetName, localSymlinkName);
   if (error) {
      status = errno;
      LOG(4, "%s: error: %s\n", __FUNCTION__, Err_Errno2String(errno));
   }
   return status;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsPlatformPathHasSymlink --
 *
 *      This function determines if any of the intermediate components of the
 *      fileName makes references outside the actual shared path. We do not
 *      check for the last component as none of the server operations follow
 *      symlinks. Also some ops that call us expect to operate on a symlink
 *      final component.
 *
 *      We use following algorithm. It takes 2 parameters, sharePath and
 *      fileName, and returns non-zero errno if fileName makes an invalid
 *      reference. The idea is to resolve both the sharePath and parent
 *      directory of the fileName. The sharePath is already resolved
 *      beforehand in HgfsServerPolicyRead. During resolution, we eliminate
 *      all the ".", "..", and symlinks handled by the realpath(3) libc call.
 *
 *      We use parent because last component could be a symlink or a component
 *      that doesn't exist. After resolving, we determine if sharePath is a
 *      prefix of fileName.
 *
 *      Note that realpath(3) behaves differently on GNU and BSD systems.
 *      Following table lists the difference:
 *
 *                                  GNU realpath          BSD realpath
 *                            -----------------------  -----------------------
 *
 *      "/tmp/existingFile"   "/tmp/existingFile" (0)  "/tmp/existingFile" (0)
 *       "/tmp/missingFile"   NULL           (ENOENT)  "/tmp/missingFile"  (0)
 *        "/missingDir/foo"   NULL           (ENOENT)  NULL           (ENOENT)
 *              In /tmp, ""   NULL           (ENOENT)  "/tmp"              (0)
 *             In /tmp, "."   "/tmp"              (0)  "/tmp"              (0)
 *
 * Results:
 *      HGFS_NAME_STATUS_COMPLETE if the given path has a symlink,
        an appropriate name status error otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

HgfsNameStatus
HgfsPlatformPathHasSymlink(const char *fileName,      // IN
                           size_t fileNameLength,     // IN
                           const char *sharePath,     // IN
                           size_t sharePathLength)    // IN
{
   char *resolvedFileDirPath = NULL;
   char *fileDirName = NULL;
   HgfsInternalStatus status;
   HgfsNameStatus nameStatus = HGFS_NAME_STATUS_COMPLETE;

   ASSERT(fileName);
   ASSERT(sharePath);
   ASSERT(sharePathLength <= fileNameLength);

   LOG(4, "%s: fileName: %s, sharePath: %s#\n", __FUNCTION__,
       fileName, sharePath);

   /*
    * Return success if:
    * - empty fileName or
    * - sharePath is empty (this is for special root share that allows
    *   access to entire host) or
    * - fileName and sharePath are same.
    */
   if (fileNameLength == 0 ||
       sharePathLength == 0 ||
       Str_Strcmp(sharePath, fileName) == 0) {
      goto exit;
   }

   /* Separate out parent directory of the fileName. */
   File_GetPathName(fileName, &fileDirName, NULL);
   /*
    * File_GetPathName may return an empty string to signify the root of
    * the filesystem. To simplify subsequent processing, let's convert such
    * empty strings to "/" when found. See File_GetPathName header comment
    * for details.
    */
   if (strlen(fileDirName) == 0) {
      char *p;
      p = realloc(fileDirName, sizeof (DIRSEPS));
      if (p == NULL) {
         nameStatus = HGFS_NAME_STATUS_OUT_OF_MEMORY;
         LOG(4, "%s: failed to realloc fileDirName.\n", __FUNCTION__);
         goto exit;
      } else {
         fileDirName = p;
         Str_Strcpy(fileDirName, DIRSEPS, sizeof (DIRSEPS));
      }
   }

   /*
    * Resolve parent directory of fileName.
    * Use realpath(2) to resolve the parent.
    */
   resolvedFileDirPath = Posix_RealPath(fileDirName);
   if (resolvedFileDirPath == NULL) {
      /* Let's return some meaningful errors if possible. */
      status = errno;
      switch (status) {
         case ENOENT:
            nameStatus = HGFS_NAME_STATUS_DOES_NOT_EXIST;
            break;
         case ENOTDIR:
            nameStatus = HGFS_NAME_STATUS_NOT_A_DIRECTORY;
            break;
         default:
            nameStatus = HGFS_NAME_STATUS_FAILURE;
            break;
      }
      LOG(4, "%s: realpath failed: fileDirName: %s: %s\n",
          __FUNCTION__, fileDirName, Err_Errno2String(errno));
      goto exit;
   }

   /* Resolved parent should match with the shareName. */
   if (Str_Strncmp(sharePath, resolvedFileDirPath, sharePathLength) != 0) {
      nameStatus = HGFS_NAME_STATUS_ACCESS_DENIED;
      LOG(4, "%s: resolved parent do not match, parent: %s, resolved: %s#\n",
          __FUNCTION__, fileDirName, resolvedFileDirPath);
      goto exit;
   }

exit:
   free(resolvedFileDirPath);
   free(fileDirName);
   return nameStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerWriteWin32Stream --
 *
 *    Handle a write request in the WIN32_STREAM_ID format.
 *
 * Results:
 *    EOPNOTSUPP, because this is unimplemented.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsServerWriteWin32Stream(char const *packetIn,     // IN: incoming packet
                           HgfsOp op,                // IN: request type
                           const void *payload,      // IN: HGFS operational packet (without header)
                           size_t payloadSize,       // IN: size of HGFS operational packet
                           HgfsSessionInfo *session) // IN: session info
{
   return EOPNOTSUPP;
}



#if defined(__APPLE__)
/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetHiddenXattr --
 *
 *    For Mac hosts returns true if file has invisible bit set in the FileFinder
 *    extended attributes.
 *
 * Results:
 *    0 if succeeded getting attribute, error code otherwise otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsGetHiddenXAttr(char const *fileName,   // IN: File name
                   Bool *attribute)        // OUT: Hidden atribute
{
   struct attrlist attrList;
   struct FInfoAttrBuf attrBuf;
   HgfsInternalStatus err;

   ASSERT(fileName);
   ASSERT(attribute);

   memset(&attrList, 0, sizeof attrList);
   attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
   attrList.commonattr = ATTR_CMN_OBJTYPE | ATTR_CMN_FNDRINFO;
   err = getattrlist(fileName, &attrList, &attrBuf, sizeof attrBuf, 0);
   if (err == 0) {
      switch (attrBuf.objType) {
      case VREG: {
         FileInfo *info = (FileInfo*) attrBuf.finderInfo;
         uint16 finderFlags = CFSwapInt16BigToHost(info->finderFlags);
         *attribute = (finderFlags & kIsInvisible) != 0;
         break;
      }
      case VDIR: {
         FolderInfo *info = (FolderInfo*) attrBuf.finderInfo;
         uint16 finderFlags = CFSwapInt16BigToHost(info->finderFlags);
         *attribute = (finderFlags & kIsInvisible) != 0;
         break;
      }
      default:
         LOG(4, "%s: Unrecognized object type %d\n", __FUNCTION__,
             attrBuf.objType);
         err = EINVAL;
      }
   } else {
      LOG(4, "%s: Error %d when getting attributes\n", __FUNCTION__, err);
   }
   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ChangeInvisibleFlag --
 *
 *    Changes value of the invisible bit in a flags variable to a value defined
 *    by setHidden parameter.
 *
 * Results:
 *    TRUE flag has been changed, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
ChangeInvisibleFlag(uint16 *flags,           // IN/OUT: variable that contains flags
                    Bool setHidden)          // IN: new value for the invisible flag
{
   Bool changed = FALSE;
   /*
    * Finder keeps, reports and expects to set flags in big endian format.
    * Needs to convert to host endian before using constants
    * and then convert back to big endian before saving
    */
   uint16 finderFlags = CFSwapInt16BigToHost(*flags);
   Bool isHidden = (finderFlags & kIsInvisible) != 0;
   if (setHidden) {
      if (!isHidden) {
         finderFlags |= kIsInvisible;
         changed = TRUE;
      }
   } else if (isHidden) {
      finderFlags &= ~kIsInvisible;
      changed = TRUE;
   }

   if (changed) {
      *flags = CFSwapInt16HostToBig(finderFlags);
   }
   return changed;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSetHiddenXAttr --
 *
 *    Sets new value for the invisible attribute of a file.
 *
 * Results:
 *    0 if succeeded, error code otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsSetHiddenXAttr(char const *fileName,       // IN: path to the file
                   Bool setHidden,             // IN: new value to the invisible attribute
                   mode_t permissions)         // IN: permissions of the file
{
   HgfsInternalStatus err;
   Bool changed = FALSE;
   struct attrlist attrList;
   struct FInfoAttrBuf attrBuf;

   ASSERT(fileName);

   memset(&attrList, 0, sizeof attrList);
   attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
   attrList.commonattr = ATTR_CMN_OBJTYPE | ATTR_CMN_FNDRINFO;
   err = getattrlist(fileName, &attrList, &attrBuf, sizeof attrBuf, 0);
   if (err == 0) {
      switch (attrBuf.objType) {
      case VREG: {
         FileInfo *info = (FileInfo*) attrBuf.finderInfo;
         changed = ChangeInvisibleFlag(&info->finderFlags, setHidden);
         break;
      }
      case VDIR: {
         FolderInfo *info = (FolderInfo*) attrBuf.finderInfo;
         changed = ChangeInvisibleFlag(&info->finderFlags, setHidden);
         break;
      }
      default:
         LOG(4, "%s: Unrecognized object type %d\n", __FUNCTION__,
             attrBuf.objType);
         err = EINVAL;
      }
   } else {
      err = errno;
   }
   if (changed) {
      attrList.commonattr = ATTR_CMN_FNDRINFO;
      err = setattrlist(fileName, &attrList, attrBuf.finderInfo,
                        sizeof attrBuf.finderInfo, 0);
      if (0 != err) {
         err = errno;
      }
      if (EACCES == err) {
         mode_t mode = permissions | S_IWOTH | S_IWGRP | S_IWUSR;
         if (chmod(fileName, mode) == 0) {
            err = setattrlist(fileName, &attrList, attrBuf.finderInfo,
                              sizeof attrBuf.finderInfo, 0);
            if (0 != err) {
               err = errno;
            }
            chmod(fileName, permissions);
         } else {
            err = errno;
         }
      }
   }
   return err;
}

#else // __APPLE__

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetHiddenXAttr --
 *
 *    Always returns 0 since there is no support for invisible files in Linux
 *    HGFS server.
 *
 * Results:
 *    0 always. This is required to allow apps that use the hidden feature to
 *    continue to work. attribute value is set to FALSE always.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsGetHiddenXAttr(char const *fileName,    // IN: File name
                   Bool *attribute)         // OUT: Value of the hidden attribute
{
   *attribute = FALSE;
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsSetHiddenXAttr --
 *
 *    Sets new value for the invisible attribute of a file.
 *    Currently Linux server does not support invisible or hiddden files.
 *    So this is a nop.
 *
 * Results:
 *    0 always. This is required to allow apps that use the hidden feature to
 *    continue to work.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsSetHiddenXAttr(char const *fileName,   // IN: File name
                   Bool value,             // IN: Value of the attribute to set
                   mode_t permissions)     // IN: permissions of the file
{
   return 0;
}
#endif // __APPLE__


#if !defined(sun)
/*
 *-----------------------------------------------------------------------------
 *
 * HgfsWriteCheckIORange --
 *
 *    Verifies that the write arguments do not exceed the maxiuum file size.
 *
 * Results:
 *    0 on success, otherwise an appropriate error.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsWriteCheckIORange(off_t offset,         // IN:
                      uint32 bytesToWrite)  // IN:
{
   HgfsInternalStatus status = 0;
   struct rlimit fileSize;

   if (getrlimit(RLIMIT_FSIZE, &fileSize) < 0) {
      status = errno;
      LOG(4, "%s: Could not get file size limit\n", __FUNCTION__);
      goto exit;
   }

   LOG(6, "%s: File Size limits: 0x%"FMT64"x 0x%"FMT64"x\n",
       __FUNCTION__, fileSize.rlim_cur, fileSize.rlim_max);

   /*
    * Check the offset is within the file size range.
    */
   if (fileSize.rlim_cur < offset) {
      status = EFBIG;
      LOG(4, "%s: Write offset exceeds max file size limit - 0x%"FMT64"x\n",
           __FUNCTION__, offset);
      goto exit;
   }

   /*
    * Check the data to write does not exceed the max file size.
    */
   if (fileSize.rlim_cur - offset < bytesToWrite) {
      status = EFBIG;
      LOG(4, "%s: Write data 0x%x bytes @ 0x%"FMT64"x size exceeds max file size\n",
          __FUNCTION__, bytesToWrite, offset);
      goto exit;
   }

exit:
   LOG(6, "%s: Write data 0x%x bytes @ 0x%"FMT64"x returns %d\n",
        __FUNCTION__, bytesToWrite, offset, status);
   return status;
}
#endif
   07070100000083000081A40000000000000000000000016822550500002A2E000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerOplock.c  /*********************************************************
 * Copyright (C) 2012-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerOplock.c --
 *
 *      HGFS server opportunistic lock support that is common to all platforms.
 */

#if defined(__APPLE__)
#define _DARWIN_USE_64_BIT_INODE
#endif


#include <stdlib.h>
#include <stdio.h>

#include "vmware.h"
#include "str.h"
#include "cpName.h"
#include "cpNameLite.h"
#include "hgfsServerInt.h"
#include "hgfsServerOplockInt.h"



/*
 * Local data
 */

/* Indicates if the oplock module is initialized. */
static Bool gOplockInit = FALSE;

/*
 * Global data
 */


/*
 * Local functions
 */


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerOplockInit --
 *
 *      Set up any oplock related state used for HGFS server.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerOplockInit(void)
{
   if (gOplockInit) {
      return TRUE;
   }

   gOplockInit = HgfsPlatformOplockInit();
   return gOplockInit;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerOplockDestroy --
 *
 *      Tear down any oplock related state used for HGFS server.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServerOplockDestroy(void)
{
   if (!gOplockInit) {
      return;
   }

   /* Tear down oplock state, so we no longer catch signals. */
   HgfsPlatformOplockDestroy();

   gOplockInit = FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerOplockIsInited --
 *
 *      Check if the oplock related state is set up.
 *
 * Results:
 *      TRUE if the oplock related state is set up.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerOplockIsInited(void)
{
   return gOplockInit;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsHandle2ServerLock --
 *
 *    Retrieve the serverlock information for the file node that corresponds to
 *    the specified hgfs handle. If the server is not compiled with oplock
 *    support, we always return TRUE and HGFS_LOCK_NONE.
 *
 * Results:
 *    TRUE if the hgfs handle is valid and the lock was retrieved successfully.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsHandle2ServerLock(HgfsHandle handle,        // IN: Hgfs file handle
                      HgfsSessionInfo *session, // IN: Session info
                      HgfsLockType *lock)       // OUT: Server lock
{
#ifdef HGFS_OPLOCKS
   Bool found = FALSE;
   HgfsFileNode *fileNode;

   ASSERT(lock);

   MXUser_AcquireExclLock(session->nodeArrayLock);
   fileNode = HgfsHandle2FileNode(handle, session);
   if (fileNode == NULL) {
      goto exit;
   }

   *lock = fileNode->serverLock;
   found = TRUE;

exit:
   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
#else
   *lock = HGFS_LOCK_NONE;
   return TRUE;
#endif
}




/*
 *-----------------------------------------------------------------------------
 *
 * HgfsFileHasServerLock --
 *
 *    Check if the file with the given name is already opened with a server
 *    lock on it. If the server is compiled without oplock support, we always
 *    return FALSE.
 *
 * Results:
 *    TRUE if the node was found and has an oplock.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsFileHasServerLock(const char *utf8Name,             // IN: Name in UTF8
                      HgfsSessionInfo *session,         // IN: Session info
                      HgfsLockType *serverLock,         // OUT: Existing oplock
                      fileDesc   *fileDesc)             // OUT: Existing fd
{
#ifdef HGFS_OPLOCKS
   unsigned int i;
   Bool found = FALSE;
   ASSERT(utf8Name);

   ASSERT(session);
   ASSERT(session->nodeArray);

   MXUser_AcquireExclLock(session->nodeArrayLock);

   for (i = 0; i < session->numNodes; i++) {
      HgfsFileNode *existingFileNode = &session->nodeArray[i];

      if ((existingFileNode->state == FILENODE_STATE_IN_USE_CACHED) &&
          (existingFileNode->serverLock != HGFS_LOCK_NONE) &&
          (!stricmp(existingFileNode->utf8Name, utf8Name))) {
         LOG(4, "Found file with a lock: %s\n", utf8Name);
         *serverLock = existingFileNode->serverLock;
         *fileDesc = existingFileNode->fileDesc;
         found = TRUE;
         break;
      }
   }

   MXUser_ReleaseExclLock(session->nodeArrayLock);

   return found;
#else
   return FALSE;
#endif
}



#ifdef HGFS_OPLOCKS
/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerOplockBreakReply --
 *
 *      The client was sent an oplock break request, and responded with this
 *      reply. It contains the oplock status that the client is now in. Since
 *      the break could have actually been a degrade, it is well within the
 *      client's rights to transition to a non-broken state. We need to make
 *      sure that such a transition was legal, acknowledge the brea
 *      appropriately, and update our own state.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServerOplockBreakReply(const unsigned char *packetIn, // IN: Reply packet
                           unsigned int packetSize,       // IN: Size of packet
                           void *clientData)              // IN: From request
{
   HgfsReplyServerLockChange *reply;
   ServerLockData *lockData = clientData;

   ASSERT(packetIn);
   ASSERT(clientData);

   if (packetSize < sizeof *reply) {
      return;
   }
   reply = (HgfsReplyServerLockChange *)packetIn;

   /*
    * XXX: It should be safe to ignore the status and id from the actual
    * HgfsReply. The only information we need to properly acknowledge the break
    * is the original fd and the new lease, which, in the case of a degrade,
    * is double checked in HgfsAckOplockBreak, so we'd be safe from a garbage
    * value.
    */
   HgfsAckOplockBreak(lockData, reply->serverLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerOplockBreak --
 *
 *      When the host FS needs to break the oplock so that another client
 *      can open the file, it signals the event in the overlapped structure
 *      that we used to request an oplock.
 *      This sets off the following chains of events:
 *      1. Send the oplock break request to the guest.
 *      2. Once the guest acknowledges the oplock break, the completion
 *      routine GuestRpcServerRequestCallback will fire, causing
 *      HgfsServerOplockBreakReply to also fire, which will break the oplock
 *      on the host FS.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      If successful, allocates memory for the rpc request.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServerOplockBreak(ServerLockData *lockData)
{
   HgfsHandle hgfsHandle;
   char *requestBuffer = NULL;
   HgfsRequestServerLockChange *request;
   HgfsLockType lock;

   LOG(4, "%s: entered\n", __FUNCTION__);

   /*
    * XXX: Just because the file in not in the cache on the server,
    * does not mean it was closed on the client. It is possible that
    * we closed the file on the server because we ran out of space
    * in cache. That's why for now as long as a file has a lock,
    * we don't remove it from the node cache. This should be fixed.
    *
    * In any case, none of these cache-related failures should cause us to ack
    * the oplock break locally. That is because if the file wasn't in the
    * cache, or it had no lock, chances are someone else (maybe the VCPU
    * thread) broke the oplock and/or closed the file.
    */

   if (!HgfsFileDesc2Handle(lockData->fileDesc, &hgfsHandle)) {
      LOG(4, "%s: file is not in the cache\n", __FUNCTION__);
      goto free_and_exit;
   }

   if (!HgfsHandle2ServerLock(hgfsHandle, &lock)) {
      LOG(4, "%s: could not retrieve node's lock info.\n", __FUNCTION__);
      goto free_and_exit;
   }

   if (lock == HGFS_LOCK_NONE) {
      LOG(4, "%s: the file does not have a server lock.\n", __FUNCTION__);
      goto free_and_exit;
   }

   /*
    * We need to setup the entire request here. The command prefix will be
    * added later, so save some space for it.
    *
    * XXX: This should probably go into a common allocation function that
    * other out-of-band requests can use.
    */

   requestBuffer = malloc(sizeof *request + HGFS_CLIENT_CMD_LEN);
   if (requestBuffer == NULL) {
      LOG(4, "%s: could not allocate memory.\n", __FUNCTION__);
      goto ack_and_exit;
   }

   /* Save space for the command prefix. */
   request = (HgfsRequestServerLockChange *)
      (requestBuffer + HGFS_CLIENT_CMD_LEN);
   request->header.op = HGFS_OP_SERVER_LOCK_CHANGE;
   request->header.id = 0; /* XXX */
   request->file = hgfsHandle;
   request->newServerLock = lockData->serverLock;

   /*
    * Just send the request size for our actual request; our callee will
    * write in the command prefix and modify the request size appropriately.
    *
    * If for some reason we fail, we'll acknowledge the oplock break
    * immediately.
    */

   if (HgfsServerManager_SendRequest(requestBuffer, sizeof *request,
                                     HgfsServerOplockBreakReply, lockData)) {
      return;
   }
   free(requestBuffer);

  ack_and_exit:
   HgfsAckOplockBreak(lockData, HGFS_LOCK_NONE);

   return;

  free_and_exit:
   free(lockData);
}
#endif
  07070100000084000081A4000000000000000000000001682255050000076E000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerOplock.h  /*********************************************************
 * Copyright (C) 2013-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerOplock.h --
 *
 *	Header file for public common data types used in the HGFS
 *	opportunistic lock routines.
 */

#ifndef _HGFS_SERVER_OPLOCK_H_
#define _HGFS_SERVER_OPLOCK_H_

#include "hgfsProto.h"     // for protocol types
#include "hgfsServerInt.h" // for common server types e.g. HgfsSessionInfo


/*
 * Data structures
 */



/*
 * Global variables
 */



/*
 * Global functions
 */

Bool HgfsServerOplockInit(void);
void HgfsServerOplockDestroy(void);
Bool HgfsHandle2ServerLock(HgfsHandle handle,
                           HgfsSessionInfo *session,
                           HgfsLockType *lock);
Bool HgfsFileHasServerLock(const char *utf8Name,
                           HgfsSessionInfo *session,
                           HgfsLockType *serverLock,
                           fileDesc   *fileDesc);

Bool HgfsAcquireServerLock(fileDesc fileDesc,
                           HgfsSessionInfo *session,
                           HgfsLockType *serverLock);


#endif // ifndef _HGFS_SERVER_OPLOCK_H_
  07070100000085000081A40000000000000000000000016822550500000ADD000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerOplockInt.h   /*********************************************************
 * Copyright (C) 2013-2016,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerOplock.h --
 *
 *	Header file for private common data types used in the HGFS
 *	opportunistic lock routines.
 */

#ifndef _HGFS_SERVER_OPLOCKINT_H_
#define _HGFS_SERVER_OPLOCKINT_H_

#ifdef _WIN32
#include <winioctl.h>      // for REQUEST_OPLOCK_OUTPUT_BUFFER
#endif
#include "hgfsProto.h"     // for protocol types
#include "hgfsServerInt.h" // for common server types e.g. HgfsSessionInfo

/*
 * Does this platform have oplock support? We define it here to avoid long
 * ifdefs all over the code. For now, Linux and Windows hosts only.
 *
 * XXX: Just kidding, no oplock support yet.
 */
#if 0
#define HGFS_OPLOCKS
#endif

/*
 * XXX describe the data structure
 */

typedef void(*HgfsOplockCallback)(HgfsSessionInfo *session, void *data);

/* Server lock related structure */
typedef struct {
   fileDesc fileDesc;
   HgfsSessionInfo *session;
   HgfsLockType serverLock;
   HgfsOplockCallback callback;
   void *data;
#ifdef _WIN32
   REQUEST_OPLOCK_OUTPUT_BUFFER oplockInfo;
   OVERLAPPED overlapped;
#endif
} ServerLockData;


/*
 * Global variables
 */

/*
 * The maximum count of oplocks that the server supports.
 * This value can be adjusted as necessary, but must be a power of 2.
 */
#define HGFS_OPLOCK_MAX_COUNT 1024


/*
 * Global functions
 */

Bool
HgfsServerOplockIsInited(void);
Bool
HgfsPlatformOplockInit(void);
void
HgfsPlatformOplockDestroy(void);
Bool
HgfsAcquireAIOServerLock(fileDesc fileDesc,
                         HgfsSessionInfo *session,
                         HgfsLockType *serverLock,
                         HgfsOplockCallback callback,
                         void *data);
void
HgfsRemoveAIOServerLock(fileDesc fileDesc);

#ifdef HGFS_OPLOCKS
void
HgfsServerOplockBreak(ServerLockData *data);

void
HgfsAckOplockBreak(ServerLockData *lockData,
                   HgfsLockType replyLock);

#endif

#endif // ifndef _HGFS_SERVER_OPLOCKINT_H_
   07070100000086000081A40000000000000000000000016822550500002ECF000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerOplockLinux.c /*********************************************************
 * Copyright (C) 2012-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerOplockLinux.c --
 *
 *      HGFS server opportunistic lock support for the Linux platform.
 */


#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include "vmware.h"
#include "err.h"
#include "hgfsServerInt.h"
#include "hgfsServerOplockInt.h"

#ifdef HGFS_OPLOCKS
#   include <signal.h>
#   include "sig.h"
#endif


/*
 * Local data
 */

/*
 * Global data
 */


/*
 * Local functions
 */

#ifdef HGFS_OPLOCKS
static void HgfsServerSigOplockBreak(int sigNum,
                                     siginfo_t *info,
                                     ucontext_t *u,
                                     void *clientData);
#endif



/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformOplockInit --
 *
 *      Set up any state needed to start Linux HGFS server oplock support.
 *
 * Results:
 *      TRUE always.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPlatformOplockInit(void)
{
#ifdef HGFS_OPLOCKS
   /* Register a signal handler to catch oplock break signals. */
   Sig_Callback(SIGIO, SIG_SAFE, HgfsServerSigOplockBreak, NULL);
#endif
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPlatformOplockDestroy --
 *
 *      Tear down any state used for Linux HGFS server.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsPlatformOplockDestroy(void)
{
#ifdef HGFS_OPLOCKS
   /* Tear down oplock state, so we no longer catch signals. */
   Sig_Callback(SIGIO, SIG_NOHANDLER, NULL, NULL);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsRemoveAIOServerLock --
 *
 *      Remove an oplock for an open file.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsRemoveAIOServerLock(fileDesc fileDesc)  // IN:
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAcquireAIOServerLock --
 *
 *    Acquire an oplock for an open file and register the break oplock event.
 *
 * Results:
 *    TRUE on success. serverLock contains the type of the lock acquired.
 *    FALSE on failure. serverLock is HGFS_LOCK_NONE.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsAcquireAIOServerLock(fileDesc fileDesc,            // IN:
                         HgfsSessionInfo *session,     // IN: Session info
                         HgfsLockType *serverLock,     // IN/OUT: Oplock asked for/granted
                         HgfsOplockCallback callback,  // IN: call back
                         void *data)                   // IN: parameter for call back
{
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAcquireServerLock --
 *
 *    Acquire a lease for the open file. Typically we try and get the exact
 *    lease desired, but if the client asked for HGFS_LOCK_OPPORTUNISTIC, we'll
 *    take the "best" lease we can get.
 *
 * Results:
 *    TRUE on success. serverLock contains the type of the lock acquired.
 *    FALSE on failure. serverLock is HGFS_LOCK_NONE.
 *
 *    XXX: This function has the potential to return per-platform error codes,
 *    but since it is opportunistic by nature, it isn't necessary to do so.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsAcquireServerLock(fileDesc fileDesc,            // IN: OS handle
                      HgfsSessionInfo *session,     // IN: session info
                      HgfsLockType *serverLock)     // IN/OUT: Oplock asked for/granted
{
#ifdef HGFS_OPLOCKS
   HgfsLockType desiredLock;
   int leaseType, error;

   ASSERT(serverLock);
   ASSERT(session);

   desiredLock = *serverLock;

   if (desiredLock == HGFS_LOCK_NONE) {
      return TRUE;
   }

   if (!HgfsIsServerLockAllowed(session)) {
      return FALSE;
   }

   /*
    * First tell the kernel which signal to send us. SIGIO is already the
    * default, but if we skip this step, we won't get the siginfo_t when
    * a lease break occurs.
    *
    * XXX: Do I need to do fcntl(fileDesc, F_SETOWN, getpid())?
    */
   if (fcntl(fileDesc, F_SETSIG, SIGIO)) {
      error = errno;
      Log("%s: Could not set SIGIO as the desired lease break signal for "
          "fd %d: %s\n", __FUNCTION__, fileDesc, Err_Errno2String(error));

      return FALSE;
   }

   /*
    * If the client just wanted the best lock possible, start off with a write
    * lease and move down to a read lease if that was unavailable.
    */
   if ((desiredLock == HGFS_LOCK_OPPORTUNISTIC) ||
       (desiredLock == HGFS_LOCK_EXCLUSIVE)) {
      leaseType = F_WRLCK;
   } else if (desiredLock  == HGFS_LOCK_SHARED) {
      leaseType = F_RDLCK;
   } else {
      LOG(4, "%s: Unknown server lock\n", __FUNCTION__);

      return FALSE;
   }
   if (fcntl(fileDesc, F_SETLEASE, leaseType)) {
      /*
       * If our client was opportunistic and we failed to get his lease because
       * someone else is already writing or reading to the file, try again with
       * a read lease.
       */
      if (desiredLock == HGFS_LOCK_OPPORTUNISTIC &&
          (errno == EAGAIN || errno == EACCES)) {
         leaseType = F_RDLCK;
         if (fcntl(fileDesc, F_SETLEASE, leaseType)) {
            error = errno;
            LOG(4, "%s: Could not get any opportunistic lease for fd %d: %s\n",
                __FUNCTION__, fileDesc, Err_Errno2String(error));

            return FALSE;
         }
      } else {
         error = errno;
         LOG(4, "%s: Could not get %s lease for fd %d: %s\n",
             __FUNCTION__, leaseType == F_WRLCK ? "write" : "read",
             fileDesc, Err_Errno2String(errno));

         return FALSE;
      }
   }

   /* Got a lease of some kind. */
   LOG(4, "%s: Got %s lease for fd %d\n", __FUNCTION__,
       leaseType == F_WRLCK ? "write" : "read", fileDesc);
   *serverLock = leaseType == F_WRLCK ? HGFS_LOCK_EXCLUSIVE : HGFS_LOCK_SHARED;
   return TRUE;
#else
   return FALSE;
#endif
}


#ifdef HGFS_OPLOCKS
/*
 *-----------------------------------------------------------------------------
 *
 * HgfsAckOplockBreak --
 *
 *    Platform-dependent implementation of oplock break acknowledgement.
 *    This function gets called when the oplock break rpc command is completed.
 *    The rpc oplock break command (HgfsServerOplockBreak) is in hgfsServer.c
 *
 *    On Linux, we use fcntl() to downgrade the lease. Then we update the node
 *    cache, free the clientData, and call it a day.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsAckOplockBreak(ServerLockData *lockData, // IN: server lock info
                   HgfsLockType replyLock)   // IN: client has this lock
{
   int fileDesc, newLock;
   HgfsLockType actualLock;

   ASSERT(lockData);
   fileDesc = lockData->fileDesc;
   LOG(4, "%s: Acknowledging break on fd %d\n", __FUNCTION__, fileDesc);

   /*
    * The Linux server supports lock downgrading. We only downgrade to a shared
    * lock if our previous call to fcntl() said we could, and if the client
    * wants to downgrade to a shared lock. Otherwise, we break altogether.
    */
   if (lockData->serverLock == HGFS_LOCK_SHARED &&
       replyLock == HGFS_LOCK_SHARED) {
      newLock = F_RDLCK;
      actualLock = replyLock;
   } else {
      newLock = F_UNLCK;
      actualLock = HGFS_LOCK_NONE;
   }

   /* Downgrade or acknowledge the break altogether. */
   if (fcntl(fileDesc, F_SETLEASE, newLock) == -1) {
      int error = errno;
      Log("%s: Could not break lease on fd %d: %s\n",
          __FUNCTION__, fileDesc, Err_Errno2String(error));
   }

   /* Cleanup. */
   HgfsUpdateNodeServerLock(fileDesc, actualLock);
   free(lockData);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerSigOplockBreak --
 *
 *      Handle a pending oplock break. Called from the VMX poll loop context.
 *      All we really do is set up the state for an oplock break and call
 *      HgfsServerOplockBreak which will do the rest of the work.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerSigOplockBreak(int sigNum,       // IN: Signal number
                         siginfo_t *info,  // IN: Additional info about signal
                         ucontext_t *u,    // IN: Interrupted context (regs etc)
                         void *clientData) // IN: Ignored
{
   ServerLockData *lockData;
   int newLease, fd;
   HgfsLockType newServerLock;

   ASSERT(sigNum == SIGIO);
   ASSERT(info);
   ASSERT(clientData == NULL);

   fd = info->si_fd;
   LOG(4, "%s: Received SIGIO for fd %d\n", __FUNCTION__, fd);

   /*
    * We've got all we need from the signal handler, let it continue handling
    * signals of this type.
    */
   Sig_Continue(sigNum);

   /*
    * According to locks.c in kernel source, doing F_GETLEASE when a lease
    * break is pending will return the new lease we should use. It'll be
    * F_RDLCK if we can downgrade, or F_UNLCK if we should break altogether.
    */
   newLease = fcntl(fd, F_GETLEASE);
   if (newLease == F_RDLCK) {
      newServerLock = HGFS_LOCK_SHARED;
   } else if (newLease == F_UNLCK) {
      newServerLock = HGFS_LOCK_NONE;
   } else if (newLease == -1) {
      int error = errno;
      Log("%s: Could not get old lease for fd %d: %s\n", __FUNCTION__,
          fd, Err_Errno2String(error));
      goto error;
   } else {
      Log("%s: Unexpected reply to get lease for fd %d: %d\n",
          __FUNCTION__, fd, newLease);
      goto error;
   }

   /*
    * Setup a ServerLockData struct so that we can make use of
    * HgfsServerOplockBreak which does the heavy lifting of discovering which
    * HGFS handle we're interested in breaking, sending the break, receiving
    * the acknowledgement, and firing the platform-specific acknowledgement
    * function (where we'll downgrade the lease).
    */
   lockData = malloc(sizeof *lockData);
   if (lockData) {
      lockData->fileDesc = fd;
      lockData->serverLock = newServerLock;
      lockData->event = 0; // not needed

      /*
       * Relinquish control of this data. It'll get freed later, when the RPC
       * command completes.
       */
      HgfsServerOplockBreak(lockData);
      return;
   } else {
      Log("%s: Could not allocate memory for lease break on behalf of fd %d\n",
          __FUNCTION__, fd);
   }

  error:
   /* Clean up as best we can. */
   fcntl(fd, F_SETLEASE, F_UNLCK);
   HgfsUpdateNodeServerLock(fd, HGFS_LOCK_NONE);
}
#endif /* HGFS_OPLOCKS */
 07070100000087000081A40000000000000000000000016822550500003678000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerOplockMonitor.c   /*********************************************************
 * Copyright (c) 2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerOplockMonitor.c --
 *
 *      Implements functions for HGFS server opportunistic lock monitoring
 *      subfeature.
 */


#include "vmware.h"
#include "hashTable.h"
#include "hgfsServerOplockMonitor.h"
#include "mutexRankLib.h"
#include "util.h"

 /*
  * Local data
  */
#define AS_KEY(_x)  ((const void *)(uintptr_t)(_x))

/*
 * Define the max count for hash table gOplockMonitorMap.
 */
#define OPLOCK_MONITOR_MAP_MAX_COUNT HGFS_OPLOCK_MAX_COUNT

/*
 * Define the max count for hash table gOplockMonitorHandleMap.
 * Different monitor requests may target the same file, for each file there is
 * one item in hash table gOplockMonitorMap, and for each monitor request there
 * is one item in hash table gOplockMonitorHandleMap.
 * We support 4 monitor requests for each file.
 */
#define OPLOCK_MONITOR_HANDLE_MAP_MAX_COUNT (4 * OPLOCK_MONITOR_MAP_MAX_COUNT)


/*
 * This structure is the type of oplockMonitorData.callbackList.
 */
typedef struct {
   DblLnkLst_Links links;
   uint64 handle;
   HgfsOplockCallback callback;
   void *data;
} oplockMonitorCallbackList;

/*
 * This structure is the value field of hash table gOplockMonitorMap.
 */
typedef struct {
   fileDesc fileDesc;
   char *utf8Name;
   MXUserExclLock *lock;
   DblLnkLst_Links callbackList;
} oplockMonitorData;

/*
 * Caller can use oplock module to monitor the file change event by providing
 * the file path instead of file descriptor.
 * This hash table maps the file path to structure oplockMonitorData
 * which stores the information for the file, for example the file descriptor.
 * This hash table is mainly used to check if a file has already been opened,
 * which means when many callers monitor the same file, we only need to open
 * that file once.
 */
static HashTable *gOplockMonitorMap = NULL;

/*
 * This hash table is used to map the monitor handle to structure
 * oplockMonitorData.
 * This hash table is used when un-monitor the file change.
 */
static HashTable *gOplockMonitorHandleMap = NULL;

/* Lock for gOplockMonitorMap and gOplockMonitorHandleMap. */
static MXUserExclLock *oplockMonitorLock;

/* Indicates if the oplock monitor module is initialized. */
static Bool gOplockMonitorInit = FALSE;

void
HgfsOplockUnmonitorFileChangeInternal(HOM_HANDLE handle);


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsOplockMonitorInit --
 *
 *      Set up any related state for monitoring.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsOplockMonitorInit(void)
{
   if (gOplockMonitorInit) {
      return TRUE;
   }

   // Oplock module must be initialized first.
   if (!HgfsServerOplockIsInited()) {
      Log("%s: Oplock module is not inited\n", __FUNCTION__);
      return FALSE;
   }

   gOplockMonitorMap = HashTable_Alloc(OPLOCK_MONITOR_MAP_MAX_COUNT,
                                      HASH_ISTRING_KEY | HASH_FLAG_COPYKEY,
                                      NULL);
   ASSERT(gOplockMonitorMap);

   gOplockMonitorHandleMap = HashTable_Alloc(OPLOCK_MONITOR_HANDLE_MAP_MAX_COUNT,
                                            HASH_INT_KEY,
                                            NULL);
   ASSERT(gOplockMonitorHandleMap);

   oplockMonitorLock = MXUser_CreateExclLock("HgfsoplockMonitorLock",
                                             RANK_hgfsSharedFolders);

   gOplockMonitorInit = TRUE;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsOplockMonitorDestroy --
 *
 *      Tear down any related state for monitoring.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsOplockMonitorDestroy(void)
{
   if (!gOplockMonitorInit) {
      return;
   }

   HashTable_Free(gOplockMonitorMap);
   HashTable_Free(gOplockMonitorHandleMap);
   MXUser_DestroyExclLock(oplockMonitorLock);
   gOplockMonitorInit = FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsOplockMonitorFileChangeCallback --
 *
 *    A callback function that called when the target file/directory is
 *    changed.
 *    Calls the caller provided callback.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsOplockMonitorFileChangeCallback(HgfsSessionInfo *session,   // IN:
                                    void *data)                 // IN:
{
   oplockMonitorData *monitorData = data;

   ASSERT(monitorData);
   MXUser_AcquireExclLock(oplockMonitorLock);
   if (HashTable_Lookup(gOplockMonitorMap, monitorData->utf8Name, NULL)) {
      DblLnkLst_Links *link, *nextLink;
      DblLnkLst_ForEachSafe(link, nextLink, &monitorData->callbackList) {
         oplockMonitorCallbackList *callbackItem = DblLnkLst_Container(link,
                                                   oplockMonitorCallbackList,
                                                   links);
         callbackItem->callback(session, callbackItem->data);
         /*
          * callbackItem->data has been freed in the user callback.
          */
         callbackItem->data = NULL;
         HgfsOplockUnmonitorFileChangeInternal(callbackItem->handle);
         /*
          * callbackItem has been freed in above function.
          */
         callbackItem = NULL;
      }
   }
   MXUser_ReleaseExclLock(oplockMonitorLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsOplockMonitorFileChange --
 *
 *    Monitor the file/directory change event by using oplock.
 *    The caller provided callback will be called if the file/directory is
 *    changed.
 *    This is one-shot action, after the event is fired, the oplock will be
 *    removed.
 *    The data that caller provides will be freed by:
 *       1. caller callback if the callback is called;
 *       2. caller callback if this function failed;
 *       3. this module if caller cancels the file change monitor.
 *
 * Results:
 *    HGFS_OPLOCK_INVALID_MONITOR_HANDLE on fail, handle on success.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

HOM_HANDLE
HgfsOplockMonitorFileChange(char *utf8Name,             // IN: Name in UTF8
                            HgfsSessionInfo *session,   // IN:
                            HgfsOplockCallback callback,// IN:
                            void *data)                 // IN:
{
   oplockMonitorData *monitorData = NULL;
   oplockMonitorCallbackList *callbackItem;
   HOM_HANDLE handle = HGFS_OPLOCK_INVALID_MONITOR_HANDLE;
   HgfsFileOpenInfo openInfo;
   HgfsLocalId localId;
   fileDesc newHandle;
   HgfsInternalStatus status;
   HgfsLockType serverLock = HGFS_LOCK_SHARED;

   MXUser_AcquireExclLock(oplockMonitorLock);
   if (!gOplockMonitorInit) {
      LOG(4, "%s: Oplock monitor is not inited\n", __FUNCTION__);
      goto error;
   }

   if (   HashTable_GetNumElements(gOplockMonitorMap)
       >= OPLOCK_MONITOR_MAP_MAX_COUNT) {
      LOG(4, "%s: Exceeds OPLOCK_MONITOR_MAP_MAX_COUNT\n", __FUNCTION__);
      goto error;
   }

   if (   HashTable_GetNumElements(gOplockMonitorHandleMap)
       >= OPLOCK_MONITOR_HANDLE_MAP_MAX_COUNT) {
      LOG(4, "%s: Exceeds OPLOCK_MONITOR_HANDLE_MAP_MAX_COUNT\n", __FUNCTION__);
      goto error;
   }

   /*
    * If there are multiple monitor request for the same file, we should open
    * the file only once, and add all the callback functions into one double
    * link list.
    */
   if (HashTable_Lookup(gOplockMonitorMap, utf8Name, (void **)&monitorData)) {
      callbackItem = Util_SafeMalloc(sizeof *callbackItem);
      handle = (HOM_HANDLE)callbackItem;
      DblLnkLst_Init(&callbackItem->links);
      callbackItem->handle = handle;
      callbackItem->callback = callback;
      callbackItem->data = data;
      DblLnkLst_LinkLast(&monitorData->callbackList,
                         &callbackItem->links);
      HashTable_Insert(gOplockMonitorHandleMap,
                       AS_KEY(handle),
                       (void *)monitorData);
      MXUser_ReleaseExclLock(oplockMonitorLock);
      return handle;
   }

   memset(&openInfo, 0, sizeof(openInfo));
   openInfo.mask = HGFS_OPEN_VALID_MODE | HGFS_OPEN_VALID_SHARE_ACCESS;
   openInfo.mode = HGFS_OPEN_MODE_READ_ONLY;
#ifdef _WIN32
   openInfo.shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
#endif
   openInfo.flags = HGFS_OPEN;
   openInfo.utf8Name = utf8Name;
   openInfo.shareInfo.readPermissions = TRUE;
   status = HgfsPlatformValidateOpen(&openInfo, TRUE, session,
                                     &localId, &newHandle);

   if (status != HGFS_ERROR_SUCCESS) {
      LOG(4, "%s: Failed to open file: %s\n", __FUNCTION__, utf8Name);
      goto error;
   }

   monitorData = Util_SafeMalloc(sizeof *monitorData);
   monitorData->fileDesc = newHandle;
   monitorData->utf8Name = Util_SafeStrdup(utf8Name);
   DblLnkLst_Init(&monitorData->callbackList);

   if (!HgfsAcquireAIOServerLock(newHandle,
                                 session,
                                 &serverLock,
                                 HgfsOplockMonitorFileChangeCallback,
                                 monitorData)) {
      HgfsPlatformCloseFile(newHandle, NULL);
      LOG(4, "%s: Failed to acquire server lock for file: %s\n", __FUNCTION__, utf8Name);
      goto error;
   }

   callbackItem = Util_SafeMalloc(sizeof *callbackItem);
   handle = (HOM_HANDLE)callbackItem;
   DblLnkLst_Init(&callbackItem->links);
   callbackItem->handle = handle;
   callbackItem->callback = callback;
   callbackItem->data = data;
   DblLnkLst_LinkLast(&monitorData->callbackList,
                      &callbackItem->links);

   HashTable_Insert(gOplockMonitorMap, utf8Name, (void *)monitorData);
   HashTable_Insert(gOplockMonitorHandleMap, AS_KEY(handle), (void *)monitorData);
   MXUser_ReleaseExclLock(oplockMonitorLock);
   return handle;

error:
   if (monitorData) {
      free(monitorData->utf8Name);
      free(monitorData);
   }
   free(data);

   MXUser_ReleaseExclLock(oplockMonitorLock);
   return HGFS_OPLOCK_INVALID_MONITOR_HANDLE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsOplockUnmonitorFileChangeInternal --
 *
 *    Cancel the monitor action by closing the file descriptor.
 *    The lock for oplockMonitorLock should be acquired before calling this
 *    funcion.
 *    All the objects that related to handle will be released when returns
 *    from this function.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsOplockUnmonitorFileChangeInternal(HOM_HANDLE handle)             // IN:
{
   oplockMonitorData *monitorData = NULL;
   DblLnkLst_Links *link, *nextLink;
   oplockMonitorCallbackList *callbackItem;

   if (HashTable_Lookup(gOplockMonitorHandleMap,
                        AS_KEY(handle),
                        (void **)&monitorData)) {
      HashTable_Delete(gOplockMonitorHandleMap, AS_KEY(handle));

      DblLnkLst_ForEachSafe(link, nextLink, &monitorData->callbackList) {
         callbackItem = DblLnkLst_Container(link, oplockMonitorCallbackList, links);
         if (callbackItem->handle == handle) {
            DblLnkLst_Unlink1(&callbackItem->links);
            free(callbackItem->data);
            free(callbackItem);
            break;
         }
      }

      /*
       * Close the file if no one is monitoring it anymore.
       */
      if (DblLnkLst_IsLinked(&monitorData->callbackList) == FALSE) {
         HashTable_Delete(gOplockMonitorMap, monitorData->utf8Name);
         HgfsRemoveAIOServerLock(monitorData->fileDesc);
         free(monitorData->utf8Name);
         free(monitorData);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsOplockUnmonitorFileChange --
 *
 *    Cancel the monitor action.
 *    All the objects that related to handle will be released when returns
 *    from this function.
 *    the caller provided callback will not be called.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsOplockUnmonitorFileChange(HOM_HANDLE handle)             // IN:
{
   /*
    * This function is a callback function and may be called at any time, even
    * when the oplock monitor module is destroyed.
    * So, check if the oplock monitor module is initialized.
    */
   if (!gOplockMonitorInit) {
      Log("%s: OplockMonitor module is not inited\n", __FUNCTION__);
      return;
   }

   MXUser_AcquireExclLock(oplockMonitorLock);
   HgfsOplockUnmonitorFileChangeInternal(handle);
   MXUser_ReleaseExclLock(oplockMonitorLock);
}
07070100000088000081A40000000000000000000000016822550500000670000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerOplockMonitor.h   /*********************************************************
 * Copyright (C) 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerOplockMonitor.h --
 *
 *	Header file for public common data types used in the HGFS
 *	opportunistic lock monitoring subfeature.
 */

#ifndef _HGFS_SERVER_OPLOCK_MONITOR_H_
#define _HGFS_SERVER_OPLOCK_MONITOR_H_

#include "hgfsServerOplockInt.h"


/*
 * Data structures
 */

/*
 * Global variables
 */
#define HGFS_OPLOCK_INVALID_MONITOR_HANDLE 0


/*
 * Global functions
 */

Bool
HgfsOplockMonitorInit(void);
void
HgfsOplockMonitorDestroy(void);
HOM_HANDLE
HgfsOplockMonitorFileChange(char *utf8Name,
                            HgfsSessionInfo *session,
                            HgfsOplockCallback callback,
                            void *data);
void
HgfsOplockUnmonitorFileChange(HOM_HANDLE handle);
#endif // ifndef _HGFS_SERVER_OPLOCK_MONITOR_H_
07070100000089000081A40000000000000000000000016822550500006603000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerPacketUtil.c  /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerPacketUtil.c --
 *
 * Utility functions for manipulating packet used by hgfs server code
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "vmware.h"
#include "hgfsServer.h"
#include "hgfsServerInt.h"
#include "util.h"

static void *HSPUGetBuf(HgfsServerChannelCallbacks *chanCb,
                        MappingType mappingType,
                        HgfsVmxIov *iov,
                        uint32 iovCount,
                        uint32 startIndex,
                        size_t dataSize,
                        size_t bufSize,
                        void **buf,
                        Bool *isAllocated,
                        uint32 *iovMappedCount);
static void HSPUPutBuf(HgfsServerChannelCallbacks *chanCb,
                       MappingType mappingType,
                       HgfsVmxIov *iov,
                       uint32 iovCount,
                       uint32 startIndex,
                       size_t bufSize,
                       void **buf,
                       Bool *isAllocated,
                       uint32 *iovMappedCount);
static void HSPUCopyBufToIovec(HgfsVmxIov *iov,
                               uint32 iovMapped,
                               uint32 startIndex,
                               void *buf,
                               size_t bufSize);
static void HSPUCopyIovecToBuf(HgfsVmxIov *iov,
                               uint32 iovMapped,
                               uint32 startIndex,
                               void *buf,
                               size_t bufSize);
static Bool HSPUMapBuf(HgfsChannelMapVirtAddrFunc mapVa,
                       HgfsChannelUnmapVirtAddrFunc putVa,
                       size_t mapSize,
                       uint32 startIndex,
                       uint32 iovCount,
                       HgfsVmxIov *iov,
                       uint32 *mappedCount);
static void HSPUUnmapBuf(HgfsChannelUnmapVirtAddrFunc unmapVa,
                         uint32 startIndex,
                         HgfsVmxIov *iov,
                         uint32 *mappedCount);



/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_ValidateRequestPacketSize --
 *
 *    Validate an HGFS packet size with the HGFS header in use, the HGFS opcode request
 *    (its arguments) and optionally any opcode request data that is contained in the
 *    size for the packet.
 *
 * Results:
 *    TRUE if the packet size is large enough for the required request data.
 *    FALSE if not.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

Bool
HSPU_ValidateRequestPacketSize(HgfsPacket *packet,           // IN: Hgfs Packet
                               size_t requestHeaderSize,     // IN: request header size
                               size_t requestOpSize,         // IN: request op size
                               size_t requestOpDataSize)     // IN: request op data size
{
   size_t bytesRemaining = packet->metaPacketDataSize;
   Bool requestSizeIsOkay = FALSE;

   /*
    * Validate the request buffer size ensuring that the the contained components
    * (request header, the operation arguments and lastly any data) fall within it.
    */

   if (bytesRemaining >= requestHeaderSize) {
      bytesRemaining -= requestHeaderSize;
   } else {
      goto exit;
   }
   if (bytesRemaining >= requestOpSize) {
      bytesRemaining -= requestOpSize;
   } else {
      goto exit;
   }
   if (bytesRemaining >= requestOpDataSize) {
      requestSizeIsOkay = TRUE;
   }

exit:
   return requestSizeIsOkay;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_ValidateReplyPacketSize --
 *
 *    Validate a reply buffer size in an hgfs packet with the reply data
 *    size for the request.
 *
 * Results:
 *    TRUE if the reply buffer size is large enough for the request reply
 *    results. FALSE if not.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

Bool
HSPU_ValidateReplyPacketSize(HgfsPacket *packet,         // IN: Hgfs Packet
                             size_t replyHeaderSize,     // IN: reply header size
                             size_t replyResultSize,     // IN: reply result size
                             size_t replyResultDataSize, // IN: reply result data size
                             Bool useMappedMetaPacket)   // IN: using meta buffer
{
   size_t bytesRemaining;
   Bool replySizeIsOkay = FALSE;

   if (packet->replyPacket != NULL) {
      /* Pre-allocated reply buffer (as used by the backdoor). */
      bytesRemaining = packet->replyPacketSize;
   } else if (useMappedMetaPacket) {
      /* No reply buffer (as used by the VMCI) reuse the metapacket buffer. */
      bytesRemaining = packet->metaPacketSize;
   } else {
      /* No reply buffer but we will allocate the size required. */
      replySizeIsOkay = TRUE;
      goto exit;
   }

   if (bytesRemaining >= replyHeaderSize) {
      bytesRemaining -= replyHeaderSize;
   } else {
      goto exit;
   }
   if (bytesRemaining >= replyResultSize) {
      bytesRemaining -= replyResultSize;
   } else {
      goto exit;
   }
   if (bytesRemaining >= replyResultDataSize) {
      replySizeIsOkay = TRUE;
   }

exit:
   return replySizeIsOkay;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_GetReplyPacket --
 *
 *    Get a reply packet given an hgfs packet.
 *    Guest mappings may be established.
 *
 * Results:
 *    Pointer to reply packet.
 *
 * Side effects:
 *    Buffer may be allocated.
 *-----------------------------------------------------------------------------
 */

void *
HSPU_GetReplyPacket(HgfsPacket *packet,                  // IN/OUT: Hgfs Packet
                    HgfsServerChannelCallbacks *chanCb,  // IN: Channel callbacks
                    size_t replyDataSize,                // IN: Size of reply data
                    size_t *replyPacketSize)             // OUT: Size of reply Packet
{
   if (packet->replyPacket != NULL) {
      /*
       * When we are transferring packets over backdoor, reply packet
       * is a static buffer. Backdoor should always return from here.
       */
      packet->replyPacketDataSize = replyDataSize;
      LOG(4, "Existing reply packet %s %"FMTSZ"u %"FMTSZ"u\n", __FUNCTION__,
          replyDataSize, packet->replyPacketSize);
      ASSERT(replyDataSize <= packet->replyPacketSize);
   } else if (chanCb != NULL && chanCb->getWriteVa != NULL) {
     /* Can we write directly into guest memory? */
      if (packet->metaPacket != NULL) {
         /*
          * Use the mapped metapacket buffer for the reply.
          * This currently makes assumptions about the mapping -
          * - It is mapped read -write
          * - It is always large enough for any reply
          * This will change as it is grossly inefficient as the maximum size
          * is always mapped and copied no matter how much data it really contains.
          */
         LOG(10, "%s Using meta packet for reply packet\n", __FUNCTION__);
         ASSERT(BUF_READWRITEABLE == packet->metaMappingType);
         ASSERT(replyDataSize <= packet->metaPacketSize);

         packet->replyPacket = packet->metaPacket;
         packet->replyPacketDataSize = replyDataSize;
         packet->replyPacketSize = packet->metaPacketSize;
         packet->replyPacketIsAllocated = FALSE;
         /*
          * The reply is using the meta buffer so update the valid data size.
          *
          * Note, currently We know the reply size is going to be less than the
          * incoming request valid data size. This will updated when that part is
          * fixed. See the above comment about the assumptions and asserts.
          */
         packet->metaPacketDataSize = packet->replyPacketDataSize;
      } else {
         NOT_IMPLEMENTED();
      }
   } else {
      /* For sockets channel we always need to allocate buffer */
      LOG(10, "%s Allocating reply packet\n", __FUNCTION__);
      packet->replyPacket = Util_SafeMalloc(replyDataSize);
      packet->replyPacketIsAllocated = TRUE;
      packet->replyPacketDataSize = replyDataSize;
      packet->replyPacketSize = replyDataSize;
   }

   *replyPacketSize = packet->replyPacketSize;
   return packet->replyPacket;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_PutReplyPacket --
 *
 *    Free buffer if reply packet was allocated.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

void
HSPU_PutReplyPacket(HgfsPacket *packet,                  // IN/OUT: Hgfs Packet
                    HgfsServerChannelCallbacks *chanCb)  // IN: Channel callbacks
{
   /*
    * If there wasn't an allocated buffer for the reply, there is nothing to
    * do as the reply is in the metapacket buffer which will be handled by the
    * put on the metapacket.
    */
   if (packet->replyPacketIsAllocated) {
      LOG(10, "%s Freeing reply packet", __FUNCTION__);
      free(packet->replyPacket);
      packet->replyPacketIsAllocated = FALSE;
      packet->replyPacket = NULL;
      packet->replyPacketSize = 0;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_GetMetaPacket --
 *
 *    Get a meta packet given an hgfs packet.
 *    Guest mappings will be established.
 *
 * Results:
 *    Pointer to meta packet.
 *
 * Side effects:
 *    Buffer may be allocated.
 *-----------------------------------------------------------------------------
 */

void *
HSPU_GetMetaPacket(HgfsPacket *packet,                   // IN/OUT: Hgfs Packet
                   size_t *metaPacketSize,               // OUT: Size of metaPacket
                   HgfsServerChannelCallbacks *chanCb)   // IN: Channel callbacks
{
   *metaPacketSize = packet->metaPacketDataSize;
   if (packet->metaPacket != NULL) {
      return packet->metaPacket;
   }

   if (packet->metaPacketSize == 0) {
      return NULL;
   }

   packet->metaMappingType = BUF_READWRITEABLE;

   return HSPUGetBuf(chanCb,
                     packet->metaMappingType,
                     packet->iov,
                     packet->iovCount,
                     0,
                     packet->metaPacketDataSize,
                     packet->metaPacketSize,
                     &packet->metaPacket,
                     &packet->metaPacketIsAllocated,
                     &packet->metaPacketMappedIov);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_ValidateDataPacketSize --
 *
 *    Validate a data packet buffer size in an hgfs packet with the required data
 *    size for the request.
 *
 * Results:
 *    TRUE if the data buffer size is valid for the request.
 *    FALSE if not.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

Bool
HSPU_ValidateDataPacketSize(HgfsPacket *packet,     // IN: Hgfs Packet
                            size_t dataSize)        // IN: data size
{
   return (dataSize <= packet->dataPacketSize);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_GetDataPacketBuf --
 *
 *    Get a data packet given an hgfs packet.
 *    Guest mappings will be established.
 *
 * Results:
 *    Pointer to data packet.
 *
 * Side effects:
 *    Buffer may be allocated.
 *-----------------------------------------------------------------------------
 */

void *
HSPU_GetDataPacketBuf(HgfsPacket *packet,                   // IN/OUT: Hgfs Packet
                      MappingType mappingType,              // IN: Writeable/Readable
                      HgfsServerChannelCallbacks *chanCb)   // IN: Channel callbacks
{
   if (packet->dataPacket != NULL) {
      return packet->dataPacket;
   }

   if (packet->dataPacketSize == 0) {
      return NULL;
   }

   packet->dataMappingType = mappingType;
   return HSPUGetBuf(chanCb,
                     packet->dataMappingType,
                     packet->iov,
                     packet->iovCount,
                     packet->dataPacketIovIndex,
                     packet->dataPacketDataSize,
                     packet->dataPacketSize,
                     &packet->dataPacket,
                     &packet->dataPacketIsAllocated,
                     &packet->dataPacketMappedIov);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPUGetBuf --
 *
 *    Get a {meta, data} packet given an hgfs packet.
 *    Guest mappings will be established.
 *
 * Results:
 *    Pointer to buffer.
 *
 * Side effects:
 *    Buffer may be allocated.
 *-----------------------------------------------------------------------------
 */

static void *
HSPUGetBuf(HgfsServerChannelCallbacks *chanCb,  // IN: Channel callbacks
           MappingType mappingType,             // IN: Access type Readable/Writeable
           HgfsVmxIov *iov,                     // IN: iov array
           uint32 iovCount,                     // IN: iov array size
           uint32 startIndex,                   // IN: Start index of iov
           size_t dataSize,                     // IN: Size of data in tghe buffer
           size_t bufSize,                      // IN: Size of buffer
           void **buf,                          // OUT: Contigous buffer
           Bool *isAllocated,                   // OUT: Buffer allocated
           uint32 *iovMappedCount)              // OUT: iov mapped count
{
   uint32 iovMapped = 0;
   HgfsChannelMapVirtAddrFunc mapVa;
   Bool releaseMappings = FALSE;

   ASSERT(buf != NULL);

   *buf = NULL;
   *isAllocated = FALSE;

   if (chanCb == NULL) {
      goto exit;
   }

   if (mappingType == BUF_WRITEABLE ||
       mappingType == BUF_READWRITEABLE) {
      mapVa = chanCb->getWriteVa;
   } else {
      ASSERT(mappingType == BUF_READABLE);
      mapVa = chanCb->getReadVa;
   }

   /* Looks like we are in the middle of poweroff. */
   if (mapVa == NULL) {
      goto exit;
   }

   /* Establish guest memory mappings */
   if (!HSPUMapBuf(mapVa,
                   chanCb->putVa,
                   bufSize,
                   startIndex,
                   iovCount,
                   iov,
                   &iovMapped)) {
      /* Guest probably passed us bad physical address */
      goto exit;
   }

   if (iovMapped == 1) {
      /* A single page buffer is contiguous so hold on to guest mappings. */
      *buf = iov[startIndex].va;
      goto exit;
   }

   /* More than one page was mapped. */
   ASSERT(iov[startIndex].len < bufSize);

   LOG(10, "%s: Hgfs Allocating buffer \n", __FUNCTION__);
   *buf = Util_SafeMalloc(bufSize);
   *isAllocated = TRUE;

   if ((mappingType == BUF_READABLE || mappingType == BUF_READWRITEABLE) &&
       (0 != dataSize)) {
      HSPUCopyIovecToBuf(iov, iovMapped, startIndex, *buf, dataSize);
   }
   releaseMappings = TRUE;


exit:
   if (releaseMappings) {
      HSPUUnmapBuf(chanCb->putVa, startIndex, iov, &iovMapped);
   }
   *iovMappedCount = iovMapped;

   return *buf;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_PutMetaPacket --
 *
 *    Free meta packet buffer if allocated.
 *    Guest mappings will be released.
 *
 * Results:
 *    void.
 *
 * Side effects:
 *
 *-----------------------------------------------------------------------------
 */

void
HSPU_PutMetaPacket(HgfsPacket *packet,                   // IN/OUT: Hgfs Packet
                   HgfsServerChannelCallbacks *chanCb)   // IN: Channel callbacks
{
   if (packet->metaPacket == NULL) {
      return;
   }

   LOG(4, "%s Hgfs Putting Meta packet\n", __FUNCTION__);
   HSPUPutBuf(chanCb,
              packet->metaMappingType,
              packet->iov,
              packet->iovCount,
              0,
              packet->metaPacketDataSize,
              &packet->metaPacket,
              &packet->metaPacketIsAllocated,
              &packet->metaPacketMappedIov);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_SetDataPacketSize --
 *
 *    Set the size of the valid data in the data packet buffer.
 *
 * Results:
 *    void.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

void
HSPU_SetDataPacketSize(HgfsPacket *packet,            // IN/OUT: Hgfs Packet
                       size_t dataSize)               // IN: data size
{
   ASSERT(NULL != packet);
   ASSERT(dataSize <= packet->dataPacketSize);
   packet->dataPacketDataSize = dataSize;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPU_PutDataPacketBuf --
 *
 *    Free data packet buffer if allocated.
 *    Guest mappings will be released.
 *
 * Results:
 *    void.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

void
HSPU_PutDataPacketBuf(HgfsPacket *packet,                   // IN/OUT: Hgfs Packet
                      HgfsServerChannelCallbacks *chanCb)   // IN: Channel callbacks
{
   if (packet->dataPacket == NULL) {
      return;
   }

   LOG(4, "%s Hgfs Putting Data packet\n", __FUNCTION__);
   HSPUPutBuf(chanCb,
              packet->dataMappingType,
              packet->iov,
              packet->iovCount,
              packet->dataPacketIovIndex,
              packet->dataPacketDataSize,
              &packet->dataPacket,
              &packet->dataPacketIsAllocated,
              &packet->dataPacketMappedIov);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPUPutBuf --
 *
 *    Free buffer if allocated and release guest mappings.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

void
HSPUPutBuf(HgfsServerChannelCallbacks *chanCb,  // IN: Channel callbacks
           MappingType mappingType,             // IN: Access type Readable/Writeable
           HgfsVmxIov *iov,                     // IN: iov array
           uint32 iovCount,                     // IN: iov array size
           uint32 startIndex,                   // IN: Start index of iov
           size_t bufSize,                      // IN: Size of buffer
           void **buf,                          // OUT: Contigous buffer
           Bool *isAllocated,                   // OUT: Buffer allocated
           uint32 *iovMappedCount)              // OUT: iov mapped count
{
   ASSERT(buf != NULL);

   if (chanCb == NULL || chanCb->putVa == NULL) {
      goto exit;
   }

   if (*isAllocated &&
       (mappingType == BUF_WRITEABLE || mappingType == BUF_READWRITEABLE)) {
      /*
       * 1. Map the iov's if required for the size into host addresses.
       * 2. Write the buffer data into the iov host addresses.
       * 3. Unmap the iov's host virtual addresses.
       */
      if (0 == *iovMappedCount) {
         if (!HSPUMapBuf(chanCb->getWriteVa,
                         chanCb->putVa,
                         bufSize,
                         startIndex,
                         iovCount,
                         iov,
                         iovMappedCount)) {
            goto exit;
         }
      }
      HSPUCopyBufToIovec(iov, *iovMappedCount, startIndex, *buf, bufSize);
   }

   if (0 < *iovMappedCount) {
      HSPUUnmapBuf(chanCb->putVa, startIndex, iov, iovMappedCount);
   }

exit:
   if (*isAllocated) {
      LOG(10, "%s: Hgfs Freeing buffer \n", __FUNCTION__);
      free(*buf);
      *isAllocated = FALSE;
   }

   *buf = NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPUCopyBufToIovec --
 *
 *    Write out buffer to data Iovec.
 *
 * Results:
 *    void
 *
 * Side effects:
 *    @iov is populated with contents of @buf
 *-----------------------------------------------------------------------------
 */

static void
HSPUCopyBufToIovec(HgfsVmxIov *iov,          // IN: iovs (array of mappings)
                   uint32 iovCount,          // IN: iov count of mappings
                   uint32 startIndex,        // IN: start index into iov
                   void *buf,                // IN: Contigous Buffer
                   size_t bufSize)           // IN: Size of buffer
{
   size_t iovIndex;
   size_t endIndex;
   size_t remainingSize;
   size_t copiedAmount = 0;

   ASSERT(buf != NULL);

   for (iovIndex = startIndex,  endIndex = startIndex + iovCount, remainingSize = bufSize;
        iovIndex < endIndex && remainingSize > 0;
        iovIndex++) {
      size_t copyAmount = remainingSize < iov[iovIndex].len ?
                          remainingSize: iov[iovIndex].len;

      ASSERT(iov[iovIndex].va != NULL);

      memcpy(iov[iovIndex].va, (char *)buf + copiedAmount, copyAmount);
      remainingSize -= copyAmount;
      copiedAmount += copyAmount;
   }

   ASSERT(remainingSize == 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPUCopyIovecToBuf --
 *
 *    Read Iovec into the buffer.
 *
 * Results:
 *    void
 *
 * Side effects:
 *    @iov is populated with contents of @buf
 *-----------------------------------------------------------------------------
 */

static void
HSPUCopyIovecToBuf(HgfsVmxIov *iov,          // IN: iovs (array of mappings)
                   uint32 iovCount,          // IN: iov count of mappings
                   uint32 startIndex,        // IN: start index into iov
                   void *buf,                // IN: contigous Buffer
                   size_t bufSize)           // IN: size of buffer
{
   size_t iovIndex;
   size_t endIndex;
   size_t remainingSize;
   size_t copiedAmount = 0;

   for (iovIndex = startIndex, endIndex = startIndex + iovCount, remainingSize = bufSize;
        iovIndex < endIndex && remainingSize > 0;
        iovIndex++) {
      size_t copyAmount = remainingSize < iov[iovIndex].len ?
                          remainingSize : iov[iovIndex].len;

      memcpy((char *)buf + copiedAmount, iov[iovIndex].va, copyAmount);
      copiedAmount += copyAmount;
      remainingSize -= copyAmount;
   }
   ASSERT(copiedAmount == bufSize && remainingSize == 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPUMapBuf --
 *
 *    Map the buffer for the required size.
 *
 * Results:
 *    TRUE if we mapped the requested size and the number of mappings performed.
 *    Otherwise FALSE if something failed, and 0 mappings performed.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

static Bool
HSPUMapBuf(HgfsChannelMapVirtAddrFunc mapVa,    // IN: map virtual address function
           HgfsChannelUnmapVirtAddrFunc putVa,  // IN: unmap virtual address function
           size_t mapSize,                      // IN: size to map
           uint32 startIndex,                   // IN: Start index of iovs to map
           uint32 iovCount,                     // IN: iov count
           HgfsVmxIov *iov,                     // IN/OUT: iovs (array) to map
           uint32 *mappedCount)                 // OUT: mapped iov count
{
   uint32 iovIndex;
   uint32 mappedIovCount;
   size_t remainingSize;
   Bool mapped = TRUE;

   for (iovIndex = startIndex, mappedIovCount = 0, remainingSize = mapSize;
        iovIndex < iovCount && remainingSize > 0;
        iovIndex++, mappedIovCount++) {

      /* Check: Iov in VMCI should never cross page boundary */
      ASSERT(iov[iovIndex].len <= (PAGE_SIZE - PAGE_OFFSET(iov[iovIndex].pa)));

      iov[iovIndex].va = mapVa(&iov[iovIndex]);
      if (NULL == iov[iovIndex].va) {
         /* Failed to map the physical address. */
         break;
      }
      remainingSize = remainingSize < iov[iovIndex].len ?
                      0: remainingSize - iov[iovIndex].len;
   }

   if (0 != remainingSize) {
      /* Something failed in the mappings Undo any mappings we created. */
      HSPUUnmapBuf(putVa, startIndex, iov, &mappedIovCount);
      mapped = FALSE;
   }

   *mappedCount = mappedIovCount;
   return mapped;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HSPUUnmapBuf --
 *
 *    Unmap the buffer and release guest mappings.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *-----------------------------------------------------------------------------
 */

static void
HSPUUnmapBuf(HgfsChannelUnmapVirtAddrFunc unmapVa, // IN/OUT: Hgfs Packet
             uint32 startIndex,                    // IN: Start index of iovs to map
             HgfsVmxIov *iov,                      // IN/OUT: iovs (array) to map
             uint32 *mappedCount)                  // IN/OUT: iov count to unmap
{
   uint32 iovIndex;
   uint32 endIndex;

   for (iovIndex = startIndex, endIndex = startIndex + *mappedCount;
        iovIndex < endIndex;
        iovIndex++) {
      unmapVa(&iov[iovIndex].context);
      iov[iovIndex].va = NULL;
   }
   *mappedCount = 0;
}
 0707010000008A000081A4000000000000000000000001682255050002F8FE000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerParameters.c  /*********************************************************
 * Copyright (c) 2010-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include <string.h>
#include <stdlib.h>

#include "vmware.h"
#include "str.h"
#include "cpName.h"
#include "cpNameLite.h"
#include "hgfsServerInt.h"
#include "hgfsServerPolicy.h"
#include "codeset.h"
#include "config.h"
#include "file.h"
#include "util.h"
#include "wiper.h"
#include "vm_basic_asm.h"
#include "hgfsServerParameters.h"

#ifdef _WIN32
#define HGFS_OP_CAPFLAG_WIN32_IS_SUPPORTED  HGFS_OP_CAPFLAG_IS_SUPPORTED
#define HGFS_OP_CAPFLAG_POSIX_IS_SUPPORTED  HGFS_OP_CAPFLAG_NOT_SUPPORTED
#else
#define HGFS_OP_CAPFLAG_WIN32_IS_SUPPORTED  HGFS_OP_CAPFLAG_NOT_SUPPORTED
#define HGFS_OP_CAPFLAG_POSIX_IS_SUPPORTED  HGFS_OP_CAPFLAG_IS_SUPPORTED
#endif

#define HGFS_ASSERT_PACK_PARAMS \
   do { \
      ASSERT(packet); \
      ASSERT(packetHeader); \
      ASSERT(session); \
      ASSERT(payloadSize); \
   } while(0)

/*
 * This is the default/minimal set of capabilities which is supported by every transport.
 * Every transport and session may have additional capabilities in addition to these.
 */
static HgfsOpCapability hgfsDefaultCapabilityTable[] =
{
   {HGFS_OP_OPEN,                  HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_READ,                  HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_WRITE,                 HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_CLOSE,                 HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SEARCH_OPEN,           HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SEARCH_READ,           HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SEARCH_CLOSE,          HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_GETATTR,               HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SETATTR,               HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_CREATE_DIR,            HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_DELETE_FILE,           HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_DELETE_DIR,            HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_RENAME,                HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_QUERY_VOLUME_INFO,     HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_OPEN_V2,               HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_GETATTR_V2,            HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SETATTR_V2,            HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SEARCH_READ_V2,        HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_CREATE_SYMLINK,        HGFS_OP_CAPFLAG_POSIX_IS_SUPPORTED},
   {HGFS_OP_SERVER_LOCK_CHANGE,    HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_CREATE_DIR_V2,         HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_DELETE_FILE_V2,        HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_DELETE_DIR_V2,         HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_RENAME_V2,             HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_OPEN_V3,               HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_READ_V3,               HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_WRITE_V3,              HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_CLOSE_V3,              HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SEARCH_OPEN_V3,        HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SEARCH_READ_V3,        HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SEARCH_CLOSE_V3,       HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_GETATTR_V3,            HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_SETATTR_V3,            HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_CREATE_DIR_V3,         HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_DELETE_FILE_V3,        HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_DELETE_DIR_V3,         HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_RENAME_V3,             HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_QUERY_VOLUME_INFO_V3,  HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_CREATE_SYMLINK_V3,     HGFS_OP_CAPFLAG_POSIX_IS_SUPPORTED},
   {HGFS_OP_SERVER_LOCK_CHANGE_V3, HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_WRITE_WIN32_STREAM_V3, HGFS_OP_CAPFLAG_WIN32_IS_SUPPORTED},
   {HGFS_OP_CREATE_SESSION_V4,     HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_DESTROY_SESSION_V4,    HGFS_OP_CAPFLAG_IS_SUPPORTED},
   {HGFS_OP_READ_FAST_V4,          HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_WRITE_FAST_V4,         HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_SET_WATCH_V4,          HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_REMOVE_WATCH_V4,       HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_NOTIFY_V4,             HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_SEARCH_READ_V4,        HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_OPEN_V4,               HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_ENUMERATE_STREAMS_V4,  HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_GETATTR_V4,            HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_SETATTR_V4,            HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_DELETE_V4,             HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_LINKMOVE_V4,           HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_FSCTL_V4,              HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_ACCESS_CHECK_V4,       HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_FSYNC_V4,              HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_QUERY_VOLUME_INFO_V4,  HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_OPLOCK_ACQUIRE_V4,     HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_OPLOCK_BREAK_V4,       HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_LOCK_BYTE_RANGE_V4,    HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_UNLOCK_BYTE_RANGE_V4,  HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_QUERY_EAS_V4,          HGFS_OP_CAPFLAG_NOT_SUPPORTED},
   {HGFS_OP_SET_EAS_V4,            HGFS_OP_CAPFLAG_NOT_SUPPORTED},
};


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsValidateReplySize --
 *
 *    Verify if the size of a reply does not exceed maximum supported size.
 *
 * Results:
 *    TRUE if the packet size is acceptable, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsValidateReplySize(char const *packetIn,
                      HgfsOp op,
                      size_t packetSize)
{
   Bool result;
   HgfsRequest *request = (HgfsRequest *)packetIn;

   if (HGFS_OP_NEW_HEADER != request->op) {
      if (HGFS_OP_READ_V3 == op) {
         result = packetSize <= HgfsLargePacketMax(FALSE);
      } else {
         result = packetSize <= HGFS_PACKET_MAX;
      }
   } else {
      result = TRUE;
   }
   if (!result) {
      LOG(4, "%s: Reply exceeded maximum support size!\n", __FUNCTION__);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsGetPayloadSize --
 *
 *    Returns size of the payload based on incoming packet and total
 *    packet size.
 *
 * Results:
 *    Size of the payload in bytes.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

size_t
HgfsGetPayloadSize(char const *packetIn,        // IN: request packet
                   size_t packetSize)           // IN: request packet size
{
   HgfsRequest *request = (HgfsRequest *)packetIn;
   size_t result;
   ASSERT(packetSize >= sizeof *request);
   if (request->op < HGFS_OP_CREATE_SESSION_V4) {
      result = packetSize - sizeof *request;
   } else {
      HgfsHeader *header = (HgfsHeader *)packetIn;
      ASSERT(packetSize >= header->packetSize);
      ASSERT(header->packetSize >= header->headerSize);
      result = header->packetSize - header->headerSize;
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackHeaderV1V2 --
 *
 *    Unpack the client request that contains a basic valid HgfsHeader for protocol
 *    versions 1 and 2.
 *    Extract the useful details for the caller.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS always.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsUnpackHeaderV1V2(const HgfsRequest *request,  // IN: request header
                     size_t requestSize,          // IN: request data size
                     uint32 *requestId,           // OUT: unique request id
                     HgfsOp *opcode,              // OUT: request opcode
                     size_t *payloadSize,         // OUT: size of the payload
                     const void **payload)        // OUT: pointer to the payload
{
   /* V1 or V2 requests do not have a separate header. */
   *requestId   = request->id;
   *opcode      = request->op;
   *payloadSize = requestSize;
   *payload     = request;
   return HGFS_ERROR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackHeaderV3 --
 *
 *    Unpack the client request that contains a basic valid HgfsHeader for protocol
 *    version 3.
 *    Extract the useful details for the caller.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS always.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsUnpackHeaderV3(const HgfsRequest *request,  // IN: request header
                   size_t requestSize,          // IN: request data size
                   uint32 *requestId,           // OUT: unique request id
                   HgfsOp *opcode,              // OUT: request opcode
                   size_t *payloadSize,         // OUT: size of the payload
                   const void **payload)        // OUT: pointer to the payload
{
   /* Old header with V3 request. */
   *requestId   = request->id;
   *opcode      = request->op;
   if (requestSize > sizeof *request) {
      *payload = HGFS_REQ_GET_PAYLOAD_V3(request);
      *payloadSize = requestSize - ((char *)*payload - (char *)request);
   } else {
      *payload = NULL;
      *payloadSize = 0;
   }
   return HGFS_ERROR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackHeaderV4 --
 *
 *    Unpack the client request that contains a basic valid HgfsHeader for protocol
 *    version 4 and newer.
 *    Extract the useful details for the caller.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS if successful or
 *    HGFS_ERROR_PROTOCOL for a malformed request, and we cannot trust the data.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static HgfsInternalStatus
HgfsUnpackHeaderV4(const HgfsHeader *requestHeader,   // IN: request header
                   size_t requestSize,                // IN: request data size
                   uint64 *sessionId,                 // OUT: session Id
                   uint32 *requestId,                 // OUT: unique request id
                   uint32 *hdrFlags,                  // OUT: header flags
                   uint32 *information,               // OUT: generic information
                   HgfsOp *opcode,                    // OUT: request opcode
                   size_t *payloadSize,               // OUT: size of the payload
                   const void **payload)              // OUT: pointer to the payload
{
   HgfsInternalStatus status = HGFS_ERROR_SUCCESS;

   if (requestSize < sizeof *requestHeader) {
      LOG(4, "%s: Malformed HGFS packet received - header is too small!\n",
          __FUNCTION__);
      status = HGFS_ERROR_PROTOCOL;
      goto exit;
   }

   if (requestSize < requestHeader->packetSize ||
       requestHeader->packetSize < requestHeader->headerSize) {
      LOG(4, "%s: Malformed HGFS packet received - inconsistent header "
          "and packet sizes!\n", __FUNCTION__);
      status = HGFS_ERROR_PROTOCOL;
      goto exit;
   }

   if (HGFS_HEADER_VERSION_1 > requestHeader->version) {
      LOG(4, "%s: Malformed HGFS packet received - invalid header version!\n",
          __FUNCTION__);
      status = HGFS_ERROR_PROTOCOL;
      goto exit;
   }

   ASSERT(HGFS_OP_NEW_HEADER == requestHeader->dummy);

   /* The basics of the header are validated, get the remaining parameters. */
   *sessionId   = requestHeader->sessionId;
   *requestId   = requestHeader->requestId;
   *opcode      = requestHeader->op;

   /*
    * For version 1 of the header the file copy client did not ensure
    * the following fields (and reserved fields) were set and thus can
    * contain garbage.
    * For this reason, we just zero out these fields for this header version.
    */
   if (HGFS_HEADER_VERSION_1 == requestHeader->version) {
      *hdrFlags    = 0;
      *information = 0;
   } else {
      *hdrFlags    = requestHeader->flags;
      *information = requestHeader->information;
   }

   *payloadSize = requestHeader->packetSize - requestHeader->headerSize;
   if (0 < *payloadSize) {
      *payload = (char *)requestHeader + requestHeader->headerSize;
   } else {
      *payload = NULL;
      Log("%s: HGFS packet with header and no payload!\n", __FUNCTION__);
   }

exit:
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackPacketParams --
 *
 *    Takes the Hgfs packet and extracts the operation parameters.
 *    This validates the incoming packet as part of the processing.
 *
 * Results:
 *    HGFS_ERROR_SUCCESS if all the request parameters are successfully extracted.
 *    HGFS_ERROR_INTERNAL if an error occurs without sufficient request data to be
 *    able to send a reply to the client.
 *    Any other appropriate error if the incoming packet has errors and there is
 *    sufficient information to send a response.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsUnpackPacketParams(const void *packet,      // IN: HGFS packet
                       size_t packetSize,       // IN: request packet size
                       Bool *sessionEnabled,    // OUT: session enabled request
                       uint64 *sessionId,       // OUT: session Id
                       uint32 *requestId,       // OUT: unique request id
                       HgfsOp *opcode,          // OUT: request opcode
                       size_t *payloadSize,     // OUT: size of the opcode request
                       const void **payload)    // OUT: pointer to the opcode request
{
   const HgfsRequest *request;
   HgfsInternalStatus unpackStatus = HGFS_ERROR_SUCCESS;

   ASSERT(NULL != packet);

   request = packet;
   LOG(4, "%s: Received a request with opcode %d.\n", __FUNCTION__, request->op);

   /*
    * Error out if less than HgfsRequest size.
    * We cannot continue any further with this packet.
    */
   if (packetSize < sizeof *request) {
      LOG(4, "%s: Received a request with opcode %"FMTSZ"u.\n",
          __FUNCTION__, packetSize);
      unpackStatus = HGFS_ERROR_INTERNAL;
      goto exit;
   }

   *sessionEnabled = FALSE;

   if (request->op < HGFS_OP_OPEN_V3) {
      unpackStatus = HgfsUnpackHeaderV1V2(request,
                                          packetSize,
                                          requestId,
                                          opcode,
                                          payloadSize,
                                          payload);
   } else if (request->op < HGFS_OP_CREATE_SESSION_V4) {
      unpackStatus = HgfsUnpackHeaderV3(request,
                                        packetSize,
                                        requestId,
                                        opcode,
                                        payloadSize,
                                        payload);
   } else if (HGFS_OP_NEW_HEADER == request->op) {
      /* The legacy op means a new header but we can have V3 and newer opcodes. */
      const HgfsHeader *requestHdr = packet;
      uint32 hdrFlags = 0;
      uint32 information;

      *sessionEnabled = TRUE;

      unpackStatus = HgfsUnpackHeaderV4(requestHdr,
                                        packetSize,
                                        sessionId,
                                        requestId,
                                        &hdrFlags,
                                        &information,
                                        opcode,
                                        payloadSize,
                                        payload);

      /*
       * Test if the client sent invalid flags (and information in future cases).
       * Note, a basic sanitation was done in the unpack header itself, only V2
       * or newer allowed to pass meaningful values through.
       */
      if (0 != hdrFlags &&
          0 == (hdrFlags & (HGFS_PACKET_FLAG_REQUEST | HGFS_PACKET_FLAG_REPLY))) {
         unpackStatus = HGFS_ERROR_PROTOCOL;
      }

   } else {
      LOG(4, "%s: HGFS packet - unknown opcode == newer client or malformed!\n",
          __FUNCTION__);
      unpackStatus = HGFS_ERROR_INTERNAL;
   }

exit:
   LOG(4, "%s: unpacked request(op %d, id %u) -> %u.\n", __FUNCTION__,
       request->op, *requestId, unpackStatus);
   return unpackStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackOpenPayloadV1 --
 *
 *    Unpack and validate payload for hgfs open request V1 to the HgfsFileOpenInfo
 *    structure that is used to pass around open request information.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackOpenPayloadV1(const HgfsRequestOpen *requestV1, // IN: request payload
                        size_t payloadSize,               // IN: request payload size
                        HgfsFileOpenInfo *openInfo)       // IN/OUT: open info struct
{
   size_t extra;

   /* Enforced by the dispatch function. */
   if (payloadSize < sizeof *requestV1) {
      LOG(4, "%s: Malformed HGFS packet received - payload too small\n",
          __FUNCTION__);
      return FALSE;
   }

   extra = payloadSize - sizeof *requestV1;

   /*
    * The request file name length is user-provided, so this test must be
    * carefully written to prevent wraparounds.
    */
   if (requestV1->fileName.length > extra) {
      /* The input packet is smaller than the request. */
      LOG(4, "%s: Malformed HGFS packet received - payload too small to hold file name\n",
          __FUNCTION__);
      return FALSE;
   }

   /* For OpenV1 requests, we know exactly what fields we expect. */
   openInfo->mask = HGFS_OPEN_VALID_MODE |
                    HGFS_OPEN_VALID_FLAGS |
                    HGFS_OPEN_VALID_OWNER_PERMS |
                    HGFS_OPEN_VALID_FILE_NAME;
   openInfo->mode = requestV1->mode;
   openInfo->cpName = requestV1->fileName.name;
   openInfo->cpNameSize = requestV1->fileName.length;
   openInfo->flags = requestV1->flags;
   openInfo->ownerPerms = requestV1->permissions;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackOpenPayloadV2 --
 *
 *    Unpack and validate payload for hgfs open request V2 to the HgfsFileOpenInfo
 *    structure that is used to pass around open request information.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackOpenPayloadV2(const HgfsRequestOpenV2 *requestV2, // IN: request payload
                        size_t payloadSize,                 // IN: request payload size
                        HgfsFileOpenInfo *openInfo)         // IN/OUT: open info struct
{
   size_t extra;

   /* Enforced by the dispatch function. */
   if (payloadSize < sizeof *requestV2) {
      LOG(4, "%s: Malformed HGFS packet received - payload too small\n",
          __FUNCTION__);
      return FALSE;
   }

   extra = payloadSize - sizeof *requestV2;

   if (!(requestV2->mask & HGFS_OPEN_VALID_FILE_NAME)) {
      /* We do not support open requests without a valid file name. */
      LOG(4, "%s: Malformed HGFS packet received - invalid mask\n",
          __FUNCTION__);
      return FALSE;
   }

   /*
    * The request file name length is user-provided, so this test must be
    * carefully written to prevent wraparounds.
    */
   if (requestV2->fileName.length > extra) {
      /* The input packet is smaller than the request. */
      LOG(4, "%s: Malformed HGFS packet received - payload too small to hold file name\n",
          __FUNCTION__);
      return FALSE;
   }

   /*
    * Copy all the fields into our carrier struct. Some will probably be
    * garbage, but it's simpler to copy everything now and check the
    * valid bits before reading later.
    */

   openInfo->mask = requestV2->mask;
   openInfo->mode = requestV2->mode;
   openInfo->cpName = requestV2->fileName.name;
   openInfo->cpNameSize = requestV2->fileName.length;
   openInfo->flags = requestV2->flags;
   openInfo->specialPerms = requestV2->specialPerms;
   openInfo->ownerPerms = requestV2->ownerPerms;
   openInfo->groupPerms = requestV2->groupPerms;
   openInfo->otherPerms = requestV2->otherPerms;
   openInfo->attr = requestV2->attr;
   openInfo->allocationSize = requestV2->allocationSize;
   openInfo->desiredAccess = requestV2->desiredAccess;
   openInfo->shareAccess = requestV2->shareAccess;
   openInfo->desiredLock = requestV2->desiredLock;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackOpenPayloadV3 --
 *
 *    Unpack and validate payload for hgfs open request V3 to the HgfsFileOpenInfo
 *    structure that is used to pass around open request information.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackOpenPayloadV3(const HgfsRequestOpenV3 *requestV3, // IN: request payload
                        size_t payloadSize,                 // IN: request payload size
                        HgfsFileOpenInfo *openInfo)         // IN/OUT: open info struct
{
   size_t extra;

   /* Enforced by the dispatch function. */
   if (payloadSize < sizeof *requestV3) {
      LOG(4, "%s: Malformed HGFS packet received - payload too small\n",
          __FUNCTION__);
      return FALSE;
   }

   extra = payloadSize - sizeof *requestV3;

   if (!(requestV3->mask & HGFS_OPEN_VALID_FILE_NAME)) {
      /* We do not support open requests without a valid file name. */
      LOG(4, "%s: Malformed HGFS packet received - incorrect mask\n",
          __FUNCTION__);
      return FALSE;
   }

   /*
    * The request file name length is user-provided, so this test must be
    * carefully written to prevent wraparounds.
    */
   if (requestV3->fileName.length > extra) {
      /* The input packet is smaller than the request. */
      LOG(4, "%s: Malformed HGFS packet received - payload too small to hold file name\n",
          __FUNCTION__);
      return FALSE;
   }

   /*
    * Copy all the fields into our carrier struct. Some will probably be
    * garbage, but it's simpler to copy everything now and check the
    * valid bits before reading later.
    */
   openInfo->mask = requestV3->mask;
   openInfo->mode = requestV3->mode;
   openInfo->cpName = requestV3->fileName.name;
   openInfo->cpNameSize = requestV3->fileName.length;
   openInfo->caseFlags = requestV3->fileName.caseType;
   openInfo->flags = requestV3->flags;
   openInfo->specialPerms = requestV3->specialPerms;
   openInfo->ownerPerms = requestV3->ownerPerms;
   openInfo->groupPerms = requestV3->groupPerms;
   openInfo->otherPerms = requestV3->otherPerms;
   openInfo->attr = requestV3->attr;
   openInfo->allocationSize = requestV3->allocationSize;
   openInfo->desiredAccess = requestV3->desiredAccess;
   openInfo->shareAccess = requestV3->shareAccess;
   openInfo->desiredLock = requestV3->desiredLock;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackOpenRequest --
 *
 *    Unpack hgfs open request to the HgfsFileOpenInfo structure that is used
 *    to pass around open request information.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackOpenRequest(const void *packet,         // IN: HGFS packet
                      size_t packetSize,          // IN: packet size
                      HgfsOp op,                  // IN: requested operation
                      HgfsFileOpenInfo *openInfo) // IN/OUT: open info structure
{
   Bool result;

   ASSERT(packet);
   ASSERT(openInfo);

   openInfo->requestType = op;
   openInfo->caseFlags = HGFS_FILE_NAME_DEFAULT_CASE;

   switch (op) {
   case HGFS_OP_OPEN_V3: {
         const HgfsRequestOpenV3 *requestV3 = packet;
         LOG(4, "%s: HGFS_OP_OPEN_V3\n", __FUNCTION__);

         result = HgfsUnpackOpenPayloadV3(requestV3, packetSize, openInfo);
         break;
      }
   case HGFS_OP_OPEN_V2: {
         const HgfsRequestOpenV2 *requestV2 = packet;
         LOG(4, "%s: HGFS_OP_OPEN_V2\n", __FUNCTION__);

         result = HgfsUnpackOpenPayloadV2(requestV2, packetSize, openInfo);
         break;
      }
   case HGFS_OP_OPEN: {
         const HgfsRequestOpen *requestV1 = packet;
         LOG(4, "%s: HGFS_OP_OPEN\n", __FUNCTION__);

         result = HgfsUnpackOpenPayloadV1(requestV1, packetSize, openInfo);
         break;
      }
   default:
      NOT_REACHED();
      result = FALSE;
   }

   if (!result) {
      LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackReplyHeaderV4 --
 *
 *    Pack hgfs header that corresponds to an HGFS protocol packet.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsPackReplyHeaderV4(HgfsStatus status,            // IN: reply status
                      uint32 payloadSize,           // IN: size of the reply payload
                      HgfsOp opcode,                // IN: request type
                      uint64 sessionId,             // IN: session id
                      uint32 requestId,             // IN: request id
                      uint32 hdrFlags,              // IN: header flags
                      size_t hdrPacketSize,         // IN: header packet size
                      HgfsHeader *hdrPacket)        // OUT: outgoing packet header
{
   Bool result = FALSE;

   if (hdrPacketSize >= sizeof *hdrPacket) {
      memset(hdrPacket, 0, sizeof *hdrPacket);

      hdrPacket->version = HGFS_HEADER_VERSION;
      hdrPacket->dummy = HGFS_OP_NEW_HEADER;
      hdrPacket->packetSize = payloadSize + sizeof *hdrPacket;
      hdrPacket->headerSize = sizeof *hdrPacket;
      hdrPacket->requestId = requestId;
      hdrPacket->op = opcode;
      hdrPacket->status = status;
      hdrPacket->flags = hdrFlags;
      hdrPacket->information = status;
      hdrPacket->sessionId = sessionId;
      result = TRUE;
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackLegacyReplyHeader --
 *
 *    Pack pre-V4 reply header.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsPackLegacyReplyHeader(HgfsStatus status,            // IN: reply status
                          HgfsHandle id,                // IN: original packet id
                          size_t hdrPacketSize,         // IN: header packet size
                          HgfsReply *hdrPacket)         // OUT: outgoing packet header
{
   Bool result = FALSE;

   if (hdrPacketSize >= sizeof *hdrPacket) {
      memset(hdrPacket, 0, sizeof *hdrPacket);

      hdrPacket->status = status;
      hdrPacket->id = id;
      result = TRUE;
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackReplyHeader --
 *
 *    Pack hgfs header that corresponds to an HGFS protocol packet.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackReplyHeader(HgfsInternalStatus status,    // IN: reply status
                    uint32 payloadSize,           // IN: size of the reply payload
                    Bool sessionEnabledHeader,    // IN: session enabled header
                    uint64 sessionId,             // IN: session id
                    uint32 requestId,             // IN: request id
                    HgfsOp opcode,                // IN: request operation
                    uint32 hdrFlags,              // IN: header flags
                    size_t hdrPacketSize,         // IN: header packet size
                    void *hdrPacket)              // OUT: outgoing packet header
{
   HgfsStatus replyStatus;
   Bool result;

   if (NULL == hdrPacket) {
      result = FALSE;
      goto exit;
   }

   replyStatus = HgfsConvertFromInternalStatus(status);
   if (sessionEnabledHeader) {
      result = HgfsPackReplyHeaderV4(replyStatus, payloadSize, opcode,
                                     sessionId, requestId, HGFS_PACKET_FLAG_REPLY,
                                     hdrPacketSize, hdrPacket);
   } else {
      result = HgfsPackLegacyReplyHeader(replyStatus, requestId, hdrPacketSize,
                                         hdrPacket);
   }

exit:
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackOpenReplyV3 --
 *
 *    Pack hgfs open V3 reply payload to the HgfsReplyOpenV3 structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackOpenReplyV3(HgfsFileOpenInfo *openInfo,   // IN: open info struct
                    HgfsReplyOpenV3 *reply)       // OUT: size of packet
{
   reply->file = openInfo->file;
   reply->reserved = 0;
   reply->flags = 0;
   if (openInfo->mask & HGFS_OPEN_VALID_SERVER_LOCK) {
      reply->acquiredLock = openInfo->acquiredLock;
   } else {
      reply->acquiredLock = HGFS_LOCK_NONE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackOpenV2Reply --
 *
 *    Pack hgfs open V2 reply payload to the HgfsReplyOpenV3 structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackOpenV2Reply(HgfsFileOpenInfo *openInfo,   // IN: open info struct
                    HgfsReplyOpenV2 *reply)       // OUT: reply payload
{
   reply->file = openInfo->file;
   if (openInfo->mask & HGFS_OPEN_VALID_SERVER_LOCK) {
      reply->acquiredLock = openInfo->acquiredLock;
   } else {
      reply->acquiredLock = HGFS_LOCK_NONE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackOpenV1Reply --
 *
 *    Pack hgfs open V1 reply payload to the HgfsReplyOpenV3 structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackOpenV1Reply(HgfsFileOpenInfo *openInfo,   // IN: open info struct
                    HgfsReplyOpen *reply)         // OUT: reply payload
{
   reply->file = openInfo->file;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackOpenReply --
 *
 *    Pack hgfs open reply to the HgfsReplyOpen{V2} structure.
 *
 * Results:
 *    Always TRUE, FALSE if bad opcode.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackOpenReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                  const void *packetHeader,     // IN: packet header
                  HgfsFileOpenInfo *openInfo,   // IN: open info struct
                  size_t *payloadSize,          // OUT: size of packet
                  HgfsSessionInfo *session)     // IN: Session info
{
   Bool result = TRUE;

   ASSERT(openInfo);
   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   switch (openInfo->requestType) {
   case HGFS_OP_OPEN_V3: {
      HgfsReplyOpenV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      HgfsPackOpenReplyV3(openInfo, reply);
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_OPEN_V2: {
      HgfsReplyOpenV2 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      HgfsPackOpenV2Reply(openInfo, reply);
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_OPEN: {
      HgfsReplyOpen *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      HgfsPackOpenV1Reply(openInfo, reply);
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      NOT_REACHED();
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackClosePayload --
 *
 *    Unpack hgfs close payload to get the handle which need to be closed.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackClosePayload(const HgfsRequestClose *request,   // IN: payload
                       size_t payloadSize,                // IN: payload size
                       HgfsHandle* file)                  // OUT: HGFS handle to close
{
   LOG(4, "%s: HGFS_OP_CLOSE\n", __FUNCTION__);
   if (payloadSize >= sizeof *request) {
      *file = request->file;
      return TRUE;
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackClosePayloadV3 --
 *
 *    Unpack hgfs close payload V3 to get the handle which need to be closed.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackClosePayloadV3(const HgfsRequestCloseV3 *requestV3, // IN: payload
                         size_t payloadSize,                  // IN: payload size
                         HgfsHandle* file)                    // OUT: HGFS handle to close
{
   LOG(4, "%s: HGFS_OP_CLOSE_V3\n", __FUNCTION__);
   if (payloadSize >= sizeof *requestV3) {
      *file = requestV3->file;
      return TRUE;
   }
   LOG(4, "%s: Too small HGFS packet\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackCloseRequest --
 *
 *    Unpack hgfs close request to get the handle to close.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackCloseRequest(const void *packet,  // IN: request packet
                       size_t packetSize,   // IN: request packet size
                       HgfsOp op,           // IN: request type
                       HgfsHandle *file)    // OUT: Handle to close
{
   ASSERT(packet);

   switch (op) {
   case HGFS_OP_CLOSE_V3: {
      const HgfsRequestCloseV3 *requestV3 = packet;

      if (!HgfsUnpackClosePayloadV3(requestV3, packetSize, file)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   case HGFS_OP_CLOSE: {
      const HgfsRequestClose *requestV1 = packet;

      if (!HgfsUnpackClosePayload(requestV1, packetSize, file)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   default:
      NOT_REACHED();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackCloseReply --
 *
 *    Pack hgfs close reply to the HgfsReplyClose(V3) structure.
 *
 * Results:
 *    Always TRUE, FALSE if bad opcode.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackCloseReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                   const void *packetHeader,   // IN: packet header
                   HgfsOp op,                  // IN: request type
                   size_t *payloadSize,        // OUT: size of packet excluding header
                   HgfsSessionInfo *session)   // IN: Session info
{
   Bool result = TRUE;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_CLOSE_V3: {
      HgfsReplyCloseV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      /* Reply consists of a reserved field only. */
      reply->reserved = 0;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_CLOSE: {
      HgfsReplyClose *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      NOT_REACHED();
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSearchClosePayload --
 *
 *    Unpack hgfs search close payload to get the search handle which need to be closed.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSearchClosePayload(const HgfsRequestSearchClose *request, // IN: payload
                             size_t payloadSize,                    // IN: payload size
                             HgfsHandle* search)                    // OUT: search to close
{
   LOG(4, "%s: HGFS_OP_SEARCH_CLOSE\n", __FUNCTION__);
   if (payloadSize >= sizeof *request) {
      *search = request->search;
      return TRUE;
   }
   LOG(4, "%s: Too small HGFS packet\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSearchClosePayloadV3 --
 *
 *    Unpack hgfs search close payload V3 to get the search handle which need to
 *    be closed.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSearchClosePayloadV3(const HgfsRequestSearchCloseV3 *requestV3, // IN: payload
                               size_t payloadSize,                        // IN: payload size
                               HgfsHandle* search)                        // OUT: search
{
   LOG(4, "%s: HGFS_OP_SEARCH_CLOSE_V3\n", __FUNCTION__);
   if (payloadSize >= sizeof *requestV3) {
      *search = requestV3->search;
      return TRUE;
   }
   LOG(4, "%s: Too small HGFS packet\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSearchCloseRequest --
 *
 *    Unpack hgfs search close request to get the search handle.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackSearchCloseRequest(const void *packet,   // IN: request packet
                             size_t packetSize,    // IN: request packet size
                             HgfsOp op,            // IN: request type
                             HgfsHandle *search)   // OUT: search to close
{
   ASSERT(packet);

   switch (op) {
   case HGFS_OP_SEARCH_CLOSE_V3: {
      const HgfsRequestSearchCloseV3 *requestV3 = packet;

      if (!HgfsUnpackSearchClosePayloadV3(requestV3, packetSize, search)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   case HGFS_OP_SEARCH_CLOSE: {
      const HgfsRequestSearchClose *requestV1 = packet;

      if (!HgfsUnpackSearchClosePayload(requestV1, packetSize, search)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   default:
      NOT_REACHED();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchCloseReply --
 *
 *    Pack hgfs SearchClose reply into a HgfsReplySearchClose(V3) structure.
 *
 * Results:
 *    Always TRUE, except when it is called with a
 *    wrong op or insufficient output buffer (which is a programming error).
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackSearchCloseReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                         const void *packetHeader,   // IN: packet header
                         HgfsOp op,                  // IN: request type
                         size_t *payloadSize,        // OUT: size of packet
                         HgfsSessionInfo *session)   // IN: Session info
{
   Bool result = TRUE;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_SEARCH_CLOSE_V3: {
      HgfsReplyCloseV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      /* Reply consists of only a reserved field. */
      reply->reserved = 0;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_SEARCH_CLOSE: {
      HgfsReplyClose *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      NOT_REACHED();
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackFileName --
 *
 *    Unpack HgfsFileName into a pointer to a CPName and size of the name.
 *    Verifies that input buffer has enough space to hold the name.
 *
 * Results:
 *    TRUE on success, FALSE on failure (buffer too small).
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackFileName(const HgfsFileName *name,     // IN: file name
                   size_t maxNameSize,           // IN: space allocated for the name
                   const char **cpName,          // OUT: CP name
                   size_t *cpNameSize)           // OUT: CP name size
{
   /*
    * The request file name length is user-provided, so this test must be
    * carefully written to prevent wraparounds.
    */
   if (name->length > maxNameSize) {
      /* The input packet is smaller than the request. */
      return FALSE;
   }
   *cpName = name->name;
   *cpNameSize = name->length;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackFileNameV3 --
 *
 *    Unpack HgfsFileNameV3 into a pointer to a CPName and size of the name
 *    or into file handle.
 *    Verifies that input buffer has enough space to hold the name.
 *
 * Results:
 *    TRUE on success, FALSE on failure (buffer too small).
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackFileNameV3(const HgfsFileNameV3 *name,   // IN: file name
                     size_t maxNameSize,           // IN: space allocated for the name
                     Bool *useHandle,              // OUT: file name or handle returned?
                     const char **cpName,          // OUT: CP name
                     size_t *cpNameSize,           // OUT: CP name size
                     HgfsHandle *file,             // OUT: HGFS file handle
                     uint32 *caseFlags)            // OUT: case-sensitivity flags
{
   *useHandle = FALSE;
   *file = HGFS_INVALID_HANDLE;
   *cpName = NULL;
   *cpNameSize = 0;

   /*
    * If we've been asked to reuse a handle, we don't need to look at, let
    * alone test the filename or its length.
    */
   if (name->flags & HGFS_FILE_NAME_USE_FILE_DESC) {
      *file = name->fid;
      *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
      *useHandle = TRUE;
   } else {
      /*
       * The request file name length is user-provided, so this test must be
       * carefully written to prevent wraparounds.
       */
      if (name->length > maxNameSize) {
         /* The input packet is smaller than the request */
         LOG(4, "%s: Error unpacking file name - buffer too small\n",
             __FUNCTION__);
         return FALSE;
      }
      *cpName = name->name;
      *cpNameSize = name->length;
      *caseFlags = name->caseType;
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackDeletePayloadV3 --
 *
 *    Unpack hgfs delete request V3 payload and initialize a corresponding
 *    HgfsHandle or file name to tell us which to delete. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to delete.
 *
 *    Since the structure of the get delete request packet is the same
 *    for Delete File or Directory of the protocol, code is identical for
 *    both operations.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackDeletePayloadV3(const HgfsRequestDeleteV3 *requestV3, // IN: request payload
                          size_t payloadSize,                   // IN: payload size
                          const char **cpName,                  // OUT: cpName
                          size_t *cpNameSize,                   // OUT: cpName size
                          HgfsDeleteHint *hints,                // OUT: delete hints
                          HgfsHandle *file,                     // OUT: file handle
                          uint32 *caseFlags)                    // OUT: case-sensitivity flags
{
   Bool result = TRUE;
   Bool useHandle;

   if (payloadSize < sizeof *requestV3) {
      result = FALSE;
      goto exit;
   }

   *hints = requestV3->hints;

   if (!HgfsUnpackFileNameV3(&requestV3->fileName,
                             payloadSize - sizeof *requestV3,
                             &useHandle,
                             cpName,
                             cpNameSize,
                             file,
                             caseFlags)) {
      result = FALSE;
      goto exit;
   }

   if (useHandle) {
      *hints |= HGFS_DELETE_HINT_USE_FILE_DESC;
   }

exit:
   LOG(8, "%s: unpacking HGFS_OP_DELETE_DIR/FILE_V3 -> %d\n", __FUNCTION__,
       result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackDeletePayloadV2 --
 *
 *    Unpack hgfs delete request V2 payload and initialize a corresponding
 *    HgfsHandle or file name to tell us which to delete. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to delete.
 *
 *    Since the structure of the get delete request packet is the same
 *    for Delete File or Directory of the protocol, code is identical for
 *    both operations.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackDeletePayloadV2(const HgfsRequestDeleteV2 *requestV2, // IN: request payload
                          size_t payloadSize,                   // IN: payload size
                          const char **cpName,                  // OUT: cpName
                          size_t *cpNameSize,                   // OUT: cpName size
                          HgfsDeleteHint *hints,                // OUT: delete hints
                          HgfsHandle *file)                     // OUT: file handle
{
   Bool result = TRUE;

   /* Enforced by the dispatch function. */
   ASSERT(payloadSize >= sizeof *requestV2);

   *file = HGFS_INVALID_HANDLE;
   *hints = requestV2->hints;

   /*
    * If we've been asked to reuse a handle, we don't need to look at, let
    * alone test the filename or its length.
    */

   if (requestV2->hints & HGFS_DELETE_HINT_USE_FILE_DESC) {
      *file = requestV2->file;
      *cpName = NULL;
      *cpNameSize = 0;
   } else {
      result = HgfsUnpackFileName(&requestV2->fileName,
                                  payloadSize - sizeof *requestV2,
                                  cpName,
                                  cpNameSize);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackDeletePayloadV1 --
 *
 *    Unpack hgfs delete request V1 payload and initialize a corresponding
 *    file name to tell us which to delete.
 *
 *    Since the structure of the get delete request packet is the same
 *    for Delete File or Directory of the protocol, code is identical for
 *    both operations.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackDeletePayloadV1(const HgfsRequestDelete *requestV1,   // IN: request payload
                          size_t payloadSize,                   // IN: payload size
                          const char **cpName,                  // OUT: cpName
                          size_t *cpNameSize)                   // OUT: cpName size
{
   return HgfsUnpackFileName(&requestV1->fileName,
                             payloadSize - sizeof *requestV1,
                             cpName,
                             cpNameSize);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackDeleteRequest --
 *
 *    Unpack hgfs delete request and initialize a corresponding
 *    HgfsHandle or file name to tell us which to delete. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to delete.
 *
 *    Since the structure of the get delete request packet is the same
 *    for Delete File or Directory of the protocol, code is identical for
 *    both operations.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackDeleteRequest(const void *packet,      // IN: HGFS packet
                        size_t packetSize,       // IN: request packet size
                        HgfsOp op,               // IN: requested operation
                        const char **cpName,     // OUT: cpName
                        size_t *cpNameSize,      // OUT: cpName size
                        HgfsDeleteHint *hints,   // OUT: delete hints
                        HgfsHandle *file,        // OUT: file handle
                        uint32 *caseFlags)       // OUT: case-sensitivity flags
{
   ASSERT(packet);
   ASSERT(cpName);
   ASSERT(cpNameSize);
   ASSERT(file);
   ASSERT(hints);
   ASSERT(caseFlags);

   *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
   *hints = 0;
   *file = HGFS_INVALID_HANDLE;

   switch (op) {
   case HGFS_OP_DELETE_FILE_V3:
   case HGFS_OP_DELETE_DIR_V3: {
      const HgfsRequestDeleteV3 *requestV3 = packet;

      if (!HgfsUnpackDeletePayloadV3(requestV3,
                                     packetSize,
                                     cpName,
                                     cpNameSize,
                                     hints,
                                     file,
                                     caseFlags)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   case HGFS_OP_DELETE_FILE_V2:
   case HGFS_OP_DELETE_DIR_V2: {
      const HgfsRequestDeleteV2 *requestV2 = packet;

      if (!HgfsUnpackDeletePayloadV2(requestV2,
                                     packetSize,
                                     cpName,
                                     cpNameSize,
                                     hints,
                                     file)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   case HGFS_OP_DELETE_FILE:
   case HGFS_OP_DELETE_DIR: {
      const HgfsRequestDelete *requestV1 = packet;

      if (!HgfsUnpackDeletePayloadV1(requestV1,
                                     packetSize,
                                     cpName,
                                     cpNameSize)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   default:
      NOT_REACHED();
      LOG(4, "%s: Invalid opcode\n", __FUNCTION__);
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackDeleteReply --
 *
 *    Pack hgfs delete reply.
 *    Since the structure of the delete reply packet hasn't changed in
 *    version 2 of the protocol, HgfsReplyDeleteV2 is identical to
 *    HgfsReplyDelete. So use HgfsReplyDelete type to access packetIn to
 *    keep the code simple.
 *
 * Results:
 *    TRUE if valid op version reply filled, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackDeleteReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                    const void *packetHeader,  // IN: packet header
                    HgfsOp op,                 // IN: requested operation
                    size_t *payloadSize,       // OUT: size of packet
                    HgfsSessionInfo *session)  // IN: Session info
{
   Bool result = TRUE;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   /* No reply payload, just header. */
   switch (op) {
   case HGFS_OP_DELETE_FILE_V3:
   case HGFS_OP_DELETE_DIR_V3: {
      HgfsReplyDeleteV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_DELETE_FILE_V2:
   case HGFS_OP_DELETE_FILE:
   case HGFS_OP_DELETE_DIR_V2:
   case HGFS_OP_DELETE_DIR: {
      HgfsReplyDelete *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      LOG(4, "%s: invalid op code %d\n", __FUNCTION__, op);
      result = FALSE;
      NOT_REACHED();
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackRenamePayloadV3 --
 *
 *    Unpack hgfs rename request V3 payload and initialize a corresponding
 *    HgfsHandles or file names to tell us old and new names/handles. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to rename.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackRenamePayloadV3(const HgfsRequestRenameV3 *requestV3, // IN: request payload
                          size_t payloadSize,                   // IN: payload size
                          const char **cpOldName,               // OUT: rename src
                          size_t *cpOldNameLen,                 // OUT: rename src size
                          const char **cpNewName,               // OUT: rename dst
                          size_t *cpNewNameLen,                 // OUT: rename dst size
                          HgfsRenameHint *hints,                // OUT: rename hints
                          HgfsHandle *srcFile,                  // OUT: src file handle
                          HgfsHandle *targetFile,               // OUT: target file handle
                          uint32 *oldCaseFlags,                 // OUT: source case flags
                          uint32 *newCaseFlags)                 // OUT: dest. case flags
{
   size_t extra;
   const HgfsFileNameV3 *newName;
   Bool useHandle;

   LOG(4, "%s: HGFS_OP_RENAME_V3\n", __FUNCTION__);

   if (payloadSize < sizeof *requestV3) {
      return FALSE;
   }
   extra = payloadSize - sizeof *requestV3;

   *hints = requestV3->hints;

   /*
    * Get the old and new filenames from the request.
    *
    * Getting the new filename is somewhat inconvenient, because we
    * don't know where request->newName actually starts, thanks to the
    * fact that request->oldName is of variable length. We get around
    * this by using an HgfsFileName*, assigning it to the correct address
    * just after request->oldName ends, and using that to access the
    * new name.
    */

   /*
    * If we've been asked to reuse a handle, we don't need to look at, let
    * alone test the filename or its length. This applies to the source
    * and the target.
    */
   if (!HgfsUnpackFileNameV3(&requestV3->oldName,
                             extra,
                             &useHandle,
                             cpOldName,
                             cpOldNameLen,
                             srcFile,
                             oldCaseFlags)) {
      LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
      return FALSE;
   }
   if (useHandle) {
      *hints |= HGFS_RENAME_HINT_USE_SRCFILE_DESC;
      newName = &requestV3->newName;
   } else {
      newName = (const HgfsFileNameV3 *)(requestV3->oldName.name + 1 + *cpOldNameLen);
      extra -= *cpOldNameLen;
   }
   if (!HgfsUnpackFileNameV3(newName,
                             extra,
                             &useHandle,
                             cpNewName,
                             cpNewNameLen,
                             targetFile,
                             newCaseFlags)) {
      LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
      return FALSE;
   }
   if (useHandle) {
      *hints |= HGFS_RENAME_HINT_USE_TARGETFILE_DESC;
   }

   LOG(8, "%s: unpacking HGFS_OP_RENAME_V3 -> success\n", __FUNCTION__);
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackRenamePayloadV2 --
 *
 *    Unpack hgfs rename request V2 payload and initialize a corresponding
 *    HgfsHandle or file name to tell us which to delete. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to rename.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackRenamePayloadV2(const HgfsRequestRenameV2 *requestV2, // IN: request payload
                          size_t payloadSize,                   // IN: payload size
                          const char **cpOldName,               // OUT: rename src
                          size_t *cpOldNameLen,                 // OUT: rename src size
                          const char **cpNewName,               // OUT: rename dst
                          size_t *cpNewNameLen,                 // OUT: rename dst size
                          HgfsRenameHint *hints,                // OUT: rename hints
                          HgfsHandle *srcFile,                  // OUT: src file handle
                          HgfsHandle *targetFile)               // OUT: target file handle
{
   const HgfsFileName *newName;
   size_t extra;

   /* Enforced by the dispatch function. */
   if (payloadSize < sizeof *requestV2) {
      LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
      return FALSE;
   }
   extra = payloadSize - sizeof *requestV2;

   *hints = requestV2->hints;

   /*
    * If we've been asked to reuse a handle, we don't need to look at, let
    * alone test the filename or its length. This applies to the source
    * and the target.
    */

   if (*hints & HGFS_RENAME_HINT_USE_SRCFILE_DESC) {
      *srcFile = requestV2->srcFile;
      *cpOldName = NULL;
      *cpOldNameLen = 0;
   } else {
      if (!HgfsUnpackFileName(&requestV2->oldName,
                              extra,
                              cpOldName,
                              cpOldNameLen)) {
         LOG(4, "%s: Error decoding HGFS packet - not enough room for file name\n",
             __FUNCTION__);
         return FALSE;
      }
      extra -= *cpOldNameLen;
   }

   if (*hints & HGFS_RENAME_HINT_USE_TARGETFILE_DESC) {
      *targetFile = requestV2->targetFile;
      *cpNewName = NULL;
      *cpNewNameLen = 0;
   } else {
      newName = (const HgfsFileName *)((char *)(&requestV2->oldName + 1)
                                       + *cpOldNameLen);
      /*
       * The HgfsRequestRenameV2 structure overlay on the data has the old and
       * new data interlaced rather. The newName pointer in the data is
       * calculated as an offset from the oldName field. This confuses Coverity,
       * there is no overrun here.
       */
      /* coverity[overrun-local] */
      if (!HgfsUnpackFileName(newName,
                              extra,
                              cpNewName,
                              cpNewNameLen)) {
        LOG(4, "%s: Error decoding HGFS packet - not enough room for file name\n",
            __FUNCTION__);
        return FALSE;
      }
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackRenamePayloadV1 --
 *
 *    Unpack hgfs rename request V1 payload and initialize a corresponding
 *    old and new file names.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackRenamePayloadV1(const HgfsRequestRename *requestV1, // IN: request payload
                          size_t payloadSize,                 // IN: payload size
                          const char **cpOldName,             // OUT: rename src
                          size_t *cpOldNameLen,               // OUT: rename src size
                          const char **cpNewName,             // OUT: rename dst
                          size_t *cpNewNameLen)               // OUT: rename dst size
{
   const HgfsFileName *newName;
   uint32 extra;

   if (payloadSize < sizeof *requestV1) {
      return FALSE;
   }

   extra = payloadSize - sizeof *requestV1;

   if (!HgfsUnpackFileName(&requestV1->oldName,
                           extra,
                           cpOldName,
                           cpOldNameLen)) {
      LOG(4, "%s: Error decoding HGFS packet - not enough room for file name\n",
          __FUNCTION__);
      return FALSE;
   }

   extra -= requestV1->oldName.length;
   newName = (const HgfsFileName *)((char *)(&requestV1->oldName + 1)
                                    + requestV1->oldName.length);

   return HgfsUnpackFileName(newName, extra, cpNewName, cpNewNameLen);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackRenameRequest --
 *
 *    Unpack hgfs rename request and initialize a corresponding
 *    HgfsHandle or file name to tell us which to rename. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to rename.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackRenameRequest(const void *packet,       // IN: HGFS packet
                        size_t packetSize,        // IN: request packet size
                        HgfsOp op,                // IN: requested operation
                        const char **cpOldName,   // OUT: rename src
                        size_t *cpOldNameLen,     // OUT: rename src size
                        const char **cpNewName,   // OUT: rename dst
                        size_t *cpNewNameLen,     // OUT: rename dst size
                        HgfsRenameHint *hints,    // OUT: rename hints
                        HgfsHandle *srcFile,      // OUT: src file handle
                        HgfsHandle *targetFile,   // OUT: target file handle
                        uint32 *oldCaseFlags,     // OUT: source case-sensitivity flags
                        uint32 *newCaseFlags)     // OUT: dest. case-sensitivity flags
{
   ASSERT(packet);
   ASSERT(cpOldName);
   ASSERT(cpOldNameLen);
   ASSERT(cpNewName);
   ASSERT(cpNewNameLen);
   ASSERT(srcFile);
   ASSERT(targetFile);
   ASSERT(hints);
   ASSERT(oldCaseFlags);
   ASSERT(newCaseFlags);

   /* Default values for legacy requests. */
   *oldCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
   *newCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
   *hints = 0;

   switch (op) {
   case HGFS_OP_RENAME_V3: {
      const HgfsRequestRenameV3 *requestV3 = packet;

      if (!HgfsUnpackRenamePayloadV3(requestV3,
                                     packetSize,
                                     cpOldName,
                                     cpOldNameLen,
                                     cpNewName,
                                     cpNewNameLen,
                                     hints,
                                     srcFile,
                                     targetFile,
                                     oldCaseFlags,
                                     newCaseFlags)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   case HGFS_OP_RENAME_V2: {
      const HgfsRequestRenameV2 *requestV2 = packet;

      if (!HgfsUnpackRenamePayloadV2(requestV2,
                                     packetSize,
                                     cpOldName,
                                     cpOldNameLen,
                                     cpNewName,
                                     cpNewNameLen,
                                     hints,
                                     srcFile,
                                     targetFile)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }

   case HGFS_OP_RENAME: {
      const HgfsRequestRename *requestV1 = packet;

      if (!HgfsUnpackRenamePayloadV1(requestV1,
                                     packetSize,
                                     cpOldName,
                                     cpOldNameLen,
                                     cpNewName,
                                     cpNewNameLen)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }

   default:
      LOG(4, "%s: Invalid opcode %d\n", __FUNCTION__, op);
      NOT_REACHED();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackRenameReply --
 *
 *    Pack hgfs rename reply.
 *    Since the structure of the rename reply packet hasn't changed in
 *    version 2 of the protocol, HgfsReplyRenameV2 is identical to
 *    HgfsReplyRename. So use HgfsReplyRename type to access packetIn to
 *    keep the code simple.
 *
 * Results:
 *    TRUE if valid op and reply set, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackRenameReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                    const void *packetHeader,  // IN: packet header
                    HgfsOp op,                 // IN: requested operation
                    size_t *payloadSize,       // OUT: size of packet
                    HgfsSessionInfo *session)  // IN: Session info
{
   Bool result = TRUE;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_RENAME_V3: {
      HgfsReplyRenameV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      /* Reply consists of only a reserved field. */
      reply->reserved = 0;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_RENAME_V2:
   case HGFS_OP_RENAME: {
      HgfsReplyRename *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      LOG(4, "%s: invalid op code %d\n", __FUNCTION__, op);
      result = FALSE;
      NOT_REACHED();
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackGetattrPayloadV3 --
 *
 *    Unpack hgfs get attr request V3 payload and initialize a corresponding
 *    HgfsHandle or file name to tell us which file to get attributes. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to get attributes.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackGetattrPayloadV3(const HgfsRequestGetattrV3 *requestV3,// IN: request payload
                           size_t payloadSize,                   // IN: payload size
                           const char **cpName,                  // OUT: cpName
                           size_t *cpNameSize,                   // OUT: cpName size
                           HgfsAttrHint *hints,                  // OUT: getattr hints
                           HgfsHandle *file,                     // OUT: file handle
                           uint32 *caseFlags)                    // OUT: case-sensitivity flags
{
   Bool result = TRUE;
   Bool useHandle;

   if (payloadSize < sizeof *requestV3) {
      result = FALSE;
      goto exit;
   }

   *hints = requestV3->hints;

   if (!HgfsUnpackFileNameV3(&requestV3->fileName,
                             payloadSize - sizeof *requestV3,
                             &useHandle,
                             cpName,
                             cpNameSize,
                             file,
                             caseFlags)) {
      result = FALSE;
      goto exit;
   }

   if (useHandle) {
      *hints |= HGFS_ATTR_HINT_USE_FILE_DESC;
   }

exit:
   LOG(8, "%s: unpacking HGFS_OP_GETATTR_V3 -> %d\n", __FUNCTION__, result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackGetattrPayloadV2 --
 *
 *    Unpack hgfs Getattr request V2 payload and initialize a corresponding
 *    HgfsHandle or file name to tell us which to get attributes. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to get attributes.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackGetattrPayloadV2(const HgfsRequestGetattrV2 *requestV2,// IN: request payload
                           size_t payloadSize,                   // IN: payload size
                           const char **cpName,                  // OUT: cpName
                           size_t *cpNameSize,                   // OUT: cpName size
                           HgfsAttrHint *hints,                  // OUT: delete hints
                           HgfsHandle *file)                     // OUT: file handle
{
   Bool result = TRUE;

   if (payloadSize < sizeof *requestV2) {
      return FALSE;
   }


   *file = HGFS_INVALID_HANDLE;
   *hints = requestV2->hints;

   /*
    * If we've been asked to reuse a handle, we don't need to look at, let
    * alone test the filename or its length.
    */

   if (requestV2->hints & HGFS_ATTR_HINT_USE_FILE_DESC) {
      *file = requestV2->file;
      *cpName = NULL;
      *cpNameSize = 0;
   } else {
      result = HgfsUnpackFileName(&requestV2->fileName,
                                  payloadSize - sizeof *requestV2,
                                  cpName,
                                  cpNameSize);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackGetattrPayloadV1 --
 *
 *    Unpack hgfs getattr request V1 payload and initialize a corresponding
 *    file name to tell us which to get attributes.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackGetattrPayloadV1(const HgfsRequestGetattr *requestV1,  // IN: request payload
                           size_t payloadSize,                   // IN: payload size
                           const char **cpName,                  // OUT: cpName
                           size_t *cpNameSize)                   // OUT: cpName size
{
   return HgfsUnpackFileName(&requestV1->fileName,
                             payloadSize - sizeof *requestV1,
                             cpName,
                             cpNameSize);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackAttrV2 --
 *
 *    Packs attr version 2 reply structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackAttrV2(HgfsFileAttrInfo *attr,     // IN: attr stucture
               HgfsAttrV2 *attr2)          // OUT: attr in payload
{
   attr2->mask = attr->mask;
   attr2->type = attr->type;
   attr2->size = attr->size;
   attr2->creationTime = attr->creationTime;
   attr2->accessTime = attr->accessTime;
   attr2->writeTime = attr->writeTime;
   attr2->attrChangeTime = attr->attrChangeTime;
   attr2->specialPerms = attr->specialPerms;
   attr2->ownerPerms = attr->ownerPerms;
   attr2->groupPerms = attr->groupPerms;
   attr2->otherPerms = attr->otherPerms;
   attr2->flags = attr->flags;
   attr2->allocationSize = attr->allocationSize;
   attr2->userId = attr->userId;
   attr2->groupId = attr->groupId;
   attr2->hostFileId = attr->hostFileId;
   attr2->volumeId = attr->volumeId;
   attr2->effectivePerms = attr->effectivePerms;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackAttrV2 --
 *
 *    Unpacks attr version 2 reply structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsUnpackAttrV2(const HgfsAttrV2 *attr2,    // IN: attr in payload
                 HgfsFileAttrInfo *attr)     // OUT: attr stucture
{
   attr->mask = attr2->mask;
   attr->type = attr2->type;
   attr->size = attr2->size;
   attr->creationTime = attr2->creationTime;
   attr->accessTime = attr2->accessTime;
   attr->writeTime = attr2->writeTime;
   attr->attrChangeTime = attr2->attrChangeTime;
   attr->specialPerms = attr2->specialPerms;
   attr->ownerPerms = attr2->ownerPerms;
   attr->groupPerms = attr2->groupPerms;
   attr->otherPerms = attr2->otherPerms;
   attr->flags = attr2->flags;
   attr->allocationSize = attr2->allocationSize;
   attr->userId = attr2->userId;
   attr->groupId = attr2->groupId;
   attr->hostFileId = attr2->hostFileId;
   attr->volumeId = attr2->volumeId;
   attr->effectivePerms = attr2->effectivePerms;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsInitFileAttr --
 *
 *    Initializes HgfsFileAttrInfo structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsInitFileAttr(HgfsOp op,                // IN: request type
                 HgfsFileAttrInfo *attr)   // OUT: attr stucture
{
   /* Initialize all fields with 0. */
   memset(attr, 0, sizeof *attr);

   /* Explicitly initialize fields which need it. */
   attr->requestType = op;
   attr->mask = HGFS_ATTR_VALID_NONE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackGetattrReplyPayloadV3 --
 *
 *    Packs Getattr V3 reply payload.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackGetattrReplyPayloadV3(HgfsFileAttrInfo *attr,     // IN: attr stucture
                              const char *utf8TargetName, // IN: optional target name
                              uint32 utf8TargetNameLen,   // IN: file name length
                              HgfsReplyGetattrV3 *reply) // OUT: payload
{
   LOG(4, "%s: attr type: %u\n", __FUNCTION__, attr->type);

   HgfsPackAttrV2(attr, &reply->attr);
   reply->reserved = 0;

   if (utf8TargetName) {
      memcpy(reply->symlinkTarget.name, utf8TargetName, utf8TargetNameLen);
      CPNameLite_ConvertTo(reply->symlinkTarget.name, utf8TargetNameLen,
                           DIRSEPC);
   } else {
      ASSERT(utf8TargetNameLen == 0);
   }
   reply->symlinkTarget.length = utf8TargetNameLen;
   reply->symlinkTarget.name[utf8TargetNameLen] = '\0';
   reply->symlinkTarget.flags = 0;
   reply->symlinkTarget.fid = 0;
   reply->symlinkTarget.caseType = HGFS_FILE_NAME_DEFAULT_CASE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackGetattrReplyPayloadV2 --
 *
 *    Packs rename reply payload V2 requests.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackGetattrReplyPayloadV2(HgfsFileAttrInfo *attr,       // IN: attr stucture
                              const char *utf8TargetName,   // IN: optional target name
                              uint32 utf8TargetNameLen,     // IN: file name length
                              HgfsReplyGetattrV2 *reply)    // OUT: payload
{
   HgfsPackAttrV2(attr, &reply->attr);

   if (utf8TargetName) {
      memcpy(reply->symlinkTarget.name, utf8TargetName, utf8TargetNameLen);
      CPNameLite_ConvertTo(reply->symlinkTarget.name, utf8TargetNameLen,
                           DIRSEPC);
   } else {
      ASSERT(utf8TargetNameLen == 0);
   }
   reply->symlinkTarget.length = utf8TargetNameLen;
   reply->symlinkTarget.name[utf8TargetNameLen] = '\0';
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackGetattrReplyPayloadV1 --
 *
 *    Packs rename reply payload for V1 requests.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackGetattrReplyPayloadV1(HgfsFileAttrInfo *attr,       // IN: attr stucture
                              HgfsReplyGetattr *reply)      // OUT: reply info
{
   /* In GetattrV1, symlinks are treated as regular files. */
   if (attr->type == HGFS_FILE_TYPE_SYMLINK) {
      reply->attr.type = HGFS_FILE_TYPE_REGULAR;
   } else {
      reply->attr.type = attr->type;
   }

   reply->attr.size = attr->size;
   reply->attr.creationTime = attr->creationTime;
   reply->attr.accessTime = attr->accessTime;
   reply->attr.writeTime =  attr->writeTime;
   reply->attr.attrChangeTime = attr->attrChangeTime;
   reply->attr.permissions = attr->ownerPerms;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackGetattrRequest --
 *
 *    Unpack hgfs getattr request and initialize a corresponding
 *    HgfsFileAttrInfo structure that is used to pass around getattr request
 *    information.
 *
 *    Since the structure of the get attributes request packet hasn't changed
 *    in version 2 of the protocol, HgfsRequestGetattrV2 is identical to
 *    HgfsRequestGetattr. So use HgfsRequestGetattr type to access packetIn to
 *    keep the code simple.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackGetattrRequest(const void *packet,         // IN: HGFS packet
                         size_t packetSize,          // IN: request packet size
                         HgfsOp op,                  // IN request type
                         HgfsFileAttrInfo *attrInfo, // IN/OUT: getattr info
                         HgfsAttrHint *hints,        // OUT: getattr hints
                         const char **cpName,        // OUT: cpName
                         size_t *cpNameSize,         // OUT: cpName size
                         HgfsHandle *file,           // OUT: file handle
                         uint32 *caseType)           // OUT: case-sensitivity flags
{
   ASSERT(packet);
   ASSERT(attrInfo);
   ASSERT(cpName);
   ASSERT(cpNameSize);
   ASSERT(file);
   ASSERT(caseType);

   HgfsInitFileAttr(op, attrInfo);

   /* Default values for legacy requests. */
   *caseType = HGFS_FILE_NAME_DEFAULT_CASE;
   *hints = 0;
   *file = HGFS_INVALID_HANDLE;

   switch (op) {
   case HGFS_OP_GETATTR_V3: {
      const HgfsRequestGetattrV3 *requestV3 = packet;

      if (!HgfsUnpackGetattrPayloadV3(requestV3,
                                      packetSize,
                                      cpName,
                                      cpNameSize,
                                      hints,
                                      file,
                                      caseType)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      LOG(4, "%s: HGFS_OP_GETATTR_V3: %u\n", __FUNCTION__, *caseType);
      break;
   }

   case HGFS_OP_GETATTR_V2: {
      const HgfsRequestGetattrV2 *requestV2 = packet;

      if (!HgfsUnpackGetattrPayloadV2(requestV2,
                                      packetSize,
                                      cpName,
                                      cpNameSize,
                                      hints,
                                      file)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }

   case HGFS_OP_GETATTR: {
      const HgfsRequestGetattr *requestV1 = packet;

      if (!HgfsUnpackGetattrPayloadV1(requestV1, packetSize, cpName, cpNameSize)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }

   default:
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackGetattrReply --
 *
 *    Pack hgfs getattr reply to the HgfsReplyGetattr structure.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackGetattrReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                     const void *packetHeader,   // IN: packet header
                     HgfsFileAttrInfo *attr,     // IN: attr stucture
                     const char *utf8TargetName, // IN: optional target name
                     uint32 utf8TargetNameLen,   // IN: file name length
                     size_t *payloadSize,        // OUT: size of packet
                     HgfsSessionInfo *session)   // IN: Session info
{
   Bool result = TRUE;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   switch (attr->requestType) {
   case HGFS_OP_GETATTR_V3: {
      HgfsReplyGetattrV3 *reply;

      *payloadSize = sizeof *reply + utf8TargetNameLen;
      reply = HgfsAllocInitReply(packet, packetHeader, *payloadSize,
                                 session);
      HgfsPackGetattrReplyPayloadV3(attr, utf8TargetName, utf8TargetNameLen, reply);
      break;
   }

   case HGFS_OP_GETATTR_V2: {
      HgfsReplyGetattrV2 *reply;
      *payloadSize = sizeof *reply + utf8TargetNameLen;

      reply = HgfsAllocInitReply(packet, packetHeader, *payloadSize,
                                 session);
      HgfsPackGetattrReplyPayloadV2(attr,
                                    utf8TargetName,
                                    utf8TargetNameLen,
                                    reply);
      break;
   }

   case HGFS_OP_GETATTR: {
      HgfsReplyGetattr *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      HgfsPackGetattrReplyPayloadV1(attr, reply);
      *payloadSize = sizeof *reply;
      break;
   }

   default:
      LOG(4, "%s: Invalid GetAttr op.\n", __FUNCTION__);
      NOT_REACHED();

      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyHeaderV4 --
 *
 *    Packs SearchRead V4 reply header part for all entry records returned.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSearchReadReplyHeaderV4(HgfsSearchReadInfo *info,     // IN: reply info
                                HgfsReplySearchReadV4 *reply, // OUT: payload
                                size_t *headerSize)           // OUT: size written
{
   reply->numberEntriesReturned = info->numberRecordsWritten;
   reply->offsetToContinue = info->currentIndex;
   reply->flags = info->replyFlags;
   reply->reserved = 0;

   *headerSize = offsetof(HgfsReplySearchReadV4, entries);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyRecordV4 --
 *
 *    Packs SearchRead V4 reply record.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSearchReadReplyRecordV4(HgfsSearchReadEntry *entry,        // IN: entry info
                                HgfsDirEntryV4 *replylastEntry,    // IN/OUT: payload
                                HgfsDirEntryV4 *replyCurrentEntry) // OUT: reply buffer for dirent
{
   HgfsFileAttrInfo *attr = &entry->attr;

   memset(replyCurrentEntry, 0, sizeof *replyCurrentEntry);

   if (NULL != replylastEntry) {
      replylastEntry->nextEntryOffset = ((char*)replyCurrentEntry -
                                         (char*)replylastEntry);
   }

   /* Set the valid data mask for the entry. */
   replyCurrentEntry->mask = entry->mask;

   if (0 != (entry->mask & HGFS_SEARCH_READ_NAME)) {

      replyCurrentEntry->nextEntryOffset = 0;
      replyCurrentEntry->fileIndex = entry->fileIndex;

      if (0 != (replyCurrentEntry->mask & HGFS_SEARCH_READ_FILE_NODE_TYPE)) {
         replyCurrentEntry->fileType = attr->type;
      }
      if (0 != (entry->mask & HGFS_SEARCH_READ_FILE_SIZE)) {
         replyCurrentEntry->fileSize = attr->size;
      }
      if (0 != (entry->mask & HGFS_SEARCH_READ_ALLOCATION_SIZE)) {
         replyCurrentEntry->allocationSize = attr->allocationSize;
      }
      if (0 != (entry->mask & HGFS_SEARCH_READ_TIME_STAMP)) {
         replyCurrentEntry->creationTime = attr->creationTime;
         replyCurrentEntry->accessTime = attr->accessTime;
         replyCurrentEntry->writeTime = attr->writeTime;
         replyCurrentEntry->attrChangeTime = attr->attrChangeTime;
      }
      if (0 != (entry->mask & HGFS_SEARCH_READ_FILE_ATTRIBUTES)) {
         replyCurrentEntry->attrFlags = attr->flags;
      }
      if (0 != (entry->mask & HGFS_SEARCH_READ_FILE_ID)) {
         replyCurrentEntry->hostFileId = attr->hostFileId;
      }
      if (0 != (entry->mask & HGFS_SEARCH_READ_EA_SIZE)) {
         replyCurrentEntry->eaSize = attr->eaSize;
      }
      if (0 != (entry->mask & HGFS_SEARCH_READ_REPARSE_TAG)) {
         replyCurrentEntry->reparseTag = attr->reparseTag;
      }

      if (0 != (entry->mask & HGFS_SEARCH_READ_SHORT_NAME)) {
         ASSERT(attr->shortName.length > 0);
         memcpy(replyCurrentEntry->shortName.name,
                attr->shortName.name,
                attr->shortName.length);
         replyCurrentEntry->shortName.length = attr->shortName.length;
      }

      memcpy(replyCurrentEntry->fileName.name, entry->name, entry->nameLength);
      replyCurrentEntry->fileName.name[entry->nameLength] = 0;
      replyCurrentEntry->fileName.length = entry->nameLength;

      replyCurrentEntry->reserved = 0;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyHeaderV3 --
 *
 *    Packs SearchRead V3 reply record.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSearchReadReplyHeaderV3(HgfsSearchReadInfo *info,     // IN: reply info
                                HgfsReplySearchReadV3 *reply, // OUT: payload
                                size_t *headerSize)           // OUT: size written
{
   ASSERT(info->numberRecordsWritten <= 1 &&
          0 != (info->flags & HGFS_SEARCH_READ_SINGLE_ENTRY));
   reply->count = info->numberRecordsWritten;
   reply->reserved = 0;
   /*
    * Previous shipping tools expect to account for a whole reply,
    * which is not strictly correct, but we are stuck with it.
    */
   *headerSize = sizeof *reply;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyRecordV3 --
 *
 *    Packs SearchRead V3 reply record.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSearchReadReplyRecordV3(HgfsFileAttrInfo *attr,       // IN: attr stucture
                                const char *utf8Name,         // IN: file name
                                uint32 utf8NameLen,           // IN: file name length
                                HgfsDirEntry *replyDirent)    // OUT: reply buffer for dirent
{
   replyDirent->fileName.length = (uint32)utf8NameLen;
   replyDirent->fileName.flags = 0;
   replyDirent->fileName.fid = 0;
   replyDirent->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE;
   replyDirent->nextEntry = 0;

   if (utf8NameLen != 0) {
      memcpy(replyDirent->fileName.name, utf8Name, utf8NameLen);
      replyDirent->fileName.name[utf8NameLen] = 0;

      HgfsPackAttrV2(attr, &replyDirent->attr);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyHeaderV2 --
 *
 *    Packs SearchRead V2 reply header (common) part for all records.
 *    V2 replies only contain a single record, so there is nothing to do here.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSearchReadReplyHeaderV2(HgfsSearchReadInfo *info,     // IN: unused
                                HgfsReplySearchReadV2 *reply, // OUT: unused
                                size_t *headerSize)           // OUT: size written
{
   /* The header has already been accounted for. */
   *headerSize = sizeof *reply;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyRecordV2 --
 *
 *    Packs SearchRead V2 reply record.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSearchReadReplyRecordV2(HgfsFileAttrInfo *attr,       // IN: attr stucture
                                const char *utf8Name,         // IN: file name
                                uint32 utf8NameLen,           // IN: file name length
                                HgfsReplySearchReadV2 *reply) // OUT: reply buffer
{
   reply->fileName.length = (uint32)utf8NameLen;

   if (utf8NameLen != 0) {
      memcpy(reply->fileName.name, utf8Name, utf8NameLen);
      reply->fileName.name[utf8NameLen] = 0;
      HgfsPackAttrV2(attr, &reply->attr);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyHeaderV1 --
 *
 *    Packs SearchRead V1 reply header (common) part for all records.
 *    V1 replies only contain a single record, so there is nothing to do here.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSearchReadReplyHeaderV1(HgfsSearchReadInfo *info,   // IN: unused
                                HgfsReplySearchRead *reply, // OUT: unused
                                size_t *headerSize)         // OUT: size written
{
   /* The header has already been accounted for. */
   *headerSize = sizeof *reply;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyRecordV1 --
 *
 *    Packs SearchRead V1 reply record.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSearchReadReplyRecordV1(HgfsFileAttrInfo *attr,     // IN: attr stucture
                                const char *utf8Name,       // IN: file name
                                uint32 utf8NameLen,         // IN: file name length
                                HgfsReplySearchRead *reply) // OUT: reply buffer
{
   reply->fileName.length = (uint32)utf8NameLen;

   if (utf8NameLen != 0) {
      memcpy(reply->fileName.name, utf8Name, utf8NameLen);
      reply->fileName.name[utf8NameLen] = 0;

      /* In SearchReadV1, symlinks are treated as regular files. */
      if (attr->type == HGFS_FILE_TYPE_SYMLINK) {
         reply->attr.type = HGFS_FILE_TYPE_REGULAR;
      } else {
         reply->attr.type = attr->type;
      }
      reply->attr.size = attr->size;
      reply->attr.creationTime = attr->creationTime;
      reply->attr.accessTime = attr->accessTime;
      reply->attr.writeTime =  attr->writeTime;
      reply->attr.attrChangeTime = attr->attrChangeTime;
      reply->attr.permissions = attr->ownerPerms;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSearchReadRequest --
 *
 *    Unpack hgfs search read request and initialize a corresponding
 *    HgfsFileAttrInfo structure that is used to pass around attribute
 *    information.
 *
 *    Since the structure of the search read request packet hasn't changed in
 *    version 2 of the protocol, HgfsRequestSearchReadV2 is identical to
 *    HgfsRequestSearchRead. So use HgfsRequestSearchRead type to access
 *    packetIn to keep the code simple.
 *
 * Results:
 *    Always TRUE.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackSearchReadRequest(const void *packet,           // IN: request packet
                            size_t packetSize,            // IN: packet size
                            HgfsOp op,                    // IN: reqest type
                            HgfsSearchReadInfo *info,     // OUT: search info
                            size_t *baseReplySize,        // OUT: op base reply size
                            size_t *inlineReplyDataSize,  // OUT: size of inline reply data
                            HgfsHandle *hgfsSearchHandle) // OUT: hgfs search handle
{
   Bool result = TRUE;
   uint32 *startIndex;
   HgfsSearchReadMask *mask;
   HgfsSearchReadFlags *flags;
   size_t *replyPayloadSize;

   ASSERT(packet);
   ASSERT(info);
   ASSERT(baseReplySize);
   ASSERT(inlineReplyDataSize);
   ASSERT(hgfsSearchHandle);

   info->requestType = op;
   info->searchPattern = NULL;
   startIndex = &info->startIndex;
   replyPayloadSize = &info->payloadSize;
   mask = &info->requestedMask;
   flags = &info->flags;
   *mask = 0;
   *flags = 0;

   switch (op) {
   case HGFS_OP_SEARCH_READ_V4: {
      const HgfsRequestSearchReadV4 *request = packet;

      /* Enforced by the dispatch function. */
      ASSERT(packetSize >= sizeof *request);

      if (0 != (request->flags & HGFS_SEARCH_READ_FID_OPEN_V4)) {
         /*
          * XXX - When this is implemented, the handle will get us a node,
          * (of directory type) and then with the node, we can look up a
          * search handle, if the data is cached in the search array.
          */
         NOT_IMPLEMENTED();
      }

      *hgfsSearchHandle = request->fid;
      *startIndex = request->restartIndex;
      *mask = request->mask;
      *flags = request->flags;
      *baseReplySize = offsetof(HgfsReplySearchReadV4, entries);
      *replyPayloadSize = request->replyDirEntryMaxSize;
      *inlineReplyDataSize = 0;
      ASSERT(*replyPayloadSize > 0);

      LOG(4, "%s: HGFS_OP_SEARCH_READ_V4\n", __FUNCTION__);
      break;
   }

   case HGFS_OP_SEARCH_READ_V3: {
      const HgfsRequestSearchReadV3 *request = packet;

      /* Enforced by the dispatch function. */
      ASSERT(packetSize >= sizeof *request);

      *hgfsSearchHandle = request->search;
      *startIndex = request->offset;
      *flags = HGFS_SEARCH_READ_SINGLE_ENTRY;
      *mask = (HGFS_SEARCH_READ_FILE_NODE_TYPE |
               HGFS_SEARCH_READ_NAME |
               HGFS_SEARCH_READ_FILE_SIZE |
               HGFS_SEARCH_READ_TIME_STAMP |
               HGFS_SEARCH_READ_FILE_ATTRIBUTES |
               HGFS_SEARCH_READ_FILE_ID);
      *baseReplySize = offsetof(HgfsReplySearchReadV3, payload);
      *replyPayloadSize = HGFS_PACKET_MAX - *baseReplySize;
      *inlineReplyDataSize = *replyPayloadSize;

      LOG(4, "%s: HGFS_OP_SEARCH_READ_V3\n", __FUNCTION__);
      break;
   }

   case HGFS_OP_SEARCH_READ_V2:
   /*
    * Currently, the HgfsRequestSearchReadV2 is the same as
    * HgfsRequestSearchRead, so drop through.
    */
   case HGFS_OP_SEARCH_READ: {
      const HgfsRequestSearchRead *request = packet;

      /* Enforced by the dispatch function. */
      ASSERT(packetSize >= sizeof *request);

      *hgfsSearchHandle = request->search;
      *startIndex = request->offset;
      *flags = HGFS_SEARCH_READ_SINGLE_ENTRY;
      *mask = (HGFS_SEARCH_READ_FILE_NODE_TYPE |
               HGFS_SEARCH_READ_NAME |
               HGFS_SEARCH_READ_FILE_SIZE |
               HGFS_SEARCH_READ_TIME_STAMP |
               HGFS_SEARCH_READ_FILE_ATTRIBUTES);
      *baseReplySize = 0;
      *replyPayloadSize = HGFS_PACKET_MAX;
      *inlineReplyDataSize = *replyPayloadSize;
      break;
   }

   default:
      /* Should never occur. */
      NOT_REACHED();
      result = FALSE;
      Log("%s: ERROR Invalid OP %u\n", __FUNCTION__, op);
      break;
   }

   ASSERT(result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyRecord --
 *
 *    Pack hgfs search read reply record to the current entry record.
 *    If the last record is not NULL then update its offset to the current
 *    entry field.
 *
 * Results:
 *    TRUE on success and number of bytes written in replyRecordSize.
 *    FALSE on failure, nothing written.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackSearchReadReplyRecord(HgfsOp requestType,           // IN: search read request
                              HgfsSearchReadEntry *entry,   // IN: entry info
                              size_t bytesRemaining,        // IN: space in bytes for record
                              void *lastSearchReadRecord,   // IN/OUT: last packed entry
                              void *currentSearchReadRecord,// OUT: currrent entry to pack
                              size_t *replyRecordSize)      // OUT: size of packet
{
   Bool result = TRUE;
   size_t recordSize = 0;

   switch (requestType) {
   case HGFS_OP_SEARCH_READ_V4: {
      HgfsDirEntryV4 *replyCurrentEntry = currentSearchReadRecord;
      HgfsDirEntryV4 *replyLastEntry = lastSearchReadRecord;

      /* Skip the final empty record, it is not needed for V4.*/
      if (0 == entry->nameLength) {
         break;
      }

      recordSize = offsetof(HgfsDirEntryV4, fileName.name) +
                   entry->nameLength + 1;

      if (recordSize > bytesRemaining) {
         result = FALSE;
         break;
      }

      HgfsPackSearchReadReplyRecordV4(entry,
                                      replyLastEntry,
                                      replyCurrentEntry);
      break;
   }

   case HGFS_OP_SEARCH_READ_V3: {
      HgfsDirEntry *replyCurrentEntry = currentSearchReadRecord;

      /*
       * Previous shipping tools expect to account for a whole reply,
       * which is not strictly correct, it should be using
       * offsetof(HgfsDirEntry, fileName.name) + entry->nameLength + 1
       * but we are stuck with it.
       */
      recordSize = sizeof *replyCurrentEntry + entry->nameLength;

      if (recordSize > bytesRemaining) {
         result = FALSE;
         break;
      }

      HgfsPackSearchReadReplyRecordV3(&entry->attr,
                                      entry->name,
                                      entry->nameLength,
                                      replyCurrentEntry);
      break;
   }

   case HGFS_OP_SEARCH_READ_V2: {
      HgfsReplySearchReadV2 *replyV2 = currentSearchReadRecord;

      /* We have already accounted for the fixed part of the record. */
      recordSize = entry->nameLength;

      if (recordSize > bytesRemaining) {
         result = FALSE;
         break;
      }

      HgfsPackSearchReadReplyRecordV2(&entry->attr,
                                      entry->name,
                                      entry->nameLength,
                                      replyV2);
      break;
   }

   case HGFS_OP_SEARCH_READ: {
      HgfsReplySearchRead *replyV1 = currentSearchReadRecord;

      /* We have already accounted for the fixed part of the record. */
      recordSize = entry->nameLength;

      if (recordSize > bytesRemaining) {
         result = FALSE;
         break;
      }

      HgfsPackSearchReadReplyRecordV1(&entry->attr,
                                      entry->name,
                                      entry->nameLength,
                                      replyV1);
      break;
   }

   default: {
      Log("%s: Invalid SearchRead Op.", __FUNCTION__);
      NOT_REACHED();
      result = FALSE;
   }
   }

   if (result) {
      *replyRecordSize = recordSize;
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchReadReplyHeader --
 *
 *    Pack hgfs search read reply header (common) part to all the
 *    entries returned in the search read reply.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackSearchReadReplyHeader(HgfsSearchReadInfo *info,    // IN: request info
                              size_t *payloadSize)         // OUT: size of packet
{
   Bool result = TRUE;

   *payloadSize = 0;

   switch (info->requestType) {
   case HGFS_OP_SEARCH_READ_V4: {
      HgfsReplySearchReadV4 *reply = info->reply;

      HgfsPackSearchReadReplyHeaderV4(info, reply, payloadSize);
      break;
   }

   case HGFS_OP_SEARCH_READ_V3: {
      HgfsReplySearchReadV3 *reply = info->reply;

      HgfsPackSearchReadReplyHeaderV3(info, reply, payloadSize);
      break;
   }

   case HGFS_OP_SEARCH_READ_V2: {
      HgfsReplySearchReadV2 *reply = info->reply;

      HgfsPackSearchReadReplyHeaderV2(info, reply, payloadSize);
      break;
   }

   case HGFS_OP_SEARCH_READ: {
      HgfsReplySearchRead *reply = info->reply;

      HgfsPackSearchReadReplyHeaderV1(info, reply, payloadSize);
      break;
   }

   default: {
      LOG(4, "%s: Invalid SearchRead Op.", __FUNCTION__);
      NOT_REACHED();
      result = FALSE;
   }
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSetattrPayloadV3 --
 *
 *    Unpack hgfs set attr request V3 payload and initialize a corresponding
 *    HgfsHandle or file name to tell us which file to set attributes. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to set attributes.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSetattrPayloadV3(const HgfsRequestSetattrV3 *requestV3,// IN: request payload
                           size_t payloadSize,                   // IN: payload size
                           HgfsFileAttrInfo *attr,               // OUT: setattr info
                           const char **cpName,                  // OUT: cpName
                           size_t *cpNameSize,                   // OUT: cpName size
                           HgfsAttrHint *hints,                  // OUT: getattr hints
                           HgfsHandle *file,                     // OUT: file handle
                           uint32 *caseFlags)                    // OUT: case-sensitivity flags
{
   Bool result = TRUE;
   Bool useHandle;

   if (payloadSize < sizeof *requestV3) {
      result = FALSE;
      goto exit;
   }

   *hints = requestV3->hints;

   HgfsUnpackAttrV2(&requestV3->attr, attr);

   if (!HgfsUnpackFileNameV3(&requestV3->fileName,
                             payloadSize - sizeof *requestV3,
                             &useHandle,
                             cpName,
                             cpNameSize,
                             file,
                             caseFlags)) {
      result = FALSE;
      goto exit;
   }

   if (useHandle) {
      *hints |= HGFS_ATTR_HINT_USE_FILE_DESC;
   }

exit:
   LOG(8, "%s: unpacking HGFS_OP_SETATTR_V3 -> %d\n", __FUNCTION__, result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSetattrPayloadV2 --
 *
 *    Unpack hgfs Setattr request V2 payload and initialize a corresponding
 *    HgfsHandle or file name to tell us which to set attributes. Hints
 *    holds flags to specify a handle or name for the file or
 *    directory to set attributes.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSetattrPayloadV2(const HgfsRequestSetattrV2 *requestV2,// IN: request payload
                           size_t payloadSize,                   // IN: payload size
                           HgfsFileAttrInfo *attr,               // OUT: setattr info
                           const char **cpName,                  // OUT: cpName
                           size_t *cpNameSize,                   // OUT: cpName size
                           HgfsAttrHint *hints,                  // OUT: delete hints
                           HgfsHandle *file)                     // OUT: file handle
{
   Bool result = TRUE;

   /* Enforced by the dispatch function. */
   if (payloadSize < sizeof *requestV2) {
      return FALSE;
   }

   LOG(4, "%s: unpacking HGFS_OP_SETATTR_V2\n", __FUNCTION__);

   *file = HGFS_INVALID_HANDLE;
   *hints = requestV2->hints;

   HgfsUnpackAttrV2(&requestV2->attr, attr);

   if (requestV2->hints & HGFS_ATTR_HINT_USE_FILE_DESC) {
      *file = requestV2->file;
      *cpName = NULL;
      *cpNameSize = 0;
   } else {
      result = HgfsUnpackFileName(&requestV2->fileName,
                                  payloadSize - sizeof *requestV2,
                                  cpName,
                                  cpNameSize);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSetattrPayloadV1 --
 *
 *    Unpack hgfs setattr request V1 payload and initialize a corresponding
 *    file name to tell us which to set attributes.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSetattrPayloadV1(const HgfsRequestSetattr *requestV1,  // IN: request payload
                           size_t payloadSize,                   // IN: payload size
                           HgfsFileAttrInfo *attr,               // OUT: setattr info
                           const char **cpName,                  // OUT: cpName
                           size_t *cpNameSize,                   // OUT: cpName size
                           HgfsAttrHint *hints)                  // OUT: setattr hints
{
   LOG(4, "%s: unpacking HGFS_OP_SETATTR\n", __FUNCTION__);

   attr->mask = 0;
   attr->mask |= requestV1->update & HGFS_ATTR_SIZE ? HGFS_ATTR_VALID_SIZE : 0;
   attr->mask |= requestV1->update & HGFS_ATTR_CREATE_TIME ?
                                               HGFS_ATTR_VALID_CREATE_TIME : 0;
   attr->mask |= requestV1->update & HGFS_ATTR_ACCESS_TIME ?
                                               HGFS_ATTR_VALID_ACCESS_TIME : 0;
   attr->mask |= requestV1->update & HGFS_ATTR_WRITE_TIME ?
                                               HGFS_ATTR_VALID_WRITE_TIME : 0;
   attr->mask |= requestV1->update & HGFS_ATTR_CHANGE_TIME ?
                                               HGFS_ATTR_VALID_CHANGE_TIME : 0;
   attr->mask |= requestV1->update & HGFS_ATTR_PERMISSIONS ?
                                               HGFS_ATTR_VALID_OWNER_PERMS : 0;
   *hints     |= requestV1->update & HGFS_ATTR_ACCESS_TIME_SET ?
                                               HGFS_ATTR_HINT_SET_ACCESS_TIME : 0;
   *hints     |= requestV1->update & HGFS_ATTR_WRITE_TIME_SET ?
                                               HGFS_ATTR_HINT_SET_WRITE_TIME : 0;

   attr->type = requestV1->attr.type;
   attr->size = requestV1->attr.size;
   attr->creationTime = requestV1->attr.creationTime;
   attr->accessTime = requestV1->attr.accessTime;
   attr->writeTime = requestV1->attr.writeTime;
   attr->attrChangeTime = requestV1->attr.attrChangeTime;
   attr->ownerPerms = requestV1->attr.permissions;
   return HgfsUnpackFileName(&requestV1->fileName,
                             payloadSize - sizeof *requestV1,
                             cpName,
                             cpNameSize);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSetattrRequest --
 *
 *    Unpack hgfs setattr request and initialize a corresponding
 *    HgfsFileAttrInfo structure that is used to pass around setattr request
 *    information.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackSetattrRequest(const void *packet,       // IN: HGFS packet
                         size_t packetSize,        // IN: request packet size
                         HgfsOp op,                // IN: request type
                         HgfsFileAttrInfo *attr,   // OUT: setattr info
                         HgfsAttrHint *hints,      // OUT: setattr hints
                         const char **cpName,      // OUT: cpName
                         size_t *cpNameSize,       // OUT: cpName size
                         HgfsHandle *file,         // OUT: server file ID
                         uint32 *caseType)         // OUT: case-sensitivity flags
{
   ASSERT(packet);
   ASSERT(attr);
   ASSERT(cpName);
   ASSERT(cpNameSize);
   ASSERT(file);
   ASSERT(caseType);

   attr->requestType = op;

   /* Default values for legacy requests. */
   *caseType = HGFS_FILE_NAME_DEFAULT_CASE;
   *hints = 0;
   *file = HGFS_INVALID_HANDLE;

   switch (op) {
   case HGFS_OP_SETATTR_V3: {
      const HgfsRequestSetattrV3 *requestV3 = packet;
      if (!HgfsUnpackSetattrPayloadV3(requestV3,
                                       packetSize,
                                       attr,
                                       cpName,
                                       cpNameSize,
                                       hints,
                                       file,
                                       caseType)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }

   case HGFS_OP_SETATTR_V2: {
      const HgfsRequestSetattrV2 *requestV2 = packet;
      if (!HgfsUnpackSetattrPayloadV2(requestV2,
                                       packetSize,
                                       attr,
                                       cpName,
                                       cpNameSize,
                                       hints,
                                       file)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   case HGFS_OP_SETATTR: {
      const HgfsRequestSetattr *requestV1 = packet;
      if (!HgfsUnpackSetattrPayloadV1(requestV1,
                                       packetSize,
                                       attr,
                                       cpName,
                                       cpNameSize,
                                       hints)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   default:
      LOG(4, "%s: Incorrect opcode %d\n", __FUNCTION__, op);
      NOT_REACHED();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSetattrReply --
 *
 *    Pack hgfs setattr reply.
 *    Since the structure of the set attributes reply packet hasn't changed in
 *    version 2 of the protocol, HgfsReplySetattrV2 is identical to
 *    HgfsReplySetattr. So use HgfsReplySetattr type to access packetIn to
 *    keep the code simple.
 *
 * Results:
 *    TRUE if valid op and reply set, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackSetattrReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                     const void *packetHeader,  // IN: packet header
                     HgfsOp op,                 // IN: request type
                     size_t *payloadSize,       // OUT: size of packet
                     HgfsSessionInfo *session)  // IN: Session info
{
   Bool result = TRUE;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_SETATTR_V3: {
      HgfsReplySetattrV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      /* Reply consists of only a reserved field. */
      reply->reserved = 0;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_SETATTR_V2:
   case HGFS_OP_SETATTR: {
      HgfsReplySetattr *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      result = FALSE;
      LOG(4, "%s: invalid op code %d\n", __FUNCTION__, op);
      NOT_REACHED();
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackCreateDirPayloadV3 --
 *
 *    Unpack hgfs create directory request V3 payload and initialize a corresponding
 *    file name to tell us which directory to create.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackCreateDirPayloadV3(const HgfsRequestCreateDirV3 *requestV3, // IN: request payload
                             size_t payloadSize,                      // IN: payload size
                             HgfsCreateDirInfo *info)                 // IN/OUT: info struct
{
   /*
    * The request file name length is user-provided, so this test must be
    * carefully written to prevent wraparounds.
    */

   LOG(4, "%s: HGFS_OP_CREATE_DIR_V3\n", __FUNCTION__);
   ASSERT(payloadSize >= sizeof *requestV3);
   if (requestV3->fileName.length > payloadSize - sizeof *requestV3) {
      /* The input packet is smaller than the request. */
      return FALSE;
   }
   if (!(requestV3->mask & HGFS_CREATE_DIR_VALID_FILE_NAME)) {
      /* We do not support requests without a valid file name. */
      LOG(4, "%s: Incorrect mask %x\n", __FUNCTION__, (uint32)requestV3->mask);
      return FALSE;
   }

   /*
    * Copy all the fields into our carrier struct. Some will probably be
    * garbage, but it's simpler to copy everything now and check the
    * valid bits before reading later.
    */

   info->mask = requestV3->mask;
   info->cpName = requestV3->fileName.name;
   info->cpNameSize = requestV3->fileName.length;
   info->caseFlags = requestV3->fileName.caseType;
   info->specialPerms = requestV3->specialPerms;
   info->fileAttr = requestV3->fileAttr;
   info->ownerPerms = requestV3->ownerPerms;
   info->groupPerms = requestV3->groupPerms;
   info->otherPerms = requestV3->otherPerms;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackCreateDirPayloadV2 --
 *
 *    Unpack hgfs create directory request V2 payload and initialize a corresponding
 *    file name to tell us which directory to create.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackCreateDirPayloadV2(const HgfsRequestCreateDirV2 *requestV2, // IN: request payload
                             size_t payloadSize,                      // IN: payload size
                             HgfsCreateDirInfo *info)                 // IN/OUT: info struct
{
   /*
    * The request file name length is user-provided, so this test must be
    * carefully written to prevent wraparounds.
    */

   LOG(4, "%s: HGFS_OP_CREATE_DIR_V2\n", __FUNCTION__);
   ASSERT(payloadSize >= sizeof *requestV2);
   if (requestV2->fileName.length > payloadSize - sizeof *requestV2) {
      /* The input packet is smaller than the request. */
      return FALSE;
   }
   if (!(requestV2->mask & HGFS_CREATE_DIR_VALID_FILE_NAME)) {
      /* We do not support requests without a valid file name. */
      LOG(4, "%s: Incorrect mask %x\n", __FUNCTION__, (uint32)requestV2->mask);
      return FALSE;
   }

   /*
    * Copy all the fields into our carrier struct. Some will probably be
    * garbage, but it's simpler to copy everything now and check the
    * valid bits before reading later.
    */

   info->mask = requestV2->mask;
   info->cpName = requestV2->fileName.name;
   info->cpNameSize = requestV2->fileName.length;
   info->specialPerms = requestV2->specialPerms;
   info->ownerPerms = requestV2->ownerPerms;
   info->groupPerms = requestV2->groupPerms;
   info->otherPerms = requestV2->otherPerms;
   info->fileAttr = 0;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackCreateDirPayloadV1 --
 *
 *    Unpack hgfs create directory request V1 payload and initialize a corresponding
 *    file name to tell us which directory to create.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackCreateDirPayloadV1(const HgfsRequestCreateDir *requestV1, // IN: request payload
                             size_t payloadSize,                    // IN: payload size
                             HgfsCreateDirInfo *info)               // IN/OUT: info struct
{
   /*
    * The request file name length is user-provided, so this test must be
    * carefully written to prevent wraparounds.
    */

   LOG(4, "%s: HGFS_OP_CREATE_DIR_V1\n", __FUNCTION__);
   ASSERT(payloadSize >= sizeof *requestV1);
   if (requestV1->fileName.length > payloadSize - sizeof *requestV1) {
      /* The input packet is smaller than the request. */
      LOG(4, "%s: HGFS packet too small for the file name\n", __FUNCTION__);
      return FALSE;
   }

   /* For CreateDirV1 requests, we know exactly what fields we expect. */
   info->mask = HGFS_CREATE_DIR_VALID_OWNER_PERMS | HGFS_CREATE_DIR_VALID_FILE_NAME;
   info->cpName = requestV1->fileName.name;
   info->cpNameSize = requestV1->fileName.length;
   info->ownerPerms = requestV1->permissions;
   info->fileAttr = 0;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackCreateDirRequest --
 *
 *    Unpack hgfs CreateDir request and initialize a corresponding
 *    HgfsCreateDirInfo structure that is used to pass around CreateDir request
 *    information.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackCreateDirRequest(const void *packet,      // IN: incoming packet
                           size_t packetSize,       // IN: size of packet
                           HgfsOp op,               // IN: request type
                           HgfsCreateDirInfo *info) // IN/OUT: info struct
{
   ASSERT(packet);
   ASSERT(info);

   info->requestType = op;
   /* Default value for legacy requests. */
   info->caseFlags = HGFS_FILE_NAME_DEFAULT_CASE;

   switch (op) {
   case HGFS_OP_CREATE_DIR_V3: {
      const HgfsRequestCreateDirV3 *requestV3 = packet;
      if (!HgfsUnpackCreateDirPayloadV3(requestV3,
                                        packetSize,
                                        info)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }

   case HGFS_OP_CREATE_DIR_V2: {
      const HgfsRequestCreateDirV2 *requestV2 = packet;
      if (!HgfsUnpackCreateDirPayloadV2(requestV2,
                                        packetSize,
                                        info)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   case HGFS_OP_CREATE_DIR: {
      const HgfsRequestCreateDir *requestV1 = packet;
      if (!HgfsUnpackCreateDirPayloadV1(requestV1,
                                        packetSize,
                                        info)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   default:
      LOG(4, "%s: Incorrect opcode %d\n", __FUNCTION__, op);
      NOT_REACHED();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackCreateDirReply --
 *
 *    Pack hgfs CreateDir reply.
 *
 * Results:
 *    TRUE if valid op and reply set, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackCreateDirReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                       const void *packetHeader,  // IN: packet header
                       HgfsOp op,                 // IN: request type
                       size_t *payloadSize,        // OUT: size of packet
                       HgfsSessionInfo *session)  // IN: Session info
{
   Bool result = TRUE;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_CREATE_DIR_V3: {
      HgfsReplyCreateDirV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      /* Reply consists of only a reserved field. */
      reply->reserved = 0;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_CREATE_DIR_V2: {
      HgfsReplyCreateDirV2 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_CREATE_DIR: {
      HgfsReplyCreateDir *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      result = FALSE;
      LOG(4, "%s: invalid op code %d\n", __FUNCTION__, op);
      NOT_REACHED();
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackWriteWin32StreamPayloadV3 --
 *
 *    Unpack hgfs write stream request V3 payload and initialize a corresponding
 *    file name to tell us which directory to create.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackWriteWin32StreamPayloadV3(const HgfsRequestWriteWin32StreamV3 *requestV3, // IN:
                                    size_t payloadSize,                             // IN:
                                    HgfsHandle *file,                               // OUT:
                                    const char **data,                              // OUT:
                                    size_t *dataSize,                               // OUT:
                                    Bool *doSecurity)                               // OUT:
{
   LOG(4, "%s: HGFS_OP_WRITE_WIN32_STREAM_V3\n", __FUNCTION__);
   if (payloadSize < sizeof *requestV3) {
      LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
      return FALSE;
   }

   if (payloadSize >= requestV3->requiredSize + sizeof *requestV3) {
      *file = requestV3->file;
      *data = requestV3->payload;
      *dataSize = requestV3->requiredSize;
      *doSecurity = (requestV3->flags & HGFS_WIN32_STREAM_IGNORE_SECURITY) == 0;
      return TRUE;
   }

   LOG(4, "%s: HGFS packet too small - user data do not fit\n", __FUNCTION__);
   return FALSE;
}

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackWriteWin32StreamRequest --
 *
 *    Unpack hgfs SendFileUsingReader request. Returns file to write to, data
 *    and whether to restore the security stream.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackWriteWin32StreamRequest(const void *packet, // IN: incoming packet
                                  size_t packetSize,  // IN: size of packet
                                  HgfsOp op,          // IN: request type
                                  HgfsHandle *file,   // OUT: file to write to
                                  const char **data,  // OUT: data to write
                                  size_t *dataSize,   // OUT: size of data
                                  Bool *doSecurity)   // OUT: restore sec.str.
{
   ASSERT(packet);
   ASSERT(file);
   ASSERT(data);
   ASSERT(dataSize);
   ASSERT(doSecurity);

   if (op != HGFS_OP_WRITE_WIN32_STREAM_V3) {
      /* The only supported version for the moment is V3. */
      LOG(4, "%s: Incorrect opcode %d\n", __FUNCTION__, op);
      NOT_REACHED();
      return FALSE;
   }

   return HgfsUnpackWriteWin32StreamPayloadV3(packet,
                                              packetSize,
                                              file,
                                              data,
                                              dataSize,
                                              doSecurity);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackWriteWin32StreamReply --
 *
 *    Pack hgfs SendFileUsingReader reply.
 *    Returns the actual amount of data written in the reply.
 *
 * Results:
 *    TRUE if valid op and reply set, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackWriteWin32StreamReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                              const void *packetHeader,  // IN: packet header
                              HgfsOp op,                 // IN: request type
                              uint32 actualSize,         // IN: amount written
                              size_t *payloadSize,       // OUT: size of packet
                              HgfsSessionInfo *session)  // IN: Session info
{
   HgfsReplyWriteWin32StreamV3 *reply;
   Bool result = TRUE;

   *payloadSize = 0;

   if (HGFS_OP_WRITE_WIN32_STREAM_V3 == op) {
      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      reply->reserved = 0;
      reply->actualSize = actualSize;
      *payloadSize = sizeof *reply;
   } else {
      LOG(4, "%s: Incorrect opcode %d\n", __FUNCTION__, op);
      NOT_REACHED();
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackReadPayload --
 *
 *    Unpack hgfs read payload to get the file handle and file offset to read from and
 *    the length of data to read.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackReadPayload(const HgfsRequestRead *request,    // IN: payload
                      size_t payloadSize,                // IN: payload size
                      HgfsHandle* file,                  // OUT: HGFS handle to close
                      uint64 *offset,                    // OUT: offset to read from
                      uint32 *length)                    // OUT: length of data to read
{
   LOG(4, "%s: HGFS_OP_READ\n", __FUNCTION__);
   if (payloadSize >= sizeof *request) {
      *file = request->file;
      *offset = request->offset;
      *length = request->requiredSize;
      return TRUE;
   }
   LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackReadPayloadV3 --
 *
 *    Unpack hgfs read payload V3 to get parameters needed to perform read.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackReadPayloadV3(const HgfsRequestReadV3 *requestV3,  // IN: payload
                        size_t payloadSize,                  // IN: payload size
                        HgfsHandle* file,                    // OUT: HGFS handle to close
                        uint64 *offset,                      // OUT: offset to read from
                        uint32 *length)                      // OUT: length of data to read
{
   LOG(4, "%s: HGFS_OP_READ_V3\n", __FUNCTION__);
   if (payloadSize >= sizeof *requestV3) {
      *file = requestV3->file;
      *offset = requestV3->offset;
      *length = requestV3->requiredSize;
      return TRUE;
   }
   LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackReadRequest --
 *
 *    Unpack hgfs read request.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackReadRequest(const void *packet,     // IN: HGFS request
                      size_t packetSize,      // IN: request packet size
                      HgfsOp  op,             // IN: request type
                      HgfsHandle *file,       // OUT: Handle to close
                      uint64 *offset,         // OUT: offset to read from
                      uint32 *length)         // OUT: length of data to read
{
   Bool result;

   ASSERT(packet);

   switch (op) {
   case HGFS_OP_READ_FAST_V4:
   case HGFS_OP_READ_V3: {
         const HgfsRequestReadV3 *requestV3 = packet;

         result = HgfsUnpackReadPayloadV3(requestV3, packetSize, file, offset, length);
         break;
      }
   case HGFS_OP_READ: {
         const HgfsRequestRead *requestV1 = packet;

         result = HgfsUnpackReadPayload(requestV1, packetSize, file, offset, length);
         break;
      }
   default:
      NOT_REACHED();
      result = FALSE;
   }

   if (!result) {
      LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackWritePayload --
 *
 *    Unpack hgfs write payload to get the file handle, file offset, of data to write,
 *    write flags and pointer to the data to write.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackWritePayload(const HgfsRequestWrite *request,    // IN: request payload
                       size_t payloadSize,                 // IN: request payload size
                       HgfsHandle* file,                   // OUT: HGFS handle to write to
                       uint64 *offset,                     // OUT: offset to read from
                       uint32 *length,                     // OUT: length of data to write
                       HgfsWriteFlags *flags,              // OUT: write flags
                       const void **data)                  // OUT: data to be written
{
   LOG(4, "%s: HGFS_OP_WRITE\n", __FUNCTION__);
   if (payloadSize >= sizeof *request) {
      if (sizeof *request + request->requiredSize - 1 <= payloadSize) {
         *file = request->file;
         *flags = request->flags;
         *offset = request->offset;
         *data = request->payload;
         *length = request->requiredSize;
         return TRUE;
      }
   }
   LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackWritePayloadV3 --
 *
 *    Unpack hgfs write payload V3.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackWritePayloadV3(const HgfsRequestWriteV3 *requestV3, // IN: payload
                         size_t payloadSize,                  // IN: request payload size
                         HgfsHandle* file,                    // OUT: HGFS handle write to
                         uint64 *offset,                      // OUT: offset to read from
                         uint32 *length,                      // OUT: length of data to write
                         HgfsWriteFlags *flags,               // OUT: write flags
                         const void **data)                   // OUT: data to be written
{
   LOG(4, "%s: HGFS_OP_WRITE_V3\n", __FUNCTION__);
   if (payloadSize >= sizeof *requestV3) {
      if (sizeof *requestV3 + requestV3->requiredSize - 1 <= payloadSize) {
         *file = requestV3->file;
         *flags = requestV3->flags;
         *offset = requestV3->offset;
         *data = requestV3->payload;
         *length = requestV3->requiredSize;
         return TRUE;
      }
   }
   LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackWriteFastPayloadV4 --
 *
 *    Unpack hgfs write fast payload V4.
 *    The only difference from V3 payload is that data to write are
 *    provided in the payload but located in a separate buffer.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackWriteFastPayloadV4(const HgfsRequestWriteV3 *requestV3, // IN: payload
                             size_t payloadSize,                  // IN: request payload size
                             HgfsHandle* file,                    // OUT: HGFS handle write to
                             uint64 *offset,                      // OUT: offset to write to
                             uint32 *length,                      // OUT: size of data to write
                             HgfsWriteFlags *flags)               // OUT: write flags
{
   LOG(4, "%s: HGFS_OP_WRITE_V3\n", __FUNCTION__);
   if (payloadSize >= sizeof *requestV3) {
      *file = requestV3->file;
      *flags = requestV3->flags;
      *offset = requestV3->offset;
      *length = requestV3->requiredSize;
      return TRUE;
   }
   LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackWriteRequest --
 *
 *    Unpack hgfs write request to get parameters and data to write.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackWriteRequest(const void *writeRequest,// IN: write request params
                       size_t writeRequestSize, // IN: write request params size
                       HgfsOp writeOp,          // IN: request version
                       HgfsHandle *file,        // OUT: Handle to write to
                       uint64 *offset,          // OUT: offset to write to
                       uint32 *length,          // OUT: length of data to write
                       HgfsWriteFlags *flags,   // OUT: write flags
                       const void **data)       // OUT: data to be written
{
   Bool result;

   switch (writeOp) {
   case HGFS_OP_WRITE_FAST_V4: {
      const HgfsRequestWriteV3 *requestV3 = writeRequest;

      *data = NULL; /* Write data is retrieved from shared memory. */
      result = HgfsUnpackWriteFastPayloadV4(requestV3, writeRequestSize, file,
                                            offset, length, flags);
      break;
   }
   case HGFS_OP_WRITE_V3: {
      const HgfsRequestWriteV3 *requestV3 = writeRequest;

      result = HgfsUnpackWritePayloadV3(requestV3, writeRequestSize, file, offset,
                                        length, flags, data);
      break;
   }
   case HGFS_OP_WRITE: {
      const HgfsRequestWrite *requestV1 = writeRequest;

      result = HgfsUnpackWritePayload(requestV1, writeRequestSize, file, offset,
                                      length, flags, data);
      break;
   }
   default:
      LOG(4, "%s: Incorrect opcode %d\n", __FUNCTION__, writeOp);
      NOT_REACHED();
      result = FALSE;
   }

   if (!result) {
      LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackWriteReply --
 *
 *    Pack hgfs write reply to the HgfsReplyWrite structure.
 *
 * Results:
 *    Always TRUE, FALSE if bad opcode.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackWriteReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                   const void *packetHeader,     // IN: packet header
                   HgfsOp op,                    // IN: request type
                   uint32 actualSize,            // IN: number of bytes that were written
                   size_t *payloadSize,          // OUT: size of packet
                   HgfsSessionInfo *session)     // IN: Session info
{
   Bool result = TRUE;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_WRITE_FAST_V4:
   case HGFS_OP_WRITE_V3: {
      HgfsReplyWriteV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      reply->reserved = 0;
      reply->actualSize = actualSize;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_WRITE: {
      HgfsReplyWrite *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      reply->actualSize = actualSize;
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      NOT_REACHED();
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackQueryVolumePayload --
 *
 *    Unpack hgfs query volume payload to get the file name which must be used to query
 *    volume information.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackQueryVolumePayload(const HgfsRequestQueryVolume *request, // IN: request payload
                             size_t payloadSize,                    // IN: request payload size
                             const char **fileName,                 // OUT: volume name
                             size_t *nameLength)                    // OUT: volume name length
{
   LOG(4, "%s: HGFS_OP_QUERY_VOLUME_INFO\n", __FUNCTION__);
   if (payloadSize >= sizeof *request) {
      return HgfsUnpackFileName(&request->fileName,
                                payloadSize - sizeof *request + 1,
                                fileName,
                                nameLength);
   }
   LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackQueryVolumePayloadV3 --
 *
 *    Unpack hgfs query volume payload V3.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackQueryVolumePayloadV3(const HgfsRequestQueryVolumeV3 *requestV3, // IN: payload
                               size_t payloadSize,                        // IN: payload size
                               Bool *useHandle,                           // OUT: use handle
                               HgfsHandle* file,                          // OUT: HGFS handle
                               const char **fileName,                     // OUT: volume name
                               size_t *nameLength,                        // OUT: name length
                               uint32 * caseFlags)                        // OUT: case flags
{
   LOG(4, "%s: HGFS_OP_QUERY_VOLUME_INFO_V3\n", __FUNCTION__);
   if (payloadSize >= sizeof *requestV3) {
      return HgfsUnpackFileNameV3(&requestV3->fileName,
                                  payloadSize - sizeof *requestV3 + 1,
                                  useHandle,
                                  fileName,
                                  nameLength,
                                  file,
                                  caseFlags);
   }
   LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackQueryVolumeRequest --
 *
 *    Unpack hgfs query volume information request to get parameters related to
 *    query volume operation.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackQueryVolumeRequest(const void *packet,     // IN: HGFS packet
                             size_t packetSize,      // IN: request packet size
                             HgfsOp op,              // IN: request type
                             Bool *useHandle,        // OUT: use handle
                             const char **fileName,  // OUT: file name
                             size_t *fileNameLength, // OUT: file name length
                             uint32 *caseFlags,      // OUT: case sensitivity
                             HgfsHandle *file)       // OUT: Handle to the volume
{
   ASSERT(packet);

   switch (op) {
   case HGFS_OP_QUERY_VOLUME_INFO_V3: {
      const HgfsRequestQueryVolumeV3 *requestV3 = packet;

      if (!HgfsUnpackQueryVolumePayloadV3(requestV3, packetSize, useHandle, file,
                                          fileName, fileNameLength, caseFlags)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      break;
   }
   case HGFS_OP_QUERY_VOLUME_INFO: {
      const HgfsRequestQueryVolume *requestV1 = packet;

      if (!HgfsUnpackQueryVolumePayload(requestV1, packetSize, fileName,
                                        fileNameLength)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      *file = HGFS_INVALID_HANDLE;
      *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
      *useHandle = FALSE;
      break;
   }
   default:
      LOG(4, "%s: Incorrect opcode %d\n", __FUNCTION__, op);
      NOT_REACHED();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackQueryVolumeReply --
 *
 *    Pack hgfs query volume reply.
 *
 * Results:
 *    TRUE if valid op and reply set, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackQueryVolumeReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                         const void *packetHeader,  // IN: packet header
                         HgfsOp op,                 // IN: request type
                         uint64 freeBytes,          // IN: volume free space
                         uint64 totalBytes,         // IN: volume capacity
                         size_t *payloadSize,       // OUT: size of packet
                         HgfsSessionInfo *session)  // IN: Session info
{
   Bool result = TRUE;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_QUERY_VOLUME_INFO_V3: {
      HgfsReplyQueryVolumeV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      reply->reserved = 0;
      reply->freeBytes = freeBytes;
      reply->totalBytes = totalBytes;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_QUERY_VOLUME_INFO: {
      HgfsReplyQueryVolume *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      reply->freeBytes = freeBytes;
      reply->totalBytes = totalBytes;
         *payloadSize = sizeof *reply;
      break;
   }
   default:
      result = FALSE;
      LOG(4, "%s: invalid op code %d\n", __FUNCTION__, op);
      NOT_REACHED();
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSymlinkCreatePayload --
 *
 *    Unpack hgfs symbolic link payload to get symbolic link file name
 *    and symbolic link target.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSymlinkCreatePayload(const HgfsRequestSymlinkCreate *request, // IN: request payload
                               size_t payloadSize,                      // IN: payload size
                               const char **srcFileName,                // OUT: link file name
                               size_t *srcNameLength,                   // OUT: file name length
                               const char **tgFileName,                 // OUT: target file name
                               size_t *tgNameLength)                    // OUT: target name length
{
   uint32 prefixSize;

   LOG(4, "%s: HGFS_OP_CREATE_SYMLINK_V3\n", __FUNCTION__);
   prefixSize = offsetof(HgfsRequestSymlinkCreate, symlinkName.name);
   if (payloadSize >= prefixSize) {
      if (HgfsUnpackFileName(&request->symlinkName,
                             payloadSize - prefixSize,
                             srcFileName,
                             srcNameLength)) {
         const HgfsFileName *targetName =
            (const HgfsFileName *)(*srcFileName + 1 + *srcNameLength);
         prefixSize = ((char *)targetName - (char *)request) + offsetof(HgfsFileName, name);

         return HgfsUnpackFileName(targetName,
                                   payloadSize - prefixSize,
                                   tgFileName,
                                   tgNameLength);
      }
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSymlinkCreatePayloadV3 --
 *
 *    Unpack hgfs create symbolic link payload V3.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSymlinkCreatePayloadV3(const HgfsRequestSymlinkCreateV3 *requestV3, // IN:
                                 size_t payloadSize,                          // IN:
                                 Bool *srcUseHandle,                          // OUT:
                                 HgfsHandle* srcFile,                         // OUT:
                                 const char **srcFileName,                    // OUT:
                                 size_t *srcNameLength,                       // OUT:
                                 uint32 *srcCaseFlags,                        // OUT:
                                 Bool *tgUseHandle,                           // OUT:
                                 HgfsHandle* tgFile,                          // OUT:
                                 const char **tgFileName,                     // OUT:
                                 size_t *tgNameLength,                        // OUT:
                                 uint32 * tgCaseFlags)                        // OUT:
{
   uint32 prefixSize;

   LOG(4, "%s: HGFS_OP_CREATE_SYMLINK_V3\n", __FUNCTION__);
   prefixSize = offsetof(HgfsRequestSymlinkCreateV3, symlinkName.name);
   if (payloadSize >= prefixSize) {
      if (HgfsUnpackFileNameV3(&requestV3->symlinkName,
                               payloadSize - prefixSize,
                               srcUseHandle,
                               srcFileName,
                               srcNameLength,
                               srcFile,
                               srcCaseFlags)) {
         const HgfsFileNameV3 *targetName;

         if (*srcUseHandle) {
            targetName = &requestV3->targetName;
         } else {
            targetName = (const HgfsFileNameV3 *)(*srcFileName + 1 +
                                                  *srcNameLength);
         }
         prefixSize = ((char *)targetName - (char *)requestV3) +
                        offsetof(HgfsFileNameV3, name);

         return HgfsUnpackFileNameV3(targetName,
                                     payloadSize - prefixSize,
                                     tgUseHandle,
                                     tgFileName,
                                     tgNameLength,
                                     tgFile,
                                     tgCaseFlags);
      }
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSymlinkCreateRequest --
 *
 *    Unpack hgfs symbolic link creation request to get parameters related to
 *    creating the symbolic link.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackSymlinkCreateRequest(const void *packet,        // IN: HGFS packet
                               size_t packetSize,         // IN: request packet size
                               HgfsOp op,                 // IN: request type
                               Bool *srcUseHandle,        // OUT: use source handle
                               const char **srcFileName,  // OUT: source file name
                               size_t *srcFileNameLength, // OUT: source file name length
                               uint32 *srcCaseFlags,      // OUT: source case sensitivity
                               HgfsHandle *srcFile,       // OUT: source file handle
                               Bool *tgUseHandle,         // OUT: use target handle
                               const char **tgFileName,   // OUT: target file name
                               size_t *tgFileNameLength,  // OUT: target file name length
                               uint32 *tgCaseFlags,       // OUT: target case sensitivity
                               HgfsHandle *tgFile)        // OUT: target file handle
{
   ASSERT(packet);

   switch (op) {
   case HGFS_OP_CREATE_SYMLINK_V3: {
      const HgfsRequestSymlinkCreateV3 *requestV3 = packet;

      if (!HgfsUnpackSymlinkCreatePayloadV3(requestV3, packetSize,
                                            srcUseHandle, srcFile,
                                            srcFileName, srcFileNameLength, srcCaseFlags,
                                            tgUseHandle, tgFile,
                                            tgFileName, tgFileNameLength, tgCaseFlags)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
         break;
   }
   case HGFS_OP_CREATE_SYMLINK: {
      const HgfsRequestSymlinkCreate *requestV1 = packet;

      if (!HgfsUnpackSymlinkCreatePayload(requestV1, packetSize, srcFileName,
                                          srcFileNameLength, tgFileName, tgFileNameLength)) {
         LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
         return FALSE;
      }
      *srcFile = HGFS_INVALID_HANDLE;
      *srcCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
      *srcUseHandle = FALSE;
      *tgFile = HGFS_INVALID_HANDLE;
      *tgCaseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
      *tgUseHandle = FALSE;
      break;
   }
   default:
      LOG(4, "%s: Incorrect opcode %d\n", __FUNCTION__, op);
      NOT_REACHED();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSymlinkCreateReply --
 *
 *    Pack hgfs symbolic link creation reply.
 *
 * Results:
 *    TRUE if valid op and reply set, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackSymlinkCreateReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                           const void *packetHeader,  // IN: packet header
                           HgfsOp op,                 // IN: request type
                           size_t *payloadSize,       // OUT: size of packet
                           HgfsSessionInfo *session)  // IN: Session info
{
   Bool result = TRUE;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_CREATE_SYMLINK_V3: {
      HgfsReplySymlinkCreateV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      /* Reply only consists of a reserved field. */
      reply->reserved = 0;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_CREATE_SYMLINK: {
      HgfsReplySymlinkCreate *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      result = FALSE;
      LOG(4, "%s: invalid op code %d\n", __FUNCTION__, op);
      NOT_REACHED();
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSearchOpenPayload --
 *
 *    Unpack hgfs search open payload to get name of directory to open.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSearchOpenPayload(const HgfsRequestSearchOpen *request, // IN: payload
                            size_t payloadSize,                   // IN: payload size
                            const char **dirName,                 // OUT: directory name
                            size_t *dirNameLength)                // OUT: name length
{
   LOG(4, "%s: HGFS_OP_SEARCH_OPEN\n", __FUNCTION__);
   if (payloadSize >= sizeof *request) {
      if (sizeof *request + request->dirName.length - 1 <= payloadSize) {
         *dirName = request->dirName.name;
         *dirNameLength = request->dirName.length;
         return TRUE;
      }
   }
   LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSearchOpenPayloadV3 --
 *
 *    Unpack hgfs search open payload V3 to get name of directory to open and
 *    case flags.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSearchOpenPayloadV3(const HgfsRequestSearchOpenV3 *requestV3, // IN: payload
                              size_t payloadSize,                       // IN: payload size
                              const char **dirName,                     // OUT: directory name
                              size_t *dirNameLength,                    // OUT: name length
                              uint32 *caseFlags)                        // OUT: case flags
{
   Bool result = FALSE;
   LOG(4, "%s: HGFS_OP_SEARCH_OPEN_V3\n", __FUNCTION__);
   if (payloadSize >= sizeof *requestV3) {
      uint32 prefixSize = offsetof(HgfsRequestSearchOpenV3, dirName.name);
      Bool useDirHandle;
      HgfsHandle dirHandle;

      result = HgfsUnpackFileNameV3(&requestV3->dirName,
                                    payloadSize - prefixSize,
                                    &useDirHandle,
                                    dirName,
                                    dirNameLength,
                                    &dirHandle,
                                    caseFlags);
      if (useDirHandle) {
         LOG(4, "%s: client is trying to a handle %u\n", __FUNCTION__, dirHandle);
         result = FALSE;
      }
   }
   LOG(4, "%s: returns %d\n", __FUNCTION__, result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSearchOpenRequest --
 *
 *    Unpack hgfs search open request to get directory name and case flags.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackSearchOpenRequest(const void *packet,      // IN: HGFS packet
                            size_t packetSize,       // IN: request packet size
                            HgfsOp op,               // IN: request type
                            const char **dirName,    // OUT: directory name
                            size_t *dirNameLength,   // OUT: name length
                            uint32 *caseFlags)       // OUT: case flags
{
   ASSERT(packet);

   switch (op) {
   case HGFS_OP_SEARCH_OPEN_V3: {
         const HgfsRequestSearchOpenV3 *requestV3 = packet;

         if (!HgfsUnpackSearchOpenPayloadV3(requestV3, packetSize, dirName,
                                            dirNameLength, caseFlags)) {
            LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
            return FALSE;
         }
         break;
      }
   case HGFS_OP_SEARCH_OPEN: {
         const HgfsRequestSearchOpen *requestV1 = packet;

         if (!HgfsUnpackSearchOpenPayload(requestV1, packetSize, dirName,
                                          dirNameLength)) {
            LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
            return FALSE;
         }
         *caseFlags = HGFS_FILE_NAME_DEFAULT_CASE;
         break;
      }
   default:
      LOG(4, "%s: Incorrect opcode %d\n", __FUNCTION__, op);
      NOT_REACHED();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSearchOpenReply --
 *
 *    Pack hgfs search open reply.
 *
 * Results:
 *    TRUE unless it is invoked for a wrong op (which indicates a bug in the code).
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackSearchOpenReply(HgfsPacket *packet,          // IN/OUT: Hgfs Packet
                        const void *packetHeader,    // IN: packet header
                        HgfsOp op,                   // IN: request type
                        HgfsHandle search,           // IN: search handle
                        size_t *payloadSize,         // OUT: size of packet
                        HgfsSessionInfo *session)    // IN: Session info
{
   Bool result = TRUE;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   switch (op) {
   case HGFS_OP_SEARCH_OPEN_V3: {
      HgfsReplySearchOpenV3 *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      reply->reserved = 0;
      reply->search = search;
      *payloadSize = sizeof *reply;
      break;
   }
   case HGFS_OP_SEARCH_OPEN: {
      HgfsReplySearchOpen *reply;

      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      reply->search = search;
      *payloadSize = sizeof *reply;
      break;
   }
   default:
      NOT_REACHED();
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackCreateSessionPayloadV4 --
 *
 *    Unpack hgfs create session request V4 payload.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackCreateSessionPayloadV4(const HgfsRequestCreateSessionV4 *requestV4, // IN: payload
                                 size_t payloadSize,                          // IN:
                                 HgfsCreateSessionInfo *info)                 // IN/OUT: info
{
   LOG(4, "%s: HGFS_OP_CREATE_SESSION_V4\n", __FUNCTION__);
   if (payloadSize  < offsetof(HgfsRequestCreateSessionV4, reserved)) {
      /* The input packet is smaller than the request. */
      return FALSE;
   }

   if (requestV4->numCapabilities) {
      if (payloadSize < offsetof(HgfsRequestCreateSessionV4, capabilities) +
         requestV4->numCapabilities * sizeof(HgfsOpCapability)) {
         LOG(4, "%s: HGFS packet too small\n", __FUNCTION__);
         return FALSE;
      }
   }

   info->maxPacketSize = requestV4->maxPacketSize;
   info->flags = requestV4->flags;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackCreateSessionRequest --
 *
 *    Unpack hgfs CreateSession request and initialize a corresponding
 *    HgfsCreateDirInfo structure that is used to pass around CreateDir request
 *    information.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackCreateSessionRequest(const void *packet,          // IN: HGFS packet
                               size_t packetSize,           // IN: size of packet
                               HgfsOp op,                   // IN: request type
                               HgfsCreateSessionInfo *info) // IN/OUT: info struct
{
   const HgfsRequestCreateSessionV4 *requestV4;

   ASSERT(packet);
   ASSERT(info);

   ASSERT(op == HGFS_OP_CREATE_SESSION_V4);

   requestV4 = packet;
   if (!HgfsUnpackCreateSessionPayloadV4(requestV4, packetSize, info)) {
      LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackCreateSessionReply --
 *
 *    Pack hgfs CreateSession reply.
 *
 * Results:
 *    Always TRUE.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackCreateSessionReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                           const void *packetHeader,  // IN: packet header
                           size_t *payloadSize,       // OUT: size of packet
                           HgfsSessionInfo *session)  // IN: Session info
{
   HgfsReplyCreateSessionV4 *reply;
   uint32 numCapabilities = session->numberOfCapabilities;
   uint32 capabilitiesLen = numCapabilities * sizeof *session->hgfsSessionCapabilities;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = offsetof(HgfsReplyCreateSessionV4, capabilities) + capabilitiesLen;

   reply = HgfsAllocInitReply(packet, packetHeader, *payloadSize, session);
   reply->sessionId = session->sessionId;
   reply->numCapabilities = numCapabilities;
   reply->maxPacketSize = session->maxPacketSize;
   reply->identityOffset = 0;
   reply->flags = session->flags;
   reply->reserved = 0;
   memcpy(reply->capabilities, session->hgfsSessionCapabilities, capabilitiesLen);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackDestroySessionReply --
 *
 *    Pack hgfs CreateSession reply.
 *
 * Results:
 *    Always TRUE.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackDestroySessionReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                            const void *packetHeader,  // IN: packet header
                            size_t *payloadSize,        // OUT: size of packet
                            HgfsSessionInfo *session)  // IN: Session info
{
   HgfsReplyDestroySessionV4 *reply;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                              session);
   /* Reply only consists of a reserved field. */
   *payloadSize = sizeof *reply;
   reply->reserved = 0;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerGetDefaultCapabilities --
 *
 *    Returns list capabilities that are supported by all sessions.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsServerGetDefaultCapabilities(HgfsOpCapability *capabilities, // OUT: capabilities
                                 uint32 *numberOfCapabilities)   // OUT: number of items
{
   *numberOfCapabilities = ARRAYSIZE(hgfsDefaultCapabilityTable);
   ASSERT(*numberOfCapabilities <= HGFS_OP_MAX);
   memcpy(capabilities, hgfsDefaultCapabilityTable, sizeof hgfsDefaultCapabilityTable);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSetWatchReplyV4 --
 *
 *    Pack hgfs set watch V4 reply payload to the HgfsReplySetWatchV4 structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsPackSetWatchReplyV4(HgfsSubscriberHandle watchId, // IN: host id of thee new watch
                        HgfsReplySetWatchV4 *reply)   // OUT: reply buffer to fill
{
   reply->watchId = watchId;
   reply->reserved = 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackSetWatchReply --
 *
 *    Pack hgfs set watch reply to the HgfsReplySetWatchV4 structure.
 *
 * Results:
 *    TRUE if successfully allocated reply request, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackSetWatchReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                      const void *packetHeader,     // IN: packet header
                      HgfsOp     op,                // IN: operation code
                      HgfsSubscriberHandle watchId, // IN: id of the new watch
                      size_t *payloadSize,          // OUT: size of packet
                      HgfsSessionInfo *session)     // IN: Session info
{
   Bool result = TRUE;
   HgfsReplySetWatchV4 *reply;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   if (HGFS_OP_SET_WATCH_V4 == op) {
      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      HgfsPackSetWatchReplyV4(watchId, reply);
      *payloadSize = sizeof *reply;
   } else {
      NOT_REACHED();
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSetWatchPayloadV4 --
 *
 *    Unpack HGFS set directory notication watch payload version 4 and initializes
 *    a corresponding HgfsHandle or file name to tell us which directory to watch.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackSetWatchPayloadV4(const HgfsRequestSetWatchV4 *requestV4, // IN: request payload
                            size_t payloadSize,                     // IN: payload size
                            Bool *useHandle,                        // OUT: handle or cpName
                            uint32 *flags,                          // OUT: watch flags
                            uint32 *events,                         // OUT: event filter
                            const char **cpName,                    // OUT: cpName
                            size_t *cpNameSize,                     // OUT: cpName size
                            HgfsHandle *dir,                        // OUT: directory handle
                            uint32 *caseFlags)                      // OUT: case-sensitivity
{
   if (payloadSize < sizeof *requestV4) {
      return FALSE;
   }

   *flags = requestV4->flags;
   *events = requestV4->events;

   return HgfsUnpackFileNameV3(&requestV4->fileName,
                               payloadSize - sizeof *requestV4,
                               useHandle,
                               cpName,
                               cpNameSize,
                               dir,
                               caseFlags);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackSetWatchRequest --
 *
 *    Unpack hgfs set directory notication watch request and initialize a corresponding
 *    HgfsHandle or directory name to tell us which directory to monitor.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackSetWatchRequest(const void *packet,      // IN: HGFS packet
                          size_t packetSize,       // IN: request packet size
                          HgfsOp op,               // IN: requested operation
                          Bool *useHandle,         // OUT: handle or cpName
                          const char **cpName,     // OUT: cpName
                          size_t *cpNameSize,      // OUT: cpName size
                          uint32 *flags,           // OUT: flags for the new watch
                          uint32 *events,          // OUT: event filter
                          HgfsHandle *dir,         // OUT: direrctory handle
                          uint32 *caseFlags)       // OUT: case-sensitivity flags
{
   const HgfsRequestSetWatchV4 *requestV4 = packet;
   Bool result;

   ASSERT(packet);
   ASSERT(cpName);
   ASSERT(cpNameSize);
   ASSERT(dir);
   ASSERT(flags);
   ASSERT(events);
   ASSERT(caseFlags);
   ASSERT(useHandle);

   if (HGFS_OP_SET_WATCH_V4 != op) {
      NOT_REACHED();
      result = FALSE;
   } else {
      LOG(4, "%s: HGFS_OP_SET_WATCH_V4\n", __FUNCTION__);
      result = HgfsUnpackSetWatchPayloadV4(requestV4, packetSize, useHandle, flags,
                                           events, cpName, cpNameSize, dir, caseFlags);
   }

   if (!result) {
      LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackRemoveWatchReply --
 *
 *    Pack hgfs remove watch reply to the HgfsReplyRemoveWatchV4 structure.
 *
 * Results:
 *    TRUE if successfully allocated reply request, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackRemoveWatchReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                         const void *packetHeader,     // IN: packet header
                         HgfsOp     op,                // IN: operation code
                         size_t *payloadSize,          // OUT: size of packet
                         HgfsSessionInfo *session)     // IN: Session info
{
   Bool result = TRUE;
   HgfsReplyRemoveWatchV4 *reply;

   HGFS_ASSERT_PACK_PARAMS;

   *payloadSize = 0;

   if (HGFS_OP_REMOVE_WATCH_V4 != op) {
      NOT_REACHED();
      result = FALSE;
   } else {
      reply = HgfsAllocInitReply(packet, packetHeader, sizeof *reply,
                                 session);
      reply->reserved = 0;
      *payloadSize = sizeof *reply;
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackRemoveWatchPayload --
 *
 *    Unpack HGFS remove directory notication watch payload version 4.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackRemoveWatchPayloadV4(const HgfsRequestRemoveWatchV4 *requestV4, // IN: request payload
                               size_t payloadSize,                        // IN: payload size
                               HgfsSubscriberHandle *watchId)             // OUT: watch id
{
   if (payloadSize < sizeof *requestV4) {
      return FALSE;
   }

   *watchId = requestV4->watchId;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackRemoveWatchRequest --
 *
 *    Unpack hgfs remove directory notication watch request.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackRemoveWatchRequest(const void *packet,            // IN: HGFS packet
                             size_t packetSize,             // IN: request packet size
                             HgfsOp op,                     // IN: requested operation
                             HgfsSubscriberHandle *watchId) // OUT: watch Id to remove
{
   const HgfsRequestRemoveWatchV4 *requestV4 = packet;

   ASSERT(packet);
   ASSERT(watchId);

   ASSERT(HGFS_OP_REMOVE_WATCH_V4 == op);

   if (HGFS_OP_REMOVE_WATCH_V4 != op) {
      return FALSE;
   } else if (!HgfsUnpackRemoveWatchPayloadV4(requestV4, packetSize, watchId)) {
      LOG(4, "%s: Error decoding HGFS packet\n", __FUNCTION__);
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackCalculateNotificationSize --
 *
 *    Calculates size needed for change notification packet.
 *
 * Results:
 *    TRUE if successfully allocated reply request, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

size_t
HgfsPackCalculateNotificationSize(char const *shareName, // IN: shared folder name
                                  char *fileName)        // IN: relative file path
{
   size_t result = sizeof (HgfsRequestNotifyV4);

   if (NULL != fileName) {
      size_t shareNameLen = strlen(shareName);
      result += strlen(fileName) + 1 + shareNameLen;
   }
   result += sizeof (HgfsHeader);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackGetOplockBreakSize --
 *
 *    Gets the size needed for the oplock break request.
 *
 * Results:
 *    Size of the oplock break request.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

size_t
HgfsPackGetOplockBreakSize(void)
{
   return sizeof (HgfsRequestOplockBreakV4) + sizeof (HgfsHeader);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackOplockBreakRequestV4( --
 *
 *    Pack hgfs oplock break V4 request to be sent to the guest.
 *
 * Results:
 *    Length of the packed structure or 0 if the structure does not fit in the
 *    the buffer.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static size_t
HgfsPackOplockBreakRequestV4(HgfsHandle fileId,                // IN: file ID
                             HgfsLockType serverLock,          // IN: lock type
                             size_t bufferSize,                // IN: available space
                             HgfsRequestOplockBreakV4 *reply)  // OUT: notification buffer
{
   size_t size = 0;

   if (bufferSize < sizeof *reply) {
      goto exit;
   }

   reply->reserved = 0;
   reply->fid = fileId;
   reply->serverLock = serverLock;
   size = sizeof *reply;

exit:
   return size;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackOplockBreakRequest --
 *
 *    Pack the HGFS protocol Oplock break request.
 *
 * Results:
 *    TRUE if successfully packed the request, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackOplockBreakRequest(void *packet,                    // IN/OUT: Hgfs Packet
                           HgfsHandle fileId,               // IN: file ID
                           HgfsLockType serverLock,         // IN: lock type
                           uint64 sessionId,                // IN: session ID
                           size_t *bufferSize)              // IN/OUT: size of packet
{
   size_t opBreakRequestSize;
   HgfsRequestOplockBreakV4 *opBreakRequest;
   HgfsHeader *header = packet;
   Bool result = TRUE;

   ASSERT(packet);
   ASSERT(bufferSize);

   if (*bufferSize < sizeof *header) {
      result = FALSE;
      goto exit;
   }

   /*
    *  Initialize notification header.
    *  Set status and requestId to 0 since these fields are not relevant for
    *  oplock break requests.
    *  Initialize payload size to 0 - it is not known yet and will be filled later.
    */
   opBreakRequest = (HgfsRequestOplockBreakV4 *)((char *)header + sizeof *header);
   opBreakRequestSize = HgfsPackOplockBreakRequestV4(fileId,
                                                     serverLock,
                                                     *bufferSize - sizeof *header,
                                                     opBreakRequest);
   if (0 == opBreakRequestSize) {
      result = FALSE;
      goto exit;
   }

   result = HgfsPackReplyHeaderV4(HGFS_ERROR_SUCCESS,
                                  opBreakRequestSize,
                                  HGFS_OP_OPLOCK_BREAK_V4,
                                  sessionId,
                                  0,
                                  HGFS_PACKET_FLAG_REQUEST,
                                  *bufferSize,
                                  header);

exit:
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackOplockBreakAckPayloadV4 --
 *
 *    Unpack HGFS oplock break acknowledge payload version 4.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsUnpackOplockBreakAckPayloadV4(const HgfsReplyOplockBreakV4 *opBrkAck,  // IN: request payload
                                  size_t payloadSize,                      // IN: payload size
                                  HgfsHandle *fileId,                      // OUT: file Id to remove
                                  HgfsLockType *serverLock)                // OUT: lock type
{
   if (payloadSize < sizeof *opBrkAck) {
      return FALSE;
   }

   *fileId     = opBrkAck->fid;
   *serverLock = opBrkAck->serverLock;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackOplockBreakAckReply --
 *
 *    Unpack hgfs oplock break acknowledge reply.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsUnpackOplockBreakAckReply(const void *packet,            // IN: HGFS packet
                              size_t packetSize,             // IN: reply packet size
                              HgfsOp op,                     // IN: operation version
                              HgfsHandle *fileId,            // OUT: file Id to remove
                              HgfsLockType *serverLock)      // OUT: lock type
{
   const HgfsReplyOplockBreakV4 *replyV4 = packet;
   Bool result = FALSE;

   ASSERT(fileId);
   ASSERT(serverLock);

   ASSERT(HGFS_OP_OPLOCK_BREAK_V4 == op);

   if (HGFS_OP_OPLOCK_BREAK_V4 == op) {
      result = HgfsUnpackOplockBreakAckPayloadV4(replyV4,
                                                 packetSize,
                                                 fileId,
                                                 serverLock);
   }

   if (!result) {
      LOG(4, "%s: Error unpacking HGFS_OP_OPLOCK_BREAK_V4 packet\n",
          __FUNCTION__);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsBuildCPName --
 *
 *    Build crossplatform name out of share name and relative to the shared folder
 *    file path.
 *
 * Results:
 *    Length of the output crossplatform name.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsBuildCPName(char const *shareName,  // IN: utf8 share name
                char *fileName,         // IN: utf8 file path
                char **cpName)          // OUT: full name in cp format
{
   size_t shareNameLen = strlen(shareName) + 1;
   size_t fileNameLen = strlen(fileName) + 1;
   char *fullName = Util_SafeMalloc(shareNameLen + fileNameLen);
   int cpNameResult;

   *cpName = Util_SafeMalloc(shareNameLen + fileNameLen);
   Str_Strcpy(fullName, shareName, shareNameLen);
   fullName[shareNameLen - 1] = DIRSEPC;
   Str_Strcpy(fullName + shareNameLen, fileName, fileNameLen);

   // Unescaped name can't be longer then escaped thus it must fit.
   cpNameResult = CPName_ConvertTo(fullName, shareNameLen + fileNameLen, *cpName);
   free(fullName);
   if (cpNameResult < 0) {
      free(*cpName);
      *cpName = NULL;
   }

   return cpNameResult;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackHgfsName --
 *
 *    Pack cpName into HgfsFileName structure.
 *
 * Results:
 *    TRUE if there is enough space in the buffer,
 *    FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsPackHgfsName(char *cpName,            // IN: cpName to pack
                 size_t cpNameLen,        // IN: length of the cpName
                 size_t availableSpace,   // IN: space available for HgfsFileName
                 size_t *nameSize,        // OUT: space consumed by HgfsFileName
                 HgfsFileName *fileName)  // OUT: structure to pack cpName into
{
   if (availableSpace < offsetof(HgfsFileName, name) + cpNameLen) {
      return FALSE;
   }
   fileName->length = cpNameLen;
   memcpy(fileName->name, cpName, cpNameLen);
   *nameSize = offsetof(HgfsFileName, name) + cpNameLen;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackChangeNotifyEventV4 --
 *
 *    Pack single change directory notification event information.
 *
 * Results:
 *    Length of the packed structure or 0 if the structure does not fit in the
 *    the buffer.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static size_t
HgfsPackChangeNotifyEventV4(uint32 mask,              // IN: event mask
                            char const *shareName,    // IN: share name
                            char *fileName,           // IN: file name
                            size_t bufferSize,        // IN: available space
                            HgfsNotifyEventV4 *reply) // OUT: notificaiton buffer
{
   size_t totalLength = 0;

   if (sizeof *reply > bufferSize) {
      /* Not enough space for the event, drop the event. */
      goto exit;
   }

   reply->nextOffset = 0;
   reply->mask = mask;
   if (NULL != fileName) {
      char *cpFullName;
      size_t remainingSize;
      size_t hgfsNameSize;
      int cpFullNameSize;

      cpFullNameSize = HgfsBuildCPName(shareName, fileName, &cpFullName);
      if (cpFullNameSize < 0) {
         /* Could not build the crossplatform name, drop the event. */
         goto exit;
      }
      remainingSize = bufferSize - offsetof(HgfsNotifyEventV4, fileName);
      if (!HgfsPackHgfsName(cpFullName,
                            cpFullNameSize,
                            remainingSize,
                            &hgfsNameSize,
                            &reply->fileName)) {
         /* Name would not fit, drop the event. */
         free(cpFullName);
         goto exit;
      }
      remainingSize -= hgfsNameSize;
      totalLength = bufferSize - remainingSize;
      free(cpFullName);
   } else {
      reply->fileName.length = 0;
      totalLength = sizeof *reply;
   }

exit:
   return totalLength;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackChangeNotifyRequestV4 --
 *
 *    Pack hgfs directory change notification request to be sent to the guest.
 *
 * Results:
 *    Length of the packed structure or 0 if the structure does not fit in the
 *    the buffer.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static size_t
HgfsPackChangeNotifyRequestV4(HgfsSubscriberHandle watchId,  // IN: watch
                              uint32 flags,                  // IN: notify flags
                              uint32 mask,                   // IN: event mask
                              char const *shareName,         // IN: share name
                              char *fileName,                // IN: relative file path
                              size_t bufferSize,             // IN: available space
                              HgfsRequestNotifyV4 *reply)    // OUT: notification buffer
{
   size_t size = 0;
   size_t notificationOffset;

   if (bufferSize < sizeof *reply) {
      LOG(4, "%s: Error HGFS_OP_NOTIFY_V4 buf size %"FMTSZ"u reply size %"FMTSZ"u\n",
          __FUNCTION__, bufferSize, sizeof *reply);
      goto exit;
   }
   reply->watchId = watchId;
   reply->flags = flags;
   if ((flags & HGFS_NOTIFY_FLAG_OVERFLOW) == HGFS_NOTIFY_FLAG_OVERFLOW) {
      size = sizeof *reply;
      reply->count = 0;
      reply->flags = HGFS_NOTIFY_FLAG_OVERFLOW;
   } else {
      /*
       * For the moment server sends only one notification at a time and it relies
       * on transport to coalesce requests.
       * Later on we may consider supporting multiple notifications.
       */
      reply->count = 1;
      notificationOffset = offsetof(HgfsRequestNotifyV4, events);
      size = HgfsPackChangeNotifyEventV4(mask, shareName, fileName,
                                         bufferSize - notificationOffset,
                                         reply->events);
      if (size != 0) {
         size += notificationOffset;
      } else {
         /*
         * Set event flag to tell guest that some events were dropped
         * when filling out notification details failed.
         */
         size = sizeof *reply;
         reply->count = 0;
         reply->flags = HGFS_NOTIFY_FLAG_OVERFLOW;
      }
   }

exit:
   return size;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackChangeNotificationRequest --
 *
 *    Pack hgfs directory change notification request to the
 *    HgfsRequestNotifyV4 structure.
 *
 * Results:
 *    TRUE if successfully allocated reply request, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsPackChangeNotificationRequest(void *packet,                    // IN/OUT: Hgfs Packet
                                  HgfsSubscriberHandle subscriber, // IN: watch
                                  char const *shareName,           // IN: share name
                                  char *fileName,                  // IN: relative name
                                  uint32 mask,                     // IN: event mask
                                  uint32 notifyFlags,              // IN: notify flags
                                  HgfsSessionInfo *session,        // IN: session
                                  size_t *bufferSize)              // INOUT: size of packet
{
   size_t notifyRequestSize;
   HgfsRequestNotifyV4 *notifyRequest;
   HgfsHeader *header = packet;
   Bool result = FALSE;

   ASSERT(packet);
   ASSERT(shareName);
   ASSERT(NULL != fileName ||
          (notifyFlags & HGFS_NOTIFY_FLAG_OVERFLOW) == HGFS_NOTIFY_FLAG_OVERFLOW);
   ASSERT(session);
   ASSERT(bufferSize);

   LOG(4, "%s: HGFS_OP_NOTIFY_V4\n", __FUNCTION__);

   if (*bufferSize < sizeof *header) {
      LOG(4, "%s: Error HGFS_OP_NOTIFY_V4 buf size %"FMTSZ"u min %"FMTSZ"u\n",
          __FUNCTION__, *bufferSize, sizeof *header);
      goto exit;
   }

   /*
    *  Initialize notification header.
    *  Set status and requestId to 0 since these fields are not relevant for
    *  notifications.
    *  Initialize payload size to 0 - it is not known yet and will be filled later.
    */
   notifyRequest = (HgfsRequestNotifyV4 *)((char *)header + sizeof *header);
   notifyRequestSize = HgfsPackChangeNotifyRequestV4(subscriber,
                                                     notifyFlags,
                                                     mask,
                                                     shareName,
                                                     fileName,
                                                     *bufferSize - sizeof *header,
                                                     notifyRequest);
   if (0 != notifyRequestSize) {
      result = HgfsPackReplyHeaderV4(HGFS_ERROR_SUCCESS,
                                     notifyRequestSize,
                                     HGFS_OP_NOTIFY_V4,
                                     session->sessionId,
                                     0,
                                     HGFS_PACKET_FLAG_REQUEST,
                                     *bufferSize,
                                     header);
   }

exit:
   return result;
}
  0707010000008B000081A4000000000000000000000001682255050000529D000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsServerParameters.h  /*********************************************************
 * Copyright (C) 2013-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerParameters.h --
 *
 *	This defines the HGFS protocol message packet functions
 *	for creating requests and extracting data from replies.
 */

#ifndef _HGFS_SERVER_PARAMETERS_H_
#define _HGFS_SERVER_PARAMETERS_H_

#include "hgfsServer.h"    // for HgfsPacket type
#include "hgfsProto.h"     // for the HGFS protocol request, reply and types
#include "hgfsUtil.h"      // for HgfsInternalStatus
#include "hgfsServerInt.h" // for HgfsSessionInfo


/*
 * Global functions
 */


HgfsInternalStatus
HgfsUnpackPacketParams(const void *packet,      // IN: HGFS packet
                       size_t packetSize,       // IN: request packet size
                       Bool *sessionEnabled,    // OUT: session enabled request
                       uint64 *sessionId,       // OUT: session Id
                       uint32 *requestId,       // OUT: unique request id
                       HgfsOp *opcode,          // OUT: request opcode
                       size_t *payloadSize,     // OUT: size of the opcode request
                       const void **payload);    // OUT: pointer to the opcode request

Bool
HgfsPackReplyHeader(HgfsInternalStatus status,    // IN: reply status
                    uint32 payloadSize,           // IN: size of the reply payload
                    Bool sessionEnabledHeader,    // IN: session enabled header
                    uint64 sessionId,             // IN: session id
                    uint32 requestId,             // IN: request id
                    HgfsOp op,                    // IN: request type
                    uint32 hdrFlags,              // IN: header flags
                    size_t hdrPacketSize,         // IN: header packet size
                    void *hdrPacket);             // OUT: outgoing packet header

Bool
HgfsUnpackOpenRequest(const void *packet,          // IN: incoming packet
                      size_t packetSize,           // IN: size of packet
                      HgfsOp op,                   // IN: request type
                      HgfsFileOpenInfo *openInfo); // IN/OUT: open info struct

Bool
HgfsPackOpenReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                  const void *packetHeader,     // IN: packet header
                  HgfsFileOpenInfo *openInfo,   // IN: open info struct
                  size_t *payloadSize,          // OUT: outgoing packet size
                  HgfsSessionInfo *session);    // IN: Session Info

Bool
HgfsUnpackGetattrRequest(const void *packetHeader,   // IN: packet header
                         size_t packetSize,          // IN: request packet size
                         HgfsOp op,                  // IN: request type
                         HgfsFileAttrInfo *attrInfo, // IN/OUT: unpacked attr struct
                         HgfsAttrHint *hints,        // OUT: getattr hints
                         const char **cpName,        // OUT: cpName
                         size_t *cpNameSize,         // OUT: cpName size
                         HgfsHandle *file,           // OUT: file handle
                         uint32 *caseFlags);         // OUT: case-sensitivity flags

Bool
HgfsUnpackDeleteRequest(const void *packet,         // IN: request packet
                        size_t packetSize,          // IN: request packet size
                        HgfsOp  op,                 // IN: requested operation
                        const char **cpName,        // OUT: cpName
                        size_t *cpNameSize,         // OUT: cpName size
                        HgfsDeleteHint *hints,      // OUT: delete hints
                        HgfsHandle *file,           // OUT: file handle
                        uint32 *caseFlags);         // OUT: case-sensitivity flags

Bool
HgfsPackDeleteReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                    const void *packetHeader,   // IN: packet header
                    HgfsOp op,                  // IN: requested operation
                    size_t *payloadSize,        // OUT: size of HGFS packet
                    HgfsSessionInfo *session);  // IN: Session Info

Bool
HgfsUnpackRenameRequest(const void *packet,         // IN: request packet
                        size_t packetSize,          // IN: request packet size
                        HgfsOp op,                  // IN: requested operation
                        const char **cpOldName,     // OUT: rename src
                        size_t *cpOldNameLen,       // OUT: rename src size
                        const char **cpNewName,     // OUT: rename dst
                        size_t *cpNewNameLen,       // OUT: rename dst size
                        HgfsRenameHint *hints,      // OUT: rename hints
                        HgfsHandle *srcFile,        // OUT: src file handle
                        HgfsHandle *targetFile,     // OUT: target file handle
                        uint32 *oldCaseFlags,       // OUT: old case-sensitivity flags
                        uint32 *newCaseFlags);      // OUT: new case-sensitivity flags

Bool
HgfsPackRenameReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                    const void *packetHeader,   // IN: packet header
                    HgfsOp op,                  // IN: requested operation
                    size_t *payloadSize,        // OUT: size of packet
                    HgfsSessionInfo *session);  // IN: Session Info

Bool
HgfsPackGetattrReply(HgfsPacket *packet,          // IN/OUT: Hgfs packet
                     const void *packetHeader,    // IN: packet header
                     HgfsFileAttrInfo *attr,      // IN: attr stucture
                     const char *utf8TargetName,  // IN: optional target name
                     uint32 utf8TargetNameLen,    // IN: file name length
                     size_t *payloadSize,         // OUT: size of HGFS packet
                     HgfsSessionInfo *session);   // IN: Session Info
Bool
HgfsPackSymlinkCreateReply(HgfsPacket *packet,        // IN/OUT: Hgfs packet
                           const void *packetHeader,  // IN: packet header
                           HgfsOp op,                 // IN: request type
                           size_t *payloadSize,       // OUT: size of HGFS packet
                           HgfsSessionInfo *session); // IN: Session Info
Bool
HgfsUnpackSearchReadRequest(const void *packet,           // IN: request packet
                            size_t packetSize,            // IN: packet size
                            HgfsOp op,                    // IN: requested operation
                            HgfsSearchReadInfo *info,     // OUT: search info
                            size_t *baseReplySize,        // OUT: op base reply size
                            size_t *inlineReplyDataSize,  // OUT: size of inline reply data
                            HgfsHandle *hgfsSearchHandle);// OUT: hgfs search handle
Bool
HgfsPackSearchReadReplyRecord(HgfsOp requestType,           // IN: search read request
                              HgfsSearchReadEntry *entry,   // IN: entry info
                              size_t maxRecordSize,         // IN: max size in bytes for record
                              void *lastSearchReadRecord,   // IN/OUT: last packed entry
                              void *currentSearchReadRecord,// OUT: currrent entry to pack
                              size_t *replyRecordSize);     // OUT: size of packet
Bool
HgfsPackSearchReadReplyHeader(HgfsSearchReadInfo *info,    // IN: request info
                              size_t *payloadSize);        // OUT: size of packet

Bool
HgfsUnpackSetattrRequest(const void *packet,            // IN: request packet
                         size_t packetSize,             // IN: request packet size
                         HgfsOp op,                     // IN: requested operation
                         HgfsFileAttrInfo *attr,        // IN/OUT: getattr info
                         HgfsAttrHint *hints,           // OUT: setattr hints
                         const char **cpName,           // OUT: cpName
                         size_t *cpNameSize,            // OUT: cpName size
                         HgfsHandle *file,              // OUT: server file ID
                         uint32 *caseFlags);            // OUT: case-sensitivity flags

Bool
HgfsPackSetattrReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                     const void *packetHeader,   // IN: packet header
                     HgfsOp op,                  // IN: request type
                     size_t *payloadSize,        // OUT: size of packet
                     HgfsSessionInfo *session);  // IN: Session Info

Bool
HgfsUnpackCreateDirRequest(const void *packet,       // IN: HGFS packet
                           size_t packetSize,        // IN: size of packet
                           HgfsOp op,                // IN: requested operation
                           HgfsCreateDirInfo *info); // IN/OUT: info struct

Bool
HgfsPackCreateDirReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                       const void *packetHeader,   // IN: packet header
                       HgfsOp op,                  // IN: request type
                       size_t *payloadSize,        // OUT: size of packet
                       HgfsSessionInfo *session);  // IN: Session Info

Bool
HgfsPackQueryVolumeReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                         const void *packetHeader,  // IN: packet header
                         HgfsOp op,                 // IN: request type
                         uint64 freeBytes,          // IN: volume free space
                         uint64 totalBytes,         // IN: volume capacity
                         size_t *payloadSize,       // OUT: size of packet
                         HgfsSessionInfo *session); // IN: Session Info
Bool
HgfsUnpackQueryVolumeRequest(const void *packet,     // IN: HGFS packet
                             size_t packetSize,      // IN: request packet size
                             HgfsOp op,              // IN: request type
                             Bool *useHandle,        // OUT: use handle
                             const char **fileName,  // OUT: file name
                             size_t *fileNameLength, // OUT: file name length
                             uint32 *caseFlags,      // OUT: case sensitivity
                             HgfsHandle *file);      // OUT: Handle to the volume
Bool
HgfsUnpackSymlinkCreateRequest(const void *packet,        // IN: request packet
                               size_t packetSize,         // IN: request packet size
                               HgfsOp op,                 // IN: request type
                               Bool *srcUseHandle,        // OUT: use source handle
                               const char **srcFileName,  // OUT: source file name
                               size_t *srcFileNameLength, // OUT: source file name length
                               uint32 *srcCaseFlags,      // OUT: source case sensitivity
                               HgfsHandle *srcFile,       // OUT: source file handle
                               Bool *tgUseHandle,         // OUT: use target handle
                               const char **tgFileName,   // OUT: target file name
                               size_t *tgFileNameLength,  // OUT: target file name length
                               uint32 *tgCaseFlags,       // OUT: target case sensitivity
                               HgfsHandle *tgFile);        // OUT: target file handle
Bool
HgfsUnpackWriteWin32StreamRequest(const void *packet,   // IN: HGFS packet
                                  size_t packetSize,    // IN: size of packet
                                  HgfsOp op,            // IN: request type
                                  HgfsHandle *file,     // OUT: file to write to
                                  const char **payload, // OUT: data to write
                                  size_t *requiredSize, // OUT: size of data
                                  Bool *doSecurity);    // OUT: restore sec.str.
Bool
HgfsUnpackCreateSessionRequest(const void *packetHeader,     // IN: request packet
                               size_t packetSize,            // IN: size of packet
                               HgfsOp op,                    // IN: request type
                               HgfsCreateSessionInfo *info); // IN/OUT: info struct
Bool
HgfsPackWriteWin32StreamReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                              const void *packetHeader,   // IN: packet headert
                              HgfsOp op,                  // IN: request type
                              uint32 actualSize,          // IN: amount written
                              size_t *payloadSize,        // OUT: size of packet
                              HgfsSessionInfo *session);  // IN:Session Info

Bool
HgfsUnpackCloseRequest(const void *packet,    // IN: request packet
                       size_t packetSize,     // IN: request packet size
                       HgfsOp op,             // IN: request type
                       HgfsHandle *file);     // OUT: Handle to close
Bool
HgfsUnpackSearchOpenRequest(const void *packet,      // IN: HGFS packet
                            size_t packetSize,       // IN: request packet size
                            HgfsOp op,               // IN: request type
                            const char **dirName,    // OUT: directory name
                            size_t *dirNameLength,   // OUT: name length
                            uint32 *caseFlags);      // OUT: case flags
Bool
HgfsPackCloseReply(HgfsPacket *packet,          // IN/OUT: Hgfs Packet
                   const void *packetHeader,    // IN: packet header
                   HgfsOp op,                   // IN: request type
                   size_t *payloadSize,         // OUT: size of packet
                   HgfsSessionInfo *session);   // IN: Session Info

Bool
HgfsUnpackSearchCloseRequest(const void *packet,    // IN: request packet
                             size_t packetSize,     // IN: request packet size
                             HgfsOp op,             // IN: request type
                             HgfsHandle *file);     // OUT: Handle to close
Bool
HgfsPackSearchOpenReply(HgfsPacket *packet,          // IN/OUT: Hgfs Packet
                        const void *packetHeader,    // IN: packet header
                        HgfsOp op,                   // IN: request type
                        HgfsHandle search,           // IN: search handle
                        size_t *packetSize,          // OUT: size of packet
                        HgfsSessionInfo *session);   // IN: Session Info
Bool
HgfsPackSearchCloseReply(HgfsPacket *packet,         // IN/OUT: Hgfs Packet
                         const void *packetHeader,   // IN: packet header
                         HgfsOp op,                  // IN: request type
                         size_t *packetSize,         // OUT: size of packet
                         HgfsSessionInfo *session);  // IN: Session Info
Bool
HgfsPackWriteReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                   const void *packetHeader,     // IN: packet header
                   HgfsOp op,                    // IN: request type
                   uint32 actualSize,            // IN: number of bytes that were written
                   size_t *payloadSize,          // OUT: size of packet
                   HgfsSessionInfo *session);    // IN: Session info
Bool
HgfsUnpackReadRequest(const void *packet,     // IN: HGFS request
                      size_t packetSize,      // IN: request packet size
                      HgfsOp  op,             // IN: request type
                      HgfsHandle *file,       // OUT: Handle to close
                      uint64 *offset,         // OUT: offset to read from
                      uint32 *length);        // OUT: length of data to read
Bool
HgfsUnpackWriteRequest(const void *writeRequest,// IN: HGFS write request params
                       size_t writeRequestSize, // IN: write request params size
                       HgfsOp writeOp,          // IN: request version
                       HgfsHandle *file,        // OUT: Handle to write to
                       uint64 *offset,          // OUT: offset to write to
                       uint32 *length,          // OUT: length of data to write
                       HgfsWriteFlags *flags,   // OUT: write flags
                       const void **data);      // OUT: data to be written
Bool
HgfsPackCreateSessionReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                           const void *packetHeader,  // IN: packet header
                           size_t *payloadSize,       // OUT: size of packet
                           HgfsSessionInfo *session); // IN: Session Info
Bool
HgfsPackDestroySessionReply(HgfsPacket *packet,        // IN/OUT: Hgfs Packet
                            const void *packetHeader,  // IN: packet header
                            size_t *payloadSize,       // OUT: size of packet
                            HgfsSessionInfo *session); // IN: Session Info
void
HgfsServerGetDefaultCapabilities(HgfsOpCapability *capabilities, // OUT:
                                 uint32 *numberOfCapabilities);  // OUT:
Bool
HgfsUnpackSetWatchRequest(const void *packet,      // IN: HGFS packet
                          size_t packetSize,       // IN: request packet size
                          HgfsOp op,               // IN: requested operation
                          Bool *useHandle,         // OUT: handle or cpName
                          const char **cpName,     // OUT: cpName
                          size_t *cpNameSize,      // OUT: cpName size
                          uint32 *flags,           // OUT: flags for the new watch
                          uint32 *events,          // OUT: event filter
                          HgfsHandle *dir,         // OUT: direrctory handle
                          uint32 *caseFlags);      // OUT: case-sensitivity flags
Bool
HgfsPackSetWatchReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                      const void *packetHeader,     // IN: packet header
                      HgfsOp     op,                // IN: operation code
                      HgfsSubscriberHandle watchId, // IN: new watch id
                      size_t *payloadSize,          // OUT: size of packet
                      HgfsSessionInfo *session);    // IN: Session info
Bool
HgfsUnpackRemoveWatchRequest(const void *packet,             // IN: HGFS packet
                             size_t packetSize,              // IN: packet size
                             HgfsOp op,                      // IN: operation code
                             HgfsSubscriberHandle *watchId); // OUT: watch Id
Bool
HgfsPackRemoveWatchReply(HgfsPacket *packet,           // IN/OUT: Hgfs Packet
                         const void *packetHeader,     // IN: packet header
                         HgfsOp     op,                // IN: operation code
                         size_t *payloadSize,          // OUT: size of packet
                         HgfsSessionInfo *session);    // IN: Session info
size_t
HgfsPackCalculateNotificationSize(char const *shareName, // IN: shared folder name
                                  char *fileName);       // IN: file name
Bool
HgfsPackChangeNotificationRequest(void *packet,                    // IN/OUT: Hgfs Packet
                                  HgfsSubscriberHandle subscriber, // IN: watch
                                  char const *shareName,           // IN: share name
                                  char *fileName,                  // IN: file name
                                  uint32 mask,                     // IN: event mask
                                  uint32 notifyFlags,              // IN: notify flags
                                  HgfsSessionInfo *session,        // IN: session
                                  size_t *bufferSize);             // IN/OUT: packet size


#endif // ifndef _HGFS_SERVER_PARAMETERS_H_
   0707010000008C000081A4000000000000000000000001682255050000068A000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsThreadpool.h    /*********************************************************
 * Copyright (C) 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _HGFS_THREADPOOL_H
#define _HGFS_THREADPOOL_H

/*
 * hgfsThreadpool.h --
 *
 *	Function definitions for threadpool.
 */

#include "hgfsUtil.h"       // for HgfsInternalStatus
#include "hgfsServerInt.h"  // for HgfsSessionInfo

/*
 * The maximum count of thread in threadpool.
 * For Windows 10 guest, the file explorer issues 8 asynchronous request
 * at one time.
 * Please keep this value same with the PHYSMEM_HGFS_WORKER_THREADS
 */
#define HGFS_THREADPOOL_MAX_COUNT 10

typedef void(*HgfsThreadpoolWorkItem)(void *data);

HgfsInternalStatus HgfsThreadpool_Init(void);

Bool HgfsThreadpool_Activate(void);
void HgfsThreadpool_Deactivate(void);

void HgfsThreadpool_Exit(void);
Bool HgfsThreadpool_QueueWorkItem(HgfsThreadpoolWorkItem workItem, void *data);

#endif // _HGFS_THREADPOOL_H
  0707010000008D000081A40000000000000000000000016822550500000C4D000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServer/hgfsThreadpoolStub.c    /*********************************************************
 * Copyright (C) 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsThreadPoolStub.c --
 *
 *	Stubs for threadpool support, used to build guest components.
 */

#include "vmware.h"
#include "vm_basic_types.h"
#include "util.h"

#include "hgfsProto.h"
#include "hgfsServer.h"
#include "hgfsThreadpool.h"

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsThreadpool_Init --
 *
 *    Initialization of the threadpool component.
 *
 * Results:
 *    0 if success, error code otherwise.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

HgfsInternalStatus
HgfsThreadpool_Init(void)
{
   return HGFS_ERROR_NOT_SUPPORTED;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsThreadpool_Activate --
 *
 *    Activate the threadpool.
 *
 * Results:
 *    Always return FALSE.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsThreadpool_Activate(void)
{
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsThreadpool_Deactivate --
 *
 *    Deactivate the threadpool.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsThreadpool_Deactivate(void)
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsThreadpool_Exit --
 *
 *    Exit for the threadpool component.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsThreadpool_Exit(void)
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsThreadpool_QueueWorkItem --
 *
 *    Execute a work item.
 *
 * Results:
 *    TRUE if the work item is queued successfully,
 *    FALSE if the work item is not queued.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsThreadpool_QueueWorkItem(HgfsThreadpoolWorkItem workItem, // IN
                             void *data)                      // IN
{
   return FALSE;
}

   0707010000008E000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerManagerGuest 0707010000008F000081A40000000000000000000000016822550500000491000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerManagerGuest/Makefile.am ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libHgfsServerManagerGuest.la

libHgfsServerManagerGuest_la_SOURCES =
libHgfsServerManagerGuest_la_SOURCES += hgfsServerManagerGuest.c
libHgfsServerManagerGuest_la_SOURCES += hgfsChannelGuest.c
libHgfsServerManagerGuest_la_SOURCES += hgfsChannelGuestBd.c
   07070100000090000081A4000000000000000000000001682255050000534E000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerManagerGuest/hgfsChannelGuest.c  /*********************************************************
 * Copyright (C) 2010-2017,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsChannelGuest.c --
 *
 *    Channel abstraction for the HGFS server.
 *
 */

#include <stdlib.h>
#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "vm_atomic.h"
#include "util.h"
#include "hgfsChannelGuestInt.h"
#include "hgfsServer.h"
#include "hgfsServerManager.h"

/*
 * HGFS server connection channel and state object usage.
 *
 * Currently, all plugins can share this same HGFS server channel and state.
 * This allows us to use a common channel so it is only initialized
 * once, by the first loaded plugin which requires an HGFS channel, and torn
 * down when the final plugin that uses the HGFS server is unloaded.
 *
 * Currently, the plugins are loaded (and unloaded) in any particular order,
 * and those operations are serialized. (For example the HGFS server plugin
 * maybe the first plugin loaded that uses this channel, but is not the final
 * plugin to be unloaded that uses the channel. This also may change in the
 * future, so no dependencies can be made on order of loading and unloading
 * of plugins.)
 * Furthermore, multiple plugins use the HGFS channel and server and some plugins
 * have multiple connections. Some plugins also create and teardown connections
 * during general mutlithreaded operation of the tools processes.
 *
 * In order to support the above, we must track how many users of the shared
 * connection there are. This allows us to tear down the shared connection
 * when the final plugin that is using it is unloaded, and when no
 * channels are in use the HGFS server state can be torn down.
 */

/*
 * The HGFS server state.
 *
 * This object is initiliazed once only and is shared across all
 * connections, shared or private.
 * Each new channel connection will reference the server and so the HGFS
 * server is initialized when the first new channel is being created. Each
 * new channel just increments the reference of server state object.
 * When the final channel is torn down the final HGFS server reference is
 * also removed and the HGFS server exit is called and this object is torn down.
 */
typedef struct HgfsChannelServerData {
   const HgfsServerCallbacks  *serverCBTable; /* HGFS server entry points. */
   Atomic_uint32              refCount;       /* Server data reference count. */
} HgfsChannelServerData;

/*
 * Transport channels context.
 *
 * Multiple callers share this same channel currently as only one
 * transport channel is required. Therefore, the channel is referenced
 * for each client that it is returned to (a usage count).
 */
typedef struct HgfsChannelData {
   const char                    *name;          /* Channel name. */
   const HgfsGuestChannelCBTable *ops;           /* Channel operations. */
   uint32                        state;          /* Channel state (see flags below). */
   struct HgfsGuestConn          *connection;    /* Opaque server connection */
   HgfsChannelServerData         *serverInfo;    /* HGFS server entry points. */
   Atomic_uint32                 refCount;       /* Channel reference count. */
} HgfsChannelData;

#define HGFS_CHANNEL_STATE_INIT         (1 << 0)
#define HGFS_CHANNEL_STATE_CBINIT       (1 << 1)

/* Static channel registration - assumes only one for now. */
static HgfsChannelData gHgfsChannels[] = {
   { "guest", &gGuestBackdoorOps, 0, NULL, NULL, {0} },
};

static HgfsServerConfig gHgfsGuestCfgSettings = {
   (HGFS_CONFIG_SHARE_ALL_HOST_DRIVES_ENABLED | HGFS_CONFIG_VOL_INFO_MIN),
   HGFS_MAX_CACHED_FILENODES
};

/* HGFS server info state. Referenced by each separate channel that uses it. */
static HgfsChannelServerData gHgfsChannelServerInfo = { NULL, {0} };

static void HgfsChannelTeardownChannel(HgfsChannelData *channel);
static void HgfsChannelTeardownServer(HgfsChannelServerData *serverInfo);
static void HgfsChannelExitChannel(HgfsChannelData *channel);


/*
 *----------------------------------------------------------------------------
 *
 * HGFS SERVER DATA FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGetServer --
 *
 *      Increment the server data reference count.
 *
 * Results:
 *      The value of the reference count before the increment.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static uint32
HgfsChannelGetServer(HgfsChannelServerData *serverInfo)   // IN/OUT: ref count
{
   ASSERT(NULL != serverInfo);
   return Atomic_ReadInc32(&serverInfo->refCount);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelPutServer --
 *
 *      Decrement server data reference count.
 *
 *      Teardown the server data object if removed the final reference.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelPutServer(HgfsChannelServerData *serverInfo)   // IN/OUT: ref count
{
   ASSERT(NULL != serverInfo);
   if (Atomic_ReadDec32(&serverInfo->refCount) == 1) {
      HgfsChannelTeardownServer(serverInfo);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelInitServer --
 *
 *      Initialize HGFS server and save the state.
 *
 * Results:
 *      TRUE if success, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
HgfsChannelInitServer(HgfsServerMgrCallbacks *mgrCb,       // IN: server manager callbacks
                      HgfsChannelServerData *serverInfo)   // IN/OUT: ref count
{
   Bool result;

   ASSERT(NULL == serverInfo->serverCBTable);

   Debug("%s: Initialize Hgfs server.\n", __FUNCTION__);

   /* If we have a new connection initialize the server session with default settings. */
   result = HgfsServer_InitState(&serverInfo->serverCBTable,
                                 &gHgfsGuestCfgSettings,
                                 mgrCb);
   if (!result) {
      Debug("%s: Could not init Hgfs server.\n", __FUNCTION__);
   }
   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelExitServer --
 *
 *      Reset the HGFS server and destroy the state.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelExitServer(HgfsChannelServerData *serverInfo)   // IN/OUT: ref count
{
   if (NULL != serverInfo->serverCBTable) {
      Debug("%s: Teardown Hgfs server.\n", __FUNCTION__);
      HgfsServer_ExitState();
      serverInfo->serverCBTable = NULL;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelTeardownServer --
 *
 *      Teardown the HGFS server state for all connections.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelTeardownServer(HgfsChannelServerData *serverInfo) // IN/OUT: connection manager object
{
   HgfsChannelExitServer(serverInfo);
}


/*
 *----------------------------------------------------------------------------
 *
 * CHANNEL DATA FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */


 /*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGetChannel --
 *
 *      Increment channel data reference count.
 *
 * Results:
 *      The value of the reference count before the increment.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

uint32
HgfsChannelGetChannel(HgfsChannelData *channel)   // IN/OUT: ref count
{
   ASSERT(NULL != channel);
   return Atomic_ReadInc32(&channel->refCount);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelPutChannel --
 *
 *      Decrement channel reference count.
 *
 *      Teardown channel object if removed the final reference.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelPutChannel(HgfsChannelData *channel)   // IN/OUT: ref count
{
   ASSERT(NULL != channel);
   if (Atomic_ReadDec32(&channel->refCount) == 1) {
      HgfsChannelTeardownChannel(channel);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelInitChannel --
 *
 *      Initializes a channel by initializing the HGFS server state.
 *
 * Results:
 *      TRUE if the channel initialized, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsChannelInitChannel(HgfsChannelData *channel,          // IN/OUT: channel object
                       HgfsServerMgrCallbacks *mgrCb,     // IN: server manager callbacks
                       HgfsChannelServerData *serverInfo) // IN/OUT: server info
{
   Bool result = TRUE;
   uint32 serverInfoCount;

   channel->state = 0;
   /*
    * Reference the HGFS server as it will be used by the new channel.
    * The HGFS server should only be initialized once, i.e. on the first
    * caller instance, otherwise only reference the server info for
    * the new channel.
    */
   serverInfoCount = HgfsChannelGetServer(serverInfo);
   /* Referenced the server, save it for dereferencing. */
   channel->serverInfo = serverInfo;
   if (0 == serverInfoCount) {
      /* The HGFS server has not been initialized, do it now. */
      result = HgfsChannelInitServer(mgrCb, channel->serverInfo);
      if (!result) {
         Debug("%s: Could not init Hgfs server.\n", __FUNCTION__);
         goto exit;
      }
   }

   channel->state |= HGFS_CHANNEL_STATE_INIT;

exit:
   if (!result) {
      HgfsChannelExitChannel(channel);
   }
   Debug("%s: Init channel return %d.\n", __FUNCTION__, result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelExitChannel --
 *
 *      Teardown the channel and teardown the HGFS server.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsChannelExitChannel(HgfsChannelData *channel) // IN/OUT: channel object
{
   if (NULL != channel->serverInfo) {
      /* Remove the reference for the HGFS server info. */
      HgfsChannelPutServer(channel->serverInfo);
      channel->serverInfo = NULL;
    }
   channel->state = 0;
   Debug("%s: Exit channel returns.\n", __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelActivateChannel --
 *
 *      Activate a channel by calling the channels init callback.
 *
 * Results:
 *      TRUE if a channel is active.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsChannelActivateChannel(HgfsChannelData *channel,   // IN/OUT: channel object
                           void *rpc,                  // IN: Rpc channel
                           void *rpcCallback)          // IN: Rpc callback
{
   Bool success = FALSE;
   struct HgfsGuestConn *connData = NULL;

   if (channel->ops->init(&channel->serverInfo->serverCBTable->session,
                          rpc,
                          rpcCallback,
                          &connData)) {
      channel->state |= HGFS_CHANNEL_STATE_CBINIT;
      channel->connection = connData;
      success = TRUE;
   }
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelDeactivateChannel --
 *
 *      Deactivate a channel by calling the channels exit callback.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsChannelDeactivateChannel(HgfsChannelData *channel)   // IN/OUT: channel object
{
   channel->ops->exit(channel->connection);
   channel->state &= ~HGFS_CHANNEL_STATE_CBINIT;
   channel->connection = NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelIsChannelActive --
 *
 *      Is the channel active (initialized) for processing requests.
 *
 * Results:
 *      TRUE if a channel is active.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsChannelIsChannelActive(HgfsChannelData *channel) // IN/OUT: channel object
{
   return (0 != (channel->state & HGFS_CHANNEL_STATE_INIT) &&
           0 != (channel->state & HGFS_CHANNEL_STATE_CBINIT));
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelReceive --
 *
 *      Received a request on a channel pass on to the channel callback.
 *
 * Results:
 *      TRUE if a channel ws deactivated.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsChannelReceive(HgfsChannelData *channel,   // IN/OUT: channel object
                   char const *packetIn,       // IN: incoming packet
                   size_t packetInSize,        // IN: incoming packet size
                   char *packetOut,            // OUT: outgoing packet
                   size_t *packetOutSize)      // IN/OUT: outgoing packet size
{
   return channel->ops->receive(channel->connection,
                                packetIn,
                                packetInSize,
                                packetOut,
                                packetOutSize);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelTeardownChannel --
 *
 *      Teardown the channel for HGFS.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelTeardownChannel(HgfsChannelData *channel) // IN/OUT: connection manager object
{
   if (HgfsChannelIsChannelActive(channel)) {
      HgfsChannelDeactivateChannel(channel);
   }
   HgfsChannelExitChannel(channel);
}


/*
 *----------------------------------------------------------------------------
 *
 * CHANNEL PUBLIC FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuest_Init --
 *
 *      Sets up the channel for HGFS.
 *
 *      Initialize all the defined channels.
 *      At least one channel should succeed it's initialization
 *      completely, else we fail.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
HgfsChannelGuest_Init(HgfsServerMgrData *mgrData,    // IN/OUT: server manager data
                      HgfsServerMgrCallbacks *mgrCb) // IN: server manager callbacks
{
   Bool success = FALSE;
   HgfsChannelData *channel = &gHgfsChannels[0]; // Shared channel (internal RPC)
   uint32 channelRefCount;

   ASSERT(NULL != mgrData);
   ASSERT(NULL == mgrData->connection);
   /* Currently, the RPC override is not implemented. */
   ASSERT(NULL == mgrData->rpc);
   ASSERT(NULL == mgrData->rpcCallback);
   ASSERT(NULL != mgrData->appName);

   Debug("%s: app %s rpc = %p rpc cb = %p.\n", __FUNCTION__,
         mgrData->appName, mgrData->rpc, mgrData->rpcCallback);

   if (NULL != mgrData->rpc || NULL != mgrData->rpcCallback) {
      /*
       * XXX - Would malloc a new channel here and activate
       * with the required RPC.
       */

      Debug("%s: Guest channel RPC override not supported.\n", __FUNCTION__);
      goto exit;
   }

   /*
    * Reference the channel. Initialize only for the first
    * caller instance, otherwise only reference the channel for
    * return to the caller.
    */
   channelRefCount = HgfsChannelGetChannel(channel);
   /* We have referenced the channel, save it for later dereference. */
   mgrData->connection = channel;
   if (0 == channelRefCount) {

      /* Initialize channels objects. */
      if (!HgfsChannelInitChannel(channel, mgrCb, &gHgfsChannelServerInfo)) {
         Debug("%s: Could not init channel.\n", __FUNCTION__);
         goto exit;
      }

      /* Call the channels initializers. */
      if (!HgfsChannelActivateChannel(channel,
                                      mgrData->rpc,
                                      mgrData->rpcCallback)) {
         Debug("%s: Could not activate channel.\n", __FUNCTION__);
         goto exit;
      }
   }

   success = TRUE;

exit:
   if (!success) {
      HgfsChannelGuest_Exit(mgrData);
   }
   return success;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuest_Exit --
 *
 *      Dereference the channel which for the final reference will
 *      close the channel for HGFS.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsChannelGuest_Exit(HgfsServerMgrData *mgrData) // IN/OUT: connection manager object
{
   HgfsChannelData *channel;

   ASSERT(NULL != mgrData);
   ASSERT(NULL != mgrData->appName);

   channel = mgrData->connection;

   Debug("%s: app %s rpc = %p rpc cb = %p chn = %p.\n", __FUNCTION__,
         mgrData->appName, mgrData->rpc, mgrData->rpcCallback, channel);

   if (NULL != channel) {
      HgfsChannelPutChannel(channel);
      mgrData->connection = NULL;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuest_Receive --
 *
 *    Process packet not associated with an HGFS only registered callback.
 *
 *
 * Results:
 *    TRUE if successfully processed FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

Bool
HgfsChannelGuest_Receive(HgfsServerMgrData *mgrData, // IN/OUT : conn manager
                         char const *packetIn,       // IN: incoming packet
                         size_t packetInSize,        // IN: incoming packet size
                         char *packetOut,            // OUT: outgoing packet
                         size_t *packetOutSize)      // IN/OUT: outgoing packet size
{
   HgfsChannelData *channel;
   Bool result = FALSE;

   ASSERT(NULL != mgrData);
   ASSERT(NULL != mgrData->connection);
   ASSERT(NULL != mgrData->appName);

   channel = mgrData->connection;

   Debug("%s: %s Channel receive request.\n", __FUNCTION__, mgrData->appName);

   if (HgfsChannelIsChannelActive(channel)) {
      result = HgfsChannelReceive(channel,
                                  packetIn,
                                  packetInSize,
                                  packetOut,
                                  packetOutSize);
   }

   Debug("%s: Channel receive returns %#x.\n", __FUNCTION__, result);

   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuest_InvalidateInactiveSessions -
 *
 *    Sends a request to invalidate all the inactive HGFS server sessions.
 *
 * Results:
 *    Number of active sessions remaining inside the HGFS server.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

uint32
HgfsChannelGuest_InvalidateInactiveSessions(HgfsServerMgrData *mgrData) // IN: conn manager
{
   HgfsChannelData *channel;
   uint32 result = 0;

   ASSERT(NULL != mgrData);
   ASSERT(NULL != mgrData->connection);
   ASSERT(NULL != mgrData->appName);

   channel = mgrData->connection;

   Debug("%s: %s Channel. Invalidating inactive sessions.\n",
         __FUNCTION__, mgrData->appName);

   if (HgfsChannelIsChannelActive(channel)) {
      result = channel->ops->invalidateInactiveSessions(channel->connection);
   }

   return result;
}
  07070100000091000081A40000000000000000000000016822550500005134000000000000000000000000000000000000005300000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerManagerGuest/hgfsChannelGuestBd.c    /*********************************************************
 * Copyright (C) 2010-2017,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsChannel.c --
 *
 *    Channel abstraction for the HGFS server.
 *
 */

#include <stdlib.h>
#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "vm_atomic.h"
#include "util.h"
#include "hgfsChannelGuestInt.h"
#include "hgfsServer.h"
#include "hgfsServerManager.h"

typedef enum {
   HGFS_GST_CONN_UNINITIALIZED,
   HGFS_GST_CONN_NOTCONNECTED,
   HGFS_GST_CONN_CONNECTED,
} HgfsGuestConnState;


/* Since there is only one connection we use globals. */
typedef struct HgfsGuestConn {
   Atomic_uint32 refCount;                   /* Reference count. */
   HgfsGuestConnState state;
   const HgfsServerSessionCallbacks *serverCbTable; /* Server session callbacks. */
   HgfsServerChannelCallbacks channelCbTable;
   void *serverSession;
   size_t packetOutLen;
   unsigned char *clientPacketOut;                 /* Client supplied buffer. */
   unsigned char packetOut[HGFS_LARGE_PACKET_MAX]; /* For RPC msg callbacks. */
} HgfsGuestConn;


/* Callback functions. */
static Bool HgfsChannelGuestBdInit(const HgfsServerSessionCallbacks *serverCBTable,
                                   void *rpc,
                                   void *rpcCallback,
                                   HgfsGuestConn **connection);
static void HgfsChannelGuestBdExit(HgfsGuestConn *data);
static Bool HgfsChannelGuestBdSend(void *data,
                                   HgfsPacket *packet,
                                   HgfsSendFlags flags);
static Bool HgfsChannelGuestBdReceive(HgfsGuestConn *data,
                                      char const *packetIn,
                                      size_t packetInSize,
                                      char *packetOut,
                                      size_t *packetOutSize);
static uint32 HgfsChannelGuestBdInvalidateInactiveSessions(HgfsGuestConn *data);

const HgfsGuestChannelCBTable gGuestBackdoorOps = {
   HgfsChannelGuestBdInit,
   HgfsChannelGuestBdExit,
   HgfsChannelGuestBdReceive,
   HgfsChannelGuestBdInvalidateInactiveSessions,
};

/* Private functions. */
static Bool HgfsChannelGuestConnConnect(HgfsGuestConn *connData);
static void HgfsChannelGuestConnDestroy(HgfsGuestConn *connData);
static Bool HgfsChannelGuestReceiveInternal(HgfsGuestConn *connData,
                                            char const *packetIn,
                                            size_t packetInSize,
                                            char *packetOut,
                                            size_t *packetOutSize);


/*
 *----------------------------------------------------------------------------
 *
 * CONNECTION DATA FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnGet --
 *
 *      Increment connection reference count.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelGuestConnGet(HgfsGuestConn *connData)   // IN: connection
{
   ASSERT(connData);
   Atomic_Inc(&connData->refCount);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnPut --
 *
 *      Decrement connection reference count.
 *
 *      Free connection data if this is the last reference.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelGuestConnPut(HgfsGuestConn *connData)   // IN: connection
{
   ASSERT(connData);
   if (Atomic_ReadDec32(&connData->refCount) == 1) {
      HgfsChannelGuestConnDestroy(connData);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnInit --
 *
 *      Initializes the connection.
 *
 * Results:
 *      TRUE always and the channel initialized.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsChannelGuestConnInit(HgfsGuestConn **connData,                        // IN/OUT: channel object
                         const HgfsServerSessionCallbacks *serverCBTable) // IN: server callbacks
{
   HgfsGuestConn *conn = NULL;

   conn = Util_SafeCalloc(1, sizeof *conn);

   /* Give ourselves a reference of one. */
   HgfsChannelGuestConnGet(conn);
   conn->serverCbTable = serverCBTable;
   conn->state = HGFS_GST_CONN_NOTCONNECTED;

   *connData = conn;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnExit --
 *
 *      Teardown the connection.
 *
 *      Removes the reference and if it is the last will cause the connection
 *      to be destroyed.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsChannelGuestConnExit(HgfsGuestConn *connData) // IN/OUT: channel object
{
   connData->state = HGFS_GST_CONN_UNINITIALIZED;

   HgfsChannelGuestConnPut(connData);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnDestroy --
 *
 *      Destroy the connection.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsChannelGuestConnDestroy(HgfsGuestConn *connData) // IN/OUT: channel object
{
   /* Make sure the server closes it's own session data. */
   if (NULL != connData->serverSession) {
      connData->serverCbTable->close(connData->serverSession);
      connData->serverSession = NULL;
   }
   free(connData);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnCreate --
 *
 *      Create's the RPC connection for the HGFS guest if asked.
 *
 *      Create the pseudo connection for the guest - state transition.
 *      (See the comment in the function where the RPC initialization
 *      is expected to be added.
 *      This entails is registering our callback to receive messages for the
 *      connection object passed. We will have the ability to receive
 *      requests until we unregister our callback.)
 *
 *      NOTE: There is only handler and connction that can be used for
 *      all HGFS guest requests.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsChannelGuestConnCreate(HgfsGuestConn *connData,      // IN: connection
                           void *rpc,                    // IN: Rpc channel unused
                           void *rpcCallback)            // IN: Rpc callback unused
{
   ASSERT(connData->state == HGFS_GST_CONN_NOTCONNECTED);

   /*
    * Rpc may be NULL for some cases. For example, if we
    * just need to provide an HGFS server connection
    * not associated with an HGFS only RPC connection.
    */
   if (connData->state == HGFS_GST_CONN_NOTCONNECTED) {

      /* XXX - Here is where we would register an RPC callback if required. */

      connData->state = HGFS_GST_CONN_CONNECTED;
      HgfsChannelGuestConnGet(connData);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnClose --
 *
 *      Closes the connection for the HGFS guest.
 *
 *      If required unregisters the callback will prevent us from
 *      receiving any more requests closing the connection.
 *
 * Results:
 *      TRUE if closed, FALSE if was not connected.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsChannelGuestConnClose(HgfsGuestConn *connData,    // IN: Connection
                          void *rpc,                  // IN: Rpc channel unused
                          void *rpcCallback)          // IN: Rpc callback unused
{
   Bool result = FALSE;

   if (connData->state == HGFS_GST_CONN_CONNECTED) {
      /* XXX - Here is where we would unregister an RPC callback. */

      /* Clear the connection object since we are unregistered. */
      connData->state = HGFS_GST_CONN_NOTCONNECTED;
      HgfsChannelGuestConnPut(connData);
      result = TRUE;
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnConnect --
 *
 *      Send connection to the server.
 *
 * Results:
 *      TRUE if server returns a data object, FALSE if not.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsChannelGuestConnConnect(HgfsGuestConn *connData)  // IN: our connection data
{
   Bool result;
   static HgfsServerChannelData HgfsBdCapData = {
      0,
      HGFS_LARGE_PACKET_MAX
   };

   connData->channelCbTable.getWriteVa = NULL;
   connData->channelCbTable.getReadVa = NULL;
   connData->channelCbTable.putVa = NULL;
   connData->channelCbTable.send = HgfsChannelGuestBdSend;
   result = connData->serverCbTable->connect(connData,
                                             &connData->channelCbTable,
                                             &HgfsBdCapData,
                                             &connData->serverSession);
   if (result) {
      HgfsChannelGuestConnGet(connData);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnDisconnect --
 *
 *      Send disconnect to the server.
 *
 *      NOTE: The server data will be maintained until
 *      the connection is totally closed (last reference is gone).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsChannelGuestConnDisconnect(HgfsGuestConn *connData)  // IN: connection
{
   if (connData->serverSession != NULL) {
      /* Tell the server to to disconnect the session. */
      connData->serverCbTable->disconnect(connData->serverSession);
      HgfsChannelGuestConnPut(connData);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuestConnCloseInternal --
 *
 *      Close the client and send a disconnect to the server for the session.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Closes the client connection and empties the queues.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelGuestConnCloseInternal(HgfsGuestConn *connData, // IN: Connection data
                                  void *rpc,               // IN: Rpc channel unused
                                  void *rpcCallback)       // IN: Rpc callback unused
{
   /* Close (unregister the backdoor RPC) connection. */
   if (HgfsChannelGuestConnClose(connData, rpc, rpcCallback)) {
      /* Disconnect the connection from the server. */
      HgfsChannelGuestConnDisconnect(connData);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuestReceiveInternal --
 *
 *    Process packet not associated with any session.
 *
 *    This function is used in the HGFS server inside Tools.
 *
 *    Create an internal session if not already created, and process the packet.
 *
 * Results:
 *    TRUE if received packet ok and processed, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

static Bool
HgfsChannelGuestReceiveInternal(HgfsGuestConn *connData,  // IN: connection
                                char const *packetIn,     // IN: incoming packet
                                size_t packetInSize,      // IN: incoming packet size
                                char *packetOut,          // OUT: outgoing packet
                                size_t *packetOutSize)    // IN/OUT: outgoing packet size
{
   HgfsPacket packet;

   ASSERT(packetIn);
   ASSERT(packetOut);
   ASSERT(packetOutSize);

   if (connData->state == HGFS_GST_CONN_UNINITIALIZED) {
      /* The connection was closed as we are exiting, so bail. */
      *packetOutSize = 0;
      return FALSE;
   }

   /* This is just a ping, return nothing. */
   if (*packetOutSize == 0) {
      return TRUE;
   }

   /*
    * Create the session if not already created.
    * This session is destroyed in HgfsServer_ExitState.
    */
   if (connData->serverSession == NULL) {
      /* Do our guest connect now which will inform the server. */
      if (!HgfsChannelGuestConnConnect(connData)) {
         *packetOutSize = 0;
         return FALSE;
      }
   }

   memset(&packet, 0, sizeof packet);
   /* For backdoor there is only one iov */
   packet.iov[0].va = (void *)packetIn;
   packet.iov[0].len = packetInSize;
   packet.iovCount = 1;
   packet.metaPacket = (void *)packetIn;
   packet.metaPacketDataSize = packetInSize;
   packet.metaPacketSize = packetInSize;
   packet.replyPacket = packetOut;
   packet.replyPacketSize = *packetOutSize;
   packet.state |= HGFS_STATE_CLIENT_REQUEST;

   /* The server will perform a synchronous processing of requests. */
   connData->serverCbTable->receive(&packet, connData->serverSession);

   *packetOutSize = connData->packetOutLen;

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * REGISTERED CALLBACK FUNCTIONS
 *
 * XXX - Where we would have any internally registered callback routines.
 * This routine would call HgfsChannelGuestReceiveInternal to process the
 * request.
 *
 *----------------------------------------------------------------------------
 */


/*
 *----------------------------------------------------------------------------
 *
 * GUEST CHANNEL CALLBACKS
 *
 *----------------------------------------------------------------------------
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuestBdReceive --
 *
 *    Process packet not associated with our registered callback.
 *
 *
 * Results:
 *    TRUE if received packet ok and processed, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

Bool
HgfsChannelGuestBdReceive(HgfsGuestConn *connData,    // IN: connection
                          char const *packetIn,       // IN: incoming packet
                          size_t packetInSize,        // IN: incoming packet size
                          char *packetOut,            // OUT: outgoing packet
                          size_t *packetOutSize)      // IN/OUT: outgoing packet size
{
   Bool result = TRUE;

   ASSERT(NULL != packetIn);
   ASSERT(NULL != packetOut);
   ASSERT(NULL != packetOutSize);
   ASSERT(NULL != connData);

   if (NULL == connData) {
      result = FALSE;
      goto exit;
   }

   connData->packetOutLen = *packetOutSize;
   connData->clientPacketOut = packetOut;

   result = HgfsChannelGuestReceiveInternal(connData,
                                            packetIn,
                                            packetInSize,
                                            connData->clientPacketOut,
                                            packetOutSize);

   connData->clientPacketOut = NULL;
   connData->packetOutLen = sizeof connData->packetOut;

exit:
   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuestBdInvalidateInactiveSessions --
 *
 *    Sends a request to invalidate all the inactive HGFS server sessions.
 *
 * Results:
 *    Number of active sessions remaining inside the HGFS server.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

uint32
HgfsChannelGuestBdInvalidateInactiveSessions(HgfsGuestConn *connData)  // IN: connection
{
   ASSERT(NULL != connData);

   if (NULL == connData) {
      return 0;
   }

   if (connData->state == HGFS_GST_CONN_UNINITIALIZED) {
      /* The connection was closed as we are exiting, so bail. */
      return 0;
   }

   /* The server will perform a synchronous processing of requests. */
   if (connData->serverSession) {
      return connData->serverCbTable->invalidateInactiveSessions(connData->serverSession);
   }

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsChannelGuestBdSend --
 *
 *      Send reply to the request
 *
 * Results:
 *      Always TRUE.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsChannelGuestBdSend(void *conn,              // IN: our connection data
                       HgfsPacket *packet,      // IN/OUT: Hgfs Packet
                       HgfsSendFlags flags)     // IN: Flags to say how to process
{
   HgfsGuestConn *connData = conn;

   ASSERT(NULL != connData);
   ASSERT(NULL != packet);
   ASSERT(NULL != packet->replyPacket);
   ASSERT(packet->replyPacketDataSize <= connData->packetOutLen);
   ASSERT(packet->replyPacketSize == connData->packetOutLen);

   if (packet->replyPacketDataSize > connData->packetOutLen) {
      packet->replyPacketDataSize = connData->packetOutLen;
   }
   connData->packetOutLen = (uint32)packet->replyPacketDataSize;

   if (!(flags & HGFS_SEND_NO_COMPLETE)) {
      connData->serverCbTable->sendComplete(packet,
                                            connData->serverSession);
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuestBdInit --
 *
 *      Called from channel manager.
 *
 *      Initializes our channel connections.
 *
 * Results:
 *      Always TRUE.
 *
 * Side effects:
 *      Registers RPC call.
 *
 *----------------------------------------------------------------------------
 */

static Bool
HgfsChannelGuestBdInit(const HgfsServerSessionCallbacks *serverCBTable, // IN: server callbacks
                       void *rpc,                                       // IN: Rpc channel unused
                       void *rpcCallback,                               // IN: Rpc callback unused
                       HgfsGuestConn **connection)                      // OUT: connection object
{
   HgfsGuestConn *connData = NULL;
   Bool result;

   ASSERT(NULL != connection);

   /* Create our connection object. */
   result = HgfsChannelGuestConnInit(&connData,
                                     serverCBTable);
   if (!result) {
      Debug("%s: Error: guest connection initialized.\n", __FUNCTION__);
      goto exit;
   }

   /*
    * Create our connection now with any rpc handle and callback.
    */
   HgfsChannelGuestConnCreate(connData,
                              rpc,
                              rpcCallback);

exit:
   if (!result) {
      if (NULL != connData) {
         HgfsChannelGuestBdExit(connData);
         connData = NULL;
      }
   }
   *connection = connData;
   Debug("%s: guest initialized.\n", __FUNCTION__);
   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsChannelGuestBdExit --
 *
 *      Tearsdown our channel connections.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Unregisters RPC call.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsChannelGuestBdExit(HgfsGuestConn *connData)
{
   ASSERT(NULL != connData);

   if (NULL != connData) {
      /* Currently no rpc to unregister. */
      HgfsChannelGuestConnCloseInternal(connData, NULL, NULL);
      HgfsChannelGuestConnExit(connData);
   }
}
07070100000092000081A40000000000000000000000016822550500000A4D000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerManagerGuest/hgfsChannelGuestInt.h   /*********************************************************
 * Copyright (C) 2009-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _HGFSCHANNELGUESTINT_H_
#define _HGFSCHANNELGUESTINT_H_

#if defined(VMTOOLS_USE_GLIB)
#define G_LOG_DOMAIN          "hgfsd"
#define Debug                 g_debug
#define Warning               g_warning
#else
#include "debug.h"
#endif
#include "hgfsServer.h"
#include "hgfsServerManager.h"

/**
 * @file hgfsChannelGuestInt.h
 *
 * Prototypes of Hgfs channel packet process handler found in
 * hgfsChannelGuest.c
 */

/*
 * Opaque structure owned by the guest channel to hold the connection
 * data to the HGFS server. Only held by the channel manager to pass
 * back to the guest channel for requests and teardown.
 * (Or it would be used with any registered internal callback.)
 */
 struct HgfsGuestConn;

/*
 * Guest channel table of callbacks.
 */
typedef struct HgfsGuestChannelCBTable {
   Bool (*init)(const HgfsServerSessionCallbacks *, void *, void *, struct HgfsGuestConn **);
   void (*exit)(struct HgfsGuestConn *);
   Bool (*receive)(struct HgfsGuestConn *, char const *, size_t, char *, size_t *);
   uint32 (*invalidateInactiveSessions)(struct HgfsGuestConn *);
} HgfsGuestChannelCBTable;

/* The guest channels callback tables. */
extern const HgfsGuestChannelCBTable gGuestBackdoorOps;

/* For use by HgfsServerManager. */
Bool HgfsChannelGuest_Init(HgfsServerMgrData *data, HgfsServerMgrCallbacks *cb);
void HgfsChannelGuest_Exit(HgfsServerMgrData *data);
Bool HgfsChannelGuest_Receive(HgfsServerMgrData *data,
                              char const *packetIn,
                              size_t packetInSize,
                              char *packetOut,
                              size_t *packetOutSize);
uint32 HgfsChannelGuest_InvalidateInactiveSessions(HgfsServerMgrData *data);

#endif /* _HGFSCHANNELGUESTINT_H_ */

   07070100000093000081A40000000000000000000000016822550500001BF6000000000000000000000000000000000000005700000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerManagerGuest/hgfsServerManagerGuest.c    /*********************************************************
 * Copyright (C) 2006,2014-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * hgfsServerManagerGuest.c --
 *
 *    Functionality to utilize the hgfs server in bora/lib from within
 *    a guest application.
 *
 */

#include <string.h>
#include "hgfsServerPolicy.h"
#include "hgfsChannelGuestInt.h"
#include "hgfsServerManager.h"
#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "vm_atomic.h"
#include "hgfs.h"

/*
 * Local for now and will be used in conjunction with the manager data passed
 * on registration.
 */
typedef struct HgfsServerMgrCountedCallbacks {
   HgfsServerMgrCallbacks  serverMgrCBTable; /* Hgfs server policy manager entry points. */
   Atomic_uint32           refCount;         /* Server data reference count. */
} HgfsServerMgrCountedCallbacks;

static HgfsServerMgrCountedCallbacks     gHgfsServerManagerGuestData;


 /*
 *----------------------------------------------------------------------------
 *
 * HgfsServerManagerGet --
 *
 *      Increment server manager reference count.
 *
 * Results:
 *      The value of the reference count before the increment.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

uint32
HgfsServerManagerGet(HgfsServerMgrCountedCallbacks *serverMgrData)   // IN/OUT: ref count
{
   ASSERT(NULL != serverMgrData);
   return Atomic_ReadInc32(&serverMgrData->refCount);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerManagerPut --
 *
 *      Decrement server manager reference count.
 *
 *      Teardown server manager object if removed the final reference.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsServerManagerPut(HgfsServerMgrCountedCallbacks *serverMgrData)   // IN/OUT: ref count
{
   ASSERT(NULL != serverMgrData);
   if (Atomic_ReadDec32(&serverMgrData->refCount) == 1) {
      HgfsServerPolicy_Cleanup();
      memset(serverMgrData, 0, sizeof *serverMgrData);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerManager_ProcessPacket --
 *
 *    Handles hgfs requests from a client not by our
 *    registered RPC callback.
 *
 * Results:
 *    TRUE on success, FALSE on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool HgfsServerManager_ProcessPacket(HgfsServerMgrData *mgrData,  // IN: hgfs mgr
                                     char const *packetIn,        // IN: rqst
                                     size_t packetInSize,         // IN: rqst size
                                     char *packetOut,             // OUT: rep
                                     size_t *packetOutSize)       // IN/OUT: rep buf/data size
{
   Debug("%s: Processing Packet for %s.\n", __FUNCTION__, mgrData->appName);
   /* Pass to the channel to handle processing and the server. */
   return HgfsChannelGuest_Receive(mgrData,
                                   packetIn,
                                   packetInSize,
                                   packetOut,
                                   packetOutSize);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerManager_Register --
 *
 *    Registers the hgfs server to be used in classic synchronous fashion.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    Hgfs packets sent to this channel will be handled.
 *
 *----------------------------------------------------------------------------
 */

Bool
HgfsServerManager_Register(HgfsServerMgrData *data)   // IN: RpcIn channel
{
   HgfsServerMgrCallbacks *serverMgrCallbacks = &gHgfsServerManagerGuestData.serverMgrCBTable;
   uint32 serverMgrRefCount;

   ASSERT(data);
   ASSERT(data->appName);

   Debug("%s: Register %s.\n", __FUNCTION__, data->appName);

   /*
    * Reference the global server manager data. Initialize only for the first
    * caller to register.
    */
   serverMgrRefCount = HgfsServerManagerGet(&gHgfsServerManagerGuestData);
   if (0 == serverMgrRefCount) {
      Debug("%s: calling policy init %s.\n", __FUNCTION__, data->appName);
      /*
       * Passing NULL here is safe because the shares maintained by the guest
       * policy server never change, eliminating the need for an invalidate
       * function.
       */
      if (!HgfsServerPolicy_Init(NULL,
                                 &serverMgrCallbacks->enumResources)) {
         HgfsServerManagerPut(&gHgfsServerManagerGuestData);
         return FALSE;
      }
   }

   /*
    * The channel will reference count itself, initializing once, but store the
    * channel in the manager data object passed to us and return it to the caller.
    */
   if (!HgfsChannelGuest_Init(data, serverMgrCallbacks)) {
      HgfsServerManagerPut(&gHgfsServerManagerGuestData);
      return FALSE;
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerManager_InvalidateInactiveSessions --
 *
 *    Sends a request to invalidate all the inactive HGFS server sessions.
 *
 * Results:
 *    Number of active sessions remaining inside the HGFS server.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

uint32
HgfsServerManager_InvalidateInactiveSessions(HgfsServerMgrData *mgrData)  // IN: RpcIn channel
{
   ASSERT(mgrData);

   Debug("%s: Invalidate Inactive Sessions for %s.\n", __FUNCTION__,
         mgrData->appName);
   return HgfsChannelGuest_InvalidateInactiveSessions(mgrData);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsServerManager_Unregister --
 *
 *    Cleans up the hgfs server.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsServerManager_Unregister(HgfsServerMgrData *data)         // IN: RpcIn channel

{
   ASSERT(data);
   ASSERT(data->appName != NULL);

   Debug("%s: Unregister %s.\n", __FUNCTION__, data->appName);

   HgfsChannelGuest_Exit(data);
   HgfsServerManagerPut(&gHgfsServerManagerGuestData);
}
  07070100000094000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerPolicyGuest  07070100000095000081A40000000000000000000000016822550500000415000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerPolicyGuest/Makefile.am  ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libHgfsServerPolicyGuest.la

libHgfsServerPolicyGuest_la_SOURCES =
libHgfsServerPolicyGuest_la_SOURCES += hgfsServerPolicyGuest.c
   07070100000096000081A40000000000000000000000016822550500004A58000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsServerPolicyGuest/hgfsServerPolicyGuest.c  /*********************************************************
 * Copyright (C) 1998-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsServerPolicyGuest.c --
 *
 *     Implementation of access policy for hgfs server running in a
 *     VM. All access is allowed.
 */

#ifdef sun
#   include <stdlib.h>
#   include <strings.h>
#elif defined(__FreeBSD__)
#   include <stdlib.h>
#endif

#undef LOG

#define LGLEVEL         (10)
#define LGPFX_FMT       "%s:%s:"
#define LGPFX           "hgfsd"

#if defined VMTOOLS_USE_GLIB
#define G_LOG_DOMAIN          LGPFX
#define Debug                 g_debug
#define Warning               g_warning
#else
#include "debug.h"
#endif

#define DOLOG(_min)     ((_min) <= LGLEVEL)

#define LOG(_level, args)                                  \
   do {                                                    \
      if (DOLOG(_level)) {                                 \
         Debug(LGPFX_FMT, LGPFX, __FUNCTION__);            \
         Debug args;                                       \
      }                                                    \
   } while (0)


#include "vmware.h"
#include "hgfsServerPolicy.h"


typedef struct HgfsServerPolicyState {
   /*
    * An empty list means that the policy server enforces the "deny all access
    * requests" policy --hpreg
    */
   DblLnkLst_Links shares;
} HgfsServerPolicyState;


static HgfsServerPolicyState myState;

static void *
HgfsServerPolicyEnumSharesInit(void);
static Bool
HgfsServerPolicyEnumSharesGet(void *data,
                              char const **name,
                              size_t *len,
                              Bool *done);
static Bool
HgfsServerPolicyEnumSharesExit(void *data);


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicyDestroyShare --
 *
 *    Destroy the internal representation of a share --hpreg
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerPolicyDestroyShare(HgfsSharedFolder *share) // IN
{
   ASSERT(share);

   free(share);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicyDestroyShares --
 *
 *    Destroy the internal representation of all shares. The function is
 *    idempotent --hpreg
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsServerPolicyDestroyShares(DblLnkLst_Links *head) // IN
{
   ASSERT(head);

   while (head->next != head) {
      HgfsSharedFolder *share;

      share = DblLnkLst_Container(head->next, HgfsSharedFolder, links);
      ASSERT(share);
      DblLnkLst_Unlink1(&share->links);
      HgfsServerPolicyDestroyShare(share);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicy_Init --
 *
 *    Initialize the HGFS security server state.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerPolicy_Init(HgfsInvalidateObjectsFunc invalidateObjects,  // Unused
                      HgfsServerResEnumCallbacks *enumResources)    // OUT enum callbacks
{
   HgfsSharedFolder *rootShare;

   /*
    * Currently these callbacks are not used, so make sure our caller doesn't pass
    * it in.
    */
   ASSERT(invalidateObjects == NULL);

   LOG(8, ("HgfsServerPolicy_Init: enter\n"));

   DblLnkLst_Init(&myState.shares);

   /* For the guest, we hard code a "root" share */
   rootShare = (HgfsSharedFolder *)malloc(sizeof *rootShare);
   if (!rootShare) {
      LOG(4, ("HgfsServerPolicy_Init: memory allocation failed\n"));
      return FALSE;
   }

   DblLnkLst_Init(&rootShare->links);

   /*
    * A path = "" has special meaning; it indicates that access is
    * granted to the root of the server filesystem, and in Win32
    * causes everything after the share name in the request to be
    * interpreted as either a drive letter or UNC name. [bac]
    */
   rootShare->path = "";
   rootShare->name = HGFS_SERVER_POLICY_ROOT_SHARE_NAME;
   rootShare->readAccess = TRUE;
   rootShare->writeAccess = TRUE;
   /* These are strictly optimizations to save work later */
   rootShare->pathLen = strlen(rootShare->path);
   rootShare->nameLen = strlen(rootShare->name);
   rootShare->handle = HGFS_INVALID_FOLDER_HANDLE;

   /* Add the root node to the end of the list */
   DblLnkLst_LinkLast(&myState.shares, &rootShare->links);

   /*
    * Fill the share enumeration callback table.
    */
   enumResources->init = HgfsServerPolicyEnumSharesInit;
   enumResources->get = HgfsServerPolicyEnumSharesGet;
   enumResources->exit = HgfsServerPolicyEnumSharesExit;

   LOG(8, ("HgfsServerPolicy_Init: exit\n"));
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicy_Cleanup --
 *
 *    Cleanup the HGFS security server state.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerPolicy_Cleanup(void)
{
   LOG(8, ("HgfsServerPolicy_Cleanup: enter\n"));
   HgfsServerPolicyDestroyShares(&myState.shares);

   LOG(8, ("HgfsServerPolicy_Cleanup: exit\n"));
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicyGetShare --
 *
 *    Get the share whose name matches the given name (if any).
 *
 * Results:
 *    The share, if a match is found.
 *    NULL otherwise
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsSharedFolder *
HgfsServerPolicyGetShare(HgfsServerPolicyState *state, // IN
                         char const *nameIn,           // IN: Name to check
                         size_t nameInLen)             // IN: Length of nameIn
{
   DblLnkLst_Links *l;

   ASSERT(state);
   ASSERT(nameIn);

   /*
    * First try to find a share that matches the given name exactly.
    * This is to handle the case where 2 share names differ in case only.
    */

   for (l = state->shares.next; l != &state->shares; l = l->next) {
      HgfsSharedFolder *share;

      share = DblLnkLst_Container(l, HgfsSharedFolder, links);
      ASSERT(share);
      if (nameInLen == share->nameLen &&
          !memcmp(nameIn, share->name, nameInLen)) {
         return share;
      }
   }

   /*
    * There was no match. As a fall back try a case insensitive match.
    * This is because some Windows applications uppercase or lowercase the
    * entire path before sending the request.
    */

   for (l = state->shares.next; l != &state->shares; l = l->next) {
      HgfsSharedFolder *share;
      char *tempName;

      /*
       * Null terminate the input name before a case insensitive comparison.
       * This is just to protect against bad implementations of strnicmp.
       */

      if (!(tempName = (char *)malloc(nameInLen + 1))) {
         LOG(4, ("HgfsServerPolicyGetShare: couldn't allocate tempName\n"));
         return NULL;
      }

      memcpy(tempName, nameIn, nameInLen);
      tempName[nameInLen] = 0;

      share = DblLnkLst_Container(l, HgfsSharedFolder, links);
      ASSERT(share);
      if (nameInLen == share->nameLen &&
#ifdef _WIN32
          !strnicmp(tempName, share->name, nameInLen)) {
#else
          !strncasecmp(tempName, share->name, nameInLen)) {
#endif
         free(tempName);
         return share;
      }

      free(tempName);
   }

   return NULL;
}


/* State used by HgfsServerPolicyEnumSharesGet and friends */
typedef struct State {
   DblLnkLst_Links *next;
} GetSharesState;


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicyEnumSharesInit --
 *
 *    Setup state for HgfsServerPolicyEnumSharesGet
 *
 * Results:
 *    Pointer to state on success.
 *    NULL on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void *
HgfsServerPolicyEnumSharesInit(void)
{
   GetSharesState *that;

   that = malloc(sizeof *that);
   if (!that) {
      LOG(4, ("HgfsServerPolicyEnumSharesInit: couldn't allocate state\n"));
      return NULL;
   }

   that->next = myState.shares.next;
   return that;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicyEnumSharesGet --
 *
 *    Enumerate share names one at a time.
 *
 *    When finished, sets "done" to TRUE.
 *
 *    Should be called with the results obtained by calling
 *    HgfsServerPolicyEnumSharesInit.
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure (never happens).
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsServerPolicyEnumSharesGet(void *data,        // IN:  Callback data
                              char const **name, // OUT: Share name
                              size_t *len,       // OUT: Name length
                              Bool *done)        // OUT: Completion status
{
   GetSharesState *that;
   HgfsSharedFolder *share;

   that = (GetSharesState *)data;
   ASSERT(that);
   ASSERT(name);
   ASSERT(len);
   ASSERT(done);

   if (that->next == &myState.shares) {
      /* No more shares */
      *done = TRUE;
      return TRUE;
   }

   share = DblLnkLst_Container(that->next, HgfsSharedFolder, links);
   ASSERT(share);
   that->next = share->links.next;
   *name = share->name;
   *len = share->nameLen;
   LOG(4, ("HgfsServerPolicyEnumSharesGet: Share name is \"%s\"\n",
           *name));
   *done = FALSE;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicyEnumSharesExit --
 *
 *    Cleanup state from HgfsServerPolicyEnumSharesGet
 *
 * Results:
 *    TRUE on success.
 *    FALSE on failure (never happens).
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HgfsServerPolicyEnumSharesExit(void *data) // IN: Callback data
{
   GetSharesState *that;

   that = (GetSharesState *)data;
   ASSERT(that);
   free(that);
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicy_GetSharePath --
 *
 *    Get the local path for a share name by looking at the requested
 *    name, finding the matching share (if any), checking access
 *    permissions, and returning the share's local path.
 *
 * Results:
 *    An HgfsNameStatus value indicating the result is returned.
 *
 *    The local path for the shareName is also returned if a match is found and
 *    access is permitted.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsNameStatus
HgfsServerPolicy_GetSharePath(char const *nameIn,        // IN: Name to check
                              size_t nameInLen,          // IN: Length of nameIn
                              HgfsOpenMode mode,         // IN: Requested access mode
                              size_t *sharePathLen,      // OUT: Length of share path
                              char const **sharePath)    // OUT: Share path
{
   HgfsSharedFolder *myShare;

   ASSERT(nameIn);
   ASSERT(sharePathLen);
   ASSERT(sharePath);

   myShare = HgfsServerPolicyGetShare(&myState, nameIn, nameInLen);
   if (!myShare) {
      LOG(4, ("HgfsServerPolicy_GetSharePath: No matching share name\n"));
      return HGFS_NAME_STATUS_DOES_NOT_EXIST;
   }

   /*
    * See if access is allowed in the requested mode.
    *
    * XXX Yeah, this is retarded. We should be using bits instead of
    * an enum for HgfsOpenMode. Add it to the todo list. [bac]
    */
   switch (HGFS_OPEN_MODE_ACCMODE(mode)) {
   case HGFS_OPEN_MODE_READ_ONLY:
      if (!myShare->readAccess) {
         LOG(4, ("HgfsServerPolicy_GetSharePath: Read access denied\n"));
         return HGFS_NAME_STATUS_ACCESS_DENIED;
      }
      break;

   case HGFS_OPEN_MODE_WRITE_ONLY:
      if (!myShare->writeAccess) {
         LOG(4, ("HgfsServerPolicy_GetSharePath: Write access denied\n"));
         return HGFS_NAME_STATUS_ACCESS_DENIED;
      }
      break;

   case HGFS_OPEN_MODE_READ_WRITE:
      if (!myShare->readAccess || !myShare->writeAccess) {
         LOG(4, ("HgfsServerPolicy_GetSharePath: Read/write access denied\n"));
         return HGFS_NAME_STATUS_ACCESS_DENIED;
      }
      break;

   default:
      LOG(0, ("HgfsServerPolicy_GetSharePath: Invalid mode\n"));
      return HGFS_NAME_STATUS_FAILURE;
      break;
   }

   *sharePathLen = myShare->pathLen;
   *sharePath = myShare->path;
   return HGFS_NAME_STATUS_COMPLETE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicy_ProcessCPName --
 *
 *    Get the local path for a share name by looking at the requested
 *    name, finding the matching share (if any) and returning the share's
 *    local path local path and permissions.
 *
 * Results:
 *    An HgfsNameStatus value indicating the result is returned.
 *
 *    The local path for the shareName is also returned if a match is found.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsNameStatus
HgfsServerPolicy_ProcessCPName(char const *nameIn,            // IN: name in CPName form
                               size_t nameInLen,              // IN: length of the name
                               Bool *readAccess,              // OUT: Read permissions
                               Bool *writeAccess,             // OUT: Write permissions
                               HgfsSharedFolderHandle *handle,// OUT: folder handle
                               char const **shareBaseDir)     // OUT: Shared directory
{
   HgfsSharedFolder *myShare;

   ASSERT(nameIn);
   ASSERT(shareBaseDir);

   myShare = HgfsServerPolicyGetShare(&myState, nameIn, nameInLen);
   if (!myShare) {
      LOG(4, ("%s: No matching share name\n", __FUNCTION__));
      return HGFS_NAME_STATUS_DOES_NOT_EXIST;
   }

   *readAccess = myShare->readAccess;
   *writeAccess = myShare->writeAccess;
   *shareBaseDir = myShare->path;
   *handle = myShare->handle;
   return HGFS_NAME_STATUS_COMPLETE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicy_GetShareOptions --
 *
 *    Get the HGFS share config options by looking at the requested name,
 *    finding the matching share (if any).
 *
 * Results:
 *    HGFS_NAME_STATUS_COMPLETE on success, and HGFS_NAME_STATUS_DOES_NOT_EXIST
 *    if no matching share.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsNameStatus
HgfsServerPolicy_GetShareOptions(char const *nameIn,             // IN: Share name
                                 size_t nameInLen,               // IN: Share name length
                                 HgfsShareOptions *configOptions)// OUT: Config options
{
   HgfsSharedFolder *share;
   char const *inEnd;
   char *next;
   int len;

   ASSERT(nameIn);
   ASSERT(configOptions);

   inEnd = nameIn + nameInLen;
   len = CPName_GetComponent(nameIn, inEnd, (char const **) &next);
   if (len < 0) {
      LOG(4, ("HgfsServerPolicy_GetShareOptions: get first component failed\n"));
      return HGFS_NAME_STATUS_FAILURE;
   }

   share = HgfsServerPolicyGetShare(&myState, nameIn, len);
   if (!share) {
      LOG(4, ("HgfsServerPolicy_GetShareOptions: No matching share name.\n"));
      return HGFS_NAME_STATUS_DOES_NOT_EXIST;
   }
   *configOptions = share->configOptions;
   return HGFS_NAME_STATUS_COMPLETE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicy_IsShareOptionSet --
 *
 *    Check if the specified config option is set.
 *
 * Results:
 *    TRUE if set.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HgfsServerPolicy_IsShareOptionSet(HgfsShareOptions configOptions, // IN: config options
                                  uint32 option)                  // IN: option to check
{
   return (configOptions & option) == option;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsServerPolicy_GetShareMode --
 *
 *    Get the access mode for a share by looking at the requested
 *    name, finding the matching share (if any), and returning
 *    the share's access mode.
 *
 * Results:
 *    An HgfsNameStatus value indicating the result is returned.
 *
 *    The access mode for the shareName is also returned if a match is found.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

HgfsNameStatus
HgfsServerPolicy_GetShareMode(char const *nameIn,        // IN: Share name to retrieve
                              size_t nameInLen,          // IN: Length of Share name
                              HgfsOpenMode *mode)        // OUT: Share's access mode
{
   HgfsSharedFolder *share;

   ASSERT(nameIn);
   ASSERT(mode);

   share = HgfsServerPolicyGetShare(&myState, nameIn, nameInLen);
   if (!share) {
      LOG(4, ("HgfsServerPolicy_GetShareMode: No matching share name\n"));
      return HGFS_NAME_STATUS_DOES_NOT_EXIST;
   }

   /*
    * Get the access mode.
    */
   if (share->readAccess && share->writeAccess) {
      *mode = HGFS_OPEN_MODE_READ_WRITE;
   } else if (share->readAccess) {
      *mode = HGFS_OPEN_MODE_READ_ONLY;
   } else if (share->writeAccess) {
      *mode = HGFS_OPEN_MODE_WRITE_ONLY;
   } else {
      /* Share should be at least read or write access. */
      ASSERT(FALSE);
      LOG(4, ("HgfsServerPolicy_GetShareMode: Invalid access mode\n"));
      return HGFS_NAME_STATUS_FAILURE;
   }

   return HGFS_NAME_STATUS_COMPLETE;
}
07070100000097000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsUri    07070100000098000081A4000000000000000000000001682255050000042E000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsUri/Makefile.am    ################################################################################
### Copyright (C) 2015-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libHgfsUri.la

libHgfsUri_la_SOURCES =
libHgfsUri_la_SOURCES += hgfsUriPosix.c

AM_CFLAGS =
AM_CFLAGS += -DVMTOOLS_USE_GLIB
AM_CFLAGS += @GLIB2_CPPFLAGS@

  07070100000099000081A40000000000000000000000016822550500000E75000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/hgfsUri/hgfsUriPosix.c /*********************************************************
 * Copyright (C) 2015-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsPosix.c --
 *
 *    Provides a library for guest applications to convert local pathames to
 *    x-vmware-share:// style URIs
 */

#if !defined __linux__ && !defined __APPLE__ && !defined __FreeBSD__
#   error This file should not be compiled
#endif

#include "vmware.h"
#include "debug.h"
#include "str.h"
#include <glib.h>

#include "hgfsUri.h"
#include "hgfsHelper.h"

#include "util.h"
#include "unicode.h"
#include "hgfsEscape.h"

#include "ghIntegrationCommon.h"    // For GHI_HGFS_SHARE_URL_UTF8


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUri_ConvertFromPathToHgfsUri --
 *
 *    Test the UTF8 pathname to see if it's on an HGFS Share. If it is
 *    construct a UTF8 URI in the form of x-vmware-share://share_name/item.txt.
 *    If not, convert to a regular UTF8 URI string.
 *
 * Results:
 *    Filename as UTF8 URI string if success, NULL if failed.
 *
 * Side effects:
 *    Memory may be allocated for the returned string.
 *
 *-----------------------------------------------------------------------------
 */

char *
HgfsUri_ConvertFromPathToHgfsUri(const char *pathName,  // IN: path to convert
                                 Bool hgfsOnly)         // IN
{
   char *shareUri = NULL;
   Bool isHgfsName = FALSE;
   char *sharesDefaultRootPath = NULL;

   /* We can only operate on full paths. */
   if (pathName[0] != DIRSEPC) {
      return shareUri;
   }

   /* Retrieve the servername & share name in use. */
   if (!HgfsHlpr_QuerySharesDefaultRootPath(&sharesDefaultRootPath)) {
      Debug("%s: Unable to query shares default root path\n", __FUNCTION__);
      goto exit;
   }

   if (Unicode_StartsWith(pathName, sharesDefaultRootPath)) {
      char *relativeSharePath = NULL;
      char *escapedSharePath = NULL;
      UnicodeIndex relativePathStart = strlen(sharesDefaultRootPath);
      if (   strlen(pathName) > relativePathStart
          && pathName[relativePathStart] == DIRSEPC) {
         relativePathStart++;
      }
      relativeSharePath = Unicode_RemoveRange(pathName, 0, relativePathStart);
      HgfsEscape_Undo(relativeSharePath, strlen(relativeSharePath) + 1);
      escapedSharePath = g_uri_escape_string(relativeSharePath, "/", FALSE);
      shareUri = Unicode_Append(GHI_HGFS_SHARE_URL_UTF8, escapedSharePath);
      g_free(escapedSharePath);
      free(relativeSharePath);
      isHgfsName = TRUE;
   }

exit:
   if (!isHgfsName && !hgfsOnly) {
      /* Only convert non-hgfs file name if hgfsOnly is not set. */
      char *escapedPath = g_uri_escape_string(pathName, "/", FALSE);
      shareUri = Str_Asprintf(NULL,
                              "file://%s",
                               escapedPath);
      g_free(escapedPath);
   }
   HgfsHlpr_FreeSharesRootPath(sharesDefaultRootPath);
   return shareUri;
}
   0707010000009A000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/impersonate    0707010000009B000081A40000000000000000000000016822550500000445000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/impersonate/Makefile.am    ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libImpersonate.la

libImpersonate_la_SOURCES =
libImpersonate_la_SOURCES += impersonate.c
libImpersonate_la_SOURCES += impersonatePosix.c

AM_CFLAGS = @LIB_IMPERSONATE_CPPFLAGS@
   0707010000009C000081A40000000000000000000000016822550500002744000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/impersonate/impersonate.c  /*********************************************************
 * Copyright (C) 2003-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * impersonate.c --
 *
 * Description:
 *      Code to impersonate as a user when running under a privileged account. 
 *      Nested impersonation is not supported.
 */

#include <string.h>

#include "vmware.h"
#include "auth.h"
#include "userlock.h"
#include "mutexRankLib.h"
#include "impersonateInt.h"

static Atomic_Ptr impersonateLockStorage;

Bool impersonationEnabled = FALSE;


/*
 *----------------------------------------------------------------------
 *
 * ImpersonateGetLock --
 *
 *      Get/create the impersonate lock.
 *
 * Results:
 *      See above.
 *
 * Side effects:
 *      See above.
 *
 *----------------------------------------------------------------------
 */

static INLINE MXUserRecLock *
ImpersonateGetLock(void)
{
   MXUserRecLock *lock = MXUser_CreateSingletonRecLock(&impersonateLockStorage,
                                                       "impersonateLock",
                                                       RANK_impersonateLock);
   return lock;
}


/*
 *----------------------------------------------------------------------
 *
 * ImpersonateLock --
 *
 *      Acquire or release the impersonate lock. Protects access to
 *      the library's static and TLS states.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
ImpersonateLock(Bool lock) // IN
{
   MXUserRecLock *impersonateLock = ImpersonateGetLock();

   if (lock) {
      MXUser_AcquireRecLock(impersonateLock);
   } else {
      MXUser_ReleaseRecLock(impersonateLock);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * Impersonate_Init --
 *
 *      Initialize the impersonation module. On windows also load
 *      userenv.dll.
 *      Without calling this, code calling into this module will 
 *      essentially be noops.
 *
 *      Call when single-threaded.
 *
 * Side effects:
 *
 *	Loads the library. We keep the library loaded thereafter.
 *
 *----------------------------------------------------------------------
 */

void
Impersonate_Init(void)
{
   if (!impersonationEnabled) {
      ImpersonateInit();
      impersonationEnabled = TRUE;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * Impersonate_Runas --
 *
 *      Impersonate as the appropriate runas user. In linux this is always
 *      the config file owner regardless the calling context. In windows, the
 *      runas user is the caller passed into the method, except when the VM has
 *      a preconfigured runas user, in which case we will impersonate using his
 *      credentials instead.
 *
 *      In windows, if caller is not set, fail if preconfigured runas user is
 *      not found.
 *
 * Results:  
 *      TRUE if impersonation succeeds, FALSE otherwise.
 *
 * Side effects:
 *      imp.impersonatedUser may be updated.
 * 
 *----------------------------------------------------------------------------
 */

Bool
Impersonate_Runas(const char *cfg,           // IN
                  const char *caller,        // IN
                  AuthToken callerToken)     // IN
{
   Bool res;

   if (!impersonationEnabled) {
      return TRUE;
   }

   ImpersonateLock(TRUE);
   res = ImpersonateRunas(cfg, caller, callerToken);
   ImpersonateLock(FALSE);

   return res;
}


/*
 *----------------------------------------------------------------------------
 *
 * Impersonate_Owner --
 *
 *      Impersonate as the owner of the specified file.
 *
 * Results:  
 *      TRUE if impersonation succeeds, FALSE otherwise.
 *
 * Side effects:
 *      imp.impersonatedUser may be updated.
 * 
 *----------------------------------------------------------------------------
 */

Bool
Impersonate_Owner(const char *file)        // IN
{
   Bool res;

   if (!impersonationEnabled) {
      return TRUE;
   }

   ImpersonateLock(TRUE);
   res = ImpersonateOwner(file);
   ImpersonateLock(FALSE);

   return res;
}


/*
 *----------------------------------------------------------------------------
 *
 * Impersonate_Do --
 *
 *      Impersonate as user. Can be nested if impersonated as that same user 
 *      each time. Can switch back to root temporarily regardless of nesting
 *      level via Impersonate_ForceRoot. Calling Impersonate_UnforceRoot will
 *      return to original impersonation at the same nesting level.
 *
 * Results:  
 *      TRUE if impersonation succeeds, FALSE otherwise.
 *
 * Side effects:
 *      imp.impersonatedUser may be updated.
 *      imp.impersonatedToken(Win32 only) may be updated.
 * 
 *----------------------------------------------------------------------------
 */

Bool
Impersonate_Do(const char *user,             // IN 
               AuthToken token)              // IN
{
   Bool res;

   if (!impersonationEnabled) {
      return TRUE;
   }

   ImpersonateLock(TRUE);
   res = ImpersonateDo(user, token);
   ImpersonateLock(FALSE);

   return res;
}


/*
 *----------------------------------------------------------------------------
 *
 * Impersonate_Undo --
 *
 *      Undoes a previous impersonation. When we undo the last in the nesting
 *      of impersonation ops, switch back to root.
 *
 * Results:  
 *      TRUE on success, FALSE otherwise
 *
 * Side effects:
 *      On reverting back to root,
 *      imp.impersonatedUser is freed.
 *      imp.impersonatedToken (win32) is invalid.
 * 
 *----------------------------------------------------------------------------
 */

Bool
Impersonate_Undo(void)
{
   Bool res;
   ImpersonationState *imp = NULL;

   if (!impersonationEnabled) {
      return TRUE;
   }

   ImpersonateLock(TRUE);
   imp = ImpersonateGetTLS();
   ASSERT(imp);

   WIN32_ONLY(ASSERT(!imp->forceRoot));
   imp->refCount--;

   POSIX_ONLY(IMPWARN(("Impersonate_Undo (%x %x) drop refcount to %d\n", 
                       getpid(), imp, imp->refCount)));
   WIN32_ONLY(IMPWARN(("Impersonate_Undo (%x) drop refcount to %d\n", 
                       (int) imp, imp->refCount)));

   if (imp->refCount > 0) {
      ImpersonateLock(FALSE);
      return TRUE;
   }

   res = ImpersonateUndo();
   ImpersonateLock(FALSE);

   return res;
}


/*
 *----------------------------------------------------------------------------
 *
 * Impersonate_Who --
 *
 *      Returns currently impersonated user name. If not impersonated,
 *      returns NULL.
 *
 * Results:  
 *      Currently impersonated user name. NULL if not impersonated.
 *
 * Side effects:
 *      None.
 * 
 *----------------------------------------------------------------------------
 */

char *
Impersonate_Who(void)
{
   char *impUser;
   ImpersonationState *imp = NULL;

   if (!impersonationEnabled) {
      return strdup("");
   }

   ImpersonateLock(TRUE);
   imp = ImpersonateGetTLS();
   ASSERT(imp);

   impUser = strdup(imp->impersonatedUser);
   VERIFY(impUser);
   ImpersonateLock(FALSE);

   return impUser;
}


/*
 *----------------------------------------------------------------------------
 *
 * Impersonate_ForceRoot --
 *
 *      Go back to base impersonate level (LocalSystem/root) for a brief period
 *      of time. Doesnt do anything on linux.
 *      Should only be used when already impersonated. This call is not nestable.     
 *      No other impersonation is permitted before calling Impersonate_UnforceRoot.
 *
 * Results:  
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      imp.forceRoot is set to TRUE on success.
 * 
 *----------------------------------------------------------------------------
 */

Bool 
Impersonate_ForceRoot(void) 
{
   Bool res;

   if (!impersonationEnabled) {
      return TRUE;
   }

   ImpersonateLock(TRUE);
   res = ImpersonateForceRoot();
   ImpersonateLock(FALSE);

   return res;
}


/*
 *----------------------------------------------------------------------------
 *
 * Impersonate_UnforceRoot --
 *
 *      Go back to impersonate the user that we switched to root from.
 *      See Impersonate_ForceRoot.
 *
 * Results:  
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      imp.forceRoot is set to FALSE on success.
 * 
 *----------------------------------------------------------------------------
 */

Bool 
Impersonate_UnforceRoot(void) 
{
   Bool res;

   if (!impersonationEnabled) {
      return TRUE;
   }

   ImpersonateLock(TRUE);
   res = ImpersonateUnforceRoot();
   ImpersonateLock(FALSE);

   return res;
}


#ifdef _WIN32
/*
 *----------------------------------------------------------------------------
 *
 * Impersonate_CfgRunasOnly --
 *
 *      Impersonate as the preconfigured runas user for the VM. 
 *      Fails if runas user credentials are not found.
 *
 * Results:  
 *      TRUE if preconfigured runas user is found impersonation succeeds,
 *      FALSE otherwise.
 *
 * Side effects:
 *      imp.impersonatedUser may be updated.
 * 
 *----------------------------------------------------------------------------
 */

Bool
Impersonate_CfgRunasOnly(const char *cfg)        // IN
{
   Bool res;

   ImpersonateLock(TRUE);
   res = Impersonate_Runas(cfg, NULL, NULL);
   ImpersonateLock(FALSE);
   
   return res;
}
#endif //_WIN32
0707010000009D000081A400000000000000000000000168225505000007F4000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/impersonate/impersonateInt.h   /*********************************************************
 * Copyright (C) 2003-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * impersonateInt.h --
 *
 *	Header file shared by impersonate code
 */

#ifndef _IMPERSONATE_INT_H_
#define _IMPERSONATE_INT_H_

#include "vmware.h"
#include "msg.h"
#include "impersonate.h"
#include "auth.h"

//#define IMP_VERBOSE 1
#define INVALID_PTHREAD_KEY_VALUE (-1)
#ifdef IMP_VERBOSE
#define IMPWARN(x) Warning x
#else
#define IMPWARN(x)
#endif

typedef struct ImpersonationState {
   const char *impersonatedUser;       // the user we are currently impersonating
   int refCount;                       // # of times we are impersonating as same user

#ifdef _WIN32
   HANDLE impersonatedToken;           // the access token currently impersonated with
   Bool forceRoot;                     // are we temporarily switching back to root?
#endif
} ImpersonationState;

void ImpersonateInit(void);
ImpersonationState *ImpersonateGetTLS(void);
Bool ImpersonateRunas(const char *cfg, const char *caller, AuthToken callerToken);
Bool ImpersonateOwner(const char *file);
Bool ImpersonateDo(const char *user, AuthToken token);
Bool ImpersonateUndo(void);
Bool ImpersonateForceRoot(void);
Bool ImpersonateUnforceRoot(void);

#endif // ImpersonateInt.h
0707010000009E000081A40000000000000000000000016822550500002E7C000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/lib/impersonate/impersonatePosix.c /*********************************************************
 * Copyright (C) 2003-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * impersonatePosix.c --
 *
 * Description:
 *      Posix specific functions to impersonate as specific users.
 */

#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#if !defined(VMX86_TOOLS)
#include <pthread.h>
#endif
#include <stdio.h>

#include "impersonateInt.h"
#include "su.h"
#include "posix.h"

#if !defined(VMX86_TOOLS)
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; 
static pthread_key_t threadLocalStorageKey = INVALID_PTHREAD_KEY_VALUE;
static void ThreadLocalFree(void *ptr);
#else
static ImpersonationState *impLinux = NULL;
#endif

static Bool ImpersonateDoPosix(struct passwd *pwd);

/*
 *----------------------------------------------------------------------------
 *
 * ImpersonateInit --
 *
 *      Linux specific initialization (thread local storage for linux)
 *
 * Results:  
 *      None.
 *
 * Side effects:
 *      Memory created.
 * 
 *----------------------------------------------------------------------------
 */

void
ImpersonateInit(void)
{
#if !defined(VMX86_TOOLS)
   int status;

   status = pthread_key_create(&threadLocalStorageKey, ThreadLocalFree);
   if (status != 0) {
      Warning("Impersonate: key_create failed: %d\n", status);
      VERIFY(status == 0);
      return;
   }
   VERIFY(threadLocalStorageKey != INVALID_PTHREAD_KEY_VALUE);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * ThreadLocalFree --
 *
 *      A wrapper for "free()".  This function is called when a thread
 *      terminates so that the thread-local state can be deallocated.
 *
 *----------------------------------------------------------------------
 */
#if !defined(VMX86_TOOLS)
static void
ThreadLocalFree(void *ptr)
{
   ImpersonationState *imp = (ImpersonationState*) ptr;

   IMPWARN(("Impersonate: ThreadLocalFree(0x%08x)\n", imp));

   ASSERT(imp);
   ASSERT(imp->impersonatedUser == NULL);
   ASSERT(imp->refCount == 0);

   free(imp);
}
#endif

/*
 *----------------------------------------------------------------------------
 *
 * ImpersonateGetTLS --
 *
 *      This function abstracts away the differences between Linux and 
 *      Windows for obtaining a pointer to thread-local state.
 *
 * Results:  
 *      Returns pointer to thread-local state.
 *
 * Side effects:
 *      On Linux this function will allocate state on the first occasion
 *      that a particular thread calls this function for the first time.
 * 
 *----------------------------------------------------------------------------
 */

ImpersonationState *
ImpersonateGetTLS(void)
{
   ImpersonationState *ptr = NULL;
   int status;

   /* If a prior call has already allocated state, then use it */
#if !defined(VMX86_TOOLS)
   /* If a prior call has already allocated state, then use it */
   ptr = pthread_getspecific(threadLocalStorageKey);
#else
	ptr = impLinux;
#endif
   if (ptr != NULL) {
      return ptr;
   }

   /* No state allocated, so we need to allocate it */
   ptr = calloc(1, sizeof *ptr);
   VERIFY(ptr);
#if !defined(VMX86_TOOLS)
   status = pthread_setspecific(threadLocalStorageKey, ptr);
#else
	impLinux = ptr;
	status = 0;
#endif
   if (status != 0) {
      Warning("Impersonate: setspecific: %d\n", status);
      VERIFY(status == 0);
   }

   return ptr;
}

/*
 *----------------------------------------------------------------------------
 *
 * ImpersonateRunas --
 *
 *      Impersonate as the appropriate runas user. In linux this is always
 *      the config file owner regardless the calling context. 
 *
 * Results:  
 *      TRUE if impersonation succeeds, FALSE otherwise.
 *
 * Side effects:
 *      imp.impersonatedUser may be updated.
 * 
 *----------------------------------------------------------------------------
 */

Bool
ImpersonateRunas(const char *cfg,        // IN
                 const char *caller,     // IN
                 AuthToken callerToken)  // IN
{
   /*
    * In linux, this call always impersonates as the owner of the config file.
    */

   ASSERT(!caller && !callerToken);
   return ImpersonateOwner(cfg);
}


/*
 *----------------------------------------------------------------------------
 *
 * ImpersonateOwner --
 *
 *      Impersonate the owner of the config file. Only makes sense on linux.
 *
 * Results:  
 *      TRUE if impersonation succeeds, false otherwise.
 *
 * Side effects:
 *      imp.impersonatedUser may be updated.
 * 
 *----------------------------------------------------------------------------
 */

Bool 
ImpersonateOwner(const char *file)          // IN
{
   struct stat buf;
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   int error;

   if (Posix_Stat(file, &buf) == -1) {
      Warning("Failed to lookup owner for: %s. Reason: %s\n", file, 
              Err_Errno2String(errno));
      return FALSE;
   }

   if ((error = Posix_Getpwuid_r(buf.st_uid, &pw, buffer, BUFSIZ, &ppw)) != 0 || !ppw) {
      if (error == 0) {
         error = ENOENT;
      }
      Warning("Failed to lookup user with uid: %" FMTUID ". Reason: %s\n", buf.st_uid,
              Err_Errno2String(error));
      return FALSE;
   }
   
   return ImpersonateDoPosix(ppw);
}


/*
 *----------------------------------------------------------------------
 *
 * ImpersonateUndo -- Linux specific
 *
 *      Change back into the superuser 
 *
 * Side effects:
 *
 *	EUID is set back to the superuser, and environment variables are
 *	updated back.
 *
 *----------------------------------------------------------------------
 */

Bool
ImpersonateUndo(void)
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   ImpersonationState *imp = NULL;
   int ret;
   int error;

#if !defined(VMX86_TOOLS)
   pthread_mutex_lock(&mut);
#endif

   imp = ImpersonateGetTLS();
   ASSERT(imp);
   //ASSERT(imp->impersonatedUser);
   
   if ((error = Posix_Getpwuid_r(0, &pw, buffer, BUFSIZ, &ppw)) != 0 || !ppw) {
      if (error == 0) {
         error = ENOENT;
      }
      ret = error;
      Warning("Failed to get password entry for uid 0: %s\n",
              Err_Errno2String(error));
      goto exit;
   }

#if __APPLE__
   NOT_IMPLEMENTED();
#else
   /* Return to root */
   ret = Id_SetEUid(ppw->pw_uid);
   if (ret < 0) {
      goto exit;
   }
#endif

   ret = Id_SetGid(ppw->pw_gid);
   if (ret < 0) {
      goto exit;
   }

   /* 
    * The call to initgroups leaks memory in versions of glibc earlier than 2.1.93.
    * See bug 10042. -jhu 
    */

   ret = initgroups(ppw->pw_name, ppw->pw_gid);
   if (ret < 0) {
      goto exit;
   }

   /* Restore root's environment */
   Posix_Setenv("USER", ppw->pw_name, 1);
   Posix_Setenv("HOME", ppw->pw_dir, 1);
   Posix_Setenv("SHELL", ppw->pw_shell, 1);

   free((char *)imp->impersonatedUser);
   imp->impersonatedUser = NULL;
   ret = 0;

exit:
   VERIFY(ret == 0);
#if !defined(VMX86_TOOLS)
   pthread_mutex_unlock(&mut);
#endif
   return (ret ? FALSE : TRUE);
}


/*
 *----------------------------------------------------------------------------
 *
 * ImpersonateDoPosix --
 *
 *      Impersonate as the user corresponding to the passwd entry
 *      XXX: Mostly copied from vmsd_impersonate.c
 *
 * Results:  
 *      TRUE if impersonation succeeds, FALSE otherwise.
 *
 * Side effects:
 *      imp.impersonatedUser is updated.
 * 
 *----------------------------------------------------------------------------
 */

Bool
ImpersonateDoPosix(struct passwd *pwd)            // IN
{
   int ret = 0;
   ImpersonationState *imp = NULL;

#if !defined(VMX86_TOOLS)
   pthread_mutex_lock(&mut);
#endif

   imp = ImpersonateGetTLS();
   ASSERT(imp);

   if (pwd->pw_uid == geteuid()) {
      imp->refCount++;
      IMPWARN(("ImpersonateDoPosix (%s : %x : %x) refcount = %d\n",
               imp->impersonatedUser, getpid(), imp, imp->refCount));
      goto unlock;
   }

   ASSERT(getuid() == 0);
   VERIFY(geteuid() == 0);

   ret = Id_SetGid(pwd->pw_gid);
   if (ret < 0) {
      goto exit;
   }
   
   /* 
    * The call to initgroups leaks memory in versions of glibc earlier than
    * 2.1.93.See bug 10042. -jhu 
    */
   
   ret = initgroups(pwd->pw_name, pwd->pw_gid);
   if (ret < 0) {
      goto exit;
   }

#if __APPLE__
   NOT_IMPLEMENTED();
#else
   ret = Id_SetEUid(pwd->pw_uid);
   if (ret < 0) {
      goto exit;
   }
#endif

   /* Setup the user's environment */
   Posix_Setenv("USER", pwd->pw_name, 1);
   Posix_Setenv("HOME", pwd->pw_dir, 1);
   Posix_Setenv("SHELL", pwd->pw_shell, 1);

   imp->impersonatedUser = strdup(pwd->pw_name);
   VERIFY(imp->impersonatedUser);

exit:
   imp->refCount = 1;
   VERIFY(ret == 0);
unlock:
#if !defined(VMX86_TOOLS)
   pthread_mutex_unlock(&mut);
#endif
   return (ret ? FALSE : TRUE);
}


/*
 *----------------------------------------------------------------------------
 *
 * ImpersonateDo --
 *
 *      Impersonate as user. Can be nested if impersonated as that same user 
 *      each time. Can switch back to root temporarily regardless of nesting
 *      level via Impersonate_ForceRoot. Calling Impersonate_UnforceRoot will
 *      return to original impersonation at the same nesting level.
 *
 * Results:  
 *      TRUE if impersonation succeeds, FALSE otherwise.
  *
 * Side effects:
 *      imp.impersonatedUser may be updated.
 * 
 *----------------------------------------------------------------------------
 */

Bool 
ImpersonateDo(const char *user,       // IN
              AuthToken token)        // IN
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   int error;

   if ((error = Posix_Getpwnam_r(user, &pw, buffer, BUFSIZ, &ppw)) != 0 || !ppw) {
      if (error == 0) {
         error = ENOENT;
      }
      Warning("Failed to get password entry for : %s. Reason: %s\n", user,
              Err_Errno2String(error));
      return FALSE;
   }

   return ImpersonateDoPosix(ppw);
}


/*
 *----------------------------------------------------------------------------
 *
 * ImpersonateForceRoot --
 *
 *      Go back to base impersonate level (LocalSystem/root) for a brief
 *      period of time.
 *      Should only be used when already impersonated. This call is not nestable.     
 *      No other impersonation is permitted before calling Impersonate_UnforceRoot.
 *
 * Results:  
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      imp.forceRoot is set to TRUE on success.
 * 
 *----------------------------------------------------------------------------
 */

Bool
ImpersonateForceRoot(void)
{
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * ImpersonateUnforceRoot --
 *
 *      Unforce from root to original impersonation context
 *
 * Results:  
 *      TRUE on success, FALSE otherwise.
 *
 * Side effects:
 *      imp.forceRoot is set to FALSE on success
 * 
 *----------------------------------------------------------------------------
 */

Bool 
ImpersonateUnforceRoot(void)
{ 
   return TRUE;
}
0707010000009F000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include    070701000000A0000081A40000000000000000000000016822550500008617000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/asyncsocket.h  /*********************************************************
 * Copyright (C) 2003-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef __ASYNC_SOCKET_H__
#define __ASYNC_SOCKET_H__

/*
 * asyncsocket.h --
 *
 *      The AsyncSocket object is a fairly simple wrapper around a basic TCP
 *      socket. It's potentially asynchronous for both read and write
 *      operations. Reads are "requested" by registering a receive function
 *      that is called once the requested amount of data has been read from
 *      the socket. Similarly, writes are queued along with a send function
 *      that is called once the data has been written. Errors are reported via
 *      a separate callback.
 */

#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_USERLEVEL

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#endif

#include "includeCheck.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Error codes
 */
#define ASOCKERR_SUCCESS           0
#define ASOCKERR_GENERIC           1
#define ASOCKERR_TIMEOUT           2
#define ASOCKERR_NOTCONNECTED      3
#define ASOCKERR_REMOTE_DISCONNECT 4
#define ASOCKERR_INVAL             5
#define ASOCKERR_CONNECT           6
#define ASOCKERR_ACCEPT            7
#define ASOCKERR_POLL              8
#define ASOCKERR_CLOSED            9
#define ASOCKERR_BIND              10
#define ASOCKERR_BINDADDRINUSE     11
#define ASOCKERR_LISTEN            12
#define ASOCKERR_CONNECTSSL        13
#define ASOCKERR_NETUNREACH        14
#define ASOCKERR_ADDRUNRESV        15
#define ASOCKERR_BUSY              16
#define ASOCKERR_PROXY_NEEDS_AUTHENTICATION  17
#define ASOCKERR_PROXY_CONNECT_FAILED        18
#define ASOCKERR_WEBSOCK_UPGRADE_NOT_FOUND   19
#define ASOCKERR_WEBSOCK_TOO_MANY_CONNECTION 20
#define ASOCKERR_PROXY_INVALID_OR_NOT_SUPPORTED 21

/*
 * Cross-platform codes for AsyncSocket_GetGenericError():
 */
#ifdef _WIN32
#define ASOCK_ENOTCONN          WSAENOTCONN
#define ASOCK_ENOTSOCK          WSAENOTSOCK
#define ASOCK_EADDRINUSE        WSAEADDRINUSE
#define ASOCK_ECONNECTING       WSAEWOULDBLOCK
#define ASOCK_EWOULDBLOCK       WSAEWOULDBLOCK
#define ASOCK_ENETUNREACH       WSAENETUNREACH
#define ASOCK_ECONNRESET        WSAECONNRESET
#define ASOCK_ECONNABORTED      WSAECONNABORTED
#define ASOCK_EPIPE             ERROR_NO_DATA
#define ASOCK_EHOSTUNREACH      WSAEHOSTUNREACH
#define ASOCK_ETIMEDOUT         WSAETIMEDOUT
#define ASOCK_ECONNREFUSED      WSAECONNREFUSED
#define ASOCK_EACCES            WSAEACCES
#else
#define ASOCK_ENOTCONN          ENOTCONN
#define ASOCK_ENOTSOCK          ENOTSOCK
#define ASOCK_EADDRINUSE        EADDRINUSE
#define ASOCK_ECONNECTING       EINPROGRESS
#define ASOCK_EWOULDBLOCK       EWOULDBLOCK
#define ASOCK_ENETUNREACH       ENETUNREACH
#define ASOCK_ECONNRESET        ECONNRESET
#define ASOCK_ECONNABORTED      ECONNABORTED
#define ASOCK_EPIPE             EPIPE
#define ASOCK_EHOSTUNREACH      EHOSTUNREACH
#define ASOCK_ETIMEDOUT         ETIMEDOUT
#define ASOCK_ECONNREFUSED      ECONNREFUSED
#define ASOCK_EACCES            EACCES
#endif

/*
 * Websocket close status codes --
 *
 * enum has numbers in names because RFC6455 refers to the numbers frequently.
 */
enum {
   WEB_SOCKET_CLOSE_STATUS_1000_NORMAL = 1000,
   WEB_SOCKET_CLOSE_STATUS_1001_GOING_AWAY = 1001,
   WEB_SOCKET_CLOSE_STATUS_1002_PROTOCOL_ERROR = 1002,
   WEB_SOCKET_CLOSE_STATUS_1003_INVALID_DATA = 1003,
   WEB_SOCKET_CLOSE_STATUS_1005_EMPTY = 1005,
   WEB_SOCKET_CLOSE_STATUS_1006_ABNORMAL = 1006,
   WEB_SOCKET_CLOSE_STATUS_1007_INCONSISTENT_DATA = 1007,
   WEB_SOCKET_CLOSE_STATUS_1008_POLICY_VIOLATION = 1008,
   WEB_SOCKET_CLOSE_STATUS_1009_MESSAGE_TOO_BIG = 1009,
   WEB_SOCKET_CLOSE_STATUS_1010_UNSUPPORTED_EXTENSIONS = 1010,
   WEB_SOCKET_CLOSE_STATUS_1015_TLS_HANDSHAKE_ERROR = 1015,
};

/*
 * Flags passed into AsyncSocket_Connect*().
 * Default value is '0'.
 * The first two flags allow explicitly selecting
 * an ESX network stack. They no longer make sense because the
 * COS is gone. The flags are left around just to ensure we don't have
 * any flag collisions from users of the library.
 * The 3rd is for code that uses inet_pton() to get an IP address.
 * inet_pton() returns address in network-byte-order,
 * instead of the expected host-byte-order.
 */
typedef enum {
// ASOCKCONN_USE_ESX_SHADOW_STACK       = 1<<0,
// ASOCKCONN_USE_ESX_NATIVE_STACK       = 1<<1,
   ASOCKCONN_ADDR_IN_NETWORK_BYTE_ORDER = 1<<2
} AsyncSocketConnectFlags;

/*
 * SSL opaque type declarations (so we don't have to include ssl.h)
 */
struct SSLSockStruct;
struct _SSLVerifyParam;

/*
 * AsyncSocket type is opaque
 */
typedef struct AsyncSocket AsyncSocket;

/*
 * AsyncSocket registers poll callbacks, so give client the opportunity
 * to control how this is done.
 *
 * All the AsyncSocket constructors (Listen, Connect, Attach) take an
 * optional AsyncSocketPollParam* argument; if NULL the default behavior is
 * used (callback is registered in POLL_CS_MAIN and locked by the BULL).
 * Or the client can specify its favorite poll class and locking behavior.
 * Use of IVmdbPoll is only supported for regular sockets and for Attach.
 */
#include "poll.h"
struct IVmdbPoll;
typedef struct AsyncSocketPollParams {
   int flags;               /* Default 0, only POLL_FLAG_NO_BULL is valid */
   MXUserRecLock *lock;     /* Default: none but BULL */
   PollClassSet pollClass;  /* Default is POLL_CS_MAIN */
   struct IVmdbPoll *iPoll; /* Default NULL: use Poll_Callback */
} AsyncSocketPollParams;

/*
 * Initialize platform libraries
 */
int AsyncSocket_Init(void);

/*
 * Check the current state of the socket
 */
typedef enum AsyncSocketState {
   AsyncSocketListening,
   AsyncSocketConnecting,
   AsyncSocketConnected,
   AsyncSocketCBCancelled,
   AsyncSocketClosed,
   AsyncSocketConnectedRdOnly,
} AsyncSocketState;


typedef struct AsyncSocketNetworkStats {
   uint32 cwndBytes;             /* maximum outstanding bytes */
   uint32 rttSmoothedAvgMillis;  /* rtt average in milliseconds */
   uint32 rttSmoothedVarMillis;  /* rtt variance in milliseconds */
   uint32 queuedBytes;           /* unsent bytes in send queue */
   uint32 inflightBytes;         /* current outstanding bytes */
   double packetLossPercent;     /* packet loss percentage */
} AsyncSocketNetworkStats;


/*
 * The following covers all facilities involving dynamic socket options w.r.t.
 * various async sockets, excluding the async socket options API on the
 * sockets themselves, which can be found in asyncSocketVTable.h and those
 * files implementing that API.
 *
 * Potential related future work is covered in
 * asyncsocket/README-asyncSocketOptions-future-work.txt.
 *
 * Summary of dynamic socket options:
 *
 * Dynamic socket option = setting settable by the async socket API user
 * including during the lifetime of the socket. An interface spiritually
 * similar to setsockopt()'s seemed appropriate.
 *
 * The option-setting API looks as follows:
 *
 *   int ...SetOption(AsyncSocket *asyncSocket,
 *                    AsyncSocketOpts_Layer layer, // enum type
 *                    AsyncSocketOpts_ID optID, // an integer type
 *                    const void *valuePtr,
 *                    size_t inBufLen)
 *
 * Both native (setsockopt()) and non-native (usually, struct member)
 * options are supported. layer and optID arguments are conceptually similar
 * to setsockopt() level and option_name arguments, respectively.
 *
 * FOR NATIVE (setsockopt()) OPTIONS:
 *    layer = setsockopt() level value.
 *    optID = setsockopt() option_name value.
 *
 * FOR NON-NATIVE (struct member inside socket impl.) OPTIONS:
 *    layer = ..._BASE, ..._TCP, ..._FEC, etc.
 *       (pertains to the various AsyncSocket types);
 *    optID = value from enum type appropriate to the chosen layer.
 *
 * Examples (prefixes omitted for space):
 *
 *    -- NATIVE OPTIONS --
 *    optID          | layer       | <= | ssopt() level | ssopt() option_name
 *    ---------------+-------------+----+---------------+--------------------
 *    == option_name | == level    | <= | SOL_SOCKET    | SO_SNDBUF
 *    == option_name | == level    | <= | IPPROTO_TCP   | TCP_NODELAY
 *
 *    -- NON-NATIVE OPTIONS --
 *    optID                          | layer | <= | AsyncSocket type(s)
 *    -------------------------------+-------+----+--------------------
 *    _SEND_LOW_LATENCY_MODE         | _BASE | <= | any
 *       (enum AsyncSocket_OptID)    |       |    |
 *    _ALLOW_DECREASING_BUFFER_SIZE  | _TCP  | <= | AsyncTCPSocket
 *       (enum AsyncTCPSocket_OptID) |       |    |
 *    _MAX_CWND                      | _FEC  | <= | FECAsyncSocket
 *       (enum FECAsyncSocket_OptID) |       |    |
 *
 * Socket option lists for each non-native layer are just enums. Each socket
 * type should declare its own socket option enum in its own .h file; e.g., see
 * AsyncTCPSocket_OptID in this file. Some option lists apply to all async
 * sockets; these are also here in asyncsocket.h.
 *
 * The only way in which different socket option layers coexist in the same
 * file is the layer enum, AsyncSocketOpts_Layer, in the present file,
 * which enumerates all possible layers.
 *
 * The lack of any other cross-pollution between different non-native option
 * lists' containing files is a deliberate design choice.
 */

/*
 * Integral type used for the optID argument to ->setOption() async socket API.
 *
 * For a non-native option, use an enum value for your socket type.
 * (Example: ASYNC_TCP_SOCKET_OPT_ALLOW_DECREASING_BUFFER_SIZE
 * of type AsyncTCPSocket_OptID, which would apply to TCP sockets only.)
 *
 * For a native (setsockopt()) option, use the setsockopt() integer directly.
 * (Example: TCP_NODELAY.)
 *
 * Let's use a typedef as a small bit of abstraction and to be able to easily
 * change it to size_t, if (for example) we start indexing arrays with this
 * thing.
 */
typedef int AsyncSocketOpts_ID;

/*
 * Enum type used for the layer argument to ->setOption() async socket API.
 * As explained in the summary comment above, this
 * informs the particular ->setOption() implementation how to interpret
 * the accompanying optID integer value, as it may refer to one of several
 * option lists; and possible different socket instances (not as of this
 * writing).
 *
 * If editing, see summary comment above first for background.
 *
 * The values explicitly in this enum are for non-native options.
 * For native options, simply use the level value as for setsockopt().
 *
 * Ordinal values for all these non-native layers must not clash
 * with the native levels; hence the `LEVEL + CONSTANT` trick
 * just below.
 */
typedef enum {

   /*
    * Used when optID applies to a non-native socket option applicable to ANY
    * async socket type.
    */
   ASYNC_SOCKET_OPTS_LAYER_BASE = SOL_SOCKET + 1000,

   /*
    * Next enums must follow the above ordinally, so just:
    *    ASYNC_SOCKET_OPTS_LAYER_<layer name 1>,
    *    ASYNC_SOCKET_OPTS_LAYER_<layer name 2>, ...
    */

   ASYNC_SOCKET_OPTS_LAYER_BLAST_PROXY,

} AsyncSocketOpts_Layer;

/*
 * Enum type used for the OptId argument to ->setOption() async socket API,
 * when optID refers to a non-native option of any AsyncSocket regardless
 * of type.
 */
typedef enum {
   /*
    * Bool indicating whether to put the socket into a mode where we attempt
    * to issue sends directly from within ->send(). Ordinarily
    * (FALSE), we would set up a Poll callback from within ->send(),
    * which introduces some non-zero latency to the send path. In
    * low-latency-send mode (TRUE), that delay is potentially avoided. This
    * does introduce a behavioral change; the send completion
    * callback may be triggered before the call to ->send() returns. As
    * not all clients may be expecting this, we don't enable this mode
    * unless requested by the client.
    *
    * Default: FALSE.
    */
   ASYNC_SOCKET_OPT_SEND_LOW_LATENCY_MODE,
   /*
    * This socket config option provides a way to set DSCP value
    * on the TOS field of IP packet which is a 6 bit value.
    * Permissible values to configure are 0x0 to 0x3F, although
    * there are only subset of these values which are widely used.
    *
    * Default: 0.
    */
   ASYNC_SOCKET_OPT_DSCP
} AsyncSocket_OptID;

/*
 * Note: If you need to add a non-native option that applies to AsyncTCPSockets
 * only, you'd probably introduce an enum here named AsyncTCPSocket_OptID; and
 * at least one layer named ASYNC_SOCKET_OPTS_LAYER_TCP in the enum
 * AsyncSocketOpts_Layer.
 */


/* API functions for all AsyncSockets. */

AsyncSocketState AsyncSocket_GetState(AsyncSocket *sock);

const char * AsyncSocket_Err2String(int err);

const char * AsyncSocket_MsgError(int asyncSockErr);

int AsyncSocket_GetGenericErrno(AsyncSocket *s);

/*
 * Return a "unique" ID
 */
int AsyncSocket_GetID(AsyncSocket *asock);

/*
 * Return the fd corresponding to the socket.
 */
int AsyncSocket_GetFd(AsyncSocket *asock);

/*
 * Return the remote IP address associated with this socket if applicable
 */
int AsyncSocket_GetRemoteIPStr(AsyncSocket *asock,
                               const char **ipStr);

/*
 * Return the remote port associated with this socket if applicable
 */
int AsyncSocket_GetRemotePort(AsyncSocket *asock,
                              uint32 *port);

int AsyncSocket_GetLocalVMCIAddress(AsyncSocket *asock,
                                    uint32 *cid, uint32 *port);
int AsyncSocket_GetRemoteVMCIAddress(AsyncSocket *asock,
                                     uint32 *cid, uint32 *port);

int AsyncSocket_GetINETIPStr(AsyncSocket *asock, int socketFamily,
                             char **ipRetStr);
unsigned int AsyncSocket_GetPort(AsyncSocket *asock);

/*
 * Recv callback fires once previously requested data has been received
 */
typedef void (*AsyncSocketRecvFn) (void *buf, int len, AsyncSocket *asock,
                                   void *clientData);

/*
 * Send callback fires once previously queued data has been sent
 */
typedef void (*AsyncSocketSendFn) (void *buf, int len, AsyncSocket *asock,
                                   void *clientData);

/*
 * Error callback fires on I/O errors during read/write operations
 */
typedef void (*AsyncSocketErrorFn) (int error, AsyncSocket *asock,
                                    void *clientData);

typedef void (*AsyncSocketConnectFn) (AsyncSocket *asock, void *clientData);

typedef void (*AsyncSocketSslAcceptFn) (Bool status, AsyncSocket *asock,
                                        void *clientData);
typedef void (*AsyncSocketSslConnectFn) (Bool status, AsyncSocket *asock,
                                         void *clientData);
typedef void (*AsyncSocketCloseFn) (AsyncSocket *asock, void *clientData);

/*
 * Callback to handle http upgrade request header
 */
typedef int (*AsyncWebSocketHandleUpgradeRequestFn) (AsyncSocket *asock,
                                                     void *clientData,
                                                     const char *httpRequest,
                                                     char **httpResponse);

typedef int (*AsyncWebSocketHandoffSocketFn) (AsyncSocket *asock, void *cbData,
                                              void *buf, uint32 bufLen,
                                              uint32 currentPos);

/*
 * Listen on port and fire callback with new asock
 */
AsyncSocket *AsyncSocket_Listen(const char *addrStr,
                                unsigned int port,
                                AsyncSocketConnectFn connectFn,
                                void *clientData,
                                AsyncSocketPollParams *pollParams,
                                int *outError);
AsyncSocket *AsyncSocket_ListenLoopback(unsigned int port,
                                        AsyncSocketConnectFn connectFn,
                                        void *clientData,
                                        AsyncSocketPollParams *pollParams,
                                        int *outError);
AsyncSocket *AsyncSocket_ListenVMCI(unsigned int cid,
                                    unsigned int port,
                                    AsyncSocketConnectFn connectFn,
                                    void *clientData,
                                    AsyncSocketPollParams *pollParams,
                                    int *outError);
AsyncSocket *AsyncSocket_ListenWebSocket(const char *addrStr,
                                         unsigned int port,
                                         Bool useSSL,
                                         const char *protocols[],
                                         AsyncSocketConnectFn connectFn,
                                         void *clientData,
                                         AsyncSocketPollParams *pollParams,
                                         void *sslCtx,
                                         int *outError);
AsyncSocket *AsyncSocket_PrepareListenWebSocket(Bool useSSL,
                                                 const char *protocols[],
                                                 AsyncSocketConnectFn connectFn,
                                                 void *clientData,
                                                 AsyncSocketPollParams *pollParams,
                                                 void *sslCtx,
                                                 AsyncWebSocketHandleUpgradeRequestFn handleUpgradeRequestFn,
                                                 AsyncWebSocketHandoffSocketFn alpnCb,
                                                 const char* alpn);
AsyncSocket *AsyncSocket_RegisterListenWebSocket(AsyncSocket *asock,
                                                 const char *addrStr,
                                                 unsigned int port,
                                                 AsyncSocketPollParams *pollParams,
                                                 int *outError);
AsyncSocket* AsyncSocket_UpgradeToWebSocket(AsyncSocket *asock,
                                            const char *protocols[],
                                            AsyncSocketConnectFn connectFn,
                                            void *clientData,
                                            Bool useSSL,
                                            void *sslCtx,
                                            AsyncWebSocketHandleUpgradeRequestFn handleUpgradeRequestFn,
                                            int *outError);

#ifndef _WIN32
AsyncSocket *AsyncSocket_ListenWebSocketUDS(const char *pipeName,
                                            Bool useSSL,
                                            const char *protocols[],
                                            AsyncSocketConnectFn connectFn,
                                            void *clientData,
                                            AsyncSocketPollParams *pollParams,
                                            int *outError);
AsyncSocket *AsyncSocket_ListenSocketUDS(const char *pipeName,
                                         AsyncSocketConnectFn connectFn,
                                         void *clientData,
                                         AsyncSocketPollParams *pollParams,
                                         int *outError);
#endif


/*
 * Connect to address:port and fire callback with new asock.
 * If a custom error handler is needed, call AsyncSocket_SetErrorFn immediately
 * after the new asock is created. If this is done on a thread that is not the
 * poll thread, both calls should be done under the asyncsocket lock (passed
 * via pollParams).
 */
AsyncSocket *AsyncSocket_Connect(const char *hostname,
                                 unsigned int port,
                                 AsyncSocketConnectFn connectFn,
                                 void *clientData,
                                 AsyncSocketConnectFlags flags,
                                 AsyncSocketPollParams *pollParams,
                                 int *error);
AsyncSocket *AsyncSocket_ConnectWithFd(const char *hostname,
                                       unsigned int port,
                                       int tcpSocketFd,
                                       AsyncSocketConnectFn connectFn,
                                       void *clientData,
                                       AsyncSocketConnectFlags flags,
                                       AsyncSocketPollParams *pollParams,
                                       int *error);
AsyncSocket *AsyncSocket_ConnectVMCI(unsigned int cid, unsigned int port,
                                     AsyncSocketConnectFn connectFn,
                                     void *clientData,
                                     AsyncSocketConnectFlags flags,
                                     AsyncSocketPollParams *pollParams,
                                     int *error);
#ifndef _WIN32
AsyncSocket *AsyncSocket_ConnectUnixDomain(const char *path,
                                           AsyncSocketConnectFn connectFn,
                                           void *clientData,
                                           AsyncSocketConnectFlags flags,
                                           AsyncSocketPollParams *pollParams,
                                           int *error);
#else
AsyncSocket *
AsyncSocket_ConnectNamedPipe(const char *pipeName,
                             AsyncSocketConnectFn connectFn,
                             void *clientData,
                             AsyncSocketConnectFlags flags,
                             AsyncSocketPollParams *pollParams,
                             int *outError);

#define ASOCK_NAMEDPIPE_ALLOW_DEFAULT             (0)
#define ASOCK_NAMEDPIPE_ALLOW_ADMIN_USER_VMWARE   (SDPRIV_GROUP_ADMIN  |   \
                                                   SDPRIV_USER_CURRENT |   \
                                                   SDPRIV_GROUP_VMWARE)
#define ASOCK_NAMEDPIPE_ALLOW_ADMIN_USER          (SDPRIV_GROUP_ADMIN  |   \
                                                   SDPRIV_USER_CURRENT)

AsyncSocket*
AsyncSocket_CreateNamedPipe(const char *pipeName,
                            AsyncSocketConnectFn connectFn,
                            void *clientData,
                            DWORD openMode,
                            DWORD pipeMode,
                            uint32 numInstances,
                            DWORD accessType,
                            AsyncSocketPollParams *pollParams,
                            int *error);
AsyncSocket *
AsyncSocket_AttachToNamedPipe(HANDLE handle, AsyncSocketPollParams *pollParams,
                              int *outError);

/*
 * Obtain the client process id of the given named pipe
 */
Bool
AsyncSocket_GetNamedPipeClientProcessId(AsyncSocket* asock,
                                        PULONG clientPid);
#endif

AsyncSocket *
AsyncSocket_ConnectWebSocket(const char *url,
                             struct _SSLVerifyParam *sslVerifyParam,
                             const char *httpProxy,
                             const char *cookies,
                             const char *protocols[],
                             AsyncSocketConnectFn connectFn,
                             void *clientData,
                             AsyncSocketConnectFlags flags,
                             AsyncSocketPollParams *pollParams,
                             int *error);

/*
 * Initiate SSL connection on existing asock, with optional cert verification
 */
Bool AsyncSocket_ConnectSSL(AsyncSocket *asock,
                            struct _SSLVerifyParam *verifyParam,
                            const char *hostname,
                            void *sslContext);
int AsyncSocket_StartSslConnect(AsyncSocket *asock,
                                struct _SSLVerifyParam *verifyParam,
                                const char *hostname,
                                void *sslCtx,
                                AsyncSocketSslConnectFn sslConnectFn,
                                void *clientData);

Bool AsyncSocket_AcceptSSL(AsyncSocket *asock, void *sslCtx);
int AsyncSocket_StartSslAccept(AsyncSocket *asock,
                               void *sslCtx,
                               AsyncSocketSslAcceptFn sslAcceptFn,
                               void *clientData);

/*
 * Create a new AsyncSocket from an existing socket
 */
AsyncSocket *AsyncSocket_AttachToFd(int fd, AsyncSocketPollParams *pollParams,
                                    int *error);
AsyncSocket *AsyncSocket_AttachToSSLSock(struct SSLSockStruct *sslSock,
                                         AsyncSocketPollParams *pollParams,
                                         int *error);

int AsyncSocket_UseNodelay(AsyncSocket *asyncSocket, Bool nodelay);
int AsyncSocket_SetTCPTimeouts(AsyncSocket *asyncSocket,
                               int keepIdleSec, int keepIntvlSec, int keepCnt);
Bool AsyncSocket_EstablishMinBufferSizes(AsyncSocket *asyncSocket,
                                         int sendSz,
                                         int recvSz);
int AsyncSocket_SetSendLowLatencyMode(AsyncSocket *asyncSocket, Bool enable);
int AsyncSocket_SetOption(AsyncSocket *asyncSocket,
                          AsyncSocketOpts_Layer layer,
                          AsyncSocketOpts_ID optID,
                          const void *valuePtr, socklen_t inBufLen);
int AsyncSocket_GetOption(AsyncSocket *asyncSocket,
                          AsyncSocketOpts_Layer layer,
                          AsyncSocketOpts_ID optID,
                          void *valuePtr, socklen_t *outBufLen);

/*
 * Waits until at least one packet is received or times out.
 */
int AsyncSocket_DoOneMsg(AsyncSocket *s, Bool read, int timeoutMS);

/*
 * Waits until at least one connect() is accept()ed or times out.
 */
int AsyncSocket_WaitForConnection(AsyncSocket *s, int timeoutMS);

/*
 * Waits until a socket is ready with readable data or times out.
 */
int AsyncSocket_WaitForReadMultiple(AsyncSocket **asock, int numSock,
                                    int timeoutMS, int *outIdx);

/*
 * Send all pending packets onto the wire or give up after timeoutMS msecs.
 */
int AsyncSocket_Flush(AsyncSocket *asock, int timeoutMS);

/*
 * Drain recv for a remotely disconnected TCP socket.
 */
int AsyncSocket_TCPDrainRecv(AsyncSocket *base, int timeoutMS);

/*
 * Specify the exact amount of data to receive and the receive function to call.
 */
int AsyncSocket_Recv(AsyncSocket *asock, void *buf, int len, void *cb, void *cbData);

/*
 * Specify the maximum amount of data to receive and the receive function to call.
 */
int AsyncSocket_RecvPartial(AsyncSocket *asock, void *buf, int len,
                            void *cb, void *cbData);

int AsyncSocket_Peek(AsyncSocket *asock, void *buf, int len, void *cb, void *cbData);

int AsyncSocket_PeekPartial(AsyncSocket *asock, void *buf, int len,
                            void *cb, void *cbData);

/*
 * Specify the amount of data to receive and the receive function to call.
 */
int AsyncSocket_RecvPassedFd(AsyncSocket *asock, void *buf, int len,
                             void *cb, void *cbData);

/*
 * Retrieve socket received via RecvPassedFd.
 */
int AsyncSocket_GetReceivedFd(AsyncSocket *asock);


/*
 * Specify the amount of data to send/receive and how long to wait before giving
 * up.
 */
int AsyncSocket_RecvBlocking(AsyncSocket *asock,
                             void *buf, int len, int *received,
                             int timeoutMS);

int AsyncSocket_RecvPartialBlocking(AsyncSocket *asock,
                                    void *buf, int len, int *received,
                                    int timeoutMS);

int AsyncSocket_SendBlocking(AsyncSocket *asock,
                             void *buf, int len, int *sent,
                             int timeoutMS);

/*
 * Specify the amount of data to send and the send function to call
 */
int AsyncSocket_Send(AsyncSocket *asock, void *buf, int len,
                      AsyncSocketSendFn sendFn, void *clientData);
int AsyncSocket_SendWithFd(AsyncSocket *asock, void *buf, int len, int passFd,
                           AsyncSocketSendFn sendFn, void *clientData);

int AsyncSocket_IsSendBufferFull(AsyncSocket *asock);
int AsyncSocket_GetNetworkStats(AsyncSocket *asock,
                                AsyncSocketNetworkStats *stats);
int AsyncSocket_GetSNIHostname(AsyncSocket *asock,
                               const char **sniHostname);
int AsyncSocket_CancelRecv(AsyncSocket *asock, int *partialRecvd, void **recvBuf,
                           void **recvFn);
int AsyncSocket_CancelRecvEx(AsyncSocket *asock, int *partialRecvd, void **recvBuf,
                             void **recvFn, Bool cancelOnSend);
/*
 * Unregister asynchronous send and recv from poll
 */
int AsyncSocket_CancelCbForClose(AsyncSocket *asock);

/*
 * Set the error handler to invoke on I/O errors (default is to close the
 * socket). This should be done immediately after an asyncsocket is created.
 * If this is done on a thread that is not the poll thread, the asyncsocket
 * lock (passed via pollParams) should be held throughout.
 */
int AsyncSocket_SetErrorFn(AsyncSocket *asock, AsyncSocketErrorFn errorFn,
                           void *clientData);

/*
 * Set optional AsyncSocket_Close() behaviors.
 */
int AsyncSocket_SetCloseOptions(AsyncSocket *asock,
                                int flushEnabledMaxWaitMsec,
                                AsyncSocketCloseFn closeCb);

/*
 * Close the write side of the connection.
 */
int AsyncSocket_CloseWrite(AsyncSocket *asock);

/*
 * Close the connection and destroy the asock.
 */
int AsyncSocket_Close(AsyncSocket *asock);

/*
 * Retrieve the URI Supplied for a websocket connection
 */
char *AsyncSocket_GetWebSocketURI(AsyncSocket *asock);

/*
 * Retrieve the Cookie Supplied for a websocket connection
 */
char *AsyncSocket_GetWebSocketCookie(AsyncSocket *asock);

/*
 * Set the Cookie  for a websocket connection
 */
int AsyncSocket_SetWebSocketCookie(AsyncSocket *asock,
                                   void *clientData,
                                   const char *path,
                                   const char *sessionId);

/*
 * Retrieve the close status, if received, for a websocket connection
 */
uint16 AsyncSocket_GetWebSocketCloseStatus(AsyncSocket *asock);

/*
 * Get negotiated websocket protocol
 */
const char *AsyncSocket_GetWebSocketProtocol(AsyncSocket *asock);

/*
 * Set the flag for whether or not to delay websocket upgrade response
 */
int AsyncSocket_SetDelayWebSocketUpgradeResponse(AsyncSocket *asock,
                                                 Bool delayWebSocketUpgradeResponse);

/*
 * Send the websocket upgrade response
 */
void
AsyncSocket_WebSocketServerSendUpgradeResponse(AsyncSocket *base,
                                               char *httpResponseTemp);

/*
 * Get error code for websocket failure
 */
int AsyncSocket_GetWebSocketError(AsyncSocket *asock);

const char * stristr(const char *s, const char *find);

/*
 * Helper function to parse websocket URL
 */
Bool AsyncSocket_WebSocketParseURL(const char *url, char **hostname,
                                   unsigned int *port, Bool *useSSL,
                                   char **relativeURL, char **uriHostname);

/*
 * Find and return the value for the given header key in the supplied buffer
 */
char *AsyncSocket_WebSocketGetHttpHeader(const char *request,
                                         const char *webKey);

unsigned AsyncSocket_WebSocketGetNumAccepted(AsyncSocket *asock);

Bool AsyncSocket_SetKeepAlive(AsyncSocket *asock, int keepIdle);

/*
 * Send an HTTP error code, only valid on AsyncWebSockets
 */
void AsyncSocket_WebSocketServerSendError(AsyncSocket *asock, const char *text);


/*
 * Some logging macros for convenience
 */
#define ASOCKPREFIX "SOCKET "

/* Both gcc and msvc support ##__VA_ARGS__ to handle zero-length variadic arguments */
#define ASOCKWARN(_asock, fmt, ...)                               \
   Warning(ASOCKPREFIX "%d (%d) " fmt, AsyncSocket_GetID(_asock), \
           AsyncSocket_GetFd(_asock), ##__VA_ARGS__)

#define ASOCKLG0(_asock, fmt, ...)                            \
   Log(ASOCKPREFIX "%d (%d) " fmt, AsyncSocket_GetID(_asock), \
       AsyncSocket_GetFd(_asock), ##__VA_ARGS__)

#define ASOCKLOG(_level, _asock, fmt, ...)                          \
   do {                                                             \
      if (((_level) == 0) || DOLOG_BYNAME(asyncsocket, (_level))) { \
         Log(ASOCKPREFIX "%d (%d) " fmt, AsyncSocket_GetID(_asock), \
             AsyncSocket_GetFd(_asock), ##__VA_ARGS__);             \
      }                                                             \
   } while (0)

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // __ASYNC_SOCKET_H__
 070701000000A1000081A400000000000000000000000168225505000009F5000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/auth.h /*********************************************************
 * Copyright (C) 1998-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _AUTH_H_
#define _AUTH_H_

/*
 * auth.h --
 *
 *	Defines for the authorization library.
 */

#include "vm_basic_types.h"
#include "unicodeTypes.h"

#if _WIN32
#  include <windows.h>
   /*
    * This is quite possibly wrong, but fixes compile errors
    * for now.  Come back to this when authentication is
    * properly implemented.
    */
   typedef HANDLE AuthToken;
#else
#  include <pwd.h> //for getpwent, etc.
#  include <grp.h> //for initgroups
   typedef const struct passwd * AuthToken;
#endif

#if defined(__cplusplus)
extern "C" {
#endif

#if _WIN32

BOOL Auth_StoreAccountInformation(const char *username, const char *password);
BOOL Auth_DeleteAccountInformation();
BOOL Auth_RetrieveAccountInformation(char **username, char **password);

#define AUTH_ATTRIBUTE_RUNAS_LOCAL_SYSTEM    0x1
#define AUTH_LOCAL_SYSTEM_USER "SYSTEM"

BOOL Auth_StoreAccountInformationForVM(const char *filename, uint32 attributes,
				       const char *username, const char *password);
BOOL Auth_DeleteAccountInformationForVM(const char *filename);
BOOL Auth_AccountInformationIsStoredForVMs();
BOOL Auth_DeleteAccountInformationStoredForVMs();
uint32 Auth_RetrieveAccountInformationForVM(const char *filename, uint32 *attributes,
					    char **username, char **password);

#else

AuthToken Auth_GetPwnam(const char *user);
AuthToken Auth_AuthenticateSelf(void);
AuthToken Auth_AuthenticateUserPAM(const char *user, const char *pass,
                                   const char *service);

#endif

AuthToken Auth_AuthenticateUser(const char *user, const char *pass);

void Auth_CloseToken(AuthToken token);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
   070701000000A2000081A40000000000000000000000016822550500000887000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/backdoor.h /*********************************************************
 * Copyright (C) 1999-2017, 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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"

#if defined(__cplusplus)
extern "C" {
#endif

void
Backdoor(Backdoor_proto *bp); // IN/OUT

void
Backdoor_ForceLegacy(Bool force);  // IN

void
Backdoor_InOut(Backdoor_proto *bp); // IN/OUT

void
Backdoor_Vmcall(Backdoor_proto *bp); // IN/OUT

void
Backdoor_Vmmcall(Backdoor_proto *bp); // IN/OUT

void
Backdoor_HbOut(Backdoor_proto_hb *bp); // IN/OUT

void
Backdoor_HbIn(Backdoor_proto_hb *bp); // IN/OUT

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _BACKDOOR_H_ */
 070701000000A3000081A40000000000000000000000016822550500004C41000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/backdoor_def.h /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#if defined __cplusplus
extern "C" {
#endif

/*
 * If you want to add a new low-level backdoor call for a guest userland
 * application, please consider using the GuestRpc mechanism instead.
 */

#define BDOOR_MAGIC 0x564D5868

/* Low-bandwidth backdoor port number for the IN/OUT interface. */

#define BDOOR_PORT        0x5658

/* Flags used by the hypercall interface. */

#define BDOOR_FLAGS_LB    0
#define BDOOR_FLAGS_READ  0
#define BDOOR_FLAGS_HB    (1<<0)
#define BDOOR_FLAGS_WRITE (1<<1)

#define BDOOR_IS_LB(_flags)    (((_flags) & BDOOR_FLAGS_HB) == 0)
#define BDOOR_IS_HB(_flags)    !BDOOR_IS_LB(_flags)
#define BDOOR_IS_READ(_flags)  (((_flags) & BDOOR_FLAGS_WRITE) == 0)
#define BDOOR_IS_WRITE(_flags) !BDOOR_IS_READ(_flags)

/*
 * Max number of BPNs that can be passed in a single call from monitor->VMX with
 * a HB backdoor request.  This should be kept in parity with
 * IOSPACE_MAX_REP_BPNS to keep performance between the two HB backdoor
 * interfaces comparable.
 */
#define BDOOR_HB_MAX_BPNS  513

#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 Hausner 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 Weissman offered to
 *   implement that)
 *
 * o The old BIOS code, which will soon be jettisoned
 */
#define   BDOOR_CMD_APMFUNCTION               2 /* CPL0 only. */
#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 /* Disabled by default. */
#define   BDOOR_CMD_GETHWVERSION             17
#define   BDOOR_CMD_OSNOTFOUND               18 /* CPL0 only. */
#define   BDOOR_CMD_GETUUID                  19
#define   BDOOR_CMD_GETMEMSIZE               20
//#define BDOOR_CMD_HOSTCOPY                 21 /* Not in use. Was devel only. */
//#define BDOOR_CMD_SERVICE_VM               22 /* Not in use. Never shipped. */
#define   BDOOR_CMD_GETTIME                  23 /* Deprecated -> GETTIMEFULL. */
#define   BDOOR_CMD_STOPCATCHUP              24
#define   BDOOR_CMD_PUTCHR                   25 /* Disabled by default. */
//#define BDOOR_CMD_ENABLE_MSG               26 /* Not in use. Was devel only.*/
//#define BDOOR_CMD_GOTO_TCL                 27 /* Not in use. Was devel only */
#define   BDOOR_CMD_INITPCIOPROM             28 /* CPL 0 only. */
//#define BDOOR_CMD_INT13                    29 /* Not in use. */
#define   BDOOR_CMD_MESSAGE                  30
#define   BDOOR_CMD_SIDT                     31
#define   BDOOR_CMD_SGDT                     32
#define   BDOOR_CMD_SLDT_STR                 33
#define   BDOOR_CMD_ISACPIDISABLED           34
//#define BDOOR_CMD_TOE                      35 /* Not in use. */
#define   BDOOR_CMD_ISMOUSEABSOLUTE          36
#define   BDOOR_CMD_PATCH_SMBIOS_STRUCTS     37 /* CPL 0 only. */
#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 /* Not in use. */
//#define BDOOR_CMD_PATCH_ACPI_TABLES        43 /* Not in use. */
//#define BDOOR_CMD_DEVEL_FAKEHARDWARE       44 /* Not in use. */
#define   BDOOR_CMD_GETHZ                    45
#define   BDOOR_CMD_GETTIMEFULL              46
//#define BDOOR_CMD_STATELOGGER              47 /* Not in use. */
#define   BDOOR_CMD_CHECKFORCEBIOSSETUP      48 /* CPL 0 only. */
#define   BDOOR_CMD_LAZYTIMEREMULATION       49 /* CPL 0 only. */
#define   BDOOR_CMD_BIOSBBS                  50 /* CPL 0 only. */
//#define BDOOR_CMD_VASSERT                  51 /* Not in use. */
#define   BDOOR_CMD_ISGOSDARWIN              52
#define   BDOOR_CMD_DEBUGEVENT               53
#define   BDOOR_CMD_OSNOTMACOSXSERVER        54 /* CPL 0 only. */
#define   BDOOR_CMD_GETTIMEFULL_WITH_LAG     55
#define   BDOOR_CMD_ACPI_HOTPLUG_DEVICE      56 /* Devel only. */
#define   BDOOR_CMD_ACPI_HOTPLUG_MEMORY      57 /* Devel only. */
#define   BDOOR_CMD_ACPI_HOTPLUG_CBRET       58 /* Devel only. */
//#define BDOOR_CMD_GET_HOST_VIDEO_MODES     59 /* Not in use. */
#define   BDOOR_CMD_ACPI_HOTPLUG_CPU         60 /* Devel only. */
//#define BDOOR_CMD_USB_HOTPLUG_MOUSE        61 /* Not in use. Never shipped. */
#define   BDOOR_CMD_XPMODE                   62 /* CPL 0 only. */
#define   BDOOR_CMD_NESTING_CONTROL          63
#define   BDOOR_CMD_FIRMWARE_INIT            64 /* CPL 0 only. */
#define   BDOOR_CMD_FIRMWARE_ACPI_SERVICES   65 /* CPL 0 only. */
#  define BDOOR_CMD_FAS_GET_TABLE_SIZE           0
#  define BDOOR_CMD_FAS_GET_TABLE_DATA           1
#  define BDOOR_CMD_FAS_GET_PLATFORM_NAME        2
#  define BDOOR_CMD_FAS_GET_PCIE_OSC_MASK        3
#  define BDOOR_CMD_FAS_GET_APIC_ROUTING         4
#  define BDOOR_CMD_FAS_GET_TABLE_SKIP           5
#  define BDOOR_CMD_FAS_GET_SLEEP_ENABLES        6
#  define BDOOR_CMD_FAS_GET_HARD_RESET_ENABLE    7
#  define BDOOR_CMD_FAS_GET_MOUSE_HID            8
#  define BDOOR_CMD_FAS_GET_SMBIOS_VERSION       9
#  define BDOOR_CMD_FAS_GET_64BIT_PCI_HOLE_SIZE 10
//#define BDOOR_CMD_FAS_GET_NVDIMM_FMT_CODE     11 /* Not in use. Never shipped. */
#  define BDOOR_CMD_FAS_SRP_ENABLED             12
#  define BDOOR_CMD_FAS_EXIT_BOOT_SERVICES      13
#  define BDOOR_CMD_FAS_GET_API_ENABLES         14
#  define BDOOR_CMD_FAS_UNACCEPTED_MEM_ENABLED  15
#define   BDOOR_CMD_SENDPSHAREHINTS          66 /* Not in use. Deprecated. */
#define   BDOOR_CMD_ENABLE_USB_MOUSE         67
#define   BDOOR_CMD_GET_VCPU_INFO            68
//#define BDOOR_CMD_VCPU_SLC64                0 /* BT-is-dead. Deprecated. */
#  define BDOOR_CMD_VCPU_SYNC_VTSCS           1
//#define BDOOR_CMD_VCPU_HV_REPLAY_OK         2 /* Replay-is-dead. Deprecated.*/
#  define BDOOR_CMD_VCPU_LEGACY_X2APIC_OK     3
#  define BDOOR_CMD_VCPU_MMIO_HONORS_PAT      4
#  define BDOOR_CMD_VCPU_RESERVED            31
#define   BDOOR_CMD_EFI_SERIALCON_CONFIG     69 /* CPL 0 only. */
//#define   BDOOR_CMD_BUG328986              70 /* CPL 0 only. Deprecated. */
#define   BDOOR_CMD_FIRMWARE_ERROR           71 /* CPL 0 only. */
#  define BDOOR_CMD_FE_INSUFFICIENT_MEM       0
#  define BDOOR_CMD_FE_EXCEPTION              1
#  define BDOOR_CMD_FE_SGX                    2
#  define BDOOR_CMD_FE_PCI_MMIO               3
//#define BDOOR_CMD_FE_GMM                    4 /* GMM is deprecated. */
#define   BDOOR_CMD_VMK_INFO                 72
#define   BDOOR_CMD_EFI_BOOT_CONFIG          73 /* CPL 0 only. */
#  define BDOOR_CMD_EBC_LEGACYBOOT_ENABLED        0
#  define BDOOR_CMD_EBC_GET_ORDER                 1
#  define BDOOR_CMD_EBC_SHELL_ACTIVE              2
#  define BDOOR_CMD_EBC_GET_NETWORK_BOOT_PROTOCOL 3
#  define BDOOR_CMD_EBC_QUICKBOOT_ENABLED         4
#  define BDOOR_CMD_EBC_GET_PXE_ARCH              5
#  define BDOOR_CMD_EBC_SKIP_DELAYS               6
#  define BDOOR_CMD_EBC_GET_NETWORK_BOOT_URI      7
#define   BDOOR_CMD_GET_HW_MODEL             74 /* CPL 0 only. */
#define   BDOOR_CMD_GET_SVGA_CAPABILITIES    75 /* CPL 0 only. */
#define   BDOOR_CMD_GET_FORCE_X2APIC         76 /* CPL 0 only  */
#define   BDOOR_CMD_SET_PCI_HOLE             77 /* CPL 0 only  */
#define   BDOOR_CMD_GET_PCI_HOLE             78 /* CPL 0 only  */
#define   BDOOR_CMD_GET_PCI_BAR              79 /* CPL 0 only  */
#define   BDOOR_CMD_SHOULD_GENERATE_SYSTEMID 80 /* CPL 0 only  */
#define   BDOOR_CMD_READ_DEBUG_FILE          81 /* Devel only. */
#define   BDOOR_CMD_SCREENSHOT               82 /* Devel only. */
#define   BDOOR_CMD_INJECT_KEY               83 /* Devel only. */
#define   BDOOR_CMD_INJECT_MOUSE             84 /* Devel only. */
#define   BDOOR_CMD_MKS_GUEST_STATS          85 /* CPL 0 only. */
#  define BDOOR_CMD_MKSGS_RESET               0
#  define BDOOR_CMD_MKSGS_ADD_PPN             1
#  define BDOOR_CMD_MKSGS_REMOVE_PPN          2
#define   BDOOR_CMD_ABSPOINTER_RESTRICT      86
//#define BDOOR_CMD_GUEST_INTEGRITY          87 /* GI is deprecated. */
#define   BDOOR_CMD_MKSTEST                  88 /* Devel only. */
#  define BDOOR_CMD_MKSTEST_STATS_START       0
#  define BDOOR_CMD_MKSTEST_STATS_STOP        1
#  define BDOOR_CMD_MKSTEST_CASE_START        2
#  define BDOOR_CMD_MKSTEST_CASE_STOP         3
#define   BDOOR_CMD_SECUREBOOT               89
#define   BDOOR_CMD_COPY_PHYSMEM             90 /* Devel only. */
#define   BDOOR_CMD_STEALCLOCK               91 /* CPL 0 only. */
#  define BDOOR_STEALCLOCK_STATUS_DISABLED    0
#  define BDOOR_STEALCLOCK_STATUS_ENABLED     1
#define   BDOOR_CMD_GUEST_PAGE_HINTS         92 /* CPL 0 only  */
#define   BDOOR_CMD_FIRMWARE_UPDATE          93 /* CPL 0 only. */
#  define BDOOR_CMD_FU_GET_HOST_VERSION       0
#  define BDOOR_CMD_FU_UPDATE_FROM_HOST       1
#  define BDOOR_CMD_FU_LOCK                   2
#define   BDOOR_CMD_FUZZER_HELPER            94 /* Devel only. */
#  define BDOOR_CMD_FUZZER_INIT               0
#  define BDOOR_CMD_FUZZER_NEXT               1
#define   BDOOR_CMD_PUTCHR12                 95
//#define BDOOR_CMD_GMM                      96 /* GMM is deprecated. */
#define   BDOOR_CMD_PRECISIONCLOCK           97
#  define BDOOR_CMD_PRECISIONCLOCK_GETTIME    0
#  define BDOOR_CMD_PRECISIONCLOCK_SETTIME    1
#  define BDOOR_CMD_PRECISIONCLOCK_ADJTIME    2
#  define BDOOR_CMD_PRECISIONCLOCK_ADJFREQ    3
#  define BDOOR_CMD_PRECISIONCLOCK_NUMCMDS    4
//#define   BDOOR_CMD_COREDUMP_UNSYNC        98 /* Not in use.  PR 3328536. */
#define   BDOOR_CMD_APPLE_GPU_RES_SET        99
#define   BDOOR_CMD_GETBUILDNUM             100
#define   BDOOR_CMD_GETENTROPY              101 /* Configurable, off by default. */
#define   BDOOR_CMD_REPORTGUESTCRASH        102
#define   BDOOR_CMD_MAX                     103


/*
 * 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 be impacted.
 */

/* Nesting control operations */

#define NESTING_CONTROL_RESTRICT_BACKDOOR 0
#define NESTING_CONTROL_OPEN_BACKDOOR     1
#define NESTING_CONTROL_QUERY             2
#define NESTING_CONTROL_MAX               2

/* EFI Boot Order options, nibble-sized. */
#define EFI_BOOT_ORDER_TYPE_EFI           0x0
#define EFI_BOOT_ORDER_TYPE_LEGACY        0x1
#define EFI_BOOT_ORDER_TYPE_NONE          0xf

#define BDOOR_NETWORK_BOOT_PROTOCOL_NONE   0x0
#define BDOOR_NETWORK_BOOT_PROTOCOL_IPV4   0x1
#define BDOOR_NETWORK_BOOT_PROTOCOL_IPV6   0x2
#define BDOOR_NETWORK_BOOT_PROTOCOL_HTTPV4 0x3
#define BDOOR_NETWORK_BOOT_PROTOCOL_HTTPV6 0x4

#define BDOOR_SECUREBOOT_STATUS_DISABLED  0xFFFFFFFFUL
#define BDOOR_SECUREBOOT_STATUS_APPROVED  1
#define BDOOR_SECUREBOOT_STATUS_DENIED    2

#define BDOOR_FAS_API_ENABLE_FDT          0x00000001UL

/* High-bandwidth backdoor port. */

#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 BDOOR_PMC_PSEUDO_TSC  0x10003

#define IS_BDOOR_PMC(index)  (((index) | 3) == 0x10003)
#define BDOOR_CMD(ecx)       ((ecx) & 0xffff)

/* Sub commands for BDOOR_CMD_VMK_INFO */
#define BDOOR_CMD_VMK_INFO_ENTRY   1

/*
 * Current format for the guest page hints is:
 *
 * Arg0: BDOOR_MAGIC, Arg3: BDOOR_PORT
 *
 * Arg1: (rbx on x86)
 *
 *  0         64
 *  |   PPN   |
 *
 * Arg2: (rcx on x86)
 *
 *  0         16        32         64
 *  | Command |  Type   | Reserved |
 *
 * Arg4: (rsi on x86)
 *
 *  0          16         64
 *  | numPages | Reserved |
 *
 */
#define BDOOR_GUEST_PAGE_HINTS_NOT_SUPPORTED ((unsigned)-1)
#define BDOOR_GUEST_PAGE_HINTS_MAX_PAGES     (0xffff)
#define BDOOR_GUEST_PAGE_HINTS_TYPE_PSHARE   (0)
#define BDOOR_GUEST_PAGE_HINTS_TYPE(reg)     (((reg) >> 16) & 0xffff)

#ifdef VM_ARM_64

/*
 * VMware x86 I/O space virtualization on arm.
 *
 * Implementation goal
 * ---
 * The goal of this implementation is to precisely mimic the semantics of the
 * "VMware x86 I/O space virtualization on x86", in particular:
 *
 * o A vCPU can perform an N-byte access to an I/O port address that is not
 *   N-byte aligned.
 *
 * o A vCPU can perform an N-byte access to I/O port address A without
 *   impacting I/O port addresses [ A + 1; A + N ).
 *
 * o A vCPU can access the I/O space when running 32-bit or 64-bit code.
 *
 * o A vCPU running in unprivileged mode can use the backdoor.
 *
 * As a result, VMware virtual device drivers that were initially developed for
 * x86 can trivially be ported to arm.
 *
 * Mechanism
 * ---
 * In this section, we call W<n> the 32-bit register which aliases the low 32
 * bits of the 64-bit register X<n>.
 *
 * A vCPU which wishes to use the "VMware x86 I/O space virtualization on arm"
 * must follow these 4 steps:
 *
 * 1) Write to general-purpose registers specific to the x86 I/O space
 *    instruction.
 *
 * The vCPU writes to the arm equivalent of general-purpose x86 registers (see
 * the BDOOR_ARG* mapping below) that are used by the x86 I/O space instruction
 * it is about to perform.
 *
 * Examples:
 * o For an IN instruction without DX register, there is nothing to do.
 * o For an OUT instruction with DX register, the vCPU places the I/O port
 *   address in bits W3<15:0> and the value to write in W0<7:0> (1 byte access)
 *   or W0<15:0> (2 bytes access) or W0 (4 bytes access).
 * o For an REP OUTS instruction, the vCPU places the I/O port address in bits
 *   W3<15:0>, the source virtual address in W4 (32-bit code) or X4 (64-bit
 *   code) and the number of repetitions in W2 (32-bit code) or X2 (64-bit
 *   code).
 *
 * 2) Write the x86 I/O space instruction to perform.
 *
 * The vCPU sets a value in W7, as described below:
 *
 * Transfer size, bits [1:0]
 *    00: 1 byte
 *    01: 2 bytes
 *    10: 4 bytes
 *    11: Invalid value
 *
 * Transfer direction, bit [2]
 *    0: Write (OUT/OUTS/REP OUTS instructions)
 *    1: Read (IN/INS/REP INS instructions)
 *
 * Instruction type, bits [4:3]
 *    00: Non-string instruction (IN/OUT) without DX register
 *        The port address (8-bit immediate) is set in W7<12:5>.
 *
 *    01: Non-string instruction (IN/OUT) with DX register
 *
 *    10: String instruction without REP prefix (INS/OUTS)
 *        The direction flag (EFLAGS.DF) is set in W7<5>.
 *
 *    11: String instruction with REP prefix (REP INS/REP OUTS)
 *        The direction flag (EFLAGS.DF) is set in W7<5>.
 *
 * All other bits not described above are reserved for future use and must be
 * set to 0.
 *
 * 3) Perform the x86 I/O space instruction.
 *
 * Several mechanisms are available:
 *
 * o From EL1
 *   The vCPU executes the HVC instruction with the immediate X86_IO_MAGIC.
 *
 *   This is the mechanism to favor from EL1 because it is architectural.
 *
 * o From EL1 and EL0
 *   The vCPU sets X7<63:32> to X86_IO_MAGIC and executes the
 *   MRS XZR, MDCCSR_EL0 instruction.
 *
 *   This is the mechanism to favor from EL0 because it has a negligible impact
 *   on vCPU performance.
 *
 * o From EL1 and EL0, only when monitor_control.hv_hypercall = "TRUE"
 *   The vCPU executes the BRK instruction with the immediate X86_IO_MAGIC.
 *
 *   Pro: This mechanism cannot be intercepted by EL3 code.
 *   Con: This mechanism has a significant impact on vCPU performance when
 *        running a debugger in the guest.
 *
 * 4) Read from general-purpose registers specific to the x86 I/O space
 *    instruction.
 *
 * The vCPU reads from the arm equivalent of general-purpose x86 registers (see
 * the BDOOR_ARG* mapping below) that are used by the x86 I/O space instruction
 * it has just performed.
 *
 * Examples:
 * o For an OUT instruction, there is nothing to do.
 * o For an IN instruction, retrieve the value that was read from W0<7:0> (1
 *   byte access) or W0<15:0> (2 bytes access) or W0 (4 bytes access).
 */

#define X86_IO_MAGIC          0x86

#define X86_IO_W7_SIZE_SHIFT  0
#define X86_IO_W7_SIZE_MASK   (0x3 << X86_IO_W7_SIZE_SHIFT)
#define X86_IO_W7_DIR         (1 << 2)
#define X86_IO_W7_WITH        (1 << 3)
#define X86_IO_W7_STR         (1 << 4)
#define X86_IO_W7_DF          (1 << 5)
#define X86_IO_W7_IMM_SHIFT   5
#define X86_IO_W7_IMM_MASK    (0xff << X86_IO_W7_IMM_SHIFT)

#define BDOOR_ARG0 REG_X0
#define BDOOR_ARG1 REG_X1
#define BDOOR_ARG2 REG_X2
#define BDOOR_ARG3 REG_X3
#define BDOOR_ARG4 REG_X4
#define BDOOR_ARG5 REG_X5
#define BDOOR_ARG6 REG_X6

#else

#define BDOOR_ARG0 REG_RAX
#define BDOOR_ARG1 REG_RBX
#define BDOOR_ARG2 REG_RCX
#define BDOOR_ARG3 REG_RDX
#define BDOOR_ARG4 REG_RSI
#define BDOOR_ARG5 REG_RDI
#define BDOOR_ARG6 REG_RBP

#endif


#if defined __cplusplus
}
#endif

#endif // _BACKDOOR_DEF_H_
   070701000000A4000081A400000000000000000000000168225505000011CE000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/include/backdoor_types.h   /*********************************************************
 * Copyright (C) 1999-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * backdoor_types.h --
 *
 *    Type definitions for backdoor interaction code.
 */

#ifndef _BACKDOOR_TYPES_H_
#define _BACKDOOR_TYPES_H_


#if !(defined __i386__ || defined __x86_64__  || defined __aarch64__ || defined _M_IX86  || defined _M_X64)
#error The backdoor protocol is only supported on x86 and aarch64 architectures.
#endif

/*
 * The three different backdoor interfaces: the legacy IN/OUT interface, and the
 * hypercall interfaces for AMD and Intel.
 */
typedef enum {
   BACKDOOR_INTERFACE_NONE,
   BACKDOOR_INTERFACE_IO,
   BACKDOOR_INTERFACE_VMMCALL,
   BACKDOOR_INTERFACE_VMCALL
} BackdoorInterface;

/*
 * 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

#if defined (VM_X86_64)  ||  defined (VM_ARM_64)
#define DECLARE_REG_STRUCT DECLARE_REG64_STRUCT
#else
#define DECLARE_REG_STRUCT DECLARE_REG32_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_ */
  070701000000A5000081A400000000000000000000000168225505000007DE000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/base64.h   /*********************************************************
 * Copyright (C) 2004-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * base64.h --
 *
 *      Functions to base64 encode/decode buffers. Implemented in
 *      lib/misc/base64.c.
 */

#ifndef _BASE64_H
#define _BASE64_H

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

Bool Base64_Encode(uint8 const *src, size_t srcLength,
                   char *target, size_t targSize,
                   size_t *dataLength);
Bool Base64_Decode(char const *src,
                   uint8 *target, size_t targSize,
                   size_t *dataLength);
Bool Base64_ChunkDecode(char const *src, size_t inSize,
                        uint8 *target, size_t targSize,
                        size_t *dataLength);
Bool Base64_ValidEncoding(char const *src, size_t srcLength);
size_t Base64_EncodedLength(uint8 const *src, size_t srcLength);
size_t Base64_DecodedLength(char const *src, size_t srcLength);
Bool Base64_EasyEncode(const uint8 *src, size_t srcLength, char **target);
Bool Base64_EasyDecode(const char *src, uint8 **target, size_t *targSize);
Bool Base64_DecodeFixed(const char *src, char *outBuf, size_t outBufSize);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
  070701000000A6000081A4000000000000000000000001682255050000011F000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/buildNumber.h  #define BUILD_NUMBER \
	"build-24697584"
#define BUILD_NUMBER_NUMERIC \
	24697584
#define BUILD_NUMBER_NUMERIC_STRING \
	"24697584"
#define PRODUCT_BUILD_NUMBER \
	"product-build-52591"
#define PRODUCT_BUILD_NUMBER_NUMERIC \
	52591
#define PRODUCT_BUILD_NUMBER_NUMERIC_STRING \
	"52591"
 070701000000A7000081A400000000000000000000000168225505000005F6000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/capsProvider.h /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * capsProvider.h --
 *
 *      Interface implemented by dnd manager objects to obtain their
 *      capabilities. Mainly needed by Windows host and guest code.
 */

#ifndef __CAPS_PROVIDER_H__
#define __CAPS_PROVIDER_H__

#if defined VMX86_TOOLS || COMPILE_WITHOUT_CUI
#   ifdef LIB_EXPORT
#   undef LIB_EXPORT
#   endif
#define LIB_EXPORT
#else
#include "libExport.hh"
#endif

#if defined(SWIG)
class CapsProvider
#else
class LIB_EXPORT CapsProvider
#endif
{
public:
   virtual ~CapsProvider() {};
   virtual Bool CheckCapability(uint32 caps) = 0;
   virtual uint64 GetDnDSizeThreshold() { return 0; } // 0 means no size control
};

#endif

  070701000000A8000081A40000000000000000000000016822550500002D0D000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/circList.h /*********************************************************
 * Copyright (C) 1998-2017,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 *   circList.h --
 *
 * macros, prototypes and struct definitions for double-linked
 * circular lists.
 */

#ifndef _CIRCLIST_H_
#define _CIRCLIST_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include "vmware.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef struct ListItem {
   struct ListItem *prev;
   struct ListItem *next;
} ListItem;


/*
 *----------------------------------------------------------------------
 *
 * CircList_IsEmpty --
 *
 *      A NULL list is an empty list.
 *
 * Result:
 *      TRUE if list is empty, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
CircList_IsEmpty(const ListItem *item)  // IN
{
   return item == NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_InitItem --
 *
 *      Initialize item as a single-element circular list.
 *
 * Result:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
CircList_InitItem(ListItem *item)  // OUT
{
   item->prev = item->next = item;
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_First --
 *
 *      Return first item in the list.
 *
 * Result:
 *      First item.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE ListItem *
CircList_First(ListItem *item)  // IN
{
   return item;
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_Last --
 *
 *      Return last item in the list.
 *
 * Result:
 *      Last item.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE ListItem *
CircList_Last(ListItem *item)
{
   return item->prev;
}


/*
 * CIRC_LIST_CONTAINER - get the struct for this entry (like list_entry)
 * @ptr: the &struct ListItem pointer.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list struct within the struct.
 */
#define CIRC_LIST_CONTAINER(ptr, type, member) \
   VMW_CONTAINER_OF(ptr, type, member)
/* 
 * Historical name, left here to reduce churn.
 * TODO: remove, all LIST_CONTAINER uses should be
 * VMW_CONTAINER_OF and stop depending on circList.h
 * to provide the definition.
 */
#define LIST_CONTAINER(ptr, type, member) VMW_CONTAINER_OF(ptr, type, member)

/*
 * LIST_SCAN_FROM scans the list from "from" up until "until".
 * The loop variable p should not be destroyed in the process.
 * "from" is an element in the list where to start scanning.
 * "until" is the element where search should stop.
 * member is the field to use for the search - either "next" or "prev".
 */
#define CIRC_LIST_SCAN_FROM(p, from, until, member)   \
   for (p = (from); (p) != NULL;   \
      (p) = (((p)->member == (until)) ? NULL : (p)->member))

/* scan the entire list (non-destructively) */
#define CIRC_LIST_SCAN(p, l)   \
   CIRC_LIST_SCAN_FROM(p, CircList_First(l), CircList_First(l), next)


/* scan the entire list where loop element may be destroyed */
#define CIRC_LIST_SCAN_SAFE(p, pn, l)   \
   if (!CircList_IsEmpty(l))  \
      for (p = (l), (pn) = CircList_Next(p, l); (p) != NULL;   \
           (p) = (pn), (pn) = CircList_Next(p, l))

/* scan the entire list backwards where loop element may be destroyed */
#define CIRC_LIST_SCAN_BACK_SAFE(p, pn, l)   \
   if (!CircList_IsEmpty(l))  \
      for (p = CircList_Last(l), (pn) = CircList_Prev(p, l); (p) != NULL;   \
           (p) = (pn), (pn) = CircList_Prev(p, l))


/*
 *----------------------------------------------------------------------
 *
 * CircList_Next --
 *
 *      Returns the next member of a doubly linked list, or NULL if last.
 *      Assumes: p is member of the list headed by head.
 *
 * Result:
 *      If head or p is NULL, return NULL. Otherwise,
 *      next list member (or null if last).
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE ListItem *
CircList_Next(ListItem *p,        // IN
              ListItem *head)     // IN
{
   if (head == NULL || p == NULL) {
      return NULL;
   }
   /* both p and head are non-null */
   p = p->next;
   return p == head ? NULL : p;
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_Prev --
 *
 *      Returns the prev member of a doubly linked list, or NULL if first.
 *      Assumes: p is member of the list headed by head.
 *
 * Result:
 *      If head or prev is NULL, return NULL. Otherwise,
 *      prev list member (or null if first).
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE ListItem *
CircList_Prev(ListItem *p,        // IN
              ListItem *head)     // IN
{
   if (head == NULL || p == NULL) {
      return NULL;
   }
   /* both p and head are non-null */
   return p == head ? NULL : p->prev;
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_DeleteItem --
 *
 *      Deletes a member of a doubly linked list, possibly modifies the
 *      list header itself.
 *      Assumes neither p nor headp is null and p is a member of *headp.
 *
 * Result:
 *      None
 *
 * Side effects:
 *      Modifies *headp.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
CircList_DeleteItem(ListItem *p,         // IN
                    ListItem **headp)    // IN/OUT
{
   ListItem *next;

   ASSERT(p != NULL);
   ASSERT(headp != NULL);

   next = p->next;
   if (p == next) {
      *headp = NULL;
   } else {
      next->prev = p->prev;
      p->prev->next = next;
      if (*headp == p) {
         *headp = next;
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_Queue --
 *
 *      Adds a new member to the back of a doubly linked list (queue)
 *      Assumes neither p nor headp is null and p is not a member of *headp.
 *
 * Result:
 *      None
 *
 * Side effects:
 *      Modifies *headp.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
CircList_Queue(ListItem *p,              // IN
               ListItem **headp)         // IN/OUT
{
   ListItem *head;

   head = *headp;
   if (CircList_IsEmpty(head)) {
      CircList_InitItem(p);
      *headp = p;
   } else {
      p->prev = head->prev;
      p->next = head;
      p->prev->next = p;
      head->prev = p;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_Push --
 *
 *      Adds a new member to the front of a doubly linked list (stack)
 *      Assumes neither p nor headp is null and p is not a member of *headp.
 *
 * Result:
 *      None
 *
 * Side effects:
 *      Modifies *headp.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
CircList_Push(ListItem *p,               // IN
              ListItem **headp)          // IN/OUT
{
   CircList_Queue(p, headp);
   *headp = p;
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_InsertAfter --
 *
 *      Adds a new member to the list after the provided item.  Assumes p
 *      is not a member of a list already.
 *
 * Result:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
CircList_InsertAfter(ListItem *p,      // IN:
                     ListItem *after)  // IN:
{
   p->prev = after;
   p->next = after->next;
   p->next->prev = p;
   after->next = p;
}


/*
 *----------------------------------------------------------------------
 *
 * CircList_Splice --
 *
 *      Make a single list {l1 l2} from {l1} and {l2} and return it.
 *      It is okay for one or both lists to be NULL.
 *      No checking is done. It is assumed that l1 and l2 are two
 *      distinct lists.
 *
 * Result:
 *      A list { l1 l2 }.
 *
 * Side effects:
 *      Modifies l1 and l2 list pointers.
 *
 *----------------------------------------------------------------------
 */

static INLINE ListItem *
CircList_Splice(ListItem *l1,      // IN
                ListItem *l2)      // IN
{
   ListItem *l1Last, *l2Last;

   if (CircList_IsEmpty(l1)) {
      return l2;
   }

   if (CircList_IsEmpty(l2)) {
      return l1;
   }

   l1Last = l1->prev;   /* last elem of l1 */
   l2Last = l2->prev;   /* last elem of l2 */

   /*
    *    l1 -> ... -> l1Last    l2 -> ... l2Last
    */
   l1Last->next = l2;
   l2->prev = l1Last;

   l1->prev = l2Last;
   l2Last->next = l1;

   return l1;
}


#if 0  /* Presently unused, enable if a use is found */
/*
 *----------------------------------------------------------------------
 *
 * CircList_Split --
 *
 *      Make a list l = {l1 l2} into two separate lists {l1} and {l2}, where:
 *      l = { ... x -> p -> ... } split into:
 *      l1 = { ... -> x }
 *      l2 = { p -> ... }
 *      Assumes neither p nor l is null and p is a member of l.
 *      If p is the first element of l, then l1 will be NULL.
 *
 * Result:
 *      None.
 *
 * Side effects:
 *      Sets *l1p and *l2p to the resulting two lists.
 *      Modifies l's pointers.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
CircList_Split(ListItem *p,         // IN
               ListItem *l,         // IN
               ListItem **l1p,      // OUT
               ListItem **l2p)      // OUT
{
   ListItem *last;

   if (p == CircList_First(l)) {   /* first element */
      *l1p = NULL;
      *l2p = l;
      return;
   }

   last = l->prev;

   *l1p = l;
   p->prev->next = l;
   l->prev = p->prev;

   *l2p = p;
   p->prev = last;
   last->next = p;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * CircList_Size --
 *
 *	Return the number of items in the list.
 *
 * Result:
 *	The number of items in the list.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE int
CircList_Size(ListItem *head)     // IN
{
   ListItem *li;
   int ret = 0;

   CIRC_LIST_SCAN(li, head) {
      ret++;
   }
   return ret;
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _CIRCLIST_H_ */
   070701000000A9000081A40000000000000000000000016822550500002908000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/clamped.h  /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * clamped.h --
 *
 *      Clamped arithmetic. This header file provides inline
 *      arithmetic operations that don't overflow. Instead, they
 *      saturate at the data type's max or min value.
 */

#ifndef _CLAMPED_H_
#define _CLAMPED_H_

#include "vm_basic_types.h"
#include "vm_assert.h"
#include "vm_basic_defs.h"

#if defined __cplusplus
extern "C" {
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_U64To32 --
 *
 *      Convert unsigned 64-bit to 32-bit, clamping instead of truncating.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_UINT32, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_U64To32(uint32 *out,  // OUT
                uint64 a)     // IN
{
   uint32 clamped = (uint32)a;

   if (UNLIKELY(a != clamped)) {
      *out = MAX_UINT32;
      return FALSE;
   }

   *out = clamped;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_S64To32 --
 *
 *      Convert signed 64-bit to 32-bit, clamping instead of truncating.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_INT32 or MIN_INT32, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_S64To32(int32 *out,  // OUT
                int64 a)     // IN
{
   int32 clamped = (int32)a;

   if (UNLIKELY(a != clamped)) {
      *out = a < 0 ? MIN_INT32 : MAX_INT32;
      return FALSE;
   }

   *out = clamped;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_S32To16 --
 *
 *      Convert signed 32-bit to 16-bit, clamping instead of truncating.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_INT16 or MIN_INT16, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_S32To16(int16 *out,  // OUT
                int32 a)     // IN
{
   int16 clamped = (int16)a;

   if (UNLIKELY(a != clamped)) {
      *out = a < 0 ? MIN_INT16 : MAX_INT16;
      return FALSE;
   }

   *out = clamped;
   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * Clamped_SAdd32 --
 *
 *      Signed 32-bit addition.
 *
 *      Add two integers, clamping the result to MAX_INT32 or
 *      MIN_INT32 if it would have overflowed.
 *
 * Results:
 *      On success, returns TRUE.
 *      If the result would have overflowed and we clamped it, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Clamped_SAdd32(int32 *out,  // OUT
               int32 a,     // IN
               int32 b)     // IN
{
   return Clamped_S64To32(out, (int64)a + b);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_UMul32 --
 *
 *      Unsigned 32-bit multiplication.
 *
 *      We're abusing the fact that 32x32-bit multiplication always
 *      returns a 64-bit result on x86 anyway, and that the compiler
 *      should be smart enough to optimize the code here into a
 *      32x32-bit multiply.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_UINT32, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_UMul32(uint32 *out,  // OUT
               uint32 a,     // IN
               uint32 b)     // IN
{
   return Clamped_U64To32(out, (uint64)a * b);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_SMul32 --
 *
 *      Signed 32-bit multiplication.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_INT32 or MIN_INT32, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_SMul32(int32 *out,  // OUT
               int32 a,     // IN
               int32 b)     // IN
{
   return Clamped_S64To32(out, (int64)a * b);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_UAdd32 --
 *
 *      Unsigned 32-bit addition.
 *
 *      This is a utility function for 32-bit unsigned addition,
 *      in which the result is clamped to MAX_UINT32 on overflow.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_UINT32, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_UAdd32(uint32 *out,  // OUT
               uint32 a,     // IN
               uint32 b)     // IN
{
   uint32 c = a + b;

   /*
    * Checking against one src operand is sufficient.
    */
   if (UNLIKELY(c < a)) {
      *out = MAX_UINT32;
      return FALSE;
   }

   *out = c;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_UAdd64 --
 *
 *      Unsigned 64-bit addition.
 *
 *      This is a utility function for 64-bit unsigned addition,
 *      in which the result is clamped to MAX_UINT64 on overflow.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_UINT64, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_UAdd64(uint64 *out,   // OUT
               uint64 a,      // IN
               uint64 b)      // IN
{
   uint64 c = a + b;

   /*
    * Checking against one src operand is sufficient.
    */
   if (UNLIKELY(c < a)) {
      *out = MAX_UINT64;
      return FALSE;
   }

   *out = c;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_URoundUpBits32 --
 *
 *      Round up an unsigned 32-bit number by the specified number
 *      of bits.  Clamp to MAX_UINT32 on overflow.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_UINT32, returns FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_URoundUpBits32(uint32 *out,  // OUT
                       uint32 x,     // IN
                       uint32 bits)  // IN
{
   uint32 mask = (1 << bits) - 1;
   uint32 c = (x + mask) & ~mask;

   ASSERT(bits < sizeof(uint32) * 8);

   if (UNLIKELY(x + mask < x)) {
      *out = MAX_UINT32;
      return FALSE;
   }

   *out = c;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_UMul64 --
 *
 *      Unsigned 64-bit multiplication.
 *
 * Results:
 *      On success, returns TRUE. If the result would have overflowed
 *      and we clamped it to MAX_UINT64, returns FALSE.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_UMul64(uint64 *out,  // OUT
               uint64 a,     // IN
               uint64 b)     // IN
{
   uint32 aL = a & MASK64(32);
   uint32 aH = a >> 32;
   uint32 bL = b & MASK64(32);
   uint32 bH = b >> 32;

   ASSERT(out != NULL);

   if (UNLIKELY(aH > 0 && bH > 0)) {
      *out = MAX_UINT64;
      return FALSE;
   } else {
      uint64 s1 = (aH * (uint64)bL) << 32;
      uint64 s2 = (aL * (uint64)bH) << 32;
      uint64 s3 = (aL * (uint64)bL);
      uint64 sum;
      Bool clamped;

      ASSERT(s1 == 0 || s2 == 0);
      sum = s1 + s2;
      clamped = !Clamped_UAdd64(&sum, sum, s3);

      *out = sum;
      return !clamped;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Clamped_USub64 --
 *
 *      Unsigned 64-bit subtraction.
 *      Compute (a - b), and clamp to 0 if the result would have underflowed.
 *
 * Results:
 *      On success, returns TRUE. If the result would have underflowed
 *      and we clamped it to 0, returns FALSE.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Clamped_USub64(uint64 *out,  // OUT
               uint64 a,     // IN
               uint64 b)     // IN
{
   ASSERT(out != NULL);

   if (UNLIKELY(b > a)) {
      *out = 0;
      return FALSE;
   } else {
      *out = a - b;
      return TRUE;
   }
}


#if defined __cplusplus
} // extern "C"
#endif

#endif // ifndef _CLAMPED_H_
070701000000AA000081A4000000000000000000000001682255050000463C000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/codeset.h  /* **********************************************************
 * Copyright (c) 2007-2022 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * codeset.h --
 *
 *      UTF-16 handling macros. Based on utf16.h from ICU 1.8.1.
 *
 *      ICU 1.8.1 license follows:
 *
 *      ICU License - ICU 1.8.1 and later
 *
 *      COPYRIGHT AND PERMISSION NOTICE
 *
 *      Copyright (c) 1995-2006 International Business Machines Corporation
 *      and others
 *
 *      All rights reserved.
 *
 *           Permission is hereby granted, free of charge, to any
 *      person obtaining a copy of this software and associated
 *      documentation files (the "Software"), to deal in the Software
 *      without restriction, including without limitation the rights
 *      to use, copy, modify, merge, publish, distribute, and/or sell
 *      copies of the Software, and to permit persons to whom the
 *      Software is furnished to do so, provided that the above
 *      copyright notice(s) and this permission notice appear in all
 *      copies of the Software and that both the above copyright
 *      notice(s) and this permission notice appear in supporting
 *      documentation.
 *
 *           THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
 *      KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 *      WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 *      PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT
 *      SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
 *      BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR
 *      CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
 *      FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 *      CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *      OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *           Except as contained in this notice, the name of a
 *      copyright holder shall not be used in advertising or otherwise
 *      to promote the sale, use or other dealings in this Software
 *      without prior written authorization of the copyright holder.
 */
#ifndef __CODESET_H__
#   define __CODESET_H__

#include "vm_basic_types.h"
#include "vm_assert.h"
#include "dynbuf.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * These platforms use UTF-8 (or pretend to):
 *   FreeBSD: really UTF-8
 *   ESX: UTF-8 by policy decree
 *   Mac: really UTF-8
 */

#if defined(__FreeBSD__) || \
    defined(VMX86_SERVER) || \
    defined(__APPLE__) || \
    defined __ANDROID__
#define CURRENT_IS_UTF8
#endif

/*
 * Guard these defines, borrowed from ICU's utf16.h, so that source files
 * can include both.
 */

#ifndef __UTF16_H__

/**
 * Is this code point a surrogate (U+d800..U+dfff)?
 * @param c 32-bit code point
 * @return TRUE or FALSE
 * @stable ICU 2.4
 */
#define U_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800)

/**
 * Does this code unit alone encode a code point (BMP, not a surrogate)?
 * @param c 16-bit code unit
 * @return TRUE or FALSE
 * @stable ICU 2.4
 */
#define U16_IS_SINGLE(c) (!U_IS_SURROGATE(c))

/**
 * Is this code unit a lead surrogate (U+d800..U+dbff)?
 * @param c 16-bit code unit
 * @return TRUE or FALSE
 * @stable ICU 2.4
 */
#define U16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)

/**
 * Is this code unit a trail surrogate (U+dc00..U+dfff)?
 * @param c 16-bit code unit
 * @return TRUE or FALSE
 * @stable ICU 2.4
 */
#define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)

/**
 * Is this code unit a surrogate (U+d800..U+dfff)?
 * @param c 16-bit code unit
 * @return TRUE or FALSE
 * @stable ICU 2.4
 */
#define U16_IS_SURROGATE(c) U_IS_SURROGATE(c)

/**
 * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)),
 * is it a lead surrogate?
 * @param c 16-bit code unit
 * @return TRUE or FALSE
 * @stable ICU 2.4
 */
#define U16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)

/**
 * Helper constant for U16_GET_SUPPLEMENTARY.
 * @internal
 */
#define U16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000)

/**
 * Get a supplementary code point value (U+10000..U+10ffff)
 * from its lead and trail surrogates.
 * The result is undefined if the input values are not
 * lead and trail surrogates.
 *
 * @param lead lead surrogate (U+d800..U+dbff)
 * @param trail trail surrogate (U+dc00..U+dfff)
 * @return supplementary code point (U+10000..U+10ffff)
 * @stable ICU 2.4
 */
#define U16_GET_SUPPLEMENTARY(lead, trail) \
   (((uint32)(lead)<<10UL)+(uint32)(trail)-U16_SURROGATE_OFFSET)


/**
 * Get the lead surrogate (0xd800..0xdbff) for a
 * supplementary code point (0x10000..0x10ffff).
 * @param supplementary 32-bit code point (U+10000..U+10ffff)
 * @return lead surrogate (U+d800..U+dbff) for supplementary
 * @stable ICU 2.4
 */
#define U16_LEAD(supplementary) ((utf16_t)(((supplementary)>>10)+0xd7c0))

/**
 * Get the trail surrogate (0xdc00..0xdfff) for a
 * supplementary code point (0x10000..0x10ffff).
 * @param supplementary 32-bit code point (U+10000..U+10ffff)
 * @return trail surrogate (U+dc00..U+dfff) for supplementary
 * @stable ICU 2.4
 */
#define U16_TRAIL(supplementary) ((utf16_t)(((supplementary)&0x3ff)|0xdc00))

/**
 * How many 16-bit code units are used to encode this Unicode code point? (1 or 2)
 * The result is not defined if c is not a Unicode code point (U+0000..U+10ffff).
 * @param c 32-bit code point
 * @return 1 or 2
 * @stable ICU 2.4
 */
#define U16_LENGTH(c) ((uint32)(c)<=0xffff ? 1 : 2)

/**
 * The maximum number of 16-bit code units per Unicode code point (U+0000..U+10ffff).
 * @return 2
 * @stable ICU 2.4
 */
#define U16_MAX_LENGTH 2

/**
 * Get a code point from a string at a code point boundary offset,
 * and advance the offset to the next code point boundary.
 * (Post-incrementing forward iteration.)
 * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
 *
 * The offset may point to the lead surrogate unit
 * for a supplementary code point, in which case the macro will read
 * the following trail surrogate as well.
 * If the offset points to a trail surrogate or
 * to a single, unpaired lead surrogate, then that itself
 * will be returned as the code point.
 *
 * @param s const utf16_t * string
 * @param i string offset, must be i<length
 * @param length string length
 * @param c output uint32 variable
 * @see U16_NEXT_UNSAFE
 * @stable ICU 2.4
 */
#define U16_NEXT(s, i, length, c) { \
      (c)=(s)[(i)++]; \
      if(U16_IS_LEAD(c)) { \
         utf16_t __c2; \
         if((i)<(length) && U16_IS_TRAIL(__c2=(s)[(i)])) { \
            ++(i); \
            (c)=U16_GET_SUPPLEMENTARY((c), __c2); \
         } \
      } \
   }

/**
 * Move the string offset from one code point boundary to the previous one
 * and get the code point between them.
 * (Pre-decrementing backward iteration.)
 * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
 *
 * The input offset may be the same as the string length.
 * If the offset is behind a trail surrogate unit
 * for a supplementary code point, then the macro will read
 * the preceding lead surrogate as well.
 * If the offset is behind a lead surrogate or behind a single, unpaired
 * trail surrogate, then that itself
 * will be returned as the code point.
 *
 * @param s const UChar * string
 * @param start starting string offset (usually 0)
 * @param i string offset, must be start<i
 * @param c output UChar32 variable
 * @see U16_PREV_UNSAFE
 * @stable ICU 2.4
 */
#define U16_PREV(s, start, i, c) { \
      (c)=(s)[--(i)]; \
      if(U16_IS_TRAIL(c)) { \
         utf16_t __c2; \
         if((i)>(start) && U16_IS_LEAD(__c2=(s)[(i)-1])) { \
            --(i); \
            (c)=U16_GET_SUPPLEMENTARY(__c2, (c)); \
         } \
      } \
   }

#endif // __UTF16_H__


/*
 * Use this instead of "UTF-16" to specify UTF-16 in native byte order.
 */

#define CODESET_NATIVE_UTF16 "UTF-16LE"


/*
 * Flags for conversion functions
 */

#define CSGTG_NORMAL    0x0000  /* Without any information loss. */
#define CSGTG_TRANSLIT  0x0001  /* Transliterate unknown characters. */
#define CSGTG_IGNORE    0x0002  /* Skip over untranslatable characters. */

/*
 * XXX -- this function is a temporary fix. It should be removed once we fix
 *        the 3rd party library pathname issue.
 */
char *
CodeSet_GetAltPathName(const utf16_t *pathW); // IN


Bool
CodeSet_Init(const char *icuDataDir); // IN: ICU datafile directory in current page,
                                      // can be NULL.

void
CodeSet_DontUseIcu(void);

Bool
CodeSet_GenericToGenericDb(const char *codeIn,  // IN
                           const char *bufIn,   // IN
                           size_t sizeIn,       // IN
                           const char *codeOut, // IN
                           unsigned int flags,  // IN
                           DynBuf *db);         // IN/OUT

Bool
CodeSet_GenericToGeneric(const char *codeIn,  // IN
                         const char *bufIn,   // IN
                         size_t sizeIn,       // IN
                         const char *codeOut, // IN
                         unsigned int flags,  // IN
                         char **bufOut,       // IN/OUT
                         size_t *sizeOut);    // IN/OUT

Bool
CodeSet_Utf8ToCurrent(const char *bufIn,   // IN
                      size_t sizeIn,       // IN
                      char **bufOut,       // OUT
                      size_t *sizeOut);    // OUT

Bool
CodeSet_CurrentToUtf8(const char *bufIn,  // IN
                      size_t sizeIn,      // IN
                      char **bufOut,      // OUT
                      size_t *sizeOut);   // OUT

Bool
CodeSet_Utf16leToUtf8Db(const char *bufIn,   // IN
                        size_t sizeIn,       // IN
                        DynBuf *db);         // IN

Bool
CodeSet_Utf16leToUtf8(const char *bufIn,   // IN
                      size_t sizeIn,       // IN
                      char **bufOut,       // OUT
                      size_t *sizeOut);    // OUT/OPT

Bool
CodeSet_Utf8ToUtf16le(const char *bufIn,   // IN
                      size_t sizeIn,       // IN
                      char **bufOut,       // OUT
                      size_t *sizeOut);    // OUT/OPT

Bool
CodeSet_CurrentToUtf16le(const char *bufIn,   // IN
                         size_t sizeIn,       // IN
                         char **bufOut,       // OUT
                         size_t *sizeOut);    // OUT/OPT

Bool
CodeSet_Utf16leToCurrent(const char *bufIn,   // IN
                         size_t sizeIn,       // IN
                         char **bufOut,       // OUT
                         size_t *sizeOut);    // OUT/OPT

Bool
CodeSet_Utf16beToCurrent(const char *bufIn,   // IN
                         size_t sizeIn,       // IN
                         char **bufOut,       // OUT
                         size_t *sizeOut);    // OUT/OPT

Bool
CodeSetOld_Utf8Normalize(const char *bufIn,     // IN
                         size_t sizeIn,         // IN
                         Bool precomposed,      // IN
                         DynBuf *db);           // OUT

Bool
CodeSet_Utf8FormDToUtf8FormC(const char *bufIn,     // IN
                             size_t sizeIn,         // IN
                             char **bufOut,         // OUT
                             size_t *sizeOut);      // OUT/OPT

Bool
CodeSet_Utf8FormCToUtf8FormD(const char *bufIn,     // IN
                             size_t sizeIn,         // IN
                             char **bufOut,         // OUT
                             size_t *sizeOut);      // OUT/OPT

const char *
CodeSet_GetCurrentCodeSet(void);

Bool
CodeSet_IsEncodingSupported(const char *name);  // IN:

Bool
CodeSet_Validate(const char *buf,   // IN: the string
                 size_t size,	    // IN: length of string
                 const char *code); // IN: encoding

Bool CodeSet_UTF8ToUTF32(const char *utf8,  // IN:
                         char **utf32);     // OUT:

Bool CodeSet_UTF32ToUTF8(const char *utf32,  // IN:
                         char **utf8);       // OUT:

int CodeSet_LengthInCodePoints(const char *utf8);  // IN:

int CodeSet_CodePointOffsetToByteOffset(const char *utf8,      // IN:
                                        int codePointOffset);  // IN:

int CodeSet_GetUtf8(const char *string,  // IN:
                    const char *end,     // IN:
                    uint32 *uchar);      // OUT/OPT:

Bool CodeSet_IsValidUTF8(const char *bufIn,  // IN:
                         size_t sizeIn);     // IN:

Bool CodeSet_IsStringValidUTF8(const char *string);  // IN:

Bool CodeSet_IsValidUTF8String(const char *bufIn,  // IN:
                               size_t sizeIn);     // IN:

char *CodeSet_JsonEscape(const char *utf8);  // IN:

char *CodeSet_JsonUnescape(const char *utf8);  // IN:

/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8ToUtf16 --
 *
 *      A convenience wrapper that accepts a NUL-terminated UTF-8 string
 *      and returns an allocated UTF-16 (LE) string. ASSERTs on failure.
 *
 * Results:
 *      The allocted UTF-16 (LE) string, free with free().
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE utf16_t *
CodeSet_Utf8ToUtf16(const char *str)  // IN:
{
   utf16_t *strW;

   if (!CodeSet_Utf8ToUtf16le(str, strlen(str), (char **) &strW, NULL)) {
      NOT_IMPLEMENTED();
   }

   return strW;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf16ToUtf8 --
 *
 *      A convenience wrapper that accepts a NUL-terminated UTF-16 (LE)
 *      string and returns an allocated UTF-8 string. ASSERTs on failure.
 *
 * Results:
 *      The allocted UTF-8 string, free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
CodeSet_Utf16ToUtf8(const utf16_t *strW)  // IN:
{
   char *str;
   size_t len;

   for (len = 0; strW[len]; len++)
      ;

   if (!CodeSet_Utf16leToUtf8((const char *) strW, len * sizeof strW[0],
                              (char **) &str, NULL)) {
      NOT_IMPLEMENTED();
   }

   return str;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8FindCodePointBoundary
 *
 *      Determine if buf[offset] is a valid UTF-8 code point boundary
 *      and find the previous boundary if it is not. The contents of
 *      buf[offset] need not be defined, only data prior to this
 *      location is examined. Useful for finding a suitable place to
 *      put a NUL terminator.
 *
 * Results:
 *
 *      Returns the offset of the byte immediately following the last
 *      complete UTF-8 code point in buf that is entirely within the
 *      range [0, offset-1]. Note that if the final UTF-8 code point
 *      is complete, the input offset will be returned unchanged.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE size_t
CodeSet_Utf8FindCodePointBoundary(const char *buf,   // IN
                                  size_t offset)     // IN
{
   size_t origOffset = offset;

   if (offset > 0) {

      signed char c;
      /*
       * Back up 1 byte and then find the start of the UTF-8 code
       * point occupying that location.
       */

      offset--;
      while (offset > 0 && (buf[offset] & 0xc0) == 0x80) {
         offset--;
      }

      /*
       * Maximum UTF-8 code point length is 4
       */

      ASSERT(origOffset - offset <= 4);

      c = buf[offset];

      /*
       * The first byte of a UTF-8 code point needs to be one of
       * 0b0XXXXXXX, 0b110XXXXX, 0b1110XXXX, 0b11110XXX
       */

      ASSERT(c >= 0 || (c >> 5) == -2 || (c >> 4) == -2 || (c >> 3) == -2);

      /*
       * offset now points to the start of a UTF-8 code point. If it
       * is a single byte or if the length, as encoded in the first
       * byte, matches the number of bytes we have backed up, then the
       * entire code point is present, so the original offset is a
       * valid code point starting offset.
       *
       * Length is encoded as
       * 2 bytes: 0b110XXXXX
       * 3 bytes: 0b1110XXXX
       * 4 bytes: 0b11110XXX
       * Thus the first byte is -2 when shifted right (signed) by
       * (7 - length).
       */

      if (c >= 0 || (c >> (7 - origOffset + offset)) == -2) {
         return origOffset;
      }

      /*
       * Else we truncated a code point. Return its starting point.
       */
   }
   return offset;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf16FindCodePointBoundary
 *
 *      Determine if buf[offset] is a valid UTF-16 code point boundary
 *      and find the previous boundary if it is not. The contents of
 *      buf[offset] need not be defined, only data prior to this
 *      location is examined. Useful for finding a suitable place to
 *      put a NUL terminator.
 *
 * Results:
 *
 *      Returns the offset of the byte immediately following the last
 *      complete UTF-16 code point in buf that is entirely within the
 *      range [0, offset-1]. Note that if the final UTF-16 code point
 *      is complete, the input offset will be returned unchanged.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE size_t
CodeSet_Utf16FindCodePointBoundary(const char *buf, // IN
                                   size_t offset)   // IN
{
   size_t origOffset;
   const utf16_t *utf16Buf = (const utf16_t *)buf;

   origOffset = offset / 2;
   offset = origOffset - 1;

   if (origOffset > 0 && U16_IS_LEAD(utf16Buf[offset])) {
      return offset * 2;
   }

   return origOffset * 2;
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* __CODESET_H__ */
070701000000AB000081A40000000000000000000000016822550500000A15000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/community_source.h /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * community_source.h --
 *
 *    Macros for excluding source code from community.
 */

#ifndef _COMMUNITY_SOURCE_H_
#define _COMMUNITY_SOURCE_H_

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

/* 
 * 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_AMD_SECRET
#if !defined(COMMUNITY_SOURCE) || defined(AMD_SOURCE)
/*
 * It's ok to include AMD_SECRET source code for non-Community Source,
 * or for drops directed at AMD.
 */
   #define COMMUNITY_SOURCE_AMD_SECRET
#endif

#undef COMMUNITY_SOURCE_INTEL_SECRET
#if !defined(COMMUNITY_SOURCE) || defined(INTEL_SOURCE)
/*
 * It's ok to include INTEL_SECRET source code for non-Community Source,
 * or for drops directed at Intel.
 */
   #define COMMUNITY_SOURCE_INTEL_SECRET
#endif

#endif
   070701000000AC000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/compat 070701000000AD000081A40000000000000000000000016822550500000A0E000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/compat/compat_stdarg.h /*********************************************************
 * Copyright (C) 2006-2016,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * compat_stdarg.h --
 *
 *    Compatibility defines for systems that need the stdarg features. If your program
 *    needs va_init, va_copy, va_end, etc. then include this file instead of including
 *    stdarg.h directly.
 *
 *    Note that the header guard for this file does not follow typical naming
 *    convention as _COMPAT_STDARG_H conflicts with emscripten's compat/stdarg.h
 */

#ifndef _VMWARE_COMPAT_STDARG_H
#define _VMWARE_COMPAT_STDARG_H 1

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include <stdarg.h>

#if !defined(va_copy)
#   if defined(__va_copy)
#      define va_copy __va_copy
#   elif defined(_WIN32)
#      define va_copy(ap1, ap2) (*(ap1) = *(ap2))
#   elif defined(VA_LIST_IS_ARRAY)
#      define va_copy(ap1, ap2) memcpy(ap1, ap2, sizeof(va_list))
#   else
#      define va_copy(ap1, ap2) ((ap1) = (ap2))
#   endif
#endif

#endif /* _VMWARE_COMPAT_STDARG_H */
  070701000000AE000081A40000000000000000000000016822550500005D74000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/conf.h /*********************************************************
 * Copyright (c) 2002-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * conf.h --
 *
 *    Manage the tools configuration file.
 *
 */


#ifndef __CONF_H__
#define __CONF_H__

#define CONF_FILE         "tools.conf"

#if ! defined(_WIN32)
#   define CONFVAL_POWERONSCRIPT_DEFAULT  "poweron-vm-default"
#   define CONFVAL_POWEROFFSCRIPT_DEFAULT "poweroff-vm-default"
#   define CONFVAL_RESUMESCRIPT_DEFAULT   "resume-vm-default"
#   define CONFVAL_SUSPENDSCRIPT_DEFAULT  "suspend-vm-default"
#else
#   define CONFVAL_POWERONSCRIPT_DEFAULT  "poweron-vm-default.bat"
#   define CONFVAL_POWEROFFSCRIPT_DEFAULT "poweroff-vm-default.bat"
#   define CONFVAL_RESUMESCRIPT_DEFAULT   "resume-vm-default.bat"
#   define CONFVAL_SUSPENDSCRIPT_DEFAULT  "suspend-vm-default.bat"
#endif

#define CONFNAME_POWERONSCRIPT            "poweron-script"
#define CONFNAME_POWEROFFSCRIPT           "poweroff-script"
#define CONFNAME_RESUMESCRIPT             "resume-script"
#define CONFNAME_SUSPENDSCRIPT            "suspend-script"
#define CONFNAME_LOG                      "log"
#define CONFNAME_LOGFILE                  "log.file"
#define CONFNAME_LOGLEVEL                 "log.level"
#define CONFNAME_DISABLETOOLSVERSION      "disable-tools-version"
#define CONFNAME_HIDETOOLSVERSION         "hide-tools-version"
#define CONFNAME_DISABLEPMTIMERWARNING    "disable-pmtimerwarning"
#define CONFGROUPNAME_VMTOOLS             "vmtools"

/*
 ******************************************************************************
 * BEGIN AppInfo goodies.
 */

/**
 * Defines the string used for the AppInfo config file group.
 */
#define CONFGROUPNAME_APPINFO "appinfo"

/**
 * Define a custom AppInfo poll interval (in seconds).
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * poll interval.
 *
 * @param int   User-defined poll interval.  Set to 0 to disable polling.
 */
#define CONFNAME_APPINFO_POLLINTERVAL "poll-interval"

/**
 * Defines the configuration to publish the application information or not.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * value.
 *
 * @param boolean Set to TRUE to disable publishing.
 *                Set to FALSE to enable publishing.
 */
#define CONFNAME_APPINFO_DISABLED "disabled"

/**
 * Defines the configuration to remove duplicate applications.
 *
 * @param boolean Set to TRUE to remove duplicate apps.
 *                Set to FALSE to keep duplicate apps.
 */
#define CONFNAME_APPINFO_REMOVE_DUPLICATES "remove-duplicates"

/**
 * Defines the configuration to use the WMI for getting the application
 * version information.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * value.
 *
 * @param boolean Set to TRUE to use WMI.
 *                Set to FALSE to use native Win32 APIs.
 */
#define CONFNAME_APPINFO_USE_WMI "useWMI"

/*
 * END AppInfo goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN containerInfo goodies.
 */

/**
 * Defines the string used for the ContainerInfo config file group.
 */
#define CONFGROUPNAME_CONTAINERINFO "containerinfo"

/**
 * Define a custom ContainerInfo poll interval (in seconds).
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * poll interval.
 *
 * @param int   User-defined poll interval.  Set to 0 to disable polling.
 */
#define CONFNAME_CONTAINERINFO_POLLINTERVAL "poll-interval"

/**
 * Define the limit on the maximum number of containers to collect info from.
 * If the number of running containers exceeds the limit, only the most recently
 * created containers will be published.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * max container limit.
 *
 * @param int   User-defined max limit for # of containers queried.
 */
#define CONFNAME_CONTAINERINFO_LIMIT "max-containers"

/**
 * Defines the configuration to remove duplicate containers.
 *
 * @param boolean Set to TRUE to remove duplicate containers.
 *                Set to FALSE to keep duplicate containers.
 */
#define CONFNAME_CONTAINERINFO_REMOVE_DUPLICATES "remove-duplicates"

/**
 * Define the docker unix socket to use to communicate with the docker daemon.
 *
 * @note Illegal values result in a @c g_warning and fallback to docker's
 * default unix socket.
 *
 * @param string  absolute file path of the docker unix socket in use.
 */
#define CONFNAME_CONTAINERINFO_DOCKERSOCKET "docker-unix-socket"

/**
 * Define the containerd unix socket to connect with containerd gRPC server.
 * This should be used to get containers.
 *
 * @note Illegal values result in a @c g_warning and fallback to the containerd
 * default unix socket.
 *
 * @param string  absolute file path of the containerd unix socket in use.
 */
#define CONFNAME_CONTAINERINFO_CONTAINERDSOCKET "containerd-unix-socket"

/**
 * Define the list of namespaces to be queried for the running containers.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * list of namespaces.
 *
 * @param string  Comma separated list of namespaces to be queried.
 */
#define CONFNAME_CONTAINERINFO_ALLOWED_NAMESPACES "allowed-namespaces"

/*
 * END containerInfo goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN ServiceDiscovery goodies.
 */

/**
 * Defines the string used for the ServiceDiscovery config file group.
 */
#define CONFGROUPNAME_SERVICEDISCOVERY "servicediscovery"

/**
 * Defines the configuration to perform service discovery or not.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * value.
 *
 * @param boolean Set to TRUE to disable publishing.
 *                Set to FALSE to enable publishing.
 */
#define CONFNAME_SERVICEDISCOVERY_DISABLED "disabled"

/*
 * END ServiceDiscovery goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN ComponentMgr goodies.
 */

/**
 * Defines the current poll interval (in seconds).
 * This value is controlled by the componentMgr.poll-interval config file
 * option.
 */
#define COMPONENTMGR_CONF_POLLINTERVAL "poll-interval"

/**
 * Name of section of configuration to be read from tools.conf.
 */
#define COMPONENTMGR_CONF_GROUPNAME "componentmgr"

/**
 * Defines the components  managed by the componentMgr plugin.
 * This value is controlled by the componentMgr.included config file option.
 */
#define COMPONENTMGR_CONF_INCLUDEDCOMPONENTS "included"

/*
 * END ComponentMgr goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN GuestStore upgrader goodies.
 ******************************************************************************
 */

/**
 * Defines the string used for the GuestStore upgrade config file group.
 */
#define CONFGROUPNAME_GSUPGRADE "gueststoreupgrade"

/**
 * Defines the value for GuestStore upgrade feature to be enabled or not.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * value.
 *
 * @param string  Set to "off" no tools upgrade from GuestStore.
 *                Set to "manual" tools upgrade from GuestStore manual start.
 *                Set to "immediate" tools upgrade from GuestStore start
 *                autoCONFNAME_GSUPGRADE_RESOURCEmatically checking on the poll-interval frequency.
 *                Set to "powercycle" tools upgrade from GuestStore on system
 *                power on.
 */
#define CONFNAME_GSUPGRADE_POLICY "policy"

/**
 * Define a custom GuestStore check poll interval (in seconds).
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * poll interval.
 *
 * @param int   User-defined poll interval.  Set to 0 to disable polling.
 */
#define CONFNAME_GSUPGRADE_POLLINTERVAL "poll-interval"

/**
 * Define a custom GuestStore content path prefix.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * resource path.
 *
 * @param string  User-defined GuestStore resource path.
 */
#define CONFNAME_GSUPGRADE_RESOURCE "resource"

/**
 * Define a custom GuestStore content vmtools key.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * vmtools key.
 *
 * @param string  User-defined GuestStore vmtools key.
 *                Set to "vmtools" for the latest version.
 *                Suggested examples for earlier versions are:
 *                Set to "vmtools-11.0.0" for "vmtools-11.0.0/"
 *                Set to "vmtools-11.1.0" for "vmtools-11.1.0/"
 *                Set to "vmtools-11.2.0" for "vmtools-11.2.0/"
 *                Set to "vmtools-11.3.0" for "vmtools-11.3.0/"
 */
#define CONFNAME_GSUPGRADE_VMTOOLS_VERSION "vmtools-version-key"

/*
 * END GuestStore upgrader goodies.
 ******************************************************************************
 */


/*
 ******************************************************************************
 * BEGIN GlobalConf goodies.
 */

/**
 * Defines the string used for the GlobalConf config file group.
 */
#define CONFGROUPNAME_GLOBALCONF "globalconf"

/**
 * Defines the configuration to enable/disable the GlobalConf module.
 *
 * @note Illegal values result in @c g_warning and fallback to the default
 * value.
 *
 * @param boolean Set to TRUE to enable the module.
 *                Set to FALSE to disable the module.
 */
#define CONFNAME_GLOBALCONF_ENABLED "enabled"

/**
 * Define a custom GlobalConf poll interval (in seconds).
 *
 * @note Illegal values result in @c g_warning and fallback to the default
 * value.
 *
 * @param int   User-defined poll interval.
 */
#define CONFNAME_GLOBALCONF_POLL_INTERVAL "poll-interval"

/**
 * Define the global configuration resource in GuestStore.
 *
 * @note Illegal values result in @c g_warning and fallback to the default
 * value.
 *
 * @param string   Resource identifier in GuestStore.
 */
#define CONFNAME_GLOBALCONF_RESOURCE "resource"

/*
 * END GlobalConf goodies.
 ******************************************************************************
 */


/*
 ******************************************************************************
 * BEGIN GuestInfo goodies.
 */

/**
 * Defines the string used for the GuestInfo config file group.
 */
#define CONFGROUPNAME_GUESTINFO "guestinfo"

/**
 * Lets user disable just the perf monitor.
 */
#define CONFNAME_GUESTINFO_DISABLEPERFMON "disable-perf-mon"

/**
 * Lets user disable just DiskInfo.
 *
 * If thinking of deprecating this, please read bug 535343 first.
 */
#define CONFNAME_GUESTINFO_DISABLEQUERYDISKINFO "disable-query-diskinfo"

/**
 * Define a custom GuestInfo poll interval (in seconds).
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * poll interval.
 *
 * @param int   User-defined poll interval.  Set to 0 to disable polling.
 */
#define CONFNAME_GUESTINFO_POLLINTERVAL "poll-interval"

/**
 * Define a custom GuestStats poll interval (in seconds).
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * stats interval.
 *
 * @param int   User-defined poll interval for stats.  Set to 0 to disable polling.
 */
#define CONFNAME_GUESTINFO_STATSINTERVAL "stats-interval"

/**
 * Indicates whether stat results should be written to the log.
 */
#define CONFNAME_GUESTINFO_ENABLESTATLOGGING "enable-stat-logging"

/**
 * Set a comma separated list of network interface names that can be the
 * primary one
 *
 * @note interface names can use wildcards like '*' and '?'
 *
 * @param string comma separated list of interface name patterns.
 */

#define CONFNAME_GUESTINFO_PRIMARYNICS "primary-nics"

/**
 * Set a comma separated list of network interface names that have
 * low priority (so they will be sorted to the end).
 *
 * @note interface names can use wildcards like '*' and '?'
 *
 * @param string comma separated list of interface name patterns.
 */

#define CONFNAME_GUESTINFO_LOWPRIORITYNICS "low-priority-nics"

/**
 * Set a comma separated list of network interface names that shall be ignored.
 *
 * @note interface names can use wildcards like '*' and '?'
 *
 * @param string comma separated list of interface name patterns.
 */

#define CONFNAME_GUESTINFO_EXCLUDENICS "exclude-nics"

/**
 * Define a custom max IPv4 routes to gather.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * NICINFO_MAX_ROUTES.
 *
 * @param int   User-defined max routes with range [0, NICINFO_MAX_ROUTES].
 *              Set to 0 to disable gathering.
 */
#define CONFNAME_GUESTINFO_MAXIPV4ROUTES "max-ipv4-routes"

/**
 * Define a custom max IPv6 routes to gather.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * NICINFO_MAX_ROUTES.
 *
 * @param int   User-defined max routes with range [0, NICINFO_MAX_ROUTES].
 *              Set to 0 to disable gathering.
 */
#define CONFNAME_GUESTINFO_MAXIPV6ROUTES "max-ipv6-routes"

/**
 * Lets user include reserved space in diskInfo space metrics on Linux.
 *
 * @param boolean Set to true to include reserved space.
 */
#define CONFNAME_DISKINFO_INCLUDERESERVED "diskinfo-include-reserved"

/**
 * Report UUID of disks for vmdk mapping via vim.
 *
 * @param boolean Set to true to report UUID to VMX.
 */
#define CONFNAME_DISKINFO_REPORT_UUID "diskinfo-report-uuid"

/**
 * Avoid buggy USB driver when querying disks for UUID.
 * Turning this on can result in a major delay if the vmdk is
 * being replicated.  See PR 2575285, 26539.
 *
 * @param boolean Set to true to work around buggy 3rd party driver.
 */
#define CONFNAME_DISKINFO_DRIVER_WORKAROUND "diskinfo-usb-workaround"

/**
 * Report Linux disk device for vmdk mapping via vim.
 *
 * @param boolean Set to true to report devices to VMX.
 */
#define CONFNAME_DISKINFO_REPORT_DEVICE "diskinfo-report-device"

/*
 * END GuestInfo goodies.
 ******************************************************************************
 */


/*
 ******************************************************************************
 * BEGIN GuestOSInfo goodies.
 */

/**
 * Defines the string used for the GuestOSInfo config file group.
 */
#define CONFGROUPNAME_GUESTOSINFO "guestosinfo"

/**
 * Lets users override the short OS name sent by Tools.
 */
#define CONFNAME_GUESTOSINFO_SHORTNAME "short-name"

/**
 * Lets users override the long OS name sent by Tools.
 */
#define CONFNAME_GUESTOSINFO_LONGNAME "long-name"

/*
 * END GuestOSInfo goodies.
 ******************************************************************************
 */


/*
 ******************************************************************************
 * BEGIN Unity goodies.
 */

/**
 * Defines the string used for the Unity config file group.
 */
#define CONFGROUPNAME_UNITY "unity"

/**
 * Lets users override system decisions about whether unity should be available.
 */
#define CONFNAME_UNITY_FORCEENABLE "forceEnable"

/**
 * Lets users override the desktop background color when in Unity mode.
 */
#define CONFNAME_UNITY_BACKGROUNDCOLOR "desktop.backgroundColor"

/**
 * Lets users enable (or disable) the Protocol Buffer enabled service
 */
#define CONFNAME_UNITY_ENABLEPBRPC "pbrpc.enable"

/**
 * Lets users configure the socket type for the PBRPC Services
 */
#define CONFNAME_UNITY_PBRPCSOCKETTYPE "pbrpc.socketType"
#define CONFNAME_UNITY_PBRPCSOCKETTYPE_IPSOCKET "ipsocket"
#define CONFNAME_UNITY_PBRPCSOCKETTYPE_VSOCKET "vsocket"

/*
 * END Unity goodies.
 ******************************************************************************
 */


/*
 ******************************************************************************
 * BEGIN deployPkg goodies.
 */

/**
 * Defines the string used for the deployPkg config file group.
 */
#define CONFGROUPNAME_DEPLOYPKG "deployPkg"

/**
 * Lets users configure the process timeout value in deployPkg
 * Valid value range: 1 ~ 3600
 */
#define CONFNAME_DEPLOYPKG_PROCESSTIMEOUT "process-timeout"

/**
 * The guest customization is allowed or not
 * Valid value: true / false
 */
#define CONFNAME_DEPLOYPKG_ENABLE_CUST "enable-customization"

/**
 * How long does guest customization wait until cloud-init execution done
 * Valid value range: 0 ~ 1800
 */
#define CONFNAME_DEPLOYPKG_WAIT_CLOUDINIT_TIMEOUT "wait-cloudinit-timeout"

/*
 * END deployPkg goodies.
 ******************************************************************************
 */


/** Where to find Tools data in the Win32 registry. */
#if defined(_WIN32)
#   ifndef WSTR
#      define WSTR_(X) L##X
#      define WSTR(X) WSTR_(X)
#   endif
#   define CONF_VMWARE_TOOLS_REGKEY  "Software\\VMware, Inc.\\VMware Tools"
#   define CONF_VMWARE_TOOLS_INSTPATH_KEY "InstallPath"

#   define CONF_VMWARE_TOOLS_REGKEY_W WSTR(CONF_VMWARE_TOOLS_REGKEY)
#   define CONF_VMWARE_TOOLS_INSTPATH_KEY_W WSTR(CONF_VMWARE_TOOLS_INSTPATH_KEY)
#   ifdef UNITY_FOR_VIEW
#      define CONF_VMWARE_RDE_REGKEY  "SOFTWARE\\VMware, Inc.\\VMware VDM\\RemoteExperienceAgent"
#      define CONF_VMWARE_RDE_INSTPATH_KEY "InstallPath"

#      define CONF_VMWARE_RDE_REGKEY_W WSTR(CONF_VMWARE_RDE_REGKEY)
#      define CONF_VMWARE_RDE_INSTPATH_KEY_W WSTR(CONF_VMWARE_RDE_INSTPATH_KEY)
#   endif
#endif

/* Wait 5 seconds between polls to see if the conf file has changed */
#define CONF_POLL_TIME     5

/*
 ******************************************************************************
 * BEGIN upgrader goodies.
 */

#define CONFGROUPNAME_AUTOUPGRADE "autoupgrade"

#define CONFNAME_AUTOUPGRADE_ALLOW_UPGRADE "allow-upgrade"
#define CONFNAME_AUTOUPGRADE_ALLOW_ADD_FEATURE "allow-add-feature"
#define CONFNAME_AUTOUPGRADE_ALLOW_REMOVE_FEATURE "allow-remove-feature"
#define CONFNAME_AUTOUPGRADE_ALLOW_MSI_TRANSFORMS "allow-msi-transforms"

/*
 * END upgrader goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN logging goodies.
 */

/**
 * Defines the string used for the logging config file group.
 */
#define CONFGROUPNAME_LOGGING "logging"

#define CONFNAME_LOGGING_INSTALL_VMXGUESTLOGDISABLED "install-vmxGuestLogDisabled"

/*
 * END logging goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN environment goodies.
 */

/**
 * Defines the strings used for setenvironment and unsetenvironment
 * config groups.
 *
 * These config groups are used for setting and unsetting environment
 * variables for the service. The keys in this config group can be
 * specified in following 2 formats:
 *
 * 1. <variableName> = <value>
 * 2. <serviceName>.<variableName> = <value>
 *
 * Variables specified in format #1 are applied to all services reading
 * the config file whereas variables specified in format #2 are applied
 * only to the specified service.
 */
#define CONFGROUPNAME_SET_ENVIRONMENT "setenvironment"
#define CONFGROUPNAME_UNSET_ENVIRONMENT "unsetenvironment"

/*
 * END environment goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN gitray goodies.
 */

#define CONFGROUPNAME_GITRAY "gitray"

/*
 * END gitray goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN AMSI Fileless goodies.
 */

/**
 * Defines the string used for vsep plugin AMSI Fileless config file group
 */
#define CONFGROUPNAME_AMSI "giamsi"

/* Default state of AMSI config*/
#define VSEP_DEFAULT_AMSI_STATE TRUE

/**
 * Defines user-defined maximum AMSI client connections.
 */
#define CONFNAME_AMSI_MAX_CLIENT_CONNECTIONS "amsi-max-client-connections"

/**
 * Defines user-defined maximum script size supported in fileless.
 */
#define CONFNAME_AMSI_MAX_SCRIPT_SIZE_IN_BYTES "amsi-max-script-size-in-bytes"

/*
 * END Fileless goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN CarbonBlack helper plugin goodies.
 */

/**
 * Define the string used for cbhelper config file group
 */
#define CONFGROUPNAME_CBHELPER "cbhelper"

/**
 * Defines user-defined polling interval in seconds.
 */
#define CONFNAME_CBHELPER_POLLINTERVAL "poll-interval"

/*
 ******************************************************************************
 * END CarbonBlack helper plugin goodies.
 */

/*
 ******************************************************************************
 * BEGIN deviceHelper plugin goodies.
 */

/**
 * Defines the string used for the device helper config file group.
 */
#define CONFGROUPNAME_DEVICEHELPER "devicehelper"

/**
 * Defines the configuration to perform device helper or not.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * value.
 *
 * @param boolean Set to TRUE to disable device helper feature.
 *                Set to FALSE to enable device helper feature.
 */
#define CONFNAME_DEVICEHELPER_DISABLED "disabled"

/*
 * END deviceHelper goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN gdp plugin goodies.
 */

/**
 * Defines the string used for the gdp config file group.
 */
#define CONFGROUPNAME_GDP "gdp"

/**
 * Defines a custom history cache buffer size limit (in bytes).
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * cache buffer size limit 1048576.
 *
 * @param int   User-defined cache buffer size limit within [262144, 4194304].
 *              Set 0 to disable caching.
 */
#define CONFNAME_GDP_CACHE_SIZE "cacheSize"

/**
 * Defines a custom history cache item count limit.
 *
 * @note Illegal values result in a @c g_warning and fallback to the default
 * cache item count limit 256.
 *
 * @param int   User-defined cache item count limit within [64, 1024].
 */
#define CONFNAME_GDP_CACHE_COUNT "cacheCount"

/*
 * END gdp goodies.
 ******************************************************************************
 */

/*
 ******************************************************************************
 * BEGIN timeSync plugin goodies.
 */

/**
 * Defines the string used for the timeSync config file group.
 */
#define CONFGROUPNAME_TIMESYNC "timeSync"

/**
 * Enable (or disable) the TimeInfo API.
 */
#define CONFNAME_TIMESYNC_TIMEINFO_ENABLED "timeInfo.enabled"

/*
 * END timeSync goodies.
 ******************************************************************************
 */

#endif /* __CONF_H__ */
070701000000AF000081A40000000000000000000000016822550500001592000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/config.h   /*********************************************************
 * Copyright (C) 1998-2018,2021-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _CONFIG_H_
#define _CONFIG_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "preference.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Well-known configuration variable names
 */

#define	CONFIG_VMWAREDIR	"libdir"

struct CryptoKey;
struct KeySafeUserRing;

void Config_SetAny(const char *value,
                   const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetAnySecure(const char *value,
                         const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetString(const char *value,
                      const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetStringPlain(const char *value,
                           const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetStringSecure(const char *value,
                            const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetBool(Bool value, const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetBoolPlain(Bool value, const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetBoolSecure(Bool value, const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetLong(int32 value,
                    const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetInt64(int64 value,
                     const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetLongPlain(int32 value,
                         const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetLongSecure(int32 value,
                          const char *fmt, ...) PRINTF_DECL(2, 3);
void Config_SetDouble(double value,
                      const char *fmt, ...) PRINTF_DECL(2, 3);

char *Config_GetString(const char *defaultValue,
                       const char *fmt, ...) PRINTF_DECL(2, 3);
char *Config_GetStringPlain(const char *defaultValue,
                            const char *fmt, ...) PRINTF_DECL(2, 3);
char *Config_GetStringSecure(const char *defaultValue,
                             const char *fmt, ...) PRINTF_DECL(2, 3);
char *Config_GetAsString(const char *fmt, ...) PRINTF_DECL(1, 2);
char *Config_GetStringEnum(const char *defaultValue,
                           const char **choices,
                           const char *fmt, ...) PRINTF_DECL(3, 4);

int Config_CompareVersion(int version);
int Config_CompareVersions(int version1, int version2);
char *Config_GetPathName(const char *defaultValue,
                         const char *fmt, ...) PRINTF_DECL(2, 3);
Bool Config_GetBool(Bool defaultValue,
                    const char *fmt, ...) PRINTF_DECL(2, 3);
Bool Config_GetBoolPlain(Bool defaultValue,
                         const char *fmt, ...) PRINTF_DECL(2, 3);
Bool Config_GetBoolSecure(Bool defaultValue,
                          const char *fmt, ...) PRINTF_DECL(2, 3);
int32 Config_GetLong(int32 defaultValue,
                     const char *fmt, ...) PRINTF_DECL(2, 3);
int64 Config_GetInt64(int64 defaultValue,
                      const char *fmt, ...) PRINTF_DECL(2, 3);
int32 Config_GetLongPlain(int32 defaultValue,
                          const char *fmt, ...) PRINTF_DECL(2, 3);
int32 Config_GetLongSecure(int32 defaultValue,
                           const char *fmt, ...) PRINTF_DECL(2, 3);
int32 Config_GetTriState(int32 defaultValue,
                         const char *fmt, ...) PRINTF_DECL(2, 3);
double Config_GetDouble(double defaultValue,
                        const char *fmt, ...) PRINTF_DECL(2, 3);
Bool Config_NotSet(const char *fmt, ...) PRINTF_DECL(1, 2);
Bool Config_Unset(const char *fmt, ...) PRINTF_DECL(1, 2);
Bool Config_UnsetWithPrefix(const char *fmt, ...) PRINTF_DECL(1, 2);
Bool Config_NeedSave(void);

void Config_Set(void *value, int type,
                const char *fmt, ...) PRINTF_DECL(3, 4);

/*
 * This is tricky to call because it returns allocated storage. Use
 * the typed wrappers instead (Config_Get*).
 */
void *Config_Get(const void *pDefaultValue, int type,
                 const char *fmt, ...) PRINTF_DECL(3, 4);

Bool Config_Load(const char *filename);
Bool Config_Write(void);
Bool Config_WriteNoMsg(void);

Bool Config_FileIsWritable(void);

uint32 Config_GetMask(uint32 defaultMask, const char *optionName);
uint64 Config_GetMask64(uint64 defaultMask, const char *optionName);

Bool Config_GetDataFileKey(struct CryptoKey **key,
                           struct KeySafeUserRing **userRing);

Bool Config_GetDataFileKeys(struct KeySafeUserRing **parentKeys,
                            struct KeySafeUserRing **allKeys);

Bool Config_TriToBool(Bool boolDefaultValue,
                      int32 triValue);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _CONFIG_H_
  070701000000B0000081A400000000000000000000000168225505000011C7000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/cpName.h   /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * cpName.h --
 *
 *    Cross-platform name format used by hgfs.
 *
 */

#ifndef __CP_NAME_H__
#define __CP_NAME_H__


#ifdef __KERNEL__
#  include "driver-config.h"
#  include <linux/string.h>
#elif defined __FreeBSD__
#   if defined _KERNEL
#      include <sys/libkern.h>
#      define strchr(s,c)       index(s,c)
#   else
#      include <string.h>
#   endif
#elif defined __APPLE__ && defined KERNEL
#  include <string.h>
#elif !defined sun
#  include <stdlib.h>
#  include <string.h>
#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 <x>" 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 */
   HGFS_NAME_STATUS_NOT_A_DIRECTORY,     /* Name has path component not a dir */
} 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_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__ */
 070701000000B1000081A400000000000000000000000168225505000008FA000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/cpNameLite.h   /*********************************************************
 * Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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 <linux/string.h>
#elif defined _KERNEL && defined __FreeBSD__
#  include <sys/libkern.h>
#  define strchr(s,c)       index(s,c)
#else
#  include <string.h>
#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__ */
  070701000000B2000081A40000000000000000000000016822550500000D1D000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/cpNameUtil.h   /*********************************************************
 * Copyright (C) 2005-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * cpNameUtil.h
 *
 *    Utility functions for cross-platform name format.
 *
 */

#ifndef __CP_NAME_UTIL_H__
#define __CP_NAME_UTIL_H__

#include "vm_basic_types.h"
#include "cpName.h"

char *CPNameUtil_Strrchr(char const *cpNameIn,
                         size_t cpNameInSize,
                         char searchChar);

int CPNameUtil_ConvertToRoot(char const *nameIn,
                             size_t bufOutSize,
                             char *bufOut);
int CPNameUtil_LinuxConvertToRoot(char const *nameIn,
                                  size_t bufOutSize,
                                  char *bufOut);
int CPNameUtil_WindowsConvertToRoot(char const *nameIn,
                                    size_t bufOutSize,
                                    char *bufOut);
/* 
 * Convert a set of files or directories CP names from current to form C.
 * In/out name lengths include a final nul-terminator to ensure
 * all the final name component is converted.
 */
Bool CPNameUtil_Utf8FormHostToUtf8FormC(const char *cpNameToConvert,
                                        size_t cpNameToConvertLen,
                                        char **cpUtf8FormCName,
                                        size_t *cpUtf8FormCNameLen);

/* 
 * Convert a set of files or directories CP names from current from form C.
 * In/out name lengths include a final nul-terminator to ensure
 * all the final name component is converted.
 */
Bool CPNameUtil_Utf8FormCToUtf8FormHost(const char *cpUtf8FormCName,
                                        size_t cpUtf8FormCNameLen,
                                        char **cpConvertedName,
                                        size_t *cpConvertedNameLen);

void CPNameUtil_CharReplace(char *buf,
                            size_t bufSize,
                            char oldChar,
                            char newChar);

#endif /* __CP_NAME_UTIL_H__ */
   070701000000B3000081A40000000000000000000000016822550500000C38000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/cryptoError.h  /*********************************************************
 * Copyright (C) 2005-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * cryptoError.h --
 *
 *      Error code for cryptographic infrastructure library.
 */

#ifndef VMWARE_CRYPTOERROR_H
#define VMWARE_CRYPTOERROR_H 1

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vmware.h"

typedef int CryptoError;

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * This set of errors should not be expanded beyond a maximum value of 15
 * without also updating the code for AIOMgr errors, which allots only 4 bits
 * for sub-error codes.
 *
 * Adding a lot of error codes to describe particular errors is a bad idea
 * anyhow, because it can be a security hole in itself; see, for example, the
 * SSL vulnerability described at <http://www.openssl.org/~bodo/tls-cbc.txt>.
 * It is best to distinguish only those types of errors that the caller can
 * legitimately use to figure out how to fix the problem and try again.
 */
#define CRYPTO_ERROR_SUCCESS            ((CryptoError) 0)
#define CRYPTO_ERROR_OPERATION_FAILED   ((CryptoError) 1)
#define CRYPTO_ERROR_UNKNOWN_ALGORITHM  ((CryptoError) 2)
#define CRYPTO_ERROR_BAD_BUFFER_SIZE    ((CryptoError) 3)
#define CRYPTO_ERROR_INVALID_OPERATION  ((CryptoError) 4)
#define CRYPTO_ERROR_NOMEM              ((CryptoError) 5)
#define CRYPTO_ERROR_NEED_PASSWORD      ((CryptoError) 6)
#define CRYPTO_ERROR_BAD_PASSWORD       ((CryptoError) 7)
#define CRYPTO_ERROR_IO_ERROR           ((CryptoError) 8)
#define CRYPTO_ERROR_UNKNOWN_ERROR      ((CryptoError) 9)
#define CRYPTO_ERROR_NAME_NOT_FOUND     ((CryptoError) 10)
#define CRYPTO_ERROR_NO_CRYPTO          ((CryptoError) 11)
#define CRYPTO_ERROR_LOCK_FAILURE       ((CryptoError) 12)

const char *
CryptoError_ToString(CryptoError error);

const char *
CryptoError_ToMsgString(CryptoError error);


static INLINE int
CryptoError_ToInteger(CryptoError error)
{
   return (int) error;
}

static INLINE CryptoError
CryptoError_FromInteger(int index)
{
   return (CryptoError) index;
}

static INLINE Bool
CryptoError_IsSuccess(CryptoError error)
{
   return (CRYPTO_ERROR_SUCCESS == error);
}

static INLINE Bool
CryptoError_IsFailure(CryptoError error)
{
   return (CRYPTO_ERROR_SUCCESS != error);
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* cryptoError.h */
070701000000B4000081A40000000000000000000000016822550500001719000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/dataMap.h  /*********************************************************
 * Copyright (C) 2013-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef _DATA_MAP_H_
#define _DATA_MAP_H_

#include "hashMap.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef int32 DMKeyType;

/*
 * Error codes for DataMap APIs
 */
typedef enum {
   DMERR_SUCCESS,                  /* success code */
   DMERR_NOT_FOUND,                /* data does not exist */
   DMERR_ALREADY_EXIST,            /* field ID already exist */
   DMERR_DUPLICATED_FIELD_IDS,     /* duplicated IDs in deserilization */
   DMERR_INSUFFICIENT_MEM,         /* insufficient memory */
   DMERR_TYPE_MISMATCH,            /* type does not match */
   DMERR_INVALID_ARGS,             /* invalid arguments */
   DMERR_UNKNOWN_TYPE,             /* type unknow in decoding */
   DMERR_TRUNCATED_DATA,           /* more data expected during decoding */
   DMERR_BUFFER_TOO_SMALL,         /* a user buffer is too small */
   DMERR_INTEGER_OVERFLOW,         /* an integer overflow happened */
   DMERR_BAD_DATA                  /* bad data during decoding */
} ErrorCode;

/*
 * Data field types
 */
typedef enum {
   DMFIELDTYPE_EMPTY,
   DMFIELDTYPE_INT64,
   DMFIELDTYPE_STRING,
   DMFIELDTYPE_INT64LIST,
   DMFIELDTYPE_STRINGLIST,
   DMFIELDTYPE_MAX
} DMFieldType;

typedef struct {
   HashMap *map;
   uint64 cookie;   /* so we know the datamap is not some garbage data */
} DataMap;

typedef struct {
   DMKeyType fieldId;
   const char *fieldName;
} FieldIdNameEntry;

/*
 * Initializer
 */
ErrorCode
DataMap_Create(DataMap *that);   // IN/OUT
ErrorCode
DataMap_Destroy(DataMap *that);   // IN/OUT
ErrorCode
DataMap_Copy(const DataMap *src,  // IN
             DataMap *dst);       // OUT
ErrorCode
DataMap_Serialize(const DataMap *that,   //IN
                  char **buf,            // OUT
                  uint32 *bufLen);          // OUT
ErrorCode
DataMap_Deserialize(const char *bufIn,     // IN
                    const int32 bufLen,    // IN
                    DataMap *that);        // OUT

ErrorCode
DataMap_DeserializeContent(const char *bufIn,     // IN
                           const int32 bufLen,    // IN
                           DataMap *that);        // OUT
/*
 * Setters
 */

ErrorCode
DataMap_SetInt64(DataMap *that,      // IN/OUT
                 DMKeyType fieldId,  // IN
                 int64 value,        // IN
                 Bool replace);      // IN
ErrorCode
DataMap_SetString(DataMap *that,       // IN/OUT
                  DMKeyType fieldId,   // IN
                  char *str,           // IN
                  int32 strLen,        // IN
                  Bool replace);       // IN
ErrorCode
DataMap_SetInt64List(DataMap *that,        // IN/OUT
                     DMKeyType fieldId,    // IN
                     int64 *numList,       // IN
                     int32 listLen,        // IN
                     Bool replace);        // IN
ErrorCode
DataMap_SetStringList(DataMap *that,          // IN/OUT
                      DMKeyType fieldId,      // IN
                      char **strList,         // IN
                      int32 *strLens,         // IN
                      Bool replace);          // IN

/*
 *  Getters
 */

DMFieldType
DataMap_GetType(const DataMap *that,    // IN
                DMKeyType fieldId);     // IN

ErrorCode
DataMap_GetInt64(const DataMap *that,      // IN
                 DMKeyType fieldId,        // IN
                 int64 *value);            // OUT

ErrorCode
DataMap_GetString(const DataMap *that,      // IN
                  DMKeyType fieldId,        // IN
                  char **str,               // OUT
                  int32 *strLen);           // OUT

ErrorCode
DataMap_GetInt64List(const DataMap *that,      // IN
                     DMKeyType fieldId,        // IN
                     int64 **numList,          // OUT
                     int32 *listLen);          // OUT
ErrorCode
DataMap_GetStringList(const DataMap *that,      // IN
                      DMKeyType fieldId,        // IN
                      char ***strList,          // OUT
                      int32 **strLens);         // OUT

ErrorCode
DataMap_ToString(const DataMap *that,               // IN
                 FieldIdNameEntry *fieldIdList,     // IN
                 int32 fieldIdListLen,              // IN
                 int32 maxNumElements,              // IN
                 int32 maxStrLen,                   // IN
                 char **buf);                       // OUT

#ifdef __cplusplus
}    /* end of extern "C"  */
#endif


#endif // #ifdef _DATA_MAP_H_
   070701000000B5000081A400000000000000000000000168225505000022F1000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/dbllnklst.h    /*********************************************************
 * Copyright (C) 1998-2017,2020,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * dbllnklst.h --
 *
 *    Double linked lists
 *
 *    Both circular and anchored (linear) lists are supported.
 *    See bora/lib/misc/dbllnklst.c for sample code showing both use cases.
 */

#ifndef _DBLLNKLST_H_
#define _DBLLNKLST_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

#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
 *
 * DblLnkLst_LinkFirst, DblLnkLst_LinkLast, and DblLnkLst_Swap are specific
 * to anchored lists.  The rest are for both circular and anchored lists.
 */


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Init --
 *
 *    Initialize a member of a doubly linked list
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Init(DblLnkLst_Links *l) // OUT
{
   l->prev = l->next = l;
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Link --
 *
 *    Merge two doubly linked lists into one
 *
 *    The operation is commutative
 *    The operation is inversible (its inverse is DblLnkLst_Unlink)
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Link(DblLnkLst_Links *l1, // IN/OUT
               DblLnkLst_Links *l2) // IN/OUT
{
   DblLnkLst_Links *tmp;

   (tmp      = l1->prev)->next = l2;
   (l1->prev = l2->prev)->next = l1;
    l2->prev = tmp                 ;
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Unlink --
 *
 *    Split one doubly linked list into two
 *
 *    No check is performed: the caller must ensure that both members
 *    belong to the same doubly linked list
 *
 *    The operation is commutative
 *    The operation is inversible (its inverse is DblLnkLst_Link)
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Unlink(DblLnkLst_Links *l1, // IN/OUT
                 DblLnkLst_Links *l2) // IN/OUT
{
   DblLnkLst_Links *tmp;

   tmp       = l1->prev            ;
   (l1->prev = l2->prev)->next = l1;
   (l2->prev = tmp     )->next = l2;
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Unlink1 --
 *
 *    Unlink an element from its list.
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Unlink1(DblLnkLst_Links *l) // IN/OUT
{
   DblLnkLst_Unlink(l, l->next);
}


/*
 *----------------------------------------------------------------------------
 *
 * DblLnkLst_IsLinked --
 *
 *    Determines whether an element is linked with any other elements.
 *
 * Results:
 *    TRUE if link is linked, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE Bool
DblLnkLst_IsLinked(DblLnkLst_Links const *l) // IN
{
   /*
    * A DblLnkLst_Links is either linked to itself (not linked) or linked to
    * other elements in a list (linked).
    */
   return l->prev != l;
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_LinkFirst --
 *
 *    Insert 'l' at the beginning of the list anchored at 'head'
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_LinkFirst(DblLnkLst_Links *head, // IN/OUT
                    DblLnkLst_Links *l)    // IN/OUT
{
   DblLnkLst_Link(head->next, l);
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_LinkLast --
 *
 *    Insert 'l' at the end of the list anchored at 'head'
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_LinkLast(DblLnkLst_Links *head, // IN/OUT
                   DblLnkLst_Links *l)    // IN/OUT
{
   DblLnkLst_Link(head, l);
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Swap --
 *
 *    Swap all entries between the list anchored at 'head1' and the list
 *    anchored at 'head2'.
 *
 *    The operation is commutative
 *    The operation is inversible (its inverse is itself)
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Swap(DblLnkLst_Links *head1,  // IN/OUT
               DblLnkLst_Links *head2)  // IN/OUT
{
   DblLnkLst_Links const tmp = *head1;

   if (DblLnkLst_IsLinked(head2)) {
      (head1->prev = head2->prev)->next = head1;
      (head1->next = head2->next)->prev = head1;
   } else {
      DblLnkLst_Init(head1);
   }

   if (tmp.prev != head1) {
      (head2->prev = tmp.prev)->next = head2;
      (head2->next = tmp.next)->prev = head2;
   } else {
      DblLnkLst_Init(head2);
   }
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _DBLLNKLST_H_ */
   070701000000B6000081A4000000000000000000000001682255050000044E000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/debug.h    /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * debug.h --
 *
 *    Platform specific debug routines
 *
 */


#ifndef __DEBUG_H__
#   define __DEBUG_H__

#   include "vm_basic_types.h"

void Debug(char const *fmt, ...) PRINTF_DECL(1, 2);

#endif /* __DEBUG_H__ */
  070701000000B7000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/deployPkg  070701000000B8000081A400000000000000000000000168225505000014F4000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/deployPkg/deployPkgFormat.h    /*********************************************************
 * Copyright (c) 2006-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * deployPkgFormat.h --
 *
 *      A deployment package format used primarily to
 *      upload and install software in a guest OS.
 *
 *      The package can be a file, or a section embedded inside
 *      of another file or raw block device.
 */

#ifndef _DEPLOY_PKG_FORMAT_H_
#define _DEPLOY_PKG_FORMAT_H_

#define VMWAREDEPLOYPKG_SIGNATURE_LENGTH 16
#define VMWAREDEPLOYPKG_SIGNATURE "VMWAREDEPLOYPKG_"

#define VMWAREDEPLOYPKG_CMD_LENGTH 456
#define VMWAREDEPLOYPKG_SEED_LENGTH 8

#define VMWAREDEPLOYPKG_PAYLOAD_TYPE_CAB 0         // cabinet file
#define VMWAREDEPLOYPKG_PAYLOAD_TYPE_ZIP 1         // zip
#define VMWAREDEPLOYPKG_PAYLOAD_TYPE_GZIPPED_TAR 2 // tar.gz

// XXX Delete this - it's redundant
typedef enum {
   Cabinet,                // cabinet file
   Zip,
   GzippedTar              // tar.gz
} VMwareDeployPkgPayloadType;

#define VMWAREDEPLOYPKG_CURRENT_MAJOR_VERSION 1
#define VMWAREDEPLOYPKG_CURRENT_MINOR_VERSION 0

#include "vm_basic_types.h"

#define VMWAREDEPLOYPKG_HEADER_FLAGS_NONE 0
#define VMWAREDEPLOYPKG_HEADER_FLAGS_SKIP_REBOOT 1
#define VMWAREDEPLOYPKG_HEADER_FLAGS_IGNORE_CLOUD_INIT 2
#define VMWAREDEPLOYPKG_HEADER_FLAGS_RAW_CLOUD_INIT 4

#ifdef _WIN32
#include "pshpack4.h" // 4 byte alignment assumed.
#endif

/*
 * VMware deployment package header. 4 byte alignment assumed.
 * The header size is exactly 512 bytes to make it easier to
 * embed in a disk device, such as a partition.
 *
 * The payload is extracted and expanded into a temporary folder.
 * During expansion, original relative path names are preserved.
 * The specified command is then executed on the host.
 * Its working directory is the extraction folder.
 * The command string may contain OS-specific environment
 * variables.
 * In addition, the variable VMWAREPKGDIR is defined to be
 * the location of the extraction folder.
 *
 * (Request for comment: is VMWAREPKGDIR really necessary?
 *  Remove from spec if not.)
 *
 * The field seed is used by the password obfuscation library to hide details
 * that are required for obfuscating password in the config file.
 *
 * Command string example:
 * deploy.bat -opt1 myfile.exe foo.xml "%WINDIR%\system32"
 *
 * Seed is a piece of information used by the obfuscation code to compute
 * cryptography keys.
 *
 * The extraction folder is deleted after the command returns.
 * A return value of zero indicates successful deployment.
 *
 * Here is the approximate layout. Do not make assumptions about the 
 * exact location and relative position of the individual sections.
 * Use the offset and length fields from the header instead.
 *
 * <pre>
 *
 *         +-------------------------+
 *         |         header          |
 *         +-------------------------+
 *         |         padding         |
 *         +-------------------------+
 *         |        payload          |
 *         +-------------------------+
 *         |      (seed+command)     |
 *         |         padding         |
 *         +-------------------------+
 * </pre>
 */

#pragma pack(push, 1)
typedef struct {
   char signature[VMWAREDEPLOYPKG_SIGNATURE_LENGTH]; // Not null terminated.
   uint8 majorVersion;
   uint8 minorVersion;
   uint8 payloadType;
   uint8 reserved;

   uint16 pkgProcessTimeout; // timeout value for process execution in deployPkg

   /*
    * Structs are aligned to word length. For 32 bit architecture it is 4 bytes
    * aligned and for 64 bit it is 8 bytes aligned. Need to make sure package
    * created in 32 bit architecture can be read correctly in 64 bit architecture
    * and vice-versa. So when adding or removing fields, a padding field maybe
    * needed to enable the payload section to start at the same place in both 32
    * and 64 bit architecture.
    */
   uint16 archPadding;   // offset 22

   uint64 pkgLength;     // Total length of package including header, offset 24.
   uint64 payloadOffset; // Relative to beginning of header, offset 32.
   uint64 payloadLength; // Length of payload, offset 40.

   /*
    * Command string and padding, null terminated.
    * This padding makes the header sector-aligned, making it easier
    * to embed in disks and disk partitions.
    * This string may contain OS-specific env variables, e.g. %SYSTEMDRIVE%.
    */

   char seed[VMWAREDEPLOYPKG_SEED_LENGTH];   // offset 48
   char command[VMWAREDEPLOYPKG_CMD_LENGTH]; // offset 56

} VMwareDeployPkgHdr;
#pragma pack(pop)

#ifdef _WIN32
#include "poppack.h"
#endif

#endif // _DEPLOY_PKG_FORMAT_H_
070701000000B9000081A400000000000000000000000168225505000013CA000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/deployPkg/linuxDeployment.h    /*********************************************************
 * Copyright (c) 2009-2019, 2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * linuxDeployment.h --
 *
 *      C interface to package deployment.
 */

#ifndef LINUX_DEPLOYMENT_H
#define LINUX_DEPLOYMENT_H

#include "vm_basic_types.h"

#include "imgcust-common/log.h"
#include "imgcust-common/imgcust-api.h"

#define DEPLOYPKG_PROCESSTIMEOUT_DEFAULT 100

typedef enum {
   DEPLOYPKG_STATUS_SUCCESS,
   DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED,
   DEPLOYPKG_STATUS_ERROR,
   DEPLOYPKG_STATUS_CAB_ERROR
} DeployPkgStatus;

/*
 *------------------------------------------------------------------------------
 *
 * DeployPkg_SetTimeout --
 *
 *      Give the deploy package an application specific timeout value.
 *      Package deployment engines such as tools-deployPkg-plugin or standalone program
 * linuxDeployPkg can call this API to set gProcessTimeout.
 *      This API should be called before DeployPkg_DeployPackageFromFile or
 * DeployPkg_DeployPackageFromFileEx.
 *      If the package header includs valid 'timeout' value, then that value will
 * overwrite gProcessTimeout.
 *      If no valid 'timeout' value from both package header and deployment engine, then
 * default value 100s will be used.
 *
 * @param logger [in]
 *      timeout value to be used for process execution period control
 *
 *------------------------------------------------------------------------------
 */

IMGCUST_API void
DeployPkg_SetProcessTimeout(uint16 timeout);


/*
 *------------------------------------------------------------------------------
 *
 * DeployPkg_SetWaitForCloudinitDoneTimeout
 *
 *     Set the timeout value of customization process waits for cloud-init
 *     execution done before trigger reboot and after connect network adapters.
 *
 * @param timeout [in]
 *     timeout value to be used for waiting for cloud-init execution done
 *
 *------------------------------------------------------------------------------
 */

IMGCUST_API void
DeployPkg_SetWaitForCloudinitDoneTimeout(uint16 timeout);



/*
 *------------------------------------------------------------------------------
 *
 * DeployPkg_SetLogger --
 *
 *      Give the deploy package an application specific logger.
 *
 * @param logger [in] logger to be used for deploy operation
 *
 *------------------------------------------------------------------------------
 */

IMGCUST_API void
DeployPkg_SetLogger(LogFunction log);


/*
 *------------------------------------------------------------------------------
 *
 * DeployPkg_DeployPackageFromFileEx --
 *
 *      C-style wrapper to decode a package from a file, extract its payload,
 *      expand the payload into a temporary directory, and then execute
 *      the command specified in the package.
 *
 * @param file IN: the package file
 * @return DEPLOYPKG_STATUS_SUCCESS on success
 *         DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED if customization task is
 *         delegated to cloud-init
 *         DEPLOYPKG_STATUS_ERROR on failure
 *
 *------------------------------------------------------------------------------
 */

IMGCUST_API DeployPkgStatus
DeployPkg_DeployPackageFromFileEx(const char* file);


/*
 *------------------------------------------------------------------------------
 *
 * DeployPkg_DeployPackageFromFile --
 *
 *      C-style wrapper to decode a package from a file, extract its payload,
 *      expand the payload into a temporary directory, and then execute
 *      the command specified in the package.
 *
 * @param file IN: the package file
 * @return 0 on success, -1 on failure
 *
 *------------------------------------------------------------------------------
 */

IMGCUST_API int
DeployPkg_DeployPackageFromFile(const char* file);


/*
 *------------------------------------------------------------------------------
 *
 * ExtractCabPackage --
 *
 *      C-style wrapper to extract a package from a file using libmspack.
 *
 * @param[in]  cabFileName  the Cabinet file's path
 * @param[in]  destDir  a destination directory where to uncab
 *
 * @return TRUE on success, otherwise - FALSE.
 *
 *------------------------------------------------------------------------------
 */

IMGCUST_API Bool
ExtractCabPackage(const char* cabFileName, const char* destDir);

#endif // LINUX_DEPLOYMENT_H
  070701000000BA000081A40000000000000000000000016822550500000768000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/dictll.h   /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dictll.h --
 *
 *    Low-level dictionary format --hpreg
 */

#ifndef __DICTLL_H__
#   define __DICTLL_H__

#include "vm_basic_types.h"
#include "dynbuf.h"

#if defined(__cplusplus)
extern "C" {
#endif

int
DictLL_ReadLine(FILE *stream,  // IN
                char **line,   // OUT
                char **name,   // OUT
                char **value); // OUT

Bool
DictLL_WriteLine(FILE *stream,       // IN
                 char const *name,   // IN
                 char const *value); // IN

const char *
DictLL_UnmarshalLine(const char *buf,  // IN
                     size_t bufSize,   // IN
                     char **line,      // OUT
                     char **name,      // OUT
                     char **value);    // OUT

Bool
DictLL_MarshalLine(DynBuf *output,     // IN/OUT
                   char const *name,   // IN/OPT
                   char const *value); // IN


Bool
DictLL_ReadUTF8BOM(FILE *file); // IN/OUT

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* __DICTLL_H__ */
070701000000BB000081A4000000000000000000000001682255050000348C000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/dynarray.h /*********************************************************
 * Copyright (C) 2004-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dynarray.h --
 *
 *    Dynamic array of objects.
 *
 *    Use a DynArray to hold a dynamically resizable array
 *    of objects with a fixed width.
 */

#ifndef _DYNARRAY_H_
#define _DYNARRAY_H_

#include "dynbuf.h"
#include "vm_basic_types.h"
#include "vm_assert.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef struct DynArray {
   DynBuf buf;
   size_t width;
} DynArray;

/*
 * The SVGA drivers require the __cdecl calling convention.
 * The qsort comparison function is compiled with the __stdecl
 * convention by default, so if we are compiling SVGA (which defines
 * STD_CALL) we need to explicitly declare the function with __cdecl.
 */
#if defined(STD_CALL)
#define CDECLCONV __cdecl
#else
#define CDECLCONV
#endif

typedef int (CDECLCONV *DynArrayCmp)(const void *, const void *);

Bool
DynArray_Init(DynArray *a, unsigned int count, size_t width);

void
DynArray_Destroy(DynArray *a);

Bool
DynArray_SetCount(DynArray *a, unsigned int c);

void
DynArray_QSort(DynArray *a, DynArrayCmp compare);

unsigned int
DynArray_AllocCount(const DynArray *a);


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_Trim --
 *
 *      Resize the array to fit exactly DynArray_Count() elements.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (why?  who knows...)
 *
 * Side effects:
 *      Resizes the array
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
DynArray_Trim(DynArray *a)    // IN/OUT
{
   ASSERT(a);

   return DynBuf_Trim(&a->buf);
}



/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_AddressOf --
 *
 *      Fetch a pointer to the address of the ith element.
 *
 * Results:
 *      The pointer to the ith element or NULL if the index is out of
 *      bounds.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void *
DynArray_AddressOf(const DynArray *a,   // IN
                   unsigned int i)      // IN
{
   uintptr_t offset = i * a->width;

   ASSERT(a);

   if (offset + a->width <= DynBuf_GetSize(&a->buf)) {
      return offset + (uint8 *)DynBuf_Get(&a->buf);
   }

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_AddressOfUnsafe --
 *
 *      Fetch a pointer to the address of the ith element. Only call
 *      this if you already know that 'i' is valid. The index is not
 *      checked against the size of the array.
 *
 * Results:
 *      Always returns a pointer to the ith element.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void *
DynArray_AddressOfUnsafe(const DynArray *a,   // IN
                         unsigned int i)      // IN
{
   uintptr_t offset = i * a->width;

   ASSERT(a);
   ASSERT(offset + a->width <= DynBuf_GetSize(&a->buf));

   return offset + (uint8 *)DynBuf_Get(&a->buf);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_Count --
 *
 *      Returns the number of elements in the array.
 *
 *      XXX: This is relatively slow, since we do an integer division.
 *           Avoid calling this in inner loops.
 *
 * Results:
 *      See above.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE unsigned int
DynArray_Count(const DynArray *a)       // IN
{
   ASSERT(a);

   return (unsigned int) (DynBuf_GetSize(&a->buf) / a->width);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_Copy --
 *
 *      Copies all data and metadata from src Dynarray to dest DynArray.
 *
 *      Dest should be an initialized DynArray of size zero.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
DynArray_Copy(DynArray *src,        // IN
              DynArray *dest)       // OUT
{
   ASSERT(src);
   ASSERT(dest);
   ASSERT(dest->width);
   ASSERT(dest->width == src->width);
   ASSERT(DynArray_AllocCount(dest) == 0);

   return DynBuf_Copy(&src->buf, &dest->buf);
}


/*
 * Use the following macros to define your own DynArray type to
 * make its usage less cumbersome.  You also get type-checking
 * for free, as demonstrated by this example:
 *
 * Assume:
 *
 *    typedef struct { int n, d; } Fraction;
 *    typedef struct { float r, i; } Complex;
 *
 * Without DEFINE_DYNARRAY_TYPE:
 *
 *    DynArray a1, a2;
 *    DynArray_Init(&a1, 4, sizeof(Fraction));
 *    DynArray_Init(&a2, 16, sizeof(Complex));
 *
 *    Fraction *f2 = (Fraction *)DynArray_AddressOf(&a2, 3); // Runtime Error
 *
 *
 * With DEFINE_DYNARRAY_TYPE:
 *
 *    DEFINE_DYNARRAY_TYPE(Fraction)
 *    DEFINE_DYNARRAY_TYPE(Complex)
 *    FractionArray a1;
 *    ComplexArray a2;
 *    FractionArray_Init(&a1, 4);
 *    ComplexArray_Init(&a2, 16);
 *
 *    Fraction *f2 = FractionArray_AddressOf(&a2, 3); // Compile Error
 *
 * Yes, it's a poor man's template (but better than nothing).
 *
 * Note that emscripten does not allow function pointer casting, and has a
 * compiler warning to enforce this, so we cannot use DEFINE_DYNARRAY_TYPE
 * as it relies on a function cast to define T##Array_QSort.
 *
 */

#ifndef __EMSCRIPTEN__

#define DEFINE_DYNARRAY_TYPE(T)     DEFINE_DYNARRAY_NAMED_TYPE(T, T)

#define DEFINE_DYNARRAY_NAMED_TYPE(T, TYPE)                             \
   DECLARE_DYNARRAY_TYPE(T)                                             \
   DEFINEONLY_DYNARRAY_NAMED_TYPE(T, TYPE)

#define DECLARE_DYNARRAY_TYPE(T)                                        \
   typedef DynArray T##Array;

#define DEFINEONLY_DYNARRAY_NAMED_TYPE(T, TYPE)                         \
                                                                        \
   typedef int (CDECLCONV *DynArray##T##Cmp)(const TYPE *,              \
                                             const TYPE *);             \
                                                                        \
   static INLINE Bool                                                   \
   T##Array_Init(T##Array *a, unsigned int count)                       \
   {                                                                    \
      return DynArray_Init((DynArray *)a, count, sizeof(TYPE));         \
   }                                                                    \
                                                                        \
   static INLINE void                                                   \
   T##Array_Destroy(T##Array *a)                                        \
   {                                                                    \
      DynArray_Destroy((DynArray *)a);                                  \
   }                                                                    \
                                                                        \
   static INLINE TYPE*                                                  \
   T##Array_AddressOf(T##Array *a, unsigned int i)                      \
   {                                                                    \
      return (TYPE*)DynArray_AddressOf((DynArray *)a, i);               \
   }                                                                    \
                                                                        \
   static INLINE TYPE*                                                  \
   T##Array_AddressOfUnsafe(T##Array *a, unsigned int i)                \
   {                                                                    \
      return (TYPE*)DynArray_AddressOfUnsafe((DynArray *)a, i);         \
   }                                                                    \
                                                                        \
   static INLINE unsigned int                                           \
   T##Array_Count(const T##Array *a)                                    \
   {                                                                    \
      return DynArray_Count((const DynArray *)a);                       \
   }                                                                    \
                                                                        \
   static INLINE Bool                                                   \
   T##Array_SetCount(T##Array *a, unsigned int c)                       \
   {                                                                    \
      return DynArray_SetCount((DynArray *)a, c);                       \
   }                                                                    \
                                                                        \
   static INLINE Bool                                                   \
   T##Array_Push(T##Array *a, TYPE val)                                 \
   {                                                                    \
      unsigned int count = T##Array_Count(a);                           \
      if (!T##Array_SetCount(a, count + 1)) {                           \
         return FALSE;                                                  \
      }                                                                 \
      *T##Array_AddressOf(a, count) = val;                              \
      return TRUE;                                                      \
   }                                                                    \
                                                                        \
   static INLINE Bool                                                   \
   T##Array_PushFront(T##Array *a, TYPE val)                            \
   {                                                                    \
      unsigned int count = T##Array_Count(a);                           \
      if (!T##Array_SetCount(a, count + 1)) {                           \
         return FALSE;                                                  \
      } else {                                                          \
         unsigned int i;                                                \
         for (i = count; i > 0; --i) {                                  \
            *T##Array_AddressOf(a, i) = *T##Array_AddressOf(a, i-1);    \
         }                                                              \
         *T##Array_AddressOf(a, 0) = val;                               \
         return TRUE;                                                   \
      }                                                                 \
   }                                                                    \
                                                                        \
   static INLINE unsigned int                                           \
   T##Array_AllocCount(T##Array *a)                                     \
   {                                                                    \
      return DynArray_AllocCount((DynArray *)a);                        \
   }                                                                    \
                                                                        \
   static INLINE Bool                                                   \
   T##Array_Trim(T##Array *a)                                           \
   {                                                                    \
      return DynArray_Trim((DynArray *)a);                              \
   }                                                                    \
   static INLINE void                                                   \
   T##Array_QSort(T##Array *a, DynArray##T##Cmp compare)                \
   {                                                                    \
      DynArray_QSort((DynArray *)a, (DynArrayCmp)compare);              \
   }                                                                    \
   static INLINE Bool                                                   \
   T##Array_Copy(T##Array *src, T##Array *dest)                         \
   {                                                                    \
      return DynArray_Copy((DynArray *)src, (DynArray *)dest);          \
   }

/* Define DynArray of DynBuf. */
DEFINE_DYNARRAY_TYPE(DynBuf)

#endif // ifndef __EMSCRIPTEN__

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _DYNARRAY_H_ */
070701000000BC000081A400000000000000000000000168225505000026CE000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/dynbuf.h   /*********************************************************
 * Copyright (C) 1998-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dynbuf.h --
 *
 *    Dynamic buffers
 */

#ifndef DYNBUF_H
#   define DYNBUF_H

#include <string.h>
#include "vm_basic_types.h"
#include "vm_assert.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef struct DynBuf {
   char   *data;
   size_t  size;
   size_t  allocated;
} DynBuf;


void
DynBuf_Init(DynBuf *b); // OUT

void
DynBuf_InitWithMemory(DynBuf *b,
                      size_t dataSize,
                      void *data);

void
DynBuf_InitWithString(DynBuf *b,
                      char *str);

void
DynBuf_Destroy(DynBuf *b); // IN

void
DynBuf_Attach(DynBuf *b,    // IN
              size_t size,  // IN
              void *data);  // IN

void *
DynBuf_Detach(DynBuf *b); // IN/OUT

char *
DynBuf_DetachString(DynBuf *b); // IN/OUT

Bool
DynBuf_Enlarge(DynBuf *b,        // IN/OUT
               size_t min_size); // IN

Bool
DynBuf_Append(DynBuf *b,        // IN/OUT
              void const *data, // IN
              size_t size);     // IN

MUST_CHECK_RETURN Bool
DynBuf_Insert(DynBuf *b,        // IN/OUT
              size_t offset,    // IN
              void const *data, // IN
              size_t size);     // IN

Bool
DynBuf_Trim(DynBuf *b); // IN/OUT

Bool
DynBuf_Copy(DynBuf *src,    // IN
            DynBuf *dest);  // OUT

void
DynBuf_SafeInternalAppend(DynBuf *b,            // IN/OUT
                          void const *data,     // IN
                          size_t size,          // IN
                          char const *file,     // IN
                          unsigned int lineno); // IN

#define DynBuf_SafeAppend(_buf, _data, _size) \
   DynBuf_SafeInternalAppend(_buf, _data, _size, __FILE__, __LINE__)

void
DynBuf_SafeInternalInsert(DynBuf *b,            // IN/OUT
                          size_t offset,        // IN
                          void const *data,     // IN
                          size_t size,          // IN
                          char const *file,     // IN
                          unsigned int lineno); // IN

#define DynBuf_SafeInsert(_buf, _offset, _data, _size) \
   DynBuf_SafeInternalInsert(_buf, _offset, _data, _size, __FILE__, __LINE__)

void
DynBuf_SafeInternalEnlarge(DynBuf *b,            // IN/OUT
                           size_t min_size,      // IN
                           char const *file,     // IN
                           unsigned int lineno); // IN

#define DynBuf_SafeEnlarge(_buf, _min_size) \
   DynBuf_SafeInternalEnlarge(_buf,  _min_size, __FILE__, __LINE__)


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Get --
 *
 *      Retrieve a pointer to the data contained in a dynamic buffer --hpreg
 *
 * Results:
 *      The pointer to the data
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(SWIG)
static void *
#else
static INLINE void *
#endif
DynBuf_Get(DynBuf const *b) // IN
{
   ASSERT(b);

   return b->data;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_GetString --
 *
 * Results:
 *      Returns a pointer to the dynamic buffer data as a NUL-terminated
 *      string.
 *
 * Side effects:
 *      DynBuf might allocate additional memory and will panic if it fails to.
 *
 *-----------------------------------------------------------------------------
 */

#if defined(SWIG)
static char *
#else
static INLINE char *
#endif
DynBuf_GetString(DynBuf *b) // IN
{
   ASSERT(b);

   if (b->size == b->allocated) {
      ASSERT_MEM_ALLOC(DynBuf_Enlarge(b, b->size + 1));
   }
   b->data[b->size] = '\0';
   return b->data;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_GetSize --
 *
 *      Returns the current size of the dynamic buffer --hpreg
 *
 * Results:
 *      The current size of the dynamic buffer
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(SWIG)
static size_t
#else
static INLINE size_t
#endif
DynBuf_GetSize(DynBuf const *b) // IN
{
   ASSERT(b);

   return b->size;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_SetSize --
 *
 *      Set the current size of a dynamic buffer --hpreg
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(SWIG)
static void
#else
static INLINE void
#endif
DynBuf_SetSize(DynBuf *b,   // IN/OUT:
               size_t size) // IN
{
   ASSERT(b);
   ASSERT(size <= b->allocated);

   b->size = size;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_GetAllocatedSize --
 *
 *      Returns the current allocated size of the dynamic buffer --hpreg
 *
 * Results:
 *      The current allocated size of the dynamic buffer
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(SWIG)
static size_t
#else
static INLINE size_t
#endif
DynBuf_GetAllocatedSize(DynBuf const *b) // IN
{
   ASSERT(b);

   return b->allocated;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_GetAvailableSize --
 *
 *      Returns the current available space in the dynamic buffer.
 *
 * Results:
 *      Current available space in dynamic buffer
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(SWIG)
static size_t
#else
static INLINE size_t
#endif
DynBuf_GetAvailableSize(DynBuf const *b) // IN
{
   ASSERT(b);

   return b->allocated - b->size;
}


/*
 *----------------------------------------------------------------------------
 *
 * DynBuf_AppendString --
 *
 *     Appends the string to the specified DynBuf object, including its NUL
 *     terminator.  Note that this is NOT like strcat; repeated calls will
 *     leave embedded NULs in the middle of the buffer. (Compare to
 *     DynBuf_Strcat.)
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      DynBuf may change its size or allocate additional memory.
 *
 *----------------------------------------------------------------------------
 */

#if defined(SWIG)
static Bool
#else
static INLINE Bool
#endif
DynBuf_AppendString(DynBuf *buf,         // IN/OUT
                    const char *string)  // IN
{
   return DynBuf_Append(buf, string, strlen(string) + 1 /* NUL */);
}


/*
 *----------------------------------------------------------------------------
 *
 * DynBuf_SafeAppendString --
 *
 *     "Safe" version of the above that does not fail.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      DynBuf may change its size or allocate additional memory.
 *
 *----------------------------------------------------------------------------
 */

#if defined(SWIG)
static void
#else
static INLINE void
#endif
DynBuf_SafeAppendString(DynBuf *buf,         // IN/OUT
                        const char *string)  // IN
{
   DynBuf_SafeAppend(buf, string, strlen(string) + 1 /* NUL */);
}


/*
 *----------------------------------------------------------------------------
 *
 * DynBuf_Strcat --
 *
 *      A DynBuf version of strcat.  Unlike DynBuf_AppendString, does NOT
 *      visibly NUL-terminate the DynBuf, thereby allowing future appends to
 *      do proper string concatenation without leaving embedded NULs in the
 *      middle.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      DynBuf may change its size or allocate additional memory.
 *
 *----------------------------------------------------------------------------
 */

#if defined(SWIG)
static Bool
#else
static INLINE Bool
#endif
DynBuf_Strcat(DynBuf *buf,         // IN/OUT
              const char *string)  // IN
{
   Bool success;

   ASSERT(buf != NULL);
   ASSERT(string != NULL);

   /*
    * We actually do NUL-terminate the buffer internally, but this is not
    * visible to callers, and they should not rely on this.
    */
   success = DynBuf_AppendString(buf, string);
   if (LIKELY(success)) {
      ASSERT(buf->size > 0);
      buf->size--;
   }
   return success;
}


/*
 *----------------------------------------------------------------------------
 *
 * DynBuf_EnsureMinSize --
 *
 *      Ensure that the size of the DynBuf is at least 'size'.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      DynBuf may change its size or allocate additional memory.
 *
 *----------------------------------------------------------------------------
 */

#if defined(SWIG)
static Bool
#else
static INLINE Bool
#endif
DynBuf_EnsureMinSize(DynBuf *buf, // IN/OUT
                     size_t size) // IN
{
   return buf->allocated >= size ? TRUE : DynBuf_Enlarge(buf, size);
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* DYNBUF_H */
  070701000000BD000081A40000000000000000000000016822550500000741000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/dynxdr.h   /*********************************************************
 * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _DYNXDR_H_
#define _DYNXDR_H_

/*
 * dynxdr.h --
 *
 *    Functions for creating and destroying an XDR stream that is backed
 *    by a dynamic memory buffer. Uses DynBuf, so requires code using it to
 *    link lib/misc.
 *
 *    This stream only does encoding. For decoding, we generally have data
 *    already available in the form of a pre-allocated buffer, in which
 *    case we can use the xdrmem_create() function.
 *
 *    Note: xdr_destroy() is a no-op for this stream. Use DynXdr_Destroy()
 *    instead. Also, XDR_SETPOS and XDR_INLINE are not supported.
 */

#include <rpc/types.h>
#include <rpc/xdr.h>
#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

XDR *DynXdr_Create(XDR *in);
Bool DynXdr_AppendRaw(XDR *xdrs, const void *buf, size_t len);
void *DynXdr_AllocGet(XDR *xdrs);
void *DynXdr_Get(XDR *xdrs);
void DynXdr_Destroy(XDR *xdrs, Bool release);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _DYNXDR_H_ */

   070701000000BE000081A400000000000000000000000168225505000007A0000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/embed_version.h    /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * embed_version.h --
 *
 * Embeds a version string in an ELF binary that is readable by modinfo.
 */

#ifndef _EMBED_VERSION_H_
#define _EMBED_VERSION_H_

/*
 * Using section attributes, embed the specified version in the "modinfo"
 * section of the ELF binary. We don't do this on Windows, where the PE format
 * already has version information stuffed inside it, nor on Mac OS X, which
 * doesn't use ELF.
 *
 * We can't declare vm_version as static, otherwise it may get optimized out.
 * I've seen this when building with gcc 4.1, but not with 3.3.
 *
 * The argument to the macro should be the base name for the version number
 * macros to embed in the final binary, as described in vm_version.h (see
 * declaration of VM_VERSION_TO_STR).
 */
#if !defined(_WIN32) && !defined(__APPLE__)

#define VM_EMBED_VERSION(ver)                                    \
const char vm_version[]                                          \
   __attribute__((section(".modinfo"), unused)) = "version=" ver

#else
#define VM_EMBED_VERSION(ver)
#endif

#endif /* _EMBED_VERSION_H_ */
070701000000BF000081A40000000000000000000000016822550500000CAC000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/err.h  /*********************************************************
 * Copyright (C) 2004-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * err.h --
 *
 *      General error handling library
 */

#ifndef _ERR_H_
#define _ERR_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include <errno.h>
#include "vm_basic_defs.h"

#if defined(__cplusplus)
extern "C" {
#endif

#if defined(_WIN32)
   typedef DWORD Err_Number;
#else
   typedef int Err_Number;
#endif

#define ERR_INVALID ((Err_Number) -1)

const char *Err_ErrString(void);

const char *Err_Errno2String(Err_Number errorNumber);

Err_Number Err_String2Errno(const char *string);

#if defined(VMX86_DEBUG)
Err_Number Err_String2ErrnoDebug(const char *string);
#endif

#if defined(_WIN32)
char *Err_SanitizeMessage(const char *msg);
#endif

void Err_Exit(void);

/*
 *----------------------------------------------------------------------
 *
 * Err_Errno --
 *
 *      Gets last error in a platform independent way.
 *
 * Results:
 *      Last error.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#if defined(_WIN32)
   #define Err_Errno() GetLastError()
#else
   #define Err_Errno() errno
#endif


/*
 *----------------------------------------------------------------------
 *
 * Err_SetErrno --
 *
 *      Set the last error in a platform independent way.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	Yes.
 *
 *----------------------------------------------------------------------
 */

#if defined(_WIN32)
   #define Err_SetErrno(e) SetLastError(e)
#else
   #define Err_SetErrno(e) (errno = (e))
#endif


/*
 *----------------------------------------------------------------------
 *
 * WITH_ERRNO --
 *
 *      Execute "body" with "e" bound to the last error number
 *	and preserving the last error in surrounding code.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	Yes.
 *
 *----------------------------------------------------------------------
 */

#if defined(_WIN32)
#define WITH_ERRNO(e, body) do { \
      Err_Number e = Err_Errno(); \
      int __win__##e = errno; \
      body; \
      Err_SetErrno(e); \
      errno = __win__##e; \
   } while (0)
#else
#define WITH_ERRNO(e, body) do { \
      Err_Number e = Err_Errno(); \
      body; \
      Err_SetErrno(e); \
   } while (0)
#endif

#define WITH_ERRNO_FREE(p) WITH_ERRNO(__errNum__, free((void *)p))

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
070701000000C0000081A40000000000000000000000016822550500000A69000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/escape.h   /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * escape.h --
 *
 *    Buffer escaping --hpreg
 */

#ifndef __ESCAPE_H__
#   define __ESCAPE_H__

#include "vmware.h"

#if defined(__cplusplus)
extern "C" {
#endif

void *
Escape_DoString(const char *escStr,    // IN
                int const *bytesToEsc, // IN
                void const *bufIn,     // IN
                size_t sizeIn,         // IN
                size_t *sizeOut);      // OUT/OPT

void *
Escape_Do(char escByte,          // IN
          int const *bytesToEsc, // IN
          void const *bufIn,     // IN
          size_t sizeIn,         // IN
          size_t *sizeOut);      // OUT/OPT

void *
Escape_Undo(char escByte,      // IN
            void const *bufIn, // IN
            size_t sizeIn,     // IN
            size_t *sizeOut);  // OUT/OPT

Bool
Escape_UndoFixed(char escByte,        // IN
                 void const *bufIn,   // IN
                 size_t sizeIn,       // IN
                 void *bufOut,        // IN/OUT
                 size_t bufOutSize);  // IN

const char *
Escape_Strchr(char escByte,      // IN
              const char *bufIn, // IN
              char c);           // IN

char *
Escape_Unescape(char escByte,       // IN
                const char *bufIn); // IN

void *
Escape_AnsiToUnix(void const *bufIn, // IN
                  size_t sizeIn,     // IN
                  size_t *sizeOut);  // OUT/OPT

void *
Escape_Sh(void const *bufIn, // IN
          size_t sizeIn,     // IN
          size_t *sizeOut);  // OUT/OPT

void *
Escape_BRE(void const *bufIn, // IN
           size_t sizeIn,     // IN
           size_t *sizeOut);  // OUT/OPT

void
Escape_UnescapeCString(char *buf); // IN/OUT

char *
Escape_Comma(const char *string); // IN

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* __ESCAPE_H__ */
   070701000000C1000081A40000000000000000000000016822550500002EE6000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/file.h /*********************************************************
 * Copyright (c) 1998-2020,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * file.h --
 *
 *	Interface to host file system and related utility functions.
 */

#ifndef _FILE_H_
#define _FILE_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include <stdio.h>

#include "fileIO.h"
#include "unicodeTypes.h"
#include "err.h"

#if defined(_WIN32)
#define FILE_MAXPATH	MAX_PATH
#else
# ifdef __FreeBSD__
#  include <sys/param.h> // For __FreeBSD_version
# endif
# if defined(__FreeBSD__)
#  include <sys/syslimits.h>  // PATH_MAX
# else
#  include <limits.h>  // PATH_MAX
# endif
#define FILE_MAXPATH	PATH_MAX
#endif

#if defined(__cplusplus)
extern "C" {
#endif

#define FILE_SEARCHPATHTOKEN ";"

#define FILE_UNLINK_DEFAULT_WAIT_MS 2000

/*
 * Opaque, platform-specific stucture for supporting the directory walking API.
 */

typedef struct WalkDirContextImpl WalkDirContextImpl;
typedef WalkDirContextImpl *WalkDirContext;

/*
 * When File_MakeTempEx2 is called, it creates a temporary file or a directory
 * in a specified directory. File_MakeTempEx2 calls a user-specified callback
 * function to get the filename. Callback function should be of type
 * File_MakeTempCreateNameFunc.
 *
 * 'num' specifies nth time this function is called.
 *
 * 'data' specifies the payload that the user specified when executing
 * File_MakeTempEx2 function.
 *
 * If successful, this function should return a dynamically allocated string
 * with the filename.
 *
 * File_MakeTempEx2 frees the pathName after a successful call to this
 * function.
 *
 */

typedef char *File_MakeTempCreateNameFunc(uint32 num,
                                          void *data);

#if defined(__APPLE__)
Bool FileMacos_IsOnSparseDmg(int fd);

Bool FileMacOS_MakeSecureLibraryCopies(const char   *inDir,
                                       const char  **dylibName,
                                       unsigned      numDylibs,
                                       char        **outDir);

#elif defined VMX86_SERVER
struct FS_PartitionListResult;

int File_GetVMFSAttributes(const char *pathName,
                           struct FS_PartitionListResult **fsAttrs);

int File_GetVMFSFSType(const char *pathName,
                       int fd,
                       uint16 *fsTypeNum);

int File_GetVMFSVersion(const char *pathName,
                        uint32 *versionNum);

int File_GetVMFSBlockSize(const char *pathName,
                          uint32 *blockSize);

int File_GetVMFSMountInfo(const char *pathName,
                          char **fsType,
                          uint32 *version,
                          char **remoteIP,
                          char **remoteMountPoint,
                          char **localMountPoint);

int File_GetVMFSLockInfo(const char *path,
                         uint32 *outOpenFlags,
                         uint32 *outWorldID,
                         char **outWorldName,
                         char **outVMFSMacAddr,
                         uint32 *outVMFSLockMode);
#endif

Bool File_SupportsZeroedThick(const char *pathName);

Bool File_SupportsMultiWriter(const char *pathName);

Bool File_SupportsOptimisticLock(const char *pathName);

Bool File_SupportsMandatoryLock(const char *pathName);

Bool File_Exists(const char *pathName);

int File_Unlink(const char *pathName);

int File_UnlinkIfExists(const char *pathName);

int File_UnlinkDelayed(const char *pathName);

int File_UnlinkNoFollow(const char *pathName);

int File_UnlinkRetry(const char *pathName,
                     uint32 maxWaitTimeMilliSec);

void File_SplitName(const char *pathName,
                    char **volume,
                    char **dir,
                    char **base);

void File_GetPathName(const char *fullPath,
                      char **pathName,
                      char **base);

char *File_StripSlashes(const char *path);

char *File_PathJoin(const char *dirName,
                    const char *baseName);

Bool File_CreateDirectory(const char *pathName);

Bool File_CreateDirectoryEx(const char *pathName,
                            int mode);

Bool File_EnsureDirectory(const char *pathName);

Bool File_EnsureDirectoryEx(const char *pathName,
                            int mode);

Bool File_DeleteEmptyDirectory(const char *pathName);

Bool File_CreateDirectoryHierarchy(const char *pathName,
                                   char **topmostCreated);

Bool File_CreateDirectoryHierarchyEx(const char *pathName,
                                     int mode,
                                     char **topmostCreated);

Bool File_DeleteDirectoryContent(const char *pathName);

Bool File_DeleteDirectoryTree(const char *pathName);

int File_ListDirectory(const char *dirName,
                       char ***ids);

Bool File_IsOsfsVolumeEmpty(const char *pathName);

#ifndef _WIN32
char * File_StripFwdSlashes(const char *pathName);
#endif

/*
 * Simple file-system walk.
 */

WalkDirContext File_WalkDirectoryStart(const char *dirName);

Bool File_WalkDirectoryNext(WalkDirContext context,
                            char **fileName);

void File_WalkDirectoryEnd(WalkDirContext context);

Bool File_IsDirectory(const char *pathName);

Bool File_IsFile(const char *pathName);

Bool File_IsSymLink(const char *pathName);

Bool File_ContainSymLink(const char *pathName);

Bool File_IsCharDevice(const char *pathName);

Bool File_GetParent(char **canPath);

Bool File_IsRemote(const char *pathName);

Bool File_IsEmptyDirectory(const char *pathName);

char *File_Cwd(const char *drive); // XXX belongs to `process' module

char *File_FullPath(const char *pathName);

Bool File_IsFullPath(const char *pathName);

uint64 File_GetFreeSpace(const char *pathName,
                         Bool doNotAscend);

uint64 File_GetCapacity(const char *pathName);

#ifdef _WIN32
char *File_GetNTGlobalFinalPath(const char *pathName);
#endif

int File_MakeTempEx(const char *dir,
                    const char *pathName,
                    char **presult);

int File_MakeTempEx2(const char *dir,
                     Bool createTempFile,
                     File_MakeTempCreateNameFunc *createNameFunc,
                     void *createFuncData,
                     char **presult);

char *File_MakeSafeTempDir(const char *prefix);
char *File_MakeSafeTempSubdir(const char *safeDir, const char *subdirName);

int64 File_GetModTime(const char *pathName);

char *File_GetModTimeString(const char *pathName);

char *File_GetUniqueFileSystemID(const char *pathName);

char *File_GetMountPath(const char *pathName,
                        Bool checkEntirePath);

#ifdef _WIN32
char *File_GetVolumeGUID(const char *pathName);
#endif

Bool File_GetTimes(const char *pathName,
                   VmTimeType *createTime,
                   VmTimeType *accessTime,
                   VmTimeType *writeTime,
                   VmTimeType *attrChangeTime);

Bool File_SetTimes(const char *pathName,
                   VmTimeType createTime,
                   VmTimeType accessTime,
                   VmTimeType writeTime,
                   VmTimeType attrChangeTime);

Bool File_GetFilePermissions(const char *pathName,
                            int *mode);

Bool File_SetFilePermissions(const char *pathName,
                             int mode);

Bool File_SupportsFileSize(const char *pathName,
                           uint64 fileSize);

Bool File_GetMaxFileSize(const char *pathName,
                         uint64 *maxFileSize);

Bool File_SupportsLargeFiles(const char *pathName);

char *File_MapPathPrefix(const char *oldPath,
                         const char **oldPrefixes,
                         const char **newPrefixes,
                         size_t numPrefixes);

Bool File_CopyFromFdToFd(FileIODescriptor src,
                         FileIODescriptor dst);

Bool File_CopyFromFd(FileIODescriptor src,
                     const char *dstName,
                     Bool overwriteExisting);

Bool File_Copy(const char *srcName,
               const char *dstName,
               Bool overwriteExisting);

Bool File_MoveTree(const char *srcName,
                   const char *dstName,
                   Bool overwriteExisting,
                   Bool *asMove);

Bool File_CopyTree(const char *srcName,
                   const char *dstName,
                   Bool overwriteExisting,
                   Bool followSymlinks);

Bool File_Replace(const char *oldFile,
                  const char *newFile);

int File_Rename(const char *oldFile,
                const char *newFile);

int File_RenameRetry(const char *oldFile,
                     const char *newFile,
                     uint32 msecMaxWaitTime);

Bool File_Move(const char *oldFile,
               const char *newFile,
               Bool *asRename);

void File_Rotate(const char *pathName,
                 int n,
                 Bool noRename,
                 char **newFileName);

int File_GetFSMountInfo(const char *pathName,
                        char **fsType,
                        uint32 *version,
                        char **remoteIP,
                        char **remoteMountPoint,
                        char **localMountPoint);

/* Get size only for regular file. */
int64 File_GetSize(const char *pathName);

/* Get size for file or directory. */
int64 File_GetSizeEx(const char *pathName);

int64 File_GetSizeByPath(const char *pathName);

int64 File_GetSizeAlternate(const char *pathName);

Bool File_IsSameFile(const char *path1,
                     const char *path2);

char *File_PrependToPath(const char *searchPath,
                         const char *elem);

Bool File_FindFileInSearchPath(const char *file,
                               const char *searchPath,
                               const char *cwd,
                               char **result);

char *File_ReplaceExtension(const char *pathName,
                            const char *newExtension,
                            uint32 numExtensions,
                            ...);

char *File_RemoveExtension(const char *pathName);

Bool File_MakeCfgFileExecutable(const char *pathName);

char *File_ExpandAndCheckDir(const char *dirName);

char *File_GetSafeTmpDir(Bool useConf);

char *File_GetSafeRandomTmpDir(Bool useConf);

char *File_GetUacSafeRandomTmpDir(Bool useConf);

int File_MakeSafeTemp(const char *tag,
                      char **presult);

Bool File_DoesVolumeSupportAcls(const char *pathName);

Bool File_IsSubPathOf(const char *base, const char *path);

Bool File_DoesVolumeSupportConvertBlocks(const char *pathName);

/*
 *---------------------------------------------------------------------------
 *
 * File_IsDirsep --
 *
 *      Is the argument character a directory separator?
 *
 * Results:
 *     TRUE   Yes
 *     FALSE  No
 *
 * Side effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

static INLINE Bool
File_IsDirsep(int c)  // IN:
{
#if defined(_WIN32)
   return (c == '/') || (c == '\\');  // Until util.h dependencies work out
#else
   return c == '/';
#endif
}


#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _FILE_H_
  070701000000C2000081A400000000000000000000000168225505000043C0000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/fileIO.h   /*********************************************************
 * Copyright (c) 1998-2018, 2021-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fileIO.h --
 *
 *	Host-independent wrapper for low-level fileIO functions.
 *
 */

/*
 * Note:
 *
 *  . FileIO_[Read|Write]() [read|write]s exactly the number of bytes requested
 *    unless an error occurs
 *  . FileIO_Seek() supports files larger than 2 GB
 *  . If a function returns a generic error, you can call your native function
 *    to retrieve the last error code
 */

#ifndef _FILEIO_H_
#define _FILEIO_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include <stdio.h>
#include <stdlib.h>
#if !defined(_WIN32)
#include <sys/types.h>
#include <dirent.h>
#endif

#include "vm_basic_types.h"
#include "unicodeTypes.h"

#include "iovector.h"        // for struct iovec

#if defined(__cplusplus)
extern "C" {
#endif

struct FileLockToken;

#if defined(_WIN32)

# include <windows.h>

typedef struct FileIODescriptor {
   HANDLE                win32;
   uint32                flags;
   char                 *fileName;
   struct FileLockToken *lockToken;
} FileIODescriptor;

#else

typedef struct FileIODescriptor {
   int                   posix;
   int                   flags;
   char                 *fileName;
   struct FileLockToken *lockToken;
} FileIODescriptor;

#endif

typedef enum {
   /* distance is relative to the beginning of the file */
   FILEIO_SEEK_BEGIN,
   /* distance is relative to the current position in the file */
   FILEIO_SEEK_CURRENT,
   /* distance is relative to the end of the file */
   FILEIO_SEEK_END,
} FileIOSeekOrigin;

#define FILEIO_OPEN_ACCESS_READ  (1 << 0)
#define FILEIO_OPEN_ACCESS_WRITE (1 << 1)
/*
 * Use synchronous writes (no lazy buffer cache flush)
 */
#define FILEIO_OPEN_SYNC         (1 << 2)
/*
 * Delete the file as soon as possible (i.e. when nobody uses it anymore)
 */
#define FILEIO_OPEN_DELETE_ASAP  (1 << 3)
#define FILEIO_OPEN_UNBUFFERED   (1 << 4)
/*
 * Lock the file on open
 */
#define FILEIO_OPEN_LOCKED       (1 << 5)
/*
 * Asynchronous file I/O
 */
#define FILEIO_ASYNCHRONOUS      (1 << 6)
/*
 * Open non-blocking mode
 */
#define FILEIO_OPEN_NONBLOCK     (1 << 7)
/*
 * Open with more privileges
 */
#define FILEIO_OPEN_PRIVILEGED   (1 << 8)
/*
 * Open exclusive.
 * On Windows host it doesn't pass the flag FILE_SHARE_(READ|WRITE) to
 * CreateFile. Right now, EXCLUSIVE_READ is not used and EXCLUSIVE_WRITE
 * is only used by the cdrom code to implement the exclusive option.
 * On Linux hosts, it passes O_EXCL if both are set.
 * By default, we share r/w. -Maxime
 */
#define FILEIO_OPEN_EXCLUSIVE_READ     (1 << 9)
#define FILEIO_OPEN_EXCLUSIVE_WRITE    (1 << 10)
/*
 * Open sequential.
 * This flag only changes the behavior on Windows host. It is off by default.
 */
#define  FILEIO_OPEN_SEQUENTIAL_SCAN   (1 << 11)
/*
 * Make IOCTL be run by root.  This flag only changes the behavior on Linux
 * host. It is off by default.
 *
 * XXX: This has nothing to do with fileIO, but since AIOMgr shares the flags
 * with fileIO, I had to add it here.  In some future it would be nice to
 * unshare the flags between the two at which point this could be fixed.
 * --Tommy
 */
#define  FILEIO_OPEN_PRIVILEGED_IOCTL    (1 << 12)
/*
 * Lock the file on open with a exclusive leased lock that can be broken
 * (supported on ESX file systems)
 */
#define FILEIO_OPEN_EXCLUSIVE_LOCK       (1 << 13)
/*
 * Lock the file on open with a multiwriter leased lock that can be broken
 * (supported on ESX file systems)
 */
#define FILEIO_OPEN_MULTIWRITER_LOCK     (1 << 14)
/*
 * Lock the file on open with an SWMR leased lock that can be broken
 * (supported on ESX file systems)
 */
#define FILEIO_OPEN_SWMR_LOCK            (1 << 15)
/*
 * Valid only for MacOS. It eventually results into O_EXLOCK flag passed to open
 * system call.
 *
 * O_EXLOCK, O_SHLOCK behavior is tested on Mac OS X Server 10.6, kernel 10.0.0.
 *
 * |                      | Block devices      | Regular files
 * |----------------------|--------------------|----------------
 * | Locking behavior     | mandatory          | advisory
 * |                      |                    |
 * | If O_NONBLOCK absent | open doesn't block | open blocks
 * |                      | on conflicts       | on conflicts
 */
#define FILEIO_OPEN_EXCLUSIVE_LOCK_MACOS (1 << 16)
/*
 * Open file in APPEND-only mode.  All writes go to the current end of file,
 * not to the current file pointer location.
 */
#define FILEIO_OPEN_APPEND               (1 << 17)
/*
 * Valid only on POSIXen. Don't follow a symbolic link.
 */
#define FILEIO_OPEN_ACCESS_NOFOLLOW      (1 << 18)
/*
 * Valid only on Windows. Set FILE_SHARE_DELETE.
 */
#define FILEIO_OPEN_SHARE_DELETE         (1 << 19)
/*
 * Strengths of file lock.
 * Advisory:
 *   Must use FileIO plus lock flags to get locking.
 *   Never uses kernel/fs-level lock, so naked open() bypasses locking.
 * Mandatory:
 *   Requires kernel/fs-level, so naked open() respects lock.
 *   Kernel/fs-level locks are available on ESX but not hosted.
 * Best:
 *   Adaptively picks between mandatory and advisory.
 * Almost all cases should use the "best" lock.
 */
#define FILEIO_OPEN_LOCK_BEST            FILEIO_OPEN_LOCKED /* historical */
#define FILEIO_OPEN_LOCK_ADVISORY        (1 << 20)
#define FILEIO_OPEN_LOCK_MANDATORY       (1 << 21)

/*
 * Flag passed to open() to enable use of swmr-reader locks on VMFS.  This
 * definition must match USEROBJ_OPEN_SWMR_LOCK in user_vsiTypes.h.
 */
#define O_SWMR_LOCK                      (1 << 21)  // 0x00200000

/*
 * OPTIMISTIC is an alternative to EXCLUSIVE and MANDATORY. It applies
 * only on ESX, and gives VMkernel permission to use a type of lock
 * called "optimistic" to speed up opens.  A general guideline is to use it
 * only for read-only opens of small files (< 1KB).
 */
#define FILEIO_OPEN_OPTIMISTIC_LOCK      (1 << 22)  // 0x00400000

/*
 * Flag passed to open() to enable use of oplocks on VMFS.  This definition
 * must match USEROBJ_OPEN_OPTIMISTIC_LOCK in user_vsiTypes.h.
 */
#define O_OPTIMISTIC_LOCK                (1 << 22)  // 0x00400000

/*
 * POSIX specific close the file descriptor when the program uses a variant
 * of the exec system call capability. This is useful in fork/exec scenarios.
 */
#define FILEIO_OPEN_CLOSE_ON_EXEC        (1 << 23)  // 0x00800000

/*
 * Flag passed to open() to not attempt to get the LUN attributes as part of
 * the open operation. Applicable only to opening of SCSI devices. This
 * definition must match the definition of USEROBJ_OPEN_NOATTR in
 * user_vsiTypes.h and FS_OPEN_NOATTR in fs_public.h
 */
#define O_NOATTR                         (1 << 26)  // 0x04000000

/*
 * Flag passed to open() to get multiwriter VMFS lock.  This definition must
 * match USEROBJ_OPEN_MULTIWRITER_LOCK in user_vsiTypes.h.
 */

#define O_MULTIWRITER_LOCK               (1 << 27)  // 0x08000000

/*
 * Flag passed to open() to get exclusive VMFS lock.  This definition must
 * match USEROBJ_OPEN_EXCLUSIVE_LOCK in user_vsiTypes.h.
 */

#define O_EXCLUSIVE_LOCK                 (1 << 28)  // 0x10000000

/* File Access check args */
#define FILEIO_ACCESS_READ       (1 << 0)
#define FILEIO_ACCESS_WRITE      (1 << 1)
#define FILEIO_ACCESS_EXEC       (1 << 2)
#define FILEIO_ACCESS_EXISTS     (1 << 3)

typedef enum {               //  File doesn't exist   File exists
   FILEIO_OPEN,              //  error
   FILEIO_OPEN_EMPTY,        //  error               size = 0
   FILEIO_OPEN_CREATE,       //  create
   FILEIO_OPEN_CREATE_SAFE,  //  create              error
   FILEIO_OPEN_CREATE_EMPTY, //  create              size = 0
} FileIOOpenAction;

typedef enum {
   /*
    * Generic status codes
    */

   /* No error */
   FILEIO_SUCCESS,
   /* The user cancelled the operation */
   FILEIO_CANCELLED,
   /* Generic error */
   FILEIO_ERROR,

   /*
    * Status codes specific to FileIO_Open()
    */

   /* FILEIO_OPEN_CREATE_SAFE was used and the file already existed */
   FILEIO_OPEN_ERROR_EXIST,

   /* Couldn't obtain the requested lock */
   FILEIO_LOCK_FAILED,

   /* Status codes specific to FileIO_Read() */

   /* Tried to read beyond the end of a file */
   FILEIO_READ_ERROR_EOF,

   /* Couldnt locate file */
   FILEIO_FILE_NOT_FOUND,

   /* Insufficient Permissions */
   FILEIO_NO_PERMISSION,

   /* File name too long */
   FILEIO_FILE_NAME_TOO_LONG,
   /*
    * Status codes specific for FileIO_Write()
    */

   /* Attempts to write  file that exceeds maximum file size */
   FILEIO_WRITE_ERROR_FBIG,

   /* The device containint the file has no room for the data */
   FILEIO_WRITE_ERROR_NOSPC,

   /* Attempts to write file that exceeds user's disk quota */
   FILEIO_WRITE_ERROR_DQUOT,

   /*
    * NB: Until disklib error handling is changed, there must be no more
    *     than 16 total error codes here.
    */
   FILEIO_ERROR_LAST,  /* Must be last! */

} FileIOResult;

#if defined(__APPLE__)
typedef int (FileIOPrivilegedOpener)(const char *path,
                                     int flags);
#endif


const char *FileIO_MsgError(FileIOResult status);

void FileIO_Invalidate(FileIODescriptor *file);

Bool FileIO_IsValid(const FileIODescriptor *fd);

FileIOResult FileIO_Create(FileIODescriptor *file,
                           const char *pathName,
                           int access,
                           FileIOOpenAction action,
                           int mode);

FileIOResult FileIO_CreateRetry(FileIODescriptor *file,
                               const char *pathName,
                               int access,
                               FileIOOpenAction action,
                               int mode,
                               uint32 maxWaitTimeMsec);

FileIOResult FileIO_Open(FileIODescriptor *file,
                         const char *pathName,
                         int access,
                         FileIOOpenAction action);

FileIOResult FileIO_OpenRetry(FileIODescriptor *file,
                              const char *pathName,
                              int access,
                              FileIOOpenAction action,
                              uint32 maxWaitTimeMsec);

uint64 FileIO_Seek(const FileIODescriptor *file,
                   int64 distance,
                   FileIOSeekOrigin origin);

FileIOResult FileIO_Read(FileIODescriptor *file,
                         void *buf,
                         size_t requested,
                         size_t *actual);

FileIOResult FileIO_Write(FileIODescriptor *file,
                          const void *buf,
                          size_t requested,
                          size_t *actual);

char *FileIO_AtomicTempPath(const char *path);

FileIOResult FileIO_AtomicTempFile(FileIODescriptor *fileFD,
                                   FileIODescriptor *tempFD);

Bool FileIO_AtomicUpdate(FileIODescriptor *newFD,
                         FileIODescriptor *currFD);

int FileIO_AtomicUpdateEx(FileIODescriptor *newFD,
                          FileIODescriptor *currFD,
                          Bool renameOnNFS);

#if !defined(VMX86_TOOLS) || !defined(__FreeBSD__)

FileIOResult FileIO_Readv(FileIODescriptor *fd,
                          struct iovec const *v,
                          int count,
                          size_t totalSize,
                          size_t *bytesRead);

FileIOResult FileIO_Writev(FileIODescriptor *fd,
                           struct iovec const *v,
                           int count,
                           size_t totalSize,
                           size_t *bytesWritten);
#endif

FileIOResult FileIO_Preadv(
               FileIODescriptor *fd,        // IN: File descriptor
               struct iovec const *entries, // IN: Vector to read into
               int numEntries,              // IN: Number of vector entries
               uint64 offset,               // IN: Offset to start reading
               size_t totalSize,            // IN: totalSize (bytes) in entries
               size_t *actual);             // OUT: number of bytes read

FileIOResult FileIO_Pwritev(
              FileIODescriptor *fd,        // IN: File descriptor
              struct iovec const *entries, // IN: Vector to write from
              int numEntries,              // IN: Number of vector entries
              uint64 offset,               // IN: Offset to start writing
              size_t totalSize,            // IN: Total size (bytes) in entries
              size_t *actual);             // OUT: number of bytes written

FileIOResult FileIO_Pread(
                        FileIODescriptor *fd,    // IN: File descriptor
                        void *buf,               // IN: Buffer to read into
                        size_t len,              // IN: Length of the buffer
                        uint64 offset);          // IN: Offset to start reading

FileIOResult FileIO_Pwrite(
                         FileIODescriptor *fd,   // IN: File descriptor
                         void const *buf,        // IN: Buffer to write from
                         size_t len,             // IN: Length of the buffer
                         uint64 offset);         // IN: Offset to start writing

FileIOResult FileIO_Access(const char *pathName,
                           int accessMode);

Bool    FileIO_Truncate(FileIODescriptor *file,
                        uint64 newSize);

FileIOResult  FileIO_Sync(const FileIODescriptor *file);

FileIOResult FileIO_GetAllocSize(const FileIODescriptor *fd,
                                 uint64 *logicalBytes,
                                 uint64 *allocedBytes);

int64   FileIO_GetSize(const FileIODescriptor *fd);

Bool    FileIO_SetAllocSize(const FileIODescriptor *fd,
                            uint64 size);

FileIOResult FileIO_GetAllocSizeByPath(const char *pathName,
                                       uint64 *logicalBytes,
                                       uint64 *allocedBytes);

int64   FileIO_GetSizeByPath(const char *pathName);

FileIOResult    FileIO_Close(FileIODescriptor *file);

FileIOResult    FileIO_CloseAndUnlink(FileIODescriptor *file);

uint32  FileIO_GetFlags(FileIODescriptor *file);

#if defined(_WIN32)
Bool    FileIO_GetVolumeSectorSize(const char *name,
                                   uint32 *sectorSize);
#endif

Bool    FileIO_SupportsFileSize(const FileIODescriptor *file,
                                uint64 testSize);

int64   FileIO_GetModTime(const FileIODescriptor *fd);

FileIOResult FileIO_Lock(FileIODescriptor *file,
                         int access);

FileIOResult FileIO_Unlock(FileIODescriptor *file);

/* Only users not using FileIO_Open should use these two */
void FileIO_Init(FileIODescriptor *fd,
                 const char *pathName);

void FileIO_Cleanup(FileIODescriptor *fd);

const char *FileIO_ErrorEnglish(FileIOResult status);

void FileIO_OptionalSafeInitialize(void);

#if defined(_WIN32)
FileIODescriptor FileIO_CreateFDWin32(HANDLE win32,
                                      DWORD access,
                                      DWORD attributes);
#else
FileIODescriptor FileIO_CreateFDPosix(int posix,
                                      int flags);

int FileIO_PrivilegedPosixOpen(const char *pathName,
                               int flags);
#endif

FILE *FileIO_DescriptorToStream(FileIODescriptor *fd,
                                Bool textMode);

const char *FileIO_Filename(FileIODescriptor *fd);

#ifdef VMX86_SERVER
FileIOResult FileIO_CreateDeviceFileNoPrompt(FileIODescriptor *fd,
                                             const char *pathName,
                                             int openMode,
                                             FileIOOpenAction action,
                                             int perms,
                                             const char *device);
#endif

/*
 *-------------------------------------------------------------------------
 *
 * FileIO_IsSuccess --
 *
 *      Returns TRUE if the error code is success.
 *
 * Result:
 *      TRUE/FALSE.
 *
 * Side effects:
 *      None.
 *
 *-------------------------------------------------------------------------
 */

static INLINE Bool
FileIO_IsSuccess(FileIOResult res)      // IN
{
   return res == FILEIO_SUCCESS;
}

Bool FileIO_SupportsPrealloc(const char *pathName,
                             Bool fsCheck);

#if defined(__APPLE__)
void FileIO_SetPrivilegedOpener(FileIOPrivilegedOpener *opener);
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _FILEIO_H_
070701000000C3000081A40000000000000000000000016822550500000A12000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/fileLock.h /*********************************************************
 * Copyright (C) 1998-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fileLock.h --
 *
 *      Interface to file locking functions
 */

#ifndef _FILELOCK_H_
#define _FILELOCK_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "unicodeTypes.h"
#include "msgList.h"

#if defined(__cplusplus)
extern "C" {
#endif

/* The default time, in milliseconds, to wait for a lock before giving up */
#define	FILELOCK_DEFAULT_WAIT (vmx86_server ? 7000 : 3500)

/* The wait time that provides "try lock" functionality */
#define	FILELOCK_TRYLOCK_WAIT 0

/* Wait "forever" to acquire the lock (maximum uint32) */
#define	FILELOCK_INFINITE_WAIT 0xFFFFFFFF

/*
 * This is the maximum path length overhead that the file locking code
 * may introduce via all of its components.
 */

#define	FILELOCK_OVERHEAD 15

/* File locking functions */
typedef struct FileLockToken FileLockToken;

char *FileLock_TokenPathName(const FileLockToken *fileLockToken);

FileLockToken *FileLock_Lock(const char *filePath,
                             const Bool readOnly,
                             const uint32 maxWaitTimeMsec,
                             int *err,
                             MsgList **msgs);

Bool FileLock_Unlock(const FileLockToken *lockToken,
                     int *err,
                     MsgList **msgs);

Bool FileLock_IsLocked(const char *filePath,
                       int *err,
                       MsgList **msgs);

Bool FileLock_Remove(const char *filePath,
                     int *err,
                     MsgList **msgs);

Bool FileLock_CleanupVM(const char *cfgfilePath,
                        int *err,
                        MsgList **msgs);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _FILELOCK_H_
  070701000000C4000081A40000000000000000000000016822550500001032000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/include/file_extensions.h  /*********************************************************
 * Copyright (C) 1998-2016, 2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _FILE_EXTENSIONS_H_
#define _FILE_EXTENSIONS_H_


#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

/*
 * Virtual disk and related file types.
 */

#define DISK_FILE_EXTENSION "vmdk"
#define REDO_FILE_EXTENSION "REDO"
#define SWAP_FILE_EXTENSION "vswp"

/*
 * VM configuration and related file types.
 */

#define CONFIG_FILE_EXTENSION "vmx"           // VM configuration file
#define CONFIG_ALT_FILE_EXTENSION "cfg"       // Obsolete synonym for .vmx
#define EXTENDED_CONFIG_FILE_EXTENSION "vmxf" // Foundry metadata
#define MANAGED_CONFIG_FILE_EXTENSION "vmxa"  // ACE Top Level
#define VRM_CONFIG_FILE_EXTENSION "vmx"       // ACE Instance
#define VPX_TEMPLATE_EXTENSION "vmtx"         // VirtualCenter template
#define TEAM_FILE_EXTENSION "vmtm"            // Foundry VM team
#define POLICY_FILE_EXTENSION "vmpl"          // ACE/VRM policy file
#define BUNDLE_FILE_EXTENSION "vmwarevm"      // VM configuration bundle directory
#define SIDECAR_FILE_EXTENSION "vmfd"         // Virtual machine filter data aka sidecar
#define HBRPERSIST_FILE_EXTENSION "psf"       // HBR/VR persistent state file

/*
 * Snapshot and related file types.
 */

#define MAINMEM_FILE_EXTENSION "vmem"
#define SUSPEND_FILE_EXTENSION "vmss"
#define CHECKPOINT_FILE_EXTENSION "vmsn"
#define VPLAY_FILE_EXTENSION "vmlog"
#define SNAPSHOT_METADATA_EXTENSION "vmsd"
#define CHECKPOINT_FILE_EXTENSION_OLD "cpt"   // Obsolete synonym for vmsn

/*
 * Foundry scripts.
 */

#define VIX_ACTION_FILE_EXTENSION "vmac"      // Foundry action
#define VIX_BATCH_FILE_EXTENSION "vmba"       // Foundry batch script

/*
 * ACE/VRM management transit files.
 */

#define VRM_HOTFIXREQ_FILE_EXTENSION "vmhr"   // ACE hotfix request
#define VRM_HOTFIX_FILE_EXTENSION "vmhf"      // ACE hotfix response

/*
 * VM Download.
 */

#define RVM_DOWNLOAD_FILE_EXTENSION "vmdownload"    // Downloaded file
#define RVM_STATE_FILE_EXTENSION "vmstate"          // Download metadata
#define DOWNLOAD_BUNDLE_FILE_EXTENSION "vmdownload" // VM download bundle directory

/*
 * Other file types.
 */

#define SCREENSHOT_EXTENSION "png"
#define NVRAM_EXTENSION "nvram"
#define LOCK_FILE_EXTENSION "lck"
#define VIRTUALPC_EXTENSION "vmc"
#define SYMANTEC_LIVESTATE_EXTENSION "sv2i"
#define STORAGECRAFT_SHADOWSTOR_EXTENSION "spf"
#define ACRONIS_EXTENSION "tib"
#define OPEN_VM_FORMAT_EXTENSION "ovf"
#define ARCHIVED_OPEN_VM_FORMAT_EXTENSION "ova"
#define NAMESPACEDB_EXTENSION "db"
#define DATASETSSTORE_DISKMODE_EXTENSION "dsd"
#define DATASETSSTORE_VMMODE_EXTENSION "dsv"
// "xvm" // VMware console configuration file

/*
 * Extensions repeated with leading period.
 * Moved from bora/public/dumper.h.
 */

#define STDPATH_EXT     "." SUSPEND_FILE_EXTENSION
#define CPTPATH_EXT     "." CHECKPOINT_FILE_EXTENSION
#define CPTPATH_EXT_OLD "." CHECKPOINT_FILE_EXTENSION_OLD
#define CONFIG_EXT      "." CONFIG_FILE_EXTENSION
#define CONFIG_EXT_ALT  "." CONFIG_ALT_FILE_EXTENSION
#define CONFIG_EXT_MGD  "." MANAGED_CONFIG_FILE_EXTENSION
#define CONFIG_EXT_TEAM "." TEAM_FILE_EXTENSION
#define VPX_TEMPL_EXT   "." VPX_TEMPLATE_EXTENSION
#define SCREENSHOT_EXT  "." SCREENSHOT_EXTENSION
#define SWAPPATH_EXT    "." SWAP_FILE_EXTENSION


#endif /* _FILE_EXTENSIONS_H_ */
  070701000000C5000081A400000000000000000000000168225505000029DF000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/ghIntegrationCommon.h  /*********************************************************
 * Copyright (c) 2008-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * ghIntegrationCommon.h --
 *
 *	Common data structures and definitions used by Guest/Host Integration.
 */

#ifndef _GHINTEGRATIONCOMMON_H_
#define _GHINTEGRATIONCOMMON_H_

/*
 * Common data structures and definitions used by Guest/Host Integration.
 */
#define GHI_HGFS_SHARE_URL_SCHEME_UTF8    "x-vmware-share"
#define GHI_HGFS_SHARE_URL_UTF8           "x-vmware-share://"
#define GHI_HGFS_SHARE_URL                _T(GHI_HGFS_SHARE_URL_UTF8)

/*
 * Messages over different channels will be handled by
 * different modules.
 */
#define GHI_CHANNEL_TOOLS_USER                  0  // Handled by tools module
                                                   // in local VM (TOOLS_DND_NAME guestRPC)
                                                   // or by VDPUnityMKSControl module
                                                   // in View RMKS
#define GHI_CHANNEL_TOOLS_MAIN                  1  // Handled by tools module
                                                   // in local VM (TOOLS_DAEMON_NAME guestRPC)
#define GHI_CHANNEL_VIEW_REMOTE_SHARED_FOLDER   2  // VDPSharedFolderMgrMKSControl module
                                                   // in View RMKS
#define GHI_CHANNEL_DND                         3  // DnD for both local VM and View RMKS.
#define GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON      4  // VDPRdeCommonMKSControl module
                                                   // in View RMKS
#define GHI_CHANNEL_VIEW_USB_REDIRECTION        5  // VDPUsbRedirectionMKSControl module in
                                                   // View RMKS
#define GHI_CHANNEL_VIEW_REMOTE_VDP_COMMON      6  // Handled by View VDP core module
#define GHI_CHANNEL_VIEW_PROTOCOL               7  // Interactions with different protocols
                                                   // in View RMKS
#define GHI_CHANNEL_FCP                         8  // FCP for View RMKS
#define GHI_CHANNEL_VIEW_SDR                    9  // Handled by View SDR
#define GHI_CHANNEL_VIEW_WHFB_REDIRECTION       10 // WhfbRedirectionMKSControl module in
                                                   // View RMKS
#define GHI_CHANNEL_VIEW_SCREEN_CAPTURE         11 // ScreenCapture in View RMKS
#define GHI_CHANNEL_COUNT                       12

typedef uint32 GHIChannelType;

#define GHI_REQUEST_SUCCESS_OK                  0  // Guest received the message and returned OK.
#define GHI_REQUEST_SUCCESS_ERROR               1  // Guest received the message but returned ERROR.
#define GHI_REQUEST_GUEST_RPC_FAILED            2  // Not sent to guest
                                                   // or guest failed to return,
                                                   // including timeout.
#define GHI_REQUEST_GENERAL_ERROR               3  // General error, can be guest error
                                                   // or prc error.
#define GHI_REQUEST_FAILED_WITH_UTF8_MESSAGE    4  // Failed and with utf8 error message returned.

typedef uint32 GHIRequestResult;

#define GHI_GUEST_CHANNEL_BITS(channel)   ((channel) << 24)
#define GHI_GUEST_GET_MSG_CHANNEL(msg)    (((msg) >> 24) & 0xff)
typedef uint32 GHIGuestToHostMessageType;


/*
 * MKS->UI messages over GHI_CHANNEL_VIEW_REMOTE_SHARED_FOLDER.
 *
 * Only for View product.
 */
#define GHI_CHANNEL_VIEW_REMOTE_SHARED_FOLDER_BITS \
            GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_VIEW_REMOTE_SHARED_FOLDER)
#define GHI_GUEST_RDPDR_CAP   (GHI_CHANNEL_VIEW_REMOTE_SHARED_FOLDER_BITS | 0x000001)


/*
 * UI->MKS Messages over GHI_CHANNEL_DND.
 */
#define GHI_DND_DND_HOST_GUEST_CMD              "ghi.dnd.dnd.hostguest"
#define GHI_DND_COPYPASTE_HOST_GUEST_CMD        "ghi.dnd.copypaste.hostguest"
#define GHI_DND_HOST_SHAKEHAND_CMD              "ghi.dnd.shakehand"
#define GHI_DND_HOST_GETFILES_CMD               "ghi.dnd.host.getfiles"
#define GHI_DND_HOST_GETFILES_ANSWER_OVERWRITE  "ghi.dnd.host.getfiles.answer.overwrite"
#define GHI_DND_HOST_SENDFILES_CMD              "ghi.dnd.host.sendfiles"
#define GHI_DND_HOST_TRANSFERFILES_CANCEL_CMD   "ghi.dnd.host.transferfiles.cancel"
#define GHI_DND_HOST_ADDBLOCK_CMD               "ghi.dnd.host.addblock"
#define GHI_DND_HOST_REMOVEBLOCK_CMD            "ghi.dnd.host.removeblock"

/*
 * Results of UI->MKS Messages over GHI_CHANNEL_DND.
 */
#define GHI_DND_GUEST_RET_MAX_LEN               64
#define GHI_DND_GUEST_RET_ERROR                 "error"
#define GHI_DND_GUEST_RET_INPROGRESS            "inProgress"
#define GHI_DND_GUEST_RET_DONE                  "done"

/*
 * MKS->UI messages over GHI_CHANNEL_DND.
 */
#define GHI_CHANNEL_DND_BITS                          GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_DND)
#define GHI_GUEST_DND_DND_CMD                         (GHI_CHANNEL_DND_BITS | 0x000001)
#define GHI_GUEST_DND_COPYPASTE_CMD                   (GHI_CHANNEL_DND_BITS | 0x000002)
#define GHI_GUEST_DND_NOTIFY_BLOCKROOT                (GHI_CHANNEL_DND_BITS | 0x000003)
#define GHI_GUEST_DND_TRANSFERFILES_PROGRESS          (GHI_CHANNEL_DND_BITS | 0x000004)
#define GHI_GUEST_DND_GETFILE_OVERWRITE_QUESTION      (GHI_CHANNEL_DND_BITS | 0x000005)
#define GHI_GUEST_DND_CAPABILITY                      (GHI_CHANNEL_DND_BITS | 0x000006)


/*
 * UI->MKS Messages over GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON.
 */
#define GHI_RDE_COMMON_GENERIC_CMD              "ghi.rde.generic"
#define GHI_RDE_COMMON_SET_IME_ENABLED_CMD      "ghi.rde.set.ime.enabled"
#define GHI_RDE_COMMON_SET_IME_HOST_KEYS_CMD    "ghi.rde.set.ime.host.keys"

/*
 * MKS->UI messages over GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON.
 */
#define GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON_BITS \
            GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON)
#define GHI_GUEST_RDE_COMMON_HOST_SET_DPI \
            (GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON_BITS | 0x000001)
#define GHI_GUEST_RDE_COMMON_UNLOCK_DESKTOP \
            (GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON_BITS | 0x000002)
#define GHI_GUEST_RDE_COMMON_CLIPBOARD_DATA_SENT_DONE \
            (GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON_BITS | 0x000003)
#define GHI_GUEST_RDE_COMMON_GENERIC \
            (GHI_CHANNEL_VIEW_REMOTE_RDE_COMMON_BITS | 0x000004)

/*
 * MKS->UI messages over GHI_CHANNEL_VIEW_USB_REDIRECTION.
 */
#define GHI_CHANNEL_VIEW_USB_REDIRECTION_BITS \
            GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_VIEW_USB_REDIRECTION)
#define GHI_GUEST_USB_REDIRECTION_USB_INSTANCE_ID \
            (GHI_CHANNEL_VIEW_USB_REDIRECTION_BITS | 0x000001)
#define GHI_GUEST_USB_REDIRECTION_DEVICES_FILTER_STATUS \
            (GHI_CHANNEL_VIEW_USB_REDIRECTION_BITS | 0x000002)

/*
 * UI->MKS messages over GHI_CHANNEL_VIEW_USB_REDIRECTION.
 */
#define GHI_HOST_USB_REDIRECTION_STARTUSBD_CMD  "ghi.usb.redirection.startusbd"

 /*
  * UI->MKS messages over GHI_CHANNEL_VIEW_PROTOCOL.
  */
#define GHI_SET_BUFFER_WITHOUT_AUDIO_CMD \
        "ghi.view.protocol.set.buffer.without.audio"

/*
 * MKS->UI messages over GHI_CHANNEL_FCP, used by View FCP.
 */
#define GHI_CHANNEL_FCP_BITS                          GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_FCP)
#define GHI_GUEST_FCP_TRANSFERFILES_PROGRESS          (GHI_CHANNEL_FCP_BITS | 0x000001)

/*
 * UI->MKS Messages over GHI_CHANNEL_FCP, used by View FCP.
 */
#define GHI_FCP_HOST_TRANSFERFILES_CANCEL_CMD   "ghi.fcp.host.transferfiles.cancel"

/*
 * MKS->UI messages over GHI_CHANNEL_VIEW_REMOTE_VDP_COMMON.
 */
#define GHI_CHANNEL_VIEW_REMOTE_VDP_COMMON_BITS \
           GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_VIEW_REMOTE_VDP_COMMON)
#define GHI_GUEST_VDP_COMMON_CAP_FEATURES \
           (GHI_CHANNEL_VIEW_REMOTE_VDP_COMMON_BITS | 0x000001)
#define GHI_GUEST_VDP_COMMON_CAP_RECEIVED \
           (GHI_CHANNEL_VIEW_REMOTE_VDP_COMMON_BITS | 0x000002)

/*
 * UI->MKS messages over GHI_CHANNEL_VIEW_REMOTE_VDP_COMMON.
 */
#define GHI_HOST_VDP_COMMON_SYNC_GUEST_LEDS_CMD  "ghi.mks.common.sync.guest.leds"
#define GHI_HOST_VDP_COMMON_GET_GUEST_CAPS_CMD   "ghi.mks.common.get.guest.caps"

/*
 * MKS->UI messages over GHI_CHANNEL_VIEW_WHFB_REDIRECTION
 */
#define GHI_CHANNEL_VIEW_WHFB_REDIRECTION_BITS \
           GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_VIEW_WHFB_REDIRECTION)
#define GHI_GUEST_WHFB_REDIRECTION_UNLOCK_REQUEST \
           (GHI_CHANNEL_VIEW_WHFB_REDIRECTION_BITS | 0x000001)

/*
 * UI->MKS messages over GHI_CHANNEL_VIEW_WHFB_REDIRECTION.
 */
#define GHI_WHFB_REDIRECTION_SET_SESSIONPIN_CMD              "ghi.whfb.set.sessionpin"
#define GHI_WHFB_REDIRECTION_SET_USERVERIFICATIONRESULT_CMD  "ghi.whfb.set.userverificationresult"

/*
 * Capabilities for the message GHI_GUEST_VDP_COMMON_CAP_FEATURES
 */
typedef enum {
   VDP_COMMON_SET_KEYBOARD_STATE_CAP = 0,
   VDP_COMMON_CAP_ITEM_COUNT
} VDPCommonCapType;

/*
 * UI->MKS messages over GHI_CHANNEL_VIEW_SDR
 */
#define GHI_VIEW_SDR_ADD_DRIVE      "ghi.view.sdr.add.drive"
#define GHI_VIEW_SDR_REMOVE_DRIVE   "ghi.view.sdr.remove.drive"

/*
 * MKS->UI messages over GHI_CHANNEL_VIEW_SDR.
 */
#define GHI_CHANNEL_VIEW_SDR_BITS         GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_VIEW_SDR)
#define GHI_VIEW_SDR_VDP_CONNECTED        (GHI_CHANNEL_VIEW_SDR_BITS | 0x000001)
#define GHI_VIEW_SDR_VDP_DISCONNECTED     (GHI_CHANNEL_VIEW_SDR_BITS | 0x000002)
#define GHI_VIEW_SDR_VDP_SDRPOLICY        (GHI_CHANNEL_VIEW_SDR_BITS | 0x000003)


/*
 * UI->MKS messages over GHI_CHANNEL_VIEW_SCREEN_CAPTURE
 */
#define GHI_VIEW_SCREEN_CAPTURE_TAKE_SNAPSHOT      "ghi.view.screen.capture.take.snapshot"
#define GHI_VIEW_SCREEN_CAPTURE_ENUM_TOPOLOGY      "ghi.view.screen.capture.enum.topology"

/*
 * MKS->UI messages over GHI_CHANNEL_VIEW_SCREEN_CAPTURE.
 */
#define GHI_CHANNEL_VIEW_SCREEN_CAPTURE_BITS \
           GHI_GUEST_CHANNEL_BITS(GHI_CHANNEL_VIEW_SCREEN_CAPTURE)
#define GHI_GUEST_SCREEN_CAPTURE_FUNC_READY        (GHI_CHANNEL_VIEW_SCREEN_CAPTURE_BITS | 0x000001)
#define GHI_GUEST_SCREEN_CAPTURE_TOPOLOGY          (GHI_CHANNEL_VIEW_SCREEN_CAPTURE_BITS | 0x000002)

#endif // ifndef _GHINTEGRATIONCOMMON_H_
 070701000000C6000081A40000000000000000000000016822550500000AF7000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/glibUtils.h    /*********************************************************
 * Copyright (c) 2011-2017,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _GLIBUTILS_H_
#define _GLIBUTILS_H_

#if defined(__cplusplus)
extern "C" {
#endif

/**
 * @file glibUtils.h
 *
 * A collection of utility functions that depend only on glib.
 *
 * These functions are guaranteed to have no dependencies on bora/lib libraries
 * or headers.
 */

#include <glib.h>
#if defined(_WIN32)
#  include <windows.h>
#endif

/**
 * @brief Description for a logger.
 *
 * Contains information about a logger. The properties here are aimed at
 * helping the logging code using this library to construct and appropriate
 * log message depending on the output being used.
 *
 * For example, some sinks (like syslog) already add a timestamp to every log
 * message. So if the @a addsTimestamp field is TRUE, the logging code can
 * choose to rely on that and not add a redundant timestamp field to the log
 * message.
 */
typedef struct GlibLogger {
   gboolean          shared;        /**< Output is shared with other processes. */
   gboolean          addsTimestamp; /**< Output adds timestamp automatically. */
   GLogFunc          logfn;         /**< The function that writes to the output. */
   GDestroyNotify    dtor;          /**< Destructor. */
   gboolean          logHeader;     /**< Header needs to be logged. */
} GlibLogger;


GlibLogger *
GlibUtils_CreateFileLogger(const char *path,
                           gboolean append,
                           guint maxSize,
                           guint maxFiles);

GlibLogger *
GlibUtils_CreateStdLogger(void);

#if defined(_WIN32)

gboolean
GlibUtils_AttachConsole(void);

GlibLogger *
GlibUtils_CreateDebugLogger(void);

GlibLogger *
GlibUtils_CreateEventLogger(const wchar_t *source,
                            DWORD eventId);

#else

GlibLogger *
GlibUtils_CreateSysLogger(const char *domain,
                          const char *facility);

#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _GLIBUTILS_H_ */
 070701000000C7000081A400000000000000000000000168225505000006D1000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/globalConfig.h /*********************************************************
 * Copyright (c) 2020-2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _GLOBAL_CONFIG_H_
#define _GLOBAL_CONFIG_H_

#if (defined(_WIN32) && !defined(_ARM64_)) || \
    (defined(__linux__) && !defined(USERWORLD))

#define GLOBALCONFIG_SUPPORTED 1

#include "vmware/tools/plugin.h"
#include <time.h>
#include "guestStoreClient.h"

/**
 * @file globalConfig.h
 *
 * Interface of the module to fetch the tools.conf file from GuestStore.
 */

gboolean GlobalConfig_Start(ToolsAppCtx *ctx);

gboolean GlobalConfig_LoadConfig(GKeyFile **config,
                                 time_t *mtime);

gboolean GlobalConfig_GetEnabled(GKeyFile *conf);

void GlobalConfig_SetEnabled(gboolean enabled,
                             GKeyFile *conf);

gboolean GlobalConfig_DeleteConfig(void);

GuestStoreClientError GlobalConfig_DownloadConfig(GKeyFile *config);

#else

#undef GLOBALCONFIG_SUPPORTED

#endif

#endif /* _GLOBAL_CONFIG_H_ */
   070701000000C8000081A40000000000000000000000016822550500000575000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guestApp.h /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * guestApp.h --
 *
 *    Utility functions common to all guest applications
 */


#ifndef __GUESTAPP_H__
#   define __GUESTAPP_H__

#include "vm_basic_types.h"

#if defined(_WIN32)
#include <windows.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

const char *
GuestApp_GetDefaultScript(const char *confName); // IN

#ifdef _WIN32
LPWSTR
GuestApp_GetInstallPathW(void);
#endif

char *
GuestApp_GetInstallPath(void);

char *
GuestApp_GetConfPath(void);

#ifdef __cplusplus
}
#endif

#endif /* __GUESTAPP_H__ */
   070701000000C9000081A40000000000000000000000016822550500000E5A000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guestInfo.h    /*********************************************************
 * Copyright (C) 2003-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file guestInfo.h
 *
 * Common declarations that aid in sending guest information to the host.
 */

/**
 * @defgroup vmtools_guestInfoAPI GuestInfo API Reference
 * @{
 *
 * @brief APIs implementing the GuestInfo feature.
 *
 * Definitions below are used for communication across the backdoor between
 * the VMware Tools Service (running in the guest) and the VMX (running in
 * the host).
 *
 * @sa @ref vmtools_guestInfo for a high level overview.
 */

#ifndef _GUEST_INFO_H_
#define _GUEST_INFO_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "vm_basic_types.h"

#include "dbllnklst.h"
#include "guestrpc/nicinfo.h"

#define GUEST_INFO_COMMAND "SetGuestInfo"
#define GUEST_DISK_INFO_COMMAND "SetGuestDiskInfo"
#define MAX_VALUE_LEN 100

#define MAX_NICS     16
#define MAX_IPS      8     // Max number of IP addresses for a single NIC
#define INFO_IPADDRESS_V2_MAX_IPS 64
#define MAC_ADDR_SIZE 19
#define IP_ADDR_SIZE 16
#define PARTITION_NAME_SIZE MAX_VALUE_LEN
#define FSTYPE_SIZE 260 // Windows fs types can be up to MAX_PATH chars
#define DISK_DEVICE_NAME_SIZE 15 // Max size for disk device name - scsi?:?

/* Value to be used when "primary" IP address is indeterminable. */
#define GUESTINFO_IP_UNKNOWN "unknown"

typedef enum {
   INFO_ERROR,       /* Zero is unused so that errors in atoi can be caught. */
   INFO_DNS_NAME,
   INFO_IPADDRESS,
   INFO_DISK_FREE_SPACE,
   INFO_BUILD_NUMBER,
   INFO_OS_NAME_FULL,
   INFO_OS_NAME,
   INFO_UPTIME,
   INFO_MEMORY,
   INFO_IPADDRESS_V2,
   INFO_IPADDRESS_V3,
   INFO_OS_DETAILED,
   INFO_MAX
} GuestInfoType;

typedef enum {
   INFO_IP_ADDRESS_FAMILY_IPV4,
   INFO_IP_ADDRESS_FAMILY_IPV6
} GuestInfoIPAddressFamilyType;

typedef struct NicEntryV1 {
   unsigned int numIPs;
   char macAddress[MAC_ADDR_SIZE];  // In the format "12-23-34-45-56-67"
   char ipAddress[MAX_IPS][IP_ADDR_SIZE];
} NicEntryV1;

typedef struct GuestNicInfoV1 {
   unsigned int numNicEntries;
   NicEntryV1 nicList[MAX_NICS];
} GuestNicInfoV1;

#pragma pack(push, 1)
typedef struct _PartitionEntry {
   uint64 freeBytes;
   uint64 totalBytes;
   char name[PARTITION_NAME_SIZE];
} PartitionEntry, *PPartitionEntry;
#pragma pack(pop)

typedef struct _DiskInfo {
   unsigned int numEntries;
   PPartitionEntry partitionList;
} GuestDiskInfo, *PGuestDiskInfo;

#define DISK_INFO_VERSION_1 1

/* Disk info json keys */
#define DISK_INFO_KEY_VERSION          "version"
#define DISK_INFO_KEY_DISKS            "disks"
#define DISK_INFO_KEY_DISK_NAME        "name"
#define DISK_INFO_KEY_DISK_FREE        "free"
#define DISK_INFO_KEY_DISK_SIZE        "size"
#define DISK_INFO_KEY_DISK_UUID        "uuid"
#define DISK_INFO_KEY_DISK_FSTYPE      "fstype"
#define DISK_INFO_KEY_DISK_DEVICE_ARR  "devices"

/**
 * @}
 */

#endif // _GUEST_INFO_H_
  070701000000CA000081A4000000000000000000000001682255050000423B000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guestStats.h   /*********************************************************
 * Copyright (C) 2008-2018,2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file guestStats.h
 *
 *    Common declarations that aid in sending guest statistics to the vmx
 *    and may be further to vmkernel.
 */

#ifndef _GUEST_STATS_H_
#define _GUEST_STATS_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include "vm_assert.h"
#include "vm_basic_types.h"

#define PUBLISH_EXPERIMENTAL_STATS   0
#define ADD_NEW_STATS                0

/*
 * Version 1: Legacy data
 * Version 2: Dead
 * Version 3: Dead
 * Version 4: Dead
 * Version 5: Legacy structure followed by one or more GuestStat
 *            structures and data.
 */

#define GUESTMEMINFO_V1 1
#define GUESTMEMINFO_V2 2
#define GUESTMEMINFO_V3 3
#define GUESTMEMINFO_V4 4
#define GUESTMEMINFO_V5 5

/*
 * Flags for GuestMemInfoLegacy
 *
 * !!! DON'T ADD/CHANGE FLAGS !!!
 *
 * This is deprecated. All new values are returned via a GuestStat list.
 */

#define MEMINFO_MEMTOTAL         (1 << 0)
#define MEMINFO_DEPRECATED1      (1 << 1)
#define MEMINFO_DEPRECATED2      (1 << 2)
#define MEMINFO_DEPRECATED3      (1 << 3)
#define MEMINFO_DEPRECATED4      (1 << 4)
#define MEMINFO_DEPRECATED5      (1 << 5)
#define MEMINFO_DEPRECATED6      (1 << 6)
#define MEMINFO_DEPRECATED7      (1 << 7)
#define MEMINFO_DEPRECATED8      (1 << 8)
#define MEMINFO_DEPRECATED9      (1 << 9)
#define MEMINFO_HUGEPAGESTOTAL   (1 << 10)
#define MEMINFO_DEPRECATED10     (1 << 11)
#define MEMINFO_DEPRECATED11     (1 << 12)
#define MEMINFO_MEMNEEDED        (1 << 13)

/*
 * Legacy GuestMemInfo structure.
 *
 * !!! DON'T CHANGE IT !!!
 *
 * It should stay the same to ensure binary compatibility.
 */

#pragma pack(push, 1)
typedef struct GuestMemInfoLegacy {
   uint32 version;            ///< MemInfo structure version.
   uint32 flags;              ///< Indicates which stats are valid.
   uint64 memTotal;           ///< Total physical memory in Kb.
   uint64 deprecated1[9];     ///< No longer used.
   uint64 hugePagesTotal;     ///< Total number of huge pages.
   uint64 deprecated2[2];     ///< No longer used.
} GuestMemInfoLegacy;
#pragma pack(pop)

/*
 * A stat begins with a header. The header has a mask which says what data
 * follows. Each datum has a size field which says how much data follows so it
 * can be used or ignored. The order of the data that follows is that of the
 * bits, lowest order bit to highest.
 */

typedef enum {
    GUEST_DATUM_PRAGMA            = 0x0001,  // escape hatch (future expansion)
    GUEST_DATUM_NAMESPACE         = 0x0002,  // UTF8 string
    GUEST_DATUM_ID                = 0x0004,  // uint8 - uint64
    GUEST_DATUM_VALUE_TYPE_ENUM   = 0x0008,  // uint8 - uint32
    GUEST_DATUM_VALUE_TYPE_STRING = 0x0010,  // UTF8 string
    GUEST_DATUM_VALUE_UNIT_ENUM   = 0x0020,  // uint8 - uint32
    GUEST_DATUM_VALUE_UNIT_STRING = 0x0040,  // UTF8 string
    GUEST_DATUM_VALUE             = 0x0080,  // value data
} GuestDatum;

#pragma pack(push, 1)
typedef struct GuestStatHeader {
   GuestDatum  datumFlags;  // Indicates how many and which data follow
} GuestStatHeader;
#pragma pack(pop)

#ifdef _MSC_VER
#pragma warning (disable :4200) // non-std extension: zero-sized array in struct
#endif

#pragma pack(push, 1)
typedef struct GuestDatumHeader {
   uint16  dataSize;  // dataSize - May be zero
   char    data[0];   // data - if dataSize is not zero.
} GuestDatumHeader;
#pragma pack(pop)

/*
 * Units datum enum.
 * Note: The entirety (all bits) of the units must always be understood by a client.
 *
 * First we define some modifiers, then the enum itself
 *
 * bits 0-5 define base types (information, time, etc.)
 * bits 6-10 are modifiers, four of which are reserved (in the future, we could
 * define two of them as custom modifiers, for things like changing the radix from 2^10
 * to 10^3 for storage, or for denoting rates are in 100ns units).
 */
#define GuestUnitsModifier_Rate      0x0040
#define GuestUnitsModifier_Reserved0 0x0080
#define GuestUnitsModifier_Reserved1 0x0100
#define GuestUnitsModifier_Reserved2 0x0200
#define GuestUnitsModifier_Reserved3 0x0400

/*
 * bits 11-15 are scale modifiers:
 * This includes common scales: (P)ositive powers, (N)egative powers,
 * and (C)ustom scales (bits, pages, etc.), which are always type specific.
 */
#define GuestUnitsScale_P0           0x0000
#define GuestUnitsScale_P1           0x0800
#define GuestUnitsScale_P2           0x1000
#define GuestUnitsScale_P3           0x1800
#define GuestUnitsScale_P4           0x2000
#define GuestUnitsScale_P5           0x2800
#define GuestUnitsScale_P6           0x3000
#define GuestUnitsScale_Reserved0    0x3800

#define GuestUnitsScale_N1           0x4000
#define GuestUnitsScale_N2           0x4800
#define GuestUnitsScale_N3           0x5000
#define GuestUnitsScale_N4           0x5800
#define GuestUnitsScale_N5           0x6000
#define GuestUnitsScale_N6           0x6800
#define GuestUnitsScale_Reserved1    0x7000
#define GuestUnitsScale_Reserved2    0x7800

#define GuestUnitsScale_C0           0x8000
#define GuestUnitsScale_C1           0x8800
#define GuestUnitsScale_C2           0x9000
#define GuestUnitsScale_C3           0x9800
// 0xA000-0xF800 are reserved.

typedef enum {
   GuestUnitsInvalid            = 0, // Must never be sent
   GuestUnitsNone               = 1, // A valid value, but not any of the below units.
   GuestUnitsNumber             = 2, // default radix is 1000
   GuestUnitsInformation        = 3, // default radix is 1024
   GuestUnitsDuration           = 4, // default radix is 1000
   GuestUnitsCycles             = 5, // default radix is 1000

   GuestUnitsBytes              = GuestUnitsInformation | GuestUnitsScale_P0,
   GuestUnitsKiB                = GuestUnitsInformation | GuestUnitsScale_P1,
   GuestUnitsMiB                = GuestUnitsInformation | GuestUnitsScale_P2,
   GuestUnitsPages              = GuestUnitsInformation | GuestUnitsScale_C0,
   GuestUnitsHugePages          = GuestUnitsInformation | GuestUnitsScale_C1,
   GuestUnitsBytesPerSecond     = GuestUnitsBytes       | GuestUnitsModifier_Rate,
   GuestUnitsKiBPerSecond       = GuestUnitsKiB         | GuestUnitsModifier_Rate,
   GuestUnitsMiBPerSecond       = GuestUnitsMiB         | GuestUnitsModifier_Rate,
   GuestUnitsPagesPerSecond     = GuestUnitsPages       | GuestUnitsModifier_Rate,
   GuestUnitsHugePagesPerSecond = GuestUnitsHugePages   | GuestUnitsModifier_Rate,

   GuestUnitsAttoSeconds        = GuestUnitsDuration    | GuestUnitsScale_N6,
   GuestUnitsFemtoSeconds       = GuestUnitsDuration    | GuestUnitsScale_N5,
   GuestUnitsPicoSeconds        = GuestUnitsDuration    | GuestUnitsScale_N4,
   GuestUnitsNanoSeconds        = GuestUnitsDuration    | GuestUnitsScale_N3,
   GuestUnitsMicroSeconds       = GuestUnitsDuration    | GuestUnitsScale_N2,
   GuestUnitsMilliSeconds       = GuestUnitsDuration    | GuestUnitsScale_N1,
   GuestUnitsSeconds            = GuestUnitsDuration    | GuestUnitsScale_P0,

   GuestUnitsHz                 = GuestUnitsCycles | GuestUnitsScale_P0 | GuestUnitsModifier_Rate,
   GuestUnitsKiloHz             = GuestUnitsCycles | GuestUnitsScale_P1 | GuestUnitsModifier_Rate,
   GuestUnitsMegaHz             = GuestUnitsCycles | GuestUnitsScale_P2 | GuestUnitsModifier_Rate,
   GuestUnitsGigaHz             = GuestUnitsCycles | GuestUnitsScale_P3 | GuestUnitsModifier_Rate,
   GuestUnitsTeraHz             = GuestUnitsCycles | GuestUnitsScale_P4 | GuestUnitsModifier_Rate,

   GuestUnitsPercent            = GuestUnitsNumber | GuestUnitsScale_C0, // integers: must be 0...100; FP: 0.0...1.0
   GuestUnitsNumberPerSecond    = GuestUnitsNumber | GuestUnitsModifier_Rate,
} GuestValueUnits;

/*
 * Data type datum enum.
 * Note: The entirety (all bits) of the type must always be understood by a client.
 *
 * Bits 0-5 are for types.
 * Bits 6-15 are reserved. In the future, one bit will denote arrays.
 */

#define GuestTypeModifier_Reserved0 0x0040 // More Reserved1-9 not shown.

typedef enum {
   GuestTypeInvalid,  // Must never be sent
   GuestTypeNil,      // A stat that has no value
   GuestTypeInt8,     // Little endian
   GuestTypeUint8,    // Little endian
   GuestTypeInt16,    // Little endian
   GuestTypeUint16,   // Little endian
   GuestTypeInt32,    // Little endian
   GuestTypeUint32,   // Little endian
   GuestTypeInt64,    // Little endian
   GuestTypeUint64,   // Little endian
   GuestTypeFloat,    // IEEE 754
   GuestTypeDouble,   // IEEE 754
   GuestTypeString,   // NUL terminated UTF8
   GuestTypeBinary,   // Binary blob
} GuestValueType;

/*
 * Defines the namespace used for guest tools buildin query.
 */
#define GUEST_TOOLS_NAMESPACE "_tools/v1"

/*
 * Defined stat IDs for guest tools builtin query.
 * See vmx/vigorapi/GuestStats.java for documentation
 *
 * NOTE: These IDs are relative to GUEST_TOOLS_NAMESPACE
 * NOTE: DO NOT re-order or remove the IDs. IDs can only be added to the end,
 *       unless you make the totally backward-compatibility breaking change
 *       of bumping the namespace version.
 */
#define GUEST_STAT_TOOLS_IDS \
   /* 6.0u1 stats */ \
   DEFINE_GUEST_STAT(GuestStatID_Invalid,                         0,  "__INVALID__") \
   DEFINE_GUEST_STAT(GuestStatID_None,                            1,  "__NONE__") \
   DEFINE_GUEST_STAT(GuestStatID_ContextSwapRate,                 2,  "guest.contextSwapRate") \
   DEFINE_GUEST_STAT(GuestStatID_MemActiveFileCache,              3,  "guest.mem.activeFileCache") \
   DEFINE_GUEST_STAT(GuestStatID_MemFree,                         4,  "guest.mem.free") \
   DEFINE_GUEST_STAT(GuestStatID_MemNeeded,                       5,  "guest.mem.needed") \
   DEFINE_GUEST_STAT(GuestStatID_MemPhysUsable,                   6,  "guest.mem.physUsable") \
   DEFINE_GUEST_STAT(GuestStatID_PageInRate,                      7,  "guest.page.inRate") \
   DEFINE_GUEST_STAT(GuestStatID_PageOutRate,                     8,  "guest.page.outRate") \
   DEFINE_GUEST_STAT(GuestStatID_SwapSpaceRemaining,              9,  "guest.swap.spaceRemaining") \
   DEFINE_GUEST_STAT(GuestStatID_PhysicalPageSize,                10, "guest.page.size") \
   DEFINE_GUEST_STAT(GuestStatID_HugePageSize,                    11, "guest.hugePage.size") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_HugePagesTotal,            12, "guest.hugePage.total") \
   /* 6.5 stats */ \
   DEFINE_GUEST_STAT(GuestStatID_MemNeededReservation,            13, "guest.mem.neededReservation") \
   DEFINE_GUEST_STAT(GuestStatID_PageSwapInRate,                  14, "guest.swap.pageInRate") \
   DEFINE_GUEST_STAT(GuestStatID_PageSwapOutRate,                 15, "guest.swap.pageOutRate") \
   DEFINE_GUEST_STAT(GuestStatID_ProcessCreationRate,             16, "guest.processCreationRate") \
   DEFINE_GUEST_STAT(GuestStatID_SwapSpaceUsed,                   17, "guest.swap.used") \
   DEFINE_GUEST_STAT(GuestStatID_SwapFilesCurrent,                18, "guest.swap.filesCurrent") \
   DEFINE_GUEST_STAT(GuestStatID_SwapFilesMax,                    19, "guest.swap.filesMax") \
   DEFINE_GUEST_STAT(GuestStatID_ThreadCreationRate,              20, "guest.threadCreationRate") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_HugePagesFree,             21, "guest.hugePage.free") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_LowWaterMark,              22, "guest.mem.lowWaterMark") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemActive,                 23, "guest.mem.active") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemActiveAnon,             24, "guest.mem.activeAnon") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemAvailable,              25, "guest.mem.available") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemBuffers,                26, "guest.mem.buffers") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemCached,                 27, "guest.mem.cached") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemCommitted,              28, "guest.mem.committed") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemDirty,                  29, "guest.mem.dirty") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemInactive,               30, "guest.mem.inactive") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemInactiveAnon,           31, "guest.mem.inactiveAnon") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemInactiveFile,           32, "guest.mem.inactiveFile") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemPinned,                 33, "guest.mem.pinned") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemSlabReclaim,            34, "guest.mem.slabReclaim") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemSwapCached,             35, "guest.mem.swap.cached") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_PageDirectScanRate,        36, "guest.page.directScanRate") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_PageFaultRate,             37, "guest.page.faultRate") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_PageFreeRate,              38, "guest.page.freeRate") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_PageMajorFaultRate,        39, "guest.page.majorFaultRate") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_PageStealRate,             40, "guest.page.stealRate") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_PageSwapScanRate,          41, "guest.page.swapScanRate") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_Swappiness,                42, "guest.swappiness") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemModifiedPages,        43, "guest.mem.modifiedPages") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemAvailableToMm,        44, "guest.mem.availableToMm") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemStandbyCore,          45, "guest.mem.standby.core") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemStandbyNormal,        46, "guest.mem.standby.normal") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemStandbyReserve,       47, "guest.mem.standby.reserve") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemPagedPoolResident,    48, "guest.mem.pagedPool.resident") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemSystemCodeResident,   49, "guest.system.codeResident") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemSystemDriverResident, 50, "guest.system.driverResident") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemNonPagedPool,         51, "guest.mem.nonPagedPool.size") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemCache,                52, "guest.mem.cache") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_FreeSystemPtes,          53, "guest.system.freePtes") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemCommitLimit,          54, "guest.mem.commitLimit") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemCommitted,            55, "guest.mem.committed") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_MemPrivateWorkingSet,    56, "guest.mem.privateWorkingSet") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_DiskReadRate,            57, "guest.disk.readRate") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_DiskWriteRate,           58, "guest.disk.writeRate") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_AutomaticSwapFileMax,    59, "guest.swap.automaticFileMax") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_MemTotal,                  60, "guest.mem.total") \
   /* (6.7, ] stats */ \
   DEFINE_GUEST_STAT(GuestStatID_Linux_CpuRunQueue,               61, "guest.cpu.runQueue") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_DiskRequestQueue,          62, "guest.disk.requestQueue") \
   DEFINE_GUEST_STAT(GuestStatID_Linux_DiskRequestQueueAvg,       63, "guest.disk.requestQueueAvg") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_ProcessorQueue,          64, "guest.processor.queue") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_DiskQueue,               65, "guest.disk.queue") \
   DEFINE_GUEST_STAT(GuestStatID_Windows_DiskQueueAvg,            66, "guest.disk.queueAvg") \
   DEFINE_GUEST_STAT(GuestStatID_Max,                             67, "__MAX__")

/*
 * Define stats enumeration
 */
#undef DEFINE_GUEST_STAT
#define DEFINE_GUEST_STAT(x,y,z) x,
typedef enum GuestStatToolsID {
   GUEST_STAT_TOOLS_IDS
} GuestStatToolsID;

/*
 * Enforce ordering and compactness of the enumeration
 */
#undef DEFINE_GUEST_STAT
#define DEFINE_GUEST_STAT(x,y,z) ASSERT_ON_COMPILE(x==y);

MY_ASSERTS(GUEST_STAT_IDS_ARE_WELL_ORDERED, GUEST_STAT_TOOLS_IDS)

#undef DEFINE_GUEST_STAT

#endif // _GUEST_STATS_H_
 070701000000CB000081A400000000000000000000000168225505000006A9000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guestStoreClient.h /*********************************************************
 * Copyright (c) 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 *  guestStoreClient.h  --
 *
 *  Wrapper functions to load the GuestStore libraries.
 */

#ifndef _GUEST_STORE_CLIENT_H_
#define _GUEST_STORE_CLIENT_H_

#include "vmware/tools/guestStoreClientLib.h"

typedef GuestStoreLibError GuestStoreClientError;

/*
 * Caller provided callback to get total content size in bytes and so far
 * received bytes. Return FALSE to cancel content download.
 */
typedef GuestStore_GetContentCallback GuestStoreClient_GetContentCb;

gboolean
GuestStoreClient_Init(void);

gboolean
GuestStoreClient_DeInit(void);

GuestStoreClientError
GuestStoreClient_GetContent(const char *contentPath,
                            const char *outputPath,
                            GuestStoreClient_GetContentCb getContentCb,
                            void *clientCbData);

#endif /* _GUEST_STORE_CLIENT_H_ */   070701000000CC000081A40000000000000000000000016822550500000935000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guestStoreConst.h  /*********************************************************
 * Copyright (c) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * guestStoreConst.h --
 *
 *    GuestStore common constant definitions.
 */

#ifndef _GUESTSTORE_CONST_H_
#define _GUESTSTORE_CONST_H_

/*
 * GuestStore maximum content path length, set it
 * to be less than USERLINUX_PATH_MAX.
 */
#define GUESTSTORE_CONTENT_PATH_MAX  1024

/*
 * Buffer size to handle GuestStore content request. The request is
 * a content path encoded in HTTP protocol or DataMap.
 */
#define GUESTSTORE_REQUEST_BUFFER_SIZE   (1024 * 4)

/*
 * Buffer size to receive and forward GuestStore content bytes, set to
 * the maximum size of an IP packet.
 */
#define GUESTSTORE_RESPONSE_BUFFER_SIZE  (1024 * 64)

/*
 * GuestStore vmx to guest connection pending timeout value.
 */
#define GUESTSTORE_VMX_TO_GUEST_CONN_TIMEOUT  5 // seconds

/*
 * GuestStore default connection inactivity timeout value. This value shall
 * be greater than gstored timeout value which is currently 60 seconds.
 */
#define GUESTSTORE_DEFAULT_CONN_TIMEOUT  900 // seconds


/*
 * NOTE: changing the following IDs may break data map encoding compatibility.
 */

/* Tools to VMX field IDs */
enum {
   GUESTSTORE_REQ_FLD_CMD           = 1,
   GUESTSTORE_REQ_FLD_PATH          = 2,
   GUESTSTORE_REQ_FLD_MAX
};

/* Command Types */
enum {
   GUESTSTORE_REQ_CMD_GET           = 1,
   GUESTSTORE_REQ_CMD_CLOSE         = 2,
};

/* VMX to tools field IDs */
enum {
   GUESTSTORE_RES_FLD_ERROR_CODE    = 1,
   GUESTSTORE_RES_FLD_CONTENT_SIZE  = 2,
};

#endif  /* _GUESTSTORE_CONST_H_ */
   070701000000CD000081A40000000000000000000000016822550500000B0C000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guestStoreDefs.h   /*********************************************************
 * Copyright (c) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 *  guestStoreDefs.h  --
 *    Common definitions for VMware Tools guestStore plugin and client library.
 */

#ifndef __GUESTSTOREDEFS_H__
#define __GUESTSTOREDEFS_H__

#include "vm_basic_defs.h"

/*
 * GuestStore client connection definitions.
 */
#ifdef _WIN32
#define GUESTSTORE_LOOPBACK_PORT_MIN  7332
#define GUESTSTORE_LOOPBACK_PORT_MAX  7342
#else
#define GUESTSTORE_PIPE_DIR   "/var/run/vmware"
#define GUESTSTORE_PIPE_NAME  GUESTSTORE_PIPE_DIR "/guestStorePipe"
#endif


/*
 * HTTP definitions.
 */
#define HTTP_VER  "HTTP/1.1"

#define HTTP_LINE_END  "\r\n"

#define HTTP_HEADER_END      HTTP_LINE_END HTTP_LINE_END
#define HTTP_HEADER_END_LEN  (sizeof HTTP_HEADER_END - 1)

#define HTTP_REQ_METHOD_GET  "GET"

#define HTTP_STATUS_CODE_OK         200
#define HTTP_STATUS_CODE_FORBIDDEN  403
#define HTTP_STATUS_CODE_NOT_FOUND  404

#define HTTP_RES_OK_LINE         HTTP_VER " "  \
   XSTR(HTTP_STATUS_CODE_OK) " OK" HTTP_LINE_END
#define HTTP_RES_FORBIDDEN_LINE  HTTP_VER " "  \
   XSTR(HTTP_STATUS_CODE_FORBIDDEN) " Forbidden" HTTP_LINE_END
#define HTTP_RES_NOT_FOUND_LINE  HTTP_VER " "  \
   XSTR(HTTP_STATUS_CODE_NOT_FOUND) " Not Found" HTTP_LINE_END

#define CONTENT_LENGTH_HEADER      "Content-Length: "
#define CONTENT_LENGTH_HEADER_LEN  ((int)(sizeof(CONTENT_LENGTH_HEADER) - 1))

#define HTTP_RES_COMMON_HEADERS  "Date: %s" HTTP_LINE_END  \
   "Server: VMGuestStore" HTTP_LINE_END                    \
   "Accept-Ranges: bytes" HTTP_LINE_END                    \
   CONTENT_LENGTH_HEADER "%" FMT64 "d" HTTP_LINE_END       \
   "Content-Type: application/octet-stream" HTTP_LINE_END  \
   "Connection: close" HTTP_LINE_END                       \
   HTTP_LINE_END

#define HTTP_RES_OK         HTTP_RES_OK_LINE        HTTP_RES_COMMON_HEADERS
#define HTTP_RES_FORBIDDEN  HTTP_RES_FORBIDDEN_LINE HTTP_RES_COMMON_HEADERS
#define HTTP_RES_NOT_FOUND  HTTP_RES_NOT_FOUND_LINE HTTP_RES_COMMON_HEADERS

#endif /* __GUESTSTOREDEFS_H__ */
070701000000CE000081A40000000000000000000000016822550500000EED000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guest_msg_def.h    /*********************************************************
 * Copyright (c) 1998-2016,2022-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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_VMKERNEL

#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]
 *
 * It would be nice to have as many as 1024 channels however, as we discovered
 * in PR 2978984, it becomes particularly difficult to raise the number beyond
 * 250. After much discussion, 128 was chosen.
 */
#define GUESTMSG_MAX_CHANNEL 128

/* 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_ */
   070701000000CF000081A40000000000000000000000016822550500007CEC000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guest_os.h /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _GUEST_OS_H_
#define _GUEST_OS_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "guest_os_tables.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * There's no practical max to the number of guests that can be defined in
 * the list below (guest IDs are limited to 2^32), but there is a maximum
 * of MAXGOSSET guests that can comprise a set, such as ALLLINUX, ALLDARWIN,
 * or ALLWIN64.
 *
 * Be conservative and only declare entries in this list if you need to refer
 * to the guest specifically in vmx/main/guest_os.c,
 * vmcore/vmx/main/monitorControl.c, or similar. Don't rely on every supported
 * guest having an entry in this list.
 */

typedef enum GuestOSType {
   GUEST_OS_BASE                = 0x5000,
   GUEST_OS_BASE_MINUS_ONE      = 0x4fff, /* So that ANY is equal to BASE */
#define GOT(_name) _name,
GUEST_OS_TYPE_GEN
#undef GOT
} GuestOSType;

/*
 * Maximum number of guests in a set, must be <= LIST_SIZE in geninfo.h
 */
#define MAXGOSSET 128

typedef enum GuestOSFamilyType {
   GUEST_OS_FAMILY_ANY         = 0x0000,
   GUEST_OS_FAMILY_LINUX       = 0x0001,
   GUEST_OS_FAMILY_WINDOWS     = 0x0002,
   GUEST_OS_FAMILY_WIN9X       = 0x0004,
   GUEST_OS_FAMILY_WINNT       = 0x0008,
   GUEST_OS_FAMILY_WIN2000     = 0x0010,
   GUEST_OS_FAMILY_WINXP       = 0x0020,
   GUEST_OS_FAMILY_WINNET      = 0x0040,
   GUEST_OS_FAMILY_NETWARE     = 0x0080,
   GUEST_OS_FAMILY_DARWIN      = 0x0100
} GuestOSFamilyType;

#define ALLOS                       GUEST_OS_ANY
#define BS(suf)                     GUEST_OS_##suf
#define GOS_IN_SET(gos, ...)        Gos_InSet(gos, __VA_ARGS__, 0)
#define GOS_IN_SET_ARRAY(gos, set)  Gos_InSetArray(gos, set)

Bool Gos_InSet(uint32 gos, ...);
Bool Gos_InSetArray(uint32 gos, const uint32 *set);

#define ALLWIN9X              BS(WIN95), BS(WIN98), BS(WINME)
#define ALLWIN2000            BS(WIN2000)

#define ALLWINXP32            BS(WINXP)
#define ALLWINXP64            BS(WINXPPRO_64)
#define ALLWINXP              ALLWINXP32, ALLWINXP64

#define ALLFREEBSD32          BS(FREEBSD),    BS(FREEBSD11),  \
                              BS(FREEBSD12),  BS(FREEBSD13),  \
                              BS(FREEBSD14),  BS(FREEBSD15)

#define ALLFREEBSD64          BS(FREEBSD_64),                           \
                              BS(FREEBSD11_64),  BS(FREEBSD12_64),      \
                              BS(FREEBSD13_64),  BS(FREEBSD13_ARM_64),  \
                              BS(FREEBSD14_64),  BS(FREEBSD14_ARM_64),  \
                              BS(FREEBSD15_64),  BS(FREEBSD15_ARM_64)

#define ALLFREEBSD            ALLFREEBSD32, ALLFREEBSD64

#define ALLWINNET32           BS(WINNET)
#define ALLWINNET64           BS(WINNET_64)
#define ALLWINNET             ALLWINNET32, ALLWINNET64

#define ALLWINLONGHORN32      BS(LONGHORN)
#define ALLWINLONGHORN64      BS(LONGHORN_64)
#define ALLWINLONGHORN        ALLWINLONGHORN32, ALLWINLONGHORN64

#define ALLWINVISTA32         BS(WINVISTA)
#define ALLWINVISTA64         BS(WINVISTA_64)
#define ALLWINVISTA           ALLWINVISTA32, ALLWINVISTA64

#define ALLWIN2008R2_64       BS(WIN2008R2_64)
#define ALLWIN2008R2          ALLWIN2008R2_64

#define ALLWINSEVEN32         BS(WIN_7)
#define ALLWINSEVEN64         BS(WIN_7_64)
#define ALLWINSEVEN           ALLWINSEVEN32, ALLWINSEVEN64

#define ALLWINEIGHTSERVER64   BS(WIN_8_SERVER_64)
#define ALLWINEIGHTSERVER     ALLWINEIGHTSERVER64

#define ALLWINEIGHTCLIENT32   BS(WIN_8)
#define ALLWINEIGHTCLIENT64   BS(WIN_8_64)
#define ALLWINEIGHTCLIENT     ALLWINEIGHTCLIENT32, ALLWINEIGHTCLIENT64

#define ALLWINEIGHT           ALLWINEIGHTSERVER, ALLWINEIGHTCLIENT

#define ALLWIN_10_SERVER64    BS(WIN_2016SRV_64), BS(WIN_2019SRV_64)

#define ALLWIN_10_SERVER      ALLWIN_10_SERVER64

#define ALLWIN_10_CLIENT32    BS(WIN_10)
#define ALLWIN_10_CLIENT64    BS(WIN_10_64), BS(WIN_10_ARM_64)
#define ALLWIN_10_CLIENT      ALLWIN_10_CLIENT32, ALLWIN_10_CLIENT64

#define ALLWIN_10_32          ALLWIN_10_CLIENT32
#define ALLWIN_10_64          ALLWIN_10_CLIENT64, ALLWIN_10_SERVER
#define ALLWIN_10             ALLWIN_10_CLIENT,   ALLWIN_10_SERVER

#define ALLWIN_11_SERVER64    BS(WIN_2022SRV_64), BS(WIN_2025SRV_64)
#define ALLWIN_11_SERVER      ALLWIN_11_SERVER64

#define ALLWIN_11_CLIENT64    BS(WIN_11_64), BS(WIN_11_ARM_64)
#define ALLWIN_11_CLIENT      ALLWIN_11_CLIENT64

#define ALLWIN_11_64          ALLWIN_11_CLIENT64, ALLWIN_11_SERVER
#define ALLWIN_11             ALLWIN_11_CLIENT64, ALLWIN_11_SERVER

#define ALLWIN_12_CLIENT64    BS(WIN_12_64), BS(WIN_12_ARM_64)
#define ALLWIN_12_CLIENT      ALLWIN_12_CLIENT64

#define ALLWIN_12             ALLWIN_12_CLIENT64

#define ALLHYPER_V            BS(HYPER_V)

#define ALLWINVISTA_OR_HIGHER ALLWINVISTA,         ALLWINLONGHORN,     \
                              ALLWIN2008R2,        ALLWINSEVEN,        \
                              ALLWINEIGHTSERVER,   ALLWINEIGHTCLIENT,  \
                              ALLWIN_10_SERVER,    ALLWIN_10_CLIENT,   \
                              ALLWIN_11_SERVER,    ALLWIN_11_CLIENT,   \
                              ALLWIN_12_CLIENT,    ALLHYPER_V

#define ALLWINNT32            BS(WINNT),           ALLWIN2000,          \
                              ALLWINXP32,          ALLWINNET32,         \
                              ALLWINVISTA32,       ALLWINLONGHORN32,    \
                              ALLWINSEVEN32,       ALLWINEIGHTCLIENT32, \
                              ALLWIN_10_CLIENT32

#define ALLWINNT64            ALLWINXP64,            ALLWINNET64,       \
                              ALLWINVISTA64,         ALLWINLONGHORN64,  \
                              ALLWINSEVEN64,         ALLWIN2008R2_64,   \
                              ALLWINEIGHTCLIENT64,   ALLWINEIGHTSERVER, \
                              ALLWIN_10_CLIENT64,    ALLWIN_10_SERVER,  \
                              ALLWIN_11_CLIENT64,    ALLWIN_11_SERVER,  \
                              ALLWIN_12_CLIENT64,    ALLHYPER_V

#define ALLWINNT              ALLWINNT32, ALLWINNT64

#define ALLWIN32              ALLWIN9X, ALLWINNT32
#define ALLWIN64              ALLWINNT64
#define ALLWIN                ALLWIN32, ALLWIN64

#define ALLOTHER              BS(OTHER), BS(OTHER_64)

#define ALLSOLARIS11_OR_HIGHER \
                              BS(SOLARIS11_64)

#define ALLSOLARIS10_OR_HIGHER \
                              BS(SOLARIS10), BS(SOLARIS10_64), \
                              ALLSOLARIS11_OR_HIGHER

#define ALLSOLARIS            BS(SOLARIS_6_AND_7),    \
                              BS(SOLARIS8),           \
                              BS(SOLARIS9),           \
                              ALLSOLARIS10_OR_HIGHER

#define ALLNETWARE            BS(NETWARE4), BS(NETWARE5), BS(NETWARE6)

#define ALLPHOTON             BS(PHOTON_64), BS(PHOTON_ARM_64)

#define ALL26XLINUX32         BS(DEBIAN),  BS(RHEL),   \
                              BS(UBUNTU),  BS(CENTOS), \
                              BS(ORACLE),  BS(OTHER26XLINUX)

#define ALL26XLINUX64         BS(DEBIAN_64),  BS(RHEL_64),   \
                              BS(UBUNTU_64),  BS(CENTOS_64), \
                              BS(ORACLE_64),  BS(OTHER26XLINUX_64)

#define ALL3XLINUX32          BS(OTHER3XLINUX), BS(CENTOS6), BS(ORACLE6)

#define ALL3XLINUX64          BS(OTHER3XLINUX_64),            \
                              BS(CENTOS6_64), BS(CENTOS7_64), \
                              BS(ORACLE6_64), BS(ORACLE7_64)

#define ALL4XLINUX32          BS(OTHER4XLINUX)

#define ALL4XLINUX64          BS(OTHER4XLINUX_64), BS(PHOTON_64),       \
                              BS(CENTOS8_64),      BS(CENTOS9_64),      \
                              BS(ORACLE8_64),      BS(ORACLE9_64),      \
                              BS(CRXSYS1_64),      BS(CRXPOD1_64),      \
                              BS(AMAZONLINUX2_64), BS(AMAZONLINUX3_64), \
                              BS(LINUX_MINT_64)

#define ALL5XLINUX32          BS(OTHER5XLINUX)

#define ALL5XLINUX64          BS(OTHER5XLINUX_64), BS(OTHER5XLINUX_ARM_64), \
                              BS(DEBIAN_ARM_64),   BS(UBUNTU_ARM_64),       \
                              BS(RHEL9_64),        BS(RHEL9_ARM_64),        \
                              BS(ROCKY_LINUX_64),  BS(ROCKY_LINUX_ARM_64),  \
                              BS(ALMA_LINUX_64),   BS(ALMA_LINUX_ARM_64),   \
                              BS(CRXSYS1_ARM_64),  BS(CRXPOD1_ARM_64),      \
                              BS(CRXSYS2_64),      BS(CRXSYS2_ARM_64)

#define ALL6XLINUX32          BS(OTHER6XLINUX)

#define ALL6XLINUX64          BS(OTHER6XLINUX_64), BS(OTHER6XLINUX_ARM_64),  \
                              BS(RHEL10_64),       BS(RHEL10_ARM_64),        \
                              BS(ORACLE10_64),     BS(PROLINUX_64),          \
                              BS(PARDUS_64)

#define ALL7XLINUX32          BS(OTHER7XLINUX)

#define ALL7XLINUX64          BS(OTHER7XLINUX_64), BS(OTHER7XLINUX_ARM_64)

#define ALLVMKERNEL           BS(VMKERNEL),   BS(VMKERNEL5),      \
                              BS(VMKERNEL6),  BS(VMKERNEL65),     \
                              BS(VMKERNEL7),  BS(VMKERNEL7_ARM),  \
                              BS(VMKERNEL8),  BS(VMKERNEL8_ARM),  \
                              BS(VMKERNEL9),  BS(VMKERNEL9_ARM)

#define ALLLINUX32            BS(VMKERNEL),      BS(OTHERLINUX), \
                              BS(OTHER24XLINUX), ALL26XLINUX32,  \
                              ALL3XLINUX32,      ALL4XLINUX32,   \
                              ALL5XLINUX32,      ALL6XLINUX32,   \
                              ALL7XLINUX32

#define ALLLINUX64            BS(OTHERLINUX_64), BS(OTHER24XLINUX_64), \
                              ALL26XLINUX64,     ALL3XLINUX64,         \
                              ALL4XLINUX64,      ALL5XLINUX64,         \
                              ALL6XLINUX64,      ALL7XLINUX64,         \
                              ALLPHOTON

#define ALLLINUX              ALLLINUX32, ALLLINUX64

#define ALLDARWIN32           BS(DARWIN9), BS(DARWIN10), BS(DARWIN11)

#define ALLDARWIN64           BS(DARWIN9_64),  BS(DARWIN10_64),  \
                              BS(DARWIN11_64), BS(DARWIN12_64),  \
                              BS(DARWIN13_64), BS(DARWIN14_64),  \
                              BS(DARWIN15_64), BS(DARWIN16_64),  \
                              BS(DARWIN17_64), BS(DARWIN18_64),  \
                              BS(DARWIN19_64), BS(DARWIN20_64),  \
                              BS(DARWIN21_64), BS(DARWIN22_64),  \
                              BS(DARWIN23_64), BS(DARWIN24_64)

#define ALLDARWIN             ALLDARWIN32, ALLDARWIN64

#define ALL64                 ALLLINUX64,        ALLWIN64,          \
                              ALLFREEBSD64,      ALLDARWIN64,       \
                              ALLPHOTON,         ALLVMKERNEL,       \
                              BS(SOLARIS10_64),  BS(SOLARIS11_64),  \
                              BS(OTHER_64),      BS(OTHER_ARM_64)

#define ALLECOMSTATION        BS(ECOMSTATION), BS(ECOMSTATION2)

#define ALLOS2                BS(OS2), ALLECOMSTATION

#define ALLCRX                BS(CRXSYS1_64),     BS(CRXPOD1_64),     \
                              BS(CRXSYS1_ARM_64), BS(CRXPOD1_ARM_64), \
                              BS(CRXSYS2_64),     BS(CRXSYS2_ARM_64)

#define ALLARM                BS(WIN_10_ARM_64),        BS(WIN_11_ARM_64),       \
                              BS(WIN_12_ARM_64),                                 \
                              BS(UBUNTU_ARM_64),        BS(PHOTON_ARM_64),       \
                              BS(VMKERNEL7_ARM),        BS(VMKERNEL8_ARM),       \
                              BS(VMKERNEL9_ARM),                                 \
                              BS(OTHER_ARM_64),         BS(DEBIAN_ARM_64),       \
                              BS(OTHER5XLINUX_ARM_64),  BS(OTHER6XLINUX_ARM_64), \
                              BS(OTHER7XLINUX_ARM_64),                           \
                              BS(FREEBSD13_ARM_64),     BS(FREEBSD14_ARM_64),    \
                              BS(FREEBSD15_ARM_64),                              \
                              BS(ALMA_LINUX_ARM_64),    BS(ROCKY_LINUX_ARM_64),  \
                              BS(CRXSYS1_ARM_64),       BS(CRXSYS2_ARM_64),      \
                              BS(CRXPOD1_ARM_64),                                \
                              BS(RHEL9_ARM_64),         BS(RHEL10_ARM_64)

/*
 * Architecture prefixes. No prefix implies the X86 architecture.
 */

#define STR_OS_ARM_PREFIX          "arm-"
#define STR_OS_RISCV_PREFIX        "riscv-"

/* vmkernel (ESX) */
#define STR_OS_VMKERNEL            "vmkernel"

/* Linux */
#define	STR_OS_ALMA_LINUX          "almaLinux"
#define STR_OS_AMAZON_LINUX        "amazonlinux"
#define STR_OS_ANNVIX              "Annvix"
#define STR_OS_ARCH                "Arch"
#define STR_OS_ARKLINUX            "Arklinux"
#define STR_OS_ASIANUX             "asianux"
#define STR_OS_AUROX               "Aurox"
#define STR_OS_BLACKCAT            "BlackCat"
#define STR_OS_CENTOS              "centos"
#define STR_OS_CRXPOD              "CRXPod"
#define STR_OS_CRXSYS              "CRXSys"
#define STR_OS_COBALT              "Cobalt"
#define STR_OS_CONECTIVA           "Conectiva"
#define STR_OS_DEBIAN              "debian"
#define STR_OS_FEDORA              "Fedora"
#define STR_OS_FLATCAR             "flatcar"
#define STR_OS_FUSION_OS           "fusionos"
#define STR_OS_GENTOO              "Gentoo"
#define STR_OS_IMMUNIX             "Immunix"
#define STR_OS_KYLIN_LINUX         "kylinlinux"
#define STR_OS_LINUX               "linux"
#define STR_OS_LINUX_FROM_SCRATCH "Linux-From-Scratch"
#define STR_OS_LINUX_FULL         "Other Linux"
#define STR_OS_LINUX_MINT         "linuxMint"
#define STR_OS_LINUX_PPC          "Linux-PPC"
#define STR_OS_MANDRAKE           "mandrake"
#define STR_OS_MANDRAKE_FULL      "Mandrake Linux"
#define STR_OS_MANDRIVA           "mandriva"
#define STR_OS_MIRACLE_LINUX      "miraclelinux"
#define STR_OS_MKLINUX            "MkLinux"
#define STR_OS_NOVELL             "nld"
#define STR_OS_NOVELL_FULL        "Novell Linux Desktop 9"
#define STR_OS_ORACLE             "oraclelinux"
#define STR_OS_OTHER_LINUX        "otherlinux"
#define STR_OS_OTHER_LINUX_FULL   "Other Linux"
#define STR_OS_OTHER              "other"
#define STR_OS_OTHER_FULL         "Other OSes"
#define STR_OS_OTHER_LINUX        "otherlinux"
#define STR_OS_OTHER_LINUXFULL    "Other Linux"
#define STR_OS_OTHER_24           "other24xlinux"
#define STR_OS_OTHER_24_FULL      "Other Linux 2.4.x kernel"
#define STR_OS_OTHER_26           "other26xlinux"
#define STR_OS_OTHER_26_FULL      "Other Linux 2.6.x kernel"
#define STR_OS_OTHER_3X           "other3xlinux"
#define STR_OS_OTHER_3X_FULL      "Other Linux 3.x kernel"
#define STR_OS_OTHER_4X           "other4xlinux"
#define STR_OS_OTHER_4X_FULL      "Other Linux 4.x kernel"
#define STR_OS_OTHER_5X           "other5xlinux"
#define STR_OS_OTHER_5X_FULL      "Other Linux 5.x kernel"
#define STR_OS_OTHER_6X           "other6xlinux"
#define STR_OS_OTHER_6X_FULL      "Other Linux 6.x kernel"
#define STR_OS_OTHER_7X           "other7xlinux"
#define STR_OS_OTHER_7X_FULL      "Other Linux 7.x and later kernel"
#define STR_OS_PARDUS             "pardus"
#define STR_OS_PHOTON             "vmware-photon"
#define STR_OS_PHOTON_FULL        "VMware Photon OS"
#define STR_OS_PROLINUX           "prolinux"
#define STR_OS_PROLINUX_FULL      "ProLinux"
#define STR_OS_PLD                "PLD"
#define STR_OS_RED_HAT            "redhat"
#define STR_OS_RED_HAT_EN         "rhel"
#define STR_OS_RED_HAT_FULL       "Red Hat Linux"
#define STR_OS_ROCKY_LINUX        "rockyLinux"
#define STR_OS_SLACKWARE          "Slackware"
#define STR_OS_SLES               "sles"
#define STR_OS_SUSE               "suse"
#define STR_OS_SUSE_FULL          "SUSE Linux"
#define STR_OS_OPENSUSE           "opensuse"
#define STR_OS_SMESERVER          "SMEServer"
#define STR_OS_SUN_DESK           "sjds"
#define STR_OS_SUN_DESK_FULL      "Sun Java Desktop System"
#define STR_OS_TINYSOFA           "Tiny Sofa"
#define STR_OS_TURBO              "turbolinux"
#define STR_OS_TURBO_FULL         "Turbolinux"
#define STR_OS_UBUNTU             "ubuntu"
#define STR_OS_ULTRAPENGUIN       "UltraPenguin"
#define STR_OS_UNITEDLINUX        "UnitedLinux"
#define STR_OS_VALINUX            "VALinux"
#define STR_OS_YELLOW_DOG         "Yellow Dog"
#define STR_OS_ECOMSTATION        "eComStation"

/* Windows */
#define STR_OS_WIN_31                   "win31"
#define STR_OS_WIN_31_FULL              "Windows 3.1"
#define STR_OS_WIN_95                   "win95"
#define STR_OS_WIN_95_FULL              "Windows 95"
#define STR_OS_WIN_98                   "win98"
#define STR_OS_WIN_98_FULL              "Windows 98"
#define STR_OS_WIN_ME                   "winMe"
#define STR_OS_WIN_ME_FULL              "Windows Me"
#define STR_OS_WIN_NT                   "winNT"
#define STR_OS_WIN_NT_FULL              "Windows NT"
#define STR_OS_WIN_2000_PRO             "win2000Pro"
#define STR_OS_WIN_2000_PRO_FULL        "Windows 2000 Professional"
#define STR_OS_WIN_2000_SERV            "win2000Serv"
#define STR_OS_WIN_2000_SERV_FULL       "Windows 2000 Server"
#define STR_OS_WIN_2000_ADV_SERV        "win2000AdvServ"
#define STR_OS_WIN_2000_ADV_SERV_FULL   "Windows 2000 Advanced Server"
#define STR_OS_WIN_2000_DATACENT_SERV   "win2000DataCentServ"
#define STR_OS_WIN_2000_DATACENT_SERV_FULL "Windows 2000 Data Center Server"
#define STR_OS_WIN_XP_HOME              "winXPHome"
#define STR_OS_WIN_XP_HOME_FULL         "Windows XP Home Edition"
#define STR_OS_WIN_XP_PRO               "winXPPro"
#define STR_OS_WIN_XP_PRO_FULL          "Windows XP Professional"
#define STR_OS_WIN_XP_PRO_X64           "winXPPro-64"
#define STR_OS_WIN_XP_PRO_X64_FULL      "Windows XP Professional x64 Edition"
#define STR_OS_WIN_NET_WEB              "winNetWeb"
#define STR_OS_WIN_NET_WEB_FULL         "Windows Server 2003 Web Edition"
#define STR_OS_WIN_NET_ST               "winNetStandard"
#define STR_OS_WIN_NET_ST_FULL          "Windows Server 2003 Standard Edition"
#define STR_OS_WIN_NET_EN               "winNetEnterprise"
#define STR_OS_WIN_NET_EN_FULL          "Windows Server 2003 Enterprise Edition"
#define STR_OS_WIN_NET_BUS              "winNetBusiness"
#define STR_OS_WIN_NET_BUS_FULL         "Windows Server 2003 Small Business"
#define STR_OS_WIN_NET_COMPCLUSTER      "winNetComputeCluster"
#define STR_OS_WIN_NET_COMPCLUSTER_FULL "Windows Server 2003 Compute Cluster Edition"
#define STR_OS_WIN_NET_STORAGESERVER    "winNetStorageSvr"
#define STR_OS_WIN_NET_STORAGESERVER_FULL "Windows Storage Server 2003"
#define STR_OS_WIN_NET_DC_FULL          "Windows Server 2003 Datacenter Edition"
#define STR_OS_WIN_NET_DC               "winNetDatacenter"
#define STR_OS_WIN_LONG                 "longhorn"
#define STR_OS_WIN_VISTA                "winVista"
#define STR_OS_WIN_VISTA_FULL           "Windows Vista"
#define STR_OS_WIN_VISTA_X64            "winVista-64"
#define STR_OS_WIN_VISTA_X64_FULL       "Windows Vista x64 Edition"
#define STR_OS_WIN_VISTA_ULTIMATE       "winVistaUltimate-32"
#define STR_OS_WIN_VISTA_ULTIMATE_FULL  "Windows Vista Ultimate Edition"
#define STR_OS_WIN_VISTA_HOME_PREMIUM   "winVistaHomePremium-32"
#define STR_OS_WIN_VISTA_HOME_PREMIUM_FULL "Windows Vista Home Premium Edition"
#define STR_OS_WIN_VISTA_HOME_BASIC     "winVistaHomeBasic-32"
#define STR_OS_WIN_VISTA_HOME_BASIC_FULL "Windows Vista Home Basic Edition"
#define STR_OS_WIN_VISTA_ENTERPRISE     "winVistaEnterprise-32"
#define STR_OS_WIN_VISTA_ENTERPRISE_FULL "Windows Vista Enterprise Edition"
#define STR_OS_WIN_VISTA_BUSINESS       "winVistaBusiness-32"
#define STR_OS_WIN_VISTA_BUSINESS_FULL  "Windows Vista Business Edition"
#define STR_OS_WIN_VISTA_STARTER        "winVistaStarter-32"
#define STR_OS_WIN_VISTA_STARTER_FULL   "Windows Vista Starter Edition"
#define STR_OS_WIN_2008_CLUSTER         "winServer2008Cluster-32"
#define STR_OS_WIN_2008_CLUSTER_FULL    "Windows Server 2008 Cluster Server Edition"
#define STR_OS_WIN_2008_DATACENTER      "winServer2008Datacenter-32"
#define STR_OS_WIN_2008_DATACENTER_FULL "Windows Server 2008 Datacenter Edition"
#define STR_OS_WIN_2008_DATACENTER_CORE "winServer2008DatacenterCore-32"
#define STR_OS_WIN_2008_DATACENTER_CORE_FULL "Windows Server 2008 Datacenter Edition (core installation)"
#define STR_OS_WIN_2008_ENTERPRISE      "winServer2008Enterprise-32"
#define STR_OS_WIN_2008_ENTERPRISE_FULL "Windows Server 2008 Enterprise Edition"
#define STR_OS_WIN_2008_ENTERPRISE_CORE "winServer2008EnterpriseCore-32"
#define STR_OS_WIN_2008_ENTERPRISE_CORE_FULL "Windows Server 2008 Enterprise Edition (core installation)"
#define STR_OS_WIN_2008_ENTERPRISE_ITANIUM "winServer2008EnterpriseItanium-32"
#define STR_OS_WIN_2008_ENTERPRISE_ITANIUM_FULL "Windows Server 2008 Enterprise Edition for Itanium-based Systems"
#define STR_OS_WIN_2008_MEDIUM_MANAGEMENT "winServer2008MediumManagement-32"
#define STR_OS_WIN_2008_MEDIUM_MANAGEMENT_FULL "Windows Essential Business Server Management Server"
#define STR_OS_WIN_2008_MEDIUM_MESSAGING "winServer2008MediumMessaging-32"
#define STR_OS_WIN_2008_MEDIUM_MESSAGING_FULL "Windows Essential Business Server Messaging Server"
#define STR_OS_WIN_2008_MEDIUM_SECURITY "winServer2008MediumSecurity-32"
#define STR_OS_WIN_2008_MEDIUM_SECURITY_FULL "Windows Essential Business Server Security Server"
#define STR_OS_WIN_2008_SERVER_FOR_SMALLBUSINESS "winServer2008ForSmallBusiness-32"
#define STR_OS_WIN_2008_SERVER_FOR_SMALLBUSINESS_FULL "Windows Server 2008 for Windows Essential Server Solutions"
#define STR_OS_WIN_2008_SMALL_BUSINESS  "winServer2008SmallBusiness-32"
#define STR_OS_WIN_2008_SMALL_BUSINESS_FULL "Windows Server 2008 Small Business Server"
#define STR_OS_WIN_2008_SMALL_BUSINESS_PREMIUM "winServer2008SmallBusinessPremium-32"
#define STR_OS_WIN_2008_SMALL_BUSINESS_PREMIUM_FULL "Windows Server 2008 Small Business Server Premium Edition"
#define STR_OS_WIN_2008_STANDARD        "winServer2008Standard-32"
#define STR_OS_WIN_2008_STANDARD_FULL   "Windows Server 2008 Standard Edition"
#define STR_OS_WIN_2008_STANDARD_CORE   "winServer2008StandardCore-32"
#define STR_OS_WIN_2008_STANDARD_CORE_FULL "Windows Server 2008 Standard Edition (core installation)"
#define STR_OS_WIN_2008_STORAGE_ENTERPRISE "winServer2008StorageEnterprise-32"
#define STR_OS_WIN_2008_STORAGE_ENTERPRISE_FULL "Windows Server 2008 Storage Server Enterprise"
#define STR_OS_WIN_2008_STORAGE_EXPRESS  "winServer2008StorageExpress-32"
#define STR_OS_WIN_2008_STORAGE_EXPRESS_FULL "Windows Server 2008 Storage Server Express"
#define STR_OS_WIN_2008_STORAGE_STANDARD "winServer2008StorageStandard-32"
#define STR_OS_WIN_2008_STORAGE_STANDARD_FULL "Windows Server 2008 Storage Server Standard"
#define STR_OS_WIN_2008_STORAGE_WORKGROUP "winServer2008StorageWorkgroup-32"
#define STR_OS_WIN_2008_STORAGE_WORKGROUP_FULL "Windows Server 2008 Storage Server Workgroup"
#define STR_OS_WIN_2008_WEB_SERVER       "winServer2008Web-32"
#define STR_OS_WIN_2008_WEB_SERVER_FULL  "Windows Server 2008 Web Server Edition"

/* Windows 64-bit */
#define STR_OS_WIN_VISTA_ULTIMATE_X64         "winVistaUltimate-64"
#define STR_OS_WIN_VISTA_HOME_PREMIUM_X64     "winVistaHomePremium-64"
#define STR_OS_WIN_VISTA_HOME_BASIC_X64       "winVistaHomeBasic-64"
#define STR_OS_WIN_VISTA_ENTERPRISE_X64       "winVistaEnterprise-64"
#define STR_OS_WIN_VISTA_BUSINESS_X64         "winVistaBusiness-64"
#define STR_OS_WIN_VISTA_STARTER_X64          "winVistaStarter-64"

#define STR_OS_WIN_2008_CLUSTER_X64           "winServer2008Cluster-64"
#define STR_OS_WIN_2008_DATACENTER_X64        "winServer2008Datacenter-64"
#define STR_OS_WIN_2008_DATACENTER_CORE_X64   "winServer2008DatacenterCore-64"
#define STR_OS_WIN_2008_ENTERPRISE_X64        "winServer2008Enterprise-64"
#define STR_OS_WIN_2008_ENTERPRISE_CORE_X64   "winServer2008EnterpriseCore-64"
#define STR_OS_WIN_2008_MEDIUM_MANAGEMENT_X64 "winServer2008MediumManagement-64"
#define STR_OS_WIN_2008_MEDIUM_MESSAGING_X64  "winServer2008MediumMessaging-64"
#define STR_OS_WIN_2008_MEDIUM_SECURITY_X64   "winServer2008MediumSecurity-64"
#define STR_OS_WIN_2008_SERVER_FOR_SMALLBUSINESS_X64 "winServer2008ForSmallBusiness-64"
#define STR_OS_WIN_2008_SMALL_BUSINESS_X64    "winServer2008SmallBusiness-64"
#define STR_OS_WIN_2008_SMALL_BUSINESS_PREMIUM_X64 "winServer2008SmallBusinessPremium-64"
#define STR_OS_WIN_2008_STANDARD_X64          "winServer2008Standard-64"
#define STR_OS_WIN_2008_STANDARD_CORE_X64     "winServer2008StandardCore-64"
#define STR_OS_WIN_2008_STORAGE_ENTERPRISE_X64 "winServer2008StorageEnterprise-64"
#define STR_OS_WIN_2008_STORAGE_EXPRESS_X64   "winServer2008StorageExpress-64"
#define STR_OS_WIN_2008_STORAGE_STANDARD_X64  "winServer2008StorageStandard-64"
#define STR_OS_WIN_2008_STORAGE_WORKGROUP_X64 "winServer2008StorageWorkgroup-64"
#define STR_OS_WIN_2008_WEB_SERVER_X64        "winServer2008Web-64"

/* All */
#define STR_OS_64BIT_SUFFIX "-64"
#define STR_OS_64BIT_SUFFIX_FULL " (64 bit)"
#define STR_OS_EMPTY ""

/* Windows 7 */

#define STR_OS_WINDOWS       "windows"

#define STR_OS_WIN_SEVEN     STR_OS_WINDOWS "7"
#define STR_OS_WIN_SEVEN_X64 STR_OS_WIN_SEVEN STR_OS_64BIT_SUFFIX

#define STR_OS_WIN_SEVEN_GENERIC           "Windows 7"
#define STR_OS_WIN_SEVEN_STARTER_FULL      "Windows 7 Starter"
#define STR_OS_WIN_SEVEN_HOME_BASIC_FULL   "Windows 7 Home Basic"
#define STR_OS_WIN_SEVEN_HOME_PREMIUM_FULL "Windows 7 Home Premium"
#define STR_OS_WIN_SEVEN_ULTIMATE_FULL     "Windows 7 Ultimate"
#define STR_OS_WIN_SEVEN_PROFESSIONAL_FULL "Windows 7 Professional"
#define STR_OS_WIN_SEVEN_ENTERPRISE_FULL   "Windows 7 Enterprise"

/* Windows Server 2008 R2 (based on Windows 7) */

#define STR_OS_WIN_2008R2_X64 STR_OS_WINDOWS "7srv" STR_OS_64BIT_SUFFIX

#define STR_OS_WIN_2008R2_FOUNDATION_FULL "Windows Server 2008 R2 Foundation Edition"
#define STR_OS_WIN_2008R2_STANDARD_FULL   "Windows Server 2008 R2 Standard Edition"
#define STR_OS_WIN_2008R2_ENTERPRISE_FULL "Windows Server 2008 R2 Enterprise Edition"
#define STR_OS_WIN_2008R2_DATACENTER_FULL "Windows Server 2008 R2 Datacenter Edition"
#define STR_OS_WIN_2008R2_WEB_SERVER_FULL "Windows Web Server 2008 R2 Edition"

/* Windows 8 */

#define STR_OS_WIN_EIGHT               STR_OS_WINDOWS "8"
#define STR_OS_WIN_EIGHT_X64           STR_OS_WIN_EIGHT STR_OS_64BIT_SUFFIX

#define STR_OS_WIN_EIGHT_GENERIC_FULL        "Windows 8%s"
#define STR_OS_WIN_EIGHTSERVER_GENERIC_FULL  "Windows Server%s 2012"
#define STR_OS_WIN_EIGHT_FULL                "Windows 8%s"
#define STR_OS_WIN_EIGHT_PRO_FULL            "Windows 8%s Pro"
#define STR_OS_WIN_EIGHT_ENTERPRISE_FULL     "Windows 8%s Enterprise"


/* Windows Server 2012 */

#define STR_OS_WIN_EIGHTSERVER_X64 STR_OS_WINDOWS "8srv" STR_OS_64BIT_SUFFIX 

#define STR_OS_WIN_2012_FOUNDATION_FULL      "Windows Server 2012%s Foundation Edition"
#define STR_OS_WIN_2012_ESSENTIALS_FULL      "Windows Server 2012%s Essentials Edition"
#define STR_OS_WIN_2012_STANDARD_FULL        "Windows Server 2012%s Standard Edition"
#define STR_OS_WIN_2012_ENTERPRISE_FULL      "Windows Server 2012%s Enterprise Edition"
#define STR_OS_WIN_2012_DATACENTER_FULL      "Windows Server 2012%s Datacenter Edition"
#define STR_OS_WIN_2012_STORAGESERVER_FULL   "Windows Server 2012%s Storage Server"
#define STR_OS_WIN_2012_WEB_SERVER_FULL      "Windows Web Server 2012%s Edition"
#define STR_OS_WIN_2012_MULTIPOINT_STANDARD_FULL  "Windows MultiPoint Server 2012%s Standard"
#define STR_OS_WIN_2012_MULTIPOINT_PREMIUM_FULL   "Windows MultiPoint Server 2012%s Premium"


/*
 * Windows on Arm
 *
 * Window on Arm support starts with Windows 10.
 */

#define STR_OS_ARM_WIN       "arm-windows"


/*
 * Windows 10
 *
 * Microsoft renamed Windows 9 to Windows 10 at the last minute; Windows 9 was
 * never officially released. We retain the Windows 9 identifier strings as
 * Windows 10 to ensure that things continue to work.
 */

#define STR_OS_WIN_10        STR_OS_WINDOWS "9"
#define STR_OS_WIN_10_X64    STR_OS_WIN_10 STR_OS_64BIT_SUFFIX

#define STR_OS_WIN_10_GENERIC_FULL         "Windows 10"
#define STR_OS_WIN_10_HOME_FULL            "Windows 10 Home"
#define STR_OS_WIN_10_EDUCATION_FULL       "Windows 10 Education"
#define STR_OS_WIN_10_ENTERPRISE_FULL      "Windows 10 Enterprise"
#define STR_OS_WIN_10_PRO_WORKSTATION_FULL "Windows 10 Pro for Workstations"
#define STR_OS_WIN_10_PRO_FULL             "Windows 10 Pro"
#define STR_OS_WIN_10_IOTCORE_FULL         "Windows 10 IoT Core"

/*
 * Windows 11
 */

#define STR_OS_WIN_11        STR_OS_WINDOWS "11"
#define STR_OS_WIN_11_X64    STR_OS_WIN_11 STR_OS_64BIT_SUFFIX

#define STR_OS_WIN_11_GENERIC_FULL         "Windows 11"
#define STR_OS_WIN_11_HOME_FULL            "Windows 11 Home"
#define STR_OS_WIN_11_EDUCATION_FULL       "Windows 11 Education"
#define STR_OS_WIN_11_ENTERPRISE_FULL      "Windows 11 Enterprise"
#define STR_OS_WIN_11_PRO_WORKSTATION_FULL "Windows 11 Pro for Workstations"
#define STR_OS_WIN_11_PRO_FULL             "Windows 11 Pro"
#define STR_OS_WIN_11_IOTCORE_FULL         "Windows 11 IoT Core"

/*
 * Windows 12
 */

#define STR_OS_WIN_12        STR_OS_WINDOWS "12"
#define STR_OS_WIN_12_X64    STR_OS_WIN_12 STR_OS_64BIT_SUFFIX

/* No full names known yet */

/* Windows Server 2016 */

#define STR_OS_WIN_2016SRV_X64 STR_OS_WINDOWS "9srv" STR_OS_64BIT_SUFFIX

/* Windows Server 2019 */

#define STR_OS_WIN_2019SRV_X64 STR_OS_WINDOWS "2019srv" STR_OS_64BIT_SUFFIX

/* Windows Server 2022 */

#define STR_OS_WIN_2022SRV_X64 STR_OS_WINDOWS "2019srvNext" STR_OS_64BIT_SUFFIX

/* Windows Server 2025 */

#define STR_OS_WIN_2025SRV_X64 STR_OS_WINDOWS "2022srvNext" STR_OS_64BIT_SUFFIX

/* THIS SPACE FOR RENT (Windows 10 and later official server variant names) */

#define STR_OS_WIN_10_SERVER_2016_GENERIC_FULL "Windows Server 2016"
#define STR_OS_WIN_10_SERVER_2019_GENERIC_FULL "Windows Server 2019"
#define STR_OS_WIN_11_SERVER_2022_GENERIC_FULL "Windows Server 2022"
#define STR_OS_WIN_11_SERVER_2025_GENERIC_FULL "Windows Server 2025"

/* Microsoft Hyper-V */
#define STR_OS_HYPER_V      "winHyperV"
#define STR_OS_HYPER_V_FULL "Hyper-V Server"

/* Windows Future/Unknown */

#define STR_OS_WIN_UNKNOWN                   "windowsUnknown"
#define STR_OS_WIN_UNKNOWN_X64               STR_OS_WIN_UNKNOWN STR_OS_64BIT_SUFFIX
#define STR_OS_WIN_UNKNOWN_GENERIC           "Windows Unknown"

/* Modifiers for Windows Vista, Windows Server 2008, and later. */
#define STR_OS_WIN_32_BIT_EXTENSION ", 32-bit"
#define STR_OS_WIN_64_BIT_EXTENSION ", 64-bit"

/* FreeBSD */
#define STR_OS_FREEBSD "freeBSD"

/* Solaris */
#define STR_OS_SOLARIS "solaris"

/* Netware */
#define STR_OS_NETWARE "netware"

/* Mac OS */
#define STR_OS_MACOS "darwin"

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
070701000000D0000081A400000000000000000000000168225505000088A8000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/include/guest_os_tables.h  /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _GUEST_OS_TABLES_H_
#define _GUEST_OS_TABLES_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Unless explicitly specified (i.e. _ARM_), all guests are assumed to be
 * Intel architecture.
 */

#define GUEST_OS_TYPE_GEN                                                  \
   GOT(GUEST_OS_ANY)                                                       \
   GOT(GUEST_OS_DOS)                                                       \
   GOT(GUEST_OS_WIN31)                                                     \
   GOT(GUEST_OS_WIN95)                                                     \
   GOT(GUEST_OS_WIN98)                                                     \
   GOT(GUEST_OS_WINME)                                                     \
   GOT(GUEST_OS_WINNT)                                                     \
   GOT(GUEST_OS_WIN2000)                                                   \
   GOT(GUEST_OS_WINXP)                                                     \
   GOT(GUEST_OS_WINXPPRO_64)                                               \
   GOT(GUEST_OS_WINNET)                                                    \
   GOT(GUEST_OS_WINNET_64)                                                 \
   GOT(GUEST_OS_LONGHORN)                                                  \
   GOT(GUEST_OS_LONGHORN_64)                                               \
   GOT(GUEST_OS_WINVISTA)                                                  \
   GOT(GUEST_OS_WINVISTA_64)                                               \
   GOT(GUEST_OS_WIN_7)               /* Windows 7 32-bit */                \
   GOT(GUEST_OS_WIN_7_64)            /* Windows 7 64-bit */                \
   GOT(GUEST_OS_WIN_8)               /* Windows 8 32-bit */                \
   GOT(GUEST_OS_WIN_8_64)            /* Windows 8 64-bit */                \
   GOT(GUEST_OS_WIN_10)              /* Windows 10 32-bit */               \
   GOT(GUEST_OS_WIN_10_64)           /* Windows 10 64-bit */               \
   GOT(GUEST_OS_WIN_10_ARM_64)       /* Windows 10 Arm 64-bit */           \
   GOT(GUEST_OS_WIN_11_64)           /* Windows 11 64-bit */               \
   GOT(GUEST_OS_WIN_11_ARM_64)       /* Windows 11 Arm 64-bit */           \
   GOT(GUEST_OS_WIN_12_64)           /* Windows 12 64-bit */               \
   GOT(GUEST_OS_WIN_12_ARM_64)       /* Windows 12 Arm 64-bit */           \
   GOT(GUEST_OS_WIN2008R2_64)        /* Server 2008 R2 64-bit */           \
   GOT(GUEST_OS_WIN_8_SERVER_64)     /* Windows 8 Server 64-bit */         \
   GOT(GUEST_OS_WIN_2016SRV_64)      /* Windows Server 2016 64-bit */      \
   GOT(GUEST_OS_WIN_2019SRV_64)      /* Windows Server 2019 64-bit */      \
   GOT(GUEST_OS_WIN_2022SRV_64)      /* Windows Server 2022 64-bit */      \
   GOT(GUEST_OS_WIN_2025SRV_64)      /* Windows Server 2025 64-bit */      \
   GOT(GUEST_OS_HYPER_V)             /* Microsoft Hyper-V */               \
   GOT(GUEST_OS_OS2)                                                       \
   GOT(GUEST_OS_ECOMSTATION)         /* OS/2 variant; 1.x */               \
   GOT(GUEST_OS_ECOMSTATION2)        /* OS/2 variant; 2.x */               \
   GOT(GUEST_OS_OTHERLINUX)                                                \
   GOT(GUEST_OS_OTHERLINUX_64)                                             \
   GOT(GUEST_OS_OTHER24XLINUX)                                             \
   GOT(GUEST_OS_OTHER24XLINUX_64)                                          \
   GOT(GUEST_OS_OTHER26XLINUX)                                             \
   GOT(GUEST_OS_OTHER26XLINUX_64)                                          \
   GOT(GUEST_OS_OTHER3XLINUX)        /* Linux 3.x 32-bit */                \
   GOT(GUEST_OS_OTHER3XLINUX_64)     /* Linux 3.x 64-bit */                \
   GOT(GUEST_OS_OTHER4XLINUX)        /* Linux 4.x 32-bit */                \
   GOT(GUEST_OS_OTHER4XLINUX_64)     /* Linux 4.x 64-bit  */               \
   GOT(GUEST_OS_OTHER5XLINUX)        /* Linux 5.x 32-bit */                \
   GOT(GUEST_OS_OTHER5XLINUX_64)     /* Linux 5.x 64-bit */                \
   GOT(GUEST_OS_OTHER5XLINUX_ARM_64) /* Linux 5.x Arm 64-bit */            \
   GOT(GUEST_OS_OTHER6XLINUX)        /* Linux 6.x 32-bit */                \
   GOT(GUEST_OS_OTHER6XLINUX_64)     /* Linux 6.x 64-bit */                \
   GOT(GUEST_OS_OTHER6XLINUX_ARM_64) /* Linux 6.x Arm 64-bit */            \
   GOT(GUEST_OS_OTHER7XLINUX)        /* Linux 7.x and later 32-bit */      \
   GOT(GUEST_OS_OTHER7XLINUX_64)     /* Linux 7.x and later 64-bit */      \
   GOT(GUEST_OS_OTHER7XLINUX_ARM_64) /* Linux 7.x and later Arm 64-bit */  \
   GOT(GUEST_OS_OTHER)               /* Other 32-bit */                    \
   GOT(GUEST_OS_OTHER_64)            /* Other 64-bit */                    \
   GOT(GUEST_OS_OTHER_ARM_64)        /* Other Arm 64-bit */                \
   GOT(GUEST_OS_UBUNTU)                                                    \
   GOT(GUEST_OS_UBUNTU_64)                                                 \
   GOT(GUEST_OS_UBUNTU_ARM_64)                                             \
   GOT(GUEST_OS_DEBIAN)                                                    \
   GOT(GUEST_OS_DEBIAN_64)                                                 \
   GOT(GUEST_OS_DEBIAN_ARM_64)                                             \
   GOT(GUEST_OS_RHEL)                                                      \
   GOT(GUEST_OS_RHEL_64)                                                   \
   GOT(GUEST_OS_RHEL9_64)                                                  \
   GOT(GUEST_OS_RHEL9_ARM_64)                                              \
   GOT(GUEST_OS_RHEL10_64)                                                 \
   GOT(GUEST_OS_RHEL10_ARM_64)                                             \
   GOT(GUEST_OS_FREEBSD)                                                   \
   GOT(GUEST_OS_FREEBSD_64)                                                \
   GOT(GUEST_OS_FREEBSD11)                                                 \
   GOT(GUEST_OS_FREEBSD11_64)                                              \
   GOT(GUEST_OS_FREEBSD12)                                                 \
   GOT(GUEST_OS_FREEBSD12_64)                                              \
   GOT(GUEST_OS_FREEBSD13)                                                 \
   GOT(GUEST_OS_FREEBSD13_64)                                              \
   GOT(GUEST_OS_FREEBSD13_ARM_64)                                          \
   GOT(GUEST_OS_FREEBSD14)                                                 \
   GOT(GUEST_OS_FREEBSD14_64)                                              \
   GOT(GUEST_OS_FREEBSD14_ARM_64)                                          \
   GOT(GUEST_OS_FREEBSD15)                                                 \
   GOT(GUEST_OS_FREEBSD15_64)                                              \
   GOT(GUEST_OS_FREEBSD15_ARM_64)                                          \
   GOT(GUEST_OS_SOLARIS_6_AND_7)                                           \
   GOT(GUEST_OS_SOLARIS8)                                                  \
   GOT(GUEST_OS_SOLARIS9)                                                  \
   GOT(GUEST_OS_SOLARIS10)                                                 \
   GOT(GUEST_OS_SOLARIS10_64)                                              \
   GOT(GUEST_OS_SOLARIS11_64)                                              \
   GOT(GUEST_OS_DARWIN9)             /* Mac OS 10.5 */                     \
   GOT(GUEST_OS_DARWIN9_64)                                                \
   GOT(GUEST_OS_DARWIN10)            /* Mac OS 10.6 */                     \
   GOT(GUEST_OS_DARWIN10_64)                                               \
   GOT(GUEST_OS_DARWIN11)            /* Mac OS 10.7 */                     \
   GOT(GUEST_OS_DARWIN11_64)                                               \
   GOT(GUEST_OS_DARWIN12_64)         /* Mac OS 10.8 */                     \
   GOT(GUEST_OS_DARWIN13_64)         /* Mac OS 10.9 */                     \
   GOT(GUEST_OS_DARWIN14_64)         /* Mac OS 10.10 */                    \
   GOT(GUEST_OS_DARWIN15_64)         /* Mac OS 10.11 */                    \
   GOT(GUEST_OS_DARWIN16_64)         /* Mac OS 10.12 */                    \
   GOT(GUEST_OS_DARWIN17_64)         /* Mac OS 10.13 */                    \
   GOT(GUEST_OS_DARWIN18_64)         /* Mac OS 10.14 */                    \
   GOT(GUEST_OS_DARWIN19_64)         /* Mac OS 10.15 */                    \
   GOT(GUEST_OS_DARWIN20_64)         /* Mac OS 11 */                       \
   GOT(GUEST_OS_DARWIN21_64)         /* Mac OS 12 */                       \
   GOT(GUEST_OS_DARWIN22_64)         /* Mac OS 13 */                       \
   GOT(GUEST_OS_DARWIN23_64)         /* Mac OS 14 */                       \
   GOT(GUEST_OS_DARWIN24_64)         /* Mac OS 15 */                       \
   GOT(GUEST_OS_OPENSERVER_5_AND_6)                                        \
   GOT(GUEST_OS_UNIXWARE7)                                                 \
   GOT(GUEST_OS_NETWARE4)                                                  \
   GOT(GUEST_OS_NETWARE5)                                                  \
   GOT(GUEST_OS_NETWARE6)                                                  \
   GOT(GUEST_OS_VMKERNEL)            /* ESX 4.x 64-bit */                  \
   GOT(GUEST_OS_VMKERNEL5)           /* ESX 5.x 64-bit */                  \
   GOT(GUEST_OS_VMKERNEL6)           /* ESX 6 64-bit */                    \
   GOT(GUEST_OS_VMKERNEL65)          /* ESX 6.5 and 6.7 64-bit */          \
   GOT(GUEST_OS_VMKERNEL7)           /* ESX 7 64-bit */                    \
   GOT(GUEST_OS_VMKERNEL7_ARM)       /* ESX 7 Arm 64-bit */                \
   GOT(GUEST_OS_VMKERNEL8)           /* ESX 8 64-bit */                    \
   GOT(GUEST_OS_VMKERNEL8_ARM)       /* ESX 8 Arm 64-bit */                \
   GOT(GUEST_OS_VMKERNEL9)           /* ESX 9 and later 64-bit */          \
   GOT(GUEST_OS_VMKERNEL9_ARM)       /* ESX 9 and later Arm 64-bit */      \
   GOT(GUEST_OS_PHOTON_64)           /* VMware Photon 64-bit */            \
   GOT(GUEST_OS_PHOTON_ARM_64)       /* VMware Photon Arm 64-bit */        \
   GOT(GUEST_OS_ORACLE)                                                    \
   GOT(GUEST_OS_ORACLE_64)                                                 \
   GOT(GUEST_OS_ORACLE6)                                                   \
   GOT(GUEST_OS_ORACLE6_64)                                                \
   GOT(GUEST_OS_ORACLE7_64)                                                \
   GOT(GUEST_OS_ORACLE8_64)                                                \
   GOT(GUEST_OS_ORACLE9_64)                                                \
   GOT(GUEST_OS_ORACLE10_64)                                               \
   GOT(GUEST_OS_CENTOS)                                                    \
   GOT(GUEST_OS_CENTOS_64)                                                 \
   GOT(GUEST_OS_CENTOS6)                                                   \
   GOT(GUEST_OS_CENTOS6_64)                                                \
   GOT(GUEST_OS_CENTOS7_64)                                                \
   GOT(GUEST_OS_CENTOS8_64)                                                \
   GOT(GUEST_OS_CENTOS9_64)                                                \
   GOT(GUEST_OS_AMAZONLINUX2_64)                                           \
   GOT(GUEST_OS_AMAZONLINUX3_64)                                           \
   GOT(GUEST_OS_CRXSYS1_64)        /* VMware CRX system VM 1.0 64-bit */   \
   GOT(GUEST_OS_CRXSYS1_ARM_64)    /* VMware CRX system VM 1.0 Arm 64-bit */ \
   GOT(GUEST_OS_CRXSYS2_64)        /* VMware CRX system VM 2.0 64-bit */   \
   GOT(GUEST_OS_CRXSYS2_ARM_64)    /* VMware CRX system VM 2.0 Arm 64-bit */ \
   GOT(GUEST_OS_CRXPOD1_64)        /* VMware CRX pod VM 1.0 64-bit */      \
   GOT(GUEST_OS_CRXPOD1_ARM_64)    /* VMware CRX pod VM 1.0 Arm 64-bit */  \
   GOT(GUEST_OS_LINUX_MINT_64)                                             \
   GOT(GUEST_OS_ROCKY_LINUX_64)                                            \
   GOT(GUEST_OS_ROCKY_LINUX_ARM_64)                                        \
   GOT(GUEST_OS_ALMA_LINUX_64)                                             \
   GOT(GUEST_OS_ALMA_LINUX_ARM_64)                                         \
   GOT(GUEST_OS_PROLINUX_64)                                               \
   GOT(GUEST_OS_PARDUS_64)


/*
 * Mappings between VIM guest OS keys and the rest of the civilized world.
 *
 * Format: GOKM(vmxKey, vimKey, reversible)
 */
#define GUEST_OS_KEY_MAP \
   /* Windows guests */ \
   GOKM("win31",                                win31Guest,              TRUE) \
   GOKM("win95",                                win95Guest,              TRUE) \
   GOKM("win98",                                win98Guest,              TRUE) \
   GOKM("winMe",                                winMeGuest,              TRUE) \
   GOKM("winNT",                                winNTGuest,              TRUE) \
   GOKM("nt4",                                  winNTGuest,              FALSE) \
   GOKM("win2000",                              win2000ProGuest,         FALSE) \
   GOKM("win2000Pro",                           win2000ProGuest,         TRUE) \
   GOKM("win2000Serv",                          win2000ServGuest,        TRUE) \
   GOKM("win2000AdvServ",                       win2000AdvServGuest,     TRUE) \
   GOKM("winXPHome",                            winXPHomeGuest,          TRUE) \
   GOKM("whistler",                             winXPHomeGuest,          FALSE) \
   GOKM("winXPPro",                             winXPProGuest,           TRUE) \
   GOKM("winXPPro-64",                          winXPPro64Guest,         TRUE) \
   GOKM("winNetWeb",                            winNetWebGuest,          TRUE) \
   GOKM("winNetStandard",                       winNetStandardGuest,     TRUE) \
   GOKM("winNetEnterprise",                     winNetEnterpriseGuest,   TRUE) \
   GOKM("winNetDatacenter",                     winNetDatacenterGuest,   TRUE) \
   GOKM("winNetBusiness",                       winNetBusinessGuest,     TRUE) \
   GOKM("winNetStandard-64",                    winNetStandard64Guest,   TRUE) \
   GOKM("winNetEnterprise-64",                  winNetEnterprise64Guest, TRUE) \
   GOKM("winNetDatacenter-64",                  winNetDatacenter64Guest, TRUE) \
   GOKM("longhorn",                             winLonghornGuest,        TRUE) \
   GOKM("longhorn-64",                          winLonghorn64Guest,      TRUE) \
   GOKM("winvista",                             winVistaGuest,           TRUE) \
   GOKM("winvista-64",                          winVista64Guest,         TRUE) \
   GOKM("windows7",                             windows7Guest,           TRUE) \
   GOKM("windows7-64",                          windows7_64Guest,        TRUE) \
   GOKM("windows7srv-64",                       windows7Server64Guest,   TRUE) \
   GOKM("windows8",                             windows8Guest,           TRUE) \
   GOKM("windows8-64",                          windows8_64Guest,        TRUE) \
   GOKM("windows8srv-64",                       windows8Server64Guest,   TRUE) \
   GOKM("windows9",                             windows9Guest,           TRUE) \
   GOKM("windows9-64",                          windows9_64Guest,        TRUE) \
   GOKM("windows11-64",                         windows11_64Guest,       TRUE) \
   GOKM("windows12-64",                         windows12_64Guest,       TRUE) \
   GOKM("windows9srv-64",                       windows9Server64Guest,   TRUE) \
   GOKM("windows2019srv-64",                    windows2019srv_64Guest,  TRUE) \
   GOKM("windows2019srvNext-64",                windows2019srvNext_64Guest, TRUE) \
   GOKM("windows2022srvNext-64",                windows2022srvNext_64Guest, TRUE) \
   GOKM("winHyperV",                            windowsHyperVGuest,      TRUE) \
   GOKM("winServer2008Cluster-32",              winLonghornGuest,        FALSE) \
   GOKM("winServer2008Datacenter-32",           winLonghornGuest,        FALSE) \
   GOKM("winServer2008DatacenterCore-32",       winLonghornGuest,        FALSE) \
   GOKM("winServer2008Enterprise-32",           winLonghornGuest,        FALSE) \
   GOKM("winServer2008EnterpriseCore-32",       winLonghornGuest,        FALSE) \
   GOKM("winServer2008EnterpriseItanium-32",    winLonghornGuest,        FALSE) \
   GOKM("winServer2008SmallBusiness-32",        winLonghornGuest,        FALSE) \
   GOKM("winServer2008SmallBusinessPremium-32", winLonghornGuest,        FALSE) \
   GOKM("winServer2008Standard-32",             winLonghornGuest,        FALSE) \
   GOKM("winServer2008StandardCore-32",         winLonghornGuest,        FALSE) \
   GOKM("winServer2008MediumManagement-32",     winLonghornGuest,        FALSE) \
   GOKM("winServer2008MediumMessaging-32",      winLonghornGuest,        FALSE) \
   GOKM("winServer2008MediumSecurity-32",       winLonghornGuest,        FALSE) \
   GOKM("winServer2008ForSmallBusiness-32",     winLonghornGuest,        FALSE) \
   GOKM("winServer2008StorageEnterprise-32",    winLonghornGuest,        FALSE) \
   GOKM("winServer2008StorageExpress-32",       winLonghornGuest,        FALSE) \
   GOKM("winServer2008StorageStandard-32",      winLonghornGuest,        FALSE) \
   GOKM("winServer2008StorageWorkgroup-32",     winLonghornGuest,        FALSE) \
   GOKM("winServer2008Web-32",                  winLonghornGuest,        FALSE) \
   GOKM("winServer2008Cluster-64",              winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008Datacenter-64",           winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008DatacenterCore-64",       winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008Enterprise-64",           winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008EnterpriseCore-64",       winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008EnterpriseItanium-64",    winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008SmallBusiness-64",        winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008SmallBusinessPremium-64", winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008Standard-64",             winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008StandardCore-64",         winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008MediumManagement-64",     winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008MediumMessaging-64",      winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008MediumSecurity-64",       winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008ForSmallBusiness-64",     winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008StorageEnterprise-64",    winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008StorageExpress-64",       winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008StorageStandard-64",      winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008StorageWorkgroup-64",     winLonghorn64Guest,      FALSE) \
   GOKM("winServer2008Web-64",                  winLonghorn64Guest,      FALSE) \
   GOKM("winVistaUltimate-32",                  winVistaGuest,           FALSE) \
   GOKM("winVistaHomePremium-32",               winVistaGuest,           FALSE) \
   GOKM("winVistaHomeBasic-32",                 winVistaGuest,           FALSE) \
   GOKM("winVistaEnterprise-32",                winVistaGuest,           FALSE) \
   GOKM("winVistaBusiness-32",                  winVistaGuest,           FALSE) \
   GOKM("winVistaStarter-32",                   winVistaGuest,           FALSE) \
   GOKM("winVistaUltimate-64",                  winVista64Guest,         FALSE) \
   GOKM("winVistaHomePremium-64",               winVista64Guest,         FALSE) \
   GOKM("winVistaHomeBasic-64",                 winVista64Guest,         FALSE) \
   GOKM("winVistaEnterprise-64",                winVista64Guest,         FALSE) \
   GOKM("winVistaBusiness-64",                  winVista64Guest,         FALSE) \
   GOKM("winVistaStarter-64",                   winVista64Guest,         FALSE) \
   /* Linux guests */ \
   GOKM("redhat",                               redhatGuest,             TRUE) \
   GOKM("rhel2",                                rhel2Guest,              TRUE) \
   GOKM("rhel3",                                rhel3Guest,              TRUE) \
   GOKM("rhel3-64",                             rhel3_64Guest,           TRUE) \
   GOKM("rhel4",                                rhel4Guest,              TRUE) \
   GOKM("rhel4-64",                             rhel4_64Guest,           TRUE) \
   GOKM("rhel5",                                rhel5Guest,              TRUE) \
   GOKM("rhel5-64",                             rhel5_64Guest,           TRUE) \
   GOKM("rhel6",                                rhel6Guest,              TRUE) \
   GOKM("rhel6-64",                             rhel6_64Guest,           TRUE) \
   GOKM("rhel7",                                rhel7Guest,              TRUE) \
   GOKM("rhel7-64",                             rhel7_64Guest,           TRUE) \
   GOKM("rhel8-64",                             rhel8_64Guest,           TRUE) \
   GOKM("rhel9-64",                             rhel9_64Guest,           TRUE) \
   GOKM("rhel10-64",                            rhel10_64Guest,          TRUE) \
   GOKM("centos",                               centosGuest,             TRUE) \
   GOKM("centos-64",                            centos64Guest,           TRUE) \
   GOKM("centos6",                              centos6Guest,            TRUE) \
   GOKM("centos6-64",                           centos6_64Guest,         TRUE) \
   GOKM("centos7",                              centos7Guest,            FALSE) \
   GOKM("centos7-64",                           centos7_64Guest,         TRUE) \
   GOKM("centos8-64",                           centos8_64Guest,         TRUE) \
   GOKM("centos9-64",                           centos9_64Guest,         TRUE) \
   GOKM("oraclelinux",                          oracleLinuxGuest,        TRUE) \
   GOKM("oraclelinux-64",                       oracleLinux64Guest,      TRUE) \
   GOKM("oraclelinux6",                         oracleLinux6Guest,       TRUE) \
   GOKM("oraclelinux6-64",                      oracleLinux6_64Guest,    TRUE) \
   GOKM("oraclelinux7",                         oracleLinux7Guest,       FALSE) \
   GOKM("oraclelinux7-64",                      oracleLinux7_64Guest,    TRUE) \
   GOKM("oraclelinux8-64",                      oracleLinux8_64Guest,    TRUE) \
   GOKM("oraclelinux9-64",                      oracleLinux9_64Guest,    TRUE) \
   GOKM("oraclelinux10-64",                     oracleLinux10_64Guest,   TRUE) \
   GOKM("suse",                                 suseGuest,               TRUE) \
   GOKM("suse-64",                              suse64Guest,             TRUE) \
   GOKM("sles",                                 slesGuest,               TRUE) \
   GOKM("sles-64",                              sles64Guest,             TRUE) \
   GOKM("sles10",                               sles10Guest,             TRUE) \
   GOKM("sles10-64",                            sles10_64Guest,          TRUE) \
   GOKM("sles11",                               sles11Guest,             TRUE) \
   GOKM("sles11-64",                            sles11_64Guest,          TRUE) \
   GOKM("sles12",                               sles12Guest,             TRUE) \
   GOKM("sles12-64",                            sles12_64Guest,          TRUE) \
   GOKM("sles15-64",                            sles15_64Guest,          TRUE) \
   GOKM("sles16-64",                            sles16_64Guest,          TRUE) \
   GOKM("mandrake",                             mandrakeGuest,           TRUE) \
   GOKM("mandrake-64",                          mandriva64Guest,         FALSE) \
   GOKM("mandriva",                             mandrivaGuest,           TRUE) \
   GOKM("mandriva-64",                          mandriva64Guest,         TRUE) \
   GOKM("turbolinux",                           turboLinuxGuest,         TRUE) \
   GOKM("turbolinux-64",                        turboLinux64Guest,       TRUE) \
   GOKM("ubuntu",                               ubuntuGuest,             TRUE) \
   GOKM("ubuntu-64",                            ubuntu64Guest,           TRUE) \
   GOKM("debian4",                              debian4Guest,            TRUE) \
   GOKM("debian4-64",                           debian4_64Guest,         TRUE) \
   GOKM("debian5",                              debian5Guest,            TRUE) \
   GOKM("debian5-64",                           debian5_64Guest,         TRUE) \
   GOKM("debian6",                              debian6Guest,            TRUE) \
   GOKM("debian6-64",                           debian6_64Guest,         TRUE) \
   GOKM("debian7",                              debian7Guest,            TRUE) \
   GOKM("debian7-64",                           debian7_64Guest,         TRUE) \
   GOKM("debian8",                              debian8Guest,            TRUE) \
   GOKM("debian8-64",                           debian8_64Guest,         TRUE) \
   GOKM("debian9",                              debian9Guest,            TRUE) \
   GOKM("debian9-64",                           debian9_64Guest,         TRUE) \
   GOKM("debian10",                             debian10Guest,           TRUE) \
   GOKM("debian10-64",                          debian10_64Guest,        TRUE) \
   GOKM("debian11",                             debian11Guest,           TRUE) \
   GOKM("debian11-64",                          debian11_64Guest,        TRUE) \
   GOKM("debian12",                             debian12Guest,           TRUE) \
   GOKM("debian12-64",                          debian12_64Guest,        TRUE) \
   GOKM("debian13",                             debian13Guest,           TRUE) \
   GOKM("debian13-64",                          debian13_64Guest,        TRUE) \
   GOKM("asianux3",                             asianux3Guest,           TRUE) \
   GOKM("asianux3-64",                          asianux3_64Guest,        TRUE) \
   GOKM("asianux4",                             asianux4Guest,           TRUE) \
   GOKM("asianux4-64",                          asianux4_64Guest,        TRUE) \
   GOKM("asianux5-64",                          asianux5_64Guest,        TRUE) \
   GOKM("asianux7-64",                          asianux7_64Guest,        TRUE) \
   GOKM("asianux8-64",                          asianux8_64Guest,        TRUE) \
   GOKM("asianux9-64",                          asianux9_64Guest,        TRUE) \
   GOKM("nld9",                                 nld9Guest,               TRUE) \
   GOKM("oes",                                  oesGuest,                TRUE) \
   GOKM("sjds",                                 sjdsGuest,               TRUE) \
   GOKM("opensuse",                             opensuseGuest,           TRUE) \
   GOKM("opensuse-64",                          opensuse64Guest,         TRUE) \
   GOKM("fedora",                               fedoraGuest,             TRUE) \
   GOKM("fedora-64",                            fedora64Guest,           TRUE) \
   GOKM("coreos-64",                            coreos64Guest,           TRUE) \
   GOKM("vmware-photon-64",                     vmwarePhoton64Guest,     TRUE) \
   GOKM("other24xlinux",                        other24xLinuxGuest,      TRUE) \
   GOKM("other24xlinux-64",                     other24xLinux64Guest,    TRUE) \
   GOKM("other26xlinux",                        other26xLinuxGuest,      TRUE) \
   GOKM("other26xlinux-64",                     other26xLinux64Guest,    TRUE) \
   GOKM("other3xlinux",                         other3xLinuxGuest,       TRUE) \
   GOKM("other3xlinux-64",                      other3xLinux64Guest,     TRUE) \
   GOKM("other4xlinux",                         other4xLinuxGuest,       TRUE) \
   GOKM("other4xlinux-64",                      other4xLinux64Guest,     TRUE) \
   GOKM("other5xlinux",                         other5xLinuxGuest,       TRUE) \
   GOKM("other5xlinux-64",                      other5xLinux64Guest,     TRUE) \
   GOKM("other6xlinux",                         other6xLinuxGuest,       TRUE) \
   GOKM("other6xlinux-64",                      other6xLinux64Guest,     TRUE) \
   GOKM("other7xlinux",                         other7xLinuxGuest,       TRUE) \
   GOKM("other7xlinux-64",                      other7xLinux64Guest,     TRUE) \
   GOKM("linux",                                otherLinuxGuest,         FALSE) \
   GOKM("otherlinux",                           otherLinuxGuest,         TRUE) \
   GOKM("otherlinux-64",                        otherLinux64Guest,       TRUE) \
   GOKM("genericlinux",                         genericLinuxGuest,       TRUE) \
   GOKM("amazonlinux2-64",                      amazonlinux2_64Guest,    TRUE) \
   GOKM("amazonlinux3-64",                      amazonlinux3_64Guest,    TRUE) \
   GOKM("almalinux-64",                         almalinux_64Guest,       TRUE) \
   GOKM("rockylinux-64",                        rockylinux_64Guest,      TRUE) \
   GOKM("CRXPod1-64",                           crxPod1Guest,            TRUE) \
   GOKM("CRXSys1-64",                           crxSys1Guest,            TRUE) \
   /* Netware guests */ \
   GOKM("netware4",                             netware4Guest,           TRUE) \
   GOKM("netware5",                             netware5Guest,           TRUE) \
   GOKM("netware6",                             netware6Guest,           TRUE) \
   /* Solaris guests */ \
   GOKM("solaris6",                             solaris6Guest,           TRUE) \
   GOKM("solaris7",                             solaris7Guest,           TRUE) \
   GOKM("solaris8",                             solaris8Guest,           TRUE) \
   GOKM("solaris9",                             solaris9Guest,           TRUE) \
   GOKM("solaris10",                            solaris10Guest,          TRUE) \
   GOKM("solaris10-64",                         solaris10_64Guest,       TRUE) \
   GOKM("solaris11-64",                         solaris11_64Guest,       TRUE) \
   /* macOS guests */ \
   GOKM("darwin",                               darwinGuest,             TRUE) \
   GOKM("darwin-64",                            darwin64Guest,           TRUE) \
   GOKM("darwin10",                             darwin10Guest,           TRUE) \
   GOKM("darwin10-64",                          darwin10_64Guest,        TRUE) \
   GOKM("darwin11",                             darwin11Guest,           TRUE) \
   GOKM("darwin11-64",                          darwin11_64Guest,        TRUE) \
   GOKM("darwin12-64",                          darwin12_64Guest,        TRUE) \
   GOKM("darwin13-64",                          darwin13_64Guest,        TRUE) \
   GOKM("darwin14-64",                          darwin14_64Guest,        TRUE) \
   GOKM("darwin15-64",                          darwin15_64Guest,        TRUE) \
   GOKM("darwin16-64",                          darwin16_64Guest,        TRUE) \
   GOKM("darwin17-64",                          darwin17_64Guest,        TRUE) \
   GOKM("darwin18-64",                          darwin18_64Guest,        TRUE) \
   GOKM("darwin19-64",                          darwin19_64Guest,        TRUE) \
   GOKM("darwin20-64",                          darwin20_64Guest,        TRUE) \
   GOKM("darwin21-64",                          darwin21_64Guest,        TRUE) \
   GOKM("darwin22-64",                          darwin22_64Guest,        TRUE) \
   GOKM("darwin23-64",                          darwin23_64Guest,        TRUE) \
   /* ESX guests */ \
   GOKM("vmkernel",                             vmkernelGuest,           TRUE) \
   GOKM("vmkernel5",                            vmkernel5Guest,          TRUE) \
   GOKM("vmkernel6",                            vmkernel6Guest,          TRUE) \
   GOKM("vmkernel65",                           vmkernel65Guest,         TRUE) \
   GOKM("vmkernel7",                            vmkernel7Guest,          TRUE) \
   GOKM("vmkernel8",                            vmkernel8Guest,          TRUE) \
   /* Other guests */ \
   GOKM("dos",                                  dosGuest,                TRUE) \
   GOKM("os2",                                  os2Guest,                TRUE) \
   GOKM("os2experimental",                      os2Guest,                FALSE) \
   GOKM("eComStation",                          eComStationGuest,        TRUE) \
   GOKM("eComStation2",                         eComStation2Guest,       TRUE) \
   GOKM("freeBSD",                              freebsdGuest,            TRUE) \
   GOKM("freeBSD-64",                           freebsd64Guest,          TRUE) \
   GOKM("freeBSD11",                            freebsd11Guest,          TRUE) \
   GOKM("freeBSD11-64",                         freebsd11_64Guest,       TRUE) \
   GOKM("freeBSD12",                            freebsd12Guest,          TRUE) \
   GOKM("freeBSD12-64",                         freebsd12_64Guest,       TRUE) \
   GOKM("freeBSD13",                            freebsd13Guest,          TRUE) \
   GOKM("freeBSD13-64",                         freebsd13_64Guest,       TRUE) \
   GOKM("freeBSD14",                            freebsd14Guest,          TRUE) \
   GOKM("freeBSD14-64",                         freebsd14_64Guest,       TRUE) \
   GOKM("freeBSD15",                            freebsd15Guest,          TRUE) \
   GOKM("freeBSD15-64",                         freebsd15_64Guest,       TRUE) \
   GOKM("openserver5",                          openServer5Guest,        TRUE) \
   GOKM("openserver6",                          openServer6Guest,        TRUE) \
   GOKM("unixware7",                            unixWare7Guest,          TRUE) \
   GOKM("other",                                otherGuest,              TRUE) \
   GOKM("other-64",                             otherGuest64,            TRUE) \


#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
070701000000D1000081A40000000000000000000000016822550500000BC1000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hashMap.h  /*********************************************************
 * Copyright (C) 2009-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef _HASHMAP_H_
#define _HASHMAP_H_

#include <vmware.h>
#ifdef VMX86_SERVER
#include "aioMgr.h"
#endif

#if defined(__cplusplus)
extern "C" {
#endif

typedef struct HashMap HashMap;

/*
 * ----------------------------------------------------------------------------
 *
 * HashMapIteratorFn --
 *
 *    A function pointer definition that should be passed to HashMap_Iterate,
 *    it will be called for all entries in the hashmap.  The key and data will
 *    be set appropriately and the user data pointer will be passed untouched
 *    from the HashMap_Iterate call.
 *
 * Results:
 *    void
 *
 * Side Effects:
 *    Implementation dependent.
 *
 * ----------------------------------------------------------------------------
 */

typedef void (* HashMapIteratorFn)(void *key, void *data, void *userData);

HashMap *HashMap_AllocMap(uint32 numEntries, size_t keySize, size_t dataSize);
HashMap *HashMap_AllocMapAlpha(uint32 numEntries, uint32 alpha, size_t keySize,
                               size_t dataSize);
void HashMap_DestroyMap(HashMap *map);
Bool HashMap_Put(HashMap *map, const void *key, const void *data);
void *HashMap_Get(HashMap *map, const void *key);
void *HashMap_ConstTimeGet(struct HashMap *map, const void *key);
void HashMap_Clear(HashMap *map);
Bool HashMap_Remove(HashMap *map, const void *key);
uint32 HashMap_Count(HashMap *map);
void HashMap_Iterate(HashMap* map, HashMapIteratorFn mapFn, Bool clear,
      void *userData);
Bool HashMap_DoTests(void);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _HASHMAP_H_ */
   070701000000D2000081A400000000000000000000000168225505000017B6000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hashTable.h    /*********************************************************
 * Copyright (C) 2004-2017,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hashTable.h --
 *
 *      Hash table.
 */

#ifndef _HASH_TABLE_H_
#define _HASH_TABLE_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_defs.h"
#include "vm_atomic.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef struct HashTable HashTable;
typedef struct PtrHashTable PtrHashTable;

typedef void (*HashTableFreeEntryFn)(void *clientData);

typedef int (*HashTableForEachCallback)(const char *key,
                                        void *value,
                                        void *clientData);

#define HASH_STRING_KEY         0       // case-sensitive string key
#define HASH_ISTRING_KEY        1       // case-insensitive string key
#define HASH_INT_KEY            2       // uintptr_t or pointer key

/*
 * The flag bits are ored into the type field.
 * Atomic hash tables only support insert, lookup, and replace.
 */

#define HASH_TYPE_MASK          7
#define HASH_FLAG_MASK          (~HASH_TYPE_MASK)
#define HASH_FLAG_ATOMIC        0x08    // thread-safe hash table
#define HASH_FLAG_COPYKEY       0x10    // copy string key

HashTable *
HashTable_Alloc(uint32               numEntries,  // IN:
                int                  keyType,     // IN:
                HashTableFreeEntryFn fn);         // IN/OPT:

HashTable *
HashTable_AllocOnce(Atomic_Ptr          *var,         // IN/OUT:
                    uint32               numEntries,  // IN:
                    int                  keyType,     // IN:
                    HashTableFreeEntryFn fn);         // IN/OPT:

void
HashTable_Free(HashTable *hashTable);  // IN/OUT:

void
HashTable_FreeUnsafe(HashTable *hashTable);  // IN/OUT:

Bool
HashTable_Insert(HashTable  *hashTable,    // IN/OUT:
                 const void *keyStr,       // IN:
                 void       *clientData);  // IN/OPT:

Bool
HashTable_Lookup(const HashTable  *hashTable,    // IN:
                 const void       *keyStr,       // IN:
                 void            **clientData);  // OUT/OPT:

void *
HashTable_LookupOrInsert(HashTable  *hashTable,    // IN/OUT:
                         const void *keyStr,       // IN:
                         void       *clientData);  // IN/OPT:

Bool
HashTable_ReplaceOrInsert(HashTable  *hashTable,    // IN/OUT:
                          const void *keyStr,       // IN:
                          void       *clientData);  // IN/OPT

Bool
HashTable_ReplaceIfEqual(HashTable  *hashTable,       // IN/OUT:
                         const void *keyStr,          // IN:
                         void       *oldClientData,   // IN/OPT
                         void       *newClientData);  // IN/OPT

Bool
HashTable_Delete(HashTable  *hashTable,  // IN/OUT:
                 const void *keyStr);    // IN:

Bool
HashTable_LookupAndDelete(HashTable  *hashTable,    // IN/OUT:
                          const void *keyStr,       // IN:
                          void      **clientData);  // OUT:

void
HashTable_Clear(HashTable *ht);  // IN/OUT:

void
HashTable_ToArray(const HashTable   *ht,           // IN:
                  void            ***clientDatas,  // OUT:
                  size_t            *size);        // OUT:

void
HashTable_KeyArray(const HashTable   *ht,     // IN:
                   const void      ***keys,   // OUT:
                   size_t            *size);  // OUT:

size_t
HashTable_GetNumElements(const HashTable *ht);  // IN:

int
HashTable_ForEach(const HashTable          *ht,           // IN:
                  HashTableForEachCallback  cb,           // IN:
                  void                     *clientData);  // IN:

/*
 * Specialize hash table that uses the callers data structure as its
 * hash entry as well, the hash key being an address that must be unique.
 */

typedef struct PtrHashEntry {
   struct PtrHashEntry  *next;
   void                 *ptr;
} PtrHashEntry;

/*
 * PTRHASH_CONTAINER - get the struct for this entry (like PtrHashEntry)
 * @ptr: the &struct PtrHashEntry pointer.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list struct within the struct.
 */

#define PTRHASH_CONTAINER(ptr, type, member) \
   ((type *)((char *)(ptr) - offsetof(type, member)))

typedef int (*PtrHashForEachCallback)(PtrHashEntry *entry,
                                      const void *clientData);

PtrHashTable *PtrHash_Alloc(uint32 numBuckets);

void PtrHash_Free(PtrHashTable *hashTable);

size_t PtrHash_AllocSize(PtrHashTable *ht);

size_t PtrHash_GetNumElements(const PtrHashTable *ht);

int PtrHash_ForEach(const PtrHashTable *ht,
                    PtrHashForEachCallback cb,
                    const void *clientData);

PtrHashEntry *PtrHash_Lookup(const PtrHashTable *hashTable,
                             const void *keyPtr);

PtrHashEntry *PtrHash_LookupAndDelete(PtrHashTable *hashTable,
                                      const void *keyPtr);

Bool PtrHash_Insert(PtrHashTable *hashTable,
                    PtrHashEntry *entry);

Bool PtrHash_Delete(PtrHashTable *hashTable,
                    const void *keyPtr);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
  070701000000D3000081A40000000000000000000000016822550500002F96000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfs.h /*********************************************************
 * Copyright (C) 1998-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/



/*
 * 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"
#ifdef VMX86_TOOLS
#   include "rpcvmx.h"
#else
#   include "config.h"
#endif
#include "vm_assert.h"

/* Page size for HGFS packet (4K). */
#define HGFS_PAGE_SIZE 4096

/*
 * Maximum allowed header size in bytes.
 */
#define HGFS_HEADER_SIZE_MAX 2048

/*
 * 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 127

/* Maximum number of bytes to read or write to a hgfs server in a single packet. */
#define HGFS_IO_MAX HGFS_PAGE_SIZE

/*
 * Maximum allowed packet size in bytes. All hgfs code should be made
 * safe with respect to this limit.
 */
#define HGFS_PACKET_MAX 6144

/* Maximum number of bytes to read or write to a V3 server in a single hgfs packet. */
#define HGFS_LARGE_IO_MAX (HGFS_PAGE_SIZE * HGFS_LARGE_IO_MAX_PAGES)

/*
 * 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 (HGFS_LARGE_IO_MAX + HGFS_HEADER_SIZE_MAX)

/*
 * Legacy definitions for HGFS_LARGE_IO_MAX_PAGES, HGFS_LARGE_IO_MAX and
 * HGFS_LARGE_PACKET_MAX. They are used both in Windows client and hgFileCopy
 * library for performing vmrun CopyFileFromHostToGuest/GuestToHost.
 */
#define HGFS_LEGACY_LARGE_IO_MAX_PAGES 15
#define HGFS_LEGACY_LARGE_IO_MAX       (HGFS_PAGE_SIZE * HGFS_LEGACY_LARGE_IO_MAX_PAGES)
#define HGFS_LEGACY_LARGE_PACKET_MAX   (HGFS_LEGACY_LARGE_IO_MAX + HGFS_HEADER_SIZE_MAX)

static size_t gHgfsLargeIoMax = 0;
static size_t gHgfsLargePacketMax = 0;

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsLargeIoMax --
 *
 *    Gets the maximum number of bytes to read or write to a V3 server in a
 *    single hgfs packet.
 *    By default, a caller should pass useLegacy=FALSE to get its value with the
 *    control of feature switch. Passing useLegacy=TRUE means you want to
 *    directly use the legacy value.
 *
 * Results:
 *    Returns the maximum number of bytes to read or write to a V3 server in a
 *    single hgfs packet.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE size_t HgfsLargeIoMax(Bool useLegacy) // IN
{
   if (useLegacy) {
      return HGFS_LEGACY_LARGE_IO_MAX;
   }
   if (gHgfsLargeIoMax > 0) {
      return gHgfsLargeIoMax;
   }
#ifdef VMX86_TOOLS
   if (!RpcVMX_ConfigGetBool(TRUE, "hgfs.packetSize.large")) {
#else
   if (!Config_GetBool(TRUE, "hgfs.packetSize.large")) {
#endif
      gHgfsLargeIoMax = HGFS_LEGACY_LARGE_IO_MAX;
   } else {
      gHgfsLargeIoMax = HGFS_LARGE_IO_MAX;
   }
   return gHgfsLargeIoMax;
}

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsLargePacketMax --
 *
 *    Gets the maximum number of bytes to allow guests to make read / write
 *    requests.
 *    By default, a caller should pass useLegacy=FALSE to get its value with the
 *    control of feature switch. Passing useLegacy=TRUE means you want to
 *    directly use the legacy value.
 *
 * Results:
 *    Returns the maximum number of bytes to allow guests to make read / write
 *    requests.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE size_t HgfsLargePacketMax(Bool useLegacy) // IN
{
   if (useLegacy) {
      return HGFS_LEGACY_LARGE_PACKET_MAX;
   }
   if (gHgfsLargePacketMax > 0) {
      return gHgfsLargePacketMax;
   }
#ifdef VMX86_TOOLS
   if (!RpcVMX_ConfigGetBool(TRUE, "hgfs.packetSize.large")) {
#else
   if (!Config_GetBool(TRUE, "hgfs.packetSize.large")) {
#endif
      gHgfsLargePacketMax = HGFS_LEGACY_LARGE_PACKET_MAX;
   } else {
      gHgfsLargePacketMax = HGFS_LARGE_PACKET_MAX;
   }
   return gHgfsLargePacketMax;
}

/*
 * File type
 *
 * 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.
 *
 *
 * 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_FILE_TYPE_REGULAR,
   HGFS_FILE_TYPE_DIRECTORY,
   HGFS_FILE_TYPE_SYMLINK,
} HgfsFileType;



/*
 * 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,
   HGFS_STATUS_INVALID_PARAMETER,
   HGFS_STATUS_NOT_SAME_DEVICE,
   /*
    * Following error codes are for V4 and above protocol only.
    * Server must never retun these codes for legacy clients.
    */
   HGFS_STATUS_STALE_SESSION,
   HGFS_STATUS_TOO_MANY_SESSIONS,

   HGFS_STATUS_TRANSPORT_ERROR,
} 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_
  070701000000D4000081A4000000000000000000000001682255050000081B000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsBd.h   /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#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_
 070701000000D5000081A400000000000000000000000168225505000013EF000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsDevLinux.h /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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_FUSENAME "vmhgfs-fuse"     // Name of FS (e.g. "-o subtype=vmhgfs-fuse")
#define HGFS_FUSETYPE "fuse." HGFS_FUSENAME // Type of FS (e.g. "fuse.vmhgfs-fuse")
#define HGFS_MOUNT_POINT "/mnt/hgfs"    // Type of FS (e.g. vmhgfs-fuse )
#define HGFS_DEVICE_NAME "dev"          // Name of our device under /proc/fs/HGFS_NAME/
#define HGFS_SUPER_MAGIC 0xbacbacbc     // Superblock magic number
#define HGFS_DEFAULT_TTL 1              // Default TTL for dentries

typedef enum {
   HGFS_MOUNTINFO_VERSION_NONE,
   HGFS_MOUNTINFO_VERSION_1,
   HGFS_MOUNTINFO_VERSION_2,
} HgfsMountInfoVersion;

/*
 * The mount info flags.
 * These specify flags from options parsed on the mount command line.
 */
#define HGFS_MNTINFO_SERVER_INO         (1 << 0) /* Use server inode numbers? */

/*
 * 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 infoSize;           // HgfsMountInfo structure size
   HgfsMountInfoVersion version; // HgfsMountInfo structure version
   uint32 fd;                 // file descriptor of client file
   uint32 flags;              // hgfs specific mount flags
#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
#if defined __APPLE__
   char shareNameHost[MAXPATHLEN]; // must be ".host"
   char shareNameDir[MAXPATHLEN];  // desired share name for mounting
#else
   const char *shareNameHost; // must be ".host"
   const char *shareNameDir;  // desired share name for mounting
#endif
#endif
}
#if __GNUC__
__attribute__((__packed__))
#else
#   error Compiler packing...
#endif
HgfsMountInfo;

/*
 * Version 1 of the MountInfo object.
 * This is used so that newer kernel clients can allow mounts using
 * older versions of the mounter application for backwards compatibility.
 */
typedef
struct HgfsMountInfoV1 {
   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
#if defined __APPLE__
   char shareNameHost[MAXPATHLEN]; // must be ".host"
   char shareNameDir[MAXPATHLEN];  // desired share name for mounting
#else
   const char *shareNameHost; // must be ".host"
   const char *shareNameDir;  // desired share name for mounting
#endif
#endif
} HgfsMountInfoV1;

#endif //ifndef _HGFS_DEV_H_
 070701000000D6000081A40000000000000000000000016822550500000796000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsEscape.h   /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * hgfsEscape.h --
 *
 *    Escape and unescape filenames that are not legal on a particular
 *    platform.
 *
 */

#ifndef __HGFS_ESCAPE_H__
#define __HGFS_ESCAPE_H__

int HgfsEscape_GetSize(char const *bufIn,
                       uint32 sizeIn);
int HgfsEscape_Do(char const *bufIn,
                  uint32 sizeIn,
                  uint32 sizeBufOut,
                  char *bufOut);

uint32 HgfsEscape_Undo(char *bufIn,
                       uint32 sizeIn);

#endif // __HGFS_ESCAPE_H__
  070701000000D7000081A4000000000000000000000001682255050000052D000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsHelper.h   /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsClient.h
 *
 *    Provides a helper library for guest applications to access
 *    the HGFS file system.
 */

#ifndef _HGFS_HELPER_H_
#define _HGFS_HELPER_H_

#include "vm_basic_types.h"
#include "unicode.h"

Bool HgfsHlpr_QuerySharesDefaultRootPath(char **hgfsRootPath);
void HgfsHlpr_FreeSharesRootPath(char *hgfsRootPath);
#if defined(_WIN32)
Bool HgfsHlpr_ReadRegistryDefaultRootPath(char **hgfsRootPath);
#endif // _WIN32

#endif
   070701000000D8000081A40000000000000000000000016822550500015723000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsProto.h    /*********************************************************
 * Copyright (C) 1998-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * 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,        /* Read V3 directory entries */
   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_WRITE_WIN32_STREAM_V3, /* Write WIN32_STREAM_ID format data to file */
   /*
    * Operations for version 4, deprecating version 3 operations.
    */

   HGFS_OP_CREATE_SESSION_V4,     /* Create a session and return host capabilities. */
   HGFS_OP_DESTROY_SESSION_V4,    /* Destroy/close session. */
   HGFS_OP_READ_FAST_V4,          /* Read */
   HGFS_OP_WRITE_FAST_V4,         /* Write */
   HGFS_OP_SET_WATCH_V4,          /* Start monitoring directory changes. */
   HGFS_OP_REMOVE_WATCH_V4,       /* Stop monitoring directory changes. */
   HGFS_OP_NOTIFY_V4,             /* Notification for a directory change event. */
   HGFS_OP_SEARCH_READ_V4,        /* Read V4 directory entries. */
   HGFS_OP_OPEN_V4,               /* Open file */
   HGFS_OP_ENUMERATE_STREAMS_V4,  /* Enumerate alternative named streams for a file. */
   HGFS_OP_GETATTR_V4,            /* Get file attributes */
   HGFS_OP_SETATTR_V4,            /* Set file attributes */
   HGFS_OP_DELETE_V4,             /* Delete a file or a directory */
   HGFS_OP_LINKMOVE_V4,           /* Rename/move/create hard link. */
   HGFS_OP_FSCTL_V4,              /* Sending FS control requests. */
   HGFS_OP_ACCESS_CHECK_V4,       /* Access check. */
   HGFS_OP_FSYNC_V4,              /* Flush all cached data to the disk. */
   HGFS_OP_QUERY_VOLUME_INFO_V4,  /* Query volume information. */
   HGFS_OP_OPLOCK_ACQUIRE_V4,     /* Acquire OPLOCK. */
   HGFS_OP_OPLOCK_BREAK_V4,       /* Break or downgrade OPLOCK. */
   HGFS_OP_LOCK_BYTE_RANGE_V4,    /* Acquire byte range lock. */
   HGFS_OP_UNLOCK_BYTE_RANGE_V4,  /* Release byte range lock. */
   HGFS_OP_QUERY_EAS_V4,          /* Query extended attributes. */
   HGFS_OP_SET_EAS_V4,            /* Add or modify extended attributes. */

   HGFS_OP_MAX,                   /* Dummy op, must be last in enum */
   HGFS_OP_NEW_HEADER = 0xff,     /* Header op, must be unique, distinguishes packet headers. */
} HgfsOp;


/*
 * If we get to where the OP table has grown such that we hit the invalid opcode to
 * distinguish between header structures in the packet, then we must ensure that there
 * is no valid HGFS opcode with that same value.
 * The following assert is designed to force anyone who adds new opcodes which cause the
 * above condition to occur to verify the opcode values and then can remove this check.
 */
MY_ASSERTS(hgfsOpValuesAsserts,
   ASSERT_ON_COMPILE(HGFS_OP_MAX < HGFS_OP_NEW_HEADER);
)

/* HGFS protocol versions. */

typedef enum {
   HGFS_PROTOCOL_VERSION_NONE,
   HGFS_PROTOCOL_VERSION_1,
   HGFS_PROTOCOL_VERSION_2,
   HGFS_PROTOCOL_VERSION_3,
   HGFS_PROTOCOL_VERSION_4,
} HgfsProtocolVersion;

/* 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))

 /*
 * 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

/*
 * Access mode bits.
 *
 * Different operating systems have different set of file access mode.
 * Here are constants that are rich enough to describe all access modes in an OS
 * independent way.
 */

typedef uint32 HgfsAccessMode;
/*
 * Generic access rights control coarse grain access for the file.
 * A particular generic rigth can be expanded into different set of specific rights
 * on different OS.
 */

/*
 * HGFS_MODE_GENERIC_READ means ability to read file data and read various file
 * attributes and properties.
 */
#define HGFS_MODE_GENERIC_READ        (1 << 0)
/*
 * HGFS_MODE_GENERIC_WRITE means ability to write file data and updaate various file
 * attributes and properties.
 */
#define HGFS_MODE_GENERIC_WRITE       (1 << 1)
/*
 * HGFS_MODE_GENERIC_EXECUE means ability to execute file. For network redirectors
 * ability to execute usualy implies ability to read data; for local file systems
 * HGFS_MODE_GENERIC_EXECUTE does not imply ability to read data.
 */
#define HGFS_MODE_GENERIC_EXECUTE     (1 << 2)

/* Specific rights define fine grain access modes. */
#define HGFS_MODE_READ_DATA           (1 << 3)  // Ability to read file data
#define HGFS_MODE_WRITE_DATA          (1 << 4)  // Ability to writge file data
#define HGFS_MODE_APPEND_DATA         (1 << 5)  // Appending data to the end of file
#define HGFS_MODE_DELETE              (1 << 6)  // Ability to delete the file
#define HGFS_MODE_TRAVERSE_DIRECTORY  (1 << 7)  // Ability to access files in a directory
#define HGFS_MODE_LIST_DIRECTORY      (1 << 8)  // Ability to list file names
#define HGFS_MODE_ADD_SUBDIRECTORY    (1 << 9)  // Ability to create a new subdirectory
#define HGFS_MODE_ADD_FILE            (1 << 10) // Ability to create a new file
#define HGFS_MODE_DELETE_CHILD        (1 << 11) // Ability to delete file/subdirectory
#define HGFS_MODE_READ_ATTRIBUTES     (1 << 12) // Ability to read attributes
#define HGFS_MODE_WRITE_ATTRIBUTES    (1 << 13) // Ability to write attributes
#define HGFS_MODE_READ_EXTATTRIBUTES  (1 << 14) // Ability to read extended attributes
#define HGFS_MODE_WRITE_EXTATTRIBUTES (1 << 15) // Ability to write extended attributes
#define HGFS_MODE_READ_SECURITY       (1 << 16) // Ability to read permissions/ACLs/owner
#define HGFS_MODE_WRITE_SECURITY      (1 << 17) // Ability to change permissions/ACLs
#define HGFS_MODE_TAKE_OWNERSHIP      (1 << 18) // Ability to change file owner/group

/*
 * 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,
   HGFS_LOCK_BATCH,
   HGFS_LOCK_LEASE,
} HgfsLockType;


/*
 * 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.
 */

#pragma pack(push, 1)
typedef 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 */
} HgfsAttr;
#pragma pack(pop)


/* 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)
#define HGFS_ATTR_HIDDEN_FORCED (1 << 3)
#define HGFS_ATTR_REPARSE_POINT (1 << 4)

/* V4 additional definitions for hgfsAttrFlags. */
#define HGFS_ATTR_COMPRESSED            (1 << 5)
#define HGFS_ATTR_ENCRYPTED             (1 << 6)
#define HGFS_ATTR_OFFLINE               (1 << 7)
#define HGFS_ATTR_READONLY              (1 << 8)
#define HGFS_ATTR_SPARSE                (1 << 9)
#define HGFS_ATTR_TEMPORARY             (1 << 10)

#define HGFS_ATTR_SEQUENTIAL_ONLY       (1 << 11)

/*
 * 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)

/* V4 additional open mask flags. */
#define HGFS_OPEN_VALID_EA                      (1 << 12)
#define HGFS_OPEN_VALID_ACL                     (1 << 13)
#define HGFS_OPEN_VALID_STREAM_NAME             (1 << 14)

/*
 * 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)
#define HGFS_ATTR_VALID_VOLID             (1 << 15)
/*
 * Add our file and volume identifiers.
 * NOTE: On Windows hosts, the file identifier is not guaranteed to be valid
 *       particularly with FAT. A defrag operation could cause it to change.
 *       Therefore, to not confuse older clients, and non-Windows
 *       clients we have added a separate flag.
 *       The Windows client will check for both flags for the
 *       file ID, and return the information to the guest application.
 *       However, it will use the ID internally, when it has an open
 *       handle on the server.
 *       Non-Windows clients need the file ID to be always guaranteed,
 *       which is to say, that the ID remains constant over the course of the
 *       file's lifetime, and will use the HGFS_ATTR_VALID_FILEID flag
 *       only to determine if the ID is valid.
 */
#define HGFS_ATTR_VALID_NON_STATIC_FILEID (1 << 16)
/*
 * File permissions that are in effect for the user which runs HGFS server.
 * Client needs to know effective permissions in order to implement access(2).
 * Client can't derive it from group/owner/other permissions because of two resaons:
 * 1. It does not know user/group id of the user which runs HGFS server
 * 2. Effective permissions account for additional restrictions that may be imposed
 *    by host file system, for example by ACL.
 */
#define HGFS_ATTR_VALID_EFFECTIVE_PERMS   (1 << 17)
#define HGFS_ATTR_VALID_EXTEND_ATTR_SIZE  (1 << 18)
#define HGFS_ATTR_VALID_REPARSE_POINT     (1 << 19)
#define HGFS_ATTR_VALID_SHORT_NAME        (1 << 20)


/*
 * 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)
#define HGFS_CREATE_DIR_VALID_FILE_ATTR         (1 << 5)

/*
 *  Version 2 of HgfsAttr
 */

#pragma pack(push, 1)
typedef 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 */
   uint32 volumeId;              /* volume identifier, non-zero is valid. */
   uint32 effectivePerms;        /* Permissions in effect for the user on the host. */
   uint64 reserved2;             /* Reserved for future use */
} HgfsAttrV2;
#pragma pack(pop)


/*
 * 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 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/)
 */

#pragma pack(push, 1)
typedef struct HgfsFileName {
   uint32 length; /* Does NOT include terminating NUL */
   char name[1];
} HgfsFileName;
#pragma pack(pop)


/*
 * Windows hosts only: the server may return the DOS 8 dot 3 format
 * name as part of the directory entry.
 */
#pragma pack(push, 1)
typedef struct HgfsShortFileName {
   uint32 length;            /* Does NOT include terminating NUL */
   char name[12 * 4];        /* UTF8 max char size is 4 bytes. */
} HgfsShortFileName;
#pragma pack(pop)

/*
 * 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.
 */

#pragma pack(push, 1)
typedef struct HgfsFileNameV3 {
   uint32 length;           /* Does NOT include terminating NUL */
   uint32 flags;            /* Flags described below. */
   HgfsCaseType caseType;   /* Case-sensitivity type. */
   HgfsHandle fid;
   char name[1];
} HgfsFileNameV3;
#pragma pack(pop)


/*
 * 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.
 */

#pragma pack(push, 1)
typedef struct HgfsRequest {
   HgfsHandle id;        /* Opaque request ID used by the requestor */
   HgfsOp op;
} HgfsRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReply {
   HgfsHandle id;        /* Opaque request ID used by the requestor */
   HgfsStatus status;
} HgfsReply;
#pragma pack(pop)


/*
 * Messages for our file operations.
 */

/* Deprecated */

#pragma pack(push, 1)
typedef 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;
} HgfsRequestOpen;
#pragma pack(pop)


/* Version 2 of HgfsRequestOpen */

#pragma pack(push, 1)
typedef 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 */
   HgfsLockType desiredLock;     /* The type of lock desired by the client */
   uint64 reserved1;             /* Reserved for future use */
   uint64 reserved2;             /* Reserved for future use */
   HgfsFileName fileName;
} HgfsRequestOpenV2;
#pragma pack(pop)


/* Version 3 of HgfsRequestOpen */

#pragma pack(push, 1)
typedef 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 */
   HgfsLockType desiredLock;     /* The type of lock desired by the client */
   uint64 reserved1;             /* Reserved for future use */
   uint64 reserved2;             /* Reserved for future use */
   HgfsFileNameV3 fileName;
} HgfsRequestOpenV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplyOpen {
   HgfsReply header;
   HgfsHandle file;      /* Opaque file ID used by the server */
} HgfsReplyOpen;
#pragma pack(pop)


/* Version 2 of HgfsReplyOpen */

#pragma pack(push, 1)
typedef struct HgfsReplyOpenV2 {
   HgfsReply header;
   HgfsHandle file;                  /* Opaque file ID used by the server */
   HgfsLockType acquiredLock;        /* The type of lock acquired by the server */
} HgfsReplyOpenV2;
#pragma pack(pop)


/* Version 3 of HgfsReplyOpen */


/*
 * The HGFS open V3 can acquire locks and reserve disk space when requested.
 * However, current versions of the server don't implement the locking or allocation of
 * disk space on a create. These results flags indicate to the client if the server
 * implements handling those fields and so the clients can respond accordingly.
 */
typedef uint32 HgfsReplyOpenFlags;

#define HGFS_OPEN_REPLY_ALLOC_DISK_SPACE      (1 << 0)
#define HGFS_OPEN_REPLY_LOCKED_FILE           (1 << 1)

#pragma pack(push, 1)
typedef struct HgfsReplyOpenV3 {
   HgfsHandle file;                  /* Opaque file ID used by the server */
   HgfsLockType acquiredLock;        /* The type of lock acquired by the server */
   HgfsReplyOpenFlags flags;         /* Opened file flags */
   uint32 reserved;                  /* Reserved for future use */
} HgfsReplyOpenV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestRead {
   HgfsRequest header;
   HgfsHandle file;      /* Opaque file ID used by the server */
   uint64 offset;
   uint32 requiredSize;
} HgfsRequestRead;
#pragma pack(pop)

/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplyRead {
   HgfsReply header;
   uint32 actualSize;
   char payload[1];
} HgfsReplyRead;
#pragma pack(pop)


/*
 * Version 3 of HgfsRequestRead.
 * Server must support HGFS_LARGE_PACKET_MAX to implement this op.
 */

#pragma pack(push, 1)
typedef struct HgfsRequestReadV3 {
   HgfsHandle file;      /* Opaque file ID used by the server */
   uint64 offset;
   uint32 requiredSize;
   uint64 reserved;      /* Reserved for future use */
} HgfsRequestReadV3;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyReadV3 {
   uint32 actualSize;
   uint64 reserved;      /* Reserved for future use */
   char payload[1];
} HgfsReplyReadV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestWrite {
   HgfsRequest header;
   HgfsHandle file;      /* Opaque file ID used by the server */
   HgfsWriteFlags flags;
   uint64 offset;
   uint32 requiredSize;
   char payload[1];
} HgfsRequestWrite;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplyWrite {
   HgfsReply header;
   uint32 actualSize;
} HgfsReplyWrite;
#pragma pack(pop)

/*
 * Version 3 of HgfsRequestWrite.
 * Server must support HGFS_LARGE_PACKET_MAX to implement this op.
 */

#pragma pack(push, 1)
typedef 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];
} HgfsRequestWriteV3;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyWriteV3 {
   uint32 actualSize;
   uint64 reserved;      /* Reserved for future use */
} HgfsReplyWriteV3;
#pragma pack(pop)

/* Stream write flags */
typedef enum {
   HGFS_WIN32_STREAM_IGNORE_SECURITY = (1<<0),
} HgfsWin32StreamFlags;

/*
 * HgfsRequestWriteWin32Stream.
 * Server must support HGFS_LARGE_PACKET_MAX to implement this op.
 */

#pragma pack(push, 1)
typedef struct HgfsRequestWriteWin32StreamV3 {
   HgfsHandle file;      /* Opaque file ID used by the server */
   HgfsWin32StreamFlags flags;
   uint32 reserved1;
   uint32 requiredSize;
   uint64 reserved2;     /* Reserved for future use */
   char payload[1];
} HgfsRequestWriteWin32StreamV3;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyWriteWin32StreamV3 {
   uint32 actualSize;
   uint64 reserved;      /* Reserved for future use */
} HgfsReplyWriteWin32StreamV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestClose {
   HgfsRequest header;
   HgfsHandle file;      /* Opaque file ID used by the server */
} HgfsRequestClose;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplyClose {
   HgfsReply header;
} HgfsReplyClose;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsRequestCloseV3 {
   HgfsHandle file;      /* Opaque file ID used by the server */
   uint64 reserved;      /* Reserved for future use */
} HgfsRequestCloseV3;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyCloseV3 {
   uint64 reserved;
} HgfsReplyCloseV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestSearchOpen {
   HgfsRequest header;
   HgfsFileName dirName;
} HgfsRequestSearchOpen;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsRequestSearchOpenV3 {
   uint64 reserved;      /* Reserved for future use */
   HgfsFileNameV3 dirName;
} HgfsRequestSearchOpenV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplySearchOpen {
   HgfsReply header;
   HgfsHandle search;    /* Opaque search ID used by the server */
} HgfsReplySearchOpen;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplySearchOpenV3 {
   HgfsHandle search;    /* Opaque search ID used by the server */
   uint64 reserved;      /* Reserved for future use */
} HgfsReplySearchOpenV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestSearchRead {
   HgfsRequest header;
   HgfsHandle search;    /* Opaque search ID used by the server */
   uint32 offset;        /* The first result is offset 0 */
} HgfsRequestSearchRead;
#pragma pack(pop)


/* Version 2 of HgfsRequestSearchRead */

#pragma pack(push, 1)
typedef struct HgfsRequestSearchReadV2 {
   HgfsRequest header;
   HgfsHandle search;    /* Opaque search ID used by the server */
   uint32 offset;        /* The first result is offset 0 */
} HgfsRequestSearchReadV2;
#pragma pack(pop)

#pragma pack(push, 1)
typedef 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 */
} HgfsRequestSearchReadV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplySearchRead {
   HgfsReply header;
   HgfsAttr attr;
   HgfsFileName fileName;
   /* fileName.length = 0 means "no entry at this offset" */
} HgfsReplySearchRead;
#pragma pack(pop)


/* Version 2 of HgfsReplySearchRead */

#pragma pack(push, 1)
typedef 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;
} HgfsReplySearchReadV2;
#pragma pack(pop)


/* 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;

#pragma pack(push, 1)
typedef struct HgfsReplySearchReadV3 {
   uint64 count;         /* Number of directory entries. */
   uint64 reserved;      /* Reserved for future use. */
   char payload[1];      /* Directory entries. */
} HgfsReplySearchReadV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestSearchClose {
   HgfsRequest header;
   HgfsHandle search;    /* Opaque search ID used by the server */
} HgfsRequestSearchClose;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplySearchClose {
   HgfsReply header;
} HgfsReplySearchClose;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsRequestSearchCloseV3 {
   HgfsHandle search;    /* Opaque search ID used by the server */
   uint64 reserved;      /* Reserved for future use */
} HgfsRequestSearchCloseV3;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplySearchCloseV3 {
   uint64 reserved;      /* Reserved for future use */
} HgfsReplySearchCloseV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestGetattr {
   HgfsRequest header;
   HgfsFileName fileName;
} HgfsRequestGetattr;
#pragma pack(pop)


/* Version 2 of HgfsRequestGetattr */

#pragma pack(push, 1)
typedef 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. */
} HgfsRequestGetattrV2;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsRequestGetattrV3 {
   HgfsAttrHint hints;       /* Flags for file handle valid. */
   uint64 reserved;          /* Reserved for future use */
   HgfsFileNameV3 fileName;  /* Filename used when file handle invalid. */
} HgfsRequestGetattrV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplyGetattr {
   HgfsReply header;
   HgfsAttr attr;
} HgfsReplyGetattr;
#pragma pack(pop)


/* Version 2 of HgfsReplyGetattr */

#pragma pack(push, 1)
typedef 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;
} HgfsReplyGetattrV2;
#pragma pack(pop)


#pragma pack(push, 1)
typedef 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;
} HgfsReplyGetattrV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestSetattr {
   HgfsRequest header;
   HgfsAttrChanges update;  /* Which fields need to be updated */
   HgfsAttr attr;
   HgfsFileName fileName;
} HgfsRequestSetattr;
#pragma pack(pop)


/* Version 2 of HgfsRequestSetattr */

#pragma pack(push, 1)
typedef 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. */
} HgfsRequestSetattrV2;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsRequestSetattrV3 {
   HgfsAttrHint hints;
   HgfsAttrV2 attr;
   uint64 reserved;          /* Reserved for future use */
   HgfsFileNameV3 fileName;  /* Filename used when file handle invalid. */
} HgfsRequestSetattrV3;
#pragma pack(pop)

/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplySetattr {
   HgfsReply header;
} HgfsReplySetattr;
#pragma pack(pop)


/* Version 2 of HgfsReplySetattr */

#pragma pack(push, 1)
typedef struct HgfsReplySetattrV2 {
   HgfsReply header;
} HgfsReplySetattrV2;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplySetattrV3 {
   uint64 reserved;          /* Reserved for future use */
} HgfsReplySetattrV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestCreateDir {
   HgfsRequest header;
   HgfsPermissions permissions;
   HgfsFileName fileName;
} HgfsRequestCreateDir;
#pragma pack(pop)


/* Version 2 of HgfsRequestCreateDir */

#pragma pack(push, 1)
typedef struct HgfsRequestCreateDirV2 {
   HgfsRequest header;
   HgfsCreateDirValid mask;
   HgfsPermissions specialPerms;
   HgfsPermissions ownerPerms;
   HgfsPermissions groupPerms;
   HgfsPermissions otherPerms;
   HgfsFileName fileName;
} HgfsRequestCreateDirV2;
#pragma pack(pop)


/* Version 3 of HgfsRequestCreateDir */

#pragma pack(push, 1)
typedef struct HgfsRequestCreateDirV3 {
   HgfsCreateDirValid mask;
   HgfsPermissions specialPerms;
   HgfsPermissions ownerPerms;
   HgfsPermissions groupPerms;
   HgfsPermissions otherPerms;
   HgfsAttrFlags fileAttr;
   HgfsFileNameV3 fileName;
} HgfsRequestCreateDirV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplyCreateDir {
   HgfsReply header;
} HgfsReplyCreateDir;
#pragma pack(pop)


/* Version 2 of HgfsReplyCreateDir */

#pragma pack(push, 1)
typedef struct HgfsReplyCreateDirV2 {
   HgfsReply header;
} HgfsReplyCreateDirV2;
#pragma pack(pop)


/* Version 3 of HgfsReplyCreateDir */

#pragma pack(push, 1)
typedef struct HgfsReplyCreateDirV3 {
   uint64 reserved;              /* Reserved for future use */
} HgfsReplyCreateDirV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsRequestDelete {
   HgfsRequest header;
   HgfsFileName fileName;
} HgfsRequestDelete;
#pragma pack(pop)


/* Version 2 of HgfsRequestDelete */

#pragma pack(push, 1)
typedef 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 */
} HgfsRequestDeleteV2;
#pragma pack(pop)


/* Version 3 of HgfsRequestDelete */

#pragma pack(push, 1)
typedef struct HgfsRequestDeleteV3 {
   HgfsDeleteHint hints;
   uint64 reserved;              /* Reserved for future use */
   HgfsFileNameV3 fileName;      /* Name used if the file is HGFS_HANDLE_INVALID */
} HgfsRequestDeleteV3;
#pragma pack(pop)


/* Deprecated */

#pragma pack(push, 1)
typedef struct HgfsReplyDelete {
   HgfsReply header;
} HgfsReplyDelete;
#pragma pack(pop)

/* Version 2 of HgfsReplyDelete */

#pragma pack(push, 1)
typedef struct HgfsReplyDeleteV2 {
   HgfsReply header;
} HgfsReplyDeleteV2;
#pragma pack(pop)


/* Version 2 of HgfsReplyDelete */

#pragma pack(push, 1)
typedef struct HgfsReplyDeleteV3 {
   uint64 reserved;              /* Reserved for future use */
} HgfsReplyDeleteV3;
#pragma pack(pop)


/*
 * 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
 */

#pragma pack(push, 1)
typedef struct HgfsRequestRename {
   HgfsRequest header;
   HgfsFileName oldName;
   HgfsFileName newName;
} HgfsRequestRename;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyRename {
   HgfsReply header;
} HgfsReplyRename;
#pragma pack(pop)


#pragma pack(push, 1)
typedef 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;
} HgfsRequestRenameV2;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyRenameV2 {
   HgfsReply header;
} HgfsReplyRenameV2;
#pragma pack(pop)


/* HgfsRequestRename and HgfsReplyRename for v3. */

#pragma pack(push, 1)
typedef struct HgfsRequestRenameV3 {
   HgfsRenameHint hints;
   uint64 reserved;              /* Reserved for future use */
   HgfsFileNameV3 oldName;
   HgfsFileNameV3 newName;
} HgfsRequestRenameV3;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyRenameV3 {
   uint64 reserved;              /* Reserved for future use */
} HgfsReplyRenameV3;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsRequestQueryVolume {
   HgfsRequest header;
   HgfsFileName fileName;
} HgfsRequestQueryVolume;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyQueryVolume {
   HgfsReply header;
   uint64 freeBytes;
   uint64 totalBytes;
} HgfsReplyQueryVolume;
#pragma pack(pop)


/* HgfsRequestQueryVolume and HgfsReplyQueryVolume for v3. */

#pragma pack(push, 1)
typedef struct HgfsRequestQueryVolumeV3 {
   uint64 reserved;              /* Reserved for future use */
   HgfsFileNameV3 fileName;
} HgfsRequestQueryVolumeV3;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyQueryVolumeV3 {
   uint64 freeBytes;
   uint64 totalBytes;
   uint64 reserved;              /* Reserved for future use */
} HgfsReplyQueryVolumeV3;
#pragma pack(pop)



/* New operations for Version 2 */

#pragma pack(push, 1)
typedef struct HgfsRequestServerLockChange {
   HgfsRequest header;
   HgfsHandle file;
   HgfsLockType newServerLock;
} HgfsRequestServerLockChange;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplyServerLockChange {
   HgfsReply header;
   HgfsLockType serverLock;
} HgfsReplyServerLockChange;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsRequestSymlinkCreate {
   HgfsRequest header;
   HgfsFileName symlinkName;

   /* This filename is in "CPNameLite" format. See CPNameLite.c for details. */
   HgfsFileName targetName;
} HgfsRequestSymlinkCreate;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplySymlinkCreate {
   HgfsReply header;
} HgfsReplySymlinkCreate;
#pragma pack(pop)


/* HgfsRequestSymlinkCreate and HgfsReplySymlinkCreate for v3. */

#pragma pack(push, 1)
typedef struct HgfsRequestSymlinkCreateV3 {
   uint64 reserved;              /* Reserved for future use */
   HgfsFileNameV3 symlinkName;

   /* This filename is in "CPNameLite" format. See CPNameLite.c for details. */
   HgfsFileNameV3 targetName;
} HgfsRequestSymlinkCreateV3;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsReplySymlinkCreateV3 {
   uint64 reserved;              /* Reserved for future use */
} HgfsReplySymlinkCreateV3;
#pragma pack(pop)

/* HGFS protocol version 4 definitions. */
#define HGFS_HEADER_VERSION_1                 1
#define HGFS_HEADER_VERSION                   HGFS_HEADER_VERSION_1

/*
 * Flags to indicate the type of packet following the header and
 * the overall state of the operation.
 */

#define HGFS_PACKET_FLAG_REQUEST              (1 << 0)       // Request packet
#define HGFS_PACKET_FLAG_REPLY                (1 << 1)       // Reply packet
#define HGFS_PACKET_FLAG_INFO_EXTERROR        (1 << 2)       // Info has ext error
#define HGFS_PACKET_FLAG_VALID_FLAGS          (0x7)          // Mask for valid values

#pragma pack(push, 1)
typedef struct HgfsHeader {
   uint8 version;       /* Header version. */
   uint8 reserved1[3];  /* Reserved for future use. */
   HgfsOp dummy;        /* Needed to distinguish between older and newer header. */
   uint32 packetSize;   /* Size of the packet, including the header size. */
   uint32 headerSize;   /* Size of the Hgfs header. */
   uint32 requestId;    /* Request ID. */
   HgfsOp op;           /* Operation. */
   uint32 status;       /* Return value. */
   uint32 flags;        /* Flags. See above. */
   uint32 information;  /* Generic field, used e.g. for native error code. */
   uint64 sessionId;    /* Session ID. */
   uint64 reserved;     /* Reserved for future use. */
} HgfsHeader;
#pragma pack(pop)

typedef uint32 HgfsOpCapFlags;

/*
 * The operation capability flags.
 *
 * These flags apply to all operations and occupy the least significant
 * 16 bits of the HgfsOpCapFlags type.
 */

/*
 * HGFS_OP_CAPFLAG_NOT_SUPPORTED
 * If no flags are set then the capability is not supported by the host.
 */
#define HGFS_OP_CAPFLAG_NOT_SUPPORTED           0

/*
 * HGFS_OP_CAPFLAG_IS_SUPPORTED
 * Set for each request that is supported by a host or client.
 * To be set for an Hgfs session both host and client must have the capability.
 */
#define HGFS_OP_CAPFLAG_IS_SUPPORTED            (1 << 0)

/*
 * HGFS_OP_CAPFLAG_ASYNCHRONOUS
 * Set for each request that can be handled asynchronously by a host or client.
 * By default all operations are handled synchronously but if this flag is set
 * by a client and a host then the operation can be handled in an asynchronous manner too.
 */
#define HGFS_OP_CAPFLAG_ASYNCHRONOUS            (1 << 1)

/*
 * The operation specific capability flags.
 *
 * These flags apply only to the operation given by the name and occupy the
 * most significant 16 bits of the HgfsOpCapFlags type.
 */

/*
 * Following flags define which optional parameters for file open
 * requests are supported by the host.
 * HGFS_OP_CAPFLAG_OPENV4_EA - host is capable of setting EA when creating
 *                             a new file.
 * HGFS_OP_CAPFLAG_OPENV4_ACL - host is capable of setting ACLs when creating
 *                              a new file.
 * HGFS_OP_CAPFLAG_OPENV4_NAMED_STREAMS - opening/enumerating named streams
 *                                        is supported.
 * HGFS_OP_CAPFLAG_OPENV4_SHARED_ACCESS - host supports file sharing restrictions.
 * HGFS_OP_CAPFLAG_OPENV4_UNIX_PERMISSIONS - host stores POSIX permissions with
 *                                           file.
 * HGFS_OP_CAPFLAG_OPENV4_POSIX_DELETION - host supports POSIX file deletion semantics.
 */
#define HGFS_OP_CAPFLAG_OPENV4_EA                 (1 << 16)
#define HGFS_OP_CAPFLAG_OPENV4_ACL                (1 << 17)
#define HGFS_OP_CAPFLAG_OPENV4_NAMED_STREAMS      (1 << 18)
#define HGFS_OP_CAPFLAG_OPENV4_SHARED_ACCESS      (1 << 19)
#define HGFS_OP_CAPFLAG_OPENV4_UNIX_PERMISSIONS   (1 << 20)
#define HGFS_OP_CAPFLAG_OPENV4_POSIX_DELETION     (1 << 21)

/*
 *  There is a significant difference in byte range locking semantics between Windows
 *  and POSIX file systems. Windows implements mandatory locking which means that every
 *  read or write request that conflicts with byte range locks is rejected. POSIX has
 *  an advisory locking which means that locks are validated only when another lock is
 *  requested and are not enforced for read/write operations.
 *  Applications in guest OS may expect byte range locking semantics that matches guest
 *  OS which may be different from semantics that is natively supported by host OS. In
 *  this case either HGFS server or HGFS client should provide compensation for the host
 *  OS semantics to maintain application compatibility.
 *  Client must know if the server is capable to provide appropriate byte range locking
 *  semantics to perform some compensation on behalf of server when necessary.
 *
 *  Following flags define various capabilities of byte range lock implementation on
 *  the host.
 *
 *  HGFS_OP_CAPFLAG_BYTE_RANGE_LOCKS_64 means that server is capable of locking 64 bit
 *                                      length ranges.
 *  HGFS_OP_CAPFLAG_BYTE_RANGE_LOCKS_32 means that server is limited to 32-bit ranges.
 *  HGFS_OP_CAPFLAG_BYTE_RANGE_LOCKS_MANDATORY means that server is capable of enforcing
 *                                             read/write restrictions for locked ranges.
 *  HGFS_OP_CAPFLAG_BYTE_RANGE_LOCKS_ADVISORY means that server supports advisory locking;
 *                                            locks are validated only for other bytes
 *                                            range locking and are not enforced
 *                                            for read/write operations.
 */
#define HGFS_OP_CAPFLAG_BYTE_RANGE_LOCKS_64               (1 << 16)
#define HGFS_OP_CAPFLAG_BYTE_RANGE_LOCKS_32               (1 << 17)
#define HGFS_OP_CAPFLAG_BYTE_RANGE_LOCKS_MANDATORY        (1 << 18)
#define HGFS_OP_CAPFLAG_BYTE_RANGE_LOCKS_ADVISORY         (1 << 19)

/* HGFS_SUPPORTS_HARD_LINKS is set when the host supports hard links. */
#define HGFS_OP_CAPFLAG_LINKMOVE_HARD_LINKS               (1 << 16)

 /*
  * HGFS_SET_WATCH_SUPPORTS_FINE_GRAIN_EVENTS is set when host supports
  * fine grain event reporting for directory notification.
  */
#define HGFS_OP_CAPFLAG_SET_WATCH_FINE_GRAIN_EVENTS       (1 << 16)


#pragma pack(push, 1)
typedef struct HgfsOpCapability {
   HgfsOp op;                         /* Op. */
   HgfsOpCapFlags flags;              /* Flags. */
} HgfsOpCapability;
#pragma pack(pop)

typedef HgfsFileName HgfsUserName;
typedef HgfsFileName HgfsGroupName;

/* Following structures describe user identity on the host which runs HGFS service. */

#pragma pack(push, 1)
typedef struct HgfsIdentity {
   uint32 uid;                        /* user id. */
   uint32 gid;                        /* Primary group id. */
   HgfsUserName user;                 /* User name in form specified in RFC 3530. */
   HgfsGroupName group;               /* Group name in form specified in RFC 3530. */
} HgfsIdentity;
#pragma pack(pop)

#define HGFS_INVALID_SESSION_ID     (~((uint64)0))

/*
 * The HGFS session flags. These determine the state and validity of the session
 * information.
 * It is envisaged that flags will be set for notifying the clients of file system
 * feature support that transcend multiple request types i.e., HGFS opcodes.
 */
typedef uint32 HgfsSessionFlags;

#define HGFS_SESSION_MAXPACKETSIZE_VALID    (1 << 0)
#define HGFS_SESSION_CHANGENOTIFY_ENABLED   (1 << 1)
#define HGFS_SESSION_OPLOCK_ENABLED         (1 << 2)
#define HGFS_SESSION_ASYNC_IO_ENABLED       (1 << 3)

#pragma pack(push, 1)
typedef struct HgfsRequestCreateSessionV4 {
   uint32 numCapabilities;            /* Number of capabilities to follow. */
   uint32 maxPacketSize;              /* Maximum packet size supported. */
   HgfsSessionFlags flags;            /* Session capability flags. */
   uint32 reserved;                   /* Reserved for future use. */
   HgfsOpCapability capabilities[1];    /* Array of HgfsCapabilities. */
} HgfsRequestCreateSessionV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyCreateSessionV4 {
   uint64 sessionId;                  /* Session ID. */
   uint32 numCapabilities;            /* Number of capabilities to follow. */
   uint32 maxPacketSize;              /* Maximum packet size supported. */
   uint32 identityOffset;             /* Offset to HgfsIdentity or 0 if no identity. */
   HgfsSessionFlags flags;            /* Flags. */
   uint32 reserved;                   /* Reserved for future use. */
   HgfsOpCapability capabilities[1];    /* Array of HgfsCapabilities. */
} HgfsReplyCreateSessionV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsRequestDestroySessionV4 {
   uint64 reserved;    /* Reserved for future use. */
} HgfsRequestDestroySessionV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyDestroySessionV4 {
   uint64 reserved;    /* Reserved for future use. */
} HgfsReplyDestroySessionV4;
#pragma pack(pop)

/* Adds new error status: HGFS_STATUS_INVALID_SESSION. */

/*
 * If file handle is used to set watch (HGFS_FILE_NAME_USE_FILE_DESC
 * is set in the fileName), closing this handle implicitly removes the watch.
 */
#pragma pack(push, 1)
typedef struct HgfsRequestSetWatchV4 {
    uint64 events;             /* What events to watch? */
    uint32 flags;              /* Flags. */
    uint64 reserved;           /* Reserved for future use. */
    HgfsFileNameV3 fileName;   /* Filename to watch. */
} HgfsRequestSetWatchV4;
#pragma pack(pop)

/*
 *  Coarse grain notification event types.
 */
#define HGFS_ACTION_ADDED        (1 << 0)   /* File was added. */
#define HGFS_ACTION_REMOVED      (1 << 1)   /* File was removed. */
#define HGFS_ACTION_MODIFIED     (1 << 2)   /* File attributes were changed. */
#define HGFS_ACTION_RENAMED      (1 << 3)   /* File was renamed. */

/*
 * Fine grain notification event types.
 * HgfsRequestSetWatch events.
 */
#define HGFS_NOTIFY_ACCESS                   (1 << 0)    /* File accessed (read) */
#define HGFS_NOTIFY_ATTRIB                   (1 << 1)    /* File attributes changed. */
#define HGFS_NOTIFY_SIZE                     (1 << 2)    /* File size changed. */
#define HGFS_NOTIFY_ATIME                    (1 << 3)    /* Access time changed. */
#define HGFS_NOTIFY_MTIME                    (1 << 4)    /* Modification time changed. */
#define HGFS_NOTIFY_CTIME                    (1 << 5)    /* Attribute time changed. */
#define HGFS_NOTIFY_CRTIME                   (1 << 6)    /* Creation time changed. */
#define HGFS_NOTIFY_NAME                     (1 << 7)    /* File / Directory name. */
#define HGFS_NOTIFY_OPEN                     (1 << 8)    /* File opened */
#define HGFS_NOTIFY_CLOSE_WRITE              (1 << 9)    /* Modified file closed. */
#define HGFS_NOTIFY_CLOSE_NOWRITE            (1 << 10)   /* Non-modified file closed. */
#define HGFS_NOTIFY_CREATE_FILE              (1 << 11)   /* File created */
#define HGFS_NOTIFY_CREATE_DIR               (1 << 12)   /* Directory created */
#define HGFS_NOTIFY_DELETE_FILE              (1 << 13)   /* File deleted */
#define HGFS_NOTIFY_DELETE_DIR               (1 << 14)   /* Directory deleted */
#define HGFS_NOTIFY_DELETE_SELF              (1 << 15)   /* Watched directory deleted */
#define HGFS_NOTIFY_MODIFY                   (1 << 16)   /* File modified. */
#define HGFS_NOTIFY_MOVE_SELF                (1 << 17)   /* Watched directory moved. */
#define HGFS_NOTIFY_OLD_FILE_NAME            (1 << 18)   /* Rename: old file name. */
#define HGFS_NOTIFY_NEW_FILE_NAME            (1 << 19)   /* Rename: new file name. */
#define HGFS_NOTIFY_OLD_DIR_NAME             (1 << 20)   /* Rename: old dir name. */
#define HGFS_NOTIFY_NEW_DIR_NAME             (1 << 21)   /* Rename: new dir name. */
#define HGFS_NOTIFY_CHANGE_EA                (1 << 22)   /* Extended attributes. */
#define HGFS_NOTIFY_CHANGE_SECURITY          (1 << 23)   /* Security/permissions. */
#define HGFS_NOTIFY_ADD_STREAM               (1 << 24)   /* Named stream created. */
#define HGFS_NOTIFY_DELETE_STREAM            (1 << 25)   /* Named stream deleted. */
#define HGFS_NOTIFY_CHANGE_STREAM_SIZE       (1 << 26)   /* Named stream size changed. */
#define HGFS_NOTIFY_CHANGE_STREAM_LAST_WRITE (1 << 27)   /* Stream timestamp changed. */
#define HGFS_NOTIFY_WATCH_DELETED            (1 << 28)   /* Dir with watch deleted. */
#define HGFS_NOTIFY_EVENTS_DROPPED           (1 << 29)   /* Notifications dropped. */

/* HgfsRequestSetWatch flags. */
#define HGFS_NOTIFY_FLAG_WATCH_TREE  (1 << 0)    /* Watch the entire directory tree. */
#define HGFS_NOTIFY_FLAG_DONT_FOLLOW (1 << 1)    /* Don't follow symlinks. */
#define HGFS_NOTIFY_FLAG_ONE_SHOT    (1 << 2)    /* Generate only one notification. */
#define HGFS_NOTIFY_FLAG_POSIX_HINT  (1 << 3)    /* Client is POSIX and thus expects
                                                  * fine grain notification. Server
                                                  * may provide coarse grain
                                                  * notification even if this flag is
                                                  * set.
                                                  */

typedef uint64 HgfsSubscriberHandle;
#define HGFS_INVALID_SUBSCRIBER_HANDLE         ((HgfsSubscriberHandle)~((HgfsSubscriberHandle)0))

#pragma pack(push, 1)
typedef struct HgfsReplySetWatchV4 {
    HgfsSubscriberHandle watchId; /* Watch identifier for subsequent references. */
    uint64 reserved;              /* Reserved for future use. */
} HgfsReplySetWatchV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsRequestRemoveWatchV4 {
    HgfsSubscriberHandle watchId;  /* Watch identifier to remove. */
} HgfsRequestRemoveWatchV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyRemoveWatchV4 {
    uint64 reserved;       /* Reserved for future use. */
} HgfsReplyRemoveWatchV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsNotifyEventV4 {
   uint32 nextOffset;        /* Offset of next event; 0 if it i sthe last one. */
   uint64 mask;              /* Event occurred. */
   uint64 reserved;          /* Reserved for future use. */
   HgfsFileName fileName;    /* Filename. */
} HgfsNotifyEventV4;
#pragma pack(pop)

/* Too many events, some or all event were dropped by the server. */
#define HGFS_NOTIFY_FLAG_OVERFLOW          (1 << 0)
/* Watch had been removed either explicitly or implicitly. */
#define HGFS_NOTIFY_FLAG_REMOVED           (1 << 1)
/* Server generated coasrse grain events. */
#define HGFS_NOTIFY_FLAG_COARSE_GRAIN      (1 << 2)

#pragma pack(push, 1)
typedef struct HgfsRequestNotifyV4 {
   HgfsSubscriberHandle watchId; /* Watch identifier. */
   uint32 flags;                 /* Various flags. */
   uint32 count;                 /* Number of events occured. */
   uint64 reserved;              /* Reserved for future use. */
   HgfsNotifyEventV4 events[1];  /* Events. HgfsNotifyEvent(s). */
} HgfsRequestNotifyV4;
#pragma pack(pop)

// Query EA flags values.
#define HGFS_QUERY_EA_INDEX_SPECIFIED (1 << 0)
#define HGFS_QUERY_EA_SINGLE_ENTRY    (1 << 1)
#define HGFS_QUERY_EA_RESTART_SCAN    (1 << 2)

#pragma pack(push, 1)
typedef struct HgfsRequestQueryEAV4 {
   uint32 flags;                 /* EA flags. */
   uint32 index;
   uint64 reserved;              /* Reserved for future use. */
   uint32 eaNameLength;          /* EA name length. */
   uint32 eaNameOffset;          /* Offset of the eaName field. */
   HgfsFileNameV3 fileName;      /* File to watch. */
   char eaNames[1];              /* List of NULL terminated EA names.
                                  * Actual location of the data depends on
                                  * fileName length and defined by eaNameOffset.
                                  */
} HgfsRequestQueryEAV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyQueryEAV4 {
   uint32 nextOffset;            /* Offset of the next structure when more then
                                  * one record is returned.
                                  */
   uint32 flags;                 /* EA flags. */
   uint32 index;                 /* Index needed to resume scan. */
   uint64 reserved;              /* Reserved for future use. */
   uint32 eaDataLength;          /* EA value length. */
   char eaData[1];               /* NULL termianed EA name followed by EA value. */
} HgfsReplyQueryEAV4;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct HgfsEAV4 {
   uint32 nextOffset;      /* Offset of the next structure in the chain. */
   uint32 valueLength;     /* EA value length. */
   char data[1];           /* NULL terminated EA name followed by EA value. */
} HgfsEAV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsRequestSetEAV4 {
   uint32   flags;           /* Flags, see below. */
   uint64   reserved;        /* Reserved for future use. */
   uint32   numEAs;          /* Number of EAs in this request. */
   HgfsEAV4 attributes[1];   /* Array of attributes. */
} HgfsRequestSetEAV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplySetEAV4 {
   uint64 reserved;        /* Reserved for future use. */
} HgfsReplySetEAV4;
#pragma pack(pop)

/*
 * EA Flags. When both flags are set EA is either created or replaced if it exists.
 * HGFS_EA_FLAG_CREATE - create if EA is not present, error otherwise.
 * HGFS_EA_FLAG_REPLACE - Replace exisitng EA. Error if EA not already present.
 */
#define HGFS_EA_FLAG_CREATE      (1 << 0)
#define HGFS_EA_FLAG_REPLACE     (1 << 1)

/*
 * Byte range lock flag values:
 * HGFS_RANGE_LOCK_EXCLUSIVE - Requested lock is exclusive when this flag is set,
 *                             otherwise it is a shared lock.
 * HGFS_RANGE_LOCK_FAIL_IMMEDIATLY - If the flag is not set server waits until the
 *                                   lock becomes available.
 */
#define HGFS_RANGE_LOCK_EXCLUSIVE               (1 << 0)
#define HGFS_RANGE_LOCK_FAIL_IMMEDIATLY         (1 << 1)

#pragma pack(push, 1)
typedef struct HgfsRequestLockRangeV4 {
   HgfsHandle     fid;          /* File to take lock on. */
   uint32 flags;                /* Various flags. */
   uint64 start;                /* Starting offset in the file. */
   uint64 length;               /* Number of bytes to lock. */
   uint64 reserved;             /* Reserved for future use. */
} HgfsRequestLockRangeV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyLockRangeV4 {
   uint64 reserved;             /* Reserved for future use. */
} HgfsReplyLockRangeV4;
#pragma pack(pop)

#define HGFS_RANGE_LOCK_UNLOCK_ALL               (1 << 0)

#pragma pack(push, 1)
typedef struct HgfsRequestUnlockRangeV4 {
   HgfsHandle     fid;          /* File to take lock on. */
   uint32 flags;                /* Various flags. */
   uint64 start;                /* Starting offset in the file. */
   uint64 length;               /* Number of bytes to lock. */
   uint64 reserved;             /* Reserved for future use. */
} HgfsRequestUnlockRangeV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyUnlockRangeV4 {
   uint64 reserved;             /* Reserved for future use. */
} HgfsReplyUnlockRangeV4;
#pragma pack(pop)

/*
 * There are three types of oplocks: level 1, batch, and level 2. Both the level 1 and
 * batch oplocks are "exclusive access" opens. They are used slightly differently,
 * however, and hence have somewhat different semantics. A level 2 oplock is a "shared
 * access" grant on the file.
 * Level 1 is used by a remote client that wishes to modify the data. Once granted a
 * Level 1 oplock, the remote client may cache the data, modify the data in its cache
 * and need not write it back to the server immediately.
 * Batch oplocks are used by remote clients for accessing script files where the file is
 * opened, read or written, and then closed repeatedly. Thus, a batch oplock
 * corresponds not to a particular application opening the file, but rather to a remote
 * clients network file system caching the file because it knows something about the
 * semantics of the given file access. The name "batch" comes from the fact that this
 * behavior was observed by Microsoft with "batch files" being processed by command line
 * utilities. Log files especially exhibit this behavior when a script it being
 * processed each command is executed in turn. If the output of the script is redirected
 * to a log file the file fits the pattern described earlier, namely open/write/close.
 * With many lines in a file this pattern can be repeated hundreds of times.
 * Level 2 is used by a remote client that merely wishes to read the data. Once granted
 * a Level 2 oplock, the remote client may cache the data and need not worry that the
 * data on the remote file server will change without it being advised of that change.
 * An oplock must be broken whenever the cache consistency guarantee provided by the
 * oplock can no longer be provided. Thus, whenever a second network client attempts to
 * access data in the same file across the network, the file server is responsible for
 * "breaking" the oplocks and only then allowing the remote client to access the file.
 * This ensures that the data is guaranteed to be consistent and hence we have preserved
 * the consistency guarantees essential to proper operation.
 *
 * HGFS_OPLOCK_NONE: no oplock. No caching on client side.
 * HGFS_OPLOCK_SHARED: shared (or LEVEL II) oplock. Read caching is allowed.
 * HGFS_OPLOCK_EXCLUSIVE: exclusive (or LEVEL I) oplock. Read/write caching is allowed.
 * HGFS_OPLOCK_BATCH: batch oplock. Read/Write and Open caching is allowed.
 */

#pragma pack(push, 1)
typedef struct HgfsRequestServerLockChangeV2 {
   HgfsHandle fid;                    /* File to take lock on. */
   HgfsLockType serverLock;           /* Lock type. */
   uint64 reserved;
} HgfsRequestServerLockChangeV2;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyServerLockChangeV2 {
   HgfsLockType serverLock;            /* Lock granted. */
   uint64 reserved;
} HgfsReplyServerLockChangeV2;
#pragma pack(pop)

/*
 * This request is sent from server to the client to notify that oplock
 * is revoked or downgraded.
 */

#pragma pack(push, 1)
typedef struct HgfsRequestOplockBreakV4 {
   HgfsHandle fid;                    /* File handle. */
   HgfsLockType serverLock;           /* Lock downgraded to this type. */
   uint64 reserved;                   /* Reserved for future use. */
} HgfsRequestOplockBreakV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyOplockBreakV4 {
   HgfsHandle fid;                    /* File handle. */
   HgfsLockType serverLock;           /* Lock type. */
   uint64 reserved;                   /* Reserved for future use. */
} HgfsReplyOplockBreakV4;
#pragma pack(pop)

/*
 *  Flusing of a whole volume is not supported.
 *  Flusing of reqular files is supported on all hosts.
 *  Flusing of directories is supproted on POSIX hosts and is
 *  NOOP on Windows hosts.
 */
#pragma pack(push, 1)
typedef struct HgfsRequestFsyncV4 {
   HgfsHandle fid;      /* File to sync. */
   uint64 reserved;
} HgfsRequestFsyncV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyFsyncV4 {
   uint64 reserved;
} HgfsReplyFsyncV4;
#pragma pack(pop)

/*
 * This request is name based only.
 * Server fails this request if HGFS_FILE_E_USE_FILE_DESC is set in the fileName.
 */
#pragma pack(push, 1)
typedef struct HgfsRequestAccessCheckV4 {
   HgfsFileNameV3 fileName;     /* File concerned. */
   HgfsPermissions perms;       /* Permissions to check for. */
   uint64 reserved;             /* Reserved for future use. */
} HgfsRequestAccessCheckV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyAccessCheckV4 {
   uint64 reserved;             /* Reserved for future use. */
} HgfsReplyAccessCheckV4;
#pragma pack(pop)

/*
 * Additional HgfsPersmissions type: checks file existense without
 * requesting any particular access.
 * Matches F_OK mode parameter for POSIX access (2) API.
 */
#define HGFS_PERM_EXISTS  8

/*
 * HGFS_PLATFORM_ALL is a HGFS specific platform independent FSCTL
 * that correspond to different OS specific codes.
 * Other types of FSCTL are platform specific to allow better user
 * experience when guest and host OS are the same. HGFS does not interpret
 * platform specific FSCTL in any way, it just passes it through to the
 * host. If the host run appropriate OS it executes FSCTL on user's behalf,
 * otherwise it fails the request.
 */
typedef enum HgfsPlatformType {
   HGFS_PLATFORM_ALL,
   HGFS_PLATFORM_WINDOWS,
   HGFS_PLATFORM_LINUX,
   HGFS_PLATFORM_MAC
}HgfsPlatformType;

#define HGFS_FSCTL_SET_SPARSE 1 /* Platform independent FSCTL to make file sparse. */

/* Platform together with the code define exact meaning of the operation. */
#pragma pack(push, 1)
typedef struct HgfsRequestFsctlV4 {
   HgfsHandle fid;
   uint32 code;
   HgfsPlatformType platform;
   uint32 dataLength;
   char data[1];
} HgfsRequestFsctlV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyFsctlV4 {
   uint32 dataLength;
   char data[1];
} HgfsReplyFsctlV4;
#pragma pack(pop)

/*
 * Creating a new file or reading file attributes involves ACL. There is a good
 * definition of multi-platform ACLs in RFC 3530, section 5.11. HGFS should use
 * ACLs defined in this document (http://tools.ietf.org/html/rfc3530#section-5.11).
 * ACL support is not mandatory. If a request to create file with ACL comes to a host
 * that does not support ACL, the request should succeed and setting ACL is ignored.
 * Such behavior is consistent with other file systems.
 */
typedef uint64 HgfsOpenCreateOptions;

/* O_SYMLINK in Mac OS or FILE_FLAG_OPEN_REPARSE_POINT in Windows. */
#define HGFS_OPENCREATE_OPTION_SYMLINK            (1 << 0)
/* O_SHLOCK in Mac OS or obtain shared range lock for the whole file. */
#define HGFS_OPENCREATE_OPTION_SHLOCK             (1 << 1)
/* O_EXLOCK in Mac OS or obtain exclusive range lock for the whole file. */
#define HGFS_OPENCREATE_OPTION_EXLOCK             (1 << 2)
/* O_SYNC in Linux, ignored in Mac, FILE_FLAG_WRITE_THROUGH in Windows. */
#define HGFS_OPENCREATE_OPTION_WRITETHROUGH       (1 << 3)
/* FILE_FLAG_NO_BUFFERING in Windows, O_SYNC in Linux, ignored on Mac OS. */
#define HGFS_OPENCREATE_OPTION_NO_BUFERING        (1 << 4)
/*
 * O_NOFOLLOW in POSIX. Windows server checks for reparse point
 * and fails the request if file has one.
 */
#define HGFS_OPENCREATE_OPTION_NO_FOLLOW          (1 << 5)
/* FILE_FLAG_NO_RECALL in Windows. Ignored by POSIX host. */
#define HGFS_OPENCREATE_OPTION_NO_RECALL          (1 << 6)
/* FILE_FLAG_RANDOM_ACCESS in Windows. Ignored by POSIX host. */
#define HGFS_OPENCREATE_OPTION_RANDOM             (1 << 7)
/* FILE_FLAG_SEQUENTIAL_SCAN in Windows. Ignored by POSIX host. */
#define HGFS_OPENCREATE_OPTION_SEQUENTIAL         (1 << 8)
/* FILE_FLAG_BACKUP_SEMANTICS in Windows. Ignored by POSIX host. */
#define HGFS_OPENCREATE_OPTION_BACKUP_SEMANTICS   (1 << 9)
/* Fail opening if the file already exists and it is not a directory. */
#define HGFS_OPENCREATE_OPTION_DIRECTORY          (1 << 10)
/* Fail opening if the file already exists and it is a directory. */
#define HGFS_OPENCREATE_OPTION_NON_DIRECTORY      (1 << 11)

#pragma pack(push, 1)
typedef struct HgfsRequestOpenV4 {
   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 */
   HgfsOpenCreateOptions createOptions; /* Various options. */
   HgfsLockType requestedLock;   /* The type of lock desired by the client */
   HgfsFileNameV3 fileName;      /* fid can be used only for relative open,
                                  * i.e. to open named stream.
                                  */
   HgfsFileName streamName;      /* Name of the alternative named stream.
                                  * All flags are the same as defined in fileName.
                                  * The name is used in conjuction with fileName
                                  * field, for example if Windows opens file
                                  * "abc.txt:stream" then fileName contains
                                  * "abc.txt" and streamName contains "stream"
                                  */
   /*
    * EA to set if the file is created or overwritten. The parameter should be ignored
    * if the file already exists.
    * It is needed to correctly implement Windows semantics for opening files.
    * It should work atomically - failure to add EA should result in failure to create
    * the new file.
    * If the host file system does not support EA server should fail the request rather
    * then succeeding and silently dropping EA.
    */
   HgfsRequestSetEAV4 extendedAttributes;
   uint32 aclLength;               /* Length of the acl field. */
   char acl[1];                    /* Multi-platform ACL as defined in RFC 3530. */
} HgfsRequestOpenV4;
#pragma pack(pop)

typedef enum HgfsOpenResult {
   HGFS_FILE_OPENED,
   HGFS_FILE_CREATED,
   HGFS_FILE_OVERWRITTEN,
   HGFS_FILE_SUPERSIDED,
 } HgfsOpenResult;

/*
 * Win32 API has a special value for the desired access - MAXIMUM_ALLOWED.
 * Such desired access means that file system must grant as much rights for the file
 * as it is allowed for the current user.
 * HGFS client must know what access rights were granted to properly communicate this
 * information to the IoManager; grantedAccess field is used for this purpose.
 */
#pragma pack(push, 1)
typedef struct HgfsReplyOpenV4 {
   HgfsHandle file;                   /* Opaque file ID used by the server */
   HgfsLockType grantedLock;          /* The type of lock acquired by the server */
   HgfsOpenResult openResult;         /* Opened/overwritten or a new file created? */
   uint32 grantedAccess;              /* Granted access rights. */
   uint64 fileId;                     /* Persistent volume-wide unique file id. */
   uint64 volumeId;                   /* Persistent unique volume id. */
} HgfsReplyOpenV4;
#pragma pack(pop)

/*
 *  Flags that define behaviour of the move/creating hard link operation.
 */
typedef uint64 HgfsMoveLinkFlags;

#define HGFS_LINKMOVE_FLAG_REPLACE_EXISTING   (1 << 0)   /* Delete existing target. */
#define HGFS_LINKMOVE_FLAG_HARD_LINK          (1 << 1)   /* Create hard link. */

#pragma pack(push, 1)
typedef struct HgfsRequestLinkMoveV4 {
   HgfsFileNameV3 oldFileName;      /* Path to the exisitng source file.*/
   HgfsFileNameV3 newFileName;      /* Path to the destinatio name.*/
   HgfsMoveLinkFlags flags;         /* Flags that define behaviour of the operation.*/
} HgfsRequestLinkMoveV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyLinkMove4 {
   uint64 reserved;                 /* Reserved for future use. */
} HgfsReplyLinkMove4;
#pragma pack(pop)

/*
 * HgfsQueryVolumeMaskV4 mask in a request defines which volume properties client needs;
 * mask in a reply defines which properties were actually returned by the host.
 *
 * HGFS_QUERY_VOLUME_MASK_SIZE controls totalBytes, freeBytes and availableBytes.
 * HGFS_QUERY_VOLUME_MASK_FS_CAPABILITIES controls capabilities.
 * HGFS_QUERY_VOLUME_MASK_ATTRIBUTES controls creationTime.
 * HGFS_QUERY_VOLUME_MASK_VOLUME_GEOMETRY controls bytesPerSector and sectorPerCluster.
 * HGFS_QUERY_VOLUME_MASK_VOLUME_LABEL controls volume label.
 * HGFS_QUERY_VOLUME_MASK_FS_NAME controls fileSystemName.
 */
typedef uint64 HgfsQueryVolumeMaskV4;

#define HGFS_QUERY_VOLUME_MASK_SIZE             (1 << 0)
#define HGFS_QUERY_VOLUME_MASK_ATTRIBUTES       (1 << 1)
#define HGFS_QUERY_VOLUME_MASK_FS_CAPABILITIES  (1 << 2)
#define HGFS_QUERY_VOLUME_MASK_VOLUME_LABEL     (1 << 3)
#define HGFS_QUERY_VOLUME_MASK_VOLUME_GEOMETRY  (1 << 4)
#define HGFS_QUERY_VOLUME_MASK_FS_NAME          (1 << 5)

typedef uint64 HgfsFileSystemCapabilities;
#define HGFS_VOLUME_CASE_SENSITIVE           (1 << 0)
#define HGFS_VOLUME_SUPPORTS_EA              (1 << 1)
#define HGFS_VOLUME_SUPPORTS_COMPRESSION     (1 << 2)
#define HGFS_VOLUME_SUPPORTS_SHORT_NAMES     (1 << 3)
#define HGFS_VOLUME_SUPPORTS_ACL             (1 << 4)
#define HGFS_VOLUME_READ_ONLY                (1 << 5)
#define HGFS_VOLUME_SUPPORTS_ENCRYPTION      (1 << 6)
#define HGFS_VOLUME_SUPPORTS_OBJECT_ID       (1 << 7)
#define HGFS_VOLUME_SUPPORTS_REMOTE_STORAGE  (1 << 8)
#define HGFS_VOLUME_SUPPORTS_SYMLINKS        (1 << 9)
#define HGFS_VOLUME_SUPPORTS_SPARSE_FILES    (1 << 10)
#define HGFS_VOLUME_SUPPORTS_UNICODE         (1 << 11)
#define HGFS_VOLUME_SUPPORTS_QUOTA           (1 << 12)
#define HGFS_VOLUME_SUPPORTS_NAMED_STREAMS   (1 << 13)

#pragma pack(push, 1)
typedef struct HgfsRequestQueryVolumeV4 {
   HgfsQueryVolumeMaskV4 mask;
   HgfsFileNameV3 name;
} HgfsRequestQueryVolumeV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyQueryVolumeV4 {
   HgfsQueryVolumeMaskV4 mask; /* Identifies which values were set by the host. */
   uint64 totalBytes;          /* Total volume capacity. */
   uint64 freeBytes;           /* Free space on the volume. */
   uint64 availableBytes;      /* Free space available for the user. */
   HgfsFileSystemCapabilities capabilities; /* File system capabilities. */
   uint64 creationTime;        /* Volume creation time. */
   uint32 bytesPerSector;      /* Sector size for the volume. */
   uint32 sectorsPerCluster;   /* Cluster size for the volume. */
   HgfsFileName volumeLabel;   /* Volume name or label. */
   HgfsFileName fileSystemName;/* File system name. */
} HgfsReplyQueryVolumeV4;
#pragma pack(pop)

typedef uint32 HgfsSearchReadMask;
#define HGFS_SEARCH_READ_NAME                (1 << 0)
#define HGFS_SEARCH_READ_SHORT_NAME          (1 << 1)
#define HGFS_SEARCH_READ_FILE_SIZE           (1 << 2)
#define HGFS_SEARCH_READ_ALLOCATION_SIZE     (1 << 3)
#define HGFS_SEARCH_READ_EA_SIZE             (1 << 4)
#define HGFS_SEARCH_READ_TIME_STAMP          (1 << 5)
#define HGFS_SEARCH_READ_FILE_ATTRIBUTES     (1 << 6)
#define HGFS_SEARCH_READ_FILE_NODE_TYPE      (1 << 7)
#define HGFS_SEARCH_READ_REPARSE_TAG         (1 << 8)
#define HGFS_SEARCH_READ_FILE_ID             (1 << 9)

typedef uint32 HgfsSearchReadFlags;
#define HGFS_SEARCH_READ_INITIAL_QUERY       (1 << 1)
#define HGFS_SEARCH_READ_SINGLE_ENTRY        (1 << 2)
#define HGFS_SEARCH_READ_FID_OPEN_V4         (1 << 3)
#define HGFS_SEARCH_READ_REPLY_FINAL_ENTRY   (1 << 4)

/*
 * Read directory request can be used to enumerate files in a directory.
 * File handle used in the request can be either from HgfsRequestOpenV4 or
 * HgfsRequestSearchOpenV3.
 * searchPattern parameter allows filter out file names in the server for optimization.
 * It is optional - host may ignore patterns and return entries that do not match
 * the pattern. It is client responsibility to filter out names that do not match
 * the pattern.
 *
 * The mask field in request allows client to specify which properties it is
 * interested in. It allows to implement optimization in the server by skipping
 * parameters which client does not need.
 *
 * The HGFS Server fills mask field in the reply buffer to specify which
 * of the requested properties it supports, which may be a subset of the
 * requested properties.
 */

#pragma pack(push, 1)
typedef struct HgfsRequestSearchReadV4 {
   HgfsSearchReadMask mask;
   HgfsSearchReadFlags flags;
   HgfsHandle fid;
   uint32 replyDirEntryMaxSize;
   uint32 restartIndex;
   uint64 reserved;
   HgfsFileName searchPattern;
} HgfsRequestSearchReadV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsDirEntryV4 {
   uint32 nextEntryOffset;
   uint32 fileIndex;
   HgfsSearchReadMask mask;      /* Returned mask: may be a subset of requested mask. */
   HgfsAttrFlags attrFlags;      /* File system attributes of the entry */
   HgfsFileType fileType;
   uint64 fileSize;
   uint64 allocationSize;
   uint64 creationTime;
   uint64 accessTime;
   uint64 writeTime;
   uint64 attrChangeTime;
   uint64 hostFileId;            /* File Id of the file on host: inode_t on Linux */
   uint32 eaSize;                /* Byte size of any extended attributes. */
   uint32 reparseTag;            /* Windows only: reparse point tag. */
   uint64 reserved;              /* Reserved for future use. */
   HgfsShortFileName shortName;  /* Windows only: 8 dot 3 format name. */
   HgfsFileName fileName;        /* Entry file name. */
} HgfsDirEntryV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplySearchReadV4 {
   uint32 numberEntriesReturned; /* number of directory entries in this reply. */
   uint32 offsetToContinue;      /* Entry index of the directory entry. */
   HgfsSearchReadFlags flags;    /* Flags to indicate reply specifics */
   uint64 reserved;              /* Reserved for future use. */
   HgfsDirEntryV4 entries[1];    /* Unused as entries transfered using shared memory. */
} HgfsReplySearchReadV4;
#pragma pack(pop)

/*
 * File handle returned by HgfsRequestOpenV4 or later. Descriptors returned by
 * HgfsHandle fid; earlier versions of HgfsRequestOpen are not supported.
 */
#pragma pack(push, 1)
typedef struct HgfsRequestEnumerateStreamsV4 {
   uint32 restartIndex;
} HgfsRequestEnumerateStreamsV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsRequestStreamEntryV4 {
   uint32 nextEntryOffset;
   uint32 fileIndex;
   HgfsFileName fileName;
} HgfsRequestStreamEntryV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyEnumerateStreamsV4 {
   uint32 numberEntriesReturned;
   uint32 offsetToContinue;
   uint64 reserved;
   HgfsRequestStreamEntryV4 entries[1];
} HgfsReplyEnumerateStreamsV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsRequestGetattrV4 {
   uint32 mask;
   uint32 flags;
   uint64 reserved;
   HgfsFileNameV3 name;
} HgfsRequestGetattrV4;
#pragma pack(pop)

/*
 * V4 reports different file size for symlinks then V3 or V2.
 * It does not return file name length as EOF - it reports actual EOF.
 * On POSIX the value is always 0 and on Windows it is an actual EOF of
 * a file with a reparse point.
 * Each client must adjust the value for file size according to guest OS rules.
 *
 * Mask in HgfsAttr2V2 should be extended to include short name, symlink target and ACL.
 * If the host does not support a requested feature it is free to clear the
 * correspondent bit in the mask and ignore the feature.
 *
 * Multi-platform notice: symbolic link is represented by a file with REPARSE_POINT
 * on Windows. Thus Windows supports swtiching a file type between
 * regular or directory => symlink and back.
 * Setting symlinkTarget attribute on Windows host results in assigning
 * reparse point to the host file.
 */

#pragma pack(push, 1)
typedef struct HgfsAttrV4 {
   HgfsAttrV2 attr;
   uint32 numberOfLinks;
   HgfsFileName shortName;
   HgfsFileName symlinkTarget;
   uint32 aclLength;
   uint64 reserved;
   char acl[1];
} HgfsAttrV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyGetattrV4 {
   HgfsAttrV4 attr;
} HgfsReplyGetattrV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsRequestSetattrV4 {
   HgfsAttrHint hints;
   HgfsAttrV2 attr;
   uint64 reserved;          /* Reserved for future use */
   HgfsFileNameV3 fileName;  /* Filename used when file handle invalid. */
} HgfsRequestSetattrV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplySetattrV4 {
   uint32 mask;                      /* Defines which attributes were set. */
} HgfsReplySetattrV4;
#pragma pack(pop)

/*
 * Unlike V3 deletion this command can be used to delete both files and directories.
 * Its semantics depends on whether fid or file path is specified in the fileName.
 * When path is used it implements/emulates POSIX semantics - name is deleted from
 * the directory however if the file is opened it is still accessible. When fid is used
 * the file name disappears from the folder only when the last handle for the file is
 * closed - Windows style deletion.
 */

#pragma pack(push, 1)
typedef struct HgfsRequestDeleteFileV4 {
   HgfsFileNameV3 fileName;
} HgfsRequestDeleteFileV4;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct HgfsReplyDeleteFileV4 {
   uint64 reserved;
} HgfsReplyDeleteFileV4;
#pragma pack(pop)

#endif /* _HGFS_PROTO_H_ */
 070701000000D9000081A40000000000000000000000016822550500001E29000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsServer.h   /*********************************************************
 * Copyright (C) 1998-2020,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _HGFS_SERVER_H_
#define _HGFS_SERVER_H_

#include "dbllnklst.h"
#include "hgfs.h"             /* for HGFS_PACKET_MAX */
#include "vm_basic_defs.h"    /* for vmx86_debug */

#if defined(__cplusplus)
extern "C" {
#endif

#define HGFS_VMX_IOV_CONTEXT_SIZE (vmx86_debug ? 112 : 96)
typedef struct HgfsVmxIov {
   void *va;           /* Virtual addr */
   uint64 pa;          /* Physical address passed by the guest */
   uint32 len;         /* length of data; should be <= PAGE_SIZE for VMCI; arbitrary for backdoor */
   union {
      void *ptr;
      char clientStorage[HGFS_VMX_IOV_CONTEXT_SIZE];
   } context;         /* Mapping context */
} HgfsVmxIov;

typedef enum {
   BUF_READABLE,      /* Establish readable mappings */
   BUF_WRITEABLE,     /* Establish writeable mappings */
   BUF_READWRITEABLE, /* Establish read-writeable mappings */
} MappingType;

typedef uint64 HgfsStateFlags;
#define HGFS_STATE_CLIENT_REQUEST         (1 << 0)
#define HGFS_STATE_ASYNC_REQUEST          (1 << 1)
typedef struct HgfsPacket {
   uint64 id;

   HgfsStateFlags state;

   /* For metapacket we always establish writeable mappings */
   void *metaPacket;
   size_t metaPacketSize;
   uint32 metaPacketMappedIov;
   size_t metaPacketDataSize;
   Bool metaPacketIsAllocated;
   MappingType metaMappingType;

   void *dataPacket;
   size_t dataPacketSize;
   uint32 dataPacketMappedIov;
   size_t dataPacketDataSize;
   uint32 dataPacketIovIndex;
   Bool dataPacketIsAllocated;
   /* What type of mapping was established - readable/ writeable ? */
   MappingType dataMappingType;

   void *replyPacket;
   size_t replyPacketSize;
   size_t replyPacketDataSize;
   Bool replyPacketIsAllocated;

   /* Iov for the packet private to the channel. */
   HgfsVmxIov channelIov[2];

   uint32 iovCount;
   HgfsVmxIov iov[1];

} HgfsPacket;


/*
 * Function used for sending replies to the client for a session.
 * Passed by the caller at session connect time.
 */


/*
 * Send flags.
 *
 * Contains a bitwise OR of a combination of the following flags:
 * HGFS_SEND_CAN_DELAY - directs the channel to try and optimize
 * otherwise it will send the data immediately.
 * HGFS_SEND_NO_COMPLETE - directs the channel to not call the
 * send complete callback. Caller does not call completion notification
 * callback, for example to free buffers.
 */

typedef uint32 HgfsSendFlags;

#define HGFS_SEND_CAN_DELAY         (1 << 0)
#define HGFS_SEND_NO_COMPLETE       (1 << 1)

// Channel capability flags
typedef uint32 HgfsChannelFlags;
#define HGFS_CHANNEL_SHARED_MEM     (1 << 0)
#define HGFS_CHANNEL_ASYNC          (1 << 1)

typedef struct HgfsServerChannelData {
   HgfsChannelFlags flags;
   uint32 maxPacketSize;
}HgfsServerChannelData;


/* Default maximum number of open nodes. */
#define HGFS_MAX_CACHED_FILENODES   30

typedef uint32 HgfsConfigFlags;
#define HGFS_CONFIG_USE_HOST_TIME                    (1 << 0)
#define HGFS_CONFIG_NOTIFY_ENABLED                   (1 << 1)
#define HGFS_CONFIG_VOL_INFO_MIN                     (1 << 2)
#define HGFS_CONFIG_OPLOCK_ENABLED                   (1 << 3)
#define HGFS_CONFIG_SHARE_ALL_HOST_DRIVES_ENABLED    (1 << 4)
#define HGFS_CONFIG_THREADPOOL_ENABLED               (1 << 5)
#define HGFS_CONFIG_OPLOCK_MONITOR_ENABLED           (1 << 6)

typedef struct HgfsServerConfig {
   HgfsConfigFlags flags;
   uint32 maxCachedOpenNodes;
}HgfsServerConfig;

/*
 * Function used to notify HGFS server that a shared folder has been created or updated.
 * It allows HGFS server to maintain up-to-date list of shared folders and its
 * properties.
 */
typedef uint32 HgfsSharedFolderHandle;
#define HGFS_INVALID_FOLDER_HANDLE         ((HgfsSharedFolderHandle)~((HgfsSharedFolderHandle)0))

/*
 * Callback functions to enumerate the share resources.
 * Filled in by the HGFS server policy and passed in to the HGFS server
 * so that it can call out to them to enumerate the shares.
 */
typedef void * (*HgfsServerResEnumInitFunc)(void);
typedef Bool (*HgfsServerResEnumGetFunc)(void *data,
                                         char const **name,
                                         size_t *len,
                                         Bool *done);
typedef Bool (*HgfsServerResEnumExitFunc)(void *);

typedef struct HgfsServerResEnumCallbacks {
   HgfsServerResEnumInitFunc init;
   HgfsServerResEnumGetFunc get;
   HgfsServerResEnumExitFunc exit;
} HgfsServerResEnumCallbacks;


/*
 * Server Manager callback functions to enumerate the share resources and state logging.
 * Passed to the HGFS server on initialization.
 */
typedef struct HgfsServerMgrCallbacks {
   HgfsServerResEnumCallbacks enumResources;
} HgfsServerMgrCallbacks;

typedef enum {
   HGFS_QUIESCE_CHANNEL_FREEZE,  /*Op sent before the channel device quiesce*/
   HGFS_QUIESCE_FREEZE,
   HGFS_QUIESCE_THAW,
} HgfsQuiesceOp;

/*
 * Function used for invalidating nodes and searches that fall outside of a
 * share when the list of shares changes.
 */
typedef void (*HgfsInvalidateObjectsFunc)(DblLnkLst_Links *shares);

typedef Bool (*HgfsChannelSendFunc)(void *opaqueSession,
                                    HgfsPacket *packet,
                                    HgfsSendFlags flags);
typedef void * (*HgfsChannelMapVirtAddrFunc)(HgfsVmxIov *iov);
typedef void (*HgfsChannelUnmapVirtAddrFunc)(void *context);
typedef void (*HgfsChannelRegisterThreadFunc)(void);
typedef void (*HgfsChannelUnregisterThreadFunc)(void);

typedef struct HgfsServerChannelCallbacks {
   HgfsChannelMapVirtAddrFunc getReadVa;
   HgfsChannelMapVirtAddrFunc getWriteVa;
   HgfsChannelUnmapVirtAddrFunc putVa;
   HgfsChannelSendFunc send;
}HgfsServerChannelCallbacks;

typedef struct HgfsServerSessionCallbacks {
   Bool (*connect)(void *, HgfsServerChannelCallbacks *, HgfsServerChannelData *,void **);
   void (*disconnect)(void *);
   void (*close)(void *);
   void (*receive)(HgfsPacket *packet, void *);
   void (*invalidateObjects)(void *, DblLnkLst_Links *);
   uint32 (*invalidateInactiveSessions)(void *);
   void (*sendComplete)(HgfsPacket *, void *);
   void (*quiesce)(void *, HgfsQuiesceOp);
} HgfsServerSessionCallbacks;

/* XXX: TODO delete this layer if no other non-session callbacks are required. */
typedef struct HgfsServerCallbacks {
   HgfsServerSessionCallbacks session;
} HgfsServerCallbacks;

Bool HgfsServer_InitState(const HgfsServerCallbacks **,
                          HgfsServerConfig *,
                          HgfsServerMgrCallbacks *);
void HgfsServer_ExitState(void);

Bool HgfsServer_ShareAccessCheck(HgfsOpenMode accessMode,
                                 Bool shareWriteable,
                                 Bool shareReadable);

uint32 HgfsServer_GetHandleCounter(void);
void HgfsServer_SetHandleCounter(uint32 newHandleCounter);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _HGFS_SERVER_H_
   070701000000DA000081A40000000000000000000000016822550500000A93000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsServerManager.h    /*********************************************************
 * Copyright (C) 2006-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _HGFS_SERVER_MANAGER_H_
# define _HGFS_SERVER_MANAGER_H_

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * hgfsServerManager.h --
 *
 *    Common routines needed to register an HGFS server.
 */


#ifndef VMX86_TOOLS
#include "device_shared.h" // For DeviceLock and functions

Bool Hgfs_PowerOn(void);

void HgfsServerManager_GetDeviceLock(DeviceLock **lock);
Bool HgfsServerManager_ChangeState(Bool enable);

#else  /* VMX86_TOOLS */
//#include "hgfsServer.h" // For HgfsReceiveFlags

typedef struct HgfsServerMgrData {
   const char  *appName;         // Application name to register
   void        *rpc;             // RpcChannel unused
   void        *rpcCallback;     // RpcChannelCallback unused
   void        *connection;      // Connection object returned on success
} HgfsServerMgrData;


#define HgfsServerManager_DataInit(mgr, _name, _rpc, _rpcCallback) \
   do {                                                            \
      (mgr)->appName       = (_name);                              \
      (mgr)->rpc           = (_rpc);                               \
      (mgr)->rpcCallback   = (_rpcCallback);                       \
      (mgr)->connection    = NULL;                                 \
   } while (0)

Bool HgfsServerManager_Register(HgfsServerMgrData *data);
void HgfsServerManager_Unregister(HgfsServerMgrData *data);
Bool HgfsServerManager_ProcessPacket(HgfsServerMgrData *mgrData,
                                     char const *packetIn,
                                     size_t packetInSize,
                                     char *packetOut,
                                     size_t *packetOutSize);
uint32 HgfsServerManager_InvalidateInactiveSessions(HgfsServerMgrData *mgrData);
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _HGFS_SERVER_MANAGER_H_
 070701000000DB000081A40000000000000000000000016822550500001403000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsServerPolicy.h /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _HGFS_SERVER_POLICY_H_
#define _HGFS_SERVER_POLICY_H_

#include "vm_basic_types.h"
#include "hgfs.h"
#include "dbllnklst.h"
#include "cpName.h"
#include "hgfsServer.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Name of share that corresponds to the root of the server's
 * filesystem.
 */
#define HGFS_SERVER_POLICY_ROOT_SHARE_NAME "root"


typedef uint32 HgfsShareOptions;

/*
 * Structure representing one shared folder. We maintain a list of
 * these to check accesses against.
 */
typedef struct HgfsSharedFolder {
   DblLnkLst_Links links;
   const char *name;     /* Name of share */
   const char *path;     /*
                          * Path of share in server's filesystem. Should
                          * not include final path separator.
                          */
   const char *shareTags;/* Tags associated with this share (comma delimited). */
   size_t shareTagsLen;  /* Length of shareTag string */
   size_t nameLen;       /* Length of name string */
   size_t pathLen;       /* Length of path string */
   Bool readAccess;      /* Read permission for this share */
   Bool writeAccess;     /* Write permission for this share */
   HgfsShareOptions configOptions; /* User-config options. */
   HgfsSharedFolderHandle handle;  /* Handle assigned by HGFS server
                                    * when the folder was registered with it.
                                    * Policy package keeps the context and returns
                                    * it along with other shared folder properties.
                                    * Keeping it here ensures consistent lookup all
                                    * properties of the shared folder which takes into
                                    * account such details like case sensitive/case
                                    * insensitive name lookup.
                                    */
} HgfsSharedFolder;

/* Per share user configurable options. */
#define HGFS_SHARE_HOST_DEFAULT_CASE  (1 << 0)
#define HGFS_SHARE_FOLLOW_SYMLINKS    (1 << 1)

typedef struct HgfsServerPolicy_ShareList {
   size_t count;
   char **shareNames;
} HgfsServerPolicy_ShareList;

Bool
HgfsServerPolicy_Init(HgfsInvalidateObjectsFunc invalidateObjects,
                      HgfsServerResEnumCallbacks *enumResources);

Bool
HgfsServerPolicy_Cleanup(void);

HgfsNameStatus
HgfsServerPolicy_GetSharePath(char const *nameIn,         // IN:
                              size_t nameInLen,           // IN:
                              HgfsOpenMode mode,          // IN:
                              size_t *sharePathLen,       // OUT:
                              char const **sharePath);    // OUT:
HgfsNameStatus
HgfsServerPolicy_GetShareMode(char const *nameIn,        // IN: Share name to retrieve
                              size_t nameInLen,          // IN: Length of Share name
                              HgfsOpenMode *mode);       // OUT: Share's access mode
HgfsNameStatus
HgfsServerPolicy_GetShareOptions(char const *nameIn,        // IN: Share name
                          size_t nameInLen,                 // IN: Share name length
                          HgfsShareOptions *configOptions); // OUT: Share config options

Bool
HgfsServerPolicy_IsShareOptionSet(HgfsShareOptions shareOptions,  // IN: Config options
                                  uint32 option);                 // IN: Option to check
HgfsNameStatus
HgfsServerPolicy_ProcessCPName(char const *nameIn,            // IN: name in CPName form
                               size_t nameInLen,              // IN: length of the name
                               Bool *readAccess,              // OUT: Read permissions
                               Bool *writeAccess,             // OUT: Write permissions
                               HgfsSharedFolderHandle *handle,// OUT: folder handle
                               char const **shareBaseDir);    // OUT: Shared directory

void
HgfsServerPolicy_FreeShareList(HgfsServerPolicy_ShareList *shareList); // IN: list to free

HgfsServerPolicy_ShareList *
HgfsServerPolicy_GetSharesWithTag(const char *tag); // IN: tag to search for

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _HGFS_SERVER_POLICY_H_
 070701000000DC000081A4000000000000000000000001682255050000055A000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsUri.h  /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hgfsUri.h
 *
 *    Provides a library for guest applications to convert local pathames to
 *    x-vmware-share:// style URIs
 */

#ifndef _HGFS_URI_H_
#define _HGFS_URI_H_

#include "vm_basic_types.h"
#include "unicode.h"

#if defined(_WIN32)
char *HgfsUri_ConvertFromUtf16ToHgfsUri(wchar_t *pathNameUtf16, Bool hgfsOnly);
#endif // _WIN32
#if defined __linux__ || defined __APPLE__
char *HgfsUri_ConvertFromPathToHgfsUri(const char *pathName, Bool hgfsOnly);
#endif // POSIX

#endif
  070701000000DD000081A4000000000000000000000001682255050000194C000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsUtil.h /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * 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 <linux/time.h> // 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 <sys/time.h>
#   else
#      include <time.h>
#   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 <linux/errno.h>
#      elif defined sun || defined __FreeBSD__ || defined __APPLE__
#         include <sys/errno.h>
#      endif
#   else
#      include <errno.h>
#   endif
    typedef int HgfsInternalStatus;
    /*
     * There is no internal error in Linux.
     * Define a const that is converted to HGFS_INTERNAL_STATUS_ERROR.
     */
#   define EINTERNAL                    1001
#else
#   include <windows.h>
    typedef DWORD HgfsInternalStatus;
#endif

#if defined _WIN32
#define HGFS_ERROR_SUCCESS           ERROR_SUCCESS
#define HGFS_ERROR_IO                ERROR_IO_DEVICE
#define HGFS_ERROR_ACCESS_DENIED     ERROR_ACCESS_DENIED
#define HGFS_ERROR_INVALID_PARAMETER ERROR_INVALID_PARAMETER
#define HGFS_ERROR_INVALID_HANDLE    ERROR_INVALID_HANDLE
#define HGFS_ERROR_PROTOCOL          RPC_S_PROTOCOL_ERROR
#define HGFS_ERROR_STALE_SESSION     ERROR_CONNECTION_INVALID
#define HGFS_ERROR_BUSY              ERROR_RETRY
#define HGFS_ERROR_PATH_BUSY         ERROR_RETRY
#define HGFS_ERROR_FILE_NOT_FOUND    ERROR_FILE_NOT_FOUND
#define HGFS_ERROR_FILE_EXIST        ERROR_ALREADY_EXISTS
#define HGFS_ERROR_NOT_SUPPORTED     ERROR_NOT_SUPPORTED
#define HGFS_ERROR_NOT_ENOUGH_MEMORY ERROR_NOT_ENOUGH_MEMORY
#define HGFS_ERROR_TOO_MANY_SESSIONS ERROR_MAX_SESSIONS_REACHED
#define HGFS_ERROR_INTERNAL          ERROR_INTERNAL_ERROR
#else
#define HGFS_ERROR_SUCCESS           0
#define HGFS_ERROR_IO                EIO
#define HGFS_ERROR_ACCESS_DENIED     EACCES
#define HGFS_ERROR_INVALID_PARAMETER EINVAL
#define HGFS_ERROR_INVALID_HANDLE    EBADF
#define HGFS_ERROR_PROTOCOL          EPROTO
#define HGFS_ERROR_STALE_SESSION     ENETRESET
#define HGFS_ERROR_BUSY              EBUSY
#define HGFS_ERROR_PATH_BUSY         EBUSY
#define HGFS_ERROR_FILE_NOT_FOUND    ENOENT
#define HGFS_ERROR_FILE_EXIST        EEXIST
#define HGFS_ERROR_NOT_SUPPORTED     EOPNOTSUPP
#define HGFS_ERROR_NOT_ENOUGH_MEMORY ENOMEM
#define HGFS_ERROR_TOO_MANY_SESSIONS ECONNREFUSED
#define HGFS_ERROR_INTERNAL          EINTERNAL
#endif // _WIN32

/*
 * Unfortunately, we need a catch-all "generic error" to use with
 * HgfsInternalStatus, because there are times when cross-platform code needs
 * 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)

#ifndef _WIN32
/*
 * This error code is used to notify the client that some of the parameters passed
 * (e.g. file handles) are not supported. Clients are expected to correct
 * the parameter (e.g. pass file name instead) and retry.
 *
 * Note that this error code is artificially made up and in future may conflict
 * with an "official" error code when added.
 */
#define EPARAMETERNOTSUPPORTED  (MAX_INT32 - 1)
#endif

/*
 * 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(packetSize, request) (packetSize - (sizeof *request - 1))
#define HGFS_NAME_BUFFER_SIZET(packetSize, sizet) (packetSize - ((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_ */

070701000000DE000081A4000000000000000000000001682255050000073C000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hgfsVirtualDir.h   /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * hgfsVirtualDir.h --
 * 
 *    Defines for virtual directory names in hgfs.
 */


#ifndef _HGFSVIRTUALDIR_H_
#   define _HGFSVIRTUALDIR_H_

/*
 * These are the names of the virtual directories for accessing UNC
 * names and drives on the host machine.
 */
# define HGFS_DRIVE_DIR_NAME "drive"
# define HGFS_UNC_DIR_NAME "unc"

#define HGFS_STR_LEN(str) (sizeof str - 1)

#endif /* _HGFSVIRTUALDIR_H_ */
070701000000DF000081A400000000000000000000000168225505000005BA000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hostType.h /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hostType.h --
 *
 *      Interface to host-specific information functions
 *   
 */

#ifndef _HOSTTYPE_H_
#define _HOSTTYPE_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

Bool HostType_OSIsVMK(void);
Bool HostType_OSIsSimulator(void);

/* Old name. TODO: remove */
static INLINE Bool
HostType_OSIsPureVMK(void)
{
   return HostType_OSIsVMK();
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* ifndef _HOSTTYPE_H_ */
  070701000000E0000081A40000000000000000000000016822550500002306000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/hostinfo.h /*********************************************************
 * Copyright (C) 1998-2024 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hostinfo.h --
 *
 *      Interface to host-specific information functions
 *
 */

#if !defined(_HOSTINFO_H_)
#define _HOSTINFO_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "x86vendor.h"
#include "unicodeTypes.h"
#include "x86cpuid.h"

#if defined(__cplusplus)
extern "C" {
#endif

#define MAX_OS_NAME_LEN 128
#define MAX_OS_FULLNAME_LEN 512

typedef enum {
   HOSTINFO_PROCESS_QUERY_DEAD,    // Procss is dead (does not exist)
   HOSTINFO_PROCESS_QUERY_ALIVE,   // Process is alive (does exist)
   HOSTINFO_PROCESS_QUERY_UNKNOWN  // Process existence cannot be determined
} HostinfoProcessQuery;

typedef struct HostinfoProcessSnapshot HostinfoProcessSnapshot;

HostinfoProcessSnapshot *Hostinfo_AcquireProcessSnapshot(void);
void Hostinfo_ReleaseProcessSnapshot(HostinfoProcessSnapshot *s);

HostinfoProcessQuery Hostinfo_QueryProcessSnapshot(HostinfoProcessSnapshot *s,
                                                   int pid);

HostinfoProcessQuery Hostinfo_QueryProcessExistence(int pid);
HostinfoProcessQuery Hostinfo_QueryProcessReaped(int pid);

/* This macro defines the current version of the structured header. */
#define HOSTINFO_STRUCT_HEADER_VERSION 1

/*
 * This struct is used to build a detailed OS data. The detailed OS data will
 * be composed of two parts. The first part is the header and the second part
 * will be a string that is appended to this header in memory.
 */
typedef struct HostinfoDetailedDataHeader {
   uint32  version;
   char    shortName[MAX_OS_NAME_LEN + 1];
   char    fullName[MAX_OS_FULLNAME_LEN + 1];
} HostinfoDetailedDataHeader;

char *Hostinfo_NameGet(void);            // Don't free result
char *Hostinfo_HostName(void);           // free result
char *Hostinfo_GetOSName(void);          // free result
char *Hostinfo_GetOSGuestString(void);   // free result
char *Hostinfo_GetOSDetailedData(void);  // free result

void Hostinfo_MachineID(uint32 *hostNameHash,
                        uint64 *hostHardwareID);

Bool Hostinfo_GetMemoryInfoInPages(unsigned int *minSize,
                                   unsigned int *maxSize,
       unsigned int *currentSize);
#ifdef __linux__
Bool Hostinfo_GetSwapInfoInPages(unsigned int *totalSwap,
                                 unsigned int *freeSwap);
#endif
Bool Hostinfo_GetRatedCpuMhz(int32 cpuNumber,
                             uint32 *mHz);
char *Hostinfo_GetCpuDescription(uint32 cpuNumber);
void Hostinfo_GetTimeOfDay(VmTimeType *time);
VmTimeType Hostinfo_SystemUpTime(void);
VmTimeType Hostinfo_SystemTimerNS(void);

static INLINE VmTimeType
Hostinfo_SystemTimerUS(void)
{
   return Hostinfo_SystemTimerNS() / 1000ULL;
}

static INLINE VmTimeType
Hostinfo_SystemTimerMS(void)
{
   return Hostinfo_SystemTimerNS() / 1000000ULL;
}

/*
 * Apple's kernel major versions are the same as their marketed
 * minor versions + 4. (E.g. Marketed 10.8.0 == Kernel 12.0.0)
 * These constants simplify this and make code easier to read / understand.
 */
enum {
   HOSTINFO_OS_VERSION_MACOS_10_5  = 9,
   HOSTINFO_OS_VERSION_MACOS_10_6  = 10,
   HOSTINFO_OS_VERSION_MACOS_10_7  = 11,
   HOSTINFO_OS_VERSION_MACOS_10_8  = 12,
   HOSTINFO_OS_VERSION_MACOS_10_9  = 13,
   HOSTINFO_OS_VERSION_MACOS_10_10 = 14,
   HOSTINFO_OS_VERSION_MACOS_10_11 = 15,
   HOSTINFO_OS_VERSION_MACOS_10_12 = 16,
   HOSTINFO_OS_VERSION_MACOS_10_13 = 17,
   HOSTINFO_OS_VERSION_MACOS_10_14 = 18,
   HOSTINFO_OS_VERSION_MACOS_10_15 = 19,
   HOSTINFO_OS_VERSION_MACOS_11    = 20,
};

int Hostinfo_OSVersion(unsigned int i);
int Hostinfo_GetSystemBitness(void);
const char *Hostinfo_OSVersionString(void);

#if defined(_WIN32)
Bool Hostinfo_OSIsWinNT(void);
Bool Hostinfo_OSIsWow64(void);
int Hostinfo_EnumerateAllProcessPids(uint32 **processIds);
#else
void Hostinfo_ResetProcessState(const int *keepFds,
                                size_t numKeepFds);

int Hostinfo_Execute(const char *path,
                     char * const *args,
                     Bool wait,
                     const int *keepFds,
                     size_t numKeepFds);

typedef enum HostinfoDaemonizeFlags {
   HOSTINFO_DAEMONIZE_DEFAULT = 0,
   HOSTINFO_DAEMONIZE_NOCHDIR = (1 << 0),
   HOSTINFO_DAEMONIZE_NOCLOSE = (1 << 1),
   HOSTINFO_DAEMONIZE_EXIT    = (1 << 2),
   HOSTINFO_DAEMONIZE_LOCKPID = (1 << 3),
} HostinfoDaemonizeFlags;

Bool Hostinfo_Daemonize(const char *path,
                        char * const *args,
                        HostinfoDaemonizeFlags flags,
                        const char *pidPath,
                        const int *keepFds,
                        size_t numKeepFds);
#endif

Bool Hostinfo_NestingSupported(void);
Bool Hostinfo_VCPUInfoBackdoor(unsigned bit);
Bool Hostinfo_TouchBackDoor(void);
Bool Hostinfo_TouchVirtualPC(void);
Bool Hostinfo_TouchXen(void);
Bool Hostinfo_HyperV(void);
char *Hostinfo_HypervisorCPUIDSig(void);
void Hostinfo_LogHypervisorCPUID(void);
char *Hostinfo_HypervisorInterfaceSig(void);
uint32 Hostinfo_GetNestedBuildNum(void);

#define HGMP_PRIVILEGE    0
#define HGMP_NO_PRIVILEGE 1
char *Hostinfo_GetModulePath(uint32 priv);
char *Hostinfo_GetLibraryPath(void *addr);

char *Hostinfo_GetUser(void);
void Hostinfo_LogMemUsage(void);


/*
 * HostInfoCpuIdInfo --
 *
 *      Contains cpuid information for a CPU.
 */

typedef struct {
   CpuidVendor vendor;

   uint32 version;
   uint8 family;
   uint8 model;
   uint8 stepping;
   uint8 type;

   uint32 features;
   uint32 extfeatures;
} HostinfoCpuIdInfo;

#define AMDVBSSupport(eax) (CPUID_MODEL_IS_ZEN2(eax) || \
                            CPUID_MODEL_IS_ZEN3(eax) || \
                            CPUID_MODEL_IS_ZEN4(eax) || \
                            CPUID_MODEL_IS_ZEN5(eax))

uint32 Hostinfo_NumCPUs(void);
char *Hostinfo_GetCpuidStr(void);
Bool Hostinfo_GetCpuid(HostinfoCpuIdInfo *info);

#if defined(_WIN32)
typedef enum {
   OS_WIN95                  = 1,
   OS_WIN98                  = 2,
   OS_WINME                  = 3,
   OS_WINNT                  = 4,
   OS_WIN2K                  = 5,
   OS_WINXP                  = 6,
   OS_WIN2K3                 = 7,
   OS_VISTA                  = 8,
   OS_WINSEVEN               = 9,
   OS_WIN8                   = 10,
   OS_WIN10                  = 11,
   OS_WIN11                  = 12,
   OS_UNKNOWN                = 99999 // last, highest value
} OS_TYPE;

typedef enum {
   OS_DETAIL_WIN95           = 1,
   OS_DETAIL_WIN98           = 2,
   OS_DETAIL_WINME           = 3,
   OS_DETAIL_WINNT           = 4,
   OS_DETAIL_WIN2K           = 5,
   OS_DETAIL_WIN2K_PRO       = 6,
   OS_DETAIL_WIN2K_SERV      = 7,
   OS_DETAIL_WIN2K_ADV_SERV  = 8,
   OS_DETAIL_WINXP           = 9,
   OS_DETAIL_WINXP_HOME      = 10,
   OS_DETAIL_WINXP_PRO       = 11,
   OS_DETAIL_WINXP_X64_PRO   = 12,
   OS_DETAIL_WIN2K3          = 13,
   OS_DETAIL_WIN2K3_WEB      = 14,
   OS_DETAIL_WIN2K3_ST       = 15,
   OS_DETAIL_WIN2K3_EN       = 16,
   OS_DETAIL_WIN2K3_BUS      = 17,
   OS_DETAIL_VISTA           = 18,
   OS_DETAIL_WIN2K8          = 19,
   OS_DETAIL_WINSEVEN        = 20,
   OS_DETAIL_WIN2K8R2        = 21,
   OS_DETAIL_WIN8            = 22,
   OS_DETAIL_WIN8SERVER      = 23,
   OS_DETAIL_WIN10           = 24,
   OS_DETAIL_WIN10SERVER     = 25,
   OS_DETAIL_WIN11           = 26,
   OS_DETAIL_WIN11SERVER     = 27,
   OS_DETAIL_UNKNOWN         = 99999  // last, highest value
} OS_DETAIL_TYPE;

/* generic names (to protect the future) but Windows specific for now */
OS_TYPE Hostinfo_GetOSType(void);
OS_DETAIL_TYPE Hostinfo_GetOSDetailType(void);

Bool Hostinfo_GetMhzOfProcessor(int32 processorNumber,
				uint32 *currentMhz,
                                uint32 *maxMhz);
uint64 Hostinfo_SystemIdleTime(void);

#endif
void Hostinfo_LogLoadAverage(void);
Bool Hostinfo_GetLoadAverage(uint32 *l);

#ifdef __APPLE__
size_t Hostinfo_GetKernelZoneElemSize(char const *name);
char *Hostinfo_GetHardwareModel(void);
int Hostinfo_ProcessIsRosetta(void);
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* ifndef _HOSTINFO_H_ */
  070701000000E1000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/imgcust-common 070701000000E2000081A4000000000000000000000001682255050000065C000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/imgcust-common/imgcust-api.h   /*********************************************************
 * Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * imgcust-api.h --
 *
 *      C interface to package deployment.
 */

#ifndef IMGCUST_API_H
#define IMGCUST_API_H

#ifdef WIN32
/*
 * We get warnings c4251 and c4275 when exporting c++ classes that
 * inherit from STL classes or use them as members. We can't
 * export these classes or the client will get duplicate symbols,
 * so we disable the warning.
 */
#pragma warning( disable : 4251 )
#pragma warning( disable : 4275 )

// if _IMGCUST_DLL is defined, we export functions/classes with this prefix
#ifdef _IMGCUST_DLL
   #define IMGCUST_API __declspec(dllexport)
#else
   #define IMGCUST_API __declspec(dllimport)
#endif
#else // linux
#define IMGCUST_API __attribute__ ((visibility ("default")))
#endif // WIN32

#endif // IMGCUST_API_H
070701000000E3000081A40000000000000000000000016822550500000563000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/include/imgcust-common/log.h   /*********************************************************
 * Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * log.h --
 *
 *      Logging method that need to be provided by the client of the
 *      library to enable logging.
 */

#ifndef IMGCUST_COMMON_LOG_H
#define IMGCUST_COMMON_LOG_H

#define GUESTCUST_LOG_DIRNAME "vmware-imc"

#ifdef __cplusplus
namespace ImgCustCommon
{
#endif

enum LogLevel {log_debug, log_info, log_warning, log_error};

typedef void (*LogFunction) (int level, const char *fmtstr, ...);

#ifdef __cplusplus
} // namespace ImgCustCommon
#endif

#endif // IMGCUST_COMMON_LOG_H
 070701000000E4000081A40000000000000000000000016822550500000DA9000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/imgcust-common/process.h   /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * process.h --
 *
 *      Functions to launch an external process.
 */

#ifndef IMGCUST_COMMON_PROCESS_H
#define IMGCUST_COMMON_PROCESS_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct ProcessOpaque *ProcessHandle;

typedef enum _ProcessError {
   PROCESS_SUCCESS,
   PROCESS_FAILED
} ProcessError;


/*
 *------------------------------------------------------------------------------
 *
 * Process_Create --
 *
 *      Creates a process and returns result of the operation.
 *
 *      Since this file can be included in a c++ file that already has the
 *      namespaced c++ definition of LogFunction defined, we can't use the c
 *      version of LogFunction as an input. Only choice is to make it a raw
 *      pointer and cast it in the processXXX.c file which can use the C
 *      definition of LogFunction.
 *
 *------------------------------------------------------------------------------
 */

ProcessError
Process_Create(ProcessHandle *h, char *args[], void *log);


/*
 *------------------------------------------------------------------------------
 *
 * Process_RunToComplete --
 *
 *      Runs the process to completion and returns its result.
 *
 *------------------------------------------------------------------------------
 */

ProcessError
Process_RunToComplete(ProcessHandle h, unsigned long timeout);


/*
 *------------------------------------------------------------------------------
 *
 * Process_GetStdout --
 *
 *      Returns process's standard output.
 *
 *------------------------------------------------------------------------------
 */

const char *
Process_GetStdout(ProcessHandle h);


/*
 *------------------------------------------------------------------------------
 *
 * Process_GetStderr --
 *
 *      Returns process's standard error output.
 *
 *------------------------------------------------------------------------------
 */

const char *
Process_GetStderr(ProcessHandle h);


/*
 *------------------------------------------------------------------------------
 *
 * Process_GetExitCode --
 *
 *      Returns process's exit code.
 *
 *------------------------------------------------------------------------------
 */

int
Process_GetExitCode(ProcessHandle h);


/*
 *------------------------------------------------------------------------------
 *
 * Process_Destroy --
 *
 *      Destroys the process and returns result of the operation.
 *
 *------------------------------------------------------------------------------
 */

ProcessError
Process_Destroy(ProcessHandle h);

#ifdef __cplusplus
} // extern "C"
#endif

#endif // IMGCUST_COMMON_PROCESS_H
   070701000000E5000081A4000000000000000000000001682255050000069D000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/impersonate.h  /*********************************************************
 * Copyright (C) 2003-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * impersonate.h --
 *
 *	Provides functions to assist in impersonating and unimpersonating
 *	as a given user.
 */

#ifndef _IMPERSONATE_H_
#define _IMPERSONATE_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "auth.h"

#if defined(__cplusplus)
extern "C" {
#endif

void Impersonate_Init(void);

Bool Impersonate_Owner(const char *file);
Bool Impersonate_Do(const char *user, AuthToken token);
Bool Impersonate_Undo(void);
char *Impersonate_Who(void);

Bool Impersonate_ForceRoot(void);
Bool Impersonate_UnforceRoot(void);

Bool Impersonate_Runas(const char *cfg, const char *caller, 
                       AuthToken callerToken);

#ifdef _WIN32
Bool Impersonate_CfgRunasOnly(const char *cfg);
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _IMPERSONATE_H_
   070701000000E6000081A4000000000000000000000001682255050000155C000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/includeCheck.h /*********************************************************
 * Copyright (c) 1998-2019, 2021-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * includeCheck.h --
 *
 *      Restrict include file use.
 *
 * In every .h file, define one or more of these
 *
 *      INCLUDE_ALLOW_VMX
 *      INCLUDE_ALLOW_USERLEVEL
 *      INCLUDE_ALLOW_VMCORE
 *      INCLUDE_ALLOW_MODULE
 *      INCLUDE_ALLOW_VMKERNEL
 *      INCLUDE_ALLOW_DISTRIBUTE
 *      INCLUDE_ALLOW_VMK_MODULE
 *      INCLUDE_ALLOW_VMKDRIVERS
 *      INCLUDE_ALLOW_MKS
 *
 * 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.
 */

#if defined VMM && defined ULM
#error "VMM and ULM cannot be defined at the same time."
#endif

#if defined ULM && !defined USERLEVEL
#error "All ULM code must be compiled with USERLEVEL set."
#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 && \
    !defined INCLUDE_ALLOW_MKS
#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 ULM && !defined INCLUDE_ALLOW_USERLEVEL && \
    !defined INCLUDE_ALLOW_MKS && !defined VSAN_USERLEVEL
#error "The surrounding include file is not allowed at userlevel."
#endif
#undef INCLUDE_ALLOW_USERLEVEL

#if defined MODULE && !defined VMKERNEL_MODULE && \
    !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 INCLUDE_ALLOW_MKS && !(defined COREMKS)
#error "The surrounding include file is not allowed outside of the MKS."
#endif
#undef INCLUDE_ALLOW_MKS
070701000000E7000081A40000000000000000000000016822550500000511000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/ioplGet.h  /*********************************************************
 * Copyright (C) 2012-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * ioplGet.h --
 *
 *   A utility function to retrieve the IOPL level of the current thread
 *   Compiles on x86, x64 of Linux and Windows
 */

#ifndef _IOPL_GET_H_
#define _IOPL_GET_H_

#include "x86_basic_defs.h"
#include "vm_basic_asm.h"

#if defined(__cplusplus)
extern "C" {
#endif

#define Iopl_Get() ((GetCallerEFlags() >> EFLAGS_IOPL_SHIFT) & 0x3)

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
   070701000000E8000081A400000000000000000000000168225505000011A6000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/iovector.h /*********************************************************
 * Copyright (C) 1998-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * iovector.h --
 *
 *      iov management code API.
 */

#ifndef _IOVECTOR_H_
#define _IOVECTOR_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Ugly definition of struct iovec.
 */
#if defined(__linux__) || defined(sun) || defined(__APPLE__) || \
    defined(__FreeBSD__) || defined(__EMSCRIPTEN__)
#include <sys/uio.h>    // for struct iovec
#else

#ifndef HAS_IOVEC
struct iovec {
   void *iov_base; /* Starting address. */
   size_t iov_len; /* Length in bytes. */
};
#endif   // HAS_IOVEC

#endif

/*
 * An I/O Vector.
 */
typedef struct VMIOVec {
   SectorType startSector;
   SectorType numSectors;
   uint64 numBytes;             /* Total bytes from all of the entries */
   uint32 numEntries;           /* Total number of entries */
   Bool read;                   /* is it a readv operation? else it's write */
   struct iovec *entries;       /* Array of entries (dynamically allocated) */
   struct iovec *allocEntries;  /* The original array that can be passed to free().
                                 * NULL if entries is on a stack. */
} VMIOVec;

#define LAZY_ALLOC_MAGIC      ((void*)0xF0F0)

VMIOVec* IOV_Split(VMIOVec *regionV,
                   SectorType numSectors,
                   uint32 sectorSize);

void IOV_Log(const VMIOVec *iov);
void IOV_Zero(VMIOVec *iov);
Bool IOV_IsZero(VMIOVec* iov);
VMIOVec* IOV_Duplicate(VMIOVec* iovIn);
VMIOVec* IOV_Allocate(int numEntries);
void IOV_Free(VMIOVec* iov);
void IOV_DuplicateStatic(VMIOVec *iovIn,
                         int numStaticEntries,
                         struct iovec *staticEntries,
                         VMIOVec *iovOut);

void IOV_MakeSingleIOV(VMIOVec* v,
                       struct iovec* iov,
                       SectorType startSector,
                       SectorType dataLen,
                       uint32 sectorSize,
                       uint8* buffer,
                       Bool read);

void IOV_WriteIovToBuf(struct iovec const *entries,
                       int numEntries,
                       uint8 *bufOut,
                       size_t bufSize);

void IOV_WriteBufToIov(const uint8 *bufIn,
                       size_t bufSize,
                       struct iovec const *entries,
                       int numEntries);

size_t
IOV_WriteIovToBufPlus(struct iovec* entries,
                      int numEntries,
                      uint8* bufOut,
                      size_t bufSize,
                      size_t iovOffset);

size_t
IOV_WriteBufToIovPlus(uint8* bufIn,
                      size_t bufSize,
                      struct iovec* entries,
                      int numEntries,
                      size_t iovOffset);

size_t
IOV_WriteIovToIov(VMIOVec *srcIov,
                  VMIOVec *dstIov,
                  uint32 sectorSizeShift);

/*
 *-----------------------------------------------------------------------------
 *
 * IOV_ASSERT, IOV_Assert --
 *
 *      Checks that the 'numEntries' iovecs in 'iov' are non-null and have
 *      nonzero lengths.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	Assert-fails if the iovec is invalid.
 *
 *-----------------------------------------------------------------------------
 */


#if VMX86_DEBUG
#define IOV_ASSERT(IOVEC, NUM_ENTRIES) IOV_Assert(IOVEC, NUM_ENTRIES)
void IOV_Assert(struct iovec *iov,       // IN: iovector
                uint32 numEntries);      // IN: # of entries in 'iov'
#else
#define IOV_ASSERT(IOVEC, NUM_ENTRIES) ((void) 0)
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* #ifndef _IOVECTOR_H_ */
  070701000000E9000081A40000000000000000000000016822550500000BB5000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/jsmn.h /* **********************************************************
 * Copyright (c) 2019,2021 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * Copyright (c) 2010 Serge A. Zaitsev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef __JSMN_H_
#define __JSMN_H_

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * JSON type identifier. Basic types are:
 *    o Object
 *    o Array
 *    o String
 *    o Other primitive: number, boolean (true/false) or null
 */
typedef enum {
   JSMN_UNDEFINED = 0,
   JSMN_OBJECT = 1,
   JSMN_ARRAY = 2,
   JSMN_STRING = 3,
   JSMN_PRIMITIVE = 4
} jsmntype_t;

enum jsmnerr {
   /* Not enough tokens were provided */
   JSMN_ERROR_NOMEM = -1,
   /* Invalid character inside JSON string */
   JSMN_ERROR_INVAL = -2,
   /* The string is not a full JSON packet, more bytes expected */
   JSMN_ERROR_PART = -3
};

/**
 * JSON token description.
 * type     type (object, array, string etc.)
 * start start position in JSON data string
 * end      end position in JSON data string
 */
typedef struct {
   jsmntype_t type;
   int start;
   int end;
   int size;
#ifdef JSMN_PARENT_LINKS
   int parent;
#endif
} jsmntok_t;

/**
 * JSON parser. Contains an array of token blocks available. Also stores
 * the string being parsed now and current position in that string
 */
typedef struct {
   unsigned int pos; /* offset in the JSON string */
   unsigned int toknext; /* next token to allocate */
   int toksuper; /* superior token node, e.g parent object or array */
} jsmn_parser;

/**
 * Create JSON parser over an array of tokens
 */
void jsmn_init(jsmn_parser *parser);

/**
 * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
 * a single JSON object.
 */
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
      jsmntok_t *tokens, unsigned int num_tokens);

#ifdef __cplusplus
}
#endif

#endif /* __JSMN_H_ */
   070701000000EA000081A40000000000000000000000016822550500000640000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/libExport.hh   /*********************************************************
 * Copyright (C) 2004-2016,2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * libExport.hh --
 *
 *      Declares proper decorations for dll-export interface
 *      for all modules in libs.
 */

#ifndef LIB_EXPORT_HH
#define LIB_EXPORT_HH

#include "vm_api.h"

#ifndef LIB_EXPORT
   #ifdef LIB_EXPORT_SOURCE
      #define LIB_EXPORT VMW_LIB_DYNAMIC
   #else
      #define LIB_EXPORT VMW_LIB_CLIENT
   #endif
#endif

#ifndef LIB_EXPORT_WUI
   #ifdef LIB_EXPORT_WUI_SOURCE
      #define LIB_EXPORT_WUI VMW_LIB_DYNAMIC
   #else
      #define LIB_EXPORT_WUI VMW_LIB_CLIENT
   #endif
#endif

#ifndef VMSTRING_EXPORT
   #ifdef VMSTRING_EXPORT_SOURCE
      #define VMSTRING_EXPORT VMW_LIB_DYNAMIC
   #else
      #define VMSTRING_EXPORT VMW_LIB_CLIENT
   #endif
#endif

#endif // LIB_EXPORT_HH
070701000000EB000081A40000000000000000000000016822550500000579000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/localconfig.h  /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _LOCALCONFIG_H_
#define _LOCALCONFIG_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "preference.h"

#if defined(__cplusplus)
extern "C" {
#endif

#define LocalConfig_GetBool Preference_GetBool
#define LocalConfig_GetTriState Preference_GetTriState
#define LocalConfig_GetLong Preference_GetLong
#define LocalConfig_GetString Preference_GetString
#define LocalConfig_GetPathName Preference_GetPathName

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
   070701000000EC000081A40000000000000000000000016822550500004F7D000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/log.h  /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef VMWARE_LOG_H
#define VMWARE_LOG_H

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "productState.h"
#include <stdarg.h>

#if defined(_WIN32)
#include <windows.h>
#endif

#if defined(__cplusplus)
extern "C" {
#endif


/**
 * Log Facility
 * ------------
 *
 * The Log Facility exists to record events of program execution for purposes
 * of auditing, debugging, and monitoring. Any non-trivial program should use
 * logging, to enable those purposes.
 *
 * Events recorded by the Log Facility (i.e. calls to here-declared functions)
 * are automatically filtered, time-stamped, and persisted.
 *
 * Configuration for field engineers is documented at
 *   https://wiki.eng.vmware.com/PILogFacility
 *
 * For full details on configurable parameters and their semantics,
 * use the source, Luke -- starting at lib/log/logFacility.c
 *
 * The events are explicitly annotated by the developer (i.e. you)
 * with a level, an optional group, and a message.
 *
 * Notes:
 * - Log() is defined as Log_Info()
 * - Warning() is defined as Log_Warning()
 *
 * Log Level
 * ---------
 *
 * The Log Level indicates the (in)significance of the event, with larger
 * numbers indicating lesser significance. The level, whether explicit
 * (e.g. Log_Level(level, ...)) or implicit (e.g. Log_Info(...)),
 * should be chosen with some care. A Log_Info() message containing the word
 * "warning" or "error" is almost certainly mis-routed.
 *
 * The following rules of thumb provide a rough guide to choice of level.
 *
 * * VMW_LOG_AUDIT -- always logged, for auditing purposes
 *  + change to authorization
 *  + change to configuration
 *
 * * VMW_LOG_PANIC -- system broken; cannot exit gracefully
 *  + wild pointer; corrupt arena
 *  + error during error exit
 *
 * * VMW_LOG_ERROR -- system broken; must exit
 *  + required resource inaccessible (memory; storage; network)
 *  + incorrigible internal inconsistency
 *
 * * VMW_LOG_WARNING -- unexpected condition; may require immediate attention
 *  + inconsistency corrected or ignored
 *  + timeout or slow operation
 *
 * * VMW_LOG_NOTICE -- unexpected condition; may require eventual attention
 *  + missing config; default used
 *  + lower level error ignored
 *
 * * VMW_LOG_INFO -- expected condition
 *  + non-standard configuration
 *  + alternate path taken (e.g. on lower level error)
 *
 * * VMW_LOG_VERBOSE -- normal operation; potentially useful information
 *  + system health observation, for monitoring
 *  + unexpected non-error state
 *
 * * VMW_LOG_TRIVIA -- normal operation; excess information
 *  + vaguely interesting note
 *  + anything else the developer thinks might be useful
 *
 * * VMW_LOG_DEBUG_* -- flow and logic tracing
 *  + routine entry, with parameters; routine exit, with return value
 *  + intermediate values or decisions
 *
 * Log Facility Message Groups
 * ---------------------------
 *
 * For information about Log Facility Message Groups visit
 * lib/log/logFacility.c.
 *
 * Log Facility Message Filtering
 * ------------------------------
 *
 * For information about Log Facility message filtering visit
 * lib/log/logFacility.c.
 *
 * Log Message Guidelines
 * ----------------------
 *
 * Every Log message should be unique, unambiguously describing the event
 * being logged, and should include all relevant data, in human-readable form.
 * Source line number is *not* useful.
 * + printf-style arguments
 * + function name as prefix (controversial)
 * + format pure number (e.g. error number) in decimal
 * + format bitfield (e.g. compound error code) in hex
 * + format disk size or offset in hex; specify units if not bytes
 * + quote string arguments (e.g. pathnames) which can contain spaces
 *
 * Level            Value    Comments
 *---------------------------------------------------------------------------
 */

typedef enum {
   VMW_LOG_AUDIT    = 0,  // Always output; never to stderr
   VMW_LOG_PANIC    = 1,  // Desperation
   VMW_LOG_ERROR    = 2,  // Irremediable error
   VMW_LOG_WARNING  = 3,  // Unexpected condition; may need immediate attention
   VMW_LOG_NOTICE   = 4,  // Unexpected condition; may need eventual attention
   VMW_LOG_INFO     = 5,  // Expected condition
   VMW_LOG_VERBOSE  = 6,  // Extra information
   VMW_LOG_TRIVIA   = 7,  // Excess information
   VMW_LOG_DEBUG_00 = 8,
   VMW_LOG_DEBUG_01 = 9,
   VMW_LOG_DEBUG_02 = 10,
   VMW_LOG_DEBUG_03 = 11,
   VMW_LOG_DEBUG_04 = 12,
   VMW_LOG_DEBUG_05 = 13,
   VMW_LOG_DEBUG_06 = 14,
   VMW_LOG_DEBUG_07 = 15,
   VMW_LOG_DEBUG_08 = 16,
   VMW_LOG_DEBUG_09 = 17,
   VMW_LOG_DEBUG_10 = 18,
   VMW_LOG_DEBUG_11 = 19,
   VMW_LOG_DEBUG_12 = 20,
   VMW_LOG_DEBUG_13 = 21,
   VMW_LOG_DEBUG_14 = 22,
   VMW_LOG_DEBUG_15 = 23,
   VMW_LOG_MAX      = 24,
} VmwLogLevel;

#if defined(VMX86_DEBUG)
   #define LOG_FILTER_DEFAULT_LEVEL VMW_LOG_VERBOSE
#else
   #define LOG_FILTER_DEFAULT_LEVEL VMW_LOG_INFO
#endif

#if defined(VMX86_SERVER)
/* WORLD_MAX_OPID_STRING_SIZE */
#define LOG_MAX_OPID_LENGTH 128
#else
/* We do not expect long opIDs in non-ESX environments. 32 should be enough. */
#define LOG_MAX_OPID_LENGTH 32
#endif

#define LOG_NO_KEEPOLD                 0  // Keep no old log files
#define LOG_NO_ROTATION_SIZE           0  // Do not rotate based on file size
#define LOG_NO_THROTTLE_THRESHOLD      0  // No threshold before throttling
#define LOG_NO_BPS_LIMIT               0xFFFFFFFF  // unlimited input rate

/*
 * The defaults for how many older log files to kept around, and what to do
 * with rotation-by-size.
 */

#if defined(VMX86_SERVER)
#define LOG_DEFAULT_KEEPOLD       10
#define LOG_DEFAULT_ROTATION_SIZE 2048000
#else
#define LOG_DEFAULT_KEEPOLD       3
#define LOG_DEFAULT_ROTATION_SIZE LOG_NO_ROTATION_SIZE
#endif

/*
 * The "routing" parameter contains the level in the low order bits;
 * the higher order bits specify the group of the log call.
 */

#define VMW_LOG_LEVEL_BITS 5  // Log level bits (32 levels max)
#define VMW_LOG_LEVEL_MASK ((int)(1 << VMW_LOG_LEVEL_BITS) - 1)

#define VMW_LOG_LEVEL(routing)  ((routing) & VMW_LOG_LEVEL_MASK)
#define VMW_LOG_MODULE(routing) (((routing) >> VMW_LOG_LEVEL_BITS))

void
LogV(uint32 routing,
     const char *fmt,
     va_list args);

void
Log_Level(uint32 routing,
          const char *fmt,
          ...) PRINTF_DECL(2, 3);

/*
 * Log     = Log_Info
 * Warning = Log_Warning
 */

static INLINE void PRINTF_DECL(1, 2)
Log_Panic(const char *fmt,
          ...)
{
   va_list ap;

   va_start(ap, fmt);
   LogV(VMW_LOG_PANIC, fmt, ap);
   va_end(ap);
}


static INLINE void PRINTF_DECL(1, 2)
Log_Audit(const char *fmt,
          ...)
{
   va_list ap;

   va_start(ap, fmt);
   LogV(VMW_LOG_AUDIT, fmt, ap);
   va_end(ap);
}


static INLINE void PRINTF_DECL(1, 2)
Log_Error(const char *fmt,
          ...)
{
   va_list ap;

   va_start(ap, fmt);
   LogV(VMW_LOG_ERROR, fmt, ap);
   va_end(ap);
}


static INLINE void PRINTF_DECL(1, 2)
Log_Warning(const char *fmt,
            ...)
{
   va_list ap;

   va_start(ap, fmt);
   LogV(VMW_LOG_WARNING, fmt, ap);
   va_end(ap);
}


static INLINE void PRINTF_DECL(1, 2)
Log_Notice(const char *fmt,
           ...)
{
   va_list ap;

   va_start(ap, fmt);
   LogV(VMW_LOG_NOTICE, fmt, ap);
   va_end(ap);
}


static INLINE void PRINTF_DECL(1, 2)
Log_Info(const char *fmt,
         ...)
{
   va_list ap;

   va_start(ap, fmt);
   LogV(VMW_LOG_INFO, fmt, ap);
   va_end(ap);
}


static INLINE void PRINTF_DECL(1, 2)
Log_Verbose(const char *fmt,
            ...)
{
   va_list ap;

   va_start(ap, fmt);
   LogV(VMW_LOG_VERBOSE, fmt, ap);
   va_end(ap);
}


static INLINE void PRINTF_DECL(1, 2)
Log_Trivia(const char *fmt,
           ...)
{
   va_list ap;

   va_start(ap, fmt);
   LogV(VMW_LOG_TRIVIA, fmt, ap);
   va_end(ap);
}

#if !defined(VMM)
typedef struct {
   int32       legalLevelValue;
   const char *legalName;
   const char *levelIdStr;
} LogLevelData;

const LogLevelData *
Log_MapByLevel(VmwLogLevel level);

const LogLevelData *
Log_MapByName(const char *name);

typedef struct LogOutput LogOutput;

/* Forward decl */
struct Dictionary;
struct CfgInterface;

struct CfgInterface *
Log_CfgInterface(void);

int32
Log_SetStderrLevel(uint32 group,
                   int32 level);

int32
Log_GetStderrLevel(uint32 group);

int32
Log_SetLogLevel(uint32 group,
                int32 level);

int32
Log_GetLogLevel(uint32 group);

int32
Log_LookupGroupNumber(const char *groupName);

const char *
Log_LookupGroupName(uint32 group);

LogOutput *
Log_NewStdioOutput(const char *appPrefix,
                   struct Dictionary *params,
                   struct CfgInterface *cfgIf);

LogOutput *
Log_NewSyslogOutput(const char *appPrefix,
                    const char *instanceName,
                    struct Dictionary *params,
                    struct CfgInterface *cfgIf);

LogOutput *
Log_NewFileOutput(const char *appPrefix,
                  const char *instanceName,
                  struct Dictionary *params,
                  struct CfgInterface *cfgIf);

typedef struct {
   uint32 keepOld;
   uint32 rotateSize;
   uint32 throttleThreshold;
   uint32 throttleBPS;
   Bool   useTimeStamp;
   Bool   useMilliseconds;
   Bool   useThreadName;
   Bool   useLevelDesignator;
   Bool   useOpID;
   Bool   append;
   Bool   syncAfterWrite;
   Bool   fastRotation;
} LogFileParameters;

Bool
Log_GetFileParameters(const LogOutput *output,
                      LogFileParameters *parms);

typedef void (LogCustomMsgFunc)(int level,
                                const char *msg);

LogOutput *
Log_NewCustomOutput(const char *instanceName,
                    LogCustomMsgFunc *msgFunc,
                    int minLogLevel);

typedef struct {
   uint8  level;
   uint32 group;
   Bool   additionalLine;
   size_t msgLen;
   char   timeStamp[64];
   char   threadName[32];
   char   opID[LOG_MAX_OPID_LENGTH + 1];  // Will be empty string on hosted products
} LogLineMetadata;

typedef void (LogCustomMsgFuncEx)(const LogLineMetadata * const metadata,
                                  const char *msg);

LogOutput *
Log_NewCustomOutputEx(const char *instanceName,
                      LogCustomMsgFuncEx *msgFunc,
                      int minLogLevel);

#if defined(VMX86_SERVER)
LogOutput *
Log_NewEsxKernelLogOutput(const char *appPrefix,
                          struct Dictionary *params,
                          struct CfgInterface *cfgIf);

LogOutput *
Log_NewCrxSyslogOutput(const char *appPrefix,
                       struct Dictionary *params,
                       struct CfgInterface *cfgIf);
#endif

Bool
Log_FreeOutput(LogOutput *toOutput);

Bool
Log_AddOutput(LogOutput *output);

Bool
Log_ReplaceOutput(LogOutput *fromOutput,
                  LogOutput *toOutput,
                  Bool copyOver);

int32
Log_SetOutputLevel(LogOutput *output,
                   int32 level);

/*
 * Structure contains all the pointers to where value can be updated.
 * Making VmxStatsInfo as a struct has its own advantage, such as updating
 * 'droppedChars' from the struct instead within LogFile.
 */

struct StatFileMinMax64;

typedef struct {
   uint64          *logMsgsDropped;    // Number of dropped messages
   uint64          *logBytesDropped;   // Number of drop bytes
   uint64          *logBytesLogged;    // Bytes logged

   struct StatsFileMinMax64 *logWriteMinMaxTime; // Min/max write time in US
   uint64          *logWriteAvgTime;   // Average time to write in US
} VmxStatsInfo;

Bool
Log_SetVmxStatsData(LogOutput *output,
                    VmxStatsInfo *vmxStats);

/*
 * The most common Log Facility client usage is via the "InitWith" functions.
 * These functions - not the "Int" versions - handle informing the Log
 * Facility of the ProductState (product description) via inline code. This is
 * done to avoid making the Log Facility depend on the ProductState library -
 * the product should have the dependency, not an underlying library.
 *
 * In complex cases, where an "InitWith" is not sufficient and Log_AddOutput
 * must be used directly, the client should call Log_SetProductInfo, passing
 * the appropriate parameters, so the log file header information will be
 * correct.
 */

void
Log_SetProductInfo(const char *appName,
                   const char *appVersion,
                   const char *buildNumber,
                   const char *compilationOption);

static INLINE void
Log_SetProductInfoSimple(void)
{
   Log_SetProductInfo(ProductState_GetName(),
                      ProductState_GetVersion(),
                      ProductState_GetBuildNumberString(),
                      ProductState_GetCompilationOption());
}

LogOutput *
Log_InitWithCustomInt(struct CfgInterface *cfgIf,
                      LogCustomMsgFunc *msgFunc,
                      int minLogLevel);


static INLINE LogOutput *
Log_InitWithCustom(struct CfgInterface *cfgIf,
                   LogCustomMsgFunc *msgFunc,
                   int minLogLevel)
{
   Log_SetProductInfoSimple();

   return Log_InitWithCustomInt(cfgIf, msgFunc, minLogLevel);
}

LogOutput *
Log_InitWithCustomIntEx(struct CfgInterface *cfgIf,
                        LogCustomMsgFuncEx *msgFunc,
                        int minLogLevel);


static INLINE LogOutput *
Log_InitWithCustomEx(struct CfgInterface *cfgIf,
                     LogCustomMsgFuncEx *msgFunc,
                     int minLogLevel)
{
   Log_SetProductInfoSimple();

   return Log_InitWithCustomIntEx(cfgIf, msgFunc, minLogLevel);
}

LogOutput *
Log_InitWithFileInt(const char *appPrefix,
                    struct Dictionary *dict,
                    struct CfgInterface *cfgIf,
                    Bool boundNumFiles);

static INLINE LogOutput *
Log_InitWithFile(const char *appPrefix,
                 struct Dictionary *dict,
                 struct CfgInterface *cfgIf,
                 Bool boundNumFiles)
{
   Log_SetProductInfoSimple();

   return Log_InitWithFileInt(appPrefix, dict, cfgIf, boundNumFiles);
}

LogOutput *
Log_InitWithFileSimpleInt(const char *appPrefix,
                          struct CfgInterface *cfgIf,
                          const char *fileName);

static INLINE LogOutput *
Log_InitWithFileSimple(const char *appPrefix,
                       const char *fileName)
{
   Log_SetProductInfoSimple();

   return Log_InitWithFileSimpleInt(appPrefix, Log_CfgInterface(), fileName);
}

LogOutput *
Log_InitWithSyslogInt(const char *appPrefix,
                      struct Dictionary *dict,
                      struct CfgInterface *cfgIf);

static INLINE LogOutput *
Log_InitWithSyslog(const char *appPrefix,
                   struct Dictionary *dict,
                   struct CfgInterface *cfgIf)
{
   Log_SetProductInfoSimple();

   return Log_InitWithSyslogInt(appPrefix, dict, cfgIf);
}

LogOutput *
Log_InitWithSyslogSimpleInt(const char *appPrefix,
                            struct CfgInterface *cfgIf,
                            const char *syslogID);

static INLINE LogOutput *
Log_InitWithSyslogSimple(const char *appPrefix,
                         const char *syslogID)
{
   Log_SetProductInfoSimple();

   return Log_InitWithSyslogSimpleInt(appPrefix, Log_CfgInterface(), syslogID);
}

LogOutput *
Log_InitWithStdioSimpleInt(const char *appPrefix,
                           struct CfgInterface *cfgIf,
                           const char *minLevel,
                           Bool withLinePrefix);

static INLINE LogOutput *
Log_InitWithStdioSimple(const char *appPrefix,
                        const char *minLevel,
                        Bool withLinePrefix)
{
   Log_SetProductInfoSimple();

   return Log_InitWithStdioSimpleInt(appPrefix, Log_CfgInterface(), minLevel,
                                     withLinePrefix);
}

void
Log_Exit(void);

Bool
Log_Outputting(void);

Bool
Log_IsEnabled(uint32 routing);

const char *
Log_GetFileName(void);

const char *
Log_GetOutputFileName(LogOutput *output);

void
Log_SkipLocking(Bool skipLocking);

void
Log_DisableThrottling(void);

uint32
Log_MaxLineLength(void);

size_t
Log_MakeTimeString(Bool millisec,
                   char *buf,
                   size_t max);

typedef Bool (LogMayDeleteFunc)(void *userData,
                                const char *fileName,
                                uint32 *pid);

Bool
Log_BoundNumFiles(const LogOutput *output,
                  LogMayDeleteFunc *mayDeleteFunc,
                  void *userData);

typedef struct {
#if defined(_WIN32)
   HANDLE handle;
#else
   int fd;
#endif
} LogFileObject;

Bool
Log_GetFileObject(const LogOutput *output,
                  LogFileObject *result);

/*
 * Assemble a line.
 */

void *
Log_BufBegin(void);

void
Log_BufAppend(void *acc,
              const char *fmt,
              ...) PRINTF_DECL(2, 3);

void
Log_BufEndLevel(void *acc,
                uint32 routing);


/*
 * Debugging
 */

void
Log_HexDump(const char *prefix,
            const void *data,
            size_t size);

void
Log_HexDumpLevel(uint32 routing,
                 const char *prefix,
                 const void *data,
                 size_t size);

void
Log_Time(VmTimeType *time,
         int count,
         const char *message);

void
Log_Histogram(uint32 n,
              uint32 histo[],
              int nbuckets,
              const char *message,
              int *count,
              int limit);

typedef Bool (GetOpId)(size_t maxStringLen,
                       char *opId);

void
Log_RegisterOpIdFunction(GetOpId *getOpIdFunc);

void
Log_LoadGroupFilters(const char *appPrefix,
                     struct CfgInterface *cfgIf);

long
Log_OffsetUtc(void);

/*
 * log throttling:
 *
 * throttleThreshold = start log throttling only after this many bytes have
 *                     been logged (allows initial vmx startup spew).
 *
 * throttleBPS = start throttling if more than this many bytes per second are
 *               logged. Continue throttling until rate drops below this value.
 *
 * bytesLogged = total bytes logged.
 *
 * logging rate =  (bytesLogged - lastBytesSample)/(curTime - lastSampleTime)
 */

typedef struct {
   uint64      throttleThreshold;
   uint64      bytesLogged;
   uint64      lastBytesSample;
   VmTimeType  lastTimeSample;
   uint32      throttleBPS;
   Bool        throttled;
} LogThrottleInfo;

Bool
Log_IsThrottled(LogThrottleInfo *info,
                size_t msgLen);

#endif /* !VMM */

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* VMWARE_LOG_H */

/*
 * Log Facility Message Group macros
 */

#if !defined(VMW_LOG_GROUP_LEVELS)
   #include "vm_basic_defs.h"
   #include "loglevel_userVars.h"

   #define LOGFACILITY_GROUPVAR(group) XCONC(_logFacilityGroup_, group)

   enum LogFacilityGroupValue {
      LOGLEVEL_USER(LOGFACILITY_GROUPVAR)
   };

   #define VMW_LOG_GROUP_LEVELS
#endif

/*
 * Legacy VMW_LOG_ROUTING macro
 *
 * Group name is "inherited" from the LOGLEVEL_MODULE define.
 */

#if defined(VMW_LOG_ROUTING)
   #undef VMW_LOG_ROUTING
#endif

#if defined(LOGLEVEL_MODULE)
   #define VMW_LOG_ROUTING(level) \
   (((LOGFACILITY_GROUPVAR(LOGLEVEL_MODULE) + 1) << VMW_LOG_LEVEL_BITS) | (level))
#else
   #define VMW_LOG_ROUTING(level) (level)
#endif

/*
 * VMW_LOG_ROUTING_EX macro
 *
 * Group name is specified in the macro.
 */

#if defined(VMW_LOG_ROUTING_EX)
   #undef VMW_LOG_ROUTING_EX
#endif

#define VMW_LOG_ROUTING_EX(name, level) \
        (((LOGFACILITY_GROUPVAR(name) + 1) << VMW_LOG_LEVEL_BITS) | (level))

   070701000000ED000081A4000000000000000000000001682255050000067A000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/logFixed.h /*********************************************************
 * Copyright (C) 2010-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _LOGFIXED_H_
#define _LOGFIXED_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 *  LogFixed_Base2 and LogFixed_Base10 provide their values expressed
 *  as a ration of two uint32 numbers with an accuracy of better than 1%.
 *
 * Reminder: A log, base x, of zero is undefined. These routines will assert
 * in development builds when a zero value is passed to them.
 */

void LogFixed_Base2(uint64 value,
                    uint32 *numerator,
                    uint32 *denominator);

void LogFixed_Base10(uint64 value,
                    uint32 *numerator,
                    uint32 *denominator);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif  // _LOGFIXED_H_
  070701000000EE000081A4000000000000000000000001682255050000048E000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/logToHost.h    /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * logToHost.h --
 *
 *    Log function prototypes to discretionarily direct log to the host or the
 *    guest.
 */

#ifndef __LOG_TO_HOST_H__
#define __LOG_TO_HOST_H__

void WarningToGuest(const char *fmt, ...);
void WarningToHost(const char *fmt, ...);

#endif /* __LOG_TO_HOST_H__ */
  070701000000EF000081A40000000000000000000000016822550500000E46000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/loglevel_defs.h    /*********************************************************
 * Copyright (C) 1998-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _LOGLEVEL_DEFS_H_
#define _LOGLEVEL_DEFS_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#ifndef LOGLEVEL_MODULE
#error "loglevel_defs.h must be included with LOGLEVEL_MODULE defined"
#endif

#ifndef LOGLEVEL_EXTENSION
#error "loglevel_defs.h must be included with LOGLEVEL_EXTENSION defined"
#endif

#include "vm_basic_types.h"
#include "vm_basic_defs.h"

#if defined __cplusplus
#define LOGLEVEL_EXTERN_C_BEGIN extern "C" {
#define LOGLEVEL_EXTERN_C_END }
#else
#define LOGLEVEL_EXTERN_C_BEGIN
#define LOGLEVEL_EXTERN_C_END
#endif

LOGLEVEL_EXTERN_C_BEGIN


/*
 * CPP variable name hacks
 */

#define LOGLEVEL_EXTOFFSET(ext) XCONC(_loglevel_offset_, ext)
#define LOGLEVEL_EXTNAME(ext) XSTR(ext)
#define LOGLEVEL_MODULEVAR(mod) XCONC(_loglevel_mod_, mod) 


/*
 * LogLevel declaration
 */

#define LOGLEVEL_EXTENSION_DECLARE(list) \
   LOGLEVEL_EXTERN_C_BEGIN \
   VMX86_EXTERN_DATA const int8 *logLevelPtr; \
   VMX86_EXTERN_DATA int LOGLEVEL_EXTOFFSET(LOGLEVEL_EXTENSION); \
   LOGLEVEL_EXTERN_C_END \
   enum { list(LOGLEVEL_MODULEVAR) }


#ifdef VMX86_LOG

/*
 * Cross extension
 */

int LogLevel_Set(const char *extension, const char *module, int val);

/*
 * Intra extension
 */

#define LOGLEVEL_BYNAME(_mod) \
        logLevelPtr[LOGLEVEL_EXTOFFSET(LOGLEVEL_EXTENSION) + \
                    LOGLEVEL_MODULEVAR(_mod)]

#define DOLOG_BYNAME(_mod, _min) \
        UNLIKELY(LOGLEVEL_BYNAME(_mod) >= (_min))

/*
 * Variadic macro wrinkle: C99 says "one or more arguments"; some compilers
 * (gcc+clang+msvc) support zero arguments, but differ in tolerating a trailing
 * comma. Solution: include format string in variadic arguments.
 *
 * C++2a introduces __VA_OPT__, which would allow this definition instead:
 * define LOG_BYNAME(_mod, _min, _fmt, ...) \
 *        (DOLOG_BYNAME(_mod, _min) ? Log(_fmt __VA_OPT__(,) __VA_ARGS__) \
 *                                  : (void) 0)
 * MSVC has always ignored a spurious trailing comma, so does not need this.
 */
#define LOG_BYNAME(_mod, _min, ...) \
        (DOLOG_BYNAME(_mod, _min) ? Log(__VA_ARGS__) : (void) 0)

/*
 * Default
 */

#define LOGLEVEL()     LOGLEVEL_BYNAME(LOGLEVEL_MODULE)
#define DOLOG(_min)    DOLOG_BYNAME(LOGLEVEL_MODULE, _min)
#define LOG(_min, ...) LOG_BYNAME(LOGLEVEL_MODULE, _min, __VA_ARGS__)

#else /* VMX86_LOG */

#define LOGLEVEL_BYNAME(_mod)           0
#define DOLOG_BYNAME(_mod, _min)        (FALSE)
#define LOG_BYNAME(_mod, _min, ...)

#define LOGLEVEL()      0
#define DOLOG(_min)     (FALSE)
#define LOG(_min, ...)


#endif /* VMX86_LOG */


#ifdef VMX86_DEVEL
   #define LOG_DEVEL(...) Log(__VA_ARGS__)
#else
   #define LOG_DEVEL(...)
#endif


LOGLEVEL_EXTERN_C_END

#endif /* _LOGLEVEL_DEFS_H_ */
  070701000000F0000081A400000000000000000000000168225505000004A4000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/loglevel_user.h    /*********************************************************
 * Copyright (c) 1998-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _LOGLEVEL_USER_H_
#define _LOGLEVEL_USER_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#define LOGLEVEL_EXTENSION user
#include "loglevel_defs.h"

#include "loglevel_userVars.h"

LOGLEVEL_EXTENSION_DECLARE(LOGLEVEL_USER);

#endif /* _LOGLEVEL_USER_H_ */
070701000000F1000081A400000000000000000000000168225505000029F7000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/include/loglevel_userVars.h    /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _LOGLEVEL_USER_VARS_H_
#define _LOGLEVEL_USER_VARS_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

/* KEEP IN SORTED ORDER! */

#define LOGLEVEL_USER(LOGLEVEL_VAR) \
   LOGLEVEL_VAR(9pfs), \
   LOGLEVEL_VAR(acpi), \
   LOGLEVEL_VAR(acpiGPE), \
   LOGLEVEL_VAR(ahci), \
   LOGLEVEL_VAR(aio), \
   LOGLEVEL_VAR(aioGeneric), \
   LOGLEVEL_VAR(aioHttp), \
   LOGLEVEL_VAR(aioKernel), \
   LOGLEVEL_VAR(aioMgr), \
   LOGLEVEL_VAR(aioWin32), \
   LOGLEVEL_VAR(aioWin32Completion), \
   LOGLEVEL_VAR(amdIommu), \
   LOGLEVEL_VAR(appstate), \
   LOGLEVEL_VAR(assignHw), \
   LOGLEVEL_VAR(asyncsocket), \
   LOGLEVEL_VAR(atapiCdrom), \
   LOGLEVEL_VAR(authenticode), \
   LOGLEVEL_VAR(automation), \
   LOGLEVEL_VAR(AVCapture), \
   LOGLEVEL_VAR(backdoor), \
   LOGLEVEL_VAR(barrier), \
   LOGLEVEL_VAR(battery), \
   LOGLEVEL_VAR(blit), /* lib/blit */ \
   LOGLEVEL_VAR(brtalk), \
   LOGLEVEL_VAR(buslogic), \
   LOGLEVEL_VAR(buslogicMdev), \
   LOGLEVEL_VAR(button), \
   LOGLEVEL_VAR(cdrom), \
   LOGLEVEL_VAR(checkpoint), \
   LOGLEVEL_VAR(checksum), \
   LOGLEVEL_VAR(chipset), \
   LOGLEVEL_VAR(cmos), \
   LOGLEVEL_VAR(cptOps), \
   LOGLEVEL_VAR(cpucount), \
   LOGLEVEL_VAR(CpuidInfo), \
   LOGLEVEL_VAR(crc32), \
   LOGLEVEL_VAR(crtbora),  /* apps/crtbora */ \
   LOGLEVEL_VAR(cui), \
   LOGLEVEL_VAR(dataCache), \
   LOGLEVEL_VAR(dataSetsMgr), \
   LOGLEVEL_VAR(dataSetsStore),\
   LOGLEVEL_VAR(device), \
   LOGLEVEL_VAR(deviceGroup), \
   LOGLEVEL_VAR(devicePowerOn), \
   LOGLEVEL_VAR(deviceSwap), \
   LOGLEVEL_VAR(deviceThread), \
   LOGLEVEL_VAR(dict), \
   LOGLEVEL_VAR(digestlib), \
   LOGLEVEL_VAR(directBoot), \
   LOGLEVEL_VAR(disk), \
   LOGLEVEL_VAR(disklib), \
   LOGLEVEL_VAR(diskVmnix), \
   LOGLEVEL_VAR(dma), \
   LOGLEVEL_VAR(dmg), \
   LOGLEVEL_VAR(dnd), \
   LOGLEVEL_VAR(docker), \
   LOGLEVEL_VAR(dui), \
   LOGLEVEL_VAR(duiDevices), \
   LOGLEVEL_VAR(duiLocalization), \
   LOGLEVEL_VAR(duiMKS), \
   LOGLEVEL_VAR(duiProxyApps), \
   LOGLEVEL_VAR(dumper), \
   LOGLEVEL_VAR(dvx), \
   LOGLEVEL_VAR(e1000), \
   LOGLEVEL_VAR(efinv), \
   LOGLEVEL_VAR(efivarstore), \
   LOGLEVEL_VAR(ehci), \
   LOGLEVEL_VAR(enableDetTimer), \
   LOGLEVEL_VAR(epd), \
   LOGLEVEL_VAR(extcfgdevice), \
   LOGLEVEL_VAR(fakeDma), \
   LOGLEVEL_VAR(filtlib), \
   LOGLEVEL_VAR(FiltLibTestLog), \
   LOGLEVEL_VAR(flashram), \
   LOGLEVEL_VAR(floppy), \
   LOGLEVEL_VAR(fsresx), \
   LOGLEVEL_VAR(ftConfig), /*lib/ftConfig */ \
   LOGLEVEL_VAR(ftcpt), \
   LOGLEVEL_VAR(grainTrack), \
   LOGLEVEL_VAR(grm), \
   LOGLEVEL_VAR(guestAppMonitor), \
   LOGLEVEL_VAR(guestInstall), \
   LOGLEVEL_VAR(guest_msg), \
   LOGLEVEL_VAR(guest_rpc), \
   LOGLEVEL_VAR(guestVars), \
   LOGLEVEL_VAR(gui), \
   LOGLEVEL_VAR(guiWin32), \
   LOGLEVEL_VAR(Heap), \
   LOGLEVEL_VAR(hbaCommon), \
   LOGLEVEL_VAR(hbr), \
   LOGLEVEL_VAR(hdaudio), \
   LOGLEVEL_VAR(hdaudio_alsa), \
   LOGLEVEL_VAR(hgfs), \
   LOGLEVEL_VAR(hgfsServer), \
   LOGLEVEL_VAR(hidQueue), \
   LOGLEVEL_VAR(hostctl), \
   LOGLEVEL_VAR(hostonly), \
   LOGLEVEL_VAR(hpet), \
   LOGLEVEL_VAR(http), \
   LOGLEVEL_VAR(ich7m), \
   LOGLEVEL_VAR(inputdevtap), \
   LOGLEVEL_VAR(ipc), \
   LOGLEVEL_VAR(ipcMgr), \
   LOGLEVEL_VAR(keyboard), \
   LOGLEVEL_VAR(keymap), \
   LOGLEVEL_VAR(keypersist), \
   LOGLEVEL_VAR(largepage), \
   LOGLEVEL_VAR(libconnect), \
   LOGLEVEL_VAR(license), \
   LOGLEVEL_VAR(llc), \
   LOGLEVEL_VAR(lsilogic), \
   LOGLEVEL_VAR(lwdFilter), \
   LOGLEVEL_VAR(macbw), \
   LOGLEVEL_VAR(macfi), \
   LOGLEVEL_VAR(macfilter), \
   LOGLEVEL_VAR(machPoll), \
   LOGLEVEL_VAR(maclatency), \
   LOGLEVEL_VAR(main), \
   LOGLEVEL_VAR(mainMem), \
   LOGLEVEL_VAR(mainMemReplayCheck), \
   LOGLEVEL_VAR(masReceipt), /* lib/masReceipt */ \
   LOGLEVEL_VAR(memoryHotplug), \
   LOGLEVEL_VAR(memspace), \
   LOGLEVEL_VAR(migrate), \
   LOGLEVEL_VAR(migrateVM), \
   LOGLEVEL_VAR(mirror), \
   LOGLEVEL_VAR(mks), \
   LOGLEVEL_VAR(mksBasicOps), \
   LOGLEVEL_VAR(mksClient), \
   LOGLEVEL_VAR(mksControl), \
   LOGLEVEL_VAR(mksCursorPosition), \
   LOGLEVEL_VAR(mksDX11Window), \
   LOGLEVEL_VAR(mksDX11Renderer), \
   LOGLEVEL_VAR(mksDX11Basic), \
   LOGLEVEL_VAR(mksDX11ResourceView), \
   LOGLEVEL_VAR(mksDX11ShimOps), \
   LOGLEVEL_VAR(mksDX12Renderer), \
   LOGLEVEL_VAR(mksFrame), \
   LOGLEVEL_VAR(mksGLBasic), \
   LOGLEVEL_VAR(mksGLContextMux), \
   LOGLEVEL_VAR(mksGLDraw), \
   LOGLEVEL_VAR(mksGLFBO), \
   LOGLEVEL_VAR(mksGLManager), \
   LOGLEVEL_VAR(mksGLQuery), \
   LOGLEVEL_VAR(mksGLShader), \
   LOGLEVEL_VAR(mksGLState), \
   LOGLEVEL_VAR(mksGLTextureView), \
   LOGLEVEL_VAR(mksGLWindow), \
   LOGLEVEL_VAR(mksHostCursor), \
   LOGLEVEL_VAR(mksInput), \
   LOGLEVEL_VAR(mksKeyboard), \
   LOGLEVEL_VAR(mksMouse), \
   LOGLEVEL_VAR(mksMTLRenderer), \
   LOGLEVEL_VAR(mksRenderOps), \
   LOGLEVEL_VAR(mksServer), \
   LOGLEVEL_VAR(mksSWB), \
   LOGLEVEL_VAR(mksVulkanRenderer), \
   LOGLEVEL_VAR(mksVulkanCmds), \
   LOGLEVEL_VAR(mksWinBSOD), \
   LOGLEVEL_VAR(mor), \
   LOGLEVEL_VAR(mstat), \
   LOGLEVEL_VAR(msvga), \
   LOGLEVEL_VAR(mvnc), \
   LOGLEVEL_VAR(namespaceDb), \
   LOGLEVEL_VAR(namespaceMgr), \
   LOGLEVEL_VAR(netPkt), \
   LOGLEVEL_VAR(numa), \
   LOGLEVEL_VAR(numaHost), \
   LOGLEVEL_VAR(nvdimm), \
   LOGLEVEL_VAR(nvme), \
   LOGLEVEL_VAR(nvramMgr), \
   LOGLEVEL_VAR(objc), /* lib/objc */ \
   LOGLEVEL_VAR(objlib), \
   LOGLEVEL_VAR(oemDevice), \
   LOGLEVEL_VAR(opNotification), \
   LOGLEVEL_VAR(oprom), \
   LOGLEVEL_VAR(ovhdmem), \
   LOGLEVEL_VAR(parallel), \
   LOGLEVEL_VAR(passthrough), \
   LOGLEVEL_VAR(pci), \
   LOGLEVEL_VAR(pcibridge), \
   LOGLEVEL_VAR(pci_e1000), \
   LOGLEVEL_VAR(pci_ehci), \
   LOGLEVEL_VAR(pci_hdaudio), \
   LOGLEVEL_VAR(pci_hyper), \
   LOGLEVEL_VAR(pciPassthru), \
   LOGLEVEL_VAR(pciPlugin), \
   LOGLEVEL_VAR(pci_scsi), \
   LOGLEVEL_VAR(pci_svga), \
   LOGLEVEL_VAR(pci_uhci), \
   LOGLEVEL_VAR(pci_vide), \
   LOGLEVEL_VAR(pci_vlance), \
   LOGLEVEL_VAR(pci_vmci), \
   LOGLEVEL_VAR(pci_vmxnet3), \
   LOGLEVEL_VAR(pci_xhci), \
   LOGLEVEL_VAR(pmemobj), \
   LOGLEVEL_VAR(policy), \
   LOGLEVEL_VAR(poll), \
   LOGLEVEL_VAR(precisionclock), \
   LOGLEVEL_VAR(promotedisk), \
   LOGLEVEL_VAR(pvnvram), \
   LOGLEVEL_VAR(pvscsi), \
   LOGLEVEL_VAR(qat), \
   LOGLEVEL_VAR(remoteDevice), \
   LOGLEVEL_VAR(replayVMX), \
   LOGLEVEL_VAR(sbx), \
   LOGLEVEL_VAR(scsi), \
   LOGLEVEL_VAR(secureBoot), \
   LOGLEVEL_VAR(serial), \
   LOGLEVEL_VAR(serviceImpl), /* lib/serviceImpl */ \
   LOGLEVEL_VAR(serviceUser), /* lib/serviceUser */ \
   LOGLEVEL_VAR(sg), /* lib/sg */ \
   LOGLEVEL_VAR(sgx), \
   LOGLEVEL_VAR(sgxmpa), \
   LOGLEVEL_VAR(sgxRegistrationTool), \
   LOGLEVEL_VAR(shader), \
   LOGLEVEL_VAR(sharedFolderMgr),  /* mks/remote/vdpFolderSharedMgrVmdb */ \
   LOGLEVEL_VAR(shim3D), \
   LOGLEVEL_VAR(slotfs), \
   LOGLEVEL_VAR(smbios), \
   LOGLEVEL_VAR(smc), \
   LOGLEVEL_VAR(smram), \
   LOGLEVEL_VAR(snapshot), \
   LOGLEVEL_VAR(sound), \
   LOGLEVEL_VAR(sparseChecker), \
   LOGLEVEL_VAR(ssl), \
   LOGLEVEL_VAR(state3d), \
   LOGLEVEL_VAR(stats), \
   LOGLEVEL_VAR(svga), \
   LOGLEVEL_VAR(svgadevtap), \
   LOGLEVEL_VAR(svga_rect), \
   LOGLEVEL_VAR(syncWaitQ), \
   LOGLEVEL_VAR(tarReader),\
   LOGLEVEL_VAR(timer), \
   LOGLEVEL_VAR(tools), \
   LOGLEVEL_VAR(toolsIso), \
   LOGLEVEL_VAR(toolsversion), \
   LOGLEVEL_VAR(tpm2emu), \
   LOGLEVEL_VAR(tpm2Verification), \
   LOGLEVEL_VAR(txt), \
   LOGLEVEL_VAR(udpfec),    /* lib/udpfec */ \
   LOGLEVEL_VAR(uhci), \
   LOGLEVEL_VAR(undopoint), \
   LOGLEVEL_VAR(unityMsg),  /* mks/remote/vdpUnityVmdb */ \
   LOGLEVEL_VAR(upitbe), \
   LOGLEVEL_VAR(upitd), \
   LOGLEVEL_VAR(usb), \
   LOGLEVEL_VAR(usb_xhci), \
   LOGLEVEL_VAR(util), \
   LOGLEVEL_VAR(uwt), /* lib/unityWindowTracker */ \
   LOGLEVEL_VAR(vaBasicOps), \
   LOGLEVEL_VAR(vcpuhotplug), \
   LOGLEVEL_VAR(vcpuNUMA), \
   LOGLEVEL_VAR(vdfs), \
   LOGLEVEL_VAR(vdfs_9p), \
   LOGLEVEL_VAR(vdpPlugin), \
   LOGLEVEL_VAR(vdtiPciCfgSpc), \
   LOGLEVEL_VAR(vflash), \
   LOGLEVEL_VAR(vg), \
   LOGLEVEL_VAR(vga), \
   LOGLEVEL_VAR(vide), \
   LOGLEVEL_VAR(viewClient), \
   LOGLEVEL_VAR(vigor), \
   LOGLEVEL_VAR(viommu), \
   LOGLEVEL_VAR(vlance), \
   LOGLEVEL_VAR(vmcf), \
   LOGLEVEL_VAR(vmci), \
   LOGLEVEL_VAR(vmgenc), \
   LOGLEVEL_VAR(vmGL), \
   LOGLEVEL_VAR(vmhs), \
   LOGLEVEL_VAR(vmIPC), \
   LOGLEVEL_VAR(vmkcfg), \
   LOGLEVEL_VAR(vmkEvent), \
   LOGLEVEL_VAR(vmkmgmtlib), \
   LOGLEVEL_VAR(vmLock), \
   LOGLEVEL_VAR(vmmouse), \
   LOGLEVEL_VAR(vmname),  /* lib/vmname */ \
   LOGLEVEL_VAR(vmnetBridge), \
   LOGLEVEL_VAR(vmOvhd), \
   LOGLEVEL_VAR(vmUpsellController), \
   LOGLEVEL_VAR(vmva), \
   LOGLEVEL_VAR(vmWindowController), \
   LOGLEVEL_VAR(vmxnet), \
   LOGLEVEL_VAR(vmxnet3), \
   LOGLEVEL_VAR(vmxvmdbCallbacks), \
   LOGLEVEL_VAR(vncBlit),   \
   LOGLEVEL_VAR(vncDecode), \
   LOGLEVEL_VAR(vncEncode), \
   LOGLEVEL_VAR(vncRegEnc), \
   LOGLEVEL_VAR(vncServer), \
   LOGLEVEL_VAR(vncServerOS), \
   LOGLEVEL_VAR(vnet), \
   LOGLEVEL_VAR(vprobe), \
   LOGLEVEL_VAR(VProbeClient), \
   LOGLEVEL_VAR(vrdma), \
   LOGLEVEL_VAR(vsanobj), \
   LOGLEVEL_VAR(vsock), \
   LOGLEVEL_VAR(vsockProxy), \
   LOGLEVEL_VAR(vthread), \
   LOGLEVEL_VAR(vtpm), \
   LOGLEVEL_VAR(vui), \
   LOGLEVEL_VAR(vusbaudio), \
   LOGLEVEL_VAR(vusbccid), \
   LOGLEVEL_VAR(vusbhid), \
   LOGLEVEL_VAR(vusbkeyboard), \
   LOGLEVEL_VAR(vusbmouse), \
   LOGLEVEL_VAR(vusbrng),\
   LOGLEVEL_VAR(vusbtablet), \
   LOGLEVEL_VAR(vusbvideo),\
   LOGLEVEL_VAR(vvolbe), \
   LOGLEVEL_VAR(vvtd), \
   LOGLEVEL_VAR(vwdt), \
   LOGLEVEL_VAR(wifi), /* macWireless and wpa_supplicant */ \
   LOGLEVEL_VAR(win32util), \
   LOGLEVEL_VAR(worker), \
   LOGLEVEL_VAR(xled), \
   LOGLEVEL_VAR(xpmode)

   /* end of list */

#endif /* _LOGLEVEL_USER_VARS_H_ */
 070701000000F2000081A400000000000000000000000168225505000025EB000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/memaligned.h   /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * memaligned.h --
 *
 *    misc util functions
 */

#ifndef _MEMALIGNED_H_
#define _MEMALIGNED_H_

#ifdef __linux__
#include <malloc.h>
#else
#include <stdlib.h>
#ifdef __FreeBSD__
#include <unistd.h>
#endif
#endif
#include "vmware.h"

#if defined(__cplusplus)
extern "C" {
#endif

#if defined __APPLE__ && !vm_x86_64
/*
 * Bug 471584: Mac OS X 10.6's valloc() implementation for 32-bit
 * processes can exhaust our process's memory space.
 *
 * Work around this by using our own simple page-aligned memory
 * allocation implementation based on malloc() for 32-bit processes.
 */
#define MEMALIGNED_USE_INTERNAL_IMPL 1
#endif


#ifdef MEMALIGNED_USE_INTERNAL_IMPL

/*
 *-----------------------------------------------------------------------------
 *
 * AlignedMallocImpl --
 *
 *      Internal implementation of page-aligned memory for operating systems
 *      that lack a working page-aligned allocation function.
 *
 *      Resulting pointer needs to be freed with AlignedFreeImpl.
 *
 * Result:
 *      A pointer.  NULL on out of memory condition.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void *
AlignedMallocImpl(size_t size) // IN
{
   size_t paddedSize;
   void **buf;
   void **alignedResult;

#undef PAGE_MASK
#define PAGE_MASK (PAGE_SIZE - 1)
#define PAGE_ROUND_DOWN(_value) ((uintptr_t)(_value) & ~PAGE_MASK)
#define PAGE_ROUND_UP(_value) PAGE_ROUND_DOWN((uintptr_t)(_value) + PAGE_MASK)

   /*
    * This implementation allocates PAGE_SIZE extra bytes with
    * malloc() to ensure the buffer spans a page-aligned memory
    * address, which we return.  (We could use PAGE_SIZE - 1 to save a
    * byte if we ensured 'size' was non-zero.)
    *
    * After padding, we allocate an extra pointer to hold the original
    * pointer returned by malloc() (stored immediately preceding the
    * page-aligned address).  We free this in AlignedFreeImpl().
    *
    * Finally, we allocate enough space to hold 'size' bytes.
    */
   paddedSize = PAGE_SIZE + sizeof *buf + size;

   // Check for overflow.
   if (paddedSize < size) {
      return NULL;
   }

   buf = (void **)malloc(paddedSize);
   if (!buf) {
      return NULL;
   }

   alignedResult = (void **)PAGE_ROUND_UP(buf + 1);
   *(alignedResult - 1) = buf;

   return alignedResult;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AlignedReallocImpl --
 *
 *      Internal implementation of page-aligned memory for operating systems
 *      that lack a working page-aligned allocation function.
 *
 *      Resulting pointer needs to be freed with AlignedFreeImpl.
 *
 * Result:
 *      A pointer.  NULL on out of memory condition.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void *
AlignedReallocImpl(void *oldbuf,    // IN
                   size_t newsize)  // IN
{
   size_t paddedSize;
   void **buf;
   void **alignedResult;
   void *oldptr = NULL;

   if (oldbuf) {
      oldptr = (*((void **)oldbuf - 1));
   }

   paddedSize = PAGE_SIZE + sizeof *buf + newsize;

   // Check for overflow.
   if (paddedSize < newsize) {
      return NULL;
   }

   buf = (void **)realloc(oldptr, paddedSize);
   if (!buf) {
      return NULL;
   }

   alignedResult = (void **)PAGE_ROUND_UP(buf + 1);
   *(alignedResult - 1) = buf;

   return alignedResult;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AlignedFreeImpl --
 *
 *      Internal implementation to free a page-aligned buffer allocated
 *      with AlignedMallocImpl.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
AlignedFreeImpl(void *buf) // IN
{
   if (!buf) {
      return;
   }

   free(*((void **)buf - 1));
}

#endif // MEMALIGNED_USE_INTERNAL_IMPL


/*
 *---------------------------------------------------------------------------
 *
 * Aligned_UnsafeMalloc --
 *
 *      Alloc a chunk of memory aligned on a page boundary. Resulting pointer
 *      needs to be freed with Aligned_Free.
 *
 * Result:
 *      A pointer.  NULL on out of memory condition.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

static INLINE void*
Aligned_UnsafeMalloc(size_t size) // IN
{
   void *buf;
#if defined MEMALIGNED_USE_INTERNAL_IMPL
   buf = AlignedMallocImpl(size);
#elif defined _WIN32
   buf = _aligned_malloc(size, PAGE_SIZE);
#elif __linux__
   buf = memalign(PAGE_SIZE, size);
#else // Apple, BSD, Solaris (tools)
   buf = valloc(size); 
#endif
   ASSERT(((uintptr_t)buf % PAGE_SIZE) == 0);

   return buf;
}


/*
 *---------------------------------------------------------------------------
 *
 * Aligned_Malloc --
 *
 *      Alloc a chunk of memory aligned on a page boundary. Resulting pointer
 *      needs to be freed with Aligned_Free.  You should never use this
 *      function.  Especially if size was derived from guest provided data.
 *
 * Result:
 *      A pointer.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

static INLINE void*
Aligned_Malloc(size_t size) // IN
{
   void *buf;

   buf = Aligned_UnsafeMalloc(size);
   VERIFY(buf);
   return buf;
}


/*
 *---------------------------------------------------------------------------
 *
 * Aligned_Calloc --
 *
 *      Alloc a chunk of memory aligned on a page boundary. Resulting pointer
 *      needs to be freed with Aligned_Free.
 *
 * Result:
 *      A pointer.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

static INLINE void*
Aligned_Calloc(size_t nmemb, // IN
               size_t size)  // IN
{
   void *buf = Aligned_Malloc(nmemb * size);
   VERIFY(buf);
   memset(buf, 0, nmemb * size);
   return buf;
}


/*
 *---------------------------------------------------------------------------
 *
 * Aligned_Free --
 *
 *      Free a chunk of memory allocated using the 2 functions above.
 *
 * Result:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

static INLINE void
Aligned_Free(void *buf)  // IN
{
#if defined MEMALIGNED_USE_INTERNAL_IMPL
   AlignedFreeImpl(buf);
#elif defined _WIN32
   _aligned_free(buf);
#else
   free(buf);
#endif
}


/*
 *---------------------------------------------------------------------------
 *
 * Aligned_UnsafeRealloc --
 *
 *      This function is not implemented because it cannot be done safely and
 *      portably.  See https://reviewboard.eng.vmware.com/r/284303/ for
 *      discussion.
 *
 *---------------------------------------------------------------------------
 */


/*
 *---------------------------------------------------------------------------
 *
 * Aligned_Realloc --
 *
 *      Realloc a chunk of memory aligned on a page boundary, potentially
 *      copying the previous data to a new buffer if necessary.  Resulting
 *      pointer needs to be freed with Aligned_Free.  You should never use this
 *      function.  Especially if size was derived from guest provided data.
 *
 * Result:
 *      A pointer.
 *
 * Side effects:
 *      Old buf may be freed.
 *
 *---------------------------------------------------------------------------
 */

static INLINE void*
Aligned_Realloc(void *buf,   // IN
                size_t size) // IN
{
#if defined MEMALIGNED_USE_INTERNAL_IMPL
   return AlignedReallocImpl(buf, size);
#elif defined _WIN32
   return _aligned_realloc(buf, size, PAGE_SIZE);
#else
   /*
    * Some valloc(3) manpages claim that realloc(3) on a buffer allocated by
    * valloc() will return an aligned buffer.  If so, we have a fast path;
    * simply realloc, validate the alignment, and return.  For realloc()s that
    * do not maintain the alignment (such as glibc 2.13 x64 for allocations of
    * 16 pages or less) then we fall back to a slowpath and copy the data.
    * Note that we can't avoid the realloc overhead in this case: on entry to
    * Aligned_Realloc we have no way to find out how big the source buffer is!
    * Only after the realloc do we know a safe range to copy.  We may copy more
    * data than necessary -- consider the case of resizing from one page to
    * 100 pages -- but that is safe, just slow.
    */
   buf = realloc(buf, size);
   if (((uintptr_t)buf % PAGE_SIZE) != 0) {
      void *newbuf;

      newbuf = Aligned_UnsafeMalloc(size);
      VERIFY(newbuf);
      memcpy(newbuf, buf, size);
      free(buf);
      return newbuf;
   }
   return buf;
#endif
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
 070701000000F3000081A400000000000000000000000168225505000009EB000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/message.h  /*********************************************************
 * Copyright (C) 1999-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * message.h --
 *
 *    Second layer of the internal communication channel between guest
 *    applications and vmware
 */

#ifndef __MESSAGE_H__
#   define __MESSAGE_H__

#include "vm_basic_types.h"

#ifdef __cplusplus
extern "C" {
#endif


/* The channel object */
typedef struct Message_Channel {
   /* Identifier */
   uint16 id;

   /* Reception buffer */
   /*  Data */
   unsigned char *in;
   /*  Allocated size */
   size_t inAlloc;
   Bool inPreallocated;

   /* The cookie */
   uint32 cookieHigh;
   uint32 cookieLow;
} Message_Channel;

Bool Message_OpenAllocated(uint32 proto, Message_Channel *chan,
                           char *receiveBuffer, size_t receiveBufferSize);
Message_Channel* Message_Open(uint32 proto);
Bool Message_Send(Message_Channel *chan, const unsigned char *buf,
                  size_t bufSize);
Bool Message_Receive(Message_Channel *chan, unsigned char **buf,
                     size_t *bufSize);
Bool Message_CloseAllocated(Message_Channel *chan);
Bool Message_Close(Message_Channel *chan);

#ifdef __cplusplus
}
#endif

#endif /* __MESSAGE_H__ */
 070701000000F4000081A40000000000000000000000016822550500001D5B000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/mntinfo.h  /*********************************************************
 * Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * mntinfo.h --
 *
 *    Macros to abstract differences between structures and functions used in
 *    accessing information about mounted file systems.
 *
 */


#ifndef __MNTINFO_H__
#define __MNTINFO_H__

#ifdef sun
# include <sys/mnttab.h>
# include <libgen.h>
# include <limits.h>
#elif defined(__linux__)
# include <mntent.h>
#elif defined(__FreeBSD__)
# include <sys/mount.h>
#endif
#include "posix.h"

/*
 *----------------------------------------------------------------------------
 *
 * DECLARE_MNTINFO, MNTINFO
 * OPEN_MNTFILE, GETNEXT_MNTINFO, CLOSE_MNTFILE,
 * MNTINFO_NAME, MNTINFO_FSTYPE, MNTINFO_MNTPT --
 *
 *    Cross-platform macros for accessing information about the mounted file
 *    systems.  This is necessary since the interfaces for getmntent(3) are
 *    slightly different on Linux and Solaris.
 *
 *    DECLARE_MNTINFO() is used to declare the variable used when invoking
 *    GETNEXT_MNTINFO().  MNTINFO is the type that can be used when passing
 *    between functions.
 *
 *    OPEN_MNTFILE() and CLOSE_MNTFILE() must be called before and after
 *    a series of GETNEXT_MNTINFO() calls, respectively.  GETNEXT_MNTINFO() is
 *    called successively to retrieve information about the next mounted file
 *    system.
 *
 *    MNTINFO_NAME, MNTINFO_FSTYPE, and MNTINFO_MNTPT retrieve the name, file
 *    system type, and mount point of the provided MNTINFO, respectively.
 *
 *    MNTFILE is a string with the name of the file containing mount
 *    information.
 *
 * Results:
 *    OPEN_MNTFILE:    MNTHANDLE on success, NULL on failure
 *    GETNEXT_MNTINFO: on success, TRUE and mnt is filled with file system's
 *                     information; FALSE when no mounts left or on failure
 *    CLOSE_MNTFILE:   TRUE on success, FALSE on failure
 *
 *    MNTINFO_NAME:    mount's name on success, NULL on failure
 *    MNTINFO_FSTYPE:  mount's file system type on success, NULL on failure
 *    MNTINFO_MNTPT:   mount's mount point on success, NULL on failure
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#ifdef sun
# define MNTFILE                        MNTTAB
# define MNTHANDLE                      FILE *
# define MNTINFO                        struct mnttab
# define DECLARE_MNTINFO(name)          struct mnttab __ ## name; \
                                        struct mnttab *name = &__ ## name
# define OPEN_MNTFILE(mode)             Posix_Fopen(MNTFILE, mode)
# define GETNEXT_MNTINFO(fp, mnt)       (Posix_Getmntent(fp, mnt) == 0)
# define CLOSE_MNTFILE(fp)              (fclose(fp) == 0)
# define MNTINFO_NAME(mnt)              mnt->mnt_special
# define MNTINFO_FSTYPE(mnt)            mnt->mnt_fstype
# define MNTINFO_MNTPT(mnt)             mnt->mnt_mountp
# define MNTINFO_MNT_IS_RO(mnt)         (hasmntopt((mnt), "rw") == NULL)
#elif defined(__linux__)
# define MNTFILE                        MOUNTED
# define MNTHANDLE                      FILE *
# define MNTINFO                        struct mntent
# define DECLARE_MNTINFO(name)          struct mntent *name
# define OPEN_MNTFILE(mode)             Posix_Setmntent(MNTFILE, mode)
# define GETNEXT_MNTINFO(fp, mnt)       ((mnt = Posix_Getmntent(fp)) != NULL)
# define CLOSE_MNTFILE(fp)              (endmntent(fp) == 1)
# define MNTINFO_NAME(mnt)              mnt->mnt_fsname
# define MNTINFO_FSTYPE(mnt)            mnt->mnt_type
# define MNTINFO_MNTPT(mnt)             mnt->mnt_dir
# define MNTINFO_MNT_IS_RO(mnt)         (hasmntopt((mnt), "rw") == NULL)
#elif defined(__FreeBSD__) || defined(__APPLE__)
struct mntHandle {
   struct statfs *mountPoints;  // array of mountpoints per getmntinfo(3)
   int numMountPoints;          // number of elements in mntArray
   int mountIndex;              // current location within mountPoints array
};
# define MNTFILE                        _PATH_FSTAB
# define MNTHANDLE                      struct mntHandle *
# define MNTINFO                        struct statfs
# define DECLARE_MNTINFO(name)          struct statfs __ ## name; \
                                        struct statfs *name = &__ ## name

# define OPEN_MNTFILE(mode)                                             \
({                                                                      \
   MNTHANDLE mntHandle;                                                 \
   mntHandle = malloc(sizeof *mntHandle);                               \
   if (mntHandle != NULL) {                                             \
      mntHandle->numMountPoints = getmntinfo(&mntHandle->mountPoints,   \
                                             MNT_NOWAIT);               \
      mntHandle->mountIndex = 0;                                        \
   }                                                                    \
   mntHandle;                                                           \
})

# define GETNEXT_MNTINFO(mntHandle, mnt)                                \
({                                                                      \
   /* Avoid multiple evaluations/expansions. */                         \
   MNTHANDLE thisHandle = (mntHandle);                                  \
   MNTINFO *thisMnt = (mnt);                                            \
   Bool boolVal = FALSE;                                                \
   ASSERT(thisHandle);                                                  \
   if (thisHandle->mountIndex < thisHandle->numMountPoints) {           \
      memcpy(thisMnt,                                                   \
             &thisHandle->mountPoints[thisHandle->mountIndex],          \
             sizeof *thisMnt);                                          \
      ++thisHandle->mountIndex;                                         \
      boolVal = TRUE;                                                   \
   }                                                                    \
   boolVal;                                                             \
})

# define CLOSE_MNTFILE(mntHandle)                                       \
({                                                                      \
   free(mntHandle);                                                     \
   TRUE;                                                                \
})
# define MNTINFO_NAME(mnt)              mnt->f_mntfromname
# define MNTINFO_FSTYPE(mnt)            mnt->f_fstypename
# define MNTINFO_MNTPT(mnt)             mnt->f_mntonname
# define MNTINFO_MNT_IS_RO(mnt)         ((mnt)->f_flags & MNT_RDONLY)
#else
# error "Define mount information macros for your OS type"
#endif

#endif /* __MNTINFO_H__ */
 070701000000F5000081A40000000000000000000000016822550500001F8A000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/msg.h  /*********************************************************
 * Copyright (C) 1998-2017,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * msg.h  --
 *
 *  user interaction through (non-modal) messages and (modal) dialogs
 */

#ifndef _MSG_H_
#define _MSG_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include <string.h>
#include <stdarg.h>
#include "err.h"
#include "vm_basic_types.h"
#include "msgid.h"
#include "msgfmt.h"
#include "msgList.h"
#if defined VMX86_SERVER && !defined VMCORE
#include "voblib.h"
#endif

#if defined(__cplusplus)
extern "C" {
#endif


#define INVALID_MSG_CODE (-1)

/*
 * Data structures, types, and constants
 */

typedef struct Msg_String {
   const char *idFmt;
} Msg_String;
   
typedef enum MsgSeverity {
   MSG_INFO,
   MSG_INFO_TIMEOUT,
   MSG_WARNING,
   MSG_ERROR,
   MSG_CONFIG_EDITOR,
   MSG_WEB_LINK_GET_LICENSE_ERROR,
   MSG_WEB_LINK_EXTEND_LICENSE_ERROR,
   MSG_WEB_LINK_EXTEND_LICENSE_INFO,
   MSG_WEB_LINK_HOME_PAGE_INFO,
   MSG_NUM_SEVERITIES
} MsgSeverity;

typedef enum HintResult {
   HINT_CONTINUE,
   HINT_CANCEL,
   HINT_NOT_SHOWN
} HintResult;

typedef enum HintOptions {
   HINT_OK,
   HINT_OKCANCEL
} HintOptions;

typedef struct MsgCallback {
   void (*post)(MsgSeverity severity, const char *msgID, const char *message);
   int (*question)(char const * const *names, int defaultAnswer,
                   const char *msgID, const char *message);
   int (*progress)(const char *msgID, const char *message,
		   int percent, Bool cancelButton);
   HintResult (*hint)(HintOptions options,
		      const char *msgID, const char *message);

   void *(*lazyProgressStart)(const char *msgID,
                              const char *message,
                              Bool allowCancel);
   Bool  (*lazyProgress)(void *handle,
                         const char *msgID,
                         const char *message,
                         Bool allowCancel,
                         int percent);
   void  (*lazyProgressEnd)(void *handle);

   void (*postList)(MsgSeverity severity, MsgList *messages);
   int (*questionList)(const Msg_String *buttons, int defaultAnswer,
                       MsgList *messages);
   int (*progressList)(MsgList *messages, int percent, Bool cancelButton);
   HintResult (*hintList)(HintOptions options, MsgList *messages);
   void *(*lazyProgressStartList)(MsgList *messages);
   void (*forceUnblock)(void);
   void (*msgPostHook)(int severity, const MsgList *msgs);
} MsgCallback;

#define MSG_QUESTION_MAX_BUTTONS   10

#define MSG_PROGRESS_START (-1)
#define MSG_PROGRESS_STOP 101

VMX86_EXTERN_DATA Msg_String const Msg_YesNoButtons[];
VMX86_EXTERN_DATA Msg_String const Msg_OKButtons[];
VMX86_EXTERN_DATA Msg_String const Msg_RetryCancelButtons[];
VMX86_EXTERN_DATA Msg_String const Msg_OKCancelButtons[];
VMX86_EXTERN_DATA Msg_String const Msg_RetryAbortButtons[];

VMX86_EXTERN_DATA Msg_String const Msg_Severities[];


/*
 * Functions
 */

void Msg_Append(const char *idFmt,
                ...) PRINTF_DECL(1, 2);
void Msg_VAppend(const char *idFmt, va_list args);
void Msg_AppendStr(const char *id);
void Msg_AppendMsgList(const MsgList *msgs);

static INLINE void
Msg_AppendVobContext(void)
{
#if defined VMX86_SERVER && !defined VMCORE
   /* inline to avoid lib/vob dependency unless necessary */
   MsgList *msgs = NULL;

   VobLib_CurrentContextMsgAppend(&msgs);
   Msg_AppendMsgList(msgs);

   MsgList_Free(msgs);
#endif
}

void Msg_Post(MsgSeverity severity,
              const char *idFmt,
              ...) PRINTF_DECL(2, 3);
void Msg_PostMsgList(MsgSeverity severity,
                     const MsgList *msgs);

char *Msg_Format(const char *idFmt,
                 ...) PRINTF_DECL(1, 2);
char *Msg_VFormat(const char *idFmt,
                  va_list arguments);
unsigned Msg_Question(Msg_String const *buttons,
                      int defaultAnswer,
                      const char *idFmt, ...) PRINTF_DECL(3, 4);

/*
 * Unfortunately, gcc warns about both NULL and "" being passed as format
 * strings, and callers of Msg_Progress() like to do that.  So I'm removing
 * the PRINTF_DECL() for now.  --Jeremy.
 */
int Msg_Progress(int percentDone, Bool cancelButton, const char *idFmt,
                 ...);
int Msg_ProgressScaled(int percentDone, int opsDone, int opsTotal,
                       Bool cancelButton);

void *Msg_LazyProgressStart(Bool allowCancel,
                            const char *idFmt,
                            ...) PRINTF_DECL(2, 3);

Bool Msg_LazyProgress(void *handle,
                      Bool allowCancel,
                      int percent,
                      const char *idFmt,
                      ...) PRINTF_DECL(4, 5);

void Msg_LazyProgressEnd(void *handle);


HintResult Msg_Hint(Bool defaultShow,
                    HintOptions options,
                    const char *idFmt,
                    ...) PRINTF_DECL(3, 4);

HintResult Msg_HintMsgList(Bool defaultShow, HintOptions options,
                           MsgList *msg);
int Msg_CompareAnswer(Msg_String const *buttons, unsigned answer,
                      const char *string);
Bool Msg_IsAnswered(Msg_String const *buttons, int defaultAnswer,
                    const char *id, unsigned int *reply);
char *Msg_GetString(const char *idString);
char *Msg_GetStringSafe(const char *idString);
char *Msg_GetPlainButtonText(const char *idString);
char *Msg_GetLocale(void);
void Msg_SetLocale(const char *locale, const char *binaryName);
void Msg_SetLocaleEx(const char *locale, const char *binaryName,
                     const char *baseDirPath);
char *Msg_FormatFloat(double value, unsigned int precision);
char *Msg_FormatSizeInBytes(uint64 size);
Bool Msg_LoadMessageFile(const char *locale, const char *fileName);
void Msg_ForceUnblock(void);


/*
 * Message buffer management
 */

const char *Msg_GetMessages(void);
const char *Msg_GetMessagesAndReset(void);
void Msg_LogAndReset(void);
MsgList *Msg_GetMsgList(void);
MsgList *Msg_GetMsgListAndReset(void);
char *Msg_LocalizeList(const MsgList *messages);
void Msg_Reset(Bool log);
Bool Msg_Present(void);
void Msg_ExitThread(void);
void Msg_Exit(void);


/*
 * Don't know, don't care. -- edward
 */

#define MSG_POST_NOMEM() \
   Msg_Post(MSG_ERROR, MSGID(msg.noMem) "Cannot allocate memory.\n")

#ifndef VMX86_DEBUG
   #define MSG_CHECK_ORPHANED_MESSAGES(id, fmt, arg)
#elif defined VMX86_DEVEL
   #define MSG_CHECK_ORPHANED_MESSAGES(id, fmt, arg) ( \
      Msg_Present() ? \
	 Msg_Post(MSG_INFO, \
		  id fmt "THIS MESSAGE ON DEVEL BUILDS only - " \
		  "Please file bug about orphan Msg_Append\n", \
		  arg) : \
	 (void) 0 \
   )
#else
   #define MSG_CHECK_ORPHANED_MESSAGES(id, fmt, arg) ( \
      Msg_Present() ? \
	 (Log(fmt, arg), \
	  Msg_Reset(TRUE)) : \
	 (void) 0 \
   )
#endif


/*
 * To implement message dialogs
 */

void Msg_SetCallback(MsgCallback *cb);
void Msg_SetThreadCallback(MsgCallback *cb);

void Msg_GetCallback(MsgCallback *cb);
void Msg_GetThreadCallback(MsgCallback *cb);


/*
 * Conversion functions
 */

#define Msg_ErrString() (Err_ErrString())
#define Msg_Errno2String(errorNumber) ( \
    Err_Errno2String(errorNumber) )

#ifdef _WIN32
const char *Msg_HResult2String(long hr);
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _MSG_H_
  070701000000F6000081A4000000000000000000000001682255050000096A000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/msgList.h  /*********************************************************
 * Copyright (C) 2009-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * msgList.h  --
 * 
 *   Utilities to manipulate (stateless) lists of messages.
 */

#ifndef _MSGLIST_H_
#define _MSGLIST_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include <string.h>
#include <stdarg.h>
#include "vm_basic_types.h"
#include "msgid.h"
#include "msgfmt.h"

#if defined(__cplusplus)
extern "C" {
#endif


/*
 * Data structures, types, and constants
 */


typedef struct MsgList MsgList;
struct MsgList {
   MsgList     *next;
   char        *id;
   char        *format;
   MsgFmt_Arg  *args;
   int          numArgs;
};

/*
 * Functions
 */

MsgList *MsgList_Create(const char *idFmt, ...) PRINTF_DECL(1, 2);
MsgList *MsgList_VCreate(const char *idFmt, va_list args);
MsgList *MsgList_CreateStr(const char *id);

void MsgList_Append(MsgList **tail, const char *idFmt, ...) PRINTF_DECL(2, 3);
void MsgList_VAppend(MsgList **tail, const char *idFmt, va_list args);
void MsgList_AppendStr(MsgList **tail, const char *id);
void MsgList_AppendMsgList(MsgList **tail, MsgList *messages);

void MsgList_Log(const MsgList *messages);
char *MsgList_ToEnglishString(const MsgList *messages);
MsgList *MsgList_Copy(const MsgList *src);
void MsgList_Free(MsgList *messages);

const char *MsgList_GetMsgID(const MsgList *messages);

Bool MsgList_Present(const MsgList *messages);

static INLINE void
MsgList_LogAndFree(MsgList *messages)
{
   MsgList_Log(messages);
   MsgList_Free(messages);
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _MSGLIST_H_
  070701000000F7000081A40000000000000000000000016822550500001587000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/msgfmt.h   /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * msgfmg.h --
 *
 *	MsgFmt: format messages for the Msg module
 */

#ifndef _MSGFMT_H_
#define _MSGFMT_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#ifndef VMKERNEL
#include "str.h" // for HAS_BSD_PRINTF
#endif

#if defined(__cplusplus)
extern "C" {
#endif


/*
 * Format parser callback functions
 */

typedef int
MsgFmt_LitFunc(void *clientData, // IN
               char const *buf,  // IN
               int bufSize);     // IN

typedef int
MsgFmt_SpecFunc(void *clientData,       // IN
                char const *pos,        // IN
                unsigned int posSize,   // IN
                char const *type,       // IN
                unsigned int typeSize); // IN


/*
 * Format specifier flags from MsgFmt_ParseSpec()
 */

#define MSGFMT_FLAG_ALT		0x0001
#define MSGFMT_FLAG_ZERO	0x0002
#define MSGFMT_FLAG_MINUS	0x0004
#define MSGFMT_FLAG_SPACE	0x0008
#define MSGFMT_FLAG_PLUS	0x0010
#define MSGFMT_FLAG_QUOTE	0x0020


/*
 * A format argument
 *
 * In addition to being a internal data structure,
 * MsgFmt_Arg defines the Vob (vmkernel observations) protocol
 * between vmkernel and vmx.  As such, it must be carefully aligned,
 * so that all the fields (except the pointers) have fixed sizes
 * and the same offsets in the 64-bit vmkernel, the 32-bit vmx,
 * and the 64-bit vmx.
 */

typedef enum MsgFmt_ArgType {
   MSGFMT_ARG_INVALID, // must be 0
   MSGFMT_ARG_INT32,
   MSGFMT_ARG_INT64,
   MSGFMT_ARG_PTR32,
   MSGFMT_ARG_PTR64,
   MSGFMT_ARG_FLOAT64,
   MSGFMT_ARG_STRING8,
   MSGFMT_ARG_STRING16,
   MSGFMT_ARG_STRING32,
   MSGFMT_ARG_ERRNO,
} MsgFmt_ArgType;

typedef enum MsgFmt_ArgPlatform {
   MSGFMT_PLATFORM_UNKNOWN,
   MSGFMT_PLATFORM_LINUX,
   MSGFMT_PLATFORM_WINDOWS,
   MSGFMT_PLATFORM_MACOS,
} MsgFmt_ArgPlatform;

typedef struct MsgFmt_Arg {
   int32 type;
   int32 pad;
   union {
      int32 signed32;
      int64 signed64;
      uint32 unsigned32;
      uint64 unsigned64;
      double float64;
      char *string8char;   // same as string8, different type
      int8 *string8;
      int16 *string16;
      int32 *string32;
      int32 offset;

      void *ptr;	// private
   } v;
   struct {
      int32 platform;
      int32 number;
   } e;
   union {		// private
      int32 precision;
      char *localString;
      uint64 pad;
   } p;
} MsgFmt_Arg;

#if defined __linux__
#define MSGFMT_CURRENT_PLATFORM MSGFMT_PLATFORM_LINUX
#elif defined _WIN32
#define MSGFMT_CURRENT_PLATFORM MSGFMT_PLATFORM_WINDOWS
#elif defined __APPLE__
#define MSGFMT_CURRENT_PLATFORM MSGFMT_PLATFORM_MACOS
#else
#define MSGFMT_CURRENT_PLATFORM MSGFMT_PLATFORM_UNKNOWN
#endif


/*
 * Global functions
 */

typedef int
MsgFmt_ParseFunc(MsgFmt_LitFunc *litFunc,    // IN
                 MsgFmt_SpecFunc *specFunc,  // IN
                 void *clientData,           // IN
                 char const *in);            // IN

MsgFmt_ParseFunc MsgFmt_Parse;
MsgFmt_ParseFunc MsgFmt_ParseWin32;

int
MsgFmt_ParseSpec(char const *pos,       // IN: n$ location
                 unsigned int posSize,  // IN: n$ length
                 char const *type,      // IN: flags, width, etc.
                 unsigned int typeSize, // IN: size of above
		 int *position,         // OUT: argument position
		 int *flags,            // OUT: flags, see MSGFMT_FLAG_*
		 int *width,            // OUT: width
		 int *precision,        // OUT: precision
		 char *lengthMod,       // OUT: length modifier
		 char *conversion);     // OUT: conversion specifier

Bool MsgFmt_GetArgs(const char *fmt, va_list va,
                    MsgFmt_Arg **args, int *numArgs, char **error);
Bool MsgFmt_GetArgsWithBuf(const char *fmt, va_list va,
                           MsgFmt_Arg **args, int *numArgs, char **error,
			   void *buf, size_t *bufSize);
void MsgFmt_FreeArgs(MsgFmt_Arg *args, int numArgs);

void MsgFmt_SwizzleArgs(MsgFmt_Arg *args,
                        int numArgs);
int MsgFmt_GetSwizzledString(const MsgFmt_Arg *args, int numArgs, int idx,
                             const void *bufEnd, const int8 **str);
int MsgFmt_UnswizzleArgs(MsgFmt_Arg *args,
                          int numArgs,
                          void *bufEnd);

MsgFmt_Arg* MsgFmt_CopyArgs(MsgFmt_Arg* copyArgs,
                            int numArgs);
int MsgFmt_Snprintf(char *buf, size_t size, const char *format,
                    const MsgFmt_Arg *args, int numArgs);
char *MsgFmt_Asprintf(size_t *length, const char *format,
                      const MsgFmt_Arg *args, int numArgs);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _MSGFMT_H_
 070701000000F8000081A40000000000000000000000016822550500000B37000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/msgid.h    /*********************************************************
 * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * msgid.h --
 *
 *	Message ID magic
 */

#ifndef _MSGID_H_
#define _MSGID_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include "msgid_defs.h"
#include "vm_basic_defs.h"

#ifndef VMKERNEL
#include <string.h>
#endif

#if defined(__cplusplus)
extern "C" {
#endif


// the X hides MSG_MAGIC so it won't appear in the object file
#define MSG_MAGICAL(s) \
   (s != NULL && strncmp(s, MSG_MAGIC"X", MSG_MAGIC_LEN) == 0)

// Start after MSG_MAGIC so it won't appear in the object file either.
#define MSG_HAS_BUTTONID(s) \
   (MSG_MAGICAL(s) && \
    (strncmp(&(s)[MSG_MAGIC_LEN], MSG_BUTTON_ID, MSG_BUTTON_ID_LEN) == 0))


/*
 *-----------------------------------------------------------------------------
 *
 * Msg_HasMsgID --
 *
 *      Check that a string has a message ID.
 *	The full "MSG_MAGIC(...)" prefix is required, not just MSG_MAGIC.
 *
 * Results:
 *      True if string has a message ID.
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Msg_HasMsgID(const char *s)
{
   return MSG_MAGICAL(s) &&
          *(s += MSG_MAGIC_LEN) == '(' &&
          strchr(s + 1, ')') != NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Msg_StripMSGID --
 *
 *      Returns the string that is inside the MSGID() or if it doesn't
 *      have a MSGID just return the string.
 *
 * Results:
 *      The unlocalized string.
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE const char *
Msg_StripMSGID(const char *idString)    // IN
{
   const char *s = idString;

   if (MSG_MAGICAL(s) &&
       *(s += MSG_MAGIC_LEN) == '(' &&
       (s = strchr(s + 1, ')')) != NULL) {
      return s + 1;
   }
   return idString;
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _MSGID_H_
 070701000000F9000081A40000000000000000000000016822550500000762000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/msgid_defs.h   /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * msgid_defs.h --
 *
 *	Message ID magic definitions
 */

#ifndef _MSGID_DEFS_H_
#define _MSGID_DEFS_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"


/*
 * Message ID macros
 *
 * Use as in
 *	Msg_Append(MSGID(file.openFailed) "Failed to open file %s: %s.\n"
 *		   fileName, Msg_ErrString())
 *	Msg_Append(MSGID(mks.powerOnFailed) "Power on failed.\n")
 * or
 *	Msg_Hint(TRUE, HINT_OK,
 *		 MSGID(mks.noDGA) "No full screen mode.\n").
 *
 * Don't make MSG_MAGIC_LEN (sizeof MSG_MAGIC - 1), since
 * that may cause the string to be in the object file, even
 * when it's not used at run time.  And we are trying
 * to avoid littering the output with the magic string.
 *
 * -- edward
 */

#define MSG_MAGIC	"@&!*@*@"
#define MSG_MAGIC_LEN	7
#define MSGID(id)	MSG_MAGIC "(msg." #id ")"
#define MSG_BUTTON_ID "(button."
#define MSG_BUTTON_ID_LEN 8
#define BUTTONID(id)	MSG_MAGIC MSG_BUTTON_ID #id ")"

#endif // ifndef _MSGID_DEFS_H_
  070701000000FA000081A400000000000000000000000168225505000010E6000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/mul64.h    /*********************************************************
 * Copyright (C) 2003-2014,2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * mul64.h
 *
 *      Integer by fixed point multiplication, with rounding.
 *
 *      These routines are implemented in assembly language for most
 *      supported platforms.  This file has plain C fallback versions.
 */

#ifndef _MUL64_H_
#define _MUL64_H_

#define INCLUDE_ALLOW_USERLEVEL

#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
#include "includeCheck.h"

#include "vm_basic_asm.h"

#if defined __cplusplus
extern "C" {
#endif


#ifdef MUL64_NO_ASM
/*
 *-----------------------------------------------------------------------------
 *
 * Mul64x3264 --
 *
 *    Unsigned integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 * 
 *       Unsigned 64-bit integer multiplicand.
 *       Unsigned 32-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Unsigned 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */
static INLINE uint64
Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift)
{
   uint64 lo, hi, lo2, hi2;
   unsigned carry;

   // ASSERT(shift >= 0 && shift < 64);

   lo = (multiplicand & 0xffffffff) * multiplier;
   hi = (multiplicand >> 32) * multiplier;

   lo2 = lo + (hi << 32);
   carry = lo2 < lo;
   hi2 = (hi >> 32) + carry;

   if (shift == 0) {
      return lo2;
   } else {
      return (lo2 >> shift) + (hi2 << (64 - shift)) +
         ((lo2 >> (shift - 1)) & 1);
   }
}

/*
 *-----------------------------------------------------------------------------
 *
 * Muls64x32s64 --
 *
 *    Signed integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 * 
 *       Signed 64-bit integer multiplicand.
 *       Unsigned 32-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Signed 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */
static INLINE int64
Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
{
   uint64 lo, hi, lo2, hi2;
   unsigned carry;

   // ASSERT(shift >= 0 && shift < 64);

   hi = ((uint64)multiplicand >> 32) * multiplier;
   if (multiplicand < 0) {
      hi -= (uint64)multiplier << 32;
   }
   lo = ((uint64)multiplicand & 0xffffffff) * multiplier;

   lo2 = lo + (hi << 32);
   carry = lo2 < lo;
   hi2 = (((int64)hi >> 32) + carry);

   if (shift == 0) {
      return lo2;
   } else {
      return (lo2 >> shift) + (hi2 << (64 - shift)) +
         ((lo2 >> (shift - 1)) & 1);
   }
}
#endif


#if defined __cplusplus
} // extern "C"
#endif

#endif // _MUL64_NOASM_H_

  070701000000FB000081A40000000000000000000000016822550500000C32000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/mutexRank.h    /*********************************************************
 * Copyright (C) 2010-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * mutexRank.h --
 *
 *	Base lock rank defines. See userlock.h for the related APIs.
 */

#ifndef _MUTEXRANK_H_
#define _MUTEXRANK_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Core rank defines.
 *
 * ANY RANK USAGE ABOVE RANK_LEAF IS RESERVED BY THE PI GROUP.
 */

#define RANK_UNRANKED            0
#define RANK_LEAF                0xFF000000
#define RANK_INVALID             0xFFFFFFFF

/*
 * For situations where we need to create locks on behalf of
 * third-party code, but we don't know what ranking scheme, if any,
 * that code uses.  For now, the only usage is in bora/lib/ssl.
 */
#define RANK_THIRDPARTY          RANK_UNRANKED

/*
 * Log lock rank.
 *
 * Very special case. Don't change it. The effect is that critical
 * logging code cannot call anything else which requires a lock, but
 * everyone else can safely Log() while holding a leaf lock.
 */
#define RANK_logLock             (RANK_LEAF + 2)

/*
 * overheadMem lock rank.
 *
 * Very special case. Don't change it. One must be able to enter
 * the overheadMem Facility at any rank (RANK_LEAF or lower) and
 * still be able to acquire a lock in overheadMem *AND* be able
 * to Log().
 */

#define RANK_overheadMem         (RANK_LEAF + 1)

/*
 * bora/lib/allocTrack rank (not really).
 *
 * This is another special case. It hooks malloc/free and the like,
 * and thus can basically sneak in underneath anyone. To that end
 * allocTrack uses unranked, native locks internally to avoid any
 * complications.
 */

/*
 * VMX/VMM/device lock rank space.
 *
 * This rank space is at the bottom, from 1 to RANK_VMX_LEAF. See
 * vmx/public/mutexRankVMX.h for definitions.
 */

/*
 * Foundry lock rank space.
 *
 * This rank space is from RANK_foundryLockBase on up to
 * RANK_foundryLockLeaf. See apps/lib/foundry/mutexRankFoundry.h for
 * definitions.
 */
#define RANK_foundryLockBase     0x80000000

/*
 * bora/lib lock rank space.
 *
 * This rank space is from RANK_libLockBase on up to RANK_LEAF. See
 * lib/public/mutexRankLib.h for definitions.
 */
#define RANK_libLockBase         0xF0000000

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _MUTEXRANK_H_
  070701000000FC000081A4000000000000000000000001682255050000233C000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/mutexRankLib.h /*********************************************************
 * Copyright (c) 2010-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _LIBMUTEXRANK_H
#define _LIBMUTEXRANK_H

#include "mutexRank.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * MXUser mutex ranks for bora/lib code.
 *
 * The ranks define the ordering in which locks are allowed to be acquired.
 *
 * Only locks with higher rank numbers (generally more localized)
 * can be acquired while a lock with a lower rank number is active.
 *
 * bora/lib lock rank space is from RANK_libLockBase on up to
 * RANK_LEAF (see vm_basic_defs).asdf
 *
 * (Keep all of the below offsets in hex).
 */

/*
 * hostDeviceInfo HAL lock
 *
 * Must be < vmhs locks since this is held around the RANK_vmhsHDILock
 * callback lock which vmhs passes into that library.
 */
#define RANK_hdiHALLock             (RANK_libLockBase + 0x1005)

/*
 * vmhs locks (must be < vigor)
 */
#define RANK_vmhsHDILock            (RANK_libLockBase + 0x3002)
#define RANK_vmhsThrMxLock          (RANK_libLockBase + 0x3005)
#define RANK_vmhsVmxMxLock          (RANK_libLockBase + 0x3005)

/*
 * hgfs locks
 */
#define RANK_hgfsSessionArrayLock    (RANK_libLockBase + 0x4010)
#define RANK_hgfsSharedFolders       (RANK_libLockBase + 0x4030)
#define RANK_hgfsNotifyLock          (RANK_libLockBase + 0x4040)
#define RANK_hgfsFileIOLock          (RANK_libLockBase + 0x4050)
#define RANK_hgfsSearchArrayLock     (RANK_libLockBase + 0x4060)
#define RANK_hgfsNodeArrayLock       (RANK_libLockBase + 0x4070)
#define RANK_hgfsActivateLock        (RANK_libLockBase + 0x4080)
#define RANK_hgfsThreadpoolLock      (RANK_libLockBase + 0x4090)

#define RANK_nfcLibAioCtxLock        (RANK_libLockBase + 0x4300)

/*
 * vigor (must be < VMDB range and < disklib, see bug 741290)
 */
#define RANK_vigorOnlineLock         (RANK_libLockBase + 0x4400)
#define RANK_vigorOfflineLock        (RANK_libLockBase + 0x4410)

/*
 * The lock range for IO filters:
 * [RANK_libLockBase + 0x4420 , RANK_libLockBase + 0x442F]
 *
 * (must be > vigor and < filtlib, see PR 2613901)
 * Inbox filters may need to use lock ranks and these filters can be loaded
 * under Vigor-Offline. Therefore, any lock used by a filter in its callbacks
 * should have a rank greater than RANK_vigorOfflineLock. IO filter lock ranks
 * should also be lower than RANK_filtLibPollLock because this allows
 * VMIOF/filtlib APIs that hold filtLibPollLock to be invoked while holding an
 * IO filter lock.
 */
#define RANK_ioFiltersBase            (RANK_libLockBase + 0x4420)

/*
 * filtlib (must be > vigor and < disklib and workercmlp, PR 1340298)
 */
#define RANK_filtLibPollLock         (RANK_libLockBase + 0x4430)

/*
 * filtib lock which protects a disk's allocation bitmap state.
 * Must be > filtLibPollLock, as it could be acquire with the poll lock held.
 * And as evidenced by PR1437159, it must also be lower than workerLibCmplLock.
 */
#define RANK_filtLibAllocBitmapLock (RANK_filtLibPollLock + 1)

/*
 * remoteUSB (must be < workerCmpl)
 */
#define RANK_remoteUSBGlobalLock (RANK_filtLibAllocBitmapLock + 1)

/*
 * workerLib default completion lock
 *
 * Used for workerLib callers who don't provide their own lock. Held
 * around arbitrary completion callbacks so it makes sense to be of
 * a low rank.
 *
 * Must be > RANK_vigorOfflineLock because we may queue work in Vigor
 * offline.
 *
 * Must be < RANK_nfcLibLock, because NFC uses AIO Generic to perform
 * async writes to the virtual disk.
 *
 * Must be > RANK_filtLibPollLock so that filtlib timers can wait
 * for queued work.
 *
 * Must be > RANK_filtLibAllocBitmapLock due to PR1437159.
 *
 * Must be > RANK_remoteUSBGlobalLock so that virtual CCID can wait for
 * queued work.
 */
#define RANK_workerLibCmplLock       (RANK_remoteUSBGlobalLock + 1)

/*
 * NFC lib lock
 */
#define RANK_nfcLibInitLock          (RANK_libLockBase + 0x4505)
#define RANK_nfcLibLock              (RANK_libLockBase + 0x4506)
#define RANK_nfcLibSessionListLock   (RANK_libLockBase + 0x4507)
#define RANK_nfcLibAioLock           (RANK_libLockBase + 0x4508)
#define RANK_nfcLibLastErrorLock     (RANK_libLockBase + 0x4509)

/*
 * Policy lib lock
 */
#define RANK_policyLibLock           (RANK_libLockBase + 0x4605)

/*
 * disklib and I/O related locks
 */
#define RANK_diskLibLock             (RANK_libLockBase + 0x5001)
#define RANK_digestLibLock           (RANK_libLockBase + 0x5004)
#define RANK_nasPluginLock           (RANK_libLockBase + 0x5007)
#define RANK_nasPluginMappingLock    (RANK_libLockBase + 0x5008)
#define RANK_diskLibPluginLock       (RANK_libLockBase + 0x5010)
#define RANK_vmioPluginRootLock      (RANK_libLockBase + 0x5020)
#define RANK_vmioPluginSysLock       (RANK_libLockBase + 0x5040)
#define RANK_fsCmdLock               (RANK_libLockBase + 0x5050)
#define RANK_scsiStateLock           (RANK_libLockBase + 0x5060)
#define RANK_parInitLock             (RANK_libLockBase + 0x5070)
#define RANK_datasetsLock            (RANK_libLockBase + 0x5075)
#define RANK_namespaceLock           (RANK_libLockBase + 0x5080)
#define RANK_objLibInitLock          (RANK_libLockBase + 0x5085)
#define RANK_vvolLibLock             (RANK_libLockBase + 0x5090)
#define RANK_aioMgrInitLock          (RANK_libLockBase + 0x5095)

/*
 * Persistent-memory logical and hardware  management locks
 */
/* The nvdimm layer is the hardware layer */
#define RANK_nvdHandleLock          (RANK_libLockBase + 0x5300)
/* The pmem layer is the logical layer */
#define RANK_pmemHandleLock         (RANK_libLockBase + 0x5310)

/*
 * VMDB range:
 * (RANK_libLockBase + 0x5500, RANK_libLockBase + 0x5600)
 */
#define RANK_vmuSecPolicyLock        (RANK_libLockBase + 0x5505)
#define RANK_vmdbCnxRpcLock          (RANK_libLockBase + 0x5510)
#define RANK_vmdbCnxRpcBarrierLock   (RANK_libLockBase + 0x5520)
#define RANK_vmdbCnxLock             (RANK_libLockBase + 0x5530)
#define RANK_vmdbSecureLock          (RANK_libLockBase + 0x5540)
#define RANK_vmdbDbLock              (RANK_libLockBase + 0x5550)
#define RANK_vmdbW32HookLock         (RANK_libLockBase + 0x5560)
#define RANK_vmdbWQPoolLock          (RANK_libLockBase + 0x5570)
#define RANK_vmdbMemMapLock          (RANK_libLockBase + 0x5580)

/*
 * USB range:
 * (RANK_libLockBase + 0x6500, RANK_libLockBase + 0x6600)
 */

#define RANK_usbArbLibGlobalLock     (RANK_libLockBase + 0x6505)
#define RANK_usbEnumGlobalLock       (RANK_libLockBase + 0x6506)
#define RANK_usbArbLibAsockLock      (RANK_libLockBase + 0x6507)
#define RANK_usbEnumBackendLock      (RANK_libLockBase + 0x6508)
#define RANK_sensorQueueLock         (RANK_libLockBase + 0x6509)
#define RANK_ccidListLock            (RANK_libLockBase + 0x650A)
#define RANK_readerLock              (RANK_libLockBase + 0x650B)
#define RANK_ccidLock                (RANK_libLockBase + 0x650C)

/*
 * misc locks
 *
 * Assuming ordering is important here for the listed locks. Other
 * non-leaf locks are usually defined with RANK_LEAF - 1.
 *
 * At least:
 *    impersonate < pollDefault
 *    keyLocator < preference (for checking AESNI)
 *    keyLocator < sslState (bug 743010)
 *    configDb < keyLocator (for unlocking dictionaries)
 *    battery/button < preference
 *    workerLib < something for sure under VThread_Create
 *    licenseCheck < preference
 *    sslState < sslCrlCache
 *    sslCrlCache < getSafeTmpDir
 */

#define RANK_vigorTransportListLock  (RANK_libLockBase + 0x7010)
#define RANK_batteryLock             (RANK_libLockBase + 0x7030)
#define RANK_buttonLock              (RANK_libLockBase + 0x7040)
#define RANK_impersonateLock         (RANK_libLockBase + 0x7045)
#define RANK_pollDefaultLock         (RANK_libLockBase + 0x7050)
#define RANK_workerLibLock           (RANK_libLockBase + 0x7060)
#define RANK_configDbLock            (RANK_libLockBase + 0x7070)
#define RANK_keyLocatorLock          (RANK_libLockBase + 0x7080)
#define RANK_sslStateLock            (RANK_libLockBase + 0x7085)
#define RANK_sslCrlCacheLock         (RANK_libLockBase + 0x7086)
#define RANK_getSafeTmpDirLock       (RANK_libLockBase + 0x7087)
#define RANK_licenseCheckLock        (RANK_libLockBase + 0x7090)
#define RANK_preferenceLock          (RANK_libLockBase + 0x7100)

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _LIBMUTEXRANK_H */
070701000000FD000081A40000000000000000000000016822550500000EEA000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/netutil.h  /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * netutil.h --
 *
 *    Utility network functions.
 *
 */


#ifndef __NETUTIL_H__
#define __NETUTIL_H__

#ifdef _WIN32
#   include <windows.h>
#   include <ws2tcpip.h>
#   include "vmware/iphlpapi_packed.h"
#   include <ipmib.h>
#   include <netioapi.h>
#else
#   include <arpa/inet.h>
#endif

#include "vm_basic_types.h"
#include "guestInfo.h"

/*
 * Interface types as assigned by IANA.
 * See http://www.iana.org/assignments/ianaiftype-mib for more details.
 */

typedef enum {
   IANA_IFTYPE_OTHER            = 1,
   IANA_IFTYPE_ETHERNETCSMACD   = 6,
} IanaIfType;


/*
 * Modified from iptypes.h...
 */
char *NetUtil_GetPrimaryIP(void);

GuestNic *NetUtil_GetPrimaryNic(void);

#ifdef _WIN32

/*
 * Brute-force this. Trying to do it "properly" with
 * WINVER/NTDDI_VERSION checks/manips in a way that compiles for both
 * VC8 and VC9 didn't work.
 */
#ifndef FIXED_INFO
typedef  FIXED_INFO_W2KSP1 FIXED_INFO;
typedef  FIXED_INFO_W2KSP1 *PFIXED_INFO;
#endif

DWORD NetUtil_LoadIpHlpApiDll(void);
DWORD NetUtil_FreeIpHlpApiDll(void);

/* Wrappers for functions in iphlpapi.dll */
PFIXED_INFO NetUtil_GetNetworkParams(void);
PIP_ADAPTER_INFO NetUtil_GetAdaptersInfo(void);
ULONG NetUtil_GetAdaptersAddresses(ULONG Family,
                                   ULONG Flags,
                                   PVOID rsvd,
                                   PIP_ADAPTER_ADDRESSES adap_addresses,
                                   PULONG SizePointer);

PMIB_IPFORWARDTABLE NetUtilWin32_GetIpForwardTable(void);
PMIB_IPFORWARD_TABLE2 NetUtilWin32_GetIpForwardTable2(void);
void NetUtilWin32_FreeMibTable(PMIB_IPFORWARD_TABLE2);
#endif

#ifdef WIN32
int NetUtil_InetPToN(int af, const char *src, void *dst);
const char *NetUtil_InetNToP(int af, const void *src, char *dst,
                             socklen_t size);
#else // ifdef WIN32
#   define NetUtil_InetPToN     inet_pton
#   define NetUtil_InetNToP     inet_ntop
#endif

#if defined(__linux__)
#   ifdef DUMMY_NETUTIL
/*
 * Dummy interface table to enable other tools'/libraries' unit tests.
 */
typedef struct {
   int           ifIndex;
   const char   *ifName;
} NetUtilIfTableEntry;


/*
 * {-1, NULL}-terminated array of NetUtilIfTableEntry pointers.
 *
 * (Test) applications wishing to use the dummy NetUtil_GetIf{Index,Name}
 * functions must define this variable somewhere.  It allows said apps
 * to work with a priori knowledge of interface name <=> index mappings
 * returned by said APIs.
 */
EXTERN NetUtilIfTableEntry netUtilIfTable[];

int   NetUtil_GetIfIndex(const char *ifName);
char *NetUtil_GetIfName(int ifIndex);
#   endif // ifdef DUMMY_NETUTIL
#endif // if defined(__linux__)

size_t NetUtil_GetHardwareAddress(int ifIndex,         // IN
                                  char *hwAddr,        // OUT
                                  size_t hwAddrSize,   // IN
                                  IanaIfType *ifType); // OUT

#endif // ifndef _NETUTIL_H_
  070701000000FE000081A40000000000000000000000016822550500000BA1000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/nicInfo.h  /*********************************************************
 * Copyright (c) 2014-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * nicInfo.h --
 *
 *    Functions used to communicate guest networking information to the host.
 *
 */

#ifndef NICINFO_H
#define NICINFO_H

#include <glib.h>

#include "guestInfo.h"

typedef enum {
   NICINFO_PRIORITY_PRIMARY,
   NICINFO_PRIORITY_NORMAL,
   NICINFO_PRIORITY_LOW,
   NICINFO_PRIORITY_MAX
} NicInfoPriority;

Bool GuestInfo_GetFqdn(int outBufLen, char fqdn[]);
Bool GuestInfo_GetNicInfo(unsigned int maxIPv4Routes,
                          unsigned int maxIPv6Routes,
                          NicInfoV3 **nicInfo,
                          Bool *maxNicsError);
void GuestInfo_FreeNicInfo(NicInfoV3 *nicInfo);
char *GuestInfo_GetPrimaryIP(void);

/*
 * Comparison routines -- handy for caching, unit testing.
 */

Bool
GuestInfo_IsEqual_DhcpConfigInfo(const DhcpConfigInfo *a,
                                 const DhcpConfigInfo *b);

Bool
GuestInfo_IsEqual_DnsConfigInfo(const DnsConfigInfo *a,
                                const DnsConfigInfo *b);

Bool
GuestInfo_IsEqual_DnsHostname(const DnsHostname *a,
                              const DnsHostname *b);

Bool
GuestInfo_IsEqual_InetCidrRouteEntry(const InetCidrRouteEntry *a,
                                     const InetCidrRouteEntry *b,
                                     const NicInfoV3 *aInfo,
                                     const NicInfoV3 *bInfo);

Bool
GuestInfo_IsEqual_IpAddressEntry(const IpAddressEntry *a,
                                 const IpAddressEntry *b);

Bool
GuestInfo_IsEqual_NicInfoV3(const NicInfoV3 *a,
                            const NicInfoV3 *b);

Bool
GuestInfo_IsEqual_TypedIpAddress(const TypedIpAddress *a,
                                 const TypedIpAddress *b);

Bool
GuestInfo_IsEqual_WinsConfigInfo(const WinsConfigInfo *a,
                                 const WinsConfigInfo *b);

void GuestInfo_SetIfaceExcludeList(char **list);

void GuestInfo_SetIfacePrimaryList(char **list);

void GuestInfo_SetIfaceLowPriorityList(char **list);

Bool GuestInfo_IfaceIsExcluded(const char *name);

NicInfoPriority GuestInfo_IfaceGetPriority(const char *name);

#endif
   070701000000FF000081A40000000000000000000000016822550500000D30000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/panic.h    /*********************************************************
 * Copyright (C) 2003-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * panic.h --
 *
 *	Module to encapsulate common Panic behavior.
 */

#ifndef _PANIC_H_
#define _PANIC_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Initialize module to read custom behavior from config files.
 */

void Panic_Init(void);

/*
 * If you want the Panic module to just handle everything: implement Panic()
 * as a call to Panic_Panic().  If you want to implement your own Panic(),
 * you can still use functions below to query whether certain features are
 * desired and get the default implementation of each feature.
 */

NORETURN void Panic_Panic(const char *format, va_list args);

/*
 * On panic, post a UI dialog about the panic and how to diagnose it:
 */

void Panic_SetPanicMsgPost(Bool postMsg);
Bool Panic_GetPanicMsgPost(void);
void Panic_PostPanicMsg(const char *format, ...);

/*
 * On panic, break into a debugger or enter an infinite loop until a
 * debugger stops it:
 */

typedef enum {
   PanicBreakAction_Never,
   PanicBreakAction_IfDebuggerAttached,
   PanicBreakAction_Always
} PanicBreakAction;

void Panic_SetBreakAction(PanicBreakAction action);
PanicBreakAction Panic_GetBreakAction(void);
Bool Panic_GetBreakOnPanic(void);
void Panic_BreakOnPanic(void);
void Panic_LoopOnPanic(void);


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_SetBreakOnPanic --
 *
 *      Allow the debug breakpoint on panic to be suppressed.  If passed FALSE,
 *      then any subsequent Panics will not attempt to attract a debugger.
 *
 * Results:
 *      void.
 *
 * Side effects:
 *      Enables/Disables break into debugger on Panic().
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Panic_SetBreakOnPanic(Bool breakOnPanic)  // IN:
{
   Panic_SetBreakAction(breakOnPanic ? PanicBreakAction_Always
                                     : PanicBreakAction_Never);
}


/*
 * On panic, dump core; Panic is also the place where various pieces of
 * back end stash information about the core dump.
 */

void Panic_SetCoreDumpOnPanic(Bool dumpCore);
Bool Panic_GetCoreDumpOnPanic(void);
int  Panic_GetCoreDumpFlags(void);
void Panic_SetCoreDumpFlags(int flags);

/*
 * Extra debugging information that Panic module knows how to dump.
 */
void Panic_DumpGuiResources(void);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif //  _PANIC_H_
07070100000100000081A40000000000000000000000016822550500002A07000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/poll.h /*********************************************************
 * Copyright (C) 1998-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


#ifndef _POLL_H_
#define _POLL_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "vmware.h"
#include "userlock.h"

#if defined(__cplusplus)
extern "C" {
#endif

#ifdef _WIN32
#define HZ 100
#elif defined __linux__
#include <asm/param.h>
#elif __APPLE__
#include <TargetConditionals.h>
/*
 * Old SDKs don't define TARGET_OS_IPHONE at all.
 * New ones define it to 0 on Mac OS X, 1 on iOS.
 */
#if !defined(TARGET_OS_IPHONE) || TARGET_OS_IPHONE == 0
#include <sys/kernel.h>
#endif
#include <sys/poll.h>
#define HZ 100
#endif
#ifdef __ANDROID__
/*
 * <poll.h> of android should be included, but its name is same
 * with this file. So its content is put here to avoid conflict.
 */
#include <asm/poll.h>
#define HZ 100
typedef unsigned int  nfds_t;
int poll(struct pollfd *, nfds_t, long);
#endif


/*
 * Poll event types: each type has a different reason for firing,
 * or condition that must be met before firing.
 */

typedef enum {
   /*
    * Actual Poll queue types against which you can register callbacks.
    */
   POLL_VIRTUALREALTIME = -1, /* Negative because it doesn't have its own Q */
   POLL_VTIME = 0,
   POLL_REALTIME,
   POLL_DEVICE,
   POLL_MAIN_LOOP,
   POLL_NUM_QUEUES
} PollEventType;


/*
 * Classes of events
 *
 * These are the predefined classes.  More can be declared
 * with Poll_AllocClass().
 */

typedef enum PollClass {
   POLL_CLASS_MAIN,
   POLL_CLASS_PAUSE,
   POLL_CLASS_IPC,
   POLL_CLASS_CPT,
   POLL_CLASS_MKS,
   POLL_FIXED_CLASSES,
   POLL_DEFAULT_FIXED_CLASSES,
   /* Size enum to maximum */
   POLL_MAX_CLASSES = 31,
} PollClass;

/*
 * Do not use; Special pseudo private poll class supported by
 * PollDefault only
 */
#define POLL_DEFAULT_CLASS_NET POLL_FIXED_CLASSES
#define POLL_DEFAULT_CS_NET    PollClassSet_Singleton(POLL_DEFAULT_CLASS_NET)

/*
 * Each callback is registered in a set of classes
 */

typedef struct PollClassSet {
   uintptr_t bits;
} PollClassSet;

/* An empty PollClassSet. */
static INLINE PollClassSet
PollClassSet_Empty(void)
{
   PollClassSet set = { 0 };
   return set;
}

/* A PollClassSet with the single member. */
static INLINE PollClassSet
PollClassSet_Singleton(PollClass c)
{
   PollClassSet s = PollClassSet_Empty();

   ASSERT_ON_COMPILE(POLL_MAX_CLASSES < sizeof s.bits * 8);
   ASSERT(c < POLL_MAX_CLASSES);

   s.bits = CONST3264U(1) << c;
   return s;
}

/* Combine two PollClassSets. */
static INLINE PollClassSet
PollClassSet_Union(PollClassSet lhs, PollClassSet rhs)
{
   PollClassSet set;
   set.bits = lhs.bits | rhs.bits;
   return set;
}

/* Add single class to PollClassSet. */
static INLINE PollClassSet
PollClassSet_Include(PollClassSet set, PollClass c)
{
   return PollClassSet_Union(set, PollClassSet_Singleton(c));
}


#define POLL_CS_MAIN    PollClassSet_Singleton(POLL_CLASS_MAIN)
#define POLL_CS_PAUSE   PollClassSet_Union(POLL_CS_MAIN,            \
                           PollClassSet_Singleton(POLL_CLASS_PAUSE))
#define POLL_CS_CPT     PollClassSet_Union(POLL_CS_PAUSE,           \
                           PollClassSet_Singleton(POLL_CLASS_CPT))
#define POLL_CS_IPC     PollClassSet_Union(POLL_CS_CPT,             \
                           PollClassSet_Singleton(POLL_CLASS_IPC))
#define POLL_CS_VMDB    POLL_CS_PAUSE /* POLL_CLASS_VMDB is retired */
#define POLL_CS_MKS	PollClassSet_Singleton(POLL_CLASS_MKS)
/* 
 * DANGER.  You don't need POLL_CS_ALWAYS.  Really.  So don't use it.
 */
#define POLL_CS_ALWAYS  PollClassSet_Union(POLL_CS_CPT, POLL_CS_IPC)

/*
 * Poll class-set taxonomy:
 * POLL_CS_MAIN
 *    - Unless you NEED another class, use POLL_CS_MAIN.
 * POLL_CS_PAUSE
 *    - For callbacks that must occur even if the guest is paused.
 *      Most VMDB or Foundry commands are in this category.
 * POLL_CS_CPT
 *    - Only for callbacks which can trigger intermediate Checkpoint 
 *      transitions.
 *      The ONLY such callback is Migrate.
 * POLL_CS_IPC
 *    - Only for callbacks which can contain Msg_(Post|Hint|Question) 
 *      responses, and for signal handlers (why)?
 *      Vigor, VMDB, and Foundry can contain Msg_* responses.
 * POLL_CS_MKS
 *    - Callback runs in MKS thread.
 * POLL_CS_ALWAYS
 *    - Only for events that must be processed immediately.
 *      The ONLY such callback is OvhdMemVmxSizeCheck.
 */


/*
 * Poll_Callback flags
 */

#define POLL_FLAG_PERIODIC		0x01    // keep after firing
#define POLL_FLAG_REMOVE_AT_POWEROFF	0x02  	// self-explanatory
#define POLL_FLAG_READ			0x04	// device is ready for reading
#define POLL_FLAG_WRITE			0x08	// device is ready for writing
#define POLL_FLAG_SOCKET                0x10    // device is a Windows socket
#define POLL_FLAG_NO_BULL               0x20    // callback does its own locking
#define POLL_FLAG_WINSOCK               0x40    // Winsock style write events
#define POLL_FLAG_FD                    0x80    // device is a Windows file descriptor.
#define POLL_FLAG_ACCEPT_INVALID_FDS    0x100   // For broken 3rd party libs, e.g. curl
#define POLL_FLAG_THUNK_TO_WND          0x200   // thunk callback to window message loop


typedef void (*PollerFunction)(void *clientData);
typedef void (*PollerFireWrapper)(PollerFunction func,
                                  void *funcData,
                                  void *wrapperData);
typedef Bool (*PollerErrorFn)(const char *errorStr);

/*
 * Initialisers:
 *
 *      For the sake of convenience, we declare the initialisers
 *      for custom implmentations here, even though the actual
 *      implementations are distinct from the core poll code.
 */


/* Socket pair created with non-blocking mode */
#define POLL_OPTIONS_SOCKET_PAIR_NONBLOCK_CONN  0x01

typedef unsigned int SocketSpecialOpts;

typedef struct PollOptions {
   Bool locked;           // Use internal MXUser for locking
   Bool allowFullQueue;   // Don't assert when device event queue is full.
   VThreadID windowsMsgThread;       // thread that processes Windows messages
   PollerFireWrapper fireWrapperFn;  // optional; may be useful for stats
   void *fireWrapperData; // optional
   PollerErrorFn errorFn; // optional; called upon unrecoverable error
   SocketSpecialOpts pollSocketOpts;
} PollOptions;


void Poll_InitDefault(void);
void Poll_InitDefaultEx(const PollOptions *opts);
void Poll_InitGtk(void); // On top of glib for Linux
void Poll_InitCF(void);  // On top of CoreFoundation for OSX

Bool Poll_IsInitialized(void);

/*
 * Functions
 */
int Poll_SocketPair(Bool vmci, Bool stream, int fds[2], SocketSpecialOpts opts);
void Poll_Loop(Bool loop, Bool *exit, PollClass c);
void Poll_LoopTimeout(Bool loop, Bool *exit, PollClass c, int timeout);
Bool Poll_LockingEnabled(void);
void Poll_Exit(void);


/*
 * Poll_Callback adds a callback regardless of whether an identical one exists.
 * The exception to this rule is POLL_DEVICE callbacks: there is a maximum of
 * one read and one write callback per fd.
 *
 * Poll_CallbackRemove removes one callback. If there are multiple identical
 * callbacks, which one is removed is an implementation detail. Note that in
 * the case of POLL_DEVICE and POLL_REALTIME callbacks, the fd/delay used to
 * create the callback is not specified when removing, so all callbacks
 * of those types with the same flags, function, and clientData are considered
 * "identical" even if their fd/delay differed.
 */

VMwareStatus Poll_Callback(PollClassSet classSet,
                           int flags,
                           PollerFunction f,
                           void *clientData,
                           PollEventType type,
                           PollDevHandle info, // fd/microsec delay
                           MXUserRecLock *lck);
Bool Poll_CallbackRemove(PollClassSet classSet,
                         int flags,
                         PollerFunction f,
                         void *clientData,
                         PollEventType type);
Bool Poll_CallbackRemoveOneByCB(PollClassSet classSet,
                                int flags,
                                PollerFunction f,
                                PollEventType type,
                                void **clientData);

void Poll_NotifyChange(PollClassSet classSet);

/*
 * Wrappers for Poll_Callback and Poll_CallbackRemove that present
 * simpler subsets of those interfaces.
 */

VMwareStatus Poll_CB_Device(PollerFunction f,
                            void *clientData,
                            PollDevHandle device,
                            Bool periodic);

Bool Poll_CB_DeviceRemove(PollerFunction f,
                          void *clientData,
                          Bool periodic);


VMwareStatus Poll_CB_RTime(PollerFunction f,
                           void *clientData,
                           int64 delay,   // microseconds
                           Bool periodic,
                           MXUserRecLock *lock);

Bool Poll_CB_RTimeRemove(PollerFunction f,
                         void *clientData,
                         Bool periodic);


#ifdef _WIN32
void Poll_SetPumpsWindowsMessages(Bool pumps);
void Poll_SetWindowMessageRecipient(HWND hWnd, UINT msg, Bool alwaysThunk);
Bool Poll_FireWndCallback(void *lparam);
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _POLL_H_
 07070100000101000081A400000000000000000000000168225505000011DC000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/pollImpl.h /*********************************************************
 * Copyright (C) 2006-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * pollImpl.h --
 *
 *      Header file for poll implementations. Poll consumers should not
 *      include is file.
 */


#ifndef _POLLIMPL_H_
#define _POLLIMPL_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "poll.h"
#include "vm_basic_asm.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * PollImpl:
 *
 * A Poll implementation should provide a filled in PollImpl
 * to pass to Poll_Init.
 */

typedef struct PollImpl {
   void         (*Init)                 (void);
   void         (*Exit)                 (void);
   void         (*LoopTimeout)          (Bool loop, Bool *exit,
                                         PollClass c, int timeout);
   VMwareStatus (*Callback)             (PollClassSet classSet, int flags,
                                         PollerFunction f, void *clientData,
                                         PollEventType type,
                                         PollDevHandle info,
                                         MXUserRecLock *lock);
   Bool         (*CallbackRemove)       (PollClassSet classSet, int flags,
                                         PollerFunction f, void *clientData,
                                         PollEventType type);
   Bool         (*CallbackRemoveOneByCB)(PollClassSet classSet, int flags,
                                         PollerFunction f, PollEventType type,
                                         void **clientData);
   Bool         (*LockingEnabled)       (void);
   void         (*NotifyChange)         (PollClassSet classSet);
} PollImpl;


void Poll_InitWithImpl(const PollImpl *impl);

/* Check if a PollClass is part of the set. */
static INLINE Bool
PollClassSet_IsMember(PollClassSet set, PollClass c)
{
   return (set.bits & PollClassSet_Singleton(c).bits) != 0;
}

/* Compare two PollClassSets. */
static INLINE Bool
PollClassSet_Equals(PollClassSet lhs, PollClassSet rhs)
{
   return lhs.bits == rhs.bits;
}

/* Verifies if the class set is empty. */
static INLINE Bool
PollClassSet_IsEmpty(PollClassSet cs)
{
   return PollClassSet_Equals(cs, PollClassSet_Empty());
}

/* Remove from a PollClassSet. */
static INLINE void
PollClassSet_Remove(PollClassSet *set, PollClass c)
{
   set->bits &= ~PollClassSet_Singleton(c).bits;
}

/* Find first set.  POLL_MAX_CLASSES for none set. */
static INLINE PollClass
PollClassSet_FFS(PollClassSet *set)
{
   if (set->bits != 0) {
      return (PollClass)lssbPtr_0(set->bits);
   }
   return POLL_MAX_CLASSES;
}


/*
 *----------------------------------------------------------------------------
 *
 * PollLockingAlwaysEnabled --
 * PollLockingNotAvailable --
 *
 *      Simple LockingEnabled() functions for poll implementation that does
 *      not dynamically enable locking.
 *
 * Results:
 *      Bool.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE Bool
PollLockingAlwaysEnabled(void)
{
   return TRUE;
}

static INLINE Bool
PollLockingNotAvailable(void)
{
   return FALSE;
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _POLLIMPL_H_ */
07070100000102000081A40000000000000000000000016822550500005202000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/posix.h    /*********************************************************
 * Copyright (C) 2008-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef VMWARE_POSIX_H
#define VMWARE_POSIX_H

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include <stdio.h>
#include <stdlib.h>
#if !defined(_WIN32)
#include <sys/types.h>
#include <dirent.h>
#include <netdb.h>
#endif

#include "vm_basic_types.h"
#include "unicodeTypes.h"
#include "unicodeBase.h"
#include "codeset.h"
#include "err.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Require the _FILE_OFFSET_BITS=64 interface, where all Posix file
 * structures and functions are transparently 64-bit.
 *
 * Some OSes (FreeBSD, OS X) natively use 64-bit file offsets and
 * will never be a problem. Other OSes (Linux, Solaris) default
 * to 32-bit offsets in 32-bit mode. Android ... does its own thing.
 * Windows only offers the Posix APIs as 32-bit.
 *
 * There are two ways of getting 64-bit offsets, defined by the LFS standard:
 *    _LARGEFILE64_SOURCE, which defines xxx64 types and functions
 *    _FILE_OFFSET_BITS=64, which replaces all types and functions
 * The _LARGEFILE64_SOURCE strategy was transitional (late 90s) and is now
 * deprecated. All modern code should use _FILE_OFFSET_BITS=64.
 *
 * Instead of checking for the exact details, check for what matters
 * for the purposes of this file: types (e.g. off_t) must be 64-bit.
 */
#if !defined(_WIN32) && !defined(__ANDROID__)
MY_ASSERTS(_file_offset_bits64, ASSERT_ON_COMPILE(sizeof(off_t) == 8);)
#endif

struct stat;

#if defined(_WIN32)
typedef int mode_t;
#else
struct statfs;
struct utimbuf;
struct timeval;
struct passwd;
#if !defined(sun)
struct mntent;
#else
struct mnttab;
#endif
#endif


int Posix_Creat(const char *pathName, mode_t mode);
int Posix_Open(const char *pathName, int flags, ...);
FILE *Posix_Fopen(const char *pathName, const char *mode);
FILE *Posix_Popen(const char *pathName, const char *mode);
int Posix_Rename(const char *fromPathName, const char *toPathName);
int Posix_Rmdir(const char *pathName);
int Posix_Unlink(const char *pathName);
FILE *Posix_Freopen(const char *pathName, const char *mode, FILE *stream);
int Posix_Access(const char *pathName, int mode);
int Posix_EuidAccess(const char *pathName, int mode);
int Posix_Stat(const char *pathName, struct stat *statbuf);
int Posix_Chmod(const char *pathName, mode_t mode);
void Posix_Perror(const char *str);
int Posix_Printf(const char *format, ...);
int Posix_Fprintf(FILE *stream, const char *format, ...);

int Posix_Mkdir(const char *pathName, mode_t mode);
int Posix_Chdir(const char *pathName);
char *Posix_Getenv(const char *name);
long Posix_Pathconf(const char *pathName, int name);
int Posix_Lstat(const char *pathName, struct stat *statbuf);
char *Posix_MkTemp(const char *pathName);


/*
 *-----------------------------------------------------------------------------
 *
 * Posix_Free --
 *
 *      Wrapper around free() that preserves errno.
 *
 *      C11 (and earlier) does not prohibit free() implementations from
 *      modifying errno.  That is undesirable since it can clobber errno along
 *      cleanup paths, and it is expected to be prohibited by a future (as of
 *      January 2017) version of the POSIX standard.  See:
 *      <http://stackoverflow.com/a/30571921/179715>
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

#define Posix_Free(p) WITH_ERRNO_FREE(p)

#if !defined(_WIN32)
/*
 * These Windows APIs actually work with non-ASCII (MBCS) strings.
 * Make them NULL wrappers for all other platforms.
 */
#define Posix_GetHostName gethostname
#if defined(__APPLE__)
#define Posix_GetHostByName gethostbyname
#endif
#define Posix_GetAddrInfo getaddrinfo
#define Posix_FreeAddrInfo freeaddrinfo
#define Posix_GetNameInfo getnameinfo

void *Posix_Dlopen(const char *pathName, int flags);

int Posix_Utime(const char *pathName, const struct utimbuf *times);

int Posix_Mknod(const char *pathName, mode_t mode, dev_t dev);
int Posix_Chown(const char *pathName, uid_t owner, gid_t group);
int Posix_Lchown(const char *pathName, uid_t owner, gid_t group);
int Posix_Link(const char *oldPath, const char *newPath);
int Posix_Symlink(const char *oldPath, const char *newPath);
int Posix_Mkfifo(const char *pathName, mode_t mode);
int Posix_Truncate(const char *pathName, off_t length);
int Posix_Utimes(const char *pathName, const struct timeval *time);
int Posix_Execl(const char *pathName, const char *arg0, ...);
int Posix_Execlp(const char *fileName, const char *arg0, ...);
int Posix_Execv(const char *pathName, char *const argVal[]);
int Posix_Execve(const char *pathName, char *const argVal[],
                 char *const envPtr[]);
int Posix_Execvp(const char *fileName, char *const argVal[]);
DIR *Posix_OpenDir(const char *pathName);
int Posix_System(const char *command);
int Posix_Putenv(char *name);
int Posix_Unsetenv(const char *name);

/*
 * These functions return dynamically allocated stings that have to be
 * freed by the caller so they must be used in the ESX environment. They
 * are different than their POSIX "base" functions.
 */

char *Posix_RealPath(const char *pathName);
char *Posix_ReadLink(const char *pathName);

struct passwd *Posix_Getpwnam(const char *name);
struct passwd *Posix_Getpwuid(uid_t uid);

int Posix_Setenv(const char *name, const char *value, int overWrite);

int Posix_Getpwnam_r(const char *name, struct passwd *pw,
                     char *buf, size_t size, struct passwd **ppw);
int Posix_Getpwuid_r(uid_t uid, struct passwd *pw,
                     char *buf, size_t size, struct passwd **ppw);
struct passwd *Posix_Getpwent(void);
void Posix_Endpwent(void);
struct group *Posix_Getgrnam(const char *name);
int Posix_Getgrnam_r(const char *name, struct group *gr,
                 char *buf, size_t size, struct group **pgr);

#if !defined(sun)
int Posix_Statfs(const char *pathName, struct statfs *statfsbuf);

int Posix_GetGroupList(const char *user, gid_t group, gid_t *groups,
                       int *ngroups);

#if !defined(__APPLE__) && !defined(__FreeBSD__)
int Posix_Mount(const char *source, const char *target,
                const char *filesystemtype, unsigned long mountflags,
                const void *data);
int Posix_Umount(const char *target);
FILE *Posix_Setmntent(const char *pathName, const char *mode);
struct mntent *Posix_Getmntent(FILE *fp);
struct mntent *Posix_Getmntent_r(FILE *fp, struct mntent *m,
                                 char *buf, int size);

#endif // !defined(__APPLE__) && !defined(__FreeBSD__)
#else  // !defined(sun)
int Posix_Getmntent(FILE *fp, struct mnttab *mp);

#endif // !defined(sun)
#if !defined(__APPLE__)


/*
 *----------------------------------------------------------------------
 *
 * Posix_GetHostByName --
 *
 *      Wrapper for gethostbyname().  Caller should release memory
 *      allocated for the hostent structure returned by calling
 *      Posix_FreeHostent().
 *
 * Results:
 *      NULL    Error
 *      !NULL   Pointer to hostent structure
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static INLINE struct hostent*
Posix_GetHostByName(const char *name)  // IN
{
   struct hostent *newhostent;
   int error;
   struct hostent he;
   char buffer[1024];
   struct hostent *phe = &he;
   char **p;
   int i;
   int naddrs;

   ASSERT(name);

   if ((gethostbyname_r(name, &he, buffer, sizeof buffer,
#if !defined(sun) && !defined(SOLARIS) && !defined(SOL10)
                        &phe,
#endif
                        &error) == 0) && phe) {

      newhostent = (struct hostent *)Util_SafeMalloc(sizeof *newhostent);
      newhostent->h_name = Unicode_Alloc(phe->h_name,
                                         STRING_ENCODING_DEFAULT);
      if (phe->h_aliases) {
         newhostent->h_aliases = Unicode_AllocList(phe->h_aliases,
                                                   -1,
                                                   STRING_ENCODING_DEFAULT);
      } else {
         newhostent->h_aliases = NULL;
      }
      newhostent->h_addrtype = phe->h_addrtype;
      newhostent->h_length = phe->h_length;

      naddrs = 1;
      for (p = phe->h_addr_list; *p; p++) {
         naddrs++;
      }
      newhostent->h_addr_list = (char **)Util_SafeMalloc(naddrs *
                                 sizeof(*(phe->h_addr_list)));
      for (i = 0; i < naddrs - 1; i++) {
         newhostent->h_addr_list[i] = (char *)Util_SafeMalloc(phe->h_length);
         memcpy(newhostent->h_addr_list[i], phe->h_addr_list[i], phe->h_length);
      }
      newhostent->h_addr_list[naddrs - 1] = NULL;
      return newhostent;
   }
   /* There has been an error */
   return NULL;
}
#endif // !define(__APPLE__)


/*
 *----------------------------------------------------------------------
 *
 * Posix_FreeHostent --
 *
 *      Free the memory allocated for an hostent structure returned
 *      by Posix_GetHostByName.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
Posix_FreeHostent(struct hostent *he)
{
#if !defined(__APPLE__)
   char **p;

   if (he) {
      // See Posix_Free.
      int err = errno;
      free(he->h_name);
      if (he->h_aliases) {
         Util_FreeStringList(he->h_aliases, -1);
      }
      p = he->h_addr_list;
      while (*p) {
         free(*p++);
      }
      free(he->h_addr_list);
      free(he);
      errno = err;
   }
#else
   (void) he;
#endif
}

#else  // !define(_WIN32)

#if defined(_WINSOCKAPI_) || defined(_WINSOCK2API_)
#include <winbase.h>
#include "vm_atomic.h"
#if defined(VM_WIN_UWP)
/* UWP use the network definition in winsock2.h */
#include <winsock2.h>
#endif

/*
 *----------------------------------------------------------------------
 *
 * Posix_GetHostName --
 *
 *      Wrapper for gethostname().
 *
 * Results:
 *      0    Success
 *      -1   Error
 *
 * Side effects:
 *      On error, error code returned by WSAGetLastError() is updated.
 *
 *----------------------------------------------------------------------
 */

static INLINE int
Posix_GetHostName(char *name,   // OUT
                  int namelen)  // IN
{
   char *nameMBCS = (char *)Util_SafeMalloc(namelen);
   char *nameUTF8;
   int retval;

   ASSERT(name);

   retval = gethostname(nameMBCS, namelen);

   if (retval == 0) {
      nameUTF8 = Unicode_Alloc(nameMBCS, STRING_ENCODING_DEFAULT);
      if (!Unicode_CopyBytes(name, nameUTF8, namelen, NULL,
                             STRING_ENCODING_UTF8)) {
         retval = -1;
         WSASetLastError(WSAEFAULT);
      }
      Posix_Free(nameUTF8);
   }

   Posix_Free(nameMBCS);

   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_GetHostByName --
 *
 *      Wrapper for gethostbyname().  Caller should release memory
 *      allocated for the hostent structure returned by calling
 *      Posix_FreeHostent().
 *
 * Results:
 *      NULL    Error
 *      !NULL   Pointer to hostent structure
 *
 * Side effects:
 *      On error, error code returned by WSAGetLastError() is updated.
 *
 *----------------------------------------------------------------------
 */

static INLINE struct hostent*
Posix_GetHostByName(const char *name)  // IN
{
   struct hostent *newhostent;
   char *nameMBCS;
   struct hostent *hostentMBCS;
   struct hostent *ret = NULL;

   ASSERT(name);

   nameMBCS = (char *)Unicode_GetAllocBytes(name, STRING_ENCODING_DEFAULT);

   if (nameMBCS != NULL) {
      hostentMBCS = gethostbyname(nameMBCS);
      Posix_Free(nameMBCS);

      if (hostentMBCS != NULL) {
         newhostent = (struct hostent *)Util_SafeMalloc(sizeof *newhostent);

         newhostent->h_name = Unicode_Alloc(hostentMBCS->h_name,
                                            STRING_ENCODING_DEFAULT);
         if (hostentMBCS->h_aliases) {
            newhostent->h_aliases = Unicode_AllocList(hostentMBCS->h_aliases,
                                                      -1,
                                                      STRING_ENCODING_DEFAULT);
         } else {
            newhostent->h_aliases = NULL;
         }
         newhostent->h_addrtype = hostentMBCS->h_addrtype;
         newhostent->h_length = hostentMBCS->h_length;
         newhostent->h_addr_list = hostentMBCS->h_addr_list;
         ret = newhostent;
      }
   } else {
      /* There has been an error converting from UTF-8 to local encoding. */
      WSASetLastError(WSANO_RECOVERY);
   }

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_FreeHostent --
 *
 *      Free the memory allocated for an hostent structure returned
 *      by Posix_GetHostByName.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
Posix_FreeHostent(struct hostent *he)
{
   if (he) {
      Posix_Free(he->h_name);
      if (he->h_aliases) {
         Util_FreeStringList(he->h_aliases, -1);
      }
      Posix_Free(he);
   }
}
#endif  // defined(_WINSOCKAPI_) || defined(_WINSOCK2API_)

#ifdef _WS2TCPIP_H_
/*
 *----------------------------------------------------------------------
 *
 * Posix_GetAddrInfo --
 *
 *      Wrapper for getaddrinfo().
 *
 *      Inlined to match Ws2tcpip.h inclusion.
 *
 * Results:
 *      0       Success
 *      != 0    Error
 *
 * Side effects:
 *      On error, error code returned by WSAGetLastError() is updated.
 *
 *----------------------------------------------------------------------
 */

static INLINE int
Posix_GetAddrInfo(const char *nodename,         // IN
                  const char *servname,         // IN
                  const struct addrinfo *hints, // IN
                  struct addrinfo **res)        // OUT
{
   int retval;
   struct addrinfoW *resW;
   utf16_t *nodenameW = Unicode_GetAllocUTF16(nodename);
   utf16_t *servnameW = Unicode_GetAllocUTF16(servname);

   ASSERT(nodename || servname);
   ASSERT(res);

   /*
    * The string conversion required is between UTF-8 and UTF-16 encodings.
    * Note that struct addrinfo and ADDRINFOW are identical except for the
    * fields ai_canonname (char * vs. PWSTR) and ai_next (obviously),
    * and those fields must be NULL, so hints can be cast to UTF-16.
    */

   retval = GetAddrInfoW(nodenameW, servnameW, (struct addrinfoW *)hints,
                         &resW);

   if (retval == 0) {
      struct addrinfoW *cur;
      struct addrinfo **pres = res;

      for (cur = resW; cur != NULL; cur = cur->ai_next) {
         *pres = (struct addrinfo *)Util_SafeMalloc(sizeof **pres);
         (*pres)->ai_flags = cur->ai_flags;
         (*pres)->ai_family = cur->ai_family;
         (*pres)->ai_socktype = cur->ai_socktype;
         (*pres)->ai_protocol = cur->ai_protocol;
         (*pres)->ai_addrlen = cur->ai_addrlen;
         if (cur->ai_canonname) {
            (*pres)->ai_canonname = Unicode_AllocWithUTF16(cur->ai_canonname);
         } else {
            (*pres)->ai_canonname = NULL;
         }
         (*pres)->ai_addr = (struct sockaddr *)
                            Util_SafeMalloc((*pres)->ai_addrlen);
         memcpy((*pres)->ai_addr, cur->ai_addr, (*pres)->ai_addrlen);
         pres = &((*pres)->ai_next);
      }
      *pres = NULL;
      FreeAddrInfoW(resW);
   }

   Posix_Free(nodenameW);
   Posix_Free(servnameW);

   return retval;
}


/*
 *----------------------------------------------------------------------------
 *
 * Posix_FreeAddrInfo --
 *
 *      Free the addrinfo structure allocated by Posix_GetAddrInfo.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
Posix_FreeAddrInfo(struct addrinfo *ai)
{
   struct addrinfo *temp;

   // See Posix_Free.
   int err = errno;
   while (ai) {
      temp = ai;
      ai = ai->ai_next;
      free(temp->ai_canonname);
      free(temp->ai_addr);
      free(temp);
   }
   errno = err;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_GetNameInfo --
 *
 *      Wrapper for getnameinfo().
 *
 *      Inlined to match Ws2tcpip.h inclusion.
 *
 * Results:
 *      0       Success
 *      != 0    Error
 *
 * Side effects:
 *      On error, error code returned by WSAGetLastError() is updated.
 *
 *----------------------------------------------------------------------
 */

static INLINE int
Posix_GetNameInfo(const struct sockaddr *sa,  // IN
                  socklen_t salen,            // IN
                  char *host,                 // OUT
                  DWORD hostlen,              // IN
                  char *serv,                 // OUT
                  DWORD servlen,              // IN
                  int flags)                  // IN
{
   int retval;
   utf16_t *hostW = NULL;
   utf16_t *servW = NULL;
   char *hostUTF8 = NULL;
   char *servUTF8 = NULL;

   if (host) {
      hostW = (utf16_t *)Util_SafeMalloc(hostlen * sizeof *hostW);
   }
   if (serv) {
      servW = (utf16_t *)Util_SafeMalloc(servlen * sizeof *servW);
   }

   retval = GetNameInfoW(sa, salen, hostW, hostlen, servW,
                         servlen, flags);

   if (retval == 0) {
      if (host) {
         hostUTF8 = Unicode_AllocWithUTF16(hostW);

         if (!Unicode_CopyBytes(host, hostUTF8, hostlen, NULL,
                                STRING_ENCODING_UTF8)) {
            retval = EAI_MEMORY;
            WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
            goto exit;
         }
      }
      if (serv) {
         servUTF8 = Unicode_AllocWithUTF16(servW);

         if (!Unicode_CopyBytes(serv, servUTF8, servlen, NULL,
                                STRING_ENCODING_UTF8)) {
            retval = EAI_MEMORY;
            WSASetLastError(WSA_NOT_ENOUGH_MEMORY);
            goto exit;
         }
      }
   }

exit:
   Posix_Free(hostW);
   Posix_Free(servW);
   Posix_Free(hostUTF8);
   Posix_Free(servUTF8);

   return retval;
}
#endif // ifdef _WS2TCPIP_H_
#endif // !define(_WIN32)

#if (defined(VMX86_SERVER) || defined(__APPLE__)) && \
   !defined(UNICODE_BUILDING_POSIX_WRAPPERS)
/*
 * ESX and Mac OS are UTF-8 environments so these functions can be
 * "defined away" - the POSIX wrapper call can be directly mapped to the
 * POSIX function avoiding unneccesary (call and handling) overhead.
 *
 * NOTE: PLEASE KEEP THESE IN SORTED ORDER
 */
#define Posix_Access access
#define Posix_Chdir chdir
#define Posix_Chmod chmod
#define Posix_Chown chown
#define Posix_Creat creat
#define Posix_Dlopen dlopen
#define Posix_Execl execl
#define Posix_Execlp execlp
#define Posix_Execv execv
#define Posix_Execve execve
#define Posix_Execvp execvp
#define Posix_Fopen fopen
#define Posix_Fprintf fprintf
#define Posix_Freopen freopen
#define Posix_Getenv getenv
#define Posix_GetGroupList getgrouplist
#define Posix_Getmntent getmntent
#define Posix_Getmntent_r getmntent_r
#define Posix_Getpwnam getpwnam
#define Posix_Getpwnam_r getpwnam_r
#define Posix_Getpwuid getpwuid
#define Posix_Getpwuid_r getpwuid_r
#define Posix_Getgrnam getgrnam
#define Posix_Getgrnam_r getgrnam_r
#define Posix_Lchown lchown
#define Posix_Link link
#define Posix_Lstat lstat
#define Posix_Mkdir mkdir
#define Posix_Mkfifo mkfifo
#define Posix_Mknod mknod
#define Posix_Mount mount
#define Posix_Open open
#define Posix_OpenDir opendir
#define Posix_Pathconf pathconf
#define Posix_Perror perror
#define Posix_Popen popen
#define Posix_Printf printf
#define Posix_Putenv putenv
#define Posix_Rename rename
#define Posix_Rmdir rmdir
#define Posix_Setenv setenv
#define Posix_Setmntent setmntent
#define Posix_Stat stat
#define Posix_Statfs statfs
#define Posix_Symlink symlink
#define Posix_System system
#define Posix_Truncate truncate
#define Posix_Umount umount
#define Posix_Unlink unlink
#define Posix_Unsetenv unsetenv
#define Posix_Utime utime
#define Posix_Utimes utimes
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // VMWARE_POSIX_H
  07070100000103000081A4000000000000000000000001682255050000073C000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/preference.h   /*********************************************************
 * Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _PREFERENCE_H_
#define _PREFERENCE_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

void Preference_DisableUserPreferences(void);
Bool Preference_Init(void);
void Preference_Exit(void);
Bool Preference_GetBool(Bool defaultValue, const char *fmt);
int32 Preference_GetTriState(int32 defaultValue, const char *fmt);
int32 Preference_GetLong(int32 defaultValue, const char *fmt);
int64 Preference_GetInt64(int64 defaultvalue, const char *fmt);
double Preference_GetDouble(double defaultValue, const char *fmt);
char *Preference_GetString(const char *defaultValue, const char *fmt);

void Preference_Log(void);
char *Preference_GetPathName(const char *defaultValue, const char *fmt);
void Preference_SetFromString(const char *string, Bool overwrite);
Bool Preference_NotSet(const char *fmt);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
07070100000104000081A400000000000000000000000168225505000015CE000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/procMgr.h  /*********************************************************
 * Copyright (C) 2002-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * procMgr.h --
 *
 *    Process management library.
 *
 */


#ifndef __PROCMGR_H__
#   define __PROCMGR_H__

#include "vm_basic_types.h"
#include "auth.h"
#include "dynarray.h"
#if !defined(_WIN32)
#  include <sys/types.h>
#endif
#include <time.h>

/*
 * Keeps track of the platform-specific handle(s) to an asynchronous process.
 */
typedef struct ProcMgr_AsyncProc ProcMgr_AsyncProc;

#if defined(_WIN32)
   typedef DWORD ProcMgr_Pid;
#else /* POSIX */
   typedef pid_t ProcMgr_Pid;
#endif

/*
 * Process information structure.
 * This holds basic information we return per process
 * when listing process information inside the guest.
 */

typedef struct ProcMgrProcInfo {
   ProcMgr_Pid procId;
   char *procCmdName;             // UTF-8
#if defined(__linux__) || defined(_WIN32)
   char *procCmdAbsPath;          // UTF-8
#endif
   char *procCmdLine;             // UTF-8
   char *procOwner;               // UTF-8
#if defined(_WIN32)
   Bool procDebugged;
#endif
   time_t procStartTime;
} ProcMgrProcInfo;

DEFINE_DYNARRAY_TYPE(ProcMgrProcInfo);


typedef struct ProcMgr_ProcArgs {
#if defined(_WIN32)
   /*
    * If a caller needs to use a non-default set of arguments for
    * CreateProcess[AsUser] in ProcMgr_Exec[A]sync, this structure should be used. 
    *
    * - If 'userArgs' is NULL, defaults are used:
    *   - bInheritHandles defaults to TRUE
    *   - lpStartupInfo is instantiated and initialized with:
    *     - cb initialized to size of the object
    *     - dwFlags initialized to STARTF_USESHOWWINDOW
    *     - wShowWindow initialized to SW_MINIMIZE.
    *   - defaults for all other parameters are NULL/FALSE
    *
    * - If 'userArgs' is not NULL, the values in the 'userArgs' object are used
    *   according to the following rules:
    *   - If lpStartupInfo is NULL, it is instantiated and initialized with:
    *     - cb initialized to size of the object
    *     - dwFlags initialized to STARTF_USESHOWWINDOW
    *     - wShowWindow initialized to SW_MINIMIZE.
    *     - The caller would need to do some of this initialization if they set
    *       lpStartupInfo.
    *   - If hToken is set:
    *     - if lpStartupInfo->lpDesktop is not NULL, then it is used directly. Otherwise,
    *       lpStartupInfo->lpDesktop is initialized appropriately.
    *
    *     XXX: Make it more convenient for callers(like ToolsDaemonTcloRunProgramImpl) 
    *     to set just wShowWindow without needing to instantiate and initialize a 
    *     STARTUPINFO object. 
    */
   HANDLE hToken;

   LPCWSTR lpApplicationName;
   LPSECURITY_ATTRIBUTES lpProcessAttributes;
   LPSECURITY_ATTRIBUTES lpThreadAttributes;
   BOOL bInheritHandles;
   DWORD dwCreationFlags;
   LPVOID lpEnvironment;
   LPCWSTR lpCurrentDirectory;
   LPSTARTUPINFO lpStartupInfo;
#else
   /*
    * The environment variables to run the program with. If NULL, use the current
    * environment.
    */
   char **envp;

   /*
    * If non-NULL, the directory to be changed to before the process is
    * started.
    */
   char *workingDirectory;
#endif
} ProcMgr_ProcArgs;




typedef void ProcMgr_Callback(Bool status, void *clientData);

#if defined(_WIN32)
typedef HANDLE Selectable;
#else
typedef int Selectable;
#endif

ProcMgrProcInfoArray *ProcMgr_ListProcesses(void);

#if defined(_WIN32)
ProcMgrProcInfoArray *ProcMgr_ListProcessesEx(Bool useRemoteThreadForCmdLine,
                                              Bool useWMIForCmdLine);
#endif

void ProcMgr_FreeProcList(ProcMgrProcInfoArray *procList);
Bool ProcMgr_KillByPid(ProcMgr_Pid procId);


Bool ProcMgr_ExecSync(char const *cmd,       // UTF-8
                      ProcMgr_ProcArgs *userArgs);
ProcMgr_AsyncProc *ProcMgr_ExecAsync(char const *cmd,     // UTF-8
                                     ProcMgr_ProcArgs *userArgs);
#if defined(__linux__)
Bool ProcMgr_ExecSyncWithExitCode(char const *cmd,
                                  ProcMgr_ProcArgs *userArgs,
                                  Bool *validExitCode,
                                  int *exitCode);
#endif
void ProcMgr_Kill(ProcMgr_AsyncProc *asyncProc);
Selectable ProcMgr_GetAsyncProcSelectable(ProcMgr_AsyncProc *asyncProc);
ProcMgr_Pid ProcMgr_GetPid(ProcMgr_AsyncProc *asyncProc);
Bool ProcMgr_IsAsyncProcRunning(ProcMgr_AsyncProc *asyncProc);
int ProcMgr_GetExitCode(ProcMgr_AsyncProc *asyncProc, int *result);
void ProcMgr_Free(ProcMgr_AsyncProc *asyncProc);
#if !defined(_WIN32)
Bool ProcMgr_ImpersonateUserStart(const char *user,      // UTF-8
                                  AuthToken token);
Bool ProcMgr_ImpersonateUserStop(void);
#endif
Bool ProcMgr_GetImpersonatedUserInfo(char **username, char **homeDir);

#endif /* __PROCMGR_H__ */
  07070100000105000081A400000000000000000000000168225505000006D6000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/product.h  /*********************************************************
 * Copyright (C) 2017-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * product.h --
 *
 *      This file contains the Product enum.
 *
 *      Products that don't want to include productState and vm_basic_types.h
 *      and want to know about the magic values for various products should
 *      include this file.
 */

#ifndef _PRODUCT_H_
#define _PRODUCT_H_

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Public types.
 */

typedef enum {
   PRODUCT_GENERIC      = 0,
   PRODUCT_WORKSTATION  = 1 << 0,
   PRODUCT_ESX          = 1 << 1,
   PRODUCT_PLAYER       = 1 << 2,
   PRODUCT_TOOLS        = 1 << 3,
   PRODUCT_VDM_CLIENT   = 1 << 4,
   PRODUCT_CVP          = 1 << 5,
   PRODUCT_FUSION       = 1 << 6,
   PRODUCT_VIEW         = 1 << 7,
   PRODUCT_VMRC         = 1 << 8,
   PRODUCT_VMACORETESTS = 1 << 9,
   PRODUCT_SRM          = 1 << 10,
   /* etc */
} Product;


#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
  07070100000106000081A4000000000000000000000001682255050000103B000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/productState.h /*********************************************************
 * Copyright (C) 2006-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * productState.h --
 *
 *      ProductState is a runtime encapsulation of the identity of a product
 *      and product dependent characteristics.
 */

#ifndef _PRODUCT_STATE_H_
#define _PRODUCT_STATE_H_

#include "vm_basic_types.h"
#include "product.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef uint64 ProductMask;
#define PRODUCTMASK_HOSTED (PRODUCT_WORKSTATION |\
                            PRODUCT_PLAYER      |\
                            PRODUCT_CVP         |\
                            PRODUCT_FUSION      |\
                            PRODUCT_VMRC)

typedef uint64 ProductCaps;
/*
 * Define as needed.
 *
 * #define PRODUCT_CAP_FOO (1 << 0)
 */

typedef enum {
   PRODUCTSTATE_FLAG_NONE = 0,
   PRODUCTSTATE_FLAG_PRODUCT = 1 << 0,
   PRODUCTSTATE_FLAG_NAME = 1 << 1,
   PRODUCTSTATE_FLAG_VERSION = 1 << 2,
   PRODUCTSTATE_FLAG_BUILDNUMBER = 1 << 3,
   PRODUCTSTATE_FLAG_CAPABILITIES = 1 << 4,
   PRODUCTSTATE_FLAG_LICENSENAME = 1 << 5,
   PRODUCTSTATE_FLAG_LICENSEVERSION = 1 << 6,
   PRODUCTSTATE_FLAG_BUNDLEIDENTIFIER = 1 << 7,
} ProductStateSerializationFlags;

/*
 * Public functions.
 *
 * PR 567850
 * ProductState_Set should only be called once. Subsequent calls will be
 * ignored.
 */

void ProductState_Set(Product product,
                      const char *name,
                      const char *version,
                      unsigned int buildNumber,
                      ProductCaps capabilities,
                      const char *licenseName,
                      const char *licenseVersion,
                      const char *bundleIdentifier);
void ProductState_SetProduct(uint64 product);
void ProductState_SetName(const char *name);
void ProductState_SetLicenseName(const char *licenseName);
unsigned int  ProductState_GetBuildNumber(void);
const char   *ProductState_GetBuildNumberString(void);
const char   *ProductState_GetBundleIdentifier(void);
ProductCaps   ProductState_GetCapabilities(void);
const char   *ProductState_GetCompilationOption(void);
const char   *ProductState_GetConfigName(void);
const char   *ProductState_GetFullVersion(void);
void          ProductState_GetHelp(Product *helpProduct,
                                   const char **helpVersion);
const char   *ProductState_GetLicenseName(void);
const char   *ProductState_GetLicenseVersion(void);
const char   *ProductState_GetName(void);
Product       ProductState_GetProduct(void);
const char   *ProductState_GetRegistryPath(void);
char         *ProductState_GetRegistryPathForProduct(const char *productName);
const char   *ProductState_GetVersion(void);
void          ProductState_GetVersionNumber(unsigned int *major,
                                            unsigned int *minor,
                                            unsigned int *patchLevel);

Bool ProductState_IsProduct(ProductMask product);
Bool ProductState_AllowUnlicensedVMX(void);

void ProductState_SetConfigName(const char *configName);
/* etc */

void ProductState_SetHelp(Product helpProduct,
                          const char *helpVersion);

char *ProductState_Serialize(ProductStateSerializationFlags flags);
ProductStateSerializationFlags ProductState_Deserialize(const char *state);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _PRODUCT_STATE_H_ */
 07070100000107000081A40000000000000000000000016822550500000D75000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/random.h   /*********************************************************
 * Copyright (C) 1998-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * random.h --
 *
 *    Random bits generation. Please use CryptoRandom_GetBytes if
 *    you require a FIPS-compliant source of random data.
 */

#ifndef __RANDOM_H__
#   define __RANDOM_H__

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * OS-native random number generator based on one-way hashes.
 *
 * Good enough for any non-cryptographic use, but slower than
 * alternative algorithms. Recommended for generating seeds.
 *
 * Period: infinite
 * Speed: slow
 */

Bool Random_Crypto(size_t size,
                   void *buffer);

/*
 * The next call to Random_Crypto will fail after this function is called.
 * This function does nothing in a release build.
 */

void Random_CryptoFail(void);

/*
 * Research grade Mersenne Twister random number generator.
 *
 * Period: 2^800
 * Speed: ~23 cycles
 */

typedef struct rqContext rqContext;

rqContext *Random_QuickSeed(uint32 seed);

uint32 Random_Quick(rqContext *context);

/*
 * Good quality non-deterministic random number generator.
 *
 * This generator uses &(*state) as the seed to go beyond 64-bits without
 * additional storage space; the low-grade entropy makes seeding
 * non-deterministic. Multiple generators in the same address space
 * with the same seed will produce unique streams, but using the same
 * seed will NOT produce the same sequence (due to ASLR). See
 * Raondom_FastStream for a deterministic generator.
 *
 * Initialize by setting *state to any seed (including zero) and calling
 * Random_Fast TWICE. (Unless the seed is very good, the first two values
 * are not very random).
 *
 * Period: 2^64
 * Speed: ~10 cycles
 */

uint32 Random_Fast(uint64 *state);
uint64 Random_Fast64(uint64 *state);

static INLINE void
Random_FastSeed(uint64 *state,  // OUT:
                uint64 seed)    // IN:
{
   *state = seed;
   (void) Random_Fast(state);
   (void) Random_Fast(state);
}

/*
 * Good quality deterministic random number generator.
 *
 * Period: 2^64
 * Speed: ~10 cycles
 */

typedef struct {
   uint64 state;
   uint64 sequence;
} RandomFastContext;

uint32 Random_FastStream(RandomFastContext *rfc);

uint64 Random_FastStream64(RandomFastContext *rfc);

void Random_FastStreamSeed(RandomFastContext *rfc,
                           uint64 seed,
                           uint64 seq);

/*
 * Simple multiplicative congruential RNG.
 *
 * Deprecated; prefer Random_Fast for better quality.
 * Period: 2^31-1
 * Speed: ~9 cycles
 */

int Random_Simple(int seed);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* __RANDOM_H__ */
   07070100000108000081A4000000000000000000000001682255050000048F000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/removable_device.h /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _REMOVABLE_DEVICE_H_
#define _REMOVABLE_DEVICE_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#define REMOVABLE_DEVICE_PRETTY_NAME_LENGTH 32

typedef struct {
   char 	name[REMOVABLE_DEVICE_PRETTY_NAME_LENGTH];
   uint32	uid;
   Bool		enabled;
} RD_Info;

#endif
 07070100000109000081A40000000000000000000000016822550500000BBB000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/rpcin.h    /*********************************************************
 * Copyright (C) 2007-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * rpcin.h --
 *
 *    Remote Procedure Call between VMware and guest applications
 *    C declarations
 */


#ifndef __RPCIN_H__
#   define __RPCIN_H__

#ifdef __cplusplus
extern "C" {
#endif

typedef void RpcIn_ErrorFunc(void *clientData, char const *status);

typedef void RpcIn_ClearErrorFunc(void *clientData);

typedef struct RpcIn RpcIn;

#if defined(VMTOOLS_USE_GLIB) /* { */

#include "vmware/tools/guestrpc.h"

RpcIn *RpcIn_Construct(GMainContext *mainCtx,
                       RpcIn_Callback dispatch,
                       gpointer clientData);

Bool RpcIn_start(RpcIn *in, unsigned int delay,
                 RpcIn_ErrorFunc *errorFunc,
                 RpcIn_ClearErrorFunc *clearErrorFunc,
                 void *errorData);

#else /* } { */

#include "dbllnklst.h"

/*
 * Type for old RpcIn callbacks. Don't use this anymore - this is here
 * for backwards compatibility.
 */
typedef Bool
(*RpcIn_Callback)(char const **result,     // OUT
                  size_t *resultLen,       // OUT
                  const char *name,        // IN
                  const char *args,        // IN
                  size_t argsSize,         // IN
                  void *clientData);       // IN

RpcIn *RpcIn_Construct(DblLnkLst_Links *eventQueue);

Bool RpcIn_start(RpcIn *in, unsigned int delay,
                 RpcIn_Callback resetCallback, void *resetClientData,
                 RpcIn_ErrorFunc *errorFunc,
                 RpcIn_ClearErrorFunc *clearErrorFunc,
                 void *errorData);


/*
 * Don't use this function anymore - it's here only for backwards compatibility.
 * Use RpcIn_RegisterCallbackEx() instead.
 */
void RpcIn_RegisterCallback(RpcIn *in, const char *name,
                            RpcIn_Callback callback, void *clientData);

void RpcIn_UnregisterCallback(RpcIn *in, const char *name);

unsigned int RpcIn_SetRetVals(char const **result, size_t *resultLen,
                              const char *resultVal, Bool retVal);

#endif /* } */

void RpcIn_Destruct(RpcIn *in);
void RpcIn_stop(RpcIn *in);

#ifdef __cplusplus
} // extern "C"
#endif

#endif /* __RPCIN_H__ */
 0707010000010A000081A40000000000000000000000016822550500000B6A000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/rpcout.h   /*********************************************************
 * Copyright (C) 2007-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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,
                 Bool *rpcStatus, 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 version of the RpcOut_sendOne 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);

/* 
 * A variant of the RpcOut_SendOneRaw in which the caller supplies the
 * receive buffer so as to avoid the need to call malloc internally.
 * Useful in situations where calling malloc is not allowed.
 */

Bool RpcOut_SendOneRawPreallocated(void *request, size_t reqLen, char *reply,
                                   size_t repLen);

#endif /* __RPCOUT_H__ */
  0707010000010B000081A40000000000000000000000016822550500000A09000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/rpcvmx.h   /*********************************************************
 * Copyright (C) 2004-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * rpcvmx.h --
 *
 *      Simple utility library (usable by guest drivers as well as userlevel
 *      Tools code) that provides some useful VMX interaction capability, e.g.
 *      logging to the VM's VMX log, querying config variables, etc.
 *
 *      NB: This library is *NOT* threadsafe, so if you want to avoid
 *          corrupting your log statements or other screwups, add your own
 *          locking around calls to RpcVMX_Log.
 */

#ifndef _RPCVMX_H_
#define _RPCVMX_H_

#include <stdarg.h>

#include "vm_basic_types.h"
#include "rpcvmxext.h"

#define RPCVMX_MAX_LOG_LEN          (2048) /* 2kb max - make it dynamic? */

/*
 * Set a prefix to prepend to any future log statements.
 */
void RpcVMX_LogSetPrefix(const char *prefix);

/*
 * Get the currently set prefix (returns empty string if no prefix set)
 */
const char *RpcVMX_LogGetPrefix(const char *prefix);

/*
 * Save as RpcVMX_Log but takes a va_list instead of inline arguments.
 */
void RpcVMX_LogV(const char *fmt, va_list args);

/*
 * Get the value of "guestinfo.$key" in the host VMX dictionary and return it.
 * Returns the default if the key is not set.
 */
char *RpcVMX_ConfigGetString(const char *defval, const char *key);

/*
 * Same as _ConfigGetString, but convert the value to a 32-bit quantity.
 * XXX Returns 0, *NOT* the default, if the key was set but the value could
 *     not be converted to an int32.
 */
int32 RpcVMX_ConfigGetLong(int32 defval, const char *key);

/*
 * Same as _ConfigGetString, but convert the value to a Bool. Returns the
 * default value if the key was set but could not be converted.
 */
Bool RpcVMX_ConfigGetBool(Bool defval, const char *key);

#endif /* _VMXRPC_H_ */

   0707010000010C000081A40000000000000000000000016822550500000728000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/rpcvmxext.h    /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * rpcvmxext.h --
 *
 *      Extension of the utility library (usable by guest drivers as well as userlevel
 *      Tools code) that provides some useful VMX interaction capability, e.g.
 *      logging to the VM's VMX log, querying config variables, etc.
 *
 *      NB: This library is *NOT* threadsafe, so if you want to avoid
 *          corrupting your log statements or other screwups, add your own
 *          locking around calls to RpcVMX_Log.
 */

#ifndef _RPCVMXEXT_H_
#define _RPCVMXEXT_H_

#include "vm_basic_types.h"

/*
 * Format the provided string with the provided arguments, and post it to the
 * VMX logfile via RPC.
 */
void RpcVMX_Log(const char *fmt, ...) PRINTF_DECL(1, 2);

/*
 * Report driver name and driver version to vmx to store the key-value in
 * GuestVars, and write a log in vmware.log using RpcVMX_Log.
 */
void RpcVMX_ReportDriverVersion(const char *drivername, const char *versionString);

#endif /* _RPCVMXEXT_H_ */

0707010000010D000081A4000000000000000000000001682255050000103B000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/sha1.h /*********************************************************
 * Copyright (C) 1998-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * sha1.h --
 *
 *	SHA1 encryption
 */

#ifndef _SHA1_H_
#define _SHA1_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

/* for uint32 */
#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif


#if defined __APPLE__ && defined USERLEVEL

/*
 * Apple provides basic crypto functions in its system runtime that are
 * maintained for both speed and security. Use those instead.
 */

#include <CommonCrypto/CommonDigest.h>

#define SHA1_HASH_LEN CC_SHA1_DIGEST_LENGTH
#define SHA1_CTX      CC_SHA1_CTX
#define SHA1Init      CC_SHA1_Init
#define SHA1Update    CC_SHA1_Update
#define SHA1Final     CC_SHA1_Final

#else

/*
 * Prevent linkage conflicts with the SHA1 APIs brought in from
 * OpenSSL. (Pro tip: If you're doing anything security-related, you
 * _must_ be using lib/crypto hash routines to preserve FIPS
 * compatibility.)
 */

#define SHA1Init             VMW_SHA1Init
#define SHA1Update           VMW_SHA1Update
#define SHA1Final            VMW_SHA1Final

/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain

Test Vectors (from FIPS PUB 180-1)
"abc"
  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/

/*
	12/15/98: JEB: Removed main and moved prototypes to sha1.h
			Made SHA1Transform a static function
*/

#define	SHA1_HASH_LEN	20

typedef struct SHA1_CTX {
    uint32 state[5];
    uint32 count[2];
    unsigned char buffer[64];
} SHA1_CTX;

void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context,
                const unsigned char *data,
                size_t len);
void SHA1Final(unsigned char digest[SHA1_HASH_LEN], SHA1_CTX* context);

#endif // defined __APPLE__ && defined USERLEVEL

/* Opaque handle */
typedef union {
#if defined __APPLE__
   uint8 _private[104 + 8];  // sizeof CC_SHA256_CTX + extra field,
                             // where SHA256 is largest CTX
#elif defined _WIN32
   uint8 _private[384];      // see CryptoHashInitCommon
#else
   uintptr_t _private;
#endif
} CryptoHash_SHA1_CTX;

void CryptoHash_InitSHA1(CryptoHash_SHA1_CTX *ctx);
void CryptoHash_UpdateSHA1(CryptoHash_SHA1_CTX *ctx,
                           const void *data, size_t len);
void CryptoHash_FinalSHA1(CryptoHash_SHA1_CTX *ctx,
                          unsigned char digest[SHA1_HASH_LEN]);
void CryptoHash_ComputeSHA1(const void *data, size_t len,
                            unsigned char digest[SHA1_HASH_LEN]);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _SHA1_H_
 0707010000010E000081A40000000000000000000000016822550500003929000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/sigPosixRegs.h /*********************************************************
 * Copyright (C) 2009-2017,2020-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * sigPosixRegs.h --
 *
 *      Platform-specific definitions for saved CPU registers inside
 *      ucontext_t. These aren't part of sigPosix.h since few source
 *      files need them, and this header is a bit invasive, and it must
 *      be defined before system headers.
 */

#ifndef _SIGPOSIXREGS_H_
#define _SIGPOSIXREGS_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#if defined(__cplusplus)
extern "C" {
#endif

#if __linux__         // We need the REG_foo offsets in the gregset_t;
#  define _GNU_SOURCE // _GNU_SOURCE maps to __USE_GNU

/* And, the REG_foo definitions conflict with our own in x86/regname_arch.h */
#  if defined(__x86_64__)
#    define REG_RAX GNU_REG_RAX
#    define REG_RBX GNU_REG_RBX
#    define REG_RCX GNU_REG_RCX
#    define REG_RDX GNU_REG_RDX
#    define REG_RSI GNU_REG_RSI
#    define REG_RDI GNU_REG_RDI
#    define REG_RSP GNU_REG_RSP
#    define REG_RBP GNU_REG_RBP
#    define REG_RIP GNU_REG_RIP
#    define REG_R8  GNU_REG_R8
#    define REG_R9  GNU_REG_R9
#    define REG_R10 GNU_REG_R10
#    define REG_R11 GNU_REG_R11
#    define REG_R12 GNU_REG_R12
#    define REG_R13 GNU_REG_R13
#    define REG_R14 GNU_REG_R14
#    define REG_R15 GNU_REG_R15
#  elif defined(__i386__)
#    define REG_EAX GNU_REG_EAX
#    define REG_EBX GNU_REG_EBX
#    define REG_ECX GNU_REG_ECX
#    define REG_EDX GNU_REG_EDX
#    define REG_ESI GNU_REG_ESI
#    define REG_EDI GNU_REG_EDI
#    define REG_ESP GNU_REG_ESP
#    define REG_EBP GNU_REG_EBP
#    define REG_EIP GNU_REG_EIP
#  endif
#endif

#include <signal.h>
#include <sys/ucontext.h>

#if __linux__ && !defined __ANDROID__
#  if defined(__x86_64__)
#    undef REG_RAX
#    undef REG_RBX
#    undef REG_RCX
#    undef REG_RDX
#    undef REG_RSI
#    undef REG_RDI
#    undef REG_RSP
#    undef REG_RBP
#    undef REG_RIP
#    undef REG_R8
#    undef REG_R9
#    undef REG_R10
#    undef REG_R11
#    undef REG_R12
#    undef REG_R13
#    undef REG_R14
#    undef REG_R15
#  elif defined(__i386__)
#    undef REG_EAX
#    undef REG_EBX
#    undef REG_ECX
#    undef REG_EDX
#    undef REG_ESI
#    undef REG_EDI
#    undef REG_ESP
#    undef REG_EBP
#    undef REG_EIP
#  endif
#endif

#if defined(__APPLE__)
#if __DARWIN_UNIX03
#ifdef __x86_64__
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rax)
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rbx)
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rcx)
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rdx)
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rdi)
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rsi)
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rbp)
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rsp)
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__rip)
#define SC_R8(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r8)
#define SC_R9(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r9)
#define SC_R10(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__r10)
#define SC_R11(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__r11)
#define SC_R12(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__r12)
#define SC_R13(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__r13)
#define SC_R14(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__r14)
#define SC_R15(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__r15)
#elif defined(__arm__)
#define SC_R0(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[0])
#define SC_R1(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[1])
#define SC_R2(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[2])
#define SC_R3(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[3])
#define SC_R4(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[4])
#define SC_R5(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[5])
#define SC_R6(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[6])
#define SC_R7(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[7])
#define SC_R8(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[8])
#define SC_R9(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[9])
#define SC_R10(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__r[10])
#define SC_FP(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[11])
#define SC_IP(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__r[12])
#define SC_SP(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__sp)
#define SC_LR(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__lr)
#define SC_PC(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__pc)
#elif defined(__aarch64__)
#define SC_X(uc,n) ((unsigned long) (uc)->uc_mcontext->__ss.__x[n])
#define SC_FP(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__x[29])
#define SC_SP(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__sp)
#define SC_PC(uc)  ((unsigned long) (uc)->uc_mcontext->__ss.__pc)
#define SC_PSR(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__cpsr)
#else
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__eax)
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__ebx)
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__ecx)
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__edx)
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__edi)
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__esi)
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__ebp)
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__esp)
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext->__ss.__eip)
#endif
#else
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext->ss.eax)
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext->ss.ebx)
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext->ss.ecx)
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext->ss.edx)
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext->ss.edi)
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext->ss.esi)
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext->ss.ebp)
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext->ss.esp)
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext->ss.eip)
#endif
#elif defined(__FreeBSD__)
#ifdef __x86_64__
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext.mc_rax)
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext.mc_rbx)
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext.mc_rcx)
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext.mc_rdx)
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext.mc_rdi)
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext.mc_rsi)
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext.mc_rbp)
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext.mc_rsp)
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext.mc_rip)
#define SC_R8(uc)  ((unsigned long) (uc)->uc_mcontext.mc_r8)
#define SC_R9(uc)  ((unsigned long) (uc)->uc_mcontext.mc_r9)
#define SC_R10(uc) ((unsigned long) (uc)->uc_mcontext.mc_r10)
#define SC_R11(uc) ((unsigned long) (uc)->uc_mcontext.mc_r11)
#define SC_R12(uc) ((unsigned long) (uc)->uc_mcontext.mc_r12)
#define SC_R13(uc) ((unsigned long) (uc)->uc_mcontext.mc_r13)
#define SC_R14(uc) ((unsigned long) (uc)->uc_mcontext.mc_r14)
#define SC_R15(uc) ((unsigned long) (uc)->uc_mcontext.mc_r15)
#elif defined(__aarch64__)
#define SC_X(uc,n) ((unsigned long) (uc)->uc_mcontext.mc_gpregs.gp_x[n])
#else
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext.mc_eax)
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext.mc_ebx)
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext.mc_ecx)
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext.mc_edx)
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext.mc_edi)
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext.mc_esi)
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext.mc_ebp)
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext.mc_esp)
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext.mc_eip)
#endif
#elif defined (sun)
#ifdef __x86_64__
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RAX])
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RBX])
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RCX])
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RDX])
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RDI])
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RSI])
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RBP])
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RSP])
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_RIP])
#define SC_R8(uc)  ((unsigned long) (uc)->uc_mcontext.gregs[REG_R8])
#define SC_R9(uc)  ((unsigned long) (uc)->uc_mcontext.gregs[REG_R9])
#define SC_R10(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_R10])
#define SC_R11(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_R11])
#define SC_R12(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_R12])
#define SC_R13(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_R13])
#define SC_R14(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_R14])
#define SC_R15(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_R15])
#else
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[EAX])
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[EBX])
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[ECX])
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[EDX])
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[EDI])
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[ESI])
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[EBP])
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[ESP])
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[EIP])
#endif
#elif defined(ANDROID_X86)
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_EAX])
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_EBX])
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_ECX])
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_EDX])
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_EDI])
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_ESI])
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_EBP])
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_ESP])
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[REG_EIP])
#else
#ifdef __x86_64__
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RAX])
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RBX])
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RCX])
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RDX])
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RDI])
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RSI])
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RBP])
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RSP])
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_RIP])
#define SC_R8(uc)  ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_R8])
#define SC_R9(uc)  ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_R9])
#define SC_R10(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_R10])
#define SC_R11(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_R11])
#define SC_R12(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_R12])
#define SC_R13(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_R13])
#define SC_R14(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_R14])
#define SC_R15(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_R15])
#elif defined(__arm__)
#define SC_R0(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r0)
#define SC_R1(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r1)
#define SC_R2(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r2)
#define SC_R3(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r3)
#define SC_R4(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r4)
#define SC_R5(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r5)
#define SC_R6(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r6)
#define SC_R7(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r7)
#define SC_R8(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r8)
#define SC_R9(uc)  ((unsigned long) (uc)->uc_mcontext.arm_r9)
#define SC_R10(uc) ((unsigned long) (uc)->uc_mcontext.arm_r10)
#define SC_FP(uc)  ((unsigned long) (uc)->uc_mcontext.arm_fp)
#define SC_IP(uc)  ((unsigned long) (uc)->uc_mcontext.arm_ip)
#define SC_SP(uc)  ((unsigned long) (uc)->uc_mcontext.arm_sp)
#define SC_LR(uc)  ((unsigned long) (uc)->uc_mcontext.arm_lr)
#define SC_PC(uc)  ((unsigned long) (uc)->uc_mcontext.arm_pc)
#elif defined(__aarch64__)
#define SC_X(uc,n) ((unsigned long) (uc)->uc_mcontext.regs[n])
#define SC_FP(uc)  ((unsigned long) (uc)->uc_mcontext.regs[29])
#define SC_SP(uc)  ((unsigned long) (uc)->uc_mcontext.sp)
#define SC_PC(uc)  ((unsigned long) (uc)->uc_mcontext.pc)
#define SC_PSR(uc) ((unsigned long) (uc)->uc_mcontext.pstate)
#else
#define SC_EAX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_EAX])
#define SC_EBX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_EBX])
#define SC_ECX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_ECX])
#define SC_EDX(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_EDX])
#define SC_EDI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_EDI])
#define SC_ESI(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_ESI])
#define SC_EBP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_EBP])
#define SC_ESP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_ESP])
#define SC_EIP(uc) ((unsigned long) (uc)->uc_mcontext.gregs[GNU_REG_EIP])
#endif
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // ifndef _SIGPOSIXREGS_H_
   0707010000010F000081A400000000000000000000000168225505000019F7000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/sigc++2to3.h   /*********************************************************
 * Copyright (C) 2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * sigc++2to3.h --
 *
 *     A header file that contain definations that will allow some sigc++2 APIs
 *     to work when builing with sigc++3.
 */

#pragma once


#include <type_traits>
#include <sigc++config.h>
#include <sigc++/sigc++.h>


#if SIGCXX_MAJOR_VERSION >= 3

namespace sigc {


// Bridge slot syntax change
template <typename T_ret, typename... T_args>
class slot : public slot<T_ret(T_args...)> {
private:
   using _true_slot = slot<T_ret(T_args...)>;
public:
   inline slot() = default;
   inline slot(const _true_slot& src) : _true_slot(src) {}
   template<typename T_functor>
   inline slot(const T_functor& func) : _true_slot(func) {}
};

template <typename T_ret>
using slot0 = slot<T_ret()>;
template <typename T_ret, typename T_arg1>
using slot1 = slot<T_ret(T_arg1)>;
template <typename T_ret, typename T_arg1, typename T_arg2>
using slot2 = slot<T_ret(T_arg1, T_arg2)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3>
using slot3 = slot<T_ret(T_arg1, T_arg2, T_arg3)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4>
using slot4 = slot<T_ret(T_arg1, T_arg2, T_arg3, T_arg4)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5>
using slot5 = slot<T_ret(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5, typename T_arg6>
using slot6 = slot<T_ret(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5, T_arg6)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5, typename T_arg6, typename T_arg7>
using slot7 = slot<T_ret(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5, T_arg6, T_arg7)>;


// Bridge signal syntax change
template <typename T_ret, typename... T_args>
class signal : public signal<T_ret(T_args...)>, public trackable {
public:
   signal() = default;

   decltype(auto) make_slot() const {
      return mem_fun(*this, &signal::emit);
   }
};

template <typename T_ret>
using signal0 = signal<T_ret()>;
template <typename T_ret, typename T_arg1>
using signal1 = signal<T_ret(T_arg1)>;
template <typename T_ret, typename T_arg1, typename T_arg2>
using signal2 = signal<T_ret(T_arg1, T_arg2)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3>
using signal3 = signal<T_ret(T_arg1, T_arg2, T_arg3)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4>
using signal4 = signal<T_ret(T_arg1, T_arg2, T_arg3, T_arg4)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5>
using signal5 = signal<T_ret(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5, typename T_arg6>
using signal6 = signal<T_ret(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5, T_arg6)>;
template <typename T_ret, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5, typename T_arg6, typename T_arg7>
using signal7 = signal<T_ret(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5, T_arg6, T_arg7)>;


//Bridge bound_mem_functor
template <typename T_ret, typename T_obj>
using bound_mem_functor0 = bound_mem_functor<T_ret (T_obj::*)()>;
template <typename T_ret, typename T_obj, typename T_arg1>
using bound_mem_functor1 = bound_mem_functor<T_ret (T_obj::*)(T_arg1), T_arg1>;
template <typename T_ret, typename T_obj, typename T_arg1, typename T_arg2>
using bound_mem_functor2 = bound_mem_functor<T_ret (T_obj::*)(T_arg1, T_arg2), T_arg1, T_arg2>;
template <typename T_ret, typename T_obj, typename T_arg1, typename T_arg2, typename T_arg3>
using bound_mem_functor3 = bound_mem_functor<T_ret (T_obj::*)(T_arg1, T_arg2, T_arg3), T_arg1, T_arg2, T_arg3>;
template <typename T_ret, typename T_obj, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4>
using bound_mem_functor4 = bound_mem_functor<T_ret (T_obj::*)(T_arg1, T_arg2, T_arg3, T_arg4), T_arg1, T_arg2, T_arg3, T_arg4>;
template <typename T_ret, typename T_obj, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5>
using bound_mem_functor5 = bound_mem_functor<T_ret (T_obj::*)(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5), T_arg1, T_arg2, T_arg3, T_arg4, T_arg5>;
template <typename T_ret, typename T_obj, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5, typename T_arg6>
using bound_mem_functor6 = bound_mem_functor<T_ret (T_obj::*)(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5, T_arg6), T_arg1, T_arg2, T_arg3, T_arg4, T_arg5, T_arg6>;
template <typename T_ret, typename T_obj, typename T_arg1, typename T_arg2, typename T_arg3, typename T_arg4, typename T_arg5, typename T_arg6, typename T_arg7>
using bound_mem_functor7 = bound_mem_functor<T_ret (T_obj::*)(T_arg1, T_arg2, T_arg3, T_arg4, T_arg5, T_arg6, T_arg7), T_arg1, T_arg2, T_arg3, T_arg4, T_arg5, T_arg6, T_arg7>;



// Add old mem_fun API for pointer
template<typename T_return, typename T_obj, typename... T_arg>
inline decltype(auto)
mem_fun(T_obj *obj, T_return (T_obj::*func)(T_arg...))
{
   return bound_mem_functor<T_return (T_obj::*)(T_arg...), T_arg...>(*obj, func);
}

template<typename T_return, typename T_obj, typename... T_arg>
inline decltype(auto)
mem_fun(const T_obj *obj, T_return (T_obj::*func)(T_arg...) const)
{
   return bound_mem_functor<T_return (T_obj::*)(T_arg...) const, T_arg...>(*obj, func);
}


// Stub sigc::ref impl
template <typename T>
inline decltype(auto) ref(T& t) { return std::ref(t); }
template <typename T>
inline decltype(auto) ref(const T& t) { return std::cref(t); }


} //namespace sigc

#endif // SIGCXX_MAJOR_VERSION >= 3
 07070100000110000081A40000000000000000000000016822550500000584000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/slashProc.h    /*********************************************************
 * Copyright (C) 2009-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file slashProc.h
 */

#ifndef _SLASHPROC_H_
#define _SLASHPROC_H_

#include <glib.h>
#include <net/route.h>


/*
 * Global functions
 */


EXTERN GHashTable *SlashProcNet_GetSnmp(void);
EXTERN GHashTable *SlashProcNet_GetSnmp6(void);

EXTERN GPtrArray  *SlashProcNet_GetRoute(unsigned int, unsigned short);
EXTERN void        SlashProcNet_FreeRoute(GPtrArray *);

EXTERN GPtrArray  *SlashProcNet_GetRoute6(unsigned int, unsigned int);
EXTERN void        SlashProcNet_FreeRoute6(GPtrArray *);

#endif // ifndef _SLASHPROC_H_
07070100000111000081A4000000000000000000000001682255050000091D000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/sslDirect.h    /*********************************************************
 * Copyright (c) 2014-2016,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * sslDirect.h --
 *
 *	declarations in ssl.h that are required by AsyncSocket.
 *
 */

#ifndef _SSLDIRECT_H_
#define _SSLDIRECT_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

typedef struct _SSLVerifyParam SSLVerifyParam;
typedef struct SSLSockStruct *SSLSock;
typedef char* (SSLLibFn)(const char*, const char*);

void SSL_Init(SSLLibFn *getLibFn, const char *defaultLib, const char *name);
SSLSock SSL_New(int fd,Bool closeFdOnShutdown);
void SSL_SetCloseOnShutdownFlag(SSLSock ssl);
Bool SSL_SetupAcceptWithContext(SSLSock sSock, void *ctx);
int SSL_TryCompleteAccept(SSLSock sSock);
ssize_t SSL_Read(SSLSock ssl, char *buf, size_t num);
ssize_t SSL_RecvDataAndFd(SSLSock ssl, char *buf, size_t num, int *fd);
ssize_t SSL_Write(SSLSock ssl, const char  *buf, size_t num);
int SSL_Shutdown(SSLSock ssl);
int SSL_GetFd(SSLSock sSock);
int SSL_Pending(SSLSock ssl);
int SSL_WantRead(const SSLSock ssl);


#ifdef _WIN32
#define SSLGeneric_read(sock,buf,num) recv(sock,buf,num,0)
#define SSLGeneric_write(sock,buf,num) send(sock,buf,num,0)
#define SSLGeneric_recvmsg(sock,msg,flags) recvmsg(sock,msg,flags)
#define SSLGeneric_close(sock) closesocket(sock)
#else
#define SSLGeneric_read(sock,buf,num) read(sock, buf, num)
#define SSLGeneric_write(sock,buf,num) write(sock, buf,num)
#define SSLGeneric_recvmsg(sock,msg,flags) recvmsg(sock,msg,flags)
#define SSLGeneric_close(sock) close(sock)
#endif

#endif // ifndef _SSLDIRECT_H_

   07070100000112000081A40000000000000000000000016822550500002350000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/str.h  /*********************************************************
 * Copyright (C) 1998-2020,2022-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * str.h --
 *
 *    string wrapping functions
 */

#ifndef _STR_H_
#define _STR_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#if defined(__linux__)
#include <wchar.h>
#elif defined(_WIN32)
#include <tchar.h>
#elif __APPLE__
#include <stdlib.h>
#endif
#include "compat/compat_stdarg.h" // Provides stdarg.h plus va_copy

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif



/*
 * These platforms use bsd_vsnprintf().
 *
 * XXX: open-vm-tools does not use bsd_vsnprintf because bsd_vsnprintf uses
 * convertutf.{h,c}, and the license for those files does not meet the
 * redistribution requirements for Debian.
 * <https://github.com/vmware/open-vm-tools/issues/148>
 */
#if !defined(VMX86_TOOLS) || defined(_WIN32)
#if (defined _WIN32 && !defined STR_NO_WIN32_LIBS) || \
    (defined __linux__ && !defined __UCLIBC__ && !defined __ANDROID__) || \
    defined __APPLE__ || defined __EMSCRIPTEN__
#define HAS_BSD_PRINTF 1
#endif
#endif

/*
 * ASCII/UTF-8 versions
 *
 * NOTE: All size_t arguments and integer returns values are in bytes.
 *
 * NOTE: Str_Asprintf/Str_Vasprintf return NULL on failure, while
 * Str_SafeAsprintf/Str_SafeVasprintf VERIFY.
 *
 * NOTE: "%s" refers to strings of "char" units, while "%S" refers to
 * strings of "wchar_t" units, regardless of platform.
 */

#ifdef HAS_BSD_PRINTF
int Str_Sprintf_C_Locale(char *buf,               // OUT:
                         size_t max,              // IN:
                         const char *fmt,         // IN:
                         ...) PRINTF_DECL(3, 4);  // IN:
#endif

int Str_Sprintf(char *buf,               // OUT:
                size_t max,              // IN:
                const char *fmt,         // IN:
                ...) PRINTF_DECL(3, 4);  // IN:
int Str_Snprintf(char *buf,               // OUT:
                 size_t len,              // IN:
                 const char *fmt,         // IN:
                 ...) PRINTF_DECL(3, 4);  // IN:
int Str_Vsnprintf(char *buf,        // OUT:
                  size_t len,       // IN:
                  const char *fmt,  // IN:
                  va_list args);    // IN:

size_t Str_Strlen(const char *src,  // IN:
                  size_t maxLen);   // IN:
char *Str_Strnstr(const char *src,  // IN:
                  const char *sub,  // IN:
                  size_t n);        // IN:
char *Str_Strcpy(char *dst,        // OUT:
                 const char *src,  // IN:
                 size_t maxLen);   // IN:
char *Str_Strncpy(char *dest,       // OUT:
                  size_t destSize,  // IN:
                  const char *src,  // IN:
                  size_t n);        // IN:
char *Str_Strcat(char *dst,        // IN/OUT:
                 const char *src,  // IN:
                 size_t maxLen);   // IN:
char *Str_Strncat(char *buf,        // IN/OUT:
                  size_t bufSize,   // IN:
                  const char *src,  // IN:
                  size_t n);        // IN:

char *Str_Asprintf(size_t *length,          // OUT/OPT:
                   const char *format,      // IN:
                   ...) PRINTF_DECL(2, 3);  // IN:
char *Str_Vasprintf(size_t *length,      // OUT/OPT:
                    const char *format,  // IN:
                    va_list arguments);  // IN:
char *Str_SafeAsprintf(size_t *length,          // OUT/OPT:
                       const char *format,      // IN:
                       ...) PRINTF_DECL(2, 3);  // IN:
char *Str_SafeVasprintf(size_t *length,      // OUT/OPT:
                        const char *format,  // IN:
                        va_list arguments);  // IN:

#if defined(_WIN32) // {

/*
 * wchar_t versions
 *
 * NOTE: All size_t arguments and integer return values are in
 * wchar_ts, not bytes.
 *
 * NOTE: Str_Aswprintf/Str_Vaswprintf return NULL on failure, while
 * Str_SafeAswprintf/Str_SafeVaswprintf VERIFY.
 *
 * NOTE: "%s" refers to strings of "char" units, while "%S" refers to
 * strings of "wchar_t" units, regardless of platform.
 */

int Str_Swprintf(wchar_t *buf,        // OUT:
                 size_t max,          // IN:
                 const wchar_t *fmt,  // IN:
                 ...);
int Str_Snwprintf(wchar_t *buf,        // OUT:
                  size_t len,          // IN:
                  const wchar_t *fmt,  // IN:
                  ...);
int Str_Vsnwprintf(wchar_t *buf,        // OUT:
                   size_t len,          // IN:
                   const wchar_t *fmt,  // IN:
                   va_list args);
wchar_t *Str_Wcscpy(wchar_t *dst,        // OUT:
                    const wchar_t *src,  // IN:
                    size_t maxLen);      // IN:
wchar_t *Str_Wcscat(wchar_t *dst,        // IN/OUT:
                    const wchar_t *src,  // IN:
                    size_t maxLen);      // IN:
wchar_t *Str_Wcsncat(wchar_t *buf,        // IN/OUT:
                     size_t bufSize,      // IN:
                     const wchar_t *src,  // IN:
                     size_t n);           // IN:

wchar_t *Str_Aswprintf(size_t *length,         // OUT/OPT:
                       const wchar_t *format,  // IN:
                       ...);                   // IN:
wchar_t *Str_Vaswprintf(size_t *length,         // OUT/OPT:
                        const wchar_t *format,  // IN:
                        va_list arguments);     // IN:
wchar_t *Str_SafeAswprintf(size_t *length,         // OUT/OPT:
                           const wchar_t *format,  // IN:
                           ...);                   // IN:
wchar_t *Str_SafeVaswprintf(size_t *length,         // OUT/OPT:
                            const wchar_t *format,  // IN:
                            va_list arguments);     // IN:

/*
 * These are handly for Windows programmers.  They are like
 * the _tcs functions, but with Str_Strcpy-style bounds checking.
 */

#ifdef UNICODE
   #define Str_Tcscpy(s1, s2, n) Str_Wcscpy(s1, s2, n)
   #define Str_Tcscat(s1, s2, n) Str_Wcscat(s1, s2, n)
#else
   #define Str_Tcscpy(s1, s2, n) Str_Strcpy(s1, s2, n)
   #define Str_Tcscat(s1, s2, n) Str_Strcat(s1, s2, n)
#endif

#endif // } defined(_WIN32)


/*
 * Wrappers for standard string functions
 *
 * These are either for Windows-Posix compatibility,
 * or just gratuitous wrapping for consistency.
 */

#define Str_Strcmp(s1, s2) strcmp(s1, s2)
#define Str_Strncmp(s1, s2, n) strncmp(s1, s2, n)
#define Str_Strequal(s1,s2) (0 == (Str_Strcmp(s1, s2)))

#define Str_Strchr(s, c) strchr(s, c)
#define Str_Strrchr(s, c) strrchr(s, c)
#define Str_Strspn(s1, s2) strspn(s1, s2)
#define Str_Strcspn(s1, s2) strcspn(s1, s2)

#if defined(_WIN32)
   #define Str_Strcasecmp(s1, s2) _stricmp(s1, s2)
   #define Str_Strncasecmp(s1, s2, n) _strnicmp(s1, s2, n)
   #define Str_ToUpper(s) _strupr(s)
   #define Str_ToLower(s) _strlwr(s)
#else
   #define Str_Strcasecmp(s1, s2) strcasecmp(s1, s2)
   #define Str_Strncasecmp(s1, s2, n) strncasecmp(s1, s2, n)
   char *Str_ToUpper(char *string);
   char *Str_ToLower(char *string);
#endif

#ifdef _WIN32
   #define Str_Tcscmp(s1, s2) _tcscmp(s1, s2)
   #define Str_Tcsncmp(s1, s2, n) _tcsncmp(s1, s2, n)
   #define Str_Tcsicmp(s1, s2) _tcsicmp(s1, s2)
   #define Str_Tcsnicmp(s1, s2, n) _tcsnicmp(s1, s2, n)
   #define Str_Tcschr(s, c) _tcschr(s, c)
   #define Str_Tcsrchr(s, c) _tcsrchr(s, c)
   #define Str_Tcsspn(s1, s2) _tcsspn(s1, s2)
   #define Str_Tcscspn(s1, s2) _tcscspn(s1, s2)
   #define Str_Tcsupr(s) _tcsupr(s)
   #define Str_Tcslwr(s) _tcslwr(s)
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _STR_H_ */
07070100000113000081A4000000000000000000000001682255050000100B000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/strutil.h  /*********************************************************
 * Copyright (c) 1998-2018, 2021-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * strutil.h --
 *
 *    String utility functions.
 */


#ifndef STRUTIL_H
#define STRUTIL_H

#include <stdarg.h>
#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

struct DynBuf;

char *StrUtil_GetNextToken(unsigned int *index, const char *str,
                           const char *delimiters);
#if defined(_WIN32)
wchar_t *StrUtil_GetNextTokenW(unsigned int *index, const wchar_t *str,
                               const wchar_t *delimiters);
#endif
Bool StrUtil_GetNextIntToken(int32 *out, unsigned int *index, const char *str,
                             const char *delimiters);
Bool StrUtil_GetNextUintToken(uint32 *out, unsigned int *index, const char *str,
                              const char *delimiters);
Bool StrUtil_GetNextInt64Token(int64 *out, unsigned int *index, const char *str,
                              const char *delimiters);
Bool StrUtil_DecimalStrToUint(unsigned int *out, const char **str);
Bool StrUtil_StrToInt(int32 *out, const char *str);
Bool StrUtil_StrToUint(uint32 *out, const char *str);
Bool StrUtil_StrToInt64(int64 *out, const char *str);
Bool StrUtil_StrToUint64(uint64 *out, const char *str);
Bool StrUtil_StrToSizet(size_t *out, const char *str);
Bool StrUtil_StrToDouble(double *out, const char *str);
Bool StrUtil_CapacityToBytes(SectorType *out, const char *str,
                             unsigned int bytes);
Bool StrUtil_CapacityToSectorType(SectorType *out, const char *str,
                                  unsigned int bytes);
char *StrUtil_FormatSizeInBytesUnlocalized(uint64 size);

size_t StrUtil_GetLongestLineLength(const char *buf, size_t bufLength);

Bool StrUtil_StartsWith(const char *s, const char *prefix);
Bool StrUtil_CaselessStartsWith(const char *s, const char *prefix);
Bool StrUtil_EndsWith(const char *s, const char *suffix);
Bool StrUtil_CaselessEndsWith(const char *s, const char *suffix);
const char * StrUtil_CaselessStrstr(const char *str, const char *strSearch);
Bool StrUtil_IsASCII(const char *s);

Bool StrUtil_VDynBufPrintf(struct DynBuf *b, const char *fmt, va_list args);
Bool StrUtil_DynBufPrintf(struct DynBuf *b, const char *fmt, ...) PRINTF_DECL(2, 3);
void StrUtil_SafeDynBufPrintf(struct DynBuf *b, const char *fmt, ...) PRINTF_DECL(2, 3);

void StrUtil_SafeStrcat(char **prefix, const char *str);
void StrUtil_SafeStrcatFV(char **prefix, const char *fmt, va_list args);
void StrUtil_SafeStrcatF(char **prefix, const char *fmt, ...) PRINTF_DECL(2, 3);

char *StrUtil_TrimWhitespace(const char *str);

char *StrUtil_ReplaceAll(const char *orig, const char *what, const char *with);

char *StrUtil_GetNextItem(char **list, char delim);

char *StrUtil_GetLastItem(char **list, char delim);

Bool StrUtil_HasListItem(char const *list, char delim, char const *item);

Bool StrUtil_HasListItemCase(char const *list, char delim, char const *item);

char *StrUtil_AppendListItem(char const *list, char delim, char const *item);

void StrUtil_RemoveListItem(char * const list, char delim, char const *item);

void StrUtil_RemoveListItemCase(char * const list, char delim,
                                char const *item);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* STRUTIL_H */
 07070100000114000081A40000000000000000000000016822550500000E56000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/include/su.h   /*********************************************************
 * Copyright (C) 1998-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * su.h --
 *
 *   Manage super-user priviledges
 *
 */

#ifndef USER_SU_H
#define USER_SU_H

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "vm_assert.h"

#if defined(__cplusplus)
extern "C" {
#endif

#if defined(__APPLE__)

#include <sys/types.h>
#include <unistd.h>

int Id_SetGid(gid_t egid);
int Id_SetREUid(uid_t ruid, uid_t euid);
int Id_SetRESUid(uid_t ruid, uid_t euid, uid_t suid);

#define Id_GetEUid() geteuid()

void *Id_AuthGetLocal();
void *Id_AuthGetExternal(size_t *size);
Bool Id_AuthSet(void const *buf, size_t size);
Bool Id_AuthCheck(char const *right,
                  char const *localizedDescription,
                  Bool showDialogIfNeeded);

#elif defined(__linux__) || defined(sun) || defined(__FreeBSD__) || \
      defined(__EMSCRIPTEN__)

#include <sys/types.h>
#include <unistd.h>

/* Our set of set*id functions which affect current thread only */
int Id_SetUid(uid_t euid);
int Id_SetGid(gid_t egid);
int Id_SetREUid(uid_t ruid, uid_t euid);
int Id_SetREGid(gid_t rgid, gid_t egid);
int Id_SetRESUid(uid_t ruid, uid_t euid, uid_t suid);
int Id_SetRESGid(gid_t rgid, gid_t egid, gid_t sgid);

/* For symmetry */
#define Id_GetEUid() geteuid()

/*
 *----------------------------------------------------------------------------
 *
 * Id_SetEUid --
 *
 *      Set specified effective uid for current thread.  Does not affect
 *      real uid or saved uid.
 *
 * Results:
 *	0 on success, -1 on failure, errno set
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

static INLINE int
Id_SetEUid(uid_t euid)
{
   return Id_SetRESUid((uid_t)-1, euid, (uid_t)-1);
}


/*
 *----------------------------------------------------------------------------
 *
 * Id_SetEGid --
 *
 *      Set specified effective gid for current thread.  Does not affect
 *      real gid or saved gid.
 *
 * Results:
 *      0 on success, -1 on failure, errno set
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

static INLINE int
Id_SetEGid(gid_t egid)
{
   return Id_SetRESGid((gid_t)-1, egid, (gid_t)-1);
}
#endif /* linux */

#if defined(_WIN32)
static INLINE int Id_BeginSuperUser(void) { return -1; }
static INLINE void Id_EndSuperUser(int id) { }
static INLINE Bool Id_IsSuperUser(void) { return TRUE; }
static INLINE Bool Id_IsSetUGid(void) { return FALSE; }
#else
static INLINE Bool Id_IsSuperUser(void) { return 0 == geteuid(); }
uid_t Id_BeginSuperUser(void);
void Id_EndSuperUser(uid_t uid);
Bool Id_IsSetUGid(void);
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* USER_SU_H */

  07070100000115000081A400000000000000000000000168225505000007FF000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/syncDriver.h   /*********************************************************
 * Copyright (c) 2005-2018, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * syncDriver.h --
 *
 *    Interface to the Sync Driver.
 */

#ifndef _SYNC_DRIVER_H_
#define _SYNC_DRIVER_H_

#include "vm_basic_types.h"

#if defined(_WIN32) /* { */

# include <windows.h>
# define SYNCDRIVER_INVALID_HANDLE INVALID_HANDLE_VALUE
typedef HANDLE SyncDriverHandle;

#else /* }{ POSIX */

# define INFINITE -1
# define SYNCDRIVER_INVALID_HANDLE NULL

typedef struct SyncHandle * SyncDriverHandle;

#endif /* } */

typedef enum {
   SYNCDRIVER_IDLE,
   SYNCDRIVER_BUSY,
   SYNCDRIVER_ERROR
} SyncDriverStatus;

Bool SyncDriver_Init(void);
Bool SyncDriver_Freeze(const char *drives, Bool enableNullDriver,
                       SyncDriverHandle *handle,
                       const char *excludedFileSystems,
                       Bool ignoreFrozenFS);
Bool SyncDriver_Thaw(const SyncDriverHandle handle);
SyncDriverStatus SyncDriver_QueryStatus(const SyncDriverHandle handle,
                                        int32 timeout);
void SyncDriver_CloseHandle(SyncDriverHandle *handle);
#if defined(__linux__)
void SyncDriver_GetAttr(const SyncDriverHandle handle, const char **name,
                        Bool *quiesces);
#endif

#endif

 07070100000116000081A4000000000000000000000001682255050000062A000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/syncDriverIoc.h    /*********************************************************
 * Copyright (C) 2007-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * syncDriverIoc.h --
 *
 * ioctl commands used by the sync driver on Unix systems.
 *
 * SYNC_IOC_FREEZE:     Freezes the provided paths.
 * SYNC_IOC_THAW:       Thaws frozen block devices after a FREEZE ioctl.
 * SYNC_IOC_QUERY:      Returns the total number of frozen devices (not
 *                      specific to the fd used).
 */

#ifndef _SYNCDRIVERIOC_H_
#define _SYNCDRIVERIOC_H_

#ifdef __linux__

# include <linux/ioctl.h>

# define SYNC_IOC_FREEZE      _IOW(0xF5,0x01,const char *)
# define SYNC_IOC_THAW        _IO(0xF5,0x02)
# define SYNC_IOC_QUERY       _IOR(0xF5,0x03,int)

#else

# error "Driver not yet implemented for this OS."

#endif

#endif /* _SYNCDRIVERIOC_H_ */

  07070100000117000081A40000000000000000000000016822550500000C37000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/system.h   /*********************************************************
 * Copyright (c) 1998-2016,2018,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * system.h --
 *
 *    System-specific routines used by the tools daemon.
 *
 */


#ifndef __SYSTEM_H__
#   define __SYSTEM_H__

#include "vm_basic_types.h"
#ifdef _WIN32
#include "dbllnklst.h"
#endif
#include "unicode.h"

uint64 System_GetTimeMonotonic(void);
uint64 System_Uptime(void);
void System_Shutdown(Bool reboot);
Bool System_GetNodeName(size_t outBufSize, char *outBuf);

char *System_GetEnv(Bool global, const char *valueName);
int System_SetEnv(Bool global, const char *valueName, const char *value);

Bool System_IsUserAdmin(void);

#ifdef _WIN32
/*
 * Representation of monitors gathered by System_GetMonitors.
 */
typedef struct MonListNode {
   RECT rect;
   RECT work;
   BOOL isPrimary;
   DWORD bpp;
   BOOL isActive;
   uint32 srcId;
   DblLnkLst_Links l;
} MonListNode;

/*
 * The value returned by System_GetServiceState if the current state of the
 * vmtools service can't be determined. We need to use a value that is not
 * already used for a real state. The current values run from 0x1 to 0x7, so
 * 0xffffffff should be fairly safe (cf. winsvc.h).
 */
#define VM_SERVICE_STATE_UNKNOWN 0xffffffff

BOOL System_SetProcessPrivilege(wchar_t *privName, Bool enable);
Bool System_IsScreenSaverActive(void);
Bool System_IsScreenSaverRunning(void);
Bool System_IsSecureDesktopActive(void);
Bool System_DisableAndKillScreenSaver(void);
DWORD System_GetServiceState(LPCWSTR szServiceName);
DblLnkLst_Links *System_GetMonitors();
void System_SetFocusedWindow(HWND windowToFocus, Bool force);
Bool System_EnableDesktopComposition(BOOL enabled);
LPWSTR System_GetImageFilePathForWindow(HWND hwnd);
HANDLE System_OpenProcessForHWND(DWORD mask, BOOL inherit, HWND hwnd);
LONG System_VerifyTrust(const char *filePath);
LPWSTR System_GetProcessCmdLine(DWORD pId);
#endif


/*
 * TODO:  Targets' make/SCons files, or perhaps the entire build infrastructure
 *        as a whole, should define a POSIX_LIKE_ENVIRONMENT variable which is
 *        then acted upon and translates to a -DPOSIX_LIKE_ENVIRONMENT
 *        preprocessor option.
 */
#if !defined(_WIN32)
const char **System_GetNativeEnviron(const char **compatEnviron);
void System_FreeNativeEnviron(const char **nativeEnviron);
#endif

#endif /* __SYSTEM_H__ */
 07070100000118000081A400000000000000000000000168225505000010C2000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/timeutil.h /*********************************************************
 * Copyright (C) 1998-2017,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * timeutil.h --
 *
 *   Miscellaneous time related utility functions.
 *
 */

#ifndef _TIMEUTIL_H_
#define _TIMEUTIL_H_

#include <time.h>

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "vm_assert.h"

#if defined(__cplusplus)
extern "C" {
#endif


#define MAX_DAYSLEFT     4096

struct timeval;

/*struct timespec is not supported on UWP*/
#if defined(_WIN32) && !defined(VM_WIN_UWP) && _MSC_VER < 1900
struct timespec {
   time_t tv_sec;
   long   tv_nsec;
};
#endif

/* Similar to a struct tm but with slightly less weird semantics. */
typedef struct TimeUtil_Date {
   unsigned int year;   /* e.g. 1970 */
   unsigned int month;  /* [1, 12] */
   unsigned int day;    /* [1, 31] */
   unsigned int hour;   /* [0, 23] */
   unsigned int minute; /* [0, 59] */
   unsigned int second; /* [0, 61] (for leap seconds) */
} TimeUtil_Date;

typedef struct TimeUtil_TimeOfDay {
   unsigned long seconds;
   unsigned long useconds;
} TimeUtil_TimeOfDay;

typedef struct TimeUtil_Expiration {
   /*
    * Does it expire?
    */

   Bool expires;

   /*
    * When does it expire? (valid only if 'expires' == TRUE)
    *
    * Note: TimeUtil_Expiration only uses the 'year', 'month'
    * and 'day' fields of 'when'.
    */

   TimeUtil_Date when;

   /*
    * Compute this once for all, to avoid problems when the current day changes
    * (valid only if 'expires' == TRUE).
    */

   unsigned int daysLeft;
} TimeUtil_Expiration;


time_t TimeUtil_MakeTime(const TimeUtil_Date *d); // IN

Bool TimeUtil_StringToDate(TimeUtil_Date *d,    // IN/OUT
                           char const *date);   // IN: 'YYYYMMDD' or 'YYYY/MM/DD' or 'YYYY-MM-DD'

Bool TimeUtil_DaysSubtract(TimeUtil_Date *d,  // IN/OUT
                           unsigned int nr);  // IN

int TimeUtil_DeltaDays(TimeUtil_Date const *left,   // IN
                       TimeUtil_Date const *right); // IN

void TimeUtil_DaysAdd(TimeUtil_Date *d, // IN/OUT
                      unsigned int nr); // IN

void TimeUtil_PopulateWithCurrent(Bool local,        // IN
                                  TimeUtil_Date *d); // OUT

void TimeUtil_GetTimeOfDay(TimeUtil_TimeOfDay *d); // OUT

unsigned int TimeUtil_DaysLeft(TimeUtil_Date const *d); // IN

Bool TimeUtil_ExpirationLowerThan(TimeUtil_Expiration const *left,   // IN
                                  TimeUtil_Expiration const *right); // IN

Bool TimeUtil_DateLowerThan(TimeUtil_Date const *left,   // IN
                            TimeUtil_Date const *right); // IN

void TimeUtil_ProductExpiration(TimeUtil_Expiration *e); // OUT

char * TimeUtil_GetTimeFormat(int64 utcTime,  // IN
                              Bool showDate,  // IN
                              Bool showTime); // IN

int TimeUtil_NtTimeToUnixTime(struct timespec *unixTime, // OUT
                              VmTimeType ntTime);        // IN

VmTimeType TimeUtil_UnixTimeToNtTime(struct timespec unixTime); // IN

Bool TimeUtil_IsValidDate(TimeUtil_Date const *d); // IN


#ifdef _WIN32
Bool TimeUtil_UTCTimeToSystemTime(const __time64_t utcTime,    // IN
                                  SYSTEMTIME *systemTime);     // OUT
#endif

int TimeUtil_GetLocalWindowsTimeZoneIndexAndName(char **ptzName);

time_t TimeUtil_SecondsSinceEpoch(TimeUtil_Date *d); // IN

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _TIMEUTIL_H_
  07070100000119000081A400000000000000000000000168225505000005F5000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/tracer.hh  /*********************************************************
 * Copyright (c) 2013-2017,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * tracer.hh --
 *
 *    A dumb object to trace function enter/exit.  (Devel-only.)
 */


#ifndef TRACER_HH
#define TRACER_HH

#include "vm_basic_defs.h"

#include "glib.h"


#ifdef VMX86_DEVEL
#   define TRACE_CALL()    Tracer _fn_tracer (__FUNCTION__)
class Tracer {
public:
   Tracer(const char* fnName)
      : mFnName(fnName)
   {
      g_debug("> %s: enter\n", mFnName);
   }

   ~Tracer()
   {
      g_debug("< %s: exit\n", mFnName);
   }

private:
   Tracer();                    // = delete
   Tracer(const Tracer&);       // = delete

   const char* mFnName;
};
#else
#   define TRACE_CALL()
#endif

#endif // ifndef TRACER_HH
   0707010000011A000081A40000000000000000000000016822550500000610000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/unicode.h  /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicode.h --
 *
 *      Unicode-aware string library.
 */

#ifndef _UNICODE_H_
#define _UNICODE_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

// Start here: string creation, destruction, and encoding conversion.
#include "unicodeBase.h"

// Basic string operations: length, append, find, insert, replace.
#include "unicodeOperations.h"

// Character transformations: upper/lower/title case, case folding, etc.
#include "unicodeTransforms.h"

#ifdef USE_ICU
// Unicode functionality depending on the third-party ICU library.
#include "unicodeICU.h"
#endif // USE_ICU

#endif // _UNICODE_H_
0707010000011B000081A400000000000000000000000168225505000027D7000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/unicodeBase.h  /*********************************************************
 * Copyright (C) 2007-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeBase.h --
 *
 *      Basic Unicode string creation and encoding conversion.
 */

#ifndef _UNICODE_BASE_H_
#define _UNICODE_BASE_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include <stdlib.h>
#include <errno.h>
#include "util.h"
#include "unicodeTypes.h"
#include "err.h"

#if defined(__cplusplus)
extern "C" {
#endif

#define UNICODE_SUBSTITUTION_CHAR "\xEF\xBF\xBD"

/*
 * Unescapes \\uABCD in string literals to Unicode code point
 * U+ABCD, and \\U001FABCD to Unicode code point U+1FABCD.
 *
 * The resulting string is never freed, so this is not to be used for
 * general runtime Unicode string creation.
 *
 * Use to replace:
 *
 *   const char *utf8Copyright = "Copyright \302\251 COMPANY_NAME";
 *
 * with:
 *
 *   const char *copyright = U_UNESCAPE("Copyright \\u00A9 COMPANY_NAME");
 */

#define U_UNESCAPE(x) Unicode_GetStatic(x, TRUE)


/*
 * In contexts where an errno makes sense, use this to report conversion
 * failure. ERANGE is chosen since no Unicode routines deal with math; this
 * avoids overloading an errno that may a specific meaning for a certain
 * routine.
 */

#ifndef _WIN32
#define UNICODE_CONVERSION_ERRNO ERANGE
#endif


/*
 * Allocates a Unicode string given a buffer of the specified length
 * (not necessarily NUL-terminated) in the specified encoding.
 *
 * Returns NULL if the buffer was invalid or memory could not be
 * allocated.
 */

char *Unicode_AllocWithLength(const void *buffer,
                              ssize_t lengthInBytes,
                              StringEncoding encoding);


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Alloc --
 *
 *      Allocates a new Unicode string given a NUL-terminated buffer
 *      of bytes in the specified string encoding.
 *
 *	If buffer is NULL, then NULL is returned.
 *
 *      Note that regardless of the encoding of the buffer passed to this
 *      function, the returned string can hold any Unicode characters.
 *
 * Results:
 *      An allocated Unicode string containing the decoded characters
 *      in buffer, or NULL if input is NULL.  Caller must pass to
 *      free to free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_Alloc(const void *buffer,      // IN
              StringEncoding encoding) // IN
{
   return Unicode_AllocWithLength(buffer, -1, encoding);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_AllocWithUTF8 --
 *
 *      Allocates a new Unicode string given a NUL-terminated UTF-8 string.
 *
 *	If utf8String is NULL, then NULL is returned.
 *
 * Results:
 *      An allocated Unicode string containing the characters in
 *      utf8String, or NULL if utf8String is NULL.  Caller must pass to
 *      free to free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_AllocWithUTF8(const char *utf8String) // IN
{
   return Unicode_AllocWithLength(utf8String, -1, STRING_ENCODING_UTF8);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_AllocWithUTF16 --
 *
 *      Allocates a new Unicode string given a NUL-terminated UTF-16
 *      string in host-endian order.
 *
 *	If utf16String is NULL, then NULL is returned.
 *
 * Results:
 *      An allocated Unicode string containing the characters in
 *      utf16String, or NULL if utf16String is NULL.  Caller must pass to
 *      free to free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_AllocWithUTF16(const utf16_t *utf16String) // IN
{
   return Unicode_AllocWithLength(utf16String, -1, STRING_ENCODING_UTF16);
}


char *Unicode_Duplicate(const char *str);

char **Unicode_AllocList(char **srcList, ssize_t length,
                         StringEncoding encoding);

char **Unicode_GetAllocList(char *const srcList[],
                            ssize_t length,
                            StringEncoding encoding);

/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_AllocListWithUTF16 --
 *
 *      Allocates a list (actually a vector) of Unicode strings from a list
 *      (vector) of UTF-16 strings.  The input list has a specified length or
 *      can be an argv-style NULL-terminated list (if length is negative).
 *
 * Results:
 *      An allocated list (vector) of Unicode strings.
 *      The result must be freed with Util_FreeStringList.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char **
Unicode_AllocListWithUTF16(utf16_t **utf16list, // IN:
                           ssize_t length)      // IN:
{
   return Unicode_AllocList((char **) utf16list, length,
			    STRING_ENCODING_UTF16);
}

/*
 * Compute the number of bytes in a string.
 */
ssize_t Unicode_LengthInBytes(const void *buffer,
                              StringEncoding encoding);

/*
 * Gets the number of UTF-16 code units in the NUL-terminated UTF-16 array.
 */
ssize_t Unicode_UTF16Strlen(const utf16_t *utf16);

/*
 * Duplicates a UTF-16 string.
 */
utf16_t *Unicode_UTF16Strdup(const utf16_t *utf16);

/*
 * Tests if the buffer's bytes are valid in the specified encoding.
 */
Bool Unicode_IsBufferValid(const void *buffer,
                           ssize_t lengthInBytes,
                           StringEncoding encoding);

Bool Unicode_IsStringValidUTF8(const char *str);

/*
 * Tests if the buffer's unicode contents can be converted to the
 * specified encoding.
 */
Bool Unicode_CanGetBytesWithEncoding(const char *ustr,
                                     StringEncoding encoding);

/*
 * Escape non-printable bytes of the buffer with \xAB, where 0xAB
 * is the non-printable byte value.
 */
char *Unicode_EscapeBuffer(const void *buffer,
                           ssize_t lengthInBytes,
                           StringEncoding encoding);

/*
 * Returns the length in number of native code units (UTF-8 bytes or
 * UTF-16 words) of the string.
 */
UnicodeIndex Unicode_LengthInCodeUnits(const char *str);

/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_IsEmpty --
 *
 *      Test if the Unicode string is empty.
 *
 * Results:
 *      TRUE if the string has length 0, FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Unicode_IsEmpty(const char *str)  // IN:
{
   ASSERT(str != NULL);
   return str[0] == '\0';
}


/*
 * Efficiently returns the upper bound on the number of bytes required
 * to encode the Unicode string in the specified encoding, including
 * NUL termination.
 */
size_t Unicode_BytesRequired(const char *str,
                             StringEncoding encoding);

/*
 * Extracts the contents of the Unicode string into a NUL-terminated
 * buffer using the specified encoding. Copies at most
 * maxLengthInBytes including NUL termination. Returns FALSE if
 * truncation occurred, TRUE otherwise. If retLength is not NULL,
 * *retLength holds the number of bytes actually copied, not including
 * the NUL termination, upon return.
 */
Bool Unicode_CopyBytes(void *destBuffer,
                       const char *srcBuffer,
                       size_t maxLengthInBytes,
                       size_t *retLength,
                       StringEncoding encoding);

void *Unicode_GetAllocBytes(const char *str,
                            StringEncoding encoding);

void *Unicode_GetAllocBytesWithLength(const char *str,
                                      StringEncoding encoding,
                                      ssize_t lengthInBytes);


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_GetAllocUTF16 --
 *
 *      Allocates and returns a NUL terminated buffer into which the contents
 *      of the unicode string are extracted using the (host native) UTF-16
 *      encoding.  (Note that UTF-16 NUL is two bytes: "\0\0".)
 *
 *      NULL is returned for NULL argument.
 *
 * Results:
 *      Pointer to the dynamically allocated memory,
 *      or NULL on NULL argument.
 *
 *      Caller is responsible to free the memory allocated by this routine.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE utf16_t *
Unicode_GetAllocUTF16(const char *str)  // IN:
{
   /* Cast for compatibility with C++ compilers. */
   return (utf16_t *) Unicode_GetAllocBytes(str, STRING_ENCODING_UTF16);
}


/*
 * Helper function for Unicode string literal macros.
 */
const char *Unicode_GetStatic(const char *asciiBytes,
                              Bool unescape);

/*
 * Helper macros for Win32 Unicode string transition.
 */
#if defined(_WIN32)
   #define UNICODE_GET_UTF16(s)     Unicode_GetAllocUTF16(s)
   #define UNICODE_RELEASE_UTF16(s) WITH_ERRNO_FREE(s)
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _UNICODE_BASE_H_
 0707010000011C000081A40000000000000000000000016822550500000BB2000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/unicodeICU.h   /*********************************************************
 * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeICU.h --
 *
 *      Unicode operations that depend on the third-party ICU support
 *      library.
 */

#ifndef _UNICODE_ICU_H_
#define _UNICODE_ICU_H_

#define INCLUDE_ALLOW_USERLEVEL

#ifndef USE_ICU
#error These interfaces require the ICU library (define USE_ICU).
#endif

#include "includeCheck.h"

#include "unicodeBase.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef enum {
   UNICODE_COMPARE_DEFAULT = 0,
   UNICODE_COMPARE_IGNORE_ACCENTS,
   UNICODE_COMPARE_IGNORE_CASE,
   UNICODE_COMPARE_IGNORE_PUNCTUATION
} UnicodeCompareOption;


/*
 * Different languages and cultures have unique rules for how strings
 * are compared and sorted.  For example:
 *
 *   Swedish: z < "o with umlaut"
 *   German:  "o with umlaut" < z
 *
 * When producing a result visible to the user (like a sorted list of
 * virtual machine names) string comparsion must obey the rules set by
 * the user's language and culture, collectively called the "locale".
 */

int Unicode_CompareWithLocale(const char *str1,
                              const char *str2,
                              const char *locale,
                              UnicodeCompareOption compareOption);

/*
 * Transforms the case of the string using the given locale's rules.
 *
 * Pass in a NULL locale to use the process's default locale.
 *
 * Changing the case of a string can change its length, so don't
 * assume the string is the same length after calling these functions.
 */
char *Unicode_ToLower(const char *str, const char *locale);
char *Unicode_ToUpper(const char *str, const char *locale);

#ifdef HAVE_ICU_38
char *Unicode_ToTitle(const char *str, const char *locale);
#endif

typedef enum {
   UNICODE_NORMAL_FORM_C, // "e with acute accent" -> U+00E9
   UNICODE_NORMAL_FORM_D  // "e with acute accent" -> U+0065 U+0302
} UnicodeNormalizationForm;

/*
 * Normalizes Unicode characters composed of multiple parts into a
 * standard form.
 */
char *Unicode_Normalize(const char *str,
                          UnicodeNormalizationForm form);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _UNICODE_ICU_H_
  0707010000011D000081A400000000000000000000000168225505000055B3000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/include/unicodeOperations.h    /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeOperations.h --
 *
 *      Basic Unicode string operations.
 *
 *      UnicodeIndex index and length arguments are in terms of code
 *      units, not characters.  The size of a code unit depends on the
 *      implementation (one byte for UTF-8, one 16-bit word for
 *      UTF-16).  Do not store these values on disk, modify them, or
 *      do arithmetic operations on them.
 *
 *      Instead of iterating over the code units in a string to do
 *      character operations, use the library functions provided to
 *      search and transform strings.
 *
 *      If the functionality you need is not present, email the
 *      i18n-dev mailing list.
 */

#ifndef _UNICODE_OPERATIONS_H_
#define _UNICODE_OPERATIONS_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include <string.h>

#include "unicodeBase.h"
#include "vm_assert.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Primitive operations.  All other Unicode operations are implemented
 * in terms of these.
 *
 * Pass -1 for any length parameter to indicate "from start until end
 * of string".
 */

int Unicode_CompareRange(const char *str1,
                         UnicodeIndex str1Start,
                         UnicodeIndex str1Length,
                         const char *str2,
                         UnicodeIndex str2Start,
                         UnicodeIndex str2Length,
                         Bool ignoreCase);

UnicodeIndex Unicode_FindSubstrInRange(const char *str,
                                       UnicodeIndex strStart,
                                       UnicodeIndex strLength,
                                       const char *strToFind,
                                       UnicodeIndex strToFindStart,
                                       UnicodeIndex strToFindLength);

UnicodeIndex Unicode_FindLastSubstrInRange(const char *str,
                                           UnicodeIndex strStart,
                                           UnicodeIndex strLength,
                                           const char *strToFind,
                                           UnicodeIndex strToFindStart,
                                           UnicodeIndex strToFindLength);
char *Unicode_Substr(const char *str,
                     UnicodeIndex start,
                     UnicodeIndex length);

char *Unicode_ReplaceRange(const char *destination,
                           UnicodeIndex destinationStart,
                           UnicodeIndex destinationLength,
                           const char *source,
                           UnicodeIndex sourceStart,
                           UnicodeIndex sourceLength);

char *Unicode_Join(const char *first,
                   ...);

char *Unicode_Format(const char *fmt, ...);

UnicodeIndex Unicode_LengthInCodePoints(const char *str);

/*
 * Simple in-line functions that may be used below.
 */


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_IsIndexAtCodePointBoundary --
 *
 *      Check a string index (in bytes) for code point boundary.
 *
 *	The index must be valid (>= 0 and <= string length).
 *	The end of the string is considered a valid boundary.
 *
 * Results:
 *      TRUE if index is at a code point boundary.
 *
 * Side effects:
 *      Panic if index is not valid.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Unicode_IsIndexAtCodePointBoundary(const char *str,     // IN:
                                   UnicodeIndex index)  // IN:
{
   ASSERT(index >= 0 && index <= Unicode_LengthInCodeUnits(str));

   return (str[index] & 0xc0) != 0x80;
}


/*
 * Other operations, each based upon calls to primitives.
 */


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Append --
 *
 *      Allocates and returns a new string containing 'destination'
 *      followed by 'source'.
 *
 * Results:
 *      The newly-allocated string.  Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_Append(const char *destination, // IN
               const char *source)      // IN
{
   return Unicode_ReplaceRange(destination,
                               -1,
                               0,
                               source,
                               0,
                               -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_AppendRange --
 *
 *      Allocates and returns a new string containing 'destination'
 *      followed by the specified range of 'source'.
 *
 * Results:
 *      The newly-allocated string.  Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_AppendRange(const char *dest,        // IN:
                    const char *src,         // IN:
                    UnicodeIndex srcStart,   // IN:
                    UnicodeIndex srcLength)  // IN:
{
   return Unicode_ReplaceRange(dest,
                               Unicode_LengthInCodePoints(dest),
                               0,
                               src,
                               srcStart,
                               srcLength);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Compare --
 *
 *      Compares two Unicode strings for canonical equivalence in code
 *      point order.
 *
 *      If the result is to be visible in a user interface, use
 *      Unicode_CompareWithLocale to support language and
 *      culture-specific comparison and sorting rules.
 *
 * Results:
 *      -1 if str1 < str2, 0 if str1 == str2, 1 if str1 > str2.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE int
Unicode_Compare(const char *str1, // IN
                const char *str2) // IN
{
   return Unicode_CompareRange(str1,
                               0,
                               -1,
                               str2,
                               0,
                               -1,
                               FALSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_CompareIgnoreCase --
 *
 *      Compares two Unicode strings for case-insensitive canonical
 *      equivalence in code point order.
 *
 *      If the result is to be visible in a user interface, use
 *      Unicode_CompareWithLocale to support language and
 *      culture-specific comparison and sorting rules.
 *
 * Results:
 *      -1 if str1 < str2, 0 if str1 == str2, 1 if str1 > str2.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE int
Unicode_CompareIgnoreCase(const char *str1, // IN
                          const char *str2) // IN
{
   return Unicode_CompareRange(str1,
                               0,
                               -1,
                               str2,
                               0,
                               -1,
                               TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeEndsWith --
 * Unicode_EndsWith --
 * Unicode_EndsWithIgnoreCase --
 *
 *      Tests if 'str' ends with 'suffix'.
 *
 * Results:
 *      TRUE if 'str' ends with 'suffix', FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
UnicodeEndsWith(const char *str,      // IN:
                const char *suffix,   // IN:
                Bool ignoreCase)      // IN:
{
   UnicodeIndex strLength = Unicode_LengthInCodePoints(str);
   UnicodeIndex suffixLength = Unicode_LengthInCodePoints(suffix);
   UnicodeIndex offset = strLength - suffixLength;

   if (suffixLength > strLength) {
      return FALSE;
   }

   return Unicode_CompareRange(str,
                               offset,
                               suffixLength,
                               suffix,
                               0,
                               suffixLength,
                               ignoreCase) == 0;
}


static INLINE Bool
Unicode_EndsWith(const char *str,    // IN
                 const char *suffix) // IN
{
   return UnicodeEndsWith(str, suffix, FALSE);
}


static INLINE Bool
Unicode_EndsWithIgnoreCase(const char *str,    // IN
                           const char *suffix) // IN
{
   return UnicodeEndsWith(str, suffix, TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Find --
 *
 *      Finds the first occurrence of 'strToFind' inside 'str'.
 *
 * Results:
 *      If 'strToFind' exists inside 'str', returns the first starting
 *      index of 'strToFind' in that range.
 *
 *      Otherwise, returns UNICODE_INDEX_NOT_FOUND.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE UnicodeIndex
Unicode_Find(const char *str,       // IN
             const char *strToFind) // IN
{
   return Unicode_FindSubstrInRange(str,
                                    0,
                                    -1,
                                    strToFind,
                                    0,
                                    -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_FindFromIndex --
 *
 *      Finds the first occurrence of 'strToFind' inside 'str' in the range
 *      [fromIndex, lengthOfStr).
 *
 * Results:
 *      If 'strToFind' exists inside 'str' in the specified range,
 *      returns the first starting index of 'strToFind' in that range.
 *
 *      Otherwise, returns UNICODE_INDEX_NOT_FOUND.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE UnicodeIndex
Unicode_FindFromIndex(const char *str,        // IN
                      const char *strToFind,  // IN
                      UnicodeIndex fromIndex) // IN
{
   return Unicode_FindSubstrInRange(str,
                                    fromIndex,
                                    -1,
                                    strToFind,
                                    0,
                                    -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_FindInRange --
 *
 *      Finds the first occurrence of 'strToFind' inside 'str' in the range
 *      [start, start+length).
 *
 * Results:
 *      If 'strToFind' exists inside 'str' in the specified range,
 *      returns the first starting index of 'strToFind' in that range.
 *
 *      Otherwise, returns UNICODE_INDEX_NOT_FOUND.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE UnicodeIndex
Unicode_FindInRange(const char *str,        // IN
                    const char *strToFind,  // IN
                    UnicodeIndex start,     // IN
                    UnicodeIndex length)    // IN
{
   return Unicode_FindSubstrInRange(str,
                                    start,
                                    length,
                                    strToFind,
                                    0,
                                    -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_FindLast --
 *
 *      Finds the last occurrence of 'strToFind' inside 'str'.
 *
 * Results:
 *      If 'strToFind' exists inside 'str', returns the last starting
 *      index of 'strToFind' in that range.
 *
 *      Otherwise, returns UNICODE_INDEX_NOT_FOUND.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE UnicodeIndex
Unicode_FindLast(const char *str,       // IN
                 const char *strToFind) // IN
{
   return Unicode_FindLastSubstrInRange(str,
                                        0,
                                        -1,
                                        strToFind,
                                        0,
                                        -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_FindLastFromIndex --
 *
 *      Finds the last occurrence of 'strToFind' inside 'str' in the range
 *      [fromIndex, lengthOfStr).
 *
 * Results:
 *      If 'strToFind' exists inside 'str' in the specified range,
 *      returns the last starting index of 'strToFind' in that range.
 *
 *      Otherwise, returns UNICODE_INDEX_NOT_FOUND.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE UnicodeIndex
Unicode_FindLastFromIndex(const char *str,        // IN
                          const char *strToFind,  // IN
                          UnicodeIndex fromIndex) // IN
{
   return Unicode_FindLastSubstrInRange(str,
                                        fromIndex,
                                        -1,
                                        strToFind,
                                        0,
                                        -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_FindLastInRange --
 *
 *      Finds the last occurrence of 'strToFind' inside 'str' in the range
 *      [start, start+length).
 *
 * Results:
 *      If 'strToFind' exists inside 'str' in the specified range,
 *      returns the last starting index of 'strToFind' in that range.
 *
 *      Otherwise, returns UNICODE_INDEX_NOT_FOUND.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE UnicodeIndex
Unicode_FindLastInRange(const char *str,        // IN
                        const char *strToFind,  // IN
                        UnicodeIndex start,     // IN
                        UnicodeIndex length)    // IN
{
   return Unicode_FindLastSubstrInRange(str,
                                        start,
                                        length,
                                        strToFind,
                                        0,
                                        -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Insert --
 *
 *      Allocates and returns a new copy of 'destination', with the
 *      string 'source' inserted at the index 'destinationStart'.
 *
 * Results:
 *      The newly-allocated string.  Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_Insert(const char *destination,       // IN
               UnicodeIndex destinationStart, // IN
               const char *source)            // IN
{
   return Unicode_ReplaceRange(destination,
                               destinationStart,
                               0,
                               source,
                               0,
                               -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_InsertRange --
 *
 *      Allocates and returns a new copy of 'destination', with the
 *      specified range of the string 'source' inserted at the index
 *      'destinationStart'.
 *
 * Results:
 *      The newly-allocated string.  Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_InsertRange(const char *destination,
                    UnicodeIndex destinationStart,
                    const char *source,
                    UnicodeIndex sourceStart,
                    UnicodeIndex sourceLength)
{
   return Unicode_ReplaceRange(destination,
                               destinationStart,
                               0,
                               source,
                               sourceStart,
                               sourceLength);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_IsEqual --
 *
 *      Tests two strings for canonical equivalence.
 *
 * Results:
 *      TRUE if the two strings are canonically equivalent, FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Unicode_IsEqual(const char *str1, // IN
                const char *str2) // IN
{
   return Unicode_CompareRange(str1,
                               0,
                               -1,
                               str2,
                               0,
                               -1,
                               FALSE) == 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_RemoveRange --
 *
 *      Allocates and returns a new string that contains a copy of
 *      'destination' with the code units in the range [start, start + length)
 *      removed.
 *
 * Results:
 *      The newly-allocated string.  Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_RemoveRange(const char *destination,
                    UnicodeIndex start,
                    UnicodeIndex length)
{
   return Unicode_ReplaceRange(destination, start, length, "", 0, 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Replace --
 *
 *      Allocates and returns a new string that contains a copy of
 *      'destination' with the code units in the range
 *      [destinationStart, destinarionStart + destinationLength) replaced
 *      with 'source'.
 *
 * Results:
 *      The newly-allocated string.  Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_Replace(const char *destination,
                UnicodeIndex destinationStart,
                UnicodeIndex destinationLength,
                const char *source)
{
   return Unicode_ReplaceRange(destination,
                               destinationStart,
                               destinationLength,
                               source,
                               0,
                               -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeStartsWith --
 * Unicode_StartsWith --
 * Unicode_StartsWithIgnoreCase --
 *
 *      Tests if 'str' starts with 'prefix'.
 *
 * Results:
 *      TRUE if 'str' starts with 'prefix', FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
UnicodeStartsWith(const char *str,     // IN:
                  const char *prefix,  // IN:
                  Bool ignoreCase)     // IN:
{
   UnicodeIndex strLength = Unicode_LengthInCodePoints(str);
   UnicodeIndex prefixLength = Unicode_LengthInCodePoints(prefix);

   if (prefixLength > strLength) {
      return FALSE;
   }

   return Unicode_CompareRange(str,
                               0,
                               prefixLength,
                               prefix,
                               0,
                               prefixLength,
                               ignoreCase) == 0;
}


static INLINE Bool
Unicode_StartsWith(const char *str,    // IN
                   const char *prefix) // IN
{
   return UnicodeStartsWith(str, prefix, FALSE);
}


static INLINE Bool
Unicode_StartsWithIgnoreCase(const char *str,    // IN
                             const char *prefix) // IN
{
   return UnicodeStartsWith(str, prefix, TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Truncate --
 *
 *      Allocates and returns a new copy of 'str' truncated to the
 *      specified length in code units.
 *
 * Results:
 *      The newly-allocated truncated copy of 'str'.  Caller must free
 *      with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE char *
Unicode_Truncate(const char *str,     // IN
                 UnicodeIndex length) // IN
{
   return Unicode_Substr(str, 0, length);
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _UNICODE_OPERATIONS_H_
 0707010000011E000081A40000000000000000000000016822550500000732000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/include/unicodeTransforms.h    /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeTransforms.h --
 *
 *      Operations that transform all the characters in a string.
 *
 *      Transform operations like uppercase and lowercase are
 *      locale-sensitive (depending on the user's country and language
 *      preferences).
 */

#ifndef _UNICODE_TRANSFORMS_H_
#define _UNICODE_TRANSFORMS_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include "unicodeTypes.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Standardizes the case of the string by doing a locale-agnostic
 * uppercase operation, then a locale-agnostic lowercase operation.
 */
char *Unicode_FoldCase(const char *str);

/*
 * Trims whitespace from either side of the string.
 */
char *Unicode_Trim(const char *str);
char *Unicode_TrimLeft(const char *str);
char *Unicode_TrimRight(const char *str);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _UNICODE_TRANSFORMS_H_
  0707010000011F000081A40000000000000000000000016822550500003677000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/unicodeTypes.h /*********************************************************
 * Copyright (C) 2007-2017, 2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeTypes.h --
 *
 *      Types used throughout the Unicode library.
 */

#ifndef _UNICODE_TYPES_H_
#define _UNICODE_TYPES_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "vm_assert.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef ssize_t UnicodeIndex;

/*
 * Special UnicodeIndex value returned when a string is not found.
 */
enum {
   UNICODE_INDEX_NOT_FOUND = -1
};


/*
 * Encodings passed to convert encoded byte strings to and from
 * Unicode.
 *
 * Keep this enum synchronized with ICU_ENCODING_LIST in unicodeICU.cc!
 */

typedef enum {
   STRING_ENCODING_FIRST,

   /*
    * Byte string encodings that support all characters in Unicode.
    *
    * If you don't know what to use for bytes in a new system, use
    * STRING_ENCODING_UTF8.
    */

   STRING_ENCODING_UTF8 = STRING_ENCODING_FIRST,

   STRING_ENCODING_UTF16_LE, // Little-endian UTF-16.
   STRING_ENCODING_UTF16_BE, // Big-endian UTF-16.
   STRING_ENCODING_UTF16_XE, // UTF-16 with BOM.

   STRING_ENCODING_UTF32_LE, // Little-endian UTF-32.
   STRING_ENCODING_UTF32_BE, // Big-endian UTF-32.
   STRING_ENCODING_UTF32_XE, // UTF-32 with BOM.

   /*
    * Legacy byte string encodings that only support a subset of Unicode.
    */

   /*
    * Latin encodings
    */

   STRING_ENCODING_US_ASCII,
   STRING_ENCODING_ISO_8859_1,
   STRING_ENCODING_ISO_8859_2,
   STRING_ENCODING_ISO_8859_3,
   STRING_ENCODING_ISO_8859_4,
   STRING_ENCODING_ISO_8859_5,
   STRING_ENCODING_ISO_8859_6,
   STRING_ENCODING_ISO_8859_7,
   STRING_ENCODING_ISO_8859_8,
   STRING_ENCODING_ISO_8859_9,
   STRING_ENCODING_ISO_8859_10,
   // ISO-8859-11 is unused.
   // Oddly, there is no ISO-8859-12.
   STRING_ENCODING_ISO_8859_13,
   STRING_ENCODING_ISO_8859_14,
   STRING_ENCODING_ISO_8859_15,

   /*
    * Chinese encodings
    */

   STRING_ENCODING_GB_18030,
   STRING_ENCODING_BIG_5,
   STRING_ENCODING_BIG_5_HK,
   STRING_ENCODING_GBK,
   STRING_ENCODING_GB_2312,
   STRING_ENCODING_ISO_2022_CN,

   /*
    * Japanese encodings
    */

   STRING_ENCODING_SHIFT_JIS,
   STRING_ENCODING_EUC_JP,
   STRING_ENCODING_ISO_2022_JP,
   STRING_ENCODING_ISO_2022_JP_1,
   STRING_ENCODING_ISO_2022_JP_2,

   /*
    * Korean encodings
    */

   STRING_ENCODING_ISO_2022_KR,

   /*
    * Windows encodings
    */

   STRING_ENCODING_WINDOWS_1250,
   STRING_ENCODING_WINDOWS_1251,
   STRING_ENCODING_WINDOWS_1252,
   STRING_ENCODING_WINDOWS_1253,
   STRING_ENCODING_WINDOWS_1254,
   STRING_ENCODING_WINDOWS_1255,
   STRING_ENCODING_WINDOWS_1256,
   STRING_ENCODING_WINDOWS_1257,
   STRING_ENCODING_WINDOWS_1258,

   STRING_ENCODING_ISO_6937_2_ADD,
   STRING_ENCODING_JIS_X0201,
   STRING_ENCODING_JIS_ENCODING,
   STRING_ENCODING_EXTENDED_UNIX_CODE_FIXED_WIDTH_FOR_JAPANESE,
   STRING_ENCODING_BS_4730,
   STRING_ENCODING_SEN_850200_C,
   STRING_ENCODING_IT,
   STRING_ENCODING_ES,
   STRING_ENCODING_DIN_66003,
   STRING_ENCODING_NS_4551_1,
   STRING_ENCODING_NF_Z_62_010,
   STRING_ENCODING_ISO_10646_UTF_1,
   STRING_ENCODING_ISO_646_BASIC_1983,
   STRING_ENCODING_INVARIANT,
   STRING_ENCODING_ISO_646_IRV_1983,
   STRING_ENCODING_NATS_SEFI,
   STRING_ENCODING_NATS_SEFI_ADD,
   STRING_ENCODING_NATS_DANO,
   STRING_ENCODING_NATS_DANO_ADD,
   STRING_ENCODING_SEN_850200_B,
   STRING_ENCODING_KS_C_5601_1987,
   STRING_ENCODING_JIS_C6220_1969_JP,
   STRING_ENCODING_JIS_C6220_1969_RO,
   STRING_ENCODING_PT,
   STRING_ENCODING_GREEK7_OLD,
   STRING_ENCODING_LATIN_GREEK,
   STRING_ENCODING_NF_Z_62_010__1973_,
   STRING_ENCODING_LATIN_GREEK_1,
   STRING_ENCODING_ISO_5427,
   STRING_ENCODING_JIS_C6226_1978,
   STRING_ENCODING_BS_VIEWDATA,
   STRING_ENCODING_INIS,
   STRING_ENCODING_INIS_8,
   STRING_ENCODING_INIS_CYRILLIC,
   STRING_ENCODING_ISO_5427_1981,
   STRING_ENCODING_ISO_5428_1980,
   STRING_ENCODING_GB_1988_80,
   STRING_ENCODING_GB_2312_80,
   STRING_ENCODING_NS_4551_2,
   STRING_ENCODING_VIDEOTEX_SUPPL,
   STRING_ENCODING_PT2,
   STRING_ENCODING_ES2,
   STRING_ENCODING_MSZ_7795_3,
   STRING_ENCODING_JIS_C6226_1983,
   STRING_ENCODING_GREEK7,
   STRING_ENCODING_ASMO_449,
   STRING_ENCODING_ISO_IR_90,
   STRING_ENCODING_JIS_C6229_1984_A,
   STRING_ENCODING_JIS_C6229_1984_B,
   STRING_ENCODING_JIS_C6229_1984_B_ADD,
   STRING_ENCODING_JIS_C6229_1984_HAND,
   STRING_ENCODING_JIS_C6229_1984_HAND_ADD,
   STRING_ENCODING_JIS_C6229_1984_KANA,
   STRING_ENCODING_ISO_2033_1983,
   STRING_ENCODING_ANSI_X3_110_1983,
   STRING_ENCODING_T_61_7BIT,
   STRING_ENCODING_T_61_8BIT,
   STRING_ENCODING_ECMA_CYRILLIC,
   STRING_ENCODING_CSA_Z243_4_1985_1,
   STRING_ENCODING_CSA_Z243_4_1985_2,
   STRING_ENCODING_CSA_Z243_4_1985_GR,
   STRING_ENCODING_ISO_8859_6_E,
   STRING_ENCODING_ISO_8859_6_I,
   STRING_ENCODING_T_101_G2,
   STRING_ENCODING_ISO_8859_8_E,
   STRING_ENCODING_ISO_8859_8_I,
   STRING_ENCODING_CSN_369103,
   STRING_ENCODING_JUS_I_B1_002,
   STRING_ENCODING_IEC_P27_1,
   STRING_ENCODING_JUS_I_B1_003_SERB,
   STRING_ENCODING_JUS_I_B1_003_MAC,
   STRING_ENCODING_GREEK_CCITT,
   STRING_ENCODING_NC_NC00_10_81,
   STRING_ENCODING_ISO_6937_2_25,
   STRING_ENCODING_GOST_19768_74,
   STRING_ENCODING_ISO_8859_SUPP,
   STRING_ENCODING_ISO_10367_BOX,
   STRING_ENCODING_LATIN_LAP,
   STRING_ENCODING_JIS_X0212_1990,
   STRING_ENCODING_DS_2089,
   STRING_ENCODING_US_DK,
   STRING_ENCODING_DK_US,
   STRING_ENCODING_KSC5636,
   STRING_ENCODING_UNICODE_1_1_UTF_7,
   STRING_ENCODING_ISO_2022_CN_EXT,
   STRING_ENCODING_ISO_8859_16,
   STRING_ENCODING_OSD_EBCDIC_DF04_15,
   STRING_ENCODING_OSD_EBCDIC_DF03_IRV,
   STRING_ENCODING_OSD_EBCDIC_DF04_1,
   STRING_ENCODING_ISO_11548_1,
   STRING_ENCODING_KZ_1048,
   STRING_ENCODING_ISO_10646_UCS_2,
   STRING_ENCODING_ISO_10646_UCS_4,
   STRING_ENCODING_ISO_10646_UCS_BASIC,
   STRING_ENCODING_ISO_10646_UNICODE_LATIN1,
   STRING_ENCODING_ISO_10646_J_1,
   STRING_ENCODING_ISO_UNICODE_IBM_1261,
   STRING_ENCODING_ISO_UNICODE_IBM_1268,
   STRING_ENCODING_ISO_UNICODE_IBM_1276,
   STRING_ENCODING_ISO_UNICODE_IBM_1264,
   STRING_ENCODING_ISO_UNICODE_IBM_1265,
   STRING_ENCODING_UNICODE_1_1,
   STRING_ENCODING_SCSU,
   STRING_ENCODING_UTF_7,
   STRING_ENCODING_CESU_8,
   STRING_ENCODING_BOCU_1,
   STRING_ENCODING_ISO_8859_1_WINDOWS_3_0_LATIN_1,
   STRING_ENCODING_ISO_8859_1_WINDOWS_3_1_LATIN_1,
   STRING_ENCODING_ISO_8859_2_WINDOWS_LATIN_2,
   STRING_ENCODING_ISO_8859_9_WINDOWS_LATIN_5,
   STRING_ENCODING_HP_ROMAN8,
   STRING_ENCODING_ADOBE_STANDARD_ENCODING,
   STRING_ENCODING_VENTURA_US,
   STRING_ENCODING_VENTURA_INTERNATIONAL,
   STRING_ENCODING_DEC_MCS,
   STRING_ENCODING_IBM_850,
   STRING_ENCODING_PC8_DANISH_NORWEGIAN,
   STRING_ENCODING_IBM_862,
   STRING_ENCODING_PC8_TURKISH,
   STRING_ENCODING_IBM_SYMBOLS,
   STRING_ENCODING_IBM_THAI,
   STRING_ENCODING_HP_LEGAL,
   STRING_ENCODING_HP_PI_FONT,
   STRING_ENCODING_HP_MATH8,
   STRING_ENCODING_ADOBE_SYMBOL_ENCODING,
   STRING_ENCODING_HP_DESKTOP,
   STRING_ENCODING_VENTURA_MATH,
   STRING_ENCODING_MICROSOFT_PUBLISHING,
   STRING_ENCODING_WINDOWS_31J,
   STRING_ENCODING_MACINTOSH,
   STRING_ENCODING_IBM_037,
   STRING_ENCODING_IBM_038,
   STRING_ENCODING_IBM_273,
   STRING_ENCODING_IBM_274,
   STRING_ENCODING_IBM_275,
   STRING_ENCODING_IBM_277,
   STRING_ENCODING_IBM_278,
   STRING_ENCODING_IBM_280,
   STRING_ENCODING_IBM_281,
   STRING_ENCODING_IBM_284,
   STRING_ENCODING_IBM_285,
   STRING_ENCODING_IBM_290,
   STRING_ENCODING_IBM_297,
   STRING_ENCODING_IBM_420,
   STRING_ENCODING_IBM_423,
   STRING_ENCODING_IBM_424,
   STRING_ENCODING_IBM_437,
   STRING_ENCODING_IBM_500,
   STRING_ENCODING_IBM_851,
   STRING_ENCODING_IBM_852,
   STRING_ENCODING_IBM_855,
   STRING_ENCODING_IBM_857,
   STRING_ENCODING_IBM_860,
   STRING_ENCODING_IBM_861,
   STRING_ENCODING_IBM_863,
   STRING_ENCODING_IBM_864,
   STRING_ENCODING_IBM_865,
   STRING_ENCODING_IBM_868,
   STRING_ENCODING_IBM_869,
   STRING_ENCODING_IBM_870,
   STRING_ENCODING_IBM_871,
   STRING_ENCODING_IBM_880,
   STRING_ENCODING_IBM_891,
   STRING_ENCODING_IBM_903,
   STRING_ENCODING_IBM_904,
   STRING_ENCODING_IBM_905,
   STRING_ENCODING_IBM_918,
   STRING_ENCODING_IBM_1026,
   STRING_ENCODING_EBCDIC_AT_DE,
   STRING_ENCODING_EBCDIC_AT_DE_A,
   STRING_ENCODING_EBCDIC_CA_FR,
   STRING_ENCODING_EBCDIC_DK_NO,
   STRING_ENCODING_EBCDIC_DK_NO_A,
   STRING_ENCODING_EBCDIC_FI_SE,
   STRING_ENCODING_EBCDIC_FI_SE_A,
   STRING_ENCODING_EBCDIC_FR,
   STRING_ENCODING_EBCDIC_IT,
   STRING_ENCODING_EBCDIC_PT,
   STRING_ENCODING_EBCDIC_ES,
   STRING_ENCODING_EBCDIC_ES_A,
   STRING_ENCODING_EBCDIC_ES_S,
   STRING_ENCODING_EBCDIC_UK,
   STRING_ENCODING_EBCDIC_US,
   STRING_ENCODING_UNKNOWN_8BIT,
   STRING_ENCODING_MNEMONIC,
   STRING_ENCODING_MNEM,
   STRING_ENCODING_VISCII,
   STRING_ENCODING_VIQR,
   STRING_ENCODING_KOI8_R,
   STRING_ENCODING_HZ_GB_2312,
   STRING_ENCODING_IBM_866,
   STRING_ENCODING_IBM_775,
   STRING_ENCODING_KOI8_U,
   STRING_ENCODING_IBM_00858,
   STRING_ENCODING_IBM_00924,
   STRING_ENCODING_IBM_01140,
   STRING_ENCODING_IBM_01141,
   STRING_ENCODING_IBM_01142,
   STRING_ENCODING_IBM_01143,
   STRING_ENCODING_IBM_01144,
   STRING_ENCODING_IBM_01145,
   STRING_ENCODING_IBM_01146,
   STRING_ENCODING_IBM_01147,
   STRING_ENCODING_IBM_01148,
   STRING_ENCODING_IBM_01149,
   STRING_ENCODING_IBM_1047,
   STRING_ENCODING_PTCP154,
   STRING_ENCODING_AMIGA_1251,
   STRING_ENCODING_KOI7_SWITCHED,
   STRING_ENCODING_BRF,
   STRING_ENCODING_TSCII,
   STRING_ENCODING_TIS_620,
   STRING_ENCODING_WINDOWS_709,
   STRING_ENCODING_WINDOWS_710,
   STRING_ENCODING_WINDOWS_720,
   STRING_ENCODING_WINDOWS_737,
   STRING_ENCODING_WINDOWS_875,
   STRING_ENCODING_WINDOWS_1361,
   STRING_ENCODING_WINDOWS_10000,
   STRING_ENCODING_WINDOWS_10001,
   STRING_ENCODING_WINDOWS_10002,
   STRING_ENCODING_WINDOWS_10003,
   STRING_ENCODING_WINDOWS_10004,
   STRING_ENCODING_WINDOWS_10005,
   STRING_ENCODING_WINDOWS_10006,
   STRING_ENCODING_WINDOWS_10007,
   STRING_ENCODING_WINDOWS_10008,
   STRING_ENCODING_WINDOWS_10010,
   STRING_ENCODING_WINDOWS_10017,
   STRING_ENCODING_WINDOWS_10021,
   STRING_ENCODING_WINDOWS_10029,
   STRING_ENCODING_WINDOWS_10079,
   STRING_ENCODING_WINDOWS_10081,
   STRING_ENCODING_WINDOWS_10082,
   STRING_ENCODING_WINDOWS_20000,
   STRING_ENCODING_WINDOWS_20001,
   STRING_ENCODING_WINDOWS_20002,
   STRING_ENCODING_WINDOWS_20003,
   STRING_ENCODING_WINDOWS_20004,
   STRING_ENCODING_WINDOWS_20005,
   STRING_ENCODING_WINDOWS_20105,
   STRING_ENCODING_WINDOWS_20106,
   STRING_ENCODING_WINDOWS_20107,
   STRING_ENCODING_WINDOWS_20108,
   STRING_ENCODING_WINDOWS_20269,
   STRING_ENCODING_WINDOWS_20833,
   STRING_ENCODING_WINDOWS_20949,
   STRING_ENCODING_WINDOWS_21025,
   STRING_ENCODING_WINDOWS_21027,
   STRING_ENCODING_WINDOWS_29001,
   STRING_ENCODING_WINDOWS_38598,
   STRING_ENCODING_WINDOWS_50221,
   STRING_ENCODING_WINDOWS_50222,
   STRING_ENCODING_WINDOWS_50229,
   STRING_ENCODING_WINDOWS_50930,
   STRING_ENCODING_WINDOWS_50931,
   STRING_ENCODING_WINDOWS_50933,
   STRING_ENCODING_WINDOWS_50935,
   STRING_ENCODING_WINDOWS_50936,
   STRING_ENCODING_WINDOWS_50937,
   STRING_ENCODING_WINDOWS_50939,
   STRING_ENCODING_WINDOWS_51936,
   STRING_ENCODING_WINDOWS_51950,
   STRING_ENCODING_WINDOWS_57002,
   STRING_ENCODING_WINDOWS_57003,
   STRING_ENCODING_WINDOWS_57004,
   STRING_ENCODING_WINDOWS_57005,
   STRING_ENCODING_WINDOWS_57006,
   STRING_ENCODING_WINDOWS_57007,
   STRING_ENCODING_WINDOWS_57008,
   STRING_ENCODING_WINDOWS_57009,
   STRING_ENCODING_WINDOWS_57010,
   STRING_ENCODING_WINDOWS_57011,
   STRING_ENCODING_IBM_813,
   STRING_ENCODING_IBM_943_P130_1999,
   STRING_ENCODING_IBM_33722,
   STRING_ENCODING_WINDOWS_949,
   STRING_ENCODING_IBM_1363,
   STRING_ENCODING_IBM_1386,
   STRING_ENCODING_IBM_1373,
   STRING_ENCODING_IBM_5471,
   STRING_ENCODING_IBM_874,

   // Add more encodings here.

   // Sentinel value after the last explicitly specified encoding.
   STRING_ENCODING_MAX_SPECIFIED,

   /*
    * The environment-specified "default" encoding for this process.
    */

   STRING_ENCODING_DEFAULT = -1,
   STRING_ENCODING_UNKNOWN = -2,

   /*
    * UTF-16 and UTF-32 in native byte order.
    */

   STRING_ENCODING_UTF16 = STRING_ENCODING_UTF16_LE,
   STRING_ENCODING_UTF32 = STRING_ENCODING_UTF32_LE,
} StringEncoding;


const char *Unicode_EncodingEnumToName(StringEncoding encoding);
StringEncoding Unicode_EncodingNameToEnum(const char *encodingName);
Bool Unicode_IsEncodingValid(StringEncoding encoding);
void Unicode_Init(int argc, char ***argv, char ***envp);
void Unicode_InitEx(int argc, char ***argv, char ***envp,
                    const char *icuDataDir);
#if defined (_WIN32)
void Unicode_InitW(int argc, utf16_t **wargv, utf16_t **wenvp,
                   char ***argv, char ***envp);
#endif

void Unicode_Shutdown(int argc, char **argv, char **envp);

StringEncoding Unicode_GetCurrentEncoding(void);
StringEncoding Unicode_ResolveEncoding(StringEncoding encoding);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _UNICODE_TYPES_H_
 07070100000120000081A40000000000000000000000016822550500002E56000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/userlock.h /*********************************************************
 * Copyright (c) 2009-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _USERLOCK_H_
#define _USERLOCK_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include <stdarg.h>

#include "vm_atomic.h"
#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "mutexRank.h"
#include "vthreadBase.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef struct MXUserExclLock      MXUserExclLock;
typedef struct MXUserRecLock       MXUserRecLock;
typedef struct MXUserRWLock        MXUserRWLock;
typedef struct MXUserRankLock      MXUserRankLock;
typedef struct MXUserCondVar       MXUserCondVar;
typedef struct MXUserSemaphore     MXUserSemaphore;
typedef struct MXUserBinSemaphore  MXUserBinSemaphore;
typedef struct MXUserEvent         MXUserEvent;
typedef struct MXUserBarrier       MXUserBarrier;

/*
 * Exclusive ownership lock
 */

MXUserExclLock *MXUser_CreateExclLock(const char *name,
                                      MX_Rank rank);

void MXUser_AcquireExclLock(MXUserExclLock *lock);
Bool MXUser_TryAcquireExclLock(MXUserExclLock *lock);
void MXUser_ReleaseExclLock(MXUserExclLock *lock);
void MXUser_DestroyExclLock(MXUserExclLock *lock);
Bool MXUser_IsCurThreadHoldingExclLock(MXUserExclLock *lock);

/* Use only when necessary */
MXUserExclLock *MXUser_CreateSingletonExclLockInt(Atomic_Ptr *lockStorage,
                                                  const char *name,
                                                  MX_Rank rank);

/* This is the public interface */
static INLINE MXUserExclLock *
MXUser_CreateSingletonExclLock(Atomic_Ptr *lockStorage,
                               const char *name,
                               MX_Rank rank)
{
   MXUserExclLock *lock;

   ASSERT(lockStorage);

   lock = (MXUserExclLock *) Atomic_ReadPtr(lockStorage);

   if (UNLIKELY(lock == NULL)) {
      lock = MXUser_CreateSingletonExclLockInt(lockStorage, name, rank);
   }

   return lock;
}

MXUserCondVar *MXUser_CreateCondVarExclLock(MXUserExclLock *lock);

void MXUser_WaitCondVarExclLock(MXUserExclLock *lock,
                                MXUserCondVar *condVar);

void MXUser_TimedWaitCondVarExclLock(MXUserExclLock *lock,
                                     MXUserCondVar *condVar,
                                     uint32 waitTimeMS);

Bool MXUser_EnableStatsExclLock(MXUserExclLock *lock,
                                Bool trackAcquisitionTime,
                                Bool trackHeldTime);

Bool MXUser_DisableStatsExclLock(MXUserExclLock *lock);

Bool MXUser_SetContentionRatioFloorExclLock(MXUserExclLock *lock,
                                            double ratio);

Bool MXUser_SetContentionCountFloorExclLock(MXUserExclLock *lock,
                                            uint64 count);

Bool MXUser_SetContentionDurationFloorExclLock(MXUserExclLock *lock,
                                               uint64 count);

/*
 * Recursive lock.
 */

MXUserRecLock *MXUser_CreateRecLock(const char *name,
                                    MX_Rank rank);

void MXUser_AcquireRecLock(MXUserRecLock *lock);
Bool MXUser_TryAcquireRecLock(MXUserRecLock *lock);
void MXUser_ReleaseRecLock(MXUserRecLock *lock);
void MXUser_DestroyRecLock(MXUserRecLock *lock);
Bool MXUser_IsCurThreadHoldingRecLock(MXUserRecLock *lock);

/* Use only when necessary */
MXUserRecLock *MXUser_CreateSingletonRecLockInt(Atomic_Ptr *lockStorage,
                                                const char *name,
                                                MX_Rank rank);

/* This is the public interface */
static INLINE MXUserRecLock *
MXUser_CreateSingletonRecLock(Atomic_Ptr *lockStorage,
                              const char *name,
                              MX_Rank rank)
{
   MXUserRecLock *lock;

   ASSERT(lockStorage);

   lock = (MXUserRecLock *) Atomic_ReadPtr(lockStorage);

   if (UNLIKELY(lock == NULL)) {
      lock = MXUser_CreateSingletonRecLockInt(lockStorage, name, rank);
   }

   return lock;
}

void MXUser_DumpRecLock(MXUserRecLock *lock);

MXUserCondVar *MXUser_CreateCondVarRecLock(MXUserRecLock *lock);

void MXUser_WaitCondVarRecLock(MXUserRecLock *lock,
                               MXUserCondVar *condVar);

void MXUser_TimedWaitCondVarRecLock(MXUserRecLock *lock,
                                    MXUserCondVar *condVar,
                                    uint32 waitTimeMS);

void MXUser_IncRefRecLock(MXUserRecLock *lock);

void MXUser_DecRefRecLock(MXUserRecLock *lock);

Bool MXUser_EnableStatsRecLock(MXUserRecLock *lock,
                               Bool trackAcquisitionTime,
                               Bool trackHeldTime);

Bool MXUser_DisableStatsRecLock(MXUserRecLock *lock);

Bool MXUser_SetContentionRatioFloorRecLock(MXUserRecLock *lock,
                                           double ratio);

Bool MXUser_SetContentionCountFloorRecLock(MXUserRecLock *lock,
                                           uint64 count);

Bool MXUser_SetContentionDurationFloorRecLock(MXUserRecLock *lock,
                                              uint64 count);


/*
 * Read-write lock
 */

MXUserRWLock *MXUser_CreateRWLock(const char *name,
                                   MX_Rank rank);

void MXUser_AcquireForRead(MXUserRWLock *lock);
void MXUser_AcquireForWrite(MXUserRWLock *lock);
void MXUser_ReleaseRWLock(MXUserRWLock *lock);
void MXUser_DestroyRWLock(MXUserRWLock *lock);

#define MXUSER_RW_FOR_READ   0
#define MXUSER_RW_FOR_WRITE  1
#define MXUSER_RW_LOCKED     2

Bool MXUser_IsCurThreadHoldingRWLock(MXUserRWLock *lock,
                                     uint32 queryType);

/* Use only when necessary */
MXUserRWLock *MXUser_CreateSingletonRWLockInt(Atomic_Ptr *lockStorage,
                                              const char *name,
                                              MX_Rank rank);

/* This is the public interface */
static INLINE MXUserRWLock *
MXUser_CreateSingletonRWLock(Atomic_Ptr *lockStorage,
                             const char *name,
                             MX_Rank rank)
{
   MXUserRWLock *lock;

   ASSERT(lockStorage);

   lock = (MXUserRWLock *) Atomic_ReadPtr(lockStorage);

   if (UNLIKELY(lock == NULL)) {
      lock = MXUser_CreateSingletonRWLockInt(lockStorage, name, rank);
   }

   return lock;
}

/*
 * Stateful auto-reset event
 */

MXUserEvent *MXUser_CreateEvent(const char *name,
                                MX_Rank rank);

void MXUser_SignalEvent(MXUserEvent *event);
void MXUser_WaitEvent(MXUserEvent *event);
Bool MXUser_TryWaitEvent(MXUserEvent *event);
PollDevHandle MXUser_GetHandleForEvent(MXUserEvent *event);
void MXUser_DestroyEvent(MXUserEvent *event);

MXUserEvent *MXUser_CreateSingletonEvent(Atomic_Ptr *eventStorage,
                                         const char *name,
                                         MX_Rank rank);

/*
 * Computational barrier
 */

MXUserBarrier *MXUser_CreateBarrier(const char *name,
                                    MX_Rank rank,
                                    uint32 count);

void MXUser_DestroyBarrier(MXUserBarrier *barrier);
void MXUser_EnterBarrier(MXUserBarrier *barrier);

MXUserBarrier *MXUser_CreateSingletonBarrier(Atomic_Ptr *barrierStorage,
                                             const char *name,
                                             MX_Rank rank,
                                             uint32 count);

/*
 * Counting semaphore
 */

MXUserSemaphore *MXUser_CreateSemaphore(const char *name,
                                        MX_Rank rank);

void MXUser_DestroySemaphore(MXUserSemaphore *sema);
void MXUser_UpSemaphore(MXUserSemaphore *sema);
void MXUser_DownSemaphore(MXUserSemaphore *sema);
Bool MXUser_TryDownSemaphore(MXUserSemaphore *sema);

Bool MXUser_TimedDownSemaphore(MXUserSemaphore *sema,
                               uint32 waitTimeMS);
Bool MXUser_TimedDownSemaphoreNS(MXUserSemaphore *sema,
                                 uint64 waitTimeNS);

MXUserSemaphore *MXUser_CreateSingletonSemaphore(Atomic_Ptr *semaStorage,
                                                 const char *name,
                                                 MX_Rank rank);

/*
 * Binary semaphore
 */

MXUserBinSemaphore *MXUser_CreateBinSemaphore(const char *name,
                                              MX_Rank rank);

void MXUser_DestroyBinSemaphore(MXUserBinSemaphore *binSema);
void MXUser_UpBinSemaphore(MXUserBinSemaphore *binSema);
void MXUser_DownBinSemaphore(MXUserBinSemaphore *binSema);
Bool MXUser_TryDownBinSemaphore(MXUserBinSemaphore *binSema);

Bool MXUser_TimedDownBinSemaphore(MXUserBinSemaphore *binSema,
                                  uint32 waitTimeMS);
Bool MXUser_TimedDownBinSemaphoreNS(MXUserBinSemaphore *binSema,
                                    uint64 waitTimeNS);

/*
 * Rank lock
 *
 * Rank "locks" are entities that perform rank checking but do not provide
 * any form of mutual exclusion. Their main use is for protecting certain
 * situations involving Poll and friends/enemies.
 */

MXUserRankLock *MXUser_CreateRankLock(const char *name,
                                      MX_Rank rank);

void MXUser_AcquireRankLock(MXUserRankLock *lock);
void MXUser_ReleaseRankLock(MXUserRankLock *lock);
void MXUser_DestroyRankLock(MXUserRankLock *lock);

/*
 * Generic conditional variable functions.
 */

#define MXUSER_WAIT_INFINITE 0xFFFFFFFF

void MXUser_SignalCondVar(MXUserCondVar *condVar);
void MXUser_BroadcastCondVar(MXUserCondVar *condVar);
void MXUser_DestroyCondVar(MXUserCondVar *condVar);

void MXUser_LockingTreeCollection(Bool enabled);
Bool MXUser_IsLockingTreeAvailable(void);
void MXUser_DumpLockTree(const char *fileName,
                         const char *timeStamp);

void MXUser_EmptyLockTree(void);


#if defined(VMX86_DEBUG) && !defined(DISABLE_MXUSER_DEBUG)
#define MXUSER_DEBUG  // debugging "everywhere" when requested
#endif

#if defined(MXUSER_DEBUG)
void MXUser_TryAcquireFailureControl(Bool (*func)(const char *lockName));
Bool MXUser_IsCurThreadHoldingLocks(void);
#endif
void MXUser_StatisticsControl(double contentionRatioFloor,
                              uint64 minAccessCountFloor,
                              uint64 contentionDurationFloor);

void MXUser_PerLockData(void);
void MXUser_SetStatsFunc(void *context,
                         uint32 maxLineLength,
                         Bool trackHeldTime,
                         void (*statsFunc)(void *context,
                                           const char *fmt,
                                           va_list ap));

void MXUser_SetInPanic(void);
Bool MXUser_InPanic(void);

struct MX_MutexRec;

MXUserRecLock       *MXUser_BindMXMutexRec(struct MX_MutexRec *mutex,
                                           MX_Rank rank);

struct MX_MutexRec  *MXUser_GetRecLockVmm(MXUserRecLock *lock);
MX_Rank              MXUser_GetRecLockRank(MXUserRecLock *lock);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif  // _USERLOCK_H_
  07070100000121000081A4000000000000000000000001682255050000313A000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/util.h /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * util.h --
 *
 *    misc util functions
 */

#ifndef UTIL_H
#define UTIL_H

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#ifndef VMKBOOT
   #include <stdarg.h>
   #include <string.h>
   #include <stdlib.h>

   #ifdef _WIN32
      #ifdef USERLEVEL
         #include <tchar.h>   /* Needed for MBCS string functions */
         #include <windows.h> /* for definition of HANDLE */
      #endif
   #else
      #include <unistd.h>
      #include <sys/types.h>
      #include "errno.h"
   #endif
#endif
#include "vm_assert.h"
#include "vm_basic_defs.h"
#include "unicodeTypes.h"
#include "utilZero.h"

#if defined(__cplusplus)
extern "C" {
#endif

uint32 CRC_Compute(const uint8 *buf, int len);
uint32 Util_Checksum32(const uint32 *buf, int len);
uint32 Util_Checksum(const uint8 *buf, int len);
uint32 Util_Checksumv(void *iov, int numEntries);
uint32 Util_HashString(const char *str);
char *Util_ExpandString(const char *fileName);
void Util_ExitThread(int);
NORETURN void Util_ExitProcessAbruptly(int);
int Util_HasAdminPriv(void);
#if defined _WIN32 && defined USERLEVEL
int Util_TokenHasAdminPriv(HANDLE token);
#endif

Bool Util_Data2Buffer(char *buf, size_t bufSize, const void *data0,
                      size_t dataSize);
Bool Util_Data2BufferEx(char *buf, size_t bufSize, const void *data0,
                        size_t dataSize, char sep);

char *Util_GetCanonicalPath(const char *path);
#ifdef _WIN32
char *Util_CompatGetCanonicalPath(const char *path);
char *Util_GetCanonicalPathForHash(const char *path);
char *Util_CompatGetLowerCaseCanonicalPath(const char* path);
#endif
int Util_BumpNoFds(uint32 *cur, uint32 *wanted);
Bool Util_CanonicalPathsIdentical(const char *path1, const char *path2);
Bool Util_IsAbsolutePath(const char *path);

char *Util_DeriveFileName(const char *source,
                          const char *name,
                          const char *ext);

typedef struct UtilSingleUseResource UtilSingleUseResource;
UtilSingleUseResource *Util_SingleUseAcquire(const char *name);
void Util_SingleUseRelease(UtilSingleUseResource *res);

#ifndef _WIN32
Bool Util_IPv4AddrValid(const char *addr);
Bool Util_IPv6AddrValid(const char *addr);
Bool Util_IPAddrValid(const char *addr);
#endif

#if defined(__linux__) || defined(__FreeBSD__) || defined(sun)
Bool Util_GetProcessName(pid_t pid, char *bufOut, size_t bufOutSize);
#endif

#if defined __linux__ && !defined VMX86_SERVER
Bool Util_IsPhysicalSSD(const char* device);
#endif

// backtrace functions and utilities

#define UTIL_BACKTRACE_LINE_LEN (511)
typedef void (*Util_OutputFunc)(void *data, const char *fmt, ...);

void Util_Backtrace(int bugNr);
void Util_BacktraceWithFunc(int bugNr,
                            Util_OutputFunc outFunc,
                            void *outFuncData);

// sleep functions

void Util_Usleep(long usec);
void Util_Sleep(unsigned int sec);

int Util_CompareDotted(const char *s1, const char *s2);

/*
 * This enum defines how Util_GetOpt should handle non-option arguments:
 *
 * UTIL_NONOPT_PERMUTE: Permute argv so that all non-options are at the end.
 * UTIL_NONOPT_STOP:    Stop when first non-option argument is seen. (This is
 *                      the standard POSIX behavior.)
 * UTIL_NONOPT_ALL:     Return each non-option argument as if it were
 *                      an option with character code 1.
 */
typedef enum { UTIL_NONOPT_PERMUTE, UTIL_NONOPT_STOP, UTIL_NONOPT_ALL } Util_NonOptMode;
struct option;
int Util_GetOpt(int argc, char * const *argv, const struct option *opts,
                Util_NonOptMode mode, Bool manualErrorHandling);


#if defined(VMX86_STATS)
Bool Util_QueryCStResidency(uint32 *numCpus, uint32 *numCStates,
                            uint64 **transitns, uint64 **residency,
                            uint64 **transTime, uint64 **residTime);
#endif

#define UTIL_FASTRAND_SEED_MAX (0x7fffffff)
Bool Util_Throttle(uint32 count);
uint32 Util_FastRand(uint32 seed);

// Not thread safe!
void Util_OverrideHomeDir(const char *path);

Bool Util_MakeSureDirExistsAndAccessible(char const *path,
                                         unsigned int mode);

#if _WIN32
#   define DIRSEPS	      "\\"
#   define DIRSEPS_W	      L"\\"
#   define DIRSEPC	      '\\'
#   define DIRSEPC_W	      L'\\'
#   define VALID_DIRSEPS      "\\/"
#   define VALID_DIRSEPS_W    L"\\/"
#   define CUR_DIRS_W         L"."
#   define CUR_DIRC_W         L'.'
#else
#   define DIRSEPS	      "/"
#   define DIRSEPC	      '/'
#   define VALID_DIRSEPS      DIRSEPS
#endif

#define CURR_DIRS             "."
#define CURR_DIRC             '.'

/*
 *-----------------------------------------------------------------------
 *
 * Util_Safe[Malloc, Realloc, Calloc, Strdup] and
 * Util_Safe[Malloc, Realloc, Calloc, Strdup]Bug --
 *
 *      These functions work just like the standard C library functions
 *      (except Util_SafeStrdup[,Bug]() accept NULL, see below),
 *      but will not fail. Instead they Panic(), printing the file and
 *      line number of the caller, if the underlying library function
 *      fails.  The Util_SafeFnBug functions print bugNumber in the
 *      Panic() message.
 *
 *      These functions should only be used when there is no way to
 *      gracefully recover from the error condition.
 *
 *      The internal versions of these functions expect a bug number
 *      as the first argument.  If that bug number is something other
 *      than -1, the panic message will include the bug number.
 *
 *      Since Util_SafeStrdup[,Bug]() do not need to return NULL
 *      on error, they have been extended to accept the null pointer
 *      (and return it).  The competing view is that they should
 *      panic on NULL.  This is a convenience vs. strictness argument.
 *      Convenience wins.  -- edward
 *
 * Results:
 *      The freshly allocated memory.
 *
 * Side effects:
 *      Panic() if the library function fails.
 *
 *--------------------------------------------------------------------------
 */

void *UtilSafeMalloc0(size_t size);
void *UtilSafeMalloc1(size_t size,
                      int bugNumber, const char *file, int lineno);

void *UtilSafeRealloc0(void *ptr, size_t size);
void *UtilSafeRealloc1(void *ptr, size_t size,
                      int bugNumber, const char *file, int lineno);

void *UtilSafeCalloc0(size_t nmemb, size_t size);
void *UtilSafeCalloc1(size_t nmemb, size_t size,
                      int bugNumber, const char *file, int lineno);

char *UtilSafeStrdup0(const char *s);
char *UtilSafeStrdup1(const char *s,
                      int bugNumber, const char *file, int lineno);

char *UtilSafeStrndup0(const char *s, size_t n);
char *UtilSafeStrndup1(const char *s, size_t n,
                      int bugNumber, const char *file, int lineno);

/*
 * Debug builds carry extra arguments into the allocation functions for
 * better error reporting. Non-debug builds don't pay this extra overhead.
 */
#ifdef VMX86_DEBUG

#define Util_SafeMalloc(_size) \
   UtilSafeMalloc1((_size), -1, __FILE__, __LINE__)

#define Util_SafeMallocBug(_bugNr, _size) \
   UtilSafeMalloc1((_size),(_bugNr), __FILE__, __LINE__)

#define Util_SafeRealloc(_ptr, _size) \
   UtilSafeRealloc1((_ptr), (_size), -1, __FILE__, __LINE__)

#define Util_SafeReallocBug(_bugNr, _ptr, _size) \
   UtilSafeRealloc1((_ptr), (_size), (_bugNr), __FILE__, __LINE__)

#define Util_SafeCalloc(_nmemb, _size) \
   UtilSafeCalloc1((_nmemb), (_size), -1, __FILE__, __LINE__)

#define Util_SafeCallocBug(_bugNr, _nmemb, _size) \
   UtilSafeCalloc1((_nmemb), (_size), (_bugNr), __FILE__, __LINE__)

#define Util_SafeStrndup(_str, _size) \
   UtilSafeStrndup1((_str), (_size), -1, __FILE__, __LINE__)

#define Util_SafeStrndupBug(_bugNr, _str, _size) \
   UtilSafeStrndup1((_str), (_size), (_bugNr), __FILE__, __LINE__)

#define Util_SafeStrdup(_str) \
   UtilSafeStrdup1((_str), -1, __FILE__, __LINE__)

#define Util_SafeStrdupBug(_bugNr, _str) \
   UtilSafeStrdup1((_str), (_bugNr), __FILE__, __LINE__)

#else  /* VMX86_DEBUG */

#define Util_SafeMalloc(_size) \
   UtilSafeMalloc0((_size))

#define Util_SafeMallocBug(_bugNr, _size) \
   UtilSafeMalloc0((_size))

#define Util_SafeRealloc(_ptr, _size) \
   UtilSafeRealloc0((_ptr), (_size))

#define Util_SafeReallocBug(_ptr, _size) \
   UtilSafeRealloc0((_ptr), (_size))

#define Util_SafeCalloc(_nmemb, _size) \
   UtilSafeCalloc0((_nmemb), (_size))

#define Util_SafeCallocBug(_bugNr, _nmemb, _size) \
   UtilSafeCalloc0((_nmemb), (_size))

#define Util_SafeStrndup(_str, _size) \
   UtilSafeStrndup0((_str), (_size))

#define Util_SafeStrndupBug(_bugNr, _str, _size) \
   UtilSafeStrndup0((_str), (_size))

#define Util_SafeStrdup(_str) \
   UtilSafeStrdup0((_str))

#define Util_SafeStrdupBug(_bugNr, _str) \
   UtilSafeStrdup0((_str))

#endif  /* VMX86_DEBUG */


void *Util_Memdup(const void *src, size_t size);
void *Util_Memcpy(void *dest, const void *src, size_t count);
void  Util_Memfree(void *ptr);

Bool Util_ConstTimeMemDiff(const void *secret, const void *guess, size_t len);
Bool Util_ConstTimeStrDiff(const char *secret, const char *guess);


#ifndef VMKBOOT
/*
 *-----------------------------------------------------------------------------
 *
 * Util_FreeList --
 * Util_FreeStringList --
 *
 *      Free a list (actually a vector) of allocated objects.
 *      The list (vector) itself is also freed.
 *
 *      The list either has a specified length or is
 *      argv-style NULL terminated (if length is negative).
 *
 *      The list can be NULL, in which case no operation is performed.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      errno or Windows last error is preserved.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Util_FreeList(void **list,      // IN/OUT/OPT: the list to free
              ssize_t length)   // IN: the length
{
   // See Posix_Free.
   int err;

   if (list == NULL) {
      ASSERT(length <= 0);
      return;
   }

   err = errno;

   if (length >= 0) {
      ssize_t i;

      for (i = 0; i < length; i++) {
         free(list[i]);
         DEBUG_ONLY(list[i] = NULL);
      }
   } else {
      void **s;

      for (s = list; *s != NULL; s++) {
         free(*s);
         DEBUG_ONLY(*s = NULL);
      }
   }
   free(list);
   errno = err;
}

static INLINE void
Util_FreeStringList(char **list,      // IN/OUT/OPT: the list to free
                    ssize_t length)   // IN: the length
{
   Util_FreeList((void **) list, length);
}
#endif /* VMKBOOT */


/*
 *-----------------------------------------------------------------------------
 *
 * Util_Memcpy32 --
 *
 *      Special purpose version of memcpy that requires nbytes be a
 *      multiple of 4.  This assumption lets us have a very small,
 *      inlineable implementation.
 *
 * Results:
 *      dst
 *
 * Side effects:
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void *
Util_Memcpy32(void *dst,
              const void *src,
              size_t nbytes)
{
   ASSERT((nbytes % 4) == 0);
#if defined __GNUC__ && (defined(__i386__) || defined(__x86_64__))
   do {
      int dummy0, dummy1, dummy2;
      __asm__ __volatile__(
           "cld \n\t"
           "rep ; movsl"  "\n\t"
        : "=&c" (dummy0), "=&D" (dummy1), "=&S" (dummy2)
        : "0" (nbytes / 4), "1" ((long) dst), "2" ((long) src)
        : "memory", "cc"
      );
      return dst;
   } while (0);
#else
   return memcpy(dst, src, nbytes);
#endif
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* UTIL_H */
  07070100000122000081A40000000000000000000000016822550500001B1D000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/utilZero.h /*********************************************************
 * Copyright (c) 2017-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * utilZero.h --
 *
 *    Utility functions for zeroing memory, and verifying memory is
 *    zeroed.
 */

#ifndef UTIL_ZERO_H
#define UTIL_ZERO_H

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#ifdef VMKBOOT
   #include "vm_libc.h"
#else
   #include <string.h>
   #include <stdlib.h>
   #ifndef _WIN32
      #include <unistd.h>
      #include <sys/types.h>
      #include <errno.h>
   #endif
#endif

#include "vm_assert.h"
#include "vm_basic_defs.h"
#include "unicodeTypes.h"

#if defined(__cplusplus)
extern "C" {
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Util_ValidateBytes --
 *
 *      Check that memory is filled with the specified value.
 *
 * Results:
 *      NULL   No error
 *      !NULL  First address that doesn't have the proper value
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void *
Util_ValidateBytes(const void *ptr,  // IN: ptr to check
                   size_t size,      // IN: size of ptr
                   uint8 byteValue)  // IN: memory must be filled with this
{
   uint8 *p;
   uint8 *end;
   uint64 bigValue;

   ASSERT(ptr);

   if (size == 0) {
      return NULL;
   }

   p = (uint8 *) ptr;
   end = p + size;

   /* Compare bytes until a "nice" boundary is achieved. */
   while ((uintptr_t) p % sizeof bigValue) {
      if (*p != byteValue) {
         return p;
      }

      p++;

      if (p == end) {
         return NULL;
      }
   }

   /* Compare using a "nice sized" chunk for a long as possible. */
   memset(&bigValue, (int) byteValue, sizeof bigValue);

   while (p + sizeof bigValue <= end) {
      if (*((uint64 *) p) != bigValue) {
         /* That's not right... let the loop below report the exact address. */
         break;
      }

      size -= sizeof bigValue;
      p += sizeof bigValue;
   }

   /* Handle any trailing bytes. */
   while (p < end) {
      if (*p != byteValue) {
         return p;
      }

      p++;
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_BufferIsEmpty --
 *
 *    Determine if the specified buffer of 'len' bytes starting at 'base'
 *    is empty (i.e. full of zeroes).
 *
 * Results:
 *    TRUE  Yes
 *    FALSE No
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Util_BufferIsEmpty(void const *base,  // IN:
                   size_t len)        // IN:
{
   return Util_ValidateBytes(base, len, '\0') == NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_Zero --
 *
 *      Zeros out bufSize bytes of buf. NULL is legal.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Util_Zero(void *buf,       // OUT
          size_t bufSize)  // IN
{
   if (buf != NULL) {
#if defined _WIN32 && defined USERLEVEL
      /*
       * Simple memset calls might be optimized out.  See CERT advisory
       * MSC06-C.
       */
      SecureZeroMemory(buf, bufSize);
#else
      memset(buf, 0, bufSize);
#if !defined _WIN32
      /*
       * Memset calls before free might be optimized out.  See PR1248269.
       */
      __asm__ __volatile__("" : : "r"(&buf) : "memory");
#endif
#endif
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_ZeroString --
 *
 *      Zeros out a NULL-terminated string. NULL is legal.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Util_ZeroString(char *str)  // IN/OUT/OPT
{
   if (str != NULL) {
      Util_Zero(str, strlen(str));
   }
}


#ifndef VMKBOOT
/*
 *-----------------------------------------------------------------------------
 *
 * Util_ZeroFree --
 *
 *      Zeros out bufSize bytes of buf, and then frees it. NULL is
 *      legal.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      buf is zeroed, and then free() is called on it.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Util_ZeroFree(void *buf,       // OUT/OPT
              size_t bufSize)  // IN
{
   if (buf != NULL) {
      // See Posix_Free.
      int err = errno;
      Util_Zero(buf, bufSize);
      free(buf);
      errno = err;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_ZeroFreeString --
 *
 *      Zeros out a NULL-terminated string, and then frees it. NULL is
 *      legal.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      str is zeroed, and then free() is called on it.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Util_ZeroFreeString(char *str)  // IN/OUT/OPT
{
   if (str != NULL) {
      // See Posix_Free.
      int err = errno;
      Util_ZeroString(str);
      free(str);
      errno = err;
   }
}
#endif /* VMKBOOT */


#ifdef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * Util_ZeroFreeStringW --
 *
 *      Zeros out a NUL-terminated wide-character string, and then frees it.
 *      NULL is legal.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      str is zeroed, and then free() is called on it.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Util_ZeroFreeStringW(wchar_t *str)  // IN/OUT/OPT
{
   if (str != NULL) {
      // See Posix_Free.
      int err = errno;
      Util_Zero(str, wcslen(str) * sizeof *str);
      free(str);
      errno = err;
   }
}
#endif /* _WIN32 */

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* UTIL_ZERO_H */
   07070100000123000081A4000000000000000000000001682255050000189A000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/uuid.h /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * uuid.h --
 *
 *      UUID generation
 */

#ifndef _UUID_H_
#define _UUID_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL // for typedefs only
#include "includeCheck.h"

#if defined(__cplusplus)
extern "C" {
#endif

#define UUID_SIZE 16
#define UUID_STRSIZE (2*UUID_SIZE + 1)
#define UUID_MAXLEN 48

/*
 * ISO 11578 (now X.667 section 6.4) defines the canonical text format of a
 * UUID, which looks like this example:
 *
 *    "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
 *
 * It is always precisely 36 characters long (excluding terminating NUL).
 */
#define UUID_ISO_11578_LEN 36

typedef enum {
   UUID_WITH_PATH = 0,
   UUID_RANDOM,
   UUID_VPX_BIOS,
   UUID_VPX_INSTANCE,
   UUID_UNKNOWN
} UUIDStyle;

/* Scheme control */
#define UUID_CREATE_WS6      0  /* the WS6 scheme - "native" path */
#define UUID_CREATE_WS65     1  /* the WS65 scheme - UTF-8 path */
#define UUID_CREATE_ESXi2018 2  /* UTF-8 path, no host UUID for >= 2018 ESXi */
#define UUID_CREATE_CURRENT  2  /* the current scheme - always the latest */


/*
 * RFC 4122-compliant UUIDs and UEFI/Microsoft GUIDs are essentially
 * the same thing except for byte-ordering issues.  Although their
 * fields are named differently, the meanings are the same.  The only
 * important difference is that RFC 4122 recommends always storing and
 * transmitting binary UUIDs with the multi-byte fields in network
 * byte order (big-endian), while binary UEFI/Microsoft GUIDs are
 * typically stored and transmitted with the first three fields in x86
 * CPU native order (little-endian).  But both UUIDs and GUIDs use the
 * same canonical text format representation, in which the hex digits
 * are in big-endian order.  Details of each are below.
 */

/*
 * An RFC 4122-compliant UUID.
 *
 * See RFC 4122 section 4.1.2 (Layout and Byte Order). The RFC
 * recommends that multi-byte types be stored in big-endian (most
 * significant byte first) order.
 *
 * The UUID packed text string
 *    00112233-4455-6677-8899-AABBCCDDEEFF
 * represents
 *    timeLow = 0x00112233
 *    timeMid = 0x4455
 *    timeHiAndVersion = 0x6677
 *    clockSeqHiAndReserved = 0x88
 *    clockSeqLow = 0x99
 *    node[0] = 0xAA
 *    node[1] = 0xBB
 *    node[2] = 0xCC
 *    node[3] = 0xDD
 *    node[4] = 0xEE
 *    node[5] = 0xFF
 * and the structure is stored as the sequence of bytes
 *    00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 *
 * Confusingly, some applications use the field names from this
 * definition but store timeLow, timeMid, and timeHiAndVersion in
 * little-endian order as UEFI/Microsoft GUIDs do; for example, the
 * SMBIOS standard does so.  In that case, the structure is stored as
 * the sequence of bytes
 *    33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF
 */

#pragma pack(push, 1)
typedef struct {
   uint32 timeLow;
   uint16 timeMid;
   uint16 timeHiAndVersion;
   uint8  clockSeqHiAndReserved;
   uint8  clockSeqLow;
   uint8  node[6];
} UUIDRFC4122;
#pragma pack(pop)


/*
 * An EFI/UEFI/Microsoft-compliant GUID.
 *
 * See MdeModulePkg/Universal/DevicePathDxe/DevicePathFromText.c::StrToGuid(),
 * BaseTools/Source/C/Common/ParseInf.c::StringToGuid(),
 * http://en.wikipedia.org/wiki/GUID . All multi-byte types are stored in CPU
 * (a.k.a. native) byte order.
 *
 * The GUID packed text string
 *    00112233-4455-6677-8899-AABBCCDDEEFF
 * represents
 *    data1 = 0x00112233
 *    data2 = 0x4455
 *    data3 = 0x6677
 *    data4[0] = 0x88
 *    data4[1] = 0x99
 *    data4[2] = 0xAA
 *    data4[3] = 0xBB
 *    data4[4] = 0xCC
 *    data4[5] = 0xDD
 *    data4[6] = 0xEE
 *    data4[7] = 0xFF
 * and the structure is stored as the sequence of bytes
 *       big-endian CPU: 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
 *    little-endian CPU: 33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF
 */

#pragma pack(push, 1)
typedef struct {
   uint32 data1;
   uint16 data2;
   uint16 data3;
   uint8  data4[8];
} EFIGUID;
#pragma pack(pop)

Bool UUID_ConvertPackedToBin(EFIGUID *destID,
                             const char *text);

Bool UUID_ConvertPackedToUUIDRFC4122(UUIDRFC4122 *destID,
                                     const char *text);

Bool UUID_ConvertToBin(uint8 dest_id[UUID_SIZE],
                       const char *text);

char *UUID_ConvertToText(const uint8 id[UUID_SIZE]);

void UUID_ConvertToTextBuf(const uint8 id[UUID_SIZE],
                           char *buffer,
                           size_t len);

char *UUID_CreateLocation(const char *configFileFullPath,
                          int schemeControl);

char *UUID_CreateRandom(void);

Bool UUID_CreateRandomRFC4122V4(UUIDRFC4122 *id);

Bool UUID_CreateRandomEFI(EFIGUID *id);

char *UUID_CreateRandomVpxStyle(uint8 vpxdId,
                                UUIDStyle);

Bool UUID_IsUUIDGeneratedByThatVpxd(const uint8 *id,
                                    int vpxdInstanceId);

char *UUID_PackText(const char *text,
                    char *pack,
                    size_t packLen);

char *UUID_ProperHostUUID(void);

char *UUID_GetHostUUID(void);

UUIDStyle UUID_GetStyle(const uint8 *id);

Bool UUID_Equal(const uint8 id1[UUID_SIZE],
                const uint8 id2[UUID_SIZE]);

/* Like UUID_GetHostUUID, except gets actual host UUID */
char *UUID_GetRealHostUUID(void);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif
  07070100000124000081A4000000000000000000000001682255050000B587000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vix.h  /*********************************************************
 * Copyright (C) 2004-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * This is the C interface to the VIX API.
 * This is platform-independent.
 */

#ifndef _VIX_H_
#define _VIX_H_

#ifdef __cplusplus
extern "C" {
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Basic Types --
 *
 *-----------------------------------------------------------------------------
 */

#include "vm_basic_types.h"


typedef int VixHandle;
enum {
   VIX_INVALID_HANDLE   = 0,
};

/*
 * These are the types of handles.
 */
typedef int VixHandleType;
enum {
   VIX_HANDLETYPE_NONE                 = 0,
   VIX_HANDLETYPE_HOST                 = 2,
   VIX_HANDLETYPE_VM                   = 3,
   VIX_HANDLETYPE_NETWORK              = 5,
   VIX_HANDLETYPE_JOB                  = 6,
   VIX_HANDLETYPE_SNAPSHOT             = 7,
   VIX_HANDLETYPE_PROPERTY_LIST        = 9,
   VIX_HANDLETYPE_METADATA_CONTAINER   = 11
};

/*
 * The "//{{ Begin VIX_ERROR  }}" and "//{{ End VIX_ERROR }}" lines are
 * to bracket the error code definitions that will be copied over
 * to vixDiskLib.h during build time. If you modify these two lines, please
 * make sure you also change bora/lib/distribute/vixDiskLib.h and
 * bora/support/scripts/replaceVixErrors.py
 */

// {{ Begin VIX_ERROR }}
/*
 * An error is a 64-bit value. If there is no error, then the value is
 * set to VIX_OK. If there is an error, then the least significant bits
 * will be set to one of the integer error codes defined below. The more
 * significant bits may or may not be set to various values, depending on 
 * the errors.
 */
typedef uint64 VixError;
#define VIX_ERROR_CODE(err)   ((err) & 0xFFFF)
#define VIX_SUCCEEDED(err)    (VIX_OK == (err))
#define VIX_FAILED(err)       (VIX_OK != (err))

/*
 * The error codes are returned by all public VIX routines.
 */
enum {
   VIX_OK                                       = 0,

   /* General errors */
   VIX_E_FAIL                                   = 1,
   VIX_E_OUT_OF_MEMORY                          = 2,
   VIX_E_INVALID_ARG                            = 3,
   VIX_E_FILE_NOT_FOUND                         = 4,
   VIX_E_OBJECT_IS_BUSY                         = 5,
   VIX_E_NOT_SUPPORTED                          = 6,
   VIX_E_FILE_ERROR                             = 7,
   VIX_E_DISK_FULL                              = 8,
   VIX_E_INCORRECT_FILE_TYPE                    = 9,
   VIX_E_CANCELLED                              = 10,
   VIX_E_FILE_READ_ONLY                         = 11,
   VIX_E_FILE_ALREADY_EXISTS                    = 12,
   VIX_E_FILE_ACCESS_ERROR                      = 13,
   VIX_E_REQUIRES_LARGE_FILES                   = 14,
   VIX_E_FILE_ALREADY_LOCKED                    = 15,
   VIX_E_VMDB                                   = 16,
   VIX_E_NOT_SUPPORTED_ON_REMOTE_OBJECT         = 20,
   VIX_E_FILE_TOO_BIG                           = 21,
   VIX_E_FILE_NAME_INVALID                      = 22,
   VIX_E_ALREADY_EXISTS                         = 23,
   VIX_E_BUFFER_TOOSMALL                        = 24,
   VIX_E_OBJECT_NOT_FOUND                       = 25,
   VIX_E_HOST_NOT_CONNECTED                     = 26,
   VIX_E_INVALID_UTF8_STRING                    = 27,
   VIX_E_OPERATION_ALREADY_IN_PROGRESS          = 31,
   VIX_E_UNFINISHED_JOB                         = 29,
   VIX_E_NEED_KEY                               = 30,
   VIX_E_LICENSE                                = 32,
   VIX_E_VM_HOST_DISCONNECTED                   = 34,
   VIX_E_AUTHENTICATION_FAIL                    = 35,
   VIX_E_HOST_CONNECTION_LOST                   = 36,
   VIX_E_DUPLICATE_NAME                         = 41,
   VIX_E_ARGUMENT_TOO_BIG                       = 44,

   /* Handle Errors */
   VIX_E_INVALID_HANDLE                         = 1000,
   VIX_E_NOT_SUPPORTED_ON_HANDLE_TYPE           = 1001,
   VIX_E_TOO_MANY_HANDLES                       = 1002,

   /* XML errors */
   VIX_E_NOT_FOUND                              = 2000,
   VIX_E_TYPE_MISMATCH                          = 2001,
   VIX_E_INVALID_XML                            = 2002,

   /* VM Control Errors */
   VIX_E_TIMEOUT_WAITING_FOR_TOOLS              = 3000,
   VIX_E_UNRECOGNIZED_COMMAND                   = 3001,
   VIX_E_OP_NOT_SUPPORTED_ON_GUEST              = 3003,
   VIX_E_PROGRAM_NOT_STARTED                    = 3004,
   VIX_E_CANNOT_START_READ_ONLY_VM              = 3005,
   VIX_E_VM_NOT_RUNNING                         = 3006,
   VIX_E_VM_IS_RUNNING                          = 3007,
   VIX_E_CANNOT_CONNECT_TO_VM                   = 3008,
   VIX_E_POWEROP_SCRIPTS_NOT_AVAILABLE          = 3009,
   VIX_E_NO_GUEST_OS_INSTALLED                  = 3010,
   VIX_E_VM_INSUFFICIENT_HOST_MEMORY            = 3011,
   VIX_E_SUSPEND_ERROR                          = 3012,
   VIX_E_VM_NOT_ENOUGH_CPUS                     = 3013,
   VIX_E_HOST_USER_PERMISSIONS                  = 3014,
   VIX_E_GUEST_USER_PERMISSIONS                 = 3015,
   VIX_E_TOOLS_NOT_RUNNING                      = 3016,
   VIX_E_GUEST_OPERATIONS_PROHIBITED            = 3017,
   VIX_E_ANON_GUEST_OPERATIONS_PROHIBITED       = 3018,
   VIX_E_ROOT_GUEST_OPERATIONS_PROHIBITED       = 3019,
   VIX_E_MISSING_ANON_GUEST_ACCOUNT             = 3023,
   VIX_E_CANNOT_AUTHENTICATE_WITH_GUEST         = 3024,
   VIX_E_UNRECOGNIZED_COMMAND_IN_GUEST          = 3025,
   VIX_E_CONSOLE_GUEST_OPERATIONS_PROHIBITED    = 3026,
   VIX_E_MUST_BE_CONSOLE_USER                   = 3027,
   VIX_E_VMX_MSG_DIALOG_AND_NO_UI               = 3028,
   /* VIX_E_NOT_ALLOWED_DURING_VM_RECORDING        = 3029, Removed in version 1.11 */
   /* VIX_E_NOT_ALLOWED_DURING_VM_REPLAY           = 3030, Removed in version 1.11 */
   VIX_E_OPERATION_NOT_ALLOWED_FOR_LOGIN_TYPE   = 3031,
   VIX_E_LOGIN_TYPE_NOT_SUPPORTED               = 3032,
   VIX_E_EMPTY_PASSWORD_NOT_ALLOWED_IN_GUEST    = 3033,
   VIX_E_INTERACTIVE_SESSION_NOT_PRESENT        = 3034,
   VIX_E_INTERACTIVE_SESSION_USER_MISMATCH      = 3035,
   /* VIX_E_UNABLE_TO_REPLAY_VM                    = 3039, Removed in version 1.11 */
   VIX_E_CANNOT_POWER_ON_VM                     = 3041,
   VIX_E_NO_DISPLAY_SERVER                      = 3043,
   /* VIX_E_VM_NOT_RECORDING                       = 3044, Removed in version 1.11 */
   /* VIX_E_VM_NOT_REPLAYING                       = 3045, Removed in version 1.11 */
   VIX_E_TOO_MANY_LOGONS                        = 3046,
   VIX_E_INVALID_AUTHENTICATION_SESSION         = 3047,

   /* VM Errors */
   VIX_E_VM_NOT_FOUND                           = 4000,
   VIX_E_NOT_SUPPORTED_FOR_VM_VERSION           = 4001,
   VIX_E_CANNOT_READ_VM_CONFIG                  = 4002,
   VIX_E_TEMPLATE_VM                            = 4003,
   VIX_E_VM_ALREADY_LOADED                      = 4004,
   VIX_E_VM_ALREADY_UP_TO_DATE                  = 4006,
   VIX_E_VM_UNSUPPORTED_GUEST                   = 4011,

   /* Property Errors */
   VIX_E_UNRECOGNIZED_PROPERTY                  = 6000,
   VIX_E_INVALID_PROPERTY_VALUE                 = 6001,
   VIX_E_READ_ONLY_PROPERTY                     = 6002,
   VIX_E_MISSING_REQUIRED_PROPERTY              = 6003,
   VIX_E_INVALID_SERIALIZED_DATA                = 6004,
   VIX_E_PROPERTY_TYPE_MISMATCH                 = 6005,

   /* Completion Errors */
   VIX_E_BAD_VM_INDEX                           = 8000,

   /* Message errors */
   VIX_E_INVALID_MESSAGE_HEADER                 = 10000,
   VIX_E_INVALID_MESSAGE_BODY                   = 10001,

   /* Snapshot errors */
   VIX_E_SNAPSHOT_INVAL                         = 13000,
   VIX_E_SNAPSHOT_DUMPER                        = 13001,
   VIX_E_SNAPSHOT_DISKLIB                       = 13002,
   VIX_E_SNAPSHOT_NOTFOUND                      = 13003,
   VIX_E_SNAPSHOT_EXISTS                        = 13004,
   VIX_E_SNAPSHOT_VERSION                       = 13005,
   VIX_E_SNAPSHOT_NOPERM                        = 13006,
   VIX_E_SNAPSHOT_CONFIG                        = 13007,
   VIX_E_SNAPSHOT_NOCHANGE                      = 13008,
   VIX_E_SNAPSHOT_CHECKPOINT                    = 13009,
   VIX_E_SNAPSHOT_LOCKED                        = 13010,
   VIX_E_SNAPSHOT_INCONSISTENT                  = 13011,
   VIX_E_SNAPSHOT_NAMETOOLONG                   = 13012,
   VIX_E_SNAPSHOT_VIXFILE                       = 13013,
   VIX_E_SNAPSHOT_DISKLOCKED                    = 13014,
   VIX_E_SNAPSHOT_DUPLICATEDDISK                = 13015,
   VIX_E_SNAPSHOT_INDEPENDENTDISK               = 13016,
   VIX_E_SNAPSHOT_NONUNIQUE_NAME                = 13017,
   VIX_E_SNAPSHOT_MEMORY_ON_INDEPENDENT_DISK    = 13018,
   VIX_E_SNAPSHOT_MAXSNAPSHOTS                  = 13019,
   VIX_E_SNAPSHOT_MIN_FREE_SPACE                = 13020,
   VIX_E_SNAPSHOT_HIERARCHY_TOODEEP             = 13021,
   // DEPRECRATED VIX_E_SNAPSHOT_RRSUSPEND                     = 13022,
   VIX_E_SNAPSHOT_NOT_REVERTABLE                = 13024,

   /* Host Errors */
   VIX_E_HOST_DISK_INVALID_VALUE                = 14003,
   VIX_E_HOST_DISK_SECTORSIZE                   = 14004,
   VIX_E_HOST_FILE_ERROR_EOF                    = 14005,
   VIX_E_HOST_NETBLKDEV_HANDSHAKE               = 14006,
   VIX_E_HOST_SOCKET_CREATION_ERROR             = 14007,
   VIX_E_HOST_SERVER_NOT_FOUND                  = 14008,
   VIX_E_HOST_NETWORK_CONN_REFUSED              = 14009,
   VIX_E_HOST_TCP_SOCKET_ERROR                  = 14010,
   VIX_E_HOST_TCP_CONN_LOST                     = 14011,
   VIX_E_HOST_NBD_HASHFILE_VOLUME               = 14012,
   VIX_E_HOST_NBD_HASHFILE_INIT                 = 14013,
   VIX_E_HOST_SERVER_SHUTDOWN                   = 14014,
   VIX_E_HOST_SERVER_NOT_AVAILABLE              = 14015,
   
   /* Disklib errors */
   VIX_E_DISK_INVAL                             = 16000,
   VIX_E_DISK_NOINIT                            = 16001,
   VIX_E_DISK_NOIO                              = 16002,
   VIX_E_DISK_PARTIALCHAIN                      = 16003,
   VIX_E_DISK_NEEDSREPAIR                       = 16006,
   VIX_E_DISK_OUTOFRANGE                        = 16007,
   VIX_E_DISK_CID_MISMATCH                      = 16008,
   VIX_E_DISK_CANTSHRINK                        = 16009,
   VIX_E_DISK_PARTMISMATCH                      = 16010,
   VIX_E_DISK_UNSUPPORTEDDISKVERSION            = 16011,
   VIX_E_DISK_OPENPARENT                        = 16012,
   VIX_E_DISK_NOTSUPPORTED                      = 16013,
   VIX_E_DISK_NEEDKEY                           = 16014,
   VIX_E_DISK_NOKEYOVERRIDE                     = 16015,
   VIX_E_DISK_NOTENCRYPTED                      = 16016,
   VIX_E_DISK_NOKEY                             = 16017,
   VIX_E_DISK_INVALIDPARTITIONTABLE             = 16018,
   VIX_E_DISK_NOTNORMAL                         = 16019,
   VIX_E_DISK_NOTENCDESC                        = 16020,
   VIX_E_DISK_NEEDVMFS                          = 16022,
   VIX_E_DISK_RAWTOOBIG                         = 16024,
   VIX_E_DISK_TOOMANYOPENFILES                  = 16027,
   VIX_E_DISK_TOOMANYREDO                       = 16028,
   VIX_E_DISK_RAWTOOSMALL                       = 16029,
   VIX_E_DISK_INVALIDCHAIN                      = 16030,
   VIX_E_DISK_KEY_NOTFOUND                      = 16052, // metadata key is not found
   VIX_E_DISK_SUBSYSTEM_INIT_FAIL               = 16053,
   VIX_E_DISK_INVALID_CONNECTION                = 16054,
   VIX_E_DISK_ENCODING                          = 16061,
   VIX_E_DISK_CANTREPAIR                        = 16062,
   VIX_E_DISK_INVALIDDISK                       = 16063,
   VIX_E_DISK_NOLICENSE                         = 16064,
   VIX_E_DISK_NODEVICE                          = 16065,
   VIX_E_DISK_UNSUPPORTEDDEVICE                 = 16066,
   VIX_E_DISK_CAPACITY_MISMATCH                 = 16067,
   VIX_E_DISK_PARENT_NOTALLOWED                 = 16068,
   VIX_E_DISK_ATTACH_ROOTLINK                   = 16069,

   /* Crypto Library Errors */
   VIX_E_CRYPTO_UNKNOWN_ALGORITHM               = 17000,
   VIX_E_CRYPTO_BAD_BUFFER_SIZE                 = 17001,
   VIX_E_CRYPTO_INVALID_OPERATION               = 17002,
   VIX_E_CRYPTO_RANDOM_DEVICE                   = 17003,
   VIX_E_CRYPTO_NEED_PASSWORD                   = 17004,
   VIX_E_CRYPTO_BAD_PASSWORD                    = 17005,
   VIX_E_CRYPTO_NOT_IN_DICTIONARY               = 17006,
   VIX_E_CRYPTO_NO_CRYPTO                       = 17007,
   VIX_E_CRYPTO_ERROR                           = 17008,
   VIX_E_CRYPTO_BAD_FORMAT                      = 17009,
   VIX_E_CRYPTO_LOCKED                          = 17010,
   VIX_E_CRYPTO_EMPTY                           = 17011,
   VIX_E_CRYPTO_KEYSAFE_LOCATOR                 = 17012,

   /* Remoting Errors. */
   VIX_E_CANNOT_CONNECT_TO_HOST                 = 18000,
   VIX_E_NOT_FOR_REMOTE_HOST                    = 18001,
   VIX_E_INVALID_HOSTNAME_SPECIFICATION         = 18002,
    
   /* Screen Capture Errors. */
   VIX_E_SCREEN_CAPTURE_ERROR                   = 19000,
   VIX_E_SCREEN_CAPTURE_BAD_FORMAT              = 19001,
   VIX_E_SCREEN_CAPTURE_COMPRESSION_FAIL        = 19002,
   VIX_E_SCREEN_CAPTURE_LARGE_DATA              = 19003,

   /* Guest Errors */
   VIX_E_GUEST_VOLUMES_NOT_FROZEN               = 20000,
   VIX_E_NOT_A_FILE                             = 20001,
   VIX_E_NOT_A_DIRECTORY                        = 20002,
   VIX_E_NO_SUCH_PROCESS                        = 20003,
   VIX_E_FILE_NAME_TOO_LONG                     = 20004,
   VIX_E_OPERATION_DISABLED                     = 20005,

   /* Tools install errors */
   VIX_E_TOOLS_INSTALL_NO_IMAGE                 = 21000,
   VIX_E_TOOLS_INSTALL_IMAGE_INACCESIBLE        = 21001,
   VIX_E_TOOLS_INSTALL_NO_DEVICE                = 21002,
   VIX_E_TOOLS_INSTALL_DEVICE_NOT_CONNECTED     = 21003,
   VIX_E_TOOLS_INSTALL_CANCELLED                = 21004,
   VIX_E_TOOLS_INSTALL_INIT_FAILED              = 21005,
   VIX_E_TOOLS_INSTALL_AUTO_NOT_SUPPORTED       = 21006,
   VIX_E_TOOLS_INSTALL_GUEST_NOT_READY          = 21007,
   VIX_E_TOOLS_INSTALL_SIG_CHECK_FAILED         = 21008,
   VIX_E_TOOLS_INSTALL_ERROR                    = 21009,
   VIX_E_TOOLS_INSTALL_ALREADY_UP_TO_DATE       = 21010,
   VIX_E_TOOLS_INSTALL_IN_PROGRESS              = 21011,
   VIX_E_TOOLS_INSTALL_IMAGE_COPY_FAILED        = 21012,

   /* Wrapper Errors */
   VIX_E_WRAPPER_WORKSTATION_NOT_INSTALLED      = 22001,
   VIX_E_WRAPPER_VERSION_NOT_FOUND              = 22002,
   VIX_E_WRAPPER_SERVICEPROVIDER_NOT_FOUND      = 22003,
   VIX_E_WRAPPER_PLAYER_NOT_INSTALLED           = 22004,
   VIX_E_WRAPPER_RUNTIME_NOT_INSTALLED          = 22005,
   VIX_E_WRAPPER_MULTIPLE_SERVICEPROVIDERS      = 22006,

   /* FuseMnt errors*/
   VIX_E_MNTAPI_MOUNTPT_NOT_FOUND               = 24000,
   VIX_E_MNTAPI_MOUNTPT_IN_USE                  = 24001,
   VIX_E_MNTAPI_DISK_NOT_FOUND                  = 24002,
   VIX_E_MNTAPI_DISK_NOT_MOUNTED                = 24003,
   VIX_E_MNTAPI_DISK_IS_MOUNTED                 = 24004,
   VIX_E_MNTAPI_DISK_NOT_SAFE                   = 24005,
   VIX_E_MNTAPI_DISK_CANT_OPEN                  = 24006,
   VIX_E_MNTAPI_CANT_READ_PARTS                 = 24007,
   VIX_E_MNTAPI_UMOUNT_APP_NOT_FOUND            = 24008,
   VIX_E_MNTAPI_UMOUNT                          = 24009,
   VIX_E_MNTAPI_NO_MOUNTABLE_PARTITONS          = 24010,
   VIX_E_MNTAPI_PARTITION_RANGE                 = 24011,
   VIX_E_MNTAPI_PERM                            = 24012,
   VIX_E_MNTAPI_DICT                            = 24013,
   VIX_E_MNTAPI_DICT_LOCKED                     = 24014,
   VIX_E_MNTAPI_OPEN_HANDLES                    = 24015,
   VIX_E_MNTAPI_CANT_MAKE_VAR_DIR               = 24016,
   VIX_E_MNTAPI_NO_ROOT                         = 24017,
   VIX_E_MNTAPI_LOOP_FAILED                     = 24018,
   VIX_E_MNTAPI_DAEMON                          = 24019,
   VIX_E_MNTAPI_INTERNAL                        = 24020,
   VIX_E_MNTAPI_SYSTEM                          = 24021,
   VIX_E_MNTAPI_NO_CONNECTION_DETAILS           = 24022,
   /* FuseMnt errors: Do not exceed 24299 */

   /* VixMntapi errors*/
   VIX_E_MNTAPI_INCOMPATIBLE_VERSION            = 24300,
   VIX_E_MNTAPI_OS_ERROR                        = 24301,
   VIX_E_MNTAPI_DRIVE_LETTER_IN_USE             = 24302,
   VIX_E_MNTAPI_DRIVE_LETTER_ALREADY_ASSIGNED   = 24303,
   VIX_E_MNTAPI_VOLUME_NOT_MOUNTED              = 24304,
   VIX_E_MNTAPI_VOLUME_ALREADY_MOUNTED          = 24305,
   VIX_E_MNTAPI_FORMAT_FAILURE                  = 24306,
   VIX_E_MNTAPI_NO_DRIVER                       = 24307,
   VIX_E_MNTAPI_ALREADY_OPENED                  = 24308,
   VIX_E_MNTAPI_ITEM_NOT_FOUND                  = 24309,
   VIX_E_MNTAPI_UNSUPPROTED_BOOT_LOADER         = 24310,
   VIX_E_MNTAPI_UNSUPPROTED_OS                  = 24311,
   VIX_E_MNTAPI_CODECONVERSION                  = 24312,
   VIX_E_MNTAPI_REGWRITE_ERROR                  = 24313,
   VIX_E_MNTAPI_UNSUPPORTED_FT_VOLUME           = 24314,
   VIX_E_MNTAPI_PARTITION_NOT_FOUND             = 24315,
   VIX_E_MNTAPI_PUTFILE_ERROR                   = 24316,
   VIX_E_MNTAPI_GETFILE_ERROR                   = 24317,
   VIX_E_MNTAPI_REG_NOT_OPENED                  = 24318,
   VIX_E_MNTAPI_REGDELKEY_ERROR                 = 24319,
   VIX_E_MNTAPI_CREATE_PARTITIONTABLE_ERROR     = 24320,
   VIX_E_MNTAPI_OPEN_FAILURE                    = 24321,
   VIX_E_MNTAPI_VOLUME_NOT_WRITABLE             = 24322,

   /* Success on operation that completes asynchronously */
   VIX_ASYNC                                    = 25000,

   /* Async errors */
   VIX_E_ASYNC_MIXEDMODE_UNSUPPORTED            = 26000,

   /* Network Errors */
   VIX_E_NET_HTTP_UNSUPPORTED_PROTOCOL     = 30001,
   VIX_E_NET_HTTP_URL_MALFORMAT            = 30003,
   VIX_E_NET_HTTP_COULDNT_RESOLVE_PROXY    = 30005,
   VIX_E_NET_HTTP_COULDNT_RESOLVE_HOST     = 30006,
   VIX_E_NET_HTTP_COULDNT_CONNECT          = 30007,
   VIX_E_NET_HTTP_HTTP_RETURNED_ERROR      = 30022,
   VIX_E_NET_HTTP_OPERATION_TIMEDOUT       = 30028,
   VIX_E_NET_HTTP_SSL_CONNECT_ERROR        = 30035,
   VIX_E_NET_HTTP_TOO_MANY_REDIRECTS       = 30047,
   VIX_E_NET_HTTP_TRANSFER                 = 30200,
   VIX_E_NET_HTTP_SSL_SECURITY             = 30201,
   VIX_E_NET_HTTP_GENERIC                  = 30202,
};

// {{ End VIX_ERROR }}

const char *Vix_GetErrorText(VixError err, const char *locale);


/*
 *-----------------------------------------------------------------------------
 *
 * VIX Handles --
 *
 * These are common functions that apply to handles of several types. 
 *-----------------------------------------------------------------------------
 */

/* 
 * VIX Property Type
 */

typedef int VixPropertyType;
enum {
   VIX_PROPERTYTYPE_ANY             = 0,
   VIX_PROPERTYTYPE_INTEGER         = 1,
   VIX_PROPERTYTYPE_STRING          = 2,
   VIX_PROPERTYTYPE_BOOL            = 3,
   VIX_PROPERTYTYPE_HANDLE          = 4,
   VIX_PROPERTYTYPE_INT64           = 5,
   VIX_PROPERTYTYPE_BLOB            = 6
};

/*
 * VIX Property ID's
 */

typedef int VixPropertyID;
enum {
   VIX_PROPERTY_NONE                                  = 0,

   /* Properties used by several handle types. */
   VIX_PROPERTY_META_DATA_CONTAINER                   = 2,

   /* VIX_HANDLETYPE_HOST properties */
   VIX_PROPERTY_HOST_HOSTTYPE                         = 50,
   VIX_PROPERTY_HOST_API_VERSION                      = 51,
   VIX_PROPERTY_HOST_SOFTWARE_VERSION                 = 52,

   /* VIX_HANDLETYPE_VM properties */
   VIX_PROPERTY_VM_NUM_VCPUS                          = 101,
   VIX_PROPERTY_VM_VMX_PATHNAME                       = 103, 
   VIX_PROPERTY_VM_VMTEAM_PATHNAME                    = 105, 
   VIX_PROPERTY_VM_MEMORY_SIZE                        = 106,
   VIX_PROPERTY_VM_READ_ONLY                          = 107,
   VIX_PROPERTY_VM_NAME                               = 108,
   VIX_PROPERTY_VM_GUESTOS                            = 109,
   VIX_PROPERTY_VM_IN_VMTEAM                          = 128,
   VIX_PROPERTY_VM_POWER_STATE                        = 129,
   VIX_PROPERTY_VM_TOOLS_STATE                        = 152,
   VIX_PROPERTY_VM_IS_RUNNING                         = 196,
   VIX_PROPERTY_VM_SUPPORTED_FEATURES                 = 197,
   /* VIX_PROPERTY_VM_IS_RECORDING                       = 236, Removed in version 1.11 */
   /* VIX_PROPERTY_VM_IS_REPLAYING                       = 237, Removed in version 1.11 */
   VIX_PROPERTY_VM_SSL_ERROR                          = 293,

   /* Result properties; these are returned by various procedures */
   VIX_PROPERTY_JOB_RESULT_ERROR_CODE                 = 3000,
   VIX_PROPERTY_JOB_RESULT_VM_IN_GROUP                = 3001,
   VIX_PROPERTY_JOB_RESULT_USER_MESSAGE               = 3002,
   VIX_PROPERTY_JOB_RESULT_EXIT_CODE                  = 3004,
   VIX_PROPERTY_JOB_RESULT_COMMAND_OUTPUT             = 3005,
   VIX_PROPERTY_JOB_RESULT_HANDLE                     = 3010,
   VIX_PROPERTY_JOB_RESULT_GUEST_OBJECT_EXISTS        = 3011,
   VIX_PROPERTY_JOB_RESULT_GUEST_PROGRAM_ELAPSED_TIME = 3017,
   VIX_PROPERTY_JOB_RESULT_GUEST_PROGRAM_EXIT_CODE    = 3018,
   VIX_PROPERTY_JOB_RESULT_ITEM_NAME                  = 3035,
   VIX_PROPERTY_JOB_RESULT_FOUND_ITEM_DESCRIPTION     = 3036,
   VIX_PROPERTY_JOB_RESULT_SHARED_FOLDER_COUNT        = 3046,
   VIX_PROPERTY_JOB_RESULT_SHARED_FOLDER_HOST         = 3048,
   VIX_PROPERTY_JOB_RESULT_SHARED_FOLDER_FLAGS        = 3049,
   VIX_PROPERTY_JOB_RESULT_PROCESS_ID                 = 3051,
   VIX_PROPERTY_JOB_RESULT_PROCESS_OWNER              = 3052,
   VIX_PROPERTY_JOB_RESULT_PROCESS_COMMAND            = 3053,
   VIX_PROPERTY_JOB_RESULT_FILE_FLAGS                 = 3054,
   VIX_PROPERTY_JOB_RESULT_PROCESS_START_TIME         = 3055,
   VIX_PROPERTY_JOB_RESULT_VM_VARIABLE_STRING         = 3056,
   VIX_PROPERTY_JOB_RESULT_PROCESS_BEING_DEBUGGED     = 3057,
   VIX_PROPERTY_JOB_RESULT_SCREEN_IMAGE_SIZE          = 3058,
   VIX_PROPERTY_JOB_RESULT_SCREEN_IMAGE_DATA          = 3059,
   VIX_PROPERTY_JOB_RESULT_FILE_SIZE                  = 3061,
   VIX_PROPERTY_JOB_RESULT_FILE_MOD_TIME              = 3062,
   VIX_PROPERTY_JOB_RESULT_EXTRA_ERROR_INFO           = 3084,

   /* Event properties; these are sent in the moreEventInfo for some events. */
   VIX_PROPERTY_FOUND_ITEM_LOCATION                   = 4010,

   /* VIX_HANDLETYPE_SNAPSHOT properties */
   VIX_PROPERTY_SNAPSHOT_DISPLAYNAME                  = 4200,   
   VIX_PROPERTY_SNAPSHOT_DESCRIPTION                  = 4201,
   VIX_PROPERTY_SNAPSHOT_POWERSTATE                   = 4205,
   /* VIX_PROPERTY_SNAPSHOT_IS_REPLAYABLE                = 4207, Removed in version 1.11 */

   VIX_PROPERTY_GUEST_SHAREDFOLDERS_SHARES_PATH       = 4525,

   /* Virtual machine encryption properties */
   VIX_PROPERTY_VM_ENCRYPTION_PASSWORD                = 7001,
};

/*
 * These are events that may be signalled by calling a procedure
 * of type VixEventProc.
 */

typedef int VixEventType;
enum {
   VIX_EVENTTYPE_JOB_COMPLETED          = 2,
   VIX_EVENTTYPE_JOB_PROGRESS           = 3,
   VIX_EVENTTYPE_FIND_ITEM              = 8,
   VIX_EVENTTYPE_CALLBACK_SIGNALLED     = 2,  // Deprecated - Use VIX_EVENTTYPE_JOB_COMPLETED instead.
};


/*
 * These are the property flags for each file.
 */

enum {
   VIX_FILE_ATTRIBUTES_DIRECTORY       = 0x0001,
   VIX_FILE_ATTRIBUTES_SYMLINK         = 0x0002,       
};


/*
 * Procedures of this type are called when an event happens on a handle.
 */

typedef void VixEventProc(VixHandle handle,
                          VixEventType eventType,
                          VixHandle moreEventInfo,
                          void *clientData);

/*
 * Handle Property functions
 */

void Vix_ReleaseHandle(VixHandle handle);

void Vix_AddRefHandle(VixHandle handle);

VixHandleType Vix_GetHandleType(VixHandle handle);

VixError Vix_GetProperties(VixHandle handle, 
                           VixPropertyID firstPropertyID, ...);

VixError Vix_GetPropertyType(VixHandle handle, VixPropertyID propertyID, 
                             VixPropertyType *propertyType);

void Vix_FreeBuffer(void *p);


/*
 *-----------------------------------------------------------------------------
 *
 * VIX Host --
 *
 *-----------------------------------------------------------------------------
 */

typedef int VixHostOptions;
enum {
   /*
    * The following option was removed in version 1.11.
      VIX_HOSTOPTION_USE_EVENT_PUMP        = 0x0008,
    */
   VIX_HOSTOPTION_VERIFY_SSL_CERT       = 0x4000,
};

typedef int VixServiceProvider;
enum {
   VIX_SERVICEPROVIDER_DEFAULT                   = 1,
   VIX_SERVICEPROVIDER_VMWARE_SERVER             = 2,
   VIX_SERVICEPROVIDER_VMWARE_WORKSTATION        = 3,
   VIX_SERVICEPROVIDER_VMWARE_PLAYER             = 4,
   VIX_SERVICEPROVIDER_VMWARE_VI_SERVER          = 10,
   VIX_SERVICEPROVIDER_VMWARE_WORKSTATION_SHARED = 11,
};

/*
 * VIX_API_VERSION tells VixHost_Connect to use the latest API version 
 * that is available for the product specified in the VixServiceProvider 
 * parameter.
 */
enum {
   VIX_API_VERSION      = -1
};

VixHandle VixHost_Connect(int apiVersion,
                          VixServiceProvider hostType,
                          const char *hostName,
                          int hostPort,
                          const char *userName,
                          const char *password,
                          VixHostOptions options,
                          VixHandle propertyListHandle,
                          VixEventProc *callbackProc,
                          void *clientData);
 
void VixHost_Disconnect(VixHandle hostHandle);

/*
 * VM Registration
 */

VixHandle VixHost_RegisterVM(VixHandle hostHandle,
                             const char *vmxFilePath,
                             VixEventProc *callbackProc,
                             void *clientData);

VixHandle VixHost_UnregisterVM(VixHandle hostHandle,
                               const char *vmxFilePath,
                               VixEventProc *callbackProc,
                               void *clientData);

/*
 * VM Search
 */

typedef int VixFindItemType;
enum {
   VIX_FIND_RUNNING_VMS         = 1,
   VIX_FIND_REGISTERED_VMS      = 4,
};

VixHandle VixHost_FindItems(VixHandle hostHandle,
                            VixFindItemType searchType,
                            VixHandle searchCriteria,
                            int32 timeout,
                            VixEventProc *callbackProc,
                            void *clientData);


/*
 * VixHost_OpenVM() supercedes VixVM_Open() since it allows for
 * the passing of option flags and extra data in the form of a
 * property list.
 * It is recommended to use VixHost_OpenVM() instead of VixVM_Open().
 */

typedef int VixVMOpenOptions;
enum {
   VIX_VMOPEN_NORMAL  = 0x0,
};

VixHandle VixHost_OpenVM(VixHandle hostHandle,
                         const char *vmxFilePathName,
                         VixVMOpenOptions options,
                         VixHandle propertyListHandle,
                         VixEventProc *callbackProc,
                         void *clientData);


/*
 * Following functions were removed in version 1.11.
 *
   typedef int VixPumpEventsOptions;
   enum {
    VIX_PUMPEVENTOPTION_NONE = 0,
   };
   void Vix_PumpEvents(VixHandle hostHandle, VixPumpEventsOptions options);

   VixHandle VixVM_OpenUrlInGuest(VixHandle vmHandle,
                                  const char *url,
                                  int windowState,
                                  VixHandle propertyListHandle,
                                  VixEventProc *callbackProc,
                                  void *clientData);
 */

/*
 *-----------------------------------------------------------------------------
 *
 * PropertyList --
 *
 *-----------------------------------------------------------------------------
 */

#ifndef VIX_HIDE_FROM_JAVA
VixError VixPropertyList_AllocPropertyList(VixHandle hostHandle,
                                           VixHandle *resultHandle, 
                                           int firstPropertyID,
                                           ...);
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * VIX VM --
 *
 * This describes the persistent configuration state of a single VM. The 
 * VM may or may not be running.
 *
 *-----------------------------------------------------------------------------
 */

VixHandle VixVM_Open(VixHandle hostHandle,
                     const char *vmxFilePathName,
                     VixEventProc *callbackProc,
                     void *clientData);

typedef int VixVMPowerOpOptions;
enum {
   VIX_VMPOWEROP_NORMAL                      = 0,
   VIX_VMPOWEROP_FROM_GUEST                  = 0x0004,
   VIX_VMPOWEROP_SUPPRESS_SNAPSHOT_POWERON   = 0x0080,
   VIX_VMPOWEROP_LAUNCH_GUI                  = 0x0200,
   VIX_VMPOWEROP_START_VM_PAUSED             = 0x1000,
};

/*
 * Power operations 
 */

VixHandle VixVM_PowerOn(VixHandle vmHandle,
                        VixVMPowerOpOptions powerOnOptions,
                        VixHandle propertyListHandle,
                        VixEventProc *callbackProc,
                        void *clientData);

VixHandle VixVM_PowerOff(VixHandle vmHandle,
                         VixVMPowerOpOptions powerOffOptions,
                         VixEventProc *callbackProc,
                         void *clientData);

VixHandle VixVM_Reset(VixHandle vmHandle,
                      VixVMPowerOpOptions resetOptions,
                      VixEventProc *callbackProc,
                      void *clientData);

VixHandle VixVM_Suspend(VixHandle vmHandle,
                        VixVMPowerOpOptions suspendOptions,
                        VixEventProc *callbackProc,
                        void *clientData);

VixHandle VixVM_Pause(VixHandle vmHandle,
                      int options,
                      VixHandle propertyList,
                      VixEventProc *callbackProc,
                      void *clientData);

VixHandle VixVM_Unpause(VixHandle vmHandle,
                        int options,
                        VixHandle propertyList,
                        VixEventProc *callbackProc,
                        void *clientData);

typedef int VixVMDeleteOptions;
enum {
   VIX_VMDELETE_DISK_FILES     = 0x0002,
};

VixHandle VixVM_Delete(VixHandle vmHandle,
                       VixVMDeleteOptions deleteOptions,
                       VixEventProc *callbackProc,
                       void *clientData);

/*
 * This is the state of an individual VM.  These values are bitwise flags.
 * The actual value returned for may be a bitwise OR of one more of these
 * flags, along with other reserved values not documented here.
 */

typedef int VixPowerState;
enum {
      VIX_POWERSTATE_POWERING_OFF    = 0x0001,
      VIX_POWERSTATE_POWERED_OFF     = 0x0002,
      VIX_POWERSTATE_POWERING_ON     = 0x0004,
      VIX_POWERSTATE_POWERED_ON      = 0x0008,
      VIX_POWERSTATE_SUSPENDING      = 0x0010,
      VIX_POWERSTATE_SUSPENDED       = 0x0020,
      VIX_POWERSTATE_TOOLS_RUNNING   = 0x0040,
      VIX_POWERSTATE_RESETTING       = 0x0080,
      VIX_POWERSTATE_BLOCKED_ON_MSG  = 0x0100,
      VIX_POWERSTATE_PAUSED          = 0x0200,
      VIX_POWERSTATE_RESUMING        = 0x0800,
};

typedef int VixToolsState;
enum {
      VIX_TOOLSSTATE_UNKNOWN           = 0x0001,
      VIX_TOOLSSTATE_RUNNING           = 0x0002,
      VIX_TOOLSSTATE_NOT_INSTALLED     = 0x0004,
};


/*
 * These flags describe optional functions supported by different
 * types of VM.
 */
enum {
      VIX_VM_SUPPORT_SHARED_FOLDERS       = 0x0001,
      VIX_VM_SUPPORT_MULTIPLE_SNAPSHOTS   = 0x0002,
      VIX_VM_SUPPORT_TOOLS_INSTALL        = 0x0004,
      VIX_VM_SUPPORT_HARDWARE_UPGRADE     = 0x0008,
};

/*
 * VIX_ADMINISTRATOR_USER_NAME and VIX_CONSOLE_USER_NAME are no longer
 * supported. If your code includes references to these constants please
 * update your code to use a valid guest username and password when calling 
 * VixVM_LoginInGuest(). 
 */

//#define VIX_ADMINISTRATOR_USER_NAME    "__VMware_Vix_Guest_User_Admin__"
//#define VIX_CONSOLE_USER_NAME          "__VMware_Vix_Guest_Console_User__"

/*
 * Guest operations
 */

VixHandle VixVM_WaitForToolsInGuest(VixHandle vmHandle,
                                    int timeoutInSeconds,
                                    VixEventProc *callbackProc,
                                    void *clientData);

/*
 * VixVM_LoginInGuest option flags.
 */
enum {
   VIX_LOGIN_IN_GUEST_REQUIRE_INTERACTIVE_ENVIRONMENT      = 0x08,                     
};

VixHandle VixVM_LoginInGuest(VixHandle vmHandle,
                             const char *userName,
                             const char *password,
                             int options,
                             VixEventProc *callbackProc,
                             void *clientData);

VixHandle VixVM_LogoutFromGuest(VixHandle vmHandle,
                                VixEventProc *callbackProc,
                                void *clientData);


/* 
 * Guest Process functions 
 */

typedef int VixRunProgramOptions;
enum {
   VIX_RUNPROGRAM_RETURN_IMMEDIATELY   = 0x0001,
   VIX_RUNPROGRAM_ACTIVATE_WINDOW      = 0x0002,
};

VixHandle VixVM_RunProgramInGuest(VixHandle vmHandle,
                                  const char *guestProgramName,
                                  const char *commandLineArgs,
                                  VixRunProgramOptions options,
                                  VixHandle propertyListHandle,
                                  VixEventProc *callbackProc,
                                  void *clientData);

VixHandle VixVM_ListProcessesInGuest(VixHandle vmHandle,
                                     int options,
                                     VixEventProc *callbackProc,
                                     void *clientData);

VixHandle VixVM_KillProcessInGuest(VixHandle vmHandle,
                                   uint64 pid,
                                   int options,
                                   VixEventProc *callbackProc,
                                   void *clientData);

VixHandle VixVM_RunScriptInGuest(VixHandle vmHandle,
                                 const char *interpreter,
                                 const char *scriptText,
                                 VixRunProgramOptions options,
                                 VixHandle propertyListHandle,
                                 VixEventProc *callbackProc,
                                 void *clientData);

/* 
 * Guest File functions 
 */

VixHandle VixVM_CopyFileFromHostToGuest(VixHandle vmHandle,
                                        const char *hostPathName,
                                        const char *guestPathName,
                                        int options,
                                        VixHandle propertyListHandle,
                                        VixEventProc *callbackProc,
                                        void *clientData);

VixHandle VixVM_CopyFileFromGuestToHost(VixHandle vmHandle,
                                        const char *guestPathName,
                                        const char *hostPathName,
                                        int options,
                                        VixHandle propertyListHandle,
                                        VixEventProc *callbackProc,
                                        void *clientData);

VixHandle VixVM_DeleteFileInGuest(VixHandle vmHandle,
                                  const char *guestPathName,
                                  VixEventProc *callbackProc,
                                  void *clientData);

VixHandle VixVM_FileExistsInGuest(VixHandle vmHandle,
                                  const char *guestPathName,
                                  VixEventProc *callbackProc,
                                  void *clientData);

VixHandle VixVM_RenameFileInGuest(VixHandle vmHandle,
                                  const char *oldName,
                                  const char *newName,
                                  int options,
                                  VixHandle propertyListHandle,
                                  VixEventProc *callbackProc,
                                  void *clientData);

VixHandle VixVM_CreateTempFileInGuest(VixHandle vmHandle,
                                      int options,
                                      VixHandle propertyListHandle,
                                      VixEventProc *callbackProc,
                                      void *clientData);

VixHandle VixVM_GetFileInfoInGuest(VixHandle vmHandle,
                                   const char *pathName,
                                   VixEventProc *callbackProc,
                                   void *clientData);


/* 
 * Guest Directory functions 
 */

VixHandle VixVM_ListDirectoryInGuest(VixHandle vmHandle,
                                     const char *pathName,
                                     int options,
                                     VixEventProc *callbackProc,
                                     void *clientData);

VixHandle VixVM_CreateDirectoryInGuest(VixHandle vmHandle,
                                       const char *pathName,
                                       VixHandle propertyListHandle,
                                       VixEventProc *callbackProc,
                                       void *clientData);

VixHandle VixVM_DeleteDirectoryInGuest(VixHandle vmHandle,
                                       const char *pathName,
                                       int options,
                                       VixEventProc *callbackProc,
                                       void *clientData);

VixHandle VixVM_DirectoryExistsInGuest(VixHandle vmHandle,
                                       const char *pathName,
                                       VixEventProc *callbackProc,
                                       void *clientData);

/*
 * Guest Variable Functions
 */
enum {
   VIX_VM_GUEST_VARIABLE            = 1,
   VIX_VM_CONFIG_RUNTIME_ONLY       = 2,
   VIX_GUEST_ENVIRONMENT_VARIABLE   = 3,
};     

VixHandle VixVM_ReadVariable(VixHandle vmHandle,
                             int variableType,
                             const char *name,
                             int options,
                             VixEventProc *callbackProc,
                             void *clientData);

VixHandle VixVM_WriteVariable(VixHandle vmHandle,
                              int variableType,
                              const char *valueName,
                              const char *value,
                              int options,
                              VixEventProc *callbackProc,
                              void *clientData);


/* 
 * Snapshot functions that operate on a VM
 */

VixError VixVM_GetNumRootSnapshots(VixHandle vmHandle,
                                   int *result);

VixError VixVM_GetRootSnapshot(VixHandle vmHandle,
                               int index,
                               VixHandle *snapshotHandle);

VixError VixVM_GetCurrentSnapshot(VixHandle vmHandle, 
                                  VixHandle *snapshotHandle);

VixError VixVM_GetNamedSnapshot(VixHandle vmHandle, 
                                const char *name,
                                VixHandle *snapshotHandle);

typedef int VixRemoveSnapshotOptions;
enum {
   VIX_SNAPSHOT_REMOVE_CHILDREN    = 0x0001,
};

VixHandle VixVM_RemoveSnapshot(VixHandle vmHandle, 
                               VixHandle snapshotHandle,
                               VixRemoveSnapshotOptions options,
                               VixEventProc *callbackProc,
                               void *clientData);

VixHandle VixVM_RevertToSnapshot(VixHandle vmHandle,
                                 VixHandle snapshotHandle,
                                 VixVMPowerOpOptions options,
                                 VixHandle propertyListHandle,
                                 VixEventProc *callbackProc,
                                 void *clientData);

typedef int VixCreateSnapshotOptions;
enum {
   VIX_SNAPSHOT_INCLUDE_MEMORY     = 0x0002,
};

VixHandle VixVM_CreateSnapshot(VixHandle vmHandle,
                               const char *name,
                               const char *description,
                               VixCreateSnapshotOptions options,
                               VixHandle propertyListHandle,
                               VixEventProc *callbackProc,
                               void *clientData);


/*
 * Shared Folders Functions
 */

/*
 * These are the flags describing each shared folder.
 */
typedef int VixMsgSharedFolderOptions;
enum  {
   VIX_SHAREDFOLDER_WRITE_ACCESS     = 0x04,
};

VixHandle VixVM_EnableSharedFolders(VixHandle vmHandle,
                                    Bool enabled,      
                                    int options,       
                                    VixEventProc *callbackProc,
                                    void *clientData); 

VixHandle VixVM_GetNumSharedFolders(VixHandle vmHandle,
                                    VixEventProc *callbackProc,
                                    void *clientData);

VixHandle VixVM_GetSharedFolderState(VixHandle vmHandle,
                                     int index,
                                     VixEventProc *callbackProc,
                                     void *clientData);

VixHandle VixVM_SetSharedFolderState(VixHandle vmHandle,
                                     const char *shareName,
                                     const char *hostPathName,
                                     VixMsgSharedFolderOptions flags,
                                     VixEventProc *callbackProc,
                                     void *clientData);

VixHandle VixVM_AddSharedFolder(VixHandle vmHandle,
                                const char *shareName,
                                const char *hostPathName,
                                VixMsgSharedFolderOptions flags,
                                VixEventProc *callbackProc,
                                void *clientData);

VixHandle VixVM_RemoveSharedFolder(VixHandle vmHandle,
                                   const char *shareName,
                                   int flags,
                                   VixEventProc *callbackProc,
                                   void *clientData);


/*
 * Screen Capture
 */

#ifndef VIX_HIDE_FROM_JAVA
enum {
   VIX_CAPTURESCREENFORMAT_PNG            = 0x01,
   VIX_CAPTURESCREENFORMAT_PNG_NOCOMPRESS = 0x02,
};

VixHandle VixVM_CaptureScreenImage(VixHandle vmHandle, 
                                   int captureType,
                                   VixHandle additionalProperties,
                                   VixEventProc *callbackProc,
                                   void *clientdata);
#endif   // VIX_HIDE_FROM_JAVA



/*
 * VM Cloning --
 */

typedef int VixCloneType;
enum {
   VIX_CLONETYPE_FULL       = 0,
   VIX_CLONETYPE_LINKED     = 1,
};

VixHandle VixVM_Clone(VixHandle vmHandle,
                      VixHandle snapshotHandle,
                      VixCloneType cloneType,
                      const char *destConfigPathName,
                      int options,
                      VixHandle propertyListHandle,
                      VixEventProc *callbackProc,
                      void *clientData);




/*
 * Misc Functions
 */

VixHandle VixVM_UpgradeVirtualHardware(VixHandle vmHandle,
                                       int options,
                                       VixEventProc *callbackProc,
                                       void *clientData);

enum {
   VIX_INSTALLTOOLS_MOUNT_TOOLS_INSTALLER = 0x00,
   VIX_INSTALLTOOLS_AUTO_UPGRADE          = 0x01,
   VIX_INSTALLTOOLS_RETURN_IMMEDIATELY    = 0x02
};

VixHandle VixVM_InstallTools(VixHandle vmHandle,
                             int options,
                             const char *commandLineArgs,
                             VixEventProc *callbackProc,
                             void *clientData);


/*
 *-----------------------------------------------------------------------------
 *
 * VIX Job --
 *
 *-----------------------------------------------------------------------------
 */

/* 
 * Synchronization functions 
 * (used to detect when an asynch operation completes). 
 */

VixError VixJob_Wait(VixHandle jobHandle, 
                     VixPropertyID firstPropertyID, 
                     ...);

VixError VixJob_CheckCompletion(VixHandle jobHandle, 
                                Bool *complete);


/* 
 * Accessor functions 
 * (used to get results of a completed asynch operation). 
 */

VixError VixJob_GetError(VixHandle jobHandle);

int VixJob_GetNumProperties(VixHandle jobHandle,
                            int resultPropertyID);

VixError VixJob_GetNthProperties(VixHandle jobHandle,
                                 int index,
                                 int propertyID,
                                 ...);



/*
 *-----------------------------------------------------------------------------
 *
 * VIX Snapshot --
 *
 *-----------------------------------------------------------------------------
 */


VixError VixSnapshot_GetNumChildren(VixHandle parentSnapshotHandle, 
                                    int *numChildSnapshots);

VixError VixSnapshot_GetChild(VixHandle parentSnapshotHandle,
                              int index,
                              VixHandle *childSnapshotHandle);

VixError VixSnapshot_GetParent(VixHandle snapshotHandle,
                               VixHandle *parentSnapshotHandle);






#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* _VIX_H_ */
 07070100000125000081A4000000000000000000000001682255050001290D000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vixCommands.h  /*********************************************************
 * Copyright (c) 2003-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vixCommands.h --
 *
 * Defines used when Vix crosses various IPC boundaries.
 */

#ifndef _VIX_COMMANDS_H_
#define _VIX_COMMANDS_H_

#include "vixOpenSource.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * These describe the format of the message objects.
 * This will change when the client/vmx support different
 * structures for the message header. Hopefully, that won't
 * happen.
 */
#define VIX_COMMAND_MAGIC_WORD        0xd00d0001
#define VIX_COMMAND_MESSAGE_VERSION   5

/*
 * These give upper bounds for how big any VIX IPC meesage
 * should be. There are for confidence checks and to ignore maliciously
 * large messages that may be part of an DoS attack. The may need to
 * be revised if large messages are added to the protocol.
 */

#define VIX_COMMAND_MAX_SIZE           (16 * 1024 * 1024)
#define VIX_COMMAND_MAX_REQUEST_SIZE   65536

/*
 * We don't want to allow guest ops commands with input size too large.
 * Limit it to the max request size with enough room for the credentials.
 * Check bugs 824773, 926819 for more details.
 */
#define VIX_COMMAND_MAX_USER_INPUT_SIZE (VIX_COMMAND_MAX_REQUEST_SIZE - 5000)

/*
 * The types of credential we can pass with any request.
 */
#define VIX_USER_CREDENTIAL_NONE                             0
#define VIX_USER_CREDENTIAL_NAME_PASSWORD                    1
#define VIX_USER_CREDENTIAL_ANONYMOUS                        2
#define VIX_USER_CREDENTIAL_ROOT                             3
#define VIX_USER_CREDENTIAL_NAME_PASSWORD_OBFUSCATED         4
#define VIX_USER_CREDENTIAL_CONSOLE_USER                     5
#define VIX_USER_CREDENTIAL_HOST_CONFIG_SECRET               6
#define VIX_USER_CREDENTIAL_HOST_CONFIG_HASHED_SECRET        7
#define VIX_USER_CREDENTIAL_NAMED_INTERACTIVE_USER           8
#define VIX_USER_CREDENTIAL_TICKETED_SESSION                 9
#define VIX_USER_CREDENTIAL_SSPI                             10
#define VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN                11
#define VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN_HOST_VERIFIED  12

#define VIX_SHARED_SECRET_CONFIG_USER_NAME          "__VMware_Vix_Shared_Secret_1__"

/*
 * Base64 encoded value of string %HOST_VERIFIED_SIGNATURE%
 */
#define VIX_BASE64_ENCODED_HOST_VERIFIED_SIGNATURE_VALUE    "JUhPU1RfVkVSSUZJRURfU0lHTkFUVVJFJQA="


/*
 * This is the port for the server side remote Vix component
 */
#define VIX_SERVER_PORT          61525
#define VIX_TOOLS_SOCKET_PORT    61526

/*
 * These are the flags set in the commonFlags field.
 */
enum VixCommonCommandOptionValues {
   VIX_COMMAND_REQUEST                       = 0x01,
   VIX_COMMAND_REPORT_EVENT                  = 0x02,
   VIX_COMMAND_FORWARD_TO_GUEST              = 0x04,
   VIX_COMMAND_GUEST_RETURNS_STRING          = 0x08,
   VIX_COMMAND_GUEST_RETURNS_INTEGER_STRING  = 0x10,
   /* DEPRECATED VIX_COMMAND_GUEST_RETURNS_ENCODED_STRING  = 0x20, */
   VIX_COMMAND_GUEST_RETURNS_PROPERTY_LIST   = 0x40,
   VIX_COMMAND_GUEST_RETURNS_BINARY          = 0x80,
   // We cannot add more constants here. This is stored in a uint8,
   // so it is full. Use requestFlags or responseFlags.
};


/*
 * These are the flags set in the request Flags field.
 */
enum {
   VIX_REQUESTMSG_ONLY_RELOAD_NETWORKS                = 0x001,
   VIX_REQUESTMSG_RETURN_ON_INITIATING_TOOLS_UPGRADE  = 0x002,
   VIX_REQUESTMSG_RUN_IN_ANY_VMX_STATE                = 0x004,
   VIX_REQUESTMSG_REQUIRES_INTERACTIVE_ENVIRONMENT    = 0x008,
   VIX_REQUESTMSG_INCLUDES_AUTH_DATA_V1               = 0x010,
   VIX_REQUESTMSG_REQUIRES_VMDB_NOTIFICATION          = 0x020,
   VIX_REQUESTMSG_ESCAPE_XML_DATA                     = 0x040,
   VIX_REQUESTMSG_HAS_HASHED_SHARED_SECRET            = 0x080,
   VIX_REQUESTMSG_VIGOR_COMMAND                       = 0x100,
};


/*
 * These are the flags set in responseFlags.
 */
enum VixResponseFlagsValues {
   VIX_RESPONSE_SOFT_POWER_OP             = 0x0001,
   VIX_RESPONSE_EXTENDED_RESULT_V1        = 0x0002,
   VIX_RESPONSE_TRUNCATED                 = 0x0004,
   VIX_RESPONSE_FSR                       = 0x0008,
   VIX_RESPONSE_VMDB_NOTIFICATION_POSTED  = 0x0010,
   VIX_RESPONSE_VIGOR_COMMAND             = 0x0020,
};


/*
 * This is the header for one message, either a request or a
 * response, and sent either to or from the VMX.
 *
 * Every message has 3 regions:
 *
 *  -------------------------------------
 *  |   Header  |  Body  |  Credential  |
 *  -------------------------------------
 *
 * The credential and the body may either or both be empty.
 * The 3 regions always appear in this order. First the header, then a body 
 * if there is one, then a credential if there is one.
 * There should be no gaps between these regions. New regions are added
 * to the end. This means the lengths can also be used to compute
 * offsets to the regions.
 *
 * The length of the headers, the credential, and the body are all stored in
 * the common header. This should allow parsing code to receive complete
 * messages even if it does not understand them.
 *
 * Currently that the credential is only used for a Request. It is 
 * currently empty for a response.
 *
 */
#pragma pack(push, 1)
typedef struct VixMsgHeader {
   uint32   magic;
   uint16   messageVersion;

   uint32   totalMessageLength;
   uint32   headerLength;
   uint32   bodyLength;
   uint32   credentialLength;

   uint8    commonFlags;
} VixMsgHeader;
#pragma pack(pop)


/*
 * These are the headers for a single request, response, or event.
 * In theory, either the VMX or the client may issue a request
 * to the other.  In practice, legacy foundry clients can only
 * accept response messages from the VMX, not requests.  Because of
 * this, an event message is a special kind of response message.
 */
#pragma pack(push, 1)
typedef struct VixCommandRequestHeader {
   VixMsgHeader      commonHeader;

   uint32            opCode;
   uint32            requestFlags;

   uint32            timeOut;

   uint64            cookie;
   uint32            clientHandleId; // for remote case

   uint32            userCredentialType;
} VixCommandRequestHeader;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixCommandResponseHeader {
   VixMsgHeader   commonHeader;

   uint64         requestCookie;

   uint32         responseFlags;

   uint32         duration;

   uint32         error;
   uint32         additionalError;
   uint32         errorDataLength;
} VixCommandResponseHeader;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgEventHeader {
   VixCommandResponseHeader   responseHeader;

   int32                      eventType;
} VixMsgEventHeader;
#pragma pack(pop)


/*
 * A trivial request that is just a generic
 * response header (it has no body).
 */
#pragma pack(push, 1)
typedef struct VixMsgTrivialRequest {
   VixCommandRequestHeader   header;
} VixMsgTrivialRequest;
#pragma pack(pop)


/*
 * A trivial event that is just a generic
 * event header (it has no body).
 */

#pragma pack(push, 1)
typedef struct VixMsgTrivialEvent {
   VixMsgEventHeader          eventHeader;
} VixMsgTrivialEvent;
#pragma pack(pop)


/*
 * **********************************************************
 * This is a generic progress update from the VMX.
 * The VMX may send several of these before sending a final response 
 * message. These only report progress, they do not mean the job
 * has completed. These messages are identified by the
 * VIX_COMMAND_REPORT_EVENT flag in the commonFlags field and
 * VIX_EVENTTYPE_JOB_PROGRESS as the eventType.
 */

#pragma pack(push, 1)
typedef struct VixMsgProgressEvent {
   VixMsgEventHeader          eventHeader;

   int64                      workToDo;
   int64                      workDone;
} VixMsgProgressEvent;
#pragma pack(pop)


/*
 * This is an event sent from the VMX to all clients when some property changes.
 * It may be used for any runtime property.
 */
#pragma pack(push, 1)
typedef struct VixMsgPropertyChangedEvent {
   VixMsgEventHeader        eventHeader;
   int                      options;
   uint32                   propertyListSize;
} VixMsgPropertyChangedEvent;
#pragma pack(pop)



/*
 * **********************************************************
 * This is a userName and password pair.
 */
#pragma pack(push, 1)
typedef struct VixCommandNamePassword {
   uint32    nameLength;
   uint32    passwordLength;
} VixCommandNamePassword;
#pragma pack(pop)

/*
 * **********************************************************
 * This is a ticketed session for authentication.
 */
#pragma pack(push, 1)
typedef struct VixCommandTicketedSession {
   uint32    ticketLength;
} VixCommandTicketedSession;
#pragma pack(pop)

/*
 * **********************************************************
 * This is a SSPI token for acquiring credentials
 */
#pragma pack(push, 1)
typedef struct VixCommandSSPI {
   uint32    tokenLength;
} VixCommandSSPI;
#pragma pack(pop)


/*
 * **********************************************************
 * This is a SAML bearer token with optional userName to specify
 * an IdProvider store.
 */
#pragma pack(push, 1)
typedef struct VixCommandSAMLToken {
   uint32    tokenLength;
   uint32    nameLength;
} VixCommandSAMLToken;
#pragma pack(pop)

/*
 * **********************************************************
 * Basic power op request. The response is just a generic
 * response header (it has no body).
 */
#pragma pack(push, 1)
typedef struct VixMsgPowerOpRequest {
   VixCommandRequestHeader   header;
   VixVMPowerOpOptions       powerOpOptions;
   /*
    * Starting in Workstation 7.0, a serialized property list buffer
    * can be appended here. This was originally used for augmenting
    * poweroff to support revert to snapshot upon poweroff functionality.
    */
} VixMsgPowerOpRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Get/Set Properties Request
 */

#pragma pack(push, 1)
typedef struct VixMsgGetVMStateResponse {
   VixCommandResponseHeader   header;
   uint32                     bufferSize;
   // This is followed by the buffer of serialized properties
} VixMsgGetVMStateResponse;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgSetVMStateRequest {
   VixCommandRequestHeader header;
   uint32                  bufferSize;
   // This is followed by the buffer of serialized properties
} VixMsgSetVMStateRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgAuthDataV1 {
   int64  nonce;
   uint32 sequenceNumber;
   uint8  hashValue[32];
} VixMsgAuthDataV1;
#pragma pack(pop)



/*
 * **********************************************************
 * Basic reload state request. The response is just a generic
 * response header (it has no body).
 */
#pragma pack(push, 1)
typedef struct VixMsgReloadVMStateRequest {
   VixCommandRequestHeader   header;
   // This is followed by an array of VixMsgConfigurationObjectType objects
} VixMsgReloadVMStateRequest;
#pragma pack(pop)


/*
 * This is a prefix to a configuration object. The current supported
 * types are defined below in the VixCommonConfigObjectType enum.
 * Following each object type struct is the specific object. Currently,
 * we support:
 * 
 *    VIX_NETWORK_SETTING_CONFIG   - VixMsgNICBandwidth
 */
#pragma pack(push, 1)
typedef struct VixMsgConfigurationObjectType {
   int32    configurationType;
   uint32   objectSize;
} VixMsgConfigurationObjectType;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgNICBandwidth {
   Bool        validNICNum;
   int32       nicNum;
   char        pvnGUID[64];

   uint32      totalBandwidth;
   uint32      maxSendBandwidth;
   uint32      maxReceiveBandwidth;

   uint32      packetLossPattern;
   uint32      packetLossRate;
   uint32      packetLossMinBurstDuration;
   uint32      packetLossMaxBurstDuration;

   uint32      minLatency;
   uint32      maxLatency;

   uint32      options;
} VixMsgNICBandwidth;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgLANSegmentConfiguration {
   VixMsgConfigurationObjectType   configHeader;
   VixMsgNICBandwidth              lanSegment;
} VixMsgLANSegmentConfiguration;
#pragma pack(pop)

/*
 * These are options to the bandwidth commands.
 */
enum VixMsgPacketLossType {
   // packetLossPattern values
   VIX_PACKETLOSS_RANDOM   = 1,
};

/*
 * These are the types of configuration objects we can send
 * to a VIX_COMMAND_RELOAD_VM command.
 */
enum VixMsgConfigObjectType {
   VIX_LAN_SEGMENT_SETTING_CONFIG   = 1,
};

/*
 * **********************************************************
 * Wait for tools request. The response is just a generic
 * response header (it has no body).
 */
#pragma pack(push, 1)
typedef struct VixMsgWaitForToolsRequest {
   VixCommandRequestHeader header;
   int32                   timeoutInSeconds;
   int32                   minVersion;
} VixMsgWaitForToolsRequest;
#pragma pack(pop)



/*
 * **********************************************************
 * Run a program on the guest.
 */
#pragma pack(push, 1)
typedef struct VixMsgRunProgramRequest {
   VixCommandRequestHeader header;

   int32                   runProgramOptions;
   uint32                  programNameLength;
   uint32                  commandLineArgsLength;
} VixMsgRunProgramRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgOldRunProgramResponse {
   VixCommandResponseHeader   header;

   int32                      exitCode;
   VmTimeType                 deltaTime;
} VixMsgOldRunProgramResponse;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgRunProgramResponse {
   VixCommandResponseHeader   header;

   int32                      exitCode;
   VmTimeType                 deltaTime;

   int64                      pid;
   uint32                     stdOutLength;
   uint32                     stdErrLength;
} VixMsgRunProgramResponse;
#pragma pack(pop)


/*
 * **********************************************************
 * Install VMware tools.
 */
#pragma pack(push, 1)
typedef struct VixMsgInstallToolsRequest {
   VixCommandRequestHeader header;

   int32                   installOptions;
   uint32                  commandLineArgsLength;
} VixMsgInstallToolsRequest;
#pragma pack(pop)



/*
 * **********************************************************
 * Send keystrokes to the guest.
 */

enum VixKeyStrokeCharType {
   VIX_KEYSTROKE_SCANCODE     = 1,
   VIX_KEYSTROKE_TEXT_CHAR    = 2,
};

enum VixKeyStrokeModifiers {
   VIX_KEYSTROKE_MODIFIER_KEY_DOWN          = 0x01,
   VIX_KEYSTROKE_MODIFIER_KEY_UP            = 0x02,
   VIX_KEYSTROKE_MODIFIER_CONTROL           = 0x04,
   VIX_KEYSTROKE_MODIFIER_SHIFT             = 0x08,
   VIX_KEYSTROKE_MODIFIER_ALT               = 0x10,
   VIX_KEYSTROKE_MODIFIER_KEY_DOWN_ONLY     = 0x80,
   VIX_KEYSTROKE_MODIFIER_KEY_UP_ONLY       = 0x100,
};


#pragma pack(push, 1)
typedef struct VixMsgKeyStroke {
   int32                   modifier;
   int32                   scanCode;
   int32                   duration;
   int32                   delayAfterKeyUp;
   int32                   repeat;
} VixMsgKeyStroke;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgSendKeyStrokesRequest {
   VixCommandRequestHeader header;

   int32                   keyStrokeType;
   int32                   options;
   int64                   targetPid;
   int32                   numKeyStrokes;
   uint32                  windowNameLength;
} VixMsgSendKeyStrokesRequest;
#pragma pack(pop)

/*
 * send a mouse event to the guest
 */

#pragma pack(push, 1)
typedef struct VixMsgSendMouseEventRequest {
   VixCommandRequestHeader header;

   int16                    x;
   int16                    y;
   int16                    buttons;
   int32                    options;
} VixMsgSendMouseEventRequest;
#pragma pack(pop)




/*
 * **********************************************************
 * Read or write the registry on the guest.
 */
#pragma pack(push, 1)
typedef struct VixMsgRegistryRequest {
   VixCommandRequestHeader header;

   uint32                  registryKeyLength;
   int32                   expectedRegistryKeyType;
   uint32                  dataToWriteSize;
} VixMsgRegistryRequest;
#pragma pack(pop)



/*
 * **********************************************************
 * Copy files between the host and the guest.
 */
#pragma pack(push, 1)
typedef struct VixCommandRenameFileRequest {
   VixCommandRequestHeader header;

   int32                   copyFileOptions;
   uint32                  oldPathNameLength;
   uint32                  newPathNameLength;
   uint32                  filePropertiesLength;
} VixCommandRenameFileRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixCommandRenameFileRequestEx {
   VixCommandRequestHeader header;

   int32                   copyFileOptions;
   uint32                  oldPathNameLength;
   uint32                  newPathNameLength;
   uint32                  filePropertiesLength;
   Bool                    overwrite;
} VixCommandRenameFileRequestEx;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixCommandHgfsSendPacket {
   VixCommandRequestHeader header;

   uint32                  hgfsPacketSize;
   int32                   timeout;
} VixCommandHgfsSendPacket;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgSetGuestFileAttributesRequest {
   VixCommandRequestHeader header;

   int32                  fileOptions;
   int64                  accessTime;
   int64                  modificationTime;
   int32                  ownerId;
   int32                  groupId;
   int32                  permissions;
   Bool                   hidden;
   Bool                   readOnly;
   uint32                 guestPathNameLength;
} VixMsgSetGuestFileAttributesRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Perform a simple operation (like delete or check for existence)
 * on a file or registry key on the guest.
 */
#pragma pack(push, 1)
typedef struct VixMsgSimpleFileRequest {
   VixCommandRequestHeader header;

   int32                   fileOptions;
   uint32                  guestPathNameLength;
} VixMsgSimpleFileRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgListDirectoryRequest {
   VixCommandRequestHeader header;

   int32                   fileOptions;
   uint32                  guestPathNameLength;
   int64                   offset;
} VixMsgListDirectoryRequest;
#pragma pack(pop)

enum VixListDirectoryOptions {
   VIX_LIST_DIRECTORY_USE_OFFSET = 0x01
};

#pragma pack(push, 1)
typedef struct VixMsgListFilesRequest {
   VixCommandRequestHeader header;

   int32                   fileOptions;
   uint32                  guestPathNameLength;
   uint32                  patternLength;
   int32                   index;
   int32                   maxResults;
   uint64                  offset;
} VixMsgListFilesRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixCommandInitiateFileTransferToGuestRequest {
   VixCommandRequestHeader header;

   int32                   options;
   uint32                  guestPathNameLength;
   Bool                    overwrite;
} VixCommandInitiateFileTransferToGuestRequest;
#pragma pack(pop)


/*
 * This is used to reply to several operations, like testing whether
 * a file or registry key exists on the client.
 */
#pragma pack(push, 1)
typedef struct VixMsgCheckExistsResponse {
   VixCommandResponseHeader   header;
   Bool                       exists;
} VixMsgCheckExistsResponse;
#pragma pack(pop)


/*
 * **********************************************************
 * Perform a create file operation (like createDir or moveFile)
 * on a file in the guest. This lets you pass in things like the initial file
 * properties.
 */
#pragma pack(push, 1)
typedef struct VixMsgCreateFileRequest {
   VixCommandRequestHeader header;

   int32                   fileOptions;
   uint32                  guestPathNameLength;
   uint32                  filePropertiesLength;
} VixMsgCreateFileRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgCreateFileRequestEx {
   VixCommandRequestHeader header;

   int32                   fileOptions;
   uint32                  guestPathNameLength;
   uint32                  filePropertiesLength;
   Bool                    createParentDirectories;
} VixMsgCreateFileRequestEx;
#pragma pack(pop)


/*
 * **********************************************************
 * Hot extend a disk in a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgHotExtendDiskRequest {
   VixCommandRequestHeader header;
   int32                    hotDiskOptions;
   uint32                   typeLength;
   int32                    adapterNum;
   int32                    targetNum;
   uint64                   newNumSectors;
} VixMsgHotExtendDiskRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Hot plug CPU in a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgHotPlugCPURequest {
   VixCommandRequestHeader header;
   uint32                  newNumCPU;
} VixMsgHotPlugCPURequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Hot plug memory in a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgHotPlugMemoryRequest {
   VixCommandRequestHeader header;
   uint32                  newSizeMb;
} VixMsgHotPlugMemoryRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Hot add device in a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgHotAddDeviceRequest {
   VixCommandRequestHeader header;
   int32                   deviceType;
   uint32                  devicePropsBufferSize;
   int32                   backingType;
   uint32                  backingPropsBufferSize;
} VixMsgHotAddDeviceRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgHotAddDeviceResponse {
   VixCommandResponseHeader   header;
   int32                      adapterNum;
   int32                      targetNum;
} VixMsgHotAddDeviceResponse;
#pragma pack(pop)


/*
 * **********************************************************
 * Hot remove device in a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgHotRemoveDeviceRequest {
   VixCommandRequestHeader header;
   int32                   deviceType;
   uint32                  devicePropsBufferSize;
} VixMsgHotRemoveDeviceRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Change monitor type of a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgHotChangeMonitorTypeRequest {
   VixCommandRequestHeader header;
   int32                   monitorType;
} VixMsgHotChangeMonitorTypeRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Hot plug begin batch.
 */
#pragma pack(push, 1)
typedef struct VixMsgHotPlugBeginBatchRequest {
   VixCommandRequestHeader header;
   int32                   flags;
} VixMsgHotPlugBeginBatchRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Hot plug commit batch.
 */
#pragma pack(push, 1)
typedef struct VixMsgHotPlugCommitBatchRequest {
   VixCommandRequestHeader header;
   int32                   status;
} VixMsgHotPlugCommitBatchRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Transfer connection.  Besides fields here you are supposed to
 * receive file descriptor OOB.
 */
#pragma pack(push, 1)
typedef struct VixMsgTransferConnectionRequest {
   VixCommandRequestHeader header;
   Bool                    isPrivileged;
   uint32                  cryptoLength;
   uint32                  fdLength;
/* uint8                   cryptoData[]; */
/* uint8                   fdData[]; */
} VixMsgTransferConnectionRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Pass data.  Besides fields here you may receive also
 * file descriptor.  Data is just command which was pending
 * on original connection already transferred via
 * TransferConnectionRequest.
 */
#pragma pack(push, 1)
typedef struct VixMsgTransferRequestRequest {
   VixCommandRequestHeader header;
   uint32                  dataLength;
   uint32                  fdLength;
/* uint8                   data[]; */
/* uint8                   fdData[]; */
} VixMsgTransferRequestRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Pass final data.  Besides fields here you may receive also
 * file descriptor.  Data is just what was already received
 * on the socket passed by TransferConnectionRequest.
 */
#pragma pack(push, 1)
typedef struct VixMsgTransferFinalDataRequest {
   VixCommandRequestHeader header;
   uint32                  dataLength;
   uint32                  fdLength;
/* uint8                   data[]; */
/* uint8                   fdData[]; */
} VixMsgTransferFinalDataRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Create a snapshot of a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgCreateSnapshotRequest {
   VixCommandRequestHeader    header;

   int32                      options;

   Bool                       powerOff;
   Bool                       saveDeviceState;

   uint32                     nameLength;
   uint32                     descriptionLength;
} VixMsgCreateSnapshotRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgCreateSnapshotResponse {
   VixCommandResponseHeader   header;
   int32                      snapshotUID;
} VixMsgCreateSnapshotResponse;
#pragma pack(pop)


/*
 * Several snapshot operations for a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgSnapshotRequest {
   VixCommandRequestHeader    header;

   int32                      options;
   int32                      snapshotId;
} VixMsgSnapshotRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgSnapshotUpdateEvent {
   VixMsgEventHeader          eventHeader;

   int32                      options;
   uint32                     propertyListLength;
   /*
    * This is followed by a serialized property list.
    */
} VixMsgSnapshotUpdateEvent;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgSnapshotMRURequest {
   VixCommandRequestHeader    header;

   int32                      snapshotId;
   int32                      maxMRU;
} VixMsgSnapshotMRURequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgSetSnapshotInfoRequest {
   VixCommandRequestHeader    header;

   int32                      snapshotId;
   int32                      clientFlags;
   int32                      numTierUIDs;

   uint32                     displayNameLength;
   uint32                     descriptionLength;
   uint32                     propertyListLength;
   uint32                     tierUIDListLength;

   /*
    * Followed by:
    *   displayName string
    *   description string
    *   serialized property list.
    */
} VixMsgSetSnapshotInfoRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgSetSnapshotInfoResponse {
   VixCommandResponseHeader    header;

   uint32                     propertyListLength;
} VixMsgSetSnapshotInfoResponse;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgRemoveBulkSnapshotRequest {
   VixCommandRequestHeader    header;

   int32                      options;
   int32                      numSnapshots;
   /*
    * This is followed by numSnapshots snapshotIDs.
    */
} VixMsgRemoveBulkSnapshotRequest;
#pragma pack(pop)

/*
 * Stop recording or playback of a snapshot event log.
 */
#pragma pack(push, 1)
typedef struct VixMsgVMSnapshotPauseRequest {
   VixCommandRequestHeader    header;

   int32                      options;
} VixMsgVMSnapshotPauseRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgDebuggerEvent {
   VixMsgEventHeader          eventHeader;

   int32                      blobLength;
   /*
    * This is followed by the blob buffer.
    */
} VixMsgDebuggerEvent;
#pragma pack(pop)


/*
 * **********************************************************
 * Shared folder operations.
 */
#pragma pack(push, 1)
typedef struct VixMsgSharedFolderRequest {
   VixCommandRequestHeader   header;

   int32                     options;
   int32                     index;
   uint32                    shareNameLength;
   uint32                    hostPathNameLength;
} VixMsgSharedFolderRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgSharedFolderResponse {
   VixCommandResponseHeader      header;
   int32                         numSharedFolders;
} VixMsgSharedFolderResponse;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgGetSharedFolderInfoResponse {
   VixCommandResponseHeader   header;

   uint32                     shareNameLength;
   uint32                     hostPathNameLength;
   int32                      sharedFolderFlags;
} VixMsgGetSharedFolderInfoResponse;
#pragma pack(pop)


/*
 * Add or change a shared folder request.
 */
#pragma pack(push, 1)
typedef struct VixMsgSetSharedFolderRequest {
   VixCommandRequestHeader   header;

   int32                     options;
   uint32                    shareNameLength;
   uint32                    hostPathNameLength;
} VixMsgSetSharedFolderRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Capture the screen of a VM
 */

#pragma pack(push, 1)
typedef struct VixMsgCaptureScreenRequest {
   VixCommandRequestHeader header;
   
   int32                   format;  // Identifies the requested data format.
   int32                    maxSize; // Max data response size in bytes
                                    //    (-1 is any size)

   int32                    captureScreenOptions;
} VixMsgCaptureScreenRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgCaptureScreenResponse {
   VixCommandResponseHeader header;
   
   int32                   format; // Format of the data in the response.
   uint32                  dataOffset; // Relative to the address of this struct.
} VixMsgCaptureScreenResponse;
#pragma pack(pop)

/*
 * **********************************************************
 * Run a script in the guest.
 */
#pragma pack(push, 1)
typedef struct VixMsgRunScriptRequest {
   VixCommandRequestHeader header;

   int32                   scriptOptions;

   uint32                  interpreterNameLength;
   uint32                  scriptLength;
   uint32                  propertiesLength;
} VixMsgRunScriptRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * An unsupported command. This is used to test future versions
 * of the API sending us commands we don't recognize.
 */
#pragma pack(push, 1)
typedef struct VixUnsupportedCommandRequest {
   VixCommandRequestHeader   header;
   char                      junk[2053];
} VixUnsupportedCommandRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Create a session key between the client and the VMX.
 */
#pragma pack(push, 1)
typedef struct VixCommandMakeSessionKeyRequest {
   VixCommandRequestHeader   header;

   int32                     keyOptions;
   int32                     timeout;
   uint32                    responseKeyLength;
   int32                     responseKeyCypherType;
   int32                     cypherType;
} VixCommandMakeSessionKeyRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixCommandMakeSessionKeyResponse {
   VixCommandResponseHeader     header;

   int32                        keyOptions;
   int32                        timeout;
   uint32                       keyLength;
   int32                        cypherType;
} VixCommandMakeSessionKeyResponse;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixCommandGenerateNonceResponse {
   VixCommandResponseHeader     header;

   int64                        nonce;
} VixCommandGenerateNonceResponse;
#pragma pack(pop)


enum {
   VIX_CYPHERTYPE_NONE        = 0,
   VIX_CYPHERTYPE_DEFAULT     = 1,
};


/*
 * **********************************************************
 * Force a guest process to quit.
 */

#pragma pack(push, 1)
typedef struct VixCommandKillProcessRequest {
   VixCommandRequestHeader    header;

   uint64                     pid;
   uint32                     options;
} VixCommandKillProcessRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Read and write variables like guest variables and config values.
 */
#pragma pack(push, 1)
typedef struct VixMsgReadVariableRequest {
   VixCommandRequestHeader header;

   int32                   variableType;
   int32                   options;
   uint32                  nameLength;
} VixMsgReadVariableRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgReadVariableResponse {
   VixCommandResponseHeader   header;

   int32                      valueType;
   int32                      valueProperties;
   uint32                     valueLength;
} VixMsgReadVariableResponse;
#pragma pack(pop)


/*
 * Several snapshot operations for a running VM.
 */
#pragma pack(push, 1)
typedef struct VixMsgWriteVariableRequest {
   VixCommandRequestHeader header;

   int32                   variableType;
   int32                   options;

   uint32                  nameLength;
   uint32                  valueLength;
} VixMsgWriteVariableRequest;
#pragma pack(pop)



/*
 * **********************************************************
 * Perform a create file operation (like createDir or moveFile)
 * on a file in the guest. This lets you pass in things like the initial file
 * properties.
 */
#pragma pack(push, 1)
typedef struct VixMsgCreateTempFileRequest {
   VixCommandRequestHeader header;

   int32                   options;
   uint32                  propertyNameLength;
   uint32                  filePrefixLength;
   uint32                  fileSuffixLength;
} VixMsgCreateTempFileRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgCreateTempFileRequestEx {
   VixCommandRequestHeader header;

   int32                   options;
   uint32                  filePrefixLength;
   uint32                  fileSuffixLength;
   uint32                  directoryPathLength;
   uint32                  propertyListLength;
} VixMsgCreateTempFileRequestEx;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct {
   VixCommandRequestHeader header;

   int32                   fileOptions;
   uint32                  guestPathNameLength;
   uint32                  filePropertiesLength;
   Bool                    recursive;
} VixMsgDeleteDirectoryRequest;
#pragma pack(pop)

/*
 * **********************************************************
 * Connect/Disconnect device request. The response is just a generic
 * response header (it has no body).
 */
#pragma pack(push, 1)
typedef struct VixMsgConnectDeviceRequest {
   VixCommandRequestHeader    header;
   int32                      options;
   Bool                       connected;
   uint32                     nameLength;
} VixMsgConnectDeviceRequest;
#pragma pack(pop)

/*
 * **********************************************************
 * Get the state of a virtual device.
 */
#pragma pack(push, 1)
typedef struct VixMsgGetDeviceStateRequest {
   VixCommandRequestHeader header;

   int32                   options;
   uint32                  nameLength;
} VixMsgGetDeviceStateRequest;
#pragma pack(pop)


/*
 * This is used to reply to IsDeviceConnected operations.
 */
#pragma pack(push, 1)
typedef struct VixMsgGetDeviceStateResponse {
   VixCommandResponseHeader   header;
   Bool                       connected;
   int32                      stateFlags;
   // Maybe capacity and percent allocated?
} VixMsgGetDeviceStateResponse;
#pragma pack(pop)


/*
 * **********************************************************
 * Enable/disable all shared folders on this VM. The response
 * is just a generic response header (it has no body).
 */
#pragma pack(push, 1)
typedef struct VixMsgEnableSharedFoldersRequest {
   VixCommandRequestHeader   header;
   Bool                      enabled;
   int32                     sharedFolderOptions;
} VixMsgEnableSharedFoldersRequest;
#pragma pack(pop)


/*
 * **********************************************************
 * Mount volumes in the guest.
 */

enum VixMountOptions {
   VIX_MOUNT_ALL              = 0x0001,
   VIX_MOUNT_REMOUNT_FIRST    = 0x0002,
};


#pragma pack(push, 1)
typedef struct VixMsgMountHGFSRequest {
   VixCommandRequestHeader header;

   int32                   mountOptions;
   int32                   mountType;

   /* The str path list has the form "host1\0dest1\0host2\0dest2\0host3\0dest3\0\0" */
   uint32                  pathListLength;
} VixMsgMountHGFSRequest;
#pragma pack(pop)


/*
 * Get guest networking config
 */
#pragma pack(push, 1)
typedef struct VixMsgGetGuestNetworkingConfigRequest {
   VixCommandRequestHeader   header;

   int32                     options;
} VixMsgGetGuestNetworkingConfigRequest;
#pragma pack(pop)


/*
 * Set guest networking config
 */
#pragma pack(push, 1)
typedef struct VixMsgSetGuestNetworkingConfigRequest {
   VixCommandRequestHeader   header;

   int32                     options;
   uint32                    bufferSize;
} VixMsgSetGuestNetworkingConfigRequest;
#pragma pack(pop)


/*
 * Query VMX performance data
 */
#pragma pack(push, 1)
typedef struct VixMsgGetPerformanceDataRequest {
   VixCommandRequestHeader   header;

   // unused for now, but left for future expansion in case we
   // get such a large list that we want to pass the desired properties.
   int32                     options;
   uint32                    sizeOfPropertyList;
   // This is followed by the buffer of properties we wish to fetch
} VixMsgGetPerformanceDataRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgGetPerformanceDataResponse {
   VixCommandResponseHeader   header;
   uint32                     bufferSize;
   // This is followed by the buffer of serialized properties
} VixMsgGetPerformanceDataResponse;
#pragma pack(pop)


/*
 * Run a program in guest with (VI version with more args)
 */
#pragma pack(push, 1)
typedef struct VixMsgStartProgramRequest {
   VixCommandRequestHeader   header;

   Bool   startMinimized;
   uint32 programPathLength;
   uint32 argumentsLength;
   uint32 workingDirLength;
   uint32 numEnvVars;
   uint32 envVarLength;

   // This is followed by the buffer of the args
} VixMsgStartProgramRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgListProcessesExRequest {
   VixCommandRequestHeader   header;

   // if we need to make multiple trips, this is the key used to identify
   // the result being processed
   uint32 key;

   // if we need to make multiple trips, this is the offset in the reply
   // from which to send the next chunk
   uint32 offset;
   uint32 numPids;

   // This is followed by the list of uint64s
} VixMsgListProcessesExRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgReadEnvironmentVariablesRequest {
   VixCommandRequestHeader   header;

   uint32 numNames;
   uint32 namesLength;

   // This is followed by the list of NUL-terminated names
} VixMsgReadEnvironmentVariablesRequest;
#pragma pack(pop)

/* IdProvider support */

#pragma pack(push, 1)
typedef struct VixMsgAddAliasRequest {
   VixCommandRequestHeader header;

   uint32                  options;

   uint32                  userNameLen;
   uint32                  pemCertLen;
   Bool                    addMapping;

   int32                   subjectType;    // one of VixGuestAuthSubjectType
   uint32                  subjectNameLen;
   uint32                  aliasCommentLen;

   /* Followed by the NUL-terminated string arguments. */
   /* char[]               userName; */
   /* char[]               pemCert; */
   /* char[]               subjectName; */
   /* char[]               aliasComment; */
} VixMsgAddAuthAliasRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgRemoveAuthAliasRequest {
   VixCommandRequestHeader header;

   uint32                  options;

   uint32                  userNameLen;
   uint32                  pemCertLen;

   // special case for RemoveAliasByCert:
   // if subjectType is NONE, then all aliases will be removed.
   int32                   subjectType;    // one of VixGuestAuthSubjectType
   uint32                  subjectNameLen;

   /* Followed by the NUL-terminated string arguments. */
   /* char[]               userName; */
   /* char[]               pemCert; */
   /* char[]               subjectName; */
} VixMsgRemoveAuthAliasRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgListAuthAliasesRequest {
   VixCommandRequestHeader header;

   uint32                  options;

   uint32                  userNameLen;

   /* char[]               userName; */
} VixMsgListAuthAliasesRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgListMappedAliasesRequest {
   VixCommandRequestHeader header;

   uint32                  options;
} VixMsgListMappedAliasesRequest;
#pragma pack(pop)

/*
 * Windows Registry Management Support.
 */
#pragma pack(push, 1)
typedef struct VixMsgCreateRegKeyRequest {
   VixCommandRequestHeader header;

   uint32 options;

   uint32 pathLength;
   uint32 wowBitness;
   Bool isVolatile;
   uint32 classTypeLength;

   /*
    * Followed by NUL-terminated string arguments.
    * char[] path;
    * char[] classType;
    */
} VixMsgCreateRegKeyRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgListRegKeysRequest {
   VixCommandRequestHeader header;

   uint32 options;

   /*
    * If we need multiple roundtrips, this is the index
    * used to identify the result being processed.
    */
   uint32 index;

   /*
    * If we need multiple roundtrips, this is the offset
    * in the reply from which to send the next chunk.
    */
   uint32 offset;

   uint32 pathLength;
   uint32 wowBitness;
   Bool recursive;
   uint32 matchPatternLength;

   /*
    * Followed by NUL-terminated string arguments.
    * char[] path;
    * char[] matchPattern;
    */
} VixMsgListRegKeysRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgDeleteRegKeyRequest {
   VixCommandRequestHeader header;

   uint32 options;

   uint32 pathLength;
   uint32 wowBitness;
   Bool recursive;

   /*
    * Followed by NUL-terminated string arguments.
    * char[] path;
    */
} VixMsgDeleteRegKeyRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgSetRegValueRequest {
   VixCommandRequestHeader header;

   uint32 options;

   uint32 pathLength;
   uint32 wowBitness;
   uint32 nameLength;
   uint32 dataBlobType;
   uint32 dataBlobLength;

   /*
    * Followed by NUL-terminated string arguments.
    * char[] path;
    * char[] name;
    *
    * Followed by a data blob of specified length
    * containing information of specified type.
    * void *dataBlob;
    */
} VixMsgSetRegValueRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgListRegValuesRequest {
   VixCommandRequestHeader header;

   uint32 options;

   /*
    * If we need multiple roundtrips, this is the index
    * used to identify the result being processed.
    */
   uint32 index;

   /*
    * If we need multiple roundtrips, this is the offset
    * in the reply from which to send the next chunk.
    */
   uint32 offset;

   uint32 pathLength;
   uint32 wowBitness;
   Bool expandStrings;
   uint32 matchPatternLength;

   /*
    * Followed by NUL-terminated string arguments.
    * char[] path;
    * char[] matchPattern;
    */
} VixMsgListRegValuesRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgDeleteRegValueRequest {
   VixCommandRequestHeader header;

   uint32 options;

   uint32 pathLength;
   uint32 wowBitness;
   uint32 nameLength;

   /*
    * Followed by NUL-terminated string arguments.
    * char[] path;
    * char[] name;
    */
} VixMsgDeleteRegValueRequest;
#pragma pack(pop)


/*
 * HOWTO: Adding a new Vix Command. Step 3.
 *
 * Add a new struct to pass over the control socket into the VMX.
 * You only need to do this if your command is manipulating a running
 * VM, but that is a common situation. If your command only manipulates
 * non-running VMs, then you can skip this.
 *
 * This particular command passes strings as both a param and a
 * result. This is the most general case, because it means that both
 * the request and response have a variable-length string on the end.
 * You can make a simpler request or response if it only passes integers
 * and so is fixed size.
 */

/*
 * **********************************************************
 * Sample Command.
 */

#pragma pack(push, 1)
typedef struct VixMsgSampleCommandRequest {
   VixCommandRequestHeader header;

   int32                   intArg;
   uint32                  strArgLength;
} VixMsgSampleCommandRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgSampleCommandResponse {
   VixCommandResponseHeader   header;

   int32                      intResult;
   uint32                     strResultLength;
} VixMsgSampleCommandResponse;
#pragma pack(pop)

// End of "HOWTO: Adding a new Vix Command. Step 3."


/*
 * **********************************************************
 *  Debugger related commands.
 */

#pragma pack(push, 1)
typedef struct VixMsgAttachDebuggerRequest {
   VixCommandRequestHeader   header;
   
   int32                     options;
   uint32                    propertyListBufferSize;
} VixMsgAttachDebuggerRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgAttachDebuggerResponse {
   VixCommandResponseHeader header;
   uint32   propertyListBufferSize;
} VixMsgAttachDebuggerResponse;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgIssueDebuggerCommandRequest {
   VixCommandRequestHeader   header;

   int32                     options;
   uint32                    propertyListBufferSize;
   uint32                    debuggerBlobBufferSize;
} VixMsgIssueDebuggerCommandRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgIssueDebuggerCommandResponse {
   VixCommandResponseHeader header;
   uint32   propertyListBufferSize;
   uint32   debuggerBlobBufferSize;
} VixMsgIssueDebuggerCommandResponse;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgDetachDebuggerRequest {
   VixCommandRequestHeader   header;
  
   int32                     options;
   uint32                    propertyListBufferSize;
} VixMsgDetachDebuggerRequest;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct VixMsgDetachDebuggerResponse {
   VixCommandResponseHeader header;
   uint32   propertyListBufferSize;
} VixMsgDetachDebuggerResponse;
#pragma pack(pop)

/*
 * **********************************************************
 * VM Pause state change event format
 */

#pragma pack(push, 1)
typedef struct VixMsgPauseStateChangedEvent {
   VixMsgEventHeader          eventHeader;
   Bool                       paused;
} VixMsgPauseStateChangedEvent;
#pragma pack(pop)


/*
 * **********************************************************
 * Wait for a user action, such as a user logging into the guest.
 */

/*
 * Vix_WaitForUserActionInGuest Request
 * VIX_COMMAND_WAIT_FOR_USER_ACTION_IN_GUEST
 */

#pragma pack(push, 1)
typedef struct VixMsgWaitForUserActionRequest {
   VixCommandRequestHeader    header;

   int32                      userType;
   int32                      userAction;

   int32                      timeoutInSeconds;
   int32                      options;

   uint32                     userNameLength;
   uint32                     propertyBufferSize;

   // This is followed by:
   //    userName
   //    buffer of serialized properties
} VixMsgWaitForUserActionRequest;
#pragma pack(pop)


#pragma pack(push, 1)
typedef struct VixMsgWaitForUserActionResponse {
   VixCommandRequestHeader    header;

   Bool                       actionHappened;

   uint32                     bufferSize;
   // This is followed by the buffer of serialized properties
} VixMsgWaitForUserActionResponse;
#pragma pack(pop)


/*
 * **********************************************************
 * List filesystems
 */

#pragma pack(push, 1)
typedef struct VixCommandListFileSystemsRequest {
   VixCommandRequestHeader    header;

   uint32                     options;
   uint32                     propertyListSize;
} VixCommandListFileSystemsRequest;
#pragma pack(pop)

/*
 * **********************************************************
 * Acquire Credentials.
 */

#pragma pack(push, 1)
typedef struct VixCommandAcquireCredentialsRequest {
   VixCommandRequestHeader    header;

   int64                      sessionID;
} VixCommandAcquireCredentialsRequest;
#pragma pack(pop)

/*
 * **********************************************************
 * A simple request packet that contains an options field and a
 * property list.
 */

#pragma pack(push, 1)
typedef struct VixCommandGenericRequest {
   VixCommandRequestHeader    header;

   uint32                     options;
   uint32                     propertyListSize;
   // This is followed by the buffer of serialized properties
} VixCommandGenericRequest;
#pragma pack(pop)

/*
 * The security classifications for async op types/op code. Each op code
 * is given a security category, and the VMX uses that category to determine
 * whether a client is allowed to perform the given command.
 */
typedef enum VixCommandSecurityCategory {
   
   /* The default for unknown commands */
   VIX_COMMAND_CATEGORY_UNKNOWN,
   
   /*
    * A command that should be executed in the guest OS by the VIX Tools.
    * component. These are allowed for all connection types.
    */
   VIX_COMMAND_CATEGORY_ALWAYS_ALLOWED,

   /*
    * A command that only allowed by privileged connections; in the VI
    * world this is means that only Hostd is allowed to perform these
    * commands.
    */
   VIX_COMMAND_CATEGORY_PRIVILEGED,

   /*
    * A command that may or may not be privileged. Usually, extra inspection
    * of the payload is required to make the determination. This should be
    * used sparingly, since must always be accompanied by "deep packet
    * inspection" code in the VMX (mainDispatch.c).
    */
   VIX_COMMAND_CATEGORY_MIXED,
} VixCommandSecurityCategory;

/*
 * This is the list of all Vix commands
 *
 * Be really careful with these. These values are passed over the socket
 * between clients and the VMX process. One client may connect to newer or
 * older versions of the VMX, so we cannot ever change or recycle values if
 * if we add or remove command ids. This is why the values are explicitly 
 * assigned, and there may be gaps in the numeric sequence as some commands 
 * are no longer supported.
 */
typedef int VixAsyncOpType;
enum {
   VIX_COMMAND_UNKNOWN                          = -1,

   VIX_COMMAND_VM_POWERON                       = 0,
   VIX_COMMAND_VM_POWEROFF                      = 1,
   VIX_COMMAND_VM_RESET                         = 2,
   VIX_COMMAND_VM_SUSPEND                       = 3,
   VIX_COMMAND_RUN_PROGRAM                      = 4,
   /* DEPRECATED VIX_COMMAND_GET_PROPERTY                     = 5, */
   /* DEPRECATED VIX_COMMAND_SET_PROPERTY                     = 6, */
   VIX_COMMAND_KEYSTROKES                       = 7,
   VIX_COMMAND_READ_REGISTRY                    = 8,
   VIX_COMMAND_WRITE_REGISTRY                   = 10,
   VIX_COMMAND_COPY_FILE_FROM_GUEST_TO_HOST     = 12,
   VIX_COMMAND_COPY_FILE_FROM_HOST_TO_GUEST     = 13,
   VIX_COMMAND_CREATE_SNAPSHOT                  = 14,
   VIX_COMMAND_REMOVE_SNAPSHOT                  = 15,
   VIX_COMMAND_REVERT_TO_SNAPSHOT               = 16,
   VIX_COMMAND_VM_CLONE                         = 17,
   VIX_COMMAND_DELETE_GUEST_FILE                = 18,
   VIX_COMMAND_GUEST_FILE_EXISTS                = 19,
   VIX_COMMAND_FIND_VM                          = 20,
   VIX_COMMAND_CALL_PROCEDURE                   = 21,
   VIX_COMMAND_REGISTRY_KEY_EXISTS              = 22,
   VIX_COMMAND_WIN32_WINDOW_MESSAGE             = 23,
   VIX_COMMAND_CONSOLIDATE_SNAPSHOTS            = 24,
   VIX_COMMAND_INSTALL_TOOLS                    = 25,
   VIX_COMMAND_CANCEL_INSTALL_TOOLS             = 26,
   VIX_COMMAND_UPGRADE_VIRTUAL_HARDWARE         = 27,
   VIX_COMMAND_SET_NIC_BANDWIDTH                = 28,
   /* DEPRECATED VIX_COMMAND_CREATE_DISK                      = 29, */
   /* DEPRECATED VIX_COMMAND_CREATE_FLOPPY                    = 30, */
   VIX_COMMAND_RELOAD_VM                        = 31,
   VIX_COMMAND_DELETE_VM                        = 32,
   /* DEPRECATED VIX_COMMAND_SYNCDRIVER_FREEZE                = 33, */
   /* DEPRECATED VIX_COMMAND_SYNCDRIVER_THAW                  = 34, */
   /* DEPRECATED VIX_COMMAND_HOT_ADD_DISK                     = 35, */
   /* DEPRECATED VIX_COMMAND_HOT_REMOVE_DISK                  = 36, */
   /* DEPRECATED VIX_COMMAND_SET_GUEST_PRINTER                = 37, */
   VIX_COMMAND_WAIT_FOR_TOOLS                   = 38,
   VIX_COMMAND_CREATE_RUNNING_VM_SNAPSHOT       = 39,
   VIX_COMMAND_CONSOLIDATE_RUNNING_VM_SNAPSHOT  = 40,
   VIX_COMMAND_GET_NUM_SHARED_FOLDERS           = 41,
   VIX_COMMAND_GET_SHARED_FOLDER_STATE          = 42,
   VIX_COMMAND_EDIT_SHARED_FOLDER_STATE         = 43,
   VIX_COMMAND_REMOVE_SHARED_FOLDER             = 44,
   VIX_COMMAND_ADD_SHARED_FOLDER                = 45,
   VIX_COMMAND_RUN_SCRIPT_IN_GUEST              = 46,
   VIX_COMMAND_OPEN_VM                          = 47,
   /* DEPRECATED VIX_COMMAND_GET_DISK_PROPERTIES              = 48, */
   /* DEPRECATED VIX_COMMAND_OPEN_URL                         = 49, */
   VIX_COMMAND_GET_HANDLE_STATE                 = 50,
   /* DEPRECATED VIX_COMMAND_SET_HANDLE_STATE                 = 51, */
   VIX_COMMAND_CREATE_WORKING_COPY              = 55, // DELETE this when we switch remote foundry to VIM
   VIX_COMMAND_DISCARD_WORKING_COPY             = 56, // DELETE this when we switch remote foundry to VIM
   VIX_COMMAND_SAVE_WORKING_COPY                = 57, // DELETE this when we switch remote foundry to VIM
   VIX_COMMAND_CAPTURE_SCREEN                   = 58,
   /* DEPRECATED VIX_COMMAND_GET_VMDB_VALUES                  = 59, */
   /* DEPRECATED VIX_COMMAND_SET_VMDB_VALUES                  = 60, */
   /* DEPRECATED VIX_COMMAND_READ_XML_FILE                    = 61, */
   VIX_COMMAND_GET_TOOLS_STATE                  = 62,
   VIX_COMMAND_CHANGE_SCREEN_RESOLUTION         = 69,
   VIX_COMMAND_DIRECTORY_EXISTS                 = 70,
   VIX_COMMAND_DELETE_GUEST_REGISTRY_KEY        = 71,
   VIX_COMMAND_DELETE_GUEST_DIRECTORY           = 72,
   VIX_COMMAND_DELETE_GUEST_EMPTY_DIRECTORY     = 73,
   VIX_COMMAND_CREATE_TEMPORARY_FILE            = 74,
   VIX_COMMAND_LIST_PROCESSES                   = 75,
   VIX_COMMAND_MOVE_GUEST_FILE                  = 76,
   VIX_COMMAND_CREATE_DIRECTORY                 = 77,
   VIX_COMMAND_CHECK_USER_ACCOUNT               = 78,
   VIX_COMMAND_LIST_DIRECTORY                   = 79,
   VIX_COMMAND_REGISTER_VM                      = 80,
   VIX_COMMAND_UNREGISTER_VM                    = 81,
   VIX_CREATE_SESSION_KEY_COMMAND               = 83,
   VMXI_HGFS_SEND_PACKET_COMMAND                = 84,
   VIX_COMMAND_KILL_PROCESS                     = 85,
   /* DEPRECATED VIX_VM_FORK_COMMAND                          = 86, */
   VIX_COMMAND_LOGOUT_IN_GUEST                  = 87,
   VIX_COMMAND_READ_VARIABLE                    = 88,
   VIX_COMMAND_WRITE_VARIABLE                   = 89,
   VIX_COMMAND_CONNECT_DEVICE                   = 92,
   VIX_COMMAND_IS_DEVICE_CONNECTED              = 93,
   VIX_COMMAND_GET_FILE_INFO                    = 94,
   VIX_COMMAND_SET_FILE_INFO                    = 95,
   VIX_COMMAND_MOUSE_EVENTS                     = 96,
   VIX_COMMAND_OPEN_TEAM                        = 97,
   /* DEPRECATED VIX_COMMAND_FIND_HOST_DEVICES                = 98, */
   VIX_COMMAND_ANSWER_MESSAGE                   = 99,
   VIX_COMMAND_ENABLE_SHARED_FOLDERS            = 100,
   VIX_COMMAND_MOUNT_HGFS_FOLDERS               = 101,
   VIX_COMMAND_HOT_EXTEND_DISK                  = 102,

   /* DEPRECATED VIX_COMMAND_GET_VPROBES_VERSION              = 104, */
   /* DEPRECATED VIX_COMMAND_GET_VPROBES                      = 105, */
   /* DEPRECATED VIX_COMMAND_VPROBE_GET_GLOBALS               = 106, */
   /* DEPRECATED VIX_COMMAND_VPROBE_LOAD                      = 107, */
   /* DEPRECATED VIX_COMMAND_VPROBE_RESET                     = 108, */

   /* DEPRECATED VIX_COMMAND_LIST_USB_DEVICES                 = 109, */
   VIX_COMMAND_CONNECT_HOST                     = 110,

   VIX_COMMAND_CREATE_LINKED_CLONE              = 112,

   /* DEPRECATED VIX_COMMAND_STOP_SNAPSHOT_LOG_RECORDING      = 113, */
   /* DEPRECATED VIX_COMMAND_STOP_SNAPSHOT_LOG_PLAYBACK       = 114, */


   VIX_COMMAND_SAMPLE_COMMAND                   = 115,

   VIX_COMMAND_GET_GUEST_NETWORKING_CONFIG      = 116,
   VIX_COMMAND_SET_GUEST_NETWORKING_CONFIG      = 117,

   /* DEPRECATED VIX_COMMAND_FAULT_TOLERANCE_REGISTER         = 118, */
   /* DEPRECATED VIX_COMMAND_FAULT_TOLERANCE_UNREGISTER       = 119, */
   /* DEPRECATED VIX_COMMAND_FAULT_TOLERANCE_CONTROL          = 120, */
   /* DEPRECATED VIX_COMMAND_FAULT_TOLERANCE_QUERY_SECONDARY  = 121, */

   VIX_COMMAND_VM_PAUSE                         = 122,
   VIX_COMMAND_VM_UNPAUSE                       = 123,
   /* DEPRECATED VIX_COMMAND_GET_SNAPSHOT_LOG_INFO            = 124, */
   /* DEPRECATED VIX_COMMAND_SET_REPLAY_SPEED                 = 125, */

   /* DEPRECATED VIX_COMMAND_ANSWER_USER_MESSAGE              = 126, */
   /* DEPRECATED VIX_COMMAND_SET_CLIENT_LOCALE                = 127, */

   VIX_COMMAND_GET_PERFORMANCE_DATA             = 128,

   /* DEPRECATED VIX_COMMAND_REFRESH_RUNTIME_PROPERTIES       = 129, */

   VIX_COMMAND_GET_SNAPSHOT_SCREENSHOT          = 130,
   /* DEPRECATED VIX_COMMAND_ADD_TIMEMARKER                   = 131, */

   VIX_COMMAND_WAIT_FOR_USER_ACTION_IN_GUEST    = 132,
   /* DEPRECATED VIX_COMMAND_VMDB_END_TRANSACTION             = 133, */
   /* DEPRECATED VIX_COMMAND_VMDB_SET                         = 134, */

   VIX_COMMAND_CHANGE_VIRTUAL_HARDWARE          = 135,

   VIX_COMMAND_HOT_PLUG_CPU                     = 136,
   VIX_COMMAND_HOT_PLUG_MEMORY                  = 137,
   VIX_COMMAND_HOT_ADD_DEVICE                   = 138,
   VIX_COMMAND_HOT_REMOVE_DEVICE                = 139,

   /* DEPRECATED VIX_COMMAND_DEBUGGER_ATTACH                  = 140, */
   /* DEPRECATED VIX_COMMAND_DEBUGGER_DETACH                  = 141, */
   /* DEPRECATED VIX_COMMAND_DEBUGGER_SEND_COMMAND            = 142, */

   /* DEPRECATED VIX_COMMAND_GET_RECORD_STATE                 = 143, */
   /* DEPRECATED VIX_COMMAND_SET_RECORD_STATE                 = 144, */
   /* DEPRECATED VIX_COMMAND_REMOVE_RECORD_STATE              = 145, */
   /* DEPRECATED VIX_COMMAND_GET_REPLAY_STATE                 = 146, */
   /* DEPRECATED VIX_COMMAND_SET_REPLAY_STATE                 = 147, */
   /* DEPRECATED VIX_COMMAND_REMOVE_REPLAY_STATE              = 148, */

   /* DEPRECATED VIX_COMMAND_CANCEL_USER_PROGRESS_MESSAGE     = 150, */
   
   VIX_COMMAND_GET_VMX_DEVICE_STATE             = 151,

   /* DEPRECATED VIX_COMMAND_GET_NUM_TIMEMARKERS              = 152, */
   /* DEPRECATED VIX_COMMAND_GET_TIMEMARKER                   = 153, */
   /* DEPRECATED VIX_COMMAND_REMOVE_TIMEMARKER                = 154, */

   VIX_COMMAND_SET_SNAPSHOT_INFO                = 155,
   VIX_COMMAND_SNAPSHOT_SET_MRU                 = 156,

   VIX_COMMAND_LOGOUT_HOST                      = 157,

   VIX_COMMAND_HOT_PLUG_BEGIN_BATCH             = 158,
   VIX_COMMAND_HOT_PLUG_COMMIT_BATCH            = 159,

   VIX_COMMAND_TRANSFER_CONNECTION              = 160,
   VIX_COMMAND_TRANSFER_REQUEST                 = 161,
   VIX_COMMAND_TRANSFER_FINAL_DATA              = 162,

   /* DEPRECATED VIX_COMMAND_ADD_ROLLING_SNAPSHOT_TIER        = 163,    */
   /* DEPRECATED VIX_COMMAND_REMOVE_ROLLING_SNAPSHOT_TIER     = 164,    */
   /* DEPRECATED VIX_COMMAND_LIST_ROLLING_SNAPSHOT_TIER       = 165,    */

   /* DEPRECATED VIX_COMMAND_ADD_ROLLING_SNAPSHOT_TIER_VMX    = 166,    */
   /* DEPRECATED VIX_COMMAND_REMOVE_ROLLING_SNAPSHOT_TIER_VMX = 167,    */
   /* DEPRECATED VIX_COMMAND_LIST_ROLLING_SNAPSHOT_TIER_VMX   = 168,    */

   VIX_COMMAND_LIST_FILESYSTEMS                 = 169,

   VIX_COMMAND_CHANGE_DISPLAY_TOPOLOGY          = 170,

   VIX_COMMAND_SUSPEND_AND_RESUME               = 171,

   VIX_COMMAND_REMOVE_BULK_SNAPSHOT             = 172,

   VIX_COMMAND_COPY_FILE_FROM_READER_TO_GUEST   = 173,

   VIX_COMMAND_GENERATE_NONCE                   = 174,

   VIX_COMMAND_CHANGE_DISPLAY_TOPOLOGY_MODES    = 175,

   VIX_COMMAND_QUERY_CHILDREN                   = 176,

   VIX_COMMAND_LIST_FILES                       = 177,

   VIX_COMMAND_CREATE_DIRECTORY_EX              = 178,

   VIX_COMMAND_MOVE_GUEST_FILE_EX               = 179,

   VIX_COMMAND_MOVE_GUEST_DIRECTORY             = 180,

   VIX_COMMAND_CREATE_TEMPORARY_FILE_EX         = 181,

   VIX_COMMAND_CREATE_TEMPORARY_DIRECTORY       = 182,

   VIX_COMMAND_SET_GUEST_FILE_ATTRIBUTES        = 183,

   VIX_COMMAND_COPY_FILE_FROM_GUEST_TO_READER   = 184,

   VIX_COMMAND_START_PROGRAM                    = 185,

   VIX_COMMAND_LIST_PROCESSES_EX                = 186,

   VIX_COMMAND_READ_ENV_VARIABLES               = 187,

   VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST   = 188,
   VIX_COMMAND_INITIATE_FILE_TRANSFER_TO_GUEST     = 189,

   VIX_COMMAND_ACQUIRE_CREDENTIALS              = 190,
   VIX_COMMAND_RELEASE_CREDENTIALS              = 191,
   VIX_COMMAND_VALIDATE_CREDENTIALS             = 192,
   VIX_COMMAND_TERMINATE_PROCESS                = 193,
   VIX_COMMAND_DELETE_GUEST_FILE_EX             = 194,
   VIX_COMMAND_DELETE_GUEST_DIRECTORY_EX        = 195,
   VIX_COMMAND_HOT_CHANGE_MONITOR_TYPE          = 196,

   VIX_COMMAND_ADD_AUTH_ALIAS                   = 197,
   VIX_COMMAND_REMOVE_AUTH_ALIAS                = 198,
   VIX_COMMAND_LIST_AUTH_PROVIDER_ALIASES       = 199,
   VIX_COMMAND_LIST_AUTH_MAPPED_ALIASES         = 200,

   VIX_COMMAND_CREATE_REGISTRY_KEY              = 201,
   VIX_COMMAND_LIST_REGISTRY_KEYS               = 202,
   VIX_COMMAND_DELETE_REGISTRY_KEY              = 203,
   VIX_COMMAND_SET_REGISTRY_VALUE               = 204,
   VIX_COMMAND_LIST_REGISTRY_VALUES             = 205,
   VIX_COMMAND_DELETE_REGISTRY_VALUE            = 206,

   VIX_COMMAND_REMOVE_AUTH_ALIAS_BY_CERT        = 207,

   /*
    * HOWTO: Adding a new Vix Command. Step 2a.
    *
    * Add a new ID for your new function prototype here. BE CAREFUL. The
    * OFFICIAL list of id's is in the bfg-main tree, in bora/lib/public/vixCommands.h.
    * When people add new command id's in different tree, they may collide and use
    * the same ID values. This can merge without conflicts, and cause runtime bugs.
    * Once a new command is added here, a command info field needs to be added
    * in bora/lib/foundryMsg/foundryMsg.c as well.
    */
   VIX_COMMAND_LAST_NORMAL_COMMAND              = 208,

   VIX_TEST_UNSUPPORTED_TOOLS_OPCODE_COMMAND    = 998,
   VIX_TEST_UNSUPPORTED_VMX_OPCODE_COMMAND      = 999,
};


/*
 * These are the command names that are passed through the backdoor from the
 * VMX to the tools.
 */
#define VIX_BACKDOOR_COMMAND_VERSION               "Vix_1_"
#define VIX_BACKDOORCOMMAND_RUN_PROGRAM            VIX_BACKDOOR_COMMAND_VERSION"Run_Program"
#define VIX_BACKDOORCOMMAND_SYNCDRIVER_FREEZE      VIX_BACKDOOR_COMMAND_VERSION"SyncDriver_Freeze"
#define VIX_BACKDOORCOMMAND_SYNCDRIVER_THAW        VIX_BACKDOOR_COMMAND_VERSION"SyncDriver_Thaw"
#define VIX_BACKDOORCOMMAND_GET_PROPERTIES         VIX_BACKDOOR_COMMAND_VERSION"Get_ToolsProperties"
#define VIX_BACKDOORCOMMAND_UNRECOGNIZED_COMMAND   VIX_BACKDOOR_COMMAND_VERSION"Unrecognized_Command"
#define VIX_BACKDOORCOMMAND_COMMAND                VIX_BACKDOOR_COMMAND_VERSION"Relayed_Command"
#define VIX_BACKDOORCOMMAND_MOUNT_VOLUME_LIST      VIX_BACKDOOR_COMMAND_VERSION"Mount_Volumes"


/*
 * This is the set of features that may be supported by different
 * versions of the VMX or Vix Tools.
 */
enum VixToolsFeatures {
   VIX_TOOLSFEATURE_SUPPORT_GET_HANDLE_STATE   = 0x0001,
   /* VIX_TOOLSFEATURE_SUPPORT_OPEN_URL           = 0x0002, Removed in version 1.11*/
};


enum  {
   VIX_TOOLS_READ_FILE_ACCESS    = 0x01,
   VIX_TOOLS_WRITE_FILE_ACCESS   = 0x02,
};


/*
 * These are the command names that are passed through the backdoor from the tools
 * to the VMX.
 */
#define VIX_BACKDOORCOMMAND_RUN_PROGRAM_DONE    "Run_Program_Done"


#define VIX_FEATURE_UNKNOWN_VALUE "Unknown"


/*
 * VIX_COMMAND_RUN_PROGRAM returns 2 integer values as an array. These
 * are the indexes
 * TODO: Delete this enum
 */
enum VixRunProgramResultValues {
   VIX_COMMAND_RUN_PROGRAM_ELAPSED_TIME_RESULT   = 0,
   VIX_COMMAND_RUN_PROGRAM_EXIT_CODE_RESULT      = 1,
};

/* These are the values of Vix objects. */
#define VIX_VM_OBJECT_TYPE                        "VixVM"

/* VM enumeration */
#ifdef _WIN32
#define VIX_WINDOWSREGISTRY_VMWARE_KEY             "Software\\" COMPANY_NAME
#define VIX_WINDOWSREGISTRY_RUNNING_VM_LIST        "Running VM List"
#define VIX_WINDOWSREGISTRY_VMWARE_KEY_RUNNING_VM_LIST VIX_WINDOWSREGISTRY_VMWARE_KEY "\\" VIX_WINDOWSREGISTRY_RUNNING_VM_LIST
#endif


/*
 * This is used to denote that the contents of a VIX XML-like response
 * string has been escaped. Old Tools did not escape the contents.
 * This tag is only used for existing commands that did not originally perform
 * escaping. Any new command must always escape any strings passed in XML.
 * See ListProcessesInGuest as an example.
 * The protocol works as follows:
 * 1) A client library that internally knows how to handle escaped XML opts in
 *    by including the VIX_REQUESTMSG_ESCAPE_XML_DATA in relevent requests.
 * 2) Tools that understands the VIX_REQUESTMSG_ESCAPE_XML_DATA flag sees that
 *    it is set in the request, and then escapes all string data within the
 *    XML response. To indicate to the client that it has understood the
 *    request, it include the VIX_XML_ESCAPED_TAG in the response (at the
 *    begining of the response).
 * 3) When the client library receives the response, it searches for the
 *    VIX_XML_ESCAPED_TAG. If it is present, it then unescapes all string
 *    data in the response. If the tag is not present, the client library
 *    assumes that the Tools did not understand VIX_REQUESTMSG_ESCAPE_XML_DATA
 *    and that the string data is not escaped.
 * The following characters are escaped: '<', '>', and '%'.
 * For new commands (starting with those released in M/N for the vSphere
 * guest ops project), the escaping is exactly the same, but the
 * VIX_REQUESTMSG_ESCAPE_XML_DATA flag and the VIX_XML_ESCAPED_TAG are not
 * used, since both ends expect escaping.
 */
#define VIX_XML_ESCAPED_TAG   "<escaped/>"

#define VIX_XML_ESCAPE_CHARACTER '%'


/*
 *-----------------------------------------------------------------------------
 *
 * VixMsg --
 *
 * These are the formatting and parsing utilities provided by the VixMsg
 * library.
 *
 *-----------------------------------------------------------------------------
 */

#ifndef VIX_HIDE_FROM_JAVA
struct VixCommandRequestHeader *
VixMsg_AllocRequestMsg(size_t msgHeaderAndBodyLength,
                       int opCode,
                       uint64 cookie,
                       int credentialType,
                       const char *userNamePassword);

struct VixCommandResponseHeader *
VixMsg_AllocResponseMsg(const struct VixCommandRequestHeader *requestHeader,
                        VixError error,
                        uint32 additionalError,
                        size_t responseBodyLength,
                        const void *responseBody,
                        size_t *responseMsgLength);

void VixMsg_InitResponseMsg(struct VixCommandResponseHeader *responseHeader,
                            const struct VixCommandRequestHeader *requestHeader,
                            VixError error,
                            uint32 additionalError,
                            size_t totalMessageLength);

VixError VixMsg_ValidateMessage(const void *vMsg, size_t msgLength);

VixError VixMsg_ValidateRequestMsg(const void *vMsg, size_t msgLength);

VixError VixMsg_ValidateResponseMsg(const void *vMsg, size_t msgLength);

VixError VixMsg_ParseWriteVariableRequest(VixMsgWriteVariableRequest *msg,
                                          char **valueName,
                                          char **value);

VixError VixMsg_ObfuscateNamePassword(const char *userName,
                                      const char *password,
                                      char **result);

VixError VixMsg_DeObfuscateNamePassword(const char *packagedName,
                                        char **userNameResult,
                                        char **passwordResult);

VixError VixMsg_EncodeString(const char *str, char **result);

VixError VixMsg_DecodeString(const char *str, char **result);

Bool VixMsg_ValidateCommandInfoTable(void);

const char *VixAsyncOp_GetDebugStrForOpCode(int opCode);

VixCommandSecurityCategory VixMsg_GetCommandSecurityCategory(int opCode);

/*
 * Vix private internal properties shared between the Vix client
 * and the VMX.
 */

enum {
   VIX_PROPERTY_VM_POWER_OFF_TO_SNAPSHOT_UID       = 5102,
};

VixError VixMsg_AllocGenericRequestMsg(int opCode,
                                       uint64 cookie,
                                       int credentialType,
                                       const char *userNamePassword,
                                       int options,
                                       VixPropertyListImpl *propertyList,
                                       VixCommandGenericRequest **request);

VixError VixMsg_ParseGenericRequestMsg(const VixCommandGenericRequest *request,
                                       int *options,
                                       VixPropertyListImpl *propertyList);

VixError
VixMsg_ParseSimpleResponseWithString(const VixCommandResponseHeader *response,
                                     const char **result);

void *VixMsg_MallocClientData(size_t size);
void *VixMsg_ReallocClientData(void *ptr, size_t size);
char *VixMsg_StrdupClientData(const char *s, Bool *allocateFailed);

/*
 * Parser state used by VMAutomationMsgParser* group of functions.
 */
typedef struct {
   const char *currentPtr;
   const char *endPtr;
} VMAutomationMsgParser;

/* Keep the original type name around all the old code can stay the same. */
typedef VMAutomationMsgParser VMAutomationRequestParser;


#define VMAutomationRequestParserInit VMAutomationMsgParserInitRequest
#define VMAutomationMsgParserInitRequest(state, msg, fixedLength) \
   __VMAutomationMsgParserInitRequest(__FUNCTION__, __LINE__, state, msg, fixedLength)
VixError
__VMAutomationMsgParserInitRequest(const char *caller,
                                   unsigned int line,
                                   VMAutomationMsgParser *state,
                                   const struct VixCommandRequestHeader *msg,
                                   size_t fixedLength);

#define VMAutomationMsgParserInitResponse(state, msg, fixedLength) \
   __VMAutomationMsgParserInitResponse(__FUNCTION__, __LINE__, state, msg, fixedLength)
VixError
__VMAutomationMsgParserInitResponse(const char *caller,
                                    unsigned int line,
                                    VMAutomationMsgParser *state,
                                    const struct VixCommandResponseHeader *msg,
                                    size_t fixedLength);

#define VMAutomationRequestParserGetRemainingData \
   VMAutomationMsgParserGetRemainingData
const void *
VMAutomationMsgParserGetRemainingData(VMAutomationMsgParser *state,
                                      size_t *length);

#define VMAutomationRequestParserGetData VMAutomationMsgParserGetData
#define VMAutomationMsgParserGetData(state, length, result) \
   __VMAutomationMsgParserGetData(__FUNCTION__, __LINE__,               \
                                  state, length, (const char **)result)
VixError __VMAutomationMsgParserGetData(const char *caller,
                                        unsigned int line,
                                        VMAutomationMsgParser *state,
                                        size_t length,
                                        const char **result);

#define VMAutomationRequestParserGetOptionalString \
   VMAutomationMsgParserGetOptionalString
#define VMAutomationMsgParserGetOptionalString(state, length, result) \
   __VMAutomationMsgParserGetOptionalString(__FUNCTION__, __LINE__,     \
                                            state, length, result)
VixError __VMAutomationMsgParserGetOptionalString(const char *caller,
                                                  unsigned int line,
                                                  VMAutomationMsgParser *state,
                                                  size_t length,
                                                  const char **result);

#define VMAutomationRequestParserGetOptionalStrings \
   VMAutomationMsgParserGetOptionalStrings
#define VMAutomationMsgParserGetOptionalStrings(state, count, length,     \
           result)                                                            \
   __VMAutomationMsgParserGetOptionalStrings(__FUNCTION__, __LINE__,      \
                                             state, count, length, result)
VixError __VMAutomationMsgParserGetOptionalStrings
   (const char *caller,
    unsigned int line,
    VMAutomationMsgParser *state,
    uint32 count,
    size_t length,
    const char **result);

#define VMAutomationRequestParserGetString VMAutomationMsgParserGetString
#define VMAutomationMsgParserGetString(state, length, result) \
   __VMAutomationMsgParserGetString(__FUNCTION__, __LINE__,             \
                                    state, length, result)
VixError __VMAutomationMsgParserGetString(const char *caller,
                                          unsigned int line,
                                          VMAutomationMsgParser *state,
                                          size_t length,
                                          const char **result);

#define VMAutomationRequestParserGetPropertyList \
   VMAutomationMsgParserGetPropertyList
#define VMAutomationMsgParserGetPropertyList(state, length, propList) \
   __VMAutomationMsgParserGetPropertyList(__FUNCTION__, __LINE__,       \
                                          state, length, propList)
VixError
__VMAutomationMsgParserGetPropertyList(const char *caller,
                                       unsigned int line,
                                       VMAutomationMsgParser *state,
                                       size_t length,
                                       VixPropertyListImpl *propList);

#endif   // VIX_HIDE_FROM_JAVA

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _VIX_COMMANDS_H_

   07070100000126000081A40000000000000000000000016822550500007394000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vixOpenSource.h    /*********************************************************
 * Copyright (C) 2007-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * This header file is given out as part of the open source
 * tools. Things in this file are public, but they may not have
 * been tested or documented, and that may change in future releases.
 * The public Vix API is defined in vix.h
 *
 * These definitions are used by the implementation of the vix
 * client, the VMX process, and the tols.
 *
 */

#ifndef _VIXOpenSource_h_
#define _VIXOpenSource_h_

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * VIX_HIDE_BORA_DEPENDENCIES:
 *
 * This flag allows some mini-applications to use parts of Vix that will
 * someday make it into the public API, without including all of the include
 * directories in bora. Specifically, some VMware apps will want to use a
 * separate, more public, build tree, but still make use API that have not been
 * publicly released yet.
 */

/*
 * VIX_HIDE_FROM_JAVA
 *
 * Setting this flag minizes the functions exposed here.  The java
 * binding is generated by processing this header, resulting in a
 * large number of currently unused functions.  To keep the number
 * of functions that need to be exposed via vixWrapper, we hide all
 * but the needed functions to java.
 */

/*
 * If we're hiding functions from java, also hide the bora dependencies.
 */
#ifdef VIX_HIDE_FROM_JAVA
#define VIX_HIDE_BORA_DEPENDENCIES
#endif

#include "vm_basic_types.h"

#ifndef VIX_HIDE_BORA_DEPENDENCIES
#include "cryptoError.h"
#endif // VIX_HIDE_BORA_DEPENDENCIES

#include "vix.h"    // Vix Public API



/*
 *-----------------------------------------------------------------------------
 *
 * Vix Errors --
 *
 *-----------------------------------------------------------------------------
 */


#ifndef VIX_HIDE_BORA_DEPENDENCIES
VixError Vix_TranslateSystemError(int systemError);
VixError Vix_TranslateCryptoError(CryptoError cryptoError);
VixError Vix_TranslateErrno(int systemError);

#ifdef _WIN32
VixError Vix_TranslateCOMError(HRESULT comError);
VixError Vix_TranslateGuestRegistryError(int systemError);
#endif

#endif // VIX_HIDE_BORA_DEPENDENCIES


/*
 * This is an expanded view of a VixError
 * Every VixError is a 64-bit int, so it can fit into this struct.
 *
 * The flags, extraErrorType, and extraError are all optional. They
 * do not have to be set for any error. In fact, these are guaranteed
 * to be all 0 when the error is VIX_OK. This means that any program
 * that checks (VIX_OK == err) or (VIX_OK != err) will always work.
 *
 * The basic error field is a Vix error value, and it's the lsb
 * of the new struct. This means that a 64-bit error can be assigned
 * enum constant, like an integer. For example, err = VIX_E_FAIL; works.
 * This just leaves the flags and extraError fields as 0.
 */
#pragma pack(push, 1)
typedef struct VixErrorFields {
   uint16   error;

   uint8    flags;

   uint8    extraErrorType;
   uint32   extraError;
} VixErrorFields;
#pragma pack(pop)

/*
 * These are the flags for a Vix error.
 */
enum {
   VIX_ERRORFLAG_GUEST         = 0x0001,
   VIX_ERRORFLAG_REMOTE        = 0x0002,
};

/*
 * These are the types of extra error in a Vix error.
 */
enum {
   VIX_ERROREXTRATYPE_NONE          = 0,
   VIX_ERROREXTRATYPE_SNAPSHOT      = 1,
   VIX_ERROREXTRATYPE_DISKLIB       = 2,
   VIX_ERROREXTRATYPE_WINDOWS       = 3,
   VIX_ERROREXTRATYPE_LINUX         = 4,
   VIX_ERROREXTRATYPE_FILE          = 5,
   VIX_ERROREXTRATYPE_VMDB          = 6,
   VIX_ERROREXTRATYPE_AIO           = 7,
   VIX_ERROREXTRATYPE_CRYPTO        = 8,
   VIX_ERROREXTRATYPE_KEYSAFE       = 9,
   VIX_ERROREXTRATYPE_BLOCKLIST     = 10,
   VIX_ERROREXTRATYPE_V2I           = 11,
   VIX_ERROREXTRATYPE_MSGPOST       = 12,
   VIX_ERROREXTRATYPE_NFC           = 13,	// NfcErrorCode
};


/*
 * These are the types of extra error in a Vix error.
 */
#define VIX_ERROR_BASE_ERROR(err) ((VixErrorFields *) &err)->error
#define VIX_ERROR_EXTRA_ERROR(err) ((VixErrorFields *) &err)->extraError
#define VIX_ERROR_EXTRA_ERROR_TYPE(err) ((VixErrorFields *) &err)->extraErrorType
#define VIX_ERROR_FROM_GUEST(err) (((VixErrorFields *) &err)->flags & VIX_ERRORFLAG_GUEST)
#define VIX_ERROR_FROM_REMOTE(err) (((VixErrorFields *) &err)->flags & VIX_ERRORFLAG_REMOTE)
#define VIX_ERROR_SET_FROM_GUEST(err) (((VixErrorFields *) &err)->flags |= VIX_ERRORFLAG_GUEST)
#define VIX_ERROR_SET_FROM_REMOTE(err) (((VixErrorFields *) &err)->flags |= VIX_ERRORFLAG_REMOTE)

#define VIX_SET_GUEST_WINDOWS_ERROR(err, vixError, winError)          \
   do {                                                               \
      err = 0;                                                        \
      VIX_ERROR_BASE_ERROR(err) = vixError;                           \
      VIX_ERROR_EXTRA_ERROR(err) = winError;                          \
      VIX_ERROR_EXTRA_ERROR_TYPE(err) = VIX_ERROREXTRATYPE_WINDOWS;   \
      VIX_ERROR_SET_FROM_GUEST(err);                                  \
   } while (0)

#define VIX_ERROR_SET_ADDITIONAL_ERROR(err, vixError, additionalError) \
   do {                                                                \
      err = additionalError;                                           \
      err = (err << 32) | vixError;                                    \
   } while (0)

#define VIX_SET_ERROR(err, vixError, origErrorType, origError) \
   do {                                                        \
      err = 0;                                                 \
      VIX_ERROR_BASE_ERROR(err) = vixError;                    \
      VIX_ERROR_EXTRA_ERROR_TYPE(err) = origErrorType;         \
      VIX_ERROR_EXTRA_ERROR(err) = origError;                  \
   } while (0)

/*
 * This defines additional error codes.
 * The public error codes are defined in vix.h
 * These error codes are in addition to those.
 */
enum {
   VIX_E_OP_NOT_SUPPORTED_ON_NON_VMWARE_VM         = 3038,

   VIX_E_VI_OP_NOT_SUPPORTED_ON_GUEST              = 3048,
   VIX_E_INVALID_LOGIN_CREDENTIALS                 = 3050,
   VIX_E_GUEST_AUTHTYPE_DISABLED                   = 3051,

   /* File Errors */
   VIX_E_DIRECTORY_NOT_EMPTY                       = 20006,

   VIX_E_GUEST_AUTH_MULIPLE_MAPPINGS               = 20007,

   /* Guest Reg Errors */
   VIX_E_REG_KEY_INVALID                           = 20008,
   VIX_E_REG_KEY_HAS_SUBKEYS                       = 20009,
   VIX_E_REG_VALUE_NOT_FOUND                       = 20010,
   VIX_E_REG_KEY_ALREADY_EXISTS                    = 20011,
   VIX_E_REG_KEY_PARENT_VOLATILE                   = 20012,

   /* Generic Guest Errors */
   VIX_E_HGFS_MOUNT_FAIL                           = 20050,

   /* Reg Errors*/
   VIX_E_REG_INCORRECT_VALUE_TYPE                  = 25000
   /* WARNING. Do not exceed 2**16 */
};

/*
 *-----------------------------------------------------------------------------
 *
 * VIX Handles --
 *
 * These are common functions that apply to handles of several types.
 *-----------------------------------------------------------------------------
 */

/*
 * VIX Property ID's
 *
 * These are used in the tools, but they are not (yet) part of the public
 * API. They may be promoted, but for now they are not tested or documented.
 */

enum {
   VIX_PROPERTY_VM_GUEST_TEMP_DIR_PROPERTY            = 203,

   /* VMX properties. */
   VIX_PROPERTY_VMX_VERSION                           = 4400,
   VIX_PROPERTY_VMX_PRODUCT_NAME                      = 4401,
   /* DEPRECTATED VIX_PROPERTY_VMX_VIX_FEATURES                      = 4402, */

   /* GuestOS and Tools properties. */
   VIX_PROPERTY_GUEST_TOOLS_VERSION                   = 4500,
   VIX_PROPERTY_GUEST_TOOLS_API_OPTIONS               = 4501,
   VIX_PROPERTY_GUEST_OS_FAMILY                       = 4502,
   VIX_PROPERTY_GUEST_OS_VERSION                      = 4503,
   VIX_PROPERTY_GUEST_OS_PACKAGE_LIST                 = 4504,
   VIX_PROPERTY_GUEST_NAME                            = 4505,
   VIX_PROPERTY_GUEST_POWER_OFF_SCRIPT                = 4506,
   VIX_PROPERTY_GUEST_POWER_ON_SCRIPT                 = 4507,
   VIX_PROPERTY_GUEST_RESUME_SCRIPT                   = 4508,
   VIX_PROPERTY_GUEST_SUSPEND_SCRIPT                  = 4509,
   VIX_PROPERTY_GUEST_TOOLS_PRODUCT_NAM               = 4511,
   VIX_PROPERTY_FOREIGN_VM_TOOLS_VERSION              = 4512,
   VIX_PROPERTY_VM_DHCP_ENABLED                       = 4513,
   VIX_PROPERTY_VM_IP_ADDRESS                         = 4514,
   VIX_PROPERTY_VM_SUBNET_MASK                        = 4515,
   VIX_PROPERTY_VM_DEFAULT_GATEWAY                    = 4516,
   VIX_PROPERTY_VM_DNS_SERVER_DHCP_ENABLED            = 4517,
   VIX_PROPERTY_VM_DNS_SERVER                         = 4518,
   VIX_PROPERTY_GUEST_TOOLS_WORD_SIZE                 = 4519,
   VIX_PROPERTY_GUEST_OS_VERSION_SHORT                = 4520,

   VIX_PROPERTY_GUEST_AUTH_SSPI_TOKEN                 = 4531,
   VIX_PROPERTY_GUEST_AUTH_SSPI_SESSION_ID            = 4532,
   VIX_PROPERTY_GUEST_AUTH_SESSION_TICKET             = 4533,

   /* VI guest operation status */
   VIX_PROPERTY_GUEST_START_PROGRAM_ENABLED           = 4540,
   VIX_PROPERTY_GUEST_LIST_PROCESSES_ENABLED          = 4541,
   VIX_PROPERTY_GUEST_TERMINATE_PROCESS_ENABLED       = 4542,
   VIX_PROPERTY_GUEST_READ_ENVIRONMENT_VARIABLE_ENABLED      = 4543,

   VIX_PROPERTY_GUEST_VALIDATE_CREDENTIALS_ENABLED     = 4544,
   VIX_PROPERTY_GUEST_ACQUIRE_CREDENTIALS_ENABLED      = 4545,
   VIX_PROPERTY_GUEST_RELEASE_CREDENTIALS_ENABLED      = 4546,

   VIX_PROPERTY_GUEST_MAKE_DIRECTORY_ENABLED           = 4547,
   VIX_PROPERTY_GUEST_DELETE_FILE_ENABLED              = 4548,
   VIX_PROPERTY_GUEST_DELETE_DIRECTORY_ENABLED         = 4549,
   VIX_PROPERTY_GUEST_MOVE_DIRECTORY_ENABLED           = 4550,
   VIX_PROPERTY_GUEST_MOVE_FILE_ENABLED                = 4551,
   VIX_PROPERTY_GUEST_CREATE_TEMP_FILE_ENABLED         = 4552,
   VIX_PROPERTY_GUEST_CREATE_TEMP_DIRECTORY_ENABLED    = 4553,
   VIX_PROPERTY_GUEST_LIST_FILES_ENABLED               = 4554,
   VIX_PROPERTY_GUEST_CHANGE_FILE_ATTRIBUTES_ENABLED   = 4555,
   VIX_PROPERTY_GUEST_INITIATE_FILE_TRANSFER_FROM_GUEST_ENABLED   = 4556,
   VIX_PROPERTY_GUEST_INITIATE_FILE_TRANSFER_TO_GUEST_ENABLED   = 4557,
   VIX_PROPERTY_GUEST_ADD_AUTH_ALIAS_ENABLED           = 4558,
   VIX_PROPERTY_GUEST_REMOVE_AUTH_ALIAS_ENABLED        = 4559,
   VIX_PROPERTY_GUEST_LIST_AUTH_ALIASES_ENABLED        = 4560,
   VIX_PROPERTY_GUEST_LIST_MAPPED_ALIASES_ENABLED      = 4561,
   VIX_PROPERTY_GUEST_CREATE_REGISTRY_KEY_ENABLED      = 4562,
   VIX_PROPERTY_GUEST_LIST_REGISTRY_KEYS_ENABLED       = 4563,
   VIX_PROPERTY_GUEST_DELETE_REGISTRY_KEY_ENABLED      = 4564,
   VIX_PROPERTY_GUEST_SET_REGISTRY_VALUE_ENABLED       = 4565,
   VIX_PROPERTY_GUEST_LIST_REGISTRY_VALUES_ENABLED     = 4566,
   VIX_PROPERTY_GUEST_DELETE_REGISTRY_VALUE_ENABLED    = 4567,
   VIX_PROPERTY_GUEST_REMOVE_AUTH_ALIAS_BY_CERT_ENABLED = 4568,
};



/*
 *-----------------------------------------------------------------------------
 *
 * PropertyList --
 *
 *-----------------------------------------------------------------------------
 */

/*
 * VIX Property Type
 */

enum {
   //VIX_PROPERTYTYPE_ANY             = 0,
   //VIX_PROPERTYTYPE_INTEGER         = 1,
   //VIX_PROPERTYTYPE_STRING          = 2,
   //VIX_PROPERTYTYPE_BOOL            = 3,
   //VIX_PROPERTYTYPE_HANDLE          = 4,
   //VIX_PROPERTYTYPE_INT64           = 5,
   //VIX_PROPERTYTYPE_BLOB            = 6,
   VIX_PROPERTYTYPE_POINTER           = 7
};


#ifndef VIX_HIDE_FROM_JAVA
/*
 * This is a single name/value pair.
 */
typedef struct VixPropertyValue
{
   int                        propertyID;
   VixPropertyType            type;

   union {
      Bool                    boolValue;
      char                    *strValue;
      int                     intValue;
      int64                   int64Value;
      VixHandle               handleValue;
      struct {
         unsigned char        *blobContents;
         int                  blobSize;
      } blobValue;
      void                    *ptrValue;
   } value;

   Bool                       isDirty;
   Bool                       isSensitive;
   struct VixPropertyValue    *next;
} VixPropertyValue;


/*
 * This is the entire list.
 */
typedef struct VixPropertyListImpl
{
   VixPropertyValue    *properties;
} VixPropertyListImpl;


/*
 * This defines what action Deserialize should take when it encounters
 * a string that is not UTF-8.
 */
typedef enum VixPropertyListBadEncodingAction {
   /*
    * Cancel the deserialization and return an error. This is the recommended
    * value since it is the strictest; you don't have to think about how
    * any clients or library code will handle escaped values.
    * This should always be used when parsing property lists passing arguments
    * to RPCs since we should be very strict in terms of actions we take
    * based on arguments.
    */
   VIX_PROPERTY_LIST_BAD_ENCODING_ERROR,

   /*
    * Escape any non-UTF-8 characters in the string, add the result to the
    * property list, and continue deserializing. This should only be used
    * when there are likely to be applications generated non-ASCII values
    * for the property list in question (e.g., the Tools properties sent by
    * pre-i18n Tools) and the properties are more informative then
    * actionable (something like the hostname of a guest, maybe).
    */
   VIX_PROPERTY_LIST_BAD_ENCODING_ESCAPE,
} VixPropertyListBadEncodingAction;


void VixPropertyList_Initialize(VixPropertyListImpl *propList);

void VixPropertyList_RemoveAllWithoutHandles(VixPropertyListImpl *propList);

VixError VixPropertyList_Serialize(VixPropertyListImpl *propListImpl,
                                   Bool dirtyOnly,
                                   size_t *resultSize,
                                   char **resultBuffer);

VixError VixPropertyList_Deserialize(VixPropertyListImpl *propListImpl,
                                     const char *buffer,
                                     size_t bufferSize,
                                     VixPropertyListBadEncodingAction action);

VixError
VixPropertyList_DeserializeNoClobber(VixPropertyListImpl *propListImpl,
                                     const char *buffer,
                                     size_t bufferSize,
                                     VixPropertyListBadEncodingAction action);

VixError VixPropertyList_GetString(struct VixPropertyListImpl *propList,
                                   int propertyID,
                                   int index,
                                   char **resultValue);

VixError VixPropertyList_SetStringSensitive(struct VixPropertyListImpl *propList,
                                            int propertyID,
                                            const char *value);

VixError VixPropertyList_SetString(struct VixPropertyListImpl *propList,
                                   int propertyID,
                                   const char *value);

VixError VixPropertyList_GetInteger(struct VixPropertyListImpl *propList,
                                    int propertyID,
                                    int index,
                                    int *resultValue);

VixError VixPropertyList_SetInteger(struct VixPropertyListImpl *propList,
                                    int propertyID,
                                    int value);

VixError VixPropertyList_GetBool(struct VixPropertyListImpl *propList,
                                 int propertyID,
                                 int index,
                                 Bool *resultValue);

VixError VixPropertyList_SetBool(struct VixPropertyListImpl *propList,
                                 int propertyID,
                                 Bool value);

VixError VixPropertyList_GetHandle(struct VixPropertyListImpl *propList,
                                   int propertyID,
                                   int index,
                                   VixHandle *resultValue);

VixError VixPropertyList_SetHandle(struct VixPropertyListImpl *propList,
                                   int propertyID,
                                   VixHandle value);

VixError VixPropertyList_GetInt64(struct VixPropertyListImpl *propList,
                                  int propertyID,
                                  int index,
                                  int64 *resultValue);

VixError VixPropertyList_SetInt64(struct VixPropertyListImpl *propList,
                                  int propertyID,
                                  int64 value);

VixError VixPropertyList_GetBlob(struct VixPropertyListImpl *propList,
                                 int propertyID,
                                 int index,
                                 int *resultSize,
                                 unsigned char **resultValue);

VixError VixPropertyList_SetBlob(struct VixPropertyListImpl *propList,
                                 int propertyID,
                                 int blobSize,
                                 const unsigned char *value);

VixError VixPropertyList_SetBlobSensitive(struct VixPropertyListImpl *propList,
                                          int propertyID,
                                          int blobSize,
                                          const unsigned char *value);

VixError VixPropertyList_RemoveAll(VixHandle propertyListHandle);

VixError VixPropertyList_Remove(VixHandle propertyListHandle,
                                int propertyID);

VixError VixPropertyList_RemoveFromImpl(VixPropertyListImpl *propList,
                                        int propertyID);

VixError VixPropertyList_AppendProperties(VixHandle handle,
                                          int firstPropertyID,
                                          ...);

VixError VixPropertyList_FindProperty(VixPropertyListImpl *propList,
                                      int propertyID,
                                      VixPropertyType type,
                                      int index,
                                      Bool createIfMissing,
                                      VixPropertyValue **resultEntry);

Bool VixPropertyList_PropertyExists(VixPropertyListImpl *propList,
                                    int propertyID,
                                    VixPropertyType type);

VixError VixPropertyListAppendProperty(VixPropertyListImpl *propList,
                                       int propertyID,
                                       VixPropertyType type,
                                       VixPropertyValue **resultEntry);

int VixPropertyList_GetNumProperties(VixHandle propertyListHandle,
                                     int propertyID);

VixError VixPropertyList_GetOptionalProperties(VixHandle propertyListHandle,
                                               int firstPropertyID,
                                               ...);

VixError VixPropertyList_GetIndexedProperties(VixHandle propertyListHandle,
                                              Bool ignoreMissingProperties,
                                              int firstPropertyID,
                                              int firstPropertyIndex,
                                              ...);

VixError VixPropertyList_GetPtr(VixPropertyListImpl *propList,
                                int propertyID,
                                int index,
                                void **resultValue);

VixError VixPropertyList_SetPtr(VixPropertyListImpl *propList,
                                int propertyID,
                                void *value);

int VixPropertyList_NumItems(VixPropertyListImpl *propList);

Bool VixPropertyList_Empty(VixPropertyListImpl *propList);

void VixPropertyList_MarkAllSensitive(VixPropertyListImpl *propList);

Bool Vix_XMLFindElementText(const char *pattern,
                            char *str,
                            char *endStr,
                            char **startText,
                            char **stopText,
                            char **resumeSearch);

Bool Vix_XMLFindStringElementText(const char *pattern,
                                  char *str,
                                  char *endStr,
                                  Bool doUnescape,
                                  char **startText,
                                  char **stopText,
                                  Bool *needToFree,
                                  char **resumeSearch);

Bool Vix_XMLResultIsEscaped(const char *resultXML,
                            const char *dataParentTag);

Bool Vix_IsValidString(const char *str);
#endif   // VIX_HIDE_FROM_JAVA


/*
 *-----------------------------------------------------------------------------
 *
 * VixVM --
 *
 * This describes the persistent configuration state of a single VM. The
 * VM may or may not be running.
 *
 *-----------------------------------------------------------------------------
 */

#define VIX_FOREIGN_VM_TOOLS_VMX_VERSION_STRING    "Foreign VM Tools"

/*
 * These are the types of variable strings we can read in the VM.
 */
enum {
   //VIX_VM_GUEST_VARIABLE            = 1,
   //VIX_VM_CONFIG_RUNTIME_ONLY       = 2,
   //VIX_GUEST_ENVIRONMENT_VARIABLE   = 3,
   VIX_GUEST_CONFIG                 = 4,
   VIX_VMDB_VARIABLE                = 5,
};

/*
 * Options for RunProgramInGuest().
 */
enum {
   //VIX_RUNPROGRAM_RETURN_IMMEDIATELY       = 0x0001,
   //VIX_RUNPROGRAM_ACTIVATE_WINDOW          = 0x0002,
   /* DEPRECATED VIX_RUNPROGRAM_USE_INTERACTIVE_SESSION  = 0x0004, */
   VIX_RUNPROGRAM_RUN_AS_LOCAL_SYSTEM      = 0x0008,
};


/*
 * Options for VixVM_ListFileSystemsInGuest()
 */
enum {
   VIX_FILESYSTEMS_SHOW_ALL     = 0x000,
};


/*
 * These are the property flags for each file.  This is a superset
 * of the values defined in the public header.
 */

enum {
   //VIX_FILE_ATTRIBUTES_DIRECTORY     = 0x0001,
   //VIX_FILE_ATTRIBUTES_SYMLINK       = 0x0002,
   VIX_FILE_ATTRIBUTES_HIDDEN          = 0x0004,
   VIX_FILE_ATTRIBUTES_READONLY        = 0x0008,
};

/*
 * These are the propery flags for SetGuestFileAttributes request.
 */

enum {
   VIX_FILE_ATTRIBUTE_SET_ACCESS_DATE        = 0x0001,
   VIX_FILE_ATTRIBUTE_SET_MODIFY_DATE        = 0x0002,
   VIX_FILE_ATTRIBUTE_SET_READONLY           = 0x0004,
   VIX_FILE_ATTRIBUTE_SET_HIDDEN             = 0x0008,
   VIX_FILE_ATTRIBUTE_SET_UNIX_OWNERID       = 0x0010,
   VIX_FILE_ATTRIBUTE_SET_UNIX_GROUPID       = 0x0020,
   VIX_FILE_ATTRIBUTE_SET_UNIX_PERMISSIONS   = 0x0040,
};

/*
 * Subject types for Alias management.
 */
typedef enum VixGuestAuthSubjectType {
   VIX_GUEST_AUTH_SUBJECT_TYPE_NONE              = 0,
   VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED             = 1,
   VIX_GUEST_AUTH_SUBJECT_TYPE_ANY               = 2,
} VixGuestAuthSubjectType;

/*
 * Types for Windows Registry Management.
 */

/*
 * In a 64 bit Windows OS, the registry has two views: 32 bit and 64 bit.
 * Normally using "registry redirection", 32 bit applications automatically
 * access the 32 bit view, while 64 bit applications access 64 bit view.
 * However, applications can pass a flag to specifically access either 32
 * or 64 bit registry view, irrespective of their own bitness.
 *
 * NOTE: 32 bit windows will ignore these flags if passed.
 *
 * Based on the above, and on the fact that in our case 32 bit tools run only
 * on 32 bit windows and 64 bit tools run only on 64 bit windows, we get the
 * following registry view access matrix:
 * Application      wowNative            wow32             wow64
 * -----------      --------             -----             -----
 * 32 bit tools     32 bit view          32 bit view       32 bit view
 * 64 bit tools     64 bit view          32 bit view       64 bit view
 * So in essence, we always access 32 bit view UNLESS its 64 bit tools and user
 * has specified either wowNative or wow64 as the registry access flag.
 */

typedef enum VixRegKeyWowBitness {
   VIX_REGISTRY_KEY_WOW_NATIVE        = 0,
   VIX_REGISTRY_KEY_WOW_32            = 1,
   VIX_REGISTRY_KEY_WOW_64            = 2,
} VixRegKeyWowBitness;

typedef enum VixRegValueDataType {
   VIX_REGISTRY_VALUE_DWORD           = 0,
   VIX_REGISTRY_VALUE_QWORD           = 1,
   VIX_REGISTRY_VALUE_STRING          = 2,
   VIX_REGISTRY_VALUE_EXPAND_STRING   = 3,
   VIX_REGISTRY_VALUE_MULTI_STRING    = 4,
   VIX_REGISTRY_VALUE_BINARY          = 5,
} VixRegValueDataType;

/*
 *-----------------------------------------------------------------------------
 *
 * VixDebug --
 *
 *      Vix debug macros, to allow conditional printf debugging with file/line
 *      information.
 *
 *      Use as:
 *
 *      VIX_DEBUG("test debug message: %s %d\n", stringArg, intArg);
 *
 *       Output will go to logfile if VIX_DEBUG_PREFERENCE_NAME is non-zero
 *
 *      VIX_DEBUG_LEVEL(3, "test debug message: %s %d\n", stringArg, intArg);
 *
 *       Output will go to logfile if VIX_DEBUG_PREFERENCE_NAME is >=
 *       the first argument to the macro.
 *
 *-----------------------------------------------------------------------------
 */

#ifndef VIX_HIDE_FROM_JAVA

extern int vixDebugGlobalSpewLevel;
extern int vixApiTraceGlobalSpewLevel;
extern char *VixAllocDebugString(char *fmt, ...) PRINTF_DECL(1,2);
extern void VixDebugInit(int debugLevel, int apiTraceLevel,
                         Bool panicOnVixAssert);
extern const char *VixDebug_GetFileBaseName(const char *path);
extern void VixAssert(const char *cond, const char *file, int lineNum);

extern VixError VixLogError(VixError err, const char *function, int line,
                const char *fileName, const char *fmt, ...) PRINTF_DECL(5, 6);


/*
 * preference name for client and vmx
 */
#define VIX_DEBUG_PREFERENCE_NAME  "vix.debugLevel"
#define VIX_ASSERT_PREFERENCE_NAME "vix.doAssert"
#define VIX_API_TRACE_PREFERENCE_NAME "vix.apiTraceLevel"

/*
 * Assertions.  Normally we'd just use ASSERT(), but we've hit many cases
 * where ASSERT() is desired by foundry developers, but not by foundry users.
 * So we have our own VIX_ASSERT(), which is configured via a preference,
 * vix.doAssert, off by default.
 */
#ifdef VMX86_DEBUG
# ifdef __cplusplus
# define  VIX_ASSERT(cond) (UNLIKELY(!(cond)) ? VixAssert(#cond, __FILE__, __LINE__) : (void) 0)
# else
# define  VIX_ASSERT(cond) (UNLIKELY(!(cond)) ? VixAssert(#cond, __FILE__, __LINE__) : 0)
# endif
#else
#define  VIX_ASSERT(cond)
#endif

#define DEFAULT_VIX_LOG_LEVEL    0
#define DEFAULT_VIX_API_TRACE_LEVEL 0

#define VIX_DEBUG_LEVEL(logLevel, ...)                       \
   if (logLevel <= vixDebugGlobalSpewLevel) {                \
      char *debugString = VixAllocDebugString(__VA_ARGS__);  \
                                                             \
      Log("Vix: [%s:%d]: %s",                                \
          VixDebug_GetFileBaseName(__FILE__), __LINE__,      \
          debugString);                                      \
      free(debugString);                                     \
   }

#define VIX_DEBUG(...)                                       \
   if (0 != vixDebugGlobalSpewLevel) {                       \
      char *debugString = VixAllocDebugString(__VA_ARGS__);  \
                                                             \
      Log("Vix: [%s:%d]: %s",                                \
          VixDebug_GetFileBaseName(__FILE__), __LINE__,      \
          debugString);                                      \
      free(debugString);                                     \
   }

#define VIX_DEBUG_ALWAYS(...)                                \
   {                                                         \
      char *debugString = VixAllocDebugString(__VA_ARGS__);  \
                                                             \
      Log("Vix: [%s:%d]: %s",                                \
          VixDebug_GetFileBaseName(__FILE__), __LINE__,      \
          debugString);                                      \
       free(debugString);                                    \
    }

#define VIX_API_TRACE_ON() (vixApiTraceGlobalSpewLevel > 0)

#define VIX_API_LOG(...)                                     \
   if (VIX_API_TRACE_ON()) {                                 \
      char *debugString = VixAllocDebugString(__VA_ARGS__);  \
                                                             \
      Log("VixApiLog: %s %s\n", __FUNCTION__, debugString);  \
      free(debugString);                                     \
   }

// If no MSG is given, a description of err is supplemented.
#define VIX_ERROR(err) (VIX_ERROR_MSG(err, NULL))
#define VIX_ERROR_MSG(err, ...) (VixLogError(err, __FUNCTION__, __LINE__, \
      VixDebug_GetFileBaseName(__FILE__), __VA_ARGS__))

#endif   // VIX_HIDE_FROM_JAVA

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _VIXOpenSource_h_



07070100000127000081A4000000000000000000000001682255050000166F000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmGuestAppMonitorLib.h /*********************************************************
 * Copyright (c) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmGuestAppMonitorLib.h
 *
 * This is the VMware Guest Application Monitor Lib,
 * an API used to communicate the health of one or more applications
 * running in a virtual machine to VMware infrastructure.
 *
 * @defgroup VMGuestAppMonitor
 *
 * @brief VMGuestAppMonitorLib A collection of routines used in the guest
 *                             by an application monitoring agent to
 *                             indicate the liveness of a monitored set of
 *                             applications.
 *
 *  @code
 *  VMGuestAppMonitor_Enable();
 *
 *  -- Call at least every 30 seconds
 *  VMGuestAppMonitor_MarkActive();
 *
 *  -- When finished monitoring
 *  VMGuestAppMonitor_Disable();
 *  @endcode
 *
 *  To signal an application failure, simply do not call
 *  VMGuestAppMonitor_MarkActive().
 *
 *  @endcode
 *
 *  @addtogroup VMGuestAppMonitor
 * @{
 */

#ifndef _VM_GUEST_APP_MONITOR_LIB_H_
#define _VM_GUEST_APP_MONITOR_LIB_H_

#ifdef __cplusplus
extern "C" {
#endif

/**
 * VMGuestAppMonitorLib error codes.
 */

typedef enum {
   VMGUESTAPPMONITORLIB_ERROR_SUCCESS      = 0,  /**< No error. */
   VMGUESTAPPMONITORLIB_ERROR_OTHER,             /**< Other error */
   VMGUESTAPPMONITORLIB_ERROR_NOT_RUNNING_IN_VM, /**< Not running in a VM */
   VMGUESTAPPMONITORLIB_ERROR_NOT_ENABLED,       /**< Monitoring is not enabled */
   VMGUESTAPPMONITORLIB_ERROR_NOT_SUPPORTED,     /**< Monitoring is not supported */
} VMGuestAppMonitorLibError;


/*
 ******************************************************************************
 * VMGuestAppMonitor_Enable --                                          */ /**
 *
 * Enable application monitoring. After this call, the agent must
 * call VMGuestAppMonitor_MarkActive() at least once every 30
 * seconds or the application will be viewed as having failed.
 *
 * @return VMGUESTAPPMONITORLIB_ERROR_SUCCESS if monitoring has been enabled.
 *
 ******************************************************************************
 */

VMGuestAppMonitorLibError
VMGuestAppMonitor_Enable(void);


/*
 ******************************************************************************
 * VMGuestAppMonitor_Disable --                                         */ /**
 *
 * Disable application monitoring.
 *
 * @return  TRUE if monitoring has been disabled.
 *
 ******************************************************************************
 */

VMGuestAppMonitorLibError
VMGuestAppMonitor_Disable(void);


/*
 ******************************************************************************
 * VMGuestAppMonitor_IsEnabled --                                       */ /**
 *
 * Return the current state of application monitoring.
 *
 * @return  1 (TRUE) if monitoring is enabled.
 *
 ******************************************************************************
 */

int
VMGuestAppMonitor_IsEnabled(void);


/*
 ******************************************************************************
 * VMGuestAppMonitor_MarkActive --                                      */ /**
 *
 * Marks the application as active. This function needs to be called
 * at least once every 30 seconds while application monitoring is
 * enabled or HA will determine that the application has failed.
 *
 * @return  VMGUESTAPPMONITORLIB_ERROR_SUCCESS if successful and
 *          VMGUESTAPPMONITORLIB_ERROR_NOT_ENABLED if monitoring is not enabled.
 *
 ******************************************************************************
 */

VMGuestAppMonitorLibError
VMGuestAppMonitor_MarkActive(void);


/*
 ******************************************************************************
 * VMGuestAppMonitor_GetAppStatus --                                    */ /**
 *
 * Return the current status recorded for the application.
 *
 * @return  the application status. The caller must free the result.
 *
 ******************************************************************************
 */

char *
VMGuestAppMonitor_GetAppStatus(void);

/*
 ******************************************************************************
 * VMGuestAppMonitor_PostAppState --                                    */ /**
 *
 * Post application state.
 *
 * @return  VMGUESTAPPMONITORLIB_ERROR_SUCCESS if successful
 *
 ******************************************************************************
 */


VMGuestAppMonitorLibError
VMGuestAppMonitor_PostAppState(const char *state);


/*
 ******************************************************************************
 * VMGuestAppMonitor_Free --                                            */ /**
 *
 * Free the result of VMGuestAppMonitor_GetAppStatus.
 *
 * @param[in] str  Pointer to the memory to be freed.
 *
 ******************************************************************************
 */

void
VMGuestAppMonitor_Free(char *str);

#ifdef __cplusplus
}
#endif

#endif /* _VM_GUEST_APP_MONITOR_LIB_H_ */

/** @} */
 07070100000128000081A400000000000000000000000168225505000045EB000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmGuestLib.h   /*********************************************************
 * Copyright (c) 2003-2016,2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VM_GUEST_LIB_H_
#define _VM_GUEST_LIB_H_


#include "vm_basic_types.h"
#include "vmSessionId.h"

#ifdef __cplusplus
extern "C" {
#endif


/*
 * This is the VMware GuestLib, an API used for accessing various
 * performance statistics pertaining to the VMware virtual environment
 * from within a VMware Virtual Machine.
 */


#if __GNUC__ > 3 || \
    (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) || \
    defined (__clang__)
#define VMGUESTLIB_DEPRECATED __attribute__((__deprecated__))
#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
#define VMGUESTLIB_DEPRECATED __declspec(deprecated)
#else
#define VMGUESTLIB_DEPRECATED
#endif


/*
 * Error codes returned by GuestLib functions.
 *
 * XXX These should be unified with Foundry's error codes.
 */
typedef enum {
   VMGUESTLIB_ERROR_SUCCESS                = 0,  // No error
   VMGUESTLIB_ERROR_OTHER,                       // Other error
   VMGUESTLIB_ERROR_NOT_RUNNING_IN_VM,           // Not running in a VM
   VMGUESTLIB_ERROR_NOT_ENABLED,                 // GuestLib not enabled on the host.
   VMGUESTLIB_ERROR_NOT_AVAILABLE,               // This stat not available on this host.
   VMGUESTLIB_ERROR_NO_INFO,                     // UpdateInfo() has never been called.
   VMGUESTLIB_ERROR_MEMORY,                      // Not enough memory
   VMGUESTLIB_ERROR_BUFFER_TOO_SMALL,            // Buffer too small
   VMGUESTLIB_ERROR_INVALID_HANDLE,              // Handle is invalid
   VMGUESTLIB_ERROR_INVALID_ARG,                 // One or more arguments were invalid
   VMGUESTLIB_ERROR_UNSUPPORTED_VERSION          // The host doesn't support this request
} VMGuestLibError;


char const * VMGuestLib_GetErrorText(VMGuestLibError error); // IN

/*
 * GuestLib handle.
 *
 * This handle provides a context for accessing all GuestLib
 * state. Use VMGuestLib_OpenHandle to get a handle for use with other
 * GuestLib functions, and use VMGuestLib_CloseHandle to release a
 * handle previously acquired with VMGuestLib_OpenHandle.
 *
 * All of the statistics and session state are maintained per GuestLib
 * handle, so operating on one GuestLib handle will not affect the
 * state of another handle.
 */

struct _VMGuestLibHandle;
typedef struct _VMGuestLibHandle* VMGuestLibHandle;


VMGuestLibError VMGuestLib_OpenHandle(VMGuestLibHandle *handle); // OUT
VMGuestLibError VMGuestLib_CloseHandle(VMGuestLibHandle handle); // IN


/*
 * Update the info and session state for the given handle.
 *
 * Concurrency/thread safety: No locking is done internally around the
 * access of a handle. If a calling program uses multiple threads then
 * the caller must either ensure that each thread of execution is
 * using a separate handle, or the caller must implement locking
 * around calls to VMGuestLib_UpdateInfo() on a given handle to ensure
 * that two threads do not update the handle concurrently.
 *
 * Because the state is maintained per handle and no two handles can
 * be updated exactly simultaneously, the state of two handles may
 * differ even if they are updated one immediately after the other.
 *
 * VMGuestLib_UpdateInfo() is a fairly heavyweight function; it should
 * be viewed similar to a system call in terms of the computational
 * cost and performance hit. For this reason, a user of the API who
 * is concerned about performance will get best results by minimizing
 * the number of calls to VMGuestLib_UpdateInfo().
 */

VMGuestLibError VMGuestLib_UpdateInfo(VMGuestLibHandle handle); // IN


/*
 * Session ID
 *
 * This is used to detect changes in the "session" of a virtual
 * machine. "Session" in this context refers to the particular running
 * instance of this virtual machine on a given host. Moving a virtual
 * machine to another host using VMotion will cause a change in
 * session ID, as will suspending and resuming a virtual machine or
 * reverting to a snapshot.
 *
 * Any of the events above (VMotion, suspend/resume, snapshot revert)
 * are likely to render invalid any information previously retrieved
 * through this API, so the intention of the session ID is to provide
 * applications with a mechanism to detect those events and react
 * accordingly, e.g. by refreshing and resetting any state that relies
 * on validity of previously retrieved information.
 *
 * Use VMGuestLib_GetSessionId() to retrieve the ID for the current
 * session after calling VMGuestLib_UpdateInfo(). After a VMotion or
 * similar event, VMGuestLib_GetSessionId() will return a new value.
 * See code example below for an example of how to use this.
 *
 * If VMGuestLib_UpdateInfo() has never been called,
 * VMGUESTLIB_ERROR_NO_INFO is returned.
 *
 * The session ID should be considered opaque and cannot be compared
 * in any meaningful way with the session IDs from any other virtual
 * machine (e.g. to determine if two virtual machines are on the same
 * host).
 *
 * Here is simple pseudo-code (with no error checking) showing a naive
 * implementation of detecting stale information using the session ID.
 *
 * -----
 *
 * VMSessionId sid = 0;
 * Bool done = FALSE;
 *
 * while (!done) {
 *    VMSessionId tmp;
 *
 *    VMGuestLib_UpdateInfo();
 *    VMGuestLib_GetSessionId(&tmp);
 *    if (tmp != sid) {
 *       ResetStats();
 *       sid = tmp;
 *    }
 * }
 *
 * -----
 */


VMGuestLibError VMGuestLib_GetSessionId(VMGuestLibHandle handle,  // IN
                                        VMSessionId *id);         // OUT


/*
 * Specific Stat accessors. The values returned by these accessor
 * functions are up to date as of the last call to VMGuestLib_UpdateInfo().
 *
 * If VMGuestLib_UpdateInfo() has never been called,
 * VMGUESTLIB_ERROR_NO_INFO is returned.
 */


/* CPU */

/*
 * Retrieves the minimum processing power in MHz available to the virtual
 * machine. Assigning a cpuReservationMhz ensures that even as other virtual
 * machines on a single host consume shared processing power, there is
 * still a certain minimum amount for this virtual machine.
 */
VMGuestLibError VMGuestLib_GetCpuReservationMHz(VMGuestLibHandle handle,    // IN
                                                uint32 *cpuReservationMHz); // OUT

/*
 * Retrieves the maximum processing power in MHz available to the virtual
 * machine. Assigning a cpuLimitMHz ensures that this virtual machine never
 * consumes more than a certain amount of the available processor power. By
 * limiting the amount of processing power consumed, a portion of this
 * shared resource is available to other virtual machines.
 */
VMGuestLibError VMGuestLib_GetCpuLimitMHz(VMGuestLibHandle handle, // IN
                                          uint32 *cpuLimitMHz);    // OUT

/*
 * Retrieves the number of CPU shares allocated to the virtual machine.
 */
VMGuestLibError VMGuestLib_GetCpuShares(VMGuestLibHandle handle, // IN
                                        uint32 *cpuShares);      // OUT

/*
 * Retrieves the number of milliseconds during which the virtual machine
 * has been using the CPU. This value is always less than or equal to
 * elapsedMS. This value, in conjunction with elapsedMS, can be used to
 * estimate efective virtual machine CPU speed.
 */
VMGuestLibError VMGuestLib_GetCpuUsedMs(VMGuestLibHandle handle, // IN
                                        uint64 *cpuUsedMs);      // OUT

/*
 * Host Processor speed. This can be used along with CpuUsedMs and
 * elapsed time to estimate approximate effective VM CPU speed
 * over a time interval. The following pseudocode illustrates how
 * to make this calculation:
 *
 * ------------------------------------
 *
 * uint32 effectiveVMSpeed;
 * uint32 hostMhz;
 * uint64 elapsed1;
 * uint64 elapsed2;
 * uint64 used1;
 * uint64 used2;
 *
 *
 * VMGuestLib_UpdateInfo(handle);
 * VMGuestLib_GetHostProcessorSpeed(handle, &hostMhz);
 * VMGuestLib_GetElapsedMs(handle, &elapsed1);
 * VMGuestLib_GetUsedMs(handle, &used1);
 * ....
 * VMGuestLib_UpdateInfo(handle);
 * VMGuestLib_GetElapsedMs(handle, &elapsed2);
 * VMGuestLib_GetUsedMs(handle, &used2);
 *
 * effectiveVMSpeed = hostMhz * ((used2 - used1) / (elapsed2 - elapsed1));
 *
 *
 * ------------------------------------
 *
 * After this code executes, effectiveVMSpeed will be the approximate
 * average effective speed of the VM's virtual CPU over the time period
 * between the two calls to VMGuestLib_UpdateInfo().
 *
 */

VMGuestLibError VMGuestLib_GetHostProcessorSpeed(VMGuestLibHandle handle, // IN
                                                 uint32 *mhz);            // OUT


/* Memory */

/*
 * Retrieves the minimum amount of memory that is available to the virtual
 * machine. Assigning a cpuReservationMB ensures that even as other virtual
 * machines on a single host consume memory, there is still a certain
 * minimum amount for this virtual machine.
 */
VMGuestLibError VMGuestLib_GetMemReservationMB(VMGuestLibHandle handle,   // IN
                                               uint32 *memReservationMB); // OUT

/*
 * Retrieves the maximum amount of memory that is available to the virtual
 * machine. Assigning a cpuLimitMB ensures that this virtual machine never
 * consumes more than a certain amount of the available processor power. By
 * limiting the amount of processing power consumed, a portion of this
 * shared resource is available to other virtual machines.
 */
VMGuestLibError VMGuestLib_GetMemLimitMB(VMGuestLibHandle handle, // IN
                                         uint32 *memLimitMB);     // OUT

/*
 * Retrieves the number of memory shares allocated to the virtual machine.
 * This API returns an error if the memory shares exceeds the maximum
 * value of 32-bit unsigned integer (0xFFFFFFFF). Therefore,
 * VMGuestLib_GetMemShares64 API should be used instead of this API.
 */
VMGuestLibError VMGuestLib_GetMemShares(VMGuestLibHandle handle, // IN
                                        uint32 *memShares);      // OUT

/*
 * Retrieves the number of memory shares allocated to the virtual machine.
 */
VMGuestLibError VMGuestLib_GetMemShares64(VMGuestLibHandle handle, // IN
                                          uint64 *memShares64);    // OUT

/*
 * Retrieves the mapped memory size of this virtual machine. This
 * is the current total amount of guest memory that is backed by
 * physical memory. Note that this number may include pages of
 * memory shared between multiple virtual machines and thus may be
 * an overestimate of the amount of physical host memory "consumed"
 * by this virtual machine.
 */
VMGuestLibError VMGuestLib_GetMemMappedMB(VMGuestLibHandle handle,  // IN
                                          uint32 *memMappedSizeMB); // OUT

/*
 * Retrieves the estimated amount of memory the virtual machine is actively
 * using. This method returns an estimated working set size for the virtual
 * machine.
 */
VMGuestLibError VMGuestLib_GetMemActiveMB(VMGuestLibHandle handle, // IN
                                          uint32 *memActiveMB);    // OUT

/*
 * Retrieves the amount of overhead memory associated with this virtual
 * machine consumed on the host system.
 */
VMGuestLibError VMGuestLib_GetMemOverheadMB(VMGuestLibHandle handle, // IN
                                            uint32 *memOverheadMB);  // OUT

/*
 * Retrieves the amount of memory that has been reclaimed from this virtual
 * machine via the VMware Memory Balloon mechanism.
 */
VMGuestLibError VMGuestLib_GetMemBalloonedMB(VMGuestLibHandle handle, // IN
                                             uint32 *memBalloonedMB); // OUT

/*
 * Retrieves the amount of memory associated with this virtual machine that
 * has been swapped by the host system.
 */
VMGuestLibError VMGuestLib_GetMemSwappedMB(VMGuestLibHandle handle, // IN
                                           uint32 *memSwappedMB);   // OUT

/*
 * Retrieves the amount of physical memory associated with this virtual
 * machine that is copy-on-write (COW) shared on the host.
 */
VMGuestLibError VMGuestLib_GetMemSharedMB(VMGuestLibHandle handle, // IN
                                          uint32 *memSharedMB);    // OUT

/*
 * Retrieves the estimated amount of physical memory on the host saved
 * from copy-on-write (COW) shared guest physical memory.
 */
VMGuestLibError VMGuestLib_GetMemSharedSavedMB(VMGuestLibHandle handle,   // IN
                                               uint32 *memSharedSavedMB); // OUT

/*
 * Retrieves the estimated amount of physical host memory currently
 * consumed for this virtual machine's physical memory. This is the
 * same as (mapped memory) - (sharedSaved memory).
 */
VMGuestLibError VMGuestLib_GetMemUsedMB(VMGuestLibHandle handle, // IN
                                        uint32 *memUsedMB);      // OUT



/* Elapsed Time */

/*
 * Retrieves the number of milliseconds that have passed in real time since
 * the virtual machine started running on the current host system. The
 * elapsed time counter is reset any time the virtual machine is powered
 * on, resumed, or migrated via VMotion. This value, in conjunction with
 * cpuUsedMS, can be used to estimate effective virtual machine CPU speed.
 * The cpuUsedMS value is always less than or equal to this value.
 */
VMGuestLibError VMGuestLib_GetElapsedMs(VMGuestLibHandle handle, // IN
                                        uint64 *elapsedMs);      // OUT

/*
 * Resource Pool Path.
 *
 * Retrieves a string representation of the path to this virtual machine in
 * the resource pool namespace of the host system.
 *
 * pathBuffer is a pointer to a buffer that will receive the resource
 * pool path string. bufferSize is a pointer to the size of the
 * pathBuffer in bytes. If bufferSize is not large enough to
 * accomodate the path and NUL terminator, then
 * VMGUESTLIB_ERROR_BUFFER_TOO_SMALL is returned and bufferSize
 * contains the amount of memory needed (in bytes).
 */

VMGuestLibError VMGuestLib_GetResourcePoolPath(VMGuestLibHandle handle, // IN
                                               size_t *bufferSize,      // IN/OUT
                                               char *pathBuffer);       // OUT

#ifndef SWIGJAVA
/*
 * CPU stolen time. The time (in ms) that the VM was runnable but not scheduled
 * to run.
 */

VMGuestLibError VMGuestLib_GetCpuStolenMs(VMGuestLibHandle handle, // IN
                                          uint64 *cpuStolenMs);    // OUT
/*
 * Memory Target Size.
 */

VMGuestLibError VMGuestLib_GetMemTargetSizeMB(VMGuestLibHandle handle,  // IN
                                              uint64 *memTargetSizeMB); // OUT

/*
 * Number of physical CPU cores on the host machine.
 */

VMGuestLibError
VMGuestLib_GetHostNumCpuCores(VMGuestLibHandle handle,   // IN
                              uint32 *hostNumCpuCores);  // OUT

/*
 * Total CPU time used by host.
 */

VMGuestLibError
VMGuestLib_GetHostCpuUsedMs(VMGuestLibHandle handle,  // IN
                            uint64 *hostCpuUsedMs);   // OUT

/*
 * Total memory swapped out on the host.
 */

VMGuestLibError
VMGuestLib_GetHostMemSwappedMB(VMGuestLibHandle handle,     // IN
                               uint64 *hostMemSwappedMB);   // OUT

/*
 * Total COW (Copy-On-Write) memory on host.
 */

VMGuestLibError
VMGuestLib_GetHostMemSharedMB(VMGuestLibHandle handle,   // IN
                              uint64 *hostMemSharedMB);  // OUT

/*
 * Total consumed memory on host.
 */

VMGuestLibError
VMGuestLib_GetHostMemUsedMB(VMGuestLibHandle handle,  // IN
                            uint64 *hostMemUsedMB);   // OUT

/*
 * Total memory available to host OS kernel.
 */

VMGuestLibError
VMGuestLib_GetHostMemPhysMB(VMGuestLibHandle handle,  // IN
                            uint64 *hostMemPhysMB);   // OUT

/*
 * Total physical memory free on host.
 */

VMGuestLibError
VMGuestLib_GetHostMemPhysFreeMB(VMGuestLibHandle handle,    // IN
                                uint64 *hostMemPhysFreeMB); // OUT

/*
 * Total host kernel memory overhead.
 *
 * Note: This function is deprecated in GuestSDK 11.2.0. Will be removed
 *       in the future releases. hostMemKernOvhdMB is always set to 0.
 */

VMGUESTLIB_DEPRECATED
VMGuestLibError
VMGuestLib_GetHostMemKernOvhdMB(VMGuestLibHandle handle,     // IN
                                uint64 *hostMemKernOvhdMB);  // OUT

/*
 * Total mapped memory on host.
 */

VMGuestLibError
VMGuestLib_GetHostMemMappedMB(VMGuestLibHandle handle,  // IN
                              uint64 *hostMemMappedMB); // OUT

/*
 * Total unmapped memory on host.
 */

VMGuestLibError
VMGuestLib_GetHostMemUnmappedMB(VMGuestLibHandle handle,    // IN
                                uint64 *hostMemUnmappedMB); // OUT


/*
 * Semi-structured hypervisor stats collection, for troubleshooting.
 */
VMGuestLibError
VMGuestLib_StatGet(const char *encoding,  // IN
                   const char *stat,      // IN
                   char **reply,          // OUT
                   size_t *replySize);    // OUT

void VMGuestLib_StatFree(char *reply, size_t replySize);
#endif /* SWIGJAVA */

#ifdef __cplusplus
}
#endif

#endif /* _VM_GUEST_LIB_H_ */
 07070100000129000081A4000000000000000000000001682255050000045E000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmSessionId.h  /*********************************************************
 * Copyright (C) 2006-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VM_SESSION_ID_H_
#define _VM_SESSION_ID_H_

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

typedef uint64 VMSessionId;

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _VM_SESSION_ID_H_ */
  0707010000012A000081A40000000000000000000000016822550500001420000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_api.h   /*********************************************************
 * Copyright (c) 2010-2016,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 *
 * vm_api.h --
 *
 *    Import/export macro definitions.
 */


#ifndef VM_API_H
#define VM_API_H

/*
 * API macros
 *
 * These macros can be used by libraries to export/import/hide symbols.
 *
 * The general approach is:
 *
 * 1) libfoo defines a macro, say EXPORT_FOO_API or STATIC_FOO_API,
 *    in its Makefile/SCons file depending on the type of the library.
 *
 * 2) libfoo has the following code in a header file, e.g. foo/platform.h,
 *    that is included by all headers that export/import/hide symbols:
 *
 *     // In a file named platform.h
 *     #include <vm_api.h>
 *
 *     #ifdef STATIC_FOO_API
 *     #  define FOO_API VMW_LIB_STATIC
 *     #  define FOO_INLINE_API VMW_LIB_STATIC
 *     #elif defined EXPORT_FOO_API
 *     #  define FOO_API VMW_LIB_DYNAMIC
 *     #  define FOO_INLINE_API VMW_LIB_DYNAMIC_INLINE
 *     #else
 *     #  define FOO_API VMW_LIB_CLIENT
 *     #  define FOO_INLINE_API VMW_LIB_CLIENT_INLINE
 *     #endif
 *
 *    For example:
 *     // In a file named FooTypes.h
 *     #ifndef FOO_TYPES_H
 *     #define FOO_TYPES_H
 *
 *     #include <foo/platform.h>
 *
 *     class FOO_API FooObject { };
 *     FOO_API FooObject *GetFooObject();
 *
 *     class FOO_INLINE_API FooException { };
 *     FOO_INLINE_API int GetInt() { return 5; }
 *
 *     #endif // FOO_TYPES_H
 *
 * DLL/DSO import/export macros.
 *
 * 3) Compiling a shared library - define EXPORT_FOO_API.
 *    libfoo can now use FOO_API for all symbols it would like to export,
 *    which resolves to VMW_LIB_DYNAMIC, while compiling libfoo as a dynamic
 *    shared library.
 *    On posix systems types that need to export RTTI information, including
 *    exceptions, must have default visibility. However these types may not need
 *    __declspec(dllexport) on Windows. Such classes should be marked with a
 *    FOO_INLINE_API macro.
 *
 * 4) Linking against a shared library - no defines needed.
 *    Whenever a client of libfoo includes its headers, these symbols will be
 *    marked with VMW_LIB_CLIENT or VMW_LIB_CLIENT_INLINE, since EXPORT_FOO_API
 *    and STATIC_FOO_API are not defined for the client.
 *
 * Static library macros.
 *
 * 3) Compiling a static library - define STATIC_FOO_API.
 *    libfoo should hide all of its symbols so that they don't leak through to
 *    another library, say libbar, which links libfoo statically. FOO_API and
 *    FOO_INLINE_API would both resolve to VMW_LIB_STATIC while compiling libfoo
 *    as a static library.
 *
 * 4) Linking against a static library - define STATIC_FOO_API.
 *    Whenever a client of libfoo includes its headers, the libfoo symbols
 *    should be hidden on posix systems, marked with VMW_LIB_STATIC. On Windows
 *    defining STATIC_FOO_API is a must when linking against the libfoo static
 *    library. If it's not defined then the libfoo symbols would get an
 *    __declspec(dllimport) declaration. As a result the linker will try to
 *    import them from the list of DLLs present in the link line instead of
 *    linking them directly from the libfoo static library.
 *
 * NOTE: By default, symbols are hidden when compiling with MSC and exported
 * when compiling with GCC.  Thus, it's best to compile with GCC's
 * -fvisibility=hidden and -fvisibility-inlines-hidden flags, so that only
 * symbols explicitly marked with VMW_LIB_DYNAMIC are exported.  Also note that
 * these flags, as well as the attributes, are available in GCC 4 and later.
 *
 * @see http://gcc.gnu.org/wiki/Visibility
 */

#ifdef _MSC_VER
#  define VMW_LIB_STATIC
#  define VMW_LIB_CLIENT   __declspec(dllimport)
#  define VMW_LIB_CLIENT_INLINE
#  define VMW_LIB_DYNAMIC  __declspec(dllexport)
#  define VMW_LIB_DYNAMIC_INLINE
#elif defined __GNUC__
#  define VMW_LIB_STATIC  __attribute__ ((visibility ("hidden")))
#  define VMW_LIB_CLIENT  __attribute__ ((visibility ("default")))
#  define VMW_LIB_CLIENT_INLINE  __attribute__ ((visibility ("default")))
#  define VMW_LIB_DYNAMIC  __attribute__ ((visibility ("default")))
#  define VMW_LIB_DYNAMIC_INLINE  __attribute__ ((visibility ("default")))
#else
#  define VMW_LIB_STATIC
#  define VMW_LIB_CLIENT
#  define VMW_LIB_CLIENT_INLINE
#  define VMW_LIB_DYNAMIC
#  define VMW_LIB_DYNAMIC_INLINE
#endif /* _MSC_VER */

#endif /* VM_API_H */
0707010000012B000081A400000000000000000000000168225505000040AC000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_assert.h    /*********************************************************
 * Copyright (C) 1998-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vm_assert.h --
 *
 *	The basic assertion facility for all VMware code.
 *
 *      For proper use, see bora/doc/assert
 */

#ifndef _VM_ASSERT_H_
#define _VM_ASSERT_H_

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

// XXX not necessary except some places include vm_assert.h improperly
#include "vm_basic_types.h"

/* No stdarg.h on Linux kernels 5.15+ */
#ifndef KBUILD_MODNAME
#include <stdarg.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Some bits of vmcore are used in VMKernel code and cannot have
 * the VMKERNEL define due to other header dependencies.
 */
#if defined(VMKERNEL) && !defined(VMKPANIC)
#define VMKPANIC 1
#endif

/*
 * 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 BINARY_CHECKER || defined COREQUERY || defined DECODER ||   \
    defined DIS16 || defined FROBOS || defined TRAPAPI_APP ||           \
    defined VMM_LINKER || defined VMSS2CORE

# if defined (VMKPANIC)
#  include "vmk_assert.h"
# else /* !VMKPANIC */
#  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 _ASSERT_PANIC_NORETURN(name) \
           Panic(_##name##Fmt "\n", __FILE__, __LINE__)
#  define _ASSERT_PANIC_BUG_NORETURN(bug, name) \
           Panic(_##name##Fmt " bugNr=%d\n", __FILE__, __LINE__, bug)
# endif /* VMKPANIC */
#endif


// These strings don't have newline so that a bug can be tacked on.
#define _AssertAssertFmt           "ASSERT %s:%d"
#define _AssertVerifyFmt           "VERIFY %s:%d"
#define _AssertNotImplementedFmt   "NOT_IMPLEMENTED %s:%d"
#define _AssertNotReachedFmt       "NOT_REACHED %s:%d"
#define _AssertMemAllocFmt         "MEM_ALLOC %s:%d"
#define _AssertNotTestedFmt        "NOT_TESTED %s:%d"


/*
 * Panic and log functions
 */

void Log(const char *fmt, ...)     PRINTF_DECL(1, 2);
void Warning(const char *fmt, ...) PRINTF_DECL(1, 2);

#if defined VMKPANIC
void Panic_SaveRegs(void);

NORETURN void Panic_NoSave(const char *fmt, ...) PRINTF_DECL(1, 2);

# define Panic(fmt...)        \
   do {                       \
      Panic_SaveRegs();       \
      Panic_NoSave(fmt);      \
   } while(0)

#else /* !VMKPANIC */
NORETURN void Panic(const char *fmt, ...) PRINTF_DECL(1, 2);
#endif

void LogThrottled(uint32 *count, const char *fmt, ...) PRINTF_DECL(2, 3);
void WarningThrottled(uint32 *count, const char *fmt, ...) PRINTF_DECL(2, 3);


#ifndef ASSERT_IFNOT
   /*
    * 'UNLIKELY' is defined with __builtin_expect, which does not warn when
    * passed an assignment (gcc bug 36050). To get around this, we put 'cond'
    * in an 'if' statement and make sure it never gets executed by putting
    * that inside of 'if (0)'. We use gcc's statement expression syntax to
    * make ASSERT an expression because some code uses it that way.
    *
    * Since statement expression syntax is a gcc extension and since it's
    * not clear if this is a problem with other compilers, the ASSERT
    * definition was not changed for them. Using a bare 'cond' with the
    * ternary operator may provide a solution.
    *
    * PR 271512: When compiling with gcc, catch assignments inside an ASSERT.
    */

# ifdef __GNUC__
#  define ASSERT_IFNOT(cond, panic)                                       \
         ({if (UNLIKELY(!(cond))) { panic; if (0) { if (cond) {;}}} (void)0;})
# else
#  define ASSERT_IFNOT(cond, panic)                                       \
         (UNLIKELY(!(cond)) ? (panic) : (void)0)
# endif
#endif


/*
 * Assert, panic, and log macros
 *
 * Some of these are redefined or undef below in !VMX86_DEBUG.
 */

#if defined VMX86_DEBUG
   /*
    * Assert is a debug-only construct.
    *
    * Assert should capture (i.e., document and validate) invariants,
    * including method preconditions, postconditions, loop invariants,
    * class invariants, data structure invariants, etc.
    *
    * ASSERT() is special cased because of interaction with Windows DDK.
    */
# undef  ASSERT
# define ASSERT(cond) ASSERT_IFNOT(cond, _ASSERT_PANIC(AssertAssert))
# define ASSERT_BUG(bug, cond) \
           ASSERT_IFNOT(cond, _ASSERT_PANIC_BUG(bug, AssertAssert))
#endif

   /*
    * Verify is present on all build types.
    *
    * Verify should protect against missing functionality (e.g., unhandled
    * cases), bugs and other forms of gaps, and also be used as the fail-safe
    * way to plug remaining security risks. Verify is not the correct primitive
    * to use to validate an invariant, as a condition never being true implies
    * that it need not be handled.
    */
#undef  VERIFY
#define VERIFY(cond) \
           ASSERT_IFNOT(cond, _ASSERT_PANIC_NORETURN(AssertVerify))
#define VERIFY_BUG(bug, cond) \
           ASSERT_IFNOT(cond, _ASSERT_PANIC_BUG_NORETURN(bug, AssertVerify))

   /*
    * NOT IMPLEMENTED is useful to indicate that a codepath has not yet
    * been implemented, and should cause execution to forcibly quit if it is
    * ever reached. Some instances use NOT_IMPLEMENTED for things that will
    * never be implemented (as implied by ASSERT_NOT_IMPLEMENTED).
    *
    * PR1151214 asks for ASSERT_NOT_IMPLEMENTED to be replaced with VERIFY.
    * ASSERT_NOT_IMPLEMENTED is a conditional NOT_IMPLEMENTED. Despite the
    * name, ASSERT_NOT_IMPLEMENTED is present in release builds.
    *
    * NOT_IMPLEMENTED_BUG is NOT_IMPLEMENTED with the bug number included
    * in the panic string.
    */
#define ASSERT_NOT_IMPLEMENTED(cond) \
           ASSERT_IFNOT(cond, NOT_IMPLEMENTED())

#if defined VMKPANIC || defined VMM
# define NOT_IMPLEMENTED()        _ASSERT_PANIC_NORETURN(AssertNotImplemented)
#else
# define NOT_IMPLEMENTED()        _ASSERT_PANIC(AssertNotImplemented)
#endif

#if defined VMM
# define NOT_IMPLEMENTED_BUG(bug) \
          _ASSERT_PANIC_BUG_NORETURN(bug, AssertNotImplemented)
#else
# define NOT_IMPLEMENTED_BUG(bug) _ASSERT_PANIC_BUG(bug, AssertNotImplemented)
#endif

   /*
    * NOT_REACHED is meant to indicate code paths that we can never
    * execute. This can be very dangerous on release builds due to how
    * some compilers behave when you do potentially reach the point
    * indicated by NOT_REACHED and can lead to very difficult to debug
    * failures. NOT_REACHED should be used sparingly due to this.
    *
    * On debug builds, NOT_REACHED is a Panic with a fixed string.
    */
#if defined VMKPANIC || defined VMM
# define NOT_REACHED()            _ASSERT_PANIC_NORETURN(AssertNotReached)
#else
# define NOT_REACHED()            _ASSERT_PANIC(AssertNotReached)
#endif

#if !defined VMKERNEL && !defined VMKBOOT && !defined VMKERNEL_MODULE
   /*
    * PR 2621164,2624036: ASSERT_MEM_ALLOC is deprecated and should not be
    * used. Please use VERIFY where applicable, since the latter aligns
    * better with the consistency model as defined by bora/doc/assert. You
    * could also consider the Util_Safe*alloc* functions in userland.
    *
    * Despite its name, ASSERT_MEM_ALLOC is present in both debug and release
    * builds.
    */
# define ASSERT_MEM_ALLOC(cond) \
           ASSERT_IFNOT(cond, _ASSERT_PANIC(AssertMemAlloc))
#endif

   /*
    * ASSERT_NO_INTERRUPTS & ASSERT_HAS_INTERRUPTS are shorthand to
    * assert whether interrupts are disabled or enabled.
    */
#define ASSERT_NO_INTERRUPTS()  ASSERT(!INTERRUPTS_ENABLED())
#define ASSERT_HAS_INTERRUPTS() ASSERT(INTERRUPTS_ENABLED())

   /*
    * NOT_TESTED may be used to indicate that we've reached a code path.
    * It simply puts an entry in the log file.
    *
    * ASSERT_NOT_TESTED does the same, conditionally.
    * NOT_TESTED_ONCE will only log the first time we executed it.
    * NOT_TESTED_1024 will only log every 1024th time we execute it.
    */
#ifdef VMX86_DEVEL
# define NOT_TESTED()      Warning(_AssertNotTestedFmt "\n", __FILE__, __LINE__)
#else
# define NOT_TESTED()      Log(_AssertNotTestedFmt "\n", __FILE__, __LINE__)
#endif

#define ASSERT_NOT_TESTED(cond) (UNLIKELY(!(cond)) ? NOT_TESTED() : (void)0)
#define NOT_TESTED_ONCE()       DO_ONCE(NOT_TESTED())

#define NOT_TESTED_1024()                                               \
   do {                                                                 \
      static MONITOR_ONLY(PERVCPU) uint16 count = 0;                    \
      if (UNLIKELY(count == 0)) { NOT_TESTED(); }                       \
      count = (count + 1) & 1023;                                       \
   } while (0)

#define LOG_ONCE(...) DO_ONCE(Log(__VA_ARGS__))


/*
 * Redefine macros that have a different behaviour on release
 * builds. This includes no behaviour (ie. removed).
 */

#if !defined VMX86_DEBUG // {

# undef  ASSERT
# define ASSERT(cond)          ((void)0)
# define ASSERT_BUG(bug, cond) ((void)0)

/*
 * NOT_REACHED on debug builds is a Panic; but on release
 * builds reaching it is __builtin_unreachable()
 * which is "undefined behaviour" according to
 * gcc. (https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)
 *
 * When used correctly __builtin_unreachable() allows the compiler
 * to generate slightly better code and eliminates some warnings
 * when the compiler can't identify a "fallthrough" path that is
 * never reached.
 *
 * When used incorrectly, __builtin_unreachable is a dangerous
 * construct and we should structure code in such a way that we
 * need fewer instances of NOT_REACHED to silence the compiler,
 * and use the function attribute "noreturn" where appropriate
 * and potentially then using NOT_REACHED as documentation.
 *
 * We should *never* have code after NOT_REACHED in a block as
 * it's unclear to the reader if that path is ever possible, and
 * as mentioned above, gcc will do weird and wonderful things to us.
 *
 * 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).  However, __assume() is used in the Windows ULM
 * implementation, because the newer compiler used for that project
 * generates correct code.
 *
 * With gcc, the __builtin_unreachable() extension is used when the
 * compiler is known to support it.
 */

# if defined VMKPANIC || defined VMM || defined ULM_ESX
#  undef  NOT_REACHED
#  define NOT_REACHED() __builtin_unreachable()
# elif defined ULM_WIN
#  undef  NOT_REACHED
#  define NOT_REACHED() __assume(0)
# else
 // keep debug definition
# endif

# 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).
 *
 * NOTE: if GCC ever changes so that it ignores unused types altogether, this
 * assert might not fire!  We explicitly mark it as unused because GCC 4.8+
 * uses -Wunused-local-typedefs as part of -Wall, which means the typedef will
 * generate a warning.
 */

#if defined(_Static_assert) || defined(__cplusplus) ||                         \
    !defined(__GNUC__) || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)
#define ASSERT_ON_COMPILE(e) \
   do { \
      enum { AssertOnCompileMisused = ((e) ? 1 : -1) }; \
      UNUSED_TYPE(typedef char AssertOnCompileFailed[AssertOnCompileMisused]); \
   } while (0)
#else
#define ASSERT_ON_COMPILE(e) \
   do {                      \
      _Static_assert(e, #e); \
   } while (0)
#endif

/*
 * 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                     \
   }

/*
 * Avoid generating extra code due to asserts which are required by
 * Clang static analyzer, e.g. right before a statement would fail, using
 * the __clang_analyzer__ macro defined only when clang SA is parsing files.
 */
#ifdef __clang_analyzer__
# define ANALYZER_ASSERT(cond) ASSERT(cond)
#else
# define ANALYZER_ASSERT(cond) ((void)0)
#endif

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* ifndef _VM_ASSERT_H_ */
0707010000012C000081A4000000000000000000000001682255050001A7A4000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_atomic.h    /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vm_atomic.h --
 *
 *       Atomic power
 *
 * Note: Only partially tested on ARM processors: Works for View Open
 *       Client, which shouldn't have threads, and ARMv8 processors.
 */

#ifndef _ATOMIC_H_
#define _ATOMIC_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#if defined _MSC_VER && !defined BORA_NO_WIN32_INTRINS
#pragma warning(push)
#pragma warning(disable : 4255)      // disable no-prototype() to (void) warning
#include <intrin.h>
#pragma warning(pop)
#endif

#ifdef __wasm__
#define VM_ATOMIC_USE_C11
#endif

#ifdef VM_ATOMIC_USE_C11
#include <stdatomic.h>
#endif

#include "vm_basic_types.h"
#include "vm_assert.h"

#if defined __cplusplus
extern "C" {
#endif

/*
 * There are two concepts involved when dealing with atomic accesses:
 * 1. Atomicity of the access itself
 * 2. Ordering of the access with respect to other reads&writes (from the view
 *    of other processors/devices).
 *
 * Two examples help to clarify #2:
 * a. Inc: A caller implementing a simple independent global event counter
 *         might not care if the compiler or processor visibly reorders the
 *         increment around other memory accesses.
 * b. Dec: A caller implementing a reference count absolutely *doesn't* want
 *         the compiler or processor to visibly reordering writes after that
 *         decrement: if that happened, the program could then end up writing
 *         to memory that was freed by another processor.
 *
 * C11 has standardized a good model for expressing these orderings when doing
 * atomics. It defines three *tiers* of ordering:
 * 1. Sequential Consistency (every processor sees the same total order of
 *    events)
 *
 * 2. Acquire/Release ordering (roughly, everybody can agree previous events
 *    have completed, but they might disagree on the ordering of previous
 *    independent events).
 *
 *    The relative ordering provided by this tier is sufficient for common
 *    locking and initialization activities, but is insufficient for unusual
 *    synchronization schemes (e.g. IRIW aka Independent Read Independent
 *    Write designs such Dekker's algorithm, Peterson's algorithm, etc.)
 *
 *    In other words, this tier is close in behavior to Sequential Consistency
 *    in much the same way a General-Relativity universe is close to a
 *    Newtonian universe.
 * 3. Relaxed (i.e unordered/unfenced)
 *
 * In C11 standard's terminology for atomic memory ordering,
 * - in case (a) we want "relaxed" ordering for perf and,
 * - in case (b) we want "sequentially consistent" ordering (or perhaps the
 *   only slightly weaker "release" ordering) for correctness.
 *
 * There are standardized mappings of operations to orderings for every
 * processor architecture. See
 * - https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
 * - http://preshing.com/20120913/acquire-and-release-semantics/
 *
 * In this file:
 * 1. all RMW (Read/Modify/Write) operations are sequentially consistent.
 *    This includes operations like Atomic_IncN, Atomic_ReadIfEqualWriteN,
 *    Atomic_ReadWriteN, etc.
 * 2. all R and W operations are relaxed. This includes operations like
 *    Atomic_WriteN, Atomic_ReadN, Atomic_TestBitN, etc.
 *
 * The below routines of course ensure both the CPU and compiler honor the
 * ordering constraint.
 *
 * Notes:
 * 1. Since R-only and W-only operations do not provide ordering, callers
 *    using them for synchronizing operations like double-checked
 *    initialization or releasing spinlocks must provide extra barriers.
 * 2. This implementation of Atomic operations is suboptimal. On x86,simple
 *    reads and writes have acquire/release semantics at the hardware level.
 *    On arm64, we have separate instructions for sequentially consistent
 *    reads and writes (the same instructions are used for acquire/release).
 *    Neither of these are exposed for R-only or W-only callers.
 *
 * For further details on x86 and ARM memory ordering see
 * https://wiki.eng.vmware.com/ARM/MemoryOrdering.
 */

#ifdef VM_ARM_64
#   include "vm_atomic_arm64_begin.h"
#endif


/* Basic atomic types: 8, 16, 32, 64 and 128 bits */
typedef ALIGNED(1) struct Atomic_uint8 {
   volatile uint8 value;
} Atomic_uint8;

typedef ALIGNED(2) struct Atomic_uint16 {
   volatile uint16 value;
} Atomic_uint16;

typedef ALIGNED(4) struct Atomic_uint32 {
   volatile uint32 value;
} Atomic_uint32;

typedef ALIGNED(8) struct Atomic_uint64 {
   volatile uint64 value;
} Atomic_uint64;

#ifdef VM_HAS_INT128
typedef ALIGNED(16) struct Atomic_uint128 {
   volatile uint128 value;
} Atomic_uint128;
#endif

#if defined __arm__
/*
 * LDREX without STREX or CLREX may cause problems in environments where the
 * context switch may not clear the reference monitor - according ARM manual
 * the reference monitor should be cleared after a context switch, but some
 * may not like Linux kernel's non-preemptive context switch path. So use of
 * ARM routines in kernel code may not be safe.
 */
#   if defined __ARM_ARCH_7__ || defined __ARM_ARCH_7A__ ||  \
       defined __ARM_ARCH_7R__|| defined __ARM_ARCH_7M__
#      define VM_ARM_V7
#      ifdef __KERNEL__
#         warning LDREX/STREX may not be safe in linux kernel, since it      \
          does not issue CLREX on context switch (as of 2011-09-29).
#      endif
#   else
#     error Only ARMv7 extends the synchronization primitives ldrex/strex.   \
            For the lower ARM version, please implement the atomic functions \
            by kernel APIs.
#   endif
#endif

/* Data Memory Barrier */
#ifdef VM_ARM_V7
#define dmb() __asm__ __volatile__("dmb" : : : "memory")
#endif

/*
 * Whether GCC flags output operands are supported.
 * If building with GCC 6+ on x86, and 10+ on arm, flags output is supported.
 * Some pieces are still built with GCC 4, which doesn't support flag outputs.
 */
#ifdef __GCC_ASM_FLAG_OUTPUTS__
#define IF_ASM_FLAG_OUTPUT(supportedValue, fallbackValue) supportedValue
#else /* older gcc (or not gcc), flags output is not supported */
#define IF_ASM_FLAG_OUTPUT(supportedValue, fallbackValue) fallbackValue
#endif


/* Convert a volatile uint32 to Atomic_uint32. */
static INLINE Atomic_uint32 *
Atomic_VolatileToAtomic32(volatile uint32 *var)  // IN:
{
   return (Atomic_uint32 *)var;
}
#define Atomic_VolatileToAtomic Atomic_VolatileToAtomic32


/* Convert a volatile uint64 to Atomic_uint64. */
static INLINE Atomic_uint64 *
Atomic_VolatileToAtomic64(volatile uint64 *var)  // IN:
{
   return (Atomic_uint64 *)var;
}


/*
 * The Read/Modify/Write operations on x86/x64 are all written using the
 * "memory" constraint. This is to ensure the compiler treats the operation as
 * a full barrier, flushing any pending/cached state currently residing in
 * registers.
 */

/* Force the link step to fail for unimplemented functions. */
extern int AtomicUndefined(void const *);


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadIfEqualWrite128 --
 *
 *      Compare exchange: Read variable, if equal to oldVal, write newVal
 *
 * Results:
 *      The value that was compared against oldVal.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

#ifdef VM_HAS_INT128
static INLINE uint128
Atomic_ReadIfEqualWrite128(Atomic_uint128 *ptr,   // IN/OUT
                           uint128        oldVal, // IN
                           uint128        newVal) // IN
{
#if defined __GNUC__ && defined VM_ARM_64
#define VM_HAS_ATOMIC_READIFEQUALWRITE128 // This function can be used.
   /*
    * Don't use __sync_val_compare_and_swap, as this cannot magically
    * use the right (LL/SC vs LSE) atomics without -moutline-atomics.
    */
#if __GNUC__ >= 9
   if (Atomic_HaveLse()) {
      SMP_RW_BARRIER_RW();
      __asm__ __volatile__(
         ".arch armv8.2-a            \n\t"
         "casp %0, %H0, %2, %H2, %1  \n\t"
         : "+r" (oldVal),
           "+Q" (ptr->value)
         : "r" (newVal)
      );
      SMP_RW_BARRIER_RW();
      return oldVal;
   } else
#endif /* __GNUC__ */
   {
      union {
         uint128 raw;
         struct {
            uint64 lo;
            uint64 hi;
         };
      } res, _old = { oldVal }, _new = { newVal };
      uint32 failed;

      SMP_RW_BARRIER_RW();
      __asm__ __volatile__(
         "1: ldxp    %x0, %x1, %3        \n\t"
         "   cmp     %x0, %x4            \n\t"
         "   ccmp    %x1, %x5, #0, eq    \n\t"
         "   b.ne    2f                  \n\t"
         "   stxp    %w2, %x6, %x7, %3   \n\t"
         "   cbnz    %w2, 1b             \n\t"
         "2:                             \n\t"
         : "=&r" (res.lo),
           "=&r" (res.hi),
           "=&r" (failed),
           "+Q" (ptr->value)
         : "r" (_old.lo),
           "r" (_old.hi),
           "r" (_new.lo),
           "r" (_new.hi)
         : "cc"
      );
      SMP_RW_BARRIER_RW();
      return res.raw;
   }
#elif defined __GNUC__ && defined __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
#define VM_HAS_ATOMIC_READIFEQUALWRITE128 // This function can be used.
   return __sync_val_compare_and_swap(&ptr->value, oldVal, newVal);
#else
   return AtomicUndefined(ptr + oldVal + newVal);
#endif
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Read8 --
 *
 *      Read the value of the specified object atomically.
 *
 * Results:
 *      The value of the atomic variable.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_Read8(Atomic_uint8 const *var)  // IN:
{
   uint8 val;

#if defined VM_ATOMIC_USE_C11
   val = atomic_load((const _Atomic(uint8) *)&var->value);
#elif defined __GNUC__ && defined VM_ARM_32
   val = AtomicUndefined(var);
#elif defined __GNUC__ && defined VM_ARM_64
   val = _VMATOM_X(R, 8, &var->value);
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
   __asm__ __volatile__(
      "movb %1, %0"
      : "=q" (val)
      : "m" (var->value)
   );
#elif defined _MSC_VER
   val = var->value;
#else
#error Atomic_Read8 not implemented
#endif

   return val;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadWrite8 --
 *
 *      Read followed by write.
 *
 * Results:
 *      The value of the atomic variable before the write.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_ReadWrite8(Atomic_uint8 *var,  // IN/OUT:
                  uint8 val)          // IN:
{
#if defined VM_ATOMIC_USE_C11
   return atomic_exchange((_Atomic(uint8) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_32
   return AtomicUndefined(var + val);
#elif defined __GNUC__ && defined VM_ARM_64
   return _VMATOM_X(RW, 8, TRUE, &var->value, val);
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
   __asm__ __volatile__(
      "xchgb %0, %1"
      : "=q" (val),
        "+m" (var->value)
      : "0" (val)
      : "memory"
   );
   return val;
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (volatile char) == sizeof var->value);
   return _InterlockedExchange8((volatile char *)&var->value, val);
#else
#error Atomic_ReadWrite8 not implemented
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Write8 --
 *
 *      Write the specified value to the specified object atomically.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Write8(Atomic_uint8 *var,  // IN/OUT:
              uint8 val)          // IN:
{
#if defined VM_ATOMIC_USE_C11
   atomic_store((_Atomic(uint8) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_32
   AtomicUndefined(var + val);
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(W, 8, &var->value, val);
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
   __asm__ __volatile__(
      "movb %1, %0"
      : "=m" (var->value)
      : "qn" (val)
   );
#elif defined _MSC_VER
   var->value = val;
#else
#error Atomic_Write8 not implemented
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadIfEqualWrite8 --
 *
 *      Compare exchange: Read variable, if equal to oldVal, write newVal.
 *
 * Results:
 *      The value that was compared against oldVal.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_ReadIfEqualWrite8(Atomic_uint8 *var,  // IN/OUT:
                         uint8 oldVal,       // IN:
                         uint8 newVal)       // IN:
{
#if defined VM_ATOMIC_USE_C11
   atomic_compare_exchange_strong(
      (_Atomic(uint8) *)&var->value, &oldVal, newVal);
   return oldVal;
#elif defined __GNUC__ && defined VM_ARM_32
   return AtomicUndefined(var + oldVal + newVal);
#elif defined __GNUC__ && defined VM_ARM_64
   return _VMATOM_X(RIFEQW, 8, TRUE, &var->value, oldVal, newVal);
#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
   uint8 val;

   __asm__ __volatile__(
      "lock; cmpxchgb %2, %1"
      : "=a" (val),
        "+m" (var->value)
      : "q" (newVal),
        "0" (oldVal)
      : "cc", "memory"
   );

   return val;
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (volatile char) == sizeof var->value);
   return _InterlockedCompareExchange8((volatile char *)&var->value,
                                       newVal, oldVal);
#else
#error Atomic_ReadIfEqualWrite8 not implemented
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadAnd8 --
 *
 *      Atomic read (returned), bitwise AND with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_ReadAnd8(Atomic_uint8 *var, // IN/OUT
                uint8 val)         // IN
{
   uint8 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_and((_Atomic(uint8) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 8, TRUE, &var->value, and, val);
#else
   do {
      res = Atomic_Read8(var);
   } while (res != Atomic_ReadIfEqualWrite8(var, res, res & val));
#endif

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_And8 --
 *
 *      Atomic read, bitwise AND with a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_And8(Atomic_uint8 *var, // IN/OUT
            uint8 val)         // IN
{
#if defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 8, TRUE, &var->value, and, val);
#else
   (void)Atomic_ReadAnd8(var, val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadOr8 --
 *
 *      Atomic read (returned), bitwise OR with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_ReadOr8(Atomic_uint8 *var, // IN/OUT
               uint8 val)         // IN
{
   uint8 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_or((_Atomic(uint8) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 8, TRUE, &var->value, orr, val);
#else
   do {
      res = Atomic_Read8(var);
   } while (res != Atomic_ReadIfEqualWrite8(var, res, res | val));
#endif

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Or8 --
 *
 *      Atomic read, bitwise OR with a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Or8(Atomic_uint8 *var, // IN/OUT
           uint8 val)         // IN
{
#if defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 8, TRUE, &var->value, orr, val);
#else
   (void)Atomic_ReadOr8(var, val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadXor8 --
 *
 *      Atomic read (returned), bitwise XOR with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_ReadXor8(Atomic_uint8 *var, // IN/OUT
                uint8 val)         // IN
{
   uint8 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_xor((_Atomic(uint8) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 8, TRUE, &var->value, eor, val);
#else
   do {
      res = Atomic_Read8(var);
   } while (res != Atomic_ReadIfEqualWrite8(var, res, res ^ val));
#endif

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Xor8 --
 *
 *      Atomic read, bitwise XOR with a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Xor8(Atomic_uint8 *var, // IN/OUT
            uint8 val)         // IN
{
#if defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 8, TRUE, &var->value, eor, val);
#else
   (void)Atomic_ReadXor8(var, val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadAdd8 --
 *
 *      Atomic read (returned), add a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_ReadAdd8(Atomic_uint8 *var, // IN/OUT
                uint8 val)         // IN
{
   uint8 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_add((_Atomic(uint8) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 8, TRUE, &var->value, add, val);
#else
   do {
      res = Atomic_Read8(var);
   } while (res != Atomic_ReadIfEqualWrite8(var, res, res + val));
#endif

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Add8 --
 *
 *      Atomic read, add a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Add8(Atomic_uint8 *var, // IN/OUT
            uint8 val)         // IN
{
#if defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 8, TRUE, &var->value, add, val);
#else
   (void)Atomic_ReadAdd8(var, val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Sub8 --
 *
 *      Atomic read, subtract a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Sub8(Atomic_uint8 *var, // IN/OUT
            uint8 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_sub((_Atomic(uint8) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 8, TRUE, &var->value, sub, val);
#else
   Atomic_Add8(var, -val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Inc8 --
 *
 *      Atomic read, increment, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Inc8(Atomic_uint8 *var) // IN/OUT
{
   Atomic_Add8(var, 1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Dec8 --
 *
 *      Atomic read, decrement, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Dec8(Atomic_uint8 *var) // IN/OUT
{
   Atomic_Sub8(var, 1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadInc8 --
 *
 *      Atomic read (returned), increment, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_ReadInc8(Atomic_uint8 *var) // IN/OUT
{
   return Atomic_ReadAdd8(var, 1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadDec8 --
 *
 *      Atomic read (returned), decrement, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint8
Atomic_ReadDec8(Atomic_uint8 *var) // IN/OUT
{
   return Atomic_ReadAdd8(var, (uint8)-1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Read32 --
 *
 *      Read
 *
 * Results:
 *      The value of the atomic variable.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
Atomic_Read32(Atomic_uint32 const *var) // IN
{
   uint32 value;

#if defined VMM || defined VM_ARM_64 || defined VMKERNEL || defined VMKERNEL_MODULE
   ASSERT(((uintptr_t)var % 4) == 0);
#endif

#if defined VM_ATOMIC_USE_C11
   value = atomic_load((_Atomic(uint32) *)&var->value);
#elif defined __GNUC__
   /*
    * Use inline assembler to force using a single load instruction to
    * ensure that the compiler doesn't split a transfer operation into multiple
    * instructions.
    */

#if defined VM_ARM_32
   __asm__ __volatile__(
      "ldr %0, [%1]"
      : "=r" (value)
      : "r" (&var->value)
   );
#elif defined VM_ARM_64
   value = _VMATOM_X(R, 32, &var->value);
#else
   __asm__ __volatile__(
      "mov %1, %0"
      : "=r" (value)
      : "m" (var->value)
   );
#endif
#elif defined _MSC_VER
   /*
    * Microsoft docs guarantee simple reads and writes to properly
    * aligned 32-bit variables use only a single instruction.
    * http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx
    */

   value = var->value;
#else
#error Atomic_Read32 not implemented
#endif

   return value;
}
#define Atomic_Read Atomic_Read32


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadWrite32 --
 *
 *      Read followed by write
 *
 * Results:
 *      The value of the atomic variable before the write.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
Atomic_ReadWrite32(Atomic_uint32 *var, // IN/OUT
                   uint32 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   return atomic_exchange((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint32 retVal;
   uint32 res;

   dmb();

   __asm__ __volatile__(
   "1: ldrex %[retVal], [%[var]] \n\t"
      "strex %[res], %[val], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [retVal] "=&r" (retVal), [res] "=&r" (res)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();

   return retVal;
#elif defined VM_ARM_64
   return _VMATOM_X(RW, 32, TRUE, &var->value, val);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "xchgl %0, %1"
      : "=r" (val),
        "+m" (var->value)
      : "0" (val)
      : "memory"
   );
   return val;
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   return _InterlockedExchange((long *)&var->value, (long)val);
#else
#error Atomic_ReadWrite32 not implemented
#endif // __GNUC__
}
#define Atomic_ReadWrite Atomic_ReadWrite32


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Write32 --
 *
 *      Write
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Write32(Atomic_uint32 *var, // OUT
               uint32 val)         // IN
{
#if defined VMM || defined VM_ARM_64 || defined VMKERNEL || defined VMKERNEL_MODULE
   ASSERT(((uintptr_t)var % 4) == 0);
#endif

#if defined VM_ATOMIC_USE_C11
   atomic_store((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__
#if defined VM_ARM_64
   _VMATOM_X(W, 32, &var->value, val);
#elif defined VM_ARM_32
   /*
    * Best left this way due to the intricacies of exclusive load/store
    * operations on legacy (32-bit) ARM.
    *
    * A3.4.1 ARM DDI 0406C:
    *
    * When a processor writes using any instruction other than a
    * Store-Exclusive:
    *
    * - if the write is to a physical address that is not covered by its local
    *   monitor the write does not affect the state of the local monitor
    * - if the write is to a physical address that is covered by its local
    *   monitor it is IMPLEMENTATION DEFINED whether the write affects the
    *   state of the local monitor.
    *
    * A3.4.5 ARM DDI 0406C:
    *
    * If two STREX instructions are executed without an intervening LDREX the
    * second STREX returns a status value of 1. This means that:
    *
    * - ARM recommends that, in a given thread of execution, every STREX has a
    *   preceding LDREX associated with it
    * - it is not necessary for every LDREX to have a subsequent STREX.
    */

   Atomic_ReadWrite32(var, val);
#else
   /*
    * Use inline assembler to force using a single store instruction to
    * ensure that the compiler doesn't split a transfer operation into multiple
    * instructions.
    */

   __asm__ __volatile__(
      "mov %1, %0"
      : "=m" (var->value)
      : "r" (val)
   );
#endif
#elif defined _MSC_VER
   /*
    * Microsoft docs guarantee simple reads and writes to properly
    * aligned 32-bit variables use only a single instruction.
    * http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx
    */

   var->value = val;
#else
#error Atomic_Write32 not implemented
#endif
}
#define Atomic_Write Atomic_Write32


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadIfEqualWrite32 --
 *
 *      Compare exchange: Read variable, if equal to oldVal, write newVal
 *
 * Results:
 *      The value that was compared against oldVal.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
Atomic_ReadIfEqualWrite32(Atomic_uint32 *var, // IN/OUT
                          uint32 oldVal,      // IN
                          uint32 newVal)      // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_compare_exchange_strong(
      (_Atomic(uint32) *)&var->value, &oldVal, newVal);
   return oldVal;
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint32 retVal;
   uint32 res;

   dmb();

   __asm__ __volatile__(
   "1: ldrex %[retVal], [%[var]] \n\t"
      "mov %[res], #0 \n\t"
      "teq %[retVal], %[oldVal] \n\t"
      "strexeq %[res], %[newVal], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [retVal] "=&r" (retVal), [res] "=&r" (res)
      : [var] "r" (&var->value), [oldVal] "r" (oldVal), [newVal] "r" (newVal)
      : "cc"
   );

   dmb();

   return retVal;
#elif defined VM_ARM_64
   return _VMATOM_X(RIFEQW, 32, TRUE, &var->value, oldVal, newVal);
#else /* VM_X86_ANY */
   uint32 val;

   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; cmpxchgl %2, %1"
      : "=a" (val),
        "+m" (var->value)
      : "r" (newVal),
        "0" (oldVal)
      : "cc", "memory"
   );
   return val;
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   return _InterlockedCompareExchange((long *)&var->value,
                                      (long)newVal,
                                      (long)oldVal);
#else
#error Atomic_ReadIfEqualWrite32 not implemented
#endif
}
#define Atomic_ReadIfEqualWrite Atomic_ReadIfEqualWrite32


#if defined VM_64BIT || defined VM_ARM_V7 || defined VM_ATOMIC_USE_C11
/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadIfEqualWrite64 --
 *
 *      Compare exchange: Read variable, if equal to oldVal, write newVal
 *
 * Results:
 *      The value that was compared against oldVal.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint64
Atomic_ReadIfEqualWrite64(Atomic_uint64 *var, // IN/OUT
                          uint64 oldVal,      // IN
                          uint64 newVal)      // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_compare_exchange_strong(
      (_Atomic(uint64) *)&var->value, &oldVal, newVal);
   return oldVal;
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint64 retVal;
   uint32 res;

   dmb();

   /*
    * Under Apple LLVM version 5.0 (clang-500.2.76) (based on LLVM 3.3svn)
    * There will be a warning:
    * "value size does not match register size specified by the constraint
    * and modifier [-Wasm-operand-widths]"
    * on the lines:
    * : [var] "r" (&var->value), [oldVal] "r" (oldVal), [newVal] "r" (newVal)
    *                                          ^
    * : [var] "r" (&var->value), [oldVal] "r" (oldVal), [newVal] "r" (newVal)
    *                                                                 ^
    *
    * Furthermore, using a 32-bits register to store a
    * 64-bits value of an variable looks risky.
    */
#if defined __APPLE__ && __clang__ == 1 && __clang_major__ >= 5
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wasm-operand-widths"
#endif
   __asm__ __volatile__(
   "1: ldrexd %[retVal], %H[retVal], [%[var]] \n\t"
      "mov %[res], #0 \n\t"
      "teq %[retVal], %[oldVal] \n\t"
      "teqeq %H[retVal], %H[oldVal] \n\t"
      "strexdeq %[res], %[newVal], %H[newVal], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [retVal] "=&r" (retVal), [res] "=&r" (res)
      : [var] "r" (&var->value), [oldVal] "r" (oldVal), [newVal] "r" (newVal)
      : "cc"
   );
#if defined __APPLE__ && __clang__ == 1 && __clang_major__ >= 5
#pragma clang diagnostic pop
#endif // defined __APPLE__ && __clang__ == 1 && __clang_major__ >= 5
   dmb();

   return retVal;
#elif defined VM_ARM_64
   return _VMATOM_X(RIFEQW, 64, TRUE, &var->value, oldVal, newVal);
#else /* VM_X86_64 */
   uint64 val;

   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "lock; cmpxchgq %2, %1"
      : "=a" (val),
        "+m" (var->value)
      : "r" (newVal),
        "0" (oldVal)
      : "cc", "memory"
   );
   return val;
#endif //VM_ARM_V7
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   return _InterlockedCompareExchange64((__int64 *)&var->value,
                                        (__int64)newVal,
                                        (__int64)oldVal);
#else
#error Atomic_ReadIfEqualWrite64 not implemented
#endif
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_And32 --
 *
 *      Atomic read, bitwise AND with a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_And32(Atomic_uint32 *var, // IN/OUT
             uint32 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_and((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint32 res;
   uint32 tmp;

   dmb();

   __asm__ __volatile__(
   "1: ldrex %[tmp], [%[var]] \n\t"
      "and %[tmp], %[tmp], %[val] \n\t"
      "strex %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined VM_ARM_64
   _VMATOM_X(OP, 32, TRUE, &var->value, and, val);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; andl %1, %0"
      : "+m" (var->value)
      : "ri" (val)
      : "cc", "memory"
   );
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   _InterlockedAnd((long *)&var->value, (long)val);
#else
#error Atomic_And32 not implemented
#endif
}
#define Atomic_And Atomic_And32


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Or32 --
 *
 *      Atomic read, bitwise OR with a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Or32(Atomic_uint32 *var, // IN/OUT
            uint32 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_or((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint32 res;
   uint32 tmp;

   dmb();

   __asm__ __volatile__(
   "1: ldrex %[tmp], [%[var]] \n\t"
      "orr %[tmp], %[tmp], %[val] \n\t"
      "strex %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined VM_ARM_64
   _VMATOM_X(OP, 32, TRUE, &var->value, orr, val);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; orl %1, %0"
      : "+m" (var->value)
      : "ri" (val)
      : "cc", "memory"
   );
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   _InterlockedOr((long *)&var->value, (long)val);
#else
#error Atomic_Or32 not implemented
#endif
}
#define Atomic_Or Atomic_Or32


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Xor32 --
 *
 *      Atomic read, bitwise XOR with a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Xor32(Atomic_uint32 *var, // IN/OUT
             uint32 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_xor((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint32 res;
   uint32 tmp;

   dmb();

   __asm__ __volatile__(
   "1: ldrex %[tmp], [%[var]] \n\t"
      "eor %[tmp], %[tmp], %[val] \n\t"
      "strex %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined VM_ARM_64
   _VMATOM_X(OP, 32, TRUE, &var->value, eor, val);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; xorl %1, %0"
      : "+m" (var->value)
      : "ri" (val)
      : "cc", "memory"
   );
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   _InterlockedXor((long *)&var->value, (long)val);
#else
#error Atomic_Xor32 not implemented
#endif
}
#define Atomic_Xor Atomic_Xor32


#if defined VM_64BIT
/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Xor64 --
 *
 *      Atomic read, bitwise XOR with a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Xor64(Atomic_uint64 *var, // IN/OUT
             uint64 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_xor((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__
#if defined VM_ARM_64
   _VMATOM_X(OP, 64, TRUE, &var->value, eor, val);
#else /* VM_X86_64 */
   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "lock; xorq %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#endif
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   _InterlockedXor64((__int64 *)&var->value, (__int64)val);
#else
#error Atomic_Xor64 not implemented
#endif
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Add32 --
 *
 *      Atomic read, add a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Add32(Atomic_uint32 *var, // IN/OUT
             uint32 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_add((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint32 res;
   uint32 tmp;

   dmb();

   __asm__ __volatile__(
   "1: ldrex %[tmp], [%[var]] \n\t"
      "add %[tmp], %[tmp], %[val] \n\t"
      "strex %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined VM_ARM_64
   _VMATOM_X(OP, 32, TRUE, &var->value, add, val);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; addl %1, %0"
      : "+m" (var->value)
      : "ri" (val)
      : "cc", "memory"
   );
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   _InterlockedExchangeAdd((long *)&var->value, (long)val);
#else
#error Atomic_Add32 not implemented
#endif
}
#define Atomic_Add Atomic_Add32


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Sub32 --
 *
 *      Atomic read, subtract a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Sub32(Atomic_uint32 *var, // IN/OUT
             uint32 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_sub((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint32 res;
   uint32 tmp;

   dmb();

   __asm__ __volatile__(
      "1: ldrex %[tmp], [%[var]] \n\t"
      "sub %[tmp], %[tmp], %[val] \n\t"
      "strex %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined VM_ARM_64
   _VMATOM_X(OP, 32, TRUE, &var->value, sub, val);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; subl %1, %0"
      : "+m" (var->value)
      : "ri" (val)
      : "cc", "memory"
   );
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   /*
    * Microsoft warning C4146, enabled by the /sdl option for
    * additional security checks, objects to `-val' when val is
    * unsigned, even though that is always well-defined by C and has
    * exactly the semantics we want, namely negation modulo 2^32.
    * (The signed version, in contrast, has undefined behaviour at
    * some inputs.)
    *
    * https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4146?view=msvc-170
    * https://docs.microsoft.com/en-us/cpp/build/reference/sdl-enable-additional-security-checks?view=msvc-170
    */
#   pragma warning(push)
#   pragma warning(disable: 4146)
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   _InterlockedExchangeAdd((long *)&var->value, (long)-val);
#   pragma warning(pop)
#else
#error Atomic_Sub32 not implemented
#endif
}
#define Atomic_Sub Atomic_Sub32


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Inc32 --
 *
 *      Atomic read, increment, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Inc32(Atomic_uint32 *var) // IN/OUT
{
#if defined VM_ATOMIC_USE_C11
   Atomic_Add32(var, 1);
#elif defined __GNUC__
#if defined VM_ARM_ANY
   Atomic_Add32(var, 1);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; incl %0"
      : "+m" (var->value)
      :
      : "cc", "memory"
   );
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   _InterlockedIncrement((long *)&var->value);
#else
#error Atomic_Inc32 not implemented
#endif
}
#define Atomic_Inc Atomic_Inc32


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Dec32 --
 *
 *      Atomic read, decrement, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Dec32(Atomic_uint32 *var) // IN/OUT
{
#if defined VM_ATOMIC_USE_C11
   Atomic_Sub32(var, 1);
#elif defined __GNUC__
#if defined VM_ARM_ANY
   Atomic_Sub32(var, 1);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; decl %0"
      : "+m" (var->value)
      :
      : "cc", "memory"
   );
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   _InterlockedDecrement((long *)&var->value);
#else
#error Atomic_Dec32 not implemented
#endif
}
#define Atomic_Dec Atomic_Dec32


/*
 * Note that the technique below can be used to implement ReadX(), where X is
 * an arbitrary mathematical function.
 */


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadOr32 --
 *
 *      Atomic read (returned), bitwise OR with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
Atomic_ReadOr32(Atomic_uint32 *var, // IN/OUT
                uint32 val)         // IN
{
   uint32 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_or((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 32, TRUE, &var->value, orr, val);
#else
   do {
      res = Atomic_Read32(var);
   } while (res != Atomic_ReadIfEqualWrite32(var, res, res | val));
#endif

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadAnd32 --
 *
 *      Atomic read (returned), bitwise And with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
Atomic_ReadAnd32(Atomic_uint32 *var, // IN/OUT
                 uint32 val)         // IN
{
   uint32 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_and((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 32, TRUE, &var->value, and, val);
#else
   do {
      res = Atomic_Read32(var);
   } while (res != Atomic_ReadIfEqualWrite32(var, res, res & val));
#endif

   return res;
}


#if defined VM_64BIT
/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadOr64 --
 *
 *      Atomic read (returned), bitwise OR with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint64
Atomic_ReadOr64(Atomic_uint64 *var, // IN/OUT
                uint64 val)         // IN
{
   uint64 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_or((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 64, TRUE, &var->value, orr, val);
#else
   do {
      res = var->value;
   } while (res != Atomic_ReadIfEqualWrite64(var, res, res | val));
#endif

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadAnd64 --
 *
 *      Atomic read (returned), bitwise AND with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint64
Atomic_ReadAnd64(Atomic_uint64 *var, // IN/OUT
                 uint64 val)         // IN
{
   uint64 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_and((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 64, TRUE, &var->value, and, val);
#else
   do {
      res = var->value;
   } while (res != Atomic_ReadIfEqualWrite64(var, res, res & val));
#endif

   return res;
}
#endif /* defined VM_64BIT */


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadAdd32 --
 *
 *      Atomic read (returned), add a value, write.
 *
 *      If you have to implement ReadAdd32() on an architecture other than
 *      x86 or x86-64, you might want to consider doing something similar to
 *      Atomic_ReadOr32().
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
Atomic_ReadAdd32(Atomic_uint32 *var, // IN/OUT
                 uint32 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   return atomic_fetch_add((_Atomic(uint32) *)&var->value, val);
#elif defined __GNUC__
#ifdef VM_ARM_V7
   uint32 res;
   uint32 retVal;
   uint32 tmp;

   dmb();

   __asm__ __volatile__(
      "1: ldrex %[retVal], [%[var]] \n\t"
      "add %[tmp], %[val], %[retVal] \n\t"
      "strex %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [tmp] "=&r" (tmp), [res] "=&r" (res), [retVal] "=&r" (retVal)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();

   return retVal;
#elif defined VM_ARM_64
   return _VMATOM_X(ROP, 32, TRUE, &var->value, add, val);
#else /* VM_X86_ANY */
   /* Checked against the Intel manual and GCC --walken */
   __asm__ __volatile__(
      "lock; xaddl %0, %1"
      : "=r" (val),
        "+m" (var->value)
      : "0" (val)
      : "cc", "memory"
   );
   return val;
#endif /* VM_X86_ANY */
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (long) == sizeof var->value);
   return _InterlockedExchangeAdd((long *)&var->value, (long)val);
#else
#error Atomic_ReadAdd32 not implemented
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadInc32 --
 *
 *      Atomic read (returned), increment, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
Atomic_ReadInc32(Atomic_uint32 *var) // IN/OUT
{
   return Atomic_ReadAdd32(var, 1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadDec32 --
 *
 *      Atomic read (returned), decrement, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
Atomic_ReadDec32(Atomic_uint32 *var) // IN/OUT
{
   return Atomic_ReadAdd32(var, (uint32)-1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_CMPXCHG64 --
 *
 *      Compare exchange: Read variable, if equal to oldVal, write newVal
 *
 * Results:
 *      TRUE if equal, FALSE if not equal
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Atomic_CMPXCHG64(Atomic_uint64 *var,   // IN/OUT
                 uint64 oldVal,        // IN
                 uint64 newVal)        // IN
{
#if defined VM_ATOMIC_USE_C11
   return atomic_compare_exchange_strong(
      (_Atomic(uint64) *)&var->value, &oldVal, newVal);
#elif defined __GNUC__
#if defined VM_ARM_ANY
   return Atomic_ReadIfEqualWrite64(var, oldVal, newVal) == oldVal;
#else /* VM_X86_ANY */

   Bool equal;
   /* Checked against the Intel manual and GCC --walken */
#if defined __x86_64__
   uint64 dummy;
   __asm__ __volatile__(
      "lock; cmpxchgq %3, %0"
      IF_ASM_FLAG_OUTPUT("", "\n\t" "sete %1")
      : "+m" (*var),
        IF_ASM_FLAG_OUTPUT("=@cce", "=qm") (equal),
        "=a" (dummy)
      : "r" (newVal),
        "2" (oldVal)
      : "cc", "memory"
   );
#else /* 32-bit version for non-ARM */
   typedef struct {
      uint32 lowValue;
      uint32 highValue;
   } S_uint64;

   int dummy1, dummy2;
#   if defined __PIC__
   /*
    * Rules for __asm__ statements in __PIC__ code
    * --------------------------------------------
    *
    * The compiler uses %ebx for __PIC__ code, so an __asm__ statement cannot
    * clobber %ebx. The __asm__ statement can temporarily modify %ebx, but _for
    * each parameter that is used while %ebx is temporarily modified_:
    *
    * 1) The constraint cannot be "m", because the memory location the compiler
    *    chooses could then be relative to %ebx.
    *
    * 2) The constraint cannot be a register class which contains %ebx (such as
    *    "r" or "q"), because the register the compiler chooses could then be
    *    %ebx. (This happens when compiling the Fusion UI with gcc 4.2.1, Apple
    *    build 5577.)
    *
    * 3) Using register classes even for other values is problematic, as gcc
    *    can decide e.g. %ecx == %edi == 0 (as compile-time constants) and
    *    ends up using one register for two things. Which breaks xchg's ability
    *    to temporarily put the PIC pointer somewhere else. PR772455
    *
    * For that reason alone, the __asm__ statement should keep the regions
    * where it temporarily modifies %ebx as small as possible, and should
    * prefer specific register assignments.
    */
   __asm__ __volatile__(
      "xchgl %%ebx, %6"      "\n\t"
      "lock; cmpxchg8b (%3)" "\n\t"
      "xchgl %%ebx, %6"
      IF_ASM_FLAG_OUTPUT("", "\n\t" "sete %0")
      : IF_ASM_FLAG_OUTPUT("=@cce", "=qm") (equal),
        "=a" (dummy1),
        "=d" (dummy2)
      : /*
         * See the "Rules for __asm__ statements in __PIC__ code" above: %3
         * must use a register class which does not contain %ebx.
         * "a"/"c"/"d" are already used, so we are left with either "S" or "D".
         *
         * Note that this assembly uses ALL GP registers (with %esp reserved for
         * stack, %ebp reserved for frame, %ebx reserved for PIC).
         */
        "S" (var),
        "1" (((S_uint64 *)&oldVal)->lowValue),
        "2" (((S_uint64 *)&oldVal)->highValue),
        "D" (((S_uint64 *)&newVal)->lowValue),
        "c" (((S_uint64 *)&newVal)->highValue)
      : "cc", "memory"
   );
#   else
   __asm__ __volatile__(
      "lock; cmpxchg8b %0"
      IF_ASM_FLAG_OUTPUT("", "\n\t" "sete %1")
      : "+m" (*var),
        IF_ASM_FLAG_OUTPUT("=@cce", "=qm") (equal),
        "=a" (dummy1),
        "=d" (dummy2)
      : "2" (((S_uint64 *)&oldVal)->lowValue),
        "3" (((S_uint64 *)&oldVal)->highValue),
        "b" (((S_uint64 *)&newVal)->lowValue),
        "c" (((S_uint64 *)&newVal)->highValue)
      : "cc", "memory"
   );
#   endif
#endif
   return equal;
#endif //VM_ARM_V7
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   return (__int64)oldVal == _InterlockedCompareExchange64((__int64 *)&var->value,
                                                           (__int64)newVal,
                                                           (__int64)oldVal);
#else
#error Atomic_CMPXCHG64 not implemented
#endif // !GNUC
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_CMPXCHG32 --
 *
 *      Compare exchange: Read variable, if equal to oldVal, write newVal
 *
 * Results:
 *      TRUE if equal, FALSE if not equal
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Atomic_CMPXCHG32(Atomic_uint32 *var,   // IN/OUT
                 uint32 oldVal,        // IN
                 uint32 newVal)        // IN
{
#if defined VM_ATOMIC_USE_C11
   return atomic_compare_exchange_strong(
      (_Atomic(uint32) *)&var->value, &oldVal, newVal);
#elif defined __GNUC__
#if defined VM_ARM_ANY
   return Atomic_ReadIfEqualWrite32(var, oldVal, newVal) == oldVal;
#else /* VM_X86_ANY */
   Bool equal;
   uint32 dummy;

   __asm__ __volatile__(
      "lock; cmpxchgl %3, %0"
      IF_ASM_FLAG_OUTPUT("", "\n\t" "sete %1")
      : "+m" (*var),
        IF_ASM_FLAG_OUTPUT("=@cce", "=qm") (equal),
        "=a" (dummy)
      : "r" (newVal),
        "2" (oldVal)
      : "cc", "memory"
   );
   return equal;
#endif /* VM_X86_ANY */
#else // defined __GNUC__
   return Atomic_ReadIfEqualWrite32(var, oldVal, newVal) == oldVal;
#endif // !defined __GNUC__
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Read64 --
 *
 *      Read and return.
 *
 * Results:
 *      The value of the atomic variable.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint64
Atomic_Read64(Atomic_uint64 const *var) // IN
{
#if defined VM_ATOMIC_USE_C11
   return atomic_load((const _Atomic(uint64) *)&var->value);
#else
#if defined __GNUC__
   uint64 value;
#endif

#if defined VMM || defined VM_ARM_64 || defined VMKERNEL || defined VMKERNEL_MODULE
   ASSERT((uintptr_t)var % 8 == 0);
#endif

#if defined __GNUC__ && defined __x86_64__
   /*
    * Use asm to ensure we emit a single load.
    */
   __asm__ __volatile__(
      "movq %1, %0"
      : "=r" (value)
      : "m" (var->value)
   );
#elif defined __GNUC__ && defined __i386__
   /*
    * Since cmpxchg8b will replace the contents of EDX:EAX with the
    * value in memory if there is no match, we need only execute the
    * instruction once in order to atomically read 64 bits from
    * memory.  The only constraint is that ECX:EBX must have the same
    * value as EDX:EAX so that if the comparison succeeds.  We
    * intentionally don't tell gcc that we are using ebx and ecx as we
    * don't modify them and do not care what value they store.
    */
   __asm__ __volatile__(
      "mov %%ebx, %%eax"   "\n\t"
      "mov %%ecx, %%edx"   "\n\t"
      "lock; cmpxchg8b %1"
      : "=&A" (value)
      : "m" (*var)
      : "cc"
   );
#elif defined _MSC_VER && defined VM_64BIT
   /*
    * Microsoft docs guarantee "Simple reads and writes to properly
    * aligned 64-bit variables are atomic on 64-bit Windows."
    * http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx
    *
    * XXX Unconditionally verify that value is properly aligned. Bug 61315.
    */
   return var->value;
#elif defined _MSC_VER && defined VM_ARM_32
   /* MSVC + 32-bit ARM has add64 but no cmpxchg64 */
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   return _InterlockedAdd64((__int64 *)&var->value, 0);
#elif defined _MSC_VER && defined __i386__
   /* MSVC + 32-bit x86 has cmpxchg64 but no add64 */
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   return _InterlockedCompareExchange64((__int64 *)&var->value,
                                        (__int64)255,  // Unlikely value to
                                        (__int64)255); // not dirty cache
#elif defined __GNUC__ && defined VM_ARM_V7
   __asm__ __volatile__(
      "ldrexd %[value], %H[value], [%[var]] \n\t"
      : [value] "=&r" (value)
      : [var] "r" (&var->value)
   );
#elif defined VM_ARM_64
   value = _VMATOM_X(R, 64, &var->value);
#endif

#if defined __GNUC__
   return value;
#endif
#endif // !defined VM_ATOMIC_USE_C11
}


/*
 *----------------------------------------------------------------------
 *
 * Atomic_ReadUnaligned64 --
 *
 *      Atomically read a 64 bit integer, possibly misaligned.
 *      This function can be *very* expensive, costing over 50 kcycles
 *      on Nehalem.
 *
 *      Note that "var" needs to be writable, even though it will not
 *      be modified.
 *
 * Results:
 *      The value of the atomic variable.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

#if defined VM_64BIT
static INLINE uint64
Atomic_ReadUnaligned64(Atomic_uint64 const *var)  // IN:
{
   return Atomic_ReadIfEqualWrite64((Atomic_uint64*)var, 0, 0);
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * Atomic_ReadAdd64 --
 *
 *      Atomically adds a 64-bit integer to another
 *
 * Results:
 *      Returns the old value just prior to the addition
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static INLINE uint64
Atomic_ReadAdd64(Atomic_uint64 *var, // IN/OUT
                 uint64 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   return atomic_fetch_add((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   return _VMATOM_X(ROP, 64, TRUE, &var->value, add, val);
#elif defined __x86_64__

#if defined __GNUC__
   __asm__ __volatile__(
      "lock; xaddq %0, %1"
      : "=r" (val),
        "+m" (var->value)
      : "0" (val)
      : "cc", "memory"
   );
   return val;
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   return _InterlockedExchangeAdd64((__int64 *)&var->value, (__int64)val);
#else
#error Atomic_ReadAdd64 not implemented
#endif

#else
   uint64 oldVal;
   uint64 newVal;

   do {
      oldVal = var->value;
      newVal = oldVal + val;
   } while (!Atomic_CMPXCHG64(var, oldVal, newVal));

   return oldVal;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Atomic_ReadSub64 --
 *
 *      Atomically subtracts a 64-bit integer from another.
 *
 * Results:
 *      Returns the old value just prior to the subtraction
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static INLINE uint64
Atomic_ReadSub64(Atomic_uint64 *var, // IN/OUT
                 uint64 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   return atomic_fetch_sub((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   return _VMATOM_X(ROP, 64, TRUE, &var->value, sub, val);
#else
#   ifdef _MSC_VER
   /*
    * Microsoft warning C4146, enabled by the /sdl option for
    * additional security checks, objects to `-val' when val is
    * unsigned, even though that is always well-defined by C and has
    * exactly the semantics we want, namely negation modulo 2^64.
    * (The signed version, in contrast, has undefined behaviour at
    * some inputs.)
    *
    * https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4146?view=msvc-170
    * https://docs.microsoft.com/en-us/cpp/build/reference/sdl-enable-additional-security-checks?view=msvc-170
    */
#      pragma warning(push)
#      pragma warning(disable: 4146)
#   endif
   return Atomic_ReadAdd64(var, -val);
#   ifdef _MSC_VER
#      pragma warning(pop)
#   endif
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Atomic_ReadInc64 --
 *
 *      Atomically increments a 64-bit integer
 *
 * Results:
 *      Returns the old value just prior to incrementing
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static INLINE uint64
Atomic_ReadInc64(Atomic_uint64 *var) // IN/OUT
{
   return Atomic_ReadAdd64(var, 1);
}


/*
 *----------------------------------------------------------------------
 *
 * Atomic_ReadDec64 --
 *
 *      Atomically decrements a 64-bit integer
 *
 * Results:
 *      Returns the old value just prior to decrementing
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static INLINE uint64
Atomic_ReadDec64(Atomic_uint64 *var) // IN/OUT
{
   return Atomic_ReadAdd64(var, (uint64)CONST64(-1));
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Add64 --
 *
 *      Atomic read, add a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Add64(Atomic_uint64 *var, // IN/OUT
             uint64 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_add((_Atomic(uint64) *)&var->value, val);
#elif !defined VM_64BIT
   Atomic_ReadAdd64(var, val); /* Return value is unused. */
#elif defined __GNUC__
#if defined VM_ARM_64
   _VMATOM_X(OP, 64, TRUE, &var->value, add, val);
#else /* defined VM_X86_64 */
   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "lock; addq %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#endif
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   _InterlockedExchangeAdd64((__int64 *)&var->value, (__int64)val);
#else
#error Atomic_Add64 not implemented
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Sub64 --
 *
 *      Atomic read, subtract a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Sub64(Atomic_uint64 *var, // IN/OUT
             uint64 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_sub((_Atomic(uint64) *)&var->value, val);
#elif !defined VM_64BIT
   Atomic_ReadSub64(var, val); /* Return value is unused. */
#elif defined __GNUC__
#if defined VM_ARM_64
   _VMATOM_X(OP, 64, TRUE, &var->value, sub, val);
#else /* VM_X86_64 */
   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "lock; subq %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#endif
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   _InterlockedExchangeAdd64((__int64 *)&var->value, (__int64)-val);
#else
#error Atomic_Sub64 not implemented
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Inc64 --
 *
 *      Atomic read, increment, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Inc64(Atomic_uint64 *var) // IN/OUT
{
#if defined VM_ARM_64 || defined VM_ATOMIC_USE_C11
   Atomic_Add64(var, 1);
#elif !defined __x86_64__
   Atomic_ReadInc64(var);  /* Return value is unused. */
#elif defined __GNUC__
   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "lock; incq %0"
      : "+m" (var->value)
      :
      : "cc", "memory"
   );
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   _InterlockedIncrement64((__int64 *)&var->value);
#else
#error Atomic_Inc64 not implemented
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Dec64 --
 *
 *      Atomic read, decrement, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Dec64(Atomic_uint64 *var) // IN/OUT
{
#if defined VM_ARM_64 || defined VM_ATOMIC_USE_C11
   Atomic_Sub64(var, 1);
#elif !defined __x86_64__
   Atomic_ReadDec64(var);  /* Return value is unused. */
#elif defined __GNUC__
   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "lock; decq %0"
      : "+m" (var->value)
      :
      : "cc", "memory"
   );
#elif defined _MSC_VER
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   _InterlockedDecrement64((__int64 *)&var->value);
#else
#error Atomic_Dec64 not implemented
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadWrite64 --
 *
 *      Read followed by write
 *
 * Results:
 *      The value of the atomic variable before the write.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint64
Atomic_ReadWrite64(Atomic_uint64 *var, // IN/OUT
                   uint64 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   return atomic_exchange((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__ && defined __x86_64__
   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "xchgq %0, %1"
      : "=r" (val),
      "+m" (var->value)
      : "0" (val)
      : "memory"
   );
   return val;
#elif defined __GNUC__ && defined VM_ARM_64
   return _VMATOM_X(RW, 64, TRUE, &var->value, val);
#elif defined _MSC_VER && defined VM_64BIT
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   return _InterlockedExchange64((__int64 *)&var->value, (__int64)val);
#else
   uint64 oldVal;

   do {
      oldVal = var->value;
   } while (!Atomic_CMPXCHG64(var, oldVal, val));

   return oldVal;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Write64 --
 *
 *      Write
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Write64(Atomic_uint64 *var, // OUT
               uint64 val)         // IN
{
#if defined VMM || defined VM_ARM_64 || defined VMKERNEL || defined VMKERNEL_MODULE
   ASSERT((uintptr_t)var % 8 == 0);
#endif

#if defined VM_ATOMIC_USE_C11
   atomic_store((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__ && defined __x86_64__
   /*
    * There is no move instruction for 64-bit immediate to memory, so unless
    * the immediate value fits in 32-bit (i.e. can be sign-extended), GCC
    * breaks the assignment into two movl instructions.  The code below forces
    * GCC to load the immediate value into a register first.
    */

   __asm__ __volatile__(
      "movq %1, %0"
      : "=m" (var->value)
      : "r" (val)
   );
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(W, 64, &var->value, val);
#elif defined _MSC_VER && defined VM_64BIT
   /*
    * Microsoft docs guarantee "Simple reads and writes to properly aligned
    * 64-bit variables are atomic on 64-bit Windows."
    * http://msdn.microsoft.com/en-us/library/ms684122%28VS.85%29.aspx
    *
    * XXX Unconditionally verify that value is properly aligned. Bug 61315.
    */

   var->value = val;
#else
   (void)Atomic_ReadWrite64(var, val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Or64 --
 *
 *      Atomic read, bitwise OR with a 64-bit value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Or64(Atomic_uint64 *var, // IN/OUT
            uint64 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_or((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__ && defined __x86_64__
   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "lock; orq %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 64, TRUE, &var->value, orr, val);
#elif defined _MSC_VER && defined VM_64BIT
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   _InterlockedOr64((__int64 *)&var->value, (__int64)val);
#else
   uint64 oldVal;
   uint64 newVal;
   do {
      oldVal = var->value;
      newVal = oldVal | val;
   } while (!Atomic_CMPXCHG64(var, oldVal, newVal));
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_And64 --
 *
 *      Atomic read, bitwise AND with a 64-bit value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_And64(Atomic_uint64 *var, // IN/OUT
             uint64 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_and((_Atomic(uint64) *)&var->value, val);
#elif defined __GNUC__ && defined __x86_64__
   /* Checked against the AMD manual and GCC --hpreg */
   __asm__ __volatile__(
      "lock; andq %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 64, TRUE, &var->value, and, val);
#elif defined _MSC_VER && defined VM_64BIT
   ASSERT_ON_COMPILE(sizeof (__int64) == sizeof var->value);
   _InterlockedAnd64((__int64 *)&var->value, (__int64)val);
#else
   uint64 oldVal;
   uint64 newVal;
   do {
      oldVal = var->value;
      newVal = oldVal & val;
   } while (!Atomic_CMPXCHG64(var, oldVal, newVal));
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_SetBit64 --
 *
 *      Atomically set the bit 'bit' in var.  Bit must be between 0 and 63.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_SetBit64(Atomic_uint64 *var, // IN/OUT
                unsigned bit)       // IN
{
#if defined __x86_64__ && defined __GNUC__
   ASSERT(bit <= 63);
   __asm__ __volatile__(
      "lock; btsq %1, %0"
      : "+m" (var->value)
      : "ri" ((uint64)bit)
      : "cc", "memory"
   );
#else
   uint64 oldVal;
   uint64 newVal;
   ASSERT(bit <= 63);
   do {
      oldVal = var->value;
      newVal = oldVal | (CONST64U(1) << bit);
   } while (!Atomic_CMPXCHG64(var, oldVal, newVal));
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ClearBit64 --
 *
 *      Atomically clear the bit 'bit' in var.  Bit must be between 0 and 63.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_ClearBit64(Atomic_uint64 *var, // IN/OUT
                  unsigned bit)       // IN
{
#if defined __x86_64__ && defined __GNUC__
   ASSERT(bit <= 63);
   __asm__ __volatile__(
      "lock; btrq %1, %0"
      : "+m" (var->value)
      : "ri" ((uint64)bit)
      : "cc", "memory"
   );
#else
   uint64 oldVal;
   uint64 newVal;
   ASSERT(bit <= 63);
   do {
      oldVal = var->value;
      newVal = oldVal & ~(CONST64U(1) << bit);
   } while (!Atomic_CMPXCHG64(var, oldVal, newVal));
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_TestBit64 --
 *
 *      Read the bit 'bit' in var.  Bit must be between 0 and 63.
 *
 * Results:
 *      TRUE if the tested bit was set; else FALSE.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Atomic_TestBit64(Atomic_uint64 *var, // IN
                 unsigned bit)       // IN
{
   Bool out;
   ASSERT(bit <= 63);
#if defined __x86_64__ && defined __GNUC__
   __asm__ __volatile__(
      "btq %2, %1"
      IF_ASM_FLAG_OUTPUT("", "\n\t" "setc %0")
      : IF_ASM_FLAG_OUTPUT("=@ccc", "=rm") (out)
      : "m" (var->value),
        "rJ" ((uint64)bit)
      : "cc"
   );
#else
   out = (var->value & (CONST64U(1) << bit)) != 0;
#endif
   return out;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_TestSetBit64 --
 *
 *      Atomically test and set the bit 'bit' in var.
 *      Bit must be between 0 and 63.
 *
 * Results:
 *      TRUE if the tested bit was set; else FALSE.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Atomic_TestSetBit64(Atomic_uint64 *var, // IN/OUT
                    unsigned bit)       // IN
{
#if defined __x86_64__ && defined __GNUC__
   Bool out;
   ASSERT(bit <= 63);
   __asm__ __volatile__(
      "lock; btsq %2, %1"
      IF_ASM_FLAG_OUTPUT("", "\n\t" "setc %0")
      : IF_ASM_FLAG_OUTPUT("=@ccc", "=rm") (out),
        "+m" (var->value)
      : "rJ" ((uint64)bit)
      : "cc", "memory"
   );
   return out;
#else
   uint64 oldVal;
   uint64 mask;
   ASSERT(bit <= 63);
   mask = CONST64U(1) << bit;
   do {
      oldVal = var->value;
   } while (!Atomic_CMPXCHG64(var, oldVal, oldVal | mask));
   return (oldVal & mask) != 0;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Read16 --
 *
 *      Read and return.
 *
 * Results:
 *      The value of the atomic variable.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint16
Atomic_Read16(Atomic_uint16 const *var) // IN
{
   uint16 value;

#if defined VMM || defined VM_ARM_64 || defined VMKERNEL || \
    defined VMKERNEL_MODULE
   ASSERT((uintptr_t)var % 2 == 0);
#endif

#if defined VM_ATOMIC_USE_C11
   value = atomic_load((_Atomic(uint16) *)&var->value);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "movw %1, %0"
      : "=r" (value)
      : "m" (var->value)
   );
#elif defined __GNUC__ && defined VM_ARM_V7
   NOT_TESTED();

   __asm__ __volatile__(
      "ldrh %0, [%1]"
      : "=r" (value)
      : "r" (&var->value)
   );
#elif defined __GNUC__ && defined VM_ARM_64
   value = _VMATOM_X(R, 16, &var->value);
#else
   value = (uint16)AtomicUndefined(var);
#endif

   return value;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadWrite16 --
 *
 *      Read followed by write
 *
 * Results:
 *      The value of the atomic variable before the write.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint16
Atomic_ReadWrite16(Atomic_uint16 *var,  // IN/OUT:
                   uint16 val)          // IN:
{
#if defined VM_ATOMIC_USE_C11
   return atomic_exchange((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "xchgw %0, %1"
      : "=r" (val),
        "+m" (var->value)
      : "0" (val)
      : "memory"
   );
   return val;
#elif defined __GNUC__ && defined VM_ARM_V7
   uint16 retVal;
   uint16 res;

   NOT_TESTED();

   dmb();

   __asm__ __volatile__(
   "1: ldrexh %[retVal], [%[var]] \n\t"
      "strexh %[res], %[val], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [retVal] "=&r" (retVal), [res] "=&r" (res)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();

   return retVal;
#elif defined __GNUC__ && defined VM_ARM_64
   return _VMATOM_X(RW, 16, TRUE, &var->value, val);
#else
   return (uint16)AtomicUndefined(var + val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Write16 --
 *
 *      Write
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Write16(Atomic_uint16 *var,  // OUT:
               uint16 val)          // IN:
{
#if defined VMM || defined VM_ARM_64 || defined VMKERNEL || \
    defined VMKERNEL_MODULE
   ASSERT((uintptr_t)var % 2 == 0);
#endif

#if defined VM_ATOMIC_USE_C11
   atomic_store((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "movw %1, %0"
      : "=m" (var->value)
      : "r" (val)
   );
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(W, 16, &var->value, val);
#elif defined VM_ARM_32
   /*
    * Best left this way due to the intricacies of exclusive load/store
    * operations on legacy (32-bit) ARM.
    */
   Atomic_ReadWrite16(var, val);
#else
   AtomicUndefined(var + val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadIfEqualWrite16 --
 *
 *      Compare exchange: Read variable, if equal to oldVal, write newVal
 *
 * Results:
 *      The value that was compared against oldVal.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint16
Atomic_ReadIfEqualWrite16(Atomic_uint16 *var,   // IN/OUT
                          uint16 oldVal,        // IN
                          uint16 newVal)        // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_compare_exchange_strong(
      (_Atomic(uint16) *)&var->value, &oldVal, newVal);
   return oldVal;
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   uint16 val;

   __asm__ __volatile__(
      "lock; cmpxchgw %2, %1"
      : "=a" (val),
        "+m" (var->value)
      : "r" (newVal),
        "0" (oldVal)
      : "cc", "memory"
   );
   return val;
#elif defined __GNUC__ && defined VM_ARM_V7
   uint16 retVal;
   uint16 res;

   NOT_TESTED();

   dmb();

   __asm__ __volatile__(
   "1: ldrexh %[retVal], [%[var]] \n\t"
      "mov %[res], #0 \n\t"
      "teq %[retVal], %[oldVal] \n\t"
      "strexheq %[res], %[newVal], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [retVal] "=&r" (retVal), [res] "=&r" (res)
      : [var] "r" (&var->value), [oldVal] "r" (oldVal), [newVal] "r" (newVal)
      : "cc"
   );

   dmb();

   return retVal;
#elif defined __GNUC__ && defined VM_ARM_64
   return _VMATOM_X(RIFEQW, 16, TRUE, &var->value, oldVal, newVal);
#else
   return (uint16)AtomicUndefined(var + oldVal + newVal);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadAnd16 --
 *
 *      Atomic read (returned), bitwise AND with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint16
Atomic_ReadAnd16(Atomic_uint16 *var, // IN/OUT
                 uint16 val)         // IN
{
   uint16 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_and((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 16, TRUE, &var->value, and, val);
#else
   do {
      res = Atomic_Read16(var);
   } while (res != Atomic_ReadIfEqualWrite16(var, res, res & val));
#endif

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_And16 --
 *
 *      Atomic read, bitwise AND with a 16-bit value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_And16(Atomic_uint16 *var, // IN/OUT
             uint16 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_and((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "lock; andw %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#elif defined __GNUC__ && defined VM_ARM_V7
   uint16 res;
   uint16 tmp;

   NOT_TESTED();

   dmb();

   __asm__ __volatile__(
   "1: ldrexh %[tmp], [%[var]] \n\t"
      "and %[tmp], %[tmp], %[val] \n\t"
      "strexh %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 16, TRUE, &var->value, and, val);
#else
   AtomicUndefined(var + val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Or16 --
 *
 *      Atomic read, bitwise OR with a 16-bit value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Or16(Atomic_uint16 *var, // IN/OUT
            uint16 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_or((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "lock; orw %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#elif defined __GNUC__ && defined VM_ARM_V7
   uint16 res;
   uint16 tmp;

   NOT_TESTED();

   dmb();

   __asm__ __volatile__(
   "1: ldrexh %[tmp], [%[var]] \n\t"
      "orr %[tmp], %[tmp], %[val] \n\t"
      "strexh %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 16, TRUE, &var->value, orr, val);
#else
   AtomicUndefined(var + val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Xor16 --
 *
 *      Atomic read, bitwise XOR with a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Xor16(Atomic_uint16 *var, // IN/OUT
             uint16 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_xor((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "lock; xorw %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#elif defined __GNUC__ && defined VM_ARM_V7
   uint16 res;
   uint16 tmp;

   NOT_TESTED();

   dmb();

   __asm__ __volatile__(
   "1: ldrexh %[tmp], [%[var]] \n\t"
      "eor %[tmp], %[tmp], %[val] \n\t"
      "strexh %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 16, TRUE, &var->value, eor, val);
#else
   AtomicUndefined(var + val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Add16 --
 *
 *      Atomic read, add a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Add16(Atomic_uint16 *var, // IN/OUT
             uint16 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_add((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "lock; addw %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#elif defined __GNUC__ && defined VM_ARM_V7
   uint16 res;
   uint16 tmp;

   NOT_TESTED();

   dmb();

   __asm__ __volatile__(
   "1: ldrexh %[tmp], [%[var]] \n\t"
      "add %[tmp], %[tmp], %[val] \n\t"
      "strexh %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 16, TRUE, &var->value, add, val);
#else
   AtomicUndefined(var + val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Sub16 --
 *
 *      Atomic read, subtract a value, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Sub16(Atomic_uint16 *var, // IN/OUT
             uint16 val)         // IN
{
#if defined VM_ATOMIC_USE_C11
   atomic_fetch_sub((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "lock; subw %1, %0"
      : "+m" (var->value)
      : "re" (val)
      : "cc", "memory"
   );
#elif defined __GNUC__ && defined VM_ARM_V7
   uint16 res;
   uint16 tmp;

   NOT_TESTED();

   dmb();

   __asm__ __volatile__(
      "1: ldrexh %[tmp], [%[var]] \n\t"
      "sub %[tmp], %[tmp], %[val] \n\t"
      "strexh %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [res] "=&r" (res), [tmp] "=&r" (tmp)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();
#elif defined __GNUC__ && defined VM_ARM_64
   _VMATOM_X(OP, 16, TRUE, &var->value, sub, val);
#else
   AtomicUndefined(var + val);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Inc16 --
 *
 *      Atomic read, increment, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Inc16(Atomic_uint16 *var) // IN/OUT
{
#if defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "lock; incw %0"
      : "+m" (var->value)
      :
      : "cc", "memory"
   );
#else
   Atomic_Add16(var, 1);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_Dec16 --
 *
 *      Atomic read, decrement, write.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_Dec16(Atomic_uint16 *var) // IN/OUT
{
#if defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "lock; decw %0"
      : "+m" (var->value)
      :
      : "cc", "memory"
   );
#else
   Atomic_Sub16(var, 1);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ReadOr16 --
 *
 *      Atomic read (returned), bitwise OR with a value, write.
 *
 * Results:
 *      The value of the variable before the operation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint16
Atomic_ReadOr16(Atomic_uint16 *var, // IN/OUT
                uint16 val)         // IN
{
   uint16 res;

#if defined VM_ATOMIC_USE_C11
   res = atomic_fetch_or((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && defined VM_ARM_64
   res = _VMATOM_X(ROP, 16, TRUE, &var->value, orr, val);
#else
   do {
      res = var->value;
   } while (res != Atomic_ReadIfEqualWrite16(var, res, res | val));
#endif

   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * Atomic_ReadAdd16 --
 *
 *      Atomically adds a 16-bit integer to another
 *
 * Results:
 *      Returns the old value just prior to the addition
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static INLINE uint16
Atomic_ReadAdd16(Atomic_uint16 *var,  // IN/OUT
                 uint16 val)          // IN:
{
#if defined VM_ATOMIC_USE_C11
   return atomic_fetch_add((_Atomic(uint16) *)&var->value, val);
#elif defined __GNUC__ && (defined __x86_64__ || defined __i386__)
   __asm__ __volatile__(
      "lock; xaddw %0, %1"
      : "=r" (val),
        "+m" (var->value)
      : "0" (val)
      : "cc", "memory"
   );
   return val;
#elif defined __GNUC__ && defined VM_ARM_V7
   uint16 res;
   uint16 retVal;
   uint16 tmp;

   NOT_TESTED();

   dmb();

   __asm__ __volatile__(
      "1: ldrexh %[retVal], [%[var]] \n\t"
      "add %[tmp], %[val], %[retVal] \n\t"
      "strexh %[res], %[tmp], [%[var]] \n\t"
      "teq %[res], #0 \n\t"
      "bne 1b"
      : [tmp] "=&r" (tmp), [res] "=&r" (res), [retVal] "=&r" (retVal)
      : [var] "r" (&var->value), [val] "r" (val)
      : "cc"
   );

   dmb();

   return retVal;
#elif defined __GNUC__ && defined VM_ARM_64
   return _VMATOM_X(ROP, 16, TRUE, &var->value, add, val);
#else
   return (uint16)AtomicUndefined(var + val);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Atomic_ReadInc16 --
 *
 *      Atomically increments a 16-bit integer
 *
 * Results:
 *      Returns the old value just prior to incrementing
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static INLINE uint16
Atomic_ReadInc16(Atomic_uint16 *var) // IN/OUT
{
   return Atomic_ReadAdd16(var, 1);
}


/*
 *----------------------------------------------------------------------
 *
 * Atomic_ReadDec16 --
 *
 *      Atomically decrements a 16-bit integer
 *
 * Results:
 *      Returns the old value just prior to decrementing
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static INLINE uint16
Atomic_ReadDec16(Atomic_uint16 *var) // IN/OUT
{
   return Atomic_ReadAdd16(var, (uint16)-1);
}


/*
 * Template code for the Atomic_<name> type and its operators.
 *
 * The cast argument is an intermediate type cast to make some
 * compilers stop complaining about casting uint32 <-> void *,
 * even though we only do it in the 32-bit case so they are always
 * the same size.  So for val of type uint32, instead of
 * (void *)val, we have (void *)(uintptr_t)val.
 * The specific problem case is the Windows ddk compiler
 * (as used by the SVGA driver).  -- edward
 *
 * NOTE: See the comment in vm_assert.h for why we need UNUSED_TYPE in
 * AtomicAssertOnCompile(), and why we need to be very careful doing so.
 */

#define MAKE_ATOMIC_TYPE(name, size, in, out, cast)                           \
   typedef Atomic_uint ## size Atomic_ ## name;                               \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   AtomicAssertOnCompile ## name(void)                                        \
   {                                                                          \
      enum { AssertOnCompileMisused =    8 * sizeof (in) == size              \
                                      && 8 * sizeof (out) == size             \
                                      && 8 * sizeof (cast) == size            \
                                         ? 1 : -1 };                          \
      UNUSED_TYPE(typedef char AssertOnCompileFailed[AssertOnCompileMisused]);\
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE out                                                          \
   Atomic_Read ## name(Atomic_ ## name const *var)                            \
   {                                                                          \
      return (out)(cast)Atomic_Read ## size(var);                             \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   Atomic_Write ## name(Atomic_ ## name *var,                                 \
                        in val)                                               \
   {                                                                          \
      Atomic_Write ## size(var, (uint ## size)(cast)val);                     \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE out                                                          \
   Atomic_ReadWrite ## name(Atomic_ ## name *var,                             \
                            in val)                                           \
   {                                                                          \
      return (out)(cast)Atomic_ReadWrite ## size(var,                         \
                (uint ## size)(cast)val);                                     \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE out                                                          \
   Atomic_ReadIfEqualWrite ## name(Atomic_ ## name *var,                      \
                                   in oldVal,                                 \
                                   in newVal)                                 \
   {                                                                          \
      return (out)(cast)Atomic_ReadIfEqualWrite ## size(var,                  \
                (uint ## size)(cast)oldVal, (uint ## size)(cast)newVal);      \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   Atomic_And ## name(Atomic_ ## name *var,                                   \
                      in val)                                                 \
   {                                                                          \
      Atomic_And ## size(var, (uint ## size)(cast)val);                       \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   Atomic_Or ## name(Atomic_ ## name *var,                                    \
                     in val)                                                  \
   {                                                                          \
      Atomic_Or ## size(var, (uint ## size)(cast)val);                        \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   Atomic_Xor ## name(Atomic_ ## name *var,                                   \
                      in val)                                                 \
   {                                                                          \
      Atomic_Xor ## size(var, (uint ## size)(cast)val);                       \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   Atomic_Add ## name(Atomic_ ## name *var,                                   \
                      in val)                                                 \
   {                                                                          \
      Atomic_Add ## size(var, (uint ## size)(cast)val);                       \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   Atomic_Sub ## name(Atomic_ ## name *var,                                   \
                      in val)                                                 \
   {                                                                          \
      Atomic_Sub ## size(var, (uint ## size)(cast)val);                       \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   Atomic_Inc ## name(Atomic_ ## name *var)                                   \
   {                                                                          \
      Atomic_Inc ## size(var);                                                \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE void                                                         \
   Atomic_Dec ## name(Atomic_ ## name *var)                                   \
   {                                                                          \
      Atomic_Dec ## size(var);                                                \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE out                                                          \
   Atomic_ReadOr ## name(Atomic_ ## name *var,                                \
                         in val)                                              \
   {                                                                          \
      return (out)(cast)Atomic_ReadOr ## size(var, (uint ## size)(cast)val);  \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE out                                                          \
   Atomic_ReadAdd ## name(Atomic_ ## name *var,                               \
                          in val)                                             \
   {                                                                          \
      return (out)(cast)Atomic_ReadAdd ## size(var, (uint ## size)(cast)val); \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE out                                                          \
   Atomic_ReadInc ## name(Atomic_ ## name *var)                               \
   {                                                                          \
      return (out)(cast)Atomic_ReadInc ## size(var);                          \
   }                                                                          \
                                                                              \
                                                                              \
   static INLINE out                                                          \
   Atomic_ReadDec ## name(Atomic_ ## name *var)                               \
   {                                                                          \
      return (out)(cast)Atomic_ReadDec ## size(var);                          \
   }


/*
 * Since we use a macro to generate these definitions, it is hard to look for
 * them. So DO NOT REMOVE THIS COMMENT and keep it up-to-date. --hpreg
 *
 * Atomic_Ptr
 * Atomic_ReadPtr --
 * Atomic_WritePtr --
 * Atomic_ReadWritePtr --
 * Atomic_ReadIfEqualWritePtr --
 * Atomic_AndPtr --
 * Atomic_OrPtr --
 * Atomic_XorPtr --
 * Atomic_AddPtr --
 * Atomic_SubPtr --
 * Atomic_IncPtr --
 * Atomic_DecPtr --
 * Atomic_ReadOrPtr --
 * Atomic_ReadAddPtr --
 * Atomic_ReadIncPtr --
 * Atomic_ReadDecPtr --
 *
 * Atomic_Int
 * Atomic_ReadInt --
 * Atomic_WriteInt --
 * Atomic_ReadWriteInt --
 * Atomic_ReadIfEqualWriteInt --
 * Atomic_AndInt --
 * Atomic_OrInt --
 * Atomic_XorInt --
 * Atomic_AddInt --
 * Atomic_SubInt --
 * Atomic_IncInt --
 * Atomic_DecInt --
 * Atomic_ReadOrInt --
 * Atomic_ReadAddInt --
 * Atomic_ReadIncInt --
 * Atomic_ReadDecInt --
 *
 * Atomic_Bool
 * Atomic_ReadBool --
 * Atomic_WriteBool --
 * Atomic_ReadWriteBool --
 * Atomic_ReadIfEqualWriteBool --
 * Atomic_AndBool --
 * Atomic_OrBool --
 * Atomic_XorBool --
 * Atomic_AddBool --
 * Atomic_SubBool --
 * Atomic_IncBool --
 * Atomic_DecBool --
 * Atomic_ReadOrBool --
 * Atomic_ReadAddBool --
 * Atomic_ReadIncBool --
 * Atomic_ReadDecBool --
 */
#if defined VM_64BIT
MAKE_ATOMIC_TYPE(Ptr, 64, void const *, void *, uintptr_t)
#else
MAKE_ATOMIC_TYPE(Ptr, 32, void const *, void *, uintptr_t)
#endif
MAKE_ATOMIC_TYPE(Int, 32, int, int, int)
MAKE_ATOMIC_TYPE(Bool, 8, Bool, Bool, Bool)

/*
 * Define arbitrary sized bit vector to be used by
 * Atomic_TestSetBitVector and Atomic_TestClearBitVector.
 */
#define ATOMIC_BITVECTOR(varName, capacity) \
      Atomic_uint8 varName[CEILING(capacity, 8)]

/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_SetBitVector --
 *
 *      Atomically set the bit 'index' in bit vector var.
 *
 *      The index input value specifies which bit to modify and is 0-based.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_SetBitVector(Atomic_uint8 *var, // IN/OUT
                    unsigned index)    // IN
{
   Atomic_Or8(var + index / 8, 1 << index % 8);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_TestSetBitVector --
 *
 *      Atomically test and set the bit 'index' in bit vector var.
 *
 *      The index input value specifies which bit to modify and is 0-based.
 *
 * Results:
 *      Returns the value of the bit before modification.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Atomic_TestSetBitVector(Atomic_uint8 *var, // IN/OUT
                        unsigned index)    // IN
{
#if defined __x86_64__ && defined __GNUC__
   Bool bit;
   __asm__ __volatile__(
      "lock; bts %2, %1"
      IF_ASM_FLAG_OUTPUT("", "\n\t" "setc %0")
      : IF_ASM_FLAG_OUTPUT("=@ccc", "=qQm") (bit),
        "+m" (var->value)
      : "rI" (index)
      : "cc", "memory"
   );
   return bit;
#else
   uint8 bit = 1 << index % 8;
   return (Atomic_ReadOr8(var + index / 8, bit) & bit) != 0;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_ClearBitVector --
 *
 *      Atomically clear the bit 'index' in bit vector var.
 *
 *      The index input value specifies which bit to modify and is 0-based.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
Atomic_ClearBitVector(Atomic_uint8 *var, // IN/OUT
                      unsigned index)    // IN
{
   Atomic_And8(var + index / 8, ~(1 << index % 8));
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_TestClearBitVector --
 *
 *      Atomically test and clear the bit 'index' in bit vector var.
 *
 *      The index input value specifies which bit to modify and is 0-based.
 *
 * Results:
 *      Returns the value of the bit before modification.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Atomic_TestClearBitVector(Atomic_uint8 *var, // IN/OUT
                          unsigned index)    // IN
{
#if defined __x86_64__ && defined __GNUC__
   Bool bit;
   __asm__ __volatile__(
      "lock; btr %2, %1"
      IF_ASM_FLAG_OUTPUT("", "\n\t" "setc %0")
      : IF_ASM_FLAG_OUTPUT("=@ccc", "=qQm") (bit),
        "+m" (var->value)
      : "rI" (index)
      : "cc", "memory"
   );
   return bit;
#else
   uint8 bit = 1 << index % 8;
   return (Atomic_ReadAnd8(var + index / 8, ~bit) & bit) != 0;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Atomic_TestBitVector --
 *
 *      Test the bit 'index' (zero-based) in bit vector var.
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
Atomic_TestBitVector(const Atomic_uint8 *var, // IN
                     unsigned index)          // IN
{
   uint8 bit = 1 << index % 8;
   return (Atomic_Read8(var + index / 8) & bit) != 0;
}


#ifdef VM_ARM_64
#   include "vm_atomic_arm64_end.h"
#endif

#if defined __cplusplus
}  // extern "C"
#endif

#endif // ifndef _ATOMIC_H_
0707010000012D000081A400000000000000000000000168225505000056CD000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_atomic_arm64_begin.h    /*********************************************************
 * Copyright (C) 2017-2018,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vm_atomic_arm64_begin.h --
 *
 *      Define private macros used to implement ARM64 atomic operations.
 */

#if !(   defined _ATOMIC_H_                                                   \
      || defined _VM_UNINTERRUPTIBLE_H_                                       \
      || defined _WAIT_UNTIL_LIKELY_H_)
#   error "Only files which implement an atomic API can include this file!"
#endif

#include "vm_basic_asm_arm64.h"

/*
 * Atomic_LsePresent should be set to 1 for CPUs that have the LSE extenstion
 * and where the atomic instructions are known to have a performance benefit.
 * Seemingly, on some low-end chips (CA55) there may not be a benefit.
 *
 * Not every operation can be performed using a single-instruction atomic -
 * LSE doesn't cover all kinds of logical/arithmetic operations. For example,
 * there's an ldeor instruction, but not an ldorr. For cases, where there is no
 * combined instruction that atomically performs the load/store and the ALU
 * operation, we fall back to CAS or to LL/SC. On some uarches - e.g. Neoverse
 * N1 - CAS shows better behavior during heavy contention than LL/SC. LL/SC,
 * though, remains the safest option. Atomic_PreferCasForOps controls this.
 */

/*
 * The silliness with _VMATOM_HAVE_LSE_DEFINED is necessary because this
 * could be included multiple times (via vm_atomic and vm_atomic_relaxed).
 */
#ifndef _VMATOM_HAVE_LSE_DEFINED
typedef struct  {
   Bool LsePresent;
#ifndef VMKERNEL
   Bool ProbedForLse;
#endif
   Bool PreferCasForOps;
} Atomic_ConfigParams;

#if defined(VMX86_SERVER) || defined(VMKBOOT)
/*
 * When building UW code for ESXi, Atomic_Config a weak symbol.
 * When building for kernel mode, Atomic_Config is exported by
 * bora/vmkernel/lib/arm64/atomic.c
 */
#ifndef VMKERNEL
#pragma weak Atomic_Config
Atomic_ConfigParams Atomic_Config;
#else
extern Atomic_ConfigParams Atomic_Config;
#endif

static INLINE Bool
Atomic_HaveLse(void)
{
#ifndef VMKERNEL
   /*
    * Can't just include sys/auxv.h, unfortunately.
    */
   extern uint64 getauxval(uint64 type);
#define _VMATOM_AT_ESXI_HWCAP                    2000
#define _VMATOM_AT_ESXI_HWCAP_HAVE_LSE           (1 << 0)
#define _VMATOM_AT_ESXI_HWCAP_PREFER_CAS_FOR_OPS (1 << 1)

   if (!Atomic_Config.ProbedForLse) {
      uint64 cap = getauxval(_VMATOM_AT_ESXI_HWCAP);
      Atomic_Config.LsePresent = (cap &_VMATOM_AT_ESXI_HWCAP_HAVE_LSE) != 0;
      Atomic_Config.PreferCasForOps = Atomic_Config.LsePresent &&
         (cap & _VMATOM_AT_ESXI_HWCAP_PREFER_CAS_FOR_OPS) != 0;
      SMP_W_BARRIER_W();
      Atomic_Config.ProbedForLse = TRUE;
   }
#undef _VMATOM_AT_ESXI_HWCAP
#undef _VMATOM_AT_ESXI_HWCAP_HAVE_LSE
#undef _VMATOM_AT_ESXI_HWCAP_PREFER_CAS_FOR_OPS
#endif

   return Atomic_Config.LsePresent;
}

static INLINE Bool
Atomic_PreferCasForOps(void) {
   return Atomic_Config.PreferCasForOps;
}
#else /* !VMX86_SERVER && !VMKBOOT */
/*
 * Not building for ESXi? Assume no LSE.
 */
#define Atomic_PreferCasForOps() FALSE
#define Atomic_HaveLse()         FALSE
#endif
#define _VMATOM_HAVE_LSE_DEFINED
#endif /* _VMATOM_HAVE_LSE_DEFINED */

#define _VMATOM_LSE_HAVE(x)  _VMATOM_LSE_HAVE_##x
#define _VMATOM_LSE_HAVE_add 1
#define _VMATOM_LSE_HAVE_sub 0
#define _VMATOM_LSE_HAVE_eor 1
#define _VMATOM_LSE_HAVE_orr 0
#define _VMATOM_LSE_HAVE_and 0

#define _VMATOM_PREFER_LSE(op) ((_VMATOM_LSE_HAVE(op) && Atomic_HaveLse()) || Atomic_PreferCasForOps())

/*                      bit size, instruction suffix, register prefix, extend suffix */
#define _VMATOM_SIZE_8         8,                  b,               w,             b
#define _VMATOM_SIZE_16       16,                  h,               w,             h
#define _VMATOM_SIZE_32       32,                   ,               w,             w
#define _VMATOM_SIZE_64       64,                   ,               x,             x
/*
 * Expand 'size' (a _VMATOM_SIZE_*) into its 4 components, then expand
 * 'snippet' (a _VMATOM_SNIPPET_*)
 */
#define _VMATOM_X2(snippet, size, ...) snippet(size, __VA_ARGS__)
/* Prepend a prefix to 'shortSnippet' and 'shortSize'. */
#define _VMATOM_X(shortSnippet, shortSize, ...)                               \
   _VMATOM_X2(_VMATOM_SNIPPET_##shortSnippet, _VMATOM_SIZE_##shortSize,       \
              __VA_ARGS__)

/* Read relaxed (returned). */
#define _VMATOM_SNIPPET_R_NF(bs, is, rp, es, atm) ({                          \
   uint##bs _val;                                                             \
                                                                              \
   /* ldr is atomic if and only if 'atm' is aligned on #bs bits. */           \
   __asm__ __volatile__(                                                      \
      "ldr"#is" %"#rp"0, %1                                              \n\t"\
      : "=r" (_val)                                                           \
      : "m" (*atm)                                                            \
   );                                                                         \
                                                                              \
   _val;                                                                      \
})
#define _VMATOM_SNIPPET_R _VMATOM_SNIPPET_R_NF

/* Read acquire/seq_cst (returned). */
#define _VMATOM_SNIPPET_R_SC(bs, is, rp, es, atm) ({                          \
   uint##bs _val;                                                             \
                                                                              \
   /* ldar is atomic if and only if 'atm' is aligned on #bs bits. */          \
   __asm__ __volatile__(                                                      \
      "ldar"#is" %"#rp"0, %1                                             \n\t"\
      : "=r" (_val)                                                           \
      : "Q" (*atm)                                                            \
   );                                                                         \
                                                                              \
   _val;                                                                      \
})

/* Write relaxed. */
#define _VMATOM_SNIPPET_W_NF(bs, is, rp, es, atm, val) ({                     \
   /*                                                                         \
    * str is atomic if and only if 'atm' is aligned on #bs bits.              \
    *                                                                         \
    * Clearing the exclusive monitor is not required. The local monitor is    \
    * cleared on any exception return, and the global monitor (as per B2.10.2,\
    * ARM DDI 0487A.k) is cleared by a successful write.                      \
    */                                                                        \
   __asm__ __volatile__(                                                      \
      "str"#is" %"#rp"1, %0                                              \n\t"\
      : "=m" (*atm)                                                           \
      : "r" (val)                                                             \
   );                                                                         \
})
#define _VMATOM_SNIPPET_W _VMATOM_SNIPPET_W_NF

/* Write release/seq_cst. */
#define _VMATOM_SNIPPET_W_SC(bs, is, rp, es, atm, val) ({                     \
   /*                                                                         \
    * stlr is atomic if and only if 'atm' is aligned on #bs bits.             \
    *                                                                         \
    * Clearing the exclusive monitor is not required. The local monitor is    \
    * cleared on any exception return, and the global monitor (as per B2.10.2,\
    * ARM DDI 0487A.k) is cleared by a successful write.                      \
    */                                                                        \
   __asm__ __volatile__(                                                      \
      "stlr"#is" %"#rp"1, %0                                             \n\t"\
      : "=Q" (*atm)                                                           \
      : "r" (val)                                                             \
   );                                                                         \
})

/*
 * Since on x86, some atomic operations are using LOCK semantics, assumptions
 * have been made about the ordering these operations imply on surrounding
 * code. As a result, on arm64 we have to provide these same guarantees.
 * We do this by making use of DMB barriers both before and after the atomic
 * ldrx/strx sequences.
 */
#define _VMATOM_FENCE(fenced)                                                 \
   if (fenced) {                                                              \
      SMP_RW_BARRIER_RW();                                                    \
   }

/* Read (not returned), op with modval, write. */
#define _VMATOM_SNIPPET_OP(bs, is, rp, es, fenced, atm, op, modval) ({        \
   uint##bs _newval;                                                          \
                                                                              \
   _VMATOM_FENCE(fenced);                                                     \
   if (_VMATOM_PREFER_LSE(op)) {                                              \
      if (_VMATOM_LSE_HAVE(op)) {                                             \
         __asm__ __volatile__(                                                \
            ".arch armv8.2-a                                             \n\t"\
            "st" #op #is" %"#rp"1, %0                                    \n\t"\
            : "+Q" (*atm)                                                     \
            : "r" (modval)                                                    \
         );                                                                   \
      } else {                                                                \
         uint##bs _oldval;                                                    \
         uint##bs _clobberedval;                                              \
         __asm__ __volatile__(                                                \
            ".arch armv8.2-a                                             \n\t"\
            "   ldr"#is" %"#rp"1, %3                                     \n\t"\
            "1: mov      %"#rp"0, %"#rp"1                                \n\t"\
            "  "#op"     %"#rp"2, %"#rp"0, %"#rp"4                       \n\t"\
            "   cas"#is" %"#rp"1, %"#rp"2, %3                            \n\t"\
            "   cmp      %"#rp"0, %"#rp"1, uxt"#es"                      \n\t"\
            "   b.ne     1b                                              \n\t"\
            : "=&r" (_oldval),                                                \
              "=&r" (_clobberedval),                                          \
              "=&r" (_newval),                                                \
              "+Q" (*atm)                                                     \
            : "r" (modval)                                                    \
            : "cc"                                                            \
         );                                                                   \
      }                                                                       \
   } else {                                                                   \
      uint32 _failed;                                                         \
      __asm__ __volatile__(                                                   \
         "1: ldxr"#is" %"#rp"0, %2                                       \n\t"\
         "  "#op"      %"#rp"0, %"#rp"0, %"#rp"3                         \n\t"\
         "   stxr"#is" %w1    , %"#rp"0, %2                              \n\t"\
         "   cbnz      %w1    , 1b                                       \n\t"\
         : "=&r" (_newval),                                                   \
           "=&r" (_failed),                                                   \
           "+Q" (*atm)                                                        \
         : "r" (modval)                                                       \
      );                                                                      \
   }                                                                          \
   _VMATOM_FENCE(fenced);                                                     \
})

/* Read (returned), op with modval, write. */
#define _VMATOM_SNIPPET_ROP(bs, is, rp, es, fenced, atm, op, modval) ({       \
   uint##bs _newval;                                                          \
   uint##bs _oldval;                                                          \
                                                                              \
   _VMATOM_FENCE(fenced);                                                     \
   if (_VMATOM_PREFER_LSE(op)) {                                              \
      if (_VMATOM_LSE_HAVE(op)) {                                             \
         __asm__ __volatile__(                                                \
            ".arch armv8.2-a                                             \n\t"\
            "ld" #op #is" %"#rp"2, %"#rp"0, %1                           \n\t"\
            : "=r" (_oldval),                                                 \
              "+Q" (*atm)                                                     \
            : "r" (modval)                                                    \
         );                                                                   \
      } else {                                                                \
         uint##bs _clobberedval;                                              \
         __asm__ __volatile__(                                                \
            ".arch armv8.2-a                                             \n\t"\
            "   ldr"#is"  %"#rp"1, %3                                    \n\t"\
            "1: mov      %"#rp"0, %"#rp"1                                \n\t"\
            "  "#op"     %"#rp"2, %"#rp"0, %"#rp"4                       \n\t"\
            "   cas"#is" %"#rp"1, %"#rp"2, %3                            \n\t"\
            "   cmp      %"#rp"0, %"#rp"1, uxt"#es"                      \n\t"\
            "   b.ne     1b                                              \n\t"\
            : "=&r" (_oldval),                                                \
              "=&r" (_clobberedval),                                          \
              "=&r" (_newval),                                                \
              "+Q" (*atm)                                                     \
            : "r" (modval)                                                    \
            : "cc"                                                            \
         );                                                                   \
      }                                                                       \
   } else {                                                                   \
      uint32 _failed;                                                         \
      __asm__ __volatile__(                                                   \
         "1: ldxr"#is" %"#rp"0, %3                                       \n\t"\
         "  "#op"      %"#rp"1, %"#rp"0, %"#rp"4                         \n\t"\
         "   stxr"#is" %w2    , %"#rp"1, %3                              \n\t"\
         "   cbnz      %w2    , 1b                                       \n\t"\
         : "=&r" (_oldval),                                                   \
           "=&r" (_newval),                                                   \
           "=&r" (_failed),                                                   \
           "+Q" (*atm)                                                        \
         : "r" (modval)                                                       \
      );                                                                      \
   }                                                                          \
   _VMATOM_FENCE(fenced);                                                     \
                                                                              \
   _oldval;                                                                   \
})

/* Read (returned), write. */
#define _VMATOM_SNIPPET_RW(bs, is, rp, es, fenced, atm, val) ({               \
   uint##bs _oldval;                                                          \
                                                                              \
   _VMATOM_FENCE(fenced);                                                     \
   if (Atomic_HaveLse()) {                                                    \
      __asm__ __volatile__(                                                   \
         ".arch armv8.2-a                                                \n\t"\
         "swp"#is" %"#rp"2, %"#rp"0, %1                                  \n\t"\
         : "=r" (_oldval),                                                    \
           "+Q" (*atm)                                                        \
         : "r" (val)                                                          \
      );                                                                      \
   } else {                                                                   \
      uint32 _failed;                                                         \
      __asm__ __volatile__(                                                   \
         "1: ldxr"#is" %"#rp"0, %2                                       \n\t"\
         "   stxr"#is" %w1    , %"#rp"3, %2                              \n\t"\
         "   cbnz      %w1    , 1b                                       \n\t"\
         : "=&r" (_oldval),                                                   \
           "=&r" (_failed),                                                   \
           "+Q" (*atm)                                                        \
         : "r" (val)                                                          \
      );                                                                      \
   }                                                                          \
   _VMATOM_FENCE(fenced);                                                     \
                                                                              \
   _oldval;                                                                   \
})

/* Read (returned), if equal to old then write new. */
#define _VMATOM_SNIPPET_RIFEQW(bs, is, rp, es, fenced, atm, old, new) ({      \
   uint##bs _oldval;                                                          \
                                                                              \
   _VMATOM_FENCE(fenced);                                                     \
   if (Atomic_HaveLse()) {                                                    \
      __asm__ __volatile__(                                                   \
         ".arch armv8.2-a                                                \n\t"\
         "cas"#is" %"#rp"0, %"#rp"2, %1                                  \n\t"\
         : "=r" (_oldval),                                                    \
           "+Q" (*atm)                                                        \
         : "r" (new), "0" (old)                                               \
      );                                                                      \
   } else {                                                                   \
      uint32 _failed;                                                         \
      __asm__ __volatile__(                                                   \
         "1: ldxr"#is" %"#rp"0, %2                                       \n\t"\
         "   cmp       %"#rp"0, %"#rp"3, uxt"#es"                        \n\t"\
         "   b.ne      2f                                                \n\t"\
         "   stxr"#is" %w1    , %"#rp"4, %2                              \n\t"\
         "   cbnz      %w1    , 1b                                       \n\t"\
         "2:                                                             \n\t"\
         : "=&r" (_oldval),                                                   \
           "=&r" (_failed),                                                   \
           "+Q" (*atm)                                                        \
         : "r" (old),                                                         \
           "r" (new)                                                          \
         : "cc"                                                               \
      );                                                                      \
   }                                                                          \
   _VMATOM_FENCE(fenced);                                                     \
                                                                              \
   _oldval;                                                                   \
})
   0707010000012E000081A40000000000000000000000016822550500000719000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_atomic_arm64_end.h  /*********************************************************
 * Copyright (C) 2017,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vm_atomic_arm64_end.h --
 *
 *      Undefine private macros used to implement ARM64 atomic operations.
 */

#if !defined _VMATOM_X
#   error "vm_atomic_arm64_begin.h must be included before this file!"
#endif

/* Undefine all the private macros we previously defined. */
#undef _VMATOM_LSE_HAVE
#undef _VMATOM_LSE_HAVE_add
#undef _VMATOM_LSE_HAVE_sub
#undef _VMATOM_LSE_HAVE_eor
#undef _VMATOM_LSE_HAVE_orr
#undef _VMATOM_LSE_HAVE_and
#undef _VMATOM_SIZE_8
#undef _VMATOM_SIZE_16
#undef _VMATOM_SIZE_32
#undef _VMATOM_SIZE_64
#undef _VMATOM_X2
#undef _VMATOM_X
#undef _VMATOM_SNIPPET_R_NF
#undef _VMATOM_SNIPPET_R
#undef _VMATOM_SNIPPET_R_SC
#undef _VMATOM_SNIPPET_W_NF
#undef _VMATOM_SNIPPET_W
#undef _VMATOM_SNIPPET_W_SC
#undef _VMATOM_FENCE
#undef _VMATOM_SNIPPET_OP
#undef _VMATOM_SNIPPET_ROP
#undef _VMATOM_SNIPPET_RW
#undef _VMATOM_SNIPPET_RIFEQW
#undef _VMATOM_PREFER_LSE
#undef _VMATOM_AT_HWCAP_LSE
#undef _VMATOM_AT_HWCAP
   0707010000012F000081A400000000000000000000000168225505000085B9000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_basic_asm.h /*********************************************************
 * Copyright (c) 2003-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vm_basic_asm.h --
 *
 *      Basic asm macros.
 */

#ifndef _VM_BASIC_ASM_H_
#define _VM_BASIC_ASM_H_

#define INCLUDE_ALLOW_USERLEVEL

#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
#include "includeCheck.h"

#if defined _MSC_VER && !defined BORA_NO_WIN32_INTRINS
#pragma warning(push)
#pragma warning(disable : 4255)      // disable no-prototype() to (void) warning
#include <intrin.h>
#pragma warning(pop)
#endif

#include "vm_basic_types.h"

#if defined VM_X86_64
#include "vm_basic_asm_x86_common.h"
#include "vm_basic_asm_x86_64.h"
#elif defined VM_X86_32
#include "vm_basic_asm_x86_common.h"
#include "vm_basic_asm_x86.h"
#elif defined VM_ARM_32
#include "vm_basic_asm_arm32.h"
#define MUL64_NO_ASM 1
#include "mul64.h"
#elif defined VM_ARM_64
#include "vm_basic_asm_arm64.h"
#ifdef VMKERNEL
#include "vmk_arm_mode.h"
#endif
#else
#define MUL64_NO_ASM 1
#include "mul64.h"
#endif

#if defined _M_ARM64EC || defined _M_ARM64
#include "vm_assert.h"
#define MUL64_NO_ASM 1
#include "mul64.h"
#endif

#if defined __cplusplus
extern "C" {
#endif


/*
 * Locate most and least significant bit set functions. Use our own name
 * space to avoid namespace collisions. The new names follow a pattern,
 * <prefix><size><option>, where:
 *
 * <prefix> is [lm]ssb (least/most significant bit set)
 * <size> is size of the argument: 32 (32-bit), 64 (64-bit) or Ptr (pointer)
 * <option> is for alternative versions of the functions
 *
 * NAME        FUNCTION                    BITS     FUNC(0)
 *-----        --------                    ----     -------
 * lssb32_0    LSB set (uint32)            0..31    -1
 * mssb32_0    MSB set (uint32)            0..31    -1
 * lssb64_0    LSB set (uint64)            0..63    -1
 * mssb64_0    MSB set (uint64)            0..63    -1
 * lssbPtr_0   LSB set (uintptr_t;32-bit)  0..31    -1
 * lssbPtr_0   LSB set (uintptr_t;64-bit)  0..63    -1
 * mssbPtr_0   MSB set (uintptr_t;32-bit)  0..31    -1
 * mssbPtr_0   MSB set (uintptr_t;64-bit)  0..63    -1
 * lssb32      LSB set (uint32)            1..32    0
 * mssb32      MSB set (uint32)            1..32    0
 * lssb64      LSB set (uint64)            1..64    0
 * mssb64      MSB set (uint64)            1..64    0
 * lssbPtr     LSB set (uintptr_t;32-bit)  1..32    0
 * lssbPtr     LSB set (uintptr_t;64-bit)  1..64    0
 * mssbPtr     MSB set (uintptr_t;32-bit)  1..32    0
 * mssbPtr     MSB set (uintptr_t;64-bit)  1..64    0
 */

#if defined(_MSC_VER) && !defined(__clang__) // Clang defines _MSC_VER on Windows
static inline int
lssb32_0(const uint32 value)
{
   unsigned long idx;
   unsigned char ret;

   if (UNLIKELY(value == 0)) {
      return -1;
   }
   ret = _BitScanForward(&idx, (unsigned long)value);
#ifdef __analysis_assume
   __analysis_assume(ret != 0);
#endif

#pragma warning(suppress: 6001 6102) // Suppress bogus complaint that idx may be uninitialized in error case
   return idx;
}

static inline int
mssb32_0(const uint32 value)
{
   unsigned long idx;
   unsigned char ret;

   if (UNLIKELY(value == 0)) {
      return -1;
   }
   ret = _BitScanReverse(&idx, (unsigned long)value);
#ifdef __analysis_assume
   __analysis_assume(ret != 0);
#endif

#pragma warning(suppress: 6001 6102) // Suppress bogus complaint that idx may be uninitialized in error case
   return idx;
}

static inline int
lssb64_0(const uint64 value)
{
   if (UNLIKELY(value == 0)) {
      return -1;
   } else {
#ifdef VM_X86_64
      unsigned long idx;
      unsigned char ret;

      ret = _BitScanForward64(&idx, (unsigned __int64)value);
#ifdef __analysis_assume
      __analysis_assume(ret != 0);
#endif

#pragma warning(suppress: 6001 6102) // Suppress bogus complaint that idx may be uninitialized in error case
      return idx;
#else
      /* The coding was chosen to minimize conditionals and operations */
      int lowFirstBit = lssb32_0((uint32) value);
      if (lowFirstBit == -1) {
         lowFirstBit = lssb32_0((uint32) (value >> 32));
         if (lowFirstBit != -1) {
            return lowFirstBit + 32;
         }
      }
      return lowFirstBit;
#endif
   }
}

static inline int
mssb64_0(const uint64 value)
{
   if (UNLIKELY(value == 0)) {
      return -1;
   } else {
#ifdef VM_X86_64
      unsigned long idx;
      unsigned char ret;

      ret = _BitScanReverse64(&idx, (unsigned __int64)value);
#ifdef __analysis_assume
      __analysis_assume(ret != 0);
#endif

#pragma warning(suppress: 6001 6102) // Suppress bogus complaint that idx may be uninitialized in error case
      return idx;
#else
      /* The coding was chosen to minimize conditionals and operations */
      if (value > 0xFFFFFFFFULL) {
         return 32 + mssb32_0((uint32) (value >> 32));
      }
      return mssb32_0((uint32) value);
#endif
   }
}
#endif

#if defined __GNUC__ || defined __clang__

#ifdef VM_X86_ANY
#define USE_ARCH_X86_CUSTOM
#endif

/* **********************************************************
 *  GCC's intrinsics for the lssb and mssb family produce sub-optimal code,
 *  so we use inline assembly to improve matters.  However, GCC cannot
 *  propagate constants through inline assembly, so we help GCC out by
 *  allowing it to use its intrinsics for compile-time constant values.
 *  Some day, GCC will make better code and these can collapse to intrinsics.
 *
 *  For example, in Decoder_AddressSize, inlined into VVT_GetVTInstrInfo:
 *  __builtin_ffs(a) compiles to:
 *  mov   $0xffffffff, %esi
 *  bsf   %eax, %eax
 *  cmovz %esi, %eax
 *  sub   $0x1, %eax
 *  and   $0x7, %eax
 *
 *  While the code below compiles to:
 *  bsf   %eax, %eax
 *  sub   $0x1, %eax
 *
 *  Ideally, GCC should have recognized non-zero input in the first case.
 *  Other instances of the intrinsic produce code like
 *  sub $1, %eax; add $1, %eax; clts
 * **********************************************************
 */

static inline int
lssb32_0(uint32 v)
{
   int value = (int)v;
#ifdef USE_ARCH_X86_CUSTOM
   if (!__builtin_constant_p(value)) {
      if (UNLIKELY(value == 0)) {
         return -1;
      } else {
         int pos;
         __asm__ ("bsfl %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
         return pos;
      }
   }
#endif
   return __builtin_ffs(value) - 1;
}

static inline int
mssb32_0(uint32 value)
{
   /*
    * We must keep the UNLIKELY(...) outside the #if defined ...
    * because __builtin_clz(0) is undefined according to gcc's
    * documentation.
    */
   if (UNLIKELY(value == 0)) {
      return -1;
   } else {
      int pos;
#ifdef USE_ARCH_X86_CUSTOM
      if (!__builtin_constant_p(value)) {
         __asm__ ("bsrl %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
         return pos;
      }
#endif
      pos = 32 - __builtin_clz(value) - 1;
      return pos;
   }
}

static inline int
lssb64_0(const uint64 v)
{
   int64 value = (int64)v;

#ifdef USE_ARCH_X86_CUSTOM
   if (!__builtin_constant_p(value)) {
      if (UNLIKELY(value == 0)) {
         return -1;
      } else {
         intptr_t pos;
#ifdef VM_X86_64
         __asm__ ("bsf %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
#else
         /* The coding was chosen to minimize conditionals and operations */
         pos = lssb32_0((uint32) value);
         if (pos == -1) {
            pos = lssb32_0((uint32) (value >> 32));
            if (pos != -1) {
               return pos + 32;
            }
         }
#endif
         return pos;
      }
   }
#endif
   return __builtin_ffsll(value) - 1;
}


static inline int
mssb64_0(const uint64 value)
{
   if (UNLIKELY(value == 0)) {
      return -1;
   } else {
      intptr_t pos;

#ifdef USE_ARCH_X86_CUSTOM
#ifdef VM_X86_64
      __asm__ ("bsr %1, %0\n" : "=r" (pos) : "rm" (value) : "cc");
#else
      /* The coding was chosen to minimize conditionals and operations */
      if (value > 0xFFFFFFFFULL) {
         pos = 32 + mssb32_0((uint32) (value >> 32));
      } else {
         pos = mssb32_0((uint32) value);
      }
#endif
#else
      pos = 64 - __builtin_clzll(value) - 1;
#endif

      return pos;
   }
}

#ifdef USE_ARCH_X86_CUSTOM
#undef USE_ARCH_X86_CUSTOM
#endif

#endif // __GNUC__

static inline int
lssbPtr_0(const uintptr_t value)
{
#ifdef VM_64BIT
   return lssb64_0((uint64) value);
#else
   return lssb32_0((uint32) value);
#endif
}

static inline unsigned
lssbPtr(const uintptr_t value)
{
   return (unsigned)lssbPtr_0(value) + 1;
}

static inline int
mssbPtr_0(const uintptr_t value)
{
#ifdef VM_64BIT
   return mssb64_0((uint64) value);
#else
   return mssb32_0((uint32) value);
#endif
}

static inline unsigned
mssbPtr(const uintptr_t value)
{
   return (unsigned)mssbPtr_0(value) + 1;
}

static inline unsigned
lssb32(const uint32 value)
{
   return (unsigned)lssb32_0(value) + 1;
}

static inline unsigned
mssb32(const uint32 value)
{
   return (unsigned)mssb32_0(value) + 1;
}

static inline unsigned
lssb64(const uint64 value)
{
   return (unsigned)lssb64_0(value) + 1;
}

static inline unsigned
mssb64(const uint64 value)
{
   return (unsigned)mssb64_0(value) + 1;
}


/*
 *-----------------------------------------------------------------------------
 *
 * uint16set --
 *
 *      memset a given address with an uint16 value, count times.
 *
 * Results:
 *      Pointer to filled memory range.
 *
 * Side effects:
 *      As with memset.
 *
 *-----------------------------------------------------------------------------
 */

static inline void *
uint16set(void *dst,    // OUT
          uint16 val,   // IN
          size_t count) // IN
{
#if defined __GNUC__ && defined VM_ARM_32
   void *tmpDst = dst;

   __asm__ __volatile__(
      "cmp     %1, #0\n\t"
      "beq     2f\n"
      "1:\n\t"
      "strh    %2, [%0], #2\n\t"
      "subs    %1, %1, #1\n\t"
      "bne     1b\n"
      "2:"
      : "+r" (tmpDst), "+r" (count)
      : "r" (val)
      : "cc", "memory");
#elif defined __GNUC__ && defined VM_ARM_64
   void   *tmpDst = dst;
   uint64  tmpVal = 0;

   if (count == 0) {
      return dst;
   }

   __asm__ __volatile__(
      "cbz     %3, 1f\n\t"

      // Copy 16 bits twice...
      "bfm     %2, %3, #0, #15\n\t"
      "lsl     %2, %2, #16\n\t"
      "bfm     %2, %3, #0, #15\n\t"

      // Copy 32 bits from the bottom of the reg. to the top...
      "lsl     %2, %2, #32\n\t"
      "bfm     %2, %2, #32, #63\n"

      // Copy into dst 8 bytes (4 uint16s) at a time
      "1:\t"
      "cmp     %1, #4\n\t"
      "b.lo    2f\n\t"
      "str     %2, [%0], #8\n\t"
      "sub     %1, %1, #4\n\t"
      "b       1b\n"

      // Copy into dst 4 bytes at a time
      "2:\t"
      "cmp     %1, #2\n\t"
      "b.lo    3f\n\t"
      "str     %w2, [%0], #4\n\t"
      "sub     %1, %1, #2\n\t"
      "b       2b\n"

      // We have 1 or zero items left...
      "3:\t"
      "cbz     %1, 4f\n\t"
      "strh    %w2, [%0]\n"
      "4:"
      : "+r" (tmpDst), "+r" (count), "+r" (tmpVal)
      : "r" ((uint64)val)
      : "cc", "memory");
#elif defined __GNUC__ && defined VM_X86_ANY
   size_t dummy0;
   void *dummy1;

   __asm__ __volatile__(
      "cld"            "\n\t"
      "rep ; stosw"    "\n"
      : "=c" (dummy0), "=D" (dummy1)
      : "0" (count), "1" (dst), "a" (val)
      : "memory", "cc");
#elif defined _MSC_VER && defined VM_X86_ANY
   __stosw((uint16 *)dst, val, count);
#else /* Fall back to a plain C implementation. */
   size_t i;

   for (i = 0; i < count; i++) {
      ((uint16 *)dst)[i] = val;
   }
#endif
   return dst;
}


/*
 *-----------------------------------------------------------------------------
 *
 * uint32set --
 *
 *      memset a given address with an uint32 value, count times.
 *
 * Results:
 *      Pointer to filled memory range.
 *
 * Side effects:
 *      As with memset.
 *
 *-----------------------------------------------------------------------------
 */

static inline void *
uint32set(void *dst,    // OUT
          uint32 val,   // IN
          size_t count) // IN
{
#if defined __GNUC__ && defined VM_ARM_32
   void *tmpDst = dst;

   __asm__ __volatile__(
      "cmp     %1, #0\n\t"
      "beq     2f\n"
      "1:\n\t"
      "str     %2, [%0], #4\n\t"
      "subs    %1, %1, #1\n\t"
      "bne     1b\n"
      "2:"
      : "+r" (tmpDst), "+r" (count)
      : "r" (val)
      : "cc", "memory");
#elif defined __GNUC__ && defined VM_ARM_64
   void   *tmpDst = dst;
   uint64  tmpVal = val;

   if (count == 0) {
      return dst;
   }

   __asm__ __volatile__(
      "cbz     %2, 1f\n\t"

      // Drop our value in the top 32 bits, then copy from there to the bottom
      "lsl     %2, %2, #32\n\t"
      "bfm     %2, %2, #32, #63\n"

      // Copy four at a time
      "1:\t"
      "cmp     %1, #16\n\t"
      "b.lo    2f\n\t"
      "stp     %2, %2, [%0], #16\n\t"
      "stp     %2, %2, [%0], #16\n\t"
      "stp     %2, %2, [%0], #16\n\t"
      "stp     %2, %2, [%0], #16\n\t"
      "sub     %1, %1, #16\n\t"
      "b       1b\n"

      // Copy remaining pairs of data
      "2:\t"
      "cmp     %1, #2\n\t"
      "b.lo    3f\n\t"
      "str     %2, [%0], #8\n\t"
      "sub     %1, %1, #2\n\t"
      "b       2b\n"

      // One or zero values left to copy
      "3:\t"
      "cbz     %1, 4f\n\t"
      "str     %w2, [%0]\n\t" // No incr
      "4:"
      : "+r" (tmpDst), "+r" (count), "+r" (tmpVal)
      :
      : "cc", "memory");
#elif defined __GNUC__ && defined VM_X86_ANY
   size_t dummy0;
   void *dummy1;

   __asm__ __volatile__(
      "cld"            "\n\t"
      "rep ; stosl"    "\n"
      : "=c" (dummy0), "=D" (dummy1)
      : "0" (count), "1" (dst), "a" (val)
      : "memory", "cc");
#elif defined _MSC_VER && defined VM_X86_ANY
   __stosd((unsigned long *)dst, (unsigned long)val, count);
#else /* Fall back to a plain C implementation. */
   size_t i;

   for (i = 0; i < count; i++) {
      ((uint32 *)dst)[i] = val;
   }
#endif
   return dst;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Bswap16 --
 *
 *      Swap the 2 bytes of "v" as follows: 32 -> 23.
 *
 *-----------------------------------------------------------------------------
 */

static inline uint16
Bswap16(uint16 v)
{
#if defined(VM_ARM_64) && !defined(_MSC_VER)
   __asm__("rev16 %w0, %w0" : "+r"(v));
   return v;
#else
   return ((v >> 8) & 0x00ff) | ((v << 8) & 0xff00);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Bswap32 --
 *
 *      Swap the 4 bytes of "v" as follows: 3210 -> 0123.
 *
 *-----------------------------------------------------------------------------
 */

static inline uint32
Bswap32(uint32 v) // IN
{
#if defined(__GNUC__) && defined(VM_X86_ANY)
   /* Checked against the Intel manual and GCC. --hpreg */
   __asm__(
      "bswap %0"
      : "=r" (v)
      : "0" (v)
   );
   return v;
#elif defined(VM_ARM_32) && !defined(__ANDROID__) && !defined(_MSC_VER)
    __asm__("rev %0, %0" : "+r"(v));
    return v;
#elif defined(VM_ARM_64) && !defined(_MSC_VER)
   __asm__("rev32 %x0, %x0" : "+r"(v));
   return v;
#else
   return    (v >> 24)
          | ((v >>  8) & 0xFF00)
          | ((v & 0xFF00) <<  8)
          |  (v << 24)          ;
#endif
}
#define Bswap Bswap32


/*
 *-----------------------------------------------------------------------------
 *
 * Bswap64 --
 *
 *      Swap the 8 bytes of "v" as follows: 76543210 -> 01234567.
 *
 *-----------------------------------------------------------------------------
 */

static inline uint64
Bswap64(uint64 v) // IN
{
#if defined _MSC_VER
   return _byteswap_uint64(v);
#elif defined __GNUC__

/* TODO: Return __builtin_bswap64(v) if gcc-arm64 is verified to use "rev". */
#if defined VM_ARM_64
   __asm__("rev %0, %0" : "+r"(v));
   return v;
#else
   return __builtin_bswap64(v);
#endif

#else
   return ((uint64)Bswap32((uint32)v) << 32) | Bswap32((uint32)(v >> 32));
#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)
#if defined(__GNUC__) || defined(VM_ARM_32)
{
#ifdef VM_ARM_64
   __asm__ __volatile__("yield");
#elif defined VM_ARM_32
   /*
    * YIELD is available in ARMv6K and above, so we could probably refine this
    * instead of leaving it empty.
    */
#else // x86
   __asm__ __volatile__( "pause" :);
#endif
}
#elif defined(_MSC_VER)
{
#ifdef VM_X86_ANY
   _mm_pause();
#else
   __yield();
#endif
}
#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;
#elif defined(VM_X86_32)
   uint64 tim;

   __asm__ __volatile__(
      "rdtsc"
      : "=A" (tim)
   );

   return tim;
#elif defined(VM_ARM_64)
   /*
    * Keep this implementation in sync with:
    * bora/vmkernel/hardware/arm64/tscsync_arch.h::TSCSyncRDTSC()
    * bora/lib/vprobe/arm64/vp_emit_tc.c::VpEmit_BuiltinRDTSCWork()
    * bora/modules/vmkernel/tests/core/xmapTest/xmapTest_arm64.c::XMapTest_SetupLoopCode()
    */
#ifdef VMKERNEL
   return MRSx(VMK_CNT_CT_EL);
#else
   return MRS(CNTVCT_EL0);
#endif
#else
   /*
    * For platform without cheap timer, just return 0.
    */
   return 0;
#endif
}
#elif defined(_MSC_VER)
#ifdef VM_X86_ANY
{
   return __rdtsc();
}
#else
{
   /*
    * We need to do more investigation here to find
    * a Microsoft equivalent of that code.
    */
   NOT_IMPLEMENTED();
   return 0;
}
#endif /* VM_X86_ANY */
#else  /* __GNUC__  */
#error No compiler defined for RDTSC
#endif /* __GNUC__  */


/*
 *-----------------------------------------------------------------------------
 *
 * DEBUGBREAK --
 *
 *    Does an int3 for MSVC / GCC, bkpt/brk for ARM. This is a macro to make
 *    sure int3 is always inlined.
 *
 *-----------------------------------------------------------------------------
 */

#ifdef VM_ARM_32
#define DEBUGBREAK() __asm__("bkpt")
#elif defined(VM_ARM_64)
#define DEBUGBREAK() __asm__("brk #0")
#elif defined(_MSC_VER)
#define DEBUGBREAK() __debugbreak()
#else
#define DEBUGBREAK() __asm__("int $3")
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * {Clear, Set, Test, Toggle}Bit{32, 64} --
 *
 *    Sets tests clears or toggles a specified single bit in the provided
 *    variable.
 *
 *    The index input value specifies which bit to modify and is 0-based.
 *    Index is truncated by hardware to a 5-bit or 6-bit offset for the
 *    32 and 64-bit flavors, respectively, but input values are not validated
 *    with asserts to avoid include dependencies.
 *
 *    64-bit flavors are not handcrafted for 32-bit builds because they may
 *    defeat compiler optimizations.
 *
 *-----------------------------------------------------------------------------
 */

static inline void
SetBit32(uint32 *var, unsigned index)
{
   *var |= 1 << index;
}

static inline void
ClearBit32(uint32 *var, unsigned index)
{
   *var &= ~(1 << index);
}

static inline void
ToggleBit32(uint32 *var, unsigned index)
{
   *var ^= 1 << index;
}

static inline void
SetBit64(uint64 *var, unsigned index)
{
   *var |= CONST64U(1) << index;
}

static inline void
ClearBit64(uint64 *var, unsigned index)
{
   *var &= ~(CONST64U(1) << index);
}

static inline void
ToggleBit64(uint64 *var, unsigned index)
{
   *var ^= (CONST64U(1) << index);
}

static inline Bool
TestBit32(const uint32 *var, unsigned index)
{
   return (*var & (1 << index)) != 0;
}

static inline Bool
TestBit64(const uint64 *var, unsigned index)
{
   return (*var & (CONST64U(1) << index)) != 0;
}

/*
 *-----------------------------------------------------------------------------
 *
 * {Clear,Set,Complement,Test}BitVector --
 *
 *    Sets, clears, complements, or tests a specified single bit in the
 *    provided array.  The index input value specifies which bit to modify
 *    and is 0-based.  Bit number can be +-2Gb (+-128MB) relative from 'var'
 *    variable.
 *
 *    All functions return value of the bit before modification was performed.
 *
 *-----------------------------------------------------------------------------
 */

#if defined __GCC_ASM_FLAG_OUTPUTS__
/*
 * See https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
 * 6.47.2.4 Flag Output Operands
 *
 * This expands to 0 or 1 instructions followed by the output operand string.
 */
#define GCC_ASM_BT_EPILOG : "=@ccc"
#else
#define GCC_ASM_BT_EPILOG "\n\tsetc\t%0" : "=qQm"
#endif

static inline Bool
SetBitVector(void *var, int32 index)
{
#if defined(__GNUC__) && defined(VM_X86_ANY)
   Bool bit;
   __asm__("bts\t%2, %1"
           GCC_ASM_BT_EPILOG (bit), "+m" (*(uint32 *)var)
           : "rI" (index)
           : "memory", "cc");
   return bit;
#elif defined(_MSC_VER)
   return _bittestandset((long *)var, index) != 0;
#else
   Bool retVal = (((uint8 *)var)[index / 8] & (1 << (index % 8))) != 0;
   ((uint8 *)var)[index / 8] |= 1 << (index % 8);
   return retVal;
#endif
}

static inline Bool
ClearBitVector(void *var, int32 index)
{
#if defined(__GNUC__) && defined(VM_X86_ANY)
   Bool bit;
   __asm__("btr\t%2, %1"
           GCC_ASM_BT_EPILOG (bit), "+m" (*(uint32 *)var)
           : "rI" (index)
           : "memory", "cc");
   return bit;
#elif defined(_MSC_VER)
   return _bittestandreset((long *)var, index) != 0;
#else
   Bool retVal = (((uint8 *)var)[index / 8] & (1 << (index % 8))) != 0;
   ((uint8 *)var)[index / 8] &= ~(1 << (index % 8));
   return retVal;
#endif
}

static inline Bool
ComplementBitVector(void *var, int32 index)
{
#if defined(__GNUC__) && defined(VM_X86_ANY)
   Bool bit;
   __asm__("btc\t%2, %1"
           GCC_ASM_BT_EPILOG (bit), "+m" (*(uint32 *)var)
           : "rI" (index)
           : "memory", "cc");
   return bit;
#elif defined(_MSC_VER)
   return _bittestandcomplement((long *)var, index) != 0;
#else
   Bool retVal = (((uint8 *)var)[index / 8] & (1 << (index % 8))) != 0;
   ((uint8 *)var)[index / 8] ^= ~(1 << (index % 8));
   return retVal;
#endif
}

static inline Bool
TestBitVector(const void *var, int32 index)
{
#if defined(__GNUC__) && defined(VM_X86_ANY)
   Bool bit;
   __asm__("bt\t%2, %1"
           GCC_ASM_BT_EPILOG (bit)
           : "m" (*(const uint32 *)var), "rI" (index)
           : "cc");
   return bit;
#elif defined _MSC_VER
   return _bittest((long *)var, index) != 0;
#else
   return (((const uint8 *)var)[index / 8] & (1 << (index % 8))) != 0;
#endif
}

#undef GCC_ASM_BT_EPILOG

/*
 *-----------------------------------------------------------------------------
 * RoundDownPow2_{64,32} --
 *
 *   Rounds a value down to the previous power of 2.  Returns the original
 *   value if it is a power of 2. Returns 0 for input of 0 and 1 for 1.
 *-----------------------------------------------------------------------------
 */

static inline uint64
RoundDownPow2_64(uint64 value)
{
   if ((value & (value - 1)) == 0) {
      /*
       * Already zero or a power of two.
       */
      return value;
   }

   return CONST64U(1) << mssb64_0(value);
}


static inline uint32
RoundDownPow2_32(uint32 value)
{
   if ((value & (value - 1)) == 0) {
      /*
       * Already zero or a power of two.
       */
      return value;
   }

   return 1U << mssb32_0(value);
}


/*
 *-----------------------------------------------------------------------------
 * RoundUpPow2_{64,32} --
 *
 *   Rounds a value up to the next higher power of 2.  Returns the original
 *   value if it is a power of 2.  The next power of 2 for inputs {0, 1} is 1.
 *   The result is undefined for inputs above {2^63, 2^31} (but equal to 1
 *   in this implementation).
 *-----------------------------------------------------------------------------
 */

static inline uint64
RoundUpPow2C64(uint64 value)
{
   if (value <= 1 || value > (CONST64U(1) << 63)) {
      return 1; // Match the assembly's undefined value for large inputs.
   } else {
      return (CONST64U(2) << mssb64_0(value - 1));
   }
}

#if defined(__GNUC__) && defined(VM_X86_64)
static inline uint64
RoundUpPow2Asm64(uint64 value)
{
   uint64 out = 2;
   __asm__("lea -1(%[in]), %%rcx;"      // rcx = value - 1.  Preserve original.
           "bsr %%rcx, %%rcx;"          // rcx = log2(value - 1) if value != 1
                                        // if value == 0, then rcx = 63
                                        // if value == 1 then zf = 1, else zf = 0.
           "rol %%cl, %[out];"          // out = 2 << rcx (if rcx != -1)
                                        //     = 2^(log2(value - 1) + 1)
                                        // if rcx == -1 (value == 0), out = 1
                                        // zf is always unmodified.
           "cmovz %[in], %[out]"        // if value == 1 (zf == 1), write 1 to out.
       : [out]"+r"(out) : [in]"r"(value) : "%rcx", "cc");
   return out;
}
#endif

static inline uint64
RoundUpPow2_64(uint64 value)
{
#if defined(__GNUC__) && defined(VM_X86_64)
   if (__builtin_constant_p(value)) {
      return RoundUpPow2C64(value);
   } else {
      return RoundUpPow2Asm64(value);
   }
#else
   return RoundUpPow2C64(value);
#endif
}

static inline uint32
RoundUpPow2C32(uint32 value)
{
   if (value <= 1 || value > (1U << 31)) {
      return 1; // Match the assembly's undefined value for large inputs.
   } else {
      int mssb32 = mssb32_0(value - 1);
      /* invariant: mssb32 >= 0 */
      return (2U << (uint32)mssb32);
   }
}

#ifdef __GNUC__
static inline uint32
RoundUpPow2Asm32(uint32 value)
{
#ifdef VM_ARM_32
   uint32 out = 1;
   // Note: None Thumb only!
   //       The value of the argument "value"
   //       will be affected!
   __asm__("sub %[in], %[in], #1;"         // r1 = value - 1 . if value == 0 then r1 = 0xFFFFFFFF
           "clz %[in], %[in];"             // r1 = log2(value - 1) if value != 1
                                           // if value == 0 then r1 = 0
                                           // if value == 1 then r1 = 32
           "mov %[out], %[out], ror %[in]" // out = 2^(32 - r1)
                                           // if out == 2^32 then out = 1 as it is right rotate
       : [in]"+r"(value),[out]"+r"(out));
   return out;
#elif defined(VM_ARM_64) || defined(__wasm__)
   return RoundUpPow2C32(value);
#else
   uint32 out = 2;

   __asm__("lea -1(%[in]), %%ecx;"      // ecx = value - 1.  Preserve original.
           "bsr %%ecx, %%ecx;"          // ecx = log2(value - 1) if value != 1
                                        // if value == 0, then ecx = 31
                                        // if value == 1 then zf = 1, else zf = 0.
           "rol %%cl, %[out];"          // out = 2 << ecx (if ecx != -1)
                                        //     = 2^(log2(value - 1) + 1).
                                        // if ecx == -1 (value == 0), out = 1
                                        // zf is always unmodified
           "cmovz %[in], %[out]"        // if value == 1 (zf == 1), write 1 to out.
       : [out]"+r"(out) : [in]"r"(value) : "%ecx", "cc");
   return out;
#endif
}
#endif // __GNUC__

static inline uint32
RoundUpPow2_32(uint32 value)
{
#ifdef __GNUC__
   if (__builtin_constant_p(value)) {
      return RoundUpPow2C32(value);
   } else {
      return RoundUpPow2Asm32(value);
   }
#else
   return RoundUpPow2C32(value);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * PopCount32 --
 *
 *     Counts "1" bits in a uint32.
 *
 * Results:
 *     Returns the number of bits set to 1.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static inline unsigned
PopCount32(uint32 value)
{
#if defined(__GNUC__) && defined(__POPCNT__)
   return __builtin_popcount(value);
#else
   /*
    * Attribution:
    *     This algorithm was copied from:
    *         http://www.aggregate.org/MAGIC#Population Count (Ones Count)
    *
    *     A virtually identical version (but in assembly) appears in an
    *     AMD reference manual.
    *
    *     No license appears in the original code, but the website
    *     header states:
    *
    *     "None of the following coding tricks came from proprietary
    *     sources; further, we believe that each of the tricks we did
    *     not invent is essentially "standard engineering practice" in
    *     the specialized niche where it applies. Thus, although we
    *     have not conducted patent searches, etc., to confirm it, we
    *     believe that these are tricks that freely can be used for
    *     any purpose. Of course, The Aggregate accepts no
    *     responsibility for your use of these tricks; you must
    *     confirm that the trick does what you want and that you can
    *     use it as you intend. That said, we do intend to maintain
    *     this page by adding new algorithms and/or correcting
    *     existing entries. If you have any comments, please contact
    *     Professor Hank Dietz, http://aggregate.org/hankd/"
    *
    *     "This document should be cited using something like the
    *     following bibtex entry:" (most recent retrieval date added)
    *
    *     @techreport{magicalgorithms,
    *     author={Henry Gordon Dietz},
    *     title={{The Aggregate Magic Algorithms}},
    *     institution={University of Kentucky},
    *     howpublished={Aggregate.Org online technical report},
    *     URL={http://aggregate.org/MAGIC/},
    *     urldate={2016-01-27}
    *     }
    */
   value -= ((value >> 1) & 0x55555555);
   value = (((value >> 2) & 0x33333333) + (value & 0x33333333));
   value = (((value >> 4) + value) & 0x0f0f0f0f);
   value += (value >> 8);
   value += (value >> 16);
   return value & 0x0000003f;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * PopCount64 --
 *
 *     Counts "1" bits in a uint64.
 *
 * Results:
 *     Returns the number of bits set to 1.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static inline unsigned
PopCount64(uint64 value)
{
#if defined(__GNUC__) && defined(__POPCNT__)
#if defined(VM_X86_64)
   return __builtin_popcountll(value);
#else
   return PopCount32(value) + PopCount32(value >> 32);
#endif
#else
   value -= (value >> 1) & 0x5555555555555555ULL;
   value = ((value >> 2) & 0x3333333333333333ULL) +
           (value & 0x3333333333333333ULL);
   value = ((value >> 4) + value) & 0x0f0f0f0f0f0f0f0fULL;
   value += value >> 8;
   value += value >> 16;
   value += value >> 32;
   return (unsigned) (value & 0xff);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * INTR_R_BARRIER_R --
 * INTR_R_BARRIER_W --
 * INTR_R_BARRIER_RW --
 * INTR_W_BARRIER_R --
 * INTR_W_BARRIER_W --
 * INTR_W_BARRIER_RW --
 * INTR_RW_BARRIER_R --
 * INTR_RW_BARRIER_W --
 * INTR_RW_BARRIER_RW --
 *
 *      Enforce ordering on memory operations witnessed by and
 *      affected by interrupt handlers.
 *
 *      This should be used to replace the legacy COMPILER_MEM_BARRIER
 *      for code that has been audited to determine it only needs
 *      ordering with respect to interrupt handlers, and not to other
 *      CPUs (SMP_*), memory-mapped I/O (MMIO_*), or DMA (DMA_*).
 *
 *-----------------------------------------------------------------------------
 */

#ifdef __GNUC__

static inline void
INTR_RW_BARRIER_RW(void)
{
   __asm__ __volatile__("" ::: "memory");
}

#define INTR_R_BARRIER_R INTR_RW_BARRIER_RW
#define INTR_R_BARRIER_W INTR_RW_BARRIER_RW
#define INTR_R_BARRIER_RW INTR_RW_BARRIER_RW
#define INTR_W_BARRIER_R INTR_RW_BARRIER_RW
#define INTR_W_BARRIER_W INTR_RW_BARRIER_RW
#define INTR_W_BARRIER_RW INTR_RW_BARRIER_RW
#define INTR_RW_BARRIER_R INTR_RW_BARRIER_RW
#define INTR_RW_BARRIER_W INTR_RW_BARRIER_RW

#elif defined _MSC_VER

static inline void
INTR_R_BARRIER_R(void)
{
   _ReadBarrier();
}

static inline void
INTR_W_BARRIER_W(void)
{
   _WriteBarrier();
}

static inline void
INTR_RW_BARRIER_RW(void)
{
   _ReadWriteBarrier();
}

#define INTR_R_BARRIER_W INTR_RW_BARRIER_RW
#define INTR_R_BARRIER_RW INTR_RW_BARRIER_RW
#define INTR_W_BARRIER_R INTR_RW_BARRIER_RW
#define INTR_W_BARRIER_RW INTR_RW_BARRIER_RW
#define INTR_RW_BARRIER_R INTR_RW_BARRIER_RW
#define INTR_RW_BARRIER_W INTR_RW_BARRIER_RW

#else
#error No compiler defined for INTR_*_BARRIER_*
#endif


#if defined __cplusplus
} // extern "C"
#endif

#endif // _VM_BASIC_ASM_H_

   07070100000130000081A4000000000000000000000001682255050000701C000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_basic_asm_arm64.h   /*********************************************************
 * Copyright (C) 2013-2024 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vm_basic_asm_arm64.h --
 *
 *      Basic assembler macros for the AArch64 architecture.
 */

#ifndef _VM_BASIC_ASM_ARM64_H_
#define _VM_BASIC_ASM_ARM64_H_

#include "vm_basic_defs.h"

#if defined __cplusplus
extern "C" {
#endif

/*
 *----------------------------------------------------------------------
 *
 * _DMB --
 *
 *      Data memory barrier.
 *
 *      Memory barrier governing visibility of explicit load/stores.
 *
 *      The options for shareability domains are:
 *      NSH     - Non-shareable
 *      ISH     - Inner Shareable
 *      OSH     - Outer Shareable
 *      default - Full System
 *
 *      The options for access types are:
 *      LD      - Load         , Barrier, Load _or Store_ (yes, really)
 *      ST      - Store        , Barrier, Store
 *      default - Load or Store, Barrier, Load or Store
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

#if defined __GNUC__
#define _DMB(t) asm volatile ("dmb " #t ::: "memory")
#elif defined _MSC_VER
#define _DMB(t) __dmb(_ARM64_BARRIER_##t)
#else
#error No compiler defined for _DMB
#endif


/*
 *----------------------------------------------------------------------
 *
 * _DSB --
 *
 *      Data synchronization barrier.
 *
 *      Synchronizes the execution stream with memory accesses. Like a DMB but
 *      also forces all cache/TLB maintenance operations to complete.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

#if defined __GNUC__
#define _DSB(t) asm volatile ("dsb " #t ::: "memory")
#elif defined _MSC_VER
#define _DSB(t) __dsb(_ARM64_BARRIER_##t)
#else
#error No compiler defined for _DSB
#endif


/*
 *----------------------------------------------------------------------
 *
 * ISB --
 *
 *      Instruction synchronization barrier.
 *
 *      Pipeline flush - all instructions fetched after ISB have effects of
 *      cache/maintenance and system register updates prior to the ISB visible.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static inline void
ISB(void)
{
#if defined __GNUC__
   asm volatile ("isb" ::: "memory");
#elif defined _MSC_VER
   __isb(_ARM64_BARRIER_SY);
#else
#error No compiler defined for ISB
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * ESB --
 *
 *      Error synchronization barrier.
 *
 *      Error synchronization event as per Arm ARM. NOP if ARMv8.2
 *      RAS extensions are not implemented.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      SError exception or DISR/VDISR getting updated.
 *
 *----------------------------------------------------------------------
 */

#if defined __GNUC__
static inline void
ESB(void)
{
   /*
    * The assembler of gcc 9.3 with -march=armv8-a errors out with
    * "Error: selected processor does not support `esb'"
    * There is no way to cleanly temporarily push/pop -march=armv8.2-a or the
    * ras extension. The error does not occur with gcc versions >= 10.2.
    */
# if __GNUC__ > 10 || __GNUC__ == 10 && __GNUC_MINOR__ >= 2
   asm volatile("esb" ::: "memory");
# else
   asm volatile(
      ".arch armv8.2-a"       "\n\t"
      "esb"                   "\n\t"
      ".arch " XSTR(VMW_ARCH)
      ::: "memory"
   );
# endif
}
#endif

/*
 * Memory Barriers
 * ===============
 *
 *    Terminology
 *    -----------
 *
 * A compiler memory barrier prevents the compiler from re-ordering memory
 * accesses accross the barrier. It is not a CPU instruction, it is a compiler
 * directive (i.e. it does not emit any code).
 *
 * => A compiler memory barrier on its own is useful for coordinating
 *    with an interrupt handler (or preemption logic in the scheduler)
 *    on the same CPU, so that the order of read and write
 *    instructions in code that might be interrupted is consistent
 *    with the barriers. But when there are other CPUs involved, or
 *    other types of devices like memory-mapped I/O and DMA
 *    controllers, a compiler memory barrier is not enough.
 *
 * A CPU memory barrier prevents the CPU from re-ordering memory accesses
 * accross the barrier. It is a CPU instruction.
 *
 * => On its own the CPU instruction isn't useful because the compiler
 *    may reorder loads and stores around the CPU instruction.  It is
 *    useful only when combined with a compiler memory barrier.
 *
 * A memory barrier is the union of a compiler memory barrier and a CPU memory
 * barrier.
 *
 *    Semantics
 *    ---------
 *
 * At the time COMPILER_MEM_BARRIER was created (and references to it were
 * added to the code), the code was only targetting x86. The intent of the code
 * was really to use a memory barrier, but because x86 uses a strongly ordered
 * memory model, the CPU would not re-order most memory accesses (store-load
 * ordering still requires MFENCE even on x86), and the code could get away
 * with using just a compiler memory barrier. So COMPILER_MEM_BARRIER was born
 * and was implemented as a compiler memory barrier _on x86_. But make no
 * mistake, _the semantics that the code expects from COMPILER_MEM_BARRIER is
 * that of a memory barrier_!
 *
 *    DO NOT USE!
 *    -----------
 *
 * On at least one non-x86 architecture, COMPILER_MEM_BARRIER is
 * 1) A misnomer
 * 2) Not fine-grained enough to provide the best performance.
 * For the above two reasons, usage of COMPILER_MEM_BARRIER is now deprecated.
 * _Do not add new references to COMPILER_MEM_BARRIER._ Instead, precisely
 * document the intent of your code by using
 * <mem_type/purpose>_<before_access_type>_BARRIER_<after_access_type>.
 * Existing references to COMPILER_MEM_BARRIER are being slowly but surely
 * converted, and when no references are left, COMPILER_MEM_BARRIER will be
 * retired.
 *
 * Thanks for pasting this whole comment into every architecture header.
 */

/*
 * To match x86 TSO semantics, we need to guarantee ordering for
 * everything _except_ store-load:
 *
 * - DMB ISHLD orders load-load and load-store.
 * - DMB ISHST orders store-store.
 *
 * In contrast, SMP_RW_BARRIER_RW, or DMB ISH, orders all four
 * (load-load, load-store, store-load, store-store), so it's stronger
 * than we need -- like x86 MFENCE.
 */
#define COMPILER_MEM_BARRIER() do { _DMB(ISHLD); _DMB(ISHST); } while (0)

/*
 * Memory barriers. These take the form of
 *
 * <mem_type/purpose>_<before_access_type>_BARRIER_<after_access_type>
 *
 * where:
 *   <mem_type/purpose> is either INTR, SMP, DMA, or MMIO.
 *   <*_access type> is either R(load), W(store) or RW(any).
 *
 * Above every use of these memory barriers in the code, there _must_ be a
 * comment to justify the use, i.e. a comment which:
 * 1) Precisely identifies which memory accesses must not be re-ordered across
 *    the memory barrier.
 * 2) Explains why it is important that the memory accesses not be re-ordered.
 *
 * Thanks for pasting this whole comment into every architecture header.
 */

#define SMP_R_BARRIER_R()     SMP_R_BARRIER_RW()
#define SMP_R_BARRIER_W()     SMP_R_BARRIER_RW()
#define SMP_R_BARRIER_RW()    _DMB(ISHLD)
#define SMP_W_BARRIER_R()     SMP_RW_BARRIER_RW()
#define SMP_W_BARRIER_W()     _DMB(ISHST)
#define SMP_W_BARRIER_RW()    SMP_RW_BARRIER_RW()
#define SMP_RW_BARRIER_R()    SMP_RW_BARRIER_RW()
#define SMP_RW_BARRIER_W()    SMP_RW_BARRIER_RW()
#define SMP_RW_BARRIER_RW()   _DMB(ISH)

/*
 * Like the above, only for use with cache coherent observers other than CPUs,
 * i.e. DMA masters.
 * On ARM it means that we extend the `dmb' options to an outer-shareable
 * memory where all our devices are.
 */

#define DMA_R_BARRIER_R()     DMA_R_BARRIER_RW()
#define DMA_R_BARRIER_W()     DMA_R_BARRIER_RW()
#define DMA_R_BARRIER_RW()    _DMB(OSHLD)
#define DMA_W_BARRIER_R()     DMA_RW_BARRIER_RW()
#define DMA_W_BARRIER_W()     _DMB(OSHST)
#define DMA_W_BARRIER_RW()    DMA_RW_BARRIER_RW()
#define DMA_RW_BARRIER_R()    DMA_RW_BARRIER_RW()
#define DMA_RW_BARRIER_W()    DMA_RW_BARRIER_RW()
#define DMA_RW_BARRIER_RW()   _DMB(OSH)

/*
 * And finally a set for use with MMIO accesses.
 * Synchronization of accesses to a non-cache coherent device memory
 * (in general case) requires strongest available barriers on ARM.
 */

#define MMIO_R_BARRIER_R()    MMIO_R_BARRIER_RW()
#define MMIO_R_BARRIER_W()    MMIO_R_BARRIER_RW()
#define MMIO_R_BARRIER_RW()   _DSB(LD)
#define MMIO_W_BARRIER_R()    MMIO_RW_BARRIER_RW()
#define MMIO_W_BARRIER_W()    _DSB(ST)
#define MMIO_W_BARRIER_RW()   MMIO_RW_BARRIER_RW()
#define MMIO_RW_BARRIER_R()   MMIO_RW_BARRIER_RW()
#define MMIO_RW_BARRIER_W()   MMIO_RW_BARRIER_RW()
#define MMIO_RW_BARRIER_RW()  _DSB(SY)

#ifdef __GNUC__

/*
 * _GET_CURRENT_PC --
 * GET_CURRENT_PC --
 *
 * Returns the current program counter. In the example below:
 *
 *   foo.c
 *   L123: Foo(GET_CURRENT_PC())
 *
 * the return value from GET_CURRENT_PC will point a debugger to L123.
 */

#define _GET_CURRENT_PC(pc)                                                   \
   asm volatile ("1: adr %0, 1b" : "=r" (pc))

static INLINE_ALWAYS void *
GET_CURRENT_PC(void)
{
   void *pc;

   _GET_CURRENT_PC(pc);
   return pc;
}

/*
 * GET_CURRENT_LOCATION --
 *
 * Updates the arguments with the values of the pc, fp, sp and the
 * return address at the current code location where the macro is invoked.
 */

#define GET_CURRENT_LOCATION(pc, fp, sp, retAddr) do {                        \
   _GET_CURRENT_PC(pc);                                                       \
   asm volatile ("mov %0, sp" : "=r" (sp));                                   \
   fp = (uint64)GetFrameAddr();                                               \
   retAddr = (uint64)GetReturnAddress();                                      \
} while (0)


/*
 *----------------------------------------------------------------------
 *
 * MRS --
 *
 *      Get the value of system register 'name'.
 *
 * Results:
 *      The value.
 *
 * Side effects:
 *      Depends on 'name'.
 *
 *----------------------------------------------------------------------
 */

#define MRS(name) ({                                                          \
   uint64 val;                                                                \
   asm volatile ("mrs %0, " XSTR(name) : "=r" (val) :: "memory");             \
   val;                                                                       \
})


/*
 *----------------------------------------------------------------------
 *
 * MSR --
 * MSR_IMMED --
 *
 *      Set the value of system register 'name'.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Depends on 'name'.
 *
 *----------------------------------------------------------------------
 */

#define MSR(name, val)                                                        \
   asm volatile ("msr " XSTR(name) ", %0" :: "r" (val) : "memory")

#define MSR_IMMED(name, val)                                                  \
   asm volatile ("msr " XSTR(name) ", %0" :: "i" (val) : "memory")

#endif // ifdef __GNUC__


/*
 *----------------------------------------------------------------------
 *
 * MMIORead32 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      32-bit value at given location.
 *
 *----------------------------------------------------------------------
 */

static inline uint32
MMIORead32(const volatile void *addr) // IN
{
   uint32 res;

#if defined __GNUC__
   asm volatile ("ldr %w0, %1"
                 : "=r" (res)
                 : "m" (*(const volatile uint32 *)addr));
#elif defined _MSC_VER
   res = __iso_volatile_load32((const volatile __int32 *)addr);
#else
#error No compiler defined for MMIORead32
#endif
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIORead64 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      64-bit value at given location.
 *
 *----------------------------------------------------------------------
 */

static inline uint64
MMIORead64(const volatile void *addr) // IN
{
   uint64 res;

#if defined __GNUC__
   asm volatile ("ldr %x0, %1"
                 : "=r" (res)
                 : "m" (*(const volatile uint64 *)addr));
#elif defined _MSC_VER
   res = __iso_volatile_load64((const volatile __int64 *)addr);
#else
#error No compiler defined for MMIORead64
#endif
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite32 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */

static inline void
MMIOWrite32(volatile void *addr, // OUT
            uint32 val)          // IN
{
#if defined __GNUC__
   asm volatile ("str %w1, %0"
                 : "=m" (*(volatile uint32 *)addr)
                 : "r" (val));
#elif defined _MSC_VER
   __iso_volatile_store32((volatile __int32 *)addr, val);
#else
#error No compiler defined for MMIOWrite32
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite64 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */

static inline void
MMIOWrite64(volatile void *addr, // OUT
            uint64 val)          // IN
{
#if defined __GNUC__
   asm volatile ("str %x1, %0"
                 : "=m" (*(volatile uint64 *)addr)
                 : "r" (val));
#elif defined _MSC_VER
   __iso_volatile_store64((volatile __int64 *)addr, val);
#else
#error No compiler defined for MMIOWrite64
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * MMIORead16 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      16-bit value at given location.
 *
 *----------------------------------------------------------------------
 */

static inline uint16
MMIORead16(const volatile void *addr) // IN
{
   uint16 res;

#if defined __GNUC__
   asm volatile ("ldrh %w0, %1"
                 : "=r" (res)
                 : "m" (*(const volatile uint16 *)addr));
#elif defined _MSC_VER
   res = __iso_volatile_load16((const volatile __int16 *)addr);
#else
#error No compiler defined for MMIORead16
#endif
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite16 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */

static inline void
MMIOWrite16(volatile void *addr,  // OUT
            uint16 val)           // IN
{
#if defined __GNUC__
   asm volatile ("strh %w1, %0"
                 : "=m" (*(volatile uint16 *)addr)
                 : "r" (val));
#elif defined _MSC_VER
   __iso_volatile_store16((volatile __int16 *)addr, val);
#else
#error No compiler defined for MMIOWrite16
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * MMIORead8 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      8-bit value at given location.
 *
 *----------------------------------------------------------------------
 */

static inline uint8
MMIORead8(const volatile void *addr) // IN
{
   uint8 res;

#if defined __GNUC__
   asm volatile ("ldrb %w0, %1"
                 : "=r" (res)
                 : "m" (*(const volatile uint8 *)addr));
#elif defined _MSC_VER
   res = __iso_volatile_load8((const volatile __int8 *)addr);
#else
#error No compiler defined for MMIORead8
#endif
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite8 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */

static inline void
MMIOWrite8(volatile void *addr, // OUT
           uint8 val)           // IN
{
#if defined __GNUC__
   asm volatile ("strb %w1, %0"
                 : "=m" (*(volatile uint8 *)addr)
                 : "r" (val));
#elif defined _MSC_VER
   __iso_volatile_store8((volatile __int8 *)addr, val);
#else
#error No compiler defined for MMIOWrite8
#endif
}


#ifdef VM_HAS_INT128
/*
 *----------------------------------------------------------------------
 *
 * MMIORead128 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      128-bit value at given location.
 *
 *----------------------------------------------------------------------
 */
static inline uint128
MMIORead128(const volatile void *addr) // IN
{
   union {
      uint128 val128;
      struct {
         uint64 val64[2];
      };
   } res;
   asm volatile ("ldp %x0, %x1, %2"
                 : "=r" (res.val64[0]), "=r" (res.val64[1])
                 : "Q" (*(const volatile uint128 *)addr));
   return res.val128;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite128 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */
static inline void
MMIOWrite128(volatile void *addr, // OUT
             uint128 val)         // IN
{
   union {
      uint128 val128;
      struct {
         uint64 val64[2];
      };
   } res = { .val128 = val };
   asm volatile ("stp %x1, %x2, %0"
                 : "=Q" (*(volatile uint128 *)addr)
                 : "r" (res.val64[0]), "r" (res.val64[1]));
}
#endif // VM_HAS_INT128


#ifdef __GNUC__

/*
 *----------------------------------------------------------------------
 *
 * WFI --
 *
 *      Wait for interrupt.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static inline void
WFI(void)
{
   asm volatile ("wfi" ::: "memory");
}


/*
 *----------------------------------------------------------------------
 *
 * WFE --
 *
 *      Wait for event.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static inline void
WFE(void)
{
   asm volatile ("wfe" ::: "memory");
}


/*
 *----------------------------------------------------------------------
 *
 * SEV --
 *
 *      Generate a global event.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static inline void
SEV(void)
{
   asm volatile ("sev" ::: "memory");
}


/*
 *-----------------------------------------------------------------------------
 *
 * SetSPELx --
 *
 *    Set SP_ELx to the given value when operating with SP_EL0.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static inline void
SetSPELx(VA va)
{
   asm volatile (
      "msr     spsel, #1 \n\t"
      "mov     sp, %0    \n\t"
      "msr     spsel, #0 \n\t"
      :
      : "r" (va)
      : "memory"
   );
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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.
 *
 * Results:
 *    Quotient and remainder
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
Div643232(uint64 dividend,   // IN
          uint32 divisor,    // IN
          uint32 *quotient,  // OUT
          uint32 *remainder) // OUT
{
   *quotient = (uint32)(dividend / divisor);
   *remainder = (uint32)(dividend % divisor);
}

/*
 *-----------------------------------------------------------------------------
 *
 * 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
 *
 *-----------------------------------------------------------------------------
 */

static inline void
Div643264(uint64 dividend,   // IN
          uint32 divisor,    // IN
          uint64 *quotient,  // OUT
          uint32 *remainder) // OUT
{
   *quotient = dividend / divisor;
   *remainder = dividend % divisor;
}


/*
 *----------------------------------------------------------------------
 *
 * uint64set --
 *
 *      memset a given address with an uint64 value, count times.
 *
 * Results:
 *      Pointer to filled memory range.
 *
 * Side effects:
 *      As with memset.
 *
 *----------------------------------------------------------------------
 */

static inline void *
uint64set(void *dst, uint64 val, uint64 count)
{
   void   *tmpDst = dst;

   if (count == 0) {
      return dst;
   }

   __asm__ __volatile__ (
      // Copy two at a time
      "1:\t"
      "cmp     %1, #8\n\t"
      "b.lo    2f\n\t"
      "stp     %2, %2, [%0], #16\n\t"
      "stp     %2, %2, [%0], #16\n\t"
      "stp     %2, %2, [%0], #16\n\t"
      "stp     %2, %2, [%0], #16\n\t"
      "sub     %1, %1, #8\n\t"
      "b       1b\n"

      // Copy remaining data
      "2:\t"
      "cbz     %1, 3f\n\t"
      "str     %2, [%0], #8\n\t"
      "sub     %1, %1, #1\n\t"
      "b       2b\n"

      // One or zero values left to copy
      "3:\n\t"
      : "+r" (tmpDst), "+r" (count)
      : "r" (val)
      : "cc", "memory");

   return dst;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RDTSC_BARRIER --
 *
 *      Implements an RDTSC fence.  Instructions executed prior to the
 *      fence will have completed before the fence and all stores to
 *      memory are flushed from the store buffer.
 *
 *      On arm64, we need to do an ISB according to ARM ARM to prevent
 *      instruction reordering, and to ensure no store reordering we do a DMB.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Cause loads and stores prior to this to be globally visible, and
 *      RDTSC will not pass.
 *
 *-----------------------------------------------------------------------------
 */

static inline void
RDTSC_BARRIER(void)
{
   ISB();
   _DMB(SY);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DCacheCleanInvalidate --
 *
 *      Data Cache Clean and Invalidate to Point of Coherence range.
 *
 *      Use the typical cache line size, for simplicity.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static inline void
DCacheCleanInvalidate(VA va, uint64 len)
{
   VA dva;

   /* Clean and Invalidate D-cache to PoC. */
   for (dva = ROUNDDOWN(va, CACHELINE_SIZE);
        dva < va + len;
        dva += CACHELINE_SIZE) {
      asm volatile ("dc civac, %0" :: "r" (dva) : "memory");
   }

   /* Ensure completion. */
   _DSB(SY);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DCacheClean --
 *
 *      Data Cache Clean range to Point of Coherence.
 *
 *      Use the typical cache line size, for simplicity.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static inline void
DCacheClean(VA va, uint64 len)
{
   VA dva;

   /* Clean D-cache to PoC. */
   for (dva = ROUNDDOWN(va, CACHELINE_SIZE);
        dva < va + len;
        dva += CACHELINE_SIZE) {
      asm volatile ("dc cvac, %0" :: "r" (dva) : "memory");
   }

   /* Ensure completion of clean. */
   _DSB(SY);
}

#endif // ifdef __GNUC__

#if defined _MSC_VER
/* Until we implement Mul64x6464() with Windows intrinsics... */
#define MUL64_NO_ASM 1
#endif

#ifdef MUL64_NO_ASM
#include "mul64.h"
#else
/*
 *-----------------------------------------------------------------------------
 *
 * Mul64x6464 --
 *
 *    Unsigned integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 *
 *       Unsigned 64-bit integer multiplicand.
 *       Unsigned 64-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Unsigned 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

static inline uint64
Mul64x6464(uint64 multiplicand,
           uint64 multiplier,
           uint32 shift)
{
   if (shift == 0) {
      return multiplicand * multiplier;
   } else {
      uint64 lo, hi;

      asm("mul   %0, %2, %3" "\n\t"
          "umulh %1, %2, %3"
          : "=&r" (lo), "=r" (hi)
          : "r" (multiplicand), "r" (multiplier));
      return (hi << (64 - shift) | lo >> shift) + (lo >> (shift - 1) & 1);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Muls64x64s64 --
 *
 *    Signed integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 *
 *       Signed 64-bit integer multiplicand.
 *       Unsigned 64-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Signed 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

static inline int64
Muls64x64s64(int64 multiplicand,
             int64 multiplier,
             uint32 shift)
{
   if (shift == 0) {
      return multiplicand * multiplier;
   } else {
      uint64 lo, hi;

      asm("mul   %0, %2, %3" "\n\t"
          "smulh %1, %2, %3"
          : "=&r" (lo), "=r" (hi)
          : "r" (multiplicand), "r" (multiplier));
      return (hi << (64 - shift) | lo >> shift) + (lo >> (shift - 1) & 1);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Mul64x3264 --
 *
 *    Unsigned integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 *
 *       Unsigned 64-bit integer multiplicand.
 *       Unsigned 32-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Unsigned 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

static inline uint64
Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift)
{
   return Mul64x6464(multiplicand, multiplier, shift);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Muls64x32s64 --
 *
 *    Signed integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 *
 *       Signed 64-bit integer multiplicand.
 *       Unsigned 32-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Signed 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

static inline int64
Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
{
   return Muls64x64s64(multiplicand, multiplier, shift);
}
#endif


#if defined __cplusplus
} // extern "C"
#endif

#endif // ifndef _VM_BASIC_ASM_ARM64_H_
07070100000131000081A40000000000000000000000016822550500004641000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_basic_asm_x86.h /*********************************************************
 * Copyright (C) 1998-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#if defined __cplusplus
extern "C" {
#endif


#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

/*
 * XTEST
 *     Return TRUE if processor is in transaction region.
 *
 *  Using condition codes as output values (=@ccnz) requires gcc6 or
 *  above.  Clang does not support condition codes as output
 *  constraints.
 *
 */
#if (defined(VMM) || defined(VMKERNEL) || defined(FROBOS) || defined(ULM))
static INLINE Bool
xtest(void)
{
   Bool result;
#if defined(__clang__)
   __asm__ __volatile__("xtest\n"
                        "setnz %%al"
                        : "=a" (result) : : "cc");
#elif defined (__GNUC__)
   __asm__ __volatile__("xtest"
                        : "=@ccnz" (result) : : "cc");
#elif defined (_WIN32)
   result = _xtest();
#else
#error No xtest implementation for this compiler.
#endif
   return result;
}

#endif /* VMM || VMKERNEL || FROBOS || ULM */


/*
 * 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(void *save)
{
   __asm__ __volatile__ ("fxsave %0\n" : "=m" (*(uint8 *)save) : : "memory");
}

static INLINE void 
FXRSTOR_ES1(const void *load)
{
   __asm__ __volatile__ ("fxrstor %0\n"
                         : : "m" (*(const uint8 *)load) : "memory");
}

static INLINE void 
FXRSTOR_AMD_ES0(const void *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" (*(const uint8 *)load)
        : "ax", "memory");
}
#endif /* __GNUC__ */

/*
 * XSAVE/XRSTOR
 *     save/restore GSSE/SIMD/MMX fpu state
 *
 * The pointer passed in must be 64-byte aligned.
 * See above comment for more information.
 */
#if defined(__GNUC__) && (defined(VMM) || defined(VMKERNEL) || defined(FROBOS))

static INLINE void 
XSAVE_ES1(void *save, uint64 mask)
{
   __asm__ __volatile__ (
        "xsave %0 \n"
        : "=m" (*(uint8 *)save)
        : "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
}

static INLINE void 
XSAVEOPT_ES1(void *save, uint64 mask)
{
   __asm__ __volatile__ (
        "xsaveopt %0 \n"
        : "=m" (*(uint8 *)save)
        : "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
}

static INLINE void 
XRSTOR_ES1(const void *load, uint64 mask)
{
   __asm__ __volatile__ (
        "xrstor %0 \n"
        :
        : "m" (*(const uint8 *)load),
          "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
}

static INLINE void 
XRSTOR_AMD_ES0(const void *load, uint64 mask)
{
   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.
        "mov %%ebx, %%eax \n"
        "xrstor %1 \n"
        :
        : "m" (dummy), "m" (*(const uint8 *)load),
          "b" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "eax", "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().
 *
 * 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
{
   __asm__(
      "divl %4"
      : "=a" (*quotient),
        "=d" (*remainder)
      : "0" ((uint32)dividend),
        "1" ((uint32)(dividend >> 32)),
        "rm" (divisor)
      : "cc"
   );
}

#elif defined _MSC_VER

static INLINE void
Div643232(uint64 dividend,   // IN
          uint32 divisor,    // IN
          uint32 *quotient,  // OUT
          uint32 *remainder) // OUT
{
   __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
 *
 * 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;

   __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, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 * 
 *       Unsigned 64-bit integer multiplicand.
 *       Unsigned 32-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Unsigned 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

#if defined(__GNUC__) && !defined(MUL64_NO_ASM)

static INLINE uint64
Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift)
{
   uint64 result;
   uint32 tmp1, tmp2;
   // ASSERT(shift >= 0 && shift < 64);
  
   __asm__("mov   %%eax, %2\n\t"      // Save lo(multiplicand)
           "mov   %%edx, %%eax\n\t"   // Get hi(multiplicand)
           "mull  %4\n\t"             // p2 = hi(multiplicand) * multiplier
           "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
           "shll  $1, %%eax\n\t"      // Save lo(p1) bit 31 in CF in case shift=32
           "mov   %%edx, %%eax\n\t"   // result = hi(p2):hi(p1) >> (shift & 31)
           "mov   %1, %%edx\n\t"
           "shrdl %%edx, %%eax\n\t"
           "mov   $0, %2\n\t"
           "adcl  $0, %2\n\t"         // Get highest order bit shifted out, from CF
           "shrl  %%cl, %%edx\n\t"
           "jmp   3f\n"
        "2:\n\t"
           "xor   %2, %2\n\t"
           "shrdl %%edx, %%eax\n\t"   // result = hi(p2):hi(p1):lo(p1) >> shift
           "adcl  $0, %2\n\t"         // Get highest order bit shifted out, from CF
           "shrdl %1, %%edx\n"
        "3:\n\t"
           "addl  %2, %%eax\n\t"      // result += highest order bit shifted out
           "adcl  $0, %%edx"
           : "=A" (result), "=&r" (tmp1), "=&r" (tmp2)
           : "0" (multiplicand), "rm" (multiplier), "c" (shift)
           : "cc");
   return result;
}

#elif defined _MSC_VER
#pragma warning(disable: 4035)

static INLINE uint64
Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift)
{
   // ASSERT(shift >= 0 && shift < 64);

   __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
      shl  eax, 1                           // Save lo(p1) bit 31 in CF in case shift=32
      mov  eax, edx                         // result = hi(p2):hi(p1) >> (shift & 31)
      mov  edx, ebx
      shrd eax, edx, cl
      mov  esi, 0
      adc  esi, 0                           // Get highest order bit shifted out, from CF
      shr  edx, cl
      jmp  SHORT l3
   l2:
      Xor  esi, esi
      shrd eax, edx, cl                     // result = hi(p2):hi(p1):lo(p1) >> shift
      adc  esi, 0                           // Get highest order bit shifted out, from CF
      shrd edx, ebx, cl
   l3:
      add  eax, esi                         // result += highest order bit shifted out
      adc  edx, 0
   }
   // return with result in edx:eax
}

#pragma warning(default: 4035)
#else
#define MUL64_NO_ASM 1
#include "mul64.h"
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * Muls64x32s64 --
 *
 *    Signed integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 * 
 *       Signed 64-bit integer multiplicand.
 *       Unsigned 32-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Signed 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

#if defined(__GNUC__) && !defined(MUL64_NO_ASM)

static INLINE int64
Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
{
   int64 result;
   uint32 tmp1, tmp2;
   // ASSERT(shift >= 0 && shift < 64);

   __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
           "shll  $1, %%eax\n\t"      // Save lo(p1) bit 31 in CF in case shift=32
           "mov   %%edx, %%eax\n\t"   // result = hi(p2):hi(p1) >> (shift & 31)
           "mov   %1, %%edx\n\t"
           "shrdl %%edx, %%eax\n\t"
           "mov   $0, %2\n\t"
           "adcl  $0, %2\n\t"         // Get highest order bit shifted out from CF
           "sarl  %%cl, %%edx\n\t"
           "jmp   3f\n"
        "2:\n\t"
           "xor   %2, %2\n\t"
           "shrdl %%edx, %%eax\n\t"   // result = hi(p2):hi(p1):lo(p1) >> shift
           "adcl  $0, %2\n\t"         // Get highest order bit shifted out from CF
           "shrdl %1, %%edx\n"
        "3:\n\t"
           "addl  %2, %%eax\n\t"      // result += highest order bit shifted out
           "adcl  $0, %%edx"
           : "=A" (result), "=&r" (tmp1), "=&rm" (tmp2)
           : "0" (multiplicand), "rm" (multiplier), "c" (shift)
           : "cc");
   return result;
}

#elif defined(_MSC_VER)
#pragma warning(disable: 4035)

static INLINE int64
Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
{
   //ASSERT(shift >= 0 && shift < 64);
  
   __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
      shl  eax, 1                           // Save lo(p1) bit 31 in CF in case shift=32
      mov  eax, edx                         // result = hi(p2):hi(p1) >> (shift & 31)
      mov  edx, ebx
      shrd eax, edx, cl
      mov  esi, 0
      adc  esi, 0                           // Get highest order bit shifted out, from CF
      sar  edx, cl
      jmp  SHORT l3
   l2:
      Xor  esi, esi
      shrd eax, edx, cl                     // result = hi(p2):hi(p1):lo(p1) << shift
      adc  esi, 0                           // Get highest order bit shifted out, from CF
      shrd edx, ebx, cl
   l3:
      add  eax, esi                         // result += highest order bit shifted out
      adc  edx, 0
   }
   // return with result in edx:eax
}

#pragma warning(default: 4035)
#endif


#if defined __cplusplus
} // extern "C"
#endif

#endif // _VM_BASIC_ASM_X86_H_
   07070100000132000081A40000000000000000000000016822550500004831000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_basic_asm_x86_64.h  /*********************************************************
 * Copyright (C) 1998-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#ifndef VM_X86_64
#error "This file is x86-64 only!"
#endif

#if defined(__GNUC__)
/*
 * _GET_CURRENT_PC --
 * GET_CURRENT_PC --
 *
 * Returns the current program counter (i.e. instruction pointer i.e. rip
 * register on x86_64). In the example below:
 *
 *   foo.c
 *   L123: Foo(GET_CURRENT_PC())
 *
 * the return value from GET_CURRENT_PC will point a debugger to L123.
 */

#define _GET_CURRENT_PC(rip)                                                  \
   asm volatile("lea 0(%%rip), %0" : "=r" (rip))

static INLINE_ALWAYS void *
GET_CURRENT_PC(void)
{
   void *rip;

   _GET_CURRENT_PC(rip);
   return rip;
}

/*
 * GET_CURRENT_LOCATION --
 *
 * Updates the arguments with the values of the %rip, %rbp, and %rsp
 * registers and the return address at the current code location where
 * the macro is invoked.
 */

#define GET_CURRENT_LOCATION(rip, rbp, rsp, retAddr) do {                     \
   _GET_CURRENT_PC(rip);                                                      \
   asm volatile("mov %%rbp, %0" "\n\t"                                        \
                "mov %%rsp, %1"                                               \
                : "=r" (rbp), "=r" (rsp));                                    \
   retAddr = (uint64)GetReturnAddress();                                      \
} while (0)
#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(VMM) || defined(VMKERNEL) || defined(FROBOS) || defined(ULM)
static INLINE void
FXSAVE_ES1(void *save)
{
#ifdef __GNUC__
   __asm__ __volatile__ ("fxsaveq %0  \n" : "=m" (*(uint8 *)save) : : "memory");
#elif defined(_MSC_VER)
   _fxsave64(save);
#endif
}

static INLINE void
FXSAVE_COMPAT_ES1(void *save)
{
#ifdef __GNUC__
   __asm__ __volatile__ ("fxsave %0  \n" : "=m" (*(uint8 *)save) : : "memory");
#elif defined(_MSC_VER)
   _fxsave(save);
#endif
}

static INLINE void
FXRSTOR_ES1(const void *load)
{
#ifdef __GNUC__
   __asm__ __volatile__ ("fxrstorq %0 \n"
                         : : "m" (*(const uint8 *)load) : "memory");
#elif defined(_MSC_VER)
   _fxrstor64(load);
#endif
}

static INLINE void
FXRSTOR_COMPAT_ES1(const void *load)
{
#ifdef __GNUC__
   __asm__ __volatile__ ("fxrstor %0 \n"
                         : : "m" (*(const uint8 *)load) : "memory");
#elif defined(_MSC_VER)
   _fxrstor(load);
#endif
}

#if defined(__GNUC__)
static INLINE void
FXRSTOR_AMD_ES0(const void *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" (*(const uint8 *)load)
        : "ax", "memory");
}

#endif /* __GNUC__ */

/*
 * XSAVE/XRSTOR
 *     save/restore GSSE/SIMD/MMX fpu state
 *
 * The pointer passed in must be 64-byte aligned.
 * See above comment for more information.
 */

static INLINE void
XSAVE_ES1(void *save, uint64 mask)
{
#ifdef __GNUC__
   __asm__ __volatile__ (
        "xsaveq %0 \n"
        : "=m" (*(uint8 *)save)
        : "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
#elif defined(_MSC_VER)
   _xsave64(save, mask);
#endif
}

static INLINE void
XSAVE_COMPAT_ES1(void *save, uint64 mask)
{
#ifdef __GNUC__
   __asm__ __volatile__ (
        "xsave %0 \n"
        : "=m" (*(uint8 *)save)
        : "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
#elif defined(_MSC_VER)
   _xsave(save, mask);
#endif
}

static INLINE void
XSAVEOPT_ES1(void *save, uint64 mask)
{
#ifdef __GNUC__
   __asm__ __volatile__ (
        "xsaveoptq %0 \n"
        : "=m" (*(uint8 *)save)
        : "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
#elif defined(_MSC_VER)
   _xsaveopt64(save, mask);
#endif
}

static INLINE void
XSAVEC_COMPAT_ES1(void *save, uint64 mask)
{
#ifdef __GNUC__
   __asm__ __volatile__ (
        "xsavec %0 \n"
        : "=m" (*(uint8 *)save)
        : "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
#elif defined(_MSC_VER)
   _xsavec(save, mask);
#endif
}

static INLINE void
XRSTOR_ES1(const void *load, uint64 mask)
{
#ifdef __GNUC__
   __asm__ __volatile__ (
        "xrstorq %0 \n"
        :
        : "m" (*(const uint8 *)load),
          "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
#elif defined(_MSC_VER)
   _xrstor64(load, mask);
#endif
}

static INLINE void
XRSTOR_COMPAT_ES1(const void *load, uint64 mask)
{
#ifdef __GNUC__
   __asm__ __volatile__ (
        "xrstor %0 \n"
        :
        : "m" (*(const uint8 *)load),
          "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
#elif defined(_MSC_VER)
   _xrstor(load, mask);
#endif
}

#if defined(__GNUC__)
static INLINE void
XRSTOR_AMD_ES0(const void *load, uint64 mask)
{
   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.
        "mov %%ebx, %%eax \n"
        "xrstorq %1 \n"
        :
        : "m" (dummy), "m" (*(const uint8 *)load),
          "b" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "eax", "memory");
}

#endif /* __GNUC__ */

/*
 * XSAVES/XRSTORS
 *     saves/restores processor state components
 *
 * The pointer passed in must be 64-byte aligned.
 */

#if defined(__GNUC__)
static INLINE void
XSAVES(const void *save, uint64 mask)
{
   __asm__ __volatile__ (
        "xsaves %0 \n"
        : "=m" (*(uint8 *)save)
        : "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
}

static INLINE void
XRSTORS(const void *load, uint64 mask)
{
   __asm__ __volatile__ (
        "xrstors %0 \n"
        :
        : "m" (*(const uint8 *)load),
          "a" ((uint32)mask), "d" ((uint32)(mask >> 32))
        : "memory");
}

#endif /* __GNUC__ */
#endif /* VMM || VMKERNEL || FROBOS || ULM */

/*
 * XTEST
 *     Return TRUE if processor is in transaction region.
 *
 *  Using condition codes as output values (=@ccnz) requires gcc6 or
 *  above.  Clang does not support condition codes as output
 *  constraints.
 *
 */
#if (defined(VMM) || defined(VMKERNEL) || defined(FROBOS) || defined(ULM))
static INLINE Bool
xtest(void)
{
   Bool result;
#if defined(__clang__)
   __asm__ __volatile__("xtest\n"
                        "setnz %%al"
                        : "=a" (result) : : "cc");
#elif defined(__GNUC__)
   __asm__ __volatile__("xtest"
                        : "=@ccnz" (result) : : "cc");
#elif defined (_WIN64)
   result = _xtest();
#else
#error No xtest implementation for this compiler.
#endif
   return result;
}

#endif /* VMM || VMKERNEL || FROBOS || ULM */

/*
 *-----------------------------------------------------------------------------
 *
 * Mul64x6464 --
 *
 *    Unsigned integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 *
 *       Unsigned 64-bit integer multiplicand.
 *       Unsigned 64-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Unsigned 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

#if defined(__GNUC__) && !defined(MUL64_NO_ASM)

static INLINE uint64
Mul64x6464(uint64 multiplicand,
           uint64 multiplier,
           uint32 shift)
{
   /*
    * Implementation:
    *    Multiply 64x64 bits to yield a full 128-bit product.
    *    Clear the carry bit (needed for the shift == 0 case).
    *    Shift result in RDX:RAX right by "shift".
    *    Add the carry bit.  (If shift > 0, this is the highest order bit
    *      that was discarded by the shift; else it is 0.)
    *    Return the low-order 64 bits of the above.
    *
    */
   uint64 result, dummy;

   __asm__("mulq    %3           \n\t"
           "clc                  \n\t"
           "shrdq   %b4, %1, %0  \n\t"
           "adc     $0, %0       \n\t"
           : "=a" (result),
             "=d" (dummy)
           : "0"  (multiplier),
             "rm" (multiplicand),
             "c"  (shift)
           : "cc");
   return result;
}

#elif defined(_MSC_VER) && !defined(MUL64_NO_ASM)

static INLINE uint64
Mul64x6464(uint64 multiplicand,
           uint64 multiplier,
           uint32 shift)
{
   /*
    * Unfortunately, MSVC intrinsics don't give us access to the carry
    * flag after a 128-bit shift, so the implementation is more
    * awkward:
    *    Multiply 64x64 bits to yield a full 128-bit product.
    *    Shift result right by "shift".
    *    If shift != 0, extract and add in highest order bit that was
    *      discarded by the shift.
    *    Return the low-order 64 bits of the above.
    */
   if (shift == 0) {
      return multiplicand * multiplier;
   } else {
      uint64 lo, hi;

      lo = _umul128(multiplicand, multiplier, &hi);
      return __shiftright128(lo, hi, (uint8)shift) + (lo >> (shift - 1) & 1);
   }
}

#else
#define MUL64_NO_ASM 1
#include "mul64.h"
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * Muls64x64s64 --
 *
 *    Signed integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 *
 *       Signed 64-bit integer multiplicand.
 *       Unsigned 64-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Signed 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

#if defined(__GNUC__) && !defined(MUL64_NO_ASM)

static inline int64
Muls64x64s64(int64 multiplicand,
             int64 multiplier,
             uint32 shift)
{
   int64 result, dummy;

   /* Implementation:
    *    Multiply 64x64 bits to yield a full 128-bit product.
    *    Clear the carry bit (needed for the shift == 0 case).
    *    Shift result in RDX:RAX right by "shift".
    *    Add the carry bit.  (If shift > 0, this is the highest order bit
    *      that was discarded by the shift; else it is 0.)
    *    Return the low-order 64 bits of the above.
    *
    *    Note: using the unsigned shrd instruction is correct because
    *    shift < 64 and we return only the low 64 bits of the shifted
    *    result.
    */
   __asm__("imulq   %3           \n\t"
           "clc                  \n\t"
           "shrdq   %b4, %1, %0  \n\t"
           "adc     $0, %0       \n\t"
           : "=a" (result),
             "=d" (dummy)
           : "0"  (multiplier),
             "rm" (multiplicand),
             "c"  (shift)
           : "cc");
   return result;
}

#elif defined(_MSC_VER) && !defined(MUL64_NO_ASM)

static INLINE int64
Muls64x64s64(int64 multiplicand,
             int64 multiplier,
             uint32 shift)
{
   /*
    * Unfortunately, MSVC intrinsics don't give us access to the carry
    * flag after a 128-bit shift, so the implementation is more
    * awkward:
    *    Multiply 64x64 bits to yield a full 128-bit product.
    *    Shift result right by "shift".
    *    If shift != 0, extract and add in highest order bit that was
    *      discarded by the shift.
    *    Return the low-order 64 bits of the above.
    *
    * Note: using an unsigned shift is correct because shift < 64 and
    * we return only the low 64 bits of the shifted result.
    */
   if (shift == 0) {
      return multiplicand * multiplier;
   } else {
      int64 lo, hi;

      lo = _mul128(multiplicand, multiplier, &hi);
      return __shiftright128(lo, hi, (uint8)shift) + (lo >> (shift - 1) & 1);
   }
}

#endif

#ifndef MUL64_NO_ASM
/*
 *-----------------------------------------------------------------------------
 *
 * Mul64x3264 --
 *
 *    Unsigned integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 *
 *       Unsigned 64-bit integer multiplicand.
 *       Unsigned 32-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Unsigned 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint64
Mul64x3264(uint64 multiplicand, uint32 multiplier, uint32 shift)
{
   return Mul64x6464(multiplicand, multiplier, shift);
}

/*
 *-----------------------------------------------------------------------------
 *
 * Muls64x32s64 --
 *
 *    Signed integer by fixed point multiplication, with rounding:
 *       result = floor(multiplicand * multiplier * 2**(-shift) + 0.5)
 *
 *       Signed 64-bit integer multiplicand.
 *       Unsigned 32-bit fixed point multiplier, represented as
 *         (multiplier, shift), where shift < 64.
 *
 * Result:
 *       Signed 64-bit integer product.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE int64
Muls64x32s64(int64 multiplicand, uint32 multiplier, uint32 shift)
{
   return Muls64x64s64(multiplicand, multiplier, 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_
   07070100000133000081A4000000000000000000000001682255050000522D000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_basic_asm_x86_common.h  /*********************************************************
 * Copyright (C) 2013-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vm_basic_asm_x86_common.h --
 *
 *	Basic assembler macros common to 32-bit and 64-bit x86 ISA.
 */

#ifndef _VM_BASIC_ASM_X86_COMMON_H_
#define _VM_BASIC_ASM_X86_COMMON_H_

#define INCLUDE_ALLOW_USERLEVEL
#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
#include "includeCheck.h"

#ifndef VM_X86_ANY
#error "Should be included only in x86 builds"
#endif

#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)

static INLINE unsigned int
GetCallerEFlags(void)
{
   unsigned long flags;
   asm volatile("pushf; pop %0" : "=r"(flags));
   return flags;
}

#elif defined(_MSC_VER)
static INLINE uint8
INB(uint16 port)
{
   return __inbyte(port);
}
static INLINE void
OUTB(uint16 port, uint8 value)
{
   __outbyte(port, value);
}
static INLINE uint16
INW(uint16 port)
{
   return __inword(port);
}
static INLINE void
OUTW(uint16 port, uint16 value)
{
   __outword(port, value);
}
static INLINE  uint32
IN32(uint16 port)
{
   return __indword(port);
}
static INLINE void
OUT32(uint16 port, uint32 value)
{
   __outdword(port, value);
}

#ifndef VM_X86_64
#ifdef NEAR
#undef NEAR
#endif

#endif // VM_X86_64

static INLINE unsigned int
GetCallerEFlags(void)
{
   return __getcallerseflags();
}

#endif // __GNUC__

/* Sequence recommended by Intel for the Pentium 4. */
#define INTEL_MICROCODE_VERSION() (             \
   X86MSR_SetMSR(MSR_BIOS_SIGN_ID, 0),          \
   __GET_EAX_FROM_CPUID(1),                     \
   X86MSR_GetMSR(MSR_BIOS_SIGN_ID))


/*
 *-----------------------------------------------------------------------------
 *
 * CLFLUSH --
 *
 *      Wrapper around the CLFLUSH instruction.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      See CLFLUSH instruction in Intel SDM or AMD Programmer's Manual.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
CLFLUSH(const void *addr)
{
#ifdef __GNUC__
   __asm__ __volatile__(
      "clflush %0"
      :: "m" (*(uint8 *)addr));
#elif defined _MSC_VER
   _mm_clflush(addr);
#else
#error No compiler defined for CLFLUSH
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * MFENCE --
 *
 *      Wrapper around the MFENCE instruction.
 *
 *      Caveat Emptor! This function is _NOT_ _PORTABLE_ and most certainly
 *      not something you should use. Take a look at the SMP_*_BARRIER_*,
 *      DMA_*_BARRIER_* and MMIO_*_BARRIER_* interfaces instead, when writing
 *      general OS/VMM code.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      See MFENCE instruction in Intel SDM or AMD Programmer's Manual.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
MFENCE(void)
{
#ifdef __GNUC__
   __asm__ __volatile__(
      "mfence"
      ::: "memory"
   );
#elif defined _MSC_VER
   _ReadWriteBarrier();
   _mm_mfence();
   _ReadWriteBarrier();
#else
#error No compiler defined for MFENCE
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * LFENCE --
 *
 *      Wrapper around the LFENCE instruction.
 *
 *      Caveat Emptor! This function is _NOT_ _PORTABLE_ and most certainly
 *      not something you should use. Take a look at the SMP_*_BARRIER_*,
 *      DMA_*_BARRIER_* and MMIO_*_BARRIER_* interfaces instead, when writing
 *      general OS/VMM code.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      See LFENCE instruction in Intel SDM or AMD Programmer's Manual.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
LFENCE(void)
{
#ifdef __GNUC__
   __asm__ __volatile__(
      "lfence"
      : : : "memory"
   );
#elif defined _MSC_VER
   _ReadWriteBarrier();
   _mm_lfence();
   _ReadWriteBarrier();
#else
#error No compiler defined for LFENCE
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * SFENCE --
 *
 *      Wrapper around the SFENCE instruction.
 *
 *      Caveat Emptor! This function is _NOT_ _PORTABLE_ and most certainly
 *      not something you should use. Take a look at the SMP_*_BARRIER_*,
 *      DMA_*_BARRIER_* and MMIO_*_BARRIER_* interfaces instead, when writing
 *      general OS/VMM code.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      See SFENCE instruction in Intel SDM or AMD Programmer's Manual.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
SFENCE(void)
{
#ifdef __GNUC__
   __asm__ __volatile__(
      "sfence"
      : : : "memory"
   );
#elif defined _MSC_VER
   _ReadWriteBarrier();
   _mm_sfence();
   _ReadWriteBarrier();
#else
#error No compiler defined for SFENCE
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * RDTSC_BARRIER --
 *
 *      Implements an RDTSC fence.  Instructions executed prior to the
 *      fence will have completed before the fence and all stores to
 *      memory are flushed from the store buffer.
 *
 *      On AMD, MFENCE is sufficient.  On Intel, only LFENCE is
 *      documented to fence RDTSC, but LFENCE won't drain the store
 *      buffer.  So, use MFENCE;LFENCE, which will work on both AMD and
 *      Intel.
 *
 *      It is the callers' responsibility to check for SSE2 before
 *      calling this function.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Cause loads and stores prior to this to be globally visible, and
 *      RDTSC will not pass.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
RDTSC_BARRIER(void)
{
   MFENCE();
   LFENCE();
}


/*
 *-----------------------------------------------------------------------------
 *
 * LOCKED_INSN_BARRIER --
 *
 *      Implements a full WB load/store barrier using a locked instruction.
 *
 *      See PR 1674199 for details. You may choose to use this for
 *      performance reasons over MFENCE iff you are only dealing with
 *      WB memory accesses.
 *
 *      DANGER! Do not use this barrier instead of MFENCE when dealing
 *      with non-temporal instructions or UC/WC memory accesses.
 *
 *      Caveat Emptor! This function is _NOT_ _PORTABLE_ and most certainly
 *      not something you should use. Take a look at the SMP_*_BARRIER_*,
 *      DMA_*_BARRIER_* and MMIO_*_BARRIER_* interfaces instead, when writing
 *      general OS/VMM code.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Cause WB loads and stores before the call to be globally visible
 *      before WB loads and stores after this call.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
LOCKED_INSN_BARRIER(void)
{
   volatile long temp = 0;

#if defined __GNUC__
   __asm__ __volatile__ (
      "lock xorl $1, %0"
      : "+m" (temp)
      : /* no additional inputs */
      : "cc", "memory");
#elif defined _MSC_VER
   /*
    * Ignore warning about _InterlockedXor operation on a local variable; we are
    * using the operation for its side-effects only.
    */
   #pragma warning(suppress:28113)
   _InterlockedXor(&temp, 1);
#else
#error LOCKED_INSN_BARRIER not defined for this compiler
#endif
}

/*
 * Memory Barriers
 * ===============
 *
 *    Terminology
 *    -----------
 *
 * A compiler memory barrier prevents the compiler from re-ordering memory
 * accesses accross the barrier. It is not a CPU instruction, it is a compiler
 * directive (i.e. it does not emit any code).
 *
 * => A compiler memory barrier on its own is useful for coordinating
 *    with an interrupt handler (or preemption logic in the scheduler)
 *    on the same CPU, so that the order of read and write
 *    instructions in code that might be interrupted is consistent
 *    with the barriers. But when there are other CPUs involved, or
 *    other types of devices like memory-mapped I/O and DMA
 *    controllers, a compiler memory barrier is not enough.
 *
 * A CPU memory barrier prevents the CPU from re-ordering memory accesses
 * accross the barrier. It is a CPU instruction.
 *
 * => On its own the CPU instruction isn't useful because the compiler
 *    may reorder loads and stores around the CPU instruction.  It is
 *    useful only when combined with a compiler memory barrier.
 *
 * A memory barrier is the union of a compiler memory barrier and a CPU memory
 * barrier.
 *
 *    Semantics
 *    ---------
 *
 * At the time COMPILER_MEM_BARRIER was created (and references to it were
 * added to the code), the code was only targetting x86. The intent of the code
 * was really to use a memory barrier, but because x86 uses a strongly ordered
 * memory model, the CPU would not re-order most memory accesses (store-load
 * ordering still requires MFENCE even on x86), and the code could get away
 * with using just a compiler memory barrier. So COMPILER_MEM_BARRIER was born
 * and was implemented as a compiler memory barrier _on x86_. But make no
 * mistake, _the semantics that the code expects from COMPILER_MEM_BARRIER is
 * that of a memory barrier_!
 *
 *    DO NOT USE!
 *    -----------
 *
 * On at least one non-x86 architecture, COMPILER_MEM_BARRIER is
 * 1) A misnomer
 * 2) Not fine-grained enough to provide the best performance.
 * For the above two reasons, usage of COMPILER_MEM_BARRIER is now deprecated.
 * _Do not add new references to COMPILER_MEM_BARRIER._ Instead, precisely
 * document the intent of your code by using
 * <mem_type/purpose>_<before_access_type>_BARRIER_<after_access_type>.
 * Existing references to COMPILER_MEM_BARRIER are being slowly but surely
 * converted, and when no references are left, COMPILER_MEM_BARRIER will be
 * retired.
 *
 * Thanks for pasting this whole comment into every architecture header.
 */

#if defined __GNUC__
#   define COMPILER_MEM_BARRIER()   __asm__ __volatile__("" ::: "memory")
#elif defined _MSC_VER
#   define COMPILER_MEM_BARRIER()   _ReadWriteBarrier()
#endif

/*
 * Memory barriers. These take the form of
 *
 * <mem_type/purpose>_<before_access_type>_BARRIER_<after_access_type>
 *
 * where:
 *   <mem_type/purpose> is either INTR, SMP, DMA, or MMIO.
 *   <*_access type> is either R(load), W(store) or RW(any).
 *
 * Above every use of these memory barriers in the code, there _must_ be a
 * comment to justify the use, i.e. a comment which:
 * 1) Precisely identifies which memory accesses must not be re-ordered across
 *    the memory barrier.
 * 2) Explains why it is important that the memory accesses not be re-ordered.
 *
 * Thanks for pasting this whole comment into every architecture header.
 *
 * This is a simplified version of Table 7-3 (Memory Access Ordering Rules) from
 * AMD AMD64 Architecture Programmer's Manual Volume 2: System Programming
 * (September 2018, Publication 24593, Revision 3.30).
 *
 * https://www.amd.com/system/files/TechDocs/24593.pdf#page=228
 *
 * This table only includes the memory types we care about in the context of
 * SMP, DMA and MMIO barriers.
 *
 * +-------------+------+------+------+------+------+------+
 * |\ 2nd mem op |      |      |      |      |      |      |
 * | `---------. | R WB | R UC | R WC | W WB | W UC | W WC |
 * | 1st mem op \|      |      |      |      |      |      |
 * +-------------+------+------+------+------+------+------+
 * |    R WB     |      |      | LF1  |      |      |      |
 * +-------------+------+------+------+------+------+------+
 * |    R UC     |      |      | LF1  |      |      |      |
 * +-------------+------+------+------+------+------+------+
 * |    R WC     |      |      | LF1  |      |      |      |
 * +-------------+------+------+------+------+------+------+
 * |    W WB     | MF1  |      | MF1  |      |      | SF2  |
 * +-------------+------+------+------+------+------+------+
 * |    W UC     | MF1  |      | MF1  |      |      | SF2  |
 * +-------------+------+------+------+------+------+------+
 * |    W WC     | MF1  |      | MF1  | SF1  |      | SF2  |
 * +-------------+------+------+------+------+------+------+
 *
 * MF1 - WB or WC load may pass a previous non-conflicting WB, WC or UC store.
 *       Use MFENCE. This is a combination of rules 'e' and 'i' in the AMD
 *       diagram.
 * LF1 - WC load may pass a previous WB, WC or UC load. Use LFENCE. This is
 *       rule 'b' in the AMD diagram.
 * SF1 - WB store may pass a previous WC store. Use SFENCE. This is rule 'j' in
 *       the AMD diagram.
 * SF2 - WC store may pass a previous UC, WB or non-conflicting WC store. Use
 *       SFENCE. This is rule 'h' in the AMD diagram.
 *
 * To figure out the specific barrier required, pick and collapse the relevant
 * rows and columns, choosing the strongest barrier.
 *
 * SMP barriers only concern with access to "normal memory" (write-back cached
 * i.e. WB using above terminology), so we only need to worry about store-load
 * reordering. In other cases a compiler barrier is sufficient. SMP store-load
 * reordering is handled with a locked XOR (instead of a proper MFENCE
 * instruction) for performance reasons. See PR 1674199 for more details.
 *
 * DMA barriers are equivalent to SMP barriers on x86.
 *
 * MMIO barriers are used to mix access to different memory types, so more
 * reordering is possible, and is handled via LFENCE/SFENCE. Also, a proper
 * MFENCE must be used instead of the locked XOR trick, due to the latter
 * not guarding non-temporal/WC accesses.
 */

#define SMP_R_BARRIER_R()     INTR_R_BARRIER_R()
#define SMP_R_BARRIER_W()     INTR_R_BARRIER_W()
#define SMP_R_BARRIER_RW()    INTR_R_BARRIER_RW()
#define SMP_W_BARRIER_R()     LOCKED_INSN_BARRIER()
#define SMP_W_BARRIER_W()     INTR_W_BARRIER_W()
#define SMP_W_BARRIER_RW()    LOCKED_INSN_BARRIER()
#define SMP_RW_BARRIER_R()    LOCKED_INSN_BARRIER()
#define SMP_RW_BARRIER_W()    INTR_RW_BARRIER_W()
#define SMP_RW_BARRIER_RW()   LOCKED_INSN_BARRIER()

/*
 * Like the above, only for use with observers other than CPUs,
 * i.e. DMA masters. Same as SMP barriers for x86.
 */

#define DMA_R_BARRIER_R()     SMP_R_BARRIER_R()
#define DMA_R_BARRIER_W()     SMP_R_BARRIER_W()
#define DMA_R_BARRIER_RW()    SMP_R_BARRIER_RW()
#define DMA_W_BARRIER_R()     SMP_W_BARRIER_R()
#define DMA_W_BARRIER_W()     SMP_W_BARRIER_W()
#define DMA_W_BARRIER_RW()    SMP_W_BARRIER_RW()
#define DMA_RW_BARRIER_R()    SMP_RW_BARRIER_R()
#define DMA_RW_BARRIER_W()    SMP_RW_BARRIER_W()
#define DMA_RW_BARRIER_RW()   SMP_RW_BARRIER_RW()

/*
 * And finally a set for use with MMIO accesses. These barriers must be stronger
 * because they are used when mixing accesses to different memory types.
 */

#define MMIO_R_BARRIER_R()    LFENCE()
#define MMIO_R_BARRIER_W()    SMP_R_BARRIER_W()
#define MMIO_R_BARRIER_RW()   LFENCE()
#define MMIO_W_BARRIER_R()    MFENCE()
#define MMIO_W_BARRIER_W()    SFENCE()
#define MMIO_W_BARRIER_RW()   MFENCE()
#define MMIO_RW_BARRIER_R()   MFENCE()
#define MMIO_RW_BARRIER_W()   SFENCE()
#define MMIO_RW_BARRIER_RW()  MFENCE()


/*
 *----------------------------------------------------------------------
 *
 * MMIORead8 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      8-bit value at given location.
 *
 *----------------------------------------------------------------------
 */
static INLINE uint8
MMIORead8(const volatile void *addr)
{
   volatile uint8 *addr8 = (volatile uint8 *) addr;

   return *addr8;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite8 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */
static INLINE void
MMIOWrite8(volatile void *addr, // IN
           uint8 val)           // IN
{
   volatile uint8 *addr8 = (volatile uint8 *) addr;

   *addr8 = val;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIORead16 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      16-bit value at given location.
 *
 *----------------------------------------------------------------------
 */
static INLINE uint16
MMIORead16(const volatile void *addr)
{
   volatile uint16 *addr16 = (volatile uint16 *) addr;

   return *addr16;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite16 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */
static INLINE void
MMIOWrite16(volatile void *addr,  // IN
            uint16 val)           // IN
{
   volatile uint16 *addr16 = (volatile uint16 *) addr;

   *addr16 = val;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIORead32 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      32-bit value at given location.
 *
 *----------------------------------------------------------------------
 */
static INLINE uint32
MMIORead32(const volatile void *addr)
{
   volatile uint32 *addr32 = (volatile uint32 *) addr;

   return *addr32;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite32 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */
static INLINE void
MMIOWrite32(volatile void *addr, // OUT
            uint32 val)
{
   volatile uint32 *addr32 = (volatile uint32 *) addr;

   *addr32 = val;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIORead64 --
 *
 *      IO read from address "addr".
 *
 * Results:
 *      64-bit value at given location.
 *
 *----------------------------------------------------------------------
 */
static INLINE uint64
MMIORead64(const volatile void *addr)
{
   volatile uint64 *addr64 = (volatile uint64 *) addr;

   return *addr64;
}


/*
 *----------------------------------------------------------------------
 *
 * MMIOWrite64 --
 *
 *      IO write to address "addr".
 *
 *----------------------------------------------------------------------
 */
static INLINE void
MMIOWrite64(volatile void *addr, // OUT
            uint64 val)
{
   volatile uint64 *addr64 = (volatile uint64 *) addr;

   *addr64 = val;
}

#endif // _VM_BASIC_ASM_X86_COMMON_H_
   07070100000134000081A400000000000000000000000168225505000066CE000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_basic_defs.h    /*********************************************************
 * Copyright (c) 2003-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"
#include "vm_basic_types.h" // For INLINE.

/*
 * <stddef.h> provides definitions for:
 *   NULL, offsetof
 * References:
 *   C90 7.17, C99 7.19, C11 7.19
 */
/* Use linux/stddef.h when building Linux kernel modules. */
#ifdef KBUILD_MODNAME
#  include <linux/stddef.h>
#elif !defined(VMKERNEL)
#  include <stddef.h>
#else
   /*
    * Vmkernel's bogus __FreeBSD__ value causes gcc <stddef.h> to break.
    * Work around by doing similar things. Bug 2116887 and 2229647.
    */
#  ifndef offsetof
      /*
       * We use the builtin offset for gcc/clang, except when we're running
       * under the vmkernel's GDB macro preprocessor, since gdb doesn't
       * understand __builtin_offsetof.
       */
#     if defined VMKERNEL_GDB_MACRO_BUILDER
#        define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#     else
#        define offsetof __builtin_offsetof
#     endif
#  endif

#  ifndef NULL
#     ifdef  __cplusplus
#        define NULL    0
#     else
#        define NULL    ((void *)0)
#     endif
#  endif

#endif  // VMKERNEL

#if defined _WIN32 && defined USERLEVEL
   #include <windows.h> // for Sleep() and LOWORD() etc.
   #undef GetFreeSpace  // Unpollute preprocessor namespace.
#endif


/*
 * Simple macros
 */

#define VMW_CONTAINER_OF(ptr, type, member) \
   ((type *)((char *)(ptr) - offsetof(type, member)))

#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 VMW_CLAMP(x, min, max) \
   ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))

#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 VMKERNEL || defined VMKBOOT
# define CEIL(_a, _b)        CEILING(_a, _b)
# define FLOOR(_a, _b)       ((_a)/(_b))
# define ALIGN_DOWN(_a, _b)  ROUNDDOWN(_a, _b)
# define ALIGN_UP(_a, _b)    ROUNDUP(_a, _b)
# define IS_ALIGNED(_a, _b)  (ALIGN_DOWN(_a, _b) == _a)
#endif

#if defined __APPLE__
#include <machine/param.h>
#undef MASK
#include <mach/machine/vm_param.h>
#undef PAGE_SHIFT
#undef PAGE_SIZE
#undef PAGE_MASK
#endif

/*
 * The MASK macro behaves badly when given negative numbers or numbers larger
 * than the highest order bit number (e.g. 32 on a 32-bit machine) as an
 * argument. The range 0..31 is safe.
 */

#define MASK(n)      ((1 << (n)) - 1)            /* make an n-bit mask */
#define MASK64(n)    ((CONST64U(1) << (n)) - 1)  /* make an n-bit mask */
#define MASK128(n)   (((uint128)1 << (n)) - 1)   /* make an n-bit mask */
/*
 * MASKRANGE64 makes a bit vector starting at bit lo and ending at bit hi.  No
 * checking for lo < hi is done.
 */
#define MASKRANGE64(hi, lo)      (MASK64((hi) - (lo) + 1) << (lo))

/* SIGNEXT64 sign extends a n-bit value to 64-bits. */
#define SIGNEXT64(val, n)       (((int64)(val) << (64 - (n))) >> (64 - (n)))

#define DWORD_ALIGN(x)          ((((x) + 3) >> 2) << 2)
#define QWORD_ALIGN(x)          ((((x) + 7) >> 3) << 3)

#define IMPLIES(a,b) (!(a) || (b))


/*
 * 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
#ifndef XSTR
#define XSTR(x)                 MAKESTR(x)
#endif


/*
 * Wide versions of string constants.
 */

#ifndef WSTR
#define WSTR_(X)     L ## X
#define WSTR(X)      WSTR_(X)
#endif


/*
 * Page operations
 *
 * It has been suggested that these definitions belong elsewhere
 * (like cpu_types.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
 */

#define PAGE_SHIFT_4KB   12
#define PAGE_SHIFT_16KB  14
#define PAGE_SHIFT_64KB  16

#define PAGE_SIZE_4KB    4096
#define PAGE_SIZE_16KB   16384
#define PAGE_SIZE_64KB   65536

#if defined __x86_64__ || defined __i386__
   #define VMW_PAGE_SHIFT PAGE_SHIFT_4KB
   #define VMW_PAGE_SIZE  PAGE_SIZE_4KB
#elif defined __APPLE__
   #if defined VM_ARM_ANY
      #define VMW_PAGE_SHIFT PAGE_SHIFT_16KB
      #define VMW_PAGE_SIZE  PAGE_SIZE_16KB
   #else
      #define VMW_PAGE_SHIFT PAGE_SHIFT_4KB
      #define VMW_PAGE_SIZE  PAGE_SIZE_4KB
   #endif
#elif defined VM_ARM_64
   #define VMW_PAGE_SHIFT PAGE_SHIFT_4KB
   #define VMW_PAGE_SIZE  PAGE_SIZE_4KB
#elif defined __arm__
   #define VMW_PAGE_SHIFT PAGE_SHIFT_4KB
   #define VMW_PAGE_SIZE  PAGE_SIZE_4KB
#elif defined __wasm__
   #define VMW_PAGE_SHIFT PAGE_SHIFT_4KB
   #define VMW_PAGE_SIZE  PAGE_SIZE_4KB
#else
   #error Could not determine page size information for this compiler.
#endif

#ifndef PAGE_SHIFT
#define PAGE_SHIFT VMW_PAGE_SHIFT
#endif

#ifndef PAGE_SIZE
#define PAGE_SIZE     VMW_PAGE_SIZE
#endif

#define PAGE_MASK_4KB    (PAGE_SIZE_4KB - 1)
#define PAGE_MASK_16KB   (PAGE_SIZE_16KB - 1)
#define PAGE_MASK_64KB   (PAGE_SIZE_64KB - 1)

#ifndef PAGE_MASK
#define PAGE_MASK     (PAGE_SIZE - 1)
#endif

#define PAGE_OFFSET_4KB(_addr)   ((uintptr_t)(_addr) & (PAGE_SIZE_4KB - 1))
#define PAGE_OFFSET_16KB(_addr)  ((uintptr_t)(_addr) & (PAGE_SIZE_16KB - 1))
#define PAGE_OFFSET_64KB(_addr)  ((uintptr_t)(_addr) & (PAGE_SIZE_64KB - 1))

#ifndef PAGE_OFFSET
#define PAGE_OFFSET(_addr)  ((uintptr_t)(_addr) & (PAGE_SIZE - 1))
#endif

#ifndef PAGE_NUMBER
#define PAGE_NUMBER(_addr)  ((uintptr_t)(_addr) / PAGE_SIZE)
#endif

#ifndef BYTES_2_PAGES
#define BYTES_2_PAGES(_nbytes)  ((_nbytes) >> PAGE_SHIFT)
#endif

#ifndef BYTES_2_PAGES_4KB
#define BYTES_2_PAGES_4KB(_nbytes)  ((_nbytes) >> PAGE_SHIFT_4KB)
#endif

#ifndef PAGES_2_BYTES
#define PAGES_2_BYTES(_npages)  (((uint64)(_npages)) << PAGE_SHIFT)
#endif

#ifndef VM_PAGE_BASE
#define VM_PAGE_BASE(_addr)  ((_addr) & ~(PAGE_SIZE - 1))
#endif

#ifndef VM_PAGES_SPANNED
#define VM_PAGES_SPANNED(_addr, _size) \
   (BYTES_2_PAGES(PAGE_OFFSET(_addr) + PAGE_OFFSET(_size) + (PAGE_SIZE - 1)) + \
    BYTES_2_PAGES(_size))
#endif

#ifndef KBYTES_SHIFT
#define KBYTES_SHIFT 10
#endif

#ifndef MBYTES_SHIFT
#define MBYTES_SHIFT 20
#endif

#ifndef GBYTES_SHIFT
#define GBYTES_SHIFT 30
#endif

#ifndef KBYTES_2_PAGES
#define KBYTES_2_PAGES(_nkbytes) \
   ((uint64)(_nkbytes) >> (PAGE_SHIFT - KBYTES_SHIFT))
#endif

#ifndef MBYTES_2_PAGES
#define MBYTES_2_PAGES(_nMbytes) \
   ((uint64)(_nMbytes) << (MBYTES_SHIFT - PAGE_SHIFT))
#endif

#ifndef MBYTES_2_PAGES_4KB
#define MBYTES_2_PAGES_4KB(_nMbytes) \
   ((uint64)(_nMbytes) << (MBYTES_SHIFT - PAGE_SHIFT_4KB))
#endif

#ifndef PAGES_2_KBYTES
#define PAGES_2_KBYTES(_npages) ((_npages) << (PAGE_SHIFT - KBYTES_SHIFT))
#endif

#ifndef PAGES_2_MBYTES
#define PAGES_2_MBYTES(_npages) ((_npages) >> (MBYTES_SHIFT - PAGE_SHIFT))
#endif

#ifndef ROUNDUP_PAGES_2_MBYTES
#define ROUNDUP_PAGES_2_MBYTES(_npages) \
(((_npages) + MASK(MBYTES_SHIFT - PAGE_SHIFT)) >> (MBYTES_SHIFT - PAGE_SHIFT))
#endif

#ifndef ROUNDDOWN_PAGES_2_MBYTES
#define ROUNDDOWN_PAGES_2_MBYTES(_npages) \
((_npages) >> (MBYTES_SHIFT - PAGE_SHIFT))
#endif

#ifndef GBYTES_2_PAGES
#define GBYTES_2_PAGES(_nGbytes) \
   ((uint64)(_nGbytes) << (GBYTES_SHIFT - PAGE_SHIFT))
#endif

#ifndef PAGES_2_GBYTES
#define PAGES_2_GBYTES(_npages) ((_npages) >> (GBYTES_SHIFT - PAGE_SHIFT))
#endif

#ifndef BYTES_2_KBYTES
#define BYTES_2_KBYTES(_nbytes) ((_nbytes) >> KBYTES_SHIFT)
#endif

#ifndef KBYTES_2_BYTES
#define KBYTES_2_BYTES(_nkbytes) ((uint64)(_nkbytes) << KBYTES_SHIFT)
#endif

#ifndef BYTES_2_MBYTES
#define BYTES_2_MBYTES(_nbytes) ((_nbytes) >> MBYTES_SHIFT)
#endif

#ifndef MBYTES_2_BYTES
#define MBYTES_2_BYTES(_nMbytes) ((uint64)(_nMbytes) << MBYTES_SHIFT)
#endif

#ifndef BYTES_2_GBYTES
#define BYTES_2_GBYTES(_nbytes) ((_nbytes) >> GBYTES_SHIFT)
#endif

#ifndef GBYTES_2_BYTES
#define GBYTES_2_BYTES(_nGbytes) ((uint64)(_nGbytes) << GBYTES_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

#ifndef VM_PAE_LARGE_2_BYTES
#define VM_PAE_LARGE_2_BYTES(_2mbytes) ((_2mbytes) << VM_PAE_LARGE_PAGE_SHIFT)
#endif

#ifndef VM_1GB_PAGE_SHIFT
#define VM_1GB_PAGE_SHIFT 30
#endif

#ifndef VM_1GB_PAGE_SIZE
#define VM_1GB_PAGE_SIZE (1 << VM_1GB_PAGE_SHIFT)
#endif

#ifndef VM_1GB_2_PAGES
#define VM_1GB_2_PAGES (BYTES_2_PAGES(VM_1GB_PAGE_SIZE))
#endif

#ifndef VM_1GB_2_PDIRS
#define VM_1GB_2_PDIRS (VM_1GB_PAGE_SIZE / 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

#ifndef HIDWORD
#define HIDWORD(_qw)   ((uint32)((_qw) >> 32))
#endif
#ifndef LODWORD
#define LODWORD(_qw)   ((uint32)(_qw))
#endif
#define QWORD(_hi, _lo)   ((((uint64)(_hi)) << 32) | ((uint32)(_lo)))

#ifndef HIDWORD128
#define HIDWORD128(_qw)    ((uint64)((_qw) >> 64))
#endif
#ifndef LODWORD128
#define LODWORD128(_qw)    ((uint64)(_qw))
#endif
#ifndef QWORD128
#define QWORD128(_hi, _lo) ((((uint128)(_hi)) << 64) | ((uint64)(_lo)))
#endif


/*
 * 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__
#define GetFrameAddr() __builtin_frame_address(0)
#endif // __GNUC__

#ifdef __GNUC__
#  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 */)
#endif /* __GNUC__ */


#ifdef USERLEVEL // {

/*
 * Standardize some Posix names on Windows.
 */

#ifdef _WIN32 // {

/* Conflict with definition of Visual Studio 2015 */
#if (_MSC_VER < 1900)
#define snprintf  _snprintf
#endif

#define strtok_r  strtok_s

typedef int uid_t;
typedef int gid_t;

static INLINE void
sleep(unsigned int sec)
{
   Sleep(sec * 1000);
}

static INLINE int
usleep(unsigned long usec)
{
   Sleep(CEILING(usec, 1000));

   return 0;
}

typedef int pid_t;
#define       F_OK          0
#define       X_OK          1
#define       W_OK          2
#define       R_OK          4

#endif // } _WIN32

#endif // } USERLEVEL

#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)

// The macOS kernel SDK defines va_copy in stdarg.h.
#include <stdarg.h>

#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.
 */

#if defined(_WIN32) && defined(_MSC_VER)
#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 definitions of unicode characters.
 */

#ifndef UTF8_ELLIPSIS
#define UTF8_ELLIPSIS "\xe2\x80\xa6"
#endif

/*
 * Convenience macros and definitions. Can often be used instead of #ifdef.
 */

#ifdef VMK_HAS_VMM
#define VMK_HAS_VMM_ONLY(...) __VA_ARGS__
#else
#define VMK_HAS_VMM_ONLY(...)
#endif

#if defined VMM || defined VMK_HAS_VMM
/* Structure field only used to support the VMM (as opposed to the ULM). */
#define VMM_ONLY_FIELD(name) name
#else
/*
 * Structure field only used to support the VMM (as opposed to the ULM).
 * Until VMK_HAS_VMM is retired, keep this field so the size of the structure
 * is unchanged (was bug 3354277), but prepend an underscore to the field's
 * name to verify at compile time that the field is indeed not used.
 */
#define VMM_ONLY_FIELD(name) _##name
#endif

#undef ARM64_ONLY
#if defined(_MSC_VER)
/*
 * Old MSVC versions (such as MSVC 14.29.30133, used to build Workstation's
 * offset checker) are notorious to have non-standard __VA_ARGS__ handling.
 */
#if defined(VMX86_DESKTOP) && (_MSC_VER > 1929)
#pragma message("ERROR: Compiler version: " XSTR(_MSC_VER))
#pragma message("ERROR: PR 3405101: Is __VA_ARGS__ hack needed for Arm & x86?")
#endif
#ifdef VM_ARM_64
#define ARM64_ONLY(x) x
#else
#define ARM64_ONLY(x)
#endif
#else
#ifdef VM_ARM_64
#define ARM64_ONLY(...)  __VA_ARGS__
#else
#define ARM64_ONLY(...)
#endif
#endif

#undef X86_ONLY
#ifdef _MSC_VER
/*
 * Old MSVC versions (such as MSVC 14.29.30133, used to build Workstation's
 * offset checker) are notorious to have non-standard __VA_ARGS__ handling.
 */
#if defined(VMX86_DESKTOP) && (_MSC_VER > 1929)
#pragma message("ERROR: Compiler version: " XSTR(_MSC_VER))
#pragma message("ERROR: PR 3405101: Is __VA_ARGS__ hack needed for Arm & x86?")
#endif
#ifdef VM_X86_ANY
#define X86_ONLY(x)      x
#else
#define X86_ONLY(x)
#endif
#else
#ifdef VM_X86_ANY
#define X86_ONLY(...)    __VA_ARGS__
#else
#define X86_ONLY(...)
#endif
#endif

#undef DEBUG_ONLY
#ifdef VMX86_DEBUG
#define vmx86_debug      1
#define DEBUG_ONLY(...)  __VA_ARGS__
#else
#define vmx86_debug      0
#define DEBUG_ONLY(...)
#endif

#if defined(VMX86_DEBUG) || defined(VMX86_ENABLE_SPLOCK_STATS)
#define LOCK_STATS_ON
#define LOCK_STATS_ONLY(...)  __VA_ARGS__
#else
#define LOCK_STATS_ONLY(...)
#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_BETA
#define vmx86_beta     1
#define BETA_ONLY(x)   x
#else
#define vmx86_beta     0
#define BETA_ONLY(x)
#endif

#ifdef VMX86_RELEASE
#define vmx86_release   1
#define RELEASE_ONLY(x) x
#else
#define vmx86_release   0
#define RELEASE_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_FDM
#define vmx86_fdm 1
#else
#define vmx86_fdm 0
#endif

#ifdef VMX86_ESXIO
#define vmx86_esxio      1
#else
#define vmx86_esxio      0
#endif

#ifdef VMKERNEL
#define vmkernel 1
#define VMKERNEL_ONLY(x) x
#else
#define vmkernel 0
#define VMKERNEL_ONLY(x)
#endif

#ifdef COMP_TEST
#define vmx86_test   1
#else
#define vmx86_test   0
#endif

/*
 * In MSVC, _WIN32 is defined as 1 when the compilation target is
 * 32-bit ARM, 64-bit ARM, x86, or x64 (which implies _WIN64). This
 * is documented in C/C++ preprocessor section of the Microsoft C++,
 * C, and Assembler documentation (https://via.vmw.com/EchK).
 */

#ifdef _WIN32
#define WIN32_ONLY(x) x
#define POSIX_ONLY(x)
#define vmx86_win32 1
#else
#define WIN32_ONLY(x)
#define POSIX_ONLY(x) x
#define vmx86_win32 0
#endif

#ifdef __linux__
#define vmx86_linux 1
#define LINUX_ONLY(x) x
#else
#define vmx86_linux 0
#define LINUX_ONLY(x)
#endif

#ifdef __APPLE__
#define vmx86_apple 1
#define APPLE_ONLY(x) x
#else
#define vmx86_apple 0
#define APPLE_ONLY(x)
#endif

#if defined(__APPLE__) && defined(VMW_APPLE_SANDBOX)
#define vmw_apple_sandbox 1
#else
#define vmw_apple_sandbox 0
#endif

#if defined(__APPLE__) && defined(VMW_APPLE_APP_STORE)
#define vmw_apple_app_store 1
#else
#define vmw_apple_app_store 0
#endif

#ifdef VMM
#define vmx86_vmm 1
#define VMM_ONLY(x) x
#else
#define vmx86_vmm 0
#define VMM_ONLY(x)
#endif

#ifdef VMX86_VMX
#define vmx86_vmx 1
#else
#define vmx86_vmx 0
#endif

#ifdef VMM_BOOTSTRAP
#define vmm_bootstrap 1
#else
#define vmm_bootstrap 0
#endif

#ifdef ULM
#define vmx86_ulm 1
#define ULM_ONLY(x) x
#ifdef ULM_MAC
#define ulm_mac 1
#else
#define ulm_mac 0
#endif
#ifdef ULM_WIN
#define ulm_win 1
#else
#define ulm_win 0
#endif
#ifdef ULM_ESX
#define ulm_esx 1
#else
#define ulm_esx 0
#endif
#else
#define vmx86_ulm 0
#define ulm_mac 0
#define ulm_win 0
#define ulm_esx 0
#define ULM_ONLY(x)
#endif

#if defined(VMM) || defined(ULM)
#define MONITOR_ONLY(x) x
#else
#define MONITOR_ONLY(x)
#endif

#if defined(VMM) || defined(VMKERNEL)
#define USER_ONLY(x)
#else
#define USER_ONLY(x) x
#endif

#ifdef _WIN32
#define VMW_INVALID_HANDLE INVALID_HANDLE_VALUE
#else
#define VMW_INVALID_HANDLE (-1LL)
#endif

#ifdef _WIN32
#define fsync(fd) _commit(fd)
#define fileno(f) _fileno(f)
#else
#endif

/*
 * Debug output macros for Windows drivers (the Eng variant is for
 * display/printer drivers only.
 */
#ifdef _WIN32
#if defined(VMX86_LOG)
#ifdef _WIN64
#define WinDrvPrint(arg, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, (ULONG)~0, arg, __VA_ARGS__)
#else
#define WinDrvPrint(arg, ...) DbgPrint(arg, __VA_ARGS__)
#endif
#define WinDrvEngPrint(arg, ...) EngDbgPrint(arg, __VA_ARGS__)
#else
#define WinDrvPrint(arg, ...)
#define WinDrvEngPrint(arg, ...)
#endif
#endif // _WIN32

/*
 * Use to initialize cbSize for this structure to preserve < Vista
 * compatibility.
 */
#define NONCLIENTMETRICSINFO_V1_SIZE CCSIZEOF_STRUCT(NONCLIENTMETRICS, \
                                                     lfMessageFont)

/* This is not intended to be thread-safe. */
#ifndef KBUILD_MODNAME
#define DO_ONCE(code)                                                   \
   do {                                                                 \
      static MONITOR_ONLY(PERVCPU) Bool _doOnceDone = FALSE;            \
      if (UNLIKELY(!_doOnceDone)) {                                     \
         _doOnceDone = TRUE;                                            \
         code;                                                          \
      }                                                                 \
   } while (0)
#endif

/*
 * Bug 827422 and 838523.
 */

#if defined __GNUC__
#define VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
#else
#define VISIBILITY_HIDDEN /* nothing */
#endif


/*
 * Bitfield extraction.
 */

#define EXTRACT_BITSLICE32(_val , _lsb, _msb)  \
   (((uint32)(_val) << (31 - (_msb))) >> ((31 - (_msb)) + (_lsb)))
#define EXTRACT_BITFIELD32(_val, _pos, _len) \
   EXTRACT_BITSLICE32((_val), (_pos), ((_pos) + (_len) - 1))
#define EXTRACT_BITSLICE64(_val, _lsb, _msb) \
   (((uint64)(_val) << (63 - (_msb))) >> ((63 - (_msb)) + (_lsb)))
#define EXTRACT_BITFIELD64(_val, _pos, _len) \
   EXTRACT_BITSLICE64((_val), (_pos), ((_pos) + (_len) - 1))

/*
 * Typical cache line size.  Use this for aligning structures to cache
 * lines for performance, but do not rely on it for correctness.
 *
 * On x86, all current processors newer than P4 have 64-byte lines,
 * but P4 had 128.
 *
 * On ARM, the line size can vary between cores.  64-byte lines are
 * common, but either larger or smaller powers of two are possible.
 */
#define CACHELINE_SIZE             64
#define CACHELINE_SHIFT            6
#define CACHELINE_ALIGNMENT_MASK   (CACHELINE_SIZE - 1)


/*
 * Bits to bytes sizes.
 */

#define SIZE_8BIT   1
#define SIZE_16BIT  2
#define SIZE_24BIT  3
#define SIZE_32BIT  4
#define SIZE_48BIT  6
#define SIZE_64BIT  8
#define SIZE_80BIT  10
#define SIZE_128BIT 16
#define SIZE_256BIT 32
#define SIZE_512BIT 64

/*
 * Allocate a variable of type _type, aligned to _align bytes, returning a
 * pointer to the variable in _var.  Potentially _align - 1 bytes may be
 * wasted.  On x86, GCC 6.3.0 behaves sub-optimally when variables are declared
 * on the stack using the aligned attribute, so this pattern is preferred.
 * See PRs 1795155, 1819963.
 *
 * GCC9 has been shown to exhibit aliasing issues when using
 * '-fstrict-aliasing=2' that did not happen under GCC6 with this
 * construct.
 * See @9503890, PR 2906490.
 */
#define WITH_PTR_TO_ALIGNED_VAR(_type, _align, _var)                     \
   do {                                                                  \
      uint8 _buf_##_var[sizeof(_type) + (_align) - 1];                   \
      _type *_var = (_type *) ((uintptr_t)(_buf_##_var + (_align) - 1) & \
                               ~((uintptr_t) ((_align) - 1)));

#define END_PTR_TO_ALIGNED_VAR \
   } while (0)


/*
 * -Wswitch means that when you pass switch an enum that it's looking for
 * all values from that enum, and only that enum, to be accounted for.
 * "default:;" is fine for catching values you don't care about. But today
 * we have a bunch of code that uses internal and external enum values, or
 * in other words combines two enums into a single variable. This cast is
 * the workaround, but we really need to fix this mess.
 */
#define UNCHECKED_SWITCH__FIXME(x) switch ((uint64)(x))


/*
 * When clang static analyzer parses source files, it implicitly defines
 * __clang_analyzer__ macro. We use this to define our custom macro to stop
 * its execution for the current path of analysis by calling a function that
 * doesn't return, making it think that it hit a failed assertion.
 *
 * DO NOT use to silence the analyzer! See PR2447238.
 */
#ifdef __clang_analyzer__
#define VMW_CLANG_ANALYZER_NORETURN() Panic("Disable Clang static analyzer")
#else
#define VMW_CLANG_ANALYZER_NORETURN() ((void)0)
#endif

/* VMW_FALLTHROUGH
 *
 *   Instructs capable compilers to not warn when a case label of a
 *   'switch' statement falls through to the next label.
 *
 *   If not a matched compiler, expands to nothing.
 */
#if (defined(__GNUC__) && (__GNUC__ >= 9)) ||           \
    (defined(__clang__) && (__clang_major__ >= 13))
#define VMW_FALLTHROUGH() __attribute__((fallthrough))
#else
#define VMW_FALLTHROUGH()
#endif

#endif // ifndef _VM_BASIC_DEFS_H_
  07070100000135000081A4000000000000000000000001682255050000753E000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_basic_types.h   /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vm_basic_types.h --
 *
 *      Basic data types.
 */

#ifndef _VM_BASIC_TYPES_H_
#define _VM_BASIC_TYPES_H_

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

/*
 * Standardize MSVC arch macros to GCC arch macros.
 */
#if defined _MSC_VER && defined _M_X64 && !defined _M_ARM64EC
#  define __x86_64__ 1
#elif defined _MSC_VER && defined _M_IX86
#  define __i386__ 1
#elif defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM64EC)
#  define __aarch64__ 1
#elif defined _MSC_VER && defined _M_ARM
#  define __arm__ 1
#endif

/*
 * Apple/Darwin uses __arm64__, but defines the more standard
 * __aarch64__ too. Code below assumes __aarch64__.
 */
#if defined __arm64__ && !defined __aarch64__
#  error Unexpected: defined __arm64__ without __aarch64__
#endif

/*
 * Setup a bunch of defines for instruction set architecture (ISA) related
 * properties.
 *
 * For compiler types/size:
 *
 * - VM_32BIT for a 32-bit ISA (with the same C compiler types/sizes as 32-bit
 *   x86/ARM).
 * - VM_64BIT for a 64-bit ISA (with the same C compiler types/sizes as 64-bit
 *   x86/ARM).
 *
 * For a given <arch> in {X86, ARM}:
 *
 * - VM_<arch>_32 for the 32-bit variant.
 * - VM_<arch>_64 for the 64-bit variant.
 * - VM_<arch>_ANY for any variant of <arch>.
 */

#ifdef __i386__
#define VM_X86_32
#define VM_X86_ANY
#define VM_32BIT
#endif

#ifdef __x86_64__
#define VM_X86_64
#define vm_x86_64 1
#define VM_X86_ANY
#define VM_64BIT
#else
#define vm_x86_64 0
#endif

#ifdef __arm__
#define VM_ARM_32
#define VM_ARM_ANY
#define VM_32BIT
#endif

#ifdef __aarch64__
#define VM_ARM_64
#define vm_arm_64 1
#define VM_ARM_ANY
#define VM_64BIT
#else
#define vm_arm_64 0
#endif

#ifdef VM_ARM_ANY
#define vm_arm_any 1
#else
#define vm_arm_any 0
#endif

#ifdef VM_X86_ANY
#define vm_x86_any 1
#else
#define vm_x86_any 0
#endif

#if defined(__APPLE__) && defined(VM_ARM_64)
#define VM_MAC_ARM
#define vm_mac_arm 1
#else
#define vm_mac_arm 0
#endif

#define vm_64bit (sizeof (void *) == 8)

#ifdef _MSC_VER

#pragma warning (3 :4505) // unreferenced local function
#pragma warning (disable :4018) // signed/unsigned mismatch
#pragma warning (suppress:4619) // suppress warning next line (C4761 was removed in vs2019u4)
#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'
#pragma warning (disable :4146) // unary minus operator applied to unsigned type, result still unsigned
#pragma warning (disable :4142) // benign redefinition of type

#endif

#if defined(__FreeBSD__) && (__FreeBSD__ + 0 < 5)
#  error FreeBSD detected without major version (PR 2116887)
#endif

/*
 * C99 <stdint.h> or equivalent
 * Userlevel: 100% <stdint.h>
 * - gcc-4.5 or later, and earlier for some sysroots
 * - vs2010 or later
 * Kernel: <stdint.h> is often unavailable (and no common macros)
 * - Linux: uses <linux/types.h> instead
 *   (and defines uintptr_t since 2.6.24, but not intptr_t)
 * - Solaris: conflicts with gcc <stdint.h>, but has <sys/stdint.h>
 * - VMKernel + FreeBSD combination collides with gcc <stdint.h>,
 *   but has <sys/stdint.h>
 * - Windows: some types in <crtdefs.h>, no definitions for other types.
 *
 * NB about LLP64 in LP64 environments:
 * - Apple uses 'long long' uint64_t
 * - Linux kernel uses 'long long' uint64_t
 * - Linux userlevel uses 'long' uint64_t
 * - Windows uses 'long long' uint64_t
 */
#if !defined(VMKERNEL) &&  \
    defined(__linux__) && defined(__KERNEL__)
#  include <linux/types.h>
#  include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
   typedef unsigned long uintptr_t;
#endif
   typedef   signed long  intptr_t;
#elif (defined(__sun__) && defined(_KERNEL)) || \
      (defined(VMKERNEL) && defined(__FreeBSD__)) || \
      defined(_SYS_STDINT_H_)
#  include <sys/stdint.h>
#elif defined(_MSC_VER) && defined(_KERNEL_MODE)
   /* Windows driver headers (km/crt) lack stdint.h */
#  include <crtdefs.h>  // uintptr_t
   typedef unsigned __int64   uint64_t;
   typedef unsigned int       uint32_t;
   typedef unsigned short     uint16_t;
   typedef unsigned char      uint8_t;

   typedef __int64            int64_t;
   typedef int                int32_t;
   typedef short              int16_t;
   typedef signed char        int8_t;
#else
   /* Common case */
#  include <stdint.h>
#endif

/*
 * size_t and ssize_t, or equivalent
 * Options:
 * C90+ <stddef.h> has size_t, but is incompatible with many kernels.
 * POSIX <sys/types.h> has size_t and ssize_t, is always available at
 *    userlevel but is missing from some kernels.
 *
 * Special cases:
 * - Linux kernel (again) does everything via <linux/types.h>
 * - VMKernel may or may not have POSIX headers (tcpip only)
 * - VMM does not have POSIX headers
 * - Windows <sys/types.h> does not define ssize_t
 */
#if defined(VMKERNEL) || defined(VMM)
   /* Guard against FreeBSD <sys/types.h> collison. */
#  if !defined(_SIZE_T_DEFINED) && !defined(_SIZE_T)
#     define _SIZE_T_DEFINED
#     define _SIZE_T
      typedef __SIZE_TYPE__ size_t;
#endif
#  if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T)
#     define _SSIZE_T_DEFINED
#     define _SSIZE_T
      typedef int64_t ssize_t;
#  endif
#elif defined(__linux__) && defined(__KERNEL__)
   /* <linux/types.h> provided size_t, ssize_t. */
#else
#  include <sys/types.h>
#  if defined(_WIN64)
      typedef int64_t ssize_t;
#  elif defined(_WIN32)
      typedef int32_t ssize_t;
#  endif
#endif


typedef uint64_t    uint64;
typedef  int64_t     int64;
typedef uint32_t    uint32;
typedef  int32_t     int32;
typedef uint16_t    uint16;
typedef  int16_t     int16;
typedef  uint8_t     uint8;
typedef   int8_t      int8;


/*
 * The _XTYPEDEF_BOOL guard prevents colliding with:
 * <X11/Xlib.h> #define Bool int
 * <X11/Xdefs.h> typedef int Bool;
 * If using this header AND X11 headers, be sure to #undef Bool and
 * be careful about the different size.
 */
#if !defined(_XTYPEDEF_BOOL)
#define _XTYPEDEF_BOOL
/*
 * C does not specify whether char is signed or unsigned, and
 * both gcc and msvc implement it as a non-signed, non-unsigned type.
 * Thus, (uint8_t *)&Bool and (int8_t *)&Bool are possible compile errors.
 * This is intentional.
 */
typedef char           Bool;
#endif

#ifndef FALSE
#define FALSE          0
#endif

#ifndef TRUE
#define TRUE           1
#endif

#define IS_BOOL(x)     (((x) & ~1) == 0)


/*
 * 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 <inttypes.h>
#endif
#ifdef HAVE_SYS_INTTYPES_H
#include <sys/inttypes.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#ifdef __FreeBSD__
#include <sys/param.h> /* For __FreeBSD_version */
#endif

#if !defined(USING_AUTOCONF)
#   if defined(__FreeBSD__) || defined(sun)
#      ifndef KLD_MODULE
#         if defined(__FreeBSD__)
#            if !defined(VMKERNEL)
#               include <inttypes.h>
#            endif
#         else /* sun */
#            include <sys/inttypes.h>
#         endif
#      endif
#   elif defined __APPLE__
#      if KERNEL
#         include <sys/unistd.h>
#      else
#         include <unistd.h>
#         include <inttypes.h>
#         include <stdlib.h>
#      endif
#   endif
#endif


#if defined __GNUC__ && defined __SIZEOF_INT128__
#define VM_HAS_INT128 // 128-bit integers can be used.

typedef unsigned __int128 uint128;
typedef          __int128  int128;

#define MIN_INT128   ((int128)1 << 127)
#define MAX_INT128   (~MIN_INT128)
#define MIN_UINT128  ((uint128)0)
#define MAX_UINT128  (~MIN_UINT128)
#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);
 * The spaces are important for C++11 compatibility.
 *
 * FMTH is for handles/fds.
 */

#ifdef _MSC_VER
   /* MSVC added C99-compatible formatting in vs2015. */
   #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 defined __APPLE__ || defined __EMSCRIPTEN__ || \
      (!defined VMKERNEL && defined __linux__ && defined __KERNEL__)
   /* semi-LLP64 targets; 'long' is 64-bit, but uint64_t is 'long long' */
   #define FMT64         "ll"
   #if defined(__APPLE__) && KERNEL
      /* macOS osfmk/kern added 'z' length specifier in 10.13 */
      #define FMTSZ      "l"
   #else
      #define FMTSZ      "z"
   #endif
   #define FMTPD         "l"
   #define FMTH          ""
#elif defined __GNUC__
   /*
    * Every POSIX system we target has C99-compatible printf
    * (supports 'z' for size_t and 'll' for long long).
    */
   #define FMTH          ""
   #define FMTSZ         "z"
   #if defined(VM_X86_64) || defined(VM_ARM_64)
      #define FMT64      "l"
      #define FMTPD      "l"
   #else
      #define FMT64      "ll"
      #define FMTPD      ""
   #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.
 */

#if !defined(CONST64) || !defined(CONST64U)
#ifdef _MSC_VER
#define CONST64(c) c##I64
#define CONST64U(c) c##uI64
#elif defined __APPLE__
#define CONST64(c) c##LL
#define CONST64U(c) c##uLL
#elif __GNUC__
#if defined(VM_X86_64) || defined(VM_ARM_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.
 */

#if defined(VM_X86_64) || defined(VM_ARM_64)
    #define CONST3264(a) CONST64(a)
    #define CONST3264U(a) CONST64U(a)
#else
    #define CONST3264(a) (a)
    #define CONST3264U(a) (a)
#endif

#define MIN_INT8   ((int8)0x80)
#define MAX_INT8   ((int8)0x7f)

#define MIN_UINT8  ((uint8)0)
#define MAX_UINT8  ((uint8)0xff)

#define MIN_INT16  ((int16)0x8000)
#define MAX_INT16  ((int16)0x7fff)

#define MIN_UINT16 ((uint16)0)
#define MAX_UINT16 ((uint16)0xffff)

#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))


typedef uintptr_t VA;
typedef uintptr_t VPN;

typedef uint64    PA;
typedef uint64    PPN;

typedef uint64    TPA;
typedef uint64    TPPN;

typedef uint64    PhysMemOff;
typedef uint64    PhysMemSize;

typedef uint64    BA;
typedef uint64    BPN;

typedef uint64    PageCnt;
typedef uint64    PageNum;
typedef unsigned  MemHandle;
typedef unsigned  IoHandle;
typedef int32     World_ID;
typedef uint64    VSCSI_HandleID;

/* !! do not alter the definition of INVALID_WORLD_ID without ensuring
 * that the values defined in both bora/public/vm_basic_types.h and
 * lib/vprobe/vm_basic_types.h are the same.  Additionally, the definition
 * of VMK_INVALID_WORLD_ID in vmkapi_world.h also must be defined with
 * the same value
 */

#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  int8    Reg8;
typedef  int16   Reg16;
typedef  int32   Reg32;
typedef  int64   Reg64;

typedef uint8   UReg8;
typedef uint16  UReg16;
typedef uint32  UReg32;
typedef uint64  UReg64;

#ifdef VM_HAS_INT128
typedef  int128  Reg128;
typedef uint128 UReg128;
#endif

#if (defined(VMM) || defined(COREQUERY) || defined(EXTDECODER) ||  \
     defined (VMKERNEL) || defined (VMKBOOT) || defined (ULM)) &&  \
    !defined (FROBOS) || defined (VSAN_USERLEVEL)
typedef  Reg64  Reg;
typedef UReg64 UReg;
#endif
typedef uint64 MA;
typedef uint32 MPN32;

/*
 * 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;

/*
 * 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 TPPN64;
typedef uint64 MA64;
typedef uint64 MPN;

/*
 * IO device DMA virtual address and page number (translated by IOMMU to
 * MA/MPN). IOPN can be in the inclusive range 0 -> MAX_IOPN.
 */
typedef uint64 IOA;
typedef uint64 IOPN;

/*
 * VA typedefs for user world apps.
 */
typedef VA32 UserVA32;
typedef VA64 UserVA64;
typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */
typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */
typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */
#ifdef VMKERNEL
typedef UserVA64 UserVA;
#else
typedef void * UserVA;
#endif


/* Maximal observable PPN value. */
#define MAX_PPN_BITS      33
#define MAX_PPN           (((PPN)1 << MAX_PPN_BITS) - 1)

#define INVALID_PPN       ((PPN)0x000fffffffffffffull)
#define INVALID_PPN32     ((PPN32)0xffffffff)
#define APIC_INVALID_PPN  ((PPN)0x000ffffffffffffeull)

#define INVALID_BPN       ((BPN)0x0000ffffffffffffull)

#define MPN38_MASK        ((1ull << 38) - 1)

#define RESERVED_MPN      ((MPN)0)
#define INVALID_MPN       ((MPN)MPN38_MASK)
#define MEMREF_MPN        ((MPN)MPN38_MASK - 1)
#define RELEASED_MPN      ((MPN)MPN38_MASK - 2)

/* account for special MPNs defined above */
#define MAX_MPN           ((MPN)MPN38_MASK - 3) /* 50 bits of address space */

#define INVALID_IOPN      ((IOPN)-1)
#define MAX_IOPN          (IOA_2_IOPN((IOA)-1))

#define INVALID_LPN       ((LPN)-1)
#define INVALID_VPN       ((VPN)-1)
#define INVALID_LPN64     ((LPN64)-1)
#define INVALID_PAGENUM   ((PageNum)0x0000003fffffffffull)
#define INVALID_PAGENUM32 ((uint32)-1)

/*
 * Format modifier for printing VA, LA, and VPN.
 * Use them like this: Log("%#" FMTLA "x\n", laddr)
 */

#if defined(VMM) || defined(FROBOS64) || vm_x86_64 || vm_arm_64 || defined __APPLE__
#   define FMTLA "l"
#   define FMTVA "l"
#   define FMTVPN "l"
#else
#   define FMTLA ""
#   define FMTVA ""
#   define FMTVPN ""
#endif

#ifndef EXTERN
#define EXTERN        extern
#endif
#define CONST         const

#ifndef INLINE
#   ifdef _MSC_VER
       /*
        * On UWP(Universal Windows Platform),
        * Only X86 32bit support '__inline'
        */
#      if defined(VM_WIN_UWP) && !defined(VM_X86_32)
#            define INLINE
#      else
#            define INLINE        __inline
#      endif
#   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

#ifdef _WIN32

/* under windows, __declspec(thread) is supported since VS 2003 */
#define __thread __declspec(thread)

#else

/*
 * under other platforms instead, __thread is supported by gcc since
 * version 3.3.1 and by clang since version 3.x
 */

#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


/*
 * At present, we effectively require a compiler that is at least
 * gcc-4.4 (circa 2009).  Enforce this here, various things below
 * this line depend upon it.
 *
 * Current oldest compilers:
 * - buildhost compiler: 4.4.3
 * - hosted kernel modules: 4.5
 * - widespread usage: 4.8
 *
 * SWIG's preprocessor is exempt.
 * clang pretends to be gcc (4.2.1 by default), so needs to be excluded.
 */
#if !defined __clang__ && !defined SWIG
#if defined __GNUC__ && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))
#error "gcc version is too old, need gcc-4.4 or better"
#endif
#endif

/*
 * Similarly, we require a compiler that is at least vs2015.
 * Enforce this here.
 */
#if defined _MSC_VER && _MSC_VER < 1900
#error "cl.exe version is too old, need vs2015 or better"
#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 defined __GNUC__
/*
 * Starting at version 3.3, gcc does not always inline functions marked
 * 'inline' (it depends on their size and other factors). To force gcc
 * to inline a function, one must use the __always_inline__ attribute.
 * This attribute should be used sparingly and with care.  It is usually
 * preferable to let gcc make its own inlining decisions
 */
#   define INLINE_ALWAYS INLINE __attribute__((__always_inline__))
#else
#   define INLINE_ALWAYS INLINE
#endif
#define INLINE_SINGLE_CALLER INLINE_ALWAYS

/*
 * Used when a hard guarantee 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__)
#define ABSOLUTELY_NOINLINE __attribute__((__noinline__))
#elif defined(_MSC_VER)
#define ABSOLUTELY_NOINLINE __declspec(noinline)
#endif

/*
 * Used when a function has no effects except the return value and the
 * return value depends only on the parameters and/or global variables
 * Such a function can be subject to common subexpression elimination
 * and loop optimization just as an arithmetic operator would be.
 */

#if defined(__GNUC__) && (defined(VMM) || defined (VMKERNEL))
#define SIDE_EFFECT_FREE __attribute__((__pure__))
#else
#define SIDE_EFFECT_FREE
#endif

/*
 * Used when a function exmaines no input other than its arguments and
 * has no side effects other than its return value.  Stronger than
 * SIDE_EFFECT_FREE as the function is not allowed to read from global
 * memory.
 */

#if defined(__GNUC__) && (defined(VMM) || defined (VMKERNEL))
#define CONST_FUNCTION __attribute__((__const__))
#else
#define CONST_FUNCTION
#endif

/*
 * Attributes placed on function declarations to tell the compiler
 * that the function never returns.
 */

#ifdef _MSC_VER
#define NORETURN __declspec(noreturn)
#elif defined __GNUC__
#define NORETURN __attribute__((__noreturn__))
#else
#define NORETURN
#endif

/*
 * Static profiling hints for functions.
 *    A function can be either hot, cold, or neither.
 *    It is an error to specify both hot and cold for the same function.
 *    Note that there is no annotation for "neither."
 */

#if defined __GNUC__
#define HOT __attribute__((hot))
#define COLD __attribute__((cold))
#else
#define HOT
#define COLD
#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 defined __GNUC__
/*
 * 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__))
# elif defined _MSC_VER
#  define UNUSED_PARAM(_parm) __pragma(warning(suppress:4100)) _parm
# else
#  define UNUSED_PARAM(_parm) _parm
# endif
#endif

#ifndef UNUSED_TYPE
#  define UNUSED_TYPE(_parm) UNUSED_PARAM(_parm)
#endif

#ifndef UNUSED_VARIABLE
#  define UNUSED_VARIABLE(_var) (void)_var
#endif

/*
 * gcc can warn us if we're ignoring returns
 */
#if defined(__GNUC__)
# define MUST_CHECK_RETURN __attribute__((warn_unused_result))
#else
# define MUST_CHECK_RETURN
#endif

/*
 * ALIGNED specifies minimum alignment in "n" bytes.
 *
 * NOTE: __declspec(align) has limited syntax; it must essentially be
 *       an integer literal.  Expressions, such as sizeof(), do not
 *       work.
 */

#ifdef __GNUC__
#define ALIGNED(n) __attribute__((__aligned__(n)))
#elif defined(_MSC_VER)
#define ALIGNED(n) __declspec(align(n))
#else
#define ALIGNED(n)
#endif

#ifndef __has_attribute
# define __has_attribute(x) 0
#endif

/*
 * COUNTED_BY attribute may be attached to the C99 flexible array
 * member of a structure. It indicates that the number of the elements
 * of the array is given by the field "member" in the same structure as
 * the flexible array member. Compilers may use this information to
 * improve detection of object size information for such structures
 * and provide better results in compile-time diagnostics and runtime
 * features like the array bound sanitizer.
 *
 * https://people.kernel.org/kees/bounded-flexible-arrays-in-c
 */

#if __has_attribute(__counted_by__)
# define COUNTED_BY(member) __attribute__((__counted_by__(member)))
#else
# define COUNTED_BY(member)
#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)

/*
 * 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 Solaris 10 and earlier, uid_t
 * is a ulong, but on other platforms it's an unsigned int.
 * Use this like this: printf("The uid is %" FMTUID ".\n", uid);
 */
#if defined(sun) && !defined(SOL11)
#   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

#ifdef __APPLE__
/*
 * Format specifier for all these annoying types such as {S,U}Int32
 * which are 'long' in 32-bit builds
 *       and  'int' in 64-bit builds.
 */
#   ifdef __LP64__
#      define FMTLI ""
#   else
#      define FMTLI "l"
#   endif

/*
 * Format specifier for all these annoying types such as NS[U]Integer
 * which are  'int' in 32-bit builds
 *       and 'long' in 64-bit builds.
 */
#   ifdef __LP64__
#      define FMTIL "l"
#   else
#      define FMTIL ""
#   endif
#endif


/*
 * Define type for poll device handles.
 */

typedef int64 PollDevHandle;

/*
 * Define the utf16_t type.
 */

#if defined(_WIN32) && defined(_NATIVE_WCHAR_T_DEFINED)
typedef wchar_t utf16_t;
#else
typedef uint16 utf16_t;
#endif

/*
 * Define for point and rectangle types.  Defined here so they
 * can be used by other externally facing headers in bora/public.
 */

typedef struct VMPoint {
   int x, y;
} VMPoint;

#if defined _WIN32 && defined USERLEVEL
struct tagRECT;
typedef struct tagRECT VMRect;
#else
typedef struct VMRect {
   int left;
   int top;
   int right;
   int bottom;
} VMRect;
#endif

/*
 * ranked locks "everywhere"
 */

typedef uint32 MX_Rank;

#endif  /* _VM_BASIC_TYPES_H_ */
  07070100000136000081A4000000000000000000000001682255050000066F000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_compilation_options.h   /*********************************************************
 * Copyright (C) 2012-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/
#ifndef VM_COMPILATION_OPTION_H
#define VM_COMPILATION_OPTION_H

#ifdef VMX86_DEVEL
#   ifdef VMX86_DEBUG
#      define COMPILATION_OPTION "DEBUG"
#   else
#      define COMPILATION_OPTION "OPT"
#   endif
#else
#   ifdef VMX86_ALPHA
#      define COMPILATION_OPTION "ALPHA"
#   elif defined(VMX86_BETA)
#      ifdef VMX86_EXPERIMENTAL
#         define COMPILATION_OPTION "BETA-EXPERIMENTAL"
#      else
#         define COMPILATION_OPTION "BETA"
#      endif
#   elif defined(VMX86_RELEASE)
#      define COMPILATION_OPTION "Release"
#   elif defined(VMX86_OPT)
#      define COMPILATION_OPTION "OPT"
#   elif defined(VMX86_DEBUG)
#      define COMPILATION_OPTION "DEBUG"
#   elif defined(VMX86_STATS)
#      define COMPILATION_OPTION "STATS"
#   endif
#endif

#endif
 07070100000137000081A40000000000000000000000016822550500000A1F000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_ctype.h /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vm_ctype.h -- wrappers to <ctype.h> calls which are vulnerable on Windows implementation
 */

#ifndef _VM_CTYPE_H_
#define _VM_CTYPE_H_

#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include <ctype.h>

#if defined __cplusplus
extern "C" {
#endif


/*
 * On Windows platform, ctype.h functions are implemented via table lookup,
 * and a negative index is unsafe.  See bug 83950.
 * Attack the parameter with 0xFF to cast away the signedness.
 */

#ifdef _WIN32

#define CType_IsAlnum(c) isalnum((c) & 0xFF)
#define CType_IsAlpha(c) isalpha((c) & 0xFF)
#define CType_IsAscii(c) isascii((c) & 0xFF)
static __inline int CType_IsBlank(int c) { return c == ' ' || c == '\t'; }
#define CType_IsCntrl(c) iscntrl((c) & 0xFF)
#define CType_IsDigit(c) isdigit((c) & 0xFF)
#define CType_IsGraph(c) isgraph((c) & 0xFF)
#define CType_IsLower(c) islower((c) & 0xFF)
#define CType_IsPrint(c) isprint((c) & 0xFF)
#define CType_IsPunct(c) ispunct((c) & 0xFF)
#define CType_IsSpace(c) isspace((c) & 0xFF)
#define CType_IsUpper(c) isupper((c) & 0xFF)
#define CType_IsXDigit(c) isxdigit((c) & 0xFF)

#else

#define CType_IsAlnum(c) isalnum((c))
#define CType_IsAlpha(c) isalpha((c))
#define CType_IsAscii(c) isascii((c))
#define CType_IsBlank(c) isblank((c))
#define CType_IsCntrl(c) iscntrl((c))
#define CType_IsDigit(c) isdigit((c))
#define CType_IsGraph(c) isgraph((c))
#define CType_IsLower(c) islower((c))
#define CType_IsPrint(c) isprint((c))
#define CType_IsPunct(c) ispunct((c))
#define CType_IsSpace(c) isspace((c))
#define CType_IsUpper(c) isupper((c))
#define CType_IsXDigit(c) isxdigit((c))

#endif /* _WIN32 */


#if defined __cplusplus
}
#endif

#endif // _VM_CTYPE_H_
 07070100000138000081A40000000000000000000000016822550500003FDE000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_device_version.h    /*********************************************************
 * Copyright (C) 1998,2005-2012,2014-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef VM_DEVICE_VERSION_H
#define VM_DEVICE_VERSION_H

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#ifdef _WIN32
#ifdef __MINGW32__
#include "initguid.h"
#else
#include "guiddef.h"
#endif
#endif

/* LSILogic 53C1030 Parallel SCSI controller
 * LSILogic SAS1068 SAS controller
 */
#define PCI_VENDOR_ID_LSILOGIC          0x1000
#define PCI_DEVICE_ID_LSI53C1030        0x0030
#define PCI_DEVICE_ID_LSISAS1068        0x0054

/* Our own PCI IDs
 *    VMware SBX (Sandbox device for graphics driver VM)
 *    VMware SVGA II (Unified VGA)
 *    VMware SVGA (PCI Accelerator)
 *    VMware vmxnet (Idealized NIC)
 *    VMware vmxscsi (Abortive idealized SCSI controller)
 *    VMware chipset (Subsystem ID for our motherboards)
 *    VMware e1000 (Subsystem ID)
 *    VMware vmxnet3 (Uniform Pass Through NIC)
 *    VMware HD Audio codec
 *    VMware HD Audio controller
 */
#define PCI_VENDOR_ID_VMWARE                    0x15AD
#define PCI_DEVICE_ID_VMWARE_SBX                0x0420
#define PCI_DEVICE_ID_VMWARE_SVGA4              0x0408
#define PCI_DEVICE_ID_VMWARE_SVGA_EFI           0x0407
#define PCI_DEVICE_ID_VMWARE_SVGA3              0x0406
#define PCI_DEVICE_ID_VMWARE_SVGA2              0x0405
#define PCI_DEVICE_ID_VMWARE_SVGA               0x0710
#define PCI_DEVICE_ID_VMWARE_VGA                0x0711
#define PCI_DEVICE_ID_VMWARE_NET                0x0720
#define PCI_DEVICE_ID_VMWARE_SCSI               0x0730
#define PCI_DEVICE_ID_VMWARE_VMCI               0x0740
#define PCI_DEVICE_ID_VMWARE_CHIPSET            0x1976
#define PCI_DEVICE_ID_VMWARE_82545EM            0x0750 /* single port */
#define PCI_DEVICE_ID_VMWARE_82546EB            0x0760 /* dual port   */
#define PCI_DEVICE_ID_VMWARE_EHCI               0x0770
#define PCI_DEVICE_ID_VMWARE_UHCI               0x0774
#define PCI_DEVICE_ID_VMWARE_XHCI_0096          0x0778
#define PCI_DEVICE_ID_VMWARE_XHCI_0100          0x0779
#define PCI_DEVICE_ID_VMWARE_XHCI_0120          0x077A
#define PCI_DEVICE_ID_VMWARE_1394               0x0780
#define PCI_DEVICE_ID_VMWARE_BRIDGE             0x0790
#define PCI_DEVICE_ID_VMWARE_ROOTPORT           0x07A0
#define PCI_DEVICE_ID_VMWARE_VMXNET3            0x07B0
#define PCI_DEVICE_ID_VMWARE_PVSCSI             0x07C0
#define PCI_DEVICE_ID_VMWARE_82574              0x07D0
#define PCI_DEVICE_ID_VMWARE_AHCI               0x07E0
#define PCI_DEVICE_ID_VMWARE_NVME               0x07F0
#define PCI_DEVICE_ID_VMWARE_HDAUDIO_CODEC      0x1975
#define PCI_DEVICE_ID_VMWARE_HDAUDIO_CONTROLLER 0x1977

/*
 * TXT vendor, device and revision ID. We are keeping vendor
 * as Intel since tboot code does not like anything other
 * than Intel in the SINIT ACM header.
 */
#define TXT_VENDOR_ID                           0x8086
#define TXT_DEVICE_ID                           0xB002
#define TXT_REVISION_ID                         0x01

/* The hypervisor device might grow.  Please leave room
 * for 7 more subfunctions.
 */
#define PCI_DEVICE_ID_VMWARE_HYPER      0x0800
#define PCI_DEVICE_ID_VMWARE_VMI        0x0801

#define PCI_DEVICE_VMI_CLASS            0x05
#define PCI_DEVICE_VMI_SUBCLASS         0x80
#define PCI_DEVICE_VMI_INTERFACE        0x00
#define PCI_DEVICE_VMI_REVISION         0x01

/*
 * Device IDs for the PCI passthru test device:
 *
 * 0x0809 is for old fashioned PCI with MSI.
 * 0x080A is for PCI express with MSI-X.
 * 0x080B is for PCI express with configurable BARs.
 */
#define PCI_DEVICE_ID_VMWARE_PCI_TEST   0x0809
#define PCI_DEVICE_ID_VMWARE_PCIE_TEST1 0x080A
#define PCI_DEVICE_ID_VMWARE_PCIE_TEST2 0x080B

#define PCI_DEVICE_ID_VMWARE_VRDMA      0x0820
#define PCI_DEVICE_ID_VMWARE_VTPM       0x0830

/*
 * VMware Device Virtualization Extension (DVX) devices
 */
#define PCI_DEVICE_ID_VMWARE_DVX_SAMPLE 0x0840
#define PCI_DEVICE_ID_VMWARE_DVX_TEST   0x0841

/*
 * VMware Virtual Device Test Infrastructure (VDTI) devices
 */
#define PCI_DEVICE_ID_VMWARE_VDTI               0x7E57  /* stands for "TEST" */

/* From linux/pci_ids.h:
 *   AMD Lance Ethernet controller
 *   BusLogic SCSI controller
 *   Ensoniq ES1371 sound controller
 */
#define PCI_VENDOR_ID_AMD               0x1022
#define PCI_DEVICE_ID_AMD_VLANCE        0x2000
#define PCI_DEVICE_ID_AMD_IOMMU         0x1577
#define PCI_VENDOR_ID_BUSLOGIC			0x104B
#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC	0x0140
#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER	0x1040
#define PCI_VENDOR_ID_ENSONIQ           0x1274
#define PCI_DEVICE_ID_ENSONIQ_ES1371    0x1371

/* From linux/pci_ids.h:
 *    Intel 82439TX (430 HX North Bridge)
 *    Intel 82371AB (PIIX4 South Bridge)
 *    Intel 82443BX (440 BX North Bridge and AGP Bridge)
 *    Intel 82545EM (e1000, server adapter, single port)
 *    Intel 82546EB (e1000, server adapter, dual port)
 *    Intel HECI (as embedded in ich9m)
 *    Intel XHCI (Panther Point / Intel 7 Series, 5Gbps)
 *    Intel XHCI (Cannon Lake / Intel 300 Series, 10Gbps)
 */
#define PCI_VENDOR_ID_INTEL                   0x8086
#define PCI_DEVICE_ID_INTEL_82439TX           0x7100
#define PCI_DEVICE_ID_INTEL_82371AB_0         0x7110
#define PCI_DEVICE_ID_INTEL_82371AB_2         0x7112
#define PCI_DEVICE_ID_INTEL_82371AB_3         0x7113
#define PCI_DEVICE_ID_INTEL_82371AB           0x7111
#define PCI_DEVICE_ID_INTEL_82443BX           0x7190
#define PCI_DEVICE_ID_INTEL_82443BX_1         0x7191
#define PCI_DEVICE_ID_INTEL_82443BX_2         0x7192 /* Used when no AGP support */
#define PCI_DEVICE_ID_INTEL_82545EM           0x100f
#define PCI_DEVICE_ID_INTEL_82546EB           0x1010
#define PCI_DEVICE_ID_INTEL_82574             0x10d3
#define PCI_DEVICE_ID_INTEL_82574_APPLE       0x10f6
#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI 0x1e31
#define PCI_DEVICE_ID_INTEL_CANNONLAKE_XHCI   0xa36d

/*
 *  From drivers/usb/host/xhci-pci.c:
 *    Intel XHCI (Lynx Point / Intel 8 Series)
 */
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31

/*
 * Intel Volume Management Device (VMD)
 */
#define PCI_DEVICE_ID_INTEL_VMD_GEN1           0x201d
#define PCI_DEVICE_ID_INTEL_VMD_GEN2           0x28c0
#define PCI_DEVICE_ID_INTEL_VMD_GEN3           0x476F

/*
 * Intel Quickassist (QAT) devices.
 */
#define PCI_DEVICE_ID_INTEL_QAT_DH895XCC     0x0435
#define PCI_DEVICE_ID_INTEL_QAT_DH895XCC_VF  0x0443

#define PCI_DEVICE_ID_INTEL_QAT_C62X         0x37c8
#define PCI_DEVICE_ID_INTEL_QAT_C62X_VF      0x37c9

/*
 * Intel FPGAs
 */

#define PCI_DEVICE_ID_INTEL_FPGA_SKL_PF 0xbcc0
#define PCI_DEVICE_ID_INTEL_FPGA_SKL_VF 0xbcc1

#define E1000E_PCI_DEVICE_ID_CONFIG_STR "e1000e.pci.deviceID"
#define E1000E_PCI_SUB_VENDOR_ID_CONFIG_STR "e1000e.pci.subVendorID"
#define E1000E_PCI_SUB_DEVICE_ID_CONFIG_STR "e1000e.pci.subDeviceID"

/*
 * Intel HD Audio controller and Realtek ALC885 codec.
 */
#define PCI_DEVICE_ID_INTEL_631XESB_632XESB  0x269a
#define PCI_VENDOR_ID_REALTEK                0x10ec
#define PCI_DEVICE_ID_REALTEK_ALC885         0x0885


/*
 * Fresco Logic xHCI (USB 3.0) Controller
 */
#define PCI_VENDOR_ID_FRESCO            0x1B73
#define PCI_DEVICE_ID_FRESCO_FL1000     0x1000   // Original 1-port chip
#define PCI_DEVICE_ID_FRESCO_FL1009     0x1009   // New 2-port chip (Driver 3.0.98+)
#define PCI_DEVICE_ID_FRESCO_FL1400     0x1400   // Unknown (4-port? Dev hardware?)

/*
 * NEC/Renesas xHCI (USB 3.0) Controller
 */
#define PCI_VENDOR_ID_NEC               0x1033
#define PCI_DEVICE_ID_NEC_UPD720200     0x0194
#define PCI_REVISION_NEC_UPD720200      0x03
#define PCI_FIRMWARE_NEC_UPD720200      0x3015

#define SATA_ID_SERIAL_STR "00000000000000000001"  /* Must be 20 Bytes */
#define SATA_ID_FIRMWARE_STR  "00000001"    /* Must be 8 Bytes */

#define AHCI_ATA_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SATA Hard Drive"
#define AHCI_ATAPI_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SATA CDRW Drive"

/************* Strings for IDE Identity Fields **************************/
#define VIDE_ID_SERIAL_STR	"00000000000000000001"	/* Must be 20 Bytes */
#define VIDE_ID_FIRMWARE_STR	"00000001"		/* Must be 8 Bytes */

/* No longer than 40 Bytes */
#define VIDE_ATA_MODEL_STR PRODUCT_GENERIC_NAME " Virtual IDE Hard Drive"
#define VIDE_ATAPI_MODEL_STR PRODUCT_GENERIC_NAME " Virtual IDE CDROM Drive"

#define ATAPI_VENDOR_ID	"NECVMWar"		/* Must be 8 Bytes */
#define ATAPI_PRODUCT_ID PRODUCT_GENERIC_NAME " IDE CDROM"	/* Must be 16 Bytes */
#define ATAPI_REV_LEVEL	"1.00"			/* Must be 4 Bytes */

#define IDE_NUM_INTERFACES   2	/* support for two interfaces */
#define IDE_DRIVES_PER_IF    2

/************* Strings for SCSI Identity Fields **************************/
#define SCSI_DISK_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SCSI Hard Drive"
#define SCSI_DISK_VENDOR_NAME COMPANY_NAME
#define SCSI_DISK_REV_LEVEL "1.0"
#define SCSI_CDROM_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SCSI CDROM Drive"
#define SCSI_CDROM_VENDOR_NAME COMPANY_NAME
#define SCSI_CDROM_REV_LEVEL "1.0"

/************* NVME implementation limits ********************************/
#define NVME_MAX_CONTROLLERS   4
#define NVME_MIN_NAMESPACES    1
#define NVME_MAX_NAMESPACES    64 /* We support 64 namespaces same
                                   * as PVSCSI controller.
                                   */
#define NVME_HW20_MAX_NAMESPACES 15 // HWv20 and before supports 15 namespaces
#define NVME_FUTURE_MAX_NAMESPACES 256 /* To support NVME to the possible 256
                                        * disks per controller in future.
                                        */
/************* SCSI implementation limits ********************************/
#define SCSI_MAX_CONTROLLERS	 4	  // Need more than 1 for MSCS clustering
#define	SCSI_MAX_DEVICES         16	  // BT-958 emulates only 16
#define PVSCSI_HWV14_MAX_DEVICES 65	  /* HWv14 And Later Supports 64
					   * + controller at ID 7
					   */
#define PVSCSI_MAX_DEVICES       255	  // 255 (including the controller)
#define PVSCSI_MAX_NUM_DISKS     (PVSCSI_HWV14_MAX_DEVICES - 1)

/************* SATA implementation limits ********************************/
#define SATA_MAX_CONTROLLERS   4
#define SATA_MAX_DEVICES       30
#define AHCI_MIN_PORTS         1
#define AHCI_MAX_PORTS SATA_MAX_DEVICES

/*
 * Official limit for supported number of disks is 440 per VM.
 * VM can have more disks (up to 636 as of now), but such VM is not
 * supported (main reason being too long downtime during (s)vmotion).
 */
#define MAX_NUM_DISKS_SUPPORTED 440

/*
 * VSCSI_BV_INTS is the number of uint32's needed for a bit vector
 * to cover all scsi devices per target.
 */
#define VSCSI_BV_INTS            CEILING(PVSCSI_MAX_DEVICES, 8 * sizeof (uint32))
#define SCSI_IDE_CHANNEL         SCSI_MAX_CONTROLLERS
#define SCSI_IDE_HOSTED_CHANNEL  (SCSI_MAX_CONTROLLERS + 1)
#define SCSI_SATA_CHANNEL_FIRST  (SCSI_IDE_HOSTED_CHANNEL + 1)
#define SCSI_NVME_CHANNEL_FIRST  (SCSI_SATA_CHANNEL_FIRST + \
                                  SATA_MAX_CONTROLLERS)
#define SCSI_MAX_CHANNELS        (SCSI_NVME_CHANNEL_FIRST + \
                                  NVME_MAX_CONTROLLERS)

/************* SCSI-NVME channel IDs *******************************/
#define NVME_ID_TO_SCSI_ID(nvmeId)    \
   (SCSI_NVME_CHANNEL_FIRST + (nvmeId))

#define SCSI_ID_TO_NVME_ID(scsiId)    \
   ((scsiId) - SCSI_NVME_CHANNEL_FIRST)

/************* SCSI-SATA channel IDs********************************/
#define SATA_ID_TO_SCSI_ID(sataId)    \
   (SCSI_SATA_CHANNEL_FIRST + (sataId))

#define SCSI_ID_TO_SATA_ID(scsiId)    \
   ((scsiId) - SCSI_SATA_CHANNEL_FIRST)

/************* Strings for the VESA BIOS Identity Fields *****************/
#define VBE_OEM_STRING COMPANY_NAME " SVGA"
#define VBE_VENDOR_NAME COMPANY_NAME
#define VBE_PRODUCT_NAME PRODUCT_GENERIC_NAME

/************* PCI implementation limits ********************************/
#define PCI_MAX_BRIDGES         15

/************* Ethernet implementation limits ***************************/
#define MAX_ETHERNET_CARDS      10

/********************** Floppy limits ***********************************/
#define MAX_FLOPPY_DRIVES      2

/************* PCI Passthrough implementation limits ********************/
#define MAX_PCI_PASSTHRU_DEVICES 128

/************* Test device implementation limits ********************/
#define MAX_PCI_TEST_DEVICES 128

/************* VDTI PCI Device implementation limits ********************/
#define MAX_VDTI_PCI_DEVICES 16

/************* USB implementation limits ********************************/
#define MAX_USB_DEVICES_PER_HOST_CONTROLLER 127

/************* NVDIMM implementation limits ********************************/
#define NVDIMM_MAX_CONTROLLERS   1
#define MAX_NVDIMM 64

/************* vRDMA implementation limits ******************************/
#define MAX_VRDMA_DEVICES 10

/************* QAT implementation limits ********************/
#define MAX_QAT_PCI_DEVICES 4

/************* PrecisionClock implementation limits ********************/
#define MAX_PRECISIONCLOCK_DEVICES 1

/************* DeviceGroup implementation limits ********************/
#define MAX_DEVICE_GROUP_DEVICES 4

/************* Serial/Parallel Ports limits ********************/
#define NUM_SERIAL_PORTS     32
#define NUM_PARALLEL_PORTS   3

/************* USB host controller limits ********************/
#define USB_EHCI_MAX_CONTROLLERS        1
#define USB_XHCI_MAX_CONTROLLERS        1

/*
 * As per USB specification 127 devices can be connected. Along with user usb
 * devices other types of devices like root hub, hub, keyboard, mouse are also
 * present and are not expose directly to users. These other devices also
 * occupy ports on USB.
 *
 * Although we have 20 devices limit for virtual usb mass storage on each
 * controller we can't just put 20 here as we need to account for other devices
 * which are necessary for functionality
 * TODO: enforce 20 devices limit from hostd
 */
#define USB_MAX_DEVICES_PER_HOST_CTRL   127
/************* Strings for Host USB Driver *******************************/

#ifdef _WIN32

/*
 * Globally unique ID for the VMware device interface. Define INITGUID before including
 * this header file to instantiate the variable.
 */
DEFINE_GUID(GUID_DEVICE_INTERFACE_VMWARE_USB_DEVICES,
0x2da1fe75, 0xaab3, 0x4d2c, 0xac, 0xdf, 0x39, 0x8, 0x8c, 0xad, 0xa6, 0x65);

/*
 * Globally unique ID for the VMware device setup class.
 */
DEFINE_GUID(GUID_CLASS_VMWARE_USB_DEVICES,
0x3b3e62a5, 0x3556, 0x4d7e, 0xad, 0xad, 0xf5, 0xfa, 0x3a, 0x71, 0x2b, 0x56);

/*
 * This string defines the device ID string of a VMware USB device.
 * The format is USB\Vid_XXXX&Pid_YYYY, where XXXX and YYYY are the
 * hexadecimal representations of the vendor and product ids, respectively.
 *
 * The official vendor ID for VMware, Inc. is 0x0E0F.
 * The product id for USB generic devices is 0x0001.
 */
#define USB_VMWARE_DEVICE_ID_WIDE L"USB\\Vid_0E0F&Pid_0001"
#define USB_DEVICE_ID_LENGTH (sizeof(USB_VMWARE_DEVICE_ID_WIDE) / sizeof(WCHAR))

#ifdef UNICODE
#define USB_PNP_SETUP_CLASS_NAME L"VMwareUSBDevices"
#define USB_PNP_DRIVER_NAME L"vmusb"
#else
#define USB_PNP_SETUP_CLASS_NAME "VMwareUSBDevices"
#define USB_PNP_DRIVER_NAME "vmusb"
#endif
#endif

/*
 * Our JEDEC 2 Manufacturer ID number is 2 in bank 10.  Our number is nine
 * bytes of continuation code (with an odd parity bit in bit 7) followed by the
 * number itself.
 *
 */
#define JEDEC_VENDOR_ID_VMWARE          0x289
#define JEDEC_DEVICE_ID_VMWARE_NVDIMM   0x1

#endif /* VM_DEVICE_VERSION_H */
  07070100000139000081A40000000000000000000000016822550500004229000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_product.h   /*********************************************************
 * Copyright (c) 2006-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


#ifndef VM_PRODUCT_H
#define VM_PRODUCT_H

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"


/****
 ****
 **** PLEASE use the various PRODUCT_* and *_NAME defines as though
 **** they were variables -- do not embed them in format strings,
 **** since they could contain a "%" sign or actually be a variable
 **** someday.
 ****
 ****/


/*
 * This name should be used when referring to the company
 */
#define COMPANY_NAME       "VMware, Inc."


/*
 * VMware's Internet Assigned Numbers Authority Private Enterprise Number.
 * https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers.
 */
#define VMW_IANA_PEN       6876
#define VMW_IANA_PEN_STR "6876"
#define VMW_IANA_PEN_LEN      4


/*
 * This generic name should be used when referring to any product of the
 * VMware product line, like VMware Workstation, VMware Server, and so
 * on...
 */
#define PRODUCT_GENERIC_NAME "VMware"
#define PRODUCT_GENERIC_NAME_UPPER "VMWARE"
#define PRODUCT_GENERIC_NAME_LOWER "vmware"


/*
 * Brief names are used when the VMware prefix is not wanted.
 */
#define PRODUCT_SCALABLE_SERVER_BRIEF_NAME "ESX"
#define PRODUCT_ESXI_BRIEF_NAME "ESXi"
#define PRODUCT_ESXIO_BRIEF_NAME "ESXio"
#define PRODUCT_VMVISOR_BRIEF_NAME PRODUCT_ESXI_BRIEF_NAME
#define PRODUCT_WORKSTATION_BRIEF_NAME "Workstation"
#define PRODUCT_WORKSTATION_SERVER_BRIEF_NAME "Workstation Server"
#define PRODUCT_PLAYER_BRIEF_NAME "Player"
#define PRODUCT_MAC_DESKTOP_BRIEF_NAME "Fusion"
#define PRODUCT_VMRC_BRIEF_NAME "Remote Console"


/*
 * Product names include the formal VMware prefix.
 */
#define MAKE_NAME(_brief) PRODUCT_GENERIC_NAME " " _brief

/*
 * This name should be used when referring to VMware Tools
 */
#define VMWARE_TOOLS_SHORT_NAME MAKE_NAME("Tools")
#define VMWARE_VGAUTH_SHORT_NAME MAKE_NAME("Guest Authentication")

#define PRODUCT_SCALABLE_SERVER_NAME MAKE_NAME(PRODUCT_SCALABLE_SERVER_BRIEF_NAME)
#define PRODUCT_ESXI_NAME MAKE_NAME(PRODUCT_ESXI_BRIEF_NAME)
#define PRODUCT_ESXIO_NAME MAKE_NAME(PRODUCT_ESXIO_BRIEF_NAME)
#define PRODUCT_VMVISOR_NAME PRODUCT_ESXI_NAME
#define PRODUCT_WORKSTATION_NAME MAKE_NAME(PRODUCT_WORKSTATION_BRIEF_NAME)
#define PRODUCT_WORKSTATION_SERVER_NAME MAKE_NAME(PRODUCT_WORKSTATION_SERVER_BRIEF_NAME)
#define PRODUCT_CONSOLE_NAME MAKE_NAME("Server Console")
#define PRODUCT_PLAYER_NAME MAKE_NAME(PRODUCT_PLAYER_BRIEF_NAME)
#define PRODUCT_PLAYER_NAME_FOR_LICENSE PRODUCT_PLAYER_NAME
#define PRODUCT_MAC_DESKTOP_NAME_FOR_LICENSE "VMware Fusion for Mac OS"
#define PRODUCT_VMRC_NAME MAKE_NAME(PRODUCT_VMRC_BRIEF_NAME)
#define PRODUCT_VMRC_NAME_FOR_LICENSE PRODUCT_VMRC_NAME

#define PRODUCT_VLICENSE_SHORT_NAME "VLICENSE"
#define PRODUCT_VLICENSE_NAME MAKE_NAME("License Infrastructure")

#define PRODUCT_P2V_SHORT_NAME "P2V"
#define PRODUCT_P2V_NAME MAKE_NAME("P2V Assistant")

#define PRODUCT_V2V_SHORT_NAME "V2V"
#define PRODUCT_V2V_NAME MAKE_NAME("Virtual Machine Importer")

#define PRODUCT_SYSIMAGE_SHORT_NAME "SysImage"
#define PRODUCT_SYSIMAGE_NAME MAKE_NAME("System Image Framework")

#define PRODUCT_VPX_NAME MAKE_NAME("VirtualCenter")

// Selector for the VPXD mode of operation
#define PRODUCT_VPXD_SHORT_NAME "VirtualCenter"
#define PRODUCT_CCP_SHORT_NAME "ClusterControlPlane"

#define PRODUCT_VPXA_NAME PRODUCT_VPX_NAME " Agent"

#define PRODUCT_FDM_NAME MAKE_NAME("Fault Domain Manager")

#define PRODUCT_HA_NAME MAKE_NAME("High Availability Extension")

#define PRODUCT_SDK_NAME MAKE_NAME("SDK")

#define PRODUCT_DDK_NAME MAKE_NAME("ESX DDK")

#define PRODUCT_NGCINSTALLER_NAME MAKE_NAME("vSphere Web Client")

#define PRODUCT_SSOINSTALLER_NAME MAKE_NAME("Single Sign On")

#define PRODUCT_SSOREGMM_NAME MAKE_NAME("vCenter Registration Tool")

#define PRODUCT_VDDK_SHORT_NAME "VDDK"
#define PRODUCT_VDDK_NAME MAKE_NAME("Virtual Disk Development Kit")

#define PRODUCT_VDM_CLIENT_NAME MAKE_NAME("Horizon Client")
#define PRODUCT_VDM_CLIENT_NAME_FOR_LICENSE PRODUCT_VDM_CLIENT_NAME

#define PRODUCT_XVP_SHORT_NAME "XVP"
#define PRODUCT_XVP_NAME MAKE_NAME("vCenter XVP Manager")
#define PRODUCT_RMKSCONTAINER_NAME MAKE_NAME("Remote MKS Container")

#define PRODUCT_HBR_SERVER_NAME MAKE_NAME("vSphere Replication Server")

#define PRODUCT_VIEW_SHORT_NAME "Horizon View"
#define PRODUCT_VIEW_NAME MAKE_NAME("Horizon View")
#define PRODUCT_VIEW_NAME_FOR_LICENSE PRODUCT_VIEW_NAME

#define PRODUCT_FLEX_GENERIC_SHORT_NAME "Horizon FLEX"
#define PRODUCT_FLEX_GENERIC_NAME MAKE_NAME(PRODUCT_FLEX_GENERIC_SHORT_NAME)

#define PRODUCT_FLEX_SERVER_SHORT_NAME PRODUCT_FLEX_GENERIC_SHORT_NAME " Server"
#define PRODUCT_FLEX_SERVER_NAME MAKE_NAME(PRODUCT_FLEX_SERVER_SHORT_NAME)

#define PRODUCT_VMCF_NAME MAKE_NAME("VMCF")

#if defined(__linux__) || defined(__FreeBSD__)
#define PRODUCT_NETDUMP_NAME PRODUCT_GENERIC_NAME_LOWER "-netdumper"
#else
#define PRODUCT_NETDUMP_NAME PRODUCT_VMVISOR_NAME " dump collector"
#endif

#define PRODUCT_INTEGRITY_SHORT_NAME "VUM"
#define PRODUCT_INTEGRITY_NAME MAKE_NAME("Update Manager")
#define PRODUCT_INTEGRITY_DISPLAY_NAME MAKE_NAME("vSphere Update Manager")

/*
 * VMware USB Arbitration Service version definitions
 */

#define PRODUCT_USB_ARBITRATOR_SHORT_NAME "vmware-usbarbitrator"
#define PRODUCT_USB_ARBITRATOR_NAME MAKE_NAME("USB Arbitration Service")
#ifdef _WIN32
#define PRODUCT_USB_ARBITRATOR_EXECUTABLE PRODUCT_GENERIC_NAME_LOWER "-usbarbitrator.exe"
#else
#define PRODUCT_USB_ARBITRATOR_EXECUTABLE PRODUCT_GENERIC_NAME_LOWER "-usbarbitrator"
#endif

// VMware Remote Console (VMRC)
#define PRODUCT_VMRC_UPPER "VMRC"
#define PRODUCT_VMRC_LOWER "vmrc"

/*
 * TODO: This properly lives in productState, but we need it here to
 * define DEFAULT_LIBDIRECTORY.  If that can be moved to productState,
 * it's no longer needed here.
 */
#define PRODUCT_MAC_DESKTOP_NAME MAKE_NAME(PRODUCT_MAC_DESKTOP_BRIEF_NAME)


#if !(   defined(VMX86_ESXIO)    \
      || defined(VMX86_SERVER)   \
      || defined(VMKBOOT)        \
      || defined(VMX86_DESKTOP)  \
      || defined(VMX86_HORIZON_VIEW)     \
      || defined(VMX86_VPX)      \
      || defined(VMX86_SDK)      \
      || defined(VMX86_TOOLS)    \
      || defined(VMX86_V2V)      \
      || defined(VMX86_SYSIMAGE) \
      || defined(VMX86_VLICENSE) \
      || defined(VMX86_P2V)      \
      || defined(VMX86_DDK)      \
      || defined(VMX86_VDDK)     \
      || defined(VMX86_NETDUMP) \
      || defined(VMX86_HBR_SERVER) \
      || defined(VMX86_VMCF) \
      || defined(VMX86_VMRC))
#   if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
      /*
       * XXX Make the product be Workstation by default if none of the defines
       * XXX above are not defined in defs-globaldefs.mk -- Edward A. Waugh
       */
#      define VMX86_DESKTOP
#   else
#      error Unknown product
#   endif
#endif


#if defined(VMX86_ESXIO)
# define PRODUCT_SHORT_NAME PRODUCT_ESXIO_NAME
#elif defined(VMX86_SERVER)
# define PRODUCT_SHORT_NAME PRODUCT_ESXI_NAME
#elif defined(VMKBOOT)
# define PRODUCT_SHORT_NAME PRODUCT_ESXI_NAME
#elif defined(VMX86_VMRC) /* check VMX86_VMRC before VMX86_DESKTOP */
# define PRODUCT_SHORT_NAME PRODUCT_VMRC_NAME
#elif defined(VMX86_TOOLS)
# define PRODUCT_SHORT_NAME VMWARE_TOOLS_SHORT_NAME
#elif defined(VMX86_VGAUTH)
# define PRODUCT_SHORT_NAME VMWARE_VGAUTH_SHORT_NAME
#elif defined(VMX86_VPX)
#  if defined(CSI_HA)
#     define PRODUCT_SHORT_NAME PRODUCT_HA_NAME
#  elif defined(CSI_FDM)
#     define PRODUCT_SHORT_NAME PRODUCT_FDM_NAME
#  elif defined(VPXA)
#     define PRODUCT_SHORT_NAME PRODUCT_VPXA_NAME
#  elif defined(XVP)
#     define PRODUCT_SHORT_NAME PRODUCT_XVP_NAME
#  elif defined(INSTALL_NGC)
#     define PRODUCT_SHORT_NAME PRODUCT_NGCINSTALLER_NAME
#  elif defined(INSTALL_SSO)
#     define PRODUCT_SHORT_NAME PRODUCT_SSOINSTALLER_NAME
#  elif defined(INSTALL_SSOREGMM)
#     define PRODUCT_SHORT_NAME PRODUCT_SSOREGMM_NAME
#  else
#     define PRODUCT_SHORT_NAME PRODUCT_VPX_NAME
#  endif
#elif defined(VMX86_SDK)
# define PRODUCT_SHORT_NAME PRODUCT_SDK_NAME
#elif defined(VMX86_P2V)
# define PRODUCT_SHORT_NAME PRODUCT_P2V_NAME
#elif defined(VMX86_V2V)
# define PRODUCT_SHORT_NAME PRODUCT_V2V_NAME
#elif defined(VMX86_SYSIMAGE)
# define PRODUCT_SHORT_NAME PRODUCT_SYSIMAGE_NAME
#elif defined(VMX86_VLICENSE)
# define PRODUCT_SHORT_NAME PRODUCT_VLICENSE_NAME
#elif defined(VMX86_DDK)
# define PRODUCT_SHORT_NAME PRODUCT_DDK_NAME
#elif defined(VMX86_VDDK)
# define PRODUCT_SHORT_NAME PRODUCT_VDDK_NAME
#elif defined(VMX86_NETDUMP)
# define PRODUCT_SHORT_NAME PRODUCT_NETDUMP_NAME
#elif defined(VMX86_HBR_SERVER)
# define PRODUCT_SHORT_NAME PRODUCT_HBR_SERVER_NAME
#elif defined(VMX86_HORIZON_VIEW)
// Do not change product name; many consumers depend on it.
# define PRODUCT_SHORT_NAME PRODUCT_VIEW_NAME
#elif defined(VMX86_VMCF)
# define PRODUCT_SHORT_NAME PRODUCT_VMCF_NAME
#elif defined(VMX86_INTEGRITY)
# define PRODUCT_SHORT_NAME PRODUCT_INTEGRITY_NAME
// VMX86_DESKTOP must be last because it is the default and is always defined.
#elif defined(VMX86_DESKTOP)
# if defined(__APPLE__)
#  define PRODUCT_SHORT_NAME PRODUCT_MAC_DESKTOP_NAME
# else
#  define PRODUCT_SHORT_NAME PRODUCT_WORKSTATION_NAME
# endif
#endif


/*
 * Names of programs
 */

#define VMWARE_EXECUTABLE PRODUCT_GENERIC_NAME_LOWER

#define VMWARE_VMX_EXECUTABLE PRODUCT_GENERIC_NAME_LOWER "-vmx"
#if defined(__linux__) || defined(__FreeBSD__)
#   define VMAUTHD_EXECUTABLE PRODUCT_GENERIC_NAME_LOWER "-authd"
#else
#   define VMAUTHD_DISPLAY_NAME   "VMware Authorization Service"
#   define VMSERVERD_DISPLAY_NAME "VMware Registration Service"
#   define VMNAT_DISPLAY_NAME     "VMware NAT Service"
#   define TOOLS_SERVICE_DISPLAY_NAME  "VMware Tools Service"
#   define TOOLS_SERVICE_NAME          "VMTools"
#   define VMAUTHD_SERVICE_NAME   "VMAuthdService"
#endif


/*
 * Configuration paths
 */

#ifndef _WIN32
#   define PRODUCT_NAME PRODUCT_SHORT_NAME
/*
 * Checked against the ProductID field of licenses.  This ensures that
 * a license intended for one flavor of the product will not allow
 * another flavor of the product to run.
 */
#   if defined(__APPLE__)
#      define PRODUCT_OS "Mac OS"
#   else
#      define PRODUCT_OS "Linux"
#   endif

/*
 * Note: changing PRODUCT_NAME_FOR_LICENSE and PRODUCT_LICENSE_VERSION
 * or macros it cleverly depends on (such as PRODUCT_NAME) requires a
 * coordinated dormant license file change. Otherwise licensing for
 * that product may break because the Licensecheck API is being passed
 * a parameter that no longer match the content of the dormant license
 * file.
 */
#   if defined(VMX86_ESXIO)
#      define PRODUCT_NAME_FOR_LICENSE "VMware ESXio"
#   elif defined(VMX86_SERVER)
#      define PRODUCT_NAME_FOR_LICENSE "VMware ESX Server"
#   elif defined(VMX86_VMRC) /* check VMX86_VMRC before VMX86_DESKTOP */
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_VMRC_NAME_FOR_LICENSE
#   elif defined(VMX86_VPX)
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NAME " Server"
#   elif defined(VMX86_SYSIMAGE)
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NAME
#   elif defined(VMX86_NETDUMP)
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NETDUMP_NAME
// VMX86_DESKTOP must be last because it is the default and is always defined.
#   elif defined(VMX86_DESKTOP)
#      if defined(__APPLE__)
#         define PRODUCT_NAME_FOR_LICENSE PRODUCT_MAC_DESKTOP_NAME_FOR_LICENSE
#      else
#         define PRODUCT_NAME_FOR_LICENSE "VMware Workstation"
#      endif
#   else   /* It is a product that doesn't use a license */
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NAME
#   endif

/*
 * VMWARE_HOST_DIRECTORY is for host-specific configuration files.
 * DEFAULT_LIBDIRECTORY is the default for the 'libdir' config variable.
 */
#   if defined(__APPLE__)
#      if defined VMX86_DESKTOP
#         define VMWARE_HOST_DIRECTORY_PREFIX \
             "/Library/Preferences/" PRODUCT_SHORT_NAME
#      else
#         define VMWARE_HOST_DIRECTORY_PREFIX \
             "/Library/Application Support/" PRODUCT_SHORT_NAME
#      endif
#      define VMWARE_HOST_DIRECTORY VMWARE_HOST_DIRECTORY_PREFIX
#      if defined VMX86_DESKTOP
/* On Mac OS, use Location_Get() instead of DEFAULT_LIBDIRECTORY. */
#         define DEFAULT_LIBDIRECTORY \
             "/dev/null/Non-existing DEFAULT_LIBDIRECTORY"
#      else
#         define DEFAULT_LIBDIRECTORY VMWARE_HOST_DIRECTORY
#      endif
#   else
#      define VMWARE_HOST_DIRECTORY "/etc/" PRODUCT_GENERIC_NAME_LOWER
#      define DEFAULT_LIBDIRECTORY "/usr/lib/" PRODUCT_GENERIC_NAME_LOWER
#   endif

/* For user-specific files. */
#   if defined(__APPLE__)
#      define VMWARE_USER_DIRECTORY "~/Library/Preferences/" PRODUCT_SHORT_NAME
#   else
#      define VMWARE_USER_DIRECTORY "~/." PRODUCT_GENERIC_NAME_LOWER
#   endif

#   define VMWARE_MODULE_NAME "/dev/vmmon"
#   define VMWARE_CONFIG PRODUCT_GENERIC_NAME_LOWER "-config.pl"

#else

/* PRODUCT_SHORT_NAME and PRODUCT_FULL_NAME are used to display the name
   depending on how much space we have */
#   define PRODUCT_FULL_NAME PRODUCT_SHORT_NAME
#   define PRODUCT_NAME PRODUCT_FULL_NAME

/* Directory name in the registry */
#define PRODUCT_REG_NAME PRODUCT_NAME

/*
 * Checked against the ProductID field of licenses. This ensures that
 * a license intended for one flavor of the product will not allow
 * another flavor of the product to run.
 */
#   if defined(VMX86_VMRC) /* check VMX86_VMRC before VMX86_DESKTOP */
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_VMRC_NAME
#   elif defined(VMX86_FLEX) /* check VMX86_FLEX before VMX86_DESKTOP */
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_FLEX_NAME
#   elif defined(VMX86_VPX)
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_NAME " Server"
// VMX86_DESKTOP must be last because it is the default and is always defined.
#   elif defined(VMX86_DESKTOP)
#      if defined(__APPLE__)
#         define PRODUCT_NAME_FOR_LICENSE PRODUCT_MAC_DESKTOP_NAME_FOR_LICENSE
#      else
#         define PRODUCT_NAME_FOR_LICENSE "VMware Workstation"
#      endif
#   else
#      define PRODUCT_NAME_FOR_LICENSE PRODUCT_REG_NAME
#   endif

#define PRIVATE_REG_KEY "Private"

#endif

/*
 * Used when referring to an unspecified member of the VMware product line
 * ex. "This file was created by an incompatible version of PRODUCT_LINE_NAME"
 */
#define PRODUCT_LINE_NAME PRODUCT_GENERIC_NAME " software"

#define PRODUCT_REG_PATH "SOFTWARE\\" COMPANY_NAME "\\" PRODUCT_REG_NAME
#define PRIVATE_REG_PATH PRODUCT_REG_PATH "\\" PRIVATE_REG_KEY

/*
 * Defines used primarily in Tools, but perhaps useful elsewhere.  Only error
 * on unrecognized platform during Tools builds.  Note that NetWare must come
 * before linux below since it uses the Linux gcc which automatically defines
 * linux; the other platforms don't have this issue.
 */
#if defined(__linux__)
#  define PRODUCT_NAME_PLATFORM         PRODUCT_NAME " for Linux"
#elif defined(_WIN32)
#  define PRODUCT_NAME_PLATFORM         PRODUCT_NAME " for Windows"
#elif defined(__FreeBSD__)
#  define PRODUCT_NAME_PLATFORM         PRODUCT_NAME " for FreeBSD"
#elif defined(sun)
#  define PRODUCT_NAME_PLATFORM         PRODUCT_NAME " for Solaris"
#elif defined(__APPLE__)
#  define PRODUCT_NAME_PLATFORM         PRODUCT_NAME " for Mac OS X"
#elif defined __ANDROID__
#  define PRODUCT_NAME_PLATFORM         PRODUCT_NAME " for Android"
#else
#  ifdef VMX86_TOOLS
#    error "Define a product string for this platform."
#  endif
#endif


/*
 * For Host Agent (hostd)
 */

#define HOST_AGENT_PRODUCT_NAME     PRODUCT_NAME " Host Agent"

/* Used by bora/vim/lib/vmgina module.
 * @todo Use this also in /bora/install/msi/InstUtil/desktop/vmInstUtil.cpp
 *       to guarantee that the service is installed with exactly this name.
 */
#define HOST_AGENT_SERVICE_NAME     "VMwareHostd"

// Name of the environment variable that controls which proxy server to use
// while opening connections to vCenter and vSphere servers. Currently vCloud
// VMRC uses it.
#define VMWARE_HTTPSPROXY  "VMWARE_HTTPSPROXY"

#endif /* VM_PRODUCT_H */
   0707010000013A000081A40000000000000000000000016822550500006A6E000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_product_versions.h  /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/
#ifndef VM_PRODUCT_VERSIONS_H
#define VM_PRODUCT_VERSIONS_H

/*
 *
 * This file contains sections delineated by marker tags like  MARKER_XXX_...
 * These tags are used to generate vm_product_versions_fast.h, which is a
 * version header that enables faster compiles.
 *
 * When BUILD_NUMBER and similar macros are defined as numbers, builds become
 * slow as the consumer can't be cached (the crypto-hash for the C compiler's
 * inputs changes each build). For cases where that is still necessary,
 * vm_product_versions_slow.h can be used. Note: the fast headers require
 * linking to bora/lib/version.
 *
 * to avoid multiple copy of similar header, xx_fast.h code is generated from
 * this file using these marker tag.
 *
 */

//MARKER_FAST_UNCOMMENT_START
//#ifdef __cplusplus
//extern "C" {
//#endif
//
//extern unsigned int gProductVersion[];
//extern unsigned int gVieFileVersion[];
//extern unsigned int gVpxVersionNumeric[];
//extern unsigned int gNetdumpFileVersion[];
//extern unsigned int gFoundryFileVersion[];
//
//extern unsigned int gOvftoolFileVersion[];
//extern const char gEsxReleaseVersionString[];
//
//#ifdef __cplusplus
//}
//#endif
//MARKER_FAST_UNCOMMENT_END

/*
 * NOTE: Some of the macro expansions in this file require information
 *       from the generated file, 'buildNumber.h'.  For those
 *       expansions, and for those expansions only, you must include
 *       "vm_version.h" first.
 */

/*
 * Used in .rc files on the Win32 platform. We must use PRODUCT_BUILD_NUMBER
 * in numeric Win32 version numbers to stay below the 65k (circa) limit.
 *
 * When building the Tools, we make an effort to follow the "internal" Tools
 * version. Otherwise we use a hard-coded value for Workstation and a different
 * hard-coded value for every other product.
 */

//MARKER_FAST_REMOVE_START
#if defined(VMX86_VMRC) /* check VMX86_VMRC before VMX86_DESKTOP */
   #define PRODUCT_VERSION    12,1,0,PRODUCT_BUILD_NUMBER_NUMERIC   /* VMRC_VERSION_NUMBER below has to match this */
#elif defined(VMX86_FLEX) /* check VMX86_FLEX before VMX86_DESKTOP */
   #define PRODUCT_VERSION    8,0,0,PRODUCT_BUILD_NUMBER_NUMERIC   /* FLEX_VERSION_NUMBER below has to match this */
#elif defined(VMX86_TOOLS)
   #define PRODUCT_VERSION    TOOLS_VERSION_EXT_CURRENT_CSV
#elif defined(VMX86_VLICENSE)
   #define PRODUCT_VERSION    1,1,5,PRODUCT_BUILD_NUMBER_NUMERIC
#elif defined(VMX86_VPX)
   /* this should be kept in sync with the corresponding vpx branch. */
   #define PRODUCT_VERSION    9,0,0,PRODUCT_BUILD_NUMBER_NUMERIC
#elif defined(VMX86_HORIZON_VIEW)
   #define PRODUCT_VERSION    8,13,0,PRODUCT_BUILD_NUMBER_NUMERIC
// VMX86_DESKTOP must be last because it is the default and is always defined.
#elif defined(VMX86_DESKTOP)
   // WORKSTATION_VERSION_NUMBER below has to match this
   #define PRODUCT_VERSION    17,0,0,PRODUCT_BUILD_NUMBER_NUMERIC
#elif defined(VMX86_SYSIMAGE)
   #define PRODUCT_VERSION    TOOLS_VERSION_EXT_CURRENT_CSV
   #define SYSIMAGE_VERSION TOOLS_VERSION_CURRENT_STR
   #define SYSIMAGE_VERSION_EXT_STR TOOLS_VERSION_EXT_CURRENT_STR
#else
   /* Generic catch-all. */
   #define PRODUCT_VERSION    0,0,0,PRODUCT_BUILD_NUMBER_NUMERIC
#endif
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#define PRODUCT_VERSION gProductVersion
//MARKER_FAST_UNCOMMENT_END

/*
 * The VIE components are shared by different products and may be updated by newer
 * installers. Since the installer replaces the component files based on the version
 * resource, it's important that the file version be monotonically increasing. As
 * a result, these components need their own file version number that is
 * independent of the VMX86_XXX macros. This goes into the FILEVERSION property of
 * the version resources. The first release of this stuff was with VPX which had a
 * FILEVERSION of 1,0,0,PRODUCT_BUILD_NUMBER_NUMERIC
 *
 * P2VA 2.0     : 2,1,2
 * VPX 1.2      : 2,1,3
 * V2V 1.0      : 2.2.0
 * SYSIMAGE 1.0 : 2.2.1 or later (TBD)
 * Symantec     : 2.2.2 7/2005
 * VC 2.0       : 2.2.3
 * P2V 2.1      : 2.2.4 (also used for WS55 betas and RC)
 * V2V 1.5      : 2.2.5 V2V 1.5 released with WS55
 * WS 5.1       : 2.2.5 to be set when WS55 branches
 * VCB 1.0      : e.x.p esx-dali: first release with vmacore + vstor3Bus
 * VCB 1.0.1    : 3.0.1 includes security fix for registry alteration vulnerability
 * VCB 1.1      : 3.1
 * VMI 2.0      : 3.1.0
 * P2VA 3.0     : 3.?.?
 */
//MARKER_FAST_REMOVE_START
#define VIE_FILEVERSION 6,5,0,PRODUCT_BUILD_NUMBER_NUMERIC
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#define VIE_FILEVERSION gVieFileVersion
//MARKER_FAST_UNCOMMENT_END

/*
 * This string can be a little more "free form".  The license
 * manager doesn't depend on it.  This is the version that will
 * be used by the build process, the UI, etc.  Things people see.
 *
 * If platforms are on different version numbers, manage it here.
 *
 * Manage version numbers for each product here.
 *
 *  NOTE:  BE AWARE that Scons/Makefiles and build scripts depend
 *         on these defines.
 *
 *         In particular, only the first quoted token after the
 *         macro name will be used for the macro value by the build
 *         system.  Also, if VERSION_MAJOR, VERSION_MINOR, and
 *         VERSION_MAINT macros are defined, they override the
 *         VERSION macro in the build system.
 */

/* ESX release versioning scheme:
 *
 * <major>.<minor>.<update>-<quarter>.<patch>
 *
 * This maps to MACROs:
 *
 * <MAJOR>.<MINOR>.<MAINT>-<UPDATE>.<PATCH>
 *
 * The reason of the mismatches is that scons assumes all products would define
 * macros in the same style.
 *
 * Rules for updating macros:
 *
 * Set MAINT (update) to 0 for all initial GA/pre-release build.  Increment for
 * each on-prem update release.
 *
 * Set UPDATE (quarter) to 0 when MAJOR/MINOR changes.  Increment with each
 * quarterly release.
 *
 * Set PATCH to 0 for all initial GA/experimental builds.  Increment it for
 * each build that will be delivered externally.
 *
 * THEORETICAL EXAMPLES:
 *
 * 7.0.0-0.0: Pe-release/GA
 * 7.0.0-0.1: Patch 1
 * 7.0.0-1.2: Patch 2 / quarterly release 1
 * 7.0.1-2.3: Update 1 / quarterly release 2
 * 7.0.1-3.4: Patch 3 / quarterly release 3
 * 7.0.2-5.5: Update 2 / quarterly release 5
 */
#define ESX_VERSION_MAJOR "9"
#define ESX_VERSION_MINOR "0"
#define ESX_VERSION_MAINT "0" // 0 = Pre-release/GA, 3 = Update 3
#define ESX_VERSION_UPDATE ESX_VERSION_MAINT // ESX's own update level

#define ESX_VERSION ESX_VERSION_MAJOR "." ESX_VERSION_MINOR "." \
                    ESX_VERSION_UPDATE
#define ESX_VERSION_THIRD_PARTY ESX_VERSION_MAJOR ESX_VERSION_MINOR \
                                ESX_VERSION_UPDATE

#define ESX_RELEASE_UPDATE "0" // Quarterly release
#define ESX_RELEASE_PATCH "0"  // 0 = experimental
#define ESX_RELEASE_QUARTERLY ESX_RELEASE_UPDATE // ESX's own quarterly release
#define ESX_RELEASE ESX_RELEASE_QUARTERLY "." ESX_RELEASE_PATCH

// ESX release versioning string -
//    <major>.<minor>.<update>-<quarter>.<patch>.<buildid>
//MARKER_FAST_REMOVE_START
#define ESX_RELEASE_VERSION_STR ESX_VERSION "-" ESX_RELEASE "." \
                                XSTR(BUILD_NUMBER_NUMERIC)
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#define ESX_RELEASE_VERSION_STR gEsxReleaseVersionString
//MARKER_FAST_UNCOMMENT_END

#define WORKSTATION_RELEASE_DESCRIPTION ""
#define P2V_VERSION "e.x.p"
#define P2V_FILE_VERSION 3,0,0,0

/*
 * HEADS UP:  Don't merge patch version bumps (e.g. x.y.0 -> x.y.1) to CBS
 * branches (*-main), 'cuz it breaks stuff in VIX land.  See bug 939456.
 * necessary VIX changes also need to be done. See bug 939456.
 *
 * ALSO, leave FOO_VERSION at e.x.p on all EXCEPT release branches.
 * lmclient.h has a FLEX_VERSION struct so the versionPrefix can't be FLEX
 */
#define WORKSTATION_VERSION_NUMBER "17.0.0" /* this version number should always match real WS version number */
#define WORKSTATION_VERSION "e.x.p"
#define PLAYER_VERSION_NUMBER "17.0.0" /* this version number should always match real Player version number */
#define PLAYER_VERSION "e.x.p"
#define VMRC_VERSION_NUMBER "12.1.0" /* this version number should always match real VMRC version number */
#define VMRC_VERSION "12.1.0"
#define FLEX_CLIENT_VERSION_NUMBER "8.0.0"
#define FLEX_CLIENT_VERSION "e.x.p"

#define THINPRINT_VERSION "1.1.2"

/*
 * In the *-main branches, FUSION_VERSION should always be set to "e.x.p".
 * In a Fusion release branch, when you modify FUSION_VERSION, check that the
 * computation of 'lastVersion' in
 * bora/install/desktop/macos/makedmg.sh::GenerateDescriptorXML() does what you
 * want.
 */
#define FUSION_VERSION "e.x.p"

#define VIM_VERSION "9.0.0"
/*
 *For smooth version bump up for quaterly releases, we need to have a fallback
 *mechanism to previous version in all those components which perform version
 *specific tasks. Assuming that components can create version specific
 *functions if they can't eliminate version specific code, a default behavior
 *to fallback to previous version will decouple the component's version
 *specific work from version bump up activity in this file. We e.g. change
 *current version to 6.7 in this file, those functions which are written for
 *6.6 will be used until new functions for 6.7 arent available.
 *This way version bump up activity will ideally need just these steps
 *1. Change current version
 *2. Add a row for fresh previous version
*/
/*
 *VCENTER_PREVIOUS_VERSIONS
 *Macro to store all vCenter previous versions. This will be used by component
 *to move to suitale previous product version related functions is current or
 *specific is not available.
 *Please keep the version order correct for avoiding any potential issue
 */
#define VCENTER_PREVIOUS_VERSIONS \
"4.0.0,\
4.1.0,\
5.0.0,\
5.1.0,\
5.5.0,\
6.0.0,\
6.5.0"
// Put VPX_VERSION first, because vpx/make/defs.mk doesn't check for suffix.
#define VPX_VERSION "9.0.0"
#define VPX_VERSION_MAJOR "9"
#define VPX_VERSION_MINOR "0"
#define VPX_VERSION_MAINT "0"
#define VPX_VERSION_PATCH "00000"
#define VPX_VERSION_THIRD_PARTY VPX_VERSION_MAJOR VPX_VERSION_MINOR \
                                VPX_VERSION_MAINT
//MARKER_FAST_REMOVE_START
#define VPX_VERSION_NUMERIC 9,0,0,PRODUCT_BUILD_NUMBER_NUMERIC
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#define VPX_VERSION_NUMERIC gVpxVersionNumeric
//MARKER_FAST_UNCOMMENT_END

// Last supported ESX version by VC.
#define VPX_MIN_HOST_VERSION "7.0.0"

#define MAX_SUPPORTED_VI_VERSION "6.6" //from ovfTool/src/supportedVersions.h
#define VCDB_CURRENT_SCHEMA_VERSION           900 // from PitCADatabase.h

#define VPX_RELEASE_UPDATE "0" /* 0 = Pre-release/GA, 1 = Update 1 */
#define VPX_RELEASE_PATCH "0"  /* 0 = experimental */
#define VPX_RELEASE VPX_RELEASE_UPDATE "." VPX_RELEASE_PATCH

/* expected database version for current release */
#define VPXD_VDB_DB_VERSION_ID            900
#define VPXD_VDB_DB_VERSION_VALUE         "VirtualCenter Database 9.0"

/*
 * OSM Release Version for OSS/TP usage tracking and ticket filing
 */
#define VPX_OSM_VERSION "latest"

// Virtual Appliance Patch Version Number
// This is the last component of the VCSA w.x.y.z version number
// While patching / minor update this number is used by VCSA
// to validate a patch iso .
// Changing the version is required when CPD releases an update.
#define VA_PATCH_VERSION  "5100"

// esxcli
#define ESXCLI_VERSION "8.0.0"

#define HORIZONOSOT_VERSION "1.2.2303"
#define HORIZONVMDS_VERSION "1.0.0"

#define INTEGRITY_VERSION "9.0.0" /* Should use VPX_VERSION? */
#define SVA_VERSION "1.0.0"
#define SSO_VERSION "1.0.0"
#define SDK_VERSION "4.1.0"
#define FOUNDRY_VERSION "1.17.0"

//MARKER_FAST_REMOVE_START
#define FOUNDRY_FILE_VERSION 1,17,0,PRODUCT_BUILD_NUMBER_NUMERIC
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#define FOUNDRY_FILE_VERSION gFoundryFileVersion
//MARKER_FAST_UNCOMMENT_END

#define VLICENSE_VERSION "1.1.5"
#define DDK_VERSION "e.x.p"
#define VIPERL_VERSION "7.0.0"
#define RCLI_VERSION "7.0.0"
#define VDM_VERSION "e.x.p"
#define NETDUMP_VERSION        "5.1.0"

//MARKER_FAST_REMOVE_START
#define NETDUMP_FILE_VERSION    5,1,0,PRODUCT_BUILD_NUMBER_NUMERIC
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#define NETDUMP_FILE_VERSION  gNetdumpFileVersion
//MARKER_FAST_UNCOMMENT_END

#define VDDK_VERSION          "9.0.0"
#define VDDK_VERSION_MAJOR    9
#define VDDK_VERSION_MINOR    0
#define VDDK_VERSION_MAINT    0

//MARKER_FAST_REMOVE_START
#define VDDK_FILE_VERSION     VDDK_VERSION_MAJOR,VDDK_VERSION_MINOR,\
                              VDDK_VERSION_MAINT,PRODUCT_BUILD_NUMBER_NUMERIC
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#define VDDK_FILE_VERSION gVddkFileVersion
//MARKER_FAST_UNCOMMENT_END

#define OVFTOOL_VERSION "4.7.0"
#define VCSA_INSTALLER_VERSION "1.0.0"

//MARKER_FAST_REMOVE_START
#define OVFTOOL_FILE_VERSION 4,7,0,PRODUCT_BUILD_NUMBER_NUMERIC
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#define OVFTOOL_FILE_VERSION gOvftoolFileVersion
//MARKER_FAST_UNCOMMENT_END

#define VGAUTH_VERSION "1.0.0"
#define COMMON_AGENT_VERSION "e.x.p"
#define VIEWY_VERSION "e.x.p"
#define VMCFSDK_VERSION "e.x.p"
#define PCOIP_VERSION "e.x.p"
#define HOSTD_VERSION "e.x.p"
#define RECOVERYLIBS_VERSION "2.0.0"
#define PRECHECK_VERSION "e.x.p"
#define VHSESDK_VERSION "1.0.0"
#define VIEWVC_VERSION "14.0.2"
#define WCP_VERSION "0.1.10"
#define VSTATS_VERSION "0.0.1"
#define XVP_VERSION "1.0.0"
/*
 * All of these components should follow the current version of View, except
 * Horizon DaaS Agent and mfw which have their own version.
 * SCons parsing code requires that each line have a version string, so we
 * can't just do something like #define RDESDK_VERSION VIEW_VERSION"
 */
#define VIEW_CLIENT_VERSION "8.13.0"
#define VIEW_CLIENT_VERSION_NUMBER VIEW_CLIENT_VERSION
#define VIEW_VERSION "8.13.0"
#define RDE_RFT_ALL_VERSION "8.13.0"
#define RDE_RFT_REL_VERSION "8.13.0"
#define RDESDKREL_VERSION "8.13.0"
#define MKSVCHANDEV_VERSION "15.13.0"
#define TSMMRDEV_VERSION "8.13.0"
#define RDF_VERSION "8.13.0"
#define HORIZON_DAAS_AGENT_VERSION "24.1.0"
#define HORIZON_USB_AGENT_VERSION "11.13.0"
#define HORIZON_UPDATETOOL_VERSION "2.0.9"
#define MFW_VERSION "16.9.0"
#define HORIZONTOOL_VERSION "16.13.0"

/*
 * This is the Horizon Marketing version which is shared between
 * Horizon Client, Agent and Server. It is set in the format of
 * YYMM of the targeted release date.
 */
#define HORIZON_YYMM_VERSION "2403"

#ifndef MAKESTR
#define MAKESTR(x) #x
#define XSTR(x) MAKESTR(x)
#endif

/*
 * The current Tools version, derived from vm_tools_version.h. Do not modify this.
 */
#define TOOLS_VERSION TOOLS_VERSION_CURRENT_STR

#ifdef VMX86_VPX
#define VIM_API_TYPE "VirtualCenter"
#else
#define VIM_API_TYPE "HostAgent"
#endif

#define VIM_EESX_PRODUCT_LINE_ID "embeddedEsx"
#define VIM_ESX_PRODUCT_LINE_ID "esx"
#define VIM_ESXIO_PRODUCT_LINE_ID "esxio"
#define VIM_WS_PRODUCT_LINE_ID "ws"

#if defined(VMX86_VMRC) /* check VMX86_VMRC before VMX86_DESKTOP */
#  define PRODUCT_VERSION_NUMBER VMRC_VERSION
#elif defined(VMX86_FLEX) /* check VMX86_FLEX before VMX86_DESKTOP */
#  define PRODUCT_VERSION_NUMBER FLEX_VERSION
#elif defined(VMX86_SERVER)
#  define PRODUCT_VERSION_NUMBER ESX_VERSION
#elif defined(VMX86_VPX)
#  if defined(XVP)
#     define PRODUCT_VERSION_NUMBER XVP_VERSION
#  else
#     define PRODUCT_VERSION_NUMBER VPX_VERSION
#  endif
#elif defined(VMX86_SDK)
#  define PRODUCT_VERSION_NUMBER SDK_VERSION
#elif defined(VMX86_P2V)
#  define PRODUCT_VERSION_NUMBER P2V_VERSION
#elif defined(VMX86_VIPERL)
#  define PRODUCT_VERSION_NUMBER VIPERL_VERSION
#elif defined(VMX86_SYSIMAGE)
#  define PRODUCT_VERSION_NUMBER SYSIMAGE_VERSION
#elif defined(VMX86_VCB)
#  define PRODUCT_VERSION_NUMBER VCB_VERSION
#elif defined(VMX86_FOUNDRY)
#  define PRODUCT_VERSION_NUMBER FOUNDRY_VERSION
#elif defined(VMX86_VLICENSE)
#  define PRODUCT_VERSION_NUMBER VLICENSE_VERSION
#elif defined(VMX86_DDK)
#  define PRODUCT_VERSION_NUMBER DDK_VERSION
#elif defined(VMX86_TOOLS)
#  define PRODUCT_VERSION_NUMBER TOOLS_VERSION
#elif defined(VMX86_VDDK)
#  define PRODUCT_VERSION_NUMBER VDDK_VERSION
#elif defined(VMX86_HBR_SERVER)
#  define PRODUCT_VERSION_NUMBER ESX_VERSION
#elif defined(VMX86_HORIZON_VIEW)
#  if defined(VDM_CLIENT)
#    define PRODUCT_VERSION_NUMBER VIEW_CLIENT_VERSION
#  else
#    define PRODUCT_VERSION_NUMBER VIEW_VERSION
#  endif
#elif defined(VMX86_INTEGRITY)
#  define PRODUCT_VERSION_NUMBER INTEGRITY_VERSION
#elif defined(VMX86_VGAUTH)
#  define PRODUCT_VERSION_NUMBER VGAUTH_VERSION
 // VMX86_DESKTOP must be last because it is the default and is always defined.
#elif defined(VMX86_DESKTOP)
#  if defined(__APPLE__)
#    define PRODUCT_VERSION_NUMBER FUSION_VERSION
#  else
#    define PRODUCT_VERSION_NUMBER WORKSTATION_VERSION
#  endif
#endif

/*
 * Continue to just append BUILD_NUMBER here, PRODUCT_BUILD_NUMBER is
 * not needed in the string.
 */

//MARKER_FAST_REMOVE_START
#define PRODUCT_VERSION_STRING PRODUCT_VERSION_NUMBER " " BUILD_NUMBER
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#ifdef __cplusplus
//extern "C" {
//#endif
//extern const char gProductVersionString[];
//#ifdef __cplusplus
//}
//#endif
//#define PRODUCT_VERSION_STRING gProductVersionString
//MARKER_FAST_UNCOMMENT_END
/*
 * The license manager requires that PRODUCT_VERSION_STRING matches the
 * following pattern: <x>[.<y>][.<z>].
 *
 * If platforms are on different version numbers, manage it here.
 */

/*
 * Note: changing PRODUCT_NAME_FOR_LICENSE and PRODUCT_LICENSE_VERSION
 * or macros it cleverly depends on (such as PRODUCT_NAME) requires a
 * coordinated dormant license file change. Otherwise licensing for
 * that product may break because the Licensecheck API is being passed
 * a parameter that no longer match the content of the dormant license
 * file.
 */
#define PRODUCT_MAC_DESKTOP_VERSION_STRING_FOR_LICENSE "13.0"
#define PRODUCT_PLAYER_VERSION_STRING_FOR_LICENSE "17.0"
#define PRODUCT_VMRC_VERSION_STRING_FOR_LICENSE "10.0"
#define PRODUCT_FLEX_VERSION_STRING_FOR_LICENSE "8.0"

#if defined(VMX86_TOOLS)
/* This product doesn't use a license */
#  define PRODUCT_VERSION_STRING_FOR_LICENSE ""
#  define PRODUCT_LICENSE_VERSION "0.0"
#else
#  if defined(VMX86_SERVER)
#    define PRODUCT_LICENSE_VERSION "9.0"
#  elif defined(VMX86_VMRC) /* check VMX86_VMRC before VMX86_DESKTOP */
#    define PRODUCT_LICENSE_VERSION PRODUCT_VMRC_VERSION_STRING_FOR_LICENSE
#  elif defined(VMX86_FLEX) /* check VMX86_FLEX before VMX86_DESKTOP */
#    define PRODUCT_LICENSE_VERSION PRODUCT_FLEX_VERSION_STRING_FOR_LICENSE
#  elif defined(VMX86_VPX)
#    define PRODUCT_LICENSE_VERSION "9.0"
#    define PRODUCT_LICENSE_FILE_VERSION "9.0.0.0"
#  elif defined(VMX86_SDK)
#    define PRODUCT_LICENSE_VERSION "1.0"
#  elif defined(VMX86_P2V)
#    define PRODUCT_LICENSE_VERSION "1.0"
// VMX86_DESKTOP must be last because it is the default and is always defined.
#  elif defined(VMX86_DESKTOP)
#    if defined(__APPLE__)
#      define PRODUCT_LICENSE_VERSION PRODUCT_MAC_DESKTOP_VERSION_STRING_FOR_LICENSE
#    else
#      define PRODUCT_LICENSE_VERSION "17.0"
#    endif
#  else
#    define PRODUCT_LICENSE_VERSION "0.0"
#  endif
#  define PRODUCT_VERSION_STRING_FOR_LICENSE PRODUCT_LICENSE_VERSION
#endif
#define PRODUCT_ESX_LICENSE_VERSION "9.0"
#define PRODUCT_ESX_LICENSE_FILE_VERSION "9.0.0.0"

/*
 * The configuration file version string should be changed
 * whenever we make incompatible changes to the config file
 * format or to the meaning of settings.  When we do this,
 * we must also add code that detects the change and can
 * convert an old config file to a new one.
 *
 * In practice, config.version is no longer modified. Instead
 * we avoid making incompatible changes to the config file
 * format and the meaning of an individual setting is never
 * changed.
 */

#define CONFIG_VERSION_VARIABLE         "config.version"

/*
 * PREF_VERSION_VARIABLE somehow cannot be written through Dictionary_Write
 * (there is a bug after the first reload). So it's not used.
 */
/* #define PREF_VERSION_VARIABLE        "preferences.version"*/

#define CONFIG_VERSION_DEFAULT          1    /* if no version in file*/
#define CONFIG_VERSION                  8

#define CONFIG_VERSION_UNIFIEDSVGAME    3    /* Merged (S)VGA for WinME*/
#define CONFIG_VERSION_UNIFIEDSVGA      4    /* Merged (S)VGA enabled.  -J.*/
#define CONFIG_VERSION_440BX            5    /* 440bx becomes default */
#define CONFIG_VERSION_NEWMACSTYLE      3    /* ethernet?.oldMACStyle */
#define CONFIG_VERSION_WS2              2    /* config version of WS2.0.x */
#define CONFIG_VERSION_MIGRATION        6    /* migration work for WS3 */
#define CONFIG_VERSION_ESX2             6    /* config version of ESX 2.x */
#define CONFIG_VERSION_UNDOPOINT        7    /* Undopoint paradigm (WS40) */
#define CONFIG_VERSION_WS4              7    /* config version of WS4.0.x */
#define CONFIG_VERSION_MSNAP            8    /* Multiple Snapshots */
#define CONFIG_VERSION_WS5              8    /* WS5.0 */

/*
 * Product version strings allows UIs to refer to a single place for specific
 * versions of product names.  These do not include a "VMware" prefix.
 */

#define PRODUCT_VERSION_SCALABLE_SERVER_1 PRODUCT_SCALABLE_SERVER_BRIEF_NAME " 1.x"
#define PRODUCT_VERSION_SCALABLE_SERVER_2 PRODUCT_SCALABLE_SERVER_BRIEF_NAME " 2.x"
#define PRODUCT_VERSION_SCALABLE_SERVER_3 PRODUCT_SCALABLE_SERVER_BRIEF_NAME " 3.x"
#define PRODUCT_VERSION_SCALABLE_SERVER_30 PRODUCT_SCALABLE_SERVER_BRIEF_NAME " 3.0"
#define PRODUCT_VERSION_SCALABLE_SERVER_31 PRODUCT_SCALABLE_SERVER_BRIEF_NAME " 3.5"
#define PRODUCT_VERSION_SCALABLE_SERVER_40 PRODUCT_ESXI_BRIEF_NAME " 4.x"
#define PRODUCT_VERSION_SCALABLE_SERVER_50 PRODUCT_ESXI_BRIEF_NAME " 5.0"
#define PRODUCT_VERSION_SCALABLE_SERVER_51 PRODUCT_ESXI_BRIEF_NAME " 5.1"
#define PRODUCT_VERSION_SCALABLE_SERVER_55 PRODUCT_ESXI_BRIEF_NAME " 5.5"
#define PRODUCT_VERSION_SCALABLE_SERVER_60 PRODUCT_ESXI_BRIEF_NAME " 6.0"
#define PRODUCT_VERSION_SCALABLE_SERVER_65 PRODUCT_ESXI_BRIEF_NAME " 6.5"
#define PRODUCT_VERSION_SCALABLE_SERVER_67 PRODUCT_ESXI_BRIEF_NAME " 6.7"
#define PRODUCT_VERSION_SCALABLE_SERVER_70 PRODUCT_ESXI_BRIEF_NAME " 7.0"
#define PRODUCT_VERSION_SCALABLE_SERVER_701 PRODUCT_ESXI_BRIEF_NAME " 7.0 U1"
#define PRODUCT_VERSION_SCALABLE_SERVER_702 PRODUCT_ESXI_BRIEF_NAME " 7.0 U2"
#define PRODUCT_VERSION_WGS_1 "Server 1.x"
#define PRODUCT_VERSION_WGS_2 "Server 2.x"
#define PRODUCT_VERSION_GSX_3 "GSX Server 3.x"
#define PRODUCT_VERSION_WORKSTATION_4 PRODUCT_WORKSTATION_BRIEF_NAME " 4.x"
#define PRODUCT_VERSION_WORKSTATION_5 PRODUCT_WORKSTATION_BRIEF_NAME " 5.x"
#define PRODUCT_VERSION_WORKSTATION_6 PRODUCT_WORKSTATION_BRIEF_NAME " 6.0"
#define PRODUCT_VERSION_WORKSTATION_65 PRODUCT_WORKSTATION_BRIEF_NAME " 6.5"
#define PRODUCT_VERSION_WORKSTATION_7 PRODUCT_WORKSTATION_BRIEF_NAME " 7.x"
#define PRODUCT_VERSION_WORKSTATION_80 PRODUCT_WORKSTATION_BRIEF_NAME " 8.x"
#define PRODUCT_VERSION_WORKSTATION_90 PRODUCT_WORKSTATION_BRIEF_NAME " 9.x"
#define PRODUCT_VERSION_WORKSTATION_100 PRODUCT_WORKSTATION_BRIEF_NAME " 10.x"
#define PRODUCT_VERSION_WORKSTATION_110 PRODUCT_WORKSTATION_BRIEF_NAME " 11.x"
#define PRODUCT_VERSION_WORKSTATION_120 PRODUCT_WORKSTATION_BRIEF_NAME " 12.x"
// Workstation 13.x is skipped.
#define PRODUCT_VERSION_WORKSTATION_140 PRODUCT_WORKSTATION_BRIEF_NAME " 14.x"
#define PRODUCT_VERSION_WORKSTATION_150 PRODUCT_WORKSTATION_BRIEF_NAME " 15.x"
#define PRODUCT_VERSION_WORKSTATION_160 PRODUCT_WORKSTATION_BRIEF_NAME " 16.x"
#define PRODUCT_VERSION_WORKSTATION_162 PRODUCT_WORKSTATION_BRIEF_NAME " 16.2.x"
#define PRODUCT_VERSION_WORKSTATION_170 PRODUCT_WORKSTATION_BRIEF_NAME " 17.x"
#define PRODUCT_VERSION_WORKSTATION_175 PRODUCT_WORKSTATION_BRIEF_NAME " 17.5.x"
#define PRODUCT_VERSION_PLAYER_1 PRODUCT_PLAYER_BRIEF_NAME " 1.x"
#define PRODUCT_VERSION_MAC_DESKTOP_1 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 1.1"
#define PRODUCT_VERSION_MAC_DESKTOP_2 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 2.x"
#define PRODUCT_VERSION_MAC_DESKTOP_3 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 3.x"
#define PRODUCT_VERSION_MAC_DESKTOP_40 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 4.x"
#define PRODUCT_VERSION_MAC_DESKTOP_50 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 5.x"
#define PRODUCT_VERSION_MAC_DESKTOP_60 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 6.x"
#define PRODUCT_VERSION_MAC_DESKTOP_70 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 7.x"
#define PRODUCT_VERSION_MAC_DESKTOP_80 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 8.x"
// Fusion 9.x is skipped.
#define PRODUCT_VERSION_MAC_DESKTOP_100 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 10.x"
#define PRODUCT_VERSION_MAC_DESKTOP_110 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 11.x"
#define PRODUCT_VERSION_MAC_DESKTOP_120 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 12.x"
#define PRODUCT_VERSION_MAC_DESKTOP_122 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 12.2.x"
#define PRODUCT_VERSION_MAC_DESKTOP_130 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 13.x"
#define PRODUCT_VERSION_MAC_DESKTOP_135 PRODUCT_MAC_DESKTOP_BRIEF_NAME " 13.5.x"

/*
 * VDFS Versions
 */
#define VDFS_VERSION_MAJOR "0"
#define VDFS_VERSION_MINOR "1"
#define VDFS_VERSION_MAINT "0"
#define VDFS_VERSION VDFS_VERSION_MAJOR "." VDFS_VERSION_MINOR "." \
                    VDFS_VERSION_MAINT
#define VDFS_RELEASE_UPDATE "0" /* 0 = Pre-release/GA, 1 = Update 1 */
#define VDFS_RELEASE_PATCH "0"  /* 0 = experimental */
#define VDFS_RELEASE VDFS_RELEASE_UPDATE "." VDFS_RELEASE_PATCH

/*
 * ZDOM Versions
 */
#define ZDOM_VERSION_MAJOR "0"
#define ZDOM_VERSION_MINOR "1"
#define ZDOM_VERSION_MAINT "0"
#define ZDOM_VERSION ZDOM_VERSION_MAJOR "." ZDOM_VERSION_MINOR "." \
                    ZDOM_VERSION_MAINT
#define ZDOM_RELEASE_UPDATE "0" /* 0 = Pre-release/GA, 1 = Update 1 */
#define ZDOM_RELEASE_PATCH "0"  /* 0 = experimental */
#define ZDOM_RELEASE ZDOM_RELEASE_UPDATE "." ZDOM_RELEASE_PATCH

/*
 * vsansky Versions
 */
#define VSANSKY_VERSION_MAJOR "0"
#define VSANSKY_VERSION_MINOR "1"
#define VSANSKY_VERSION_MAINT "0"
#define VSANSKY_VERSION VSANSKY_VERSION_MAJOR "." VSANSKY_VERSION_MINOR "." \
                       VSANSKY_VERSION_MAINT
#define VSANSKY_RELEASE_UPDATE "0" /* 0 = Pre-release/GA, 1 = Update 1 */
#define VSANSKY_RELEASE_PATCH "0"  /* 0 = experimental */
#define VSANSKY_RELEASE VSANSKY_RELEASE_UPDATE "." VSANSKY_RELEASE_PATCH

#endif
  0707010000013B000081A40000000000000000000000016822550500011D6C000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_tools_version.h /*********************************************************
 * Copyright (c) 1998-2025 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


#if defined(USE_GOBUILD_TOOLS_VERSION) && !defined(GOBUILD_TOOLS_VERSION_USED)

#define GOBUILD_TOOLS_VERSION_USED
#include "vmtools_version.h"
#undef GOBUILD_TOOLS_VERSION_USED

#else

#ifndef VM_TOOLS_VERSION_H
#define VM_TOOLS_VERSION_H

#ifndef RC_INVOKED
#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"
#endif /* RC_INVOKED */

/*
 * WARNING: The installer perl script PARSE_VM_TOOLS_VERSION.PL
 *          is dependent on the format of this file. So if
 *          reformating or changing macro names is being performed
 *          please run the perl script to ensure it succeeds.
 */
/* clang-format off */


/*
 * VERSION_MAGIC: 0x1 build-034 (beta-1)
 *                0x2 build-049 (beta-2)
 *                0x3 build-066 (beta-3)
 *                0x4 build-084
 *                0x5 build-093 (pre-public beta)
 *                0x6 build-096 (new layout of cross page)
 */


/*
 * XXX
 * XXX Don't ever change this number unless you want to
 * XXX force an upgrade of the VMware tools
 */

#define VERSION_MAGIC      0x6


#ifndef RC_INVOKED

#if defined __cplusplus
extern "C" {
#endif

typedef uint32 ToolsVersion;
typedef enum {
  TOOLS_TYPE_UNSET = -1,
  TOOLS_TYPE_UNKNOWN = 0,
  TOOLS_TYPE_MSI,
  TOOLS_TYPE_TARBALL,
  TOOLS_TYPE_OSP,
  TOOLS_TYPE_OVT,
  TOOLS_TYPE_MAX,
} ToolsInstallType;

typedef struct {
   uint8 major;
   uint8 minor;
   uint8 base;
} ToolsVersionComponents;

static INLINE void
TOOLS_VERSION_UINT_TO_COMPONENTS(const ToolsVersion toolsVersion,   // IN
                                 ToolsVersionComponents *comps)     // IN/OUT
{
   comps->major = (toolsVersion >> 10) & 0x1f;      /* Keep lowest 5 bits after shift. */
   comps->minor = (toolsVersion >> 5) & 0x1f;
   comps->base  = toolsVersion & 0x1f;
}

#if defined __cplusplus
} // extern "C"
#endif

#endif // RC_INVOKED

/*
 * Reserve the highest possible Tools version for Tools whose lifecycle isn't
 * to be managed by VMware's platform.
 *
 * Even though the ToolsVersion type is defined as uint32 above, the VMX had,
 * for a long time, scanned it via sscanf(3) using "%d". To preserve backwards
 * compatibility with such a VMX, the "highest possible" version is defined to
 * be the largest signed 32-bit integer instead of the largest unsigned 32-bit
 * integer.
 */
#define TOOLS_VERSION_UNMANAGED 0x7fffffff

/* Historically -1 indicates tools version is not available. */
#define TOOLS_VERSION_INVALID   0xffffffff

#define STRINGER(MJR, MNR, BASE)     #MJR "." #MNR "." #BASE
#define TOOLS_VERSION_STRINGER(MJR, MNR, BASE)    STRINGER(MJR, MNR, BASE)

#define TOOLS_VERSION_CSV(MJR, MNR, BASE)  MJR, MNR, BASE

/*
 * Generate the CSV define from the base variant defines.
 * E.g #define DRIVER_VERSION_CSV   TOOLS_VERSION_TO_CSV(TOOLS_VERSION_WS60_FF)
 * Result is: DRIVER_VERSION_CSV set to 7,2,0
 */
#define TOOLS_VERSION_TO_CSV(BASE_VARIANT)     \
   TOOLS_VERSION_CSV(BASE_VARIANT##_V_MJR,BASE_VARIANT##_V_MNR,BASE_VARIANT##_V_BASE)

/*
 * Generate the dotted STRING define from the base variant defines.
 * E.g #define DRIVER_VERSION_STR   TOOLS_VERSION_TO_STR(TOOLS_VERSION_WS60_FF)
 * Result is: DRIVER_VERSION_STR set to "7.2.0"
 */
#define TOOLS_VERSION_TO_STR(BASE_VARIANT)     \
   TOOLS_VERSION_STRINGER(BASE_VARIANT##_V_MJR,BASE_VARIANT##_V_MNR,BASE_VARIANT##_V_BASE)


#define TOOLS_VERSION_UINT(MJR, MNR, BASE)    (((MJR) << 10) + ((MNR) << 5) + (BASE))


/*
 * Allocate 5 bits to each sub-version in the dotted tools version. This
 * should take care of us for any reasonable usage: 32 x 32 x 32.
 * This macro takes the base variant appended with _V suffix and
 * constructs the version number defines for the version.
 * It then passes the version numbers into the UINT construct macro above.
 */
#define TOOLS_VERSION_TO_UINT(BASE_VARIANT_VER)   \
   TOOLS_VERSION_UINT(BASE_VARIANT_VER##_MJR,BASE_VARIANT_VER##_MNR,BASE_VARIANT_VER##_BASE)


/*
 * The different tools versions that could be installed. Tools newer than
 * those contained in the install package will have version > CURRENT
 */
/*
 * Legacy (pre-dotted) versions.
 *
 * Note: Do **NOT** change any of these values, they are used in some
 * legacy code and will break if changed.
 */
#define   TOOLS_VERSION_NONE         0   // indicates no tools
#define   TOOLS_VERSION_OLD          1   // indicates pre-versioned tools
#define   TOOLS_VERSION_WS30_FF      2
#define   TOOLS_VERSION_WS30_BETA2   3
#define   TOOLS_VERSION_WS30         4
#define   TOOLS_VERSION_WS31_BETA    5

/*
 * Versions:-
 * For non-RC compiled variant:
 * Define each version component pass to macro with BASENAME+SUFFIX (_V).
 * Where BASENAME describes the version and append
 * the appropriate suffix:_V
 * For RC and non-RC define BASENAME_V and append _MJR or _MNR or _BASE.
 * Set each version number accordingly.
 */
#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS31_RC1       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS31_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS31_RC1_V_MJR       1
#define   TOOLS_VERSION_WS31_RC1_V_MNR       0
#define   TOOLS_VERSION_WS31_RC1_V_BASE      0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS32           TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS32_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS32_V_MJR           1
#define   TOOLS_VERSION_WS32_V_MNR           0
#define   TOOLS_VERSION_WS32_V_BASE          1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX15_BETA     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX15_BETA_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX15_BETA_V_MJR     2
#define   TOOLS_VERSION_ESX15_BETA_V_MNR     0
#define   TOOLS_VERSION_ESX15_BETA_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_GSX20_BETA     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_GSX20_BETA_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_GSX20_BETA_V_MJR     3
#define   TOOLS_VERSION_GSX20_BETA_V_MNR     0
#define   TOOLS_VERSION_GSX20_BETA_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS40_FF       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS40_FF_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS40_FF_V_MJR       4
#define   TOOLS_VERSION_WKS40_FF_V_MNR       0
#define   TOOLS_VERSION_WKS40_FF_V_BASE      0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS40_BETA1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS40_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS40_BETA1_V_MJR    4
#define   TOOLS_VERSION_WKS40_BETA1_V_MNR    0
#define   TOOLS_VERSION_WKS40_BETA1_V_BASE   1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS40_BETA2    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS40_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS40_BETA2_V_MJR    4
#define   TOOLS_VERSION_WKS40_BETA2_V_MNR    0
#define   TOOLS_VERSION_WKS40_BETA2_V_BASE   2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS40_BETA3    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS40_BETA3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS40_BETA3_V_MJR    4
#define   TOOLS_VERSION_WKS40_BETA3_V_MNR    0
#define   TOOLS_VERSION_WKS40_BETA3_V_BASE   3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS40_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS40_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS40_RELEASE_V_MJR  4
#define   TOOLS_VERSION_WKS40_RELEASE_V_MNR  0
#define   TOOLS_VERSION_WKS40_RELEASE_V_BASE 4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS401_RELEASE    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS401_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS401_RELEASE_V_MJR    4
#define   TOOLS_VERSION_WKS401_RELEASE_V_MNR    0
#define   TOOLS_VERSION_WKS401_RELEASE_V_BASE   5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX20_BETA1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX20_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX20_BETA1_V_MJR    5
#define   TOOLS_VERSION_ESX20_BETA1_V_MNR    0
#define   TOOLS_VERSION_ESX20_BETA1_V_BASE   1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX20_BETA2    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX20_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX20_BETA2_V_MJR    5
#define   TOOLS_VERSION_ESX20_BETA2_V_MNR    0
#define   TOOLS_VERSION_ESX20_BETA2_V_BASE   2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_VPX_BETA1      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_VPX_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_VPX_BETA1_V_MJR      5
#define   TOOLS_VERSION_VPX_BETA1_V_MNR      0
#define   TOOLS_VERSION_VPX_BETA1_V_BASE     3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX20_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX20_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX20_RELEASE_V_MJR  5
#define   TOOLS_VERSION_ESX20_RELEASE_V_MNR  0
#define   TOOLS_VERSION_ESX20_RELEASE_V_BASE 4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX20_PATCH1   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX20_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX20_PATCH1_V_MJR   5
#define   TOOLS_VERSION_ESX20_PATCH1_V_MNR   0
#define   TOOLS_VERSION_ESX20_PATCH1_V_BASE  5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX20_PATCH2   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX20_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX20_PATCH2_V_MJR   5
#define   TOOLS_VERSION_ESX20_PATCH2_V_MNR   0
#define   TOOLS_VERSION_ESX20_PATCH2_V_BASE  6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX20_PATCH3   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX20_PATCH3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX20_PATCH3_V_MJR   5
#define   TOOLS_VERSION_ESX20_PATCH3_V_MNR   0
#define   TOOLS_VERSION_ESX20_PATCH3_V_BASE  7

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS41_BETA1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS41_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS41_BETA1_V_MJR    5
#define   TOOLS_VERSION_WKS41_BETA1_V_MNR    1
#define   TOOLS_VERSION_WKS41_BETA1_V_BASE   0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX21_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX21_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX21_RELEASE_V_MJR  5
#define   TOOLS_VERSION_ESX21_RELEASE_V_MNR  1
#define   TOOLS_VERSION_ESX21_RELEASE_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX21_PATCH1   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX21_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX21_PATCH1_V_MJR   5
#define   TOOLS_VERSION_ESX21_PATCH1_V_MNR   1
#define   TOOLS_VERSION_ESX21_PATCH1_V_BASE  2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX21_PATCH2   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX21_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX21_PATCH2_V_MJR   5
#define   TOOLS_VERSION_ESX21_PATCH2_V_MNR   1
#define   TOOLS_VERSION_ESX21_PATCH2_V_BASE  3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX21_PATCH3   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX21_PATCH3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX21_PATCH3_V_MJR   5
#define   TOOLS_VERSION_ESX21_PATCH3_V_MNR   1
#define   TOOLS_VERSION_ESX21_PATCH3_V_BASE  4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS452_GSX31_B    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS452_GSX31_B_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS452_GSX31_B_V_MJR    5
#define   TOOLS_VERSION_WKS452_GSX31_B_V_MNR    2
#define   TOOLS_VERSION_WKS452_GSX31_B_V_BASE   0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_GSX31_REL      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_GSX31_REL_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_GSX31_REL_V_MJR      5
#define   TOOLS_VERSION_GSX31_REL_V_MNR      2
#define   TOOLS_VERSION_GSX31_REL_V_BASE     1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS452_REL     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS452_REL_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS452_REL_V_MJR     5
#define   TOOLS_VERSION_WKS452_REL_V_MNR     2
#define   TOOLS_VERSION_WKS452_REL_V_BASE    2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_GSX32_BETA     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_GSX32_BETA_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_GSX32_BETA_V_MJR     5
#define   TOOLS_VERSION_GSX32_BETA_V_MNR     2
#define   TOOLS_VERSION_GSX32_BETA_V_BASE    3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_GSX32_RC       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_GSX32_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_GSX32_RC_V_MJR       5
#define   TOOLS_VERSION_GSX32_RC_V_MNR       2
#define   TOOLS_VERSION_GSX32_RC_V_BASE      4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_GSX32_REL      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_GSX32_REL_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_GSX32_REL_V_MJR      5
#define   TOOLS_VERSION_GSX32_REL_V_MNR      2
#define   TOOLS_VERSION_GSX32_REL_V_BASE     5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WKS50_FF       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WKS50_FF_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WKS50_FF_V_MJR       6
#define   TOOLS_VERSION_WKS50_FF_V_MNR       0
#define   TOOLS_VERSION_WKS50_FF_V_BASE      0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX25_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX25_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX25_RELEASE_V_MJR  6
#define   TOOLS_VERSION_ESX25_RELEASE_V_MNR  1
#define   TOOLS_VERSION_ESX25_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX25_PATCH1   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX25_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX25_PATCH1_V_MJR   6
#define   TOOLS_VERSION_ESX25_PATCH1_V_MNR   1
#define   TOOLS_VERSION_ESX25_PATCH1_V_BASE  1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ACE_RC2        TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ACE_RC2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ACE_RC2_V_MJR        6
#define   TOOLS_VERSION_ACE_RC2_V_MNR        2
#define   TOOLS_VERSION_ACE_RC2_V_BASE       0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ACE101_BETA    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ACE101_BETA_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ACE101_BETA_V_MJR    6
#define   TOOLS_VERSION_ACE101_BETA_V_MNR    2
#define   TOOLS_VERSION_ACE101_BETA_V_BASE   1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ACE101_REL     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ACE101_REL_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ACE101_REL_V_MJR     6
#define   TOOLS_VERSION_ACE101_REL_V_MNR     2
#define   TOOLS_VERSION_ACE101_REL_V_BASE    2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS50_BETA2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS50_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS50_BETA2_V_MJR     6
#define   TOOLS_VERSION_WS50_BETA2_V_MNR     3
#define   TOOLS_VERSION_WS50_BETA2_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS50_RC1       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS50_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS50_RC1_V_MJR       6
#define   TOOLS_VERSION_WS50_RC1_V_MNR       4
#define   TOOLS_VERSION_WS50_RC1_V_BASE      0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX251_RC1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX251_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX251_RC1_V_MJR     6
#define   TOOLS_VERSION_ESX251_RC1_V_MNR     5
#define   TOOLS_VERSION_ESX251_RC1_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX251_PATCH1  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX251_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX251_PATCH1_V_MJR  6
#define   TOOLS_VERSION_ESX251_PATCH1_V_MNR  5
#define   TOOLS_VERSION_ESX251_PATCH1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX252_RC      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX252_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX252_RC_V_MJR      6
#define   TOOLS_VERSION_ESX252_RC_V_MNR      5
#define   TOOLS_VERSION_ESX252_RC_V_BASE     5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX252_PATCH1  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX252_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX252_PATCH1_V_MJR  6
#define   TOOLS_VERSION_ESX252_PATCH1_V_MNR  5
#define   TOOLS_VERSION_ESX252_PATCH1_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX252_RHELPATCH     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX252_RHELPATCH_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX252_RHELPATCH_V_MJR  6
#define   TOOLS_VERSION_ESX252_RHELPATCH_V_MNR  5
#define   TOOLS_VERSION_ESX252_RHELPATCH_V_BASE 7

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX253_RC      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX253_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX253_RC_V_MJR      6
#define   TOOLS_VERSION_ESX253_RC_V_MNR      5
#define   TOOLS_VERSION_ESX253_RC_V_BASE     9

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS50_RC2       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS50_RC2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS50_RC2_V_MJR       6
#define   TOOLS_VERSION_WS50_RC2_V_MNR       6
#define   TOOLS_VERSION_WS50_RC2_V_BASE      0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS50_RELEASE   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS50_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS50_RELEASE_V_MJR   6
#define   TOOLS_VERSION_WS50_RELEASE_V_MNR   7
#define   TOOLS_VERSION_WS50_RELEASE_V_BASE  0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS55_BETA1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS55_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS55_BETA1_V_MJR     6
#define   TOOLS_VERSION_WS55_BETA1_V_MNR     8
#define   TOOLS_VERSION_WS55_BETA1_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS55_BETA2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS55_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS55_BETA2_V_MJR     6
#define   TOOLS_VERSION_WS55_BETA2_V_MNR     8
#define   TOOLS_VERSION_WS55_BETA2_V_BASE    1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS55_RC1       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS55_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS55_RC1_V_MJR       6
#define   TOOLS_VERSION_WS55_RC1_V_MNR       8
#define   TOOLS_VERSION_WS55_RC1_V_BASE      2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS55_RC2       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS55_RC2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS55_RC2_V_MJR       6
#define   TOOLS_VERSION_WS55_RC2_V_MNR       8
#define   TOOLS_VERSION_WS55_RC2_V_BASE      3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS55_RELEASE   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS55_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS55_RELEASE_V_MJR   6
#define   TOOLS_VERSION_WS55_RELEASE_V_MNR   8
#define   TOOLS_VERSION_WS55_RELEASE_V_BASE  4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS552_BETA1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS552_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS552_BETA1_V_MJR    6
#define   TOOLS_VERSION_WS552_BETA1_V_MNR    9
#define   TOOLS_VERSION_WS552_BETA1_V_BASE   0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_VSERVER_BETA1  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_VSERVER_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_VSERVER_BETA1_V_MJR  6
#define   TOOLS_VERSION_VSERVER_BETA1_V_MNR  12
#define   TOOLS_VERSION_VSERVER_BETA1_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_VSERVER_RC1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_VSERVER_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_VSERVER_RC1_V_MJR    6
#define   TOOLS_VERSION_VSERVER_RC1_V_MNR    12
#define   TOOLS_VERSION_VSERVER_RC1_V_BASE   1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_VSERVER_REL    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_VSERVER_REL_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_VSERVER_REL_V_MJR    6
#define   TOOLS_VERSION_VSERVER_REL_V_MNR    12
#define   TOOLS_VERSION_VSERVER_REL_V_BASE   2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX30_BETA1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX30_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX30_BETA1_V_MJR    7
#define   TOOLS_VERSION_ESX30_BETA1_V_MNR    0
#define   TOOLS_VERSION_ESX30_BETA1_V_BASE   0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX30_BETA2    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX30_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX30_BETA2_V_MJR    7
#define   TOOLS_VERSION_ESX30_BETA2_V_MNR    0
#define   TOOLS_VERSION_ESX30_BETA2_V_BASE   1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX30_RC1      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX30_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX30_RC1_V_MJR      7
#define   TOOLS_VERSION_ESX30_RC1_V_MNR      0
#define   TOOLS_VERSION_ESX30_RC1_V_BASE     2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX30_RC2      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX30_RC2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX30_RC2_V_MJR      7
#define   TOOLS_VERSION_ESX30_RC2_V_MNR      0
#define   TOOLS_VERSION_ESX30_RC2_V_BASE     3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX30_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX30_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX30_RELEASE_V_MJR  7
#define   TOOLS_VERSION_ESX30_RELEASE_V_MNR  0
#define   TOOLS_VERSION_ESX30_RELEASE_V_BASE 4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX301_RC1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX301_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX301_RC1_V_MJR     7
#define   TOOLS_VERSION_ESX301_RC1_V_MNR     1
#define   TOOLS_VERSION_ESX301_RC1_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX301_RELEASE    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX301_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX301_RELEASE_V_MJR    7
#define   TOOLS_VERSION_ESX301_RELEASE_V_MNR    1
#define   TOOLS_VERSION_ESX301_RELEASE_V_BASE   1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX301_PATCH    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX301_PATCH_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX301_PATCH_V_MJR    7
#define   TOOLS_VERSION_ESX301_PATCH_V_MNR    1
#define   TOOLS_VERSION_ESX301_PATCH_V_BASE   2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX302_GA    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX302_GA_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX302_GA_V_MJR    7
#define   TOOLS_VERSION_ESX302_GA_V_MNR    1
#define   TOOLS_VERSION_ESX302_GA_V_BASE   2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX303_GA    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX303_GA_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX303_GA_V_MJR    7
#define   TOOLS_VERSION_ESX303_GA_V_MNR    1
#define   TOOLS_VERSION_ESX303_GA_V_BASE   3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS60_FF        TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS60_FF_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS60_FF_V_MJR        7
#define   TOOLS_VERSION_WS60_FF_V_MNR        2
#define   TOOLS_VERSION_WS60_FF_V_BASE       0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS60_BETA1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS60_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS60_BETA1_V_MJR     7
#define   TOOLS_VERSION_WS60_BETA1_V_MNR     2
#define   TOOLS_VERSION_WS60_BETA1_V_BASE    1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS60_BETA2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS60_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS60_BETA2_V_MJR     7
#define   TOOLS_VERSION_WS60_BETA2_V_MNR     2
#define   TOOLS_VERSION_WS60_BETA2_V_BASE    2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS60_BETA3     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS60_BETA3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS60_BETA3_V_MJR     7
#define   TOOLS_VERSION_WS60_BETA3_V_MNR     2
#define   TOOLS_VERSION_WS60_BETA3_V_BASE    3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS60_RC1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS60_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS60_RC1_V_MJR     7
#define   TOOLS_VERSION_WS60_RC1_V_MNR     2
#define   TOOLS_VERSION_WS60_RC1_V_BASE    4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS60_RC2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS60_RC2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS60_RC2_V_MJR     7
#define   TOOLS_VERSION_WS60_RC2_V_MNR     2
#define   TOOLS_VERSION_WS60_RC2_V_BASE    5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS60_RELEASE      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS60_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS60_RELEASE_V_MJR     7
#define   TOOLS_VERSION_WS60_RELEASE_V_MNR     2
#define   TOOLS_VERSION_WS60_RELEASE_V_BASE    6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS601_BETA1      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS601_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS601_BETA1_V_MJR     7
#define   TOOLS_VERSION_WS601_BETA1_V_MNR     2
#define   TOOLS_VERSION_WS601_BETA1_V_BASE    7

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS601_RELEASE      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS601_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS601_RELEASE_V_MJR     7
#define   TOOLS_VERSION_WS601_RELEASE_V_MNR     2
#define   TOOLS_VERSION_WS601_RELEASE_V_BASE    8

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS603_RELEASE      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS603_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS603_RELEASE_V_MJR     7
#define   TOOLS_VERSION_WS603_RELEASE_V_MNR     2
#define   TOOLS_VERSION_WS603_RELEASE_V_BASE    9

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION10_BETA4     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION10_BETA4_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION10_BETA4_V_MJR     7
#define   TOOLS_VERSION_FUSION10_BETA4_V_MNR     3
#define   TOOLS_VERSION_FUSION10_BETA4_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION10_RC1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION10_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION10_RC1_V_MJR     7
#define   TOOLS_VERSION_FUSION10_RC1_V_MNR     3
#define   TOOLS_VERSION_FUSION10_RC1_V_BASE    1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION10_RELEASE     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION10_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION10_RELEASE_V_MJR     7
#define   TOOLS_VERSION_FUSION10_RELEASE_V_MNR     3
#define   TOOLS_VERSION_FUSION10_RELEASE_V_BASE    2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX310_BETA1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX310_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX310_BETA1_V_MJR     7
#define   TOOLS_VERSION_ESX310_BETA1_V_MNR     4
#define   TOOLS_VERSION_ESX310_BETA1_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX310_BETA2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX310_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX310_BETA2_V_MJR     7
#define   TOOLS_VERSION_ESX310_BETA2_V_MNR     4
#define   TOOLS_VERSION_ESX310_BETA2_V_BASE    1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX310_RC1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX310_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX310_RC1_V_MJR     7
#define   TOOLS_VERSION_ESX310_RC1_V_MNR     4
#define   TOOLS_VERSION_ESX310_RC1_V_BASE    2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX310_RELEASE     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX310_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX310_RELEASE_V_MJR     7
#define   TOOLS_VERSION_ESX310_RELEASE_V_MNR     4
#define   TOOLS_VERSION_ESX310_RELEASE_V_BASE    3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX310_UPDATE1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX310_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX310_UPDATE1_V_MJR     7
#define   TOOLS_VERSION_ESX310_UPDATE1_V_MNR     4
#define   TOOLS_VERSION_ESX310_UPDATE1_V_BASE    4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX310_UPDATE2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX310_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX310_UPDATE2_V_MJR     7
#define   TOOLS_VERSION_ESX310_UPDATE2_V_MNR     4
#define   TOOLS_VERSION_ESX310_UPDATE2_V_BASE    6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SERVER20_FF     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SERVER20_FF_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SERVER20_FF_V_MJR     7
#define   TOOLS_VERSION_SERVER20_FF_V_MNR     5
#define   TOOLS_VERSION_SERVER20_FF_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION11_BETA1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION11_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION11_BETA1_V_MJR     7
#define   TOOLS_VERSION_FUSION11_BETA1_V_MNR     6
#define   TOOLS_VERSION_FUSION11_BETA1_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION11_RC1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION11_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION11_RC1_V_MJR     7
#define   TOOLS_VERSION_FUSION11_RC1_V_MNR     6
#define   TOOLS_VERSION_FUSION11_RC1_V_BASE    1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION11_RELEASE     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION11_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION11_RELEASE_V_MJR     7
#define   TOOLS_VERSION_FUSION11_RELEASE_V_MNR     6
#define   TOOLS_VERSION_FUSION11_RELEASE_V_BASE    2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SERVER20_BETA1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SERVER20_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SERVER20_BETA1_V_MJR     7
#define   TOOLS_VERSION_SERVER20_BETA1_V_MNR     7
#define   TOOLS_VERSION_SERVER20_BETA1_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SERVER20_BETA2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SERVER20_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SERVER20_BETA2_V_MJR     7
#define   TOOLS_VERSION_SERVER20_BETA2_V_MNR     7
#define   TOOLS_VERSION_SERVER20_BETA2_V_BASE    1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SERVER20_RC1       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SERVER20_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SERVER20_RC1_V_MJR     7
#define   TOOLS_VERSION_SERVER20_RC1_V_MNR     7
#define   TOOLS_VERSION_SERVER20_RC1_V_BASE    2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SERVER20_RC2       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SERVER20_RC2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SERVER20_RC2_V_MJR     7
#define   TOOLS_VERSION_SERVER20_RC2_V_MNR     7
#define   TOOLS_VERSION_SERVER20_RC2_V_BASE    3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SERVER20_RELEASE       TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SERVER20_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SERVER20_RELEASE_V_MJR     7
#define   TOOLS_VERSION_SERVER20_RELEASE_V_MNR     7
#define   TOOLS_VERSION_SERVER20_RELEASE_V_BASE    4


/*
 * XXX: Chromium (WS 6.5) releases should have 7.8.x numbers.
 * We still haven't used that range (beta 1 was released with 7.7.1).
 */

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS65_BETA1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS65_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS65_BETA1_V_MJR     7
#define   TOOLS_VERSION_WS65_BETA1_V_MNR     8
#define   TOOLS_VERSION_WS65_BETA1_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS65_BETA2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS65_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS65_BETA2_V_MJR     7
#define   TOOLS_VERSION_WS65_BETA2_V_MNR     8
#define   TOOLS_VERSION_WS65_BETA2_V_BASE    1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS65_RC1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS65_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS65_RC1_V_MJR     7
#define   TOOLS_VERSION_WS65_RC1_V_MNR     8
#define   TOOLS_VERSION_WS65_RC1_V_BASE    2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS65_RELEASE     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS65_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS65_RELEASE_V_MJR     7
#define   TOOLS_VERSION_WS65_RELEASE_V_MNR     8
#define   TOOLS_VERSION_WS65_RELEASE_V_BASE    3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS65_UPDATE1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS65_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS65_UPDATE1_V_MJR     7
#define   TOOLS_VERSION_WS65_UPDATE1_V_MNR     8
#define   TOOLS_VERSION_WS65_UPDATE1_V_BASE    4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION15_BETA1     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION15_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION15_BETA1_V_MJR     7
#define   TOOLS_VERSION_FUSION15_BETA1_V_MNR     9
#define   TOOLS_VERSION_FUSION15_BETA1_V_BASE    0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION15_BETA2     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION15_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION15_BETA2_V_MJR     7
#define   TOOLS_VERSION_FUSION15_BETA2_V_MNR     9
#define   TOOLS_VERSION_FUSION15_BETA2_V_BASE    1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION15_RC1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION15_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION15_RC1_V_MJR      7
#define   TOOLS_VERSION_FUSION15_RC1_V_MNR      9
#define   TOOLS_VERSION_FUSION15_RC1_V_BASE     2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION15_RELEASE    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION15_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION15_RELEASE_V_MJR      7
#define   TOOLS_VERSION_FUSION15_RELEASE_V_MNR      9
#define   TOOLS_VERSION_FUSION15_RELEASE_V_BASE     3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION15_UPDATE1    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION15_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION15_UPDATE1_V_MJR      7
#define   TOOLS_VERSION_FUSION15_UPDATE1_V_MNR      9
#define   TOOLS_VERSION_FUSION15_UPDATE1_V_BASE     4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION15_UPDATE2    TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION15_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION15_UPDATE2_V_MJR      7
#define   TOOLS_VERSION_FUSION15_UPDATE2_V_MNR      9
#define   TOOLS_VERSION_FUSION15_UPDATE2_V_BASE     5

/*
 * At the request of CPD, ESX 4.0 receives its own major version (8.0.0).
 */
#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX40_RC1      TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX40_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX40_RC1_V_MJR      8
#define   TOOLS_VERSION_ESX40_RC1_V_MNR      0
#define   TOOLS_VERSION_ESX40_RC1_V_BASE     0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX40_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX40_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX40_RELEASE_V_MJR  8
#define   TOOLS_VERSION_ESX40_RELEASE_V_MNR  0
#define   TOOLS_VERSION_ESX40_RELEASE_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX40_UPDATE1  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX40_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX40_UPDATE1_V_MJR  8
#define   TOOLS_VERSION_ESX40_UPDATE1_V_MNR  0
#define   TOOLS_VERSION_ESX40_UPDATE1_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX40_UPDATE2  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX40_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX40_UPDATE2_V_MJR  8
#define   TOOLS_VERSION_ESX40_UPDATE2_V_MNR  0
#define   TOOLS_VERSION_ESX40_UPDATE2_V_BASE 3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS70_BETA1  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS70_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS70_BETA1_V_MJR  8
#define   TOOLS_VERSION_WS70_BETA1_V_MNR  1
#define   TOOLS_VERSION_WS70_BETA1_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS70_BETA2  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS70_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS70_BETA2_V_MJR  8
#define   TOOLS_VERSION_WS70_BETA2_V_MNR  1
#define   TOOLS_VERSION_WS70_BETA2_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS70_RC  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS70_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS70_RC_V_MJR  8
#define   TOOLS_VERSION_WS70_RC_V_MNR  1
#define   TOOLS_VERSION_WS70_RC_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_WS70_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_WS70_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_WS70_RELEASE_V_MJR  8
#define   TOOLS_VERSION_WS70_RELEASE_V_MNR  1
#define   TOOLS_VERSION_WS70_RELEASE_V_BASE 3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION30_BETA1  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION30_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION30_BETA1_V_MJR  8
#define   TOOLS_VERSION_FUSION30_BETA1_V_MNR  2
#define   TOOLS_VERSION_FUSION30_BETA1_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION30_BETA2  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION30_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION30_BETA2_V_MJR  8
#define   TOOLS_VERSION_FUSION30_BETA2_V_MNR  2
#define   TOOLS_VERSION_FUSION30_BETA2_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION30_RC  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION30_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION30_RC_V_MJR  8
#define   TOOLS_VERSION_FUSION30_RC_V_MNR  2
#define   TOOLS_VERSION_FUSION30_RC_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION30_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION30_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION30_RELEASE_V_MJR  8
#define   TOOLS_VERSION_FUSION30_RELEASE_V_MNR  2
#define   TOOLS_VERSION_FUSION30_RELEASE_V_BASE 3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION30_UPDATE1   TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION30_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION30_UPDATE1_V_MJR  8
#define   TOOLS_VERSION_FUSION30_UPDATE1_V_MNR  2
#define   TOOLS_VERSION_FUSION30_UPDATE1_V_BASE 4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX45_BETA1  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX45_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX45_BETA1_V_MJR  8
#define   TOOLS_VERSION_ESX45_BETA1_V_MNR  3
#define   TOOLS_VERSION_ESX45_BETA1_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX45_RC1  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX45_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX45_RC1_V_MJR   8
#define   TOOLS_VERSION_ESX45_RC1_V_MNR   3
#define   TOOLS_VERSION_ESX45_RC1_V_BASE  1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX45_RELEASE  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX45_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX45_RELEASE_V_MJR  8
#define   TOOLS_VERSION_ESX45_RELEASE_V_MNR  3
#define   TOOLS_VERSION_ESX45_RELEASE_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED10_BETA1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED10_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED10_BETA1_V_MJR  8
#define   TOOLS_VERSION_HOSTED10_BETA1_V_MNR  4
#define   TOOLS_VERSION_HOSTED10_BETA1_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED10_RC TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED10_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED10_RC_V_MJR  8
#define   TOOLS_VERSION_HOSTED10_RC_V_MNR  4
#define   TOOLS_VERSION_HOSTED10_RC_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED10_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED10_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED10_RELEASE_V_MJR  8
#define   TOOLS_VERSION_HOSTED10_RELEASE_V_MNR  4
#define   TOOLS_VERSION_HOSTED10_RELEASE_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED10_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED10_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED10_UPDATE1_V_MJR  8
#define   TOOLS_VERSION_HOSTED10_UPDATE1_V_MNR  4
#define   TOOLS_VERSION_HOSTED10_UPDATE1_V_BASE 3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED10_UPDATE2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED10_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED10_UPDATE2_V_MJR  8
#define   TOOLS_VERSION_HOSTED10_UPDATE2_V_MNR  4
#define   TOOLS_VERSION_HOSTED10_UPDATE2_V_BASE 4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX50_BETA1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX50_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX50_BETA1_V_MJR  8
#define   TOOLS_VERSION_ESX50_BETA1_V_MNR  5
#define   TOOLS_VERSION_ESX50_BETA1_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX50_RC1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX50_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX50_RC1_V_MJR  8
#define   TOOLS_VERSION_ESX50_RC1_V_MNR  5
#define   TOOLS_VERSION_ESX50_RC1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX50_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX50_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX50_RELEASE_V_MJR  8
#define   TOOLS_VERSION_ESX50_RELEASE_V_MNR  6
#define   TOOLS_VERSION_ESX50_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX50_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX50_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX50_UPDATE1_V_MJR  8
#define   TOOLS_VERSION_ESX50_UPDATE1_V_MNR  6
#define   TOOLS_VERSION_ESX50_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_ESX50_UPDATE2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_ESX50_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_ESX50_UPDATE2_V_MJR  8
#define   TOOLS_VERSION_ESX50_UPDATE2_V_MNR  6
#define   TOOLS_VERSION_ESX50_UPDATE2_V_BASE 10

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED11_BETA1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED11_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED11_BETA1_V_MJR  8
#define   TOOLS_VERSION_HOSTED11_BETA1_V_MNR  7
#define   TOOLS_VERSION_HOSTED11_BETA1_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED11_BETA2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED11_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED11_BETA2_V_MJR  8
#define   TOOLS_VERSION_HOSTED11_BETA2_V_MNR  7
#define   TOOLS_VERSION_HOSTED11_BETA2_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED11_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED11_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED11_RELEASE_V_MJR  8
#define   TOOLS_VERSION_HOSTED11_RELEASE_V_MNR  8
#define   TOOLS_VERSION_HOSTED11_RELEASE_V_BASE 0

/* Fusion 4.1.2 (hosted11-pd-rel) */
#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED11_UPDATE3 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED11_UPDATE3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED11_UPDATE3_V_MJR  8
#define   TOOLS_VERSION_HOSTED11_UPDATE3_V_MNR  8
#define   TOOLS_VERSION_HOSTED11_UPDATE3_V_BASE 3

/* Workstation 8.0.4, Fusion 4.1.3 (hosted11-pd-rel) */
#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED11_UPDATE4 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED11_UPDATE4_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED11_UPDATE4_V_MJR  8
#define   TOOLS_VERSION_HOSTED11_UPDATE4_V_MNR  8
#define   TOOLS_VERSION_HOSTED11_UPDATE4_V_BASE 4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_MNNEXT_BETA1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_MNNEXT_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_MNNEXT_BETA1_V_MJR  8
#define   TOOLS_VERSION_MNNEXT_BETA1_V_MNR  9
#define   TOOLS_VERSION_MNNEXT_BETA1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_MNNEXT_RC1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_MNNEXT_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_MNNEXT_RC1_V_MJR  8
#define   TOOLS_VERSION_MNNEXT_RC1_V_MNR  9
#define   TOOLS_VERSION_MNNEXT_RC1_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_MNNEXT_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_MNNEXT_RELEASE_V)
#define   TOOLS_VERSION_MNNEXT_RELEASE_V_MJR  9
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_MNNEXT_RELEASE_V_MNR  0
#define   TOOLS_VERSION_MNNEXT_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED12_TECHPREVIEW2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED12_TECHPREVIEW2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED12_TECHPREVIEW2_V_MJR  9
#define   TOOLS_VERSION_HOSTED12_TECHPREVIEW2_V_MNR  1
#define   TOOLS_VERSION_HOSTED12_TECHPREVIEW2_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED12_RC TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED12_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED12_RC_V_MJR  9
#define   TOOLS_VERSION_HOSTED12_RC_V_MNR  1
#define   TOOLS_VERSION_HOSTED12_RC_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED12_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED12_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED12_RELEASE_V_MJR  9
#define   TOOLS_VERSION_HOSTED12_RELEASE_V_MNR  2
#define   TOOLS_VERSION_HOSTED12_RELEASE_V_BASE 0

/* Fusion 5.0.1 (hosted12-pd-rel) */
#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED12_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED12_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED12_UPDATE1_V_MJR  9
#define   TOOLS_VERSION_HOSTED12_UPDATE1_V_MNR  2
#define   TOOLS_VERSION_HOSTED12_UPDATE1_V_BASE 1

/* Workstation 9.0.1, Fusion 5.0.2 (hosted12-pd-rel) */
#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED12_UPDATE2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED12_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED12_UPDATE2_V_MJR  9
#define   TOOLS_VERSION_HOSTED12_UPDATE2_V_MNR  2
#define   TOOLS_VERSION_HOSTED12_UPDATE2_V_BASE 2

/* Workstation 9.0.2 (hosted12-pd-rel) */
#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED12_UPDATE3 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED12_UPDATE3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED12_UPDATE3_V_MJR  9
#define   TOOLS_VERSION_HOSTED12_UPDATE3_V_MNR  2
#define   TOOLS_VERSION_HOSTED12_UPDATE3_V_BASE 3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_OP_BETA1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_OP_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_OP_BETA1_V_MJR  9
#define   TOOLS_VERSION_OP_BETA1_V_MNR  3
#define   TOOLS_VERSION_OP_BETA1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_OP_BETA2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_OP_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_OP_BETA2_V_MJR  9
#define   TOOLS_VERSION_OP_BETA2_V_MNR  3
#define   TOOLS_VERSION_OP_BETA2_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_OP_RC1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_OP_RC1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_OP_RC1_V_MJR  9
#define   TOOLS_VERSION_OP_RC1_V_MNR  3
#define   TOOLS_VERSION_OP_RC1_V_BASE 3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_OP_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_OP_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_OP_RELEASE_V_MJR  9
#define   TOOLS_VERSION_OP_RELEASE_V_MNR  4
#define   TOOLS_VERSION_OP_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED13_TECHPREVIEW1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED13_TECHPREVIEW1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED13_TECHPREVIEW1_V_MJR  9
#define   TOOLS_VERSION_HOSTED13_TECHPREVIEW1_V_MNR  5
#define   TOOLS_VERSION_HOSTED13_TECHPREVIEW1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED13_RC TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED13_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED13_RC_V_MJR  9
#define   TOOLS_VERSION_HOSTED13_RC_V_MNR  5
#define   TOOLS_VERSION_HOSTED13_RC_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED13_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED13_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED13_RELEASE_V_MJR  9
#define   TOOLS_VERSION_HOSTED13_RELEASE_V_MNR  6
#define   TOOLS_VERSION_HOSTED13_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED13_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED13_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED13_UPDATE1_V_MJR  9
#define   TOOLS_VERSION_HOSTED13_UPDATE1_V_MNR  6
#define   TOOLS_VERSION_HOSTED13_UPDATE1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_2015_BETA1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_2015_BETA1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_2015_BETA1_V_MJR  9
#define   TOOLS_VERSION_2015_BETA1_V_MNR  7
#define   TOOLS_VERSION_2015_BETA1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_2015_BETA2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_2015_BETA2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_2015_BETA2_V_MJR  9
#define   TOOLS_VERSION_2015_BETA2_V_MNR  7
#define   TOOLS_VERSION_2015_BETA2_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_2015_RC TOOLS_VERSION_TO_UINT(TOOLS_VERSION_2015_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_2015_RC_V_MJR  9
#define   TOOLS_VERSION_2015_RC_V_MNR  7
#define   TOOLS_VERSION_2015_RC_V_BASE 3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED14_TECHPREVIEW1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED14_TECHPREVIEW1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED14_TECHPREVIEW1_V_MJR  9
#define   TOOLS_VERSION_HOSTED14_TECHPREVIEW1_V_MNR  8
#define   TOOLS_VERSION_HOSTED14_TECHPREVIEW1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED14_TECHPREVIEW2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED14_TECHPREVIEW2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED14_TECHPREVIEW2_V_MJR  9
#define   TOOLS_VERSION_HOSTED14_TECHPREVIEW2_V_MNR  8
#define   TOOLS_VERSION_HOSTED14_TECHPREVIEW2_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION70_RELEASE     TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION70_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION70_RELEASE_V_MJR     9
#define   TOOLS_VERSION_FUSION70_RELEASE_V_MNR     8
#define   TOOLS_VERSION_FUSION70_RELEASE_V_BASE    3

#ifndef RC_INVOKED
#define   TOOLS_VERSION_FUSION70_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_FUSION70_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_FUSION70_UPDATE1_V_MJR  9
#define   TOOLS_VERSION_FUSION70_UPDATE1_V_MNR  8
#define   TOOLS_VERSION_FUSION70_UPDATE1_V_BASE 4

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED14_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED14_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED14_RELEASE_V_MJR  9
#define   TOOLS_VERSION_HOSTED14_RELEASE_V_MNR  9
#define   TOOLS_VERSION_HOSTED14_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED14_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED14_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED14_UPDATE1_V_MJR  9
#define   TOOLS_VERSION_HOSTED14_UPDATE1_V_MNR  9
#define   TOOLS_VERSION_HOSTED14_UPDATE1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HOSTED14_UPDATE2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HOSTED14_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HOSTED14_UPDATE2_V_MJR  9
#define   TOOLS_VERSION_HOSTED14_UPDATE2_V_MNR  9
#define   TOOLS_VERSION_HOSTED14_UPDATE2_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_2015_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_2015_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_2015_RELEASE_V_MJR  9
#define   TOOLS_VERSION_2015_RELEASE_V_MNR  10
#define   TOOLS_VERSION_2015_RELEASE_V_BASE 0

/*
 * Patch01OVT - open-vm-tools release from vsphere60p01tools - 9.10.2
 */

#ifndef RC_INVOKED
#define   TOOLS_VERSION_2015_PATCH01OVT TOOLS_VERSION_TO_UINT(TOOLS_VERSION_2015_PATCH01OVT_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_2015_PATCH01OVT_V_MJR  9
#define   TOOLS_VERSION_2015_PATCH01OVT_V_MNR  10
#define   TOOLS_VERSION_2015_PATCH01OVT_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_FC TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_FC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_FC_V_MJR  9
#define   TOOLS_VERSION_TORQUE_WRENCH_FC_V_MNR  12
#define   TOOLS_VERSION_TORQUE_WRENCH_FC_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_RC TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_RC_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_RC_V_MJR  9
#define   TOOLS_VERSION_TORQUE_WRENCH_RC_V_MNR  12
#define   TOOLS_VERSION_TORQUE_WRENCH_RC_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_RELEASE_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_RELEASE_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_PATCH1_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_PATCH1_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_PATCH1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE1_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE1_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE2_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE2_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE2_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE3OVT TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_UPDATE3OVT_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE3OVT_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE3OVT_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE3OVT_V_BASE 7

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE3 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_UPDATE3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE3_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE3_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE3_V_BASE 8

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE4 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_UPDATE4_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE4_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE4_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE4_V_BASE 9

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE5  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_UPDATE5_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE5_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE5_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_UPDATE5_V_BASE 10

#ifndef RC_INVOKED
#define   TOOLS_VERSION_TORQUE_WRENCH_FROZEN1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_TORQUE_WRENCH_FROZEN1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_TORQUE_WRENCH_FROZEN1_V_MJR  10
#define   TOOLS_VERSION_TORQUE_WRENCH_FROZEN1_V_MNR  0
#define   TOOLS_VERSION_TORQUE_WRENCH_FROZEN1_V_BASE 12

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SCREW_DRIVER_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SCREW_DRIVER_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SCREW_DRIVER_RELEASE_V_MJR  10
#define   TOOLS_VERSION_SCREW_DRIVER_RELEASE_V_MNR  1
#define   TOOLS_VERSION_SCREW_DRIVER_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SCREW_DRIVER_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE1_V_MJR  10
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE1_V_MNR  1
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SCREW_DRIVER_PATCH2  TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SCREW_DRIVER_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SCREW_DRIVER_PATCH2_V_MJR  10
#define   TOOLS_VERSION_SCREW_DRIVER_PATCH2_V_MNR  1
#define   TOOLS_VERSION_SCREW_DRIVER_PATCH2_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SCREW_DRIVER_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE2_V_MJR  10
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE2_V_MNR  1
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE2_V_BASE 10

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE3 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SCREW_DRIVER_UPDATE3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE3_V_MJR  10
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE3_V_MNR  1
#define   TOOLS_VERSION_SCREW_DRIVER_UPDATE3_V_BASE 15

#ifndef RC_INVOKED
#define   TOOLS_VERSION_CHAINSAW_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_CHAINSAW_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_CHAINSAW_RELEASE_V_MJR  10
#define   TOOLS_VERSION_CHAINSAW_RELEASE_V_MNR  2
#define   TOOLS_VERSION_CHAINSAW_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_CHAINSAW_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_CHAINSAW_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_CHAINSAW_PATCH1_V_MJR  10
#define   TOOLS_VERSION_CHAINSAW_PATCH1_V_MNR  2
#define   TOOLS_VERSION_CHAINSAW_PATCH1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_CHAINSAW_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_CHAINSAW_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_CHAINSAW_UPDATE1_V_MJR  10
#define   TOOLS_VERSION_CHAINSAW_UPDATE1_V_MNR  2
#define   TOOLS_VERSION_CHAINSAW_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_CHAINSAW_PATCH2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_CHAINSAW_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_CHAINSAW_PATCH2_V_MJR  10
#define   TOOLS_VERSION_CHAINSAW_PATCH2_V_MNR  2
#define   TOOLS_VERSION_CHAINSAW_PATCH2_V_BASE 7

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_RELEASE_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_RELEASE_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH1_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH1_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_APPDEF_BETA TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_APPDEF_BETA_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_APPDEF_BETA_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_APPDEF_BETA_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_APPDEF_BETA_V_BASE 2

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_UPDATE1_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_UPDATE1_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH2_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH2_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH2_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH3 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH3_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH3_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH3_V_BASE 7

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_UPDATE2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_UPDATE2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_UPDATE2_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_UPDATE2_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_UPDATE2_V_BASE 10

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH4 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH4_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH4_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH4_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH4_V_BASE 11

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH5 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH5_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH5_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH5_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH5_V_BASE 13

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH6 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH6_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH6_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH6_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH6_V_BASE 14

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH7 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH7_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH7_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH7_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH7_V_BASE 15

#ifndef RC_INVOKED

#define   TOOLS_VERSION_JACKHAMMER_UPDATE3 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_UPDATE3_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_UPDATE3_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_UPDATE3_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_UPDATE3_V_BASE 20

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH8 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH8_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH8_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH8_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH8_V_BASE 21

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH9 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH9_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH9_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH9_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH9_V_BASE 22

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH10 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH10_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH10_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH10_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH10_V_BASE 23

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH11 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH11_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH11_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH11_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH11_V_BASE 24

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH12 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH12_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH12_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH12_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH12_V_BASE 25

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JACKHAMMER_PATCH13 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JACKHAMMER_PATCH13_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JACKHAMMER_PATCH13_V_MJR  10
#define   TOOLS_VERSION_JACKHAMMER_PATCH13_V_MNR  3
#define   TOOLS_VERSION_JACKHAMMER_PATCH13_V_BASE 26

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SLEDGEHAMMER_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SLEDGEHAMMER_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SLEDGEHAMMER_RELEASE_V_MJR  11
#define   TOOLS_VERSION_SLEDGEHAMMER_RELEASE_V_MNR  0
#define   TOOLS_VERSION_SLEDGEHAMMER_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SLEDGEHAMMER_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SLEDGEHAMMER_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SLEDGEHAMMER_PATCH1_V_MJR  11
#define   TOOLS_VERSION_SLEDGEHAMMER_PATCH1_V_MNR  0
#define   TOOLS_VERSION_SLEDGEHAMMER_PATCH1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SLEDGEHAMMER_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SLEDGEHAMMER_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SLEDGEHAMMER_UPDATE1_V_MJR  11
#define   TOOLS_VERSION_SLEDGEHAMMER_UPDATE1_V_MNR  0
#define   TOOLS_VERSION_SLEDGEHAMMER_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SLEDGEHAMMER_PATCH2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SLEDGEHAMMER_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SLEDGEHAMMER_PATCH2_V_MJR  11
#define   TOOLS_VERSION_SLEDGEHAMMER_PATCH2_V_MNR  0
#define   TOOLS_VERSION_SLEDGEHAMMER_PATCH2_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_BALLPEENHAMMER_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_BALLPEENHAMMER_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_BALLPEENHAMMER_RELEASE_V_MJR  11
#define   TOOLS_VERSION_BALLPEENHAMMER_RELEASE_V_MNR  1
#define   TOOLS_VERSION_BALLPEENHAMMER_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_BALLPEENHAMMER_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_BALLPEENHAMMER_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_BALLPEENHAMMER_PATCH1_V_MJR  11
#define   TOOLS_VERSION_BALLPEENHAMMER_PATCH1_V_MNR  1
#define   TOOLS_VERSION_BALLPEENHAMMER_PATCH1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_BALLPEENHAMMER_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_BALLPEENHAMMER_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_BALLPEENHAMMER_UPDATE1_V_MJR  11
#define   TOOLS_VERSION_BALLPEENHAMMER_UPDATE1_V_MNR  1
#define   TOOLS_VERSION_BALLPEENHAMMER_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_BALLPEENHAMMER_PATCH2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_BALLPEENHAMMER_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_BALLPEENHAMMER_PATCH2_V_MJR  11
#define   TOOLS_VERSION_BALLPEENHAMMER_PATCH2_V_MNR  1
#define   TOOLS_VERSION_BALLPEENHAMMER_PATCH2_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_CRESCENT_WRENCH_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_CRESCENT_WRENCH_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_CRESCENT_WRENCH_RELEASE_V_MJR  11
#define   TOOLS_VERSION_CRESCENT_WRENCH_RELEASE_V_MNR  2
#define   TOOLS_VERSION_CRESCENT_WRENCH_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_CRESCENT_WRENCH_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_CRESCENT_WRENCH_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_CRESCENT_WRENCH_PATCH1_V_MJR  11
#define   TOOLS_VERSION_CRESCENT_WRENCH_PATCH1_V_MNR  2
#define   TOOLS_VERSION_CRESCENT_WRENCH_PATCH1_V_BASE 1

#ifndef RC_INVOKED
#define   TOOLS_VERSION_CRESCENT_WRENCH_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_CRESCENT_WRENCH_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_CRESCENT_WRENCH_UPDATE1_V_MJR  11
#define   TOOLS_VERSION_CRESCENT_WRENCH_UPDATE1_V_MNR  2
#define   TOOLS_VERSION_CRESCENT_WRENCH_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_CRESCENT_WRENCH_PATCH2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_CRESCENT_WRENCH_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_CRESCENT_WRENCH_PATCH2_V_MJR  11
#define   TOOLS_VERSION_CRESCENT_WRENCH_PATCH2_V_MNR  2
#define   TOOLS_VERSION_CRESCENT_WRENCH_PATCH2_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JIGSAW_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JIGSAW_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JIGSAW_RELEASE_V_MJR  11
#define   TOOLS_VERSION_JIGSAW_RELEASE_V_MNR  3
#define   TOOLS_VERSION_JIGSAW_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_JIGSAW_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_JIGSAW_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_JIGSAW_UPDATE1_V_MJR  11
#define   TOOLS_VERSION_JIGSAW_UPDATE1_V_MNR  3
#define   TOOLS_VERSION_JIGSAW_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SABRE_SAW_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SABRE_SAW_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SABRE_SAW_RELEASE_V_MJR  12
#define   TOOLS_VERSION_SABRE_SAW_RELEASE_V_MNR  0
#define   TOOLS_VERSION_SABRE_SAW_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SABRE_SAW_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SABRE_SAW_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SABRE_SAW_UPDATE1_V_MJR  12
#define   TOOLS_VERSION_SABRE_SAW_UPDATE1_V_MNR  0
#define   TOOLS_VERSION_SABRE_SAW_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SABRE_SAW_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SABRE_SAW_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SABRE_SAW_PATCH1_V_MJR  12
#define   TOOLS_VERSION_SABRE_SAW_PATCH1_V_MNR  0
#define   TOOLS_VERSION_SABRE_SAW_PATCH1_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_MITER_SAW_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_MITER_SAW_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_MITER_SAW_RELEASE_V_MJR  12
#define   TOOLS_VERSION_MITER_SAW_RELEASE_V_MNR  1
#define   TOOLS_VERSION_MITER_SAW_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_MITER_SAW_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_MITER_SAW_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_MITER_SAW_UPDATE1_V_MJR  12
#define   TOOLS_VERSION_MITER_SAW_UPDATE1_V_MNR  1
#define   TOOLS_VERSION_MITER_SAW_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_BANDSAW_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_BANDSAW_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_BANDSAW_RELEASE_V_MJR  12
#define   TOOLS_VERSION_BANDSAW_RELEASE_V_MNR  2
#define   TOOLS_VERSION_BANDSAW_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_BANDSAW_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_BANDSAW_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_BANDSAW_UPDATE1_V_MJR  12
#define   TOOLS_VERSION_BANDSAW_UPDATE1_V_MNR  2
#define   TOOLS_VERSION_BANDSAW_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_BANDSAW_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_BANDSAW_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_BANDSAW_PATCH1_V_MJR  12
#define   TOOLS_VERSION_BANDSAW_PATCH1_V_MNR  2
#define   TOOLS_VERSION_BANDSAW_PATCH1_V_BASE 6

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HEDGE_TRIMMER_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HEDGE_TRIMMER_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HEDGE_TRIMMER_RELEASE_V_MJR  12
#define   TOOLS_VERSION_HEDGE_TRIMMER_RELEASE_V_MNR  3
#define   TOOLS_VERSION_HEDGE_TRIMMER_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_HEDGE_TRIMMER_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_HEDGE_TRIMMER_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_HEDGE_TRIMMER_UPDATE1_V_MJR  12
#define   TOOLS_VERSION_HEDGE_TRIMMER_UPDATE1_V_MNR  3
#define   TOOLS_VERSION_HEDGE_TRIMMER_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SOCKET_WRENCH_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SOCKET_WRENCH_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SOCKET_WRENCH_RELEASE_V_MJR  12
#define   TOOLS_VERSION_SOCKET_WRENCH_RELEASE_V_MNR  4
#define   TOOLS_VERSION_SOCKET_WRENCH_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_SOCKET_WRENCH_UPDATE1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_SOCKET_WRENCH_UPDATE1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_SOCKET_WRENCH_UPDATE1_V_MJR  12
#define   TOOLS_VERSION_SOCKET_WRENCH_UPDATE1_V_MNR  4
#define   TOOLS_VERSION_SOCKET_WRENCH_UPDATE1_V_BASE 5

#ifndef RC_INVOKED
#define   TOOLS_VERSION_PLIERS_RELEASE TOOLS_VERSION_TO_UINT(TOOLS_VERSION_PLIERS_RELEASE_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_PLIERS_RELEASE_V_MJR  12
#define   TOOLS_VERSION_PLIERS_RELEASE_V_MNR  5
#define   TOOLS_VERSION_PLIERS_RELEASE_V_BASE 0

#ifndef RC_INVOKED
#define   TOOLS_VERSION_PLIERS_PATCH1 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_PLIERS_PATCH1_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_PLIERS_PATCH1_V_MJR  12
#define   TOOLS_VERSION_PLIERS_PATCH1_V_MNR  5
#define   TOOLS_VERSION_PLIERS_PATCH1_V_BASE 1


#ifndef RC_INVOKED
#define   TOOLS_VERSION_PLIERS_PATCH2 TOOLS_VERSION_TO_UINT(TOOLS_VERSION_PLIERS_PATCH2_V)
#endif /* RC_INVOKED */
#define   TOOLS_VERSION_PLIERS_PATCH2_V_MJR  12
#define   TOOLS_VERSION_PLIERS_PATCH2_V_MNR  5
#define   TOOLS_VERSION_PLIERS_PATCH2_V_BASE 2

#define   TOOLS_VERSION_CURRENT        TOOLS_VERSION_PLIERS_PATCH2
#define   TOOLS_VERSION_CURRENT_STR    TOOLS_VERSION_TO_STR(TOOLS_VERSION_PLIERS_PATCH2)
#define   TOOLS_VERSION_CURRENT_CSV    TOOLS_VERSION_TO_CSV(TOOLS_VERSION_PLIERS_PATCH2)

/*
 * The extended Tools version is the current Tools version with the
 * build number appended. Useful until the PRODUCT_VERSION
 * vs. PRODUCT_VERSION_STRING mess in vm_version.h is sorted out.
 *
 * For Windows tools, the fourth component of the version number must
 * be < 65k (circa), so we use PRODUCT_BUILD_NUMBER instead of
 * BUILD_NUMBER.
 */

#define TOOLS_VERSION_EXT_CURRENT_CSV \
   TOOLS_VERSION_CURRENT_CSV,PRODUCT_BUILD_NUMBER_NUMERIC
#define TOOLS_VERSION_EXT_CURRENT_STR \
   TOOLS_VERSION_CURRENT_STR "." PRODUCT_BUILD_NUMBER_NUMERIC_STRING

/* clang-format on */

#endif /* VM_TOOLS_VERSION_H */

#endif /* defined(USE_GOBUILD_TOOLS_VERSION) && !defined(GOBUILD_TOOLS_VERSION_USED) */
0707010000013C000081A40000000000000000000000016822550500000C55000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_valgrind.h  /*********************************************************
 * Copyright (c) 2014, 2021-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * vm_valgrind.h --
 *
 *      This header incorporates the client requests used by Valgrind
 *      (http://www.valgrind.org/) to instrument the binary for dynamic
 *      analysis.
 *
 *      Various hints are available to mark regions of memory as accessible,
 *      inaccessible, defined, undefined, to check the definedness or
 *      accessibliity of regions of memory, or to run code outside of
 *      Valgrind's instrumentation.
 *
 *      See the Valgrind headers for further information.
 *
 *      If USE_VALGRIND is defined, the Valgrind instrumentation macros will
 *      take effect.
 *
 *      If USE_VALGRIND is not defined, the Valgrind instrumentation macros
 *      will still be available but will have no effect (no-op).
 */

#ifndef _VM_VALGRIND_H_
#define _VM_VALGRIND_H_ 1

#ifdef USE_VALGRIND
# include "valgrind/memcheck.h"
#else

/*
 * No-ops for Valgrind macros we might use.  The Valgrind headers include their
 * own "NVALGRIND" mechanism to disable the emission of the Valgrind Client
 * Request magic sequences, but that assumes that we have suitable headers
 * available in all possible build environments.  NVALGRIND just turns each of
 * the VALGRIND_* macros into a default expression, usually 0, or a default
 * empty statement, "do { (void)0; } while (0);".  Let's do that ourselves.
 */

#define RUNNING_ON_VALGRIND 0
#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) (void)0
#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr, _qzz_len) (void)0
#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr, _qzz_len) (void)0
#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr, _qzz_len) (void)0
#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr, _qzz_len) (void)0
#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr, _qzz_len) (void)0

#endif

/*
 * VALGRIND_SPEED_FACTOR is an approximation of how much Valgrind's
 * instrumentation is likely to slow down execution.  It is a _very_ rough
 * guess because the actual slowdown will depend on the nature of the code
 * being executed, but regardless this is a handy macro for adjusting (by
 * multiplying or dividing) times or loop counts or anything else impacted by
 * Valgrind's instrumentation overhead.
 */

#define VALGRIND_SPEED_FACTOR (RUNNING_ON_VALGRIND ? 100 : 1)

#endif /* _VM_VALGRIND_H_ */
   0707010000013D000081A40000000000000000000000016822550500000C69000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_version.h   /*********************************************************
 * Copyright (c) 1998-2019,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * NOTE: This file should only be included when the build number
 *       stored in 'buildNumber.h' is needed.
 */

/*
 *
 * This file contain some of the code wrapped around marker tag like
 * MARKER_XXX_START / MARKER_XXX_END
 *
 * code section wrapped around these marker tag are getting processed in
 * bora/scons/modules/buildnum.sc to generate xx_fast.h header. these generated
 * header which is free from MACROS e.g. BUILD_NUMBER
 *
 * These MACROS are changing with every build and causing the file content
 * change. Eventually with every build the hash code of these headers and
 * source file including these headers changed. Due to this reason file are not
 * cacheable by build (both SCons/Bazel) and end up building them again and
 * again in place of downloading their build artifact from cache.
 *
 * to avoid multiple copy of similar header, xx_fast.h code is generated from
 * this file using these marker tag.
 *
 */

#ifndef VM_VERSION_H
#define VM_VERSION_H

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"
#include "vm_product.h"
//MARKER_FAST_REMOVE_START
#if defined(VMX86_TOOLS) || defined(VMX86_SYSIMAGE)
#include "vm_tools_version.h"
#endif
//MARKER_FAST_REMOVE_END
#include "vm_vmx_type.h"
#include "vm_compilation_options.h"

/*
 * buildNumber.h is generated by make/mk/buildNumber.mk to match
 * the build number defined by the BUILD_NUMBER and PRODUCT_BUILD_NUMBER
 * variables at the beginning of every build.
 */
//MARKER_FAST_REMOVE_START
#include "buildNumber.h"
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#include "buildNumberFast.h"
//MARKER_FAST_UNCOMMENT_END
/*
 * This is used so we can identify the build and release type
 * in any generated core files.
 */
//MARKER_FAST_UNCOMMENT_START
//#define BUILD_VERSION gBuildVersion
//MARKER_FAST_UNCOMMENT_END

//MARKER_FAST_REMOVE_START
#define BUILD_VERSION COMPILATION_OPTION BUILD_NUMBER
//MARKER_FAST_REMOVE_END

//MARKER_FAST_REMOVE_START
#include "vm_product_versions.h"
//MARKER_FAST_REMOVE_END
//MARKER_FAST_UNCOMMENT_START
//#include "vm_product_versions_fast.h"
//MARKER_FAST_UNCOMMENT_END

#endif /* VM_VERSION_H */
   0707010000013E000081A40000000000000000000000016822550500000755000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vm_vmx_type.h  /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/
#ifndef VM_VMX_TYPE_H
#define VM_VMX_TYPE_H

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

/*
 * This allows UIs and guest binaries to know what kind of VMX they
 * are dealing with. Don't change those values (only add new ones if
 * needed) because they rely on them --hpreg
 */

typedef enum {
   VMX_TYPE_UNSET,
   VMX_TYPE_EXPRESS, /* This deprecated type was used for VMware Express */
   VMX_TYPE_SCALABLE_SERVER,
   VMX_TYPE_WGS, /* This deprecated type was used for VMware Server */
   VMX_TYPE_WORKSTATION,
   VMX_TYPE_WORKSTATION_ENTERPRISE /* This deprecated type was used for ACE 1.x */
} VMX_Type;


/*
 * This allows UIs and guest binaries to know what platform the VMX is
 * running.
 */

typedef enum {
   VMX_PLATFORM_UNSET,
   VMX_PLATFORM_LINUX,
   VMX_PLATFORM_WIN32,
   VMX_PLATFORM_MACOS,
} VMX_Platform;

#endif
   0707010000013F000081A40000000000000000000000016822550500002252000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmblock.h  /*********************************************************
 * Copyright (C) 2006-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vmblock.h --
 *
 *   User-level interface to the vmblock device.
 *
 *   VMBLOCK_DEVICE should be opened with VMBLOCK_DEVICE_MODE mode. Then
 *   VMBLOCK_CONTROL should be called to perform blocking operations.
 *   The links which can be blocked are in the directory VMBLOCK_MOUNT_POINT.
 *
 *   VMBLOCK_CONTROL takes the file descriptor of the VMBLOCK_DEVICE, an
 *   operation, and the path of the target of the file being operated on (if
 *   applicable).
 *
 *   The operation should be one of:
 *   VMBLOCK_ADD_FILEBLOCK
 *   VMBLOCK_DEL_FILEBLOCK
 *   VMBLOCK_LIST_FILEBLOCKS
 *
 *   path should be something in /tmp/VMwareDnD/ rather than in
 *   VMBLOCK_MOUNT_POINT.
 *
 *   VMBLOCK_CONTROL returns 0 on success or returns -1 and sets errno on
 *   failure.
 */

#ifndef _VMBLOCK_H_
#define _VMBLOCK_H_

#if defined(sun) || defined(__FreeBSD__)
# include <sys/ioccom.h>
#endif

#if defined(__FreeBSD__)
# include <sys/param.h>
#endif

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * FUSE definitions. They are supposed to be used by userspace code and
 * therefore not guarded by ARCH defines since FUSE can potentially
 * be used on different operating systems.
 */

#define VMBLOCK_FUSE_ADD_FILEBLOCK     'a'
#define VMBLOCK_FUSE_DEL_FILEBLOCK     'd'
#ifdef VMX86_DEVEL
# define VMBLOCK_FUSE_LIST_FILEBLOCKS  'l'
#endif /* VMX86_DEVEL */

/*
 * If you try 'read'-ing from file descriptor vmblock-fuse is supposed
 * to respond with the following. It is used to check whether we deal
 * with FUSE or in-kernel implementation.
 */
#define VMBLOCK_FUSE_READ_RESPONSE     "I am VMBLOCK-FUSE"

#define VMBLOCK_FUSE_FS_NAME           "fuse.vmware-vmblock"
#define VMBLOCK_FUSE_MOUNT_POINT       "/var/run/vmblock-fuse"
#define VMBLOCK_FUSE_CTRL_MNTPNT       "blockdir"
#define VMBLOCK_FUSE_FS_ROOT           VMBLOCK_FUSE_MOUNT_POINT "/" VMBLOCK_FUSE_CTRL_MNTPNT
#define VMBLOCK_FUSE_DEVICE_NAME       "dev"
#define VMBLOCK_FUSE_DEVICE            VMBLOCK_FUSE_MOUNT_POINT "/" VMBLOCK_FUSE_DEVICE_NAME
#define VMBLOCK_FUSE_NOTIFY_MNTPNT     "notifydir"
#define VMBLOCK_FUSE_NOTIFY_ROOT       VMBLOCK_FUSE_MOUNT_POINT "/" VMBLOCK_FUSE_NOTIFY_MNTPNT
#define VMBLOCK_FUSE_DEVICE_MODE       O_RDWR

/* Commands for the control half of vmblock driver */
#if defined(vmblock_fuse)
/* These definitions are for vmblock-fuse module itself */
# include <unistd.h>
# include <limits.h>
# include <string.h>
# include <errno.h>
# include "vm_basic_types.h"
# define VMBLOCK_ADD_FILEBLOCK         VMBLOCK_FUSE_ADD_FILEBLOCK
# define VMBLOCK_DEL_FILEBLOCK         VMBLOCK_FUSE_DEL_FILEBLOCK
# ifdef VMX86_DEVEL
#  define VMBLOCK_LIST_FILEBLOCKS      VMBLOCK_FUSE_LIST_FILEBLOCKS
# endif /* VMX86_DEVEL */
/*
 * Some of the following names don't actually make much sense on their own.
 * They're used for consistency with the other ports. See the file header for
 * explanations of what they're used for.
 */
# define VMBLOCK_FS_NAME               VMBLOCK_FUSE_FS_NAME
# define VMBLOCK_DEVICE_NAME           VMBLOCK_FUSE_DEVICE_NAME
# define VMBLOCK_CONTROL_MOUNTPOINT    VMBLOCK_FUSE_CTRL_MNTPNT
# define VMBLOCK_FS_ROOT               VMBLOCK_FUSE_FS_ROOT
# define VMBLOCK_DEVICE                VMBLOCK_FUSE_DEVICE
# define VMBLOCK_DEVICE_MODE           VMBLOCK_FUSE_DEVICE_MODE
# define VMBLOCK_MOUNT_POINT           VMBLOCK_FUSE_MOUNT_POINT

#elif defined(__linux__)
# define VMBLOCK_ADD_FILEBLOCK         98
# define VMBLOCK_DEL_FILEBLOCK         99
# ifdef VMX86_DEVEL
#  define VMBLOCK_LIST_FILEBLOCKS      100
# endif
# define VMBLOCK_FS_NAME               "vmblock"
# define VMBLOCK_CONTROL_DIRNAME       VMBLOCK_FS_NAME
# define VMBLOCK_CONTROL_DEVNAME       "dev"
# define VMBLOCK_CONTROL_MOUNTPOINT    "mountPoint"
# define VMBLOCK_CONTROL_PROC_DIRNAME  "fs/" VMBLOCK_CONTROL_DIRNAME

# define VMBLOCK_MOUNT_POINT            "/proc/" VMBLOCK_CONTROL_PROC_DIRNAME   \
                                       "/" VMBLOCK_CONTROL_MOUNTPOINT
# define VMBLOCK_FS_ROOT                VMBLOCK_MOUNT_POINT
# define VMBLOCK_DEVICE                 "/proc/" VMBLOCK_CONTROL_PROC_DIRNAME   \
                                       "/" VMBLOCK_CONTROL_DEVNAME
# define VMBLOCK_DEVICE_MODE            O_WRONLY

#elif defined(sun) || defined(__FreeBSD__)
# define VMBLOCK_FS_NAME                "vmblock"
# define VMBLOCK_MOUNT_POINT            "/var/run/" VMBLOCK_FS_NAME
# define VMBLOCK_FS_ROOT                VMBLOCK_MOUNT_POINT
# define VMBLOCK_DEVICE                 VMBLOCK_MOUNT_POINT
# define VMBLOCK_DEVICE_MODE            O_RDONLY
# if defined(sun)                       /* if (sun) { */
   /*
    * Construct ioctl(2) commands for blocks.  _IO() is a helper macro to
    * construct unique command values more easily.  I chose 'v' because I
    * didn't see it being used elsewhere, and the command numbers begin at one.
    */
#  define VMBLOCK_ADD_FILEBLOCK          _IO('v', 1)
#  define VMBLOCK_DEL_FILEBLOCK          _IO('v', 2)
#  ifdef VMX86_DEVEL
#   define VMBLOCK_LIST_FILEBLOCKS       _IO('v', 3)
#  endif

# elif defined(__FreeBSD__)              /* } else if (FreeBSD) { */
   /*
    * Similar to Solaris, construct ioctl(2) commands for block operations.
    * Since the FreeBSD implementation does not change the user's passed-in
    * data (pathname), we use the _IOW macro to define commands which write
    * to the kernel.  (As opposed to _IOR or _IOWR.)  Groups 'v' and 'V'
    * are taken by terminal drivers, so I opted for group 'Z'.
    */
#  define VMBLOCK_ADD_FILEBLOCK          _IOW('Z', 1, char[MAXPATHLEN] )
#  define VMBLOCK_DEL_FILEBLOCK          _IOW('Z', 2, char[MAXPATHLEN] )
#  ifdef VMX86_DEVEL
#   define VMBLOCK_LIST_FILEBLOCKS       _IO('Z', 3)
#   define VMBLOCK_PURGE_FILEBLOCKS      _IO('Z', 4)
#  endif

# endif                                 /* } */
#else
# error "Unknown platform for vmblock."
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _VMBLOCK_H_ */
  07070100000140000081A400000000000000000000000168225505000014DD000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmblock_user.h /*********************************************************
 * Copyright (C) 2009-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vmblock_user.h --
 *
 *   Provides interfaces that allow user level programs to talk to the
 *   vmblock fs.
 */

#ifndef _VMBLOCK_USER_H_
#define _VMBLOCK_USER_H_

#include "vm_basic_types.h"
#include "vmblock.h"

#if defined(__cplusplus)
extern "C" {
#endif

static INLINE int
VMBLOCK_CONTROL_FUSE(int fd,            // IN
                     char op,           // IN
                     const char *path)  // IN
{
   /*
    * buffer needs room for an operation character and a string with max length
    * PATH_MAX - 1.
    */

   char buffer[PATH_MAX];
   size_t pathLength;

   pathLength = strlen(path);
   if (pathLength >= PATH_MAX) {
      errno = ENAMETOOLONG;
      return -1;
   }

   buffer[0] = op;
   memcpy(buffer + 1, path, pathLength);

   /*
    * The lseek is only to prevent the file pointer from overflowing;
    * vmblock-fuse ignores the file pointer / offset. Overflowing the file
    * pointer causes write to fail:
    * http://article.gmane.org/gmane.comp.file-systems.fuse.devel/6648
    * There's also a race condition here where many threads all calling
    * VMBLOCK_CONTROL at the same time could have all their seeks executed one
    * after the other, followed by all the writes. Again, it's not a problem
    * unless the file pointer overflows which is very unlikely with 32 bit
    * offsets and practically impossible with 64 bit offsets.
    */

   if (lseek(fd, 0, SEEK_SET) < 0) {
      return -1;
   }

   if (write(fd, buffer, pathLength + 1) < 0) {
      return -1;
   }

   return 0;
}

#if defined(vmblock_fuse)

#define VMBLOCK_CONTROL(fd, op, path) VMBLOCK_CONTROL_FUSE(fd, op, path)

#elif defined(__linux__)

static INLINE int
VMBLOCK_CONTROL(int fd, int op, const char *path)
{
   return write(fd, path, op);
}

#elif defined(__FreeBSD__)

static INLINE int
VMBLOCK_CONTROL(int fd, int cmd, const char *path)
{
   char tpath[MAXPATHLEN];
   size_t pathSize;

   if (path == NULL) {
      return -1;
   }

  /*
   * FreeBSD's ioctl data parameters must be of fixed size.  Guarantee a safe
   * buffer of size MAXPATHLEN by copying the user's string to one of our own.
   */
   pathSize = strlcpy(tpath, path, MAXPATHLEN);
   if (pathSize >= sizeof tpath) {
      return -1;
   }

   return ioctl(fd, cmd, tpath);
}

#elif defined(sun)

static INLINE int
VMBLOCK_CONTROL(int fd, int cmd, const char *path)
{
   return ioctl(fd, cmd, path);
}

#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _VMBLOCK_USER_H_
   07070100000141000081A40000000000000000000000016822550500000578000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmcheck.h  /*********************************************************
 * Copyright (c) 2006-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmcheck.h --
 *
 *      Utility functions for discovering our virtualization status.
 */

#ifndef __VMCHECK_H__
#   define __VMCHECK_H__

#include "vm_basic_types.h"

#ifdef __cplusplus
extern "C" {
#endif

Bool
VmCheck_GetVersion(uint32 *version, // OUT
                   uint32 *type);   // OUT

Bool
VmCheck_GetHWVersion(uint32 *hwversion); // OUT

Bool
VmCheck_IsVirtualWorld(void);

#ifdef __cplusplus
}
#endif

#endif /* __VMCHECK_H__ */
07070100000142000081A40000000000000000000000016822550500007A0C000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmci_defs.h    /*********************************************************
 * Copyright (c) 2005-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMCI_DEF_H_
#define _VMCI_DEF_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMMEXT
#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMMON
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "vm_atomic.h"
#include "vm_assert.h"

#if defined __cplusplus
extern "C" {
#endif

/* Register offsets. */
#define VMCI_STATUS_ADDR        0x00
#define VMCI_CONTROL_ADDR       0x04
#define VMCI_ICR_ADDR           0x08
#define VMCI_IMR_ADDR           0x0c
#define VMCI_DATA_OUT_ADDR      0x10
#define VMCI_DATA_IN_ADDR       0x14
#define VMCI_CAPS_ADDR          0x18
#define VMCI_RESULT_LOW_ADDR    0x1c
#define VMCI_RESULT_HIGH_ADDR   0x20
#define VMCI_DATA_OUT_LOW_ADDR  0x24
#define VMCI_DATA_OUT_HIGH_ADDR 0x28
#define VMCI_DATA_IN_LOW_ADDR   0x2c
#define VMCI_DATA_IN_HIGH_ADDR  0x30
#define VMCI_PAGE_SHIFT_ADDR    0x34

/* Max number of devices. */
#define VMCI_MAX_DEVICES 1

/* Status register bits. */
#define VMCI_STATUS_INT_ON     0x1

/* Control register bits. */
#define VMCI_CONTROL_RESET        0x1
#define VMCI_CONTROL_INT_ENABLE   0x2
#define VMCI_CONTROL_INT_DISABLE  0x4

/* Capabilities register bits. */
#define VMCI_CAPS_HYPERCALL     0x1
#define VMCI_CAPS_GUESTCALL     0x2
#define VMCI_CAPS_DATAGRAM      0x4
#define VMCI_CAPS_NOTIFICATIONS 0x8
#define VMCI_CAPS_PPN64         0x10
#define VMCI_CAPS_DMA_DATAGRAM  0x20
#define VMCI_CAPS_CLEAR_TO_ACK  (0x1 << 31)

#define VMCI_CAPS_NOT_ACKED (VMCI_CAPS_HYPERCALL | VMCI_CAPS_GUESTCALL | \
                             VMCI_CAPS_DATAGRAM | VMCI_CAPS_NOTIFICATIONS)

/* Interrupt Cause register bits. */
#define VMCI_ICR_DATAGRAM      0x1
#define VMCI_ICR_NOTIFICATION  0x2
#define VMCI_ICR_INOUT         0x4

/* Interrupt Mask register bits. */
#define VMCI_IMR_DATAGRAM      0x1
#define VMCI_IMR_NOTIFICATION  0x2
#define VMCI_IMR_INOUT         0x4

/* Interrupt type. */
typedef enum VMCIIntrType {
   VMCI_INTR_TYPE_INTX = 0,
   VMCI_INTR_TYPE_MSI =  1,
   VMCI_INTR_TYPE_MSIX = 2
} VMCIIntrType;

/*
 * Maximum MSI/MSI-X interrupt vectors in the device.
 */
#define VMCI_MAX_INTRS_NOTIFICATION 2
#define VMCI_MAX_INTRS_INOUT        3
#define VMCI_MAX_INTRS              VMCI_MAX_INTRS_INOUT

/*
 * Supported interrupt vectors.  There is one for each ICR value above,
 * but here they indicate the position in the vector array/message ID.
 */
#define VMCI_INTR_DATAGRAM     0
#define VMCI_INTR_NOTIFICATION 1
#define VMCI_INTR_INOUT        2


/*
 * A single VMCI device has an upper limit of 128 MiB on the amount of
 * memory that can be used for queue pairs. Since each queue pair
 * consists of at least two pages, the memory limit also dictates the
 * number of queue pairs a guest can create.
 * With the addition of a configurable max QP memory limit (see comment
 * for VMCI_MAX_GUEST_QP_MEMORY_CONFIG_MAX), VMCI_MAX_GUEST_QP_COUNT
 * will continue to the based off VMCI_MAX_GUEST_QP_MEMORY and not
 * adjusted according to the configured max QP memory limit.
 */
#define VMCI_MAX_GUEST_QP_MEMORY (128 * 1024 * 1024)
#define VMCI_MAX_GUEST_QP_COUNT  (VMCI_MAX_GUEST_QP_MEMORY / PAGE_SIZE / 2)

/*
 * VMX config option vmci.qpMemoryLimit can be used to set the max QP memory
 * limit. This value is restricted to a maximum of 1024 MiB. This VMX config
 * option is currently only valid for FSVMs.
 */
#define VMCI_MAX_GUEST_QP_MEMORY_CONFIG_MAX (1024 * 1024 * 1024)

/*
 * There can be at most PAGE_SIZE doorbells since there is one doorbell
 * per byte in the doorbell bitmap page.
 */
#define VMCI_MAX_GUEST_DOORBELL_COUNT PAGE_SIZE

/*
 * We have a fixed set of resource IDs available in the VMX.
 * This allows us to have a very simple implementation since we statically
 * know how many will create datagram handles. If a new caller arrives and
 * we have run out of slots we can manually increment the maximum size of
 * available resource IDs.
 */

typedef uint32 VMCI_Resource;

/* VMCI reserved hypervisor datagram resource IDs. */
#define VMCI_RESOURCES_QUERY        0
#define VMCI_GET_CONTEXT_ID         1
#define VMCI_SET_NOTIFY_BITMAP      2
#define VMCI_DOORBELL_LINK          3
#define VMCI_DOORBELL_UNLINK        4
#define VMCI_DOORBELL_NOTIFY        5
/*
 * VMCI_DATAGRAM_REQUEST_MAP and VMCI_DATAGRAM_REMOVE_MAP are
 * obsoleted by the removal of VM to VM communication.
 */
#define VMCI_DATAGRAM_REQUEST_MAP   6
#define VMCI_DATAGRAM_REMOVE_MAP    7
#define VMCI_EVENT_SUBSCRIBE        8
#define VMCI_EVENT_UNSUBSCRIBE      9
#define VMCI_QUEUEPAIR_ALLOC        10
#define VMCI_QUEUEPAIR_DETACH       11
/*
 * VMCI_VSOCK_VMX_LOOKUP was assigned to 12 for Fusion 3.0/3.1,
 * WS 7.0/7.1 and ESX 4.1
 */
#define VMCI_HGFS_TRANSPORT         13
#define VMCI_UNITY_PBRPC_REGISTER   14
/*
 * This resource is used for VMCI socket control packets sent to the
 * hypervisor (CID 0) because RID 1 is already reserved.
 */
#define VSOCK_PACKET_HYPERVISOR_RID 15
#define VMCI_RESOURCE_MAX           16
/*
 * The core VMCI device functionality only requires the resource IDs of
 * VMCI_QUEUEPAIR_DETACH and below.
 */
#define VMCI_CORE_DEVICE_RESOURCE_MAX  VMCI_QUEUEPAIR_DETACH

/*
 * VMCI reserved host datagram resource IDs.
 * vsock control channel has resource id 1.
 */
#define VMCI_DVFILTER_DATA_PATH_DATAGRAM 2

/* VMCI Ids. */
typedef uint32 VMCIId;

typedef struct VMCIIdRange {
   int8 action;   // VMCI_FA_X, for use in filters.
   VMCIId begin;  // Beginning of range
   VMCIId end;    // End of range
} VMCIIdRange;

typedef struct VMCIHandle {
   VMCIId context;
   VMCIId resource;
} VMCIHandle;

static INLINE VMCIHandle
VMCI_MAKE_HANDLE(VMCIId cid,  // IN:
                 VMCIId rid)  // IN:
{
   VMCIHandle h;
   h.context = cid;
   h.resource = rid;
   return h;
}

/*
 *----------------------------------------------------------------------
 *
 * VMCI_HANDLE_TO_UINT64 --
 *
 *     Helper for VMCI handle to uint64 conversion.
 *
 * Results:
 *     The uint64 value.
 *
 * Side effects:
 *     None.
 *
 *----------------------------------------------------------------------
 */

static INLINE uint64
VMCI_HANDLE_TO_UINT64(VMCIHandle handle) // IN:
{
   uint64 handle64;

   handle64 = handle.context;
   handle64 <<= 32;
   handle64 |= handle.resource;
   return handle64;
}


/*
 *----------------------------------------------------------------------
 *
 * VMCI_UINT64_TO_HANDLE --
 *
 *     Helper for uint64 to VMCI handle conversion.
 *
 * Results:
 *     The VMCI handle value.
 *
 * Side effects:
 *     None.
 *
 *----------------------------------------------------------------------
 */

static INLINE VMCIHandle
VMCI_UINT64_TO_HANDLE(uint64 handle64) // IN:
{
   VMCIId context = (VMCIId)(handle64 >> 32);
   VMCIId resource = (VMCIId)handle64;

   return VMCI_MAKE_HANDLE(context, resource);
}

#define VMCI_HANDLE_TO_CONTEXT_ID(_handle) ((_handle).context)
#define VMCI_HANDLE_TO_RESOURCE_ID(_handle) ((_handle).resource)
#define VMCI_HANDLE_EQUAL(_h1, _h2) ((_h1).context == (_h2).context && \
				     (_h1).resource == (_h2).resource)

#define VMCI_INVALID_ID 0xFFFFFFFF
static const VMCIHandle VMCI_INVALID_HANDLE = {VMCI_INVALID_ID,
					       VMCI_INVALID_ID};

#define VMCI_HANDLE_INVALID(_handle)   \
   VMCI_HANDLE_EQUAL((_handle), VMCI_INVALID_HANDLE)

/*
 * The below defines can be used to send anonymous requests.
 * This also indicates that no response is expected.
 */
#define VMCI_ANON_SRC_CONTEXT_ID   VMCI_INVALID_ID
#define VMCI_ANON_SRC_RESOURCE_ID  VMCI_INVALID_ID
#define VMCI_ANON_SRC_HANDLE       VMCI_MAKE_HANDLE(VMCI_ANON_SRC_CONTEXT_ID, \
						    VMCI_ANON_SRC_RESOURCE_ID)

/* The lowest 16 context ids are reserved for internal use. */
#define VMCI_RESERVED_CID_LIMIT 16

/*
 * Hypervisor context id, used for calling into hypervisor
 * supplied services from the VM.
 */
#define VMCI_HYPERVISOR_CONTEXT_ID 0

/*
 * Well-known context id, a logical context that contains a set of
 * well-known services. This context ID is now obsolete.
 */
#define VMCI_WELL_KNOWN_CONTEXT_ID 1

/*
 * Context ID used by host endpoints.
 */
#define VMCI_HOST_CONTEXT_ID  2
#define VMCI_HOST_CONTEXT_INVALID_EVENT         ((uintptr_t)~0)

#define VMCI_CONTEXT_IS_VM(_cid) (VMCI_INVALID_ID != _cid && \
                                  _cid > VMCI_HOST_CONTEXT_ID)

/*
 * The VMCI_CONTEXT_RESOURCE_ID is used together with VMCI_MAKE_HANDLE to make
 * handles that refer to a specific context.
 */
#define VMCI_CONTEXT_RESOURCE_ID 0


/*
 *-----------------------------------------------------------------------------
 *
 * VMCI error codes.
 *
 *-----------------------------------------------------------------------------
 */

#define VMCI_SUCCESS_QUEUEPAIR_ATTACH     5
#define VMCI_SUCCESS_QUEUEPAIR_CREATE     4
#define VMCI_SUCCESS_LAST_DETACH          3
#define VMCI_SUCCESS_ACCESS_GRANTED       2
#define VMCI_SUCCESS_ENTRY_DEAD           1
#define VMCI_SUCCESS                      0LL
#define VMCI_ERROR_INVALID_RESOURCE      (-1)
#define VMCI_ERROR_INVALID_ARGS          (-2)
#define VMCI_ERROR_NO_MEM                (-3)
#define VMCI_ERROR_DATAGRAM_FAILED       (-4)
#define VMCI_ERROR_MORE_DATA             (-5)
#define VMCI_ERROR_NO_MORE_DATAGRAMS     (-6)
#define VMCI_ERROR_NO_ACCESS             (-7)
#define VMCI_ERROR_NO_HANDLE             (-8)
#define VMCI_ERROR_DUPLICATE_ENTRY       (-9)
#define VMCI_ERROR_DST_UNREACHABLE       (-10)
#define VMCI_ERROR_PAYLOAD_TOO_LARGE     (-11)
#define VMCI_ERROR_INVALID_PRIV          (-12)
#define VMCI_ERROR_GENERIC               (-13)
#define VMCI_ERROR_PAGE_ALREADY_SHARED   (-14)
#define VMCI_ERROR_CANNOT_SHARE_PAGE     (-15)
#define VMCI_ERROR_CANNOT_UNSHARE_PAGE   (-16)
#define VMCI_ERROR_NO_PROCESS            (-17)
#define VMCI_ERROR_NO_DATAGRAM           (-18)
#define VMCI_ERROR_NO_RESOURCES          (-19)
#define VMCI_ERROR_UNAVAILABLE           (-20)
#define VMCI_ERROR_NOT_FOUND             (-21)
#define VMCI_ERROR_ALREADY_EXISTS        (-22)
#define VMCI_ERROR_NOT_PAGE_ALIGNED      (-23)
#define VMCI_ERROR_INVALID_SIZE          (-24)
#define VMCI_ERROR_REGION_ALREADY_SHARED (-25)
#define VMCI_ERROR_TIMEOUT               (-26)
#define VMCI_ERROR_DATAGRAM_INCOMPLETE   (-27)
#define VMCI_ERROR_INCORRECT_IRQL        (-28)
#define VMCI_ERROR_EVENT_UNKNOWN         (-29)
#define VMCI_ERROR_OBSOLETE              (-30)
#define VMCI_ERROR_QUEUEPAIR_MISMATCH    (-31)
#define VMCI_ERROR_QUEUEPAIR_NOTSET      (-32)
#define VMCI_ERROR_QUEUEPAIR_NOTOWNER    (-33)
#define VMCI_ERROR_QUEUEPAIR_NOTATTACHED (-34)
#define VMCI_ERROR_QUEUEPAIR_NOSPACE     (-35)
#define VMCI_ERROR_QUEUEPAIR_NODATA      (-36)
#define VMCI_ERROR_BUSMEM_INVALIDATION   (-37)
#define VMCI_ERROR_MODULE_NOT_LOADED     (-38)
#define VMCI_ERROR_DEVICE_NOT_FOUND      (-39)
#define VMCI_ERROR_QUEUEPAIR_NOT_READY   (-40)
#define VMCI_ERROR_WOULD_BLOCK           (-41)

/* VMCI clients should return error code withing this range */
#define VMCI_ERROR_CLIENT_MIN     (-500)
#define VMCI_ERROR_CLIENT_MAX     (-550)

/* Internal error codes. */
#define VMCI_SHAREDMEM_ERROR_BAD_CONTEXT (-1000)

#define VMCI_PATH_MAX 256

/* VMCI reserved events. */
typedef uint32 VMCI_Event;

#define VMCI_EVENT_CTX_ID_UPDATE  0  // Only applicable to guest endpoints
#define VMCI_EVENT_CTX_REMOVED    1  // Applicable to guest and host
#define VMCI_EVENT_QP_RESUMED     2  // Only applicable to guest endpoints
#define VMCI_EVENT_QP_PEER_ATTACH 3  // Applicable to guest, host and VMX
#define VMCI_EVENT_QP_PEER_DETACH 4  // Applicable to guest, host and VMX
#define VMCI_EVENT_MEM_ACCESS_ON  5  // Applicable to VMX and vmk.  On vmk,
                                     // this event has the Context payload type.
#define VMCI_EVENT_MEM_ACCESS_OFF 6  // Applicable to VMX and vmk.  Same as
                                     // above for the payload type.
#define VMCI_EVENT_GUEST_PAUSED   7  // Applicable to vmk. This event has the
                                     // Context payload type.
#define VMCI_EVENT_GUEST_UNPAUSED 8  // Applicable to vmk. Same as above for
                                     // the payload type.
#define VMCI_EVENT_MAX            9

/*
 * Of the above events, a few are reserved for use in the VMX, and
 * other endpoints (guest and host kernel) should not use them. For
 * the rest of the events, we allow both host and guest endpoints to
 * subscribe to them, to maintain the same API for host and guest
 * endpoints.
 */

#define VMCI_EVENT_VALID_VMX(_event) (_event == VMCI_EVENT_QP_PEER_ATTACH || \
                                      _event == VMCI_EVENT_QP_PEER_DETACH || \
                                      _event == VMCI_EVENT_MEM_ACCESS_ON || \
                                      _event == VMCI_EVENT_MEM_ACCESS_OFF)

#if defined(VMX86_SERVER)
#define VMCI_EVENT_VALID(_event) (_event < VMCI_EVENT_MAX)
#else // VMX86_SERVER
#define VMCI_EVENT_VALID(_event) (_event < VMCI_EVENT_MAX && \
                                  _event != VMCI_EVENT_MEM_ACCESS_ON && \
                                  _event != VMCI_EVENT_MEM_ACCESS_OFF && \
                                  _event != VMCI_EVENT_GUEST_PAUSED && \
                                  _event != VMCI_EVENT_GUEST_UNPAUSED)
#endif // VMX86_SERVER

/* Reserved guest datagram resource ids. */
#define VMCI_EVENT_HANDLER 0

/* VMCI privileges. */
typedef enum VMCIResourcePrivilegeType {
   VMCI_PRIV_CH_PRIV,
   VMCI_PRIV_DESTROY_RESOURCE,
   VMCI_PRIV_ASSIGN_CLIENT,
   VMCI_PRIV_DG_CREATE,
   VMCI_PRIV_DG_SEND,
   VMCI_PRIV_NOTIFY,
   VMCI_NUM_PRIVILEGES,
} VMCIResourcePrivilegeType;

/*
 * VMCI coarse-grained privileges (per context or host
 * process/endpoint. An entity with the restricted flag is only
 * allowed to interact with the hypervisor and trusted entities.
 */
typedef uint32 VMCIPrivilegeFlags;

#define VMCI_PRIVILEGE_FLAG_RESTRICTED     0x01
#define VMCI_PRIVILEGE_FLAG_TRUSTED        0x02
#define VMCI_PRIVILEGE_ALL_FLAGS           (VMCI_PRIVILEGE_FLAG_RESTRICTED | \
                                            VMCI_PRIVILEGE_FLAG_TRUSTED)
#define VMCI_NO_PRIVILEGE_FLAGS            0x00
#define VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS  VMCI_NO_PRIVILEGE_FLAGS
#define VMCI_LEAST_PRIVILEGE_FLAGS         VMCI_PRIVILEGE_FLAG_RESTRICTED
#define VMCI_MAX_PRIVILEGE_FLAGS           VMCI_PRIVILEGE_FLAG_TRUSTED

#define VMCI_PUBLIC_GROUP_NAME "vmci public group"
/* 0 through VMCI_RESERVED_RESOURCE_ID_MAX are reserved. */
#define VMCI_RESERVED_RESOURCE_ID_MAX 1023

#define VMCI_DOMAIN_NAME_MAXLEN  32

#define VMCI_LGPFX "VMCI: "
#define VMCI_DRIVER_NAME "vmci"


/*
 * VMCIQueueHeader
 *
 * A Queue cannot stand by itself as designed.  Each Queue's header
 * contains a pointer into itself (the producerTail) and into its peer
 * (consumerHead).  The reason for the separation is one of
 * accessibility: Each end-point can modify two things: where the next
 * location to enqueue is within its produceQ (producerTail); and
 * where the next dequeue location is in its consumeQ (consumerHead).
 *
 * An end-point cannot modify the pointers of its peer (guest to
 * guest; NOTE that in the host both queue headers are mapped r/w).
 * But, each end-point needs read access to both Queue header
 * structures in order to determine how much space is used (or left)
 * in the Queue.  This is because for an end-point to know how full
 * its produceQ is, it needs to use the consumerHead that points into
 * the produceQ but -that- consumerHead is in the Queue header for
 * that end-points consumeQ.
 *
 * Thoroughly confused?  Sorry.
 *
 * producerTail: the point to enqueue new entrants.  When you approach
 * a line in a store, for example, you walk up to the tail.
 *
 * consumerHead: the point in the queue from which the next element is
 * dequeued.  In other words, the next in line is the one at the
 * head of the line.
 *
 * Also, producerTail points to an empty byte in the Queue, whereas
 * consumerHead points to a valid byte of data (unless producerTail ==
 * consumerHead in which case consumerHead does not point to a valid
 * byte of data).
 *
 * For a queue of buffer 'size' bytes, the tail and head pointers will be in
 * the range [0, size-1].
 *
 * If produceQHeader->producerTail == consumeQHeader->consumerHead
 * then the produceQ is empty.
 */

typedef struct VMCIQueueHeader {
   /* All fields are 64bit and aligned. */
   VMCIHandle    handle;       /* Identifier. */
   Atomic_uint64 producerTail; /* Offset in this queue. */
   Atomic_uint64 consumerHead; /* Offset in peer queue. */
} VMCIQueueHeader;


/*
 * If one client of a QueuePair is a 32bit entity, we restrict the QueuePair
 * size to be less than 4GB, and use 32bit atomic operations on the head and
 * tail pointers. 64bit atomic read on a 32bit entity involves cmpxchg8b which
 * is an atomic read-modify-write. This will cause traces to fire when a 32bit
 * consumer tries to read the producer's tail pointer, for example, because the
 * consumer has read-only access to the producer's tail pointer.
 *
 * We provide the following macros to invoke 32bit or 64bit atomic operations
 * based on the architecture the code is being compiled on.
 */

/* Architecture independent maximum queue size. */
#define QP_MAX_QUEUE_SIZE_ARCH_ANY   CONST64U(0xffffffff)

#ifdef VM_64BIT
#  define QP_MAX_QUEUE_SIZE_ARCH     CONST64U(0xffffffffffffffff)
#  define QPAtomic_ReadOffset(x)     Atomic_Read64(x)
#  define QPAtomic_WriteOffset(x, y) Atomic_Write64(x, y)
#else
   /*
    * Wrappers below are being used to call Atomic_Read32 because of the
    * 'type punned' compilation warning received when Atomic_Read32 is
    * called with a Atomic_uint64 pointer typecasted to Atomic_uint32
    * pointer from QPAtomic_ReadOffset. Ditto with QPAtomic_WriteOffset.
    */

   static INLINE uint32
   TypeSafe_Atomic_Read32(void *var) // IN:
   {
      return Atomic_Read32((Atomic_uint32 *)(var));
   }

   static INLINE void
   TypeSafe_Atomic_Write32(void *var, uint32 val) // IN:
   {
      Atomic_Write32((Atomic_uint32 *)(var), (uint32)(val));
   }

#  define QP_MAX_QUEUE_SIZE_ARCH  CONST64U(0xffffffff)
#  define QPAtomic_ReadOffset(x)  TypeSafe_Atomic_Read32((void *)(x))
#  define QPAtomic_WriteOffset(x, y) \
          TypeSafe_Atomic_Write32((void *)(x), (uint32)(y))
#endif	/* __x86_64__  */


static INLINE PPN32
VMCI_PPN64_TO_PPN32(PPN ppn)
{
   ASSERT(ppn <= MAX_UINT32);
   return (PPN32)ppn;
}

/*
 *-----------------------------------------------------------------------------
 *
 * QPAddPointer --
 *
 *      Helper to add a given offset to a head or tail pointer. Wraps the value
 *      of the pointer around the max size of the queue.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
QPAddPointer(Atomic_uint64 *var, // IN:
             size_t add,         // IN:
             uint64 size)        // IN:
{
   uint64 newVal = QPAtomic_ReadOffset(var);

   if (newVal >= size - add) {
      newVal -= size;
   }
   newVal += add;

   QPAtomic_WriteOffset(var, newVal);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_ProducerTail() --
 *
 *      Helper routine to get the Producer Tail from the supplied queue.
 *
 * Results:
 *      The contents of the queue's producer tail.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint64
VMCIQueueHeader_ProducerTail(const VMCIQueueHeader *qHeader) // IN:
{
   VMCIQueueHeader *qh = (VMCIQueueHeader *)qHeader;
   return QPAtomic_ReadOffset(&qh->producerTail);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_ConsumerHead() --
 *
 *      Helper routine to get the Consumer Head from the supplied queue.
 *
 * Results:
 *      The contents of the queue's consumer tail.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint64
VMCIQueueHeader_ConsumerHead(const VMCIQueueHeader *qHeader) // IN:
{
   VMCIQueueHeader *qh = (VMCIQueueHeader *)qHeader;
   return QPAtomic_ReadOffset(&qh->consumerHead);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_AddProducerTail() --
 *
 *      Helper routine to increment the Producer Tail.  Fundamentally,
 *      QPAddPointer() is used to manipulate the tail itself.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
VMCIQueueHeader_AddProducerTail(VMCIQueueHeader *qHeader, // IN/OUT:
                                size_t add,               // IN:
                                uint64 queueSize)         // IN:
{
   QPAddPointer(&qHeader->producerTail, add, queueSize);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_AddConsumerHead() --
 *
 *      Helper routine to increment the Consumer Head.  Fundamentally,
 *      QPAddPointer() is used to manipulate the head itself.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
VMCIQueueHeader_AddConsumerHead(VMCIQueueHeader *qHeader, // IN/OUT:
                                size_t add,               // IN:
                                uint64 queueSize)         // IN:
{
   QPAddPointer(&qHeader->consumerHead, add, queueSize);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_CheckAlignment --
 *
 *      Checks if the given queue is aligned to page boundary.  Returns TRUE if
 *      the alignment is good.
 *
 * Results:
 *      TRUE or FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
VMCIQueueHeader_CheckAlignment(const VMCIQueueHeader *qHeader) // IN:
{
   uintptr_t hdr, offset;

   hdr = (uintptr_t) qHeader;
   offset = hdr & (PAGE_SIZE -1);

   return offset == 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_GetPointers --
 *
 *      Helper routine for getting the head and the tail pointer for a queue.
 *      Both the VMCIQueues are needed to get both the pointers for one queue.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
VMCIQueueHeader_GetPointers(const VMCIQueueHeader *produceQHeader, // IN:
                            const VMCIQueueHeader *consumeQHeader, // IN:
                            uint64 *producerTail,                  // OUT:
                            uint64 *consumerHead)                  // OUT:
{
   if (producerTail) {
      *producerTail = VMCIQueueHeader_ProducerTail(produceQHeader);
   }

   if (consumerHead) {
      *consumerHead = VMCIQueueHeader_ConsumerHead(consumeQHeader);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_ResetPointers --
 *
 *      Reset the tail pointer (of "this" queue) and the head pointer (of
 *      "peer" queue).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
VMCIQueueHeader_ResetPointers(VMCIQueueHeader *qHeader) // IN/OUT:
{
   QPAtomic_WriteOffset(&qHeader->producerTail, CONST64U(0));
   QPAtomic_WriteOffset(&qHeader->consumerHead, CONST64U(0));
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_Init --
 *
 *      Initializes a queue's state (head & tail pointers).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
VMCIQueueHeader_Init(VMCIQueueHeader *qHeader, // IN/OUT:
                     const VMCIHandle handle)  // IN:
{
   qHeader->handle = handle;
   VMCIQueueHeader_ResetPointers(qHeader);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_FreeSpace --
 *
 *      Finds available free space in a produce queue to enqueue more
 *      data or reports an error if queue pair corruption is detected.
 *
 * Results:
 *      Free space size in bytes or an error code.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE int64
VMCIQueueHeader_FreeSpace(const VMCIQueueHeader *produceQHeader, // IN:
                          const VMCIQueueHeader *consumeQHeader, // IN:
                          const uint64 produceQSize)             // IN:
{
   uint64 tail;
   uint64 head;
   uint64 freeSpace;

   tail = VMCIQueueHeader_ProducerTail(produceQHeader);
   head = VMCIQueueHeader_ConsumerHead(consumeQHeader);

   if (tail >= produceQSize || head >= produceQSize) {
      return VMCI_ERROR_INVALID_SIZE;
   }

   /*
    * Deduct 1 to avoid tail becoming equal to head which causes ambiguity. If
    * head and tail are equal it means that the queue is empty.
    */

   if (tail >= head) {
      freeSpace = produceQSize - (tail - head) - 1;
   } else {
      freeSpace = head - tail - 1;
   }

   return freeSpace;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMCIQueueHeader_BufReady --
 *
 *      VMCIQueueHeader_FreeSpace() does all the heavy lifting of
 *      determing the number of free bytes in a Queue.  This routine,
 *      then subtracts that size from the full size of the Queue so
 *      the caller knows how many bytes are ready to be dequeued.
 *
 * Results:
 *      On success, available data size in bytes (up to MAX_INT64).
 *      On failure, appropriate error code.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE int64
VMCIQueueHeader_BufReady(const VMCIQueueHeader *consumeQHeader, // IN:
                         const VMCIQueueHeader *produceQHeader, // IN:
                         const uint64 consumeQSize)             // IN:
{
   int64 freeSpace;

   freeSpace = VMCIQueueHeader_FreeSpace(consumeQHeader,
                                         produceQHeader,
                                         consumeQSize);
   if (freeSpace < VMCI_SUCCESS) {
      return freeSpace;
   } else {
      return consumeQSize - freeSpace - 1;
   }
}


/*
 * Defines for the VMCI traffic filter:
 * - VMCI_FA_<name> defines the filter action values
 * - VMCI_FP_<name> defines the filter protocol values
 * - VMCI_FD_<name> defines the direction values (guest or host)
 * - VMCI_FT_<name> are the type values (allow or deny)
 */

#define VMCI_FA_INVALID -1
#define VMCI_FA_ALLOW    0
#define VMCI_FA_DENY     (VMCI_FA_ALLOW + 1)
#define VMCI_FA_MAX      (VMCI_FA_DENY + 1)

#define VMCI_FP_INVALID     -1
#define VMCI_FP_HYPERVISOR   0
#define VMCI_FP_QUEUEPAIR    (VMCI_FP_HYPERVISOR + 1)
#define VMCI_FP_DOORBELL     (VMCI_FP_QUEUEPAIR + 1)
#define VMCI_FP_DATAGRAM     (VMCI_FP_DOORBELL + 1)
#define VMCI_FP_STREAMSOCK   (VMCI_FP_DATAGRAM + 1)
#define VMCI_FP_ANY          (VMCI_FP_STREAMSOCK + 1)
#define VMCI_FP_MAX          (VMCI_FP_ANY + 1)

#define VMCI_FD_INVALID  -1
#define VMCI_FD_GUEST     0
#define VMCI_FD_HOST      (VMCI_FD_GUEST + 1)
#define VMCI_FD_ANY       (VMCI_FD_HOST + 1)
#define VMCI_FD_MAX       (VMCI_FD_ANY + 1)

/*
 * The filter list tracks VMCI Id ranges for a given filter.
 */

typedef struct {
   uint32 len;
   VMCIIdRange *list;
} VMCIFilterList;


/*
 * The filter info is used to communicate the filter configuration
 * from the VMX to the host kernel.
 */

typedef struct {
   VA64   list;   // List of VMCIIdRange
   uint32 len;    // Length of list
   uint8  dir;    // VMCI_FD_X
   uint8  proto;  // VMCI_FP_X
} VMCIFilterInfo;

/*
 * In the host kernel, the ingoing and outgoing filters are
 * separated. The VMCIProtoFilters type captures all filters in one
 * direction. The VMCIFilters type captures all filters.
 */

typedef VMCIFilterList VMCIProtoFilters[VMCI_FP_MAX];
typedef VMCIProtoFilters VMCIFilters[VMCI_FD_MAX];


/*
 * Common header for describing a buffer used for VMCI datagram
 * send/receive using DMA. The header is placed in a single
 * page buffer, and the address is presented to the device through
 * the VMCI_DATA_OUT_LOW_ADDR/VMCI_DATA_OUT_HIGH_ADDR registers
 * for send, and VMCI_DATA_IN_LOW_ADDR/VMCI_DATA_IN_HIGH_ADDR for
 * receives. If opcode is 0, the header is followed immediately by
 * the data area of the specified size in contiguous physical
 * addresses. If opcode is 1, the header is followed by a series
 * of scatter gather array elements (formatted as VMCISgElem)
 * each describing contiguous physical buffers. The scatter gather
 * array elements must all fit within the same page as the header.
 * On send, the busy flag will be set by the guest when the buffer
 * is ready for sending, and cleared by the device when the send
 * is complete. On receive, the busy flag is cleared by the guest
 * when the buffer is ready to receive new datagrams, and set by
 * the device when the incoming datagrams have been copied to the
 * buffer and are ready for guest consumption.
 */

#define VMCI_DATA_IN_OUT_DATA_INLINE   0
#define VMCI_DATA_IN_OUT_DATA_SG_ARRAY 1

typedef struct {
   uint32 busy;
   uint32 opcode; // 0: data follows header, 1: sg array follows header
   uint32 size;   // size of data or number of sg elements
   uint32 rsvd;
   uint64 result; // Result of a send datagram operation
} VMCIDataInOutHeader;

/*
 * Format of the the scatter gather element used by SG arrays following
 * VMCIDataInOutHeader.
 */

typedef struct {
   uint64 addr;  // guest physical address
   uint64 size;  // length of data
} VMCISgElem;

#if defined __cplusplus
} // extern "C"
#endif

#endif // _VMCI_DEF_H_
07070100000143000081A40000000000000000000000016822550500007459000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmci_sockets.h /*********************************************************
 * Copyright (C) 2007-2017, 2019, 2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmci_sockets.h --
 *
 *    vSockets public constants and types.
 */

#ifndef _VMCI_SOCKETS_H_
#define _VMCI_SOCKETS_H_


#if defined(_WIN32)
#  if !defined(NT_INCLUDED)
#     include <winsock2.h>
#  endif // !NT_INCLUDED
#else // _WIN32
#if defined(__linux__) && !defined(VMKERNEL)
#  if !defined(__KERNEL__)
#    include <sys/socket.h>
#  endif // __KERNEL__
#else // linux && !VMKERNEL
#  if defined(__APPLE__)
#    include <sys/socket.h>
#    include <string.h>
#  elif defined(__FreeBSD__)
#     include <sys/socket.h>
#  endif // __FreeBSD__
#endif // linux && !VMKERNEL
#endif

#if defined __cplusplus
extern "C" {
#endif


/**
 * \brief Option name for STREAM socket buffer size.
 *
 * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set
 * or get an \c unsigned \c long \c long that specifies the size of the
 * buffer underlying a vSockets STREAM socket.
 *
 * \note Value is clamped to the MIN and MAX.
 *
 * \see VMCISock_GetAFValueFd()
 * \see SO_VMCI_BUFFER_MIN_SIZE
 * \see SO_VMCI_BUFFER_MAX_SIZE
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * unsigned long long val = 0x1000;
 * int fd = socket(af, SOCK_STREAM, 0);
 * setsockopt(fd, af, SO_VMCI_BUFFER_SIZE, &val, sizeof val);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_BUFFER_SIZE                 0

/**
 * \brief Option name for STREAM socket minimum buffer size.
 *
 * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set
 * or get an \c unsigned \c long \c long that specifies the minimum size
 * allowed for the buffer underlying a vSockets STREAM socket.
 *
 * \see VMCISock_GetAFValueFd()
 * \see SO_VMCI_BUFFER_SIZE
 * \see SO_VMCI_BUFFER_MAX_SIZE
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * unsigned long long val = 0x500;
 * int fd = socket(af, SOCK_STREAM, 0);
 * setsockopt(fd, af, SO_VMCI_BUFFER_MIN_SIZE, &val, sizeof val);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_BUFFER_MIN_SIZE             1

/**
 * \brief Option name for STREAM socket maximum buffer size.
 *
 * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set or
 * get an unsigned long long that specifies the maximum size allowed for the
 * buffer underlying a vSockets STREAM socket.
 *
 * \see VMCISock_GetAFValueFd()
 * \see SO_VMCI_BUFFER_SIZE
 * \see SO_VMCI_BUFFER_MIN_SIZE
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * unsigned long long val = 0x4000;
 * int fd = socket(af, SOCK_STREAM, 0);
 * setsockopt(fd, af, SO_VMCI_BUFFER_MAX_SIZE, &val, sizeof val);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_BUFFER_MAX_SIZE             2

/**
 * \brief Option name for socket peer's host-specific VM ID.
 *
 * Use as the option name in \c getsockopt(3) to get a host-specific identifier
 * for the peer endpoint's VM.  The identifier is a signed integer.
 *
 * \note Only available for ESX (VMKernel/userworld) endpoints.
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * int id;
 * socklen_t len = sizeof id;
 * int fd = socket(af, SOCK_DGRAM, 0);
 * getsockopt(fd, af, SO_VMCI_PEER_HOST_VM_ID, &id, &len);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_PEER_HOST_VM_ID             3

/**
 * \brief Option name for socket's service label.
 *
 * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set or
 * get the service label for a socket.  The service label is a C-style
 * NUL-terminated string.
 *
 * \note Only available for ESX (VMkernel/userworld) endpoints.
 */

#define SO_VMCI_SERVICE_LABEL               4

/**
 * \brief Option name for determining if a socket is trusted.
 *
 * Use as the option name in \c getsockopt(3) to determine if a socket is
 * trusted.  The value is a signed integer.
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * int trusted;
 * socklen_t len = sizeof trusted;
 * int fd = socket(af, SOCK_DGRAM, 0);
 * getsockopt(fd, af, SO_VMCI_TRUSTED, &trusted, &len);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_TRUSTED                     5

/**
 * \brief Option name for STREAM socket connection timeout.
 *
 * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to set or
 * get the connection timeout for a STREAM socket.  The value is platform
 * dependent.  On ESX, Linux and Mac OS, it is a \c struct \c timeval.
 * On Windows, it is a \c DWORD.
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * struct timeval t = { 5, 100000 }; // 5.1 seconds
 * int fd = socket(af, SOCK_STREAM, 0);
 * setsockopt(fd, af, SO_VMCI_CONNECT_TIMEOUT, &t, sizeof t);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_CONNECT_TIMEOUT             6

/**
 * \brief Option name for using non-blocking send/receive.
 *
 * Use as the option name for \c setsockopt(3) or \c getsockopt(3) to set or
 * get the non-blocking transmit/receive flag for a STREAM socket.  This flag
 * determines whether \c send() and \c recv() can be called in non-blocking
 * contexts for the given socket.  The value is a signed integer.
 *
 * This option is only relevant to kernel endpoints, where descheduling
 * the thread of execution is not allowed, for example, while holding a
 * spinlock.  It is not to be confused with conventional non-blocking socket
 * operations.
 *
 * \note Only available for VMKernel endpoints.
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * int nonblock;
 * socklen_t len = sizeof nonblock;
 * int fd = socket(af, SOCK_STREAM, 0);
 * getsockopt(fd, af, SO_VMCI_NONBLOCK_TXRX, &nonblock, &len);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_NONBLOCK_TXRX               7

/**
 * \brief Option name for STREAM socket connection disconect cause
 *
 * Use as the option name in \c getsockopt(3) to get the cause of the
 * peer disconnect for a stream socket.
 *
 * \note Only available for ESX (VMkernel/userworld) endpoints.
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * int32 cause;
 * socklen_t len = sizeof cause;
 * int fd = socket(af, SOCK_DGRAM, 0);
 * ...
 * if (recv(fd, buf, buflen, 0) == 0) {
 *    getsockopt(fd, af, SO_VMCI_DISCONNECT_CAUSE, &cause, &len);
 * }
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_DISCONNECT_CAUSE            8

#define VMCI_SOCKETS_DISCONNECT_REGULAR     0
#define VMCI_SOCKETS_DISCONNECT_VMOTION     1

/**
 * \brief Option name for DGRAM socket to rate limit peers
 *
 * Use as the option name in \c setsockopt(3) or \c getsockopt to
 * set or get the packet rate (packets per second) for peers of a
 * datagram socket. A value of -1 means no limit set, value of 0
 * means to drop all packets.
 *
 * \note Only available for ESX (VMkernel/userworld) endpoints.
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * int32 rate = 50;
 * socklen_t len = sizeof rate;
 * int fd = socket(af, SOCK_DGRAM, 0);
 * setsockopt(fd, af, SO_VMCI_PEER_LIMIT_RATE, &rate, sizeof rate);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_PEER_LIMIT_RATE             9
#define VSOCK_OPT_PEER_RATE_MAX             (1 << 14)

/**
 * \brief Option name for DGRAM socket to block non-privileged ports
 *
 * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to
 * block peers of a datagram socket if they are using unprivileged
 * ports. A value of 1 means to block, value of 0 means to unblock
 * such peers. If a rate limit is set but priv_limit is not, then
 * the rate limit will apply to unprivileged ports too. Otherwise,
 * if a priv_limit is set on a socket, then all packets from
 * unprivileged ports will be dropped. The value is a signed integer.
 *
 * \note Only available for ESX (VMkernel/userworld) endpoints.
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * int priv = 1;
 * int fd = socket(af, SOCK_DGRAM, 0);
 * setsockopt(fd, af, SO_VMCI_PEER_LIMIT_PRIV, &priv, sizeof priv);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_PEER_PRIV_LIMIT             10

/**
 * \brief Option name for DGRAM socket to control peer rate limit burst
 *
 * Use as the option name in \c setsockopt(3) or \c getsockopt(3) to
 * set the number of packets a rate-limited peer of a datagram socket
 * can issue before packets are dropped. The allowed values are 1-4,
 * corresponding to 1, 3, 7, 15 packet bursts. The default value is 2,
 * when the rate limit option is specified on the socket. This value
 * is ignored if a rate limit is not set on the socket. The value is an
 * unsigned 8-bit integer.
 *
 * \note Only available for ESX (VMkernel/userworld) endpoints.
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * uint8 burst = 3;
 * int fd = socket(af, SOCK_DGRAM, 0);
 * setsockopt(fd, af, SO_VMCI_PEER_BURST_LEVEL, &burst, sizeof burst);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define SO_VMCI_PEER_BURST_LEVEL            11

/**
 * \brief The vSocket equivalent of INADDR_ANY.
 *
 * This works for the \c svm_cid field of sockaddr_vm and indicates the
 * context ID of the current endpoint.
 *
 * \see sockaddr_vm
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * struct sockaddr_vm addr;
 * int fd = socket(af, SOCK_DGRAM, 0);
 * addr.svm_family = af;
 * addr.svm_cid = VMADDR_CID_ANY;
 * addr.svm_port = 2000;
 * bind(fd, &addr, sizeof addr);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define VMADDR_CID_ANY  ((unsigned int)-1)

/**
 * \brief Bind to any available port.
 *
 * Works for the \c svm_port field of sockaddr_vm.
 *
 * \see sockaddr_vm
 *
 * An example is given below.
 *
 * \code
 * int vmciFd;
 * int af = VMCISock_GetAFValueFd(&vmciFd);
 * struct sockaddr_vm addr;
 * int fd = socket(af, SOCK_DGRAM, 0);
 * addr.svm_family = af;
 * addr.svm_cid = VMADDR_CID_ANY;
 * addr.svm_port = VMADDR_PORT_ANY;
 * bind(fd, &addr, sizeof addr);
 * ...
 * close(fd);
 * VMCISock_ReleaseAFValueFd(vmciFd);
 * \endcode
 */

#define VMADDR_PORT_ANY ((unsigned int)-1)

/**
 * \brief Invalid vSockets version.
 *
 * \see VMCISock_Version()
 */

#define VMCI_SOCKETS_INVALID_VERSION ((unsigned int)-1)

/**
 * \brief The epoch (first) component of the vSockets version.
 *
 * A single byte representing the epoch component of the vSockets version.
 *
 * \see VMCISock_Version()
 *
 * An example is given below.
 *
 * \code
 * unsigned int ver = VMCISock_Version();
 * unsigned char epoch = VMCI_SOCKETS_VERSION_EPOCH(ver);
 * \endcode
 */

#define VMCI_SOCKETS_VERSION_EPOCH(_v) (((_v) & 0xFF000000) >> 24)

/**
 * \brief The major (second) component of the vSockets version.
 *
 * A single byte representing the major component of the vSockets version.
 * Typically changes for every major release of a product.
 *
 * \see VMCISock_Version()
 *
 * An example is given below.
 *
 * \code
 * unsigned int ver = VMCISock_Version();
 * unsigned char major = VMCI_SOCKETS_VERSION_MAJOR(ver);
 * \endcode
 */

#define VMCI_SOCKETS_VERSION_MAJOR(_v) (((_v) & 0x00FF0000) >> 16)

/**
 * \brief The minor (third) component of the vSockets version.
 *
 * Two bytes representing the minor component of the vSockets version.
 *
 * \see VMCISock_Version()
 *
 * An example is given below.
 *
 * \code
 * unsigned int ver = VMCISock_Version();
 * unsigned short minor = VMCI_SOCKETS_VERSION_MINOR(ver);
 * \endcode
 */

#define VMCI_SOCKETS_VERSION_MINOR(_v) (((_v) & 0x0000FFFF))

/** \cond PRIVATE */
#if defined(_WIN32) || defined(VMKERNEL)
   typedef unsigned short sa_family_t;
#endif // _WIN32

#if defined(VMKERNEL)
   struct sockaddr {
      sa_family_t sa_family;
      char sa_data[14];
   };
#endif
/** \endcond */

/**
 * \brief Address structure for vSockets.
 *
 * The address family should be set to whatever VMCISock_GetAFValueFd()
 * returns.  The structure members should all align on their natural
 * boundaries without resorting to compiler packing directives.  The total
 * size of this structure should be exactly the same as that of \c struct
 * \c sockaddr.
 *
 * \see VMCISock_GetAFValueFd()
 */

struct sockaddr_vm {
#if defined(__APPLE__) || defined(__FreeBSD__)
   unsigned char svm_len;
#endif // __APPLE__ || __FreeBSD__

   /** \brief Address family. \see VMCISock_GetAFValueFd() */
   sa_family_t svm_family;

   /** \cond PRIVATE */
   unsigned short svm_reserved1;
   /** \endcond */

   /** \brief Port.  \see VMADDR_PORT_ANY */
   unsigned int svm_port;

   /** \brief Context ID.  \see VMADDR_CID_ANY */
   unsigned int svm_cid;

   /** \cond PRIVATE */
   unsigned char svm_zero[sizeof(struct sockaddr) -
#if defined(__APPLE__)
                             sizeof(unsigned char) -
#endif // __APPLE__
                             sizeof(sa_family_t) -
                             sizeof(unsigned short) -
                             sizeof(unsigned int) -
                             sizeof(unsigned int)];
   /** \endcond */
};

/** \cond PRIVATE */
struct uuid_2_cid {
   unsigned int u2c_context_id;
   unsigned int u2c_pad;
   char u2c_uuid_string[128];
};
/** \endcond */

#if defined(_WIN32)
#  if !defined(NT_INCLUDED)
#     include <winioctl.h>
#     define VMCI_SOCKETS_DEVICE          L"\\\\.\\VMCI"
#     define VMCI_SOCKETS_VERSION         0x81032058
#     define VMCI_SOCKETS_GET_AF_VALUE    0x81032068
#     define VMCI_SOCKETS_GET_LOCAL_CID   0x8103206c
#     define VMCI_SOCKETS_UUID_2_CID      0x810320a4

      static __inline unsigned int __VMCISock_DeviceIoControl(DWORD cmd)
      {
         unsigned int val = (unsigned int)-1;
         HANDLE device = CreateFileW(VMCI_SOCKETS_DEVICE, GENERIC_READ, 0, NULL,
                                     OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
         if (INVALID_HANDLE_VALUE != device) {
            DWORD ioReturn;
            DeviceIoControl(device, cmd, &val, sizeof val, &val, sizeof val,
                            &ioReturn, NULL);
            CloseHandle(device);
            device = INVALID_HANDLE_VALUE;
         }
         return val;
      }

      static __inline unsigned int VMCISock_Version(void)
      {
         return __VMCISock_DeviceIoControl(VMCI_SOCKETS_VERSION);
      }

      static __inline int VMCISock_GetAFValue(void)
      {
         return (int)__VMCISock_DeviceIoControl(VMCI_SOCKETS_GET_AF_VALUE);
      }

      static __inline int VMCISock_GetAFValueFd(int *outFd)
      {
         (void)outFd; /* Unused parameter. */
         return VMCISock_GetAFValue();
      }

      static __inline void VMCISock_ReleaseAFValueFd(int fd)
      {
         (void)fd; /* Unused parameter. */
      }

      static __inline unsigned int VMCISock_GetLocalCID(void)
      {
         return __VMCISock_DeviceIoControl(VMCI_SOCKETS_GET_LOCAL_CID);
      }

      static __inline unsigned int VMCISock_Uuid2ContextId(const char *uuidString)
      {
         struct uuid_2_cid io;
         HANDLE device = CreateFileW(VMCI_SOCKETS_DEVICE, GENERIC_READ, 0, NULL,
                                     OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
         io.u2c_context_id = VMADDR_CID_ANY;
         if (INVALID_HANDLE_VALUE != device) {
            DWORD ioReturn;
            strncpy_s(io.u2c_uuid_string, sizeof io.u2c_uuid_string,
                      uuidString, _TRUNCATE);
            DeviceIoControl(device, VMCI_SOCKETS_UUID_2_CID, &io, sizeof io,
                            &io, sizeof io, &ioReturn, NULL);
            CloseHandle(device);
            device = INVALID_HANDLE_VALUE;
         }
         return io.u2c_context_id;
      }
#  endif // !NT_INCLUDED
#else // _WIN32
#if (defined(__linux__) && !defined(VMKERNEL)) || (defined(__APPLE__))
#  if defined(__linux__) && defined(__KERNEL__)
   void VMCISock_KernelRegister(void);
   void VMCISock_KernelDeregister(void);
   int VMCISock_GetAFValue(void);
   int VMCISock_GetLocalCID(void);
#  elif defined(__APPLE__) && (KERNEL)
   /* Nothing to define here. */
#  else // __KERNEL__
#  include <fcntl.h>
#  include <stdio.h>
#  include <string.h>
#  include <sys/ioctl.h>
#  include <sys/stat.h>
#  include <sys/types.h>
#  include <unistd.h>

/** \cond PRIVATE */
#  define VMCI_SOCKETS_DEFAULT_DEVICE      "/dev/vsock"
#  define VMCI_SOCKETS_CLASSIC_ESX_DEVICE  "/vmfs/devices/char/vsock/vsock"
#  if defined(__linux__)
#     define VMCI_SOCKETS_VERSION       1972
#     define VMCI_SOCKETS_GET_AF_VALUE  1976
#     define VMCI_SOCKETS_GET_LOCAL_CID 1977
#     define VMCI_SOCKETS_UUID_2_CID    1991
#  elif defined(__APPLE__)
#     include <sys/ioccom.h>
#     define VMCI_SOCKETS_VERSION       _IOR( 'V', 21, unsigned)
#     define VMCI_SOCKETS_GET_AF_VALUE  _IOR( 'V', 25, int)
#     define VMCI_SOCKETS_GET_LOCAL_CID _IOR( 'V', 26, unsigned)
#     define VMCI_SOCKETS_UUID_2_CID    _IOWR('V', 40, struct uuid_2_cid)
#endif
/** \endcond */

   /*
    ***********************************************************************
    * VMCISock_Version                                               */ /**
    *
    * \brief Retrieve the vSockets version.
    *
    * Returns the current version of vSockets.  The version is a 32-bit
    * unsigned integer that consist of three components: the epoch, the
    * major version, and the minor version.  Use the \c VMCI_SOCKETS_VERSION
    * macros to extract the components.
    *
    * \see VMCI_SOCKETS_VERSION_EPOCH()
    * \see VMCI_SOCKETS_VERSION_MAJOR()
    * \see VMCI_SOCKETS_VERSION_MINOR()
    *
    * \retval  VMCI_SOCKETS_INVALID_VERSION  Not available.
    * \retval  other                         The current version.
    *
    * An example is given below.
    *
    * \code
    * unsigned int ver = VMCISock_Version();
    * if (ver != VMCI_SOCKETS_INVALID_VERSION) {
    *    printf("vSockets version=%d.%d.%d\n",
    *           VMCI_SOCKETS_VERSION_EPOCH(ver),
    *           VMCI_SOCKETS_VERSION_MAJOR(ver),
    *           VMCI_SOCKETS_VERSION_MINOR(ver));
    * }
    * \endcode
    *
    ***********************************************************************
    */

   static inline unsigned int VMCISock_Version(void)
   {
      int fd;
      unsigned int version;

      fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDONLY);
      if (fd < 0) {
         fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDONLY);
         if (fd < 0) {
            return VMCI_SOCKETS_INVALID_VERSION;
         }
      }

      if (ioctl(fd, VMCI_SOCKETS_VERSION, &version) < 0) {
         version = VMCI_SOCKETS_INVALID_VERSION;
      }

      close(fd);
      return version;
   }

   /*
    ***********************************************************************
    * VMCISock_GetAFValueFd                                          */ /**
    *
    * \brief Retrieve the address family value for vSockets.
    *
    * Returns the value to be used for the vSockets address family.
    * This value should be used as the domain argument to \c socket(2) (when
    * you might otherwise use \c AF_INET).  For vSocket-specific options,
    * this value should also be used for the level argument to
    * \c setsockopt(2) (when you might otherwise use \c SOL_TCP).
    *
    * \see VMCISock_ReleaseAFValueFd()
    * \see sockaddr_vm
    *
    * \param[out]    outFd    File descriptor to the VMCI device.  The
    *                         address family value is valid until this
    *                         descriptor is closed.  This parameter is
    *                         not necessarily valid, but it is set if
    *                         the return value is not -1.
    *                         Call VMCISock_ReleaseAFValueFd() to  close
    *                         this descriptor.
    *
    * \retval  -1       Not available.
    * \retval  other    The address family value.
    *
    * An example is given below.
    *
    * \code
    * int vmciFd;
    * int af = VMCISock_GetAFValueFd(&vmciFd);
    * if (af != -1) {
    *    int fd = socket(af, SOCK_STREAM, 0);
    *    ...
    *    close(fd);
    *    close(vmciFd);
    * }
    * \endcode
    *
    ***********************************************************************
    */

   static inline int VMCISock_GetAFValueFd(int *outFd)
   {
      int fd;
      int family = -1;

#if defined(__linux__)
      /*
       * vSockets is now in mainline kernel with address family 40.  As part
       * of upstreaming, we removed the IOCTL we use below to determine the
       * address family.  So to handle both a new and old kernel we do this:
       * 1. Check if our family already exists by making a socket with it.
       *    Some weird kernel might claim this too, but it's very unlikely
       *    (Linus' tree has us at 40, and that's what we care about).
       * 2. If that fails, try the normal IOCTL path, since it's probably an
       *    older kernel with vSockets from Tools.
       * 3. If that fails, then vSockets really isn't available.
       */
#define AF_VSOCK_LOCAL 40
      {
         int s = socket(AF_VSOCK_LOCAL, SOCK_DGRAM, 0);
         if (s != -1) {
            close(s);
            if (outFd) {
               *outFd = -1;
            }
            return AF_VSOCK_LOCAL;
         }
      }
#undef AF_VSOCK_LOCAL
#endif // linux

      fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDONLY);
      if (fd < 0) {
         fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDONLY);
         if (fd < 0) {
            return -1;
         }
      }

      if (ioctl(fd, VMCI_SOCKETS_GET_AF_VALUE, &family) < 0) {
         family = -1;
      }

      /*
       * fd is intentionally left open when outFd is NULL. Closing it here
       * will break applications running on Linux without a fixed AF for
       * vSockets. In such cases, the fd will be closed during cleanup when
       * the application exits. Refer to the description of
       * VMCISock_GetAFValue.
       */
      if (family < 0) {
         close(fd);
      } else if (outFd) {
         *outFd = fd;
      }

      /*
       * The above comment explains why fd is left open even when outFd
       * is NULL.
       */
      /* coverity[leaked_handle] */
      return family;
   }

   /** \cond PRIVATE */
   /*
    ***********************************************************************
    * VMCISock_GetAFValue                                            */ /**
    *
    * \brief Retrieve the address family value for vSockets.
    *
    * Returns the value to be used for the vSockets address family.
    * This value should be used as the domain argument to \c socket(2) (when
    * you might otherwise use \c AF_INET).  For vSocket-specific options,
    * this value should also be used for the level argument to
    * \c setsockopt(2) (when you might otherwise use \c SOL_TCP).
    *
    * \note This function leaves its descriptor to the vsock device open so
    * that the socket implementation knows that the socket family is still in
    * use.  This is done because the address family is registered with the
    * kernel on-demand and a notification is needed to unregister the address
    * family.  Use of this function is thus discouraged; please use
    * VMCISock_GetAFValueFd() instead.
    *
    * \see VMCISock_GetAFValueFd()
    * \see sockaddr_vm
    *
    * \retval  -1       Not available.
    * \retval  other    The address family value.
    *
    * An example is given below.
    *
    * \code
    * int af = VMCISock_GetAFValue();
    * if (af != -1) {
    *    int fd = socket(af, SOCK_STREAM, 0);
    *    ...
    *    close(fd);
    * }
    * \endcode
    *
    ***********************************************************************
    */

   static inline int VMCISock_GetAFValue(void)
   {
      return VMCISock_GetAFValueFd(NULL);
   }
   /** \endcond PRIVATE */

   /*
    ***********************************************************************
    * VMCISock_ReleaseAFValueFd                                      */ /**
    *
    * \brief Release the file descriptor obtained when retrieving the
    *        address family value.
    *
    * Use this to release the file descriptor obtained by calling
    * VMCISock_GetAFValueFd().
    *
    * \see VMCISock_GetAFValueFd()
    *
    * \param[in]  fd    File descriptor to the VMCI device.
    *
    ***********************************************************************
    */

   static inline void VMCISock_ReleaseAFValueFd(int fd)
   {
      if (fd >= 0) {
         close(fd);
      }
   }

   /*
    ***********************************************************************
    * VMCISock_GetLocalCID                                           */ /**
    *
    * \brief Retrieve the current context ID.
    *
    * \see VMADDR_CID_ANY
    *
    * \retval  VMADDR_CID_ANY    Not available.
    * \retval  other             The current context ID.
    *
    * An example is given below.
    *
    * \code
    * int vmciFd;
    * int af = VMCISock_GetAFValueFd(&vmciFd);
    * struct sockaddr_vm addr;
    * addr.svm_family = af;
    * addr.svm_cid = VMCISock_GetLocalCID();
    * VMCISock_ReleaseAFValueFd(vmciFd);
    * \endcode
    *
    ***********************************************************************
    */

   static inline unsigned int VMCISock_GetLocalCID(void)
   {
      int fd;
      unsigned int contextId;

      fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDONLY);
      if (fd < 0) {
         fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDONLY);
         if (fd < 0) {
            return VMADDR_CID_ANY;
         }
      }

      if (ioctl(fd, VMCI_SOCKETS_GET_LOCAL_CID, &contextId) < 0) {
         contextId = VMADDR_CID_ANY;
      }

      close(fd);
      return contextId;
   }

   /*
    ***********************************************************************
    * VMCISock_Uuid2ContextId                                        */ /**
    *
    * \brief Retrieve the context ID of a running VM, given a VM's UUID.
    *
    * Retrieves the context ID of a running virtual machine given that virtual
    * machines's unique identifier.  The identifier is local to the host and
    * its meaning is platform-specific.  On ESX, which is currently the only
    * supported platform, it is the "bios.uuid" field as specified in the VM's
    * VMX file.
    *
    * \see VMADDR_CID_ANY
    *
    * \retval  VMADDR_CID_ANY    Not available.
    * \retval  other             The VM's context ID.
    *
    * \note Only available for ESX (userworld) endpoints.
    *
    * An example is given below.
    *
    * \code
    * int vmciFd;
    * int af = VMCISock_GetAFValueFd(&vmciFd);
    * unsigned int cid = VMCISock_Uuid2ContextId(
    *    "56 4d 07 d8 cc d5 c4 0d-98 44 dc 1e 8f e0 da f3");
    * VMCISock_ReleaseAFValueFd(vmciFd);
    * \endcode
    *
    ***********************************************************************
    */

   static inline unsigned int VMCISock_Uuid2ContextId(const char *uuidString)
   {
      int fd;
      struct uuid_2_cid io;

      fd = open(VMCI_SOCKETS_DEFAULT_DEVICE, O_RDONLY);
      if (fd < 0) {
         fd = open(VMCI_SOCKETS_CLASSIC_ESX_DEVICE, O_RDONLY);
         if (fd < 0) {
            return VMADDR_CID_ANY;
         }
      }

      strncpy(io.u2c_uuid_string, uuidString, sizeof io.u2c_uuid_string - 1);
      io.u2c_uuid_string[sizeof io.u2c_uuid_string - 1] = '\0';
      if (ioctl(fd, VMCI_SOCKETS_UUID_2_CID, &io) < 0) {
         io.u2c_context_id = VMADDR_CID_ANY;
      }

      close(fd);
      return io.u2c_context_id;
   }
#  endif // __KERNEL__
#elif defined(__FreeBSD__)
   /*
    * No FreeBSD support yet, but it might appear in the future. Just define
    * some stubs that return errors - that way a client doesn't have to ifdef
    * the calls (assuming it can handle the failures).
    */
#  define VMCISock_Version()                    VMCI_SOCKETS_INVALID_VERSION
#  define VMCISock_GetAFValueFd(outFd)          (-1)
#  define VMCISock_GetAFValue()                 VMCISock_GetAFValueFd(NULL)
#  define VMCISock_ReleaseAFValueFd(fd)         do { } while (0)
#  define VMCISock_GetLocalCID()                VMADDR_CID_ANY
#  define VMCISock_Uuid2ContextId(uuidString)   VMADDR_CID_ANY
#endif // __FreeBSD__
#endif // _WIN32


#if defined __cplusplus
} // extern "C"
#endif

#endif // _VMCI_SOCKETS_H_

   07070100000144000081A400000000000000000000000168225505000012B7000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmfs.h /*********************************************************
 * Copyright (c) 2003-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmfs.h --
 *
 *	assorted vmfs related helper functions needed by userlevel.
 */

#ifndef __VMFS_H__
#define __VMFS_H__

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMKERNEL
#include "includeCheck.h"

#include "unicodeTypes.h"

#if defined(__cplusplus)
extern "C" {
#endif

// Consolidate all path stuff here so it's consistent between user and kernel
#define VMFS_ROOT_DIR_NAME              "vmfs"
#define VMFS_ROOT_DIR_PATH              "/vmfs/"
                                                                                
#define DEVFS_DIR_NAME                  "devices"
#define VCFS_DIR_NAME                   "volumes"
#define DELTADISK_DIR_NAME		"deltadisks"
#define CBT_DIR_NAME		        "cbt"
#define MULTI_EXTENT_DIR_NAME		"multiextent"
#define FILE_DIR_NAME                   "file"
#define RAMDISK_DIR_NAME                "ramdisk"
#define SVM_DIR_NAME                    "svm"
#define VFLASH_DIR_NAME                 "vflash"
#define VDFM_DIR_NAME                   "vdfm"

#define DEVFS_MOUNT_POINT               VMFS_ROOT_DIR_PATH DEVFS_DIR_NAME
#define VCFS_MOUNT_POINT                VMFS_ROOT_DIR_PATH VCFS_DIR_NAME

#define DEVFS_MOUNT_PATH                DEVFS_MOUNT_POINT "/"
#define VCFS_MOUNT_PATH                 VCFS_MOUNT_POINT "/"

#define DELTADISK_MOUNT_POINT	        DEVFS_MOUNT_PATH DELTADISK_DIR_NAME
#define DELTADISK_MOUNT_PATH		DELTADISK_MOUNT_POINT "/"

#define CBT_MOUNT_POINT	                DEVFS_MOUNT_PATH CBT_DIR_NAME
#define CBT_MOUNT_PATH		        CBT_MOUNT_POINT "/"

#define FILE_MOUNT_POINT	        DEVFS_MOUNT_PATH FILE_DIR_NAME
#define FILE_MOUNT_PATH		        FILE_MOUNT_POINT "/"

#define RAMDISK_MOUNT_POINT	        DEVFS_MOUNT_PATH RAMDISK_DIR_NAME
#define RAMDISK_MOUNT_PATH		RAMDISK_MOUNT_POINT "/"

#define SVM_MOUNT_POINT	                DEVFS_MOUNT_PATH SVM_DIR_NAME
#define SVM_MOUNT_PATH		        SVM_MOUNT_POINT "/"

#define VFLASH_MOUNT_POINT              DEVFS_MOUNT_PATH VFLASH_DIR_NAME
#define VFLASH_MOUNT_PATH               VFLASH_MOUNT_POINT "/"

#define VDFM_MOUNT_POINT                DEVFS_MOUNT_PATH VDFM_DIR_NAME
#define VDFM_MOUNT_PATH                 VDFM_MOUNT_POINT "/"

#define CDROM_DRIVER_STRING             "cdrom"
#define PSA_STOR_DISK_DRIVER_STRING     "disks"
#define SCSI_GENERIC_DRIVER_STRING      "genscsi"
#define OLD_SCSI_GENERIC_DRIVER_STRING  "generic"
#define COW_DRIVER_NAME                 "deltadisks"
#define MULTI_EXTENT_DRIVER_NAME        "multiextent"

#define FDS_DRIVER_ALL_STRING           "fdsall"

#define CDROM_MOUNT_POINT               DEVFS_MOUNT_PATH CDROM_DRIVER_STRING
#define DISKS_MOUNT_POINT               DEVFS_MOUNT_PATH PSA_STOR_DISK_DRIVER_STRING
#define GENERIC_SCSI_MOUNT_POINT        DEVFS_MOUNT_PATH SCSI_GENERIC_DRIVER_STRING
#define MULTI_EXTENT_MOUNT_POINT	DEVFS_MOUNT_PATH MULTI_EXTENT_DIR_NAME
#define CDROM_MOUNT_PATH                CDROM_MOUNT_POINT "/"
#define DISKS_MOUNT_PATH                DISKS_MOUNT_POINT "/"
#define GENERIC_SCSI_MOUNT_PATH         GENERIC_SCSI_MOUNT_POINT "/"
#define MULTI_EXTENT_MOUNT_PATH		MULTI_EXTENT_MOUNT_POINT "/"

#define VISOR_DEVFS_MOUNT_PATH          "/dev/"
#define VISOR_CDROM_MOUNT_POINT         VISOR_DEVFS_MOUNT_PATH CDROM_DRIVER_STRING
#define VISOR_DISKS_MOUNT_POINT         VISOR_DEVFS_MOUNT_PATH PSA_STOR_DISK_DRIVER_STRING
#define VISOR_GENERIC_SCSI_MOUNT_POINT  VISOR_DEVFS_MOUNT_PATH SCSI_GENERIC_DRIVER_STRING
#define VISOR_CDROM_MOUNT_PATH          VISOR_CDROM_MOUNT_POINT "/"
#define VISOR_DISKS_MOUNT_PATH          VISOR_DISKS_MOUNT_POINT "/"
#define VISOR_GENERIC_SCSI_MOUNT_PATH   VISOR_GENERIC_SCSI_MOUNT_POINT "/"

typedef enum {
   VMFS_SYMBOLIC,
   VMFS_SCSI_DEV,
   VMFS_COS_SYMBOLIC,
   VMFS_COS_SCSI_DEV,
} Vmfs_VolNameType;

#if defined(VMX86_SERVER)
char *Vmfs_GetCOSFileName(const char *vmfsFile);
#endif /* VM86_SERVER */

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* __VMFS_H__ */

 07070100000145000081A40000000000000000000000016822550500000E20000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmk_exports.h  /*********************************************************
 * Copyright (C) 2009-2016,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vmk_exports.h --
 *
 *      Macros for exporting symbols from vmkernel.
 */

#ifndef _VMK_EXPORTS_H
#define _VMK_EXPORTS_H

#ifdef VMKERNEL
#   include "vm_basic_defs.h"

#   define _VMK_KERNEL_EXPORT(string) asm(                                    \
   ".pushsection .vmkexports, \"aS\", @progbits"                       "\n\t" \
   ".string \"" string "\""                                            "\n\t" \
   ".popsection"                                                              \
)
#else
#   define _VMK_KERNEL_EXPORT(string)
#endif

/*
 * The following macros allow you to export symbols from vmkernel,
 * thus making them available for modules to access.
 *
 * The versions with no explicit name-space and version imply the
 * default vmkernel name-space/version.  They should be considered
 * deprecated as they impede your ability to create very stable APIs.
 *
 * Sample usage:
 *
 * VMK_KERNEL_EXPORT(foo)          : export foo
 *
 * VMK_KERNEL_ALIAS(foo, fooAlias) : export foo as fooAlias
 *
 * VMK_KERNEL_EXPORT_TO_NAMESPACE(foo, "ns", "ver") :
 *                                   export foo @ns:ver
 *
 * VMK_KERNEL_ALIAS_TO_NAMESPACE(foo, fooAlias, "ns", "ver") :
 *                                   export foo as fooAlias @ns:ver
 *
 * Further details on the name-space/version implementation can be
 * found in the VMKAPI module headers and documentation.  The 2 second
 * version is that if you export foo@myns:1, modules wanting access to
 * that symbol will require an explicit
 * VMK_MODULE_NAMESPACE_REQUIRED("myns", "1") tag to see that symbol.
 */

#define VMK_KERNEL_EXPORT(symbol)                                             \
   _VMK_KERNEL_EXPORT(XSTR(symbol))

#define VMK_KERNEL_ALIAS(symbol, alias)                                       \
   _VMK_KERNEL_EXPORT(XSTR(symbol) "!" XSTR(alias))

#define VMK_KERNEL_EXPORT_TO_NAMESPACE(symbol, namespace, version)            \
   _VMK_KERNEL_EXPORT(XSTR(symbol) "@" namespace ":" version)

#define VMK_KERNEL_ALIAS_TO_NAMESPACE(symbol, alias, namespace, version)      \
   _VMK_KERNEL_EXPORT(XSTR(symbol) "!" XSTR(alias) "@" namespace ":" version)

#endif /* _VMK_EXPORTS_H */
07070100000146000081A400000000000000000000000168225505000004FB000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmsignal.h /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * vmsignal.h --
 *
 *    Posix signal handling utility functions
 *
 */


#ifndef __VMSIGNAL_H__
#   define __VMSIGNAL_H__

#include <signal.h>

int Signal_SetGroupHandler(int const *signals, struct sigaction *olds, unsigned int nr, void (*handler)(int signal));
int Signal_ResetGroupHandler(int const *signals, struct sigaction const *olds, unsigned int nr);


#endif /* __VMSIGNAL_H__ */
 07070100000147000081A4000000000000000000000001682255050000059B000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmstdio.h  /*********************************************************
 * Copyright (C) 1998-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmstdio.h --
 *
 *    Functions that operate on FILE objects --hpreg
 */

#ifndef __VMSTDIO_H__
#   define __VMSTDIO_H__

#include <stdio.h>

#if defined(__cplusplus)
extern "C" {
#endif

typedef enum {
   StdIO_Error,
   StdIO_EOF,
   StdIO_Success,
} StdIO_Status;

StdIO_Status
StdIO_ReadNextLine(FILE *stream,         // IN
                   char **buf,           // OUT
                   size_t maxBufLength,  // IN
                   size_t *count);       // OUT

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* __VMSTDIO_H__ */
 07070100000148000081A40000000000000000000000016822550500000819000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmsupport.h    /*********************************************************
 * Copyright (C) 2009-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmsupport.h --
 *
 *   Defines constants related to VM support data collection.
 *
 */

#ifndef _VMSUPPORT_H_
#define _VMSUPPORT_H_

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * The status of vm-support tool running in the guest, exported in VMDB at:
 * vm/#/vmx/guestTools/vmsupport/gStatus
 * This enum must be kept in sync with vm-support script implementation.
 * The ordering in enum is important.
 */

typedef enum {
   VMSUPPORT_NOT_RUNNING = 0,   /* script is not running */
   VMSUPPORT_BEGINNING   = 1,   /* script is beginning */
   VMSUPPORT_RUNNING     = 2,   /* script running in progress */
   VMSUPPORT_ENDING      = 3,   /* script is ending */
   VMSUPPORT_ERROR       = 10,  /* script failed */
   VMSUPPORT_UNKNOWN     = 100  /* collection not supported */
} VMSupportStatus;

/*
 * The command for vm-support tool launched in the guest, set in VMDB at:
 * vm/#/vmx/guestTools/vmsupport/hgCmd
 */

typedef enum {
  VMSUPPORT_CMD_RESET = 0,
  VMSUPPORT_CMD_SET   = 1
} VMSupportCmd;

#define RPC_VMSUPPORT_START   "vmsupport.start"
#define RPC_VMSUPPORT_STATUS  "tools.vmsupport.status"

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _VMSUPPORT_H_
   07070100000149000081A4000000000000000000000001682255050000046C000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmtoolsd_version.h /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMTOOLSD_VERSION_H_
#define _VMTOOLSD_VERSION_H_

#include "vm_tools_version.h"
#define VMTOOLSD_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define VMTOOLSD_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _VMTOOLS_VERSION_H_ */

0707010000014A000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware 0707010000014B000081A40000000000000000000000016822550500000F16000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware.h   /*********************************************************
 * Copyright (C) 2003-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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_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_
  0707010000014C000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc    0707010000014D000081A40000000000000000000000016822550500000651000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/appInfo.h  /*********************************************************
 * Copyright (C) 2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _APPINFO_H_
#define _APPINFO_H_

/**
 * @file appInfo.h
 *
 * Common declarations that aid in sending application information
 * from 'appInfo' plugin in 'VMware Tools' to the host.
 */

/**
 * The guest Variable name for the application information.
 */
#define APP_INFO_GUESTVAR_KEY         "appInfo"
#define APP_INFO_GUESTINFO_KEY        "guestinfo." APP_INFO_GUESTVAR_KEY

#define APP_INFO_VERSION_1            1
#define APP_INFO_KEY_VERSION          "version"
#define APP_INFO_KEY_UPDATE_COUNTER   "updateCounter"
#define APP_INFO_KEY_PUBLISHTIME      "publishTime"
#define APP_INFO_KEY_APPS             "applications"
#define APP_INFO_KEY_APP_NAME         "a"
#define APP_INFO_KEY_APP_VERSION      "v"

#endif /* _APPINFO_H_ */
   0707010000014E000081A4000000000000000000000001682255050000246D000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/capabilities.h /*********************************************************
 * Copyright (c) 2008-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * capabilities.h --
 *
 *  Common definitions for the guestCaps system that allows a guest to
 *  register an arbitrary number of boolean capabilites with the vmx.
 */

#ifndef _GUEST_CAPS_H_
#define _GUEST_CAPS_H_

/* clang-format off */
/*
 * Guest capabilities.
 *
 * The guest uses this enum to communicate whether a certain
 * feature is supported by the tools.
 *
 * The guest sends an RPC where it specifies which features
 * are turned off and on, for example:
 *
 * "tools.capability.features 0=1 2=1 3=0"
 *
 * In the above example, the guest is capable of showing the
 * start menu and setting the work area, but does not support
 * multiple monitors.
 *
 * For capabilities that can be managed by tools.conf settings, guest should
 * send a separate RPC tools.capability.features for each capability change,
 * even when multiple such capabilities are changed by tools.conf at the same
 * time.
 *
 * NOTE: the order for these has to stay constant for backward compatibility
 * with older Tools versions. New capabilities must be added at the end.
 */

typedef enum {
   UNITY_CAP_START_MENU                 = 0,  // can show the start menu
   UNITY_CAP_VIRTUAL_DESK               = 1,  // supports virtual desktops
   UNITY_CAP_WORK_AREA                  = 2,  // can set the work area
   UNITY_CAP_MULTI_MON                  = 3,  // supports multiple monitors
   GHI_CAP_SHELL_ACTION_BROWSE          = 4,  // supports the "browse" action verb
   GHI_CAP_SHELL_LOCATION_HGFS          = 5,  // supports HGFS location URIs
   GHI_CAP_SHELL_ACTION_RUN             = 6,  // supports the "run" action verb
   GHI_CAP_CMD_SHELL_ACTION             = 7,  // allows "ghi.guest.shell.action" command
   HGFSU_CAP_MIRROR_DESKTOP             = 8,  // supports remapping GOS Desktop to HGFS
   HGFSU_CAP_MIRROR_DOCUMENTS           = 9,  // supports remapping GOS Documents to HGFS
   HGFSU_CAP_MIRROR_MUSIC               = 10, // supports remapping GOS Music to HGFS
   HGFSU_CAP_MIRROR_PICTURES            = 11, // supports remapping GOS Pictures to HGFS
   HGFSU_CAP_DESKTOP_SHORTCUT           = 12, // supports creating HGFS link on GOS Desktop
   HGFSU_CAP_MAP_DRIVE                  = 13, // supports mapping a GOS drive letter to HGFS
   GHI_CAP_SET_HANDLER                  = 14, // supports setting the handler for types/protocols
   UNITY_CAP_STATUS_UNITY_ACTIVE        = 15, // supports GuestRpc bits for Unity Status
   GHI_CAP_SET_OUTLOOK_TEMP_FOLDER      = 16, // supports setting the Outlook temp folder
                                              // 17 is obsolete, do not use
   CAP_SET_TOPO_MODES                   = 18, // supports setting topology modes in video driver
   GHI_CAP_TRAY_ICONS                   = 19, // supports ghi.guest.trayIcon commands
   GHI_CAP_SET_FOCUSED_WINDOW           = 20, // supports ghi.guest.setFocusedWindow
   GHI_CAP_GET_EXEC_INFO_HASH           = 21, // supports ghi.guest.getExecInfoHash
   UNITY_CAP_STICKY_WINDOWS             = 22, // supports unity.window.{un,}stick
   CAP_CHANGE_HOST_3D_AVAILABILITY_HINT = 23, // supports sending 3D support hint to guest
   CAP_AUTOUPGRADE_AT_SHUTDOWN          = 24, // supports auto-upgrading tools at OS shutdown
   GHI_CAP_AUTOLOGON                    = 25, // supports autologon
   CAP_DESKTOP_AUTOLOCK                 = 26, // supports desktop autolock
                                              // 27 is obsolete, do not use
   HGFSU_CAP_MIRROR_DOWNLOADS           = 28, // supports remapping GOS Downloads to HGFS
   HGFSU_CAP_MIRROR_MOVIES              = 29, // supports remapping GOS Movies to HGFS
   GHI_CAP_TOGGLE_START_UI              = 30, // supports showing/hiding the Start UI
   GHI_CAP_SET_DISPLAY_SCALING          = 31, // supports setting the display scaling (DPI)
   UNITY_CAP_DISABLE_MOUSE_BUTTON_SWAPPING     = 32, // supports disabling mouse button swapping
   UNITY_CAP_CARET_POSITION             = 33, // supports sending caret position updates
   CAP_GUESTSTORE_UPGRADE               = 34, // supports tools upgrade from GuestStore
   CAP_DEVICE_HELPER                    = 35, // supports tools device helper for Windows guests
   CAP_VMBACKUP_NVME                    = 36, // supports NVMe for vmbackup
   CAP_HOST_VERIFIED_SAML_TOKEN         = 37, // supports host verification of SAML tokens
} GuestCapabilities;

typedef struct {
   GuestCapabilities cap;
   const char *vmdbPath;
   const char *vmdbKey;
} GuestCapElem;

/* guest_rpc command to send over the wire. */
#define GUEST_CAP_FEATURES                   "tools.capability.features"

#if defined(VM_NEED_VMDB_GUEST_CAP_MAPPING)

/* VMDB paths prefixes to store various capabilities sent from the guest. */
#define UNITY_CAP_VMDB_PATH                  "guest/caps/unityFeatures"
#define GHI_CAP_VMDB_PATH                    "guest/caps/ghiFeatures"
#define HGFSU_CAP_VMDB_PATH                  "guest/caps/hgfsUsabilityFeatures"
#define CAP_VMDB_PATH                        "guest/caps"

/*
 * If you change these strings, make sure you also change the
 *  vmdb schema, since these strings are used as vmdb keys.
 */
static GuestCapElem guestCapTable[] = {
   { UNITY_CAP_START_MENU,                 UNITY_CAP_VMDB_PATH, "startmenu" },
   { UNITY_CAP_VIRTUAL_DESK,               UNITY_CAP_VMDB_PATH, "virtualdesk" },
   { UNITY_CAP_WORK_AREA,                  UNITY_CAP_VMDB_PATH, "workarea" },
   { UNITY_CAP_MULTI_MON,                  UNITY_CAP_VMDB_PATH, "multimon" },

   { GHI_CAP_SHELL_ACTION_BROWSE,          GHI_CAP_VMDB_PATH,   "shellActionBrowse" },
   { GHI_CAP_SHELL_LOCATION_HGFS,          GHI_CAP_VMDB_PATH,   "shellLocationHGFS" },
   { GHI_CAP_SHELL_ACTION_RUN,             GHI_CAP_VMDB_PATH,   "shellActionRun" },
   { GHI_CAP_CMD_SHELL_ACTION,             GHI_CAP_VMDB_PATH,   "cmdShellAction" },

   { HGFSU_CAP_MIRROR_DESKTOP,             HGFSU_CAP_VMDB_PATH, "mirrorDesktop" },
   { HGFSU_CAP_MIRROR_DOCUMENTS,           HGFSU_CAP_VMDB_PATH, "mirrorDocuments" },
   { HGFSU_CAP_MIRROR_MUSIC,               HGFSU_CAP_VMDB_PATH, "mirrorMusic" },
   { HGFSU_CAP_MIRROR_PICTURES,            HGFSU_CAP_VMDB_PATH, "mirrorPictures" },
   { HGFSU_CAP_DESKTOP_SHORTCUT,           HGFSU_CAP_VMDB_PATH, "createShortcut" },
   { HGFSU_CAP_MAP_DRIVE,                  HGFSU_CAP_VMDB_PATH, "mapDrive" },
   { GHI_CAP_SET_HANDLER,                  GHI_CAP_VMDB_PATH,   "setHandler" },
   { UNITY_CAP_STATUS_UNITY_ACTIVE,        UNITY_CAP_VMDB_PATH, "unityActive" },
   { GHI_CAP_SET_OUTLOOK_TEMP_FOLDER,      GHI_CAP_VMDB_PATH,   "setOutlookTempFolder" },
   { CAP_SET_TOPO_MODES,                   CAP_VMDB_PATH,       "displayTopologyModesSet" },
   { GHI_CAP_TRAY_ICONS,                   GHI_CAP_VMDB_PATH,   "trayIcons" },
   { GHI_CAP_SET_FOCUSED_WINDOW,           GHI_CAP_VMDB_PATH,   "setFocusedWindow"},
   { GHI_CAP_GET_EXEC_INFO_HASH,           GHI_CAP_VMDB_PATH,   "getExecInfoHash"},
   { UNITY_CAP_STICKY_WINDOWS,             UNITY_CAP_VMDB_PATH, "sticky"},
   { CAP_CHANGE_HOST_3D_AVAILABILITY_HINT, CAP_VMDB_PATH,       "changeHost3DAvailabilityHint" },
   { CAP_AUTOUPGRADE_AT_SHUTDOWN,          CAP_VMDB_PATH,       "autoUpgradeAtShutdown"},
   { GHI_CAP_AUTOLOGON,                    GHI_CAP_VMDB_PATH,   "autologon" },
   { CAP_DESKTOP_AUTOLOCK,                 CAP_VMDB_PATH,       "desktopAutolock" },
   { HGFSU_CAP_MIRROR_DOWNLOADS,           HGFSU_CAP_VMDB_PATH, "mirrorDownloads" },
   { HGFSU_CAP_MIRROR_MOVIES,              HGFSU_CAP_VMDB_PATH, "mirrorMovies" },
   { GHI_CAP_TOGGLE_START_UI,              GHI_CAP_VMDB_PATH,   "toggleStartUI"},
   { GHI_CAP_SET_DISPLAY_SCALING,          GHI_CAP_VMDB_PATH,   "setDisplayScaling"},
   { UNITY_CAP_DISABLE_MOUSE_BUTTON_SWAPPING, UNITY_CAP_VMDB_PATH, "mouseButtonSwapping" },
   { UNITY_CAP_CARET_POSITION,             UNITY_CAP_VMDB_PATH, "getCaretPosition" },
   /*
    * GuestStoreUpgrade is available on ESXi only at this time. Therefore, we
    * don't define VMDB schema for it and don't store it in VMDB.
    */
   { CAP_GUESTSTORE_UPGRADE,               NULL,                NULL },
   { CAP_DEVICE_HELPER,                    NULL,                NULL },
   { CAP_VMBACKUP_NVME,                    NULL,                NULL },
   { CAP_HOST_VERIFIED_SAML_TOKEN,         NULL,                NULL },
};
// clang-format on

#endif // VM_NEED_VMDB_GUEST_CAP_MAPPING

#endif // _GUEST_CAPS_H_
   0707010000014F000081A400000000000000000000000168225505000007D7000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/containerInfo.h    /*********************************************************
 * Copyright (c) 2021-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _CONTAINERINFO_H_
#define _CONTAINERINFO_H_

/**
 * @file containerInfo.h
 *
 * Common declarations that aid in sending container information
 * from 'containerInfo' plugin in 'VMware Tools' to the host.
 */

/*
 Sample JSON published to the guestinfo variable.
 $ vmtoolsd --cmd "info-get guestinfo.vmtools.containerInfo" | jq
 {
  "version": "1",
  "updateCounter": "11",
  "publishTime": "2021-10-27T18:18:00.855Z",
  "containerinfo": {
    "k8s.io": [
      {
        "i": "k8s.gcr.io/pause"
      }
    ]
  }
}
*/

/* clang-format off */

#define CONTAINERINFO_KEY                  "containerinfo"
#define CONTAINERINFO_GUESTVAR_KEY         "vmtools." CONTAINERINFO_KEY
#define CONTAINERINFO_GUESTINFO_KEY        "guestinfo." CONTAINERINFO_GUESTVAR_KEY
#define CONTAINERINFO_VERSION_1            1
#define CONTAINERINFO_KEY_VERSION          "version"
#define CONTAINERINFO_KEY_UPDATE_COUNTER   "updateCounter"
#define CONTAINERINFO_KEY_PUBLISHTIME      "publishTime"
#define CONTAINERINFO_KEY_IMAGE            "i"

/* clang-format on */

#endif
 07070100000150000081A40000000000000000000000016822550500000850000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/deploypkg.h    /*********************************************************
 * Copyright (C) 2006-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef __TOOLS_DEPLOYPKG_H_
#define __TOOLS_DEPLOYPKG_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"


/*
 * deploypkg.h
 *
 *   -- Define constants related to tools package deployment.
 */

typedef enum {
   TOOLSDEPLOYPKG_IDLE = 0,
   TOOLSDEPLOYPKG_PENDING,
   TOOLSDEPLOYPKG_COPYING,
   TOOLSDEPLOYPKG_DEPLOYING,
   TOOLSDEPLOYPKG_RUNNING,
   TOOLSDEPLOYPKG_DONE
} ToolsDeployPackageState;

typedef enum {
   TOOLSDEPLOYPKG_ERROR_SUCCESS = 0,
   TOOLSDEPLOYPKG_ERROR_NOT_SUPPORT,   // Old tools do not support option
   TOOLSDEPLOYPKG_ERROR_PKG_NOT_FOUND, // Specified pkg is not found
   TOOLSDEPLOYPKG_ERROR_RPC_INVALID,
   TOOLSDEPLOYPKG_ERROR_COPY_FAILED,
   TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED,
   TOOLSDEPLOYPKG_ERROR_CUST_SCRIPT_DISABLED, // User defined script is disabled
   TOOLSDEPLOYPKG_ERROR_CUST_DISABLED,       // Guest customization is disabled
   // cloud-init version is too old to support raw cloud-init data
   TOOLSDEPLOYPKG_ERROR_CLOUDINIT_NOT_SUPPORT_RAWDATA,
   // cloud-init meta dat is not valid format
   TOOLSDEPLOYPKG_ERROR_CLOUDINIT_WRONG_META_FORMAT
} ToolsDeployPkgError;

#define QUERY_NICS_SUPPORTED  "queryNicsSupported"
#define NICS_STATUS_CONNECTED "connected"

#endif //__TOOLS_DEPLOYPKG_H_
07070100000151000081A4000000000000000000000001682255050000064D000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/guestcust-events.h /*********************************************************
 * 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 Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*!
 * @file guestcust-events.h --
 */

#ifndef _GUESTCUST_EVENTS_H
#define _GUESTCUST_EVENTS_H

/*
 * Customization-specific events generated in the guest and handled by
 * hostd. They are sent via the Tools/deployPkgState/error VIGOR path.
 * The error(int) field is overloaded for both the deploy pkg errors
 * and the deploy pkg events.
 * Therefore, We start these at 100 to avoid conflict with the deployPkg error
 * codes listed in bora/guestABI/include/vmware/guestrpc/deploypkg.h
 */
typedef enum {
   GUESTCUST_EVENT_CUSTOMIZE_FAILED = 100,
   GUESTCUST_EVENT_NETWORK_SETUP_FAILED,
   GUESTCUST_EVENT_SYSPREP_FAILED,
   GUESTCUST_EVENT_ENABLE_NICS,
   GUESTCUST_EVENT_QUERY_NICS
} GuestCustEvent;

#endif // _GUESTCUST_EVENTS_H
   07070100000152000081A4000000000000000000000001682255050000091F000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/powerops.h /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _POWEROPS_H_
#define _POWEROPS_H_

/**
 * @file powerops.h
 *
 * Definition of commands and information related to VM power operations.
 */


#define TOOLSOPTION_SCRIPTS_POWERON    "toolScripts.afterPowerOn"
#define TOOLSOPTION_SCRIPTS_POWEROFF   "toolScripts.beforePowerOff"
#define TOOLSOPTION_SCRIPTS_RESUME     "toolScripts.afterResume"
#define TOOLSOPTION_SCRIPTS_SUSPEND    "toolScripts.beforeSuspend"


/**
 * The guest OS state changes that the VMX can initiate.
 */
typedef enum {
   /* Must be first. */
   GUESTOS_STATECHANGE_NONE = 0,

   GUESTOS_STATECHANGE_HALT,
   GUESTOS_STATECHANGE_REBOOT,
   GUESTOS_STATECHANGE_POWERON,
   GUESTOS_STATECHANGE_RESUME,
   GUESTOS_STATECHANGE_SUSPEND,

   /* Must be last. */
   GUESTOS_STATECHANGE_LAST,
} GuestOsState;


/**
 * Info regarding a state change command (OS_Halt, OS_Reboot, etc.)
 */
typedef struct GuestOsStateChangeCmd {
   unsigned int id;
   char const *name;
   char const *tcloCmd;
} GuestOsStateChangeCmd;


/**
 * The table of state change cmds corresponding to tclo commands.
 */
static const GuestOsStateChangeCmd stateChangeCmdTable[] = {
   { GUESTOS_STATECHANGE_POWERON, "poweron", "OS_PowerOn" },
   { GUESTOS_STATECHANGE_RESUME,  "resume",  "OS_Resume" },
   { GUESTOS_STATECHANGE_SUSPEND, "suspend", "OS_Suspend" },
   { GUESTOS_STATECHANGE_HALT,    "halt",    "OS_Halt" },
   { GUESTOS_STATECHANGE_REBOOT,  "reboot",  "OS_Reboot" },
};

#endif /* _POWEROPS_H_ */

 07070100000153000081A400000000000000000000000168225505000008C1000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/serviceDiscovery.h /*********************************************************
 * Copyright (c) 2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _SERVICEDISCOVERY_H_
#define _SERVICEDISCOVERY_H_

/**
 * @file serviceDiscovery.h
 *
 * Common declarations that aid in sending services information
 * from 'serviceDiscovery' plugin in 'VMware Tools' to the host.
 */


/*
 * Namespace DB used for service discovery
 */
#define SERVICE_DISCOVERY_NAMESPACE_DB_NAME "com.vmware.vrops.sdmp"

/*
 * ready - used to identify if data is succesfully written to Namespace DB
 * signal - signal send by sdmp client for plugin to start data collection
 */
#define SERVICE_DISCOVERY_KEY_READY "ready"
#define SERVICE_DISCOVERY_KEY_SIGNAL "signal"
#define SERVICE_DISCOVERY_KEY_GDP_SIGNAL "gdp-signal"

/*
 * keys for types of service data collected by plugin.
 */
#define SERVICE_DISCOVERY_KEY_PROCESSES "listening-process-info"
#define SERVICE_DISCOVERY_KEY_CONNECTIONS "connection-info"
#define SERVICE_DISCOVERY_KEY_PERFORMANCE_METRICS                              \
   "listening-process-perf-metrics"
#define SERVICE_DISCOVERY_KEY_VERSIONS "versions"

/*
 * keys for types of service data collected by plugin from Windows guest
 */
#define SERVICE_DISCOVERY_WIN_KEY_RELATIONSHIP "pid-to-ppid"
#define SERVICE_DISCOVERY_WIN_KEY_NET "net"
#define SERVICE_DISCOVERY_WIN_KEY_IIS_PORTS "iis-ports-info"
#define SERVICE_DISCOVERY_WIN_KEY_SHAREPOINT_PORTS "sharepoint-ports-info"

#endif /* _SERVICEDISCOVERY_H_ */
   07070100000154000081A40000000000000000000000016822550500001637000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/tclodefs.h /*********************************************************
 * Copyright (c) 2009-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _TCLODEFS_H_
#define _TCLODEFS_H_

/**
 * @file tclodefs.h
 *
 * Miscelaneous GuestRPC definitions. There include generic definitions not
 * particular to any application, and also some application-specific definitions
 * that don't really justify a separate header file.
 */

/** TCLO channel name for the main Tools service. */
#define TOOLS_DAEMON_NAME         "toolbox"
/** TCLO channel name for the Tools configuration UI. */
#define TOOLS_CTLPANEL_NAME       "toolbox-ui"
/** TCLO channel name for the Tools user service. */
#define TOOLS_DND_NAME            "toolbox-dnd"
/** TCLO channel name for the Tools upgraded. */
#define TOOLS_UPGRADER_NAME       "tools-upgrader"
/** TCLO channel name for the VDI single-sign-on service. */
#define TOOLS_SSO_NAME            "tools-sso"
/** TCLO channel name for the HGFS driver. */
#define TOOLS_HGFS_NAME           "tools-hgfs"

/*
 * The TCLO channel names used by the View Agent Service.
 * vdiagent may register RPC callbacks to be notified of certain events not
 * handled by Tools Service.
 */
#define TOOLS_VDIAGENT_NAME       "vdiagent"

/** Reply from host when the command is not recognized. */
#define RPCI_UNKNOWN_COMMAND      "Unknown command"

/** Reply from the guest when the command is not recognized. */
#define GUEST_RPC_UNKNOWN_COMMAND "Unknown Command"

/** Reply from host when the option is disabled. */
#define GUEST_RPC_DISABLED_OPTION "disabled"

#define GUESTRPC_TCLO_VSOCK_LISTEN_PORT      975
#define GUESTRPC_RPCI_VSOCK_LISTEN_PORT      976

/*
 * Tools options.
 */

#define TOOLSOPTION_COPYPASTE                     "copypaste"
#define TOOLSOPTION_AUTOHIDE                      "autohide"
#define TOOLSOPTION_BROADCASTIP                   "broadcastIP"
#define TOOLSOPTION_ENABLEDND                     "enableDnD"
#define TOOLSOPTION_MAP_ROOT_HGFS_SHARE           "mapRootHgfsShare"
#define TOOLSOPTION_LINK_ROOT_HGFS_SHARE          "linkRootHgfsShare"
#define TOOLSOPTION_ENABLE_MESSAGE_BUS_TUNNEL     "enableMessageBusTunnel"
#define TOOLSOPTION_GUEST_LOG_LEVEL               "guestLogLevel"
#define TOOLSOPTION_ENABLE_APPINFO                "enableAppInfo"
#define TOOLSOPTION_ENABLE_GUESTSTORE_ACCESS      "enableGuestStoreAccess"
#define TOOLSOPTION_ENABLE_DEVICEHELPER           "enableDeviceHelper"

/*
 * Device helper commands.
 */

#define DEVICEHELPER_CONFIG_DONE_CMD    "guest.deviceHelper.config_done"

#define DEVICEHELPER_CONFIG_STATUS_SUCCESS     1
#define DEVICEHELPER_CONFIG_STATUS_FAILURE     0

/*
 * Auto-upgrade commands.
 */

#define AUTOUPGRADE_AVAILABLE_CMD       "vmx.capability.tools_is_upgradable"
#define AUTOUPGRADE_START_CMD           "guest.initiateAutoUpgrade"
#define AUTOUPGRADE_POWERON_POLICY_CMD  "vmx.autoupgrade.poweron_policy"

/* More upgrader commands. */
#define GUEST_UPGRADER_SEND_CMD_LINE_ARGS  "guest.upgrader_send_cmd_line_args"

/*
 * GuestStore Upgrade commands.
 */

#define GSUPGRADE_START_CMD   "guestStoreUpgrade.start"
#define GSUPGRADE_CANCEL_CMD  "guestStoreUpgrade.cancel"

/*
 * Shrink commands.
 */

#define DISK_SHRINK_CMD             "disk.shrink"

/*
 * Auto-lock commands.
 */

#define DESKTOP_AUTOLOCK_CMD        "Autolock_Desktop"

/*
 * Guest log commands.
 */

#define GUEST_LOG_STATE_CMD "guest.log.state"
#define GUEST_LOG_TEXT_CMD "guest.log.text"

/*
 *  Update tools health command.
 */
#define UPDATE_TOOLS_HEALTH_CMD "update.tools.health"
#define TOOLS_HEALTH_NORMAL_KEY "normal"
#define TOOLS_HEALTH_HUNG_KEY "hung"
#define TOOLS_HEALTH_GUEST_SLOW_KEY "guest_slow"

/*
 * The max selection buffer length has to be less than the
 * ipc msg max size b/c the selection is transferred from mks -> vmx
 * and then through the backdoor to the tools. Also, leave
 * some room for ipc msg overhead.
 */
#define MAX_SELECTION_BUFFER_LENGTH       ((1 << 16) - 100)

#define VMWARE_DONT_EXCHANGE_SELECTIONS	(-2)
#define VMWARE_SELECTION_NOT_READY        (-1)

#define VMWARE_GUI_AUTO_GRAB              0x001
#define VMWARE_GUI_AUTO_UNGRAB            0x002
#define VMWARE_GUI_AUTO_SCROLL            0x004
#define VMWARE_GUI_AUTO_RAISE             0x008
#define VMWARE_GUI_EXCHANGE_SELECTIONS    0x010
#define VMWARE_GUI_WARP_CURSOR_ON_UNGRAB  0x020
#define VMWARE_GUI_FULL_SCREEN            0x040

#define VMWARE_GUI_TO_FULL_SCREEN         0x080
#define VMWARE_GUI_TO_WINDOW              0x100

#define VMWARE_GUI_AUTO_RAISE_DISABLED    0x200

#define VMWARE_GUI_SYNC_TIME              0x400

/* When set, toolboxes should not show the cursor options page. */
#define VMWARE_DISABLE_CURSOR_OPTIONS     0x800

#define  RPCIN_TCLO_PING                 0x1

enum {
   GUESTRPCPKT_TYPE_DATA = 1,
   GUESTRPCPKT_TYPE_PING
};

enum {
   GUESTRPCPKT_FIELD_TYPE = 1,
   GUESTRPCPKT_FIELD_PAYLOAD,
   GUESTRPCPKT_FIELD_FAST_CLOSE
};

#endif /* _TCLODEFS_H_ */

 07070100000155000081A400000000000000000000000168225505000007CB000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/timesync.h /*********************************************************
 * Copyright (C) 2009-2019,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _TIMESYNC_H_
#define _TIMESYNC_H_

/**
 * @file timesync.h
 *
 * Definitions related to the time sync functionality.
 */

#define TOOLSOPTION_SYNCTIME                        "synctime"
#define TOOLSOPTION_SYNCTIME_PERIOD                 "synctime.period"
#define TOOLSOPTION_SYNCTIME_ALLOW                \
   "time.synchronize.allow"
#define TOOLSOPTION_SYNCTIME_ENABLE               \
   "time.synchronize.tools.enable"
#define TOOLSOPTION_SYNCTIME_STARTUP_BACKWARD     \
   "time.synchronize.tools.startup.backward"
#define TOOLSOPTION_SYNCTIME_STARTUP              \
   "time.synchronize.tools.startup"
#define TOOLSOPTION_SYNCTIME_SLEWCORRECTION       \
   "time.synchronize.tools.slewCorrection"
#define TOOLSOPTION_SYNCTIME_PERCENTCORRECTION    \
   "time.synchronize.tools.percentCorrection"
#define TOOLSOPTION_SYNCTIME_GUEST_RESYNC         \
   "time.synchronize.guest.resync"
#define TOOLSOPTION_SYNCTIME_GUEST_RESYNC_TIMEOUT \
   "time.synchronize.guest.resync.timeout"

#define TIMESYNC_SYNCHRONIZE "Time_Synchronize"
#define TIMEINFO_UPDATE "TimeInfo_Update"


#endif /* _TIMESYNC_H_ */

 07070100000156000081A40000000000000000000000016822550500000BA6000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/guestrpc/vmbackup.h /*********************************************************
 * Copyright (C) 2007-2017,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * These are definitions shared by the different components that implement
 * the different endpoints of the vmbackup functionality (VIM, VMX, Tools).
 */

#ifndef _VMBACKUP_DEF_H_
#define _VMBACKUP_DEF_H_

/*
 * How often (in milliseconds) does the guest send keep alive messages
 * to the host during long operations.
 */
#define VMBACKUP_KEEP_ALIVE_PERIOD        5000

/* These are RPC messages used between the VMX and the Tools. */
#define VMBACKUP_PROTOCOL_PREFIX          "vmbackup."
#define VMBACKUP_PROTOCOL_START           VMBACKUP_PROTOCOL_PREFIX"start"
#define VMBACKUP_PROTOCOL_START_WITH_OPTS VMBACKUP_PROTOCOL_PREFIX"startWithOpts"
#define VMBACKUP_PROTOCOL_ABORT           VMBACKUP_PROTOCOL_PREFIX"abort"
#define VMBACKUP_PROTOCOL_SNAPSHOT_DONE   VMBACKUP_PROTOCOL_PREFIX"snapshotDone"
#define VMBACKUP_PROTOCOL_EVENT_SET       VMBACKUP_PROTOCOL_PREFIX"eventSet"
#define VMBACKUP_PROTOCOL_SNAPSHOT_COMPLETED \
   VMBACKUP_PROTOCOL_PREFIX"snapshotCompleted"

/* These are responses to messages sent to the guest. */
#define VMBACKUP_PROTOCOL_ERROR           "protocol.error"
#define VMBACKUP_PROTOCOL_RESPONSE        "protocol.response"

/* These are events sent from the guest. */
#define VMBACKUP_EVENT_RESET              "reset"
#define VMBACKUP_EVENT_REQUESTOR_ABORT    "req.aborted"
#define VMBACKUP_EVENT_REQUESTOR_DONE     "req.done"
#define VMBACKUP_EVENT_REQUESTOR_ERROR    "req.error"
#define VMBACKUP_EVENT_REQUESTOR_MANIFEST "req.manifest"
#define VMBACKUP_EVENT_GENERIC_MANIFEST   "req.genericManifest"
#define VMBACKUP_EVENT_SNAPSHOT_ABORT     "prov.snapshotAbort"
#define VMBACKUP_EVENT_SNAPSHOT_COMMIT    "prov.snapshotCommit"
#define VMBACKUP_EVENT_SNAPSHOT_PREPARE   "prov.snapshotPrepare"
#define VMBACKUP_EVENT_WRITER_ERROR       "req.writerError"
#define VMBACKUP_EVENT_KEEP_ALIVE         "req.keepAlive"

/* These are the event codes sent with the events */
typedef enum {
   VMBACKUP_SUCCESS = 0,
   VMBACKUP_INVALID_STATE,
   VMBACKUP_SCRIPT_ERROR,
   VMBACKUP_SYNC_ERROR,
   VMBACKUP_REMOTE_ABORT,
   VMBACKUP_UNEXPECTED_ERROR
} VmBackupStatus;

#endif

  07070100000157000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools   07070100000158000081A40000000000000000000000016822550500000ECE000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/desktopevents.h   /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _DESKTOPEVENTS_H_
#define _DESKTOPEVENTS_H_

/**
 * @file desktopevents.h
 *
 * Public interface for the "switchUser" plugin. This plugin exposes some
 * user session-related events to other plugins.
 *
 * Aside from the functionality exposed in this file, the plugin also
 * emits the TOOLS_CORE_SIG_SESSION_CHANGE signal (which is handled
 * by vmtoolsd automatically when it's run from within the SCM;
 * see plugin.h).
 *
 * Currently the plugin is only available on Win32.
 *
 * @addtogroup vmtools_plugins
 * @{
 */

#if defined(_WIN32)

/**
 * Signal sent when a "desktop switch" event is detected.
 *
 * Defined in desktopevents.h.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: the application context.
 * @param[in]  data     Client data.
 */
#define TOOLS_CORE_SIG_DESKTOP_SWITCH "tcs_desktop_switch"


/**
 * Name of the message that can be sent to the desktop events window to
 * shut down the vmusr instance.
 */
#define DESKTOP_EVENTS_SHUTDOWN "VMdesktopEventsShutdownMsg"


/**
 * Name of the message that can be sent to the vm3dservice user daemons
 * for shutdown.
 */
#define DESKTOP_VM3DSERVICE_SHUTDOWN "VMdesktopEventsVM3DServiceShutdownMsg"

#else   // !WIN32

/**
 * Signal emitted upon X I/O error callback firing.
 *
 * @param[in]   src     The source object.
 * @param[in]   ctx     ToolsAppCtx *: the application context.
 * @param[in]   data    Client data.
 */
#define TOOLS_CORE_SIG_XIOERROR "tcs_de_xioerror"

/**
 * Signal emitted upon SmcCallbacks::save_yourself.
 *
 * @param[in]   src             The source object.
 * @param[in]   ctx             ToolsAppCtx *: the application context.
 * @parma[in]   saveType        Refer to SMlib.xml.
 * @param[in]   shutdown        0 = checkpoint, 1 = shutdown.
 * @param[in]   interactStyle   May interact with user?
 * @param[in]   fast            Shutdown as quickly as possible.
 * @param[in]   data            Client data.
 */
#define TOOLS_CORE_SIG_XSM_SAVE_YOURSELF "tcs_de_xsm_save_yourself"

/**
 * Signal emitted upon SmcCallbacks::die.
 *
 * @param[in]   src     The source object.
 * @param[in]   ctx     ToolsAppCtx *: the application context.
 * @param[in]   data    Client data.
 */
#define TOOLS_CORE_SIG_XSM_DIE "tcs_de_xsm_die"

/**
 * Signal emitted upon SmcCallbacks::save_complete.
 *
 * @param[in]   src     The source object.
 * @param[in]   ctx     ToolsAppCtx *: the application context.
 * @param[in]   data    Client data.
 */
#define TOOLS_CORE_SIG_XSM_SAVE_COMPLETE "tcs_de_xsm_save_complete"

/**
 * Signal emitted upon SmcCallbacks::shutdown_cancelled.
 *
 * @param[in]   src     The source object.
 * @param[in]   ctx     ToolsAppCtx *: the application context.
 * @param[in]   data    Client data.
 */
#define TOOLS_CORE_SIG_XSM_SHUTDOWN_CANCELLED "tcs_de_xsm_shutdown_cancelled"

#endif  // if defined(_WIN32)

/** @} */

#endif /* _DESKTOPEVENTS_H_ */

  07070100000159000081A40000000000000000000000016822550500001C6D000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/gdp.h /*********************************************************
 * Copyright (c) 2020-2021,2023-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMWARE_TOOLS_GDP_H_
#define _VMWARE_TOOLS_GDP_H_

/**
 * @file gdp.h
 *
 * Public interface of the "gdp" plugin service.
 */

/*
 * glib-object.h should not be placed inside `extern "C"' blocks.
 * However, this header is often placed inside such blocks.
 * Here we change back into C++ for glib-object.h
 */
#ifdef __cplusplus
extern "C++" {
#endif
#include <glib-object.h>
#ifdef __cplusplus
}
#endif

#include "vmware/tools/plugin.h"

/*
 * GDP Protocol version
 */
#define GDP_PROTOCOL_VERSION 2

/*
 * GDP Protocol version before versioning was introduced.
 */
#define GDP_PROTOCOL_UNVERSIONED_VERSION 1

/*
 * First GDP Protocol version to support versioning.
 */
#define GDP_PROTOCOL_VERSIONED_VERSION 2

/*
 * Maximum GDP Data Message protocol version generated by GDP plugin.
 */
#define GDP_PROTOCOL_DM_MAX_VERSION GDP_PROTOCOL_VERSION

/*
 * Maximum GDP Data Message Response protocol version handled by GDP plugin.
 */
#define GDP_PROTOCOL_DM_RESP_MAX_VERSION GDP_PROTOCOL_VERSION

/*
 * Size in bytes:
 * 17 * 4096 - Maximum VMCI datagram size
 *        24 - VMCI datagram header size
 */
#define GDP_MAX_PACKET_LEN (17 * 4096 - 24)

/*
 * Limit GDP packet JSON base64 key value size to (16 * 4096) bytes, then
 * the rest JSON content will have (4096 - 24) bytes available.
 *
 * Base64 (16 * 4096) bytes are (12 * 4096) bytes before encoding.
 */
#define GDP_USER_DATA_LEN (12 * 4096)

/*
 * Property name of the gdp plugin service in the tools
 * applicatin context service object.
 */
#define TOOLS_PLUGIN_SVC_PROP_GDP "tps_prop_gdp"

/*
 * GdpError definitions.
 * The GDP_ERR_ITEM Tuple is:
 *   - GdpEnum name
 *   - error-id string id
 *   - Default error message string
 *
 * GDP_ERR_MAX item MUST BE LAST
 */
#define GDP_ERR_LIST                                      \
   GDP_ERR_ITEM(GDP_ERROR_SUCCESS = 0,                    \
                "success",                                \
                "No error")                               \
   GDP_ERR_ITEM(GDP_ERROR_INVALID_DATA,                   \
                "invalid-data",                           \
                "Invalid data")                           \
   GDP_ERR_ITEM(GDP_ERROR_DATA_SIZE,                      \
                "data-size",                              \
                "Data size too large")                    \
   GDP_ERR_ITEM(GDP_ERROR_GENERAL,                        \
                "error",                                  \
                "General error")                          \
   GDP_ERR_ITEM(GDP_ERROR_STOP,                           \
                "stopped-for-shutdown",                   \
                "Stopped for vmtoolsd shutdown")          \
   GDP_ERR_ITEM(GDP_ERROR_UNREACH,                        \
                "publisher-unreachable",                  \
                "Host daemon unreachable")                \
   GDP_ERR_ITEM(GDP_ERROR_TIMEOUT,                        \
                "timeout",                                \
                "Operation timed out")                    \
   GDP_ERR_ITEM(GDP_ERROR_NO_SUBSCRIBERS,                 \
                "no-subscribers",                         \
                "No subscribers for data")                \
   GDP_ERR_ITEM(GDP_ERR_MAX,                              \
                "last-error",                             \
                "last-error")

/*
 * GdpError codes enum.
 */
#define GDP_ERR_ITEM(a, b, c) a,
typedef enum GdpError {
   GDP_ERR_LIST
} GdpError;
#undef GDP_ERR_ITEM


/**
 * @brief Type of the public interface of the gdp plugin service.
 *
 * This struct is published in the tools application context service object's
 * TOOLS_PLUGIN_SVC_PROP_GDP property.
 */
typedef struct ToolsPluginSvcGdp {
   GdpError (*publish)(gint64 createTime,
                       const gchar *topic,
                       const gchar *token,
                       const gchar *category,
                       const gchar *data,
                       guint32 dataLen,
                       gboolean cacheData,
                       gboolean requireSubs);
} ToolsPluginSvcGdp;


/*
 ******************************************************************************
 * ToolsPluginSvcGdp_Publish --                                          */ /**
 *
 * @brief Publishes guest data to host side gdp daemon.
 *
 * This function is thread-safe and blocking, it should be called by vmtoolsd
 * pool threads started by ToolsCorePool_StartThread. Do not call the function
 * in ToolsOnLoad nor in/after TOOLS_CORE_SIG_SHUTDOWN handler.
 *
 * @param[in]          ctx         The application context
 * @param[in]          createTime  UTC timestamp, in number of micro-
 *                                 seconds since January 1, 1970 UTC.
 * @param[in]          topic       Topic
 * @param[in,optional] token       Token, can be NULL
 * @param[in,optional] category    Category, can be NULL that defaults to
 *                                 "application"
 * @param[in]          data        Buffer containing data to publish
 * @param[in]          dataLen     Buffer length
 * @param[in]          cacheData   Cache the data if TRUE
 * @param[in]          requireSubs Require subscriber(s) if TRUE
 *
 * @return GDP_ERROR_SUCCESS on success.
 * @return Other GdpError code otherwise.
 *
 ******************************************************************************
 */

static inline GdpError
ToolsPluginSvcGdp_Publish(ToolsAppCtx *ctx,      // IN
                          gint64 createTime,     // IN
                          const gchar *topic,    // IN
                          const gchar *token,    // IN, OPTIONAL
                          const gchar *category, // IN, OPTIONAL
                          const gchar *data,     // IN
                          guint32 dataLen,       // IN
                          gboolean cacheData,    // IN
                          gboolean requireSubs)  // IN
{
   ToolsPluginSvcGdp *svcGdp = NULL;
   g_object_get(ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GDP, &svcGdp, NULL);
   if (svcGdp != NULL && svcGdp->publish != NULL) {
      return svcGdp->publish(createTime, topic, token,
                             category, data, dataLen, cacheData, requireSubs);
   }
   return GDP_ERROR_GENERAL;
}

#endif /* _VMWARE_TOOLS_GDP_H_ */
   0707010000015A000081A40000000000000000000000016822550500000C37000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/guestStore.h  /*********************************************************
 * Copyright (c) 2020,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _GUESTSTORE_H_
#define _GUESTSTORE_H_

/**
 * @file guestStore.h
 *
 * Public interface for the GuestStore plugin.
 *
 * @addtogroup vmtools_plugins
 * @{
 */

/*
 * glib-object.h should not be placed inside `extern "C"' blocks.
 * However, this header is often placed inside such blocks.
 * Here we change back into C++ for glib-object.h
 */
#ifdef __cplusplus
extern "C++" {
#endif
#include <glib-object.h>
#ifdef __cplusplus
}
#endif

#include "vmware/tools/plugin.h"

/**
 * Signal sent when GuestStore is enabled or disabled.
 *
 * @param[in]  src      The source object.
 * @param[in]  enabled  TRUE if VMX GuestStore access is enabled, FALSE otherwise.
 * @param[in]  data     Client data.
 */
#define TOOLS_CORE_SIG_GUESTSTORE_STATE "tcs_gueststore_state"

/*
 * Property name of the guestStore plugin in the tools application context
 * service object.
 */
#define TOOLS_PLUGIN_SVC_PROP_GUESTSTORE "tps_prop_gueststore"

/**
 * @brief Type of the public interface of the guestStore plugin.
 *
 * This struct is published in the tools application context service object's
 * TOOLS_PLUGIN_SVC_PROP_GUESTSTORE property.
 */
typedef struct ToolsPluginSvcGuestStore {
   void (*shutdown)(void);
} ToolsPluginSvcGuestStore;


/*
 ******************************************************************************
 * ToolsPluginSvcGuestStore_Shutdown --                                  */ /**
 *
 * @brief Shuts down guestStore plugin.
 *
 * To avoid possible deadlock at vmtoolsd shutdown time, guestStore plugin
 * needs to be shut down before tools core thread pool. This function provides
 * a special way to shut down guestStore plugin other than regular in-plugin
 * TOOLS_CORE_SIG_SHUTDOWN signal handler.
 *
 * @param[in]              ctx       The app context
 *
 ******************************************************************************
 */

static inline void
ToolsPluginSvcGuestStore_Shutdown(ToolsAppCtx *ctx) // IN
{
   ToolsPluginSvcGuestStore *svcGuestStore = NULL;
   g_object_get(ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GUESTSTORE,
                &svcGuestStore, NULL);
   if (svcGuestStore != NULL && svcGuestStore->shutdown != NULL) {
      svcGuestStore->shutdown();
   }
}

/** @} */

#endif /* _GUESTSTORE_H_ */
 0707010000015B000081A40000000000000000000000016822550500001F41000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/guestStoreClientLib.h /*********************************************************
 * Copyright (c) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 *  guestStoreClientLib.h  --
 *    Definitions for VMware Tools guestStore client library.
 */

#ifndef __GUESTSTORECLIENTLIB_H__
#define __GUESTSTORECLIENTLIB_H__

#include "vm_basic_types.h"

#define GUESTSTORE_LIB_ERR_LIST                                              \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_SUCCESS = 0,                             \
                           gsliberr.success,                                 \
                           "Success")                                        \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_GENERIC,                                 \
                           gsliberr.generic,                                 \
                           "Generic error")                                  \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_TLS,                                     \
                           gsliberr.tls,                                     \
                           "TLS error")                                      \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_NOT_INITIALIZED,                         \
                           gsliberr.not.initialized,                         \
                           "Not initialized")                                \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_INVALID_PARAMETER,                       \
                           gsliberr.invalid.parameter,                       \
                           "Invalid parameter")                              \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_NOT_ENOUGH_MEMORY,                       \
                           gsliberr.not.enough.memory,                       \
                           "Not enough memory")                              \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CREATE_OUTPUT_FILE,                      \
                           gsliberr.create.output.file,                      \
                           "Create output file error")                       \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_WRITE_OUTPUT_FILE,                       \
                           gsliberr.write.output.file,                       \
                           "Write output file error")                        \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_GENERIC,                         \
                           gsliberr.connect.generic,                         \
                           "Connect generic error")                          \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_SERVICE_NOT_RUNNING,             \
                           gsliberr.connect.service.not.running,             \
                           "Connect service not running")                    \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_PERMISSION_DENIED,               \
                           gsliberr.connect.permission.denied,               \
                           "Connect permission denied")                      \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_SECURITY_VIOLATION,              \
                           gsliberr.connect.security.violation,              \
                           "Connect security violation")                     \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONNECT_PEER_RESET,                      \
                           gsliberr.connect.peer.reset,                      \
                           "Connect peer reset")                             \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_SEND,                                    \
                           gsliberr.send,                                    \
                           "Send error")                                    \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_RECV,                                    \
                           gsliberr.recv,                                    \
                           "Receive error")                                  \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONTENT_FORBIDDEN,                       \
                           gsliberr.content.forbidden,                       \
                           "Content forbidden")                              \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CONTENT_NOT_FOUND,                       \
                           gsliberr.content.not.found,                       \
                           "Content not found")                              \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_SERVER,                                  \
                           gsliberr.server,                                  \
                           "Server error")                                   \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CANCELLED,                               \
                           gsliberr.cancelled,                               \
                           "Cancelled")                                      \
   GUESTSTORE_LIB_ERR_ITEM(GSLIBERR_CHECKSUM,                                \
                           gsliberr.checksum,                                \
                           "Checksum error")

/*
 * Error codes
 */
#define GUESTSTORE_LIB_ERR_ITEM(a, b, c) a,
typedef enum {
GUESTSTORE_LIB_ERR_LIST
GUESTSTORE_LIB_ERR_MAX
} GuestStoreLibError;
#undef GUESTSTORE_LIB_ERR_ITEM

/*
 * Log levels
 */
typedef enum {
   GSLIBLOGLEVEL_ERROR = 1,
   GSLIBLOGLEVEL_WARNING,
   GSLIBLOGLEVEL_INFO,
   GSLIBLOGLEVEL_DEBUG,
} GuestStoreLibLogLevel;


#ifdef __cplusplus
extern "C" {
#endif

/*
 * Caller provided function to receive log messages from GuestStore client
 * library. Caller can log the messages to its own logging facilities.
 */
typedef void (*GuestStore_Logger) (GuestStoreLibLogLevel level,
                                   const char *message,
                                   void *clientData);

/*
 * Caller provided Panic function in non-recoverable error situations.
 * This function shall exit the library host process.
 */
typedef void (*GuestStore_Panic) (const char *message,
                                  void *clientData);

/*
 * Caller provided callback to get total content size in bytes and so far
 * received bytes. Return FALSE to cancel content download.
 */
typedef Bool (*GuestStore_GetContentCallback) (int64 contentSize,
                                               int64 contentBytesReceived,
                                               void *clientData);

/*
 * GuestStore client library Init entry point function.
 */
GuestStoreLibError
GuestStore_Init(void);

/*
 * GuestStore client library GetContent entry point function.
 */
GuestStoreLibError
GuestStore_GetContent(
   const char *contentPath,                     // IN
   const char *outputPath,                      // IN
   GuestStore_Logger logger,                    // IN, OPTIONAL
   GuestStore_Panic panic,                      // IN, OPTIONAL
   GuestStore_GetContentCallback getContentCb,  // IN, OPTIONAL
   void *clientData);                           // IN, OPTIONAL

/*
 * GuestStore client library DeInit entry point function.
 * Call of GuestStore_DeInit should match succeeded GuestStore_Init call.
 */
GuestStoreLibError
GuestStore_DeInit(void);

#ifdef __cplusplus
}
#endif

#endif /* __GUESTSTORECLIENTLIB_H__ */
   0707010000015C000081A40000000000000000000000016822550500001D42000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/guestrpc.h    /*********************************************************
 * Copyright (C) 2008,2014-2016,2018-2020,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMWARE_TOOLS_GUESTRPC_H_
#define _VMWARE_TOOLS_GUESTRPC_H_

/**
 * @file guestrpc.h
 *
 *    Defines the interface between applications and the underlying GuestRPC
 *    channel. The goal is to have an abstraction so applications can run over
 *    the backdoor, vSockets or TCP/IP sockets by just picking up the
 *    desired channel at runtime, without the need to modify the code.
 *
 *    For this reason, the behavior of all channels is modeled after the RpcIn
 *    channel currently used in Tools, so the socket-based channels won't
 *    provide much better functionality than what the backdoor provides (aside
 *    from being interrupt-based rather than poll-based).
 *
 * @addtogroup vmtools_guestrpc
 * @{
 */

/*
 * glib.h should not be placed inside `extern "C"' blocks.
 * However, this header is often placed inside such blocks.
 * Here we change back into C++ for glib.h
 */
#ifdef __cplusplus
extern "C++" {
#endif
#include <glib.h>
#ifdef __cplusplus
}
#endif

#include "vmware/tools/utils.h"

G_BEGIN_DECLS

/** Aliases. */
#define RPCIN_SETRETVALS  RpcChannel_SetRetVals
#define RPCIN_SETRETVALSF RpcChannel_SetRetValsF

/** Error messages returned by RpcChannel Send APIs **/

#define RPCCHANNEL_SEND_PERMISSION_DENIED "Permission denied"

typedef struct _RpcChannel RpcChannel;

/** Data structure passed to RPC callbacks. */
typedef struct RpcInData {
   /** RPC name. */
   const char *name;
   /**
    * RPC arguments. Either the raw argument data, or de-serialized XDR data
    * in case @a xdrIn was provided in the registration data.
    */
   const char *args;
   /** Size of raw argument data, in bytes. */
   size_t argsSize;
   /**
    * Data to be returned to the caller, or pointer to XDR structure if
    * @a xdrOut was provided in the registration data.
    */
   char *result;
   /** Length in bytes of raw data being returned (ignored for XDR structures). */
   size_t resultLen;
   /**
    * Whether the RPC library should free the contents of the @a result
    * field (using vm_free()).
    */
   gboolean freeResult;
   /** Application context. */
   void *appCtx;
   /** Client data specified in the registration data. */
   void *clientData;
} RpcInData;

typedef enum RpcChannelType {
   RPCCHANNEL_TYPE_INACTIVE,
   RPCCHANNEL_TYPE_BKDOOR,
   RPCCHANNEL_TYPE_PRIV_VSOCK,
   RPCCHANNEL_TYPE_UNPRIV_VSOCK
} RpcChannelType;

/**
 * Type for RpcIn callbacks. The callback function is responsible for
 * allocating memory for the result string.
 */
typedef gboolean (*RpcIn_Callback)(RpcInData *data);


/** Defines the registration data for a GuestRPC application. */
typedef struct RpcChannelCallback {
   /** String identifying the RPC message. */
   const char       *name;
   /** Function to call when data arrives. */
   RpcIn_Callback    callback;
   /** Data to provide to callback function. */
   gpointer          clientData;
   /** If not NULL, the input data will be deserialized using this function. */
   gpointer          xdrIn;
   /**
    * If not NULL, the output data will be serialized using this function. The
    * output data should be stored in the @a result field of the RpcInData
    * structure, and should have been allocated with glib's @a g_malloc() if
    * @a freeResult is TRUE.
    */
   gpointer          xdrOut;
   /**
    * If xdrIn is not NULL, this should be the amount of memory to allocate
    * for deserializing the input data.
    */
   size_t            xdrInSize;
} RpcChannelCallback;

/**
 * Signature for the callback function called after a channel reset.
 *
 * @param[in]  chan     The RPC channel.
 * @param[in]  success  Whether reset was successful.
 * @param[in]  data     Client data.
 */
typedef void (*RpcChannelResetCb)(RpcChannel *chan,
                                  gboolean success,
                                  gpointer data);

/**
 * Signature for the application callback function when unable to establish
 * an RpcChannel connection.
 *
 * @param[in]  _state     Client data.
 */
typedef void (*RpcChannelFailureCb)(gpointer _state);


gboolean
RpcChannel_Start(RpcChannel *chan);

void
RpcChannel_Stop(RpcChannel *chan);

RpcChannelType
RpcChannel_GetType(RpcChannel *chan);

gboolean
RpcChannel_Send(RpcChannel *chan,
                char const *data,
                size_t dataLen,
                char **result,
                size_t *resultLen);

void
RpcChannel_Free(void *ptr);

#if !defined(USE_RPCI_ONLY)
gboolean
RpcChannel_BuildXdrCommand(const char *cmd,
                           void *xdrProc,
                           void *xdrData,
                           char **result,
                           size_t *resultLen);
gboolean
RpcChannel_Dispatch(RpcInData *data);

void
RpcChannel_Setup(RpcChannel *chan,
                 const gchar *appName,
                 GMainContext *mainCtx,
                 gpointer appCtx,
                 RpcChannelResetCb resetCb,
                 gpointer resetData,
                 RpcChannelFailureCb failureCb,
                 guint maxFailures);

void
RpcChannel_RegisterCallback(RpcChannel *chan,
                            RpcChannelCallback *rpc);

void
RpcChannel_UnregisterCallback(RpcChannel *chan,
                              RpcChannelCallback *rpc);
#endif

RpcChannel *
RpcChannel_Create(void);

void
RpcChannel_Destroy(RpcChannel *chan);

gboolean
RpcChannel_SetRetVals(RpcInData *data,
                      char const *result,
                      gboolean retVal);

gboolean
RpcChannel_SetRetValsF(RpcInData *data,
                       char *result,
                       gboolean retVal);

gboolean
RpcChannel_SendOneRaw(const char *data,
                      size_t dataLen,
                      char **result,
                      size_t *resultLen);

#if defined(__linux__) || defined(_WIN32)
gboolean
RpcChannel_SendOneRawPriv(const char *data,
                          size_t dataLen,
                          char **result,
                          size_t *resultLen);
#endif

gboolean
RpcChannel_SendOne(char **reply,
                   size_t *repLen,
                   const char *reqFmt,
                   ...);

#if defined(__linux__) || defined(_WIN32)
gboolean
RpcChannel_SendOnePriv(char **reply,
                       size_t *repLen,
                       const char *reqFmt,
                       ...);
#endif

RpcChannel *
RpcChannel_New(void);

#if defined(__linux__) || defined(_WIN32)
RpcChannel *
VSockChannel_New(int flags);
#endif

void
RpcChannel_SetBackdoorOnly(void);

RpcChannel *
BackdoorChannel_New(void);

G_END_DECLS

/** @} */

#endif

  0707010000015D000081A40000000000000000000000016822550500000B9C000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/i18n.h    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _I18N_H_
#define _I18N_H_

/**
 * @file i18n.h
 *
 * @defgroup vmtools_i18n Internationalization
 * @{
 *
 * @brief Functions and macros to help with internationalization of applications.
 *
 * To create a localizable string, use the MSGID macro in the following manner:
 *
 * @code
 *    VMTools_GetString("domain", MSGID(message.id) "Default English text.")
 * @endcode
 *
 * Or, in shorthand form:
 *
 * @code
 *    SU_(message.id, "Default English text.")
 * @endcode
 *
 * This will instruct the code to retrive the message under key "message.id"
 * in the translation catalog for the configured locale.
 *
 * The shorthand macros use the VMW_TEXT_DOMAIN macro to identify the domain
 * from which translated messages will be loaded. Each domain should first be
 * initialized by calling VMTools_BindTextDomain().
 */

#include <glib.h>

/*
 * Copied from msgid.h to avoid exposing VMware internal headers. Don't
 * change these values. Ever.
 */
#define MSG_MAGIC       "@&!*@*@"
#define MSG_MAGIC_LEN   7
#define MSGID(id)       MSG_MAGIC "(" #id ")"

/**
 * Shorthand macro to retrieve a localized message in UTF-8.
 *
 * @param[in]  msgid    The message ID.
 * @param[in]  en       English version of the message.
 *
 * @return A localized message.
 */
#define SU_(msgid, en) VMTools_GetString(VMW_TEXT_DOMAIN, MSGID(msgid) en)

#if defined(_WIN32)
/**
 * Shorthand macro to retrieve a localized message in UTF-16LE. Win32 only.
 *
 * @param[in]  msgid    The message ID.
 * @param[in]  en       English version of the message.
 *
 * @return A localized message.
 */
#  define SW_(msgid, en) VMTools_GetUtf16String(VMW_TEXT_DOMAIN, MSGID(msgid) en)
#endif

G_BEGIN_DECLS

void
VMTools_BindTextDomain(const char *domain,
                       const char *locale,
                       const char *catdir);

const char *
VMTools_GetString(const char *domain,
                  const char *msgid);

#if defined(_WIN32)
const wchar_t *
VMTools_GetUtf16String(const char *domain,
                       const char *msgid);
#endif

G_END_DECLS

/** @} */

#endif /* _I18N_H_ */

0707010000015E000081A40000000000000000000000016822550500003317000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/log.h /*********************************************************
 * Copyright (c) 2011-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMTOOLS_LOG_H_
#define _VMTOOLS_LOG_H_

/**
 * @file log.h
 *
 * Some wrappers around glib log functions, expanding their functionality to
 * support common usage patterns at VMware.
 *
 * @defgroup vmtools_logging Logging
 * @{
 *
 * The Tools logging facility is built on top of glib's logging functions
 * (http://developer.gnome.org/glib/stable/glib-Message-Logging.html). Some
 * convenience macros built on top of glib's existing macros are also provided.
 *
 * Logging is configurable on a per-domain basis. The configuration options
 * for each domain are:
 *
 *    - level: minimum log level to log. Also used to declare specific log
 *      domain configurations.
 *      - Valid values: error, critical, warning, message, info, debug, none
 *      - This value is required when configuring a domain.
 *    - handler: the handler to use when logging.
 *      - Valid values: std, outputdebugstring (Win32-only), file, file+ (same as
 *        "file", but appends to existing log file), vmx, syslog.
 *      - Default: "syslog".
 *
 * For file handlers, the following extra configuration information can be
 * provided:
 *
 *    - data: path to the log file, required.
 *    - maxOldLogFiles: maximum number of rotated log files to keep around. By
 *      default, at most 10 backed up log files will be kept. Value should be >= 1.
 *    - maxLogSize: maximum size of each log file, defaults to 10 (MB). A value of
 *      0 disables log rotation.
 *
 * When using syslog on Unix, the following options are available:
 *
 *    - facility: either of "daemon", "user" or "local[0-7]". Controls whether to
 *      connect to syslog as LOG_DAEMON, LOG_USER or LOG_LOCAL[0-7], respectively
 *      (see syslog(3)). Defaults to "user". Any unknown value is mapped to
 *      LOG_USER. This option should be defined for the application's default
 *      log domain (it's ignored for all other domains).
 *
 * The "vmx" logger will log all messages to the host; it's not recommended
 * for normal use, since writing to the host log is an expensive operation and
 * can also affect other running applications that need to send messages to the
 * host. Do not use this logger unless explicitly instructed to do so.
 *
 * Log levels:
 *
 * glib log levels are supported.  The error levels from
 * most to least severe:
 *
 * 'error' - fatal errors
 * 'critical' - critical errors
 * 'warning' - something unexpected happened (useful when an error will
 *            be reported back.)
 * 'message' - messages about services starting, version data
 * 'info'    - informational and diagnostic messages.
 * 'debug'   - debug messages, typically only of interest ot a developer
 *
 *
 * Until vSphere 6.0, the default logging level for beta/rel is 'warning'.
 * Since vsphere 6.0 it is 'message'.
 *
 * When adding new logging messages, be sure to use the appropriate
 * level to balance the amount of logging and usability.  The goal
 * is to be able to debug a customer problem with the default log level
 * whenever possible, while not filling up logfiles with noise
 * or customer-sensitive data.
 *
 *
 * Logging configuration should be under the "[logging]" group in the
 * application's configuration file.
 *
 * Each application can specify a default log domain (which defaults to
 * "vmtools"). If no handler is specified for a particular domain when
 * logging, the default handler will be used. The default logging level
 * for the default domain is "warning" in non-debug builds, and "message"
 * in debug builds.
 *
 * Example of logging configuration in the config file:
 *
 * @verbatim
 * [logging]
 * # Turns on logging globally. It can still be disabled for each domain.
 * log = true
 *
 * # Disables core dumps on fatal errors; they're enabled by default.
 * enableCoreDump = false
 *
 * # Defines the "vmsvc" domain, logging to stdout/stderr.
 * vmsvc.level = info
 * vmsvc.handler = std
 *
 * # Defines the "unity" domain, logging to a file.
 * unity.level = warning
 * unity.handler = file
 * unity.data = /tmp/unity.log
 *
 * # Defines the "vmtoolsd" domain, and disable logging for it.
 * vmtoolsd.level = none
 * @endverbatim
 *
 * Log file names can contain references to pre-defined variables. The following
 * variables are expanded when determining the path of the log file:
 *
 *    - @a ${USER}: expands to the current user's login name
 *    - @a ${PID}: expands to the current process's ID.
 *    - @a ${IDX}: expands to the log file index (for rolling logs).
 *
 * So, for example, @a log.${USER}.${PID}.txt would expand to "log.jdoe.1234.txt"
 * for user "jdoe" if the process ID were 1234.
 * */

#if !defined(G_LOG_DOMAIN)
#  error "G_LOG_DOMAIN must be defined."
#endif

#include <glib.h>
#include "vmware/tools/guestrpc.h"
#include "vm_basic_types.h"

#if defined(__GNUC__)
#  define FUNC __func__
#else
#  define FUNC __FUNCTION__
#endif

/*
 *******************************************************************************
 * g_info --                                                              */ /**
 *
 * Log a message with G_LOG_LEVEL_INFO; this function is missing in glib < 2.39
 * for whatever reason.
 *
 * @param[in]  fmt   Log message format.
 * @param[in]  ...   Message arguments.
 *
 *******************************************************************************
 */
#if !defined(g_info)
#  define g_info(fmt, ...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, fmt, ## __VA_ARGS__)
#endif

/** default logging level */
#ifdef VMX86_DEBUG
#define VMTOOLS_LOGGING_LEVEL_DEFAULT "info"
#else
#define VMTOOLS_LOGGING_LEVEL_DEFAULT "message"
#endif


/*
 * As of version 2.46, glib thinks the Windows compiler where
 * _MSC_VER >= 1400 can handle G_HAVE_ISO_VARARGS.  This makes our
 * magic macros wrapping their macros fail in the simplest case,
 * where only the fmt arg is present (eg vm_debug("test").
 * vm_debug("test %d", 123) works fine.
 *
 * Work around this by making g_debug() et all be inline functions,
 * which is how it works if G_HAVE_ISO_VARARGS isn't set.
 *
 * Though experimentation we found that this also works:
 *
 * #define LOGHELPER(...)  ,##_VA_ARGS__
 * #define LOG(fmt, ...) g_debug_macro(__FUNCTION__, ": " fmt, LOGHELPER(__VA_ARGS__))
 *
 * but since its disgusting and even more magical, the inline variant was chosen
 * instead.
 */

#if defined(_WIN32) && GLIB_CHECK_VERSION(2, 46, 0)
static inline void
g_critical_inline(const gchar *fmt,
               ...)
{
   va_list args;
   va_start(args, fmt);
   g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, fmt, args);
   va_end(args);
}

/*
 *******************************************************************************
 * vm_{critical,debug,error,info,message,warning} --                      */ /**
 *
 * Wrapper around the corresponding glib function that automatically includes
 * the calling function name in the log message. The "fmt" parameter must be
 * a string constant.
 *
 * @param[in]  fmt   Log message format.
 * @param[in]  ...   Message arguments.
 *
 *******************************************************************************
 */
#define  vm_critical(fmt, ...)      g_critical_inline("%s: " fmt, FUNC, ## __VA_ARGS__)

static inline void
g_debug_inline(const gchar *fmt,
               ...)
{
   va_list args;
   va_start(args, fmt);
   g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, fmt, args);
   va_end(args);
}

/** @copydoc vm_critical */
#define  vm_debug(fmt, ...)      g_debug_inline("%s: " fmt, FUNC, ## __VA_ARGS__)

static inline void
g_error_inline(const gchar *fmt,
               ...)
{
   va_list args;
   va_start(args, fmt);
   g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, fmt, args);
   va_end(args);
}

/** @copydoc vm_critical */
#define  vm_error(fmt, ...)      g_error_inline("%s: " fmt, FUNC, ## __VA_ARGS__)


static inline void
g_info_inline(const gchar *fmt,
               ...)
{
   va_list args;
   va_start(args, fmt);
   g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, fmt, args);
   va_end(args);
}

/** @copydoc vm_critical */
#define  vm_info(fmt, ...)      g_info_inline("%s: " fmt, FUNC, ## __VA_ARGS__)

static inline void
g_message_inline(const gchar *fmt,
               ...)
{
   va_list args;
   va_start(args, fmt);
   g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
   va_end(args);
}

/** @copydoc vm_critical */
#define  vm_message(fmt, ...)      g_message_inline("%s: " fmt, FUNC, ## __VA_ARGS__)

static inline void
g_warning_inline(const gchar *fmt,
               ...)
{
   va_list args;
   va_start(args, fmt);
   g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args);
   va_end(args);
}

/** @copydoc vm_critical */
#define  vm_warning(fmt, ...)      g_warning_inline("%s: " fmt, FUNC, ## __VA_ARGS__)

#else // ! (windows & glib >= 2.46)

/*
 *******************************************************************************
 * vm_{critical,debug,error,info,message,warning} --                      */ /**
 *
 * Wrapper around the corresponding glib function that automatically includes
 * the calling function name in the log message. The "fmt" parameter must be
 * a string constant.
 *
 * @param[in]  fmt   Log message format.
 * @param[in]  ...   Message arguments.
 *
 *******************************************************************************
 */
#define  vm_critical(fmt, ...)   g_critical("%s: " fmt, FUNC, ## __VA_ARGS__)

/** @copydoc vm_critical */
#define  vm_debug(fmt, ...)      g_debug("%s: " fmt, FUNC, ## __VA_ARGS__)

/** @copydoc vm_critical */
#define  vm_error(fmt, ...)      g_error("%s: " fmt, FUNC, ## __VA_ARGS__)

/** @copydoc vm_critical */
#define  vm_info(fmt, ...)       g_info("%s: " fmt, FUNC, ## __VA_ARGS__)

/** @copydoc vm_critical */
#define  vm_message(fmt, ...)    g_message("%s: " fmt, FUNC, ## __VA_ARGS__)

/** @copydoc vm_critical */
#define  vm_warning(fmt, ...)    g_warning("%s: " fmt, FUNC, ## __VA_ARGS__)
#endif // ! (windows & glib >= 2.46)

/* Checks if a string is null before it is passed in logging function  */
#define VM_SAFE_STR(string)      (string != NULL ? string : "(NULL)")

G_BEGIN_DECLS

void
VMTools_ConfigLogToStdio(const gchar *domain);

void
VMTools_ConfigLogging(const gchar *defaultDomain,
                      GKeyFile *cfg,
                      gboolean force,
                      gboolean reset);

void
VMTools_UseVmxGuestLog(const gchar *appName);

void
VMTools_SetupVmxGuestLog(gboolean refreshRpcChannel, GKeyFile *cfg,
                         const gchar *level);

void
VMTools_TeardownVmxGuestLog(void);

typedef enum {
   TO_HOST,
   IN_GUEST
} LogWhere;

void
VMTools_Log(LogWhere where,
            GLogLevelFlags level,
            const gchar *domain,
            const gchar *fmt,
            ...);

void
VMTools_VmxLog(RpcChannel *chan,
               const gchar *fmt,
               ...);

void
VMTools_VmxLogThrottled(uint32 *count,
                        RpcChannel *chan,
                        const gchar *fmt,
                        ...);

G_END_DECLS

#define host_warning(fmt, ...)                                          \
   VMTools_Log(TO_HOST, G_LOG_LEVEL_WARNING, G_LOG_DOMAIN, fmt, ## __VA_ARGS__)

#define guest_warning(fmt, ...)                                         \
   VMTools_Log(IN_GUEST, G_LOG_LEVEL_WARNING, G_LOG_DOMAIN, fmt, ## __VA_ARGS__)

#define host_message(fmt, ...)                                          \
   VMTools_Log(TO_HOST, G_LOG_LEVEL_MESSAGE, G_LOG_DOMAIN, fmt, ## __VA_ARGS__)

#define guest_message(fmt, ...)                                         \
   VMTools_Log(IN_GUEST, G_LOG_LEVEL_MESSAGE, G_LOG_DOMAIN, fmt, ## __VA_ARGS__)

#define host_info(fmt, ...)                                     \
   VMTools_Log(TO_HOST, G_LOG_LEVEL_INFO, G_LOG_DOMAIN, fmt, ## __VA_ARGS__)

#define guest_info(fmt, ...)                                    \
   VMTools_Log(IN_GUEST, G_LOG_LEVEL_INFO, G_LOG_DOMAIN, fmt, ## __VA_ARGS__)

#define host_debug(fmt, ...)                                            \
   VMTools_Log(TO_HOST, G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN, fmt, ## __VA_ARGS__)

#define guest_debug(fmt, ...)                                           \
   VMTools_Log(IN_GUEST, G_LOG_LEVEL_DEBUG, G_LOG_DOMAIN, fmt, ## __VA_ARGS__)

/** @} */

#endif /* _VMTOOLS_LOG_H_ */
 0707010000015F000081A40000000000000000000000016822550500005902000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/plugin.h  /*********************************************************
 * Copyright (c) 2008-2020,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMWARE_TOOLS_PLUGIN_H_
#define _VMWARE_TOOLS_PLUGIN_H_

/**
 * @file plugin.h
 *
 *    Defines the interface between the core tools services and the plugins
 *    that are dynamically loaded into the service.
 *
 * @addtogroup vmtools_plugins
 * @{
 */

/*
 * glib.h should not be placed inside `extern "C"' blocks.
 * However, this header is often placed inside such blocks.
 * Here we change back into C++ for glib.h
 */
#ifdef __cplusplus
extern "C++" {
#endif
#include <glib.h>
#ifdef __cplusplus
}
#endif

#if defined(G_PLATFORM_WIN32)
#  include <windows.h>
#  include <objbase.h>
#endif
#include "vmware/guestrpc/capabilities.h"
#include "vmware/tools/guestrpc.h"
#include "vmware/tools/utils.h"

/**
 * Error reporting macro. Call this if the app encounters an error
 * that requires the service to quit. The service's main loop will stop
 * as soon as it regains control of the application.
 *
 * @param[in]  ctx      The application context.
 * @param[in]  err      Error code. Must not be 0.
 */
#define VMTOOLSAPP_ERROR(ctx, err) do {   \
   ASSERT((err) != 0);                    \
   (ctx)->errorCode = (err);              \
   g_main_loop_quit((ctx)->mainLoop);     \
} while (0)


/**
 * Attaches the given event source to the app context's main loop.
 *
 * @param[in]  ctx      The application context.
 * @param[in]  src      Source to attach.
 * @param[in]  cb       Callback to call when event is "ready".
 * @param[in]  data     Data to provide to the callback.
 * @param[in]  destroy  Destruction notification callback.
 */
#define VMTOOLSAPP_ATTACH_SOURCE(ctx, src, cb, data, destroy) do {      \
   GSource *__src = (src);                                              \
   g_source_set_callback(__src, (GSourceFunc) (cb), (data), (destroy)); \
   g_source_attach(__src, g_main_loop_get_context((ctx)->mainLoop));    \
} while (0)

/**
 * Checks if the Tools service is main (system) service or not.
 * @param[in]  ctx     The application context or tools service state.
 */
#define TOOLS_IS_MAIN_SERVICE(ctx) (strcmp((ctx)->name, \
                                           VMTOOLS_GUEST_SERVICE) == 0)

/**
 * Checks if the Tools service is user (logged in user) service or not.
 * @param[in]  ctx     The application context or tools service state.
 */
#define TOOLS_IS_USER_SERVICE(ctx) (strcmp((ctx)->name, \
                                           VMTOOLS_USER_SERVICE) == 0)

/* Indentation levels for the state log function below. */
#define TOOLS_STATE_LOG_ROOT        0
#define TOOLS_STATE_LOG_CONTAINER   1
#define TOOLS_STATE_LOG_PLUGIN      2

/**
 * Convenience function for printing state logs. This function makes sure
 * all code logging state information uses the same log domain, level and
 * use the same indentation.
 *
 * @param[in] level  Indentation level (see constants above).
 * @param[in] fmt    Message format.
 * @param[in] ...    Message arguments.
 */

static inline void
ToolsCore_LogState(guint level,
                   const char *fmt,
                   ...)
{
   gchar *indented = g_strdup_printf("%*s%s", 3 * level, "", fmt);

   va_list args;
   va_start(args, fmt);
   g_logv("state", G_LOG_LEVEL_INFO, indented, args);
   va_end(args);

   g_free(indented);
}


/**
 * Signal sent when registering or unregistering capabilities.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: the application context.
 * @param[in]  set      gboolean: TRUE if setting capabilities, FALSE if unsetting them.
 * @param[in]  data     Client data.
 *
 * @return A GArray instance with the capabilities to be set or unset. The
 *         elements should be of type ToolsAppCapability.
 */
#define TOOLS_CORE_SIG_CAPABILITIES "tcs_capabilities"

/**
 * Signal sent when the config file is reloaded.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: The application context.
 * @param[in]  data     Client data.
 */
#define TOOLS_CORE_SIG_CONF_RELOAD "tcs_conf_reload"

/**
 * Signal sent when the service receives a request to dump its internal
 * state to the log. This is for debugging purposes, and plugins can
 * respond to the signal by dumping their own state also.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: The application context.
 * @param[in]  data     Client data.
 */
#define TOOLS_CORE_SIG_DUMP_STATE  "tcs_dump_state"

/**
 * Signal sent when a successful RpcChannel reset occurs.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: The application context.
 * @param[in]  data     Client data.
 */
#define TOOLS_CORE_SIG_RESET  "tcs_reset"

/**
 * Signal sent when RpcChannel is going to be destroyed.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: The application context.
 * @param[in]  data     Client data.
 */
#define TOOLS_CORE_SIG_NO_RPC  "tcs_no_rpc"

/**
 * Signal sent when a "set option" RPC message arrives.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: The application context.
 * @param[in]  option   gchar *: Option being set.
 * @param[in]  value    gchar *: Option value.
 * @param[in]  data     Client data.
 *
 * @return A gboolean saying whether the option was recognized and the value
 *         was valid.
 */
#define TOOLS_CORE_SIG_SET_OPTION "tcs_set_option"

/**
 * Signal sent when shutting down the service.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: The application context.
 * @param[in]  data     Client data.
 */
#define TOOLS_CORE_SIG_SHUTDOWN "tcs_shutdown"

#if defined(G_PLATFORM_WIN32)

/**
 * Signal sent when the service receives a control message. For a list
 * of control messages, see the documentation on MSDN:
 *
 *    http://msdn.microsoft.com/en-us/library/ms683241%28VS.85%29.aspx
 *
 * The signal is only available on Win32. Receiving this signal doesn't
 * mean the service is running through the Windows SCM: plugins may detect
 * equivalent notifications through other means and send this signal with
 * the appropriate parameters so others can react to them.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: The application context.
 * @param[in]  handle   SERVICE_STATUS_HANDLE: Service status handle. May be
 *                      NULL if process is not under SCM control.
 * @param[in]  control  guint: The control code.
 * @param[in]  evtType  guint: The event type.
 * @param[in]  evtData  gpointer: Event data.
 * @param[in]  data     Client data.
 *
 * @return See MSDN documentation. NO_ERROR has precedence over
 * ERROR_CALL_NOT_IMPLEMENTED; if any handler returns any value other than
 * those two, then that value will be used as the return value, unless the
 * Tools service itself also handles the control message, or the MSDN
 * documentation specifies a return value.
 */
#define TOOLS_CORE_SIG_SERVICE_CONTROL  "tcs_service_control"

#endif

/**
 * @brief Property where the container's ToolsAppCtx is stored.
 *
 * This property is useful in cases where the client code has access to the
 * service object instance but not to the container's context object, such as
 * in the callback for the object's "notify" signal.
 */
#define TOOLS_CORE_PROP_CTX "tcs_app_ctx"

/**
 * Event signaled when VMTools discovers a newer version is available.
 *
 * Name of the event that can be set to the notification event to
 * indicate a new version of tools is available for install or upgrade.
 */
#define TOOLS_CORE_EVENTS_TOOLS_NEW_VERSION "VMToolsNewVersion"

/**
 * Event signaled when VMTools requires a system restart to complete an install.
 *
 * Name of the event that can be set to the notification event to
 * indicate a system restart is required to complete the install or
 * upgrade of tools.
 */
#define TOOLS_CORE_EVENTS_TOOLS_NEED_REBOOT "VMToolsNeedReboot"

#define TOOLS_CORE_EVENTS_GLOBAL_SCOPE      "Global"



/**
 * This enum lists all API versions that different versions of vmtoolsd support.
 * The "ToolsAppCtx" instance provided to plugins contains a "version" field
 * which is a bit-mask of these values, telling plugins what features the
 * container supports.
 *
 * Refer to a specific feature's documentation for which version of the API
 * is needed for it to be supported.
 */
typedef enum {
   TOOLS_CORE_API_V1    = 0x1,
} ToolsCoreAPI;


struct ToolsServiceProperty;

/**
 * Type of the function that installs a new property in
 * the application context service object.
 *
 * @param[in] obj    The application context service object.
 * @param[in] prop   Property to install.
 */
typedef void (*RegisterServiceProperty)(gpointer obj,
                                        struct ToolsServiceProperty *prop);

/**
 * Defines the context of a tools application. This data is provided by the
 * core services to applications when they're loaded.
 */
typedef struct ToolsAppCtx {
   /** Supported API versions. This is a bit-mask. */
   ToolsCoreAPI      version;
   /** Name of the application. */
   const gchar      *name;
   /** Whether we're running under a VMware hypervisor. */
   gboolean          isVMware;
   /** Error code to return from the main loop. */
   int               errorCode;
   /** The main loop instance for the service. */
   GMainLoop        *mainLoop;
   /** The RPC channel used to communicate with the VMX. */
   RpcChannel       *rpc;
   /** Service configuration from the config file. */
   GKeyFile         *config;
#if defined(G_PLATFORM_WIN32)
   /** Whether COM is initialized. */
   gboolean          comInitialized;
#else
   /** The FD to access the VMware blocking fs. -1 if no FD available. */
   int               blockFD;
   /** The FD to access the uinput. -1 if no FD available. */
   int               uinputFD;
   /** The native environment (without any VMware modifications). */
   const char      **envp;
#endif
   /**
    * A GObject instance shared among all plugins. The object itself doesn't
    * provide any functionality; but the service emits a few signals on this
    * object (see the signal name declarations in this header), and plugins can
    * register and emit their own signals using this object.
    */
   gpointer          serviceObj;

   /**
    * Function pointer for plugins to register properties to
    * the service object serviceObj.
    * This allows a plugin to share data and services to others.
    */
   RegisterServiceProperty registerServiceProperty;
} ToolsAppCtx;

#if defined(G_PLATFORM_WIN32)
/**
 * Initializes COM if it hasn't been initialized yet.
 *
 * @param[in]  ctx   The application context.
 *
 * @return TRUE if COM is initialized when the function returns.
 */
static inline gboolean
ToolsCore_InitializeCOM(ToolsAppCtx *ctx)
{
   if (!ctx->comInitialized) {
      HRESULT ret = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
      ctx->comInitialized = SUCCEEDED(ret);
      if (!ctx->comInitialized) {
         g_log(ctx->name, G_LOG_LEVEL_WARNING,
               "COM initialization failed(0x%x)\n", ret);
      }
   }
   return ctx->comInitialized;
}
#endif


/* Capabilities. */

/** Identifies the type of a Tools capability. */
typedef enum {
   TOOLS_CAP_OLD        = 0,
   TOOLS_CAP_OLD_NOVAL  = 1,
   TOOLS_CAP_NEW        = 2
} ToolsCapabilityType;

/**
 * Information about a capability supported by the application. This structure
 * supports both old-style capabilities (which have a separate RPC message for
 * each capability) and new-style capabilities (as defined in guestCaps.h).
 *
 * The service will register all capabilities with non-zero values when the
 * service is started (or the host asks for the service to register its
 * capabilities).
 */
typedef struct ToolsAppCapability {
   /** Identifies the type of the capability. */
   ToolsCapabilityType  type;
   /**
    * For old-style, the capability name. The RPC message for setting the
    * capability will be "tools.capability.[name]". Ignored for TOOLS_CAP_NEW.
    */
   const gchar         *name;
   /**
    * The capability entry in the enum defined in guestCaps.h.
    * Used only for TOOLS_CAP_NEW.
    */
   GuestCapabilities    index;
   /** The capability value. 0 means disabled. Ignored for TOOLS_CAP_OLD_NOVAL. */
   guint                value;
} ToolsAppCapability;


/* Application registration. */

/** Type of the application feature being registered. */
typedef enum {
   /**
    * Denotes a list of GuestRPC registrations (type RpcChannelCallback).
    */
   TOOLS_APP_GUESTRPC   = 1,
   /**
    * Denotes a list of signals the application is interested in (type
    * ToolsPluginSignalCb).
    */
   TOOLS_APP_SIGNALS    = 2,
   /**
    * Denotes an application provider (type ToolsAppProvider). This allows
    * plugins to extend the functionality of vmtoolsd by adding new application
    * types (that other plugins can hook into).
    */
   TOOLS_APP_PROVIDER   = 3,
   /**
    * Denotes a property made available through the service's instance object
    * (ToolsAppCtx::serviceObj).
    */
   TOOLS_SVC_PROPERTY   = 4,
} ToolsAppType;


struct ToolsPluginData;

/**
 * Defines the registration data for an "application provider". Application
 * providers allow plugins to hook into new application frameworks that will
 * be then managed by vmtoolsd - for example, an HTTP server or a dbus endpoint.
 *
 * Application providers will be loaded during startup but not activated until
 * at least one plugin provides registration data for that provider.
 */
typedef struct ToolsAppProvider {
   /** A name describing the provider. */
   const gchar   *name;
   /**
    * Application type. Optimally, new providers would request a new type to be
    * added to the "official" ToolsAppType enum declared above, although that
    * is not strictly necessary. Providers should at least try to choose an
    * unused value.
    */
   ToolsAppType   regType;
   /** Size of the registration structure for this provider. */
   size_t         regSize;
   /**
    * Activation callback (optional). This is called when vmtoolsd detects that
    * there is at least one application that needs to be registered with this
    * provider.
    *
    * @param[in]  ctx   The application context.
    * @param[in]  prov  The provider instance.
    * @param[out] err   Where to store any error information.
    */
   void (*activate)(ToolsAppCtx *ctx, struct ToolsAppProvider *prov, GError **err);
   /**
    * Registration callback. This is called after "activate", to register an
    * application provided by a plugin.
    *
    * @param[in]  ctx      The application context.
    * @param[in]  prov     The provider instance.
    * @param[in]  plugin   The plugin that owns the registration.
    * @param[in]  reg      The application registration data.
    *
    * @return Whether registration succeeded.
    */
   gboolean (*registerApp)(ToolsAppCtx *ctx,
                           struct ToolsAppProvider *prov,
                           struct ToolsPluginData *plugin,
                           gpointer reg);
   /**
    * Shutdown callback (optional). Called when the service is being shut down.
    * The provider is responsible for keeping track of registrations and
    * cleaning them up during shutdown.
    *
    * This method is only called if the provider was successfully activated.
    *
    * @param[in]  ctx   The application context.
    * @param[in]  prov  The provider instance.
    */
   void (*shutdown)(ToolsAppCtx *ctx, struct ToolsAppProvider *prov);
   /**
    * Debugging callback (optional). This callback is called when dumping the
    * service state to the logs for debugging purposes.
    *
    * This callback is called once with a "NULL" reg, so that the provider can
    * log its internal state, and then once for each registration struct
    * provided by loaded plugins.
    *
    * @param[in]  ctx   The application context.
    * @param[in]  prov  The provider instance.
    * @param[in]  reg   The application registration data.
    */
   void (*dumpState)(ToolsAppCtx *ctx, struct ToolsAppProvider *prov, gpointer reg);
} ToolsAppProvider;


/**
 * Defines a "app-specific" registration. The array contains data specific
 * to an "application provider" implementation.
 *
 * When the service is shutting down, if the @a data field is not NULL, the
 * array instance will be freed, including its backing element array.
 * See the documentation for g_array_free(). This will happen only after any
 * plugin's shutdown callback is called, so plugins have a chance of performing
 * custom clean up of this data.
 */
typedef struct ToolsAppReg {
   ToolsAppType   type;
   GArray        *data;
} ToolsAppReg;


/**
 * Defines a property that is exposed through the containers instance object
 * (ToolsAppCtx::serviceObj). Plugins can expose properties through this
 * mechanism to share state with other plugins. Properties can be accessed
 * using GObject's g_set_property / g_get_property APIs, or monitored by
 * registering for the "notify" signal on the object.
 *
 * All properties exposed using this mechanism are opaque pointers. It's up
 * for individual plugins to define the actual types of the properties.
 *
 * Properties are not ref counted, so consumers (code calling "g_get_property")
 * should be aware of the life cycle of the property as defined by the producer.
 */
typedef struct ToolsServiceProperty {
   const char    *name;
} ToolsServiceProperty;


/**
 * Defines a struct for mapping callbacks to signals. Normally it would suffice
 * to use g_signal_connect() directly to register interest in signals; but to
 * allow dynamic registration of signals by plugins, using this struct allows
 * registration to be delayed until all plugins have been loaded and have had
 * the chance to register their own signals. The daemon code then can go
 * through the plugins' registration data and connect all desired signals.
 */

typedef struct ToolsPluginSignalCb {
   const gchar   *signame;
   gpointer       callback;
   gpointer       clientData;
} ToolsPluginSignalCb;


/**
 * The registration data for an application. This gives the service information
 * about all functionality exported by the application, and any events that the
 * application may be interested in.
 *
 * When the plugin is shut down, if the @a regs field is not NULL, it (and its
 * element array) are freed with g_array_free().
 *
 * Plugins shouldn't try to free the returned structure, since the container
 * will try to use it even after the shutdown signal is sent to plugins. The
 * pointer should either point to statically allocated memory, or be leaked
 * (which should't be a problem since it will only be really leaked when the
 * process is shutting down).
 */
typedef struct ToolsPluginData {
   /** Name of the application (required). */
   char const                *name;
   /**
    * List of features provided by the app. Registration of applications
    * happens in the same order provided by this array.
    */
   GArray                    *regs;
   /**
    * Callback for registration errors. Whenever an entry provided in the #regs
    * array fails to be registered, this function, if provided, will be called.
    *
    * The plugin has two options when this callback fires:
    *
    * . adjust its internal state so that the failed registration doesn't
    *   affect the normal operation of the other functions provided by the
    *   plugin, and return TRUE.
    *
    * . return FALSE so that the container does not try to register any more
    *   features of the plugin.
    *
    * Note that in either case, registrations that succeeded will not be
    * reverted, so the plugin should still make sure that those still work,
    * event if just in a "disabled" state. The plugin will not be unloaded when
    * these errors happen.
    *
    * Plugins can use the property of the #regs field that registration will
    * happen in the order returned by the plugin's "on load" callback to have
    * some control about what features to enable / disable.
    *
    * @param[in]  ctx      The application context.
    * @param[in]  type     Type of the app that failed to register.
    * @param[in]  data     App-specific data that failed to register. If NULL,
    *                      it means that no provider of the given type exists.
    * @param[in]  plugin   The plugin's registration data.
    *
    * @return See above. TRUE to continue registration, FALSE to stop.
    */
   gboolean (*errorCb)(ToolsAppCtx *ctx,
                       ToolsAppType type,
                       gpointer data,
                       struct ToolsPluginData *plugin);
   /** Private plugin data. */
   gpointer                   _private;
} ToolsPluginData;

/**
 * Definition for tagging functions to be exported in the plugin binary. Use this
 * to tag the plugin entry point function, and any other functions that the plugin
 * needs to export.
 */
#if defined(G_PLATFORM_WIN32)
#  define TOOLS_MODULE_EXPORT    VMTOOLS_EXTERN_C __declspec(dllexport)
#elif defined(GCC_EXPLICIT_EXPORT)
#  define TOOLS_MODULE_EXPORT    VMTOOLS_EXTERN_C __attribute__((visibility("default")))
#else
#  define TOOLS_MODULE_EXPORT    VMTOOLS_EXTERN_C
#endif

/**
 * Signature for the plugin entry point function. The function should be called
 * @a ToolsOnLoad, and be exported in the plugin binary (e.g., by tagging it
 * with TOOLS_MODULE_EXPORT).
 *
 * If the plugin wants to stay loaded, it always should return the registration
 * data, even if all it contains is the (mandatory) plugin name. Plugins which
 * return NULL will be unloaded before the service is started, so they shouldn't
 * modify the service state (for example, by adding callbacks to the service's
 * main loop).
 */
typedef ToolsPluginData *(*ToolsPluginOnLoad)(ToolsAppCtx *ctx);

/** @} */

#endif /* _VMWARE_TOOLS_PLUGIN_H_ */

  07070100000160000081A400000000000000000000000168225505000016A0000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/rpcdebug.h    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMWARE_TOOLS_RPCDEBUG_H_
#define _VMWARE_TOOLS_RPCDEBUG_H_

/**
 * @file rpcdebug.h
 *
 * Defines the public API for the "GuestRPC Debug Channel" implementation, and
 * the interface between the debug library and debug plugins.
 *
 * @addtogroup vmtools_debug
 * @{
 */

#include "vmware/tools/plugin.h"


/**
 * Shorthand macro to both call CU_ASSERT() and return from the
 * function if the assertion fails. Note that this file doesn't
 * include CUnit.h, so you'll need to include that header to use
 * this macro.
 */
#define RPCDEBUG_ASSERT(test, retval) do {   \
   CU_ASSERT(test);                          \
   g_return_val_if_fail(test, retval);       \
} while (0)


struct RpcDebugPlugin;

/**
 * Signature for the plugin's "receive" function, to validate the data
 * applications send using RpcChannel_Send.
 */
typedef gboolean (*RpcDebugRecvFn)(char *data,
                                   size_t dataLen,
                                   char **result,
                                   size_t *resultLen);

/** Defines a mapping between a message and a "receive" function. */
typedef struct RpcDebugRecvMapping {
   gchar            *name;
   RpcDebugRecvFn    recvFn;
   /**
    * If not NULL, should be a xdrproc_t function for deserializing the data
    * in the received message.
    */
   gpointer          xdrProc;
   /** If xdrProc is provided, should be the size of the structure to allocate. */
   size_t            xdrSize;
} RpcDebugRecvMapping;


/**
 * Signature for validation functions. Validation functions are called after
 * an application has processed an "incoming" RPC, so that the plugin can
 * validate the response.
 */
typedef gboolean (*RpcDebugValidateFn)(RpcInData *data,
                                       gboolean ret);

/** Defines a mapping between a message and a "validate" function. */
typedef struct RpcDebugMsgMapping {
   gchar                  *message;
   size_t                  messageLen;
   RpcDebugValidateFn      validateFn;
   gboolean                freeMsg;
} RpcDebugMsgMapping;

/** Defines a (NULL-terminated) list of message / validator mappings. */
typedef struct RpcDebugMsgList {
   RpcDebugMsgMapping     *mappings;
   size_t                  index;
} RpcDebugMsgList;


/**
 * Signature for the plugin's "send" function, which provides the data
 * to be sent when the service tries to read from the RPC Channel.
 *
 * The function should return FALSE if the service should finish the
 * test (any data provided when this function returns FALSE is ignored).
 */
typedef gboolean (*RpcDebugSendFn)(RpcDebugMsgMapping *rpcdata);

/** Signature for the plugin's "shutdown" function. */
typedef void (*RpcDebugShutdownFn)(ToolsAppCtx *ctx,
                                   struct RpcDebugPlugin *plugin);

/**
 * Registration data for debug plugins, should be returned by the plugin's
 * entry point function.
 */
typedef struct RpcDebugPlugin {
   /** Maps "incoming" RPCs to specific receive functions. NULL-terminated. */
   RpcDebugRecvMapping *recvFns;
   /**
    * Default receive function for when no mapping matches the incoming command.
    * May be NULL.
    */
   RpcDebugRecvFn       dfltRecvFn;
   /** Send function. */
   RpcDebugSendFn       sendFn;
   /** Shutdown function. */
   RpcDebugShutdownFn   shutdownFn;
   /** Plugin data that debug plugins can also export. */
   ToolsPluginData     *plugin;
} RpcDebugPlugin;


/**
 * Signature for the plugin's entry point. The function works in a similar
 * way to the "ToolsOnLoad" function for regular plugins.
 */
typedef RpcDebugPlugin *(*RpcDebugOnLoadFn)(ToolsAppCtx *ctx);

struct RpcDebugLibData;

/**
 * Describes the external interface of the library. An instance of this struct
 * is returned by RpcDebug_Initialize() and can be used by applications using
 * the library to use the debugging functionality.
 */
typedef struct RpcDebugLibData {
   RpcChannel *    (*newDebugChannel)  (ToolsAppCtx *,
                                        struct RpcDebugLibData *);
   int             (*run)              (ToolsAppCtx *,
                                        gpointer runMainLoop,
                                        gpointer runData,
                                        struct RpcDebugLibData *);
   RpcDebugPlugin   *debugPlugin;
} RpcDebugLibData;

/** Signature of the library's initialization function. */
typedef RpcDebugLibData *(* RpcDebugInitializeFn)(ToolsAppCtx *, gchar *);


G_BEGIN_DECLS

void
RpcDebug_DecRef(ToolsAppCtx *ctx);

void
RpcDebug_IncRef(void);

RpcDebugLibData *
RpcDebug_Initialize(ToolsAppCtx *ctx,
                    gchar *dbgPlugin);

gboolean
RpcDebug_SendNext(RpcDebugMsgMapping *rpcdata,
                  RpcDebugMsgList *list);

void
RpcDebug_SetResult(const char *str,
                   char **res,
                   size_t *len);

G_END_DECLS

/** @} */

#endif /* _VMWARE_TOOLS_RPCDEBUG_H_ */

07070100000161000081A40000000000000000000000016822550500002040000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/threadPool.h  /*********************************************************
 * Copyright (C) 2010-2019,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _THREADPOOL_H_
#define _THREADPOOL_H_

/**
 * @file threadPool.h
 *
 * Public interface for vmtoolsd's thread pool.
 *
 * @defgroup vmtools_threads  Threading
 * @brief Thread Pooling and Monitoring
 * @{
 *
 * vmtoolsd provides a worker thread pool for use by plugins. This pool is
 * shared among all plugins, and is configurable from the Tools config file.
 * Plugins can submit tasks to the thread pool by using one of the inline
 * functions declared in this header.
 *
 * The thread pool is a shared resource, so code whose execution time may be
 * very long might want to, instead, create a dedicated thread for execution.
 * The shared thread pool also provides a facility to more easily do that,
 * with the lifecycle of the new thread managed by the thread pool so that it
 * is properly notified of service shutdown.
 *
 * Finally, depending on the configuration, the shared thread pool might not
 * be a thread pool at all: if the configuration has disabled threading, tasks
 * destined to the shared thread pool will be executed on the main service
 * thread.
 */

/*
 * glib-object.h should not be placed inside `extern "C"' blocks.
 * However, this header is often placed inside such blocks.
 * Here we change back into C++ for glib-object.h
 */
#ifdef __cplusplus
extern "C++" {
#endif
#include <glib-object.h>
#ifdef __cplusplus
}
#endif

#include "vmware/tools/plugin.h"

#define TOOLS_CORE_PROP_TPOOL "tcs_prop_thread_pool"

/** Type of callback function used to register tasks with the pool. */
typedef void (*ToolsCorePoolCb)(ToolsAppCtx *ctx,
                                gpointer data);

/**
 * @brief Public interface of the shared thread pool.
 *
 * This struct is published in the service's TOOLS_CORE_PROP_TPOOL property,
 * and contains function pointers to the internal implementation of the
 * thread pool's functions. In general, applications may prefer to use the
 * inline functions provided below instead, since they take care of some of
 * the boilerplate code.
 */
typedef struct ToolsCorePool {
   guint (*submit)(ToolsAppCtx *ctx,
                   ToolsCorePoolCb cb,
                   gpointer data,
                   GDestroyNotify dtor);
   void (*cancel)(guint id);
   gboolean (*start)(ToolsAppCtx *ctx,
                     const gchar *threadName,
                     ToolsCorePoolCb cb,
                     ToolsCorePoolCb interrupt,
                     gpointer data,
                     GDestroyNotify dtor);
} ToolsCorePool;


/*
 *******************************************************************************
 * ToolsCorePool_GetPool --                                               */ /**
 *
 * @brief Returns the thread pool instance for the service.
 *
 * @param[in] ctx Application context.
 *
 * @return The thread pool instance, or NULL if it's not available.
 *
 *******************************************************************************
 */

static inline ToolsCorePool *
ToolsCorePool_GetPool(ToolsAppCtx *ctx)
{
   ToolsCorePool *pool = NULL;
   g_object_get(ctx->serviceObj, TOOLS_CORE_PROP_TPOOL, &pool, NULL);
   return pool;
}


/*
 *******************************************************************************
 * ToolsCorePool_SubmitTask --                                            */ /**
 *
 * @brief Submits a task for execution in the thread pool.
 *
 * The task is queued in the thread pool and will be executed as soon as a
 * worker thread is available. If the thread pool is disabled, the task will
 * be executed on the main service thread as soon as the main loop is idle.
 *
 * The task data's destructor will be called after the task finishes executing,
 * or in case the thread pool is destroyed before the task is executed.
 *
 * @param[in] ctx    Application context.
 * @param[in] cb     Function to execute the task.
 * @param[in] data   Opaque data for the task.
 * @param[in] dtor   Destructor for the task data.
 *
 * @return An identifier for the task, or 0 on error.
 *
 *******************************************************************************
 */

static inline guint
ToolsCorePool_SubmitTask(ToolsAppCtx *ctx,
                         ToolsCorePoolCb cb,
                         gpointer data,
                         GDestroyNotify dtor)
{
   ToolsCorePool *pool = ToolsCorePool_GetPool(ctx);
   if (pool != NULL) {
      return pool->submit(ctx, cb, data, dtor);
   }
   return 0;
}


/*
 *******************************************************************************
 * ToolsCorePool_CancelTask --                                            */ /**
 *
 * @brief Cancels a task previously submitted to the pool.
 *
 * If the task is currently being executed, this function does nothing.
 * Otherwise, the task is removed from the task queue, and its destructor
 * (if any) is called.
 *
 * @param[in] ctx    Application context.
 * @param[in] taskId Task ID returned by ToolsCorePool_SubmitTask().
 *
 *******************************************************************************
 */

static inline void
ToolsCorePool_CancelTask(ToolsAppCtx *ctx,
                         guint taskId)
{
   ToolsCorePool *pool = ToolsCorePool_GetPool(ctx);
   if (pool != NULL) {
      pool->cancel(taskId);
   }
}


/*
 *******************************************************************************
 * ToolsCorePool_StartThread --                                           */ /**
 *
 * @brief Starts a task on its own thread.
 *
 * This function will run a task on a dedicated thread that is not part of
 * the shared thread pool. The thread will be managed by the thread pool, so
 * that it's properly cleaned up when the service is shutting down.
 *
 * Threads started by this function cannot be stopped by using the cancel
 * function. Instead, if the application itself wants to stop the thread, it
 * should call the interrupt function it provided to the thread pool, or use
 * some other method of communicating with the thread.
 *
 * @param[in] ctx          Application context.
 * @param[in] threadName   Name for the new thread.
 * @param[in] cb           Function that implements the task to execute.
 * @param[in] interrupt    A function that will request the task to be
 *                         interrupted. This will be called when the pool
 *                         needs to stop all managed threads (e.g. during
 *                         service shutdown). The task should stop what it's
 *                         doing and end the thread soon after this callback
 *                         is fired.
 * @param[in] data         Opaque data for both task callback and interrupt
 *                         functions.
 * @param[in] dtor         Destructor for the task data.
 *
 * @return TRUE iff thread was successfully started.
 *
 *******************************************************************************
 */

static inline gboolean
ToolsCorePool_StartThread(ToolsAppCtx *ctx,
                          const gchar *threadName,
                          ToolsCorePoolCb cb,
                          ToolsCorePoolCb interrupt,
                          gpointer data,
                          GDestroyNotify dtor)
{
   ToolsCorePool *pool = ToolsCorePool_GetPool(ctx);
   if (pool != NULL) {
      return pool->start(ctx, threadName, cb, interrupt, data, dtor);
   }
   return FALSE;
}

/** @} */

#endif /* _THREADPOOL_H_ */

07070100000162000081A400000000000000000000000168225505000013F2000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/utils.h   /*********************************************************
 * Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMWARE_TOOLS_UTILS_H_
#define _VMWARE_TOOLS_UTILS_H_

/**
 * @file utils.h
 *
 *    Public functions from the VMTools shared library, and other definitions.
 *
 * @defgroup vmtools_utils Utility Functions
 * @{
 *
 * @brief A collection of useful functions.
 *
 * This module contains functions for loading configuration data and extensions
 * to the glib API that are useful when writing applications.
 *
 */

#define  VMTOOLS_GUEST_SERVICE   "vmsvc"
#define  VMTOOLS_USER_SERVICE    "vmusr"

#if defined(__cplusplus)
#  define VMTOOLS_EXTERN_C extern "C"
#else
#  define VMTOOLS_EXTERN_C
#endif

#include <glib.h>
#if defined(G_PLATFORM_WIN32)
#  include <windows.h>
#else
#  include <signal.h>
#  include <sys/time.h>
#endif

#ifndef ABS
#  define ABS(x) (((x) >= 0) ? (x) : -(x))
#endif


/**
 * Converts an UTF-8 path to the local (i.e., glib) file name encoding.
 * This is a no-op on Windows, since the local encoding is always UTF-8
 * in glib. The returned value should not be freed directly; instead,
 * use VMTOOLS_RELEASE_FILENAME_LOCAL.
 *
 * @param[in]  path  Path in UTF-8 (should not be NULL).
 * @param[out] err   Where to store errors (type: GError **; may be NULL).
 *
 * @return The path in glib's filename encoding, or NULL on error.
 */

#if defined(G_PLATFORM_WIN32)
#  define VMTOOLS_GET_FILENAME_LOCAL(path, err) (gchar *) (path)
#else
#  define VMTOOLS_GET_FILENAME_LOCAL(path, err) g_filename_from_utf8((path),  \
                                                                     -1,      \
                                                                     NULL,    \
                                                                     NULL,    \
                                                                     (err))
#endif

/**
 * Frees a path allocated with VMTOOLS_GET_FILENAME_LOCAL. No-op on Windows.
 *
 * @param[in]  path  Path in UTF-8.
 */

#if defined(G_PLATFORM_WIN32)
#  define VMTOOLS_RELEASE_FILENAME_LOCAL(path)   (void) (path)
#else
#  define VMTOOLS_RELEASE_FILENAME_LOCAL(path)   g_free(path)
#endif

/** Convenience macro around VMTools_WrapArray. */
#define VMTOOLS_WRAP_ARRAY(a) VMTools_WrapArray((a), sizeof *(a), G_N_ELEMENTS(a))


G_BEGIN_DECLS

void
vm_free(void *ptr);

gboolean
VMTools_LoadConfig(const gchar *path,
                   GKeyFileFlags flags,
                   GKeyFile **config,
                   time_t *mtime);

gboolean
VMTools_AddConfig(GKeyFile *srcConfig,
                  GKeyFile *dstConfig);

gboolean
VMTools_CompareConfig(GKeyFile *config1,
                      GKeyFile *config2);

gboolean
VMTools_WriteConfig(const gchar *path,
                    GKeyFile *config,
                    GError **err);

gboolean
VMTools_ChangeLogFilePath(const gchar *delimiter,
                          const gchar *appendString,
                          const gchar *domain,
                          GKeyFile *conf);

gboolean
VMTools_ConfigGetBoolean(GKeyFile *config,
                         const gchar *section,
                         const gchar *key,
                         const gboolean defValue);

gint
VMTools_ConfigGetInteger(GKeyFile *config,
                         const gchar *section,
                         const gchar *key,
                         const gint defValue);

gchar *
VMTools_ConfigGetString(GKeyFile *config,
                        const gchar *section,
                        const gchar *key,
                        const gchar *defValue);

#if defined(G_PLATFORM_WIN32)

gboolean
VMTools_AttachConsole(void);

GSource *
VMTools_NewHandleSource(HANDLE h);

#else

/** Type of callback used by the signal event source. */
typedef gboolean (*SignalSourceCb)(const siginfo_t *, gpointer);

GSource *
VMTools_NewSignalSource(int signum);

gchar *
VMTools_GetLibdir(void);

#endif

GSource *
VMTools_CreateTimer(gint timeout);

void
VMTools_AcquireLogStateLock(void);

void
VMTools_ReleaseLogStateLock(void);

gchar *
VMTools_GetTimeAsString(void);

void
VMTools_SuspendLogIO(void);

void
VMTools_ResumeLogIO(void);

GArray *
VMTools_WrapArray(gconstpointer data,
                  guint elemSize,
                  guint count);

G_END_DECLS

/** @} */

#endif /* _VMWARE_TOOLS_UTILS_H_ */

  07070100000163000081A4000000000000000000000001682255050000066B000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmware/tools/vmbackup.h    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMBACKUP_H_
#define _VMBACKUP_H_

/**
 * @file vmbackup.h
 *
 * Public interface of the "vmbackup" plugin. Defines events generated by the
 * plugin that other code can monitor.
 */

/**
 * Signal sent when access to disk resources has been affected in some manner
 * (e.g., disks have been frozen in preparation for a snapshot). Plugins that
 * access the disk should listen for this signal and avoid performing disk
 * operations while I/O is frozen.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      ToolsAppCtx *: The application context.
 * @param[in]  freeze   TRUE if I/O is being frozen, FALSE otherwise.
 * @param[in]  data     Client data.
 */
#define TOOLS_CORE_SIG_IO_FREEZE    "tcs_io_freeze"

#endif /* _VMBACKUP_H_ */

 07070100000164000081A4000000000000000000000001682255050000175C000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vmxrpc.h   /*********************************************************
 * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmxrpc.h --
 *
 *    VMware extensions to XDR types. Wrappers to allow the use of types declared
 *    in vm_basic_types.h in structures.
 */

#ifndef _VMXRPC_H_
#define _VMXRPC_H_

#include <rpc/types.h>
#include <rpc/xdr.h>
#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * glibc and Solaris headers seem to define functions for unsigned types with
 * slightly different names than all other platforms. Provide macros to
 * translate the names to the more common ones.
 */
#if defined(__GLIBC__) || defined(sun)
#  define xdr_u_int16_t(x, i)    xdr_uint16_t(x, i)
#  define xdr_u_int32_t(x, i)    xdr_uint32_t(x, i)
#  define xdr_u_int64_t(x, i)    xdr_uint64_t(x, i)
#endif


/* Wrapper around xdr_free that does casting automatically. */
#define VMX_XDR_FREE(func, data) xdr_free((xdrproc_t)(func), (char *)(data))


/*
 *-----------------------------------------------------------------------------
 *
 * xdr_int8 --
 *
 *    XDR function for encoding/decoding int8.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE bool_t
xdr_int8(XDR *xdrs,  // IN
         int8 *ip)   // IN/OUT
{
   /* XDR doesn't seem to have a "signed char" representation. */
   return xdr_char(xdrs, (char*)ip);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xdr_uint8 --
 *
 *    XDR function for encoding/decoding uint8.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE bool_t
xdr_uint8(XDR *xdrs, // IN
          uint8 *ip) // IN/OUT
{
   return xdr_u_char(xdrs, ip);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xdr_int16 --
 *
 *    XDR function for encoding/decoding int16.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE bool_t
xdr_int16(XDR *xdrs, // IN
          int16 *ip) // IN/OUT
{
   return xdr_int16_t(xdrs, ip);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xdr_uint16 --
 *
 *    XDR function for encoding/decoding uint16.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE bool_t
xdr_uint16(XDR *xdrs,   // IN
           uint16 *ip)  // IN/OUT
{
   return xdr_u_int16_t(xdrs, ip);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xdr_int32 --
 *
 *    XDR function for encoding/decoding int32.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE bool_t
xdr_int32(XDR *xdrs, // IN
          int32 *ip) // IN/OUT
{
   return xdr_int32_t(xdrs, ip);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xdr_uint32 --
 *
 *    XDR function for encoding/decoding uint32.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE bool_t
xdr_uint32(XDR *xdrs,   // IN
           uint32 *ip)  // IN/OUT
{
   return xdr_u_int32_t(xdrs, ip);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xdr_int64 --
 *
 *    XDR function for encoding/decoding int64.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE bool_t
xdr_int64(XDR *xdrs, // IN
          int64 *ip) // IN/OUT
{
   return xdr_int64_t(xdrs, ip);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xdr_uint64 --
 *
 *    XDR function for encoding/decoding uint64.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE bool_t
xdr_uint64(XDR *xdrs,   // IN
           uint64 *ip)  // IN/OUT
{
   return xdr_u_int64_t(xdrs, ip);
}


/* See vm_basic_types.h. X defines a different Bool. */
#if !defined(__STRICT_ANSI__) || defined(__FreeBSD__)
/*
 *-----------------------------------------------------------------------------
 *
 * xdr_Bool --
 *
 *    XDR function for encoding/decoding Bool.
 *
 * Results:
 *    Whether the call succeeded.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */
static INLINE bool_t
xdr_Bool(XDR *xdrs,  // IN
         Bool *ip)   // IN/OUT
{
   return xdr_char(xdrs, ip);
}
#endif

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _VMXRPC_H_ */

07070100000165000081A4000000000000000000000001682255050000115B000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/vthreadBase.h  /*********************************************************
 * Copyright (C) 2006-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vthreadBase.h --
 *
 *	Subset of vthread defines that are used by libs that need to make
 *      vthread calls but don't actually do any vthreading.
 *
 *      May be used without lib/thread or with lib/thread.  (But don't try
 *      to do both simultaneously, since lib/thread needs to do more
 *      bookkeeping.)
 */

#ifndef VTHREAD_BASE_H
#define VTHREAD_BASE_H

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_atomic.h"

#if !defined VMM

/*
 * Most major OSes now support __thread, so begin making TLS access via
 * __thread the common case. If VMW_HAVE_TLS is defined, __thread may
 * be used.
 *
 * Linux: since glibc-2.3
 * Windows: since Vista and vs2005 via __declspec(thread)
 *          (Prior to Vista, __declspec(thread) was ignored when
 *           a library is loaded via LoadLibrary / delay-load)
 * macOS: since 10.7 via clang (xcode-4.6)
 * iOS: 64-bit since 8.0, 32-bit since 9.0 (per llvm commit)
 * watchOS: since 2.0
 * Android: since NDKr12 (June 2016, per NDK wiki)
 * FreeBSD and Solaris: "a long time", gcc-4.1 was needed.
 */
#if defined __ANDROID__
   /* No modern NDK currently in use, no macro known */
#elif defined __APPLE__
   /* macOS >= 10.7 tested. iOS >= {8.0,9.0} NOT tested */
#  if __MAC_OS_X_VERSION_MIN_REQUIRED+0 >= 1070
#     define VMW_HAVE_TLS
#  elif  (defined __LP64__ && __IPHONE_OS_VERSION_MIN_REQUIRED+0 >= 80000) || \
         (!defined __LP64__ && __IPHONE_OS_VERSION_MIN_REQUIRED+0 >= 90000)
#     define VMW_HAVE_TLS
#  endif
#else
   /* All other platforms require new enough version to support TLS */
#  define VMW_HAVE_TLS
#endif

#endif /* VMM */

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Types
 */

typedef uintptr_t VThreadID;

#define VTHREAD_INVALID_ID    (VThreadID)(0)

#ifdef VMM
/*
 *-----------------------------------------------------------------------------
 *
 * VThread_CurID --
 * VThread_CurName --
 *
 *      Get the current thread ID / name. This is only inline for the monitor.
 *
 *      The extern symbols herein are defined in monitor.c and
 *      initialized by SharedArea_PowerOn.
 *
 * Results:
 *      Thread ID / name.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE VThreadID
VThread_CurID(void)
{
   extern const VThreadID vthreadCurID;

   return vthreadCurID;
}

static INLINE const char *
VThread_CurName(void)
{
   extern const char vcpuThreadName[];

   return vcpuThreadName;
}

#else

#define VTHREADBASE_MAX_NAME    32  /* Arbitrary */


/* Common VThreadBase functions */
const char *VThreadBase_CurName(void);
VThreadID VThreadBase_CurID(void);
void VThreadBase_SetName(const char *name);
void VThreadBase_SetNamePrefix(const char *prefix);

/* For implementing a thread library */
void VThreadBase_ForgetSelf(void);

/* Match up historical VThread_ names with VThreadBase_ names */
static INLINE const char *
VThread_CurName(void)
{ return VThreadBase_CurName(); }

static INLINE VThreadID
VThread_CurID(void)
{ return VThreadBase_CurID(); }

static INLINE void
VThread_SetName(const char *name)
{ VThreadBase_SetName(name); }


#ifdef _WIN32
static INLINE Bool
VThreadBase_IsInSignal(void)
{
   /* Win32 does not worry about async-signal-safety. */
   return FALSE;
}
#else
Bool VThreadBase_IsInSignal(void);
void VThreadBase_SetIsInSignal(Bool isInSignal);
#endif

uint64 VThreadBase_GetKernelID(void);

#endif /* VMM */

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // VTHREAD_BASE_H
 07070100000166000081A40000000000000000000000016822550500000DF0000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/include/wiper.h    /*********************************************************
 * Copyright (C) 2004-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * wiper.h --
 *
 *      Library for wiping a virtual disk.
 *
 */

#ifndef _WIPER_H_
# define _WIPER_H_

#if defined(_WIN32) && defined(_MSC_VER)
#include <windows.h>
#endif

#include "vm_basic_types.h"
#include "dbllnklst.h"

#if defined(__cplusplus)
extern "C" {
#endif

#define TOOLS_WIPE_CANCEL "Wipe cancelled by user.\n"

typedef enum {
   PARTITION_UNSUPPORTED = 0,
   PARTITION_EXT2,
   PARTITION_EXT3,
   PARTITION_REISERFS,
   PARTITION_NTFS,
   PARTITION_FAT,
   PARTITION_UFS,
   PARTITION_PCFS,
   PARTITION_EXT4,
   PARTITION_HFS,
   PARTITION_ZFS,
   PARTITION_XFS,
   PARTITION_BTRFS,
} WiperPartition_Type;

/* Max size of a path */
#define NATIVE_MAX_PATH 256
#define MAX_WIPER_FILE_SIZE (2 << 30)   /* The maximum wiper file size in bytes */

typedef struct WiperPartition {
   unsigned char mountPoint[NATIVE_MAX_PATH];

   /* Type of the partition */
   WiperPartition_Type type;

   /* Filesystem name - testing */
   const char *fsName;

   /* Filesystem type (name) */
   const char *fsType;

   /*
    * Clients should specifically set this flag to TRUE to enable free space
    * reclamation using unmaps.
    */
   Bool attemptUnmaps;

   /*
    * NULL if type is not PARTITION_UNSUPPORTED, otherwise describes
    * why the partition can not be wiped.
    */
   const char *comment;

#if defined(_WIN32)
   /* Private flags used by the Win32 implementation */
   DWORD flags;
#endif

   DblLnkLst_Links link;
} WiperPartition;

typedef struct WiperPartition_List {
   DblLnkLst_Links link;
} WiperPartition_List;

typedef struct WiperInitData {
#if defined(_WIN32)
   HINSTANCE resourceModule;
#endif
} WiperInitData;

Bool Wiper_Init(WiperInitData *clientData);
Bool WiperPartition_Open(WiperPartition_List *pl, Bool shrinkableOnly);
void WiperPartition_Close(WiperPartition_List *pl);

WiperPartition *WiperSinglePartition_Allocate(void);
WiperPartition *WiperSinglePartition_Open(const char *mntpt, Bool shrinkableOnly);
void WiperSinglePartition_Close(WiperPartition *);
Bool Wiper_IsWipeSupported(const WiperPartition *);

unsigned char *WiperSinglePartition_GetSpace(const WiperPartition *p,
                                             uint64 *avail,
                                             uint64 *free,
                                             uint64 *total);

/* External definition of the wiper state */
struct Wiper_State;
typedef struct Wiper_State Wiper_State;

Wiper_State *Wiper_Start(const WiperPartition *p, unsigned int maxWiperFileSize);

unsigned char *Wiper_Next(Wiper_State **s, unsigned int *progress);
unsigned char *Wiper_Cancel(Wiper_State **s);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _WIPER_H_ */
07070100000167000081A400000000000000000000000168225505000028A0000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/include/x86_basic_defs.h   /*********************************************************
 * Copyright (c) 2006-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * x86_basic_defs.h --
 *
 *      Basic macros describing the x86 architecture.
 */

#ifndef _X86_BASIC_DEFS_H_
#define _X86_BASIC_DEFS_H_

#define INCLUDE_ALLOW_USERLEVEL

#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
#include "includeCheck.h"

#define X86_MAX_INSTR_LEN  15   /* Max byte length of an x86 instruction */

#define NUM_IDT_VECTORS 256

/*
 * Control registers
 */

#define CR0_PE         0x00000001
#define CR0_MP         0x00000002
#define CR0_EM         0x00000004
#define CR0_TS         0x00000008
#define CR0_ET         0x00000010
#define CR0_NE         0x00000020
#define CR0_WP         0x00010000
#define CR0_AM         0x00040000
#define CR0_NW         0x20000000
#define CR0_CD         0x40000000
#define CR0_PG         0x80000000

#define CR0_CACHE_CONTROL (CR0_CD | CR0_NW)

#define CR0_RESERVED   CONST64U(0xffffffff1ffaffc0)
/*
 * Note: The "Not Reserved" bits in CR0 are:
 *   PG, CD, NW, AM, WP, NE, ET, TS, EM, MP, PE
 *        |   |   |               |   |   |
 *        |   |   +---------------+---+---+---> CR0_MUTABLE
 *        |   |
 *        +---+--> CR0_CACHE_CONTROL
 *
 * (CR0_MUTABLE is defined in vmkernel/private/x86/cpu.h)
 */

#define CR3_PWT        0x00000008
#define CR3_PCD        0x00000010
#define CR3_PDB_SHIFT  12
#define CR3_PDB_MASK   0xfffff000
#define CR3_IGNORE     0xFFF
#define PAE_CR3_IGNORE 0x1F
#ifndef CR3_PCID_MASK
#define CR3_PCID_MASK  0xFFF
#endif

#define CR3_LAM_U57    (1ULL << 61)
#define CR3_LAM_U48    (1ULL << 62)
#define CR3_LAM_USER   (CR3_LAM_U48 | CR3_LAM_U57)
#define CR3_NO_FLUSH   (1ULL << 63)

#define CR4_VME        0x00000001
#define CR4_PVI        0x00000002
#define CR4_TSD        0x00000004
#define CR4_DE         0x00000008
#define CR4_PSE        0x00000010
#define CR4_PAE        0x00000020
#define CR4_MCE        0x00000040
#define CR4_PGE        0x00000080
#define CR4_PCE        0x00000100
#define CR4_OSFXSR     0x00000200 // CPU/OS supports SIMD insts
#define CR4_OSXMMEXCPT 0x00000400 // #XF exception enable PIII only
#define CR4_UMIP       0x00000800
#define CR4_LA57       0x00001000
#define CR4_VMXE       0x00002000
#define CR4_SMXE       0x00004000
#define CR4_FSGSBASE   0x00010000
#define CR4_PCIDE      0x00020000
#define CR4_OSXSAVE    0x00040000
#define CR4_SMEP       0x00100000
#define CR4_SMAP       0x00200000
#define CR4_PKE        0x00400000
#define CR4_CET        0x00800000
#define CR4_PKS        0x01000000
#define CR4_UINTR      0x02000000
#define CR4_LASS       0x08000000
#define CR4_LAM_SUP    0x10000000
#define CR4_RESERVED   CONST64U(0xffffffffee089000)
#define CR8_RESERVED   CONST64U(0xfffffffffffffff0)

/*
 * Debug registers
 */

#define DR_COUNT       4

#define DR6_B0         0x00000001
#define DR6_B1         0x00000002
#define DR6_B2         0x00000004
#define DR6_B3         0x00000008
#define DR6_B0123      (DR6_B0 | DR6_B1 | DR6_B2 | DR6_B3)
#define DR6_B(_n)      (1 << (_n))
#define DR6_BD         0x00002000
#define DR6_BS         0x00004000
#define DR6_BT         0x00008000
#define DR6_RTM        0x00010000
#define DR6_ONES       0xfffe0ff0
#define DR6_DEFAULT    (DR6_ONES | DR6_RTM)
#define DR6_RESERVED_MASK 0xfffe1ff0

#define DR7_L_MASK(_n)   (1 << ((_n) * 2))
#define DR7_G_MASK(_n)   (1 << ((_n) * 2 + 1))
#define DR7_LG_MASK(_n)  (3 << ((_n) * 2))
#define DR7_RW_MASK(_n)  (3 << (16 + (_n) * 4))
#define DR7_LEN_MASK(_n) (3 << (18 + (_n) * 4))
#define DR7_BP_MASK(_n) (DR7_L_MASK(_n)  |\
                         DR7_G_MASK(_n)  |\
                         DR7_RW_MASK(_n) |\
                         DR7_LEN_MASK(_n))

#define DR7_L0         DR7_L_MASK(0)
#define DR7_G0         DR7_G_MASK(0)
#define DR7_L1         DR7_L_MASK(1)
#define DR7_G1         DR7_G_MASK(1)
#define DR7_L2         DR7_L_MASK(2)
#define DR7_G2         DR7_G_MASK(2)
#define DR7_L3         DR7_L_MASK(3)
#define DR7_G3         DR7_G_MASK(3)
#define DR7_ENABLED    0x000000ff

#define DR7_LE         0x00000100    // Deprecated in modern hardware
#define DR7_GE         0x00000200    // Deprecated in modern hardware
#define DR7_GD         0x00002000
#define DR7_ONES       0x00000400
#define DR7_RTM        0x00000800
#define DR7_RESERVED   CONST64U(0xffffffff0000d400)
#define DR7_DEFUNCT    (DR7_LE | DR7_GE)
#define DR7_DEFAULT    DR7_ONES
#define DR7_LX_MASK    (DR7_L0 | DR7_L1 | DR7_L2 | DR7_L3 | DR7_LE)
#define DR7_GX_MASK    (DR7_G0 | DR7_G1 | DR7_G2 | DR7_G3 | DR7_GE)
#define DR7_LGX_MASK   (DR7_LX_MASK | DR7_GX_MASK)

#define DR7_RW(_r,_n)  (((_r) >> (16+(_n)*4)) & 0x3)
#define DR7_L(_r,_n)   (((_r) >> ((_n)*2)) & 1)
#define DR7_G(_r,_n)   (((_r) >> (1 + (_n)*2)) & 1)
#define DR7_LEN(_r,_n) (((_r) >> (18+(_n)*4)) & 0x3)

#define DR7_RW_BITS(_n,_rw)     ((_rw) << (16 + (_n) * 4))
#define DR7_LEN_BITS(_n,_len)   ((_len) << (18 + (_n) * 4))

#define DR7_RW_INST    0x0
#define DR7_RW_WRITES  0x1
#define DR7_RW_IO      0x2
#define DR7_RW_ACCESS  0x3

#define DR7_LENGTH_1   0x0
#define DR7_LENGTH_2   0x1
#define DR7_LENGTH_8   0x2
#define DR7_LENGTH_4   0x3

#define DEBUG_STATUS_B0   (1<<0)
#define DEBUG_STATUS_B1   (1<<1)
#define DEBUG_STATUS_B2   (1<<2)
#define DEBUG_STATUS_B3   (1<<3)
#define DEBUG_STATUS_DB   (1<<13)
#define DEBUG_STATUS_BS   (1<<14)
#define DEBUG_STATUS_BT   (1<<15)

/*
 * Exception vectors
 */
#define EXC_VEC_MIN       0
#define EXC_VEC_MAX      31

#define EXC_DE            0
#define EXC_DB            1
#define EXC_NMI           2
#define EXC_BP            3
#define EXC_OF            4
#define EXC_BR            5
#define EXC_UD            6
#define EXC_NM            7
#define EXC_DF            8
#define EXC_TS           10
#define EXC_NP           11
#define EXC_SS           12
#define EXC_GP           13
#define EXC_PF           14
#define EXC_MF           16
#define EXC_AC           17
#define EXC_MC           18
#define EXC_XF           19  // SIMD exception.
#define EXC_VE           20  // Virtualization exception - VT only.
#define EXC_CP           21  // Control Protection exception.
#define EXC_HV           28  // Hypervisor injection exception (SVM only)
#define EXC_VC           29  // VMM communication exception (SVM / SEV-ES only).
#define EXC_SX           30  // Security exception (SVM only).

/* Bitmap of the exception vectors that have associated error codes */
#define EXC_WITH_ERR_CODE_MASK ((1u << EXC_DF) | (1u << EXC_TS) | \
                                (1u << EXC_NP) | (1u << EXC_SS) | \
                                (1u << EXC_GP) | (1u << EXC_PF) | \
                                (1u << EXC_AC) | (1u << EXC_CP) | \
                                (1u << EXC_VC) | (1u << EXC_SX))

/*
 * eflag/rflag definitions
 */

#define EFLAGS_IOPL_SHIFT 12

typedef enum x86_FLAGS {
   EFLAGS_NONE         = 0,
   EFLAGS_CF           = (1 << 0),     /* User */
   EFLAGS_SET          = (1 << 1),
   EFLAGS_PF           = (1 << 2),     /* User */
   EFLAGS_AF           = (1 << 4),     /* User */
   EFLAGS_ZF           = (1 << 6),     /* User */
   EFLAGS_SF           = (1 << 7),     /* User */
   EFLAGS_TF           = (1 << 8),     /* Priv */
   EFLAGS_IF           = (1 << 9),     /* Priv */
   EFLAGS_DF           = (1 << 10),    /* User */
   EFLAGS_OF           = (1 << 11),    /* User */
   EFLAGS_NT           = (1 << 14),    /* Priv */
   EFLAGS_RF           = (1 << 16),    /* Priv */
   EFLAGS_VM           = (1 << 17),    /* Priv */
   EFLAGS_AC           = (1 << 18),    /* Priv */
   EFLAGS_VIF          = (1 << 19),    /* Priv */
   EFLAGS_VIP          = (1 << 20),    /* Priv */
   EFLAGS_ID           = (1 << 21),    /* Priv */

   EFLAGS_IOPL         = 3 << EFLAGS_IOPL_SHIFT,
   EFLAGS_ARITH        = (EFLAGS_CF | EFLAGS_PF | EFLAGS_AF | EFLAGS_ZF |
                          EFLAGS_SF | EFLAGS_OF),
   EFLAGS_USER         = (EFLAGS_CF | EFLAGS_PF | EFLAGS_AF | EFLAGS_ZF |
                          EFLAGS_SF | EFLAGS_DF | EFLAGS_OF),
   EFLAGS_PRIV         = (EFLAGS_TF  | EFLAGS_IF  | EFLAGS_IOPL | EFLAGS_NT  |
                          EFLAGS_RF  | EFLAGS_VM  | EFLAGS_AC   | EFLAGS_VIF |
                          EFLAGS_VIP | EFLAGS_ID),
   EFLAGS_ALL          = (EFLAGS_CF | EFLAGS_PF | EFLAGS_AF | EFLAGS_ZF |
                          EFLAGS_SF | EFLAGS_DF | EFLAGS_OF | EFLAGS_TF |
                          EFLAGS_IF | EFLAGS_IOPL | EFLAGS_NT | EFLAGS_RF |
                          EFLAGS_VM | EFLAGS_AC | EFLAGS_VIF | EFLAGS_VIP |
                          EFLAGS_ID),
   EFLAGS_ALL_16       = EFLAGS_ALL & 0xffff,
   EFLAGS_REAL_32      = (EFLAGS_ALL & ~(EFLAGS_VIP | EFLAGS_VIF | EFLAGS_VM)),
   EFLAGS_V8086_32     = (EFLAGS_ALL & ~(EFLAGS_VIP | EFLAGS_VIF |
                                         EFLAGS_VM  | EFLAGS_IOPL)),
   EFLAGS_REAL_16      = EFLAGS_REAL_32 & 0xffff,
   EFLAGS_V8086_16     = EFLAGS_V8086_32 & 0xffff,
   EFLAGS_CLEAR_ON_EXC = (EFLAGS_TF | EFLAGS_VM | EFLAGS_RF | EFLAGS_NT),
   EFLAGS__4           = 0x7fffffff    /* ensure 4 byte encoding */
} x86_FLAGS;

/*
 * MPX bound configuration registers
 */
#define BNDCFG_EN        0x00000001
#define BNDCFG_BNDPRSV   0x00000002
#define BNDCFG_RSVD      0x00000ffc
#define BNDCFG_BDBASE    CONST64U(0xfffffffffffff000)

/* Reset state of RIP */
#define RESET_RIP       0xfff0
#define RESET_RIP_TDX   0xfffffff0  /* Reset RIP for TDX protected mode boot. */

#endif // ifndef _VM_BASIC_DEFS_H_
07070100000168000081A4000000000000000000000001682255050002355E000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/x86cpuid.h /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef _X86CPUID_H_
#define _X86CPUID_H_

/* http://www.sandpile.org/ia32/cpuid.htm */

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMX

#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_MODULE
#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"
#include "community_source.h"
#include "x86vendor.h"
#include "vm_assert.h"

#if defined __cplusplus
extern "C" {
#endif


/*
 * 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;
   uint64 force8byteAlign[2]; /* See CpuidInfoNodePtr (needed on Apple Mac). */
} CPUIDRegsUnion;

/*
 * Results of calling cpuid(eax, ecx) on all host logical CPU.
 */
#ifdef _MSC_VER
// TODO: Move this under the push
#pragma warning (disable :4200) // non-std extension: zero-sized array in struct
#pragma warning (push)
#pragma warning (disable :4100) // unreferenced parameters
#endif

#if defined VMKERNEL || (!defined(__FreeBSD__) && !defined(__sun__))
/*
 * FreeBSD and Solaris do not support pragma pack until gcc-4.6,
 * but do not need these structures (which are part of vmmon).
 * Vmkernel sets __FreeBSD__ for a few files.
 */
#pragma pack(push, 1)
typedef 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
} CPUIDReply;

typedef struct CPUIDQuery {
   uint32 eax;                // IN
   uint32 ecx;                // IN
   uint32 numLogicalCPUs;     // IN/OUT
   CPUIDReply logicalCPUs[0]; // OUT
} CPUIDQuery;
#pragma pack(pop)
#endif

/*
 * Table of known CPUID levels.
 *
 * The first parameter defines whether the level has its default masks
 * generated from the values in this file. Any level which is marked as FALSE
 * here *must* have all monitor support types set to NA. A static assert in
 * lib/cpuidcompat/cpuidcompat.c will check this.
 *
 * The second parameter is the "short name" of the level. It's mainly used for
 * token concatenation in various macros.
 *
 * The third parameter is the actual numeric value of that level (the EAX input
 * value).
 *
 * The fourth parameter is a "subleaf count", where 0 means that ecx is
 * ignored, otherwise is the count of sub-leaves.
 *
 * The fifth parameter is the first hardware version that is *aware* of the
 * CPUID level (0 = existed since dawn of time), even though we may not expose
 * this level or parts of it to guest.
 */

/*            MASKS, LVL, VAL,      CNT, HWV */
#define CPUID_KNOWN_LEVELS                   \
   CPUIDLEVEL(TRUE,  0,   0x0,        0,  0) \
   CPUIDLEVEL(TRUE,  1,   0x1,        0,  0) \
   CPUIDLEVEL(FALSE, 2,   0x2,        0,  0) \
   CPUIDLEVEL(FALSE, 4,   0x4,        7,  0) \
   CPUIDLEVEL(FALSE, 5,   0x5,        0,  0) \
   CPUIDLEVEL(TRUE,  6,   0x6,        0,  0) \
   CPUIDLEVEL(TRUE,  7,   0x7,        3,  0) \
   CPUIDLEVEL(TRUE,  9,   0x9,        0, 17) \
   CPUIDLEVEL(FALSE, A,   0xa,        0,  0) \
   CPUIDLEVEL(FALSE, B,   0xb,        3,  0) \
   CPUIDLEVEL(TRUE,  D,   0xd,       19,  0) \
   CPUIDLEVEL(TRUE,  F,   0xf,        2, 13) \
   CPUIDLEVEL(TRUE,  10,  0x10,       4, 13) \
   CPUIDLEVEL(TRUE,  12,  0x12,       4, 13) \
   CPUIDLEVEL(TRUE,  14,  0x14,       2, 13) \
   CPUIDLEVEL(TRUE,  15,  0x15,       0, 13) \
   CPUIDLEVEL(TRUE,  16,  0x16,       0, 13) \
   CPUIDLEVEL(TRUE,  17,  0x17,       4, 14) \
   CPUIDLEVEL(TRUE,  18,  0x18,       8, 17) \
   CPUIDLEVEL(TRUE,  19,  0x19,       0, 20) \
   CPUIDLEVEL(TRUE,  1A,  0x1a,       0, 17) \
   CPUIDLEVEL(TRUE,  1B,  0x1b,       2, 17) \
   CPUIDLEVEL(TRUE,  1C,  0x1c,       0, 20) \
   CPUIDLEVEL(TRUE,  1D,  0x1d,       2, 19) \
   CPUIDLEVEL(TRUE,  1E,  0x1e,       1, 19) \
   CPUIDLEVEL(FALSE, 1F,  0x1f,       6, 17) \
   CPUIDLEVEL(TRUE,  20,  0x20,       1, 20) \
   CPUIDLEVEL(TRUE,  21,  0x21,       1, 20) \
   CPUIDLEVEL(TRUE,  23,  0x23,       4, 21) \
   CPUIDLEVEL(TRUE,  24,  0x24,       1, 22) \
   CPUIDLEVEL(FALSE, 400, 0x40000000, 0,  0) \
   CPUIDLEVEL(FALSE, 401, 0x40000001, 0,  0) \
   CPUIDLEVEL(FALSE, 402, 0x40000002, 0,  0) \
   CPUIDLEVEL(FALSE, 403, 0x40000003, 0,  0) \
   CPUIDLEVEL(FALSE, 404, 0x40000004, 0,  0) \
   CPUIDLEVEL(FALSE, 405, 0x40000005, 0,  0) \
   CPUIDLEVEL(FALSE, 406, 0x40000006, 0,  0) \
   CPUIDLEVEL(FALSE, 410, 0x40000010, 0,  0) \
   CPUIDLEVEL(FALSE, 80,  0x80000000, 0,  0) \
   CPUIDLEVEL(TRUE,  81,  0x80000001, 0,  0) \
   CPUIDLEVEL(FALSE, 82,  0x80000002, 0,  0) \
   CPUIDLEVEL(FALSE, 83,  0x80000003, 0,  0) \
   CPUIDLEVEL(FALSE, 84,  0x80000004, 0,  0) \
   CPUIDLEVEL(FALSE, 85,  0x80000005, 0,  0) \
   CPUIDLEVEL(FALSE, 86,  0x80000006, 0,  0) \
   CPUIDLEVEL(FALSE, 87,  0x80000007, 0,  0) \
   CPUIDLEVEL(TRUE,  88,  0x80000008, 0,  0) \
   CPUIDLEVEL(TRUE,  8A,  0x8000000a, 0,  0) \
   CPUIDLEVEL(FALSE, 819, 0x80000019, 0,  0) \
   CPUIDLEVEL(FALSE, 81A, 0x8000001a, 0,  0) \
   CPUIDLEVEL(FALSE, 81B, 0x8000001b, 0,  0) \
   CPUIDLEVEL(FALSE, 81C, 0x8000001c, 0,  0) \
   CPUIDLEVEL(FALSE, 81D, 0x8000001d, 5,  0) \
   CPUIDLEVEL(FALSE, 81E, 0x8000001e, 0,  0) \
   CPUIDLEVEL(TRUE,  81F, 0x8000001f, 0, 14) \
   CPUIDLEVEL(TRUE,  820, 0x80000020, 4, 17) \
   CPUIDLEVEL(TRUE,  821, 0x80000021, 0, 17) \
   CPUIDLEVEL(TRUE,  822, 0x80000022, 0, 20) \
   CPUIDLEVEL(TRUE,  823, 0x80000023, 0, 20) \
   CPUIDLEVEL(FALSE, 826, 0x80000026, 4, 20)

/* Define all CPUID levels in the form: CPUID_LEVEL_<ShortName> */
typedef enum {
#define CPUIDLEVEL(t, s, v, c, h) CPUID_LEVEL_##s,
   CPUID_KNOWN_LEVELS
#undef CPUIDLEVEL
   CPUID_NUM_KNOWN_LEVELS
} CpuidLevel;

/* Enum to translate between shorthand name and actual CPUID level value. */
enum {
#define CPUIDLEVEL(t, s, v, c, h) CPUID_LEVEL_VAL_##s = v,
   CPUID_KNOWN_LEVELS
#undef CPUIDLEVEL
};


/* Named feature leaves */
#define CPUID_FEATURE_INFORMATION    0x01
#define CPUID_PROCESSOR_TOPOLOGY     4
#define CPUID_MWAIT_FEATURES         5
#define CPUID_PMC_FEATURES           0xa
#define CPUID_XSAVE_FEATURES         0xd
#define CPUID_RDT_FEATURES           0x10
#define CPUID_SGX_FEATURES           0x12
#define CPUID_PT_FEATURES            0x14
#define CPUID_HYPERVISOR_LEVEL_0     0x40000000
#define CPUID_VMW_FEATURES           0x40000010
#define CPUID_HYPERVISOR_LEVEL_MAX   0x400000FF
#define CPUID_SVM_FEATURES           0x8000000a
#define CPUID_SEV_INFO               0x8000001f

/*
 * 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
} CpuidReg;

#define CPUID_INTEL_VENDOR_STRING       "GenuntelineI"
#define CPUID_AMD_VENDOR_STRING         "AuthcAMDenti"
#define CPUID_CYRIX_VENDOR_STRING       "CyriteadxIns"
#define CPUID_VIA_VENDOR_STRING         "CentaulsaurH"
#define CPUID_HYGON_VENDOR_STRING       "HygouinenGen"
#define CPUID_INTEL_TDX_VENDOR_STRING   "Inte    lTDX"

#define CPUID_HYPERV_HYPERVISOR_VENDOR_STRING  "Microsoft Hv"
#define CPUID_KVM_HYPERVISOR_VENDOR_STRING     "KVMKVMKVM\0\0\0"
#define CPUID_VMWARE_HYPERVISOR_VENDOR_STRING  "VMwareVMware"
#define CPUID_XEN_HYPERVISOR_VENDOR_STRING     "XenVMMXenVMM"

#define CPUID_INTEL_VENDOR_STRING_FIXED      "GenuineIntel"
#define CPUID_AMD_VENDOR_STRING_FIXED        "AuthenticAMD"
#define CPUID_CYRIX_VENDOR_STRING_FIXED      "CyrixInstead"
#define CPUID_VIA_VENDOR_STRING_FIXED        "CentaurHauls"
#define CPUID_HYGON_VENDOR_STRING_FIXED      "HygonGenuine"
#define CPUID_INTEL_TDX_VENDOR_STRING_FIXED  "IntelTDX    "

/*
 * FIELD 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_KNOWN_LEVELS, above).
 *
 * The second parameter is the CPUID sub-level (subleaf) of the feature. Please
 * make sure here the number is consistent with the "subleaf count" in
 * CPUIDLEVEL macro. I.e., if a feature is being added to a _new_ subleaf,
 * update the subleaf count above as well.
 *
 * The third parameter is the result register.
 *
 * The fourth and fifth parameters are the bit position of the field and the
 * width, respectively.
 *
 * The sixth is the name of the field.
 *
 * The seventh parameter specifies the monitor support characteristics for
 * this field. The value must be a valid CpuidFieldSupported value (omitting
 * CPUID_FIELD_SUPPORT_ for convenience). The meaning of those values are
 * described below.
 *
 * The eighth parameter specifies the first virtual hardware version that
 * implements the field (if 7th field is YES or ANY), or 0 (if 7th field is
 * NO or NA).  The value FUT means HW_VERSION_FUTURE.  The field's hardware
 * version must match the version in defaultMasks (cpuidcompat.c) if defined
 * there, and must be less than or equal to the version of the cpuid leaf
 * it's in.
 *
 * FLAG is defined identically to FIELD, but its accessors are more appropriate
 * for 1-bit flags, and compile-time asserts enforce that the size is 1 bit
 * wide.
 */


/*
 * CpuidFieldSupported is made up of the following values:
 *
 *     NO: A feature/field that IS NOT SUPPORTED by the monitor.  Even
 *     if the host supports this feature, we will never expose it to
 *     the guest.
 *
 *     YES: A feature/field that IS SUPPORTED by the monitor.  If the
 *     host supports this feature, we will expose it to the guest.  If
 *     not, then we will not set the feature.
 *
 *     ANY: A feature/field that IS ALWAYS SUPPORTED by the monitor.
 *     Even if the host does not support the feature, the monitor can
 *     expose the feature to the guest. As with "YES", the guest cpuid
 *     value defaults to the host/evc cpuid value.  But usually the
 *     guest cpuid value is recomputed at power on, ignoring the default
 *     value.
 *
 *     NA: Only legal for levels not masked/tested by default (see
 *     above for this definition).  Such fields must always be marked
 *     as NA.
 *
 * These distinctions can be translated into a common CPUID mask string as
 * follows:
 *
 *     YES --> "H" (Host).  We support the feature, so show it to the
 *     guest if the host has the feature.
 *
 *     ANY/NA --> "X" (Ignore).  By default, don't perform checks for
 *     this feature bit.  Per-GOS masks may choose to set this bit in
 *     the guest.  (e.g. the APIC feature bit is always set to 1.)
 *
 *     See lib/cpuidcompat/cpuidcompat.c for any possible overrides to
 *     these defaults.
 */

/*
 * XSAVEOPT was incorrectly missed until HWv11. See comment for
 * DisableReqHWVersion in vmFeatureCPUID.c for more detail.
 */

typedef enum {
   CPUID_FIELD_SUPPORTED_NO,
   CPUID_FIELD_SUPPORTED_YES,
   CPUID_FIELD_SUPPORTED_ANY,
   CPUID_FIELD_SUPPORTED_NA,
   CPUID_NUM_FIELD_SUPPORTEDS
} CpuidFieldSupported;

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_0                                            \
FIELD(  0,  0, EAX,  0, 32, NUMLEVELS,                           ANY,   4 ) \
FIELD(  0,  0, EBX,  0, 32, VENDOR1,                             YES,   4 ) \
FIELD(  0,  0, ECX,  0, 32, VENDOR3,                             YES,   4 ) \
FIELD(  0,  0, EDX,  0, 32, VENDOR2,                             YES,   4 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_1                                            \
FIELD(  1,  0, EAX,  0,  4, STEPPING,                            ANY,   4 ) \
FIELD(  1,  0, EAX,  4,  4, MODEL,                               ANY,   4 ) \
FIELD(  1,  0, EAX,  8,  4, FAMILY,                              YES,   4 ) \
FIELD(  1,  0, EAX, 12,  2, TYPE,                                ANY,   4 ) \
FIELD(  1,  0, EAX, 16,  4, EXTENDED_MODEL,                      ANY,   4 ) \
FIELD(  1,  0, EAX, 20,  8, EXTENDED_FAMILY,                     YES,   4 ) \
FIELD(  1,  0, EBX,  0,  8, BRAND_ID,                            ANY,   4 ) \
FIELD(  1,  0, EBX,  8,  8, CLFL_SIZE,                           ANY,   4 ) \
FIELD(  1,  0, EBX, 16,  8, LCPU_COUNT,                          ANY,   4 ) \
FIELD(  1,  0, EBX, 24,  8, APICID,                              ANY,   4 ) \
FLAG(   1,  0, ECX,  0,  1, SSE3,                                YES,   4 ) \
FLAG(   1,  0, ECX,  1,  1, PCLMULQDQ,                           YES,   4 ) \
FLAG(   1,  0, ECX,  2,  1, DTES64,                              NO,    0 ) \
FLAG(   1,  0, ECX,  3,  1, MWAIT,                               YES,   7 ) \
FLAG(   1,  0, ECX,  4,  1, DSCPL,                               NO,    0 ) \
FLAG(   1,  0, ECX,  5,  1, VMX,                                 YES,   8 ) \
FLAG(   1,  0, ECX,  6,  1, SMX,                                 YES, FUT ) \
FLAG(   1,  0, ECX,  7,  1, EIST,                                NO,    0 ) \
FLAG(   1,  0, ECX,  8,  1, TM2,                                 NO,    0 ) \
FLAG(   1,  0, ECX,  9,  1, SSSE3,                               YES,   4 ) \
FLAG(   1,  0, ECX, 10,  1, CNXTID,                              NO,    0 ) \
FLAG(   1,  0, ECX, 11,  1, SDBG,                                NO,    0 ) \
FLAG(   1,  0, ECX, 12,  1, FMA,                                 YES,   8 ) \
FLAG(   1,  0, ECX, 13,  1, CMPXCHG16B,                          YES,   4 ) \
FLAG(   1,  0, ECX, 14,  1, xTPR,                                NO,    0 ) \
FLAG(   1,  0, ECX, 15,  1, PDCM,                                NO,    0 ) \
FLAG(   1,  0, ECX, 17,  1, PCID,                                YES,   9 ) \
FLAG(   1,  0, ECX, 18,  1, DCA,                                 NO,    0 ) \
FLAG(   1,  0, ECX, 19,  1, SSE41,                               YES,   4 ) \
FLAG(   1,  0, ECX, 20,  1, SSE42,                               YES,   4 ) \
FLAG(   1,  0, ECX, 21,  1, x2APIC,                              ANY,   9 ) \
FLAG(   1,  0, ECX, 22,  1, MOVBE,                               YES,   4 ) \
FLAG(   1,  0, ECX, 23,  1, POPCNT,                              YES,   4 ) \
FLAG(   1,  0, ECX, 24,  1, TSC_DEADLINE,                        ANY,  11 ) \
FLAG(   1,  0, ECX, 25,  1, AES,                                 YES,   4 ) \
FLAG(   1,  0, ECX, 26,  1, XSAVE,                               YES,   8 ) \
FLAG(   1,  0, ECX, 27,  1, OSXSAVE,                             ANY,   8 ) \
FLAG(   1,  0, ECX, 28,  1, AVX,                                 YES,   8 ) \
FLAG(   1,  0, ECX, 29,  1, F16C,                                YES,   9 ) \
FLAG(   1,  0, ECX, 30,  1, RDRAND,                              YES,   9 ) \
FLAG(   1,  0, ECX, 31,  1, HYPERVISOR,                          ANY,   4 ) \
FLAG(   1,  0, EDX,  0,  1, FPU,                                 YES,   4 ) \
FLAG(   1,  0, EDX,  1,  1, VME,                                 YES,   4 ) \
FLAG(   1,  0, EDX,  2,  1, DE,                                  YES,   4 ) \
FLAG(   1,  0, EDX,  3,  1, PSE,                                 YES,   4 ) \
FLAG(   1,  0, EDX,  4,  1, TSC,                                 YES,   4 ) \
FLAG(   1,  0, EDX,  5,  1, MSR,                                 YES,   4 ) \
FLAG(   1,  0, EDX,  6,  1, PAE,                                 YES,   4 ) \
FLAG(   1,  0, EDX,  7,  1, MCE,                                 YES,   4 ) \
FLAG(   1,  0, EDX,  8,  1, CX8,                                 YES,   4 ) \
FLAG(   1,  0, EDX,  9,  1, APIC,                                ANY,   4 ) \
FLAG(   1,  0, EDX, 11,  1, SEP,                                 YES,   4 ) \
FLAG(   1,  0, EDX, 12,  1, MTRR,                                YES,   4 ) \
FLAG(   1,  0, EDX, 13,  1, PGE,                                 YES,   4 ) \
FLAG(   1,  0, EDX, 14,  1, MCA,                                 YES,   4 ) \
FLAG(   1,  0, EDX, 15,  1, CMOV,                                YES,   4 ) \
FLAG(   1,  0, EDX, 16,  1, PAT,                                 YES,   4 ) \
FLAG(   1,  0, EDX, 17,  1, PSE36,                               YES,   4 ) \
FLAG(   1,  0, EDX, 18,  1, PSN,                                 YES,   4 ) \
FLAG(   1,  0, EDX, 19,  1, CLFSH,                               YES,   4 ) \
FLAG(   1,  0, EDX, 21,  1, DS,                                  YES,   4 ) \
FLAG(   1,  0, EDX, 22,  1, ACPI,                                ANY,   4 ) \
FLAG(   1,  0, EDX, 23,  1, MMX,                                 YES,   4 ) \
FLAG(   1,  0, EDX, 24,  1, FXSR,                                YES,   4 ) \
FLAG(   1,  0, EDX, 25,  1, SSE,                                 YES,   4 ) \
FLAG(   1,  0, EDX, 26,  1, SSE2,                                YES,   4 ) \
FLAG(   1,  0, EDX, 27,  1, SS,                                  YES,   4 ) \
FLAG(   1,  0, EDX, 28,  1, HTT,                                 ANY,   7 ) \
FLAG(   1,  0, EDX, 29,  1, TM,                                  NO,    0 ) \
FLAG(   1,  0, EDX, 30,  1, IA64,                                NO,    0 ) \
FLAG(   1,  0, EDX, 31,  1, PBE,                                 NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_2                                            \
FIELD(  2,  0, EAX,  0,  8, LEAF2_COUNT,                         NA,    0 ) \
FIELD(  2,  0, EAX,  8,  8, LEAF2_CACHE1,                        NA,    0 ) \
FIELD(  2,  0, EAX, 16,  8, LEAF2_CACHE2,                        NA,    0 ) \
FIELD(  2,  0, EAX, 24,  8, LEAF2_CACHE3,                        NA,    0 ) \
FIELD(  2,  0, EBX,  0,  8, LEAF2_CACHE4,                        NA,    0 ) \
FIELD(  2,  0, EBX,  8,  8, LEAF2_CACHE5,                        NA,    0 ) \
FIELD(  2,  0, EBX, 16,  8, LEAF2_CACHE6,                        NA,    0 ) \
FIELD(  2,  0, EBX, 24,  8, LEAF2_CACHE7,                        NA,    0 ) \
FIELD(  2,  0, ECX,  0,  8, LEAF2_CACHE8,                        NA,    0 ) \
FIELD(  2,  0, ECX,  8,  8, LEAF2_CACHE9,                        NA,    0 ) \
FIELD(  2,  0, ECX, 16,  8, LEAF2_CACHE10,                       NA,    0 ) \
FIELD(  2,  0, ECX, 24,  8, LEAF2_CACHE11,                       NA,    0 ) \
FIELD(  2,  0, EDX,  0,  8, LEAF2_CACHE12,                       NA,    0 ) \
FIELD(  2,  0, EDX,  8,  8, LEAF2_CACHE13,                       NA,    0 ) \
FIELD(  2,  0, EDX, 16,  8, LEAF2_CACHE14,                       NA,    0 ) \
FIELD(  2,  0, EDX, 24,  8, LEAF2_CACHE15,                       NA,    0 ) \

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_4                                            \
FIELD(  4,  0, EAX,  0,  5, LEAF4_CACHE_TYPE,                    NA,    0 ) \
FIELD(  4,  0, EAX,  5,  3, LEAF4_CACHE_LEVEL,                   NA,    0 ) \
FLAG(   4,  0, EAX,  8,  1, LEAF4_CACHE_SELF_INIT,               NA,    0 ) \
FLAG(   4,  0, EAX,  9,  1, LEAF4_CACHE_FULLY_ASSOC,             NA,    0 ) \
FIELD(  4,  0, EAX, 14, 12, LEAF4_CACHE_NUMHT_SHARING,           NA,    0 ) \
FIELD(  4,  0, EAX, 26,  6, LEAF4_CORE_COUNT,                    NA,    0 ) \
FIELD(  4,  0, EBX,  0, 12, LEAF4_CACHE_LINE,                    NA,    0 ) \
FIELD(  4,  0, EBX, 12, 10, LEAF4_CACHE_PART,                    NA,    0 ) \
FIELD(  4,  0, EBX, 22, 10, LEAF4_CACHE_WAYS,                    NA,    0 ) \
FIELD(  4,  0, ECX,  0, 32, LEAF4_CACHE_SETS,                    NA,    0 ) \
FLAG(   4,  0, EDX,  0,  1, LEAF4_CACHE_WBINVD_NOT_GUARANTEED,   NA,    0 ) \
FLAG(   4,  0, EDX,  1,  1, LEAF4_CACHE_IS_INCLUSIVE,            NA,    0 ) \
FLAG(   4,  0, EDX,  2,  1, LEAF4_CACHE_COMPLEX_INDEXING,        NA,    0 )

/*     LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,              MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_5                                            \
FIELD(  5,  0, EAX,  0, 16, MWAIT_MIN_SIZE,                      NA,    0 ) \
FIELD(  5,  0, EBX,  0, 16, MWAIT_MAX_SIZE,                      NA,    0 ) \
FLAG(   5,  0, ECX,  0,  1, MWAIT_EXTENSIONS,                    NA,    0 ) \
FLAG(   5,  0, ECX,  1,  1, MWAIT_INTR_BREAK,                    NA,    0 ) \
FLAG(   5,  0, ECX,  3,  1, MONITORLESS_MWAIT,                   NA,    0 ) \
FIELD(  5,  0, EDX,  0,  4, MWAIT_C0_SUBSTATE,                   NA,    0 ) \
FIELD(  5,  0, EDX,  4,  4, MWAIT_C1_SUBSTATE,                   NA,    0 ) \
FIELD(  5,  0, EDX,  8,  4, MWAIT_C2_SUBSTATE,                   NA,    0 ) \
FIELD(  5,  0, EDX, 12,  4, MWAIT_C3_SUBSTATE,                   NA,    0 ) \
FIELD(  5,  0, EDX, 16,  4, MWAIT_C4_SUBSTATE,                   NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_6                                            \
FLAG(   6,  0, EAX,  0,  1, THERMAL_SENSOR,                      NO,    0 ) \
FLAG(   6,  0, EAX,  1,  1, TURBO_MODE,                          NO,    0 ) \
FLAG(   6,  0, EAX,  2,  1, APIC_INVARIANT,                      ANY,   4 ) \
FLAG(   6,  0, EAX,  4,  1, PLN,                                 NO,    0 ) \
FLAG(   6,  0, EAX,  5,  1, ECMD,                                NO,    0 ) \
FLAG(   6,  0, EAX,  6,  1, PTM,                                 NO,    0 ) \
FLAG(   6,  0, EAX,  7,  1, HWP,                                 NO,    0 ) \
FLAG(   6,  0, EAX,  8,  1, HWP_NOTIFICATION,                    NO,    0 ) \
FLAG(   6,  0, EAX,  9,  1, HWP_ACTIVITY_WINDOW,                 NO,    0 ) \
FLAG(   6,  0, EAX, 10,  1, HWP_ENERGY_PERFORMANCE_PREFERENCE,   NO,    0 ) \
FLAG(   6,  0, EAX, 11,  1, HWP_PACKAGE_LEVEL_REQUEST,           NO,    0 ) \
FLAG(   6,  0, EAX, 13,  1, HDC,                                 NO,    0 ) \
FLAG(   6,  0, EAX, 14,  1, TURBO_BOOST_MAX_3,                   NO,    0 ) \
FLAG(   6,  0, EAX, 15,  1, HWP_CAPABILITIES,                    NO,    0 ) \
FLAG(   6,  0, EAX, 16,  1, HWP_PECI,                            NO,    0 ) \
FLAG(   6,  0, EAX, 17,  1, HWP_FLEXIBLE,                        NO,    0 ) \
FLAG(   6,  0, EAX, 18,  1, HWP_FAST_ACCESS,                     NO,    0 ) \
FLAG(   6,  0, EAX, 19,  1, HW_FEEDBACK,                         NO,    0 ) \
FLAG(   6,  0, EAX, 20,  1, HWP_IGNORE_IDLE_REQUEST,             NO,    0 ) \
FLAG(   6,  0, EAX, 22,  1, HWP_CTL_MSR,                         NO,    0 ) \
FLAG(   6,  0, EAX, 23,  1, HW_FEEDBACK_ENHANCED,                NO,    0 ) \
FLAG(   6,  0, EAX, 24,  1, HWP_THERM_INTERRUPT_MSR,             NO,    0 ) \
FIELD(  6,  0, EBX,  0,  4, NUM_INTR_THRESHOLDS,                 NO,    0 ) \
FLAG(   6,  0, ECX,  0,  1, HW_COORD_FEEDBACK,                   NO,    0 ) \
FLAG(   6,  0, ECX,  1,  1, ACNT2,                               ANY,  13 ) \
FLAG(   6,  0, ECX,  3,  1, ENERGY_PERF_BIAS,                    NO,    0 ) \
FIELD(  6,  0, ECX,  8,  4, HW_FEEDBACK_NUM_CLASSES,             NO,    0 ) \
FLAG(   6,  0, EDX,  0,  1, PERF_CAP_REPORTING,                  NO,    0 ) \
FLAG(   6,  0, EDX,  1,  1, ENERGY_CAP_REPORTING,                NO,    0 ) \
FIELD(  6,  0, EDX,  8,  4, HW_FEEDBACK_SIZE,                    NO,    0 ) \
FIELD(  6,  0, EDX, 16, 16, HW_FEEDBACK_INDEX,                   NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_7                                            \
FIELD(  7,  0, EAX,  0, 32, LEAF_7_MAX_SUBLEVEL,                 YES,  18 ) \
FLAG(   7,  0, EBX,  0,  1, FSGSBASE,                            YES,   9 ) \
FLAG(   7,  0, EBX,  1,  1, TSC_ADJUST,                          ANY,  11 ) \
FLAG(   7,  0, EBX,  2,  1, SGX,                                 ANY,  17 ) \
FLAG(   7,  0, EBX,  3,  1, BMI1,                                YES,   9 ) \
FLAG(   7,  0, EBX,  4,  1, HLE,                                 ANY,  11 ) \
FLAG(   7,  0, EBX,  5,  1, AVX2,                                YES,  11 ) \
FLAG(   7,  0, EBX,  6,  1, FDP_EXCPTN_ONLY,                     ANY,   4 ) \
FLAG(   7,  0, EBX,  7,  1, SMEP,                                YES,   9 ) \
FLAG(   7,  0, EBX,  8,  1, BMI2,                                YES,  11 ) \
FLAG(   7,  0, EBX,  9,  1, ENFSTRG,                             YES,   9 ) \
FLAG(   7,  0, EBX, 10,  1, INVPCID,                             YES,  11 ) \
FLAG(   7,  0, EBX, 11,  1, RTM,                                 YES,  11 ) \
FLAG(   7,  0, EBX, 12,  1, PQM,                                 NO,    0 ) \
FLAG(   7,  0, EBX, 13,  1, FP_SEGMENT_ZERO,                     ANY,  11 ) \
FLAG(   7,  0, EBX, 14,  1, MPX,                                 ANY,  13 ) \
FLAG(   7,  0, EBX, 15,  1, PQE,                                 YES,  20 ) \
FLAG(   7,  0, EBX, 16,  1, AVX512F,                             YES,  13 ) \
FLAG(   7,  0, EBX, 17,  1, AVX512DQ,                            YES,  13 ) \
FLAG(   7,  0, EBX, 18,  1, RDSEED,                              YES,  11 ) \
FLAG(   7,  0, EBX, 19,  1, ADX,                                 YES,  11 ) \
FLAG(   7,  0, EBX, 20,  1, SMAP,                                YES,  11 ) \
FLAG(   7,  0, EBX, 21,  1, AVX512IFMA,                          YES,  17 ) \
FLAG(   7,  0, EBX, 23,  1, CLFLUSHOPT,                          YES,  13 ) \
FLAG(   7,  0, EBX, 24,  1, CLWB,                                YES,  13 ) \
FLAG(   7,  0, EBX, 25,  1, PT,                                  NO,    0 ) \
FLAG(   7,  0, EBX, 26,  1, AVX512PF,                            YES,  13 ) \
FLAG(   7,  0, EBX, 27,  1, AVX512ER,                            YES,  13 ) \
FLAG(   7,  0, EBX, 28,  1, AVX512CD,                            YES,  13 ) \
FLAG(   7,  0, EBX, 29,  1, SHA,                                 YES,  14 ) \
FLAG(   7,  0, EBX, 30,  1, AVX512BW,                            YES,  13 ) \
FLAG(   7,  0, EBX, 31,  1, AVX512VL,                            YES,  13 ) \
FLAG(   7,  0, ECX,  0,  1, PREFETCHWT1,                         YES,  13 ) \
FLAG(   7,  0, ECX,  1,  1, AVX512VBMI,                          YES,  17 ) \
FLAG(   7,  0, ECX,  2,  1, UMIP,                                YES,  17 ) \
FLAG(   7,  0, ECX,  3,  1, PKU,                                 YES,  13 ) \
FLAG(   7,  0, ECX,  4,  1, OSPKE,                               ANY,  13 ) \
FLAG(   7,  0, ECX,  5,  1, WAITPKG,                             YES,  21 ) \
FLAG(   7,  0, ECX,  6,  1, AVX512VBMI2,                         YES,  17 ) \
FLAG(   7,  0, ECX,  7,  1, CET_SS,                              YES,  20 ) \
FLAG(   7,  0, ECX,  8,  1, GFNI,                                YES,  17 ) \
FLAG(   7,  0, ECX,  9,  1, VAES,                                YES,  17 ) \
FLAG(   7,  0, ECX, 10,  1, VPCLMULQDQ,                          YES,  17 ) \
FLAG(   7,  0, ECX, 11,  1, AVX512VNNI,                          YES,  17 ) \
FLAG(   7,  0, ECX, 12,  1, AVX512BITALG,                        YES,  17 ) \
FLAG(   7,  0, ECX, 13,  1, TME_EN,                              NO,    0 ) \
FLAG(   7,  0, ECX, 14,  1, AVX512VPOPCNTDQ,                     YES,  16 ) \
FLAG(   7,  0, ECX, 16,  1, VA57,                                NO,    0 ) \
FIELD(  7,  0, ECX, 17,  5, MAWA,                                NO,    0 ) \
FLAG(   7,  0, ECX, 22,  1, RDPID,                               YES,  17 ) \
FLAG(   7,  0, ECX, 23,  1, KEY_LOCKER,                          NO,    0 ) \
FLAG(   7,  0, ECX, 24,  1, BUS_LOCK_DB,                         NO,    0 ) \
FLAG(   7,  0, ECX, 25,  1, CLDEMOTE,                            YES,  18 ) \
FLAG(   7,  0, ECX, 27,  1, MOVDIRI,                             YES,  18 ) \
FLAG(   7,  0, ECX, 28,  1, MOVDIR64B,                           YES,  18 ) \
FLAG(   7,  0, ECX, 29,  1, ENQCMD,                              YES, FUT ) \
FLAG(   7,  0, ECX, 30,  1, SGX_LC,                              ANY,  17 ) \
FLAG(   7,  0, ECX, 31,  1, PKS,                                 YES,  20 ) \
FLAG(   7,  0, EDX,  1,  1, SGK_KEYS,                            NO,    0 ) \
FLAG(   7,  0, EDX,  2,  1, AVX512QVNNIW,                        YES,  16 ) \
FLAG(   7,  0, EDX,  3,  1, AVX512QFMAPS,                        YES,  16 ) \
FLAG(   7,  0, EDX,  4,  1, FAST_SHORT_REPMOV,                   YES,  18 ) \
FLAG(   7,  0, EDX,  5,  1, UINTR,                               NO,    0 ) \
FLAG(   7,  0, EDX,  8,  1, AVX512VP2INTERSECT,                  YES,  18 ) \
FLAG(   7,  0, EDX,  9,  1, SRBDS_CTRL,                          NO,    0 ) \
FLAG(   7,  0, EDX, 10,  1, MDCLEAR,                             YES,   9 ) \
FLAG(   7,  0, EDX, 11,  1, RTM_ALWAYS_ABORT,                    NO,    0 ) \
FLAG(   7,  0, EDX, 13,  1, RTM_FORCE_ABORT,                     NO,    0 ) \
FLAG(   7,  0, EDX, 14,  1, SERIALIZE,                           YES,  20 ) \
FLAG(   7,  0, EDX, 15,  1, HYBRID,                              NO,    0 ) \
FLAG(   7,  0, EDX, 16,  1, TSXLDTRK,                            YES,  21 ) \
FLAG(   7,  0, EDX, 18,  1, PCONFIG,                             NO,    0 ) \
FLAG(   7,  0, EDX, 19,  1, ARCH_LBR,                            YES,  20 ) \
FLAG(   7,  0, EDX, 20,  1, CET_IBT,                             YES,  20 ) \
FLAG(   7,  0, EDX, 22,  1, AMX_BF16,                            YES,  20 ) \
FLAG(   7,  0, EDX, 23,  1, AVX512FP16,                          YES,  20 ) \
FLAG(   7,  0, EDX, 24,  1, AMX_TILE,                            YES,  20 ) \
FLAG(   7,  0, EDX, 25,  1, AMX_INT8,                            YES,  20 ) \
FLAG(   7,  0, EDX, 26,  1, IBRSIBPB,                            ANY,   9 ) \
FLAG(   7,  0, EDX, 27,  1, STIBP,                               YES,   9 ) \
FLAG(   7,  0, EDX, 28,  1, FCMD,                                YES,   9 ) \
FLAG(   7,  0, EDX, 29,  1, ARCH_CAPABILITIES,                   ANY,   9 ) \
FLAG(   7,  0, EDX, 30,  1, CORE_CAPABILITIES,                   NO,    0 ) \
FLAG(   7,  0, EDX, 31,  1, SSBD,                                YES,   9 ) \
FLAG(   7,  1, EAX,  0,  1, SHA512,                              NO,    0 ) \
FLAG(   7,  1, EAX,  1,  1, SM3,                                 NO,    0 ) \
FLAG(   7,  1, EAX,  2,  1, SM4,                                 NO,    0 ) \
FLAG(   7,  1, EAX,  3,  1, RAO_INT,                             NO,    0 ) \
FLAG(   7,  1, EAX,  4,  1, AVX_VNNI,                            YES,  20 ) \
FLAG(   7,  1, EAX,  5,  1, AVX512BF16,                          YES,  18 ) \
FLAG(   7,  1, EAX,  6,  1, LASS,                                NO,    0 ) \
FLAG(   7,  1, EAX,  7,  1, CMPCCXADD,                           YES,  22 ) \
FLAG(   7,  1, EAX,  8,  1, ARCH_PERFMON_EXT,                    NO,    0 ) \
FLAG(   7,  1, EAX, 10,  1, FAST_ZERO_MOVSB,                     YES,  20 ) \
FLAG(   7,  1, EAX, 11,  1, FAST_SHORT_STOSB,                    YES,  20 ) \
FLAG(   7,  1, EAX, 12,  1, FAST_SHORT_CMPSB_SCASB,              YES,  20 ) \
FLAG(   7,  1, EAX, 17,  1, FRED,                                NO,    0 ) \
FLAG(   7,  1, EAX, 18,  1, LKGS,                                NO,    0 ) \
FLAG(   7,  1, EAX, 19,  1, WRMSRNS,                             NO,    0 ) \
FLAG(   7,  1, EAX, 20,  1, NMI_SRC,                             NO,    0 ) \
FLAG(   7,  1, EAX, 21,  1, AMX_FP16,                            YES,  21 ) \
FLAG(   7,  1, EAX, 22,  1, HRESET,                              NO,    0 ) \
FLAG(   7,  1, EAX, 23,  1, AVX_IFMA,                            YES,  22 ) \
FLAG(   7,  1, EAX, 26,  1, LAM,                                 YES,  22 ) \
FLAG(   7,  1, EAX, 27,  1, MSRLIST,                             NO,    0 ) \
FLAG(   7,  1, EBX,  0,  1, LEAF7_PPIN,                          NO,    0 ) \
FLAG(   7,  1, EBX,  1,  1, PBNDKB,                              NO,    0 ) \
FLAG(   7,  1, EDX,  4,  1, AVX_VNNI_INT8,                       YES,  22 ) \
FLAG(   7,  1, EDX,  5,  1, AVX_NE_CONVERT,                      YES,  22 ) \
FLAG(   7,  1, EDX,  8,  1, AMX_COMPLEX,                         NO,    0 ) \
FLAG(   7,  1, EDX, 10,  1, AVX_VNNI_INT16,                      NO,    0 ) \
FLAG(   7,  1, EDX, 13,  1, UTMR,                                NO,    0 ) \
FLAG(   7,  1, EDX, 14,  1, PREFETCHI,                           YES,  21 ) \
FLAG(   7,  1, EDX, 15,  1, USER_MSR,                            NO,    0 ) \
FLAG(   7,  1, EDX, 18,  1, CET_SSS,                             NO,    0 ) \
FLAG(   7,  2, EDX,  0,  1, PSFD,                                YES,  20 ) \
FLAG(   7,  2, EDX,  1,  1, IPRED_CTRL,                          YES,  21 ) \
FLAG(   7,  2, EDX,  2,  1, RRSBA_CTRL,                          YES,  21 ) \
FLAG(   7,  2, EDX,  3,  1, DDPD_U,                              YES,  21 ) \
FLAG(   7,  2, EDX,  4,  1, BHI_CTRL,                            YES,  21 ) \
FLAG(   7,  2, EDX,  5,  1, MCDT_NO,                             NO,    0 ) \
FLAG(   7,  2, EDX,  6,  1, UC_LOCK_DISABLE,                     NO,    0 ) \
FLAG(   7,  2, EDX,  7,  1, MONITOR_MITG_NO,                     NO,    0 ) \
FLAG(   7,  2, EDX, 19,  1, AVX10,                               NO,    0 ) \
FLAG(   7,  2, EDX, 21,  1, APX_F,                               NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_9                                            \
FIELD(  9,  0, EAX,  0, 32, IA32_PLATFORM_DCA_CAP_VAL,           NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_A                                            \
FIELD(  A,  0, EAX,  0,  8, PMC_VERSION,                         NA,    0 ) \
FIELD(  A,  0, EAX,  8,  8, PMC_NUM_GEN,                         NA,    0 ) \
FIELD(  A,  0, EAX, 16,  8, PMC_WIDTH_GEN,                       NA,    0 ) \
FIELD(  A,  0, EAX, 24,  8, PMC_EBX_LENGTH,                      NA,    0 ) \
FLAG(   A,  0, EBX,  0,  1, PMC_CORE_CYCLES,                     NA,    0 ) \
FLAG(   A,  0, EBX,  1,  1, PMC_INSTR_RETIRED,                   NA,    0 ) \
FLAG(   A,  0, EBX,  2,  1, PMC_REF_CYCLES,                      NA,    0 ) \
FLAG(   A,  0, EBX,  3,  1, PMC_LAST_LVL_CREF,                   NA,    0 ) \
FLAG(   A,  0, EBX,  4,  1, PMC_LAST_LVL_CMISS,                  NA,    0 ) \
FLAG(   A,  0, EBX,  5,  1, PMC_BR_INST_RETIRED,                 NA,    0 ) \
FLAG(   A,  0, EBX,  6,  1, PMC_BR_MISS_RETIRED,                 NA,    0 ) \
FLAG(   A,  0, EBX,  7,  1, PMC_TOPDOWN_SLOTS,                   NA,    0 ) \
FLAG(   A,  0, ECX,  0,  1, PMC_FIXED0,                          NA,    0 ) \
FLAG(   A,  0, ECX,  1,  1, PMC_FIXED1,                          NA,    0 ) \
FLAG(   A,  0, ECX,  2,  1, PMC_FIXED2,                          NA,    0 ) \
FLAG(   A,  0, ECX,  3,  1, PMC_FIXED3,                          NA,    0 ) \
FIELD(  A,  0, EDX,  0,  5, PMC_NUM_FIXED,                       NA,    0 ) \
FIELD(  A,  0, EDX,  5,  8, PMC_WIDTH_FIXED,                     NA,    0 ) \
FLAG(   A,  0, EDX, 15,  1, PMC_ANYTHREAD_DEPRECATED,            NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_B                                            \
FIELD(  B,  0, EAX,  0,  5, TOPOLOGY_MASK_WIDTH,                 NA,    0 ) \
FIELD(  B,  0, EBX,  0, 16, TOPOLOGY_CPUS_SHARING_LEVEL,         NA,    0 ) \
FIELD(  B,  0, ECX,  0,  8, TOPOLOGY_LEVEL_NUMBER,               NA,    0 ) \
FIELD(  B,  0, ECX,  8,  8, TOPOLOGY_LEVEL_TYPE,                 NA,    0 ) \
FIELD(  B,  0, EDX,  0, 32, TOPOLOGY_X2APIC_ID,                  NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_D                                            \
FLAG(   D,  0, EAX,  0,  1, XCR0_MASTER_LEGACY_FP,               YES,   8 ) \
FLAG(   D,  0, EAX,  1,  1, XCR0_MASTER_SSE,                     YES,   8 ) \
FLAG(   D,  0, EAX,  2,  1, XCR0_MASTER_YMM_H,                   YES,   8 ) \
FLAG(   D,  0, EAX,  3,  1, XCR0_MASTER_BNDREGS,                 YES,  13 ) \
FLAG(   D,  0, EAX,  4,  1, XCR0_MASTER_BNDCSR,                  YES,  13 ) \
FLAG(   D,  0, EAX,  5,  1, XCR0_MASTER_OPMASK,                  YES,  13 ) \
FLAG(   D,  0, EAX,  6,  1, XCR0_MASTER_ZMM_H,                   YES,  13 ) \
FLAG(   D,  0, EAX,  7,  1, XCR0_MASTER_HI16_ZMM,                YES,  13 ) \
FLAG(   D,  0, EAX,  9,  1, XCR0_MASTER_PKRU,                    YES,  13 ) \
FLAG(   D,  0, EAX, 17,  1, XCR0_MASTER_XTILECFG,                YES,  20 ) \
FLAG(   D,  0, EAX, 18,  1, XCR0_MASTER_XTILEDATA,               YES,  20 ) \
FIELD(  D,  0, EBX,  0, 32, XSAVE_ENABLED_SIZE,                  ANY,   8 ) \
FIELD(  D,  0, ECX,  0, 32, XSAVE_MAX_SIZE,                      YES,   8 ) \
FIELD(  D,  0, EDX,  0, 29, XCR0_MASTER_UPPER,                   NO,    0 ) \
FLAG(   D,  0, EDX, 30,  1, XCR0_MASTER_LWP,                     NO,    0 ) \
FLAG(   D,  0, EDX, 31,  1, XCR0_MASTER_EXTENDED_XSAVE,          NO,    0 ) \
FLAG(   D,  1, EAX,  0,  1, XSAVEOPT,                            YES,  11 ) \
FLAG(   D,  1, EAX,  1,  1, XSAVEC,                              YES,  13 ) \
FLAG(   D,  1, EAX,  2,  1, XGETBV_ECX1,                         YES,  17 ) \
FLAG(   D,  1, EAX,  3,  1, XSAVES,                              YES,  13 ) \
FLAG(   D,  1, EAX,  4,  1, XFD,                                 YES,  20 ) \
FIELD(  D,  1, EBX,  0, 32, XSAVES_ENABLED_SIZE,                 ANY,  13 ) \
FLAG(   D,  1, ECX,  8,  1, XSS_MASTER_PT,                       NO,    0 ) \
FLAG(   D,  1, ECX, 10,  1, XSS_MASTER_PASID,                    YES, FUT ) \
FLAG(   D,  1, ECX, 11,  1, XSS_MASTER_CET_U,                    YES,  20 ) \
FLAG(   D,  1, ECX, 12,  1, XSS_MASTER_CET_S,                    YES,  20 ) \
FLAG(   D,  1, ECX, 13,  1, XSS_MASTER_HDC,                      NO,    0 ) \
FLAG(   D,  1, ECX, 14,  1, XSS_MASTER_UINTR,                    NO,    0 ) \
FLAG(   D,  1, ECX, 15,  1, XSS_MASTER_LBR,                      NO,    0 ) \
FLAG(   D,  1, ECX, 16,  1, XSS_MASTER_HWP,                      NO,    0 ) \
FIELD(  D,  1, EDX,  0, 32, XSS_MASTER_UPPER,                    NO,    0 ) \
FIELD(  D,  2, EAX,  0, 32, XSAVE_YMM_SIZE,                      YES,   8 ) \
FIELD(  D,  2, EBX,  0, 32, XSAVE_YMM_OFFSET,                    YES,   8 ) \
FLAG(   D,  2, ECX,  0,  1, XSAVE_YMM_SUP_BY_XSS,                NO,    0 ) \
FLAG(   D,  2, ECX,  1,  1, XSAVE_YMM_ALIGN,                     YES,  13 ) \
FLAG(   D,  2, ECX,  2,  1, XSAVE_YMM_XFD,                       NO,    0 ) \
FIELD(  D,  3, EAX,  0, 32, XSAVE_BNDREGS_SIZE,                  YES,  13 ) \
FIELD(  D,  3, EBX,  0, 32, XSAVE_BNDREGS_OFFSET,                YES,  13 ) \
FLAG(   D,  3, ECX,  0,  1, XSAVE_BNDREGS_SUP_BY_XSS,            NO,    0 ) \
FLAG(   D,  3, ECX,  1,  1, XSAVE_BNDREGS_ALIGN,                 YES,  13 ) \
FLAG(   D,  3, ECX,  2,  1, XSAVE_BNDREGS_XFD,                   NO,    0 ) \
FIELD(  D,  4, EAX,  0, 32, XSAVE_BNDCSR_SIZE,                   YES,  13 ) \
FIELD(  D,  4, EBX,  0, 32, XSAVE_BNDCSR_OFFSET,                 YES,  13 ) \
FLAG(   D,  4, ECX,  0,  1, XSAVE_BNDCSR_SUP_BY_XSS,             NO,    0 ) \
FLAG(   D,  4, ECX,  1,  1, XSAVE_BNDCSR_ALIGN,                  YES,  13 ) \
FLAG(   D,  4, ECX,  2,  1, XSAVE_BNDCSR_XFD,                    NO,    0 ) \
FIELD(  D,  5, EAX,  0, 32, XSAVE_OPMASK_SIZE,                   YES,  13 ) \
FIELD(  D,  5, EBX,  0, 32, XSAVE_OPMASK_OFFSET,                 YES,  13 ) \
FLAG(   D,  5, ECX,  0,  1, XSAVE_OPMASK_SUP_BY_XSS,             NO,    0 ) \
FLAG(   D,  5, ECX,  1,  1, XSAVE_OPMASK_ALIGN,                  YES,  13 ) \
FLAG(   D,  5, ECX,  2,  1, XSAVE_OPMASK_XFD,                    NO,    0 ) \
FIELD(  D,  6, EAX,  0, 32, XSAVE_ZMM_H_SIZE,                    YES,  13 ) \
FIELD(  D,  6, EBX,  0, 32, XSAVE_ZMM_H_OFFSET,                  YES,  13 ) \
FLAG(   D,  6, ECX,  0,  1, XSAVE_ZMM_H_SUP_BY_XSS,              NO,    0 ) \
FLAG(   D,  6, ECX,  1,  1, XSAVE_ZMM_H_ALIGN,                   YES,  13 ) \
FLAG(   D,  6, ECX,  2,  1, XSAVE_ZMM_H_XFD,                     NO,    0 ) \
FIELD(  D,  7, EAX,  0, 32, XSAVE_HI16_ZMM_SIZE,                 YES,  13 ) \
FIELD(  D,  7, EBX,  0, 32, XSAVE_HI16_ZMM_OFFSET,               YES,  13 ) \
FLAG(   D,  7, ECX,  0,  1, XSAVE_HI16_ZMM_SUP_BY_XSS,           NO,    0 ) \
FLAG(   D,  7, ECX,  1,  1, XSAVE_HI16_ZMM_ALIGN,                YES,  13 ) \
FLAG(   D,  7, ECX,  2,  1, XSAVE_HI16_ZMM_XFD,                  NO,    0 ) \
FIELD(  D,  8, EAX,  0, 32, XSAVES_PT_STATE_SIZE,                NO,    0 ) \
FLAG(   D,  8, ECX,  0,  1, XSAVES_PT_STATE_SUP_BY_XSS,          NO,    0 ) \
FLAG(   D,  8, ECX,  1,  1, XSAVES_PT_STATE_ALIGN,               NO,    0 ) \
FLAG(   D,  8, ECX,  2,  1, XSAVES_PT_STATE_XFD,                 NO,    0 ) \
FIELD(  D,  9, EAX,  0, 32, XSAVE_PKRU_SIZE,                     YES,  13 ) \
FIELD(  D,  9, EBX,  0, 32, XSAVE_PKRU_OFFSET,                   YES,  13 ) \
FLAG(   D,  9, ECX,  0,  1, XSAVE_PKRU_SUP_BY_XSS,               NO,    0 ) \
FLAG(   D,  9, ECX,  1,  1, XSAVE_PKRU_ALIGN,                    YES,  13 ) \
FLAG(   D,  9, ECX,  2,  1, XSAVE_PKRU_XFD,                      NO,    0 ) \
FIELD(  D, 10, EAX,  0, 32, XSAVES_PASID_STATE_SIZE,             YES, FUT ) \
FLAG(   D, 10, ECX,  0,  1, XSAVES_PASID_STATE_SUP_BY_XSS,       YES, FUT ) \
FLAG(   D, 10, ECX,  1,  1, XSAVES_PASID_STATE_ALIGN,            YES, FUT ) \
FLAG(   D, 10, ECX,  2,  1, XSAVES_PASID_STATE_XFD,              YES, FUT ) \
FIELD(  D, 11, EAX,  0, 32, XSAVES_CET_U_SIZE,                   YES,  20 ) \
FLAG(   D, 11, ECX,  0,  1, XSAVES_CET_U_SUP_BY_XSS,             YES,  20 ) \
FLAG(   D, 11, ECX,  1,  1, XSAVES_CET_U_ALIGN,                  YES,  20 ) \
FLAG(   D, 11, ECX,  2,  1, XSAVES_CET_U_XFD,                    YES,  20 ) \
FIELD(  D, 12, EAX,  0, 32, XSAVES_CET_S_SIZE,                   YES,  20 ) \
FLAG(   D, 12, ECX,  0,  1, XSAVES_CET_S_SUP_BY_XSS,             YES,  20 ) \
FLAG(   D, 12, ECX,  1,  1, XSAVES_CET_S_ALIGN,                  YES,  20 ) \
FLAG(   D, 12, ECX,  2,  1, XSAVES_CET_S_XFD,                    YES,  20 ) \
FIELD(  D, 13, EAX,  0, 32, XSAVES_HDT_SIZE,                     NO,    0 ) \
FLAG(   D, 13, ECX,  0,  1, XSAVES_HDT_SUP_BY_XSS,               NO,    0 ) \
FLAG(   D, 13, ECX,  1,  1, XSAVES_HDT_ALIGN,                    NO,    0 ) \
FLAG(   D, 13, ECX,  2,  1, XSAVES_HDT_XFD,                      NO,    0 ) \
FIELD(  D, 14, EAX,  0, 32, XSAVES_UINTR_SIZE,                   NO,    0 ) \
FLAG(   D, 14, ECX,  0,  1, XSAVES_UINTR_SUP_BY_XSS,             NO,    0 ) \
FLAG(   D, 14, ECX,  1,  1, XSAVES_UINTR_ALIGN,                  NO,    0 ) \
FLAG(   D, 14, ECX,  2,  1, XSAVES_UINTR_XFD,                    NO,    0 ) \
FIELD(  D, 15, EAX,  0, 32, XSAVES_LBR_SIZE,                     NO,    0 ) \
FLAG(   D, 15, ECX,  0,  1, XSAVES_LBR_SUP_BY_XSS,               NO,    0 ) \
FLAG(   D, 15, ECX,  1,  1, XSAVES_LBR_ALIGN,                    NO,    0 ) \
FLAG(   D, 15, ECX,  2,  1, XSAVES_LBR_XFD,                      NO,    0 ) \
FIELD(  D, 16, EAX,  0, 32, XSAVES_HWP_SIZE,                     NO,    0 ) \
FLAG(   D, 16, ECX,  0,  1, XSAVES_HWP_SUP_BY_XSS,               NO,    0 ) \
FLAG(   D, 16, ECX,  1,  1, XSAVES_HWP_ALIGN,                    NO,    0 ) \
FLAG(   D, 16, ECX,  2,  1, XSAVES_HWP_XFD,                      NO,    0 ) \
FIELD(  D, 17, EAX,  0, 32, XSAVE_XTILECFG_SIZE,                 YES,  20 ) \
FIELD(  D, 17, EBX,  0, 32, XSAVE_XTILECFG_OFFSET,               YES,  20 ) \
FLAG(   D, 17, ECX,  0,  1, XSAVE_XTILECFG_SUP_BY_XSS,           NO,    0 ) \
FLAG(   D, 17, ECX,  1,  1, XSAVE_XTILECFG_ALIGN,                YES,  20 ) \
FLAG(   D, 17, ECX,  2,  1, XSAVE_XTILECFG_XFD,                  YES,  20 ) \
FIELD(  D, 18, EAX,  0, 32, XSAVE_XTILEDATA_SIZE,                YES,  20 ) \
FIELD(  D, 18, EBX,  0, 32, XSAVE_XTILEDATA_OFFSET,              YES,  20 ) \
FLAG(   D, 18, ECX,  0,  1, XSAVE_XTILEDATA_SUP_BY_XSS,          NO,    0 ) \
FLAG(   D, 18, ECX,  1,  1, XSAVE_XTILEDATA_ALIGN,               YES,  20 ) \
FLAG(   D, 18, ECX,  2,  1, XSAVE_XTILEDATA_XFD,                 YES,  20 ) \
/* D, 62: AMD LWP leaf on BD, PD, SR. Dropped in Zen. Never referenced. */

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_F                                            \
FIELD(  F,  0, EBX,  0, 32, PQM_MAX_RMID,                        NO,    0 ) \
FLAG(   F,  0, EDX,  1,  1, PQM_CMT_SUPPORT,                     NO,    0 ) \
FIELD(  F,  1, EBX,  0, 32, PQM_CMT_CONV,                        NO,    0 ) \
FIELD(  F,  1, ECX,  0, 32, PQM_CMT_NUM_RMID,                    NO,    0 ) \
FLAG(   F,  1, EDX,  0,  1, PQM_CMT_OCCUPANCY,                   NO,    0 ) \
FLAG(   F,  1, EDX,  1,  1, PQM_MBM_TOTAL,                       NO,    0 ) \
FLAG(   F,  1, EDX,  2,  1, PQM_MBM_LOCAL,                       NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_10                                           \
FLAG(  10,  0, EBX,  1,  1, PQE_L3,                              YES,  20 ) \
FLAG(  10,  0, EBX,  2,  1, PQE_L2,                              NO,    0 ) \
FLAG(  10,  0, EBX,  3,  1, PQE_MBA,                             NO,    0 ) \
FIELD( 10,  1, EAX,  0,  5, PQE_L3_MASK_LENGTH,                  YES,  20 ) \
FIELD( 10,  1, EBX,  0, 32, PQE_L3_ISOLATION_UNIT_MAP,           NO,    0 ) \
FLAG(  10,  1, ECX,  2,  1, PQE_L3_CDP,                          NO,    0 ) \
FIELD( 10,  1, EDX,  0, 16, PQE_L3_MAX_COS_NUMBER,               YES,  20 ) \
FIELD( 10,  2, EAX,  0,  5, PQE_L2_MASK_LENGTH,                  NO,    0 ) \
FIELD( 10,  2, EBX,  0, 32, PQE_L2_ISOLATION_UNIT_MAP,           NO,    0 ) \
FLAG(  10,  2, ECX,  2,  1, PQE_L2_CDP,                          NO,    0 ) \
FIELD( 10,  2, EDX,  0, 16, PQE_L2_MAX_COS_NUMBER,               NO,    0 )


/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_12                                           \
FLAG(  12,  0, EAX,  0,  1, SGX1,                                ANY,  17 ) \
FLAG(  12,  0, EAX,  1,  1, SGX2,                                ANY,  21 ) \
FLAG(  12,  0, EAX,  5,  1, SGX_OVERSUB_ENCLV,                   NO,    0 ) \
FLAG(  12,  0, EAX,  6,  1, SGX_OVERSUB_ENCLS,                   NO,    0 ) \
FLAG(  12,  0, EAX,  7,  1, SGX_EVERIFYREPORT2,                  ANY, FUT ) \
FLAG(  12,  0, EAX, 10,  1, SGX_EUPDATESVN,                      NO,    0 ) \
FLAG(  12,  0, EAX, 11,  1, SGX_EDECCSSA,                        ANY, FUT ) \
FLAG(  12,  0, EBX,  0,  1, SGX_MISCSELECT_EXINFO,               ANY, FUT ) \
FLAG(  12,  0, EBX,  1,  1, SGX_MISCSELECT_CPINFO,               ANY, FUT ) \
FIELD( 12,  0, EDX,  0,  8, MAX_ENCLAVE_SIZE_NOT64,              ANY,  17 ) \
FIELD( 12,  0, EDX,  8,  8, MAX_ENCLAVE_SIZE_64,                 ANY,  17 ) \
FLAG(  12,  1, EAX,  1,  1, SECS_ATTRIBUTES_DEBUG,               ANY,  17 ) \
FLAG(  12,  1, EAX,  2,  1, SECS_ATTRIBUTES_MODE64BIT,           ANY,  17 ) \
FLAG(  12,  1, EAX,  4,  1, SECS_ATTRIBUTES_PROVISIONKEY,        ANY,  17 ) \
FLAG(  12,  1, EAX,  5,  1, SECS_ATTRIBUTES_EINITTOKEN_KEY,      ANY,  17 ) \
FLAG(  12,  1, EAX,  6,  1, SECS_ATTRIBUTES_CET,                 NO,    0 ) \
FLAG(  12,  1, EAX,  7,  1, SECS_ATTRIBUTES_KSS,                 ANY,  17 ) \
FLAG(  12,  1, EAX,  8,  1, SECS_ATTRIBUTES_LAM_U57,             NO,    0 ) \
FLAG(  12,  1, EAX,  9,  1, SECS_ATTRIBUTES_LAM_U48,             NO,    0 ) \
FLAG(  12,  1, EAX, 10,  1, SECS_ATTRIBUTES_AEXNOTIFY,           ANY, FUT ) \
FIELD( 12,  1, ECX,  0, 32, SECS_ATTRIBUTES_XFRM_LOW,            ANY,  17 ) \
FIELD( 12,  1, EDX,  0, 32, SECS_ATTRIBUTES_XFRM_HIGH,           ANY,  17 ) \
FIELD( 12,  2, EAX,  0,  4, EPC00_VALID,                         ANY,  17 ) \
FIELD( 12,  2, EAX, 12, 20, EPC00_BASE_LOW,                      ANY,  17 ) \
FIELD( 12,  2, EBX,  0, 20, EPC00_BASE_HIGH,                     ANY,  17 ) \
FIELD( 12,  2, ECX,  0,  4, EPC00_PROTECTED,                     ANY,  17 ) \
FIELD( 12,  2, ECX, 12, 20, EPC00_SIZE_LOW,                      ANY,  17 ) \
FIELD( 12,  2, EDX,  0, 20, EPC00_SIZE_HIGH,                     ANY,  17 ) \
FIELD( 12,  3, EAX,  0,  4, EPC01_VALID,                         NO,    0 ) \
FIELD( 12,  3, EAX, 12, 20, EPC01_BASE_LOW,                      NO,    0 ) \
FIELD( 12,  3, EBX,  0, 20, EPC01_BASE_HIGH,                     NO,    0 ) \
FIELD( 12,  3, ECX,  0,  4, EPC01_PROTECTED,                     NO,    0 ) \
FIELD( 12,  3, ECX, 12, 20, EPC01_SIZE_LOW,                      NO,    0 ) \
FIELD( 12,  3, EDX,  0, 20, EPC01_SIZE_HIGH,                     NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_14                                           \
FIELD( 14,  0, EAX,  0, 32, PT_MAX_SUBLEAF,                      NO,    0 ) \
FLAG(  14,  0, EBX,  0,  1, PT_CR3_FILTER,                       NO,    0 ) \
FLAG(  14,  0, EBX,  1,  1, PT_CFG_PSB_CYC,                      NO,    0 ) \
FLAG(  14,  0, EBX,  2,  1, PT_IP_FILTER_PERSIST_MSR,            NO,    0 ) \
FLAG(  14,  0, EBX,  3,  1, PT_MTC,                              NO,    0 ) \
FLAG(  14,  0, EBX,  4,  1, PT_PTWRITE,                          NO,    0 ) \
FLAG(  14,  0, EBX,  5,  1, PT_POWER_EVENT,                      NO,    0 ) \
FLAG(  14,  0, EBX,  6,  1, PT_PSB_PMI,                          NO,    0 ) \
FLAG(  14,  0, EBX,  7,  1, PT_EVENT_TRACE_ENABLE,               NO,    0 ) \
FLAG(  14,  0, EBX,  8,  1, PT_TNT_DISABLE,                      NO,    0 ) \
FLAG(  14,  0, EBX,  9,  1, PT_TRIGGERING,                       NO,    0 ) \
FLAG(  14,  0, ECX,  0,  1, PT_TOPA,                             NO,    0 ) \
FLAG(  14,  0, ECX,  1,  1, PT_TOPA_MULTI,                       NO,    0 ) \
FLAG(  14,  0, ECX,  2,  1, PT_SRO,                              NO,    0 ) \
FLAG(  14,  0, ECX,  3,  1, PT_TRACE_TRANS,                      NO,    0 ) \
FLAG(  14,  0, ECX, 31,  1, PT_LIP,                              NO,    0 ) \
FIELD( 14,  1, EAX,  0,  3, PT_NUM_ADDR_RANGES,                  NO,    0 ) \
FIELD( 14,  1, EAX,  8,  3, PT_NUM_RTIT_TRIGGERS,                NO,    0 ) \
FIELD( 14,  1, EAX, 16, 16, PT_AVAIL_MTC_ENCS,                   NO,    0 ) \
FIELD( 14,  1, EBX,  0, 16, PT_AVAIL_CYC_THRESH_ENCS,            NO,    0 ) \
FIELD( 14,  1, EBX, 16, 16, PT_AVAIL_PSB_FREQ_ENCS,              NO,    0 ) \
FLAG(  14,  1, ECX,  0,  1, PT_TRIG_ACTION_ATTR,                 NO,    0 ) \
FLAG(  14,  1, ECX,  1,  1, PT_TRIG_PAUSE_RESUME,                NO,    0 ) \
FLAG(  14,  1, ECX, 15,  1, PT_TRIG_DR_MATCH,                    NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_15                                           \
FIELD( 15,  0, EAX,  0, 32, DENOM_TSC_TO_CORE_CRYSTAL_CLK,       NO,    0 ) \
FIELD( 15,  0, EBX,  0, 32, NUMER_TSC_TO_CORE_CRYSTAL_CLK,       NO,    0 ) \
FIELD( 15,  0, ECX,  0, 32, CORE_CRYSTAL_CLK_FREQ,               NO,    0 ) \

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_16                                           \
FIELD( 16,  0, EAX,  0, 16, PROC_BASE_FREQ,                      NO,    0 ) \
FIELD( 16,  0, EBX,  0, 16, PROC_MIN_FREQ,                       NO,    0 ) \
FIELD( 16,  0, ECX,  0, 16, BUS_FREQ,                            NO,    0 ) \

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_17                                           \
FIELD( 17,  0, EAX,  0, 31, MAX_SOCID_INDEX,                     NO,    0 ) \
FIELD( 17,  0, EBX,  0, 16, SOC_VENDOR_ID,                       NO,    0 ) \
FIELD( 17,  0, EBX, 16,  1, SOC_INDUSTRY_STD,                    NO,    0 ) \
FIELD( 17,  0, ECX,  0, 31, SOC_PROJECT_ID,                      NO,    0 ) \
FIELD( 17,  0, EDX,  0, 31, SOC_STEPPING_ID,                     NO,    0 ) \
FIELD( 17,  1, EAX,  0, 32, SOC_VENDOR_BRAND_STRING_1_0,         NO,    0 ) \
FIELD( 17,  1, EBX,  0, 32, SOC_VENDOR_BRAND_STRING_1_1,         NO,    0 ) \
FIELD( 17,  1, ECX,  0, 32, SOC_VENDOR_BRAND_STRING_1_2,         NO,    0 ) \
FIELD( 17,  1, EDX,  0, 32, SOC_VENDOR_BRAND_STRING_1_3,         NO,    0 ) \
FIELD( 17,  2, EAX,  0, 32, SOC_VENDOR_BRAND_STRING_2_0,         NO,    0 ) \
FIELD( 17,  2, EBX,  0, 32, SOC_VENDOR_BRAND_STRING_2_1,         NO,    0 ) \
FIELD( 17,  2, ECX,  0, 32, SOC_VENDOR_BRAND_STRING_2_2,         NO,    0 ) \
FIELD( 17,  2, EDX,  0, 32, SOC_VENDOR_BRAND_STRING_2_3,         NO,    0 ) \
FIELD( 17,  3, EAX,  0, 32, SOC_VENDOR_BRAND_STRING_3_0,         NO,    0 ) \
FIELD( 17,  3, EBX,  0, 32, SOC_VENDOR_BRAND_STRING_3_1,         NO,    0 ) \
FIELD( 17,  3, ECX,  0, 32, SOC_VENDOR_BRAND_STRING_3_2,         NO,    0 ) \
FIELD( 17,  3, EDX,  0, 32, SOC_VENDOR_BRAND_STRING_3_3,         NO,    0 ) \

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_18                                           \
FIELD( 18,  0, EAX,  0, 32, TLB_INFO_MAX_SUBLEAF,                NO,    0 ) \
FLAG(  18,  0, EBX,  0,  1, TLB_INFO_LEVEL_SIZE_4K,              NO,    0 ) \
FLAG(  18,  0, EBX,  1,  1, TLB_INFO_LEVEL_SIZE_2M,              NO,    0 ) \
FLAG(  18,  0, EBX,  2,  1, TLB_INFO_LEVEL_SIZE_4M,              NO,    0 ) \
FLAG(  18,  0, EBX,  3,  1, TLB_INFO_LEVEL_SIZE_1G,              NO,    0 ) \
FIELD( 18,  0, EBX,  8,  3, TLB_INFO_PARTITIONING,               NO,    0 ) \
FIELD( 18,  0, EBX, 16, 16, TLB_INFO_NUM_WAYS,                   NO,    0 ) \
FIELD( 18,  0, ECX,  0, 32, TLB_INFO_NUM_SETS,                   NO,    0 ) \
FIELD( 18,  0, EDX,  0,  5, TLB_INFO_TYPE,                       NO,    0 ) \
FIELD( 18,  0, EDX,  5,  3, TLB_INFO_LEVEL,                      NO,    0 ) \
FLAG(  18,  0, EDX,  8,  1, TLB_INFO_FULLY_ASSOCIATIVE,          NO,    0 ) \
FIELD( 18,  0, EDX, 14, 12, TLB_INFO_MAX_ADDRESSABLE_IDS,        NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_19                                           \
FLAG(  19,  0, EAX,  0,  1, KEY_LOCKER_CPL0_ONLY,                NO,    0 ) \
FLAG(  19,  0, EAX,  1,  1, KEY_LOCKER_NO_ENCRYPT,               NO,    0 ) \
FLAG(  19,  0, EAX,  2,  1, KEY_LOCKER_NO_DECRYPT,               NO,    0 ) \
FLAG(  19,  0, EBX,  0,  1, AESKLE,                              NO,    0 ) \
FLAG(  19,  0, EBX,  2,  1, AESKLE_WIDE,                         NO,    0 ) \
FLAG(  19,  0, EBX,  4,  1, KEY_LOCKER_MSRS,                     NO,    0 ) \
FLAG(  19,  0, ECX,  0,  1, LOADWKEY_NOBACKUP,                   NO,    0 ) \
FLAG(  19,  0, ECX,  1,  1, KEY_LOCKER_KEY_SOURCE,               NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_1A                                           \
FIELD( 1A,  0, EAX,  0, 24, NATIVE_MODEL_ID,                     NO,    0 ) \
FIELD( 1A,  0, EAX, 24,  8, CORE_TYPE,                           NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_1B                                           \
FIELD( 1B,  0, EAX,  0, 12, PCONFIG_SUBLEAF_TYPE,                NO,    0 ) \
FIELD( 1B,  0, EBX,  0, 32, PCONFIG_TARGET_ID1,                  NO,    0 ) \
FIELD( 1B,  0, ECX,  0, 32, PCONFIG_TARGET_ID2,                  NO,    0 ) \
FIELD( 1B,  0, EDX,  0, 32, PCONFIG_TARGET_ID3,                  NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_1C                                           \
FIELD( 1C,  0, EAX,  0,  8, LBR_DEPTH,                           YES,  20 ) \
FLAG(  1C,  0, EAX, 30,  1, LBR_DEEP_CSTATE_RESET,               YES,  20 ) \
FLAG(  1C,  0, EAX, 31,  1, LBR_IP_CONTAINS_LIP,                 YES,  20 ) \
FLAG(  1C,  0, EBX,  0,  1, LBR_CPL_FILTERING,                   YES,  20 ) \
FLAG(  1C,  0, EBX,  1,  1, LBR_BRANCH_FILTERING,                YES,  20 ) \
FLAG(  1C,  0, EBX,  2,  1, LBR_CALL_STACK_MODE,                 YES,  20 ) \
FLAG(  1C,  0, ECX,  0,  1, LBR_MISPREDICT,                      YES,  20 ) \
FLAG(  1C,  0, ECX,  1,  1, LBR_TIMED_LBRS,                      YES,  20 ) \
FLAG(  1C,  0, ECX,  2,  1, LBR_BRANCH_TYPE,                     YES,  20 ) \
FLAG(  1C,  0, ECX, 16,  1, LBR_EVENT_LOGGING_PMC0,               NO,   0 ) \
FLAG(  1C,  0, ECX, 17,  1, LBR_EVENT_LOGGING_PMC1,               NO,   0 ) \
FLAG(  1C,  0, ECX, 18,  1, LBR_EVENT_LOGGING_PMC2,               NO,   0 ) \
FLAG(  1C,  0, ECX, 19,  1, LBR_EVENT_LOGGING_PMC3,               NO,   0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_1D                                           \
FIELD( 1D,  0, EAX,  0, 32, TILE_PALETTE_MAX,                    YES,  20 ) \
FIELD( 1D,  1, EAX,  0, 16, TILE_PALETTE1_TOTAL_BYTES,           YES,  20 ) \
FIELD( 1D,  1, EAX, 16, 16, TILE_PALETTE1_BYTES_PER_TILE,        YES,  20 ) \
FIELD( 1D,  1, EBX,  0, 16, TILE_PALETTE1_BYTES_PER_ROW,         YES,  20 ) \
FIELD( 1D,  1, EBX, 16, 16, TILE_PALETTE1_NUM_REGS,              YES,  20 ) \
FIELD( 1D,  1, ECX,  0, 16, TILE_PALETTE1_MAX_ROWS,              YES,  20 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_1E                                           \
FIELD( 1E,  0, EBX,  0,  8, TMUL_MAX_K,                          YES,  20 ) \
FIELD( 1E,  0, EBX,  8, 16, TMUL_MAX_N,                          YES,  20 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_1F                                           \
FIELD( 1F,  0, EAX,  0,  5, TOPOLOGY_V2_MASK_WIDTH,              NA,    0 ) \
FIELD( 1F,  0, EBX,  0, 16, TOPOLOGY_V2_CPUS_SHARING_LEVEL,      NA,    0 ) \
FIELD( 1F,  0, ECX,  0,  8, TOPOLOGY_V2_LEVEL_NUMBER,            NA,    0 ) \
FIELD( 1F,  0, ECX,  8,  8, TOPOLOGY_V2_LEVEL_TYPE,              NA,    0 ) \
FIELD( 1F,  0, EDX,  0, 32, TOPOLOGY_V2_X2APIC_ID,               NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_20                                           \
FIELD( 20,  0, EAX,  0, 32, HRESET_MAX_SUBLEAF,                  NO,    0 ) \
FIELD( 20,  0, EBX,  0, 32, HRESET_ENABLE_MSR_VALID_BITS,        NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_21                                           \
FIELD( 21,  0, EAX,  0, 32, TDX_MAX_SUBLEAF,                     NO,    0 ) \
FIELD( 21,  0, EBX,  0, 32, TDX_VENDOR1,                         NO,    0 ) \
FIELD( 21,  0, ECX,  0, 32, TDX_VENDOR3,                         NO,    0 ) \
FIELD( 21,  0, EDX,  0, 32, TDX_VENDOR2,                         NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_23                                           \
FIELD( 23,  0, EAX,  0, 32, ARCH_PMC_MAX_SUBLEAF,                NO,    0 ) \
FLAG(  23,  0, EBX,  0,  1, ARCH_PMC_UNITMASK2,                  NO,    0 ) \
FLAG(  23,  0, EBX,  1,  1, ARCH_PMC_ZBIT,                       NO,    0 ) \
FIELD( 23,  0, ECX,  0,  8, ARCH_PMC_SLOTS_PER_CYCLE,            NO,    0 ) \
FIELD( 23,  1, EAX,  0, 32, ARCH_PMC_GEN_BITMAP,                 NO,    0 ) \
FIELD( 23,  1, EBX,  0, 32, ARCH_PMC_FIXED_BITMAP,               NO,    0 ) \
FIELD( 23,  2, EAX,  0, 32, ARCH_PMC_GEN_RELOADABLE_BITMAP,      NO,    0 ) \
FIELD( 23,  2, EBX,  0, 32, ARCH_PMC_FIXED_RELOADABLE_BITMAP,    NO,    0 ) \
FIELD( 23,  2, ECX,  0, 32, ARCH_PMC_GEN_CAN_RELOAD_BITMAP,      NO,    0 ) \
FIELD( 23,  2, EDX,  0, 32, ARCH_PMC_FIXED_CAN_RELOAD_BITMAP,    NO,    0 ) \
FLAG(  23,  3, EAX,  0,  1, ARCH_PMC_CORE_CYCLES,                NO,    0 ) \
FLAG(  23,  3, EAX,  1,  1, ARCH_PMC_INSTR_RETIRED,              NO,    0 ) \
FLAG(  23,  3, EAX,  2,  1, ARCH_PMC_REF_CYCLES,                 NO,    0 ) \
FLAG(  23,  3, EAX,  3,  1, ARCH_PMC_LAST_LVL_CREF,              NO,    0 ) \
FLAG(  23,  3, EAX,  4,  1, ARCH_PMC_LAST_LVL_CMISS,             NO,    0 ) \
FLAG(  23,  3, EAX,  5,  1, ARCH_PMC_BR_INST_RETIRED,            NO,    0 ) \
FLAG(  23,  3, EAX,  6,  1, ARCH_PMC_BR_MISS_RETIRED,            NO,    0 ) \
FLAG(  23,  3, EAX,  7,  1, ARCH_PMC_TOPDOWN_SLOTS,              NO,    0 ) \
FLAG(  23,  3, EAX,  8,  1, ARCH_PMC_TOPDOWN_BACKEND,            NO,    0 ) \
FLAG(  23,  3, EAX,  9,  1, ARCH_PMC_TOPDOWN_BAD_SPEC,           NO,    0 ) \
FLAG(  23,  3, EAX, 10,  1, ARCH_PMC_TOPDOWN_FRONTEND,           NO,    0 ) \
FLAG(  23,  3, EAX, 11,  1, ARCH_PMC_TOPDOWN_RETIRE,             NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_24                                           \
FIELD( 24,  0, EAX,  0, 32, AVX10_MAX_SUBLEAF,                   NO,    0 ) \
FIELD( 24,  0, EBX,  0,  8, AVX10_VERSION,                       NO,    0 ) \
FLAG(  24,  0, EBX,  16, 1, AVX10_128,                           NO,    0 ) \
FLAG(  24,  0, EBX,  17, 1, AVX10_256,                           NO,    0 ) \
FLAG(  24,  0, EBX,  18, 1, AVX10_512,                           NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_400                                          \
FIELD(400,  0, EAX,  0, 32, MAX_HYP_LEVEL,                       NA,    0 ) \
FIELD(400,  0, EBX,  0, 32, HYPERVISOR_VENDOR0,                  NA,    0 ) \
FIELD(400,  0, ECX,  0, 32, HYPERVISOR_VENDOR1,                  NA,    0 ) \
FIELD(400,  0, EDX,  0, 32, HYPERVISOR_VENDOR2,                  NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_401                                          \
FIELD(401,  0, EAX,  0, 32, HV_INTERFACE_SIGNATURE,              NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_402                                          \
FIELD(402,  0, EAX,  0, 32, BUILD_NUMBER,                        NA,    0 ) \
FIELD(402,  0, EBX,  0, 16, MINOR_VERSION,                       NA,    0 ) \
FIELD(402,  0, EBX, 16, 16, MAJOR_VERSION,                       NA,    0 ) \
FIELD(402,  0, ECX,  0, 32, SERVICE_PACK,                        NA,    0 ) \
FIELD(402,  0, EDX,  0, 24, SERVICE_NUMBER,                      NA,    0 ) \
FIELD(402,  0, EDX, 24,  8, SERVICE_BRANCH,                      NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_403                                          \
FLAG( 403,  0, EAX,  0,  1, VP_RUNTIME_AVAIL,                    NA,    0 ) \
FLAG( 403,  0, EAX,  1,  1, REF_COUNTER_AVAIL,                   NA,    0 ) \
FLAG( 403,  0, EAX,  2,  1, BASIC_SYNIC_MSRS_AVAIL,              NA,    0 ) \
FLAG( 403,  0, EAX,  3,  1, SYNTH_TIMER_MSRS_AVAIL,              NA,    0 ) \
FLAG( 403,  0, EAX,  4,  1, APIC_ACCESS_MSRS_AVAIL,              NA,    0 ) \
FLAG( 403,  0, EAX,  5,  1, HYPERCALL_MSRS_AVAIL,                NA,    0 ) \
FLAG( 403,  0, EAX,  6,  1, VP_INDEX_MSR_AVAIL,                  NA,    0 ) \
FLAG( 403,  0, EAX,  7,  1, VIRT_RESET_MSR_AVAIL,                NA,    0 ) \
FLAG( 403,  0, EAX,  8,  1, STATS_PAGES_MSRS_AVAIL,              NA,    0 ) \
FLAG( 403,  0, EAX,  9,  1, REF_TSC_AVAIL,                       NA,    0 ) \
FLAG( 403,  0, EAX, 10,  1, GUEST_IDLE_MSR_AVAIL,                NA,    0 ) \
FLAG( 403,  0, EAX, 11,  1, FREQUENCY_MSRS_AVAIL,                NA,    0 ) \
FLAG( 403,  0, EAX, 12,  1, SYNTH_DEBUG_MSRS_AVAIL,              NA,    0 ) \
FLAG( 403,  0, EBX,  0,  1, CREATE_PARTITIONS_FLAG,              NA,    0 ) \
FLAG( 403,  0, EBX,  1,  1, ACCESS_PARTITION_ID_FLAG,            NA,    0 ) \
FLAG( 403,  0, EBX,  2,  1, ACCESS_MEMORY_POOL_FLAG,             NA,    0 ) \
FLAG( 403,  0, EBX,  3,  1, ADJUST_MESSAGE_BUFFERS_FLAG,         NA,    0 ) \
FLAG( 403,  0, EBX,  4,  1, POST_MESSAGES_FLAG,                  NA,    0 ) \
FLAG( 403,  0, EBX,  5,  1, SIGNAL_EVENTS_FLAG,                  NA,    0 ) \
FLAG( 403,  0, EBX,  6,  1, CREATE_PORT_FLAG,                    NA,    0 ) \
FLAG( 403,  0, EBX,  7,  1, CONNECT_PORT_FLAG,                   NA,    0 ) \
FLAG( 403,  0, EBX,  8,  1, ACCESS_STATS_FLAG,                   NA,    0 ) \
FLAG( 403,  0, EBX, 11,  1, DEBUGGING_FLAG,                      NA,    0 ) \
FLAG( 403,  0, EBX, 12,  1, CPU_MANAGEMENT_FLAG,                 NA,    0 ) \
FLAG( 403,  0, EBX, 13,  1, CONFIGURE_PROFILER_FLAG,             NA,    0 ) \
FLAG( 403,  0, EBX, 14,  1, ENABLE_EXPANDED_STACKWALKING_FLAG,   NA,    0 ) \
FLAG( 403,  0, EBX, 16,  1, ACCESS_VSM,                          NA,    0 ) \
FLAG( 403,  0, EBX, 17,  1, ACCESS_VP_REGISTERS,                 NA,    0 ) \
FIELD(403,  0, ECX,  0,  4, MAX_POWER_STATE,                     NA,    0 ) \
FLAG( 403,  0, ECX,  4,  1, HPET_NEEDED_FOR_C3,                  NA,    0 ) \
FLAG( 403,  0, EDX,  0,  1, MWAIT_AVAIL,                         NA,    0 ) \
FLAG( 403,  0, EDX,  1,  1, GUEST_DEBUGGING_AVAIL,               NA,    0 ) \
FLAG( 403,  0, EDX,  2,  1, PERFORMANCE_MONITOR_AVAIL,           NA,    0 ) \
FLAG( 403,  0, EDX,  3,  1, CPU_DYN_PARTITIONING_AVAIL,          NA,    0 ) \
FLAG( 403,  0, EDX,  4,  1, XMM_REGS_FOR_HYPERCALL_INPUT,        NA,    0 ) \
FLAG( 403,  0, EDX,  5,  1, GUEST_IDLE_AVAIL,                    NA,    0 ) \
FLAG( 403,  0, EDX,  6,  1, HYPERVISOR_SLEEP_STATE_AVAIL,        NA,    0 ) \
FLAG( 403,  0, EDX,  7,  1, NUMA_DISTANCE_QUERY_AVAIL,           NA,    0 ) \
FLAG( 403,  0, EDX,  8,  1, TIMER_FREQUENCY_AVAIL,               NA,    0 ) \
FLAG( 403,  0, EDX,  9,  1, SYNTH_MACHINE_CHECK_AVAIL,           NA,    0 ) \
FLAG( 403,  0, EDX, 10,  1, GUEST_CRASH_MSRS_AVAIL,              NA,    0 ) \
FLAG( 403,  0, EDX, 11,  1, DEBUG_MSRS_AVAIL,                    NA,    0 ) \
FLAG( 403,  0, EDX, 12,  1, NPIEP1_AVAIL,                        NA,    0 ) \
FLAG( 403,  0, EDX, 13,  1, DISABLE_HYPERVISOR_AVAIL,            NA,    0 ) \
FLAG( 403,  0, EDX, 15,  1, XMM_REGS_FOR_HYPERCALL_OUTPUT,       NA,    0 ) \
FLAG( 403,  0, EDX, 17,  1, SINT_POLLING_AVAIL,                  NA,    0 ) \
FLAG( 403,  0, EDX, 19,  1, DIRECT_SYN_TIMER,                    NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_404                                         \
FLAG( 404,  0, EAX,  0,  1, USE_HYPERCALL_TO_SWITCH_ADDR_SPACE,  NA,    0 ) \
FLAG( 404,  0, EAX,  1,  1, USE_HYPERCALL_TO_FLUSH_TLB,          NA,    0 ) \
FLAG( 404,  0, EAX,  2,  1, USE_HYPERCALL_FOR_TLB_SHOOTDOWN,     NA,    0 ) \
FLAG( 404,  0, EAX,  3,  1, USE_MSRS_FOR_EOI_ICR_TPR,            NA,    0 ) \
FLAG( 404,  0, EAX,  4,  1, USE_MSR_FOR_RESET,                   NA,    0 ) \
FLAG( 404,  0, EAX,  5,  1, USE_RELAXED_TIMING,                  NA,    0 ) \
FLAG( 404,  0, EAX,  6,  1, USE_DMA_REMAPPING,                   NA,    0 ) \
FLAG( 404,  0, EAX,  7,  1, USE_INTERRUPT_REMAPPING,             NA,    0 ) \
FLAG( 404,  0, EAX,  8,  1, USE_X2APIC,                          NA,    0 ) \
FLAG( 404,  0, EAX,  9,  1, DEPRECATE_AUTOEOI,                   NA,    0 ) \
FIELD(404,  0, EBX,  0, 32, SPINLOCK_RETRIES,                    NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_405                                          \
FIELD(405,  0, EAX,  0, 32, MAX_VCPU,                            NA,    0 ) \
FIELD(405,  0, EBX,  0, 32, MAX_LCPU,                            NA,    0 ) \
FIELD(405,  0, ECX,  0, 32, MAX_REMAPPABLE_VECTORS,              NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_406                                          \
FLAG( 406,  0, EAX,  0,  1, APIC_OVERLAY_ASSIST,                 NA,    0 ) \
FLAG( 406,  0, EAX,  1,  1, MSR_BITMAPS,                         NA,    0 ) \
FLAG( 406,  0, EAX,  2,  1, ARCH_PMCS,                           NA,    0 ) \
FLAG( 406,  0, EAX,  3,  1, SLAT,                                NA,    0 ) \
FLAG( 406,  0, EAX,  4,  1, DMA_REMAPPING,                       NA,    0 ) \
FLAG( 406,  0, EAX,  5,  1, INTERRUPT_REMAPPING,                 NA,    0 ) \
FLAG( 406,  0, EAX,  6,  1, MEMORY_PATROL_SCRUBBER,              NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_410                                          \
FIELD(410,  0, EAX,  0, 32, TSC_HZ,                              NA,    0 ) \
FIELD(410,  0, EBX,  0, 32, APICBUS_HZ,                          NA,    0 ) \
FLAG( 410,  0, ECX,  0,  1, VMMCALL_BACKDOOR,                    NA,    0 ) \
FLAG( 410,  0, ECX,  1,  1, VMCALL_BACKDOOR,                     NA,    0 ) \
FLAG( 410,  0, ECX,  3,  1, TDX_API_ENABLED,                     NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_80                                           \
FIELD( 80,  0, EAX,  0, 32, NUM_EXT_LEVELS,                      NA,    0 ) \
FIELD( 80,  0, EBX,  0, 32, LEAF80_VENDOR1,                      NA,    0 ) \
FIELD( 80,  0, ECX,  0, 32, LEAF80_VENDOR3,                      NA,    0 ) \
FIELD( 80,  0, EDX,  0, 32, LEAF80_VENDOR2,                      NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_81                                           \
FIELD( 81,  0, EAX,  0,  4, LEAF81_STEPPING,                     ANY,   4 ) \
FIELD( 81,  0, EAX,  4,  4, LEAF81_MODEL,                        ANY,   4 ) \
FIELD( 81,  0, EAX,  8,  4, LEAF81_FAMILY,                       ANY,   4 ) \
FIELD( 81,  0, EAX, 12,  2, LEAF81_TYPE,                         ANY,   4 ) \
FIELD( 81,  0, EAX, 16,  4, LEAF81_EXTENDED_MODEL,               ANY,   4 ) \
FIELD( 81,  0, EAX, 20,  8, LEAF81_EXTENDED_FAMILY,              ANY,   4 ) \
FIELD( 81,  0, EBX,  0, 16, LEAF81_BRAND_ID,                     ANY,   4 ) \
FIELD( 81,  0, EBX, 28,  4, LEAF81_PKG_TYPE,                     ANY,   4 ) \
FLAG(  81,  0, ECX,  0,  1, LAHF64,                              YES,   4 ) \
FLAG(  81,  0, ECX,  1,  1, CMPLEGACY,                           ANY,   9 ) \
FLAG(  81,  0, ECX,  2,  1, SVM,                                 YES,   8 ) \
FLAG(  81,  0, ECX,  3,  1, EXTAPICSPC,                          YES,   4 ) \
FLAG(  81,  0, ECX,  4,  1, CR8AVAIL,                            YES,   9 ) \
FLAG(  81,  0, ECX,  5,  1, ABM,                                 YES,   4 ) \
FLAG(  81,  0, ECX,  6,  1, SSE4A,                               YES,   4 ) \
FLAG(  81,  0, ECX,  7,  1, MISALIGNED_SSE,                      YES,   4 ) \
FLAG(  81,  0, ECX,  8,  1, 3DNPREFETCH,                         YES,   4 ) \
FLAG(  81,  0, ECX,  9,  1, OSVW,                                ANY,   8 ) \
FLAG(  81,  0, ECX, 10,  1, IBS,                                 NO,    0 ) \
FLAG(  81,  0, ECX, 11,  1, XOP,                                 YES,   8 ) \
FLAG(  81,  0, ECX, 12,  1, SKINIT,                              NO,    0 ) \
FLAG(  81,  0, ECX, 13,  1, WATCHDOG,                            NO,    0 ) \
FLAG(  81,  0, ECX, 15,  1, LWP,                                 NO,    0 ) \
FLAG(  81,  0, ECX, 16,  1, FMA4,                                YES,   8 ) \
FLAG(  81,  0, ECX, 17,  1, TCE,                                 NO,    0 ) \
FLAG(  81,  0, ECX, 19,  1, NODEID_MSR,                          NO,    0 ) \
FLAG(  81,  0, ECX, 21,  1, TBM,                                 YES,   9 ) \
FLAG(  81,  0, ECX, 22,  1, TOPOLOGY,                            ANY,  18 ) \
FLAG(  81,  0, ECX, 23,  1, PERFCORE,                            ANY,   4 ) \
FLAG(  81,  0, ECX, 24,  1, PERFNB,                              NO,    0 ) \
FLAG(  81,  0, ECX, 26,  1, DATABK,                              NO,    0 ) \
FLAG(  81,  0, ECX, 27,  1, PERFTSC,                             NO,    0 ) \
FLAG(  81,  0, ECX, 28,  1, PERFL3,                              NO,    0 ) \
FLAG(  81,  0, ECX, 29,  1, MONITORX,                            YES,  21 ) \
FLAG(  81,  0, ECX, 30,  1, ADDR_MASK_EXT,                       NO,    0 ) \
FLAG(  81,  0, EDX,  0,  1, LEAF81_FPU,                          YES,   4 ) \
FLAG(  81,  0, EDX,  1,  1, LEAF81_VME,                          YES,   4 ) \
FLAG(  81,  0, EDX,  2,  1, LEAF81_DE,                           YES,   4 ) \
FLAG(  81,  0, EDX,  3,  1, LEAF81_PSE,                          YES,   4 ) \
FLAG(  81,  0, EDX,  4,  1, LEAF81_TSC,                          YES,   4 ) \
FLAG(  81,  0, EDX,  5,  1, LEAF81_MSR,                          YES,   4 ) \
FLAG(  81,  0, EDX,  6,  1, LEAF81_PAE,                          YES,   4 ) \
FLAG(  81,  0, EDX,  7,  1, LEAF81_MCE,                          YES,   4 ) \
FLAG(  81,  0, EDX,  8,  1, LEAF81_CX8,                          YES,   4 ) \
FLAG(  81,  0, EDX,  9,  1, LEAF81_APIC,                         ANY,   4 ) \
FLAG(  81,  0, EDX, 11,  1, SYSC,                                ANY,   4 ) \
FLAG(  81,  0, EDX, 12,  1, LEAF81_MTRR,                         YES,   4 ) \
FLAG(  81,  0, EDX, 13,  1, LEAF81_PGE,                          YES,   4 ) \
FLAG(  81,  0, EDX, 14,  1, LEAF81_MCA,                          YES,   4 ) \
FLAG(  81,  0, EDX, 15,  1, LEAF81_CMOV,                         YES,   4 ) \
FLAG(  81,  0, EDX, 16,  1, LEAF81_PAT,                          YES,   4 ) \
FLAG(  81,  0, EDX, 17,  1, LEAF81_PSE36,                        YES,   4 ) \
FLAG(  81,  0, EDX, 20,  1, NX,                                  YES,   4 ) \
FLAG(  81,  0, EDX, 22,  1, MMXEXT,                              YES,   4 ) \
FLAG(  81,  0, EDX, 23,  1, LEAF81_MMX,                          YES,   4 ) \
FLAG(  81,  0, EDX, 24,  1, LEAF81_FXSR,                         YES,   4 ) \
FLAG(  81,  0, EDX, 25,  1, FFXSR,                               YES,   4 ) \
FLAG(  81,  0, EDX, 26,  1, PDPE1GB,                             YES,   9 ) \
FLAG(  81,  0, EDX, 27,  1, RDTSCP,                              YES,   4 ) \
FLAG(  81,  0, EDX, 29,  1, LM,                                  YES,   4 ) \
FLAG(  81,  0, EDX, 30,  1, 3DNOWPLUS,                           YES,   4 ) \
FLAG(  81,  0, EDX, 31,  1, 3DNOW,                               YES,   4 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_82                                           \
FIELD( 82,  0, EAX,  0, 32, LEAF82_BRAND_STRING_EAX,             NA,    0 ) \
FIELD( 82,  0, EBX,  0, 32, LEAF82_BRAND_STRING_EBX,             NA,    0 ) \
FIELD( 82,  0, ECX,  0, 32, LEAF82_BRAND_STRING_ECX,             NA,    0 ) \
FIELD( 82,  0, EDX,  0, 32, LEAF82_BRAND_STRING_EDX,             NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_83                                           \
FIELD( 83,  0, EAX,  0, 32, LEAF83_BRAND_STRING_EAX,             NA,    0 ) \
FIELD( 83,  0, EBX,  0, 32, LEAF83_BRAND_STRING_EBX,             NA,    0 ) \
FIELD( 83,  0, ECX,  0, 32, LEAF83_BRAND_STRING_ECX,             NA,    0 ) \
FIELD( 83,  0, EDX,  0, 32, LEAF83_BRAND_STRING_EDX,             NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_84                                           \
FIELD( 84,  0, EAX,  0, 32, LEAF84_BRAND_STRING_EAX,             NA,    0 ) \
FIELD( 84,  0, EBX,  0, 32, LEAF84_BRAND_STRING_EBX,             NA,    0 ) \
FIELD( 84,  0, ECX,  0, 32, LEAF84_BRAND_STRING_ECX,             NA,    0 ) \
FIELD( 84,  0, EDX,  0, 32, LEAF84_BRAND_STRING_EDX,             NA,    0 )

/*    LEVEL, REG, POS, SIZE, NAME,                          MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_85                                           \
FIELD( 85,  0, EAX,  0,  8, ITLB_ENTRIES_2M4M_PGS,               NA,    0 ) \
FIELD( 85,  0, EAX,  8,  8, ITLB_ASSOC_2M4M_PGS,                 NA,    0 ) \
FIELD( 85,  0, EAX, 16,  8, DTLB_ENTRIES_2M4M_PGS,               NA,    0 ) \
FIELD( 85,  0, EAX, 24,  8, DTLB_ASSOC_2M4M_PGS,                 NA,    0 ) \
FIELD( 85,  0, EBX,  0,  8, ITLB_ENTRIES_4K_PGS,                 NA,    0 ) \
FIELD( 85,  0, EBX,  8,  8, ITLB_ASSOC_4K_PGS,                   NA,    0 ) \
FIELD( 85,  0, EBX, 16,  8, DTLB_ENTRIES_4K_PGS,                 NA,    0 ) \
FIELD( 85,  0, EBX, 24,  8, DTLB_ASSOC_4K_PGS,                   NA,    0 ) \
FIELD( 85,  0, ECX,  0,  8, L1_DCACHE_LINE_SIZE,                 NA,    0 ) \
FIELD( 85,  0, ECX,  8,  8, L1_DCACHE_LINES_PER_TAG,             NA,    0 ) \
FIELD( 85,  0, ECX, 16,  8, L1_DCACHE_ASSOC,                     NA,    0 ) \
FIELD( 85,  0, ECX, 24,  8, L1_DCACHE_SIZE,                      NA,    0 ) \
FIELD( 85,  0, EDX,  0,  8, L1_ICACHE_LINE_SIZE,                 NA,    0 ) \
FIELD( 85,  0, EDX,  8,  8, L1_ICACHE_LINES_PER_TAG,             NA,    0 ) \
FIELD( 85,  0, EDX, 16,  8, L1_ICACHE_ASSOC,                     NA,    0 ) \
FIELD( 85,  0, EDX, 24,  8, L1_ICACHE_SIZE,                      NA,    0 )

/*    LEVEL, REG, POS, SIZE, NAME,                          MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_86                                           \
FIELD( 86,  0, EAX,  0, 12, L2_ITLB_ENTRIES_2M4M_PGS,            NA,    0 ) \
FIELD( 86,  0, EAX, 12,  4, L2_ITLB_ASSOC_2M4M_PGS,              NA,    0 ) \
FIELD( 86,  0, EAX, 16, 12, L2_DTLB_ENTRIES_2M4M_PGS,            NA,    0 ) \
FIELD( 86,  0, EAX, 28,  4, L2_DTLB_ASSOC_2M4M_PGS,              NA,    0 ) \
FIELD( 86,  0, EBX,  0, 12, L2_ITLB_ENTRIES_4K_PGS,              NA,    0 ) \
FIELD( 86,  0, EBX, 12,  4, L2_ITLB_ASSOC_4K_PGS,                NA,    0 ) \
FIELD( 86,  0, EBX, 16, 12, L2_DTLB_ENTRIES_4K_PGS,              NA,    0 ) \
FIELD( 86,  0, EBX, 28,  4, L2_DTLB_ASSOC_4K_PGS,                NA,    0 ) \
FIELD( 86,  0, ECX,  0,  8, L2CACHE_LINE,                        NA,    0 ) \
FIELD( 86,  0, ECX,  8,  4, L2CACHE_LINE_PER_TAG,                NA,    0 ) \
FIELD( 86,  0, ECX, 12,  4, L2CACHE_WAYS,                        NA,    0 ) \
FIELD( 86,  0, ECX, 16, 16, L2CACHE_SIZE,                        NA,    0 ) \
FIELD( 86,  0, EDX,  0,  8, L3CACHE_LINE,                        NA,    0 ) \
FIELD( 86,  0, EDX,  8,  4, L3CACHE_LINE_PER_TAG,                NA,    0 ) \
FIELD( 86,  0, EDX, 12,  4, L3CACHE_WAYS,                        NA,    0 ) \
FIELD( 86,  0, EDX, 18, 14, L3CACHE_SIZE,                        NA,    0 )

/*    LEVEL, REG, POS, SIZE, NAME,                          MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_87                                           \
FLAG(  87,  0, EBX,  0,  1, MCA_OVERFLOW_RECOV,                  NA,    0 ) \
FLAG(  87,  0, EBX,  1,  1, SUCCOR,                              NA,    0 ) \
FLAG(  87,  0, EBX,  2,  1, HWA,                                 NA,    0 ) \
FLAG(  87,  0, EBX,  3,  1, SCALABLE_MCA,                        NA,    0 ) \
FLAG(  87,  0, EBX,  4,  1, PFEH_SUPPORT_PRESENT,                NA,    0 ) \
FIELD( 87,  0, ECX,  0, 32, POWER_SAMPLE_TIME_RATIO,             NA,    0 ) \
FLAG(  87,  0, EDX,  0,  1, TS,                                  NA,    0 ) \
FLAG(  87,  0, EDX,  1,  1, FID,                                 NA,    0 ) \
FLAG(  87,  0, EDX,  2,  1, VID,                                 NA,    0 ) \
FLAG(  87,  0, EDX,  3,  1, TTP,                                 NA,    0 ) \
FLAG(  87,  0, EDX,  4,  1, LEAF87_TM,                           NA,    0 ) \
FLAG(  87,  0, EDX,  5,  1, STC,                                 NA,    0 ) \
FLAG(  87,  0, EDX,  6,  1, 100MHZSTEPS,                         NA,    0 ) \
FLAG(  87,  0, EDX,  7,  1, HWPSTATE,                            NA,    0 ) \
FLAG(  87,  0, EDX,  8,  1, TSC_INVARIANT,                       NA,    0 ) \
FLAG(  87,  0, EDX,  9,  1, CORE_PERF_BOOST,                     NA,    0 ) \
FLAG(  87,  0, EDX, 10,  1, EFFECTIVE_FREQUENCY,                 NA,    0 ) \
FLAG(  87,  0, EDX, 11,  1, PROC_FEEDBACK_INTERFACE,             NA,    0 ) \
FLAG(  87,  0, EDX, 12,  1, PROC_POWER_REPORTING,                NA,    0 ) \
FLAG(  87,  0, EDX, 13,  1, CONNECTED_STANDBY,                   NA,    0 ) \
FLAG(  87,  0, EDX, 14,  1, RAPL,                                NA,    0 ) \
FLAG(  87,  0, EDX, 15,  1, FAST_CPPC,                           NA,    0 )

/*    LEVEL, REG, POS, SIZE, NAME,                          MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_88                                           \
FIELD( 88,  0, EAX,  0,  8, PHYS_BITS,                           YES,   4 ) \
FIELD( 88,  0, EAX,  8,  8, VIRT_BITS,                           YES,   4 ) \
FIELD( 88,  0, EAX, 16,  8, GUEST_PHYS_ADDR_SZ,                  YES,   8 ) \
FLAG(  88,  0, EBX,  0,  1, CLZERO,                              YES,  14 ) \
FLAG(  88,  0, EBX,  1,  1, IRPERF,                              NO,    0 ) \
FLAG(  88,  0, EBX,  2,  1, XSAVE_ERR_PTR,                       NO,    0 ) \
FLAG(  88,  0, EBX,  3,  1, INVLPGB,                             NO,    0 ) \
FLAG(  88,  0, EBX,  4,  1, RDPRU,                               NO,    0 ) \
FLAG(  88,  0, EBX,  6,  1, MBE,                                 NO,    0 ) \
FLAG(  88,  0, EBX,  8,  1, MCOMMIT,                             NO,    0 ) \
FLAG(  88,  0, EBX,  9,  1, WBNOINVD,                            YES,  17 ) \
FLAG(  88,  0, EBX, 10,  1, LBREXTN,                             NO,    0 ) \
FLAG(  88,  0, EBX, 12,  1, LEAF88_IBPB,                         ANY,   9 ) \
FLAG(  88,  0, EBX, 13,  1, WBINVD_INT,                          NO,    0 ) \
FLAG(  88,  0, EBX, 14,  1, LEAF88_IBRS,                         ANY,  20 ) \
FLAG(  88,  0, EBX, 15,  1, LEAF88_STIBP,                        NO,    0 ) \
FLAG(  88,  0, EBX, 16,  1, LEAF88_IBRS_ALWAYS,                  YES,  20 ) \
FLAG(  88,  0, EBX, 17,  1, LEAF88_STIBP_ALWAYS,                 NO,    0 ) \
FLAG(  88,  0, EBX, 18,  1, LEAF88_PREFER_IBRS,                  YES,  20 ) \
FLAG(  88,  0, EBX, 19,  1, LEAF88_IBRS_SAME_MODE,               YES,  20 ) \
FLAG(  88,  0, EBX, 20,  1, LMSLE_UNSUPPORTED,                   NO,    0 ) \
FLAG(  88,  0, EBX, 23,  1, PPIN,                                NO,    0 ) \
FLAG(  88,  0, EBX, 24,  1, LEAF88_SSBD_SPEC_CTRL,               YES,  20 ) \
FLAG(  88,  0, EBX, 25,  1, LEAF88_SSBD_VIRT_SPEC_CTRL,          NO,    0 ) \
FLAG(  88,  0, EBX, 26,  1, LEAF88_SSBD_NOT_NEEDED,              NO,    0 ) \
FLAG(  88,  0, EBX, 27,  1, CPPC,                                NO,    0 ) \
FLAG(  88,  0, EBX, 28,  1, LEAF88_PSFD,                         YES,  20 ) \
FLAG(  88,  0, EBX, 29,  1, BTC_NO,                              NO,    0 ) \
FIELD( 88,  0, ECX,  0,  8, LEAF88_CORE_COUNT,                   YES,   4 ) \
FIELD( 88,  0, ECX, 12,  4, APICID_COREID_SIZE,                  YES,   7 ) \
FIELD( 88,  0, ECX, 16,  2, PERFTSC_SIZE,                        NO,    0 ) \
FIELD( 88,  0, EDX,  0, 16, INVLPGB_MAX,                         NO,    0 ) \
FIELD( 88,  0, EDX, 16,  8, RDPRU_MAX,                           NO,    0 )

#define CPUID_8A_EDX_11 \
FLAG(  8A,  0, EDX, 11,  1, SVMEDX_RSVD1,                        NO,    0 )
#define CPUID_8A_EDX_14 \
FLAG(  8A,  0, EDX, 14,  1, SVMEDX_RSVD2,                        NO,    0 )

/*    LEVEL, REG, POS, SIZE, NAME,                          MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_8A                                           \
FIELD( 8A,  0, EAX,  0,  8, SVM_REVISION,                        YES,   4 ) \
FLAG(  8A,  0, EAX,  8,  1, SVM_HYPERVISOR,                      NO,    0 ) \
FIELD( 8A,  0, EAX,  9, 23, SVMEAX_RSVD,                         NO,    0 ) \
FIELD( 8A,  0, EBX,  0, 32, SVM_NUM_ASIDS,                       YES,   7 ) \
FIELD( 8A,  0, ECX,  0, 32, SVMECX_RSVD,                         NO,    0 ) \
FLAG(  8A,  0, EDX,  0,  1, SVM_NPT,                             YES,   7 ) \
FLAG(  8A,  0, EDX,  1,  1, SVM_LBR,                             NO,    0 ) \
FLAG(  8A,  0, EDX,  2,  1, SVM_LOCK,                            ANY,   7 ) \
FLAG(  8A,  0, EDX,  3,  1, SVM_NRIP,                            YES,   7 ) \
FLAG(  8A,  0, EDX,  4,  1, SVM_TSC_RATE_MSR,                    NO,    0 ) \
FLAG(  8A,  0, EDX,  5,  1, SVM_VMCB_CLEAN,                      YES,   7 ) \
FLAG(  8A,  0, EDX,  6,  1, SVM_FLUSH_BY_ASID,                   YES,   7 ) \
FLAG(  8A,  0, EDX,  7,  1, SVM_DECODE_ASSISTS,                  YES,   7 ) \
FIELD( 8A,  0, EDX,  8,  2, SVMEDX_RSVD0,                        NO,    0 ) \
FLAG(  8A,  0, EDX, 10,  1, SVM_PAUSE_FILTER,                    NO,    0 ) \
CPUID_8A_EDX_11 \
FLAG(  8A,  0, EDX, 12,  1, SVM_PAUSE_THRESHOLD,                 NO,    0 ) \
FLAG(  8A,  0, EDX, 13,  1, SVM_AVIC,                            NO,    0 ) \
CPUID_8A_EDX_14 \
FLAG(  8A,  0, EDX, 15,  1, SVM_V_VMSAVE_VMLOAD,                 NO,    0 ) \
FLAG(  8A,  0, EDX, 16,  1, SVM_VGIF,                            NO,    0 ) \
FLAG(  8A,  0, EDX, 17,  1, SVM_GMET,                            YES,  17 ) \
FLAG(  8A,  0, EDX, 18,  1, SVM_x2AVIC,                          NO,    0 ) \
FLAG(  8A,  0, EDX, 19,  1, SVM_SSS,                             YES,  20 ) \
FLAG(  8A,  0, EDX, 20,  1, SVM_GUEST_SPEC_CTRL,                 NO,    0 ) \
FLAG(  8A,  0, EDX, 21,  1, SVM_NON_WRITEABLE_PT,                NO,    0 ) \
FLAG(  8A,  0, EDX, 23,  1, SVM_HOST_MCE_OVERRIDE,               NO,    0 ) \
FLAG(  8A,  0, EDX, 24,  1, SVM_TLB_CTL,                         NO,    0 ) \
FLAG(  8A,  0, EDX, 25,  1, SVM_NMI_VIRT,                        NO,    0 ) \
FLAG(  8A,  0, EDX, 26,  1, SVM_IBS_VIRT,                        NO,    0 ) \
FLAG(  8A,  0, EDX, 27,  1, SVM_EXTLVT_OFFSET_FAULT,             NO,    0 ) \
FLAG(  8A,  0, EDX, 28,  1, SVM_VMCB_ADDR_CHK,                   NO,    0 ) \
FLAG(  8A,  0, EDX, 29,  1, SVM_BUS_LOCK_THRESHOLD,              NO,    0 ) \
FLAG(  8A,  0, EDX, 30,  1, SVM_IDLE_HLT,                        NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_819                                          \
FIELD(819,  0, EAX,  0, 12, L1_ITLB_ENTRIES_1G_PGS,              NA,    0 ) \
FIELD(819,  0, EAX, 12,  4, L1_ITLB_ASSOC_1G_PGS,                NA,    0 ) \
FIELD(819,  0, EAX, 16, 12, L1_DTLB_ENTRIES_1G_PGS,              NA,    0 ) \
FIELD(819,  0, EAX, 28,  4, L1_DTLB_ASSOC_1G_PGS,                NA,    0 ) \
FIELD(819,  0, EBX,  0, 12, L2_ITLB_ENTRIES_1G_PGS,              NA,    0 ) \
FIELD(819,  0, EBX, 12,  4, L2_ITLB_ASSOC_1G_PGS,                NA,    0 ) \
FIELD(819,  0, EBX, 16, 12, L2_DTLB_ENTRIES_1G_PGS,              NA,    0 ) \
FIELD(819,  0, EBX, 28,  4, L2_DTLB_ASSOC_1G_PGS,                NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_81A                                          \
FLAG( 81A,  0, EAX,  0,  1, FP128,                               NA,    0 ) \
FLAG( 81A,  0, EAX,  1,  1, MOVU,                                NA,    0 ) \
FLAG( 81A,  0, EAX,  2,  1, FP256,                               NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_81B                                          \
FLAG( 81B,  0, EAX,  0,  1, IBS_FFV,                             NA,    0 ) \
FLAG( 81B,  0, EAX,  1,  1, IBS_FETCHSAM,                        NA,    0 ) \
FLAG( 81B,  0, EAX,  2,  1, IBS_OPSAM,                           NA,    0 ) \
FLAG( 81B,  0, EAX,  3,  1, RW_OPCOUNT,                          NA,    0 ) \
FLAG( 81B,  0, EAX,  4,  1, OPCOUNT,                             NA,    0 ) \
FLAG( 81B,  0, EAX,  5,  1, BRANCH_TARGET_ADDR,                  NA,    0 ) \
FLAG( 81B,  0, EAX,  6,  1, OPCOUNT_EXT,                         NA,    0 ) \
FLAG( 81B,  0, EAX,  7,  1, RIP_INVALID_CHECK,                   NA,    0 ) \
FLAG( 81B,  0, EAX,  8,  1, OP_BRN_FUSE,                         NA,    0 ) \
FLAG( 81B,  0, EAX,  9,  1, IBS_FETCH_CTL_EXTD,                  NA,    0 ) \
FLAG( 81B,  0, EAX, 10,  1, IBS_OP_DATA4,                        NA,    0 ) \
FLAG( 81B,  0, EAX, 11,  1, IBS_FETCH_OP,                        NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_81C                                          \
FLAG( 81C,  0, EAX,  0,  1, LWP_AVAIL,                           NA,    0 ) \
FLAG( 81C,  0, EAX,  1,  1, LWP_VAL_AVAIL,                       NA,    0 ) \
FLAG( 81C,  0, EAX,  2,  1, LWP_IRE_AVAIL,                       NA,    0 ) \
FLAG( 81C,  0, EAX,  3,  1, LWP_BRE_AVAIL,                       NA,    0 ) \
FLAG( 81C,  0, EAX,  4,  1, LWP_DME_AVAIL,                       NA,    0 ) \
FLAG( 81C,  0, EAX,  5,  1, LWP_CNH_AVAIL,                       NA,    0 ) \
FLAG( 81C,  0, EAX,  6,  1, LWP_RNH_AVAIL,                       NA,    0 ) \
FLAG( 81C,  0, EAX, 29,  1, LWP_CONT_AVAIL,                      NA,    0 ) \
FLAG( 81C,  0, EAX, 30,  1, LWP_PTSC_AVAIL,                      NA,    0 ) \
FLAG( 81C,  0, EAX, 31,  1, LWP_INT_AVAIL,                       NA,    0 ) \
FIELD(81C,  0, EBX,  0,  8, LWP_CB_SIZE,                         NA,    0 ) \
FIELD(81C,  0, EBX,  8,  8, LWP_EVENT_SIZE,                      NA,    0 ) \
FIELD(81C,  0, EBX, 16,  8, LWP_MAX_EVENTS,                      NA,    0 ) \
FIELD(81C,  0, EBX, 24,  8, LWP_EVENT_OFFSET,                    NA,    0 ) \
FIELD(81C,  0, ECX,  0,  4, LWP_LATENCY_MAX,                     NA,    0 ) \
FLAG( 81C,  0, ECX,  5,  1, LWP_DATA_ADDR_VALID,                 NA,    0 ) \
FIELD(81C,  0, ECX,  6,  3, LWP_LATENCY_ROUND,                   NA,    0 ) \
FIELD(81C,  0, ECX,  9,  7, LWP_VERSION,                         NA,    0 ) \
FIELD(81C,  0, ECX, 16,  8, LWP_MIN_BUF_SIZE,                    NA,    0 ) \
FLAG( 81C,  0, ECX, 28,  1, LWP_BRANCH_PRED,                     NA,    0 ) \
FLAG( 81C,  0, ECX, 29,  1, LWP_IP_FILTERING,                    NA,    0 ) \
FLAG( 81C,  0, ECX, 30,  1, LWP_CACHE_LEVEL,                     NA,    0 ) \
FLAG( 81C,  0, ECX, 31,  1, LWP_CACHE_LATENCY,                   NA,    0 ) \
FLAG( 81C,  0, EDX,  0,  1, LWP_SUPPORTED,                       NA,    0 ) \
FLAG( 81C,  0, EDX,  1,  1, LWP_VAL_SUPPORTED,                   NA,    0 ) \
FLAG( 81C,  0, EDX,  2,  1, LWP_IRE_SUPPORTED,                   NA,    0 ) \
FLAG( 81C,  0, EDX,  3,  1, LWP_BRE_SUPPORTED,                   NA,    0 ) \
FLAG( 81C,  0, EDX,  4,  1, LWP_DME_SUPPORTED,                   NA,    0 ) \
FLAG( 81C,  0, EDX,  5,  1, LWP_CNH_SUPPORTED,                   NA,    0 ) \
FLAG( 81C,  0, EDX,  6,  1, LWP_RNH_SUPPORTED,                   NA,    0 ) \
FLAG( 81C,  0, EDX, 29,  1, LWP_CONT_SUPPORTED,                  NA,    0 ) \
FLAG( 81C,  0, EDX, 30,  1, LWP_PTSC_SUPPORTED,                  NA,    0 ) \
FLAG( 81C,  0, EDX, 31,  1, LWP_INT_SUPPORTED,                   NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_81D                                          \
FIELD(81D,  0, EAX,  0,  5, LEAF81D_CACHE_TYPE,                  NA,    0 ) \
FIELD(81D,  0, EAX,  5,  3, LEAF81D_CACHE_LEVEL,                 NA,    0 ) \
FLAG( 81D,  0, EAX,  8,  1, LEAF81D_CACHE_SELF_INIT,             NA,    0 ) \
FLAG( 81D,  0, EAX,  9,  1, LEAF81D_CACHE_FULLY_ASSOC,           NA,    0 ) \
FIELD(81D,  0, EAX, 14, 12, LEAF81D_NUM_SHARING_CACHE,           NA,    0 ) \
FIELD(81D,  0, EBX,  0, 12, LEAF81D_CACHE_LINE_SIZE,             NA,    0 ) \
FIELD(81D,  0, EBX, 12, 10, LEAF81D_CACHE_PHYS_PARTITIONS,       NA,    0 ) \
FIELD(81D,  0, EBX, 22, 10, LEAF81D_CACHE_WAYS,                  NA,    0 ) \
FIELD(81D,  0, ECX,  0, 32, LEAF81D_CACHE_NUM_SETS,              NA,    0 ) \
FLAG( 81D,  0, EDX,  0,  1, LEAF81D_CACHE_WBINVD,                NA,    0 ) \
FLAG( 81D,  0, EDX,  1,  1, LEAF81D_CACHE_INCLUSIVE,             NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_81E                                          \
FIELD(81E,  0, EAX,  0, 32, EXTENDED_APICID,                     NA,    0 ) \
FIELD(81E,  0, EBX,  0,  8, COMPUTE_UNIT_ID,                     NA,    0 ) \
FIELD(81E,  0, EBX,  8,  2, CORES_PER_COMPUTE_UNIT,              NA,    0 ) \
FIELD(81E,  0, ECX,  0,  8, NODEID_VAL,                          NA,    0 ) \
FIELD(81E,  0, ECX,  8,  3, NODES_PER_PKG,                       NA,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_81F                                          \
FLAG( 81F,  0, EAX,  0,  1, SME,                                 NO,    0 ) \
FLAG( 81F,  0, EAX,  1,  1, SEV,                                 YES,  17 ) \
FLAG( 81F,  0, EAX,  2,  1, PAGE_FLUSH_MSR,                      NO,    0 ) \
FLAG( 81F,  0, EAX,  3,  1, SEV_ES,                              YES,  17 ) \
FLAG( 81F,  0, EAX,  4,  1, SEV_SNP,                             YES, FUT ) \
FLAG( 81F,  0, EAX,  5,  1, VMPL,                                YES, FUT ) \
FLAG( 81F,  0, EAX,  6,  1, RMPQUERY,                            NO,    0 ) \
FLAG( 81F,  0, EAX,  7,  1, VMPL_SSS,                            NO,    0 ) \
FLAG( 81F,  0, EAX,  8,  1, SECURE_TSC,                          NO,    0 ) \
FLAG( 81F,  0, EAX,  9,  1, TSC_AUX_VIRT,                        YES,  20 ) \
FLAG( 81F,  0, EAX, 10,  1, SEV_HEC,                             NO,    0 ) \
FLAG( 81F,  0, EAX, 11,  1, SEV_64BIT_REQ,                       NO,    0 ) \
FLAG( 81F,  0, EAX, 12,  1, SEV_RESTR_INJECTION,                 NO,    0 ) \
FLAG( 81F,  0, EAX, 13,  1, SEV_ALT_INJECTION,                   NO,    0 ) \
FLAG( 81F,  0, EAX, 14,  1, SEV_DEBUG_SWAP,                      NO,    0 ) \
FLAG( 81F,  0, EAX, 15,  1, SEV_NO_HOST_IBS,                     NO,    0 ) \
FLAG( 81F,  0, EAX, 16,  1, SEV_VTE,                             NO,    0 ) \
FLAG( 81F,  0, EAX, 17,  1, VMGEXIT_PARAMETER,                   NO,    0 ) \
FLAG( 81F,  0, EAX, 18,  1, VIRTUAL_MSR_TOM,                     NO,    0 ) \
FLAG( 81F,  0, EAX, 19,  1, SEV_IBS_VIRT,                        NO,    0 ) \
FLAG( 81F,  0, EAX, 24,  1, VMSA_REG_PROT,                       YES, FUT ) \
FLAG( 81F,  0, EAX, 25,  1, SMT_PROTECTION,                      NO,    0 ) \
FLAG( 81F,  0, EAX, 28,  1, SVSM_COMM_PAGE_MSR,                  NO,    0 ) \
FLAG( 81F,  0, EAX, 29,  1, NESTED_VIRT_SNP_MSR,                 NO,    0 ) \
FIELD(81F,  0, EBX,  0,  6, SME_PAGE_TABLE_BIT_NUM,              YES,  17 ) \
FIELD(81F,  0, EBX,  6,  6, SME_PHYS_ADDR_SPACE_REDUCTION,       NO,    0 ) \
FIELD(81F,  0, EBX, 12,  4, NUM_VMPL,                            YES, FUT ) \
FIELD(81F,  0, ECX,  0, 32, NUM_ENCRYPTED_GUESTS,                NO,    0 ) \
FIELD(81F,  0, EDX,  0, 32, SEV_MIN_ASID,                        NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_820                                          \
FLAG( 820,  0, EBX,  1,  1, LEAF820_MBE,                         NO,    0 ) \
FLAG( 820,  0, EBX,  2,  1, LEAF820_SMBE,                        NO,    0 ) \
FLAG( 820,  0, EBX,  3,  1, LEAF820_EVT_CFG,                     NO,    0 ) \
FLAG( 820,  0, EBX,  4,  1, LEAF820_L3RR,                        NO,    0 ) \
FIELD(820,  1, EAX,  0, 32, CAPACITY_MASK_LEN,                   NO,    0 ) \
FIELD(820,  1, EDX,  0, 32, NUM_SERVICE_CLASSES,                 NO,    0 ) \
FIELD(820,  2, EAX,  0, 32, SMBE_LENGTH,                         NO,    0 ) \
FIELD(820,  2, EDX,  0, 32, COS_MAX,                             NO,    0 ) \
FIELD(820,  3, EBX,  0,  8, NUM_BANDWIDTH_EVENTS,                NO,    0 ) \
FLAG( 820,  3, ECX,  0,  1, L3_CACHE_LCL_BW_FILL,                NO,    0 ) \
FLAG( 820,  3, ECX,  1,  1, L3_CACHE_RMT_BW_FILL,                NO,    0 ) \
FLAG( 820,  3, ECX,  2,  1, L3_CACHE_LCL_BW_NT_WRITE,            NO,    0 ) \
FLAG( 820,  3, ECX,  3,  1, L3_CACHE_RMT_BW_NT_WRITE,            NO,    0 ) \
FLAG( 820,  3, ECX,  4,  1, L3_CACHE_LCL_SLOW_BW_FILL,           NO,    0 ) \
FLAG( 820,  3, ECX,  5,  1, L3_CACHE_RMT_SLOW_BW_FILL,           NO,    0 ) \
FLAG( 820,  3, ECX,  6,  1, L3_CACHE_BW_VIC,                     NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_821                                          \
FLAG( 821,  0, EAX,  0,  1, NO_NESTED_DATA_BP,                   NO,    0 ) \
FLAG( 821,  0, EAX,  1,  1, NON_SERIALIZING_FSGSBASE,            NO,    0 ) \
FLAG( 821,  0, EAX,  2,  1, ALWAYS_SERIALIZING_LFENCE,           YES,  19 ) \
FLAG( 821,  0, EAX,  3,  1, SMM_PGCFG_LOCK,                      NO,    0 ) \
FLAG( 821,  0, EAX,  6,  1, NULL_SELECTOR_CLEARS_BASE,           NO,    0 ) \
FLAG( 821,  0, EAX,  7,  1, UPPER_ADDRESS_IGNORE,                YES,  20 ) \
FLAG( 821,  0, EAX,  8,  1, AUTOMATIC_IBRS,                      YES,  20 ) \
FLAG( 821,  0, EAX,  9,  1, NO_SMMCTL_MSR,                       NO,    0 ) \
FLAG( 821,  0, EAX, 10,  1, AMD_FAST_SHORT_STOSB,                YES,  20 ) \
FLAG( 821,  0, EAX, 11,  1, AMD_FAST_SHORT_CMPSB,                YES,  20 ) \
FLAG( 821,  0, EAX, 13,  1, PREFETCHCTL_MSR,                     NO,    0 ) \
FLAG( 821,  0, EAX, 17,  1, CPL3_CPUID_GP,                       NO,    0 ) \
FLAG( 821,  0, EAX, 18,  1, EPSF,                                NO,    0 ) \
FLAG( 821,  0, EAX, 19,  1, FAST_REP_SCASB,                      YES,  22 ) \
FLAG( 821,  0, EAX, 20,  1, LEAF821_PREFETCHI,                   YES,  22 ) \
FIELD(821,  0, EBX,  0, 12, MICROCODE_PATCH_SIZE,                NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_822                                          \
FLAG( 822,  0, EAX,  0,  1, AMD_PERFMON_V2,                      NO,    0 ) \
FLAG( 822,  0, EAX,  1,  1, AMD_LBREXT_V2,                       NO,    0 ) \
FLAG( 822,  0, EAX,  2,  1, AMD_LBR_PMC_FREEZE,                  NO,    0 ) \
FIELD(822,  0, EBX,  0,  4, AMD_NUM_CORE_PMC,                    NO,    0 ) \
FIELD(822,  0, EBX,  4,  6, AMD_LBR_STACK_SIZE,                  NO,    0 ) \
FIELD(822,  0, EBX, 10,  6, AMD_NUM_DF_PMC,                      NO,    0 ) \
FIELD(822,  0, EBX, 16,  6, AMD_NUM_UMC_PMC,                     NO,    0 ) \
FIELD(822,  0, ECX,  0, 32, AMD_ACTIVE_UMC_PMC_MASK,             NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_823                                          \
FLAG( 823,  0, EAX,  0,  1, MEM_HMK,                             NO,    0 ) \
FIELD(823,  0, EBX,  0, 16, MEM_HMK_MAX_ENCR_KEYID,              NO,    0 )

/*    LEVEL, SUB-LEVEL, REG, POS, SIZE, NAME,               MON SUPP, HWV  */
#define CPUID_FIELD_DATA_LEVEL_826                                          \
FIELD(826,  0, EAX,  0,  5, AMD_TOPOLOGY_MASK_WIDTH,             NA,    0 ) \
FLAG( 826,  0, EAX, 29,  1, AMD_TOPOLOGY_EFFICIENCY_RANK,        NA,    0 ) \
FLAG( 826,  0, EAX, 30,  1, AMD_TOPOLOGY_HETEROGENEOUS_CORES,    NA,    0 ) \
FLAG( 826,  0, EAX, 31,  1, AMD_TOPOLOGY_ASYMMETRIC_CORES,       NA,    0 ) \
FIELD(826,  0, EBX,  0, 16, AMD_TOPOLOGY_CPUS_SHARING_LEVEL,     NA,    0 ) \
FIELD(826,  0, EBX, 16,  8, AMD_TOPOLOGY_POWER_RANKING,          NA,    0 ) \
FIELD(826,  0, EBX, 24,  4, AMD_TOPOLOGY_NATIVE_MODEL_ID,        NA,    0 ) \
FIELD(826,  0, EBX, 28,  4, AMD_TOPOLOGY_CORE_TYPE,              NA,    0 ) \
FIELD(826,  0, ECX,  0,  8, AMD_TOPOLOGY_LEVEL_NUMBER,           NA,    0 ) \
FIELD(826,  0, ECX,  8,  8, AMD_TOPOLOGY_LEVEL_TYPE,             NA,    0 ) \
FIELD(826,  0, EDX,  0, 32, AMD_TOPOLOGY_EXT_APIC_ID,            NA,    0 )

#define CPUID_FIELD_DATA                                              \
   CPUID_FIELD_DATA_LEVEL_0                                           \
   CPUID_FIELD_DATA_LEVEL_1                                           \
   CPUID_FIELD_DATA_LEVEL_2                                           \
   CPUID_FIELD_DATA_LEVEL_4                                           \
   CPUID_FIELD_DATA_LEVEL_5                                           \
   CPUID_FIELD_DATA_LEVEL_6                                           \
   CPUID_FIELD_DATA_LEVEL_7                                           \
   CPUID_FIELD_DATA_LEVEL_9                                           \
   CPUID_FIELD_DATA_LEVEL_A                                           \
   CPUID_FIELD_DATA_LEVEL_B                                           \
   CPUID_FIELD_DATA_LEVEL_D                                           \
   CPUID_FIELD_DATA_LEVEL_F                                           \
   CPUID_FIELD_DATA_LEVEL_10                                          \
   CPUID_FIELD_DATA_LEVEL_12                                          \
   CPUID_FIELD_DATA_LEVEL_14                                          \
   CPUID_FIELD_DATA_LEVEL_15                                          \
   CPUID_FIELD_DATA_LEVEL_16                                          \
   CPUID_FIELD_DATA_LEVEL_17                                          \
   CPUID_FIELD_DATA_LEVEL_18                                          \
   CPUID_FIELD_DATA_LEVEL_19                                          \
   CPUID_FIELD_DATA_LEVEL_1A                                          \
   CPUID_FIELD_DATA_LEVEL_1B                                          \
   CPUID_FIELD_DATA_LEVEL_1C                                          \
   CPUID_FIELD_DATA_LEVEL_1D                                          \
   CPUID_FIELD_DATA_LEVEL_1E                                          \
   CPUID_FIELD_DATA_LEVEL_1F                                          \
   CPUID_FIELD_DATA_LEVEL_20                                          \
   CPUID_FIELD_DATA_LEVEL_21                                          \
   CPUID_FIELD_DATA_LEVEL_23                                          \
   CPUID_FIELD_DATA_LEVEL_24                                          \
   CPUID_FIELD_DATA_LEVEL_400                                         \
   CPUID_FIELD_DATA_LEVEL_401                                         \
   CPUID_FIELD_DATA_LEVEL_402                                         \
   CPUID_FIELD_DATA_LEVEL_403                                         \
   CPUID_FIELD_DATA_LEVEL_404                                         \
   CPUID_FIELD_DATA_LEVEL_405                                         \
   CPUID_FIELD_DATA_LEVEL_406                                         \
   CPUID_FIELD_DATA_LEVEL_410                                         \
   CPUID_FIELD_DATA_LEVEL_80                                          \
   CPUID_FIELD_DATA_LEVEL_81                                          \
   CPUID_FIELD_DATA_LEVEL_82                                          \
   CPUID_FIELD_DATA_LEVEL_83                                          \
   CPUID_FIELD_DATA_LEVEL_84                                          \
   CPUID_FIELD_DATA_LEVEL_85                                          \
   CPUID_FIELD_DATA_LEVEL_86                                          \
   CPUID_FIELD_DATA_LEVEL_87                                          \
   CPUID_FIELD_DATA_LEVEL_88                                          \
   CPUID_FIELD_DATA_LEVEL_8A                                          \
   CPUID_FIELD_DATA_LEVEL_819                                         \
   CPUID_FIELD_DATA_LEVEL_81A                                         \
   CPUID_FIELD_DATA_LEVEL_81B                                         \
   CPUID_FIELD_DATA_LEVEL_81C                                         \
   CPUID_FIELD_DATA_LEVEL_81D                                         \
   CPUID_FIELD_DATA_LEVEL_81E                                         \
   CPUID_FIELD_DATA_LEVEL_81F                                         \
   CPUID_FIELD_DATA_LEVEL_820                                         \
   CPUID_FIELD_DATA_LEVEL_821                                         \
   CPUID_FIELD_DATA_LEVEL_822                                         \
   CPUID_FIELD_DATA_LEVEL_823                                         \
   CPUID_FIELD_DATA_LEVEL_826

/*
 * 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_<name>_MASK  == mask for feature/field
 * CPUID_<name>_SHIFT == offset of field
 *
 * e.g. - CPUID_VIRT_BITS_MASK  = 0xff00
 *      - CPUID_VIRT_BITS_SHIFT = 8
 */
#define VMW_BIT_MASK(shift)  (0xffffffffu >> (32 - shift))

#define FIELD(lvl, ecxIn, reg, bitpos, size, name, s, hwv)     \
   CPUID_##name##_SHIFT        = bitpos,                       \
   CPUID_##name##_MASK         = VMW_BIT_MASK(size) << bitpos, \
   CPUID_INTERNAL_SHIFT_##name = bitpos,                       \
   CPUID_INTERNAL_MASK_##name  = VMW_BIT_MASK(size) << bitpos, \
   CPUID_INTERNAL_REG_##name   = CPUID_REG_##reg,              \
   CPUID_INTERNAL_EAXIN_##name = CPUID_LEVEL_VAL_##lvl,        \
   CPUID_INTERNAL_ECXIN_##name = ecxIn,

#define FLAG FIELD

enum {
   /* Define data for every CPUID field we have */
   CPUID_FIELD_DATA
};
#undef VMW_BIT_MASK
#undef FIELD
#undef FLAG

/*
 * CPUID_MASK --
 * CPUID_SHIFT --
 * CPUID_ISSET --
 * CPUID_GET --
 * CPUID_SET --
 * CPUID_CLEAR --
 * CPUID_SETTO --
 *
 * Accessor macros for all CPUID consts/fields/flags.  Level and reg are not
 * required, but are used to force compile-time asserts which help verify that
 * the flag is being used on the right CPUID input and result register.
 *
 * Note: ASSERT_ON_COMPILE is duplicated rather than factored into its own
 * macro, because token concatenation does not work as expected if an input is
 * #defined (e.g. APIC) when macros are nested.  Also, compound statements
 * within parenthes is a GCC extension, so we must use runtime asserts with
 * other compilers.
 */

#if defined(__GNUC__) && !defined(__clang__)

#define CPUID_MASK(eaxIn, reg, flag)                                    \
   ({                                                                   \
      ASSERT_ON_COMPILE(eaxIn == CPUID_INTERNAL_EAXIN_##flag &&         \
              CPUID_REG_##reg == (CpuidReg)CPUID_INTERNAL_REG_##flag);  \
      CPUID_INTERNAL_MASK_##flag;                                       \
   })

#define CPUID_SHIFT(eaxIn, reg, flag)                                   \
   ({                                                                   \
      ASSERT_ON_COMPILE(eaxIn == CPUID_INTERNAL_EAXIN_##flag &&         \
              CPUID_REG_##reg == (CpuidReg)CPUID_INTERNAL_REG_##flag);  \
      CPUID_INTERNAL_SHIFT_##flag;                                      \
   })

#define CPUID_ISSET(eaxIn, reg, flag, data)                             \
   ({                                                                   \
      ASSERT_ON_COMPILE(eaxIn == CPUID_INTERNAL_EAXIN_##flag &&         \
              CPUID_REG_##reg == (CpuidReg)CPUID_INTERNAL_REG_##flag);  \
      (((data) & CPUID_INTERNAL_MASK_##flag) != 0);                     \
   })

#define CPUID_GET(eaxIn, reg, field, data)                              \
   ({                                                                   \
      ASSERT_ON_COMPILE(eaxIn == CPUID_INTERNAL_EAXIN_##field &&        \
              CPUID_REG_##reg == (CpuidReg)CPUID_INTERNAL_REG_##field); \
      (((uint32)(data) & CPUID_INTERNAL_MASK_##field) >>                \
       CPUID_INTERNAL_SHIFT_##field);                                   \
   })

#else

/*
 * CPUIDCheck --
 *
 * Return val after verifying parameters.
 */

static INLINE uint32
CPUIDCheck(int32 eaxIn, int32 eaxInCheck,
           CpuidReg reg, CpuidReg regCheck, uint32 val)
{
   ASSERT(eaxIn == eaxInCheck && reg == regCheck);
   return val;
}

#define CPUID_MASK(eaxIn, reg, flag)                                    \
   CPUIDCheck(eaxIn, CPUID_INTERNAL_EAXIN_##flag,                       \
              CPUID_REG_##reg, (CpuidReg)CPUID_INTERNAL_REG_##flag,     \
              CPUID_INTERNAL_MASK_##flag)

#define CPUID_SHIFT(eaxIn, reg, flag)                                   \
   CPUIDCheck(eaxIn, CPUID_INTERNAL_EAXIN_##flag,                       \
              CPUID_REG_##reg, (CpuidReg)CPUID_INTERNAL_REG_##flag,     \
              CPUID_INTERNAL_SHIFT_##flag)

#define CPUID_ISSET(eaxIn, reg, flag, data)                             \
   (CPUIDCheck(eaxIn, CPUID_INTERNAL_EAXIN_##flag,                      \
               CPUID_REG_##reg, (CpuidReg)CPUID_INTERNAL_REG_##flag,    \
               CPUID_INTERNAL_MASK_##flag & (data)) != 0)

#define CPUID_GET(eaxIn, reg, field, data)                              \
   CPUIDCheck(eaxIn, CPUID_INTERNAL_EAXIN_##field,                      \
              CPUID_REG_##reg, (CpuidReg)CPUID_INTERNAL_REG_##field,    \
              ((uint32)(data) & CPUID_INTERNAL_MASK_##field) >>         \
              CPUID_INTERNAL_SHIFT_##field)

#endif


#define CPUID_SET(eaxIn, reg, flag, dataPtr)                            \
   do {                                                                 \
      ASSERT_ON_COMPILE(                                                \
         (uint32)eaxIn   == (uint32)CPUID_INTERNAL_EAXIN_##flag &&      \
         CPUID_REG_##reg == (CpuidReg)CPUID_INTERNAL_REG_##flag);       \
      *(dataPtr) |= CPUID_INTERNAL_MASK_##flag;                         \
   } while (0)

#define CPUID_CLEAR(eaxIn, reg, flag, dataPtr)                          \
   do {                                                                 \
      ASSERT_ON_COMPILE(                                                \
         (uint32)eaxIn   == (uint32)CPUID_INTERNAL_EAXIN_##flag &&      \
         CPUID_REG_##reg == (CpuidReg)CPUID_INTERNAL_REG_##flag);       \
      *(dataPtr) &= ~CPUID_INTERNAL_MASK_##flag;                        \
   } while (0)

#define CPUID_SETTO(eaxIn, reg, field, dataPtr, val)                    \
   do {                                                                 \
      uint32 _v = val;                                                  \
      uint32 *_d = dataPtr;                                             \
      ASSERT_ON_COMPILE(                                                \
         (uint32)eaxIn   == (uint32)CPUID_INTERNAL_EAXIN_##field &&     \
         CPUID_REG_##reg == (CpuidReg)CPUID_INTERNAL_REG_##field);      \
      *_d = (*_d & ~CPUID_INTERNAL_MASK_##field) |                      \
         (_v << CPUID_INTERNAL_SHIFT_##field);                          \
      ASSERT(_v == (*_d & CPUID_INTERNAL_MASK_##field) >>               \
             CPUID_INTERNAL_SHIFT_##field);                             \
   } while (0)


/*
 * Definitions of various fields' values and more complicated
 * macros/functions for reading cpuid fields.
 */

#define CPUID_FAMILY_EXTENDED        15

/* 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            0x4
#define CPUID_FAMILY_K5              0x5
#define CPUID_FAMILY_K6              0x5
#define CPUID_FAMILY_K7              0x6
#define CPUID_FAMILY_K8              0xf
#define CPUID_FAMILY_K8L             0x10
#define CPUID_FAMILY_K8MOBILE        0x11
#define CPUID_FAMILY_LLANO           0x12
#define CPUID_FAMILY_BOBCAT          0x14
#define CPUID_FAMILY_BULLDOZER       0x15  // BD PD SR EX
#define CPUID_FAMILY_KYOTO           0x16  // Note: Jaguar microarch
#define CPUID_FAMILY_ZEN             0x17
#define CPUID_FAMILY_ZEN3            0x19

/* Effective VIA CPU Families */
#define CPUID_FAMILY_C7               6

/* Effective Hygon CPU Families. */
#define CPUID_FAMILY_DHYANA          0x18

/* 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_NEHALEM_1E        0x1e  // Lynnfield
#define CPUID_MODEL_NEHALEM_1F        0x1f  // Havendale
#define CPUID_MODEL_NEHALEM_25        0x25  // Westmere / Clarkdale
#define CPUID_MODEL_ATOM_26           0x26  // Lincroft
#define CPUID_MODEL_ATOM_27           0x27  // Saltwell
#define CPUID_MODEL_SANDYBRIDGE_2A    0x2a  // Sandybridge (desktop/mobile)
#define CPUID_MODEL_NEHALEM_2C        0x2c  // Westmere-EP
#define CPUID_MODEL_SANDYBRIDGE_2D    0x2d  // Sandybridge-EP
#define CPUID_MODEL_NEHALEM_2E        0x2e  // Nehalem-EX
#define CPUID_MODEL_NEHALEM_2F        0x2f  // Westmere-EX
#define CPUID_MODEL_ATOM_35           0x35  // Cloverview
#define CPUID_MODEL_ATOM_36           0x36  // Cedarview
#define CPUID_MODEL_ATOM_37           0x37  // Bay Trail
#define CPUID_MODEL_SANDYBRIDGE_3A    0x3a  // Ivy Bridge
#define CPUID_MODEL_HASWELL_3C        0x3c  // Haswell DT
#define CPUID_MODEL_BROADWELL_3D      0x3d  // Broadwell-Ult
#define CPUID_MODEL_SANDYBRIDGE_3E    0x3e  // Ivy Bridge-EP
#define CPUID_MODEL_HASWELL_3F        0x3f  // Haswell EP/EN/EX
#define CPUID_MODEL_HASWELL_45        0x45  // Haswell Ultrathin
#define CPUID_MODEL_HASWELL_46        0x46  // Haswell (Crystal Well)
#define CPUID_MODEL_BROADWELL_47      0x47  // Broadwell (Denlow)
#define CPUID_MODEL_ATOM_4A           0x4a  // Future Silvermont
#define CPUID_MODEL_ATOM_4C           0x4c  // Airmont
#define CPUID_MODEL_ATOM_4D           0x4d  // Avoton
#define CPUID_MODEL_SKYLAKE_4E        0x4e  // Skylake-Y / Kaby Lake U/Y ES
#define CPUID_MODEL_BROADWELL_4F      0x4f  // Broadwell EP/EN/EX
#define CPUID_MODEL_SKYLAKE_55        0x55  // Skylake EP/EN/EX
#define CPUID_MODEL_BROADWELL_56      0x56  // Broadwell DE
#define CPUID_MODEL_KNL_57            0x57  // Knights Landing
#define CPUID_MODEL_ATOM_5A           0x5a  // Future Silvermont
#define CPUID_MODEL_ATOM_5D           0x5d  // Future Silvermont
#define CPUID_MODEL_SKYLAKE_5E        0x5e  // Skylake-S / Kaby Lake S/H ES
#define CPUID_MODEL_ATOM_5F           0x5f  // Denverton
#define CPUID_MODEL_ATOM_86           0x86  // Snow Ridge
#define CPUID_MODEL_CANNONLAKE_66     0x66  // Cannon Lake
#define CPUID_MODEL_ICELAKE_7E        0x7e  // Ice Lake U/Y
#define CPUID_MODEL_ICELAKE_6A        0x6a  // Ice Lake SP (ICX)
#define CPUID_MODEL_ICELAKE_6C        0x6c  // Ice Lake D
#define CPUID_MODEL_LAKEFIELD_8A      0x8a  // Lakefield
#define CPUID_MODEL_TIGERLAKE_8C      0x8c  // Tiger Lake UP3/UP4/H35
#define CPUID_MODEL_TIGERLAKE_8D      0x8d  // Tiger Lake H81
#define CPUID_MODEL_SAPPHIRERAPIDS_8F 0x8f  // Sapphire Rapids
#define CPUID_MODEL_KNM_85            0x85  // Knights Mill
#define CPUID_MODEL_KABYLAKE_8E       0x8e  // Kaby Lake U/Y QS
#define CPUID_MODEL_ALDERLAKE_97      0x97  // Alder Lake-S
#define CPUID_MODEL_ALDERLAKE_9A      0x9a  // Alder Lake-P
#define CPUID_MODEL_KABYLAKE_9E       0x9e  // Kaby Lake S/H QS
#define CPUID_MODEL_COMETLAKE_A5      0xa5  // Comet Lake S
#define CPUID_MODEL_COMETLAKE_A6      0xa6  // Comet Lake U
#define CPUID_MODEL_ROCKETLAKE_A7     0xa7  // Rocket Lake S
#define CPUID_MODEL_RAPTORLAKE_B7     0xb7  // Raptor Lake S/HX B-0
#define CPUID_MODEL_RAPTORLAKE_BA     0xba  // Raptor Lake H/P/PX J-0, U Q-0
#define CPUID_MODEL_RAPTORLAKE_BF     0xbf  // Raptor Lake S/HX C-0

/* Intel stepping information */
#define CPUID_STEPPING_KABYLAKE_ES     0x8  // Kaby Lake S/H/U/Y ES
#define CPUID_STEPPING_COFFEELAKE_A    0xA  // Coffee Lake U/S/H
#define CPUID_STEPPING_COFFEELAKE_D    0xD  // Last Coffee Lake stepping
#define CPUID_STEPPING_CASCADELAKE_A   0x5  // Cascade Lake A-step
#define CPUID_STEPPING_CASCADELAKE_B1  0x7  // Cascade Lake B1-step
#define CPUID_STEPPING_WHISKEYLAKE     0xB  // Whiskey Lake U
#define CPUID_STEPPING_AMBERLAKE       0xC  // Amber Lake Y
#define CPUID_STEPPING_COOPERLAKE      0xA  // Cooper Lake-SP

#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 (Opteron & Phenom)
#define CPUID_MODEL_SHANGHAI_04       0x04 // Shanghai RB
#define CPUID_MODEL_SHANGHAI_05       0x05 // Shanghai BL
#define CPUID_MODEL_SHANGHAI_06       0x06 // Shanghai DA
#define CPUID_MODEL_ISTANBUL_MAGNY_08 0x08 // Istanbul (6 core) & Magny-cours (12) HY
#define CPUID_MODEL_ISTANBUL_MAGNY_09 0x09 // HY - G34 package
#define CPUID_MODEL_PHAROAH_HOUND_0A  0x0A // Pharoah Hound
#define CPUID_MODEL_PILEDRIVER_1F     0x1F // Max piledriver model defined in BKDG
#define CPUID_MODEL_PILEDRIVER_10     0x10 // family == CPUID_FAMILY_BULLDOZER
#define CPUID_MODEL_PILEDRIVER_02     0x02 // family == CPUID_FAMILY_BULLDOZER
#define CPUID_MODEL_OPTERON_REVF_41   0x41 // family == CPUID_FAMILY_K8
#define CPUID_MODEL_KYOTO_00          0x00 // family == CPUID_FAMILY_KYOTO
#define CPUID_MODEL_STEAMROLLER_3F    0x3F // Max Steamroller model defined in BKDG
#define CPUID_MODEL_STEAMROLLER_30    0x30 // family == CPUID_FAMILY_BULLDOZER
#define CPUID_MODEL_EXCAVATOR_60      0x60 // family == CPUID_FAMILY_BULLDOZER
#define CPUID_MODEL_EXCAVATOR_6F      0x6F // Max Excavator model defined in BKDG
#define CPUID_MODEL_ZEN_00            0x00 // family == CPUID_FAMILY_ZEN
#define CPUID_MODEL_ZEN_NAPLES_01     0x01 // family == CPUID_FAMILY_ZEN
#define CPUID_MODEL_ZEN_1F            0x1F // Max Zen model defined in BKDG
#define CPUID_MODEL_ZEN2_30           0x30 // family == CPUID_FAMILY_ZEN
#define CPUID_MODEL_ZEN2_3F           0x3F // Max Zen2 model
#define CPUID_MODEL_ZEN2_70           0x70 // Ryzen3: family Zen, model Zen2
#define CPUID_MODEL_ZEN2_7F           0x7F // Ryzen3: max model
#define CPUID_MODEL_ZEN3_00           0x00 // family == CPUID_FAMILY_ZEN3
#define CPUID_MODEL_ZEN3_0F           0x0F // Max Zen3 model

/* AMD stepping information */
#define CPUID_STEPPING_ZEN_NAPLES_B2  0x02 // Zen Naples ZP-B2

/* VIA model information */
#define CPUID_MODEL_NANO                15 // Isaiah

/* Hygon model information. */
#define CPUID_MODEL_DHYANA_A             0 // Dhyana A

/*
 *----------------------------------------------------------------------
 *
 * CPUID_IsVendor{AMD,Intel,VIA,Hygon} --
 *
 *      Determines if the vendor string in cpuid id0 is from
 *      {AMD,Intel,VIA,Hygon}.
 *
 * Results:
 *      True iff vendor string is CPUID_{AMD,INTEL,VIA,HYGON}_VENDOR_STRING
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
static INLINE Bool
CPUID_IsRawVendor(const CPUIDRegs *id0, const char* vendor)
{
   // hard to get strcmp() in some environments, so do it in the raw
   return (id0->ebx == *(const uint32 *)(uintptr_t) (vendor + 0) &&
           id0->ecx == *(const uint32 *)(uintptr_t) (vendor + 4) &&
           id0->edx == *(const uint32 *)(uintptr_t) (vendor + 8));
}

static INLINE Bool
CPUID_IsVendorAMD(const CPUIDRegs *id0)
{
   return CPUID_IsRawVendor(id0, CPUID_AMD_VENDOR_STRING);
}

static INLINE Bool
CPUID_IsVendorIntel(const CPUIDRegs *id0)
{
   return CPUID_IsRawVendor(id0, CPUID_INTEL_VENDOR_STRING);
}

static INLINE Bool
CPUID_IsVendorVIA(const CPUIDRegs *id0)
{
   return CPUID_IsRawVendor(id0, CPUID_VIA_VENDOR_STRING);
}

static INLINE Bool
CPUID_IsVendorHygon(const CPUIDRegs *id0)
{
   return CPUID_IsRawVendor(id0, CPUID_HYGON_VENDOR_STRING);
}

static INLINE uint32
CPUID_EFFECTIVE_FAMILY(uint32 v) /* %eax from CPUID with %eax=1. */
{
   uint32 f = CPUID_GET(1, EAX, FAMILY, v);
   return f != CPUID_FAMILY_EXTENDED ? f : f +
      CPUID_GET(1, EAX, EXTENDED_FAMILY, v);
}

/*
 * 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. */
{
   uint32 m = CPUID_GET(1, EAX, MODEL, v);
   uint32 em = CPUID_GET(1, EAX, EXTENDED_MODEL, v);
   return m + (em << 4);
}

static INLINE uint32
CPUID_EFFECTIVE_STEPPING(uint32 v) /* %eax from CPUID with %eax=1. */
{
   return CPUID_GET(1, EAX, STEPPING, v);
}

/*
 * 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, Lynnfield, Clarkdale.
 */
static INLINE Bool
CPUID_UARCH_IS_NEHALEM(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   uint32 effectiveModel = CPUID_EFFECTIVE_MODEL(v);

   return CPUID_FAMILY_IS_P6(v) &&
          (effectiveModel == CPUID_MODEL_NEHALEM_1A ||
           effectiveModel == CPUID_MODEL_NEHALEM_1E ||
           effectiveModel == CPUID_MODEL_NEHALEM_1F ||
           effectiveModel == CPUID_MODEL_NEHALEM_25 ||
           effectiveModel == CPUID_MODEL_NEHALEM_2C ||
           effectiveModel == CPUID_MODEL_NEHALEM_2E ||
           effectiveModel == CPUID_MODEL_NEHALEM_2F);
}


static INLINE Bool
CPUID_UARCH_IS_SANDYBRIDGE(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   uint32 effectiveModel = CPUID_EFFECTIVE_MODEL(v);

   return CPUID_FAMILY_IS_P6(v) &&
          (effectiveModel == CPUID_MODEL_SANDYBRIDGE_2A ||
           effectiveModel == CPUID_MODEL_SANDYBRIDGE_2D ||
           effectiveModel == CPUID_MODEL_SANDYBRIDGE_3E ||
           effectiveModel == CPUID_MODEL_SANDYBRIDGE_3A);
}


static INLINE Bool
CPUID_MODEL_IS_BROADWELL(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   uint32 effectiveModel = CPUID_EFFECTIVE_MODEL(v);

   return CPUID_FAMILY_IS_P6(v) &&
          (effectiveModel == CPUID_MODEL_BROADWELL_3D ||
           effectiveModel == CPUID_MODEL_BROADWELL_47 ||
           effectiveModel == CPUID_MODEL_BROADWELL_4F ||
           effectiveModel == CPUID_MODEL_BROADWELL_56);
}


static INLINE Bool
CPUID_MODEL_IS_HASWELL(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   uint32 effectiveModel = CPUID_EFFECTIVE_MODEL(v);

   return CPUID_FAMILY_IS_P6(v) &&
          (effectiveModel == CPUID_MODEL_HASWELL_3C ||
           effectiveModel == CPUID_MODEL_HASWELL_3F ||
           effectiveModel == CPUID_MODEL_HASWELL_45 ||
           effectiveModel == CPUID_MODEL_HASWELL_46);
}

static INLINE Bool
CPUID_MODEL_IS_CASCADELAKE(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_SKYLAKE_55 &&
          CPUID_EFFECTIVE_STEPPING(v) >= CPUID_STEPPING_CASCADELAKE_A &&
          CPUID_EFFECTIVE_STEPPING(v) <= CPUID_STEPPING_CASCADELAKE_B1;
}

static INLINE Bool
CPUID_MODEL_IS_COOPERLAKE(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_SKYLAKE_55 &&
          CPUID_EFFECTIVE_STEPPING(v) == CPUID_STEPPING_COOPERLAKE;
}

static INLINE Bool
CPUID_MODEL_IS_SKYLAKE(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_SKYLAKE_5E &&
            CPUID_EFFECTIVE_STEPPING(v) != CPUID_STEPPING_KABYLAKE_ES) ||
           (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_SKYLAKE_55 &&
            !CPUID_MODEL_IS_COOPERLAKE(v)                      &&
            !CPUID_MODEL_IS_CASCADELAKE(v))                            ||
           (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_SKYLAKE_4E &&
            CPUID_EFFECTIVE_STEPPING(v) != CPUID_STEPPING_KABYLAKE_ES));
}

static INLINE Bool
CPUID_MODEL_IS_COFFEELAKE(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_KABYLAKE_9E &&
            CPUID_EFFECTIVE_STEPPING(v) >= CPUID_STEPPING_COFFEELAKE_A &&
            CPUID_EFFECTIVE_STEPPING(v) <= CPUID_STEPPING_COFFEELAKE_D) ||
           (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_KABYLAKE_8E &&
            CPUID_EFFECTIVE_STEPPING(v) == CPUID_STEPPING_COFFEELAKE_A));
}

static INLINE Bool
CPUID_MODEL_IS_WHISKEYLAKE(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_KABYLAKE_8E &&
          CPUID_EFFECTIVE_STEPPING(v) == CPUID_STEPPING_WHISKEYLAKE;
}

static INLINE Bool
CPUID_MODEL_IS_COMETLAKE(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_COMETLAKE_A5 ||
           CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_COMETLAKE_A6);
}

static INLINE Bool
CPUID_MODEL_IS_AMBERLAKE(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_KABYLAKE_8E &&
          CPUID_EFFECTIVE_STEPPING(v) == CPUID_STEPPING_AMBERLAKE;
}

static INLINE Bool
CPUID_MODEL_IS_KABYLAKE(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_KABYLAKE_9E &&
            CPUID_EFFECTIVE_STEPPING(v) < CPUID_STEPPING_COFFEELAKE_A) ||
           (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_KABYLAKE_8E &&
            CPUID_EFFECTIVE_STEPPING(v) < CPUID_STEPPING_COFFEELAKE_A) ||
           (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_SKYLAKE_5E &&
            CPUID_EFFECTIVE_STEPPING(v) == CPUID_STEPPING_KABYLAKE_ES) ||
           (CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_SKYLAKE_4E &&
            CPUID_EFFECTIVE_STEPPING(v) == CPUID_STEPPING_KABYLAKE_ES));
}

static INLINE Bool
CPUID_MODEL_IS_CANNONLAKE(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_CANNONLAKE_66;
}

static INLINE Bool
CPUID_UARCH_IS_SKYLAKE(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   return CPUID_MODEL_IS_SKYLAKE(v)     ||
          CPUID_MODEL_IS_KABYLAKE(v)    ||
          CPUID_MODEL_IS_COFFEELAKE(v)  ||
          CPUID_MODEL_IS_WHISKEYLAKE(v) ||
          CPUID_MODEL_IS_COMETLAKE(v)   ||
          CPUID_MODEL_IS_AMBERLAKE(v)   ||
          CPUID_MODEL_IS_CASCADELAKE(v) ||
          CPUID_MODEL_IS_COOPERLAKE(v)  ||
          CPUID_MODEL_IS_CANNONLAKE(v);
}

static INLINE Bool
CPUID_MODEL_IS_ICELAKE(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_ICELAKE_7E ||
           CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_ICELAKE_6A ||
           CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_ICELAKE_6C);
}

static INLINE Bool
CPUID_MODEL_IS_TIGERLAKE(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_TIGERLAKE_8C ||
           CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_TIGERLAKE_8D);
}

static INLINE Bool
CPUID_MODEL_IS_ROCKETLAKE(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_ROCKETLAKE_A7);
}

static INLINE Bool
CPUID_UARCH_IS_ICELAKE(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   return CPUID_MODEL_IS_ICELAKE(v)  ||
          CPUID_MODEL_IS_TIGERLAKE(v) ||
          CPUID_MODEL_IS_ROCKETLAKE(v);
}

static INLINE Bool
CPUID_MODEL_IS_ALDERLAKE(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_ALDERLAKE_97 ||
           CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_ALDERLAKE_9A);
}

static INLINE Bool
CPUID_MODEL_IS_RAPTORLAKE(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_RAPTORLAKE_B7 ||
           CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_RAPTORLAKE_BF ||
           CPUID_EFFECTIVE_MODEL(v) == CPUID_MODEL_RAPTORLAKE_BA);
}


static INLINE Bool
CPUID_MODEL_IS_SAPPHIRERAPIDS(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_SAPPHIRERAPIDS_8F;
}

static INLINE Bool
CPUID_UARCH_IS_SAPPHIRERAPIDS(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   return CPUID_MODEL_IS_SAPPHIRERAPIDS(v) ||
          CPUID_MODEL_IS_ALDERLAKE(v) ||
          CPUID_MODEL_IS_RAPTORLAKE(v);
}


static INLINE Bool
CPUID_UARCH_IS_HASWELL(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   return CPUID_FAMILY_IS_P6(v) &&
          (CPUID_MODEL_IS_BROADWELL(v) || CPUID_MODEL_IS_HASWELL(v));
}


static INLINE Bool
CPUID_MODEL_IS_CENTERTON(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_ATOM_1C;
}

static INLINE Bool
CPUID_MODEL_IS_AVOTON(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_ATOM_4D;
}

static INLINE Bool
CPUID_MODEL_IS_BAYTRAIL(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_ATOM_37;
}

static INLINE Bool
CPUID_UARCH_IS_SILVERMONT(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   return CPUID_FAMILY_IS_P6(v) &&
          (CPUID_MODEL_IS_AVOTON(v) || CPUID_MODEL_IS_BAYTRAIL(v));
}

static INLINE Bool
CPUID_MODEL_IS_DENVERTON(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_ATOM_5F;
}

static INLINE Bool
CPUID_MODEL_IS_SNOWRIDGE(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_ATOM_86;
}

static INLINE Bool
CPUID_UARCH_IS_TREMONT(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   return CPUID_FAMILY_IS_P6(v) && CPUID_MODEL_IS_SNOWRIDGE(v);
}

static INLINE Bool
CPUID_MODEL_IS_WESTMERE(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   uint32 effectiveModel = CPUID_EFFECTIVE_MODEL(v);

   return CPUID_FAMILY_IS_P6(v) &&
          (effectiveModel == CPUID_MODEL_NEHALEM_25 || // Clarkdale
           effectiveModel == CPUID_MODEL_NEHALEM_2C || // Westmere-EP
           effectiveModel == CPUID_MODEL_NEHALEM_2F);  // Westmere-EX
}


static INLINE Bool
CPUID_MODEL_IS_SANDYBRIDGE(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   uint32 effectiveModel = CPUID_EFFECTIVE_MODEL(v);

   return CPUID_FAMILY_IS_P6(v) &&
          (effectiveModel == CPUID_MODEL_SANDYBRIDGE_2A ||
           effectiveModel == CPUID_MODEL_SANDYBRIDGE_2D);
}


static INLINE Bool
CPUID_MODEL_IS_IVYBRIDGE(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is Intel. */
   uint32 effectiveModel = CPUID_EFFECTIVE_MODEL(v);

   return CPUID_FAMILY_IS_P6(v) && (
       effectiveModel == CPUID_MODEL_SANDYBRIDGE_3E ||
       effectiveModel == CPUID_MODEL_SANDYBRIDGE_3A);
}


static INLINE Bool
CPUID_MODEL_IS_KNIGHTS_LANDING(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_KNL_57;
}

static INLINE Bool
CPUID_MODEL_IS_KNIGHTS_MILL(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_KNM_85;
}


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;
}

/*
 *----------------------------------------------------------------------
 *
 * CPUID_FAMILY_IS_K8EXT --
 *
 *      Return TRUE for family K8 with effective model >= 0x10.
 *
 *----------------------------------------------------------------------
 */
static INLINE Bool
CPUID_FAMILY_IS_K8EXT(uint32 eax)
{
   return CPUID_FAMILY_IS_K8(eax) &&
          CPUID_GET(1, EAX, EXTENDED_MODEL, eax) != 0;
}

static INLINE Bool
CPUID_FAMILY_IS_K8L(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_K8L ||
          CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_LLANO;
}

static INLINE Bool
CPUID_FAMILY_IS_LLANO(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_LLANO;
}

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);
}

static INLINE Bool
CPUID_FAMILY_IS_BOBCAT(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_BOBCAT;
}

static INLINE Bool
CPUID_FAMILY_IS_BULLDOZER(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_BULLDOZER;
}

static INLINE Bool
CPUID_FAMILY_IS_KYOTO(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_KYOTO;
}

static INLINE Bool
CPUID_FAMILY_IS_ZEN(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_ZEN;
}

static INLINE Bool
CPUID_FAMILY_IS_ZEN3(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_ZEN3;
}


/*
 * 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_EFFECTIVE_FAMILY(v) == CPUID_FAMILY_K8L &&
          CPUID_EFFECTIVE_MODEL(v)  == CPUID_MODEL_BARCELONA_02;
}


static INLINE Bool
CPUID_MODEL_IS_SHANGHAI(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is AMD. */
   return CPUID_EFFECTIVE_FAMILY(v) == CPUID_FAMILY_K8L &&
          (CPUID_MODEL_SHANGHAI_04  <= CPUID_EFFECTIVE_MODEL(v) &&
           CPUID_EFFECTIVE_MODEL(v) <= CPUID_MODEL_SHANGHAI_06);
}


static INLINE Bool
CPUID_MODEL_IS_ISTANBUL_MAGNY(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is AMD. */
   return CPUID_EFFECTIVE_FAMILY(v) == CPUID_FAMILY_K8L &&
          (CPUID_MODEL_ISTANBUL_MAGNY_08 <= CPUID_EFFECTIVE_MODEL(v) &&
           CPUID_EFFECTIVE_MODEL(v)      <= CPUID_MODEL_ISTANBUL_MAGNY_09);
}


static INLINE Bool
CPUID_MODEL_IS_PHAROAH_HOUND(uint32 v) // IN: %eax from CPUID with %eax=1.
{
   /* Assumes the CPU manufacturer is AMD. */
   return CPUID_EFFECTIVE_FAMILY(v) == CPUID_FAMILY_K8L &&
          CPUID_EFFECTIVE_MODEL(v)  == CPUID_MODEL_PHAROAH_HOUND_0A;
}


static INLINE Bool
CPUID_MODEL_IS_BULLDOZER(uint32 eax)
{
   /*
    * Bulldozer is models of family 0x15 that are below 10 excluding
    * Piledriver 02.
    */
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_BULLDOZER &&
          CPUID_EFFECTIVE_MODEL(eax)  < CPUID_MODEL_PILEDRIVER_10 &&
          CPUID_EFFECTIVE_MODEL(eax) != CPUID_MODEL_PILEDRIVER_02;
}


static INLINE Bool
CPUID_MODEL_IS_PILEDRIVER(uint32 eax)
{
   /* Piledriver is models 0x02 & 0x10 of family 0x15 (so far). */
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_BULLDOZER &&
          ((CPUID_EFFECTIVE_MODEL(eax) >= CPUID_MODEL_PILEDRIVER_10 &&
            CPUID_EFFECTIVE_MODEL(eax) <= CPUID_MODEL_PILEDRIVER_1F) ||
           CPUID_EFFECTIVE_MODEL(eax) == CPUID_MODEL_PILEDRIVER_02);
}


static INLINE Bool
CPUID_MODEL_IS_STEAMROLLER(uint32 eax)
{
   /* Steamroller is model 0x30 of family 0x15 (so far). */
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_BULLDOZER &&
          (CPUID_EFFECTIVE_MODEL(eax) >= CPUID_MODEL_STEAMROLLER_30 &&
           CPUID_EFFECTIVE_MODEL(eax) <= CPUID_MODEL_STEAMROLLER_3F);
}


static INLINE Bool
CPUID_MODEL_IS_EXCAVATOR(uint32 eax)
{
   /* Excavator is model 0x60 of family 0x15 (so far). */
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_BULLDOZER &&
          (CPUID_EFFECTIVE_MODEL(eax) >= CPUID_MODEL_EXCAVATOR_60 &&
           CPUID_EFFECTIVE_MODEL(eax) <= CPUID_MODEL_EXCAVATOR_6F);
}


static INLINE Bool
CPUID_MODEL_IS_KYOTO(uint32 eax)
{
   /* Kyoto is models 0x00 of family 0x16 (so far). */
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_KYOTO &&
          CPUID_EFFECTIVE_MODEL(eax) == CPUID_MODEL_KYOTO_00;
}


static INLINE Bool
CPUID_MODEL_IS_ZEN(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_ZEN &&
          CPUID_EFFECTIVE_MODEL(eax) <= CPUID_MODEL_ZEN_1F;
}


static INLINE Bool
CPUID_MODEL_IS_ZEN2(uint32 eax)
{
  return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_ZEN &&
         ((CPUID_EFFECTIVE_MODEL(eax) >= CPUID_MODEL_ZEN2_30 &&
           CPUID_EFFECTIVE_MODEL(eax) <= CPUID_MODEL_ZEN2_3F) ||
          (CPUID_EFFECTIVE_MODEL(eax) >= CPUID_MODEL_ZEN2_70 &&
           CPUID_EFFECTIVE_MODEL(eax) <= CPUID_MODEL_ZEN2_7F));
}


static INLINE Bool
CPUID_FAMILY_IS_DHYANA(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_DHYANA;
}


static INLINE Bool
CPUID_MODEL_IS_DHYANA_A(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_DHYANA &&
          CPUID_EFFECTIVE_MODEL(eax)  == CPUID_MODEL_DHYANA_A;
}


static INLINE Bool
CPUID_MODEL_IS_ZEN3(uint32 eax)
{
   return CPUID_EFFECTIVE_FAMILY(eax) == CPUID_FAMILY_ZEN3 &&
          CPUID_EFFECTIVE_MODEL(eax) <= CPUID_MODEL_ZEN3_0F;
}


#define CPUID_LEAF4_CACHE_TYPE_NULL      0
#define CPUID_LEAF4_CACHE_TYPE_DATA      1
#define CPUID_LEAF4_CACHE_TYPE_INST      2
#define CPUID_LEAF4_CACHE_TYPE_UNIF      3
#define CPUID_LEAF4_CACHE_INDEXING_DIRECT  0
#define CPUID_LEAF4_CACHE_INDEXING_COMPLEX 1

#define CPUID_TOPOLOGY_LEVEL_TYPE_INVALID   0
#define CPUID_TOPOLOGY_LEVEL_TYPE_SMT       1
#define CPUID_TOPOLOGY_LEVEL_TYPE_CORE      2
#define CPUID_TOPOLOGY_LEVEL_TYPE_MODULE    3
#define CPUID_TOPOLOGY_LEVEL_TYPE_TILE      4
#define CPUID_TOPOLOGY_LEVEL_TYPE_DIE       5

#define CPUID_AMD_LEAF85_L1_CACHE_FULLY_ASSOC     0xff
#define CPUID_AMD_LEAF86_L2_L3_CACHE_FULLY_ASSOC  0x0f
#define CPUID_AMD_LEAF81D_CACHE_TYPE_NULL   0
#define CPUID_AMD_LEAF81D_CACHE_TYPE_DATA   1
#define CPUID_AMD_LEAF81D_CACHE_TYPE_INST   2
#define CPUID_AMD_LEAF81D_CACHE_TYPE_UNIF   3

#define CPUID_AMD_TOPOLOGY_LEVEL_TYPE_INVALID   0
#define CPUID_AMD_TOPOLOGY_LEVEL_TYPE_CORE      1
#define CPUID_AMD_TOPOLOGY_LEVEL_TYPE_COMPLEX   2
#define CPUID_AMD_TOPOLOGY_LEVEL_TYPE_CCD       3
#define CPUID_AMD_TOPOLOGY_LEVEL_TYPE_SOCKET    4

/*
 * Hypervisor CPUID space is 0x400000XX.
 */
static INLINE Bool
CPUID_IsHypervisorLevel(uint32 level)
{
   return (level & 0xffffff00) == 0x40000000;
}

/*
 *----------------------------------------------------------------------
 *
 * CPUID_LevelUsesEcx --
 *
 *      Returns TRUE for leaves that support input ECX != 0 (subleaves).
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
CPUID_LevelUsesEcx(uint32 level) {
   switch (level)
   {

#define CPUIDLEVEL(t, s, v, c, h)     \
      case v:                         \
         return c != 0;

      CPUID_KNOWN_LEVELS

#undef CPUIDLEVEL

      default:
         return FALSE;
   }
}

#ifdef _MSC_VER
#pragma warning (pop)
#endif


#if defined __cplusplus
} // extern "C"
#endif

#endif // _X86CPUID_H_
  07070100000169000081A4000000000000000000000001682255050000138A000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/x86cpuid_asm.h /*********************************************************
 * Copyright (C) 2003-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * x86cpuid_asm.h
 *
 *      CPUID-related assembly functions.
 */

#ifndef _X86CPUID_ASM_H_
#define _X86CPUID_ASM_H_

#define INCLUDE_ALLOW_USERLEVEL

#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
#include "includeCheck.h"

#include "vm_basic_asm.h"
#include "x86cpuid.h"


#ifdef __GNUC__ // {

/*
 * 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).
 *
 */

#if defined VM_X86_64 || !defined __PIC__
#define VM_CPUID_BLOCK  "cpuid"
#define VM_EBX_OUT(reg) "=b"(reg)
#else
/* %ebx is reserved on i386 PIC. */
#define VM_CPUID_BLOCK  "movl %%ebx, %1\n\t" \
                        "cpuid\n\t"          \
                        "xchgl %%ebx, %1\n\t"
#define VM_EBX_OUT(reg) "=&rm"(reg)
#endif

static INLINE void
__GET_CPUID(uint32     eax,  // IN
            CPUIDRegs *regs) // OUT
{
   __asm__ __volatile__(
      VM_CPUID_BLOCK
      : "=a" (regs->eax),
        VM_EBX_OUT(regs->ebx),
        "=c" (regs->ecx),
        "=d" (regs->edx)
      : "a" (eax)
      : "memory"
   );
}

static INLINE void
__GET_CPUID2(uint32 eax,      // IN
             uint32 ecx,      // IN
             CPUIDRegs *regs) // OUT
{
   __asm__ __volatile__(
      VM_CPUID_BLOCK
      : "=a" (regs->eax),
        VM_EBX_OUT(regs->ebx),
        "=c" (regs->ecx),
        "=d" (regs->edx)
      : "a" (eax), "c" (ecx)
      : "memory"
   );
}

static INLINE uint32
__GET_EAX_FROM_CPUID(uint32 eax) // IN
{
   uint32 ebx;

   __asm__ __volatile__(
      VM_CPUID_BLOCK
      : "=a" (eax),
        VM_EBX_OUT(ebx)
      : "a" (eax)
      : "memory", "%ecx", "%edx"
   );

   return eax;
}

static INLINE uint32
__GET_EBX_FROM_CPUID(uint32 eax) // IN
{
   uint32 ebx;

   __asm__ __volatile__(
      VM_CPUID_BLOCK
      : "=a" (eax), VM_EBX_OUT(ebx)
      : "a" (eax)
      : "memory", "%ecx", "%edx"
   );

   return ebx;
}

static INLINE uint32
__GET_ECX_FROM_CPUID(uint32 eax) // IN
{
   uint32 ecx;
   uint32 ebx;

   __asm__ __volatile__(
      VM_CPUID_BLOCK
      : "=a" (eax),
        VM_EBX_OUT(ebx),
        "=c" (ecx)
      : "a" (eax)
      : "memory", "%edx"
   );

   return ecx;
}

static INLINE uint32
__GET_EDX_FROM_CPUID(uint32 eax) // IN
{
   uint32 edx;
   uint32 ebx;

   __asm__ __volatile__(
      VM_CPUID_BLOCK
      : "=a" (eax),
        VM_EBX_OUT(ebx),
        "=d" (edx)
      : "a" (eax)
      : "memory", "%ecx"
   );

   return edx;
}


#undef VM_CPUID_BLOCK
#undef VM_EBX_OUT

#elif defined(_MSC_VER) // } {

static INLINE void
__GET_CPUID(uint32 input, CPUIDRegs *regs)
{
   __cpuid((int *)regs, input);
}

static INLINE void
__GET_CPUID2(uint32 inputEax, uint32 inputEcx, CPUIDRegs *regs)
{
   __cpuidex((int *)regs, inputEax, inputEcx);
}

static INLINE uint32
__GET_EAX_FROM_CPUID(uint32 input)
{
   CPUIDRegs regs;
   __cpuid((int *)&regs, input);
   return regs.eax;
}

static INLINE uint32
__GET_EBX_FROM_CPUID(uint32 input)
{
   CPUIDRegs regs;
   __cpuid((int *)&regs, input);
   return regs.ebx;
}

static INLINE uint32
__GET_ECX_FROM_CPUID(uint32 input)
{
   CPUIDRegs regs;
   __cpuid((int *)&regs, input);
   return regs.ecx;
}

static INLINE uint32
__GET_EDX_FROM_CPUID(uint32 input)
{
   CPUIDRegs regs;
   __cpuid((int *)&regs, input);
   return regs.edx;
}

#else // }
#error
#endif

#define CPUID_FOR_SIDE_EFFECTS() ((void)__GET_EAX_FROM_CPUID(0))

#endif
  0707010000016A000081A400000000000000000000000168225505000006C2000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/include/x86vendor.h    /*********************************************************
 * Copyright (C) 1998-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef _X86VENDOR_H_
#define _X86VENDOR_H_

/*
 * CPU vendors
 */

typedef enum {
   CPUID_VENDOR_UNKNOWN,
   CPUID_VENDOR_COMMON,
   CPUID_VENDOR_INTEL,
   CPUID_VENDOR_AMD,
   CPUID_VENDOR_CYRIX,
   CPUID_VENDOR_VIA,
   CPUID_VENDOR_HYGON,
   CPUID_NUM_VENDORS
} CpuidVendor;

#endif
  0707010000016B000081A40000000000000000000000016822550500000469000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/include/xdg.h  /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * xdg.h --
 *
 *	vmware-xdg-* script wrapper library.
 */

#ifndef _VMWARE_XDG_H_
#define _VMWARE_XDG_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

extern const char *Xdg_DetectDesktopEnv(void);

#endif // ifndef _VMWARE_XDG_H_
   0707010000016C000081A40000000000000000000000016822550500001337000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/include/xdrutil.h  /*********************************************************
 * Copyright (C) 2008-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _XDRUTIL_H_
#define _XDRUTIL_H_

/*
 * xdrutil.h --
 *
 *    Utility functions for code that uses XDR to encode/decode data.
 */

#include <rpc/rpc.h>
#include "vm_basic_types.h"
#include "util.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Helper macros for iterating over an rpcgen-generated array. Given a struct S:
 *
 * struct S {
 *    struct {
 *       u_int f_len;
 *       T *f_val;
 *    } f;
 * };
 *
 * Iterate over the array like this:
 *
 * S s;
 * u_int i;
 *
 * XDRUTIL_FOREACH(i, &s, f) {
 *    T *t = XDRUTIL_GETITEM(&s, f, i);
 * }
 *
 * 'f' should be a string with the field name.
 */
#define XDRUTIL_COUNT(ptr, field) ((ptr)->field.field##_len)
#define XDRUTIL_FOREACH(counter, ptr, field)                               \
   for ((counter) = 0; (counter) < XDRUTIL_COUNT(ptr, field); (counter)++)

#define XDRUTIL_GETITEM(ptr, field, idx) &((ptr)->field.field##_val[idx])

/*
 * Wrapper for XdrUtil_ArrayAppend that automatically populates the arguments
 * from a given XDR array.
 */
#ifdef __GNUC__
#   if defined(__cpp_decltype) || defined(__GXX_EXPERIMENTAL_CXX0X__)
#      define XDRUTIL_ARRAYAPPEND(ptr, field, cnt)                      \
          (decltype ((ptr)->field.field##_val))                         \
          XdrUtil_ArrayAppend((void **) &(ptr)->field.field##_val,      \
                              &(ptr)->field.field##_len,                \
                              sizeof *(ptr)->field.field##_val,         \
                              (cnt))
#   else
#      define XDRUTIL_ARRAYAPPEND(ptr, field, cnt)                      \
          (typeof ((ptr)->field.field##_val))                           \
          XdrUtil_ArrayAppend((void **) &(ptr)->field.field##_val,      \
                              &(ptr)->field.field##_len,                \
                              sizeof *(ptr)->field.field##_val,         \
                              (cnt))
#   endif
#else
#   define XDRUTIL_ARRAYAPPEND(ptr, field, cnt)                         \
       XdrUtil_ArrayAppend((void **) &(ptr)->field.field##_val,         \
                           &(ptr)->field.field##_len,                   \
                           sizeof *(ptr)->field.field##_val,            \
                           (cnt))
#endif

/*
 * Macros for assigning to XDR optional strings, opaque fields, and
 * optional opaque fields.
 *
 * Usage:
 * // XDR
 * struct MyFoo { string *foo; };
 * struct MyBar { opaque bar; };
 * struct MyOptBar { opaque *bar; };
 *
 * // C
 * char buf[] = { 0xca, 0xfe, 0xba, 0xbe, 0x80, 0x08 };
 *
 * MyFoo foo;
 * XDRUTIL_STRING_OPT(&foo.foo, "Hello, world!");
 *
 * MyBar bar;
 * XDRUTIL_OPAQUE(&bar.bar, buf, sizeof buf);
 *
 * MyOptBar obar;
 * XDRUTIL_OPAQUE_OPT(&obar.bar, buf, sizeof buf);
 */

#define XDRUTIL_STRING_OPT(ptr, src)                            do {    \
   (ptr) = Util_SafeMalloc(sizeof *(ptr));                              \
   *(ptr) = Util_SafeStrdup((src));                                     \
} while (0)

#define XDRUTIL_OPAQUE(ptr, src, srcSize)                       do {    \
   struct { u_int len; char *val; } __opaque_temp = {(srcSize), NULL};  \
   ASSERT_ON_COMPILE(sizeof(*(ptr)) == sizeof(__opaque_temp));          \
                                                                        \
   __opaque_temp.val = Util_SafeMalloc((srcSize));                      \
   memcpy(__opaque_temp.val, (src), (srcSize));                         \
   memcpy(ptr, &__opaque_temp, sizeof __opaque_temp);                   \
} while (0)

#define XDRUTIL_OPAQUE_OPT(ptr, src, srcSize)                   do {    \
   *(ptr) = Util_SafeMalloc(sizeof (struct { u_len; void*; }));         \
   XDRUTIL_OPAQUE(*(ptr), (src), (srcSize));                            \
} while(0)

void *
XdrUtil_ArrayAppend(void **array, u_int *arrayLen, size_t elemSz, u_int elemCnt);

Bool
XdrUtil_Deserialize(const void *data, size_t dataLen, void *xdrProc, void *dest);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _XDRUTIL_H_ */

 0707010000016D000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/jsmn   0707010000016E000081A40000000000000000000000016822550500000425000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/jsmn/LICENSE   Copyright (c) 2010 Serge A. Zaitsev

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

   0707010000016F000081A400000000000000000000000168225505000003F7000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/jsmn/Makefile.am   ################################################################################
### Copyright (c) 2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libJsmn.la

libJsmn_la_SOURCES =
libJsmn_la_SOURCES += jsmn.c

AM_CFLAGS =
AM_CFLAGS += @GLIB2_CPPFLAGS@
 07070100000170000081A400000000000000000000000168225505000000EC000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/jsmn/README    This is a local copy of jsmn, a minimalistic json parser.

jsmn included as src because its tiny, and its been augmented to use
Log() since the original code has no logging.

https://zserge.com/jsmn.html
https://github.com/zserge/jsmn

07070100000171000081A40000000000000000000000016822550500002FD7000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/jsmn/jsmn.c    /* **********************************************************
 * Copyright (c) 2019-2021 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * Copyright (c) 2010 Serge A. Zaitsev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


/*
 * From https://zserge.com/jsmn.html
 *
 * Some local additions (commented with / * vmware * / for logging.
 */

/*
 * Turn on JSMN_PARENT to spend a bit of memory to get a lot of speed
 * in large documents.
 */
#define JSMN_PARENT  1
/* Tune on JSMN_STRICT to force strict json parsing */
#define JSMN_STRICT  1
#include "jsmn.h"

#include "log.h"
#include "vm_assert.h"

#define LOGLEVEL_MODULE jsmn
#include "loglevel_user.h"

/**
 * Allocates a fresh unused token from the token pool.
 */
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
      jsmntok_t *tokens, size_t num_tokens) {
   jsmntok_t *tok;
   if (parser->toknext >= num_tokens) {
      return NULL;
   }
   tok = &tokens[parser->toknext++];
   tok->start = tok->end = -1;
   tok->size = 0;
#ifdef JSMN_PARENT_LINKS
   tok->parent = -1;
#endif
   return tok;
}

/**
 * Fills token type and boundaries.
 */
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
                            int start, int end) {
   token->type = type;
   token->start = start;
   token->end = end;
   token->size = 0;
}

/**
 * Fills next available token with JSON primitive.
 */
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
      size_t len, jsmntok_t *tokens, size_t num_tokens) {
   jsmntok_t *token;
   int start;

   start = parser->pos;

   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
      switch (js[parser->pos]) {
#ifndef JSMN_STRICT
         /* In strict mode primitive must be followed by "," or "}" or "]" */
         case ':':
#endif
         case '\t' : case '\r' : case '\n' : case ' ' :
         case ','  : case ']'  : case '}' :
            goto found;
      }
      if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
         /* vmware */
         Log("%s: Unexpected char '0x%02x' in primitive at pos %d\n",
             __FUNCTION__, js[parser->pos], parser->pos);
         /* vmware */
         parser->pos = start;
         return JSMN_ERROR_INVAL;
      }
   }
#ifdef JSMN_STRICT
   /* In strict mode primitive must be followed by a comma/object/array */
   parser->pos = start;
   return JSMN_ERROR_PART;
#endif

found:
   if (tokens == NULL) {
      parser->pos--;
      return 0;
   }
   token = jsmn_alloc_token(parser, tokens, num_tokens);
   if (token == NULL) {
      parser->pos = start;
      return JSMN_ERROR_NOMEM;
   }
   jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
   token->parent = parser->toksuper;
#endif
   parser->pos--;
   return 0;
}

/**
 * Fills next token with JSON string.
 */
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
      size_t len, jsmntok_t *tokens, size_t num_tokens) {
   jsmntok_t *token;

   int start = parser->pos;

   parser->pos++;

   /* Skip starting quote */
   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
      char c = js[parser->pos];

      /* Quote: end of string */
      if (c == '\"') {
         if (tokens == NULL) {
            return 0;
         }
         token = jsmn_alloc_token(parser, tokens, num_tokens);
         if (token == NULL) {
            parser->pos = start;
            return JSMN_ERROR_NOMEM;
         }
         jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
#ifdef JSMN_PARENT_LINKS
         token->parent = parser->toksuper;
#endif
         return 0;
      }

      /* Backslash: Quoted symbol expected */
      if (c == '\\' && parser->pos + 1 < len) {
         int i;
         parser->pos++;
         switch (js[parser->pos]) {
            /* Allowed escaped symbols */
            case '\"': case '/' : case '\\' : case 'b' :
            case 'f' : case 'r' : case 'n'  : case 't' :
               break;
            /* Allows escaped symbol \uXXXX */
            case 'u':
               parser->pos++;
               for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
                  /* If it isn't a hex character we have an error */
                  if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
                           (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
                           (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
                     /* vmware */
                     Log("%s: Unexpected char '0x%02x' in escaped unicode "
                         "at pos %d\n",
                         __FUNCTION__, js[parser->pos], parser->pos);
                     /* vmware */
                     parser->pos = start;
                     return JSMN_ERROR_INVAL;
                  }
                  parser->pos++;
               }
               parser->pos--;
               break;
            /* Unexpected symbol */
            default:
               /* vmware */
               Log("%s: Unexpected symbol '0x%02x' in string at pos %d\n",
                   __FUNCTION__, js[parser->pos], parser->pos);
               /* vmware */
               parser->pos = start;
               return JSMN_ERROR_INVAL;
         }
      }
   }
   parser->pos = start;
   return JSMN_ERROR_PART;
}

/**
 * Parse JSON string and fill tokens.
 */
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
      jsmntok_t *tokens, unsigned int num_tokens) {
   int r;
   int i;
   jsmntok_t *token;
   int count = parser->toknext;

   for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
      char c;
      jsmntype_t type;

      c = js[parser->pos];
      switch (c) {
         case '{': case '[':
            count++;
            if (tokens == NULL) {
               break;
            }
            token = jsmn_alloc_token(parser, tokens, num_tokens);
            if (token == NULL)
               return JSMN_ERROR_NOMEM;
            if (parser->toksuper != -1) {
               tokens[parser->toksuper].size++;
#ifdef JSMN_PARENT_LINKS
               token->parent = parser->toksuper;
#endif
            }
            token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
            token->start = parser->pos;
            parser->toksuper = parser->toknext - 1;
            break;
         case '}': case ']':
            if (tokens == NULL)
               break;
            type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
#ifdef JSMN_PARENT_LINKS
            if (parser->toknext < 1) {
               return JSMN_ERROR_INVAL;
            }
            token = &tokens[parser->toknext - 1];
            for (;;) {
               if (token->start != -1 && token->end == -1) {
                  if (token->type != type) {
                     return JSMN_ERROR_INVAL;
                  }
                  token->end = parser->pos + 1;
                  parser->toksuper = token->parent;
                  break;
               }
               if (token->parent == -1) {
                  if(token->type != type || parser->toksuper == -1) {
                     return JSMN_ERROR_INVAL;
                  }
                  break;
               }
               token = &tokens[token->parent];
            }
#else
            for (i = parser->toknext - 1; i >= 0; i--) {
               token = &tokens[i];
               if (token->start != -1 && token->end == -1) {
                  if (token->type != type) {
                     return JSMN_ERROR_INVAL;
                  }
                  parser->toksuper = -1;
                  token->end = parser->pos + 1;
                  break;
               }
            }
            /* Error if unmatched closing bracket */
            if (i == -1) return JSMN_ERROR_INVAL;
            for (; i >= 0; i--) {
               token = &tokens[i];
               if (token->start != -1 && token->end == -1) {
                  parser->toksuper = i;
                  break;
               }
            }
#endif
            break;
         case '\"':
            r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
            if (r < 0) return r;
            count++;
            if (parser->toksuper != -1 && tokens != NULL)
               tokens[parser->toksuper].size++;
            break;
         case '\t' : case '\r' : case '\n' : case ' ':
            break;
         case ':':
            parser->toksuper = parser->toknext - 1;
            break;
         case ',':
            if (tokens != NULL && parser->toksuper != -1 &&
                  tokens[parser->toksuper].type != JSMN_ARRAY &&
                  tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
               parser->toksuper = tokens[parser->toksuper].parent;
#else
               for (i = parser->toknext - 1; i >= 0; i--) {
                  if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
                     if (tokens[i].start != -1 && tokens[i].end == -1) {
                        parser->toksuper = i;
                        break;
                     }
                  }
               }
#endif
            }
            break;
#ifdef JSMN_STRICT
         /* In strict mode primitives are: numbers and booleans */
         case '-': case '0': case '1' : case '2': case '3' : case '4':
         case '5': case '6': case '7' : case '8': case '9':
         case 't': case 'f': case 'n' :
            /* And they must not be keys of the object */
            if (tokens != NULL && parser->toksuper != -1) {
               jsmntok_t *t = &tokens[parser->toksuper];
               if (t->type == JSMN_OBJECT ||
                     (t->type == JSMN_STRING && t->size != 0)) {
                  /* vmware */
                  Log("%s: Unexpected char '%c' in STRING/OBJECT at pos %d\n",
                      __FUNCTION__, js[parser->pos], parser->pos);
                  /* vmware */
                  return JSMN_ERROR_INVAL;
               }
            }
#else
         /* In non-strict mode every unquoted value is a primitive */
         default:
#endif
            r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
            if (r < 0) return r;
            count++;
            if (parser->toksuper != -1 && tokens != NULL)
               tokens[parser->toksuper].size++;
            break;

#ifdef JSMN_STRICT
         /* Unexpected char in strict mode */
         default:
            /* vmware */
            Log("%s: Unexpected char '0x%02x' at pos %d\n",
                __FUNCTION__, js[parser->pos], parser->pos);
            /* vmware */
            return JSMN_ERROR_INVAL;
#endif
      }
   }

   if (tokens != NULL) {
      for (i = parser->toknext - 1; i >= 0; i--) {
         /* Unmatched opened object or array */
         if (tokens[i].start != -1 && tokens[i].end == -1) {
            return JSMN_ERROR_PART;
         }
      }
   }

   return count;
}

/**
 * Creates a new parser based over a given  buffer with an array of tokens
 * available.
 */
void jsmn_init(jsmn_parser *parser) {
   parser->pos = 0;
   parser->toknext = 0;
   parser->toksuper = -1;
}

 07070100000172000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/lock   07070100000173000081A400000000000000000000000168225505000004CD000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/Makefile.am   ################################################################################
### Copyright (C) 2010-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libLock.la

libLock_la_SOURCES =
libLock_la_SOURCES += ul.c
libLock_la_SOURCES += ulCondVar.c
libLock_la_SOURCES += ulExcl.c
libLock_la_SOURCES += ulRec.c
libLock_la_SOURCES += ulRW.c
libLock_la_SOURCES += ulSema.c
libLock_la_SOURCES += ulBarrier.c
libLock_la_SOURCES += ulStats.c

AM_CFLAGS = @LIB_USER_CPPFLAGS@
   07070100000174000081A4000000000000000000000001682255050000637A000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ul.c  /*********************************************************
 * Copyright (c) 2009-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include "vmware.h"
#include "str.h"
#include "util.h"
#include "userlock.h"
#include "ulInt.h"
#include "ulIntShared.h"
#include "hashTable.h"
#include "random.h"

static Bool mxInPanic = FALSE;  // track when involved in a panic
static Bool mxUserCollectLockingTree = FALSE;

Bool (*MXUserTryAcquireForceFail)() = NULL;

static MX_Rank (*MXUserMxCheckRank)(void) = NULL;
static void (*MXUserMxLockLister)(void) = NULL;
void (*MXUserMX_LockRec)(struct MX_MutexRec *lock) = NULL;
void (*MXUserMX_UnlockRec)(struct MX_MutexRec *lock) = NULL;
Bool (*MXUserMX_TryLockRec)(struct MX_MutexRec *lock) = NULL;
Bool (*MXUserMX_IsLockedByCurThreadRec)(const struct MX_MutexRec *lock) = NULL;
char *(*MXUserMX_NameRec)(const struct MX_MutexRec *lock) = NULL;
static void (*MXUserMX_SetInPanic)(void) = NULL;
static Bool (*MXUserMX_InPanic)(void) = NULL;

#define	MXUSER_MAX_LOOP 5


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_IsLockingTreeAvailable
 *
 *      Is the lock tracking tree available for reporting?
 *
 * Results:
 *      TRUE  MXuser lock tree tracking is enabled
 *      FALSE MXUser lock tree tracking is disabled
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_IsLockingTreeAvailable(void)
{
   return mxUserCollectLockingTree;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_LockingTreeCollection
 *
 *      Enable or disable locking tree data collection.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_LockingTreeCollection(Bool enabled)  // IN:
{
   mxUserCollectLockingTree = vmx86_devel && vmx86_debug && enabled;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserInternalSingleton --
 *
 *      A "singleton" function for the MXUser internal recursive lock.
 *
 *      Internal MXUser recursive locks have no statistics gathering or
 *      tracking abilities. They need to used with care and rarely.
 *
 * Results:
 *      NULL    Failure
 *      !NULL   A pointer to an initialized MXRecLock
 *
 * Side effects:
 *      Manifold.
 *
 *-----------------------------------------------------------------------------
 */

MXRecLock *
MXUserInternalSingleton(Atomic_Ptr *storage)  // IN:
{
   MXRecLock *lock = Atomic_ReadPtr(storage);

   if (UNLIKELY(lock == NULL)) {
      MXRecLock *newLock = Util_SafeMalloc(sizeof *newLock);

      if (MXRecLockInit(newLock)) {
         lock = Atomic_ReadIfEqualWritePtr(storage, NULL, (void *) newLock);

         if (lock) {
            MXRecLockDestroy(newLock);
            free(newLock);
         } else {
            lock = Atomic_ReadPtr(storage);
         }
      } else {
         free(newLock);
         lock = Atomic_ReadPtr(storage);  // maybe another thread succeeded
      }
   }

   return lock;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserSydrome --
 *
 *      Generate the syndrome bits for this MXUser library.
 *
 *      Each MXUser library has unique syndrome bits enabling the run time
 *      detection of locks created with one copy of the MXUser library and
 *      passed to another copy of the MXUser library.
 *
 *      The syndrome bits provide a detection mechanism for locks created and
 *      maintained by one copy of the lock library being used by another copy
 *      of the lock library. Undetected, strange crashes can occur due to locks
 *      that are aliased or are incompatible with the lock library that is
 *      manipulating them.
 *
 *      The syndrome bits are generated by using a source of bits that is
 *      external to a program and its libraries. This way no code or data
 *      based scheme can be spoofed or aliased.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static uint32
MXUserSyndrome(void)
{
   uint32 syndrome;
   static Atomic_uint32 syndromeMem;  // implicitly zero -- mbellon

   syndrome = Atomic_Read(&syndromeMem);

   if (syndrome == 0) {
#if defined(_WIN32)
#if defined(VM_WIN_UWP)
      syndrome = (uint32)GetTickCount64();
#else
      syndrome = GetTickCount();
#endif
#else
      syndrome = time(NULL) & 0xFFFFFFFF;
#endif

      /*
       * Protect against a total failure.
       */

      if (syndrome == 0) {
         syndrome++;
      }

      /* blind write; if racing one thread or the other will do */
      Atomic_ReadIfEqualWrite(&syndromeMem, 0, syndrome);

      syndrome = Atomic_Read(&syndromeMem);
   }

   ASSERT(syndrome != 0);

   return syndrome;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserGetSignature --
 *
 *      Return a signature appropriate for the specified object type.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

uint32
MXUserGetSignature(MXUserObjectType objectType)  // IN:
{
   uint32 signature;

   ASSERT((objectType >= 0) && (objectType < 16) &&
          (objectType != MXUSER_TYPE_NEVER_USE));

   /*
    * Create a unique signature by combining the unique syndrome of this
    * instance of lib/lock with a mapping of objectType. The unique syndome
    * within the signature is used to catch multiple copies of lib/lock that
    * are "leaking" locks between them (e.g. locks may be aliased (which means
    * no protection) or internal implementation details may have changed).
    */

   signature = (MXUserSyndrome() & 0x0FFFFFFF) | (objectType << 28);

   ASSERT(signature != 0);

   return signature;
}


/*
 *---------------------------------------------------------------------
 *
 *  MXUser_SetInPanic --
 *
 *     Notify the locking system that a panic is occurring.
 *
 *  Results:
 *     Set the "in a panic" state in userland locks and, when possible,
 *     MX locks.
 *
 *  Side effects:
 *     None
 *
 *---------------------------------------------------------------------
 */

void
MXUser_SetInPanic(void)
{
   mxInPanic = TRUE;

   if (MXUserMX_SetInPanic != NULL) {
      MXUserMX_SetInPanic();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDumpAndPanic --
 *
 *      Dump a lock, print a message and die
 *
 * Results:
 *      A panic.
 *
 * Side effects:
 *      Manifold.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserDumpAndPanic(MXUserHeader *header,  // IN:
                   const char *fmt,       // IN:
                   ...)                   // IN:
{
   char *msg;
   va_list ap;
   static uint32 loopCounter = 0;  // Is panic looping through there?

   ASSERT((header != NULL) && (header->dumpFunc != NULL));

   if (++loopCounter > MXUSER_MAX_LOOP) {
      /*
       * Panic is looping through MXUser to here - no progress is being made.
       * Switch to panic mode in the hopes that this will allow some progress.
       */

      MXUser_SetInPanic();
   }

   if (header->badHeader) {
      Warning("%s: Corrupt lock @ %p\n", __FUNCTION__, header);

      Warning("\tname %p\n", header->name);
      Warning("\tsignature 0x%X\n", header->signature);
      Warning("\trank 0x%X\n", header->rank);

      Warning("\tdumpFunc %p\n", header->dumpFunc);
      Warning("\tstatsFunc %p\n", header->statsFunc);

      Warning("\titem.next %p\n", header->item.next);
      Warning("\titem.prev %p\n", header->item.prev);

      Warning("\tserial number %"FMT64"u\n", header->serialNumber);
   } else {
      (*header->dumpFunc)(header);
   }

   va_start(ap, fmt);
   msg = Str_SafeVasprintf(NULL, fmt, ap);
   va_end(ap);

   Panic("%s", msg);
}


/*
 *---------------------------------------------------------------------
 *
 *  MXUser_InPanic --
 *
 *     Is the caller in the midst of a panic?
 *
 *  Results:
 *     TRUE   Yes
 *     FALSE  No
 *
 *  Side effects:
 *     None
 *
 *---------------------------------------------------------------------
 */

Bool
MXUser_InPanic(void)
{
   return mxInPanic || (MXUserMX_InPanic != NULL && MXUserMX_InPanic());
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserInstallMxHooks --
 *
 *      The MX facility may notify the MXUser facility that it is place and
 *      that MXUser should check with it. This function should be called from
 *      MX_Init.
 *
 * Results:
 *      As Above.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserInstallMxHooks(void (*theLockListFunc)(void),
                     MX_Rank (*theRankFunc)(void),
                     void  (*theLockFunc)(struct MX_MutexRec *lock),
                     void  (*theUnlockFunc)(struct MX_MutexRec *lock),
                     Bool  (*theTryLockFunc)(struct MX_MutexRec *lock),
                     Bool  (*theIsLockedFunc)(const struct MX_MutexRec *lock),
                     char *(*theNameFunc)(const struct MX_MutexRec *lock),
                     void  (*theSetInPanicFunc)(void),
                     Bool  (*theInPanicFunc)(void))
{
   /*
    * This function can be called more than once but the second and later
    * invocations must be attempting to install the same hook functions as
    * the first invocation.
    */

   if ((MXUserMxLockLister == NULL) &&
       (MXUserMxCheckRank == NULL) &&
       (MXUserMX_LockRec == NULL) &&
       (MXUserMX_UnlockRec == NULL) &&
       (MXUserMX_TryLockRec == NULL) &&
       (MXUserMX_IsLockedByCurThreadRec == NULL) &&
       (MXUserMX_NameRec == NULL) &&
       (MXUserMX_SetInPanic == NULL) &&
       (MXUserMX_InPanic == NULL)
       ) {
      MXUserMxLockLister = theLockListFunc;
      MXUserMxCheckRank = theRankFunc;
      MXUserMX_LockRec = theLockFunc;
      MXUserMX_UnlockRec = theUnlockFunc;
      MXUserMX_TryLockRec = theTryLockFunc;
      MXUserMX_IsLockedByCurThreadRec = theIsLockedFunc;
      MXUserMX_NameRec = theNameFunc;
      MXUserMX_SetInPanic = theSetInPanicFunc;
      MXUserMX_InPanic = theInPanicFunc;
   } else {
      ASSERT((MXUserMxLockLister == theLockListFunc) &&
             (MXUserMxCheckRank == theRankFunc) &&
             (MXUserMX_LockRec == theLockFunc) &&
             (MXUserMX_UnlockRec == theUnlockFunc) &&
             (MXUserMX_TryLockRec == theTryLockFunc) &&
             (MXUserMX_IsLockedByCurThreadRec == theIsLockedFunc) &&
             (MXUserMX_NameRec == theNameFunc) &&
             (MXUserMX_SetInPanic == theSetInPanicFunc) &&
             (MXUserMX_InPanic == theInPanicFunc)
            );
   }
}

#if defined(MXUSER_DEBUG)
#define MXUSER_MAX_LOCKS_PER_THREAD (2 * MXUSER_MAX_REC_DEPTH)

typedef struct MXUserPerThread {
   struct MXUserPerThread  *next;
   uint32                   locksHeld;
   MXUserHeader            *lockArray[MXUSER_MAX_LOCKS_PER_THREAD];
} MXUserPerThread;

static Atomic_Ptr perThreadLockMem;
static MXUserPerThread *perThreadFreeList = NULL;

static Atomic_Ptr hashTableMem;

/*
 *-----------------------------------------------------------------------------
 *
 * MXUserAllocPerThread --
 *
 *     Allocate a perThread structure.
 *
 *     Memory is allocated for the specified thread as necessary. Use a
 *     victim cache in front of malloc to provide a slight performance
 *     advantage. The lock here is equivalent to the lock buried inside
 *     malloc but no complex calculations are necessary to perform an
 *     allocation most of the time.
 *
 *     The maximum size of the list will be roughly the maximum number of
 *     threads having taken locks at the same time - a bounded number less
 *     than or equal to the maximum of threads created.
 *
 * Results:
 *     As above.
 *
 * Side effects:
 *      Memory may be allocated.
 *
 *-----------------------------------------------------------------------------
 */

static MXUserPerThread *
MXUserAllocPerThread(void)
{
   MXUserPerThread *perThread;
   MXRecLock *perThreadLock = MXUserInternalSingleton(&perThreadLockMem);

   ASSERT(perThreadLock != NULL);

   MXRecLockAcquire(perThreadLock,
                    NULL);          // non-stats

   if (perThreadFreeList == NULL) {
      perThread = Util_SafeMalloc(sizeof *perThread);
   } else {
      perThread = perThreadFreeList;
      perThreadFreeList = perThread->next;
   }

   MXRecLockRelease(perThreadLock);

   ASSERT(perThread != NULL);

   memset(perThread, 0, sizeof *perThread);  // ensure all zeros

   return perThread;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserFreePerThread --
 *
 *     Free a perThread structure.
 *
 *     The structure is placed on the free list -- for "later".
 *
 * Results:
 *     As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserFreePerThread(MXUserPerThread *perThread)  // IN:
{
   MXRecLock *perThreadLock;

   ASSERT(perThread != NULL);
   ASSERT(perThread->next == NULL);

   perThreadLock = MXUserInternalSingleton(&perThreadLockMem);
   ASSERT(perThreadLock != NULL);

   MXRecLockAcquire(perThreadLock,
                    NULL);          // non-stats

   perThread->next = perThreadFreeList;
   perThreadFreeList = perThread;

   MXRecLockRelease(perThreadLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserGetPerThread --
 *
 *      Return a pointer to the per thread data for the specified thread.
 *
 *      Memory is allocated for the specified thread as necessary. This memory
 *      is never released since it it is highly likely a thread will use a
 *      lock and need to record data in the perThread.
 *
 * Results:
 *      NULL   mayAlloc was FALSE and the thread doesn't have a perThread
 *     !NULL   the perThread of the specified thread
 *
 * Side effects:
 *      Memory may be allocated.
 *
 *-----------------------------------------------------------------------------
 */

static MXUserPerThread *
MXUserGetPerThread(Bool mayAlloc)  // IN: alloc perThread if not present?
{
   HashTable *hash;
   MXUserPerThread *perThread = NULL;
   void *tid = MXUserCastedThreadID();

   hash = HashTable_AllocOnce(&hashTableMem, 1024,
                              HASH_INT_KEY | HASH_FLAG_ATOMIC, NULL);

   if (!HashTable_Lookup(hash, tid, (void **) &perThread)) {
      /* No entry for this tid was found, allocate one? */

      if (mayAlloc) {
         MXUserPerThread *newEntry = MXUserAllocPerThread();

         /*
          * Attempt to (racey) insert a perThread on behalf of the specified
          * thread. If yet another thread takes care of this first, clean up
          * the mess.
          */

         perThread = HashTable_LookupOrInsert(hash, tid, newEntry);
         ASSERT(perThread != NULL);

         if (perThread != newEntry) {
            MXUserFreePerThread(newEntry);
         }
      } else {
         perThread = NULL;
      }
   }

   return perThread;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserListLocks
 *
 *      Allow a caller to list, via warnings, the list of locks the caller
 *      has acquired. Ensure that no memory for lock tracking is allocated
 *      if no locks have been taken.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      The list is printed.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserListLocks(void)
{
   MXUserPerThread *perThread = MXUserGetPerThread(FALSE);

   if (perThread != NULL) {
      uint32 i;

      for (i = 0; i < perThread->locksHeld; i++) {
         MXUserHeader *hdr = perThread->lockArray[i];

         Warning("\tMXUser lock %s (@0x%p) rank 0x%x\n", hdr->name, hdr,
                 hdr->rank);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_IsCurThreadHoldingLocks --
 *
 *      Are any MXUser locks held by the calling thread?
 *
 * Results:
 *      TRUE   Yes
 *      FALSE  No
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_IsCurThreadHoldingLocks(void)
{
   MXUserPerThread *perThread = MXUserGetPerThread(FALSE);

   return (perThread == NULL) ? FALSE : (perThread->locksHeld != 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserThreadRank --
 *
 *      Return the highest rank held by the specified thread via MXUser locks.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      Can optionally determine if a lock has been locked before.
 *
 *-----------------------------------------------------------------------------
 */

static MX_Rank
MXUserThreadRank(MXUserPerThread *perThread,  // IN:
                 MXUserHeader *header,        // IN:
                 Bool *firstUse)              // OUT:
{
   uint32 i;
   Bool foundOnce = TRUE;
   MX_Rank maxRank = RANK_UNRANKED;

   ASSERT(perThread != NULL);

   /*
    * Determine the maximum rank held. Note if the lock being acquired
    * was previously entered into the tracking system.
    */

   for (i = 0; i < perThread->locksHeld; i++) {
      MXUserHeader *chkHdr = perThread->lockArray[i];

      maxRank = MAX(chkHdr->rank, maxRank);

      if (chkHdr == header) {
         foundOnce = FALSE;
      }
   }

   if (firstUse) {
      *firstUse = foundOnce;
   }

   return maxRank;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserCurrentRank --
 *
 *      Return the highest rank held by the current thread via MXUser locks.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

MX_Rank
MXUserCurrentRank(void)
{
   MX_Rank maxRank;
   MXUserPerThread *perThread = MXUserGetPerThread(FALSE);

   if (perThread == NULL) {
      maxRank = RANK_UNRANKED;
   } else {
      maxRank = MXUserThreadRank(perThread, NULL, NULL);
   }

   return maxRank;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserAcquisitionTracking --
 *
 *      Perform the appropriate tracking for lock acquisition.
 *
 * Results:
 *      Panic when a rank violation is detected (checkRank is TRUE).
 *      Add a lock instance to perThread lock list.
 *
 * Side effects:
 *      Manifold.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserAcquisitionTracking(MXUserHeader *header,  // IN:
                          Bool checkRank)        // IN:
{
   MXUserPerThread *perThread = MXUserGetPerThread(TRUE);

   VERIFY(perThread->locksHeld < MXUSER_MAX_LOCKS_PER_THREAD);

   /*
    * Rank checking anyone?
    *
    * Rank checking is abandoned once we're in a panic situation. This will
    * improve the chances of obtaining a good log and/or coredump.
    */

   if (checkRank && (header->rank != RANK_UNRANKED) && !MXUser_InPanic()) {
      MX_Rank maxRank;
      Bool firstInstance = TRUE;

      /*
       * Determine the highest rank held by the calling thread. Check for
       * MX locks if they are present.
       */

      maxRank = MXUserThreadRank(perThread, header, &firstInstance);

      if (MXUserMxCheckRank) {
         maxRank = MAX(maxRank, (*MXUserMxCheckRank)());
      }

      /*
       * Perform rank checking when a lock is entered into the tracking
       * system for the first time. This works out well because:
       *
       * Recursive locks are rank checked only upon their first acquisition...
       * just like MX locks.
       *
       * Exclusive locks will have a second entry added into the tracking
       * system but will immediately panic due to the run time checking - no
       * (real) harm done.
       */

      if (firstInstance && (header->rank <= maxRank)) {
         Warning("%s: lock rank violation by thread %s\n", __FUNCTION__,
                 VThread_CurName());
         Warning("%s: locks held:\n", __FUNCTION__);

         if (MXUserMxLockLister) {
            (*MXUserMxLockLister)();
         }

         MXUserListLocks();

         MXUserDumpAndPanic(header, "%s: rank violation maxRank=0x%x\n",
                            __FUNCTION__, maxRank);
      }
   }

   /* Add a lock instance to the calling threads perThread information */
   perThread->lockArray[perThread->locksHeld++] = header;

   /*
    * Maintain the lock tracking tree when approporiate.
    */

   if (vmx86_devel && vmx86_debug && mxUserCollectLockingTree) {
      uint32 i;
      MXUserLockTreeNode *node = NULL;

      MXUserLockTreeAcquire();

      for (i = 0; i < perThread->locksHeld; i++) {
         header = perThread->lockArray[i];

         node = MXUserLockTreeAdd(node, header->name,
                                  header->serialNumber, header->rank);
      }

      MXUserLockTreeRelease();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserReleaseTracking --
 *
 *      Perform the appropriate tracking for lock release.
 *
 * Results:
 *      A panic.
 *
 * Side effects:
 *      Manifold.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserReleaseTracking(MXUserHeader *header)  // IN: lock, via its header
{
   uint32 i;
   uint32 lastEntry;
   MXUserPerThread *perThread = MXUserGetPerThread(FALSE);

   /* MXUserAcquisitionTracking should have already created a perThread */
   if (UNLIKELY(perThread == NULL)) {
      MXUserDumpAndPanic(header, "%s: perThread not found! (thread 0x%p)\n",
                         __FUNCTION__, MXUserCastedThreadID());
   }

   /* Search the perThread for the argument lock */
   for (i = 0; i < perThread->locksHeld; i++) {
      if (perThread->lockArray[i] == header) {
         break;
      }
   }

   /* The argument lock had better be in the perThread */
   if (UNLIKELY(i >= perThread->locksHeld)) {
      MXUserDumpAndPanic(header,
                         "%s: lock not found! (thread 0x%p; count %u)\n",
                         __FUNCTION__, MXUserCastedThreadID(),
                         perThread->locksHeld);
   }

   /* Remove the argument lock from the perThread */
   lastEntry = perThread->locksHeld - 1;

   if (i < lastEntry) {
      uint32 j;

      for (j = i + 1; j < perThread->locksHeld; j++) {
         perThread->lockArray[i++] = perThread->lockArray[j];
      }
   }

   perThread->lockArray[lastEntry] = NULL;  // tidy up memory
   perThread->locksHeld--;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_TryAcquireFailureControl --
 *
 *      Should a TryAcquire operation fail, no matter "what", sometimes?
 *
 *      Failures occur statistically in debug builds to force our code down
 *      all of its paths.
 *
 * Results:
 *      Unknown
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_TryAcquireFailureControl(Bool (*func)(const char *name))  // IN:
{
   MXUserTryAcquireForceFail = func;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserValidateHeader --
 *
 *      Validate an MXUser object header.
 *
 * Results:
 *      Return  All is well
 *      Panic   All is NOT well
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserValidateHeader(MXUserHeader *header,         // IN:
                     MXUserObjectType objectType)  // IN:
{
   uint32 expected = MXUserGetSignature(objectType);

   if (header->badHeader) {
      return; // No need to panic on a bad header repeatedly...
   }

   if (header->signature != expected) {
      header->badHeader = TRUE;

      MXUserDumpAndPanic(header,
                        "%s: signature failure! expected 0x%X observed 0x%X\n",
                         __FUNCTION__, expected, header->signature);

   }

   if (header->serialNumber == 0) {
      header->badHeader = TRUE;

      MXUserDumpAndPanic(header, "%s: Invalid serial number!\n",
                         __FUNCTION__);
   }
}
#endif
  07070100000175000081A40000000000000000000000016822550500002355000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulBarrier.c   /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include "vmware.h"
#include "str.h"
#include "util.h"
#include "userlock.h"
#include "ulInt.h"

struct BarrierContext
{
   uint32           count;    // Number of threads currently in this context
   MXUserCondVar   *condVar;  // Threads within this context are parked here
};

typedef struct BarrierContext BarrierContext;

struct MXUserBarrier
{
   MXUserHeader     header;        // Barrier's ID information
   MXUserExclLock  *lock;          // Barrier's (internal) lock
   uint32           configCount;   // Hold until this many threads arrive
   volatile uint32  curContext;    // Arrivals go to this context
   BarrierContext   contexts[2];   // The normal and abnormal contexts
};


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDumpBarrier --
 *
 *      Dump a barrier.
 *
 * Results:
 *      A dump.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserDumpBarrier(MXUserHeader *header)  // IN:
{
   uint32 curContext;
   MXUserBarrier *barrier = (MXUserBarrier *) header;

   Warning("%s: Barrier @ 0x%p\n", __FUNCTION__, barrier);

   Warning("\tsignature 0x%X\n", barrier->header.signature);
   Warning("\tname %s\n", barrier->header.name);
   Warning("\trank 0x%X\n", barrier->header.rank);
   Warning("\tserial number %"FMT64"u\n", barrier->header.serialNumber);

   Warning("\tlock 0x%p\n", barrier->lock);
   Warning("\tconfigured count %u\n", barrier->configCount);
   curContext = barrier->curContext;

   Warning("\tcurrent context %u\n", curContext);

   Warning("\tcontext[%u] count %u\n", curContext,
           barrier->contexts[curContext].count);
   Warning("\tcontext[%u] condVar 0x%p\n", curContext,
           &barrier->contexts[curContext].condVar);

   curContext = (curContext + 1) & 0x1;

   Warning("\tcontext[%u] count %u\n", curContext,
           barrier->contexts[curContext].count);
   Warning("\tcontext[%u] condVar 0x%p\n", curContext,
           &barrier->contexts[curContext].condVar);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateBarrier --
 *
 *      Create a computational barrier.
 *
 *      The barriers are self regenerating - they do not need to be
 *      initialized or reset after creation.
 *
 * Results:
 *      A pointer to a barrier.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

MXUserBarrier *
MXUser_CreateBarrier(const char *userName,  // IN: shall be known as
                     MX_Rank rank,          // IN: internal lock's rank
                     uint32 count)          // IN:
{
   char *properName;
   MXUserBarrier *barrier;

   ASSERT(count != 0);

   barrier = Util_SafeCalloc(1, sizeof *barrier);

   if (userName == NULL) {
      properName = Str_SafeAsprintf(NULL, "Barrier-%p", GetReturnAddress());
   } else {
      properName = Util_SafeStrdup(userName);
   }

   barrier->lock = MXUser_CreateExclLock(properName, rank);
   barrier->contexts[0].condVar = MXUser_CreateCondVarExclLock(barrier->lock);
   barrier->contexts[1].condVar = MXUser_CreateCondVarExclLock(barrier->lock);

   barrier->configCount = count;
   barrier->curContext = 0;

   barrier->header.signature = MXUserGetSignature(MXUSER_TYPE_BARRIER);
   barrier->header.name = properName;
   barrier->header.rank = rank;
   barrier->header.serialNumber = MXUserAllocSerialNumber();
   barrier->header.dumpFunc = MXUserDumpBarrier;
   barrier->header.statsFunc = NULL;

   MXUserAddToList(&barrier->header);

   return barrier;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DestroyBarrier --
 *
 *      Destroy a barrier.
 *
 * Results:
 *      The barrier is destroyed. Don't try to use the pointer again.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_DestroyBarrier(MXUserBarrier *barrier)  // IN/OUT:
{
   if (LIKELY(barrier != NULL)) {
      MXUserValidateHeader(&barrier->header, MXUSER_TYPE_BARRIER);

      if ((barrier->contexts[0].count != 0) ||
          (barrier->contexts[1].count != 0)) {
         MXUserDumpAndPanic(&barrier->header,
                            "%s: Attempted destroy on barrier while in use\n",
                            __FUNCTION__);
      }

      barrier->header.signature = 0;  // just in case...

      MXUserRemoveFromList(&barrier->header);

      MXUser_DestroyCondVar(barrier->contexts[0].condVar);
      MXUser_DestroyCondVar(barrier->contexts[1].condVar);
      MXUser_DestroyExclLock(barrier->lock);

      free(barrier->header.name);
      barrier->header.name = NULL;
      free(barrier);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_EnterBarrier --
 *
 *      Enter a barrier
 *
 *      All threads entering the barrier will be suspended until the number
 *      threads that have entered reaches the configured number upon which
 *      time all of the threads will return from this routine.
 *
 *      "Nobody comes out until everyone goes in."
 *
 * Results:
 *      None
 *
 * Side effects:
 *      The caller may sleep.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_EnterBarrier(MXUserBarrier *barrier)  // IN/OUT:
{
   BarrierContext *ptr;
   uint32 context;

   ASSERT(barrier != NULL);
   MXUserValidateHeader(&barrier->header, MXUSER_TYPE_BARRIER);

   MXUser_AcquireExclLock(barrier->lock);

   context = barrier->curContext;
   ptr = &barrier->contexts[context];

   ptr->count++;

   if (ptr->count == barrier->configCount) {
      /*
       * The last thread has entered; release the other threads
       *
       * Flip the current context. Should a thread leave the barrier and
       * enter the barrier while the barrier is "emptying" the thread will
       * park on the condVar that is not "emptying". Eventually everything
       * will "work out" and all of the threads will be parked on the opposite
       * context's condVar.
       */

      barrier->curContext = (context + 1) & 0x1;
      ASSERT(barrier->contexts[barrier->curContext].count == 0);

      /* Wake up all of the waiting threads. */
      MXUser_BroadcastCondVar(ptr->condVar);
   } else {
      /*
       * Not the last thread in... wait/sleep until the last thread appears.
       * Protect against spurious wakeups.
       */

      while (barrier->curContext == context) {
         MXUser_WaitCondVarExclLock(barrier->lock, ptr->condVar);
      }
   }

   ptr->count--;

   MXUser_ReleaseExclLock(barrier->lock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateSingletonBarrier --
 *
 *      Ensures that the specified backing object (Atomic_Ptr) contains a
 *      barrier. This is useful for modules that need to protect something
 *      with a barrier but don't have an existing Init() entry point where a
 *      barrier can be created.
 *
 * Results:
 *      A pointer to the requested barrier.
 *
 * Side effects:
 *      Generally the barrier's resources are intentionally leaked (by design).
 *
 *-----------------------------------------------------------------------------
 */

MXUserBarrier *
MXUser_CreateSingletonBarrier(Atomic_Ptr *barrierStorage,  // IN/OUT:
                              const char *name,            // IN:
                              MX_Rank rank,                // IN:
                              uint32 count)                // IN:
{
   MXUserBarrier *barrier;

   ASSERT(barrierStorage != NULL);

   barrier = Atomic_ReadPtr(barrierStorage);

   if (UNLIKELY(barrier == NULL)) {
      MXUserBarrier *newBarrier = MXUser_CreateBarrier(name, rank, count);

      barrier = Atomic_ReadIfEqualWritePtr(barrierStorage, NULL,
                                           (void *) newBarrier);

      if (barrier) {
         MXUser_DestroyBarrier(newBarrier);
      } else {
         barrier = Atomic_ReadPtr(barrierStorage);
      }
   }

   return barrier;
}

   07070100000176000081A40000000000000000000000016822550500003679000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulCondVar.c   /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#if defined(_WIN32)
#include <windows.h>
#else
#include <sys/time.h>
#endif

#include "vmware.h"
#include "vm_basic_asm.h"
#include "str.h"
#include "err.h"
#include "util.h"
#include "userlock.h"
#include "ulInt.h"

/*
 * A portable condition variable
 */

struct MXUserCondVar {
   uint32             signature;
   MXUserHeader      *header;
   MXRecLock         *ownerLock;
   Atomic_uint32      referenceCount;

#if defined(_WIN32)
   CONDITION_VARIABLE condObject;
#else
   pthread_cond_t     condObject;
#endif
};

#if defined(_WIN32)
/*
 *-----------------------------------------------------------------------------
 *
 * MXUserCreateInternal --
 *
 *      Create/initialize the environmentally specific portion of an
 *      MXUserCondVar.
 *
 * Results:
 *      TRUE   Success
 *      FALSE  Failure
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
MXUserCreateInternal(MXUserCondVar *condVar)  // IN/OUT:
{
   InitializeConditionVariable(&condVar->condObject);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDestroyInternal --
 *
 *      Destroy the environmentally specific portion of an MXUserCondVar.
 *
 * Results:
 *      As expect.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
MXUserDestroyInternal(MXUserCondVar *condVar)  // IN/OUT:
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserWaitInternal --
 *
 *      Perform the environmentally specific portion of a wait on an
 *      MXUserCondVar.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      It is possible to return from this routine without the condition
 *      variable having been signalled (spurious wake up); code accordingly!
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
MXUserWaitInternal(MXRecLock *lock,         // IN/OUT:
                   MXUserCondVar *condVar,  // IN/OUT:
                   uint32 waitTimeMsec)     // IN:
{
   int lockCount = MXRecLockCount(lock);
   DWORD waitTime = (waitTimeMsec == MXUSER_WAIT_INFINITE) ? INFINITE :
                                                             waitTimeMsec;

   /*
    * When using the native lock found within the MXUser lock, be sure to
    * decrement the count before the wait/sleep and increment it after the
    * wait/sleep - the (native) wait/sleep will perform a lock release
    * before the wait/sleep and a lock acquisition after the wait/sleep.
    * The MXUser internal accounting information must be maintained.
    */

   MXRecLockDecCount(lock, lockCount);
   SleepConditionVariableCS(&condVar->condObject, &lock->nativeLock,
                            waitTime);
   MXRecLockIncCount(lock, lockCount);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserSignalInternal --
 *
 *      Perform the environmentally specific portion of signalling an
 *      MXUserCondVar.
 *
 * Results:
 *      0    Success
 *      !0   Error
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE int
MXUserSignalInternal(MXUserCondVar *condVar)  // IN/OUT:
{
   WakeConditionVariable(&condVar->condObject);

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserBroadcastInternal --
 *
 *      Perform the environmentally specific portion of broadcasting on an
 *      MXUserCondVar.
 *
 * Results:
 *      0    Success
 *      !0   Error
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */


static INLINE int
MXUserBroadcastInternal(MXUserCondVar *condVar)  // IN/OUT:
{
   WakeAllConditionVariable(&condVar->condObject);

   return 0;
}

#else /* _WIN32 */

/*
 *-----------------------------------------------------------------------------
 *
 * MXUserCreateInternal --
 *
 *      Create/initialize the environmentally specific portion of an
 *      MXUserCondVar.
 *
 * Results:
 *      TRUE   Success
 *      FALSE  Failure
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
MXUserCreateInternal(MXUserCondVar *condVar)  // IN/OUT:
{
   return pthread_cond_init(&condVar->condObject, NULL) == 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDestroyInternal --
 *
 *      Destroy the environmentally specific portion of an MXUserCondVar.
 *
 * Results:
 *      As expected.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
MXUserDestroyInternal(MXUserCondVar *condVar)  // IN/OUT:
{
   pthread_cond_destroy(&condVar->condObject);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserWaitInternal --
 *
 *      Perform the environmentally specific portion of a wait on an
 *      MXUserCondVar.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      It is possible to return from this routine without the condition
 *      variable having been signalled (spurious wake up); code accordingly!
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
MXUserWaitInternal(MXRecLock *lock,         // IN/OUT:
                   MXUserCondVar *condVar,  // IN/OUT:
                   uint32 waitTimeMsec)     // IN:
{
   int err;
   int lockCount = MXRecLockCount(lock);

   /*
    * When using the native lock found within the MXUser lock, be sure to
    * decrement the count before the wait/sleep and increment it after the
    * wait/sleep - the (native) wait/sleep will perform a lock release before
    * the wait/sleep and a lock acquisition after the wait/sleep. The
    * MXUser internal accounting information must be maintained.
    */

   MXRecLockDecCount(lock, lockCount);

   if (waitTimeMsec == MXUSER_WAIT_INFINITE) {
      err = pthread_cond_wait(&condVar->condObject, &lock->nativeLock);
   } else {
      struct timeval curTime;
      struct timespec endTime;
      uint64 endNS;

      /*
       * pthread_cond_timedwait takes an absolute time. Yes, this is
       * beyond ridiculous, and the justifications for this
       * vs. relative time make no sense. But IIWII.
       */
#define A_BILLION (1000 * 1000 * 1000)
      gettimeofday(&curTime, NULL);
      endNS = ((uint64) curTime.tv_sec * A_BILLION) +
              ((uint64) curTime.tv_usec * 1000) +
              ((uint64) waitTimeMsec * (1000 * 1000));

      endTime.tv_sec = (time_t) (endNS / A_BILLION);
      endTime.tv_nsec = (long int) (endNS % A_BILLION);
#undef A_BILLION

      err = pthread_cond_timedwait(&condVar->condObject, &lock->nativeLock,
                                   &endTime);
   }

   MXRecLockIncCount(lock, lockCount);

   if (err != 0) {
      if (err != ETIMEDOUT) {
         Panic("%s: failure %d on condVar (0x%p; %s)\n", __FUNCTION__, err,
               condVar, condVar->header->name);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserSignalInternal --
 *
 *      Perform the environmentally specific portion of signalling an
 *      MXUserCondVar.
 *
 * Results:
 *      0    Success
 *      !0   Error
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE int
MXUserSignalInternal(MXUserCondVar *condVar)  // IN/OUT:
{
   return pthread_cond_signal(&condVar->condObject);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserBroadcaseInternal --
 *
 *      Perform the environmentally specific portion of broadcasting on an
 *      MXUserCondVar.
 *
 * Results:
 *      0    Success
 *      !0   Error
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE int
MXUserBroadcastInternal(MXUserCondVar *condVar)  // IN/OUT:
{
   return pthread_cond_broadcast(&condVar->condObject);
}

#endif /* _WIN32 */


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserCreateCondVar --
 *
 *      Create/initialize a condition variable associated with the specified
 *      lock.
 *
 * Results:
 *      !NULL  Success; a pointer to the (new) condition variable
 *      NULL   Failure
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

MXUserCondVar *
MXUserCreateCondVar(MXUserHeader *header,  // IN:
                    MXRecLock *lock)       // IN:
{
   MXUserCondVar *condVar = Util_SafeCalloc(1, sizeof *condVar);

   if (UNLIKELY(!MXUserCreateInternal(condVar))) {
      Panic("%s: native lock initialization routine failed\n", __FUNCTION__);
   }

   condVar->signature = MXUserGetSignature(MXUSER_TYPE_CONDVAR);
   condVar->header = header;
   condVar->ownerLock = lock;

   return condVar;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserWaitCondVar --
 *
 *      The internal wait on a condition variable routine.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      An attempt to use a lock other than the one the specified condition
 *      variable was specified for will result in a panic.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserWaitCondVar(MXUserHeader *header,    // IN:
                  MXRecLock *lock,         // IN/OUT:
                  MXUserCondVar *condVar,  // IN/OUT:
                  uint32 waitTimeMsec)     // IN:
{
   ASSERT(header != NULL);
   ASSERT(lock != NULL);
   ASSERT(condVar != NULL);
   ASSERT(condVar->signature == MXUserGetSignature(MXUSER_TYPE_CONDVAR));

   if (condVar->ownerLock != lock) {
      Panic("%s: invalid use of lock %s with condVar (0x%p; %s)\n",
             __FUNCTION__, header->name, condVar, condVar->header->name);
   }

   if (vmx86_debug && !MXRecLockIsOwner(lock)) {
      Panic("%s: lock %s for condVar (0x%p) not owned\n",
            __FUNCTION__, condVar->header->name, condVar);
   }

   Atomic_Inc(&condVar->referenceCount);
   MXUserWaitInternal(lock, condVar, waitTimeMsec);
   Atomic_Dec(&condVar->referenceCount);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_SignalCondVar
 *
 *      Signal on a condVar - wake up one thread blocked on the specified
 *      condition variable.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_SignalCondVar(MXUserCondVar *condVar)  // IN/OUT:
{
   int err;

   ASSERT(condVar != NULL);
   ASSERT(condVar->signature == MXUserGetSignature(MXUSER_TYPE_CONDVAR));

   err = MXUserSignalInternal(condVar);

   if (err != 0) {
      Panic("%s: failure %d on condVar (0x%p; %s) \n", __FUNCTION__, err,
            condVar, condVar->header->name);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_BroadcastCondVar
 *
 *      Broadcast on a condVar - wake up all threads blocked on the specified
 *      condition variable.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_BroadcastCondVar(MXUserCondVar *condVar)  // IN/OUT:
{
   int err;

   ASSERT(condVar != NULL);
   ASSERT(condVar->signature == MXUserGetSignature(MXUSER_TYPE_CONDVAR));

   err = MXUserBroadcastInternal(condVar);

   if (err != 0) {
      Panic("%s: failure %d on condVar (0x%p; %s) \n", __FUNCTION__, err,
            condVar, condVar->header->name);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DestroyCondVar --
 *
 *      Destroy a condition variable.
 *
 *      A condVar must be destroyed before the lock it is associated with.
 *
 * Results:
 *      As above. Don't use the pointer again...
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_DestroyCondVar(MXUserCondVar *condVar)  // IN/OUT:
{
   if (condVar != NULL) {
      ASSERT(condVar->signature == MXUserGetSignature(MXUSER_TYPE_CONDVAR));

      if (Atomic_Read(&condVar->referenceCount) != 0) {
         Panic("%s: Attempted destroy on active condVar (0x%p; %s)\n",
               __FUNCTION__, condVar, condVar->header->name);
      }

      condVar->signature = 0;  // just in case...

      MXUserDestroyInternal(condVar);

      condVar->header = NULL;
      condVar->ownerLock = NULL;

      free(condVar);
   }
}
   07070100000177000081A40000000000000000000000016822550500004E46000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulExcl.c  /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include "vmware.h"
#include "str.h"
#include "util.h"
#include "userlock.h"
#include "hostinfo.h"
#include "ulInt.h"

struct MXUserExclLock
{
   MXUserHeader  header;
   MXRecLock     recursiveLock;
   Atomic_Ptr    heldStatsMem;
   Atomic_Ptr    acquireStatsMem;
};


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserStatsActionExcl --
 *
 *      Perform the statistics action for the specified lock.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserStatsActionExcl(MXUserHeader *header)  // IN:
{
   MXUserExclLock *lock = (MXUserExclLock *) header;
   MXUserHeldStats *heldStats = Atomic_ReadPtr(&lock->heldStatsMem);
   MXUserAcquireStats *acquireStats = Atomic_ReadPtr(&lock->acquireStatsMem);

   if (UNLIKELY(heldStats != NULL)) {
      MXUserDumpBasicStats(&heldStats->data, header);

      if (Atomic_ReadPtr(&heldStats->histo) != NULL) {
         MXUserHistoDump(Atomic_ReadPtr(&heldStats->histo), header);
      }
   }

   if (LIKELY(acquireStats != NULL)) {
      Bool isHot;
      Bool doLog;
      double contentionRatio;

      /*
       * Dump the statistics for the specified lock.
       */

      MXUserDumpAcquisitionStats(&acquireStats->data, header);

      if (Atomic_ReadPtr(&acquireStats->histo) != NULL) {
         MXUserHistoDump(Atomic_ReadPtr(&acquireStats->histo), header);
      }

      /*
       * Has the lock gone "hot"? If so, implement the hot actions.
       */

      MXUserKitchen(&acquireStats->data, &contentionRatio, &isHot, &doLog);

      if (UNLIKELY(isHot)) {
         MXUserForceAcquisitionHisto(&lock->acquireStatsMem,
                                     MXUSER_DEFAULT_HISTO_MIN_VALUE_NS,
                                     MXUSER_DEFAULT_HISTO_DECADES);

         if (UNLIKELY(heldStats != NULL)) {
            MXUserForceHeldHisto(&lock->heldStatsMem,
                                 MXUSER_DEFAULT_HISTO_MIN_VALUE_NS,
                                 MXUSER_DEFAULT_HISTO_DECADES);
         }

         if (doLog) {
            Log("HOT LOCK (%s); contention ratio %f\n",
                lock->header.name, contentionRatio);
         }
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_EnableStatsExclLock
 *
 *      Enable basic stats on the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_EnableStatsExclLock(MXUserExclLock *lock,       // IN/OUT:
                           Bool trackAcquisitionTime,  // IN:
                           Bool trackHeldTime)         // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   if (vmx86_stats) {
      MXUserEnableStats(trackAcquisitionTime ? &lock->acquireStatsMem : NULL,
                        trackHeldTime        ? &lock->heldStatsMem    : NULL);
   }

   return vmx86_stats;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DisableStatsExclLock --
 *
 *      Disable basic stats on the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_DisableStatsExclLock(MXUserExclLock *lock)  // IN/OUT:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   if (vmx86_stats) {
      MXUserDisableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
   }

   return vmx86_stats;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_SetContentionRatioFloorExclLock --
 *
 *      Set the contention ratio floor for the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_SetContentionRatioFloorExclLock(MXUserExclLock *lock,  // IN/OUT:
                                       double ratio)          // IN:
{
   Bool result;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   if (vmx86_stats) {
      result = MXUserSetContentionRatioFloor(&lock->acquireStatsMem, ratio);
   } else {
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_SetContentionCountFloorExclLock --
 *
 *      Set the contention count floor for the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_SetContentionCountFloorExclLock(MXUserExclLock *lock,  // IN/OUT:
                                       uint64 count)          // IN:
{
   Bool result;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   if (vmx86_stats) {
      result = MXUserSetContentionCountFloor(&lock->acquireStatsMem, count);
   } else {
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_SetContentionDurationFloorExclLock --
 *
 *      Set the contention count floor for the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_SetContentionDurationFloorExclLock(MXUserExclLock *lock,  // IN/OUT:
                                          uint64 duration)       // IN:
{
   Bool result;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   if (vmx86_stats) {
      result = MXUserSetContentionDurationFloor(&lock->acquireStatsMem,
                                                duration);
   } else {
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDumpExclLock --
 *
 *      Dump an exclusive lock.
 *
 * Results:
 *      A dump.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserDumpExclLock(MXUserHeader *header)  // IN:
{
   MXUserExclLock *lock = (MXUserExclLock *) header;

   Warning("%s: Exclusive lock @ %p\n", __FUNCTION__, lock);

   Warning("\tsignature 0x%X\n", lock->header.signature);
   Warning("\tname %s\n", lock->header.name);
   Warning("\trank 0x%X\n", lock->header.rank);
   Warning("\tserial number %"FMT64"u\n", lock->header.serialNumber);

   Warning("\tlock count %d\n", MXRecLockCount(&lock->recursiveLock));

   Warning("\taddress of owner data %p\n",
           &lock->recursiveLock.nativeThreadID);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateExclLock --
 *
 *      Create an exclusive lock.
 *
 * Results:
 *      A pointer to an exclusive lock.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

MXUserExclLock *
MXUser_CreateExclLock(const char *userName,  // IN:
                      MX_Rank rank)          // IN:
{
   uint32 statsMode;
   char *properName;
   MXUserExclLock *lock = Util_SafeCalloc(1, sizeof *lock);

   if (userName == NULL) {
      properName = Str_SafeAsprintf(NULL, "X-%p", GetReturnAddress());
   } else {
      properName = Util_SafeStrdup(userName);
   }

   if (UNLIKELY(!MXRecLockInit(&lock->recursiveLock))) {
      Panic("%s: native lock initialization routine failed\n", __FUNCTION__);
   }

   lock->header.signature = MXUserGetSignature(MXUSER_TYPE_EXCL);
   lock->header.name = properName;
   lock->header.rank = rank;
   lock->header.serialNumber = MXUserAllocSerialNumber();
   lock->header.dumpFunc = MXUserDumpExclLock;

   statsMode = MXUserStatsMode();

   switch (MXUserStatsMode()) {
   case 0:
      MXUserDisableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
      lock->header.statsFunc = NULL;
      break;

   case 1:
      MXUserEnableStats(&lock->acquireStatsMem, NULL);
      lock->header.statsFunc = MXUserStatsActionExcl;
      break;

   case 2:
      MXUserEnableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
      lock->header.statsFunc = MXUserStatsActionExcl;
      break;

   default:
      Panic("%s: unknown stats mode: %d!\n", __FUNCTION__, statsMode);
   }

   MXUserAddToList(&lock->header);

   return lock;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DestroyExclLock --
 *
 *      Destroy an exclusive lock.
 *
 * Results:
 *      Lock is destroyed. Don't use the pointer again.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_DestroyExclLock(MXUserExclLock *lock)  // IN:
{
   if (lock != NULL) {
      MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

      if (MXRecLockCount(&lock->recursiveLock) > 0) {
         MXUserDumpAndPanic(&lock->header,
                            "%s: Destroy of an acquired exclusive lock\n",
                            __FUNCTION__);
      }

      MXRecLockDestroy(&lock->recursiveLock);

      MXUserRemoveFromList(&lock->header);

      if (vmx86_stats) {
         MXUserDisableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
      }

      lock->header.signature = 0;  // just in case...
      free(lock->header.name);
      lock->header.name = NULL;
      free(lock);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_AcquireExclLock --
 *
 *      An acquisition is made (lock is taken) on the specified exclusive lock.
 *
 * Results:
 *      The lock is acquired (locked).
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_AcquireExclLock(MXUserExclLock *lock)  // IN/OUT:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   MXUserAcquisitionTracking(&lock->header, TRUE);

   if (vmx86_stats) {
      VmTimeType value = 0;
      MXUserAcquireStats *acquireStats;

      acquireStats = Atomic_ReadPtr(&lock->acquireStatsMem);

      MXRecLockAcquire(&lock->recursiveLock,
                       (acquireStats == NULL) ? NULL : &value);

      if (LIKELY(acquireStats != NULL)) {
         MXUserHisto *histo;
         MXUserHeldStats *heldStats;

         MXUserAcquisitionSample(&acquireStats->data, TRUE,
                            value > acquireStats->data.contentionDurationFloor,
                                 value);

         histo = Atomic_ReadPtr(&acquireStats->histo);

         if (UNLIKELY(histo != NULL)) {
            MXUserHistoSample(histo, value, GetReturnAddress());
         }

         heldStats = Atomic_ReadPtr(&lock->heldStatsMem);

         if (UNLIKELY(heldStats != NULL)) {
            heldStats->holdStart = Hostinfo_SystemTimerNS();
         }
      }
   } else {
      MXRecLockAcquire(&lock->recursiveLock,
                       NULL);  // non-stats
   }

   if (vmx86_debug && (MXRecLockCount(&lock->recursiveLock) > 1)) {
      MXUserDumpAndPanic(&lock->header,
                         "%s: Acquire on an acquired exclusive lock\n",
                         __FUNCTION__);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_ReleaseExclLock --
 *
 *      Release (unlock) an exclusive lock.
 *
 * Results:
 *      The lock is released.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_ReleaseExclLock(MXUserExclLock *lock)  // IN/OUT:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   if (vmx86_stats) {
      MXUserHeldStats *heldStats = Atomic_ReadPtr(&lock->heldStatsMem);

      if (UNLIKELY(heldStats != NULL)) {
         VmTimeType value = Hostinfo_SystemTimerNS() - heldStats->holdStart;
         MXUserHisto *histo = Atomic_ReadPtr(&heldStats->histo);

         MXUserBasicStatsSample(&heldStats->data, value);

         if (UNLIKELY(histo != NULL)) {
            MXUserHistoSample(histo, value, GetReturnAddress());
         }
      }
   }

   if (vmx86_debug) {
      if (MXRecLockCount(&lock->recursiveLock) == 0) {
         MXUserDumpAndPanic(&lock->header,
                            "%s: Release of an unacquired exclusive lock\n",
                             __FUNCTION__);
      }

      if (!MXRecLockIsOwner(&lock->recursiveLock)) {
         MXUserDumpAndPanic(&lock->header,
                            "%s: Non-owner release of a exclusive lock\n",
                            __FUNCTION__);
      }
   }

   MXUserReleaseTracking(&lock->header);

   MXRecLockRelease(&lock->recursiveLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_TryAcquireExclLock --
 *
 *      An attempt is made to conditionally acquire (lock) an exclusive lock.
 *
 * Results:
 *      TRUE    Acquired (locked)
 *      FALSE   Not acquired
 *
 * Side effects:
 *      None
 *
 * NOTE:
 *      A "TryAcquire" does not rank check should the acquisition succeed.
 *      This duplicates the behavor of MX locks.
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_TryAcquireExclLock(MXUserExclLock *lock)  // IN/OUT:
{
   Bool success;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   if (MXUserTryAcquireFail(lock->header.name)) {
      return FALSE;
   }

   success = MXRecLockTryAcquire(&lock->recursiveLock);

   if (success) {
      MXUserAcquisitionTracking(&lock->header, FALSE);

      if (vmx86_debug && (MXRecLockCount(&lock->recursiveLock) > 1)) {
         MXUserDumpAndPanic(&lock->header,
                            "%s: Acquire on an acquired exclusive lock\n",
                            __FUNCTION__);
      }
   }

   if (vmx86_stats) {
      MXUserAcquireStats *acquireStats;

      acquireStats = Atomic_ReadPtr(&lock->acquireStatsMem);

      if (LIKELY(acquireStats != NULL)) {
         MXUserAcquisitionSample(&acquireStats->data, success, !success,
                                 0ULL);
      }
   }

   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_IsCurThreadHoldingExclLock --
 *
 *      Is an exclusive lock held by the calling thread?
 *
 * Results:
 *      TRUE    Yes
 *      FALSE   No
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_IsCurThreadHoldingExclLock(MXUserExclLock *lock)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   return MXRecLockIsOwner(&lock->recursiveLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateSingletonExclLockInt --
 *
 *      Ensures that the specified backing object (Atomic_Ptr) contains a
 *      exclusive lock. This is useful for modules that need to protect
 *      something with a lock but don't have an existing Init() entry point
 *      where a lock can be created.
 *
 * Results:
 *      A pointer to the requested lock.
 *
 * Side effects:
 *      Generally the lock's resources are intentionally leaked (by design).
 *
 *-----------------------------------------------------------------------------
 */

MXUserExclLock *
MXUser_CreateSingletonExclLockInt(Atomic_Ptr *lockStorage,  // IN/OUT:
                                  const char *name,         // IN:
                                  MX_Rank rank)             // IN:
{
   MXUserExclLock *lock;

   ASSERT(lockStorage != NULL);

   lock = Atomic_ReadPtr(lockStorage);

   if (UNLIKELY(lock == NULL)) {
      MXUserExclLock *newLock = MXUser_CreateExclLock(name, rank);

      lock = Atomic_ReadIfEqualWritePtr(lockStorage, NULL, (void *) newLock);

      if (lock) {
         MXUser_DestroyExclLock(newLock);
      } else {
         lock = Atomic_ReadPtr(lockStorage);
      }
   }

   return lock;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateCondVarExclLock --
 *
 *      Create a condition variable for use with the specified exclusive lock.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      The created condition variable will cause a run-time error if it is
 *      used with a lock other than the one it was created for.
 *
 *-----------------------------------------------------------------------------
 */

MXUserCondVar *
MXUser_CreateCondVarExclLock(MXUserExclLock *lock)
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   return MXUserCreateCondVar(&lock->header, &lock->recursiveLock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_WaitCondVarExclLock --
 *
 *      Block (sleep) on the specified condition variable. The specified lock
 *      is released upon blocking and is reacquired before returning from this
 *      function.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      It is possible to return from this routine without the condtion
 *      variable having been signalled (spurious wake up); code accordingly!
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_WaitCondVarExclLock(MXUserExclLock *lock,    // IN:
                           MXUserCondVar *condVar)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   MXUserWaitCondVar(&lock->header, &lock->recursiveLock, condVar,
                     MXUSER_WAIT_INFINITE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_TimedWaitCondVarExclLock --
 *
 *      Block (sleep) on the specified condition variable for no longer than
 *      the specified amount of time. The specified lock is released upon
 *      blocking and is reacquired before returning from this function.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      It is possible to return from this routine without the condtion
 *      variable having been signalled (spurious wake up); code accordingly!
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_TimedWaitCondVarExclLock(MXUserExclLock *lock,    // IN:
                                MXUserCondVar *condVar,  // IN:
                                uint32 waitTimeMsec)     // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_EXCL);

   MXUserWaitCondVar(&lock->header, &lock->recursiveLock, condVar,
                     waitTimeMsec);
}
  07070100000178000081A40000000000000000000000016822550500003EB0000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulInt.h   /*********************************************************
 * Copyright (C) 2009-2019, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _ULINT_H_
#define _ULINT_H_

#if defined(_WIN32)
#include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#if defined(__linux__)
#include <features.h>
#endif

#if defined(_WIN32)
typedef DWORD MXUserThreadID;
#define MXUSER_INVALID_OWNER 0xFFFFFFFF
#else
#include <pthread.h>
typedef pthread_t MXUserThreadID;
#endif

#include "vm_basic_types.h"
#include "vm_atomic.h"
#include "vthreadBase.h"
#include "hostinfo.h"

#include "circList.h"

#define MXUSER_STAT_CLASS_ACQUISITION "a"
#define MXUSER_STAT_CLASS_HELD        "h"

/*
 * A portable recursive lock.
 */

#define MXUSER_MAX_REC_DEPTH 16

typedef struct {
#if defined(_WIN32)
   CRITICAL_SECTION nativeLock;       // Native lock object
#else
   pthread_mutex_t  nativeLock;       // Native lock object
#endif

   int              referenceCount;   // Acquisition count
   MXUserThreadID   nativeThreadID;   // Native thread ID
} MXRecLock;


/*
 * Environment specific implementations of portable recursive locks.
 *
 * A recursive lock is used throughput the MXUser locks because:
 *     - it can be used recursively for recursive locks
 *     - exclusive (non-recursive) locks catch the recursion and panic
 *       rather than deadlock.
 *
 * There are 9 environment specific primitives:
 *
 * MXUserNativeThreadID         Return native thread ID
 * MXRecLockCreateInternal      Create lock before use
 * MXRecLockDestroyInternal     Destroy lock after use
 * MXRecLockIsOwner             Is lock owned by the caller?
 * MXRecLockSetNoOwner          Set lock as owner by "nobody"
 * MXRecLockSetOwner            Set lock owner
 * MXRecLockAcquireInternal     Lock the lock
 * MXRecLockTryAcquireInternal  Conditionally acquire the lock
 * MXRecLockReleaseInternal     Unlock the lock
 *
 * Windows has a native recursive lock, the CRITICAL_SECTION. POSIXen,
 * unfortunately, do not ensure access to such a facility. The recursive
 * attribute of pthread_mutex_t is not implemented in all environments so
 * we create a recursive implementation using an exclusive pthread_mutex_t
 * and a few lines of code (most of which we need to do anyway).
 */

#if defined(_WIN32)
static INLINE int
MXRecLockCreateInternal(MXRecLock *lock)  // IN/OUT:
{
   Bool success;

   /* http://msdn.microsoft.com/en-us/library/ms682530(VS.85).aspx */
   /* magic number - allocate resources immediately; spin 0x400 times */
   success = InitializeCriticalSectionAndSpinCount(&lock->nativeLock,
                                                   0x80000400);

   return success ? 0 : GetLastError();
}


static INLINE int
MXRecLockDestroyInternal(MXRecLock *lock)  // IN/OUT:
{
   DeleteCriticalSection(&lock->nativeLock);

   return 0;
}


static INLINE MXUserThreadID
MXUserNativeThreadID(void)
{
   return GetCurrentThreadId();
}


static INLINE Bool
MXRecLockIsOwner(const MXRecLock *lock)  // IN:
{
   return lock->nativeThreadID == MXUserNativeThreadID();
}


static INLINE void
MXRecLockSetNoOwner(MXRecLock *lock)  // IN:
{
   lock->nativeThreadID = MXUSER_INVALID_OWNER;
}


static INLINE void
MXRecLockSetOwner(MXRecLock *lock)  // IN/OUT:
{
   lock->nativeThreadID = MXUserNativeThreadID();
}


static INLINE int
MXRecLockAcquireInternal(MXRecLock *lock)  // IN:
{
   EnterCriticalSection(&lock->nativeLock);

   return 0;
}


static INLINE int
MXRecLockTryAcquireInternal(MXRecLock *lock)  // IN:
{
   return TryEnterCriticalSection(&lock->nativeLock) ? 0 : EBUSY;
}


static INLINE int
MXRecLockReleaseInternal(MXRecLock *lock)  // IN:
{
   LeaveCriticalSection(&lock->nativeLock);

   return 0;
}
#else
static INLINE int
MXRecLockCreateInternal(MXRecLock *lock)  // IN/OUT:
{
   return pthread_mutex_init(&lock->nativeLock, NULL);
}


static INLINE int
MXRecLockDestroyInternal(MXRecLock *lock)  // IN:
{
   int err = pthread_mutex_destroy(&lock->nativeLock);

#if defined(__GLIBC__)
   extern const char *gnu_get_libc_version(void) __attribute__ ((weak));

   if (gnu_get_libc_version != NULL) {
      /*
       * Elision locking may be used but only on Haswell and newer (compatible)
       * processors and with glibc 2.18 or later. Unfortunately there is a
       * problem with the implemenation that requires ignoring any error here.
       *
       * TODO: Change the version checking to use a range once the issue is
       *       fixed.
       *
       * See PR 1200129 for more information.
       */

      const char *version = gnu_get_libc_version();

      if (strcmp(version, "2.18") >= 0) {
         err = 0;
      }
   }
#endif

   return err;
}


static INLINE MXUserThreadID
MXUserNativeThreadID(void)
{
   return pthread_self();
}


static INLINE Bool
MXRecLockIsOwner(const MXRecLock *lock)  // IN:
{
   return pthread_equal(lock->nativeThreadID, MXUserNativeThreadID());
}


static INLINE void
MXRecLockSetNoOwner(MXRecLock *lock)  // IN/OUT:
{
   /* a hack but it works portably */
   memset(&lock->nativeThreadID, 0xFF, sizeof lock->nativeThreadID);
}


static INLINE void
MXRecLockSetOwner(MXRecLock *lock)  // IN:
{
   lock->nativeThreadID = MXUserNativeThreadID();
}


static INLINE int
MXRecLockAcquireInternal(MXRecLock *lock)  // IN:
{
   return pthread_mutex_lock(&lock->nativeLock);
}


static INLINE int
MXRecLockTryAcquireInternal(MXRecLock *lock)  // IN:
{
   return pthread_mutex_trylock(&lock->nativeLock);
}


static INLINE int
MXRecLockReleaseInternal(MXRecLock *lock)  // IN:
{
   return pthread_mutex_unlock(&lock->nativeLock);
}
#endif


static INLINE Bool
MXRecLockInit(MXRecLock *lock)  // IN/OUT:
{
   Bool success = (MXRecLockCreateInternal(lock) == 0);

   if (success) {
      MXRecLockSetNoOwner(lock);

      lock->referenceCount = 0;
   }

   return success;
}


static INLINE void
MXRecLockDestroy(MXRecLock *lock)  // IN/OUT:
{
   int err = MXRecLockDestroyInternal(lock);

   if (vmx86_debug && (err != 0)) {
      Panic("%s: MXRecLockDestroyInternal returned %d\n", __FUNCTION__, err);
   }
}


static INLINE int
MXRecLockCount(const MXRecLock *lock)  // IN:
{
   ASSERT(lock->referenceCount >= 0);
   ASSERT(lock->referenceCount < MXUSER_MAX_REC_DEPTH);

   return lock->referenceCount;
}


static INLINE void
MXRecLockIncCount(MXRecLock *lock,  // IN/OUT:
                  int count)        // IN:
{
   int newCount = (lock->referenceCount += count);

   ASSERT(count >= 0);

   if (LIKELY(newCount == count)) {
      MXRecLockSetOwner(lock);
   }
}


static INLINE void
MXRecLockDecCount(MXRecLock *lock,  // IN/OUT:
                  int count)        // IN:
{
   int newCount = (lock->referenceCount -= count);

   ASSERT(count >= 0);

   if (LIKELY(newCount == 0)) {
      MXRecLockSetNoOwner(lock);
   }
}


static INLINE void
MXRecLockAcquire(MXRecLock *lock,       // IN/OUT:
                 VmTimeType *duration)  // OUT/OPT:
{
   int err;

   if (UNLIKELY(MXRecLockCount(lock) > 0) && MXRecLockIsOwner(lock)) {
      MXRecLockIncCount(lock, 1);

      if (duration != NULL) {
         *duration = 0ULL;
      }

      return;  // Uncontended
   }

   if (LIKELY(duration == NULL)) {
      err = MXRecLockAcquireInternal(lock);
   } else {
      VmTimeType start = Hostinfo_SystemTimerNS();

      err = MXRecLockAcquireInternal(lock);

      *duration = Hostinfo_SystemTimerNS() - start;
   }

   if (vmx86_debug && (err != 0)) {
      Panic("%s: MXRecLockAcquireInternal error %d\n", __FUNCTION__, err);
   }

   ASSERT(MXRecLockCount(lock) == 0);

   MXRecLockIncCount(lock, 1);

   return;
}


static INLINE Bool
MXRecLockTryAcquire(MXRecLock *lock)  // IN/OUT:
{
   int err;

   if (UNLIKELY(MXRecLockCount(lock) > 0) && MXRecLockIsOwner(lock)) {
      MXRecLockIncCount(lock, 1);

      return TRUE;  // Was acquired
   }

   err = MXRecLockTryAcquireInternal(lock);

   if (LIKELY(err == 0)) {
      MXRecLockIncCount(lock, 1);

      return TRUE;  // Was acquired
   }

   if (vmx86_debug && (err != EBUSY)) {
      Panic("%s: MXRecLockTryAcquireInternal error %d\n", __FUNCTION__, err);
   }

   return FALSE;  // Was not acquired
}


static INLINE void
MXRecLockRelease(MXRecLock *lock)  // IN/OUT:
{
   MXRecLockDecCount(lock, 1);

   if (LIKELY(MXRecLockCount(lock) == 0)) {
      int err = MXRecLockReleaseInternal(lock);

      if (vmx86_debug && (err != 0)) {
         Panic("%s: MXRecLockReleaseInternal returned %d\n", __FUNCTION__,
               err);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserCastedThreadID --
 *
 *      Obtains a unique thread identifier (ID) which can be stored in a
 *      pointer; typically these thread ID values are used for tracking
 *      purposes.
 *
 *      These values are not typically used with the MXUser MXRecLock
 *      implementation. Native constructs that are very low overhead are
 *      used.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void *
MXUserCastedThreadID(void)
{
   return (void *) (uintptr_t) VThread_CurID();  // unsigned
}

/*
 * MXUser object type ID values.
 */

typedef enum {
   MXUSER_TYPE_NEVER_USE  = 0,
   MXUSER_TYPE_RW         = 1,
   MXUSER_TYPE_REC        = 2,
   MXUSER_TYPE_RANK       = 3,
   MXUSER_TYPE_EXCL       = 4,
   MXUSER_TYPE_SEMA       = 5,
   MXUSER_TYPE_CONDVAR    = 6,
   MXUSER_TYPE_BARRIER    = 7,
   MXUSER_TYPE_EVENT      = 8
} MXUserObjectType;

/*
 * MXUser header - all MXUser objects start with this
 */

typedef struct MXUserHeader {
   char        *name;
   uint32       signature;
   MX_Rank      rank;

   void       (*dumpFunc)(struct MXUserHeader *);
   void       (*statsFunc)(struct MXUserHeader *);
   ListItem     item;

   uint64       serialNumber;
   Bool         badHeader;
} MXUserHeader;


/*
 * Internal functions
 */

void MXUserDumpAndPanic(MXUserHeader *header,
                        const char *fmt,
                        ...);

MXRecLock *MXUserInternalSingleton(Atomic_Ptr *storage);

uint32 MXUserGetSignature(MXUserObjectType objectType);

#if defined(MXUSER_DEBUG)
void MXUserAcquisitionTracking(MXUserHeader *header,
                           Bool checkRank);

void MXUserReleaseTracking(MXUserHeader *header);

void MXUserValidateHeader(MXUserHeader *header,
                          MXUserObjectType objectType);
#else
static INLINE void
MXUserAcquisitionTracking(MXUserHeader *header,  // IN:
                          Bool checkRank)        // IN:
{
   return;
}

static INLINE void
MXUserReleaseTracking(MXUserHeader *header)  // IN:
{
   return;
}

static INLINE void
MXUserValidateHeader(MXUserHeader *header,         // IN:
                     MXUserObjectType objectType)  // IN:
{
   return;
}
#endif

static INLINE Bool
MXUserTryAcquireFail(const char *name)  // IN:
{
   extern Bool (*MXUserTryAcquireForceFail)(const char *name);

   return vmx86_debug && MXUserTryAcquireForceFail &&
          (*MXUserTryAcquireForceFail)(name);
}

MXUserCondVar *MXUserCreateCondVar(MXUserHeader *header,
                                   MXRecLock *lock);

void MXUserWaitCondVar(MXUserHeader *header,
                       MXRecLock *lock,
                       MXUserCondVar *condVar,
                       uint32 waitTimeMsec);


typedef struct {
   char    *typeName;        // Name
   uint64   numSamples;      // Population sample size
   uint64   minTime;         // Minimum
   uint64   maxTime;         // Maximum
   uint64   timeSum;         // Sum of times (for mean)
   double   timeSquaredSum;  // Sum of times^2 (for S.D.)
} MXUserBasicStats;

typedef struct {
   double            contentionRatioFloor;
   uint64            contentionCountFloor;
   uint64            contentionDurationFloor;
   uint64            numAttempts;
   uint64            numSuccesses;
   uint64            numSuccessesContended;
   uint64            successContentionTime;
   uint64            totalContentionTime;

   MXUserBasicStats  basicStats;
} MXUserAcquisitionStats;

typedef struct {
   MXUserBasicStats  basicStats;       // total held statistics
} MXUserReleaseStats;

typedef struct
{
   MXUserAcquisitionStats  data;
   Atomic_Ptr              histo;
} MXUserAcquireStats;

typedef struct
{
   VmTimeType        holdStart;
   MXUserBasicStats  data;
   Atomic_Ptr        histo;
} MXUserHeldStats;

uint64 MXUserAllocSerialNumber(void);

void MXUserAddToList(MXUserHeader *header);
void MXUserRemoveFromList(MXUserHeader *header);

uint32 MXUserStatsMode(void);

typedef struct MXUserHisto MXUserHisto;

MXUserHisto *MXUserHistoSetUp(const char *typeName,
                              uint64 minValue,
                              uint32 decades);

void MXUserHistoTearDown(MXUserHisto *histo);

void MXUserHistoSample(MXUserHisto *histo,
                       uint64 value,
                       void *caller);

void MXUserHistoDump(MXUserHisto *histo,
                     MXUserHeader *header);

void MXUserAcquisitionStatsSetUp(MXUserAcquisitionStats *stats);

void MXUserAcquisitionSample(MXUserAcquisitionStats *stats,
                             Bool wasAcquired,
                             Bool wasContended,
                             uint64 elapsedTime);

void MXUserDumpAcquisitionStats(MXUserAcquisitionStats *stats,
                                MXUserHeader *header);

void MXUserAcquisitionStatsTearDown(MXUserAcquisitionStats *stats);

void
MXUserBasicStatsSetUp(MXUserBasicStats *stats,
                      const char *typeName);

void
MXUserBasicStatsSample(MXUserBasicStats *stats,
                       uint64 value);

void MXUserDumpBasicStats(MXUserBasicStats *stats,
                          MXUserHeader *header);

void MXUserBasicStatsTearDown(MXUserBasicStats *stats);

void MXUserKitchen(MXUserAcquisitionStats *stats,
                   double *contentionRatio,
                   Bool *isHot,
                   Bool *doLog);

#define MXUSER_DEFAULT_HISTO_MIN_VALUE_NS  1000  // 1 usec
#define MXUSER_DEFAULT_HISTO_DECADES       7     // 1 usec to 10 seconds

Bool MXUserForceAcquisitionHisto(Atomic_Ptr *mem,
                                 uint64 minValue,
                                 uint32 decades);

Bool MXUserForceHeldHisto(Atomic_Ptr *mem,
                          uint64 minValue,
                          uint32 decades);

Bool MXUserSetContentionRatioFloor(Atomic_Ptr *mem,
                                   double ratio);

Bool MXUserSetContentionCountFloor(Atomic_Ptr *mem,
                                   uint64 count);

Bool MXUserSetContentionDurationFloor(Atomic_Ptr *mem,
                                      uint64 count);

void MXUserEnableStats(Atomic_Ptr *acquisitionMem,
                       Atomic_Ptr *heldMem);

void MXUserDisableStats(Atomic_Ptr *acquisitionMem,
                        Atomic_Ptr *heldMem);

extern void  (*MXUserMX_LockRec)(struct MX_MutexRec *lock);
extern void  (*MXUserMX_UnlockRec)(struct MX_MutexRec *lock);
extern Bool  (*MXUserMX_TryLockRec)(struct MX_MutexRec *lock);
extern Bool  (*MXUserMX_IsLockedByCurThreadRec)(const struct MX_MutexRec *lock);
extern char *(*MXUserMX_NameRec)(const struct MX_MutexRec *lock);

#endif
07070100000179000081A40000000000000000000000016822550500000861000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulIntShared.h /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _ULINTSHARED_H_
#define _ULINTSHARED_H_


#if defined(MXUSER_DEBUG)
extern void MXUserListLocks(void);
#else
static INLINE void
MXUserListLocks(void)
{
   return;
}
#endif

MX_Rank MXUserCurrentRank(void);

extern void MXUserInstallMxHooks(void (*theLockListFunc)(void),
                                 MX_Rank (*theRankFunc)(void),
                                 void (*theLockFunc)(struct MX_MutexRec *lock),
                                 void (*theUnlockFunc)(struct MX_MutexRec *lock),
                                 Bool (*theTryLockFunc)(struct MX_MutexRec *lock),
                                 Bool (*theIsLockedFunc)(const struct MX_MutexRec *lock),
                                 char *(*theNameFunc)(const struct MX_MutexRec *lock),
                                 void (*theSetInPanicFunc)(void),
                                 Bool (*theInPanicFunc)(void));

typedef struct MXUserLockTreeNode MXUserLockTreeNode;

void MXUserLockTreeAcquire(void);
void MXUserLockTreeRelease(void);

MXUserLockTreeNode *MXUserLockTreeAdd(MXUserLockTreeNode *searchNode,
                                      const char *name,
                                      uint64 serialNumber,
                                      MX_Rank rank);

#endif
   0707010000017A000081A40000000000000000000000016822550500005430000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulRW.c    /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#if defined(_WIN32)
#include <windows.h>
#endif

#include "vmware.h"
#include "vm_basic_asm.h"
#include "str.h"
#include "util.h"
#include "hashTable.h"
#include "userlock.h"
#include "hostinfo.h"
#include "ulInt.h"

/*
 * Environment specific implementations of portable read-write locks.
 *
 * There are 5 environment specific primitives:
 *
 * MXUserNativeRWSupported   Are native read-write locks supported?
 * MXUserNativeRWInit        Allocate and initialize a native read-write lock
 * MXUserNativeRWDestroy     Destroy a native read-write lock
 * MXUserNativeRWAcquire     Acquire a native read-write lock
 * MXUserNativeRWRelease     Release a native read-write lock
 */

#if defined(_WIN32)
typedef SRWLOCK NativeRWLock;

static Bool
MXUserNativeRWSupported(void)
{
   return TRUE;
}

static Bool
MXUserNativeRWInit(NativeRWLock *lock)  // IN:
{
   InitializeSRWLock(lock);

   return TRUE;
}

static int
MXUserNativeRWDestroy(NativeRWLock *lock)  // IN:
{
   return 0; // nothing to do
}


static INLINE Bool
MXUserNativeRWAcquire(NativeRWLock *lock,  // IN:
                      Bool forRead,        // IN:
                      int *err)            // OUT:
{
   if (forRead) {
      AcquireSRWLockShared(lock);
   } else {
      AcquireSRWLockExclusive(lock);
   }

   *err = 0;

   return FALSE;
}

static INLINE int
MXUserNativeRWRelease(NativeRWLock *lock,  // IN:
                      Bool forRead)        // IN:
{
   if (forRead) {
      ReleaseSRWLockShared(lock);
   } else {
      ReleaseSRWLockExclusive(lock);
   }

   return 0;
}
#else  // _WIN32
#if defined(PTHREAD_RWLOCK_INITIALIZER)
typedef pthread_rwlock_t NativeRWLock;
#else
typedef int NativeRWLock;
#endif

static Bool
MXUserNativeRWSupported(void)
{
#if defined(PTHREAD_RWLOCK_INITIALIZER)
   return TRUE;
#else
   return FALSE;
#endif
}

static Bool
MXUserNativeRWInit(NativeRWLock *lock)  // IN:
{
#if defined(PTHREAD_RWLOCK_INITIALIZER)
   return (pthread_rwlock_init(lock, NULL) == 0);
#else
   return FALSE;
#endif
}

static int
MXUserNativeRWDestroy(NativeRWLock *lock)  // IN:
{
#if defined(PTHREAD_RWLOCK_INITIALIZER)
   return pthread_rwlock_destroy(lock);
#else
   return ENOSYS;
#endif
}

static INLINE Bool
MXUserNativeRWAcquire(NativeRWLock *lock,  // IN:
                      Bool forRead,        // IN:
                      int *err)            // OUT:
{
   Bool contended;

#if defined(PTHREAD_RWLOCK_INITIALIZER)
   *err = (forRead) ? pthread_rwlock_tryrdlock(lock) :
                      pthread_rwlock_trywrlock(lock);

   contended = (*err != 0);

   if (*err == EBUSY) {
      *err = (forRead) ? pthread_rwlock_rdlock(lock) :
                         pthread_rwlock_wrlock(lock);
   }
#else
   *err = ENOSYS;
   contended = FALSE;
#endif

   return contended;
}

static INLINE int
MXUserNativeRWRelease(NativeRWLock *lock,  // IN:
                      Bool forRead)        // IN:
{
#if defined(PTHREAD_RWLOCK_INITIALIZER)
   return pthread_rwlock_unlock(lock);
#else
   return ENOSYS;
#endif
}
#endif  // _WIN32

typedef enum {
   RW_UNLOCKED,
   RW_LOCKED_FOR_READ,
   RW_LOCKED_FOR_WRITE
} HolderState;

typedef struct {
   HolderState   state;
   VmTimeType    holdStart;
} HolderContext;

struct MXUserRWLock
{
   MXUserHeader    header;

   Bool            useNative;
   NativeRWLock    nativeLock;
   MXRecLock       recursiveLock;

   Atomic_uint32   holderCount;
   HashTable      *holderTable;

   Atomic_Ptr      heldStatsMem;
   Atomic_Ptr      acquireStatsMem;
};


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserStatsActionRW --
 *
 *      Perform the statistics action for the specified lock.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserStatsActionRW(MXUserHeader *header)  // IN:
{
   MXUserRWLock *lock = (MXUserRWLock *) header;
   MXUserHeldStats *heldStats = Atomic_ReadPtr(&lock->heldStatsMem);
   MXUserAcquireStats *acquireStats = Atomic_ReadPtr(&lock->acquireStatsMem);

   if (heldStats) {
      MXUserDumpBasicStats(&heldStats->data, header);

      if (Atomic_ReadPtr(&heldStats->histo) != NULL) {
         MXUserHistoDump(Atomic_ReadPtr(&heldStats->histo), header);
      }
   }

   if (acquireStats) {
      Bool isHot;
      Bool doLog;
      double contentionRatio;

      /*
       * Dump the statistics for the specified lock.
       */

      MXUserDumpAcquisitionStats(&acquireStats->data, header);

      if (Atomic_ReadPtr(&acquireStats->histo) != NULL) {
         MXUserHistoDump(Atomic_ReadPtr(&acquireStats->histo), header);
      }

      /*
       * Has the lock gone "hot"? If so, implement the hot actions.
       */

      MXUserKitchen(&acquireStats->data, &contentionRatio, &isHot, &doLog);

      if (isHot) {
         MXUserForceAcquisitionHisto(&lock->acquireStatsMem,
                                     MXUSER_DEFAULT_HISTO_MIN_VALUE_NS,
                                     MXUSER_DEFAULT_HISTO_DECADES);

         if (heldStats) {
            MXUserForceHeldHisto(&lock->heldStatsMem,
                                 MXUSER_DEFAULT_HISTO_MIN_VALUE_NS,
                                 MXUSER_DEFAULT_HISTO_DECADES);
         }

         if (doLog) {
            Log("HOT LOCK (%s); contention ratio %f\n",
                lock->header.name, contentionRatio);
         }
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDumpRWLock --
 *
 *      Dump an read-write lock.
 *
 * Results:
 *      A dump.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserDumpRWLock(MXUserHeader *header)  // IN:
{
   MXUserRWLock *lock = (MXUserRWLock *) header;

   Warning("%s: Read-write lock @ 0x%p\n", __FUNCTION__, lock);

   Warning("\tsignature 0x%X\n", lock->header.signature);
   Warning("\tname %s\n", lock->header.name);
   Warning("\trank 0x%X\n", lock->header.rank);
   Warning("\tserial number %"FMT64"u\n", lock->header.serialNumber);

   if (LIKELY(lock->useNative)) {
      Warning("\taddress of native lock 0x%p\n", &lock->nativeLock);
   } else {
      Warning("\tcount %d\n", MXRecLockCount(&lock->recursiveLock));
   }

   Warning("\tholderCount %d\n", Atomic_Read(&lock->holderCount));
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateRWLock --
 *
 *      Create a read/write lock.
 *
 *      If native read-write locks are not available, a recursive lock will
 *      be used to provide one reader or one writer access... which is
 *      better than nothing.
 *
 * Results:
 *      A pointer to a read/write lock.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserFreeHashEntry(void *data)  // IN:
{
   free(data);
}

MXUserRWLock *
MXUser_CreateRWLock(const char *userName,  // IN:
                    MX_Rank rank)          // IN:
{
   Bool lockInited;
   char *properName;
   Bool useNative = MXUserNativeRWSupported();
   MXUserRWLock *lock = Util_SafeCalloc(1, sizeof *lock);

   if (userName == NULL) {
      if (LIKELY(useNative)) {
         properName = Str_SafeAsprintf(NULL, "RW-%p", GetReturnAddress());
      } else {
         /* emulated */
         properName = Str_SafeAsprintf(NULL, "RWemul-%p", GetReturnAddress());
      }
   } else {
      properName = Util_SafeStrdup(userName);
   }

   lock->header.signature = MXUserGetSignature(MXUSER_TYPE_RW);
   lock->header.name = properName;
   lock->header.rank = rank;
   lock->header.serialNumber = MXUserAllocSerialNumber();
   lock->header.dumpFunc = MXUserDumpRWLock;

   /*
    * Always attempt to use native locks when they are available. If, for some
    * reason, a native lock should be available but isn't, fall back to using
    * an internal recursive lock - something is better than nothing.
    */

   lock->useNative = useNative && MXUserNativeRWInit(&lock->nativeLock);

   lockInited = MXRecLockInit(&lock->recursiveLock);

   if (LIKELY(lockInited)) {
      uint32 statsMode;

      lock->holderTable = HashTable_Alloc(256,
                                          HASH_INT_KEY | HASH_FLAG_ATOMIC,
                                          MXUserFreeHashEntry);

      statsMode = MXUserStatsMode();

      switch (MXUserStatsMode()) {
      case 0:
         MXUserDisableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
         lock->header.statsFunc = NULL;
         break;

      case 1:
         MXUserEnableStats(&lock->acquireStatsMem, NULL);
         lock->header.statsFunc = MXUserStatsActionRW;
         break;

      case 2:
         MXUserEnableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
         lock->header.statsFunc = MXUserStatsActionRW;
         break;

      default:
         Panic("%s: unknown stats mode: %d!\n", __FUNCTION__, statsMode);
      }

      MXUserAddToList(&lock->header);
   } else {
      Panic("%s: native lock initialization routine failed\n", __FUNCTION__);
   }

   return lock;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DestroyRWLock --
 *
 *      Destroy a read-write lock.
 *
 * Results:
 *      The lock is destroyed. Don't try to use the pointer again.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_DestroyRWLock(MXUserRWLock *lock)  // IN:
{
   if (LIKELY(lock != NULL)) {
      MXUserValidateHeader(&lock->header, MXUSER_TYPE_RW);

      if (Atomic_Read(&lock->holderCount) != 0) {
         MXUserDumpAndPanic(&lock->header,
                            "%s: Destroy on an acquired read-write lock\n",
                            __FUNCTION__);
      }

      if (LIKELY(lock->useNative)) {
         int err = MXUserNativeRWDestroy(&lock->nativeLock);

         if (UNLIKELY(err != 0)) {
            MXUserDumpAndPanic(&lock->header, "%s: Internal error (%d)\n",
                               __FUNCTION__, err);
         }
      }

      MXRecLockDestroy(&lock->recursiveLock);

      MXUserRemoveFromList(&lock->header);

      if (vmx86_stats) {
         MXUserDisableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
      }

      HashTable_FreeUnsafe(lock->holderTable);

      lock->header.signature = 0;  // just in case...
      free(lock->header.name);
      lock->header.name = NULL;
      free(lock);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserGetHolderContext --
 *
 *      Look up the context of the calling thread with respect to the
 *      specified lock and return it.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static HolderContext *
MXUserGetHolderContext(MXUserRWLock *lock)  // IN:
{
   HolderContext *result;
   void *threadID = MXUserCastedThreadID();

   ASSERT(lock->holderTable);

   if (!HashTable_Lookup(lock->holderTable, threadID, (void **) &result)) {
      HolderContext *newContext = Util_SafeMalloc(sizeof *newContext);

      newContext->holdStart = 0;
      newContext->state = RW_UNLOCKED;

      result = HashTable_LookupOrInsert(lock->holderTable, threadID,
                                        (void *) newContext);

      if (result != newContext) {
         free(newContext);
      }
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserAcquisition --
 *
 *      Acquire a read-write lock in the specified mode.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
MXUserAcquisition(MXUserRWLock *lock,  // IN/OUT:
                  Bool forRead)        // IN:
{
   HolderContext *myContext;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_RW);

   MXUserAcquisitionTracking(&lock->header, TRUE);

   myContext = MXUserGetHolderContext(lock);

   if (UNLIKELY(myContext->state != RW_UNLOCKED)) {
      MXUserDumpAndPanic(&lock->header,
                         "%s: AcquireFor%s after AcquireFor%s\n",
                         __FUNCTION__,
                        forRead ? "Read" : "Write",
                        (myContext->state == RW_LOCKED_FOR_READ) ? "Read" :
                                                                   "Write");
   }

   if (vmx86_stats) {
      VmTimeType value;
      MXUserAcquireStats *acquireStats;

      acquireStats = Atomic_ReadPtr(&lock->acquireStatsMem);

      if (lock->useNative) {
         int err = 0;
         Bool contended;
         VmTimeType begin = Hostinfo_SystemTimerNS();

         contended = MXUserNativeRWAcquire(&lock->nativeLock, forRead, &err);

         value = contended ? Hostinfo_SystemTimerNS() - begin : 0;

         if (UNLIKELY(err != 0)) {
            MXUserDumpAndPanic(&lock->header, "%s: Error %d: contended %d\n",
                               __FUNCTION__, err, contended);
         }
      } else {
         value = 0;

         MXRecLockAcquire(&lock->recursiveLock,
                          (acquireStats == NULL) ? NULL : &value);
      }

      if (LIKELY(acquireStats != NULL)) {
         MXUserHisto *histo;
         MXUserHeldStats *heldStats;

         /*
          * The statistics are not atomically safe so protect them when
          * necessary.
          */

         if (forRead && lock->useNative) {
            MXRecLockAcquire(&lock->recursiveLock,
                             NULL);  // non-stats
         }

         MXUserAcquisitionSample(&acquireStats->data, TRUE,
                            value > acquireStats->data.contentionDurationFloor,
                                 value);

         histo = Atomic_ReadPtr(&acquireStats->histo);

         if (UNLIKELY(histo != NULL)) {
            MXUserHistoSample(histo, value, GetReturnAddress());
         }

         if (forRead && lock->useNative) {
            MXRecLockRelease(&lock->recursiveLock);
         }

         heldStats = Atomic_ReadPtr(&lock->heldStatsMem);

         if (UNLIKELY(heldStats != NULL)) {
            myContext->holdStart = Hostinfo_SystemTimerNS();
         }
      }
   } else {
      if (LIKELY(lock->useNative)) {
         int err = 0;

         MXUserNativeRWAcquire(&lock->nativeLock, forRead, &err);

         if (UNLIKELY(err != 0)) {
            MXUserDumpAndPanic(&lock->header, "%s: Error %d\n",
                               __FUNCTION__, err);
         }
      } else {
         MXRecLockAcquire(&lock->recursiveLock,
                          NULL);  // non-stats
      }
   }

   if (!forRead || !lock->useNative) {
      ASSERT(Atomic_Read(&lock->holderCount) == 0);
   }

   Atomic_Inc(&lock->holderCount);
   myContext->state = forRead ? RW_LOCKED_FOR_READ : RW_LOCKED_FOR_WRITE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_AcquireForRead --
 *
 *      Acquire a read-write lock for read-shared access. A thread may have
 *      only one read lock outstanding on a read-write lock - no recursive
 *      access.
 *
 * Results:
 *      The lock is acquired.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_AcquireForRead(MXUserRWLock *lock)  // IN/OUT:
{
   MXUserAcquisition(lock, TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_AcquireForWrite --
 *
 *      Acquire a read-write lock for write-exclusive access. A thread may
 *      have only a single write lock outstanding on a read-write lock.
 *
 * Results:
 *      The lock is acquired.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_AcquireForWrite(MXUserRWLock *lock)  // IN/OUT:
{
   MXUserAcquisition(lock, FALSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_IsCurThreadHolding --
 *
 *      Is the read-write lock held in the mode queried?
 *
 * Results:
 *      TRUE   Yes
 *      FALSE  No
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_IsCurThreadHoldingRWLock(MXUserRWLock *lock,  // IN:
                                uint32 queryType)    // IN:
{
   HolderContext *myContext;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_RW);

   myContext = MXUserGetHolderContext(lock);

   switch (queryType) {
   case MXUSER_RW_FOR_READ:
      return myContext->state == RW_LOCKED_FOR_READ;

   case MXUSER_RW_FOR_WRITE:
      return myContext->state == RW_LOCKED_FOR_WRITE;

   case MXUSER_RW_LOCKED:
      return myContext->state != RW_UNLOCKED;

   default:
      Panic("%s: unknown query type %d\n", __FUNCTION__, queryType);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_ReleaseRWLock --
 *
 *      A read-write lock is released (unlocked).
 *
 * Results:
 *      The lock is released (unlocked).
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_ReleaseRWLock(MXUserRWLock *lock)  // IN/OUT:
{
   HolderContext *myContext;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_RW);

   myContext = MXUserGetHolderContext(lock);

   if (vmx86_stats) {
      MXUserHeldStats *heldStats = Atomic_ReadPtr(&lock->heldStatsMem);

      if (UNLIKELY(heldStats != NULL)) {
         MXUserHisto *histo;
         VmTimeType duration = Hostinfo_SystemTimerNS() - myContext->holdStart;

         /*
          * The statistics are not always atomically safe so protect them
          * when necessary
          */

         if ((myContext->state == RW_LOCKED_FOR_READ) && lock->useNative) {
            MXRecLockAcquire(&lock->recursiveLock,
                             NULL);  // non-stats
         }

         MXUserBasicStatsSample(&heldStats->data, duration);

         histo = Atomic_ReadPtr(&heldStats->histo);

         if (UNLIKELY(histo != NULL)) {
            MXUserHistoSample(histo, duration, GetReturnAddress());
         }

         if ((myContext->state == RW_LOCKED_FOR_READ) && lock->useNative) {
            MXRecLockRelease(&lock->recursiveLock);
         }
      }
   }

   if (UNLIKELY(myContext->state == RW_UNLOCKED)) {
      MXUserDumpAndPanic(&lock->header,
                         "%s: Release of an unacquired read-write lock\n",
                         __FUNCTION__);
   }

   MXUserReleaseTracking(&lock->header);

   Atomic_Dec(&lock->holderCount);

   if (LIKELY(lock->useNative)) {
      int err = MXUserNativeRWRelease(&lock->nativeLock,
                                      myContext->state == RW_LOCKED_FOR_READ);

      if (UNLIKELY(err != 0)) {
         MXUserDumpAndPanic(&lock->header, "%s: Internal error (%d)\n",
                            __FUNCTION__, err);
      }
   } else {
      ASSERT(Atomic_Read(&lock->holderCount) == 0);
      MXRecLockRelease(&lock->recursiveLock);
   }

   myContext->state = RW_UNLOCKED;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateSingletonRWLockInt --
 *
 *      Ensures that the specified backing object (Atomic_Ptr) contains a
 *      RW lock. This is useful for modules that need to protect something
 *      with a lock but don't have an existing Init() entry point where a
 *      lock can be created.
 *
 * Results:
 *      A pointer to the requested lock.
 *
 * Side effects:
 *      Generally the lock's resources are intentionally leaked (by design).
 *
 *-----------------------------------------------------------------------------
 */

MXUserRWLock *
MXUser_CreateSingletonRWLockInt(Atomic_Ptr *lockStorage,  // IN/OUT:
                                const char *name,         // IN:
                                MX_Rank rank)             // IN:
{
   MXUserRWLock *lock;

   ASSERT(lockStorage != NULL);

   lock = Atomic_ReadPtr(lockStorage);

   if (UNLIKELY(lock == NULL)) {
      MXUserRWLock *newLock = MXUser_CreateRWLock(name, rank);

      lock = Atomic_ReadIfEqualWritePtr(lockStorage, NULL, (void *) newLock);

      if (lock) {
         MXUser_DestroyRWLock(newLock);
      } else {
         lock = Atomic_ReadPtr(lockStorage);
      }
   }

   return lock;
}

0707010000017B000081A40000000000000000000000016822550500006AAE000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulRec.c   /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include "vmware.h"
#include "str.h"
#include "util.h"
#include "userlock.h"
#include "hostinfo.h"
#include "ulInt.h"

struct MXUserRecLock
{
   MXUserHeader         header;
   MXRecLock            recursiveLock;
   Atomic_Ptr           heldStatsMem;
   Atomic_Ptr           acquireStatsMem;
   Atomic_uint32        refCount;

   /*
    * This is the MX recursive lock override pointer. It is used within the
    * VMX only.
    *
    *  NULL   Use the MXRecLock within this structure
    *         MXUser_CreateRecLock was used to create the lock
    *
    * !NULL   Use the MX_MutexRec pointed to by vmmLock
    *         MXUser_BindMXMutexRec was used to create the lock
    */

   struct MX_MutexRec  *vmmLock;
};


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserStatsActionRec --
 *
 *      Perform the statistics action for the specified lock.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserStatsActionRec(MXUserHeader *header)  // IN:
{
   MXUserRecLock *lock = (MXUserRecLock *) header;
   MXUserHeldStats *heldStats = Atomic_ReadPtr(&lock->heldStatsMem);
   MXUserAcquireStats *acquireStats = Atomic_ReadPtr(&lock->acquireStatsMem);

   if (UNLIKELY(heldStats != NULL)) {
      MXUserDumpBasicStats(&heldStats->data, header);

      if (Atomic_ReadPtr(&heldStats->histo) != NULL) {
         MXUserHistoDump(Atomic_ReadPtr(&heldStats->histo), header);
      }
   }

   if (LIKELY(acquireStats != NULL)) {
      Bool isHot;
      Bool doLog;
      double contentionRatio;

      /*
       * Dump the statistics for the specified lock.
       */

      MXUserDumpAcquisitionStats(&acquireStats->data, header);

      if (Atomic_ReadPtr(&acquireStats->histo) != NULL) {
         MXUserHistoDump(Atomic_ReadPtr(&acquireStats->histo), header);
      }

      /*
       * Has the lock gone "hot"? If so, implement the hot actions.
       */

      MXUserKitchen(&acquireStats->data, &contentionRatio, &isHot, &doLog);

      if (UNLIKELY(isHot)) {
         MXUserForceAcquisitionHisto(&lock->acquireStatsMem,
                                     MXUSER_DEFAULT_HISTO_MIN_VALUE_NS,
                                     MXUSER_DEFAULT_HISTO_DECADES);

         if (UNLIKELY(heldStats != NULL)) {
            MXUserForceHeldHisto(&lock->heldStatsMem,
                                 MXUSER_DEFAULT_HISTO_MIN_VALUE_NS,
                                 MXUSER_DEFAULT_HISTO_DECADES);
         }

         if (doLog) {
            Log("HOT LOCK (%s); contention ratio %f\n",
                lock->header.name, contentionRatio);
         }
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_EnableStatsRecLock
 *
 *      Enable basic stats on the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_EnableStatsRecLock(MXUserRecLock *lock,        // IN/OUT:
                          Bool trackAcquisitionTime,  // IN:
                          Bool trackHeldTime)         // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   if (vmx86_stats) {
      MXUserEnableStats(trackAcquisitionTime ? &lock->acquireStatsMem : NULL,
                        trackHeldTime        ? &lock->heldStatsMem    : NULL);
   }

   return vmx86_stats;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DisableStatsRecLock --
 *
 *      Disable basic stats on the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_DisableStatsRecLock(MXUserRecLock *lock)  // IN/OUT:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   if (vmx86_stats) {
      MXUserDisableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
   }

   return vmx86_stats;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_SetContentionRatioFloorRecLock --
 *
 *      Set the contention ratio floor for the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_SetContentionRatioFloorRecLock(MXUserRecLock *lock,  // IN/OUT:
                                      double ratio)         // IN:
{
   Bool result;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   if (vmx86_stats) {
      result = MXUserSetContentionRatioFloor(&lock->acquireStatsMem, ratio);
   } else {
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_SetContentionCountFloorRecLock --
 *
 *      Set the contention count floor for the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_SetContentionCountFloorRecLock(MXUserRecLock *lock,  // IN/OUT:
                                      uint64 count)         // IN:
{
   Bool result;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   if (vmx86_stats) {
      result = MXUserSetContentionCountFloor(&lock->acquireStatsMem, count);
   } else {
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_SetContentionDurationFloorRecLock --
 *
 *      Set the contention count floor for the specified lock.
 *
 * Results:
 *      TRUE    succeeded
 *      FALSE   failed
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_SetContentionDurationFloorRecLock(MXUserRecLock *lock,  // IN/OUT:
                                         uint64 duration)      // IN:
{
   Bool result;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   if (vmx86_stats) {
      result = MXUserSetContentionDurationFloor(&lock->acquireStatsMem,
                                                duration);
   } else {
      result = FALSE;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDumpRecLock --
 *
 *      Dump a recursive lock.
 *
 * Results:
 *      A dump.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserDumpRecLock(MXUserHeader *header)  // IN:
{
   MXUserRecLock *lock = (MXUserRecLock *) header;

   Warning("%s: Recursive lock @ %p\n", __FUNCTION__, lock);

   Warning("\tsignature 0x%X\n", lock->header.signature);
   Warning("\tname %s\n", lock->header.name);
   Warning("\trank 0x%X\n", lock->header.rank);
   Warning("\tserial number %"FMT64"u\n", lock->header.serialNumber);
   Warning("\treference count %u\n", Atomic_Read(&lock->refCount));

   if (lock->vmmLock == NULL) {
      Warning("\tlock count %d\n", MXRecLockCount(&lock->recursiveLock));

      Warning("\taddress of owner data %p\n",
              &lock->recursiveLock.nativeThreadID);
   } else {
      Warning("\tvmmLock %p\n", lock->vmmLock);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateRecLock --
 *
 *      Create a recursive lock specifying if the lock must always be
 *      silent.
 *
 *      Only the owner (thread) of a recursive lock may recurse on it.
 *
 * Results:
 *      A pointer to an recursive lock.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

MXUserRecLock *
MXUser_CreateRecLock(const char *userName,  // IN:
                     MX_Rank rank)          // IN:
{
   uint32 statsMode;
   char *properName;
   MXUserRecLock *lock = Util_SafeCalloc(1, sizeof *lock);

   if (userName == NULL) {
      properName = Str_SafeAsprintf(NULL, "R-%p", GetReturnAddress());
   } else {
      properName = Util_SafeStrdup(userName);
   }

   if (UNLIKELY(!MXRecLockInit(&lock->recursiveLock))) {
      Panic("%s: native lock initialization routine failed\n", __FUNCTION__);
   }

   lock->vmmLock = NULL;
   Atomic_Write(&lock->refCount, 1);

   lock->header.signature = MXUserGetSignature(MXUSER_TYPE_REC);
   lock->header.name = properName;
   lock->header.rank = rank;
   lock->header.serialNumber = MXUserAllocSerialNumber();
   lock->header.dumpFunc = MXUserDumpRecLock;

   statsMode = MXUserStatsMode();

   switch (statsMode) {
   case 0:
      MXUserDisableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
      lock->header.statsFunc = NULL;
      break;

   case 1:
      MXUserEnableStats(&lock->acquireStatsMem, NULL);
      lock->header.statsFunc = MXUserStatsActionRec;
      break;

   case 2:
      MXUserEnableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
      lock->header.statsFunc = MXUserStatsActionRec;
      break;

   default:
      Panic("%s: unknown stats mode: %d!\n", __FUNCTION__, statsMode);
   }

   MXUserAddToList(&lock->header);

   return lock;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserCondDestroyRecLock --
 *
 *      Destroy a recursive lock -- but only if its reference count is zero.
 *
 *      When the lock is bound to a MX lock, only the MXUser "wrapper" is
 *      freed. The caller is responsible for calling MX_DestroyLockRec() on
 *      the MX lock before calling this routine.
 *
 * Results:
 *      Lock is destroyed upon correct reference count. Don't use the
 *      pointer again.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserCondDestroyRecLock(MXUserRecLock *lock)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(Atomic_Read(&lock->refCount) > 0);

   if (Atomic_ReadDec32(&lock->refCount) == 1) {
      if (lock->vmmLock == NULL) {
         if (MXRecLockCount(&lock->recursiveLock) > 0) {
            MXUserDumpAndPanic(&lock->header,
                               "%s: Destroy of an acquired recursive lock\n",
                               __FUNCTION__);
         }

         MXRecLockDestroy(&lock->recursiveLock);

         MXUserRemoveFromList(&lock->header);

         if (vmx86_stats) {
            MXUserDisableStats(&lock->acquireStatsMem, &lock->heldStatsMem);
         }
      }

      lock->header.signature = 0;  // just in case...
      free(lock->header.name);
      lock->header.name = NULL;
      free(lock);
   }
}

void
MXUser_DestroyRecLock(MXUserRecLock *lock)  // IN:
{
   if (lock != NULL) {
      MXUserCondDestroyRecLock(lock);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_AcquireRecLock --
 *
 *      An acquisition is made (lock is taken) on the specified recursive lock.
 *
 *      Only the owner (thread) of a recursive lock may recurse on it.
 *
 * Results:
 *      The lock is acquired (locked).
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_AcquireRecLock(MXUserRecLock *lock)  // IN/OUT:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(Atomic_Read(&lock->refCount) > 0);

   if (UNLIKELY(lock->vmmLock != NULL)) {
      ASSERT(MXUserMX_LockRec);
      (*MXUserMX_LockRec)(lock->vmmLock);
   } else {
      /* Rank checking is only done on the first acquisition */
      MXUserAcquisitionTracking(&lock->header, TRUE);

      if (vmx86_stats) {
         VmTimeType value = 0;
         MXUserAcquireStats *acquireStats;

         acquireStats = Atomic_ReadPtr(&lock->acquireStatsMem);

         MXRecLockAcquire(&lock->recursiveLock,
                          (acquireStats == NULL) ? NULL : &value);

         if (LIKELY(acquireStats != NULL)) {
            if (MXRecLockCount(&lock->recursiveLock) == 1) {
               MXUserHeldStats *heldStats;
               MXUserHisto *histo;

               MXUserAcquisitionSample(&acquireStats->data, TRUE,
                            value > acquireStats->data.contentionDurationFloor,
                                       value);

               histo = Atomic_ReadPtr(&acquireStats->histo);

               if (UNLIKELY(histo != NULL)) {
                  MXUserHistoSample(histo, value, GetReturnAddress());
               }

               heldStats = Atomic_ReadPtr(&lock->heldStatsMem);

               if (UNLIKELY(heldStats != NULL)) {
                  heldStats->holdStart = Hostinfo_SystemTimerNS();
               }
            }
         }
      } else {
         MXRecLockAcquire(&lock->recursiveLock,
                          NULL);  // non-stats
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_ReleaseRecLock --
 *
 *      Release (unlock) a recursive lock.
 *
 * Results:
 *      The lock is released.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_ReleaseRecLock(MXUserRecLock *lock)  // IN/OUT:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(Atomic_Read(&lock->refCount) > 0);

   if (UNLIKELY(lock->vmmLock != NULL)) {
      ASSERT(MXUserMX_UnlockRec);
      (*MXUserMX_UnlockRec)(lock->vmmLock);
   } else {
      if (vmx86_stats) {
         MXUserHeldStats *heldStats = Atomic_ReadPtr(&lock->heldStatsMem);

         if (LIKELY(heldStats != NULL)) {
            if (MXRecLockCount(&lock->recursiveLock) == 1) {
               MXUserHeldStats *heldStats;

               heldStats = Atomic_ReadPtr(&lock->heldStatsMem);

               if (UNLIKELY(heldStats != NULL)) {
                  VmTimeType value;
                  MXUserHisto *histo = Atomic_ReadPtr(&heldStats->histo);

                  value = Hostinfo_SystemTimerNS() - heldStats->holdStart;

                  MXUserBasicStatsSample(&heldStats->data, value);

                  if (UNLIKELY(histo != NULL)) {
                     MXUserHistoSample(histo, value, GetReturnAddress());
                  }
               }
            }
         }
      }

      if (vmx86_debug) {
         if (MXRecLockCount(&lock->recursiveLock) == 0) {
            MXUserDumpAndPanic(&lock->header,
                               "%s: Release of an unacquired recursive lock\n",
                                __FUNCTION__);
         }

         if (!MXRecLockIsOwner(&lock->recursiveLock)) {
            MXUserDumpAndPanic(&lock->header,
                               "%s: Non-owner release of an recursive lock\n",
                               __FUNCTION__);
         }
      }

      MXUserReleaseTracking(&lock->header);

      MXRecLockRelease(&lock->recursiveLock);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_TryAcquireRecLock --
 *
 *      An attempt is made to conditionally acquire (lock) a recursive lock.
 *
 *      Only the owner (thread) of a recursive lock may recurse on it.
 *
 * Results:
 *      TRUE    Acquired (locked)
 *      FALSE   Not acquired
 *
 * Side effects:
 *      None
 *
 * NOTE:
 *      A "TryAcquire" does not rank check should the acquisition succeed.
 *      This duplicates the behavor of MX locks.
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_TryAcquireRecLock(MXUserRecLock *lock)  // IN/OUT:
{
   Bool success;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(Atomic_Read(&lock->refCount) > 0);

   if (UNLIKELY(lock->vmmLock != NULL)) {
      ASSERT(MXUserMX_TryLockRec);
      success = (*MXUserMX_TryLockRec)(lock->vmmLock);
   } else {
      if (MXUserTryAcquireFail(lock->header.name)) {
         success = FALSE;
         goto bail;
      }

      success = MXRecLockTryAcquire(&lock->recursiveLock);

      if (success) {
         MXUserAcquisitionTracking(&lock->header, FALSE);
      }

      if (vmx86_stats) {
         MXUserAcquireStats *acquireStats;

         acquireStats = Atomic_ReadPtr(&lock->acquireStatsMem);

         if (LIKELY(acquireStats != NULL)) {
            MXUserAcquisitionSample(&acquireStats->data, success, !success,
                                    0ULL);
         }
      }
   }

bail:
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_IsCurThreadHoldingRecLock --
 *
 *      Is a recursive lock held by the calling thread?
 *
 * Results:
 *      TRUE    Yes
 *      FALSE   No
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_IsCurThreadHoldingRecLock(MXUserRecLock *lock)  // IN:
{
   Bool result;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(Atomic_Read(&lock->refCount) > 0);

   if (UNLIKELY(lock->vmmLock != NULL)) {
      ASSERT(MXUserMX_IsLockedByCurThreadRec);
      result = (*MXUserMX_IsLockedByCurThreadRec)(lock->vmmLock);
   } else {
      result = MXRecLockIsOwner(&lock->recursiveLock);
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateSingletonRecLockInt --
 *
 *      Ensures that the specified backing object (Atomic_Ptr) contains a
 *      recursive lock. This is useful for modules that need to protect
 *      something with a lock but don't have an existing Init() entry point
 *      where a lock can be created.
 *
 * Results:
 *      A pointer to the requested lock.
 *
 * Side effects:
 *      Generally the lock's resources are intentionally leaked (by design).
 *
 *-----------------------------------------------------------------------------
 */

MXUserRecLock *
MXUser_CreateSingletonRecLockInt(Atomic_Ptr *lockStorage,  // IN/OUT:
                                 const char *name,         // IN:
                                 MX_Rank rank)             // IN:
{
   MXUserRecLock *lock;

   ASSERT(lockStorage != NULL);

   lock = Atomic_ReadPtr(lockStorage);

   if (UNLIKELY(lock == NULL)) {
      MXUserRecLock *newLock = MXUser_CreateRecLock(name, rank);

      lock = Atomic_ReadIfEqualWritePtr(lockStorage, NULL, (void *) newLock);

      if (lock) {
         MXUser_DestroyRecLock(newLock);
      } else {
         lock = Atomic_ReadPtr(lockStorage);
      }
   }

   return lock;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateCondVarRecLock --
 *
 *      Create a condition variable for use with the specified recurisve lock.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      The created condition variable will cause a run-time error if it is
 *      used with a lock other than the one it was created for.
 *
 *-----------------------------------------------------------------------------
 */

MXUserCondVar *
MXUser_CreateCondVarRecLock(MXUserRecLock *lock)
{
   MXUserCondVar *condVar;

   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(lock->vmmLock == NULL);  // only unbound locks

   condVar =  MXUserCreateCondVar(&lock->header, &lock->recursiveLock);

   return condVar;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_WaitCondVarRecLock --
 *
 *      Block (sleep) on the specified condition variable. The specified lock
 *      is released upon blocking and is reacquired before returning from this
 *      function.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      It is possible to return from this routine without the condtion
 *      variable having been signalled (spurious wake up); code accordingly!
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_WaitCondVarRecLock(MXUserRecLock *lock,     // IN:
                          MXUserCondVar *condVar)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(lock->vmmLock == NULL);  // only unbound locks

   MXUserWaitCondVar(&lock->header, &lock->recursiveLock, condVar,
                     MXUSER_WAIT_INFINITE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_TimedWaitCondVarRecLock --
 *
 *      Block (sleep) on the specified condition variable for no longer than
 *      the specified amount of time. The specified lock is released upon
 *      blocking and is reacquired before returning from this function.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      It is possible to return from this routine without the condtion
 *      variable having been signalled (spurious wake up); code accordingly!
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_TimedWaitCondVarRecLock(MXUserRecLock *lock,     // IN:
                               MXUserCondVar *condVar,  // IN:
                               uint32 waitTimeMsec)     // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(lock->vmmLock == NULL);  // only unbound locks

   MXUserWaitCondVar(&lock->header, &lock->recursiveLock, condVar,
                     waitTimeMsec);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DumpRecLock --
 *
 *      Dump a recursive lock.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_DumpRecLock(MXUserRecLock *lock)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   MXUserDumpRecLock(&lock->header);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_GetRecLockVmm --
 *
 *      Return lock->vmmLock. Perhaps this lock is bound to an MX lock.
 *
 * Results:
 *      lock->vmmLock is returned.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

struct MX_MutexRec *
MXUser_GetRecLockVmm(MXUserRecLock *lock)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   return lock->vmmLock;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_GetRecLockRank --
 *
 *      Return the rank of the specified recursive lock.
 *
 * Results:
 *      The rank of the specified recursive lock is returned.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

MX_Rank
MXUser_GetRecLockRank(MXUserRecLock *lock)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   return lock->header.rank;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_BindMXMutexRec --
 *
 *      Create an MXUserRecLock that is bound to an (already) initialized
 *      MX_MutexRec.
 *
 * Results:
 *      NULL  Creation failed
 *      !NULL Creation succeeded
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

MXUserRecLock *
MXUser_BindMXMutexRec(struct MX_MutexRec *mutex,  // IN:
                      MX_Rank rank)               // IN:
{
   char *name;
   MXUserRecLock *lock;

   ASSERT(mutex != NULL);

   /*
    * Cannot perform a binding unless MX_Init has been called. As a side
    * effect it registers these hook functions.
    */

   if ((MXUserMX_LockRec == NULL) ||
       (MXUserMX_UnlockRec == NULL) ||
       (MXUserMX_TryLockRec == NULL) ||
       (MXUserMX_IsLockedByCurThreadRec == NULL) ||
       (MXUserMX_NameRec == NULL)) {
       return NULL;
    }

   /*
    * Initialize the header (so it looks correct in memory) but don't connect
    * this lock to the MXUser statistics or debugging tracking - the MX lock
    * system will take care of this.
    */

   lock = Util_SafeCalloc(1, sizeof *lock);

   lock->header.signature = MXUserGetSignature(MXUSER_TYPE_REC);

   name = (*MXUserMX_NameRec)(mutex);

   if (name == NULL) {
      lock->header.name = Str_SafeAsprintf(NULL, "MX_%p", mutex);
   } else {
      lock->header.name = Str_SafeAsprintf(NULL, "%s *", name);
   }

   lock->header.rank = rank;
   lock->header.serialNumber = MXUserAllocSerialNumber();
   lock->header.dumpFunc = NULL;
   lock->header.statsFunc = NULL;

   Atomic_WritePtr(&lock->acquireStatsMem, NULL);
   Atomic_WritePtr(&lock->heldStatsMem, NULL);
   Atomic_Write(&lock->refCount, 1);

   lock->vmmLock = mutex;

   return lock;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_IncRefRecLock --
 *
 *      Add a reference to the lock to prevent an immediate destory from
 *      succeeding.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_IncRefRecLock(MXUserRecLock *lock)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);
   ASSERT(Atomic_Read(&lock->refCount) > 0);

   Atomic_Inc(&lock->refCount);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DecRefRecLock --
 *
 *      Remove a reference to the lock. If the reference count is zero,
 *      the lock is destroyed.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_DecRefRecLock(MXUserRecLock *lock)  // IN:
{
   ASSERT(lock != NULL);
   MXUserValidateHeader(&lock->header, MXUSER_TYPE_REC);

   MXUserCondDestroyRecLock(lock);
}
  0707010000017C000081A40000000000000000000000016822550500005DC5000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulSema.c  /*********************************************************
 * Copyright (C) 2010-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#if defined(_WIN32)
#include <windows.h>
#else
#if defined(__APPLE__)
#include <dispatch/dispatch.h>
#else
#if (_XOPEN_SOURCE < 600) && !defined(__FreeBSD__) && !defined(sun)
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#include <semaphore.h>
#include <sys/time.h>
#endif
#endif

#include "vmware.h"
#include "str.h"
#include "util.h"
#include "userlock.h"
#include "ulInt.h"
#include "hostinfo.h"
#if defined(_WIN32)
#include "windowsu.h"
#endif

#define MXUSER_A_MILLION (1000 * 1000)
#define MXUSER_A_BILLION (1000 * MXUSER_A_MILLION)

#if defined(_WIN32)
typedef HANDLE NativeSemaphore;
#else
#if defined(__APPLE__)
/*
 * The Mac OS implementation uses dispatch_semaphore_t instead of
 * semaphore_t due to better performance in the uncontended case, by
 * avoiding a system call.  It, also, avoids weird error cases that
 * resulted in bug 916600.
 */
typedef dispatch_semaphore_t NativeSemaphore;
#else
typedef sem_t NativeSemaphore;
#endif
#endif

struct MXUserSemaphore
{
   MXUserHeader     header;
   Atomic_uint32    activeUserCount;
   NativeSemaphore  nativeSemaphore;
   Atomic_Ptr       acquireStatsMem;
};


/*
 *-----------------------------------------------------------------------------
 *
 * Environment specific implementations of portable semaphores.
 *
 * All of these functions return zero (0) for success and non-zero upon
 * failure. The non-zero value is a host specified error code.
 *
 * All down operations return a boolean which indicates if the down
 * operation actually occurred (the counting variable was decremented;
 * a sleep may have occurred). This boolean is valid regardless of the
 * value returned from the down functions.
 *
 * Timed operations always wait for the length of time specified. Should
 * the native system allow interruptions/signals, retries will be performed
 * until the specified amount of time has elapsed.
 *
 * There are 6 environment specific primitives:
 *
 * MXUserInit       Initialize a native semaphore
 * MXUserDestroy    Destroy a native semaphore
 * MXUserDown       Perform a down (P) operation
 * MXUserTimedDown  Perform a down (P) operation with a timeout
 * MXUserTryDown    Perform a try down (P) operation
 * MXUserUp         Perform an up (V) operation
 *
 *-----------------------------------------------------------------------------
 */

#if defined(_WIN32)
static int
MXUserInit(NativeSemaphore *sema)  // OUT:
{
   *sema = Win32U_CreateSemaphore(NULL, 0, INT_MAX, NULL);

   return (*sema == NULL) ? GetLastError() : 0;
}

static int
MXUserDestroy(NativeSemaphore *sema)  // IN:
{
   return CloseHandle(*sema) ? 0 : GetLastError();
}

static int
MXUserTimedDown(NativeSemaphore *sema,  // IN:
                uint64 waitTimeNS,      // IN:
                Bool *downOccurred)     // OUT:
{
   int err;
   DWORD status;
   const uint64 maxWaitTimeMS = (uint64) 0x7FFFFFFF;
   uint64 waitTimeMS = CEILING(waitTimeNS, (uint64) MXUSER_A_MILLION);

   if (waitTimeMS > maxWaitTimeMS) {
      waitTimeMS = maxWaitTimeMS;
   }

   status = WaitForSingleObject(*sema, (DWORD) waitTimeMS);

   switch (status) {
      case WAIT_OBJECT_0:  // The down (decrement) occurred
         *downOccurred = TRUE;
         err = 0;
         break;

      case WAIT_TIMEOUT:  // Timed out; the down (decrement) did not occur
         *downOccurred = FALSE;
         err = 0;
         break;

      default:  // Something really terrible has happened...
         if (vmx86_debug) {
            Panic("%s: WaitForSingleObject return value %x\n",
                  __FUNCTION__, status);
         }
   }

   return err;
}

static int
MXUserDown(NativeSemaphore *sema)  // IN:
{
   DWORD status = WaitForSingleObject(*sema, INFINITE);

   /* The down (decrement) *HAD BETTER HAVE* occurred! */
   if (vmx86_debug && (status != WAIT_OBJECT_0)) {
      Panic("%s: WaitForSingleObject return value %x\n",
            __FUNCTION__, status);
   }

   return 0;
}

static int
MXUserTryDown(NativeSemaphore *sema,  // IN:
              Bool *downOccurred)     // OUT:
{
   /*
    * Use a wait for zero time to implement the try operation. This timed
    * down will either succeed immediately (down occurred), fail (something
    * terrible happened) or time out immediately (the down could not be
    * performed and that is OK).
    */

   return MXUserTimedDown(sema, 0, downOccurred);
}

static int
MXUserUp(NativeSemaphore *sema)  // IN:
{
   return ReleaseSemaphore(*sema, 1, NULL) ? 0 : GetLastError();
}

#elif defined(__APPLE__)
static int
MXUserInit(NativeSemaphore *sema)  // OUT:
{
   *sema = dispatch_semaphore_create(0);
   return *sema == NULL;
}

static int
MXUserDestroy(NativeSemaphore *sema)  // IN:
{
   dispatch_release(*sema);
   return 0;
}

static int
MXUserTimedDown(NativeSemaphore *sema,  // IN:
                uint64 waitTimeNS,      // IN:
                Bool *downOccurred)     // OUT:
{
   dispatch_time_t deadline = dispatch_time(DISPATCH_TIME_NOW, waitTimeNS);
   *downOccurred = dispatch_semaphore_wait(*sema, deadline) == 0;

   return 0;
}

static int
MXUserDown(NativeSemaphore *sema)  // IN:
{
   dispatch_semaphore_wait(*sema, DISPATCH_TIME_FOREVER);
   return 0;
}

static int
MXUserTryDown(NativeSemaphore *sema,  // IN:
              Bool *downOccurred)     // OUT:
{
   /*
    * Provide 'try' semantics by requesting an immediate timeout.
    */
   *downOccurred = dispatch_semaphore_wait(*sema, DISPATCH_TIME_NOW) == 0;
   return 0;
}

static int
MXUserUp(NativeSemaphore *sema)  // IN:
{
    dispatch_semaphore_signal(*sema);
    return 0;
}

#else
static int
MXUserInit(NativeSemaphore *sema)  // OUT:
{
   return (sem_init(sema, 0, 0) == -1) ? errno : 0;
}

static int
MXUserDestroy(NativeSemaphore *sema)  // IN:
{
   return (sem_destroy(sema) == -1) ? errno : 0;
}

static int
MXUserDown(NativeSemaphore *sema)  // IN:
{
   int err;

   /* Retry any interruptions (EINTR) */
   do {
      err = (sem_wait(sema) == -1) ? errno : 0;
   } while (err == EINTR);

   return err;
}

static int
MXUserTimedDown(NativeSemaphore *sema,  // IN:
                uint64 waitTimeNS,      // IN:
                Bool *downOccurred)     // OUT:
{
   int err;
   uint64 endNS;
   struct timeval curTime;
   struct timespec endTime;

   /*
    * sem_timedwait takes an absolute time. Yes, this is beyond ridiculous,
    * and the justifications for this vs. relative time makes no sense, but
    * it is what it is...
    */

   gettimeofday(&curTime, NULL);
   endNS = ((uint64) curTime.tv_sec * MXUSER_A_BILLION) +
           ((uint64) curTime.tv_usec * 1000) +
           waitTimeNS;

   endTime.tv_sec = (time_t) (endNS / MXUSER_A_BILLION);
   endTime.tv_nsec = (long int) (endNS % MXUSER_A_BILLION);

   do {
      err = (sem_timedwait(sema, &endTime) == -1) ? errno : 0;

      if (err == 0) {
         *downOccurred = TRUE;
      } else {
         *downOccurred = FALSE;

         /* Really timed out; no down occurred, no error */
         if (err == ETIMEDOUT) {
            err = 0;
         }
      }
   } while (err == EINTR);

   return err;
}

static int
MXUserTryDown(NativeSemaphore *sema,  // IN:
              Bool *downOccurred)     // OUT:
{
   int err = (sem_trywait(sema) == -1) ? errno : 0;

   if (err == 0) {
      *downOccurred = TRUE;
   } else {
      *downOccurred = FALSE;

      /*
       * If the error that occurred indicates that the try operation cannot
       * succeed (EAGAIN) or was interrupted (EINTR) suppress the error
       * indicator as these are considered "normal", non-error cases.
       *
       * It's OK to not loop on EINTR here since this is a try operation.
       */

      if ((err == EAGAIN) || (err == EINTR)) {
         err = 0;  // no error
      }
   }

   return err;
}

static int
MXUserUp(NativeSemaphore *sema)  // IN:
{
   return (sem_post(sema) == -1) ? errno : 0;
}
#endif  // _WIN32


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserStatsActionSema --
 *
 *      Perform the statistics action for the specified semaphore.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
MXUserStatsActionSema(MXUserHeader *header)  // IN:
{
   MXUserSemaphore *sema = (MXUserSemaphore *) header;
   MXUserAcquireStats *acquireStats = Atomic_ReadPtr(&sema->acquireStatsMem);

   if (LIKELY(acquireStats != NULL)) {
      Bool isHot;
      Bool doLog;
      double contentionRatio;

      /*
       * Dump the statistics for the specified semaphore.
       */

      MXUserDumpAcquisitionStats(&acquireStats->data, header);

      if (Atomic_ReadPtr(&acquireStats->histo) != NULL) {
         MXUserHistoDump(Atomic_ReadPtr(&acquireStats->histo), header);
      }

      /*
       * Has the semaphore gone "hot"? If so, implement the hot actions.
       */

      MXUserKitchen(&acquireStats->data, &contentionRatio, &isHot, &doLog);

      if (isHot) {
         MXUserForceAcquisitionHisto(&sema->acquireStatsMem,
                                     MXUSER_DEFAULT_HISTO_MIN_VALUE_NS,
                                     MXUSER_DEFAULT_HISTO_DECADES);

         if (doLog) {
            Log("HOT SEMAPHORE (%s); contention ratio %f\n",
                sema->header.name, contentionRatio);
         }
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDumpSemaphore --
 *
 *      Dump a semaphore.
 *
 * Results:
 *      A dump.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserDumpSemaphore(MXUserHeader *header)  // IN:
{
   MXUserSemaphore *sema = (MXUserSemaphore *) header;

   Warning("%s: semaphore @ %p\n", __FUNCTION__, sema);

   Warning("\tsignature 0x%X\n", sema->header.signature);
   Warning("\tname %s\n", sema->header.name);
   Warning("\trank 0x%X\n", sema->header.rank);
   Warning("\tserial number %"FMT64"u\n", sema->header.serialNumber);

   Warning("\treference count %u\n", Atomic_Read(&sema->activeUserCount));
   Warning("\taddress of native semaphore %p\n", &sema->nativeSemaphore);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateSemaphore --
 *
 *      Create a (counting) semaphore.
 *
 *      The initial count of the semaphore is zero (0). The maximum count that
 *      a semaphore handles is unknown but may be assumed to be large; the
 *      largest signed 32-bit number is an acceptable choice.
 *
 * Results:
 *      A pointer to a semaphore.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

MXUserSemaphore *
MXUser_CreateSemaphore(const char *userName,  // IN:
                       MX_Rank rank)          // IN:
{
   char *properName;
   MXUserSemaphore *sema = Util_SafeCalloc(1, sizeof *sema);

   if (userName == NULL) {
      properName = Str_SafeAsprintf(NULL, "Sema-%p", GetReturnAddress());
   } else {
      properName = Util_SafeStrdup(userName);
   }

   if (LIKELY(MXUserInit(&sema->nativeSemaphore) == 0)) {
      uint32 statsMode;

      sema->header.signature = MXUserGetSignature(MXUSER_TYPE_SEMA);
      sema->header.name = properName;
      sema->header.rank = rank;
      sema->header.serialNumber = MXUserAllocSerialNumber();
      sema->header.dumpFunc = MXUserDumpSemaphore;

      statsMode = MXUserStatsMode();

      switch (MXUserStatsMode()) {
      case 0:
         MXUserDisableStats(&sema->acquireStatsMem, NULL);
         sema->header.statsFunc = NULL;
         break;

      case 1:
      case 2:
         MXUserEnableStats(&sema->acquireStatsMem, NULL);
         sema->header.statsFunc = MXUserStatsActionSema;
         break;

      default:
         Panic("%s: unknown stats mode: %d!\n", __FUNCTION__, statsMode);
      }

      MXUserAddToList(&sema->header);
   } else {
      Panic("%s: native lock initialization routine failed\n", __FUNCTION__);
   }

   return sema;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DestroySemaphore --
 *
 *      Destroy a semaphore
 *
 * Results:
 *      The semaphore is destroyed. Don't try to use the pointer again.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_DestroySemaphore(MXUserSemaphore *sema)  // IN/OUT:
{
   if (LIKELY(sema != NULL)) {
      int err;

      MXUserValidateHeader(&sema->header, MXUSER_TYPE_SEMA);

      if (Atomic_Read(&sema->activeUserCount) != 0) {
         MXUserDumpAndPanic(&sema->header,
                            "%s: Attempted destroy on semaphore while in use\n",
                            __FUNCTION__);
      }

      sema->header.signature = 0;  // just in case...

      err = MXUserDestroy(&sema->nativeSemaphore);

      if (UNLIKELY(err != 0)) {
         MXUserDumpAndPanic(&sema->header, "%s: Internal error (%d)\n",
                            __FUNCTION__, err);
      }

      MXUserRemoveFromList(&sema->header);

      if (vmx86_stats) {
         MXUserAcquireStats *acquireStats;

         acquireStats = Atomic_ReadPtr(&sema->acquireStatsMem);

         if (LIKELY(acquireStats != NULL)) {
            MXUserAcquisitionStatsTearDown(&acquireStats->data);
            MXUserHistoTearDown(Atomic_ReadPtr(&acquireStats->histo));

            free(acquireStats);
         }
      }

      free(sema->header.name);
      sema->header.name = NULL;
      free(sema);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_DownSemaphore --
 *
 *      Perform a down (P; probeer te verlagen; "try to reduce") operation
 *      on a semaphore.
 *
 * Results:
 *      The count will be decremented; a sleep may occur until the decrement
 *      is possible.
 *
 * Side effects:
 *      The caller may sleep.
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_DownSemaphore(MXUserSemaphore *sema)  // IN/OUT:
{
   int err;

   ASSERT(sema != NULL);

   MXUserValidateHeader(&sema->header, MXUSER_TYPE_SEMA);

   Atomic_Inc(&sema->activeUserCount);

   MXUserAcquisitionTracking(&sema->header, TRUE);  // rank checking

   if (vmx86_stats) {
      VmTimeType start = 0;
      Bool tryDownSuccess = FALSE;
      MXUserAcquireStats *acquireStats;

      acquireStats = Atomic_ReadPtr(&sema->acquireStatsMem);

      if (LIKELY(acquireStats != NULL)) {
         start = Hostinfo_SystemTimerNS();
      }

      err = MXUserTryDown(&sema->nativeSemaphore, &tryDownSuccess);

      if (LIKELY(err == 0)) {
         if (!tryDownSuccess) {
            err = MXUserDown(&sema->nativeSemaphore);
         }
      }

      if (LIKELY((err == 0) && (acquireStats != NULL))) {
         MXUserHisto *histo;
         VmTimeType value = Hostinfo_SystemTimerNS() - start;

         MXUserAcquisitionSample(&acquireStats->data, TRUE,
                                 !tryDownSuccess, value);

         histo = Atomic_ReadPtr(&acquireStats->histo);

         if (UNLIKELY(histo != NULL)) {
            MXUserHistoSample(histo, value, GetReturnAddress());
         }
      }
   } else {
      err = MXUserDown(&sema->nativeSemaphore);
   }

   if (UNLIKELY(err != 0)) {
      MXUserDumpAndPanic(&sema->header, "%s: Internal error (%d)\n",
                         __FUNCTION__, err);
   }

   MXUserReleaseTracking(&sema->header);

   Atomic_Dec(&sema->activeUserCount);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_TimedDownSemaphoreNS --
 *
 *      Perform a down (P; probeer te verlagen; "try to reduce") operation
 *      on a semaphore with a timeout. The wait time will always have elapsed
 *      before the routine returns.
 *
 *      NOT ALL SUPPORTED PLATFORMS CAN NATIVELY HANDLE WAIT TIMES OF LESS
 *      THAN A MILLISECOND. WAIT TIMES WILL BE ROUNDED UP WHEN NECESSARY.
 *
 *      WAIT TIMES LONGER THAN A SUPPORTED PLATFORM CAN SUPPORT WILL BE
 *      SET TO THE MAXIMUM POSSIBLE.
 *
 * Results:
 *      TRUE   Down operation occurred (count has been decremented)
 *      FALSE  Down operation did not occur (time out occurred)
 *
 * Side effects:
 *      The caller may sleep.
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_TimedDownSemaphoreNS(MXUserSemaphore *sema,  // IN/OUT:
                            uint64 waitTimeNS)      // IN:
{
   int err;
   Bool downOccurred = FALSE;

   ASSERT(sema != NULL);

   MXUserValidateHeader(&sema->header, MXUSER_TYPE_SEMA);

   Atomic_Inc(&sema->activeUserCount);

   MXUserAcquisitionTracking(&sema->header, TRUE);  // rank checking

   if (vmx86_stats) {
      VmTimeType start = 0;
      Bool tryDownSuccess = FALSE;
      MXUserAcquireStats *acquireStats;

      acquireStats = Atomic_ReadPtr(&sema->acquireStatsMem);

      if (LIKELY(acquireStats != NULL)) {
         start = Hostinfo_SystemTimerNS();
      }

      err = MXUserTryDown(&sema->nativeSemaphore, &tryDownSuccess);

      if (LIKELY(err == 0)) {
         if (tryDownSuccess) {
            downOccurred = TRUE;
         } else {
            err = MXUserTimedDown(&sema->nativeSemaphore, waitTimeNS,
                                  &downOccurred);
         }
      }

      if (LIKELY((err == 0) && (acquireStats != NULL))) {
         VmTimeType value = Hostinfo_SystemTimerNS() - start;

         MXUserAcquisitionSample(&acquireStats->data, downOccurred,
                                 !tryDownSuccess, value);

         if (downOccurred) {
            MXUserHisto *histo = Atomic_ReadPtr(&acquireStats->histo);

            if (UNLIKELY(histo != NULL)) {
               MXUserHistoSample(histo, value, GetReturnAddress());
            }
         }
      }
   } else {
      err = MXUserTimedDown(&sema->nativeSemaphore, waitTimeNS,
                            &downOccurred);
   }

   if (UNLIKELY(err != 0)) {
      MXUserDumpAndPanic(&sema->header, "%s: Internal error (%d)\n",
                         __FUNCTION__, err);
   }

   MXUserReleaseTracking(&sema->header);

   Atomic_Dec(&sema->activeUserCount);

   return downOccurred;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_TimedDownSemaphore --
 *
 *      Perform a down (P; probeer te verlagen; "try to reduce") operation
 *      on a semaphore with a timeout. The wait time will always have elapsed
 *      before the routine returns.
 *
 *      ALL SUPPORTED PLATFORMS CAN NATIVELY HANDLE MILLISECOND WAITS.
 *
 * Results:
 *      TRUE   Down operation occurred (count has been decremented)
 *      FALSE  Down operation did not occur (time out occurred)
 *
 * Side effects:
 *      The caller may sleep.
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_TimedDownSemaphore(MXUserSemaphore *sema,  // IN/OUT:
                          uint32 waitTimeMS)      // IN:
{
   return MXUser_TimedDownSemaphoreNS(sema,
                                      (uint64) MXUSER_A_MILLION * waitTimeMS);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_TryDownSemaphore --
 *
 *      Perform a try down (P; probeer te verlagen; "try to reduce") operation
 *      on a semaphore.
 *
 * Results:
 *      TRUE   Down operation occurred (count has been decremented)
 *      FALSE  Down operation did not occur
 *
 * Side effects:
 *      None
 *
 * NOTE:
 *      A "TryAcquire" does not rank check should the down operation succeed.
 *      This duplicates the behavior of MX semaphores.
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUser_TryDownSemaphore(MXUserSemaphore *sema)  // IN/OUT:
{
   int err;
   Bool downOccurred = FALSE;

   ASSERT(sema != NULL);

   MXUserValidateHeader(&sema->header, MXUSER_TYPE_SEMA);

   Atomic_Inc(&sema->activeUserCount);

   err = MXUserTryDown(&sema->nativeSemaphore, &downOccurred);

   if (UNLIKELY(err != 0)) {
      MXUserDumpAndPanic(&sema->header, "%s: Internal error (%d)\n",
                         __FUNCTION__, err);
   }

   if (vmx86_stats) {
      MXUserAcquireStats *acquireStats;

      acquireStats = Atomic_ReadPtr(&sema->acquireStatsMem);

      if (LIKELY(acquireStats != NULL)) {
         MXUserAcquisitionSample(&acquireStats->data, downOccurred,
                                 !downOccurred, 0ULL);
      }
   }

   Atomic_Dec(&sema->activeUserCount);

   return downOccurred;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_UpSemaphore --
 *
 *      Perform an up (V; verhogen; "increase") operation on a semaphore.
 *
 * Results:
 *      The semaphore count is incremented. Any thread waiting on the
 *      semaphore is awoken.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_UpSemaphore(MXUserSemaphore *sema)  // IN/OUT:
{
   int err;

   ASSERT(sema != NULL);

   MXUserValidateHeader(&sema->header, MXUSER_TYPE_SEMA);

   /*
    * The purpose of the activeUserCount tracking is to help catch potentially
    * fatal cases of destroying an active semaphore; it is not expected to be
    * (nor it can be) perfect (with low overhead). In this case the time spent
    * in up is tiny and a decrement at the bottom might not be reached before
    * another thread comes out of down and does a destroy - so no
    * activeUserCount tracking here.
    */

   err = MXUserUp(&sema->nativeSemaphore);

   if (UNLIKELY(err != 0)) {
      MXUserDumpAndPanic(&sema->header, "%s: Internal error (%d)\n",
                         __FUNCTION__, err);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_CreateSingletonSemaphore --
 *
 *      Ensures that the specified backing object (Atomic_Ptr) contains a
 *      semaphore. This is useful for modules that need to protect something
 *      with a semaphore but don't have an existing Init() entry point where a
 *      semaphore can be created.
 *
 * Results:
 *      A pointer to the requested semaphore.
 *
 * Side effects:
 *      Generally the semaphore's resources are intentionally leaked
 *      (by design).
 *
 *-----------------------------------------------------------------------------
 */

MXUserSemaphore *
MXUser_CreateSingletonSemaphore(Atomic_Ptr *semaStorage,  // IN/OUT:
                                const char *name,         // IN:
                                MX_Rank rank)             // IN:
{
   MXUserSemaphore *sema;

   ASSERT(semaStorage != NULL);

   sema = Atomic_ReadPtr(semaStorage);

   if (UNLIKELY(sema == NULL)) {
      MXUserSemaphore *newSema = MXUser_CreateSemaphore(name, rank);

      sema = Atomic_ReadIfEqualWritePtr(semaStorage, NULL, (void *) newSema);

      if (sema) {
         MXUser_DestroySemaphore(newSema);
      } else {
         sema = Atomic_ReadPtr(semaStorage);
      }
   }

   return sema;
}

   0707010000017D000081A40000000000000000000000016822550500007E5F000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/lock/ulStats.c /*********************************************************
 * Copyright (C) 2010-2019, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include <math.h>  // for sqrt; should be removed soon

#if defined(_WIN32)
#include <intrin.h>
#endif

#include "vmware.h"
#include "str.h"
#include "util.h"
#include "userlock.h"
#include "ulInt.h"
#include "hostinfo.h"
#include "log.h"
#include "logFixed.h"

#define BINS_PER_DECADE 100

static double mxUserContentionRatioFloor = 0.0;   // always "off"
static uint64 mxUserContentionCountFloor = 0;     // always "off"
static uint64 mxUserContentionDurationFloor = 0;  // always "off"

static Atomic_Ptr mxLockMemPtr;   // internal singleton lock
static ListItem *mxUserLockList;  // list of all MXUser locks

typedef struct {
   void   *address;
   uint64  timeValue;
} TopOwner;

struct MXUserHisto {
   char     *typeName;               // Type (name) of histogram
   uint64   *binData;                // Hash table bins
   uint64    totalSamples;           // Population sample size
   uint64    minValue;               // Min value allowed
   uint64    maxValue;               // Max value allowed
   uint32    numBins;                // Number of histogram bins
};

static Bool    mxUserTrackHeldTimes = FALSE;
static char   *mxUserHistoLine = NULL;
static uint32  mxUserMaxLineLength = 0;
static void   *mxUserStatsContext = NULL;
static void  (*mxUserStatsFunc)(void *context,
                                const char *fmt,
                                va_list ap) = NULL;


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserAddToList --
 *
 *      Add a newly created lock to the list of all userland locks.
 *
 * Results:
 *      The lock is added to the list.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserAddToList(MXUserHeader *header)  // IN/OUT:
{
   MXRecLock *listLock = MXUserInternalSingleton(&mxLockMemPtr);

   /* Tolerate a failure. This is too low down to log */
   if (listLock) {
      MXRecLockAcquire(listLock,
                       NULL);  // non-stats
      CircList_Queue(&header->item, &mxUserLockList);
      MXRecLockRelease(listLock);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserRemoveFromList --
 *
 *      Remove a lock from the list of all userland locks.
 *
 * Results:
 *      The lock is removed from the list.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserRemoveFromList(MXUserHeader *header)  // IN/OUT:
{
   MXRecLock *listLock = MXUserInternalSingleton(&mxLockMemPtr);

   /* Tolerate a failure. This is too low down to log */
   if (listLock) {
      MXRecLockAcquire(listLock,
                       NULL);  // non-stats
      CircList_DeleteItem(&header->item, &mxUserLockList);
      MXRecLockRelease(listLock);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserHistoIndex --
 *
 *      Return the index into the histogram bins. This makes use of a
 *      fixed point approximation method.
 *
 * Results:
 *      (uint32) (BINS_PER_DECADE * log10(value))
 *
 * Side effects:
 *      The computed value may actually be larger than expected by a tiny
 *      amount - the log10 method is a ratio of two integers.
 *
 *-----------------------------------------------------------------------------
 */

static uint32
MXUserHistoIndex(uint64 value)  // IN:
{
   uint32 index;

   if (value == 0) {
      index = 0;
   } else {
      uint32 numerator = 0;
      uint32 denominator = 0;

      LogFixed_Base10(value, &numerator, &denominator);

      index = (BINS_PER_DECADE * numerator) / denominator;
   }

   return index;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserHistoSetup --
 *
 *      Set up a histogram object using the specified minimum value and
 *      decade coverage. The minimum value must be 1 or a power of 10.
 *
 *      These histograms coverage values from the minimum to
 *      minimum * 10^decades with BINS_PER_DECADE bins for each decade
 *      covered.
 *
 * Results:
 *      NULL  Failure
 *     !NULL  Success (a histogram object pointer is returned)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

MXUserHisto *
MXUserHistoSetUp(const char *typeName, // type (name) of histogram
                 uint64 minValue,      // IN: ns; 1, 10, 100, 1000...
                 uint32 decades)       // IN: decimal decades to cover from min
{
   MXUserHisto *histo;

   ASSERT(decades > 0);
   ASSERT((minValue != 0) && ((minValue == 1) || ((minValue % 10) == 0)));

   histo = Util_SafeCalloc(1, sizeof *histo);

   histo->typeName = Util_SafeStrdup(typeName);
   histo->numBins = BINS_PER_DECADE * decades;
   histo->binData = Util_SafeCalloc(histo->numBins, sizeof(uint64));
   histo->totalSamples = 0;
   histo->minValue = minValue;

   histo->maxValue = histo->minValue;

   while (decades--) {
      histo->maxValue *= 10;
   }

   return histo;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserHistoTearDown --
 *
 *      Tear down a histogram object;
 *
 * Results:
 *      The histogram object is torn down. Don't use it after this.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserHistoTearDown(MXUserHisto *histo)  // IN/OUT:
{
   if (histo != NULL) {
      free(histo->typeName);
      free(histo->binData);
      free(histo);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserHistoSample --
 *
 *      Add a sample to the specified histogram.
 *
 *      Out-of-bounds on the low end are summed in bin[0].
 *      Out-of-bounds on the high end are summed in bin[numBins - 1].
 *
 * Results:
 *      As expected.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserHistoSample(MXUserHisto *histo,  // IN/OUT:
                  uint64 durationNS,   // IN:
                  void *ownerRetAddr)  // IN:
{
   uint32 index;

   ASSERT(histo != NULL);

   histo->totalSamples++;

   if (durationNS < histo->minValue) {
      index = 0;
   } else {
      index = MXUserHistoIndex(durationNS / histo->minValue);

      if (index > histo->numBins - 1) {
         index = histo->numBins - 1;
      }
   }

   ASSERT(index < histo->numBins);

   histo->binData[index]++;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserStatsLog --
 *
 *      Output the statistics data
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
MXUserStatsLog(const char *fmt,  // IN:
               ...)              // IN:
{
   va_list ap;

   ASSERT(mxUserStatsFunc != NULL);

   va_start(ap, fmt);
   (*mxUserStatsFunc)(mxUserStatsContext, fmt, ap);
   va_end(ap);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserHistoDump --
 *
 *      Dump the specified histogram for the specified lock.
 *
 * Results:
 *      The histogram is dumped to the statistics log.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserHistoDump(MXUserHisto *histo,    // IN:
                MXUserHeader *header)  // IN:
{
   ASSERT(header != NULL);
   ASSERT(histo != NULL);

   if (histo->totalSamples) {
      char *p;
      uint32 i;
      uint32 spaceLeft;

      ASSERT(mxUserHistoLine != NULL);

      i = Str_Sprintf(mxUserHistoLine, mxUserMaxLineLength,
                      "MXUser: h l=%"FMT64"u t=%s min=%"FMT64"u "
                      "max=%"FMT64"u\n",
                      header->serialNumber, histo->typeName,
                      histo->minValue, histo->maxValue);

      /*
       * The terminating "\n\0" will be overwritten each time a histogram
       * bin is added to the line. This will ensure that the line is always
       * properly terminated no matter what happens.
       */

      p = &mxUserHistoLine[i - 1];
      spaceLeft = mxUserMaxLineLength - i - 2;

      /* Add as many histogram bins as possible within the line limitations */
      for (i = 0; i < histo->numBins; i++) {
         if (histo->binData[i] != 0) {
            uint32 len;
            char binEntry[32];

            len = Str_Sprintf(binEntry, sizeof binEntry, " %u-%"FMT64"u\n",
                              i, histo->binData[i]);

            if (len < spaceLeft) {
               /*
                * Append the bin number, bin count pair to the end of the
                * string. This includes the terminating "\n\0". Update the
                * pointer to the next free place to point to the '\n'. If
                * another entry is made, things work out properly. If not
                * the string is properly terminated as a line.
                */

               Str_Strcpy(p, binEntry, len + 1);
               p += len - 1;
               spaceLeft -= len;
            } else {
               break;
            }
         }
      }

      MXUserStatsLog("%s", mxUserHistoLine);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserBasicStatsSample --
 *
 *      Add a sample to the "pure" statistics object.
 *
 * Results:
 *      The sample is added.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserBasicStatsSample(MXUserBasicStats *stats,  // IN/OUT:
                       uint64 durationNS)        // IN:
{
   stats->numSamples++;

   if (durationNS < stats->minTime) {
      stats->minTime = durationNS;
   }

   if (durationNS > stats->maxTime) {
      stats->maxTime = durationNS;
   }

   stats->timeSum += durationNS;

   /* Do things in floating point to avoid uint64 overflow */
   stats->timeSquaredSum += ((double) durationNS) * ((double) durationNS);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserBasicStatsSetUp --
 *
 *      Set up the "pure" statistics object.
 *
 * Results:
 *      The statistics are set up.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserBasicStatsSetUp(MXUserBasicStats *stats,  // IN/OUT:
                      const char *typeName)     // IN:
{
   stats->typeName = Util_SafeStrdup(typeName);
   stats->numSamples = 0;
   stats->minTime = ~CONST64U(0);
   stats->maxTime = 0;
   stats->timeSum = 0;
   stats->timeSquaredSum = 0.0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDumpBasicStats --
 *
 *      Dump the basic statistics. This routine may run during concurrent
 *      locking activity so explicit checks are necessary to deal with
 *      jittering data.
 *
 * Results:
 *      Interesting data is added to the statistics log.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static double
MXUserSqrt(double x)  // IN: hack until next round when FP goes away
{
   double xn;
   double xn1 = x;

   if (x == 0.0) {
      return 0.0;
   }

   do {
      xn = xn1;
      xn1 = (xn + x/xn) / 2.0;
   } while (fabs(xn1 - xn) > 1E-10);

   return xn1;
}

void
MXUserDumpBasicStats(MXUserBasicStats *stats,  // IN:
                     MXUserHeader *header)     // IN:
{
   uint64 stdDev;

   if (stats->numSamples < 2) {
      /*
       * It's possible to get a request to dump statistics when there
       * aren't any (e.g. a lock has been acquired but never released so
       * there are no "held" statistics yet). Ignore requests to dump
       * statistics when there aren't any.
       */

      if (stats->numSamples == 0) {
         return;
      }

      stdDev = 0;
   } else {
      double num;
      double mean;
      double variance;

      num = (double) stats->numSamples;
      mean = ((double) stats->timeSum) / num;
      variance = (stats->timeSquaredSum - (num*mean*mean)) / (num - 1.0);

      stdDev = (variance < 0.0) ? 0 : (uint64) (MXUserSqrt(variance) + 0.5);
   }

   MXUserStatsLog("MXUser: e l=%"FMT64"u t=%s c=%"FMT64"u min=%"FMT64"u "
                  "max=%"FMT64"u mean=%"FMT64"u sd=%"FMT64"u\n",
                  header->serialNumber, stats->typeName,
                  stats->numSamples, stats->minTime, stats->maxTime,
                  stats->timeSum/stats->numSamples, stdDev);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserBasicStatsTearDown --
 *
 *      Tear down an basic statistics object.
 *
 * Results:
 *      The statistics are set up.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserBasicStatsTearDown(MXUserBasicStats *stats)  // IN/OUT:
{
   free(stats->typeName);
   stats->typeName = NULL;
}




/*
 *-----------------------------------------------------------------------------
 *
 * MXUserAcquisitionStatsSetUp --
 *
 *      Set up an acquisition statistics object.
 *
 * Results:
 *      The statistics are set up.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserAcquisitionStatsSetUp(MXUserAcquisitionStats *stats)  // IN/OUT:
{
   MXUserBasicStatsSetUp(&stats->basicStats, MXUSER_STAT_CLASS_ACQUISITION);

   stats->contentionRatioFloor = mxUserContentionRatioFloor;
   stats->contentionCountFloor = mxUserContentionCountFloor;
   stats->contentionDurationFloor = mxUserContentionDurationFloor;
   stats->numAttempts = 0;
   stats->numSuccesses = 0;
   stats->numSuccessesContended = 0;
   stats->totalContentionTime = 0;
   stats->successContentionTime = 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserAcquisitionSample --
 *
 *      Track the acquisition specific statistical data.
 *
 * Results:
 *      Much CPU time may be used.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserAcquisitionSample(MXUserAcquisitionStats *stats,  // IN/OUT:
                        Bool wasAcquired,               // IN:
                        Bool wasContended,              // IN:
                        uint64 elapsedTime)             // IN:
{
   stats->numAttempts++;

   if (wasAcquired) {
      stats->numSuccesses++;

      if (wasContended) {
         stats->numSuccessesContended++;
         stats->totalContentionTime += elapsedTime;
         stats->successContentionTime += elapsedTime;
      }

      MXUserBasicStatsSample(&stats->basicStats, elapsedTime);
   } else {
      ASSERT(wasContended);

      stats->totalContentionTime += elapsedTime;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDumpAcquisitionStats --
 *
 *      Dump the acquisition statistics for the specified lock.
 *
 * Results:
 *      Much CPU time may be used.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserDumpAcquisitionStats(MXUserAcquisitionStats *stats,  // IN:
                           MXUserHeader *header)           // IN:
{
   if (stats->numAttempts > 0) {
      if (stats->numSuccesses > 0) {
         MXUserDumpBasicStats(&stats->basicStats, header);
      }

      MXUserStatsLog("MXUser: ce l=%"FMT64"u a=%"FMT64"u s=%"FMT64"u "
                     "sc=%"FMT64"u sct=%"FMT64"u t=%"FMT64"u\n",
                     header->serialNumber,
                     stats->numAttempts,
                     stats->numSuccesses,
                     stats->numSuccessesContended,
                     stats->successContentionTime,
                     stats->totalContentionTime);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserAcquisitionStatsTearDown --
 *
 *      Tear down an acquisition statistics object.
 *
 * Results:
 *      The statistics are set up.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserAcquisitionStatsTearDown(MXUserAcquisitionStats *stats)  // IN/OUT:
{
   MXUserBasicStatsTearDown(&stats->basicStats);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserKitchen --
 *
 *      If you can't take the heat, get out of the kitchen! Report on the
 *      heat generated by the specified lock's acquisition statistics.
 *
 * Results:
 *      Data is returned.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserKitchen(MXUserAcquisitionStats *stats,  // IN:
              double *contentionRatio,        // OUT:
              Bool *isHot,                    // OUT:
              Bool *doLog)                    // OUT:
{
   /*
    * How much "heat" is this lock generating?
    */

   if (stats->numAttempts < stats->contentionCountFloor) {
      *contentionRatio = 0.0;
   } else {
      double basic;
      double acquisition;

      /*
       * Contention shows up in two ways - failed attempts to acquire
       * and detected contention while acquiring. Determine which is
       * the largest and use that as the contention ratio for the
       * specified statistics.
       */

      basic = ((double) stats->numAttempts - stats->numSuccesses) /
               ((double) stats->numAttempts);

      acquisition = ((double) stats->numSuccessesContended) /
                     ((double) stats->numSuccesses);

      *contentionRatio = (basic < acquisition) ? acquisition : basic;
   }

   /*
    * Handle the explicit control cases.
    *
    * An mxUserContentionCountFloor value of zero (0) forces all locks to be
    * considered "cold", regardless of their activity.
    *
    * An mxUserContentionCountFloor value of ~((uint64) 0) (all Fs) forces all
    * locks to be considered "hot" regardless of their activity, with the
    * side effect that no logging of "temperature changes" is done.
    */

   if (stats->contentionCountFloor == 0) {              // never "hot"
      *isHot = FALSE;
      *doLog = FALSE;

      return;
   }

   if (stats->contentionCountFloor == ~((uint64) 0)) {  // always "hot"; no logging
      *isHot = TRUE;
      *doLog = FALSE;

      return;
   }

   /*
    * Did the thermostat trip?
    */


   if (*contentionRatio > stats->contentionRatioFloor) {  // Yes
      *isHot = TRUE;
      *doLog = TRUE;
   } else {                                               // No
      *doLog = FALSE;
      *isHot = FALSE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_StatisticsControl --
 *
 *      Specify the settings for automatic "hot locks" operation.
 *
 * Results:
 *      Enhanced statistics will be used or never used, depending on these
 *      values;
 *
 * Side effects:
 *      Unknown...
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_StatisticsControl(double contentionRatioFloor,     // IN:
                         uint64 minAccessCountFloor,      // IN:
                         uint64 contentionDurationFloor)  // IN: ns
{
   ASSERT((contentionRatioFloor > 0.0) && (contentionRatioFloor <= 1.0));

   mxUserContentionRatioFloor = contentionRatioFloor;
   mxUserContentionCountFloor = minAccessCountFloor;
   mxUserContentionDurationFloor = contentionDurationFloor;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserForceHisto --
 *
 *      Force histogram taking for the specified histogram.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      Memory is allocated.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
MXUserForceHisto(Atomic_Ptr *histoPtr,  // IN/OUT:
                 const char *typeName,  // IN:
                 uint64 minValue,       // IN:
                 uint32 decades)        // IN:
{
   MXUserHisto *ptr = Atomic_ReadPtr(histoPtr);

   if (ptr == NULL) {
      MXUserHisto *before;

      ptr = MXUserHistoSetUp(typeName, minValue, decades);

      before = Atomic_ReadIfEqualWritePtr(histoPtr, NULL, (void *) ptr);

      if (before) {
         MXUserHistoTearDown(ptr);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserForceAcquisitionHisto --
 *
 *      Force acquisition histogram taking.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      Memory is allocated.
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUserForceAcquisitionHisto(Atomic_Ptr *mem,  // IN/OUT:
                            uint64 minValue,  // IN:
                            uint32 decades)   // IN:
{
   MXUserAcquireStats *acquireStats = Atomic_ReadPtr(mem);

   if (LIKELY(acquireStats != NULL)) {
      MXUserForceHisto(&acquireStats->histo, MXUSER_STAT_CLASS_ACQUISITION,
                       minValue, decades);
   }

   return (acquireStats != NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserForceHeldHisto --
 *
 *      Force held histogram taking.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      Memory is allocated.
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUserForceHeldHisto(Atomic_Ptr *mem,  // IN/OUT:
                     uint64 minValue,  // IN:
                     uint32 decades)   // IN:
{
   MXUserHeldStats *heldStats = Atomic_ReadPtr(mem);

   if (LIKELY(heldStats != NULL)) {
      MXUserForceHisto(&heldStats->histo, MXUSER_STAT_CLASS_HELD,
                       minValue, decades);
   }

   return (heldStats != NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserStatsMode --
 *
 *      What's to be done with statistics?
 *
 * Results:
 *      0  Statistics are disabled
 *      1  Collect statistics without tracking held times
 *      2  Collect statistics with track held times
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

uint32
MXUserStatsMode(void)
{
   if (vmx86_stats && (mxUserStatsFunc != NULL) && (mxUserMaxLineLength > 0)) {
      return mxUserTrackHeldTimes ? 2 : 1;
   } else {
      return 0;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_SetStatsFunc --
 *
 *      Establish statistics taking and reporting. This is done by registering
 *      a statistics context, a reporting function and a maximum line length.
 *
 *      A maxLineLength of zero (0) and/or a statsFunc of NULL will
 *      disable/prevent statistics gathering.
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_SetStatsFunc(void *context,                    // IN:
                    uint32 maxLineLength,             // IN:
                    Bool trackHeldTimes,              // IN:
                    void (*statsFunc)(void *context,  // IN:
                                      const char *fmt,
                                      va_list ap))
{
   ASSERT(maxLineLength >= 1024);   // assert a rational minimum

   free(mxUserHistoLine);
   mxUserHistoLine = Util_SafeMalloc(maxLineLength);

   mxUserStatsContext = context;
   mxUserMaxLineLength = maxLineLength;
   mxUserStatsFunc = statsFunc;
   mxUserTrackHeldTimes = trackHeldTimes;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUser_PerLockData --
 *
 *      Perform the statistics logging duties.
 *
 *      The dumping is done on active locks so the data is, at best,
 *      approximate. This is OK for statistics builds since things should
 *      be "close enough".
 *
 *      This routine is called from within the statistics collection framework.
 *      It is called, periodically, at the end of each statistical "epoch".
 *
 * Results:
 *      Lots of statistics in statistics (obj/opt/stats) builds.
 *
 * Side effects:
 *      The overhead of the statistics process may affect the timing of
 *      events and expose bugs. Be prepared!
 *
 *-----------------------------------------------------------------------------
 */

void
MXUser_PerLockData(void)
{
   MXRecLock *listLock = MXUserInternalSingleton(&mxLockMemPtr);

   if (mxUserStatsFunc == NULL) {
      return;
   }

   if (listLock && MXRecLockTryAcquire(listLock)) {
      ListItem *entry;
      uint64 highestSerialNumber;
      static uint64 lastReportedSerialNumber = 0;

      highestSerialNumber = lastReportedSerialNumber;

      CIRC_LIST_SCAN(entry, mxUserLockList) {
         MXUserHeader *header = CIRC_LIST_CONTAINER(entry, MXUserHeader, item);

         /* Log the ID information for a lock that did exist previously */
         if (header->serialNumber > lastReportedSerialNumber) {
            MXUserStatsLog("MXUser: n n=%s l=%"FMT64"u r=0x%x\n", header->name,
                           header->serialNumber, header->rank);

            if (header->serialNumber > highestSerialNumber) {
               highestSerialNumber = header->serialNumber;
            }
         }

         /*
          * Perform the statistics action for any lock that has one.
          */

         if (header->statsFunc) {
            (*header->statsFunc)(header);
         }
      }

      lastReportedSerialNumber = highestSerialNumber;

      MXRecLockRelease(listLock);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserAllocSerialNumber --
 *
 *      Allocate and return an MXUser serial number.
 *
 *      MXUser serial numbers are never recycled.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      Panic if too many serial numbers are generated (PR 1309544).
 *
 *-----------------------------------------------------------------------------
 */

uint64
MXUserAllocSerialNumber(void)
{
   uint64 value;

   static Atomic_uint64 firstFreeSerialNumber = { 1 };  // must start not zero

   value = Atomic_ReadInc64(&firstFreeSerialNumber);

   if (value == 0) {  // We wrapped! Zounds!
      Panic("%s: too many locks!\n", __FUNCTION__);
   }

   return value;
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserSetContentionRatioFloor
 *
 *      Set acquisition tracking contention ratio floor.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUserSetContentionRatioFloor(Atomic_Ptr *mem,  // IN/OUT:
                              double ratio)     // IN:
{
   MXUserAcquireStats *acquireStats = Atomic_ReadPtr(mem);

   if (LIKELY(acquireStats != NULL)) {
      acquireStats->data.contentionRatioFloor = ratio;
   }

   return (acquireStats != NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserSetContentionCountFloor
 *
 *      Set acquisition tracking contention count floor.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUserSetContentionCountFloor(Atomic_Ptr *mem,  // IN/OUT:
                              uint64 count)     // IN:
{
   MXUserAcquireStats *acquireStats = Atomic_ReadPtr(mem);

   if (LIKELY(acquireStats != NULL)) {
      acquireStats->data.contentionCountFloor = count;
   }

   return (acquireStats != NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserSetContentionDurationFloor
 *
 *      Set acquisition tracking contention duration floor.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
MXUserSetContentionDurationFloor(Atomic_Ptr *mem,  // IN/OUT:
                                 uint64 count)     // IN:
{
   MXUserAcquireStats *acquireStats = Atomic_ReadPtr(mem);

   if (LIKELY(acquireStats != NULL)) {
      acquireStats->data.contentionDurationFloor = count;
   }

   return (acquireStats != NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserDisableStats
 *
 *      Disable any statisitics collection. This should only be used
 *      immediately after a lock is created.
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserDisableStats(Atomic_Ptr *acquisitionMem,  // IN/OPT:
                   Atomic_Ptr *heldMem)         // IN/OPT:
{
   if (acquisitionMem != NULL) {
      MXUserAcquireStats *acquireStats = Atomic_ReadPtr(acquisitionMem);

      if (UNLIKELY(acquireStats != NULL)) {
         MXUserAcquisitionStatsTearDown(&acquireStats->data);
         MXUserHistoTearDown(Atomic_ReadPtr(&acquireStats->histo));

         free(acquireStats);
      }

      Atomic_WritePtr(acquisitionMem, NULL);
   }

   if (heldMem != NULL) {
      MXUserHeldStats *heldStats = Atomic_ReadPtr(heldMem);

      if (UNLIKELY(heldStats != NULL)) {
         MXUserBasicStatsTearDown(&heldStats->data);
         MXUserHistoTearDown(Atomic_ReadPtr(&heldStats->histo));

         free(heldStats);
      }

      Atomic_WritePtr(heldMem, NULL);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * MXUserEnableStats
 *
 *      Enable statisitics collection
 *
 * Results:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
MXUserEnableStats(Atomic_Ptr *acquisitionMem,  // IN/OPT:
                  Atomic_Ptr *heldMem)         // IN/OPT:
{

   if (acquisitionMem != NULL) {
      MXUserAcquireStats *acquireStats = Atomic_ReadPtr(acquisitionMem);

      if (LIKELY(acquireStats == NULL)) {
         MXUserAcquireStats *before;

         acquireStats = Util_SafeCalloc(1, sizeof *acquireStats);
         MXUserAcquisitionStatsSetUp(&acquireStats->data);

         before = Atomic_ReadIfEqualWritePtr(acquisitionMem, NULL,
                                             (void *) acquireStats);

         if (before) {
            free(acquireStats);
         }
      }
   }

   if (heldMem != NULL) {
      MXUserHeldStats *heldStats = Atomic_ReadPtr(heldMem);

      if (LIKELY(heldStats == NULL)) {
         MXUserHeldStats *before;

         heldStats = Util_SafeCalloc(1, sizeof *heldStats);
         MXUserBasicStatsSetUp(&heldStats->data, MXUSER_STAT_CLASS_HELD);

         before = Atomic_ReadIfEqualWritePtr(heldMem, NULL,
                                             (void *) heldStats);

         if (before) {
            free(heldStats);
         }
      }
   }
}

 0707010000017E000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/message    0707010000017F000081A400000000000000000000000168225505000003DE000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/message/Makefile.am    ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libMessage.la

libMessage_la_SOURCES =
libMessage_la_SOURCES += message.c

  07070100000180000081A40000000000000000000000016822550500004705000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/message/message.c  /*********************************************************
 * Copyright (C) 1999-2017, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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 canceled by vmware if a checkpoint/restore occurs in the middle of
 *    such an operation. This layer takes care of retrying those operations.
 */

#include "backdoor_def.h"
#include "guest_msg_def.h"

#ifdef __cplusplus
extern "C" {
#endif

#if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL)
#   include "kernelStubs.h"
#else
#   include <stdio.h>
#   include <stdlib.h>
#   include "debug.h"
#endif

#include "backdoor.h"
#include "message.h"

#if defined(MESSAGE_DEBUG)
#  define MESSAGE_LOG(...)   Warning(__VA_ARGS__)
#else
#  define MESSAGE_LOG(...)
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Message_OpenAllocated --
 *
 *    Open a communication channel using an allocated, but unitialized
 *    Message_Channel structure.  A receive buffer may be optionally
 *    specified with a given size.  If a message larger than this
 *    buffer is received then the communication will end.  If no
 *    receiver buffer is specified, one will be dynamically allocated
 *    to size.  When finished with the channel, Message_CloseAllocated
 *    should be called.
 *
 * Result:
 *    TRUE on success, FALSE on failure.
 *
 * Side-effects:
 *    See above.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Message_OpenAllocated(uint32 proto, Message_Channel *chan,
                      char *receiveBuffer, size_t receiveBufferSize)
{
   uint32 flags;
   Backdoor_proto bp;

   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");
      return FALSE;
   }

   /* 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 = (unsigned char *)receiveBuffer;
   chan->inAlloc = receiveBufferSize;

   ASSERT((receiveBuffer == NULL) == (receiveBufferSize == 0));
   chan->inPreallocated = receiveBuffer != NULL;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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
{
   Message_Channel *chan = malloc(sizeof *chan);

   if (chan != NULL && !Message_OpenAllocated(proto, chan, NULL, 0)) {
      free(chan);
      chan = NULL;
   }
   return chan;
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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
{
   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;
      }
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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
{
   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) {
      if (chan->inPreallocated) {
         MESSAGE_LOG("Message: Buffer too small to receive a message over "
                     "the communication channel %u\n", chan->id);
         goto error_quit;
      } else {
         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;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Message_CloseAllocated --
 *
 *    Close a communication channel that had been allocated by the
 *    caller.  (For use with Message_OpenAllocated.)
 *
 * Result:
 *    TRUE on success, the channel is destroyed
 *    FALSE on failure
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Message_CloseAllocated(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;
   }

   if (!chan->inPreallocated) {
      free(chan->in);
   }
   chan->in = NULL;

   return ret;
}
   

/*
 *-----------------------------------------------------------------------------
 *
 * 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
{
   Bool ret = Message_CloseAllocated(chan);

   free(chan);
   return ret;
}

#ifdef __cplusplus
}
#endif
   07070100000181000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/misc   07070100000182000081A400000000000000000000000168225505000007E3000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/Makefile.am   ################################################################################
### Copyright (C) 2007-2017,2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libMisc.la

libMisc_la_SOURCES =
libMisc_la_SOURCES += base64.c
libMisc_la_SOURCES += codeset.c
libMisc_la_SOURCES += codesetBase.c
libMisc_la_SOURCES += codesetOld.c
libMisc_la_SOURCES += codesetUTF8.c
libMisc_la_SOURCES += dynarray.c
libMisc_la_SOURCES += dynbuf.c
libMisc_la_SOURCES += escape.c
libMisc_la_SOURCES += hashTable.c
libMisc_la_SOURCES += hostinfo.c
libMisc_la_SOURCES += hostinfoHV.c
libMisc_la_SOURCES += hostinfoPosix.c
libMisc_la_SOURCES += hostname.c
libMisc_la_SOURCES += hostType.c
libMisc_la_SOURCES += idLinux.c
libMisc_la_SOURCES += iovector.c
libMisc_la_SOURCES += jsonUTF8.c
libMisc_la_SOURCES += logFixed.c
libMisc_la_SOURCES += machineID.c
libMisc_la_SOURCES += miscSolaris.c
libMisc_la_SOURCES += posixDlopen.c
libMisc_la_SOURCES += posixPosix.c
libMisc_la_SOURCES += posixPwd.c
libMisc_la_SOURCES += prng.c
libMisc_la_SOURCES += random.c
libMisc_la_SOURCES += sleep.c
libMisc_la_SOURCES += timeutil.c
libMisc_la_SOURCES += util_misc.c
libMisc_la_SOURCES += utilMem.c
libMisc_la_SOURCES += vmstdio.c
libMisc_la_SOURCES += strutil.c
libMisc_la_SOURCES += vthreadBase.c

 07070100000183000081A4000000000000000000000001682255050000540B000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/base64.c  /*
 * Copyright (c) 1996, 1998 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

/*
 * Portions Copyright (c) 1995 by International Business Machines, Inc.
 *
 * International Business Machines, Inc. (hereinafter called IBM) grants
 * permission under its copyrights to use, copy, modify, and distribute this
 * Software with or without fee, provided that the above copyright notice and
 * all paragraphs of this notice appear in all copies, and that the name of IBM
 * not be used in connection with the marketing of any product incorporating
 * the Software or modifications thereof, without specific, written prior
 * permission.
 *
 * To the extent it has a right to do so, IBM grants an immunity from suit
 * under its patents, if any, for the use, sale or manufacture of products to
 * the extent that such products are used for performing Domain Name System
 * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
 * granted for any product per se or for any other function of any product.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
 * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
 * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
 */

#include <sys/types.h>

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vm_basic_types.h"
#include "vm_assert.h"
#include "base64.h"

static const char Base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char Pad64 = '=';

// Special markers
enum {
   ILLEGAL = -1, EOM = -2, WS = -3
};

/*
 * Reverse byte map used for decoding. Except for specials (negative values),
 * contains the index into Base64[] where given value is found, ie:
 * base64Reverse[Base64[n]] = n, for 0 <= n < 64
 *
 * This static initialization replaces, and should have identical result to,
 * this runtime init:
 *
 *  for (i = 0; i < 256; ++i) {
 *     base64Reverse[i] = isspace(i) ? WS : ILLEGAL;
 *  }
 *  base64Reverse['\0']  = EOM;
 *  base64Reverse['=']   = EOM;
 *  for (i = 0; Base64[i]; ++i) {
 *     base64Reverse[(unsigned)Base64[i]] = (char) i;
 *  }
 */

static const signed char base64Reverse[256] = {
   EOM,     ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 00-07 */
   ILLEGAL, WS,      WS,      WS,      WS,      WS,      ILLEGAL, ILLEGAL,   /* 08-0F */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 10-17 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 18-1F */
   WS,      ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 20-27 */
   ILLEGAL, ILLEGAL, ILLEGAL, 62,      ILLEGAL, ILLEGAL, ILLEGAL, 63,        /* 28-2F */
   52,      53,      54,      55,      56,      57,      58,      59,        /* 30-37 */
   60,      61,      ILLEGAL, ILLEGAL, ILLEGAL, EOM,     ILLEGAL, ILLEGAL,   /* 38-3F */
   ILLEGAL, 0,       1,       2,       3,       4,       5,       6,         /* 40-47 */
   7,       8,       9,       10,      11,      12,      13,      14,        /* 48-4F */
   15,      16,      17,      18,      19,      20,      21,      22,        /* 50-57 */
   23,      24,      25,      ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 58-5F */
   ILLEGAL, 26,      27,      28,      29,      30,      31,      32,        /* 60-67 */
   33,      34,      35,      36,      37,      38,      39,      40,        /* 68-6F */
   41,      42,      43,      44,      45,      46,      47,      48,        /* 70-77 */
   49,      50,      51,      ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 78-7F */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 80-87 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 88-8F */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 90-97 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* 98-9F */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* A0-A7 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* A8-AF */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* B0-B7 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* B8-BF */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* C0-C7 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* C8-CF */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* D0-D7 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* D8-DF */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* E0-E7 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* E8-EF */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,   /* F0-F7 */
   ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL }; /* F8-FF */

/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
   The following encoding technique is taken from RFC 1521 by Borenstein
   and Freed.  It is reproduced here in a slightly edited form for
   convenience.

   A 65-character subset of US-ASCII is used, enabling 6 bits to be
   represented per printable character. (The extra 65th character, "=",
   is used to signify a special processing function.)

   The encoding process represents 24-bit groups of input bits as output
   strings of 4 encoded characters. Proceeding from left to right, a
   24-bit input group is formed by concatenating 3 8-bit input groups.
   These 24 bits are then treated as 4 concatenated 6-bit groups, each
   of which is translated into a single digit in the base64 alphabet.

   Each 6-bit group is used as an index into an array of 64 printable
   characters. The character referenced by the index is placed in the
   output string.

   Table 1: The Base64 Alphabet

   Value Encoding  Value Encoding  Value Encoding  Value Encoding
   0 A             17 R            34 i            51 z
   1 B             18 S            35 j            52 0
   2 C             19 T            36 k            53 1
   3 D             20 U            37 l            54 2
   4 E             21 V            38 m            55 3
   5 F             22 W            39 n            56 4
   6 G             23 X            40 o            57 5
   7 H             24 Y            41 p            58 6
   8 I             25 Z            42 q            59 7
   9 J             26 a            43 r            60 8
   10 K            27 b            44 s            61 9
   11 L            28 c            45 t            62 +
   12 M            29 d            46 u            63 /
   13 N            30 e            47 v
   14 O            31 f            48 w         (pad) =
   15 P            32 g            49 x
   16 Q            33 h            50 y

   Special processing is performed if fewer than 24 bits are available
   at the end of the data being encoded.  A full encoding quantum is
   always completed at the end of a quantity.  When fewer than 24 input
   bits are available in an input group, zero bits are added (on the
   right) to form an integral number of 6-bit groups.  Padding at the
   end of the data is performed using the '=' character.

   Since all base64 input is an integral number of octets, only the
   -------------------------------------------------
   following cases can arise:

   (1) the final quantum of encoding input is an integral
   multiple of 24 bits; here, the final unit of encoded
   output will be an integral multiple of 4 characters
   with no "=" padding,
   (2) the final quantum of encoding input is exactly 8 bits;
   here, the final unit of encoded output will be two
   characters followed by two "=" padding characters, or
   (3) the final quantum of encoding input is exactly 16 bits;
   here, the final unit of encoded output will be three
   characters followed by one "=" padding character.
*/

/*
 *----------------------------------------------------------------------------
 *
 * Base64_Encode --
 *
 *      Base64-encodes srcLength bytes from src and stores result in dst.
 *
 * Results:
 *      TRUE if the destination held enough space for the decoded result,
 *      FALSE otherwise.
 *
 * Side effects:
 *      Updates dstSize with the number of encoded bytes (excluding the
 *      terminating '\0').
 *
 *----------------------------------------------------------------------------
 */

Bool
Base64_Encode(uint8 const *src,  // IN:
              size_t srcSize,    // IN:
              char *dst,         // OUT:
              size_t dstMax,     // IN: max result length, including NUL byte
              size_t *dstSize)   // OUT/OPT: result length
{
   char *dst0 = dst;
   Bool retval = TRUE;

   ASSERT(src || srcSize == 0);
   ASSERT(dst);

   /* Security: Carefully written to avoid fixed arithmetic attacks. */
   if (   srcSize + 2 < srcSize
       || dstMax < 1
       || (srcSize + 2) / 3 > (dstMax - 1) / 4) {
      retval = FALSE;
      goto exit;
   }

   while (LIKELY(srcSize > 2)) {
      dst[0] = Base64[src[0] >> 2];
      dst[1] = Base64[(src[0] & 0x03) << 4 | src[1] >> 4];
      dst[2] = Base64[(src[1] & 0x0f) << 2 | src[2] >> 6];
      dst[3] = Base64[src[2] & 0x3f];

      srcSize -= 3;
      src += 3;
      dst += 4;
   }

   /* Now we worry about padding. */
   if (LIKELY(srcSize--)) {
      uint8 src1 = srcSize ? src[1] : 0;

      dst[0] = Base64[src[0] >> 2];
      dst[1] = Base64[(src[0] & 0x03) << 4 | src1 >> 4];
      dst[2] = srcSize ? Base64[(src1 & 0x0f) << 2] : Pad64;
      dst[3] = Pad64;
      dst += 4;
   }

   dst[0] = '\0';	/* Returned value doesn't count \0. */

exit:
   if (dstSize) {
      *dstSize = dst - dst0;
   }

   return retval;
}


#ifdef __I_WANT_TO_TEST_THIS__
main()
{
   struct {
      char *in, *out;
   } tests[] = {
      {"", ""},
      {"MQ==", "1"},
      {"MTI=", "12"},
      {"MTIz", "123"},
      {"MTIzNA==", "1234"},
      {"SGVsbG8gRWR3YXJkIGFuZCBKb2huIQ==","Hello Edward and John!"},
      {NULL, NULL}
   }, *test;

   size_t bufMax;
   if (1) {
      for (bufMax = 0; bufMax < 7; ++bufMax) {
         char buf[999];
         size_t bufSize;

         if (bufMax == 6) {
            bufMax = sizeof buf;
         }

         printf("\nBuffer size %"FMTSZ"u:\n", bufMax);

         test = tests;
         for (; test->in; ++test) {
            Bool r;

            r = Base64_Decode(test->in, buf, bufMax, &bufSize);

            if ((bufMax > strlen(test->out)) && (bufSize < strlen(test->out))) {
               printf("Decoding of %s failed. Decoded size %"FMTSZ"u < expected %"FMTSZ"u\n",
                      test->in, bufSize, strlen(test->out));
            }
            if (memcmp(test->out, buf, bufSize) != 0) {
               printf("Decoding of %s failed.  Got %s (%"FMTSZ"u), not %s\n",
                      test->in, buf, bufSize, test->out);
            } else {
               printf("Good: %s -> %s (%"FMTSZ"u)\n", test->in, buf, bufSize);
            }

            r = Base64_Encode(test->out, strlen(test->out),
                              buf, bufMax, &bufSize);
            buf[bufMax] = 0;

            if (bufMax <= strlen(test->in) && r == 0) {
               printf("Good: %s. Failed for bufMax %"FMTSZ"u (required %"FMTSZ"u)\n", test->out, bufMax, strlen(test->in));
            } else {
                if (!r || bufSize != strlen(test->in) ||
                    strncmp(test->in, buf, bufSize) != 0) {
                   printf("Encoding of %s failed.  r = %d. Got %s (%"FMTSZ"u), not %s\n",
                          test->out, r, buf, bufSize, test->in);
                } else {
                   printf("Good: %s -> %s (%"FMTSZ"u)\n", test->out, buf, bufSize);
                }
            }
         }
      }
   }

   for (bufMax = 0; bufMax < 100000; ++bufMax) {
      char random_in[8000];
      char random_out[16000];
      size_t bufSize;

      Bool r = Base64_Encode(random_in, sizeof random_in,
                             random_out, sizeof random_out, &bufSize);

      if (!r) {
            printf("Encoding failed.\n");
      }
   }
}
#endif


/*
 *----------------------------------------------------------------------------
 *
 * Base64_Decode --
 *
 *       Skips all whitespace anywhere. Converts characters, four at
 *       a time, starting at (or after) src from base - 64 numbers into three
 *       8 bit bytes in the target area. Returns the number of data bytes
 *       stored at the target in the provided out parameter.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
Base64_Decode(char const *in,      // IN:
              uint8 *out,          // OUT:
              size_t outSize,      // IN:
              size_t *dataLength)  // OUT:
{
   /* -1 is properly handled in the Base64_ChunkDecode method. */
   /* coverity[overrun-buffer-arg:FALSE] */
   return Base64_ChunkDecode(in, -1, out, outSize, dataLength);
}


/*
 *----------------------------------------------------------------------------
 *
 * Base64_ChunkDecode --
 *
 *       Skips all whitespace anywhere. Converts characters, four at
 *       a time, starting at (or after) src from base - 64 numbers into three
 *       8 bit bytes in the target area. Conversion stops after inSize (which
 *       must be a multiple of 4) characters, or an EOM marker. Returns the
 *       number of data bytes stored at the target in the provided out parameter.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
Base64_ChunkDecode(char const *in,      // IN:
                   size_t inSize,       // IN:
                   uint8 *out,          // OUT:
                   size_t outSize,      // IN:
                   size_t *dataLength)  // OUT:
{
   uint32 b = 0;
   int n = 0;
   uintptr_t i = 0;
   size_t inputIndex = 0;

   ASSERT(in);
   ASSERT(out || outSize == 0);
   ASSERT(dataLength);
   ASSERT((inSize == -1) || (inSize % 4) == 0);
   *dataLength = 0;

   i = 0;
   for (;inputIndex < inSize;) {
      int p = base64Reverse[(unsigned char)in[inputIndex]];

      if (UNLIKELY(p < 0)) {
         switch (p) {
         case WS:
            inputIndex++;
            break;
         case EOM:
            *dataLength = i;
            return TRUE;
         case ILLEGAL:
         default:
            return FALSE;
         }
      } else {
         inputIndex++;
         if (UNLIKELY(i >= outSize)) {
            return FALSE;
         }
         b = (b << 6) | p;
         n += 6;
         if (LIKELY(n >= 8)) {
            n -= 8;
            out[i++] = b >> n;
         }
      }
   }
   *dataLength = i;
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * Base64_ValidEncoding --
 *
 *      Returns TRUE if the specified input buffer is valid Base64 input.
 *
 * Results:
 *      TRUE or FALSE.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
Base64_ValidEncoding(char const *src,   // IN:
                     size_t srcLength)  // IN:
{
   size_t i;

   ASSERT(src);
   for (i = 0; i < srcLength; i++) {
      uint8 c = src[i]; /* MSVC CRT will die on negative arguments to is* */

      if (!isalpha(c) && !isdigit(c) &&
          c != '+' && c != '=' && c != '/') {
         return FALSE;
      }
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * Base64_EncodedLength --
 *
 *      Given a binary buffer, how many bytes would it take to encode it.
 *
 * Results:
 *      Number of bytes needed to encode, including terminating NUL byte.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

size_t
Base64_EncodedLength(uint8 const *src,  // IN:
                     size_t srcLength)  // IN:
{
   return ((srcLength + 2) / 3 * 4) + 1;
}


/*
 *----------------------------------------------------------------------------
 *
 * Base64_DecodedLength --
 *
 *      Given a base64 encoded string, how many bytes do we need to decode it.
 *      Assumes no whitespace.  This is not necessarily the length of the
 *      decoded data (Base64_Decode requires a few extra bytes... don't blame
 *      me, I didn't write it).
 *
 * Results:
 *      Number of bytes needed to decode input.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

size_t
Base64_DecodedLength(char const *src,   // IN:
                     size_t srcLength)  // IN:
{
   size_t length;

   ASSERT(src);

   length = srcLength / 4 * 3;
   // PR 303173 - do the following check to avoid a negative value returned
   // from this function. Note: length can only be in a multiple of 3
   if (length > 2) {
      if (src[srcLength - 1] == '=') {
         length--;
      }
      if (src[srcLength - 2] == '=') {
         length--;
      }
   }
   return length;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Base64_EasyEncode --
 *
 *      Base64-encode 'data' into a NUL-terminated string.
 *
 * Results:
 *      On success: TRUE. '*target' is set to an allocated string, that the
 *                  caller must eventually free().
 *      On failure: FALSE. '*target' is set to NULL.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Base64_EasyEncode(const uint8 *src,  // IN: data to encode
                  size_t srcLength,  // IN: data size
                  char **target)     // OUT: encoded string
{
   Bool succeeded = FALSE;
   size_t size;

   ASSERT(src);
   ASSERT(target);

   size = Base64_EncodedLength(src, srcLength);

   *target = malloc(size);

   if (!*target) {
      goto exit;
   }

   if (!Base64_Encode(src, srcLength, *target, size, NULL)) {
      goto exit;
   }

   succeeded = TRUE;

exit:
   if (!succeeded) {
      free(*target);
      *target = NULL;
   }

   return succeeded;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Base64_EasyDecode --
 *
 *      Base64-decode 'src' into a buffer.
 *
 * Results:
 *      TRUE on success, FALSE otherwise, plus the decoded data on success.
 *      Caller must free 'target' with free().
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Base64_EasyDecode(const char *src,   // IN: data to decode
                  uint8 **target,    // OUT: decoded data
                  size_t *targSize)  // OUT: data size
{
   Bool succeeded = FALSE;
   size_t theDataSize;
   uint8 *theData;

   ASSERT(src);
   ASSERT(target);
   ASSERT(targSize);

   theDataSize = Base64_DecodedLength(src, strlen(src));

   theData = malloc(theDataSize);

   if (!theData) {
      goto exit;
   }

   if (!Base64_Decode(src, theData, theDataSize, &theDataSize)) {
      free(theData);
      goto exit;
   }

   *target = theData;
   *targSize = theDataSize;

   succeeded = TRUE;

exit:
   if (!succeeded) {
      *target = NULL;
      *targSize = 0;
   }

   return succeeded;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Base64_DecodeFixed --
 *
 *      Base64-decode 'src' into a preallocated, fixed sized buffer.
 *
 * Results:
 *      TRUE  outBuf is populated with the decoded string
 *      FALSE Decoding or memory allocation failed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Base64_DecodeFixed(const char *src,    // IN: data to decode
                   char *outBuf,       // OUT: decoded data
                   size_t outBufSize)  // IN: data size
{
   Bool success;
   uint8 *theData;
   size_t theDataSize;

   ASSERT(src);
   ASSERT(outBuf);

   success = Base64_EasyDecode(src, &theData, &theDataSize);

   if (success) {
      success = (theDataSize <= outBufSize);

      if (success) {
         memcpy(outBuf, theData, theDataSize);
      }

      free(theData);
   }

   return success;
}
 07070100000184000081A4000000000000000000000001682255050000AC89000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/codeset.c /* **********************************************************
 * Copyright (C) 1998-2020 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * codeset.c --
 *
 *    Character set and encoding conversion functions, using ICU.
 *
 *
 *      Some definitions borrow from header files from the ICU 1.8.1
 *      library.
 *
 *      ICU 1.8.1 license follows:
 *
 *      ICU License - ICU 1.8.1 and later
 *
 *      COPYRIGHT AND PERMISSION NOTICE
 *
 *      Copyright (c) 1995-2006 International Business Machines Corporation
 *      and others
 *
 *      All rights reserved.
 *
 *           Permission is hereby granted, free of charge, to any
 *      person obtaining a copy of this software and associated
 *      documentation files (the "Software"), to deal in the Software
 *      without restriction, including without limitation the rights
 *      to use, copy, modify, merge, publish, distribute, and/or sell
 *      copies of the Software, and to permit persons to whom the
 *      Software is furnished to do so, provided that the above
 *      copyright notice(s) and this permission notice appear in all
 *      copies of the Software and that both the above copyright
 *      notice(s) and this permission notice appear in supporting
 *      documentation.
 *
 *           THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
 *      KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 *      WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 *      PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT
 *      SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
 *      BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR
 *      CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
 *      FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 *      CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *      OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *           Except as contained in this notice, the name of a
 *      copyright holder shall not be used in advertising or otherwise
 *      to promote the sale, use or other dealings in this Software
 *      without prior written authorization of the copyright holder.
 */

#if defined(_WIN32)
#   include <windows.h>
#   include <malloc.h>
#   include <str.h>
#   include "windowsUtil.h"
#else
#   define _GNU_SOURCE
#   include <string.h>
#   include <stdlib.h>
#   include <errno.h>
#   include <su.h>
#   include <sys/stat.h>
#   include "hostinfo.h"
#   include "hostType.h"
#endif

#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif

#include <stdio.h>
#include "vmware.h"
#include "vm_product.h"
#include "vm_atomic.h"
#if !defined(NO_ICU)
#  include "unicode/ucnv.h"
#  include "unicode/udata.h"
#  include "unicode/putil.h"
#endif
#include "file.h"
#include "util.h"
#include "codeset.h"
#include "codesetOld.h"
#include "str.h"
#if defined __APPLE__
#   define LOCATION_WEAK
#   include "location.h"
#endif

/*
 * Macros
 */

#define CODESET_CAN_FALLBACK_ON_NON_ICU TRUE

#if defined __APPLE__
#   define POSIX_ICU_DIR DEFAULT_LIBDIRECTORY
#elif !defined _WIN32
#   if defined(VMX86_TOOLS)
#      define POSIX_ICU_DIR "/etc/vmware-tools"
#   else
#      define POSIX_ICU_DIR "/etc/vmware"
#   endif
#endif

/*
 * XXX These should be passed in from the build system,
 * but I don't have time to deal with bora-vmsoft.  -- edward
 */

#define ICU_DATA_FILE "icudt44l.dat"
#ifdef _WIN32
#define ICU_DATA_ITEM "icudt44l"
#define ICU_DATA_FILE_W XCONC(L, ICU_DATA_FILE)
#endif

#ifdef VMX86_DEVEL
#ifdef _WIN32
#define ICU_DATA_FILE_DIR "%TCROOT%/noarch/icu-data-4.4-1"
#define ICU_DATA_FILE_DIR_W XCONC(L, ICU_DATA_FILE_DIR)
#define ICU_DATA_FILE_PATH ICU_DATA_FILE_DIR_W DIRSEPS_W ICU_DATA_FILE_W
#else
#define ICU_DATA_FILE_DIR "/build/toolchain/noarch/icu-data-4.4-1"
#define ICU_DATA_FILE_PATH ICU_DATA_FILE_DIR DIRSEPS ICU_DATA_FILE
#endif
#endif

/*
 * Variables
 */

static Bool dontUseIcu = TRUE;


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_GetCurrentCodeSet --
 *
 *    Return native code set name. Always calls down to
 *    CodeSetOld_GetCurrentCodeSet. See there for more details.
 *
 * Results:
 *    See CodeSetOld_GetCurrentCodeSet.
 *
 * Side effects:
 *    See CodeSetOld_GetCurrentCodeSet.
 *
 *-----------------------------------------------------------------------------
 */

const char *
CodeSet_GetCurrentCodeSet(void)
{
   return CodeSetOld_GetCurrentCodeSet();
}

#if !defined NO_ICU

#ifdef _WIN32

/*
 *----------------------------------------------------------------------------
 *
 * CodeSetGetModulePath --
 *
 *      Returns the wide-character current module path. We can't use
 *      Win32U_GetModuleFileName because it invokes codeset.c conversion
 *      routines.
 *
 * Returns:
 *      NULL, or a utf16 string (free with free).
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static utf16_t *
CodeSetGetModulePath(HANDLE hModule) // IN
{
   utf16_t *pathW = NULL;
   DWORD size = MAX_PATH;

   while (TRUE) {
      DWORD res;

      pathW = realloc(pathW, size * (sizeof *pathW));
      if (!pathW) {
         return NULL;
      }

      res = GetModuleFileNameW(hModule, pathW, size);

      if (res == 0) {
         /* fatal error */
         goto exit;
      } else if (res == size) {
         /*
          * The buffer is too small; double its size. The maximum path length
          * on Windows is ~64KB so doubling will not push a DWORD into
          * integer overflow before success or error due to another reason
          * will occur.
          */

         size *= 2;
      } else {
         /* success */
         break;
      }
   }

  exit:
   return pathW;
}


#elif vmx86_devel && !vmx86_server && !defined(TEST_CUSTOM_ICU_DATA_FILE) // _WIN32

/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetGetModulePath --
 *
 *	Retrieve the full path to the executable. Not supported under
 *	VMvisor. Based on HostInfo_GetModulePath, simplified for safe
 *	use before ICU is initialized.
 *
 * Results:
 *      On success: The allocated, NUL-terminated file path.
 *         Note: This path can be a symbolic or hard link; it's just one
 *         possible path to access the executable.
 *
 *      On failure: NULL.
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

static char *
CodeSetGetModulePath(uint32 priv)
{
   char path[PATH_MAX];
   Bool ret = FALSE;
#if defined(__APPLE__)
   uint32_t size;
#else
   ssize_t size;
   uid_t uid = -1;
#endif

   if ((priv != HGMP_PRIVILEGE) && (priv != HGMP_NO_PRIVILEGE)) {
      return NULL;
   }

#if defined(__APPLE__)
   size = sizeof path;
   if (_NSGetExecutablePath(path, &size)) {
      goto exit;
   }

#else
#if defined(VMX86_SERVER)
   if (HostType_OSIsVMK()) {
      goto exit;
   }
#endif

   if (priv == HGMP_PRIVILEGE) {
      uid = Id_BeginSuperUser();
   }

   size = readlink("/proc/self/exe", path, sizeof path - 1);
   if (size == -1) {
      if (priv == HGMP_PRIVILEGE) {
         Id_EndSuperUser(uid);
      }
      goto exit;
   }

   path[size] = '\0';

   if (priv == HGMP_PRIVILEGE) {
      Id_EndSuperUser(uid);
   }
#endif

   ret = TRUE;

  exit:
   if (ret) {
      return strdup(path);
   } else {
      return NULL;
   }
}

#endif // _WIN32

#endif // !NO_ICU


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_GetAltPathName --
 *
 *	The Unicode path is probably not compatible in the current encoding.
 *	Try to convert the path into a short name so it may work with
 *      local encoding.
 *
 *      XXX -- this function is a temporary fix. It should be removed once
 *             we fix the 3rd party library pathname issue.
 *
 * Results:
 *      NULL, or a char string (free with free).
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
CodeSet_GetAltPathName(const utf16_t *pathW) // IN
{
   char *path = NULL;
#if defined(_WIN32) && !defined(VM_WIN_UWP)
   DWORD res;
   utf16_t shortPathW[_MAX_PATH];

   ASSERT(pathW);

   res = GetShortPathNameW(pathW, shortPathW, ARRAYSIZE(shortPathW));

   if ((res == 0) || (res >= ARRAYSIZE(shortPathW))) {
      goto exit;
   }

   if (!CodeSetOld_Utf16leToCurrent((const char *)shortPathW,
                                    wcslen(shortPathW) * sizeof *shortPathW,
                                    &path, NULL)) {
      goto exit;
   }

  exit:
#endif // _WIN32

   return path;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_DontUseIcu --
 *
 *    Tell codeset not to load or use ICU (or stop using it if it's
 *    already loaded). Codeset will fall back on codesetOld.c, which
 *    relies on system internationalization APIs (and may have more
 *    limited functionality). Not all APIs have a fallback, however
 *    (namely GenericToGeneric).
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    See above
 *
 *-----------------------------------------------------------------------------
 */

void
CodeSet_DontUseIcu(void)
{
   dontUseIcu = TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Init --
 *
 *    Looks for ICU's data file in some platform-specific
 *    directory. If present, inits ICU by feeding it the path to that
 *    directory. If already inited, returns the current state (init
 *    failed/succeeded).
 *
 *    For Windows, CodeSet_Init is thread-safe (with critical section).
 *    For Linux/Apple, call while single-threaded.
 *
 *    *********** WARNING ***********
 *    Do not call CodeSet_Init directly, it is called already by
 *    Unicode_Init. Lots of code depends on codeset. Please call
 *    Unicode_Init as early as possible.
 *    *******************************
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    See above
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Init(const char *icuDataDir) // IN: ICU data file location in Current code page.
                                     //     Default is used if NULL.
{
#ifdef NO_ICU
   /* Nothing required if not using ICU. */
   return TRUE;
#else // NO_ICU
   DynBuf dbpath;
#ifdef _WIN32
   DWORD attribs;
   utf16_t *modPath = NULL;
   utf16_t *lastSlash;
   utf16_t *wpath;
   HANDLE hFile = INVALID_HANDLE_VALUE;
   HANDLE hMapping = NULL;
   void *memMappedData = NULL;
#else
   struct stat finfo;
#endif
   char *path = NULL;
   Bool ret = FALSE;

   DynBuf_Init(&dbpath);

#ifdef USE_ICU
   /*
    * We're using system ICU, which finds its own data. So nothing to
    * do here.
    */
   dontUseIcu = FALSE;
   ret = TRUE;
   goto exit;
#endif

  /*
   * ********************* WARNING
   * Must avoid recursive calls into the codeset library here, hence
   * the idiotic hoop-jumping. DO NOT change any of these calls to
   * wrapper equivalents or call any other functions that may perform
   * string conversion.
   * ********************* WARNING
   */

#ifdef _WIN32 // {

#if vmx86_devel && !defined(TEST_CUSTOM_ICU_DATA_FILE)
   /*
    * Devel builds use toolchain directory first.
    */
   {
      WCHAR icuFilePath[MAX_PATH] = { 0 };
      DWORD n = ExpandEnvironmentStringsW(ICU_DATA_FILE_PATH,
                                          icuFilePath, ARRAYSIZE(icuFilePath));
      if (n > 0 && n < ARRAYSIZE(icuFilePath)) {
         attribs = GetFileAttributesW(icuFilePath);
         if ((INVALID_FILE_ATTRIBUTES != attribs) ||
             (attribs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
            if (!CodeSetOld_Utf16leToCurrent((const char *) icuFilePath,
                                             n * sizeof *icuFilePath,
                                             &path, NULL)) {
               goto exit;
            }
            goto found;
         }
      }
   }
#endif

   if (icuDataDir) {
      /*
       * Data file must be in the specified directory.
       */
      size_t length = strlen(icuDataDir);

      if (!DynBuf_Append(&dbpath, icuDataDir, length)) {
         goto exit;
      }
      if (length && icuDataDir[length - 1] != DIRSEPC) {
         if (!DynBuf_Append(&dbpath, DIRSEPS, strlen(DIRSEPS))) {
            goto exit;
         }
      }
      if (!DynBuf_Append(&dbpath, ICU_DATA_FILE, strlen(ICU_DATA_FILE)) ||
          !DynBuf_Append(&dbpath, "\0", 1)) {
         goto exit;
      }

      /*
       * Check for file existence.
       */
      attribs = GetFileAttributesA(DynBuf_Get(&dbpath));

      if ((INVALID_FILE_ATTRIBUTES == attribs) ||
          (attribs & FILE_ATTRIBUTE_DIRECTORY)) {
         goto exit;
      }

      path = (char *) DynBuf_Detach(&dbpath);
   } else {
      /*
       * Data file must be in the directory of the current module
       * (i.e. the module that contains CodeSet_Init()).
       */
      HMODULE hModule = W32Util_GetModuleByAddress((void *) CodeSet_Init);
      if (!hModule) {
         goto exit;
      }

      modPath = CodeSetGetModulePath(hModule);
      if (!modPath) {
         goto exit;
      }

      lastSlash = wcsrchr(modPath, DIRSEPC_W);
      if (!lastSlash) {
         goto exit;
      }

      *lastSlash = L'\0';

      if (!DynBuf_Append(&dbpath, modPath,
                         wcslen(modPath) * sizeof(utf16_t)) ||
          !DynBuf_Append(&dbpath, DIRSEPS_W,
                         wcslen(DIRSEPS_W) * sizeof(utf16_t)) ||
          !DynBuf_Append(&dbpath, ICU_DATA_FILE_W,
                         wcslen(ICU_DATA_FILE_W) * sizeof(utf16_t)) ||
          !DynBuf_Append(&dbpath, L"\0", 2)) {
         goto exit;
      }

      /*
       * Since u_setDataDirectory can't handle UTF-16, we would have to
       * now convert this path to local encoding. But that fails when
       * the module is in a path containing characters not in the
       * local encoding (see 282524). So we'll memory-map the file
       * instead and call udata_setCommonData() below.
       */
      wpath = (utf16_t *) DynBuf_Get(&dbpath);
      hFile = CreateFileW(wpath, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0,
                          NULL);
      if (INVALID_HANDLE_VALUE == hFile) {
         goto exit;
      }
      hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
      if (hMapping == NULL) {
         goto exit;
      }
      memMappedData = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
      if (memMappedData == NULL) {
         goto exit;
      }
   }

#else // } _WIN32 {

#if vmx86_devel && !vmx86_server && !defined(TEST_CUSTOM_ICU_DATA_FILE)
   {
      char *modPath;
      char *lastSlash;

      /*
       * Devel builds use toolchain directory first.
       */
      if (stat(ICU_DATA_FILE_PATH, &finfo) >= 0 && !S_ISDIR(finfo.st_mode)) {
         if ((path = strdup(ICU_DATA_FILE_PATH)) == NULL) {
            goto exit;
         }
         goto found;
      }

      /*
       * Then we try module directory, if we can get it.
       */
      modPath = CodeSetGetModulePath(HGMP_PRIVILEGE);
      if (modPath) {
         lastSlash = strrchr(modPath, DIRSEPC);
         if (lastSlash) {
            *lastSlash = '\0';

            if (DynBuf_Append(&dbpath, modPath, strlen(modPath)) &&
                DynBuf_Append(&dbpath, DIRSEPS, strlen(DIRSEPS)) &&
                DynBuf_Append(&dbpath, ICU_DATA_FILE,
                              strlen(ICU_DATA_FILE)) &&
                DynBuf_Append(&dbpath, "\0", 1)) {

               if ((stat((const char *) DynBuf_Get(&dbpath), &finfo) >= 0) &&
                   !S_ISDIR(finfo.st_mode)) {
                  free(modPath);
                  path = DynBuf_Detach(&dbpath);
                  goto found;
               } else {
                  DynBuf_SetSize(&dbpath, 0);
               }
            }
         }

         free(modPath);
      }
   }
#endif // vmx86_devel

   if (icuDataDir) {
      /* Use the caller-specified ICU data dir. */
      if (!DynBuf_Append(&dbpath, icuDataDir, strlen(icuDataDir))) {
         goto exit;
      }
   } else {
      /* Use a default ICU data dir. */
#   if defined __APPLE__
      Location_Get_Type *Location_Get = Location_Get_Addr();

      if (Location_Get) {
         char *icuDir = Location_Get("icuDir");
         Bool success =    icuDir
                        && DynBuf_Append(&dbpath, icuDir, strlen(icuDir));

         free(icuDir);
         if (!success) {
            goto exit;
         }
      } else
#   endif

      {
         if (!DynBuf_Append(&dbpath, POSIX_ICU_DIR, strlen(POSIX_ICU_DIR)) ||
             !DynBuf_Append(&dbpath, "/icu", strlen("/icu"))) {
            goto exit;
         }
      }
   }
   if (!DynBuf_Append(&dbpath, DIRSEPS, strlen(DIRSEPS)) ||
       !DynBuf_Append(&dbpath, ICU_DATA_FILE, strlen(ICU_DATA_FILE)) ||
       !DynBuf_Append(&dbpath, "\0", 1)) {
      goto exit;
   }

   /*
    * Check for file existence. (DO NOT CHANGE TO 'stat' WRAPPER).
    */
   path = (char *) DynBuf_Detach(&dbpath);
   if (stat(path, &finfo) < 0 || S_ISDIR(finfo.st_mode)) {
      goto exit;
   }

#endif // } _WIN32

#if vmx86_devel && !vmx86_server && !defined(TEST_CUSTOM_ICU_DATA_FILE)
found:
#endif

#ifdef _WIN32
   if (memMappedData) {
      /*
       * Tell ICU to use this mapped data.
       */
      UErrorCode uerr = U_ZERO_ERROR;
      ASSERT(memMappedData);

      udata_setCommonData(memMappedData, &uerr);
      if (U_FAILURE(uerr)) {
         UnmapViewOfFile(memMappedData);
         goto exit;
      }
      udata_setAppData(ICU_DATA_ITEM, memMappedData, &uerr);
      if (U_FAILURE(uerr)) {
         UnmapViewOfFile(memMappedData);
         goto exit;
      }
   } else {
#endif
      /*
       * Tell ICU to use this directory.
       */
      u_setDataDirectory(path);
#ifdef _WIN32
   }
#endif

   dontUseIcu = FALSE;
   ret = TRUE;

  exit:
   if (!ret) {
      /*
       * There was an error initing ICU, but if we can fall back on
       * non-ICU (old CodeSet) then things are OK.
       */
      if (CODESET_CAN_FALLBACK_ON_NON_ICU) {
         ret = TRUE;
         dontUseIcu = TRUE;

#ifdef _WIN32
         OutputDebugStringW(L"CodeSet_Init: no ICU\n");
#endif
      }
   }

#ifdef _WIN32
   free(modPath);
   if (hMapping) {
      CloseHandle(hMapping);
   }
   if (hFile != INVALID_HANDLE_VALUE) {
      CloseHandle(hFile);
   }
#endif
   free(path);
   DynBuf_Destroy(&dbpath);

   return ret;
#endif
}


#if defined(CURRENT_IS_UTF8)

/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetDuplicateUtf8Str --
 *
 *    Duplicate UTF-8 string, appending zero terminator to its end.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetDuplicateUtf8Str(const char *bufIn,  // IN: Input string
                        size_t sizeIn,      // IN: Input string length
                        char **bufOut,      // OUT: "Converted" string
                        size_t *sizeOut)    // OUT/OPT: Length of string
{
   char *myBufOut;
   size_t newSize = sizeIn + sizeof *myBufOut;

   if (newSize < sizeIn) {   // Prevent integer overflow
      return FALSE;
   }

   myBufOut = malloc(newSize);
   if (myBufOut == NULL) {
      return FALSE;
   }

   memcpy(myBufOut, bufIn, sizeIn);
   myBufOut[sizeIn] = '\0';

   *bufOut = myBufOut;
   if (sizeOut) {
      *sizeOut = sizeIn;
   }
   return TRUE;
}

#endif // defined(CURRENT_IS_UTF8)


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetDynBufFinalize --
 *
 *    Append NUL terminator to the buffer, and return pointer to
 *    buffer and its data size (before appending terminator). Destroys
 *    buffer on failure.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetDynBufFinalize(Bool ok,          // IN: Earlier steps succeeded
                      DynBuf *db,       // IN: Buffer with converted string
                      char **bufOut,    // OUT: Converted string
                      size_t *sizeOut)  // OUT/OPT: Length of string in bytes
{
   /*
    * NUL can be as long as 4 bytes if UTF-32, make no assumptions.
    */
   if (!ok || !DynBuf_Append(db, "\0\0\0\0", 4) || !DynBuf_Trim(db)) {
      DynBuf_Destroy(db);
      return FALSE;
   }

   *bufOut = DynBuf_Get(db);
   if (sizeOut) {
      *sizeOut = DynBuf_GetSize(db) - 4;
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetUtf8ToUtf16le --
 *
 *    Append the content of a buffer (that uses the UTF-8 encoding) to a
 *    DynBuf (that uses the UTF-16LE encoding)
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetUtf8ToUtf16le(const char *bufIn,  // IN
                     size_t sizeIn,      // IN
                     DynBuf *db)         // IN
{
   return CodeSet_GenericToGenericDb("UTF-8", bufIn, sizeIn, "UTF-16LE", 0,
                                     db);
}


#if defined(__APPLE__)

/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8Normalize --
 *
 *    Calls down to CodeSetOld_Utf8Normalize.
 *
 * Results:
 *    See CodeSetOld_Utf8Normalize.
 *
 * Side effects:
 *    See CodeSetOld_Utf8Normalize.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf8Normalize(const char *bufIn,     // IN
                      size_t sizeIn,         // IN
                      Bool precomposed,      // IN
                      DynBuf *db)            // OUT
{
   return CodeSetOld_Utf8Normalize(bufIn, sizeIn, precomposed, db);
}

#endif /* defined(__APPLE__) */


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_GenericToGenericDb --
 *
 *    Append the content of a buffer (that uses the specified encoding) to a
 *    DynBuf (that uses the specified encoding).
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    String (sans NUL-termination) is appended to db.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_GenericToGenericDb(const char *codeIn,  // IN
                           const char *bufIn,   // IN
                           size_t sizeIn,       // IN
                           const char *codeOut, // IN
                           unsigned int flags,  // IN
                           DynBuf *db)          // IN/OUT
{
#if defined(NO_ICU)
   return CodeSetOld_GenericToGenericDb(codeIn, bufIn, sizeIn, codeOut,
                                        flags, db);
#else
   Bool result = FALSE;
   UErrorCode uerr;
   const char *bufInCur;
   const char *bufInEnd;
   UChar bufPiv[1024];
   UChar *bufPivSource;
   UChar *bufPivTarget;
   UChar *bufPivEnd;
   char *bufOut;
   char *bufOutCur;
   char *bufOutEnd;
   size_t newSize;
   size_t bufOutSize;
   size_t bufOutOffset;
   UConverter *cvin = NULL;
   UConverter *cvout = NULL;
   UConverterToUCallback toUCb;
   UConverterFromUCallback fromUCb;

   ASSERT(codeIn);
   ASSERT(sizeIn == 0 || bufIn);
   ASSERT(codeOut);
   ASSERT(db);
   ASSERT((CSGTG_NORMAL == flags) || (CSGTG_TRANSLIT == flags) ||
          (CSGTG_IGNORE == flags));

   if (dontUseIcu) {
      // fall back
      return CodeSetOld_GenericToGenericDb(codeIn, bufIn, sizeIn, codeOut,
                                           flags, db);
   }

   /*
    * Trivial case.
    */

   if ((sizeIn == 0) || (bufIn == NULL)) {
      result = TRUE;
      goto exit;
   }

   /*
    * Open converters.
    */

   uerr = U_ZERO_ERROR;
   cvin = ucnv_open(codeIn, &uerr);
   if (!cvin) {
      goto exit;
   }

   uerr = U_ZERO_ERROR;
   cvout = ucnv_open(codeOut, &uerr);
   if (!cvout) {
      goto exit;
   }

   /*
    * Set callbacks according to flags.
    */

   switch (flags) {
   case CSGTG_NORMAL:
      toUCb = UCNV_TO_U_CALLBACK_STOP;
      fromUCb = UCNV_FROM_U_CALLBACK_STOP;
      break;

   case CSGTG_TRANSLIT:
      toUCb = UCNV_TO_U_CALLBACK_SUBSTITUTE;
      fromUCb = UCNV_FROM_U_CALLBACK_SUBSTITUTE;
      break;

   case CSGTG_IGNORE:
      toUCb = UCNV_TO_U_CALLBACK_SKIP;
      fromUCb = UCNV_FROM_U_CALLBACK_SKIP;
      break;

   default:
      NOT_IMPLEMENTED();
      break;
   }

   uerr = U_ZERO_ERROR;
   ucnv_setToUCallBack(cvin, toUCb, NULL, NULL, NULL, &uerr);
   if (U_FAILURE(uerr)) {
      goto exit;
   }

   uerr = U_ZERO_ERROR;
   ucnv_setFromUCallBack(cvout, fromUCb, NULL, NULL, NULL, &uerr);
   if (U_FAILURE(uerr)) {
      goto exit;
   }

   /*
    * Convert using ucnv_convertEx().
    * As a starting guess, make the output buffer the same size as
    * the input string (with a fudge constant added in to avoid degen
    * cases).
    */

   bufInCur = bufIn;
   bufInEnd = bufIn + sizeIn;
   newSize = sizeIn + 4;
   if (newSize < sizeIn) {  // Prevent integer overflow
      goto exit;
   }
   bufOutSize = newSize;
   bufOutOffset = 0;
   bufPivSource = bufPiv;
   bufPivTarget = bufPiv;
   bufPivEnd = bufPiv + ARRAYSIZE(bufPiv);

   for (;;) {
      if (!DynBuf_Enlarge(db, bufOutSize)) {
         goto exit;
      }
      bufOut = DynBuf_Get(db);
      bufOutCur = bufOut + bufOutOffset;
      bufOutSize = DynBuf_GetAllocatedSize(db);
      bufOutEnd = bufOut + bufOutSize;

      uerr = U_ZERO_ERROR;
      ucnv_convertEx(cvout, cvin, &bufOutCur, bufOutEnd,
		     &bufInCur, bufInEnd,
		     bufPiv, &bufPivSource, &bufPivTarget, bufPivEnd,
		     FALSE, TRUE, &uerr);

      if (U_SUCCESS(uerr)) {
         /*
          * "This was a triumph. I'm making a note here: HUGE SUCCESS. It's
          * hard to overstate my satisfaction."
          */

         break;
      }

      if (U_BUFFER_OVERFLOW_ERROR != uerr) {
	 // failure
         goto exit;
      }

      /*
       * Our guess at 'bufOutSize' was obviously wrong, just double it.
       * We'll be reallocating bufOut, so will need to recompute bufOutCur
       * based on bufOutOffset.
       */

      newSize = 2 * bufOutSize;

      /*
       * Prevent integer overflow. We can use this form of checking
       * specifically because a multiple by 2 is used. This type of checking
       * does not work in the general case.
       */

      if (newSize < bufOutSize) {
         goto exit;
      }

      bufOutSize = newSize;
      bufOutOffset = bufOutCur - bufOut;
   }

   /*
    * Set final size and return.
    */

   DynBuf_SetSize(db, bufOutCur - bufOut);

   result = TRUE;

  exit:
   if (cvin) {
      ucnv_close(cvin);
   }

   if (cvout) {
      ucnv_close(cvout);
   }

   return result;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_GenericToGeneric --
 *
 *    Non-db version of CodeSet_GenericToGenericDb.
 *
 * Results:
 *    TRUE on success, plus allocated string
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_GenericToGeneric(const char *codeIn,  // IN
                         const char *bufIn,   // IN
                         size_t sizeIn,       // IN
                         const char *codeOut, // IN
                         unsigned int flags,  // IN
                         char **bufOut,       // OUT
                         size_t *sizeOut)     // OUT/OPT
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSet_GenericToGenericDb(codeIn, bufIn, sizeIn, codeOut, flags, &db);
   return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 * Generic remarks: here is my understanding of those terms as of 2001/12/27:
 *
 * BOM
 *    Byte-Order Mark
 *
 * BMP
 *    Basic Multilingual Plane. This plane comprises the first 2^16 code
 *    positions of ISO/IEC 10646's canonical code space
 *
 * UCS
 *    Universal Character Set. Encoding form specified by ISO/IEC 10646
 *
 * UCS-2
 *    Directly store all Unicode scalar value (code point) from U+0000 to
 *    U+FFFF on 2 bytes. Consequently, this representation can only hold
 *    characters in the BMP
 *
 * UCS-4
 *    Directly store a Unicode scalar value (code point) from U-00000000 to
 *    U-FFFFFFFF on 4 bytes
 *
 * UTF
 *    Abbreviation for Unicode (or UCS) Transformation Format
 *
 * UTF-8
 *    Unicode (or UCS) Transformation Format, 8-bit encoding form. UTF-8 is the
 *    Unicode Transformation Format that serializes a Unicode scalar value
 *    (code point) as a sequence of 1 to 6 bytes
 *
 * UTF-16
 *    UCS-2 + surrogate mechanism: allow to encode some non-BMP Unicode
 *    characters in a UCS-2 string, by using 2 2-byte units. See the Unicode
 *    standard, v2.0
 *
 * UTF-32
 *    Directly store all Unicode scalar value (code point) from U-00000000 to
 *    U-0010FFFF on 4 bytes. This is a subset of UCS-4
 *
 *   --hpreg
 */


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8ToCurrent --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding) into
 *    another buffer (that uses the current encoding).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf8ToCurrent(const char *bufIn,  // IN
                      size_t sizeIn,      // IN
                      char **bufOut,      // OUT
                      size_t *sizeOut)    // OUT/OPT
{
#if !defined(CURRENT_IS_UTF8)
   DynBuf db;
   Bool ok;
#endif

   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_Utf8ToCurrent(bufIn, sizeIn, bufOut, sizeOut);
   }

#if defined(CURRENT_IS_UTF8)
   return CodeSetDuplicateUtf8Str(bufIn, sizeIn, bufOut, sizeOut);
#else
   DynBuf_Init(&db);
   ok = CodeSet_GenericToGenericDb("UTF-8", bufIn, sizeIn,
                                   CodeSet_GetCurrentCodeSet(), 0, &db);
   return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_CurrentToUtf8 --
 *
 *    Convert the content of a buffer (that uses the current encoding) into
 *    another buffer (that uses the UTF-8 encoding).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_CurrentToUtf8(const char *bufIn,  // IN
                      size_t sizeIn,      // IN
                      char **bufOut,      // OUT
                      size_t *sizeOut)    // OUT/OPT
{
#if !defined(CURRENT_IS_UTF8)
   DynBuf db;
   Bool ok;
#endif

   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_CurrentToUtf8(bufIn, sizeIn, bufOut, sizeOut);
   }

#if defined(CURRENT_IS_UTF8)
   return CodeSetDuplicateUtf8Str(bufIn, sizeIn, bufOut, sizeOut);
#else
   DynBuf_Init(&db);
   ok = CodeSet_GenericToGenericDb(CodeSet_GetCurrentCodeSet(), bufIn, sizeIn,
                                   "UTF-8", 0, &db);
   return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf16leToUtf8Db --
 *
 *    Append the content of a buffer (that uses the UTF-16LE encoding) to a
 *    DynBuf (that uses the UTF-8 encoding).
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf16leToUtf8Db(const char *bufIn, // IN
                        size_t sizeIn,     // IN
                        DynBuf *db)        // IN
{
   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_Utf16leToUtf8Db(bufIn, sizeIn, db);
   }

   return CodeSet_GenericToGenericDb("UTF-16LE", bufIn, sizeIn, "UTF-8", 0,
                                     db);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf16leToUtf8 --
 *
 *    Convert the content of a buffer (that uses the UTF-16LE encoding) into
 *    another buffer (that uses the UTF-8 encoding).
 *
 *    The operation is inversible (its inverse is CodeSet_Utf8ToUtf16le).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf16leToUtf8(const char *bufIn,  // IN
                      size_t sizeIn,      // IN
                      char **bufOut,      // OUT
                      size_t *sizeOut)    // OUT/OPT
{
   DynBuf db;
   Bool ok;

   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_Utf16leToUtf8(bufIn, sizeIn, bufOut, sizeOut);
   }

   DynBuf_Init(&db);
   ok = CodeSet_Utf16leToUtf8Db(bufIn, sizeIn, &db);
   return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8ToUtf16le --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding) into
 *    another buffer (that uses the UTF-16LE encoding).
 *
 *    The operation is inversible (its inverse is CodeSet_Utf16leToUtf8).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf8ToUtf16le(const char *bufIn,  // IN
                      size_t sizeIn,      // IN
                      char **bufOut,      // OUT
                      size_t *sizeOut)    // OUT/OPT
{
   DynBuf db;
   Bool ok;

   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_Utf8ToUtf16le(bufIn, sizeIn, bufOut, sizeOut);
   }

   DynBuf_Init(&db);
   ok = CodeSetUtf8ToUtf16le(bufIn, sizeIn, &db);
   return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8FormDToUtf8FormC  --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding)
 *    which is in normal form D (decomposed) into another buffer
 *    (that uses the UTF-8 encoding) and is normalized as
 *    precomposed (Normalization Form C).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains a NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    '*bufOut' contains the allocated, NUL terminated buffer.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf8FormDToUtf8FormC(const char *bufIn,     // IN
                             size_t sizeIn,         // IN
                             char **bufOut,         // OUT
                             size_t *sizeOut)       // OUT/OPT
{
   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_Utf8FormDToUtf8FormC(bufIn, sizeIn, bufOut, sizeOut);
   }

#if defined(__APPLE__)
   {
      DynBuf db;
      Bool ok;
      DynBuf_Init(&db);
      ok = CodeSet_Utf8Normalize(bufIn, sizeIn, TRUE, &db);
      return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
   }
#else
   NOT_IMPLEMENTED();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8FormCToUtf8FormD  --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding)
 *    which is in normal form C (precomposed) into another buffer
 *    (that uses the UTF-8 encoding) and is normalized as
 *    decomposed (Normalization Form D).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains a NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    '*bufOut' contains the allocated, NUL terminated buffer.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf8FormCToUtf8FormD(const char *bufIn,     // IN
                             size_t sizeIn,         // IN
                             char **bufOut,         // OUT
                             size_t *sizeOut)       // OUT/OPT
{
   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_Utf8FormCToUtf8FormD(bufIn, sizeIn, bufOut, sizeOut);
   }

#if defined(__APPLE__)
   {
      DynBuf db;
      Bool ok;
      DynBuf_Init(&db);
      ok = CodeSet_Utf8Normalize(bufIn, sizeIn, FALSE, &db);
      return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
   }
#else
   NOT_IMPLEMENTED();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_CurrentToUtf16le --
 *
 *    Convert the content of a buffer (that uses the current encoding) into
 *    another buffer (that uses the UTF-16LE encoding).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_CurrentToUtf16le(const char *bufIn, // IN
                         size_t sizeIn,     // IN
                         char **bufOut,     // OUT
                         size_t *sizeOut)   // OUT/OPT
{
   DynBuf db;
   Bool ok;

   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_CurrentToUtf16le(bufIn, sizeIn, bufOut, sizeOut);
   }

   DynBuf_Init(&db);
   ok = CodeSet_GenericToGenericDb(CodeSet_GetCurrentCodeSet(), bufIn, sizeIn,
                                   "UTF-16LE", 0, &db);
   return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf16leToCurrent --
 *
 *    Convert the content of a buffer (that uses the UTF-16 little endian
 *    encoding) into another buffer (that uses the current encoding)
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf16leToCurrent(const char *bufIn,  // IN
                         size_t sizeIn,      // IN
                         char **bufOut,      // OUT
                         size_t *sizeOut)    // OUT/OPT
{
   DynBuf db;
   Bool ok;

   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_Utf16leToCurrent(bufIn, sizeIn, bufOut, sizeOut);
   }

   DynBuf_Init(&db);
   ok = CodeSet_GenericToGenericDb("UTF-16LE", bufIn, sizeIn,
                                   CodeSet_GetCurrentCodeSet(), 0, &db);
   return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf16beToCurrent --
 *
 *    Convert the content of a buffer (that uses the UTF-16 big endian
 *    encoding) into another buffer (that uses the current encoding)
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Utf16beToCurrent(const char *bufIn,  // IN
                         size_t sizeIn,      // IN
                         char **bufOut,      // OUT
                         size_t *sizeOut)    // OUT/OPT
{
   DynBuf db;
   Bool ok;

   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_Utf16beToCurrent(bufIn, sizeIn, bufOut, sizeOut);
   }

   DynBuf_Init(&db);
   ok = CodeSet_GenericToGenericDb("UTF-16BE", bufIn, sizeIn,
                                   CodeSet_GetCurrentCodeSet(), 0, &db);
   return CodeSetDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_IsEncodingSupported --
 *
 *    Ask ICU if it supports the specific encoding.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_IsEncodingSupported(const char *name) // IN
{
#if defined(NO_ICU)
   return CodeSetOld_IsEncodingSupported(name);
#else
   UConverter *cv;
   UErrorCode uerr;

   /*
    * Fallback if necessary.
    */
   if (dontUseIcu) {
      return CodeSetOld_IsEncodingSupported(name);
   }

   /*
    * Try to open the encoding.
    */
   uerr = U_ZERO_ERROR;
   cv = ucnv_open(name, &uerr);
   if (cv) {
      ucnv_close(cv);

      return TRUE;
   }

   return FALSE;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Validate --
 *
 *    Validate a string in the given encoding.
 *
 * Results:
 *    TRUE if string is valid,
 *    FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_Validate(const char *buf,   // IN: the string
                 size_t size,       // IN: length of string
                 const char *code)  // IN: encoding
{
#if defined(NO_ICU)
   return CodeSetOld_Validate(buf, size, code);
#else
   UConverter *cv;
   UErrorCode uerr;

   // ucnv_toUChars takes 32-bit int size
   VERIFY(size <= (size_t) MAX_INT32);

   if (size == 0) {
      return TRUE;
   }

   /*
    * Fallback if necessary.
    */

   if (dontUseIcu) {
      return CodeSetOld_Validate(buf, size, code);
   }

   /*
    * Calling ucnv_toUChars() this way is the idiom to precompute
    * the length of the output.  (See preflighting in the ICU User Guide.)
    * So if the error is not U_BUFFER_OVERFLOW_ERROR, then the input
    * is bad.
    */

   uerr = U_ZERO_ERROR;
   cv = ucnv_open(code, &uerr);
   VERIFY(U_SUCCESS(uerr));
   ucnv_setToUCallBack(cv, UCNV_TO_U_CALLBACK_STOP, NULL, NULL, NULL, &uerr);
   VERIFY(U_SUCCESS(uerr));
   ucnv_toUChars(cv, NULL, 0, buf, size, &uerr);
   ucnv_close(cv);

   return uerr == U_BUFFER_OVERFLOW_ERROR;
#endif
}

   07070100000185000081A4000000000000000000000001682255050000239D000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/codesetBase.c /*********************************************************
 * Copyright (C) 2010-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * codesetBase.c --
 *
 *    Character set and encoding conversion functions - unentangled from ICU,
 *    Unicode, codesetOld or other dependencies. Routines here can be used
 *    "anywhere" without fear of linking entanglements.
 */

#include <stdlib.h>
#include "vmware.h"
#include "codeset.h"
#include "util.h"


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_GetUtf8 --
 *
 *      Parse the next UTF-8 sequence.
 *
 * Results:
 *      0 on failure.
 *      Length of sequence and Unicode character in *uchar on success.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
CodeSet_GetUtf8(const char *string,  // IN: string
                const char *end,     // IN: end of string
                uint32 *uchar)       // OUT/OPT: the Unicode character
{
   uint8 *p = (uint8 *) string;
   uint8 *e;
   uint32 c;
   int len;

   ASSERT(string < end);

   c = *p;

   if (c < 0x80) {
      // ASCII: U+0000 - U+007F: 1 byte of UTF-8.
      len = 1;
      goto out;
   }

   if ((c < 0xC2) || (c > 0xF4)) {
      // 0x81 to 0xBF are not valid first bytes
      // 0xC0 and 0xC1 cannot appear in UTF-8, see below
      // leading char cannot be > 0xF4, illegal as well
      return 0;
   }

   if (c < 0xE0) {
      // U+0080 - U+07FF: 2 bytes of UTF-8.
      c -= 0xC0;
      len = 2;
   } else if (c < 0xF0) {
      // U+0800 - U+FFFF: 3 bytes of UTF-8.
      c -= 0xE0;
      len = 3;
   } else {
      // U+10000 - U+10FFFF: 4 bytes of UTF-8.
      c -= 0xF0;
      len = 4;
   }

   if ((e = p + len) > (uint8 *) end) {
      // input too short
      return 0;
   }

   while (++p < e) {
      if ((*p & 0xC0) != 0x80) {
         // bad trailing byte
         return 0;
      }
      c <<= 6;
      c += *p - 0x80;
   }

   /*
    * Enforce shortest encoding.
    * UTF-8 mandates that shortest possible encoding is used,
    * as otherwise doing UTF-8 => anything => UTF-8 could bypass some
    * important tests, like '/' for path separator or \0 for string
    * termination.
    *
    * This test does not work for len == 2, but that case is handled
    * by requiring the first byte to be 0xC2 or greater (see above).
    */

   if (c < 1U << (len * 5 - 4)) {
      return 0;
   }

out:
   if (uchar != NULL) {
      *uchar = c;
   }

   return len;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_LengthInCodePoints --
 *
 *    Return the length of a UTF8 string in code points (the number of
 *    unicode characters present in the string, not the length of the
 *    string in bytes).
 *
 *    Like strlen, the length returned does not include the terminating NUL.
 *
 * Results:
 *    -1 on error
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

int
CodeSet_LengthInCodePoints(const char *utf8)  // IN:
{
   char *p;
   char *end;
   uint32 codePoints = 0;

   ASSERT(utf8 != NULL);

   p = (char *) utf8;
   end = p + strlen(utf8);

   while (p < end) {
      uint32 len = CodeSet_GetUtf8(p, end, NULL);

      if (len == 0) {
         return -1;
      }

      p += len;
      codePoints++;
   }

   return codePoints;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_CodePointOffsetToByteOffset --
 *
 *    Return the byte offset of the character at the given codepoint
 *    offset.
 *
 * Results:
 *    -1 on error
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

int
CodeSet_CodePointOffsetToByteOffset(const char *utf8,    // IN:
                                    int codePointOffset) // IN:
{
   const char *p;
   const char *end;

   ASSERT(utf8 != NULL);

   p = utf8;
   end = p + strlen(utf8);

   while (p < end && codePointOffset > 0) {
      uint32 utf32;
      uint32 len = CodeSet_GetUtf8(p, end, &utf32);

      if (len == 0) {
         return -1;
      }

      p += len;
      codePointOffset--;
   }

   if (codePointOffset == 0) {
      return p - utf8;
   } else {
      return -1;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_UTF8ToUTF32 --
 *
 *    Convert a UTF8 string into a UTF32 string. The result is returned as a
 *    dynamically allocated string that the caller is responsible for.
 *
 * Results:
 *    TRUE   Input string was valid, converted string in *utf32
 *    FALSE  Input string was invalid or internal error
 *
 * Side effects:
 *    Allocates memory
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSet_UTF8ToUTF32(const char *utf8,  // IN:
                    char **utf32)      // OUT:
{
   char *p;
   char *end;
   uint32 *ptr;
   int codePoints;

   ASSERT(utf32 != NULL);

   if (utf8 == NULL) {  // NULL is not an error
      *utf32 = NULL;

      return TRUE;
   }

   codePoints = CodeSet_LengthInCodePoints(utf8);
   if (codePoints == -1) {
      *utf32 = NULL;

      return FALSE;
   }

   p = (char *) utf8;
   end = p + strlen(utf8);

   ptr = Util_SafeMalloc(sizeof *ptr * (codePoints + 1));
   *utf32 = (char *) ptr;

   while (p < end) {
      p += CodeSet_GetUtf8(p, end, ptr++);
   }

   *ptr = 0;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_UTF32ToUTF8 --
 *
 *    Convert a UTF32 string into a UTF8 string. The result is returned as a
 *    dynamically allocated string that the caller is responsible for.
 *
 * Results:
 *    TRUE   Input string was valid, converted string in *utf8
 *    FALSE  Input string was invalid or internal error
 *
 * Side effects:
 *    Allocates memory
 *
 *-----------------------------------------------------------------------------
 */


Bool
CodeSet_UTF32ToUTF8(const char *utf32,  // IN:
                    char **utf8)        // OUT:
{
   uint32 i;
   uint8 *p;
   uint8 *q;
   uint32 len;
   union {
      uint32  word;
      uint8   bytes[4];
   } value;

   ASSERT(utf8 != NULL);

   if (utf32 == NULL) {  // NULL is not an error
      *utf8 = NULL;

      return TRUE;
   }

   /*
    * Determine the length of the UTF32 string. A UTF32 string terminates
    * with four (4) bytes of zero (0).
    */

   len = 0;
   p = (uint8 *) utf32;

   while (TRUE) {
      value.bytes[0] = *p++;
      value.bytes[1] = *p++;
      value.bytes[2] = *p++;
      value.bytes[3] = *p++;

      if (value.word == 0) {
         break;
      }

      len++;
   }

   /*
    * Now that we know the length, allocate the memory for the UTF8 string.
    * The UTF8 string length calculation ensures that there will always be
    * sufficient space to represent the UTF32 string. Most of the time this
    * will involved allocating too much memory however the memory wastage
    * will be very short lived and very small.
    */

   *utf8 = Util_SafeMalloc((4 * len) + 1);  // cover the NUL byte

   /*
    * Process the UTF32 string, converting each code point into its
    * UTF8 equivalent.
    */

   p = (uint8 *) utf32;
   q = (uint8 *) *utf8;

   for (i = 0; i < len; i++) {
      value.bytes[0] = *p++;
      value.bytes[1] = *p++;
      value.bytes[2] = *p++;
      value.bytes[3] = *p++;

      if (value.word < 0x80) {                      // One byte case (ASCII)
         *q++ = value.word;
      } else if (value.word < 0x800) {              // Two byte case
         *q++ = 0xC0 | (value.word >> 6);
         *q++ = 0x80 | (value.word & 0x3F);
      } else if (value.word < 0x10000) {            // Three byte case
         *q++ = 0xE0 | (value.word >> 12);
         *q++ = 0x80 | ((value.word >> 6) & 0x3F);
         *q++ = 0x80 | (value.word & 0x3F);
      } else if (value.word < 0x110000) {           // Four byte case
         *q++ = 0xF0 | (value.word >> 18);
         *q++ = 0x80 | ((value.word >> 12) & 0x3F);
         *q++ = 0x80 | ((value.word >> 6) & 0x3F);
         *q++ = 0x80 | (value.word & 0x3F);
      } else {  // INVALID VALUE!
         free(*utf8);
         *utf8 = NULL;

         return FALSE;
      }
   }

   *q = '\0';

   return TRUE;
}
   07070100000186000081A4000000000000000000000001682255050000F461000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/codesetOld.c  /*********************************************************
 * Copyright (C) 1998-2017,2019,2021-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * codesetOld.c --
 *
 *    The old codeset implementation that depends on system libraries.
 *    Used for fallback if ICU isn't available.
 */


#if defined(_WIN32)
#   include <windows.h>
#   include <malloc.h>
#   include <str.h>
#else
#   if defined(__linux__)
#      define _GNU_SOURCE   // see nl_langinfo_l explanation below
#   endif
#   include <string.h>
#   include <stdlib.h>
#   include <unistd.h>
#   include <errno.h>
#endif

#if defined(__APPLE__)
#   include <CoreFoundation/CoreFoundation.h> /* for CFString */
#endif

#include "vmware.h"
#include "codeset.h"
#include "codesetOld.h"
#include "unicodeTypes.h"
#include "util.h"
#include "str.h"

#if defined(USE_ICONV)
   /*
    * Use nl_langinfo_l on Linux.  To get the nl_langinfo_l
    * related definitions in local.h, we need to define _GNU_SOURCE,
    * which has to be done above because one of the other standard include
    * files sucks it in.
    */
   #include <locale.h>
   #if defined(__linux__) && !defined(LC_CTYPE_MASK)  // Prior to glibc 2.3
      #define LC_CTYPE_MASK (1 << LC_CTYPE)
      #define locale_t __locale_t
      #define newlocale __newlocale
      #define freelocale __freelocale
      #define nl_langinfo_l __nl_langinfo_l
   #endif
   #include <dlfcn.h>
   #include <iconv.h>
   #include <langinfo.h>
#endif


#if defined(__FreeBSD__) || defined(sun)
static const char nul[] = {'\0', '\0'};
#else
static const wchar_t nul = L'\0';
#endif

#ifndef USE_ICONV
static Bool CodeSetOldIso88591ToUtf8Db(char const *bufIn, size_t sizeIn,
                                       unsigned int flags, DynBuf *db);
#endif

#if defined __ANDROID__
/*
 * This function will be called when buffer overflows. It will panic the app.
 * It should be in libc.a. But new Android ndk does not have it.
 */
void __stack_chk_fail(void);
void __stack_chk_fail_local (void)
{
   __stack_chk_fail();
}
#endif

#if defined(CURRENT_IS_UTF8) || defined(_WIN32)
/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldDuplicateStr --
 *
 *    Duplicate input string, appending zero terminator to its end.  Only
 *    used on Windows and on platforms where current encoding is always
 *    UTF-8, on other iconv-capable platforms we just use iconv even for
 *    UTF-8 to UTF-8 translation.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetOldDuplicateStr(const char *bufIn,  // IN: Input string
                       size_t  sizeIn,     // IN: Input string length
                       char **bufOut,      // OUT: "Converted" string
                       size_t *sizeOut)    // OUT/OPT: Length of string
{
   char *myBufOut;
   size_t newSize = sizeIn + sizeof *myBufOut;

   if (newSize < sizeIn) {   // Prevent integer overflow
      return FALSE;
   }

   myBufOut = malloc(newSize);
   if (myBufOut == NULL) {
      return FALSE;
   }

   memcpy(myBufOut, bufIn, sizeIn);
   myBufOut[sizeIn] = '\0';

   *bufOut = myBufOut;
   if (sizeOut) {
      *sizeOut = sizeIn;
   }

   return TRUE;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldDynBufFinalize --
 *
 *    Append NUL terminator to the buffer, and return pointer to buffer
 *    and its data size (before appending terminator).  Destroys buffer
 *    on failure.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetOldDynBufFinalize(Bool ok,          // IN: Earlier steps succeeded
                         DynBuf *db,       // IN: Buffer with converted string
                         char **bufOut,    // OUT: Converted string
                         size_t *sizeOut)  // OUT/OPT: Length of string in bytes
{
   if (!ok || !DynBuf_Append(db, &nul, sizeof nul) || !DynBuf_Trim(db)) {
      DynBuf_Destroy(db);

      return FALSE;
   }

   *bufOut = DynBuf_Get(db);
   if (sizeOut) {
      *sizeOut = DynBuf_GetSize(db) - sizeof nul;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldUtf8ToUtf16leDb --
 *
 *    Append the content of a buffer (that uses the UTF-8 encoding) to a
 *    DynBuf (that uses the UTF-16LE encoding)
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetOldUtf8ToUtf16leDb(const char *bufIn,   // IN:
                          size_t      sizeIn,  // IN:
                          DynBuf     *db)      // IN:
{
   const char *bufEnd = bufIn + sizeIn;
   size_t currentSize;
   size_t allocatedSize;
   uint16 *buf;

   currentSize = DynBuf_GetSize(db);
   allocatedSize = DynBuf_GetAllocatedSize(db);
   buf = (uint16 *)((char *)DynBuf_Get(db) + currentSize);
   while (bufIn < bufEnd) {
      size_t neededSize;
      uint32 uniChar;
      int n = CodeSet_GetUtf8(bufIn, bufEnd, &uniChar);

      if (n <= 0) {
         return FALSE;
      }
      bufIn += n;

      /*
       * Here we have UCS-4 character in uniChar, between 0 and 0x7FFFFFFF.
       * Let's convert it to UTF-16.
       */

      /* Non-paired surrogates are illegal in UTF-16. */
      if (uniChar >= 0xD800 && uniChar < 0xE000) {
         return FALSE;
      }
      if (uniChar < 0x10000) {
         neededSize = currentSize + sizeof *buf;
      } else if (uniChar < 0x110000) {
         neededSize = currentSize + 2 * sizeof *buf;
      } else {
         /* This character cannot be represented in UTF-16. */
         return FALSE;
      }
      if (allocatedSize < neededSize) {
         if (DynBuf_Enlarge(db, neededSize) == FALSE) {
            return FALSE;
         }
         allocatedSize = DynBuf_GetAllocatedSize(db);
         ASSERT(neededSize <= allocatedSize);
         buf = (uint16 *)((char *)DynBuf_Get(db) + currentSize);
      }
      if (uniChar < 0x10000) {
         *buf++ = uniChar;
      } else {
         *buf++ = 0xD800 + ((uniChar - 0x10000) >> 10);
         *buf++ = 0xDC00 + ((uniChar - 0x10000) & 0x3FF);
      }
      currentSize = neededSize;
   }
   /* All went fine, update buffer size. */
   DynBuf_SetSize(db, currentSize);

   return TRUE;
}


#if defined(_WIN32) // {

static DWORD GetInvalidCharsFlag(void);

/*
 * Win32-specific remarks: here is my understanding of those terms as of
 * 2002/02/12:
 *
 * ANSI code page
 *    The character set used internally by Windows applications (when they are
 *    not fully Unicode).
 *
 * OEM code page
 *    The character set used by MS-DOS and stored in the FAT filesystem.
 *
 *   --hpreg
 */


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldGenericToUtf16leDb --
 *
 *    Append the content of a buffer (that uses the specified encoding) to a
 *    DynBuf (that uses the UTF-16LE encoding) --hpreg
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetOldGenericToUtf16leDb(UINT codeIn,        // IN:
                             char const *bufIn,  // IN:
                             size_t sizeIn,      // IN:
                             DynBuf *db)         // IN:
{
   /*
    * Undocumented: calling MultiByteToWideChar() with sizeIn == 0 returns 0
    * with GetLastError() set to ERROR_INVALID_PARAMETER. Isn't this API
    * robust? --hpreg
    */

   if (sizeIn) {
      size_t initialSize;
      DWORD flags = GetInvalidCharsFlag();
      Bool invalidCharsCheck = ((flags & MB_ERR_INVALID_CHARS) != 0);

      initialSize = DynBuf_GetSize(db);
      for (;;) {
         int result;
         int resultReverse;
         DWORD error = ERROR_SUCCESS;

         if (DynBuf_Enlarge(db, sizeof(wchar_t)) == FALSE) {
            return FALSE;
         }

         /*
          * Must fail if bufIn has any invalid characters.
          * So MB_ERR_INVALID_CHARS added, otherwise can
          * lead to security issues see bug 154114.
          */
         result = MultiByteToWideChar(codeIn, flags, bufIn, sizeIn,
                     (wchar_t *)((char *)DynBuf_Get(db) + initialSize),
                     (DynBuf_GetAllocatedSize(db) - initialSize) /
                                      sizeof(wchar_t));

         if (result == 0) {
            error = GetLastError();   // may be ERROR_NO_UNICODE_TRANSLATION
         }

         /*
          * Success if: result is > 0 and is the same number
          * of characters as the input string contains. If there
          * are any invalid characters, the Win2K SP4 or later will
          * fail, but for earlier OS versions, these invalid characters
          * will be dropped. Thus only succeed if we have no dropped
          * characters.
          * For the older platforms which don't fail for invalid characters
          * we see if the reverse conversion of the converted string
          * yields the same string size that was passed in.
          * If not, then dropped characters so fail.
          */

         if (!invalidCharsCheck) {
            resultReverse = WideCharToMultiByte(codeIn, 0,
                            (wchar_t *)((char *)DynBuf_Get(db) + initialSize),
                            result, NULL, 0, 0, 0);
         }
         if (result > 0 &&
             (invalidCharsCheck ||
             (sizeIn == resultReverse))) {
            DynBuf_SetSize(db, initialSize + result * sizeof(wchar_t));
            break;
         }

         if (result > 0 && (!invalidCharsCheck && sizeIn != resultReverse)) {
            return FALSE;
         }

         ASSERT(result == 0);

         if (error != ERROR_INSUFFICIENT_BUFFER) {
            return FALSE;
         }

         /* Need a larger buffer --hpreg */
      }
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldUtf16leToGeneric --
 *
 *    Append the content of a buffer (that uses the UTF-16LE encoding) to a
 *    DynBuf (that uses the specified encoding) --hpreg
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetOldUtf16leToGeneric(char const *bufIn,  // IN:
                           size_t sizeIn,      // IN:
                           UINT codeOut,       // IN:
                           DynBuf *db)         // IN:
{
   /*
    * Undocumented: calling WideCharToMultiByte() with sizeIn == 0 returns 0
    * with GetLastError() set to ERROR_INVALID_PARAMETER. Isn't this API
    * robust? --hpreg
    */

   if (sizeIn) {
      size_t initialSize;
      Bool canHaveSubstitution = codeOut != CP_UTF8 && codeOut != CP_UTF7;

      initialSize = DynBuf_GetSize(db);
      for (;;) {
         int result;
         DWORD error;
         BOOL usedSubstitution = FALSE;

         if (DynBuf_Enlarge(db, 1) == FALSE) {
            return FALSE;
         }

         result = WideCharToMultiByte(codeOut,
                     canHaveSubstitution ? WC_NO_BEST_FIT_CHARS : 0,
                     (wchar_t const *)bufIn,
                     sizeIn / sizeof(wchar_t),
                     (char *)DynBuf_Get(db) + initialSize,
                     DynBuf_GetAllocatedSize(db) - initialSize,
                     NULL,
                     canHaveSubstitution ? &usedSubstitution : NULL);

         if (usedSubstitution) {
            return FALSE;
         }

         if (result > 0) {
            DynBuf_SetSize(db, initialSize + result);
            break;
         }

         ASSERT(result == 0);

         /* Must come first --hpreg */
         error = GetLastError();

         if (error != ERROR_INSUFFICIENT_BUFFER) {
            return FALSE;
         }

         /* Need a larger buffer --hpreg */
      }
   }

   /*
    * Undocumented: if the input buffer is not NUL-terminated, the output
    * buffer will not be NUL-terminated either --hpreg
    */

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldUtf16leToCurrent --
 *
 *    Convert the content of a buffer (that uses the UTF-16LE encoding) into
 *    another buffer (that uses the current encoding) --hpreg
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetOldUtf16leToCurrent(char const *bufIn,  // IN:
                           size_t sizeIn,      // IN:
                           char **bufOut,      // OUT:
                           size_t *sizeOut)    // OUT:
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   /* XXX We should probably use CP_THREAD_ACP on Windows 2000/XP --hpreg */
   ok = CodeSetOldUtf16leToGeneric(bufIn, sizeIn, CP_ACP, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
}

#endif // }


#if defined(__APPLE__)
/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf8Normalize --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding) into
 *    another buffer (that uses the UTF-8 encoding) that is in precomposed
 *    (Normalization Form C) or decomposed (Normalization Form D).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains a NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    '*bufOut' contains the allocated, NUL terminated buffer.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf8Normalize(char const *bufIn,  // IN:
                         size_t sizeIn,      // IN:
                         Bool precomposed,   // IN:
                         DynBuf *db)         // OUT:
{
   Bool ok = FALSE;
   CFStringRef str = NULL;
   CFMutableStringRef mutStr = NULL;
   CFIndex len, lenMut;
   size_t initialSize = DynBuf_GetSize(db);

   str = CFStringCreateWithCString(NULL, bufIn, kCFStringEncodingUTF8);
   if (str == NULL) {
      goto exit;
   }

   mutStr = CFStringCreateMutableCopy(NULL, 0, str);
   if (mutStr == NULL) {
      goto exit;
   }

   /*
    * Normalize the string, Form C - precomposed or D, not.
    */

   CFStringNormalize(mutStr, (precomposed ? kCFStringNormalizationFormC :
                                            kCFStringNormalizationFormD));

   /* 
    * Get the number (in terms of UTF-16 code units) 
    * of characters in a string.
    */

   lenMut = CFStringGetLength(mutStr);

   /*
    * Retrieve the maximum number of bytes a string of a 
    * specified length (in UTF-16 code units) will take up 
    * if encoded in a specified encoding.
    */

   len = CFStringGetMaximumSizeForEncoding(lenMut, kCFStringEncodingUTF8);
   if (len + 1 > initialSize) {
      if (DynBuf_Enlarge(db, len + 1 - initialSize) == FALSE) {
         ok = FALSE;
         goto exit;
      }
   }

   /*
    * Copies the character contents of a string to a local C 
    * string buffer after converting the characters to UTF-8.
    */

   ok = CFStringGetCString(mutStr, (char *)DynBuf_Get(db),
                           len + 1, kCFStringEncodingUTF8);
   if (ok) {
      /* Remove the NUL terminator that the above includes. */
      DynBuf_SetSize(db, strlen((char *)DynBuf_Get(db)));
   }

exit:
   if (str) {
      CFRelease(str);
   }
   if (mutStr) {
      CFRelease(mutStr);
   }

   return ok;
}
#endif /* defined(__APPLE__) */


/*
 * Linux-specific remarks:
 *
 * We use UTF-16 instead of UCS-2, because Windows 2000 introduces support
 * for basic input, output, and simple sorting of surrogates.
 *
 * We use UTF-16LE instead of UTF-16 so that iconv() does not prepend a BOM.
 *
 *   --hpreg
 */


#if defined(USE_ICONV) // {
/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldGetCodeSetFromLocale --
 *
 *    Extract the native code set from LC_CTYPE.  
 *
 * Results:
 *
 *    The name of the current code set on success.  The return value depends
 *    on how LC_CTYPE is set in the locale, even if setlocale has not been
 *    previously called.
 *
 * Side effects:
 *
 *    May briefly set and restore locale on some systems, which is not
 *    thread-safe.
 *
 *-----------------------------------------------------------------------------
 */

static char *
CodeSetOldGetCodeSetFromLocale(void)
{
   char *codeset;

#if defined(__linux__) || defined(__EMSCRIPTEN__)

   locale_t new = newlocale(LC_CTYPE_MASK, "", NULL);
   if (!new) {
      /*
       * If the machine is configured incorrectly (no current locale),
       * newlocale() could return NULL.  Try to fall back on the "C"
       * locale.
       */

      new = newlocale(LC_CTYPE_MASK, "C", NULL);
      ASSERT(new);
   }
   codeset = Util_SafeStrdup(nl_langinfo_l(CODESET, new));
   freelocale(new);

#elif defined(sun)

   char *locale = setlocale(LC_CTYPE, NULL);

   if (!setlocale(LC_CTYPE, "")) {
      /*
       * If the machine is configured incorrectly (no current locale),
       * setlocale() can fail.  Try to fall back on the "C" locale.
       */

      setlocale(LC_CTYPE, "C");
   }
   codeset = Util_SafeStrdup(nl_langinfo(CODESET));
   setlocale(LC_CTYPE, locale);

#else
#error
#endif

   return codeset;
}
#endif // } USE_ICONV


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_GetCurrentCodeSet --
 *
 *    Return native code set name - always "UTF-8" on Apple, FreeBSD,
 *    old Linux, and ESX. Obtained from GetACP on Windows and
 *    nl_langinfo on Linux, Solaris, etc.  On first invocation
 *    initialize a cache with the code set name.
 *
 * Results:
 *
 *    The name of the current code set on success.  On systems that
 *    use iconv the return value depends on how LC_CTYPE is set in the
 *    locale, even if setlocale has not been previously called.
 *
 * Side effects:
 *
 *    During first invocation may briefly set and restore locale on
 *    some systems, which is not thread-safe.
 *
 *-----------------------------------------------------------------------------
 */

const char *
CodeSetOld_GetCurrentCodeSet(void)
{
#if defined(CURRENT_IS_UTF8) || defined(VM_WIN_UWP)
   return "UTF-8";
#elif defined(_WIN32)
   static char ret[20];  // max is "windows-4294967296"
   if (ret[0] == '\0') {
      Str_Sprintf(ret, sizeof ret, "windows-%u", GetACP());
   }

   return ret;
#elif defined(USE_ICONV)
   static const char *cachedCodeset;

   /*
    * Mirror GLib behavior:
    *
    *    $G_FILENAME_ENCODING can have one or more encoding names
    *    in a comma separated list.
    *
    *    If the first entry in $G_FILENAME_ENCODING is set to
    *    "@locale", get the code set from the environment.
    *
    *    If the first entry in $G_FILENAME_ENCODING is not set to
    *    "@locale", then it is the encoding name.
    *
    *    If $G_FILENAME_ENCODING is not set and $G_BROKEN_FILENAMES
    *    is set, the get the code set from the environment.
    *
    *    If none of the above are met, the code set is UTF-8.
    *
    *    XXX - TODO - Support multiple encodings in the list.
    *    While G_FILENAME_ENCODING is documented to be a list,
    *    the current implementation (GLib 2.16) ignores all but
    *    the first entry when converting to/from UTF-8.
    */

   if (cachedCodeset == NULL) {
      char *gFilenameEncoding = getenv("G_FILENAME_ENCODING");

      if (gFilenameEncoding != NULL && *gFilenameEncoding != '\0') {
         char *p;

         gFilenameEncoding = Util_SafeStrdup(gFilenameEncoding);
         p = strchr(gFilenameEncoding, ',');

         if (p != NULL) {
            *p = '\0';
         }
         if (!strcmp(gFilenameEncoding, "@locale")) {
            free(gFilenameEncoding);
            cachedCodeset = CodeSetOldGetCodeSetFromLocale();

            return cachedCodeset;
         }
         cachedCodeset = gFilenameEncoding;

         return cachedCodeset;
      }

      if (getenv("G_BROKEN_FILENAMES")) {
         cachedCodeset = CodeSetOldGetCodeSetFromLocale();

         return cachedCodeset;
      }

      cachedCodeset = "UTF-8";
   }

   return cachedCodeset;
#else
#error
#endif
}

#if defined(USE_ICONV) // {

/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldIconvOpen --
 *
 *    Open iconv translator with requested flags.  Currently only no flags,
 *    and both CSGTG_TRANSLIT and CSGTG_IGNORE together are supported, as
 *    this is only thing we need.  If translit/ignore convertor fails,
 *    then non-transliterating conversion is used.
 *
 * Results:
 *    (iconv_t)-1 on failure
 *    iconv handle on success
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE_SINGLE_CALLER iconv_t
CodeSetOldIconvOpen(const char  *codeIn,   // IN:
                    const char  *codeOut,  // IN:
                    unsigned int flags)    // IN:
{
#ifdef __linux__
   if (flags) {
      char *codeOutExt;

      ASSERT(flags == (CSGTG_TRANSLIT | CSGTG_IGNORE));
      /*
       * We should be using //TRANSLIT,IGNORE, but glibc versions older than
       * 2.3.4 (in particular, the version that ships with redhat linux 9.0)
       * are subtly broken when passing options with a comma, in such a way
       * that iconv_open will succeed but iconv_close can crash.  For now, we
       * only use TRANSLIT and bail out after the first non-translitible
       * character.
       */

      codeOutExt = Str_Asprintf(NULL, "%s//TRANSLIT", codeOut);
      if (codeOutExt) {
         iconv_t cd = iconv_open(codeOutExt, codeIn);
         free(codeOutExt);
         if (cd != (iconv_t)-1) {
            return cd;
         }
      }
   }
#endif

   return iconv_open(codeOut, codeIn);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_GenericToGenericDb --
 *
 *    Append the content of a buffer (that uses the specified encoding) to a
 *    DynBuf (that uses the specified encoding). --hpreg
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_GenericToGenericDb(char const *codeIn,   // IN:
                              char const *bufIn,    // IN:
                              size_t sizeIn,        // IN:
                              char const *codeOut,  // IN:
                              unsigned int flags,   // IN:
                              DynBuf *db)           // IN/OUT:
{
   iconv_t cd;

   ASSERT(codeIn);
   ASSERT(sizeIn == 0 || bufIn);
   ASSERT(codeOut);
   ASSERT(db);

   // XXX make CodeSetOldIconvOpen happy
   if (flags != 0) {
      flags = CSGTG_TRANSLIT | CSGTG_IGNORE;
   }

   cd = CodeSetOldIconvOpen(codeIn, codeOut, flags);
   if (cd == (iconv_t)-1) {
      return FALSE;
   }

   for (;;) {
      size_t size;
      size_t newSize;
      char *out;
      char *outOrig;
      size_t outLeft;
      size_t status;

      /*
       * Every character we care about can occupy at most
       * 4 bytes - UCS-4 is 4 bytes, UTF-16 is 2+2 bytes,
       * and UTF-8 is also at most 4 bytes for all
       * characters under 0x1FFFFF.
       *
       * If we allocate too small buffer nothing critical
       * happens except that in //IGNORE case some
       * implementations might return EILSEQ instead of
       * E2BIG.  By having at least 4 bytes available we
       * can be sure that at least one character is
       * converted each call to iconv().
       */

      size = DynBuf_GetSize(db);
      newSize = size + 4;

      if (newSize < size) {  // Prevent integer overflow.
         goto error;
      }

      if (DynBuf_Enlarge(db, newSize) == FALSE) {
         goto error;
      }

      out = (int8 *)DynBuf_Get(db) + size;
      outOrig = out;
      outLeft = DynBuf_GetAllocatedSize(db) - size;

      /*
       * From glibc 2.2 onward bufIn is no longer const due to a change
       * in the standard. However, the implementation of iconv doesn't
       * change bufIn so a simple cast is safe. --plangdale
       */

#if defined(__linux__) || defined(__EMSCRIPTEN__)
      status = iconv(cd, (char **)&bufIn, &sizeIn, &out, &outLeft);
#else
      status = iconv(cd, &bufIn, &sizeIn, &out, &outLeft);
#endif

      DynBuf_SetSize(db, size + out - outOrig);

      /*
       * If all input characters were consumed, we are done.
       * Otherwise if at least one character was produced by conversion
       * then just increase buffer size and try again - with //IGNORE
       * iconv() returns an error (EILSEQ) but still processes as
       * many characters as possible.  If no characters were produced,
       * then consult error code - do not consult return value in other
       * cases, it can be either random positive value (if some
       * characters were transliterated) or even -1 with errno set to
       * EILSEQ (if some characters were ignored).
       */

      if (sizeIn == 0) {
         break;
      }
      if (out == outOrig) {
         if (status != -1) {
            goto error;
         }

         /*
          * Some libc implementations (one on ESX3, and one on Ganesh's
          * box) silently ignore //IGNORE.  So if caller asked for
          * getting conversion done at any cost, just return success
          * even if failure occured.  User will get truncated
          * message, but that's our best.  We have no idea whether
          * incoming encoding is 8bit, 16bit, or what, so we cannot
          * skip over characters in input stream and recover :-(
          */

         if ((flags & CSGTG_IGNORE) && errno == EILSEQ) {
            break;
         }
         if (errno != E2BIG) {
            goto error;
         }
      }
      /* Need a larger buffer --hpreg */
   }

   if (iconv_close(cd) < 0) {
      return FALSE;
   }

   return TRUE;

error:
   iconv_close(cd);

   return FALSE;
}

#else // USE_ICONV } {

/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_GenericToGenericDb
 *
 *    This non-iconv version can only handle common encodings.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_GenericToGenericDb(char const *codeIn,   // IN:
                              char const *bufIn,    // IN:
                              size_t sizeIn,        // IN:
                              char const *codeOut,  // IN:
                              unsigned int flags,   // IN:
                              DynBuf *db)           // IN/OUT:
{
   Bool ret = FALSE;
   StringEncoding encIn = Unicode_EncodingNameToEnum(codeIn);
   StringEncoding encOut = Unicode_EncodingNameToEnum(codeOut);
   StringEncoding rawCurEnc = Unicode_GetCurrentEncoding();
   char *bufOut = NULL;
   size_t sizeOut;

   /*
    * Trivial case.
    */

   if ((sizeIn == 0) || (bufIn == NULL)) {
      ret = TRUE;
      goto exit;
   }

   if (encIn == encOut) {
      if (encIn == STRING_ENCODING_UTF8) {
         if (!CodeSetOld_Utf8ToUtf16le(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else if (encIn == STRING_ENCODING_UTF16_LE) {
         if (!CodeSetOld_Utf16leToUtf8(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else if (encIn == STRING_ENCODING_UTF16_BE) {
         if (!CodeSetOld_Utf16beToUtf8(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else if (encIn == STRING_ENCODING_US_ASCII) {
         if (!CodeSetOld_AsciiToUtf8(bufIn, sizeIn, 0, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else if (encIn == rawCurEnc) {
         if (!CodeSetOld_CurrentToUtf8(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      }
      free(bufOut);
      bufOut = NULL;
      if (!DynBuf_Append(db, bufIn, sizeIn)) {
         goto exit;
      }
   } else if (rawCurEnc == encIn) {
      if (STRING_ENCODING_UTF8 == encOut) {
         if (!CodeSetOld_CurrentToUtf8(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else if (STRING_ENCODING_UTF16_LE == encOut) {
         if (!CodeSetOld_CurrentToUtf16le(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else {
         goto exit;
      }
   } else if (STRING_ENCODING_UTF8 == encIn) {
      if (rawCurEnc == encOut) {
         if (!CodeSetOld_Utf8ToCurrent(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else if (STRING_ENCODING_UTF16_LE == encOut) {
         if (!CodeSetOldUtf8ToUtf16leDb(bufIn, sizeIn, db)) {
            goto exit;
         }
      } else if (STRING_ENCODING_US_ASCII == encOut) {
         if (!CodeSetOld_Utf8ToAsciiDb(bufIn, sizeIn, flags, db)) {
            goto exit;
         }
      } else {
         goto exit;
      }
   } else if (STRING_ENCODING_UTF16_LE == encIn) {
      if (rawCurEnc == encOut) {
         if (!CodeSetOld_Utf16leToCurrent(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else if (STRING_ENCODING_UTF8 == encOut) {
         if (!CodeSetOld_Utf16leToUtf8Db(bufIn, sizeIn, db)) {
            goto exit;
         }
      } else {
         goto exit;
      }
   } else if (STRING_ENCODING_UTF16_BE == encIn) {
      if (rawCurEnc == encOut) {
         if (!CodeSetOld_Utf16beToCurrent(bufIn, sizeIn, &bufOut, &sizeOut)) {
            goto exit;
         }
      } else if (STRING_ENCODING_UTF8 == encOut) {
         if (!CodeSetOld_Utf16beToUtf8Db(bufIn, sizeIn, db)) {
            goto exit;
         }
      } else {
         goto exit;
      }
   } else if (STRING_ENCODING_US_ASCII == encIn) {
      if (STRING_ENCODING_UTF8 == encOut) {
         if (!CodeSetOld_AsciiToUtf8Db(bufIn, sizeIn, flags, db)) {
            goto exit;
         }
      } else {
         goto exit;
      }
   } else if (STRING_ENCODING_ISO_8859_1 == encIn) {
      if (STRING_ENCODING_UTF8 == encOut) {
         if (!CodeSetOldIso88591ToUtf8Db(bufIn, sizeIn, flags, db)) {
            goto exit;
         }
      } else {
         goto exit;
      }
   } else {
      goto exit;
   }

   if (bufOut != NULL) {
      if (DynBuf_GetSize(db) == 0) {
         DynBuf_Attach(db, sizeOut, bufOut);
         bufOut = NULL;
      } else {
         if (!DynBuf_Append(db, bufOut, sizeOut)) {
            goto exit;
         }
      }
   }

   ret = TRUE;

  exit:
   free(bufOut);

   return ret;
}

#endif // USE_ICONV }

/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_GenericToGeneric --
 *
 *    Non-db version of CodeSetOld_GenericToGenericDb.
 *
 * Results:
 *    TRUE on success, plus allocated string
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_GenericToGeneric(const char *codeIn,   // IN:
                            const char *bufIn,    // IN:
                            size_t sizeIn,        // IN:
                            const char *codeOut,  // IN:
                            unsigned int flags,   // IN:
                            char **bufOut,        // OUT:
                            size_t *sizeOut)      // OUT:
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_GenericToGenericDb(codeIn, bufIn, sizeIn,
                                      codeOut, flags, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 * Generic remarks: here is my understanding of those terms as of 2001/12/27:
 *
 * BOM
 *    Byte-Order Mark
 *
 * BMP
 *    Basic Multilingual Plane. This plane comprises the first 2^16 code
 *    positions of ISO/IEC 10646's canonical code space
 *
 * UCS
 *    Universal Character Set. Encoding form specified by ISO/IEC 10646
 *
 * UCS-2
 *    Directly store all Unicode scalar value (code point) from U+0000 to
 *    U+FFFF on 2 bytes. Consequently, this representation can only hold
 *    characters in the BMP
 *
 * UCS-4
 *    Directly store a Unicode scalar value (code point) from U-00000000 to
 *    U-FFFFFFFF on 4 bytes
 *
 * UTF
 *    Abbreviation for Unicode (or UCS) Transformation Format
 *
 * UTF-8
 *    Unicode (or UCS) Transformation Format, 8-bit encoding form. UTF-8 is the
 *    Unicode Transformation Format that serializes a Unicode scalar value
 *    (code point) as a sequence of 1 to 6 bytes
 *
 * UTF-16
 *    UCS-2 + surrogate mechanism: allow to encode some non-BMP Unicode
 *    characters in a UCS-2 string, by using 2 2-byte units. See the Unicode
 *    standard, v2.0
 *
 * UTF-32
 *    Directly store all Unicode scalar value (code point) from U-00000000 to
 *    U-0010FFFF on 4 bytes. This is a subset of UCS-4
 *
 *   --hpreg
 */


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf8ToCurrent --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding) into
 *    another buffer (that uses the current encoding) --hpreg
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf8ToCurrent(char const *bufIn,  // IN:
                         size_t sizeIn,      // IN:
                         char **bufOut,      // OUT:
                         size_t *sizeOut)    // OUT:
{
#if defined(CURRENT_IS_UTF8)
   return CodeSetOldDuplicateStr(bufIn, sizeIn, bufOut, sizeOut);
#elif defined(USE_ICONV)
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_GenericToGenericDb("UTF-8", bufIn, sizeIn,
                                      CodeSetOld_GetCurrentCodeSet(),
                                      0, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
#elif defined(_WIN32)
   char *buf;
   size_t size;
   Bool status;

   if (CodeSetOld_Utf8ToUtf16le(bufIn, sizeIn, &buf, &size) == FALSE) {
      return FALSE;
   }

   status = CodeSetOldUtf16leToCurrent(buf, size, bufOut, sizeOut);
   free(buf);

   return status;
#else
#error
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_CurrentToUtf8 --
 *
 *    Convert the content of a buffer (that uses the current encoding) into
 *    another buffer (that uses the UTF-8 encoding) --hpreg
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_CurrentToUtf8(char const *bufIn,  // IN:
                      size_t sizeIn,         // IN:
                      char **bufOut,         // OUT:
                      size_t *sizeOut)       // OUT:
{
#if defined(CURRENT_IS_UTF8)
   return CodeSetOldDuplicateStr(bufIn, sizeIn, bufOut, sizeOut);
#elif defined(USE_ICONV)
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_GenericToGenericDb(CodeSetOld_GetCurrentCodeSet(), bufIn,
                                      sizeIn, "UTF-8", 0, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
#elif defined(_WIN32)
   char *buf;
   size_t size;
   Bool status;

   if (CodeSetOld_CurrentToUtf16le(bufIn, sizeIn, &buf, &size) == FALSE) {
      return FALSE;
   }

   status = CodeSetOld_Utf16leToUtf8(buf, size, bufOut, sizeOut);
   free(buf);

   return status;
#else
#error
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf16leToUtf8Db --
 *
 *    Append the content of a buffer (that uses the UTF-16LE encoding) to a
 *    DynBuf (that uses the UTF-8 encoding). --hpreg
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf16leToUtf8Db(char const *bufIn,  // IN
                           size_t sizeIn,      // IN
                           DynBuf *db)         // IN
{
   const uint16 *utf16In;
   size_t numCodeUnits;
   size_t codeUnitIndex;

   if (sizeIn % sizeof *utf16In != 0) {
      return FALSE;
   }

   utf16In = (const uint16 *)bufIn;
   numCodeUnits = sizeIn / 2;

   for (codeUnitIndex = 0; codeUnitIndex < numCodeUnits; codeUnitIndex++) {
      uint32 codePoint;
      uint8 *dbBytes;
      size_t size;
      size_t newSize;

      if (utf16In[codeUnitIndex] < 0xD800 ||
          utf16In[codeUnitIndex] > 0xDFFF) {
         // Non-surrogate UTF-16 code units directly represent a code point.
         codePoint = utf16In[codeUnitIndex];
      } else {
         static const uint32 SURROGATE_OFFSET =
                                        (0xD800 << 10UL) + 0xDC00 - 0x10000;

         uint16 surrogateLead = utf16In[codeUnitIndex];
         uint16 surrogateTrail;

         // We need one more code unit for the trailing surrogate.
         codeUnitIndex++;
         if (codeUnitIndex == numCodeUnits) {
            return FALSE;
         }

         surrogateTrail = utf16In[codeUnitIndex];

         // Ensure we have a lead surrogate followed by a trail surrogate.
         if (surrogateLead > 0xDBFF ||
             surrogateTrail < 0xDC00 ||
             surrogateTrail > 0xDFFF) {
            return FALSE;
         }

         /*
          * To get a code point between 0x10000 and 0x10FFFF (2^16 to
          * (2^21) - 1):
          *
          * 1) Ensure surrogateLead is in the range [0xD800, 0xDBFF]
          *
          * 2) Ensure surrogateTrail is in the range [0xDC00, 0xDFFF]
          *
          * 3) Mask off all but the low 10 bits of lead and shift that
          *    left 10 bits: ((surrogateLead << 10) - (0xD800 << 10))
          *    -> result [0, 0xFFC00]
          *
          * 4) Add to that the low 10 bits of trail: (surrogateTrail - 0xDC00)
          *    -> result [0, 0xFFFFF]
          *
          * 5) Add to that 0x10000:
          *    -> result [0x10000, 0x10FFFF]
          */
         codePoint = ((uint32)surrogateLead << 10UL) +
                                    (uint32)surrogateTrail - SURROGATE_OFFSET;

         ASSERT(codePoint >= 0x10000 && codePoint <= 0x10FFFF);
      }

      size = DynBuf_GetSize(db);
      newSize = size + 4;

      // We'll need at most 4 more bytes for this code point.
      if ((newSize < size) ||  // Prevent integer overflow
          (DynBuf_GetAllocatedSize(db) < newSize &&
           DynBuf_Enlarge(db, newSize) == FALSE)) {
         return FALSE;
      }

      dbBytes = (uint8 *)DynBuf_Get(db) + size;

      // Convert the code point to UTF-8.
      if (codePoint <= 0x007F) {
         // U+0000 - U+007F: 1 byte of UTF-8.
         dbBytes[0] = codePoint;
         size += 1;
      } else if (codePoint <= 0x07FF) {
         // U+0080 - U+07FF: 2 bytes of UTF-8.
         dbBytes[0] = 0xC0 | (codePoint >> 6);
         dbBytes[1] = 0x80 | (codePoint & 0x3F);
         size += 2;
      } else if (codePoint <= 0xFFFF) {
         // U+0800 - U+FFFF: 3 bytes of UTF-8.
         dbBytes[0] = 0xE0 | (codePoint >> 12);
         dbBytes[1] = 0x80 | ((codePoint >> 6) & 0x3F);
         dbBytes[2] = 0x80 | (codePoint & 0x3F);
         size += 3;
      } else {
         /*
          * U+10000 - U+10FFFF: 4 bytes of UTF-8.
          *
          * See the surrogate pair handling block above for the math
          * that ensures we're in the range [0x10000, 0x10FFFF] here.
          */

         ASSERT(codePoint <= 0x10FFFF);
         dbBytes[0] = 0xF0 | (codePoint >> 18);
         dbBytes[1] = 0x80 | ((codePoint >> 12) & 0x3F);
         dbBytes[2] = 0x80 | ((codePoint >> 6) & 0x3F);
         dbBytes[3] = 0x80 | (codePoint & 0x3F);
         size += 4;
      }

      DynBuf_SetSize(db, size);
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf16leToUtf8 --
 *
 *    Convert the content of a buffer (that uses the UTF-16LE encoding) into
 *    another buffer (that uses the UTF-8 encoding). --hpreg
 *
 *    The operation is inversible (its inverse is CodeSetOld_Utf8ToUtf16le).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf16leToUtf8(char const *bufIn,  // IN:
                         size_t sizeIn,      // IN:
                         char **bufOut,      // OUT:
                         size_t *sizeOut)    // OUT:
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_Utf16leToUtf8Db(bufIn, sizeIn, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf8ToUtf16le --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding) into
 *    another buffer (that uses the UTF-16LE encoding). --hpreg
 *
 *    The operation is inversible (its inverse is CodeSetOld_Utf16leToUtf8).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf8ToUtf16le(char const *bufIn,  // IN:
                         size_t sizeIn,      // IN:
                         char **bufOut,      // OUT:
                         size_t *sizeOut)    // OUT:
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOldUtf8ToUtf16leDb(bufIn, sizeIn, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf8FormDToUtf8FormC  --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding) 
 *    which is in normal form D (decomposed) into another buffer
 *    (that uses the UTF-8 encoding) and is normalized as
 *    precomposed (Normalization Form C).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains a NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    '*bufOut' contains the allocated, NUL terminated buffer.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf8FormDToUtf8FormC(char const *bufIn,  // IN:
                                size_t sizeIn,      // IN:
                                char **bufOut,      // OUT:
                                size_t *sizeOut)    // OUT:
{
#if defined(__APPLE__)
   DynBuf db;
   Bool ok;
   DynBuf_Init(&db);
   ok = CodeSetOld_Utf8Normalize(bufIn, sizeIn, TRUE, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
#else
   NOT_IMPLEMENTED();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf8FormCToUtf8FormD  --
 *
 *    Convert the content of a buffer (that uses the UTF-8 encoding) 
 *    which is in normal form C (precomposed) into another buffer
 *    (that uses the UTF-8 encoding) and is normalized as
 *    decomposed (Normalization Form D).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains a NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    '*bufOut' contains the allocated, NUL terminated buffer.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf8FormCToUtf8FormD(char const *bufIn,  // IN:
                                size_t sizeIn,      // IN:
                                char **bufOut,      // OUT:
                                size_t *sizeOut)    // OUT:
{
#if defined(__APPLE__)
   DynBuf db;
   Bool ok;
   DynBuf_Init(&db);
   ok = CodeSetOld_Utf8Normalize(bufIn, sizeIn, FALSE, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
#else
   NOT_IMPLEMENTED();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_CurrentToUtf16le --
 *
 *    Convert the content of a buffer (that uses the current encoding) into
 *    another buffer (that uses the UTF-16LE encoding).
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_CurrentToUtf16le(char const *bufIn,  // IN:
                            size_t sizeIn,      // IN:
                            char **bufOut,      // OUT:
                            size_t *sizeOut)    // OUT:
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
#if defined(CURRENT_IS_UTF8)
   ok = CodeSetOldUtf8ToUtf16leDb(bufIn, sizeIn, &db);
#elif defined(USE_ICONV)
   ok = CodeSetOld_GenericToGenericDb(CodeSetOld_GetCurrentCodeSet(), bufIn,
                                      sizeIn, "UTF-16LE", 0, &db);
#elif defined(_WIN32)
   /* XXX We should probably use CP_THREAD_ACP on Windows 2000/XP. */
   ok = CodeSetOldGenericToUtf16leDb(CP_ACP, bufIn, sizeIn, &db);
#else
#error
#endif

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf16leToCurrent --
 *
 *    Convert the content of a buffer (that uses the UTF-16 little endian
 *    encoding) into another buffer (that uses the current encoding)
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf16leToCurrent(char const *bufIn,  // IN:
                            size_t sizeIn,      // IN:
                            char **bufOut,      // OUT:
                            size_t *sizeOut)    // OUT:
{
#if defined(CURRENT_IS_UTF8)
   return CodeSetOld_Utf16leToUtf8(bufIn, sizeIn, bufOut, sizeOut);
#elif defined(USE_ICONV)
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_GenericToGenericDb("UTF-16LE", bufIn, sizeIn,
                                      CodeSetOld_GetCurrentCodeSet(), 0, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
#elif defined(_WIN32)
   return CodeSetOldUtf16leToCurrent(bufIn, sizeIn, bufOut, sizeOut);
#else
#error
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf16beToCurrent --
 *
 *    Convert the content of a buffer (that uses the UTF-16 big endian
 *    encoding) into another buffer (that uses the current encoding)
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf16beToCurrent(char const *bufIn,  // IN:
                            size_t sizeIn,      // IN:
                            char **bufOut,      // OUT:
                            size_t *sizeOut)    // OUT:
{
#if defined(CURRENT_IS_UTF8)
   Bool status;
   char *temp;

   if ((temp = malloc(sizeIn)) == NULL) {
      return FALSE;
   }
   ASSERT((sizeIn & 1) == 0);
   ASSERT((ssize_t) sizeIn >= 0);
   swab(bufIn, temp, sizeIn);
   status = CodeSetOld_Utf16leToUtf8(temp, sizeIn, bufOut, sizeOut);
   free(temp);

   return status;
#elif defined(USE_ICONV)
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_GenericToGenericDb("UTF-16BE", bufIn, sizeIn,
                                      CodeSetOld_GetCurrentCodeSet(), 0, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
#elif defined(_WIN32)
   char c;
   char *bufIn_dup;
   int i;
   Bool status = FALSE;

   bufIn_dup = NULL;

   /* sizeIn must be even */
   ASSERT((sizeIn & 1) == 0);

   /* Make a non-const copy */
   bufIn_dup = malloc(sizeIn);
   if (bufIn_dup == NULL) {
      goto error;
   }
   memcpy(bufIn_dup, bufIn, sizeIn);

   /* Swap pairs of bytes */
   for (i = 0; i < sizeIn; i += 2) {
      c = bufIn_dup[i];
      bufIn_dup[i] = bufIn_dup[i + 1];
      bufIn_dup[i + 1] = c;
   }

   status = CodeSetOldUtf16leToCurrent(bufIn_dup, sizeIn, bufOut, sizeOut);

  error:
   free(bufIn_dup);

   return status;
#else
#error
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf16beToUtf8 --
 *
 *    Convert the content of a buffer (that uses the UTF-16BE encoding) into
 *    another buffer (that uses the UTF-8 encoding). 
 *
 * Results:
 *    TRUE on success: '*bufOut' contains the allocated, NUL terminated buffer.
 *                     If not NULL, '*sizeOut' contains the size of the buffer
 *                     (excluding the NUL terminator)
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf16beToUtf8(char const *bufIn,  // IN:
                         size_t sizeIn,      // IN:
                         char **bufOut,      // OUT:
                         size_t *sizeOut)    // OUT:
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_Utf16beToUtf8Db(bufIn, sizeIn, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Utf16beToUtf8Db --
 *
 *    Append the content of a buffer (that uses the UTF-16BE encoding) to a
 *    DynBuf (that uses the UTF-8 encoding). 
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf16beToUtf8Db(char const *bufIn,  // IN:
                           size_t sizeIn,      // IN:
                           DynBuf *db)         // IN:
{
   int i;
   char *temp;
   Bool ret = FALSE;

   if ((temp = malloc(sizeIn)) == NULL) {
      return ret;
   }
   ASSERT((sizeIn & 1) == 0);
   ASSERT((ssize_t) sizeIn >= 0);

   for (i = 0; i < sizeIn; i += 2) {
      temp[i] = bufIn[i + 1];
      temp[i + 1] = bufIn[i];
   }

   ret = CodeSetOld_Utf16leToUtf8Db(temp, sizeIn, db);
   free(temp);

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_AsciiToUtf8 --
 *
 *      Convert ASCII to UTF-8
 *
 * Results:
 *      On success, TRUE and conversion result in bufOut and sizeOut.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_AsciiToUtf8(const char *bufIn,   // IN:
                       size_t sizeIn,       // IN:
                       unsigned int flags,  // IN:
                       char **bufOut,       // OUT:
                       size_t *sizeOut)     // OUT:
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_AsciiToUtf8Db(bufIn, sizeIn, flags, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_AsciiToUtf8Db --
 *
 *      Convert ASCII to UTF-8
 *
 * Results:
 *      On success, TRUE and conversion result appended to db.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_AsciiToUtf8Db(char const *bufIn,   // IN:
                         size_t sizeIn,       // IN:
                         unsigned int flags,  // IN:
                         DynBuf *db)          // OUT:
{
   size_t oldSize = DynBuf_GetSize(db);
   size_t i;
   size_t last = 0;

   for (i = 0; i < sizeIn; i++) {
      if (UNLIKELY((unsigned char) bufIn[i] >= 0x80)) {
         if (flags == 0) {
            DynBuf_SetSize(db, oldSize);

            return FALSE;
         }
         DynBuf_Append(db, bufIn + last, i - last);
         if ((flags & CSGTG_TRANSLIT) != 0) {
            DynBuf_Append(db, "\xef\xbf\xbd", 3);
         }
         last = i + 1;
      }
   }
   DynBuf_Append(db, bufIn + last, i - last);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8ToAscii --
 *
 *      Convert UTF-8 to ASCII
 *
 * Results:
 *      On success, TRUE and conversion result in bufOut and sizeOut.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf8ToAscii(const char *bufIn,   // IN:
                       size_t sizeIn,       // IN:
                       unsigned int flags,  // IN:
                       char **bufOut,       // OUT:
                       size_t *sizeOut)     // OUT:
{
   DynBuf db;
   Bool ok;

   DynBuf_Init(&db);
   ok = CodeSetOld_Utf8ToAsciiDb(bufIn, sizeIn, flags, &db);

   return CodeSetOldDynBufFinalize(ok, &db, bufOut, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_Utf8ToAsciiDb --
 *
 *      Convert UTF-8 to ASCII
 *
 * Results:
 *      On success, TRUE and conversion result appended to db.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Utf8ToAsciiDb(char const *bufIn,   // IN:
                         size_t sizeIn,       // IN:
                         unsigned int flags,  // IN:
                         DynBuf *db)          // OUT:
{
   size_t oldSize = DynBuf_GetSize(db);
   uint8 *p = (uint8 *) bufIn;
   uint8 *end = (uint8 *) bufIn + sizeIn;
   uint8 *last = p;

   for (; p < end; p++) {
      if (UNLIKELY(*p >= 0x80)) {
         int n;

         if (flags == 0) {
            DynBuf_SetSize(db, oldSize);

            return FALSE;
         }
         DynBuf_Append(db, last, p - last);
         if ((flags & CSGTG_TRANSLIT) != 0) {
            DynBuf_Append(db, "\x1a", 1);
         }
         if ((n = CodeSet_GetUtf8((char *) p, (char *) end, NULL)) > 0) {
            p += n - 1;
         }
         last = p + 1;
      }
   }
   DynBuf_Append(db, last, p - last);

   return TRUE;
}


#ifndef USE_ICONV
/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOldIso88591ToUtf8Db --
 *
 *      Convert ISO-8859-1 to UTF-8
 *
 * Results:
 *      On success, TRUE and conversion result appended to db.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CodeSetOldIso88591ToUtf8Db(char const *bufIn,   // IN:
                           size_t sizeIn,       // IN:
                           unsigned int flags,  // IN:
                           DynBuf *db)          // OUT:
{
   size_t i;
   size_t last = 0;

   for (i = 0; i < sizeIn; i++) {
      unsigned int c = (unsigned char)bufIn[i];

      if (UNLIKELY(c >= 0x80)) {
         unsigned char buf[2];

         buf[0] = 0xC0 | (c >> 6);
         buf[1] = 0x80 | (c & 0x3F);
         DynBuf_Append(db, bufIn + last, i - last);
         DynBuf_Append(db, buf, sizeof buf);
         last = i + 1;
      }
   }
   DynBuf_Append(db, bufIn + last, i - last);

   return TRUE;
}
#endif


#if defined(_WIN32)
/*
 *-----------------------------------------------------------------------------
 *
 * GetInvalidCharsFlag --
 *
 *    The flag MB_ERR_INVALID_CHARS can only be passed to MultiByteToWideChar
 *    on Win2000 SP4 or later.  If it's passed to an NT or 9x system,
 *    MultiByteToWideChar fails with ERR_INVALID_FLAGS.
 *
 * Results:
 *      returns MB_ERR_INVALID_CHARS if this flag is supported under current OS
 *      returns zero if the flag would case MultiByteToWideChar failure.
 *
 * Side effects:
 *      none
 *
 *-----------------------------------------------------------------------------
 */

static DWORD
GetInvalidCharsFlag(void)
{
#if defined(VM_WIN_UWP)
   return MB_ERR_INVALID_CHARS;
#else
   static volatile Bool bFirstCall = TRUE;
   static DWORD retval;

   OSVERSIONINFOEX osvi;
   BOOL bOsVersionInfoEx;

   if (!bFirstCall) {   // We can return a cached result for subsequent calls
      return retval;
   }

   /*
    * Try calling GetVersionEx using the OSVERSIONINFOEX structure.
    */

   ZeroMemory(&osvi, sizeof osvi);
   osvi.dwOSVersionInfoSize = sizeof osvi;

   /*
    * Starting with msvc-12.0 / SDK v8.1 GetVersionEx is deprecated.
    * Bug 1259185 tracks switching to VerifyVersionInfo.
    */

#pragma warning(push)
#pragma warning(disable : 4996) // 'function': was declared deprecated
   if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) {
      /*
       * If GetVersionEx failed, we are running something earlier than NT4+SP6,
       * thus we cannot use MB_ERR_INVALID_CHARS
       */

       retval = 0;
       bFirstCall = FALSE;

       return retval;
   }
#pragma warning(pop)

   if (osvi.dwMajorVersion > 5) {
      retval = MB_ERR_INVALID_CHARS;  // Vista or later
   } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion > 0) {
      retval = MB_ERR_INVALID_CHARS;  // XP, 2003
   } else if (osvi.dwMajorVersion == 5
               && osvi.dwMinorVersion == 0
               && osvi.wServicePackMajor >= 4) {  // Win2000 + SP4
      retval = MB_ERR_INVALID_CHARS;
   } else {
      retval = 0;   // Do not use MB_ERR_INVALID_CHARS on this OS.
   }

   bFirstCall = FALSE;

   return retval;
#endif //VM_WIN_UWP
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_IsEncodingSupported --
 *
 *    Not really from the old codeset file, but we need a non-ICU way
 *    of doing this. Asking lib/unicode to cross-reference the
 *    encoding name with its internal list is functionally equivalent
 *    to what Unicode_IsEncodingSupported used to do (and still does
 *    if no ICU support is built-in).
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_IsEncodingSupported(const char *name)  // IN:
{
   ASSERT(name);

   return (STRING_ENCODING_UNKNOWN != Unicode_EncodingNameToEnum(name));
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Validate --
 *
 *    Validate a string in the given encoding.
 *
 * Results:
 *    TRUE if string is valid,
 *    FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Validate(const char *buf,   // IN: the string
                    size_t size,       // IN: length of string
                    const char *code)  // IN: encoding
{
   DynBuf db;
   Bool ok;

   if (size == 0) {
      return TRUE;
   }

   DynBuf_Init(&db);
   ok = CodeSetOld_GenericToGenericDb(code, buf, size, "UTF-8",
                                      CSGTG_NORMAL, &db);
   DynBuf_Destroy(&db);

   return ok;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSetOld_Init --
 *
 *    No-op.
 *
 * Results:
 *    TRUE.
 *
 * Side effects:
 *    See above
 *
 *-----------------------------------------------------------------------------
 */

Bool
CodeSetOld_Init(UNUSED_PARAM(const char *dataDir))  // IN:
{
   return TRUE;
}
   07070100000187000081A40000000000000000000000016822550500001844000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/codesetOld.h  /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * codesetOld.h --
 *
 *    The old codeset implementation that depends on system libraries.
 *    Used for fallback if ICU isn't available.
 *
 */

#ifndef __CODESET_OLD_H__
#   define __CODESET_OLD_H__

#include "dynbuf.h"
#include "codeset.h"  // for CURRENT_IS_UTF8


/*
 * These systems use iconv and nl_langinfo.
 * See the definition of CURRENT_IS_UTF8.
 */

#if !defined(CURRENT_IS_UTF8) && !defined(_WIN32)
   #define USE_ICONV
#endif


Bool
CodeSetOld_GenericToGenericDb(char const *codeIn,  // IN
                              char const *bufIn,   // IN
                              size_t sizeIn,       // IN
                              char const *codeOut, // IN
                              unsigned int flags,  // IN
                              DynBuf *db);         // IN/OUT

Bool
CodeSetOld_GenericToGeneric(const char *codeIn,  // IN
                            const char *bufIn,   // IN
                            size_t sizeIn,       // IN
                            const char *codeOut, // IN
                            unsigned int flags,  // IN
                            char **bufOut,       // IN/OUT
                            size_t *sizeOut);    // IN/OUT

Bool
CodeSetOld_Utf8ToCurrent(char const *bufIn,      // IN
                         size_t sizeIn,    // IN
                         char **bufOut,          // OUT
                         size_t *sizeOut); // OUT

Bool
CodeSetOld_CurrentToUtf8(char const *bufIn,      // IN
                         size_t sizeIn,    // IN
                         char **bufOut,          // OUT
                         size_t *sizeOut); // OUT

Bool
CodeSetOld_Utf16leToUtf8Db(char const *bufIn,   // IN
                           size_t sizeIn,	// IN
                           DynBuf *db);         // IN

Bool
CodeSetOld_Utf16leToUtf8(char const *bufIn,      // IN
                         size_t sizeIn,    // IN
                         char **bufOut,          // OUT
                         size_t *sizeOut); // OUT

Bool
CodeSetOld_Utf8ToUtf16le(char const *bufIn,      // IN
                         size_t sizeIn,    // IN
                         char **bufOut,          // OUT
                         size_t *sizeOut); // OUT

Bool
CodeSetOld_CurrentToUtf16le(char const *bufIn,      // IN
                            size_t sizeIn,    // IN
                            char **bufOut,          // OUT
                            size_t *sizeOut); // OUT

Bool
CodeSetOld_Utf16leToCurrent(char const *bufIn,      // IN
                            size_t sizeIn,    // IN
                            char **bufOut,          // OUT
                            size_t *sizeOut); // OUT

Bool
CodeSetOld_Utf16beToCurrent(char const *bufIn,      // IN
                            size_t sizeIn,    // IN
                            char **bufOut,          // OUT
                            size_t *sizeOut); // OUT

Bool
CodeSetOld_Utf8Normalize(const char *bufIn,     // IN
                         size_t sizeIn,         // IN
                         Bool precomposed,      // IN
                         DynBuf *db);           // OUT

Bool
CodeSetOld_Utf8FormDToUtf8FormC(char const *bufIn,     // IN
                                size_t sizeIn,         // IN
                                char **bufOut,         // OUT
                                size_t *sizeOut);      // OUT

Bool
CodeSetOld_Utf8FormCToUtf8FormD(char const *bufIn,     // IN
                                size_t sizeIn,         // IN
                                char **bufOut,         // OUT
                                size_t *sizeOut);      // OUT

Bool
CodeSetOld_Utf16beToUtf8(char const *bufIn, // IN
                         size_t sizeIn,     // IN
                         char **bufOut,     // OUT
                         size_t *sizeOut);  // OUT

Bool
CodeSetOld_Utf16beToUtf8Db(char const *bufIn, // IN
                           size_t sizeIn,     // IN
                           DynBuf *db);       // IN

Bool
CodeSetOld_AsciiToUtf8(const char *bufIn,   // IN
                       size_t sizeIn,       // IN
                       unsigned int flags,  // IN
                       char **bufOut,       // OUT
                       size_t *sizeOut);    // OUT

Bool
CodeSetOld_AsciiToUtf8Db(char const *bufIn,   // IN
                         size_t sizeIn,       // IN
                         unsigned int flags,  // IN
                         DynBuf *db);         // OUT

Bool
CodeSetOld_Utf8ToAscii(const char *bufIn,   // IN
                       size_t sizeIn,       // IN
                       unsigned int flags,  // IN
                       char **bufOut,       // OUT
                       size_t *sizeOut);    // OUT

Bool
CodeSetOld_Utf8ToAsciiDb(char const *bufIn,   // IN
                         size_t sizeIn,       // IN
                         unsigned int flags,  // IN
                         DynBuf *db);         // OUT

const char *
CodeSetOld_GetCurrentCodeSet(void);

Bool
CodeSetOld_IsEncodingSupported(const char *name); // IN

Bool
CodeSetOld_Validate(const char *buf,   // IN: the string
                    size_t size,       // IN: length of string
                    const char *code); // IN: encoding

Bool
CodeSetOld_Init(const char *dataDir);  // UNUSED

#endif /* __CODESET_OLD_H__ */
07070100000188000081A400000000000000000000000168225505000013F8000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/codesetUTF8.c /* **********************************************************
 * Copyright (c) 2015-2021 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
 * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */


#include "vmware.h"
#include "codeset.h"

#define UTF8_ACCEPT 0
#define UTF8_REJECT 1

static const unsigned char utf8d[] = {
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
  8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
  0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
  0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
  0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
  1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
  1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
  1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
};

static INLINE uint32
CodeSetDecode(uint32 *state,  // IN:
              uint32 byte)    // IN:
{
  uint32 type = utf8d[byte];

  *state = utf8d[256 + *state*16 + type];

  return *state;
}


/*
 *----------------------------------------------------------------------------
 *
 * CodeSet_IsStringValidUTF8 --
 *
 *      Check if the given buffer contains a valid UTF-8 string.
 *      This function will stop at first '\0' it sees.
 *
 * Results:
 *      TRUE if the given buffer contains a valid UTF-8 string, or FALSE.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

Bool
CodeSet_IsStringValidUTF8(const char *bufIn)  // IN:
{
   uint32 state = UTF8_ACCEPT;

   while (*bufIn != '\0') {
      CodeSetDecode(&state, (unsigned char) *bufIn++);
   }

   return state == UTF8_ACCEPT;
}


/*
 *----------------------------------------------------------------------------
 *
 * CodeSet_IsValidUTF8 --
 *
 *      Check if the given buffer with given size, is UTF-8 encoded.
 *      This function will return TRUE even if there is '\0' in the buffer.
 *
 * Results:
 *      TRUE if the buffer is UTF-8 encoded, or FALSE.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

Bool
CodeSet_IsValidUTF8(const char *bufIn,  // IN:
                    size_t sizeIn)      // IN:
{
   size_t i;
   uint32 state = UTF8_ACCEPT;

   for (i = 0; i < sizeIn; i++) {
      CodeSetDecode(&state, (unsigned char) *bufIn++);
   }

   return state == UTF8_ACCEPT;
}


/*
 *----------------------------------------------------------------------------
 *
 * CodeSet_IsValidUTF8String --
 *
 *      Check if the given buffer with given size, is a valid UTF-8 string,
 *      and without '\0' in it.
 *
 * Results:
 *      TRUE if passed, or FALSE.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

Bool
CodeSet_IsValidUTF8String(const char *bufIn,  // IN:
                          size_t sizeIn)      // IN:
{
   size_t i;
   uint32 state = UTF8_ACCEPT;

   for (i = 0; i < sizeIn; i++) {
      unsigned char c = (unsigned char) *bufIn++;

      if (UNLIKELY(c == '\0')) {
         return FALSE;
      }

      CodeSetDecode(&state, c);
   }

   /* If everything went well we should have proper UTF8, the data
    * might instead have ended in the middle of a UTF8 codepoint.
    */
   return state == UTF8_ACCEPT;
}
07070100000189000081A400000000000000000000000168225505000010CA000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/dynarray.c    /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dynarray.c --
 *
 *    Dynamic array of objects -- tonyc
 */

#include <stdlib.h>

#include "vmware.h"
#include "dynarray.h"


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_Init --
 *
 *      Initialize the dynamic array
 *
 * Results:
 *      TRUE on success.  FALSE on failure.
 *
 * Side effects:
 *      See above
 *
 *-----------------------------------------------------------------------------
 */

Bool
DynArray_Init(DynArray *a,          // IN/OUT
              unsigned int count,   // IN
              size_t width)         // IN
{
   ASSERT(a);

   DynBuf_Init(&a->buf);
   a->width = width;
   return DynArray_SetCount(a, count);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_Destroy --
 *
 *      Destroy the array
 *
 * Results:
 *      None
 *
 * Side effects:
 *      See above
 *
 *-----------------------------------------------------------------------------
 */

void
DynArray_Destroy(DynArray *a)    // IN/OUT
{
   ASSERT(a);

   DynBuf_Destroy(&a->buf);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_SetCount --
 *
 *      Sets the number of elements in the array.   This may enlarge
 *      the size of the array.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      May resize the array
 *
 *-----------------------------------------------------------------------------
 */

Bool
DynArray_SetCount(DynArray *a,          // IN/OUT
                  unsigned int c)       // IN
{
   size_t needed, allocated;

   ASSERT(a);

   needed = c * a->width;
   allocated = DynBuf_GetAllocatedSize(&a->buf);

   if (allocated < needed) {
      if (!DynBuf_Enlarge(&a->buf, needed)) {
         return FALSE;
      }
   }
   DynBuf_SetSize(&a->buf, needed);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_AllocCount --
 *
 *      Returns the actual size of the array.  If you want the effective
 *      size, use DynArray_Count.  Technically, you don't need this API
 *      unless you're doing trimming (i.e. Don't bother trimming if
 *      DynArray_AllocCount is within some threshold of DynArray_Count.
 *      It won't buy you much).
 *
 *      XXX: This is relatively slow, since we do an integer division.
 *           Avoid calling this in inner loops.
 *
 * Results:
 *      See above.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

unsigned int
DynArray_AllocCount(const DynArray *a)  // IN
{
   ASSERT(a);

   return (unsigned int) (DynBuf_GetAllocatedSize(&a->buf) / a->width);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynArray_QSort --
 *
 *      A wrapper for the quicksort function.  Sorts the DynArray
 *      according to the provided comparison function.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
DynArray_QSort(DynArray *a,             // IN/OUT
               DynArrayCmp compare)     // IN
{
   uint8 *arrayBuf;

   ASSERT(a);
   ASSERT(compare);

   arrayBuf = DynBuf_Get(&a->buf);
   qsort(arrayBuf, DynArray_Count(a), a->width, compare);
}
  0707010000018A000081A40000000000000000000000016822550500003393000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/dynbuf.c  /*********************************************************
 * Copyright (C) 1998-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dynbuf.c --
 *
 *    Dynamic buffers --hpreg
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "vmware.h"
#include "dynbuf.h"

/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Init --
 *
 *      Dynamic buffer constructor. The dynamic buffer is empty and starts
 *      with no memory allocated.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
DynBuf_Init(DynBuf *b)  // OUT:
{
   ASSERT(b);

   b->data = NULL;
   b->size = 0;
   b->allocated = 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_InitWithMemory --
 *
 *      Dynamic buffer constructor. The dynamic buffer is empty but starts with
 *      the specified memory allocation.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
DynBuf_InitWithMemory(DynBuf *b,        // IN/OUT:
                      size_t dataSize,  // IN:
                      void *data)       // IN:
{
   ASSERT(b);
   ASSERT(dataSize != 0);
   ASSERT(data != NULL);

   b->size = 0;
   b->data = data;
   b->allocated = dataSize;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_InitWithString --
 *
 *      Initialize buffer with a pre-allocated string.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
DynBuf_InitWithString(DynBuf *b, // IN/OUT
                      char *str) // IN
{
   if (str != NULL) {
      int len = strlen(str);
      DynBuf_InitWithMemory(b, len + 1, str);
      DynBuf_SetSize(b, len);
   } else {
      DynBuf_Init(b);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Destroy --
 *
 *      Dynamic buffer destructor --hpreg
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
DynBuf_Destroy(DynBuf *b)  // IN/OUT:
{
   ASSERT(b);

   free(b->data);
   DynBuf_Init(b);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Attach --
 *
 *      Grants ownership of the specified buffer to the DynBuf
 *      object. If there is an existing buffer, it is freed.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
DynBuf_Attach(DynBuf *b,    // IN/OUT:
              size_t size,  // IN:
              void *data)   // IN:
{
   ASSERT(b);
   ASSERT((size == 0) == (data == NULL));

   free(b->data);
   b->data = data;
   b->size = b->allocated = size;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Detach --
 *
 *      Transfers ownership of the buffer stored in the DynBuf object to the
 *      caller.
 *
 * Results:
 *      Returns a pointer to the data.  The caller must free it with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void *
DynBuf_Detach(DynBuf *b)  // IN/OUT:
{
   void *data;

   ASSERT(b);

   data = b->data;
   b->data = NULL;
   b->allocated = 0;

   return data;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_DetachString --
 *
 *      Transfers ownership of the buffer stored in the DynBuf object to the
 *      caller.
 *
 * Results:
 *      Returns a pointer to the data as a NUL-terminated string.  The caller
 *      must free it with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
DynBuf_DetachString(DynBuf *b) // IN/OUT
{
   char *data = DynBuf_GetString(b);
   DynBuf_Detach(b);
   return data;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBufRealloc --
 *
 *      Reallocate a dynamic buffer --hpreg
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
DynBufRealloc(DynBuf *b,            // IN/OUT:
              size_t newAllocated)  // IN:
{
   void *new_data;

   ASSERT(b);

   new_data = realloc(b->data, newAllocated);
   if (new_data == NULL && newAllocated) {
      /* Not enough memory */
      return FALSE;
   }

   b->data = new_data;
   b->allocated = newAllocated;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Enlarge --
 *
 *      Enlarge a dynamic buffer. The resulting dynamic buffer is guaranteed to
 *      be larger than the one you passed, and at least 'minSize' bytes
 *      large --hpreg
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DynBuf_Enlarge(DynBuf *b,       // IN/OUT:
               size_t minSize)  // IN:
{
   size_t newAllocated;

   ASSERT(b);

   newAllocated = b->allocated
                      ?
#if defined(DYNBUF_DEBUG)
                        b->allocated + 1
#else
                        /*
                         * Double the previously allocated size if it is less
                         * than 256KB; otherwise grow it linearly by 256KB
                         */
                        (b->allocated < 256 * 1024 ? b->allocated * 2
                                                   : b->allocated + 256 * 1024)
#endif
                      :
#if defined(DYNBUF_DEBUG)
                        1
#else
                        /*
                         * Most DynBuf operations are on strings. Most strings
                         * are less than 128 bytes long.
                         */
                        128
#endif
                      ;

   if (minSize > newAllocated) {
      newAllocated = minSize;
   }

   /*
    * Prevent integer overflow. We can use this form of checking specifically
    * because a multiple by 2 is used (in the worst case). This type of
    * checking does not work in the general case.
    */

   if (newAllocated < b->allocated) {
      return FALSE;
   }

   return DynBufRealloc(b, newAllocated);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_SafeInternalEnlarge --
 *
 *      Enlarge a dynamic buffer. Memory allocation failure is handled the
 *      same way as Util_SafeMalloc, that is to say, with a Panic.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
DynBuf_SafeInternalEnlarge(DynBuf *b,           // IN/OUT:
                           size_t minSize,      // IN:
                           char const *file,    // IN:
                           unsigned int lineno) // IN:
{
   if (!DynBuf_Enlarge(b, minSize)) {
      Panic("Unrecoverable memory allocation failure at %s:%u\n",
            file, lineno);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Insert --
 *
 *      Insert data at a given offset within a dynamic buffer. 'size' is the
 *      size of the data. If it is <= 0, no operation is performed.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DynBuf_Insert(DynBuf *b,        // IN/OUT:
              size_t offset,    // IN:
              void const *data, // IN:
              size_t size)      // IN:
{
   size_t new_size;

   ASSERT(b);
   ASSERT(offset <= b->size);

   if (size <= 0) {
      return TRUE;
   }

   ASSERT(data);

   new_size = b->size + size;

   if (new_size < b->size) {  // Prevent integer overflow
      return FALSE;
   }

   if (new_size > b->allocated) {
      /* Not enough room */
      if (!DynBuf_Enlarge(b, new_size)) {
         return FALSE;
      }
   }

   memmove(b->data + offset + size, b->data + offset, b->size - offset);
   memcpy(b->data + offset, data, size);
   b->size = new_size;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_SafeInternalInsert --
 *
 *      Insert data at a given offset within a dynamic buffer. Memory
 *      allocation failure is handled the same way as Util_SafeMalloc, that is
 *      to say, with a Panic.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
DynBuf_SafeInternalInsert(DynBuf *b,            // IN/OUT:
                          size_t offset,        // IN:
                          void const *data,     // IN:
                          size_t size,          // IN:
                          char const *file,     // IN:
                          unsigned int lineno)  // IN:
{
   if (!DynBuf_Insert(b, offset, data, size)) {
      Panic("Unrecoverable memory allocation failure at %s:%u\n",
            file, lineno);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Append --
 *
 *      Append data at the end of a dynamic buffer. 'size' is the size of the
 *      data. If it is <= 0, no operation is performed.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DynBuf_Append(DynBuf *b,        // IN/OUT:
              void const *data, // IN:
              size_t size)      // IN:
{
   return DynBuf_Insert(b, b->size, data, size);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_SafeInternalAppend --
 *
 *      Append data at the end of a dynamic buffer. Memory allocation failure
 *      is handled the same way as Util_SafeMalloc, that is to say, with a
 *      Panic.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
DynBuf_SafeInternalAppend(DynBuf *b,            // IN/OUT:
                          void const *data,     // IN:
                          size_t size,          // IN:
                          char const *file,     // IN:
                          unsigned int lineno)  // IN:
{
   if (!DynBuf_Append(b, data, size)) {
      Panic("Unrecoverable memory allocation failure at %s:%u\n",
            file, lineno);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Trim --
 *
 *      Reallocate a dynamic buffer to the exact size it occupies --hpreg
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure (not enough memory)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DynBuf_Trim(DynBuf *b)  // IN/OUT:
{
   ASSERT(b);

   return DynBufRealloc(b, b->size);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DynBuf_Copy --
 *
 *      Copies all data and metadata from src dynbuf to dest dynbuf.
 *
 *      Dest should be an initialized DynBuf of alloced length zero
 *      to prevent memory leaks.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DynBuf_Copy(DynBuf *src,   // IN:
            DynBuf *dest)  // OUT:
{
   ASSERT(src);
   ASSERT(dest);
   ASSERT(!dest->data);

   dest->data = malloc(src->allocated);

   if (dest->data == NULL) {
      return FALSE;
   }

   dest->size = src->size;
   dest->allocated = src->allocated;

   memcpy(dest->data, src->data, src->size);

   return TRUE;
}
 0707010000018B000081A40000000000000000000000016822550500005698000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/escape.c  /*********************************************************
 * Copyright (C) 1998-2017,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * escape.c --
 *
 *    Buffer escaping --hpreg
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "vmware.h"
#include "dynbuf.h"
#include "escape.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',
};


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_DoString --
 *
 *    Escape a buffer --hpreg
 *
 * Results:
 *    The escaped, allocated, NUL terminated buffer on success. If not NULL,
 *     '*sizeOut' contains the size of the buffer (excluding the NUL
 *     terminator)
 *    NULL on failure (not enough memory)
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void *
Escape_DoString(const char *escStr,    // IN
                int const *bytesToEsc, // IN
                void const *bufIn,     // IN
                size_t sizeIn,         // IN
                size_t *sizeOut)       // OUT/OPT
{
   char const *buf;
   DynBuf b;
   size_t startUnescaped;
   size_t index;
   size_t escStrLen;

   ASSERT(escStr);
   escStrLen = strlen(escStr);
   ASSERT(escStrLen > 0);

   ASSERT(bytesToEsc);
   /* Unsigned does matter --hpreg */
   ASSERT(bytesToEsc[(unsigned char)escStr[0]]);

   buf = (char const *)bufIn;
   ASSERT(buf);

   DynBuf_Init(&b);
   startUnescaped = 0;

   for (index = 0; index < sizeIn; index++) {
      /* Unsigned does matter --hpreg */
      unsigned char ubyte;
      char escSeq[2];

      ubyte = buf[index];
      if (bytesToEsc[ubyte]) {
         /* We must escape that byte --hpreg */

         escSeq[0] = Dec2Hex[ubyte >> 4];
         escSeq[1] = Dec2Hex[ubyte & 0xF];
         if (DynBuf_Append(&b, &buf[startUnescaped],
                           index - startUnescaped) == FALSE ||
             DynBuf_Append(&b, escStr, escStrLen) == FALSE ||
             DynBuf_Append(&b, escSeq, sizeof escSeq) == FALSE) {
            goto nem;
         }
         startUnescaped = index + 1;
      }
   }

   if (/* Last unescaped chunk (if any) --hpreg */
       DynBuf_Append(&b, &buf[startUnescaped],
                     index - startUnescaped) == FALSE ||
       /* NUL terminator --hpreg */
       DynBuf_Append(&b, "", 1) == FALSE ||
       DynBuf_Trim(&b) == FALSE) {
      goto nem;
   }

   if (sizeOut) {
      *sizeOut = DynBuf_GetSize(&b) - 1;
   }

   return DynBuf_Get(&b);

nem:
   DynBuf_Destroy(&b);

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_Do --
 *
 *    Escape a buffer
 *
 * Results:
 *    The escaped, allocated, NUL terminated buffer on success. If not NULL,
 *     '*sizeOut' contains the size of the buffer (excluding the NUL
 *     terminator)
 *    NULL on failure (not enough memory)
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void *
Escape_Do(char escByte,          // IN
          int const *bytesToEsc, // IN
          void const *bufIn,     // IN
          size_t sizeIn,         // IN
          size_t *sizeOut)       // OUT/OPT
{
   const char escStr[] = { escByte, '\0' };

   return Escape_DoString(escStr, bytesToEsc, bufIn, sizeIn, sizeOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_Undo --
 *
 *    Unescape a buffer --hpreg
 *
 * Results:
 *    The unescaped, allocated, NUL terminated buffer on success. If not NULL,
 *     '*sizeOut' contains the size of the buffer (excluding the NUL
 *     terminator)
 *    NULL on failure (not enough memory)
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void *
Escape_Undo(char escByte,      // IN
            void const *bufIn, // IN
            size_t sizeIn,     // IN
            size_t *sizeOut)   // OUT/OPT
{
   char const *buf;
   DynBuf b;
   unsigned int state;
   size_t startUnescaped;
   size_t index;
   int h = 0; /* Compiler warning --hpreg */
   int l;

   buf = (char const *)bufIn;
   ASSERT(buf);

   DynBuf_Init(&b);
   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 <byte> --hpreg */
         if (ubyte == escByte) {
            state = 1;
         }
         break;

      case 1: /* Found <escByte><byte> --hpreg */
         h = Hex2Dec[ubyte];
         state = h >= 0 ? 2 : 0;
         break;

      case 2: /* Found <escByte><hexa digit><byte> --hpreg */
         l = Hex2Dec[ubyte];
         if (l >= 0) {
            char escaped;

            escaped = h << 4 | l;
            if (DynBuf_Append(&b, &buf[startUnescaped],
                              index - 2 - startUnescaped) == FALSE ||
                DynBuf_Append(&b, &escaped, 1) == FALSE) {
               goto nem;
            }
            startUnescaped = index + 1;
         }
         state = 0;
         break;

      default:
         NOT_IMPLEMENTED();
         break;
      }
   }

   if (/* Last unescaped chunk (if any) --hpreg */
       DynBuf_Append(&b, &buf[startUnescaped],
                     index - startUnescaped) == FALSE ||
       /* NUL terminator --hpreg */
       DynBuf_Append(&b, "", 1) == FALSE ||
       DynBuf_Trim(&b) == FALSE) {
      goto nem;
   }

   if (sizeOut) {
      *sizeOut = DynBuf_GetSize(&b) - 1;
   }

   return DynBuf_Get(&b);

nem:
   DynBuf_Destroy(&b);

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_UndoFixed --
 *
 *    Unescape a buffer into a fixed sized, preallocated buffer.
 *
 * Results:
 *    TRUE   The unescaped NUL terminated data is in bufOut upon success.
 *    FALSE  Escape or memory allocation failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Escape_UndoFixed(char escByte,       // IN: escape byte
                 void const *bufIn,  // IN: string to be escaped
                 size_t bufInSize,   // IN: uint32 - fixed size for Python
                 void *bufOut,       // IN/OUT: preallocated output buffer
                 size_t bufOutSize)  // IN: uint32 - fixed size for Python
{
   Bool success;
   size_t sizeOut = 0;
   void *result = Escape_Undo(escByte, bufIn, bufInSize, &sizeOut);

   if (result == NULL) {
      success = FALSE;
   } else {
      size_t strLen = sizeOut + 1;  // Include NUL

      success = (strLen <= bufOutSize);

      if (success) {
         memcpy(bufOut, result, strLen);
      }

      free(result);
   }

   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_AnsiToUnix --
 *
 *    Convert any occurrence of \r\n into \n --hpreg
 *
 * Results:
 *    The allocated, NUL terminated buffer on success. If not NULL,
 *     '*sizeOut' contains the size of the buffer (excluding the NUL
 *     terminator)
 *    NULL on failure (not enough memory)
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void *
Escape_AnsiToUnix(void const *bufIn, // IN
                  size_t sizeIn,     // IN
                  size_t *sizeOut)   // OUT/OPT
{
   char const *buf;
   DynBuf b;
   unsigned int state;
   size_t startUnescaped;
   size_t index;

   buf = (char const *)bufIn;
   ASSERT(buf);

   DynBuf_Init(&b);
   startUnescaped = 0;
   state = 0;

   /*
    * Identify all chunks in buf (\r\n being the chunk separator), and copy
    * them into b --hpreg
    */

   for (index = 0; index < sizeIn; index++) {
      char byte;

      byte = buf[index];
      switch (state) {
      case 1: /* Found \r<byte> --hpreg */
         state = 0;
         if (byte == '\n') {
            if (DynBuf_Append(&b, &buf[startUnescaped],
                              index - 1 - startUnescaped) == FALSE) {
               goto nem;
            }
            startUnescaped = index;
            break;
         }
         /* Fall through --hpreg */

      case 0: /* Found <byte> --hpreg */
         if (byte == '\r') {
            state = 1;
         }
         break;

      default:
         NOT_IMPLEMENTED();
         break;
      }
   }

   if (/* Last unescaped chunk (if any) --hpreg */
       DynBuf_Append(&b, &buf[startUnescaped],
                     index - startUnescaped) == FALSE ||
       /* NUL terminator --hpreg */
       DynBuf_Append(&b, "", 1) == FALSE ||
       DynBuf_Trim(&b) == FALSE) {
      goto nem;
   }

   if (sizeOut) {
      *sizeOut = DynBuf_GetSize(&b) - 1;
   }

   return DynBuf_Get(&b);

nem:
   DynBuf_Destroy(&b);

   return NULL;
}


#if 0
/* Unit test suite for Escape_AnsiToUnix() --hpreg */
int
main(int argc,
     char **argv)
{
   static struct {
      const char *in;
      const char *out;
   } tests[] = {
      { "", "", },
      { "a", "a", },
      { "\ra", "\ra", },
      { "\na", "\na", },
      { "\r\na", "\na", },
      { "\n\ra", "\n\ra", },
      { "\r\r\na", "\r\na", },
      { "\r\na\r", "\na\r", },
      { "\r\na\r\n", "\na\n", },
   };
   unsigned int i;

   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
      char *out;

      out = Escape_AnsiToUnix(tests[i].in, strlen(tests[i].in), NULL);
      if (strcmp(out, tests[i].out) != 0) {
         printf("test %u failed: %s\n", i, out);
         exit(1);
      }
      free(out);
   }

   printf("all tests passed\n");

   return 0;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_Sh --
 *
 *    Escape a buffer so that it can be passed verbatim as part of an argument
 *    on a shell command line.
 *
 * Results:
 *    The escaped, allocated, NUL terminated buffer on success. If not NULL,
 *     '*sizeOut' contains the size of the buffer (excluding the NUL
 *     terminator)
 *    NULL on failure (not enough memory)
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void *
Escape_Sh(void const *bufIn, // IN
          size_t sizeIn,     // IN
          size_t *sizeOut)   // OUT/OPT
{
   static const char be[] = { '\'', };
   static const char escSeq[] = { '\'', '"', '\'', '"', };
   char const *buf;
   DynBuf b;
   size_t startUnescaped;
   size_t index;

   buf = (char const *)bufIn;
   ASSERT(buf);

   DynBuf_Init(&b);

   if (DynBuf_Append(&b, be, sizeof(be)) == FALSE) {
      goto nem;
   }

   startUnescaped = 0;
   for (index = 0; index < sizeIn; index++) {
      if (buf[index] == '\'') {
         /* We must escape that byte --hpreg */

         if (DynBuf_Append(&b, &buf[startUnescaped],
                           index - startUnescaped) == FALSE ||
             DynBuf_Append(&b, escSeq, sizeof(escSeq)) == FALSE) {
            goto nem;
         }
         startUnescaped = index;
      }
   }

   if (/* Last unescaped chunk (if any) --hpreg */
       DynBuf_Append(&b, &buf[startUnescaped],
                     index - startUnescaped) == FALSE ||
       DynBuf_Append(&b, be, sizeof(be)) == FALSE ||
       /* NUL terminator --hpreg */
       DynBuf_Append(&b, "", 1) == FALSE ||
       DynBuf_Trim(&b) == FALSE) {
      goto nem;
   }

   if (sizeOut) {
      *sizeOut = DynBuf_GetSize(&b) - 1;
   }

   return DynBuf_Get(&b);

nem:
   DynBuf_Destroy(&b);

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_BRE --
 *
 *    Escape a buffer so that it can be passed verbatim as part of a Basic
 *    (a.k.a. obsolete) Regular Expression.
 *
 * Results:
 *    The escaped, allocated, NUL terminated buffer on success. If not NULL,
 *     '*sizeOut' contains the size of the buffer (excluding the NUL
 *     terminator)
 *    NULL on failure (not enough memory)
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void *
Escape_BRE(void const *bufIn, // IN
           size_t sizeIn,     // IN
           size_t *sizeOut)   // OUT/OPT
{
   static const char escByte = '\\';
   /* Escape ] [ ^ . * $ and 'escByte'. */
   static const int bytesToEsc[] = {
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   };
   char const *buf;
   DynBuf b;
   size_t startUnescaped;
   size_t index;

   /* Unsigned does matter. */
   ASSERT(bytesToEsc[(unsigned char)escByte]);

   buf = (char const *)bufIn;
   ASSERT(buf);

   DynBuf_Init(&b);
   startUnescaped = 0;

   for (index = 0; index < sizeIn; index++) {
      /* Unsigned does matter. */
      unsigned char ubyte;

      ubyte = buf[index];
      if (bytesToEsc[ubyte]) {
         /* We must escape that byte. */

         if (DynBuf_Append(&b, &buf[startUnescaped],
                           index - startUnescaped) == FALSE ||
             DynBuf_Append(&b, &escByte, sizeof escByte) == FALSE) {
            goto nem;
         }
         startUnescaped = index;
      }
   }

   if (/* Last unescaped chunk (if any). */
       DynBuf_Append(&b, &buf[startUnescaped],
                     index - startUnescaped) == FALSE ||
       /* NUL terminator. */
       DynBuf_Append(&b, "", 1) == FALSE ||
       DynBuf_Trim(&b) == FALSE) {
      goto nem;
   }

   if (sizeOut) {
      *sizeOut = DynBuf_GetSize(&b) - 1;
   }

   return DynBuf_Get(&b);

nem:
   DynBuf_Destroy(&b);

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_StrChr --
 *
 *      Find the first occurence of c in bufIn that is not preceded by
 *      escByte.
 *
 *      XXX Doesn't handle recursive escaping, so <escByte><escByte><c>
 *          will skip that occurence of <c>
 *
 * Results:
 *      A pointer to the character, if found, NULL if not found.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

const char *
Escape_Strchr(char escByte,      // IN
              const char *bufIn, // IN
              char c)            // IN
{
   size_t i;
   Bool escaped = FALSE;

   ASSERT(escByte != c);
   ASSERT(bufIn);

   for (i = 0; bufIn[i] != '\0'; i++) {
      if (escaped) {
         escaped = FALSE;
      } else {
         if (bufIn[i] == c) {
            return &bufIn[i];
         }

         if (bufIn[i] == escByte) {
            escaped = TRUE;
         }
      }
   }

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_Unescape --
 *
 *      Removes all occurences of an escape character from a string, unless
 *      the occurence is escaped.
 *
 * Results:
 *      An allocated string.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Escape_Unescape(char escByte,       // IN
                const char *bufIn)  // IN
{
   DynBuf result;
   Bool escaped = FALSE;
   char nulByte = '\0';
   int i;

   ASSERT(bufIn);

   DynBuf_Init(&result);

   for (i = 0; bufIn[i]; i++) {
      if (bufIn[i] != escByte || escaped) {
         DynBuf_Append(&result, &(bufIn[i]), sizeof(char));
         escaped = FALSE;
      } else {
         escaped = TRUE;
      }
   }

   DynBuf_Append(&result, &nulByte, sizeof nulByte);

   return DynBuf_Get(&result);
}


/*
 *----------------------------------------------------------------------------
 *
 * Escape_UnescapeCString --
 *
 *    Unescapes a C string that has been escaped using a '\n' -> "\n" or
 *    ' ' -> "\040" type conversion.  The former is a standard '\' escaping,
 *    while the latter is an octal representation escaping.  The unescaping is
 *    done in-place within the provided buffer.
 *
 *    This function assumes the string is NUL-terminated.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
Escape_UnescapeCString(char *buf) // IN/OUT
{
   uint32 read = 0;
   uint32 write = 0;

   ASSERT(buf);

   while (buf[read] != '\0') {
      if (buf[read] == '\\') {
         uint32 val;

         if (buf[read + 1] == 'n') {
            buf[write] = '\n';
            read++;
         } else if (buf[read + 1] == '\\') {
            buf[write] = '\\';
            read++;
         } else if (sscanf(&buf[read], "\\%03o", &val) == 1) {
            buf[write] = (char)val;
            read += 3;
         } else {
            buf[write] = buf[read];
         }
      } else {
         buf[write] = buf[read];
      }

      read++;
      write++;
   }
   buf[write] = '\0';
}


#if 0
/* Unit test suite for Escape_Sh() --hpreg */
int
main(int argc,
     char **argv)
{
   static struct {
      const char *in;
      const char *out;
   } tests[] = {
      { "", "''", },
      { "a", "'a'", },
      { "'a", "''\"'\"'a'", },
      { "'a'", "''\"'\"'a'\"'\"''", },
      { "a'a", "'a'\"'\"'a'", },
   };
   unsigned int i;

   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
      char *out;

      out = Escape_Sh(tests[i].in, strlen(tests[i].in), NULL);
      if (strcmp(out, tests[i].out) != 0) {
         printf("test %u failed: %s\n", i, out);
         exit(1);
      }
      free(out);
   }

   printf("all tests passed\n");

   return 0;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Escape_Comma --
 *
 *       Use backslash character as an escape character to handle comma
 *       character escaping.
 *
 * Results:
 *       Returns a newly allocated string with comma and backslash characters
 *       escaped.
 *
 * Side effects:
 *       None.  It is a caller responsibility to deallocate the result.
 *
 *-----------------------------------------------------------------------------
 */

char *
Escape_Comma(const char *string) // IN
{
   DynBuf b;

   if (string == NULL) {
      return NULL;
   }

   DynBuf_Init(&b);

   for (; *string; ++string) {
      char c = *string;

      if (c == ',' || c == '\\') {
         if (!DynBuf_Append(&b, "\\", 1)) {
            goto out_of_memory;
         }
      }
      if (!DynBuf_Append(&b, string, 1)) {
         goto out_of_memory;
      }
   }

   DynBuf_Append(&b, string, 1);

   return DynBuf_Get(&b);

out_of_memory:
   DynBuf_Destroy(&b);
   return NULL;
}


#if 0
/* Unit test suite for Escape_Comma() */
int
main(int argc,
     char **argv)
{
   static struct {
      const char *in;
      const char *out;
   } tests[] = {
      { "123# ", "123# ", },
      { "123,", "123\\,", },
      { "'123\\", "'123\\\\", },
   };
   unsigned int i;

   for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
      char *out;

      out = Escape_Comma(tests[i].in);
      if (strcmp(out, tests[i].out) != 0) {
         printf("test %u failed: \"%s\" : \"%s\"\n", i, tests[i].in, out);
         exit(1);
      }
      free(out);
   }

   printf("all tests passed\n");

   return 0;
}

#endif

0707010000018C000081A4000000000000000000000001682255050000600E000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/hashTable.c   /*********************************************************
 * Copyright (C) 2004-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hashTable.c --
 *
 *      An implementation of hashtable with no removals.
 *      For string keys.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef _WIN32
#include <windows.h>
#endif

#include "vmware.h"
#include "hashTable.h"
#include "vm_basic_asm.h"
#include "util.h"
#include "str.h"
#include "vm_atomic.h"


#define HASH_ROTATE     5


/*
 * Pointer to hash table entry
 *
 * The assumption is that Atomic_ReadPtr() and Atomic_WritePtr()
 * are cheap, so can be used with nonatomic hash tables.
 * SETENTRYATOMIC() sets a link to point to a new entry
 * if the current value is the same as the old entry.
 * It returns TRUE on success.
 */

typedef Atomic_Ptr HashTableLink;

#define ENTRY(l) ((HashTableEntry *) Atomic_ReadPtr(&(l)))
#define SETENTRY(l, e) Atomic_WritePtr(&(l), e)
#ifdef NO_ATOMIC_HASHTABLE
#define SETENTRYATOMIC(l, old, new) (Atomic_WritePtr(&(l), new), TRUE)
#else
#define SETENTRYATOMIC(l, old, new) \
   (Atomic_ReadIfEqualWritePtr(&(l), old, new) == (old))
#endif

/*
 * An entry in the hashtable.
 */

typedef struct HashTableEntry {
   HashTableLink     next;
   const void       *keyStr;
   Atomic_Ptr        clientData;
} HashTableEntry;

/*
 * The hashtable structure.
 */

struct HashTable {
   uint32                 numEntries;
   uint32                 numBits;
   int                    keyType;
   Bool                   atomic;
   Bool                   copyKey;
   HashTableFreeEntryFn   freeEntryFn;
   HashTableLink         *buckets;

   size_t                 numElements;
};


/*
 * Local functions
 */

HashTableEntry *HashTableLookupOrInsert(HashTable *ht,
                                        const void *keyStr,
                                        void *clientData);


/*
 *-----------------------------------------------------------------------------
 *
 * HashTableComputeHash --
 *
 *      Compute hash value based on key type and hash size.
 *
 * Results:
 *      The hash value.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static uint32
HashTableComputeHash(const HashTable *ht,  // IN: hash table
                     const void *s)        // IN: string to hash
{
   uint32 h = 0;

   switch (ht->keyType) {
   case HASH_STRING_KEY: {
         int c;
         unsigned char *keyPtr = (unsigned char *) s;

         while ((c = *keyPtr++)) {
            h ^= c;
            h = h << HASH_ROTATE | h >> (32 - HASH_ROTATE);
         }
      }
      break;
   case HASH_ISTRING_KEY: {
         int c;
         unsigned char *keyPtr = (unsigned char *) s;

         while ((c = tolower(*keyPtr++))) {
            h ^= c;
            h = h << HASH_ROTATE | h >> (32 - HASH_ROTATE);
         }
      }
      break;
   case HASH_INT_KEY:
      ASSERT_ON_COMPILE(sizeof s == 4 || sizeof s == 8);

      if (sizeof s == 4) {
         h = (uint32) (uintptr_t) s;
      } else {
         /*
          * Coverity warns that s >> 32 will always equate to 0 regardless of
          * what s may be. This is true when uintptr_t is 32 bits, but
          * not when it is 64 bit.
          */
         /* coverity[result_independent_of_operands] */
         h = (uint32) (uintptr_t) s ^ (uint32) ((uint64) (uintptr_t) s >> 32);
      }
      h *= 48271;  // http://www.google.com/search?q=48271+pseudorandom
      break;
   default:
      NOT_REACHED();
   }

   {
      int numBits = ht->numBits;
      uint32 mask = MASK(numBits);

      for (; h > mask; h = (h & mask) ^ (h >> numBits)) {
      }
   }

   ASSERT(h < ht->numEntries);

   return h;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HashTableEqualKeys --
 *
 *      Compare two keys based on key type
 *
 * Results:
 *      TRUE if keys are equal.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HashTableEqualKeys(const HashTable *ht,  // IN: hash table
                   const void *key1,     // IN: key
                   const void *key2)     // IN: key
{
   switch (ht->keyType) {
   case HASH_STRING_KEY:
      return Str_Strcmp((const char *) key1, (const char *) key2) == 0;

   case HASH_ISTRING_KEY:
      return Str_Strcasecmp((const char *) key1, (const char *) key2) == 0;

   default:
      return key1 == key2;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_Alloc --
 *
 *      Create a hash table.
 *
 * Results:
 *      The new hashtable.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

HashTable *
HashTable_Alloc(uint32 numEntries,        // IN: must be a power of 2
                int keyType,              // IN: whether keys are strings
                HashTableFreeEntryFn fn)  // IN: free entry function
{
   HashTable *ht;

   ASSERT(numEntries > 0);
   if ((numEntries & (numEntries - 1)) != 0) {
      Panic("%s only takes powers of 2 \n", __FUNCTION__);
   }
#ifdef NO_ATOMIC_HASHTABLE
   VERIFY((keyType & HASH_FLAG_ATOMIC) == 0);
#endif
   ASSERT((keyType & HASH_FLAG_COPYKEY) == 0 ||
          ((keyType & HASH_TYPE_MASK) == HASH_STRING_KEY ||
           (keyType & HASH_TYPE_MASK) == HASH_ISTRING_KEY));

   ht = Util_SafeMalloc(sizeof *ht);

   ht->numBits = lssb32_0(numEntries);
   ht->numEntries = numEntries;
   ht->keyType = keyType & HASH_TYPE_MASK;
   ht->atomic = (keyType & HASH_FLAG_ATOMIC) != 0;
   ht->copyKey = (keyType & HASH_FLAG_COPYKEY) != 0;
   ht->freeEntryFn = fn;
   ht->buckets = Util_SafeCalloc(ht->numEntries, sizeof *ht->buckets);
   ht->numElements = 0;

   return ht;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_AllocOnce --
 *
 *      Create a hash table and store it in the supplied Atomic_Ptr,
 *      unless it's already been created.
 *
 * Results:
 *      The new (or existing) hashtable.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

HashTable *
HashTable_AllocOnce(Atomic_Ptr *var,          // IN/OUT: the atomic var
                    uint32 numEntries,        // IN: must be a power of 2
                    int keyType,              // IN: whether keys are strings
                    HashTableFreeEntryFn fn)  // IN: free entry function
{
   HashTable *ht;

   if ((ht = Atomic_ReadPtr(var)) == NULL) {
      HashTable *new = HashTable_Alloc(numEntries, keyType, fn);
#ifdef NO_ATOMIC_HASHTABLE
      Atomic_WritePtr(var, new);
#else
      ht = Atomic_ReadIfEqualWritePtr(var, NULL, new);
#endif
      if (ht == NULL) {
         ht = new;
      } else {
         HashTable_FreeUnsafe(new);
      }
   }
   ASSERT(ht == Atomic_ReadPtr(var));

   return ht;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_Clear --
 *
 *      Clear all entries a hashtable by freeing them.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
HashTableClearInternal(HashTable *ht)  // IN/OUT:
{
   int i;

   ht->numElements = 0;

   for (i = 0; i < ht->numEntries; i++) {
      HashTableEntry *entry;

      while ((entry = ENTRY(ht->buckets[i])) != NULL) {
         SETENTRY(ht->buckets[i], ENTRY(entry->next));
         if (ht->copyKey) {
            free((void *) entry->keyStr);
         }
         if (ht->freeEntryFn) {
            ht->freeEntryFn(Atomic_ReadPtr(&entry->clientData));
         }
         free(entry);
      }
   }
}


void
HashTable_Clear(HashTable *ht)  // IN/OUT:
{
   ASSERT(ht);
   ASSERT(!ht->atomic);

   HashTableClearInternal(ht);
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_Free --
 *
 *      Free the hash table skeleton. Do not call this if the hashTable is
 *      atomic; use HashTable_FreeUnsafe and HEED THE RESTRICTIONS!
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
HashTable_Free(HashTable *ht)  // IN/OUT:
{
   if (ht != NULL) {
      ASSERT(!ht->atomic);

      HashTableClearInternal(ht);

      free(ht->buckets);
      free(ht);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_FreeUnsafe --
 *
 *      HashTable_Free for atomic hash tables.  Non-atomic hash tables
 *      should always use HashTable_Free.  The caller should make sure
 *      no other thread may access the hash table when this is called.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
HashTable_FreeUnsafe(HashTable *ht)  // IN/OUT:
{
   if (ht != NULL) {
      HashTableClearInternal(ht);

      free(ht->buckets);
      free(ht);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HashTableLookup --
 *
 *      Core of the lookup function.
 *
 * Results:
 *      A pointer to the found HashTableEntry or NULL if not found
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static HashTableEntry *
HashTableLookup(const HashTable *ht,  // IN:
                const void *keyStr,   // IN:
                uint32 hash)          // IN:
{
   HashTableEntry *entry;

   for (entry = ENTRY(ht->buckets[hash]);
        entry != NULL;
        entry = ENTRY(entry->next)) {
      if (HashTableEqualKeys(ht, entry->keyStr, keyStr)) {
         return entry;
      }
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_Lookup --
 *
 *      Lookup an element in a hashtable.
 *
 * Results:
 *      TRUE if the entry was found. (the clientData is set)
 *      FALSE otherwise
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
HashTable_Lookup(const HashTable  *ht, // IN:
                 const void *keyStr,   // IN:
                 void **clientData)    // OUT/OPT:
{
   uint32 hash = HashTableComputeHash(ht, keyStr);
   HashTableEntry *entry = HashTableLookup(ht, keyStr, hash);

   if (entry == NULL) {
      return FALSE;
   }

   if (clientData) {
      *clientData = Atomic_ReadPtr(&entry->clientData);
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_Delete --
 *
 *      Delete an element from the hashtable.
 *
 * Results:
 *      TRUE if the entry was found and subsequently removed
 *      FALSE otherwise
 *
 * Side effects:
 *      See above
 *
 *----------------------------------------------------------------------
 */

Bool
HashTable_Delete(HashTable  *ht,      // IN/OUT: the hash table
                 const void *keyStr)  // IN: key for the element to remove
{
   return HashTable_LookupAndDelete(ht, keyStr, NULL);
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_LookupAndDelete --
 *
 *      Look up an element in the hash table, then delete it.
 *
 *      If clientData is specified, then the entry data is returned,
 *      and the free function is not called.
 *
 * Results:
 *      TRUE if the entry was found and subsequently removed
 *      FALSE otherwise
 *      Entry value is returned in *clientData.
 *
 * Side effects:
 *      See above
 *
 *----------------------------------------------------------------------
 */

Bool
HashTable_LookupAndDelete(HashTable *ht,       // IN/OUT: the hash table
                          const void *keyStr,  // IN: key for the element
                          void **clientData)   // OUT: return data
{
   uint32 hash = HashTableComputeHash(ht, keyStr);
   HashTableLink *linkp;
   HashTableEntry *entry;

   ASSERT(!ht->atomic);

   for (linkp = &ht->buckets[hash];
        (entry = ENTRY(*linkp)) != NULL;
        linkp = &entry->next) {
      if (HashTableEqualKeys(ht, entry->keyStr, keyStr)) {
         SETENTRY(*linkp, ENTRY(entry->next));
         ht->numElements--;
         if (ht->copyKey) {
            free((void *) entry->keyStr);
         }
         if (clientData != NULL) {
            *clientData = Atomic_ReadPtr(&entry->clientData);
         } else if (ht->freeEntryFn) {
            ht->freeEntryFn(Atomic_ReadPtr(&entry->clientData));
         }
         free(entry);

         return TRUE;
      }
   }

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_Insert --
 *
 *      Insert an element into the hashtable.
 *
 *      Unless the hash table was created with HASH_FLAG_COPYKEY,
 *      the string key is not duplicated and thus cannot be freed until
 *      the entry is deleted or the hash table is cleared or freed.
 *
 * Results:
 *      FALSE if item already exists.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
HashTable_Insert(HashTable  *ht,          // IN/OUT:
                 const void *keyStr,      // IN:
                 void       *clientData)  // IN/OPT:
{
   return HashTableLookupOrInsert(ht, keyStr, clientData) == NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_LookupOrInsert --
 *
 *      Look up an a key, return the element if found.
 *      Otherwise, insert it into the hashtable and return it.
 *
 *      Unless the hash table was created with HASH_FLAG_COPYKEY,
 *      the string key is not duplicated and thus cannot be freed until
 *      the entry is deleted or the hash table is cleared or freed.
 *
 * Results:
 *      Old element or new one.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void *
HashTable_LookupOrInsert(HashTable  *ht,          // IN/OUT:
                         const void *keyStr,      // IN:
                         void       *clientData)  // IN:
{
   HashTableEntry *entry = HashTableLookupOrInsert(ht, keyStr, clientData);

   return entry == NULL ? clientData : Atomic_ReadPtr(&entry->clientData);
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_ReplaceOrInsert --
 *
 *      Look up an a key.  If found, replace the existing clientData
 *      and return TRUE.
 *      Otherwise, insert new entry and return FALSE.
 *
 *      Unless the hash table was created with HASH_FLAG_COPYKEY,
 *      the string key is not duplicated and thus cannot be freed until
 *      the entry is deleted or the hash table is cleared or freed.
 *
 * Results:
 *      TRUE if replaced, FALSE if inserted.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
HashTable_ReplaceOrInsert(HashTable  *ht,          // IN/OUT:
                          const void *keyStr,      // IN:
                          void       *clientData)  // IN:
{
   HashTableEntry *entry = HashTableLookupOrInsert(ht, keyStr, clientData);

   if (entry == NULL) {
      return FALSE;
   }

#ifndef NO_ATOMIC_HASHTABLE
   if (ht->atomic && ht->freeEntryFn) {
      void *old = Atomic_ReadWritePtr(&entry->clientData, clientData);

      ht->freeEntryFn(old);
   } else
#endif
   {
      if (ht->freeEntryFn) {
         ht->freeEntryFn(Atomic_ReadPtr(&entry->clientData));
      }
      Atomic_WritePtr(&entry->clientData, clientData);
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_ReplaceIfEqual --
 *
 *      Look up an a key.  If found, replace the existing clientData
 *      if it is the same as oldClientData and return TRUE.
 *      Return FALSE otherwise.
 *
 * Results:
 *      TRUE if replaced, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
HashTable_ReplaceIfEqual(HashTable *ht,        // IN/OUT:
                         const void *keyStr,   // IN:
                         void *oldClientData,  // IN/OPT:
                         void *newClientData)  // IN/OPT:
{
   uint32 hash = HashTableComputeHash(ht, keyStr);
   HashTableEntry *entry = HashTableLookup(ht, keyStr, hash);
   Bool retval = FALSE;

   if (entry == NULL) {
      return FALSE;
   }

#ifndef NO_ATOMIC_HASHTABLE
   if (ht->atomic) {
      void *data = Atomic_ReadIfEqualWritePtr(&entry->clientData,
                                              oldClientData, newClientData);
      if (data == oldClientData) {
         retval = TRUE;
         if (ht->freeEntryFn != NULL) {
            ht->freeEntryFn(data);
         }
      }
   } else
#endif
   if (Atomic_ReadPtr(&entry->clientData) == oldClientData) {
      retval = TRUE;
      if (ht->freeEntryFn) {
         ht->freeEntryFn(Atomic_ReadPtr(&entry->clientData));
      }
      Atomic_WritePtr(&entry->clientData, newClientData);
   }

   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTableLookupOrInsert --
 *
 *      Look up an a key, return the entry if found.
 *      Otherwise, insert it into the hashtable and return NULL.
 *
 * Results:
 *      Old HashTableEntry or NULL.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

HashTableEntry *
HashTableLookupOrInsert(HashTable *ht,       // IN/OUT:
                        const void *keyStr,  // IN:
                        void *clientData)    // IN/OPT:
{
   uint32 hash = HashTableComputeHash(ht, keyStr);
   HashTableEntry *entry = NULL;
   HashTableEntry *oldEntry = NULL;
   HashTableEntry *head;

again:
   head = ENTRY(ht->buckets[hash]);

   oldEntry = HashTableLookup(ht, keyStr, hash);
   if (oldEntry != NULL) {
      if (entry != NULL) {
         if (ht->copyKey) {
            free((void *) entry->keyStr);
         }
         free(entry);
      }

      return oldEntry;
   }

   if (entry == NULL) {
      entry = Util_SafeMalloc(sizeof *entry);
      if (ht->copyKey) {
         entry->keyStr = Util_SafeStrdup(keyStr);
      } else {
         entry->keyStr = keyStr;
      }
      Atomic_WritePtr(&entry->clientData, clientData);
   }
   SETENTRY(entry->next, head);
   if (ht->atomic) {
      if (!SETENTRYATOMIC(ht->buckets[hash], head, entry)) {
         goto again;
      }
   } else {
      SETENTRY(ht->buckets[hash], entry);
   }

   ht->numElements++;

   return NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_GetNumElements --
 *
 *      Get the number of elements in the hash table.
 *
 *      Atomic hash tables do not support this function because
 *      the numElements field is not modified atomically so may be
 *      incorrect.
 *
 * Results:
 *      The number of elements.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

size_t
HashTable_GetNumElements(const HashTable *ht)  // IN:
{
   ASSERT(ht);
   ASSERT(!ht->atomic);

   return ht->numElements;
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_KeyArray --
 *
 *      Returns an array of pointers to each key in the hash table.
 *
 * Results:
 *      The key array plus its size. If the hash table is empty
 *      'size' is 0 and 'keys' is NULL. Free 'keys' array with free(). Do *not*
 *      free the individual elements of the array.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
HashTable_KeyArray(const HashTable *ht,  // IN:
                   const void ***keys,   // OUT:
                   size_t *size)         // OUT:
{
   uint32 i;
   size_t j;

   ASSERT(ht);
   ASSERT(keys);
   ASSERT(size);

   ASSERT(!ht->atomic);

   *keys = NULL;
   *size = HashTable_GetNumElements(ht);

   if (*size == 0) {
      return;
   }

   /* alloc array */
   *keys = Util_SafeMalloc(*size * sizeof **keys);

   /* fill array */
   for (i = 0, j = 0; i < ht->numEntries; i++) {
      HashTableEntry *entry;

      for (entry = ENTRY(ht->buckets[i]);
           entry != NULL;
           entry = ENTRY(entry->next)) {
         (*keys)[j++] = entry->keyStr;
      }
   }
}



/*
 *----------------------------------------------------------------------
 *
 * HashTable_ToArray --
 *
 *      Returns an array of pointers to each clientData structure in the
 *      hash table.
 *
 * Results:
 *      The clientData array plus its size. If the hash table is empty
 *      'size' is 0 and 'clientDatas' is NULL. Free with free().
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
HashTable_ToArray(const HashTable *ht,  // IN:
                  void ***clientDatas,  // OUT:
                  size_t *size)         // OUT:
{
   uint32 i;
   size_t j;

   ASSERT(ht);
   ASSERT(clientDatas);
   ASSERT(size);

   ASSERT(!ht->atomic);

   *clientDatas = NULL;
   *size = HashTable_GetNumElements(ht);

   if (*size == 0) {
      return;
   }

   /* alloc array */
   *clientDatas = Util_SafeMalloc(*size * sizeof **clientDatas);

   /* fill array */
   for (i = 0, j = 0; i < ht->numEntries; i++) {
      HashTableEntry *entry;

      for (entry = ENTRY(ht->buckets[i]);
           entry != NULL;
           entry = ENTRY(entry->next)) {
         (*clientDatas)[j++] = Atomic_ReadPtr(&entry->clientData);
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HashTable_ForEach --
 *
 *      Walks the hashtable in an undetermined order, calling the
 *      callback function for each value until either the callback
 *      returns a non-zero value or all values have been walked
 *
 * Results:
 *      0 if all callback functions returned 0, otherwise the return
 *      value of the first non-zero callback.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
HashTable_ForEach(const HashTable *ht,          // IN:
                  HashTableForEachCallback cb,  // IN:
                  void *clientData)             // IN:
{
   int i;

   ASSERT(ht);
   ASSERT(cb);

   for (i = 0; i < ht->numEntries; i++) {
      HashTableEntry *entry;

      for (entry = ENTRY(ht->buckets[i]);
           entry != NULL;
           entry = ENTRY(entry->next)) {
         int result = (*cb)(entry->keyStr, Atomic_ReadPtr(&entry->clientData),
                            clientData);

         if (result) {
            return result;
         }
      }
   }

   return 0;
}

#if 0
/*
 *----------------------------------------------------------------------
 *
 * HashPrint --
 *
 *      Print out the contents of a hashtable. Useful for
 *      debugging the data structure & the hashing algorithm.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
HashPrint(HashTable *ht) // IN
{
   int i;

   for (i = 0; i < ht->numEntries; i++) {
      HashTableEntry *entry;

      if (ht->buckets[i] == NULL) {
         continue;
      }

      printf("%4d: \n", i);

      for (entry = ENTRY(ht->buckets[i]);
           entry != NULL;
           entry = ENTRY(entry->next)) {
         if (ht->keyType == HASH_INT_KEY) {
            printf("\t%p\n", entry->keyStr);
         } else {
            printf("\t%s\n", entry->keyStr);
         }
      }
   }
}
#endif
  0707010000018D000081A40000000000000000000000016822550500001072000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/hostType.c    /*********************************************************
 * Copyright (C) 2006-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hostType.c --
 *
 *    Platform-independent code that determines the host OS type.
 */

#include <string.h>
#ifdef __linux__
#include <sys/utsname.h>
#include <unistd.h>
#endif

#include "vmware.h"
#include "hostType.h"


/*
 *----------------------------------------------------------------------
 *
 * HostType_OSIsVMK --
 *
 *      Are we running on a flavor of VMKernel?
 *
 *      Per bug 651592 comment #3 and elsewhere:
 *      - all ESX/ESXi compilation defines __linux__ because vmkernel
 *        implements the int80 Linux syscall family.
 *      - ESXi 5.0 and later set utsname.sysname to "VMkernel".
 *      - ESXi 3.5, 4.0, and 4.1 have unverified behavior.
 *      - ESX Classic through 4.x set utsname.sysname to "Linux".
 *      This implementation of the code assumes Classic mode does not
 *      exist and ESXi is at least version 5, which covers all versions
 *      currently supported by this codebase and happens to be a much
 *      simpler matrix.
 *
 *      Should it ever become necessary to revive the check for Classic,
 *      checking for the existence of "/usr/lib/vmware/vmkernel" would
 *      suffice.
 *
 * Results:
 *      TRUE if running in a UserWorld on the vmkernel in ESX.
 *      FALSE if running in a non-ESX product.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
HostType_OSIsVMK(void)
{
#if defined __linux__
   /*
    * Implementation note: it might make sense to short-circuit this whole
    * clause with "if (vmx86_server)" so that non-ESX builds get an even
    * cheaper test. I have chosen not to do so: some non-ESX code (e.g. fdm)
    * needs this check to act properly under a VMX86_VPX conditional. Saving
    * a couple of cycles is not worth the effort of maintaining a product-
    * specific macro in multi-product library code.
    */
   static enum {
      VMKTYPE_UNKNOWN = 0,  // to make default in .bss
      VMKTYPE_LINUX,
      VMKTYPE_VMKERNEL,
   } vmkernelType = VMKTYPE_UNKNOWN;

   if (vmkernelType == VMKTYPE_UNKNOWN) {
      struct utsname name;
      int rc;

      rc = uname(&name);
      if (rc == 0 && strcmp("VMkernel", name.sysname) == 0) {
         vmkernelType = VMKTYPE_VMKERNEL;
      } else {
         vmkernelType = VMKTYPE_LINUX;
      }
   }
   return vmkernelType == VMKTYPE_VMKERNEL;
#else
   /* Non-linux builds are never running in a userworld */
   return FALSE;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * HostType_OSIsSimulator --
 *
 *      Are we running on an ESX host simulator? Check presence of the
 *      mockup file.
 *
 * Results:
 *      TRUE if mockup file exists
 *      FALSE if the file doesn't exist
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
HostType_OSIsSimulator(void)
{
#if defined __linux__ && (defined VMX86_SERVER || defined VMX86_VPX)
   static int simulatorType = -1;
   if (simulatorType == -1) {
      if (access("/etc/vmware/hostd/mockupEsxHost.txt", 0) != -1) {
         simulatorType = 1;
      } else {
         simulatorType = 0;
      }
   }
   return (simulatorType == 1);
#else
   /* We are only interested in the case where VMX86_SERVER or friends are defined */
   return FALSE;
#endif
}
  0707010000018E000081A4000000000000000000000001682255050000263B000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/hostinfo.c    /*********************************************************
 * Copyright (C) 1998-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hostinfo.c --
 *
 *    Platform-independent code that calls into hostinfo<OS>-specific
 *    code.
 */

#include "vmware.h"
#include <string.h>

#if defined(__i386__) || defined(__x86_64__)
#include "x86cpuid_asm.h"
#endif
#include "hostinfo.h"
#include "hostinfoInt.h"
#include "str.h"
#include "util.h"
#include "str.h"
#include "dynbuf.h"
#include "backdoor_def.h"

#define LOGLEVEL_MODULE hostinfo
#include "loglevel_user.h"

#define LGPFX "HOSTINFO:"


/*
 * HostinfoOSData caches its returned value.
 */

volatile Bool hostinfoCacheValid = FALSE;
char          hostinfoCachedOSName[MAX_OS_NAME_LEN];
char          hostinfoCachedOSFullName[MAX_OS_FULLNAME_LEN];
char          hostinfoCachedDetailedData[MAX_DETAILED_STRING_LEN];


#if defined(__i386__) || defined(__x86_64__)
/*
 *-----------------------------------------------------------------------------
 *
 * HostInfoGetCpuidStrSection --
 *
 *       Append a section (either low or high) of CPUID as a string in DynBuf.
 *       E.g.
 *          00000000:00000005756E65476C65746E49656E69-
 *          00000001:00000F4A000208000000649DBFEBFBFF-
 *       or
 *          80000000:80000008000000000000000000000000-
 *          80000001:00000000000000000000000120100000-
 *          80000008:00003024000000000000000000000000-
 *
 *       The returned eax of args[0] is used to determine the upper bound for
 *       the following input arguments. And the input args should be in
 *       ascending order.
 *
 * Results:
 *       None. The string will be appended in buf.
 *
 * Side effect:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

static void
HostInfoGetCpuidStrSection(const uint32 args[],    // IN: input eax arguments
                           const size_t args_size, // IN: size of the argument array
                           DynBuf *buf)            // IN/OUT: result string in DynBuf
{
   static const char format[] = "%08X:%08X%08X%08X%08X-";
   CPUIDRegs reg;
   uint32 max_arg;
   char temp[64];
   int i;

   __GET_CPUID(args[0], &reg);
   max_arg = reg.eax;
   if (max_arg < args[0]) {
      Warning(LGPFX" No CPUID information available. Based = %08X.\n",
              args[0]);
      return;
   }
   DynBuf_Append(buf, temp,
                 Str_Sprintf(temp, sizeof temp, format, args[0], reg.eax,
                             reg.ebx, reg.ecx, reg.edx));

   for (i = 1; i < args_size && args[i] <= max_arg; i++) {
      ASSERT(args[i] > args[i - 1]); // Ascending order.
      __GET_CPUID(args[i], &reg);

      DynBuf_Append(buf, temp,
                    Str_Sprintf(temp, sizeof temp, format, args[i], reg.eax,
                                reg.ebx, reg.ecx, reg.edx));
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetCpuidStr --
 *
 *       Get the basic and extended CPUID as a string. E.g.
 *          00000000:00000005756E65476C65746E49656E69-
 *          00000001:00000F4A000208000000649DBFEBFBFF-
 *          80000000:80000008000000000000000000000000-
 *          80000001:00000000000000000000000120100000-
 *          80000008:00003024000000000000000000000000
 *
 *       If the extended CPUID is not available, only returns the basic CPUID.
 *
 * Results:
 *       The CPUID string if the processor supports the CPUID instruction and
 *       this is a processor we recognize. It should never fail, since it
 *       would at least return leaf 0. Caller needs to free the returned string.
 *
 * Side effect:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_GetCpuidStr(void)
{
   static const uint32 basic_args[] = {0x0, 0x1, 0xa};
   static const uint32 extended_args[] = {0x80000000, 0x80000001, 0x80000008};
   DynBuf buf;
   char *result;

   DynBuf_Init(&buf);

   HostInfoGetCpuidStrSection(basic_args, ARRAYSIZE(basic_args), &buf);
   HostInfoGetCpuidStrSection(extended_args, ARRAYSIZE(extended_args), &buf);

   // Trim buffer and set NULL character to replace last '-'.
   DynBuf_Trim(&buf);
   result = (char*)DynBuf_Get(&buf);
   ASSERT(result && result[0]); // We should at least get result from eax = 0x0.
   result[DynBuf_GetSize(&buf) - 1] = '\0';

   return DynBuf_Detach(&buf);
}
#endif // defined(__i386__) || defined(__x86_64__)


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetCpuid --
 *
 *       Get cpuid information for a CPU.  Which CPU the information is for
 *       depends on the OS scheduler. We are assuming that all CPUs in
 *       the system have identical numbers of cores and threads.
 *
 * Results:
 *       TRUE if the processor supports the cpuid instruction and this
 *       is a process we recognize, FALSE otherwise.
 *
 * Side effect:
 *       None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Hostinfo_GetCpuid(HostinfoCpuIdInfo *info) // OUT
{
#if defined(__i386__) || defined(__x86_64__)
   CPUIDRegs regs = {0};

   /* Get basic and extended CPUID info. */

   __GET_CPUID(0, &regs);
   if (regs.eax == 0) {
      Warning(LGPFX" No CPUID information available.\n");
      return FALSE;
   }

   /* Calculate vendor information. */

   if (memcmp(&regs.ebx, CPUID_INTEL_VENDOR_STRING, 12) == 0) {
      info->vendor = CPUID_VENDOR_INTEL;
   } else if (memcmp(&regs.ebx, CPUID_AMD_VENDOR_STRING, 12) == 0) {
      info->vendor = CPUID_VENDOR_AMD;
   } else if (memcmp(&regs.ebx, CPUID_HYGON_VENDOR_STRING, 12) == 0) {
      info->vendor = CPUID_VENDOR_HYGON;
   } else {
      info->vendor = CPUID_VENDOR_UNKNOWN;
   }

   /* Pull out versioning and feature information. */

   __GET_CPUID(1, &regs);
   info->version = regs.eax;
   info->family = CPUID_GET(1, EAX, FAMILY, regs.eax);
   info->model = CPUID_GET(1, EAX, MODEL, regs.eax);
   info->stepping = CPUID_GET(1, EAX, STEPPING, regs.eax);
   info->type = CPUID_GET(1, EAX, TYPE, regs.eax);
   info->extfeatures = regs.ecx;
   info->features = regs.edx;

   return TRUE;
#else // defined(__i386__) || defined(__x86_64__)
   return FALSE;
#endif // defined(__i386__) || defined(__x86_64__)
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetOSName --
 *
 *      Query the operating system and build a string to identify it.
 *
 *      Examples:
 *         Windows: <OS NAME> <SERVICE PACK> (Build <BUILD_NUMBER>)
 *         example: Windows XP Professional Service Pack 2 (Build 2600)
 *
 *         Linux:   <OS NAME> <OS RELEASE> <SPECIFIC_DISTRO_INFO>
 *         example: Linux 2.4.18-3 Red Hat Linux release 7.3 (Valhalla)
 *
 *         Mac OS:  <OS NAME> <OS VERSION> (<BUILD VERSION>) <KERNEL NAME> <KERNEL RELEASE>
 *         example: Mac OS X 10.8.5 (12F45) Darwin 12.5.0
 *
 * Return value:
 *      NULL  Unable to obtain the OS name.
 *     !NULL  The OS name. The caller is responsible for freeing it.
 *
 * Side effects:
 *      Memory is allocated.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_GetOSName(void)
{
   char *name;
   Bool data = hostinfoCacheValid ? TRUE : HostinfoOSData();

   if (data) {
       name = Util_SafeStrdup(hostinfoCachedOSFullName);
   } else {
      name = NULL;
   }

   return name;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetOSGuestString --
 *
 *      Query the operating system and build a string to identify it. The
 *      returned string is the same as you'd see in a VM's .vmx file.
 *
 * Return value:
 *      NULL  Unable to obtain the OS name.
 *     !NULL  The OS name. The caller is responsible for freeing it.
 *
 * Side effects:
 *      Memory is allocated.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_GetOSGuestString(void)
{
   char *name;
   Bool data = hostinfoCacheValid ? TRUE : HostinfoOSData();

   if (data) {
       name = Util_SafeStrdup(hostinfoCachedOSName);
   } else {
      name = NULL;
   }

   return name;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetOSDetailedData --
 *
 *      Query the operating system and build a property list containing
 *      detailed information about the guest OS.
 *
 * Return value:
 *      NULL  Unable to obtain the detailed data.
 *     !NULL  The detailed data string. The caller must free it.
 *
 * Side effects:
 *      Memory is allocated.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_GetOSDetailedData(void)
{
   char *detailedData;
   Bool data = hostinfoCacheValid ? TRUE : HostinfoOSData();

   if (data) {
      detailedData = Util_SafeStrdup(hostinfoCachedDetailedData);
   } else {
      detailedData = NULL;
   }

   return detailedData;
}
 0707010000018F000081A400000000000000000000000168225505000063CA000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/hostinfoHV.c  /*********************************************************
 * Copyright (C) 2011-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hostinfoHV.c --
 *
 *    Code to detect different hypervisors and features.
 */

#include <string.h>
#include "vmware.h"
#if defined(__i386__) || defined(__x86_64__)
#  include "x86cpuid_asm.h"
#endif
#if defined __i386__ || defined __x86_64__ || defined __aarch64__
#  include "backdoor_def.h"
#  include "backdoor_types.h"
#endif
#include "hostinfo.h"
#include "util.h"

#define LGPFX "HOSTINFO:"

#define LOGLEVEL_MODULE hostinfo
#include "loglevel_user.h"

#if !defined(_WIN32)

/*
 * Are vmcall/vmmcall available in the compiler?
 */
#if defined(__linux__) && defined(__GNUC__)
#define GCC_VERSION (__GNUC__ * 10000 + \
                     __GNUC_MINOR__ * 100 + \
                     __GNUC_PATCHLEVEL__)
#if GCC_VERSION > 40803 && !defined(__aarch64__)
#define USE_HYPERCALL
#endif
#endif

#define BDOOR_FLAGS_LB_READ (BDOOR_FLAGS_LB | BDOOR_FLAGS_READ)

#define Vmcall(cmd, result)              \
   __asm__ __volatile__(                 \
      "vmcall"                           \
      : "=a" (result)                    \
      : "0"  (BDOOR_MAGIC),              \
        "c"  (cmd),                      \
        "d"  (BDOOR_FLAGS_LB_READ)       \
   )

#define Vmmcall(cmd, result)             \
   __asm__ __volatile__(                 \
      "vmmcall"                          \
      : "=a" (result)                    \
      : "0"  (BDOOR_MAGIC),              \
        "c"  (cmd),                      \
        "d"  (BDOOR_FLAGS_LB_READ)       \
   )

#define Ioportcall(cmd, result)          \
   __asm__ __volatile__(                 \
      "inl %%dx, %%eax"                  \
      : "=a" (result)                    \
      : "0"  (BDOOR_MAGIC),              \
        "c"  (cmd),                      \
        "d"  (BDOOR_PORT)                \
   )
#endif

/*
 *----------------------------------------------------------------------
 *
 *  HostinfoBackdoorGetInterface --
 *
 *      Check whether hypercall is present or backdoor is being used.
 *
 * Results:
 *      BACKDOOR_INTERFACE_VMCALL  - Intel hypercall is used.
 *      BACKDOOR_INTERFACE_VMMCALL - AMD hypercall is used.
 *      BACKDOOR_INTERFACE_IO      - Backdoor I/O Port is used.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */
#if defined(__i386__) || defined(__x86_64__)
static BackdoorInterface
HostinfoBackdoorGetInterface(void)
{
#if defined(USE_HYPERCALL)
   /* Setting 'interface' is idempotent, no atomic access is required. */
   static BackdoorInterface interface = BACKDOOR_INTERFACE_NONE;

   if (UNLIKELY(interface == BACKDOOR_INTERFACE_NONE)) {
#if defined(__i386__) || defined(__x86_64__)
      CPUIDRegs regs;

      /* Check whether we're on a VMware hypervisor that supports vmmcall. */
      __GET_CPUID(CPUID_FEATURE_INFORMATION, &regs);
      if (CPUID_ISSET(CPUID_FEATURE_INFORMATION, ECX, HYPERVISOR, regs.ecx)) {
         __GET_CPUID(CPUID_HYPERVISOR_LEVEL_0, &regs);
         if (CPUID_IsRawVendor(&regs, CPUID_VMWARE_HYPERVISOR_VENDOR_STRING)) {
            if (__GET_EAX_FROM_CPUID(CPUID_HYPERVISOR_LEVEL_0) >=
                                     CPUID_VMW_FEATURES) {
               uint32 features = __GET_ECX_FROM_CPUID(CPUID_VMW_FEATURES);
               if (CPUID_ISSET(CPUID_VMW_FEATURES, ECX,
                               VMCALL_BACKDOOR, features)) {
                  interface = BACKDOOR_INTERFACE_VMCALL;
               } else if (CPUID_ISSET(CPUID_VMW_FEATURES, ECX,
                                      VMMCALL_BACKDOOR, features)) {
                  interface = BACKDOOR_INTERFACE_VMMCALL;
               }
            }
         }
      }
      if (interface == BACKDOOR_INTERFACE_NONE) {
         interface = BACKDOOR_INTERFACE_IO;
      }
#else
      interface = BACKDOOR_INTERFACE_IO;
#endif
   }
   return interface;
#else
   return BACKDOOR_INTERFACE_IO;
#endif
}
#endif // defined(__i386__) || defined(__x86_64__)

/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_HypervisorPresent --
 *
 *      Check if hypervisor is present.
 *
 * Results:
 *      TRUE iff hypervisor cpuid bit is present.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

#if defined(__i386__) || defined(__x86_64__)
static Bool
Hostinfo_HypervisorPresent(void)
{
   static Bool hypervisorPresent;
   CPUIDRegs regs;

   if (!hypervisorPresent) {
      __GET_CPUID(CPUID_FEATURE_INFORMATION, &regs);
      hypervisorPresent = CPUID_ISSET(CPUID_FEATURE_INFORMATION, ECX,
                                      HYPERVISOR, regs.ecx);
   }
   return hypervisorPresent;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_HypervisorCPUIDSig --
 *
 *      Get the hypervisor signature string from CPUID.
 *
 * Results:
 *      Unqualified 16 byte nul-terminated hypervisor string
 *	String may contain garbage and caller must free
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

char *
Hostinfo_HypervisorCPUIDSig(void)
{
   uint32 *name = NULL;
#if defined(__i386__) || defined(__x86_64__)
   CPUIDRegs regs;

   if (!Hostinfo_HypervisorPresent()) {
      return NULL;
   }

   __GET_CPUID(0x40000000, &regs);
   if (regs.eax < 0x40000000) {
      Log(LGPFX" CPUID hypervisor bit is set, but no "
          "hypervisor vendor signature is present.\n");
   }

   name = Util_SafeMalloc(4 * sizeof *name);

   name[0] = regs.ebx;
   name[1] = regs.ecx;
   name[2] = regs.edx;
   name[3] = 0;
#endif // defined(__i386__) || defined(__x86_64__)

   return (char *)name;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_LogHypervisorCPUID --
 *
 *      Logs hypervisor CPUID leafs.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

void
Hostinfo_LogHypervisorCPUID(void)
{
#if defined(__i386__) || defined(__x86_64__)
   CPUIDRegs regs;
   uint32 maxLeaf;
   uint32 leafId;
   if (!Hostinfo_HypervisorPresent()) {
      Log(LGPFX" Hypervisor not found. CPUID hypervisor bit is not set.\n");
      return;
   }

   __GET_CPUID(0x40000000, &regs);
   maxLeaf = regs.eax > 0x400000FF ? 0x400000FF : regs.eax;
   if (maxLeaf < 0x40000000) {
      Log(LGPFX" CPUID hypervisor bit is set, but no "
          "hypervisor vendor signature is present.\n");
      return;
   } else {
      Log("CPUID level   %10s   %10s   %10s   %10s\n", "EAX", "EBX",
          "ECX", "EDX");
      for (leafId = 0x40000000; leafId <= maxLeaf; leafId++) {
         __GET_CPUID(leafId, &regs);
         Log("0x%08x    0x%08x   0x%08x   0x%08x   0x%08x\n",
             leafId, regs.eax, regs.ebx, regs.ecx, regs.edx);
      }
   }
#endif // defined(__i386__) || defined(__x86_64__)

   return;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_HypervisorInterfaceSig --
 *
 *      Get hypervisor interface signature string from CPUID.
 *
 * Results:
 *      Unqualified 8 byte nul-terminated hypervisor interface signature string.
 *      String may contain garbage and caller must free.
 *      NULL if hypervisor is not present.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

char *
Hostinfo_HypervisorInterfaceSig(void)
{
   uint32 *intrSig = NULL;
#if defined(__i386__) || defined(__x86_64__)
   CPUIDRegs regs;

   if (!Hostinfo_HypervisorPresent()) {
      return NULL;
   }

   __GET_CPUID(0x40000000, &regs);
   if (regs.eax < 0x40000001) {
      Log(LGPFX" CPUID hypervisor bit is set, but no "
          "hypervisor interface signature is present.\n");
      return NULL;
   }

   __GET_CPUID(0x40000001, &regs);
   if (regs.eax != 0) {
      intrSig = Util_SafeMalloc(2 * sizeof *intrSig);
      intrSig[0] = regs.eax;
      intrSig[1] = 0;
   }
#endif // defined(__i386__) || defined(__x86_64__)

   return (char *)intrSig;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_TouchXen --
 *
 *      Check for Xen.
 *
 *      Official way is to call Hostinfo_HypervisorCPUIDSig(), which
 *         returns a hypervisor string.  This is a secondary check
 *	   that guards against a backdoor failure.  See PR156185,
 *         http://xenbits.xensource.com/xen-unstable.hg?file/6a383beedf83/tools/misc/xen-detect.c
 *      (Canonical way is /proc/xen, but CPUID is better).
 *
 * Results:
 *      TRUE if we are running in a Xen dom0 or domU.
 *      Linux:
 *         Illegal instruction exception on real hardware.
 *         Obscure Xen implementations might return FALSE.
 *      Windows:
 *         FALSE on real hardware.
 *
 * Side effects:
 *	Linux: Will raise exception on native hardware.
 *	Windows: None.
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_TouchXen(void)
{
#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
#define XEN_CPUID 0x40000000
   CPUIDRegs regs;
   uint32 name[4];

   /*
    * PV mode: ud2a "xen" cpuid (faults on native hardware).
    * (Only Linux can run PV, so skip others here).
    * Since PV cannot trap CPUID, this is a Xen hook.
    */

   regs.eax = XEN_CPUID;
   __asm__ __volatile__(
      "xchgl %%ebx, %0"  "\n\t"
      "ud2a ; .ascii \"xen\" ; cpuid" "\n\t"
      "xchgl %%ebx, %0"
      : "=&r" (regs.ebx), "=&c" (regs.ecx), "=&d" (regs.edx)
      : "a" (regs.eax)
   );

   name[0] = regs.ebx;
   name[1] = regs.ecx;
   name[2] = regs.edx;
   name[3] = 0;

   if (strcmp(CPUID_XEN_HYPERVISOR_VENDOR_STRING, (const char*)name) == 0) {
      return TRUE;
   }

   /* Passed checks.  But native and anything non-Xen would #UD before here. */
   NOT_TESTED();
   Log("Xen detected but hypervisor unrecognized (Xen variant?)\n");
   Log("CPUID 0x4000 0000: eax=%x ebx=%x ecx=%x edx=%x\n",
       regs.eax, regs.ebx, regs.ecx, regs.edx);
#endif

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_HyperV --
 *
 *      Check for HyperV.
 *
 * Results:
 *      TRUE if we are running in HyperV.
 *      FALSE otherwise.
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_HyperV(void)
{
   Bool hyperVAvailable = FALSE;
#if defined(__i386__) || defined(__x86_64__)
   char *hypervisorSig = Hostinfo_HypervisorCPUIDSig();

   if (hypervisorSig) {
      if (!memcmp(CPUID_HYPERV_HYPERVISOR_VENDOR_STRING, hypervisorSig,
                  sizeof CPUID_HYPERV_HYPERVISOR_VENDOR_STRING)) {
         hyperVAvailable = TRUE;
      }
      free(hypervisorSig);
   }
#endif

   return hyperVAvailable;
}


#if defined(_WIN32)

#if defined(_WIN64)
// from touchBackdoorMasm64.asm
void Hostinfo_BackdoorInOut(Backdoor_proto *myBp);
#endif


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_TouchBackDoor --
 *
 *      Access the backdoor. This is used to determine if we are
 *      running in a VM or on a physical host. On a physical host
 *      this should generate a GP which we catch and thereby determine
 *      that we are not in a VM. However some OSes do not handle the
 *      GP correctly and the process continues running returning garbage.
 *      In this case we check the EBX register which should be
 *      BDOOR_MAGIC if the IN was handled in a VM. Based on this we
 *      return either TRUE or FALSE.
 *
 * Results:
 *      TRUE if we succesfully accessed the backdoor, FALSE or segfault
 *      if not.
 *
 * Side effects:
 *	Exception if not in a VM.
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_TouchBackDoor(void)
{
   uint32 ebxval;

#if defined(_WIN64)
   Backdoor_proto bp;

   bp.in.ax.quad = BDOOR_MAGIC;
   bp.in.size = ~BDOOR_MAGIC;
   bp.in.cx.quad = BDOOR_CMD_GETVERSION;
   bp.in.dx.quad = BDOOR_PORT;

   Hostinfo_BackdoorInOut(&bp);

   ebxval = bp.out.bx.words.low;
#else // _WIN64
   _asm {
         push edx
         push ecx
         push ebx
         mov ecx, BDOOR_CMD_GETVERSION
         mov ebx, ~BDOOR_MAGIC
         mov eax, BDOOR_MAGIC
         mov dx, BDOOR_PORT
         in eax, dx
         mov ebxval, ebx
         pop ebx
         pop ecx
         pop edx
   }
#endif // _WIN64

   return (ebxval == BDOOR_MAGIC) ? TRUE : FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_TouchVirtualPC --
 *
 *      Access MS Virtual PC's backdoor. This is used to determine if
 *      we are running in a MS Virtual PC or on a physical host.  Works
 *      the same as Hostinfo_TouchBackDoor, except the entry to MS VPC
 *      is an invalid opcode instead or writing to a port.  Since
 *      MS VPC is 32-bit only, the WIN64 path returns FALSE.
 *      See:  See: http://www.codeproject.com/KB/system/VmDetect.aspx
 *
 * Results:
 *      TRUE if we succesfully accessed MS Virtual PC, FALSE or
 *      segfault if not.
 *
 * Side effects:
 *	Exception if not in a VM.
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_TouchVirtualPC(void)
{
#if defined(_WIN64)
   return FALSE; // MS Virtual PC is 32-bit only
#else  // _WIN32
   uint32 ebxval;

   _asm {
      push ebx
      mov  ebx, 0

      mov  eax, 1 // Virtual PC function number

      // execute invalid opcode to call into MS Virtual PC

      __emit 0Fh
      __emit 3Fh
      __emit 07h
      __emit 0Bh

      mov ebxval, ebx
      pop ebx
   }
   return !ebxval; // ebx is zero if inside Virtual PC
#endif
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_NestingSupported --
 *
 *      Access the backdoor with a nesting control query. This is used
 *      to determine if we are running in a VM that supports nesting.
 *      This function should only be called after determining that the
 *	backdoor is present with Hostinfo_TouchBackdoor().
 *
 * Results:
 *      TRUE if the outer VM supports nesting.
 *      FALSE otherwise.
 *
 * Side effects:
 *      Exception if not in a VM, so don't do that!
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_NestingSupported(void)
{
   uint32 cmd = NESTING_CONTROL_QUERY << 16 | BDOOR_CMD_NESTING_CONTROL;
   uint32 result;

#if defined(_WIN64)
   Backdoor_proto bp;

   bp.in.ax.quad = BDOOR_MAGIC;
   bp.in.cx.quad = cmd;
   bp.in.dx.quad = BDOOR_PORT;

   Hostinfo_BackdoorInOut(&bp);

   result = bp.out.ax.words.low;
#else
   _asm {
         push edx
         push ecx
         mov ecx, cmd
         mov eax, BDOOR_MAGIC
         mov dx, BDOOR_PORT
         in eax, dx
         mov result, eax
         pop ecx
         pop edx
   }
#endif

   if (result >= NESTING_CONTROL_QUERY && result != ~0U) {
      return TRUE;
   }
   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_VCPUInfoBackdoor --
 *
 *      Access the backdoor with an VCPU info query. This is used to
 *      determine whether a VCPU supports a particular feature,
 *      determined by 'bit'.  This function should only be called after
 *      determining that the backdoor is present with
 *      Hostinfo_TouchBackdoor().
 *
 * Results:
 *      TRUE if the outer VM supports the feature.
 *	FALSE otherwise.
 *
 * Side effects:
 *	Exception if not in a VM, so don't do that!
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_VCPUInfoBackdoor(unsigned bit)
{
   uint32 cmd = BDOOR_CMD_GET_VCPU_INFO;
   uint32 result;

#if defined(_WIN64)
   Backdoor_proto bp;

   bp.in.ax.quad = BDOOR_MAGIC;
   bp.in.cx.quad = cmd;
   bp.in.dx.quad = BDOOR_PORT;

   Hostinfo_BackdoorInOut(&bp);

   result = bp.out.ax.words.low;
#else
   _asm {
         push edx
         push ecx
         mov ecx, cmd
         mov eax, BDOOR_MAGIC
         mov dx, BDOOR_PORT
         in eax, dx
         mov result, eax
         pop ecx
         pop edx
   }
#endif
   /* If reserved bit is 1, this command wasn't implemented. */
   return (result & (1 << BDOOR_CMD_VCPU_RESERVED)) == 0 &&
          (result & (1 << bit))                     != 0;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_GetNestedBuildNum --
 *
 *      Perform a backdoor call to query the build number of the
 *      outer VMware hypervisor.
 *
 * Results:
 *      The build number of the outer VMware hypervisor, or -1 if
 *      the backdoor call is not supported.
 *
 * Side effects:
 *      Exception if not in a VM, so don't do that!
 *
 *----------------------------------------------------------------------
 */

uint32
Hostinfo_GetNestedBuildNum(void)
{
   uint32 cmd = BDOOR_CMD_GETBUILDNUM;
   int32 result;

#if defined(_WIN64)
   Backdoor_proto bp;

   bp.in.ax.quad = BDOOR_MAGIC;
   bp.in.cx.quad = cmd;
   bp.in.dx.quad = BDOOR_PORT;

   Hostinfo_BackdoorInOut(&bp);

   result = bp.out.ax.words.low;
#else
   _asm {
         push edx
         push ecx
         mov ecx, cmd
         mov eax, BDOOR_MAGIC
         mov dx, BDOOR_PORT
         in eax, dx
         mov result, eax
         pop ecx
         pop edx
   }
#endif
   return result;
}

#else // !defined(_WIN32)

/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_TouchBackDoor --
 *
 *      Access the backdoor. This is used to determine if we are
 *      running in a VM or on a physical host. On a physical host
 *      this should generate a GP which we catch and thereby determine
 *      that we are not in a VM. However some OSes do not handle the
 *      GP correctly and the process continues running returning garbage.
 *      In this case we check the EBX register which should be
 *      BDOOR_MAGIC if the IN was handled in a VM. Based on this we
 *      return either TRUE or FALSE.  If hypercall support is present,
 *      return TRUE without touching the backdoor.
 *
 * Results:
 *      TRUE if we have hypercall support or succesfully accessed the
 *      backdoor, FALSE or segfault if not.
 *
 * Side effects:
 *      Exception if not in a VM.
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_TouchBackDoor(void)
{
#if defined(__i386__) || defined(__x86_64__)
   uint32 eax;
   uint32 ebx;
   uint32 ecx;

   switch (HostinfoBackdoorGetInterface()) {
#  if defined(USE_HYPERCALL)
   case BACKDOOR_INTERFACE_VMCALL:  // Fall Through
   case BACKDOOR_INTERFACE_VMMCALL:
      return TRUE;
      break;
#  endif
   default:
      __asm__ __volatile__(
#     if defined __PIC__ && !vm_x86_64 // %ebx is reserved by the compiler.
         "xchgl %%ebx, %1" "\n\t"
         "inl %%dx, %%eax" "\n\t"
         "xchgl %%ebx, %1"
         : "=a" (eax),
           "=&rm" (ebx),
#     else
         "inl %%dx, %%eax"
         : "=a" (eax),
           "=b" (ebx),
#     endif
           "=c" (ecx)
         : "0" (BDOOR_MAGIC),
           "1" (~BDOOR_MAGIC),
           "2" (BDOOR_CMD_GETVERSION),
           "d" (BDOOR_PORT)
      );
      break;
   }
   if (ebx == BDOOR_MAGIC) {
      return TRUE;
   }
#elif defined __aarch64__
   register uint32 w0 asm("w0") = BDOOR_MAGIC;
   register uint32 w1 asm("w1") = ~BDOOR_MAGIC;
   register uint32 w2 asm("w2") = BDOOR_CMD_GETVERSION;
   register uint32 w3 asm("w3") = BDOOR_PORT;
   register uint64 x7 asm("x7") = (uint64)X86_IO_MAGIC << 32 |
                                  X86_IO_W7_WITH |
                                  X86_IO_W7_DIR |
                                  2 << X86_IO_W7_SIZE_SHIFT;
   __asm__ __volatile__(
      "mrs xzr, mdccsr_el0     \n\t"
      : "+r" (w0),
        "+r" (w1),
        "+r" (w2)
      : "r" (w3),
        "r" (x7));

   if (w1 == BDOOR_MAGIC) {
      return TRUE;
   }
#endif

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_TouchVirtualPC --
 *
 *      Access MS Virtual PC's backdoor. This is used to determine if
 *      we are running in a MS Virtual PC or on a physical host.  Works
 *      the same as Hostinfo_TouchBackDoor, except the entry to MS VPC
 *      is an invalid opcode instead or writing to a port.  Since
 *      MS VPC is 32-bit only, the 64-bit path returns FALSE.
 *      See: http://www.codeproject.com/KB/system/VmDetect.aspx
 *
 * Results:
 *      TRUE if we succesfully accessed MS Virtual PC, FALSE or
 *      segfault if not.
 *
 * Side effects:
 *      Exception if not in a VM.
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_TouchVirtualPC(void)
{
#if !defined VM_X86_32
   return FALSE;
#else
   uint32 ebxval;

   __asm__ __volatile__ (
#  if defined __PIC__        // %ebx is reserved by the compiler.
     "xchgl %%ebx, %1" "\n\t"
     ".long 0x0B073F0F" "\n\t"
     "xchgl %%ebx, %1"
     : "=&rm" (ebxval)
     : "a" (1),
       "0" (0)
#  else
     ".long 0x0B073F0F"
     : "=b" (ebxval)
     : "a" (1),
       "b" (0)
#  endif
  );
  return !ebxval; // %%ebx is zero if inside Virtual PC
#endif
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_NestingSupported --
 *
 *      Access the backdoor with a nesting control query. This is used
 *      to determine if we are running inside a VM that supports nesting.
 *      This function should only be called after determining that the
 *	backdoor is present with Hostinfo_TouchBackdoor().
 *
 * Results:
 *      TRUE if the outer VM supports nesting.
 *	FALSE otherwise.
 *
 * Side effects:
 *	Exception if not in a VM, so don't do that!
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_NestingSupported(void)
{
#if defined(__i386__) || defined(__x86_64__)
   uint32 cmd = NESTING_CONTROL_QUERY << 16 | BDOOR_CMD_NESTING_CONTROL;
   uint32 result;

   switch (HostinfoBackdoorGetInterface()) {
#  if defined(USE_HYPERCALL)
   case BACKDOOR_INTERFACE_VMCALL:
      Vmcall(cmd, result);
      break;
   case BACKDOOR_INTERFACE_VMMCALL:
      Vmmcall(cmd, result);
      break;
#  endif
   default:
      Ioportcall(cmd, result);
      break;
   }

   if (result >= NESTING_CONTROL_QUERY && result != ~0U) {
      return TRUE;
   }
#endif

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_VCPUInfoBackdoor --
 *
 *      Access the backdoor with an VCPU info query. This is used to
 *      determine whether a VCPU supports a particular feature,
 *      determined by 'bit'.  This function should only be called after
 *      determining that the backdoor is present with
 *      Hostinfo_TouchBackdoor().
 *
 * Results:
 *      TRUE if the outer VM supports the feature.
 *	FALSE otherwise.
 *
 * Side effects:
 *	Exception if not in a VM, so don't do that!
 *
 *----------------------------------------------------------------------
 */

Bool
Hostinfo_VCPUInfoBackdoor(unsigned bit)
{
#if defined(__i386__) || defined(__x86_64__)
   uint32 result;
   uint32 cmd = BDOOR_CMD_GET_VCPU_INFO;

   switch (HostinfoBackdoorGetInterface()) {
#  if defined(USE_HYPERCALL)
   case BACKDOOR_INTERFACE_VMCALL:
      Vmcall(cmd, result);
      break;
   case BACKDOOR_INTERFACE_VMMCALL:
      Vmmcall(cmd, result);
      break;
#  endif
   default:
      Ioportcall(cmd, result);
      break;
   }
   /* If reserved bit is 1, this command wasn't implemented. */
   return (result & (1 << BDOOR_CMD_VCPU_RESERVED)) == 0 &&
          (result & (1 << bit))                     != 0;
#endif
   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 *  Hostinfo_GetNestedBuildNum --
 *
 *      Perform a backdoor call to query the build number of the
 *      outer VMware hypervisor.
 *
 * Results:
 *      The build number of the outer VMware hypervisor, or -1 if
 *      the backdoor call is not supported.
 *
 * Side effects:
 *      Exception if not in a VM, so don't do that!
 *
 *----------------------------------------------------------------------
 */

uint32
Hostinfo_GetNestedBuildNum(void)
{
#if defined(__i386__) || defined(__x86_64__)
   uint32 result;
   uint32 cmd = BDOOR_CMD_GETBUILDNUM;

   switch (HostinfoBackdoorGetInterface()) {
#  if defined(USE_HYPERCALL)
   case BACKDOOR_INTERFACE_VMCALL:
      Vmcall(cmd, result);
      break;
   case BACKDOOR_INTERFACE_VMMCALL:
      Vmmcall(cmd, result);
      break;
#  endif
   default:
      Ioportcall(cmd, result);
      break;
   }
   return result;
#endif
   return 0;
}
#endif

  07070100000190000081A400000000000000000000000168225505000007F3000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/hostinfoInt.h /*********************************************************
 * Copyright (C) 2009-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hostinfoInt.h --
 *
 *	lib/misc Hostinfo_* private declarations.
 */

#ifndef _HOSTINFOINT_H_
#define _HOSTINFOINT_H_

#define MAX_DETAILED_FIELD_LEN 1024
#define DETAILED_DATA_DELIMITER " "


/*
 * Global variables
 */

typedef struct {
   const char *name;
   char        value[MAX_DETAILED_FIELD_LEN];
} DetailedDataField;

/* Must be sorted. Keep in same ordering as detailedDataFields */
typedef enum {
   ARCH,
   BITNESS,
   BUILD_NUMBER,
   CPE_STRING,
   DISTRO_ADDL_VERSION,
   DISTRO_NAME,
   DISTRO_VERSION,
   FAMILY_NAME,
   KERNEL_VERSION,
   PRETTY_NAME
} DetailedDataFieldType;

/*
 * Must be sorted. Keep in same ordering as StructuredFieldType. Defined in
 * hostinfoPosix.c
 */
extern DetailedDataField detailedDataFields[];

#define MAX_DETAILED_STRING_LEN (10 * MAX_DETAILED_FIELD_LEN)

extern volatile Bool hostinfoCacheValid;
extern char          hostinfoCachedOSName[MAX_OS_NAME_LEN];
extern char          hostinfoCachedOSFullName[MAX_OS_FULLNAME_LEN];
extern char          hostinfoCachedDetailedData[MAX_DETAILED_STRING_LEN];

/*
 * Global functions
 */

extern Bool HostinfoOSData(void);

#endif // ifndef _HOSTINFOINT_H_
 07070100000191000081A40000000000000000000000016822550500020C2B000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/hostinfoPosix.c   /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/time.h>
#include <pwd.h>
#include <pthread.h>
#include <time.h>
#include <sys/resource.h>
#if defined(sun)
#include <sys/systeminfo.h>
#endif
#include <sys/socket.h>
#if defined(__FreeBSD__) || defined(__APPLE__)
# include <sys/sysctl.h>
#endif
#if !defined(__APPLE__)
#define TARGET_OS_IPHONE 0
#endif
#if defined(__APPLE__)
#include <assert.h>
#include <TargetConditionals.h>
#if !TARGET_OS_IPHONE
#include <libproc.h>
#include <CoreServices/CoreServices.h>
#endif
#include <mach-o/dyld.h>
#include <mach/mach_host.h>
#include <mach/mach_init.h>
#include <mach/mach_time.h>
#include <mach/vm_map.h>
#include <sys/mman.h>
#include <AvailabilityMacros.h>
#elif defined(__FreeBSD__)
#if !defined(RLIMIT_AS)
#  if defined(RLIMIT_VMEM)
#     define RLIMIT_AS RLIMIT_VMEM
#  else
#     define RLIMIT_AS RLIMIT_RSS
#  endif
#endif
#else
#if !defined(USING_AUTOCONF) || defined(HAVE_SYS_VFS_H)
#include <sys/vfs.h>
#endif
#if !defined(sun) && !defined __ANDROID__ && (!defined(USING_AUTOCONF) || \
                                              (defined(HAVE_SYS_IO_H) && \
                                               defined(HAVE_SYS_SYSINFO_H)))
#if defined(__i386__) || defined(__x86_64__)
#include <sys/io.h>
#else
#define NO_IOPL
#endif
#include <sys/sysinfo.h>
#ifndef HAVE_SYSINFO
#define HAVE_SYSINFO 1
#endif
#endif
#endif

#if defined(__APPLE__) || defined(__FreeBSD__)
#include <paths.h>
#endif

#if defined(__linux__) || defined(__APPLE__)
#include <dlfcn.h>
#endif

#if defined(__linux__) || defined(__ANDROID__)
#include <dirent.h>
#endif

#if !defined(_PATH_DEVNULL)
#define _PATH_DEVNULL "/dev/null"
#endif

#include "vmware.h"
#include "hostType.h"
#include "hostinfo.h"
#include "hostinfoInt.h"
#include "str.h"
#include "err.h"
#include "msg.h"
#include "log.h"
#include "posix.h"
#include "file.h"
#include "backdoor_def.h"
#include "util.h"
#include "vmstdio.h"
#include "su.h"
#include "vm_atomic.h"

#if defined(__i386__) || defined(__x86_64__)
#include "x86cpuid.h"
#endif

#include "unicode.h"
#include "guest_os.h"
#include "dynbuf.h"
#include "strutil.h"

#if defined(__APPLE__)
#include "utilMacos.h"
#include "rateconv.h"
#endif

#if defined(VMX86_SERVER) || defined(USERWORLD)
#include "uwvmkAPI.h"
#include "uwvmk.h"
#include "vmkSyscall.h"
#include "uwvmkprivate.h"
#endif

#define LGPFX "HOSTINFO:"

#define SYSTEM_BITNESS_32 "i386"
#define SYSTEM_BITNESS_64_SUN "amd64"
#define SYSTEM_BITNESS_64_LINUX "x86_64"
#define SYSTEM_BITNESS_64_ARM_LINUX "aarch64"
#define SYSTEM_BITNESS_64_ARM_FREEBSD "arm64"
#define SYSTEM_BITNESS_MAXLEN \
   MAX(sizeof SYSTEM_BITNESS_32, \
   MAX(sizeof SYSTEM_BITNESS_64_SUN, \
   MAX(sizeof SYSTEM_BITNESS_64_LINUX, \
   MAX(sizeof SYSTEM_BITNESS_64_ARM_LINUX, \
       sizeof SYSTEM_BITNESS_64_ARM_FREEBSD))))

#define LSB_RELEASE "/usr/bin/lsb_release"

struct hostinfoOSVersion {
   int   hostinfoOSVersion[4];
   char *hostinfoOSVersionString;
};

static Atomic_Ptr hostinfoOSVersion;

#define DISTRO_BUF_SIZE 1024

#if !defined(__APPLE__) && !defined(VMX86_SERVER) && !defined(USERWORLD)
static const char *lsbFields[] = {
   "DISTRIB_ID=",
   "DISTRIB_RELEASE=",
   "DISTRIB_CODENAME=",
   "DISTRIB_DESCRIPTION=",
   NULL                      // MUST BE LAST
};

static const char *osReleaseFields[] = {
   "PRETTY_NAME=",
   "NAME=",
   "VERSION_ID=",
   "BUILD_ID=",
   "VERSION=",
   "CPE_NAME=",              // NIST CPE specification
   NULL                      // MUST BE LAST
};

typedef struct {
   const char *name;
   const char *filename;
} DistroInfo;

/*
 * This is the list of the location of the LSB standard distro identification
 * file for the distros known to the code in this file. The LSB standard is an
 * old standard, largely replaced by the superior os-release standard.
 *
 * If a distro *ALWAYS* supports the os-release standard (with or without an
 * LSB identifying file), there is no need to add anything to this list. For
 * distros that *ONLY* support the LSB standard, feel free to add an entry to
 * this table.
 *
 * KEEP SORTED! (sort -d)
 */

static const DistroInfo distroArray[] = {
   { "ALT",                "/etc/altlinux-release"      },
   { "Annvix",             "/etc/annvix-release"        },
   { "Arch",               "/etc/arch-release"          },
   { "Arklinux",           "/etc/arklinux-release"      },
   { "Asianux",            "/etc/asianux-release"       },
   { "Aurox",              "/etc/aurox-release"         },
   { "BlackCat",           "/etc/blackcat-release"      },
   { "Cobalt",             "/etc/cobalt-release"        },
   { "CentOS",             "/etc/centos-release"        },
   { "Conectiva",          "/etc/conectiva-release"     },
   { "Debian",             "/etc/debian_release"        },
   { "Debian",             "/etc/debian_version"        },
   { "Fedora Core",        "/etc/fedora-release"        },
   { "Gentoo",             "/etc/gentoo-release"        },
   { "Immunix",            "/etc/immunix-release"       },
   { "Knoppix",            "/etc/knoppix_version"       },
   { "Linux-From-Scratch", "/etc/lfs-release"           },
   { "Linux-PPC",          "/etc/linuxppc-release"      },
   { "Mandrake",           "/etc/mandrakelinux-release" },
   { "Mandrake",           "/etc/mandrake-release"      },
   { "Mandriva",           "/etc/mandriva-release"      },
   { "MkLinux",            "/etc/mklinux-release"       },
   { "Novell",             "/etc/nld-release"           },
   { "OracleLinux",        "/etc/oracle-release"        },
   { "Photon",             "/etc/lsb-release"           },
   { "PLD",                "/etc/pld-release"           },
   { "RedHat",             "/etc/redhat-release"        },
   { "RedHat",             "/etc/redhat_version"        },
   { "Slackware",          "/etc/slackware-release"     },
   { "Slackware",          "/etc/slackware-version"     },
   { "SMEServer",          "/etc/e-smith-release"       },
   { "Solaris",            "/etc/release"               },
   { "Sun",                "/etc/sun-release"           },
   { "SuSE",               "/etc/novell-release"        },
   { "SuSE",               "/etc/sles-release"          },
   { "SuSE",               "/etc/SuSE-release"          },
   { "Tiny Sofa",          "/etc/tinysofa-release"      },
   { "TurboLinux",         "/etc/turbolinux-release"    },
   { "Ubuntu",             "/etc/lsb-release"           },
   { "UltraPenguin",       "/etc/ultrapenguin-release"  },
   { "UnitedLinux",        "/etc/UnitedLinux-release"   },
   { "VALinux",            "/etc/va-release"            },
   { "Yellow Dog",         "/etc/yellowdog-release"     },
   { NULL,                 NULL                         },
};
#endif

/*
 * Any data obtained about the distro is obtained - UNMODIFIED - from the
 * standardized (i.e. LSB, os-release) disto description files. Under no
 * circumstances is code specific to a distro allowed or used. If the data
 * specified by a distro isn't useful, talk to the disto, not VMware.
 *
 * The fields from the standardized distro description files used may be
 * found above (i.e. lsbFields, osReleaseFields).
 *
 * Must be sorted. Keep in the same ordering as DetailedDataFieldType
 */

DetailedDataField detailedDataFields[] = {
#if defined(VM_ARM_ANY)
   { "architecture",      "Arm"   },  // Arm
#else
   { "architecture",      "X86"   },  // Intel/X86
#endif
   { "bitness",           ""      },  // "32" or "64"
   { "buildNumber",       ""      },  // When available
   { "cpeString",         ""      },  // When available
   { "distroAddlVersion", ""      },  // When available
   { "distroName",        ""      },  // Defaults to uname -s
   { "distroVersion",     ""      },  // When available
   { "familyName",        ""      },  // Defaults to uname -s
   { "kernelVersion",     ""      },  // Defaults to uname -r
   { "prettyName",        ""      },  // When available
   { NULL,                ""      },  // MUST BE LAST
};

#if defined __ANDROID__ || defined __aarch64__
/*
 * Android and arm64 do not support iopl().
 */
#define NO_IOPL
#endif

#if defined __ANDROID__
/*
 * Android doesn't support getloadavg().
 */
#define NO_GETLOADAVG
#endif


/*
 *----------------------------------------------------------------------
 *
 * HostinfoOSVersionInit --
 *
 *      Compute the OS version information
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      hostinfoOS* variables are filled in.
 *
 *----------------------------------------------------------------------
 */

static void
HostinfoOSVersionInit(void)
{
   struct hostinfoOSVersion *version;
   struct utsname u;
   char *extra;
   char *p;

   if (Atomic_ReadPtr(&hostinfoOSVersion)) {
      return;
   }

   if (uname(&u) == -1) {
      Warning("%s: unable to get host OS version (uname): %s\n",
	      __FUNCTION__, Err_Errno2String(errno));
      NOT_IMPLEMENTED();
   }

   version = Util_SafeCalloc(1, sizeof *version);
   version->hostinfoOSVersionString = Util_SafeStrndup(u.release,
                                                       sizeof u.release);

   ASSERT(ARRAYSIZE(version->hostinfoOSVersion) >= 4);

   /*
    * The first three numbers are separated by '.', if there is
    * a fourth number, it's probably separated by '.' or '-',
    * but it could be preceded by anything.
    */

   extra = Util_SafeCalloc(1, sizeof u.release);
   if (sscanf(u.release, "%d.%d.%d%s",
	      &version->hostinfoOSVersion[0], &version->hostinfoOSVersion[1],
	      &version->hostinfoOSVersion[2], extra) < 1) {
      Warning("%s: unable to parse host OS version string: %s\n",
              __FUNCTION__, u.release);
      NOT_IMPLEMENTED();
   }

   /*
    * If there is a 4th number, use it, otherwise use 0.
    * Explicitly skip over any non-digits, including '-'
    */

   p = extra;
   while (*p && !isdigit(*p)) {
      p++;
   }
   sscanf(p, "%d", &version->hostinfoOSVersion[3]);
   free(extra);

   if (Atomic_ReadIfEqualWritePtr(&hostinfoOSVersion, NULL, version)) {
      free(version->hostinfoOSVersionString);
      free(version);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_OSVersionString --
 *
 *	Returns the host version information as returned in the
 *      release field of uname(2)
 *
 * Results:
 *	const char * - pointer to static buffer containing the release
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

const char *
Hostinfo_OSVersionString(void)
{
   struct hostinfoOSVersion *version;

   HostinfoOSVersionInit();

   version = Atomic_ReadPtr(&hostinfoOSVersion);

   return version->hostinfoOSVersionString;
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_OSVersion --
 *
 *      Host OS release info.
 *
 * Results:
 *      The i-th component of a dotted release string.
 *	0 if i is greater than the number of components we support.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Hostinfo_OSVersion(unsigned int i)  // IN:
{
   struct hostinfoOSVersion *version;

   HostinfoOSVersionInit();

   version = Atomic_ReadPtr(&hostinfoOSVersion);

   return (i < ARRAYSIZE(version->hostinfoOSVersion)) ?
           version->hostinfoOSVersion[i] : 0;
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_GetTimeOfDay --
 *
 *      Return the current time of day according to the host.  We want
 *      UTC time (seconds since Jan 1, 1970).
 *
 * Results:
 *      Time of day in microseconds.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Hostinfo_GetTimeOfDay(VmTimeType *time)  // OUT:
{
   struct timeval tv;

   gettimeofday(&tv, NULL);

   *time = ((int64)tv.tv_sec * 1000000) + tv.tv_usec;
}


/*
 *----------------------------------------------------------------------------
 *
 * Hostinfo_GetSystemBitness --
 *
 *      Determines the operating system's bitness.
 *
 * Return value:
 *      32 or 64 on success.
 *      -1 on failure. Check errno for more details of error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
Hostinfo_GetSystemBitness(void)
{
#if defined __linux__
   struct utsname u;

   if (uname(&u) < 0) {
      return -1;
   }

   if (strstr(u.machine, SYSTEM_BITNESS_64_LINUX) ||
       strstr(u.machine, SYSTEM_BITNESS_64_ARM_LINUX)) {
      return 64;
   } else {
      return 32;
   }
#else
   char buf[SYSTEM_BITNESS_MAXLEN] = { '\0', };
#   if defined __FreeBSD__ || defined __APPLE__
   static int mib[2] = { CTL_HW, HW_MACHINE, };
   size_t len = sizeof buf;

   if (sysctl(mib, ARRAYSIZE(mib), buf, &len, NULL, 0) == -1) {
      return -1;
   }
#   elif defined sun
#      if !defined SOL10
   /*
    * XXX: This is bad.  We define SI_ARCHITECTURE_K to what it is on Solaris
    * 10 so that we can use a single guestd build for Solaris 9 and 10. In the
    * future we should have the Solaris 9 build just return 32 -- since it did
    * not support 64-bit x86 -- and let the Solaris 10 headers define
    * SI_ARCHITECTURE_K, then have the installer symlink to the correct binary.
    * For now, though, we'll share a single build for both versions.
    */
#         define SI_ARCHITECTURE_K  518
#      endif

   if (sysinfo(SI_ARCHITECTURE_K, buf, sizeof buf) < 0) {
      return -1;
   }
#   endif

   if (strcmp(buf, SYSTEM_BITNESS_32) == 0) {
      return 32;
   } else if (strcmp(buf, SYSTEM_BITNESS_64_SUN) == 0 ||
              strcmp(buf, SYSTEM_BITNESS_64_LINUX) == 0 ||
              strcmp(buf, SYSTEM_BITNESS_64_ARM_LINUX) == 0 ||
              strcmp(buf, SYSTEM_BITNESS_64_ARM_FREEBSD) == 0) {
      return 64;
   }

   return -1;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoPostData --
 *
 *      Post the OS name data to their cached values.
 *
 * Return value:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HostinfoPostData(const char *osName,  // IN:
                 char *osNameFull)    // IN:
{
   unsigned int lastCharPos;
   static Atomic_uint32 mutex = {0};

   /*
    * Before returning, truncate the newline character at the end of the full
    * name.
    */

   lastCharPos = strlen(osNameFull) - 1;
   if (osNameFull[lastCharPos] == '\n') {
      osNameFull[lastCharPos] = '\0';
   }

   /*
    * Serialize access. Collisions should be rare - plus the value will get
    * cached and this won't get called anymore.
    */

   while (Atomic_ReadWrite(&mutex, 1)); // Spinlock.

   if (!hostinfoCacheValid) {
      Str_Strcpy(hostinfoCachedOSName, osName, sizeof hostinfoCachedOSName);
      Str_Strcpy(hostinfoCachedOSFullName, osNameFull,
                 sizeof hostinfoCachedOSFullName);
      hostinfoCacheValid = TRUE;
   }

   Atomic_Write(&mutex, 0);  // unlock
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoOSDetailedData --
 *
 *    Builds, escapes, and stores the detailed data into the cache.
 *
 * Return value:
 *      TRUE   Success
 *      FALSE  Failure
 *
 * Side effects:
 *      Cache values are set when returning TRUE
 *
 *-----------------------------------------------------------------------------
 */

static void
HostinfoOSDetailedData(void)
{
   DetailedDataField *field;
   Bool first = TRUE;

   /* Clear the string cache */
   memset(hostinfoCachedDetailedData, '\0',
          sizeof hostinfoCachedDetailedData);

   for (field = detailedDataFields; field->name != NULL; field++) {
      if (field->value[0] != '\0') {
         /* Account for the escape and NUL char */
         int len;
         const char *c;
         char escapedString[2 * MAX_DETAILED_FIELD_LEN + 1];
         char fieldString[MAX_DETAILED_FIELD_LEN];
         int32 i = 0;

         /* Add delimiter between properties - after the first one */
         if (!first) {
            Str_Strcat(hostinfoCachedDetailedData,
                       DETAILED_DATA_DELIMITER,
                       sizeof hostinfoCachedDetailedData);
         }

         /* Escape single quotes and back slashes in the value. */
         for (c = field->value; *c != '\0'; c++) {
            if (*c == '\'' || *c == '\\') {
               escapedString[i++] = '\\';
            }

            escapedString[i++] = *c;
         }

         escapedString[i] = '\0';

         /* No trailing spaces */
         while (--i >= 0 && isspace(escapedString[i])) {
            escapedString[i] = '\0';
         }

         len = Str_Snprintf(fieldString, sizeof fieldString, "%s='%s'",
                            field->name, escapedString);
         if (len == -1) {
            Warning("%s: Error: detailed data field too large\n",
                    __FUNCTION__);
            memset(hostinfoCachedDetailedData, '\0',
                   sizeof hostinfoCachedDetailedData);
            return;
         }

         Str_Strcat(hostinfoCachedDetailedData, fieldString,
                    sizeof hostinfoCachedDetailedData);

         first = FALSE;
      }
   }
}


#if defined(__APPLE__) // MacOS
/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoMacOS --
 *
 *      Determine the specifics concerning MacOS.
 *
 * Return value:
 *      Returns TRUE on success and FALSE on failure.
 *
 * Side effects:
 *      Cache values are set when returning TRUE.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoMacOS(struct utsname *buf)  // IN:
{
   int len;
   unsigned int i;
   char *productName;
   char *productVersion;
   char *productBuildVersion;
   char osName[MAX_OS_NAME_LEN];
   char osNameFull[MAX_OS_FULLNAME_LEN];
   Bool haveVersion = FALSE;
   static char const *versionPlists[] = {
      "/System/Library/CoreServices/ServerVersion.plist",
      "/System/Library/CoreServices/SystemVersion.plist"
   };

   /*
    * Read the version info from ServerVersion.plist or SystemVersion.plist.
    * Mac OS Server (10.6 and earlier) has both files, and the product name in
    * ServerVersion.plist is "Mac OS X Server". Client versions of Mac OS only
    * have SystemVersion.plist with the name "Mac OS X".
    *
    * This is better than executing system_profiler or sw_vers, or using the
    * deprecated Gestalt() function (which only gets version numbers).
    * All of those methods just read the same plist files anyway.
    */

   for (i = 0; !haveVersion && i < ARRAYSIZE(versionPlists); i++) {
      CFDictionaryRef versionDict =
         UtilMacos_CreateCFDictionaryWithContentsOfFile(versionPlists[i]);
      if (versionDict != NULL) {
         haveVersion = UtilMacos_ReadSystemVersion(versionDict,
                                                   &productName,
                                                   &productVersion,
                                                   &productBuildVersion);
         CFRelease(versionDict);
      }
   }

   Str_Strcpy(detailedDataFields[DISTRO_NAME].value, productName,
              sizeof detailedDataFields[DISTRO_NAME].value);
   Str_Strcpy(detailedDataFields[DISTRO_VERSION].value, productVersion,
              sizeof detailedDataFields[DISTRO_VERSION].value);
   Str_Strcpy(detailedDataFields[BUILD_NUMBER].value, productBuildVersion,
              sizeof detailedDataFields[BUILD_NUMBER].value);

   if (haveVersion) {
      len = Str_Snprintf(osNameFull, sizeof osNameFull,
                         "%s %s (%s) %s %s", productName, productVersion,
                         productBuildVersion, buf->sysname, buf->release);

      free(productName);
      free(productVersion);
      free(productBuildVersion);
   } else {
      Log("%s: Failed to read system version plist.\n", __FUNCTION__);

      len = Str_Snprintf(osNameFull, sizeof osNameFull, "%s %s", buf->sysname,
                         buf->release);
   }

   if (len != -1) {
      if (Hostinfo_GetSystemBitness() == 64) {
         len = Str_Snprintf(osName, sizeof osName, "%s%d%s", STR_OS_MACOS,
                            Hostinfo_OSVersion(0), STR_OS_64BIT_SUFFIX);
      } else {
         len = Str_Snprintf(osName, sizeof osName, "%s%d", STR_OS_MACOS,
                            Hostinfo_OSVersion(0));
      }
   }

   if (len == -1) {
      Warning("%s: Error: buffer too small\n", __FUNCTION__);
   } else {
      HostinfoPostData(osName, osNameFull);
   }

   return (len != -1);
}
#endif


#if defined(VMX86_SERVER) || defined(USERWORLD)  // ESXi
/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoESX --
 *
 *      Determine the specifics concerning ESXi.
 *
 * Return value:
 *      TRUE   Success
 *      FALSE  Failure
 *
 * Side effects:
 *      Cache values are set when returning TRUE.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoESX(struct utsname *buf)  // IN:
{
   int len;
   uint32 major;
   uint32 minor;
   char osName[MAX_OS_NAME_LEN];
   char osNameFull[MAX_OS_FULLNAME_LEN];

   if (sscanf(buf->release, "%u.%u", &major, &minor) != 2) {
      if (sscanf(buf->release, "%u", &major) != 1) {
         major = 0;
      }

      minor = 0;
   }

   if (major <= 4) {
      Str_Strcpy(osName, STR_OS_VMKERNEL, sizeof osName);
   } else {
      if (minor == 0) {
         Str_Sprintf(osName, sizeof osName, "%s%d", STR_OS_VMKERNEL,
                     major);
      } else {
         Str_Sprintf(osName, sizeof osName, "%s%d%d", STR_OS_VMKERNEL,
                     major, minor);
      }
   }

   len = Str_Snprintf(osNameFull, sizeof osNameFull, "VMware ESXi %s",
                      buf->release);

   if (len == -1) {
      Warning("%s: Error: buffer too small\n", __FUNCTION__);
   } else {
      HostinfoPostData(osName, osNameFull);
   }

   return (len != -1);
}
#endif


#if !defined(__APPLE__) && !defined(VMX86_SERVER) && !defined(USERWORLD)

typedef struct ShortNameSet {
   const char   *pattern;
   const char   *shortName;
   Bool        (*setFunc)(const struct ShortNameSet *entry, // IN:
                          int version,                      // IN:
                          const char *distroLower,          // IN:
                          char *distroShort,                // OUT:
                          int distroShortSize);             // IN:
} ShortNameSet;


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSearchShortNames --
 *
 *      Search a generic ShortNameSet table.
 *      If a match is found execute the entry's setFunc and
 *      return the result.
 *
 * Return value:
 *      TRUE    success; a match was found
 *      FALSE   failure; no match was found
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSearchShortNames(const ShortNameSet *array, // IN:
                         int  version,              // IN:
                         const char *distroLower,   // IN:
                         char *distroShort,         // OUT:
                         int distroShortSize)       // IN:
{
   const ShortNameSet *p = array;

   ASSERT(p != NULL);

   while (p->pattern != NULL) {
      ASSERT(p->setFunc != NULL);

      if (strstr(distroLower, p->pattern) != NULL) {
         return (*p->setFunc)(p, version, distroLower, distroShort,
                              distroShortSize);
      }

      p++;
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoArchString --
 *
 *      Return the machine architecture prefix. The X86 and X86_64 machine
 *      architectures are implied - no prefix. All others require an official
 *      string from VMware.
 *
 * Return value:
 *      As above.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static const char *
HostinfoArchString(void)
{
#if defined(VM_ARM_ANY)
   return STR_OS_ARM_PREFIX;
#elif defined(VM_X86_ANY)
   return "";
#else
#error Unsupported architecture!
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoDefaultLinux --
 *
 *      Build and return generic data about the Linux disto. Only return what
 *      has been required - short description (i.e. guestOS string), long
 *      description (nice looking string).
 *
 * Return value:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HostinfoDefaultLinux(char *distro,            // OUT/OPT:
                     size_t distroSize,       // IN:
                     char *distroShort,       // OUT/OPT:
                     size_t distroShortSize)  // IN:
{
   char generic[128];
   const char *distroOut = NULL;
   const char *distroShortOut = NULL;
   int majorVersion = Hostinfo_OSVersion(0);
   int minorVersion = Hostinfo_OSVersion(1);

   switch (majorVersion) {
   case 1:
      distroOut = STR_OS_OTHER_FULL;
      distroShortOut = STR_OS_OTHER;
      break;

   case 2:
      if (minorVersion < 4) {
         distroOut = STR_OS_OTHER_FULL;
         distroShortOut = STR_OS_OTHER;
      } else if (minorVersion < 6) {
         distroOut = STR_OS_OTHER_24_FULL;
         distroShortOut = STR_OS_OTHER_24;
      } else {
         distroOut = STR_OS_OTHER_26_FULL;
         distroShortOut = STR_OS_OTHER_26;
      }

      break;

   case 3:
      distroOut = STR_OS_OTHER_3X_FULL;
      distroShortOut = STR_OS_OTHER_3X;
      break;

   case 4:
      distroOut = STR_OS_OTHER_4X_FULL;
      distroShortOut = STR_OS_OTHER_4X;
      break;

   case 5:
      distroOut = STR_OS_OTHER_5X_FULL;
      distroShortOut = STR_OS_OTHER_5X;
      break;

   case 6:
      distroOut = STR_OS_OTHER_6X_FULL;
      distroShortOut = STR_OS_OTHER_6X;
      break;

   default:
      /*
       * Anything newer than this code explicitly handles returns the
       * "highest" known short description and a dynamically created,
       * appropriate long description.
       */

      Str_Sprintf(generic, sizeof generic, "Other Linux %d.%d kernel",
                  majorVersion, minorVersion);
      distroOut = generic;
      distroShortOut = STR_OS_OTHER_5X;
   }

   if (distro != NULL) {
      ASSERT(distroOut != NULL);
      Str_Strcpy(distro, distroOut, distroSize);
   }

   if (distroShort != NULL) {
      ASSERT(distroShortOut != NULL);
      Str_Strcpy(distroShort, distroShortOut, distroShortSize);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoGenericSetShortName --
 *
 *      Set the short name using the short name entry in the specified table
 *      entry.
 *
 * Return value:
 *      TRUE    success
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoGenericSetShortName(const ShortNameSet *entry, // IN:
                            int version,               // IN:
                            const char *distroLower,   // IN:
                            char *distroShort,         // OUT:
                            int distroShortSize)       // IN:
{
   ASSERT(entry != NULL);
   ASSERT(entry->shortName != NULL);

   Str_Sprintf(distroShort, distroShortSize, "%s%s", HostinfoArchString(),
               entry->shortName);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSetAmazonShortName --
 *
 *      Set the short name for the Amazon distro.
 *
 * Return value:
 *      TRUE    success
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSetAmazonShortName(const ShortNameSet *entry, // IN: Unused
                           int version,               // IN:
                           const char *distroLower,   // IN: Unused
                           char *distroShort,         // OUT:
                           int distroShortSize)       // IN:
{
   if (version < 2) {
      version = 2;
   }

   Str_Sprintf(distroShort, distroShortSize, "%s%s%d",
               HostinfoArchString(), STR_OS_AMAZON_LINUX, version);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSetAsianuxShortName --
 *
 *      Set short name for the Asianux (a.k.a. Miracle Linux) distro.
 *
 * Return value:
 *      TRUE    success
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSetAsianuxShortName(const ShortNameSet *entry, // IN: Unused
                            int version,               // IN:
                            const char *distroLower,   // IN: Unused
                            char *distroShort,         // OUT:
                            int distroShortSize)       // IN:
{
   if (version < 3) {
      Str_Strcpy(distroShort, STR_OS_ASIANUX, distroShortSize);
   } else {
      Str_Sprintf(distroShort, distroShortSize, "%s%s%d",
                  HostinfoArchString(), STR_OS_ASIANUX, version);
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoBCSetShortName --
 *
 *      Handle Big Cloud Enterprise Linux.
 *
 * Return value:
 *      TRUE    success
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoBCSetShortName(const ShortNameSet *entry, // IN:
                       int version,               // IN:
                       const char *distroLower,   // IN:
                       char *distroShort,         // OUT:
                       int distroShortSize)       // IN:
{
   HostinfoDefaultLinux(NULL, 0, distroShort, distroShortSize);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSetCentosShortName --
 *
 *      Set the short name for the CentOS distro.
 *
 * Return value:
 *      TRUE    success
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSetCentosShortName(const ShortNameSet *entry, // IN: Unused
                           int version,               // IN:
                           const char *distroLower,   // IN: Unused
                           char *distroShort,         // OUT:
                           int distroShortSize)       // IN:
{
   if (version < 6) {
      Str_Strcpy(distroShort, STR_OS_CENTOS, distroShortSize);
   } else {
      Str_Sprintf(distroShort, distroShortSize, "%s%s%d",
                  HostinfoArchString(), STR_OS_CENTOS, version);
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSetDebianShortName --
 *
 *      Set the short name for the Debian distro.
 *
 * Return value:
 *      TRUE    success
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSetDebianShortName(const ShortNameSet *entry, // IN: Unused
                           int version,               // IN:
                           const char *distroLower,   // IN: Unused
                           char *distroShort,         // OUT:
                           int distroShortSize)       // IN:
{
   if (version <= 4) {
      Str_Strcpy(distroShort, STR_OS_DEBIAN "4", distroShortSize);
   } else {
      Str_Sprintf(distroShort, distroShortSize, "%s%s%d",
                  HostinfoArchString(), STR_OS_DEBIAN, version);
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSetOracleShortName --
 *
 *      Set the short name for the Oracle distro.
 *
 * Return value:
 *      TRUE    success
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSetOracleShortName(const ShortNameSet *entry, // IN: Unused
                           int version,               // IN:
                           const char *distroLower,   // IN: Unused
                           char *distroShort,         // OUT:
                           int distroShortSize)       // IN:
{
   /*
    * [root@localhost ~]# lsb_release -sd
    * "Enterprise Linux Enterprise Linux Server release 5.4 (Carthage)"
    *
    * Not sure why they didn't brand their releases as "Oracle Enterprise
    * Linux". Oh well. It's fixed in 6.0, though.
    */

   if (version == 0) {
      Str_Sprintf(distroShort, distroShortSize, "%s%s",
                  HostinfoArchString(), STR_OS_ORACLE);
   } else {
      Str_Sprintf(distroShort, distroShortSize, "%s%s%d",
                  HostinfoArchString(), STR_OS_ORACLE, version);
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSetRedHatShortName --
 *
 *      Set short name of the RedHat distro.
 *
 * Return value:
 *      TRUE    success
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSetRedHatShortName(const ShortNameSet *entry, // IN: Unused
                           int version,               // IN:
                           const char *distroLower,   // IN:
                           char *distroShort,         // OUT:
                           int distroShortSize)       // IN:
{
   if (strstr(distroLower, "enterprise") == NULL) {
      Str_Sprintf(distroShort, distroShortSize, "%s%s",
                  HostinfoArchString(), STR_OS_RED_HAT);
   } else {
      if (version == 0) {
         Str_Sprintf(distroShort, distroShortSize, "%s%s",
                     HostinfoArchString(), STR_OS_RED_HAT_EN);
      } else {
         Str_Sprintf(distroShort, distroShortSize, "%s%s%d",
                     HostinfoArchString(), STR_OS_RED_HAT_EN, version);
      }
   }

   return TRUE;
}


/*
 *      Short name subarray for the SUSE Enterprise distro.
 *
 *      Keep in sorted order (sort -d)!
 */

#define SUSE_SAP_LINUX "server for sap applications 12"

static const ShortNameSet suseEnterpriseShortNameArray[] = {
   { "desktop 10",    STR_OS_SLES "10",  HostinfoGenericSetShortName },
   { "desktop 11",    STR_OS_SLES "11",  HostinfoGenericSetShortName },
   { "desktop 12",    STR_OS_SLES "12",  HostinfoGenericSetShortName },
   { "desktop 15",    STR_OS_SLES "15",  HostinfoGenericSetShortName },
   { "desktop 16",    STR_OS_SLES "16",  HostinfoGenericSetShortName },
   { "server 10",     STR_OS_SLES "10",  HostinfoGenericSetShortName },
   { "server 11",     STR_OS_SLES "11",  HostinfoGenericSetShortName },
   { "server 12",     STR_OS_SLES "12",  HostinfoGenericSetShortName },
   { "server 15",     STR_OS_SLES "15",  HostinfoGenericSetShortName },
   { "server 16",     STR_OS_SLES "16",  HostinfoGenericSetShortName },
   { SUSE_SAP_LINUX,  STR_OS_SLES "12",  HostinfoGenericSetShortName },
   { NULL,            NULL,              NULL                        } // MUST BE LAST
};


/*
 *      Short name array for the SUSE distro.
 *
 *      Keep in sorted order (sort -d)!
 */

static const ShortNameSet suseShortNameArray[] = {
   { "sun",           STR_OS_SUN_DESK,     HostinfoGenericSetShortName },
   { "novell",        STR_OS_NOVELL "9",   HostinfoGenericSetShortName },
   { NULL,            NULL,                NULL                        } // MUST BE LAST
};


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSetSuseShortName --
 *
 *      Set the short name for the SUSE distros. Due to ownership and naming
 *      changes, other distros have to be "filtered" and named differently.
 *
 * Return value:
 *      TRUE    success
 *      FALSE   failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSetSuseShortName(const ShortNameSet *entry, // IN:
                         int version,               // IN:
                         const char *distroLower,   // IN:
                         char *distroShort,         // OUT:
                         int distroShortSize)       // IN:
{
   Bool found;

   ASSERT(entry != NULL);

   if (strstr(distroLower, "enterprise") == NULL) {
      found = HostinfoSearchShortNames(suseShortNameArray, version,
                                       distroLower, distroShort,
                                       distroShortSize);
      if (!found) {
         Str_Sprintf(distroShort, distroShortSize, "%s%s",
                     HostinfoArchString(), STR_OS_SUSE);
      }
   } else {
      found = HostinfoSearchShortNames(suseEnterpriseShortNameArray, version,
                                       distroLower, distroShort,
                                       distroShortSize);
      if (!found) {
         Str_Sprintf(distroShort, distroShortSize, "%s%s",
                     HostinfoArchString(), STR_OS_SLES);
      }
   }

   return TRUE;
}


/*
 * Table mapping from distro name to the officially recognized shortname.
 *
 * WARNING: If you are not VMware, do not change this table. Values that are
 * not recognized by the VMware host will be ignored. Any change here must
 * be accompanied by additional changes to the host.
 *
 * If you are interested in extending this table, do not send a pull request.
 * Instead, submit a request via the open-vm-tools github issue tracker
 * https://github.com/vmware/open-vm-tools/issues.
 *
 * Some distros do not have a simple substitution and special logic is
 * necessary to handle distros that do not have simple substitutions.
 *
 * Some of the special logic - functions - use a subtable.
 * If you're not VMware, do not add anything to those tables either.
 */

static const ShortNameSet shortNameArray[] = {
/* Long distro name      Short distro name          Short name set function */
{ "almalinux",           STR_OS_ALMA_LINUX,         HostinfoGenericSetShortName },
{ "amazon",              NULL,                      HostinfoSetAmazonShortName  },
{ "annvix",              STR_OS_ANNVIX,             HostinfoGenericSetShortName },
{ "arch",                STR_OS_ARCH,               HostinfoGenericSetShortName },
{ "arklinux",            STR_OS_ARKLINUX,           HostinfoGenericSetShortName },
{ "asianux",             NULL,                      HostinfoSetAsianuxShortName },
{ "aurox",               STR_OS_AUROX,              HostinfoGenericSetShortName },
{ "bigcloud",            NULL,                      HostinfoBCSetShortName      },
/* Big Cloud must come before Red Hat Entry */
{ "black cat",           STR_OS_BLACKCAT,           HostinfoGenericSetShortName },
{ "centos",              NULL,                      HostinfoSetCentosShortName  },
{ "cobalt",              STR_OS_COBALT,             HostinfoGenericSetShortName },
{ "conectiva",           STR_OS_CONECTIVA,          HostinfoGenericSetShortName },
{ "debian",              NULL,                      HostinfoSetDebianShortName  },
{ "red hat",             NULL,                      HostinfoSetRedHatShortName  },
/* Red Hat must come before the Enterprise Linux entry */
{ "enterprise linux",    NULL,                      HostinfoSetOracleShortName  },
{ "fedora",              STR_OS_FEDORA,             HostinfoGenericSetShortName },
{ "flatcar",             STR_OS_FLATCAR,            HostinfoGenericSetShortName },
{ "fusionos",            STR_OS_FUSION_OS,          HostinfoGenericSetShortName },
{ "gentoo",              STR_OS_GENTOO,             HostinfoGenericSetShortName },
{ "immunix",             STR_OS_IMMUNIX,            HostinfoGenericSetShortName },
{ "kylin linux",         STR_OS_KYLIN_LINUX,        HostinfoGenericSetShortName },
{ "linux-from-scratch",  STR_OS_LINUX_FROM_SCRATCH, HostinfoGenericSetShortName },
{ "linux-ppc",           STR_OS_LINUX_PPC,          HostinfoGenericSetShortName },
{ "mandrake",            STR_OS_MANDRAKE,           HostinfoGenericSetShortName },
{ "mandriva",            STR_OS_MANDRIVA,           HostinfoGenericSetShortName },
{ "miracle linux",       STR_OS_MIRACLE_LINUX,      HostinfoGenericSetShortName },
{ "mklinux",             STR_OS_MKLINUX,            HostinfoGenericSetShortName },
{ "opensuse",            STR_OS_OPENSUSE,           HostinfoGenericSetShortName },
{ "oracle",              NULL,                      HostinfoSetOracleShortName  },
{ "pardus",              STR_OS_PARDUS,             HostinfoGenericSetShortName },
{ "pld",                 STR_OS_PLD,                HostinfoGenericSetShortName },
{ "prolinux",            STR_OS_PROLINUX,           HostinfoGenericSetShortName },
{ "rocky linux",         STR_OS_ROCKY_LINUX,        HostinfoGenericSetShortName },
{ "slackware",           STR_OS_SLACKWARE,          HostinfoGenericSetShortName },
{ "sme server",          STR_OS_SMESERVER,          HostinfoGenericSetShortName },
{ "suse",                NULL,                      HostinfoSetSuseShortName    },
{ "tiny sofa",           STR_OS_TINYSOFA,           HostinfoGenericSetShortName },
{ "turbolinux",          STR_OS_TURBO,              HostinfoGenericSetShortName },
{ "ubuntu",              STR_OS_UBUNTU,             HostinfoGenericSetShortName },
{ "ultra penguin",       STR_OS_ULTRAPENGUIN,       HostinfoGenericSetShortName },
{ "united linux",        STR_OS_UNITEDLINUX,        HostinfoGenericSetShortName },
{ "va linux",            STR_OS_VALINUX,            HostinfoGenericSetShortName },
{ "vmware photon",       STR_OS_PHOTON,             HostinfoGenericSetShortName },
{ "yellow dog",          STR_OS_YELLOW_DOG,         HostinfoGenericSetShortName },
{ NULL,                  NULL,                      NULL                        } // MUST BE LAST
};


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoGetOSShortName --
 *
 *      Returns distro information based in .vmx format (distroShort).
 *
 * Return value:
 *
 *      True - we found the Short Name and copied it to distroShort
 *      False - we did not find the short name for the specified versionStr
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoGetOSShortName(const char *distro,     // IN: full distro name
                       const char *versionStr, // IN/OPT: distro version
                       char *distroShort,      // OUT: short distro name
                       int distroShortSize)    // IN: size of short distro name
{
   uint32 version;
   Bool found;
   char *distroLower;

   ASSERT(distro != NULL);
   ASSERT(distroShort != NULL);

   /* Come up with a distro version */

   if (versionStr == NULL) {
      const char *p = distro;

      /* The first digit in the distro string is the version */
      while (*p != '\0') {
         if (isdigit(*p)) {
            versionStr = p;
            break;
         }

         p++;
      }
   }

   if (versionStr == NULL) {
      version = 0;
   } else {
      if (sscanf(versionStr, "%u", &version) != 1) {
         version = 0;
      }
   }

   /* Normalize the distro string */
   distroLower = Str_ToLower(Util_SafeStrdup(distro));

   /* Search distroLower for a match */
   found = HostinfoSearchShortNames(shortNameArray, version, distroLower,
                                    distroShort, distroShortSize);

   free(distroLower);

   return found;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoReadDistroFile --
 *
 *      Attempt to open and read the specified distro identification file.
 *      If the file has data and can be read, attempt to identify the distro.
 *
 *      os-release rules require strict compliance. No data unless things
 *      are perfect. For the LSB, we will return the contents of the file
 *      even if things aren't strictly compliant.
 *
 * Return value:
 *     !NULL  Success. A pointer to an dynamically allocated array of pointers
 *                     to dynamically allocated strings, one for each field
 *                     corresponding to the values argument plus one which
 *                     contains a concatenation of all discovered data.
 *      NULL  Failure.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char **
HostinfoReadDistroFile(Bool osReleaseRules,   // IN: osRelease rules
                       const char *fileName,  // IN: distro file
                       const char *values[])  // IN: search strings
{
   int i;
   int fd;
   DynBuf b;
   int bufSize;
   struct stat st;
   char lineBuf[DISTRO_BUF_SIZE];
   FILE *s = NULL;
   uint32 nArgs = 0;
   Bool any = FALSE;
   Bool success = FALSE;
   char **result = NULL;
   char *distroOrig = NULL;

   /* It's OK for the file to not exist, don't warn for this.  */
   if ((fd = Posix_Open(fileName, O_RDONLY)) == -1) {
      return FALSE;
   }

   DynBuf_Init(&b);

   if (fstat(fd, &st)) {
      Warning("%s: could not stat file '%s': %d\n", __FUNCTION__, fileName,
           errno);
      goto out;
   }

   if (st.st_size == 0) {
      Warning("%s: Cannot work with empty file.\n", __FUNCTION__);
      goto out;
   }

   bufSize = st.st_size;

   distroOrig = Util_SafeCalloc(bufSize + 1, sizeof *distroOrig);

   if (read(fd, distroOrig, bufSize) != bufSize) {
      Warning("%s: could not read file '%s': %d\n", __FUNCTION__, fileName,
              errno);
      goto out;
   }

   distroOrig[bufSize] = '\0';

   lseek(fd, 0, SEEK_SET);

   s = fdopen(fd, "r");

   if (s == NULL) {
      Warning("%s: fdopen conversion failed.\n", __FUNCTION__);
      goto out;
   }

   /*
    * Attempt to parse a file with one name=value pair per line. Values are
    * expected to be embedded in double quotes.
    */

   nArgs = 0;
   for (i = 0; values[i] != NULL; i++) {
      nArgs++;
   }
   nArgs++;  // For the appended version of the data

   result = Util_SafeCalloc(nArgs, sizeof(char *));

   while (fgets(lineBuf, sizeof lineBuf, s) != NULL) {
      for (i = 0; values[i] != NULL; i++) {
          size_t len = strlen(values[i]);

          if (strncmp(lineBuf, values[i], len) == 0) {
             char *p;
             char *data;

             if (lineBuf[len] == '"') {
                data = &lineBuf[len + 1];
                p = strrchr(data, '"');

                if (p == NULL) {
                   Warning("%s: Invalid os-release file.", __FUNCTION__);
                   goto out;
                }
             } else {
                data = &lineBuf[len];

                p = strchr(data, '\n');

                if (p == NULL) {
                   Warning("%s: os-release file line too long.",
                           __FUNCTION__);
                   goto out;
                }
             }

             *p = '\0';

             if (p >= &data[MAX_DETAILED_FIELD_LEN]) {
                Warning("%s: Unexpectedly long data encountered; truncated.",
                        __FUNCTION__);

                data[MAX_DETAILED_FIELD_LEN - 1] = '\0';
             }

             if (any) {
                DynBuf_Strcat(&b, " ");
             }

             DynBuf_Strcat(&b, data);
             result[i] = Util_SafeStrdup(data);

             any = TRUE;
          }
      }
   }

   if (ferror(s)) {
       Warning("%s: Error occurred while reading '%s'\n", __FUNCTION__,
               fileName);

       goto out;
   }

   if (DynBuf_GetSize(&b) == 0) {
      /*
       * The distro identification file was not standards compliant.
       */

      if (osReleaseRules) {
         /*
          * We must strictly comply with the os-release standard. Error.
          */

         success = FALSE;
      } else {
         /*
          * Our old code played fast and loose with the LSB standard. If there
          * was a distro identification file but the contents were not LSB
          * compliant (e.g. RH 7.2), we returned success along with the
          * contents "as is"... in the hopes that the available data would
          * be "good enough". Continue the practice to maximize compatibility.
          */

         DynBuf_Strcat(&b, distroOrig);
         DynBuf_Append(&b, "\0", 1);  // Terminate the string

         success = TRUE;
      }
   } else {
      DynBuf_Append(&b, "\0", 1);  // Terminate the string

      success = TRUE;
   }

out:
   if (s != NULL) {
      fclose(s);
   } else if (fd != -1) {
      close(fd);
   }

   free(distroOrig);

   if (success) {
      result[nArgs - 1] = DynBuf_Detach(&b);
   } else {
      Util_FreeStringList(result, nArgs);
      result = NULL;
   }

   DynBuf_Destroy(&b);

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HostinfoGetCmdOutput --
 *
 *      Run a cmd & get its cmd line output
 *
 * Results:
 *      An allocated string or NULL if an error occurred.
 *
 * Side effects:
 *	The cmd is run.
 *
 *----------------------------------------------------------------------
 */

static char *
HostinfoGetCmdOutput(const char *cmd)  // IN:
{
   Bool isSuperUser = FALSE;
   DynBuf db;
   FILE *stream;
   char *out = NULL;

   /*
    * Attempt to lower privs, because we use popen and an attacker
    * may control $PATH.
    */
   if (vmx86_linux && Id_IsSuperUser()) {
      Id_EndSuperUser(getuid());
      isSuperUser = TRUE;
   }

   DynBuf_Init(&db);

   stream = Posix_Popen(cmd, "r");
   if (stream == NULL) {
      Warning("Unable to get output of command \"%s\"\n", cmd);

      goto exit;
   }

   for (;;) {
      char *line = NULL;
      size_t size;

      switch (StdIO_ReadNextLine(stream, &line, 0, &size)) {
      case StdIO_Error:
         goto closeIt;
         break;

      case StdIO_EOF:
         break;

      case StdIO_Success:
         break;

      default:
         NOT_IMPLEMENTED();
      }

      if (line == NULL) {
         break;
      }

      /* size does not include the NUL terminator. */
      DynBuf_Append(&db, line, size);
      free(line);
   }

   /* Return NULL instead of an empty string if there's no output. */
   if (DynBuf_Get(&db) != NULL) {
      out = DynBuf_DetachString(&db);
   }

 closeIt:
   pclose(stream);

 exit:
   DynBuf_Destroy(&db);

   if (isSuperUser) {
      Id_BeginSuperUser();
   }

   return out;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoOsRelease --
 *
 *      Attempt to return the distro identification data we're interested in
 *      from the os-release standard file(s). Look for the os-release data by
 *      following the priority order established by the os-release standard.
 *
 *      The fields of interest are found in osReleaseFields above.
 *
 *      https://www.linux.org/docs/man5/os-release.html
 *
 *      IF THIS ROUTINE IS MODIFIED IN ANY WAY - DIRECTLY OR INDIRECTLY - TO
 *      USE FILES OTHER THAN THOSE OFFICIALLY SANCTIONED BY THE os-release
 *      STANDARD, THE CODE IS NO LONGER IN COMPLIANCE WITH THE os-release
 *      STANDARD AND VMware IS NOT RESPONSIBLE FOR THE BEHAVIOR THAT RESULTS.
 *
 * Return value:
 *      -1     Failure. No data returned.
 *      0..n   Success. A "score", the number of interesting pieces of data
 *             found. The strings found, in the order specified by the
 *             search table above, are returned in args as an array of pointers
 *             to dynamically allocated strings.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static int
HostinfoOsRelease(char ***args)  // OUT:
{
   int score;

   *args = HostinfoReadDistroFile(TRUE, "/etc/os-release", osReleaseFields);

   if (*args == NULL) {
      *args = HostinfoReadDistroFile(TRUE, "/usr/lib/os-release",
                                     osReleaseFields);
   }

   if (*args == NULL) {
      score = -1;
   } else {
      uint32 i;
      size_t fields = ARRAYSIZE(osReleaseFields) - 1;  // Exclude terminator

      score = 0;

      for (i = 0; i < fields; i++) {
         if ((*args)[i] != NULL) {
            score++;
         }
      }
   }

   return score;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoLsbRemoveQuotes --
 *
 *      If present, removes 1 set of double quotes around a LSB output.
 *
 * Return value:
 *      Pointer to a substring of the input
 *
 * Side effects:
 *      Replaces second double quote with a NUL character.
 *
 *-----------------------------------------------------------------------------
 */

static char *
HostinfoLsbRemoveQuotes(char *lsbOutput)  // IN/OUT:
{
   char *lsbStart = lsbOutput;

   ASSERT(lsbStart != NULL);

   if (lsbStart[0] == '"') {
      char *quoteEnd = strchr(++lsbStart, '"');

      if (quoteEnd != NULL) {
         *quoteEnd = '\0';
      }
   }

   return lsbStart;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoLsb --
 *
 *      Attempt to return the distro identification data we're interested in
 *      from the LSB standard file.
 *
 *      The fields of interest are found in lsbFields above.
 *
 *      https://refspecs.linuxfoundation.org/lsb.shtml
 *
 * Return value:
 *      -1     Failure. No data returned.
 *      0..n   Success. A "score", the number of interesting pieces of data
 *             found. The strings found, in the order specified by the
 *             search table above, are returned in args as an array of pointers
 *             to dynamically allocated strings.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static int
HostinfoLsb(char ***args)  // OUT:
{
   uint32 i;
   int score;
   char *lsbOutput;
   size_t fields = ARRAYSIZE(lsbFields) - 1;  // Exclude terminator

   /*
    * In recent times, an increasing number of distros do not have the
    * LSB support installed. Perform a quick check for it and bail if
    * it's not accessible.
    */

   if (access(LSB_RELEASE, F_OK | X_OK) == -1) {
      return -1;
   }

   /*
    * Try to get OS detailed information from the lsb_release command.
    */

   lsbOutput = HostinfoGetCmdOutput(LSB_RELEASE " -sd 2>/dev/null");

   if (lsbOutput == NULL) {
      /*
       * Try to get more detailed information from the version file.
       */

      for (i = 0; distroArray[i].filename != NULL; i++) {
         *args = HostinfoReadDistroFile(FALSE, distroArray[i].filename,
                                        lsbFields);

         if (*args != NULL) {
            break;
         }
      }
   } else {
      *args = Util_SafeCalloc(fields + 1, sizeof(char *));

      /* LSB Description (pretty name) */
      (*args)[fields] = Util_SafeStrdup(HostinfoLsbRemoveQuotes(lsbOutput));
      free(lsbOutput);

      /* LSB Distributor */
      lsbOutput = HostinfoGetCmdOutput(LSB_RELEASE " -si 2>/dev/null");

      if (lsbOutput != NULL) {
         (*args)[0] = Util_SafeStrdup(HostinfoLsbRemoveQuotes(lsbOutput));
         free(lsbOutput);
      }

      /* LSB Release */
      lsbOutput = HostinfoGetCmdOutput(LSB_RELEASE " -sr 2>/dev/null");

      if (lsbOutput != NULL) {
         (*args)[1] = Util_SafeStrdup(HostinfoLsbRemoveQuotes(lsbOutput));
         free(lsbOutput);
      }

      /* LSB Description */
      (*args)[3] = Util_SafeStrdup((*args)[fields]);
   }

   if (*args == NULL) {
      score = -1;
   } else {
      score = 0;

      for (i = 0; i < fields; i++) {
         if ((*args)[i] != NULL) {
            score++;
         }
      }
   }


   return score;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoBestScore --
 *
 *      Return the best distro and distroShort data possible. Do this by
 *      examining the LSB and os-release data and choosing the "best fit".
 *      The "best fit" is determined inspecting the available information
 *      returned by distro identification methods. If none is available,
 *      return a safe, generic result. Otherwise, use the method that has
 *      the highest score (of valid, useful data).
 *
 * Return value:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HostinfoBestScore(char *distro,            // OUT:
                  size_t distroSize,       // IN:
                  char *distroShort,       // OUT:
                  size_t distroShortSize)  // IN:
{
   char **lsbData = NULL;
   char **osReleaseData = NULL;
   int lsbScore = HostinfoLsb(&lsbData);
   int osReleaseScore = HostinfoOsRelease(&osReleaseData);

   /*
    * Now that the os-release standard is long stable, choose it over the LSB
    * standard, all things being the same.
    */

   if ((lsbScore > 0) && (lsbScore > osReleaseScore)) {
      size_t fields = ARRAYSIZE(lsbFields) - 1;  // Exclude terminator

      if (lsbData[0] != NULL) {  // Name
         Str_Strcpy(detailedDataFields[DISTRO_NAME].value, lsbData[0],
                    sizeof detailedDataFields[DISTRO_NAME].value);
      }

      if (lsbData[1] != NULL) {  // Release
         Str_Strcpy(detailedDataFields[DISTRO_VERSION].value, lsbData[1],
                    sizeof detailedDataFields[DISTRO_VERSION].value);
      }

      if (lsbData[3] != NULL) {  // Description
         Str_Strcpy(detailedDataFields[PRETTY_NAME].value, lsbData[3],
                    sizeof detailedDataFields[PRETTY_NAME].value);
      }

      if (lsbData[fields] != NULL) {
         Str_Strcpy(distro, lsbData[fields], distroSize);
      }

      /* If this isn't a recognized distro, specify a default. */
      if (!HostinfoGetOSShortName(distro, lsbData[1], distroShort,
                                  distroShortSize)) {
         HostinfoDefaultLinux(NULL, 0, distroShort, distroShortSize);
      }

      goto bail;
   }

   if (osReleaseScore > 0) {
      size_t fields = ARRAYSIZE(osReleaseFields) - 1;  // Exclude terminator

      if (osReleaseData[0] != NULL) {
         Str_Strcpy(detailedDataFields[PRETTY_NAME].value, osReleaseData[0],
                    sizeof detailedDataFields[PRETTY_NAME].value);
      }

      if (osReleaseData[1] != NULL) {
         Str_Strcpy(detailedDataFields[DISTRO_NAME].value, osReleaseData[1],
                    sizeof detailedDataFields[DISTRO_NAME].value);
      }

      if (osReleaseData[2] != NULL) {
         Str_Strcpy(detailedDataFields[DISTRO_VERSION].value, osReleaseData[2],
                    sizeof detailedDataFields[DISTRO_VERSION].value);
      }

      if (osReleaseData[3] != NULL) {
         Str_Strcpy(detailedDataFields[BUILD_NUMBER].value, osReleaseData[3],
                    sizeof detailedDataFields[BUILD_NUMBER].value);
      }

      if (osReleaseData[4] != NULL) {
         Str_Strcpy(detailedDataFields[DISTRO_ADDL_VERSION].value,
                    osReleaseData[4],
                    sizeof detailedDataFields[DISTRO_ADDL_VERSION].value);
      }

      if (osReleaseData[5] != NULL) {
         Str_Strcpy(detailedDataFields[CPE_STRING].value, osReleaseData[5],
                    sizeof detailedDataFields[CPE_STRING].value);
      }

      if (osReleaseData[fields] != NULL) {
         Str_Strcpy(distro, osReleaseData[fields], distroSize);
      }

      /* If this isn't a recognized distro, specify a default. */
      if (!HostinfoGetOSShortName(distro, osReleaseData[2],
                                  distroShort, distroShortSize)){
         HostinfoDefaultLinux(NULL, 0, distroShort, distroShortSize);
      }

      goto bail;
   }

   /* Not LSB or os-release compliant. Report something generic. */
   HostinfoDefaultLinux(distro, distroSize, distroShort, distroShortSize);

bail:

   if (lsbData != NULL) {
      Util_FreeStringList(lsbData, ARRAYSIZE(lsbFields));
   }

   if (osReleaseData != NULL) {
      Util_FreeStringList(osReleaseData, ARRAYSIZE(osReleaseFields));
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoLinux --
 *
 *      Determine the specifics concerning Linux.
 *
 * Return value:
 *      TRUE   Success
 *      FALSE  Failure
 *
 * Side effects:
 *      Cache values are set when returning TRUE
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoLinux(struct utsname *buf)  // IN:
{
   int len;
   char distro[DISTRO_BUF_SIZE];
   char distroShort[DISTRO_BUF_SIZE];
   char osName[MAX_OS_NAME_LEN];
   char osNameFull[MAX_OS_FULLNAME_LEN];

   HostinfoBestScore(distro, sizeof distro, distroShort, sizeof distroShort);

   len = Str_Snprintf(osNameFull, sizeof osNameFull, "%s %s %s", buf->sysname,
                      buf->release, distro);

   if (len != -1) {
      if (Hostinfo_GetSystemBitness() == 64) {
         len = Str_Snprintf(osName, sizeof osName, "%s%s", distroShort,
                            STR_OS_64BIT_SUFFIX);
      } else {
         len = Str_Snprintf(osName, sizeof osName, "%s", distroShort);
      }
   }

   if (len == -1) {
      Warning("%s: Error: buffer too small\n", __FUNCTION__);
   } else {
      HostinfoPostData(osName, osNameFull);
   }

   return (len != -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoBSD --
 *
 *      Determine the specifics concerning BSD.
 *
 * Return value:
 *      TRUE   Success
 *      FALSE  Failure
 *
 * Side effects:
 *      Cache values are set when returning TRUE
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoBSD(struct utsname *buf)  // IN:
{
   int len;
   int majorVersion;
   char distroShort[DISTRO_BUF_SIZE];
   char osName[MAX_OS_NAME_LEN];
   char osNameFull[MAX_OS_FULLNAME_LEN];

   /*
    * FreeBSD releases report their version as "x.y-RELEASE".
    */

   majorVersion = Hostinfo_OSVersion(0);

   /*
    * FreeBSD 11 and later are identified using a different guest ID than
    * older FreeBSD.
    */

   if (majorVersion < 11) {
      Str_Strcpy(distroShort, STR_OS_FREEBSD, sizeof distroShort);
   } else {
      Str_Sprintf(distroShort, sizeof distroShort, "%s%s%d",
                  HostinfoArchString(), STR_OS_FREEBSD, majorVersion);
   }

   len = Str_Snprintf(osNameFull, sizeof osNameFull, "%s %s", buf->sysname,
                      buf->release);

   if (len != -1) {
      if (Hostinfo_GetSystemBitness() == 64) {
         len = Str_Snprintf(osName, sizeof osName, "%s%s", distroShort,
                            STR_OS_64BIT_SUFFIX);
      } else {
         len = Str_Snprintf(osName, sizeof osName, "%s", distroShort);
      }
   }

   if (len == -1) {
      Warning("%s: Error: buffer too small\n", __FUNCTION__);
   } else {
      HostinfoPostData(osName, osNameFull);
   }

   return (len != -1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSun --
 *
 *      Determine the specifics concerning Sun.
 *
 * Return value:
 *      TRUE   Success
 *      FALSE  Failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSun(struct utsname *buf)  // IN:
{
   int len;
   char osName[MAX_OS_NAME_LEN];
   char osNameFull[MAX_OS_FULLNAME_LEN];
   char solarisRelease[3] = "";

   /*
    * Solaris releases report their version as "x.y". For our supported
    * releases it seems that x is always "5", and is ignored in favor of "y"
    * for the version number.
    */

   if (sscanf(buf->release, "5.%2[0-9]", solarisRelease) != 1) {
      return FALSE;
   }

   len = Str_Snprintf(osNameFull, sizeof osNameFull, "%s %s", buf->sysname,
                      buf->release);

   if (len != -1) {
      if (Hostinfo_GetSystemBitness() == 64) {
         len = Str_Snprintf(osName, sizeof osName, "%s%s%s", STR_OS_SOLARIS,
                            solarisRelease, STR_OS_64BIT_SUFFIX);
      } else {
         len = Str_Snprintf(osName, sizeof osName, "%s%s", STR_OS_SOLARIS,
                            solarisRelease);
      }
   }

   if (len == -1) {
      Warning("%s: Error: buffer too small\n", __FUNCTION__);
   } else {
      HostinfoPostData(osName, osNameFull);
   }

   return (len != -1);
}
#endif // !defined(__APPLE__) && !defined(VMX86_SERVER) && !defined(USERWORLD)


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoOSData --
 *
 *      Determine the OS short (.vmx format) and long names.
 *
 * Return value:
 *      TRUE   Success
 *      FALSE  Failure
 *
 * Side effects:
 *      Cache values are set when returning TRUE.
 *
 *-----------------------------------------------------------------------------
 */

Bool
HostinfoOSData(void)
{
   Bool success;
   struct utsname buf;
   const char *bitness;

   /*
    * Use uname to get complete OS information.
    */

   if (uname(&buf) < 0) {
      Warning("%s: uname failed %d\n", __FUNCTION__, errno);

      return FALSE;
   }

   Str_Strcpy(detailedDataFields[FAMILY_NAME].value, buf.sysname,
              sizeof detailedDataFields[FAMILY_NAME].value);
   Str_Strcpy(detailedDataFields[KERNEL_VERSION].value, buf.release,
              sizeof detailedDataFields[KERNEL_VERSION].value);
   /* Default distro name is set to uname's sysname field */
   Str_Strcpy(detailedDataFields[DISTRO_NAME].value, buf.sysname,
              sizeof detailedDataFields[DISTRO_NAME].value);

#if defined(VMX86_SERVER) || defined(USERWORLD)  // ESXi
   bitness = "64";
#else
   bitness = (Hostinfo_GetSystemBitness() == 64) ? "64" : "32";
#endif
   Str_Strcpy(detailedDataFields[BITNESS].value, bitness,
              sizeof detailedDataFields[BITNESS].value);

#if defined(VMX86_SERVER) || defined(USERWORLD)  // ESXi
   success = HostinfoESX(&buf);
#elif defined(__APPLE__) // MacOS
   success = HostinfoMacOS(&buf);
#else
   if (strstr(buf.sysname, "Linux")) {
      success = HostinfoLinux(&buf);
   } else if (strstr(buf.sysname, "FreeBSD")) {
      success = HostinfoBSD(&buf);
   } else if (strstr(buf.sysname, "SunOS")) {
      success = HostinfoSun(&buf);
   } else {
      success = FALSE;  // Unknown to us
   }
#endif

   /* Build detailed data */
   HostinfoOSDetailedData();

   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_NumCPUs --
 *
 *      Get the number of logical CPUs on the host.  If the CPUs are
 *      hyperthread-capable, this number may be larger than the number of
 *      physical CPUs.  For example, if the host has four hyperthreaded
 *      physical CPUs with 2 logical CPUs apiece, this function returns 8.
 *
 *      This function returns the number of CPUs that the host presents to
 *      applications, which is what we want in the vast majority of cases.  We
 *      would only ever care about the number of physical CPUs for licensing
 *      purposes.
 *
 * Results:
 *      On success, the number of CPUs (> 0) the host tells us we have.
 *      On failure, 0xFFFFFFFF (-1).
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

uint32
Hostinfo_NumCPUs(void)
{
#if defined(sun)
   static int count = 0;

   if (count <= 0) {
      count = sysconf(_SC_NPROCESSORS_CONF);
   }

   return count;
#elif defined(__APPLE__)
   uint32 out;
   size_t outSize = sizeof out;

   /*
    * Quoting sys/sysctl.h:
    * "
    * These are the support HW selectors for sysctlbyname.  Parameters that
    * are byte counts or frequencies are 64 bit numbers. All other parameters
    * are 32 bit numbers.
    * ...
    * hw.activecpu - The number of processors currently available for executing
    *                threads. Use this number to determine the number threads
    *                to create in SMP aware applications. This number can
    *                change when power management modes are changed.
    * "
    *
    * Apparently the only way to retrieve this info is by name, and I have
    * verified the info changes when you dynamically switch a CPU
    * offline/online. --hpreg
    */

   if (sysctlbyname("hw.activecpu", &out, &outSize, NULL, 0) == -1) {
      return -1;
   }

   return out;
#elif defined(__FreeBSD__)
   uint32 out;
   size_t outSize = sizeof out;

#if __FreeBSD__version >= 500019
   if (sysctlbyname("kern.smp.cpus", &out, &outSize, NULL, 0) == -1) {
      return -1;
   }
#else
   if (sysctlbyname("machdep.smp_cpus", &out, &outSize, NULL, 0) == -1) {
      if (errno == ENOENT) {
         out = 1;
      } else {
         return -1;
      }
   }
#endif

   return out;
#else
   static int count = 0;

   if (count <= 0) {
      FILE *f;
      char *line;

#if defined(VMX86_SERVER)
      if (HostType_OSIsVMK()) {
         VMK_ReturnStatus status = VMKernel_GetNumCPUsUsed(&count);

         if (status != VMK_OK) {
            count = 0;

            return -1;
         }

         return count;
      }
#endif
      f = Posix_Fopen("/proc/cpuinfo", "r");
      if (f == NULL) {
	 return -1;
      }

      while (StdIO_ReadNextLine(f, &line, 0, NULL) == StdIO_Success) {
	 if (strncmp(line, "processor", strlen("processor")) == 0) {
	    count++;
	 }
	 free(line);
      }

      fclose(f);

      if (count == 0) {
	 return -1;
      }
   }

   return count;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_NameGet --
 *
 *      Return the fully qualified host name of the host.
 *      Thread-safe. --hpreg
 *
 * Results:
 *      The (memorized) name on success
 *      NULL on failure
 *
 * Side effects:
 *      A host name resolution can occur.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_NameGet(void)
{
   char *result;

   static Atomic_Ptr state; /* Implicitly initialized to NULL. --hpreg */

   result = Atomic_ReadPtr(&state);

   if (UNLIKELY(result == NULL)) {
      char *before;

      result = Hostinfo_HostName();

      before = Atomic_ReadIfEqualWritePtr(&state, NULL, result);

      if (before) {
         free(result);

         result = before;
      }
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetUser --
 *
 *      Return current user name, or NULL if can't tell.
 *      XXX Not thread-safe (somebody could do a setenv()). --hpreg
 *
 * Results:
 *      User name.  Must be free()d by caller.
 *
 * Side effects:
 *	No.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_GetUser(void)
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   char *env = NULL;
   char *name = NULL;

   if ((Posix_Getpwuid_r(getuid(), &pw, buffer, sizeof buffer, &ppw) == 0) &&
       (ppw != NULL)) {
      if (ppw->pw_name) {
         name = Unicode_Duplicate(ppw->pw_name);
      }
   }

   if (!name) {
      env = Posix_Getenv("USER");

      if (env) {
         name = Unicode_Duplicate(env);
      }
   }

   return name;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoGetLoadAverage --
 *
 *      Returns system average load.
 *
 * Results:
 *      TRUE on success, FALSE otherwise.
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoGetLoadAverage(float *avg0,  // IN/OUT:
                       float *avg1,  // IN/OUT:
                       float *avg2)  // IN/OUT:
{
#if !defined(NO_GETLOADAVG) && (defined(__linux__) && !defined(__UCLIBC__)) || defined(__APPLE__)
   double avg[3];
   int res;

   res = getloadavg(avg, 3);
   if (res < 3) {
      NOT_TESTED_ONCE();

      return FALSE;
   }

   if (avg0) {
      *avg0 = (float) avg[0];
   }
   if (avg1) {
      *avg1 = (float) avg[1];
   }
   if (avg2) {
      *avg2 = (float) avg[2];
   }

   return TRUE;
#else
   /*
    * Not implemented. This function is currently only used in the vmx, so
    * getloadavg is always available to us. If the linux tools ever need this,
    * we can go back to having a look at the output of /proc/loadavg, but
    * let's not do that now as long as it's not necessary.
    */

   NOT_IMPLEMENTED();

   return FALSE;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetLoadAverage --
 *
 *      Returns system average load * 100.
 *
 * Results:
 *      TRUE/FALSE
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Hostinfo_GetLoadAverage(uint32 *avg)  // IN/OUT:
{
   float avg0 = 0;

   if (!HostinfoGetLoadAverage(&avg0, NULL, NULL)) {
      return FALSE;
   }

   *avg = (uint32) 100 * avg0;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_LogLoadAverage --
 *
 *      Logs system average load.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

void
Hostinfo_LogLoadAverage(void)
{
   float avg0 = 0, avg1 = 0, avg2 = 0;

   if (HostinfoGetLoadAverage(&avg0, &avg1, &avg2)) {
      Log("LOADAVG: %.2f %.2f %.2f\n", avg0, avg1, avg2);
   }
}


#if __APPLE__
/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSystemTimerMach --
 *
 *      Returns system time based on a monotonic, nanosecond-resolution,
 *      fast timer provided by the Mach kernel. Requires speed conversion
 *      so is non-trivial (but lockless).
 *
 *      See also Apple TechNote QA1398.
 *
 *      NOTE: on x86, macOS does TSC->ns conversion in the commpage
 *      for mach_absolute_time() to correct for speed-stepping, so x86
 *      should always be 1:1 a.k.a. 'unity'.
 *
 *      On iOS, mach_absolute_time() uses an ARM register and always
 *      needs conversion.
 *
 * Results:
 *      Current value of timer
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

static VmTimeType
HostinfoSystemTimerMach(void)
{
   static Atomic_uint64 machToNS;

   union {
      uint64 raw;
      RateConv_Ratio ratio;
   } u;
   VmTimeType result;

   u.raw = Atomic_Read64(&machToNS);

   if (UNLIKELY(u.raw == 0)) {
      mach_timebase_info_data_t timeBase;
      kern_return_t kr;

      /* Ensure atomic works correctly */
      ASSERT_ON_COMPILE(sizeof u == sizeof(machToNS));

      kr = mach_timebase_info(&timeBase);
      ASSERT(kr == KERN_SUCCESS);

      /*
       * Officially, TN QA1398 recommends using a static variable and
       *    NS = mach_absolute_time() * timeBase.numer / timebase.denom
       * (where denom != 0 is an obvious init check).
       * In practice...
       * x86 (incl x86_64) has only been seen using 1/1
       * iOS has been seen to use 125/3 (~24MHz)
       * PPC has been seen to use 1000000000/33333335 (~33MHz),
       *     which overflows 64-bit multiply in ~8 seconds (!!)
       *     (2^63 signed bits / 2^30 numer / 2^30 ns/sec ~= 2^3)
       * We will use fixed-point for everything because it's faster
       * than floating point and (with a 128-bit multiply) cannot overflow.
       * Even in the 'unity' case, the four instructions in x86_64 fixed-point
       * are about as expensive as checking for 'unity'.
       */
      if (timeBase.numer == 1 && timeBase.denom == 1) {
         u.ratio.mult = 1;  // Trivial conversion
         u.ratio.shift = 0;
      } else {
         Bool status;
         status = RateConv_ComputeRatio(timeBase.denom, timeBase.numer,
                                        &u.ratio);
         VERIFY(status);  // Assume we can get fixed-point parameters
      }

      /*
       * Use ReadWrite for implicit barrier.
       * Initialization is idempotent, so no multi-init worries.
       * The fixed-point conversions are stored in an atomic to ensure
       * they are never read "torn".
       */
      Atomic_ReadWrite64(&machToNS, u.raw);
      ASSERT(u.raw != 0);  // Used as initialization check
   }

   /* Fixed-point */
   result = Muls64x32s64(mach_absolute_time(),
                         u.ratio.mult, u.ratio.shift);

   /*
    * A smart programmer would use a global variable to ASSERT that
    * mach_absolute_time() and/or the fixed-point result is non-decreasing.
    * This turns out to be impractical: the synchronization needed to safely
    * make that check can prevent the very effect being checked.
    * Thus, we simply trust the documentation.
    */

   ASSERT(result >= 0);
   return result;
}
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_SystemTimerNS --
 *
 *      Return the time.
 *         - These timers are documented to be non-decreasing
 *         - These timers never take locks
 *
 * NOTES:
 *      These are the routines to use when performing timing measurements.
 *
 *      The actual resolution of these "clocks" are undefined - it varies
 *      depending on hardware, OSen and OS versions.
 *
 *     *** NOTE: This function and all children must be callable
 *     while RANK_logLock is held. ***
 *
 * Results:
 *      The time in nanoseconds is returned.
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

VmTimeType
Hostinfo_SystemTimerNS(void)
{
#ifdef __APPLE__
   return HostinfoSystemTimerMach();
#else
   struct timespec ts;
   int ret;

   /*
    * clock_gettime() is implemented on Linux as a commpage routine that
    * adds a known offset to TSC, which makes it very fast. Other OSes...
    * are at worst a single syscall (see: vmkernel PR820064), which still
    * makes this the best time API. Also, clock_gettime() allows nanosecond
    * resolution and any alternative is worse: gettimeofday() is microsecond.
    */
   ret = clock_gettime(CLOCK_MONOTONIC, &ts);
   ASSERT(ret == 0);

   return (VmTimeType)ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_LogMemUsage --
 *      Log system memory usage.
 *
 * Results:
 *      System memory usage is logged.
 *
 * Side effects:
 *      No.
 *
 *-----------------------------------------------------------------------------
 */

void
Hostinfo_LogMemUsage(void)
{
   int fd = Posix_Open("/proc/self/statm", O_RDONLY);

   if (fd != -1) {
      size_t len;
      char buf[64];

      len = read(fd, buf, sizeof buf);
      close(fd);

      if (len != -1) {
         int a[7] = { 0 };

         buf[len < sizeof buf ? len : sizeof buf - 1] = '\0';

         sscanf(buf, "%d %d %d %d %d %d %d",
                &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6]);

         Log("RUSAGE size=%d resident=%d share=%d trs=%d lrs=%d drs=%d dt=%d\n",
             a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_ResetProcessState --
 *
 *      Clean up signal handlers and file descriptors before an exec().
 *      Fds which need to be kept open can be passed as an array.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Hostinfo_ResetProcessState(const int *keepFds, // IN:
                           size_t numKeepFds)  // IN:
{
   int s, fd;
   struct sigaction sa;
   struct rlimit rlim;

   /*
    * Disable itimers before resetting the signal handlers.
    * Otherwise, the process may still receive timer signals:
    * SIGALRM, SIGVTARLM, or SIGPROF.
    */

   struct itimerval it;
   it.it_value.tv_sec = it.it_value.tv_usec = 0;
   it.it_interval.tv_sec = it.it_interval.tv_usec = 0;
   setitimer(ITIMER_REAL, &it, NULL);
   setitimer(ITIMER_VIRTUAL, &it, NULL);
   setitimer(ITIMER_PROF, &it, NULL);

   for (s = 1; s <= NSIG; s++) {
      sa.sa_handler = SIG_DFL;
      sigfillset(&sa.sa_mask);
      sa.sa_flags = SA_RESTART;
      sigaction(s, &sa, NULL);
   }

   for (fd = (int) sysconf(_SC_OPEN_MAX) - 1; fd > STDERR_FILENO; fd--) {
      size_t i;

      for (i = 0; i < numKeepFds; i++) {
         if (fd == keepFds[i]) {
            break;
         }
      }
      if (i == numKeepFds) {
         (void) close(fd);
      }
   }

   if (getrlimit(RLIMIT_AS, &rlim) == 0) {
      rlim.rlim_cur = rlim.rlim_max;
      setrlimit(RLIMIT_AS, &rlim);
   }

#ifdef __linux__
#ifndef NO_IOPL
   /*
    * Drop iopl to its default value.
    * iopl() is not implemented in userworlds
    */
   if (!vmx86_server) {
      int err;
      uid_t euid;

      euid = Id_GetEUid();
      /* At this point, _unless we are running as root_, we shouldn't have root
         privileges --hpreg */
      ASSERT(euid != 0 || getuid() == 0);
      Id_SetEUid(0);
      err = iopl(0);
      Id_SetEUid(euid);
      VERIFY(err == 0);
   }
#endif
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_Daemonize --
 *
 *      Cross-platform daemon(3)-like wrapper.
 *
 *      Restarts the current process as a daemon, given the path to the
 *      process (usually from Hostinfo_GetModulePath).  This means:
 *
 *         * You're detached from your parent.  (Your parent doesn't
 *           need to wait for you to exit.)
 *         * Your process no longer has a controlling terminal or
 *           process group.
 *         * Your stdin/stdout/stderr fds are redirected to /dev/null. All
 *           other descriptors, except for the ones that are passed in the
 *           parameter keepFds, are closed.
 *         * Your signal handlers are reset to SIG_DFL in the daemonized
 *           process, and all the signals are unblocked.
 *         * Your main() function is called with the specified NULL-terminated
 *           argument list.
 *
 *      (Don't forget that the first string in args is argv[0] -- the
 *      name of the process).
 *
 *      Unless 'flags' contains HOSTINFO_DAEMONIZE_NOCHDIR, then the
 *      current directory of the daemon process is set to "/".
 *
 *      Unless 'flags' contains HOSTINFO_DAEMONIZE_NOCLOSE, then all stdio
 *      file descriptors of the daemon process are redirected to /dev/null.
 *      This is true even if the stdio descriptors are included in keepFds,
 *      i.e. the list of fds to be kept open.
 *
 *      If 'flags' contains HOSTINFO_DAEMONIZE_EXIT, then upon successful
 *      launch of the daemon, the original process will exit.
 *
 *      If pidPath is non-NULL, then upon success, writes the PID
 *      (as a US-ASCII string followed by a newline) of the daemon
 *      process to that path.
 *
 *      If 'flags' contains HOSTINFO_DAEMONIZE_LOCKPID and pidPath is
 *      non-NULL, then an exclusive flock(2) is taken on pidPath to prevent
 *      multiple instances of the service from running.
 *
 * Results:
 *      FALSE if the process could not be daemonized.  errno contains
 *      the error on failure.
 *      TRUE if 'flags' does not contain HOSTINFO_DAEMONIZE_EXIT and
 *      the process was daemonized.
 *      Otherwise, if the process was daemonized, this function does
 *      not return, and flow continues from your own main() function.
 *
 * Side effects:
 *      The current process is restarted with the given arguments.
 *      The process state is reset (see Hostinfo_ResetProcessState).
 *      A new session is created (so the process has no controlling terminal).
 *
 *-----------------------------------------------------------------------------
 */

Bool
Hostinfo_Daemonize(const char *path,             // IN: NUL-terminated UTF-8
                                                 // path to exec
                   char * const *args,           // IN: NULL-terminated UTF-8
                                                 // argv list
                   HostinfoDaemonizeFlags flags, // IN: flags
                   const char *pidPath,          // IN/OPT: NUL-terminated
                                                 // UTF-8 path to write PID
                   const int *keepFds,           // IN/OPT: array of fds to be
                                                 // kept open
                   size_t numKeepFds)            // IN: number of fds in
                                                 // keepFds
{
   /*
    * We use the double-fork method to make a background process whose
    * parent is init instead of the original process.
    *
    * We do this instead of calling daemon(), because daemon() is
    * deprecated on Mac OS 10.5 hosts, and calling it causes a compiler
    * warning.
    *
    * We must exec() after forking, because Mac OS library frameworks
    * depend on internal Mach ports, which are not correctly propagated
    * across fork calls.  exec'ing reinitializes the frameworks, which
    * causes them to reopen their Mach ports.
    */

   int pidPathFd = -1;
   int childPid;
   int pipeFds[2] = { -1, -1 };
   uint32 err = EINVAL;
   char *pathLocalEncoding = NULL;
   char **argsLocalEncoding = NULL;
   int *tempFds = NULL;
   size_t numTempFds = numKeepFds + 1;
   sigset_t sig;

   ASSERT_ON_COMPILE(sizeof (errno) <= sizeof err);
   ASSERT(args);
   ASSERT(path);
   ASSERT(numKeepFds == 0 || keepFds);

   if (pidPath) {
      pidPathFd = Posix_Open(pidPath, O_WRONLY | O_CREAT, 0644);
      if (pidPathFd == -1) {
         err = errno;
         Warning("%s: Couldn't open PID path [%s], error %u.\n",
                 __FUNCTION__, pidPath, err);
         errno = err;
         return FALSE;
      }

      /*
       * Lock this file to take a mutex on daemonizing this process. The child
       * will keep this file descriptor open for as long as it is running.
       *
       * flock(2) is a BSD extension (also supported on Linux) which creates a
       * lock that is inherited by the child after fork(2). fcntl(2) locks do
       * not have this property. Solaris only supports fcntl(2) locks.
       */
#ifndef sun
      if ((flags & HOSTINFO_DAEMONIZE_LOCKPID) &&
          flock(pidPathFd, LOCK_EX | LOCK_NB) == -1) {
         err = errno;
         Warning("%s: Lock held on PID path [%s], error %u, not daemonizing.\n",
                 __FUNCTION__, pidPath, err);
         errno = err;
         close(pidPathFd);
         return FALSE;
      }
#endif

      numTempFds++;
   }

   if (pipe(pipeFds) == -1) {
      err = errno;
      Warning("%s: Couldn't create pipe, error %u.\n", __FUNCTION__, err);
      pipeFds[0] = pipeFds[1] = -1;
      goto cleanup;
   }

   tempFds = malloc(sizeof tempFds[0] * numTempFds);
   if (!tempFds) {
      err = errno;
      Warning("%s: Couldn't allocate memory, error %u.\n", __FUNCTION__, err);
      goto cleanup;
   }
   if (keepFds) {
      memcpy(tempFds, keepFds, sizeof tempFds[0] * numKeepFds);
   }
   tempFds[numKeepFds++] = pipeFds[1];
   if (pidPath) {
      tempFds[numKeepFds++] = pidPathFd;
   }

   if (fcntl(pipeFds[1], F_SETFD, 1) == -1) {
      err = errno;
      Warning("%s: Couldn't set close-on-exec for fd %d, error %u.\n",
              __FUNCTION__, pipeFds[1], err);
      goto cleanup;
   }

   /* Convert the strings from UTF-8 before we fork. */
   pathLocalEncoding = Unicode_GetAllocBytes(path, STRING_ENCODING_DEFAULT);
   if (!pathLocalEncoding) {
      Warning("%s: Couldn't convert path [%s] to default encoding.\n",
              __FUNCTION__, path);
      goto cleanup;
   }

   argsLocalEncoding = Unicode_GetAllocList(args, STRING_ENCODING_DEFAULT, -1);
   if (!argsLocalEncoding) {
      Warning("%s: Couldn't convert arguments to default encoding.\n",
              __FUNCTION__);
      goto cleanup;
   }

   childPid = fork();

   switch (childPid) {
   case -1:
      err = errno;
      Warning("%s: Couldn't fork first child, error %u.\n", __FUNCTION__,
              err);
      goto cleanup;
   case 0:
      /* We're the first child.  Continue on. */
      break;
   default:
      {
         /* We're the original process.  Check if the first child exited. */
         int status;

         close(pipeFds[1]);
         waitpid(childPid, &status, 0);
         if (WIFEXITED(status) && WEXITSTATUS(status) != EXIT_SUCCESS) {
            Warning("%s: Child %d exited with error %d.\n",
                    __FUNCTION__, childPid, WEXITSTATUS(status));
            goto cleanup;
         } else if (WIFSIGNALED(status)) {
            Warning("%s: Child %d exited with signal %d.\n",
                    __FUNCTION__, childPid, WTERMSIG(status));
            goto cleanup;
         }

         /*
          * Check if the second child exec'ed successfully.  If it had
          * an error, it will write a uint32 errno to this pipe before
          * exiting.  Otherwise, its end of the pipe will be closed on
          * exec and this call will fail as expected.
          * The assumption is that we don't get a partial read. In case,
          * it did happen, we can detect it by the number of bytes read.
          */

         while (TRUE) {
            int res = read(pipeFds[0], &err, sizeof err);

            if (res > 0) {
               Warning("%s: Child could not exec %s, read %d, error %u.\n",
                       __FUNCTION__, path, res, err);
               goto cleanup;
            } else if ((res == -1) && (errno == EINTR)) {
               continue;
            }
            break;
         }

         err = 0;
         goto cleanup;
      }
   }

   /*
    * Close all fds except for the write end of the error pipe (which we've
    * already set to close on successful exec), the pid file, and the ones
    * requested by the caller. Also reset the signal mask to unblock all
    * signals. fork() clears pending signals.
    */

   Hostinfo_ResetProcessState(tempFds, numKeepFds);
   free(tempFds);
   tempFds = NULL;
   sigfillset(&sig);
   sigprocmask(SIG_UNBLOCK, &sig, NULL);

   if (!(flags & HOSTINFO_DAEMONIZE_NOCLOSE) && setsid() == -1) {
      Warning("%s: Couldn't create new session, error %d.\n",
              __FUNCTION__, errno);

      _exit(EXIT_FAILURE);
   }

   switch (fork()) {
   case -1:
      {
         Warning("%s: Couldn't fork second child, error %d.\n",
                 __FUNCTION__, errno);

         _exit(EXIT_FAILURE);
      }
   case 0:
      // We're the second child.  Continue on.
      break;
   default:
      /*
       * We're the first child.  We don't need to exist any more.
       *
       * Exiting here causes the second child to be reparented to the
       * init process, so the original process doesn't need to wait
       * for the child we forked off.
       */

      _exit(EXIT_SUCCESS);
   }

   /*
    * We can't use our i18n wrappers for file manipulation at this
    * point, since we've forked; internal library mutexes might be
    * invalid.
    */

   if (!(flags & HOSTINFO_DAEMONIZE_NOCHDIR) && chdir("/") == -1) {
      uint32 err = errno;

      Warning("%s: Couldn't chdir to /, error %u.\n", __FUNCTION__, err);

      /* Let the original process know we failed to chdir. */
      if (write(pipeFds[1], &err, sizeof err) == -1) {
         Warning("%s: Couldn't write to parent pipe: %u, "
                 "original error: %u.\n", __FUNCTION__, errno, err);
      }
      _exit(EXIT_FAILURE);
   }

   if (!(flags & HOSTINFO_DAEMONIZE_NOCLOSE)) {
      int fd;

      fd = open(_PATH_DEVNULL, O_RDONLY);
      if (fd != -1) {
         dup2(fd, STDIN_FILENO);
         close(fd);
      }

      fd = open(_PATH_DEVNULL, O_WRONLY);
      if (fd != -1) {
         dup2(fd, STDOUT_FILENO);
         dup2(fd, STDERR_FILENO);
         close(fd);
      }
   }

   if (pidPath) {
      int64 pid;
      char pidString[32];
      int pidStringLen;

      ASSERT_ON_COMPILE(sizeof (pid_t) <= sizeof pid);
      ASSERT(pidPathFd >= 0);

      pid = getpid();
      pidStringLen = Str_Sprintf(pidString, sizeof pidString,
                                 "%"FMT64"d\n", pid);
      if (pidStringLen <= 0) {
         err = EINVAL;

         if (write(pipeFds[1], &err, sizeof err) == -1) {
            Warning("%s: Couldn't write to parent pipe: %u, original "
                    "error: %u.\n", __FUNCTION__, errno, err);
         }
         _exit(EXIT_FAILURE);
      }

      if (ftruncate(pidPathFd, 0) == -1) {
         err = errno;
         Warning("%s: Couldn't truncate path [%s], error %d.\n",
                 __FUNCTION__, pidPath, err);

         if (write(pipeFds[1], &err, sizeof err) == -1) {
            Warning("%s: Couldn't write to parent pipe: %u, original "
                    "error: %u.\n", __FUNCTION__, errno, err);
         }
         _exit(EXIT_FAILURE);
      }

      if (write(pidPathFd, pidString, pidStringLen) != pidStringLen) {
         err = errno;
         Warning("%s: Couldn't write PID to path [%s], error %d.\n",
                 __FUNCTION__, pidPath, err);

         if (write(pipeFds[1], &err, sizeof err) == -1) {
            Warning("%s: Couldn't write to parent pipe: %u, original "
                    "error: %u.\n", __FUNCTION__, errno, err);
         }
         _exit(EXIT_FAILURE);
      }

      if (fsync(pidPathFd) == -1) {
         err = errno;
         Warning("%s: Couldn't flush PID to path [%s], error %d.\n",
                 __FUNCTION__, pidPath, err);

         if (write(pipeFds[1], &err, sizeof err) == -1) {
            Warning("%s: Couldn't write to parent pipe: %u, original "
                    "error: %u.\n", __FUNCTION__, errno, err);
         }
         _exit(EXIT_FAILURE);
      }

      /* Leave pidPathFd open to hold the mutex until this process exits. */
      if (!(flags & HOSTINFO_DAEMONIZE_LOCKPID)) {
         close(pidPathFd);
      }
   }

   if (execv(pathLocalEncoding, argsLocalEncoding) == -1) {
      err = errno;
      Warning("%s: Couldn't exec %s, error %d.\n", __FUNCTION__, path, err);

      /* Let the original process know we failed to exec. */
      if (write(pipeFds[1], &err, sizeof err) == -1) {
         Warning("%s: Couldn't write to parent pipe: %u, "
                 "original error: %u.\n", __FUNCTION__, errno, err);
      }
      _exit(EXIT_FAILURE);
   }

   NOT_REACHED();

  cleanup:
   free(tempFds);

   if (pipeFds[0] != -1) {
      close(pipeFds[0]);
   }
   if (pipeFds[1] != -1) {
      close(pipeFds[1]);
   }
   Util_FreeStringList(argsLocalEncoding, -1);
   free(pathLocalEncoding);

   if (err == 0) {
      if (flags & HOSTINFO_DAEMONIZE_EXIT) {
         _exit(EXIT_SUCCESS);
      }
   } else {
      if (pidPath) {
         /*
          * Unlink pidPath on error before closing pidPathFd to avoid racing
          * with another process attempting to daemonize and unlinking the
          * file it created instead.
          */
         if (Posix_Unlink(pidPath) != 0) {
            Warning("%s: Unable to unlink %s: %u\n",
                    __FUNCTION__, pidPath, errno);
         }
      }

      errno = err;
   }

   if (pidPath) {
      close(pidPathFd);
   }

   return err == 0;
}


#if !defined(__APPLE__) && !defined(__FreeBSD__)
/*
 *----------------------------------------------------------------------
 *
 * HostinfoGetCpuInfo --
 *
 *      Get some attribute from /proc/cpuinfo for a given CPU
 *
 * Results:
 *      On success: Allocated, NUL-terminated attribute string.
 *      On failure: NULL.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static char *
HostinfoGetCpuInfo(int nCpu,         // IN:
                   const char *name) // IN:
{
   FILE *f;
   char *line;
   int cpu = 0;
   char *value = NULL;

   f = Posix_Fopen("/proc/cpuinfo", "r");

   if (f == NULL) {
      Warning(LGPFX" %s: Unable to open /proc/cpuinfo\n", __FUNCTION__);

      return NULL;
   }

   while (cpu <= nCpu &&
          StdIO_ReadNextLine(f, &line, 0, NULL) == StdIO_Success) {
      char *s;

      if ((s = strstr(line, name)) &&
          (s = strchr(s, ':'))) {
         char *e;

         s++;
         e = s + strlen(s);

         /* Skip leading and trailing while spaces */
         for (; s < e && isspace(*s); s++);
         for (; s < e && isspace(e[-1]); e--);
         *e = 0;

         /* Free previous value */
         free(value);
         value = strdup(s);
         VERIFY(value);

         cpu++;
      }
      free(line);
   }

   fclose(f);

   return value;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetRatedCpuMhz --
 *
 *      Get the rated CPU speed of a given processor.
 *      Return value is in MHz.
 *
 * Results:
 *      TRUE on success, FALSE on failure
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Hostinfo_GetRatedCpuMhz(int32 cpuNumber,  // IN:
                        uint32 *mHz)      // OUT:
{
#if defined(__APPLE__) || defined(__FreeBSD__)

#  if defined(__APPLE__)
#     define CPUMHZ_SYSCTL_NAME "hw.cpufrequency_max"
#  elif __FreeBSD__version >= 50011
#     define CPUMHZ_SYSCTL_NAME "hw.clockrate"
#  endif

#  if defined(CPUMHZ_SYSCTL_NAME)
   uint32 hz;
   size_t hzSize = sizeof hz;

   /* 'cpuNumber' is ignored: Intel Macs are always perfectly symmetric. */

   if (sysctlbyname(CPUMHZ_SYSCTL_NAME, &hz, &hzSize, NULL, 0) == -1) {
      return FALSE;
   }

   *mHz = hz / 1000000;

   return TRUE;
#  else
   return FALSE;
#  endif
#else
#if defined(VMX86_SERVER)
   if (HostType_OSIsVMK()) {
      uint32 tscKhzEstimate;
      VMK_ReturnStatus status = VMKernel_GetTSCkhzEstimate(&tscKhzEstimate);

      /*
       * The TSC frequency matches the CPU frequency in all modern CPUs.
       * Regardless, the TSC frequency is a much better estimate of
       * reality than failing or returning zero.
       */

      *mHz = tscKhzEstimate / 1000;

      return (status == VMK_OK);
   }
#endif

   {
      float fMhz = 0;
      char *readVal = HostinfoGetCpuInfo(cpuNumber, "cpu MHz");

      if (readVal == NULL) {
         return FALSE;
      }

      if (sscanf(readVal, "%f", &fMhz) == 1) {
         *mHz = (unsigned int)(fMhz + 0.5);
      }

      free(readVal);
   }

   return TRUE;
#endif
}


#if defined(__APPLE__) || defined(__FreeBSD__)
/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoGetSysctlStringAlloc --
 *
 *      Obtains the value of a string-type host sysctl.
 *
 * Results:
 *      On success: Allocated, NUL-terminated string.
 *      On failure: NULL.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static char *
HostinfoGetSysctlStringAlloc(char const *sysctlName) // IN
{
   char *desc;
   size_t descSize;

   if (sysctlbyname(sysctlName, NULL, &descSize, NULL, 0) == -1) {
      return NULL;
   }

   desc = malloc(descSize);
   if (!desc) {
      return NULL;
   }

   if (sysctlbyname(sysctlName, desc, &descSize, NULL, 0) == -1) {
      free(desc);

      return NULL;
   }

   return desc;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetCpuDescription --
 *
 *      Get the descriptive name associated with a given CPU.
 *
 * Results:
 *      On success: Allocated, NUL-terminated string.
 *      On failure: NULL.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_GetCpuDescription(uint32 cpuNumber)  // IN:
{
#if defined(__APPLE__)
   /* 'cpuNumber' is ignored: Intel Macs are always perfectly symmetric. */
   return HostinfoGetSysctlStringAlloc("machdep.cpu.brand_string");
#elif defined(__FreeBSD__)
   return HostinfoGetSysctlStringAlloc("hw.model");
#elif defined VMX86_SERVER
   /* VMKernel treats mName as an in/out parameter so terminate it. */
   char mName[64] = { 0 };

   if (VMKernel_GetCPUModelName(cpuNumber, mName,
                                sizeof(mName)) == VMK_OK) {
      mName[sizeof(mName) - 1] = '\0';

      return strdup(mName);
   }

   return NULL;
#else
   return HostinfoGetCpuInfo(cpuNumber, "model name");
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_Execute --
 *
 *      Start program 'path'.  If 'wait' is TRUE, wait for program
 *	to complete and return exit status.
 *
 * Results:
 *      Exit status of 'path'.
 *
 * Side effects:
 *      Run a separate program.
 *
 *----------------------------------------------------------------------
 */

int
Hostinfo_Execute(const char *path,   // IN:
                 char * const *args, // IN:
                 Bool wait,          // IN:
                 const int *keepFds, // IN/OPT: array of fds to be kept open
                 size_t numKeepFds)  // IN: number of fds in keepFds
{
   int pid;
   int status;

   if (path == NULL) {
      return 1;
   }

   pid = fork();

   if (pid == -1) {
      return -1;
   }

   if (pid == 0) {
      Hostinfo_ResetProcessState(keepFds, numKeepFds);
      Posix_Execvp(path, args);
      exit(127);
   }

   if (wait) {
      for (;;) {
         if (waitpid(pid, &status, 0) == -1) {
            if (errno == ECHILD) {
               return 0;   // This sucks.  We really don't know.
            }
            if (errno != EINTR) {
               return -1;
            }
         } else {
            return status;
         }
      }
   } else {
      return 0;
   }
}


#ifdef __APPLE__
/*
 * How to retrieve kernel zone information. A little bit of history
 * ---
 * 1) In Mac OS versions < 10.6, we could retrieve kernel zone information like
 *    zprint did, i.e. by invoking the host_zone_info() Mach call.
 *
 *    osfmk/mach/mach_host.defs defines both arrays passed to host_zone_info()
 *    as 'out' parameters, but the implementation of the function in
 *    osfmk/kern/zalloc.c clearly treats them as 'inout' parameters. This issue
 *    is confirmed in practice: the input values passed by the user process are
 *    ignored. Now comes the scary part: is the input of the kernel function
 *    deterministically invalid, or is it some non-deterministic garbage (in
 *    which case the kernel could corrupt the user address space)? The answer
 *    is in the Mach IPC code. A cursory kernel debugging session seems to
 *    imply that the input pointer values are garbage, but the input size
 *    values are always 0. So host_zone_info() seems safe to use in practice.
 *
 * 2) In Mac OS 10.6, Apple introduced the 64-bit kernel.
 *
 *    2.1) They modified host_zone_info() to always returns KERN_NOT_SUPPORTED
 *         when the sizes (32-bit or 64-bit) of the user and kernel virtual
 *         address spaces do not match. Was bug 377049.
 *
 *         zprint got away with it by re-executing itself to match the kernel.
 *
 *    2.2) They broke the ABI for 64-bit user processes: the
 *         'zone_info.zi_*_size' fields are 32-bit in the Mac OS 10.5 SDK, and
 *         64-bit in the Mac OS 10.6 SDK. So a 64-bit user process compiled
 *         against the Mac OS 10.5 SDK works with the Mac OS 10.5 (32-bit)
 *         kernel but fails with the Mac OS 10.6 64-bit kernel.
 *
 *         zprint in Mac OS 10.6 is compiled against the Mac OS 10.6 SDK, so it
 *         got away with it.
 *
 *    The above two things made it very impractical for us to keep calling
 *    host_zone_info(). Instead we invoked zprint and parsed its non-localized
 *    output.
 *
 * 3) In Mac OS 10.7, Apple cleaned their mess and solved all the above
 *    problems by introducing a new mach_zone_info() Mach call. So this is what
 *    we use now. Was bug 816610.
 *
 * 4) In Mac OS 10.8, Apple appears to have modified mach_zone_info() to always
 *    return KERN_INVALID_HOST(!) when the calling process (not the calling
 *    thread!) is not root.
 */


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetKernelZoneElemSize --
 *
 *      Retrieve the size of the elements in a named kernel zone.
 *
 * Results:
 *      On success: the size (in bytes) > 0.
 *      On failure: 0.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

size_t
Hostinfo_GetKernelZoneElemSize(char const *name) // IN: Kernel zone name
{
   size_t result = 0;
   mach_zone_name_t *namesPtr;
   mach_msg_type_number_t namesSize;
   mach_zone_info_t *infosPtr;
   mach_msg_type_number_t infosSize;
   kern_return_t kr;
   mach_msg_type_number_t i;

   ASSERT(name);

   kr = mach_zone_info(mach_host_self(), &namesPtr, &namesSize, &infosPtr,
                       &infosSize);
   if (kr != KERN_SUCCESS) {
      Warning("%s: mach_zone_info failed %u.\n", __FUNCTION__, kr);
      return result;
   }

   ASSERT(namesSize == infosSize);
   for (i = 0; i < namesSize; i++) {
      if (!strcmp(namesPtr[i].mzn_name, name)) {
         result = infosPtr[i].mzi_elem_size;
         /* Check that nothing of value was lost during the cast. */
         ASSERT(result == infosPtr[i].mzi_elem_size);
         break;
      }
   }

   ASSERT_ON_COMPILE(sizeof namesPtr <= sizeof (vm_address_t));
   kr = vm_deallocate(mach_task_self(), (vm_address_t)namesPtr,
                      namesSize * sizeof *namesPtr);
   ASSERT(kr == KERN_SUCCESS);

   ASSERT_ON_COMPILE(sizeof infosPtr <= sizeof (vm_address_t));
   kr = vm_deallocate(mach_task_self(), (vm_address_t)infosPtr,
                      infosSize * sizeof *infosPtr);
   ASSERT(kr == KERN_SUCCESS);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetHardwareModel --
 *
 *      Obtains the hardware model identifier (i.e. "MacPro5,1") from the host.
 *
 * Results:
 *      On success: Allocated, NUL-terminated string.
 *      On failure: NULL.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_GetHardwareModel(void)
{
   return HostinfoGetSysctlStringAlloc("hw.model");
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_ProcessIsRosetta --
 *
 *      Checks if the current process is running as a translated binary.
 *
 * Results:
 *      0 for a native process, 1 for a translated process,
 *      and -1 when an error occurs.
 *
 * Side effects:
 *      None
 *----------------------------------------------------------------------
 */

int
Hostinfo_ProcessIsRosetta(void)
{
   int ret = 0;
   size_t size = sizeof ret;

   if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
      return errno == ENOENT ? 0 : -1;
   }
   return ret;
}
#endif /* __APPLE__ */


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_SystemUpTime --
 *
 *      Return system uptime in microseconds.
 *
 *      Please note that the actual resolution of this "clock" is undefined -
 *      it varies between OSen and OS versions. Use Hostinfo_SystemTimerUS
 *      whenever possible.
 *
 * Results:
 *      System uptime in microseconds or zero in case of a failure.
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

VmTimeType
Hostinfo_SystemUpTime(void)
{
#if defined(__APPLE__)
   return Hostinfo_SystemTimerUS();
#elif defined(__linux__)
   int res;
   double uptime;
   int fd;
   char buf[256];

   static Atomic_Int fdStorage = { -1 };
   static Atomic_uint32 logFailedPread = { 1 };

   /*
    * /proc/uptime does not exist on Visor.  Use syscall instead.
    * Discovering Visor is a run-time check with a compile-time hint.
    */
   if (vmx86_server && HostType_OSIsVMK()) {
      uint64 uptime;
#ifdef VMX86_SERVER
      if (UNLIKELY(VMKernel_GetUptimeUS(&uptime) != VMK_OK)) {
         Log("%s: failure!\n", __FUNCTION__);

         uptime = 0;  // A timer read failure - this is really bad!
      }
#endif
      return uptime;
   }

   fd = Atomic_ReadInt(&fdStorage);

   /* Do we need to open the file the first time through? */
   if (UNLIKELY(fd == -1)) {
      fd = open("/proc/uptime", O_RDONLY);

      if (fd == -1) {
         Warning(LGPFX" Failed to open /proc/uptime: %s\n",
                 Err_Errno2String(errno));

         return 0;
      }

      /* Try to swap ours in. If we lose the race, close our fd */
      if (Atomic_ReadIfEqualWriteInt(&fdStorage, -1, fd) != -1) {
         close(fd);
      }

      /* Get the winning fd - either ours or theirs, doesn't matter anymore */
      fd = Atomic_ReadInt(&fdStorage);
   }

   ASSERT(fd != -1);

   res = pread(fd, buf, sizeof buf - 1, 0);
   if (res == -1) {
      /*
       * In case some kernel broke pread (like 2.6.28-rc1), have a fall-back
       * instead of spewing the log.  This should be rare.  Using a lock
       * around lseek and read does not work here as it will deadlock with
       * allocTrack/fileTrack enabled.
       */

      if (Atomic_ReadIfEqualWrite(&logFailedPread, 1, 0) == 1) {
         Warning(LGPFX" Failed to pread /proc/uptime: %s\n",
                 Err_Errno2String(errno));
      }
      fd = open("/proc/uptime", O_RDONLY);
      if (fd == -1) {
         Warning(LGPFX" Failed to retry open /proc/uptime: %s\n",
                 Err_Errno2String(errno));

         return 0;
      }
      res = read(fd, buf, sizeof buf - 1);
      close(fd);
      if (res == -1) {
         Warning(LGPFX" Failed to read /proc/uptime: %s\n",
                 Err_Errno2String(errno));

         return 0;
      }
   }
   ASSERT(res < sizeof buf);
   buf[res] = '\0';

   if (sscanf(buf, "%lf", &uptime) != 1) {
      Warning(LGPFX" Failed to parse /proc/uptime\n");

      return 0;
   }

   return uptime * 1000 * 1000;
#else
NOT_IMPLEMENTED();
#endif
}


#if !defined(__APPLE__)
/*
 *----------------------------------------------------------------------
 *
 * HostinfoFindEntry --
 *
 *      Search a buffer for a pair `STRING <blanks> DIGITS'
 *	and return the number DIGITS, or 0 when fail.
 *
 * Results:
 *      TRUE on  success, FALSE on failure
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static Bool
HostinfoFindEntry(char *buffer,         // IN: Buffer
                  const char *string,   // IN: String sought
                  unsigned int *value)  // OUT: Value
{
   char *p = strstr(buffer, string);
   unsigned int val;

   if (p == NULL) {
      return FALSE;
   }

   p += strlen(string);

   while (*p == ' ' || *p == '\t') {
      p++;
   }
   if (*p < '0' || *p > '9') {
      return FALSE;
   }

   val = strtoul(p, NULL, 10);
   if ((errno == ERANGE) || (errno == EINVAL)) {
      return FALSE;
   }

   *value = val;

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * HostinfoGetMemInfo --
 *
 *      Get some attribute from /proc/meminfo
 *      Return value is in KB.
 *
 * Results:
 *      TRUE on success, FALSE on failure
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
HostinfoGetMemInfo(const char *name,    // IN:
                   unsigned int *value) // OUT:
{
   size_t len;
   char   buffer[4096];

   int fd = Posix_Open("/proc/meminfo", O_RDONLY);

   if (fd == -1) {
      Warning(LGPFX" %s: Unable to open /proc/meminfo\n", __FUNCTION__);

      return FALSE;
   }

   len = read(fd, buffer, sizeof buffer - 1);
   close(fd);

   if (len == -1) {
      return FALSE;
   }

   buffer[len] = '\0';

   return HostinfoFindEntry(buffer, name, value);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoSysinfo --
 *
 *      Retrieve system information on a Linux system.
 *
 * Results:
 *      TRUE on success: '*totalRam', '*freeRam', '*totalSwap' and '*freeSwap'
 *                       are set if not NULL
 *      FALSE on failure
 *
 * Side effects:
 *      None.
 *
 *      This seems to be a very expensive call: like 5ms on 1GHz P3 running
 *      RH6.1 Linux 2.2.12-20.  Yes, that's 5 milliseconds.  So caller should
 *      take care.  -- edward
 *
 *-----------------------------------------------------------------------------
 */

static Bool
HostinfoSysinfo(uint64 *totalRam,  // OUT: Total RAM in bytes
                uint64 *freeRam,   // OUT: Free RAM in bytes
                uint64 *totalSwap, // OUT: Total swap in bytes
                uint64 *freeSwap)  // OUT: Free swap in bytes
{
#ifdef HAVE_SYSINFO
   // Found in linux/include/kernel.h for a 2.5.6 kernel --hpreg
   struct vmware_sysinfo {
	   long uptime;			/* Seconds since boot */
	   unsigned long loads[3];	/* 1, 5, and 15 minute load averages */
	   unsigned long totalram;	/* Total usable main memory size */
	   unsigned long freeram;	/* Available memory size */
	   unsigned long sharedram;	/* Amount of shared memory */
	   unsigned long bufferram;	/* Memory used by buffers */
	   unsigned long totalswap;	/* Total swap space size */
	   unsigned long freeswap;	/* swap space still available */
	   unsigned short procs;	/* Number of current processes */
	   unsigned short pad;		/* explicit padding for m68k */
	   unsigned long totalhigh;	/* Total high memory size */
	   unsigned long freehigh;	/* Available high memory size */
	   unsigned int mem_unit;	/* Memory unit size in bytes */
	   // Padding: libc5 uses this..
	   char _f[20 - 2 * sizeof(long) - sizeof(int)];
   };
   struct vmware_sysinfo si;

   if (sysinfo((struct sysinfo *)&si) < 0) {
      return FALSE;
   }

   if (si.mem_unit == 0) {
      /*
       * Kernel versions < 2.3.23. Those kernels used a smaller sysinfo
       * structure, whose last meaningful field is 'procs' --hpreg
       */

      si.mem_unit = 1;
   }

   if (totalRam) {
      *totalRam = (uint64)si.totalram * si.mem_unit;
   }
   if (freeRam) {
      *freeRam = (uint64)si.freeram * si.mem_unit;
   }
   if (totalSwap) {
      *totalSwap = (uint64)si.totalswap * si.mem_unit;
   }
   if (freeSwap) {
      *freeSwap = (uint64)si.freeswap * si.mem_unit;
   }

   return TRUE;
#else // ifdef HAVE_SYSINFO
   NOT_IMPLEMENTED();
#endif // ifdef HAVE_SYSINFO
}
#endif // ifndef __APPLE__


#if defined(__linux__) || defined(__FreeBSD__) || defined(sun)
/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoGetLinuxMemoryInfoInPages --
 *
 *      Obtain the minimum memory to be maintained, total memory available,
 *      and free memory available on the host (Linux) in pages.
 *
 * Results:
 *      TRUE on success: '*minSize', '*maxSize' and '*currentSize' are set
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
HostinfoGetLinuxMemoryInfoInPages(unsigned int *minSize,      // OUT:
                                  unsigned int *maxSize,      // OUT:
                                  unsigned int *currentSize)  // OUT:
{
   uint64 total;
   uint64 free;
   unsigned int cached = 0;

   /*
    * Note that the free memory provided by linux does not include buffer and
    * cache memory. Linux tries to use the free memory to cache file. Most of
    * those memory can be freed immediately when free memory is low,
    * so for our purposes it should be counted as part of the free memory .
    * There is no good way to collect the useable free memory in 2.2 and 2.4
    * kernel.
    *
    * Here is our solution: The free memory we report includes cached memory.
    * Mmapped memory is reported as cached. The guest RAM memory, which is
    * mmaped to a ram file, therefore make up part of the cached memory. We
    * exclude the size of the guest RAM from the amount of free memory that we
    * report here. Since we don't know about the RAM size of other VMs, we
    * leave that to be done in serverd/MUI.
    */

   if (HostinfoSysinfo(&total, &free, NULL, NULL) == FALSE) {
      return FALSE;
   }

   /*
    * Convert to pages and round up total memory to the nearest multiple of 8
    * or 32 MB, since the "total" amount of memory reported by Linux is the
    * total physical memory - amount used by the kernel.
    */

   if (total < (uint64)128 * 1024 * 1024) {
      total = ROUNDUP(total, (uint64)8 * 1024 * 1024);
   } else {
      total = ROUNDUP(total, (uint64)32 * 1024 * 1024);
   }

   *minSize = 128; // XXX - Figure out this value
   *maxSize = total / PAGE_SIZE;

   HostinfoGetMemInfo("Cached:", &cached);
   if (currentSize) {
      *currentSize = free / PAGE_SIZE + cached / (PAGE_SIZE / 1024);
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HostinfoGetSwapInfoInPages --
 *
 *      Obtain the total swap and free swap on the host (Linux) in
 *      pages.
 *
 * Results:
 *      TRUE on success: '*totalSwap' and '*freeSwap' are set if not NULL
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Hostinfo_GetSwapInfoInPages(unsigned int *totalSwap,  // OUT:
                            unsigned int *freeSwap)   // OUT:
{
   uint64 total;
   uint64 free;

   if (HostinfoSysinfo(NULL, NULL, &total, &free) == FALSE) {
      return FALSE;
   }

   if (totalSwap != NULL) {
      *totalSwap = total / PAGE_SIZE;
   }

   if (freeSwap != NULL) {
      *freeSwap = free / PAGE_SIZE;
   }

   return TRUE;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetMemoryInfoInPages --
 *
 *      Obtain the minimum memory to be maintained, total memory available,
 *      and free memory available on the host in pages.
 *
 * Results:
 *      TRUE on success: '*minSize', '*maxSize' and '*currentSize' are set
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Hostinfo_GetMemoryInfoInPages(unsigned int *minSize,      // OUT:
                              unsigned int *maxSize,      // OUT:
                              unsigned int *currentSize)  // OUT:
{
#if defined(__APPLE__)
   mach_msg_type_number_t count;
   vm_statistics_data_t stat;
   kern_return_t error;
   uint64_t memsize;
   size_t memsizeSize = sizeof memsize;

   /*
    * Largely inspired by
    * darwinsource-10.4.5/top-15/libtop.c::libtop_p_vm_sample().
    */

   count = HOST_VM_INFO_COUNT;
   error = host_statistics(mach_host_self(), HOST_VM_INFO,
                           (host_info_t) &stat, &count);

   if (error != KERN_SUCCESS || count != HOST_VM_INFO_COUNT) {
      Warning("%s: Unable to retrieve host vm stats.\n", __FUNCTION__);

      return FALSE;
   }

   // XXX Figure out this value.
   *minSize = 128;

   /*
    * XXX Hopefully this includes cached memory as well. We should check.
    * No. It returns only completely used pages.
    */

   *currentSize = stat.free_count;

   /*
    * Adding up the stat values does not sum to 100% of physical memory.
    * The correct value is available from sysctl so we do that instead.
    */

   if (sysctlbyname("hw.memsize", &memsize, &memsizeSize, NULL, 0) == -1) {
      Warning("%s: Unable to retrieve host vm hw.memsize.\n", __FUNCTION__);

      return FALSE;
   }

   *maxSize = memsize / PAGE_SIZE;
   return TRUE;
#elif defined(VMX86_SERVER)
   uint64 total;
   uint64 free;
   VMK_ReturnStatus status;

   if (VmkSyscall_Init(FALSE, NULL, 0)) {
      status = VMKernel_GetMemSize(&total, &free);
      if (status == VMK_OK) {
         *minSize = 128;
         *maxSize = total / PAGE_SIZE;
         *currentSize = free / PAGE_SIZE;

         return TRUE;
      }
   }

   return FALSE;
#else
   return HostinfoGetLinuxMemoryInfoInPages(minSize, maxSize, currentSize);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_GetModulePath --
 *
 *	Retrieve the full path to the executable. Not supported under VMvisor.
 *
 *      The value can be controlled by the invoking user, so the calling code
 *      should perform extra checks if it is going to use the value to
 *      open/exec content in a security-sensitive context.
 *
 * Results:
 *      On success: The allocated, NUL-terminated file path.
 *         Note: This path can be a symbolic or hard link; it's just one
 *         possible path to access the executable.
 *
 *      On failure: NULL.
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_GetModulePath(uint32 priv)  // IN:
{
   char *path;

#if defined(__APPLE__)
   uint32_t pathSize = FILE_MAXPATH;
#else
   uid_t uid = -1;
#endif

   if ((priv != HGMP_PRIVILEGE) && (priv != HGMP_NO_PRIVILEGE)) {
      Warning("%s: invalid privilege parameter\n", __FUNCTION__);

      return NULL;
   }

#if defined(__APPLE__)
   path = Util_SafeMalloc(pathSize);
   if (_NSGetExecutablePath(path, &pathSize)) {
      Warning(LGPFX" %s: _NSGetExecutablePath failed.\n", __FUNCTION__);
      free(path);

      return NULL;
   }

#else
#if defined(VMX86_SERVER)
   if (HostType_OSIsVMK()) {
      return NULL;
   }
#endif

   // "/proc/self/exe" only exists on Linux 2.2+.
   ASSERT(Hostinfo_OSVersion(0) > 2 ||
          (Hostinfo_OSVersion(0) == 2 && Hostinfo_OSVersion(1) >= 2));

   if (priv == HGMP_PRIVILEGE) {
      uid = Id_BeginSuperUser();
   }

   path = Posix_ReadLink("/proc/self/exe");

   if (priv == HGMP_PRIVILEGE) {
      Id_EndSuperUser(uid);
   }

   if (path == NULL) {
      Warning(LGPFX" %s: readlink failed: %s\n", __FUNCTION__,
              Err_Errno2String(errno));
   }
#endif

   return path;
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_GetLibraryPath --
 *
 *      Try and deduce the path to the library where the specified
 *      address resides. Expected usage is that the caller will pass
 *      in the address of one of the caller's own functions.
 *
 *      Not implemented on iOS (iOS does not support dynamic loading).
 *      Not implemented on FreeBSD (doesn't support fs /proc/self)
 *      Not fully implemented on ESX (the path MAY OR MAY NOT BE ABSOLUTE).
 *
 * Results:
 *      The absolute path or NULL on failure.
 *
 * Side effects:
 *      Memory is allocated.
 *
 *----------------------------------------------------------------------
 */

char *
Hostinfo_GetLibraryPath(void *addr)  // IN
{
   char *path = NULL;

   /*
    * Try fast path first.
    *
    * Does NOT work for iOS and FreeBSD, as iOS does not support dynamic loading
    * and FreeBSD (non-linux) doesn't support the file system /proc/self/ .
    */
#if !TARGET_OS_IPHONE && !defined(__FreeBSD__)
   Dl_info info;

   if (dladdr(addr, &info)) {
      if (vmx86_server ||
          *info.dli_fname == DIRSEPC) { // We have an absolute path.
         return Unicode_Alloc(info.dli_fname, STRING_ENCODING_DEFAULT);
      }
   }
#endif // !TARGET_OS_IPHONE && !defined(__FreeBSD__)

   /*
    * Slow path for ESX, Linux, Android and macOS.
    */
#if defined(VMX86_SERVER)
   {
      // Slow path not needed on ESX by any caller.
   }
#elif defined(__linux__) || defined(__ANDROID__)
   {
      DIR *dir;

      /*
       * /proc/pid/map_files/ (since Linux 3.3)
       *         This subdirectory contains entries corresponding to
       *         memory-mapped files (see mmap(2)).  Entries are named by
       *         memory region start and end address pair (expressed as
       *         hexadecimal numbers), and are symbolic links to the mapped
       *         files themselves.
       *
       *             # ls -l /proc/self/map_files/
       *             lr--------. 1 root root 64 Apr 16 21:31
       *                         3252e00000-3252e20000 -> /usr/lib64/ld-2.15.so
       */
      dir = Posix_OpenDir("/proc/self/map_files");
      if (dir == NULL) {
         return NULL;
      }

      for (;;) {
         struct dirent *entry;
         char *sep;
         char *end;
         uintptr_t startAddr;
         uintptr_t endAddr;

         errno = 0;
         entry = readdir(dir);
         if (entry == NULL) {
            ASSERT(errno == 0);
            break;
         }

         if (entry->d_type != DT_LNK) { // procfs supports `d_type`.
            continue;
         }

         sep = strchr(entry->d_name, '-');
         if (sep == NULL) {
            continue; // The file name does NOT in `1234abcd-abcd1234` format
         }

         errno = 0;
         endAddr = (uintptr_t) strtoll(sep + 1, &end, 16);
         if (*end != '\0' || errno != 0) {
            continue; // The address is NOT hexadecimal numbers.
         }

         if (endAddr < (uintptr_t) addr) {
            continue; // `addr` is NOT in range.
         }

         *sep = '\0'; // Terminate the start address part of the file name.
         errno = 0;
         startAddr = (uintptr_t) strtoll(entry->d_name, &end, 16);
         if (*end != '\0' || errno != 0) {
            continue; // The address is NOT hexadecimal numbers.
         }
         *sep = '-'; // Restore to the original file name.

         ASSERT((uintptr_t) addr <= endAddr);
         if (startAddr <= (uintptr_t) addr) {
            char targetBuf[PAGE_SIZE];
            ssize_t targetLen;

            /*
             * readlinkat() does not append a terminating null byte to buf.
             * It will (silently) truncate the contents in case the buffer
             * is too small to hold all the contents.
             */
            targetLen = readlinkat(dirfd(dir), entry->d_name,
                                   targetBuf, sizeof targetBuf);
            if (targetLen == -1 ||
                targetLen == sizeof targetBuf) { // truncation may have occurred
               break;
            }

            targetBuf[targetLen] = '\0';
            ASSERT(targetBuf[0] == DIRSEPC); // Ensure we have absolute path.

            path = Unicode_Alloc(targetBuf, STRING_ENCODING_DEFAULT);
            break;
         }
      } // for each entry in "/proc/self/map_files"

      closedir(dir);
   }
#elif defined(__APPLE__) && !TARGET_OS_IPHONE
   {
      char pathBuf[MAXPATHLEN];
      int pathLen;
      pid_t pid;

      pid = getpid();
      errno = 0;
      /*
       * I cannot find a document for proc_regionfilename().
       * The only information I have is its source code:
       *    https://opensource.apple.com/source/Libc/Libc-825.40.1/darwin/
       *    libproc.c.auto.html
       *
       * Parameters and return value:
       *    pid:          The process ID of the `address` belongs to.
       *    address:      The address you want to search.
       *    buffer:       A buffer to receive the file path.
       *    buffersize:   The size of the `buffer`, at least `MAXPATHLEN`.
       *    return value: The length of the path in `buffer`, or 0 on error.
       *
       * proc_regionfilename() does not append a terminating NUL byte to buffer.
       * It will silently truncate the contents in case the buffer is too small
       * to hold all the contents.
       */
      pathLen = proc_regionfilename(pid,
                                    (uintptr_t) addr,
                                    pathBuf,
                                    sizeof pathBuf);
      if (pathLen == 0 ||
          pathLen == sizeof pathBuf) { // truncation may have occurred
         goto out;
      }

      ASSERT(errno == 0);
      pathBuf[pathLen] = '\0';

      path = Unicode_Alloc(pathBuf, STRING_ENCODING_DEFAULT);
   out:
      ; // A noop is needed at here to make the compiler happy.
   }
#endif

   return path;
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_AcquireProcessSnapshot --
 *
 *      Acquire a snapshot of the process table. On POSIXen, this is
 *      a NOP.
 *
 * Results:
 *      !NULL - A process snapshot pointer.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

struct HostinfoProcessSnapshot {
   int dummy;
};

static HostinfoProcessSnapshot hostinfoProcessSnapshot = { 0 };

HostinfoProcessSnapshot *
Hostinfo_AcquireProcessSnapshot(void)
{
   return &hostinfoProcessSnapshot;
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_ReleaseProcessSnapshot --
 *
 *      Release a snapshot of the process table. On POSIXen, this is
 *      a NOP.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

void
Hostinfo_ReleaseProcessSnapshot(HostinfoProcessSnapshot *s)  // IN/OPT:
{
   if (s != NULL) {
      VERIFY(s == &hostinfoProcessSnapshot);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_QueryProcessExistence --
 *
 *      Determine if a PID is "alive" or "dead". Failing to be able to
 *      do this perfectly, do not make any assumption - say the answer
 *      is unknown.
 *
 * Results:
 *      HOSTINFO_PROCESS_QUERY_ALIVE    Process is alive
 *      HOSTINFO_PROCESS_QUERY_DEAD     Process is dead
 *      HOSTINFO_PROCESS_QUERY_UNKNOWN  Don't know
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

HostinfoProcessQuery
Hostinfo_QueryProcessExistence(int pid)  // IN:
{
   HostinfoProcessQuery result;

   switch ((kill(pid, 0) == -1) ? errno : 0) {
   case 0:
   case EPERM:
      result = HOSTINFO_PROCESS_QUERY_ALIVE;
      break;

   case ESRCH:
      result = HOSTINFO_PROCESS_QUERY_DEAD;
      break;

   default:
      result = HOSTINFO_PROCESS_QUERY_UNKNOWN;
      break;
   }

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_QueryProcessReaped --
 *
 *      Determine if the resources of a "dead" process have been reclaimed.
 *      On Linux, this is equivalent to querying the process's existence.
 *      On ESX, we can query the vmkernel.
 *
 * Results:
 *      HOSTINFO_PROCESS_QUERY_ALIVE    Process is not yet reaped
 *      HOSTINFO_PROCESS_QUERY_DEAD     Process has been reaped
 *      HOSTINFO_PROCESS_QUERY_UNKNOWN  Don't know
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

HostinfoProcessQuery
Hostinfo_QueryProcessReaped(int pid)  // IN:
{
#if defined(VMX86_SERVER) || defined(USERWORLD)  // ESXi
   VMK_ReturnStatus status = VMKPrivate_WaitForWorldDeath(pid, 1);
   HostinfoProcessQuery result;

   switch (status) {
   case VMK_TIMEOUT:
      result = HOSTINFO_PROCESS_QUERY_ALIVE;
      break;

   /*
    * VMK_BAD_PARAM indicates the pid is no longer associated with
    * a userworld.
    *
    * VMK_OK indicates the pid has been reaped.
    */
   case VMK_BAD_PARAM:
   case VMK_OK:
      result = HOSTINFO_PROCESS_QUERY_DEAD;
      break;

   /* VMK_DEATH_PENDING (on caller), VMK_WAIT_INTERRUPTED */
   default:
      result = HOSTINFO_PROCESS_QUERY_UNKNOWN;
      break;
   }

   return result;
#else
   return Hostinfo_QueryProcessExistence(pid);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_QueryProcessSnapshot --
 *
 *      Determine if a PID is "alive" or "dead" within the specified
 *      process snapshot. Failing to be able to do this perfectly,
 *      do not make any assumption - say the answer is unknown.
 *
 * Results:
 *      HOSTINFO_PROCESS_QUERY_ALIVE    Process is alive
 *      HOSTINFO_PROCESS_QUERY_DEAD     Process is dead
 *      HOSTINFO_PROCESS_QUERY_UNKNOWN  Don't know
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

HostinfoProcessQuery
Hostinfo_QueryProcessSnapshot(HostinfoProcessSnapshot *s,  // IN:
                              int pid)                     // IN:
{
   ASSERT(s != NULL);

   return Hostinfo_QueryProcessExistence(pid);
}
 07070100000192000081A400000000000000000000000168225505000017B9000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/hostname.c    /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * hostname.c --
 *
 *   Get the host name.
 */

#if defined(_WIN32)

#include <windows.h>
#include <winsock.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "vmware.h"
#include "str.h"
#include "log.h"
#include "hostinfo.h"
#if defined(_WIN32)	// Windows
#include "windowsu.h"
#endif
#include "unicode.h"

#if defined(_WIN32)	// Windows
/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_HostName --
 *
 *      Return the fully qualified host name of the host.
 *
 * Results:
 *      The host name on success; must be freed
 *      NULL if unable to determine the name
 *
 * Side effects:
 *      A host name resolution can occur
 *
 *----------------------------------------------------------------------
 */

char *
Hostinfo_HostName(void)
{
   char *result;
   HMODULE dllHandle;
   struct hostent *myHostEnt;
   struct hostent *(WINAPI *GetHostByNameFn)(char *hostName);
   int            (WINAPI *GetHostNameFn)(char *hostName, int size);

   char hostName[1024] = { '\0' };

   result = Win32U_GetComputerNameEx(ComputerNamePhysicalDnsFullyQualified);

   if (result != NULL) {
      return result;
   }

   Warning("%s GetComputerNameEx failed: %d\n", __FUNCTION__, GetLastError());

   dllHandle = LoadLibraryA("ws2_32");

   if (!dllHandle) {
      Warning("%s Failed to load ws2_32, will try wsock32.\n", __FUNCTION__);

      dllHandle = LoadLibraryA("wsock32");

      if (!dllHandle) {
         Warning("%s Failed to wsock32.\n", __FUNCTION__);

         return NULL;
      }
   }

   GetHostNameFn = (void *) GetProcAddress(dllHandle, "gethostname");

   if (!GetHostNameFn) {
      Warning("%s Failed to find gethostname.\n", __FUNCTION__);
      FreeLibrary(dllHandle);

      return NULL;
   }

   if ((*GetHostNameFn)(hostName, sizeof hostName) == SOCKET_ERROR) {
      Warning("%s gethostname failed.\n", __FUNCTION__);
      FreeLibrary(dllHandle);

      return NULL;
   }

   GetHostByNameFn = (void *) GetProcAddress(dllHandle, "gethostbyname");

   if (!GetHostByNameFn) {
      Warning("%s Failed to find gethostbyname.\n", __FUNCTION__);
      FreeLibrary(dllHandle);

      return Unicode_Alloc(hostName, STRING_ENCODING_DEFAULT);
   }

   myHostEnt = (*GetHostByNameFn)(hostName);

   FreeLibrary(dllHandle);

   if (myHostEnt == (struct hostent *) NULL) {
      Warning("%s gethostbyname failed.\n", __FUNCTION__);
   } else {
      Str_Strcpy(hostName, myHostEnt->h_name, sizeof hostName);
   }

   return Unicode_Alloc(hostName, STRING_ENCODING_DEFAULT);
}
#elif defined(__APPLE__)	// MacOS X
#define SYS_NMLN _SYS_NAMELEN
#include <mach-o/dyld.h>
#include <mach/host_info.h>
#include <mach/mach_host.h>
#include <mach/mach_init.h>
#include <unistd.h>
#include <sys/utsname.h>

/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_HostName --
 *
 *      Return the fully qualified host name of the host.
 *
 * Results:
 *      The host name on success; must be freed
 *      NULL on failure
 *
 * Side effects:
 *      A host name resolution can occur.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_HostName(void)
{
   struct utsname un;

   char *result = NULL;

   if ((uname(&un) == 0) && (*un.nodename != '\0')) {
      /* 'un.nodename' is already fully qualified. */
      if (Unicode_IsStringValidUTF8(un.nodename)) {  // ASCII is OK
         result = Unicode_Duplicate(un.nodename);
      }
   }

   return result;
}
#elif defined(__linux__)
#include <unistd.h>
#include <sys/utsname.h>
#include <netdb.h>

/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_HostName --
 *
 *      Return the fully qualified host name of the host.
 *
 * Results:
 *      The host name on success; must be freed
 *      NULL on failure
 *
 * Side effects:
 *      A host name resolution can occur.
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_HostName(void)
{
   struct utsname un;

   char *result = NULL;

   if ((uname(&un) == 0) && (*un.nodename != '\0')) {
      char *p;
      int error;
      struct hostent he;
      char buffer[1024];

      struct hostent *phe = &he;

      /*
       * Fully qualify 'un.nodename'. If the name cannot be fully
       * qualified, use whatever unqualified name is available or bug
       * 139607 will occur.
       */

      p = un.nodename;

      if ((gethostbyname_r(p, &he, buffer, sizeof buffer, &phe,
                           &error) == 0) && (phe != NULL)) {
         p = phe->h_name;
      }

      if (Unicode_IsStringValidUTF8(p)) {  // ASCII is OK
         result = Unicode_Duplicate(p);
      }
   }

   return result;
}
#else			// Not any specifically coded OS
/*
 *-----------------------------------------------------------------------------
 *
 * Hostinfo_HostName --
 *
 *      Stub for uncoded OSen
 *
 * Results:
 *      NULL
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Hostinfo_HostName(void)
{
   return NULL;
}
#endif
   07070100000193000081A400000000000000000000000168225505000071A4000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/idLinux.c /*********************************************************
 * Copyright (C) 1998-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * idLinux.c --
 *
 *   uid/gid helpers.
 */

#include <errno.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <string.h>
#include <unistd.h>
#ifdef __linux__
#if defined(__GLIBC__) && \
           (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16))
#include <sys/auxv.h>
#endif
#endif
#ifdef __APPLE__
#include <sys/socket.h>
#include <TargetConditionals.h>
#if !defined TARGET_OS_IPHONE
#define TARGET_OS_IPHONE 0
#endif
#if !TARGET_OS_IPHONE
#include <Security/Authorization.h>
#include <Security/AuthorizationTags.h>
#endif
#endif
#if defined __ANDROID__
#include <bits/glibc-syscalls.h>
#endif

#include "vmware.h"
#include "su.h"
#include "vm_atomic.h"

#if defined(__linux__)
/*
 * 64bit linux has no 16 bit versions and
 * the 32bit versions have the un-suffixed names.
 * And obviously, we're not using glibc 2.0
 * for our 64bit builds!
 */
#ifdef VM_64BIT
#define SYS_setreuid32         (abort(), 0)
#define SYS_setregid32         (abort(), 0)
#define SYS_setresuid32        (abort(), 0)
#define SYS_getresuid32        (abort(), 0)
#define SYS_setresgid32        (abort(), 0)
#define SYS_getresgid32        (abort(), 0)
#define SYS_setuid32           (abort(), 0)
#define SYS_setgid32           (abort(), 0)
#endif

/*
 * On 32bit systems:
 * Set to 1 when system supports 32bit uids, cleared to 0 when system
 * supports 16bit calls only.  By default we assume that 32bit uids
 * are supported and clear this flag on first failure.
 *
 * On 64bit systems:
 * Only the 32bit uid syscalls exist, but they do not have the names
 * with the '32' suffix, so we get the behaviour we want by forcing
 * the code to use the unsuffixed syscalls.
 */
#ifdef VM_64BIT
static int uid32 = 0;
#else
static int uid32 = 1;
#endif
#endif // __linux__

#if defined(__APPLE__) && !TARGET_OS_IPHONE
#include <sys/kauth.h>

static AuthorizationRef IdAuthCreate(void);
static AuthorizationRef IdAuthCreateWithFork(void);

#endif


#if !defined(__APPLE__)
/*
 *----------------------------------------------------------------------------
 *
 * Id_SetUid --
 *
 *	If calling thread has euid = 0, it sets real, effective and saved uid
 *	to the specified value.
 *	If calling thread has euid != 0, then only effective uid is set.
 *
 * Results:
 *      0 on success, -1 on failure, errno set
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

int
Id_SetUid(uid_t euid)		// IN: new euid
{
#if defined(__FreeBSD__) || defined(sun)
   return setuid(euid);
#elif defined(__linux__) || defined __ANDROID__
   if (uid32) {
      int r = syscall(SYS_setuid32, euid);
      if (r != -1 || errno != ENOSYS) {
         return r;
      }
      uid32 = 0;
   }
   return syscall(SYS_setuid, euid);
#else
#   error "Id_SetUid is not implemented for this platform"
#endif
}
#endif


/*
 *----------------------------------------------------------------------------
 *
 * Id_SetGid --
 *
 *      If calling thread has euid = 0, it sets real, effective and saved gid
 *	to the specified value.
 *	If calling thread has euid != 0, then only effective gid is set.
 *
 * Results:
 *      0 on success, -1 on failure, errno set
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

int
Id_SetGid(gid_t egid)		// IN: new egid
{
#if defined(__APPLE__)
   Warning("XXXMACOS: implement %s\n", __func__);

   return -1;
#elif defined(sun) || defined(__FreeBSD__)
   return setgid(egid);
#else
   if (uid32) {
      int r = syscall(SYS_setgid32, egid);

      if (r != -1 || errno != ENOSYS) {
         return r;
      }
      uid32 = 0;
   }

   return syscall(SYS_setgid, egid);
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * Id_SetRESUid --
 *
 *      Sets uid, euid and saved uid to the specified values.  You can use -1
 *	for values which should not change.
 *
 * Results:
 *      0 on success, -1 on failure, errno set
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

int
Id_SetRESUid(uid_t uid,		// IN: new uid
	     uid_t euid,	// IN: new effective uid
	     uid_t suid)	// IN: new saved uid
{
#if defined(__FreeBSD__)
   return setresuid(uid, euid, suid);
#elif defined(__linux__)
   if (uid32) {
      int r = syscall(SYS_setresuid32, uid, euid, suid);

      if (r != -1 || errno != ENOSYS) {
         return r;
      }
      uid32 = 0;
   }

   return syscall(SYS_setresuid, uid, euid, suid);
#else
   Warning("XXX: implement %s\n", __func__);

   return -1;
#endif
}


#if defined(__linux__)
/*
 *----------------------------------------------------------------------------
 *
 * IdGetRESUid --
 *
 *      Gets uid, effective uid and saved uid.
 *
 * Results:
 *      0 on success, -1 on failure.
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

static int
IdGetRESUid(uid_t *uid,   // OUT:
            uid_t *euid,  // OUT:
            uid_t *suid)  // OUT:
{
   if (uid32) {
      int r = syscall(SYS_getresuid32, uid, euid, suid);

      if (r != -1 || errno != ENOSYS) {
         return r;
      }
      uid32 = 0;
   }

   return syscall(SYS_getresuid, uid, euid, suid);
}
#endif


#if !defined(__APPLE__)
/*
 *----------------------------------------------------------------------------
 *
 * Id_SetRESGid --
 *
 *      Sets gid, egid and saved gid to the specified values.  You can use -1
 *      for values which should not change.
 *
 * Results:
 *      0 on success, -1 on failure, errno set
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

int
Id_SetRESGid(gid_t gid,		// IN: new gid
	     gid_t egid,	// IN: new effective gid
	     gid_t sgid)	// IN: new saved gid
{
#if defined(__FreeBSD__)
   return setresgid(gid, egid, sgid);
#elif defined(__linux__)
   if (uid32) {
      int r = syscall(SYS_setresgid32, gid, egid, sgid);

      if (r != -1 || errno != ENOSYS) {
         return r;
      }
      uid32 = 0;
   }

   return syscall(SYS_setresgid, gid, egid, sgid);
#else
   Warning("XXX: implement %s\n", __func__);

   return -1;
#endif
}
#endif


#if defined(__linux__)
/*
 *----------------------------------------------------------------------------
 *
 * IdGetRESGid --
 *
 *      Gets gid, effective gid and saved gid.
 *
 * Results:
 *      0 on success, -1 on failure.
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

static int
IdGetRESGid(gid_t *gid,   // OUT:
            gid_t *egid,  // OUT:
            gid_t *sgid)  // OUT:
{
   if (uid32) {
      int r = syscall(SYS_getresgid32, gid, egid, sgid);

      if (r != -1 || errno != ENOSYS) {
         return r;
      }
      uid32 = 0;
   }

   return syscall(SYS_getresgid, gid, egid, sgid);
}
#endif


/*
 *----------------------------------------------------------------------------
 *
 * Id_SetREUid --
 *
 *      Sets uid and euid to the specified values.  You can use -1
 *      for values which should not change.  If you are changing uid,
 *      or if you are changing euid to value which differs from old uid,
 *      then saved uid is updated to new euid value.
 *
 * Results:
 *      0 on success, -1 on failure, errno set
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

int
Id_SetREUid(uid_t uid,		// IN: new uid
	    uid_t euid)		// IN: new effective uid
{
#if defined(__APPLE__)
   Warning("XXXMACOS: implement %s\n", __func__);
   return -1;
#elif defined(sun) || defined(__FreeBSD__)
   return setreuid(uid, euid);
#else
   if (uid32) {
      int r = syscall(SYS_setreuid32, uid, euid);

      if (r != -1 || errno != ENOSYS) {
         return r;
      }
      uid32 = 0;
   }

   return syscall(SYS_setreuid, uid, euid);
#endif
}


#if !defined(__APPLE__)
/*
 *----------------------------------------------------------------------------
 *
 * Id_SetREGid --
 *
 *      Sets gid and egid to the specified values.  You can use -1
 *      for values which should not change.  If you are changing gid,
 *      or if you are changing egid to value which differs from old gid,
 *      then saved gid is updated to new egid value.
 *
 * Results:
 *      0 on success, -1 on failure, errno set
 *
 * Side effects:
 *      errno may be modified on success
 *
 *----------------------------------------------------------------------------
 */

int
Id_SetREGid(gid_t gid,		// IN: new gid
	    gid_t egid)		// IN: new effective gid
{
#if defined(sun) || defined(__FreeBSD__)
   return setregid(gid, egid);
#else
   if (uid32) {
      int r = syscall(SYS_setregid32, gid, egid);

      if (r != -1 || errno != ENOSYS) {
         return r;
      }
      uid32 = 0;
   }

   return syscall(SYS_setregid, gid, egid);
#endif
}
#endif


#if defined(__APPLE__) && !TARGET_OS_IPHONE
/*
 *----------------------------------------------------------------------------
 *
 * IdAuthCreate --
 *
 *      Create an Authorization session.
 *
 *      An Authorization session remembers which process name and which
 *      credentials created it, and how much time has elapsed since it last
 *      prompted the user at the console to authenticate to grant the
 *      Authorization session a specific right.
 *
 * Results:
 *      On success: A ref to the Authorization session.
 *      On failure: NULL.
 *
 * Side effects:
 *      See IdAuthCreateWithFork.
 *
 *----------------------------------------------------------------------------
 */

static AuthorizationRef
IdAuthCreate(void)
{
   /*
    * Bug 195868: If thread credentials are in use, we need to fork.
    * Otherwise, avoid forking, as it breaks Apple's detection of
    * whether the calling process is a GUI process.
    *
    * This is needed because AuthorizationRefs created by GUI
    * processes can be passed to non-GUI processes to allow them to
    * prompt for authentication with a dialog that automatically
    * steals focus from the current GUI app.  (This is how the Mac UI
    * and VMX work today.)
    *
    * TODO: How should we handle single-threaded apps where uid !=
    * euid or gid != egid?  Some callers may expect us to check
    * against euid, others may expect us to check against uid.
    */

   uid_t thread_uid;
   gid_t thread_gid;
   int ret;

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
   ret = syscall(SYS_gettid, &thread_uid, &thread_gid);
#ifdef __clang__
#pragma clang diagnostic pop
#endif

   if (ret != -1) {
      /*
       * We have per-thread UIDs in use, so Apple's authorization
       * APIs don't work.  Fork so we can use them.
       */

      return IdAuthCreateWithFork();
   } else {
      if (errno != ESRCH) {
         Warning("%s: gettid failed, error %d.\n", __func__, errno);

         return NULL;
      } else {
          // Per-thread identities are not in use in this thread.
         AuthorizationRef auth;
         OSStatus ret;

         ret = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
                                   kAuthorizationFlagDefaults, &auth);

         if (ret == errAuthorizationSuccess) {
            return auth;
         } else {
            ASSERT_ON_COMPILE(sizeof ret == sizeof (int32));
            Warning("%s: AuthorizationCreate failed, error %d.\n",
                    __func__, (int32)ret);

            return NULL;
         }
      }
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * IdAuthCreateWithFork --
 *
 *      Create an Authorization session.
 *
 *      An Authorization session remembers which process name and which
 *      credentials created it, and how much time has elapsed since it last
 *      prompted the user at the console to authenticate to grant the
 *      Authorization session a specific right.
 *
 * Results:
 *      On success: A ref to the Authorization session.
 *      On failure: NULL.
 *
 * Side effects:
 *      The current process is forked.
 *
 *----------------------------------------------------------------------------
 */

static AuthorizationRef
IdAuthCreateWithFork(void)
{
   int fds[2] = { -1, -1, };
   pid_t child;
   AuthorizationRef auth = NULL;
   struct {
      Bool success;
      AuthorizationExternalForm ext;
   } data;
   uint8 buf;

   /*
    * XXX One more Apple bug related to thread credentials:
    *     AuthorizationCreate() incorrectly uses process instead of thread
    *     credentials. So for this code to properly work in the VMX for
    *     example, we must do this elaborate fork/handshake dance. Fortunately
    *     this function is only called once very early when a process starts.
    */

   if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
      Warning("%s: socketpair() failed.\n", __func__);
      goto out;
   }

   child = fork();
   if (child < 0) {
      Warning("%s: fork() failed.\n", __func__);
      goto out;
   }

   if (child) {
      size_t rcvd;
      int status;
      pid_t result;

      // Parent: use fds[0]

      // Wait until the child has created its process ref to the auth session.
      for (rcvd = 0; rcvd < sizeof data; ) {
         ssize_t actual;

         actual = read(fds[0], (void *)&data + rcvd, sizeof data - rcvd);
         ASSERT(actual <= sizeof data - rcvd);
         if (actual < 0) {
            ASSERT(errno == EPIPE);
            Warning("%s: parent read() failed because child died.\n",
                    __func__);
            data.success = FALSE;
            break;
         }

         rcvd += actual;
      }

      if (data.success) {
         if (AuthorizationCreateFromExternalForm(&data.ext, &auth)
             != errAuthorizationSuccess) {
            Warning("%s: parent AuthorizationCreateFromExternalForm() "
                    "failed.\n", __func__);
         }
      }

      // Tell the child it can now destroy its process ref to the auth session.
      write(fds[0], &buf, sizeof buf);

      // Reap the child, looping if we get interrupted by a signal.
      do {
         result = waitpid(child, &status, 0);
      } while (result == -1 && errno == EINTR);

      VERIFY(result == child);
   } else {
      // Child: use fds[1]
      OSStatus ret;

      ret = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
                                kAuthorizationFlagDefaults, &auth);
      data.success = ret == errAuthorizationSuccess;
      if (data.success) {
         ret = AuthorizationMakeExternalForm(auth, &data.ext);
         data.success = ret == errAuthorizationSuccess;
         if (!data.success) {
            Warning("%s: child AuthorizationMakeExternalForm() failed: %d.\n",
                    __func__, (int32)ret);
         }
      } else {
         Warning("%s: child AuthorizationCreate() failed: %d.\n",
                 __func__, (int32)ret);
      }

      // Tell the parent it can now create a process ref to the auth session.
      if (write(fds[1], &data, sizeof data) == sizeof data) {
         /*
          * Wait until the child can destroy its process ref to the auth
          * session.
          */

         for (;;) {
            ssize_t actual = read(fds[1], &buf, sizeof buf);

            ASSERT(actual <= sizeof buf);
            if (actual) {
               break;
            }
         }
      }

      /*
       * This implicitly:
       * o Destroys the child process ref to the Authorization session.
       * o Closes fds[0] and fds[1]
       */

      exit(0);
   }

out:
   close(fds[0]);
   close(fds[1]);

   return auth;
}


static Atomic_Ptr procAuth = { 0 };


/*
 *----------------------------------------------------------------------------
 *
 * IdAuthGet --
 *
 *      Get a ref to the process' Authorization session.
 *
 * Results:
 *      On success: The ref.
 *      On failure: NULL.
 *
 * Side effects:
 *      If the process' Authorization session does not exist yet, it is
 *      created.
 *
 *----------------------------------------------------------------------------
 */

static AuthorizationRef
IdAuthGet(void)
{
   AuthorizationRef authRef;

   if (UNLIKELY(Atomic_ReadPtr(&procAuth) == NULL)) {
      AuthorizationRef newProcAuth = IdAuthCreate();

      if (Atomic_ReadIfEqualWritePtr(&procAuth, NULL, newProcAuth)) {
         // Someone else snuck in before we did.  Free the new authorization.
         AuthorizationFree(newProcAuth, kAuthorizationFlagDefaults);
      }
   }

   authRef = Atomic_ReadPtr(&procAuth);
   if (authRef == NULL) {
      Log("%s: Failed to obtain an AuthorizationRef.\n", __func__);
   }

   return authRef;
}


/*
 *----------------------------------------------------------------------------
 *
 * Id_AuthGetLocal --
 *
 *      Get a local ref to the process' Authorization session.
 *
 * Results:
 *      On success: The ref.
 *      On failure: NULL.
 *
 * Side effects:
 *      If the process' Authorization session does not exist yet, it is
 *      created.
 *
 *----------------------------------------------------------------------------
 */

void *
Id_AuthGetLocal(void)
{
   return (void *)IdAuthGet();
}


/*
 *----------------------------------------------------------------------------
 *
 * Id_AuthGetExternal --
 *
 *      Get a cross-process ref to the process' Authorization session.
 *
 * Results:
 *      On success: An allocated cross-process ref.
 *      On failure: NULL.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

void *
Id_AuthGetExternal(size_t *size) // OUT
{
   AuthorizationRef auth;
   AuthorizationExternalForm *ext;

   auth = IdAuthGet();
   if (!auth) {
      return NULL;
   }

   ext = malloc(sizeof *ext);
   if (!ext) {
      Warning("Unable to allocate an AuthorizationExternalForm.\n");

      return NULL;
   }

   if (AuthorizationMakeExternalForm(auth, ext) != errAuthorizationSuccess) {
      Warning("AuthorizationMakeExternalForm() failed.\n");
      free(ext);

      return NULL;
   }

   *size = sizeof *ext;

   return ext;
}


/*
 *----------------------------------------------------------------------------
 *
 * Id_AuthSet --
 *
 *      Set the process' Authorization session to the Authorization session
 *      referred to by a cross-process ref.
 *
 * Results:
 *      On success: TRUE.
 *      On failure: FALSE.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

Bool
Id_AuthSet(void const *buf, // IN
           size_t size)     // IN
{
   AuthorizationRef newProcAuth;

   AuthorizationExternalForm const *ext =
                                      (AuthorizationExternalForm const *)buf;

   if (!buf || size != sizeof *ext) {
      Warning("%s: Invalid argument.\n", __func__);

      return FALSE;
   }

   ASSERT(!Atomic_ReadPtr(&procAuth));
   if (AuthorizationCreateFromExternalForm(ext,
                                   &newProcAuth) != errAuthorizationSuccess) {
      Warning("AuthorizationCreateFromExternalForm failed.\n");

      return FALSE;
   }

   if (Atomic_ReadIfEqualWritePtr(&procAuth, NULL, newProcAuth)) {
      /*
       * This is meant to be called very early on in the life of the
       * process.  If someone else has snuck in an authorization,
       * we're toast.
       */

      NOT_IMPLEMENTED();
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * Id_AuthCheck --
 *
 *      Check if 'right' is granted to the process' Authorization
 *      session, using the specified localized UTF-8 description as
 *      the prompt if it's non-NULL.
 *
 * Results:
 *      TRUE if the right was granted, FALSE if the user cancelled,
 *      entered the wrong password three times in a row, or if an
 *      error was encountered, or if the Authorization session is
 *      invalid or has not been granted the 'right'.
 *
 * Side effects:
 *      If showDialogIfNeeded is set and the specified Authorization session
 *      does not have the required privilege, displays a dialog to the user.
 *      The dialog grabs keyboard focus if Id_AuthSet() was previously called
 *      with a cross-process ref to a GUI process.
 *
 *----------------------------------------------------------------------------
 */

Bool
Id_AuthCheck(char const *right,                // IN
             char const *localizedDescription, // IN: UTF-8
             Bool showDialogIfNeeded)          // IN
{
   AuthorizationRef auth;
   AuthorizationItem rightsItems[1] = { { 0 } };
   AuthorizationRights rights;
   AuthorizationItem environmentItems[1] = { { 0 } };
   AuthorizationEnvironment environmentWithDescription = { 0 };
   const AuthorizationEnvironment *environment =
                                              kAuthorizationEmptyEnvironment;

   auth = IdAuthGet();
   if (!auth) {
      return FALSE;
   }

   rightsItems[0].name = right;
   rights.items = rightsItems;
   rights.count = ARRAYSIZE(rightsItems);

   /*
    * By default, the API displays a dialog saying "APPLICATIONNAME
    * requires that you type your password" (if you're an admin; the
    * message is different if you're not).
    *
    * If the localized description is present, the API uses that
    * description and appends a space followed by the above string.
    */

   if (localizedDescription) {
      environmentItems[0].name = kAuthorizationEnvironmentPrompt;
      environmentItems[0].valueLength = strlen(localizedDescription);
      environmentItems[0].value = (void *)localizedDescription;
      environmentWithDescription.items = environmentItems;
      environmentWithDescription.count = ARRAYSIZE(environmentItems);
      environment = &environmentWithDescription;
   }

   /*
    * TODO: Is this actually thread-safe when multiple threads act on
    * the same AuthorizationRef?  Apple's documentation doesn't
    * actually say whether it is or is not.
    */

   return AuthorizationCopyRights(auth, &rights, environment,
             (showDialogIfNeeded ? kAuthorizationFlagInteractionAllowed |
                                   kAuthorizationFlagDefaults :
                                   kAuthorizationFlagDefaults) |
             kAuthorizationFlagExtendRights,
             NULL) == errAuthorizationSuccess;
}
#endif

#if !defined(_WIN32)
/*
 *----------------------------------------------------------------------------
 *
 * Id_BeginSuperUser --
 *
 *      Transition the calling thread from whatever its current effective
 *      user is to effectively root.
 *
 * Results:
 *      Returns the uid of the effective user from before the transition to
 *      effectively root. A "-1" is used to indicate that the thread was
 *      already root when this routine was called.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

uid_t
Id_BeginSuperUser(void)
{
   uid_t uid = Id_GetEUid();

   VERIFY(uid != (uid_t) -1);

   if (uid == 0) {
      uid = (uid_t) -1; // already root; nothing to do
   } else {
#if defined(__APPLE__)
#if TARGET_OS_IPHONE
      Warning("XXXIOS: implement %s\n", __func__);
#else
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
      syscall(SYS_settid, KAUTH_UID_NONE, KAUTH_GID_NONE /* Ignored. */);
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif
#else
      Id_SetRESUid((uid_t) -1, (uid_t) 0, (uid_t) -1); // effectively root
#endif
   }

   return uid;
}


/*
 *----------------------------------------------------------------------------
 *
 * Id_EndSuperUser --
 *
 *      Transition the calling thread from effective root user to
 *      another user.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      When transitioning from being effectively root to the specified
 *      user (uid) the effective gid of the calling thread may be lost.
 *
 *----------------------------------------------------------------------------
 */

void
Id_EndSuperUser(uid_t uid)  // IN:
{
   if ((uid != (uid_t) -1) && (uid != Id_GetEUid())) {
      ASSERT(uid != 0);  // Don't allow cheating like this

#if defined(__APPLE__)
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
      if (syscall(SYS_settid, uid, getgid()) == -1) {
#ifdef __clang__
#pragma clang diagnostic pop
#endif
         Log("Failed to release super user privileges.\n");
      }
#else
      Id_SetRESUid((uid_t) -1, uid, (uid_t) -1); // revert to uid
#endif
   }
}


#if defined(__linux__)
/*
 *----------------------------------------------------------------------------
 *
 * IdIsSetUGid --
 *
 *      Check if the binary is setuid or setgid.
 *
 * Results:
 *      TRUE if the process should be considered setuid or setgid.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

static Bool
IdIsSetUGid(void)
{
#if defined(__ANDROID__)
   /* Android does not have a secure_getenv, so be conservative. */
   return TRUE;
#else
   /*
    * We use __secure_getenv, which returns NULL if the binary is
    * setuid or setgid. Alternatives include,
    *
    *   a) getauxval(AT_SECURE); not available until glibc 2.16.
    *   b) __libc_enable_secure; may not be exported.
    *
    * Use (a) when we are based on glibc 2.16, or newer.
    */

#if defined(__GLIBC__) && \
           (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16))
   return getauxval(AT_SECURE) != 0;
#else
   static const char envName[] = "VMW_SETUGID_TEST";

   /*
    * Solves issue where includes are taken from glibc-2.11 while libraries are being
    * linked against glibc-2.31 for a rde-rft-rel app
    */
   extern __attribute__((weak)) char *__secure_getenv(const char *);
   extern __attribute__((weak)) char *secure_getenv(const char *);

   if (setenv(envName, "1", TRUE) == -1) {
      return TRUE; /* Conservative */
   }
   if (__secure_getenv != NULL) {
      return __secure_getenv(envName) == NULL;
   }
   if (secure_getenv != NULL) {
      return secure_getenv(envName) == NULL;
   }
   return TRUE;
#endif
#endif
}
#endif


/*
 *----------------------------------------------------------------------------
 *
 * Id_IsSetUGid --
 *
 *      Check if the environment should be treated with suspicion in a
 *      security sensitive context.
 *
 *      Most commonly, this will return TRUE when a binary is setuid or
 *      setgid, but will also be TRUE when the uid does not match the
 *      effective uid (for example if the current binary was exec'd from
 *      a setuid or setgid binary without proper uid scrubbing).
 *
 *      Detecting dangerous environments is best-effort and there are
 *      some known holes (see PR 1085298), so the best practice is to
 *      design systems such that this checking is not required.
 *
 * Results:
 *      TRUE if the process should be treated with suspicion in a
 *      security sensitive context. Otherwise FALSE.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

Bool
Id_IsSetUGid(void)
{
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(sun)
   uid_t ruid = getuid();
   uid_t euid = geteuid();
   gid_t rgid = getgid();
   gid_t egid = getegid();

   return issetugid() == 1 || ruid != euid || rgid != egid;
#elif defined(__linux__)
   uid_t ruid, euid, suid;
   gid_t rgid, egid, sgid;

   /* We want to make sure that this call always succeeds. */
   if (IdGetRESUid(&ruid, &euid, &suid) != 0 ||
       IdGetRESGid(&rgid, &egid, &sgid) != 0) {
      return TRUE; /* Conservative */
   }

   return IdIsSetUGid() ||
          ruid != euid || ruid != suid ||
          rgid != egid || rgid != sgid;
#else
   return TRUE; /* Conservative */
#endif
}
#endif
07070100000194000081A40000000000000000000000016822550500006200000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/iovector.c    /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * iovector.c --
 *
 *      I/O vector management code.
 */

#ifdef _WIN32
#include <windows.h>
#endif

#include "vmware.h"
#include "util.h"
#include "iovector.h"

#define LGPFX   "IOV: "

/*
 * Structure used when duplicating iov.
 */
struct VMIOVecAndEntries {
   VMIOVec iov; /* has to be first */
   struct iovec e[0];
};


/*
 *---------------------------------------------------------------------------
 *
 * IOV_Log --
 *
 *      Logs the content of an iov to the log file.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
IOV_Log(const VMIOVec *iov)     // IN
{
   if (iov) {
      uint32 i;
      Log("###### dumping content of iov ######\n");
      Log("%s\n", iov->read ? "READ" : "WRITE");
      Log("startSector = %"FMT64"d\n", iov->startSector);
      Log("numSectors = %"FMT64"d\n", iov->numSectors);
      Log("numBytes = %"FMT64"d\n", iov->numBytes);
      Log("numEntries = %d\n", iov->numEntries);
      for (i = 0; i < iov->numEntries; i++) {
         Log("  entries[%d] = %p / %"FMTSZ"u\n", 
             i, iov->entries[i].iov_base, (size_t)iov->entries[i].iov_len);
      }
   } else {
      Log("###### iov is NULL!! ######\n");
   }
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_Zero --
 *
 *      Zeros the content of an iov.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
IOV_Zero(VMIOVec *iov)   // IN
{
   uint64 numBytesLeft;
   int i;

   ASSERT(iov);
   ASSERT(iov->read);

   numBytesLeft = iov->numBytes;
   i = 0;

   while (numBytesLeft > 0) {
      size_t c = MIN(numBytesLeft, iov->entries[i].iov_len);
      void *buf;

      VERIFY(i < iov->numEntries);
      buf = iov->entries[i].iov_base;
      ASSERT(buf && buf != LAZY_ALLOC_MAGIC);
      memset(buf, 0, c);
      numBytesLeft -= c;
      i++;
   }
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_Allocate --
 *
 *      Allocates a brand new iov to be freed with IOV_Free.
 *
 * Results:
 *      A VMIOVec*.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

VMIOVec*
IOV_Allocate(int numEntries)          // IN
{
   struct VMIOVecAndEntries *iov;

   iov = Util_SafeMalloc(sizeof *iov + numEntries * sizeof(struct iovec));
   iov->iov.entries = iov->e;
   iov->iov.allocEntries = NULL;
   iov->iov.numEntries = numEntries;

   return &iov->iov;
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_DuplicateStatic --
 *
 *      Duplicate an iov, potentially using a static'ly allocated array of
 *      struct iovec. 
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
IOV_DuplicateStatic(VMIOVec *iovIn,                     // IN
                    int numStaticEntries,               // IN
                    struct iovec *staticEntries,        // IN
                    VMIOVec *iovOut)                    // OUT
{
   ASSERT(staticEntries);
   ASSERT(iovIn);
   ASSERT(iovOut);

   Util_Memcpy(iovOut, iovIn, sizeof *iovOut);
   if (iovIn->numEntries <= numStaticEntries) {
      iovOut->allocEntries = NULL;
      iovOut->entries = staticEntries;
   } else {
      iovOut->allocEntries = Util_SafeMalloc(iovIn->numEntries * 
                                             sizeof(struct iovec));
      iovOut->entries = iovOut->allocEntries;
   }
   Util_Memcpy(iovOut->entries, iovIn->entries, 
          iovIn->numEntries * sizeof(struct iovec));
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_MakeSingleIOV --
 *
 *      Fills in an iov.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
IOV_MakeSingleIOV(VMIOVec* v,              // IN/OUT
                  struct iovec* entry,     // IN
                  SectorType startSector,  // IN
                  SectorType dataLen,      // IN
                  uint32 sectorSize,       // IN
                  uint8* buffer,           // IN
                  Bool read)               // IN
{
   ASSERT(v);
   ASSERT(entry);

   v->read = read;
   v->startSector = startSector;
   v->numSectors = dataLen;
   v->numBytes = dataLen * sectorSize;
   v->numEntries = 1;
   v->entries = entry;
   v->allocEntries = entry;
   entry->iov_base = (char *)buffer;
   entry->iov_len = (size_t) v->numBytes;
}


/*
 *-----------------------------------------------------------------------------
 *
 * IOV_IsZero --
 *
 *      Tell if an iov is full of zeros. Used when we are about to write an iov
 *      in a grain, if it's zero and the grain does not exist, we just do
 *      nothing.
 *
 * Result:
 *      TRUE/FALSE
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
IOV_IsZero(VMIOVec* iov)      // IN: the iov to scan
{
   uint32 i;

   for (i = 0; i < iov->numEntries; i++) {
      if (!Util_BufferIsEmpty(iov->entries[i].iov_base,
                              iov->entries[i].iov_len)) {
         return FALSE;
      }
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * IOVSplitList --
 *
 *      Utility function to split an iovec (byte granularity scatter-gather
 *      array) into two-- an initial one that's exactly a whole number of
 *      sectors long, and the remainder.  If the table entry that finishes
 *      off the requested region is actually larger than the amount of space
 *      left in the region, it is truncated and the remaining bytes and
 *      their location are returned in overlap. The size of the region is
 *      passed in via regionV->numSectors, and the rest of regionV is
 *      filled in with to describe a request for exactly that region.
 *
 * Results:
 *      Pointer to the first remaining entry from the original entries list.
 *
 * Side effects:
 *      overlap filled in if the original entries list doesn't have a clean
 *      break on the region boundary, otherwise overlap->iov_len == 0.
 *      Also in cases over overlap, the last entry of entries is truncated.
 *
 *----------------------------------------------------------------------------
 */

static struct iovec *
IOVSplitList(VMIOVec *regionV,      // IN/OUT: VMIOVec for this region
             struct iovec *entries, // IN/OUT: iovec to be split
             struct iovec *endPtr,  // IN: pointer to after last entry
             struct iovec *overlap, // OUT: overlap info if last truncated
             uint32 sectorSize)     // IN: # bytes in a sector
{
   struct iovec *curEntry;
  
   curEntry = entries;
   regionV->entries = curEntry;
   regionV->numEntries = 0;
   regionV->numBytes = 0;
   ASSERT(curEntry < endPtr); /* Better be at least one entry */

   do {
      regionV->numEntries++;
      regionV->numBytes += curEntry->iov_len;

      if (regionV->numBytes > regionV->numSectors * sectorSize) {
         int spillover;
         
         spillover = (int) (regionV->numBytes -
                                            regionV->numSectors * sectorSize);
         ASSERT(spillover < curEntry->iov_len);
         ASSERT(spillover > 0);

         /*
          * Truncate the last overlapping entry and store the excess. After
          * we finish this region, we'll smash this last entry with its
          * remainder and just move on to the next region.
          */

         regionV->numBytes -= spillover;
         curEntry->iov_len -= spillover;
         overlap->iov_len = spillover;
         overlap->iov_base = (char *)curEntry->iov_base + curEntry->iov_len;
         break;
      } else if (regionV->numBytes == regionV->numSectors * sectorSize) {
         /*
          * Clean finish. The last entry for this region will be handled
          * with no overlap, so increment past it for the start of the next
          * region's entries.
          */

         overlap->iov_len = 0;
         curEntry++;
         break;
      }
      curEntry++;
   } while (curEntry < endPtr);

   ASSERT(regionV->numBytes == regionV->numSectors * sectorSize);

   return curEntry;
}


/*                              
 *----------------------------------------------------------------------------
 *
 * IOV_Split --
 *
 *      Utility function useful for iterating over VMIOVec.  You setup
 *      numSectors and then pass in the vector for the whole remaining
 *      transfer.  The code creates a VMIOVec to describe the subset of the
 *      transfer contained in the region and adjusts origV so that it describes
 *      the remainder.  
 *
 * Results:
 *      a VMIOVec* describing the first numSectors sectors of origV.
 *
 * Side effects:
 *      See above-- origV is split into regionV and the remainder with
 *      overlap filled in if the last entry of the region straddled the
 *      boundary.  Otherwise overlap->iov_len is set to zero.
 *
 *----------------------------------------------------------------------------
 */

VMIOVec*
IOV_Split(VMIOVec *origV,         // IN/OUT: VMIOVec for whole xfer
          SectorType numSectors,  // IN
          uint32 sectorSize)      // IN: # bytes in a sector
{  
   struct VMIOVecAndEntries *v;
   int cpySize;
   VMIOVec* iov;

   ASSERT(origV);
   ASSERT(numSectors > 0);
   ASSERT(numSectors <= origV->numSectors);

   /*
    * The resulting iov cannot have more entries than the incoming one.
    */

   v = Util_SafeMalloc(sizeof *v + origV->numEntries * sizeof(struct iovec));
   iov = &v->iov;
   Util_Memcpy(iov, origV, sizeof *iov);
   iov->allocEntries = NULL;
   iov->numSectors = numSectors;

   /*
    * Handle lazy allocation of backing store.
    */

   if (origV->entries->iov_base == LAZY_ALLOC_MAGIC && 
       origV->entries->iov_len == 0) {

      ASSERT(origV->numEntries == 1);
      iov->entries = v->e;
      Util_Memcpy(iov->entries, origV->entries, sizeof(struct iovec));

      iov->numBytes = iov->numSectors * sectorSize;

      origV->startSector += numSectors;
      origV->numSectors -= numSectors;
      origV->numBytes -= iov->numBytes;

      return iov;
   }

   /* See if the region is the whole thing */
   if (origV->numSectors == numSectors) {
      cpySize = origV->numEntries * sizeof *origV->entries;
      iov->entries = v->e;
      Util_Memcpy(iov->entries, origV->entries, cpySize);

      origV->startSector += numSectors;
      origV->numSectors = 0;

      origV->numEntries = 0;
      origV->numBytes = 0;
   } else {
      void* tmpPtr;
      struct iovec overlap = { 0, };

      origV->startSector += numSectors;
      origV->numSectors -= numSectors;
      origV->entries = IOVSplitList(iov, origV->entries,
                                    origV->entries + origV->numEntries, 
                                    &overlap, sectorSize);

      cpySize = iov->numEntries * sizeof *iov->entries;
      tmpPtr = iov->entries;
      iov->entries = v->e;
      Util_Memcpy(iov->entries, tmpPtr, cpySize);

      origV->numEntries -= iov->numEntries;
      if (overlap.iov_len != 0) { 
         origV->entries->iov_len = overlap.iov_len;
         origV->entries->iov_base = overlap.iov_base;
         origV->numEntries++; 
      }
      origV->numBytes -= iov->numBytes;
   }
   ASSERT(iov->numEntries > 0);

   return iov;
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_WriteIovToBuf --
 *
 *      This function takes an iov and a buffer as input and writes the content
 *      of the buffers pointed to by the iov into buf.
 *
 * Result:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
IOV_WriteIovToBuf(struct iovec const *entries, // IN
                  int numEntries,              // IN
                  uint8 *bufOut,               // OUT
                  size_t bufSize)              // IN
{
   size_t count = 0;
   int i;

   ASSERT(entries);
   ASSERT(bufOut);

   for (i = 0; i < numEntries; i++) {
      size_t numBytes;

      ASSERT(entries[i].iov_base);
      ASSERT(entries[i].iov_base != LAZY_ALLOC_MAGIC);

      numBytes = MIN(bufSize - count, entries[i].iov_len);

      Util_Memcpy(&bufOut[count], entries[i].iov_base, numBytes);
      count += numBytes;

      if (count >= bufSize) {
         return;
      }

      VERIFY(count <= bufSize);
   }
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_Duplicate --
 *     
 *      Allocates a brand new iov, the resulting iov should be free'd using
 *      IOV_Free.
 * 
 * Result:
 *      A duplicated iov.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

VMIOVec*
IOV_Duplicate(VMIOVec* iovIn)     // IN
{
   struct VMIOVecAndEntries* v;

   v = Util_SafeMalloc(sizeof *v + iovIn->numEntries * sizeof(struct iovec));
   Util_Memcpy(&v->iov, iovIn, sizeof *iovIn);
   v->iov.allocEntries = NULL;
   v->iov.entries = v->e;
   Util_Memcpy(v->iov.entries, iovIn->entries,
          iovIn->numEntries * sizeof(struct iovec));

   return &v->iov;
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_Free --
 *     
 *      Frees an iov.
 * 
 * Result:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
IOV_Free(VMIOVec* iov)     // IN
{
   ASSERT(iov);
   if (iov->allocEntries) {
      free(iov->allocEntries);
      iov->allocEntries = NULL;
   }
   free(iov);
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_WriteBufToIov --
 *
 *      This function copies the content of bufIn into the buffer pointed to by
 *      entries. It basically does the opposite of IOV_WriteIovToBuf.
 *
 * Result:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

void
IOV_WriteBufToIov(const uint8 *bufIn,          // IN
                  size_t bufSize,              // IN
                  struct iovec const *entries, // OUT
                  int numEntries)              // IN
{
   size_t count = 0;
   int i;

   ASSERT(entries);
   VERIFY_BUG(29009, bufIn);

   for (i = 0; i < numEntries; i++) {
      size_t numBytes;

      ASSERT(entries[i].iov_base);
      ASSERT(entries[i].iov_base != LAZY_ALLOC_MAGIC);

      numBytes = MIN(bufSize - count, entries[i].iov_len);

      Util_Memcpy(entries[i].iov_base, &bufIn[count], numBytes);
      count += numBytes;
      if (count >= bufSize) {
         return;
      }
      VERIFY(count <= bufSize);
   }
}


/*
 *---------------------------------------------------------------------------
 *
 * IOVFindFirstEntryOffset --
 *
 *      This function takes an iov and a byte offset and returns the
 *      index of the first entry and offset in that entry where copy starts.
 * 
 * Result:
 *      If offset is within iov, returns the index of the first entry and
 *      sets entryOffset. Otherwise, return numEntries.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

static int
IOVFindFirstEntryOffset(struct iovec* entries,        // IN
                        int numEntries,               // IN
                        size_t iovOffset,             // IN
                        size_t *entryOffsetp)         // OUT

{
   size_t entryLen = 0, entryOffset = 0;
   int i;

   ASSERT(entries);
   ASSERT(entryOffsetp);

   /* find the entry where to start */
   for (i = 0; (iovOffset >= entryOffset) && (i < numEntries); i++) {
      entryLen = entries[i].iov_len;
      entryOffset += entryLen;
   }

   if (iovOffset >= entryOffset) {
      /* iov offset is outside the iov - copy nothing */
      Log(LGPFX"%s:%d i %d (of %d), offsets: entry %"FMTSZ"u, iov %"FMTSZ"u "
          "invalid iov offset\n",
          __FILE__, __LINE__, i, numEntries, entryOffset, iovOffset);

      return numEntries;
   }

   /* i is index in next entry. Set entryOffset to offset in current entry */
   entryOffset = iovOffset - (entryOffset - entryLen);
   ASSERT(entryOffset < entryLen);

   *entryOffsetp = entryOffset;

   return i - 1;
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_WriteIovToBufPlus --
 *
 *      This function takes an iov and a buffer as input and writes the content
 *      of the buffers pointed to by the iov into buf.
 *      It is similar to IOV_WriteIovToBuf but copy may start at any point
 *      within the iov and may only partially overlap.
 *      iovOffset is the offset in bytes within the iov where to start copying.
 * 
 * Result:
 *      Returns the number of bytes copied.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

size_t
IOV_WriteIovToBufPlus(struct iovec* entries,    // IN
                  int numEntries,               // IN
                  uint8* bufOut,                // OUT
                  size_t bufSize,               // IN
                  size_t iovOffset)             // IN
{
   size_t entryLen, entryOffset;
   size_t count = bufSize;
   int i;

   VERIFY_BUG(29009, bufOut);

   i = IOVFindFirstEntryOffset(entries, numEntries, iovOffset, &entryOffset);

   for (; count && (i < numEntries); i++) {
      char *base = (char *)(entries[i].iov_base) + entryOffset;
      size_t iov_len = entries[i].iov_len;

      ASSERT(entries[i].iov_base || entries[i].iov_len == 0);
      ASSERT(entries[i].iov_base != LAZY_ALLOC_MAGIC);

      if (iov_len <= 0) {
         continue;
      }
      entryLen = MIN(count, iov_len - entryOffset);

      Util_Memcpy(bufOut, base, entryLen);

      count -= entryLen;
      bufOut += entryLen;
      entryOffset = 0;
   }

   ASSERT(count <= bufSize);

   return bufSize - count;
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_WriteBufToIovPlus --
 *
 *      This function copies the content of bufIn into the buffer pointed to by
 *      entries. It is similar to IOV_WriteBufToIov but the buffer may be
 *      copied anywhere within the iov and may only partially overlap.
 *      iovOffset is the offset in bytes within the iov where to start copying.
 * 
 * Result:
 *      Returns the number of bytes copied.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

size_t
IOV_WriteBufToIovPlus(uint8* bufIn,             // IN
                      size_t bufSize,           // IN
                      struct iovec* entries,    // OUT
                      int numEntries,           // IN
                      size_t iovOffset)         // IN
{
   size_t entryLen, entryOffset;
   size_t count = bufSize;
   int i;

   VERIFY_BUG(29009, bufIn);

   i = IOVFindFirstEntryOffset(entries, numEntries, iovOffset, &entryOffset);

   for (; count && (i < numEntries); i++) {
      char *base = (char *)(entries[i].iov_base) + entryOffset;
      size_t iov_len = entries[i].iov_len;

      VERIFY_BUG(33859, entries[i].iov_base || entries[i].iov_len == 0);
      ASSERT(entries[i].iov_base != LAZY_ALLOC_MAGIC);

      if (iov_len <= 0) {
         continue;
      }
      entryLen = MIN(count, iov_len - entryOffset);
      
      Util_Memcpy(base, bufIn, entryLen);

      count -= entryLen;
      bufIn += entryLen;
      entryOffset = 0;
   }

   ASSERT(count <= bufSize);

   return bufSize - count;
}


/*
 *---------------------------------------------------------------------------
 *
 * IOV_WriteIovToIov --
 *
 *      This function copies the overlapping portion from the source iov
 *      to the target iov, that is the region defined by:
 *      startSector = MAX(srcIov->startSector, dstIov->startSector)
 *      numSectors = MIN(<last src sector>, <last dst sector>) - startSector.
 *
 *      sectorSizeShift is conversion factor between sector and byte.
 *
 *      NOTE: assume that iov->numBytes is the actual number of bytes to copy.
 *      Do not copy beyond numBytes for either src or dst iov.
 * 
 * Result:
 *      Returns the number of bytes copied.
 *
 * Side effects:
 *      None.
 *
 *---------------------------------------------------------------------------
 */

size_t
IOV_WriteIovToIov(VMIOVec *srcIov,        // IN
                  VMIOVec *dstIov,        // OUT
                  uint32 sectorSizeShift) // IN
{
   size_t entryLen = 0, srcEntryOffset, copyLen, retval;
   uint64 srcStartByte, dstStartByte, startByte, endByte;
   int64 count, srcIovOffset, dstIovOffset;
   struct iovec *srcEntries = srcIov->entries;
   uint32 srcNumEntries = srcIov->numEntries;
   struct iovec *dstEntries = dstIov->entries;
   uint32 dstNumEntries = dstIov->numEntries;
   int i;

   ASSERT(srcIov);
   ASSERT(dstIov);

   /* find start byte address for src, dst and common region */
   srcStartByte = srcIov->startSector << sectorSizeShift;
   dstStartByte = dstIov->startSector << sectorSizeShift;
   startByte = MAX(srcStartByte, dstStartByte);

   /* find num bytes and end byte address for common region */
   endByte = srcStartByte + srcIov->numBytes;
   count = dstStartByte + dstIov->numBytes;
   endByte = MIN(endByte, count);

   count = endByte - startByte;

   /* count is number of bytes to copy, [startByte,endByte) is region to copy */
   if (count <= 0) {    /* no overlap */
      Log(LGPFX"%s:%d iov [%"FMT64"u:%"FMT64"u] and [%"FMT64"u:%"FMT64"u] - "
          "no overlap!\n", __FILE__, __LINE__, srcIov->startSector,
          srcIov->numSectors, dstIov->startSector, dstIov->numSectors);

      return 0;
   }

   srcEntries = srcIov->entries;

   ASSERT(srcEntries);
   ASSERT(dstIov->entries);

   /* srcIovOffset is byte offset where to start copy in src iov */
   srcIovOffset = startByte - srcStartByte;
   /* dstIovOffset is byte offset where to start copy in dst iov */
   dstIovOffset = startByte - dstStartByte;

   ASSERT(srcIovOffset >= 0);
   ASSERT(dstIovOffset >= 0);

   retval = (size_t)count;

   /* first find the src entry where to start */
   i = IOVFindFirstEntryOffset(srcEntries, srcNumEntries,
                               (size_t) srcIovOffset, &srcEntryOffset);

   for (; count && (i < srcNumEntries); i++) {
      size_t iov_len = srcEntries[i].iov_len;

      ASSERT(srcEntries[i].iov_base || srcEntries[i].iov_len == 0);
      ASSERT(srcEntries[i].iov_base != LAZY_ALLOC_MAGIC);

      if (iov_len <= 0) {
         continue;
      }
      entryLen = MIN(count, iov_len - srcEntryOffset);

      copyLen = IOV_WriteBufToIovPlus(
                           (uint8 *)(srcEntries[i].iov_base) + srcEntryOffset,
                                      entryLen, dstEntries,
                                      dstNumEntries, dstIovOffset);

      if (copyLen == 0) {  /* finished */
         break;
      }
      ASSERT(copyLen <= entryLen);

      count -= copyLen;
      dstIovOffset += copyLen;
      srcEntryOffset = 0;
   }

   ASSERT(count <= retval);

   return retval - count;
}


#ifdef VMX86_DEBUG
/*
 *-----------------------------------------------------------------------------
 *
 * IOV_Assert --
 *
 *      Checks that the 'numEntries' iovecs in 'iov' are non-null and have
 *      nonzero lengths.
 *
 *      Meant to be called via IOV_ASSERT macro.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *	Assert-fails if the iovec is invalid.
 *
 *-----------------------------------------------------------------------------
 */

void
IOV_Assert (struct iovec *iov,          // IN: iovec to check
            uint32 numEntries)          // IN: # of entries in 'iov'
{
   ASSERT(iov);
   ASSERT(numEntries);

   for (; numEntries-- > 0; iov++) {
      ASSERT(iov->iov_base);
      ASSERT(iov->iov_len);
   }
}
#endif
07070100000195000081A40000000000000000000000016822550500003768000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/jsonUTF8.c    /*********************************************************
 * Copyright (c) 2020-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include <stdlib.h>
#include "vmware.h"
#include "codeset.h"
#include "vm_ctype.h"
#include "dynbuf.h"
#include "strutil.h"
#include "unicodeBase.h"


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_JsonEscape --
 *
 *      Escape a unicode string following JSON rules.
 *
 *      From https://www.rfc-editor.org/rfc/rfc8259.html#section-7:
 *
 *      ... All Unicode characters may be placed within the
 *      quotation marks, except for the characters that MUST be escaped:
 *      quotation mark, reverse solidus, and the control characters (U+0000
 *      through U+001F).
 *
 *      ... If the character is in the Basic
 *      Multilingual Plane (U+0000 through U+FFFF), then it may be
 *      represented as a six-character sequence: a reverse solidus, followed
 *      by the lowercase letter u, followed by four hexadecimal digits that
 *      encode the character's code point....
 *
 *      Alternatively, there are two-character sequence escape
 *      representations of some popular characters.  So, for example, a
 *      string containing only a single reverse solidus character may be
 *      represented more compactly as "\\"
 *
 *      ...
 *
 *                  %x62 /          ; b    backspace       U+0008
 *                  %x66 /          ; f    form feed       U+000C
 *                  %x6E /          ; n    line feed       U+000A
 *                  %x72 /          ; r    carriage return U+000D
 *                  %x74 /          ; t    tab             U+0009
 *
 * Results:
 *      NULL Failure!
 *     !NULL Success! The escaped string. The caller is responsible to free
 *                    this.
 *
 * Side effects:
 *      Memory is allocated
 *
 *-----------------------------------------------------------------------------
 */

char *
CodeSet_JsonEscape(const char *utf8)                   // IN:
{
   DynBuf b;
   char *res;
   const char *p;
   const char *end;
   Bool success = TRUE;

   ASSERT(utf8 != NULL);
   if (utf8 == NULL) {
      return NULL;
   }

   DynBuf_Init(&b);

   p = utf8;
   end = p + strlen(utf8);

   while (p < end) {
      uint32 len = CodeSet_GetUtf8(p, end, NULL);

      if (len == 0) {
         success = FALSE;
         break;
      }

      if (len > 1 || (*p > 0x001F && *p != '"' && *p != '\\')) {
         DynBuf_SafeAppend(&b, p, len);
      } else {
         DynBuf_SafeAppend(&b, "\\", 1);
         switch (*p) {
         case '"':
         case '\\':
            DynBuf_SafeAppend(&b, p, 1);
            break;
         case '\b':
            DynBuf_SafeAppend(&b, "b", 1);
            break;
         case '\f':
            DynBuf_SafeAppend(&b, "f", 1);
            break;
         case '\n':
            DynBuf_SafeAppend(&b, "n", 1);
            break;
         case '\r':
            DynBuf_SafeAppend(&b, "r", 1);
            break;
         case '\t':
            DynBuf_SafeAppend(&b, "t", 1);
            break;
         default:
            StrUtil_SafeDynBufPrintf(&b, "u%04x", *p);
            break;
         }
      }
      p += len;
   }

   if (success) {
      res = DynBuf_DetachString(&b);
   } else {
      res = NULL;
   }

   DynBuf_Destroy(&b);

   return res;
}


/* Constants used by json unescape routines. */

/* Number of hex digits in a "\u" escape sequence. */
#define JSON_UESC_NDIGITS 4

/*
 * Maximum number of UTF-8 code units (bytes) per Unicode code point.
 * From bora/lib/unicode/unicode/utf8.h.
 */
#ifndef U8_MAX_LENGTH
#define U8_MAX_LENGTH 4
#endif

/*
 *----------------------------------------------------------------------------
 *
 * CodeSet_JsonGetHex --
 *
 *      Retrieve and convert to an integer the four hex digits that are
 *      part of the six character escape sequence that starts with "\u".
 *
 *      On entry, p points to the first code point following "\u."
 *
 * Results:
 *      TRUE on success, with *value set to the integer value.
 *
 *      FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
CodeSet_JSonGetHex(const char *p,       // IN:
                   const char *end,     // IN:
                   int32 *value)        // OUT:
{
   char hexBuf[JSON_UESC_NDIGITS + 1];   /* +1 for NUL */
   int numHexDigits = 0;

   ASSERT(p <= end);

   /*
    * Assumes called with p set to first code point following "\u" and looks
    * for four hex digits.   No need to call CodeSet_GetUtf8 to verify that
    * the code point length of these characters is one since it's always on
    * a code point boundary and it's OK to check directly for specific
    * ASCII characters in such a case, and if there's a match to an ASCII
    * character then advancing the pointer by a single character will advance
    * to the next code point.
    */
   while (numHexDigits < JSON_UESC_NDIGITS) {
      if (p >= end || !CType_IsXDigit(*p)) {
         return FALSE;
      }
      hexBuf[numHexDigits++] = *p++;
   }

   hexBuf[numHexDigits] = '\0';
   *value = strtol(hexBuf, NULL, 16);
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CodeSet_JsonUnescapeU --
 *
 *      Handle a JSON escape sequence beginning with "\u", consisting either
 *      of:
 *         (1) "\u" followed by four hex digits; or
 *         (2) two such consecutive sequences encoding a character
 *             outside the Basic MultiLingual Plane as a UTF-16
 *             surrogate pair.
 *
 *      Note "\u0000" is not allowed and is considered an error if
 *      encountered.
 *
 *      On entry to the routine, p should be pointing at the backslash
 *      character that starts the (possible) escape sequence.
 *
 *      outBuf is the base of a char array of size >= U8_MAX_LENGTH + 1, i.e.,
 *      large enough to hold a NUL-terminated UTF-8 encoding of any Unicode
 *      code point.
 *
 * Results:
 *      On success, the length of the escape sequence, with the unescaped
 *      result plus a NUL terminator in outBuf.
 *
 *      0 on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
CodeSet_JsonUnescapeU(const char *p,        // IN:
                      const char *end,      // IN:
                      char *outBuf)         // OUT:
{
   uint32 w;
   uint32 utf32Buf[2];  /* code point value plus 0 terminator */
   char *utf8String;
   uint32 len;
   const char *start = p;

   /*
    * Assumes called only if starts with "\u".  No need to call
    * CodeSet_GetUtf8 in this ASSERT since this is checking for specific ASCII
    * characters - see comment preceding ASSERT in CodeSet_JsonUnescapeOne
    * below.
    */
   ASSERT(p < end && *p == '\\');
   ASSERT(&p[1] < end && p[1] == 'u');

   /* Code point of 0 ("\u0000") not allowed. */
   if (!CodeSet_JSonGetHex(&p[2], end, &w) || w == 0) {
      return 0;
   }

   /* Advance p past "\u" and the hex digits that follow. */
   p += 2 + JSON_UESC_NDIGITS;

   /* If the value is a leading surrogate, then handle the trailing one. */
   if (U16_IS_LEAD(w)) {
      uint32 trail;

      /*
       * Check for '\', 'u', and four digits representing a trailer.  As
       * elsewhere, no need to call CodeSet_GetUtf8 since this is checking for
       * specific ASCII characters, and bails out if any of the checks fail.
       */
      if (p < end && *p++ == '\\' && p < end && *p++ == 'u' &&
          CodeSet_JSonGetHex(p, end, &trail) && U16_IS_TRAIL(trail)) {
         w = U16_GET_SUPPLEMENTARY(w, trail);

         /* Advance p past the digits that follow "\u". */
         p += JSON_UESC_NDIGITS;
      } else {
         return 0;
      }
   } else if (U16_IS_TRAIL(w)) {
      return 0;
   }

   /*
    * To get the UTF-8 for this code point, create a UTF-32 string
    * and convert to UTF-8.
    */
   utf32Buf[0] = w;
   utf32Buf[1] = 0;   /* needs a 4-byte 0 terminator */

   if (!CodeSet_UTF32ToUTF8((char *)utf32Buf, &utf8String)) {
      return 0;
   }

   len = strlen(utf8String);
   ASSERT(Unicode_IsBufferValid(utf8String, len, STRING_ENCODING_UTF8));
   ASSERT(len <= U8_MAX_LENGTH);
   memcpy(outBuf, utf8String, len + 1);

   free(utf8String);
   return p - start;
}


/*
 *----------------------------------------------------------------------------
 *
 * CodeSet_JsonUnescapeOne --
 *
 *      Handle a single JSON escape sequence.
 *
 *      On entry to the routine, p should be pointing at the backslash
 *      character that starts the (possible) escape sequence.
 *
 *      outBuf is the base of a char array of size >= U8_MAX_LENGTH + 1, i.e.,
 *      large enough to hold a NUL-terminated UTF-8 encoding of any Unicode
 *      code point.
 *
 * Results:
 *      On success, the length of the escape sequence, with the unescaped
 *      result plus a NUL terminator in outBuf.
 *
 *      0 on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
CodeSet_JsonUnescapeOne(const char *p,        // IN:
                        const char *end,      // IN:
                        char *outBuf)         // OUT:
{
   int len = 0;
   const char *start = p;

   /*
    * Assumes called only if first character is '\'.  Note that in the
    * ASSERT it's not necessary to call CodeSet_GetUtf8 to verify the
    * code point length is 1.  Since this is on a code point boundary,
    * if the byte matches a specific ASCII character (in this case, '\')
    * that is sufficient to verify the code point length of 1.
    */
   ASSERT(p < end && *p == '\\');

   /*
    * Advance p by a single char to get to the next code point since it's
    * known to be an ASCII character (i.e., '\') and therefore code point
    * length is 1.
    */
   if (++p < end) {
      /*
       * Preset len and outBuf for common case of valid two-character escape
       * sequence with one-character output; different values will be assigned
       * if the sequence turns out to start with "\u"  or is invalid.
       */
      len = 2;
      outBuf[1] = '\0';

      /*
       * As above, since this on a code point boundary and checking whether
       * it matches specific ASCII characters, it's not necessary to call
       * CodeSet_GetUtf8 to verify that the code point length is 1.  In the
       * event *p is the first byte of a multi-byte UTF-8 code point, we'll
       * end up in the default case of the switch and fail.
       */
      switch (*p) {
      case '\"':
      case '\\':
      case '/':
         outBuf[0] = *p;
         break;
      case 'b':
         outBuf[0] = '\b';
         break;
      case 'f':
         outBuf[0] = '\f';
         break;
      case 'r':
         outBuf[0] = '\r';
         break;
      case 'n':
         outBuf[0] = '\n';
         break;
      case 't':
         outBuf[0] = '\t';
         break;
      case 'u':
         len = CodeSet_JsonUnescapeU(start, end, outBuf);
         break;
      default:
         len = 0;
         break;
      }
   }
   return len;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CodeSet_JsonUnescape --
 *
 *      Copy a UTF8 string, reverting any JSON escape sequences found within
 *      the string according to the STD-90 spec at
 *      https://tools.ietf.org/html/std90.  This processes the same
 *      escape sequences that are allowed by the jsmn parser, and generally
 *      tries to follow the same logic as the jsmn escape parsing.  Any
 *      strings passed in to this routine have likely been through jsmn, and
 *      any invalid escape sequences should have been rejected.  However, this
 *      routine and those it calls still check for the possibility of
 *      invalid escape sequences and return failure when running into one, as
 *      opposed to assuming and/or asserting they are valid.
 *
 *      A general unescape routine is difficult to do, so the logic here is
 *      specific to JSON (as opposed to CodeSet_JsonEscape, which relies on
 *      the more general CodeSet_Utf8Escape).
 *
 * Results:
 *      NULL Failure!
 *     !NULL Success! The un-escaped string. The caller is responsible to free
 *                    this.
 *
 * Side effects:
 *      Returns a dynamically allocated string that must be freed by the
 *      caller.
 *
 *-----------------------------------------------------------------------------
 */

char *
CodeSet_JsonUnescape(const char *utf8)   // IN:
{
   DynBuf b;
   char *res;
   const char *p;
   const char *end;
   Bool success = TRUE;

   ASSERT(utf8 != NULL);

   DynBuf_Init(&b);
   p = utf8;
   end = p + strlen(p);

   while (p < end && success) {
      char unescaped[U8_MAX_LENGTH + 1];  /* +1 for NUL */
      uint32 len = CodeSet_GetUtf8(p, end, NULL);

      if (len == 0) {
         success = FALSE;
      } else if (len > 1 || *p != '\\') {
         DynBuf_SafeAppend(&b, p, len);
      } else if ((len = CodeSet_JsonUnescapeOne(p, end, unescaped)) != 0) {
         DynBuf_SafeAppend(&b, unescaped, strlen(unescaped));
      } else {
         success = FALSE;
      }
      p += len;
   }

   if (success) {
      res = DynBuf_DetachString(&b);
   } else {
      res = NULL;
   }

   DynBuf_Destroy(&b);

   return res;
}
07070100000196000081A40000000000000000000000016822550500001FEA000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/logFixed.c    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include "vmware.h"
#include "vm_basic_asm.h"

/*
 *-----------------------------------------------------------------------------
 *
 * LogFixed_Base2 --
 *
 *      Return log2(value) expressed as the ratio of two uint32 numbers.
 *
 * Results:
 *      As expected.
 *
 * Side effects:
 *      None
 *
 * NOTE:
 *      The log2(x) can be approximated as:
 *
 *      log2(x) = log2((1 + N) * 2^P)
 *              = P + log2(1 + N)
 *              = P + table[index]
 *
 *      where index are less significant bits than P, masked to whatever number
 *      of bits are necessary to ensure the accuracy goal. Thus, P is the bit
 *      number of the highest bit set; shifts are used to position the lower
 *      order bits to provide the requested number of index bits.
 *
 *      The number of index bits is TABLE_BITS, the size of the table,
 *      1 << TABLE_BITS.
 *
 *      The table entries are computed as the numerator of a fraction of
 *      BINARY_BASE:
 *
 *      table[i] = BINARY_BASE *
 *                        log2(1.0 + (((double) i) / (double) TABLE_SIZE)))
 *
 *      which allows log2(x) to be expressed as:
 *
 *      log2(x) = ((BINARY_BASE * P) + table[index]) / BINARY_BASE
 *
 *      maxError = 2.821500E-03; avgError = 1.935068E-05 over the range
 *      1 to 2E6. The test program is below.
 *
 *-----------------------------------------------------------------------------
 */

#define BINARY_BASE (64 * 1024)
#define TABLE_BITS 8

static const uint16 log2Table[] = {
     0,    368,    735,   1101,   1465,   1828,   2190,   2550,   2909,   3266,
  3622,   3977,   4331,   4683,   5034,   5383,   5731,   6078,   6424,   6769,
  7112,   7454,   7794,   8134,   8472,   8809,   9145,   9480,   9813,  10146,
 10477,  10807,  11136,  11463,  11790,  12115,  12440,  12763,  13085,  13406,
 13726,  14045,  14363,  14680,  14995,  15310,  15624,  15936,  16248,  16558,
 16868,  17176,  17484,  17790,  18096,  18400,  18704,  19006,  19308,  19608,
 19908,  20207,  20505,  20801,  21097,  21392,  21686,  21980,  22272,  22563,
 22854,  23143,  23432,  23720,  24007,  24293,  24578,  24862,  25146,  25429,
 25710,  25991,  26272,  26551,  26829,  27107,  27384,  27660,  27935,  28210,
 28483,  28756,  29028,  29300,  29570,  29840,  30109,  30377,  30644,  30911,
 31177,  31442,  31707,  31971,  32234,  32496,  32757,  33018,  33278,  33538,
 33796,  34054,  34312,  34568,  34824,  35079,  35334,  35588,  35841,  36093,
 36345,  36596,  36847,  37096,  37346,  37594,  37842,  38089,  38336,  38582,
 38827,  39071,  39315,  39559,  39801,  40044,  40285,  40526,  40766,  41006,
 41245,  41483,  41721,  41959,  42195,  42431,  42667,  42902,  43136,  43370,
 43603,  43836,  44068,  44299,  44530,  44760,  44990,  45219,  45448,  45676,
 45904,  46131,  46357,  46583,  46808,  47033,  47257,  47481,  47704,  47927,
 48149,  48371,  48592,  48813,  49033,  49253,  49472,  49690,  49909,  50126,
 50343,  50560,  50776,  50992,  51207,  51421,  51635,  51849,  52062,  52275,
 52487,  52699,  52910,  53121,  53331,  53541,  53751,  53960,  54168,  54376,
 54584,  54791,  54998,  55204,  55410,  55615,  55820,  56024,  56228,  56432,
 56635,  56837,  57040,  57242,  57443,  57644,  57844,  58044,  58244,  58443,
 58642,  58841,  59039,  59236,  59433,  59630,  59827,  60023,  60218,  60413,
 60608,  60802,  60996,  61190,  61383,  61576,  61768,  61960,  62152,  62343,
 62534,  62724,  62914,  63104,  63293,  63482,  63671,  63859,  64047,  64234,
 64421,  64608,  64794,  64980,  65165,  65351
};


void
LogFixed_Base2(uint64 value,         // IN:
               uint32 *numerator,    // OUT:
               uint32 *denominator)  // OUT:
{
   uint32 index;
   uint32 rawBits;
   uint32 maxBits;
   uint32 highBit;
   uint32 bitsOver;

   ASSERT_ON_COMPILE(ARRAYSIZE(log2Table) == (1 << TABLE_BITS));

   highBit = mssb64_0(value);
   ASSERT(highBit != -1);

   if (highBit <= TABLE_BITS) {
      index = (value << (TABLE_BITS - highBit)) & ((1 << TABLE_BITS) - 1);

      *numerator = (BINARY_BASE * highBit) + log2Table[index];
      *denominator = BINARY_BASE;

      return;
   }

   /*
    * If additional bits are available, use them to interpolate the table
    * to decrease the errors (especially the average). Bound the number of
    * additional bits as there is only a limited amount of bits available
    * from the interpolation table.
    */

   bitsOver = highBit - TABLE_BITS;
   if (bitsOver > 16) {
      bitsOver = 16;
   }

   maxBits = TABLE_BITS + bitsOver;

   rawBits = (value >> (highBit - maxBits)) & ((1 << maxBits) - 1);

   index = rawBits >> bitsOver;

   *numerator = (BINARY_BASE * highBit) + log2Table[index];

   if (index < (1 << TABLE_BITS) - 1) {
      uint32 extraBits = rawBits & ((1 << bitsOver) - 1);
      uint16 delta = log2Table[index + 1] - log2Table[index];

      *numerator += (extraBits * delta) / (1 << bitsOver);
   }

   *denominator = BINARY_BASE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * LogFixed_Base10 --
 *
 *      Return log10(value) expressed as the ratio of two uint32 numbers.
 *
 * Results:
 *      As expected.
 *
 * Side effects:
 *      None
 *
 * NOTE:
 *      Start with the identity:
 *
 *      log10(x) = log2(x) / log2(10)
 *
 *      Write as a fraction:
 *
 *      log2Numer / (log2Denom * log2(10))
 *
 *      log2(10) is 3.321928
 *
 *      maxError = 8.262237E-04; avgError = -1.787911E-05 over the range
 *      1 to 2E6. The test program is below.
 *
 *-----------------------------------------------------------------------------
 */

#define LOG10_BASE2 3.321928

void
LogFixed_Base10(uint64 value,         // IN:
                uint32 *numerator,    // OUT:
                uint32 *denominator)  // OUT:
{
   uint32 log2Numerator = 0;
   uint32 log2Denominator = 0;

   LogFixed_Base2(value, &log2Numerator, &log2Denominator);

   *numerator = log2Numerator;
   *denominator = LOG10_BASE2 * BINARY_BASE;
}

#if defined(TEST_PROG)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define LOG_TESTS 2000000

main()
{
   uint32 i;
   double sumLog2 = 0.0;
   double sumLog10 = 0.0;
   double maxErrorLog2 = 1E-30;
   double maxErrorLog10 = 1E-30;

   uint32 value = 1;

   for (i = 0; i < LOG_TESTS; i++) {
      double delta;
      double realLog;
      double fudgedLog;
      uint32 numerator;
      uint32 denominator;

      realLog = log2((double) value);
      LogFixed_Base2((uint64) value, &numerator, &denominator);
      fudgedLog = ((double) numerator) / ((double) denominator);

      delta = realLog - fudgedLog;

      if (delta > maxErrorLog2) {
         maxErrorLog2 = delta;
      }

      sumLog2 += delta;

      realLog = log10((double) value);
      LogFixed_Base10((uint64) value, &numerator, &denominator);
      fudgedLog = ((double) numerator) / ((double) denominator);

      delta = realLog - fudgedLog;

      if (delta > maxErrorLog10) {
         maxErrorLog10 = delta;
      }

      sumLog10 += delta;

      value++;
   }

   printf("LogFixed_BaseTwo: maxError = %E; avgError = %E\n", maxErrorLog2,
          sumLog2/((double) LOG_TESTS));

   printf("LogFixed_BaseTen: maxError = %E; avgError = %E\n", maxErrorLog10,
          sumLog10/((double) LOG_TESTS));
}
#endif
  07070100000197000081A40000000000000000000000016822550500003B67000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/machineID.c   /*********************************************************
 * Copyright (C) 2007-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * machineID.c --
 *
 *	Obtain "universally unique" identification information.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#if defined(_WIN32)	// Windows
#include <windows.h>
#include <process.h>
#include <iptypes.h>
#endif

#include "vmware.h"
#include "hostinfo.h"
#include "util.h"
#include "log.h"
#include "str.h"
#include "err.h"
#include "vm_product.h"
#include "vm_atomic.h"

#define LOGLEVEL_MODULE main
#include "loglevel_user.h"

#if defined(_WIN32)	// Windows
/*
 *----------------------------------------------------------------------
 *
 * FindWindowsAdapter --
 *
 *      Search the list of networking interfaces for an appropriate choice.
 *
 *      A suitable adapter either must contain or must not contain a
 *      (simple) pattern string as specified by the findPattern argument.
 *
 * Results:
 *      IP_ADAPTER_INFO pointer or NULL if no suitable choice can be made.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static IP_ADAPTER_INFO *
FindWindowsAdapter(IP_ADAPTER_INFO *head,  // IN:
                   char *pattern,          // IN:
                   Bool findPattern)       // IN:
{
   IP_ADAPTER_INFO *adapterInfo;
   IP_ADAPTER_INFO *adapterChoice = NULL;

   ASSERT(head);
   ASSERT(pattern);

   for (adapterInfo = head; adapterInfo; adapterInfo = adapterInfo->Next) {
      // Bias - only ethernet adapters; more likely to be local
      if (adapterInfo->AddressLength == 6) {
         Bool takeIt = FALSE;

         if ((adapterInfo->Description == NULL) ||
             (*adapterInfo->Description == '\0')) {
            takeIt = TRUE;
         } else {
            char *p = strstr(adapterInfo->Description, pattern);

            if (((p != NULL) && findPattern) ||
                ((p == NULL) && !findPattern)) {
               takeIt = TRUE;
            }
         }

         if (takeIt) {
            adapterChoice = adapterInfo;
            break;
         }
      }
   }

   return adapterChoice;
}


/*
 *----------------------------------------------------------------------
 *
 * ObtainHardwareID --
 *
 *      Locate and return the hardwareID for this machine.
 *
 * Results:
 *      0       No errors occured
 *      >0      failure (errno)
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      Return the MAC address of a suitable networking interface.
 *      The hardwareID will be zero if nothing suitable could be found.
 *
 *----------------------------------------------------------------------
 */

static int
ObtainHardwareID(uint64 *hardwareID) // OUT:
{
   void *buf;
   DWORD status;
   HMODULE dllHandle;
   IP_ADAPTER_INFO *adapterList;
   IP_ADAPTER_INFO *adapterChoice;
   DWORD (WINAPI *getAdaptersFn)(IP_ADAPTER_INFO *, ULONG *);

   ULONG bufLen = 0;

   // Deal with BUG 21643
   dllHandle = LoadLibrary(TEXT("icmp.dll"));

   if (!dllHandle) {
      Warning("%s Failed to load icmp.dll.\n", __FUNCTION__);

      return EINVAL;
   }

   FreeLibrary(dllHandle);

   dllHandle = LoadLibrary(TEXT("IpHlpApi.dll"));

   if (!dllHandle) {
      Warning("%s Failed to load iphlpapi.dll.\n", __FUNCTION__);

      return EINVAL;
   }

   getAdaptersFn = (void *) GetProcAddress(dllHandle, "GetAdaptersInfo");

   if (!getAdaptersFn) {
      FreeLibrary(dllHandle);
      Warning("%s Failed to find GetAdaptersInfo.\n", __FUNCTION__);

      return EINVAL;
   }

   // Force GetAdaptersInfo to provide the amount of memory necessary.

   status = (*getAdaptersFn)(NULL, &bufLen);

   switch (status) {
   case ERROR_NOT_SUPPORTED:
      Warning("%s GetAdaptersInfo is not supported.\n", __FUNCTION__);
      // fall through
   case ERROR_NO_DATA:
      FreeLibrary(dllHandle);
      *hardwareID = 0;
      return 0;
      break;

   case ERROR_BUFFER_OVERFLOW:
   case NO_ERROR:
      break;

   default:
      FreeLibrary(dllHandle);
      Warning("%s GetAdaptersInfo failure %d: %d.\n", __FUNCTION__,
              __LINE__, status);

      return EINVAL;
   }

   buf = malloc(bufLen);

   if (buf == NULL) {
      FreeLibrary(dllHandle);

      return ENOMEM;
   }

   adapterList = (IP_ADAPTER_INFO *) buf;

   status = (*getAdaptersFn)(adapterList, &bufLen);

   FreeLibrary(dllHandle);

   if (status != NO_ERROR) {
      // something is seriously wrong; worked before...
      Warning("%s GetAdaptersInfo failure %d: %d.\n", __FUNCTION__,
              __LINE__, status);

      free(buf);

      return EINVAL;
   }

   // Try to find real hardware.
   adapterChoice = FindWindowsAdapter(adapterList, PRODUCT_GENERIC_NAME,
                                      FALSE);

   if (adapterChoice == NULL) {
      *hardwareID = 0;
   } else {
      union bytes {
         uint64 data;
         char   bytes[8];
      } x;

      x.bytes[0] = adapterChoice->Address[0];
      x.bytes[1] = adapterChoice->Address[1];
      x.bytes[2] = adapterChoice->Address[2];
      x.bytes[3] = adapterChoice->Address[3];
      x.bytes[4] = adapterChoice->Address[4];
      x.bytes[5] = adapterChoice->Address[5];
      x.bytes[6] = '\0';
      x.bytes[7] = '\0';

      *hardwareID = x.data;
   }

   free(buf);

   return 0;
}
#elif defined(__APPLE__)	// MacOS X
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if_dl.h>
#include <net/ethernet.h>


/*
 *----------------------------------------------------------------------
 *
 * CheckEthernet --
 *
 *      Determine if "en<n>" exists and, if so, return its MAC address.
 *
 * Results:
 *      NULL    interface doesn't exist or isn't usable
 *      !NULL   interface exists and is usable
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static struct ifaddrs *
CheckEthernet(struct ifaddrs *ifp,  // IN:
              uint32 n)             // IN:
{
   struct ifaddrs *p;
   char name[8];

   // Construct the interface name
   Str_Sprintf(name, sizeof name, "en%u", n);

   // Go through the list and see if this interface exists and is usable.
   p = ifp;

   while (p != NULL) {
      if ((strcmp(p->ifa_name, name) == 0) &&
          (p->ifa_addr != NULL) &&
          (p->ifa_addr->sa_family == AF_LINK)) {
         break;
      }

      p = p->ifa_next;
   }

   return p;
}


/*
 *----------------------------------------------------------------------
 *
 * ObtainHardwareID --
 *
 *      Locate and return the hardwareID for this machine.
 *
 * Results:
 *      0       No errors occured
 *      >0      failure (errno)
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      Return the MAC address of a suitable networking interface.
 *      The hardwareID will be zero if nothing suitable could be found.
 *
 *----------------------------------------------------------------------
 */

static int
ObtainHardwareID(uint64 *hardwareID)  // OUT:
{
   uint32 i;
   struct ifaddrs *ifp;

   // Attempt to get the list of networking interfaces
   if (getifaddrs(&ifp) == -1) {
      int saveErrno = errno;

      Warning("%s getifaddrs failure: %s.\n", __FUNCTION__,
              Err_Errno2String(saveErrno));

      return saveErrno;
   }

   // search through a "reasonable" number of interfaces
   for (i = 0, *hardwareID = 0; i < 8; i++) {
      struct ifaddrs *p = CheckEthernet(ifp, i);

      if (p != NULL) {
         memcpy(hardwareID, LLADDR((struct sockaddr_dl *)p->ifa_addr), ETHER_ADDR_LEN);
         break;
      }
   }

   freeifaddrs(ifp);

   return 0;
}
#elif defined(__linux__) || defined __ANDROID__
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/time.h>
#if defined __ANDROID__
#include <sys/socket.h>  // For SOCK_DGRAM etc.
#endif


/*
 *----------------------------------------------------------------------
 *
 * CheckEthernet --
 *
 *      Determine if "eth<n>" exists and, if so, return its MAC address
 *
 * Results:
 *      0       No errors occured
 *      >0      failure (errno)
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
CheckEthernet(uint32 n,        // IN:
              char *machineID) // OUT:
{
   int          fd;
   int          erc;
   struct ifreq ifreq;
   int          saveErrno;

   fd = socket(AF_INET, SOCK_DGRAM, 0);

   if (fd == -1) {
      return errno;
   }

   /* get the MAC address of eth<n> */
   Str_Sprintf(ifreq.ifr_name, IFNAMSIZ, "eth%u", n);

   erc = ioctl(fd, SIOCGIFHWADDR, &ifreq);
   saveErrno = errno;

   close(fd);

   if (erc == -1) {
      return saveErrno;
   }

   machineID[0] = ifreq.ifr_hwaddr.sa_data[0] & 0xFF;
   machineID[1] = ifreq.ifr_hwaddr.sa_data[1] & 0xFF;
   machineID[2] = ifreq.ifr_hwaddr.sa_data[2] & 0xFF;
   machineID[3] = ifreq.ifr_hwaddr.sa_data[3] & 0xFF;
   machineID[4] = ifreq.ifr_hwaddr.sa_data[4] & 0xFF;
   machineID[5] = ifreq.ifr_hwaddr.sa_data[5] & 0xFF;
   machineID[6] = '\0';
   machineID[7] = '\0';

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * ObtainHardwareID --
 *
 *      Locate the hardwareID for this machine.
 *
 * Results:
 *      0       No errors occured
 *      >0      failure (errno)
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      Return the MAC address of a suitable networking interface.
 *      The hardwareID will be zero if nothing suitable could be found.
 *
 *      This is a "good enough" initial hack but will need to be revisited.
 *      Ideally checks should be made for sysfs (Linux 2.6 and later) and,
 *      if present, search through the information and try to insure that
 *      the ethernet chosen is on the motherboard.
 *
 *----------------------------------------------------------------------
 */

static int
ObtainHardwareID(uint64 *hardwareID)  // OUT:
{
   uint32 i;

   // search through a "reasonable" number of interfaces
   for (i = 0; i < 8; i++) {
      int erc;

      erc = CheckEthernet(i, (char *) hardwareID);

      if (erc == 0) {
         return 0;
      } else {
         if (erc != ENODEV) {
            Warning("%s unexpected failure: %d.\n", __FUNCTION__, erc);
            return erc;
         }
      }
   }

   *hardwareID = 0;

   return 0;
}
#else				// Not a specifically code OS
#include <unistd.h>


/*
 *----------------------------------------------------------------------
 *
 * ObtainHardwareID --
 *
 *      Locate the hardwareID for this machine.
 *
 * Results:
 *      0       No errors occured
 *      >0      failure (errno)
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      This is a dummy to catch an uncoded OS.
 *----------------------------------------------------------------------
 */

static int
ObtainHardwareID(uint64 *hardwareID)  // OUT:
{
   *hardwareID = gethostid();

   return 0;
}
#endif				// Not a specifically code OS


/*
 *----------------------------------------------------------------------
 *
 * HostNameHash --
 *
 *      Return the hash value of a string.
 *
 * Results:
 *      The hash value of the string.
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      This uses the DBJ2 hash algorithm.
 *
 *----------------------------------------------------------------------
 */

static uint32
HostNameHash(unsigned char *str) // IN:
{
   uint32 c;

   uint32 hash = 5381;

   while ((c = *str++) != '\0') {
      hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
   }

   return hash;
}

 
/*
 *----------------------------------------------------------------------
 *
 * Hostinfo_MachineID --
 *
 *      Return the machine ID information.
 *
 *      There are two pieces of identification returned: a hash of the
 *      hostname and and a hardware identifier. If either of them is
 *      unavailable they will be given a value of zero.
 *
 *      The hardware identifier should be extracted from a piece of hardware
 *      that has individual discrimination - each "part" is uniquely labelled.
 *      The MAC address of an ethernet and the WWN of a FibreChannel HBA are
 *      perfect examples.
 *
 * Results:
 *	The values are returned.
 *
 * Side effects:
 *      The hardware identifier should persistent across system reboots and
 *      resets as long of the hardware is available.
 *
 *      Currently the Windows implementation is used only for file locking -
 *      not system identification - so any acceptable piece of hardware
 *      may be used, even if it changes during a reboot.
 *
 *----------------------------------------------------------------------
 */

void
Hostinfo_MachineID(uint32 *hostNameHash,    // OUT:
                   uint64 *hostHardwareID)  // OUT:
{
   static Atomic_Ptr cachedHardwareID;
   static Atomic_Ptr cachedHostNameHash;
   uint64 *tmpHardwareID;
   uint32 *tmpNameHash;

   ASSERT(hostNameHash);
   ASSERT(hostHardwareID);

   tmpNameHash = Atomic_ReadPtr(&cachedHostNameHash);
   if (!tmpNameHash) {
      char *hostName;

      tmpNameHash = Util_SafeMalloc(sizeof *tmpNameHash);

      // 4 bytes (32 bits) of host name information
      hostName = (char *) Hostinfo_HostName();

      if (hostName == NULL) {
         Warning("%s Hostinfo_HostName failure; providing default.\n",
                __FUNCTION__);

         *tmpNameHash = 0;
      } else {
         *tmpNameHash = HostNameHash((unsigned char *) hostName);
         free(hostName);
      }

      if (Atomic_ReadIfEqualWritePtr(&cachedHostNameHash, NULL, 
                                     tmpNameHash)) {
         free(tmpNameHash);
         tmpNameHash = Atomic_ReadPtr(&cachedHostNameHash);
      }
   }
   *hostNameHash = *tmpNameHash;

   tmpHardwareID = Atomic_ReadPtr(&cachedHardwareID);
   if (!tmpHardwareID) {
      int  erc;

      tmpHardwareID = Util_SafeMalloc(sizeof *tmpHardwareID);

      // 8 bytes (64 bits) of hardware information
      erc = ObtainHardwareID(tmpHardwareID);
      if (erc != 0) {
         Warning("%s ObtainHardwareID failure (%s); providing default.\n",
                 __FUNCTION__, Err_Errno2String(erc));

         *tmpHardwareID = 0;
      }

      if (Atomic_ReadIfEqualWritePtr(&cachedHardwareID, NULL, 
                                     tmpHardwareID)) {
         free(tmpHardwareID);
         tmpHardwareID = Atomic_ReadPtr(&cachedHardwareID);
      }
   }
   *hostHardwareID = *tmpHardwareID;
}
 07070100000198000081A400000000000000000000000168225505000007FE000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/miscSolaris.c /*********************************************************
 * Copyright (C) 2005-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 *----------------------------------------------------------------------
 *
 * miscSolaris --
 *
 *      Implementation of new Linux functions for Solaris.
 *
 *----------------------------------------------------------------------
 */

#ifdef sun
#include "vmware.h"
#include <unistd.h>
#include <iso/stdlib_iso.h>
#include <fcntl.h>

/*
 *----------------------------------------------------------------------
 *
 * daemon --
 *
 *   Implementation of function daemon() for Solaris.
 *
 * Results:
 *   0 if successful, -1 if failed.
 *
 * Side effects:
 *   None
 *
 *----------------------------------------------------------------------
 */

int
daemon(int nochdir,  // IN:
       int noclose)  // IN:
{
   int fd;

   switch (fork()) {
   case -1:
      return (-1);
   case 0:
      break;
   default:
      /* It is parent, so exit here. */
      exit(0);
   }

   if (setsid() == -1) {
      return (-1);
   }

   if (!nochdir) {
      chdir("/");
   }

   if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
      dup2(fd, 0);
      dup2(fd, 1);
      dup2(fd, 2);
      if (fd > 2) {
         close (fd);
      }
   }

   return (0);
}
#endif
  07070100000199000081A4000000000000000000000001682255050000068E000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/posixDlopen.c /*********************************************************
 * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#define UNICODE_BUILDING_POSIX_WRAPPERS
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#if !defined(_WIN32)
#include <dlfcn.h>
#endif

#include "vmware.h"
#include "posixInt.h"


#if !defined(_WIN32)
/*
 *----------------------------------------------------------------------
 *
 * Posix_Dlopen --
 *
 *      POSIX dlopen()
 *
 * Results:
 *      NULL	Error
 *      !NULL	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

void *
Posix_Dlopen(const char *pathName,  // IN:
             int flag)              // IN:
{
   char *path;
   void *ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return NULL;
   }

   ret = dlopen(path, flag);

   Posix_Free(path);
   return ret;
}
#endif
  0707010000019A000081A400000000000000000000000168225505000016E3000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/posixInt.h    /*********************************************************
 * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * posixInt.h --
 *
 *	Internal definitions for the Posix wrapper module.
 */

#ifndef _POSIXINT_H_
#define _POSIXINT_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "posix.h"
#include "unicode.h"
#include "hashTable.h"
#include "vm_atomic.h"
#include "util.h"
#include "str.h"


#ifndef _WIN32 // {

/*
 *----------------------------------------------------------------------
 *
 * PosixConvertToCurrent --
 *
 *      Utility function to convert a UTF8 string to the current encoding.
 *
 * Results:
 *      TRUE on success.
 *      Conversion result in *out or NULL on failure.
 *      errno is untouched on success, set to UNICODE_CONVERSION_ERRNO
 *      on failure.
 *
 * Side effects:
 *      As described.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
PosixConvertToCurrent(const char *in,   // IN: string to convert
                      char **out)       // OUT: conversion result
{
   int e = errno;
   char *p = Unicode_GetAllocBytes(in, STRING_ENCODING_DEFAULT);
   Bool success = p != NULL || in == NULL;

   if (success) {
      errno = e;
      *out = p;
   } else {
      errno = UNICODE_CONVERSION_ERRNO;
      *out = NULL;
   }
   return success;
}


/*
 *----------------------------------------------------------------------
 *
 * PosixConvertToCurrentList --
 *
 *      Utility function to convert a list of UTF8 strings to the current
 *      encoding. Return NULL if list is NULL.
 *
 * Results:
 *      TRUE on success.
 *      Conversion result in *out or NULL on failure.
 *      errno is untouched on success, set to UNICODE_CONVERSION_ERRNO
 *      on failure.
 *
 * Side effects:
 *      As described.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
PosixConvertToCurrentList(char *const *in,  // IN: list to convert
                          char ***out)      // OUT: conversion result
{
   int e = errno;
   char **p;
   Bool success;

   if (in == NULL) {
      p = NULL;
      success = TRUE;
   } else {
      p = Unicode_GetAllocList(in, -1, STRING_ENCODING_DEFAULT);
      success = p != NULL;
   }

   if (!success) {
      e = UNICODE_CONVERSION_ERRNO;
   }
   *out = p;
   errno = e;
   return success;
}

#endif // }


/*
 * Hash table for Posix_Getenv
 */

typedef struct PosixEnvEntry {
   Atomic_Ptr value;
   Atomic_Ptr lastValue;
} PosixEnvEntry;

static INLINE void
PosixEnvFree(void *v)
{
   // never called
   NOT_REACHED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * PosixGetenvHash --
 *
 *      Save away UTF8 string for Posix_Getenv() to make it persistent.
 *
 * Results:
 *      The value.
 *
 * Side effects:
 *      The passed-in value string may be saved in a hash table,
 *      or it may be freed.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE_SINGLE_CALLER char *
PosixGetenvHash(const char *name,  // IN
                char *value)       // IN/OUT: may be freed
{
   static Atomic_Ptr htPtr;
   HashTable *ht;
   char *oldValue;
   PosixEnvEntry *e;

   /*
    * Don't need to save NULL.
    */

   if (value == NULL) {
      return value;
   }

   ht = HashTable_AllocOnce(&htPtr, 128,
                            HASH_FLAG_ATOMIC | HASH_FLAG_COPYKEY |
                               HASH_STRING_KEY,
                            PosixEnvFree);

   /*
    * We have to allow any number of concurrent getenv()'s.
    * So if the saved value is the same, don't change it,
    * and if it must change, all the threads together must
    * establish a single new value.
    *
    * On the other hand, we don't have to support concurrent
    * getenv() and setenv().
    */

   for (;;) {
      /*
       * If not in hash table, then insert and return.
       */

      if (!HashTable_Lookup(ht, name, (void **) &e)) {
         e = Util_SafeMalloc(sizeof *e);
         Atomic_WritePtr(&e->value, value);
         Atomic_WritePtr(&e->lastValue, NULL);
         if (!HashTable_Insert(ht, name, e)) {
            Posix_Free(e);
            continue;
         }
         break;
      }

      /*
       * If old value is the same, then use it.
       */

      oldValue = Atomic_ReadPtr(&e->value);
      if (Str_Strcmp(oldValue, value) == 0) {
         Posix_Free(value);
         value = oldValue;
         break;
      }

      /*
       * If value has changed, use new value, but don't free old
       * value yet because somebody else may be going through this
       * same code and still using it.  Because we don't need
       * to care about concurrent setenv(), a single lastValue
       * slot is sufficient.
       */

      if (Atomic_ReadIfEqualWritePtr(&e->value, oldValue, value) == oldValue) {
         oldValue = Atomic_ReadWritePtr(&e->lastValue, oldValue);
         Posix_Free(oldValue);
         break;
      }
   }

   return value;
}

#endif // ifndef _POSIXINT_H_
 0707010000019B000081A4000000000000000000000001682255050000AA7B000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/posixPosix.c  /*********************************************************
 * Copyright (C) 2008-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#define UNICODE_BUILDING_POSIX_WRAPPERS

#if __linux__
#define _GNU_SOURCE // Needed to get euidaccess()
#endif

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include "su.h"
#include <utime.h>
#include <sys/time.h>
#include <stdarg.h>

#if defined(__APPLE__)
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/kauth.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <CoreFoundation/CoreFoundation.h>
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include <spawn.h>
extern char **environ;
#endif
#elif defined(__FreeBSD__)
#include <sys/param.h>
#include <sys/mount.h>
#elif defined(sun)
#include <alloca.h>
#include <sys/mnttab.h>
#else
#include <sys/statfs.h>
#include <sys/mount.h>
#include <mntent.h>
#endif

#if (!defined(__FreeBSD__) || __FreeBSD_release >= 503001) && !defined __ANDROID__
#define VM_SYSTEM_HAS_GETPWNAM_R 1
#define VM_SYSTEM_HAS_GETPWUID_R 1
#define VM_SYSTEM_HAS_GETGRNAM_R 1
#endif

# if defined(__FreeBSD__)
#  include <sys/syslimits.h>  // PATH_MAX
# else
#  include <limits.h>  // PATH_MAX
# endif

#include "vmware.h"
#include "posixInt.h"
#if defined(sun)
#include "hashTable.h" // For setenv emulation
#endif

#include "vm_basic_defs.h"

#if defined __ANDROID__
/*
 * Android doesn't support getmntent_r() or setmntent().
 */
#define NO_GETMNTENT_R
#define NO_SETMNTENT

EXTERN int truncate(const char *, off_t);
#endif


/*
 *----------------------------------------------------------------------
 *
 * Posix_Open --
 *
 *      Open a file using POSIX open.
 *
 * Results:
 *      -1	error
 *      >= 0	success (file descriptor)
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Open(const char *pathName,  // IN:
           int flags,             // IN:
           ...)                   // IN:
{
   char *path;
   mode_t mode = 0;
   int fd;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   if ((flags & O_CREAT) != 0) {
      va_list a;

      /*
       * The FreeBSD tools compiler
       * (toolchain/lin32/gcc-4.1.2-5/bin/i686-freebsd5.0-gcc)
       * wants us to use va_arg(a, int) instead of va_arg(a, mode_t),
       * so oblige.  -- edward
       */

      va_start(a, flags);
      ASSERT_ON_COMPILE(sizeof (int) >= sizeof(mode_t));
      mode = va_arg(a, int);
      va_end(a);
   }

   fd = open(path, flags, mode);

   Posix_Free(path);

   return fd;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Creat --
 *
 *      Create a file via POSIX creat()
 *
 * Results:
 *      -1	Error
 *      >= 0	File descriptor (success)
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Creat(const char *pathName,  // IN:
            mode_t mode)           // IN:
{
   return Posix_Open(pathName, O_CREAT | O_WRONLY | O_TRUNC, mode);
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Fopen --
 *
 *      Open a file via POSIX fopen()
 *
 * Results:
 *      A file pointer, or NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

FILE *
Posix_Fopen(const char *pathName,  // IN:
            const char *mode)      // IN:
{
   char *path;
   FILE *stream;

   ASSERT(mode);

   if (!PosixConvertToCurrent(pathName, &path)) {
      return NULL;
   }

   stream = fopen(path, mode);

   Posix_Free(path);

   return stream;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Stat --
 *
 *      POSIX stat()
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Stat(const char *pathName,  // IN:
           struct stat *statbuf)  // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = stat(path, statbuf);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Chmod --
 *
 *      POSIX chmod()
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Chmod(const char *pathName,  // IN:
            mode_t mode)           // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = chmod(path, mode);

   Posix_Free(path);
   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Rename --
 *
 *      POSIX rename().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Rename(const char *fromPathName,  // IN:
             const char *toPathName)    // IN:
{
   char *toPath;
   char *fromPath;
   int result;

   if (!PosixConvertToCurrent(fromPathName, &fromPath)) {
      return -1;
   }
   if (!PosixConvertToCurrent(toPathName, &toPath)) {
      Posix_Free(fromPath);
      return -1;
   }

   result = rename(fromPath, toPath);

   Posix_Free(toPath);
   Posix_Free(fromPath);
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Unlink --
 *
 *      POSIX unlink().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Unlink(const char *pathName)  // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = unlink(path);

   Posix_Free(path);
   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Rmdir --
 *
 *      POSIX rmdir().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Rmdir(const char *pathName)  // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = rmdir(path);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Freopen --
 *
 *      Open a file via POSIX freopen()
 *
 * Results:
 *      -1	Error
 *      >= 0	File descriptor (success)
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

FILE *
Posix_Freopen(const char *pathName,  // IN:
              const char *mode,      // IN:
              FILE *input_stream)    // IN:
{
   char *path;
   FILE *stream;

   ASSERT(mode);

   if (!PosixConvertToCurrent(pathName, &path)) {
      return NULL;
   }

   stream = freopen(path, mode, input_stream);

   Posix_Free(path);
   return stream;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Access --
 *
 *      POSIX access().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Access(const char *pathName,  // IN:
             int mode)              // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

#if defined(VMX86_SERVER)
   /*
    * ESX can return EINTR making retries a necessity. This is a bug in
    * ESX that is being worked around here - POSIX says access cannot return
    * EINTR.
    */

   do {
      ret = access(path, mode);
   } while ((ret == -1) && (errno == EINTR));
#else
   ret = access(path, mode);
#endif

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_EuidAccess --
 *
 *      POSIX euidaccess().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_EuidAccess(const char *pathName,  // IN:
                 int mode)              // IN:
{
#if defined(__GLIBC__)
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = euidaccess(path, mode);

   Posix_Free(path);
   return ret;
#else
   errno = ENOSYS;
   return -1;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Utime --
 *
 *      POSIX utime().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Utime(const char *pathName,         // IN:
            const struct utimbuf *times)  // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = utime(path, times);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Perror --
 *
 *      POSIX perror()
 *
 * Results:
 *      Appends error message corresponding to errno to the passed in string,
 *      and puts the result on stderr.
 *
 * Side effects:
 *      Message printed to stderr.
 *
 *----------------------------------------------------------------------
 */

void
Posix_Perror(const char *str)  // IN:
{
   char *tmpstr = Unicode_GetAllocBytes(str, STRING_ENCODING_DEFAULT);

   // ignore conversion error silently
   perror(tmpstr);

   Posix_Free(tmpstr);
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Pathconf --
 *
 *      POSIX pathconf()
 *
 * Results:
 *      Returns the limit, -1 if limit doesn't exist or on error
 *
 * Side effects:
 *      errno is set on error.
 *
 *----------------------------------------------------------------------
 */

long
Posix_Pathconf(const char *pathName,  // IN:
               int name)              // IN:
{
   char *path;
   long ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = pathconf(path, name);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Popen --
 *
 *      Open a file using POSIX popen().
 *
 * Results:
 *      Returns a non-NULL FILE* on success, NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

FILE *
Posix_Popen(const char *pathName,  // IN:
            const char *mode)      // IN:
{
   char *path;
   FILE *stream;

   ASSERT(mode);

   if (!PosixConvertToCurrent(pathName, &path)) {
      return NULL;
   }

   stream = popen(path, mode);

   Posix_Free(path);

   return stream;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Mknod --
 *
 *      POSIX mknod().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Mknod(const char *pathName,  // IN:
            mode_t mode,           // IN:
            dev_t dev)             // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = mknod(path, mode, dev);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Chown --
 *
 *      POSIX chown().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Chown(const char *pathName,  // IN:
            uid_t owner,           // IN:
            gid_t group)           // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = chown(path, owner, group);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Lchown --
 *
 *      POSIX lchown().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Lchown(const char *pathName,  // IN:
             uid_t owner,           // IN:
             gid_t group)           // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = lchown(path, owner, group);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Link --
 *
 *      POSIX link().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Link(const char *uOldPath,  // IN:
           const char *uNewPath)  // IN:
{
   char *oldPath;
   char *newPath;
   int ret;

   if (!PosixConvertToCurrent(uOldPath, &oldPath)) {
      return -1;
   }
   if (!PosixConvertToCurrent(uNewPath, &newPath)) {
      Posix_Free(oldPath);
      return -1;
   }

   ret = link(oldPath, newPath);

   Posix_Free(oldPath);
   Posix_Free(newPath);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Symlink --
 *
 *      POSIX symlink().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Symlink(const char *uOldPath,  // IN:
              const char *uNewPath)  // IN:
{
   char *oldPath;
   char *newPath;
   int ret;

   if (!PosixConvertToCurrent(uOldPath, &oldPath)) {
      return -1;
   }
   if (!PosixConvertToCurrent(uNewPath, &newPath)) {
      Posix_Free(oldPath);
      return -1;
   }

   ret = symlink(oldPath, newPath);

   Posix_Free(oldPath);
   Posix_Free(newPath);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Mkfifo --
 *
 *      POSIX mkfifo().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Mkfifo(const char *pathName,  // IN:
             mode_t mode)           // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = mkfifo(path, mode);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Truncate --
 *
 *      POSIX truncate().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Truncate(const char *pathName,  // IN:
               off_t length)          // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = truncate(path, length);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Utimes --
 *
 *      POSIX utimes().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Utimes(const char *pathName,         // IN:
             const struct timeval *times)  // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = utimes(path, times);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Execl --
 *
 *      POSIX execl().
 *
 * Results:
 *      -1      Error
 *      0       Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Execl(const char *pathName,  // IN:
            const char *arg0,      // IN:
            ...)                   // IN:
{
   int ret = -1;
   char *path;
   va_list vl;
   char **argv = NULL;
   int i, count = 0;

   if (!PosixConvertToCurrent(pathName, &path)) {
      goto exit;
   }

   if (arg0) {
      count = 1;
      va_start(vl, arg0);
      while (va_arg(vl, char *)) {
         count ++;
      }
      va_end(vl);
   }

   argv = (char **) malloc(sizeof(char *) * (count + 1));
   if (argv == NULL) {
      errno = ENOMEM;
      goto exit;
   }
   if (argv) {
      errno = 0;
      if (count > 0) {
         if (!PosixConvertToCurrent(arg0, &argv[0])) {
            goto exit;
         }
         va_start(vl, arg0);
         for (i = 1; i < count; i++) {
            if (!PosixConvertToCurrent(va_arg(vl, char *), &argv[i])) {
               va_end(vl);
               goto exit;
            }
         }
         va_end(vl);
      }
      argv[count] = NULL;
      if (errno != 0) {
         goto exit;
      }
   }

   ret = execv(path, argv);

exit:
   Util_FreeStringList(argv, -1);
   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Execlp --
 *
 *      POSIX execlp().
 *
 * Results:
 *      -1      Error
 *      0       Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Execlp(const char *fileName,  // IN:
             const char *arg0,      // IN:
             ...)                   // IN:
{
   int ret = -1;
   char *file;
   va_list vl;
   char **argv = NULL;
   int i, count = 0;

   if (!PosixConvertToCurrent(fileName, &file)) {
      goto exit;
   }

   if (arg0) {
      count = 1;
      va_start(vl, arg0);
      while (va_arg(vl, char *)) {
         count ++;
      }
      va_end(vl);
   }

   argv = (char **) malloc(sizeof(char *) * (count + 1));
   if (argv == NULL) {
      errno = ENOMEM;
      goto exit;
   }
   if (argv) {
      errno = 0;
      if (count > 0) {
         if (!PosixConvertToCurrent(arg0, &argv[0])) {
            goto exit;
         }
         va_start(vl, arg0);
         for (i = 1; i < count; i++) {
            if (!PosixConvertToCurrent(va_arg(vl, char *), &argv[i])) {
               va_end(vl);
               goto exit;
            }
         }
         va_end(vl);
      }
      argv[count] = NULL;
      if (errno != 0) {
         goto exit;
      }
   }

   ret = execvp(file, argv);

exit:
   Util_FreeStringList(argv, -1);
   Posix_Free(file);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Execv --
 *
 *      POSIX execv().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Execv(const char *pathName,  // IN:
            char *const argVal[])  // IN:
{
   int ret = -1;
   char *path;
   char **argv = NULL;

   if (!PosixConvertToCurrent(pathName, &path)) {
      goto exit;
   }
   if (!PosixConvertToCurrentList(argVal, &argv)) {
      goto exit;
   }

   ret = execv(path, argv);

exit:
   Util_FreeStringList(argv, -1);
   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Execve --
 *
 *      POSIX execve().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Execve(const char *pathName,  // IN:
             char *const argVal[],  // IN:
             char *const envPtr[])  // IN:
{
   int ret = -1;
   char *path;
   char **argv = NULL;
   char **envp = NULL;

   if (!PosixConvertToCurrent(pathName, &path)) {
      goto exit;
   }
   if (!PosixConvertToCurrentList(argVal, &argv)) {
      goto exit;
   }
   if (!PosixConvertToCurrentList(envPtr, &envp)) {
      goto exit;
   }

   ret = execve(path, argv, envp);

exit:
   Util_FreeStringList(argv, -1);
   Util_FreeStringList(envp, -1);
   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Execvp --
 *
 *      POSIX execvp().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Execvp(const char *fileName,   // IN:
             char *const argVal[])  // IN:
{
   int ret = -1;
   char *file;
   char **argv = NULL;

   if (!PosixConvertToCurrent(fileName, &file)) {
      goto exit;
   }
   if (!PosixConvertToCurrentList(argVal, &argv)) {
      goto exit;
   }

   ret = execvp(file, argv);

exit:
   Util_FreeStringList(argv, -1);
   Posix_Free(file);

   return ret;
}


#if TARGET_OS_IPHONE

/*
 *----------------------------------------------------------------------
 *
 * Posix_SplitCommands --
 *
 *      Split the commands into arguments.
 *
 * Results:
 *      The length of the commands and the arguments.
 *
 * Side effects:
 *      NO
 *
 *----------------------------------------------------------------------
 */

int
Posix_SplitCommands(char *command,  // IN
                    char **argv)    // OUT
{
   char *token;
   char *savePtr;
   char *str;
   int count;
   for (count = 0, str = command; ; str = NULL, count++) {
      token = strtok_r(str, " ", &savePtr);
      if (token == NULL) {
         break;
      }
      if (argv != NULL) {
         *argv = token;
         argv++;
      }
   }
   return count;
}

#endif


/*
 *----------------------------------------------------------------------
 *
 * Posix_System --
 *
 *      POSIX system()
 *
 * Results:
 *      Returns the status of command, or -1 on failure.
 *
 * Side effects:
 *      errno is set on error.
 *
 *----------------------------------------------------------------------
 */

int
Posix_System(const char *command)  // IN:
{
   char *tmpcommand;
   int ret;

   if (!PosixConvertToCurrent(command, &tmpcommand)) {
      return -1;
   }

#if TARGET_OS_IPHONE
   pid_t pid;
   int count;
   char **argv;
   char *commandCopy = strdup(command);
   count = Posix_SplitCommands(commandCopy, NULL);
   free(commandCopy);
   if (!count) {
      return -1;
   }
   argv = malloc(count * sizeof(char *));
   commandCopy = strdup(command);
   Posix_SplitCommands(commandCopy, argv);
   ret = posix_spawn(&pid, argv[0], NULL, NULL, argv, environ);
   free(argv);
   free(commandCopy);
#else
   ret = system(tmpcommand);
#endif

   Posix_Free(tmpcommand);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Mkdir --
 *
 *      POSIX mkdir().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Mkdir(const char *pathName,  // IN:
            mode_t mode)           // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = mkdir(path, mode);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Chdir --
 *
 *      POSIX chdir().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Chdir(const char *pathName)  // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = chdir(path);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_RealPath --
 *
 *      POSIX realpath().
 *
 * Results:
 *      NULL	Error
 *      !NULL	Success (result must be freed by the caller)
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

char *
Posix_RealPath(const char *pathName)  // IN:
{
   char *path;
   char rpath[PATH_MAX];
   char *p;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return NULL;
   }

   p = realpath(path, rpath);

   Posix_Free(path);

   return p == NULL ? NULL : Unicode_Alloc(rpath, STRING_ENCODING_DEFAULT);
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_ReadLink --
 *
 *      POSIX readlink().
 *
 * Results:
 *      NULL	Error
 *      !NULL	Success (result must be freed by the caller)
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

char *
Posix_ReadLink(const char *pathName)  // IN:
{
   char *path = NULL;
   char *result = NULL;

   if (PosixConvertToCurrent(pathName, &path)) {
      size_t size = 2 * 1024;

      while (TRUE) {
         char *linkPath = Util_SafeMalloc(size);
         ssize_t len = readlink(path, linkPath, size);

         if (len == -1) {
            Posix_Free(linkPath);
            break;
         }

         if (len < size) {
            linkPath[len] = '\0'; // Add the missing NUL to path
            result = Unicode_Alloc(linkPath, STRING_ENCODING_DEFAULT);
            Posix_Free(linkPath);
            break;
         }
         Posix_Free(linkPath);

         size += 1024;
      }
   }

   Posix_Free(path);

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Lstat --
 *
 *      POSIX lstat()
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Lstat(const char *pathName,  // IN:
            struct stat *statbuf)  // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = lstat(path, statbuf);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_OpenDir --
 *
 *      POSIX opendir()
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

DIR *
Posix_OpenDir(const char *pathName)  // IN:
{
   char *path;
   DIR *ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return NULL;
   }

   ret = opendir(path);

   Posix_Free(path);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getenv --
 *
 *      POSIX getenv().
 *
 * Results:
 *      NULL    The name was not found or an error occurred
 *      !NULL   The value associated with the name in UTF8. This does not
 *              need to be freed.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

char *
Posix_Getenv(const char *name)  // IN:
{
   char *rawName;
   char *rawValue;

   if (!PosixConvertToCurrent(name, &rawName)) {
      return NULL;
   }
   rawValue = getenv(rawName);
   Posix_Free(rawName);

   if (rawValue == NULL) {
      return NULL;
   }

   return PosixGetenvHash(name, Unicode_Alloc(rawValue,
                                              STRING_ENCODING_DEFAULT));
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Putenv --
 *
 *      POSIX putenv().  This wrapper will only assert the string is ASCII.
 *                       putenv() should not be used.
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      Environment may be changed.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Putenv(char *name)  // IN:
{
   ASSERT(Unicode_IsBufferValid(name, -1, STRING_ENCODING_US_ASCII));

   return putenv(name);
}


#if !defined(sun) // {

/*
 *----------------------------------------------------------------------
 *
 * Posix_Statfs --
 *
 *      POSIX statfs()
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

int
Posix_Statfs(const char *pathName,      // IN:
             struct statfs *statfsbuf)  // IN:
{
   char *path;
   int ret;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return -1;
   }

   ret = statfs(path, statfsbuf);

   Posix_Free(path);

   return ret;
}
#endif // } !defined(sun)


/*
 *----------------------------------------------------------------------
 *
 * Posix_Setenv --
 *
 *      POSIX setenv().
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      Environment may be changed.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Setenv(const char *name,   // IN:
             const char *value,  // IN:
             int overWrite)      // IN:
{
   int ret = -1;
   char *rawName = NULL;
   char *rawValue = NULL;

   if (!PosixConvertToCurrent(name, &rawName)) {
      goto exit;
   }
   if (!PosixConvertToCurrent(value, &rawValue)) {
      goto exit;
   }

#if defined(sun)
   if (overWrite || !getenv(rawName)) {
      static HashTable *trackEnv = NULL; // Tracks values to avoid leaks.
      char *keyStr;
      char *fullStr;
      int fslen;
      int rawNameLen;
      int rawValueLen;

      if (!trackEnv) {
         trackEnv = HashTable_Alloc(16, HASH_STRING_KEY, free);
      }

      /*
       * In order to keep memory management and hash table manipulation simple,
       * each env var is stored as a memory block containing the NUL-terminated
       * environment variable name, followed immediately in memory by the
       * full argument to putenv ('varname=value').
       */

      rawNameLen = strlen(rawName) + 1;
      rawValueLen = strlen(rawValue) + 1;
      fslen = rawNameLen + rawValueLen + 1; // 1 is for '=' sign
      keyStr = malloc(rawNameLen + fslen);
      fullStr = keyStr + rawNameLen;

      /*
       * Use memcpy because Str_Snprintf() doesn't play well with non-UTF8
       * strings.
       */

      memcpy(keyStr, rawName, rawNameLen);
      memcpy(fullStr, rawName, rawNameLen);
      fullStr[rawNameLen - 1] = '=';
      memcpy(fullStr + rawNameLen, rawValue, rawValueLen);

      ret = putenv(fullStr);
      HashTable_Insert(trackEnv, keyStr, keyStr); // Any old value will be freed
   } else {
      ret = 0;
   }
#else
   ret = setenv(rawName, rawValue, overWrite);
#endif

exit:
   Posix_Free(rawName);
   Posix_Free(rawValue);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Unsetenv --
 *
 *      POSIX unsetenv().
 *
 * Results:
 *      0 on success. -1 on error.
 *
 * Side effects:
 *      Environment may be changed.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Unsetenv(const char *name)  // IN:
{
   char *rawName;
   int ret;

   if (!PosixConvertToCurrent(name, &rawName)) {
      return -1;
   }

#if defined(sun)
   ret = putenv(rawName);
#elif defined(__FreeBSD__)
   /*
    * Our tools build appears to use an old enough libc version that returns
    * void.
    */
   unsetenv(rawName);
   ret = 0;
#else
   ret = unsetenv(rawName);
#endif
   Posix_Free(rawName);

   return ret;
}


#if !defined(sun) // {

#if !defined(__APPLE__) && !defined(__FreeBSD__) // {
/*
 *----------------------------------------------------------------------
 *
 * Posix_Mount --
 *
 *      POSIX mount()
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error. On success, filesystem is mounted.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Mount(const char *source,          // IN:
            const char *target,          // IN:
            const char *filesystemtype,  // IN:
            unsigned long mountflags,    // IN:
            const void *data)            // IN:
{
   int ret = -1;
   char *tmpsource = NULL;
   char *tmptarget = NULL;

   if (!PosixConvertToCurrent(source, &tmpsource)) {
      goto exit;
   }
   if (!PosixConvertToCurrent(target, &tmptarget)) {
      goto exit;
   }

   ret = mount(tmpsource, tmptarget, filesystemtype, mountflags, data);

exit:
   Posix_Free(tmpsource);
   Posix_Free(tmptarget);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Umount --
 *
 *      POSIX umount()
 *
 * Results:
 *      -1	Error
 *      0	Success
 *
 * Side effects:
 *      errno is set on error. On success, filesystem is unmounted.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Umount(const char *target)  // IN:
{
   char *tmptarget;
   int ret;

   if (!PosixConvertToCurrent(target, &tmptarget)) {
      return -1;
   }

   ret = umount(tmptarget);

   Posix_Free(tmptarget);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Setmntent --
 *
 *      Open a file via POSIX setmntent()
 *
 * Results:
 *      NULL	Error
 *      !NULL	File stream
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

FILE *
Posix_Setmntent(const char *pathName,  // IN:
                const char *mode)      // IN:
{
#if defined NO_SETMNTENT
   NOT_IMPLEMENTED();
   errno = ENOSYS;
   return NULL;
#else
   char *path;
   FILE *stream;

   ASSERT(mode != NULL);

   if (!PosixConvertToCurrent(pathName, &path)) {
      return NULL;
   }
   stream = setmntent(path, mode);
   Posix_Free(path);

   return stream;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getmntent --
 *
 *      POSIX getmntent()
 *
 * Results:
 *      Pointer to updated mntent struct or NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

struct mntent *
Posix_Getmntent(FILE *fp)  // IN:
{
   int ret;
   struct mntent *m;
   static struct mntent sm = {0};

   m = getmntent(fp);
   if (!m) {
      return NULL;
   }

   /* Free static structure string pointers before reuse. */
   Posix_Free(sm.mnt_fsname);
   sm.mnt_fsname = NULL;
   Posix_Free(sm.mnt_dir);
   sm.mnt_dir = NULL;
   Posix_Free(sm.mnt_type);
   sm.mnt_type = NULL;
   Posix_Free(sm.mnt_opts);
   sm.mnt_opts = NULL;

   /* Fill out structure with new values. */
   sm.mnt_freq = m->mnt_freq;
   sm.mnt_passno = m->mnt_passno;

   ret = ENOMEM;
   if (m->mnt_fsname &&
       (sm.mnt_fsname = Unicode_Alloc(m->mnt_fsname,
                                      STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (m->mnt_dir &&
       (sm.mnt_dir = Unicode_Alloc(m->mnt_dir,
                                   STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (m->mnt_type &&
       (sm.mnt_type = Unicode_Alloc(m->mnt_type,
                                    STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (m->mnt_opts &&
       (sm.mnt_opts = Unicode_Alloc(m->mnt_opts,
                                    STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   ret = 0;

exit:
   if (ret != 0) {
      errno = ret;
      return NULL;
   }

   return &sm;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getmntent_r --
 *
 *      POSIX getmntent_r()
 *
 * Results:
 *      Pointer to updated mntent struct or NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

struct mntent *
Posix_Getmntent_r(FILE *fp,          // IN:
                  struct mntent *m,  // IN:
                  char *buf,         // IN:
                  int size)          // IN:
{
#if defined NO_GETMNTENT_R
   NOT_IMPLEMENTED();
   errno = ENOSYS;
   return NULL;
#else
   int ret;
   char *fsname = NULL;
   char *dir = NULL;
   char *type = NULL;
   char *opts = NULL;
   size_t n;

   if (!getmntent_r(fp, m, buf, size)) {
      return NULL;
   }

   /*
    * Convert strings to UTF-8
    */

   ret = ENOMEM;
   if (m->mnt_fsname &&
       (fsname = Unicode_Alloc(m->mnt_fsname,
                               STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (m->mnt_dir &&
       (dir = Unicode_Alloc(m->mnt_dir,
                            STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (m->mnt_type &&
       (type = Unicode_Alloc(m->mnt_type,
                             STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (m->mnt_opts &&
       (opts = Unicode_Alloc(m->mnt_opts, STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }

   /*
    * Put UTF-8 strings into the structure.
    */

   ret = ERANGE;
   n = 0;

   if (fsname) {
      size_t len = strlen(fsname) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      m->mnt_fsname = memcpy(buf + n, fsname, len);
      n += len;
   }

   if (dir != NULL) {
      size_t len = strlen(dir) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      m->mnt_dir = memcpy(buf + n, dir, len);
      n += len;
   }

   if (type) {
      size_t len = strlen(type) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      m->mnt_type = memcpy(buf + n, type, len);
      n += len;
   }

   if (opts) {
      size_t len = strlen(opts) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      m->mnt_opts = memcpy(buf + n, opts, len);
   }
   ret = 0;

exit:

   Posix_Free(fsname);
   Posix_Free(dir);
   Posix_Free(type);
   Posix_Free(opts);

   if (ret != 0) {
      errno = ret;

      return NULL;
   }

   return m;
#endif // NO_GETMNTENT_R
}


/*
 *----------------------------------------------------------------------------
 *
 * Posix_Printf --
 *
 *      POSIX printf.
 *
 * Returns:
 *      Returns the number of characters printed out or a negative value on
 *      failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
Posix_Printf(const char *format,  // IN:
             ...)                 // IN:
{
   va_list args;
   char *output;
   char *outCurr;
   int numChars;

   va_start(args, format);
   output = Str_Vasprintf(NULL, format, args);
   va_end(args);

   if (!PosixConvertToCurrent(output, &outCurr)) {
      return -1;
   }
   numChars = printf("%s", outCurr);

   Posix_Free(output);
   Posix_Free(outCurr);

   return numChars;
}


/*
 *----------------------------------------------------------------------------
 *
 * Posix_Fprintf --
 *
 *      POSIX fprintf.
 *
 * Returns:
 *      Returns the number of characters printed out or a negative value on
 *      failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

int
Posix_Fprintf(FILE *stream,        // IN:
              const char *format,  // IN:
              ...)                 // IN:
{
   va_list args;
   char *output;
   char *outCurr;
   int nOutput;

   va_start(args, format);
   output = Str_Vasprintf(NULL, format, args);
   va_end(args);

   if (!PosixConvertToCurrent(output, &outCurr)) {
      return -1;
   }
   nOutput = fprintf(stream, "%s", outCurr);

   Posix_Free(output);
   Posix_Free(outCurr);

   return nOutput;
}


#endif // } !defined(__APPLE__) && !defined(__FreeBSD)


#else  // } !defined(sun) {
/*
 *----------------------------------------------------------------------
 *
 * Posix_Getmntent --
 *
 *      POSIX getmntent() for Solaris
 *
 * Results:
 *      -1  EOF
 *      0   Success
 *      >0  Error
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Getmntent(FILE *fp,           // IN:
                struct mnttab *mp)  // IN:
{
   int ret;
   static struct mnttab m = {0};

   ret = getmntent(fp, mp);
   if (ret == 0) {
      Posix_Free(m.mnt_special);
      Posix_Free(m.mnt_mountp);
      Posix_Free(m.mnt_fstype);
      Posix_Free(m.mnt_mntopts);
      Posix_Free(m.mnt_time);
      m.mnt_special = Unicode_Alloc(mp->mnt_special, STRING_ENCODING_DEFAULT);
      m.mnt_mountp = Unicode_Alloc(mp->mnt_mountp, STRING_ENCODING_DEFAULT);
      m.mnt_fstype = Unicode_Alloc(mp->mnt_fstype, STRING_ENCODING_DEFAULT);
      m.mnt_mntopts = Unicode_Alloc(mp->mnt_mntopts, STRING_ENCODING_DEFAULT);
      m.mnt_time = Unicode_Alloc(mp->mnt_time, STRING_ENCODING_DEFAULT);
      mp->mnt_special = m.mnt_special;
      mp->mnt_mountp = m.mnt_mountp;
      mp->mnt_fstype = m.mnt_fstype;
      mp->mnt_mntopts = m.mnt_mntopts;
      mp->mnt_time = m.mnt_time;
   }

   return ret;
}
#endif // } !defined(sun)


/*
 *----------------------------------------------------------------------
 *
 * Posix_MkTemp --
 *
 *      POSIX mktemp().  It is implemented via mkstemp() to avoid
 *      warning about using dangerous mktemp() API - but note that
 *      it suffers from all mktemp() problems - caller has to use
 *      O_EXCL when creating file, and retry if file already exists.
 *
 * Results:
 *      NULL    Error
 *      !NULL   Success (result must be freed by the caller)
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

char *
Posix_MkTemp(const char *pathName)  // IN:
{
   char *result = NULL;
   char *path;
   int fd;

   if (!PosixConvertToCurrent(pathName, &path)) {
      return NULL;
   }
   fd = mkstemp(path);
   if (fd >= 0) {
      close(fd);
      unlink(path);
      result = Unicode_Alloc(path, STRING_ENCODING_DEFAULT);
   }
   Posix_Free(path);
   return result;
}
 0707010000019C000081A400000000000000000000000168225505000062A0000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/posixPwd.c    /*********************************************************
 * Copyright (C) 2008-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#if !defined(_POSIX_PTHREAD_SEMANTICS) && defined(sun)
#define _POSIX_PTHREAD_SEMANTICS 1 // Needed to get POSIX-correct getpw*_r() on Solaris
#endif

#define UNICODE_BUILDING_POSIX_WRAPPERS

#include "vmware.h"
#include "posixInt.h"

#include <pwd.h>
#include <grp.h>

#if (!defined(__FreeBSD__) || __FreeBSD_release >= 503001) && !defined __ANDROID__
#define VM_SYSTEM_HAS_GETPWNAM_R 1
#define VM_SYSTEM_HAS_GETPWUID_R 1
#define VM_SYSTEM_HAS_GETGRNAM_R 1
#endif

static struct passwd *GetpwInternal(struct passwd *pw);
static int GetpwInternal_r(struct passwd *pw, char *buf, size_t size,
                           struct passwd **ppw);

#if defined __ANDROID__
/*
 * Android doesn't support getpwent().
 */
#define NO_GETPWENT
#endif


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getpwnam --
 *
 *      POSIX getpwnam()
 *
 * Results:
 *      Pointer to updated passwd struct on NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

struct passwd *
Posix_Getpwnam(const char *name)  // IN:
{
   struct passwd *pw;
   char *tmpname;

   if (!PosixConvertToCurrent(name, &tmpname)) {
      return NULL;
   }
   pw = getpwnam(tmpname);
   Posix_Free(tmpname);

   return GetpwInternal(pw);
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getpwuid --
 *
 *      POSIX getpwuid()
 *
 * Results:
 *      Pointer to updated passwd struct on NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

struct passwd *
Posix_Getpwuid(uid_t uid)  // IN:
{
   struct passwd *pw = getpwuid(uid);

   return GetpwInternal(pw);
}


/*
 *----------------------------------------------------------------------
 *
 * GetpwInternal --
 *
 *      Helper function for Posix_Getpwnam, Posix_Getpwuid and Posix_Getpwent
 *
 * Results:
 *      Pointer to updated passwd struct on NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

static struct passwd *
GetpwInternal(struct passwd *pw)  // IN:
{
   int ret;
   static struct passwd spw = {0};

   if (!pw) {
      return NULL;
   }

   /* Free static structure string pointers before reuse. */
   Posix_Free(spw.pw_passwd);
   spw.pw_passwd = NULL;
   Posix_Free(spw.pw_dir);
   spw.pw_dir = NULL;
   Posix_Free(spw.pw_name);
   spw.pw_name = NULL;
#if !defined __ANDROID__
   Posix_Free(spw.pw_gecos);
   spw.pw_gecos = NULL;
#endif
   Posix_Free(spw.pw_shell);
   spw.pw_shell = NULL;
#if defined(__FreeBSD__)
   Posix_Free(spw.pw_class);
   spw.pw_class = NULL;
#endif

   /* Fill out structure with new values. */
   spw.pw_uid = pw->pw_uid;
   spw.pw_gid = pw->pw_gid;
#if defined(__FreeBSD__)
   spw.pw_change = pw->pw_change;
   spw.pw_expire = pw->pw_expire;
   spw.pw_fields = pw->pw_fields;
#endif

#if !defined(sun)
   ret = ENOMEM;
#else
   ret = EIO;
#endif
   if (pw->pw_passwd &&
       (spw.pw_passwd = Unicode_Alloc(pw->pw_passwd,
                                      STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (pw->pw_dir &&
       (spw.pw_dir = Unicode_Alloc(pw->pw_dir,
                                   STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (pw->pw_name &&
       (spw.pw_name = Unicode_Alloc(pw->pw_name,
                                    STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
#if !defined __ANDROID__
   if (pw->pw_gecos &&
       (spw.pw_gecos = Unicode_Alloc(pw->pw_gecos,
                                     STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
#endif
   if (pw->pw_shell &&
       (spw.pw_shell = Unicode_Alloc(pw->pw_shell,
                                     STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
#if defined(__FreeBSD__)
   if (pw->pw_class &&
       (spw.pw_class = Unicode_Alloc(pw->pw_class,
                                     STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
#endif
   ret = 0;

exit:
   if (ret != 0) {
      errno = ret;
      return NULL;
   }
   return &spw;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getpwent --
 *
 *      POSIX getpwent()
 *
 * Results:
 *      Pointer to updated passwd struct or NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

struct passwd *
Posix_Getpwent(void)
{
#if defined NO_GETPWENT
   NOT_IMPLEMENTED();
   errno = ENOSYS;
   return NULL;
#else
   struct passwd *pw = getpwent();

   return GetpwInternal(pw);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Endpwent --
 *
 *      POSIX endpwent()
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Posix_Endpwent(void)
{
#if defined(__ANDROID__)
   /*
    * endpwent() not avail until Android O
    * TODO: When Android O(Oreo) becomes mainstream, we can remove this #if
    * Refer https://github.com/android-ndk/ndk/issues/77
    */
   return;
#else
   endpwent();
#endif
}


#if !defined(VM_SYSTEM_HAS_GETPWNAM_R) || \
   !defined(VM_SYSTEM_HAS_GETPWUID_R) || \
   !defined(VM_SYSTEM_HAS_GETGRNAM_R) // {
/*
 *-----------------------------------------------------------------------------
 *
 * CopyFieldIntoBuf --
 *
 *      Copies a field in a passwd/group structure into the supplied buffer,
 *      and sets that pointer into dest. Used as a helper function for the
 *      EmulateGet* routines.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      Updates *buf and *bufLen to allocate space for the copied field.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CopyFieldIntoBuf(const char *src,  // IN:
                 char **dest,      // OUT:
                 char **buf,       // OUT
                 size_t *bufLen)   // OUT:
{
   if (src) {
      size_t needLen = strlen(src) + 1;

      if (*bufLen < needLen) {
         return FALSE;
      }

      *dest = *buf;
      memcpy(*dest, src, needLen);
      *buf += needLen;
      *bufLen -= needLen;
   } else {
      *dest = NULL;
   }

   return TRUE;
}


#endif // }


#if !defined(VM_SYSTEM_HAS_GETPWNAM_R) || !defined(VM_SYSTEM_HAS_GETPWUID_R) // {
/*
 *-----------------------------------------------------------------------------
 *
 * PasswdCopy --
 *
 *      Copies a password structure as part of emulating the getpw*_r routines.
 *
 * Results:
 *      'new' if successful, NULL otherwise.
 *
 * Side effects:
 *      Modifies 'buf'
 *
 *-----------------------------------------------------------------------------
 */

static struct passwd *
PasswdCopy(struct passwd *orig, // IN
           struct passwd *new,  // IN/OUT
           char *buf,           // IN
           size_t bufLen)       // IN
{
   if (!orig) {
      return NULL;
   }

   *new = *orig;

   if (!CopyFieldIntoBuf(orig->pw_name, &new->pw_name, &buf, &bufLen)) {
      return NULL;
   }
   if (!CopyFieldIntoBuf(orig->pw_passwd, &new->pw_passwd, &buf, &bufLen)) {
      return NULL;
   }
#if !defined __ANDROID__
   if (!CopyFieldIntoBuf(orig->pw_gecos, &new->pw_gecos, &buf, &bufLen)) {
      return NULL;
   }
#endif
   if (!CopyFieldIntoBuf(orig->pw_dir, &new->pw_dir, &buf, &bufLen)) {
      return NULL;
   }
   if (!CopyFieldIntoBuf(orig->pw_shell, &new->pw_shell, &buf, &bufLen)) {
      return NULL;
   }
#ifdef __FreeBSD__
   if (!CopyFieldIntoBuf(orig->pw_class, &new->pw_class, &buf, &bufLen)) {
      return NULL;
   }
#endif

   return new;
}
#endif // }


#ifndef VM_SYSTEM_HAS_GETPWNAM_R // {
/*
 *-----------------------------------------------------------------------------
 *
 * EmulateGetpwnam_r --
 *
 *      Emulates getpwnam_r() for old/odd systems that don't have it
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Data may be stored in 'buf'.
 *
 *-----------------------------------------------------------------------------
 */

static int
EmulateGetpwnam_r(const char *name,       // IN
                  struct passwd *pwbuf,   // IN/OUT
                  char *buf,              // IN
                  size_t buflen,          // IN
                  struct passwd **pwbufp) // IN/OUT
{
   static Atomic_uint32 mutex = {0};
   struct passwd *pw;
   int savedErrno;

   ASSERT(pwbuf);
   ASSERT(name);
   ASSERT(buf);
   ASSERT(pwbufp);

   /*
    * XXX Use YIELD() here when it works on FreeBSD.
    */
   while (Atomic_ReadWrite(&mutex, 1)); // Spinlock.

   pw = getpwnam(name);
   savedErrno = errno;
   *pwbufp = PasswdCopy(pw, pwbuf, buf, buflen);

   Atomic_Write(&mutex, 0);

   if (pw) {
      return 0;
   } else if (savedErrno) {
      return savedErrno;
   } else {
      return ENOENT;
   }
}
#endif


#ifndef VM_SYSTEM_HAS_GETPWUID_R
/*
 *-----------------------------------------------------------------------------
 *
 * EmulateGetpwuid_r --
 *
 *      Emulates getpwuid_r() for old/odd systems that don't have it
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
EmulateGetpwuid_r(uid_t uid,               // IN:
                  struct passwd *pwbuf,    // IN/OUT:
                  char *buf,               // IN:
                  size_t buflen,           // IN:
                  struct passwd **pwbufp)  // IN/OUT:
{
   static Atomic_uint32 mutex = {0};
   struct passwd *pw;
   int savedErrno;

   ASSERT(pwbuf);
   ASSERT(buf);
   ASSERT(pwbufp);

   /*
    * XXX Use YIELD() here when it works on FreeBSD.
    */

   while (Atomic_ReadWrite(&mutex, 1)); // Spinlock

   pw = getpwuid(uid);
   savedErrno = errno;
   *pwbufp = PasswdCopy(pw, pwbuf, buf, buflen);

   Atomic_Write(&mutex, 0);

   if (pw) {
      return 0;
   } else if (savedErrno) {
      return savedErrno;
   } else {
      return ENOENT;
   }
}
#endif // }


#ifndef VM_SYSTEM_HAS_GETGRNAM_R // {
/*
 *-----------------------------------------------------------------------------
 *
 * GroupCopy --
 *
 *      Copies a password structure as part of emulating the getgr*_r routines.
 *
 * Results:
 *      'new' if successful, NULL otherwise.
 *
 * Side effects:
 *      Modifies 'buf'
 *
 *-----------------------------------------------------------------------------
 */

static struct group *
GroupCopy(struct group *orig,  // IN:
          struct group *new,   // IN/OUT:
          char *buf,           // IN:
          size_t bufLen)       // IN:
{
   if (!orig) {
      return NULL;
   }

   *new = *orig;

   if (!CopyFieldIntoBuf(orig->gr_name, &new->gr_name, &buf, &bufLen)) {
      return NULL;
   }
   if (!CopyFieldIntoBuf(orig->gr_passwd, &new->gr_passwd, &buf, &bufLen)) {
      return NULL;
   }

   if (orig->gr_mem) {
      int i;
      uintptr_t alignLen;
      char **newGrMem;

      /*
       * Before putting the gr_mem 'char **' array into 'buf', aligns the
       * buffer to a pointer-size boundary.
       */

      alignLen = ((((uintptr_t) buf) +
                    (sizeof(void *) - 1)) & ~(sizeof(void *) - 1));
      alignLen -= ((uintptr_t) buf);

      if (bufLen < alignLen) {
         return NULL;
      }
      buf += alignLen;
      bufLen -= alignLen;

      /*
       * Count the number of items in the gr_mem array, and then copy them all.
       */

      for (i = 0; orig->gr_mem[i]; i++);
      i++; // need space for a terminating NULL

      if (bufLen < (i * sizeof(void *))) {
         return NULL;
      }
      newGrMem = (char **)buf;
      buf += i * sizeof(void *);
      bufLen -= i * sizeof(void *);

      for (i = 0; orig->gr_mem[i]; i++, newGrMem++) {
         size_t flen;

         flen = strlen(orig->gr_mem[i]) + 1;
         if (bufLen < flen) {
            return NULL;
         }

         *newGrMem = buf;
         memcpy(*newGrMem, orig->gr_mem[i], flen);
         buf += flen;
         bufLen -= flen;
      }
      *newGrMem = NULL;
   }

   return new;
}
#endif // }


#ifndef VM_SYSTEM_HAS_GETGRNAM_R // {
/*
 *-----------------------------------------------------------------------------
 *
 * EmulateGetgrnam_r --
 *
 *      Emulates getgrnam_r() for old/odd systems that don't have it
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Data may be stored in 'buf'.
 *
 *-----------------------------------------------------------------------------
 */

static int
EmulateGetgrnam_r(const char *name,       // IN:
                  struct group *grbuf,    // IN/OUT:
                  char *buf,              // IN:
                  size_t buflen,          // IN:
                  struct group **grbufp)  // IN/OUT:
{
   static Atomic_uint32 mutex = {0};
   struct group *gr;
   int savedErrno;

   ASSERT(grbuf);
   ASSERT(name);
   ASSERT(buf);
   ASSERT(grbufp);

   /*
    * XXX Use YIELD() here once it is available on FreeBSD
    */

   while (Atomic_ReadWrite(&mutex, 1)); // Spinlock

   gr = getgrnam(name);
   savedErrno = errno;
   *grbufp = GroupCopy(gr, grbuf, buf, buflen);

   Atomic_Write(&mutex, 0);

   if (gr) {
      return 0;
   } else if (savedErrno) {
      return savedErrno;
   } else {
      return ENOENT;
   }
}
#endif // }


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getpwnam_r --
 *
 *      POSIX getpwnam_r()
 *
 * Results:
 *      Returns 0 with success and pointer to updated passwd struct
 *      or returns error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Getpwnam_r(const char *name,     // IN:
                 struct passwd *pw,    // IN:
                 char *buf,            // IN:
                 size_t size,          // IN:
                 struct passwd **ppw)  // OUT:
{
   int ret;
   char *tmpname;

   if (!PosixConvertToCurrent(name, &tmpname)) {
      /*
       * Act like nonexistent user, almost.
       * While getpwnam_r() returns 0 on nonexistent user,
       * we will return the errno instead.
       */

      *ppw = NULL;

      return errno;
   }

#if defined(VM_SYSTEM_HAS_GETPWNAM_R)
   ret = getpwnam_r(tmpname, pw, buf, size, ppw);
#else
   ret = EmulateGetpwnam_r(tmpname, pw, buf, size, ppw);
#endif

   Posix_Free(tmpname);

   // ret is errno on failure, *ppw is NULL if no matching entry found.
   if (ret != 0 || *ppw == NULL) {
      return ret;
   }

   return GetpwInternal_r(pw, buf, size, ppw);
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getpwuid_r --
 *
 *      POSIX getpwuid_r()
 *
 * Results:
 *      Returns 0 with success and pointer to updated passwd struct
 *      or returns error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Getpwuid_r(uid_t uid,            // IN:
                 struct passwd *pw,    // IN:
                 char *buf,            // IN:
                 size_t size,          // IN:
                 struct passwd **ppw)  // OUT:
{
   int ret;

#if defined(VM_SYSTEM_HAS_GETPWNAM_R)
   ret = getpwuid_r(uid, pw, buf, size, ppw);
#else
   ret = EmulateGetpwuid_r(uid, pw, buf, size, ppw);
#endif
   if (ret != 0 || *ppw == NULL) {
      // ret is errno on failure, *ppw is NULL if no matching entry found.
      return ret;
   }

   return GetpwInternal_r(pw, buf, size, ppw);
}


/*
 *----------------------------------------------------------------------
 *
 * GetpwInternal_r --
 *
 *      Helper function for Posix_Getpwnam_r and Posix_Getpwuid_r
 *
 * Results:
 *      Returns 0 with success and pointer to updated passwd struct
 *      or returns error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
GetpwInternal_r(struct passwd *pw,    // IN:
                char *buf,            // IN:
                size_t size,          // IN:
                struct passwd **ppw)  // OUT:
{
   int ret;
   char *pwname = NULL;
   char *passwd = NULL;
#if !defined __ANDROID__
   char *gecos = NULL;
#endif
   char *dir = NULL;
   char *shell = NULL;
   size_t n;

   /*
    * Maybe getpwnam_r didn't use supplied struct, but we don't care.
    * We just fix up the one it gives us.
    */

   pw = *ppw;

   /*
    * Convert strings to UTF-8
    */

   ret = ENOMEM;
   if (pw->pw_name &&
       (pwname = Unicode_Alloc(pw->pw_name,
                               STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (pw->pw_passwd &&
       (passwd = Unicode_Alloc(pw->pw_passwd,
                               STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
#if !defined __ANDROID__
   if (pw->pw_gecos &&
       (gecos = Unicode_Alloc(pw->pw_gecos,
                              STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
#endif
   if (pw->pw_dir &&
       (dir = Unicode_Alloc(pw->pw_dir,
                            STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (pw->pw_shell &&
       (shell = Unicode_Alloc(pw->pw_shell,
                              STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }

   /*
    * Put UTF-8 strings into the structure.
    */

   ret = ERANGE;
   n = 0;

   if (pwname) {
      size_t len = strlen(pwname) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      pw->pw_name = memcpy(buf + n, pwname, len);
      n += len;
   }

   if (passwd != NULL) {
      size_t len = strlen(passwd) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      pw->pw_passwd = memcpy(buf + n, passwd, len);
      n += len;
   }
#if !defined __ANDROID__
   if (gecos) {
      size_t len = strlen(gecos) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      pw->pw_gecos = memcpy(buf + n, gecos, len);
      n += len;
   }
#endif
   if (dir) {
      size_t len = strlen(dir) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      pw->pw_dir = memcpy(buf + n, dir, len);
      n += len;
   }

   if (shell) {
      size_t len = strlen(shell) + 1;

      if (n + len > size || n + len < n) {
         goto exit;
      }
      pw->pw_shell = memcpy(buf + n, shell, len);
      n += len;
   }
   ret = 0;

exit:
   Posix_Free(passwd);
   Posix_Free(dir);
   Posix_Free(pwname);
#if !defined __ANDROID__
   Posix_Free(gecos);
#endif
   Posix_Free(shell);

   return ret;
}


#if !defined(sun) // {
/*
 *----------------------------------------------------------------------
 *
 * Posix_GetGroupList --
 *
 *      POSIX getgrouplist()
 *
 * Results:
 *      Returns number of groups found, or -1 if *ngroups is
 *      smaller than number of groups found.  Also returns
 *      the list of groups.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Posix_GetGroupList(const char *user,  // IN:
                   gid_t group,       // IN:
                   gid_t *groups,     // OUT:
                   int *ngroups)      // IN/OUT:
{
   char *tmpuser;
   int ret;

   if (!PosixConvertToCurrent(user, &tmpuser)) {
      /*
       * Act like nonexistent user.
       * The supplied gid is always returned, so there's exactly
       * one group.
       * While the man page doesn't say, the return value is
       * the same as *ngroups in the success case.
       *
       * Should we always return -1 instead?
       *
       * -- edward
       */

      int n = *ngroups;

      *ngroups = 1;
      if (n < 1) {
         return -1;
      }
      ASSERT(groups != NULL);
      *groups = group;

      return 1;
   }

   ret = getgrouplist(tmpuser, group, groups, ngroups);

   Posix_Free(tmpuser);

   return ret;
}


#endif // }

/*
 *----------------------------------------------------------------------
 *
 * Posix_Getgrnam --
 *
 *      POSIX getgrnam()
 *
 * Results:
 *      Pointer to updated group struct on NULL on error.
 *
 * Side effects:
 *      errno is set on error
 *
 *----------------------------------------------------------------------
 */

struct group *
Posix_Getgrnam(const char *name)  // IN:
{
   struct group *gr;
   char *tmpname;
   int ret;
   static struct group sgr = {0};

   if (!PosixConvertToCurrent(name, &tmpname)) {
      return NULL;
   }
   gr = getgrnam(tmpname);
   Posix_Free(tmpname);

   if (!gr) {
      return NULL;
   }

   /* Free static structure string pointers before reuse. */
   Posix_Free(sgr.gr_name);
   sgr.gr_name = NULL;
   Posix_Free(sgr.gr_passwd);
   sgr.gr_passwd = NULL;
   Util_FreeStringList(sgr.gr_mem, -1);
   sgr.gr_mem = NULL;

   /* Fill out structure with new values. */
   sgr.gr_gid = gr->gr_gid;

   ret = ENOMEM;
   if (gr->gr_passwd &&
       (sgr.gr_passwd = Unicode_Alloc(gr->gr_passwd,
                                      STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (gr->gr_name &&
       (sgr.gr_name = Unicode_Alloc(gr->gr_name,
                                    STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (gr->gr_mem) {
      sgr.gr_mem = Unicode_AllocList(gr->gr_mem, -1,
                                     STRING_ENCODING_DEFAULT);
   }

   ret = 0;

 exit:
   if (ret != 0) {
      errno = ret;
      return NULL;
   }

   return &sgr;
}


/*
 *----------------------------------------------------------------------
 *
 * Posix_Getgrnam_r --
 *
 *      POSIX getgrnam_r()
 *
 * Results:
 *      Returns 0 with success and pointer to updated group struct
 *      or returns error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Posix_Getgrnam_r(const char *name,     // IN:
                 struct group *gr,     // IN:
                 char *buf,            // IN:
                 size_t size,          // IN:
                 struct group **pgr)   // OUT:
{
   int ret, i;
   char *tmpname;
   char *grname = NULL;
   char *grpasswd = NULL;
   char **grmem = NULL;
   size_t n;

   if (!PosixConvertToCurrent(name, &tmpname)) {
      /*
       * Act like nonexistent group, almost.
       * While getgrnam_r() returns 0 on nonexistent group,
       * we will return the errno instead.
       */

      *pgr = NULL;

      return errno;
   }

#if defined(VM_SYSTEM_HAS_GETGRNAM_R)
   ret = getgrnam_r(tmpname, gr, buf, size, pgr);
#else
   ret = EmulateGetgrnam_r(tmpname, gr, buf, size, pgr);
#endif
   Posix_Free(tmpname);

   // ret is errno on failure, *pgr is NULL if no matching entry found.
   if (ret != 0 || *pgr == NULL) {
      return ret;
   }

   /*
    * Maybe getgrnam_r didn't use supplied struct, but we don't care.
    * We just fix up the one it gives us.
    */

   gr = *pgr;

   /*
    * Convert strings to UTF-8
    */

   ret = ENOMEM;
   if (gr->gr_name &&
       (grname = Unicode_Alloc(gr->gr_name,
                               STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (gr->gr_passwd &&
       (grpasswd = Unicode_Alloc(gr->gr_passwd,
                                 STRING_ENCODING_DEFAULT)) == NULL) {
      goto exit;
   }
   if (gr->gr_mem) {
      grmem = Unicode_AllocList(gr->gr_mem, -1, STRING_ENCODING_DEFAULT);
   }

   /*
    * Put UTF-8 strings into the structure.
    */

   ret = ERANGE;
   n = 0;

   if (grname) {
      size_t len = strlen(grname) + 1;

      if (n + len > size) {
         goto exit;
      }
      gr->gr_name = memcpy(buf + n, grname, len);
      n += len;
   }

   if (grpasswd != NULL) {
      size_t len = strlen(grpasswd) + 1;

      if (n + len > size) {
         goto exit;
      }
      gr->gr_passwd = memcpy(buf + n, grpasswd, len);
      n += len;
   }

   if (grmem) {
      for (i = 0; grmem[i]; i++) {
         size_t len = strlen(grmem[i]) + 1;

         if (n + len > size) {
            goto exit;
         }
         gr->gr_mem[i] = memcpy(buf + n, grmem[i], len);
         n += len;
      }
   }

   ret = 0;

 exit:
   Posix_Free(grpasswd);
   Posix_Free(grname);
   Util_FreeStringList(grmem, -1);

   return ret;
}
0707010000019D000081A40000000000000000000000016822550500001F62000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/prng.c    /*********************************************************
 * Copyright (C) 1998-2016,2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * prng.c --
 *
 *    Pseudo-random bits generation. --hpreg
 *    (freestanding / no library dependencies)
 */

#ifdef VMKERNEL
#include "vmk_exports.h"
#endif
#include "vmware.h"
#include "random.h"


/*
 *-----------------------------------------------------------------------------
 *
 * RandomFastImpl --
 *
 *      Return uniformly distributed pseudo-random numbers in the range of 0
 *      and 2^32-1 using the algorithm PCG-XSH-RR by M. O'Neill.
 *      See http://www.pcg-random.org/
 *
 *      *** NOTE: THIS ALGORITHM IS SUBMITTED BUT NOT (YET) PUBLISHED
 *      *** IN A PEER-REVIEWED JOURNAL. It looks quite good (certainly better
 *      *** than Random_Simple), but is subject to change until standardized.
 *      *** If accepted, will likely replace Random_Quick and Random_Simple.
 *
 *      PCG-XSH-RR is an LCG:
 *         S' = (N * S + C) mod M
 *      with N = 6364136223846793005, M = 2^64, and C any odd number
 *      (thus making M and C relatively prime, as required for LCGs).
 *      PCG applies an output permutation "xorshift high, random rotate"
 *         output = rotate32((state ^ (state >> 18)) >> 27, state >> 59)
 *      The xorshift improves the quality of lower-order bits, and the
 *      random rotate uses highest-quality bits to further randomize
 *      lower-order bits; these permutations produce much higher quality
 *      random numbers than the underlying LCG.
 *
 *      The period of this RNG is 2^64, and takes 3.5-7 cycles depending
 *      on optimization.
 *
 *      Generated x86_64 assembly (for Random_Fast):
 *          mov    (%rdi),%rcx
 *          movabs $0x5851f42d4c957f2d,%rax
 *          mov    %rdi,%rdx
 *          or     $0x1,%rdx
 *          imul   %rcx,%rax     \
 *          add    %rdx,%rax      } LCG
 *          mov    %rax,(%rdi)   /
 *          mov    %rcx,%rax
 *          shr    $0x12,%rax   \
 *          xor    %rcx,%rax     \
 *          shr    $0x3b,%rcx     } rotate32
 *          shr    $0x1b,%rax    /
 *          ror    %cl,%eax     /
 *          retq
 *
 * Results:
 *      A random number in the specified range is returned.
 *
 * Side Effects:
 *      The RNG context is modified for later use by Random_Fast.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE uint32
RandomRor(uint32 val,    // IN:
          unsigned rot)  // IN: rotation
{
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
   // Processor has barrel roll right instruction
   __asm__("rorl %%cl, %0": "=rm"(val): "0"(val), "c"(rot));
   return val;
#else
   // Emulate barrel roll right with two shifts
   return (val >> rot) | (val << ((-rot) & 31));
#endif
}

static INLINE uint32
RandomFastImpl(uint64 *rs,  // IN/OUT:
               uint64 inc)  // IN:
{
   uint64 state = *rs;
   uint32 xorshift, rot;

   *rs = state * CONST64U(6364136223846793005) + inc;
   xorshift = ((state >> 18) ^ state) >> 27;
   rot = state >> 59;
   return RandomRor(xorshift, rot);
}

/*
 *-----------------------------------------------------------------------------
 *
 * Random_Fast --
 * Random_Fast64 --
 * Random_FastStream --
 * Random_FastStreamSeed --
 *
 *      Wrappers around RandomFastImpl to generate specific behaviors.
 *         Random_Fast: non-deterministic self-seeding based on address
 *         Random_FastStream: deterministic seeding
 *
 *      The self-seeding generator has two quirks worth mentioning.
 *      (1) PCG generates the random value and manipulates the state for
 *          the next use in parallel. (See the disassembly for details).
 *          The downside of doing so is that the first generated value is
 *          a mere permutation of the seed, so proper seeding requires running
 *          the generator once to distribute the seed across all bits.
 *      (2) PCG discards the 27 least significant state bits as low-quality.
 *          A naive seed which does not populate the upper bits (including the
 *          very common choices of 0, getpid(), or time()) effectively starts
 *          the 2^64 period at 0x00000000. This is statisically valid (we
 *          would expect 2^32 sequences to generate zero as their first value),
 *          but unexpected. To work around weak seeds, always run the generator
 *          once to bypass a potential 0x00000000. This DOES have the
 *          statistical flaw that 0x00000000 will be slightly less likely.
 *       Thus, when using Random_Fast, be sure to discard the first TWO values
 *       to ensure good seeding. If the statistical imbalance in doing so is
 *       of concern, use Random_FastStream with good (e.g. Random_Crypto)
 *       seeding, or use Random_Quick which has a stronger seed algorithm.
 *
 * Results:
 *      A random number in the specified range is returned.
 *
 * Side Effects:
 *      The RNG context is modified for later use by Random_Fast[Stream].
 *
 *-----------------------------------------------------------------------------
 */

uint32
Random_Fast(uint64 *rs)  // IN/OUT:
{
   uint64 inc = (uintptr_t)(void *)rs | 0x1;  // stream selector, must be odd
   return RandomFastImpl(rs, inc);
}

uint64
Random_Fast64(uint64 *rs)  // IN/OUT:
{
   return QWORD(Random_Fast(rs), Random_Fast(rs)) ;
}

uint32
Random_FastStream(RandomFastContext *rfc)  // IN/OUT:
{
   return RandomFastImpl(&rfc->state, rfc->sequence);
}

uint64
Random_FastStream64(RandomFastContext *rfc)  // IN/OUT:
{
   return QWORD(RandomFastImpl(&rfc->state, rfc->sequence),
                RandomFastImpl(&rfc->state, rfc->sequence));
}

void
Random_FastStreamSeed(RandomFastContext *rfc,  // OUT:
                      uint64 seed,             // IN:
                      uint64 seq)              // IN:
{
   rfc->state = 0;
   rfc->sequence = (seq << 1) | 0x1;  // stream selector, must be odd
   Random_FastStream(rfc);
   rfc->state += seed;
   Random_FastStream(rfc);
}


/*
 *----------------------------------------------------------------------
 *
 * Random_Simple --
 *
 *      Generates the next random number in the pseudo-random sequence
 *      defined by the multiplicative linear congruential generator
 *      S' = 16807 * S mod (2^31 - 1).  This is the ACM "minimal standard
 *      random number generator". Based on method described by D.G. Carta
 *      in CACM, January 1990, optimization to avoid modulo from
 *      Carl Waldspurger (OSDI 1994).
 *
 *      Usage: provide previous random number as the seed for next one.
 *
 * Results:
 *      A random integer number is returned.
 *
 * Side Effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Random_Simple(int seed)  // IN:
{
   uint64 product    = 33614 * (uint64) seed;
   uint32 product_lo = (uint32) (product & 0xFFFFFFFF) >> 1;
   uint32 product_hi = product >> 32;
   int32  test       = product_lo + product_hi;

   return test > 0 ? test : (test & 0x7FFFFFFF) + 1;
}

#ifdef VMKERNEL
VMK_KERNEL_EXPORT(Random_Fast);
VMK_KERNEL_EXPORT(Random_FastStream);
VMK_KERNEL_EXPORT(Random_FastStream64);
VMK_KERNEL_EXPORT(Random_FastStreamSeed);
VMK_KERNEL_EXPORT(Random_Simple);
#endif
  0707010000019E000081A40000000000000000000000016822550500002759000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/random.c  /*********************************************************
 * Copyright (C) 1998-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * random.c --
 *
 *    Random bits generation. --hpreg
 *    (Also see prng.c for freestanding generators)
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(_WIN32)
#   include <windows.h>
#   include <wincrypt.h>
#else
#   include <errno.h>
#   include <fcntl.h>
#   include <unistd.h>

#   define GENERIC_RANDOM_DEVICE "/dev/urandom"
#endif

#include "vmware.h"
#include "vm_basic_asm.h"  // RDTSC()
#include "log.h"
#include "random.h"
#include "util.h"

#if !defined(VMX86_RELEASE)
#include "vm_atomic.h"
#endif


#if defined(_WIN32)
#if !defined(VM_WIN_UWP)
/*
 *-----------------------------------------------------------------------------
 *
 * RandomBytesWin32 --
 *
 *      Generate 'size' bytes of cryptographically strong random bits in
 *      'buffer'.
 *
 * Results:
 *      TRUE   success
 *      FALSE  failure
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RandomBytesWin32(size_t size,   // IN:
                 void *buffer)  // OUT:
{
   HCRYPTPROV csp;

   if (size != (DWORD) size) {
      Log("%s: Overflow: %"FMTSZ"d\n", __FUNCTION__, size);
      return FALSE;
   }

   if (CryptAcquireContext(&csp, NULL, NULL, PROV_RSA_FULL,
                           CRYPT_VERIFYCONTEXT) == FALSE) {
      Log("%s: CryptAcquireContext failed: %d\n", __FUNCTION__, GetLastError());
      return FALSE;
   }

   if (CryptGenRandom(csp, (DWORD) size, buffer) == FALSE) {
      Log("%s: CryptGenRandom failed: %d\n", __FUNCTION__, GetLastError());
      CryptReleaseContext(csp, 0);
      return FALSE;
   }

   if (CryptReleaseContext(csp, 0) == FALSE) {
      Log("%s: CryptReleaseContext failed: %d\n", __FUNCTION__, GetLastError());
      return FALSE;
   }

   return TRUE;
}
#endif // !VM_WIN_UWP
#else
/*
 *-----------------------------------------------------------------------------
 *
 * RandomBytesPosix --
 *
 *      Generate 'size' bytes of cryptographically strong random bits in
 *      'buffer'.
 *
 * Results:
 *      TRUE   success
 *      FALSE  failure
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RandomBytesPosix(const char *name,  // IN:
                 size_t size,       // IN:
                 void *buffer)      // OUT:
{
   int fd = open(name, O_RDONLY);

   if (fd == -1) {
      Log("%s: failed to open %s: %s\n", __FUNCTION__, name, strerror(errno));
      return FALSE;
   }

   /*
    * Although /dev/urandom does not block, it can return short reads. That
    * said, reads returning nothing should not happen. Just in case, track
    * those any that do appear.
    */

   while (size > 0) {
      ssize_t bytesRead = read(fd, buffer, size);

      if ((bytesRead == 0) || ((bytesRead == -1) && (errno != EINTR))) {
         close(fd);

         if (bytesRead == 0) {
            Log("%s: zero length read while reading from %s\n",
                __FUNCTION__, name);
         } else {
            Log("%s: %"FMTSZ"u byte read failed while reading from %s: %s\n",
                __FUNCTION__, size, name, strerror(errno));
         }

         return FALSE;
      }

      if (bytesRead > 0) {
         size -= bytesRead;
         buffer = ((uint8 *) buffer) + bytesRead; 
      }
   }

   if (close(fd) == -1) {
      Log("%s: failed to close %s: %s\n", __FUNCTION__, name, strerror(errno));
   }

   return TRUE;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Random_Crypto --
 *
 *      Generate 'size' bytes of cryptographically strong random bits in
 *      'buffer'. Use this function when you need non-predictable random
 *      bits, typically in security applications, where the bits are generated
 *      external to the application.
 *
 *      DO NOT USE THIS FUNCTION UNLESS YOU HAVE AN ABSOLUTE, EXPLICIT
 *      NEED FOR CRYPTOGRAPHICALLY VALID RANDOM NUMBERS.
 *
 * Results:
 *      TRUE   success
 *      FALSE  failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#if !defined(VMX86_RELEASE)
static Atomic_uint32 forceFail;
#endif

Bool
Random_Crypto(size_t size,   // IN:
              void *buffer)  // OUT:
{
#if !defined(VMX86_RELEASE)
   if (Atomic_ReadIfEqualWrite32(&forceFail, 1, 0) == 1) {
      return FALSE;
   }
#endif

#if defined(_WIN32)
   return RandomBytesWin32(size, buffer);
#else
   /*
    * We use /dev/urandom and not /dev/random because it is good enough and
    * because it cannot block. --hpreg
    */

   return RandomBytesPosix(GENERIC_RANDOM_DEVICE, size, buffer);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Random_CryptoFail --
 *
 *      This function will cause the next call to Random_Crypto to fail.
 *
 *      NOTE: This function does nothing in a release build.
 *
 * Results:
 *      TRUE   success
 *      FALSE  failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
Random_CryptoFail(void)
{
#if !defined(VMX86_RELEASE)
   Atomic_Write32(&forceFail, 1);
#endif
}



/*
 *-----------------------------------------------------------------------------
 *
 * Random_QuickSeed --
 *
 *      Creates a context for the quick random number generator and returns it.
 *
 * Results:
 *      A pointer to the context used for the random number generator. The
 *      context is dynamically allocated memory that must be freed by caller.
 *
 * Side Effects:
 *      None
 *
 * NOTE:
 *      Despite the look of the code this RNG is extremely fast.
 *
 *-----------------------------------------------------------------------------
 */

#define N 25
#define M 18

#define A 0x8EBFD028
#define S 7
#define B 0x2B5B2500
#define T 15
#define C 0xDB8B0000
#define L 16

struct rqContext {
  uint32 x[N];
  int p, q;
};

rqContext *
Random_QuickSeed(uint32 seed)  // IN:
{
   uint32 i;
   struct rqContext *rs = Util_SafeMalloc(sizeof *rs);

   const uint32 xx[N] = {
      0x95F24DAB, 0x0B685215, 0xE76CCAE7, 0xAF3EC239, 0x715FAD23,
      0x24A590AD, 0x69E4B5EF, 0xBF456141, 0x96BC1B7B, 0xA7BDF825,
      0xC1DE75B7, 0x8858A9C9, 0x2DA87693, 0xB657F9DD, 0xFFDC8A9F,
      0x8121DA71, 0x8B823ECB, 0x885D05F5, 0x4E20CD47, 0x5A9AD5D9,
      0x512C0C03, 0xEA857CCD, 0x4CC1D30F, 0x8891A8A1, 0xA6B7AADB
   };


   for (i = 0; i < N; i++) {
      rs->x[i] = xx[i] ^ seed;
   }

   rs->p = N - 1;
   rs->q = N - M - 1;

   return rs;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Random_Quick --
 *
 *      Return uniformly distributed pseudo-random numbers in the range of 0
 *      and 2^32-1 using the (research grade) tGFSR algorithm tt800. The period
 *      of this RNG is 2^(32*N) - 1 while generally having lower overhead than
 *      Random_Crypto().
 *
 * Results:
 *      A random number in the specified range is returned.
 *
 * Side Effects:
 *      The RNG context is modified for later use by Random_Quick.
 *
 * NOTE:
 *      Despite the look of the code this RNG is extremely fast.
 *
 *-----------------------------------------------------------------------------
 */

uint32
Random_Quick(rqContext *rs)  // IN/OUT:
{
   uint32 y, z;

   ASSERT(rs);

   if (rs->p == N - 1) {
      rs->p = 0;
   } else {
      (rs->p)++;
   }

   if (rs->q == N - 1) {
      rs->q = 0;
   } else {
      (rs->q)++;
   }

   z = rs->x[(rs->p)];
   y = rs->x[(rs->q)] ^ ( z >> 1 );

   if (z % 2) {
      y ^= A;
   }

   if (rs->p == N - 1) {
      rs->x[0] = y;
   } else {
      rs->x[(rs->p) + 1] = y;
   }

   y ^= ( ( y << S ) & B );
   y ^= ( ( y << T ) & C );

   y ^= ( y >> L ); // improves bits

   return y;
}


#if 0
/*
 *----------------------------------------------------------------------
 *
 * Random_SpeedTest --
 *
 *      Benchmarks the speed of various random number generators.
 *      (Intended for debugging).
 *
 * Results:
 *      Populates *out with cycle counts.
 *
 * Side Effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

typedef struct {
   uint64 nop;
   uint64 simple;
   uint64 fast;
   uint64 quick;
} RandomSpeedTestResults;
void Random_SpeedTest(uint64 iters, RandomSpeedTestResults *out);

static int ABSOLUTELY_NOINLINE
RandomNop(int *seed)
{
   return *(volatile int *)seed;
}

void
Random_SpeedTest(uint64 iters,                 // IN:
                 RandomSpeedTestResults *out)  // OUT:
{
   int i;
   uint64 start;
   int nop, simple;
   uint64 fast;
   rqContext *rq;

   simple = nop = getpid();
   Random_FastSeed(&fast, simple);
   rq = Random_QuickSeed(nop);

   start = RDTSC();
   for (i = 0; i < iters; i++) {
      RandomNop(&nop);
   }
   out->nop = RDTSC() - start;

   start = RDTSC();
   for (i = 0; i < iters; i++) {
      simple = Random_Simple(simple);
   }
   out->simple = RDTSC() - start;

   start = RDTSC();
   for (i = 0; i < iters; i++) {
      Random_Fast(&fast);
   }
   out->fast = RDTSC() - start;

   start = RDTSC();
   for (i = 0; i < iters; i++) {
      Random_Quick(rq);
   }
   out->quick = RDTSC() - start;

   free(rq);
}
#endif
   0707010000019F000081A40000000000000000000000016822550500002444000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/sha1.c    /*********************************************************
 * Copyright (C) 1998-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * SHA-1 in C
 * Originally released by Steve Reid <steve@edmweb.com> into the public domain
 *
 * Test Vectors (from FIPS PUB 180-1)
 * "abc"
 *   A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
 * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
 *   84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
 * A million repetitions of "a"
 *   34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
 *
 * Major changes:
 *
 *    12/98: JEB: Removed main and moved prototypes to sha1.h
 *                Made SHA1Transform a static function
 *
 *    10/14: rberinde: Added SSE3 code and test, cleaned up a bit.
 *
 * If any changes are made to this file, please run:
 *    test-esx -n misc/sha1.sh
 */

#if defined(_WIN32)
#   include <memory.h>
#endif

#if defined(sun) && !defined(SOL9)
#   include <memory.h>
#endif

#if defined(__FreeBSD__) && defined(_KERNEL)
#   include <sys/libkern.h>
#   include <sys/systm.h>
#endif

#include <string.h>
#include "vmware.h"
#include "sha1.h"
#include "vm_basic_asm.h"
#include "vmk_exports.h"

/* Initialization vectors. */
static const uint32 sha1InitVec[5] = { 0x67452301,
                                       0xEFCDAB89,
                                       0x98BADCFE,
                                       0x10325476,
                                       0xC3D2E1F0 };

/* If the endianess is not defined (it is done in string.h of glibc 2.1.1), we
   default to LE --hpreg */
#ifndef LITTLE_ENDIAN
# define LITTLE_ENDIAN /* This should be #define'd if true. */
#endif

#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

#define F0(w,x,y) ((w&(x^y))^y)
#define F1(w,x,y) (w^x^y)
#define F2(w,x,y) (((w|x)&y)|(w&x))
#define F3(w,x,y) (w^x^y)

typedef union {
   unsigned char c[64];
   uint32 l[16];
} CHAR64LONG16;


/*
 *-----------------------------------------------------------------------------
 *
 * R --
 *
 *    SHA-1 core function.
 *
 * Results:
 *    Product in 'f'.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
R(CHAR64LONG16 *block,  // IN/OUT
  uint32 *f,            // IN/OUT
  int i)                // IN
{
   uint32 a, b, c, d, e, round, blk;
   a = f[0];
   b = f[1];
   c = f[2];
   d = f[3];
   e = f[4];
   f[1] = a;
   f[2] = rol(b, 30);
   f[3] = c;
   f[4] = d;
   if (i < 20) {
      round = 0x5A827999 + F0(b,c,d);
   } else if (i < 40) {
      round = 0x6ED9EBA1 + F1(b,c,d);
   } else if (i < 60) {
      round = 0x8F1BBCDC + F2(b,c,d);
   } else {
      round = 0xCA62C1D6 + F3(b,c,d);
   }
   if (i < 16) {
#ifdef LITTLE_ENDIAN
      blk = Bswap(block->l[i]);
#else
      blk = block->l[i];
#endif
   } else {
      blk = rol(block->l[(i+13) & 15] ^ block->l[(i+8) & 15] ^
                block->l[(i+2) & 15] ^ block->l[i & 15], 1);
   }
   block->l[i & 15] = blk;
   f[0] = e + round + blk + rol(a, 5);
}


/*
 *-----------------------------------------------------------------------------
 *
 * SHA1TransformNoSSE --
 *
 *    Apply SHA-1 transformation on a single 512-bit block.
 *
 * Results:
 *    'state' is updated.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
SHA1TransformNoSSE(uint32 state[5],           // IN/OUT
                   unsigned char buffer[64])  // IN
{
    int i;
    uint32 f[5];
    CHAR64LONG16* block = (CHAR64LONG16*)buffer;

    /* Copy context->state[] to working vars */
    for (i = 0; i < 5; i++) {
       f[i] = state[i];
    }

    for (i = 0; i < 80; i++) {
       R(block, f, i);
    }

    /* Add the working vars back into context.state[] */
    for (i = 0; i < 5; i++) {
       state[i] += f[i];
    }
}


/*
 *-----------------------------------------------------------------------------
 *
 * SHA1Transform --
 *
 *    Apply SHA-1 transformation on one or more 512-bit block buffers.
 *
 * Results:
 *    'state' is updated.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
SHA1Transform(uint32 state[5],              // IN/OUT
              const unsigned char *buffer,  // IN
              uint32 numBlocks)             // IN
{
    uint32 i;

    for (i = 0; i < numBlocks; i++) {
       unsigned char workspace[64];

       /*
        * Do not do that work in SHA1TransformNoSSE, otherwise gcc 2.7.2.3 will
        * go south and allocate a stack frame of 0x9c8 bytes, that immediately
        * leads to a stack smash and a host reset
        */
       memcpy(workspace, buffer, sizeof(workspace));
       SHA1TransformNoSSE(state, workspace);
       buffer += 64;
    }
}


/*
 *-----------------------------------------------------------------------------
 *
 * SHA1Init --
 *
 *    Fill context with initial SHA1 state.
 *
 * Results:
 *    Initialized context.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
SHA1Init(SHA1_CTX* context)  // OUT
{
    uint32 i;
    for (i = 0; i < 5; i++) {
       context->state[i] = sha1InitVec[i];
    }
    context->count[0] = context->count[1] = 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SHA1Update --
 *
 *    Hash data into context.
 *
 * Results:
 *    Updated context.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
SHA1Update(SHA1_CTX* context,          // IN/OUT
           const unsigned char *data,  // IN
           size_t len)                 // IN
{
    size_t curOfs, numRemaining;

    /* Current offset inside the current buffer. */
    curOfs = (context->count[0] >> 3) & 63;
    if ((context->count[0] += (uint32) (len << 3)) < (len << 3))
       context->count[1]++;
    context->count[1] += (uint32) (len >> 29);

    numRemaining = 64 - curOfs;

    if (len >= numRemaining) {
        /* Complete the current buffer and update. */
        memcpy(&context->buffer[curOfs], data, numRemaining);
        SHA1Transform(context->state, context->buffer, 1);
        data += numRemaining;
        len -= numRemaining;
        curOfs = 0;

        /* Update with any complete 64-byte buffers. */
        if (len >= 64) {
           size_t numBlocks = len / 64;

           SHA1Transform(context->state, data, numBlocks);
           data += 64 * numBlocks;
           len -= 64 * numBlocks;
        }
    }

    /* Copy over whatever is left. */
    ASSERT(len + curOfs < 64);
    memcpy(&context->buffer[curOfs], data, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * SHA1Final --
 *
 *    Add padding and return the message digest.
 *
 * Results:
 *    160 bit SHA1 value in digest.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
SHA1Final(unsigned char digest[SHA1_HASH_LEN],  // OUT
          SHA1_CTX* context)                    // IN
{
    size_t i, j;
    unsigned char finalcount[8];

    for (i = 0; i < 8; i++) {
        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
    }
    SHA1Update(context, (unsigned char *)"\200", 1);
    while ((context->count[0] & 504) != 448) {
        SHA1Update(context, (unsigned char *)"\0", 1);
    }
    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
    for (i = 0; i < SHA1_HASH_LEN; i++) {
        digest[i] = (unsigned char)
         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
    }
    /* Wipe variables */
    i = j = 0;
    memset(context->buffer, 0, 64);
    memset(context->state, 0, SHA1_HASH_LEN);
    memset(context->count, 0, 8);
    memset(&finalcount, 0, 8);
}
070701000001A0000081A40000000000000000000000016822550500000915000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/sleep.c   /*********************************************************
 * Copyright (C) 2012 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * sleep.c --
 *
 *   Portable, signal-safe implementation of Util_Usleep and Util_Sleep.
 */

#include "vmware.h"
#include "util.h"
#include "hostinfo.h"

#ifndef _WIN32
#include <unistd.h>
#endif

/*
 *----------------------------------------------------------------------
 *
 * Util_Usleep --
 *
 *    Sleeps for at least usec microseconds.  If interrupted by a signal,
 *    goes back to sleep.
 *
 *    This function is a drop-in replacement for usleep(3), so the argument is
 *    long because usleep(3) takes a long.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    A delay.
 *
 *----------------------------------------------------------------------
 */

void
Util_Usleep(long usec) // IN
{
   VmTimeType t_end, t;

   t_end = Hostinfo_SystemTimerUS() + usec;

   do {
      usleep(usec);
      t = Hostinfo_SystemTimerUS();
      usec = t_end - t;
   } while (t < t_end);
}


/*
 *----------------------------------------------------------------------
 *
 * Util_Sleep --
 *
 *    Sleeps for at least sec seconds.  If interrupted by a signal,
 *    goes back to sleep.
 *
 *    This function is a drop-in replacement for sleep(3), so the argument is
 *    unsigned int because sleep(3) takes unsigned int.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    A delay.
 *
 *----------------------------------------------------------------------
 */

void
Util_Sleep(unsigned int sec) // IN
{
   Util_Usleep((long)sec * 1000 * 1000);
}
   070701000001A1000081A4000000000000000000000001682255050000B47C000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/strutil.c /*********************************************************
 * Copyright (c) 1998-2019, 2021-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * strutil.c --
 *
 *    String utility functions.
 */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <wchar.h>
#else
#include <strings.h> /* For strncasecmp */
#include <stdint.h>
#endif
#include "vmware.h"
#include "strutil.h"
#include "str.h"
#include "dynbuf.h"
#include "vm_ctype.h"
#include "util.h"

#ifndef SIZE_MAX /* SIZE_MAX is new in C99 */
#define SIZE_MAX ((size_t) -1)
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_IsEmpty --
 *
 *      Test if a non-NULL string is empty.
 *
 * Results:
 *      TRUE if the string has length 0, FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#ifdef VMX86_DEBUG
static INLINE Bool
StrUtil_IsEmpty(const char *str)  // IN:
{
   ASSERT(str != NULL);
   return str[0] == '\0';
}
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_GetNextToken --
 *
 *      Get the next token from a string after a given index w/o modifying the
 *      original string.
 *
 * Results:
 *      An allocated, NUL-terminated string containing the token. 'index' is
 *         updated to point after the returned token
 *      NULL if no tokens are left
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
StrUtil_GetNextToken(unsigned int *index,    // IN/OUT: Index to start at
                     const char *str,        // IN    : String to parse
                     const char *delimiters) // IN    : Chars separating tokens
{
   unsigned int startIndex;
   unsigned int length;
   char *token;

   ASSERT(index);
   ASSERT(str);
   ASSERT(delimiters);
   ASSERT(*index <= strlen(str));

#define NOT_DELIMITER (Str_Strchr(delimiters, str[*index]) == NULL)

   /* Skip leading delimiters. */
   for (; ; (*index)++) {
      if (str[*index] == '\0') {
         return NULL;
      }

      if (NOT_DELIMITER) {
         break;
      }
   }
   startIndex = *index;

   /*
    * Walk the string until we reach the end of it, or we find a
    * delimiter.
    */
   for ((*index)++; str[*index] != '\0' && NOT_DELIMITER; (*index)++) {
   }

#undef NOT_DELIMITER

   length = *index - startIndex;
   ASSERT(length);
   token = Util_SafeMalloc(length + 1 /* NUL */);
   memcpy(token, str + startIndex, length);
   token[length] = '\0';

   return token;
}


#if defined(_WIN32)
/*
 ******************************************************************************
 * StrUtil_GetNextTokenW --
 *
 *      Get the next token from a UTF-16 string after a given index w/o
 *      modifying the original string. This routine is based on the
 *      StrUtil_GetNextToken() function but convert it from ASCII to Unicode
 *      version.
 *
 * Results:
 *      An allocated, NULL-terminated string containing the token. 'index'
 *      is updated to point after the returned token
 *      NULL if no tokens are left
 *
 * Side effects:
 *      None
 *
 ******************************************************************************
 */

wchar_t *
StrUtil_GetNextTokenW(unsigned int  *index,      // IN/OUT: Index to start at
                      const wchar_t *str,        // IN    : String to parse
                      const wchar_t *delimiters) // IN    : Separating tokens
{
   unsigned int startIndex;
   unsigned int length;
   wchar_t *token;

   ASSERT(index != NULL);
   ASSERT(str != NULL);
   ASSERT(delimiters != NULL);
   ASSERT(*index <= wcslen(str));

#define NOT_DELIMITER (wcschr(delimiters, str[*index]) == NULL)

   /* Skip leading delimiters. */
   for (;; (*index)++) {
      if (str[*index] == L'\0') {
         return NULL;
      }

      if (NOT_DELIMITER) {
         break;
      }
   }
   startIndex = *index;

   /*
    * Walk the string until we reach the end of it, or we find a
    * delimiter.
    */
   for ((*index)++; str[*index] != L'\0' && NOT_DELIMITER; (*index)++) {
   }

#undef NOT_DELIMITER

   length = *index - startIndex;
   ASSERT(length);
   token = Util_SafeMalloc(sizeof *token * (length + 1));
   wmemcpy(token, str + startIndex, length);
   token[length] = L'\0';

   return token;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_GetNextIntToken --
 *
 *      Acts like StrUtil_GetNextToken except it returns an int32.
 *
 * Results:
 *      TRUE if a valid int was parsed and 'out' contains the parsed int.
 *      FALSE otherwise. Contents of 'out' are undefined.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_GetNextIntToken(int32 *out,             // OUT   : parsed int
                        unsigned int *index,    // IN/OUT: Index to start at
                        const char *str,        // IN    : String to parse
                        const char *delimiters) // IN    : Chars separating tokens
{
   char *resultStr;
   Bool valid = FALSE;

   ASSERT(out);
   ASSERT(index);
   ASSERT(str);
   ASSERT(delimiters);

   resultStr = StrUtil_GetNextToken(index, str, delimiters);
   if (resultStr == NULL) {
      return FALSE;
   }

   valid = StrUtil_StrToInt(out, resultStr);
   free(resultStr);

   return valid;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_GetNextUintToken --
 *
 *      Acts like StrUtil_GetNextIntToken except it returns an uint32.
 *
 * Results:
 *      TRUE if a valid int was parsed and 'out' contains the parsed int.
 *      FALSE otherwise. Contents of 'out' are undefined.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_GetNextUintToken(uint32 *out,            // OUT   : parsed int
                         unsigned int *index,    // IN/OUT: Index to start at
                         const char *str,        // IN    : String to parse
                         const char *delimiters) // IN    : Chars separating tokens
{
   char *resultStr;
   Bool valid = FALSE;

   ASSERT(out);
   ASSERT(index);
   ASSERT(str);
   ASSERT(delimiters);

   resultStr = StrUtil_GetNextToken(index, str, delimiters);
   if (resultStr == NULL) {
      return FALSE;
   }

   valid = StrUtil_StrToUint(out, resultStr);
   free(resultStr);

   return valid;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_GetNextInt64Token --
 *
 *      Acts like StrUtil_GetNextToken except it returns an int64.
 *
 * Results:
 *      TRUE on a successful retrieval. FALSE otherwise.
 *      Token is stored in 'out', which is left undefined in the FALSE case.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_GetNextInt64Token(int64 *out,             // OUT: The output value
                          unsigned int *index,    // IN/OUT: Index to start at
                          const char *str,        // IN    : String to parse
                          const char *delimiters) // IN    : Chars separating tokens
{
   char *resultStr;
   Bool result;

   ASSERT(out);
   ASSERT(index);
   ASSERT(str);
   ASSERT(delimiters);

   resultStr = StrUtil_GetNextToken(index, str, delimiters);
   result = resultStr ? StrUtil_StrToInt64(out, resultStr) : FALSE;
   free(resultStr);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_DecimalStrToUint --
 *
 *      Convert a string into an integer.
 *
 * Results:
 *      TRUE if the conversion was successful, and 'out' contains the converted
 *      result, and 'str' is updated to point to new place after last processed
 *      digit.
 *      FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_DecimalStrToUint(unsigned int *out, // OUT
                         const char **str)  // IN/OUT : String to parse
{
   unsigned long val;
   char *ptr;

   ASSERT(out);
   ASSERT(str);

   errno = 0;
   val = strtoul(*str, &ptr, 10);
   if (ptr == *str || errno == ERANGE || val != (unsigned int)val) {
      return FALSE;
   }
   *str = ptr;
   *out = (unsigned int)val;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_StrToInt --
 *
 *      Convert a string into an integer.
 *
 * Results:
 *      TRUE if the conversion was successful and 'out' contains the converted
 *      result.
 *      FALSE otherwise. 'out' is undefined.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_StrToInt(int32 *out,      // OUT
                 const char *str) // IN : String to parse
{
   char *ptr;
   long val;

   ASSERT(out);
   ASSERT(str);

   errno = 0;

   val = strtol(str, &ptr, 0);
   *out = (int32)val;

   /*
    * Input must be non-empty, complete, no overflow, and value read must fit
    * into 32 bits - both signed and unsigned values are accepted.
    */

   return ptr != str && *ptr == '\0' && errno != ERANGE &&
          (val == (int32)val || val == (uint32)val);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_StrToUint --
 *
 *      Convert a string into unsigned integer.
 *
 * Results:
 *      TRUE if the conversion succeeded and 'out' contains the result.
 *      FALSE otherwise. 'out' is undefined.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_StrToUint(uint32 *out,     // OUT
                  const char *str) // IN : String to parse
{
   char *ptr;
   unsigned long val;

   ASSERT(out);
   ASSERT(str);

   errno = 0;

   val = strtoul(str, &ptr, 0);
   *out = (uint32)val;

   /*
    * Input must be non-empty, complete, no overflow, and value read must fit
    * into 32 bits - both signed and unsigned values are accepted.
    */

   return ptr != str && *ptr == '\0' && errno != ERANGE &&
          (val == (uint32)val || val == (int32)val);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_StrToInt64 --
 *
 *      Convert a string into a 64bit integer.
 *
 * Results:
 *      TRUE if conversion was successful, FALSE otherwise.
 *      Value is stored in 'out', which is left undefined in the FALSE case.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_StrToInt64(int64 *out,      // OUT: The output value
                   const char *str) // IN : String to parse
{
   char *ptr;

   ASSERT(out);
   ASSERT(str);

   errno = 0;

#if defined(_WIN32)
   *out = _strtoi64(str, &ptr, 0);
#elif defined(__FreeBSD__)
   *out = strtoq(str, &ptr, 0);
#else
   *out = strtoll(str, &ptr, 0);
#endif

   return ptr != str && ptr[0] == '\0' && errno != ERANGE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_StrToUint64 --
 *
 *      Convert a string into an unsigned 64bit integer.
 *
 * Results:
 *      TRUE if conversion was successful, FALSE otherwise.
 *      Value is stored in 'out', which is left undefined in the FALSE case.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_StrToUint64(uint64 *out,     // OUT: The output value
                    const char *str) // IN : String to parse
{
   char *ptr;

   ASSERT(out);
   ASSERT(str);

   errno = 0;

#if defined(_WIN32)
   *out = _strtoui64(str, &ptr, 0);
#elif defined(__FreeBSD__)
   *out = strtouq(str, &ptr, 0);
#else
   *out = strtoull(str, &ptr, 0);
#endif

   return ptr != str && ptr[0] == '\0' && errno != ERANGE && errno != EINVAL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_StrToSizet --
 *
 *      Convert a string into an unsigned integer that is either 32-bits or
 *      64-bits long, depending on the underlying architecture.
 *
 * Results:
 *      TRUE if conversion was successful, FALSE otherwise.
 *      Value is stored in 'out', which is left undefined in the FALSE case.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_StrToSizet(size_t *out,     // OUT: The output value
                   const char *str) // IN : String to parse
{
   char *ptr;

   ASSERT(out);
   ASSERT(str);

   errno = 0;
#if defined VM_64BIT
   ASSERT_ON_COMPILE(sizeof *out == sizeof(uint64));
#   if defined(_WIN32)
   *out = _strtoui64(str, &ptr, 0);
#   elif defined(__FreeBSD__)
   *out = strtouq(str, &ptr, 0);
#   else
   *out = strtoull(str, &ptr, 0);
#   endif
#else
   ASSERT_ON_COMPILE(sizeof *out == sizeof(uint32));
   *out = strtoul(str, &ptr, 0);
#endif

   return ptr != str && *ptr == '\0' && errno != ERANGE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_StrToDouble --
 *
 *      Convert a string into a double.
 *
 * Results:
 *      TRUE if the conversion was successful and 'out' contains the converted
 *      result.
 *      FALSE otherwise. 'out' is undefined.
 *
 * Side effects:
 *      Modifies errno.
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_StrToDouble(double *out,      // OUT: The output value
                    const char *str)  // IN : String to parse
{
   char *ptr = NULL;

   ASSERT(out);
   ASSERT(str);

   errno = 0;

   *out = strtod(str, &ptr);

   /*
    * Input must be complete and no overflow.
    */

   return ptr != str && *ptr == '\0' && errno != ERANGE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_CapacityToBytes --
 *
 *      Converts a string containing a measure of capacity (such as
 *      "100MB" or "1.5k") into an unadorned and primitive quantity of bytes
 *      capacity. The comment before the switch statement describes the kinds
 *      of capacity expressible.
 *
 * Results:
 *      TRUE if conversion was successful, FALSE otherwise.
 *      Value is stored in 'out', which is left undefined in the FALSE case.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_CapacityToBytes(uint64 *out,        // OUT: The output value
                        const char *str,    // IN: String to parse
                        unsigned int bytes) // IN: Bytes per unit in an
                                            //     unadorned string

{
   double quantity;
   char *rest;

   ASSERT(out);
   ASSERT(str);

   errno = 0;
   quantity = strtod(str, &rest);
   if (errno == ERANGE) {
      return FALSE;
   }

   /* Skip over any whitespace in the suffix. */
   while (*rest == ' ' || *rest == '\t') {
      rest++;
   }
   if (*rest != '\0') {
      uint64 shift;
      Bool suffixOK = TRUE;

      /*
       * [kK], [mM], [gG], and [tT] represent kilo, mega, giga, and tera
       * byte quantities respectively. [bB] represents a singular byte
       * quantity. [sS] represents a sector quantity.
       *
       * For kilo, mega, giga, and tera we're OK with an additional byte
       * suffix. Otherwise, the presence of an additional suffix is an error.
       */
      switch (*rest) {
      case 'b': case 'B': shift = 0;  suffixOK = FALSE; break;
      case 's': case 'S': shift = 9;  suffixOK = FALSE; break;
      case 'k': case 'K': shift = 10;                   break;
      case 'm': case 'M': shift = 20;                   break;
      case 'g': case 'G': shift = 30;                   break;
      case 't': case 'T': shift = 40;                   break;
      default :                                         return FALSE;
      }
      switch(*++rest) {
      case '\0':
         break;
      case 'b': case 'B':
         if (suffixOK && !*++rest) {
            break;
         }
         /* FALLTHRU */
      default:
         return FALSE;
      }
      quantity *= CONST64U(1) << shift;
   } else {
      /*
       * No suffix, so multiply by the number of bytes per unit as specified
       * by the caller.
       */
      quantity *= bytes;
   }

   *out = quantity;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_CapacityToSectorType --
 *
 *      Converts a string containing a measure of disk capacity (such as
 *      "100MB" or "1.5k") into an unadorned and primitive quantity of sector
 *      capacity.
 *
 * Results:
 *      TRUE if conversion was successful, FALSE otherwise.
 *      Value is stored in 'out', which is left undefined in the FALSE case.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_CapacityToSectorType(SectorType *out,    // OUT: The output value
                             const char *str,    // IN: String to parse
                             unsigned int bytes) // IN: Bytes per unit in an
                                                 //     unadorned string

{
   uint64 quantityInBytes;

   if (StrUtil_CapacityToBytes(&quantityInBytes, str, bytes) == FALSE) {
      return FALSE;
   }

   /*
    * Convert from "number of bytes" to "number of sectors", rounding up or
    * down appropriately.
    *
    * XXX: We should use DISKLIB_SECTOR_SIZE, but do we really want the
    * disklib header dependencies in this file?
    *
    */
   *out = (SectorType)((quantityInBytes + 256) / 512);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_FormatSizeInBytesUnlocalized --
 *
 *      Format a size (in bytes) to a string in a user-friendly way.
 *
 *      Example: 160041885696 -> "149.1 GB"
 *
 * Results:
 *      The allocated, NUL-terminated string (not localized).
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
StrUtil_FormatSizeInBytesUnlocalized(uint64 size) // IN
{
   /*
    * XXX TODO, BUG 199661:
    * This is a direct copy of Msg_FormatSizeInBytes without localization.
    * These two functions should ideally share the basic functionality, and
    * just differ in the string localization
    */

   char const *fmt;
   double sizeInSelectedUnit;
   unsigned int precision;
   char *sizeFormat;
   char *sizeString;
   char *result;
   static const double epsilon = 0.01;
   double delta;

   if (size >= CONST64U(1) << 40 /* 1 TB */) {
      fmt = "%s TB";
      sizeInSelectedUnit = (double)size / (CONST64U(1) << 40);
      precision = 1;
   } else if (size >= CONST64U(1) << 30 /* 1 GB */) {
      fmt = "%s GB";
      sizeInSelectedUnit = (double)size / (CONST64U(1) << 30);
      precision = 1;
   } else if (size >= CONST64U(1) << 20 /* 1 MB */) {
      fmt = "%s MB";
      sizeInSelectedUnit = (double)size / (CONST64U(1) << 20);
      precision = 1;
   } else if (size >= CONST64U(1) << 10 /* 1 KB */) {
      fmt = "%s KB";
      sizeInSelectedUnit = (double)size / (CONST64U(1) << 10);
      precision = 1;
   } else if (size >= CONST64U(2) /* 2 bytes */) {
      fmt = "%s bytes";
      sizeInSelectedUnit = (double)size;
      precision = 0; // No fractional byte.
   } else if (size >= CONST64U(1) /* 1 byte */) {
      fmt = "%s byte";
      sizeInSelectedUnit = (double)size;
      precision = 0; // No fractional byte.
   } else {
      ASSERT(size == CONST64U(0) /* 0 bytes */);
      fmt = "%s bytes";
      sizeInSelectedUnit = (double)size;
      precision = 0; // No fractional byte.
   }

   /*
    * We cast to uint32 instead of uint64 here because of a problem with the
    * NetWare Tools build. However, it's safe to cast to uint32 since we have
    * already reduced the range of sizeInSelectedUnit above.
    */
   // If it would display with .0, round it off and display the integer value.
   delta = (uint32)(sizeInSelectedUnit + 0.5) - sizeInSelectedUnit;
   if (delta < 0) {
      delta = -delta;
   }
   if (delta <= epsilon) {
      precision = 0;
      sizeInSelectedUnit = (double)(uint32)(sizeInSelectedUnit + 0.5);
   }

   sizeFormat = Str_Asprintf(NULL, "%%.%uf", precision);
   sizeString = Str_Asprintf(NULL, sizeFormat, sizeInSelectedUnit);
   result = Str_Asprintf(NULL, fmt, sizeString);
   free(sizeFormat);
   free(sizeString);

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * StrUtil_GetLongestLineLength --
 *
 *      Given a buffer with one or more lines
 *      this function computes the length of the
 *      longest line in a buffer.  Input buffer is an array of
 *      arbitrary bytes (including NUL character), line separator
 *      is '\n', and is counted in line length.  Like:
 *        "", 0     => 0
 *        "\n", 1   => 1
 *        "X", 1    => 1
 *        "XX\n", 3 => 3
 *        "X\nY", 3 => 2
 *        "\n\n", 2 => 1
 *
 * Results:
 *      Returns the length of the longest line in the 'buf'.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

size_t
StrUtil_GetLongestLineLength(const char *buf,   //IN
                             size_t bufLength)  //IN
{
    size_t longest = 0;

    while (bufLength) {
       const char *next;
       size_t len;

       ASSERT(buf != NULL);
       /* coverity[var_deref_model] */
       next = memchr(buf, '\n', bufLength);
       if (next) {
          next++;
          len = next - buf;
       } else {
          len = bufLength;
       }
       if (len > longest) {
          longest = len;
       }
       bufLength -= len;
       buf = next;
    }

    return longest;
}


/*
 *----------------------------------------------------------------------
 *
 * StrUtil_StartsWith --
 *
 *      Determines if a string starts with another string.
 *
 * Results:
 *      TRUE if 's' starts with 'prefix', FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
StrUtil_StartsWith(const char *s,      // IN
                   const char *prefix) // IN
{
   ASSERT(s != NULL);
   ASSERT(prefix != NULL);

   while (*prefix && *prefix == *s) {
      prefix++;
      s++;
   }

   return *prefix == '\0';
}


/*
 *----------------------------------------------------------------------
 *
 * StrUtil_CaselessStartsWith --
 *
 *      A case-insensitive version of StrUtil_StartsWith.
 *
 * Results:
 *      TRUE if 's' starts with 'prefix', FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
StrUtil_CaselessStartsWith(const char *s,      // IN
                           const char *prefix) // IN
{
   ASSERT(s != NULL);
   ASSERT(prefix != NULL);

   return Str_Strncasecmp(s, prefix, strlen(prefix)) == 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_EndsWith --
 *
 *      Detects if a string ends with another string.
 *
 * Results:
 *      TRUE  if string 'suffix' is found at the end of string 's'
 *      FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_EndsWith(const char *s,      // IN
                 const char *suffix) // IN
{
   size_t slen;
   size_t suffixlen;

   ASSERT(s != NULL);
   ASSERT(suffix != NULL);

   slen = strlen(s);
   suffixlen = strlen(suffix);

   if (suffixlen > slen) {
      return FALSE;
   }

   return strcmp(s + slen - suffixlen, suffix) == 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_CaselessEndsWith --
 *
 *      A case-insensitive version of StrUtil_EndsWith.
 *
 * Results:
 *      TRUE  if string 'suffix' is found at the end of string 's'
 *      FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_CaselessEndsWith(const char *s,      // IN
                         const char *suffix) // IN
{
   size_t slen;
   size_t suffixlen;

   ASSERT(s != NULL);
   ASSERT(suffix != NULL);
   ASSERT(StrUtil_IsASCII(suffix));

   slen = strlen(s);
   suffixlen = strlen(suffix);

   if (suffixlen > slen) {
      return FALSE;
   }

   return Str_Strcasecmp(s + (slen - suffixlen), suffix) == 0;
}


/**
 *-----------------------------------------------------------------------------
 *
 * StrUtil_CaselessStrstr --
 *
 *    This is a case-insensitive version of strstr in C string.h.
 *
 * Results:
 *    return a pointer to the first occurrence of strSearch in str, or NULL
 *    if strSearch does not appear in str. If strSearch is zero length, the
 *    function returns str.
 *
 * Side effects:
 *    none
 *
 *-----------------------------------------------------------------------------
 */

const char *
StrUtil_CaselessStrstr(const char *str,         // IN
                       const char *strSearch)   // IN
{
   size_t len;

   if (strSearch == NULL || *strSearch == '\0') {
      return str;
   }

   if (str == NULL || *str == '\0') {
      return NULL;
   }

   len = strlen(strSearch);
   for (; *str; str++) {
      if (strncasecmp(str, strSearch, len) == 0) {
         return str;
      }
   }
   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_IsASCII --
 *
 * Results:
 *      Returns TRUE if the string contains only ASCII characters, FALSE
 *      otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_IsASCII(const char *s) // IN
{
   ASSERT(s != NULL);

   while (*s != '\0') {
      if (!CType_IsAscii(*s)) {
         return FALSE;
      }
      s++;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_VDynBufPrintf --
 *
 *      This is a vprintf() variant which appends directly into a
 *      DynBuf.  Does NOT visibly NUL-terminate the DynBuf.
 *
 *      This function does not use any temporary buffer. The printf()
 *      result can be arbitrarily large. This function automatically
 *      grows the DynBuf as necessary.
 *
 * Results:
 *      TRUE on success, FALSE on memory allocation failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_VDynBufPrintf(DynBuf *b,        // IN/OUT
                      const char *fmt,  // IN
                      va_list args)     // IN
{
   /*
    * Arbitrary lower-limit on buffer size allocation, to avoid doing
    * many tiny enlarge operations.
    */

   const size_t minAllocSize = 128;

   while (1) {
      int i;
      size_t size = DynBuf_GetSize(b);
      size_t allocSize = DynBuf_GetAllocatedSize(b);

      /* Make sure the buffer isn't still unallocated */
      if (allocSize < minAllocSize) {
         Bool success = DynBuf_Enlarge(b, minAllocSize);
         if (!success) {
            return FALSE;
         }
         continue;
      }

      /*
       * Is there any allocated-but-not-occupied space? If so, try the printf.
       * If there was no space to begin with, or Str_Vsnprintf() ran out of
       * space, this will fail.
       */

      if (allocSize - size > 0) {
         va_list tmpArgs;

         va_copy(tmpArgs, args);

         /*
          * We actually do NUL-terminate the buffer internally, but this is not
          * visible to callers, and they should not rely on this.
          */
         i = Str_Vsnprintf((char *) DynBuf_Get(b) + size, allocSize - size,
                           fmt, tmpArgs);
         va_end(tmpArgs);
      } else {
         i = -1;
      }

      if (i >= 0) {
         /*
          * Success. Enlarge the buffer.
          *
          * The ASSERT here is to verify that printf() isn't lying
          * about the length of the string it wrote. This actually
          * happens, believe it or not. See bug 253674.
          */

         ASSERT(i + size == allocSize ||
                ((char *)DynBuf_Get(b))[i + size] == '\0');

         DynBuf_SetSize(b, size + i);
         break;

      } else {
         /*
          * Failure. We must grow the buffer first.  Note that this is
          * just a minimum size- dynbuf will probably decide to double
          * the actual reallocated buffer size.
          */

         Bool success = DynBuf_Enlarge(b, size + minAllocSize);

         if (!success) {
            return FALSE;
         }
      }
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_DynBufPrintf --
 *
 *      A StrUtil_VDynBufPrintf() wrapper which takes a variadic argument list.
 *
 * Results:
 *      TRUE on success, FALSE on memory allocation failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_DynBufPrintf(DynBuf *b,        // IN/OUT
                     const char *fmt,  // IN
                     ...)              // IN
{
   va_list args;
   Bool success;

   va_start(args, fmt);
   success = StrUtil_VDynBufPrintf(b, fmt, args);
   va_end(args);

   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_SafeDynBufPrintf --
 *
 *      A 'safe' variant of StrUtil_DynBufPrintf(), which catches
 *      memory allocation failures and panics.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
StrUtil_SafeDynBufPrintf(DynBuf *b,        // IN/OUT
                         const char *fmt,  // IN
                         ...)              // IN
{
   va_list args;
   Bool success;

   va_start(args, fmt);
   success = StrUtil_VDynBufPrintf(b, fmt, args);
   va_end(args);

   VERIFY(success);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_SafeStrcat --
 *
 *      Given an input buffer, append another string and return the resulting
 *      string. The input buffer is freed along the way. A fancy strcat().
 *
 * Results:
 *      New buffer returned via 'prefix'
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
StrUtil_SafeStrcat(char **prefix,    // IN/OUT:
                   const char *str)  // IN:
{
   char *tmp;
   size_t plen = (*prefix == NULL) ? 0 : strlen(*prefix);
   size_t slen = strlen(str);

   /*
    * If we're manipulating strings that are anywhere near max(size_t)/2 in
    * length we're doing something very wrong. Avoid potential overflow by
    * checking for "insane" operations. Prevent the problem before it gets
    * started.
    */

   VERIFY((plen < (SIZE_MAX/2)) && (slen < (SIZE_MAX/2)));

   tmp = Util_SafeRealloc(*prefix, plen + slen + 1 /* NUL */);

   memcpy(tmp + plen, str, slen + 1 /* NUL */);
   *prefix = tmp;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_SafeStrcatFV --
 *
 *      Given an input buffer, append another string and return the resulting
 *      string. The input buffer is freed along the way. A fancy vasprintf().
 *
 * Results:
 *      New buffer returned via 'prefix'
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
StrUtil_SafeStrcatFV(char **prefix,    // IN/OUT
                     const char *fmt,  // IN
                     va_list args)     // IN
{
   char *str = Str_SafeVasprintf(NULL, fmt, args);
   StrUtil_SafeStrcat(prefix, str);
   free(str);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_SafeStrcatF --
 *
 *      Given an input buffer, append another string and return the resulting
 *      string. The input buffer is freed along the way. A fancy asprintf().
 *
 * Results:
 *      New buffer returned via 'prefix'
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
StrUtil_SafeStrcatF(char **prefix,    // IN/OUT
                    const char *fmt,  // IN
                    ...)              // IN
{
   va_list args;

   va_start(args, fmt);
   StrUtil_SafeStrcatFV(prefix, fmt, args);
   va_end(args);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_TrimWhitespace --
 *
 *      Return a copy of the input string with leading and trailing
 *      whitespace removed.
 *
 * Results:
 *      See above. Caller should free with free().
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
StrUtil_TrimWhitespace(const char *str)  // IN
{
   char *cur = (char *)str;
   char *res = NULL;
   size_t len;

   /* Skip leading whitespace. */
   while (*cur && isspace(*cur)) {
      cur++;
   }

   /* Copy the remaining string. */
   res = Util_SafeStrdup(cur);

   /* Find the beginning of the trailing whitespace. */
   len = strlen(res);
   if (len == 0) {
      return res;
   }

   cur = res + len - 1;
   while (cur > res && isspace(*cur)) {
      cur--;
   }

   /* Truncate it. */
   cur++;
   *cur = 0;

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_ReplaceAll --
 *
 *    Replaces all occurrences of a non-empty substring with non-NULL pattern
 *    in non-NULL string.
 *
 * Results:
 *    Returns pointer to the allocated resulting string. The caller is
 *    responsible for freeing it.
 *
 *-----------------------------------------------------------------------------
 */

char *
StrUtil_ReplaceAll(const char *orig, // IN
                   const char *what, // IN
                   const char *with) // IN
{
    char *result;
    const char *current;
    char *tmp;
    size_t lenWhat;
    size_t lenWith;
    size_t occurrences = 0;
    size_t lenNew;

    ASSERT(orig != NULL);
    ASSERT(!StrUtil_IsEmpty(what));
    ASSERT(with != NULL);

    lenWhat = strlen(what);
    lenWith = strlen(with);

    current = orig;
    while ((tmp = strstr(current, what)) != NULL) {
       current = tmp + lenWhat;
       ++occurrences;
    }

    lenNew = strlen(orig) + (lenWith - lenWhat) * occurrences;
    tmp = Util_SafeMalloc(lenNew + 1);
    result = tmp;

    while (occurrences--) {
       size_t lenBefore;

       current = strstr(orig, what);
       lenBefore = current - orig;
       tmp = memcpy(tmp, orig, lenBefore);
       tmp += lenBefore;
       tmp = memcpy(tmp, with, lenWith);
       tmp += lenWith;
       orig += lenBefore + lenWhat;
    }
    memcpy(tmp, orig, strlen(orig));

    result[lenNew] = '\0';

    return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtilStrcmp --
 *
 *      Wraps around the Str_Strcmp macro to provide a function that we
 *      can take the pointer for.
 *
 * Results:
 *      Same as Str_Strcmp.
 *
 * Side effects:
 *      Same as Str_Strcmp.
 *
 *-----------------------------------------------------------------------------
 */

static int
StrUtilStrcmp(const char *s1, const char *s2)
{
   return Str_Strcmp(s1, s2);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtilStrncmp --
 *
 *      Wraps around the Str_Strncmp macro to provide a function that we
 *      can take the pointer for.
 *
 * Results:
 *      Same as Str_Strncmp.
 *
 * Side effects:
 *      Same as Str_Strncmp.
 *
 *-----------------------------------------------------------------------------
 */

static int
StrUtilStrncmp(const char *s1, const char *s2, size_t n)
{
   return Str_Strncmp(s1, s2, n);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtilStrcasecmp --
 *
 *      Wraps around the Str_Strcasecmp macro to provide a function that we
 *      can take the pointer for.
 *
 * Results:
 *      Same as Str_Strcasecmp.
 *
 * Side effects:
 *      Same as Str_Strcasecmp.
 *
 *-----------------------------------------------------------------------------
 */

static int
StrUtilStrcasecmp(const char *s1, const char *s2)
{
   return Str_Strcasecmp(s1, s2);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtilStrncasecmp --
 *
 *      Wraps around the Str_Strncasecmp macro to provide a function that we
 *      can take the pointer for.
 *
 * Results:
 *      Same as Str_Strncasecmp.
 *
 * Side effects:
 *      Same as Str_Strncasecmp.
 *
 *-----------------------------------------------------------------------------
 */

static int
StrUtilStrncasecmp(const char *s1, const char *s2, size_t n)
{
   return Str_Strncasecmp(s1, s2, n);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_GetNextItem --
 *
 *      Extract the next item from a list of items delimited by delim. It
 *      behaves like strsep except it doesn't accept a string of delimiters.
 *
 * Results:
 *      Returns a pointer to the first item and makes list point to the rest of
 *      the list or NULL if list was NULL or there was only one item.
 *
 * Side effects:
 *      The first delimiter is changed to '\0'.
 *
 *-----------------------------------------------------------------------------
 */

char*
StrUtil_GetNextItem(char **list, // IN/OUT:
                    char delim)  // IN:
{
   char *token = *list;
   char *foundDelim;

   if (*list == NULL) {
      return NULL;
   }

   foundDelim = strchr(*list, delim);
   if (foundDelim != NULL) {
      foundDelim[0] = '\0';
      *list = foundDelim + 1;
   } else {
      *list = NULL;
   }

   return token;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_GetLastItem --
 *
 *      Extract the last item from a list of items delimited by delim.
 *
 * Results:
 *      Returns a pointer to the last item and makes list point to the rest of
 *      the list or NULL if list was NULL or there was only one item.
 *
 * Side effects:
 *      The last delimiter is changed to '\0'.
 *
 *-----------------------------------------------------------------------------
 */

char*
StrUtil_GetLastItem(char **list, // IN/OUT:
                    char delim)  // IN:
{
   char *token = *list;
   char *foundDelim;

   if (*list == NULL) {
      return NULL;
   }

   foundDelim = strrchr(*list, delim);
   if (foundDelim != NULL) {
      foundDelim[0] = '\0';
      token = foundDelim + 1;
   } else {
      *list = NULL;
   }

   return token;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtilHasListItem --
 *
 *      Checks whether an item is a part of a list of tokens
 *      separated by a delimiter.
 *
 * Results:
 *      Whether the item exists in the list.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
StrUtilHasListItem(char const *list,                               // IN:
                   char delim,                                     // IN:
                   char const *item,                               // IN:
                   int (*ncmp)(char const *, char const*, size_t)) // IN:
{
   char *foundDelim;
   int itemLen = strlen(item);

   if (list == NULL) {
      return FALSE;
   }

   do {
      int tokenLen;

      foundDelim = strchr(list, delim);
      if (foundDelim == NULL) { // either single or last element
         tokenLen = strlen(list);
      } else { // ! last element
         tokenLen = foundDelim - list;
      }

      if (itemLen == tokenLen && ncmp(item, list, itemLen) == 0) {
         return TRUE;
      } else if (foundDelim != NULL) {
         // ! last element
         list = foundDelim + 1;
      }
   } while (foundDelim != NULL);

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_HasListItem --
 *
 *      Checks whether an item is a part of a list of tokens
 *      separated by a delimiter.
 *
 * Results:
 *      Whether the item exists in the list.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_HasListItem(char const *list,   // IN:
                    char delim,         // IN:
                    char const *item)   // IN:
{
   return StrUtilHasListItem(list, delim, item,
                             &StrUtilStrncmp);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_HasListItemCase --
 *
 *      Checks whether an item is a part of a list of tokens
 *      separated by a delimiter. Case insensitive.
 *
 * Results:
 *      Whether the item exists in the list.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
StrUtil_HasListItemCase(char const *list,   // IN:
                        char delim,         // IN:
                        char const *item)   // IN:
{
   return StrUtilHasListItem(list, delim, item,
                             &StrUtilStrncasecmp);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_AppendListItem --
 *
 *      Insert an item into a list of tokens separated by a delimiter.
 *
 * Results:
 *      A pointer to a new list with the item appended at the end.
 *
 * Side effects:
 *      Allocates a new list.
 *
 *-----------------------------------------------------------------------------
 */

char *
StrUtil_AppendListItem(char const *list,  // IN:
                       char delim,        // IN:
                       char const *item)  // IN:
{
   if (list == NULL) {
      return Str_Asprintf(NULL, "%s", item);
   } else {
      return Str_Asprintf(NULL, "%s%c%s", list, delim, item);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtilRemoveListItem --
 *
 *      Removes first occurence of an item from a list of tokens separated by
 *      a delimiter.
 *
 * Results:
 *      The list is modified in-place and the item is removed.
 *
 * Side effects:
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

static void
StrUtilRemoveListItem(char * const list,                    // IN/OUT:
                      char delim,                           // IN:
                      char const *item,                     // IN:
                      int (*cmp)(char const*, char const*)) // IN:
{
   char *tok;
   char *work = list;
   int maxSize = strlen(list) + 1;

   while ((tok = StrUtil_GetNextItem(&work, delim)) != NULL) {
      if (cmp(tok, item) == 0) { // found the item
         if (work != NULL) { // in the middle of the list
            // overwrite it with the rest of the list
            Str_Strcpy(tok, work, maxSize);
         } else if (tok == list) {
            tok[0] = '\0'; // only item in the list
         } else {
            tok[-1] = '\0'; // or the last element in the list
         }

         return;
      } else if (work != NULL) {
         // restore delimiter that was replaced by Str_GetNextItem
         work[-1] = delim;
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_RemoveListItem --
 *
 *      Removes first occurence of an item from a list of tokens separated by
 *      a delimiter.
 *
 * Results:
 *      The list is modified in-place and the item is removed.
 *
 * Side effects:
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

void
StrUtil_RemoveListItem(char * const list,  // IN/OUT:
                       char delim,         // IN:
                       char const *item)   // IN:
{
   StrUtilRemoveListItem(list, delim, item, &StrUtilStrcmp);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrUtil_RemoveListItemCase --
 *
 *      Remove first occurence of an item from a list of tokens separated by a
 *      delimiter. Case insensitive.
 *
 * Results:
 *      The list is modified in-place and the item is removed.
 *
 * Side effects:
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

void
StrUtil_RemoveListItemCase(char * const list,  // IN/OUT:
                           char delim,         // IN:
                           char const *item)   // IN:
{
   StrUtilRemoveListItem(list, delim, item, &StrUtilStrcasecmp);
}

070701000001A2000081A40000000000000000000000016822550500009C4D000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/timeutil.c    /*********************************************************
 * Copyright (c) 1998-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * timeutil.c --
 *
 *   Miscellaneous time related utility functions.
 */


#include <stdio.h>
#include <time.h>
#include "unicode.h"

#if defined(_WIN32)
#  include <wtypes.h>
#else
#  include <sys/time.h>
#endif
#include <ctype.h>

#include "vmware.h"
#include "vm_basic_asm.h"
#include "timeutil.h"
#include "str.h"
#include "util.h"
#ifdef _WIN32
#include "windowsu.h"
#endif


/*
 * 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)

/*
 * Local Definitions
 */

static void TimeUtilInit(TimeUtil_Date *d);
static Bool TimeUtilLoadDate(TimeUtil_Date *d, const char *date);
static const unsigned int *TimeUtilMonthDaysForYear(unsigned int year);
static Bool TimeUtilIsValidDate(unsigned int year,
                                unsigned int month,
                                unsigned int day);


/*
 * Function to guess Windows TZ Index and Name by using time offset in
 * a lookup table
 */

static int TimeUtilFindIndexAndName(int utcStdOffMins,
                                    const char *englishTzName,
                                    const char **ptzName);


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_MakeTime --
 *
 *    Converts a TimeUtil_Date to a time_t.
 *
 * Results:
 *    A time_t.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

time_t
TimeUtil_MakeTime(const TimeUtil_Date *d)  // IN:
{
   struct tm t;

   ASSERT(d != NULL);

   memset(&t, 0, sizeof t);

   t.tm_mday = d->day;
   t.tm_mon = d->month - 1;
   t.tm_year = d->year - 1900;

   t.tm_sec = d->second;
   t.tm_min = d->minute;
   t.tm_hour = d->hour;
   t.tm_isdst = -1; /* Unknown. */

   return mktime(&t);
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_StringToDate --
 *
 *    Initialize the date object with value from the string argument,
 *    while the time will be left unmodified.
 *    The string 'date' needs to be in the format of 'YYYYMMDD' or
 *    'YYYY/MM/DD' or 'YYYY-MM-DD'.
 *    Unsuccessful initialization will leave the 'd' argument unmodified.
 *
 * Results:
 *    TRUE or FALSE.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

Bool
TimeUtil_StringToDate(TimeUtil_Date *d,  // IN/OUT:
                      char const *date)  // IN:
{
   /*
    * Reduce the string to a known and handled format: YYYYMMDD.
    * Then, passed to internal function TimeUtilLoadDate.
    */

   if (strlen(date) == 8) {
      /* 'YYYYMMDD' */
      return TimeUtilLoadDate(d, date);
   } else if (strlen(date) == 10) {
      /* 'YYYY/MM/DD' */
      char temp[16] = { 0 };

      if (!((date[4] == '/' && date[7] == '/') ||
            (date[4] == '-' && date[7] == '-'))) {
         return FALSE;
      }

      Str_Strcpy(temp, date, sizeof temp);
      temp[4] = date[5];
      temp[5] = date[6];
      temp[6] = date[8];
      temp[7] = date[9];
      temp[8] = '\0';

      return TimeUtilLoadDate(d, temp);
   } else {
      return FALSE;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_DeltaDays --
 *
 *    Calculate the number of days between the two date arguments.
 *    This function ignores the time. It will be as if the time
 *    is midnight (00:00:00).
 *
 * Results:
 *    number of days:
 *    - 0 (if 'left' and 'right' are of the same date (ignoring the time).
 *    - negative, if 'left' is of a later date than 'right'
 *    - positive, if 'right' is of a later date than 'left'
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

int
TimeUtil_DeltaDays(TimeUtil_Date const *left,   // IN:
                   TimeUtil_Date const *right)  // IN:
{
   TimeUtil_Date temp1;
   TimeUtil_Date temp2;
   TimeUtil_Date temp;

   int days = 0;
   Bool inverted = FALSE;

   ASSERT(left);
   ASSERT(right);
   ASSERT(TimeUtilIsValidDate(left->year, left->month, left->day));
   ASSERT(TimeUtilIsValidDate(right->year, right->month, right->day));

   TimeUtilInit(&temp1);
   TimeUtilInit(&temp2);
   TimeUtilInit(&temp);

   temp1.year = left->year;
   temp1.month = left->month;
   temp1.day = left->day;
   temp2.year = right->year;
   temp2.month = right->month;
   temp2.day = right->day;

   if (!TimeUtil_DateLowerThan(&temp1, &temp2) &&
       !TimeUtil_DateLowerThan(&temp2, &temp1)) {
      return 0;
   } else if (TimeUtil_DateLowerThan(&temp1, &temp2)) {
      inverted = FALSE;
   } else if (TimeUtil_DateLowerThan(&temp2, &temp1)) {
      inverted = TRUE;
      temp = temp1;
      temp1 = temp2;
      temp2 = temp;
   }

   days = 1;
   TimeUtil_DaysAdd(&temp1, 1);
   while (TimeUtil_DateLowerThan(&temp1, &temp2)) {
      days++;
      TimeUtil_DaysAdd(&temp1, 1);
   }

   if (inverted) {
      return -days;
   } else {
      return days;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_DaysSubtract --
 *
 *    Subtracts 'nr' days from 'd'.
 *
 *    Simple algorithm - which can be improved as necessary:
 *    - get rough days estimation, also guarantee that the estimation is
 *      lower than the actual result.
 *    - 'add' a day-by-day to arrive at actual result.
 *    'd' will be unchanged if the function failed.
 *
 * TODO:
 *    This function can be combined with DaysAdd(), where it
 *    accepts integer (positive for addition, negative for subtraction).
 *    But, that cannot be done without changing the DaysAdd function
 *    signature.
 *    When this utility get rewritten, this can be updated.
 *
 * Results:
 *    TRUE or FALSE.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

Bool
TimeUtil_DaysSubtract(TimeUtil_Date *d,  // IN/OUT:
                      unsigned int nr)   // IN:
{
   TimeUtil_Date temp;
   int subYear = 0;
   int subMonth = 0;
   int subDay = 0;

   TimeUtil_Date estRes;
   int estYear = 0;
   int estMonth = 0;
   int estDay = 0;

   unsigned int dayCount = nr;

   ASSERT(d);

   TimeUtilInit(&temp);
   TimeUtilInit(&estRes);

   /*
    * Use lower bound for the following conversion:
    * 365 (instead of 366) days in a year
    * 30 (instead of 31) days in a month.
    *
    *   To account for February having fewer than 30 days, we will
    *   intentionally subtract an additional 2 days for each year
    *   and an additional 3 days.
    */

   dayCount = dayCount + 3 + 2 * (dayCount / 365);

   subYear = dayCount / 365;
   dayCount = dayCount % 365;
   subMonth = dayCount / 30;
   subDay = dayCount % 30;

   estDay = d->day - subDay;
   while (estDay <= 0) {
      estDay = estDay + 30;
      subMonth++;
   }
   estMonth = d->month - subMonth;
   while (estMonth <= 0) {
      estMonth = estMonth + 12;
      subYear++;
   }
   estYear = d->year - subYear;
   if (estYear <= 0) {
      return FALSE;
   }

   /*
    * making sure on the valid range, without checking
    * for leap year, etc.
    */

   if ((estDay > 28) && (estMonth == 2)) {
      estDay = 28;
   }

   estRes.year = estYear;
   estRes.month = estMonth;
   estRes.day = estDay;

   /*
    * we also copy the time from the original argument in making
    * sure that it does not play role in the comparison.
    */

   estRes.hour = d->hour;
   estRes.minute = d->minute;
   estRes.second = d->second;

   /*
    * At this point, we should have an estimated result which
    * guaranteed to be lower than the actual result. Otherwise,
    * infinite loop will happen.
    */

   ASSERT(TimeUtil_DateLowerThan(&estRes, d));

   /*
    * Perform the actual precise adjustment
    * Done by moving up (moving forward) the estimated a day at a time
    *    until they are the correct one (i.e. estDate + arg #day = arg date)
    */

   temp = estRes;
   TimeUtil_DaysAdd(&temp, nr);
   while (TimeUtil_DateLowerThan(&temp, d)) {
      TimeUtil_DaysAdd(&temp, 1);
      TimeUtil_DaysAdd(&estRes, 1);
   }

   d->year = estRes.year;
   d->month = estRes.month;
   d->day = estRes.day;

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_DaysAdd --
 *
 *    Add 'nr' days to a date.
 *    This function can be optimized a lot if needed.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
TimeUtil_DaysAdd(TimeUtil_Date *d,  // IN/OUT:
                 unsigned int nr)   // IN:
{
   const unsigned int *monthDays;
   unsigned int i;

   /*
    * Initialize the table
    */

   monthDays = TimeUtilMonthDaysForYear(d->year);

   for (i = 0; i < nr; i++) {
      /*
       * Add 1 day to the date
       */

      d->day++;
      if (d->day > monthDays[d->month]) {
         d->day = 1;
         d->month++;
         if (d->month > 12) {
            d->month = 1;
            d->year++;

            /*
             * Update the table
             */

            monthDays = TimeUtilMonthDaysForYear(d->year);
         }
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_PopulateWithCurrent --
 *
 *    Populate the given date object with the current date and time.
 *
 *    If 'local' is TRUE, the time will be expressed in the local time
 *    zone. Otherwise, the time will be expressed in UTC.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
TimeUtil_PopulateWithCurrent(Bool local,        // IN:
                             TimeUtil_Date *d)  // OUT:
{
#ifdef _WIN32
   SYSTEMTIME currentTime;

   ASSERT(d);

   if (local) {
      GetLocalTime(&currentTime);
   } else {
      GetSystemTime(&currentTime);
   }
   d->year   = currentTime.wYear;
   d->month  = currentTime.wMonth;
   d->day    = currentTime.wDay;
   d->hour   = currentTime.wHour;
   d->minute = currentTime.wMinute;
   d->second = currentTime.wSecond;
#else
   struct tm *currentTime;
   struct tm tmbuf;
   time_t utcTime;

   ASSERT(d);

   utcTime = time(NULL);
   if (local) {
      currentTime = localtime_r(&utcTime, &tmbuf);
   } else {
      currentTime = gmtime_r(&utcTime, &tmbuf);
   }
   VERIFY(currentTime);
   d->year   = 1900 + currentTime->tm_year;
   d->month  = currentTime->tm_mon + 1;
   d->day    = currentTime->tm_mday;
   d->hour   = currentTime->tm_hour;
   d->minute = currentTime->tm_min;
   d->second = currentTime->tm_sec;
#endif // _WIN32
}


/*
 *-----------------------------------------------------------------------------
 *
 * TimeUtil_GetTimeOfDay --
 *
 *      Get the current time for local timezone in seconds and micro-seconds.
 *      Same as gettimeofday on POSIX systems.
 *
 *      May need to use QueryPerformanceCounter API if we need more
 *      refinement/accuracy than what we are doing below.
 *
 * Results:
 *      void
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
TimeUtil_GetTimeOfDay(TimeUtil_TimeOfDay *timeofday)  // OUT:
{

#ifdef _WIN32
   FILETIME ft;
   uint64 tmptime = 0;

   ASSERT(timeofday != NULL);

   /* Get the system time in UTC format. */
   GetSystemTimeAsFileTime(&ft);

   /* Convert the file time to a uint64.  */
   tmptime |= ft.dwHighDateTime;
   tmptime <<= 32;
   tmptime |= ft.dwLowDateTime;

   /*
    * Convert the file time (reported in 100 ns ticks since the Windows epoch)
    * to microseconds.
    */

   tmptime /= 10;  // Microseconds since the Windows epoch

   /*
    * Shift from the Windows epoch to the Unix epoch.
    *
    * Jan 1, 1601 to Jan 1, 1970 (134,774 days)
    */

#define DELTA_EPOCH_IN_MICROSECS  (134774ULL * 24ULL * 3600ULL * 1000000ULL)
   tmptime -= DELTA_EPOCH_IN_MICROSECS;
#undef DELTA_EPOCH_IN_MICROSECS

   /* Return the seconds and microseconds in the timeofday. */
   timeofday->seconds = (unsigned long) (tmptime / 1000000UL);
   timeofday->useconds = (unsigned long) (tmptime % 1000000UL);

#else
   struct timeval curTime;

   ASSERT(timeofday != NULL);

   gettimeofday(&curTime, NULL);
   timeofday->seconds = (unsigned long) curTime.tv_sec;
   timeofday->useconds = (unsigned long) curTime.tv_usec;
#endif // _WIN32

}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_DaysLeft --
 *
 *    Computes the number of days left before a given date
 *
 * Results:
 *    0: the given date is in the past
 *    1 to MAX_DAYSLEFT: if there are 1 to MAX_DAYSLEFT days left
 *    MAX_DAYSLEFT+1 if there are more than MAX_DAYSLEFT days left
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

unsigned int
TimeUtil_DaysLeft(TimeUtil_Date const *d)  // IN:
{
   TimeUtil_Date c;
   unsigned int i;

   /* Get the current local date. */
   TimeUtil_PopulateWithCurrent(TRUE, &c);

   /*
    * Compute how many days we can add to the current date before reaching
    * the given date
    */

   for (i = 0; i < MAX_DAYSLEFT + 1; i++) {
      if ((c.year > d->year) ||
          (c.year == d->year && c.month > d->month) ||
          (c.year == d->year && c.month == d->month && c.day >= d->day)) {
         /* current date >= given date */
         return i;
      }

      TimeUtil_DaysAdd(&c, 1);
   }

   /* There are at least MAX_DAYSLEFT+1 days left */
   return MAX_DAYSLEFT + 1;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_ExpirationLowerThan --
 *
 *    Determine if 'left' is lower than 'right'
 *
 * Results:
 *    TRUE if yes
 *    FALSE if no
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

Bool
TimeUtil_ExpirationLowerThan(TimeUtil_Expiration const *left,   // IN:
                             TimeUtil_Expiration const *right)  // IN:
{
   if (left->expires == FALSE) {
      return FALSE;
   }

   if (right->expires == FALSE) {
      return TRUE;
   }

   if (left->when.year < right->when.year) {
      return TRUE;
   }

   if (left->when.year > right->when.year) {
      return FALSE;
   }

   if (left->when.month < right->when.month) {
      return TRUE;
   }

   if (left->when.month > right->when.month) {
      return FALSE;
   }

   if (left->when.day < right->when.day) {
      return TRUE;
   }

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_DateLowerThan --
 *
 *    Determine if 'left' is lower than 'right'
 *
 * Results:
 *    TRUE if yes
 *    FALSE if no
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

Bool
TimeUtil_DateLowerThan(TimeUtil_Date const *left,   // IN:
                       TimeUtil_Date const *right)  // IN:
{
   ASSERT(left);
   ASSERT(right);

   if (left->year < right->year) {
      return TRUE;
   }

   if (left->year > right->year) {
      return FALSE;
   }

   if (left->month < right->month) {
      return TRUE;
   }

   if (left->month > right->month) {
      return FALSE;
   }

   if (left->day < right->day) {
      return TRUE;
   }

   if (left->day > right->day) {
      return FALSE;
   }

   if (left->hour < right->hour) {
      return TRUE;
   }

   if (left->hour > right->hour) {
      return FALSE;
   }

   if (left->minute < right->minute) {
      return TRUE;
   }

   if (left->minute > right->minute) {
      return FALSE;
   }

   if (left->second < right->second) {
      return TRUE;
   }

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_ProductExpiration --
 *
 *    Retrieve the expiration information associated to the product in 'e'
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
TimeUtil_ProductExpiration(TimeUtil_Expiration *e)  // OUT:
{

   /*
    * The hard_expire string is used by post-build processing scripts to
    * determine if a build is set to expire or not.
    */
#ifdef HARD_EXPIRE
   static char hard_expire[] = "Expire";
   (void)hard_expire;

   ASSERT(e);

   e->expires = TRUE;

   /*
    * Decode the hard-coded product expiration date.
    */

   e->when.day = HARD_EXPIRE;
   e->when.year = e->when.day / ((DATE_MONTH_MAX + 1) * (DATE_DAY_MAX + 1));
   e->when.day -= e->when.year * ((DATE_MONTH_MAX + 1) * (DATE_DAY_MAX + 1));
   e->when.month = e->when.day / (DATE_DAY_MAX + 1);
   e->when.day -= e->when.month * (DATE_DAY_MAX + 1);

   e->daysLeft = TimeUtil_DaysLeft(&e->when);
#else
   static char hard_expire[] = "No Expire";
   (void) hard_expire;

   ASSERT(e);

   e->expires = FALSE;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_GetTimeFormat --
 *
 *    Converts a UTC time value to a human-readable string.
 *
 * Results:
 *    Returns the a formatted string of the given UTC time.  It is the
 *    caller's responsibility to free this string.  May return NULL.
 *
 *    If Win32, the time will be formatted according to the current
 *    locale.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

char *
TimeUtil_GetTimeFormat(int64 utcTime,  // IN:
                       Bool showDate,  // IN:
                       Bool showTime)  // IN:
{
#ifdef _WIN32
   SYSTEMTIME systemTime = { 0 };
   char dateStr[100] = "";
   char timeStr[100] = "";

   if (!showDate && !showTime) {
      return NULL;
   }

   if (!TimeUtil_UTCTimeToSystemTime((const __time64_t) utcTime,
                                      &systemTime)) {
      return NULL;
   }

   Win32U_GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE,
                        &systemTime, NULL, dateStr, ARRAYSIZE(dateStr));

   Win32U_GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systemTime, NULL,
                        timeStr, ARRAYSIZE(timeStr));

   if (showDate && showTime) {
      return Str_SafeAsprintf(NULL, "%s %s", dateStr, timeStr);
   } else {
      return Util_SafeStrdup(showDate ? dateStr : timeStr);
   }

#else
   /*
    * On 32-bit systems the assignment of utcTime to time_t below will truncate
    * in the year 2038.  Ignore it; there's nothing we can do.
    */

   char *str;
   char buf[26];
   const time_t t = (time_t) utcTime;  // Implicit narrowing on 32-bit

#if defined sun
   str = Util_SafeStrdup(ctime_r(&t, buf, sizeof buf));
#else
   str = Util_SafeStrdup(ctime_r(&t, buf));
#endif
   if (str != NULL) {
      str[strlen(str) - 1] = '\0';  // Remove the trailing '\n'.
   }

   return str;
#endif // _WIN32
}


/*
 *-----------------------------------------------------------------------------
 *
 * TimeUtil_NtTimeToUnixTime --
 *
 *    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
TimeUtil_NtTimeToUnixTime(struct timespec *unixTime,  // OUT: Time in Unix format
                          VmTimeType ntTime)          // IN: Time in Windows NT format
{
   ASSERT(unixTime);

   if (ntTime < UNIX_EPOCH) {
      unixTime->tv_sec = 0;
      unixTime->tv_nsec = 0;
      return -1;
   }

#ifdef __i386__
   if (sizeof unixTime->tv_sec == 4) {
      uint32 sec,nsec;

      /* 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;
      }

      Div643232(ntTime - UNIX_EPOCH, 10000000, &sec, &nsec);
      unixTime->tv_sec = sec;
      unixTime->tv_nsec = nsec * 100;

      return 0;
   }
#endif

   unixTime->tv_sec = (ntTime - UNIX_EPOCH) / 10000000;
   unixTime->tv_nsec = ((ntTime - UNIX_EPOCH) % 10000000) * 100;

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * TimeUtil_UnixTimeToNtTime --
 *
 *    Convert from Unix time to Windows NT time.
 *
 * Results:
 *    The time in Windows NT format.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VmTimeType
TimeUtil_UnixTimeToNtTime(struct timespec unixTime)  // IN: Time in Unix format
{
   return (VmTimeType)unixTime.tv_sec * 10000000 +
                                          unixTime.tv_nsec / 100 + UNIX_EPOCH;
}


/*
 *-----------------------------------------------------------------------------
 *
 * TimeUtil_IsValidDate --
 *
 *    Checks out if the given time and date are valid. This function assumes
 *    that any valid minute might contain a leap second.
 *
 * Results:
 *    TRUE if the time and date are valid, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
TimeUtil_IsValidDate(TimeUtil_Date const *d) // IN
{
   if (!TimeUtilIsValidDate(d->year, d->month, d->day)) {
      return FALSE;
   }

   return d->hour < 24 && d->minute < 60 && d->second < 61;
}


#ifdef _WIN32
/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_UTCTimeToSystemTime --
 *
 *    Converts the time from UTC time to SYSTEMTIME
 *
 * Results:
 *    TRUE if the time was converted successfully, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

Bool
TimeUtil_UTCTimeToSystemTime(const __time64_t utcTime,  // IN:
                             SYSTEMTIME *systemTime)    // OUT:
{
   int atmYear;
   int atmMonth;

   struct tm *atm;

   /*
    * _localtime64 support years up through 3000.  At least it says
    * so.  I'm getting garbage only after reaching year 4408.
    */

   if (utcTime < 0 || utcTime > (60LL * 60 * 24 * 365 * (3000 - 1970))) {
      return FALSE;
   }

   atm = _localtime64(&utcTime);
   if (atm == NULL) {
      return FALSE;
   }

   atmYear = atm->tm_year + 1900;
   atmMonth = atm->tm_mon + 1;

   /*
    * Windows's SYSTEMTIME documentation says that these are limits...
    * Main reason for this test is to cut out negative values _localtime64
    * likes to return for some inputs.
    */

   if (atmYear < 1601 || atmYear > 30827 ||
       atmMonth < 1 || atmMonth > 12 ||
       atm->tm_wday < 0 || atm->tm_wday > 6 ||
       atm->tm_mday < 1 || atm->tm_mday > 31 ||
       atm->tm_hour < 0 || atm->tm_hour > 23 ||
       atm->tm_min < 0 || atm->tm_min > 59 ||
       /* Allow leap second, just in case... */
       atm->tm_sec < 0 || atm->tm_sec > 60) {
      return FALSE;
   }

   systemTime->wYear         = (WORD) atmYear;
   systemTime->wMonth        = (WORD) atmMonth;
   systemTime->wDayOfWeek    = (WORD) atm->tm_wday;
   systemTime->wDay          = (WORD) atm->tm_mday;
   systemTime->wHour         = (WORD) atm->tm_hour;
   systemTime->wMinute       = (WORD) atm->tm_min;
   systemTime->wSecond       = (WORD) atm->tm_sec;
   systemTime->wMilliseconds = 0;

   return TRUE;
}
#endif // _WIN32


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_GetLocalWindowsTimeZoneIndexAndName --
 *
 *    Determines the name and index for the computer's current time zone.  The
 *    name is always the name of the time zone in standard time, even if
 *    Daylight Saving is currently in effect. This name is not localized, and
 *    isintended to be used when Easy Installing a Vista or later guest.
 *
 * Results:
 *    The index of the computer's current time zone.  The name of the time zone
 *    in standard time is returned in *ptzName.  The caller is responsible for
 *    freeing the returned string with free.
 *    If an error occurs, returns -1 and sets *ptzName to NULL.
 *
 * Side effects:
 *    On non-Win32 platforms, calls localtime_r() which sets globals
 *    variables (e.g. 'timezone' and 'tzname' for Linux)
 *
 *----------------------------------------------------------------------
 */

int
TimeUtil_GetLocalWindowsTimeZoneIndexAndName(char **ptzName)  // OUT: returning TZ Name
{
   int utcStdOffMins = 0;
   int winTimeZoneIndex = (-1);
   const char *tzNameByUTCOffset = NULL;
   char *englishTzName = NULL;

   *ptzName = NULL;

#if defined(_WIN32)
   {
      DYNAMIC_TIME_ZONE_INFORMATION tzInfo = {0};

      if (GetDynamicTimeZoneInformation(&tzInfo) == TIME_ZONE_ID_INVALID) {
         return (-1);
      }

      /*
       * Save the unlocalized time zone name.  We use it below to look up the
       * time zone's index.
       */
      englishTzName = Unicode_AllocWithUTF16(tzInfo.TimeZoneKeyName);

      /* 'Bias' = diff between UTC and local standard time */
      utcStdOffMins = 0 - tzInfo.Bias; // already in minutes
   }

#else // NOT _WIN32

   {
      /*
       * Use localtime_r() to get offset between our local
       * time and UTC. This varies by platform. Also, the structure
       * fields are named "*gmt*" but the man pages claim offsets are
       * to UTC, not GMT.
       */

      time_t now = time(NULL);
      struct tm tim;
      localtime_r(&now, &tim);

      #if defined(sun)
         /*
          * Offset is to standard (no need for DST adjustment).
          * Negative is east of prime meridian.
          */

         utcStdOffMins = 0 - timezone/60;
      #else
         /*
          * FreeBSD, Apple, Linux only:
          * Offset is to local (need to adjust for DST).
          * Negative is west of prime meridian.
          */

         utcStdOffMins = tim.tm_gmtoff/60;
         if (tim.tm_isdst) {
            utcStdOffMins -= 60;
         }
      #endif

      /* can't figure this out directly for non-Win32 */
      winTimeZoneIndex = (-1);
   }

#endif

   /* Look up the name and index in a table. */
   winTimeZoneIndex = TimeUtilFindIndexAndName(utcStdOffMins, englishTzName,
                                               &tzNameByUTCOffset);

   if (winTimeZoneIndex >= 0) {
      *ptzName = Unicode_AllocWithUTF8(tzNameByUTCOffset);
   }

   free(englishTzName);
   englishTzName = NULL;

   return winTimeZoneIndex;
}


/*
 ***********************************************************************
 *
 * Local Functions
 *
 ***********************************************************************
 */

/*
 *----------------------------------------------------------------------
 *
 * TimeUtilInit --
 *
 *    Initialize everything to zero
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

static void
TimeUtilInit(TimeUtil_Date *d)  // OUT:
{
   ASSERT(d);

   d->year = 0;
   d->month = 0;
   d->day = 0;
   d->hour = 0;
   d->minute = 0;
   d->second = 0;

   return;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtilIsValidDate --
 *
 *    Check whether the args represent a valid date.
 *
 * Results:
 *    TRUE or FALSE.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

static Bool
TimeUtilIsValidDate(unsigned int year,   // IN:
                    unsigned int month,  // IN:
                    unsigned int day)    // IN:
{
   const unsigned int *monthDays;

   /*
    * Initialize the table
    */

   monthDays = TimeUtilMonthDaysForYear(year);

   if ((year >= 1) &&
       (month >= 1) && (month <= 12) &&
       (day >= 1) && (day <= monthDays[month])) {
      return TRUE;
   }

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtilMonthDaysForYear --
 *
 *    Return an array of days in months depending on whether the
 *    argument represents a leap year.
 *
 * Results:
 *    A pointer to an array of 13 ints representing the days in the
 *    12 months.  There are 13 entries because month is 1-12.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

static unsigned int const *
TimeUtilMonthDaysForYear(unsigned int year)  // IN:
{
   static const unsigned int leap[13] =
                   { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
   static const unsigned int common[13] =
                   { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

   return ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) ?
           leap : common;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtilLoadDate --
 *
 *    Initialize the date object with value from the string argument,
 *    while the time will be left unmodified.
 *    The string 'date' needs to be in the format of 'YYYYMMDD'.
 *    Unsuccesful initialization will leave the 'd' argument unmodified.
 *
 * Results:
 *    TRUE or FALSE.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

static Bool
TimeUtilLoadDate(TimeUtil_Date *d,  // IN/OUT:
                 const char *date)  // IN:
{
   char temp[16] = { 0 };
   int i = 0;
   char *end = NULL;

   int32 year = 0;
   int32 month = 0;
   int32 day = 0;

   ASSERT(d);
   ASSERT(date);

   if (strlen(date) != 8) {
      return FALSE;
   }
   for (i = 0; i < strlen(date); i++) {
      if (isdigit((int) date[i]) == 0) {
         return FALSE;
      }
   }

   temp[0] = date[0];
   temp[1] = date[1];
   temp[2] = date[2];
   temp[3] = date[3];
   temp[4] = '\0';
   year = strtol(temp, &end, 10);
   if (*end != '\0') {
      return FALSE;
   }

   temp[0] = date[4];
   temp[1] = date[5];
   temp[2] = '\0';
   month = strtol(temp, &end, 10);
   if (*end != '\0') {
      return FALSE;
   }

   temp[0] = date[6];
   temp[1] = date[7];
   temp[2] = '\0';
   day = strtol(temp, &end, 10);
   if (*end != '\0') {
      return FALSE;
   }

   if (!TimeUtilIsValidDate((unsigned int) year, (unsigned int) month,
                            (unsigned int) day)) {
      return FALSE;
   }

   d->year = (unsigned int) year;
   d->month = (unsigned int) month;
   d->day = (unsigned int) day;

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtilFindIndexAndName --
 *
 *    Given a time zone's offset from UTC and optionally its name, returns
 *    the time zone's Windows index and its name in standard time.
 *
 * Results:
 *    If the time zone is found in the table, returns the index and stores
 *    the name in *ptzName.  The caller must not free the returned string.
 *    If the time zone is not found, returns -1 and sets *ptzName to NULL.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

static int
TimeUtilFindIndexAndName(int utcStdOffMins,          // IN: offset (in minutes)
                         const char *englishTzName,  // IN/OPT: The English TZ name
                         const char **ptzName)       // OUT: returning TZ Name
{
   static struct _tzinfo {
      int         winTzIndex;
      const char *winTzName;
      int         utcStdOffMins;
   } TABLE[] = {

      /*
       * These values are from Microsoft's TimeZone documentation:
       *
       * http://technet.microsoft.com/en-us/library/cc749073.aspx
       *
       * All time zones that have the same offset must be grouped together.
       */

      {   0, "Dateline Standard Time",          -720 }, // -12
      {   2, "Hawaiian Standard Time",          -600 }, // -10
      {   3, "Alaskan Standard Time",           -540 }, // -9
      {   4, "Pacific Standard Time",           -480 }, // -8
      {  10, "Mountain Standard Time",          -420 }, // -7
      {  13, "Mountain Standard Time (Mexico)", -420 }, // -7
      {  15, "US Mountain Standard Time",       -420 }, // -7
      {  20, "Central Standard Time",           -360 }, // -6
      {  25, "Canada Central Standard Time",    -360 }, // -6
      {  30, "Central Standard Time (Mexico)",  -360 }, // -6
      {  33, "Central America Standard Time",   -360 }, // -6
      {  35, "Eastern Standard Time",           -300 }, // -5
      {  40, "US Eastern Standard Time",        -300 }, // -5
      {  45, "SA Pacific Standard Time",        -300 }, // -5
      {  50, "Atlantic Standard Time",          -240 }, // -4
      {  55, "SA Western Standard Time",        -240 }, // -4
      {  56, "Pacific SA Standard Time",        -240 }, // -4
      {  60, "Newfoundland Standard Time",      -210 }, // -3.5
      {  65, "E. South America Standard Time",  -180 }, // -3
      {  70, "SA Eastern Standard Time",        -180 }, // -3
      {  73, "Greenland Standard Time",         -180 }, // -3
      {  75, "Mid-Atlantic Standard Time",      -120 }, // -2
      {  80, "Azores Standard Time",             -60 }, // -1
      {  83, "Cape Verde Standard Time",         -60 }, // -1
      {  85, "GMT Standard Time",                  0 }, // 0
      {  90, "Greenwich Standard Time",            0 }, // 0
      { 110, "W. Europe Standard Time",           60 }, // +1
      {  95, "Central Europe Standard Time",      60 }, // +1
      { 100, "Central European Standard Time",    60 }, // +1
      { 105, "Romance Standard Time",             60 }, // +1
      { 113, "W. Central Africa Standard Time",   60 }, // +1
      { 115, "E. Europe Standard Time",          120 }, // +2
      { 120, "Egypt Standard Time",              120 }, // +2
      { 125, "FLE Standard Time",                120 }, // +2
      { 130, "GTB Standard Time",                120 }, // +2
      { 135, "Israel Standard Time",             120 }, // +2
      { 140, "South Africa Standard Time",       120 }, // +2
      { 150, "Arab Standard Time",               180 }, // +3
      { 155, "E. Africa Standard Time",          180 }, // +3
      { 158, "Arabic Standard Time",             180 }, // +3
      { 160, "Iran Standard Time",               210 }, // +3.5
      { 145, "Russian Standard Time",            240 }, // +4
      { 165, "Arabian Standard Time",            240 }, // +4
      { 170, "Caucasus Standard Time",           240 }, // +4
      { 175, "Afghanistan Standard Time",        270 }, // +4.5
      { 185, "West Asia Standard Time",          300 }, // +5
      { 190, "India Standard Time",              330 }, // +5.5
      { 200, "Sri Lanka Standard Time",          330 }, // +5.5
      { 193, "Nepal Standard Time",              345 }, // +5.75
      { 180, "Ekaterinburg Standard Time",       360 }, // +6
      { 195, "Central Asia Standard Time",       360 }, // +6
      { 203, "Myanmar Standard Time",            390 }, // +6.5
      { 201, "N. Central Asia Standard Time",    420 }, // +7
      { 205, "SE Asia Standard Time",            420 }, // +7
      { 210, "China Standard Time",              480 }, // +8
      { 207, "North Asia Standard Time",         480 }, // +8
      { 215, "Singapore Standard Time",          480 }, // +8
      { 220, "Taipei Standard Time",             480 }, // +8
      { 225, "W. Australia Standard Time",       480 }, // +8
      { 235, "Tokyo Standard Time",              540 }, // +9
      { 230, "Korea Standard Time",              540 }, // +9
      { 227, "North Asia East Standard Time",    540 }, // +9
      { 245, "AUS Central Standard Time",        570 }, // +9.5
      { 250, "Cen. Australia Standard Time",     570 }, // +9.5
      { 255, "AUS Eastern Standard Time",        600 }, // +10
      { 260, "E. Australia Standard Time",       600 }, // +10
      { 265, "Tasmania Standard Time",           600 }, // +10
      { 240, "Yakutsk Standard Time",            600 }, // +10
      { 275, "West Pacific Standard Time",       600 }, // +10
      { 280, "Central Pacific Standard Time",    660 }, // +11
      { 270, "Vladivostok Standard Time",        660 }, // +11
      { 290, "New Zealand Standard Time",        720 }, // +12
      { 285, "Fiji Standard Time",               720 }, // +12
      {   1, "Samoa Standard Time",              780 }, // +13
      { 300, "Tonga Standard Time",              780 }};// +13

   size_t tableSize = ARRAYSIZE(TABLE);
   size_t look;
   int tzIndex = (-1);

   *ptzName = NULL;

   /*
    * Search for the first time zone that has the passed-in offset.  Then, if
    * the caller also passed a name, search the time zones with that offset for
    * a time zone with that name.
    *
    * If the caller does not pass a name, this loop returns the first time zone
    * that it finds with the given offset.  Because the UTC offset does not
    * uniquely identify a time zone, this function can return a time zone that
    * is not what the caller intended.  For an example, see bug 1159642.
    * Callers should pass a time zone name whenever possible.
    */

   for (look = 0; look < tableSize; look++) {
      if (TABLE[look].utcStdOffMins == utcStdOffMins) {
         /* We found a time zone with the right offset. */
         tzIndex = TABLE[look].winTzIndex;
         *ptzName = TABLE[look].winTzName;

         /* Compare names if the caller passed a name. */
         while (englishTzName != NULL &&
                look < tableSize &&
                TABLE[look].utcStdOffMins == utcStdOffMins) {
            if (strcmp(englishTzName, TABLE[look].winTzName) == 0) {
               *ptzName = TABLE[look].winTzName;
               return TABLE[look].winTzIndex;
            }

            look++;
         }

         break;
      }
   }

   return tzIndex;
}


/*
 *----------------------------------------------------------------------
 *
 * TimeUtil_SecondsSinceEpoch --
 *
 *    Converts a date into the the number of seconds since the unix epoch in
 *    UTC.
 *
 * Parameters:
 *    date to be converted.
 *
 * Results:
 *    Returns the numbers of seconds since the unix epoch.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

time_t
TimeUtil_SecondsSinceEpoch(TimeUtil_Date *d)  // IN:
{
   struct tm tmval = {0};

   /*
    * We can't handle negative time.
    */
   if (d->year < 1970) {
      ASSERT(0);
      return -1;
   }

   tmval.tm_year = d->year - 1900;
   tmval.tm_mon = d->month - 1;
   tmval.tm_mday = d->day;
   tmval.tm_hour = d->hour;
   tmval.tm_min = d->minute;
   tmval.tm_sec = d->second;

#if defined(_WIN32)
   /*
   * Workaround since Win32 doesn't have timegm(). Use the win32
   * _get_timezone to adjust to UTC.
   */
   {
      int utcSeconds = 0;

      _get_timezone(&utcSeconds);

      return mktime(&tmval) - utcSeconds;
   }
#elif (defined(__linux__) || defined(__APPLE__)) && !defined(__ANDROID__)
   return timegm(&tmval);
#else
   NOT_IMPLEMENTED();
   return -1;
#endif
}
   070701000001A3000081A400000000000000000000000168225505000046A7000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/utilMem.c /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * utilMem.c --
 *
 *    misc util functions
 */

#ifdef _WIN32
#include <windows.h>
#endif
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>

#include "vm_assert.h"
#include "util.h"

#ifdef __APPLE__
#include <TargetConditionals.h>
#if !defined TARGET_OS_IPHONE
#define TARGET_OS_IPHONE 0
#endif
#endif

static NORETURN void UtilAllocationFailure0(void);
static NORETURN void UtilAllocationFailure1(int bugNumber,
                                            const char *file, int lineno);

Bool UtilConstTimeMemDiff(const void *secret, const void *guess, size_t len, size_t *diffCount);
Bool UtilConstTimeStrDiff(const char *secret, const char *guess, size_t *diffCount);


static void
UtilAllocationFailure0(void)
{
   Panic("Unrecoverable memory allocation failure\n");
}


static void
UtilAllocationFailure1(int bugNumber, const char *file, int lineno)
{
   if (bugNumber == -1) {
      Panic("Unrecoverable memory allocation failure at %s:%d\n",
            file, lineno);
   } else {
      Panic("Unrecoverable memory allocation failure at %s:%d.  Bug "
            "number: %d\n", file, lineno, bugNumber);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilSafeMalloc0 --
 * UtilSafeMalloc1 --
 *
 *      Helper functions for Util_SafeMalloc.
 *
 * Results:
 *      Pointer to the dynamically allocated memory.
 *
 * Side effects:
 *      May Panic if ran out of memory.
 *
 *-----------------------------------------------------------------------------
 */

void *
UtilSafeMalloc0(size_t size)            // IN:
{
   void *result = malloc(size);
   if (result == NULL && size != 0) {
      UtilAllocationFailure0();
   }
   return result;
}


void *
UtilSafeMalloc1(size_t size,            // IN:
                int bugNumber,          // IN:
                const char *file,       // IN:
                int lineno)             // IN:
{
   void *result = malloc(size);
   if (result == NULL && size != 0) {
      UtilAllocationFailure1(bugNumber, file, lineno);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilSafeRealloc0 --
 * UtilSafeRealloc1 --
 *
 *      Helper functions for Util_SafeRealloc.
 *
 * Results:
 *      Pointer to the dynamically allocated memory.
 *
 * Side effects:
 *      May Panic if ran out of memory.
 *
 *-----------------------------------------------------------------------------
 */

void *
UtilSafeRealloc0(void *ptr,            // IN:
                 size_t size)          // IN:
{
   void *result = realloc(ptr, size);
   if (result == NULL && size != 0) {
      UtilAllocationFailure0();
   }
   return result;
}


void *
UtilSafeRealloc1(void *ptr,            // IN:
                 size_t size,          // IN:
                 int bugNumber,        // IN:
                 const char *file,     // IN:
                 int lineno)           // IN:
{
   void *result = realloc(ptr, size);
   if (result == NULL && size != 0) {
      UtilAllocationFailure1(bugNumber, file, lineno);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilSafeCalloc0 --
 * UtilSafeCalloc1 --
 *
 *      Helper functions for Util_SafeCalloc.
 *
 * Results:
 *      Pointer to the dynamically allocated memory.
 *
 * Side effects:
 *      May Panic if ran out of memory.
 *
 *-----------------------------------------------------------------------------
 */

void *
UtilSafeCalloc0(size_t nmemb,         // IN:
                size_t size)          // IN:
{
   void *result = calloc(nmemb, size);
   if (result == NULL && nmemb != 0 && size != 0) {
      UtilAllocationFailure0();
   }
   return result;
}


void *
UtilSafeCalloc1(size_t nmemb,         // IN:
                size_t size,          // IN:
                int bugNumber,        // IN:
                const char *file,     // IN:
                int lineno)           // IN:
{
   void *result = calloc(nmemb, size);
   if (result == NULL && nmemb != 0 && size != 0) {
      UtilAllocationFailure1(bugNumber, file, lineno);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilSafeStrdup0 --
 * UtilSafeStrdup1 --
 *
 *      Helper functions for Util_SafeStrdup.
 *
 * Results:
 *      Pointer to the dynamically allocated, duplicate string
 *
 * Side effects:
 *      May Panic if ran out of memory.
 *
 *-----------------------------------------------------------------------------
 */

char *
UtilSafeStrdup0(const char *s)        // IN:
{
   char *result;
   if (s == NULL) {
      return NULL;
   }

#if defined(_WIN32)
   if ((result = _strdup(s)) == NULL) {
#else
   if ((result = strdup(s)) == NULL) {
#endif
      UtilAllocationFailure0();
   }
   return result;
}


char *
UtilSafeStrdup1(const char *s,        // IN:
                int bugNumber,        // IN:
                const char *file,     // IN:
                int lineno)           // IN:
{
   char *result;
   if (s == NULL) {
      return NULL;
   }
#if defined(_WIN32)
   if ((result = _strdup(s)) == NULL) {
#else
   if ((result = strdup(s)) == NULL) {
#endif
      UtilAllocationFailure1(bugNumber, file, lineno);
   }
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilSafeStrndup0 --
 * UtilSafeStrndup1 --
 *
 *      Helper functions for Util_SafeStrndup.
 *
 *      Returns a string consisting of first n characters of 's' if 's' has
 *      length >= 'n', otherwise returns a string duplicate of 's'.
 *
 * Results:
 *      Pointer to the duplicated string.
 *
 * Side effects:
 *      May Panic if ran out of memory.
 *
 *-----------------------------------------------------------------------------
 */

char *
UtilSafeStrndup0(const char *s,        // IN:
                 size_t n)             // IN:
{
   size_t size;
   char *copy;
   const char *null;
   size_t newSize;

   if (s == NULL) {
      return NULL;
   }

   null = memchr(s, '\0', n);
   size = null ? (size_t)(null - s) : n;
   newSize = size + 1;
   if (newSize < size) {  // Prevent integer overflow
      copy = NULL;
   } else {
      copy = malloc(newSize);
   }

   if (copy == NULL) {
      UtilAllocationFailure0();
   }

   copy[size] = '\0';

   return memcpy(copy, s, size);
}


char *
UtilSafeStrndup1(const char *s,        // IN:
                 size_t n,             // IN:
                 int bugNumber,        // IN:
                 const char *file,     // IN:
                 int lineno)           // IN:
{
   size_t size;
   char *copy;
   const char *null;
   size_t newSize;

   if (s == NULL) {
      return NULL;
   }

   null = memchr(s, '\0', n);
   size = null ? (size_t)(null - s) : n;
   newSize = size + 1;
   if (newSize < size) {  // Prevent integer overflow
      copy = NULL;
   } else {
      copy = malloc(newSize);
   }

   if (copy == NULL) {
      UtilAllocationFailure1(bugNumber, file, lineno);
   }

   copy[size] = '\0';

   return memcpy(copy, s, size);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_Memdup --
 *
 *      Allocates a copy of data.
 *
 * Results:
 *      Returns a pointer to the allocated copy.  The caller is responsible for
 *      freeing it with free().  Returns NULL on failure or if the input size
 *      is 0.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void *
Util_Memdup(const void *src, // IN:
            size_t size)     // IN:
{
   void *dest;

   if (size == 0) {
      return NULL;
   }

   ASSERT(src != NULL);

   dest = malloc(size);
   if (dest != NULL) {
      Util_Memcpy(dest, src, size);
   }
   return dest;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_Memcpy --
 *
 *      Version of memcpy intended to accelerate aligned copies.
 *
 *      Expected benefits:
 *      2-4x performance improvemenet for small buffers (count <= 256 bytes)
 *      Equivalent performance on mid-sized buffers (256 bytes < count < 4K)
 *      ~25% performance improvement on large buffers (4K < count)
 *
 *      Has a drawback that falling through to standard memcpy has overhead
 *      of 5 instructions and 2 branches.
 *
 * Results:
 *      Returns a pointer to the destination buffer.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void *
Util_Memcpy(void *dest,      // OUT:
            const void *src, // IN:
            size_t count)    // IN:
{
#if defined(__x86_64__) || defined(__i386__)
   uintptr_t align = ((uintptr_t)dest | (uintptr_t)src | count);

#if defined __GNUC__

   #if defined(__x86_64__)

      size_t dummy0;
      void *dummy1;
      void *dummy2;

      if ((align & 7) == 0) {
         __asm__ __volatile__("\t"
                               "cld"            "\n\t"
                               "rep ; movsq"    "\n"
                               : "=c" (dummy0), "=D" (dummy1), "=S" (dummy2)
                               : "0" (count >> 3), "1" (dest), "2" (src)
                               : "memory", "cc"
            );
         return dest;
      } else if ((align & 3) == 0) {
         __asm__ __volatile__("\t"
                               "cld"            "\n\t"
                               "rep ; movsd"    "\n"
                               : "=c" (dummy0), "=D" (dummy1), "=S" (dummy2)
                               : "0" (count >> 2), "1" (dest), "2" (src)
                               : "memory", "cc"
            );
         return dest;
      }

   #elif defined(__i386__)

      size_t dummy0;
      void *dummy1;
      void *dummy2;

      if ((align & 3) == 0) {
         __asm__ __volatile__("\t"
                               "cld"            "\n\t"
                               "rep ; movsd"    "\n"
                               : "=c" (dummy0), "=D" (dummy1), "=S" (dummy2)
                               : "0" (count >> 2), "1" (dest), "2" (src)
                               : "memory", "cc"
            );
         return dest;
      }

   #endif

#elif defined _MSC_VER

   #if defined(__x86_64__)

      if ((align & 7) == 0) {
         __movsq((uint64 *)dest, (uint64 *)src, count >> 3);
         return dest;
      } else if ((align & 3) == 0) {
         __movsd((unsigned long *)dest, (unsigned long *)src, count >> 2);
         return dest;
      }

   #elif defined(__i386__)

      if ((((uintptr_t)dest | (uintptr_t)src | count) & 3) == 0) {
         __movsd((unsigned long *)dest, (unsigned long *)src, count >> 2);
         return dest;
      }

   #endif


#endif
#endif

   memcpy(dest, src, count);
   return dest;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_Memfree --
 *
 *      Frees the memory space pointed to by ptr.
 *
 *      The reason why this function is externally visible (not static)
 *      is to avoid freeing memory across dll boundary.
 *      In vmwarebase, we have many API that return newly allocated memory
 *      to the caller. If the caller linked against a different msvc runtime
 *      (for example, vmrest linked against msvcrt.dll), we will crash.
 *      Using Util_Memfree() can avoid this kind of problem, since it sits
 *      inside vmwarebase too. It will call the right free(), the one that
 *      match the malloc() used in vmwarebase.
 *
 * Results:
 *      The memory space pointed to by ptr will be freed.
 *      If ptr is NULL, no operation is performed.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Util_Memfree(void *ptr) // IN:
{
   free(ptr);
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilConstTimeMemDiff --
 *
 *       The implementation of a constant time memory comparison.  Unlike
 *       memcmp, this function does not return early if it finds a mismatch.
 *       It always examines the entire 'secret' and 'guess' buffers, so that
 *       the time spent in this function is constant for buffers of the same
 *       given 'len'.  (We don't attempt to make the time invariant for
 *       different buffer lengths.)
 *
 *       The reason why this function is externally visible (not static)
 *       and has a 'diffCount' argument is to try to prevent aggressive
 *       compiler optimization levels from short-circuiting the inner loop.
 *       The possibility of a call from outside this module with a non-NULL
 *       diffCount pointer prevents that optimization.  If we didn't have
 *       to worry about that then we wouldn't need this function; we could
 *       have put the implementation directly into Util_ConstTimeMemDiff.
 *
 * Results:
 *       Returns true if the buffers differ, false if they are identical.
 *       If diffCount is non-NULL, sets *diffCount to the total number of
 *       differences between the buffers.
 *
 * Side Effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
UtilConstTimeMemDiff(const void *secret,    // IN
                     const void *guess,     // IN
                     size_t len,            // IN
                     size_t *diffCount)     // OUT: optional
{
   const char *secretChar = secret;
   const char *guessChar = guess;

   size_t numDiffs = 0;

   while (len--) {
      numDiffs += !!(*secretChar ^ *guessChar);
      ++secretChar;
      ++guessChar;
   }

   if (diffCount != NULL) {
      *diffCount = numDiffs;
   }
   return numDiffs != 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_ConstTimeMemDiff --
 *
 *       Performs a constant time memory comparison.
 *
 *       The return values are chosen to make this as close as possible to
 *       a drop-in replacement for memcmp, so we return false (0) if the
 *       buffers match (ie there are zero differences) and true (1) if the
 *       buffers differ.
 *
 * Results:
 *       Returns zero if the buffers are identical, 1 if they differ.
 *
 * Side Effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Util_ConstTimeMemDiff(const void *secret,  // IN
                      const void *guess,   // IN
                      size_t len)          // IN
{
   return UtilConstTimeMemDiff(secret, guess, len, NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilConstTimeStrDiff --
 *
 *       The implementation of a constant time string comparison.  Unlike
 *       strcmp, this function does not return early if it finds a mismatch.
 *       It always compares the entire 'secret' string against however much
 *       of the 'guess' string is required for that comparison, so that the
 *       time spent in this function is constant for secrets of the same
 *       length.  (We don't attempt to make the time invariant for secrets
 *       of different lengths.)
 *
 *       The reason why this function is externally visible (not static)
 *       and has a 'diffCount' argument is to try to prevent aggressive
 *       compiler optimization levels from short-circuiting the inner
 *       loop.  The possibility of a call from outside this module with a
 *       non-NULL diffCount pointer prevents that optimization.  If we
 *       didn't have to worry about that then we wouldn't need this
 *       function; we could have put the implementation directly into
 *       Util_ConstTimeStrDiff.
 *
 * Results:
 *       Returns true if the strings differ, false if they are identical.
 *       If diffCount is non-NULL, sets *diffCount to the total number of
 *       differences between the strings.
 *
 * Side Effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
UtilConstTimeStrDiff(const char *secret,    // IN
                     const char *guess,     // IN
                     size_t *diffCount)     // OUT: optional
{
   size_t numDiffs = 0;

   do {
      numDiffs += !!(*secret ^ *guess);
      guess += !!(*guess);
   } while (*secret++);

   if (diffCount != NULL) {
      *diffCount = numDiffs;
   }
   return numDiffs != 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_ConstTimeStrDiff --
 *
 *       The implementation of a constant time string comparison.
 *
 *       The return values are chosen to make this as close as possible
 *       to a drop-in replacement for strcmp, so we return 0 if
 *       the buffers match (ie there are zero differences) and 1
 *       if the buffers differ.
 *
 * Results:
 *       Returns zero if the strings are identical, 1 if they differ.
 *
 * Side Effects:
 *       None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Util_ConstTimeStrDiff(const char *secret,  // IN
                      const char *guess)   // IN
{
   return UtilConstTimeStrDiff(secret, guess, NULL);
}
 070701000001A4000081A40000000000000000000000016822550500006DEB000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/util_misc.c   /*********************************************************
 * Copyright (C) 1998-2019, 2021-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * util.c --
 *
 *    misc util functions
 */

#if defined(_WIN32)
#include <winsock2.h> // also includes windows.h
#include <io.h>
#include <process.h>
#define getpid() _getpid()
#endif

#include "vm_ctype.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stddef.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>

#if !defined(_WIN32)
#  include <unistd.h>
#  include <pwd.h>
#  include <sys/socket.h>    // for AF_INET[6]
#  include <arpa/inet.h>     // for inet_pton
#  include <netinet/in.h>    // for INET6_ADDRSTRLEN
#endif

#if defined(__APPLE__)
#include <sys/stat.h>
#include <CoreFoundation/CoreFoundation.h>
#endif

#include "vmware.h"
#include "msg.h"
#include "util.h"
#include "str.h"
#include "su.h"
#include "escape.h"
#include "posix.h"
#include "unicode.h"

#if defined(_WIN32)
#include "windowsu.h"
#include "windowsUtil.h"
#endif
#if defined(__APPLE__)
#include "utilMacos.h"
#endif

/*
 * ESX with userworld VMX
 */
#if defined(VMX86_SERVER)
#include "hostType.h"
#include "user_layout.h"
#endif


char *gHomeDirOverride = NULL;


/*
 *-----------------------------------------------------------------------------
 *
 * Util_GetCanonicalPath --
 *
 *      Canonicalizes a path name.
 *
 * Results:
 *      A freshly allocated canonicalized path name.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Util_GetCanonicalPath(const char *path)  // IN:
{
   char *canonicalPath = NULL;

#if defined(__linux__) || defined(__APPLE__)
   canonicalPath = Posix_RealPath(path);
#elif defined(_WIN32)
   char driveSpec[4];
   Bool remoteDrive = FALSE;

   if (!path || !*path) {
      return NULL;
   }

   memcpy(driveSpec, path, 3);
   driveSpec[3] = '\0';

   if (strchr(VALID_DIRSEPS, driveSpec[0]) &&
       strchr(VALID_DIRSEPS, driveSpec[1])) {
      remoteDrive = TRUE;
   } else {
#if defined( VM_WIN_UWP)
      /* Don't need remote path for UWP until now*/
      remoteDrive = FALSE;
#else
      remoteDrive = (GetDriveTypeA(driveSpec) == DRIVE_REMOTE);
#endif // !VM_WIN_UWP
   }

   /*
    * If the path is *potentially* a path to remote share, we do not
    * call GetLongPathName, because if the remote server is unreachable,
    * that function could hang. We sacrifice two things by doing so:
    * 1. The UNC path could refer to the local host and we incorrectly
    *    assume remote.
    * 2. We do not resolve 8.3 names for remote paths.
    */

   if (remoteDrive) {
      canonicalPath = Util_SafeStrdup(path);
   } else {
      canonicalPath = W32Util_RobustGetLongPath(path);
   }

#else
   NOT_IMPLEMENTED();
#endif
   return canonicalPath;
}


#if defined(_WIN32)
/*
 *-----------------------------------------------------------------------------
 *
 * Util_GetCanonicalPathForHash --
 *
 *      Utility function to both get the canonical version of the input path
 *      and produce a unique case-insensitive version of the path suitable
 *      for use as a seed to hash functions.
 *
 * Results:
 *       Canonicalized UTF-8 pathname suitable for use in hashing.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Util_GetCanonicalPathForHash(const char *path)  // IN: UTF-8
{
   char *ret = NULL;
   char *cpath = Util_GetCanonicalPath(path);

   if (cpath != NULL) {
      ret = Unicode_FoldCase(cpath);
      free(cpath);
   }

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilGetLegacyEncodedString --
 *
 *      Takes a UTF-8 string, and allocates a new string in legacy encoding.
 *      This is necessary to maintain compatibility with older versions of
 *      the product, which may have stored strings (paths) in legacy
 *      encoding.  Hence, the use of WideCharToMultiByte().
 *
 * Results:
 *      An allocated string in legacy encoding (MBCS when applicable).
 *      NULL on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static char *
UtilGetLegacyEncodedString(const char *path)  // IN: UTF-8
{
   char *ret = NULL;
   char *cpath = Util_GetCanonicalPath(path);

   if (cpath != NULL) {
      char *apath = NULL;
      WCHAR *wcpath = Unicode_GetAllocUTF16(cpath);

      /* First get the length of multibyte string */
      int alen = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, wcpath, -1,
                                     NULL, 0, NULL, NULL);
      if (alen > 0) {
         int retlen;

         /* Now get the converted string */
         ret = Util_SafeMalloc(alen);
         retlen = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, wcpath, -1,
                                      ret, alen, NULL, NULL);
         if (retlen != alen) {
            free(ret);
            ret = NULL;
         }
      }
      free(cpath);
      free(wcpath);
   }

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_CompatGetCanonicalPath --
 *
 *      Canonicalizes a path name (compatibility version).
 *
 * Results:
 *      A freshly allocated canonicalized path name in legacy encoding
 *      (MBCS when applicable).
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Util_CompatGetCanonicalPath(const char *path)  // IN: UTF-8
{
   char *cpath = Util_GetCanonicalPath(path);
   char *ret = NULL;

   if (cpath != NULL) {
      ret = UtilGetLegacyEncodedString(cpath);
      free(cpath);
   }

   return ret;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Util_CanonicalPathsIdentical --
 *
 *      Utility function to compare two paths that have already been made
 *      canonical. This function exists to mask platform differences in
 *      path case-sensitivity.
 *
 *      XXX: This implementation makes assumptions about the host filesystem's
 *           case sensitivity without any regard to what filesystem the
 *           provided paths actually use. There are many ways to break this
 *           assumption, on any of our supported host OSes! The return value
 *           of this function cannot be trusted.
 *
 * Results:
 *      TRUE if the paths are equivalenr, FALSE if they are not.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Util_CanonicalPathsIdentical(const char *path1,  // IN:
                             const char *path2)  // IN:
{
   ASSERT(path1);
   ASSERT(path2);

#if defined(__linux__)
   return (strcmp(path1, path2) == 0);
#elif defined(_WIN32)
   return (_stricmp(path1, path2) == 0);
#elif defined(__APPLE__) || defined(__FreeBSD__)
   return (strcasecmp(path1, path2) == 0);
#else
   NOT_IMPLEMENTED();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_IsAbsolutePath --
 *
 *      Checks if the given path is absolute.
 *
 * Results:
 *      TRUE if the path is absolute, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Util_IsAbsolutePath(const char *path)  // IN: path to check
{
#if defined __linux__ || defined __APPLE__ || defined __FreeBSD__ || defined sun
   // path[0] is valid even for the empty string.
   return path && path[0] == DIRSEPC;
#elif defined _WIN32
   // if the length is 2, path[2] will be valid because of the null terminator.
   if (!path || strlen(path) < 2) {
      return FALSE;
   }

   // <Drive letter>:\path
   if (CType_IsAlpha(path[0]) && path[1] == ':' && path[2] == DIRSEPC) {
      return TRUE;
   }

   // UNC paths
   if (path[0] == DIRSEPC && path[1] == DIRSEPC) {
      return TRUE;
   }

   return FALSE;
#else
   NOT_IMPLEMENTED();
#endif
   NOT_REACHED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilIsAlphaOrNum --
 *
 *      Checks if character is a numeric digit or a letter of the
 *      english alphabet.
 *
 * Results:
 *      Returns TRUE if character is a digit or a letter, FALSE otherwise.
 *
 * Side effects:
 *           None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
UtilIsAlphaOrNum(char ch) //IN
{
   if ((ch >= '0' && ch <= '9') ||
       (ch >= 'a' && ch <= 'z') ||
       (ch >= 'A' && ch <= 'Z')) {
      return TRUE;
   } else {
      return FALSE;
   }
}


#ifndef _WIN32

/*
 *-----------------------------------------------------------------------------
 *
 * UtilGetHomeDirectory --
 *
 *      Retrieves the home directory for a user, given their passwd struct.
 *
 * Results:
 *      Returns user's home directory or NULL if it fails.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static char *
UtilGetHomeDirectory(struct passwd *pwd) // IN/OPT: user passwd struct
{
   if (pwd && pwd->pw_dir) {
      return Util_SafeStrdup(pwd->pw_dir);
   } else {
      return NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilGetLoginName --
 *
 *      Retrieves the login name for a user, given their passwd struct.
 *
 * Results:
 *      Returns user's login name or NULL if it fails.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static char *
UtilGetLoginName(struct passwd *pwd) // IN/OPT: user passwd struct
{
   if (pwd && pwd->pw_name) {
      return Util_SafeStrdup(pwd->pw_name);
   } else {
      return NULL;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * UtilDoTildeSubst --
 *
 *      Given a string following a tilde, this routine returns the
 *      corresponding home directory.
 *
 * Results:
 *      A string containing the home directory in native format. The
 *      returned string is a newly allocated string which may/must be
 *      freed by the caller.
 *
 * Side effects:
 *      None.
 *
 * Credit: derived from J.K.Ousterhout's Tcl
 *----------------------------------------------------------------------
 */

static char *
UtilDoTildeSubst(const char *user)  // IN: name of user
{
   char *str = NULL;
   struct passwd *pwd = NULL;

   if (gHomeDirOverride) {
      /*
       * Allow code to override the tilde expansion for things like unit tests.
       * See: Util_OverrideHomeDir
       */
      return Util_SafeStrdup(gHomeDirOverride);
   }

   if (*user == '\0') {
#if defined(__APPLE__)
      /*
       * This check mimics the checks and order of CFCopyHomeDirectoryURL(),
       * which is unfortunately not callable directly since Apple has marked it
       * as only in iOS despite the fact that they clearly ship it on macOS.
       */
      str = issetugid() ? NULL
                        : Unicode_Duplicate(Posix_Getenv("CFFIXED_USER_HOME"));

      if (str == NULL) {
         pwd = Posix_Getpwuid(getuid());
         if (pwd == NULL) {
            Log("Could not get passwd for current user.\n");
         }
      }
#endif // defined(__APPLE__)
      if (str == NULL && pwd == NULL) {
         str = Unicode_Duplicate(Posix_Getenv("HOME"));
         if (str == NULL) {
            Log("Could not expand environment variable HOME.\n");
         }
      }
   } else {
      pwd = Posix_Getpwnam(user);
      if (pwd == NULL) {
         Log("Could not get passwd for user '%s'.\n", user);
      }
   }
   if (str == NULL && pwd != NULL) {
      str = UtilGetHomeDirectory(pwd);
      Posix_Endpwent();
      if (str == NULL) {
         Log("Could not get home directory for user.\n");
      }
   }
   return str;
}

#endif // !_WIN32


/*
 *----------------------------------------------------------------------
 *
 * Util_ExpandString --
 *
 *      converts the strings by expanding "~", "~user" and environment
 *      variables
 *
 * Results:
 *
 *      Return a newly allocated string.  The caller is responsible
 *      for deallocating it.
 *
 *      Return NULL in case of error.
 *
 * Side effects:
 *      memory allocation
 *
 * Bugs:
 *      the handling of enviroment variable references is very
 *      simplistic: there can be only one in a pathname segment
 *      and it must appear last in the string
 *
 *----------------------------------------------------------------------
 */

#define UTIL_MAX_PATH_CHUNKS 100

char *
Util_ExpandString(const char *fileName) // IN  file path to expand
{
   char *copy;
   char *result = NULL;
   int nchunk = 0;
   char *chunks[UTIL_MAX_PATH_CHUNKS];
   int chunkSize[UTIL_MAX_PATH_CHUNKS];
   Bool freeChunk[UTIL_MAX_PATH_CHUNKS];
   char *cp;
   int i;

   ASSERT(fileName != NULL);

   copy = Unicode_Duplicate(fileName);

   /*
    * quick exit
    */
   if ((*fileName != '~') && (strchr(fileName, '$') == NULL)) {
      return copy;
   }

   /*
    * XXX Because the rest part of code depends pretty heavily from
    *     character pointer operations we want to leave it as-is and
    *     don't want to re-work it with using unicode library. However
    *     it's acceptable only until our Unicode type is UTF-8 and
    *     until code below works correctly with UTF-8.
    */

   /*
    * Break string into nice chunks for separate expansion.
    *
    * The rule for terminating a ~ expansion is historical.  -- edward
    */

   nchunk = 0;
   for (cp = copy; *cp != '\0';) {
      size_t len;
      if (*cp == '$') {
         char *p;
         for (p = cp + 1; UtilIsAlphaOrNum(*p) || *p == '_'; p++) {
         }
         len = p - cp;
#if !defined(_WIN32)
      } else if (cp == copy && *cp == '~') {
         len = strcspn(cp, DIRSEPS);
#endif
      } else {
         len = strcspn(cp, "$");
      }
      if (nchunk >= UTIL_MAX_PATH_CHUNKS) {
         Log("%s: Filename \"%s\" has too many chunks.\n", __FUNCTION__,
             fileName);
         goto out;
      }
      chunks[nchunk] = cp;
      chunkSize[nchunk] = len;
      freeChunk[nchunk] = FALSE;
      nchunk++;
      cp += len;
   }

   /*
    * Expand leanding ~
    */

#if !defined(_WIN32)
   if (nchunk > 0 && chunks[0][0] == '~') {
      char save = (cp = chunks[0])[chunkSize[0]];
      cp[chunkSize[0]] = 0;
      ASSERT(!freeChunk[0]);
      chunks[0] = UtilDoTildeSubst(chunks[0] + 1);
      cp[chunkSize[0]] = save;
      if (chunks[0] == NULL) {
         /* It could not be expanded, therefore leave as original. */
         chunks[0] = cp;
      } else {
         /* It was expanded, so adjust the chunks. */
         chunkSize[0] = strlen(chunks[0]);
         freeChunk[0] = TRUE;
      }
   }
#endif

   /*
    * Expand $
    */

   for (i = 0; i < nchunk; i++) {
      char save;
      char *expand = NULL;
      char buf[100];
#if defined(_WIN32)
      utf16_t bufW[100];
#endif
      cp = chunks[i];

      if (*cp != '$' || chunkSize[i] == 1) {

         /*
          * Skip if the chuck has only the $ character.
          * $ will be kept as a part of the pathname.
          */

         continue;
      }

      save = cp[chunkSize[i]];
      cp[chunkSize[i]] = 0;

      /*
       * $PID and $USER are interpreted specially.
       * Others are just getenv().
       */

      expand = Unicode_Duplicate(Posix_Getenv(cp + 1));
      if (expand != NULL) {
      } else if (strcasecmp(cp + 1, "PID") == 0) {
         Str_Snprintf(buf, sizeof buf, "%"FMTPID, getpid());
         expand = Util_SafeStrdup(buf);
      } else if (strcasecmp(cp + 1, "USER") == 0) {
#if !defined(_WIN32)
         struct passwd *pwd = Posix_Getpwuid(getuid());
         expand = UtilGetLoginName(pwd);
         Posix_Endpwent();
#else
         DWORD n = ARRAYSIZE(bufW);
         if (GetUserNameW(bufW, &n)) {
            expand = Unicode_AllocWithUTF16(bufW);
         }
#endif
         if (expand == NULL) {
            expand = Unicode_Duplicate("unknown");
         }
      } else {
         Log("Environment variable '%s' not defined in '%s'.\n",
             cp + 1, fileName);
#if !defined(_WIN32)
         /*
          * Strip off the env variable string from the pathname.
          */

         expand = Unicode_Duplicate("");

#else    // _WIN32

         /*
          * The problem is we have really no way to distinguish the caller's
          * intention is a dollar sign ($) is used as a part of the pathname
          * or as an environment variable.
          *
          * If the token does not expand to an environment variable,
          * then assume it is a part of the pathname. Do not strip it
          * off like it is done in linux host (see above)
          *
          * XXX   We should also consider using %variable% convention instead
          *       of $variable for Windows platform.
          */

         Str_Strcpy(buf, cp, 100);
         expand = Unicode_AllocWithUTF8(buf);
#endif
      }

      cp[chunkSize[i]] = save;

      ASSERT(expand != NULL);
      ASSERT(!freeChunk[i]);
      chunks[i] = expand;
      if (chunks[i] == NULL) {
         Log("%s: Cannot allocate memory to expand $ in \"%s\".\n",
             __FUNCTION__, fileName);
         goto out;
      }
      chunkSize[i] = strlen(expand);
      freeChunk[i] = TRUE;
   }

   /*
    * Put all the chunks back together.
    */

   {
      int size = 1;     // 1 for the terminating null
      for (i = 0; i < nchunk; i++) {
         size += chunkSize[i];
      }
      result = malloc(size);
   }
   if (result == NULL) {
      Log("%s: Cannot allocate memory for the expansion of \"%s\".\n",
          __FUNCTION__, fileName);
      goto out;
   }
   cp = result;
   for (i = 0; i < nchunk; i++) {
      memcpy(cp, chunks[i], chunkSize[i]);
      cp += chunkSize[i];
   }
   *cp = 0;

out:
   /*
    * Clean up and return.
    */

   for (i = 0; i < nchunk; i++) {
      if (freeChunk[i]) {
         free(chunks[i]);
      }
   }
   free(copy);

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_OverrideHomeDir --
 *
 *      This function changes the behavior of Util_ExpandPath so that
 *      it will expand "~" to the provided path rather than the current
 *      user's home directory.
 *
 *      This function is not thread safe, so a best practice is to
 *      invoke it once at the begining of program execution, much like
 *      an *_Init() function. It should also never be called more than
 *      once.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Future calls to Util_ExpandPath will substitute "~" with the
 *      given path.
 *
 *----------------------------------------------------------------------
 */
void
Util_OverrideHomeDir(const char *path) // IN
{
   ASSERT(!gHomeDirOverride);
   gHomeDirOverride = Util_SafeStrdup(path);
}


#if defined(__APPLE__)

/*
 * XXX TODO: move these to utilMacos.c after it gets split up to avoid
 * dependencies on IOKit and DiskArbitration.
 */


/*
 *-----------------------------------------------------------------------------
 *
 * Util_CFStringToUTF8CString --
 *
 *      Convert a CFString into a UTF-8 encoded C string.
 *
 *      Amazingly, CFString does not provide this functionality, so everybody
 *      (including Apple, see smb-217.18/lib/smb/charsets.c in darwinsource)
 *      ends up re-implementing it this way...
 *
 * Results:
 *      On success: Allocated, UTF8-encoded, NUL-terminated string.
 *      On failure: NULL.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Util_CFStringToUTF8CString(CFStringRef s) // IN
{
   static CFStringEncoding const encoding = kCFStringEncodingUTF8;
   char const *fast;
   char *result;

   ASSERT(s);

   fast = CFStringGetCStringPtr(s, encoding);
   if (fast) {
      result = strdup(fast);
   } else {
      size_t maxSize;

      maxSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(s),
                                                  encoding) + 1;
      result = malloc(maxSize);
      if (result) {
         if (CFStringGetCString(s, result, maxSize, encoding)) {
            /*
             * It is likely that less than 'maxSize' bytes have actually been
             * written into 'result'. If that becomes a problem in the future,
             * we can always trim the buffer here.
             */
         } else {
            free(result);
            result = NULL;
         }
      }
   }

   if (!result) {
      Log("Failed to get C string from CFString.\n");
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilMacos_CreateCFDictionaryWithContentsOfFile --
 *
 *      Creates and returns a dictionary with the contents of the specified
 *      property list file. The file can be either XML or binary.
 *
 *      This is equivalent to +[NSDictionary dictionaryWithContentsOfFile:],
 *      unfortunately Apple did not provide a similar CoreFoundation function.
 *
 * Results:
 *      Dictionary with contents of the property list file, or NULL on failure.
 *      Caller must release the dictionary with CFRelease.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

CFDictionaryRef
UtilMacos_CreateCFDictionaryWithContentsOfFile(const char *path) // IN
{
   struct stat s;
   CFURLRef url;
   CFReadStreamRef stream = NULL;
   CFPropertyListRef plist = NULL;
   CFDictionaryRef result = NULL;

   /*
    * Avoid creating the unnecessary CFURL and CFReadStream objects if the
    * file does not exist. Trying to read a non-existent file will only
    * fail in CFReadStreamOpen().
    */
   if (Posix_Stat(path, &s) != 0) {
      return NULL;
   }

   url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
                                                 (const UInt8 *)path,
                                                 strlen(path),
                                                 false);
   if (   url != NULL
       && (stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url))
       && CFReadStreamOpen(stream)
       && (plist = CFPropertyListCreateWithStream(kCFAllocatorDefault,
                                                  stream,
                                                  0,
                                                  kCFPropertyListImmutable,
                                                  NULL,
                                                  NULL))
       && (CFGetTypeID(plist) == CFDictionaryGetTypeID())) {
      result = (CFDictionaryRef)plist;
      plist = NULL;
   }

   if (plist) {
      CFRelease(plist);
   }

   if (stream) {
      CFReadStreamClose(stream);
      CFRelease(stream);
   }

   if (url) {
      CFRelease(url);
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UtilMacos_ReadSystemVersion --
 *
 *      Reads the Mac OS system version from the provided dictionary, and
 *      returns one or more of the requested values.
 *
 * Results:
 *      TRUE if the all the values were read successfully, or FALSE on failure.
 *      Caller must free the values.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
UtilMacos_ReadSystemVersion(CFDictionaryRef versionDict, // IN
                            char **productName,          // OUT/OPT
                            char **productVersion,       // OUT/OPT
                            char **productBuildVersion)  // OUT/OPT
{
   struct {
      const CFStringRef key;
      char **outValue;
   } keyedValues[] = {
      { CFSTR("ProductName"),         productName         },
      { CFSTR("ProductVersion"),      productVersion      },
      { CFSTR("ProductBuildVersion"), productBuildVersion },
   };
   int i;

   for (i = 0; i < ARRAYSIZE(keyedValues); i++) {
      CFStringRef curVal;

      if (keyedValues[i].outValue == NULL) {
         continue;
      }

      curVal = CFDictionaryGetValue(versionDict, keyedValues[i].key);
      if (   curVal != NULL
          && CFGetTypeID(curVal) == CFStringGetTypeID()
          && (*keyedValues[i].outValue = Util_CFStringToUTF8CString(curVal))) {
         continue;
      }

      /*
       * Error retrieving one of the values. Clean up any previously saved
       * values and return with failure.
       */
      while (--i >= 0) {
         if (keyedValues[i].outValue != NULL) {
            free(*keyedValues[i].outValue);
            *keyedValues[i].outValue = NULL;
         }
      }

      return FALSE;
   }

   return TRUE;
}

#endif // defined(__APPLE__)

/*
 * These are generic functions which might be useful on any platform
 * (ESX/Linux/BSD/Apple/Windows). Currently the Windows version have
 * not been defined as that is not very straightforward and I do not
 * know how to fully test it. f.e  we have to handle char* to wchar_t*
 * conversion, also it appears to cause linking problems for some Windows
 * tools.
 * If you want to use these for Windows, please feel free to fix these
 * and remove the ifdef macro below and in util.h.
 */

#if !defined(_WIN32)
/*
 *----------------------------------------------------------------------
 *
 * Util_IPv4AddrValid --
 *
 * Results:
 *      TRUE if the passed-in string represents a valid IPv4 address in
 *      dotted decimal notation, FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
Util_IPv4AddrValid(const char *addr)  // IN: IPv4 address string
{
   struct in_addr dummyInAddr;

   return inet_pton(AF_INET, addr, &dummyInAddr) == 1;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_IPv6AddrValid --
 *
 * Results:
 *      TRUE if the passed-in string represents a valid IPv6 address,
 *      FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
Util_IPv6AddrValid(const char *addr)  // IN: IPv6 address string
{
   struct in6_addr dummyInAddr;
   char   ipv6AddrStr[INET6_ADDRSTRLEN + 1];

   /*
    * IPv6 link-local addresses can have a suffix of the form
    * %{ifname}, e.g. %vmk0, %eth0.
    * We cannot pass it as-is to inet_pton(), need to strip off the
    * suffix before passing to inet_pton().
    */
   if (sscanf(addr, "%"XSTR(INET6_ADDRSTRLEN)"[^%]",
              ipv6AddrStr) != 1) {
      return FALSE;
   }
   return inet_pton(AF_INET6, ipv6AddrStr, &dummyInAddr) == 1;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_IPAddrValid --
 *
 * Results:
 *      TRUE if the passed-in string represents a valid IPv4 or IPv6 address,
 *      FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

Bool
Util_IPAddrValid(const char *addr)  // IN: IPv4 or IPv6 address string
{
   return Util_IPv4AddrValid(addr) || Util_IPv6AddrValid(addr);
}

#endif   /* ! _WIN32 */
 070701000001A5000081A40000000000000000000000016822550500001DC2000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/vmstdio.c /*********************************************************
 * Copyright (C) 1998-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmstdio.c --
 *
 *    Functions that operate on FILE objects --hpreg
 */


#include <stdio.h>
#include <errno.h>
#include <string.h>

#include "vmware.h"
#include "dynbuf.h"
#include "vmstdio.h"
#include "posixInt.h"


/*
 *-----------------------------------------------------------------------------
 *
 * SuperFgets --
 *
 *      The fgets() API is poorly designed: it doesn't return how many bytes
 *      were written to the buffer. Hence this function --hpreg
 *
 *      In addition, this function recognizes three variants of end-of-line
 *      markers regardless of the platform: \n, \r\n and \r.  The exception
 *      is that on Windows, \r\n is recognized only if the file is opened
 *      in text mode.  In binary mode, the \r and \n are interpreted as two
 *      separate end-of-line markers.
 *
 *      As input, *count is the size of bufIn.
 *
 * Results:
 *      It returns bufIn on success and NULL on error.  The line terminator
 *      and NUL terminator are not stored in bufIn.  On success, '*count' is
 *      the number of bytes written to the buffer.
 *    
 * Side effects:
 *      If the line read is terminated by a standalone '\r' (legacy Mac), the
 *      next character is pushed back using ungetc.  Thus, that character may
 *      be lost if the caller performs any write operation on the stream
 *      (including fseek, fflush, etc.) subsequently without an intervening
 *      read operation.
 *
 *-----------------------------------------------------------------------------
 */

static void *
SuperFgets(FILE *stream,   // IN:
           size_t *count,  // IN/OUT:
           void *bufIn)    // OUT:
{
   char *buf = bufIn;
   size_t size = 0;

   ASSERT(stream);
   ASSERT(buf);
   ASSERT(count);
   ASSERT(*count);

   /*
    * Keep reading until a line terminator is found or *count is reached.
    * The line terminator itself is not written into the buffer.
    * Clear errno so that we can distinguish between end-of-file and error.
    */

   errno = 0;

   for (size = 0; size < *count; size++) {
      int c;

      c = getc(stream);

      if (c == EOF) {
         if (errno) {
            /* getc returned an error. */
            return NULL;
         } else {
            /* Found an end-of-file line terminator. */
            break;
         }
      }

      if (c == '\n') {
         /* Found a Unix line terminator */
         break;
      }
      
      if (c == '\r') {

#ifndef _WIN32
         /* 
          * Look ahead to see if it is a \r\n line terminator.
          * On Windows platform, getc() returns one '\n' for a \r\n two-byte
          * sequence (for files opened in text mode), so we can skip the
          * look-ahead.
          */

         c = getc(stream);
         if (c != EOF && c != '\n') {
            /* Found a legacy Mac line terminator */

            if (ungetc(c, stream) == EOF) {
               return NULL;
            }
         }

         /* Forget that we peeked. */
         clearerr(stream);
#endif

         break;
      }

      buf[size] = c;
   }

   *count = size;

   return buf;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StdIO_ReadNextLine --
 *
 *      Read the next line from a stream.
 *
 *      A line is defined as an arbitrarily long sequence of arbitrary bytes,
 *      that ends with the first occurrence of one of these line terminators:
 *      . \r\n          (the Windows/DOS way, in text mode)
 *      . \n            (the UNIX way)
 *      . \r            (the legacy Mac (pre-OS X) way)
 *      . end-of-stream
 *
 *      Note that on Windows, getc() returns one '\n' for a \r\n two-byte
 *      sequence only if the file is opened in text mode.  Therefore \r\r\n
 *      will be interpreted as two newlines in text mode ('\r' followed by
 *      '\r\n'), but three newlines in binary mode ('\r', '\r', '\n').
 *
 *      If maxBufLength is non-zero at most maxBufLength bytes will be
 *      allocated.
 *
 * Results:
 *      StdIO_Success on success:
 *          '*buf' is an allocated, NUL terminated buffer that contains the
 *          line (excluding the line terminator).  If not NULL, '*count'
 *          contains the size of the buffer (excluding the NUL terminator).
 *
 *      StdIO_EOF if there is no next line (end of stream):
 *          '*buf' is left untouched.
 *
 *      StdIO_Error on failure:
 *          errno is set accordingly and '*buf' is left untouched.
 *
 * Side effects:
 *      If the line read is terminated by a standalone '\r' (legacy Mac), the
 *      next character is pushed back using ungetc.  Thus, that character may
 *      be lost if the caller performs any write operation on the stream
 *      (including fseek, fflush, etc.) subsequently without an intervening
 *      read operation.
 *
 *-----------------------------------------------------------------------------
 */

StdIO_Status
StdIO_ReadNextLine(FILE *stream,         // IN:
                   char **buf,           // OUT:
                   size_t maxBufLength,  // IN: May be 0.
                   size_t *count)        // OUT/OPT:
{
   DynBuf b;

   ASSERT(stream);
   ASSERT(buf);

   DynBuf_Init(&b);

   for (;;) {
      char *data;
      size_t size;
      size_t max;
      size_t nr;

      /*
       * The dynamic buffer must be at least 2 bytes large, so that at least
       * 1 stream byte and fgets()'s NUL byte can fit --hpreg
       */

      if (DynBuf_Enlarge(&b, 2) == FALSE) {
         errno = ENOMEM;
         goto error;
      }

      /* Read the next chunk of line directly in the dynamic buffer --hpreg */

      data = DynBuf_Get(&b);
      size = DynBuf_GetSize(&b);

      max = DynBuf_GetAllocatedSize(&b);
      nr = max - size;

      if (SuperFgets(stream, &nr, data + size) == NULL) {
         goto error;
      }

      size += nr;
      DynBuf_SetSize(&b, size);

      if (maxBufLength != 0 && size >= maxBufLength) {
         errno = E2BIG;
         goto error;
      }

      if (size < max) {
         /* SuperFgets() found end-of-line */

         if (size == 0 && feof(stream)) {
            /* Reached end-of-file before reading anything */
            DynBuf_Destroy(&b);

            return StdIO_EOF;
         }

         break;
      }

      /*
       * No line terminator found yet, we need a larger buffer to do the next
       * SuperFgets() --hpreg
       */

   }

   /* There is a line in the buffer --hpreg */

   /* NUL terminator --hpreg */
   if (DynBuf_Append(&b, "", 1) == FALSE) {
      errno = ENOMEM;
      goto error;
   }

   *buf = DynBuf_Get(&b);
   if (count) {
      *count = DynBuf_GetSize(&b) - 1;
   }

   return StdIO_Success;

error:
   DynBuf_Destroy(&b);

   return StdIO_Error;
}
  070701000001A6000081A40000000000000000000000016822550500005366000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/misc/vthreadBase.c /*********************************************************
 * Copyright (C) 2010-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vthreadBase.c --
 *
 *      Base thread management functionality.  Does not care whether threads
 *      are used or not.
 *
 *      For full thread management (e.g. creation/destruction), see lib/thread.
 *
 *      Major exposed functions and their properties:
 *         VThreadBase_CurName - Returns a thread name.  Will try to assign
 *                               a default name if none exists, but if called
 *                               reentrantly (e.g. due to ASSERT) will supply
 *                               a failsafe name instead.
 *         VThreadBase_CurID - Returns a VThreadID.
 *         VThreadBase_SetName - Sets current thread name.
 *
 *      Functions useful for implementing a full thread library:
 *         VThreadBase_ForgetSelf - Clears the thread name for current thread,
 *                                  to clean up resource usage prior to thread
 *                                  exit. (Only needed if __thread support is
 *                                  unavailable).
 *      Historical quirks:
 *      * Most other code uses VThread_Foo instead of VThreadBase_Foo; the
 *        public header file uses inlines to convert names.
 *
 *      By default, threads will be given names like "vthread-123",
 *      "vthread-987", etc. to match IDs provided by the host operating system.
 *      Use VThread_SetName to provide more meaningful names.
 *
 *      On most platforms, make use of __thread.
 *      On a few platforms (see vthreadBase.h definition of VMW_HAVE_TLS),
 *         use pthread getspecific/setspecific for thread name and signal count.
 */

#if defined _WIN32
#  include <windows.h>
#else
#  if defined(__sun__) && !defined(_XOPEN_SOURCE)
     /*
      * Solaris headers don't define constants we need unless
      * the Posix standard is somewhat modern.  Most of our builds
      * set this; we should chase down the oversight.
      */
#    define _XOPEN_SOURCE 500
#  endif
#  if defined __linux__
#    include <sys/syscall.h>   // for gettid(2)
#  endif
#  include <pthread.h>
#endif
#include <stdlib.h>
#include <stdio.h>   /* snprintf */

#include "vmware.h"
#include "vthreadBase.h"
#include "util.h"


/*
 * Custom code for platforms without thread-local storage.
 * On these platforms, use constructors/destructors to manage
 * pthread keys.
 */
#if !defined(VMW_HAVE_TLS)
static pthread_key_t sigNestCountKey;
static pthread_key_t vthreadNameKey;

/*
 * To avoid needing initialization, manage via constructor/destructor.
 * We only do this on Apple and Android, where:
 * - Apple reserves the first several pthread_key_t for libc
 * - Android sets bit 31 in all valid pthread_key_t
 * Thus, the default 0 value indicates unset.
 */
__attribute__((constructor))
static void VThreadBaseConstruct(void)
{
   (void) pthread_key_create(&sigNestCountKey, NULL);
   (void) pthread_key_create(&vthreadNameKey, NULL);
}
__attribute__((destructor))
static void VThreadBaseDestruct(void)
{
   /*
    * TLS destructors have a wrinkle: if you unload a library THEN
    * a thread exits, the TLS destructor will SEGV because it's unloaded.
    * Net result, we must either leak a TLS key or the value in the TLS
    * slot. This path chooses to leak the value in the TLS slot... and
    * as the value is an integer and not a pointer, there is no leak.
    */
   if (vthreadNameKey != 0) {
      (void) pthread_key_delete(vthreadNameKey);
   }
   if (sigNestCountKey != 0) {
      (void) pthread_key_delete(sigNestCountKey);
   }
}
#endif

#ifdef VMW_HAVE_TLS

#if !defined _WIN32
/*
 * Signal counting code. Signal counting operates self-contained,
 * having no particular dependency on the rest of VThreadBase
 * (especially the VThreadBaseData memory allocation).
 */
static __thread int sigNestCount;
#endif

/*
 * VThread name code.
 * Common case: thread-local storage is available, and this is easy.
 * Uncommon case: iOS/Android lack thread-local storage for the versions
 *   we currently use.
 *   Problems:
 *      PR 1686126 shows that malloc() on the thread-name path can lead to
 *      deadlock (due to signal within malloc()), yet somehow we must come up
 *      with some sort of name always.
 *      PR 1626963 shows that even if we could safely allocate memory, we
 *      cannot free it: if we were to pthread_key_create(..., &free) and this
 *      library were unloaded via dlclose() before all threads exit, the
 *      call to free() (actually, our library's .plt thunk) would crash.
 *   Solution:
 *      Solve the simpler problem. An external SetName is assumed to not
 *      be inside a signal handler (malloc is safe) AND is assumed there
 *      will be an eventual ForgetSelf (avoids leak). This isn't strictly
 *      true of all cases, but remember this only applies to iOS/Android in
 *      the first place. If SetName has not been called on the current thread,
 *      make up a thread-specific name and store it in a (non-thread-safe)
 *      global buffer as a best-effort solution.
 * Discarded ideas:
 *   1) allocating thread-name memory on first use for anonymous threads.
 *      Guaranteed to leak memory due to PR 1626963 (above).
 *   2) use Android prctl(PR_GET_NAME) + iOS pthread_getname_np(). Storing names
 *      works great, but both APIs require caller-supplied buffers ... which
 *      leads right back to needing a per-thread buffer, and the malloc()
 *      problem above. Except it's worse: we could find we have a perfectly
 *      valid name, but cannot allocate a buffer to return it.
 *   3) Re-plumbing VThreadBase_CurName() to take a caller-supplied buffer
 *      instead of returning a pointer; that would be an ugly, invasive API
 *      change that de-optimizes "modern" OSes by forcing them to strcpy when
 *      they could just return a pointer.
 *   4) Use 2-4 pthread_keys for storage (4-8 chars per key). Same problem as
 *      (2) above... this stores the data fine, but still does not provide
 *      a per-thread buffer for returning the data.
 *   5) Return a static string "<unnamed>" for *all* unknown threads.
 *      The picked solution above does have a race in that two unnamed threads
 *      generating a default name at the same time could race and one thread
 *      may get the wrong name; this option avoids that problem by refusing to
 *      name unnamed threads AT ALL.
 *      Discarded because best-effort thread identification seems better than
 *      technically-correct-but-less-useful refusal to provide any name.
 * To summarize in two points: a per-thread buffer is pretty much essential
 * for correctness, and minimize effort for "old" platforms.
 */

static __thread char vthreadName[VTHREADBASE_MAX_NAME];

#endif /* VMW_HAVE_TLS */


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBaseGetStableID --
 *
 *      Stable thread-id function.
 *
 *      In contrast to VThreadBase_GetKernelID below, this stable ID is safe
 *      for caching. Unfortunately, it tends to not be human readable,
 *      is not understood by the kernel, and makes no sense passed to
 *      any other process.
 *
 *      Win32 was always safe; for POSIX, we instead make use of the fact
 *      that pthread_t values (by definition) have to be stable across process
 *      fork. That is:
 *         pthread_t before = pthread_self();
 *         fork();
 *         pthread_t after = pthread_self();
 *         ret = pthread_equal(before, after);  <---- POSIX requires equality
 *      POSIX leaves the exact mechanism unspecified, but in practice
 *      most POSIX OSes make pthread_t a pointer and make use of the fact that
 *      the address space is fully cloned so the pointer will not change.
 *      (An exception is Solaris, which uses integer LWP indexes but
 *      clones the per-process LWP table at fork).
 *
 *      The assumption above is technically non-portable, as POSIX also
 *      permits pthread_t to be a structure. We do not support any OS
 *      which uses a structure definition.
 *
 * Results:
 *      Some sort of stable ID.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static uintptr_t
VThreadBaseGetStableID(void)
{
#if defined _WIN32
   return GetCurrentThreadId();
#elif defined __sun__
   /* pthread_t is a uint_t index into a per-process LWP table */
   return pthread_self();
#else
   /* pthread_t is (hopefully) an opaque pointer type */
   return (uintptr_t)(void *)pthread_self();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBase_CurID --
 *
 *      Get the current thread ID.
 *
 * Results:
 *      A VThreadID. Always succeeds.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

VThreadID
VThreadBase_CurID(void)
{
   return VThreadBaseGetStableID();
}


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBase_GetKernelID --
 *
 *      Native thread-id function. USE WITH GREAT CAUTION.
 *
 *      Details below. In short, the ID returned by this function is both
 *      'pretty' (tends to be short, e.g. readable as a decimal) and 'native'
 *      in that it is useful for correlating with kernel threads.
 *      However, this ID is not fork-safe on at least Linux.
 *
 *      In practice, VThreadBase chooses to use this ID for thread names only.
 *
 *      Most POSIX: With most modern threading implementations, threads are
 *        "lightweight processes" (LWP), so any native TID changes after
 *        a fork(). Which leads to pthread_atfork() - you can find out that TID
 *        changed, but it's up to you to fix up all cached copies.
 *        (A clever soul might suggest just continuing to use the old TID.
 *        That clever soul is not so clever, having forgotten that POSIX OSes
 *        recycle LWPs so all it takes is a couple of forks for you to have a
 *        cached TID on one thread match the native TID on another thread. Hope
 *        you didn't need that TID for correctness!).
 *        The good news is nearly all POSIX has a pthread NP API (non-portable)
 *        to provide the right thing.
 *      Linux (glibc): is the exception to "nearly all". The *only* way to get a
 *        system ID is via gettid() syscall. Which is a syscall and thus
 *        expensive relative to every other OS. This code implements the pthread
 *        NP wrapper that glibc *should* have.
 *      Windows: good news. Not having a fork() API means the 'pretty'
 *        ID returned here is stable forever. No special cases.
 *      Solaris: good news. Solaris implements the LWP namespace *per process*,
 *        which it clones on fork, meaning the forked child still gets the
 *        same LWP IDs. Likely a legacy of SunOS which had forkall().
 *
 *      Obviously, specific mechanisms for obtaining native IDs are *highly*
 *      non-portable, as indicated by the _np suffixes.
 *
 * Results:
 *      Some sort of system ID (e.g. LWP, Task, ...)
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

#if defined(__linux__) && !defined(__ANDROID__)
static pid_t
vmw_pthread_getthreadid_np(void)
{
   static __thread struct {
      pid_t pid;
      pid_t tid;
   } cache = { 0, 0 };

   /*
    * Linux (well, glibc) gets TWO things wrong, but the combination makes
    * a right, oddly enough. (1) There is no gettid function, because glibc
    * people decided anybody who needs a system ID is wrong (glibc bug 6399)
    * and should instead do a system call to get it. (2) they 'optimized'
    * getpid() to cache its result (which depends on forking only via POSIX
    * calls and not via syscalls), then decided they knew better than Linus
    * when he told them this was wrong.
    * BUT... the getpid cache can be used to make a sufficiently-correct and
    * fast gettid cache.
    */
   if (UNLIKELY(cache.pid != getpid())) {
      cache.pid = getpid();
      cache.tid = syscall(__NR_gettid);
   }
   return cache.tid;
}
#endif

uint64
VThreadBase_GetKernelID(void)
{
#if defined _WIN32
   return /* DWORD */ GetCurrentThreadId();
#elif defined __APPLE__
   /* Available as of 10.6 */
   uint64 hostTid;  // Mach Task ID
   pthread_threadid_np(NULL /* current thread */, &hostTid);
   return hostTid;
#elif defined __ANDROID__
   return /* pid_t */ gettid();  // Thank you, Bionic.
#elif defined __linux__
   return vmw_pthread_getthreadid_np();
#elif defined __sun__
   return /* uint_t */ pthread_self();  // Solaris uses LWP as pthread_t
#elif defined __FreeBSD__
#  if 0
   // Requires FreeBSD 9, we currently use FreeBSD 6. Include <pthread_np.h>
   return /* int */ pthread_getthreadid_np();
#  endif
   // Best effort until FreeBSD header update
   return (uint64)(uintptr_t)(void *)pthread_self();
#elif defined __EMSCRIPTEN__
   return (uint64)(uintptr_t)(void *)pthread_self();
#else
#  error "Unknown platform"
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBaseSafeName --
 *
 *      Generates a "safe" name for the current thread into a buffer.
 *
 *      Always succeeds, never recurses.
 *
 * Results:
 *      The current thread name.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VThreadBaseSafeName(char *buf,   // OUT: new name
                    size_t len)  // IN:
{
   /*
    * This function should not ASSERT, Panic or call a Str_
    * function (that can ASSERT or Panic), as the Panic handler is
    * very likely to query the thread name and end up right back here.
    */
   snprintf(buf, len - 1 /* keep buffer NUL-terminated */,
            "host-%"FMT64"u", VThreadBase_GetKernelID());
}


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBase_CurName --
 *
 *      Get the current thread name.
 *
 *      Always succeeds, never recurses.
 *
 * Results:
 *      The current thread name.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

const char *
VThreadBase_CurName(void)
{
#if defined VMW_HAVE_TLS
   if (UNLIKELY(vthreadName[0] == '\0')) {
      /*
       * Unnamed thread. If the thread's name mattered, it would
       * have called VThreadBase_SetName() earlier.
       *
       * Pick an arbitrary name and store it in thread-local storage.
       */

      VThreadBaseSafeName(vthreadName, sizeof vthreadName);
   }
   return vthreadName;
#else
   const char *raw;

   raw = (vthreadNameKey != 0) ? pthread_getspecific(vthreadNameKey)
                                : NULL;
   if (LIKELY(raw != NULL)) {
      return raw;  // fast path
   } else {
      /*
       * Unnamed thread. If the thread's name mattered, it would
       * have called VThreadBase_SetName() earlier.
       *
       * Use a static buffer for a partly-sane name and hope the
       * Panic handler dumps enough information to figure out what went
       * wrong. If better accuracy is needed, add thread-local storage
       * support for the platform.
       */

      static char name[48];
      VThreadBaseSafeName(name, sizeof name);
      return name;
   }
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBase_SetName --
 *
 *      Override the default thread name with a new name.
 *
 *      Historical: this subsumes the behavior of the old
 *      lib/nothread VThread_Init, replacing it with something that is
 *      optional.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      If TLS is not supported, allocates thread-local memory which leaks
 *      on thread exit. (If this leak is a problem, implement TLS).
 *
 *-----------------------------------------------------------------------------
 */

void
VThreadBase_SetName(const char *name)  // IN: new name
{
   ASSERT(name != NULL);

   if (vmx86_debug && strlen(name) >= VTHREADBASE_MAX_NAME) {
      Warning("%s: thread name (%s) exceeds maximum length (%u)\n",
              __FUNCTION__, name, (uint32)VTHREADBASE_MAX_NAME - 1);
   }

#if defined VMW_HAVE_TLS
   /*
    * Never copy last byte; this ensures NUL-term is always present.
    * The NUL-term is always present because vthreadName is static,
    * but gcc-8 generates a warning if it doesn't see it being explicilty
    * set.
    */

   strncpy(vthreadName, name, sizeof vthreadName - 1);
   vthreadName[sizeof vthreadName - 1] = '\0';
#else
   do {
      char *buf;

      ASSERT(vthreadNameKey != 0);
      ASSERT(!VThreadBase_IsInSignal());  // Cannot alloc in signal handler

      /*
       * The below code is racy (signal between get/set), but the
       * race is benign. The signal handler would either get a safe
       * name or "" (if it interrupts before setspecific or before strncpy,
       * respectively). But as signal handlers may not call SetName,
       * this will not double-allocate.
       */
      buf = pthread_getspecific(vthreadNameKey);
      if (buf == NULL) {
         buf = Util_SafeCalloc(1, VTHREADBASE_MAX_NAME);
         pthread_setspecific(vthreadNameKey, buf);
      }

      strncpy(buf, name, VTHREADBASE_MAX_NAME - 1);
   } while(0);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBase_SetNamePrefix --
 *
 *      Override the default thread name with a new name based on
 *      the supplied prefix. Format is "{prefix}-{id}"
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
VThreadBase_SetNamePrefix(const char *prefix)  // IN: name prefix
{
   char buf[VTHREADBASE_MAX_NAME];

   ASSERT(prefix != NULL);

   snprintf(buf, sizeof buf, "%s-%" FMT64 "u",
            prefix, VThreadBase_GetKernelID());
   buf[sizeof buf - 1] = '\0';  // snprintf does not ensure NUL-term
   VThreadBase_SetName(buf);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBase_ForgetSelf --
 *
 *      Forget the TLS parts of a thread.
 *
 *      If not intending to reallocate TLS, avoid querying the thread's
 *      VThread_CurName between this call and thread destruction.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
VThreadBase_ForgetSelf(void)
{
#if !defined VMW_HAVE_TLS
   char *buf;
#endif

   if (vmx86_debug) {
      Log("Forgetting VThreadID %" FMTPD "u (\"%s\").\n",
          VThread_CurID(), VThread_CurName());
   }

   /*
    * The VThreadID is fixed (see StableID above).
    * Only the name needs clearing.
    */
#if defined VMW_HAVE_TLS
   memset(vthreadName, '\0', sizeof vthreadName);
#else
   ASSERT(vthreadNameKey != 0);
   ASSERT(!VThreadBase_IsInSignal());

   buf = pthread_getspecific(vthreadNameKey);
   pthread_setspecific(vthreadNameKey, NULL);
   free(buf);
#endif
}


#if !defined _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBase_IsInSignal --
 *
 *      Accessor for whether current thread is or is not in a signal.
 *      lib/sig handles keeping this accurate.
 *
 * Results:
 *      Returns TRUE if a signal handler is somewhere on the stack.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
VThreadBase_IsInSignal(void)
{
#if defined VMW_HAVE_TLS
   return sigNestCount > 0;
#else
   return (intptr_t)pthread_getspecific(sigNestCountKey) > 0;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VThreadBase_SetIsInSignal --
 *
 *      Marks the current thread as or as not being inside a signal handler.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      As above.
 *
 *-----------------------------------------------------------------------------
 */

void
VThreadBase_SetIsInSignal(Bool isInSignal)  // IN:
{
#if defined VMW_HAVE_TLS
   sigNestCount += (isInSignal ? 1 : -1);
#else
   intptr_t cnt;
   ASSERT(sigNestCountKey != 0);
   cnt = (intptr_t)pthread_getspecific(sigNestCountKey);
   cnt += (isInSignal ? 1 : -1);
   (void) pthread_setspecific(sigNestCountKey, (void *)cnt);
#endif
}
#endif
  070701000001A7000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/netUtil    070701000001A8000081A40000000000000000000000016822550500000461000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/netUtil/Makefile.am    ################################################################################
### Copyright (C) 2007-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libNetUtil.la

libNetUtil_la_SOURCES =
libNetUtil_la_SOURCES += netUtilLinux.c

libNetUtil_la_CPPFLAGS =
libNetUtil_la_CPPFLAGS += @XDR_CPPFLAGS@

libNetUtil_la_LIBADD =
libNetUtil_la_LIBADD += @XDR_LIBS@

   070701000001A9000081A40000000000000000000000016822550500002870000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/netUtil/netUtilLinux.c /*********************************************************
 * Copyright (C) 1998-2017, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * netUtilLinux.c --
 *
 *    Network routines for all guest applications.
 *
 *    Linux implementation
 *
 */

#ifndef VMX86_DEVEL

#endif


#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(sun) && !defined(__APPLE__)
#   error This file should not be compiled
#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/times.h>
#include <netdb.h>
#ifdef sun
# include <sys/sockio.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
/* <netinet/in.h> must precede <arpa/in.h> for FreeBSD to compile. */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>         // for ARPHRD_ETHER

#if defined(__FreeBSD__) || defined(__APPLE__)
#include "ifaddrs.h"
#endif

#include "vm_assert.h"
#include "netutil.h"
#include "debug.h"
#include "guestApp.h"
#include "util.h"
#include "str.h"

#define MAX_IFACES      64
#define LOOPBACK        "lo"    // XXX: We would have a problem with something like "loa0".
#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * ValidateConvertAddress --
 *
 *      Helper routine validates an address as a return value for
 *      NetUtil_GetPrimaryIP.
 *
 * Results:
 *      Returns TRUE with sufficient result stored in outputBuffer on success.
 *      Returns FALSE with "" stored in outputBuffer on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
ValidateConvertAddress(const char *ifaceName,           // IN: interface name
                       const struct sockaddr_in *addr,  // IN: network address to
                                                        //     evaluate
                       char ipstr[INET_ADDRSTRLEN])     // OUT: converted address
                                                        //      stored here
{
   /*
    * 1.  Ensure this isn't a loopback device.
    * 2.  Ensure this is an (IPv4) internet address.
    */
   if (ifaceName[0] == '\0' ||
       strncmp(ifaceName, LOOPBACK, sizeof LOOPBACK - 1) == 0 ||
       addr->sin_family != AF_INET) {
      goto invalid;
   }

   /*
    * Branches separated because it just looked really silly to lump the
    * initial argument checking and actual conversion logic together.
    */

   /*
    * 3.  Attempt network to presentation conversion.
    * 4.  Ensure the IP isn't all zeros.
    */
   if (inet_ntop(AF_INET, (void *)&addr->sin_addr, ipstr, INET_ADDRSTRLEN) != NULL &&
       strcmp(ipstr, "0.0.0.0") != 0) {
      return TRUE;
   }

invalid:
   ipstr[0] = '\0';
   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * NetUtil_GetPrimaryIP --
 *
 *      Get the primary IP for this machine.
 *
 * Results:
 *      If applicable address found, returns string of said IP address.
 *      If applicable address not found, returns an empty string.
 *      If an error occurred, returns NULL.
 *
 * Side effects:
 *	Caller is responsible for free()ing returned string.
 *
 *----------------------------------------------------------------------
 */

#if !defined(__FreeBSD__) && !defined(__APPLE__) /* { */
char *
NetUtil_GetPrimaryIP(void)
{
   int sd, i;
   struct ifconf iflist;
   struct ifreq ifaces[MAX_IFACES];
   char ipstr[INET_ADDRSTRLEN] = "";

   /* Get a socket descriptor to give to ioctl(). */
   sd = socket(PF_INET, SOCK_STREAM, 0);
   if (sd < 0) {
      return NULL;
   }

   memset(&iflist, 0, sizeof iflist);
   memset(ifaces, 0, sizeof ifaces);

   /* Tell ioctl where to write interface list to and how much room it has. */
   iflist.ifc_req = ifaces;
   iflist.ifc_len = sizeof ifaces;

   if (ioctl(sd, SIOCGIFCONF, &iflist) < 0) {
      close(sd);
      return NULL;
   }

   close(sd);

   /* Loop through the list of interfaces provided by ioctl(). */
   for (i = 0; i < (sizeof ifaces/sizeof *ifaces); i++) {
      if (ValidateConvertAddress(ifaces[i].ifr_name,
                                 (struct sockaddr_in *)&ifaces[i].ifr_addr,
                                 ipstr)) {
         break;
      }
   }

   /* Success.  Here, caller, you can throw this away. */
   return strdup(ipstr);
}

#else /* } FreeBSD || APPLE { */

char *
NetUtil_GetPrimaryIP(void)
{
   struct ifaddrs *ifaces;
   struct ifaddrs *curr;
   char ipstr[INET_ADDRSTRLEN] = "";

   /*
    * getifaddrs(3) creates a NULL terminated linked list of interfaces for us
    * to traverse and places a pointer to it in ifaces.
    */
   if (getifaddrs(&ifaces) < 0) {
      return NULL;
   }

   /*
    * We traverse the list until there are no more interfaces or we have found
    * the primary interface.  This function defines the primary interface to be
    * the first non-loopback, internet interface in the interface list.
    */
   for(curr = ifaces; curr != NULL; curr = curr->ifa_next) {
      if (ValidateConvertAddress(curr->ifa_name,
                                 (struct sockaddr_in *)curr->ifa_addr,
                                 ipstr)) {
         break;
      }
   }

   /* Tell FreeBSD to free our linked list. */
   freeifaddrs(ifaces);

   /* Success.  Here, caller, you can throw this away. */
   return strdup(ipstr);
}
#endif /* } */


/*
 *----------------------------------------------------------------------
 *
 * NetUtil_GetPrimaryNic --
 *
 *      Get the primary Nic entry for this machine. Primary Nic is the 
 *      first interface that comes up when you do a ifconfig.
 *
 * Results:
 *      The primary NIC entry or NULL if an error occurred. In nicEntry
 *      returned, only IP address is retuend. All other fields remain zero. 
 *
 * Side effects:
 *      Memory is allocated for the returned NicEntry. Caller is 
 *      supposed to free it after use.
 *
 *----------------------------------------------------------------------
 */

GuestNic *
NetUtil_GetPrimaryNic(void)
{
   GuestNic *nicEntry = NULL;
   VmIpAddress *ip;
   char *ipstr;

   ipstr = NetUtil_GetPrimaryIP();
   if (NULL == ipstr) {
      goto quit;
   }

   nicEntry = Util_SafeCalloc(1, sizeof *nicEntry);
   ip = Util_SafeCalloc(1, sizeof *ip);

   nicEntry->ips.ips_len = 1;
   nicEntry->ips.ips_val = ip;

   Str_Strcpy(ip->ipAddress, ipstr, sizeof ip->ipAddress);
   free(ipstr);

quit:
   return nicEntry;
}


#if defined(__linux__)
#   ifdef DUMMY_NETUTIL
/*
 *-----------------------------------------------------------------------------
 *
 * NetUtil_GetIfIndex (dummy version) --
 *
 *      Given an interface name, return its index.
 *
 * Results:
 *      Returns a valid interface index on success or -1 on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
NetUtil_GetIfIndex(const char *ifName)  // IN: interface name
{
   NetUtilIfTableEntry *iterator;
   int ifIndex = -1;

   ASSERT(netUtilIfTable != NULL);

   for (iterator = netUtilIfTable; iterator->ifName; iterator++) {
      if (strcmp(iterator->ifName, ifName) == 0) {
         ifIndex = iterator->ifIndex;
         break;
      }
   }

   return ifIndex;
}


/*
 *-----------------------------------------------------------------------------
 *
 * NetUtil_GetIfName (dummy version) --
 *
 *      Given an interface index, return its name.
 *
 * Results:
 *      Returns a valid interface name on success, NULL on failure.
 *
 * Side effects:
 *      Caller is responsible for freeing the returned string.
 *
 *-----------------------------------------------------------------------------
 */

char *
NetUtil_GetIfName(int ifIndex)  // IN: interface index
{
   NetUtilIfTableEntry *iterator;
   char *ifName = NULL;

   ASSERT(netUtilIfTable != NULL);

   for (iterator = netUtilIfTable; iterator->ifName; iterator++) {
      if (iterator->ifIndex == ifIndex) {
         ifName = Util_SafeStrdup(iterator->ifName);
         break;
      }
   }

   return ifName;
}


#   endif // ifdef DUMMY_NETUTIL


/*
 *-----------------------------------------------------------------------------
 *
 * NetUtil_GetHardwareAddress --
 *
 *      Given an interface name, return its hardware/link layer address.
 *
 * Results:
 *      Returns TRUE and populates hwAddr on success.
 *      Returns FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

size_t
NetUtil_GetHardwareAddress(int ifIndex,         // IN
                           char *hwAddr,        // OUT
                           size_t hwAddrSize,   // IN
                           IanaIfType *ifType)  // OUT
{
   struct ifreq ifreq;
   int fd = -1;
   size_t ret = 0;

   if (hwAddrSize < IFHWADDRLEN) {
      return FALSE;
   }

   ASSERT(sizeof ifreq.ifr_name >= IF_NAMESIZE);

   memset(&ifreq, 0, sizeof ifreq);
   if (if_indextoname(ifIndex, ifreq.ifr_name) == NULL) {
      return FALSE;
   }

   if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
      return FALSE;
   }

   if (ioctl(fd, SIOCGIFHWADDR, &ifreq) == 0 &&
       ifreq.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
      memcpy(hwAddr, ifreq.ifr_hwaddr.sa_data, IFHWADDRLEN);
      *ifType = IANA_IFTYPE_ETHERNETCSMACD;
      ret = IFHWADDRLEN;
   }

   close(fd);

   return ret;
}


#endif // if defined(__linux__)
070701000001AA000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/nicInfo    070701000001AB000081A4000000000000000000000001682255050000058B000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/nicInfo/Makefile.am    ################################################################################
### Copyright (C) 2014-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libNicInfo.la

libNicInfo_la_SOURCES =
libNicInfo_la_SOURCES += compareNicInfo.c
libNicInfo_la_SOURCES += util.c
libNicInfo_la_SOURCES += nicInfo.c
libNicInfo_la_SOURCES += nicInfoPosix.c

libNicInfo_la_CPPFLAGS =
libNicInfo_la_CPPFLAGS += @GLIB2_CPPFLAGS@
libNicInfo_la_CPPFLAGS += @XDR_CPPFLAGS@
libNicInfo_la_CPPFLAGS += $(DNET_CPPFLAGS)
if USE_SLASH_PROC
libNicInfo_la_CPPFLAGS += -DUSE_SLASH_PROC
endif

libNicInfo_la_LIBADD =
libNicInfo_la_LIBADD += @DNET_LIBS@
libNicInfo_la_LIBADD += @XDR_LIBS@

 070701000001AC000081A40000000000000000000000016822550500003583000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/nicInfo/compareNicInfo.c   /*********************************************************
 * Copyright (C) 2014-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file compareNicInfo.c
 *
 * Comparison routines for NicInfo types.  Handy for caching, unit testing.
 *
 * @todo Each byte of a MAC address is assumed to be represented by two
 * characters.  So, as far as these routines are concerned, 0:1:2:3:4:5
 * != 00:01:02:03:04:05.  Is this a problem?
 */

#include <string.h>

#include "vmware.h"
#include "xdrutil.h"

#include "nicInfoInt.h"


/**
 * Common comparison prefix routine.
 */
#define RETURN_EARLY_CMP_PTRS(a, b)  do {                               \
   if (!(a) && !(b)) {                                                  \
      return TRUE;                                                      \
   } else if ((!(a) && (b)) || ((a) && !(b))) {                         \
      return FALSE;                                                     \
   }                                                                    \
} while (0)


/*
 ******************************************************************************
 * GuestInfo_IsEqual_DhcpConfigInfo --                                   */ /**
 *
 * Compares a pair of DhcpConfigInfos.
 *
 * @param[in] a DhcpConfigInfo number 1.  May be NULL.
 * @param[in] b DhcpConfigInfo number 2.  May be NULL.
 *
 * @retval TRUE  DhcpConfigInfos are equivalent.
 * @retval FALSE DhcpConfigInfos differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_DhcpConfigInfo(const DhcpConfigInfo *a,
                                 const DhcpConfigInfo *b)
{
   RETURN_EARLY_CMP_PTRS(a, b);

   return a->enabled == b->enabled &&
          strcmp(a->dhcpSettings, b->dhcpSettings) == 0;
}


/*
 ******************************************************************************
 * GuestInfo_IsEqual_DnsConfigInfo --                                    */ /**
 *
 * Compares a pair of DnsConfigInfos.
 *
 * @param[in] a DnsConfigInfo number 1.  May be NULL.
 * @param[in] b DnsConfigInfo number 2.  May be NULL.
 *
 * @retval TRUE  DnsConfigInfos are equivalent.
 * @retval FALSE DnsConfigInfos differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_DnsConfigInfo(const DnsConfigInfo *a,
                                const DnsConfigInfo *b)
{
   u_int ai;
   u_int bi;

   RETURN_EARLY_CMP_PTRS(a, b);

   if (!GuestInfo_IsEqual_DnsHostname(a->hostName, b->hostName) ||
       !GuestInfo_IsEqual_DnsHostname(a->domainName, b->domainName) ||
       a->serverList.serverList_len != b->serverList.serverList_len ||
       a->searchSuffixes.searchSuffixes_len != b->searchSuffixes.searchSuffixes_len) {
      return FALSE;
   }

   /*
    * Since the lists' lengths match, search in b for each item in a.  We'll
    * assume that we don't have any duplicates in a s.t. unique(a) is a proper
    * subset of b.
    *
    * Bail if we can't find an entry.
    */

   XDRUTIL_FOREACH(ai, a, serverList) {
      TypedIpAddress *aServer = XDRUTIL_GETITEM(a, serverList, ai);

      XDRUTIL_FOREACH(bi, b, serverList) {
         TypedIpAddress *bServer = XDRUTIL_GETITEM(b, serverList, bi);

         if (GuestInfo_IsEqual_TypedIpAddress(aServer, bServer)) {
            break;
         }
      }

      if (bi == b->serverList.serverList_len) {
         /* Exhausted b's list, didn't find aServer. */
         return FALSE;
      }
   }

   XDRUTIL_FOREACH(ai, a, searchSuffixes) {
      DnsHostname *aSuffix = XDRUTIL_GETITEM(a, searchSuffixes, ai);

      XDRUTIL_FOREACH(bi, b, searchSuffixes) {
         DnsHostname *bSuffix = XDRUTIL_GETITEM(b, searchSuffixes, bi);

         if (GuestInfo_IsEqual_DnsHostname(aSuffix, bSuffix)) {
            break;
         }
      }

      if (bi == b->searchSuffixes.searchSuffixes_len) {
         /* Exhausted b's list, didn't find aSuffix. */
         return FALSE;
      }
   }

   return TRUE;
}


/*
 ******************************************************************************
 * GuestInfo_IsEqual_DnsHostname --                                      */ /**
 *
 * Compares a pair of DnsHostnames.
 *
 * @param[in] a DnsHostname number 1.  May be NULL.
 * @param[in] b DnsHostname number 2.  May be NULL.
 *
 * @retval TRUE  DnsHostnames are equivalent.
 * @retval FALSE DnsHostnames differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_DnsHostname(const DnsHostname *a,
                              const DnsHostname *b)
{
   RETURN_EARLY_CMP_PTRS(a, b);
   return strcasecmp(*a, *b) == 0 ? TRUE : FALSE;
}


/*
 ******************************************************************************
 * GuestInfo_IsEqual_GuestNicV3 --                                       */ /**
 *
 * Compares two GuestNicV3s.
 *
 * @param[in] a GuestNicV3 number 1.  May be NULL.
 * @param[in] b GuestNicV3 number 2.  May be NULL.
 *
 * @retval TRUE  GuestNicV3s are equivalent.
 * @retval FALSE GuestNicV3s differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_GuestNicV3(const GuestNicV3 *a,
                             const GuestNicV3 *b)
{
   u_int ai;
   u_int bi;

   RETURN_EARLY_CMP_PTRS(a, b);

   /* Not optional fields. */
   ASSERT(a->macAddress);
   ASSERT(b->macAddress);

   if (strcasecmp(a->macAddress, b->macAddress) != 0) {
      return FALSE;
   }

   /*
    * Compare the IP lists.
    */

   if (a->ips.ips_len != b->ips.ips_len) {
      return FALSE;
   }

   XDRUTIL_FOREACH(ai, a, ips) {
      IpAddressEntry *aEntry = XDRUTIL_GETITEM(a, ips, ai);

      XDRUTIL_FOREACH(bi, b, ips) {
         IpAddressEntry *bEntry = XDRUTIL_GETITEM(b, ips, bi);

         if (GuestInfo_IsEqual_IpAddressEntry(aEntry, bEntry)) {
            break;
         }
      }

      if (bi == b->ips.ips_len) {
         /* Exhausted b's list, didn't find aEntry. */
         return FALSE;
      }
   }

   return
      GuestInfo_IsEqual_DnsConfigInfo(a->dnsConfigInfo, b->dnsConfigInfo) &&
      GuestInfo_IsEqual_WinsConfigInfo(a->winsConfigInfo, b->winsConfigInfo) &&
      GuestInfo_IsEqual_DhcpConfigInfo(a->dhcpConfigInfov4, b->dhcpConfigInfov4) &&
      GuestInfo_IsEqual_DhcpConfigInfo(a->dhcpConfigInfov6, b->dhcpConfigInfov6);
}


/*
 ******************************************************************************
 * GuestInfo_IsEqual_InetCidrRouteEntry --                               */ /**
 *
 * Compares two InetCidrRouteEntrys.
 *
 * @param[in] a     InetCidrRouteEntry number 1.  May be NULL.
 * @param[in] b     InetCidrRouteEntry number 2.  May be NULL.
 * @param[in] aInfo a's NicInfo container.  If a != NULL, may NOT be NULL.
 * @param[in] bInfo b's NicInfo container.  If b != NULL, may NOT be NULL.
 *
 * @retval TRUE  InetCidrRouteEntrys are equivalent.
 * @retval FALSE InetCidrRouteEntrys differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_InetCidrRouteEntry(const InetCidrRouteEntry *a,
                                     const InetCidrRouteEntry *b,
                                     const NicInfoV3 *aInfo,
                                     const NicInfoV3 *bInfo)
{
   RETURN_EARLY_CMP_PTRS(a, b);

   ASSERT(aInfo);
   ASSERT(bInfo);

   return
      GuestInfo_IsEqual_TypedIpAddress(&a->inetCidrRouteDest,
                                       &b->inetCidrRouteDest) &&
      a->inetCidrRoutePfxLen == b->inetCidrRoutePfxLen &&
      GuestInfo_IsEqual_TypedIpAddress(a->inetCidrRouteNextHop,
                                       b->inetCidrRouteNextHop) &&
      strcasecmp(aInfo->nics.nics_val[a->inetCidrRouteIfIndex].macAddress,
                 bInfo->nics.nics_val[b->inetCidrRouteIfIndex].macAddress) == 0 &&
      a->inetCidrRouteType == b->inetCidrRouteType &&
      a->inetCidrRouteMetric == b->inetCidrRouteMetric;
}


/*
 ******************************************************************************
 * GuestInfo_IsEqual_IpAddressEntry --                                   */ /**
 *
 * Compares two IpAddressEntrys.
 *
 * @param[in] a IpAddressEntry number 1.  May be NULL.
 * @param[in] b IpAddressEntry number 2.  May be NULL.
 *
 * @retval TRUE  IpAddressEntrys are equivalent.
 * @retval FALSE IpAddressEntrys differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_IpAddressEntry(const IpAddressEntry *a,
                                 const IpAddressEntry *b)
{
   RETURN_EARLY_CMP_PTRS(a, b);

   return
      GuestInfo_IsEqual_TypedIpAddress(&a->ipAddressAddr, &b->ipAddressAddr) &&
      a->ipAddressPrefixLength == b->ipAddressPrefixLength &&
      ((a->ipAddressOrigin == NULL && b->ipAddressOrigin == NULL) ||
       (a->ipAddressOrigin != NULL && b->ipAddressOrigin != NULL &&
        *a->ipAddressOrigin == *b->ipAddressOrigin)) &&
      ((a->ipAddressStatus == NULL && b->ipAddressStatus == NULL) ||
       (a->ipAddressStatus != NULL && b->ipAddressStatus != NULL &&
        *a->ipAddressStatus == *b->ipAddressStatus));
}


/*
 ******************************************************************************
 * GuestInfo_IsEqual_NicInfoV3 --                                        */ /**
 *
 * Compares two NicInfoV3s.
 *
 * @param[in] a NicInfoV3 number 1.  May be NULL.
 * @param[in] b NicInfoV3 number 2.  May be NULL.
 *
 * @retval TRUE  NicInfoV3s are equivalent.
 * @retval FALSE NicInfoV3s differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_NicInfoV3(const NicInfoV3 *a,
                            const NicInfoV3 *b)
{
   u_int ai;
   u_int bi;

   RETURN_EARLY_CMP_PTRS(a, b);

   /*
    * Compare the NIC lists.
    */

   if (a->nics.nics_len != b->nics.nics_len) {
      return FALSE;
   }

   XDRUTIL_FOREACH(ai, a, nics) {
      GuestNicV3 *eachNic = XDRUTIL_GETITEM(a, nics, ai);
      GuestNicV3 *cmpNic = GuestInfoUtilFindNicByMac(b, eachNic->macAddress);

      if (cmpNic == NULL ||
          !GuestInfo_IsEqual_GuestNicV3(eachNic, cmpNic)) {
         return FALSE;
      }
   }

   /*
    * Compare routes.
    */

   if (a->routes.routes_len != b->routes.routes_len) {
      return FALSE;
   }

   XDRUTIL_FOREACH(ai, a, routes) {
      InetCidrRouteEntry *aRoute = XDRUTIL_GETITEM(a, routes, ai);

      XDRUTIL_FOREACH(bi, b, routes) {
         InetCidrRouteEntry *bRoute = XDRUTIL_GETITEM(b, routes, bi);

         if (GuestInfo_IsEqual_InetCidrRouteEntry(aRoute, bRoute, a, b)) {
            break;
         }
      }

      if (bi == b->routes.routes_len) {
         /* Exhausted b's list, didn't find aRoute. */
         return FALSE;
      }
   }

   /*
    * Compare the stack settings:
    *    . DnsConfigInfo
    *    . WinsConfigInfo
    *    . DhcpConfigInfov4
    *    . DhcpConfigInfov6
    */

   return
      GuestInfo_IsEqual_DnsConfigInfo(a->dnsConfigInfo, b->dnsConfigInfo) &&
      GuestInfo_IsEqual_WinsConfigInfo(a->winsConfigInfo, b->winsConfigInfo) &&
      GuestInfo_IsEqual_DhcpConfigInfo(a->dhcpConfigInfov4, b->dhcpConfigInfov4) &&
      GuestInfo_IsEqual_DhcpConfigInfo(a->dhcpConfigInfov6, b->dhcpConfigInfov6);
}


/*
 ******************************************************************************
 * GuestInfo_IsEqual_TypedIpAddress --                                   */ /**
 *
 * Compares two TypedIpAddresses.
 *
 * @param[in] a TypedIpAddress number 1.  May be NULL.
 * @param[in] b TypedIpAddress number 2.  May be NULL.
 *
 * @retval TRUE  TypedIpAddresses are equivalent.
 * @retval FALSE TypedIpAddresses differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_TypedIpAddress(const TypedIpAddress *a,
                                 const TypedIpAddress *b)
{
   RETURN_EARLY_CMP_PTRS(a, b);

   if (a->ipAddressAddrType != b->ipAddressAddrType ||
       memcmp(a->ipAddressAddr.InetAddress_val,
              b->ipAddressAddr.InetAddress_val,
              a->ipAddressAddr.InetAddress_len)) {
      return FALSE;
   }

   return TRUE;
}


/*
 ******************************************************************************
 * GuestInfo_IsEqual_WinsConfigInfo --                                   */ /**
 *
 * Compares a pair of WinsConfigInfos.
 *
 * @param[in] a WinsConfigInfo number 1.  May be NULL.
 * @param[in] b WinsConfigInfo number 2.  May be NULL.
 *
 * @retval TRUE  WinsConfigInfos are equivalent.
 * @retval FALSE WinsConfigInfos differ.
 *
 ******************************************************************************
 */

Bool
GuestInfo_IsEqual_WinsConfigInfo(const WinsConfigInfo *a,
                                 const WinsConfigInfo *b)
{
   RETURN_EARLY_CMP_PTRS(a, b);

   return GuestInfo_IsEqual_TypedIpAddress(&a->primary, &b->primary) &&
          GuestInfo_IsEqual_TypedIpAddress(&a->secondary, &b->secondary);
}
 070701000001AD000081A40000000000000000000000016822550500004BB3000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/nicInfo/nicInfo.c  /*********************************************************
 * Copyright (c) 2014-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file nicInfo.c
 *
 *	Library backing parts of the vm.GuestInfo VIM APIs.
 */

#include <stdlib.h>
#include <string.h>

#if defined _WIN32
#   include <ws2tcpip.h>
#endif

#include "vm_assert.h"
#include "debug.h"
#include "nicInfoInt.h"
#include "str.h"
#include "util.h"
#include "xdrutil.h"
#include "netutil.h"
#include "wiper.h"

static GPtrArray *gIfaceExcludePatterns = NULL;
static GPtrArray *gIfacePrimaryPatterns = NULL;
static GPtrArray *gIfaceLowPriorityPatterns = NULL;

/**
 * Helper to initialize an opaque struct member.
 *
 * @todo Move to xdrutil.h?  Sticking point is dependency on Util_SafeMalloc.
 */
#define XDRUTIL_SAFESETOPAQUE(ptr, type, src, size)                     \
   do {                                                                 \
      (ptr)->type##_len = (size);                                       \
      (ptr)->type##_val = Util_SafeMalloc((size));                      \
      memcpy((ptr)->type##_val, (src), (size));                         \
   } while (0)

static void * Util_DupeThis(const void *source, size_t sourceSize);

/*
 * Global functions.
 */

/*
 ******************************************************************************
 *
 * GuestInfoResetPatternList --
 *
 * @brief Create list of patterns (to be used with the 'exclude-nics',
 * 'primary-nics' and low-priority options).
 *
 * @param[in]   list          NULL terminated array of pointers to strings with
 *                            patterns
 * @param[out]  pPatternList  pointer to the list to be created. If not NULL,
 *                            pPatternList will be freed.
 *
 ******************************************************************************
 */

static void
GuestInfoResetPatternList(char **list,
                          GPtrArray **pPatternList)
{
   guint i;

   if (*pPatternList != NULL) {
      g_ptr_array_free(*pPatternList, TRUE);
      *pPatternList = NULL;
   }

   if (list != NULL) {
      *pPatternList =
         g_ptr_array_new_with_free_func((GDestroyNotify) &g_pattern_spec_free);
      for (i = 0; list[i] != NULL; i++) {
         if (list[i][0] != '\0') {
            g_ptr_array_add(*pPatternList, g_pattern_spec_new(list[i]));
         }
      }
   }
}


/*
 ******************************************************************************
 *
 * GuestInfo_SetIfacePrimaryList --
 *
 * @brief Set list of network interfaces that can be considered primary
 *
 * @param[in] NULL terminated array of pointers to strings with patterns
 *
 * @sa gIfacePrimaryPatterns will be set
 *
 ******************************************************************************
 */

void
GuestInfo_SetIfacePrimaryList(char **list)
{
   GuestInfoResetPatternList(list, &gIfacePrimaryPatterns);
}


/*
 *******************************************************************************
 *
 * GuestInfo_SetIfaceLowPriorityList --
 *
 * @brief Set list of network interfaces that can be considered low priority
 *
 * @param[in] NULL terminated array of pointers to strings with patterns
 *
 * @sa gIfaceLowPriorityPatterns will be set
 *
 *******************************************************************************
 */

void
GuestInfo_SetIfaceLowPriorityList(char **list)
{
   GuestInfoResetPatternList(list, &gIfaceLowPriorityPatterns);
}


/*
 *******************************************************************************
 *
 * GuestInfo_SetIfaceExcludeList --
 *
 * @brief Set list of network interfaces to be excluded
 *
 * @param[in] NULL terminated array of pointers to strings with patterns
 *
 * @sa gIfaceExcludePatterns will be set
 *
 *******************************************************************************
 */


void
GuestInfo_SetIfaceExcludeList(char **list)
{
   GuestInfoResetPatternList(list, &gIfaceExcludePatterns);
}


/*
 ******************************************************************************
 *
 * GuestInfoMatchesPatternList --
 *
 * @brief Determine if a specific name matches a pattern in a list
 *
 * @param[in] The interface name.
 * @param[in] The list of patterns
 *
 * @retval TRUE if the name matches one of the patterns in the list.
 *
 ******************************************************************************
*/

static Bool
GuestInfoMatchesPatternList(const char *name,
                            const GPtrArray *patterns)
{
   int i;

   ASSERT(name);
   ASSERT(patterns);

   for (i = 0; i < patterns->len; i++) {
      if (g_pattern_match_string(g_ptr_array_index(patterns, i),
                                 name)) {
         g_debug("%s: interface %s matched pattern %d",
                 __FUNCTION__, name, i);
         return TRUE;
      }
   }
   return FALSE;
}


/*
 ******************************************************************************
 *
 * GuestInfo_IfaceIsExcluded --
 *
 * @brief Determine if a specific interface name shall be excluded.
 *
 * @param[in] The interface name.
 *
 * @retval TRUE if interface name shall be excluded.
 *
 ******************************************************************************
*/

Bool GuestInfo_IfaceIsExcluded(const char *name)
{
   ASSERT(name);
   return gIfaceExcludePatterns != NULL &&
          GuestInfoMatchesPatternList(name, gIfaceExcludePatterns);
}


/*
 ******************************************************************************
 *
 * GuestInfo_IfaceGetPriority --
 *
 * @brief Determine priority of an interface
 *
 * @param[in] The interface name.
 *
 * @retval one of NICINFO_PRIORITY_PRIMARY, NICINFO_PRIORITY_LOW or
 * NICINFO_PRIORITY_NORMAL
 *
 ******************************************************************************
*/

NicInfoPriority
GuestInfo_IfaceGetPriority(const char *name)
{
   ASSERT(name);
   g_debug("%s: checking %s", __FUNCTION__, name);
   if (gIfacePrimaryPatterns != NULL &&
       GuestInfoMatchesPatternList(name, gIfacePrimaryPatterns)) {
      return NICINFO_PRIORITY_PRIMARY;
   } else if (gIfaceLowPriorityPatterns != NULL &&
              GuestInfoMatchesPatternList(name, gIfaceLowPriorityPatterns)) {
      return NICINFO_PRIORITY_LOW;
   }
   return NICINFO_PRIORITY_NORMAL;
}


/*
 ******************************************************************************
 * GuestInfo_GetFqdn --                                                  */ /**
 *
 * @brief Returns the guest's hostname (aka fully qualified domain name, FQDN).
 *
 * @param[in]  outBufLen Size of outBuf.
 * @param[out] outBuf    Output buffer.
 *
 * @retval TRUE  Success.  Hostname written to @a outBuf.
 * @retval FALSE Failure.
 *
 ******************************************************************************
 */

Bool
GuestInfo_GetFqdn(int outBufLen,
                  char fqdn[])
{
   return GuestInfoGetFqdn(outBufLen, fqdn);
}


/*
 ******************************************************************************
 * GuestInfo_GetNicInfo --                                               */ /**
 *
 * @brief Returns guest networking configuration (and some runtime state).
 *
 * @param[in]  maxIPv4Routes  Max IPv4 routes to gather.
 * @param[in]  maxIPv6Routes  Max IPv6 routes to gather.
 * @param[out] nicInfo        Will point to a newly allocated NicInfo.
 * @param[out] maxNicsError   To determine NIC max limit error.
 *
 * @note
 * Caller is responsible for freeing @a nicInfo with GuestInfo_FreeNicInfo.
 *
 * @retval TRUE  Success.  @a nicInfo now points to a populated NicInfoV3.
 * @retval FALSE Failure.
 *
 ******************************************************************************
 */

Bool
GuestInfo_GetNicInfo(unsigned int maxIPv4Routes,
                     unsigned int maxIPv6Routes,
                     NicInfoV3 **nicInfo,
                     Bool *maxNicsError)
{
   Bool retval;

   *nicInfo = Util_SafeCalloc(1, sizeof (struct NicInfoV3));

   retval = GuestInfoGetNicInfo(maxIPv4Routes, maxIPv6Routes, *nicInfo,
                                maxNicsError);
   if (!retval) {
      GuestInfo_FreeNicInfo(*nicInfo);
      *nicInfo = NULL;
   }

   return retval;
}


/*
 ******************************************************************************
 * GuestInfo_FreeNicInfo --                                              */ /**
 *
 * @brief Frees a NicInfoV3 structure and all memory it points to.
 *
 * @param[in] nicInfo   Pointer to NicInfoV3 container.
 *
 * @sa GuestInfo_GetNicInfo
 *
 ******************************************************************************
 */

void
GuestInfo_FreeNicInfo(NicInfoV3 *nicInfo)
{
   if (nicInfo != NULL) {
      VMX_XDR_FREE(xdr_NicInfoV3, nicInfo);
      free(nicInfo);
   }
}


/*
 ******************************************************************************
 * GuestInfo_GetPrimaryIP --                                             */ /**
 *
 * @brief Get the primary IP address on the running machine.
 *
 * @note Caller is responsible for free()ing returned string.
 *
 * @return  If applicable address found, returns string of said IP address.
 *          If applicable address not found, returns an empty string.
 *
 ******************************************************************************
 */

char *
GuestInfo_GetPrimaryIP(void)
{
   char *ipstr = GuestInfoGetPrimaryIP();

   if (ipstr == NULL) {
      ipstr = Util_SafeStrdup("");
   }

   return ipstr;
}


/*
 * Private library functions.
 */


/*
 ******************************************************************************
 * GuestInfoAddNicEntry --                                               */ /**
 *
 * @brief GuestNicV3 constructor.
 *
 * @param[in,out] nicInfo       List of NICs.
 * @param[in]     macAddress    MAC address of new NIC.
 * @param[in]     dnsInfo       Per-NIC DNS config state.
 * @param[in]     winsInfo      Per-NIC WINS config state.
 * @param[out]    maxNicsError  To determine NIC max limit error.
 *
 * @note The returned GuestNic will take ownership of @a dnsInfo and
 *       @a winsInfo  The caller must not free it directly.
 *
 * @return Pointer to the new NIC, or NULL if NIC limit was reached.
 *
 ******************************************************************************
 */

GuestNicV3 *
GuestInfoAddNicEntry(NicInfoV3 *nicInfo,
                     const char macAddress[NICINFO_MAC_LEN],
                     DnsConfigInfo *dnsInfo,
                     WinsConfigInfo *winsInfo,
                     Bool *maxNicsError)
{
   GuestNicV3 *newNic;

   /* Check to see if we're going above our limit. See bug 605821. */
   if (nicInfo->nics.nics_len == NICINFO_MAX_NICS) {
      if (maxNicsError != NULL) {
         *maxNicsError = TRUE;
      }
      return NULL;
   }

   newNic = XDRUTIL_ARRAYAPPEND(nicInfo, nics, 1);
   ASSERT_MEM_ALLOC(newNic);

   newNic->macAddress = Util_SafeStrdup(macAddress);
   newNic->dnsConfigInfo = dnsInfo;
   newNic->winsConfigInfo = winsInfo;

   return newNic;
}


/*
 ******************************************************************************
 * GuestInfoAddIpAddress --                                              */ /**
 *
 * @brief Add an IP address entry into the GuestNic.
 *
 * @param[in,out] nic      The NIC information.
 * @param[in]     sockAddr The new IP address.
 * @param[in]     pfxLen   Prefix length (use 0 if unknown).
 * @param[in]     origin   Address's origin.  (Optional.)
 * @param[in]     status   Address's status.  (Optional.)
 *
 * @return Newly allocated IP address struct, NULL on failure.
 *
 ******************************************************************************
 */

IpAddressEntry *
GuestInfoAddIpAddress(GuestNicV3 *nic,
                      const struct sockaddr *sockAddr,
                      InetAddressPrefixLength pfxLen,
                      const IpAddressOrigin *origin,
                      const IpAddressStatus *status)
{
   IpAddressEntry *ip;

   ASSERT(sockAddr);

   /* Check to see if we're going above our limit. See bug 605821. */
   if (nic->ips.ips_len == NICINFO_MAX_IPS) {
      g_message("%s: IP address limit (%d) reached, skipping overflow.",
                __FUNCTION__, NICINFO_MAX_IPS);
      return NULL;
   }

   ip = XDRUTIL_ARRAYAPPEND(nic, ips, 1);
   ASSERT_MEM_ALLOC(ip);

   ASSERT_ON_COMPILE(sizeof *origin == sizeof *ip->ipAddressOrigin);
   ASSERT_ON_COMPILE(sizeof *status == sizeof *ip->ipAddressStatus);

   switch (sockAddr->sa_family) {
   case AF_INET:
      {
         static const IpAddressStatus defaultStatus = IAS_PREFERRED;

         GuestInfoSockaddrToTypedIpAddress(sockAddr, &ip->ipAddressAddr);

         ip->ipAddressPrefixLength = pfxLen;
         ip->ipAddressOrigin = origin ? Util_DupeThis(origin, sizeof *origin) : NULL;
         ip->ipAddressStatus = status ? Util_DupeThis(status, sizeof *status) :
            Util_DupeThis(&defaultStatus, sizeof defaultStatus);
      }
      break;
   case AF_INET6:
      {
         static const IpAddressStatus defaultStatus = IAS_UNKNOWN;

         GuestInfoSockaddrToTypedIpAddress(sockAddr, &ip->ipAddressAddr);

         ip->ipAddressPrefixLength = pfxLen;
         ip->ipAddressOrigin = origin ? Util_DupeThis(origin, sizeof *origin) : NULL;
         ip->ipAddressStatus = status ? Util_DupeThis(status, sizeof *status) :
            Util_DupeThis(&defaultStatus, sizeof defaultStatus);
      }
      break;
   default:
      NOT_REACHED();
   }

   return ip;
}


/*
 ******************************************************************************
 * GuestInfoSockaddrToTypedIpAddress --                                  */ /**
 *
 * @brief Converts a <tt>struct sockaddr</tt> to a @c TypedIpAddress.
 *
 * @param[in]  sa       Source @c sockaddr.
 * @param[out] typedIp  Destination @c TypedIpAddress.
 *
 * @warning Caller is responsible for making sure source is AF_INET or
 * AF_INET6.
 *
 ******************************************************************************
 */

void
GuestInfoSockaddrToTypedIpAddress(const struct sockaddr *sa,
                                  TypedIpAddress *typedIp)
{
   struct sockaddr_in *sin = (struct sockaddr_in *)sa;
   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;

   switch (sa->sa_family) {
   case AF_INET:
      typedIp->ipAddressAddrType = IAT_IPV4;
      XDRUTIL_SAFESETOPAQUE(&typedIp->ipAddressAddr, InetAddress,
                            &sin->sin_addr.s_addr,
                            sizeof sin->sin_addr.s_addr);
      break;
   case AF_INET6:
      typedIp->ipAddressAddrType = IAT_IPV6;
      XDRUTIL_SAFESETOPAQUE(&typedIp->ipAddressAddr, InetAddress,
                            &sin6->sin6_addr.s6_addr,
                            sizeof sin6->sin6_addr.s6_addr);

      /*
       * Some TCP stacks (hello Apple and FreeBSD!) deviate from the RFC and
       * embed the scope id in link-local IPv6 addresses. This breaks things
       * since the address with the scope id does not work on the wire. For
       * example:
       *
       *    fe80:4::20c:29ff:fece:3dcf
       *
       * Is an invalid IPv6 address because the "4" violates the RFC. But that's
       * what SIOCGIFCONF returns on these platforms.
       *
       * Detect link-local addresses here and make sure they comply with the
       * RFC. Just for reference, link local addresses start with '1111111010'
       * and have 54 zero bits after that:
       *
       * http://tools.ietf.org/html/rfc4291#section-2.5.6
       */
      {
         uint64  ip6_ll_test = 0x80FE;
         uint64  ip6_ll_mask = 0xC0FF;
         uint64 *ip6 = (uint64 *) typedIp->ipAddressAddr.InetAddress_val;

         if ((*ip6 & ip6_ll_mask) == ip6_ll_test) {
            *ip6 &= ip6_ll_mask;
         }
      }

      break;
   default:
      NOT_REACHED();
   }
}


#if defined _WIN32
/*
 ******************************************************************************
 * GuestInfoDupTypedIpAddress--                                          */ /**
 *
 * @brief Duplicates a @c TypedIpAddress.
 *
 * @param[in]  srcIp    Source @c TypedIpAddress.
 * @param[out] destIp   Destination @c TypedIpAddress.
 *
 ******************************************************************************
 */

void
GuestInfoDupTypedIpAddress(TypedIpAddress *srcIp,   // IN
                           TypedIpAddress *destIp)  // OUT
{
   *destIp = *srcIp;
   destIp->ipAddressAddr.InetAddress_val =
      Util_DupeThis(srcIp->ipAddressAddr.InetAddress_val,
                    srcIp->ipAddressAddr.InetAddress_len);
}

#endif // if defined _WIN32


#if defined __linux__ || defined _WIN32
/*
 ******************************************************************************
 * GuestInfoGetNicInfoIfIndex --                                         */ /**
 *
 * @brief Given a local interface's index, find its corresponding location in the
 * NicInfoV3 @c nics vector.
 *
 * @param[in]  nicInfo     NIC container.
 * @param[in]  ifIndex     Device to search for.
 * @param[out] nicifIndex  Array offset, if found.
 *
 * @retval TRUE  Device found.
 * @retval FALSE Device not found.
 *
 ******************************************************************************
 */

Bool
GuestInfoGetNicInfoIfIndex(NicInfoV3 *nicInfo,
                           int ifIndex,
                           int *nicIfIndex)
{
   char hwAddrString[NICINFO_MAC_LEN];
   unsigned char hwAddr[16];
   IanaIfType ifType;
   Bool ret = FALSE;
   u_int i;

   ASSERT(nicInfo);
   ASSERT(nicIfIndex);

   if (NetUtil_GetHardwareAddress(ifIndex, hwAddr, sizeof hwAddr,
                                  &ifType) != 6 ||
       ifType != IANA_IFTYPE_ETHERNETCSMACD) {
      return FALSE;
   }

   Str_Sprintf(hwAddrString, sizeof hwAddrString,
               "%02x:%02x:%02x:%02x:%02x:%02x",
               hwAddr[0], hwAddr[1], hwAddr[2],
               hwAddr[3], hwAddr[4], hwAddr[5]);

   XDRUTIL_FOREACH(i, nicInfo, nics) {
      GuestNicV3 *nic = XDRUTIL_GETITEM(nicInfo, nics, i);
      if (!strcasecmp(nic->macAddress, hwAddrString)) {
         *nicIfIndex = i;
         ret = TRUE;
         break;
      }
   }

   return ret;
}
#endif // if defined __linux__ || defined _WIN32


/*
 * XXX
 */


/**
 * Return a copy of arbitrary memory.
 *
 * @param[in] source     Source address.
 * @param[in] sourceSize Number of bytes to allocate, copy.
 *
 * @return Pointer to newly allocated memory.
 *
 * @todo Determine if I'm duplicating functionality.
 * @todo Move this to bora/lib/util or whatever.
 */

static void *
Util_DupeThis(const void *source,
              size_t sourceSize)
{
   void *dest;

   ASSERT(source);

   dest = Util_SafeMalloc(sourceSize);
   memcpy(dest, source, sourceSize);

   return dest;
}
 070701000001AE000081A40000000000000000000000016822550500000BCF000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/nicInfo/nicInfoInt.h   /*********************************************************
 * Copyright (c) 2014-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * nicInfoInt.h --
 *
 *    Functions used to communicate guest network information to the host.
 *
 */

#ifndef NICINFO_INT_H
#define NICINFO_INT_H

#include "nicInfo.h"

#if defined __FreeBSD__ || defined __sun__ || defined __APPLE__
#   include <sys/socket.h>      // struct sockaddr
#endif

Bool GuestInfoGetFqdn(int outBufLen, char fqdn[]);
Bool GuestInfoGetNicInfo(unsigned int maxIPv4Routes,   // IN
                         unsigned int maxIPv6Routes,   // IN
                         NicInfoV3 *nicInfo,           // OUT
                         Bool *maxNicsError);          // OUT

GuestNicV3 *GuestInfoAddNicEntry(NicInfoV3 *nicInfo,                    // IN/OUT
                                 const char macAddress[NICINFO_MAC_LEN], // IN
                                 DnsConfigInfo *dnsInfo,                // IN
                                 WinsConfigInfo *winsInfo,              // IN
                                 Bool *maxNicsError);                   // OUT

IpAddressEntry *GuestInfoAddIpAddress(GuestNicV3 *nic,                  // IN/OUT
                                      const struct sockaddr *sockAddr,  // IN
                                      InetAddressPrefixLength pfxLen,   // IN
                                      const IpAddressOrigin *origin,    // IN
                                      const IpAddressStatus *status);   // IN

char *GuestInfoGetPrimaryIP(void);

#if defined _WIN32
void GuestInfoDupTypedIpAddress(TypedIpAddress *srcIp,   // IN
                                TypedIpAddress *destIp);  // OUT
#endif // if defined _WIN32

#if defined __linux__ || defined _WIN32
Bool GuestInfoGetNicInfoIfIndex(NicInfoV3 *nicInfo,  // IN
                                int ifIndex,         // IN
                                int *nicIfIndex);    // OUT
#endif // if defined __linux__ || defined _WIN32
void GuestInfoSockaddrToTypedIpAddress(const struct sockaddr *sa,    // IN
                                       TypedIpAddress *typedIp);     // OUT

GuestNicV3 *
GuestInfoUtilFindNicByMac(const NicInfoV3 *nicInfo,
                          const char *macAddress);
#endif
 070701000001AF000081A4000000000000000000000001682255050000D9D7000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/nicInfo/nicInfoPosix.c /*********************************************************
 * Copyright (c) 2014-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file nicInfoPosix.c
 *
 * Contains POSIX-specific bits of GuestInfo collector library.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifdef sun
# include <sys/systeminfo.h>
#endif
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#if defined(__FreeBSD__) || defined(__APPLE__)
# include <sys/sysctl.h>
# include <ifaddrs.h>
# include <net/if.h>
#endif
#ifndef NO_DNET
# ifdef DNET_IS_DUMBNET
#  include <dumbnet.h>
# else
#  include <dnet.h>
# endif
#define USE_RESOLVE 1
#endif

#if defined(USERWORLD) || (defined(__linux__) && defined(NO_DNET))
#include "vm_basic_defs.h"
#include <net/if.h>
#include <netpacket/packet.h>
#include <ifaddrs.h>

#define USE_RESOLVE 1
#endif

#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>

#ifdef __linux__
#   include <net/if.h>
#endif

/*
 * resolver(3) and IPv6:
 *
 * The ISC BIND resolver included various IPv6 implementations over time, but
 * unfortunately the ISC hadn't bumped __RES accordingly.  (__RES is -supposed-
 * to behave as a version datestamp for the resolver interface.)  Similarly
 * the GNU C Library forked resolv.h and made modifications of their own, also
 * without changing __RES.
 *
 * ISC, OTOH, provided accessing IPv6 servers via a res_getservers API.
 * TTBOMK, this went public with BIND 8.3.0.  Unfortunately __RES wasn't
 * bumped for this release, so instead I'm going to assume that appearance with
 * that release of a new macro, RES_F_EDNS0ERR, implies this API is available.
 * (For internal builds, we'll know instantly when a build breaks.  The down-
 * side is that this could cause some trouble for open-vm-tools users. ,_,)
 *
 * resolv.h version     IPv6 API        __RES
 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 * glibc 2.2+           _ext            19991006
 * BIND 8.3.0           getservers      19991006
 * BIND 8.3.4+          getservers      20030124(+?)
 *
 * To distinguish between the variants where __RES == 19991006, I'll
 * discriminate on the existence of new macros included with the appropriate
 * version.
 */

#if defined __linux__
#   define      RESOLVER_IPV6_EXT
#elif (__RES > 19991006 || (__RES == 19991006 && defined RES_F_EDNS0ERR))
#   define      RESOLVER_IPV6_GETSERVERS
#endif // if defined __linux__


#include "util.h"
#include "sys/utsname.h"
#include "sys/ioctl.h"
#include "vmware.h"
#include "hostinfo.h"
#include "nicInfoInt.h"
#include "debug.h"
#include "str.h"
#include "guest_os.h"
#include "guestApp.h"
#include "guestInfo.h"
#include "xdrutil.h"
#ifdef USE_SLASH_PROC
#   include "slashProc.h"
#endif
#include "netutil.h"
#include "file.h"

#ifndef IN6_IS_ADDR_UNIQUELOCAL
#define IN6_IS_ADDR_UNIQUELOCAL(a)        \
        (((a)->s6_addr[0] == 0xfc) && (((a)->s6_addr[1] & 0xc0) == 0x00))
#endif


/*
 * Local functions
 */


#ifndef NO_DNET
static Bool RecordNetworkAddress(GuestNicV3 *nic, const struct addr *addr);
static int ReadInterfaceDetails(const struct intf_entry *entry,
                                void *arg,
                                NicInfoPriority priority);
static int ReadInterfaceDetailsPrimary(const struct intf_entry *entry,
                                       void *arg);
static int ReadInterfaceDetailsNormal(const struct intf_entry *entry,
                                      void *arg);
static int ReadInterfaceDetailsLowPriority(const struct intf_entry *entry,
                                           void *arg);
static Bool RecordRoutingInfo(unsigned int maxIPv4Routes,
                              unsigned int maxIPv6Routes,
                              NicInfoV3 *nicInfo);

#if !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(USERWORLD)
typedef struct GuestInfoIpPriority {
   char *ipstr;
   NicInfoPriority priority;
} GuestInfoIpPriority;

static int GuestInfoGetIntf(const struct intf_entry *entry, void *arg);
#endif

#endif

static Bool RecordRoutingInfo(unsigned int maxIPv4Routes,
                              unsigned int maxIPv6Routes,
                              NicInfoV3 *nicInfo);

static char *ValidateConvertAddress(const struct sockaddr *addr);


#ifdef USE_RESOLVE
static Bool RecordResolverInfo(NicInfoV3 *nicInfo);
static void RecordResolverNS(res_state resp, DnsConfigInfo *dnsConfigInfo);
static int AddResolverNSInfo(DnsConfigInfo *dnsConfigInfo, struct sockaddr *sap);
#   ifndef RESOLVER_IPV6_GETSERVERS
static void PrintResolverNSInfo(res_state resp);
#   endif
#endif


/*
 ******************************************************************************
 * GuestInfoGetFqdn --                                                   */ /**
 *
 * @copydoc GuestInfo_GetFqdn
 *
 ******************************************************************************
 */

Bool
GuestInfoGetFqdn(int outBufLen,    // IN: length of output buffer
                 char fqdn[])      // OUT: fully qualified domain name
{
   ASSERT(fqdn);
   if (gethostname(fqdn, outBufLen) < 0) {
      g_debug("Error, gethostname failed\n");
      return FALSE;
   }

   return TRUE;
}


#if defined(USERWORLD) || defined(USE_SLASH_PROC) || (defined(__linux__) && defined(NO_DNET))
/*
 ******************************************************************************
 * CountNetmaskBits --                                                   */ /**
 * CountNetmaskBitsV4 --                                                 */ /**
 * CountNetmaskBitsV6 --                                                 */ /**
 *
 * @brief Count the number of bits set in a IPV4 or IPV6 netmask
 *
 * @retval the number of bits set
 *
 ******************************************************************************
 */

static unsigned
CountNetmaskBits(uint64_t x)
{
   /* SWAR reduction, much faster than using the loop/shift */
   const uint64_t m1  = 0x5555555555555555ull; /* binary: 0101... */
   const uint64_t m2  = 0x3333333333333333ull; /* binary: 00110011 */
   const uint64_t m4  = 0x0f0f0f0f0f0f0f0full; /* binary:  4 zeros,  4 ones */

   x -= (x >> 1) & m1;             /* each 2 bits into those 2 bits */
   x = (x & m2) + ((x >> 2) & m2); /* each 4 bits into those 4 bits */
   x = (x + (x >> 4)) & m4;        /* and so on ... */
   x += x >>  8;
   x += x >> 16;
   x += x >> 32;
   return x & 0x7f;
}

static unsigned
CountNetmaskBitsV4(struct sockaddr *netmask)
{
   uint64_t value = ((struct sockaddr_in *)netmask)->sin_addr.s_addr;
   return CountNetmaskBits(value);
}
#endif

#if defined(USERWORLD) || (defined(__linux__) && defined(NO_DNET))
static unsigned
CountNetmaskBitsV6(struct sockaddr *netmask)
{
   uint64_t *value = (uint64_t *)&((struct sockaddr_in6 *)netmask)->sin6_addr;

   return CountNetmaskBits(value[0]) + CountNetmaskBits(value[1]);
}


/*
 ******************************************************************************
 * IpEntryMatchesDevice --                                               */ /**
 *
 * @brief Check if the IP entry matches the network device.
 *
 * @param[in]   devName the device name
 * @param[in]   label   the IP entry name
 *
 * @retval      TRUE if the IP entry name matches the device name
 *              FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
IpEntryMatchesDevice(const char *devName,
                     const char *label)
{
   char *p;
   size_t n;

   if ((p = strchr(label, ':')) != NULL) {
      n = p - label;
   } else {
      n = strlen(label);
   }

   /* compare sub string label[0, n) with a null terminated string devName */
   return (0 == strncmp(devName, label, n) && '\0' == devName[n]);
}


/*
 ******************************************************************************
 * GuestInfoGetInterface --                                              */ /**
 *
 * @brief Gather IP addresses from ifaddrs and put into NicInfo, filtered
 *        by priority.
 *
 * @param[in]   ifaddrs       ifaddrs structure
 * @param[in]   priority      the priority - only interfaces with this priority
 *                            will be considered
 * @param[out]  nicInfo       NicInfoV3 structure
 * @param[out]  maxNicsError  To determine NIC max limit error.
 *
 ******************************************************************************
 */

static void
GuestInfoGetInterface(struct ifaddrs *ifaddrs,
                      NicInfoPriority priority,
                      NicInfoV3 *nicInfo,
                      Bool *maxNicsError)
{
   struct ifaddrs *pkt;
   /*
    * ESXi reports an AF_PACKET record for each physical interface.
    * The MAC address is the first six bytes of sll_addr.  AF_PACKET
    * records are intermingled with AF_INET and AF_INET6 records.
    */
   for (pkt = ifaddrs; pkt != NULL; pkt = pkt->ifa_next) {
      struct sockaddr_ll *sll = (struct sockaddr_ll *)pkt->ifa_addr;

      if (GuestInfo_IfaceGetPriority(pkt->ifa_name) != priority ||
          GuestInfo_IfaceIsExcluded(pkt->ifa_name)) {
         continue;
      }

      if (sll != NULL && sll->sll_family == AF_PACKET) {
         char macAddress[NICINFO_MAC_LEN];
         GuestNicV3 *nic;
         struct ifaddrs *ip;

         /*
          * PR 2193804:
          * On ESXi, AF_PACKET family is reported for vmk* interfaces only
          * and its ifa_flags is reported as 0. No AF_PACKET family ifaddrs
          * is reported for loopback interface.
          */
#if !defined(USERWORLD)
         /*
          * Ignore loopback and downed devices.
          */
         if (!(pkt->ifa_flags & IFF_UP) || pkt->ifa_flags & IFF_LOOPBACK) {
            continue;
         }
#endif

         Str_Sprintf(macAddress, sizeof macAddress,
                     "%02x:%02x:%02x:%02x:%02x:%02x",
                     sll->sll_addr[0], sll->sll_addr[1], sll->sll_addr[2],
                     sll->sll_addr[3], sll->sll_addr[4], sll->sll_addr[5]);
         nic = GuestInfoAddNicEntry(nicInfo, macAddress, NULL, NULL,
                                    maxNicsError);
         if (nic == NULL) {
            /*
             * We reached the maximum number of NICs that we can report.
             */
            break;
         }
         /*
          * Now look for all IPv4 and IPv6 interfaces that match
          * the current AF_PACKET interface.
          */
         for (ip = ifaddrs; ip != NULL; ip = ip->ifa_next) {
            struct sockaddr *sa = (struct sockaddr *)ip->ifa_addr;
            if (sa != NULL &&
                IpEntryMatchesDevice(pkt->ifa_name, ip->ifa_name)) {
               int family = sa->sa_family;
               Bool goodAddress = FALSE;
               unsigned nBits = 0;
               /*
                * Ignore any loopback addresses.
                * A loopback address would indicate a misconfiguration, since
                * this is not a loopback device (we checked for that above).
                */
               if (family == AF_INET) {
                  struct sockaddr_in *sin = (struct sockaddr_in *)sa;
                  if ((ntohl(sin->sin_addr.s_addr) >> IN_CLASSA_NSHIFT) !=
                      IN_LOOPBACKNET) {
                     nBits = CountNetmaskBitsV4(ip->ifa_netmask);
                     goodAddress = TRUE;
                  }
               } else if (family == AF_INET6) {
                  struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
                  if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) {
                     nBits = CountNetmaskBitsV6(ip->ifa_netmask);
                     goodAddress = TRUE;
                  }
               }
               if (goodAddress) {
                  IpAddressEntry *ent = GuestInfoAddIpAddress(nic,
                                                              ip->ifa_addr,
                                                              nBits, NULL,
                                                              NULL);
                  if (NULL == ent) {
                     /*
                      * Reached the max number of IPs that can be reported
                      */
                     break;
                  }
               }
            }
         }
      }
   }
}
#endif


/*
 ******************************************************************************
 * GuestInfoGetNicInfo --                                                */ /**
 *
 * @param[in]  maxIPv4Routes  Max IPv4 routes to gather.
 * @param[in]  maxIPv6Routes  Max IPv6 routes to gather.
 * @param[out] nicInfo        NicInfoV3 container.
 * @param[out] maxNicsError   To determine NIC max limit error.
 *
 * @copydoc GuestInfo_GetNicInfo
 *
 ******************************************************************************
 */

Bool
GuestInfoGetNicInfo(unsigned int maxIPv4Routes,
                    unsigned int maxIPv6Routes,
                    NicInfoV3 *nicInfo,
                    Bool *maxNicsError)
{
#ifndef NO_DNET
   intf_t *intf;

   /* Get a handle to read the network interface configuration details. */
   if ((intf = intf_open()) == NULL) {
      g_warning("%s: intf_open() failed\n", __FUNCTION__);
      return FALSE;
   }

   /*
    * Iterate through the list of interfaces thrice - first for interfaces
    * considered to be primary; second for others, non-specified as primary
    * or low priority; and low-priority last. This ensures interfaces are
    * handled in the specified order.
    */
   if (intf_loop(intf, ReadInterfaceDetailsPrimary, nicInfo) < 0 ||
       intf_loop(intf, ReadInterfaceDetailsNormal, nicInfo) < 0 ||
       intf_loop(intf, ReadInterfaceDetailsLowPriority, nicInfo) < 0) {
      intf_close(intf);
      g_debug("%s: Error, negative result from intf_loop\n", __FUNCTION__);
      return FALSE;
   }

   intf_close(intf);

#ifdef USE_RESOLVE
   if (!RecordResolverInfo(nicInfo)) {
      return FALSE;
   }
#endif

   if ((maxIPv4Routes > 0 || maxIPv6Routes > 0) &&
       !RecordRoutingInfo(maxIPv4Routes, maxIPv6Routes, nicInfo)) {
      return FALSE;
   }

   return TRUE;
#elif defined(USERWORLD) || defined(__linux__)
   struct ifaddrs *ifaddrs = NULL;

   if (getifaddrs(&ifaddrs) == 0 && ifaddrs != NULL) {
      NicInfoPriority priority;

      /*
       * Handle primary interfaces first, then non-primary ones.
       */
      for (priority = NICINFO_PRIORITY_PRIMARY;
           priority < NICINFO_PRIORITY_MAX;
           priority++) {
         GuestInfoGetInterface(ifaddrs, priority, nicInfo, maxNicsError);
      }
      freeifaddrs(ifaddrs);
   }

#ifdef USE_RESOLVE
   if (!RecordResolverInfo(nicInfo)) {
      return FALSE;
   }
#endif

   if ((maxIPv4Routes > 0 || maxIPv6Routes > 0) &&
       !RecordRoutingInfo(maxIPv4Routes, maxIPv6Routes, nicInfo)) {
      return FALSE;
   }

   return TRUE;
#else
   (void)maxIPv4Routes;
   (void)maxIPv6Routes;
   (void)nicInfo;

   return FALSE;
#endif
}


/*
 ******************************************************************************
 * GuestInfoGetPrimaryIP --                                              */ /**
 *
 * @copydoc GuestInfo_GetPrimaryIP
 *
 ******************************************************************************
 */
#if defined(__FreeBSD__) || \
    defined(__APPLE__) || \
    defined(USERWORLD) || \
    (defined(__linux__) && defined(NO_DNET))

char *
GuestInfoGetPrimaryIP(void)
{
   struct ifaddrs *ifaces;
   struct ifaddrs *curr;
   char *currIpstr = NULL;
   NicInfoPriority currPri = NICINFO_PRIORITY_MAX;

   /*
    * getifaddrs(3) creates a NULL terminated linked list of interfaces for us
    * to traverse and places a pointer to it in ifaces.
    */
   if (getifaddrs(&ifaces) < 0) {
      return NULL;
   }

   /*
    * We traverse the list until there are no more interfaces or we have found
    * the primary interface. This function defines the primary interface to be
    * the first non-loopback, internet interface in the interface list.
    */
   for (curr = ifaces; curr != NULL; curr = curr->ifa_next) {
      char *ipstr = NULL;
      int currFamily;

      /*
       * Some interfaces ("tun") have no ifa_addr, so ignore them.
       */
      if (NULL == curr->ifa_addr) {
         continue;
      }
      currFamily = ((struct sockaddr_storage *)curr->ifa_addr)->ss_family;

      if (!(curr->ifa_flags & IFF_UP) || curr->ifa_flags & IFF_LOOPBACK) {
         continue;
      } else if (GuestInfo_IfaceIsExcluded(curr->ifa_name)) {
         continue;
      } else if (currFamily == AF_INET || currFamily == AF_INET6) {
         ipstr = ValidateConvertAddress(curr->ifa_addr);
      } else {
         continue;
      }

      if (ipstr != NULL) {
         NicInfoPriority pri = GuestInfo_IfaceGetPriority(curr->ifa_name);
         if (pri < currPri) {
            g_debug("%s: ifa_name=%s, pri=%d, currPri=%d, ipstr=%s",
                    __FUNCTION__, curr->ifa_name, pri, currPri, ipstr);
            free(currIpstr);
            currIpstr = ipstr;
            currPri = pri;
            if (pri == NICINFO_PRIORITY_PRIMARY) {
               /* not going to find anything better than that */
               break;
            }
         } else {
            free(ipstr);
         }
      }
   }

   freeifaddrs(ifaces);

   return currIpstr;
}

#else

#ifndef NO_DNET

char *
GuestInfoGetPrimaryIP(void)
{
   GuestInfoIpPriority ipp;
   intf_t *intf = intf_open();

   if (NULL == intf) {
      g_warning("%s: intf_open() failed\n", __FUNCTION__);
      return NULL;
   }

   ipp.ipstr = NULL;
   for (ipp.priority = NICINFO_PRIORITY_PRIMARY;
       ipp.priority < NICINFO_PRIORITY_MAX;
       ipp.priority++){
      intf_loop(intf, GuestInfoGetIntf, &ipp);
      if (ipp.ipstr != NULL) {
         break;
      }
   }
   intf_close(intf);

   g_debug("%s: returning '%s'",
           __FUNCTION__, ipp.ipstr ? ipp.ipstr : "<null>");

   return ipp.ipstr;
}
#else
#   error GuestInfoGetPrimaryIP needed for this platform
#endif
#endif


/*
 * Local functions
 */


#ifndef NO_DNET
/*
 ******************************************************************************
 * RecordNetworkAddress --                                               */ /**
 *
 * @brief Massages a dnet(3)-style interface address (IPv4 or IPv6) and stores
 *        it as part of a GuestNicV3 structure.
 *
 * @param[in]  nic      Operand NIC.
 * @param[in]  addr     dnet(3) address.
 *
 * @retval TRUE on success
 *
 ******************************************************************************
 */

static Bool
RecordNetworkAddress(GuestNicV3 *nic,           // IN: operand NIC
                     const struct addr *addr)   // IN: dnet(3) address to process
{
   struct sockaddr_storage ss;
   struct sockaddr *sa = (struct sockaddr *)&ss;
   const IpAddressEntry *ip;

   memset(&ss, 0, sizeof ss);
   addr_ntos(addr, sa);
   ip = GuestInfoAddIpAddress(nic, sa, addr->addr_bits, NULL, NULL);
   if (NULL == ip) {
      return FALSE;
   }
   return TRUE;
}


/*
 ******************************************************************************
 * ReadInterfaceDetails --                                               */ /**
 *
 * @brief Callback function called by libdnet when iterating over all the NICs
 * on the host. Cannot be used as a callback directly, see wrappers below.
 *
 * @param[in]  entry      Current interface entry.
 * @param[in]  arg        Pointer to NicInfoV3 container.
 * @param[in]  priority   Which priority interfaces to consider
 *
 * @note New GuestNicV3 structures are added to the NicInfoV3 structure.
 *
 * @retval 0    Success.
 * @retval -1   Failure.
 *
 ******************************************************************************
 */

static int
ReadInterfaceDetails(const struct intf_entry *entry, // IN
                     void *arg,                      // IN
                     NicInfoPriority priority)       // IN
{
   NicInfoV3 *nicInfo = arg;

   ASSERT(entry);
   ASSERT(arg);

   if (entry->intf_type == INTF_TYPE_ETH &&
       entry->intf_link_addr.addr_type == ADDR_TYPE_ETH) {

      /*
       * There is a race where the guest info plugin might be iterating over the
       * interfaces while the OS is modifying them (i.e. by bringing them up
       * after a resume). If we see an ethernet interface with an invalid MAC,
       * then ignore it for now. Subsequent iterations of the gather loop will
       * pick up any changes.
       */
      if (entry->intf_link_addr.addr_type == ADDR_TYPE_ETH) {
         char macAddress[NICINFO_MAC_LEN];
         GuestNicV3 *nic = NULL;
         int i;

         Str_Sprintf(macAddress, sizeof macAddress, "%s",
                     addr_ntoa(&entry->intf_link_addr));

         if (GuestInfo_IfaceIsExcluded(entry->intf_name) ||
             GuestInfo_IfaceGetPriority(entry->intf_name) != priority) {
            return 0;
         }

         nic = GuestInfoAddNicEntry(nicInfo, macAddress, NULL, NULL, NULL);
         if (NULL == nic) {
            /*
             * We reached maximum number of NICs we can report to the host.
             */
            return 0;
         }

         /* Record the "primary" address. */
         if (entry->intf_addr.addr_type == ADDR_TYPE_IP ||
             entry->intf_addr.addr_type == ADDR_TYPE_IP6) {
            if (!RecordNetworkAddress(nic, &entry->intf_addr)) {
               /*
                * We reached maximum number of IPs we can report to the host.
                */
               return 0;
            }
         }

         /* Walk the list of alias's and add those that are IPV4 or IPV6 */
         for (i = 0; i < entry->intf_alias_num; i++) {
            const struct addr *alias = &entry->intf_alias_addrs[i];
            if (alias->addr_type == ADDR_TYPE_IP ||
                alias->addr_type == ADDR_TYPE_IP6) {
               if (!RecordNetworkAddress(nic, alias)) {
                  /*
                   * We reached maximum number of IPs we can report to the host.
                   */
                  return 0;
               }
            }
         }
      }
   }

   return 0;
}


/*
 ******************************************************************************
 * ReadInterfaceDetailsPrimary --                                        */ /**
 *
 * @brief Callback function called by libdnet when iterating over all the NICs
 * on the host. Calls ReadInterfaceDetails with the priority param set to
 * NICINFO_PRIORITY_PRIMARY.
 *
 * @param[in]  entry     Current interface entry.
 * @param[in]  arg       Pointer to NicInfoV3 container.
 *
 * @note New GuestNicV3 structures are added to the NicInfoV3 structure.
 *
 * @retval 0    Success.
 * @retval -1   Failure.
 *
 ******************************************************************************
 */

static int
ReadInterfaceDetailsPrimary(const struct intf_entry *entry,
                            void *arg)
{
   return ReadInterfaceDetails(entry, arg, NICINFO_PRIORITY_PRIMARY);
}


/*
 ******************************************************************************
 * ReadInterfaceDetailsNormal --                                      */ /**
 *
 * @brief Callback function called by libdnet when iterating over all the NICs
 * on the host. Calls ReadInterfaceDetails with the priority param set to
 * NICINFO_PRIORITY_NORMAL.
 *
 * @param[in]  entry     Current interface entry.
 * @param[in]  arg       Pointer to NicInfoV3 container.
 *
 * @note New GuestNicV3 structures are added to the NicInfoV3 structure.
 *
 * @retval 0    Success.
 * @retval -1   Failure.
 *
 ******************************************************************************
 */

static int
ReadInterfaceDetailsNormal(const struct intf_entry *entry,
                           void *arg)
{
   return ReadInterfaceDetails(entry, arg, NICINFO_PRIORITY_NORMAL);
}

/*
 ******************************************************************************
 * ReadInterfaceDetailsLowPriority --                                    */ /**
 *
 * @brief Callback function called by libdnet when iterating over all the NICs
 * on the host. Calls ReadInterfaceDetails with the priority param set to
 * NICINFO_PRIORITY_LOW.
 *
 * @param[in]  entry     Current interface entry.
 * @param[in]  arg       Pointer to NicInfoV3 container.
 *
 * @note New GuestNicV3 structures are added to the NicInfoV3 structure.
 *
 * @retval 0    Success.
 * @retval -1   Failure.
 *
 ******************************************************************************
 */


static int
ReadInterfaceDetailsLowPriority(const struct intf_entry *entry,
                                void *arg)
{
   return ReadInterfaceDetails(entry, arg, NICINFO_PRIORITY_LOW);
}


#endif // !NO_DNET


#ifdef USE_RESOLVE

/*
 ******************************************************************************
 * RecordResolverInfo --                                                 */ /**
 *
 * @brief Query resolver(3), mapping settings to DnsConfigInfo.
 *
 * @param[out] nicInfo  NicInfoV3 container.
 *
 * @retval TRUE         Values collected, attached to @a nicInfo.
 * @retval FALSE        Something went wrong.  @a nicInfo is unharmed.
 *
 ******************************************************************************
 */

static Bool
RecordResolverInfo(NicInfoV3 *nicInfo)  // OUT
{
   DnsConfigInfo *dnsConfigInfo = NULL;
   char namebuf[DNSINFO_MAX_ADDRLEN + 1];
   char **s;
   struct __res_state res;

   memset(&res, 0, sizeof res);
   if (res_ninit(&res) == -1) {
      g_warning("%s: Resolver res_init failed.\n", __FUNCTION__);
      return FALSE;
   }

   dnsConfigInfo = Util_SafeCalloc(1, sizeof *dnsConfigInfo);

   /*
    * Copy in the host name.
    */
   if (!GuestInfoGetFqdn(sizeof namebuf, namebuf)) {
      goto fail;
   }
   dnsConfigInfo->hostName =
      Util_SafeCalloc(1, sizeof *dnsConfigInfo->hostName);
   *dnsConfigInfo->hostName = Util_SafeStrdup(namebuf);

   /*
    * Repeat with the domain name.
    */
   dnsConfigInfo->domainName =
      Util_SafeCalloc(1, sizeof *dnsConfigInfo->domainName);
   *dnsConfigInfo->domainName = Util_SafeStrdup(res.defdname);

   /*
    * Name servers.
    */
   RecordResolverNS(&res, dnsConfigInfo);

   /*
    * Search suffixes.
    */
   for (s = res.dnsrch; *s; s++) {
      DnsHostname *suffix;

      /* Check to see if we're going above our limit. See bug 605821. */
      if (dnsConfigInfo->searchSuffixes.searchSuffixes_len == DNSINFO_MAX_SUFFIXES) {
         g_message("%s: dns search suffix limit (%d) reached, skipping overflow.",
                   __FUNCTION__, DNSINFO_MAX_SUFFIXES);
         break;
      }

      suffix = XDRUTIL_ARRAYAPPEND(dnsConfigInfo, searchSuffixes, 1);
      ASSERT_MEM_ALLOC(suffix);
      *suffix = Util_SafeStrdup(*s);
   }

   /*
    * "Commit" dnsConfigInfo to nicInfo.
    */
   nicInfo->dnsConfigInfo = dnsConfigInfo;

   res_nclose(&res);

   return TRUE;

fail:
   VMX_XDR_FREE(xdr_DnsConfigInfo, dnsConfigInfo);
   free(dnsConfigInfo);
   res_nclose(&res);

   return FALSE;
}

/*
 ******************************************************************************
 * RecordResolverNSResolvConf                                            */ /**
 *
 * @brief Parses file in resolv.conf format, and copies name servers to
 * @a dnsConfigInfo.
 *
 * @param[in]  file             path of file to read
 * @param[out] dnsConfigInfo    Destination DnsConfigInfo container
 *
 ******************************************************************************
 */

static void
RecordResolverNSResolvConf(const char *file, DnsConfigInfo *dnsConfigInfo)
{
   FILE *fptr;

   fptr = fopen(file, "rt");
   if (fptr != NULL) {
      char line[256];
      while (fgets(line, sizeof(line), fptr)) {
         char *tok, *saveptr = NULL;
         tok = strtok_r(line, " \t", &saveptr);
         if ((tok != NULL) && (strcmp(tok, "nameserver") == 0)) {
            char *val;
            struct sockaddr_in sa4;
            struct sockaddr_in6 sa6;
            val = strtok_r(NULL, " \t\r\n", &saveptr);
            if (val == NULL) {
               g_warning("%s: no value for nameserver in %s\n",
                         __FUNCTION__, file);
            } else if (inet_pton(AF_INET, val, &sa4.sin_addr)) {
               sa4.sin_family = AF_INET;
               if (0 == AddResolverNSInfo(dnsConfigInfo,
                                          (struct sockaddr *) &sa4)) {
                  /* Can't add more DNS entries to dnsConfigInfo, break loop */
                  break;
               }
            } else if (inet_pton(AF_INET6, val, &sa6.sin6_addr)) {
               sa6.sin6_family = AF_INET6;
               if (0 == AddResolverNSInfo(dnsConfigInfo,
                                          (struct sockaddr *) &sa6)) {
                  /* Can't add more DNS entries to dnsConfigInfo, break loop */
                  break;
               }
            } else {
               g_warning("%s: invalid IP address '%s' in %s ignored\n",
                         __FUNCTION__, val, file);
            }
         }
      }
      fclose(fptr);
   } else {
      g_warning("%s: could not open file '%s': %s\n", __FUNCTION__, file, strerror(errno));
   }
}

/*
 ******************************************************************************
 * RecordResolverNS --                                                   */ /**
 *
 * @brief Copies name servers used by resolver(3) to @a dnsConfigInfo.
 *
 * @param[in]  resp             res_state, pointer to a resolver(3) state
 *                              structure.
 * @param[out] dnsConfigInfo    Destination DnsConfigInfo container.
 *
 ******************************************************************************
 */

static void
RecordResolverNS(res_state resp, DnsConfigInfo *dnsConfigInfo) // IN
{
   int i;
   char resolvConf[PATH_MAX];

   /*
    * If systemd-resolved is used we want to report the external DNS
    * server, not the locally installed one. We detect this by checking
    * if /etc/resolv.conf is a link to /run/systemd/resolve/stub-resolv.conf.
    * In that case, /run/systemd/resolve/resolv.conf will hold the actual
    * DNS server. See
    * https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html
    */
   if (realpath("/etc/resolv.conf", resolvConf) != NULL) {
      if (strcmp(resolvConf, "/run/systemd/resolve/stub-resolv.conf") == 0) {
         const char *file = "/run/systemd/resolve/resolv.conf";
         if (access(file, R_OK) != -1) {
            RecordResolverNSResolvConf(file, dnsConfigInfo);
            return;
         } else {
            g_debug("%s: could not access %s for reading: %s\n",
                    __FUNCTION__, file, strerror(errno));
         }
      }
   }

#if defined RESOLVER_IPV6_GETSERVERS
   {
      union res_sockaddr_union *ns;
      ns = Util_SafeCalloc(resp->nscount, sizeof *ns);
      if (res_getservers(resp, ns, resp->nscount) != resp->nscount) {
         g_warning("%s: res_getservers failed.\n", __FUNCTION__);
         /* fallthrough to free & return */
      } else {
         for (i = 0; i < resp->nscount; i++) {
            if (0 == AddResolverNSInfo(
                        dnsConfigInfo,
                        (struct sockaddr *)&ns[i])) {
               /* Can't add more DNS entries to dnsConfigInfo, break loop */
               break;
            }
            /* Else: if < 0, reason already logged in AddResolverNSInfo */
         }
      }

      /* free and return */
      free(ns);
   }
#else                                   // if defined RESOLVER_IPV6_GETSERVERS
   {
      /*
       * GLIBC resolv implementation details to consider...
       *
       * In mixed mode, where both IPv4 and IPv6 nameserver are configured,
       * both arrays (__res_state.nsaddr_list, __res_state._u._ext.nsaddrs) are
       * combined to for the list of nameservers.
       *
       * The __res_state.nscount value is limited to MAXNS and represent the
       * current number of 'managed' or 'valid' nameserver entries across
       * both list.
       *
       * However, not everyone manages the arrays the same way (of course...).
       *
       * Assume resolv.conf:
       *    nameserver A (IPv4)
       *    nameserver B (IPv6)
       *    nameserver C (IPv4)
       *
       * Case 1: front-loaded
       *         IPv4 entries are front-loaded in __res_state.nsaddr_list[] and
       *         IPv6 entries are positional in __res_state._u._ext.nsaddrs[]
       *         position matches /etc/resolv.conf order
       *           __res_state.nsaddr_list | __res_state._u._ext.nsaddrs
       *                  -----------------+--------------------
       *                          A        |       -
       *                          C        |       B
       *                          -        |       -
       *
       * Case 2: staggered / positional
       *         IPv4 entries are staggered with IPv6 entries, thus positional
       *         in both arrays relative to each other.
       *           __res_state.nsaddr_list | __res_state._u._ext.nsaddrs
       *                  -----------------+--------------------
       *                          A        |       -
       *                          -        |       B
       *                          C        |       -
       *
       * Both cases are enhanced by different IPv4 data management features:
       *    Variation 1: IPv4 sin_family is not cleared.
       *    Variation 2: IPv4 sin_family is cleared (set to 0)
       *
       * Distro+release | nscount6 |  Case      | sin_family  |
       * =======================================================================
       *  RHEL 6.10     |   yes    | front-load | not cleared |
       *  RHEL 7.2      |   yes    | front-load | not cleared |
       *  RHEL 7.6      |   no     | staggered  |  post-fix   |
       * -----------------------------------------------------------------------
       *  SLES 11-sp3   |   yes    | staggered  | not cleared | **bad
       *  SLES 12       |   no     | staggered  |   cleared   |
       * -----------------------------------------------------------------------
       *  FreeBSD 11    |  -na-    |    -na-    |     -na-    | res_getservers
       * -----------------------------------------------------------------------
       *  Ubuntu 12.04  |   yes    | staggered  | not cleared | **bad
       *  Ubuntu 14.04  |   yes    | staggered  | not cleared | **bad
       *  Ubuntu 18.04  |   no     | staggered  |   cleared   |
       * -----------------------------------------------------------------------
       *  Fedora 24     |   no     | staggered  |   cleared   |
       * =======================================================================
       *
       * Restarting the process (service: vmtoolsd) resets the IPv4 array and
       * fixes issues that arise with "staggered + not cleared" combos, as well
       * as clearing out DNS reporting info issues for ealier versions.
       *
       * Fix for PR 2302591:
       *  - Move away from using the _res global __res_state var and instead use
       *    a locally allocated __res_state structure.
       *     *  Ensures the IPv4 data is reset every time we collect DNS info.
       *     *  Calling res_nclose on the resolver structure and freeing it at
       *        the end of the configuration update ensure the IPv6 structure
       *        are not leaked (see: RecordResolverInfo())
       *  - Log the res_state nameserver configuration for debugging.
       *  - Validate and compute nscount4, nscount6 values
       *  - Pick DNS entries from both list, with the IPv6 array driving the
       *    order of the entries. Increment the '4 or '6 count when a DNS entry
       *    is selected.
       *
       * Clearing the IPv4 sin_family is REQUIRED to support all cases.
       * Otherwise the "staggered+'not cleared'" and "front-load" scenarios are
       * indistinguishable.
       *
       * Manually clearing the IPv4 sin_family field can be done after close if
       * continued use of res_init() and the _res global is desirable.
       */
      int addRC;
      int nscount4 = 0; /* number of IPv4 entries collected */
      int nscount6 = 0; /* number of IPv6 entries collected */

      PrintResolverNSInfo(resp);

      /*
       * Collect DNS entries
       *   There are up to __res_state.nscount valid entries, and nscount is
       *   limited to the array size (MAXNS).
       *   Loop up to __res_state.nscount, or until the target number of entries
       *   (IPv4 and IPv6) are collected.
       *   Requires IPv4 sin_family to be set to 0 for unused entries in
       *   __res_state.nsaddr_list[].
       */
      for (i = 0; i < resp->nscount && ((nscount4 + nscount6) < resp->nscount); i++) {
#   if defined RESOLVER_IPV6_EXT
         /*
          * IPv6 list is authoritative at a given index, process it first
          */
         if (resp->_u._ext.nsaddrs[i]) {
            /* Has IPv6 nameserver at index */
            addRC = AddResolverNSInfo(
                       dnsConfigInfo,
                       (struct sockaddr *)resp->_u._ext.nsaddrs[i]);
            if (addRC == 0) {
               /* Can't add more DNS entries to dnsConfigInfo, break loop */
               break;
            } else if (addRC > 0) {
               /* update count only if added */
               nscount6++;
            }
            /* Else: IPv6.sin6_family != AF_INET6 or other issues already
             *       logged in AddResolverNSInfo
             */
         }
         /* Else: null entry indicates no IPv6 at that position */
#   endif

         if (resp->nsaddr_list[i].sin_family == AF_INET) {
            /* Has IPv4 nameserver at index */
            addRC = AddResolverNSInfo(
                       dnsConfigInfo,
                       (struct sockaddr *)(&resp->nsaddr_list[i]));
            if (addRC == 0){
               /* Can't add more DNS entries to dnsConfigInfo, break loop */
               break;
            } else if (addRC > 0) {
               /* update count only if added */
               nscount4++;
            }
            /* Else: issue logged in AddResolverNSInfo */
         }
         /* Else: IPv4.sin_family == 0, it means a 'free' slot. */
      } /* for nscount and one of the total count is < nscount */

      if ((nscount4 + nscount6) < resp->nscount) {
         /*
          * Not all expected nameserver entries were collected
          */
         g_warning(
            "%s: dns update canceled, index=%d, ns=%d, IPv4=%d, IPv6=%d",
            __FUNCTION__,
            i, resp->nscount, nscount4, nscount6);
      }
   }
#endif                                  // if !defined RESOLVER_IPV6_GETSERVERS
}

/*
 ******************************************************************************
 * AddResolverNSInfo --                                                  */ /**
 *
 * @brief Add a nameserver IP address from resolver(3) subsystem to the
 *        DnsConfigInfo container.
 *
 * @param[out] dnsConfigInfo  Destination DnsConfigInfo container.
 * @param[in]  sap            The sockaddr structure containing the IP address
 *
 * @retval -1    IP address not added due to wrong or invalid parameter.
 * @retval  0    IP address not added due to limit reached @a dnsConfigInfo.
 * @retval  1    IP address added to @a dnsConfigInfo or otherwise.
 *
 ******************************************************************************
 */
static int
AddResolverNSInfo(DnsConfigInfo *dnsConfigInfo, struct sockaddr *sap)
{
   TypedIpAddress *ip;

   ASSERT(sap != NULL);
   ASSERT(dnsConfigInfo != NULL);

   if (sap->sa_family != AF_INET
       && sap->sa_family != AF_INET6) {
      /* Not a supported address family */
      g_debug("%s: unhandled address family (%d)", __FUNCTION__, sap->sa_family);
      return -1;
   }

   /* Check to see if we're going above our limit. See bug 605821. */
   if (dnsConfigInfo->serverList.serverList_len == DNSINFO_MAX_SERVERS) {
      g_message("%s: dns server limit (%d) reached, skipping overflow.",
         __FUNCTION__, DNSINFO_MAX_SERVERS);
      return 0;
   }

   ip = XDRUTIL_ARRAYAPPEND(dnsConfigInfo, serverList, 1);
   ASSERT_MEM_ALLOC(ip);
   GuestInfoSockaddrToTypedIpAddress(sap, ip);
   return 1;
}

#   ifndef RESOLVER_IPV6_GETSERVERS
/*
 ******************************************************************************
 * PrintResolverNSInfo --                                                */ /**
 *
 * @brief log the resolver(3) subsystem state about nameservers
 *
 * @param[in] resp    res_state, pointer to a resolver(3) state structure.
 *
 ******************************************************************************
 */
static void
PrintResolverNSInfo(res_state resp)
{
   /*
    * Init IPv6 state for unsupported case
    * Keep the message short < INET_ADDRSTRLEN for both v4 & v6
    */
   char ipv6Buff[INET6_ADDRSTRLEN+1] = "no-support";
   char ipv4Buff[INET_ADDRSTRLEN+1];
   int nscount = 0;
   int nscount6 = 0;
   int nscount4 = 0;
   int i;

   /* Split the counts per GLIBC 2.11+ source */
   nscount = resp->nscount;
#   ifdef RESOLVER_IPV6_EXT
   nscount6 = resp->_u._ext.nscount6;
#   endif
   nscount4 = nscount - nscount6;

   g_debug(
      "Resolver State: id=%d Count=%d, IPv4=%d, IPv6=%d",
       resp->id, nscount, nscount4, nscount6);

   for (i = 0; i < MAXNS; i++) {
#   ifdef RESOLVER_IPV6_EXT
      /*
       * Set IPv6 entry status
       */
      if (resp->_u._ext.nsaddrs[i] != NULL) {
         if (resp->_u._ext.nsaddrs[i]->sin6_family == AF_INET6) {
            const char *cvRes = inet_ntop(AF_INET6,
                             &resp->_u._ext.nsaddrs[i]->sin6_addr,
                             ipv6Buff, INET6_ADDRSTRLEN);
            if (cvRes == NULL) {
               /* Failed to output IPv6 address to buffer */
               int errsv = errno;
               /* Keep the message short < INET_ADDRSTRLEN for both v4 & v6 */
               Str_Sprintf(ipv6Buff, sizeof ipv6Buff, "ntop-fail:%d", errsv);
            }
         } else {
            /* Keep the message short < INET_ADDRSTRLEN for both v4 & v6 */
            Str_Sprintf(ipv6Buff, sizeof ipv6Buff, "AF:%02d",
              resp->_u._ext.nsaddrs[i]->sin6_family);
         }
      } else {
         /* Keep the message short < INET_ADDRSTRLEN for both v4 & v6 */
         Str_Sprintf(ipv6Buff, sizeof ipv6Buff, "no-entry");
      }
#   endif

      /*
       * Set IPv4 entry status
       */
      if (resp->nsaddr_list[i].sin_family == AF_INET) {
         const char *cvRes = inet_ntop(AF_INET,
                          &resp->nsaddr_list[i].sin_addr,
                          ipv4Buff, INET_ADDRSTRLEN);
         if (cvRes == NULL) {
            /* Failed to output IPv4 address to buffer */
            int errsv = errno;
            /* Keep the message short < INET_ADDRSTRLEN for both v4 & v6 */
            Str_Sprintf(ipv4Buff, sizeof ipv4Buff, "ntop-fail:%d", errsv);
         }
      } else {
         /* Keep the message short < INET_ADDRSTRLEN for both v4 & v6 */
         Str_Sprintf(ipv4Buff, sizeof ipv4Buff, "AF:%02d",
           resp->nsaddr_list[i].sin_family);
      }

      g_debug(
         "Resolver State: id=%d Lists[%d] - IPv4(%s) - IPv6(%s)",
         resp->id, i, ipv4Buff, ipv6Buff);
   }
}
#   endif // ifndef RESOLVER_IPV6_GETSERVERS
#endif // USE_RESOLVE


#ifdef USE_SLASH_PROC
/*
 ******************************************************************************
 * RecordRoutingInfoIPv4 --                                              */ /**
 *
 * @brief Query the IPv4 routing subsystem and pack up contents
 * (struct rtentry) into InetCidrRouteEntries.
 *
 * @param[in]  maxRoutes   Max routes to gather.
 * @param[out] nicInfo     NicInfoV3 container.
 *
 * @note Do not call this routine without first populating @a nicInfo 's NIC
 * list.
 *
 * @retval TRUE         Values collected, attached to @a nicInfo.
 * @retval FALSE        Something went wrong.  @a nicInfo is unharmed.
 *
 ******************************************************************************
 */

static Bool
RecordRoutingInfoIPv4(unsigned int maxRoutes,
                      NicInfoV3 *nicInfo)
{
   GPtrArray *routes = NULL;
   guint i;
   Bool ret = FALSE;

   ASSERT(maxRoutes > 0);

   if ((routes = SlashProcNet_GetRoute(maxRoutes, RTF_UP)) == NULL) {
      return FALSE;
   }

   for (i = 0; i < routes->len; i++) {
      struct rtentry *rtentry;
      struct sockaddr_in *sin_dst;
      struct sockaddr_in *sin_gateway;
      struct sockaddr_in *sin_genmask;
      InetCidrRouteEntry *icre;
      uint32_t ifIndex;

      /* Check to see if we're going above our limit. See bug 605821. */
      if (nicInfo->routes.routes_len == NICINFO_MAX_ROUTES) {
         g_message("%s: route limit (%d) reached, skipping overflow.",
                   __FUNCTION__, NICINFO_MAX_ROUTES);
         break;
      }

      rtentry = g_ptr_array_index(routes, i);

      if (!GuestInfoGetNicInfoIfIndex(nicInfo,
                                      if_nametoindex(rtentry->rt_dev),
                                      &ifIndex)) {
         continue;
      }

      icre = XDRUTIL_ARRAYAPPEND(nicInfo, routes, 1);
      ASSERT_MEM_ALLOC(icre);

      sin_dst = (struct sockaddr_in *)&rtentry->rt_dst;
      sin_gateway = (struct sockaddr_in *)&rtentry->rt_gateway;
      sin_genmask = (struct sockaddr_in *)&rtentry->rt_genmask;

      GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin_dst,
                                        &icre->inetCidrRouteDest);

      icre->inetCidrRoutePfxLen = CountNetmaskBitsV4((struct sockaddr *)sin_genmask);

      /*
       * Gateways are optional (ex: one can bind a route to an interface w/o
       * specifying a next hop address).
       */
      if (rtentry->rt_flags & RTF_GATEWAY) {
         TypedIpAddress *ip = Util_SafeCalloc(1, sizeof *ip);
         GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin_gateway, ip);
         icre->inetCidrRouteNextHop = ip;
      }

      /*
       * Interface, metric.
       */
      icre->inetCidrRouteIfIndex = ifIndex;
      icre->inetCidrRouteMetric = rtentry->rt_metric;
   }

   ret = TRUE;

   SlashProcNet_FreeRoute(routes);
   return ret;
}


/*
 ******************************************************************************
 * RecordRoutingInfoIPv6 --                                              */ /**
 *
 * @brief Query the IPv6 routing subsystem and pack up contents
 * (struct in6_rtmsg) into InetCidrRouteEntries.
 *
 * @param[in]  maxRoutes   Max routes to gather.
 * @param[out] nicInfo     NicInfoV3 container.
 *
 * @note Do not call this routine without first populating @a nicInfo 's NIC
 * list.
 *
 * @retval TRUE         Values collected, attached to @a nicInfo.
 * @retval FALSE        Something went wrong.  @a nicInfo is unharmed.
 *
 ******************************************************************************
 */

static Bool
RecordRoutingInfoIPv6(unsigned int maxRoutes,
                      NicInfoV3 *nicInfo)
{
   GPtrArray *routes = NULL;
   guint i;
   Bool ret = FALSE;

   ASSERT(maxRoutes > 0);

   /*
    * Reading large number of ipv6 routes in pathToNetRoute6 could
    * result in performance issue because:
    *  1. IPv6 route table is not efficient natively compared to ipv4
    *     because of its implementation.
    *  2. The glib I/O channel can aggravate the performace.
    * Considering bug 605821/2064541, we try to read the first maxRoutes
    * lines of pathToNetRoute6 with route flag RTF_UP set.
    */
   if ((routes = SlashProcNet_GetRoute6(maxRoutes, RTF_UP)) == NULL) {
      return FALSE;
   }

   for (i = 0; i < routes->len; i++) {
      struct sockaddr_storage ss;
      struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
      struct in6_rtmsg *in6_rtmsg;
      InetCidrRouteEntry *icre;
      uint32_t ifIndex = -1;

      /* Check to see if we're going above our limit. See bug 605821. */
      if (nicInfo->routes.routes_len == NICINFO_MAX_ROUTES) {
         g_message("%s: route limit (%d) reached, skipping overflow.",
                   __FUNCTION__, NICINFO_MAX_ROUTES);
         break;
      }

      in6_rtmsg = g_ptr_array_index(routes, i);

      if (!GuestInfoGetNicInfoIfIndex(nicInfo, in6_rtmsg->rtmsg_ifindex,
                                      &ifIndex)) {
         continue;
      }

      icre = XDRUTIL_ARRAYAPPEND(nicInfo, routes, 1);
      ASSERT_MEM_ALLOC(icre);

      /*
       * Destination.
       */
      sin6->sin6_family = AF_INET6;
      sin6->sin6_addr = in6_rtmsg->rtmsg_dst;
      GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin6,
                                        &icre->inetCidrRouteDest);

      icre->inetCidrRoutePfxLen = in6_rtmsg->rtmsg_dst_len;

      /*
       * Next hop.
       */
      if (in6_rtmsg->rtmsg_flags & RTF_GATEWAY) {
         TypedIpAddress *ip = Util_SafeCalloc(1, sizeof *ip);
         sin6->sin6_addr = in6_rtmsg->rtmsg_gateway;
         GuestInfoSockaddrToTypedIpAddress((struct sockaddr *)sin6, ip);
         icre->inetCidrRouteNextHop = ip;
      }

      /*
       * Interface, metric.
       */
      icre->inetCidrRouteIfIndex = ifIndex;
      icre->inetCidrRouteMetric = in6_rtmsg->rtmsg_metric;
   }

   ret = TRUE;

   SlashProcNet_FreeRoute6(routes);
   return ret;
}


/*
 ******************************************************************************
 * RecordRoutingInfo --                                                  */ /**
 *
 * @brief Query the routing subsystem and pack up contents into
 * InetCidrRouteEntries when either of IPv4 or IPV6 is configured.
 *
 * @param[in]  maxIPv4Routes  Max IPv4 routes to gather.
                              Set 0 to disable gathering.
 * @param[in]  maxIPv6Routes  Max IPv6 routes to gather.
                              Set 0 to disable gathering.
 * @param[out] nicInfo        NicInfoV3 container.
 *
 * @note Do not call this routine without first populating @a nicInfo 's NIC
 * list.
 *
 * @retval TRUE         Values collected(either IPv4 or IPv6 or both),
 *                      attached to @a nicInfo.
 * @retval FALSE        Something went wrong(neither IPv4 nor IPv6 configured).
 *
 ******************************************************************************
 */

static Bool
RecordRoutingInfo(unsigned int maxIPv4Routes,
                  unsigned int maxIPv6Routes,
                  NicInfoV3 *nicInfo)
{
   Bool retIPv4 = FALSE;
   Bool retIPv6 = FALSE;

   ASSERT(maxIPv4Routes > 0 || maxIPv6Routes > 0);

   /*
    * We gather IPv4 routes first, then IPv6. This means IPv4 routes are more
    * prioritized than IPv6. When there's more than NICINFO_MAX_ROUTES IPv4
    * routes in system, the IPv6 routes will be ignored. A more equitable
    * design might be getting max IPv4 and IPv6 routes first, and then pick
    * out the head NICINFO_MAX_ROUTES/2 of each route list.
    */
   if (maxIPv4Routes > 0) {
      if (RecordRoutingInfoIPv4(maxIPv4Routes, nicInfo)) {
         retIPv4 = TRUE;
      } else {
         g_warning("%s: Unable to collect IPv4 routing table.\n", __func__);
      }
   }

   if (maxIPv6Routes > 0 && nicInfo->routes.routes_len < NICINFO_MAX_ROUTES) {
      if (RecordRoutingInfoIPv6(maxIPv6Routes, nicInfo)) {
         retIPv6 = TRUE;
      } else {
         g_warning("%s: Unable to collect IPv6 routing table.\n", __func__);
      }
   }

   return (retIPv4 || retIPv6);
}

#else                                           // ifdef USE_SLASH_PROC
static Bool
RecordRoutingInfo(unsigned int maxIPv4Routes,
                  unsigned int maxIPv6Routes,
                  NicInfoV3 *nicInfo)
{
   return TRUE;
}
#endif                                          // else

#ifndef NO_DNET

#if !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(USERWORLD)
/*
 ******************************************************************************
 * GuestInfoGetIntf --                                                   */ /**
 *
 * @brief Callback function called by libdnet when iterating over all the NICs
 * on the host.
 *
 * @param[in]      intf_entry  sockaddr struct to convert.
 * @param[in/out]  arg         GuestInfoIpPriority struct
 *
 * arg points to an GuestInfoIpPriority structure containing the priority we
 * are looking for, and a pointer to the ip address to be set.
 *
 * @retval -1              If applicable address found, returns string of said
 *                         IP address in buffer.
 * @retval 0               If applicable address not found.
 *
 ******************************************************************************
 */

static int
GuestInfoGetIntf(const struct intf_entry *entry, // IN
                 void *arg)                      // IN/OUT
{
   GuestInfoIpPriority *ipp = arg;
   char **ipstr = &ipp->ipstr;

   if (entry->intf_type == INTF_TYPE_ETH &&
       entry->intf_link_addr.addr_type == ADDR_TYPE_ETH) {
      struct sockaddr_storage ss;
      struct sockaddr *saddr = (struct sockaddr *)&ss;

      if (GuestInfo_IfaceGetPriority(entry->intf_name) != ipp->priority ||
          GuestInfo_IfaceIsExcluded(entry->intf_name)) {
         return 0;
      }

      memset(&ss, 0, sizeof ss);
      addr_ntos(&entry->intf_addr, saddr);
      *ipstr = ValidateConvertAddress(saddr);
      if (*ipstr != NULL) {
         /* We need to return an error to exit out of the loop. */
         return -1;
      }
   }

   return 0;
}
#endif

#endif // ifndef NO_DNET

/*
 ******************************************************************************
 * ValidateConvertAddress --                                             */ /**
 *
 * @brief Helper routine validates an address as a return value for
 * GuestInfoGetPrimaryIP.
 *
 * @param[in]  addr  sockaddr struct to convert.
 *
 * @return  If applicable address found, returns string of said IP address.
 *          If an error occurred, returns NULL.
 *
 ******************************************************************************
 */

static char *
ValidateConvertAddress(const struct sockaddr *addr)  // IN:
{
   char ipstr[INET6_ADDRSTRLEN];

   if (addr->sa_family == AF_INET) {
      struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;

      if (addr4->sin_addr.s_addr == htonl(INADDR_LOOPBACK) ||
          addr4->sin_addr.s_addr == htonl(INADDR_ANY) ||
          !inet_ntop(addr->sa_family, &addr4->sin_addr, ipstr, sizeof ipstr)) {
         return NULL;
      }
   } else if (addr->sa_family == AF_INET6) {
      struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;

      if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr) ||
          IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr) ||
          IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr) ||
          IN6_IS_ADDR_UNIQUELOCAL(&addr6->sin6_addr) ||
          IN6_IS_ADDR_UNSPECIFIED(&addr6->sin6_addr) ||
          !inet_ntop(addr->sa_family, &addr6->sin6_addr, ipstr,
                     sizeof ipstr)) {
         return NULL;
      }
   } else {
      return NULL;
   }

   return Util_SafeStrdup(ipstr);
}
 070701000001B0000081A40000000000000000000000016822550500000761000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/lib/nicInfo/util.c /*********************************************************
 * Copyright (C) 2014-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file util.c
 *
 * Random GuestInfo type utilities.
 */

#include <string.h>

#include "vmware.h"
#include "nicInfoInt.h"
#include "xdrutil.h"


/*
 ******************************************************************************
 * GuestInfo_Util_FindNicByMac --                                        */ /**
 *
 * Searches a NIC list for the NIC identified by a MAC address.
 *
 * @param[in]   nicInfo NIC container.
 * @param[in]   mac     MAC address.
 *
 * @retval GuestNicV3* if found.
 * @retval NULL if not found.
 *
 ******************************************************************************
 */

GuestNicV3 *
GuestInfoUtilFindNicByMac(const NicInfoV3 *nicInfo,
                          const char *macAddress)
{
   u_int i;

   ASSERT(nicInfo);
   ASSERT(macAddress);

   XDRUTIL_FOREACH(i, nicInfo, nics) {
      GuestNicV3 *nic;
      nic = XDRUTIL_GETITEM(nicInfo, nics, i);
      if (strcasecmp(nic->macAddress, macAddress) == 0) {
         return nic;
      }
   }

   return NULL;
}
   070701000001B1000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002D00000000open-vm-tools-12.5.2/open-vm-tools/lib/panic  070701000001B2000081A400000000000000000000000168225505000003D5000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/panic/Makefile.am  ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libPanic.la

libPanic_la_SOURCES =
libPanic_la_SOURCES += panic.c
   070701000001B3000081A400000000000000000000000168225505000037EA000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/panic/panic.c  /*********************************************************
 * Copyright (C) 2006-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * panic.c --
 *
 *      Module to encapsulate common Panic behaviors.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifdef _WIN32
#  include <process.h>
#  include <Windows.h>
#else // Posix
#  include <unistd.h>
#  include <signal.h>
#  ifdef __APPLE__
#    include <TargetConditionals.h>
#    include <sys/types.h>
#    include <sys/sysctl.h>
#  endif
#endif // Win32 vs Posix

#include "vmware.h"
#include "log.h"
#include "panic.h"
#include "msg.h"
#include "str.h"
#include "config.h"
#include "util.h"
#include "userlock.h"
#if defined(_WIN32) || !defined(VMX86_TOOLS)
#include "coreDump.h"
#endif
#ifdef _WIN32
#include "windowsu.h"
#endif

static struct PanicState {
   Bool msgPostOnPanic;
   Bool coreDumpOnPanic;
   Bool loopOnPanic;
   int coreDumpFlags; /* Memorize for clients without init func */
   PanicBreakAction breakOnPanic; /* XXX: should this be DEVEL only? */
} panicState = { TRUE, TRUE }; /* defaults in lieu of Panic_Init() */


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_Init --
 *
 *    Inits the panic module.
 *
 * Results:
 *    void
 *
 * Side effects:
 *    Sets panic state.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic_Init(void)
{
   panicState.coreDumpOnPanic = Config_GetBool(TRUE, "coreDumpOnPanic");
   panicState.loopOnPanic = Config_GetBool(FALSE, "panic.loopOnPanic");
   panicState.breakOnPanic = Config_GetLong(PanicBreakAction_Never,
                                            "panic.breakOnPanic");
   panicState.coreDumpFlags = Config_GetLong(0, "coreDumpFlags");
}


/*
 *----------------------------------------------------------------------
 *
 * Panic_SetPanicMsgPost --
 *
 *      Allow the Msg_Post() on panic to be suppressed.  If passed FALSE,
 *      then any subsequent Panics will refrain from posting the Panic
 *      message.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Panic_SetPanicMsgPost(Bool postMsg)
{
   panicState.msgPostOnPanic = postMsg;
}


/*
 *----------------------------------------------------------------------
 *
 * Panic_GetPanicMsgPost --
 *
 *      Determines whether to post a message during Panic.
 *
 * Results:
 *      TRUE iff it's OK to post messages during Panic.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
Panic_GetPanicMsgPost(void)
{
   return panicState.msgPostOnPanic;
}


/*
 *----------------------------------------------------------------------
 *
 * Panic_SetCoreDumpOnPanic --
 *
 *      Allow the core dump on panic to be suppressed.  If passed FALSE,
 *      then any subsequent Panics will not attempt to dump core.
 *
 * Results:
 *      void.
 *
 * Side effects:
 *      Enables/Disables core dump on Panic().
 *
 * Bugs:
 *      This really should act like Panic_Loop and Panic_Break, and just
 *      be Panic_CoreDumpOnPanic, and not have to export the state back
 *      out.  That requires this module being the one that knows how to
 *      actually carry out the core dump, which seems like a good thing
 *      anyway.  (Then Panic_Panic can do it too.)
 *
 *----------------------------------------------------------------------
 */

void
Panic_SetCoreDumpOnPanic(Bool dumpCore)
{
   panicState.coreDumpOnPanic = dumpCore;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_GetCoreDumpOnPanic --
 *
 *      Returns whether panic should attempt to dump core.
 *
 * Results:
 *      TRUE if panics should dump core.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Panic_GetCoreDumpOnPanic(void)
{
   return panicState.coreDumpOnPanic;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_GetCoreDumpFlags --
 *
 *      Return the core dump flags.  The reason this module knows about this
 *      is because it's available to everyone who wants to know, and has an
 *      init function which is a convenient time to read configuration info.
 *      Putting the same functionality elsewhere is harder.
 *
 * Results:
 *      flags if set; 0 by default.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
Panic_GetCoreDumpFlags(void)
{
   return panicState.coreDumpFlags;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_SetCoreDumpFlags --
 *
 *      Although the core dump flags are read at init time, we may want to
 *      update this value later. This is especially true because the default
 *      value is read before the VM config is established.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Updates global struct panicState.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic_SetCoreDumpFlags(int flags)   // IN
{
   panicState.coreDumpFlags = flags;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_LoopOnPanic --
 *
 *    Loop until debugger intervention, if so configured.
 *
 * Results:
 *    void, eventually.
 *
 * Side effects:
 *    Stall for time.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic_LoopOnPanic(void)
{
   if (panicState.loopOnPanic) {
      fprintf(stderr, "Looping pid=%d\n", (int)getpid());
      while (panicState.loopOnPanic) {
         sleep(1);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_BreakOnPanic --
 *
 *    Attract the attention of a nearby debugger.
 *
 * Results:
 *    void, eventually.
 *
 * Side effects:
 *    DebugBreak.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic_BreakOnPanic(void)
{
#if defined(_WIN32)
   if (Panic_GetBreakOnPanic()) {
      Warning("Panic: breaking into debugger\n");
      DebugBreak();
   }
#elif defined(__APPLE__) && (defined(__x86_64__) || defined(__i386__))
   if (Panic_GetBreakOnPanic()) {
      Warning("Panic: breaking into debugger\n");
#  if TARGET_OS_IPHONE
      __builtin_trap();
#  else
      __asm__ __volatile__ ("int3");
#  endif
   }
#else // Posix
   switch (panicState.breakOnPanic) {
   case PanicBreakAction_Never:
      break;
   case PanicBreakAction_IfDebuggerAttached:
      {
         void (*handler)(int);
         handler = signal(SIGTRAP, SIG_IGN);

         /*
          * INT3 is not always ignored, so explicitely use kill() here.
          */
         kill(getpid(), SIGTRAP);

         signal(SIGTRAP, handler);
      }
      break;
   default:
   case PanicBreakAction_Always:
      Warning("Panic: breaking into debugger\n");
#  if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
      __asm__ __volatile__ ("int3");
#  else
      kill(getpid(), SIGTRAP);
#  endif
      break;
   }
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_SetBreakAction --
 *
 *      Allow the debug breakpoint on panic to be suppressed.
 *
 * Results:
 *      void.
 *
 * Side effects:
 *      Enables/Disables break into debugger on Panic().
 *
 *-----------------------------------------------------------------------------
 */

void
Panic_SetBreakAction(PanicBreakAction action)  // IN:
{
   panicState.breakOnPanic = action;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_GetBreakAction --
 *
 *      Return the break action that will be taken on an eventual panic.
 *
 * Results:
 *      The current break action.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

PanicBreakAction
Panic_GetBreakAction(void)
{
   return panicState.breakOnPanic;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_GetBreakOnPanic --
 *
 *    Whether or not we should break into the debugger on the current
 *    panic iteration.
 *
 * Results:
 *    TRUE if a break is in order, FALSE otherwise
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Panic_GetBreakOnPanic(void)
{
   Bool shouldBreak = FALSE;

   switch (panicState.breakOnPanic) {
   case PanicBreakAction_Never:
      break;
   case PanicBreakAction_IfDebuggerAttached:
#if defined(_WIN32)
      shouldBreak = IsDebuggerPresent();
#elif defined(__APPLE__) && (defined(__x86_64__) || defined(__i386__))
      {
         /*
          * https://developer.apple.com/library/content/qa/qa1361/
          */
         int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
         struct kinfo_proc info;
         size_t size;
         int ret;

         info.kp_proc.p_flag = 0;
         size = sizeof info;
         ret = sysctl(mib, ARRAYSIZE(mib), &info, &size, NULL, 0);
         if (ret == 0) {
            shouldBreak = (info.kp_proc.p_flag & P_TRACED) != 0;
         }
      }
#else
      /*
       * This case is handled by Panic_BreakOnPanic for Posix as there is no
       * portable way to know if we're being debugged other than actually
       * trapping into the debugger.
       */
#endif
      break;
   default:
   case PanicBreakAction_Always:
      shouldBreak = TRUE;
      break;
   }
   return shouldBreak;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_Panic --
 *
 *      Panic, possibly core dump
 *
 *      A nice default implementation that basic Panic can call if you don't
 *      want to write your own.  The VMX of course has its own.
 *
 *      TODO: Figure out how to trigger the Mac OS X Crash Reporter.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Death.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic_Panic(const char *format,
            va_list args)
{
   char buf[1024];
   static int count = 0;

   MXUser_SetInPanic();

   Str_Vsnprintf(buf, sizeof buf, format, args);

   /*
    * Write the message to stderr first, so there's always
    * some sort of record.
    * Don't try to do anything fancy, since this is before
    * panic loop detection.  In particular, try not to call
    * any of our functions (that may call Panic()).
    */
   fputs(buf, stderr);

#ifdef _WIN32
   /*
    * This would nominally be Win32U_OutputDebugString.  However,
    * OutputDebugString is unusual in that the W version converts
    * to local encoding and calls the A version.
    *
    * Since any such conversion is risky (read: can Panic) and
    * we haven't yet hit the loop detection, we will conservatively
    * dump UTF-8 via the A version.
    */
   OutputDebugStringA(buf);
#endif

   /*
    * Panic loop detection:
    *    first time - do the whole report and shutdown sequence
    *    second time - log and exit
    *    beyond second time - just exit
    */

   switch (count++) {  // Try HARD to not put code in here!
   case 0:  // case 0 stuff is below
      break;
   case 1:
      Log_Panic("PANIC: %s", buf);
      Log_Panic("Panic loop\n");
      /* Fall through */
   default:
      fprintf(stderr, "Panic loop\n");
      Util_ExitProcessAbruptly(1);
      NOT_REACHED();
   }

   Log_DisableThrottling(); // Make sure Panic gets logged

#ifdef _WIN32
   /*
    * Output again, in a way that we hope localizes correctly.  Since
    * we are converting, this can Panic, so it must run after loop
    * detection.
    */
   Win32U_OutputDebugString(buf);
#endif

   /*
    * Log panic information.
    */

   Log_Panic("PANIC: %s", buf);
   Util_Backtrace(0);

   /*
    * Do the debugging steps early before we have a chance
    * to double panic.
    */

   Panic_DumpGuiResources();

#if (defined(_WIN32) || !defined(VMX86_TOOLS)) && !defined(__ANDROID__) && \
    !(TARGET_OS_IPHONE) && !defined(__EMSCRIPTEN__)
   if (Panic_GetCoreDumpOnPanic()) {
      CoreDump_CoreDump();
   }
#endif

   Panic_LoopOnPanic();

   /*
    * Show pretty panic dialog.
    * This is where things can go badly wrong.
    */

   Panic_PostPanicMsg(buf);

   /*
    * Bye
    */
   Log("Exiting\n");
#if TARGET_OS_IPHONE
    __builtin_trap();
#else
   Util_ExitProcessAbruptly(-1);
#endif
   NOT_REACHED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic_DumpGuiResources --
 *
 *      Dumps userlevel resources used by the current process.
 *
 * Results:
 *      void
 *
 * Side effects:
 *      Logs.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic_DumpGuiResources(void)
{
#ifdef _WIN32
   HANDLE hUser = Win32U_GetModuleHandle("user32.dll");
   if (hUser) {
      typedef DWORD (WINAPI *fnGetGuiResources)(HANDLE, DWORD);
      fnGetGuiResources pGetGuiResources =
         (fnGetGuiResources) GetProcAddress(hUser, "GetGuiResources");
      if (pGetGuiResources) {
         Warning("Win32 object usage: GDI %d, USER %d\n",
                 pGetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS),
                 pGetGuiResources(GetCurrentProcess(), GR_USEROBJECTS));
      }
   }
#endif
}
  070701000001B4000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/panicDefault   070701000001B5000081A400000000000000000000000168225505000003F1000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/panicDefault/Makefile.am   ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libPanicDefault.la

libPanicDefault_la_SOURCES =
libPanicDefault_la_SOURCES += panicDefault.c
   070701000001B6000081A40000000000000000000000016822550500000600000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/panicDefault/panicDefault.c    /*********************************************************
 * Copyright (C) 2006-2016, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * panic.c --
 *
 *      Basic Panic()
 */


#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#include "vmware.h"
#include "panic.h"


/*
 *-----------------------------------------------------------------------------
 *
 * Panic --
 *
 *      Minimal panic.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Print message, force everything to quit.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic(const char *fmt,   // IN: message format
      ...)               // IN: message format arguments
{
   va_list ap;

   va_start(ap, fmt);
   Panic_Panic(fmt, ap);
   va_end(ap);
}
070701000001B7000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/poll   070701000001B8000081A400000000000000000000000168225505000003D1000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/poll/Makefile.am   ################################################################################
### Copyright (C) 2013-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libPoll.la

libPoll_la_SOURCES =
libPoll_la_SOURCES += poll.c
   070701000001B9000081A400000000000000000000000168225505000125AB000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/poll/poll.c    /*********************************************************
 * Copyright (C) 1998-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * poll.c -- management of the event callback queues, selects, ...
 */


#include "vmware.h"
#include "pollImpl.h"
#ifdef _WIN32
   #include "vmci_sockets.h"
   #include <winsock2.h>
   #include <ws2tcpip.h>
   #include "err.h"
   #include "preference.h"
#endif

/*
 * Maximum time (us.) to sleep when there is nothing else to do
 * before this time elapses. It has an impact on how often the
 * POLL_MAIN_LOOP events are fired.  --hpreg
 */

#define MAX_SLEEP_TIME (1 * 1000 * 1000) /* 1 s. */


static const PollImpl *pollImpl = NULL;


/*
 *----------------------------------------------------------------------
 *
 * PollSanitizeFlags --
 *
 *      For historical reasons we translate POLL_DEVICE with zero flags
 *      to POLL_FLAG_READ.  No-one knows why anymore.
 *
 * Results:
 *      Updated flags value.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE int
PollSanitizeFlags(PollEventType type,   // IN: Event type
                  int flags)            // IN: Flags
{
   if (type == POLL_DEVICE) {
      /*
       * Either read or write must be requested for devices.
       * On Windows use POLL_FLAG_READ for waiting on events.
       */
      ASSERT((flags & (POLL_FLAG_READ | POLL_FLAG_WRITE)) != 0);
   }
   return flags;
}


/*
 *----------------------------------------------------------------------
 *
 * Poll_InitWithImpl --
 *
 *      Module initialization. An implementation of Poll should call
 *      this is initialize the function table and then start Poll.
 *
 * Results: void
 *
 * Side effects: poll is alive
 *
 *----------------------------------------------------------------------
 */

void
Poll_InitWithImpl(const PollImpl *impl)
{
   ASSERT(pollImpl == NULL);

   pollImpl = impl;

   pollImpl->Init();
}


/*
 *----------------------------------------------------------------------
 *
 * Poll_IsInitialized --
 *
 *      Allows libraries to determine if they can use poll or not.
 *
 * Results:
 *      TRUE if and only if poll is initialized.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
Poll_IsInitialized(void)
{
   return pollImpl != NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * Poll_Exit --
 *
 *      module de-initalization
 *
 * Warning:
 *
 *      This function is intended to be called from vmxScsiLib or
 *      nbdScsiLib only. It has *not* been used, nor tested, in the
 *      context of the VMX product.
 *
 *----------------------------------------------------------------------
 */
void
Poll_Exit(void)
{
   if (pollImpl) {
      pollImpl->Exit();
      pollImpl = NULL;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * Poll_LockingEnabled --
 *
 *      Determine if locking is enabled in the underlying poll implementation.
 *
 * Results:
 *      TRUE if locking is enabled.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
Poll_LockingEnabled(void)
{
   return pollImpl->LockingEnabled();
}


/*
 *----------------------------------------------------------------------
 *
 * Poll_LoopTimeout --
 *
 *      The poll loop.
 *      This is supposed to be the main loop for most programs.
 *
 * Result:
 *      Void.
 *
 * Side effects:
 *      Fiat lux!
 *
 *----------------------------------------------------------------------
 */

void
Poll_LoopTimeout(Bool loop,          // IN: loop forever if TRUE, else do one pass.
                 Bool *exit,         // IN: NULL or set to TRUE to end loop.
                 PollClass class,    // IN: class of events (POLL_CLASS_*)
                 int timeout)        // IN: maximum time to sleep
{
   pollImpl->LoopTimeout(loop, exit, class, timeout);
}



/*
 *----------------------------------------------------------------------
 *
 * Poll_Loop --
 *
 *      Run Poll_LoopTimeout with the default timeout of
 *      MAX_SLEEP_TIME (1 second)
 *
 *----------------------------------------------------------------------
 */

void
Poll_Loop(Bool loop,          // IN: loop forever if TRUE, else do one pass
          Bool *exit,         // IN: NULL or set to TRUE to end loop.
          PollClass class)    // IN: class of events (POLL_CLASS_*)
{
   Poll_LoopTimeout(loop, exit, class, MAX_SLEEP_TIME);
}


/*
 *----------------------------------------------------------------------
 *
 * Poll_CallbackRemove --
 *
 *      remove a callback from the real-time queue, the virtual time
 *      queue, the file descriptor select set, or the main loop queue.
 *
 * Results:
 *      TRUE if entry found and removed, FALSE otherwise
 *
 * Side effects:
 *      queues modified
 *
 *----------------------------------------------------------------------
 */

Bool
Poll_CallbackRemove(PollClassSet classSet,
                    int flags,
                    PollerFunction f,
                    void *clientData,
                    PollEventType type)
{
   ASSERT(f);
   flags = PollSanitizeFlags(type, flags);
   return pollImpl->CallbackRemove(classSet, flags, f, clientData, type);
}


/*
 *----------------------------------------------------------------------
 *
 * Poll_Callback --
 *
 *      Insert a callback into one of the queues (e.g., the real-time
 *      queue, the virtual time queue, the file descriptor select
 *      set, or the main loop queue).
 *
 *      For the POLL_REALTIME or POLL_DEVICE queues, entries can be
 *      inserted for good, to fire on a periodic basis (by setting the
 *      POLL_FLAG_PERIODIC flag).
 *
 *      Otherwise, the callback fires only once.
 *
 *      For periodic POLL_REALTIME callbacks, "info" is the time in
 *      microseconds between execution of the callback.  For
 *      POLL_DEVICE callbacks, info is a file descriptor.
 *
 *
 *----------------------------------------------------------------------
 */

VMwareStatus
Poll_Callback(PollClassSet classSet,
              int flags,
              PollerFunction f,
              void *clientData,
              PollEventType type,
              PollDevHandle info,
              MXUserRecLock *lock)
{
   ASSERT(f);
   flags = PollSanitizeFlags(type, flags);
   return pollImpl->Callback(classSet, flags, f, clientData, type, info, lock);
}


/*
 *----------------------------------------------------------------------
 *
 * Poll_CallbackRemoveOneByCB --
 *
 *      Remove poll entry previously added by Poll_Callback.  If there
 *      are multiple entries queued specifying same callback, it is
 *      indeterminate which one will be removed and returned.
 *
 * Results:
 *      TRUE if entry found and removed, *clientData is set to entry's
 *           client data
 *      FALSE if entry was not found
 *
 * Side effects:
 *      queues modified
 *
 *----------------------------------------------------------------------
 */

Bool
Poll_CallbackRemoveOneByCB(PollClassSet classSet,
                           int flags,
                           PollerFunction f,
                           PollEventType type,
                           void **clientData)
{
   ASSERT(f);
   ASSERT(clientData);
   flags = PollSanitizeFlags(type, flags);
   return pollImpl->CallbackRemoveOneByCB(classSet, flags, f, type, clientData);
}


/*
 *----------------------------------------------------------------------------
 *
 * Poll_NotifyChange --
 *
 *      Wake up a sleeping Poll_LoopTimeout() when there is a change
 *      it should notice, and no normal event can be expected to wake
 *      it up in a timely manner.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
Poll_NotifyChange(PollClassSet classSet)  // IN
{
   pollImpl->NotifyChange(classSet);
}


/*
 *----------------------------------------------------------------------
 *
 * Wrappers for Poll_Callback and Poll_CallbackRemove -- special cases
 * with fewer arguments.
 *
 *----------------------------------------------------------------------
 */

VMwareStatus
Poll_CB_Device(PollerFunction f,
               void *clientData,
               PollDevHandle info,
               Bool periodic)
{
   return
   Poll_Callback(POLL_CS_MAIN,
                 POLL_FLAG_READ |
                 POLL_FLAG_REMOVE_AT_POWEROFF |
                 (periodic ? POLL_FLAG_PERIODIC : 0),
                 f,
                 clientData,
                 POLL_DEVICE,
                 info, NULL);
}


Bool
Poll_CB_DeviceRemove(PollerFunction f,
                     void *clientData,
                     Bool periodic)
{
   return
      Poll_CallbackRemove(POLL_CS_MAIN,
                          POLL_FLAG_READ |
                          POLL_FLAG_REMOVE_AT_POWEROFF |
                          (periodic ? POLL_FLAG_PERIODIC : 0),
                          f,
                          clientData,
                          POLL_DEVICE);
}


VMwareStatus
Poll_CB_RTime(PollerFunction f,
              void *clientData,
              int64 info, //microsecs
              Bool periodic,
              MXUserRecLock *lock)
{
   return
   Poll_Callback(POLL_CS_MAIN,
                 POLL_FLAG_REMOVE_AT_POWEROFF |
                 (periodic ? POLL_FLAG_PERIODIC : 0),
                 f,
                 clientData,
                 POLL_REALTIME,
                 info, lock);
}


Bool
Poll_CB_RTimeRemove(PollerFunction f,
                    void *clientData,
                    Bool periodic)
{
   return
      Poll_CallbackRemove(POLL_CS_MAIN,
                          POLL_FLAG_REMOVE_AT_POWEROFF |
                          (periodic ? POLL_FLAG_PERIODIC : 0),
                          f,
                          clientData,
                          POLL_REALTIME);
}

#ifdef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * PollSocketPairPrepare --
 *
 *      Do miscellaneous preparation for the socket pair before connecting
 *
 * Results:
 *      The given 'dst' server socket is bound to (and listening on, if a
 *      stream socket was requested) the specified 'addr' address, with the
 *      address updated to contain the actual assigned port number if a
 *      wildcard port was specified.
 *
 *      The given client 'src' socket is set into the specified blocking mode.
 *
 *      Returns TRUE if all preparation succeeds, otherwise FALSE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
PollSocketPairPrepare(Bool blocking,           // IN: blocking socket?
                      SOCKET src,              // IN: client side socket
                      SOCKET dst,              // IN: server side socket
                      struct sockaddr *addr,   // IN/OUT: server listener address
                      int addrlen,             // IN: size of listener address
                      int socketCommType)      // IN: SOCK_STREAM or SOCK_DGRAM?
{
   if (bind(dst, addr, addrlen) == SOCKET_ERROR) {
      Log("%s: Could not bind dst socket %d, error %d.\n",
          __FUNCTION__, dst, WSAGetLastError());
      return FALSE;
   }

   if (!blocking) {
      unsigned long a = 1;
      if (ioctlsocket(src, FIONBIO, &a) == SOCKET_ERROR) {
         Log("%s: Could not make src socket %d non-blocking, error %d.\n",
             __FUNCTION__, src, WSAGetLastError());
         return FALSE;
      }
   }

   if (socketCommType == SOCK_STREAM && listen(dst, 1) == SOCKET_ERROR) {
      Log("%s: Could not listen on dst socket %d, error %d.\n",
          __FUNCTION__, dst, WSAGetLastError());
      return FALSE;
   }

   if (getsockname(dst, addr, &addrlen) == SOCKET_ERROR) {
      Log("%s: getsockname() failed for dst socket %d, error %d.\n",
         __FUNCTION__, dst, WSAGetLastError());
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollSocketPairConnect --
 *
 *      Connects a given socket to a given address.
 *
 * Results:
 *      TRUE if connecting successfully, otherwise FALSE is returned.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
PollSocketPairConnect(Bool blocking,           // IN: blocking socket?
                      struct sockaddr *addr,   // IN: the address to connect to
                      int addrlen,             // IN: size of 'addr'
                      SOCKET sock,             // IN: connecting socket
                      SocketSpecialOpts opts)  // IN: socket special options
{
   if (blocking && (opts & POLL_OPTIONS_SOCKET_PAIR_NONBLOCK_CONN)) {
      /* Change blocking socket to non-blocking socket for timeout */
      unsigned long unblock = 1;
      if (ioctlsocket(sock, FIONBIO, &unblock) == SOCKET_ERROR) {
         Log("%s: Set socket %d to non-blocking mode failed, error %d.\n",
             __FUNCTION__, sock, WSAGetLastError());
         return FALSE;
      }

      if (connect(sock, addr, addrlen) == SOCKET_ERROR) {
         WSAPOLLFD pollFds[1];
         /* wait timeout seconds */
         unsigned int timeout = Preference_GetLong(3,
            "pref.wsa.socket.pair.connect.timeout.seconds") * 1000;
         int ret = WSAGetLastError();
         if (ret != WSAEWOULDBLOCK) {
            /* connection failed */
            Log("%s: Non-blocking socket %d could not connect to a local "
                "socket, error %d.\n", __FUNCTION__, sock, WSAGetLastError());
            return FALSE;
         }

         pollFds[0].fd = sock;
         pollFds[0].events = POLLWRNORM;
         pollFds[0].revents = 0;

         ret = WSAPoll(pollFds, 1, timeout);
         if (ret <= 0) {
            /* WSAPoll failed or connection timed out */
            if (ret == 0) {
                WSASetLastError(WSAETIMEDOUT);
            }
            Log("%s: Non-blocking socket %d connect to listener socket "
                "failed, error %d.\n", __FUNCTION__, sock, WSAGetLastError());
            return FALSE;
         }

         if ((pollFds[0].revents &
            (POLLWRNORM|POLLERR|POLLHUP)) != POLLWRNORM) {
            /* connection failed */
            int error = 0;
            int errLen = sizeof error;
            getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &errLen);
            WSASetLastError(error);
            Log("%s: Non-blocking socket %d connect to listener socket "
                "failed, error %d.\n", __FUNCTION__, sock, WSAGetLastError());
            return FALSE;
         }
      }
      /* connection successful */
      Log("%s: Non-blocking socket %d connected successfully with "
          "socket type %d.\n", __FUNCTION__, sock, addr->sa_family);
      unblock = 0;
      if (ioctlsocket(sock, FIONBIO, &unblock) == SOCKET_ERROR) {
         Log("%s: Non-blocking socket %d restored to blocking mode failed, "
             "error %d.\n", __FUNCTION__, sock, WSAGetLastError());
         return FALSE;
      }
   } else if (connect(sock, addr, addrlen) == SOCKET_ERROR) {
      if (blocking || WSAGetLastError() != WSAEWOULDBLOCK) {
         Log("%s: socket %d could not connect to a local socket, "
             "error %d.\n", __FUNCTION__, sock, WSAGetLastError());
         return FALSE;
      }
   } else {
      Log("%s: %s socket %d connected immediately!\n",
          __FUNCTION__, (blocking ? "Blocking" : "Non-blocking"), sock);
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollSocketClose --
 *
 *      Close the socket, and restore the original last error.
 *
 * Results:
 *      Socket is closed, original last error is restored.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static INLINE void
PollSocketClose(SOCKET sock) {  // IN: the socket is being closed
   int savedError = GetLastError();
   closesocket(sock);
   SetLastError(savedError);
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollSocketPairConnecting --
 *
 *      Given necessary information, like socket family type, communication
 *      type, socket address and socket type, this function initialize a socket
 *      pair and make them connect to each other.
 *
 * Results:
 *      Socket bound to a given address, and another connecting
 *      to that address.
 *      INVALID_SOCKET on error.  Use WSAGetLastError() for detail.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static SOCKET
PollSocketPairConnecting(sa_family_t sa_family,  // IN: socket family type
                         int socketCommType,     // IN: SOCK_STREAM or SOCK_DGRAM?
                         struct sockaddr *addr,  // IN: the address connected to
                         int addrlen,            // IN: length of struct sockaddr
                         Bool blocking,          // IN: blocking socket?
                         SOCKET *s,              // OUT: connected client socket
                         SocketSpecialOpts opts) // IN: socket special options
{
   SOCKET server = INVALID_SOCKET; // server (listening) socket

   SOCKET client = socket(sa_family, socketCommType, 0);
   if (client == INVALID_SOCKET) {
      Log("%s: Could not create client socket, socket family: %d.\n",
          __FUNCTION__, sa_family);
      goto outError;
   }

   server = socket(sa_family, socketCommType, 0);
   if (server == INVALID_SOCKET) {
      Log("%s: Could not create server socket, socket family: %d.\n",
          __FUNCTION__, sa_family);
      goto outError;
   }

   if (!PollSocketPairPrepare(blocking, client, server, addr, addrlen, socketCommType)) {
      Log("%s: Could not prepare socket pair for type %d, client %d, "
          "server %d.\n", __FUNCTION__, sa_family, client, server);
      goto outError;
   }

   if (!PollSocketPairConnect(blocking, addr, addrlen, client, opts)) {
      Log("%s: Could not connect socket pair for type %d, client %d, "
          "server %d.\n", __FUNCTION__, sa_family, client, server);
      goto outError;
   }

   *s = client;
   return server;

outError:
   if (server != INVALID_SOCKET) {
      PollSocketClose(server);
   }
   if (client != INVALID_SOCKET) {
      PollSocketClose(client);
   }

   *s = INVALID_SOCKET;
   return INVALID_SOCKET;
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollIPv4SocketPairStartConnecting --
 *
 *      As one of the PollXXXSocketPairStartConnecting family, this function
 *      creates an *IPv4* socket pair.
 *
 * Results:
 *      On success, returns a listening socket bound to a local address, and
 *      sets a passed-by-reference argument 's' to a client socket that has
 *      connected to the listening socket.  The client socket's blocking mode
 *      is as specified by the 'blocking' argument.
 *
 *      Returns INVALID_SOCKET on error.  Use WSAGetLastError() for detail.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static SOCKET
PollIPv4SocketPairStartConnecting(int socketCommType,     // IN: SOCK_STREAM or SOCK_DGRAM?
                                  Bool blocking,          // IN: blocking socket?
                                  SOCKET *s,              // OUT: connecting socket
                                  SocketSpecialOpts opts) // IN: socket special options
{
   struct sockaddr_in iaddr;
   int addrlen;

   addrlen = sizeof iaddr;
   memset(&iaddr, 0, addrlen);
   iaddr.sin_family = AF_INET;
   iaddr.sin_addr = in4addr_loopback;
   iaddr.sin_port = 0;

   return PollSocketPairConnecting(iaddr.sin_family, socketCommType,
                                   (struct sockaddr*) &iaddr, addrlen,
                                   blocking, s, opts);
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollIPv6SocketPairStartConnecting --
 *
 *      As one of the PollXXXSocketPairStartConnecting family, this function
 *      creates an *IPv6* socket pair.
 *
 * Results:
 *      On success, returns a listening socket bound to a local address, and
 *      sets a passed-by-reference argument 's' to a client socket that has
 *      connected to the listening socket.  The client socket's blocking mode
 *      is as specified by the 'blocking' argument.
 *
 *      Returns INVALID_SOCKET on error.  Use WSAGetLastError() for detail.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static SOCKET
PollIPv6SocketPairStartConnecting(int socketCommType,     // IN: SOCK_STREAM or SOCK_DGRAM?
                                  Bool blocking,          // IN: blocking socket?
                                  SOCKET *s,              // OUT: connecting socket
                                  SocketSpecialOpts opts) // IN: socket special options
{
   struct sockaddr_in6 iaddr6;
   int addrlen;

   addrlen = sizeof iaddr6;
   memset(&iaddr6, 0, addrlen);
   iaddr6.sin6_family = AF_INET6;
   iaddr6.sin6_addr = in6addr_loopback;
   iaddr6.sin6_port = 0;

   return PollSocketPairConnecting(iaddr6.sin6_family, socketCommType,
                                   (struct sockaddr*) &iaddr6, addrlen,
                                   blocking, s, opts);
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollVMCISocketPairStartConnecting --
 *
 *      As one of the PollXXXSocketPairStartConnecting family, this function
 *      creates a *VMCI* socket pair.
 *
 * Results:
 *      On success, returns a listening socket bound to a local address, and
 *      sets a passed-by-reference argument 's' to a client socket that has
 *      connected to the listening socket.  The client socket's blocking mode
 *      is as specified by the 'blocking' argument.
 *
 *      Returns INVALID_SOCKET on error.  Use WSAGetLastError() for detail.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static SOCKET
PollVMCISocketPairStartConnecting(int socketCommType,  // IN: SOCK_STREAM or SOCK_DGRAM?
                                  Bool blocking,       // IN: blocking socket?
                                  SOCKET *s)           // OUT: connecting socket
{
   struct sockaddr_vm vaddr;
   int addrlen;

   addrlen = sizeof vaddr;
   memset(&vaddr, 0, addrlen);
   vaddr.svm_family = VMCISock_GetAFValue();
   vaddr.svm_cid = VMADDR_CID_ANY;
   vaddr.svm_port = VMADDR_PORT_ANY;
   vaddr.svm_cid = VMCISock_GetLocalCID();

   return PollSocketPairConnecting(vaddr.svm_family, socketCommType,
                                   (struct sockaddr*) &vaddr, addrlen,
                                   blocking, s, 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollSocketPairStartConnecting --
 *
 *      Helper function that does most of the work of creating
 *      a socket pair.
 *
 * Results:
 *      Socket bound to a local address, and another connecting
 *      to that address.
 *      INVALID_SOCKET on error.  Use WSAGetLastError() for detail.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static SOCKET
PollSocketPairStartConnecting(Bool vmci,              // IN: vmci socket?
                              Bool stream,            // IN: stream socket?
                              Bool blocking,          // IN: blocking socket?
                              SOCKET *s,              // OUT: connecting socket
                              SocketSpecialOpts opts) // IN: socket special options
{
   SOCKET temp = INVALID_SOCKET;
   int socketCommType = stream ? SOCK_STREAM : SOCK_DGRAM;

   if (vmci) {
      temp = PollVMCISocketPairStartConnecting(socketCommType, blocking, s);
   } else {
      temp = PollIPv6SocketPairStartConnecting(socketCommType, blocking,
                                               s, opts);

      if (temp == INVALID_SOCKET) {
         temp = PollIPv4SocketPairStartConnecting(socketCommType, blocking,
                                                  s, opts);
      }
   }
   return temp;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Poll_SocketPair --
 *
 *      Emulate basic socketpair() using windows API.
 *
 * Results:
 *      Two sockets connected to each other.
 *
 * Side effects:
 *      A TCP/IP or VMCI connection is created.
 *
 *-----------------------------------------------------------------------------
 */

int
Poll_SocketPair(Bool vmci,              // IN: create vmci pair?
                Bool stream,            // IN: stream socket?
                int fds[2],             // OUT: 2 sockets connected to each other
                SocketSpecialOpts opts) // IN: socket special options
{
   SOCKET server = INVALID_SOCKET;

   fds[0] = INVALID_SOCKET;
   fds[1] = INVALID_SOCKET;

   server = PollSocketPairStartConnecting(vmci, stream, TRUE,
                                          (SOCKET *)&fds[0], opts);
   if (server == INVALID_SOCKET) {
      goto out;
   }
   if (stream) {
      fds[1] = accept(server, NULL, NULL);
      if (fds[1] == INVALID_SOCKET) {
         Log("%s: Could not accept on a server socket %d.\n",
             __FUNCTION__, server);
         goto out;
      }
      closesocket(server);
   } else {
      fds[1] = server;
   }
   return 0;

out:
   Warning("%s: Error creating a %s socket pair: %d/%s\n", __FUNCTION__,
           vmci ? "vmci" : "inet", WSAGetLastError(), Err_ErrString());
   closesocket(server);
   closesocket(fds[0]);
   closesocket(fds[1]);
   return SOCKET_ERROR;
}
#endif // _WIN32

//#define POLL_UNITTEST 1
//#define POLL_TESTLOCK 1
//#define POLL_TESTVMCI 1

#if POLL_UNITTEST // All the way to EOF

#if _WIN32
   #include <winsock2.h>
   #include <time.h>
   #include "err.h"
   #include "random.h"
#else
   #include <sys/socket.h>
   #include <unistd.h>
#endif
#include "vmci_sockets.h"
#if POLL_TESTLOCK
   #include "vthread.h"
   #include "util.h"
   #include "../../vmx/public/mutexRankVMX.h"
   #define GRAB_LOCK(_lock)             \
      if (_lock) {                      \
         MXUser_AcquireRecLock(_lock);  \
      }
   #define DROP_LOCK(_lock)             \
      if (_lock) {                      \
         MXUser_ReleaseRecLock(_lock);  \
      }
   #define NUM_TEST_ITERS 10
#else
   #define GRAB_LOCK(_lock)
   #define DROP_LOCK(_lock)
#endif

/*
 * Make this queue length a little bit less than poll implementation's max
 * to allow for some sockets in the test program itself.
 */

#define MAX_QUEUE_LENGTH 4090
#define MAX_VMX_QUEUE_LENGTH 450

static char reinstallPoll[1];
static char removePoll[1];
static unsigned int realTimeCount;
static unsigned int mainLoopCount;
static int fds[2];
static unsigned int deviceRCount;
static unsigned int deviceWCount;
static unsigned int state;
static unsigned int successCount;
static unsigned int failureCount;
static unsigned int dummyCount;
static Bool isVMX;
static Bool useLocking;
static Bool testVMCI;
static MXUserRecLock *cbLock;
static unsigned int lockErrors;
#if POLL_TESTLOCK
static volatile Bool exitThread;
#endif
static volatile Bool rtDeleted;
static volatile Bool mlDeleted;
static volatile Bool drDeleted;
static volatile Bool dwDeleted;
static unsigned int rtCbRace;
static unsigned int mlCbRace;
static unsigned int drCbRace;
static unsigned int dwCbRace;

typedef struct SocketPair {
   int fds[2];
   int count;
} SocketPair;
static SocketPair socketPairs[MAX_QUEUE_LENGTH];

#ifdef _WIN32
static HANDLE events[2];
static unsigned int deviceEv0Count;
static unsigned int deviceEv1Count;
static SOCKET boundSocket;
#endif


/*
 *----------------------------------------------------------------------------
 *
 * CheckLockState --
 *
 *      If testing with lock, verify that lock is held by the calling thread.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
CheckLockState(void)
{
   if (cbLock && !MXUser_IsCurThreadHoldingRecLock(cbLock)) {
      lockErrors++;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest_RealTime --
 *
 *      Real time test Poll callback.
 *
 *      Increment the callback's counter, and if 'clientData' is not NULL,
 *      self reschedule.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
PollUnitTest_RealTime(void *clientData) // IN
{
   realTimeCount++;
   CheckLockState();
   rtCbRace += (rtDeleted != FALSE);
   if (clientData == reinstallPoll) {
      Poll_Callback(POLL_CS_MAIN,
                    0,
                    PollUnitTest_RealTime,
                    clientData,
                    POLL_REALTIME,
                    0,
                    cbLock);
   } else if (clientData == removePoll) {
      Bool ret;

      ret = Poll_CallbackRemove(POLL_CS_MAIN, POLL_FLAG_PERIODIC,
                                PollUnitTest_RealTime, clientData,
                                POLL_REALTIME);
      ASSERT(ret);
   } else {
      ASSERT(clientData == NULL);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest_MainLoop --
 *
 *      Main loop test Poll callback.
 *
 *      Increment the callback's counter, and if 'clientData' is not NULL,
 *      self reschedule.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
PollUnitTest_MainLoop(void *clientData) // IN
{
   mainLoopCount++;
   CheckLockState();
   mlCbRace += (mlDeleted != FALSE);
   if (clientData == reinstallPoll) {
      Poll_Callback(POLL_CS_MAIN,
                    0,
                    PollUnitTest_MainLoop,
                    clientData,
                    POLL_MAIN_LOOP,
                    0,
                    cbLock);
   } else if (clientData == removePoll) {
      Bool ret;

      ret = Poll_CallbackRemove(POLL_CS_MAIN, POLL_FLAG_PERIODIC,
                                PollUnitTest_MainLoop, clientData, POLL_MAIN_LOOP);
      ASSERT(ret);
   } else {
      ASSERT(clientData == NULL);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest_DeviceR --
 *
 *      Device read test Poll callback.
 *
 *      Increment the callback's counter, and if 'clientData' is not NULL,
 *      self reschedule.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
PollUnitTest_DeviceR(void *clientData) // IN
{
#ifdef _WIN32
   /*
    * Windows won't tell us a socket is readable unless some kind of
    * read is performed.  Read, then re-fill the buffer.
    */

   char buf[sizeof fds];

   recv(fds[1], buf, sizeof fds, 0);
   send(fds[0], (const char *)fds, sizeof fds, 0);
   ASSERT(!memcmp(buf, fds, sizeof fds));
#endif
   deviceRCount++;

   CheckLockState();
   drCbRace += (drDeleted != FALSE);
   if (clientData == reinstallPoll) {
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_READ,
                    PollUnitTest_DeviceR,
                    clientData,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
   } else if (clientData == removePoll) {
      Bool ret;

      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceR, clientData, POLL_DEVICE);
      ASSERT(ret);
   } else {
      ASSERT(clientData == NULL);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest_DeviceW --
 *
 *      Device write test Poll callback.
 *
 *      Increment the callback's counter, and if 'clientData' is not NULL,
 *      self reschedule.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
PollUnitTest_DeviceW(void *clientData) // IN
{
   deviceWCount++;
   CheckLockState();
   dwCbRace += (dwDeleted != FALSE);
   if (clientData == reinstallPoll) {
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_WRITE,
                    PollUnitTest_DeviceW,
                    clientData,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
   } else if (clientData == removePoll) {
      Bool ret;

      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceW, clientData, POLL_DEVICE);
      ASSERT(ret);
   } else {
      ASSERT(clientData == NULL);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest_DeviceRQ --
 *
 *      Device read test Poll callback, the queue test version.
 *
 *      Increment the callback's counter, and self reschedule.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
PollUnitTest_DeviceRQ(void *clientData) // IN
{
   int queueIndex = (int)(intptr_t)clientData;
#ifdef _WIN32
   /*
    * Windows won't tell us a socket is readable unless some kind of
    * read is performed.  Read, then re-fill the buffer.
    */

   char buf[sizeof fds];

   recv(socketPairs[queueIndex].fds[1], buf, sizeof fds, 0);
   send(socketPairs[queueIndex].fds[0], (const char *)fds, sizeof fds, 0);
   ASSERT(!memcmp(buf, fds, sizeof fds));
#endif
   deviceRCount++;
   socketPairs[queueIndex].count++;

   CheckLockState();
   Poll_Callback(POLL_CS_MAIN,
                 POLL_FLAG_SOCKET | POLL_FLAG_READ,
                 PollUnitTest_DeviceRQ,
                 clientData,
                 POLL_DEVICE,
                 socketPairs[queueIndex].fds[1],
                 cbLock);
}


#if defined(POLL_TESTLOCK) && defined(_WIN32)
/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest_DeviceEvent --
 *
 *      Device read test Poll callback for Event.
 *
 *      Increment the callback's counter, and if 'clientData' is not NULL,
 *      self reschedule.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
PollUnitTest_DeviceEvent(void *clientData) // IN
{
   CheckLockState();
   if (clientData == NULL) {
      deviceEv0Count++;
      SetEvent(events[0]);
   } else {
      ASSERT(clientData == reinstallPoll);
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_READ,
                    PollUnitTest_DeviceEvent
                    clientData,
                    POLL_DEVICE,
                    (PollDevHandle)events[1],
                    cbLock);
      deviceEv1Count++;
      SetEvent(events[1]);
   }
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest_TestResult --
 *
 *      Log and count test result.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Outputs the result of the test with Warning().
 *
 *-----------------------------------------------------------------------------
 */

static void
PollUnitTest_TestResult(Bool success) // IN: TRUE on success, FALSE on failure
{
   if (success && lockErrors == 0) {
      successCount++;
      Warning("%s:   success\n", __FUNCTION__);
   } else {
      failureCount++;
      if (useLocking) {
         Warning("%s:   failure (lockErrors = %u)\n", __FUNCTION__, lockErrors);
      } else {
         Warning("%s:   failure\n", __FUNCTION__);
      }
   }
   lockErrors = 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * PollUnitTest_DummyCallback --
 *
 *      Used for tickling the poll loop periodically.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
PollUnitTest_DummyCallback(void *clientData) // IN: unused
{
   dummyCount++;
}


#if POLL_TESTLOCK
/*
 *----------------------------------------------------------------------------
 *
 * PollAddRemoveCBThread --
 *
 *      This thread continuously creates a lock, adds a poll callback that
 *      will take the lock, removes the callback, and destroys the lock.  It
 *      is designed to race against the thread running Poll_Loop().
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
PollAddRemoveCBThread(void *clientData)  // IN: unused
{
   Bool ret;
   uint32 numIters = 0;
   int periodicFlag[2] = { 0, POLL_FLAG_PERIODIC };
   void *cbData[2] = { reinstallPoll, NULL };
   uint32 prevCounts[4] = { realTimeCount, mainLoopCount, deviceRCount,
                            deviceWCount };
   uint32 perIterError[4] = { 0 };
   uint32 prevDummyCount;
   uint32 dummyDiff;
   uint32 oddIter;

   while (!exitThread) {
      oddIter = numIters++ & 0x1;

      /* Add and remove real time and main loop callbacks */
      cbLock = MXUser_CreateRecLock("pollUnitTestLock",
                                    RANK_pollUnitTestLock);
      rtDeleted = FALSE;
      Poll_Callback(POLL_CS_MAIN,
                    periodicFlag[oddIter],
                    PollUnitTest_RealTime,
                    cbData[oddIter],
                    POLL_REALTIME,
                    5000,
                    cbLock);
      mlDeleted = FALSE;
      Poll_Callback(POLL_CS_MAIN,
                    periodicFlag[1 - oddIter],
                    PollUnitTest_MainLoop,
                    cbData[1 - oddIter],
                    POLL_MAIN_LOOP,
                    0,
                    cbLock);
      prevDummyCount = dummyCount;
      Util_Usleep(15000);
      /* This tells us if poll thread gets to run during our sleep. */
      dummyDiff = dummyCount - prevDummyCount;
      MXUser_AcquireRecLock(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                periodicFlag[oddIter],
                                PollUnitTest_RealTime,
                                cbData[oddIter],
                                POLL_REALTIME);
      rtDeleted = TRUE;
      MXUser_ReleaseRecLock(cbLock);
      ASSERT(ret);
      MXUser_AcquireRecLock(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                periodicFlag[1 - oddIter],
                                PollUnitTest_MainLoop,
                                cbData[1 - oddIter],
                                POLL_MAIN_LOOP);
      mlDeleted = TRUE;
      MXUser_ReleaseRecLock(cbLock);
      ASSERT(ret);
      MXUser_DestroyRecLock(cbLock);
      if (exitThread) {
         break;
      }
      /* If dummyCallback fires multiple times, our callbacks should too. */
      perIterError[0] += (realTimeCount - prevCounts[0] < 2 && dummyDiff > 2);
      perIterError[1] += (mainLoopCount - prevCounts[1] < 2 && dummyDiff > 2);
      prevCounts[0] = realTimeCount;
      prevCounts[1] = mainLoopCount;

      /* Add and remove device callbacks */
      cbLock = MXUser_CreateRecLock("pollUnitTestLock",
                                    RANK_pollUnitTestLock);
      drDeleted = FALSE;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_READ | periodicFlag[oddIter],
                    PollUnitTest_DeviceR,
                    cbData[oddIter],
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      dwDeleted = FALSE;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_WRITE |
                    periodicFlag[1 - oddIter],
                    PollUnitTest_DeviceW,
                    cbData[1 - oddIter],
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      prevDummyCount = dummyCount;
      Util_Usleep(10000);
      dummyDiff = dummyCount - prevDummyCount;
      MXUser_AcquireRecLock(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_READ |
                                periodicFlag[oddIter],
                                PollUnitTest_DeviceR,
                                cbData[oddIter],
                                POLL_DEVICE);
      drDeleted = TRUE;
      MXUser_ReleaseRecLock(cbLock);
      ASSERT(ret);
      MXUser_AcquireRecLock(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE |
                                periodicFlag[1 - oddIter],
                                PollUnitTest_DeviceW,
                                cbData[1 - oddIter],
                                POLL_DEVICE);
      dwDeleted = TRUE;
      MXUser_ReleaseRecLock(cbLock);
      ASSERT(ret);
      MXUser_DestroyRecLock(cbLock);
      if (!exitThread) {
         perIterError[2] += (deviceRCount - prevCounts[2] < 2 && dummyDiff > 2);
         perIterError[3] += (deviceWCount - prevCounts[3] < 2 && dummyDiff > 2);
         prevCounts[2] = deviceRCount;
         prevCounts[3] = deviceWCount;
      }
   }
   cbLock = NULL;
   PollUnitTest_TestResult(perIterError[0] == 0 && perIterError[1] == 0 &&
                           perIterError[2] == 0 && perIterError[3] == 0);
}


#ifdef _WIN32
/*
 *----------------------------------------------------------------------------
 *
 * PollLockContentionThread --
 *
 *      Create lock contention on cbLock.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
PollLockContentionThread(void *clientData)  // IN: unused
{
   rqContext *rCtxt = Random_QuickSeed((uint32)time(NULL));

   while (!exitThread) {
      MXUser_AcquireRecLock(cbLock);
      Sleep(Random_Quick(rCtxt) % 5);
      MXUser_ReleaseRecLock(cbLock);
      Sleep(Random_Quick(rCtxt) % 5 + 10);
   }
}
#endif
#endif  // POLL_TESTLOCK


/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest_StateMachine --
 *
 *      State machine. The heart of Poll's unit test. Sequentially run all
 *      tests.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Outputs the result of the tests with Warning().
 *
 *-----------------------------------------------------------------------------
 */

static void
PollUnitTest_StateMachine(void *clientData) // IN: Unused
{
#if POLL_TESTLOCK
   static VThreadID cbRaceThread;
   static unsigned int raceTestIter;
#endif
   static unsigned int queueTestIter;
#ifdef _WIN32
   static unsigned int eventTestIter;
#endif
   static unsigned int maxInetSockets;
   static unsigned int maxVMCISockets;
   static unsigned int queueLen;
   Bool ret;
   int i, retval;
   int queueReads = 0;

#ifdef _WIN32
   maxVMCISockets = MAXIMUM_WAIT_OBJECTS - 2;
#else
   maxVMCISockets = 60;
#endif
   queueLen = isVMX ? MAX_VMX_QUEUE_LENGTH : MAX_QUEUE_LENGTH;

   switch (state) {
   case 0:
      Warning("%s: Poll unit test: start%s%s\n", __FUNCTION__,
              testVMCI ? " vmci tests" : "", useLocking ? " locking tests" : "");
      Warning("%s: Testing RealTime 0 0\n", __FUNCTION__);
      realTimeCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    0,
                    PollUnitTest_RealTime,
                    NULL,
                    POLL_REALTIME,
                    0,
                    cbLock);
      state++;
      break;

   case 1:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                0,
                                PollUnitTest_RealTime,
                                NULL,
                                POLL_REALTIME);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == FALSE && realTimeCount == 1);
      state++;
      break;

   case 2:
      Warning("%s: Testing RealTime 1 0\n", __FUNCTION__);
      realTimeCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_PERIODIC,
                    PollUnitTest_RealTime,
                    NULL,
                    POLL_REALTIME,
                    100000,
                    cbLock);
      state++;
      break;

   case 3:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_PERIODIC,
                                PollUnitTest_RealTime,
                                NULL,
                                POLL_REALTIME);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && realTimeCount > 1);
      state++;
      break;

   case 4:
      Warning("%s: Testing RealTime 0 1\n", __FUNCTION__);
      realTimeCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    0,
                    PollUnitTest_RealTime,
                    reinstallPoll,
                    POLL_REALTIME,
                    0,
                    cbLock);
      state++;
      break;

   case 5:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                0,
                                PollUnitTest_RealTime,
                                reinstallPoll,
                                POLL_REALTIME);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && realTimeCount > 1);
      state++;
      break;

   case 6:
      Warning("%s: Testing RealTime 1 1\n", __FUNCTION__);
      realTimeCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_PERIODIC,
                    PollUnitTest_RealTime,
                    removePoll,
                    POLL_REALTIME,
                    100000,
                    cbLock);
      state++;
      break;

   case 7:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_PERIODIC,
                                PollUnitTest_RealTime,
                                NULL,
                                POLL_REALTIME);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == FALSE && realTimeCount == 1);
      state++;
      break;

   case 8:
      Warning("%s: Testing MainLoop 0 0\n", __FUNCTION__);

      /*
       * A periodic real time callback ensures that we go over the main loop
       * queue more than once for the duration of each state.
       */
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_PERIODIC,
                    PollUnitTest_DummyCallback,
                    NULL,
                    POLL_REALTIME,
                    100000,
                    NULL);
      mainLoopCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    0,
                    PollUnitTest_MainLoop,
                    NULL,
                    POLL_MAIN_LOOP,
                    0,
                    cbLock);
      state++;
      break;

   case 9:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                0,
                                PollUnitTest_MainLoop,
                                NULL,
                                POLL_MAIN_LOOP);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == FALSE && mainLoopCount == 1);
      state++;
      break;

   case 10:
      Warning("%s: Testing MainLoop 1 0\n", __FUNCTION__);
      mainLoopCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_PERIODIC,
                    PollUnitTest_MainLoop,
                    NULL,
                    POLL_MAIN_LOOP,
                    0,
                    cbLock);
      state++;
      break;

   case 11:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_PERIODIC,
                                PollUnitTest_MainLoop,
                                NULL,
                                POLL_MAIN_LOOP);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && mainLoopCount > 1);
      state++;
      break;

   case 12:
      Warning("%s: Testing MainLoop 0 1\n", __FUNCTION__);
      mainLoopCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    0,
                    PollUnitTest_MainLoop,
                    reinstallPoll,
                    POLL_MAIN_LOOP,
                    0,
                    cbLock);
      state++;
      break;

   case 13:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                0,
                                PollUnitTest_MainLoop,
                                reinstallPoll,
                                POLL_MAIN_LOOP);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && mainLoopCount > 1);
      state++;
      break;

   case 14:
      Warning("%s: Testing MainLoop 1 1\n", __FUNCTION__);
      mainLoopCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_PERIODIC,
                    PollUnitTest_MainLoop,
                    removePoll,
                    POLL_MAIN_LOOP,
                    0,
                    cbLock);
      state++;
      break;

   case 15:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_PERIODIC,
                                PollUnitTest_MainLoop,
                                removePoll,
                                POLL_MAIN_LOOP);
      DROP_LOCK(cbLock);
      Poll_CallbackRemove(POLL_CS_MAIN,
                          POLL_FLAG_PERIODIC,
                          PollUnitTest_DummyCallback,
                          NULL,
                          POLL_REALTIME);
      PollUnitTest_TestResult(ret == FALSE && mainLoopCount == 1);
      state++;
      break;

   case 16:
      Warning("%s: Testing DeviceR 0 0\n", __FUNCTION__);
      deviceRCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_READ,
                    PollUnitTest_DeviceR,
                    NULL,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 17:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_READ,
                                PollUnitTest_DeviceR,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == FALSE && deviceRCount == 1);
      state++;
      break;

   case 18:
      Warning("%s: Testing DeviceR 1 0\n", __FUNCTION__);
      deviceRCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceR,
                    NULL,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 19:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceR,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceRCount > 1);
      state++;
      break;

   case 20:
      Warning("%s: Testing DeviceR 0 1\n", __FUNCTION__);
      deviceRCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_READ,
                    PollUnitTest_DeviceR,
                    reinstallPoll,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 21:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_READ,
                                PollUnitTest_DeviceR,
                                reinstallPoll,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceRCount > 1);
      state++;
      break;

   case 22:
      Warning("%s: Testing DeviceR 1 1\n", __FUNCTION__);
      deviceRCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceR,
                    removePoll,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 23:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceR,
                                removePoll,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == FALSE && deviceRCount == 1);
      state++;
      break;

   case 24:
      Warning("%s: Testing DeviceW 0 0\n", __FUNCTION__);
      deviceWCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_WRITE,
                    PollUnitTest_DeviceW,
                    NULL,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 25:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE,
                                PollUnitTest_DeviceW,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == FALSE && deviceWCount == 1);
      state++;
      break;

   case 26:
      Warning("%s: Testing DeviceW 1 0\n", __FUNCTION__);
      deviceWCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceW,
                    NULL,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 27:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceW,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceWCount > 1);
      state++;
      break;

   case 28:
      Warning("%s: Testing DeviceW 0 1\n", __FUNCTION__);
      deviceWCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_WRITE,
                    PollUnitTest_DeviceW,
                    reinstallPoll,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 29:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE,
                                PollUnitTest_DeviceW,
                                reinstallPoll,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceWCount > 1);
      state++;
      break;

   case 30:
      Warning("%s: Testing DeviceW 1 1\n", __FUNCTION__);
      deviceWCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceW,
                    removePoll,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 31:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceW,
                                removePoll,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == FALSE && deviceWCount == 1);
      state++;
      break;

   case 32:
      Warning("%s: Testing Device add R, add W, remove R, remove W\n",
              __FUNCTION__);
      deviceRCount = 0;
      deviceWCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceR,
                    NULL,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceW,
                    NULL,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 33:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceR,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceRCount > 1 && deviceWCount > 1);
      deviceRCount = 0;
      deviceWCount = 0;
      state++;
      break;

   case 34:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceW,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceRCount == 0 && deviceWCount > 1);
      state++;
      break;

   case 35:
      Warning("%s: Testing Device add R, add W, remove W, remove R\n",
              __FUNCTION__);
      deviceRCount = 0;
      deviceWCount = 0;
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceR,
                    NULL,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceW,
                    NULL,
                    POLL_DEVICE,
                    fds[1],
                    cbLock);
      state++;
      break;

   case 36:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceW,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceRCount > 1 && deviceWCount > 1);
      deviceRCount = 0;
      deviceWCount = 0;
      state++;
      break;

   case 37:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceR,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceWCount == 0 && deviceRCount > 1);
      state++;
#ifndef _WIN32
      // The next test only makes sense on Windows.
      state += 3;
#endif
      break;

#ifdef _WIN32
   case 38:
      Warning("%s: Testing connecting socket\n", __FUNCTION__);
      closesocket(fds[0]);
      closesocket(fds[1]);
      fds[0] = INVALID_SOCKET;
      fds[1] = INVALID_SOCKET;
      deviceWCount = 0;
      boundSocket = PollSocketPairStartConnecting(testVMCI, TRUE, FALSE,
                                                  (SOCKET *)&fds[0]);
      if (boundSocket == INVALID_SOCKET) {
         Warning("%s:   failure -- error creating socket pair\n", __FUNCTION__);
         state += 3;
      } else {
         Poll_Callback(POLL_CS_MAIN,
                       POLL_FLAG_SOCKET | POLL_FLAG_WRITE,
                       PollUnitTest_DeviceW,
                       NULL,
                       POLL_DEVICE,
                       fds[0],
                       cbLock);
         state++;
      }
      break;

   case 39:
      fds[1] = accept(boundSocket, NULL, NULL);
      if (fds[1] == INVALID_SOCKET) {
         Warning("%s: Error accepting socket %d: %d/%s\n", __FUNCTION__,
                 boundSocket, WSAGetLastError(), Err_ErrString());
      }
      state++;
      break;

   case 40:
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_SOCKET | POLL_FLAG_WRITE,
                                PollUnitTest_DeviceW,
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == FALSE && deviceWCount >= 1);
      state++;
      break;
#endif

   case 41:
      maxInetSockets = testVMCI ? queueLen - maxVMCISockets :
                                  queueLen;
      Warning("%s: Testing queue size %d\n", __FUNCTION__, queueLen);
      deviceRCount = 0;
      queueTestIter = 0;
      queueReads = 0;
      for (i = 0; i < queueLen; i++) {
         Bool useVMCI = i >= maxInetSockets;
#ifdef _WIN32
         socketPairs[i].fds[0] = INVALID_SOCKET;
         socketPairs[i].fds[1] = INVALID_SOCKET;
         if (Poll_SocketPair(useVMCI, TRUE, socketPairs[i].fds, 0) < 0) {
            Warning("%s:   failure -- error creating socketpair, iteration %d\n",
                    __FUNCTION__, i);
            break;
         }
         retval = send(socketPairs[i].fds[0], (const char *)fds, sizeof fds, 0);
#else
         int addrFamily = useVMCI ? VMCISock_GetAFValue(): AF_UNIX;
         socketPairs[i].fds[0] = -1;
         socketPairs[i].fds[1] = -1;
         if (socketpair(addrFamily, SOCK_STREAM, 0, socketPairs[i].fds) < 0) {
            Warning("%s:   failure -- error creating socketpair, iteration %d\n",
                    __FUNCTION__, i);
            break;
         }
         retval = write(socketPairs[i].fds[0], fds, 1);
#endif
         socketPairs[i].count = 0;
         Poll_Callback(POLL_CS_MAIN,
                       POLL_FLAG_SOCKET | POLL_FLAG_READ,
                       PollUnitTest_DeviceRQ,
                       (void *)(intptr_t)i,
                       POLL_DEVICE,
                       socketPairs[i].fds[1],
                       cbLock);
      }
      state++;
      break;

   case 42:
      if (++queueTestIter >= queueLen / 1000) {
         state++;
      }
      break;

   case 43:
      Warning("%s:   %d reads completed\n", __FUNCTION__, deviceRCount);
      for (i = 0; i < queueLen; i++) {
         if (socketPairs[i].count) {
            queueReads++;
         }
         GRAB_LOCK(cbLock);
         Poll_CallbackRemove(POLL_CS_MAIN,
                             POLL_FLAG_SOCKET | POLL_FLAG_READ,
                             PollUnitTest_DeviceRQ,
                             (void *)(intptr_t)i,
                             POLL_DEVICE);
         DROP_LOCK(cbLock);
#ifdef _WIN32
         closesocket(socketPairs[i].fds[0]);
         closesocket(socketPairs[i].fds[1]);
#else
         close(socketPairs[i].fds[0]);
         close(socketPairs[i].fds[1]);
#endif
      }
      Warning("%s:   read %d sockets at least once.\n", __FUNCTION__, queueReads);
      PollUnitTest_TestResult(deviceRCount > queueLen);
      state++;
      break;

   case 44:
#if POLL_TESTVMCI

      /*
       * The following tests only work inside the guest,
       * as stream VSockets are unsuported for host<->host communication.
       */

      if (!useLocking && !testVMCI) {
         testVMCI = TRUE;
   #ifdef _WIN32
         /* Discard sockets used in connect test and re-create them. */
         closesocket(fds[0]);
         closesocket(fds[1]);
         fds[0] = INVALID_SOCKET;
         fds[1] = INVALID_SOCKET;
         if (Poll_SocketPair(TRUE, TRUE, fds, 0) < 0) {
            Warning("%s:   failure -- error creating vmci socketpair\n",
                    __FUNCTION__);
            state ++;
            break;
         }
         retval = send(fds[0], (const char *)fds, sizeof fds, 0);
   #else
         close(fds[0]);
         close(fds[1]);
         fds[0] = -1;
         fds[1] = -1;
         if (socketpair(VMCISock_GetAFValue(), SOCK_STREAM, 0, fds) < 0) {
            Warning("%s:   failure -- error creating vsock socketpair\n",
                    __FUNCTION__);
            break;
         }
         retval = write(fds[0], fds, 0);
   #endif
         state = 0;
         break;
      } else {
         testVMCI = FALSE;
         state++;
         // fall through to next test
      }
#else
      state++;
#endif

   case 45:
#if POLL_TESTLOCK
      if (useLocking == FALSE) {
         useLocking = TRUE;
         cbLock = MXUser_CreateRecLock("pollUnitTestLock",
                                       RANK_pollUnitTestLock);

   #ifdef _WIN32
         /* Discard sockets used in connect test and re-create them. */
         closesocket(fds[0]);
         closesocket(fds[1]);
         fds[0] = INVALID_SOCKET;
         fds[1] = INVALID_SOCKET;
         if (Poll_SocketPair(FALSE, TRUE, fds, 0) < 0) {
            Warning("%s:   failure -- error creating socketpair\n",
                    __FUNCTION__);
            state += 3;
            break;
         }
         retval = send(fds[0], (const char *)fds, sizeof fds, 0);
   #endif
         state = 0;
         break;
      } else {
         state++;
         // fall through to next test
      }

   case 46:

      /*
       * This test requires a poll implementation that supports locking
       * for both the internal poll state and the callbacks.  It also uses
       * VThread.
       */
      Warning("%s: Testing add/remove callback and Poll_Loop race (about %u s)\n",
              __FUNCTION__, NUM_TEST_ITERS);

   #ifdef _WIN32
      closesocket(fds[0]);
      closesocket(fds[1]);
      fds[0] = INVALID_SOCKET;
      fds[1] = INVALID_SOCKET;
      if (Poll_SocketPair(FALSE, TRUE, fds, 0) < 0)
   #else
      close(fds[0]);
      close(fds[1]);
      fds[0] = -1;
      fds[1] = -1;
      if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
   #endif
      {
         Warning("%s:   failure -- error creating socketpair\n", __FUNCTION__);
         state += 3;
         break;
      }

      /* Make fds[1] both readable and writable. */
   #ifdef _WIN32
      retval = send(fds[0], (const char *)fds, sizeof fds, 0);
   #else
      retval = write(fds[0], fds, 1);
   #endif

      MXUser_DestroyRecLock(cbLock);
      cbLock = NULL;
      rtCbRace = 0;
      mlCbRace = 0;
      drCbRace = 0;
      dwCbRace = 0;
      realTimeCount = 0;
      mainLoopCount = 0;
      deviceRCount = 0;
      deviceWCount = 0;
      raceTestIter = 0;
      dummyCount = 0;
      exitThread = FALSE;
      VThread_CreateThread(PollAddRemoveCBThread, NULL,
                           "PollAddRemoveCBThread", &cbRaceThread);
      if (cbRaceThread == VTHREAD_INVALID_ID) {
         Warning("%s:   failure -- error creating thread\n", __FUNCTION__);
         state += 3;
         break;
      }
      Poll_Callback(POLL_CS_MAIN, POLL_FLAG_PERIODIC,
                    PollUnitTest_DummyCallback, NULL,
                    POLL_REALTIME, 5000, NULL);
      state++;
      break;

   case 47:
      if (++raceTestIter == NUM_TEST_ITERS) {
         state++;
      }
      break;

   case 48:
      Poll_CallbackRemove(POLL_CS_MAIN, POLL_FLAG_PERIODIC,
                          PollUnitTest_DummyCallback,
                          NULL, POLL_REALTIME);
      exitThread = TRUE;
      VThread_DestroyThread(cbRaceThread);
      PollUnitTest_TestResult(rtCbRace == 0 && mlCbRace == 0 && drCbRace == 0 &&
                              dwCbRace == 0);
      state++;
   #ifndef _WIN32
      // The next test only makes sense on Windows.
      state += 3;
   #endif
      break;

   #ifdef _WIN32
   case 49:
      Warning("%s: Testing event-based device callbacks with lock contention\n",
              __FUNCTION__);
      if (!cbLock) {
         /* The previous test may have destroyed the lock. */
         cbLock = MXUser_CreateRecLock("pollUnitTestLock",
                                       RANK_pollUnitTestLock);
      }
      deviceEv0Count = 0;
      deviceEv1Count = 0;
      eventTestIter = 0;
      exitThread = FALSE;
      VThread_CreateThread(PollLockContentionThread, NULL,
                           "PollLockContention", &cbRaceThread);
      if (cbRaceThread == VTHREAD_INVALID_ID) {
         Warning("%s:   failure -- error creating thread\n", __FUNCTION__);
         state += 3;
         break;
      }
      events[0] = CreateEvent(NULL, FALSE, TRUE, NULL);
      VERIFY(events[0]);
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                    PollUnitTest_DeviceEvent
                    NULL,
                    POLL_DEVICE,
                    (PollDevHandle)events[0],
                    cbLock);
      events[1] = CreateEvent(NULL, FALSE, TRUE, NULL);
      VERIFY(events[1]);
      Poll_Callback(POLL_CS_MAIN,
                    POLL_FLAG_READ,
                    PollUnitTest_DeviceEvent
                    reinstallPoll,
                    POLL_DEVICE,
                    (PollDevHandle)events[1],
                    cbLock);
      state++;
      break;

   case 50:
      if (++eventTestIter == 2) {
         state++;
      }
      break;

   case 51:
      exitThread = TRUE;
      VThread_DestroyThread(cbRaceThread);
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_READ | POLL_FLAG_PERIODIC,
                                PollUnitTest_DeviceEvent
                                NULL,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceEv0Count > 1);
      GRAB_LOCK(cbLock);
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_READ,
                                PollUnitTest_DeviceEvent
                                reinstallPoll,
                                POLL_DEVICE);
      DROP_LOCK(cbLock);
      PollUnitTest_TestResult(ret == TRUE && deviceEv1Count > 1);
      CloseHandle(events[0]);
      CloseHandle(events[1]);
      state++;
      break;
   #endif  // _WIN32
#endif  // POLL_TESTLOCK (#else fall through)

   case 52:
      ret = Poll_CallbackRemove(POLL_CS_MAIN,
                                POLL_FLAG_PERIODIC,
                                PollUnitTest_StateMachine,
                                NULL,
                                POLL_REALTIME);
      ASSERT(ret);
      Warning("%s: Poll unit test: stop, %u successes, %u failures\n",
              __FUNCTION__, successCount, failureCount);
      if (cbLock) {
         MXUser_DestroyRecLock(cbLock);
      }
#ifdef _WIN32
      closesocket(fds[0]);
      closesocket(fds[1]);
      WSACleanup();
#else
      close(fds[0]);
      close(fds[1]);
#endif
      break;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollUnitTest --
 *
 *      Start the unit test suite for an implementation of the Poll_* API. It
 *      will stop by itself.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Outputs the result of the tests with Warning().
 *
 *-----------------------------------------------------------------------------
 */

void
PollUnitTest(Bool vmx)  // IN: use vmx-size poll queue
{
#ifdef _WIN32
   WSADATA wsaData;
   WORD versionRequested = MAKEWORD(2, 0);
   int ret;
#endif
   int retval;

   state = 0;
   successCount = failureCount = 0;
   useLocking = FALSE;
   isVMX = vmx;
#ifdef _WIN32
   ret = WSAStartup(versionRequested, &wsaData);
   if (ret != 0) {
      Warning("%s: Error in WSAStartup: %d\n", __FUNCTION__, ret);
      return;
   }
   fds[0] = INVALID_SOCKET;
   fds[1] = INVALID_SOCKET;
   if (Poll_SocketPair(FALSE, TRUE, fds, 0) < 0) {
#else
   fds[0] = -1;
   fds[1] = -1;
   if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
#endif
      Warning("%s: socketpair failed\n", __FUNCTION__);
      return;
   }

   // Make fds[1] both readable and writable.
#ifdef _WIN32
   retval = send(fds[0], (const char *)fds, sizeof fds, 0);
#else
   retval = write(fds[0], fds, 1);
#endif
   Warning("%s: Starting\n", __FUNCTION__);
   Poll_Callback(POLL_CS_MAIN,
                 POLL_FLAG_PERIODIC,
                 PollUnitTest_StateMachine,
                 NULL,
                 POLL_REALTIME,
                 1000000 /* 1 s. */,
                 NULL);
}


#endif // POLL_UNITTEST
 070701000001BA000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/pollGtk    070701000001BB000081A40000000000000000000000016822550500000408000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/pollGtk/Makefile.am    ################################################################################
### Copyright (C) 2013-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libPollGtk.la

libPollGtk_la_SOURCES =
libPollGtk_la_SOURCES += pollGtk.c

AM_CFLAGS =
AM_CFLAGS += @GLIB2_CPPFLAGS@
070701000001BC000081A4000000000000000000000001682255050000AD4B000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/pollGtk/pollGtk.c  /*********************************************************
 * Copyright (c) 2004-2019,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * pollGtk.c -- a simple poll implementation built on top of GLib
 * For historical reasons, it is named pollGtk but does not
 * depend on gtk.
 * This is the actual Poll_* functions, and so it is different
 * than the Gtk IVmdbPoll implementation.
 *
 * This has to be at least slightly thread-safe. Specifically,
 * it has to allow any thread to schedule callbacks on the Poll
 * thread. For example, the asyncSocket library may schedule a
 * callback in a signal handler when a socket is suddenly
 * disconnected. As a result, we need to wrap a lock around the
 * queue of events.
 */


#ifdef _WIN32
#include <winsock2.h>
#pragma pack(push, 8)
#endif
#include <glib.h>
#ifdef _WIN32
#pragma pack(pop)
#endif

#include "pollImpl.h"
#include "mutexRankLib.h"
#include "err.h"

#define LOGLEVEL_MODULE poll
#include "loglevel_user.h"


/*
 * This describes a single callback waiting for an event
 * or a timeout.
 */
typedef struct {
   int            flags;
   PollerFunction cb;
   void          *clientData;
   PollClassSet   classSet;
   MXUserRecLock *cbLock;
   uint32         timesNotFired;
} PollEntryInfo;

typedef struct PollGtkEntry {
   PollEntryInfo  read;
   PollEntryInfo  write;

   PollEventType  type;
   PollDevHandle  event;       /* POLL_DEVICE file descriptor or POLL_REALTIME
                                  delay. */
   guint          gtkInputId;  /* Handle of the registered GTK callback  */
   /*
    * In practice, "channel" is only used when invoking the callbacks of clients
    * who registered with POLL_FLAG_FD.  When you create a channel from a file
    * descriptor with g_io_channel_win32_new_fd(), you MUST read from the
    * channel with g_io_channel_read() instead of reading directly from the fd
    * with read(fd).
    *
    * See http://library.gnome.org/devel/glib/unstable/glib-IO-Channels.html#g-io-channel-win32-new-fd for details.
    */
   GIOChannel *channel;
} PollGtkEntry;


/*
 * This describes a data necessary to find matching entry.
 */
typedef struct {
   int            flags;
   PollerFunction cb;
   void          *clientData;
   PollClassSet   classSet;
   PollEventType  type;
   Bool           matchAnyClientData;
} PollGtkFindEntryData;


/*
 * The global Poll state.
 */
typedef struct Poll {
   MXUserExclLock *lock;

   GHashTable     *deviceTable;
   GHashTable     *timerTable;
#ifdef _WIN32
   GHashTable     *signaledTable;
   GSList         *newSignaled;
   Bool            signaledInUse;
   guint           retrySource;
#endif
} Poll;

static Poll *pollState;
static gsize inited = 0;

static VMwareStatus
PollGtkCallback(PollClassSet classSet,   // IN
                int flags,               // IN
                PollerFunction f,        // IN
                void *clientData,        // IN
                PollEventType type,      // IN
                PollDevHandle info,      // IN
                MXUserRecLock *lock);    // IN

static gboolean PollGtkBasicCallback(gpointer data);

static gboolean PollGtkEventCallback(GIOChannel *source,
                                     GIOCondition condition,
                                     gpointer data);
static gboolean PollGtkEventCallbackWork(GIOChannel *source,
                                         GIOCondition condition,
                                         gpointer data,
                                         Bool hasPollLock,
                                         Bool *firedAll);
#ifdef _WIN32
static gboolean PollGtkFireSignaled(gpointer key,
                                    gpointer value,
                                    gpointer user_data);
static gboolean PollGtkFireSignaledList(gpointer data);
#endif

static void PollGtkRemoveOneCallback(gpointer data);

#define ASSERT_POLL_LOCKED()                                    \
   ASSERT(!pollState || !pollState->lock ||                     \
          MXUser_IsCurThreadHoldingExclLock(pollState->lock))

#define LOG_ENTRY(_l, _str, _e, _isWrite)                                     \
   do {                                                                       \
      if (_isWrite) {                                                         \
         LOG(_l, "POLL: entry %p (wcb %p, data %p, flags %x, type %x)" _str,  \
             (_e), (_e)->write.cb, (_e)->write.clientData,                    \
             (_e)->write.flags, (_e)->type);                                  \
      } else {                                                                \
         LOG(_l, "POLL: entry %p (rcb %p, data %p, flags %x, type %x)" _str,  \
             (_e), (_e)->read.cb, (_e)->read.clientData,                      \
             (_e)->read.flags, (_e)->type);                                   \
      }                                                                       \
   } while (0)

typedef void (*PollerFunctionGtk)(void *, GIOChannel *);


/*
 *----------------------------------------------------------------------------
 *
 * PollGtkLock --
 * PollGtkUnlock --
 *
 *      Locking of the internal poll state.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
PollGtkLock(void)
{
   MXUser_AcquireExclLock(pollState->lock);
}


static INLINE void
PollGtkUnlock(void)
{
   MXUser_ReleaseExclLock(pollState->lock);
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkInit --
 *
 *      Module initialization.
 *
 * Results:
 *       None
 *
 * Side effects:
 *       Initializes the module-wide state and sets pollState.
 *
 *----------------------------------------------------------------------
 */

static void
PollGtkInit(void)
{
   ASSERT(pollState == NULL);
   pollState = g_new0(Poll, 1);

   pollState->lock = MXUser_CreateExclLock("pollGtkLock",
                                           RANK_pollDefaultLock);

   pollState->deviceTable = g_hash_table_new_full(g_direct_hash,
                                                  g_direct_equal,
                                                  NULL,
                                                  PollGtkRemoveOneCallback);
   ASSERT(pollState->deviceTable);

   pollState->timerTable = g_hash_table_new_full(g_direct_hash,
                                                 g_direct_equal,
                                                 NULL,
                                                 PollGtkRemoveOneCallback);
   ASSERT(pollState->timerTable);

#ifdef _WIN32
   pollState->signaledTable = g_hash_table_new(g_direct_hash,
                                               g_direct_equal);
   ASSERT(pollState->signaledTable);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkExit --
 *
 *      Module exit.
 *
 * Results:
 *       None
 *
 * Side effects:
 *       Discards the module-wide state and clears pollState.
 *
 *----------------------------------------------------------------------
 */

static void
PollGtkExit(void)
{
   Poll *poll = pollState;

   ASSERT(poll != NULL);

   PollGtkLock();
   g_hash_table_destroy(poll->deviceTable);
   g_hash_table_destroy(poll->timerTable);
   poll->deviceTable = NULL;
   poll->timerTable = NULL;
#ifdef _WIN32
   g_hash_table_destroy(poll->signaledTable);
   poll->signaledTable = NULL;
   g_slist_free(poll->newSignaled);
   poll->newSignaled = NULL;
   if (poll->retrySource > 0) {
      g_source_remove(poll->retrySource);
      poll->retrySource = 0;
   }
#endif
   PollGtkUnlock();

   MXUser_DestroyExclLock(poll->lock);

   g_free(poll);
   pollState = NULL;
   inited = 0;
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkLoopTimeout --
 *
 *       The poll loop.
 *       This is defined here to allow libraries like Foundry to link.
 *       When run with the Gtk Poll implementation, however, this routine
 *       should never be called. The Gtk framework will pump events.
 *
 * Result:
 *       Void.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

static void
PollGtkLoopTimeout(Bool loop,          // IN: loop forever if TRUE, else do one pass.
                   Bool *exit,         // IN: NULL or set to TRUE to end loop.
                   PollClass class,    // IN: class of events (POLL_CLASS_*)
                   int timeout)        // IN: maximum time to sleep
{
   NOT_IMPLEMENTED();
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkEntryInfoMatches --
 *
 *      Test whether provided PollEntryInfo satisfies FindEntryData
 *      requirements.
 *
 * Results:
 *      TRUE if the value matches our search criteria, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE gboolean
PollGtkEntryInfoMatches(const PollEntryInfo        *entry,  // IN
                        const PollGtkFindEntryData *search) // IN
{
   return PollClassSet_Equals(entry->classSet, search->classSet) &&
          entry->cb == search->cb && entry->flags == search->flags &&
          (search->matchAnyClientData || entry->clientData == search->clientData);
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkFindReadPredicate --
 *
 *      Predicate usable by GHashTable iteration functions to find
 *      specific elements, looking for read entry.
 *
 * Results:
 *      TRUE if the value matches our search criteria, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static gboolean
PollGtkFindReadPredicate(gpointer key,   // IN
                         gpointer value, // IN
                         gpointer data)  // IN
{
   const PollGtkEntry *current = value;
   const PollGtkFindEntryData *search = data;

   ASSERT_POLL_LOCKED();
   return current->type == search->type &&
          PollGtkEntryInfoMatches(&current->read, search);
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkFindWritePredicate --
 *
 *      Predicate usable by GHashTable iteration functions to find
 *      specific elements, looking for write entry.
 *
 * Results:
 *      TRUE if the value matches our search criteria, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static gboolean
PollGtkFindWritePredicate(gpointer key,   // IN
                          gpointer value, // IN
                          gpointer data)  // IN
{
   const PollGtkEntry *current = value;
   const PollGtkFindEntryData *search = data;

   ASSERT_POLL_LOCKED();
   return current->type == search->type &&
          PollGtkEntryInfoMatches(&current->write, search);
}


#ifdef _WIN32
/*
 *----------------------------------------------------------------------------
 *
 * PollGtkFireSignaled --
 *
 *      Fire the callback(s) of a previously signaled event, if the callback(s)
 *      is still registered.
 *
 * Results:
 *      TRUE if we fire all callbacks that are ready to fire.
 *
 * Side effects:
 *      If returning TRUE, the associated key is removed from signaledTable.
 *
 *----------------------------------------------------------------------------
 */

static gboolean
PollGtkFireSignaled(gpointer key,        // IN: PollDevHandle
                    gpointer value,      // IN: PollGtkEntry *
                    gpointer user_data)  // IN: unused
{
   PollGtkEntry *entry;
   Bool firedAll = TRUE;
   GIOCondition condition = 0;

   entry = g_hash_table_lookup(pollState->deviceTable, key);

   if (entry == NULL) {
      goto exit;
   }
   if (entry->read.cb && entry->read.timesNotFired > 0) {
      condition |= G_IO_IN;
   }
   if (entry->write.cb && entry->write.timesNotFired > 0) {
      condition |= G_IO_OUT;
   }
   if (condition == 0) {
      goto exit;
   }

   PollGtkEventCallbackWork(NULL, condition, entry, TRUE, &firedAll);

exit:
   LOG(4, "POLL: entry %p %s\n", entry, entry && condition ?
       (firedAll ? "fired" : "not all fired") : "not ready to fire");
   return firedAll;
}


/*
 *----------------------------------------------------------------------------
 *
 * PollGtkFireSignaledList --
 *
 *      This is a timer based callback scheduled when the signaled list is not
 *      empty.  For each entry, we will try to fire any callback with non-zero
 *      timesNotFired.  The entry is removed from the signaled list only if
 *      all the callbacks that we try to fire do fire.
 *
 * Results:
 *      TRUE if this callback should fire again, FALSE otherwise.
 *
 * Side effects:
 *      Timer source is removed if FALSE is returned.
 *
 *----------------------------------------------------------------------------
 */

static gboolean
PollGtkFireSignaledList(gpointer data) // IN: unused
{
   Poll *poll = pollState;
   gboolean ret;
   GSList *cur;

   ASSERT(poll);
   PollGtkLock();

   /*
    * Do not allow other changes to signaledTable while iterating through the
    * hash table.  (The poll lock is dropped when callback fires.)
    */
   poll->signaledInUse = TRUE;

   g_hash_table_foreach_remove(pollState->signaledTable,
                               PollGtkFireSignaled, NULL);

   /* Now we can add any new signaled entry into the hash table. */
   for (cur = poll->newSignaled; cur; cur = g_slist_next(cur)) {
      PollGtkEntry *entry;
      gpointer key = cur->data;

      entry = g_hash_table_lookup(poll->deviceTable, key);
      if (entry) {
         g_hash_table_replace(poll->signaledTable, key, entry);
      }
   }
   g_slist_free(poll->newSignaled);
   poll->newSignaled = NULL;

   /* Return TRUE to keep this function firing, FALSE to unregister. */
   if (g_hash_table_size(poll->signaledTable) > 0) {
      LOG(5, "POLL: not removing retry source\n");
      ret = TRUE;
   } else {
      LOG(5, "POLL: no retry remains; removing timer source\n");
      poll->retrySource = 0;
      ret = FALSE;
   }

   poll->signaledInUse = FALSE;
   PollGtkUnlock();

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * PollGtkAddToSignaledList --
 *
 *      Remember a signaled event so that we can retry later.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
PollGtkAddToSignaledList(PollGtkEntry *entry)  // IN
{
   Poll *poll = pollState;
   gpointer key;

   ASSERT_POLL_LOCKED();
   ASSERT(entry);

   key = (gpointer)(intptr_t)entry->event;

   /*
    * Add it to a separate linked list if the poll thread is iterating over
    * signaledTable.
    */
   if (poll->signaledInUse) {
      if (!g_slist_find(poll->newSignaled, key)) {
         poll->newSignaled = g_slist_prepend(poll->newSignaled, key);
         LOG(4, "POLL: added entry %p event 0x%x to signaled list\n",
             entry, entry->event);
      }
   } else {
      g_hash_table_replace(poll->signaledTable, key, entry);
      if (poll->retrySource == 0) {
         poll->retrySource = g_timeout_add(0, PollGtkFireSignaledList, NULL);
      }
      LOG(4, "POLL: added entry %p event 0x%x to signaled hash table\n",
          entry, entry->event);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * PollGtkReadableSocketCheck --
 *
 *      Windows does not signal a read event for a socket if there has not
 *      been a recv() since the last time the socket is signaled (even
 *      after a new event is associated with the socket).  We check the socket
 *      and add it to the signaled list if there is data to read.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static void
PollGtkReadableSocketCheck(PollGtkEntry *entry)  // IN
{
   char buf[1];
   int ret;

   ASSERT_POLL_LOCKED();
   ASSERT(entry->read.cb && entry->read.flags & POLL_FLAG_SOCKET);

   ret = recv(entry->event, buf, 1, MSG_PEEK);
   if (ret == 1) {
      entry->read.timesNotFired = 1;
      PollGtkAddToSignaledList(entry);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * PollGtkWritableSocketCheck --
 *
 *      Windows does not signal a write event for a socket if a previous
 *      write had not resulted in WSAEWOULDBLOCK.  Do a zero-byte send()
 *      to find out if it would block and if not, add the event to the
 *      signaled list so that it will fire.
 *
 * Results:
 *      TRUE if socket is writable.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
PollGtkWritableSocketCheck(PollGtkEntry *entry)  // IN
{
   int ret;
   char c;

   ASSERT_POLL_LOCKED();
   ASSERT(entry->write.cb && entry->write.flags & POLL_FLAG_SOCKET);

   ret = send(entry->event, &c, 0, 0);
   if (ret == SOCKET_ERROR) {
      if (GetLastError() != WSAEWOULDBLOCK) {
         LOG(1, "POLL error while doing zero-byte send: %u %s\n",
             GetLastError(), Err_ErrString());
      }
      return FALSE;
   } else {
      entry->write.timesNotFired = 1;
      PollGtkAddToSignaledList(entry);
      return TRUE;
   }
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * PollGtkDeviceCallback --
 *
 *      Insert specified entry as device callback.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
PollGtkDeviceCallback(PollGtkEntry *entry)  // IN
{
   Poll *poll = pollState;
   int conditionFlags;

   ASSERT_POLL_LOCKED();
   conditionFlags = G_IO_ERR | G_IO_HUP | G_IO_NVAL;
   if (POLL_FLAG_READ & entry->read.flags) {
      conditionFlags |= G_IO_IN | G_IO_PRI;
   }
   if (POLL_FLAG_WRITE & entry->write.flags) {
      conditionFlags |= G_IO_OUT;
   }

   /*
    * XXX Looking at the GTK/GLIB source code, it seems that a returned value
    *     of 0 indicates failure (and I should check for it), but that is not
    *     clear
    */
#ifdef _WIN32
   if ((entry->read.flags | entry->write.flags) & POLL_FLAG_SOCKET) {
      entry->channel = g_io_channel_win32_new_socket(entry->event);

      if (entry->read.flags & POLL_FLAG_READ) {
         PollGtkReadableSocketCheck(entry);
      }
      if (entry->write.flags & POLL_FLAG_WRITE) {
         PollGtkWritableSocketCheck(entry);
      }
   } else if ((entry->read.flags | entry->write.flags) & POLL_FLAG_FD) {
      entry->channel = g_io_channel_win32_new_fd(entry->event);
   } else {
      entry->channel = g_io_channel_win32_new_messages(entry->event);
   }
#else
   entry->channel = g_io_channel_unix_new(entry->event);
#endif
   entry->gtkInputId = g_io_add_watch(entry->channel,
                                      conditionFlags,
                                      PollGtkEventCallback,
                                      entry);

   g_hash_table_insert(poll->deviceTable, (gpointer)(intptr_t)entry->event,
                       entry);
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkCallbackRemoveEntry --
 *
 *      Remove specified poll entry.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
PollGtkCallbackRemoveEntry(PollGtkEntry *foundEntry,  // IN
                           Bool removeWrite)          // IN
{
   Poll *poll = pollState;

   ASSERT_POLL_LOCKED();
   if (foundEntry->type == POLL_DEVICE) {
      PollGtkEntry *newEntry = NULL;
      intptr_t key;

      if (removeWrite) {
         if (foundEntry->read.flags) {
            newEntry = g_new0(PollGtkEntry, 1);
            newEntry->read = foundEntry->read;
            LOG_ENTRY(2, " to be removed, read cb remains\n", foundEntry, TRUE);
         } else {
            LOG_ENTRY(2, " to be removed\n", foundEntry, TRUE);
         }
      } else {
         if (foundEntry->write.flags) {
            newEntry = g_new0(PollGtkEntry, 1);
            newEntry->write = foundEntry->write;
            LOG_ENTRY(2, " to be removed, write cb remains\n", foundEntry,
                      FALSE);
         } else {
            LOG_ENTRY(2, " to be removed\n", foundEntry, FALSE);
         }
      }

      key = foundEntry->event;
      g_hash_table_remove(poll->deviceTable, (gpointer)key);
#ifdef _WIN32
      if (!poll->signaledInUse) {
         g_hash_table_remove(poll->signaledTable, (gpointer)key);
      }
#endif
      if (newEntry) {
         newEntry->event = key;
         newEntry->type = POLL_DEVICE;
         PollGtkDeviceCallback(newEntry);
      }
   } else {
      ASSERT(!removeWrite);
      ASSERT(foundEntry->write.cb == NULL);
      LOG_ENTRY(2, " to be removed\n", foundEntry, FALSE);
      if (!g_hash_table_remove(poll->timerTable,
                               (gpointer)(intptr_t)foundEntry->gtkInputId)) {
         LOG(2, "POLL: entry %p not found\n", foundEntry);
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkCallbackRemoveInt --
 *
 *      Remove a callback.
 *
 * Results:
 *      TRUE if entry found and removed, FALSE otherwise
 *
 * Side effects:
 *      A callback may be modified instead of completely removed.
 *
 *----------------------------------------------------------------------
 */

static Bool
PollGtkCallbackRemoveInt(PollClassSet classSet,           // IN
                         int flags,                       // IN
                         PollerFunction f,                // IN
                         void *clientData,                // IN
                         Bool matchAnyClientData,         // IN
                         PollEventType type,              // IN
                         void **foundClientData)          // OUT
{
   Poll *poll = pollState;
   GHashTable *searchTable;
   PollGtkFindEntryData searchEntry;
   PollGtkEntry *foundEntry;

   ASSERT(poll);
   ASSERT(!clientData || !matchAnyClientData);
   ASSERT(type >= 0 && type < POLL_NUM_QUEUES);
   ASSERT(foundClientData);

   searchEntry.classSet = classSet;
   searchEntry.flags = flags;
   searchEntry.cb = f;
   searchEntry.clientData = clientData;
   searchEntry.type = type;
   searchEntry.matchAnyClientData = matchAnyClientData;

   switch (type) {
   case POLL_REALTIME:
   case POLL_MAIN_LOOP:
      searchTable = poll->timerTable;
      break;
   case POLL_DEVICE:
      searchTable = poll->deviceTable;
      break;
   case POLL_VIRTUALREALTIME:
   case POLL_VTIME:
   default:
      NOT_IMPLEMENTED();
   }

   PollGtkLock();

   if (flags & POLL_FLAG_WRITE) {
      foundEntry = g_hash_table_find(searchTable,
                                     PollGtkFindWritePredicate,
                                     &searchEntry);
   } else {
      foundEntry = g_hash_table_find(searchTable,
                                     PollGtkFindReadPredicate,
                                     &searchEntry);
   }
   if (foundEntry) {
      if (flags & POLL_FLAG_WRITE) {
         *foundClientData = foundEntry->write.clientData;
         PollGtkCallbackRemoveEntry(foundEntry, TRUE);
      } else {
         *foundClientData = foundEntry->read.clientData;
         PollGtkCallbackRemoveEntry(foundEntry, FALSE);
      }
   } else {
      LOG(1, "POLL: no matching entry for cb %p, data %p, flags %x, type %x\n",
          f, clientData, flags, type);
   }

   PollGtkUnlock();
   return foundEntry != NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkCallbackRemove --
 *
 *      Remove a callback.
 *
 * Results:
 *      TRUE if entry found and removed, FALSE otherwise
 *
 * Side effects:
 *      A callback may be modified instead of completely removed.
 *
 *----------------------------------------------------------------------
 */

static Bool
PollGtkCallbackRemove(PollClassSet classSet,   // IN
                      int flags,               // IN
                      PollerFunction f,        // IN
                      void *clientData,        // IN
                      PollEventType type)      // IN
{
   void *foundClientData;

   return PollGtkCallbackRemoveInt(classSet, flags, f, clientData, FALSE, type,
                                   &foundClientData);
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkCallbackRemoveOneByCB --
 *
 *      Remove a callback.
 *
 * Results:
 *      TRUE if entry found and removed (*clientData updated), FALSE otherwise
 *
 * Side effects:
 *      A callback may be modified instead of completely removed.
 *
 *----------------------------------------------------------------------
 */

static Bool
PollGtkCallbackRemoveOneByCB(PollClassSet classSet,   // IN
                             int flags,               // IN
                             PollerFunction f,        // IN
                             PollEventType type,      // IN
                             void **clientData)       // OUT
{
   return PollGtkCallbackRemoveInt(classSet, flags, f, NULL, TRUE, type, clientData);
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkRemoveOneCallback --
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      If entry was active, it is removed from main loop.
 *
 *----------------------------------------------------------------------
 */

static void
PollGtkRemoveOneCallback(gpointer data) // IN
{
   PollGtkEntry *eventEntry = data;

   switch(eventEntry->type) {
   case POLL_REALTIME:
   case POLL_MAIN_LOOP:
      g_source_remove(eventEntry->gtkInputId);
      break;
   case POLL_DEVICE:
      g_source_remove(eventEntry->gtkInputId);
      g_io_channel_unref(eventEntry->channel);
      eventEntry->channel = NULL;
      break;
   case POLL_VIRTUALREALTIME:
   case POLL_VTIME:
   default:
      NOT_IMPLEMENTED();
   }

   g_free(eventEntry);
}


/*
 *----------------------------------------------------------------------
 *
 * PollGtkCallback --
 *
 *      For the POLL_REALTIME or POLL_DEVICE queues, entries can be
 *      inserted for good, to fire on a periodic basis (by setting the
 *      POLL_FLAG_PERIODIC flag).
 *
 *      Otherwise, the callback fires only once.
 *
 *      For periodic POLL_REALTIME callbacks, "info" is the time in
 *      microseconds between execution of the callback.  For
 *      POLL_DEVICE callbacks, info is a file descriptor.
 *
 *----------------------------------------------------------------------
 */

static VMwareStatus
PollGtkCallback(PollClassSet classSet,   // IN
                int flags,               // IN
                PollerFunction f,        // IN
                void *clientData,        // IN
                PollEventType type,      // IN
                PollDevHandle info,      // IN
                MXUserRecLock *lock)     // IN
{
   VMwareStatus result;
   Poll *poll = pollState;
   PollGtkEntry *newEntry;

   ASSERT(f);

   newEntry = g_new0(PollGtkEntry, 1);
   newEntry->type = type;
   if (flags & POLL_FLAG_WRITE) {
      newEntry->write.flags = flags;
      newEntry->write.cb = f;
      newEntry->write.clientData = clientData;
      newEntry->write.cbLock = lock;
      newEntry->write.classSet = classSet;
      LOG_ENTRY(2, " is being added\n", newEntry, TRUE);
   } else {
      newEntry->read.flags = flags;
      newEntry->read.cb = f;
      newEntry->read.clientData = clientData;
      newEntry->read.cbLock = lock;
      newEntry->read.classSet = classSet;
      LOG_ENTRY(2, " is being added\n", newEntry, FALSE);
   }

   PollGtkLock();

   if (type == POLL_DEVICE) {
      PollGtkEntry *foundEntry;

      foundEntry = g_hash_table_lookup(poll->deviceTable,
                                       (gpointer)(intptr_t)info);
      if (foundEntry) {
         /*
          * We are going to merge old entry with new.  Verify that we really
          * found entry we were looking for.
          */
         ASSERT(foundEntry->type == type);
         ASSERT(foundEntry->event == info);

         /*
          * Now verify that found entry does not wait for direction we are
          * registering.
          */
         if (flags & POLL_FLAG_WRITE) {
            ASSERT(foundEntry->write.flags == 0);
            ASSERT(foundEntry->write.cb == NULL);
            ASSERT(foundEntry->read.cb != NULL);
            newEntry->read = foundEntry->read;
            LOG_ENTRY(2, " will merge with new entry\n", foundEntry, FALSE);
         } else {
            ASSERT(foundEntry->read.flags == 0);
            ASSERT(foundEntry->read.cb == NULL);
            ASSERT(foundEntry->write.cb != NULL);
            newEntry->write = foundEntry->write;
            LOG_ENTRY(2, " will merge with new entry\n", foundEntry, TRUE);
         }

         /*
          * Either both callbacks must be for socket, or for non-socket.
          * Mixing them is not supported at this moment.
          */
         ASSERT(((newEntry->read.flags ^ newEntry->write.flags) & POLL_FLAG_SOCKET)
                == 0);
         g_hash_table_remove(poll->deviceTable, (gpointer)(intptr_t)info);
      } else if (vmx86_debug) {
         /*
          * We did not find entry by fd.  Try looking it up by flags/f/cs/cd.
          * If we can find it, then user tried to insert same flags/f/cs/cd for
          * two file descriptors.  Which is not allowed.
          */
         PollGtkFindEntryData searchEntry;

         searchEntry.flags = flags;
         searchEntry.classSet = classSet;
         searchEntry.cb = f;
         searchEntry.clientData = clientData;
         searchEntry.type = POLL_DEVICE;
         searchEntry.matchAnyClientData = FALSE;

         if (flags & POLL_FLAG_WRITE) {
            foundEntry = g_hash_table_find(poll->deviceTable,
                                           PollGtkFindWritePredicate,
                                           &searchEntry);
         } else {
            foundEntry = g_hash_table_find(poll->deviceTable,
                                           PollGtkFindReadPredicate,
                                           &searchEntry);
         }
         ASSERT(!foundEntry);
      }
   }

   ASSERT(poll != NULL);

   /*
    * Every callback must be in POLL_CLASS_MAIN (plus possibly others)
    */
   ASSERT(PollClassSet_IsMember(classSet, POLL_CLASS_MAIN) != 0);

   ASSERT(type >= 0 && type < POLL_NUM_QUEUES);
   switch(type) {
   case POLL_MAIN_LOOP:
      ASSERT(info == 0);
      /* Fall-through */
   case POLL_REALTIME:
      info = info / 1000;
      ASSERT(info == (uint32)info);
      ASSERT(info >= 0);

      newEntry->event = info;

      /*
       * info is the delay in microseconds, but we need to pass in
       * a delay in milliseconds.
       */
      newEntry->gtkInputId = g_timeout_add(info,
                                           PollGtkBasicCallback,
                                           newEntry);
      g_hash_table_insert(poll->timerTable, (gpointer)(intptr_t)newEntry->gtkInputId,
                          newEntry);
      break;

   case POLL_DEVICE:
      /*
       * info is a file descriptor/socket/handle
       */
      newEntry->event = info;

      PollGtkDeviceCallback(newEntry);
      break;

   case POLL_VIRTUALREALTIME:
   case POLL_VTIME:
   default:
      NOT_IMPLEMENTED();
   }

   result = VMWARE_STATUS_SUCCESS;

   PollGtkUnlock();

   return result;
} // Poll_Callback


/*
 *-----------------------------------------------------------------------------
 *
 * PollGtkEventCallback --
 * PolLGtkEventCallbackWork --
 *
 *       This is the basic callback marshaller. It is invoked directly by gtk
 *       in the case of event callbacks and indirectly through a wrapper for
 *       timer callbacks. It calls the real callback and either cleans up
 *       the event or (if it's PERIODIC) leaves it registered to fire again.
 *
 * Results:
 *       TRUE if the event source should remain registered. FALSE otherwise.
 *
 * Side effects:
 *       Depends on the invoked callback
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
PollGtkEventCallback(GIOChannel *source,     // IN: GIOChannel associated with
                                             //     the file descriptor
                     GIOCondition condition, // IN: State(s) of the file
                                             //     descriptor that triggered
                                             //     the GTK callback
                     gpointer data)          // IN: PollGtkEntry *
{
   Bool fired;

   return PollGtkEventCallbackWork(source, condition, data, FALSE, &fired);
}


static gboolean
PollGtkEventCallbackWork(GIOChannel *source,     // IN:
                         GIOCondition condition, // IN:
                         gpointer data,          // IN:
                         Bool hasPollLock,       // IN:
                         Bool *firedAll)         // OUT:
{
   PollGtkEntry *eventEntry;
   PollerFunction cbFunc;
   void *clientData;
   MXUserRecLock *cbLock;
   Bool needReadAndWrite = FALSE;
   Bool fireWriteCallback;
   PollDevHandle fd = -1;
   gboolean ret;
   GIOChannel *channel;

   *firedAll = FALSE;

   if (!hasPollLock) {
      PollGtkLock();
   }

   if (g_source_is_destroyed(g_main_current_source())) {
      ret = FALSE;
      goto exitHasLock;
   }

   eventEntry = data;
   ASSERT(eventEntry);

   /*
    * Cache the bits we need to fire the callback in case the
    * callback is discarded for being non-periodic.
    */
   channel = eventEntry->channel;

   if (eventEntry->read.cb &&
       (condition & (G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL))) {
      cbFunc = eventEntry->read.cb;
      clientData = eventEntry->read.clientData;
      cbLock = eventEntry->read.cbLock;
      ret = (eventEntry->read.flags & POLL_FLAG_PERIODIC) != 0;
      fireWriteCallback = FALSE;
      fd = eventEntry->event;
      if (eventEntry->write.cb && (condition & G_IO_OUT)) {
         needReadAndWrite = TRUE;
      }
   } else if (eventEntry->write.cb &&
              (condition & (G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL))) {
      cbFunc = eventEntry->write.cb;
      clientData = eventEntry->write.clientData;
      cbLock = eventEntry->write.cbLock;
      ret = (eventEntry->write.flags & POLL_FLAG_PERIODIC) != 0;
      fireWriteCallback = TRUE;
      fd = eventEntry->event;
   } else {
      ASSERT(FALSE);
      ret = TRUE;
      goto exitHasLock;
   }

   if (cbLock && !MXUser_TryAcquireRecLock(cbLock)) {
      /*
       * We cannot fire at this time.  For device callbacks, on Posix platforms
       * we should get called again at the next dispatch so we do nothing; on
       * Windows platforms we cannot rely on that so we have to remember the
       * signaled event and retry in the next loop iteration.  For non-zero
       * delay real-time callbacks, we schedule a 0-delay timer callback to
       * attempt firing again.  Main-loop callbacks are already 0-delay
       * callbacks, so we only need to return TRUE such that the source is not
       * removed.
       * TODO: Detect busy looping and apply backoff.
       */

      LOG_ENTRY(3, " did not fire\n", eventEntry, fireWriteCallback);
      if (fireWriteCallback) {
         eventEntry->write.timesNotFired++;
      } else {
         eventEntry->read.timesNotFired++;
      }

      if (eventEntry->type == POLL_DEVICE) {
#ifdef _WIN32
         PollGtkAddToSignaledList(eventEntry);
#endif
      } else {
         if (eventEntry->type == POLL_REALTIME && eventEntry->event != 0 &&
             eventEntry->read.timesNotFired == 1) {
            /* Re-purpose the event for the retry (as a 0-delay timer). */
            g_source_remove(eventEntry->gtkInputId);
            if (!g_hash_table_steal(pollState->timerTable,
                                    (gpointer)(intptr_t)eventEntry->gtkInputId)) {
               LOG_ENTRY(0, " not found\n", eventEntry, FALSE);
               ASSERT(FALSE);
            }
            eventEntry->gtkInputId = g_timeout_add(0, PollGtkBasicCallback,
                                                   eventEntry);
            g_hash_table_insert(pollState->timerTable,
                                (gpointer)(intptr_t)eventEntry->gtkInputId,
                                eventEntry);
            LOG_ENTRY(1, " rescheduled for retry\n", eventEntry, FALSE);
            ret = FALSE;
         } else {
            /* The event is already a 0-delay timer. */
            LOG_ENTRY(2, " will retry firing\n", eventEntry, FALSE);
            ret = TRUE;
         }
         ASSERT(!needReadAndWrite);
         goto exitHasLock;
      }
   } else {
      /*
       * Fire the callback.
       *
       * The callback must fire after unregistering non-periodic callbacks
       * in case the callback function re-registers itself.  If the callback
       * explicitly calls Poll_CallbackRemove, it is harmless when the callback
       * is already gone.
       */

      LOG_ENTRY(3, " about to fire\n", eventEntry, fireWriteCallback);
      *firedAll = TRUE;

      if (!ret) {
         PollGtkCallbackRemoveEntry(eventEntry, fireWriteCallback);
      } else if (fireWriteCallback) {
         eventEntry->write.timesNotFired = 0;
      } else if (!fireWriteCallback && eventEntry->read.timesNotFired > 0) {
         eventEntry->read.timesNotFired = 0;
         if (eventEntry->type == POLL_REALTIME && eventEntry->event != 0) {
            /* We need to reschedule the callback with the original delay. */
            g_source_remove(eventEntry->gtkInputId);
            if (!g_hash_table_steal(pollState->timerTable,
                                    (gpointer)(intptr_t)eventEntry->gtkInputId)) {
               LOG_ENTRY(0, " not found\n", eventEntry, FALSE);
               ASSERT(FALSE);
            }
            eventEntry->gtkInputId = g_timeout_add((guint)eventEntry->event,
                                                   PollGtkBasicCallback,
                                                   eventEntry);
            g_hash_table_insert(pollState->timerTable,
                                (gpointer)(intptr_t)eventEntry->gtkInputId,
                                eventEntry);
            LOG_ENTRY(1, " rescheduled with original delay\n",
                      eventEntry, FALSE);
         }
      }

      PollGtkUnlock();
      ((PollerFunctionGtk)cbFunc)(clientData, channel);
      if (cbLock) {
         MXUser_ReleaseRecLock(cbLock);
      }
#ifdef _WIN32
      PollGtkLock();
      eventEntry = g_hash_table_lookup(pollState->deviceTable,
                                       (gpointer)(intptr_t)fd);
      if (fireWriteCallback && eventEntry && eventEntry->write.cb &&
          (eventEntry->write.flags & POLL_FLAG_SOCKET)) {
         PollGtkWritableSocketCheck(eventEntry);
      } else if (!fireWriteCallback && eventEntry && eventEntry->read.cb &&
                 (eventEntry->read.flags & POLL_FLAG_SOCKET)) {
         PollGtkReadableSocketCheck(eventEntry);
      }
      if (fireWriteCallback || !needReadAndWrite) {
         goto exitHasLock;
      }
#else  /* ifdef _WIN32 */
      if (needReadAndWrite) {
         PollGtkLock();
      } else {
         goto exit;
      }
#endif
   }

   /*
    * We must fire both read & write callbacks.  Read callback already fired,
    * and could remove write callback.  So lookup entry from file descriptor.
    */
   if (needReadAndWrite) {
      PollGtkEntry *foundEntry;

      cbFunc = NULL;
      cbLock = NULL;
      ASSERT(fd != -1);
      foundEntry = g_hash_table_lookup(pollState->deviceTable,
                                       (gpointer)(intptr_t)fd);
      if (foundEntry && (cbFunc = foundEntry->write.cb) != NULL) {
         cbLock = foundEntry->write.cbLock;
         clientData = foundEntry->write.clientData;
         if (!cbLock || MXUser_TryAcquireRecLock(cbLock)) {
            LOG_ENTRY(3, " about to fire\n", foundEntry, TRUE);
            if (!(foundEntry->write.flags & POLL_FLAG_PERIODIC)) {
               PollGtkCallbackRemoveEntry(foundEntry, TRUE);
               ret = FALSE;
            } else {
               foundEntry->write.timesNotFired = 0;
            }
            PollGtkUnlock();
            cbFunc(clientData);
            if (cbLock) {
               MXUser_ReleaseRecLock(cbLock);
            }
#ifdef _WIN32
            PollGtkLock();
            foundEntry = g_hash_table_lookup(pollState->deviceTable,
                                             (gpointer)(intptr_t)fd);
            if (foundEntry && foundEntry->write.cb &&
                (foundEntry->write.flags & POLL_FLAG_SOCKET)) {
               PollGtkWritableSocketCheck(foundEntry);
            }
            goto exitHasLock;
#else
            goto exit;
#endif
         } else {
            LOG_ENTRY(3, " did not fire\n", foundEntry, TRUE);
            foundEntry->write.timesNotFired++;
            *firedAll = FALSE;
#ifdef _WIN32
            PollGtkAddToSignaledList(foundEntry);
#endif
         }
      }
   }

exitHasLock:
   if (!hasPollLock) {
      PollGtkUnlock();
   }
   return ret;

#ifndef _WIN32
exit:
   if (hasPollLock) {
      PollGtkLock();
   }
   return ret;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * PollGtkBasicCallback --
 *
 *       This is called by Gtk when a timeout expires. It is simply
 *       an adapter that immediately calls PollGtkEventCallback.
 *
 * Results:
 *       TRUE if the event source should remain registered. FALSE otherwise.
 *
 * Side effects:
 *       Depends on the invoked callback
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
PollGtkBasicCallback(gpointer data) // IN: The eventEntry
{
   return PollGtkEventCallback(NULL, G_IO_IN, data);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Poll_InitGtk --
 *
 *      Public init function for this Poll implementation. Poll loop will be
 *      up and running after this is called.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
Poll_InitGtk(void)
{
   static const PollImpl gtkImpl =
   {
      PollGtkInit,
      PollGtkExit,
      PollGtkLoopTimeout,
      PollGtkCallback,
      PollGtkCallbackRemove,
      PollGtkCallbackRemoveOneByCB,
      PollLockingAlwaysEnabled,
   };

   if (g_once_init_enter(&inited)) {
      gsize didInit = 1;
      Poll_InitWithImpl(&gtkImpl);
      g_once_init_leave(&inited, didInit);
   }
}
 070701000001BD000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/procMgr    070701000001BE000081A40000000000000000000000016822550500000420000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/procMgr/Makefile.am    ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libProcMgr.la

libProcMgr_la_SOURCES =
libProcMgr_la_SOURCES += procMgrPosix.c
if SOLARIS
   libProcMgr_la_SOURCES += procMgrSolaris.c
endif
070701000001BF000081A40000000000000000000000016822550500012EA0000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/procMgr/procMgrPosix.c /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * procMgrPosix.c --
 *
 *    Posix implementation of the process management lib
 *
 */

// pull in setresuid()/setresgid() if possible
#define  _GNU_SOURCE
#include <unistd.h>
#if !defined(__FreeBSD__) && !defined(sun) && !defined(__APPLE__)
#include <asm/param.h>
#endif
#if !defined(sun) && !defined(__APPLE__)
#include <locale.h>
#include <sys/stat.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <time.h>
#include <grp.h>
#include <sys/syscall.h>
#if defined(__linux__) || defined(__FreeBSD__) || defined(HAVE_SYS_USER_H)
// sys/param.h is required on FreeBSD before sys/user.h
#   include <sys/param.h>
// Pull in PAGE_SIZE/PAGE_SHIFT defines ahead of vm_basic_defs.h
#   include <sys/user.h>
#endif
#if defined (__FreeBSD__)
#include <kvm.h>
#include <limits.h>
#include <paths.h>
#include <sys/sysctl.h>
#endif
#if defined(__APPLE__)
#include <sys/sysctl.h>
#include <sys/user.h>
#include "posix.h"
#endif
#include "vmware.h"
#include "procMgr.h"
#include "vm_assert.h"
#include "debug.h"
#include "util.h"
#include "msg.h"
#include "vmsignal.h"
#undef offsetof
#include "file.h"
#include "dynbuf.h"
#include "dynarray.h"
#include "su.h"
#include "str.h"
#include "strutil.h"
#include "codeset.h"
#include "unicode.h"
#include "logToHost.h"

#ifdef USERWORLD
#include <vm_basic_types.h>
#include <vmkuserstatus.h>
#include <vmkusercompat.h>
#endif


/*
 * Use custom exit code in the range [166, 199]
 */

#define PROCMGR_CUSTOM_EXIT_CODE_BASE 166

enum {
   PROCMGR_ERROR_CMD_ENCODE = PROCMGR_CUSTOM_EXIT_CODE_BASE,
   PROCMGR_ERROR_WORKDIR_ENCODE,
   PROCMGR_ERROR_VMK_FORKEXEC,
   PROCMGR_ERROR_FORK,
   PROCMGR_ERROR_SETREGID,
   PROCMGR_ERROR_SETREUID,
   PROCMGR_ERROR_SET_SIG_HANDLER,
   PROCMGR_ERROR_WRITE_PIPE,
   PROCMGR_ERROR_WAITPID
};


/*
 * All signals that:
 * . Can terminate the process
 * . May occur even if the program has no bugs
 */
static int const cSignals[] = {
   SIGHUP,
   SIGINT,
   SIGQUIT,
   SIGTERM,
   SIGUSR1,
   SIGUSR2,
};

static Bool gOffspringProcess = FALSE;
static int  gOffspringExitCode = 0;


/*
 * Keeps track of the posix async proc info.
 */
struct ProcMgr_AsyncProc {
   pid_t waiterPid;          // pid of the waiter process
   pid_t resultPid;          // pid of the process created for the client
   int fd;                   // fd to write to when the child is done
   Bool validExitCode;
   int exitCode;
};

static pid_t ProcMgrStartProcess(char const *cmd,
                                 char * const  *envp,
                                 char const *workingDir);

static Bool ProcMgrWaitForProcCompletion(pid_t pid,
                                         Bool *validExitCode,
                                         int *exitCode);

static int ProcMgrKill(pid_t pid,
                       int sig,
                       int timeout);

#if defined(__APPLE__)
static int ProcMgrGetCommandLineArgs(long pid,
                                     DynBuf *argsBuf,
                                     char **procCmdName);

Bool ProcMgr_PromoteEffectiveToReal(void);
#endif

#ifdef sun
#define  BASH_PATH "/usr/bin/bash"
#else
#define  BASH_PATH "/bin/bash"
#endif


/*
 * Mutexes in bora-vmsoft/apps/vmtoolslib/vmtoolsLog.c and glib could have
 * been locked at fork time. vmtoolsLog.c Debug, Warning and Panic functions
 * are not safe for offspring processes. A straightforward alternative for
 * offspring process code paths is to invoke SAFE_DEBUG, SAFE_WARNING and
 * SAFE_PANIC.
*/

#define SAFE_DEBUG(fmt, ...)                \
   if (gOffspringProcess) {                 \
      OffspringDebug(fmt, ## __VA_ARGS__);  \
   } else {                                 \
      Debug(fmt, ## __VA_ARGS__);           \
   }

#define SAFE_WARNING(fmt, ...)                \
   if (gOffspringProcess) {                   \
      OffspringWarning(fmt, ## __VA_ARGS__);  \
   } else {                                   \
      Warning(fmt, ## __VA_ARGS__);           \
   }

#define SET_OFFSPRING_EXIT_CODE(exitCode)  \
   if (gOffspringProcess) {                \
      gOffspringExitCode = exitCode;       \
   }

#define EXIT_ON_ERROR_WRITE_PIPE()  \
   _exit(gOffspringExitCode ? gOffspringExitCode : PROCMGR_ERROR_WRITE_PIPE)


/*
 *----------------------------------------------------------------------
 *
 * OffspringDebug --
 *
 *      Called by offspring processes to print a debug message to stdout.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static void
OffspringDebug(const char *fmt, ...)
{
   va_list args;

   va_start(args, fmt);
   vfprintf(stdout, fmt, args);
   va_end(args);
}


/*
 *----------------------------------------------------------------------
 *
 * OffspringWarning --
 *
 *      Called by offspring processes to print a warning message to stderr.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static void
OffspringWarning(const char *fmt, ...)
{
   va_list args;

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);
}


#if !defined(USERWORLD)

#define SAFE_PANIC(fmt, ...)                \
   if (gOffspringProcess) {                 \
      OffspringPanic(fmt, ## __VA_ARGS__);  \
   } else {                                 \
      Panic(fmt, ## __VA_ARGS__);           \
   }


/*
 *----------------------------------------------------------------------
 *
 * OffspringPanic --
 *
 *      Called by offspring processes to print an error message to stderr
 *      and abort.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static void
OffspringPanic(const char *fmt, ...)
{
   char cwd[PATH_MAX];
   va_list args;

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);

   /*
    * Refer to bora-vmsoft/apps/vmtoolslib/vmtoolsLog.c::VMToolsLogPanic
    */
   if (getcwd(cwd, sizeof cwd) != NULL) {
      if (access(cwd, W_OK) == -1) {
         /*
          * Can't write to the working dir. chdir() to the user's home
          * directory as an attempt to get a valid core dump.
          */
         const char *home = getenv("HOME");
         if (home != NULL) {
            if (chdir(home)) {
               /* Just to make glibc headers happy. */
            }
         }
      }
   }

   abort();
}

#endif


/*
 *----------------------------------------------------------------------
 *
 * WaitOffspring --
 *
 *      Wait to de-zombify a direct child process specified by pid.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static void
WaitOffspring(pid_t pid)
{
   int status;
   pid_t retVal;

   retVal = waitpid(pid, &status, 0);
   if (retVal == pid) {
      SAFE_DEBUG("waitpid(%"FMTPID") returned child status: 0x%08x, "
                 "WIFEXITED flag: %d, WEXITSTATUS value: %d\n",
                 pid, status, WIFEXITED(status), WEXITSTATUS(status));
   } else {
      SAFE_WARNING("waitpid(%"FMTPID") returned %"FMTPID" with error: %s\n",
                   pid, retVal, strerror(errno));
   }
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ReadProcFile --
 *
 *    Read the contents of a file in /proc/<pid>.
 *
 *    The size is essentially unbounded because of cmdline arguments.
 *    The only way to figure out the content size is to keep reading;
 *    stat(2) and lseek(2) lie.
 *
 *    The contents are NUL terminated -- in some distros may not include
 *    a NUL for some commands (eg. pid 1, /sbin/init) -- so add
 *    one to be safe.
 *
 * Results:
 *
 *    The length of the file.
 *
 *    -1 on error.
 *
 * Side effects:
 *
 *    The returned contents must be freed by caller.
 *
 *----------------------------------------------------------------------
 */

#if defined(__linux__)
int
ProcMgr_ReadProcFile(int fd,                       // IN
                     char **contents)              // OUT
{
   int size = 0;
   char tmp[512];
   int numRead;

   *contents = NULL;
   numRead = read(fd, tmp, sizeof(tmp));
   size = numRead;

   if (numRead <= 0) {
      goto done;
   }

   /*
    * handle the 99% case
    */
   if (sizeof(tmp) > numRead) {
      char *result;

      result = malloc(numRead + 1);
      if (NULL == result) {
         size = -1;
         goto done;
      }
      memcpy(result, tmp, numRead);
      result[numRead] = '\0';
      *contents = result;
      goto done;
   } else {
      DynBuf dbuf;

      DynBuf_Init(&dbuf);
      DynBuf_Append(&dbuf, tmp, numRead);
      do {
         numRead = read(fd, tmp, sizeof(tmp));
         if (numRead > 0) {
            DynBuf_Append(&dbuf, tmp, numRead);
         }
         size += numRead;
      } while (numRead > 0);
      // add the NUL term
      DynBuf_Append(&dbuf, "", 1);
      DynBuf_Trim(&dbuf);
      *contents = DynBuf_Detach(&dbuf);
      DynBuf_Destroy(&dbuf);
   }
done:
   return size;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ListProcesses --
 *
 *      List all the processes that the calling client has privilege to
 *      enumerate. The strings in the returned structure should be all
 *      UTF-8 encoded, although we do not enforce it right now.
 *
 * Results:
 *
 *      A ProcMgrProcInfoArray.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

ProcMgrProcInfoArray *
ProcMgr_ListProcesses(void)
{
   ProcMgrProcInfoArray *procList = NULL;
   ProcMgrProcInfo procInfo;
   Bool failed = TRUE;
   DIR *dir;
   struct dirent *ent;
   static time_t hostStartTime = 0;
   static unsigned long long hertz = 100;
   int numberFound;

   procList = Util_SafeCalloc(1, sizeof *procList);
   ProcMgrProcInfoArray_Init(procList, 0);
   procInfo.procCmdName = NULL;
   procInfo.procCmdAbsPath = NULL;
   procInfo.procCmdLine = NULL;
   procInfo.procOwner = NULL;

   /*
    * Figure out when the system started.  We need this number to
    * compute process start times, which are relative to this number.
    * We grab the first float in /proc/uptime, convert it to an integer,
    * and then subtract that from the current time.  That leaves us
    * with the seconds since epoch that the system booted up.
    */
   if (0 == hostStartTime) {
      FILE *uptimeFile = fopen("/proc/uptime", "r");
      if (NULL != uptimeFile) {
         double secondsSinceBoot;
         char *realLocale;
         char *savedLocale;

         /*
          * Set the locale such that floats are delimited with ".".
          */
         realLocale = setlocale(LC_NUMERIC, NULL);
         /*
          * On Linux, the returned locale can point to static data,
          * so make a copy.
          */
         savedLocale = Util_SafeStrdup(realLocale);
         setlocale(LC_NUMERIC, "C");
         numberFound = fscanf(uptimeFile, "%lf", &secondsSinceBoot);
         setlocale(LC_NUMERIC, savedLocale);
         free(savedLocale);

         /*
          * Figure out system boot time in absolute terms.
          */
         if (numberFound) {
            hostStartTime = time(NULL) - (time_t) secondsSinceBoot;
         }
         fclose(uptimeFile);
      }

      /*
       * Figure out the "hertz" value, which may be radically
       * different than the actual CPU frequency of the machine.
       * The process start time is expressed in terms of this value,
       * so let's compute it now and keep it in a static variable.
       */
#ifdef HZ
      hertz = (unsigned long long) HZ;
#else
      /*
       * Don't do anything.  Use the default value of 100.
       */
#endif
   } // if (0 == hostStartTime)

   /*
    * Scan /proc for any directory that is all numbers.
    * That represents a process id.
    */
   dir = opendir("/proc");
   if (NULL == dir) {
      Warning("ProcMgr_ListProcesses unable to open /proc\n");
      goto quit;
   }

   while ((ent = readdir(dir))) {
      struct stat fileStat;
      char cmdFilePath[1024];
      int statResult;
      int numRead = 0;   /* number of bytes that read() actually read */
      int cmdFd;
      int replaceLoop;
      struct passwd *pwd;
      char *cmdLineTemp = NULL;
      char *cmdStatTemp = NULL;
      size_t strLen = 0;
      unsigned long long dummy;
      unsigned long long relativeStartTime;
      char *stringBegin;
      Bool cmdNameLookup = TRUE;

      /*
       * We only care about dirs that look like processes.
       */
      if (strspn(ent->d_name, "0123456789") != strlen(ent->d_name)) {
         continue;
      }

      if (snprintf(cmdFilePath,
                   sizeof cmdFilePath,
                   "/proc/%s/cmdline",
                   ent->d_name) == -1) {
         Debug("Giant process id '%s'\n", ent->d_name);
         continue;
      }

      cmdFd = open(cmdFilePath, O_RDONLY);
      if (-1 == cmdFd) {
         /*
          * We may not be able to open the file due to the security reason.
          * In that case, just ignore and continue.
          */
         continue;
      }

      /*
       * Read in the command and its arguments.  Arguments are separated
       * by \0, which we convert to ' '.  Then we add a NULL terminator
       * at the end.  Example: "perl -cw try.pl" is read in as
       * "perl\0-cw\0try.pl\0", which we convert to "perl -cw try.pl\0".
       * It would have been nice to preserve the NUL character so it is easy
       * to determine what the command line arguments are without
       * using a quote and space parsing heuristic.  But we do this
       * to have parity with how Windows reports the command line.
       * In the future, we could keep the NUL version around and pass it
       * back to the client for easier parsing when retrieving individual
       * command line parameters is needed.
       */
      numRead = ProcMgr_ReadProcFile(cmdFd, &cmdLineTemp);
      close(cmdFd);

      if (numRead < 0) {
         continue;
      }

      if (snprintf(cmdFilePath,
                   sizeof cmdFilePath,
                   "/proc/%s/exe",
                   ent->d_name) != -1) {
         int exeLen;
         char exeRealPath[1024];

         /*
          * This readlink() call on the "exe" file of the current /proc
          * entry is not intended as a check on the subsequent open() of
          * the "status" file of that entry, hence no time-of-check to
          * time-of-use issue.
          */
         /* coverity[fs_check_call] */
         exeLen = readlink(cmdFilePath, exeRealPath, sizeof exeRealPath -1);
         if (exeLen != -1) {
            exeRealPath[exeLen] = '\0';
            procInfo.procCmdAbsPath =
               Unicode_Alloc(exeRealPath, STRING_ENCODING_DEFAULT);
         }
      }

      if (numRead > 0) {
         for (replaceLoop = 0 ; replaceLoop < numRead ; replaceLoop++) {
            if ('\0' == cmdLineTemp[replaceLoop] ||
                replaceLoop == numRead - 1) {
               if (cmdNameLookup) {
                  /*
                   * Store the command name.
                   * Find the last path separator, to get the cmd name.
                   * If no separator is found, then use the whole name.
                   * This needs to be done only if there is an absolute
                   * path for the binary. Else, the parsing may result
                   * in incorrect results. Following are few examples:
                   *
                   *   sshd: root@pts/1
                   *   gdm-session-worker [pam/gdm-autologin]
                   *
                   */
                  char *cmdNameBegin = strrchr(cmdLineTemp, '/');
                  if (NULL != cmdNameBegin && cmdLineTemp[0] == '/') {
                     /*
                      * Skip over the last separator.
                      */
                     cmdNameBegin++;
                  } else {
                     cmdNameBegin = cmdLineTemp;
                  }
                  procInfo.procCmdName = Unicode_Alloc(cmdNameBegin, STRING_ENCODING_DEFAULT);
                  if (procInfo.procCmdAbsPath == NULL &&
                      cmdLineTemp[0] == '/') {
                     procInfo.procCmdAbsPath =
                        Unicode_Alloc(cmdLineTemp, STRING_ENCODING_DEFAULT);
                  }
                  cmdNameLookup = FALSE;
               }

               /*
                * In /proc/{PID}/cmdline file, the command and the
                * arguments are separated by '\0'. We need to replace
                * only the intermediate '\0' with ' ' and not the trailing
                * NUL characer.
                */
               if (replaceLoop < (numRead - 1)) {
                  cmdLineTemp[replaceLoop] = ' ';
               }
            }
         }
      } else {
         /*
          * Some procs don't have a command line text, so read a name from
          * the 'status' file (should be the first line). If unable to get a name,
          * the process is still real, so it should be included in the list, just
          * without a name.
          */
         cmdFd = -1;
         numRead = 0;

         if (snprintf(cmdFilePath,
                      sizeof cmdFilePath,
                      "/proc/%s/status",
                      ent->d_name) != -1) {
            cmdFd = open(cmdFilePath, O_RDONLY);
         }
         if (cmdFd != -1) {
            numRead = ProcMgr_ReadProcFile(cmdFd, &cmdLineTemp);
            close(cmdFd);
         }
         if (numRead > 0) {
            /*
             * Extract the part with just the name, by reading until the first
             * space, then reading the next non-space word after that, and
             * ignoring everything else. The format looks like this:
             *     "^Name:[ \t]*(.*)$"
             * for example:
             *     "Name:    nfsd"
             */
            const char *nameStart;
            char *copyItr;

            /* Skip non-whitespace. */
            for (nameStart = cmdLineTemp; *nameStart &&
                                          *nameStart != ' ' &&
                                          *nameStart != '\t' &&
                                          *nameStart != '\n'; ++nameStart);
            /* Skip whitespace. */
            for (;*nameStart &&
                  (*nameStart == ' ' ||
                   *nameStart == '\t' ||
                   *nameStart == '\n'); ++nameStart);
            /* Copy the name to the start of the string and null term it. */
            for (copyItr = cmdLineTemp; *nameStart && *nameStart != '\n';) {
               *(copyItr++) = *(nameStart++);
            }
            *copyItr = '\0';
            /*
             * Store the command name.
             */
            procInfo.procCmdName = Unicode_Alloc(cmdLineTemp, STRING_ENCODING_DEFAULT);
            if (procInfo.procCmdAbsPath == NULL &&
                cmdLineTemp[0] == '/') {
               procInfo.procCmdAbsPath = Unicode_Alloc(cmdLineTemp, STRING_ENCODING_DEFAULT);
            }
         }
      }

      /*
       * Get the inode information for this process.  This gives us
       * the process owner.
       */
      if (snprintf(cmdFilePath,
                   sizeof cmdFilePath,
                   "/proc/%s",
                   ent->d_name) == -1) {
         Debug("Giant process id '%s'\n", ent->d_name);
         goto next_entry;
      }

      /*
       * stat() /proc/<pid> to get the owner.  We use fileStat.st_uid
       * later in this code.  If we can't stat(), ignore and continue.
       * Maybe we don't have enough permission.
       *
       * This stat() call on the current /proc entry is not intended
       * as a check on the open() near the top of the while loop which
       * is on the cmdline file of the next entry in /proc, hence no
       * time-of-check to time-of-use issue.
       */
      /* coverity[fs_check_call] */
      statResult = stat(cmdFilePath, &fileStat);
      if (0 != statResult) {
         goto next_entry;
      }

      /*
       * Figure out the process start time.  Open /proc/<pid>/stat
       * and read the start time and compute it in absolute time.
       */
      if (snprintf(cmdFilePath,
                   sizeof cmdFilePath,
                   "/proc/%s/stat",
                   ent->d_name) == -1) {
         Debug("Giant process id '%s'\n", ent->d_name);
         goto next_entry;
      }
      cmdFd = open(cmdFilePath, O_RDONLY);
      if (-1 == cmdFd) {
         goto next_entry;
      }
      numRead = ProcMgr_ReadProcFile(cmdFd, &cmdStatTemp);
      close(cmdFd);
      if (0 >= numRead) {
         goto next_entry;
      }
      /*
       * Skip over initial process id and process name.  "123 (bash) [...]".
       */
      stringBegin = strchr(cmdStatTemp, ')') + 2;

      numberFound = sscanf(stringBegin, "%c %d %d %d %d %d "
                           "%lu %lu %lu %lu %lu %Lu %Lu %Lu %Lu %ld %ld "
                           "%d %ld %Lu",
                           (char *) &dummy, (int *) &dummy, (int *) &dummy,
                           (int *) &dummy, (int *) &dummy,  (int *) &dummy,
                           (unsigned long *) &dummy, (unsigned long *) &dummy,
                           (unsigned long *) &dummy, (unsigned long *) &dummy,
                           (unsigned long *) &dummy,
                           (unsigned long long *) &dummy,
                           (unsigned long long *) &dummy,
                           (unsigned long long *) &dummy,
                           (unsigned long long *) &dummy,
                           (long *) &dummy, (long *) &dummy,
                           (int *) &dummy, (long *) &dummy,
                           &relativeStartTime);
      if (20 != numberFound) {
         goto next_entry;
      }

      /*
       * Store the command line string pointer in dynbuf.
       */
      if (cmdLineTemp) {
         int i;

         /*
          * Chop off the trailing whitespace characters.
          */
         for (i = strlen(cmdLineTemp) - 1 ;
              i >= 0 && cmdLineTemp[i] == ' ' ;
              i--) {
            cmdLineTemp[i] = '\0';
         }

         procInfo.procCmdLine = Unicode_Alloc(cmdLineTemp, STRING_ENCODING_DEFAULT);
      } else {
         procInfo.procCmdLine = Unicode_Alloc("", STRING_ENCODING_UTF8);
      }

      /*
       * Store the pid in dynbuf.
       */
      procInfo.procId = (pid_t) atoi(ent->d_name);

      /*
       * Store the owner of the process.
       */
      pwd = getpwuid(fileStat.st_uid);
      procInfo.procOwner = (NULL == pwd)
                           ? Str_SafeAsprintf(&strLen, "%d", (int) fileStat.st_uid)
                           : Unicode_Alloc(pwd->pw_name, STRING_ENCODING_DEFAULT);

      /*
       * Store the time that the process started.
       */
      procInfo.procStartTime = hostStartTime + (relativeStartTime / hertz);

      /*
       * Store the process info pointer into a list buffer.
       */
      if (!ProcMgrProcInfoArray_Push(procList, procInfo)) {
         Warning("%s: failed to expand DynArray - out of memory\n",
                 __FUNCTION__);
         free(cmdLineTemp);
         free(cmdStatTemp);
         goto quit;
      }
      procInfo.procCmdName = NULL;
      procInfo.procCmdAbsPath = NULL;
      procInfo.procCmdLine = NULL;
      procInfo.procOwner = NULL;

next_entry:
      free(procInfo.procCmdName);
      procInfo.procCmdName = NULL;
      free(procInfo.procCmdAbsPath);
      procInfo.procCmdAbsPath = NULL;

      free(cmdLineTemp);
      free(cmdStatTemp);
   } // while readdir

   if (0 < ProcMgrProcInfoArray_Count(procList)) {
      failed = FALSE;
   }

quit:
   closedir(dir);

   free(procInfo.procCmdName);
   free(procInfo.procCmdAbsPath);
   free(procInfo.procCmdLine);
   free(procInfo.procOwner);

   if (failed) {
      ProcMgr_FreeProcList(procList);
      procList = NULL;
   }

   return procList;
}
#endif // defined(__linux__)


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ListProcesses --
 *
 *      List all the processes that the calling client has privilege to
 *      enumerate. The strings in the returned structure should be all
 *      UTF-8 encoded, although we do not enforce it right now.
 *
 * Results:
 *
 *      A ProcMgrProcInfoArray.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

#if defined(__FreeBSD__)
ProcMgrProcInfoArray *
ProcMgr_ListProcesses(void)
{
   ProcMgrProcInfoArray *procList = NULL;
   ProcMgrProcInfo procInfo;
   Bool failed = TRUE;
   static kvm_t *kd;
   struct kinfo_proc *kp;
   char errbuf[_POSIX2_LINE_MAX];
   int i;
   int nentries=-1;
   int flag=0;

   procList = Util_SafeCalloc(1, sizeof *procList);
   procInfo.procCmdName = NULL;
   procInfo.procCmdLine = NULL;
   procInfo.procOwner = NULL;

   /*
    * Get the handle to the Kernel Virtual Memory
    */
   kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf);
   if (kd == NULL) {
      Warning("%s: failed to open kvm with error: %s\n", __FUNCTION__, errbuf);
      goto quit;
   }

   /*
    * Get the list of process info structs
    */
   kp = kvm_getprocs(kd, KERN_PROC_PROC, flag, &nentries);
   if (kp == NULL || nentries <= 0) {
      Warning("%s: failed to get proc infos with error: %s\n",
              __FUNCTION__, kvm_geterr(kd));
      goto quit;
   }

   /*
    * Pre-allocate the dynamic array of required size.
    */
   if (!ProcMgrProcInfoArray_Init(procList, nentries)) {
      Warning("%s: failed to create DynArray - out of memory\n",
              __FUNCTION__);
      goto quit;
   }

   /*
    * Iterate through the list of process entries
    */
   for (i = 0; i < nentries; ++i, ++kp) {
      struct passwd *pwd;
      char **cmdLineTemp = NULL;
      char *cmdNameBegin = NULL;
      Bool cmdNameLookup = TRUE;

      /*
       * Store the pid of the process.
       */
      procInfo.procId = kp->ki_pid;

      /*
       * Store the owner of the process.
       */
      pwd = getpwuid(kp->ki_uid);
      procInfo.procOwner = (NULL == pwd)
                           ? Str_SafeAsprintf(NULL, "%d", (int) kp->ki_uid)
                           : Unicode_Alloc(pwd->pw_name, STRING_ENCODING_DEFAULT);

      /*
       * If the command name in the kinfo_proc struct is strictly less than the
       * maximum allowed size, then we can save it right now. Else we shall
       * need to try and parse it from the entire command line.
       */
      if (strlen(kp->ki_comm) + 1 < sizeof kp->ki_comm) {
         procInfo.procCmdName = Unicode_Alloc(kp->ki_comm, STRING_ENCODING_DEFAULT);
         cmdNameLookup = FALSE;
      }

      /*
       * Store the command line string of the process.
       */
      cmdLineTemp = kvm_getargv(kd, kp, 0);
      if (cmdLineTemp != NULL) {
         /*
          * Flatten the argument list to get cmd & all params.
          */
         DynBuf dbuf;

         DynBuf_Init(&dbuf);
         while (*cmdLineTemp != NULL) {
            if (!DynBuf_Append(&dbuf, *cmdLineTemp, strlen(*cmdLineTemp))) {
               Warning("%s: failed to append cmd/args in DynBuf - no memory\n",
                       __FUNCTION__);
               goto quit;
            }
            if (cmdNameLookup) {
               /*
                * Store the command name of the process.
                * Find the last path separator, to get the cmd name.
                * If no separator is found, then use the whole name.
                */
               cmdNameBegin = strrchr(*cmdLineTemp, '/');
               if (NULL == cmdNameBegin) {
                  cmdNameBegin = *cmdLineTemp;
               } else {
                  /*
                   * Skip over the last separator.
                   */
                  cmdNameBegin++;
               }
               procInfo.procCmdName = Unicode_Alloc(cmdNameBegin, STRING_ENCODING_DEFAULT);
               cmdNameLookup = FALSE;
            }
            cmdLineTemp++;
            if (*cmdLineTemp != NULL) {
               /*
                * Add the whitespace between arguments.
                */
               if (!DynBuf_Append(&dbuf, " ", 1)) {
                  Warning("%s: failed to append ' ' in DynBuf - no memory\n",
                          __FUNCTION__);
                  goto quit;
               }
            }
         }
         /*
          * Add the NUL term.
          */
         if (!DynBuf_Append(&dbuf, "", 1)) {
            Warning("%s: failed to append NUL in DynBuf - out of memory\n",
                    __FUNCTION__);
            goto quit;
         }
         DynBuf_Trim(&dbuf);
         procInfo.procCmdLine = DynBuf_Detach(&dbuf);
         DynBuf_Destroy(&dbuf);
      } else {
         procInfo.procCmdLine = Unicode_Alloc(kp->ki_comm, STRING_ENCODING_DEFAULT);
         if (cmdNameLookup) {
            procInfo.procCmdName = Unicode_Alloc(kp->ki_comm, STRING_ENCODING_DEFAULT);
            cmdNameLookup = FALSE;
         }
      }

      /*
       * Store the start time of the process
       */
      procInfo.procStartTime = kp->ki_start.tv_sec;

      /*
       * Store the process info pointer into a list buffer.
       */
      *ProcMgrProcInfoArray_AddressOf(procList, i) = procInfo;
      procInfo.procCmdLine = NULL;
      procInfo.procCmdName = NULL;
      procInfo.procOwner = NULL;

   } // for nentries

   failed = FALSE;

quit:
   if (kd != NULL) {
      kvm_close(kd);
   }

   free(procInfo.procCmdLine);
   free(procInfo.procCmdName);
   free(procInfo.procOwner);

   if (failed) {
      ProcMgr_FreeProcList(procList);
      procList = NULL;
   }

   return procList;
}
#endif // defined(__FreeBSD__)


#if defined(__APPLE__)
/*
 *----------------------------------------------------------------------
 *
 * ProcMgrGetCommandLineArgs --
 *
 *      Fetch all the command line arguments for a given process id.
 *      The argument names shall all be UTF-8 encoded.
 *
 * Results:
 *      Number of arguments retrieved.
 *      Buffer is returned with argument names, if any.
 *      Command name is returned.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

static int
ProcMgrGetCommandLineArgs(long pid,               // IN:  process id
                          DynBuf *argsBuf,        // OUT: Buffer with arguments
                          char **procCmdName)     // OUT: Command name string
{
   int argCount = 0;
   int argNum;
   char *argUnicode = NULL;
   char *cmdNameBegin = NULL;
   char *cmdLineRaw = NULL;
   char *cmdLineTemp;
   char *cmdLineEnd;
   size_t maxargs = 0;
   size_t maxargsSize;
   int maxargsName[] = {CTL_KERN, KERN_ARGMAX};
   int argName[] = {CTL_KERN, KERN_PROCARGS2, pid};
   Bool cmdNameLookup = TRUE;
   Bool failed = TRUE;

   if (NULL != procCmdName) {
      *procCmdName = NULL;
   }

   /*
    * Get the sysctl kern argmax.
    */
   maxargsSize = sizeof maxargs;
   if (sysctl(maxargsName, ARRAYSIZE(maxargsName),
              &maxargs, &maxargsSize, NULL, 0) < 0) {
      Warning("%s: failed to get the kernel max args with errno = %d\n",
               __FUNCTION__, errno);
      goto quit;
   }

   /*
    * Fetch the raw command line
    */
   cmdLineRaw = Util_SafeCalloc(maxargs, sizeof *cmdLineRaw);
   if (sysctl(argName, ARRAYSIZE(argName), cmdLineRaw, &maxargs, NULL, 0) < 0) {
      Debug("%s: No command line args for pid = %ld\n", __FUNCTION__, pid);
      goto quit;
   }
   cmdLineEnd = &cmdLineRaw[maxargs];

   /*
    * Format of the raw command line (without line breaks):
    * <argc value><full command path>
    * <one or more '\0' for alignment of first arg>
    * <arg-0 = command as typed><'\0'>
    * <arg-1><'\0'>... <arg-(argc-1))><'\0'>
    * <env-0><'\0'>... <env-n><'\0'>
    * where:
    * arg = command line args we want.
    * env = environment vars we ignore.
    */

   /*
    * Save the number of arguments.
    */
   memcpy(&argNum, cmdLineRaw, sizeof argNum);
   if (0 >= argNum) {
      Debug("%s: Invalid number of command line args (=%d) for pid = %ld\n",
             __FUNCTION__, argNum, pid);
      goto quit;
   }

   /*
    * Skip over the number of args (argc value) and
    * full path to command name in the command line.
    * (Please refer to format in comment above.)
    */
   cmdLineTemp = cmdLineRaw + sizeof argNum;
   cmdLineTemp += strlen(cmdLineTemp) + 1;

   /*
    * Save the arguments one by one
    */
   while (cmdLineTemp < cmdLineEnd && argCount < argNum) {
      /*
       * Skip over leading '\0' chars to reach new arg.
       */
      while (cmdLineTemp < cmdLineEnd && '\0' == *cmdLineTemp) {
         ++cmdLineTemp;
      }
      /*
       * If we are pointing to a valid arg, save it.
       */
      if (cmdLineTemp < cmdLineEnd && '\0' != *cmdLineTemp) {
         /*
          * KERN_PROCARGS2 is not guaranteed to provide argument names in UTF-8.
          * As long as we find UTF-8 argument names, we keep adding to our list.
          * As soon as we see any non UTF-8 argument, we ignore that argument
          * and return the list we have built so far.
          * NOTE: On MacOS, STRING_ENCODING_DEFAULT will default to UTF-8.
          */
         if (Unicode_IsBufferValid(cmdLineTemp, -1, STRING_ENCODING_DEFAULT) &&
             (argUnicode = Unicode_Alloc(cmdLineTemp,
                                          STRING_ENCODING_DEFAULT)) != NULL) {
            /*
             * Add the whitespace between arguments.
             */
            if (0 < argCount) {
               if (!DynBuf_Append(argsBuf, " ", 1)) {
                  Warning("%s: failed to append ' ' in DynBuf\
                           - no memory\n", __FUNCTION__);
                  goto quit;
               }
            }
            /*
             * Add the argument.
             */
            if (!DynBuf_Append(argsBuf, argUnicode, strlen(argUnicode))) {
               Warning("%s: failed to append cmd/args in DynBuf\
                        - no memory\n", __FUNCTION__);
               goto quit;
            }
            free(argUnicode);
            argUnicode = NULL;
         } else {
            break;
         }
         ++argCount;
         /*
          * If this is the command name, retrieve it.
          */
         if (NULL != procCmdName && cmdNameLookup) {
            /*
             * Store the command name of the process.
             * Find the last path separator, to get the cmd name.
             * If no separator is found, then use the whole name.
             */
            cmdNameBegin = strrchr(cmdLineTemp, '/');
            if (NULL == cmdNameBegin) {
               cmdNameBegin = cmdLineTemp;
            } else {
               /*
                * Skip over the last separator.
                */
               cmdNameBegin++;
            }
            *procCmdName = Unicode_Alloc(cmdNameBegin, STRING_ENCODING_DEFAULT);
            cmdNameLookup = FALSE;
         }
      }
      /*
       * Skip over the current arg that we just saved.
       */
      while (cmdLineTemp < cmdLineEnd && '\0' != *cmdLineTemp) {
         ++cmdLineTemp;
      }
   }

   /*
    * Add the NULL term.
    */
   if (!DynBuf_Append(argsBuf, "", 1)) {
      Warning("%s: failed to append NUL in DynBuf - out of memory\n",
              __FUNCTION__);
      goto quit;
   }
   DynBuf_Trim(argsBuf);

   failed = FALSE;
quit:
   free(cmdLineRaw);
   free(argUnicode);
   if (failed) {
      if (NULL != procCmdName) {
         free(*procCmdName);
         *procCmdName = NULL;
      }
      argCount = 0;
   }

   return argCount;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ListProcesses --
 *
 *      List all the processes that the calling client has privilege to
 *      enumerate. The strings in the returned structure should be all
 *      UTF-8 encoded, although we do not enforce it right now.
 *
 * Results:
 *      A ProcMgrProcInfoArray.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

ProcMgrProcInfoArray *
ProcMgr_ListProcesses(void)
{
   ProcMgrProcInfoArray *procList = NULL;
   ProcMgrProcInfo procInfo;
   Bool failed = TRUE;
   struct kinfo_proc *kptmp;
   struct kinfo_proc *kp = NULL;
   size_t procsize;
   int i;
   int nentries;
   int procName[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};

   procList = Util_SafeCalloc(1, sizeof *procList);
   procInfo.procCmdLine = NULL;
   procInfo.procCmdName = NULL;
   procInfo.procOwner = NULL;

   /*
    * Get the number of process info structs in the entire list.
    */
   if (sysctl(procName, ARRAYSIZE(procName), NULL, &procsize, NULL, 0) < 0) {
      Warning("%s: failed to get the size of the process struct\
               list with errno = %d\n", __FUNCTION__, errno);
      goto quit;
   }
   nentries = (int)(procsize / sizeof *kp);

   /*
    * Get the list of process info structs.
    */
   kp = Util_SafeCalloc(nentries, sizeof *kp);
   if (sysctl(procName, ARRAYSIZE(procName), kp, &procsize, NULL, 0) < 0) {
      Warning("%s: failed to get the process struct list (errno = %d)\n",
               __FUNCTION__, errno);
      goto quit;
   }

   /*
    * Recalculate the number of entries as they may have changed.
    */
   if (0 >= (nentries = (int)(procsize / sizeof *kp))) {
      goto quit;
   }

   /*
    * Pre-allocate the dynamic array of required size.
    */
   if (!ProcMgrProcInfoArray_Init(procList, nentries)) {
      Warning("%s: failed to create DynArray - out of memory\n",
              __FUNCTION__);
      goto quit;
   }

   /*
    * Iterate through the list of process entries
    */
   for (i = 0, kptmp = kp; i < nentries; ++i, ++kptmp) {
      DynBuf argsBuf;
      char buffer[BUFSIZ];
      struct passwd pw;
      struct passwd *ppw = &pw;
      int error;
      int argCount = 0;
      Bool cmdNameLookup = TRUE;

      /*
       * Store the pid of the process
       */
      procInfo.procId = kptmp->kp_proc.p_pid;

      /*
       * Store the owner of the process.
       */
      error = Posix_Getpwuid_r(kptmp->kp_eproc.e_pcred.p_ruid,
                               &pw, buffer, sizeof buffer, &ppw);
      procInfo.procOwner = (0 != error || NULL == ppw)
                           ? Str_SafeAsprintf(NULL, "%d", (int) kptmp->kp_eproc.e_pcred.p_ruid)
                           : Unicode_Alloc(ppw->pw_name, STRING_ENCODING_DEFAULT);

      /*
       * If the command name in the kinfo_proc struct is strictly less than the
       * maximum allowed size, then we can save it right now. Else we shall
       * need to try and parse it from the entire command line.
       */
      if (strlen(kptmp->kp_proc.p_comm) + 1 < sizeof kptmp->kp_proc.p_comm) {
         procInfo.procCmdName = Unicode_Alloc(kptmp->kp_proc.p_comm, STRING_ENCODING_DEFAULT);
         cmdNameLookup = FALSE;
      }

      /*
       * Store the command line arguments of the process.
       * If no arguments are found, use the full command name.
       */
      DynBuf_Init(&argsBuf);
      if (cmdNameLookup) {
         argCount = ProcMgrGetCommandLineArgs(kptmp->kp_proc.p_pid, &argsBuf, &procInfo.procCmdName);
      } else {
         argCount = ProcMgrGetCommandLineArgs(kptmp->kp_proc.p_pid, &argsBuf, NULL);
      }
      if (0 < argCount) {
         procInfo.procCmdLine = DynBuf_Detach(&argsBuf);
         /*
          * cmdName would have been filled up by the function ProcMgrGetCommandLineArgs().
          */
         cmdNameLookup = FALSE;
      } else {
         procInfo.procCmdLine = Unicode_Alloc(kptmp->kp_proc.p_comm, STRING_ENCODING_DEFAULT);
         if (cmdNameLookup) {
            procInfo.procCmdName = Unicode_Alloc(kptmp->kp_proc.p_comm, STRING_ENCODING_DEFAULT);
            cmdNameLookup = FALSE;
         }
      }
      DynBuf_Destroy(&argsBuf);

      /*
       * Store the start time of the process
       */
      procInfo.procStartTime = kptmp->kp_proc.p_starttime.tv_sec;

      /*
       * Store the process info pointer into a list buffer.
       */
      *ProcMgrProcInfoArray_AddressOf(procList, i) = procInfo;

      procInfo.procCmdLine = NULL;
      procInfo.procCmdName = NULL;
      procInfo.procOwner = NULL;

   } // nentries

   failed = FALSE;

quit:
   free(kp);
   free(procInfo.procCmdLine);
   free(procInfo.procCmdName);
   free(procInfo.procOwner);

   if (failed) {
      ProcMgr_FreeProcList(procList);
      procList = NULL;
   }

   return procList;
}
#endif // defined(__APPLE__)

/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_FreeProcList --
 *
 *      Free the memory occupied by ProcMgrProcInfoArray.
 *
 * Results:
 *
 *      None.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

void
ProcMgr_FreeProcList(ProcMgrProcInfoArray *procList)
{
   int i;
   size_t procCount;

   if (NULL == procList) {
      return;
   }

   procCount = ProcMgrProcInfoArray_Count(procList);
   for (i = 0; i < procCount; i++) {
      ProcMgrProcInfo *procInfo = ProcMgrProcInfoArray_AddressOf(procList, i);
      free(procInfo->procCmdName);
#if defined(__linux__)
      free(procInfo->procCmdAbsPath);
#endif
      free(procInfo->procCmdLine);
      free(procInfo->procOwner);
   }

   ProcMgrProcInfoArray_Destroy(procList);
   free(procList);
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgrExecSync --
 *
 *      Synchronously execute a command. The command is UTF-8 encoded.
 *
 *      Note, if the caller requests the exitCode for the process to be
 *      run they must also provide the validExitCode to determine
 *      if the exitCode contains a valid value.
 *
 * Results:
 *      - TRUE when program execution completed and succeeded and
 *        *validExitCode contains TRUE if supplied and
 *        *exitCode contains 0 if supplied by the caller
 *
 *      - FALSE when program execution failed to start the process and
 *        *validExitCode contains FALSE if supplied by the caller and
          *exitCode is not valid if also supplied.
 *
 *      - FALSE when program execution completed and failed and
 *        *validExitCode contains TRUE if supplied and
 *        *exitCode contains the exit code of the failed process if supplied
 *
 * Side effects:
 *      Lots, depending on the program.
 *
 *----------------------------------------------------------------------
 */

static Bool
ProcMgrExecSync(char const *cmd,                  // IN: UTF-8 command line
                ProcMgr_ProcArgs *userArgs,       // IN: optional
                Bool *validExitCode,              // OUT: optional exit code is valid
                int *exitCode)                    // OUT: optional exit code
{
   pid_t pid;

   ASSERT(cmd != NULL);
   Debug("Executing sync command: %s\n", cmd);

   if (validExitCode != NULL) {
      *validExitCode = FALSE;
   }

   pid = ProcMgrStartProcess(cmd, userArgs ? userArgs->envp : NULL,
                             userArgs ? userArgs->workingDirectory : NULL);

   if (pid == -1) {
      return FALSE;
   }

   return ProcMgrWaitForProcCompletion(pid, validExitCode, exitCode);
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ExecSync --
 *
 *      Synchronously execute a command. The command is UTF-8 encoded.
 *
 * Results:
 *      TRUE on success (the program had an exit code of 0)
 *      FALSE on failure or if an error occurred (detail is displayed)
 *
 * Side effects:
 *	Lots, depending on the program.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_ExecSync(char const *cmd,                  // IN: UTF-8 command line
                 ProcMgr_ProcArgs *userArgs)       // IN: optional
{
   return ProcMgrExecSync(cmd, userArgs, NULL, NULL);
}


#if defined(__linux__)
/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ExecSyncWithExitCode --
 *
 *      Synchronously execute a command. The command is UTF-8 encoded.
 *
 * Results:
 *      - TRUE when program execution completed and succeeded and
 *        *validExitCode contains TRUE and
 *        *exitCode contains 0
 *
 *      - FALSE when either exitCode or validExitCode are not valid
 *
 *      - FALSE when program execution failed to start the process and
 *        *validExitCode contains FALSE by the caller and
          *exitCode is not valid.
 *
 *      - FALSE when program execution completed and failed and
 *        *validExitCode contains TRUE
 *        *exitCode contains the exit code of the failed process
 *
 * Side effects:
 *	Lots, depending on the program.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_ExecSyncWithExitCode(char const *cmd,                  // IN: UTF-8 command line
                             ProcMgr_ProcArgs *userArgs,       // IN: optional
                             Bool *validExitCode,              // OUT: exit code is valid
                             int *exitCode)                    // OUT: exit code
{
   Bool result;

   ASSERT(exitCode != NULL && validExitCode != NULL);

   result = ProcMgrExecSync(cmd, userArgs, validExitCode, exitCode);

   Debug("Executed sync command: %s -> %d (%d)\n",
         cmd, result, *validExitCode ? *exitCode : 0);
   return result;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * ProcMgrStartProcess --
 *
 *      Fork and execute a command using the shell. This function returns
 *      immediately after the fork() in the parent process.
 *
 * Results:
 *      The pid of the forked process, or -1 on an error.
 *
 * Side effects:
 *	Lots, depending on the program
 *
 *----------------------------------------------------------------------
 */

static pid_t
ProcMgrStartProcess(char const *cmd,            // IN: UTF-8 encoded cmd
                    char * const *envp,         // IN: UTF-8 encoded env vars
                    char const *workingDir)     // IN: UTF-8 working directory
{
   pid_t pid;
   char *cmdCurrent = NULL;
   char **envpCurrent = NULL;
   char *workDir = NULL;

   ASSERT(cmd != NULL);

   /*
    * Convert the strings before the call to fork(), since the conversion
    * routines may rely on locks that do not survive fork().
    */

   if (!CodeSet_Utf8ToCurrent(cmd, strlen(cmd), &cmdCurrent, NULL)) {
      SAFE_WARNING("Could not convert from UTF-8 to current\n");
      SET_OFFSPRING_EXIT_CODE(PROCMGR_ERROR_CMD_ENCODE);
      return -1;
   }

   if ((NULL != workingDir) &&
       !CodeSet_Utf8ToCurrent(workingDir, strlen(workingDir), &workDir, NULL)) {
      SAFE_WARNING("Could not convert workingDir from UTF-8 to current\n");
      SET_OFFSPRING_EXIT_CODE(PROCMGR_ERROR_WORKDIR_ENCODE);
      return -1;
   }

   if (NULL != envp) {
      envpCurrent = Unicode_GetAllocList(envp, -1, STRING_ENCODING_DEFAULT);
   }

#ifdef USERWORLD
   do {
      static const char filePath[] = "/bin/sh";
      char * const argv[] = { "sh", "++group=host/vim/tmp",
                              "-c", cmdCurrent, NULL };
      int initFds[] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO };
      int workingDirFd;
      VmkuserStatus_Code status;
      int outPid;

      workingDirFd = open(workingDir != NULL ? workingDir : "/tmp", O_RDONLY);
      status = VmkuserCompat_ForkExec(filePath,
                                      argv,
                                      envpCurrent,
                                      workingDirFd,
                                      initFds,
                                      ARRAYSIZE(initFds),
                                      geteuid(),
                                      getegid(),
                                      0,
                                      &outPid);
      if (workingDirFd >= 0) {
         close(workingDirFd);
      }
      if (VmkuserStatus_IsOK(status)) {
         pid = (pid_t)outPid;
      } else {
         VmkuserStatus_CodeToErrno(status, &errno);
         SET_OFFSPRING_EXIT_CODE(PROCMGR_ERROR_VMK_FORKEXEC);
         pid = -1;
      }
   } while (FALSE);
#else
   pid = fork();

   if (pid == -1) {
      SAFE_WARNING("Unable to fork: %s.\n\n", strerror(errno));
      SET_OFFSPRING_EXIT_CODE(PROCMGR_ERROR_FORK);
   } else if (pid == 0) {
      static const char bashShellPath[] = BASH_PATH;
      char *bashArgs[] = { "bash", "-c", cmdCurrent, NULL };
      static const char bourneShellPath[] = "/bin/sh";
      char *bourneArgs[] = { "sh", "-c", cmdCurrent, NULL };
      const char *shellPath;
      char **args;

      /*
       * Child
       */
      gOffspringProcess = TRUE;
      gOffspringExitCode = 0;

      /*
       * Check bug 772203. To start the program, we start the shell
       * and specify the program using the option '-c'. We should return the
       * PID of the app that gets started.
       *
       * When the option '-c' is specified,
       * - bash shell just uses exec() to replace itself. So, 'bash' returns
       * the PID of the new application that is started.
       *
       * - bourne shell does a fork & exec. So two processes are started. We
       * see the PID of the shell and not the app that it starts. When the PID
       * is returned to a user to watch, they'll watch the wrong process.
       *
       * In order to return the proper PID, use bash if possible. If bash
       * is not available, then use the bourne shell.
       */
      if (File_Exists(bashShellPath)) {
         shellPath = bashShellPath;
         args = bashArgs;
      } else {
         shellPath = bourneShellPath;
         args = bourneArgs;
      }

#ifdef __APPLE__
      /*
       * On OS X with security fixes, we cannot revert the real uid if
       * its changed, so only the effective uid is changed.  But for
       * running programs we need both.  See comments for
       * ProcMgr_ImpersonateUserStart() for details.
       *
       * If it fails, bail since its a security issue if real uid is still
       * root.
       */
      if (!ProcMgr_PromoteEffectiveToReal()) {
         SAFE_PANIC("%s: Could not set real uid to effective\n",
                    __FUNCTION__);
      }
#endif

      if (NULL != workDir) {
         if (chdir(workDir) != 0) {
            SAFE_WARNING("%s: Could not chdir(%s) %s\n", __FUNCTION__,
                         workDir, strerror(errno));
         }
      }

      if (NULL != envpCurrent) {
         execve(shellPath, args, envpCurrent);
      } else  {
         execv(shellPath, args);
      }

      /* Failure */
      SAFE_PANIC("Unable to execute the \"%s\" shell command: %s.\n\n",
                 cmd, strerror(errno));
   }
#endif

   /*
    * Parent
    */

   free(cmdCurrent);
   free(workDir);
   Util_FreeStringList(envpCurrent, -1);
   return pid;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgrWaitForProcCompletion --
 *
 *      Waits until the process identified by 'pid' exits or is otherwise
 *      terminated.
 *
 * Results:
 *      TRUE on success (the program had an exit code of 0)
 *      FALSE on failure or if an error occurred (detail is displayed)
 *
 * Side effects:
 *      Prevents zombification of the process.
 *
 *----------------------------------------------------------------------
 */

static Bool
ProcMgrWaitForProcCompletion(pid_t pid,                 // IN
                             Bool *validExitCode,       // OUT: Optional
                             int *exitCode)             // OUT: Optional
{
   Bool retVal;
   int childStatus;

   if (NULL != validExitCode) {
      *validExitCode = FALSE;
   }

   for (;;) {
      pid_t status;

      status = waitpid(pid, &childStatus, 0);
      if (status == pid) {
         /* Success */
         break;
      }

      if (   status == (pid_t)-1
          && errno == EINTR) {
         /* System call interrupted by a signal */
         continue;
      }

      SAFE_WARNING("Unable to wait for the process %"FMTPID" to terminate: "
                   "%s.\n\n", pid, strerror(errno));
      SET_OFFSPRING_EXIT_CODE(PROCMGR_ERROR_WAITPID);
      return FALSE;
   }

   if ((NULL != validExitCode) && (NULL != exitCode)) {
      *validExitCode = WIFEXITED(childStatus);
      *exitCode = WEXITSTATUS(childStatus);
   }

   retVal = (WIFEXITED(childStatus) && WEXITSTATUS(childStatus) == 0);

   SAFE_DEBUG("Done waiting for process: %"FMTPID" (%s)\n", pid,
              retVal ? "success" : "failure");

   return retVal;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ExecAsync --
 *
 *      Execute a command in the background, returning immediately.
 *
 * Results:
 *      The async proc (must be freed) or
 *      NULL if the cmd failed to be forked.
 *
 * Side effects:
 *      The cmd is run.
 *      ProcMgrStartProcess sets gOffspringExitCode on failure.
 *
 *----------------------------------------------------------------------
 */

ProcMgr_AsyncProc *
ProcMgr_ExecAsync(char const *cmd,                 // IN: UTF-8 command line
                  ProcMgr_ProcArgs *userArgs)      // IN: optional
{
   ProcMgr_AsyncProc *asyncProc = NULL;
   pid_t pid;
   int fds[2];
   pid_t resultPid;
   int readFd, writeFd;

   ASSERT(cmd != NULL);
   Debug("Executing async command: '%s' in working dir '%s'\n",
         cmd, (userArgs && userArgs->workingDirectory) ? userArgs->workingDirectory : "");

   if (pipe(fds) == -1) {
      Warning("Unable to create the pipe to launch command: %s.\n", cmd);
      return NULL;
   }

   readFd = fds[0];
   writeFd = fds[1];

   pid = fork();

   if (pid == -1) {
      Warning("Unable to fork: %s.\n\n", strerror(errno));
      goto quit;
   } else if (pid == 0) {
      struct sigaction olds[ARRAYSIZE(cSignals)];
      int i, maxfd;
      Bool status = TRUE;
      pid_t childPid = -1;
      Bool validExitCode = FALSE;
      int exitCode = -1;

      /*
       * Child
       */
      gOffspringProcess = TRUE;
      gOffspringExitCode = 0;

      /*
       * shut down everything but stdio and the pipe() we just made.
       * leaving all the other fds behind can cause nastiness with the X
       * connection and I/O errors, and make wait() hang.
       *
       * should probably call Hostinfo_ResetProcessState(), but that
       * does some stuff with iopl() we don't need
       */
      maxfd = sysconf(_SC_OPEN_MAX);
      for (i = STDERR_FILENO + 1; i < maxfd; i++) {
         if (i != readFd && i != writeFd) {
            close(i);
         }
      }

      close(readFd);

      /*
       * Child should not invoke parent's logging facilities as logging mutex
       * could have been locked at fork time. Also, logging file descriptors
       * have already been closed now.
       * Child shall terminate with _exit() or abort() to avoid calling any
       * functions registered with atexit() or on_exit() in the parent.
       */

      if (Signal_SetGroupHandler(cSignals, olds, ARRAYSIZE(cSignals),
#ifndef sun
                                 SIG_DFL
#else
                                 0
#endif
                                 ) == 0) {
         gOffspringExitCode = PROCMGR_ERROR_SET_SIG_HANDLER;
         status = FALSE;
      }

      /*
       * Only run the program if we have not already experienced a failure.
       */
      if (status) {
         childPid = ProcMgrStartProcess(cmd,
                                        userArgs ? userArgs->envp : NULL,
                                        userArgs ? userArgs->workingDirectory :
                                                   NULL);
         status = childPid != -1;
      }

      /*
       * Send the child's pid back immediately, so that the caller can
       * report the result pid back synchronously.
       */
      if (write(writeFd, &childPid, sizeof childPid) == -1) {
         SAFE_WARNING("Waiter unable to write back to parent.\n");

         /*
          * This is quite bad, since the original process will block
          * waiting for data. Unfortunately, there isn't much to do
          * (other than trying some other IPC mechanism).
          */
         EXIT_ON_ERROR_WRITE_PIPE();
      }

      if (status) {
         /*
          * If everything has gone well so far, then wait until the child
          * finishes executing.
          */
         ASSERT(pid != -1);
         status = ProcMgrWaitForProcCompletion(childPid, &validExitCode,
                                               &exitCode);
      }

      /*
       * We always have to send IPC back to caller, so that it does not
       * block waiting for data we'll never send.
       */
      SAFE_DEBUG("Writing the command %s a success to fd %x\n",
                 status ? "was" : "was not", writeFd);
      if (write(writeFd, &status, sizeof status) == -1) {
         SAFE_WARNING("Waiter unable to write back to parent\n");

         /*
          * This is quite bad, since the original process will block
          * waiting for data. Unfortunately, there isn't much to do
          * (other than trying some other IPC mechanism).
          */
         EXIT_ON_ERROR_WRITE_PIPE();
      }

      if (write(writeFd, &exitCode, sizeof exitCode) == -1) {
         SAFE_WARNING("Waiter unable to write back to parent\n");

         /*
          * This is quite bad, since the original process will block
          * waiting for data. Unfortunately, there isn't much to do
          * (other than trying some other IPC mechanism).
          */
         EXIT_ON_ERROR_WRITE_PIPE();
      }

      close(writeFd);

      if (status &&
          Signal_ResetGroupHandler(cSignals, olds, ARRAYSIZE(cSignals)) == 0) {
         /*
          * We are too close to give up now.
          */
      }

      if (validExitCode) {
         gOffspringExitCode = exitCode;
      }

      _exit(gOffspringExitCode);
   }

   /*
    * Parent
    */

   close(writeFd);
   writeFd = -1;

   /*
    * Read the pid of the child's child from the pipe.
    */
   if (read(readFd, &resultPid, sizeof resultPid) != sizeof resultPid) {
      Warning("Unable to read result pid from the pipe.\n");

      /*
       * We cannot wait on the child process here, since the error
       * may have just been on our end, so the child could be running
       * for some time and we probably cannot afford to block.
       * Just force the child to quit and move on.
       */
      ProcMgrKill(pid, SIGKILL, -1);
      goto quit;
   }

   if (resultPid == -1) {
      Warning("The child failed to fork the target process.\n");

      /*
       * Clean up the child process; it should exit pretty quickly.
       */
      WaitOffspring(pid);
      goto quit;
   }

   asyncProc = Util_SafeMalloc(sizeof *asyncProc);
   asyncProc->fd = readFd;
   readFd = -1;
   asyncProc->waiterPid = pid;
   asyncProc->validExitCode = FALSE;
   asyncProc->exitCode = -1;
   asyncProc->resultPid = resultPid;

 quit:
   if (readFd != -1) {
      close(readFd);
   }
   if (writeFd != -1) {
      close(writeFd);
   }

   return asyncProc;
}

/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_IsProcessRunning --
 *
 *      Check to see if a pid is active
 *
 * Results:
 *      TRUE if the process exists; FALSE otherwise
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static Bool
ProcMgr_IsProcessRunning(pid_t pid)
{
   return ((kill(pid, 0) == 0) || (errno == EPERM));
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgrKill --
 *
 *      Try to force a pid to quit and check every so often to see
 *      if it has ended.
 *
 * Results:
 *      1 if the process died; 0 on failure, -1 on timeout
 *
 * Side effects:
 *	Depends on the program being forced to quit.
 *	errno set.
 *
 *----------------------------------------------------------------------
 */

static int
ProcMgrKill(pid_t pid,      // IN
            int sig,        // IN
            int timeout)    // IN: -1 will wait indefinitely
{
   if (kill(pid, sig) == -1) {
      int savedErrno = errno;
      Warning("Error trying to cancel process %"FMTPID" with signal %d: %s\n",
              pid, sig, Msg_ErrString());
      errno = savedErrno;
      return 0;
   } else {
      int i;

      /* Try every 100ms until we've reached the timeout */
      for (i = 0; timeout == -1 || i < timeout * 10; i++) {
         int ret;

         ret = waitpid(pid, NULL, WNOHANG);

         if (ret == -1) {
            /*
             * if we didn't start it, we can only check if its running
             * by looking in the proc table.
             *
             * Note that this is susceptible to a race.  Its possible
             * that just as we force the process to end, a new one can come
             * around and re-use the pid by the time we check on it.
             * This requires the pids have wrapped and a lot of luck.
             */
            if (ECHILD == errno) {
               if (ProcMgr_IsProcessRunning(pid)) {
                  Debug("Process %"FMTPID" is not a child, still running\n",
                        pid);
                  usleep(100000);
                  continue;
               }
               return 1;
            }
            Warning("Error trying to wait on process %"FMTPID": %s\n",
                    pid, Msg_ErrString());
         } else if (ret == 0) {
            usleep(100000);
         } else {
            Debug("Process %"FMTPID" died from signal %d on iteration #%d\n",
                  pid, sig, i);
            return 1;
         }
      }
   }

   /*
    * timed out -- system/process is incredibly unresponsive or cannot be
    * forced to quit.
    */
   Warning("%s: timed out trying to cancel pid %"FMTPID" with signal %d\n",
           __FUNCTION__, pid, sig);
   return -1;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_KillByPid --
 *
 *      Attempt to terminate the process of procId.
 *      First try SIGTERM for 5 seconds, then SIGKILL for 15
 *      if that is unsuccessful.
 *
 * Results:
 *      Bool.
 *
 * Side effects:
 *	Lots, depending on the program
 *	errno is set.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_KillByPid(ProcMgr_Pid procId)   // IN
{
   int ret;

   ret = ProcMgrKill(procId, SIGTERM, 5);
   if (ret != 1) {
      /*
       * We can't try forever, since some processes are immortal (eg systemd),
       * or a process could be stuck 'forever' in a disk wait.
       * 5+15 seconds should be long enough to handle very slow systems, while
       * not causing timeouts at the VMX layer or the guestInfo gathering.
       */
      ret = ProcMgrKill(procId, SIGKILL, 15);
      if (ret == -1) {
         /*
          * Timed out, so fake an errno.  Deadlock is what would happen
          * to ProcMgrKill() if it tried indefinitely.
          */
         errno = EDEADLK;
      }
   }

   return (ret == 1);
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_Kill --
 *
 *      Synchronously try to force a process to quit with SIGTERM.
 *      If that fails use SIGKILL.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Depends on the program being forced to quit.
 *
 *----------------------------------------------------------------------
 */

void
ProcMgr_Kill(ProcMgr_AsyncProc *asyncProc) // IN
{
   if ((asyncProc == NULL) || (asyncProc->waiterPid == -1)) {
      ASSERT(FALSE);
      return;
   }

   ProcMgr_KillByPid(asyncProc->waiterPid);
   asyncProc->waiterPid = -1;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_IsAsyncProcRunning --
 *
 *      Checks whether an async process is still running.
 *
 * Results:
 *      TRUE iff the process is still running.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_IsAsyncProcRunning(ProcMgr_AsyncProc *asyncProc) // IN
{
   int maxFd;
   fd_set readFds;
   struct timeval tv;
   int status;
   Selectable fd;

   ASSERT(asyncProc);

   /*
    * Do a select, not a read. This procedure may be called many times,
    * while polling another program. After it returns true, then the
    * watcher program will try to read the socket to get the IPC error
    * and the exit code.
    */
   fd = ProcMgr_GetAsyncProcSelectable(asyncProc);
   FD_ZERO(&readFds);
   FD_SET(fd, &readFds);
   maxFd = fd;

   tv.tv_sec = 0;
   tv.tv_usec = 0;

   status = select(maxFd + 1, &readFds, NULL, NULL, &tv);
   if (status == -1) {
      return(FALSE); // Not running
   } else if (status > 0) {
      return(FALSE); // Not running
   } else {
      return(TRUE); // Still running
   }
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_GetAsyncProcSelectable --
 *
 *      Get the selectable fd for an async proc struct.
 *
 * Results:
 *      The fd casted to a void *.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Selectable
ProcMgr_GetAsyncProcSelectable(ProcMgr_AsyncProc *asyncProc)
{
   ASSERT(asyncProc);

   return asyncProc->fd;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_GetPid --
 *
 *      Get the pid for an async proc struct.
 *
 * Results:
 *
 * Side effects:
 *
 *	None.
 *
 *----------------------------------------------------------------------
 */

ProcMgr_Pid
ProcMgr_GetPid(ProcMgr_AsyncProc *asyncProc)
{
   ASSERT(asyncProc);

   return asyncProc->resultPid;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_GetExitCode --
 *
 *      Get the exit code status of an async process. Waits on the child
 *      process so that its resources are cleaned up.
 *
 * Results:
 *      0 if successful, -1 if not.
 *
 * Side effects:
 *	     None.
 *
 *----------------------------------------------------------------------
 */

int
ProcMgr_GetExitCode(ProcMgr_AsyncProc *asyncProc,  // IN
                    int *exitCode)                 // OUT
{
   ASSERT(asyncProc);
   ASSERT(exitCode);

   *exitCode = -1;

   if (asyncProc->waiterPid != -1) {
      Bool status;

      if (read(asyncProc->fd, &status, sizeof status) != sizeof status) {
         Warning("Error reading async process status.\n");
         goto exit;
      }

      if (read(asyncProc->fd, &asyncProc->exitCode,
               sizeof asyncProc->exitCode) != sizeof asyncProc->exitCode) {
         Warning("Error reading async process exitCode.\n");
         goto exit;
      }

      asyncProc->validExitCode = TRUE;

      Debug("Child w/ fd %x exited with code=%d\n",
            asyncProc->fd, asyncProc->exitCode);
   }

   *exitCode = asyncProc->exitCode;

exit:
   if (asyncProc->waiterPid != -1) {
      Debug("Waiting on pid %"FMTPID" to de-zombify it\n",
            asyncProc->waiterPid);
      WaitOffspring(asyncProc->waiterPid);
      asyncProc->waiterPid = -1;
   }
   return (asyncProc->exitCode == -1) ? -1 : 0;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_Free --
 *
 *      Discard the state of an async process. You must call one of
 *      ProcMgr_Kill(), ProcMgr_GetAsyncStatus(), or ProcMgr_GetExitCode()
 *      before calling this function to ensure that the child process
 *      is cleaned up.
 *
 *      That clean-up cannot occur here, since blocking with a waitpid()
 *      is an excessive side effect for a Free() function.
 *
 * Results:
 *      None
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
ProcMgr_Free(ProcMgr_AsyncProc *asyncProc) // IN
{
   /*
    * Make sure that we don't leak zombie processes.
    */
#ifdef VMX86_DEBUG
   if ((asyncProc != NULL) && (asyncProc->waiterPid != -1)) {
      /*
       * Someone did not call ProcMgr_Kill(), ProcMgr_GetAsyncStatus(),
       * or ProcMgr_GetExitCode().
       */
      Warning("Leaving process %"FMTPID" to be a zombie.\n",
              asyncProc->waiterPid);
   }
#endif

   if (asyncProc != NULL && asyncProc->fd != -1) {
      close(asyncProc->fd);
   }

   free(asyncProc);
}

#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)

/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ImpersonateUserStart --
 *
 *      Impersonate a user.  Much like bora/lib/impersonate, but
 *      changes the real and saved uid as well, to work with syscalls
 *      (access() and kill()) that look at real UID instead of effective.
 *      The user name should be UTF-8 encoded, although we do not enforce
 *      it right now.
 *
 *      Note that for OS X, we cannot set real uid.  Until a security
 *      patch for 10.10.3 (https://support.apple.com/en-us/HT204659)
 *      it worked, but since the patch once the real user has been
 *      changed, it cannot be restored.  So for OS X we set just
 *      the effective uid. This requires additional tweaks in
 *      ProcMgr_ExecAsync() to call ProcMgr_PromoteEffectiveToReal(),
 *      and preventing kill(2) from being called since it looks at
 *      the real uid.
 *
 *      Assumes it will be called as root.
 *
 * Results:
 *      TRUE on success
 *
 * Side effects:
 *
 *      Uid/gid set to given user, saved uid/gid left as root.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_ImpersonateUserStart(const char *user,  // IN: UTF-8 encoded user name
                             AuthToken token)   // IN
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   gid_t root_gid;
   int error;
   int ret;
   char *userLocal;

   if ((error = getpwuid_r(0, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      /*
       * getpwuid_r() and getpwnam_r() can return a 0 (success) but not
       * set the return pointer (ppw) if there's no entry for the user,
       * according to POSIX 1003.1-2003, so patch up the errno.
       */
      Warning("Failed to lookup user with uid: %" FMTUID ". Reason: %s\n", 0,
              error == 0 ? "entry not found" : Err_Errno2String(error));
      return FALSE;
   }

   root_gid = ppw->pw_gid;

   /* convert user name to local character set */
   userLocal = (char *)Unicode_GetAllocBytes(user, Unicode_GetCurrentEncoding());
   if (!userLocal) {
       Warning("Failed to convert user name %s to local character set.\n", user);
       return FALSE;
   }

   if ((error = getpwnam_r(userLocal, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      Warning("Failed to lookup user name %s. Reason: %s\n", userLocal,
              error == 0 ? "entry not found" : Err_Errno2String(error));
      free(userLocal);
      return FALSE;
   }

   free(userLocal);

   // first change group
#if defined(USERWORLD)
   ret = Id_SetREGid(ppw->pw_gid, ppw->pw_gid);
#elif defined(__APPLE__)
   ret = setegid(ppw->pw_gid);
#else
   ret = setresgid(ppw->pw_gid, ppw->pw_gid, root_gid);
#endif
   if (ret < 0) {
      WarningToGuest("Failed to set gid for user %s\n", user);
      WarningToHost("Failed to set gid\n");
      return FALSE;
   }
#ifndef USERWORLD
   ret = initgroups(ppw->pw_name, ppw->pw_gid);
   if (ret < 0) {
      WarningToGuest("Failed to initgroups() for user %s\n", user);
      WarningToHost("Failed to initgroups()\n");
      goto failure;
   }
#endif
   // now user
#if defined(USERWORLD)
   ret = Id_SetREUid(ppw->pw_uid, ppw->pw_uid);
#elif defined(__APPLE__)
   ret = seteuid(ppw->pw_uid);
#else
   ret = setresuid(ppw->pw_uid, ppw->pw_uid, 0);
#endif
   if (ret < 0) {
      WarningToGuest("Failed to set uid for user %s\n", user);
      WarningToHost("Failed to set uid\n");
      goto failure;
   }

   // set env
   setenv("USER", ppw->pw_name, 1);
   setenv("HOME", ppw->pw_dir, 1);
   setenv("SHELL", ppw->pw_shell, 1);

   return TRUE;

failure:
   // try to restore on error
   ProcMgr_ImpersonateUserStop();

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ImpersonateUserStop --
 *
 *      Stop impersonating a user and return to root.
 *
 * Results:
 *      TRUE on success
 *
 * Side effects:
 *
 *      Uid/gid restored to root.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_ImpersonateUserStop(void)
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   int error;
   int ret;

   if ((error = getpwuid_r(0, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      Warning("Failed to lookup user with uid: %" FMTUID ". Reason: %s\n", 0,
              error == 0 ? "entry not found" : Err_Errno2String(error));
      return FALSE;
   }

   // first change back user
#if defined(USERWORLD)
   ret = Id_SetREUid(ppw->pw_uid, ppw->pw_uid);
#elif defined(__APPLE__)
   ret = seteuid(ppw->pw_uid);
#else
   ret = setresuid(ppw->pw_uid, ppw->pw_uid, 0);
#endif
   if (ret < 0) {
      Warning("Failed to set uid for root\n");
      return FALSE;
   }

   // now group
#if defined(USERWORLD)
   ret = Id_SetREGid(ppw->pw_gid, ppw->pw_gid);
#elif defined(__APPLE__)
   ret = setegid(ppw->pw_gid);
#else
   ret = setresgid(ppw->pw_gid, ppw->pw_gid, ppw->pw_gid);
#endif
   if (ret < 0) {
      Warning("Failed to set gid for root\n");
      return FALSE;
   }
#ifndef USERWORLD
   ret = initgroups(ppw->pw_name, ppw->pw_gid);
   if (ret < 0) {
      Warning("Failed to initgroups() for root\n");
      return FALSE;
   }
#endif

   // set env
   setenv("USER", ppw->pw_name, 1);
   setenv("HOME", ppw->pw_dir, 1);
   setenv("SHELL", ppw->pw_shell, 1);

   return TRUE;
}


#ifdef __APPLE__
/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_PromoteEffectiveToReal --
 *
 *      Sets the processes real uid and gid to match the effective.
 *      Once done, it cannot be undone.
 *
 *      See the commentary in ProcMgr_ImpersonateUserStart() for
 *      why this is needed.
 *
 * Results:
 *      TRUE on success
 *
 * Side effects:
 *       Real uid is now effective.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_PromoteEffectiveToReal(void)
{
   int ret;
   uid_t uid = geteuid();
   gid_t gid = getegid();

   ret = setregid(gid, gid);
   if (ret < 0) {
      SAFE_WARNING("Failed to setregid(%d) %d\n", gid, errno);
      SET_OFFSPRING_EXIT_CODE(PROCMGR_ERROR_SETREGID);
      return FALSE;
   }
   ret = setreuid(uid, uid);
   if (ret < 0) {
      SAFE_WARNING("Failed to setreuid(%d) %d\n", uid, errno);
      SET_OFFSPRING_EXIT_CODE(PROCMGR_ERROR_SETREUID);
      return FALSE;
   }

   return TRUE;
}
#endif   // __APPLE__


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_GetImpersonatedUserInfo --
 *
 *      Return info about the impersonated user.
 *
 * Results:
 *      TRUE on success
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_GetImpersonatedUserInfo(char **userName,            // OUT
                                char **homeDir)             // OUT
{
   uid_t uid = geteuid();
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   int error;

   *userName = NULL;
   *homeDir = NULL;
   if ((error = getpwuid_r(uid, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      /*
       * getpwuid_r() and getpwnam_r() can return a 0 (success) but not
       * set the return pointer (ppw) if there's no entry for the user,
       * according to POSIX 1003.1-2003, so patch up the errno.
       */
      Warning("Failed to lookup user with uid: %" FMTUID ". Reason: %s\n", uid,
              error == 0 ? "entry not found" : Err_Errno2String(error));
      return FALSE;
   }

   *userName = Unicode_Alloc(ppw->pw_name, STRING_ENCODING_DEFAULT);
   *homeDir = Unicode_Alloc(ppw->pw_dir, STRING_ENCODING_DEFAULT);

   return TRUE;
}

#endif // linux || __FreeBSD__ || __APPLE__
070701000001C0000081A40000000000000000000000016822550500005C18000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/procMgr/procMgrSolaris.c   /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * procMgrSolaris.c --
 *
 *    Solaris specific implementations of the process management lib methods.
 *
 */


#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>

#if _FILE_OFFSET_BITS != 64
#   error FILE_OFFSET_BITS=64 required
#endif
/*
 * Work around unnecessary blocking of FOB=64 in Sol10, fixed in Sol11
 * See https://www.illumos.org/issues/2410
 */
#undef _FILE_OFFSET_BITS
#include <procfs.h>
#define _FILE_OFFSET_BITS 64

#include <ctype.h>
#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>

#include "vmware.h"
#include "procMgr.h"
#include "debug.h"
#include "util.h"
#undef offsetof
#include "dynbuf.h"
#include "dynarray.h"
#include "su.h"
#include "str.h"
#include "posix.h"
#include "codeset.h"
#include "unicode.h"

static Bool
ReadArgsFromAddressSpaceFile(int asFd,
                             psinfo_t *psInfo,
                             DynBufArray *cmdLineArr);

static Bool
ReadOffsetsFromAddressSpaceFile(int asFd,
                                psinfo_t *psInfo,
                                uintptr_t *argOffs);

static char *
ExtractArgStringFromAddressSpaceFile(int asFd,
                                     uintptr_t offset);

static char *
ExtractCommandLineFromAddressSpaceFile(psinfo_t *procInfo,
                                       char **procCmdName);

/*
 *----------------------------------------------------------------------------
 *
 * ProcMgr_ListProcesses --
 *
 *      List all the processes that the calling client has privilege to
 *      enumerate. The strings in the returned structure should be all
 *      UTF-8 encoded, although we do not enforce it right now.
 *
 * Results:
 *
 *      On success, the process list is returned and the caller is responsible
 *      for freeing the memory used by calling ProcMgr_FreeProcList. On
 *      failure, NULL is returned.
 *
 * Side effects:
 *
 *      None
 *
 *----------------------------------------------------------------------------
 */

ProcMgrProcInfoArray *
ProcMgr_ListProcesses(void)
{
   ProcMgrProcInfoArray *procList = NULL;
   ProcMgrProcInfo processInfo;
   Bool failed = TRUE;
   DIR *dir;
   struct dirent *ent;

   procList = Util_SafeCalloc(1, sizeof *procList);
   ProcMgrProcInfoArray_Init(procList, 0);
   processInfo.procOwner = NULL;
   processInfo.procCmdLine = NULL;
   processInfo.procCmdName = NULL;

   dir = opendir("/proc");
   if (NULL == dir) {
      Warning("ProcMgr_ListProcesses unable to open /proc\n");
      goto exit;
   }

   while (TRUE) {
      char *tmp;
      char *cmdNameBegin = NULL;
      char *cmdNameEnd = NULL;
      struct passwd *pwd;
      char tempPath[MAXPATHLEN];
      psinfo_t procInfo;
      size_t strLen = 0;
      size_t numRead = 0;
      int psInfoFd = -1;
      Bool cmdNameLookup = TRUE;

      errno = 0;

      ent = readdir(dir);
      if (ent == NULL) {
         if (errno == 0) {
            break;
         } else {
            goto exit;
         }
      }

      if (Str_Snprintf(tempPath,
                       sizeof tempPath,
                       "/proc/%s/psinfo",
                       ent->d_name) == -1) {
         Debug("Process id '%s' too large\n", ent->d_name);
         continue;
      }
      psInfoFd = Posix_Open(tempPath, O_RDONLY);
      if (psInfoFd == -1) {
         if (errno == ENOENT || errno == EACCES) {
            continue;
         } else {
            goto exit;
         }
      }

      numRead = read(psInfoFd, &procInfo, sizeof procInfo);
      close(psInfoFd);
      if (numRead != sizeof procInfo) {
         goto exit;
      }

      processInfo.procStartTime = procInfo.pr_start.tv_sec;

      /*
       * If the command name in the ps info struct is strictly less than the
       * maximum allowed size, then we can save it right now. Else we shall
       * need to try and parse it from the entire command line, to avoid
       * saving a truncated command name.
       */
      if (strlen(procInfo.pr_fname) + 1 < sizeof procInfo.pr_fname) {
         processInfo.procCmdName = Unicode_Alloc(procInfo.pr_fname,
                                                 STRING_ENCODING_DEFAULT);
         cmdNameLookup = FALSE;
      }

      /*
       * The logic below is this:
       * 1. If we are looking for the explicit command name, we need to
       *    extract the arguments from the /proc/<pid>/as file and save argv[0].
       * 2. If we are not looking for the explicit command name, but the command
       *    line in the ps info struct is not strictly less than the maximum
       *    allowed size, we still need to extract the arguments from the
       *    /proc/<pid>/as file, to avoid saving truncated comand line.
       * 3. Else we can save the command line directly from the ps info struct.
       */
      if (cmdNameLookup) {
         tmp = ExtractCommandLineFromAddressSpaceFile(&procInfo, &processInfo.procCmdName);
      } else if (strlen(procInfo.pr_psargs) + 1 >= sizeof procInfo.pr_psargs) {
         tmp = ExtractCommandLineFromAddressSpaceFile(&procInfo, NULL);
      } else {
         tmp = NULL;
      }

      if (tmp != NULL) {
         processInfo.procCmdLine = Unicode_Alloc(tmp, STRING_ENCODING_DEFAULT);
         cmdNameLookup = FALSE;
      } else {
         /*
          * We had some issues reading procfs, mostly due to lack of
          * permissions for certain system owned precesses. So let's resort to
          * what the procinfo structure provides as a last resort.
          */
         processInfo.procCmdLine = Unicode_Alloc(procInfo.pr_psargs,
                                                 STRING_ENCODING_DEFAULT);

         if (cmdNameLookup) {
            /*
             * Now let's try and get the best effort command name from the entire
             * command line. The method below does not take care of spaces in folder
             * names and executable file names. This is the best we can do at this
             * point, considering that spaces are not common in either file or
             * folder names in Solaris, specially when owned by the system.
             */
            char *tmp2 = Unicode_Alloc(procInfo.pr_psargs, STRING_ENCODING_DEFAULT);
            cmdNameBegin = tmp2;
            /*
             * Assuming the command name to end at the first blank space.
             */
            cmdNameEnd = cmdNameBegin;
            while ('\0' != *cmdNameEnd) {
               if ('/' == *cmdNameEnd) {
                  cmdNameBegin = cmdNameEnd + 1;
               }
               if (' ' == *cmdNameEnd) {
                  break;
               }
               cmdNameEnd++;
            }
            *cmdNameEnd = '\0';
            processInfo.procCmdName = Str_SafeAsprintf(NULL, "%s", cmdNameBegin);
            free(tmp2);
         }
      }
      free(tmp);
      tmp = NULL;

      /*
       * Store the pid.
       */
      processInfo.procId = procInfo.pr_pid;

      /*
       * Store the owner of the process.
       */
      pwd = getpwuid(procInfo.pr_uid);
      processInfo.procOwner = (NULL == pwd)
                              ? Str_SafeAsprintf(&strLen, "%d", (int) procInfo.pr_uid)
                              : Unicode_Alloc(pwd->pw_name, STRING_ENCODING_DEFAULT);

      /*
       * Store the process info into a list buffer.
       */
      if (!ProcMgrProcInfoArray_Push(procList, processInfo)) {
         Warning("%s: failed to expand DynArray - out of memory\n",
                 __FUNCTION__);
         goto exit;
      }
      processInfo.procCmdName = NULL;
      processInfo.procCmdLine = NULL;
      processInfo.procOwner = NULL;
   } // while (TRUE)

   if (0 < ProcMgrProcInfoArray_Count(procList)) {
      failed = FALSE;
   }

exit:
   closedir(dir);

   free(processInfo.procOwner);
   free(processInfo.procCmdLine);
   free(processInfo.procCmdName);

   if (failed) {
      ProcMgr_FreeProcList(procList);
      procList = NULL;
   }

   return procList;
}


/*
 *----------------------------------------------------------------------------
 *
 * ExtractCommandLineFromAddressSpaceFile --
 *
 *      Read the address space file (/proc/<pid>/as) for a given process and
 *      return its command line string.
 *
 * Results:
 *
 *      On success, the command line string for the process is returned and the
 *      caller is responsible for freeing the memory used by this string. On
 *      failure, NULL is returned.
 *
 * Side effects:
 *
 *      None
 *
 *----------------------------------------------------------------------------
 */

static char *
ExtractCommandLineFromAddressSpaceFile(psinfo_t *procInfo, //IN: psinfo struct
                                       char **procCmdName) //OUT: command name
{
   int argc;
   int i;
   char tempPath[MAXPATHLEN];
   char *buf;
   int asFd = -1;
   DynBuf cmdLine;
   DynBufArray args;
   pid_t pid;
   char *cmdNameBegin;

   if (NULL != procCmdName) {
      *procCmdName = NULL;
   }
   pid = procInfo->pr_pid;

   if (Str_Snprintf(tempPath,
                sizeof tempPath,
                "/proc/%"FMT64"d/as",
                (int64_t)pid) == -1) {
      /* This should not happen. MAXPATHLEN should be large enough. */
      ASSERT(FALSE);
   }
   asFd = Posix_Open(tempPath, O_RDONLY);
   if (asFd == -1) {
      Warning("Could not open address space file for pid %"FMT64"d, %s\n",
              (int64_t)pid, strerror(errno));
      return NULL;
   }

   buf = NULL;
   if (ReadArgsFromAddressSpaceFile(asFd, procInfo, &args)) {
      /* Concatenate the strings in args into cmdLine. */
      DynBuf_Init(&cmdLine);
      argc = DynBufArray_Count(&args);
      for (i = 0; i < argc; i++) {
         buf = DynBuf_Get(DynBufArray_AddressOf(&args, i));
         DynBuf_Append(&cmdLine, buf, strlen(buf));
         if (i + 1 < argc) {
            DynBuf_Append(&cmdLine, " ", 1);
         }
         if (NULL != procCmdName && 0 == i) {
            /*
             * Store the command name of the process.
             * Find the last path separator, to get the cmd name.
             * If no separator is found, then use the whole name.
             */
            cmdNameBegin = strrchr(buf, '/');
            if (NULL == cmdNameBegin) {
               cmdNameBegin = buf;
            } else {
               /*
                * Skip over the last separator.
                */
               cmdNameBegin++;
            }
            *procCmdName = Unicode_Alloc(cmdNameBegin, STRING_ENCODING_DEFAULT);
         }
         DynBuf_Destroy(DynBufArray_AddressOf(&args, i));
      }
      DynBuf_AppendString(&cmdLine,"");
      DynBufArray_Destroy(&args);
      DynBuf_Trim(&cmdLine);
      buf = DynBuf_Detach(&cmdLine);
      DynBuf_Destroy(&cmdLine);
   }
   close(asFd);
   return buf;
}


/*
 *----------------------------------------------------------------------------
 *
 * ReadArgsFromAddressSpaceFile --
 *
 *      Read the command line arguments for a process and store them in the
 *      cmdLineArr array. The processes' address space file must be open with
 *      the file descriptor adFd. This function assumes that it runs in the
 *      same locale as the process being inspected.
 *
 * Results:
 *
 *      On success, TRUE is returned and the caller is responsible for
 *      de-allocating the memory used by the DynBufArray; by calling
 *      DynBuf_Destroy on each of its elements, and then DynBufArray_Destroy on
 *      the array itself. FALSE is returned on failure, and no de-allocation
 *      is needed.
 *
 * Side effects:
 *
 *      The cmdLineArr array is filled with the command line strings of the
 *      given process.
 *----------------------------------------------------------------------------
 */

static Bool
ReadArgsFromAddressSpaceFile(int asFd,                  //IN
                             psinfo_t *psInfo,          //IN
                             DynBufArray *cmdLineArr)   //OUT
{
   uintptr_t *argOffs;
   uintptr_t argOff;
   uintptr_t nextArgOff;
   int argc;
   int i;
   char *argBuf;
   char *argBufPtr;
   DynBuf *arg;

   argc = psInfo->pr_argc;
   DynBufArray_Init(cmdLineArr, argc);
   for (i = 0; i < argc; i++) {
      DynBuf_Init(DynBufArray_AddressOf(cmdLineArr, i));
   }
   if (argc == 0) {
      return TRUE;
   }
   argOffs = Util_SafeCalloc(argc, sizeof *argOffs);
   if (argOffs == NULL) {
      return FALSE;
   }

   if (!ReadOffsetsFromAddressSpaceFile(asFd, psInfo, argOffs)) {
      goto fail;
   }

   /* Read the command line arguments into the cmdLineArr array. */
   nextArgOff = argc > 0 ? argOffs[0] : 0;
   i = 0;
   while (i < argc) {
      argOff = argOffs[i];

      /*
       * The argument strings are contiguous in the address space file. So
       * argOff[i] + strlen(arg[i]) + 1 should be equal to argOff[i + 1].
       */
      if ((argOff == 0) || (argOff != nextArgOff)) {
         goto fail;
      }
      argBuf = ExtractArgStringFromAddressSpaceFile(asFd, argOff);
      if (argBuf == NULL) {
         goto fail;
      }
      nextArgOff = argOff + strlen(argBuf) + 1;
      argBufPtr = argBuf +  strlen(argBuf);
      while ((argBufPtr > argBuf) && isspace(*(argBufPtr - 1))) {
         argBufPtr--;
      }
      *argBufPtr = '\0';
      arg = DynBufArray_AddressOf(cmdLineArr, i);
      if (!DynBuf_Append(arg,
                         argBuf,
                         strlen(argBuf) + 1)) {
         free(argBuf);
         goto fail;
      }
      free(argBuf);
      i++;
   }
   return TRUE;

fail:
   Warning("Failed to read command line arguments\n");
   argc = DynBufArray_Count(cmdLineArr);
   for (i = 0; i < argc; i++) {
      arg = DynArray_AddressOf(cmdLineArr, i);
      DynBuf_Destroy(arg);
   }
   DynBufArray_SetCount(cmdLineArr, 0);
   DynBufArray_Destroy(cmdLineArr);
   free(argOffs);
   return FALSE;
}


/*
 *----------------------------------------------------------------------------
 *
 * ReadOffsetsFromAddressSpaceFile --
 *
 *      Read the offsets for the command line arguments strings of a process
 *      into the argOffs array. The processes' /proc/<pid>/as file must be
 *      open with the file descriptor asFd. The argOffs array must have enough
 *      space to store all the offsets for the process.
 *
 * Results:
 *
 *      TRUE on success, FALSE on error.
 *
 * Side Effects:
 *
 *      The argOffs array is filled with the offsets of the command line
 *      arguments.
 *
 *----------------------------------------------------------------------------
 */

static Bool
ReadOffsetsFromAddressSpaceFile(int asFd,              //IN
                                psinfo_t *psInfo,      //IN
                                uintptr_t *argOffs)    //OUT
{
   int argc;
   int i;
   uintptr_t argv;
   ssize_t res;

   argc = psInfo->pr_argc;
   argv = psInfo->pr_argv;
   /*
    * The offsets for the command line argument are located at an offset of
    * argv in the /proc/<pid>/as file. If the process data model is NATIVE,
    * each offset is a unitptr_t; else if the data model is ILP32 or LP64, each
    * offset is uint32_t.
    */
   if (psInfo->pr_dmodel == PR_MODEL_NATIVE) {
      /*
       * The offset for each arguments is sizeof uintptr_t bytes.
       */
      res = pread(asFd, argOffs, argc * sizeof argv, argv);
      if (res != argc * sizeof argv) {
         return FALSE;
      }
   } else {
      /*
       * The offset for each arguments is sizeof uint32_t bytes.
       */
      uint32_t *argOffs32;
      argOffs32 = Util_SafeCalloc(argc, sizeof *argOffs32);
      if (argOffs32 == NULL) {
         return FALSE;
      }
      res = pread(asFd, argOffs32, argc * sizeof *argOffs32, argv);
      if (res != argc * sizeof *argOffs32) {
         free (argOffs32);
         return FALSE;
      }
      for (i = 0; i < argc; i++) {
         argOffs[i] = argOffs32[i];
      }
      free(argOffs32);
   }
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * ExtractArgStringFromAddressSpaceFile --
 *
 *      Extract a string at a given offset in the given file. The file must be
 *      open with file descriptor asFd.
 *
 * Results:
 *
 *      On success, the NULL terminated string is returned and the
 *      caller is responsible for freeing the memory used by this string. On
 *      failure, NULL is returned.
 *
 * Side effects:
 *
 *      None
 *
 *----------------------------------------------------------------------------
 */

static char *
ExtractArgStringFromAddressSpaceFile(int asFd,          //IN
                                     uintptr_t offset)  //IN
{
   int readSize = 32;
   char *buf;
   ssize_t res;

   buf = Util_SafeMalloc(readSize * sizeof *buf);
   while (1) {
      res = pread(asFd, buf, readSize, offset);
      if (res != readSize) {
         goto fail;
      }
      if (Str_Strlen(buf, readSize) == readSize) {
         readSize *= 2;
         free(buf);
         if (readSize > NCARGS) {
            return NULL;
         }
         buf = Util_SafeMalloc(readSize * sizeof *buf);
      } else {
         break;
      }
   }
   return buf;

fail:
   free(buf);
   return NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ImpersonateUserStart --
 *
 *      Impersonate a user.  Much like bora/lib/impersonate, but
 *      changes the real and saved uid as well, to work with syscalls
 *      (access() and kill()) that look at real UID instead of effective.
 *      The user name should be UTF-8 encoded, although we do not enforce
 *      it right now. Solaris does not have setresuid/setresgid. So perform
 *      a two step process to set the real and effective uid/gid to given
 *      user and leave the saved uid/gid as root.
 *
 *      Assumes it will be called as root.
 *
 * Results:
 *      TRUE on success, FALSE on failure
 *
 * Side effects:
 *
 *      Uid/gid set to given user, saved uid/gid left as root.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_ImpersonateUserStart(const char *user,  // IN: UTF-8 encoded user name
                             AuthToken token)   // IN
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw;
   gid_t root_gid;
   int ret;
   char *userLocal;

   ppw = &pw;
   if ((ppw = getpwuid_r(0, &pw, buffer, sizeof buffer)) == NULL) {
      return FALSE;
   }

   root_gid = ppw->pw_gid;

   /* convert user name to local character set */
   userLocal = (char *)Unicode_GetAllocBytes(user, Unicode_GetCurrentEncoding());
   if (!userLocal) {
       Warning("Failed to convert user name %s to local character set.\n", user);
       return FALSE;
   }

   ppw = getpwnam_r(userLocal, &pw, buffer, sizeof buffer);

   free(userLocal);

   if (ppw == NULL) {
      return FALSE;
   }

   /* first change group. */
   ret = Id_SetGid(root_gid);
   if (ret < 0) {
      Warning("Failed to setregid() for root\n");
      return FALSE;
   }

   /*  Snippet from Solaris setregid man page --
    *
    *  A -1 argument does not change the corresponding gid. If the real user ID
    *  is being changed, or the effective user ID is being changed to a value
    *  not equal to the real user ID, the saved set-user ID is set equal to
    *  the new effective user ID.
    */
   ret = Id_SetREGid(ppw->pw_gid, -1);
   if (ret < 0) {
      Warning("Failed to setregid() for user %s\n", user);
      return FALSE;
   }
   ret = Id_SetREGid(-1, ppw->pw_gid);
   if (ret < 0) {
      Warning("Failed to setregid() for user %s\n", user);
      return FALSE;
   }
   ret = initgroups(ppw->pw_name, ppw->pw_gid);
   if (ret < 0) {
      Warning("Failed to initgroups() for user %s\n", user);
      goto failure;
   }

   /* now user. */
   ret = Id_SetUid(0);
   if (ret < 0) {
      Warning("Failed to setregid() for root\n");
      return FALSE;
   }

   /* Same as above. */
   ret = Id_SetREUid(ppw->pw_uid, -1);
   if (ret < 0) {
      Warning("Failed to setreuid() for user %s\n", user);
      goto failure;
   }
   ret = Id_SetREUid(-1, ppw->pw_uid);
   if (ret < 0) {
      Warning("Failed to setreuid() for user %s\n", user);
      goto failure;
   }

   /* set env. */
   setenv("USER", ppw->pw_name, 1);
   setenv("HOME", ppw->pw_dir, 1);
   setenv("SHELL", ppw->pw_shell, 1);

   return TRUE;

failure:
   /* try to restore on error. */
   ProcMgr_ImpersonateUserStop();
   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_ImpersonateUserStop --
 *
 *      Stop impersonating a user and return to root. Solaris does not
 *      have setresuid/setresgid. So perform a two step process while
 *      restoring uids to root.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *
 *      Uid/gid restored to root.
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_ImpersonateUserStop(void)
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw;
   int ret;

   ppw = &pw;
   if ((ppw = getpwuid_r(0, &pw, buffer, sizeof buffer)) == NULL) {
      return FALSE;
   }

   /* first change back user, Do the same two step process as above. */
   ret = Id_SetREUid(-1, ppw->pw_uid);
   if (ret < 0) {
      Warning("Failed setreuid() for root\n");
      return FALSE;
   }
   ret = Id_SetREUid(ppw->pw_uid, -1);
   if (ret < 0) {
      Warning("Failed to setreuid() for root\n");
      return FALSE;
   }

   /* now group. */
   ret = Id_SetGid(ppw->pw_gid);
   if (ret < 0) {
      Warning("Failed to setgid() for root\n");
      return FALSE;
   }
   ret = initgroups(ppw->pw_name, ppw->pw_gid);
   if (ret < 0) {
      Warning("Failed to initgroups() for root\n");
      return FALSE;
   }

   /* set env. */
   setenv("USER", ppw->pw_name, 1);
   setenv("HOME", ppw->pw_dir, 1);
   setenv("SHELL", ppw->pw_shell, 1);

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * ProcMgr_GetImpersonatedUserInfo --
 *
 *      Return info about the impersonated user.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

Bool
ProcMgr_GetImpersonatedUserInfo(char **userName,            // OUT
                                char **homeDir)             // OUT
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw;

   *userName = NULL;
   *homeDir = NULL;

   ppw = &pw;
   if ((ppw = getpwuid_r(Id_GetEUid(), &pw, buffer, sizeof buffer)) == NULL) {
      return FALSE;
   }

   *userName = Unicode_Alloc(ppw->pw_name, STRING_ENCODING_DEFAULT);
   *homeDir = Unicode_Alloc(ppw->pw_dir, STRING_ENCODING_DEFAULT);

   return TRUE;
}

070701000001C1000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel 070701000001C2000081A40000000000000000000000016822550500000539000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel/Makefile.am ################################################################################
### Copyright (C) 2009-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libRpcChannel.la

libRpcChannel_la_SOURCES =
libRpcChannel_la_SOURCES += bdoorChannel.c
libRpcChannel_la_SOURCES += rpcChannel.c
if HAVE_VSOCK
libRpcChannel_la_SOURCES += vsockChannel.c
libRpcChannel_la_SOURCES += simpleSocket.c
endif

libRpcChannel_la_CPPFLAGS =
libRpcChannel_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@
libRpcChannel_la_CPPFLAGS += @XDR_CPPFLAGS@

libRpcChannel_la_LIBADD =
libRpcChannel_la_LIBADD += @XDR_LIBS@

   070701000001C3000081A400000000000000000000000168225505000020C4000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel/bdoorChannel.c  /*********************************************************
 * Copyright (C) 2008-2016,2018-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file bdoorChannel.c
 *
 *    Implements a backdoor-based RPC channel. This is based on the
 *    RpcIn / RpcOut libraries.
 */

#include "vm_assert.h"
#include "rpcChannelInt.h"
#if defined(NEED_RPCIN)
#include "rpcin.h"
#endif
#include "rpcout.h"
#include "util.h"
#include "debug.h"

typedef struct BackdoorChannel {
   RpcOut           *out;
} BackdoorChannel;


/**
 * Starts the RpcOut channel.
 *
 * No-op if channels are already started. If RpcIn
 * channel is needed, it must have been started
 * before calling this function.
 *
 * In case of error, stops RpcIn.
 *
 * @param[in]  chan     The RPC channel instance.
 *
 * @return TRUE on success.
 */

static gboolean
BkdoorChannelStart(RpcChannel *chan)
{
   gboolean ret;
   BackdoorChannel *bdoor = chan->_private;

#if defined(NEED_RPCIN)
   /*
    * If the RpcIn channel exists, it should have been started before
    * calling this routine.
    */
   ASSERT(chan->in == NULL || chan->inStarted);

   ret = RpcOut_start(bdoor->out);
   if (!ret && chan->in != NULL) {
      /*
       * If the output channel failed to start, stop the input channel
       * if there is one.
       */

      RpcIn_stop(chan->in);
      chan->inStarted = FALSE;
   }
#else
   ret = RpcOut_start(bdoor->out);
#endif
   chan->outStarted = ret;
   return ret;
}


/**
 * Stops a channel, keeping internal state so that it can be restarted later.
 * It's safe to call this function more than once.
 *
 * @internal This function does a best effort at tearing down the host-side
 *           channels, but if the host returns any failure, it still shuts
 *           down the guest channels. See bug 388777 for details.
 *
 * @param[in]  chan     The RPC channel instance.
 */

static void
BkdoorChannelStop(RpcChannel *chan)
{
   BackdoorChannel *bdoor = chan->_private;

   if (bdoor->out != NULL) {
      if (chan->outStarted) {
         RpcOut_stop(bdoor->out);
         chan->outStarted = FALSE;
      }
   } else {
      ASSERT(!chan->outStarted);
   }
}


/**
 * Sends the data using the RpcOut library.
 *
 * rpcStatus is valid only when function returns success.
 *
 * @param[in]  chan        The RPC channel instance.
 * @param[in]  data        Data to send.
 * @param[in]  dataLen     Number of bytes to send.
 * @param[out] rpcStatus   Status of RPC command.
 * @param[out] result      Response from other side.
 * @param[out] resultLen   Number of bytes in response.
 *
 * @return The status from the remote end (TRUE if RPC was sent successfully).
 */

static gboolean
BkdoorChannelSend(RpcChannel *chan,
                  char const *data,
                  size_t dataLen,
                  Bool *rpcStatus,
                  char **result,
                  size_t *resultLen)
{
   gboolean ret = FALSE;
   const char *reply;
   size_t replyLen;
   BackdoorChannel *bdoor = chan->_private;

   if (!chan->outStarted) {
      goto exit;
   }

   ret = RpcOut_send(bdoor->out, data, dataLen, rpcStatus, &reply, &replyLen);

   /*
    * This is a hack to try to work around bug 393650 without having to revert
    * to the old behavior of opening and closing an RpcOut channel for every
    * outgoing message. The issue here is that it's possible for the code to
    * try to write to the channel when a "reset" has just happened. In these
    * cases, the current RpcOut channel is not valid anymore, and we'll get an
    * error.
    *
    * So, if the error is one of those messages, restart the RpcOut channel and
    * try to send the message again. If this second attempt fails, then give up.
    *
    * This is not 100% break-proof: a reset can still occur after we open the
    * new channel and before we try to re-send the message. But that's a race
    * that we can't easily fix, and exists even in code that just uses the
    * RpcOut_SendOne() API. Also, if some host handler returns an error that
    * starts with "RpcOut:", it will trigger this; but I don't think we have
    * any such handlers.
    */
   if (!ret) {
      Debug("RpcOut failure, restarting channel.\n");
      RpcOut_stop(bdoor->out);
      if (RpcOut_start(bdoor->out)) {
         ret = RpcOut_send(bdoor->out, data, dataLen, rpcStatus,
                           &reply, &replyLen);
      } else {
         Warning("Couldn't restart RpcOut channel; bad things may happen "
                 "until the RPC channel is reset.\n");
         chan->outStarted = FALSE;
      }
   }

   /*
    * A lot of this logic is just replicated from rpcout.c:RpcOut_SendOneRaw().
    * Look there for comments about a few details.
    */
   if (result != NULL) {
      if (reply != NULL) {
         *result = Util_SafeMalloc(replyLen + 1);
         memcpy(*result, reply, replyLen);
         (*result)[replyLen] = '\0';
      } else {
         *result = NULL;
      }
   }

   if (resultLen != NULL) {
      *resultLen = replyLen;
   }

exit:
   return ret;
}


/**
 * Callback function to destroy the Backdoor channel after
 * it fails to start or it has been stopped.
 *
 * @param[in]  chan     The RPC channel instance.
 */

static void
BkdoorChannelDestroy(RpcChannel *chan)
{
   BackdoorChannel *bdoor = chan->_private;

   /*
    * Channel should be stopped before destroying it.
    */
   ASSERT(!chan->outStarted);
   RpcOut_Destruct(bdoor->out);
   g_free(bdoor);
   chan->_private = NULL;
}


/**
 * Shuts down the Backdoor RpcOut channel.
 *
 * Due to the "split brain" nature of the backdoor, if this function
 * fails, it's possible that while the "out" channel was shut down
 * the "in" one wasn't, for example, although that's unlikely.
 *
 * @param[in]  chan     The RPC channel instance.
 */

static void
BkdoorChannelShutdown(RpcChannel *chan)
{
   BkdoorChannelStop(chan);
   BkdoorChannelDestroy(chan);
}


/**
 * Return the channel type.
 *
 * @param[in]  chan     RpcChannel
 *
 * @return RpcChannelType.
 */

static RpcChannelType
BkdoorChannelGetType(RpcChannel *chan)
{
   return RPCCHANNEL_TYPE_BKDOOR;
}


/**
 * Helper function to setup RpcChannel callbacks.
 *
 * @param[in]  chan     RpcChannel
 */

static void
BackdoorChannelSetCallbacks(RpcChannel *chan)
{
   static RpcChannelFuncs funcs = {
      BkdoorChannelStart,
      BkdoorChannelStop,
      BkdoorChannelSend,
      NULL,
      BkdoorChannelShutdown,
      BkdoorChannelGetType,
      BkdoorChannelDestroy
   };

   ASSERT(chan);
   chan->funcs = &funcs;
}


/**
 * Creates a new RpcChannel channel that uses the backdoor for communication.
 *
 * @return A new channel instance (never NULL).
 */

RpcChannel *
BackdoorChannel_New(void)
{
   RpcChannel *ret;
   BackdoorChannel *bdoor;

   ret = RpcChannel_Create();
   bdoor = g_malloc0(sizeof *bdoor);

   bdoor->out = RpcOut_Construct();
   ASSERT(bdoor->out != NULL);

#if defined(NEED_RPCIN)
   ret->inStarted = FALSE;
#endif
   ret->outStarted = FALSE;
   /*
    * Backdoor channel is not mutable as it has no
    * fallback option available.
    */
   ret->isMutable = FALSE;

   BackdoorChannelSetCallbacks(ret);
   ret->_private = bdoor;
   g_mutex_init(&ret->outLock);

   return ret;
}


/**
 * Fall back to backdoor when another type of RpcChannel fails to start.
 *
 * @param[in]  chan     RpcChannel
 */

void
BackdoorChannel_Fallback(RpcChannel *chan)
{
   BackdoorChannel *bdoor;

   ASSERT(chan);
   ASSERT(chan->_private == NULL);

   bdoor = g_malloc0(sizeof *bdoor);
   bdoor->out = RpcOut_Construct();
   ASSERT(bdoor->out != NULL);

   BackdoorChannelSetCallbacks(chan);
   chan->_private = bdoor;
}
070701000001C4000081A40000000000000000000000016822550500000634000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel/glib_stubs.c    /*********************************************************
 * Copyright (C) 2018-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file glib_stubs.c
 *
 *    rpcChannel uses glib. If anyone wants to build rpcChannel without
 *    having any dependency on glib, we need to use this stubs file.
 */

#include <stdlib.h>
#include <string.h>
#include <glib.h>

#include "util.h"

#if !defined(USE_RPCI_ONLY)
#error "This file should be compiled for RPCI-only channel!"
#endif

void *g_malloc0(size_t s) { return Util_SafeCalloc(1, s); }
void *g_malloc0_n(size_t n, size_t s) { return Util_SafeCalloc(n, s); }
void g_free(void *p) { free(p); }

void g_mutex_init(GMutex *mutex) { }
void g_mutex_clear(GMutex *mutex) { }
void g_mutex_lock(GMutex *mutex) { }
void g_mutex_unlock(GMutex *mutex) { }

void g_usleep(gulong microseconds) { }
070701000001C5000081A40000000000000000000000016822550500008E09000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel/rpcChannel.c    /*********************************************************
 * Copyright (c) 2008-2016, 2018-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file rpcChannel.c
 *
 *    Common functions to all RPC channel implementations.
 */

#include <stdlib.h>
#include <string.h>
#include "debug.h"
#include "rpcChannelInt.h"

#if defined(NEED_RPCIN)
#include "dynxdr.h"
#include "vmxrpc.h"
#include "xdrutil.h"
#include "rpcin.h"
#include "vmware/guestrpc/tclodefs.h"
#endif

#include "str.h"
#include "strutil.h"
#include "util.h"
#include "vm_assert.h"

/** Internal state of a channel. */
typedef struct RpcChannelInt {
   RpcChannel              impl;
#if defined(NEED_RPCIN)
   gchar                  *appName;
   GHashTable             *rpcs;
   GMainContext           *mainCtx;
   GSource                *resetCheck;
   gpointer                appCtx;
   RpcChannelCallback      resetReg;
   RpcChannelResetCb       resetCb;
   gpointer                resetData;
   gboolean                rpcError;
   guint                   rpcErrorCount;
   guint                   rpcResetErrorCount;  /* channel reset failures */
   /*
    * The rpcFailureCount is a cumulative count of calls made to
    * RpcChannelError().  When getting repeated channel failures,
    * the channel is constantly stopped and restarted.
    */
   guint                   rpcFailureCount;  /* cumulative channel failures */
   RpcChannelFailureCb     rpcFailureCb;
   guint                   rpcMaxFailures;
   gboolean                rpcInInitialized;
   GSource                *restartTimer; /* Channel restart timer */
#endif
} RpcChannelInt;

#define LGPFX "RpcChannel: "

static gboolean gUseBackdoorOnly = FALSE;

/*
 * Delay in seconds before retrying vSocket after
 * falling back to Backdoor (min=2sec, max=5min).
 *
 * Trying vSocket when it is not working can lead to
 * futile attempts with almost each attempt taking 2s
 * to timeout. To avoid this delay, don't attempt to use
 * vSocket for a while.
 */
#define RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY    (2)
#define RPCCHANNEL_VSOCKET_RETRY_MAX_DELAY    (5 * 60)


static void RpcChannelStopNoLock(RpcChannel *chan);


#if defined(NEED_RPCIN)
/** Max number of times to attempt a channel restart. */
#define RPCIN_MAX_RESTARTS 60

static gboolean
RpcChannelPing(RpcInData *data);

static RpcChannelCallback gRpcHandlers[] =  {
   { "ping", RpcChannelPing, NULL, NULL, NULL, 0 }
};

/**
 * Handler for a "ping" message. Does nothing.
 *
 * @param[in]  data     The RPC data.
 *
 * @return TRUE.
 */

static gboolean
RpcChannelPing(RpcInData *data)
{
   return RPCIN_SETRETVALS(data, "", TRUE);
}


/**
 * Callback for restarting the RPC channel.
 *
 * @param[in]  _chan    The RPC channel
 *
 * @return FALSE
 */

static gboolean
RpcChannelRestart(gpointer _chan)
{
   RpcChannelInt *chan = _chan;
   gboolean chanStarted;

   /* Synchronize with any RpcChannel_Send calls by other threads. */
   g_mutex_lock(&chan->impl.outLock);
   g_source_unref(chan->restartTimer);
   chan->restartTimer = NULL;

   RpcChannelStopNoLock(&chan->impl);

   if (chan->impl.vsockFailureTS != 0) {
      /* Clear vSocket channel failure */
      Log(LGPFX "Clearing backdoor behavior ...\n");
      chan->impl.vsockFailureTS = 0;
      chan->impl.vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY;
   }

   chanStarted = RpcChannel_Start(&chan->impl);
   g_mutex_unlock(&chan->impl.outLock);
   if (!chanStarted) {
      Warning("Channel restart failed [%d]\n", chan->rpcResetErrorCount);
      if (chan->resetCb != NULL) {
         chan->resetCb(&chan->impl, FALSE, chan->resetData);
      }
   } else {
      chan->rpcError = FALSE;
   }

   return FALSE;
}


/**
 * Checks and potentially resets the RPC channel. This code is based on the
 * toolsDaemon.c function "ToolsDaemon_CheckReset".
 *
 * @param[in]  _chan    The RPC channel.
 *
 * @return FALSE. The reset callback will schedule a new check when it's called.
 */

static gboolean
RpcChannelCheckReset(gpointer _chan)
{
   static int channelTimeoutAttempts = RPCIN_MAX_RESTARTS;
   RpcChannelInt *chan = _chan;

   /* Check the channel state. */
   if (chan->rpcError) {

      if (++(chan->rpcResetErrorCount) > channelTimeoutAttempts) {
         Warning("Failed to reset channel after %u attempts\n",
                 chan->rpcResetErrorCount - 1);
         if (chan->resetCb != NULL) {
            chan->resetCb(&chan->impl, FALSE, chan->resetData);
         }
         goto exit;
      }

      /* Schedule the channel restart for 1 sec in the future. */
      Debug(LGPFX "Resetting channel [%u]\n", chan->rpcResetErrorCount);
      ASSERT(chan->restartTimer == NULL);
      chan->restartTimer = g_timeout_source_new(1000);
      g_source_set_callback(chan->restartTimer, RpcChannelRestart, chan, NULL);
      g_source_attach(chan->restartTimer, chan->mainCtx);
      goto exit;
   }

   /* Reset was successful. */
   Log(LGPFX "Channel was reset successfully.\n");
   chan->rpcResetErrorCount = 0;

   if (chan->impl.vsockFailureTS != 0) {
      Log(LGPFX "Clearing backdoor behavior ...\n");
      chan->impl.vsockFailureTS = 0;
      chan->impl.vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY;
   }

   if (chan->resetCb != NULL) {
      chan->resetCb(&chan->impl, TRUE, chan->resetData);
   }

exit:
   g_source_unref(chan->resetCheck);
   chan->resetCheck = NULL;
   return FALSE;
}


/**
 * Handles an RPC reset. Calls the reset callback of all loaded plugins.
 *
 * @param[in]  data     The RPC data.
 *
 * @return TRUE.
 */

static gboolean
RpcChannelReset(RpcInData *data)
{
   gchar *msg;
   RpcChannelInt *chan = data->clientData;

   if (chan->resetCheck == NULL) {
      chan->resetCheck = g_idle_source_new();
      g_source_set_priority(chan->resetCheck, G_PRIORITY_HIGH);
      g_source_set_callback(chan->resetCheck, RpcChannelCheckReset, chan, NULL);
      g_source_attach(chan->resetCheck, chan->mainCtx);
   }

   msg = Str_Asprintf(NULL, "ATR %s", chan->appName);
   ASSERT_MEM_ALLOC(msg);
   return RPCIN_SETRETVALSF(data, msg, TRUE);
}


/**
 * A wrapper for standard RPC callback functions which provides automatic
 * XDR serialization / deserialization if requested by the application.
 *
 * @param[in]  data     RpcIn data.
 * @param[in]  rpc      The RPC registration data.
 *
 * @return Whether the RPC was handled successfully.
 */

static Bool
RpcChannelXdrWrapper(RpcInData *data,
                     RpcChannelCallback *rpc)
{
   Bool ret;
   RpcInData copy;
   void *xdrData = NULL;

   copy.freeResult = FALSE;
   copy.result = NULL;
   if (rpc->xdrIn != NULL) {
      xdrData = malloc(rpc->xdrInSize);
      if (xdrData == NULL) {
         ret = RPCIN_SETRETVALS(data, "Out of memory.", FALSE);
         goto exit;
      }

      memset(xdrData, 0, rpc->xdrInSize);
      if (!XdrUtil_Deserialize(data->args + 1, data->argsSize - 1,
                               rpc->xdrIn, xdrData)) {
         ret = RPCIN_SETRETVALS(data, "XDR deserialization failed.", FALSE);
         free(xdrData);
         goto exit;
      }

      copy.name = data->name;
      copy.args = xdrData;
      copy.argsSize = rpc->xdrInSize;
      copy.result = data->result;
      copy.resultLen = data->resultLen;
      copy.freeResult = data->freeResult;
      copy.appCtx = data->appCtx;
      copy.clientData = rpc->clientData;
   } else {
      memcpy(&copy, data, sizeof copy);
   }

   ret = rpc->callback(&copy);

   if (rpc->xdrIn != NULL) {
      VMX_XDR_FREE(rpc->xdrIn, xdrData);
      free(xdrData);
      copy.args = NULL;
      data->result = copy.result;
      data->resultLen = copy.resultLen;
      data->freeResult = copy.freeResult;
   }

   if (rpc->xdrOut != NULL && copy.result != NULL) {
      XDR xdrs;
      xdrproc_t xdrProc = rpc->xdrOut;

      if (DynXdr_Create(&xdrs) == NULL) {
         ret = RPCIN_SETRETVALS(data, "Out of memory.", FALSE);
         goto exit;
      }

      if (!xdrProc(&xdrs, copy.result, 0)) {
         ret = RPCIN_SETRETVALS(data, "XDR serialization failed.", FALSE);

         /*
          * DynXdr_Destroy only tries to free storage returned by a call to
          * DynXdr_Create(NULL).
          */
         /* coverity[address_free] */
         DynXdr_Destroy(&xdrs, TRUE);
         goto exit;
      }

      if (copy.freeResult) {
         VMX_XDR_FREE(rpc->xdrOut, copy.result);
      }
      data->result = DynXdr_Get(&xdrs);
      data->resultLen = XDR_GETPOS(&xdrs);
      data->freeResult = TRUE;

      /*
       * DynXdr_Destroy only tries to free storage returned by a call to
       * DynXdr_Create(NULL).
       */
      /* coverity[address_free] */
      DynXdr_Destroy(&xdrs, FALSE);
   }

exit:
   if (copy.freeResult && copy.result != NULL) {
      g_free(copy.result);
   }
   return ret;
}


/**
 * Builds an "rpcout" command to send a XDR struct.
 *
 * @param[in]  cmd         The command name.
 * @param[in]  xdrProc     Function to use for serializing the XDR struct.
 * @param[in]  xdrData     The XDR struct to serialize.
 * @param[out] result      Where to store the serialized data.
 * @param[out] resultLen   Where to store the serialized data length.
 *
 * @return Whether successfully built the command.
 */

gboolean
RpcChannel_BuildXdrCommand(const char *cmd,
                           void *xdrProc,
                           void *xdrData,
                           char **result,
                           size_t *resultLen)
{
   Bool ret = FALSE;
   xdrproc_t proc = xdrProc;
   XDR xdrs;

   if (DynXdr_Create(&xdrs) == NULL) {
      return FALSE;
   }

   if (!DynXdr_AppendRaw(&xdrs, cmd, strlen(cmd))) {
      goto exit;
   }

   if (!DynXdr_AppendRaw(&xdrs, " ", 1)) {
      goto exit;
   }

   if (!proc(&xdrs, xdrData, 0)) {
      goto exit;
   }

   *result = DynXdr_Get(&xdrs);
   *resultLen = xdr_getpos(&xdrs);

   ret = TRUE;

exit:

   /*
    * DynXdr_Destroy only tries to free storage returned by a call to
    * DynXdr_Create(NULL).
    */
   /* coverity[address_free] */
   DynXdr_Destroy(&xdrs, !ret);
   return ret;
}


/**
 * Dispatches the given RPC to the registered handler. This mimics the behavior
 * of the RpcIn library (but is not tied to that particular implementation of
 * an RPC channel).
 *
 * @param[in,out]    data     The RPC data.
 *
 * @return Whether the RPC was handled successfully.
 */

gboolean
RpcChannel_Dispatch(RpcInData *data)
{
   char *name;
   unsigned int index = 0;
   size_t nameLen;
   Bool status;
   RpcChannelCallback *rpc = NULL;
   RpcChannelInt *chan = data->clientData;

   name = StrUtil_GetNextToken(&index, data->args, " ");
   if (name == NULL) {
      Debug(LGPFX "Bad command (null) received.\n");
      status = RPCIN_SETRETVALS(data, "Bad command", FALSE);
      goto exit;
   }

   if (chan->rpcs != NULL) {
      rpc = g_hash_table_lookup(chan->rpcs, name);
   }

   if (rpc == NULL) {
      Debug(LGPFX "Unknown Command '%s': Handler not registered.\n", name);
      status = RPCIN_SETRETVALS(data, GUEST_RPC_UNKNOWN_COMMAND, FALSE);
      goto exit;
   }

   /* Adjust the RPC arguments. */
   nameLen = strlen(name);
   data->name = name;
   data->args = data->args + nameLen;
   data->argsSize -= nameLen;
   data->appCtx = chan->appCtx;
   data->clientData = rpc->clientData;

   if (rpc->xdrIn != NULL || rpc->xdrOut != NULL) {
      status = RpcChannelXdrWrapper(data, rpc);
   } else {
      status = rpc->callback(data);
   }

   ASSERT(data->result != NULL);

exit:
   data->name = NULL;
   free(name);
   return status;
}


/**
 * Initializes the RPC channel for inbound operations.
 *
 * This function must be called before starting the channel if the application
 * wants to receive messages on the channel. Applications don't need to call it
 * if only using the outbound functionality.
 *
 * @param[in]  chan        The RPC channel.
 * @param[in]  appName     TCLO application name.
 * @param[in]  mainCtx     Application event context.
 * @param[in]  appCtx      Application context.
 * @param[in]  resetCb     Callback for when a reset occurs.
 * @param[in]  resetData   Client data for the reset callback.
 * @param[in]  failureCB   Callback for when the channel failure limit is hit.
 * @param[in]  maxFailures Maximum channel failures allowed.
 */

void
RpcChannel_Setup(RpcChannel *chan,
                 const gchar *appName,
                 GMainContext *mainCtx,
                 gpointer appCtx,
                 RpcChannelResetCb resetCb,
                 gpointer resetData,
                 RpcChannelFailureCb failureCb,
                 guint maxFailures)
{
   size_t i;
   RpcChannelInt *cdata = (RpcChannelInt *) chan;

   ASSERT(!cdata->rpcInInitialized);

   cdata->appName = g_strdup(appName);
   cdata->appCtx = appCtx;
   cdata->mainCtx = g_main_context_ref(mainCtx);
   cdata->resetCb = resetCb;
   cdata->resetData = resetData;
   cdata->rpcFailureCb = failureCb;
   cdata->rpcMaxFailures = maxFailures;

   cdata->resetReg.name = "reset";
   cdata->resetReg.callback = RpcChannelReset;
   cdata->resetReg.clientData = chan;

   /* Register the callbacks handled by the rpcChannel library. */
   RpcChannel_RegisterCallback(chan, &cdata->resetReg);

   for (i = 0; i < ARRAYSIZE(gRpcHandlers); i++) {
      RpcChannel_RegisterCallback(chan, &gRpcHandlers[i]);
   }

   if (chan->funcs != NULL && chan->funcs->setup != NULL) {
      chan->funcs->setup(chan, mainCtx, appName, appCtx);
   } else {
      chan->mainCtx = g_main_context_ref(mainCtx);
      chan->in = RpcIn_Construct(mainCtx, RpcChannel_Dispatch, chan);
      ASSERT(chan->in != NULL);
   }

   cdata->rpcInInitialized = TRUE;
}


/**
 * Undo the RpcChannel_Setup() function if it was called earlier
 *
 * Note the outLock is initialized in the RpcChannel_New() function,
 * and should only be freed in the corresponding RpcChannel_Destroy() function.
 *
 * @param[in]  chan        The RPC channel instance.
 */

static void
RpcChannelTeardown(RpcChannel *chan)
{
   size_t i;
   RpcChannelInt *cdata = (RpcChannelInt *) chan;

   if (NULL == cdata || !cdata->rpcInInitialized) {
      return;
   }

   /*
    * Stop the restartTimer.
    */
   if (cdata->restartTimer) {
      g_source_destroy(cdata->restartTimer);
      g_source_unref(cdata->restartTimer);
      cdata->restartTimer = NULL;
   }

   RpcChannel_UnregisterCallback(chan, &cdata->resetReg);
   for (i = 0; i < ARRAYSIZE(gRpcHandlers); i++) {
      RpcChannel_UnregisterCallback(chan, &gRpcHandlers[i]);
   }

   if (cdata->rpcs != NULL) {
      g_hash_table_destroy(cdata->rpcs);
      cdata->rpcs = NULL;
   }

   cdata->resetCb = NULL;
   cdata->resetData = NULL;
   cdata->appCtx = NULL;
   cdata->rpcFailureCb = NULL;

   g_free(cdata->appName);
   cdata->appName = NULL;

   if (chan->mainCtx != NULL) {
      g_main_context_unref(chan->mainCtx);
      chan->mainCtx = NULL;
   }

   if (cdata->mainCtx != NULL) {
      g_main_context_unref(cdata->mainCtx);
      cdata->mainCtx = NULL;
   }

   if (cdata->resetCheck != NULL) {
      g_source_destroy(cdata->resetCheck);
      cdata->resetCheck = NULL;
   }

   if (chan->in != NULL) {
      RpcIn_Destruct(chan->in);
      chan->in = NULL;
   }

   cdata->rpcInInitialized = FALSE;
}


/**
 * Registers a new RPC handler in the given RPC channel. This function is
 * not thread-safe.
 *
 * @param[in]  chan     The channel instance.
 * @param[in]  rpc      Info about the RPC being registered.
 */

void
RpcChannel_RegisterCallback(RpcChannel *chan,
                            RpcChannelCallback *rpc)
{
   RpcChannelInt *cdata = (RpcChannelInt *) chan;
   ASSERT(rpc->name != NULL && strlen(rpc->name) > 0);
   ASSERT(rpc->callback);
   ASSERT(rpc->xdrIn == NULL || rpc->xdrInSize > 0);
   if (cdata->rpcs == NULL) {
      cdata->rpcs = g_hash_table_new(g_str_hash, g_str_equal);
   }
   if (g_hash_table_lookup(cdata->rpcs, rpc->name) != NULL) {
      Panic("Trying to overwrite existing RPC registration for %s!\n", rpc->name);
   }
   g_hash_table_insert(cdata->rpcs, (gpointer) rpc->name, rpc);
}


/**
 * Unregisters a new RPC handler from the given RPC channel. This function is
 * not thread-safe.
 *
 * @param[in]  chan     The channel instance.
 * @param[in]  rpc      Info about the RPC being unregistered.
 */

void
RpcChannel_UnregisterCallback(RpcChannel *chan,
                              RpcChannelCallback *rpc)
{
   RpcChannelInt *cdata = (RpcChannelInt *) chan;
   if (cdata->rpcs != NULL) {
      g_hash_table_remove(cdata->rpcs, rpc->name);
   }
}


/**
 * Callback function to clear the cumulative channel error count when RpcIn
 * is able to establish a working connection following an error or reset.
 *
 * @param[in]  _chan       The RPC channel.
 */

static void
RpcChannelClearError(void *_chan)
{
   RpcChannelInt *chan = _chan;

   Debug(LGPFX "Clearing cumulative RpcChannel error count; was %d\n",
         chan->rpcFailureCount);
   chan->rpcFailureCount = 0;
}


/**
 * Error handling function for the RPC channel. Enqueues the "check reset"
 * function for running later, if it's not yet enqueued.
 *
 * @param[in]  _chan       The RPC channel.
 * @param[in]  status      Error description.
 */

static void
RpcChannelError(void *_chan,
                char const *status)
{
   RpcChannelInt *chan = _chan;

   chan->rpcError = TRUE;

   /*
    * XXX: Workaround for PR 935520.
    * Revert the log call to Warning() after fixing PR 955746.
    */
   Debug(LGPFX "Error in the RPC receive loop: %s.\n", status);

   /*
    * If an RPC failure callback has been registered and the failure limit
    * check has not been suppressed, check whether the RpcChannel failure
    * limit has been reached.
    */
   if (chan->rpcFailureCb != NULL &&
       chan->rpcMaxFailures > 0 &&
       ++chan->rpcFailureCount >= chan->rpcMaxFailures) {
      /* Maximum number of channel errors has been reached. */
      Warning(LGPFX "RpcChannel failure count %d; calling the failure "
                    "callback function.\n", chan->rpcFailureCount);
      chan->rpcFailureCb(chan->resetData);
   }

   if (chan->resetCheck == NULL) {
      chan->resetCheck = g_idle_source_new();
      g_source_set_callback(chan->resetCheck, RpcChannelCheckReset, chan, NULL);
      g_source_attach(chan->resetCheck, chan->mainCtx);
   }
}
#endif


/**
 * Creates a new RpcChannel without any implementation.
 *
 * This is mainly for use of code that is implementing a custom RpcChannel.
 * Such implementations should provide their own "constructor"-type function
 * which should then call this function to get an RpcChannel instance. They
 * should then fill in the function pointers that provide the implementation
 * for the channel before making the channel available to the callers.
 *
 * @return A new RpcChannel instance.
 */

RpcChannel *
RpcChannel_Create(void)
{
   RpcChannelInt *chan = g_new0(RpcChannelInt, 1);
   chan->impl.vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY;
   return &chan->impl;
}


/**
 * Shuts down an RPC channel and release any held resources.
 *
 * We use the outLock to protect the chan->inStarted and chan->in as well,
 * even though a separate lock is more desirable in theory to reduce contention.
 * See RpcChannel_Stop() and RpcChannelStopNoLock() where it is done same.
 *
 * @param[in]  chan     The RPC channel.
 *
 */

void
RpcChannel_Destroy(RpcChannel *chan)
{
   if (NULL == chan) {
      return;
   }

   g_mutex_lock(&chan->outLock);

   RpcChannelStopNoLock(chan);

   if (chan->funcs != NULL && chan->funcs->shutdown != NULL) {
      /* backend shutdown function also frees backend resources */
      chan->funcs->shutdown(chan);
   }

#if defined(NEED_RPCIN)
   RpcChannelTeardown(chan);
#endif

   g_mutex_unlock(&chan->outLock);

   g_mutex_clear(&chan->outLock);

   g_free(chan);
}


/**
 * Sets the non-freeable result of the given RPC context to the given value.
 * The result should be a NULL-terminated string.
 *
 * @param[in] data     RPC context.
 * @param[in] result   Result string.
 * @param[in] retVal   Return value of this function.
 *
 * @return @a retVal
 */

gboolean
RpcChannel_SetRetVals(RpcInData *data,
                      char const *result,
                      gboolean retVal)
{
   ASSERT(data);

   /* This cast is safe: data->result will not be freed. */
   data->result = (char *)result;
   data->resultLen = strlen(data->result);
   data->freeResult = FALSE;

   return retVal;
}


/**
 * Sets the freeable result of the given RPC context to the given value.
 * The result should be a NULL-terminated string.
 *
 * @param[in] data     RPC context.
 * @param[in] result   Result string.
 * @param[in] retVal   Return value of this function.
 *
 * @return @a retVal
 */

gboolean
RpcChannel_SetRetValsF(RpcInData *data,
                       char *result,
                       gboolean retVal)
{
   ASSERT(data);

   data->result = result;
   data->resultLen = strlen(data->result);
   data->freeResult = TRUE;

   return retVal;
}


/**
 * Force to create backdoor channels only.
 * This provides a control-switch to disable vsocket channels if needed.
 * This needs to be called before RpcChannel_New to take effect.
 */

void
RpcChannel_SetBackdoorOnly(void)
{
   gUseBackdoorOnly = TRUE;
   Debug(LGPFX "Using vsocket is disabled.\n");
}


/**
 * Create a one-off RpcChannel instance using a prefered channel implementation,
 * currently this is VSockChannel.
 *
 * @return  RpcChannel
 */

static RpcChannel *
RpcChannel_NewOne(int flags)
{
   RpcChannel *chan;
#if (defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)
   chan = gUseBackdoorOnly ? BackdoorChannel_New() : VSockChannel_New(flags);
#else
   chan = BackdoorChannel_New();
#endif
   return chan;
}


/**
 * Create an RpcChannel instance using a prefered channel implementation,
 * currently this is VSockChannel.
 *
 * @return  RpcChannel
 */

RpcChannel *
RpcChannel_New(void)
{
   return RpcChannel_NewOne(0);
}


/**
 * Start an RPC channel. We may fallback to backdoor channel when other type
 * of channel fails to start.
 *
 * @param[in]  chan        The RPC channel instance.
 *
 * @return TRUE on success.
 */

gboolean
RpcChannel_Start(RpcChannel *chan)
{
   gboolean ok;
   const RpcChannelFuncs *funcs;

   if (chan == NULL || chan->funcs == NULL || chan->funcs->start == NULL) {
      return FALSE;
   }

   if (chan->outStarted) {
#if defined(NEED_RPCIN)
      /* Already started. Make sure both channels are in sync and return. */
      ASSERT(chan->in == NULL || chan->inStarted);
#endif
      return TRUE;
   }

#if defined(NEED_RPCIN)
   if (chan->in != NULL && !chan->inStarted) {
      ok = RpcIn_start(chan->in, RPCIN_MAX_DELAY, RpcChannelError,
                       RpcChannelClearError, chan);
      chan->inStarted = ok;
   }
#endif

   funcs = chan->funcs;

#if (defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)
   if (!gUseBackdoorOnly && chan->isMutable &&
       funcs->getType(chan) == RPCCHANNEL_TYPE_BKDOOR) {
      /*
       * Try vsocket first for mutable channel.
       * Existing channel needs to be destroyed before switching.
       */
      Log(LGPFX "Restore vsocket RpcOut channel ...\n");
      funcs->destroy(chan);
      VSockChannel_Restore(chan, chan->vsockChannelFlags);
      funcs = chan->funcs;
   }
#endif

   ok = funcs->start(chan);

   /*
    * Try to fallback to Backdoor channel if the failed
    * channel is mutable and is not a Backdoor channel.
    */
   if (!ok && chan->isMutable &&
       funcs->getType(chan) != RPCCHANNEL_TYPE_BKDOOR) {
      Log(LGPFX "Fallback to backdoor RpcOut channel ...\n");
      funcs->destroy(chan);
      BackdoorChannel_Fallback(chan);
      funcs = chan->funcs;
      ok = funcs->start(chan);

      /*
       * As vSocket is not available, we stick the backdoor
       * behavior until the channel is reset/restarted or
       * retry delay has passed.
       */
      chan->vsockFailureTS = time(NULL);
      /*
       * Backoff retry attempts. Cap the delay at max value.
       */
      chan->vsockRetryDelay *= 2;
      if (chan->vsockRetryDelay > RPCCHANNEL_VSOCKET_RETRY_MAX_DELAY) {
         chan->vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MAX_DELAY;
      }
      Log(LGPFX "Sticking backdoor RpcOut channel for %u seconds.\n",
          chan->vsockRetryDelay);
   }

   return ok;
}


/**
 * Stop the RPC channel.
 * The outLock must be acquired by the caller.
 *
 * @param[in]  chan        The RPC channel instance.
 */

static void
RpcChannelStopNoLock(RpcChannel *chan)
{
   if (chan == NULL || chan->funcs == NULL || chan->funcs->stop == NULL) {
      return;
   }

   chan->funcs->stop(chan);

#if defined(NEED_RPCIN)
   if (chan->in != NULL) {
      if (chan->inStarted) {
         RpcIn_stop(chan->in);
         chan->inStarted = FALSE;
      }
   } else {
      ASSERT(!chan->inStarted);
   }
#endif
}


/**
 * Wrapper for the stop function of an RPC channel struct.
 *
 * @param[in]  chan        The RPC channel instance.
 */

void
RpcChannel_Stop(RpcChannel *chan)
{
   g_mutex_lock(&chan->outLock);
   RpcChannelStopNoLock(chan);
   g_mutex_unlock(&chan->outLock);
}


/**
 * Wrapper for get channel type function of an RPC channel struct.
 *
 * @param[in]  chan        The RPC channel instance.
 */

RpcChannelType
RpcChannel_GetType(RpcChannel *chan)
{
   if (chan == NULL || chan->funcs == NULL || chan->funcs->getType == NULL) {
      return RPCCHANNEL_TYPE_INACTIVE;
   }
   return chan->funcs->getType(chan);
}


/**
 * Free the allocated memory for the results from RpcChannel_Send* calls.
 *
 * @param[in] ptr   result from RpcChannel_Send* calls.
 *
 * @return none
 */

void
RpcChannel_Free(void *ptr)
{
   free(ptr);
}


/**
 * Send function of an RPC channel struct. Retry once if it fails for
 * non-backdoor Channels. Backdoor channel already tries inside. A second try
 * may create a different type of channel.
 *
 * @param[in]  chan        The RPC channel instance.
 * @param[in]  data        Data to send.
 * @param[in]  dataLen     Number of bytes to send.
 * @param[out] result      Response from other side (should be freed by
 *                         calling RpcChannel_Free).
 * @param[out] resultLen   Number of bytes in response.
 *
 * @return The status from the remote end (TRUE if call was successful).
 */

gboolean
RpcChannel_Send(RpcChannel *chan,
                char const *data,
                size_t dataLen,
                char **result,
                size_t *resultLen)
{
   gboolean ok;
   Bool rpcStatus;
   char *res = NULL;
   size_t resLen = 0;
   const RpcChannelFuncs *funcs;

   Debug(LGPFX "Sending: %"FMTSZ"u bytes\n", dataLen);

   ASSERT(chan && chan->funcs);

   g_mutex_lock(&chan->outLock);

   funcs = chan->funcs;
   ASSERT(funcs->send);

   if (result != NULL) {
      *result = NULL;
   }
   if (resultLen != NULL) {
      *resultLen = 0;
   }

#if (defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)
   if (chan->isMutable &&
       funcs->getType(chan) == RPCCHANNEL_TYPE_BKDOOR) {
      /*
       * Switch the channel type if it has been long enough
       * time since last vsocket failure.
       */
      gboolean tryVSocket = (chan->vsockFailureTS == 0 ||
                             (time(NULL) - chan->vsockFailureTS) >=
                             chan->vsockRetryDelay);
      if (tryVSocket && funcs->stop != NULL) {
         Log(LGPFX "Stop backdoor RpcOut channel and try vsock again ...\n");
         /*
          * Stop existing RpcOut channel and start it again.
          * RpcChannel_Start will switch it to vsocket when
          * possible or fallback to Backdoor again.
          */
         funcs->stop(chan);
         if (!RpcChannel_Start(chan)) {
            ok = FALSE;
            goto exit;
         }
         funcs = chan->funcs;
         ASSERT(funcs->send);
      }
   }
#endif

   ok = funcs->send(chan, data, dataLen, &rpcStatus, &res, &resLen);

   if (!ok && (funcs->getType(chan) != RPCCHANNEL_TYPE_BKDOOR) &&
       (funcs->stop != NULL)) {

      free(res);
      res = NULL;
      resLen = 0;

      /* retry once */
      Log(LGPFX "Stop vsock RpcOut channel and try to send again ...\n");
      funcs->stop(chan);
      /*
       * This is first send failure on vsocket RpcOut channel.
       * So, we re-init the failure timestamp and retry delay
       * because RpcChannel_Start tries vsocket first. In case
       * RpcChannel_Start falls back to Backdoor these will be
       * set appropriately.
       */
      chan->vsockFailureTS = 0;
      chan->vsockRetryDelay = RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY;
      if (RpcChannel_Start(chan)) {
         /* The channel may get switched from vsocket to backdoor */
         funcs = chan->funcs;
         ASSERT(funcs->send);
         ok = funcs->send(chan, data, dataLen, &rpcStatus, &res, &resLen);
         goto done;
      }

      ok = FALSE;
      goto exit;
   }

done:
   if (ok) {
      Debug(LGPFX "Recved %"FMTSZ"u bytes\n", resLen);
   }

   if (result != NULL) {
      *result = res;
   } else {
      free(res);
   }
   if (resultLen != NULL) {
      *resultLen = resLen;
   }

exit:
   g_mutex_unlock(&chan->outLock);
   return ok && rpcStatus;
}


/**
 * Open/close RpcChannel each time for sending a Rpc message, this is a wrapper
 * for RpcChannel APIs.
 *
 * @param[in]  data        request data
 * @param[in]  dataLen     data length
 * @param[in]  result      reply, should be freed by calling RpcChannel_Free.
 * @param[in]  resultLen   reply length
 * @param[in]  priv        TRUE : create VSock channel for privileged guest RPC.
                           FALSE: follow regular RPC channel creation process.

 * @returns    TRUE on success.
 */

static gboolean
RpcChannelSendOneRaw(const char *data,
                     size_t dataLen,
                     char **result,
                     size_t *resultLen,
                     gboolean priv)
{
   RpcChannel *chan;
   gboolean status = FALSE;
   int flags;

   flags = RPCCHANNEL_FLAGS_SEND_ONE;
#if (defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)
   flags |= RPCCHANNEL_FLAGS_FAST_CLOSE;
   chan = priv ? VSockChannel_New(flags) : RpcChannel_NewOne(flags);
#else
   chan = RpcChannel_NewOne(flags);
#endif

   if (chan == NULL) {
      if (result != NULL) {
         *result = Util_SafeStrdup("RpcChannel: Unable to create "
                                   "the RpcChannel object");
         if (resultLen != NULL) {
            *resultLen = strlen(*result);
         }
      }
      goto sent;
   } else if (!RpcChannel_Start(chan)) {
      if (result != NULL) {
         *result = Util_SafeStrdup("RpcChannel: Unable to open the "
                                   "communication channel");
         if (resultLen != NULL) {
            *resultLen = strlen(*result);
         }
      }
      goto sent;
   } else if (priv && RpcChannel_GetType(chan) != RPCCHANNEL_TYPE_PRIV_VSOCK) {
      if (result != NULL) {
         *result = Util_SafeStrdup(RPCCHANNEL_SEND_PERMISSION_DENIED);
         Warning("%s: failed to set up channel, returning '%s'\n",
                 __FUNCTION__, *result);
         if (resultLen != NULL) {
            *resultLen = strlen(*result);
         }
      }
      goto sent;
   } else if (!RpcChannel_Send(chan, data, dataLen, result, resultLen)) {
      /* We already have the description of the error */
      goto sent;
   }

   status = TRUE;

sent:
   Debug(LGPFX "Request %s: reqlen=%"FMTSZ"u, replyLen=%"FMTSZ"u\n",
         status ? "OK" : "FAILED", dataLen, resultLen ? *resultLen : 0);
   if (chan) {
      RpcChannel_Stop(chan);
      RpcChannel_Destroy(chan);
   }

   return status;
}


/**
 * Open/close RpcChannel each time for sending a Rpc message, this is a wrapper
 * for RpcChannel APIs.
 *
 * @param[in]  data        request data
 * @param[in]  dataLen     data length
 * @param[in]  result      reply, should be freed by calling RpcChannel_Free.
 * @param[in]  resultLen   reply length

 * @returns    TRUE on success.
 */

gboolean
RpcChannel_SendOneRaw(const char *data,
                      size_t dataLen,
                      char **result,
                      size_t *resultLen)
{
   return RpcChannelSendOneRaw(data, dataLen, result, resultLen, FALSE);
}


#if defined(__linux__) || defined(_WIN32)

/**
 * Open/close VSock RPC Channel each time for sending a privileged Rpc message,
 * this is a wrapper for RpcChannel APIs.
 *
 * @param[in]  data        request data
 * @param[in]  dataLen     data length
 * @param[in]  result      reply, should be freed by calling RpcChannel_Free.
 * @param[in]  resultLen   reply length

 * @returns    TRUE on success.
 */

gboolean
RpcChannel_SendOneRawPriv(const char *data,
                          size_t dataLen,
                          char **result,
                          size_t *resultLen)
{
   return RpcChannelSendOneRaw(data, dataLen, result, resultLen, TRUE);
}

#endif


/**
 * Open/close RpcChannel each time for sending a Rpc message, this is a wrapper
 * for RpcChannel APIs.
 *
 * @param[out] reply       reply, should be freed by calling RpcChannel_Free.
 * @param[out] repLen      reply length
 * @param[in]  reqFmt      request data
 * @param[in]  args        optional arguments depending on reqFmt.
 * @param[in]  priv        TRUE : create VSock channel for privileged guest RPC.
                           FALSE: follow regular RPC channel creation process.

 * @returns    TRUE on success.
 */

static gboolean
RpcChannelSendOne(char **reply,
                  size_t *repLen,
                  char const *reqFmt,
                  va_list args,
                  gboolean priv)
{
   gboolean status;
   char *request;
   size_t reqLen = 0;

   status = FALSE;

   /* Format the request string */
   request = Str_Vasprintf(&reqLen, reqFmt, args);

   /*
    * If Str_Vasprintf failed, write NULL into the reply if the caller wanted
    * a reply back.
    */
   if (request == NULL) {
      goto error;
   }

   status = RpcChannelSendOneRaw(request, reqLen, reply, repLen, priv);

   free(request);

   return status;

error:
   if (reply) {
      *reply = NULL;
   }

   if (repLen) {
      *repLen = 0;
   }
   return FALSE;
}


/**
 * Open/close RpcChannel each time for sending a Rpc message, this is a wrapper
 * for RpcChannel APIs.
 *
 * @param[out] reply       reply, should be freed by calling RpcChannel_Free.
 * @param[out] repLen      reply length
 * @param[in]  reqFmt      request data
 * @param[in]  ...         optional arguments depending on reqFmt.

 * @returns    TRUE on success.
 */

gboolean
RpcChannel_SendOne(char **reply,
                   size_t *repLen,
                   char const *reqFmt,
                   ...)
{
   va_list args;
   gboolean status;

   va_start(args, reqFmt);
   status = RpcChannelSendOne(reply, repLen, reqFmt, args, FALSE);
   va_end(args);

   return status;
}


#if defined(__linux__) || defined(_WIN32)

/**
 * Open/close VSock RPC Channel each time for sending a privileged Rpc message,
 * this is a wrapper for RpcChannel APIs.
 *
 * @param[out] reply       reply, should be freed by calling RpcChannel_Free.
 * @param[out] repLen      reply length
 * @param[in]  reqFmt      request data
 * @param[in]  ...         optional arguments depending on reqFmt.

 * @returns    TRUE on success.
 */

gboolean
RpcChannel_SendOnePriv(char **reply,
                       size_t *repLen,
                       char const *reqFmt,
                       ...)
{
   va_list args;
   gboolean status;

   va_start(args, reqFmt);
   status = RpcChannelSendOne(reply, repLen, reqFmt, args, TRUE);
   va_end(args);

   return status;
}

#endif
   070701000001C6000081A40000000000000000000000016822550500000CFF000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel/rpcChannelInt.h /*********************************************************
 * Copyright (C) 2008-2016,2018-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _RPCCHANNELINT_H_
#define _RPCCHANNELINT_H_

/**
 * @file rpcChannelInt.h
 *
 *    Internal definitions for the RPC channel library.
 */

#include "vmware/tools/guestrpc.h"

#if defined(USE_RPCI_ONLY)

#undef NEED_RPCIN

#else

#define NEED_RPCIN

/** Max amount of time (in .01s) that the RpcIn loop will sleep for. */
#define RPCIN_MAX_DELAY    10

struct RpcIn;
#endif

/*
 * Flags associated with the RPC Channel
 */

/* Channel will be usaed for a single RPC */
#define RPCCHANNEL_FLAGS_SEND_ONE     0x1
/* VMX should close channel after sending reply */
#define RPCCHANNEL_FLAGS_FAST_CLOSE   0x2

/** a list of interface functions for a channel implementation */
typedef struct _RpcChannelFuncs{
   gboolean (*start)(RpcChannel *);
   void (*stop)(RpcChannel *);
   gboolean (*send)(RpcChannel *, char const *data, size_t dataLen,
                    Bool *rpcStatus, char **result, size_t *resultLen);
   void (*setup)(RpcChannel *chan, GMainContext *mainCtx,
                 const char *appName, gpointer appCtx);
   void (*shutdown)(RpcChannel *);
   RpcChannelType (*getType)(RpcChannel *chan);
   void (*destroy)(RpcChannel *);
} RpcChannelFuncs;

/**
 * Defines the interface between the application and the RPC channel.
 *
 * XXX- outLock is badly named and is used to protect the in and out
 * channels, their state (inStarted/outStarted) and _private data.
 */
struct _RpcChannel {
   const RpcChannelFuncs *funcs;
   gpointer _private;
#if defined(NEED_RPCIN)
   GMainContext *mainCtx;
   const char *appName;
   gpointer appCtx;
   struct RpcIn *in;
   gboolean inStarted;
#endif
   GMutex outLock;
   gboolean outStarted;
   int vsockChannelFlags;
   /*
    * Only vsocket channel is mutable as it can fallback to Backdoor.
    * If a channel is created as Backdoor, it will not be mutable.
    */
   gboolean isMutable;
   /*
    * Track the last vsocket connection failure timestamp.
    * Avoid using vsocket until a channel reset/restart
    * occurs, the service restarts or retry delay has passed
    * since the last failure.
    */
   uint64 vsockFailureTS;
   /*
    * Amount of time to delay next vsocket retry attempt.
    * It varies between RPCCHANNEL_VSOCKET_RETRY_MIN_DELAY
    * and RPCCHANNEL_VSOCKET_RETRY_MAX_DELAY.
    */
   uint32 vsockRetryDelay;
};

void BackdoorChannel_Fallback(RpcChannel *chan);
void VSockChannel_Restore(RpcChannel *chan, int flags);

#endif /* _RPCCHANNELINT_H_ */
 070701000001C7000081A400000000000000000000000168225505000044BC000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel/simpleSocket.c  /*********************************************************
 * Copyright (c) 2013-2017,2019-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * simpleSocket.c --
 *
 *    Simple wrappers for socket.
 *
 */

#include <stdlib.h>
#if defined(__linux__)
#include <arpa/inet.h>
#endif

#include "simpleSocket.h"
#include "vmci_defs.h"
#include "vmci_sockets.h"
#include "vm_atomic.h"
#include "dataMap.h"
#include "err.h"
#include "debug.h"

#define LGPFX "SimpleSock: "


static int
SocketGetLastError(void);

/*
 *-----------------------------------------------------------------------------
 *
 * SocketStartup --
 *
 *      Win32 special socket init.
 *
 * Results:
 *      TRUE on success
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
SocketStartup(void)
{
#if defined(_WIN32)
   static Bool initialized = FALSE;
   int err;
   WSADATA wsaData;

   if (initialized) {
      return TRUE;
   }

   err = WSAStartup(MAKEWORD(2, 0), &wsaData);
   if (err) {
      Warning(LGPFX "Error in WSAStartup: %d[%s]\n", err,
              Err_Errno2String(err));
      return FALSE;
   }

   if (2 != LOBYTE(wsaData.wVersion) || 0 != HIBYTE(wsaData.wVersion)) {
      Warning(LGPFX "Unsupported Winsock version %d.%d\n",
              LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
      return FALSE;
   }

   initialized = TRUE;
#endif

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Socket_Close --
 *
 *      wrapper for socket close.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Socket_Close(SOCKET sock)
{
   int res;

#if defined(_WIN32)
   res = closesocket(sock);
#else
   res = close(sock);
#endif

   if (res == SOCKET_ERROR) {
      int err = SocketGetLastError();
      Warning(LGPFX "Error in closing socket %d: %d[%s]\n",
              sock, err, Err_Errno2String(err));
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * SocketGetLastError --
 *
 *      Get the last error code.
 *
 * Results:
 *      error code.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
SocketGetLastError(void)
{
#if defined(_WIN32)
   return WSAGetLastError();
#else
   return errno;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Socket_Recv --
 *
 *      Block until given number of bytes of data is received or error occurs.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

gboolean
Socket_Recv(SOCKET fd,      // IN
            char *buf,      // OUT
            int len)        // IN
{
   int remaining = len;
   int sysErr;

   while (remaining > 0) {
      int rv = recv(fd, buf , remaining, 0);
      if (rv == 0) {
         Debug(LGPFX "Socket %d closed by peer.", fd);
         return FALSE;
      }
      if (rv == SOCKET_ERROR) {
         sysErr = SocketGetLastError();
         if (sysErr == SYSERR_EINTR) {
            continue;
         }
         Warning(LGPFX "Recv error for socket %d: %d[%s]", fd, sysErr,
                 Err_Errno2String(sysErr));
         return FALSE;
      }
      remaining -= rv;
      buf += rv;
   }

   Debug(LGPFX "Recved %d bytes from socket %d\n", len, fd);
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Socket_Send --
 *
 *      Block until the given number of bytes of data is sent or error occurs.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

gboolean
Socket_Send(SOCKET fd,      // IN
            char *buf,      // IN
            int len)        // IN
{
   int left = len;
   int sent = 0;
   int sysErr;

   while (left > 0) {
      int rv = send(fd, buf + sent, left, 0);
      if (rv == SOCKET_ERROR) {
         sysErr = SocketGetLastError();
         if (sysErr == SYSERR_EINTR) {
            continue;
         }
         Warning(LGPFX "Send error for socket %d: %d[%s]", fd, sysErr,
                 Err_Errno2String(sysErr));
         return FALSE;
      }
      left -= rv;
      sent += rv;
   }

   Debug(LGPFX "Sent %d bytes from socket %d\n", len, fd);
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * SocketConnectVmciInternal --
 *
 *      Connect to a VSOCK destination in blocking mode
 *
 * Results:
 *      The socket created/connected upon success.
 *      INVALID_SOCKET upon a failure:
 *         apiErr and sysErr are populated with the proper error codes.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */
static SOCKET
SocketConnectVmciInternal(const struct sockaddr_vm *destAddr, // IN
                          unsigned int localPort,             // IN
                          ApiError *apiErr,                   // OUT
                          int *sysErr)                        // OUT
{
   SOCKET fd;
   struct sockaddr_vm localAddr;

   fd = socket(destAddr->svm_family, SOCK_STREAM, 0);
   if (fd == INVALID_SOCKET) {
      *apiErr = SOCKERR_SOCKET;
      *sysErr = SocketGetLastError();
      Warning(LGPFX "failed to create socket, error %d: %s\n",
              *sysErr, Err_Errno2String(*sysErr));
      return INVALID_SOCKET;
   }

   memset(&localAddr, 0, sizeof localAddr);
   localAddr.svm_family = destAddr->svm_family;
   localAddr.svm_cid = VMCISock_GetLocalCID();
   localAddr.svm_port = localPort;

   if (bind(fd, (struct sockaddr *)&localAddr, sizeof localAddr) != 0) {
      *apiErr = SOCKERR_BIND;
      *sysErr = SocketGetLastError();
      Debug(LGPFX "Couldn't bind on source port %d, error %d, %s\n",
            localPort, *sysErr, Err_Errno2String(*sysErr));
      Socket_Close(fd);
      return INVALID_SOCKET;
   }

   Debug(LGPFX "Successfully bound to source port %d\n", localPort);

   if (connect(fd, (struct sockaddr *)destAddr, sizeof *destAddr) != 0) {
      *apiErr = SOCKERR_CONNECT;
      *sysErr = SocketGetLastError();
      Warning(LGPFX "failed to connect (%d => %d), error %d: %s\n",
              localPort, destAddr->svm_port, *sysErr,
              Err_Errno2String(*sysErr));
      Socket_Close(fd);
      return INVALID_SOCKET;
   }

   *apiErr = SOCKERR_SUCCESS;
   *sysErr = 0;
   return fd;
}


/*
 *----------------------------------------------------------------------------
 *
 * Socket_ConnectVMCI --
 *
 *      Connect to a VMCI port in blocking mode.
 *      If isPriv is true, we will try to bind the local port to a port that
 *      is less than 1024.
 *
 * Results:
 *      The socket created/connected upon success.
 *      INVALID_SOCKET upon a failure:
 *         outApiErr and outSysErr are populated with proper error codes.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

SOCKET
Socket_ConnectVMCI(unsigned int cid,                  // IN
                   unsigned int port,                 // IN
                   gboolean isPriv,                   // IN
                   ApiError *outApiErr,               // OUT optional
                   int *outSysErr)                    // OUT optional
{
#define MAX_ECONNRESET_RETRIES 8
#define MAX_ENOBUFS_RETRIES    5

   static Atomic_Bool useVsock = { TRUE };
   struct sockaddr_vm addr;
   unsigned int localPort;
   SOCKET fd;
   int sysErr = 0;
   ApiError apiErr;
   int vsockDev = -1;
   int family = VMCISock_GetAFValueFd(&vsockDev);
   int retryCountConnReset = 0;
   int retryCountNoBufs = 0;

   if (family == -1) {
      Warning(LGPFX "Couldn't get VMCI socket family info.");
      apiErr = SOCKERR_VMCI_FAMILY;
      fd = INVALID_SOCKET;
      goto done;
   }

   if (!SocketStartup()) {
      apiErr = SOCKERR_STARTUP;
      fd = INVALID_SOCKET;
      goto done;
   }

   memset((char *)&addr, 0, sizeof addr);
   addr.svm_family = family;
   addr.svm_cid = cid;
   addr.svm_port = port;

   Debug(LGPFX "creating new socket, connecting to %u:%u\n", cid, port);

   if (!isPriv) {
      fd = SocketConnectVmciInternal(&addr, VMADDR_PORT_ANY,
                                     &apiErr, &sysErr);
      goto done;
   }

   /* We are required to use a privileged source port. */
   localPort = PRIVILEGED_PORT_MAX;
   while (localPort >= PRIVILEGED_PORT_MIN) {
      fd = SocketConnectVmciInternal(&addr, localPort, &apiErr, &sysErr);
      if (fd != INVALID_SOCKET) {
         goto done;
      }

      if (apiErr == SOCKERR_BIND && sysErr == SYSERR_EADDRINUSE) {
         --localPort;
         continue; /* Try next port */
      }

      if (Atomic_ReadBool(&useVsock) &&
          apiErr == SOCKERR_CONNECT && sysErr == SYSERR_ECONNRESET) {
         /*
          * VMX might be slow releasing a port pair
          * when another client closed the client side end.
          * Simply try next port.
          */
         if (++retryCountConnReset >= MAX_ECONNRESET_RETRIES) {
            Warning(LGPFX "Give up after %d connect() retries for ECONNRESET. "
                          "Check if vmx option guest_rpc.rpci.usevsocket "
                          "is set to FALSE.\n", MAX_ECONNRESET_RETRIES);
            Atomic_WriteBool(&useVsock, FALSE);
            goto done;
         }

         --localPort;
         continue;
      }

      if (apiErr == SOCKERR_CONNECT && sysErr == SYSERR_EINTR) {
         /*
          * EINTR on connect due to signal.
          * Try again using the same port.
          */
         continue;
      }

      if (apiErr == SOCKERR_CONNECT && sysErr == SYSERR_ENOBUFS) {
         /*
          * ENOBUFS can happen if we're out of vsocks in the kernel.
          * Delay a bit and try again using the same port.
          * Have a retry count in case something has gone horribly wrong.
          */
         if (++retryCountNoBufs >= MAX_ENOBUFS_RETRIES) {
            Warning(LGPFX "Give up after %d connect() retries for ENOBUFS.\n",
                    MAX_ENOBUFS_RETRIES);
            goto done;
         }

#ifdef _WIN32
         Sleep(1);
#else
         usleep(1000);
#endif
         continue;
      }

      /* Unrecoverable error occurred */
      goto done;
   }

   Debug(LGPFX "Failed to connect using a privileged port.\n");

done:

   VMCISock_ReleaseAFValueFd(vsockDev);

   if (outApiErr) {
      *outApiErr = apiErr;
   }

   if (outSysErr) {
      *outSysErr = sysErr;
   }

   if (fd != INVALID_SOCKET) {
      Debug(LGPFX "socket %d connected\n", fd);
      Atomic_WriteBool(&useVsock, TRUE);
   }

   return fd;

#undef MAX_ECONNRESET_RETRIES
#undef MAX_ENOBUFS_RETRIES
}


/*
 *-----------------------------------------------------------------------------
 *
 * Socket_DecodePacket --
 *
 *    Helper function to decode received packet in DataMap encoding format.
 *
 * Result:
 *    None
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
Socket_DecodePacket(const char *recvBuf,       // IN
                    int fullPktLen,            // IN
                    char **payload,            // OUT
                    int32 *payloadLen)         // OUT
{
   ErrorCode res;
   DataMap map;
   char *buf;
   int32 len;

   *payload = NULL;
   *payloadLen = 0;

   /* decoding the packet */
   res = DataMap_Deserialize(recvBuf, fullPktLen, &map);
   if (res != DMERR_SUCCESS) {
      Debug(LGPFX "Error in dataMap decoding, error=%d\n", res);
      return FALSE;
   }

   res = DataMap_GetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, &buf, &len);
   if (res == DMERR_SUCCESS) {
      char *tmpPtr = malloc(len + 1);
      if (tmpPtr == NULL) {
         Debug(LGPFX "Error in allocating memory\n");
         goto error;
      }
      memcpy(tmpPtr, buf, len);
      /* add a trailing 0 for backward compatibility */
      tmpPtr[len] = '\0';

      *payload = tmpPtr;
      *payloadLen = len;
   } else {
      Debug(LGPFX "Error in decoding payload, error=%d\n", res);
      goto error;
   }

   DataMap_Destroy(&map);
   return TRUE;

error:
   DataMap_Destroy(&map);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Socket_PackSendData --
 *
 *    Helper function for building send packet and serialize it.
 *
 * Result:
 *    TRUE on sucess, FALSE otherwise.
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
Socket_PackSendData(const char *buf,             // IN
                    int len,                     // IN
                    Bool fastClose,              // IN
                    char **serBuf,               // OUT
                    int32 *serBufLen)            // OUT
{
   DataMap map;
   ErrorCode res;
   char *newBuf;
   gboolean mapCreated = FALSE;
   int64 pktType = GUESTRPCPKT_TYPE_DATA;

   res = DataMap_Create(&map);
   if (res != DMERR_SUCCESS) {
      goto error;
   }

   mapCreated = TRUE;
   res = DataMap_SetInt64(&map, GUESTRPCPKT_FIELD_TYPE,
                          pktType, TRUE);
   if (res != DMERR_SUCCESS) {
      goto error;
   }

   newBuf = malloc(len);
   if (newBuf == NULL) {
      Debug(LGPFX "Error in allocating memory.\n");
      goto error;
   }
   memcpy(newBuf, buf, len);
   res = DataMap_SetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, newBuf,
                           len, TRUE);
   if (res != DMERR_SUCCESS) {
      free(newBuf);
      goto error;
   }

   if (fastClose) {
      res = DataMap_SetInt64(&map, GUESTRPCPKT_FIELD_FAST_CLOSE, TRUE, TRUE);
      if (res != DMERR_SUCCESS) {
         goto error;
      }
   }

   res = DataMap_Serialize(&map, serBuf, serBufLen);
   if (res != DMERR_SUCCESS) {
      goto error;
   }

   DataMap_Destroy(&map);
   return TRUE;

error:
   if (mapCreated) {
      DataMap_Destroy(&map);
   }
   Debug(LGPFX "Error in dataMap encoding\n");
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Socket_RecvPacket --
 *
 *    Helper function to recv a dataMap packet over the socket.
 *    The caller has to *free* the payload to avoid memory leak.
 *
 * Result:
 *    TRUE on sucess, FALSE otherwise.
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
Socket_RecvPacket(SOCKET sock,               // IN
                  char **payload,            // OUT
                  int *payloadLen)           // OUT
{
   gboolean ok;
   uint32 packetLen;
   uint32 partialPktLen;
   int packetLenSize = sizeof packetLen;
   int fullPktLen;
   char *recvBuf;

   ok = Socket_Recv(sock, (char *)&packetLen, packetLenSize);
   if (!ok) {
      Debug(LGPFX "error in recving packet header, err=%d\n",
            SocketGetLastError());
      return FALSE;
   }

   partialPktLen = ntohl(packetLen);
   if (partialPktLen > INT_MAX - packetLenSize) {
      Panic(LGPFX "Invalid packetLen value 0x%08x\n", packetLen);
   }

   fullPktLen = partialPktLen + packetLenSize;
   recvBuf = malloc(fullPktLen);
   if (recvBuf == NULL) {
      Debug(LGPFX "Could not allocate recv buffer.\n");
      return FALSE;
   }

   memcpy(recvBuf, &packetLen, packetLenSize);
   ok = Socket_Recv(sock, recvBuf + packetLenSize,
                     fullPktLen - packetLenSize);
   if (!ok) {
      Debug(LGPFX "error in recving packet, err=%d\n",
            SocketGetLastError());
      free(recvBuf);
      return FALSE;
   }

   ok = Socket_DecodePacket(recvBuf, fullPktLen, payload, payloadLen);
   free(recvBuf);
   return ok;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Socket_SendPacket --
 *
 *    Helper function to send a dataMap packet over the socket.
 *
 * Result:
 *    TRUE on sucess, FALSE otherwise.
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
Socket_SendPacket(SOCKET sock,               // IN
                  const char *payload,       // IN
                  int payloadLen,            // IN
                  Bool fastClose)            // IN
{
   gboolean ok;
   char *sendBuf;
   int sendBufLen;

   if (!Socket_PackSendData(payload, payloadLen, fastClose,
                            &sendBuf, &sendBufLen)) {
      return FALSE;
   }

   ok = Socket_Send(sock, sendBuf, sendBufLen);
   free(sendBuf);

   return ok;
}
070701000001C8000081A40000000000000000000000016822550500000B1D000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel/simpleSocket.h  /*********************************************************
 * Copyright (C) 2013-2016,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _SIMPLESOCKET_H_
#define _SIMPLESOCKET_H_

/**
 * @file simpleSocket.h
 *
 *    header of simple socket wrappers.
 */

#include <glib.h>

#if defined(_WIN32)
#include <winsock2.h>
#include <winerror.h>
#else
#include <errno.h>
#endif

#include "vmci_defs.h"
#include "vmware/guestrpc/tclodefs.h"

/* Describe which socket API call failed */
typedef enum {
   SOCKERR_SUCCESS,
   SOCKERR_VMCI_FAMILY,
   SOCKERR_STARTUP,
   SOCKERR_SOCKET,
   SOCKERR_CONNECT,
   SOCKERR_BIND
} ApiError;

#if defined(_WIN32)

#define SYSERR_EADDRINUSE        WSAEADDRINUSE
#define SYSERR_EACCESS           WSAEACCES
#define SYSERR_EINTR             WSAEINTR
#define SYSERR_ECONNRESET        WSAECONNRESET
#define SYSERR_ENOBUFS           WSAENOBUFS

typedef int socklen_t;

#else  /* !_WIN32 */

#define SYSERR_EADDRINUSE        EADDRINUSE
#define SYSERR_EACCESS           EACCES
#define SYSERR_EINTR             EINTR
#define SYSERR_ECONNRESET        ECONNRESET
#define SYSERR_ENOBUFS           ENOBUFS

typedef int SOCKET;
#define SOCKET_ERROR              (-1)
#define INVALID_SOCKET            ((SOCKET) -1)

#endif

#define PRIVILEGED_PORT_MAX    1023
#define PRIVILEGED_PORT_MIN    1

void Socket_Close(SOCKET sock);
SOCKET Socket_ConnectVMCI(unsigned int cid,
                          unsigned int port,
                          gboolean isPriv,
                          ApiError *outApiErr,
                          int *outSysErr);
gboolean Socket_Recv(SOCKET fd,
                     char *buf,
                     int len);
gboolean Socket_Send(SOCKET fd,
                     char *buf,
                     int len);
gboolean Socket_RecvPacket(SOCKET sock,
                           char **payload,
                           int *payloadLen);
gboolean Socket_SendPacket(SOCKET sock,
                           const char *payload,
                           int payloadLen,
                           Bool fastClose);

#endif /* _SIMPLESOCKET_H_ */
   070701000001C9000081A40000000000000000000000016822550500003BBA000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcChannel/vsockChannel.c  /*********************************************************
 * Copyright (C) 2013-2016,2018-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vsockChannel.c --
 *
 *    Implement RpcChannel using vsocket.
 *
 */

#include <stdlib.h>
#include <string.h>

#include "simpleSocket.h"
#include "rpcChannelInt.h"
#include "util.h"
#include "debug.h"

#define LGPFX "VSockChan: "

/*
 * Time to wait in milliseconds before retrying a vsock RPC start
 */
#define VSOCK_START_RETRY_WAIT_TIME 100

/* Maximum number of times to retry a failed vsock RPC Channel start. */
#define VSOCK_CHANNEL_START_MAX_RETRIES  2

typedef struct VSockOut {
   SOCKET fd;
   char *payload;
   int payloadLen;
   RpcChannelType type;
   int flags;
} VSockOut;

typedef struct VSockChannel {
   VSockOut          *out;
} VSockChannel;

static void VSockChannelShutdown(RpcChannel *chan);


/*
 *-----------------------------------------------------------------------------
 *
 * VSockCreateConn --
 *
 *      Create vsocket connection. we try a privileged connection first,
 *      fallback to unprivileged one if that fails.
 *
 * Result:
 *      a valid socket/fd on success or INVALID_SOCKET on failure.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static SOCKET
VSockCreateConn(gboolean *isPriv)        // OUT
{
   ApiError apiErr;
   int sysErr;
   SOCKET fd;

   Debug(LGPFX "Creating privileged vsocket ...\n");
   fd = Socket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID,
                           GUESTRPC_RPCI_VSOCK_LISTEN_PORT,
                           TRUE, &apiErr, &sysErr);

   if (fd != INVALID_SOCKET) {
      Debug(LGPFX "Successfully created priv vsocket %d\n", fd);
      *isPriv = TRUE;
      return fd;
   }

   if (apiErr == SOCKERR_BIND && sysErr == SYSERR_EACCESS) {
      Debug(LGPFX "Creating unprivileged vsocket ...\n");
      fd = Socket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID,
                              GUESTRPC_RPCI_VSOCK_LISTEN_PORT,
                              FALSE, &apiErr, &sysErr);
      if (fd != INVALID_SOCKET) {
         Debug(LGPFX "Successfully created unpriv vsocket %d\n", fd);
         *isPriv = FALSE;
         return fd;
      }
   }

   Debug(LGPFX "Failed to create vsocket channel, %d, %d\n", apiErr, sysErr);
   return INVALID_SOCKET;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockOutConstruct --
 *
 *      Constructor for the VSockOut object
 *
 * Results:
 *      New VSockOut object.
 *
 * Side effects:
 *      Allocates memory.
 *
 *-----------------------------------------------------------------------------
 */

static VSockOut *
VSockOutConstruct(int flags)
{
   VSockOut *out = calloc(1, sizeof *out);

   if (out != NULL) {
      out->fd = INVALID_SOCKET;
      out->type = RPCCHANNEL_TYPE_INACTIVE;
      out->flags = flags;
   }
   return out;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockOutDestruct --
 *
 *      Destructor for the VSockOut object.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Frees VSockOut object memory.
 *
 *-----------------------------------------------------------------------------
 */

static void
VSockOutDestruct(VSockOut *out)        // IN
{

   ASSERT(out);
   ASSERT(out->fd == INVALID_SOCKET);

   free(out->payload);
   free(out);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockOutStart --
 *
 *      Start the VSockOut channel by creating a vsocket connection.
 *
 * Result:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
VSockOutStart(VSockOut *out)      // IN
{
   gboolean isPriv;

   ASSERT(out);
   ASSERT(out->fd == INVALID_SOCKET);

   out->fd = VSockCreateConn(&isPriv);
   if (out->fd != INVALID_SOCKET) {
      out->type = isPriv ? RPCCHANNEL_TYPE_PRIV_VSOCK :
                           RPCCHANNEL_TYPE_UNPRIV_VSOCK;
   }
   return out->fd != INVALID_SOCKET;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockOutStop --
 *
 *    Close the underlying vsocket for the VSockOut channel
 *
 * Result
 *    None
 *
 * Side-effects
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VSockOutStop(VSockOut *out)    // IN
{
   ASSERT(out);

   if (out->fd != INVALID_SOCKET) {
      Socket_Close(out->fd);
      out->fd = INVALID_SOCKET;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockOutSend --
 *
 *    Make VMware synchronously execute a TCLO command
 *
 *    Unlike the other send varieties, VSockOutSend requires that the
 *    caller pass non-NULL reply and repLen arguments.
 *
 * Result
 *    TRUE if RPC was sent successfully. 'reply' contains the result of the rpc.
 *    rpcStatus tells if the RPC command was processed successfully.
 *
 *    FALSE if RPC could not be sent successfully. 'reply' will contain a
 *    description of the error.
 *
 *    In both cases, the caller should not free the reply.
 *
 * Side-effects
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
VSockOutSend(VSockOut *out,        // IN
             const char *request,  // IN
             size_t reqLen,        // IN
             Bool *rpcStatus,      // OUT
             const char **reply,   // OUT
             size_t *repLen)       // OUT
{
   ASSERT(out);
   ASSERT(out->fd != INVALID_SOCKET);

   *reply = NULL;
   *repLen = 0;

   Debug(LGPFX "Sending request for conn %d,  reqLen=%d\n",
         out->fd, (int)reqLen);

   if (!Socket_SendPacket(out->fd, request, reqLen,
                          (out->flags & RPCCHANNEL_FLAGS_FAST_CLOSE))) {
      *reply = "VSockOut: Unable to send data for the RPCI command";
      goto error;
   }

   free(out->payload);
   out->payload = NULL;

   if (!Socket_RecvPacket(out->fd, &out->payload, &out->payloadLen)) {
      *reply = "VSockOut: Unable to receive the result of the RPCI command";
      goto error;
   }

   if (out->payloadLen < 2 ||
       ((out->payload[0] != '1') && (out->payload[0] != '0')) ||
       out->payload[1] != ' ') {
      *reply = "VSockOut: Invalid format for the result of the RPCI command";
      goto error;
   }

   *reply = out->payload + 2;
   *repLen = out->payloadLen - 2;

   Debug("VSockOut: recved %d bytes for conn %d\n", out->payloadLen, out->fd);

   *rpcStatus = out->payload[0] == '1';
   return TRUE;

error:
   *repLen = strlen(*reply);
   *rpcStatus = FALSE;
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannelDestroy --
 *
 *      Callback function to destroy the VSockChannel after it fails to start
 *      or it has been stopped.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VSockChannelDestroy(RpcChannel *chan)    // IN
{
   VSockChannel *vsock = chan->_private;

   /*
    * Channel should be stopped before destroying it.
    */
   ASSERT(!chan->outStarted);
   VSockOutDestruct(vsock->out);
   g_free(vsock);
   chan->_private = NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannelStart --
 *
 *      Starts the VSockOut channel.
 *
 * Results:
 *      TRUE on success.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
VSockChannelStart(RpcChannel *chan)    // IN
{
   gboolean ret = TRUE;
   VSockChannel *vsock = chan->_private;

#if defined(NEED_RPCIN)
   ret = chan->in == NULL || chan->inStarted;
#endif

   if (ret) {
      ret = VSockOutStart(vsock->out);
      if (!ret && (vsock->out->flags & RPCCHANNEL_FLAGS_SEND_ONE) == 0) {
         int retryCnt = 0;

         while (!ret && (retryCnt++ < VSOCK_CHANNEL_START_MAX_RETRIES)) {
            /*
             * VMX may take some time to cleanup a previous vsocket, so delay
             * the retry a little bit.  The retry is needed for the cases when
             * there is a channel start attempt in quick succession and the
             * first attempt failed because VMX was still cleaning up the
             * previous vsocket.
             *
             * Take a 100 msec pause.
             */
            g_usleep(VSOCK_START_RETRY_WAIT_TIME * 1000);
            Debug(LGPFX "VSockChannel Start - retry %d\n", retryCnt);
            ret = VSockOutStart(vsock->out);
         }
      }
   }
   chan->outStarted = ret;

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannelStop --
 *
 *      Stops a channel, keeping internal state so that it can be restarted
 *      later. It's safe to call this function more than once.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VSockChannelStop(RpcChannel *chan)   // IN
{
   VSockChannel *vsock = chan->_private;

   if (vsock->out != NULL) {
      if (chan->outStarted) {
         VSockOutStop(vsock->out);
         chan->outStarted = FALSE;
      }
   } else {
      ASSERT(!chan->outStarted);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannelShutdown --
 *
 *      Shuts down the VSockChannel.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VSockChannelShutdown(RpcChannel *chan)    // IN
{
   VSockChannelStop(chan);
   VSockChannelDestroy(chan);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannelSend --
 *
 *      Sends the data using the vsocket channel.
 *      If the caller is not interested in the reply, result and resultLen
 *      can be set to NULL, otherwise, the caller *must* free the result
 *      whether the call is successful or not to avoid memory leak.
 *
 *      rpcStatus tells if VMware could process the RPC command successully.
 *      It is valid only when function returns success.
 *
 * Result:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
VSockChannelSend(RpcChannel *chan,      // IN
                 char const *data,      // IN
                 size_t dataLen,        // IN
                 Bool *rpcStatus,       // OUT
                 char **result,         // OUT optional
                 size_t *resultLen)     // OUT optional
{
   gboolean ret = FALSE;
   VSockChannel *vsock = chan->_private;
   const char *reply = NULL;
   size_t replyLen = 0;

   if (!chan->outStarted) {
      goto exit;
   }

   /*
    * We propagate all replies from VSockOutSend: either a reply of the RPC
    * result or a description of the error on failure.
    */
   ret = VSockOutSend(vsock->out, data, dataLen, rpcStatus, &reply, &replyLen);

   if (result != NULL) {
      if (reply != NULL) {
         *result = Util_SafeMalloc(replyLen + 1);
         memcpy(*result, reply, replyLen);
         (*result)[replyLen] = '\0';
      } else {
         *result = NULL;
      }
   }

   if (resultLen != NULL) {
      *resultLen = replyLen;
   }

exit:
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannelGetType --
 *
 *      Return the channel type that is being used.
 *
 * Result:
 *      return RpcChannelType
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static RpcChannelType
VSockChannelGetType(RpcChannel *chan)     // IN
{
   VSockChannel *vsock = chan->_private;

   if (vsock->out != NULL) {
      return vsock->out->type;
   } else {
      return RPCCHANNEL_TYPE_INACTIVE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannelSetCallbacks --
 *
 *      Helper function to setup RpcChannel callbacks.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
VSockChannelSetCallbacks(RpcChannel *chan)      // IN
{
   static RpcChannelFuncs funcs = {
      VSockChannelStart,
      VSockChannelStop,
      VSockChannelSend,
      NULL,
      VSockChannelShutdown,
      VSockChannelGetType,
      VSockChannelDestroy
   };

   ASSERT(chan);
   chan->funcs = &funcs;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannel_New --
 *
 *      Creates a new RpcChannel that uses the vsocket for
 *      communication.
 *
 * Result:
 *      return A new channel instance (never NULL).
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

RpcChannel *
VSockChannel_New(int flags)   // IN
{
   RpcChannel *chan;
   VSockChannel *vsock;

   chan = RpcChannel_Create();
   vsock = g_malloc0(sizeof *vsock);

   vsock->out = VSockOutConstruct(flags);
   ASSERT(vsock->out != NULL);

#if defined(NEED_RPCIN)
   chan->inStarted = FALSE;
#endif
   chan->outStarted = FALSE;
   chan->vsockChannelFlags = flags;
   /*
    * VSock channel is mutable, it can fallback/change to Backdoor.
    */
   chan->isMutable = TRUE;

   VSockChannelSetCallbacks(chan);
   chan->_private = vsock;
   g_mutex_init(&chan->outLock);

   return chan;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VSockChannel_Restore --
 *
 *      Restores RpcChannel as VSockChannel.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
VSockChannel_Restore(RpcChannel *chan,    // IN
                     int flags)           // IN
{
   VSockChannel *vsock;

   ASSERT(chan);
   ASSERT(chan->_private == NULL);

   vsock = g_malloc0(sizeof *vsock);
   vsock->out = VSockOutConstruct(flags);
   ASSERT(vsock->out != NULL);

   VSockChannelSetCallbacks(chan);
   chan->_private = vsock;
}
  070701000001CA000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002D00000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcIn  070701000001CB000081A40000000000000000000000016822550500000419000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcIn/Makefile.am  ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libRpcIn.la

libRpcIn_la_SOURCES =
libRpcIn_la_SOURCES += rpcin.c

libRpcIn_la_CPPFLAGS =
libRpcIn_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@

   070701000001CC000081A4000000000000000000000001682255050000B6E2000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcIn/rpcin.c  /*********************************************************
 * Copyright (c) 1998-2020, 2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * rpcin.c --
 *
 *    Remote Procedure Call between VMware and guest applications
 *    C implementation.
 *
 *    This module implements the 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 (the in direction is more complicated).
 */

#ifdef __KERNEL__
#   include "kernelStubs.h"
#else
#   include <stdio.h>
#   include <string.h>
#   include <stdlib.h>
#   include <stdarg.h>
#   if defined(_WIN32) && defined(_MSC_VER)
#      include <windows.h>
#   endif
#   include "debug.h"
#   include "str.h"
#   include "strutil.h"
#endif

#include "vm_basic_types.h"

#if ((defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)) && \
    defined(VMTOOLS_USE_GLIB)
#define VMTOOLS_USE_VSOCKET
#else
#undef  VMTOOLS_USE_VSOCKET
#endif

#include <ctype.h>

#if defined(VMTOOLS_USE_VSOCKET)
#  include <glib.h>
#  include "poll.h"
#  include "asyncsocket.h"
#  include "vmci_defs.h"
#include "dataMap.h"
#if defined(__linux__)
#include <arpa/inet.h>
#else
#include <winsock2.h>
#endif
#endif

#if defined(VMTOOLS_USE_GLIB)
#  include "vmware/tools/guestrpc.h"
#  include "vmware/tools/utils.h"
#endif

#include "vmware/guestrpc/tclodefs.h"
#include "vmware.h"
#include "message.h"
#include "rpcin.h"
#include "util.h"
#include "system.h"

#if !defined(VMTOOLS_USE_GLIB)
#include "eventManager.h"

/* Which event queue should RPC events be added to? */
static DblLnkLst_Links *gTimerEventQueue;

/*
 * The RpcIn object
 */

/* The list of TCLO command callbacks we support */
typedef struct RpcInCallbackList {
   const char *name;
   size_t length; /* Length of name so we don't have to strlen a lot */
   RpcIn_Callback callback;
   struct RpcInCallbackList *next;
   void *clientData;
} RpcInCallbackList;

#endif /* VMTOOLS_USE_GLIB */

#if defined(VMTOOLS_USE_VSOCKET)

#define RPCIN_HEARTBEAT_INTERVAL              1000             /* 1 second */
#define RPCIN_MIN_SEND_BUF_SIZE               (64 * 1024)
#define RPCIN_MIN_RECV_BUF_SIZE               (64 * 1024)

struct RpcIn;

/*  container for each vsocket connection details */
typedef struct _ConnInfo {
   AsyncSocket *asock;

   int32 packetLen;
   char *recvBuf;
   int recvBufLen;

   Bool connected;
   Bool shutDown;
   Bool recvStopped;
   int sendQueueLen;

   VmTimeType timestamp;

   struct RpcIn *in;
} ConnInfo;

static void RpcInConnRecvHeader(ConnInfo *conn);
static Bool RpcInConnRecvPacket(ConnInfo *conn, const char **errmsg);
#endif  /* VMTOOLS_USE_VSOCKET */


struct RpcIn {
#if defined(VMTOOLS_USE_GLIB)
   GSource *nextEvent;
   GMainContext *mainCtx;
   RpcIn_Callback dispatch;
   gpointer clientData;
#else
   RpcInCallbackList *callbacks;
   Event *nextEvent;
#endif

#if defined(VMTOOLS_USE_VSOCKET)
   ConnInfo *conn;
   GSource *heartbeatSrc;
#endif

   Message_Channel *channel;
   unsigned int delay;   /* The delay of the previous iteration of RpcInLoop */
   unsigned int maxDelay;  /* The maximum delay to schedule in RpcInLoop */
   RpcIn_ErrorFunc *errorFunc;
   void *errorData;

   /*
    * State of the result associated to the last TCLO request we received
    */

   /* Should we send the result back? */
   Bool mustSend;

   /* The result itself */
   char *last_result;

   /* The size of the result */
   size_t last_resultLen;

   /*
    * It's possible for a callback dispatched by RpcInLoop to call RpcIn_stop.
    * When this happens, we corrupt the state of the RpcIn struct, resulting in
    * a crash the next time RpcInLoop is called. To prevent corruption of the
    * RpcIn struct, we check inLoop when RpcIn_stop is called, and if it is
    * true, we set shouldStop to TRUE instead of actually stopping the
    * channel. When RpcInLoop exits, it will stop the channel if shouldStop is
    * TRUE.
    */
   Bool inLoop;     // RpcInLoop is running.
   Bool shouldStop; // Stop the channel the next time RpcInLoop exits.

   /*
    * RpcInConnErrorHandler called; cleared when a non "reset" reply has been
    * received.
    */
   Bool errStatus;
   RpcIn_ClearErrorFunc *clearErrorFunc;
};

static Bool RpcInSend(RpcIn *in, int flags);
static Bool RpcInScheduleRecvEvent(RpcIn *in);
static void RpcInStop(RpcIn *in);
static Bool RpcInExecRpc(RpcIn *in,            // IN
                         const char *reply,    // IN
                         size_t repLen,        // IN
                         const char **errmsg); // OUT
static Bool RpcInOpenChannel(RpcIn *in, Bool useBackdoorOnly);

/*
 * The following functions are only needed in the non-glib version of the
 * library. The glib version of the library only deals with the transport
 * aspects of the code - RPC dispatching and other RPC-layer concerns are
 * handled by the rpcChannel abstraction library, or by the application.
 */

#if !defined(VMTOOLS_USE_GLIB)

/*
 *-----------------------------------------------------------------------------
 *
 * RpcInPingCallback --
 *
 *      Replies to a ping message from the VMX.
 *
 * Results:
 *      TRUE.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcInPingCallback(char const **result,     // OUT
                  size_t *resultLen,       // OUT
                  const char *name,        // IN
                  const char *args,        // IN
                  size_t argsSize,         // IN
                  void *clientData)        // IN
{
   return RpcIn_SetRetVals(result, resultLen, "", TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcIn_Construct --
 *
 *      Constructor for the RpcIn object.
 *
 * Results:
 *      New RpcIn object.
 *
 * Side effects:
 *      Sets the current timer event queue, allocates memory.
 *
 *-----------------------------------------------------------------------------
 */

RpcIn *
RpcIn_Construct(DblLnkLst_Links *eventQueue)
{
   RpcIn *result;
   result = (RpcIn *)calloc(1, sizeof(RpcIn));

   gTimerEventQueue = result? eventQueue: NULL;
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInLookupCallback --
 *
 *      Lookup a callback struct in our list.
 *
 * Results:
 *      The callback if found
 *      NULL if not found
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

static RpcInCallbackList *
RpcInLookupCallback(RpcIn *in,        // IN
                    const char *name) // IN
{
   RpcInCallbackList *p;

   ASSERT(in);
   ASSERT(name);

   for (p = in->callbacks; p; p = p->next) {
      if (strcmp(name, p->name) == 0) {
         return p;
      }
   }

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcIn_RegisterCallback --
 *
 *      Register an old-style callback to happen when a TCLO message is
 *      received. When a TCLO message beginning with 'name' is
 *      sent, the callback will be called with: the cmd name, the args
 *      (starting with the char directly after the cmd name; that's why
 *      it's helpful to add a space to the name if arguments are expected),
 *      and a pointer to the result.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
RpcIn_RegisterCallback(RpcIn *in,               // IN
                       const char *name,        // IN
                       RpcIn_Callback cb,       // IN
                       void *clientData)        // IN
{
   RpcInCallbackList *p;

   Debug("RpcIn: Registering callback '%s'\n", name);

   ASSERT(in);
   ASSERT(name);
   ASSERT(cb);
   ASSERT(RpcInLookupCallback(in, name) == NULL); // not there yet

   p = (RpcInCallbackList *) malloc(sizeof(RpcInCallbackList));
   ASSERT_NOT_IMPLEMENTED(p);

   p->length = strlen(name);
   p->name = strdup(name);
   p->callback = cb;
   p->clientData = clientData;

   p->next = in->callbacks;

   in->callbacks = p;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcIn_UnregisterCallback --
 *
 *      Unregisters an RpcIn callback by name.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
RpcIn_UnregisterCallback(RpcIn *in,               // IN
                         const char *name)        // IN
{
   RpcInCallbackList *cur, *prev;

   ASSERT(in);
   ASSERT(name);

   Debug("RpcIn: Unregistering callback '%s'\n", name);

   for (cur = in->callbacks, prev = NULL; cur && strcmp(cur->name, name);
        prev = cur, cur = cur->next);

   /*
    * If we called UnregisterCallback on a name that doesn't exist, we
    * have a problem.
    */
   VERIFY(cur != NULL);

   if (prev == NULL) {
      in->callbacks = cur->next;
   } else {
      prev->next = cur->next;
   }
   free((void *)cur->name);
   free(cur);
}


#else /* VMTOOLS_USE_GLIB */


/*
 *-----------------------------------------------------------------------------
 *
 * RpcIn_Construct --
 *
 *      Constructor for the RpcIn object. Ties the RpcIn loop to the given
 *      glib main loop, and uses the given callback to dispatch incoming
 *      RPC messages.
 *
 *      The dispatch callback receives data in a slightly different way than
 *      the regular RPC callbacks. Basically, the raw data from the backdoor
 *      is provided in the "args" field of the RpcInData struct, and "name"
 *      is NULL. So the dispatch function is responsible for parsing the RPC
 *      message, and preparing the RpcInData instance for proper use by the
 *      final consumer.
 *
 * Results:
 *      New RpcIn object.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

RpcIn *
RpcIn_Construct(GMainContext *mainCtx,    // IN
                RpcIn_Callback dispatch,  // IN
                gpointer clientData)      // IN
{
   RpcIn *result;

#if defined(VMTOOLS_USE_VSOCKET)
   Poll_InitGtk();
#endif

   ASSERT(mainCtx != NULL);
   ASSERT(dispatch != NULL);

   result = calloc(1, sizeof *result);
   if (result != NULL) {
      result->mainCtx = mainCtx;
      result->clientData = clientData;
      result->dispatch = dispatch;
   }
   return result;
}

#endif /* VMTOOLS_USE_GLIB */


/*
 *-----------------------------------------------------------------------------
 *
 * RpcIn_Destruct --
 *
 *      Destructor for the RpcIn object.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Frees all memory associated with the RpcIn object, resets the global
 *      timer event queue.
 *
 *-----------------------------------------------------------------------------
 */

void
RpcIn_Destruct(RpcIn *in) // IN
{
   ASSERT(in);
   ASSERT(in->channel == NULL);
   ASSERT(in->nextEvent == NULL);
   ASSERT(in->mustSend == FALSE);

#if defined(VMTOOLS_USE_VSOCKET)
   ASSERT(in->conn == NULL);
#endif

#if !defined(VMTOOLS_USE_GLIB)
   while (in->callbacks) {
      RpcInCallbackList *p;

      p = in->callbacks->next;
      free((void *) in->callbacks->name);
      free(in->callbacks);
      in->callbacks = p;
   }

   gTimerEventQueue = NULL;
#endif

   free(in);
}


#if defined(VMTOOLS_USE_VSOCKET)

/*
 *-----------------------------------------------------------------------------
 *
 * RpcInConnStopRecv --
 *
 *    Stop recving from the vsocket connection.
 *
 * Result:
 *    None
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInConnStopRecv(ConnInfo *conn)   // IN
{
   if (!(conn->recvStopped)) {
      int res = AsyncSocket_CancelRecvEx(conn->asock, NULL, NULL, NULL, TRUE);
      if (res != ASOCKERR_SUCCESS) {
         /* just log an error, we are closing the socket anyway */
         Debug("RpcIn: error in stopping recv for conn %d\n",
               AsyncSocket_GetFd(conn->asock));
      }
      conn->recvStopped = TRUE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInCloseConn --
 *
 *      Close vsocket connection.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInCloseConn(ConnInfo *conn) // IN
{
   int fd = AsyncSocket_GetFd(conn->asock);

   if (conn->in != NULL) {
      conn->in->conn = NULL;
      conn->in = NULL;
   }

   if (conn->sendQueueLen > 0) {
      Debug("RpcIn: Shutting down vsocket connection %d.\n", fd);
      conn->shutDown = TRUE;
      RpcInConnStopRecv(conn);
   } else {
      Debug("RpcIn: Closing vsocket connection %d\n", fd);
      AsyncSocket_Close(conn->asock);
      free(conn->recvBuf);
      free(conn);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInConnSendDoneCb --
 *
 *    AsyncSocket callback function for a send completion.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInConnSendDoneCb(void *buf,            // IN
                    int len,              // IN
                    AsyncSocket *asock,   // IN
                    void *clientData)     // IN
{
   ConnInfo *conn = (ConnInfo *)clientData;

   free(buf);

   if (AsyncSocket_GetState(asock) == AsyncSocketClosed) {
      /* the connection is closed or being closed. */
      return;
   }

   conn->sendQueueLen -= len;
   ASSERT(conn->sendQueueLen >= 0);

   if (conn->sendQueueLen == 0 && conn->shutDown) {
      Debug("RpcIn: Closing connection %d as sendbuffer is now empty.\n",
            AsyncSocket_GetFd(conn->asock));
      RpcInCloseConn(conn);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInPackSendData --
 *
 *    Helper function for building send packet and serialize it.
 *
 * Result:
 *    TRUE on sucess, FALSE otherwise.
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcInPackSendData(int fd,                      // IN
                  const char *buf,             // IN
                  int len,                     // IN
                  int flags,                   // IN
                  char **serBuf,               // OUT
                  int32 *serBufLen)            // OUT
{
   DataMap map;
   ErrorCode res;
   char *newBuf;
   gboolean mapCreated = FALSE;
   int64 pktType = (flags & RPCIN_TCLO_PING) ?
                   GUESTRPCPKT_TYPE_PING : GUESTRPCPKT_TYPE_DATA;

   res = DataMap_Create(&map);
   if (res != DMERR_SUCCESS) {
      goto quit;
   }

   mapCreated = TRUE;
   res = DataMap_SetInt64(&map, GUESTRPCPKT_FIELD_TYPE,
                          pktType, TRUE);
   if (res != DMERR_SUCCESS) {
      goto quit;
   }

   if (buf != NULL && len > 0) {
      newBuf = malloc(len);
      if (newBuf == NULL) {
         Debug("RpcIn: Error in allocating memory for conn %d.\n", fd);
         goto quit;
      }
      memcpy(newBuf, buf, len);
      res = DataMap_SetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, newBuf,
                              len, TRUE);
      if (res != DMERR_SUCCESS) {
         free(newBuf);
         goto quit;
      }
   }

   res = DataMap_Serialize(&map, serBuf, serBufLen);
   if (res != DMERR_SUCCESS) {
      goto quit;
   }

   DataMap_Destroy(&map);
   return TRUE;

quit:
   if (mapCreated) {
      DataMap_Destroy(&map);
   }
   Debug("RpcIn: Error in dataMap encoding for conn %d.\n", fd);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInConnSend --
 *
 *    Helper function for writing data to a socket.
 *    ownership of buffer is untouched.
 *
 * Result:
 *    TRUE on sucess, FALSE otherwise.
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcInConnSend(ConnInfo *conn,              // IN
              const char *buf,             // IN
              int len,                     // IN
              int flags)                   // IN
{
   int res;
   int32 packetLen;
   char *packetBuf;
   int fd = AsyncSocket_GetFd(conn->asock);

   Debug("RpcIn: sending msg to conn %d: len=%d\n",
         AsyncSocket_GetFd(conn->asock), len);

   if (!RpcInPackSendData(fd, buf, len, flags, &packetBuf, &packetLen)) {
      return FALSE;
   }

   res = AsyncSocket_Send(conn->asock, packetBuf, packetLen,
                          RpcInConnSendDoneCb, conn);
   if (res != ASOCKERR_SUCCESS) {
      Debug("RpcIn: error in AsyncSocket_Send for socket %d: %s\n",
            AsyncSocket_GetFd(conn->asock), AsyncSocket_Err2String(res));
      free(packetBuf);
      return FALSE;
   } else {
      conn->sendQueueLen += packetLen;
      return TRUE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInCloseChannel --
 *
 *      Close channel on error.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInCloseChannel(RpcIn *in,               // IN
                  char const *errmsg)      // IN
{
   /* Call the error routine */
   (*in->errorFunc)(in->errorData, errmsg);
   RpcInStop(in);
   in->shouldStop = FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInHeartbeatCallback --
 *
 *      Callback function to send a heartbeat message to VMX.
 *
 * Result:
 *      TRUE to keep the callback, FALSE otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
RpcInHeartbeatCallback(void *clientData)      // IN
{
   RpcIn *in = (RpcIn *)clientData;
   ASSERT(in);
   if (in->conn) {
      ASSERT(!in->mustSend);
      ASSERT(in->last_result == NULL);
      ASSERT(in->last_resultLen == 0);

      in->mustSend = TRUE;
      if (RpcInSend(in, RPCIN_TCLO_PING)) {
         return TRUE;
      } else {
         char *errmsg = "RpcIn: Unable to send";
         RpcInCloseChannel(in, errmsg);
         return FALSE;
      }
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInRegisterHeartbeatCallback --
 *
 *      Register a callback so we can send heartbeat messages periodically, HA
 *      monitoring depends on this.
 *
 * Result:
 *      None.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInRegisterHeartbeatCallback(RpcIn *in)      // IN
{
   ASSERT(in->heartbeatSrc == NULL);
   in->heartbeatSrc = VMTools_CreateTimer(RPCIN_HEARTBEAT_INTERVAL);
   if (in->heartbeatSrc != NULL) {
      g_source_set_callback(in->heartbeatSrc, RpcInHeartbeatCallback, in, NULL);
      g_source_attach(in->heartbeatSrc, in->mainCtx);
   } else {
      Debug("RpcIn: error in scheduling heartbeat callback.\n");
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInDecodePacket --
 *
 *    Helper function to decode received packet in DataMap encoding format.
 *
 * Result:
 *    None
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
RpcInDecodePacket(ConnInfo *conn,       // IN
                  char **payload,       // OUT
                  int32 *payloadLen)    // OUT
{
   ErrorCode res;
   DataMap map;
   int fd = AsyncSocket_GetFd(conn->asock);
   int fullPacketLen = conn->packetLen + sizeof conn->packetLen;
   char *buf;
   int32 len;


   /* decoding the packet */
   res = DataMap_Deserialize(conn->recvBuf, fullPacketLen, &map);
   if (res != DMERR_SUCCESS) {
      Debug("RpcIn: Error in dataMap decoding for conn %d, error=%d\n",
            fd, res);
      return FALSE;
   }

   res = DataMap_GetString(&map, GUESTRPCPKT_FIELD_PAYLOAD, &buf, &len);
   if (res == DMERR_SUCCESS) {
      char *tmpPtr = (char *)malloc(len + 1);
      if (tmpPtr == NULL) {
         Debug("RpcIn: Error in allocating memory for conn %d\n", fd);
         goto exit;
      }
      memcpy(tmpPtr, buf, len);
      /* add a trailing 0 for backward compatible */
      tmpPtr[len] = '\0';

      *payload = tmpPtr;
      *payloadLen = len;
   } else {
      Debug("RpcIn: Empty payload for conn %d\n", fd);
      goto exit;
   }

   DataMap_Destroy(&map);
   return TRUE;

exit:
   DataMap_Destroy(&map);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInConnRecvedCb --
 *
 *    AsyncSocket callback function after data is recved.
 *
 * Result:
 *    None
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInConnRecvedCb(void *buf,            // IN
                  int len,              // IN
                  AsyncSocket *asock,   // IN
                  void *clientData)     // IN
{
   ConnInfo *conn = (ConnInfo *)clientData;
   const char *errmsg = NULL;

   ASSERT(conn != NULL);

   if (buf == &conn->packetLen) {
      /* We just received the packet header*/
      conn->packetLen = ntohl(conn->packetLen);
      Debug("RpcIn:: Got packet length %d from conn %d.\n",
            conn->packetLen, AsyncSocket_GetFd(conn->asock));
      if (!RpcInConnRecvPacket(conn, &errmsg)) {
         RpcInCloseChannel(conn->in, errmsg);
      }
   } else {
      char *payload = NULL;
      int32 payloadLen = 0;

      ASSERT(buf == conn->recvBuf + sizeof conn->packetLen);
      ASSERT(len <= conn->recvBufLen - sizeof conn->packetLen);

      if (!RpcInDecodePacket(conn, &payload, &payloadLen)) {
         errmsg = "RpcIn: packet error";
         RpcInCloseChannel(conn->in, errmsg);
         return;
      }

      Debug("RpcIn: Got msg from conn %d: [%s]\n",
            AsyncSocket_GetFd(conn->asock), payload);

      if (RpcInExecRpc(conn->in, payload, payloadLen, &errmsg)) {
         conn->in->mustSend = TRUE;
         if (RpcInSend(conn->in, 0)) {
            if (conn->in->heartbeatSrc == NULL) {
               /* Register heartbeat callback after the first successful send
                * so we do not mess with TCLO protocol. */
               RpcInRegisterHeartbeatCallback(conn->in);
            }
            RpcInConnRecvHeader(conn);
            free(payload);
            return;
         } else {
            errmsg = "RpcIn: Unable to send";
         }
      }

      RpcInCloseChannel(conn->in, errmsg);  /* on error */
      free(payload);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInConnRecvHeader --
 *
 *    Register header recv callback for vsocket connection.
 *
 * Result:
 *    None
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInConnRecvHeader(ConnInfo *conn)   // IN
{
   int res;
   res = AsyncSocket_Recv(conn->asock, &conn->packetLen,
                          sizeof conn->packetLen,
                          RpcInConnRecvedCb, conn);

   conn->recvStopped = res != ASOCKERR_SUCCESS;
   if (res != ASOCKERR_SUCCESS) {
      Debug("RpcIn: error in recving packet header for conn: %d\n",
            AsyncSocket_GetFd(conn->asock));
      RpcInCloseChannel(conn->in, "RpcIn: error in recv");
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInConnRecvPacket --
 *
 *    Register packet recv callback for vsocket connection.
 *
 * Result:
 *    TRUE on success, FALSE on failure.
 *
 * Side-effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcInConnRecvPacket(ConnInfo *conn,         // IN
                    const char **errmsg)    // OUT
{
   int res;
   int32 pktLen = conn->packetLen;
   int fullPktLen = pktLen + sizeof pktLen;

   if (conn->recvBuf == NULL || conn->recvBufLen < fullPktLen) {
      // allocate buffer if needed.
      conn->recvBufLen = fullPktLen;
      free(conn->recvBuf);
      conn->recvBuf = malloc(conn->recvBufLen);
      if (conn->recvBuf == NULL) {
         Debug("RpcIn: Could not allocate recv buffer for socket %d, "
               "closing connection.\n", AsyncSocket_GetFd(conn->asock));
         *errmsg = "Couldn't allocate enough memory";
         return FALSE;
      }
   }

   *((int32 *)(conn->recvBuf)) = htonl(pktLen);
   res = AsyncSocket_Recv(conn->asock, conn->recvBuf + sizeof pktLen,
                          pktLen, RpcInConnRecvedCb, conn);

   conn->recvStopped = res != ASOCKERR_SUCCESS;
   if (res != ASOCKERR_SUCCESS) {
      Debug("RpcIn: error in recving packet for conn %d, "
            "closing connection.\n", AsyncSocket_GetFd(conn->asock));
      *errmsg = "RpcIn: error in recv";
   }
   return res == ASOCKERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInConnErrorHandler --
 *
 *      Connection error handler for asyncsocket.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInConnErrorHandler(int err,             // IN
                      AsyncSocket *asock,  // IN
                      void *clientData)    // IN
{
   ConnInfo *conn = (ConnInfo *)clientData;
   RpcIn *in = conn->in;

   Debug("RpcIn: Error in socket %d, closing connection: %s.\n",
         AsyncSocket_GetFd(asock), AsyncSocket_Err2String(err));

   in->errStatus = TRUE;

   if (conn->connected) {
      RpcInCloseChannel(conn->in, "RpcIn: vsocket connection error");
   } else { /* the connection never gets connected */
      RpcInCloseConn(conn);
      Debug("RpcIn: falling back to use backdoor ...\n");
      RpcInOpenChannel(in, TRUE);  /* fall back on backdoor */
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInConnectDone --
 *
 *      Callback function for AsyncSocket connect.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInConnectDone(AsyncSocket *asock,   // IN
                 void *clientData)     // IN
{
   ConnInfo *conn = (ConnInfo *)clientData;
   RpcIn *in = conn->in;

   if (AsyncSocket_GetState(asock) != AsyncSocketConnected) {
      goto exit;
   }

   if (!AsyncSocket_EstablishMinBufferSizes(asock, RPCIN_MIN_SEND_BUF_SIZE,
                                            RPCIN_MIN_RECV_BUF_SIZE)) {
      goto exit;
   }

   conn->connected = TRUE;
   RpcInConnRecvHeader(conn);
   return;

exit:
   Debug("RpcIn: failed to create vsocket connection, using backdoor.\n");
   RpcInCloseConn(conn);
   RpcInOpenChannel(in, TRUE);  /* fall back on backdoor */
}

#endif  /* VMTOOLS_USE_VSOCKET */


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInSend --
 *
 *      Send the last result back to VMware
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side-effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcInSend(RpcIn *in,   // IN
          int flags)   // IN
{
   Bool status = FALSE;
   Bool useBackdoor = TRUE;

   ASSERT(in);
   ASSERT(in->mustSend);

#if defined(VMTOOLS_USE_VSOCKET)
   if (in->conn != NULL) {
      useBackdoor = FALSE;
      status = RpcInConnSend(in->conn, in->last_result, in->last_resultLen,
                             flags);
   }
#endif

   if (useBackdoor) {
      ASSERT(in->channel);
      if (in->last_resultLen) {
         Debug("RpcIn: sending %"FMTSZ"u bytes\n", in->last_resultLen);
      }
      status = Message_Send(in->channel, (unsigned char *)in->last_result,
                            in->last_resultLen);
   }

   if (status == FALSE) {
      Debug("RpcIn: couldn't send back the last result\n");
   }

   free(in->last_result);
   in->last_result = NULL;
   in->last_resultLen = 0;
   in->mustSend = FALSE;

   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInStop --
 *
 *      Stop the RPC channel.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Sends the last result back to the host.
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInStop(RpcIn *in) // IN
{
   ASSERT(in);
   if (in->nextEvent) {
      /* The loop is started. Stop it */
#if defined(VMTOOLS_USE_GLIB)
      if (!in->inLoop) {
         g_source_destroy(in->nextEvent);
      }

      g_source_unref(in->nextEvent);
#else
      EventManager_Remove(in->nextEvent);
#endif
      in->nextEvent = NULL;
   }

   if (in->channel) {
      /* The channel is open */
      if (in->mustSend) {
         /* There is a final result to send back. Try to send it */
         RpcInSend(in, 0);
         ASSERT(in->mustSend == FALSE);
      }

      /* Try to close the channel */
      if (Message_Close(in->channel) == FALSE) {
         Debug("RpcIn: couldn't close channel\n");
      }

      in->channel = NULL;
   }

#if defined(VMTOOLS_USE_VSOCKET)
   if (in->conn != NULL) {
      if (in->mustSend) {
         /* There is a final result to send back. Try to send it */
         RpcInSend(in, 0);
      }
      RpcInCloseConn(in->conn);
   }

   if (in->heartbeatSrc != NULL) {
      g_source_destroy(in->heartbeatSrc);
      g_source_unref(in->heartbeatSrc);
      in->heartbeatSrc = NULL;
   }
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcIn_stop --
 *
 *      Stop the RPC channel.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      Sends the last result to the host, if one exists.
 *
 *-----------------------------------------------------------------------------
 */

void
RpcIn_stop(RpcIn *in) // IN
{
   if (in->inLoop) {
      in->shouldStop = TRUE;
   } else {
      RpcInStop(in);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInExecRpc --
 *
 *      Call dispatcher to run the RPC.
 *
 * Result:
 *      TRUE on success, FALSE on error.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcInExecRpc(RpcIn *in,            // IN
             const char *reply,    // IN
             size_t repLen,        // IN
             const char **errmsg)  // OUT
{
   unsigned int status;
   const char *statusStr;
   unsigned int statusLen;
   char *result;
   size_t resultLen;
   Bool freeResult = FALSE;

   /*
    * Execute the RPC
    */

#if defined(VMTOOLS_USE_GLIB)
   RpcInData data = { NULL, reply, repLen, NULL, 0, FALSE, NULL, in->clientData };

   status = in->dispatch(&data);
   result = data.result;
   resultLen = data.resultLen;
   freeResult = data.freeResult;
#else
   char *cmd;
   unsigned int index = 0;
   RpcInCallbackList *cb = NULL;

   cmd = StrUtil_GetNextToken(&index, reply, " ");
   if (cmd != NULL) {
      cb = RpcInLookupCallback(in, cmd);
      if (cb) {
         result = NULL;
         status = cb->callback((char const **) &result, &resultLen, cb->name,
                               reply + cb->length, repLen - cb->length,
                               cb->clientData);
         ASSERT(result);
      } else {
         Debug("RpcIn: Unknown Command '%s': No matching callback\n", cmd);
         status = FALSE;
         result = GUEST_RPC_UNKNOWN_COMMAND;
         resultLen = strlen(result);
      }
      free(cmd);
   } else {
      Debug("RpcIn: Bad command (null) received\n");
      status = FALSE;
      result = "Bad command";
      resultLen = strlen(result);
   }
#endif

   statusStr = status ? "OK " : "ERROR ";
   statusLen = strlen(statusStr);

   /*
    * Coverity warns that we don't allocate enough space for a null terminator.
    * A null terminator is not necessary here as we also store the length.
    */
   // coverity[alloc_strlen]
   in->last_result = (char *)malloc(statusLen + resultLen);
   if (in->last_result == NULL) {
      *errmsg = "RpcIn: Not enough memory";
      return FALSE;
   }
   memcpy(in->last_result, statusStr, statusLen);
   memcpy(in->last_result + statusLen, result, resultLen);
   in->last_resultLen = statusLen + resultLen;

   if (freeResult) {
      free(result);
   }

   /*
    * Run the event pump (in case VMware sends a long sequence of RPCs and
    * perfoms a time-consuming job) and continue to loop immediately
    */
   in->delay = 0;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInUpdateDelayTime --
 *
 *      Calculate new delay time.
 *      Use an exponential back-off, doubling the time to wait each time up to
 *      the max delay.
 *
 * Result:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcInUpdateDelayTime(RpcIn *in)            // IN
{
   if (in->delay < in->maxDelay) {
      if (in->delay > 0) {
         /*
          * Catch overflow.
          */
         in->delay = ((in->delay * 2) > in->delay) ? (in->delay * 2) : in->maxDelay;
      } else {
         in->delay = 1;
      }
      in->delay = MIN(in->delay, in->maxDelay);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * ByteDump --
 *
 *      Return a \0 terminated string that keeps ascii characters
 *      but escapes non ascii ones.
 *
 *      The output may be truncated if an internal buffer limit is reached.
 *
 * Result:
 *      Return a string that the caller should not free
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char *
ByteDump(const char *buf,
        size_t size)
{
#define BYTE_DUMP_LIMIT 128
   static const char truncationTag[] = "...";
   static char dumpBuffer[BYTE_DUMP_LIMIT + sizeof truncationTag];
   size_t i, count, nPrintable, nBinary;

   count = 0;
   nPrintable = 0;
   nBinary = 0;

   if (! size) {
      goto exit;
   }

   for (i = 0; i < size; ++i) {
      unsigned char c = buf[i];
      if (c == '\\') {
         if ((BYTE_DUMP_LIMIT - count) < 2) {
            break;
         }
         dumpBuffer[count++] = c;
         dumpBuffer[count++] = c;
         ++nPrintable;
      } else if (isprint(c)) {
         if ((BYTE_DUMP_LIMIT - count) < 1) {
            break;
         }
         dumpBuffer[count++] = c;
         ++nPrintable;
      } else {
         if ((BYTE_DUMP_LIMIT - count) < 3) {
            break;
         }
         dumpBuffer[count++] = '\\';
         Str_Snprintf(&dumpBuffer[count], 3, "%02x", c);
         count += 2;
         ++nBinary;
      }
   }

   if (nBinary > nPrintable) {
      return "(assumed/dropped binary data)";
   }

   if (i < size) {
      /* Data is truncated */
      int n = Str_Snprintf(&dumpBuffer[count], sizeof dumpBuffer - count,
                           "%s", truncationTag);
      ASSERT(n);
      count += n;
   }

exit:

   dumpBuffer[count] = 0;

   return dumpBuffer;
}


/*
 * RpcInClearErrorStatus --
 *
 *      Clear the errStatus indicator and if a callback has been registered,
 *      notify the RpcChannel layer that an error condition has been cleared.
 */

static void
RpcInClearErrorStatus(RpcIn *in) // IN
{
   if (in->errStatus) {
      Debug("RpcIn: %s: Clearing errStatus\n", __FUNCTION__);
      in->errStatus = FALSE;
      if (in->clearErrorFunc != NULL) {
         in->clearErrorFunc(in->errorData);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInLoop --
 *
 *      Receives an RPC from the host.
 *
 * Result:
 *      For the Event Manager implementation, always TRUE.
 *
 *      For the glib implementation, returns FALSE if the timer was rescheduled
 *      so that g_main_loop will unregister the old timer, or TRUE otherwise.
 *
 * Side-effects:
 *      Stops the RPC channel on error.
 *
 *-----------------------------------------------------------------------------
 */

#if defined(VMTOOLS_USE_GLIB)
static gboolean
#else
static Bool
#endif
RpcInLoop(void *clientData) // IN
{
   RpcIn *in;
   char const *errmsg = NULL;
   char const *reply;
   size_t repLen;
   Bool resched = FALSE;

#if defined(VMTOOLS_USE_GLIB)
   unsigned int current;
#endif

   in = (RpcIn *)clientData;
   ASSERT(in);
   ASSERT(in->nextEvent);
   ASSERT(in->channel);
   ASSERT(in->mustSend);

#if defined(VMTOOLS_USE_GLIB)
   current = in->delay;
#else
   /*
    * The event has fired: it is no longer valid. Note that this is
    * not true in the glib case!
    */
   in->nextEvent = NULL;
#endif

   in->inLoop = TRUE;

   /*
    * Workaround for bug 780404. Remove if we ever figure out the root cause.
    * Note that the ASSERT above catches this on non-release builds.
    */
   if (in->channel == NULL) {
      errmsg = "RpcIn: Channel is not active";
      goto error;
   }

   /*
    * This is very important: this is the only way to signal the existence of
    * this guest application to VMware.
    */
   if (RpcInSend(in, 0) == FALSE) {
      errmsg = "RpcIn: Unable to send";
      goto error;
   }

   if (Message_Receive(in->channel, (unsigned char **)&reply, &repLen) == FALSE) {
      errmsg = "RpcIn: Unable to receive";
      goto error;
   }

   if (repLen) {
      char *s = ByteDump(reply, repLen);
      Debug("RpcIn: received %d bytes, content:\"%s\"\n", (int) repLen, s);

      /* If reply is not a "reset", the channel is functioning. */
      if (in->errStatus && strcmp(s, "reset") != 0) {
         RpcInClearErrorStatus(in);
      }

      if (!RpcInExecRpc(in, reply, repLen, &errmsg)) {
         goto error;
      }
   } else {
      static uint64 lastPrintMilli = 0;
      uint64 now = System_GetTimeMonotonic() * 10;
      if ((now - lastPrintMilli) > 5000) {
         /*
          * Throttle the log to write one entry every 5 seconds
          * this allow us to figure that tools side is polling for TCLO.
          */
         Debug("RpcIn: received 0 bytes, empty TCLO poll\n");
         lastPrintMilli = now;
      }

      /* RpcIn connection is working - receiving. */
      if (in->errStatus) {
         RpcInClearErrorStatus(in);
      }

      /*
       * Nothing to execute
       */

      /* No request -> No result */
      ASSERT(in->last_result == NULL);
      ASSERT(in->last_resultLen == 0);

      RpcInUpdateDelayTime(in);
   }

   ASSERT(in->mustSend == FALSE);
   in->mustSend = TRUE;

   if (!in->shouldStop) {
      Bool needResched = TRUE;
#if defined(VMTOOLS_USE_GLIB)
      resched = in->delay != current;
      needResched = resched;
#endif
      if (needResched && !RpcInScheduleRecvEvent(in)) {
         errmsg = "RpcIn: Unable to run the loop";
         goto error;
      }
   }

exit:
   if (in->shouldStop) {
      RpcInStop(in);
      in->shouldStop = FALSE;
#if defined(VMTOOLS_USE_GLIB)
      /* Force the GMainContext to unref the GSource that runs the RpcIn loop. */
      resched = TRUE;
#endif
   }

   in->inLoop = FALSE;

   return !resched;

error:
   /* Call the error routine */
   (*in->errorFunc)(in->errorData, errmsg);
   in->shouldStop = TRUE;
   goto exit;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInScheduleRecvEvent --
 *
 *      Setup corresponding callback functions to recv data.
 *
 * Result:
 *      TRUE on success, FALSE on error.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcInScheduleRecvEvent(RpcIn *in)      // IN
{
#if defined(VMTOOLS_USE_GLIB)
   if (in->nextEvent != NULL) {
      g_source_unref(in->nextEvent);
   }
   in->nextEvent = VMTools_CreateTimer(in->delay * 10);
   if (in->nextEvent != NULL) {
      g_source_set_callback(in->nextEvent, RpcInLoop, in, NULL);
      g_source_attach(in->nextEvent, in->mainCtx);
   }
#else
   in->nextEvent = EventManager_Add(gTimerEventQueue, in->delay, RpcInLoop, in);
#endif
   return in->nextEvent != NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcInOpenChannel --
 *
 *    Create backdoor or vsocket channel.
 *
 * Result
 *    TRUE on success
 *    FALSE on failure
 *
 * Side-effects
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcInOpenChannel(RpcIn *in,                 // IN
                 Bool useBackdoorOnly)      // IN
{
#if defined(VMTOOLS_USE_VSOCKET)
   static Bool first = TRUE;
   static Bool initOk = TRUE;
   AsyncSocket *asock;
   int res;

   ASSERT(in->conn == NULL);

   while (TRUE) {  /* one pass loop */
      if (useBackdoorOnly) {
         break;
      }

      if (first) {
         first = FALSE;
         res = AsyncSocket_Init();
         initOk = (res == ASOCKERR_SUCCESS);
         if (!initOk) {
            Debug("RpcIn: Error in socket initialization: %s\n",
                  AsyncSocket_Err2String(res));
            break;
         }
      }

      if (!initOk) {
         break;
      }

      in->conn = calloc(1, sizeof *(in->conn));
      if (in->conn == NULL) {
         Debug("RpcIn: Error in allocating memory for vsocket connection.\n");
         break;
      }
      in->conn->in = in;
      asock = AsyncSocket_ConnectVMCI(VMCI_HYPERVISOR_CONTEXT_ID,
                                      GUESTRPC_TCLO_VSOCK_LISTEN_PORT,
                                      RpcInConnectDone,
                                      in->conn, 0, NULL, &res);
      if (asock == NULL) {
         Debug("RpcIn: Error in creating vsocket connection: %s\n",
               AsyncSocket_Err2String(res));
      } else {
         res = AsyncSocket_SetErrorFn(asock, RpcInConnErrorHandler, in->conn);
         if (res != ASOCKERR_SUCCESS) {
            Debug("RpcIn: Error in setting error handler for vsocket %d\n",
                  AsyncSocket_GetFd(asock));
            AsyncSocket_Close(asock);
         } else {
            Debug("RpcIn: successfully created vsocket connection %d.\n",
                  AsyncSocket_GetFd(asock));
            in->conn->asock = asock;
            return TRUE;
         }
      }
      break;
   }

   if (in->conn != NULL) {
      free(in->conn);
      in->conn = NULL;
   }

#endif

   ASSERT(in->channel == NULL);
   in->channel = Message_Open(0x4f4c4354);
   if (in->channel == NULL) {
      Debug("RpcIn: couldn't open channel with TCLO protocol\n");
      goto error;
   }

   if (!RpcInScheduleRecvEvent(in)) {
      Debug("RpcIn_start: couldn't start the loop\n");
      goto error;
   }

   in->mustSend = TRUE;
   return TRUE;

error:
   RpcInStop(in);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcIn_start --
 *
 *    Start the background loop that receives RPC from VMware
 *
 * Result
 *    TRUE on success
 *    FALSE on failure
 *
 * Side-effects
 *    None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(VMTOOLS_USE_GLIB)
Bool
RpcIn_start(RpcIn *in,                                // IN
            unsigned int delay,                       // IN
            RpcIn_ErrorFunc *errorFunc,               // IN
            RpcIn_ClearErrorFunc *clearErrorFunc,     // IN
            void *errorData)                          // IN

#else
Bool
RpcIn_start(RpcIn *in,                                // IN
            unsigned int delay,                       // IN
            RpcIn_Callback resetCallback,             // IN
            void *resetClientData,                    // IN
            RpcIn_ErrorFunc *errorFunc,               // IN
            RpcIn_ClearErrorFunc *clearErrorFunc,     // IN
            void *errorData)                          // IN
#endif
{
   ASSERT(in);

   in->delay = 0;
   in->maxDelay = delay;
   in->errorFunc = errorFunc;
   in->clearErrorFunc = clearErrorFunc;
   in->errorData = errorData;

   /* No initial result */
   ASSERT(in->last_result == NULL);
   ASSERT(in->last_resultLen == 0);
   ASSERT(in->mustSend == FALSE);
   ASSERT(in->nextEvent == NULL);

#if !defined(VMTOOLS_USE_GLIB)
   /* Register the 'reset' handler */
   if (resetCallback) {
      RpcIn_RegisterCallback(in, "reset", resetCallback, resetClientData);
   }

   RpcIn_RegisterCallback(in, "ping", RpcInPingCallback, NULL);
#endif

   return RpcInOpenChannel(in, FALSE);
}


#if !defined(VMTOOLS_USE_GLIB)
/*
 *-----------------------------------------------------------------------------
 *
 * RpcIn_SetRetVals --
 *
 *      Utility method to set the return values of a tclo command.
 *      Example:
 *          return RpcIn_SetRetVals(result, resultLen,
 *                                  "Message", FALSE);
 *
 * Results:
 *      retVal
 *
 * Side effects:
 *	Sets *result to resultVal & resultLen to strlen(*result).
 *
 *-----------------------------------------------------------------------------
 */

unsigned int
RpcIn_SetRetVals(char const **result,   // OUT
                 size_t *resultLen,     // OUT
                 const char *resultVal, // IN
                 Bool retVal)           // IN
{
   ASSERT(result);
   ASSERT(resultLen);
   ASSERT(resultVal);

   *result = resultVal;
   *resultLen = strlen(*result);

   return retVal;
}
#endif
  070701000001CD000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcOut 070701000001CE000081A400000000000000000000000168225505000003DA000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcOut/Makefile.am ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libRpcOut.la

libRpcOut_la_SOURCES =
libRpcOut_la_SOURCES += rpcout.c

  070701000001CF000081A40000000000000000000000016822550500003FDC000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcOut/rpcout.c    /*********************************************************
 * Copyright (C) 2004-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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 <stdio.h>
#   include <string.h>
#   include <stdlib.h>
#   include <stdarg.h>
#   include "str.h"
#   include "debug.h"
#endif

#include "vmware.h"
#include "rpcout.h"
#include "message.h"


/*
 * The RpcOut object
 */

struct RpcOut {
   Message_Channel channel;
   Bool started;
};


/*
 *-----------------------------------------------------------------------------
 *
 * RpcOutInitialize --
 *
 *      Initializes an already allocated RpcOut object.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

static void
RpcOutInitialize(RpcOut *rpcOut)
{
   memset(rpcOut, 0, sizeof *rpcOut);
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcOut_Construct --
 *
 *      Constructor for the RpcOut object
 *
 * Results:
 *      New RpcOut object.
 *
 * Side effects:
 *      Allocates memory.
 *
 *-----------------------------------------------------------------------------
 */

RpcOut *
RpcOut_Construct(void)
{
   RpcOut *rpcOut = malloc(sizeof *rpcOut);
   RpcOutInitialize(rpcOut);
   return rpcOut;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcOut_Destruct --
 *
 *      Destructor for the RpcOut object.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Frees RpcOut object memory.
 *
 *-----------------------------------------------------------------------------
 */

void
RpcOut_Destruct(RpcOut *out) // IN
{
   ASSERT(out != NULL);
   ASSERT(!out->started);

   free(out);
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcOut_startWithReceiveBuffer --
 *
 *      Open the channel.  This variant of RpcOut_start allows the
 *      caller to pre-allocate the receiver buffer, but by doing so it
 *      allows for some simple operations without the need to call
 *      malloc.
 *
 *      Passing in NULL and size 0 will cause this function to fall
 *      back to using malloc.
 *
 * Result:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
RpcOut_startWithReceiveBuffer(RpcOut *out, char *receiveBuffer,
                              size_t receiveBufferSize)
{
   ASSERT(out != NULL);
   ASSERT(!out->started);
   out->started = Message_OpenAllocated(RPCI_PROTOCOL_NUM, &out->channel,
                                        receiveBuffer, receiveBufferSize);
   if (!out->started) {
      Debug("RpcOut: couldn't open channel with RPCI protocol\n");
   }
   return out->started;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcOut_start --
 *
 *      Open the channel
 *
 * Result:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
RpcOut_start(RpcOut *out) // IN
{
   return RpcOut_startWithReceiveBuffer(out, NULL, 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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 if RPC was sent successfully. 'reply' contains the result of the rpc.
 *    rpcStatus tells if the RPC command was processed successfully.
 *
 *    FALSE if RPC could not be sent successfully. '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
            Bool *rpcStatus,     // OUT
            char const **reply,  // OUT
            size_t *repLen)      // OUT
{
   unsigned char *myReply;
   size_t myRepLen;
   Bool success;

   ASSERT(out != NULL);
   ASSERT(out->started);

   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;
   }

   *rpcStatus = success;
   *reply = ((const char *)myReply) + 2;
   *repLen = myRepLen - 2;

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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 = TRUE;

   ASSERT(out != NULL);

   if (out->started) {
      /* Try to close the channel */
      if (Message_CloseAllocated(&out->channel) == FALSE) {
         Debug("RpcOut: couldn't close channel\n");
         status = FALSE;
      }

      out->started = FALSE;
   }

   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;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RpcOutSendOneRawWork --
 *
 *    Helper function to make VMware execute a RPCI command.  See
 *    RpcOut_SendOneRaw and RpcOut_SendOneRawPreallocated.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RpcOutSendOneRawWork(void *request,         // IN: RPCI command
                     size_t reqLen,         // IN: Size of request buffer
                     char *callerReply,     // IN: caller supplied reply buffer
                     size_t callerReplyLen, // IN: size of caller supplied buf
                     char **reply,          // OUT: Result
                     size_t *repLen)        // OUT: Length of the result
{
   Bool status;
   Bool rpcStatus;
   /* Stack allocate so this can be used in kernel logging.  See 1389199. */
   RpcOut out;
   char const *myReply;
   size_t myRepLen;

   Debug("Rpci: Sending request='%s'\n", (char *)request);
   RpcOutInitialize(&out);
   if (!RpcOut_startWithReceiveBuffer(&out, callerReply, callerReplyLen)) {
      myReply = "RpcOut: Unable to open the communication channel";
      myRepLen = strlen(myReply);

      if (callerReply != NULL) {
         unsigned s = MIN(callerReplyLen - 1, myRepLen);
         ASSERT(reply == NULL);
         memcpy(callerReply, myReply, s);
         callerReply[s] = '\0';
      }
      if (reply != NULL) {
         *reply = NULL;
      }
      return FALSE;
   }

   status = RpcOut_send(&out, request, reqLen,
                        &rpcStatus, &myReply, &myRepLen);
   /* On failure, we already have the description of the error */

   Debug("Rpci: Sent request='%s', reply='%s', len=%"FMTSZ"u, "
         "status=%d, rpcStatus=%d\n",
         (char *)request, myReply, myRepLen, status, rpcStatus);

   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 (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;
   }

   return status && rpcStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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
{
   return RpcOutSendOneRawWork(request, reqLen, NULL, 0, reply, repLen);
}



/*
 *-----------------------------------------------------------------------------
 *
 * RpcOut_SendOneRawPreallocated --
 *
 *    Make VMware execute a RPCI command.
 *
 *    A variant of RpcOut_SendOneRaw in which the caller supplies the
 *    receive buffer instead of dynamically allocating it.  This
 *    allows the caller to call this function in situations where
 *    malloc is not allowed.  But if the response from the host is too
 *    large the rpc will fail instead of attempting to grow its own
 *    receive buffer.
 *
 *
 * 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_SendOneRawPreallocated(void *request,       // IN: RPCI command
                              size_t reqLen,       // IN: Size of request buffer
                              char *reply,         // OUT: Result
                              size_t repLen)       // IN: Length of the result
{
   return RpcOutSendOneRawWork(request, reqLen, reply, repLen, NULL, NULL);
}
070701000001D0000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcVmx 070701000001D1000081A400000000000000000000000168225505000003D9000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcVmx/Makefile.am ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libRpcVmx.la

libRpcVmx_la_SOURCES =
libRpcVmx_la_SOURCES += rpcvmx.c
   070701000001D2000081A400000000000000000000000168225505000022AC000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/rpcVmx/rpcvmx.c    /*********************************************************
 * Copyright (c) 2004-2018,2019,2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#if defined(__KERNEL__) || defined(_KERNEL) || defined(KERNEL)
#   include "kernelStubs.h"
#else
#   include <stdio.h>
#   include <stdarg.h>
#   include <string.h>
#   include <stdlib.h>
#   include "str.h"
#endif

#include "guest_msg_def.h"
#include "message.h"
#include "rpcout.h"
#include "rpcvmx.h"


typedef struct {
   char logBuf[RPCVMX_MAX_LOG_LEN + sizeof "log"];
   unsigned int logOffset;
} RpcVMXState;

static RpcVMXState RpcVMX = { "log ", sizeof "log" };


/*
 *----------------------------------------------------------------------------
 *
 * RpcVMX_LogSetPrefix
 *
 *      Allows callers to set a prefix to prepend to the log output. If the
 *      prefix overflows the (static) prefix space available, it is rejected
 *      and the prefix is reset to nothing.  Each call to VMXLog_SetPrefix
 *      replaces the previously set prefix.
 *
 * Results:
 *      TRUE if the prefix was accepted, FALSE otherwise.
 *
 * Side effects:
 *      All subsequent calls to RpcVMX_Log() will have the prefix string
 *      prepended.
 *
 *----------------------------------------------------------------------------
 */

void
RpcVMX_LogSetPrefix(const char *prefix)
{
   size_t prefixLen = strlen(prefix);

   if (prefixLen + sizeof "log" >= sizeof RpcVMX.logBuf - 1) {
      /*
       * Somebody passed a huge prefix. Don't do that!
       */
      RpcVMX.logOffset = sizeof "log";
      return;
   }
   Str_Strcpy(RpcVMX.logBuf + sizeof "log",
              prefix,
              sizeof RpcVMX.logBuf - sizeof "log");

   RpcVMX.logOffset = (unsigned int)(sizeof "log" + prefixLen);
}


/*
 *----------------------------------------------------------------------------
 *
 * RpcVMX_LogGetPrefix --
 *
 *      Returns a read-only pointer to the currently set prefix string. The
 *      client should make a copy if it wants to e.g. save the previous
 *      prefix and then restore it.
 *
 * Results:
 *      Current prefix string, empty string if no prefix is set.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

const char *
RpcVMX_LogGetPrefix(const char *prefix)
{
   UNUSED_VARIABLE(prefix);

   RpcVMX.logBuf[RpcVMX.logOffset] = '\0';
   return RpcVMX.logBuf + sizeof "log";
}


/*
 *----------------------------------------------------------------------------
 *
 * RpcVMX_Log --
 *
 *      Passes through to RpcVMX_LogV but takes arguments inline.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      See RpcVMX_LogV.
 *
 *----------------------------------------------------------------------------
 */

void
RpcVMX_Log(const char *fmt, ...)
{
   va_list args;

   va_start(args, fmt);
   RpcVMX_LogV(fmt, args);
   va_end(args);
}


/*
 *----------------------------------------------------------------------------
 *
 * RpcVMX_LogV --
 *
 *      Construct an output string using the provided format string and
 *      argument list, then send it to the VMX using the RPCI "log" command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Sends the log command described above.
 *
 *----------------------------------------------------------------------------
 */

void
RpcVMX_LogV(const char *fmt, va_list args)
{
   int payloadLen;
   char receiveBuffer[16];

   payloadLen = Str_Vsnprintf(RpcVMX.logBuf + RpcVMX.logOffset,
                              sizeof RpcVMX.logBuf - RpcVMX.logOffset,
                              fmt, args);

   if (payloadLen < 1) {
      /*
       * Overflow. We need more space in the buffer. Just set the length to
       * the buffer size and send the (truncated) log message.
       */
      payloadLen = sizeof RpcVMX.logBuf - RpcVMX.logOffset;
   }

   /*
    * Use a pre-allocated receive buffer so that it's possible to
    * perform the log without needing to call malloc.  This makes
    * RpcVMX_LogV suitable to be used in Windows kernel interrupt
    * handlers.  (It also makes it faster.)  The log command only ever
    * returns two character strings "1 " on success and "0 " on
    * failure, so we don't need a sizeable buffer.
    */
   RpcOut_SendOneRawPreallocated(RpcVMX.logBuf,
                                 (size_t)RpcVMX.logOffset + payloadLen,
                                 receiveBuffer, sizeof receiveBuffer);
}


/*
 *----------------------------------------------------------------------------
 *
 * RpcVMX_ConfigGetString --
 *
 *      Look up a config variable in the VMX's config file and return its
 *      value as a string. The caller should free the string using free() when
 *      done.
 *
 * Results:
 *      The value of the variable if it was set, or a copy of the default
 *      value string if the variable was not set.
 *
 * Side effects:
 *      Allocates memory which the caller must free().
 *
 *----------------------------------------------------------------------------
 */

char *
RpcVMX_ConfigGetString(const char *defval, const char *var)
{
   char *value;
   if (!RpcOut_sendOne(&value, NULL, "info-get guestinfo.%s", var)) {
      /* Get rid of the old value first. */
      free(value);
      value = NULL;

      if (defval) {
         /*
          * We have to dup the default, because of our contract: values we
          * return must always be freed by the caller.
          */
#if defined(_WIN32) && defined(USERLEVEL)
         value = _strdup(defval);
#else
         value = strdup(defval);
#endif
      }
   }

   return value;
}


/*
 *----------------------------------------------------------------------------
 *
 * RpcVMX_ConfigGetBool --
 *
 *      Same as RpcVMX_ConfigGetString, but convert the value to a boolean
 *      and return it.
 *
 * Results:
 *      Value of config variable as a Bool, or the default value if the config
 *      variable was not set *or* could not be converted to a Bool.
 *
 * Side effects:
 *      Perturbs the heap.
 *
 *----------------------------------------------------------------------------
 */

Bool
RpcVMX_ConfigGetBool(Bool defval, const char *var)
{
   char *value = RpcVMX_ConfigGetString(NULL, var);
   Bool ret = defval;

   if (value) {
      if (!Str_Strcasecmp(value, "TRUE")) {
         ret = TRUE;
      } else if (!Str_Strcasecmp(value, "FALSE")) {
         ret = FALSE;
      }

      free(value);
   }

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * RpcVMX_ConfigGetLong --
 *
 *      Same as RpcVMX_ConfigGetString, but convert the value to an integer.
 *      XXX We use atoi, so there's no error checking. If you care, use
 *          RpcVMX_ConfigGetString and do the conversion yourself.
 *
 * Results:
 *      The value of the config variable as a 32-bit integer if it was set,
 *      the default value if it was not set, and 0 if there was an error
 *      converting the value to an integer.
 *
 * Side effects:
 *      Perturbs the heap.
 *
 *----------------------------------------------------------------------------
 */

int32
RpcVMX_ConfigGetLong(int32 defval, const char *var)
{
   char *value = RpcVMX_ConfigGetString(NULL, var);
   int32 ret = defval;

   if (value) {
      ret = atoi(value);
      free(value);
   }

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * RpcVMX_ReportDriverVersion --
 *
 *      Report driver name and driver version to vmx to store the key-value in
 *      GuestVars, and write a log in vmware.log using RpcVMX_Log.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
RpcVMX_ReportDriverVersion(const char *drivername, const char *versionString)
{
   char setVersionCmd[128];
   Str_Sprintf(setVersionCmd, sizeof(setVersionCmd),
               "info-set guestinfo.driver.%s.version %s",
               drivername, versionString);
   RpcOut_sendOne(NULL, NULL, setVersionCmd);
   RpcVMX_Log("Driver=%s, Version=%s", drivername, versionString);
}
070701000001D3000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/lib/slashProc  070701000001D4000081A40000000000000000000000016822550500000455000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/slashProc/Makefile.am  ################################################################################
### Copyright (C) 2007-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libSlashProc.la

libSlashProc_la_SOURCES =
libSlashProc_la_SOURCES += net.c

libSlashProc_la_CPPFLAGS =
libSlashProc_la_CPPFLAGS += @GLIB2_CPPFLAGS@
libSlashProc_la_CPPFLAGS += @DNET_CPPFLAGS@

   070701000001D5000081A400000000000000000000000168225505000066DF000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/slashProc/net.c    /*********************************************************
 * Copyright (c) 2009-2018, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file net.c
 *
 *	Parses assorted /proc/net nodes.
 */


#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>

#include <glib.h>
#include <glib/gstdio.h>

#include "vmware.h"
#include "panic.h"
#include "slashProc.h"
#include "slashProcNetInt.h"


/*
 * Local data.
 */


#define PROC_NET_SNMP   "/proc/net/snmp"
#define PROC_NET_SNMP6  "/proc/net/snmp6"
#define PROC_NET_ROUTE  "/proc/net/route"
#define PROC_NET_ROUTE6 "/proc/net/ipv6_route"


/**
 * @brief Evaluate an expression with a regular expression match.
 *
 * Convenience wrapper to fetch a regular expression match result, evaluate
 * it as an argument in some expression, and then free it.
 *
 * @param[in] matchInfo  GMatchInfo RE context.
 * @param[in] matchIndex Match index.
 * @param[in] expr       Expression to evaluate.  Must contain @c MATCH as
 *                       placeholder where matched value will be inserted.
 */
#define MATCHEXPR(matchInfo, matchIndex, expr) do {                     \
   gchar *MATCH = g_match_info_fetch(matchInfo, matchIndex);            \
   expr;                                                                \
   g_free(MATCH);                                                       \
} while(0)


/**
 * Override-able @c /proc/net/snmp path.  Useful for debugging.
 */
static const char *pathToNetSnmp = PROC_NET_SNMP;


/**
 * Override-able @c /proc/net/snmp6 path.  Useful for debugging.
 */
static const char *pathToNetSnmp6 = PROC_NET_SNMP6;


/**
 * Override-able @c /proc/net/route path.  Useful for debugging.
 */
static const char *pathToNetRoute = PROC_NET_ROUTE;


/**
 * Override-able @c /proc/net/ipv6_route path.  Useful for debugging.
 */
static const char *pathToNetRoute6 = PROC_NET_ROUTE6;


/*
 * Private function prototypes.
 */

static void Ip6StringToIn6Addr(const char *ip6String,
                               struct in6_addr *in6_addr);
static guint64 MatchToGuint64(const GMatchInfo *matchInfo,
                              const gint matchIndex,
                              gint base);


/*
 * Library private functions.
 */


#ifdef VMX86_DEVEL
/*
 ******************************************************************************
 * SlashProcNetSetPathSnmp --                                           */ /**
 *
 * @brief Overrides @ref pathToNetSnmp.  Useful for debugging.
 *
 * @param[in] newPathToNetSnmp  New path to used in place of @ref PROC_NET_SNMP.
 *                              If @c NULL, @ref pathToNewSnmp reverts to @ref
 *                              PROC_NET_SNMP.
 *
 ******************************************************************************
 */

void
SlashProcNetSetPathSnmp(const char *newPathToNetSnmp)
{
   pathToNetSnmp = newPathToNetSnmp ? newPathToNetSnmp : PROC_NET_SNMP;
}


/*
 ******************************************************************************
 * SlashProcNetSetPathSnmp6 --                                          */ /**
 *
 * @brief Overrides @ref pathToNetSnmp6.  Useful for debugging.
 *
 * @sa SlashProcNetSetPathSnmp
 *
 ******************************************************************************
 */

void
SlashProcNetSetPathSnmp6(const char *newPathToNetSnmp6)
{
   pathToNetSnmp6 = newPathToNetSnmp6 ? newPathToNetSnmp6 : PROC_NET_SNMP6;
}


/*
 ******************************************************************************
 * SlashProcNetSetPathRoute --                                          */ /**
 *
 * @brief Overrides @ref pathToNetRoute.  Useful for debugging.
 *
 * @sa SlashProcNetSetPathSnmp
 *
 ******************************************************************************
 */

void
SlashProcNetSetPathRoute(const char *newPathToNetRoute)
{
   pathToNetRoute = newPathToNetRoute ? newPathToNetRoute : PROC_NET_ROUTE;
}


/*
 ******************************************************************************
 * SlashProcNetSetPathRoute6 --                                         */ /**
 *
 * @brief Overrides @ref pathToNetRoute6.  Useful for debugging.
 *
 * @sa SlashProcNetSetPathSnmp
 *
 ******************************************************************************
 */

void
SlashProcNetSetPathRoute6(const char *newPathToNetRoute6)
{
   pathToNetRoute6 = newPathToNetRoute6 ? newPathToNetRoute6 : PROC_NET_ROUTE6;
}
#endif // ifdef VMX86_DEVEL


/*
 * Library public functions.
 */


/*
 ******************************************************************************
 * SlashProcNet_GetSnmp --                                              */ /**
 *
 * @brief Reads @ref pathToNetSnmp and returns contents as a
 *        <tt>GHashTable(gchar *key, guint64 *value)</tt>.
 *
 * Example usage:
 * @code
 * GHashTable *netSnmp = SlashProcNet_GetSnmp();
 * guint64 *inDiscards = g_hash_table_lookup(netSnmp, "IpInDiscards");
 * @endcode
 *
 * @note        Caller should free the returned @c GHashTable with
 *              @c g_hash_table_destroy.
 * @note        This routine creates persistent GLib GRegexs.
 *
 * @return      On failure, NULL.  On success, a valid @c GHashTable.
 * @todo        Provide a case-insensitive key comparison function.
 * @todo        Consider init/cleanup routines to not "leak" GRegexs.
 *
 ******************************************************************************
 */

GHashTable *
SlashProcNet_GetSnmp(void)
{
   GHashTable *myHashTable = NULL;
   GIOChannel *myChannel = NULL;
   GIOStatus keyIoStatus = G_IO_STATUS_ERROR;
   GIOStatus valIoStatus = G_IO_STATUS_ERROR;
   gchar *myKeyLine = NULL;
   gchar *myValLine = NULL;
   Bool parseError = FALSE;
   int fd = -1;

   static GRegex *myKeyRegex = NULL;
   static GRegex *myValRegex = NULL;

   if (myKeyRegex == NULL) {
      myKeyRegex = g_regex_new("^(\\w+): (\\w+ )*(\\w+)$", G_REGEX_OPTIMIZE,
                               0, NULL);
      myValRegex = g_regex_new("^(\\w+): (-?\\d+ )*(-?\\d+)$", G_REGEX_OPTIMIZE,
                               0, NULL);
      ASSERT(myKeyRegex);
      ASSERT(myValRegex);
   }

   if ((fd = g_open(pathToNetSnmp, O_RDONLY)) == -1) {
      return NULL;
   }

   myChannel = g_io_channel_unix_new(fd);

   myHashTable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);

   /*
    * Expected format:
    *
    * pfx0: key0 key1 key2 ... keyN
    * pfx0: val0 val1 val2 ... valN
    * ...
    * pfxN: ...
    */

   while ((keyIoStatus = g_io_channel_read_line(myChannel, &myKeyLine, NULL, NULL,
                                                NULL)) == G_IO_STATUS_NORMAL &&
          (valIoStatus = g_io_channel_read_line(myChannel, &myValLine, NULL, NULL,
                                                NULL)) == G_IO_STATUS_NORMAL) {

      GMatchInfo *keyMatchInfo = NULL;
      GMatchInfo *valMatchInfo = NULL;

      gchar **myKeys = NULL;
      gchar **myVals = NULL;

      gchar **myKey = NULL;
      gchar **myVal = NULL;

      gchar *keyPrefix = NULL;
      gchar *valPrefix = NULL;

      /*
       * Per format above, we expect a pair of lines with a matching prefix.
       */
      {
         if (!g_regex_match(myKeyRegex, myKeyLine, 0, &keyMatchInfo) ||
             !g_regex_match(myValRegex, myValLine, 0, &valMatchInfo)) {
            parseError = TRUE;
            goto badIteration;
         }

         keyPrefix = g_match_info_fetch(keyMatchInfo, 1);
         valPrefix = g_match_info_fetch(valMatchInfo, 1);

         ASSERT(keyPrefix);
         ASSERT(valPrefix);

         if (strcmp(keyPrefix, valPrefix)) {
            parseError = TRUE;
            goto badIteration;
         }
      }

      myKeys = g_strsplit(myKeyLine, " ", 0);
      myVals = g_strsplit(myValLine, " ", 0);

      /*
       * Iterate over the columns, combining the column keys with the prefix
       * to form the new key name.  (I.e., "Ip: InDiscards" => "IpInDiscards".)
       */
      for (myKey = &myKeys[1], myVal = &myVals[1];
           *myKey && *myVal;
           myKey++, myVal++) {
         gchar *hashKey;
         guint64 *myIntVal = NULL;

         hashKey = g_strjoin(NULL, keyPrefix, *myKey, NULL);
         g_strstrip(hashKey);

         /*
          * By virtue of having matched the above regex, this conversion
          * must hold.
          */
         myIntVal = g_new(guint64, 1);
         *myIntVal = g_ascii_strtoull(*myVal, NULL, 10);

         /*
          * If our input contains duplicate keys, which I really don't see
          * happening, the latter value overrides the former.
          *
          * NB: myHashTable claims ownership of hashKey.
          */
         g_hash_table_insert(myHashTable, hashKey, myIntVal);
      }

      /*
       * Make sure the column counts matched.  If we succeeded, both pointers
       * should now be NULL.
       */
      if (*myKey || *myVal) {
         parseError = TRUE;
      }

badIteration:
      g_match_info_free(keyMatchInfo);
      g_match_info_free(valMatchInfo);

      g_free(keyPrefix);
      g_free(valPrefix);

      g_strfreev(myKeys);
      g_strfreev(myVals);

      g_free(myKeyLine);
      g_free(myValLine);
      myKeyLine = NULL;
      myValLine = NULL;

      if (parseError) {
         break;
      }
   }

   /*
    * Error conditions:
    *    Hash table empty:      Unable to parse any input.
    *    myKeyLine != NULL:     Failed to read "key" and "value" lines during
    *                           same loop iteration.
    *    parseError == TRUE:    See loop body above.
    */
   if (keyIoStatus == G_IO_STATUS_ERROR ||
       valIoStatus == G_IO_STATUS_ERROR ||
       g_hash_table_size(myHashTable) == 0 ||
       parseError) {
      g_hash_table_destroy(myHashTable);
      myHashTable = NULL;
   }

   g_free(myKeyLine);
   g_free(myValLine);
   myKeyLine = NULL;
   myValLine = NULL;

   close(fd);
   g_io_channel_unref(myChannel);

   return myHashTable;
}


/*
 ******************************************************************************
 * SlashProcNet_GetSnmp6 --                                             */ /**
 *
 * @brief Reads @ref pathToNetSnmp6 and returns contents as a
 *        <tt>GHashTable(gchar *key, guint64 *value)</tt>.
 *
 * Example usage:
 * @code
 * GHashTable *netSnmp6 = SlashProcNet_GetSnmp6();
 * guint64 *raw6Discards = g_hash_table_lookup(netSnmp6, "Ip6InDiscards");
 * @endcode
 *
 * @note        Caller should free the returned @c GHashTable with
 *              @c g_hash_table_destroy.
 * @note        This routine creates persistent GLib GRegexs.
 *
 * @return      On failure, NULL.  On success, a valid @c GHashTable.
 * @todo        Provide a case-insensitive key comparison function.
 * @todo        Consider init/cleanup routines to not "leak" GRegexs.
 *
 ******************************************************************************
 */

GHashTable *
SlashProcNet_GetSnmp6(void)
{
   GHashTable *myHashTable = NULL;
   GIOChannel *myChannel = NULL;
   GIOStatus ioStatus;
   gchar *myInputLine = NULL;
   Bool parseError = FALSE;
   int fd = -1;

   static GRegex *myRegex = NULL;

   if (myRegex == NULL) {
      myRegex = g_regex_new("^(\\w+)\\s+(-?\\d+)\\s*$", G_REGEX_OPTIMIZE,
                            0, NULL);
      ASSERT(myRegex);
   }

   if ((fd = g_open(pathToNetSnmp6, O_RDONLY)) == -1) {
      return NULL;
   }

   myChannel = g_io_channel_unix_new(fd);

   myHashTable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);

   /*
    * Expected format:
    *
    * key1                              value1
    * key2                              value2
    * ...
    * keyN                              valueN
    */

   while ((ioStatus = g_io_channel_read_line(myChannel, &myInputLine, NULL,
                                             NULL, NULL)) == G_IO_STATUS_NORMAL) {
      GMatchInfo *matchInfo = NULL;

      if (g_regex_match(myRegex, myInputLine, 0, &matchInfo)) {
         gchar *myKey = NULL;
         gchar *myVal = NULL;
         guint64 *myIntVal = NULL;

         myKey = g_match_info_fetch(matchInfo, 1);
         myVal = g_match_info_fetch(matchInfo, 2);

         /*
          * By virtue of having matched the above regex, this conversion
          * must hold.
          */
         myIntVal = g_new(guint64, 1);
         *myIntVal = g_ascii_strtoull(myVal, NULL, 10);

         /*
          * The hash table will take ownership of myKey and myIntVal.  We're
          * still responsible for myVal.
          */
         g_hash_table_insert(myHashTable, myKey, myIntVal);
         g_free(myVal);
      } else {
         parseError = TRUE;
      }

      g_match_info_free(matchInfo);
      g_free(myInputLine);
      myInputLine = NULL;

      if (parseError) {
         break;
      }
   }

   if (ioStatus == G_IO_STATUS_ERROR ||
       g_hash_table_size(myHashTable) == 0 ||
       parseError) {
      g_hash_table_destroy(myHashTable);
      myHashTable = NULL;
   }

   close(fd);
   g_io_channel_unref(myChannel);

   return myHashTable;
}


/*
 ******************************************************************************
 * SlashProcNet_GetRoute --                                             */ /**
 *
 * @brief Reads the first @c maxRoutes lines of @ref pathToNetRoute and
 *        returns a @c GPtrArray of <tt>struct rtentry</tt>s.
 *
 * Example usage:
 * @code
 * GPtrArray *rtArray;
 * guint i;
 * rtArray = SlashProcNet_GetRoute(NICINFO_MAX_ROUTES, RTF_UP);
 * for (i = 0; i < rtArray->len; i++) {
 *    struct rtentry *myRoute = g_ptr_array_index(rtArray, i);
 *    // Do something with myRoute->rt_dst.
 * }
 * SlashProcNet_FreeRoute(rtArray);
 * @endcode
 *
 * @note        Caller is responsible for freeing the @c GPtrArray with
 *              SlashProcNet_FreeRoute.
 * @note        This routine creates persistent GLib GRegexs.
 *
 * @param[in]   maxRoutes       Max routes to gather.
 * @param[in]   rtFilterFlags   Route flags used to filter out what we want.
 *                              Set ~0 if want everything.
 *
 * @return      On failure, NULL.  On success, a valid @c GPtrArray.
 * @todo        Consider init/cleanup routines to not "leak" GRegexs.
 * @todo        Consider rewriting, integrating with libdnet.
 *
 ******************************************************************************
 */

GPtrArray *
SlashProcNet_GetRoute(unsigned int maxRoutes,
                      unsigned short rtFilterFlags)
{
   GIOChannel *myChannel = NULL;
   GIOStatus myIoStatus;
   GPtrArray *myArray = NULL;
   gchar *myLine = NULL;
   int fd = -1;
   unsigned int lineCount = 0;

   static GRegex *myFieldsRE = NULL;
   static GRegex *myValuesRE = NULL;

   ASSERT(maxRoutes > 0);

   if (myFieldsRE == NULL) {
      myFieldsRE = g_regex_new("^Iface\\s+Destination\\s+Gateway\\s+Flags\\s+"
                               "RefCnt\\s+Use\\s+Metric\\s+Mask\\s+MTU\\s+"
                               "Window\\s+IRTT\\s*$", 0, 0, NULL);
      myValuesRE = g_regex_new("^(\\S+)\\s+([[:xdigit:]]{8})\\s+"
                               "([[:xdigit:]]{8})\\s+([[:xdigit:]]{4})\\s+"
                               "\\d+\\s+\\d+\\s+(\\d+)\\s+"
                               "([[:xdigit:]]{8})\\s+(\\d+)\\s+\\d+\\s+(\\d+)\\s*$",
                               0, 0, NULL);
      ASSERT(myFieldsRE);
      ASSERT(myValuesRE);
   }

   /*
    * 1.  Open pathToNetRoute, associate it with a GIOChannel.
    */

   if ((fd = g_open(pathToNetRoute, O_RDONLY)) == -1) {
      Warning("%s: open(%s): %s\n", __func__, pathToNetRoute,
              g_strerror(errno));
      return NULL;
   }

   myChannel = g_io_channel_unix_new(fd);

   /*
    * 2.  Confidence check the header, making sure it matches what we expect.
    *     (It's -extremely- unlikely this will change, but we should check
    *     anyway.)
    */

   myIoStatus = g_io_channel_read_line(myChannel, &myLine, NULL, NULL, NULL);
   if (myIoStatus != G_IO_STATUS_NORMAL ||
       g_regex_match(myFieldsRE, myLine, 0, NULL) == FALSE) {
      goto out;
   }

   g_free(myLine);
   myLine = NULL;

   myArray = g_ptr_array_new();

   /*
    * 3.  For each line...
    */

   while (lineCount < maxRoutes &&
             (myIoStatus = g_io_channel_read_line(myChannel,
                  &myLine, NULL, NULL, NULL)) == G_IO_STATUS_NORMAL) {
      GMatchInfo *myMatchInfo = NULL;
      struct rtentry *myEntry = NULL;
      struct sockaddr_in *sin = NULL;
      Bool parseError = FALSE;

      /*
       * 3a. Validate with regex.
       */
      if (!g_regex_match(myValuesRE, myLine, 0, &myMatchInfo)) {
         parseError = TRUE;
         goto iterationCleanup;
      }

      /*
       * 3b. Allocate new rtentry, add to array.  This simplifies the cleanup
       *     code path.
       */
      myEntry = g_new0(struct rtentry, 1);

      /*
       * 3c. Copy contents to new struct rtentry.
       */
      myEntry->rt_dev = g_match_info_fetch(myMatchInfo, 1);

      sin = (struct sockaddr_in *)&myEntry->rt_dst;
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = MatchToGuint64(myMatchInfo, 2, 16);

      sin = (struct sockaddr_in *)&myEntry->rt_gateway;
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = MatchToGuint64(myMatchInfo, 3, 16);

      sin = (struct sockaddr_in *)&myEntry->rt_genmask;
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = MatchToGuint64(myMatchInfo, 6, 16);

      myEntry->rt_flags = MatchToGuint64(myMatchInfo, 4, 16);
      myEntry->rt_metric = MatchToGuint64(myMatchInfo, 5, 10);
      myEntry->rt_mtu = MatchToGuint64(myMatchInfo, 7, 10);
      myEntry->rt_irtt = MatchToGuint64(myMatchInfo, 8, 10);

      if (rtFilterFlags == (unsigned short)~0 ||
          (myEntry->rt_flags & rtFilterFlags) != 0) {
         g_ptr_array_add(myArray, myEntry);
         lineCount++;
      } else {
         g_free(myEntry->rt_dev);
         g_free(myEntry);
      }

iterationCleanup:
      g_free(myLine);
      myLine = NULL;

      g_match_info_free(myMatchInfo);
      myMatchInfo = NULL;

      if (parseError) {
         /* Return NULL to signal parsing error */
         if (myArray) {
            SlashProcNet_FreeRoute(myArray);
            myArray = NULL;
         }
         break;
      }
   }

out:
   g_free(myLine);
   close(fd);
   g_io_channel_unref(myChannel);

   return myArray;
}


/*
 ******************************************************************************
 * SlashProcNet_FreeRoute --                                            */ /**
 *
 * @brief Frees memory associated with a GPtrArray allocated by
 *        SlashProcNet_GetRoute.
 *
 * @param[in]   routeArray      Array to free.
 *
 ******************************************************************************
 */

void
SlashProcNet_FreeRoute(GPtrArray *routeArray)
{
   int i;

   if (routeArray == NULL) {
      return;
   }

   for (i = 0; i < routeArray->len; i++) {
      struct rtentry *myEntry = g_ptr_array_index(routeArray, i);
      ASSERT(myEntry->rt_dev);
      g_free(myEntry->rt_dev);
      g_free(myEntry);
   }

   g_ptr_array_free(routeArray, TRUE);
}


/*
 ******************************************************************************
 * SlashProcNet_GetRoute6 --                                            */ /**
 *
 * @brief Reads the first @c maxRoutes lines of @ref pathToNetRoute6 and
 *        returns a @c GPtrArray of <tt>struct in6_rtmsg</tt>s.
 *
 * Example usage:
 * @code
 * GPtrArray *rtArray;
 * guint i;
 * rtArray = SlashProcNet_GetRoute6(NICINFO_MAX_ROUTES, RTF_UP);
 * for (i = 0; i < rtArray->len; i++) {
 *    struct in6_rtmsg *myRoute = g_ptr_array_index(rtArray, i);
 *    // Do something with myRoute->rtmsg_dst.
 * }
 * SlashProcNet_FreeRoute6(rtArray);
 * @endcode
 *
 * @note        Caller is responsible for freeing the @c GPtrArray with
 *              SlashProcNet_FreeRoute6.
 * @note        This routine creates persistent GLib GRegexs.
 *
 * @param[in]   maxRoutes       Max routes to gather.
 * @param[in]   rtFilterFlags   Route flags used to filter out what we want.
 *                              Set ~0 if want everything.
 *
 * @return      On failure, NULL.  On success, a valid @c GPtrArray.
 * @todo        Consider init/cleanup routines to not "leak" GRegexs.
 * @todo        Consider rewriting, integrating with libdnet.
 *
 ******************************************************************************
 */

GPtrArray *
SlashProcNet_GetRoute6(unsigned int maxRoutes,
                       unsigned int rtFilterFlags)
{
   GIOChannel *myChannel = NULL;
   GIOStatus myIoStatus;
   GPtrArray *myArray = NULL;
   gchar *myLine = NULL;
   Bool parseError = FALSE;
   int fd = -1;
   unsigned int lineCount = 0;

   static GRegex *myValuesRE = NULL;

   ASSERT(maxRoutes > 0);

   if (myValuesRE == NULL) {
      myValuesRE = g_regex_new("^([[:xdigit:]]{32}) ([[:xdigit:]]{2}) "
                                "([[:xdigit:]]{32}) ([[:xdigit:]]{2}) "
                                "([[:xdigit:]]{32}) ([[:xdigit:]]{8}) "
                                "[[:xdigit:]]{8} [[:xdigit:]]{8} "
                                "([[:xdigit:]]{8})\\s+(\\S+)\\s*$", 0, 0,
                                NULL);
      ASSERT(myValuesRE);
   }

   /*
    * 1.  Open pathToNetRoute6, associate it with a GIOChannel.
    */

   if ((fd = g_open(pathToNetRoute6, O_RDONLY)) == -1) {
      Warning("%s: open(%s): %s\n", __func__, pathToNetRoute6,
              g_strerror(errno));
      return NULL;
   }

   myChannel = g_io_channel_unix_new(fd);

   myArray = g_ptr_array_new();

   while (lineCount < maxRoutes &&
               (myIoStatus = g_io_channel_read_line(myChannel,
                     &myLine, NULL, NULL, NULL)) == G_IO_STATUS_NORMAL) {
      struct in6_rtmsg *myEntry = NULL;
      GMatchInfo *myMatchInfo = NULL;

      if (!g_regex_match(myValuesRE, myLine, 0, &myMatchInfo)) {
         parseError = TRUE;
         goto iterationCleanup;
      }

      myEntry = g_new0(struct in6_rtmsg, 1);

      MATCHEXPR(myMatchInfo, 1, Ip6StringToIn6Addr(MATCH, &myEntry->rtmsg_dst));
      MATCHEXPR(myMatchInfo, 3, Ip6StringToIn6Addr(MATCH, &myEntry->rtmsg_src));
      MATCHEXPR(myMatchInfo, 5, Ip6StringToIn6Addr(MATCH, &myEntry->rtmsg_gateway));

      myEntry->rtmsg_dst_len = MatchToGuint64(myMatchInfo, 2, 16);
      myEntry->rtmsg_src_len = MatchToGuint64(myMatchInfo, 4, 16);
      myEntry->rtmsg_metric = MatchToGuint64(myMatchInfo, 6, 16);
      myEntry->rtmsg_flags = MatchToGuint64(myMatchInfo, 7, 16);

      MATCHEXPR(myMatchInfo, 8, myEntry->rtmsg_ifindex = if_nametoindex(MATCH));

      if (rtFilterFlags == (uint)~0 ||
          (myEntry->rtmsg_flags & rtFilterFlags) != 0) {
         g_ptr_array_add(myArray, myEntry);
         lineCount++;
      } else {
         g_free(myEntry);
      }

iterationCleanup:
      g_free(myLine);
      myLine = NULL;

      g_match_info_free(myMatchInfo);
      myMatchInfo = NULL;

      if (parseError) {
         /* Return NULL to signal parsing error */
         if (myArray) {
            SlashProcNet_FreeRoute6(myArray);
            myArray = NULL;
         }
         break;
      }
   }

   g_free(myLine);
   myLine = NULL;

   close(fd);
   g_io_channel_unref(myChannel);

   return myArray;
}


/*
 ******************************************************************************
 * SlashProcNet_FreeRoute6 --                                           */ /**
 *
 * @brief Frees memory associated with a GPtrArray allocated by
 *        SlashProcNet_GetRoute6.
 *
 * @param[in]   routeArray      Array to free.
 *
 ******************************************************************************
 */

void
SlashProcNet_FreeRoute6(GPtrArray *routeArray)
{
   int i;

   if (routeArray == NULL) {
      return;
   }

   for (i = 0; i < routeArray->len; i++) {
      struct rtentry *myEntry = g_ptr_array_index(routeArray, i);
      g_free(myEntry);
   }

   g_ptr_array_free(routeArray, TRUE);
}


/*
 * Private functions.
 */


/*
 ******************************************************************************
 * Ip6StringToIn6Addr --                                                */ /**
 *
 * @brief Parses a @c /proc/net/ipv6_route hexadecimal IPv6 address and
 *        records it in a <tt>struct in6_addr</tt>.
 *
 * @param[in]   ip6String       Source string.
 * @param[out]  in6_addr        Output struct.
 *
 ******************************************************************************
 */

static void
Ip6StringToIn6Addr(const char *ip6String,
                   struct in6_addr *in6_addr)
{
   unsigned int i;

   ASSERT(strlen(ip6String) == 32);

   for (i = 0; i < 16; i++) {
      int nmatched;
      nmatched = sscanf(&ip6String[2 * i], "%2hhx", &in6_addr->s6_addr[i]);
      ASSERT(nmatched == 1);
   }
}


/*
 ******************************************************************************
 * MatchToGuint64 --                                                    */ /**
 *
 * @brief Wrapper around @c g_ascii_strtoull and @c g_match_info_fetch.
 *
 * @param[in]   matchInfo       Source regular expression match context.
 * @param[in]   matchIndex      Match number to fetch.
 * @param[in]   base            Base represented by matched string.
 *                              (See @c g_ascii_strtoull docs.)
 *
 ******************************************************************************
 */

static guint64
MatchToGuint64(const GMatchInfo *matchInfo,
               const gint matchIndex,
               gint base)
{
   guint64 retval;
   MATCHEXPR(matchInfo, matchIndex, retval = g_ascii_strtoull(MATCH, NULL, base));
   return retval;
}
 070701000001D6000081A4000000000000000000000001682255050000058B000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/slashProc/slashProcNetInt.h    /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file netInt.h
 *
 *	SlashProcNet internal bits.
 */

#ifndef _SLASHPROCNETINT_H_
#define _SLASHPROCNETINT_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#ifdef VMX86_DEVEL
EXTERN void SlashProcNetSetPathSnmp(const char *newPathToNetSnmp);
EXTERN void SlashProcNetSetPathSnmp6(const char *newPathToNetSnmp6);
EXTERN void SlashProcNetSetPathRoute(const char *newPathToNetRoute);
EXTERN void SlashProcNetSetPathRoute6(const char *newPathToNetRoute6);
#endif // ifdef VMX86_DEVEL

#endif // ifndef _SLASHPROCNETINT_H_

 070701000001D7000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/lib/sslDirect  070701000001D8000081A40000000000000000000000016822550500000400000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/sslDirect/Makefile.am  ################################################################################
### Copyright (C) 2013-2019 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libSslDirect.la

libSslDirect_la_CPPFLAGS =

libSslDirect_la_SOURCES =
libSslDirect_la_SOURCES += sslStubs.c
070701000001D9000081A400000000000000000000000168225505000024DC000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/sslDirect/sslStubs.c   /*********************************************************
 * Copyright (C) 2013-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * sslStubs.c --
 *
 *      Stubs for AsyncSokcet SSL functions without actually using SSL.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>

#include "str.h"

#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#endif

#include "log.h"
#include "err.h"
#include "msg.h"
#include "sslDirect.h"
#include "vm_assert.h"

#define LOGLEVEL_MODULE SSLStubs
#include "loglevel_user.h"


struct SSLSockStruct {
   int fd;
   Bool closeFdOnShutdown;
#ifdef __APPLE__
   Bool loggedKernelReadBug;
#endif
};


/*
 *----------------------------------------------------------------------
 *
 * SSL_Write()
 *
 *    Functional equivalent of the write() syscall.
 *
 * Results:
 *    Returns the number of bytes written, or -1 on error.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

ssize_t
SSL_Write(SSLSock sslSock,   // IN
          const char *buf,   // IN
          size_t num)        // IN
{
   return send(sslSock->fd, buf, num, 0);
}


/*
 *----------------------------------------------------------------------
 *
 * SSL_Read --
 *
 *    Functional equivalent of the read() syscall.
 *
 * Results:
 *    Returns the number of bytes read, or -1 on error.  The
 *    data read will be placed in buf.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------
 */

ssize_t
SSL_Read(SSLSock sslSock,   // IN
         char *buf,         // OUT
         size_t num)        // IN
{
   int ret;
   ASSERT(sslSock);

#ifdef _WIN32
   ret = recv(sslSock->fd, buf, num, 0);
#else
   ret = read(sslSock->fd, buf, num);
#endif

#ifdef __APPLE__
   /*
    * Detect bug 161237 (Apple bug 5202831), which should no longer be
    * happening due to a workaround in our code.
    *
    * There is a bug on Mac OS 10.4 and 10.5 where passing an fd
    * over a socket can result in that fd being in an inconsistent state.
    * We can detect when this happens when read(2) returns zero
    * even if the other end of the socket is not disconnected.
    * We verify this by calling write(sslSock->fd, "", 0) and
    * see if it is okay. (If the socket was really closed, it would
    * return -1 with errno==EPIPE.)
    */
   if (ret == 0) {
      ssize_t writeRet;
#ifdef VMX86_DEBUG
      struct stat statBuffer;

      /*
       * Make sure we're using a socket.
       */
      ASSERT((fstat(sslSock->fd, &statBuffer) == 0) &&
             ((statBuffer.st_mode & S_IFSOCK) == S_IFSOCK));

#endif
      writeRet = write(sslSock->fd, "", 0);
      if (writeRet == 0) {
         /*
          * The socket is still good. read(2) should not have returned zero.
          */
         if (! sslSock->loggedKernelReadBug) {
            Log("Error: Encountered Apple bug #5202831.  Disconnecting.\n");
            sslSock->loggedKernelReadBug = TRUE;
         }
      }
   }
#endif

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * SSL_GetFd()
 *
 *    Returns an socket's file descriptor or handle.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

int
SSL_GetFd(SSLSock sslSock) // IN
{
   ASSERT(sslSock);

   return sslSock->fd;
}


/*
 *----------------------------------------------------------------------
 *
 * SSL_Pending()
 *
 *	  Always returns 0 for non-SSL socket.
 *
 * Results:
 *	  0
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

int
SSL_Pending(SSLSock sslSock) // IN
{
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * SSL_New()
 *
 * Results:
 *    Returns a freshly allocated SSLSock structure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

SSLSock
SSL_New(int fd,                       // IN
        Bool closeFdOnShutdown)       // IN
{
   SSLSock sslSock;

   sslSock = calloc(1, sizeof *sslSock);
   ASSERT_MEM_ALLOC(sslSock);
   sslSock->fd = fd;
   sslSock->closeFdOnShutdown = closeFdOnShutdown;

   return sslSock;
}


/*
 *----------------------------------------------------------------------
 *
 * SSL_Shutdown()
 *
 *    Functional equivalent of the close() syscall.  Does
 *    not close the actual fd used for the connection.
 *
 *
 * Results:
 *    0 on success, -1 on failure.
 *
 * Side effects:
 *    closes the connection, freeing up the memory associated
 *    with the passed in socket object
 *
 *----------------------------------------------------------------------
 */

int
SSL_Shutdown(SSLSock sslSock)     // IN
{
   int ret = 0;
   ASSERT(sslSock);

   LOG(10, ("Starting shutdown for %d\n", sslSock->fd));
   if (sslSock->closeFdOnShutdown) {
      LOG(10, ("Trying to close %d\n", sslSock->fd));

#ifdef _WIN32
      ret = closesocket(sslSock->fd);
#else
      ret = close(sslSock->fd);
#endif

   }

   free(sslSock);
   LOG(10, ("shutdown done\n"));

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * SSL_SetCloseOnShutdownFlag()
 *
 *    Sets closeFdOnShutdown flag.
 *
 * Results:
 *    None.  Always succeeds.  Do not call close/closesocket on
 *    the fd after this, call SSL_Shutdown() instead.
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

void
SSL_SetCloseOnShutdownFlag(SSLSock sslSock)    // IN
{
   ASSERT(sslSock);
   sslSock->closeFdOnShutdown = TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * SSL_RecvDataAndFd --
 *
 *    recvmsg wrapper which can receive only file descriptors, not other
 *    control data.
 *
 * Results:
 *    Returns the number of bytes received, or -1 on error.  The
 *    data read will be placed in buf.  *fd is either -1 if no fd was
 *    received, or descriptor...
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------
 */

ssize_t
SSL_RecvDataAndFd(SSLSock sslSock,   // IN/OUT
                  char *buf,         // OUT
                  size_t num,        // IN
                  int *fd)           // OUT
{
   int ret;
   ASSERT(sslSock);
   ASSERT(fd);

   *fd = -1;

   /*
    * No fd passing over socket or Windows. Windows needs different code.
    */
#ifdef _WIN32
   return SSL_Read(sslSock, buf, num);
#else
   {
      struct iovec iov;
      struct msghdr msg = { 0 };
      uint8 cmsgBuf[CMSG_SPACE(sizeof(int))];

      iov.iov_base = buf;
      iov.iov_len = num;
      msg.msg_name = NULL;
      msg.msg_namelen = 0;
      msg.msg_iov = &iov;
      msg.msg_iovlen = 1;
      msg.msg_control = cmsgBuf;
      msg.msg_controllen = sizeof cmsgBuf;
      ret = recvmsg(sslSock->fd, &msg, 0);
      if (ret >= 0 && msg.msg_controllen != 0) {
         struct cmsghdr *cmsg;

         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
               int receivedFd = *(int *)CMSG_DATA(cmsg);

               ASSERT(*fd == -1);
               *fd = receivedFd;
            }
         }
      }
   }
#endif

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SSL_TryCompleteAccept --
 *
 *    Stub only, should not be called when SSL is not used.
 * Results:
 *    0
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

int
SSL_TryCompleteAccept(SSLSock ssl) // IN
{
   ASSERT(0);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SSL_WantRead --
 *
 *    Stub only, should not be called when SSL is not used.
 * Results:
 *    0
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

int
SSL_WantRead(const SSLSock ssl)
{
   ASSERT(0);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SSL_SetupAcceptWithContext --
 *
 *    Stub only, should not be called when SSL is not used.
 * Results:
 *    FALSE
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
SSL_SetupAcceptWithContext(SSLSock sSock, // IN: SSL socket
                           void *ctx)     // IN: OpenSSL context (SSL_CTX *)
{
   ASSERT(0);
   return FALSE;
}
070701000001DA000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/lib/string 070701000001DB000081A400000000000000000000000168225505000003D7000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/string/Makefile.am ################################################################################
### Copyright (C) 2007-2017 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libString.la

libString_la_SOURCES =

libString_la_SOURCES += str.c
 070701000001DC000081A40000000000000000000000016822550500009024000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/lib/string/str.c   /*********************************************************
 * Copyright (C) 1998-2018, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * str.c --
 *
 *    User level string wrappers
 *
 * WARNING:
 *    Do not call any variadic functions - those that use "..." repeatedly
 *    with the same va_list or memory corruption and/or crashes will occur.
 *    The suggested way deal with repeated calls is to use a va_copy:
 *
 *    va_list tmpArgs;
 *
 *    va_copy(tmpArgs, ap);
 *    // Call the variadic function
 *    va_end(tmpArgs);
 *
 */

#ifdef _WIN32
#include <windows.h>
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "vmware.h"
#include "str.h"
#ifdef HAS_BSD_PRINTF
#include "bsd_output.h"
#endif
#include "codeset.h"

#if defined _WIN32 && !defined HAS_BSD_PRINTF
#define vsnprintf _vsnprintf
#endif

#ifndef _WIN32
extern int vasprintf(char **ptr, const char *f, va_list arg);
/*
 * Declare vswprintf on platforms where it's not known to exist.  We know
 * it's available on glibc >= 2.2, FreeBSD >= 5.0, and all versions of
 * Solaris.
 * (Re: Solaris, vswprintf has been present since Solaris 8, and we only
 * support Solaris 9 and above, since that was the first release available
 * for x86, so we just assume it's already there.)
 *
 * XXX Str_Vsnwprintf and friends are still protected by _WIN32 and
 * glibc >= 2.2.  I.e., even though they should be able to work on
 * FreeBSD 5.0+ and Solaris 8+, they aren't made available there.
 */
#   if !(defined(__linux__) || defined(__FreeBSD__) || defined(sun))
extern int vswprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, va_list args);
#   endif
#endif // _WIN32


/*
 *----------------------------------------------------------------------
 *
 * Str_Vsnprintf --
 *
 *      Compatibility wrapper b/w different libc versions
 *
 * Results:
 *      int - number of bytes stored in 'str' (not including NUL terminator),
 *      -1 on overflow (insufficient space for NUL terminator is considered
 *      overflow).
 *
 *      Guaranteed to NUL-terminate if 'size' > 0.
 *
 *      NB: on overflow the buffer WILL be NUL terminated at the last
 *      UTF-8 code point boundary within the buffer's bounds.
 *
 * WARNING:
 *      Behavior of this function is guaranteed only if HAS_BSD_PRINTF is
 *      enabled.
 *
 *      See the warning at the top of this file for proper va_list usage.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

int
Str_Vsnprintf(char *str,          // OUT
              size_t size,        // IN
              const char *format, // IN
              va_list ap)         // IN
{
   int retval;

   ASSERT(str != NULL);
   ASSERT(format != NULL);

#if defined HAS_BSD_PRINTF
   retval = bsd_vsnprintf(&str, size, format, ap);
#else
   /*
    * Linux glibc 2.0.x (which we shouldn't be linking against) returns -1, but
    * glibc 2.1.x follows c99 and returns the number characters (excluding NUL)
    * that would have been written if given a sufficiently large buffer.
    *
    * In the case of Win32, this path uses _vsnprintf(), which returns -1 on
    * overflow, returns size when result fits exactly, and does not NUL
    * terminate in those cases.
    */
   retval = vsnprintf(str, size, format, ap);
#endif

   if ((retval < 0) || (retval >= size)) {
      if (size > 0) {
         /* Find UTF-8 code point boundary and place NUL termination there */
         int trunc = CodeSet_Utf8FindCodePointBoundary(str, size - 1);

         str[trunc] = '\0';
      }
   }
   if (retval >= size) {
      return -1;
   }
   return retval;
}


#ifdef HAS_BSD_PRINTF
/*
 *----------------------------------------------------------------------
 *
 * Str_Sprintf_C_Locale --
 *
 *      sprintf wrapper that fails on overflow. Enforces numeric C locale.
 *
 * Results:
 *      Returns the number of bytes stored in 'buf'.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Str_Sprintf_C_Locale(char *buf,        // OUT:
                     size_t maxSize,   // IN:
                     const char *fmt,  // IN:
                     ...)              // IN:
{
   va_list args;
   int retval;

   ASSERT(buf);
   ASSERT(fmt);

   va_start(args, fmt);
   retval = bsd_vsnprintf_c_locale(&buf, maxSize, fmt, args);
   va_end(args);

   if ((retval < 0) || (retval >= maxSize)) {
      if (maxSize > 0) {
         /* Find UTF-8 code point boundary and place NUL termination there */
         int trunc = CodeSet_Utf8FindCodePointBoundary(buf, maxSize - 1);

         buf[trunc] = '\0';
      }
   }

   if (retval >= maxSize) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }

   return retval;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * Str_Sprintf --
 *
 *      sprintf wrapper that fails on overflow
 *
 * Results:
 *      Returns the number of bytes stored in 'buf'.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Str_Sprintf(char *buf,       // OUT
            size_t maxSize,  // IN
            const char *fmt, // IN
            ...)             // IN
{
   va_list args;
   int i;

   va_start(args, fmt);
   i = Str_Vsnprintf(buf, maxSize, fmt, args);
   va_end(args);
   if (i < 0) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }
   return i;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Snprintf --
 *
 *      Compatibility wrapper b/w different libc versions
 *
 * Results:
 *      See Str_Vsnprintf.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

int
Str_Snprintf(char *str,          // OUT
             size_t size,        // IN
             const char *format, // IN
             ...)                // IN
{
   int retval;
   va_list args;

   ASSERT(str != NULL);
   ASSERT(format != NULL);

   va_start(args, format);
   retval = Str_Vsnprintf(str, size, format, args);
   va_end(args);

   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * 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
{
   size_t len;

   ASSERT(buf != NULL);
   ASSERT(src != NULL);

   len = strlen(src);
   if (len >= maxSize) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }
   return memcpy(buf, src, len + 1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_Strncpy --
 *
 *      Unlike strncpy:
 *      * Guaranteed to NUL-terminate.
 *      * If the src string is shorter than n bytes, does NOT zero-fill the
 *        remaining bytes.
 *      * Panics if a buffer overrun would have occurred.
 *
 * Results:
 *      Same as strncpy.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Str_Strncpy(char *dest,       // IN/OUT
            size_t destSize,  // IN: Size of dest
            const char *src,  // IN: String to copy
            size_t n)         // IN: Max chars of src to copy, not including NUL
{
   ASSERT(dest != NULL);
   ASSERT(src != NULL);

   n = Str_Strlen(src, n);

   if (n >= destSize) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }

   memcpy(dest, src, n);
   dest[n] = '\0';
   return dest;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Strlen --
 *
 *      Calculate length of the string.
 *
 * Results:
 *      Length of s not including the terminating '\0' character.
 *      If there is no '\0' for first maxLen bytes, then it
 *      returns maxLen.
 *
 * Side Effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

size_t
Str_Strlen(const char *s,  // IN:
           size_t maxLen)  // IN:

{
   const char *end;

   ASSERT(s != NULL);

   if ((end = memchr(s, '\0', maxLen)) == NULL) {
      return maxLen;
   }
   return end - s;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Strnstr --
 *
 *      Find a substring within a string of length at most n. 'sub' must be
 *      NUL-terminated. 'n' is interpreted as an unsigned int.
 *
 * Results:
 *      A pointer to the beginning of the substring, or NULL if not found.
 *
 * Side Effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

char *
Str_Strnstr(const char *src,  // IN:
            const char *sub,  // IN:
            size_t n)         // IN:
{
   size_t subLen;
   const char *end;

   ASSERT(src != NULL);
   ASSERT(sub != NULL);

   if ((subLen = strlen(sub)) == 0) {
      return (char *) src;
   }
   if ((end = memchr(src, '\0', n)) == NULL) {
      end = src + n;
   }
   end -= subLen - 1;
   if (end <= src) {
      return NULL;
   }
   for (;
       (src = memchr(src, sub[0], end - src)) != NULL &&
        memcmp(src, sub, subLen) != 0;
        src++) {
   }
   return (char *) src;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Strcat --
 *
 *    Wrapper for strcat that checks for buffer overruns.
 *
 * Results:
 *    Same as strcat.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

char *
Str_Strcat(char *buf,       // IN/OUT
           const char *src, // IN
           size_t maxSize)  // IN
{
   size_t bufLen;
   size_t srcLen;

   ASSERT(buf != NULL);
   ASSERT(src != NULL);

   bufLen = strlen(buf);
   srcLen = strlen(src);

   /* The first comparison checks for numeric overflow */
   if (bufLen + srcLen < srcLen || bufLen + srcLen >= maxSize) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }

   memcpy(buf + bufLen, src, srcLen + 1);

   return buf;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Strncat --
 *
 *    Wrapper for strncat that checks for buffer overruns.
 *
 *    Specifically, this function will Panic if a buffer overrun would
 *    have occurred.
 *
 *    Guaranteed to NUL-terminate.
 *
 * Results:
 *    Same as strncat.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

char *
Str_Strncat(char *buf,       // IN/OUT
            size_t bufSize,  // IN: Size of buf
            const char *src, // IN: String to append
            size_t n)        // IN: Max chars of src to append
{
   size_t bufLen;

   ASSERT(buf != NULL);
   ASSERT(src != NULL);

   /*
    * If bufLen + n is OK, we know things will fit (and avoids a strlen(src)).
    * If bufLen + n looks too big, they still might fit if the src is short
    * enough... so repeat the check using strlen(src).
    *
    * The "fit" means less than, as strncat always adds a terminating NUL.
    */

   bufLen = strlen(buf);
   bufLen = MIN(bufLen, bufSize);  // Prevent potential overflow

   if (!(bufLen + n < bufSize ||
         bufLen + strlen(src) < bufSize)) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }

   /*
    * We don't need to worry about NUL termination, because it's only
    * needed on overflow and we Panic above in that case.
    */

   return strncat(buf, src, n);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_Asprintf --
 *
 *    Same as Str_Vasprintf(), but parameters are passed inline
 *
 * Results:
 *    Same as Str_Vasprintf()
 *
 * Side effects:
 *    Same as Str_Vasprintf()
 *
 *-----------------------------------------------------------------------------
 */

char *
Str_Asprintf(size_t *length,       // OUT/OPT
             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;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_SafeAsprintf --
 *
 *    Same as Str_SafeVasprintf(), but parameters are passed inline
 *
 * Results:
 *    Same as Str_SafeVasprintf()
 *
 * Side effects:
 *    Same as Str_SafeVasprintf()
 *
 *-----------------------------------------------------------------------------
 */

char *
Str_SafeAsprintf(size_t *length,       // OUT/OPT
                 const char *format,   // IN
                 ...)                  // IN
{
   va_list arguments;
   char *result;

   va_start(arguments, format);
   result = Str_SafeVasprintf(length, format, arguments);
   va_end(arguments);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrVasprintfInternal --
 *
 *    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).
 *
 *    ASSERTs or returns NULL on failure, depending on the value of
 *    'assertOnFailure'.
 *
 * WARNING: See warning at the top of this file.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static char *
StrVasprintfInternal(size_t *length,       // OUT/OPT:
                     const char *format,   // IN:
                     va_list arguments,    // IN:
                     Bool assertOnFailure) // IN:
{
   char *buf = NULL;
   int ret;

#if defined HAS_BSD_PRINTF
   ret = bsd_vsnprintf(&buf, 0, format, arguments);

#elif !defined sun && !defined STR_NO_WIN32_LIBS
   ret = vasprintf(&buf, format, arguments);

#else
   /*
    * Simple implementation of Str_Vasprintf when we we have vsnprintf
    * but not vasprintf (e.g. in Win32 or in drivers). We just fallback
    * to vsnprintf, doubling if we didn't have enough space.
    */
   size_t bufSize = strlen(format);

   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
       */

      char *newBuf;
      va_list tmpArgs;

      bufSize *= 2;
      newBuf = realloc(buf, bufSize);
      if (!newBuf) {
         free(buf);
         buf = NULL;
         goto exit;
      }

      buf = newBuf;

      va_copy(tmpArgs, arguments);
      ret = Str_Vsnprintf(buf, bufSize, format, tmpArgs);
      va_end(tmpArgs);
   } while (ret < 0);
#endif

   if (ret < 0) {
      buf = NULL;
      goto exit;
   }
   if (length != NULL) {
      *length = ret;
   }

  exit:
   if (assertOnFailure) {
      VERIFY(buf);
   }
   return buf;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_Vasprintf --
 *
 *    See StrVasprintfInternal.
 *
 * Results:
 *    Returns NULL on failure.
 *
 * WARNING: See warning at the top of this file.
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */

char *
Str_Vasprintf(size_t *length,       // OUT/OPT
              const char *format,   // IN
              va_list arguments)    // IN
{
   return StrVasprintfInternal(length, format, arguments, FALSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_SafeVasprintf --
 *
 *    See StrVasprintfInternal.
 *
 * Results:
 *    Calls VERIFY on failure.
 *
 * WARNING: See warning at the top of this file.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

char *
Str_SafeVasprintf(size_t *length,       // OUT/OPT
                  const char *format,   // IN
                  va_list arguments)    // IN
{
   return StrVasprintfInternal(length, format, arguments, TRUE);
}

#if defined(_WIN32) // {

/*
 *----------------------------------------------------------------------
 *
 * Str_Swprintf --
 *
 *      swprintf wrapper that fails on overflow
 *
 * Results:
 *      Returns the number of wchar_ts stored in 'buf'.
 *
 * WARNING:
 *      Behavior of this function is guaranteed only if HAS_BSD_PRINTF is
 *      enabled.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Str_Swprintf(wchar_t *buf,       // OUT
             size_t maxSize,     // IN: Size of buf, in wide-characters.
             const wchar_t *fmt, // IN
             ...)                // IN
{
   va_list args;
   int i;

   va_start(args,fmt);
   i = Str_Vsnwprintf(buf, maxSize, fmt, args);
   va_end(args);
   if (i < 0) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }
   return i;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Vsnwprintf --
 *
 *      Compatibility wrapper b/w different libc versions
 *
 * Results:
 *
 *      int - number of wchar_ts stored in 'str' (not including NUL
 *      terminate character), -1 on overflow (insufficient space for
 *      NUL terminate is considered overflow)
 *
 *      NB: on overflow the buffer WILL be NUL terminated
 *
 * WARNING:
 *      Behavior of this function is guaranteed only if HAS_BSD_PRINTF is
 *      enabled.
 *
 *      See the warning at the top of this file for proper va_list usage.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

int
Str_Vsnwprintf(wchar_t *str,          // OUT
               size_t size,           // IN: Size of str, in wide-characters.
               const wchar_t *format, // IN
               va_list ap)            // IN
{
   int retval;

#if defined HAS_BSD_PRINTF
   retval = bsd_vsnwprintf(&str, size, format, ap);
#elif defined(_WIN32)
   /*
    * _vsnwprintf() returns -1 on overflow, returns size when result fits
    * exactly, and does not NUL terminate in those cases.
    */
   retval = _vsnwprintf(str, size, format, ap);
   if ((retval < 0 || retval >= size) && size > 0) {
      str[size - 1] = L'\0';
   }
#else
   /*
    * There is no standard vsnwprintf function.  vswprintf is close, but unlike
    * vsnprintf, vswprintf always returns a negative value if truncation
    * occurred.  Additionally, the state of the destination buffer on failure
    * is not specified.  Although the C99 specification says that [v]swprintf
    * should always NUL-terminate, glibc (as of 2.24) is non-conforming and
    * seems to leave the final element untouched.
    */
   retval = vswprintf(str, size, format, ap);
   if (retval < 0 && size > 0) {
      str[size - 1] = L'\0';
   }
#endif

   if (retval >= size) {
      return -1;
   }

   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Snwprintf --
 *
 *      Compatibility wrapper b/w different libc versions
 *
 * Results:
 *
 *      int - number of wchar_ts stored in 'str' (not including NUL
 *      terminate character), -1 on overflow (insufficient space for
 *      NUL terminate is considered overflow)
 *
 *      NB: on overflow the buffer WILL be NUL terminated
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

int
Str_Snwprintf(wchar_t *str,          // OUT
              size_t size,           // IN: Size of str, in wide-characters.
              const wchar_t *format, // IN
              ...)                   // IN
{
   int retval;
   va_list args;

   va_start(args, format);
   retval = Str_Vsnwprintf(str, size, format, args);
   va_end(args);
   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Wcscpy --
 *
 *      Wrapper for wcscpy that checks for buffer overruns.
 *
 * Results:
 *    Same as wcscpy.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

wchar_t *
Str_Wcscpy(wchar_t *buf,       // OUT
           const wchar_t *src, // IN
           size_t maxSize)     // IN: Size of buf, in wide-characters.
{
   size_t len;

   len = wcslen(src);
   if (len >= maxSize) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }
   return memcpy(buf, src, (len + 1)*sizeof(wchar_t));
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Wcscat --
 *
 *      Wrapper for wcscat that checks for buffer overruns.
 *
 * Results:
 *    Same as wcscat.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

wchar_t *
Str_Wcscat(wchar_t *buf,       // IN/OUT
           const wchar_t *src, // IN
           size_t maxSize)     // IN: Size of buf, in wide-characters.
{
   size_t bufLen;
   size_t srcLen;

   bufLen = wcslen(buf);
   srcLen = wcslen(src);

   /* The first comparison checks for numeric overflow */
   if (bufLen + srcLen < srcLen || bufLen + srcLen >= maxSize) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }

   memcpy(buf + bufLen, src, (srcLen + 1)*sizeof(wchar_t));

   return buf;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Wcsncat --
 *
 *    Wrapper for wcsncat that checks for buffer overruns.
 *
 *    Specifically, this function will Panic if a buffer overrun would
 *    have occurred.
 *
 * Results:
 *    Same as wcsncat.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

wchar_t *
Str_Wcsncat(wchar_t *buf,       // IN/OUT
            size_t bufSize,     // IN: Size of buf, in wide-characters.
            const wchar_t *src, // IN: String to append
            size_t n)           // IN: Max chars of src to append
{
   size_t bufLen = wcslen(buf);

   /*
    * Check bufLen + n first so we can avoid the second call to wcslen
    * if possible.
    *
    * The reason the test with bufLen and n is >= rather than just >
    * is that wcsncat always NUL-terminates the resulting string, even
    * if it reaches the length limit n. This means that if it happens that
    * bufLen + n == bufSize, wcsncat will write a NUL terminator that
    * is outside of the buffer. Therefore, we make sure this does not
    * happen by adding the == case to the Panic test.
    */

   if (bufLen + n >= bufSize &&
       bufLen + wcslen(src) >= bufSize) {
      Panic("%s:%d Buffer too small\n", __FILE__, __LINE__);
   }

   /*
    * We don't need to worry about NUL termination, because it's only
    * needed on overflow and we Panic above in that case.
    */

   return wcsncat(buf, src, n);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StrVaswprintfInternal --
 *
 *    Allocate and format a string.
 *
 * Results:
 *    The allocated string on success (if 'length' is not NULL, *length
 *    is set to the length of the allocated string, in wchat_ts)
 *
 *    ASSERTs or returns NULL on failure, depending on the value of
 *    'assertOnFailure'.
 *
 * WARNING: See warning at the top of this file.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static wchar_t *
StrVaswprintfInternal(size_t *length,         // OUT/OPT:
                      const wchar_t *format,  // IN:
                      va_list arguments,      // IN
                      Bool assertOnFailure)   // IN
{
   size_t bufSize;
   wchar_t *buf = NULL;
   int retval;

   bufSize = wcslen(format);

   do {
      /*
       * Initial allocation of wcslen(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 tmpArgs;
      wchar_t *newBuf;

      bufSize *= 2;
      newBuf = realloc(buf, bufSize * sizeof(wchar_t));
      if (!newBuf) {
         free(buf);
         buf = NULL;
         goto exit;
      }

      buf = newBuf;

      va_copy(tmpArgs, arguments);
      retval = Str_Vsnwprintf(buf, bufSize, format, tmpArgs);
      va_end(tmpArgs);
   } while (retval == -1);

   if (length) {
      *length = retval;
   }

   /*
    * Try to trim the buffer here to save memory?
    */

  exit:
   if (assertOnFailure) {
      VERIFY(buf);
   }
   return buf;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_Aswprintf --
 *
 *    Same as Str_Vaswprintf(), but parameters are passed inline.
 *
 * Results:
 *    Same as Str_Vaswprintf()
 *
 * Side effects:
 *    Same as Str_Vaswprintf()
 *
 *-----------------------------------------------------------------------------
 */

wchar_t *
Str_Aswprintf(size_t *length,         // OUT/OPT
              const wchar_t *format,  // IN
              ...)                    // IN
{
   va_list arguments;
   wchar_t *result;

   va_start(arguments, format);
   result = Str_Vaswprintf(length, format, arguments);
   va_end(arguments);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_Vaswprintf --
 *
 *    See StrVaswprintfInternal.
 *
 * Results:
 *    Returns NULL on failure.
 *
 * WARNING: See warning at the top of this file.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

wchar_t *
Str_Vaswprintf(size_t *length,         // OUT/OPT
               const wchar_t *format,  // IN
               va_list arguments)      // IN
{
   return StrVaswprintfInternal(length, format, arguments, FALSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_SafeAswprintf --
 *
 *    Same as Str_SafeVaswprintf(), but parameters are passed inline.
 *
 * Results:
 *    Same as Str_SafeVaswprintf()
 *
 * Side effects:
 *    Same as Str_SafeVaswprintf()
 *
 *-----------------------------------------------------------------------------
 */

wchar_t *
Str_SafeAswprintf(size_t *length,         // OUT/OPT
                  const wchar_t *format,  // IN
                  ...)                    // IN
{
   va_list arguments;
   wchar_t *result;

   va_start(arguments, format);
   result = Str_SafeVaswprintf(length, format, arguments);
   va_end(arguments);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_SafeVaswprintf --
 *
 *    See StrVaswprintfInternal.
 *
 * Results:
 *    Calls VERIFY on failure.
 *
 * WARNING: See warning at the top of this file.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

wchar_t *
Str_SafeVaswprintf(size_t *length,         // OUT/OPT
                   const wchar_t *format,  // IN
                   va_list arguments)      // IN
{
   return StrVaswprintfInternal(length, format, arguments, TRUE);
}

#endif // } defined(_WIN32)

#ifndef _WIN32

/*
 *-----------------------------------------------------------------------------
 *
 * Str_ToLower --
 *
 *      Convert a string to lowercase, in-place. Hand-rolled, for non-WIN32.
 *
 * Results:
 *
 *      Returns the same pointer that was passed in.
 *
 * Side effects:
 *
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

char *
Str_ToLower(char *string)  // IN
{
   char *c = string;

   while (*c) {
      *c = (*c >= 'A' && *c <= 'Z') ? *c + ('a' - 'A') : *c;
      c++;
   }

   return string;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_ToUpper --
 *
 *      Convert a string to uppercase, in-place. Hand-rolled, for non-WIN32.
 *
 * Results:
 *
 *      Returns the same pointer that was passed in.
 *
 * Side effects:
 *
 *      See above.
 *
 *-----------------------------------------------------------------------------
 */

char *
Str_ToUpper(char *string)  // IN
{
   char *c = string;

   while (*c) {
      *c = (*c >= 'a' && *c <= 'z') ? *c - ('a' - 'A') : *c;
      c++;
   }

   return string;
}

#endif // !_WIN32

#if 0

/*
 * Unit tests.
 */

#define FAIL(s) \
   do { \
      printf("FAIL: %s\n", s); \
      exit(1); \
   } while (0);


static void
CheckPrintf(const char *expected,  // IN
            const char *fmt,       // IN
            ...)                   // IN
{
#if !defined HAS_BSD_PRINTF
   NOT_TESTED_ONCE();
#else
   char buf[1024] = "";
   int count;
   int expectedCount = strlen(expected);
   va_list args;

   va_start(args, fmt);
   count = Str_Vsnprintf(buf, sizeof buf, fmt, args);
   va_end(args);

   // Verify there's a NUL somewhere since we print the buffer on failure.
   VERIFY(buf[ARRAYSIZE(buf) - 1] == '\0');

   if (count == expectedCount && strcmp(buf, expected) == 0) {
      // Success.
      return;
   }

   printf("%s\n", buf);
   printf("Format string: %s\n", fmt);
   printf("Expected count: %d\n", expectedCount);
   printf("Expected output: %s\n", expected);
   printf("Actual count: %d\n", count);
   printf("Actual output: %s\n", buf);
   FAIL("CheckPrintf");
#endif
}


static void
CheckWPrintf(const wchar_t *expected, // IN
             const wchar_t *fmt,      // IN
             ...)                     // IN
{
#if !defined HAS_BSD_PRINTF
   NOT_TESTED_ONCE();
#else
   wchar_t buf[1024] = L"";
   int count;
   int expectedCount = wcslen(expected);
   va_list args;

   va_start(args, fmt);
   count = Str_Vsnwprintf(buf, sizeof buf, fmt, args);
   va_end(args);

   // Verify there's a NUL somewhere since we print the buffer on failure.
   VERIFY(buf[ARRAYSIZE(buf) - 1] == L'\0');

   if (count == expectedCount && wcscmp(buf, expected) == 0) {
      // Success.
      return;
   }

   /*
    * %S isn't standard, and since we use the system printf here, we must use
    * %ls instead.
    */
   printf("%ls\n", buf);
   printf("Format string: %ls\n", fmt);
   printf("Expected count: %d\n", expectedCount);
   printf("Expected output: %ls\n", expected);
   printf("Actual count: %d\n", count);
   printf("Actual output: %ls\n", buf);
   FAIL("CheckWPrintf");
#endif
}


void
Str_UnitTests(void)
{
   char buf[1024];
   wchar_t bufw[1024];
   int count;
   const void *p = (void*) 0xFEEDFACE;
   int32 num1 = 0xDEADBEEF;
   int32 num2 = 0x927F82CD;
   int64 num3 = CONST64U(0xCAFEBABE42439021);
   const double d[] = { 5.0, 2017.0, 0.000482734, 8274102.3872 };
   int numChars;
   char empty[1] = {'\0'};
   wchar_t wempty[1] = {L'\0'};

   /* test empty string */
   count = Str_Snprintf(buf, 1, empty);

   if (0 != count) {
      FAIL("Failed empty string test");
   }

   count = Str_Snwprintf(bufw, 1, wempty);

   if (0 != count) {
      FAIL("Failed empty string test (W)");
   }

   /* test borderline overflow */
   count = Str_Snprintf(buf, 2, "ba");

   if (-1 != count) {
      FAIL("Failed borderline overflow test - count");
   }

   if (buf[1]) {
      FAIL("Failed borderline overflow test - NULL term");
   }

   count = Str_Snwprintf(bufw, 2, L"ba");

   if (-1 != count) {
      FAIL("Failed borderline overflow test - count (W)");
   }

   if (bufw[1]) {
      FAIL("Failed borderline overflow test - NULL term (W)");
   }

   /* test egregious overflow */
   count = Str_Snprintf(buf, 2, "baabaa");

   if (-1 != count) {
      FAIL("Failed egregious overflow test - count");
   }

   if (buf[1]) {
      FAIL("Failed egregious overflow test - NULL term");
   }

   count = Str_Snwprintf(bufw, 2, L"baabaa");

   if (-1 != count) {
      FAIL("Failed egregious overflow test - count (W)");
   }

   if (bufw[1]) {
      FAIL("Failed egregious overflow test - NULL term (W)");
   }

   /* test 'n' argument */
   count = Str_Snprintf(buf, 1024, "foo %n\n", &numChars);

   if (-1 == count) {
      FAIL("Failed 'n' arg test - count");
   }

   if (4 != numChars) {
      FAIL("Failed 'n' arg test - numChars");
   }

   count = Str_Snwprintf(bufw, 1024, L"foo %n\n", &numChars);

   if (-1 == count) {
      FAIL("Failed 'n' arg test - count (W)");
   }

   if (4 != numChars) {
      FAIL("Failed 'n' arg test - numChars (W)");
   }

   // simple
   CheckPrintf("hello", "hello");
   CheckWPrintf(L"hello", L"hello");

   // string arguments
   CheckPrintf("whazz hello up hello doc",
               "whazz %s up %S doc", "hello", L"hello");
   CheckWPrintf(L"whazz hello up hello doc",
                L"whazz %s up %S doc", "hello", L"hello");

   // character arguments
   CheckPrintf("whazz a up a doc",
               "whazz %c up %C doc", 'a', L'a');
   CheckWPrintf(L"whazz a up a doc",
                L"whazz %c up %C doc", 'a', L'a');

   // 32-bit integer arguments
   CheckPrintf("-559038737 -559038737 33653337357 3735928559 deadbeef DEADBEEF",
               "%d %i %o %u %x %X", num1, num1, num1, num1, num1, num1);
   CheckWPrintf(L"-559038737 -559038737 33653337357 3735928559 deadbeef DEADBEEF",
                L"%d %i %o %u %x %X", num1, num1, num1, num1, num1, num1);

   // 'p' argument
   CheckPrintf("FEEDFACE", "%p", p);
   CheckWPrintf(L"FEEDFACE", L"%p", p);

   // 64-bit
   CheckPrintf("CAFEBABE42439021",
               "%LX", num3);
   CheckWPrintf(L"CAFEBABE42439021",
                L"%LX", num3);
   CheckPrintf("CAFEBABE42439021",
               "%llX", num3);
   CheckWPrintf(L"CAFEBABE42439021",
                L"%llX", num3);
   CheckPrintf("CAFEBABE42439021",
               "%qX", num3);
   CheckWPrintf(L"CAFEBABE42439021",
                L"%qX", num3);
   CheckPrintf("CAFEBABE42439021",
               "%I64X", num3);
   CheckWPrintf(L"CAFEBABE42439021",
                L"%I64X", num3);

   // floating-point
   {
      const char *expected[] = {
         "5.000000e+00 5.000000E+00 5.000000 5 5",
         "2.017000e+03 2.017000E+03 2017.000000 2017 2017",
         "4.827340e-04 4.827340E-04 0.000483 0.000482734 0.000482734",
         "8.274102e+06 8.274102E+06 8274102.387200 8.2741e+06 8.2741E+06",
      };
      const wchar_t *expectedW[] = {
         L"5.000000e+00 5.000000E+00 5.000000 5 5",
         L"2.017000e+03 2.017000E+03 2017.000000 2017 2017",
         L"4.827340e-04 4.827340E-04 0.000483 0.000482734 0.000482734",
         L"8.274102e+06 8.274102E+06 8274102.387200 8.2741e+06 8.2741E+06",
      };

      size_t i;

      ASSERT_ON_COMPILE(ARRAYSIZE(d) == ARRAYSIZE(expected));
      ASSERT_ON_COMPILE(ARRAYSIZE(d) == ARRAYSIZE(expectedW));

      for (i = 0; i < ARRAYSIZE(d); i++) {
         CheckPrintf(expected[i],
                     "%e %E %f %g %G", d[i], d[i], d[i], d[i], d[i]);
         CheckWPrintf(expectedW[i],
                      L"%e %E %f %g %G", d[i], d[i], d[i], d[i], d[i]);
      }
   }

   // positional arguments
   CheckPrintf("CAFEBABE42439021 deadbeef 927f82cd",
               "%3$LX %1$x %2$x", num1, num2, num3);
   CheckWPrintf(L"CAFEBABE42439021 deadbeef 927f82cd",
                L"%3$LX %1$x %2$x", num1, num2, num3);

   // width and precision
   CheckPrintf("          8e+06           8274102.39     8274102.387",
               "%15.1g %20.2f %*.*f", d[3], d[3], 15, 3, d[3]);
   CheckWPrintf(L"          8e+06           8274102.39     8274102.387",
                L"%15.1g %20.2f %*.*f", d[3], d[3], 15, 3, d[3]);

   // flags
   CheckPrintf("5.000000e+00    +0.000483 000008.2741e+06",
               "%-15e %+f %015g", d[0], d[2], d[3]);
   CheckWPrintf(L"5.000000e+00    +0.000483 000008.2741e+06",
                L"%-15e %+f %015g", d[0], d[2], d[3]);

   // more flags
   CheckPrintf("0XDEADBEEF 5.000000E+00 2017.00",
               "%#X %#E %#G", num1, d[0], d[1]);
   CheckWPrintf(L"0XDEADBEEF 5.000000E+00 2017.00",
                L"%#X %#E %#G", num1, d[0], d[1]);

   printf("%s succeeded.\n", __FUNCTION__);
}

#endif // 0
070701000001DD000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002D00000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs  070701000001DE000081A400000000000000000000000168225505000005DD000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/Makefile.am  ################################################################################
### Copyright (C) 2008-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libStubs.la

libStubs_la_SOURCES =
libStubs_la_SOURCES += stub-config.c
libStubs_la_SOURCES += stub-log.c
libStubs_la_SOURCES += stub-debug.c
libStubs_la_SOURCES += stub-warning.c
libStubs_la_SOURCES += stub-panic.c
libStubs_la_SOURCES += stub-user-msg.c
libStubs_la_SOURCES += stub-user-panic.c
libStubs_la_SOURCES += stub-user-util.c

noinst_LTLIBRARIES += libStubsCS.la

libStubsCS_la_SOURCES =
libStubsCS_la_SOURCES += stub-config.c
libStubsCS_la_SOURCES += stub-user-msg.c
libStubsCS_la_SOURCES += stub-user-panic.c

if !LINUX
   libStubsCS_la_SOURCES += stub-msgfmt-fbsd.c
endif

   070701000001DF000081A400000000000000000000000168225505000009DE000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-config.c    /*********************************************************
 * Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-config.c --
 *
 *   Stubs for lib/config.
 *
 */

#include <string.h>

#include "vm_assert.h"
#include "config.h"
#include "preference.h"


Bool
Config_GetBool(Bool defaultValue,
               const char *fmt,
               ...)
{
   return defaultValue;
}


int32
Config_GetLong(int32 defaultValue,
               const char *fmt,
               ...)
{
   return defaultValue;
}


double
Config_GetDouble(double defaultValue,
                 const char *fmt,
                 ...)
{
   return defaultValue;
}


char *
Config_GetString(const char *defaultValue,
                 const char *fmt, ...)
{
   return (defaultValue == NULL) ? NULL : strdup(defaultValue);
}

Bool
Preference_Init(void)
{
   return TRUE;
}

void
Preference_Exit(void)
{
}

Bool
Preference_GetBool(Bool defaultValue,
                   const char *name)
{
   return defaultValue;
}


char *
Preference_GetString(const char *defaultValue,
                     const char *name)
{
   return (defaultValue == NULL) ? NULL : strdup(defaultValue);
}


char *
Preference_GetPathName(const char *defaultValue,
                       const char *fmt)
{
   return (defaultValue == NULL) ? NULL : strdup(defaultValue);
}

int32
Config_GetTriState(int32 defaultValue,
                   const char *fmt, ...)
{
   return defaultValue;
}

char *
Config_GetPathName(const char *defaultValue,
                   const char *format, ...)
{
   return defaultValue ? strdup(defaultValue) : NULL;
}

Bool
Config_NotSet(const char *fmt, ...)
{
   return FALSE;
}

int32
Preference_GetLong(int32 defaultValue,
                   const char *name)
{
   return defaultValue;
}

  070701000001E0000081A400000000000000000000000168225505000004B1000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-debug.c /*********************************************************
 * 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 Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-debug.c --
 *
 *   Stub for Debug().
 *
 */

#include <stdio.h>
#include "str.h"


void
Debug(const char *fmt, ...)
{
#ifdef VMX86_DEBUG
   char *str;
   va_list args;

   va_start(args, fmt);
   str = Str_Vasprintf(NULL, fmt, args);
   va_end(args);

   if (str != NULL) {
      fputs(str, stderr);
   }
#endif
}

   070701000001E1000081A40000000000000000000000016822550500000BC5000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-log.c   /*********************************************************
 * Copyright (C) 2008-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-log.c --
 *
 *   Stub for lib/log.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "str.h"
#include "log.h"


/*
 * XXX: the check is a hack to work around stupid libraries, like
 * bora/lib/install, that provide implementations for only some of
 * the functions of the real library, but not all.
 */
#if !defined(NO_LOG_STUB)

void
LogV(uint32 unused,
     const char *fmt,
     va_list args)
{
   char *str;

   str = Str_Vasprintf(NULL, fmt, args);
   if (str != NULL) {
      fputs(str, stderr);
      free(str);
   }
}


void
Log(const char *fmt,
    ...)
{
   va_list args;

   va_start(args, fmt);
   LogV(VMW_LOG_INFO, fmt, args);
   va_end(args);
}


void
Log_Level(uint32 routing,   // IN:
          const char *fmt,  // IN:
          ...)              // IN or OUT: depends on 'fmt'
{
   va_list ap;

   va_start(ap, fmt);
   LogV(routing, fmt, ap);
   va_end(ap);
}


void
Log_HexDumpLevel(uint32 routing,      // IN:
                 const char *prefix,  // IN: prefix for each log line
                 const void *data,    // IN: data to log
                 size_t size)         // IN: number of bytes
{
   size_t i = 0;

   while (i < size) {
      char hex[16 * 3 + 1];
      char ascii[16 + 1];
      unsigned j;

      memset(hex, ' ', sizeof hex - 1);
      hex[sizeof hex - 1] = 0;
      memset(ascii, ' ', sizeof ascii - 1);
      ascii[sizeof ascii - 1] = 0;

      for (j = 0; j < 16 && i < size; j++, i++) {
         uint8 c = ((const uint8 *)data)[i];

         hex[j * 3 + 0] = "0123456789abcdef"[c >> 4];
         hex[j * 3 + 1] = "0123456789abcdef"[c & 0xf];
         ascii[j] = isprint(c) ? c : '.';
      }

      Log_Level(routing, "%s %03"FMTSZ"x: %s%s\n", prefix, i - j, hex, ascii);
   }
}


void
Log_HexDump(const char *prefix,  // IN: prefix for each log line
            const void *data,    // IN: data to log
            size_t size)         // IN: number of bytes
{
   Log_HexDumpLevel(VMW_LOG_INFO, prefix, data, size);
}

#endif


void
Log_DisableThrottling(void)
{

}


const char *
Log_GetFileName(void)
{
   return NULL;
}
   070701000001E2000081A40000000000000000000000016822550500000879000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-msgfmt-fbsd.c   /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-msgfmt-fbsd.c --
 *
 *    FreeBSD-specific stubs for lib/misc/msgfmt.c. This stubs out two
 *    specific funtions in that file - MsgFmt_Asprintf and MsgFmt_Snprintf,
 *    which don't have FreeBSD implementations in our code base.
 *
 *    Tools don't really use the Msg_* functions for error reporting and etc,
 *    so this is easier than getting all that stuff to compile on FreeBSD.
 *
 *    The stubs won't assert, but they're sort of dumb: they'll just
 *    print the format string with no substitutions to the output.
 */

#include "msgfmt.h"
#include "str.h"

int
MsgFmt_Snprintf(char *buf,                // OUT: formatted string
                size_t size,              // IN: size of buffer
                const char *format,       // IN: format
                const MsgFmt_Arg *args,   // IN: message arguments
                int numArgs)              // IN: number of arguments
{
   return Str_Snprintf(buf, size, "%s", format);
}


char *
MsgFmt_Asprintf(size_t *length,           // OUT: length of returned string
                const char *format,       // IN: format
                const MsgFmt_Arg *args,   // IN: message arguments
                int numArgs)              // IN: number of arguments
{
   return Str_Asprintf(length, "%s", format);
}

   070701000001E3000081A40000000000000000000000016822550500000547000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-panic.c /*********************************************************
 * Copyright (C) 2008,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-panic.c --
 *
 *   Stub for lib/panic.
 *
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include "vm_assert.h"
#include "str.h"


void
Panic(const char *fmt, ...)
{
   char *str;
   va_list args;

   va_start(args, fmt);
   str = Str_Vasprintf(NULL, fmt, args);
   va_end(args);

   if (str != NULL) {
      fputs(str, stderr);
      free(str);
   }

   assert(FALSE);
   exit(255);
}


Bool
Panic_GetPanicMsgPost(void)
{
   return FALSE;
}
 070701000001E4000081A400000000000000000000000168225505000007FD000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-user-msg.c  /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-user-msg.c --
 *
 *   Stubs for Msg_* functions in lib/user.
 *
 */

#if defined(_WIN32)
#  include <windows.h>
#endif
#include "vm_assert.h"
#include "msg.h"
#include "str.h"


void
Msg_AppendMsgList(const MsgList *msgs)
{
   while (msgs != NULL) {
      Warning("%s [STUB]: %s\n", __FUNCTION__, msgs->id);
      msgs = msgs->next;
   }
}


void
Msg_Append(const char *fmt,
           ...)
{
   static char buf[1000];

   va_list args;
   va_start(args, fmt);
   Str_Vsnprintf(buf, sizeof buf, fmt, args);
   va_end(args);

   Warning("%s [STUB]: %s\n", __FUNCTION__, buf);
}


void
Msg_Post(MsgSeverity severity,
         const char *idFmt, ...)
{
   NOT_IMPLEMENTED();
}


unsigned int
Msg_Question(Msg_String const *buttons,
             int defaultAnswer,
             char const *fmt,
             ...)
{
   static char buf[1000];

   va_list args;
   va_start(args, fmt);
   Str_Vsnprintf(buf, sizeof buf, fmt, args);
   va_end(args);

   Warning("%s [STUB]: %s\n", __FUNCTION__, buf);

   return (unsigned int) defaultAnswer;
}


void
Msg_Reset(Bool log)
{
   NOT_IMPLEMENTED();
}

char *
Msg_FormatSizeInBytes(uint64 size)
{
   return NULL;
}


   070701000001E5000081A40000000000000000000000016822550500000501000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-user-panic.c    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-user-panic.c --
 *
 *   Stubs for Panic_* functions in lib/user.
 *
 */

#include <stdarg.h>
#include <stdio.h>

#include "vmware.h"
#include "panic.h"
#include "str.h"


void
Panic_PostPanicMsg(const char *fmt, ...)
{
   char *str;
   va_list args;

   va_start(args, fmt);
   str = Str_Vasprintf(NULL, fmt, args);
   va_end(args);

   if (str != NULL) {
      fputs(str, stderr);
   }
}

   070701000001E6000081A40000000000000000000000016822550500000529000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-user-util.c /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-user-util.c --
 *
 *   Stubs for Util_* functions in lib/user.
 *
 */

#if defined(_WIN32)
#  include <windows.h>
#endif

#include <stdlib.h>
#include "vm_assert.h"
#include "util.h"

void
Util_Backtrace(int bugNr)
{
   NOT_IMPLEMENTED();
}


void
Util_ExitProcessAbruptly(int code) // IN
{
#if defined(_WIN32)
   TerminateProcess(GetCurrentProcess(),code);
#else
   exit(code);
#endif
}

int
Util_HasAdminPriv()
{
   return 1;
}
   070701000001E7000081A400000000000000000000000168225505000004CB000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/stubs/stub-warning.c   /*********************************************************
 * Copyright (C) 2008-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-warning.c --
 *
 *   Stub for Warning().
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include "str.h"


void
Warning(const char *fmt, ...)
{
   char *str;
   va_list args;

   va_start(args, fmt);
   str = Str_Vasprintf(NULL, fmt, args);
   va_end(args);

   if (str != NULL) {
      fputs(str, stderr);
      free(str);
   }
}
 070701000001E8000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/lib/syncDriver 070701000001E9000081A400000000000000000000000168225505000004D5000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/lib/syncDriver/Makefile.am ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libSyncDriver.la

libSyncDriver_la_CPPFLAGS =
libSyncDriver_la_CPPFLAGS += @GLIB2_CPPFLAGS@

libSyncDriver_la_SOURCES =
libSyncDriver_la_SOURCES += syncDriverPosix.c

if LINUX
   libSyncDriver_la_SOURCES += nullDriver.c
   libSyncDriver_la_SOURCES += syncDriverLinux.c
   libSyncDriver_la_SOURCES += vmSyncDriver.c
endif

   070701000001EA000081A40000000000000000000000016822550500000A36000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/syncDriver/nullDriver.c    /*********************************************************
 * Copyright (c) 2011-2016, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file nullDriver.c
 *
 * A "null" sync driver that just calls sync(2). This is used when both the
 * Linux kernel ioctls nor the vmsync driver are available in the system,
 * since at that point it's too late to tell the vmbackup code that there
 * is no sync driver.
 */

#include "debug.h"
#include "syncDriverInt.h"
#include "util.h"


/*
 *******************************************************************************
 * NullDriverClose --                                                     */ /**
 *
 * Frees the handle.
 *
 * @param[in] handle Handle to free.
 *
 *******************************************************************************
 */

static void
NullDriverClose(SyncDriverHandle handle)
{
   free(handle);
}


/*
 *******************************************************************************
 * NullDriver_Freeze --                                                   */ /**
 *
 * Calls sync().
 *
 * @param[in]  paths            Unused.
 * @param[out] handle           Where to store the operation handle.
 * @param[in]  ignoreFrozenFS   Unused.
 *
 * @return A SyncDriverErr.
 *
 *******************************************************************************
 */

SyncDriverErr
NullDriver_Freeze(const GSList *paths,
                  SyncDriverHandle *handle,
                  Bool ignoreFrozenFS)
{
   /*
    * This is more of a "let's at least do something" than something that
    * will actually ensure data integrity... we also need to return a dummy
    * handle.
    */
   SyncHandle *h = calloc(1, sizeof *h);
   if (h == NULL) {
      return SD_ERROR;
   }

   h->close = NullDriverClose;
   *handle = h;

   Debug(LGPFX "Using null driver...\n");
   sync();
   return SD_SUCCESS;
}

  070701000001EB000081A40000000000000000000000016822550500000891000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/lib/syncDriver/syncDriverInt.h /*********************************************************
 * Copyright (c) 2011-2017, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _SYNCDRIVERINT_H_
#define _SYNCDRIVERINT_H_

/**
 * @file syncDriverInt.h
 *
 * Internal definitions for the sync driver library.
 */

#include <glib.h>
#include "syncDriver.h"

#define LGPFX "SyncDriver: "

#if !defined(Win32)

#define SYNCDRIVER_PATH_SEPARATOR   ':'

typedef enum {
   SD_SUCCESS,
   SD_ERROR,
   SD_UNAVAILABLE,
} SyncDriverErr;

typedef SyncDriverErr (*SyncFreezeFn)(const GSList *paths,
                                      SyncDriverHandle *handle,
                                      Bool ignoreFrozenFs);

typedef struct SyncHandle {
   SyncDriverErr (*thaw)(const SyncDriverHandle handle);
   void (*close)(SyncDriverHandle handle);
#if defined(__linux__)
   void (*getattr)(const SyncDriverHandle handle, const char **name,
                   Bool *quiesces);
#endif
} SyncHandle;

#if defined(__linux__)
SyncDriverErr
LinuxDriver_Freeze(const GSList *userPaths,
                   SyncDriverHandle *handle,
                   Bool ignoreFrozenFs);

SyncDriverErr
VmSync_Freeze(const GSList *userPaths,
              SyncDriverHandle *handle,
              Bool ignoreFrozenFs);

SyncDriverErr
NullDriver_Freeze(const GSList *userPaths,
                  SyncDriverHandle *handle,
                  Bool ignoreFrozenFs);
#endif

#endif

#endif /* _SYNCDRIVERINT_H_ */

   070701000001EC000081A40000000000000000000000016822550500003108000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/syncDriver/syncDriverLinux.c   /*********************************************************
 * Copyright (C) 2011-2018, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file syncDriverLinux.c
 *
 * A sync driver backend that uses the Linux "FIFREEZE" and "FITHAW" ioctls
 * to freeze and thaw file systems.
 */

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include "debug.h"
#include "dynbuf.h"
#include "syncDriverInt.h"

/* Out toolchain headers are somewhat outdated and don't define these. */
#if !defined(FIFREEZE)
#  define FIFREEZE        _IOWR('X', 119, int)    /* Freeze */
#  define FITHAW          _IOWR('X', 120, int)    /* Thaw */
#endif



typedef struct LinuxFsInfo {
   int fd;
   fsid_t fsid;
} LinuxFsInfo;

typedef struct LinuxDriver {
   SyncHandle  driver;
   size_t      fdCnt;
   LinuxFsInfo *fds;
} LinuxDriver;

static
const fsid_t MISSING_FSID = {};


/*
 *******************************************************************************
 * LinuxFiFsIdMatch --
 *
 * Check the collection of filesystems previously frozen for the specific
 * FSID.
 *
 * @param[in] fds    List of LinuxFsInfo data for filesystems previously
 *                   frozen.
 * @param[in] count  Number of fds in the list.
 * @param[in] nfsid  The Filesystem ID of interest.
 *
 * @return TRUE if the FSID matches one previously processed.  Otherwise FALSE
 *
 *******************************************************************************
 */

static Bool
LinuxFiFsIdMatch(const LinuxFsInfo *fds,
                 const size_t count,
                 const fsid_t *nfsid) {
   size_t i;

   for (i = 0; i < count; i++) {
      if (fds[i].fsid.__val[0] == nfsid->__val[0] &&
          fds[i].fsid.__val[1] == nfsid->__val[1]) {
         return TRUE;
      }
   }
   return FALSE;
}

/*
 *******************************************************************************
 * LinuxFiThaw --                                                         */ /**
 *
 * Thaws the file systems monitored by the given handle. Tries to thaw all the
 * file systems even if an error occurs in one of them.
 *
 * @param[in] handle Handle returned by the freeze call.
 *
 * @return A SyncDriverErr.
 *
 *******************************************************************************
 */

static SyncDriverErr
LinuxFiThaw(const SyncDriverHandle handle)
{
   size_t i;
   LinuxDriver *sync = (LinuxDriver *) handle;
   SyncDriverErr err = SD_SUCCESS;

   /*
    * Thaw in the reverse order of freeze
    */
   for (i = sync->fdCnt; i > 0; i--) {
      int fd = sync->fds[i-1].fd;

      Debug(LGPFX "Thawing fd=%d.\n", fd);
      if (ioctl(fd, FITHAW) == -1) {
         Debug(LGPFX "Thaw failed for fd=%d.\n", fd);
         err = SD_ERROR;
      }
   }

   return err;
}


/*
 *******************************************************************************
 * LinuxFiClose --                                                        */ /**
 *
 * Closes the file descriptors used for freezing, and frees memory associated
 * with the handle.
 *
 * @param[in] handle Handle to close.
 *
 *******************************************************************************
 */

static void
LinuxFiClose(SyncDriverHandle handle)
{
   LinuxDriver *sync = (LinuxDriver *) handle;
   size_t i;

   /*
    * Close in the reverse order of open
    */
   for (i = sync->fdCnt; i > 0; i--) {
      int fd = sync->fds[i-1].fd;

      Debug(LGPFX "Closing fd=%d.\n", fd);
      close(fd);
   }
   free(sync->fds);
   free(sync);
}


/*
 *******************************************************************************
 * LinuxFiGetAttr --                                                      */ /**
 *
 * Return some attributes of the backend provider
 *
 * @param[out] name       name of backend provider
 * @param[out] quiesces   TRUE (FALSE) if provider is (is not) capable
 *                        of quiescing.
 *
 *******************************************************************************
 */

static void
LinuxFiGetAttr(const SyncDriverHandle handle,   // IN (ignored)
               const char **name,               // OUT
               Bool *quiesces)                  // OUT
{
   *name = "fifreeze";
   *quiesces = TRUE;
}


/*
 *******************************************************************************
 * LinuxDriver_Freeze --                                                  */ /**
 *
 * Tries to freeze the filesystems using the Linux kernel's FIFREEZE ioctl.
 *
 * If the first attempt at using the ioctl fails, assume that it doesn't exist
 * and return SD_UNAVAILABLE, so that other means of freezing are tried.
 *
 * NOTE: This function performs two system calls open() and ioctl(). We have
 * seen open() being slow with NFS mount points at times and ioctl() being
 * slow when guest is performing significant IO. Therefore, caller should
 * consider running this function in a separate thread.
 *
 * @param[in]  paths           List of paths to freeze.
 * @param[out] handle          Handle to use for thawing.
 * @param[in]  ignoreFrozenFS  Switch to allow EBUSY error.
 *
 * @return A SyncDriverErr.
 *
 *******************************************************************************
 */

SyncDriverErr
LinuxDriver_Freeze(const GSList *paths,
                   SyncDriverHandle *handle,
                   Bool ignoreFrozenFS)
{
   ssize_t count = 0;
   Bool first = TRUE;
   DynBuf fds;
   LinuxDriver *sync = NULL;
   SyncDriverErr err = SD_SUCCESS;

   DynBuf_Init(&fds);

   Debug(LGPFX "Freezing using Linux ioctls...\n");

   sync = calloc(1, sizeof *sync);
   if (sync == NULL) {
      return SD_ERROR;
   }

   sync->driver.thaw = LinuxFiThaw;
   sync->driver.close = LinuxFiClose;
   sync->driver.getattr = LinuxFiGetAttr;

   /*
    * Ensure we did not get an empty list
    */
   VERIFY(paths != NULL);

   /*
    * Iterate through the requested paths. If we get an error for the first
    * path, and it's not EPERM, assume that the ioctls are not available in
    * the current kernel.
    */
   while (paths != NULL) {
      int fd;
      LinuxFsInfo fsInfo;
      struct stat sbuf;
      struct statfs fsbuf;
      const char *path = paths->data;

      Debug(LGPFX "opening path '%s'.\n", path);
      paths = g_slist_next(paths);
      fd = open(path, O_RDONLY);
      if (fd == -1) {
         switch (errno) {
         case ENOENT:
            /*
             * We sometimes get stale mountpoints or special mountpoints
             * created by the docker engine.
             */
            Debug(LGPFX "cannot find the directory '%s'.\n", path);
            continue;

         case EACCES:
            /*
             * We sometimes get access errors to virtual filesystems mounted
             * as users with permission 700, so just ignore these.
             */
            Debug(LGPFX "cannot access mounted directory '%s'.\n", path);
            continue;

         case ENXIO:
            /*
             * A bind-mounted file, such as a mount of /dev/log for a
             * chrooted application, will land us here.  Just skip it.
             */
            Debug(LGPFX "no such device or address '%s'.\n", path);
            continue;

         case EIO:
            /*
             * A mounted HGFS filesystem with the backend disabled will give
             * us these; probably could use a better way to detect HFGS, but
             * this should be enough. Just skip.
             */
            Debug(LGPFX "I/O error reading directory '%s'.\n", path);
            continue;

         default:
            Debug(LGPFX "failed to open '%s': %d (%s)\n",
                  path, errno, strerror(errno));
            err = SD_ERROR;
            goto exit;
         }
      }

      if (fstat(fd, &sbuf) == -1) {
         close(fd);
         Debug(LGPFX "failed to stat '%s': %d (%s)\n",
               path, errno, strerror(errno));
         err = SD_ERROR;
         goto exit;
      }

      if (!S_ISDIR(sbuf.st_mode)) {
         close(fd);
         Debug(LGPFX "Skipping a non-directory path '%s'.\n", path);
         continue;
      }

      if (fstatfs(fd, &fsbuf) == 0) {
         fsInfo.fsid = fsbuf.f_fsid;
      } else {
         Debug(LGPFX "failed to get file system id for path '%s'.\n", path);
         fsInfo.fsid = MISSING_FSID;
      }
      Debug(LGPFX "freezing path '%s' (fd=%d).\n", path, fd);
      if (ioctl(fd, FIFREEZE) == -1) {
         int ioctlerr = errno;

         close(fd);
         Debug(LGPFX "freeze on '%s' returned: %d (%s)\n",
               path, ioctlerr, strerror(ioctlerr));
         /*
          * Previously, an EBUSY error was ignored, assuming that we may try
          * to freeze the same superblock more than once depending on the
          * OS configuration (e.g., usage of bind mounts).
          * Use the filesystem Id to check if this filesystem has been
          * handled before and, if so, ignore it.
          * Alternatively, allow (ignore) the EBUSY if the
          * "ignoreFrozenFileSystems" switch inside "vmbackup" section of
          * tools.conf file is TRUE.
          * Otherwise, log a warning as the quiesced snapshot
          * attempt will fail.
          */
         if (ioctlerr == EBUSY) {
            if (LinuxFiFsIdMatch(DynBuf_Get(&fds),
                                 DynBuf_GetSize(&fds),
                                 &fsInfo.fsid)) {
               /*
                * We have previous knowledge of this file system by another
                * mount point.  Safe to ignore.
                */
               Debug(LGPFX "skipping path '%s' - previously frozen", path);
               continue;
            } else if (ignoreFrozenFS) {
               /*
                * Ignores the EBUSY error if the FS has been frozen by another
                * process and the 'ignoreFrozenFileSystems' setting is
                * turned on in tools.conf file.
                */
               Debug(LGPFX "Ignoring the frozen filesystem '%s'",path);
               continue;
            }
            /*
             * It appears that this FS has been locked or frozen by another
             * process.  We cannot proceed with the quiesced snapshot request.
             */
            Warning(LGPFX "'%s' appears locked or frozen by another process.  "
                    "Cannot complete the quiesced snapshot request.\n", path);
         }
         /*
          * If the ioctl does not exist, Linux will return ENOTTY. If it's not
          * supported on the device, we get EOPNOTSUPP. Ignore the latter,
          * since freezing does not make sense for all fs types, and some
          * Linux fs drivers may not have been hooked up in the running kernel.
          */
         if (ioctlerr != EOPNOTSUPP) {
            Debug(LGPFX "failed to freeze '%s': %d (%s)\n",
                  path, ioctlerr, strerror(ioctlerr));
            err = first && ioctlerr == ENOTTY ? SD_UNAVAILABLE : SD_ERROR;
            break;
         }
      } else {
         Debug(LGPFX "successfully froze '%s' (fd=%d).\n", path, fd);
         fsInfo.fd = fd;
         if (!DynBuf_Append(&fds, &fsInfo, sizeof fsInfo)) {
            if (ioctl(fd, FITHAW) == -1) {
               Warning(LGPFX "failed to thaw '%s': %d (%s)\n",
                       path, errno, strerror(errno));
            }
            close(fd);
            err = SD_ERROR;
            break;
         }
         count++;
      }

      first = FALSE;
   }

exit:
   sync->fds = DynBuf_Detach(&fds);
   sync->fdCnt = count;

   if (err != SD_SUCCESS) {
      LinuxFiThaw(&sync->driver);
      LinuxFiClose(&sync->driver);
   } else {
      *handle = &sync->driver;
   }
   return err;
}

070701000001ED000081A400000000000000000000000168225505000044B8000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/syncDriver/syncDriverPosix.c   /*********************************************************
 * Copyright (c) 2005-2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * syncDriverPosix.c --
 *
 *   Interface to the Sync Driver for non-Windows guests.
 */

#include <stdio.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <glib.h>
#include "vmware.h"
#include "debug.h"
#include "str.h"
#include "syncDriverInt.h"
#include "util.h"
#include "mntinfo.h"

static SyncFreezeFn gBackends[] = {
#if defined(__linux__) && !defined(USERWORLD)
   LinuxDriver_Freeze,
   VmSync_Freeze,
   NullDriver_Freeze,
#endif
};

static const char *gRemoteFSTypes[] = {
   "autofs",
   "cifs",
   "nfs",
   "nfs4",
   "smbfs",
   "vmhgfs"
};

typedef struct {
   const char *prefix;
   size_t len;
} RemoteDevPrefix;

#define DEF_DEV_PREFIX(a) {(a), sizeof((a)) - 1}

static RemoteDevPrefix gRemoteDevPrefixes[] = {
   DEF_DEV_PREFIX("https://"),
   DEF_DEV_PREFIX("http://")
};

#undef DEF_DEV_PREFIX

/* Cached value of excludedFileSystems */
static char *gExcludedFileSystems = NULL;

/* Array of path patterns parsed and compiled from gExcludedFileSystems */
static GPtrArray *gExcludedPathPatterns = NULL;


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriverIsRemoteFS  --
 *
 *    Checks whether a file system is remote or not
 *
 * Results:
 *    Returns TRUE for remote file system types or device names,
 *    otherwise FALSE.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
SyncDriverIsRemoteFS(const MNTINFO *mntinfo)
{
   size_t i;

   for (i = 0; i < ARRAYSIZE(gRemoteFSTypes); i++) {
      if (Str_Strcmp(gRemoteFSTypes[i], MNTINFO_FSTYPE(mntinfo)) == 0) {
         return TRUE;
      }
   }

   for (i = 0; i < ARRAYSIZE(gRemoteDevPrefixes); i++) {
      if (Str_Strncasecmp(gRemoteDevPrefixes[i].prefix,
                          MNTINFO_NAME(mntinfo),
                          gRemoteDevPrefixes[i].len) == 0) {
         return TRUE;
      }
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriverLocalMounts --
 *
 *    Returns a singly-linked list of all local disk paths mounted in the
 *    system filtering out remote file systems. There is no filtering for
 *    other mount points because we assume that the underlying driver and
 *    IOCTL can deal with "unfreezable" paths. The returned list of paths
 *    is in the reverse order of the paths returned by GETNEXT_MNTINFO.
 *    Caller must free each path and the list itself.
 *
 *    XXX: mntinfo.h mentions Solaris and Linux, but not FreeBSD. If we ever
 *    have a FreeBSD sync driver, we should make sure this function also
 *    works there.
 *
 * Results:
 *    GSList* on success, NULL on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static GSList *
SyncDriverLocalMounts(void)
{
   GSList *paths = NULL;
   GHashTable *devices;
   MNTHANDLE mounts;
   DECLARE_MNTINFO(mntinfo);

   if ((mounts = OPEN_MNTFILE("r")) == NULL) {
      Warning(LGPFX "Failed to open mount point table.\n");
      return NULL;
   }

   devices = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);

   while (GETNEXT_MNTINFO(mounts, mntinfo)) {
      const char *device;
      const char *path;
      const char *prevDevicePath;

      device = MNTINFO_NAME(mntinfo);
      path = MNTINFO_MNTPT(mntinfo);

      /*
       * Skip remote mounts because they are not freezable and opening them
       * could lead to hangs. See PR 1196785.
       */
      if (SyncDriverIsRemoteFS(mntinfo)) {
         Debug(LGPFX "Skipping remote file system, name=%s, mntpt=%s.\n",
               device, path);
         continue;
      }

      /*
       * Avoid adding a path to the list, if we have already got
       * a path mounting the same device path.
       */
      prevDevicePath = g_hash_table_lookup(devices, device);
      if (prevDevicePath != NULL) {
         Debug(LGPFX "Skipping duplicate file system, name=%s, mntpt=%s "
               "(existing path=%s).\n", device, path, prevDevicePath);
         continue;
      }

      g_hash_table_insert(devices, Util_SafeStrdup(device),
                          Util_SafeStrdup(path));

      /*
       * A mount point could depend on existence of a previous mount
       * point like a loopback. In order to avoid deadlock/hang in
       * freeze operation, a mount point needs to be frozen before
       * its dependency is frozen.
       * Typically, mount points are listed in the order they are
       * mounted by the system i.e. dependent comes after the
       * dependency. So, we need to keep them in reverse order of
       * mount points to achieve the dependency order.
       */
      paths = g_slist_prepend(paths, Util_SafeStrdup(path));
   }

   g_hash_table_destroy(devices);
   (void) CLOSE_MNTFILE(mounts);
   return paths;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriver_Init --
 *
 *    Checks whether a sync backend is available.
 *
 * Results:
 *    TRUE if there are sync backends available.
 *    FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
SyncDriver_Init(void)
{
   return ARRAYSIZE(gBackends) > 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriverFreePath --
 *
 *    A GFunc for freeing path strings. It is intended for g_slist_foreach.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
SyncDriverFreePath(gpointer data, gpointer userData)
{
   free(data);
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriverUpdateExcludedFS --
 *
 *    Update the excluded file system list and compile the path patterns.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
SyncDriverUpdateExcludedFS(const char *excludedFS)    // IN
{
   gchar **patterns = NULL;
   int i;

   ASSERT((gExcludedFileSystems == NULL && gExcludedPathPatterns == NULL) ||
          (gExcludedFileSystems != NULL && gExcludedPathPatterns != NULL));

   /*
    * Passing a NULL pointer to g_ptr_array_free appears to result in
    * a glib assert array, hence this test.  As per the above assert,
    * either both of gExcludedFileSystems and gExcludedPathPatterns
    * are NULL or both aren't.
    */
   if (gExcludedPathPatterns != NULL) {
      /*
       * Free the data but don't set the pointers to anything here because
       * they're about to get new assignments below.
       */
      g_free(gExcludedFileSystems);
      g_ptr_array_free(gExcludedPathPatterns, TRUE);
   }

   if (excludedFS == NULL) {
      Debug(LGPFX "Set the excluded file system list to (null).\n");
      gExcludedFileSystems = NULL;
      gExcludedPathPatterns = NULL;
      return;
   }

   Debug(LGPFX "Set the excluded file system list to \"%s\".\n", excludedFS);

   gExcludedFileSystems = g_strdup(excludedFS);
   gExcludedPathPatterns =
         g_ptr_array_new_with_free_func((GDestroyNotify) &g_pattern_spec_free);

   patterns = g_strsplit(gExcludedFileSystems, ",", 0);

   for (i = 0; patterns[i] != NULL; ++i) {
      if (patterns[i][0] != '\0') {
         g_ptr_array_add(gExcludedPathPatterns,
                         g_pattern_spec_new(patterns[i]));
      }
   }

   g_strfreev(patterns);
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriverIsExcludedFS --
 *
 *    See whether a given path (mount point) matches any of the
 *    file systems to be excluded.
 *
 *    Assumes that the caller has verified that the excluded file
 *    system list is non-empty.
 *
 * Results:
 *    TRUE if the path matches at least one of the excluded path patterns
 *    FALSE no matches found
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
SyncDriverIsExcludedFS(const char *path)                 // IN
{
   int i;

   ASSERT(gExcludedFileSystems != NULL && gExcludedPathPatterns != NULL);
   ASSERT(path != NULL);

   for (i = 0; i < gExcludedPathPatterns->len; ++i) {
      if (g_pattern_match_string(g_ptr_array_index(gExcludedPathPatterns, i),
                                 path)) {
         return TRUE;
      }
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriverFilterFS
 *
 *    Remove a specified list of file systems from a list of paths.  The
 *    parameter excludedFS is a string containing a comma-separated
 *    list of patterns describing file systems that are to be excluded
 *    from the the quiescing operation.  The patterns specify the paths
 *    of file system mount points.  This routine removes from pathlist
 *    any paths that match one or more patterns specified in excludedFS.
 *
 * Results:
 *    Modified path list with all paths matching excludedFS removed.
 *
 * Side effects:
 *    Calls other routines that modify the global variables
 *    gExcludedFileSystems and gExcludedPathPatterns, which cache the
 *    information in excludedFS.
 *
 *-----------------------------------------------------------------------------
 */

GSList *
SyncDriverFilterFS(GSList *pathlist,             // IN / OUT
                   const char *excludedFS)       // IN
{
   GSList *current;

   /*
    * Update the excluded file system list if excludedFS has changed.
    */
   if (g_strcmp0(excludedFS, gExcludedFileSystems) != 0) {
      SyncDriverUpdateExcludedFS(excludedFS);
   } else {
      Debug(LGPFX "Leave the excluded file system list as \"%s\".\n",
         (excludedFS != NULL) ? excludedFS : "(null)");
   }

   /*
    * If the excluded file system list is empty, return the path list as is.
    */
   if (gExcludedFileSystems == NULL) {
      return pathlist;
   }

   /*
    * Traverse the path list, removing all file systems that should be
    * excluded.
    */
   current = pathlist;
   while (current != NULL) {
      GSList *next = g_slist_next(current);
      char *path = (char *) current->data;

      if (SyncDriverIsExcludedFS(path)) {
         Debug(LGPFX "Excluding file system, name=%s\n", path);
         pathlist = g_slist_delete_link(pathlist, current);
         free(path);
      }
      current = next;
   }

   return pathlist;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriver_Freeze --
 *
 *    Freeze I/O on the indicated drives. "all" means all drives.
 *    Handle is set to SYNCDRIVER_INVALID_HANDLE on failure.
 *    Freeze operations are currently synchronous in POSIX systems, but
 *    clients should still call SyncDriver_QueryStatus to maintain future
 *    compatibility in case that changes.
 *
 *    excludedFileSystems is the value of the tools.conf setting of the same
 *    name.  If non-NULL, It's expected to be a list of patterns specifying
 *    file system mount points to be excluded from the freeze operation.
 *
 *    This function will try different available sync implementations. It will
 *    follow the order in the "gBackends" array, and keep on trying different
 *    backends while SD_UNAVAILABLE is returned. If all backends are
 *    unavailable (unlikely given the "null" backend), the the function returns
 *    error. NullDriver will be tried only if enableNullDriver is TRUE.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    See description.
 *
 *-----------------------------------------------------------------------------
 */

Bool
SyncDriver_Freeze(const char *userPaths,              // IN
                  Bool enableNullDriver,              // IN
                  SyncDriverHandle *handle,           // OUT
                  const char *excludedFileSystems,    // IN
                  Bool ignoreFrozenFS)                // IN
{
   GSList *paths = NULL;
   SyncDriverErr err = SD_UNAVAILABLE;
   size_t i = 0;

   /*
    * NOTE: Ignore disk UUIDs. We ignore the userPaths if it does
    * not start with '/' because all paths are absolute and it is
    * possible only when we get diskUUID as userPaths. So, all
    * mount points are considered instead of the userPaths provided.
    */
   if (userPaths == NULL ||
       Str_Strncmp(userPaths, "all", sizeof "all") == 0 ||
       userPaths[0] != '/') {
      paths = SyncDriverLocalMounts();
   } else {
      /*
       * The sync driver API specifies spaces as separators.
       */
      while (*userPaths != '\0') {
         const char *c;
         char *path;

         if (*userPaths == ' ') {
            /*
             * Trim spaces from beginning
             */
            userPaths++;
            continue;
         }

         c = strchr(userPaths, ' ');
         if (c == NULL) {
            path = Util_SafeStrdup(userPaths);
            paths = g_slist_append(paths, path);
            break;
         } else {
            path = Util_SafeStrndup(userPaths, c - userPaths);
            paths = g_slist_append(paths, path);
            userPaths = c;
         }
      }
   }

   paths = SyncDriverFilterFS(paths, excludedFileSystems);
   if (paths == NULL) {
      Warning(LGPFX "No file systems to freeze.\n");
      return FALSE;
   }

   while (err == SD_UNAVAILABLE && i < ARRAYSIZE(gBackends)) {
      SyncFreezeFn freezeFn = gBackends[i];
      Debug(LGPFX "Calling backend %d.\n", (int) i);
      i++;
#if defined(__linux__) && !defined(USERWORLD)
      if (!enableNullDriver && (freezeFn == NullDriver_Freeze)) {
         Debug(LGPFX "Skipping nullDriver backend.\n");
         continue;
      }
#endif
      err = freezeFn(paths, handle, ignoreFrozenFS);
   }

   /*
    * g_slist_free_full requires glib >= v2.28
    */
   g_slist_foreach(paths, SyncDriverFreePath, NULL);
   g_slist_free(paths);

   return err == SD_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriver_Thaw --
 *
 *    Thaw I/O on previously frozen volumes.
 *
 * Results:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    See description.
 *
 *-----------------------------------------------------------------------------
 */

Bool
SyncDriver_Thaw(const SyncDriverHandle handle) // IN
{
   if (handle->thaw != NULL) {
      return handle->thaw(handle) == SD_SUCCESS;
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriver_QueryStatus --
 *
 *    Polls the handle and returns the current status of the driver.
 *
 * Results:
 *    SYNCDRIVER_IDLE, since all operations are currently synchronous.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

SyncDriverStatus
SyncDriver_QueryStatus(const SyncDriverHandle handle, // IN
                       int32 timeout)                 // IN
{
   return SYNCDRIVER_IDLE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriver_CloseHandle --
 *
 *    Closes the handle the sets it to SYNCDRIVER_INVALID_HANDLE.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
SyncDriver_CloseHandle(SyncDriverHandle *handle)   // IN/OUT
{
   if (*handle != NULL) {
      if ((*handle)->close != NULL) {
         (*handle)->close(*handle);
      }
      *handle = NULL;
   }
}


#if defined(__linux__)
/*
 *-----------------------------------------------------------------------------
 *
 * SyncDriver_GetAttr --
 *
 *    Returns attributes of the backend provider for this handle.
 *    If the backend does not supply a getattr function, it's treated
 *    as non-quiescing.
 *
 * Results:
 *    No return value.
 *    Sets OUT parameters:
 *        *name:      pointer to backend provider name
 *        *quiesces:  indicates whether backend is capable of quiescing.
 *
 * Side effects:
 *   None.
 *
 *-----------------------------------------------------------------------------
 */

void
SyncDriver_GetAttr(const SyncDriverHandle handle,  // IN
                   const char **name,              // OUT
                   Bool *quiesces)                 // OUT
{

   if (handle != SYNCDRIVER_INVALID_HANDLE && handle->getattr != NULL) {
      handle->getattr(handle, name, quiesces);
   } else {
      *name = NULL;
      *quiesces = FALSE;
   }
}
#endif /* __linux__ */
070701000001EE000081A40000000000000000000000016822550500001174000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/lib/syncDriver/vmSyncDriver.c  /*********************************************************
 * Copyright (c) 2011-2016, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmSyncDriver.c
 *
 * A sync driver backend that uses VMware's "vmsync" driver.
 */

#include <fcntl.h>
#include <sys/ioctl.h>
#include <glib.h>
#include "debug.h"
#include "syncDriverInt.h"
#include "syncDriverIoc.h"
#include "strutil.h"
#include "util.h"

#define SYNC_PROC_PATH "/proc/driver/vmware-sync"

typedef struct VmSyncDriver {
   SyncHandle     driver;
   int            fd;
} VmSyncDriver;


/*
 *******************************************************************************
 * VmSyncThaw --                                                          */ /**
 *
 * Thaws filesystems previously frozen.
 *
 * @param[in] handle Handle returned by the freeze operation.
 *
 * @return A SyncDriverErr.
 *
 *******************************************************************************
 */

static SyncDriverErr
VmSyncThaw(const SyncDriverHandle handle)
{
   const VmSyncDriver *sync = (VmSyncDriver *) handle;
   ASSERT(handle != SYNCDRIVER_INVALID_HANDLE);
   return ioctl(sync->fd, SYNC_IOC_THAW) != -1 ? SD_SUCCESS : SD_ERROR;
}


/*
 *******************************************************************************
 * VmSyncClose --                                                         */ /**
 *
 * Closes the descriptor used to talk to the vmsync driver and frees memory
 * associated with it.
 *
 * @param[in] handle Handle to close.
 *
 *******************************************************************************
 */

static void
VmSyncClose(SyncDriverHandle handle)
{
   VmSyncDriver *sync = (VmSyncDriver *) handle;
   close(sync->fd);
   free(sync);
}


/*
 *******************************************************************************
 * VmSync_Freeze --                                                       */ /**
 *
 * Tries to freeze the requested filesystems with the vmsync driver.
 *
 * Opens a description to the driver's proc node, and if successful, send an
 * ioctl to freeze the requested filesystems.
 *
 * @param[in]  paths          List of paths to freeze.
 * @param[out] handle         Where to store the handle to use for thawing.
 * @param[in]  ignoreFrozenFS Unused.
 *
 * @return A SyncDriverErr.
 *
 *******************************************************************************
 */

SyncDriverErr
VmSync_Freeze(const GSList *paths,
              SyncDriverHandle *handle,
              Bool ignoreFrozenFS)
{
   int file;
   Bool first = TRUE;
   char *allPaths = NULL;
   VmSyncDriver *sync = NULL;

   Debug(LGPFX "Freezing using vmsync driver...\n");

   file = open(SYNC_PROC_PATH, O_RDONLY);
   if (file == -1) {
      return SD_UNAVAILABLE;
   }

   /*
    * Ensure we did not get an empty list
    */
   VERIFY(paths != NULL);

   /*
    * Concatenate all paths in the list into one string
    */
   while (paths != NULL) {
      if (!first) {
         /*
          * Append the separator (':')
          */
         StrUtil_SafeStrcat(&allPaths, ":");
      }
      StrUtil_SafeStrcat(&allPaths, paths->data);
      first = FALSE;
      paths = g_slist_next(paths);
   }

   sync = calloc(1, sizeof *sync);
   if (sync == NULL) {
      goto exit;
   }

   sync->driver.thaw = VmSyncThaw;
   sync->driver.close = VmSyncClose;
   sync->fd = file;

   Debug(LGPFX "Freezing %s using vmsync driver...\n", allPaths);

   if (ioctl(file, SYNC_IOC_FREEZE, allPaths) == -1) {
      free(sync);
      sync = NULL;
   }

exit:
   if (sync == NULL) {
      if (file != -1) {
         close(file);
      }
   } else {
      *handle = &sync->driver;
   }
   free(allPaths);
   return sync != NULL ? SD_SUCCESS : SD_ERROR;
}

070701000001EF000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/lib/system 070701000001F0000081A400000000000000000000000168225505000003DE000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/system/Makefile.am ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libSystem.la

libSystem_la_SOURCES =
libSystem_la_SOURCES += systemLinux.c
  070701000001F1000081A40000000000000000000000016822550500004FBF000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/system/systemLinux.c   /*********************************************************
 * Copyright (C) 1998-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * systemLinux.c --
 *
 *    System-specific routines for all guest applications.
 *
 *    Linux implementation
 *
 */

#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(sun) && !defined(__APPLE__)
#   error This file should not be compiled
#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/times.h>
#include <netdb.h>
#ifdef sun
# include <sys/sockio.h>
#endif

#include <sys/types.h>
#include <sys/socket.h>
/* <netinet/in.h> must precede <arpa/in.h> for FreeBSD to compile. */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <sys/ioctl.h>

#if defined sun || defined __APPLE__
#   include <utmpx.h>
#endif

#ifdef __FreeBSD__
#include "ifaddrs.h"
#endif

#ifdef USERWORLD
#include <vm_basic_types.h>
#include <vmkuserstatus.h>
#include <vmkuseruptime.h>
#endif

#include "vm_assert.h"
#include "system.h"
#include "debug.h"
#include "posix.h"
#include "unicode.h"
#include "dynbuf.h"
#include "hashTable.h"
#include "strutil.h"
#include "vmstdio.h"

#define MAX_IFACES      4
#define LOOPBACK        "lo"
#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16
#endif


/*
 * Data types
 */

/*
 * DynBuf container used by SNEForEachCallback.
 *
 * The SNE prefix is short for "System Native Environ".
 */

typedef struct {
   DynBuf *nativeEnvironStrings;        // FOO=BAR<NUL>BAZ=BOOM<NUL><NUL>
   DynBuf *nativeEnvironOffsets;        // off_ts relative to nativeEnvironStrings->data
} SNEBufs;


/*
 * Local function prototypes
 */


/*
 * "System Native Environ" (SNE) helpers.
 */
static HashTable *SNEBuildHash(const char **nativeEnviron);
static const char **SNEHashToEnviron(HashTable *environTable);
static int SNEForEachCallback(const char *key, void *value, void *clientData);


/*
 * Global functions
 */


/*
 *-----------------------------------------------------------------------------
 *
 * System_GetTimeMonotonic --
 *
 *      See POSIX clock_gettime(CLOCK_MONOTONIC).
 *
 * Results:
 *      Returns monotonically increasing time in hundredths of a second on
 *      success, -1 on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

uint64
System_GetTimeMonotonic(void)
{
   /*
    * Dummy variable b/c times(NULL) segfaults on FreeBSD 3.2 --greg
    */
   struct tms tp;

#if !defined VM_64BIT
   static uint64 base = 0;
   static unsigned long last = 0;
   uint32  current;


   ASSERT(sizeof(current) == sizeof(clock_t));

   current = times(&tp);     // 100ths of a second

   if (current < last) {
      /* The 'jiffies' kernel variable wrapped */
      base += (uint64)1 << (sizeof(current) * 8);
   }

   return base + (last = current);
#else  // VM_64BIT
#if defined sun || defined __APPLE__
   /*
    * PR 1710952 and PR 2136820
    * times() on Solaris & Mac can return a lower value than the
    * one in a previous call. As a workaround, we return the last
    * cached value when we get a lower value from times().
    */
   static Atomic_uint64 last = { 0 };

   while (1) {
      uint64 last1 = Atomic_Read64(&last);
      uint64 now = times(&tp);

      if (now > last1) {
         uint64 last2 = Atomic_ReadIfEqualWrite64(&last, last1, now);
         /* check if another thread changed last, and try again if true */
         if (last2 != last1) {
            continue;
         }
         return now;
      } else {
         return last1;
      }
   }
#else
   return times(&tp);
#endif
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * System_Uptime --
 *
 *      Retrieve the time (in hundredth of s.) since the system has started.
 *
 * Result:
 *      Returns the uptime on success or -1 on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

uint64
System_Uptime(void)
{
   uint64 uptime = -1;

#ifdef USERWORLD
   {
      VmkuserStatus_Code status;
      uint64 sysUptime;
      status = VmkuserUptime_GetUptime(&sysUptime);
      if (VmkuserStatus_IsOK(status)) {
         uptime = sysUptime / 10000;
      }
   }
#elif defined(__linux__)
   {
      FILE *procStream;
      char *buf = NULL;
      size_t bufSize;
      uint64 sec;
      unsigned int csec;

      if (((procStream = Posix_Fopen("/proc/uptime", "r")) != NULL) &&
          (StdIO_ReadNextLine(procStream, &buf, 80, &bufSize) == StdIO_Success) &&
          (sscanf(buf, "%"FMT64"u.%2u", &sec, &csec) == 2)) {
         uptime = sec * 100 + csec;
      } else {
         Warning("%s: Unable to parse /proc/uptime.\n", __func__);
      }

      free(buf);

      if (procStream) {
         fclose(procStream);
      }
   }
#elif defined sun || defined __APPLE__
   {
      struct utmpx *boot, tmp;

      tmp.ut_type = BOOT_TIME;
      if ((boot = getutxid(&tmp)) != NULL) {
         struct timeval now;
         struct timeval *boottime = &boot->ut_tv;

         gettimeofday(&now, NULL);
         uptime =
            (now.tv_sec * 100 + now.tv_usec / 10000) -
            (boottime->tv_sec * 100 + boottime->tv_usec / 10000);
      } else {
         Warning("%s: Unable to determine boot time.\n", __func__);
      }

      endutxent();
   }
#else // FreeBSD
   {
      /*
       * FreeBSD: src/usr.bin/w/w.c rev 1.59:
       *   "Obtain true uptime through clock_gettime(CLOCK_MONOTONIC,
       *    struct *timespec) instead of subtracting 'bootime' from 'now'."
       */
      struct timespec ts;

      if (clock_gettime(CLOCK_MONOTONIC, &ts) != -1) {
         uptime = ts.tv_sec * 100 + ts.tv_nsec / 10000000;
      } else {
         Warning("%s: clock_gettime: %d\n", __func__, errno);
      }
   }
#endif

   return uptime;
}


/*
 *-----------------------------------------------------------------------------
 *
 * System_Shutdown --
 *
 *   Initiate system shutdown.
 *
 * Return value:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
System_Shutdown(Bool reboot)  // IN: "reboot or shutdown" flag
{
   char *cmd;

   if (reboot) {
#if defined(sun)
      cmd = "/usr/sbin/shutdown -g 0 -i 6 -y";
#elif defined(USERWORLD)
      cmd = "/bin/reboot";
#else
      cmd = "/sbin/shutdown -r now";
#endif
   } else {
#if __FreeBSD__
      cmd = "/sbin/shutdown -p now";
#elif defined(sun)
      cmd = "/usr/sbin/shutdown -g 0 -i 5 -y";
#elif defined(USERWORLD)
      cmd = "/bin/halt";
#else
      cmd = "/sbin/shutdown -h now";
#endif
   }
   if (system(cmd) == -1) {
      fprintf(stderr, "Unable to execute %s command: \"%s\"\n",
              reboot ? "reboot" : "shutdown", cmd);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * System_IsUserAdmin --
 *
 *    On Windows this functions checks if the calling user has membership in
 *    the Administrators group (for NT platforms). On POSIX machines, we simply
 *    check if the user's effective UID is root.
 *
 * Return value:
 *    TRUE if the user has an effective UID of root.
 *    FALSE if not.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
System_IsUserAdmin(void)
{
   return geteuid() == 0;
}


/*
 *----------------------------------------------------------------------
 *
 * System_GetEnv --
 *    Read environment variables.
 *
 * Results:
 *    A copy of the environment variable encoded in UTF-8.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

char *
System_GetEnv(Bool global,           // IN
              const char *valueName) // IN: UTF-8
{
   char *result;

   result = Posix_Getenv(valueName);

   if (NULL != result) {
      result = strdup(result);
   }

   return(result);
} // System_GetEnv


/*
 *----------------------------------------------------------------------
 *
 * System_SetEnv --
 *
 *    Write environment variables.
 *
 *    On Linux, this only affects the local process. The global flag
 *    is ignored.
 *
 * Results:
 *    0 if success, -1 otherwise.
 *
 * Side effects:
 *    Changes the environment variable.
 *
 *----------------------------------------------------------------------
 */

int
System_SetEnv(Bool global,      // IN
              const char *valueName,  // IN: UTF-8
              const char *value)      // IN: UTF-8
{
   return Posix_Setenv(valueName, value, 1);
} // System_SetEnv


/*
 *-----------------------------------------------------------------------------
 *
 * System_GetNodeName --
 *
 *      Returns the guest's configured node name.  Does not necessarily
 *      correspond to a proper DNS host name.
 *
 * Results:
 *      On success, returns TRUE with node name written to outBuf.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
System_GetNodeName(size_t outBufSize, // IN:  size of output buffer
                   char *outBuf)      // OUT: output buffer
{
   ASSERT(outBuf);

   if (gethostname(outBuf, outBufSize) < 0) {
      Debug("Error, gethostname failed\n");
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * System_GetNativeEnviron --
 *
 *      Returns a copy of the native / unwrapped environment.
 *
 *      VMware's compatibility library wrappers override certain environment
 *      variables to make use of shipped libraries.  This creates the
 *      "compatibility environment".  Overridden variables are saved into
 *      corresponding VMWARE_-prefixed variables.  This routine recreates the
 *      "native environment" by restoring VMWARE_-prefixed variable values to
 *      their native equivalents.
 *
 *      Every value created by the wrapper begins with a 1 or 0 to indicate
 *      whether the value was set in the native environment.  Based on this:
 *        VMWARE_FOO="1foo"               -> FOO="foo"
 *        VMWARE_FOO="1"                  -> FOO=""
 *        VMWARE_FOO="0"                  -> FOO is unset in the native environment
 *
 *      Variables without the VMWARE_ prefix are just copied over to the new
 *      environment.  Note, of course, that VMWARE_-prefixed variables take
 *      precedence.  (I.e., when we encounter a VMWARE_ prefix, we'll
 *      unconditionally record the value.  For all other variables, we'll
 *      copy only if that variable had not already been set.)
 *
 * Results:
 *      An array of strings representing the native environment.  (I.e.,
 *      use it as you would environ(7).)  NB:  Memory allocation failures
 *      are considered fatal.
 *
 * Side effects:
 *      This routine allocates memory.  It's up to the caller to free it by
 *      passing the returned value to System_FreeNativeEnviron.
 *
 *      This routine assumes that keys will show up in environ only once each.
 *
 *-----------------------------------------------------------------------------
 */

const char **
System_GetNativeEnviron(const char **compatEnviron)
                           // IN: original "compatibility" environment
{
   HashTable *environTable;
   const char **nativeEnviron;

   environTable = SNEBuildHash(compatEnviron);
   nativeEnviron = SNEHashToEnviron(environTable);

   HashTable_Free(environTable);

   return nativeEnviron;
}


/*
 *-----------------------------------------------------------------------------
 *
 * System_FreeNativeEnviron --
 *
 *      Frees memory allocated by System_GetNativeEnviron.
 *
 * Results:
 *      Frees memory.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
System_FreeNativeEnviron(const char **nativeEnviron)
                            // IN: environment returned by System_GetNativeEnviron
{
   /*
    * nativeEnviron is an array of strings, and all of the strings are located in a
    * single contiguous buffer.  This makes freeing both array and buffer easy.
    */
   char *stringBuf = (char *)*nativeEnviron;

   free(stringBuf);
   free(nativeEnviron);
}


/*
 * Local functions
 */


/*
 *-----------------------------------------------------------------------------
 *
 * SNEBuildHash --
 *
 *      Compile hash table of environment variables.  See System_GetNativeEnviron
 *      for rules on precedence.
 *
 * Results:
 *      Pointer to populated hash table.  This cannot fail, as any memory failures
 *      within the HashTable and StrUtil libraries are considered fatal.
 *
 * Side effects:
 *      Caller is responsible for freeing returned table with HashTable_Free.
 *
 *-----------------------------------------------------------------------------
 */

static HashTable *
SNEBuildHash(const char **compatEnviron)
                // IN: original "compatibility" environment
{
   HashTable *environTable;
   const char **p;

   /*
    * Number of buckets picked arbitrarily.  We're more interested in having an
    * associative array than raw table performance.
    */
   environTable = HashTable_Alloc(64, HASH_STRING_KEY | HASH_FLAG_COPYKEY, free);

   for (p = compatEnviron; p && *p; p++) {
      const size_t prefixLength = sizeof "VMWARE_" - 1;
      char *key;
      char *value;
      unsigned int index;

      index = 0;
      key = StrUtil_GetNextToken(&index, *p, "=");
      if (!key) {
         /* XXX Must empty environment variables still contain a '=' delimiter? */
         Debug("%s: Encountered environment entry without '='.\n", __func__);
         continue;
      }

      /*
       * Copy the value beginning after the '=' delimiter (even if it's empty).
       */
      ++index;
      value = Util_SafeStrdup(&(*p)[index]);

      if (StrUtil_StartsWith(key, "VMWARE_") &&
          key[prefixLength] != '\0' &&
          (value[0] == '0' || value[0] == '1')) {
         /*
          * Okay, this appears to be one of the wrapper's variables, so let's
          * figure out the original environment variable name (by just indexing
          * past the prefix) and value (by indexing past the "was this variable
          * in the native environment?" marker).
          *
          * XXX Should we move this marker to a separate header?
          */
         char *realKey = &key[prefixLength];
         char *realValue = (value[0] == '0')
                           ? NULL
                           : Util_SafeStrdup(&value[1]);
         free(value);
         value = NULL;
         HashTable_ReplaceOrInsert(environTable, realKey, realValue);
      } else {
         void *hashed = HashTable_LookupOrInsert(environTable, key, value);
         if (hashed != value) {
            /*
             * The key already exists in the hashtable and its value was
             * not replaced. We need to free the memory allocated for 'value'.
             */
            free(value);
            value = NULL;
         }
      }

      /*
       * The hash table makes a copy of our key, and it takes ownership of inserted
       * values (via our passed freeing function).  So that means we're responsible
       * for freeing 'key', but -not- 'value'.
       */
      free(key);
   }

   return environTable;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SNEHashToEnviron --
 *
 *      Builds up an array of strings representing a new environment based on
 *      the caller's hash table.
 *
 * Results:
 *      Pointer to the new environment.  As memory allocation failures are
 *      considered fatal, this routine will not return NULL.
 *
 * Side effects:
 *      This is expected to be returned to System_GetNativeEnviron's caller,
 *      and s/he is to free it via System_FreeNativeEnviron.
 *
 *-----------------------------------------------------------------------------
 */

static const char **
SNEHashToEnviron(HashTable *environTable)   // IN:
{
   DynBuf nativeEnvironOffsets;
   DynBuf nativeEnvironStrings;

   SNEBufs anonBufs = {
      .nativeEnvironStrings = &nativeEnvironStrings,
      .nativeEnvironOffsets = &nativeEnvironOffsets,
   };

   const char **nativeEnviron;
   off_t *offsetIter;
   char *stringsBase;

   unsigned int numStrings;
   unsigned int i;

   DynBuf_Init(&nativeEnvironStrings);
   DynBuf_Init(&nativeEnvironOffsets);

   /*
    * Write out strings and string offsets to the dynbufs.
    */
   HashTable_ForEach(environTable, SNEForEachCallback, &anonBufs);
   ASSERT_MEM_ALLOC(DynBuf_Trim(&nativeEnvironStrings));

   /*
    * Allocate final buffer to contain the string array.  Include extra entry
    * for terminator.
    */
   numStrings = DynBuf_GetSize(&nativeEnvironOffsets) / sizeof *offsetIter;
   nativeEnviron = Util_SafeCalloc(1 + numStrings, sizeof *nativeEnviron);

   stringsBase = DynBuf_Get(&nativeEnvironStrings);
   offsetIter = DynBuf_Get(&nativeEnvironOffsets);

   for (i = 0; i < numStrings; i++, offsetIter++) {
      nativeEnviron[i] = stringsBase + *offsetIter;
   }
   nativeEnviron[i] = NULL;

   /*
    * Cleanup.
    *
    * Note the subtle difference in how these are handled:
    *   - The offsets are used only to build the array of strings, which is why that
    *     dynbuf is destroyed.
    *   - The buffer containing all of the environment strings, however, is persistent
    *     and is actually returned to the caller (indirectly via nativeEnviron).  As
    *     such it's only detached from the DynBuf.
    */
   DynBuf_Destroy(&nativeEnvironOffsets);
   DynBuf_Detach(&nativeEnvironStrings);

   return nativeEnviron;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SNEForEachCallback --
 *
 *      Given a key (environment variable name) and value, appends a string of
 *      "key=value" to the nativeEnvironStrings DynBuf.  Also appends a pointer
 *      to the new string to the nativeEnviron DynBuf.
 *
 * Results:
 *      Always zero.  Memory allocation failures are considered fatal.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
SNEForEachCallback(const char *key,     // IN: environment variable
                   void *value,         // IN: environment value
                   void *clientData)    // IN/OUT: DynBuf container (SNEBufs)
{
   DynBuf *nativeEnvironStrings = ((SNEBufs *)clientData)->nativeEnvironStrings;
   DynBuf *nativeEnvironOffsets = ((SNEBufs *)clientData)->nativeEnvironOffsets;
   size_t itemSize;
   char *itemBuf;
   off_t itemOffset;

   /*
    * A NULL value indicates that this variable is not to be set.
    */
   if (value == NULL) {
      return 0;
   }

   /* Determine the length of the new string inc. '=' delimiter and NUL. */
   itemSize = strlen(key) + strlen(value) + sizeof "=";
   itemBuf = Util_SafeMalloc(itemSize);

   /* Create new "key=value" string. */
   snprintf(itemBuf, itemSize, "%s=%s", key, (char *)value);

   ASSERT_MEM_ALLOC(DynBuf_AppendString(nativeEnvironStrings, itemBuf));

   /*
    * Get the relative offset of our newly added string (relative to the DynBuf's base
    * address), and then append that to nativeEnvironOffsets.
    */
   itemOffset = DynBuf_GetSize(nativeEnvironStrings) - itemSize;
   ASSERT_MEM_ALLOC(DynBuf_Append(nativeEnvironOffsets, &itemOffset, sizeof itemOffset));

   free(itemBuf);

   return 0;
}
 070701000001F2000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode    070701000001F3000081A4000000000000000000000001682255050000053A000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/Makefile.am    ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libUnicode.la

libUnicode_la_SOURCES =
libUnicode_la_SOURCES += unicodeCommon.c
libUnicode_la_SOURCES += unicodeSimpleBase.c
libUnicode_la_SOURCES += unicodeSimpleCaseFolding.c
libUnicode_la_SOURCES += unicodeSimpleTypes.c
libUnicode_la_SOURCES += unicodeSimpleOperations.c
libUnicode_la_SOURCES += unicodeSimpleTransforms.c
libUnicode_la_SOURCES += unicodeStatic.c

if HAVE_ICU
libUnicode_la_SOURCES += unicodeICU.c
endif
  070701000001F4000081A4000000000000000000000001682255050000268A000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeCommon.c    /*********************************************************
 * Copyright (C) 2007-2016,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeCommon.c --
 *
 *      Functions common to all implementations of lib/unicode.
 */

#include <stdlib.h>
#include <string.h>

#include "vmware.h"

#include "escape.h"
#include "vm_assert.h"
#include "unicodeBase.h"
#include "unicodeInt.h"
#include "unicodeTypes.h"


// Array of byte values we want Escape_Do() to escape when logging.
static const int NonPrintableBytesToEscape[256] = {
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Control characters.
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // More control characters.
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // Backslash
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // DEL
   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
};


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_EscapeBuffer --
 *
 *      Escape non-printable bytes of the buffer with \xAB, where 0xAB
 *      is the non-printable byte value.
 *
 * Results:
 *      The allocated, NUL-terminated, US-ASCII string containing the
 *      escaped buffer.  Caller must free the buffer.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_EscapeBuffer(const void *buffer,      // IN
                     ssize_t lengthInBytes,   // IN
                     StringEncoding encoding) // IN
{
   encoding = Unicode_ResolveEncoding(encoding);

   if (lengthInBytes == -1) {
      lengthInBytes = Unicode_LengthInBytes(buffer, encoding);
   }

   /*
    * The buffer could have NULs or 8-bit values inside.  Escape it.
    */
   return Escape_DoString("\\x",
                          NonPrintableBytesToEscape,
                          buffer,
                          lengthInBytes,
                          NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeSanityCheck --
 *
 *      Simple sanity checks on buffers of specified encodings.
 *
 * Results:
 *      TRUE if the buffer passed the sanity check for the specified encoding,
 *      FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
UnicodeSanityCheck(const void *buffer,      // IN
                   ssize_t lengthInBytes,   // IN
                   StringEncoding encoding) // IN
{
   ASSERT(Unicode_IsEncodingValid(encoding));

   /*
    * Sanity check US-ASCII here, so we can fast-path its conversion
    * to Unicode later.
    */

   if (encoding == STRING_ENCODING_US_ASCII) {
      const uint8 *asciiBytes = (const uint8 *) buffer;

      if (lengthInBytes == -1) {
	 for (; *asciiBytes != '\0'; asciiBytes++) {
	    if (*asciiBytes >= 0x80) {
	       return FALSE;
	    }
	 }
      } else {
	 ssize_t i;

	 for (i = 0; i < lengthInBytes; i++) {
	    if (asciiBytes[i] >= 0x80) {
	       return FALSE;
	    }
	 }
      }
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_LengthInBytes --
 *
 *      Compute the length in bytes of a string in a given encoding.
 *
 * Results:
 *      The number of bytes.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ssize_t
Unicode_LengthInBytes(const void *buffer,      // IN
                      StringEncoding encoding) // IN
{
   ssize_t len;

   encoding = Unicode_ResolveEncoding(encoding);

   switch (encoding) {
   case STRING_ENCODING_UTF32_LE:
   case STRING_ENCODING_UTF32_BE:
   case STRING_ENCODING_UTF32_XE:
   {
      const int32 *p;

      for (p = buffer; *p != 0; p++) {
      }
      len = (const char *) p - (const char *) buffer;
      break;
   }
   case STRING_ENCODING_UTF16_LE:
   case STRING_ENCODING_UTF16_BE:
   case STRING_ENCODING_UTF16_XE:
   {
      const utf16_t *p;

      for (p = buffer; *p != 0; p++) {
      }
      len = (const char *) p - (const char *) buffer;
      break;
   }
   default:
      // XXX assume 8-bit encoding with no embedded null
      len = strlen(buffer);
   }

   return len;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_UTF16Strlen --
 *
 *      Gets the number of code units in NUL-terminated UTF-16 array.
 *
 * Results:
 *      The number of code units in the array.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

ssize_t
Unicode_UTF16Strlen(const utf16_t *utf16) // IN
{
   ssize_t length;

   for (length = 0; utf16[length]; length++) {
      // Count the number of code units until we hit NUL.
   }

   return length;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_UTF16Strdup --
 *
 *      Duplicates a UTF-16 string.
 *
 * Results:
 *      Returns an allocated copy of the input UTF-16 string.  The caller
 *      is responsible for freeing it.
 *
 *      Panics on failure.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

utf16_t *
Unicode_UTF16Strdup(const utf16_t *utf16) // IN: May be NULL.
{
   utf16_t *copy;
   ssize_t numBytes;

   // Follow Util_SafeStrdup semantics.
   if (utf16 == NULL) {
      return NULL;
   }

   numBytes = (Unicode_UTF16Strlen(utf16) + 1 /* NUL */) * sizeof *copy;
   copy = Util_SafeMalloc(numBytes);
   memcpy(copy, utf16, numBytes);

   return copy;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_AllocWithLength --
 *
 *      Allocates a new Unicode string given a buffer with both length
 *      in bytes and string encoding specified.
 *
 *      If lengthInBytes is -1, then buffer must be NUL-terminated.
 *      Otherwise, buffer must be of the specified length, but does
 *      not need to be NUL-terminated.
 *
 *	If buffer is NULL, then NULL is returned.
 *	In this case, lengthInBytes must be 0 or -1, consistent with
 *	an empty string.
 *
 *      Note that regardless of the encoding of the buffer passed to this
 *      function, the returned string can hold any Unicode characters.
 *
 *      If the buffer contains an invalid sequence of the specified
 *      encoding or memory could not be allocated, logs the buffer,
 *      and panics.
 *
 * Results:
 *      An allocated Unicode string containing the decoded characters
 *      in buffer, or NULL if input is NULL.
 *	Caller must pass the string to free to free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_AllocWithLength(const void *buffer,       // IN:
                        ssize_t lengthInBytes,    // IN:
                        StringEncoding encoding)  // IN:
{
   char *result;

   ASSERT(lengthInBytes >= 0 || lengthInBytes == -1);

   if (buffer == NULL) {
      ASSERT(lengthInBytes <= 0);
      return NULL;
   }

   encoding = Unicode_ResolveEncoding(encoding);

   if (lengthInBytes == -1) {
      lengthInBytes = Unicode_LengthInBytes(buffer, encoding);
   }

   result = UnicodeAllocInternal(buffer, lengthInBytes, encoding, FALSE);

   if (result == NULL) {
      char *escapedBuffer = Unicode_EscapeBuffer(buffer, lengthInBytes,
                                                 encoding);

      Panic("%s: Couldn't convert invalid buffer [%s] from %s to Unicode.\n",
            __FUNCTION__,
            escapedBuffer ? escapedBuffer : "(couldn't escape bytes)",
            Unicode_EncodingEnumToName(encoding));
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_CanGetBytesWithEncoding --
 *
 *      Tests if the given Unicode string can be converted
 *      losslessly to the specified encoding.
 *
 * Results:
 *      TRUE if the string can be converted, FALSE if it cannot.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Unicode_CanGetBytesWithEncoding(const char *ustr,         // IN:
                                StringEncoding encoding)  // IN:
{
   void *tmp;

   if (ustr == NULL) {
      return TRUE;
   }

   tmp = UnicodeGetAllocBytesInternal(ustr, encoding, -1, NULL);

   if (tmp == NULL) {
      return FALSE;
   }
   free(tmp);

   return TRUE;
}
  070701000001F5000081A4000000000000000000000001682255050000362F000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeICU.c   /*********************************************************
 * Copyright (C) 2008-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeICU.c --
 *
 *      Unicode functionality that depends on the third-party ICU
 *      library.
 */

#include <unicode/ucasemap.h>
#include <unicode/ucol.h>
#include <unicode/uiter.h>
#include <unicode/ustring.h>
#include <unicode/unorm.h>

#include "util.h"
#include "unicodeBase.h"
#include "unicodeICU.h"


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_CompareWithLocale --
 *
 *      Compares two strings for equivalence under the collation rules
 *      of the specified locale.
 *
 *      The caller can specify ignoring differences in accents, case,
 *      or punctuation.
 *
 * Results:
 *      -1 if str1 < str2, 0 if str1 == str2, 1 if str1 > str2.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
Unicode_CompareWithLocale(const char *str1,                   // IN
                          const char *str2,                   // IN
                          const char *locale,                 // IN
                          UnicodeCompareOption compareOption) // IN
{
   UCollationResult compareResult;
   UColAttributeValue comparisonStrength;
   UErrorCode status = U_ZERO_ERROR;
   int result;
   UCollator *coll;
   UCharIterator str1Iter;
   UCharIterator str2Iter;

   uiter_setUTF8(&str1Iter, (const char *)str1, -1);
   uiter_setUTF8(&str2Iter, (const char *)str2, -1);

   switch (compareOption) {
   case UNICODE_COMPARE_DEFAULT:
      comparisonStrength = UCOL_DEFAULT;
      break;
   case UNICODE_COMPARE_IGNORE_ACCENTS:
      comparisonStrength = UCOL_PRIMARY;
      break;
   case UNICODE_COMPARE_IGNORE_CASE:
      comparisonStrength = UCOL_SECONDARY;
      break;
   case UNICODE_COMPARE_IGNORE_PUNCTUATION:
      comparisonStrength = UCOL_TERTIARY;
      break;
   default:
      NOT_IMPLEMENTED();
   }

   coll = ucol_open(locale, &status);

   ASSERT(U_SUCCESS(status));
   ASSERT(coll);

   if (U_FAILURE(status) || !coll) {
      return -1;
   }

   // Normalize all strings to NFD before comparing.
   ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
   ucol_setAttribute(coll, UCOL_STRENGTH, comparisonStrength, &status);

   ASSERT(U_SUCCESS(status));

   compareResult = ucol_strcollIter(coll, &str1Iter, &str2Iter, &status);

   ucol_close(coll);

   if (U_FAILURE(status)) {
      // We'll probably only get here if the input wasn't UTF-8.
      ASSERT(U_SUCCESS(status));
      return -1;
   }

   switch (compareResult) {
   case UCOL_LESS:
      result = -1;
      break;
   case UCOL_EQUAL:
      result = 0;
      break;
   case UCOL_GREATER:
      result = 1;
      break;
   default:
      NOT_IMPLEMENTED();
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Normalize --
 *
 *      Creates a Unicode string by normalizing the input string
 *      into a Unicode normal form.
 *
 *      Normalization Form C ("precomposed") ensures that accented
 *      characters use as few Unicode code points as possible.  This
 *      form is often used on Windows and Linux hosts.
 *
 *      Example: small letter e with acute -> U+00E9
 *
 *      Normalization Form D ("decomposed") ensures that accented
 *      characters (e with accent acute) use separate Unicode code
 *      points for the base letter and accents.  This form is used on
 *      Mac OS hosts.
 *
 *      Example: small letter e with acute -> U+0065 U+0301
 *
 * Results:
 *      The allocated Unicode string, or NULL on failure.
 *      Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_Normalize(const char *str,               // IN
                  UnicodeNormalizationForm form) // IN
{
   UNormalizationMode mode;
   UChar *uchars;
   char *result;
   int32_t normalizedLen;
   UErrorCode status = U_ZERO_ERROR;
   UCharIterator strIter;
   UBool neededToNormalize = FALSE;

   uiter_setUTF8(&strIter, (const char *)str, -1);

   switch (form) {
   case UNICODE_NORMAL_FORM_C:
      mode = UNORM_NFC;
      break;
   case UNICODE_NORMAL_FORM_D:
      mode = UNORM_NFD;
      break;
   default:
      NOT_REACHED();
   }

   normalizedLen = unorm_next(&strIter,
                              NULL,
                              0,
                              mode,
                              0,
                              TRUE,
                              &neededToNormalize,
                              &status);

   if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
      // We expect U_BUFFER_OVERFLOW_ERROR here. Anything else is a problem.
      ASSERT(U_SUCCESS(status));
      return NULL;
   }

   uchars = Util_SafeMalloc(sizeof *uchars * normalizedLen);

   // Reset back to the beginning of the UTF-8 input.
   (*strIter.move)(&strIter, 0, UITER_START);

   status = U_ZERO_ERROR;
   normalizedLen = unorm_next(&strIter,
                              uchars,
                              normalizedLen,
                              mode,
                              0,
                              TRUE,
                              &neededToNormalize,
                              &status);

   if (U_FAILURE(status)) {
      ASSERT(U_SUCCESS(status));
      return NULL;
   }

   result = Unicode_AllocWithLength(uchars,
                                    normalizedLen * 2,
                                    STRING_ENCODING_UTF16);
   free(uchars);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_ToLower --
 *
 *      Creates a Unicode string by lower-casing the input string using
 *      the rules of the specified locale.
 *
 *      The resulting string may not be the same length as the input
 *      string.
 *
 *      Pass NULL for the locale to use the process's default locale.
 *
 * Results:
 *      The allocated Unicode string, or NULL on failure.
 *      Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_ToLower(const char *str,    // IN
                const char *locale) // IN
{
   UCaseMap *caseMap;
   UErrorCode status = U_ZERO_ERROR;
   char *utf8Dest;
   const char *utf8Src = (const char *)str;
   int32_t utf8SrcLen = strlen(utf8Src);
   int32_t destCapacity = utf8SrcLen + 1;
   int32_t destLen;
   char *result = NULL;

   /*
    * XXX TODO: This and the two following functions are substantially
    * identical.  Refactor them!  (Note that ucasemap_utf8ToTitle
    * takes a non-const UCaseMap, so we can't just use pointers to
    * functions unless we cast.)
    */

   // Most lower-case operations don't change the length of the string.
   utf8Dest = Util_SafeMalloc(destCapacity);

   caseMap = ucasemap_open(locale, 0, &status);
   if (U_FAILURE(status)) {
      goto out;
   }

   destLen = ucasemap_utf8ToLower(caseMap,
                                  utf8Dest,
                                  destCapacity,
                                  utf8Src,
                                  utf8SrcLen,
                                  &status);

   if (status != U_BUFFER_OVERFLOW_ERROR) {
      goto out;
   }

   // If we need a bigger buffer, then reallocate and retry.
   destCapacity = destLen + 1;
   utf8Dest = Util_SafeRealloc(utf8Dest, destCapacity);

   status = U_ZERO_ERROR;
   destLen = ucasemap_utf8ToLower(caseMap,
                                  utf8Dest,
                                  destCapacity,
                                  utf8Src,
                                  utf8SrcLen,
                                  &status);

  out:
   ucasemap_close(caseMap);

   if (U_SUCCESS(status) && status != U_STRING_NOT_TERMINATED_WARNING) {
      result = utf8Dest;
   } else {
      DEBUG_ONLY(Warning("%s: Invalid UTF-8 string detected.\n",
                         __FUNCTION__));
      free(utf8Dest);
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_ToUpper --
 *
 *      Creates a Unicode string by upper-casing the input string using
 *      the rules of the specified locale.
 *
 *      The resulting string may not be the same length as the input
 *      string.
 *
 *      Pass NULL for the locale to use the process's default locale.
 *
 * Results:
 *      The allocated Unicode string, or NULL on failure.
 *      Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_ToUpper(const char *str,    // IN
                const char *locale) // IN
{
   UCaseMap *caseMap;
   UErrorCode status = U_ZERO_ERROR;
   char *utf8Dest;
   const char *utf8Src = (const char *)str;
   int32_t utf8SrcLen = strlen(utf8Src);
   int32_t destCapacity = utf8SrcLen + 1;
   int32_t destLen;
   char *result = NULL;

   // Most upper-case operations don't change the length of the string.
   utf8Dest = Util_SafeMalloc(destCapacity);

   caseMap = ucasemap_open(locale, 0, &status);
   if (U_FAILURE(status)) {
      goto out;
   }

   destLen = ucasemap_utf8ToUpper(caseMap,
                                  utf8Dest,
                                  destCapacity,
                                  utf8Src,
                                  utf8SrcLen,
                                  &status);

   if (status != U_BUFFER_OVERFLOW_ERROR) {
      goto out;
   }

   // If we need a bigger buffer, then reallocate and retry.
   destCapacity = destLen + 1;
   utf8Dest = Util_SafeRealloc(utf8Dest, destCapacity);

   status = U_ZERO_ERROR;
   destLen = ucasemap_utf8ToUpper(caseMap,
                                  utf8Dest,
                                  destCapacity,
                                  utf8Src,
                                  utf8SrcLen,
                                  &status);

  out:
   ucasemap_close(caseMap);

   if (U_SUCCESS(status) && status != U_STRING_NOT_TERMINATED_WARNING) {
      result = utf8Dest;
   } else {
      DEBUG_ONLY(Warning("%s: Invalid UTF-8 string detected.\n",
                         __FUNCTION__));
      free(utf8Dest);
   }

   return result;
}


/*
 * "ucasemap_utf8ToTitle" is not in version 3.6 of the ICU library,
 * which appears to be the default on many systems...
 *
 * XXX Currently HAVE_ICU_38 is only set by the open-source tools
 * build (based on what it detects on the current system) because that
 * is the only entity currently capable of compiling with USE_ICU.
 */

#ifdef HAVE_ICU_38

/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_ToTitle --
 *
 *      Creates a Unicode string by title-casing the input string using
 *      the rules of the specified locale.
 *
 *      The resulting string may not be the same length as the input
 *      string.
 *
 *      Pass NULL for the locale to use the process's default locale.
 *
 * Results:
 *      The allocated Unicode string, or NULL on failure.
 *      Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_ToTitle(const char *str,    // IN
                const char *locale) // IN
{
   UCaseMap *caseMap;
   UErrorCode status = U_ZERO_ERROR;
   char *utf8Dest;
   const char *utf8Src = (const char *)str;
   int32_t utf8SrcLen = strlen(utf8Src);
   int32_t destCapacity = utf8SrcLen + 1;
   int32_t destLen;
   char *result = NULL;

   // Most title-case operations don't change the length of the string.
   utf8Dest = Util_SafeMalloc(destCapacity);

   caseMap = ucasemap_open(locale, 0, &status);
   if (U_FAILURE(status)) {
      goto out;
   }

   destLen = ucasemap_utf8ToTitle(caseMap,
                                  utf8Dest,
                                  destCapacity,
                                  utf8Src,
                                  utf8SrcLen,
                                  &status);

   if (status != U_BUFFER_OVERFLOW_ERROR) {
      goto out;
   }

   // If we need a bigger buffer, then reallocate and retry.
   destCapacity = destLen + 1;
   utf8Dest = Util_SafeRealloc(utf8Dest, destCapacity);

   status = U_ZERO_ERROR;
   destLen = ucasemap_utf8ToTitle(caseMap,
                                  utf8Dest,
                                  destCapacity,
                                  utf8Src,
                                  utf8SrcLen,
                                  &status);

  out:
   ucasemap_close(caseMap);

   if (U_SUCCESS(status) && status != U_STRING_NOT_TERMINATED_WARNING) {
      result = utf8Dest;
   } else {
      DEBUG_ONLY(Warning("%s: Invalid UTF-8 string detected.\n",
                         __FUNCTION__));
      free(utf8Dest);
   }

   return result;
}

#endif // HAVE_ICU_38
 070701000001F6000081A4000000000000000000000001682255050000074B000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeInt.h   /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeInt.h --
 *
 *      Internal functions common to all implementations of the
 *      Unicode library.
 */

#ifndef _UNICODE_INT_H
#define _UNICODE_INT_H

#include "unicodeTypes.h"

#ifdef __cplusplus
extern "C" {
#endif

char *UnicodeAllocInternal(const void *buffer,
                           ssize_t lengthInBytes,
                           StringEncoding encoding,
                           Bool strict);

char *UnicodeAllocStatic(const char *asciiBytes,
                         Bool unescape);

utf16_t UnicodeSimpleCaseFold(utf16_t codeUnit);

void *UnicodeGetAllocBytesInternal(const char *src,
                                   StringEncoding encoding,
                                   ssize_t lengthInBytes,
                                   size_t *retLength);

Bool UnicodeSanityCheck(const void *buffer,
                        ssize_t lengthInBytes,
                        StringEncoding encoding);

#ifdef __cplusplus
}
#endif

#endif // _UNICODE_INT_H
 070701000001F7000081A40000000000000000000000016822550500006435000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeSimpleBase.c    /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeSimpleBase.cc --
 *
 *      Simple implementation of unicodeBase.h interface using char *
 *      containing NUL-terminated UTF-8 bytes as the typedef for
 *      Unicode.
 *
 *      Basic Unicode string creation and encoding conversion.
 *
 *      The thread-safety of const char *functions is the same as
 *      that for standard const char * functions: multiple threads can
 *      call const char *functions on the same string simultaneously.
 *
 *      However, a non-const Unicode function (like free) must
 *      not be called concurrently with any other Unicode or
 *      const char *function on the same string.
 */

#include <string.h>

#include "vmware.h"
#include "util.h"
#include "codeset.h"
#include "str.h"
#include "unicodeBase.h"
#include "unicodeInt.h"


/*
 * Padding for initial and final bytes used by an encoding.  The value
 * comes from ICU's UCNV_GET_MAX_BYTES_FOR_STRING macro and accounts
 * for leading and trailing bytes and NUL.
 */

static const size_t UNICODE_UTF16_CODE_UNITS_PADDING = 10;


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeAllocInternal --
 *
 *      Allocates a new Unicode string given a buffer with both length
 *      in bytes and string encoding specified.
 *
 *      If lengthInBytes is -1, then buffer must be NUL-terminated.
 *      Otherwise, buffer must be of the specified length, but does
 *      not need to be NUL-terminated.
 *
 *      Return NULL on memory allocation failure.
 *
 *      Return NULL if strict is true and the buffer contains an invalid
 *      sequence in the specified encoding.
 *
 *      If strict is false, then an invalid sequence is replaced by
 *      a substitution character, which is either the Unicode
 *      substitution character (U+FFFD or \xef\xbf\xbd in UTF-8)
 *      or subchar1 (ASCII SUB or control-z, value 0x1a).
 *
 * Results:
 *      An allocated Unicode string containing the decoded characters
 *      in buffer, or NULL on failure.  Caller must pass to
 *      free to free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
UnicodeAllocInternal(const void *buffer,      // IN
                     ssize_t lengthInBytes,   // IN
                     StringEncoding encoding, // IN
                     Bool strict)             // IN
{
   char *utf8Result = NULL;

   ASSERT(buffer != NULL);
   ASSERT(lengthInBytes >= 0);
   ASSERT(Unicode_IsEncodingValid(encoding));

   if (!strict) {
      CodeSet_GenericToGeneric(Unicode_EncodingEnumToName(encoding),
                               buffer, lengthInBytes,
                               "UTF-8", CSGTG_TRANSLIT, &utf8Result, NULL);
      return utf8Result;
   }

   switch (encoding) {
   case STRING_ENCODING_US_ASCII:
   case STRING_ENCODING_UTF8:
      if (Unicode_IsBufferValid(buffer, lengthInBytes, encoding)) {
         utf8Result = Util_SafeStrndup(buffer, lengthInBytes);
      }
      break;
   case STRING_ENCODING_UTF16_LE:
      // utf8Result will be left NULL on failure.
      CodeSet_Utf16leToUtf8((const char *)buffer,
                            lengthInBytes,
                            &utf8Result,
                            NULL);
      break;
   default:
      CodeSet_GenericToGeneric(Unicode_EncodingEnumToName(encoding),
                               buffer, lengthInBytes,
                               "UTF-8", 0, &utf8Result, NULL);
      break;
   }

   return utf8Result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_IsStringValidUTF8 --
 *
 *      Tests if the specified string (NUL terminated) is valid UTF8.
 *
 * Results:
 *      TRUE   The string is valid UTF8.
 *      FALSE  The string is not valid UTF8.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Unicode_IsStringValidUTF8(const char *str)  // IN:
{
   return CodeSet_IsStringValidUTF8(str);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_IsBufferValid --
 *
 *      Tests if the given buffer is valid in the specified encoding.
 *
 *      If lengthInBytes is -1, then buffer must be NUL-terminated.
 *      Otherwise, buffer must be of the specified length, but does
 *      not need to be NUL-terminated.
 *
 *      This function should not be used for heuristic determination of
 *      encodings.  Since the test looks for bit patterns in the buffer
 *      that are invalid in the specified encoding, negative results
 *      guarantee the buffer is not in the specified encoding, but positive
 *      results are inconclusive.  Source buffers containing pure ASCII
 *      will pass all 8-bit encodings, and all source buffers will pass 
 *      a windows-1252 test since win-1252 maps all 256 8-bit combinations.
 *
 * Results:
 *      TRUE if the buffer is valid, FALSE if it's not.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Unicode_IsBufferValid(const void *buffer,      // IN
                      ssize_t lengthInBytes,   // IN
                      StringEncoding encoding) // IN
{
   if (buffer == NULL) {
      ASSERT(lengthInBytes <= 0);
      return TRUE;
   }

   encoding = Unicode_ResolveEncoding(encoding);

   switch (encoding) {
   case STRING_ENCODING_US_ASCII:
      return UnicodeSanityCheck(buffer, lengthInBytes, encoding);

   case STRING_ENCODING_UTF8:
      if (lengthInBytes == -1) {
         return CodeSet_IsStringValidUTF8(buffer);
      } else {
         return CodeSet_IsValidUTF8(buffer, lengthInBytes);
      }

   default:
      if (lengthInBytes == -1) {
         lengthInBytes = Unicode_LengthInBytes(buffer, encoding);
      }

      return CodeSet_Validate(buffer, lengthInBytes,
                              Unicode_EncodingEnumToName(encoding));
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Duplicate --
 *
 *      Allocates and returns a copy of the passed-in Unicode string.
 *
 * Results:
 *      An allocated Unicode string containing a duplicate of the passed-in
 *      string.  Caller must pass to free to free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_Duplicate(const char *str) // IN
{
   return Util_SafeStrdup(str);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_AllocList --
 *
 *      Allocates a list (actually a vector) of Unicode strings from a list
 *      (vector) of strings of specified encoding.
 *      The input list has a specified length or can be an argv-style
 *      NULL-terminated list (if length is negative).
 *
 * Results:
 *      An allocated list (vector) of Unicode strings.
 *      The individual strings must be freed with free,
 *      or the entire list can be free with Util_FreeStringList.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char **
Unicode_AllocList(char **srcList,          // IN: list of strings
                  ssize_t length,          // IN: list 
                  StringEncoding encoding) // IN:
{
   char **dstList = NULL;
   ssize_t i;

   ASSERT(srcList != NULL);

   encoding = Unicode_ResolveEncoding(encoding);

   if (length < 0) {
      length = 0;
      while (srcList[length] != NULL) {
         length++;
      }
      
      /* Include the sentinel element. */
      length++;
   }

   dstList = Util_SafeMalloc(length * sizeof *dstList);

   for (i = 0; i < length; i++) {
      dstList[i] = Unicode_Alloc(srcList[i], encoding);
   }

   return dstList;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_GetAllocList --
 *
 *      Given a list of Unicode strings, converts them to a list of
 *      buffers in the specified encoding.
 *
 *      The input list has a specified length or can be an argv-style
 *      NULL-terminated list (if length is negative).
 *
 * Results:
 *      An allocated list (vector) of NUL terminated buffers in the specified
 *      encoding
 *      or NULL on conversion failure.
 *      The caller is responsible to free the memory allocated by
 *      this routine.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char **
Unicode_GetAllocList(char *const srcList[],   // IN: list of strings
                     ssize_t length,          // IN: length (-1 for NULL term.)
                     StringEncoding encoding) // IN: Encoding of returned list
{
   char **dstList = NULL;
   ssize_t i;

   ASSERT(srcList != NULL);

   encoding = Unicode_ResolveEncoding(encoding);

   if (length < 0) {
      length = 0;
      while (srcList[length] != NULL) {
         length++;
      }

      /* Include the sentinel element. */
      length++;
   }

   dstList = Util_SafeMalloc(length * sizeof *dstList);

   for (i = 0; i < length; i++) {
      dstList[i] = Unicode_GetAllocBytes(srcList[i], encoding);
      if (dstList[i] == NULL && srcList[i] != NULL) {
         while (--i >= 0) {
            free(dstList[i]);
         }
         free(dstList);
         return NULL;
      }
   }

   return dstList;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_LengthInCodeUnits --
 *
 *      Gets the length of the Unicode string in UTF-8 code units.
 *
 * Results:
 *      The length of the string in UTF-8 code units.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

UnicodeIndex
Unicode_LengthInCodeUnits(const char *str) // IN
{
   return strlen((const char *)str);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_BytesRequired --
 *
 *      Gets the number of bytes needed to encode the Unicode string in
 *      the specified encoding, including NUL-termination.
 *
 *      Use this to find the size required for the byte array passed
 *      to Unicode_CopyBytes.
 *
 * Results:
 *      The number of bytes needed to encode the Unicode string in the
 *      specified encoding.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

size_t
Unicode_BytesRequired(const char *str,         // IN
                      StringEncoding encoding) // IN
{
   const uint8 *utf8 = (const uint8 *)str;

   // Number of bytes needed for a code point [U+0000, U+FFFF].
   size_t basicCodePointSize;

   // Number of bytes needed for a code point [U+10000, U+10FFFF].
   size_t supplementaryCodePointSize;

   size_t result = 0;

   encoding = Unicode_ResolveEncoding(encoding);

   switch (encoding) {
   case STRING_ENCODING_UTF8:
      return strlen((const char *)utf8) + 1;
   case STRING_ENCODING_US_ASCII:
   case STRING_ENCODING_ISO_8859_1:
   case STRING_ENCODING_WINDOWS_1252:
      // TODO: Lots more encodings can be added here.
      basicCodePointSize = supplementaryCodePointSize = 1;
      break;
   case STRING_ENCODING_UTF16_LE:
   case STRING_ENCODING_UTF16_BE:
   case STRING_ENCODING_UTF16_XE:
      basicCodePointSize = 2;
      supplementaryCodePointSize = 4;
      break;
   case STRING_ENCODING_UTF32_LE:
   case STRING_ENCODING_UTF32_BE:
   case STRING_ENCODING_UTF32_XE:
      basicCodePointSize = 4;
      supplementaryCodePointSize = 4;
      break;
   default:
      /*
       * Assume the worst: ISO-2022-JP takes up to 7 bytes per code point.
       */
      basicCodePointSize = 7;
      supplementaryCodePointSize = 7;
      break;
   }

   /*
    * Do a simple check of how many bytes are needed to convert the
    * UTF-8 to the target encoding.  This doesn't do UTF-8 validity
    * checking, but will not overrun the end of the buffer.
    */
   while (*utf8) {
      size_t utf8NumBytesRemaining;

      // Advance one code point forward in the UTF-8 input.
      if (*utf8 <= 0x7F) {
         utf8NumBytesRemaining = 1;
         result += basicCodePointSize;
      } else if (*utf8 & 0xC0) {
         utf8NumBytesRemaining = 2;
         result += basicCodePointSize;
      } else if (*utf8 & 0xE0) {
         utf8NumBytesRemaining = 3;
         result += basicCodePointSize;
      } else if (*utf8 & 0xF0) {
         utf8NumBytesRemaining = 4;
         result += supplementaryCodePointSize;
      } else {
         // Invalid input; nothing we can do.
         break;
      }

      while (*utf8 && utf8NumBytesRemaining) {
         utf8NumBytesRemaining--;
         utf8++;
      }

      if (utf8NumBytesRemaining > 0) {
         // Invalid input; nothing we can do.
         break;
      }
   }

   // Add enough for NUL expressed in the target encoding.
   result += UNICODE_UTF16_CODE_UNITS_PADDING * basicCodePointSize;

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_CopyBytes --
 *
 *      Encodes the Unicode string using the specified encoding into
 *      the specified buffer and NUL-terminates it, writing at most
 *      maxLengthInBytes bytes in total to the buffer.
 *
 * Results:
 *      FALSE on conversion failure or if the Unicode string requires
 *      more than maxLengthInBytes bytes to be encoded in the specified
 *      encoding, including NUL termination. (Call
 *      Unicode_BytesRequired(str, encoding) to get the correct
 *      length.). Returns TRUE if no truncation was required. In
 *      either case, if retLength is not NULL, *retLength contains the
 *      number of bytes actually written to the buffer upon return.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
Unicode_CopyBytes(void *destBuffer,        // OUT
                  const char *srcBuffer,   // IN
                  size_t maxLengthInBytes, // IN
                  size_t *retLength,       // OUT
                  StringEncoding encoding) // IN
{
   const char *utf8Str = (const char *)srcBuffer;
   Bool success = FALSE;
   size_t copyBytes = 0;

   encoding = Unicode_ResolveEncoding(encoding);

   switch (encoding) {
   case STRING_ENCODING_US_ASCII:
      if (!UnicodeSanityCheck(utf8Str, -1, encoding)) {
         break;
      }
      // fall through
   case STRING_ENCODING_UTF8:
      {
         size_t len = strlen(utf8Str);
         copyBytes = MIN(len, maxLengthInBytes - 1);
         memcpy(destBuffer, utf8Str, copyBytes);

         /*
          * If we truncated, force a null termination in a UTF-8 safe
          * manner.
          */
         if (copyBytes >= len) {
            success = TRUE;
         } else {
            if (encoding == STRING_ENCODING_UTF8) {
               copyBytes =
                  CodeSet_Utf8FindCodePointBoundary(destBuffer, copyBytes);
            }
         }

         ((char*)destBuffer)[copyBytes] = '\0';
      }
      break;
   case STRING_ENCODING_UTF16_LE:
      {
         char *utf16Buf;
         size_t utf16BufLen;

         if (!CodeSet_Utf8ToUtf16le(utf8Str,
                                    strlen(utf8Str),
                                    &utf16Buf,
                                    &utf16BufLen)) {
            /* input should be valid UTF-8, no conversion error possible */
            NOT_IMPLEMENTED();
            break;
         }
         copyBytes = MIN(utf16BufLen, maxLengthInBytes - 2);
         memcpy(destBuffer, utf16Buf, copyBytes);
         copyBytes = CodeSet_Utf16FindCodePointBoundary(destBuffer, copyBytes);
         ((utf16_t*)destBuffer)[copyBytes / 2] = 0;
         free(utf16Buf);

         if (copyBytes >= utf16BufLen) {
            success = TRUE;
         }

         break;
      }
   default:
      {
         char *currentBuf;
         size_t currentBufSize;

         if (!CodeSet_GenericToGeneric("UTF-8", utf8Str, strlen(utf8Str),
                                       Unicode_EncodingEnumToName(encoding),
                                       CSGTG_NORMAL,
                                       &currentBuf, &currentBufSize)) {
            /* XXX can't distinguish error cause */
            break;
         }
         copyBytes = MIN(currentBufSize, maxLengthInBytes - 1);
         memcpy(destBuffer, currentBuf, copyBytes);
         free(currentBuf);

         /* 
          * XXX this isn't quite correct, we still need to truncate on
          * a code point boundary, based on the current encoding type,
          * rather than just null terminate blindly.
          */

         ((char*)destBuffer)[copyBytes] = 0;

         if (copyBytes >= currentBufSize) {
            success = TRUE;
         }
      }
      break;
   }

   if (retLength) {
      *retLength = copyBytes;
   }
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_GetAllocBytes --
 *
 *      Allocates and returns a NUL terminated buffer into which the contents
 *      of the unicode string are extracted using the specified encoding.
 *
 *      NOTE: The length of the NUL can depend on the encoding.
 *            UTF-16 NUL is "\0\0"; UTF-32 NUL is "\0\0\0\0".
 *
 *      NULL is returned for NULL argument.
 *
 * Results:
 *      NULL if argument is NULL.
 *      Otherwise, pointer to the dynamically allocated memory
 *      or NULL on conversion failure.
 *      The caller is responsible to free the memory allocated
 *      by this routine.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void *
Unicode_GetAllocBytes(const char *str,         // IN:
                      StringEncoding encoding) // IN:
{
   if (str == NULL) {
      return NULL;
   }

   return UnicodeGetAllocBytesInternal(str, encoding, -1, NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_GetAllocBytesWithLength --
 *
 *      Allocates and returns a buffer into which the contents of the unicode
 *      string of the specified length are extracted using the specified
 *      encoding.
 *
 *      NOTE: The buffer returned is always NUL terminated.  The length of
 *            the NUL can depend on the encoding.  UTF-16 NUL is "\0\0";
 *            UTF-32 NUL is "\0\0\0\0".
 *
 *      NULL is returned for NULL argument.
 *
 * Results:
 *      NULL if argument is NULL.
 *      Otherwise, pointer to the dynamically allocated memory
 *      or NULL on conversion failure.
 *      The caller is responsible to free the memory allocated
 *      by this routine.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void *
Unicode_GetAllocBytesWithLength(const char *str,         // IN:
                                StringEncoding encoding, // IN:
                                ssize_t lengthInBytes)   // IN:
{
   if (str == NULL) {
      return NULL;
   }
   ASSERT(lengthInBytes >= 0);

   return UnicodeGetAllocBytesInternal(str, encoding, lengthInBytes, NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeGetAllocBytesInternal --
 *
 *      Encodes the Unicode string using the specified encoding into
 *      an allocated, null-terminated buffer.
 *
 * Results:
 *      The converted string in an allocated buffer,
 *      or NULL on conversion failure.
 *
 *      The length of the result (in bytes, without termination) in retLength.
 *
 * Side effects:
 *      Panic on memory allocation failure.
 *
 *-----------------------------------------------------------------------------
 */

void *
UnicodeGetAllocBytesInternal(const char *ustr,        // IN
                             StringEncoding encoding, // IN
                             ssize_t lengthInBytes,   // IN
                             size_t *retLength)       // OUT: optional
{
   const char *utf8Str = ustr;
   char *result = NULL;

   ASSERT(ustr != NULL);

   encoding = Unicode_ResolveEncoding(encoding);

   if (lengthInBytes == -1) {
      lengthInBytes = Unicode_LengthInBytes(ustr, STRING_ENCODING_UTF8);
   }

   switch (encoding) {
   case STRING_ENCODING_US_ASCII:
      if (!UnicodeSanityCheck(utf8Str, lengthInBytes, encoding)) {
         break;
      }
      // fall through
   case STRING_ENCODING_UTF8:
      result = Util_SafeMalloc(lengthInBytes + 1);
      memcpy(result, utf8Str, lengthInBytes + 1);
      if (retLength != NULL) {
         *retLength = lengthInBytes;
      }
      break;

   case STRING_ENCODING_UTF16_LE:
      if (!CodeSet_Utf8ToUtf16le(utf8Str, lengthInBytes, &result, retLength)) {
         /* input should be valid UTF-8, no conversion error possible */
         NOT_IMPLEMENTED();
      }
      break;

   default:
      if (!CodeSet_GenericToGeneric("UTF-8", utf8Str, lengthInBytes,
                                    Unicode_EncodingEnumToName(encoding),
                                    CSGTG_NORMAL,
                                    &result, retLength)) {
         /* XXX can't distinguish error cause */
         ASSERT(result == NULL);
      }
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeAllocStatic --
 *
 *      Internal helper function to allocate a new Unicode string
 *      given an array of bytes in US-ASCII encoding.
 *
 *      If 'unescape' is TRUE, unescapes \\uABCD to U+ABCD, and
 *      \\U001FABCD to U+1FABCD.
 *
 * Results:
 *      The allocated Unicode string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
UnicodeAllocStatic(const char *asciiBytes, // IN
                   Bool unescape)          // IN
{
   utf16_t *utf16;
   // Explicitly use int8 so we don't depend on whether char is signed.
   const int8 *byte = (const int8 *)asciiBytes;
   size_t utf16Offset = 0;
   char *result;

   ASSERT(asciiBytes);

   if (!unescape) {
      DEBUG_ONLY(
         while (*byte > 0) {
            byte++;
         }
         // All input must be 7-bit US-ASCII.
         ASSERT(*byte == 0);
      );
      return Util_SafeStrdup(asciiBytes);
   }

   utf16 = Util_SafeMalloc(sizeof *utf16 * (strlen(asciiBytes) + 1));

   while (TRUE) {
      size_t hexDigitsRemaining;
      uint32 escapedCodePoint = 0;
      Bool foundEscapedCodePoint = FALSE;

      if (*byte == '\0') {
         utf16[utf16Offset] = 0;
         break;
      }

      // Only US-ASCII bytes are allowed as input.
      VERIFY(*byte > 0);

      if (*byte != '\\') {
         utf16[utf16Offset++] = *byte;
         byte++;
         continue;
      }

      // Handle the backslash.
      byte++;

      if (*byte == '\0') {
         // We'll fall out at the top of the loop.
         continue;
      }

      VERIFY(*byte > 0);

      switch (*byte) {
      case 'u':
         /*
          * \\uABCD must have exactly four hexadecimal digits after
          * the escape, denoting the Unicode code point U+ABCD.
          */
         foundEscapedCodePoint = TRUE;
         hexDigitsRemaining = 4;
         break;
      case 'U':
         /*
          * \\U0010CDEF must have exactly eight hexadecimal digits
          * after the escape, denoting the Unicode code point U+10CDEF.
          */
         foundEscapedCodePoint = TRUE;
         hexDigitsRemaining = 8;
         break;
      default:
         // Unsupported escape; treat the next byte literally.
         hexDigitsRemaining = 0;
         utf16[utf16Offset++] = *byte;
         break;
      }

      byte++;

      while (hexDigitsRemaining) {
         uint8 hexValue;

         if (*byte >= '0' && *byte <= '9') {
            hexValue = *byte - '0';
         } else if (*byte >= 'A' && *byte <= 'F') {
            hexValue = *byte - 'A' + 0xA;
         } else if (*byte >= 'a' && *byte <= 'f') {
            hexValue = *byte - 'a' + 0xA;
         } else {
            NOT_IMPLEMENTED();
         }

         escapedCodePoint = (escapedCodePoint << 4) | hexValue;

         byte++;
         hexDigitsRemaining--;
      }

      if (foundEscapedCodePoint) {
         VERIFY(escapedCodePoint <= 0x10FFFF);

         if (U16_LENGTH(escapedCodePoint) == 1) {
            utf16[utf16Offset++] = (utf16_t)escapedCodePoint;
         } else {
            utf16[utf16Offset++] = U16_LEAD(escapedCodePoint);
            utf16[utf16Offset++] = U16_TRAIL(escapedCodePoint);
         }
      }
   }

   result = Unicode_AllocWithUTF16(utf16);
   free(utf16);

   return result;
}
   070701000001F8000081A40000000000000000000000016822550500008704000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeSimpleCaseFolding.c /* **********************************************************
 * Copyright 2007-2014 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * unicodeSimpleCaseFolding.c --
 *
 *      Simple case folding tables generated from Unicode 5.0.0's
 *      CaseFolding-5.0.0.txt data file.  Data file obtained from:
 *
 *      http://unicode.org/Public/UNIDATA/CaseFolding.txt
 *
 *      Table generated using extract_fold.c from
 *      bora/vmcore/frobos/clib/fd32/unicode/extract_fold.c.
 *
 *      Copyright of CaseFolding.txt:
 *
 *      Copyright (C) 1991-2007 Unicode, Inc. All rights
 *      reserved. Distributed under the Terms of Use in
 *      http://www.unicode.org/copyright.html.
 *
 *      Permission is hereby granted, free of charge, to any person
 *      obtaining a copy of the Unicode data files and any associated
 *      documentation (the "Data Files") or Unicode software and any
 *      associated documentation (the "Software") to deal in the Data
 *      Files or Software without restriction, including without
 *      limitation the rights to use, copy, modify, merge, publish,
 *      distribute, and/or sell copies of the Data Files or Software,
 *      and to permit persons to whom the Data Files or Software are
 *      furnished to do so, provided that (a) the above copyright
 *      notice(s) and this permission notice appear with all copies of
 *      the Data Files or Software, (b) both the above copyright
 *      notice(s) and this permission notice appear in associated
 *      documentation, and (c) there is clear notice in each modified
 *      Data File or in the Software as well as in the documentation
 *      associated with the Data File(s) or Software that the data or
 *      software has been modified.
 *
 *      THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT
 *      WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 *      LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 *      PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
 *      RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
 *      INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
 *      SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
 *      WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 *      WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 *      ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 *      PERFORMANCE OF THE DATA FILES OR SOFTWARE.
 */

#include "vmware.h"
#include "unicodeTypes.h"

static const utf16_t page_00[256] =
{
   0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
   0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
   0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
   0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
   0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
   0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
   0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
   0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
   0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
   0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
   0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
   0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
   0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
   0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
   0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
   0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
   0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
   0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
   0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
   0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
   0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
   0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
   0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x03BC, 0x00B6, 0x00B7,
   0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
   0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
   0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
   0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00D7,
   0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00DF,
   0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
   0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
   0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
   0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
};

static const utf16_t page_01[256] =
{
   0x0101, 0x0101, 0x0103, 0x0103, 0x0105, 0x0105, 0x0107, 0x0107,
   0x0109, 0x0109, 0x010B, 0x010B, 0x010D, 0x010D, 0x010F, 0x010F,
   0x0111, 0x0111, 0x0113, 0x0113, 0x0115, 0x0115, 0x0117, 0x0117,
   0x0119, 0x0119, 0x011B, 0x011B, 0x011D, 0x011D, 0x011F, 0x011F,
   0x0121, 0x0121, 0x0123, 0x0123, 0x0125, 0x0125, 0x0127, 0x0127,
   0x0129, 0x0129, 0x012B, 0x012B, 0x012D, 0x012D, 0x012F, 0x012F,
   0x0130, 0x0131, 0x0133, 0x0133, 0x0135, 0x0135, 0x0137, 0x0137,
   0x0138, 0x013A, 0x013A, 0x013C, 0x013C, 0x013E, 0x013E, 0x0140,
   0x0140, 0x0142, 0x0142, 0x0144, 0x0144, 0x0146, 0x0146, 0x0148,
   0x0148, 0x0149, 0x014B, 0x014B, 0x014D, 0x014D, 0x014F, 0x014F,
   0x0151, 0x0151, 0x0153, 0x0153, 0x0155, 0x0155, 0x0157, 0x0157,
   0x0159, 0x0159, 0x015B, 0x015B, 0x015D, 0x015D, 0x015F, 0x015F,
   0x0161, 0x0161, 0x0163, 0x0163, 0x0165, 0x0165, 0x0167, 0x0167,
   0x0169, 0x0169, 0x016B, 0x016B, 0x016D, 0x016D, 0x016F, 0x016F,
   0x0171, 0x0171, 0x0173, 0x0173, 0x0175, 0x0175, 0x0177, 0x0177,
   0x00FF, 0x017A, 0x017A, 0x017C, 0x017C, 0x017E, 0x017E, 0x0073,
   0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188,
   0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259,
   0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268,
   0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275,
   0x01A1, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x0280, 0x01A8,
   0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01B0,
   0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292,
   0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF,
   0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9,
   0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CE, 0x01CE, 0x01D0,
   0x01D0, 0x01D2, 0x01D2, 0x01D4, 0x01D4, 0x01D6, 0x01D6, 0x01D8,
   0x01D8, 0x01DA, 0x01DA, 0x01DC, 0x01DC, 0x01DD, 0x01DF, 0x01DF,
   0x01E1, 0x01E1, 0x01E3, 0x01E3, 0x01E5, 0x01E5, 0x01E7, 0x01E7,
   0x01E9, 0x01E9, 0x01EB, 0x01EB, 0x01ED, 0x01ED, 0x01EF, 0x01EF,
   0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F5, 0x01F5, 0x0195, 0x01BF,
   0x01F9, 0x01F9, 0x01FB, 0x01FB, 0x01FD, 0x01FD, 0x01FF, 0x01FF
};

static const utf16_t page_02[256] =
{
   0x0201, 0x0201, 0x0203, 0x0203, 0x0205, 0x0205, 0x0207, 0x0207,
   0x0209, 0x0209, 0x020B, 0x020B, 0x020D, 0x020D, 0x020F, 0x020F,
   0x0211, 0x0211, 0x0213, 0x0213, 0x0215, 0x0215, 0x0217, 0x0217,
   0x0219, 0x0219, 0x021B, 0x021B, 0x021D, 0x021D, 0x021F, 0x021F,
   0x019E, 0x0221, 0x0223, 0x0223, 0x0225, 0x0225, 0x0227, 0x0227,
   0x0229, 0x0229, 0x022B, 0x022B, 0x022D, 0x022D, 0x022F, 0x022F,
   0x0231, 0x0231, 0x0233, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237,
   0x0238, 0x0239, 0x2C65, 0x023C, 0x023C, 0x019A, 0x2C66, 0x023F,
   0x0240, 0x0242, 0x0242, 0x0180, 0x0289, 0x028C, 0x0247, 0x0247,
   0x0249, 0x0249, 0x024B, 0x024B, 0x024D, 0x024D, 0x024F, 0x024F,
   0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257,
   0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F,
   0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267,
   0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F,
   0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277,
   0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F,
   0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287,
   0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F,
   0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
   0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F,
   0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7,
   0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF,
   0x02B0, 0x02B1, 0x02B2, 0x02B3, 0x02B4, 0x02B5, 0x02B6, 0x02B7,
   0x02B8, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF,
   0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7,
   0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF,
   0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7,
   0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x02DE, 0x02DF,
   0x02E0, 0x02E1, 0x02E2, 0x02E3, 0x02E4, 0x02E5, 0x02E6, 0x02E7,
   0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF,
   0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7,
   0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x02FE, 0x02FF
};

static const utf16_t page_03[256] =
{
   0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
   0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
   0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
   0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
   0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
   0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
   0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
   0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
   0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x03B9, 0x0346, 0x0347,
   0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
   0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
   0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
   0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
   0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
   0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
   0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F,
   0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x03AC, 0x0387,
   0x03AD, 0x03AE, 0x03AF, 0x038B, 0x03CC, 0x038D, 0x03CD, 0x03CE,
   0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
   0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
   0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
   0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
   0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
   0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
   0x03C0, 0x03C1, 0x03C3, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
   0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF,
   0x03B2, 0x03B8, 0x03D2, 0x03D3, 0x03D4, 0x03C6, 0x03C0, 0x03D7,
   0x03D9, 0x03D9, 0x03DB, 0x03DB, 0x03DD, 0x03DD, 0x03DF, 0x03DF,
   0x03E1, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7,
   0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF,
   0x03BA, 0x03C1, 0x03F2, 0x03F3, 0x03B8, 0x03B5, 0x03F6, 0x03F8,
   0x03F8, 0x03F2, 0x03FB, 0x03FB, 0x03FC, 0x037B, 0x037C, 0x037D
};

static const utf16_t page_04[256] =
{
   0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
   0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
   0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
   0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
   0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
   0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
   0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
   0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
   0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
   0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
   0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
   0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
   0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467,
   0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F,
   0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0477, 0x0477,
   0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F,
   0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
   0x0488, 0x0489, 0x048B, 0x048B, 0x048D, 0x048D, 0x048F, 0x048F,
   0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497,
   0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F,
   0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7,
   0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF,
   0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7,
   0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF,
   0x04CF, 0x04C2, 0x04C2, 0x04C4, 0x04C4, 0x04C6, 0x04C6, 0x04C8,
   0x04C8, 0x04CA, 0x04CA, 0x04CC, 0x04CC, 0x04CE, 0x04CE, 0x04CF,
   0x04D1, 0x04D1, 0x04D3, 0x04D3, 0x04D5, 0x04D5, 0x04D7, 0x04D7,
   0x04D9, 0x04D9, 0x04DB, 0x04DB, 0x04DD, 0x04DD, 0x04DF, 0x04DF,
   0x04E1, 0x04E1, 0x04E3, 0x04E3, 0x04E5, 0x04E5, 0x04E7, 0x04E7,
   0x04E9, 0x04E9, 0x04EB, 0x04EB, 0x04ED, 0x04ED, 0x04EF, 0x04EF,
   0x04F1, 0x04F1, 0x04F3, 0x04F3, 0x04F5, 0x04F5, 0x04F7, 0x04F7,
   0x04F9, 0x04F9, 0x04FB, 0x04FB, 0x04FD, 0x04FD, 0x04FF, 0x04FF
};

static const utf16_t page_05[256] =
{
   0x0501, 0x0501, 0x0503, 0x0503, 0x0505, 0x0505, 0x0507, 0x0507,
   0x0509, 0x0509, 0x050B, 0x050B, 0x050D, 0x050D, 0x050F, 0x050F,
   0x0511, 0x0511, 0x0513, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517,
   0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F,
   0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
   0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F,
   0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
   0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
   0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
   0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
   0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557,
   0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F,
   0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
   0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
   0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
   0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
   0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587,
   0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F,
   0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597,
   0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F,
   0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7,
   0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF,
   0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
   0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
   0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7,
   0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF,
   0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
   0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
   0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
   0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF,
   0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7,
   0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF
};

static const utf16_t page_10[256] =
{
   0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
   0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F,
   0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017,
   0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F,
   0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
   0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F,
   0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037,
   0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F,
   0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047,
   0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F,
   0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057,
   0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F,
   0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067,
   0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F,
   0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077,
   0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F,
   0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087,
   0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F,
   0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097,
   0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F,
   0x2D00, 0x2D01, 0x2D02, 0x2D03, 0x2D04, 0x2D05, 0x2D06, 0x2D07,
   0x2D08, 0x2D09, 0x2D0A, 0x2D0B, 0x2D0C, 0x2D0D, 0x2D0E, 0x2D0F,
   0x2D10, 0x2D11, 0x2D12, 0x2D13, 0x2D14, 0x2D15, 0x2D16, 0x2D17,
   0x2D18, 0x2D19, 0x2D1A, 0x2D1B, 0x2D1C, 0x2D1D, 0x2D1E, 0x2D1F,
   0x2D20, 0x2D21, 0x2D22, 0x2D23, 0x2D24, 0x2D25, 0x10C6, 0x10C7,
   0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF,
   0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
   0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
   0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
   0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
   0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7,
   0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF
};

static const utf16_t page_1E[256] =
{
   0x1E01, 0x1E01, 0x1E03, 0x1E03, 0x1E05, 0x1E05, 0x1E07, 0x1E07,
   0x1E09, 0x1E09, 0x1E0B, 0x1E0B, 0x1E0D, 0x1E0D, 0x1E0F, 0x1E0F,
   0x1E11, 0x1E11, 0x1E13, 0x1E13, 0x1E15, 0x1E15, 0x1E17, 0x1E17,
   0x1E19, 0x1E19, 0x1E1B, 0x1E1B, 0x1E1D, 0x1E1D, 0x1E1F, 0x1E1F,
   0x1E21, 0x1E21, 0x1E23, 0x1E23, 0x1E25, 0x1E25, 0x1E27, 0x1E27,
   0x1E29, 0x1E29, 0x1E2B, 0x1E2B, 0x1E2D, 0x1E2D, 0x1E2F, 0x1E2F,
   0x1E31, 0x1E31, 0x1E33, 0x1E33, 0x1E35, 0x1E35, 0x1E37, 0x1E37,
   0x1E39, 0x1E39, 0x1E3B, 0x1E3B, 0x1E3D, 0x1E3D, 0x1E3F, 0x1E3F,
   0x1E41, 0x1E41, 0x1E43, 0x1E43, 0x1E45, 0x1E45, 0x1E47, 0x1E47,
   0x1E49, 0x1E49, 0x1E4B, 0x1E4B, 0x1E4D, 0x1E4D, 0x1E4F, 0x1E4F,
   0x1E51, 0x1E51, 0x1E53, 0x1E53, 0x1E55, 0x1E55, 0x1E57, 0x1E57,
   0x1E59, 0x1E59, 0x1E5B, 0x1E5B, 0x1E5D, 0x1E5D, 0x1E5F, 0x1E5F,
   0x1E61, 0x1E61, 0x1E63, 0x1E63, 0x1E65, 0x1E65, 0x1E67, 0x1E67,
   0x1E69, 0x1E69, 0x1E6B, 0x1E6B, 0x1E6D, 0x1E6D, 0x1E6F, 0x1E6F,
   0x1E71, 0x1E71, 0x1E73, 0x1E73, 0x1E75, 0x1E75, 0x1E77, 0x1E77,
   0x1E79, 0x1E79, 0x1E7B, 0x1E7B, 0x1E7D, 0x1E7D, 0x1E7F, 0x1E7F,
   0x1E81, 0x1E81, 0x1E83, 0x1E83, 0x1E85, 0x1E85, 0x1E87, 0x1E87,
   0x1E89, 0x1E89, 0x1E8B, 0x1E8B, 0x1E8D, 0x1E8D, 0x1E8F, 0x1E8F,
   0x1E91, 0x1E91, 0x1E93, 0x1E93, 0x1E95, 0x1E95, 0x1E96, 0x1E97,
   0x1E98, 0x1E99, 0x1E9A, 0x1E61, 0x1E9C, 0x1E9D, 0x1E9E, 0x1E9F,
   0x1EA1, 0x1EA1, 0x1EA3, 0x1EA3, 0x1EA5, 0x1EA5, 0x1EA7, 0x1EA7,
   0x1EA9, 0x1EA9, 0x1EAB, 0x1EAB, 0x1EAD, 0x1EAD, 0x1EAF, 0x1EAF,
   0x1EB1, 0x1EB1, 0x1EB3, 0x1EB3, 0x1EB5, 0x1EB5, 0x1EB7, 0x1EB7,
   0x1EB9, 0x1EB9, 0x1EBB, 0x1EBB, 0x1EBD, 0x1EBD, 0x1EBF, 0x1EBF,
   0x1EC1, 0x1EC1, 0x1EC3, 0x1EC3, 0x1EC5, 0x1EC5, 0x1EC7, 0x1EC7,
   0x1EC9, 0x1EC9, 0x1ECB, 0x1ECB, 0x1ECD, 0x1ECD, 0x1ECF, 0x1ECF,
   0x1ED1, 0x1ED1, 0x1ED3, 0x1ED3, 0x1ED5, 0x1ED5, 0x1ED7, 0x1ED7,
   0x1ED9, 0x1ED9, 0x1EDB, 0x1EDB, 0x1EDD, 0x1EDD, 0x1EDF, 0x1EDF,
   0x1EE1, 0x1EE1, 0x1EE3, 0x1EE3, 0x1EE5, 0x1EE5, 0x1EE7, 0x1EE7,
   0x1EE9, 0x1EE9, 0x1EEB, 0x1EEB, 0x1EED, 0x1EED, 0x1EEF, 0x1EEF,
   0x1EF1, 0x1EF1, 0x1EF3, 0x1EF3, 0x1EF5, 0x1EF5, 0x1EF7, 0x1EF7,
   0x1EF9, 0x1EF9, 0x1EFA, 0x1EFB, 0x1EFC, 0x1EFD, 0x1EFE, 0x1EFF
};

static const utf16_t page_1F[256] =
{
   0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07,
   0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07,
   0x1F10, 0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F16, 0x1F17,
   0x1F10, 0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F1E, 0x1F1F,
   0x1F20, 0x1F21, 0x1F22, 0x1F23, 0x1F24, 0x1F25, 0x1F26, 0x1F27,
   0x1F20, 0x1F21, 0x1F22, 0x1F23, 0x1F24, 0x1F25, 0x1F26, 0x1F27,
   0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37,
   0x1F30, 0x1F31, 0x1F32, 0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37,
   0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x1F46, 0x1F47,
   0x1F40, 0x1F41, 0x1F42, 0x1F43, 0x1F44, 0x1F45, 0x1F4E, 0x1F4F,
   0x1F50, 0x1F51, 0x1F52, 0x1F53, 0x1F54, 0x1F55, 0x1F56, 0x1F57,
   0x1F58, 0x1F51, 0x1F5A, 0x1F53, 0x1F5C, 0x1F55, 0x1F5E, 0x1F57,
   0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67,
   0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64, 0x1F65, 0x1F66, 0x1F67,
   0x1F70, 0x1F71, 0x1F72, 0x1F73, 0x1F74, 0x1F75, 0x1F76, 0x1F77,
   0x1F78, 0x1F79, 0x1F7A, 0x1F7B, 0x1F7C, 0x1F7D, 0x1F7E, 0x1F7F,
   0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86, 0x1F87,
   0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86, 0x1F87,
   0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96, 0x1F97,
   0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96, 0x1F97,
   0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6, 0x1FA7,
   0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6, 0x1FA7,
   0x1FB0, 0x1FB1, 0x1FB2, 0x1FB3, 0x1FB4, 0x1FB5, 0x1FB6, 0x1FB7,
   0x1FB0, 0x1FB1, 0x1F70, 0x1F71, 0x1FB3, 0x1FBD, 0x03B9, 0x1FBF,
   0x1FC0, 0x1FC1, 0x1FC2, 0x1FC3, 0x1FC4, 0x1FC5, 0x1FC6, 0x1FC7,
   0x1F72, 0x1F73, 0x1F74, 0x1F75, 0x1FC3, 0x1FCD, 0x1FCE, 0x1FCF,
   0x1FD0, 0x1FD1, 0x1FD2, 0x1FD3, 0x1FD4, 0x1FD5, 0x1FD6, 0x1FD7,
   0x1FD0, 0x1FD1, 0x1F76, 0x1F77, 0x1FDC, 0x1FDD, 0x1FDE, 0x1FDF,
   0x1FE0, 0x1FE1, 0x1FE2, 0x1FE3, 0x1FE4, 0x1FE5, 0x1FE6, 0x1FE7,
   0x1FE0, 0x1FE1, 0x1F7A, 0x1F7B, 0x1FE5, 0x1FED, 0x1FEE, 0x1FEF,
   0x1FF0, 0x1FF1, 0x1FF2, 0x1FF3, 0x1FF4, 0x1FF5, 0x1FF6, 0x1FF7,
   0x1F78, 0x1F79, 0x1F7C, 0x1F7D, 0x1FF3, 0x1FFD, 0x1FFE, 0x1FFF
};

static const utf16_t page_21[256] =
{
   0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107,
   0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F,
   0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117,
   0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F,
   0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x03C9, 0x2127,
   0x2128, 0x2129, 0x006B, 0x00E5, 0x212C, 0x212D, 0x212E, 0x212F,
   0x2130, 0x2131, 0x214E, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137,
   0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F,
   0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,
   0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F,
   0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,
   0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F,
   0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
   0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
   0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
   0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
   0x2180, 0x2181, 0x2182, 0x2184, 0x2184, 0x2185, 0x2186, 0x2187,
   0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F,
   0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197,
   0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F,
   0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7,
   0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF,
   0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7,
   0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF,
   0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7,
   0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF,
   0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7,
   0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF,
   0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7,
   0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF,
   0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7,
   0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF
};

static const utf16_t page_24[256] =
{
   0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407,
   0x2408, 0x2409, 0x240A, 0x240B, 0x240C, 0x240D, 0x240E, 0x240F,
   0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417,
   0x2418, 0x2419, 0x241A, 0x241B, 0x241C, 0x241D, 0x241E, 0x241F,
   0x2420, 0x2421, 0x2422, 0x2423, 0x2424, 0x2425, 0x2426, 0x2427,
   0x2428, 0x2429, 0x242A, 0x242B, 0x242C, 0x242D, 0x242E, 0x242F,
   0x2430, 0x2431, 0x2432, 0x2433, 0x2434, 0x2435, 0x2436, 0x2437,
   0x2438, 0x2439, 0x243A, 0x243B, 0x243C, 0x243D, 0x243E, 0x243F,
   0x2440, 0x2441, 0x2442, 0x2443, 0x2444, 0x2445, 0x2446, 0x2447,
   0x2448, 0x2449, 0x244A, 0x244B, 0x244C, 0x244D, 0x244E, 0x244F,
   0x2450, 0x2451, 0x2452, 0x2453, 0x2454, 0x2455, 0x2456, 0x2457,
   0x2458, 0x2459, 0x245A, 0x245B, 0x245C, 0x245D, 0x245E, 0x245F,
   0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467,
   0x2468, 0x2469, 0x246A, 0x246B, 0x246C, 0x246D, 0x246E, 0x246F,
   0x2470, 0x2471, 0x2472, 0x2473, 0x2474, 0x2475, 0x2476, 0x2477,
   0x2478, 0x2479, 0x247A, 0x247B, 0x247C, 0x247D, 0x247E, 0x247F,
   0x2480, 0x2481, 0x2482, 0x2483, 0x2484, 0x2485, 0x2486, 0x2487,
   0x2488, 0x2489, 0x248A, 0x248B, 0x248C, 0x248D, 0x248E, 0x248F,
   0x2490, 0x2491, 0x2492, 0x2493, 0x2494, 0x2495, 0x2496, 0x2497,
   0x2498, 0x2499, 0x249A, 0x249B, 0x249C, 0x249D, 0x249E, 0x249F,
   0x24A0, 0x24A1, 0x24A2, 0x24A3, 0x24A4, 0x24A5, 0x24A6, 0x24A7,
   0x24A8, 0x24A9, 0x24AA, 0x24AB, 0x24AC, 0x24AD, 0x24AE, 0x24AF,
   0x24B0, 0x24B1, 0x24B2, 0x24B3, 0x24B4, 0x24B5, 0x24D0, 0x24D1,
   0x24D2, 0x24D3, 0x24D4, 0x24D5, 0x24D6, 0x24D7, 0x24D8, 0x24D9,
   0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE, 0x24DF, 0x24E0, 0x24E1,
   0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7, 0x24E8, 0x24E9,
   0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4, 0x24D5, 0x24D6, 0x24D7,
   0x24D8, 0x24D9, 0x24DA, 0x24DB, 0x24DC, 0x24DD, 0x24DE, 0x24DF,
   0x24E0, 0x24E1, 0x24E2, 0x24E3, 0x24E4, 0x24E5, 0x24E6, 0x24E7,
   0x24E8, 0x24E9, 0x24EA, 0x24EB, 0x24EC, 0x24ED, 0x24EE, 0x24EF,
   0x24F0, 0x24F1, 0x24F2, 0x24F3, 0x24F4, 0x24F5, 0x24F6, 0x24F7,
   0x24F8, 0x24F9, 0x24FA, 0x24FB, 0x24FC, 0x24FD, 0x24FE, 0x24FF
};

static const utf16_t page_2C[256] =
{
   0x2C30, 0x2C31, 0x2C32, 0x2C33, 0x2C34, 0x2C35, 0x2C36, 0x2C37,
   0x2C38, 0x2C39, 0x2C3A, 0x2C3B, 0x2C3C, 0x2C3D, 0x2C3E, 0x2C3F,
   0x2C40, 0x2C41, 0x2C42, 0x2C43, 0x2C44, 0x2C45, 0x2C46, 0x2C47,
   0x2C48, 0x2C49, 0x2C4A, 0x2C4B, 0x2C4C, 0x2C4D, 0x2C4E, 0x2C4F,
   0x2C50, 0x2C51, 0x2C52, 0x2C53, 0x2C54, 0x2C55, 0x2C56, 0x2C57,
   0x2C58, 0x2C59, 0x2C5A, 0x2C5B, 0x2C5C, 0x2C5D, 0x2C5E, 0x2C2F,
   0x2C30, 0x2C31, 0x2C32, 0x2C33, 0x2C34, 0x2C35, 0x2C36, 0x2C37,
   0x2C38, 0x2C39, 0x2C3A, 0x2C3B, 0x2C3C, 0x2C3D, 0x2C3E, 0x2C3F,
   0x2C40, 0x2C41, 0x2C42, 0x2C43, 0x2C44, 0x2C45, 0x2C46, 0x2C47,
   0x2C48, 0x2C49, 0x2C4A, 0x2C4B, 0x2C4C, 0x2C4D, 0x2C4E, 0x2C4F,
   0x2C50, 0x2C51, 0x2C52, 0x2C53, 0x2C54, 0x2C55, 0x2C56, 0x2C57,
   0x2C58, 0x2C59, 0x2C5A, 0x2C5B, 0x2C5C, 0x2C5D, 0x2C5E, 0x2C5F,
   0x2C61, 0x2C61, 0x026B, 0x1D7D, 0x027D, 0x2C65, 0x2C66, 0x2C68,
   0x2C68, 0x2C6A, 0x2C6A, 0x2C6C, 0x2C6C, 0x2C6D, 0x2C6E, 0x2C6F,
   0x2C70, 0x2C71, 0x2C72, 0x2C73, 0x2C74, 0x2C76, 0x2C76, 0x2C77,
   0x2C78, 0x2C79, 0x2C7A, 0x2C7B, 0x2C7C, 0x2C7D, 0x2C7E, 0x2C7F,
   0x2C81, 0x2C81, 0x2C83, 0x2C83, 0x2C85, 0x2C85, 0x2C87, 0x2C87,
   0x2C89, 0x2C89, 0x2C8B, 0x2C8B, 0x2C8D, 0x2C8D, 0x2C8F, 0x2C8F,
   0x2C91, 0x2C91, 0x2C93, 0x2C93, 0x2C95, 0x2C95, 0x2C97, 0x2C97,
   0x2C99, 0x2C99, 0x2C9B, 0x2C9B, 0x2C9D, 0x2C9D, 0x2C9F, 0x2C9F,
   0x2CA1, 0x2CA1, 0x2CA3, 0x2CA3, 0x2CA5, 0x2CA5, 0x2CA7, 0x2CA7,
   0x2CA9, 0x2CA9, 0x2CAB, 0x2CAB, 0x2CAD, 0x2CAD, 0x2CAF, 0x2CAF,
   0x2CB1, 0x2CB1, 0x2CB3, 0x2CB3, 0x2CB5, 0x2CB5, 0x2CB7, 0x2CB7,
   0x2CB9, 0x2CB9, 0x2CBB, 0x2CBB, 0x2CBD, 0x2CBD, 0x2CBF, 0x2CBF,
   0x2CC1, 0x2CC1, 0x2CC3, 0x2CC3, 0x2CC5, 0x2CC5, 0x2CC7, 0x2CC7,
   0x2CC9, 0x2CC9, 0x2CCB, 0x2CCB, 0x2CCD, 0x2CCD, 0x2CCF, 0x2CCF,
   0x2CD1, 0x2CD1, 0x2CD3, 0x2CD3, 0x2CD5, 0x2CD5, 0x2CD7, 0x2CD7,
   0x2CD9, 0x2CD9, 0x2CDB, 0x2CDB, 0x2CDD, 0x2CDD, 0x2CDF, 0x2CDF,
   0x2CE1, 0x2CE1, 0x2CE3, 0x2CE3, 0x2CE4, 0x2CE5, 0x2CE6, 0x2CE7,
   0x2CE8, 0x2CE9, 0x2CEA, 0x2CEB, 0x2CEC, 0x2CED, 0x2CEE, 0x2CEF,
   0x2CF0, 0x2CF1, 0x2CF2, 0x2CF3, 0x2CF4, 0x2CF5, 0x2CF6, 0x2CF7,
   0x2CF8, 0x2CF9, 0x2CFA, 0x2CFB, 0x2CFC, 0x2CFD, 0x2CFE, 0x2CFF
};

static const utf16_t page_FF[256] =
{
   0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07,
   0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F,
   0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17,
   0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F,
   0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
   0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
   0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
   0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F,
   0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
   0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
   0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
   0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F,
   0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67,
   0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,
   0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77,
   0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F,
   0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87,
   0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F,
   0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97,
   0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F,
   0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7,
   0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF,
   0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7,
   0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF,
   0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7,
   0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF,
   0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7,
   0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF,
   0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7,
   0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF,
   0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7,
   0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF
};

static const utf16_t *pages[256] =
{
   page_00, page_01, page_02, page_03, page_04, page_05, NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   page_10, NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    page_1E, page_1F,
   NULL,    page_21, NULL,    NULL,    page_24, NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    page_2C, NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    page_FF
};


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeSimpleCaseFold --
 *
 *      Performs simple Unicode case folding on the input UTF-16 code
 *      unit for case-insensitive, locale-agnostic matching of strings.
 *
 *      Case folding means the code unit is upper-cased, then lower-cased.
 *
 *      "Simple" means:
 *
 *      1) Only code points between U+0000 and U+FFFF are handled.
 *         (Case-sensitive Deseret characters between U+10400 and
 *         U+10427 are not handled.)
 *
 *      2) Code units that would normally grow in length when folded
 *         (German sharp S U+00DF: 'straBe' -> 'STRASSE' -> 'strasse')
 *         are ignored.
 *
 *      Case folding does not handle normalization, so decomposed
 *      (A followed by ACCENT ACUTE) characters do not match precomposed
 *      (A WITH ACCENT ACCUTE) character.
 *
 * Results:
 *      The case-folded code unit.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

utf16_t
UnicodeSimpleCaseFold(utf16_t codeUnit) // IN
{
   const utf16_t *page = pages[codeUnit >> 8];

   if (!page) {
      return codeUnit;
   }

   return page[codeUnit & 0xFF];
}
070701000001F9000081A400000000000000000000000168225505000046F7000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeSimpleOperations.c  /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeSimpleOperations.c --
 *
 *      Simple UTF-8 implementation of unicodeOperations.h interface.
 */

#include "vmware.h"

#include "util.h"
#include "str.h"
#include "unicodeBase.h"
#include "unicodeInt.h"
#include "unicodeOperations.h"
#include "codeset.h"


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_LengthInCodePoints --
 *
 *      Returns the length of the unicode string in code points
 *      ("unicode characters").
 *
 * Results:
 *      As above
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

UnicodeIndex
Unicode_LengthInCodePoints(const char *str)  // IN:
{
   return CodeSet_LengthInCodePoints(str);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_CompareRange --
 *
 *      Compares ranges of two Unicode strings for canonical
 *      equivalence in code point order.
 *
 *      Canonical equivalence means the two strings represent the same
 *      Unicode code points, regardless of the order of combining
 *      characters or use of compatibility singletons.
 *
 *      See Unicode Standard Annex #15 (Unicode Normalization Forms)
 *      for more on canonical equivalence and composition.
 *
 *      If ignoreCase is TRUE, then the two strings are case-folded
 *      (converted to upper-case, then converted to lower-case) in a
 *      locale-agnostic manner before comparing.
 *
 *      Indices and lengths that are out of bounds are pinned to the
 *      edges of the string.
 *
 *      Pass -1 for any length parameter to indicate "from start until
 *      end of string".
 *
 *      The start and length arguments are in code points - unicode
 *      "characters" - not bytes!
 *
 * Results:
 *      -1 if str1 < str2, 0 if str1 == str2, 1 if str1 > str2.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
Unicode_CompareRange(const char *str1,         // IN:
                     UnicodeIndex str1Start,   // IN:
                     UnicodeIndex str1Length,  // IN:
                     const char *str2,         // IN:
                     UnicodeIndex str2Start,   // IN:
                     UnicodeIndex str2Length,  // IN:
                     Bool ignoreCase)          // IN:
{
   int result = -1;
   char *substr1 = NULL;
   char *substr2 = NULL;
   utf16_t *str1UTF16 = NULL;
   utf16_t *str2UTF16 = NULL;
   UnicodeIndex i = 0;
   UnicodeIndex utf16Index;
   utf16_t codeUnit1;
   utf16_t codeUnit2;
   uint32 codePoint1;
   uint32 codePoint2;

   /*
    * TODO: Allocating substrings is a performance hit.  We should do this
    * search in-place.  (However, searching UTF-8 requires tender loving
    * care, and it's just easier to search UTF-16.)
    */

   if (str1Start != 0 || str1Length != -1) {
      substr1 = Unicode_Substr(str1, str1Start, str1Length);
      if (!substr1) {
         goto out;
      }
      str1 = substr1;
   }

   if (str2Start != 0 || str2Length != -1) {
      substr2 = Unicode_Substr(str2, str2Start, str2Length);
      if (!substr2) {
         goto out;
      }
      str2 = substr2;
   }

   /*
    * XXX TODO: Need to normalize the incoming strings to NFC or NFD.
    */

   str1UTF16 = Unicode_GetAllocUTF16(str1);
   if (!str1UTF16) {
      goto out;
   }

   str2UTF16 = Unicode_GetAllocUTF16(str2);
   if (!str2UTF16) {
      goto out;
   }

   /*
    * TODO: This is the naive string search algorithm, which is O(n * m). We
    * can do better with KMP or Boyer-Moore if this proves to be a bottleneck.
    */

   while (TRUE) {
      codeUnit1 = *(str1UTF16 + i);
      codeUnit2 = *(str2UTF16 + i);

      /*
       * TODO: Simple case folding doesn't handle the situation where more
       * than one code unit is needed to store the result of the case folding.
       *
       * This means that German "straBe" (where B = sharp S, U+00DF) will not
       * match "STRASSE", even though the two strings are the same.
       */

      if (ignoreCase) {
         codeUnit1 = UnicodeSimpleCaseFold(codeUnit1);
         codeUnit2 = UnicodeSimpleCaseFold(codeUnit2);
      }

      if (codeUnit1 != codeUnit2) {
         break;
      }

      if (codeUnit1 == 0) {
         // End of both strings reached: strings are equal.
         result = 0;
         goto out;
      }

      i++;
   }

   /*
    * The two UTF-16 code units differ. If they're the first code unit of a
    * surrogate pair (for Unicode values past U+FFFF), decode the surrogate
    * pair into a full Unicode code point.
    */

   if (U16_IS_SURROGATE(codeUnit1)) {
      ssize_t strUTF16Len = Unicode_UTF16Strlen(str1UTF16);

      // U16_NEXT modifies the index, so let it work on a copy.
      utf16Index = i;

      // Decode the surrogate if needed.
      U16_NEXT(str1UTF16, utf16Index, strUTF16Len, codePoint1);
   } else {
      // Not a surrogate?  Then the code point value is the code unit.
      codePoint1 = codeUnit1;
   }

   if (U16_IS_SURROGATE(codeUnit2)) {
      ssize_t strUTF16Len = Unicode_UTF16Strlen(str2UTF16);

      utf16Index = i;
      U16_NEXT(str2UTF16, utf16Index, strUTF16Len, codePoint2);
   } else {
      codePoint2 = codeUnit2;
   }

   if (codePoint1 < codePoint2) {
      result = -1;
   } else if (codePoint1 > codePoint2) {
      result = 1;
   } else {
      // If we hit the end of the string, we've already gone to 'out'.
      NOT_REACHED();
   }

out:
   free(str1UTF16);
   free(str2UTF16);

   free(substr1);
   free(substr2);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_FindSubstrInRange --
 *
 *      Searches the string 'str' in the range [strStart, strStart+strLength)
 *      for the first occurrence of the code units of 'strToFind'
 *      in the range [strToFindStart, strToFindStart+strToFindLength).
 *
 *      Indices and lengths that are out of bounds are pinned to the
 *      edges of the string.
 *
 *      Pass -1 for any length parameter to indicate "from start until
 *      end of string".
 *
 *      The start and length arguments are in code points - unicode
 *      "characters" - not bytes!
 *
 * Results:
 *      If 'strToFind' exists inside 'str' in the specified range,
 *      returns the first starting index of 'strToFind' in that range.
 *
 *      Otherwise, returns UNICODE_INDEX_NOT_FOUND.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

UnicodeIndex
Unicode_FindSubstrInRange(const char *str,               // IN:
                          UnicodeIndex strStart,         // IN:
                          UnicodeIndex strLength,        // IN:
                          const char *strToFind,         // IN:
                          UnicodeIndex strToFindStart,   // IN:
                          UnicodeIndex strToFindLength)  // IN:
{
   UnicodeIndex index;
   uint32 *utf32Source = NULL;
   uint32 *utf32Search = NULL;

   ASSERT(str);
   ASSERT(strStart >= 0);
   ASSERT((strLength >= 0) || (strLength == -1));

   ASSERT(strToFind);
   ASSERT(strToFindStart >= 0);
   ASSERT((strToFindLength >= 0) || (strToFindLength == -1));

   /*
    * Convert the string to be searched and the search string to UTF32.
    */

   if (!CodeSet_UTF8ToUTF32(str, (char **) &utf32Source)) {
      Panic("%s: invalid UTF8 string @ %p\n", __FUNCTION__, str);
   }

   if (!CodeSet_UTF8ToUTF32(strToFind, (char **) &utf32Search)) {
      Panic("%s: invalid UTF8 string @ %p\n", __FUNCTION__, strToFind);
   }

   /*
    * Do any bounds cleanup and checking that is necessary...
    */

   if (strLength < 0) {
      strLength = Unicode_LengthInCodePoints(str) - strStart;
   }

   if (strToFindLength < 0) {
      strToFindLength = Unicode_LengthInCodePoints(strToFind) - strToFindStart;
   }

   if (strLength < strToFindLength) {
      index = UNICODE_INDEX_NOT_FOUND;
      goto bail;
   }

   /*
    * Yes, this may be viewed as a bit strange but this is what strstr does.
    */

   if (strToFindLength == 0) {
      index = strStart;
      goto bail;
   }

   /*
    * Attempt to find the first occurence of the search string in the string
    * to be searched.
    */

   for (index = strStart;
        index <= (strStart + strLength - strToFindLength);
        index++) {
      UnicodeIndex i;
      Bool match = FALSE;
      UnicodeIndex indexSrc = index;
      UnicodeIndex indexSrch = strToFindStart;

      for (i = 0; i < strToFindLength; i++) {
         match = (utf32Source[indexSrc++] == utf32Search[indexSrch++]);

         if (!match) {
            break;
         }
      }

      if (match) {
         goto bail;
      }
   }

   index = UNICODE_INDEX_NOT_FOUND;

bail:

   free(utf32Source);
   free(utf32Search);

   return index;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_FindLastSubstrInRange --
 *
 *      Searches the string 'str' in the range [strStart, strStart+strLength)
 *      for the last occurrence of the code units of 'strToFind'
 *      in the range [strToFindStart, strToFindStart+strToFindLength).
 *
 *      Indices and lengths that are out of bounds are pinned to the
 *      edges of the string.
 *
 *      Pass -1 for any length parameter to indicate "from start until
 *      end of string".
 *
 *      The start and length arguments are in code points - unicode
 *      "characters" - not bytes!
 *
 * Results:
 *      If 'strToFind' exists inside 'str' in the specified range,
 *      returns the last starting index of 'strToFind' in that range.
 *
 *      Otherwise, returns UNICODE_INDEX_NOT_FOUND.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

UnicodeIndex
Unicode_FindLastSubstrInRange(const char *str,               // IN:
                              UnicodeIndex strStart,         // IN:
                              UnicodeIndex strLength,        // IN:
                              const char *strToFind,         // IN:
                              UnicodeIndex strToFindStart,   // IN:
                              UnicodeIndex strToFindLength)  // IN:
{
   UnicodeIndex index;
   uint32 *utf32Source = NULL;
   uint32 *utf32Search = NULL;

   ASSERT(str);
   ASSERT(strStart >= 0);
   ASSERT((strLength >= 0) || (strLength == -1));

   ASSERT(strToFind);
   ASSERT(strToFindStart >= 0);
   ASSERT((strToFindLength >= 0) || (strToFindLength == -1));

   /*
    * Convert the string to be searched and the search string to UTF32.
    */

   if (!CodeSet_UTF8ToUTF32(str, (char **) &utf32Source)) {
      Panic("%s: invalid UTF8 string @ %p\n", __FUNCTION__, str);
   }

   if (!CodeSet_UTF8ToUTF32(strToFind, (char **) &utf32Search)) {
      Panic("%s: invalid UTF8 string @ %p\n", __FUNCTION__, strToFind);
   }

   /*
    * Do any bounds cleanup and checking that is necessary...
    */

   if (strLength < 0) {
      strLength = Unicode_LengthInCodePoints(str) - strStart;
   }

   if (strToFindLength < 0) {
      strToFindLength = Unicode_LengthInCodePoints(strToFind) - strToFindStart;
   }

   if (strLength < strToFindLength) {
      index = UNICODE_INDEX_NOT_FOUND;
      goto bail;
   }

   /*
    * Yes, this may be viewed as a bit strange but this is what strstr does.
    */

   if (strToFindLength == 0) {
      index = strStart;
      goto bail;
   }

   /*
    * Attempt to find the last occurence of the search string in the string
    * to be searched.
    */

   for (index = strStart + strLength - strToFindLength;
        index >= strStart;
        index--) {
      UnicodeIndex i;
      Bool match = FALSE;
      UnicodeIndex indexSrc = index;
      UnicodeIndex indexSrch = strToFindStart;

      for (i = 0; i < strToFindLength; i++) {
         match = (utf32Source[indexSrc++] == utf32Search[indexSrch++]);

         if (!match) {
            break;
         }
      }

      if (match) {
         goto bail;
      }
   }

   index = UNICODE_INDEX_NOT_FOUND;

bail:

   free(utf32Source);
   free(utf32Search);

   return index;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Substr --
 *
 *      Allocates and returns a substring of 'str'.
 *
 *      Indices and lengths that are out of bounds are pinned to the
 *      edges of the string.
 *
 *      Pass -1 for the length parameter to indicate "from start until
 *      end of string".
 *
 *      The start and length arguments are in code points - unicode
 *      "characters" - not bytes!
 *
 * Results:
 *      The newly-allocated substring of 'str' in the range [index,
 *      index + length). Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_Substr(const char *str,      // IN:
               UnicodeIndex start,   // IN:
               UnicodeIndex length)  // IN:
{
   char *substr;
   uint32 codePointLen;
   uint32 *utf32 = NULL;

   ASSERT(str);
   ASSERT((start >= 0) || (start == -1));
   ASSERT((length >= 0) || (length == -1));

   if (!CodeSet_UTF8ToUTF32(str, (char **) &utf32)) {
      Panic("%s: invalid UTF8 string @ %p\n", __FUNCTION__, str);
   }

   codePointLen = 0;
   while (utf32[codePointLen] != 0) {
      codePointLen++;
   }

   if ((start < 0) || (start > codePointLen)) {
      start = codePointLen;
   }

   if ((length < 0) || ((start + length) > codePointLen)) {
      length = codePointLen - start;
   }

   utf32[start + length] = 0;

   CodeSet_UTF32ToUTF8((char *) &utf32[start], &substr);

   free(utf32);

   return substr;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_ReplaceRange --
 *
 *      Core operation upon which append, insert, replace, and remove
 *      are based.
 *
 *      Replaces the code units of destination in the range
 *      [destinationStart, destinationLength) with the code units of
 *      source in the range [sourceStart, sourceLength).
 *
 *      Indices and lengths that are out of bounds are pinned to the
 *      edges of the string.
 *
 *      Pass -1 for any length parameter to indicate "from start until
 *      end of string".
 *
 *      The start and length arguments are in code points - unicode
 *      "characters" - not bytes!
 *
 * Results:
 *      A newly-allocated string containing the results of the replace
 *      operation.  Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_ReplaceRange(const char *dest,         // IN:
                     UnicodeIndex destStart,   // IN:
                     UnicodeIndex destLength,  // IN:
                     const char *src,          // IN:
                     UnicodeIndex srcStart,    // IN:
                     UnicodeIndex srcLength)   // IN:
{
   char *result;
   char *stringOne;
   char *stringTwo;
   char *stringThree;

   ASSERT(dest);
   ASSERT((destStart >= 0) || (destStart == -1));
   ASSERT((destLength >= 0) || (destLength == -1));

   ASSERT(src);
   ASSERT((srcStart >= 0) || (srcStart == -1));
   ASSERT((srcLength >= 0) || (srcLength == -1));

   stringOne = Unicode_Substr(dest, 0, destStart);
   stringTwo = Unicode_Substr(src, srcStart, srcLength);
   stringThree = Unicode_Substr(dest, destStart + destLength, -1);

   result = Unicode_Join(stringOne, stringTwo, stringThree, NULL);

   free(stringOne);
   free(stringTwo);
   free(stringThree);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Join --
 *
 *      Allocates and returns a new string containing the 'first' followed by
 *      all the unicode strings specified as optional arguments. Appending
 *      ceases when a NULL pointer is detected.
 *
 * Results:
 *      The newly-allocated string.  Caller must free with free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_Join(const char *first,  // IN:
             ...)                // IN:
{
   char *result;

   if (first == NULL) {
      result = NULL;
   } else {
      va_list args;
      const char *cur;

      result = Unicode_Duplicate(first);

      va_start(args, first);

      while ((cur = va_arg(args, const char *)) != NULL) {
         char *temp;

         temp = Unicode_Format("%s%s", result, cur);
         free(result);
         result = temp;
      }

      va_end(args);
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Format --
 *
 *      Format a Unicode string (roughly equivalent to Str_Asprintf()).
 *
 * Results:
 *      The formatted string.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_Format(const char *fmt, // IN: the format
               ...)             // IN: the arguments
{
   va_list args;
   char *p;

   va_start(args, fmt);
   p = Str_Vasprintf(NULL, fmt, args);
   va_end(args);

   return p;
}
 070701000001FA000081A40000000000000000000000016822550500004692000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeSimpleTransforms.c  /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeSimpleTransforms.c --
 *
 *      Simple UTF-8 implementation of unicodeTransforms.h interface.
 */

#include "vmware.h"

#include "unicodeBase.h"
#include "unicodeInt.h"
#include "unicodeTransforms.h"

static INLINE Bool UnicodeSimpleIsWhiteSpace(utf16_t codeUnit);

/*
 * Tables generated from bora/lib/unicode/makeWhitespacePages.c,
 * running against ICU 3.8 (which implements Unicode 5.0.0).
 *
 * The table wspg_XY denotes the whitespace characters in the Unicode
 * range U+XY00 to U+XYFF (where XY is the hexadecimal upper byte of
 * the 16-bit Unicode code point value).
 */

static const Bool wspg_00[256] =
{
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   TRUE,  FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, TRUE,  FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   /*
    * TODO: U+00A0 (no-break space) is not treated as whitespace by
    * ICU's UnicodeString::trim().  Do we want to do the same?
    */
   TRUE,  FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
};

static const Bool wspg_16[256] =
{
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   TRUE,  FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
};

static const Bool wspg_18[256] =
{
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE,  FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
};

static const Bool wspg_20[256] =
{
   TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,
   TRUE,  TRUE,  TRUE,  FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   TRUE,  TRUE,  FALSE, FALSE, FALSE, FALSE, FALSE, TRUE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
};

static const Bool wspg_30[256] =
{
   TRUE,  FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
   FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
};

static const Bool *whitespacePages[256] =
{
   wspg_00, NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    wspg_16, NULL,
   wspg_18, NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   wspg_20, NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   wspg_30, NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
   NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,    NULL,
};

typedef enum {
   UNICODE_TRIMLEFT  = 0x1,
   UNICODE_TRIMRIGHT = 0x2,
   UNICODE_TRIMBOTH  = (UNICODE_TRIMLEFT | UNICODE_TRIMRIGHT),
} UnicodeTrimSide;


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeSimpleIsWhiteSpace --
 *
 *      Checks if the UTF-16 code unit represents white space.
 *
 * Results:
 *      TRUE if the code unit represents white space, FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static INLINE Bool
UnicodeSimpleIsWhiteSpace(utf16_t codeUnit) // IN
{
   const Bool *page = whitespacePages[codeUnit >> 8];

   if (!page) {
      return FALSE;
   }

   return page[codeUnit & 0xFF];
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_FoldCase --
 *
 *      Creates a Unicode string with standardized case by performing
 *      simple case folding (upper-case, then lower-case) on the
 *      input string.
 *
 * Results:
 *      The allocated Unicode string.  Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_FoldCase(const char *str) // IN
{
   char *folded;
   utf16_t *utf16;
   utf16_t *utf16Current;

   ASSERT(str);

   utf16 = Unicode_GetAllocBytes(str, STRING_ENCODING_UTF16);

   utf16Current = utf16;
   while (*utf16Current) {
      *utf16Current = UnicodeSimpleCaseFold(*utf16Current);
      utf16Current++;
   }

   folded = Unicode_AllocWithUTF16(utf16);
   free(utf16);

   return folded;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeTrimInternal --
 *
 *      Creates a Unicode string by trimming whitespace from the beginning
 *      and/or end of the input string, depending on the input parameter "side".
 *
 * Results:
 *      The allocated Unicode string.  Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char *
UnicodeTrimInternal(const char *str,       // IN
                    UnicodeTrimSide side)  // IN
{
   char *trimmed;
   utf16_t *utf16;
   utf16_t *utf16Start;
   utf16_t *utf16End;

   ASSERT(str);

   utf16 = Unicode_GetAllocBytes(str, STRING_ENCODING_UTF16);
   utf16Start = utf16;
   utf16End = utf16 + Unicode_UTF16Strlen(utf16);

   if (side & UNICODE_TRIMLEFT) {
      while (utf16Start != utf16End && UnicodeSimpleIsWhiteSpace(*utf16Start)) {
         utf16Start++;
      }
   }

   if (side & UNICODE_TRIMRIGHT) {
      while (utf16End != utf16Start && UnicodeSimpleIsWhiteSpace(*(utf16End - 1))) {
         utf16End--;
      }
   }

   *utf16End = 0;

   trimmed = Unicode_AllocWithUTF16(utf16Start);
   free(utf16);

   return trimmed;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Trim --
 *
 *      Creates a Unicode string by trimming whitespace from the beginning
 *      and end of the input string.
 *
 * Results:
 *      The allocated Unicode string.  Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_Trim(const char *str) // IN
{
   return UnicodeTrimInternal(str, UNICODE_TRIMBOTH);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_TrimLeft --
 *
 *      Creates a Unicode string by trimming whitespace from the beginning of
 *      the input string.
 *
 * Results:
 *      The allocated Unicode string.  Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_TrimLeft(const char *str) // IN
{
   return UnicodeTrimInternal(str, UNICODE_TRIMLEFT);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_TrimRight --
 *
 *      Creates a Unicode string by trimming whitespace from the end of the 
 *      input string.
 *
 * Results:
 *      The allocated Unicode string.  Caller must free with free().
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
Unicode_TrimRight(const char *str) // IN
{
   return UnicodeTrimInternal(str, UNICODE_TRIMRIGHT);
}
  070701000001FB000081A40000000000000000000000016822550500017F8A000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeSimpleTypes.c   /*********************************************************
 * Copyright (C) 2007-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeSimpleTypes.c --
 *
 *      Basic types and cache handling for simple UTF-8 implementation
 *      of Unicode library interface.
 */

#include <ctype.h>
#include <string.h>
#include <stdio.h>

#include "unicodeBase.h"
#include "unicodeInt.h"
#include "codeset.h"

#include "vm_assert.h"
#include "util.h"
#include "hashTable.h"
#include "vm_atomic.h"

static char *UnicodeNormalizeEncodingName(const char *encoding);

/*
 * Cross reference of IANA character set names, windows code pages
 * and ICU encodings
 *
 * See: http://www.iana.org/assignments/character-sets
 *      http://msdn2.microsoft.com/en-us/library/ms776446(VS.85).aspx
 *      http://demo.icu-project.org/icu-bin/convexp
 *
 * If you add new StringEncodings to this table, you must keep the
 * StringEncoding enum in lib/public/unicodeTypes.h in sync!
 */

#define MAXCHARSETNAMES 21
#define MIBUNDEF (-1)
#define WINUNDEF (-1)
#define SUPPORTED TRUE
#define UNSUPPORTED FALSE
#define IN_FULL_ICU FALSE

/*
 * Thread-safe hash table to speed up encoding name -> IANA table
 * index lookups.
 */
static Atomic_Ptr unicodeHashTablePtr;
static HashTable *unicodeEncCache = NULL;

static struct xRef {
   int MIBenum;                   // Assigned by IANA
   int winACP;                    // Windows code page from GetACP()
   StringEncoding encoding;       // ICU encoding enum
   Bool isSupported;              // VMware supported encoding
   int preferredMime;             // Index of preferred MIME name
   const char *names[MAXCHARSETNAMES];  // Encoding name and aliases
} xRef[] = {
   /*
    * Source: ECMA registry
    */
   { 3, 20127, STRING_ENCODING_US_ASCII, SUPPORTED, 4,
      { "ANSI_X3.4-1968", "iso-ir-6", "ANSI_X3.4-1986", "ISO_646.irv:1991",
        "ASCII", "ISO646-US", "US-ASCII", "us", "IBM367", "cp367", "csASCII",
        "646", "ascii7", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 4, 28591, STRING_ENCODING_ISO_8859_1, SUPPORTED, 3,
      { "ISO_8859-1:1987", "iso-ir-100", "ISO_8859-1", "ISO-8859-1", "latin1",
        "l1", "IBM819", "CP819", "csISOLatin1", "8859_1", "819", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 5, 28592, STRING_ENCODING_ISO_8859_2, SUPPORTED, 3,
      { "ISO_8859-2:1987", "iso-ir-101", "ISO_8859-2", "ISO-8859-2", "latin2",
        "l2", "csISOLatin2", "ibm-912_P100-1995", "ibm-912", "8859_2", "cp912",
        "912", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 6, 28593, STRING_ENCODING_ISO_8859_3, SUPPORTED, 3,
      { "ISO_8859-3:1988", "iso-ir-109", "ISO_8859-3", "ISO-8859-3", "latin3",
        "l3", "csISOLatin3", "ibm-913_P100-2000", "ibm-913", "8859_3", "cp913",
        "913", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 7, 28594, STRING_ENCODING_ISO_8859_4, SUPPORTED, 3,
      { "ISO_8859-4:1988", "iso-ir-110", "ISO_8859-4", "ISO-8859-4", "latin4",
        "l4", "csISOLatin4", "ibm-914_P100-1995", "ibm-914", "8859_4", "cp914",
        "914", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 8, 28595, STRING_ENCODING_ISO_8859_5, SUPPORTED, 3,
      { "ISO_8859-5:1988", "iso-ir-144", "ISO_8859-5", "ISO-8859-5", "cyrillic",
        "csISOLatinCyrillic", "ibm-915_P100-1995", "ibm-915", "8859_5", "cp915",
        "915", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 9, 28596, STRING_ENCODING_ISO_8859_6, SUPPORTED, 3,
      { "ISO_8859-6:1987", "iso-ir-127", "ISO_8859-6", "ISO-8859-6", "ECMA-114",
        "ASMO-708", "arabic", "csISOLatinArabic", "ibm-1089_P100-1995",
        "ibm-1089", "8859_6", "cp1089", "1089", NULL }
   },
   /*
    * Source: Windows duplicate of ISO-8859-6
    */
   { 9, 708, STRING_ENCODING_ISO_8859_6, SUPPORTED, 0,
      { "ASMO-708", NULL }
   },
   /*
    * Source: ECMA registry, ICU almost completely duplicates this entry with
    *         ibm-813 (see below), which is an older version
    */
   { 10, 28597, STRING_ENCODING_ISO_8859_7, SUPPORTED, 3,
      { "ISO_8859-7:1987", "iso-ir-126", "ISO_8859-7", "ISO-8859-7", "ELOT_928",
        "ECMA-118", "greek", "greek8", "csISOLatinGreek", "ibm-9005_X110-2007",
        "ibm-9005", "sun_eu_greek", NULL }
   },
   /*
    * Source: ICU
    */
   { MIBUNDEF, WINUNDEF, STRING_ENCODING_IBM_813, IN_FULL_ICU, 0,
      { "ibm-813_P100-1995", "ibm-813", "cp813", "813", "8859_7", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 11, 28598, STRING_ENCODING_ISO_8859_8, SUPPORTED, 3,
      { "ISO_8859-8:1988", "iso-ir-138", "ISO_8859-8", "ISO-8859-8", "hebrew",
        "csISOLatinHebrew", "ibm-5012_P100-1999", "ibm-5012", "8859_8", 
        "hebrew8", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 12, 28599, STRING_ENCODING_ISO_8859_9, SUPPORTED, 3,
      { "ISO_8859-9:1989", "iso-ir-148", "ISO_8859-9", "ISO-8859-9", "latin5",
        "l5", "csISOLatin5", "ibm-920_P100-1995", "ibm-920", "8859_9", "cp920",
        "920", "ECMA-128", "turkish", "turkish8", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 13, WINUNDEF, STRING_ENCODING_ISO_8859_10, SUPPORTED, 0,
      { "ISO-8859-10", "iso-ir-157", "l6", "ISO_8859-10:1992", "csISOLatin6",
        "latin6", "iso-8859_10-1998", NULL }
   },
   /*
    * Source: ECMA registry and ISO 6937-2:1983, not supported by ICU
    */
   { 14, WINUNDEF, STRING_ENCODING_ISO_6937_2_ADD, UNSUPPORTED, 0,
      { "ISO_6937-2-add", "iso-ir-142", "csISOTextComm", NULL }
   },
   /*
    * Source: JIS X 0201-1976.   One byte only, this is equivalent to 
    *         JIS/Roman (similar to ASCII) plus eight-bit half-width 
    *         Katakana
    */
   { 15, WINUNDEF, STRING_ENCODING_JIS_X0201, IN_FULL_ICU, 0,
      { "JIS_X0201", "X0201", "csHalfWidthKatakana", NULL }
   },
   /*
    * Source: JIS X 0202-1991.  Uses ISO 2022 escape sequences to
    *         shift code sets as documented in JIS X 0202-1991.
    *         ICU maps this to ISO-2022-JP-1
    */
   { 16, WINUNDEF, STRING_ENCODING_JIS_ENCODING, IN_FULL_ICU, 0,
      { "JIS_Encoding", "csJISEncoding", "JIS", NULL }
   },
   /*
    * Source: This charset is an extension of csHalfWidthKatakana by
    *         adding graphic characters in JIS X 0208.  The CCS's are
    *         JIS X0201:1997 and JIS X0208:1997.  The
    *         complete definition is shown in Appendix 1 of JIS
    *         X0208:1997.
    *         This charset can be used for the top-level media type "text".
    */
   { 17, 932, STRING_ENCODING_SHIFT_JIS, SUPPORTED, 0,
      { "Shift_JIS", "MS_Kanji", "csShiftJIS", "ibm-943_P15A-2003", "ibm-943",
        "x-sjis", "x-ms-cp932", "cp932", "cp943c", "IBM-943C", "ms932", "pck",
        "sjis", "ibm-943_VSUB_VPUA", NULL }
   },
   /*
    * Source: ICU, older version of Shift_JIS, use newer version above for
    *         common entries between the two
    *
    */
   { MIBUNDEF, WINUNDEF, STRING_ENCODING_IBM_943_P130_1999, SUPPORTED, 0,
      { "ibm-943_P130-1999", "cp943", "943", "ibm-943_VASCII_VSUB_VPUA",
         NULL }
   },
   /*
    * Source: Standardized by OSF, UNIX International, and UNIX Systems
    *         Laboratories Pacific.  Uses ISO 2022 rules to select
    *                code set 0: US-ASCII (a single 7-bit byte set)
    *                code set 1: JIS X0208-1990 (a double 8-bit byte set)
    *                            restricted to A0-FF in both bytes
    *                code set 2: Half Width Katakana (a single 7-bit byte set)
    *                            requiring SS2 as the character prefix
    *                code set 3: JIS X0212-1990 (a double 7-bit byte set)
    *                            restricted to A0-FF in both bytes
    *                            requiring SS3 as the character prefix
    */
   { 18, 20932, STRING_ENCODING_EUC_JP, IN_FULL_ICU, 2,
      { "Extended_UNIX_Code_Packed_Format_for_Japanese", "csEUCPkdFmtJapanese",
        "EUC-JP", "ibm-954_P101-2007", "ibm-954", "X-EUC-JP", "eucjis", "ujis", 
         NULL }
   },
   /*
    * Windows duplicate and older ICU version of EUC-JP
    */
   { 18, 51932, STRING_ENCODING_IBM_33722, IN_FULL_ICU, 0,
      { "ibm-33722_P12A_P12A-2004_U2", "ibm-33722", "ibm-5050", "ibm-33722_VPUA",
        "IBM-eucJP", NULL }
   },
   /*
    * Source: Used in Japan.  Each character is 2 octets.
    *                 code set 0: US-ASCII (a single 7-bit byte set)
    *                               1st byte = 00
    *                               2nd byte = 20-7E
    *                 code set 1: JIS X0208-1990 (a double 7-bit byte set)
    *                             restricted  to A0-FF in both bytes 
    *                 code set 2: Half Width Katakana (a single 7-bit byte set)
    *                               1st byte = 00
    *                               2nd byte = A0-FF
    *                 code set 3: JIS X0212-1990 (a double 7-bit byte set)
    *                             restricted to A0-FF in 
    *                             the first byte
    *                 and 21-7E in the second byte
    *
    *           Not supported by ICU
    */
   { 19, WINUNDEF, STRING_ENCODING_EXTENDED_UNIX_CODE_FIXED_WIDTH_FOR_JAPANESE,
     UNSUPPORTED, 0,
      { "Extended_UNIX_Code_Fixed_Width_for_Japanese", "csEUCFixWidJapanese",
         NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 20, WINUNDEF, STRING_ENCODING_BS_4730, IN_FULL_ICU, 0,
      { "BS_4730", "iso-ir-4", "ISO646-GB", "gb", "uk", "csISO4UnitedKingdom",
         NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 21, WINUNDEF, STRING_ENCODING_SEN_850200_C, UNSUPPORTED, 0,
      { "SEN_850200_C", "iso-ir-11", "ISO646-SE2", "se2",
        "csISO11SwedishForNames", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 22, WINUNDEF, STRING_ENCODING_IT, IN_FULL_ICU, 0,
      { "IT", "iso-ir-15", "ISO646-IT", "csISO15Italian", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 23, WINUNDEF, STRING_ENCODING_ES, IN_FULL_ICU, 0,
      { "ES", "iso-ir-17", "ISO646-ES", "csISO17Spanish", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 24, WINUNDEF, STRING_ENCODING_DIN_66003, IN_FULL_ICU, 0,
      { "DIN_66003", "iso-ir-21", "de", "ISO646-DE", "csISO21German", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 25, WINUNDEF, STRING_ENCODING_NS_4551_1, IN_FULL_ICU, 0,
      { "NS_4551-1", "iso-ir-60", "ISO646-NO", "no", "csISO60DanishNorwegian",
        "csISO60Norwegian1", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 26, WINUNDEF, STRING_ENCODING_NF_Z_62_010, IN_FULL_ICU, 0,
      { "NF_Z_62-010", "iso-ir-69", "ISO646-FR", "fr", "csISO69French", NULL }
   },
   /*
    * Source: Universal Transfer Format (1), this is the multibyte
    *         encoding, that subsets ASCII-7. It does not have byte
    *         ordering issues.
    *
    *         Not supported by ICU.
    */
   { 27, WINUNDEF, STRING_ENCODING_ISO_10646_UTF_1, UNSUPPORTED, 0,
      { "ISO-10646-UTF-1", "csISO10646UTF1", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 28, WINUNDEF, STRING_ENCODING_ISO_646_BASIC_1983, UNSUPPORTED, 0,
      { "ISO_646.basic:1983", "ref", "csISO646basic1983", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 29, WINUNDEF, STRING_ENCODING_INVARIANT, UNSUPPORTED, 0,
      { "INVARIANT", "csINVARIANT", NULL }
   },
   /*
    * Source: ECMA registry, ICU maps this to ASCII
    */
   { 30, WINUNDEF, STRING_ENCODING_ISO_646_IRV_1983, SUPPORTED, 0,
      { "ISO_646.irv:1983", "iso-ir-2", "irv", "csISO2IntlRefVersion", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 31, WINUNDEF, STRING_ENCODING_NATS_SEFI, UNSUPPORTED, 0,
      { "NATS-SEFI", "iso-ir-8-1", "csNATSSEFI", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 32, WINUNDEF, STRING_ENCODING_NATS_SEFI_ADD, UNSUPPORTED, 0,
      { "NATS-SEFI-ADD", "iso-ir-8-2", "csNATSSEFIADD", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 33, WINUNDEF, STRING_ENCODING_NATS_DANO, UNSUPPORTED, 0,
      { "NATS-DANO", "iso-ir-9-1", "csNATSDANO", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 34, WINUNDEF, STRING_ENCODING_NATS_DANO_ADD, UNSUPPORTED, 0,
      { "NATS-DANO-ADD", "iso-ir-9-2", "csNATSDANOADD", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 35, WINUNDEF, STRING_ENCODING_SEN_850200_B, IN_FULL_ICU, 0,
      { "SEN_850200_B", "iso-ir-10", "FI", "ISO646-FI", "ISO646-SE", "se",
        "csISO10Swedish", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 36, 51949, STRING_ENCODING_KS_C_5601_1987, SUPPORTED, 0,
      { "KS_C_5601-1987", "ibm-970_P110_P110-2006_U2", "ibm-970", "EUC-KR",
        "csEUCKR", "ibm-eucKR", "KSC_5601", "5601", "cp970", "970", 
        "ibm-970_VPUA", NULL }
   },
   /*
    * Windows-949 code page for KS_C_5601
    */
   { 36, 949, STRING_ENCODING_WINDOWS_949, SUPPORTED, 0,
      { "windows-949-2000", "KS_C_5601-1989", "KS_C_5601-1987", "KSC_5601", 
        "csKSC56011987", "korean", "iso-ir-149", "ms949", NULL }
   },
   /*
    * Another ICU converter for KS_C_5601
    */
   { 36, WINUNDEF, STRING_ENCODING_IBM_1363, SUPPORTED, 0,
      { "ibm-1363_P11B-1998", "ibm-1363", "cp1363", "5601", "ksc",
        "ibm-1363_VSUB_VPUA", NULL }
   },
   /*
    * Source: RFC-1557 (see also KS_C_5601-1987)
    */
   { 37, 50225, STRING_ENCODING_ISO_2022_KR, IN_FULL_ICU, 0,
      { "ISO-2022-KR", "csISO2022KR", NULL }
   },
   /*
    * Source: RFC-1468 (see also RFC-2237)
    *         Windows-50221 and 50222 are routed here
    */
   { 39, 50220, STRING_ENCODING_ISO_2022_JP, SUPPORTED, 0,
      { "ISO-2022-JP", "csISO2022JP", NULL }
   },
   /*
    * Source: VMware
    */
   { MIBUNDEF, WINUNDEF, STRING_ENCODING_ISO_2022_JP_1, 
     IN_FULL_ICU, 0,
      { "ISO-2022-JP-1", "ibm-5054", NULL }
   },
   /*
    * Source: RFC-1554
    */
   { 40, WINUNDEF, STRING_ENCODING_ISO_2022_JP_2, IN_FULL_ICU, 0,
      { "ISO-2022-JP-2", "csISO2022JP2", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 41, WINUNDEF, STRING_ENCODING_JIS_C6220_1969_JP, UNSUPPORTED, 0,
      { "JIS_C6220-1969-jp", "JIS_C6220-1969", "iso-ir-13", "katakana",
        "x0201-7", "csISO13JISC6220jp", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 42, WINUNDEF, STRING_ENCODING_JIS_C6220_1969_RO, UNSUPPORTED, 0,
      { "JIS_C6220-1969-ro", "iso-ir-14", "jp", "ISO646-JP",
        "csISO14JISC6220ro", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 43, WINUNDEF, STRING_ENCODING_PT, UNSUPPORTED, 0,
      { "PT", "iso-ir-16", "ISO646-PT", "csISO16Portuguese", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 44, WINUNDEF, STRING_ENCODING_GREEK7_OLD, UNSUPPORTED, 0,
      { "greek7-old", "iso-ir-18", "csISO18Greek7Old", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 45, WINUNDEF, STRING_ENCODING_LATIN_GREEK, UNSUPPORTED, 0,
      { "latin-greek", "iso-ir-19", "csISO19LatinGreek", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 46, WINUNDEF, STRING_ENCODING_NF_Z_62_010__1973_, IN_FULL_ICU, 0,
      { "NF_Z_62-010_(1973)", "iso-ir-25", "ISO646-FR1", "csISO25French", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 47, WINUNDEF, STRING_ENCODING_LATIN_GREEK_1, UNSUPPORTED, 0,
      { "Latin-greek-1", "iso-ir-27", "csISO27LatinGreek1", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 48, WINUNDEF, STRING_ENCODING_ISO_5427, UNSUPPORTED, 0,
      { "ISO_5427", "iso-ir-37", "csISO5427Cyrillic", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 49, WINUNDEF, STRING_ENCODING_JIS_C6226_1978, UNSUPPORTED, 0,
      { "JIS_C6226-1978", "iso-ir-42", "csISO42JISC62261978", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 50, WINUNDEF, STRING_ENCODING_BS_VIEWDATA, UNSUPPORTED, 0,
      { "BS_viewdata", "iso-ir-47", "csISO47BSViewdata", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 51, WINUNDEF, STRING_ENCODING_INIS, UNSUPPORTED, 0,
      { "INIS", "iso-ir-49", "csISO49INIS", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 52, WINUNDEF, STRING_ENCODING_INIS_8, UNSUPPORTED, 0,
      { "INIS-8", "iso-ir-50", "csISO50INIS8", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 53, WINUNDEF, STRING_ENCODING_INIS_CYRILLIC, UNSUPPORTED, 0,
      { "INIS-cyrillic", "iso-ir-51", "csISO51INISCyrillic", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 54, WINUNDEF, STRING_ENCODING_ISO_5427_1981, UNSUPPORTED, 0,
      { "ISO_5427:1981", "iso-ir-54", "ISO5427Cyrillic1981", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 55, WINUNDEF, STRING_ENCODING_ISO_5428_1980, UNSUPPORTED, 0,
      { "ISO_5428:1980", "iso-ir-55", "csISO5428Greek", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 56, WINUNDEF, STRING_ENCODING_GB_1988_80, UNSUPPORTED, 0,
      { "GB_1988-80", "iso-ir-57", "cn", "ISO646-CN", "csISO57GB1988", NULL }
   },
   /*
    * Source: ECMA registry
    *         Note that this encoding does not support ASCII as a subset
    */
   { 57, 20936, STRING_ENCODING_GB_2312_80, IN_FULL_ICU, 0,
      { "GB_2312-80", "iso-ir-58", "chinese", "csISO58GB231280", 
        "ibm-5478_P100-1995", "ibm-5478", "gb2312-1980", "GB2312.1980-0", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 58, WINUNDEF, STRING_ENCODING_NS_4551_2, UNSUPPORTED, 0,
      { "NS_4551-2", "ISO646-NO2", "iso-ir-61", "no2", "csISO61Norwegian2", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 59, WINUNDEF, STRING_ENCODING_VIDEOTEX_SUPPL, UNSUPPORTED, 0,
      { "videotex-suppl", "iso-ir-70", "csISO70VideotexSupp1", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 60, WINUNDEF, STRING_ENCODING_PT2, IN_FULL_ICU, 0,
      { "PT2", "iso-ir-84", "ISO646-PT2", "csISO84Portuguese2", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 61, WINUNDEF, STRING_ENCODING_ES2, IN_FULL_ICU, 0,
      { "ES2", "iso-ir-85", "ISO646-ES2", "csISO85Spanish2", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 62, WINUNDEF, STRING_ENCODING_MSZ_7795_3, UNSUPPORTED, 0,
      { "MSZ_7795.3", "iso-ir-86", "ISO646-HU", "hu", "csISO86Hungarian", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 63, WINUNDEF, STRING_ENCODING_JIS_C6226_1983, UNSUPPORTED, 0,
      { "JIS_C6226-1983", "iso-ir-87", "x0208", "JIS_X0208-1983",
        "csISO87JISX0208", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 64, WINUNDEF, STRING_ENCODING_GREEK7, UNSUPPORTED, 0,
      { "greek7", "iso-ir-88", "csISO88Greek7", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 65, WINUNDEF, STRING_ENCODING_ASMO_449, UNSUPPORTED, 0,
      { "ASMO_449", "ISO_9036", "arabic7", "iso-ir-89", "csISO89ASMO449", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 66, WINUNDEF, STRING_ENCODING_ISO_IR_90, UNSUPPORTED, 0,
      { "iso-ir-90", "csISO90", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 67, WINUNDEF, STRING_ENCODING_JIS_C6229_1984_A, UNSUPPORTED, 0,
      { "JIS_C6229-1984-a", "iso-ir-91", "jp-ocr-a", "csISO91JISC62291984a",
         NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 68, WINUNDEF, STRING_ENCODING_JIS_C6229_1984_B, UNSUPPORTED, 0,
      { "JIS_C6229-1984-b", "iso-ir-92", "ISO646-JP-OCR-B", "jp-ocr-b",
        "csISO92JISC62991984b", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 69, WINUNDEF, STRING_ENCODING_JIS_C6229_1984_B_ADD, UNSUPPORTED, 0,
      { "JIS_C6229-1984-b-add", "iso-ir-93", "jp-ocr-b-add",
        "csISO93JIS62291984badd", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 70, WINUNDEF, STRING_ENCODING_JIS_C6229_1984_HAND, UNSUPPORTED, 0,
      { "JIS_C6229-1984-hand", "iso-ir-94", "jp-ocr-hand",
        "csISO94JIS62291984hand", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 71, WINUNDEF, STRING_ENCODING_JIS_C6229_1984_HAND_ADD, UNSUPPORTED, 0,
      { "JIS_C6229-1984-hand-add", "iso-ir-95", "jp-ocr-hand-add",
        "csISO95JIS62291984handadd", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 72, WINUNDEF, STRING_ENCODING_JIS_C6229_1984_KANA, UNSUPPORTED, 0,
      { "JIS_C6229-1984-kana", "iso-ir-96", "csISO96JISC62291984kana", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 73, WINUNDEF, STRING_ENCODING_ISO_2033_1983, UNSUPPORTED, 0,
      { "ISO_2033-1983", "iso-ir-98", "e13b", "csISO2033", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 74, WINUNDEF, STRING_ENCODING_ANSI_X3_110_1983, UNSUPPORTED, 0,
      { "ANSI_X3.110-1983", "iso-ir-99", "CSA_T500-1983", "NAPLPS",
        "csISO99NAPLPS", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 75, WINUNDEF, STRING_ENCODING_T_61_7BIT, UNSUPPORTED, 0,
      { "T.61-7bit", "iso-ir-102", "csISO102T617bit", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 76, 20261, STRING_ENCODING_T_61_8BIT, UNSUPPORTED, 0,
      { "T.61-8bit", "T.61", "iso-ir-103", "csISO103T618bit", NULL }
   },
   /*
    * Source: ISO registry (formerly ECMA registry)
    *          http://www.itscj.ipsj.jp/ISO-IR/111.pdf
    *         Not supported by ICU
    */
   { 77, WINUNDEF, STRING_ENCODING_ECMA_CYRILLIC, UNSUPPORTED, 0,
      { "ECMA-cyrillic", "iso-ir-111", "KOI8-E", "csISO111ECMACyrillic", NULL }
   },
   /*
    * Source: ECMA registry
    */
   { 78, WINUNDEF, STRING_ENCODING_CSA_Z243_4_1985_1, IN_FULL_ICU, 0,
      { "CSA_Z243.4-1985-1", "iso-ir-121", "ISO646-CA", "csa7-1", "ca",
        "csISO121Canadian1", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 79, WINUNDEF, STRING_ENCODING_CSA_Z243_4_1985_2, UNSUPPORTED, 0,
      { "CSA_Z243.4-1985-2", "iso-ir-122", "ISO646-CA2", "csa7-2",
        "csISO122Canadian2", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 80, WINUNDEF, STRING_ENCODING_CSA_Z243_4_1985_GR, UNSUPPORTED, 0,
      { "CSA_Z243.4-1985-gr", "iso-ir-123", "csISO123CSAZ24341985gr", NULL }
   },
   /*
    * Source: RFC1556, ICU maps this to ISO-8859-6
    */
   { 81, WINUNDEF, STRING_ENCODING_ISO_8859_6_E, SUPPORTED, 2,
      { "ISO_8859-6-E", "csISO88596E", "ISO-8859-6-E", NULL }
   },
   /*
    * Source: RFC1556, ICU maps this to ISO-8859-6
    */
   { 82, WINUNDEF, STRING_ENCODING_ISO_8859_6_I, SUPPORTED, 2,
      { "ISO_8859-6-I", "csISO88596I", "ISO-8859-6-I", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 83, WINUNDEF, STRING_ENCODING_T_101_G2, UNSUPPORTED, 0,
      { "T.101-G2", "iso-ir-128", "csISO128T101G2", NULL }
   },
   /*
    * Source: RFC1556, ICU maps this to ISO-8859-8
    */
   { 84, WINUNDEF, STRING_ENCODING_ISO_8859_8_E, SUPPORTED, 2,
      { "ISO_8859-8-E", "csISO88598E", "ISO-8859-8-E", NULL }
   },
   /*
    * Source: RFC1556, ICU maps this to ISO-8859-8
    */
   { 85, WINUNDEF, STRING_ENCODING_ISO_8859_8_I, SUPPORTED, 2,
      { "ISO_8859-8-I", "csISO88598I", "ISO-8859-8-I", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 86, WINUNDEF, STRING_ENCODING_CSN_369103, UNSUPPORTED, 0,
      { "CSN_369103", "iso-ir-139", "csISO139CSN369103", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 87, WINUNDEF, STRING_ENCODING_JUS_I_B1_002, UNSUPPORTED, 0,
      { "JUS_I.B1.002", "iso-ir-141", "ISO646-YU", "js", "yu",
        "csISO141JUSIB1002", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 88, WINUNDEF, STRING_ENCODING_IEC_P27_1, UNSUPPORTED, 0,
      { "IEC_P27-1", "iso-ir-143", "csISO143IECP271", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 89, WINUNDEF, STRING_ENCODING_JUS_I_B1_003_SERB, UNSUPPORTED, 0,
      { "JUS_I.B1.003-serb", "iso-ir-146", "serbian", "csISO146Serbian", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 90, WINUNDEF, STRING_ENCODING_JUS_I_B1_003_MAC, UNSUPPORTED, 0,
      { "JUS_I.B1.003-mac", "macedonian", "iso-ir-147", "csISO147Macedonian",
         NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 91, WINUNDEF, STRING_ENCODING_GREEK_CCITT, UNSUPPORTED, 0,
      { "greek-ccitt", "iso-ir-150", "csISO150", "csISO150GreekCCITT", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 92, WINUNDEF, STRING_ENCODING_NC_NC00_10_81, UNSUPPORTED, 0,
      { "NC_NC00-10:81", "cuba", "iso-ir-151", "ISO646-CU", "csISO151Cuba",
         NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 93, WINUNDEF, STRING_ENCODING_ISO_6937_2_25, UNSUPPORTED, 0,
      { "ISO_6937-2-25", "iso-ir-152", "csISO6937Add", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 94, WINUNDEF, STRING_ENCODING_GOST_19768_74, UNSUPPORTED, 0,
      { "GOST_19768-74", "ST_SEV_358-88", "iso-ir-153", "csISO153GOST1976874",
         NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 95, WINUNDEF, STRING_ENCODING_ISO_8859_SUPP, UNSUPPORTED, 0,
      { "ISO_8859-supp", "iso-ir-154", "latin1-2-5", "csISO8859Supp", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 96, WINUNDEF, STRING_ENCODING_ISO_10367_BOX, UNSUPPORTED, 0,
      { "ISO_10367-box", "iso-ir-155", "csISO10367Box", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 97, WINUNDEF, STRING_ENCODING_LATIN_LAP, UNSUPPORTED, 0,
      { "latin-lap", "lap", "iso-ir-158", "csISO158Lap", NULL }
   },
   /*
    * Source: ECMA registry, not supported by ICU
    */
   { 98, WINUNDEF, STRING_ENCODING_JIS_X0212_1990, UNSUPPORTED, 0,
      { "JIS_X0212-1990", "x0212", "iso-ir-159", "csISO159JISX02121990", NULL }
   },
   /*
    * Source: Danish Standard, DS 2089, February 1974
    */
   { 99, WINUNDEF, STRING_ENCODING_DS_2089, IN_FULL_ICU, 0,
      { "DS_2089", "DS2089", "ISO646-DK", "dk", "csISO646Danish", NULL }
   },
   { 100, WINUNDEF, STRING_ENCODING_US_DK, UNSUPPORTED, 0,
      { "us-dk", "csUSDK", NULL }
   },
   { 101, WINUNDEF, STRING_ENCODING_DK_US, UNSUPPORTED, 0,
      { "dk-us", "csDKUS", NULL }
   },
   { 102, WINUNDEF, STRING_ENCODING_KSC5636, UNSUPPORTED, 0,
      { "KSC5636", "ISO646-KR", "csKSC5636", NULL }
   },
   /*
    * Source: RFC 1642, not supported by ICU
    */
   { 103, WINUNDEF, STRING_ENCODING_UNICODE_1_1_UTF_7, UNSUPPORTED, 0,
      { "UNICODE-1-1-UTF-7", "csUnicode11UTF7", NULL }
   },
   /*
    * Source: RFC-1922
    */
   { 104, 50227, STRING_ENCODING_ISO_2022_CN, IN_FULL_ICU, 0,
      { "ISO-2022-CN", "csISO2022CN", NULL }
   },
   /*
    * Source: RFC-1922
    */
   { 105, WINUNDEF, STRING_ENCODING_ISO_2022_CN_EXT, IN_FULL_ICU, 0,
      { "ISO-2022-CN-EXT", NULL }
   },
   /*
    * Source: RFC 3629
    */
   { 106, 65001, STRING_ENCODING_UTF8, SUPPORTED, 0,
      { "UTF-8", "ibm-1208", "ibm-1209", "ibm-5304", "ibm-5305", "ibm-13496",
        "ibm-13497", "ibm-17592", "ibm-17593", "cp1208", NULL }
   },
   /*
    * Source: ISO See 
    *     (http://www.iana.org/assignments/charset-reg/ISO-8859-13)[Tumasonis] 
    */
   { 109, 28603, STRING_ENCODING_ISO_8859_13, SUPPORTED, 0,
      { "ISO-8859-13", "ibm-921_P100-1995", "ibm-921", "8859_13", "cp921",
        "921", NULL }
   },
   /*
    * Source: ISO See
    *      (http://www.iana.org/assignments/charset-reg/ISO-8859-14) [Simonsen]
    */
   { 110, WINUNDEF, STRING_ENCODING_ISO_8859_14, SUPPORTED, 0,
      { "ISO-8859-14", "iso-ir-199", "ISO_8859-14:1998", "ISO_8859-14",
        "latin8", "iso-celtic", "l8", NULL }
   },
   /*
    * Source: ISO 
    *     Please see: <http://www.iana.org/assignments/charset-reg/ISO-8859-15>
    */
   { 111, 28605, STRING_ENCODING_ISO_8859_15, SUPPORTED, 0,
      { "ISO-8859-15", "ISO_8859-15", "Latin-9", "ibm-923_P100-1998", "ibm-923",
        "l9", "8859_15", "latin0", "csisolatin0", "csisolatin9", "cp923", "923",
        "iso8859_15_fdis", NULL }
   },
   /*
    * Windows duplicate of ISO-8859-15
    * windows-874: ANSI/OEM Thai
    */
   { 111, 874, STRING_ENCODING_IBM_874, SUPPORTED, 0,
      { "ibm-874", "ibm-874_P100-1995", "cp874", "ibm-9066", "TIS-620",
        "tis620.2533", "eucTH", NULL }
   },
   /*
    * Source: ISO
    */
   { 112, WINUNDEF, STRING_ENCODING_ISO_8859_16, IN_FULL_ICU, 0,
      { "ISO-8859-16", "iso-ir-226", "ISO_8859-16:2001", "ISO_8859-16",
        "latin10", "l10", NULL }
   },
   /*
    * Source: Chinese IT Standardization Technical Committee  
    *         Please see: <http://www.iana.org/assignments/charset-reg/GBK>
    */
   { 113, 936, STRING_ENCODING_GBK, SUPPORTED, 0,
      { "GBK", "CP936", "MS936", "windows-936", "windows-936-2000", NULL }
   },
   /*
    * Alternate ICU encoding for Windows-936
    */
   { MIBUNDEF, WINUNDEF, STRING_ENCODING_IBM_1386, SUPPORTED, 1,
      { "ibm-1386_P100-2001", "ibm-1386", "cp1386", "ibm-1386_VSUB_VPUA", NULL }
   },
   /*
    * Source: Chinese IT Standardization Technical Committee
    *         Please see: <http://www.iana.org/assignments/charset-reg/GB18030>
    */
   { 114, 54936, STRING_ENCODING_GB_18030, IN_FULL_ICU, 0,
      { "GB18030", "ibm-1392", NULL }
   },
   /*
    * Source:  Fujitsu-Siemens standard mainframe EBCDIC encoding
    *          Please see:
    *           <http://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF04-15
    *          Not supported by ICU
    */
   { 115, WINUNDEF, STRING_ENCODING_OSD_EBCDIC_DF04_15, UNSUPPORTED, 0,
      { "OSD_EBCDIC_DF04_15", NULL }
   },
   /*
    * Source:  Fujitsu-Siemens standard mainframe EBCDIC encoding
    *          Please see:
    *          <http://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF03-IRV>
    *          Not supported by ICU
    */
   { 116, WINUNDEF, STRING_ENCODING_OSD_EBCDIC_DF03_IRV, UNSUPPORTED, 0,
      { "OSD_EBCDIC_DF03_IRV", NULL }
   },
   /*
    * Source:  Fujitsu-Siemens standard mainframe EBCDIC encoding
    *          Please see:
    *          <http://www.iana.org/assignments/charset-reg/OSD-EBCDIC-DF04-1>
    *          Not supported by ICU
    */
   { 117, WINUNDEF, STRING_ENCODING_OSD_EBCDIC_DF04_1, UNSUPPORTED, 0,
      { "OSD_EBCDIC_DF04_1", NULL }
   },
   /*
    * Source: See <http://www.iana.org/assignments/charset-reg/ISO-11548-1>
    *            [Thibault]
    *          Not supported by ICU
    */
   { 118, WINUNDEF, STRING_ENCODING_ISO_11548_1, UNSUPPORTED, 0,
      { "ISO-11548-1", "ISO_11548-1", "ISO_TR_11548-1", "csISO115481", NULL }
   },
   /*
    * Source: See <http://www.iana.org/assignments/charset-reg/KZ-1048>  
    *    [Veremeev, Kikkarin]
    */
   { 119, WINUNDEF, STRING_ENCODING_KZ_1048, IN_FULL_ICU, 0,
      { "KZ-1048", "STRK1048-2002", "RK1048", "csKZ1048", NULL }
   },
   /*
    * Source: the 2-octet Basic Multilingual Plane, aka Unicode
    *         this needs to specify network byte order: the standard
    *         does not specify (it is a 16-bit integer space)
    */
   { 1000, WINUNDEF, STRING_ENCODING_ISO_10646_UCS_2, SUPPORTED, 0,
      { "ISO-10646-UCS-2", "csUnicode", "ibm-1204", "ibm-1205", "unicode",
        "ucs-2", NULL }
   },
   /*
    * Source: the full code space. (same comment about byte order,
    *         these are 31-bit numbers.
    */
   { 1001, WINUNDEF, STRING_ENCODING_ISO_10646_UCS_4, SUPPORTED, 0,
      { "ISO-10646-UCS-4", "csUCS4", "ibm-1236", "ibm-1237", "ucs-4", NULL }
   },
   /*
    * Source: ASCII subset of Unicode.  Basic Latin = collection 1
    *         See ISO 10646, Appendix A
    *          Not supported by ICU
    */
   { 1002, WINUNDEF, STRING_ENCODING_ISO_10646_UCS_BASIC, UNSUPPORTED, 0,
      { "ISO-10646-UCS-Basic", "csUnicodeASCII", NULL }
   },
   /*
    * Source: ISO Latin-1 subset of Unicode. Basic Latin and Latin-1 
    *          Supplement  = collections 1 and 2.  See ISO 10646, 
    *          Appendix A.  See RFC 1815.
    *          Not supported by ICU
    */
   { 1003, WINUNDEF, STRING_ENCODING_ISO_10646_UNICODE_LATIN1, UNSUPPORTED, 0,
      { "ISO-10646-Unicode-Latin1", "csUnicodeLatin1", "ISO-10646", NULL }
   },
   /*
    * Source: ISO 10646 Japanese, see RFC 1815.
    *          Not supported by ICU
    */
   { MIBUNDEF, WINUNDEF, STRING_ENCODING_ISO_10646_J_1, UNSUPPORTED, 0,
      { "ISO-10646-J-1", NULL }
   },
   /*
    * Source: IBM Latin-2, -3, -5, Extended Presentation Set, GCSGID: 1261
    *          Not supported by ICU
    */
   { 1005, WINUNDEF, STRING_ENCODING_ISO_UNICODE_IBM_1261, UNSUPPORTED, 0,
      { "ISO-Unicode-IBM-1261", "csUnicodeIBM1261", NULL }
   },
   /*
    * Source: IBM Latin-4 Extended Presentation Set, GCSGID: 1268
    *          Not supported by ICU
    */
   { 1006, WINUNDEF, STRING_ENCODING_ISO_UNICODE_IBM_1268, UNSUPPORTED, 0,
      { "ISO-Unicode-IBM-1268", "csUnicodeIBM1268", NULL }
   },
   /*
    * Source: IBM Cyrillic Greek Extended Presentation Set, GCSGID: 1276
    *          Not supported by ICU
    */
   { 1007, WINUNDEF, STRING_ENCODING_ISO_UNICODE_IBM_1276, UNSUPPORTED, 0,
      { "ISO-Unicode-IBM-1276", "csUnicodeIBM1276", NULL }
   },
   /*
    * Source: IBM Arabic Presentation Set, GCSGID: 1264
    *          Not supported by ICU
    */
   { 1008, WINUNDEF, STRING_ENCODING_ISO_UNICODE_IBM_1264, UNSUPPORTED, 0,
      { "ISO-Unicode-IBM-1264", "csUnicodeIBM1264", NULL }
   },
   /*
    * Source: IBM Hebrew Presentation Set, GCSGID: 1265
    *          Not supported by ICU
    */
   { 1009, WINUNDEF, STRING_ENCODING_ISO_UNICODE_IBM_1265, UNSUPPORTED, 0,
      { "ISO-Unicode-IBM-1265", "csUnicodeIBM1265", NULL }
   },
   /*
    * Source: RFC 1641, not supported by ICU
    */
   { 1010, WINUNDEF, STRING_ENCODING_UNICODE_1_1, UNSUPPORTED, 0,
      { "UNICODE-1-1", "csUnicode11", NULL }
   },
   /*
    * Source: SCSU See (http://www.iana.org/assignments/charset-reg/SCSU)
    *     [Scherer]
    */
   { 1011, WINUNDEF, STRING_ENCODING_SCSU, SUPPORTED, 0,
      { "SCSU", "ibm-1212", "ibm-1213", NULL }
   },
   /*
    * Source: RFC 2152
    */
   { 1012, 65000, STRING_ENCODING_UTF_7, SUPPORTED, 0,
      { "UTF-7", NULL }
   },
   /*
    * Source: RFC 2781
    */
   { 1013, 1201, STRING_ENCODING_UTF16_BE, SUPPORTED, 0,
      { "UTF-16BE", "x-utf-16be", "ibm-1200", "ibm-1201", "ibm-13488",
        "ibm-13489", "ibm-17584", "ibm-17585", "ibm-21680", "ibm-21681",
        "ibm-25776", "ibm-25777", "ibm-29872", "ibm-29873", "ibm-61955",
        "ibm-61956", "cp1200", "cp1201", "UTF16_BigEndian", 
        "UnicodeBigUnmarked", NULL }
   },
   /*
    * Source: RFC 2781
    */
   { 1014, 1200, STRING_ENCODING_UTF16_LE, SUPPORTED, 0,
      { "UTF-16LE", "x-utf-16le", "ibm-1202", "ibm-1203", "ibm-13490",
        "ibm-13491", "ibm-17586", "ibm-17587", "ibm-21682", "ibm-21683",
        "ibm-25778", "ibm-25779", "ibm-29874", "ibm-29875",
        "UTF16_LittleEndian", "UnicodeLittleUnmarked", NULL }
   },
   /*
    * Source: RFC 2781
    */
   { 1015, WINUNDEF, STRING_ENCODING_UTF16_XE, SUPPORTED, 0,
      { "UTF-16", NULL }
   },
   /*
    * Source: <http://www.unicode.org/unicode/reports/tr26>
    */
   { 1016, WINUNDEF, STRING_ENCODING_CESU_8, SUPPORTED, 0,
      { "CESU-8", "csCESU-8", "ibm-9400", NULL }
   },
   /*
    * Source: <http://www.unicode.org/unicode/reports/tr19/>
    */
   { 1017, WINUNDEF, STRING_ENCODING_UTF32_XE, SUPPORTED, 0,
      { "UTF-32", NULL }
   },
   /*
    * Source: <http://www.unicode.org/unicode/reports/tr19/>
    */
   { 1018, 12001, STRING_ENCODING_UTF32_BE, SUPPORTED, 0,
      { "UTF-32BE", "UTF32_BigEndian", "ibm-1232", "ibm-1233", "ibm-9424",
         NULL }
   },
   /*
    * Source: <http://www.unicode.org/unicode/reports/tr19/>
    */
   { 1019, 12000, STRING_ENCODING_UTF32_LE, SUPPORTED, 0,
      { "UTF-32LE", "UTF32_LittleEndian", "ibm-1234", "ibm-1235", NULL }
   },
   /*
    * Source: http://www.unicode.org/notes/tn6/
    */
   { 1020, WINUNDEF, STRING_ENCODING_BOCU_1, SUPPORTED, 0,
      { "BOCU-1", "csBOCU-1", "ibm-1214", "ibm-1215", NULL }
   },
   /*
    * Source: Extended ISO 8859-1 Latin-1 for Windows 3.0.  
    *         PCL Symbol Set id: 9U
    */
   { 2000, WINUNDEF, STRING_ENCODING_ISO_8859_1_WINDOWS_3_0_LATIN_1,
     UNSUPPORTED, 0,
      { "ISO-8859-1-Windows-3.0-Latin-1", "csWindows30Latin1", NULL }
   },
   /*
    * Source: Extended ISO 8859-1 Latin-1 for Windows 3.1.  
    *         PCL Symbol Set id: 19U
    *         Not supported by ICU
    */
   { 2001, WINUNDEF, STRING_ENCODING_ISO_8859_1_WINDOWS_3_1_LATIN_1,
     UNSUPPORTED, 0,
      { "ISO-8859-1-Windows-3.1-Latin-1", "csWindows31Latin1", NULL }
   },
   /*
    * Source: Extended ISO 8859-2.  Latin-2 for Windows 3.1.
    *         PCL Symbol Set id: 9E
    *         Not supported by ICU
    */
   { 2002, WINUNDEF, STRING_ENCODING_ISO_8859_2_WINDOWS_LATIN_2,
     UNSUPPORTED, 0,
      { "ISO-8859-2-Windows-Latin-2", "csWindows31Latin2", NULL }
   },
   /*
    * Source: Extended ISO 8859-9.  Latin-5 for Windows 3.1
    *         PCL Symbol Set id: 5T
    *         Not supported by ICU
    */
   { 2003, WINUNDEF, STRING_ENCODING_ISO_8859_9_WINDOWS_LATIN_5,
     UNSUPPORTED, 0,
      { "ISO-8859-9-Windows-Latin-5", "csWindows31Latin5", NULL }
   },
   /*
    * Source: LaserJet IIP Printer User's Manual, 
    *         HP part no 33471-90901, Hewlet-Packard, June 1989.
    */
   { 2004, WINUNDEF, STRING_ENCODING_HP_ROMAN8, IN_FULL_ICU, 0,
      { "hp-roman8", "roman8", "r8", "csHPRoman8", "ibm-1051_P100-1995",
        "ibm-1051", NULL }
   },
   /*
    * Source: PostScript Language Reference Manual
    *         PCL Symbol Set id: 10J
    */
   { 2005, WINUNDEF, STRING_ENCODING_ADOBE_STANDARD_ENCODING,
     IN_FULL_ICU, 0,
      { "Adobe-Standard-Encoding", "csAdobeStandardEncoding", 
        "ibm-1276_P100-1995", "ibm-1276", NULL }
   },
   /*
    * Source: Ventura US.  ASCII plus characters typically used in 
    *         publishing, like pilcrow, copyright, registered, trade mark, 
    *         section, dagger, and double dagger in the range A0 (hex) 
    *         to FF (hex).  
    *         PCL Symbol Set id: 14J
    *         Not supported by ICU
    */
   { 2006, WINUNDEF, STRING_ENCODING_VENTURA_US, UNSUPPORTED, 0,
      { "Ventura-US", "csVenturaUS", NULL }
   },
   /*
    * Source: Ventura International.  ASCII plus coded characters similar 
    *         to Roman8.
    *         PCL Symbol Set id: 13J
    *         Not supported by ICU
    */
   { 2007, WINUNDEF, STRING_ENCODING_VENTURA_INTERNATIONAL, UNSUPPORTED, 0,
      { "Ventura-International", "csVenturaInternational", NULL }
   },
   /*
    * Source: VAX/VMS User's Manual, 
    *         Order Number: AI-Y517A-TE, April 1986.
    */
   { 2008, WINUNDEF, STRING_ENCODING_DEC_MCS, IN_FULL_ICU, 0,
      { "DEC-MCS", "dec", "csDECMCS", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2009, 850, STRING_ENCODING_IBM_850, SUPPORTED, 0,
      { "IBM850", "cp850", "850", "csPC850Multilingual", 
        "ibm-850_P100-1995", NULL }
   },
   /*
    * Source: PC Danish Norwegian
    *         8-bit PC set for Danish Norwegian
    *         PCL Symbol Set id: 11U
    *         Not supported by ICU
    */
   { 2012, WINUNDEF, STRING_ENCODING_PC8_DANISH_NORWEGIAN, UNSUPPORTED, 0,
      { "PC8-Danish-Norwegian", "csPC8DanishNorwegian", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2013, 862, STRING_ENCODING_IBM_862, SUPPORTED, 0,
      { "IBM862", "cp862", "862", "csPC862LatinHebrew", 
        "ibm-862_P100-1995", "DOS-862", NULL }
   },
   /*
    * Source: PC Latin Turkish.  PCL Symbol Set id: 9T
    *         Not supported by ICU
    */
   { 2014, WINUNDEF, STRING_ENCODING_PC8_TURKISH, UNSUPPORTED, 0,
      { "PC8-Turkish", "csPC8Turkish", NULL }
   },
   /*
    * Source: Presentation Set, CPGID: 259
    *         Not supported by ICU
    */
   { 2015, WINUNDEF, STRING_ENCODING_IBM_SYMBOLS, UNSUPPORTED, 0,
      { "IBM-Symbols", "csIBMSymbols", NULL }
   },
   /*
    * Source: Presentation Set, CPGID: 838
    */
   { 2016, 20838, STRING_ENCODING_IBM_THAI, IN_FULL_ICU, 0,
      { "IBM-Thai", "csIBMThai", "ibm-838_P100-1995", "ibm-838", 
        "IBM838", "cp838", "838", "ibm-9030", NULL }
   },
   /*
    * Source: PCL 5 Comparison Guide, Hewlett-Packard,
    *         HP part number 5961-0510, October 1992
    *         PCL Symbol Set id: 1U
    *         Not supported by ICU
    */
   { 2017, WINUNDEF, STRING_ENCODING_HP_LEGAL, UNSUPPORTED, 0,
      { "HP-Legal", "csHPLegal", NULL }
   },
   /*
    * Source: PCL 5 Comparison Guide, Hewlett-Packard,
    *         HP part number 5961-0510, October 1992
    *         PCL Symbol Set id: 15U
    *         Not supported by ICU
    */
   { 2018, WINUNDEF, STRING_ENCODING_HP_PI_FONT, UNSUPPORTED, 0,
      { "HP-Pi-font", "csHPPiFont", NULL }
   },
   /*
    * Source: PCL 5 Comparison Guide, Hewlett-Packard,
    *         HP part number 5961-0510, October 1992
    *         PCL Symbol Set id: 8M
    *         Not supported by ICU
    */
   { 2019, WINUNDEF, STRING_ENCODING_HP_MATH8, UNSUPPORTED, 0,
      { "HP-Math8", "csHPMath8", NULL }
   },
   /*
    * Source: PostScript Language Reference Manual
    *         PCL Symbol Set id: 5M
    *         Not supported by ICU
    */
   { 2020, WINUNDEF, STRING_ENCODING_ADOBE_SYMBOL_ENCODING, UNSUPPORTED, 0,
      { "Adobe-Symbol-Encoding", "csHPPSMath", NULL }
   },
   /*
    * Source: PCL 5 Comparison Guide, Hewlett-Packard,
    *         HP part number 5961-0510, October 1992
    *         PCL Symbol Set id: 7J
    *         Not supported by ICU
    */
   { 2021, WINUNDEF, STRING_ENCODING_HP_DESKTOP, UNSUPPORTED, 0,
      { "HP-DeskTop", "csHPDesktop", NULL }
   },
   /*
    * Source: PCL 5 Comparison Guide, Hewlett-Packard,
    *         HP part number 5961-0510, October 1992
    *         PCL Symbol Set id: 6M
    *         Not supported by ICU
    */
   { 2022, WINUNDEF, STRING_ENCODING_VENTURA_MATH, UNSUPPORTED, 0,
      { "Ventura-Math", "csVenturaMath", NULL }
   },
   /*
    * Source: PCL 5 Comparison Guide, Hewlett-Packard,
    *         HP part number 5961-0510, October 1992
    *         PCL Symbol Set id: 6J
    *         Not supported by ICU
    */
   { 2023, WINUNDEF, STRING_ENCODING_MICROSOFT_PUBLISHING, UNSUPPORTED, 0,
      { "Microsoft-Publishing", "csMicrosoftPublishing", NULL }
   },
   /*
    * Source: Windows Japanese.  A further extension of Shift_JIS
    *         to include NEC special characters (Row 13), NEC
    *         selection of IBM extensions (Rows 89 to 92), and IBM
    *         extensions (Rows 115 to 119).  The CCS's are
    *         JIS X0201:1997, JIS X0208:1997, and these extensions.
    *         This charset can be used for the top-level media type "text",
    *         but it is of limited or specialized use (see RFC2278).
    *         PCL Symbol Set id: 19K
    */
   { 2024, WINUNDEF, STRING_ENCODING_WINDOWS_31J, SUPPORTED, 0,
      { "Windows-31J", "csWindows31J", NULL }
   },
   /*
    * Source: Chinese for People's Republic of China (PRC) mixed one byte, 
    *         two byte set: 
    *           20-7E = one byte ASCII 
    *           A1-FE = two byte PRC Kanji 
    *         See GB 2312-80 
    *         PCL Symbol Set Id: 18C
    */
   { 2025, WINUNDEF, STRING_ENCODING_GB_2312, IN_FULL_ICU, 0,
      { "GB2312", "csGB2312", "ibm-1383_P110-1999", "ibm-1383",
        "cp1383", "1383", "EUC-CN", "ibm-eucCN", "hp15CN",
        "ibm-1383_VPUA", NULL }
   },
   /*
    * Source: Chinese for Taiwan Multi-byte set.
    *         PCL Symbol Set Id: 18T
    */
   { 2026, 950, STRING_ENCODING_BIG_5, SUPPORTED, 0,
      { "Big5", "csBig5", "windows-950", "windows-950-2000",
        "x-big5", NULL }
   },
   /*
    * Alternate ICU converter for Windows-950 (Big5)
    */
   { MIBUNDEF, 950, STRING_ENCODING_IBM_1373, SUPPORTED, 0,
      { "ibm-1373_P100-2002", "ibm-1373", NULL }
   },
   /*
    * Source: The Unicode Standard ver1.0, ISBN 0-201-56788-1, Oct 1991
    */
   { 2027, WINUNDEF, STRING_ENCODING_MACINTOSH, IN_FULL_ICU, 0,
      { "macintosh", "mac", "csMacintosh", "macos-0_2-10.2", 
        "macroman", "x-macroman", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2028, 37, STRING_ENCODING_IBM_037, IN_FULL_ICU, 0,
      { "IBM037", "cp037", "ebcdic-cp-us", "ebcdic-cp-ca", "ebcdic-cp-wt",
        "ebcdic-cp-nl", "csIBM037", "ibm-37_P100-1995", "ibm-37",
        "037", "cpibm37", "cp37", NULL }
   },
   /*
    * Source: IBM 3174 Character Set Ref, GA27-3831-02, March 1990
    *         Not supported by ICU
    */
   { 2029, WINUNDEF, STRING_ENCODING_IBM_038, UNSUPPORTED, 0,
      { "IBM038", "EBCDIC-INT", "cp038", "csIBM038", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2030, 20273, STRING_ENCODING_IBM_273, IN_FULL_ICU, 0,
      { "IBM273", "CP273", "csIBM273", "ibm-273_P100-1995",
        "ebcdic-de", "273", NULL }
   },
   /*
    * Source: IBM 3174 Character Set Ref, GA27-3831-02, March 1990
    */
   { 2031, WINUNDEF, STRING_ENCODING_IBM_274, IN_FULL_ICU, 0,
      { "IBM274", "EBCDIC-BE", "CP274", "csIBM274", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2032, WINUNDEF, STRING_ENCODING_IBM_275, IN_FULL_ICU, 0,
      { "IBM275", "EBCDIC-BR", "cp275", "csIBM275", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2033, 20277, STRING_ENCODING_IBM_277, IN_FULL_ICU, 0,
      { "IBM277", "EBCDIC-CP-DK", "EBCDIC-CP-NO", "csIBM277",
        "ibm-277_P100-1995", "cp277", "ebcdic-dk", "277", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2034, 20278, STRING_ENCODING_IBM_278, IN_FULL_ICU, 0,
      { "IBM278", "CP278", "ebcdic-cp-fi", "ebcdic-cp-se", "csIBM278",
        "ibm-278_P100-1995", "ebcdic-sv", "278", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2035, 20280, STRING_ENCODING_IBM_280, IN_FULL_ICU, 0,
      { "IBM280", "CP280", "ebcdic-cp-it", "csIBM280",
        "ibm-280_P100-1995", "280", NULL }
   },
   /*
    * Source: IBM 3174 Character Set Ref, GA27-3831-02, March 1990
    *         Not supported by ICU
    */
   { 2036, WINUNDEF, STRING_ENCODING_IBM_281, UNSUPPORTED, 0,
      { "IBM281", "EBCDIC-JP-E", "cp281", "csIBM281", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2037, 20284, STRING_ENCODING_IBM_284, IN_FULL_ICU, 0,
      { "IBM284", "CP284", "ebcdic-cp-es", "csIBM284", 
        "ibm-284_P100-1995", "cpibm284", "284", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2038, 20285, STRING_ENCODING_IBM_285, IN_FULL_ICU, 0,
      { "IBM285", "CP285", "ebcdic-cp-gb", "csIBM285", 
        "ibm-284_P100-1995", "cpibm284", "284", NULL }
   },
   /*
    * Source: IBM 3174 Character Set Ref, GA27-3831-02, March 1990
    */
   { 2039, 20290, STRING_ENCODING_IBM_290, IN_FULL_ICU, 0,
      { "IBM290", "cp290", "EBCDIC-JP-kana", "csIBM290", 
        "ibm-290_P100-1995", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2040, 20297, STRING_ENCODING_IBM_297, IN_FULL_ICU, 0,
      { "IBM297", "cp297", "ebcdic-cp-fr", "csIBM297", 
        "ibm-297_P100-1995", "cpibm297", "297", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990, 
    *         IBM NLS RM p 11-11
    */
   { 2041, 20420, STRING_ENCODING_IBM_420, IN_FULL_ICU, 0,
      { "IBM420", "cp420", "ebcdic-cp-ar1", "csIBM420", 
        "ibm-420_X120-1999", "420", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    *         Not supporeted by ICU
    */
   { 2042, 20423, STRING_ENCODING_IBM_423, UNSUPPORTED, 0,
      { "IBM423", "cp423", "ebcdic-cp-gr", "csIBM423", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2043, 20424, STRING_ENCODING_IBM_424, IN_FULL_ICU, 0,
      { "IBM424", "cp424", "ebcdic-cp-he", "csIBM424", 
        "ibm-424_P100-1995", "424", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2011, 437, STRING_ENCODING_IBM_437, SUPPORTED, 0,
      { "IBM437", "cp437", "437", "csPC8CodePage437", 
        "ibm-437_P100-1995", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2044, 500, STRING_ENCODING_IBM_500, IN_FULL_ICU, 0,
      { "IBM500", "CP500", "ebcdic-cp-be", "ebcdic-cp-ch", 
        "csIBM500", "ibm-500_P100-1995", "500", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2045, WINUNDEF, STRING_ENCODING_IBM_851, IN_FULL_ICU, 0,
      { "IBM851", "cp851", "851", "csIBM851", "ibm-851_P100-1995",
        "csPC851", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2010, 852, STRING_ENCODING_IBM_852, SUPPORTED, 0,
      { "IBM852", "cp852", "852", "csPCp852", "ibm-852_P100-1995",
        NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2046, 855, STRING_ENCODING_IBM_855, IN_FULL_ICU, 0,
      { "IBM855", "cp855", "855", "csIBM855", "ibm-855_P100-1995",
        "csPCp855", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2047, 857, STRING_ENCODING_IBM_857, SUPPORTED, 0,
      { "IBM857", "cp857", "857", "csIBM857", "ibm-857_P100-1995",
         NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2048, 860, STRING_ENCODING_IBM_860, IN_FULL_ICU, 0,
      { "IBM860", "cp860", "860", "csIBM860", "ibm-860_P100-1995",
         NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2049, 861, STRING_ENCODING_IBM_861, IN_FULL_ICU, 0,
      { "IBM861", "cp861", "861", "cp-is", "csIBM861", 
        "ibm-861_P100-1995", NULL }
   },
   /*
    * Source: IBM Keyboard layouts and code pages, PN 07G4586 June 1991
    */
   { 2050, 863, STRING_ENCODING_IBM_863, IN_FULL_ICU, 0,
      { "IBM863", "cp863", "863", "csIBM863", "ibm-863_P100-1995",
         NULL }
   },
   /*
    * Source: IBM Keyboard layouts and code pages, PN 07G4586 June 1991
    */
   { 2051, 864, STRING_ENCODING_IBM_864, IN_FULL_ICU, 0,
      { "IBM864", "cp864", "csIBM864", "ibm-864_X110-1999", NULL }
   },
   /*
    * Source: IBM DOS 3.3 Ref (Abridged), 94X9575 (Feb 1987)
    */
   { 2052, 865, STRING_ENCODING_IBM_865, IN_FULL_ICU, 0,
      { "IBM865", "cp865", "865", "csIBM865", "ibm-865_P100-1995",
         NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2053, WINUNDEF, STRING_ENCODING_IBM_868, IN_FULL_ICU, 0,
      { "IBM868", "CP868", "cp-ar", "csIBM868", "ibm-868_P100-1995",
        "868", NULL }
   },
   /*
    * Source: IBM Keyboard layouts and code pages, PN 07G4586 June 1991
    */
   { 2054, 869, STRING_ENCODING_IBM_869, IN_FULL_ICU, 0,
      { "IBM869", "cp869", "869", "cp-gr", "csIBM869", "ibm-869_P100-1995",
         NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2055, 870, STRING_ENCODING_IBM_870, IN_FULL_ICU, 0,
      { "IBM870", "CP870", "ebcdic-cp-roece", "ebcdic-cp-yu", 
        "csIBM870", "ibm-870_P100-1995", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2056, 20871, STRING_ENCODING_IBM_871, IN_FULL_ICU, 0,
      { "IBM871", "CP871", "ebcdic-cp-is", "csIBM871",
        "ibm-871_P100-1995", "ebcdic-is", "871", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2057, 20880, STRING_ENCODING_IBM_880, IN_FULL_ICU, 0,
      { "IBM880", "cp880", "EBCDIC-Cyrillic", "csIBM880", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    *         Not supported by ICU
    */
   { 2058, WINUNDEF, STRING_ENCODING_IBM_891, UNSUPPORTED, 0,
      { "IBM891", "cp891", "csIBM891", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    *         Not supported by ICU
    */
   { 2059, WINUNDEF, STRING_ENCODING_IBM_903, UNSUPPORTED, 0,
      { "IBM903", "cp903", "csIBM903", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    *         Not supported by ICU
    */
   { 2060, WINUNDEF, STRING_ENCODING_IBM_904, UNSUPPORTED, 0,
      { "IBM904", "cp904", "904", "csIBBM904", NULL }
   },
   /*
    * Source: IBM 3174 Character Set Ref, GA27-3831-02, March 1990
    */
   { 2061, 20905, STRING_ENCODING_IBM_905, IN_FULL_ICU, 0,
      { "IBM905", "CP905", "ebcdic-cp-tr", "csIBM905", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2062, WINUNDEF, STRING_ENCODING_IBM_918, IN_FULL_ICU, 0,
      { "IBM918", "CP918", "ebcdic-cp-ar2", "csIBM918", 
        "ibm-918_P100-1995", NULL }
   },
   /*
    * Source: IBM NLS RM Vol2 SE09-8002-01, March 1990
    */
   { 2063, 1026, STRING_ENCODING_IBM_1026, IN_FULL_ICU, 0,
      { "IBM1026", "CP1026", "csIBM1026", "ibm-1026_P100-1995",
        "1026", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2064, WINUNDEF, STRING_ENCODING_EBCDIC_AT_DE, UNSUPPORTED, 0,
      { "EBCDIC-AT-DE", "csIBMEBCDICATDE", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987 
    */
   { 2065, WINUNDEF, STRING_ENCODING_EBCDIC_AT_DE_A, IN_FULL_ICU, 0,
      { "EBCDIC-AT-DE-A", "csEBCDICATDEA", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2066, WINUNDEF, STRING_ENCODING_EBCDIC_CA_FR, UNSUPPORTED, 0,
      { "EBCDIC-CA-FR", "csEBCDICCAFR", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2067, WINUNDEF, STRING_ENCODING_EBCDIC_DK_NO, UNSUPPORTED, 0,
      { "EBCDIC-DK-NO", "csEBCDICDKNO", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2068, WINUNDEF, STRING_ENCODING_EBCDIC_DK_NO_A, UNSUPPORTED, 0,
      { "EBCDIC-DK-NO-A", "csEBCDICDKNOA", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2069, WINUNDEF, STRING_ENCODING_EBCDIC_FI_SE, UNSUPPORTED, 0,
      { "EBCDIC-FI-SE", "csEBCDICFISE", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2070, WINUNDEF, STRING_ENCODING_EBCDIC_FI_SE_A, UNSUPPORTED, 0,
      { "EBCDIC-FI-SE-A", "csEBCDICFISEA", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2071, WINUNDEF, STRING_ENCODING_EBCDIC_FR, UNSUPPORTED, 0,
      { "EBCDIC-FR", "csEBCDICFR", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2072, WINUNDEF, STRING_ENCODING_EBCDIC_IT, UNSUPPORTED, 0,
      { "EBCDIC-IT", "csEBCDICIT", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2073, WINUNDEF, STRING_ENCODING_EBCDIC_PT, UNSUPPORTED, 0,
      { "EBCDIC-PT", "csEBCDICPT", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2074, WINUNDEF, STRING_ENCODING_EBCDIC_ES, UNSUPPORTED, 0,
      { "EBCDIC-ES", "csEBCDICES", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2075, WINUNDEF, STRING_ENCODING_EBCDIC_ES_A, UNSUPPORTED, 0,
      { "EBCDIC-ES-A", "csEBCDICESA", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2076, WINUNDEF, STRING_ENCODING_EBCDIC_ES_S, UNSUPPORTED, 0,
      { "EBCDIC-ES-S", "csEBCDICESS", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2077, WINUNDEF, STRING_ENCODING_EBCDIC_UK, UNSUPPORTED, 0,
      { "EBCDIC-UK", "csEBCDICUK", NULL }
   },
   /*
    * Source: IBM 3270 Char Set Ref Ch 10, GA27-2837-9, April 1987
    *         Not supported by ICU
    */
   { 2078, WINUNDEF, STRING_ENCODING_EBCDIC_US, UNSUPPORTED, 0,
      { "EBCDIC-US", "csEBCDICUS", NULL }
   },
   /*
    *         Not supported by ICU
    */
   { 2079, WINUNDEF, STRING_ENCODING_UNKNOWN_8BIT, UNSUPPORTED, 0,
      { "UNKNOWN-8BIT", "csUnknown8BiT", NULL }
   },
   /*
    * Source: RFC 1345, also known as "mnemonic+ascii+38"
    *         Not supported by ICU
    */
   { 2080, WINUNDEF, STRING_ENCODING_MNEMONIC, UNSUPPORTED, 0,
      { "MNEMONIC", "csMnemonic", NULL }
   },
   /*
    * Source: RFC 1345, also known as "mnemonic+ascii+8200"
    *         Not supported by ICU
    */
   { 2081, WINUNDEF, STRING_ENCODING_MNEM, UNSUPPORTED, 0,
      { "MNEM", "csMnem", NULL }
   },
   /*
    * Source: RFC 1456
    *         Not supported by ICU
    */
   { 2082, WINUNDEF, STRING_ENCODING_VISCII, UNSUPPORTED, 0,
      { "VISCII", "csVISCII", NULL }
   },
   /*
    * Source: RFC 1456
    *         Not supported by ICU
    */
   { 2083, WINUNDEF, STRING_ENCODING_VIQR, UNSUPPORTED, 0,
      { "VIQR", "csVIQR", NULL }
   },
   /*
    * Source: RFC 1489, based on GOST-19768-74, ISO-6937/8, 
    *         INIS-Cyrillic, ISO-5427.
    */
   { 2084, 20866, STRING_ENCODING_KOI8_R, IN_FULL_ICU, 0,
      { "KOI8-R", "csKOI8R", "koi8", "cp878", "ibm-878",
        "ibm-878_P100-1996", NULL }
   },
   /*
    * Source: RFC 1842, RFC 1843 [RFC1842, RFC1843]
    */
   { 2085, 52936, STRING_ENCODING_HZ_GB_2312, SUPPORTED, 0,
      { "HZ-GB-2312", "HZ", NULL }
   },
   /*
    * Source: IBM NLDG Volume 2 (SE09-8002-03) August 1994
    */
   { 2086, 866, STRING_ENCODING_IBM_866, SUPPORTED, 0,
      { "IBM866", "cp866", "866", "csIBM866", "ibm-866_P100-1995",
         NULL }
   },
   /*
    * Source: HP PCL 5 Comparison Guide (P/N 5021-0329) pp B-13, 1996
    */
   { 2087, 775, STRING_ENCODING_IBM_775, SUPPORTED, 0,
      { "IBM775", "cp775", "csPC775Baltic", "ibm-775_P100-1996",
        "775", NULL }
   },
   /*
    * Source: RFC 2319
    */
   { 2088, 21866, STRING_ENCODING_KOI8_U, IN_FULL_ICU, 0,
      { "KOI8-U", "ibm-1168", "ibm-1168_P100-2002", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM00858) 
    *   [Mahdi]
    */
   { 2089, 858, STRING_ENCODING_IBM_00858, SUPPORTED, 0,
      { "IBM00858", "CCSID00858", "CP00858", "PC-Multilingual-850+euro", 
        "ibm-858", "cp858", "ibm-858_P100-1997", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM00924)
    *   [Mahdi]
    */
   { 2090, 20924, STRING_ENCODING_IBM_00924, IN_FULL_ICU, 0,
      { "IBM00924", "CCSID00924", "CP00924", "ebcdic-Latin9--euro", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01140)
    *    [Mahdi]
    */
   { 2091, 1140, STRING_ENCODING_IBM_01140, IN_FULL_ICU, 0,
      { "IBM01140", "CCSID01140", "CP01140", "ebcdic-us-37+euro", 
        "ibm-1140", "cp1140", "ibm-1140_P100-1997", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01141)
    *    [Mahdi]
    */
   { 2092, 1141, STRING_ENCODING_IBM_01141, IN_FULL_ICU, 0,
      { "IBM01141", "CCSID01141", "CP01141", "ebcdic-de-273+euro",
        "ibm-1141", "cp1141", "ibm-1141_P100-1997", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01142)
    *    [Mahdi]
    */
   { 2093, 1142, STRING_ENCODING_IBM_01142, IN_FULL_ICU, 0,
      { "IBM01142", "CCSID01142", "CP01142", "ebcdic-dk-277+euro",
        "ebcdic-no-277+euro", "ibm-1142", "cp1142", "ibm-1142_P100-1997",
         NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01143)
    *    [Mahdi]
    */
   { 2094, 1143, STRING_ENCODING_IBM_01143, IN_FULL_ICU, 0,
      { "IBM01143", "CCSID01143", "CP01143", "ebcdic-fi-278+euro",
        "ebcdic-se-278+euro", "ibm-1143", "cp1143", "ibm-1143_P100-1997",
         NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01144)
    *    [Mahdi]
    */
   { 2095, 1144, STRING_ENCODING_IBM_01144, IN_FULL_ICU, 0,
      { "IBM01144", "CCSID01144", "CP01144", "ebcdic-it-280+euro", 
        "ibm-1144", "cp1144", "ibm-1144_P100-1997", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01145)
    *    [Mahdi]
    */
   { 2096, 1145, STRING_ENCODING_IBM_01145, IN_FULL_ICU, 0,
      { "IBM01145", "CCSID01145", "CP01145", "ebcdic-es-284+euro", 
        "ibm-1145", "cp1145", "ibm-1145_P100-1997", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01146)
    *    [Mahdi]
    */
   { 2097, 1146, STRING_ENCODING_IBM_01146, IN_FULL_ICU, 0,
      { "IBM01146", "CCSID01146", "CP01146", "ebcdic-gb-285+euro", 
        "ibm-1146", "cp1146", "ibm-1146_P100-1997", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01147)
    *    [Mahdi]
    */
   { 2098, 1147, STRING_ENCODING_IBM_01147, IN_FULL_ICU, 0,
      { "IBM01147", "CCSID01147", "CP01147", "ebcdic-fr-297+euro",
        "ibm-1147", "cp1147", "ibm-1147_P100-1997", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01148)
    *    [Mahdi]
    */
   { 2099, 1148, STRING_ENCODING_IBM_01148, IN_FULL_ICU, 0,
      { "IBM01148", "CCSID01148", "CP01148", "ebcdic-international-500+euro",
        "ibm-1148", "cp1148", "ibm-1148_P100-1997", NULL }
   },
   /*
    * Source: IBM See (http://www.iana.org/assignments/charset-reg/IBM01149)
    *    [Mahdi]
    */
   { 2100, 1149, STRING_ENCODING_IBM_01149, IN_FULL_ICU, 0,
      { "IBM01149", "CCSID01149", "CP01149", "ebcdic-is-871+euro",
        "ibm-1149", "cp1149", "ibm-1149_P100-1997", NULL }
   },
   /*
    * Source:   See (http://www.iana.org/assignments/charset-reg/Big5-HKSCS) 
    */
   { 2101, WINUNDEF, STRING_ENCODING_BIG_5_HK, IN_FULL_ICU, 0,
      { "Big5-HKSCS", "ibm-1375_P100-2007", "ibm-1375", "big5hk",
        "HKSCS-BIG5", NULL }
   },
   /*
    * Alternate ICU converter for Big-5-HKSCS 
    */
   { 2101, WINUNDEF, STRING_ENCODING_IBM_5471, IN_FULL_ICU, 0,
      { "ibm-5471_P100-2006", "ibm-5471", "MS950_HKSCS", "hkbig5",
         NULL }
   },
   /*
    * Source: IBM1047 (EBCDIC Latin 1/Open Systems)
    * http://www-1.ibm.com/servers/eserver/iseries/software/globalization/
    *        pdf/cp01047z.pdf
    */
   { 2102, 1047, STRING_ENCODING_IBM_1047, IN_FULL_ICU, 0,
      { "IBM1047", "IBM-1047", "cp1047", "1047", "ibm-1047_P100-1995",
         NULL }
   },
   /*
    * Source: See (http://www.iana.org/assignments/charset-reg/PTCP154)
    *         Not supported by ICU
    */
   { 2103, WINUNDEF, STRING_ENCODING_PTCP154, UNSUPPORTED, 0,
      { "PTCP154", "csPTCP154", "PT154", "CP154", "Cyrillic-Asian", NULL }
   },
   /*
    * Source:  See (http://www.amiga.ultranet.ru/Amiga-1251.html)
    *          Not supported by ICU
    */
   { 2104, WINUNDEF, STRING_ENCODING_AMIGA_1251, UNSUPPORTED, 0,
      { "Amiga-1251", "Ami1251", "Amiga1251", "Ami-1251", NULL }
   },
   /*
    * Source:  See <http://www.iana.org/assignments/charset-reg/KOI7-switched>
    * Aliases: None
    *          Not supported by ICU
    */
   { 2105, WINUNDEF, STRING_ENCODING_KOI7_SWITCHED, UNSUPPORTED, 0,
      { "KOI7-switched", NULL }
   },
   /*
    * Source: See <http://www.iana.org/assignments/charset-reg/BRF>
    * [Thibault]
    *         Not supported by ICU
    */
   { 2106, WINUNDEF, STRING_ENCODING_BRF, UNSUPPORTED, 0,
      { "BRF", "csBRF", NULL }
   },
   /*
    * Source: See <http://www.iana.org/assignments/charset-reg/TSCII>
    * [Kalyanasundaram]
    *         Not supported by ICU
    */
   { 2107, WINUNDEF, STRING_ENCODING_TSCII, UNSUPPORTED, 0,
      { "TSCII", "csTSCII", NULL }
   },
   /*
    * Source: Microsoft  
    *  (http://www.iana.org/assignments/charset-reg/windows-1250) [Lazhintseva]
    */
   { 2250, 1250, STRING_ENCODING_WINDOWS_1250, SUPPORTED, 0,
      { "windows-1250", "ibm-5346_P100-1998", "ibm-5346", "cp1250", 
        "ibm-1250_P100-1995", "ibm-1250", NULL }
   },
   /*
    * Source: Microsoft
    *  (http://www.iana.org/assignments/charset-reg/windows-1251) [Lazhintseva]
    */
   { 2251, 1251, STRING_ENCODING_WINDOWS_1251, SUPPORTED, 0,
      { "windows-1251", "ibm-5347_P100-1998", "ibm-5347", "cp1251", "ANSI1251",
        "ibm-1251_P100-1995", "ibm-1251", NULL }
   },
   /*
    * Source: Microsoft 
    *  (http://www.iana.org/assignments/charset-reg/windows-1252) [Wendt]
    */
   { 2252, 1252, STRING_ENCODING_WINDOWS_1252, SUPPORTED, 0,
      { "windows-1252", "ibm-5348_P100-1997", "ibm-5348", "cp1252", 
        "ibm-1252_P100-2000", "ibm-1252", NULL }
   },
   /*
    * Source: Microsoft 
    *  (http://www.iana.org/assignments/charset-reg/windows-1253) [Lazhintseva]
    */
   { 2253, 1253, STRING_ENCODING_WINDOWS_1253, SUPPORTED, 0,
      { "windows-1253", "ibm-5349_P100-1998", "ibm-5349", "cp1253",
        "ibm-1253", "ibm-1253_P100-1995", NULL }
   },
   /*
    * Source: Microsoft
    *  (http://www.iana.org/assignments/charset-reg/windows-1254) [Lazhintseva]
    */
   { 2254, 1254, STRING_ENCODING_WINDOWS_1254, SUPPORTED, 0,
      { "windows-1254", "ibm-5350_P100-1998", "ibm-5350", "cp1254", 
        "ibm-1254", "ibm-1254_P100-1995", NULL }
   },
   /*
    * Source: Microsoft 
    *  (http://www.iana.org/assignments/charset-reg/windows-1255) [Lazhintseva]
    */
   { 2255, 1255, STRING_ENCODING_WINDOWS_1255, SUPPORTED, 0,
      { "windows-1255", "ibm-9447_P100-2002", "ibm-9447", "cp1255", 
        "ibm-5351", "ibm-5351_P100-1998", NULL }
   },
   /*
    * Source: Microsoft
    *  (http://www.iana.org/assignments/charset-reg/windows-1256) [Lazhintseva]
    */
   { 2256, 1256, STRING_ENCODING_WINDOWS_1256, SUPPORTED, 0,
      { "windows-1256", "ibm-9448_X100-2005", "ibm-9448", "cp1256", 
        "ibm-5352", "ibm-5352_P100-1998", NULL }
   },
   /*
    * Source: Microsoft 
    *  (http://www.iana.org/assignments/charset-reg/windows-1257) [Lazhintseva]
    */
   { 2257, 1257, STRING_ENCODING_WINDOWS_1257, SUPPORTED, 0,
      { "windows-1257", "ibm-9449_P100-2002", "ibm-9449", "cp1257", 
        "ibm-5353", "ibm-5353_P100-1998", NULL }
   },
   /*
    * Source: Microsoft
    *  (http://www.iana.org/assignments/charset-reg/windows-1258) [Lazhintseva]
    */
   { 2258, 1258, STRING_ENCODING_WINDOWS_1258, SUPPORTED, 0,
      { "windows-1258", "ibm-5354_P100-1998", "ibm-5354", "cp1258", 
        "ibm-1258", "ibm-1258_P100-1997", NULL }
   },
   /*
    * Source: Thai Industrial Standards Institute (TISI)
    *    [Tantsetthi]
    */
   { 2259, WINUNDEF, STRING_ENCODING_TIS_620, SUPPORTED, 0,
      { "TIS-620", "windows-874-2000", "MS874", NULL }
   },
   /*
    * Windows specific entries for which there is no corresponding IANA mapping
    */
   
   /*
    * Windows-709: Arabic (ASMO-449+, BCON V4)
    *              Not supported by ICU
    */
   { MIBUNDEF, 709, STRING_ENCODING_WINDOWS_709, UNSUPPORTED, 0,
      { "Windows-709", "ASMO-449+", "BCON_V4", NULL }
   },
   /*
    * Windows-710: Arabic - Transparent Arabic
    *              Not supported by ICU
    */
   { MIBUNDEF, 710, STRING_ENCODING_WINDOWS_710, UNSUPPORTED, 0,
      { "Windows-710", NULL }
   },
   /*
    * DOS-720: Arabic (Transparent ASMO); Arabic (DOS)
    */
   { MIBUNDEF, 720, STRING_ENCODING_WINDOWS_720, SUPPORTED, 0,
      { "Windows-720", "DOS-720", "DOS_720", "ibm-720",
        "ibm-720_P100-1997", NULL }
   },
   /*
    * ibm737: OEM Greek (formerly 437G); Greek (DOS)
    */
   { MIBUNDEF, 737, STRING_ENCODING_WINDOWS_737, SUPPORTED, 0,
      { "Windows-737", "IBM737", "cp737", "737", "ibm-737_P100-1997",
         NULL }
   },
   /*
    * cp875: IBM EBCDIC Greek Modern
    *        ICU doesn't have "Windows-875" as an alias, use "cp875"
    */
   { MIBUNDEF, 875, STRING_ENCODING_WINDOWS_875, IN_FULL_ICU, 0,
      { "cp875", "ibm-875", "IBM875", "875", "ibm-875_P100-1995", NULL }
   },
   /*
    * Johab: Korean (Johab)
    *        Not supported by ICU
    */
   { MIBUNDEF, 1361, STRING_ENCODING_WINDOWS_1361, UNSUPPORTED, 0,
      { "Windows-1361", "Johab", NULL }
   },
   /*
    * macintosh: MAC Roman; Western European (Mac)
    * using the encoding names "mac" and "macintosh"
    * is probably a bad idea here
    */
   { MIBUNDEF, 10000, STRING_ENCODING_WINDOWS_10000, IN_FULL_ICU, 0,
      { "Windows-10000", NULL }
   },
   /*
    * x-mac-japanese: Japanese (Mac)
    *                 Not supported by ICU
    */
   { MIBUNDEF, 10001, STRING_ENCODING_WINDOWS_10001, UNSUPPORTED, 0,
      { "Windows-10001", "x-mac-japanese", NULL }
   },
   /*
    * x-mac-chinesetrad: MAC Traditional Chinese (Big5);
    *                    Chinese Traditional (Mac)
    *                    Not supported by ICU
    */
   { MIBUNDEF, 10002, STRING_ENCODING_WINDOWS_10002, UNSUPPORTED, 0,
      { "Windows-10002", "x-mac-chinesetrad", NULL }
   },
   /*
    * x-mac-korean: Korean (Mac)
    *               Not supported by ICU
    */
   { MIBUNDEF, 10003, STRING_ENCODING_WINDOWS_10003, UNSUPPORTED, 0,
      { "Windows-10003", "x-mac-korean", NULL }
   },
   /*
    * x-mac-arabic: Arabic (Mac)
    *               Not supported by ICU
    */
   { MIBUNDEF, 10004, STRING_ENCODING_WINDOWS_10004, UNSUPPORTED, 0,
      { "Windows-10004", "x-mac-arabic", NULL }
   },
   /*
    * x-mac-hebrew: Hebrew (Mac)
    *               Not supported by ICU
    */
   { MIBUNDEF, 10005, STRING_ENCODING_WINDOWS_10005, UNSUPPORTED, 0,
      { "Windows-10005", "x-mac-hebrew", NULL }
   },
   /*
    * x-mac-greek: Greek (Mac)
    */
   { MIBUNDEF, 10006, STRING_ENCODING_WINDOWS_10006, IN_FULL_ICU, 0,
      { "Windows-10006", "x-mac-greek", "macgr", "macos-6_2-10.4", NULL }
   },
   /*
    * x-mac-cyrillic: Cyrillic (Mac)
    */
   { MIBUNDEF, 10007, STRING_ENCODING_WINDOWS_10007, IN_FULL_ICU, 0,
      { "Windows-10007", "x-mac-cyrillic", "maccy", "mac-cyrillic",
        "macos-7_3-10.2", NULL }
   },
   /*
    * x-mac-chinesesimp: MAC Simplified Chinese (GB 2312);
    *                    Chinese Simplified (Mac)
    *                    Not supported by ICU
    */
   { MIBUNDEF, 10008, STRING_ENCODING_WINDOWS_10008, UNSUPPORTED, 0,
      { "Windows-10008", "x-mac-chinesesimp", NULL }
   },
   /*
    * x-mac-romanian: Romanian (Mac)
    *                 Not supported by ICU
    */
   { MIBUNDEF, 10010, STRING_ENCODING_WINDOWS_10010, UNSUPPORTED, 0,
      { "Windows-10010", "x-mac-romanian", NULL }
   },
   /*
    * x-mac-ukrainian: Ukrainian (Mac)
    *                  Not supported by ICU
    */
   { MIBUNDEF, 10017, STRING_ENCODING_WINDOWS_10017, UNSUPPORTED, 0,
      { "Windows-10017", "x-mac-ukrainian", NULL }
   },
   /*
    * x-mac-thai: Thai (Mac)
    *             Not supported by ICU
    */
   { MIBUNDEF, 10021, STRING_ENCODING_WINDOWS_10021, UNSUPPORTED, 0,
      { "Windows-10021", "x-mac-thai", NULL }
   },
   /*
    * x-mac-ce: MAC Latin 2; Central European (Mac)
    */
   { MIBUNDEF, 10029, STRING_ENCODING_WINDOWS_10029, IN_FULL_ICU, 0,
      { "Windows-10029", "x-mac-ce", "macce", "maccentraleurope",
        "x-mac-centraleurroman", "macos-29-10.2", NULL }
   },
   /*
    * x-mac-icelandic: Icelandic (Mac)
    *                  Not supported by ICU
    */
   { MIBUNDEF, 10079, STRING_ENCODING_WINDOWS_10079, UNSUPPORTED, 0,
      { "Windows-10079", "x-mac-icelandic", NULL }
   },
   /*
    * x-mac-turkish: Turkish (Mac)
    */
   { MIBUNDEF, 10081, STRING_ENCODING_WINDOWS_10081, IN_FULL_ICU, 0,
      { "Windows-10081", "x-mac-turkish", "mactr",
        "macos-35-10.2", NULL }
   },
   /*
    * x-mac-croatian: Croatian (Mac)
    *                 Not supported by ICU
    */
   { MIBUNDEF, 10082, STRING_ENCODING_WINDOWS_10082, UNSUPPORTED, 0,
      { "Windows-10082", "x-mac-croatian", NULL }
   },
   /*
    * x-Chinese_CNS: CNS Taiwan; Chinese Traditional (CNS)
    *                Not supported by ICU
    */
   { MIBUNDEF, 20000, STRING_ENCODING_WINDOWS_20000, UNSUPPORTED, 0,
      { "Windows-20000", "x-Chinese_CNS", NULL }
   },
   /*
    * x-cp20001: TCA Taiwan
    *            Not supported by ICU
    */
   { MIBUNDEF, 20001, STRING_ENCODING_WINDOWS_20001, UNSUPPORTED, 0,
      { "Windows-20001", "x-cp20001", NULL }
   },
   /*
    * x_Chinese-Eten: Eten Taiwan; Chinese Traditional (Eten)
    *                 Not supported by ICU
    */
   { MIBUNDEF, 20002, STRING_ENCODING_WINDOWS_20002, UNSUPPORTED, 0,
      { "Windows-20002", "x_Chinese-Eten", NULL }
   },
   /*
    * x-cp20003: IBM5550 Taiwan
    *            Not supported by ICU
    */
   { MIBUNDEF, 20003, STRING_ENCODING_WINDOWS_20003, UNSUPPORTED, 0,
      { "Windows-20003", "x-cp20003", NULL }
   },
   /*
    * x-cp20004: TeleText Taiwan
    *            Not supported by ICU
    */
   { MIBUNDEF, 20004, STRING_ENCODING_WINDOWS_20004, UNSUPPORTED, 0,
      { "Windows-20004", "x-cp20004", NULL }
   },
   /*
    * x-cp20005: Wang Taiwan
    *            Not supported by ICU
    */
   { MIBUNDEF, 20005, STRING_ENCODING_WINDOWS_20005, UNSUPPORTED, 0,
      { "Windows-20005", "x-cp20005", NULL }
   },
   /*
    * x-IA5: IA5 (IRV International Alphabet No. 5, 7-bit);
    *        Western European (IA5)
    *        Not supported by ICU
    */
   { MIBUNDEF, 20105, STRING_ENCODING_WINDOWS_20105, UNSUPPORTED, 0,
      { "Windows-20105", "x-IA5", NULL }
   },
   /*
    * x-IA5-German: IA5 German (7-bit)
    *               Not supported by ICU
    */
   { MIBUNDEF, 20106, STRING_ENCODING_WINDOWS_20106, UNSUPPORTED, 0,
      { "Windows-20106", "x-IA5-German", NULL }
   },
   /*
    * x-IA5-Swedish: IA5 Swedish (7-bit)
    *                Not supported by ICU
    */
   { MIBUNDEF, 20107, STRING_ENCODING_WINDOWS_20107, UNSUPPORTED, 0,
      { "Windows-20107", "x-IA5-Swedish", NULL }
   },
   /*
    * x-IA5-Norwegian: IA5 Norwegian (7-bit)
    *                  Not supported by ICU
    */
   { MIBUNDEF, 20108, STRING_ENCODING_WINDOWS_20108, UNSUPPORTED, 0,
      { "Windows-20108", "x-IA5-Norwegian", NULL }
   },
   /*
    * x-cp20269: ISO 6937 Non-Spacing Accent
    *            Not supported by ICU
    */
   { MIBUNDEF, 20269, STRING_ENCODING_WINDOWS_20269, UNSUPPORTED, 0,
      { "Windows-20269", "x-cp20269", NULL }
   },
   /*
    * x-EBCDIC-KoreanExtended: IBM EBCDIC Korean Extended
    *                          Not supported by ICU
    */
   { MIBUNDEF, 20833, STRING_ENCODING_WINDOWS_20833, UNSUPPORTED, 0,
      { "Windows-20833", "x-EBCDIC-KoreanExtended", NULL }
   },
   /*
    * x-cp20949: Korean Wansung
    *            Not supported by ICU
    */
   { MIBUNDEF, 20949, STRING_ENCODING_WINDOWS_20949, UNSUPPORTED, 0,
      { "Windows-20949", "x-cp20949", NULL }
   },
   /*
    * cp1025: IBM EBCDIC Cyrillic Serbian-Bulgarian
    *         ICU doesn't have alias "Windows-21025", use "cp1025"
    */
   { MIBUNDEF, 21025, STRING_ENCODING_WINDOWS_21025, IN_FULL_ICU, 0,
      { "cp1025", "ibm-1025", "1025", "ibm-1025_P100-1995", NULL }
   },
   /*
    * Windows-21027: (deprecated)
    *                Not supported by ICU
    */
   { MIBUNDEF, 21027, STRING_ENCODING_WINDOWS_21027, UNSUPPORTED, 0,
      { "Windows-21027", NULL }
   },
   /*
    * x-Europa: Europa 3
    *           Not supported by ICU
    */
   { MIBUNDEF, 29001, STRING_ENCODING_WINDOWS_29001, UNSUPPORTED, 0,
      { "Windows-29001", "x-Europa", NULL }
   },
   /*
    * iso-8859-8-i: ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
    *               Windows duplicate of ISO-8859-8 (Windows-28598)
    *               ICU doesn't have alias "Windows-38598", use 
    *               "iso-8859-8-i"
    */
   { MIBUNDEF, 38598, STRING_ENCODING_WINDOWS_38598, IN_FULL_ICU, 0,
      { "iso-8859-8-i", NULL }
   },
   /*
    * csISO2022JP: ISO 2022 Japanese with halfwidth Katakana;
    *              Japanese (JIS-Allow 1 byte Kana)
    *              handled by ICU with ISO-2022-JP
    */
   { MIBUNDEF, 50221, STRING_ENCODING_WINDOWS_50221, SUPPORTED, 0,
      { "csISO2022JP", NULL }
   },
   /*
    * iso-2022-jp: ISO 2022 Japanese JIS X 0201-1989;
    *              Japanese (JIS-Allow 1 byte Kana - SO/SI)
    *              handled by ICU with ISO-2022-JP
    */
   { MIBUNDEF, 50222, STRING_ENCODING_WINDOWS_50222, IN_FULL_ICU, 0,
      { "ISO-2022-JP", NULL }
   },
   /*
    * Windows-50229: ISO 2022 Traditional Chinese
    *                Not supported by ICU
    */
   { MIBUNDEF, 50229, STRING_ENCODING_WINDOWS_50229, UNSUPPORTED, 0,
      { "Windows-50229", NULL }
   },
   /*
    * Windows-50930: EBCDIC Japanese (Katakana) Extended
    *                Not supported by ICU
    */
   { MIBUNDEF, 50930, STRING_ENCODING_WINDOWS_50930, UNSUPPORTED, 0,
      { "Windows-50930", NULL }
   },
   /*
    * Windows-50931: EBCDIC US-Canada and Japanese
    *                Not supported by ICU
    */
   { MIBUNDEF, 50931, STRING_ENCODING_WINDOWS_50931, UNSUPPORTED, 0,
      { "Windows-50931", NULL }
   },
   /*
    * Windows-50933: EBCDIC Korean Extended and Korean
    *                Not supported by ICU
    */
   { MIBUNDEF, 50933, STRING_ENCODING_WINDOWS_50933, UNSUPPORTED, 0,
      { "Windows-50933", NULL }
   },
   /*
    * Windows-50935: EBCDIC Simplified Chinese Extended and Simplified Chinese
    *                Not supported by ICU
    */
   { MIBUNDEF, 50935, STRING_ENCODING_WINDOWS_50935, UNSUPPORTED, 0,
      { "Windows-50935", NULL }
   },
   /*
    * Windows-50936: EBCDIC Simplified Chinese
    *                Not supported by ICU
    */
   { MIBUNDEF, 50936, STRING_ENCODING_WINDOWS_50936, UNSUPPORTED, 0,
      { "Windows-50936", NULL }
   },
   /*
    * Windows-50937: EBCDIC US-Canada and Traditional Chinese
    *                Not supported by ICU
    */
   { MIBUNDEF, 50937, STRING_ENCODING_WINDOWS_50937, UNSUPPORTED, 0,
      { "Windows-50937", NULL }
   },
   /*
    * Windows-50939: EBCDIC Japanese (Latin) Extended and Japanese
    *                Not supported by ICU
    */
   { MIBUNDEF, 50939, STRING_ENCODING_WINDOWS_50939, UNSUPPORTED, 0,
      { "Windows-50939", NULL }
   },
   /*
    * EUC-CN: EUC Simplified Chinese; Chinese Simplified (EUC)
    *         Route to GB2312
    */
   { MIBUNDEF, 51936, STRING_ENCODING_WINDOWS_51936, IN_FULL_ICU, 0,
      { "EUC-CN", NULL }
   },
   /*
    * Windows-51950: EUC Traditional Chinese
    *                Not supported by ICU
    */
   { MIBUNDEF, 51950, STRING_ENCODING_WINDOWS_51950, UNSUPPORTED, 0,
      { "Windows-51950", NULL }
   },
   /*
    * x-iscii-de: ISCII Devanagari
    */
   { MIBUNDEF, 57002, STRING_ENCODING_WINDOWS_57002, SUPPORTED, 0,
      { "Windows-57002", "x-iscii-de", "iscii-dev", "ibm-4902", NULL }
   },
   /*
    * x-iscii-be: ISCII Bengali
    */
   { MIBUNDEF, 57003, STRING_ENCODING_WINDOWS_57003, SUPPORTED, 0,
      { "Windows-57003", "x-iscii-be", "iscii-bng", NULL }
   },
   /*
    * x-iscii-ta: ISCII Tamil
    */
   { MIBUNDEF, 57004, STRING_ENCODING_WINDOWS_57004, SUPPORTED, 0,
      { "Windows-57004", "x-iscii-ta", "iscii-tml", NULL }
   },
   /*
    * x-iscii-te: ISCII Telugu
    */
   { MIBUNDEF, 57005, STRING_ENCODING_WINDOWS_57005, SUPPORTED, 0,
      { "Windows-57005", "x-iscii-te", "iscii-tlg", NULL }
   },
   /*
    * x-iscii-as: ISCII Assamese
    */
   { MIBUNDEF, 57006, STRING_ENCODING_WINDOWS_57006, SUPPORTED, 0,
      { "Windows-57006", "x-iscii-as", NULL }
   },
   /*
    * x-iscii-or: ISCII Oriya
    */
   { MIBUNDEF, 57007, STRING_ENCODING_WINDOWS_57007, SUPPORTED, 0,
      { "Windows-57007", "x-iscii-or", "iscii-ori", NULL }
   },
   /*
    * x-iscii-ka: ISCII Kannada
    */
   { MIBUNDEF, 57008, STRING_ENCODING_WINDOWS_57008, SUPPORTED, 0,
      { "Windows-57008", "x-iscii-ka", "iscii-knd", NULL }
   },
   /*
    * x-iscii-ma: ISCII Malayalam
    */
   { MIBUNDEF, 57009, STRING_ENCODING_WINDOWS_57009, SUPPORTED, 0,
      { "Windows-57009", "x-iscii-ma", "iscii-mlm", NULL }
   },
   /*
    * x-iscii-gu: ISCII Gujarati
    */
   { MIBUNDEF, 57010, STRING_ENCODING_WINDOWS_57010, SUPPORTED, 0,
      { "Windows-57010", "x-iscii-gu", "x-iscii-guj", NULL }
   },
   /*
    * x-iscii-pa: ISCII Punjabi
    */
   { MIBUNDEF, 57011, STRING_ENCODING_WINDOWS_57011, SUPPORTED, 0,
      { "Windows-57011", "x-iscii-pa", "iscii-gur", NULL }
   },
};


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeNormalizeEncodingName --
 *
 *      Normalizes a US-ASCII encoding name by discarding all
 *      non-alphanumeric characters and converting to lower-case.
 *
 * Results:
 *      The allocated, normalized encoding name in NUL-terminated
 *      US-ASCII bytes.  Caller must free.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
UnicodeNormalizeEncodingName(const char *encodingName) // IN
{
   char *result;
   char *currentResult;

   ASSERT(encodingName);

   result = Util_SafeMalloc(strlen(encodingName) + 1);
   currentResult = result;

   for (currentResult = result; *encodingName != '\0'; encodingName++) {
      // The explicit cast from char to int is necessary for Netware builds.
      if (isalnum((int) *encodingName)) {
         *currentResult = tolower(*encodingName);
         currentResult++;
      }
   }

   *currentResult = '\0';

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeIANALookup --
 *
 *      Lookup an encoding name in the IANA cross reference table.
 *
 * Results:
 *      The index of the encoding within the table
 *      -1 if the encoding is not found
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static int
UnicodeIANALookup(const char *encodingName) // IN
{
   char *name = NULL;
   char *candidate = NULL;
   const char *p;
   int i;
   int j;
   int acp;
   void *idx;
   size_t windowsPrefixLen = sizeof "windows-" - 1 /* NUL */;

   if (UNLIKELY(unicodeEncCache == NULL)) {
      unicodeEncCache =
         HashTable_AllocOnce(&unicodeHashTablePtr, 128,
                             HASH_ISTRING_KEY | HASH_FLAG_ATOMIC |
                             HASH_FLAG_COPYKEY, NULL);
   }

   if (unicodeEncCache != NULL &&
       HashTable_Lookup(unicodeEncCache, encodingName, &idx)) {
      return (int)(uintptr_t)idx;
   }

   /*
    * check for Windows-xxxx encoding names generated from GetACP()
    * code page numbers, see: CodeSetOld_GetCurrentCodeSet()
    */
   if (   strncmp(encodingName, "windows-", windowsPrefixLen) == 0
       || strncmp(encodingName, "Windows-", windowsPrefixLen) == 0) {
      p = encodingName + windowsPrefixLen;
      acp = 0;

      // The explicit cast from char to int is necessary for Netware builds.
      while (*p && isdigit((int)*p)) {
         acp *= 10;
         acp += *p - '0';
         p++;
      }
      if (!*p) {
         for (i = 0; i < ARRAYSIZE(xRef); i++) {
            if (xRef[i].winACP == acp) {
               goto done;
            }
         }
      }
   }

   // Try the raw names first to avoid the expense of normalizing everything.
   for (i = 0; i < ARRAYSIZE(xRef); i++) {
      for (j = 0; (p = xRef[i].names[j]) != NULL; j++) {
         if (strcmp(encodingName, p) == 0) {
            goto done;
         }
      }
   }

   name = UnicodeNormalizeEncodingName(encodingName);
   for (i = 0; i < ARRAYSIZE(xRef); i++) {
      for (j = 0; (p = xRef[i].names[j]) != NULL; j++) {
         candidate = UnicodeNormalizeEncodingName(p);
         if (strcmp(name, candidate) == 0) {
            goto done;
         }
         free(candidate);
      }
   }
   free(name);

   /*
    * Did not find a matching name.  Don't validate encoding names
    * here, unrecognized encoding will be caught when converting
    * from name to enum.
    */
   Log("%s: Did not find an IANA match for encoding \"%s\"\n",
       __FUNCTION__, encodingName);
   return -1;

done:
   free(name);
   free(candidate);

   if (unicodeEncCache != NULL) {
      HashTable_Insert(unicodeEncCache, encodingName, (void *)(uintptr_t)i);
   }

   return i;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_EncodingEnumToName --
 *
 *      Converts a StringEncoding enum value to the equivalent
 *      encoding name.
 *
 * Results:
 *      A NUL-terminated US-ASCII string containing the name of the
 *      encoding.  Encodings follow the preferred MIME encoding name
 *      from IANA's Character Sets standard.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

const char *
Unicode_EncodingEnumToName(StringEncoding encoding) // IN
{
   int i;

   encoding = Unicode_ResolveEncoding(encoding);

   /* If you hit this, you probably need to call Unicode_Init() */
   ASSERT(encoding != STRING_ENCODING_UNKNOWN);

   /*
    * Look for a match in the xRef table. If found, return the
    * preferred MIME name. Whether ICU supports this encoding or
    * not isn't material here.
    */

   for (i = 0; i < ARRAYSIZE(xRef); i++) {
      if (encoding == xRef[i].encoding) {
	 return xRef[i].names[xRef[i].preferredMime];
      }
   }

   Log("%s: Unknown encoding %d.\n", __FUNCTION__, encoding);
   NOT_REACHED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_EncodingNameToEnum --
 *
 *      Converts a NUL-terminated US-ASCII string encoding name
 *      to the equivalent enum.
 *
 * Results:
 *      The StringEncoding enum value corresponding to the name, or
 *      STRING_ENCODING_UNKNOWN if the encoding name is not supported.
 *
 *      Inside tools all recognized local encodings are supported.
 *      If the local encoding is not available in our copy of ICU,
 *      fall back to the guest's facilities for converting between 
 *      the local encoding and UTF-8.
 *
 * Side effects:
 *      In tools, finding an unsupported encoding disables ICU and
 *      switches to codesetOld support.
 *
 *-----------------------------------------------------------------------------
 */

StringEncoding
Unicode_EncodingNameToEnum(const char *encodingName) // IN
{
   int idx;

   idx = UnicodeIANALookup(encodingName);
   if (idx < 0) {
      return STRING_ENCODING_UNKNOWN;
   }
   if (xRef[idx].isSupported) {
      return xRef[idx].encoding;
   }
#if defined(VMX86_TOOLS) && (!defined(OPEN_VM_TOOLS) || defined(USE_ICU))
   if (idx == UnicodeIANALookup(CodeSet_GetCurrentCodeSet())) {
      CodeSet_DontUseIcu();
      return xRef[idx].encoding;
   }
#endif
   return STRING_ENCODING_UNKNOWN;
}


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeGetCurrentEncodingInternal --
 *
 *      Calls CodeSet_GetCurrentCodeSet() and returns the corresponding
 *      encoding.
 *
 * Results:
 *      The current encoding.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

StringEncoding
UnicodeGetCurrentEncodingInternal(void)
{
   StringEncoding encoding =
      Unicode_EncodingNameToEnum(CodeSet_GetCurrentCodeSet());

   ASSERT(Unicode_IsEncodingValid(encoding));
   return encoding;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_GetCurrentEncoding --
 *
 *      Return the current encoding (corresponding to
 *      CodeSet_GetCurrentCodeSet()).
 *
 * Results:
 *      The current encoding.
 *
 * Side effects:
 *      Since the return value of CodeSet_GetCurrentCodeSet() and our
 *      look-up table do not change, we memoize the value.
 *
 *-----------------------------------------------------------------------------
 */

StringEncoding
Unicode_GetCurrentEncoding(void)
{
   static StringEncoding encoding = STRING_ENCODING_UNKNOWN;

   if (UNLIKELY(encoding == STRING_ENCODING_UNKNOWN)) {
      encoding = UnicodeGetCurrentEncodingInternal();
   }

   return encoding;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_ResolveEncoding --
 *
 *      Resolves a meta-encoding enum value (e.g. STRING_ENCODING_DEFAULT) to
 *      a concrete one (e.g. STRING_ENCODING_UTF8).
 *
 * Results:
 *      A StringEncoding enum value.  May return STRING_ENCODING_UNKNOWN.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

StringEncoding
Unicode_ResolveEncoding(StringEncoding encoding)  // IN:
{
   if (encoding == STRING_ENCODING_DEFAULT) {
      encoding = Unicode_GetCurrentEncoding();
   }

   ASSERT(Unicode_IsEncodingValid(encoding));

   return encoding;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_IsEncodingValid --
 *
 *      Checks whether we support the given encoding.
 *
 * Results:
 *      TRUE if supported, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Unicode_IsEncodingValid(StringEncoding encoding)  // IN
{
   return encoding >= STRING_ENCODING_FIRST && 
          encoding < STRING_ENCODING_MAX_SPECIFIED;
}

/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeInitInternal --
 *
 *      Convert argv and environment from default encoding into
 *      unicode and initialize the cache of the native code set name
 *      used to resolve STRING_ENCODING_DEFAULT.
 *
 *      wargv takes precedence over argv as input if both are
 *      specified, likewise with wenvp/envp.
 *
 * Results:
 *      returns on success
 *      errors are terminal
 *
 * Side effects:
 *      Calling CodeSet_GetCurrentCodeSet() initializes the cache of the
 *      native code set name.  The cached name is used to resolve references
 *      to STRING_ENCODING_DEFAULT in unicode functions.
 *
 *      argv/envp will be rewritten to point to newly allocated memory. This
 *      memory should be freed by calling Unicode_Shutdown().
 *
 *-----------------------------------------------------------------------------
 */

static void
UnicodeInitInternal(int argc,               // IN
                    const char *icuDataDir, // IN
                    utf16_t **wargv,        // IN/OUT (OPT)
                    utf16_t **wenvp,        // IN/OUT (OPT)
                    char ***argv,           // IN/OUT (OPT)
                    char ***envp)           // IN/OUT (OPT)
{
#if !defined(__APPLE__) && !defined(VMX86_SERVER)
   char **list;
   StringEncoding encoding;
   const char *currentCodeSetName;
#endif
   Bool success = FALSE;
   char panicMsg[1024];
   static volatile Bool inited = FALSE;
   static Atomic_uint32 locked = {0};

   panicMsg[0] = '\0';

   /*
    * This function must be callable multiple times. We can't depend
    * on lib/sync, so cheese it.
    */
   while (1 == Atomic_ReadIfEqualWrite(&locked, 0, 1)) {
#if !defined(__FreeBSD__)
      usleep(250 * 1000);
#endif
   }

   if (inited) {
      success = TRUE;
      goto exit;
   }

   /*
    * Always init the codeset module first.
    */
   if (!CodeSet_Init(icuDataDir)) {
      snprintf(panicMsg, sizeof panicMsg, "Failed to initialize codeset.\n");
      goto exit;
   }

   // UTF-8 native encoding for these two
#if !defined(__APPLE__) && !defined(VMX86_SERVER)
   currentCodeSetName = CodeSet_GetCurrentCodeSet();
   encoding = Unicode_EncodingNameToEnum(currentCodeSetName);
   if (!Unicode_IsEncodingValid(encoding)) {
      snprintf(panicMsg, sizeof panicMsg,
              "Unsupported local character encoding \"%s\".\n",
               currentCodeSetName);
      goto exit;
   }

   if (wargv) {
      list = Unicode_AllocList((char **)wargv, argc + 1, STRING_ENCODING_UTF16);
      if (!list) {
         snprintf(panicMsg, sizeof panicMsg, "Unicode_AllocList1 failed.\n");
         goto exit;
      }
      *argv = list;
   } else if (argv) {
      list = Unicode_AllocList(*argv, argc + 1, STRING_ENCODING_DEFAULT);
      if (!list) {
         snprintf(panicMsg, sizeof panicMsg, "Unicode_AllocList2 failed.\n");
         goto exit;
      }
      *argv = list;
   }

   if (wenvp) {
      list = Unicode_AllocList((char **)wenvp, -1, STRING_ENCODING_UTF16);
      if (!list) {
         snprintf(panicMsg, sizeof panicMsg, "Unicode_AllocList3 failed.\n");
         goto exit;
      }
      *envp = list;
   } else if (envp) {
      list = Unicode_AllocList(*envp, -1, STRING_ENCODING_DEFAULT);
      if (!list) {
         snprintf(panicMsg, sizeof panicMsg, "Unicode_AllocList failed.\n");
         goto exit;
      }
      *envp = list;
   }
#endif // !__APPLE__ && !VMX86_SERVER

   inited = TRUE;
   success = TRUE;

  exit:
   Atomic_Write(&locked, 0);

   if (!success) {
      panicMsg[sizeof panicMsg - 1] = '\0';
      Panic("%s", panicMsg);
      exit(1);
   }
}


void
Unicode_InitW(int argc,                // IN
              utf16_t **wargv,         // IN/OUT (OPT)
              utf16_t **wenvp,         // IN/OUT (OPT)
              char ***argv,            // IN/OUT (OPT)
              char ***envp)            // IN/OUT (OPT)
{
   UnicodeInitInternal(argc, NULL, wargv, wenvp, argv, envp);
}


void
Unicode_InitEx(int argc,                // IN
               char ***argv,            // IN/OUT (OPT)
               char ***envp,            // IN/OUT (OPT)
               const char *icuDataDir)  // IN (OPT)
{
   UnicodeInitInternal(argc, icuDataDir, NULL, NULL, argv, envp);
}


void
Unicode_Init(int argc,        // IN
             char ***argv,    // IN/OUT (OPT)
             char ***envp)    // IN/OUT (OPT)
{
   UnicodeInitInternal(argc, NULL, NULL, NULL, argv, envp);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_Shutdown --
 *
 *      Frees memory allocated by UnicodeInitInternal().
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
Unicode_Shutdown(int argc,              // IN
                 char **argv,           // IN (OPT)
                 char **envp)           // IN (OPT)
{
   HashTable_FreeUnsafe(unicodeEncCache);
   unicodeEncCache = NULL;

   // Please refer to comments in UnicodeInitInternal
#if !defined(__APPLE__) && !defined(VMX86_SERVER)
   if (argv != NULL) {
      Util_FreeStringList(argv, argc + 1);
   }

   if (envp != NULL) {
      Util_FreeStringList(envp, -1);
   }
#endif
}


#ifdef TEST_CUSTOM_ICU_DATA_FILE
/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeICUTest --
 *
 *      Test custom ICU data files
 *
 *      Checks string encodings for whether they are supported in 
 *      the xRef cross reference table and calls ICU with the 
 *      encodings to try to convert a simple ASCII string.  Note 
 *      that GB-2312-80 (Chinese) does not support ASCII, so it 
 *      is expected to fail the conversion.
 *
 *      To test custom ICU files, change the second arg in the call to
 *      UnicodeInitInternal() above to the *directory* containing the ICU
 *      data file, and add a call to this function.  Note that the name of
 *      the data file is hard coded to "icudt44l.dat" in lib/misc/codeset.c.
 *      Also note that in devel builds, lib/misc/codeset.c will override the 
 *      icu directory argument with a path to the toolchain, so that may need 
 *      to be disabled, too.
 *
 * Results:
 *      Prints results to stdout.
 *
 * Side effects:
 *
 *-----------------------------------------------------------------------------
 */
 
void
UnicodeICUTest(void)
{
   StringEncoding enc, enc2;
   Bool supported;
   Bool redirected;
   Bool canGetBytes;

   for (enc = STRING_ENCODING_FIRST; enc < STRING_ENCODING_MAX_SPECIFIED;
        enc++ ) {
      const char *name =  Unicode_EncodingEnumToName(enc);

      enc2 = Unicode_EncodingNameToEnum(name);
      redirected = FALSE;
      if (enc2 == STRING_ENCODING_UNKNOWN) {
         supported = FALSE;
      } else {
         supported = TRUE;
         if (enc != enc2) {
            redirected = TRUE;  // xRef mapped to different entry
         }
      }
      canGetBytes = Unicode_CanGetBytesWithEncoding("Hello world", enc);
      printf("%s: supported:%s redirected:%s works:%s result:%s\n",
             name, supported ? "yes" : "no ", redirected ? "yes" : "no ", 
             canGetBytes ? "yes" : "no ",
             (supported && enc != STRING_ENCODING_GB_2312_80) == 
             canGetBytes ? "pass" : "FAIL");
   }
}
#endif

  070701000001FC000081A40000000000000000000000016822550500001383000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/lib/unicode/unicodeStatic.c    /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * unicodeStatic.c --
 *
 *      Manages memory for static const char *literal strings
 *      created like:
 *
 *         const char *c = U_UNESCAPE("Copyright \\u00A9 VMware, Inc.");
 *
 *      Uses two HashTables to hold static const char *strings. Static
 *      const char *strings are keyed off the ASCII bytes passed to the
 *      static macros.
 *
 *      Unescaped strings are kept separate from escaped strings so
 *      users can expect a literal "\\" to stay as-is by default.
 *
 *      This implementation exists to works around gcc's lack of an
 *      intrinsic 16-bit wide character type; wchar_t on gcc is 32
 *      bits wide, which is not useful for most Unicode algorithms.
 *
 *      Note that the Win32 compiler does have an intrinsic 16-bit
 *      wide character type (L"foo"), so we can optimize for that case
 *      in a later implementation.
 *
 *      If GCC later offers an intrinsic 16-bit wide character type,
 *      we can completely get rid of this implementation.
 */

#include "vmware.h"
#include "hashTable.h"
#include "vm_atomic.h"
#include "hashTable.h"
#include "unicodeBase.h"
#include "unicodeInt.h"
#include "util.h"

/* These are Implicitly initialized to NULL */
static Atomic_Ptr UnicodeStringTable;
static Atomic_Ptr UnicodeUnescapedStringTable;


/*
 *-----------------------------------------------------------------------------
 *
 * UnicodeHashFree --
 *
 *      Called by the hash table functions when a value must be replaced.
 *
 * Results:
 *	The argument, a unicode, is freed.
 *
 * Side effects:
 *	None.
 *
 *-----------------------------------------------------------------------------
 */

static void
UnicodeHashFree(void *v)  // IN:
{
   free(v);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Unicode_GetStatic --
 *
 *      Helper function for the U_UNESCAPE() macro.
 *
 *      Given a NUL-terminated ASCII string, returns a const char *
 *      string containing the string's contents.
 *
 *      If unescape is TRUE, then \\uABCD becomes the Unicode code
 *      point U+ABCD and \\U001FABCD becomes the Unicode code point
 *      U+1FABCD in the resulting string.
 *
 * Results:
 *      A const char * string.  Memory is managed inside this module;
 *      caller does not need to free.
 *
 * Side effects:
 *      Creates the UnicodeStringTable and UnicodeUnescapedStringTable hash
 *      tables if it don't yet exist.
 *      Creates and inserts a Unicode string into the appropriate static
 *      string table if the key 'asciiBytes' is not found in the table.
 *
 *-----------------------------------------------------------------------------
 */

const char *
Unicode_GetStatic(const char *asciiBytes, // IN
                  Bool unescape)          // IN
{
   char *result = NULL;
   HashTable *stringTable;

   if (unescape) {
      stringTable = HashTable_AllocOnce(&UnicodeUnescapedStringTable, 4096, 
                                        HASH_FLAG_ATOMIC | HASH_STRING_KEY,
                                        UnicodeHashFree);
   } else {
      stringTable = HashTable_AllocOnce(&UnicodeStringTable, 4096, 
                                        HASH_FLAG_ATOMIC | HASH_STRING_KEY,
                                        UnicodeHashFree);
   }

   /*
    * Attempt a lookup for the key value; if it is found things are easy and
    * fine. Otherwise HashTable_LookupOrInsert is used to attempt to enter
    * the data in a racey manner. Should multiple threads attempt to enter
    * the same key concurrently one thread will get the entered data and the
    * other threads will detect that their entries were rejected; they
    * discard their copies of the data and use the entered data (values
    * will be stable).
    */

   if (!HashTable_Lookup(stringTable, asciiBytes, (void **) &result)) {
      char *newData = UnicodeAllocStatic(asciiBytes, unescape);

      if (newData) {
         result = HashTable_LookupOrInsert(stringTable, asciiBytes, newData);

         if (result != newData) {
            free(newData);
         }
      }
   }

   return result;
}
 070701000001FD000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/lib/user   070701000001FE000081A40000000000000000000000016822550500000418000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/lib/user/Makefile.am   ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libUser.la

libUser_la_SOURCES =
libUser_la_SOURCES += util.c
libUser_la_SOURCES += utilBacktrace.c

AM_CFLAGS = @LIB_USER_CPPFLAGS@
070701000001FF000081A40000000000000000000000016822550500004F26000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/lib/user/util.c    /*********************************************************
 * Copyright (c) 1998-2020, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * util.c --
 *
 *    misc util functions
 */

#undef WIN32_LEAN_AND_MEAN

#if defined(__linux__) && !defined(VMX86_TOOLS)
#define _GNU_SOURCE
#endif

#include "vm_ctype.h"

#if defined(_WIN32)
# include <winsock2.h> // also includes windows.h
# include <io.h>
# include <process.h>
# include "getoptWin32.h"
#endif

#include <fcntl.h>
#include <stdlib.h>
#include <limits.h>
#include <stddef.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>

#if !defined(_WIN32)
#  include <unistd.h>
#  include <getopt.h>
#  include <pwd.h>
#  include <dlfcn.h>
#endif

#if defined(__linux__) && !defined(VMX86_TOOLS) && !defined(__ANDROID__)
#  include <link.h>
#endif

#include "vmware.h"
#include "msg.h"
#include "util.h"
#include "str.h"
#include "su.h"
#include "posix.h"
#include "file.h"
#include "escape.h"
#include "base64.h"
#include "unicode.h"
#include "posix.h"
#include "random.h"

#ifndef O_BINARY
#define O_BINARY 0
#endif

/*
 * Win32 doesn't have an iovec type, so do this here to avoid messy games in
 * the header files.  --Jeremy.
 */

struct UtilVector {
   void *base;
   int   len;
};



/*
 *----------------------------------------------------------------------
 *
 * Util_Init --
 *
 *      Opportunity to confidence check things
 *
 * Results:
 *	Bool - TRUE (this should never fail)
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

Bool
Util_Init(void)
{
#ifdef VMX86_DEVEL
   /*
    * Confidence check Str_Snprintf so that we're never thrown off guard
    * by a change in the underlying libraries that Str_Snprintf doesn't
    * catch and wrap properly.
    */
   {
      char buf[2] = { 'x', 'x' };
      int rv;

      rv = Str_Snprintf(buf, sizeof buf, "a");
      ASSERT(rv == 1);
      ASSERT(!strcmp(buf, "a"));

      rv = Str_Snprintf(buf, sizeof buf, "ab");
      ASSERT(rv == -1);
      ASSERT(!strcmp(buf, "a"));
   }
#endif
   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_Checksum32 --
 *
 * Checksums a uint32 aligned block by progressive XOR.  Basically parity
 * checking of each bit position.
 *
 *----------------------------------------------------------------------
 */

uint32
Util_Checksum32(const uint32 *buf, int len)
{
   uint32 checksum = 0;
   int i;

   ASSERT((len % 4) == 0);
   for (i = 0; i < len; i+=4) checksum ^= *(buf++);
   return checksum;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_Checksum --
 *
 * Checksums a block by progressive XOR.  Basically parity
 * checking of each bit position.
 *
 *----------------------------------------------------------------------
 */

uint32
Util_Checksum(const uint8 *buf, int len)
{
   uint32 checksum;
   int remainder, shift;

   remainder = len % 4;
   len -= remainder;

   checksum = Util_Checksum32((uint32 *)buf, len);

   buf += len;
   shift = 0;
   while (remainder--) {
      /*
       * Note: this is little endian.
       */
      checksum ^= (*buf++ << shift);
      shift += 8;
   }

   return checksum;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_Checksumv --
 *
 * Checksums an iovector by progressive XOR.  Basically parity checking of
 * each bit position.
 *
 *----------------------------------------------------------------------
 */

uint32
Util_Checksumv(void *iov,      // IN
               int numEntries) // IN
{
   uint32 checksum = 0;
   struct UtilVector *vector = (struct UtilVector *) iov;
   int bytesSoFar = 0;

   while (numEntries-- > 0) {
      uint32 partialChecksum = Util_Checksum(vector->base, vector->len);
      int rotate = (bytesSoFar & 3) * 8;

      checksum ^= ((partialChecksum << rotate) |
                   (partialChecksum >> (32 - rotate)));
      bytesSoFar += vector->len;
      vector++;
   }

   return checksum;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_HashString --
 *
 *      Get a hash of the given NUL terminated string using the djb2
 *      hash algorithm.
 *
 * Results:
 *      The hashed value.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

uint32
Util_HashString(const char *str)  // IN:
{
   uint32 hash = 5381;
   int c;

   while ((c = *str++) != 0) {
      hash = ((hash << 5) + hash) + c;
   }

   return hash;
}


/*
 *----------------------------------------------------------------------
 *
 *  CRC_Compute --
 *
 *      computes the CRC of a block of data
 *
 * Results:
 *
 *      CRC code
 *
 * Side effects:
 *      Sets up the crc table if it hasn't already been computed.
 *
 *----------------------------------------------------------------------
 */

static void
UtilCRCMakeTable(uint32 crcTable[])
{
   uint32 c;
   int n, k;

   for (n = 0; n < 256; n++) {
      c = (uint32) n;
      for (k = 0; k < 8; k++) {
         if (c & 1) {
            c = 0xedb88320L ^ (c >> 1);
         } else {
            c = c >> 1;
         }
      }
      crcTable[n] = c;
   }
}

static INLINE_SINGLE_CALLER uint32
UtilCRCUpdate(uint32 crc,
              const uint8 *buf,
              int len)
{
   uint32 c = crc;
   int n;
   static uint32 crcTable[256];
   static int crcTableComputed = 0;

   if (!crcTableComputed) {
      UtilCRCMakeTable(crcTable);

      crcTableComputed = 1;
   }

   for (n = 0; n < len; n++) {
      c = crcTable[(c ^ buf[n]) & 0xff] ^ (c >> 8);
   }

   return c;
}

uint32
CRC_Compute(const uint8 *buf,
            int len)
{
   return UtilCRCUpdate(0xffffffffL, buf, len) ^ 0xffffffffL;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_FastRand --
 *
 *      Historical-name wrapper around Random_Simple.
 *      Deprecated: use Random_Fast, or Random_Simple directly.
 *
 * Results:
 *      A random number.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

uint32
Util_FastRand(uint32 seed)
{
   return Random_Simple(seed);
}


/*
 *----------------------------------------------------------------------
 *
 * Util_Throttle --
 *
 *   Use for throttling of warnings.
 *
 * Results:
 *    Will return TRUE for an increasingly sparse set of counter values:
 *    1, 2, ..., 100, 200, 300, ..., 10000, 20000, 30000, ..., .
 *
 * Side effects:
 *   None.
 *
 *----------------------------------------------------------------------
 */

Bool
Util_Throttle(uint32 count)  // IN:
{
   return count <     100                          ||
         (count <   10000 && count %     100 == 0) ||
         (count < 1000000 && count %   10000 == 0) ||
                             count % 1000000 == 0;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_Data2Buffer --
 *
 *    Format binary data for printing.
 *    Uses space as byte separator.
 *
 * Results:
 *    TRUE if all data fits into buffer, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

Bool
Util_Data2Buffer(char *buf,         // OUT
                 size_t bufSize,    // IN
                 const void *data0, // IN
                 size_t dataSize)   // IN
{
   return Util_Data2BufferEx(buf, bufSize, data0, dataSize, ' ');
}


/*
 *----------------------------------------------------------------------
 *
 * Util_Data2BufferEx --
 *
 *    Format binary data for printing.
 *    Uses custom byte separator. None if set to '\0'.
 *
 * Results:
 *    TRUE if all data fits into buffer, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

Bool
Util_Data2BufferEx(char *buf,         // OUT
                   size_t bufSize,    // IN
                   const void *data0, // IN
                   size_t dataSize,   // IN
                   char sep)          // IN
{
   size_t n; /* Chars to process from data0. */
   const Bool useSeparator = sep != 0;

   /* Number of processed chars that will fit in buf and leave space for trailing NUL. */
   const size_t outChars = useSeparator ? bufSize / 3 : (bufSize - 1) / 2;

   /* At least 1 byte (for NUL) must be available. */
   if (!bufSize) {
      return FALSE;
   }

   n = MIN(dataSize, outChars);
   if (n != 0) {
      const uint8 *data = data0;

      while (n > 0) {
         static const char digits[] = "0123456789ABCDEF";

         *buf++ = digits[*data >> 4];
         *buf++ = digits[*data & 0xF];
         if (useSeparator) {
            *buf++ = sep;
         }
         data++;
         n--;
      }
      if (useSeparator) {
         buf--; /* Overwrite the last separator with NUL. */
      }
   }
   *buf = 0;
   return dataSize <= outChars;
}


/*
 *----------------------------------------------------------------------
 *
 * Util_ExitProcessAbruptly
 *
 *    On Win32, terminate the process and all of its threads, without
 *    calling any of the DLL termination handlers.
 *
 *    On Linux, call _exit().
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
Util_ExitProcessAbruptly(int code) // IN
{
#if defined(_WIN32)
   TerminateProcess(GetCurrentProcess(), code);
#else
   _exit(code);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Util_ExitThread --
 *
 *    Terminate the running thread.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
Util_ExitThread(int code) // IN
{
#if defined(_WIN32)
   ExitThread(code);
#else
   exit(code);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Util_CompareDotted --
 *
 *      Compares two version numbers encoded as dotted strings.
 *
 * Results:
 *      0 if equal, -1 if s1 is less than s2, else 1.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

int
Util_CompareDotted(const char *s1, const char *s2)
{
   int i, x[5], y[5];

   for (i = 0; i < 5; i++) {
      x[i] = 0;
      y[i] = 0;
   }

   if (sscanf(s1, "%d.%d.%d.%d.%d", &x[0], &x[1], &x[2], &x[3], &x[4]) < 1) {
      x[0] = 1;
   }
   if (sscanf(s2, "%d.%d.%d.%d.%d", &y[0], &y[1], &y[2], &y[3], &y[4]) < 1) {
      y[0] = 1;
   }

   for (i = 0; i < 5; i++) {
      if (x[i] < y[i]) {
         return -1;
      }
      if (x[i] > y[i]) {
         return 1;
      }
   }

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_GetOpt --
 *
 *      A wrapper around getopt_long that avoids needing separate long and
 *      short option lists.
 *
 *      To use this, the array of option structs must:
 *      * Store the short option name in the 'val' member.
 *      * Set the 'name' member to NULL if the option has a short name but no
 *        long name.
 *      * For options that have only a long name, 'val' should be set to a
 *        unique value greater than UCHAR_MAX.
 *      * Terminate the array with a sentinel value that zero-initializes both
 *        'name' and 'val'.
 *
 * Results:
 *      See getopt_long.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
Util_GetOpt(int argc,                  // IN
            char * const *argv,        // IN
            const struct option *opts, // IN
            Util_NonOptMode mode,      // IN
            Bool manualErrorHandling)  // IN: True if the caller wants to handle error reporting.

{
   int ret = -1;

   struct option *longOpts = NULL;
   char *shortOptString = NULL;

   /*
    * In the worst case, each character needs "::" to indicate that it takes
    * an optional argument.
    */
   const size_t maxCharsPerShortOption = 3;
   const size_t modePrefixSize = 2; // "[+-][:]"

   size_t n = 0;
   size_t shortOptStringSize;

   while (!(opts[n].name == NULL && opts[n].val == 0)) {
      if (UNLIKELY(n == SIZE_MAX)) {
         /*
          * Avoid integer overflow.  If you have this many options, you're
          * doing something wrong.
          */
         ASSERT(FALSE);
         goto exit;
      }
      n++;
   }

   if (UNLIKELY(n > SIZE_MAX / sizeof *longOpts - 1)) {
      /* Avoid integer overflow. */
      ASSERT(FALSE);
      goto exit;
   }
   longOpts = malloc((n + 1) * sizeof *longOpts);
   if (longOpts == NULL) {
      goto exit;
   }

   if (UNLIKELY(n > (SIZE_MAX - modePrefixSize - 1 /* NUL */) /
                maxCharsPerShortOption)) {
      /* Avoid integer overflow. */
      ASSERT(FALSE);
      goto exit;
   }
   shortOptStringSize = n * maxCharsPerShortOption + modePrefixSize + 1 /* NUL */;
   shortOptString = malloc(shortOptStringSize);
   if (shortOptString == NULL) {
      goto exit;
   } else {
      struct option empty = { 0 };

      size_t i;
      struct option *longOptOut = longOpts;
      char *shortOptOut = shortOptString;

      // How to handle non-option arguments.
      switch (mode) {
         case UTIL_NONOPT_STOP:
            *shortOptOut++ = '+';
            break;
         case UTIL_NONOPT_ALL:
            *shortOptOut++ = '-';
            break;
         default:
            break;
      }

      if (manualErrorHandling) {
         /*
          * Make getopt return ':' instead of '?' if required arguments to
          * options are missing.
          */
         *shortOptOut++ = ':';
      }

      for (i = 0; i < n; i++) {
         int val = opts[i].val;

         if (opts[i].name != NULL) {
            *longOptOut++ = opts[i];
         }

         if (val > 0 && val <= UCHAR_MAX) {
            int argSpec = opts[i].has_arg;

            *shortOptOut++ = (char) val;

            if (argSpec != no_argument) {
               *shortOptOut++ = ':';

               if (argSpec == optional_argument) {
                  *shortOptOut++ = ':';
               }
            }
         }
      }

      ASSERT(longOptOut - longOpts <= n);
      *longOptOut = empty;

      ASSERT(shortOptOut - shortOptString < shortOptStringSize);
      *shortOptOut = '\0';
   }

   ret = getopt_long(argc, argv, shortOptString, longOpts, NULL);

exit:
   free(longOpts);
   free(shortOptString);
   return ret;
}




/*
 *-----------------------------------------------------------------------------
 *
 * Util_HasAdminPriv --
 *
 *    Determine if the calling code has administrator privileges --hpreg
 *
 * Results:
 *    1 if yes
 *    0 if no
 *    <0 on error
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

int
Util_HasAdminPriv(void)
{
#if defined(_WIN32)
#if defined(VM_WIN_UWP)
   return 0;
#else
   HANDLE token = INVALID_HANDLE_VALUE;
   int ret = -1;

   /*
    * Retrieve the access token of the calling thread --hpreg
    *
    * On some machines OpenThreadToken with openAsSelf set to FALSE fails.
    * Empirically, it seems that, in the security context of another user
    * (even when the impersonation token is at SecurityImpersonation level)
    * it is not able to obtain the thread token with TOKEN_DUPLICATE access.
    * Calling OpenThreadToken to open as self is more reliable and does not
    * seem to hurt. -- vui
    */

   if (OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE, TRUE,
          &token) == 0) {
      if (GetLastError() != ERROR_NO_TOKEN) {
         ret = -1;
         goto end;
      }

      if (OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE,
             &token) == 0) {
         ret = -2;
         goto end;
      }
   }

   ret = Util_TokenHasAdminPriv(token);

end:

   if (token != INVALID_HANDLE_VALUE) {
      if (CloseHandle(token) == 0 && ret >= 0) {
         ret = -13;
      }
   }

   return ret;
#endif // !VM_WIN_UWP
#else
   return Id_IsSuperUser() ? 1 : 0;
#endif
}

/*
 *-----------------------------------------------------------------------------
 *
 * Util_DeriveFileName --
 *
 *      This function is admittedly weird.  The basic idea is that we
 *      have a path to a dictionary file, and we need to make a path to
 *      a another file that's named in a similar way to that dictionary
 *      file (e.g., only difference is extension, or filename and
 *      extension).
 *
 *      This function returns a pointer to the result
 *
 * Results:
 *      Pointer to string (on success, caller should free string),
 *      otherwise NULL.
 *
 * Side effects:
 *      Allocates memory to be freed by caller.
 *
 *-----------------------------------------------------------------------------
 */

char *
Util_DeriveFileName(const char *source, // IN: path to dict file (incl filename)
                    const char *name,   // IN: what to replace filename with (optional)
                    const char *ext)    // IN: what to replace extension with (optional)
{
   char *returnResult = NULL;
   char *path = NULL;
   char *base = NULL;

   if (source == NULL || (name == NULL && ext == NULL)) {
      Warning("invalid use of function\n");
      return NULL;
   }
   File_GetPathName(source, &path, &base);

   /* If replacing name and extension */
   if (name != NULL) {
      free(base);

      /*
       * If the "name" we have to append is a relative path (i.e., not an
       * absolute path), then we need to concatenate the "name" to the
       * path of "source". If the path of "source" doesn't exist or is
       * just ".", then we don't need to bother with concatenating results
       * together.
       */

      if (!Util_IsAbsolutePath(name) && strlen(path) > 0 &&
          strcmp(path, ".") != 0) {
	 if (ext == NULL) {
	    returnResult = Str_SafeAsprintf(NULL, "%s%s%s",
                                            path, DIRSEPS, name);
	 } else {
            returnResult = Str_SafeAsprintf(NULL, "%s%s%s.%s",
                                            path, DIRSEPS, name, ext);
	 }
      } else {
	 /*
          * Path is non-existent or is just the current directory (or the
	  * result from the dictionary is an absolute path), so we
	  * just need to use the filename (using the DIRSEPS method above
          * for a non-existent path might result in something undesireable
	  * like "\foobar.vmdk")
	  */

	 if (ext == NULL) {
            returnResult = Util_SafeStrdup(name);
	 } else {
            returnResult = Str_SafeAsprintf(NULL, "%s.%s", name, ext);
	 }
      }
      free(path);
      return returnResult;
   }

   /* replacing only the file extension */

   /* strip off the existing file extension, if present */
   {
      char *p = Str_Strrchr(base, '.');
      if (p != NULL) {
	 *p = '\0';
      }
   }

   /* Combine disk path with parent path */
   if (strlen(path) > 0 && strcmp(path, ".") != 0) {
      returnResult = Str_SafeAsprintf(NULL, "%s%s%s.%s",
                                      path, DIRSEPS, base, ext);
   } else {
      /*
       * Path is non-existent or is just the current directory, so we
       * just need to use the filename (using the DIRSEPS method might
       * result in something undesireable like "\foobar.vmdk")
       */
      returnResult = Str_SafeAsprintf(NULL, "%s.%s", base, ext);
   }
   free(path);
   free(base);
   return returnResult;
}
  07070100000200000081A4000000000000000000000001682255050000315B000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/user/utilBacktrace.c   /*********************************************************
 * Copyright (C) 2013-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * utilBacktrace.c --
 *
 *    misc util functions
 */

#undef WIN32_LEAN_AND_MEAN

#if defined(__linux__) && !defined(VMX86_TOOLS)
#define _GNU_SOURCE
#endif

#include "vm_ctype.h"

#if defined(_WIN32)
# include <winsock2.h> // also includes windows.h
# include <io.h>
# include <process.h>
# include "coreDump.h"
#endif

#if !defined(_WIN32)
#  include <unistd.h>
#  include <pwd.h>
#  include <dlfcn.h>
#endif

#if defined __APPLE__
#  include <execinfo.h>
#endif

#if defined(__linux__) && !defined(VMX86_TOOLS) && !defined(__ANDROID__)
#  include <link.h>
#endif

#include "vmware.h"
#include "util.h"
#include "str.h"

#ifdef VM_X86_64
#   if defined(__GNUC__) && (!defined(USING_AUTOCONF) || defined(HAVE_UNWIND_H))
#      define UTIL_BACKTRACE_USE_UNWIND
#   endif
#endif

#define MAX_STACK_DEPTH_APPLE 128

#ifdef UTIL_BACKTRACE_USE_UNWIND
#include <unwind.h>

#define MAX_SKIPPED_FRAMES 10

struct UtilBacktraceFromPointerData {
   uintptr_t        basePtr;
   Util_OutputFunc  outFunc;
   void            *outFuncData;
   unsigned int     frameNr;
   unsigned int     skippedFrames;
};

struct UtilBacktraceToBufferData {
   uintptr_t        basePtr;
   uintptr_t       *buffer;
   size_t           len;
};
#endif /* UTIL_BACKTRACE_USE_UNWIND */


/*
 *-----------------------------------------------------------------------------
 *
 * UtilLogWrapper --
 *
 *      Adapts the Log function to meet the interface required by backtracing
 *      functions by adding an ignored void* argument.
 *
 *      NOTE: This function needs to be static on linux (and any other
 *      platform appLoader might be ported to).  See bug 403780.
 *
 * Results:
 *      Same effect as Log(fmt, ...)
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
UtilLogWrapper(void *ignored,    // IN:
               const char *fmt,  // IN:
               ...)              // IN:
{
   uint32 len;
   va_list ap;
   char thisLine[UTIL_BACKTRACE_LINE_LEN];

   va_start(ap, fmt);
   len = Str_Vsnprintf(thisLine, UTIL_BACKTRACE_LINE_LEN - 2, fmt, ap);
   va_end(ap);

   if (len >= UTIL_BACKTRACE_LINE_LEN - 2) {
      len = UTIL_BACKTRACE_LINE_LEN - 3;
   }

   if (thisLine[len - 1] != '\n') {
      thisLine[len] = '\n';
      thisLine[len + 1] = '\0';
   }

   Log("%s", thisLine);
}


#ifdef UTIL_BACKTRACE_USE_UNWIND
/*
 *-----------------------------------------------------------------------------
 *
 * UtilBacktraceFromPointerCallback --
 *
 *      Callback from _Unwind_Backtrace to print one backtrace entry
 *      to the backtrace output.
 *
 * Results:
 *      _URC_NO_REASON : Please continue with backtrace.
 *      _URC_END_OF_STACK : Force backtrace to quit.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static _Unwind_Reason_Code
UtilBacktraceFromPointerCallback(struct _Unwind_Context *ctx, // IN: Unwind context
                                 void *cbData)                // IN/OUT: Our status
{
   struct UtilBacktraceFromPointerData *data = cbData;
   uintptr_t cfa = _Unwind_GetCFA(ctx);

   /*
    * Stack grows down.  So if we are below basePtr, do nothing...
    */

   if (cfa >= data->basePtr && data->frameNr < 500) {
#ifndef VM_X86_64
#   error You should not build this on 32bit - there is no eh_frame there.
#endif
      /* bump basePtr for glibc unwind bug, see [302237] */
      data->basePtr = cfa + 8;
      /* Do output without leading '0x' to save some horizontal space... */
      data->outFunc(data->outFuncData,
                    "Backtrace[%u] %016lx rip=%016lx rbx=%016lx rbp=%016lx "
                    "r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
                    data->frameNr, cfa, _Unwind_GetIP(ctx),
                    _Unwind_GetGR(ctx, 3), _Unwind_GetGR(ctx, 6),
                    _Unwind_GetGR(ctx, 12), _Unwind_GetGR(ctx, 13),
                    _Unwind_GetGR(ctx, 14), _Unwind_GetGR(ctx, 15));
      data->frameNr++;
      return _URC_NO_REASON;
   } else if (data->skippedFrames < MAX_SKIPPED_FRAMES && !data->frameNr) {
      /*
       * Skip over the frames before the specified starting point of the
       * backtrace.
       */

      data->skippedFrames++;
      return _URC_NO_REASON;
   }
   return _URC_END_OF_STACK;
}


#if !defined(_WIN32) && !defined(VMX86_TOOLS)
/*
 *-----------------------------------------------------------------------------
 *
 * UtilSymbolBacktraceFromPointerCallback --
 *
 *      Callback from _Unwind_Backtrace to print one backtrace entry
 *      to the backtrace output.  This version includes symbol information,
 *      if available.
 *
 * Results:
 *      _URC_NO_REASON : Please continue with backtrace.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static _Unwind_Reason_Code
UtilSymbolBacktraceFromPointerCallback(struct _Unwind_Context *ctx, // IN: Unwind context
                                       void *cbData)                // IN/OUT: Our status
{
   struct UtilBacktraceFromPointerData *data = cbData;
   uintptr_t cfa = _Unwind_GetCFA(ctx);

   /*
    * Stack grows down.  So if we are below basePtr, do nothing...
    */

   if (cfa >= data->basePtr && data->frameNr < 500) {
#ifndef VM_X86_64
#   error You should not build this on 32bit - there is no eh_frame there.
#endif
      void *enclFuncAddr;
      Dl_info dli;

      /* bump basePtr for glibc unwind bug, see [302237] */
      data->basePtr = cfa + 8;
#ifdef __linux__
      enclFuncAddr = _Unwind_FindEnclosingFunction((void *)_Unwind_GetIP(ctx));
#else
      enclFuncAddr = NULL;
#endif
      if (dladdr(enclFuncAddr, &dli) ||
          dladdr((void *)_Unwind_GetIP(ctx), &dli)) {
         data->outFunc(data->outFuncData,
                      "SymBacktrace[%u] %016lx rip=%016lx in function %s "
                      "in object %s loaded at %016lx\n",
                      data->frameNr, cfa, _Unwind_GetIP(ctx),
                      dli.dli_sname, dli.dli_fname, dli.dli_fbase);
      } else {
         data->outFunc(data->outFuncData,
                      "SymBacktrace[%u] %016lx rip=%016lx \n",
                      data->frameNr, cfa, _Unwind_GetIP(ctx));
      }
      data->frameNr++;
      return _URC_NO_REASON;
   } else if (data->skippedFrames < MAX_SKIPPED_FRAMES && !data->frameNr) {
      /*
       * Skip over the frames before the specified starting point of the
       * backtrace.
       */

      data->skippedFrames++;
      return _URC_NO_REASON;
   }
   return _URC_END_OF_STACK;
}
#endif
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * UtilBacktraceFromPointerWithFunc --
 *
 *      Output a backtrace from the given frame porinter, using
 *      "outputFunc" as the logging function. For each line of the backtrace,
 *      this will call "outputFunc(outputFuncData, fmt, ...)"
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Calls outFunc repeatedly.
 *
 *-----------------------------------------------------------------------------
 */

static void
UtilBacktraceFromPointerWithFunc(uintptr_t *basePtr,       // IN:
                                 Util_OutputFunc outFunc,  // IN:
                                 void *outFuncData)        // IN:
{
#if defined(UTIL_BACKTRACE_USE_UNWIND)
   struct UtilBacktraceFromPointerData data;

   data.basePtr = (uintptr_t)basePtr;
   data.outFunc = outFunc;
   data.outFuncData = outFuncData;
   data.frameNr = 0;
   data.skippedFrames = 0;
   _Unwind_Backtrace(UtilBacktraceFromPointerCallback, &data);

#if !defined(_WIN32) && !defined(VMX86_TOOLS)
   /*
    * We do a separate pass here that includes symbols in order to
    * make sure the base backtrace that does not call dladdr() etc.
    * is safely produced.
    */
   data.basePtr = (uintptr_t)basePtr;
   data.outFunc = outFunc;
   data.outFuncData = outFuncData;
   data.frameNr = 0;
   data.skippedFrames = 0;
   _Unwind_Backtrace(UtilSymbolBacktraceFromPointerCallback, &data);
#endif

#elif !defined(VM_X86_64)
   uintptr_t *x = basePtr;
   int i;
#if !defined(_WIN32) && !defined(VMX86_TOOLS) && !defined(__ANDROID__)
   Dl_info dli;
#endif

   for (i = 0; i < 256; i++) {
      if (x < basePtr ||
	  (uintptr_t) x - (uintptr_t) basePtr > 0x8000) {
         break;
      }
      outFunc(outFuncData, "Backtrace[%d] %#08x eip %#08x \n", i, x[0], x[1]);
      x = (uintptr_t *) x[0];
   }

#if !defined(_WIN32) && !defined(VMX86_TOOLS) && !defined(__ANDROID__)
   /*
    * We do a separate pass here that includes symbols in order to
    * make sure the base backtrace that does not call dladdr() etc.
    * is safely produced.
    */
   x = basePtr;
   for (i = 0; i < 256; i++) {
      if (x < basePtr ||
	  (uintptr_t) x - (uintptr_t) basePtr > 0x8000) {
         break;
      }
      if (dladdr((uintptr_t *)x[1], &dli)) {
         outFunc(outFuncData, "SymBacktrace[%d] %#08x eip %#08x in function %s "
                              "in object %s loaded at %#08x\n",
                               i, x[0], x[1], dli.dli_sname, dli.dli_fname,
                                dli.dli_fbase);
      } else {
         outFunc(outFuncData, "SymBacktrace[%d] %#08x eip %#08x \n", i, x[0],
                 x[1]);
      }
      x = (uintptr_t *) x[0];
   }
#endif
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Util_Backtrace --
 *
 *      log the stack backtrace for a particular bug number
 *
 * Results:
 *
 *      void
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Util_Backtrace(int bugNr) // IN
{
   Util_BacktraceWithFunc(bugNr, UtilLogWrapper, NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Util_BacktraceWithFunc --
 *
 *      Outputs the stack backtrace for the bug "bugNr," using the
 *      log function "outFunc" as described in the comment for
 *      Util_BacktraceFromPointerWithFunc.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Calls outFunc several times.
 *
 *-----------------------------------------------------------------------------
 */


void
Util_BacktraceWithFunc(int bugNr,                // IN:
                       Util_OutputFunc outFunc,  // IN:
                       void *outFuncData)        // IN:
{
#if defined(_WIN32)
   CoreDumpFullBacktraceOptions options = {0};

   options.bugNumber = bugNr;
   CoreDump_LogFullBacktraceToFunc(&options, outFunc, outFuncData);
#elif defined(__APPLE__)
   void *callstack[MAX_STACK_DEPTH_APPLE];
   int frames;
   unsigned i;
   Dl_info dli;

   if (bugNr == 0) {
      outFunc(outFuncData, "Backtrace:\n");
   } else {
      outFunc(outFuncData, "Backtrace for bugNr=%d\n", bugNr);
   }
   frames = backtrace(callstack, ARRAYSIZE(callstack));
   for (i = 0; i < frames; i++) {
      outFunc(outFuncData, "Backtrace[%d] rip=%016lx\n", i, callstack[i]);
   }
   for (i = 0; i < frames; i++) {
      if (dladdr(callstack[i], &dli)) {
         outFunc(outFuncData, "SymBacktrace[%d] rip=%016lx in function %s "
                              "in object %s loaded at %016lx\n",
                 i, callstack[i], dli.dli_sname, dli.dli_fname,
                 dli.dli_fbase);
      } else {
         outFunc(outFuncData, "SymBacktrace[%d] rip=%016lxn", i,
                 callstack[i]);
      }
   }
#else
   if (bugNr == 0) {
      outFunc(outFuncData, "Backtrace:\n");
   } else {
      outFunc(outFuncData, "Backtrace for bugNr=%d\n", bugNr);
   }

   UtilBacktraceFromPointerWithFunc(__builtin_frame_address(0), outFunc,
                                    outFuncData);
#endif
}

 07070100000201000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/lib/vmCheck    07070100000202000081A400000000000000000000000168225505000003DD000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/vmCheck/Makefile.am    ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libVmCheck.la

libVmCheck_la_SOURCES =
libVmCheck_la_SOURCES += vmcheck.c
   07070100000203000081A400000000000000000000000168225505000026B6000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/vmCheck/vmcheck.c  /*********************************************************
 * Copyright (c) 2006-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * vmcheck.c --
 *
 *      Utility functions for discovering our virtualization status.
 */

#include <stdlib.h>
#include <string.h>

#ifdef WINNT_DDK
#   include <ntddk.h>
#endif

#if (!defined(WINNT_DDK) && defined(_WIN32))
// include windows.h, otherwise DWORD type used in hostinfo.h is not defined
#  include "windows.h"
#endif

#include "vmware.h"
#include "vm_version.h"
#include "vm_tools_version.h"

#if !defined(WINNT_DDK)
#  include "hostinfo.h"
#  include "str.h"
#  include "x86cpuid.h"
#endif

/*
 * backdoor.h includes some files which redefine constants in ntddk.h.  Ignore
 * warnings about these redefinitions for WIN32 platform.
 */
#ifdef WINNT_DDK
#pragma warning (push)
// Warning: Conditional expression is constant.
#pragma warning( disable:4127 )
#endif

#include "backdoor.h"

#ifdef WINNT_DDK
#pragma warning (pop)
#endif

#include "backdoor_def.h"
#include "debug.h"

#if !defined(_WIN32)
#   include "vmsignal.h"
#   include "setjmp.h"
#endif

typedef Bool (*SafeCheckFn)(void);


#if !defined(_WIN32)
static sigjmp_buf jmpBuf;
static Bool       jmpIsSet;


/*
 *----------------------------------------------------------------------
 *
 * VmCheckSegvHandler --
 *
 *    Signal handler for segv. Return to the program state saved
 *    by a previous call to sigsetjmp, or Panic if sigsetjmp hasn't
 *    been called yet. This function never returns;
 *
 * Return Value:
 *    None.
 *
 * Side effects:
 *    See the manpage for sigsetjmp for details.
 *
 *----------------------------------------------------------------------
 */

static void
VmCheckSegvHandler(int clientData) // UNUSED
{
   if (jmpIsSet) {
      siglongjmp(jmpBuf, 1);
   } else {
      Panic("Received SEGV, exiting.");
   }
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * VmCheckSafe --
 *
 *      Calls a potentially unsafe function, trapping possible exceptions.
 *
 * Results:
 *
 *      Return value of the passed function, or FALSE in case of exception.
 *
 * Side effects:
 *
 *      Temporarily suppresses signals / SEH exceptions
 *
 *----------------------------------------------------------------------
 */

static Bool
VmCheckSafe(SafeCheckFn checkFn)
{
   Bool result = FALSE;

   /*
    * On a real host this call should cause a GP and we catch
    * that and set result to FALSE.
    */

#if defined(_WIN32)
   __try {
      result = checkFn();
   } __except(EXCEPTION_EXECUTE_HANDLER) {
      /* no op */
   }
#else
   do {
      int signals[] = {
#if defined(__FreeBSD__)
         SIGBUS,
#endif
         SIGILL,
         SIGSEGV,
      };
      struct sigaction olds[ARRAYSIZE(signals)];

      if (Signal_SetGroupHandler(signals, olds, ARRAYSIZE(signals),
                                 VmCheckSegvHandler) == 0) {
         Warning("%s: Failed to set signal handlers.\n", __FUNCTION__);
         break;
      }

      if (sigsetjmp(jmpBuf, TRUE) == 0) {
         jmpIsSet = TRUE;
         result = checkFn();
      } else {
         jmpIsSet = FALSE;
      }

      if (Signal_ResetGroupHandler(signals, olds, ARRAYSIZE(signals)) == 0) {
         Warning("%s: Failed to reset signal handlers.\n", __FUNCTION__);
      }
   } while (0);
#endif

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * VmCheck_GetVersion --
 *
 *    Retrieve the version of VMware that's running on the
 *    other side of the backdoor.
 *
 * Return value:
 *    TRUE on success
 *       *version contains the VMX version
 *       *type contains the VMX type
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

Bool
VmCheck_GetVersion(uint32 *version, // OUT
                   uint32 *type)    // OUT
{
   Backdoor_proto bp;

   ASSERT(version);
   ASSERT(type);

   /* Make sure EBX does not contain BDOOR_MAGIC */
   bp.in.size = (size_t)~BDOOR_MAGIC;
   /* Make sure ECX does not contain any known VMX type */
   bp.in.cx.halfs.high = 0xFFFF;

   bp.in.cx.halfs.low = BDOOR_CMD_GETVERSION;
   Backdoor(&bp);
   if (bp.out.ax.word == 0xFFFFFFFF) {
      /*
       * No backdoor device there. This code is not executing in a VMware
       * virtual machine. --hpreg
       */
      return FALSE;
   }

   if (bp.out.bx.word != BDOOR_MAGIC) {
      return FALSE;
   }

   *version = bp.out.ax.word;

   /*
    * Old VMXs (workstation and express) didn't set their type. In that case,
    * our special pattern will still be there. --hpreg
    */

   /*
    * Need to expand this out since the toolchain's gcc doesn't like mixing
    * integral types and enums in the same trinary operator.
    */
   if (bp.in.cx.halfs.high == 0xFFFF)
      *type = VMX_TYPE_UNSET;
   else
      *type = bp.out.cx.word;

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * VmCheck_GetHWVersion --
 *
 *    Retrieve the VM's hardware version.
 *
 * Return value:
 *    TRUE on success
 *       *hwversion contains the VM hardware version
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

Bool
VmCheck_GetHWVersion(uint32 *hwversion) // OUT
{
   Backdoor_proto bp;
   ASSERT(hwversion);

   /* Make sure EBX does not contain BDOOR_MAGIC */
   bp.in.size = (size_t)~BDOOR_MAGIC;
   /* send backdoor command to get hw version */
   bp.in.cx.halfs.low = BDOOR_CMD_GETHWVERSION;
   Backdoor(&bp);

   if (bp.out.ax.word == 0xFFFFFFFF) {
      /*
       * No backdoor device there. This code is not executing in a VMware
       * virtual machine.
       */
      return FALSE;
   }

   if (bp.out.bx.word != BDOOR_MAGIC) {
      return FALSE;
   }

   *hwversion = bp.out.ax.word;
   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * VmCheck_IsVirtualWorld --
 *
 *    Verify that we're running in a VM & we're version compatible with our
 *    environment.
 *
 * Return value:
 *    TRUE if we're in a virtual machine or a Linux compilation using Valgrind,
 *    FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

Bool
VmCheck_IsVirtualWorld(void)
{
   uint32 version;
   uint32 dummy;

#if !defined(WINNT_DDK)
#ifdef USE_VALGRIND
   /*
    * Valgrind can't handle the backdoor check.
    */
   return TRUE;
#endif
#if defined VM_X86_ANY
   char *hypervisorSig;
   uint32 i;

   /*
    * Check for other environments like Xen and VirtualPC only if we haven't
    * already detected that we are on a VMware hypervisor. See PR 1035346.
    */
   hypervisorSig = Hostinfo_HypervisorCPUIDSig();
   if (hypervisorSig == NULL ||
         Str_Strcmp(hypervisorSig, CPUID_VMWARE_HYPERVISOR_VENDOR_STRING) != 0) {
      if (hypervisorSig != NULL) {
         static const struct {
            const char *vendorSig;
            const char *hypervisorName;
         } hvVendors[] = {
            { CPUID_KVM_HYPERVISOR_VENDOR_STRING, "Linux KVM" },
            { CPUID_XEN_HYPERVISOR_VENDOR_STRING, "Xen" },
         };

         for (i = 0; i < ARRAYSIZE(hvVendors); i++) {
            if (Str_Strcmp(hypervisorSig, hvVendors[i].vendorSig) == 0) {
               Debug("%s: detected %s.\n", __FUNCTION__,
                     hvVendors[i].hypervisorName);
               free(hypervisorSig);
               return FALSE;
            }
         }
      }

      free(hypervisorSig);

      if (VmCheckSafe(Hostinfo_TouchXen)) {
         Debug("%s: detected Xen.\n", __FUNCTION__);
         return FALSE;
      }

      if (VmCheckSafe(Hostinfo_TouchVirtualPC)) {
         Debug("%s: detected Virtual PC.\n", __FUNCTION__);
         return FALSE;
      }

   } else {
      free(hypervisorSig);
   }
#endif

   if (!VmCheckSafe(Hostinfo_TouchBackDoor)) {
      Debug("%s: backdoor not detected.\n", __FUNCTION__);
      return FALSE;
   }

   /* It should be safe to use the backdoor without a crash handler now. */
   if (!VmCheck_GetVersion(&version, &dummy)) {
      Debug("%s: VmCheck_GetVersion failed.\n", __FUNCTION__);
      return FALSE;
   }
#else
   /*
    * The Win32 vmwvaudio driver uses this function, so keep the old,
    * VMware-only check.
    */
   __try {
      if (!VmCheck_GetVersion(&version, &dummy)) {
         Debug("%s: VmCheck_GetVersion failed.\n", __FUNCTION__);
         return FALSE;
      }
   } __except (GetExceptionCode() == STATUS_PRIVILEGED_INSTRUCTION) {
      return FALSE;
   }
#endif

   if (version != VERSION_MAGIC) {
      Debug("The version of this program is incompatible with your %s.\n"
            "For information on updating your VMware Tools please see the\n"
            "'Upgrading VMware Tools' section of the 'VMware Tools User Guide'"
            "\nat https://docs.vmware.com/en/VMware-Tools/index.html\n"
            "\n", PRODUCT_LINE_NAME);
      return FALSE;
   }

   return TRUE;
}

  07070100000204000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/lib/vmSignal   07070100000205000081A400000000000000000000000168225505000003E1000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/lib/vmSignal/Makefile.am   ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libVmSignal.la

libVmSignal_la_SOURCES =
libVmSignal_la_SOURCES += vmsignal.c
   07070100000206000081A40000000000000000000000016822550500000C0D000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/vmSignal/vmsignal.c    /*********************************************************
 * Copyright (C) 1998-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * vmsignal.c --
 *
 *    Posix signal handling utility functions
 *
 */

#ifndef VMX86_DEVEL

#endif


#include <stdio.h>
#include <errno.h>
#include <string.h>


#include "vmsignal.h"


/*
 * Signal_SetGroupHandler --
 *
 *    Set a signal handler for a group of signals.
 *    We carefully ensure that if the handler is only used to handle the
 *    signals of the group, the handling of all the signals of the group is
 *    serialized, which means that the handler is not re-entrant.
 *
 * Return value:
 *    1 on success
 *    0 on failure (detail is displayed)
 *
 * Side effects:
 *    None
 *
 */

int
Signal_SetGroupHandler(int const *signals,          // IN
                       struct sigaction *olds,      // OUT
                       unsigned int nr,             // IN
                       void (*handler)(int signal)) // IN
{
   unsigned int i;
   struct sigaction new;

   new.sa_handler = handler;
   if (sigemptyset(&new.sa_mask)) {
      fprintf(stderr, "Unable to empty a signal set: %s.\n\n", strerror(errno));

      return 0;
   }
   for (i = 0; i < nr; i++) {
      if (sigaddset(&new.sa_mask, signals[i])) {
         fprintf(stderr, "Unable to add a signal to a signal set: %s.\n\n", strerror(errno));

         return 0;
      }
   }
   new.sa_flags = 0;

   for (i = 0; i < nr; i++) {
      if (sigaction(signals[i], &new, &olds[i])) {
         fprintf(stderr, "Unable to modify the handler of the signal %d: %s.\n\n", signals[i], strerror(errno));

         return 0;
      }
   }

   return 1;
}


/*
 * Signal_ResetGroupHandler --
 *
 *    Reset the handler of each signal of a group of signals
 *
 * Return value:
 *    1 on success
 *    0 on failure (detail is displayed)
 *
 * Side effects:
 *    None
 *
 */

int
Signal_ResetGroupHandler(int const *signals,           // IN
                         struct sigaction const *olds, // IN
                         unsigned int nr)              // IN
{
   unsigned int i;

   for (i = 0; i < nr; i++) {
      if (sigaction(signals[i], &olds[i], NULL)) {
         fprintf(stderr, "Unable to reset the handler of the signal %d: %s.\n\n", signals[i], strerror(errno));

         return 0;
      }
   }

   return 1;
}
   07070100000207000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002D00000000open-vm-tools-12.5.2/open-vm-tools/lib/wiper  07070100000208000081A400000000000000000000000168225505000003FF000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/lib/wiper/Makefile.am  ################################################################################
### Copyright (C) 2007-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libWiper.la

libWiper_la_SOURCES =
libWiper_la_SOURCES += wiperCommon.c
libWiper_la_SOURCES += wiperPosix.c
 07070100000209000081A40000000000000000000000016822550500000D63000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/lib/wiper/wiperCommon.c    /*********************************************************
 * Copyright (C) 2009-2016, 2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * wiperCommon.c --
 *
 *      OS-agnostic parts of the library for wiping a virtual disk.
 *
 */

#include <stdlib.h>
#include <string.h>
#include "dbllnklst.h"
#include "wiper.h"
#include "util.h"

/*
 *-----------------------------------------------------------------------------
 *
 * WiperSinglePartition_Allocate --
 *
 *      Allocates and initialized empty WiperPartition structure.
 *
 * Results:
 *      NULL if there is no memory, otherwise a pointer to newly allocated
 *      WiperPartition structure.
 *
 * Side Effects:
 *      Allocates memory.
 *
 *-----------------------------------------------------------------------------
 */

WiperPartition *
WiperSinglePartition_Allocate(void)
{
   WiperPartition *p = (WiperPartition *) malloc(sizeof *p);

   if (p != NULL) {
      memset(p->mountPoint, 0, sizeof p->mountPoint);
      p->type = PARTITION_UNSUPPORTED;
      p->fsType = NULL;
      p->fsName = NULL;
      p->comment = NULL;
      p->attemptUnmaps = TRUE;
      DblLnkLst_Init(&p->link);
   }

   return p;
}


/*
 *-----------------------------------------------------------------------------
 *
 * WiperSinglePartition_Close --
 *
 *      Destroy the information returned by a previous call to
 *      WiperSinglePartition_Allocate(). The partition should be removed
 *      from all lists prior to calling WiperSinglePartition_Close().
 *
 * Results:
 *      None.
 *
 * Side Effects:
 *      Frees memory occupied by the element.
 *
 *-----------------------------------------------------------------------------
 */

void
WiperSinglePartition_Close(WiperPartition *p)      // IN
{
   if (p) {
      free((char *)p->comment); /* Casting away constness */
      free((char *)p->fsType);  /* Casting away constness */
      free((char *)p->fsName);  /* Casting away constness */
      free(p);
   }
}


/*
 *---------------------------------------------------------------------------
 *
 * WiperPartition_Close --
 *
 *      Destroy the information collected by previous call to
 *      WiperPartition_Open().
 *
 * Results:
 *      None
 *
 * Side Effects:
 *      Frees memory occupied by elements of the list.
 *
 *---------------------------------------------------------------------------
 */

void
WiperPartition_Close(WiperPartition_List *pl)      // IN/OUT
{
   DblLnkLst_Links *curr, *next;

   DblLnkLst_ForEachSafe(curr, next, &pl->link) {
      WiperPartition *part = DblLnkLst_Container(curr, WiperPartition, link);

      DblLnkLst_Unlink1(curr);
      WiperSinglePartition_Close(part);
   }
}

 0707010000020A000081A40000000000000000000000016822550500006E24000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/lib/wiper/wiperPosix.c /*********************************************************
 * Copyright (C) 2004-2019, 2021-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * wiperPosix.c --
 *
 *      Linux/Solaris library for wiping a virtual disk.
 *
 */

#if !defined(__linux__) && !defined(sun) && !defined(__FreeBSD__) && !defined(__APPLE__)
#error This file should not be compiled on this platform.
#endif

#include <stdio.h>
#include <sys/stat.h>
#if defined(__linux__) || defined(sun)
# if defined(__linux__)
#  include <sys/sysmacros.h>
# endif
# include <sys/vfs.h>
#elif defined(__FreeBSD__) || defined(__APPLE__)
# include <sys/param.h>
# include <sys/ucred.h>
# include <sys/mount.h>
# include <fstab.h>
#endif
#if defined(__FreeBSD__)
#include <libgen.h>
#endif
#include <unistd.h>

#include "vmware.h"
#include "wiper.h"
#include "str.h"
#include "strutil.h"
#include "fileIO.h"
#include "vmstdio.h"
#include "mntinfo.h"
#include "posix.h"
#include "util.h"


/* Number of bytes per disk sector */
#define WIPER_SECTOR_SIZE 512

/* Number of disk sectors to write per write system call.

   The bigger it is, the less calls we do and the faster we are.

   This value has been empirically determined to give maximum performance.

--hpreg
*/
#define WIPER_SECTOR_STEP 128

/* Number of device numbers to store for device-mapper */
#define WIPER_MAX_DM_NUMBERS 8

#if defined(sun) || defined(__linux__)
# define PROCFS "proc"
#elif defined(__FreeBSD__) || defined(__APPLE__)
# define PROCFS "procfs"
#endif


/* Types */
typedef enum {
   WIPER_PHASE_CREATE,
   WIPER_PHASE_FILL,
} WiperPhase;

typedef struct File {
   unsigned char name[NATIVE_MAX_PATH];
   FileIODescriptor fd;
   uint64 size;
   struct File *next;
} File;

/* Internal definition of the wiper state */
typedef struct WiperState {
   /* State machine */
   WiperPhase phase;
   /* WiperPartition to wipe */
   const WiperPartition *p;
   /* File we are currently wiping */
   File *f;
   /* Serial number of the next wiper file to create */
   unsigned int nr;
   /*  Buffer to write in each sector of a wiper file */
   unsigned char buf[WIPER_SECTOR_STEP * WIPER_SECTOR_SIZE];
   /* Effective user id */
   uid_t euid;
} WiperState;

#ifdef sun
typedef struct WiperDiskString {
   char *name;
   size_t nameLen;
} WiperDiskString;
#endif

typedef struct PartitionInfo {
   const char          *name;
   WiperPartition_Type  type;
   const char          *comment;
   Bool                 diskBacked;
} PartitionInfo;


/* Variables */

static const char gRemoteFS[] = "Remote filesystem.";

static const PartitionInfo gKnownPartitions[] = {
   { "autofs",    PARTITION_UNSUPPORTED,  "autofs filesystem.",   FALSE       },
   { "devpts",    PARTITION_UNSUPPORTED,  "devpts filesystem.",   FALSE       },
   { "nfs",       PARTITION_UNSUPPORTED,  gRemoteFS,              FALSE       },
   { "smbfs",     PARTITION_UNSUPPORTED,  gRemoteFS,              FALSE       },
   { "swap",      PARTITION_UNSUPPORTED,  "Swap partition.",      FALSE       },
   { "vmhgfs",    PARTITION_UNSUPPORTED,  gRemoteFS,              FALSE       },
   { PROCFS,      PARTITION_UNSUPPORTED,  "proc filesystem.",     FALSE       },
   { "ext2",      PARTITION_EXT2,         NULL,                   TRUE        },
   { "ext3",      PARTITION_EXT3,         NULL,                   TRUE        },
   { "ext4",      PARTITION_EXT4,         NULL,                   TRUE        },
   { "hfs",       PARTITION_HFS,          NULL,                   TRUE        },
   { "msdos",     PARTITION_FAT,          NULL,                   TRUE        },
   { "ntfs",      PARTITION_NTFS,         NULL,                   TRUE        },
   { "pcfs",      PARTITION_PCFS,         NULL,                   TRUE        },
   { "reiserfs",  PARTITION_REISERFS,     NULL,                   TRUE        },
   { "ufs",       PARTITION_UFS,          NULL,                   TRUE        },
   { "vfat",      PARTITION_FAT,          NULL,                   TRUE        },
   { "zfs",       PARTITION_ZFS,          NULL,                   FALSE       },
   { "xfs",       PARTITION_XFS,          NULL,                   TRUE        },
   { "btrfs",     PARTITION_BTRFS,        NULL,                   TRUE        },
};

static Bool initDone = FALSE;


/* Local functions */
static Bool WiperIsDiskDevice(MNTINFO *mnt, struct stat *s);
static void WiperPartitionFilter(WiperPartition *item, MNTINFO *mnt, Bool shrinkableOnly);
static unsigned char *WiperGetSpace(WiperState *state, uint64 *free, uint64 *total);
static void WiperClean(WiperState *state);


#if defined(__linux__)

#define MAX_DISK_MAJORS       256   /* should be enough for now */
#define NUM_PRESEEDED_MAJORS  5     /* must match the below */

static unsigned int knownDiskMajor[MAX_DISK_MAJORS] = {
   /*
    * Pre-seed some major numbers we were comparing against before
    * we started scanning /proc/devices.
    */
   3,    /* First MFM, RLL and IDE hard disk/CD-ROM interface.
            Inside a VM, this is simply the First IDE hard
            disk/CD-ROM interface because we don't support
            others */
   8,    /* SCSI disk devices */
   22,   /* Second IDE hard disk/CD-ROM interface */
   43,   /* Network block device */
   259,  /* Disks in 2.6.27 */
};

static int numDiskMajors;


/*
 *-----------------------------------------------------------------------------
 *
 * WiperCollectDiskMajors --
 *
 *      Collects major numbers of devices that we considering "disks" and
 *      may try to shrink.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Populates knownDiskMajor array and numDiskMajors counter for
 *      subsequent use by linux version of WiperIsDiskDevice().
 *
 *-----------------------------------------------------------------------------
 */

static void
WiperCollectDiskMajors(void)
{
   const char diskDevNames[] = "|ide0|ide1|sd|md|nbd|device-mapper|blkext|";
   const char blockSeparator[] = "Block devices:";
   Bool seenBlockSeparator = FALSE;
   char *buf;
   int major;
   char device[64];
   FILE *f;

   numDiskMajors = NUM_PRESEEDED_MAJORS;

   f = Posix_Fopen("/proc/devices", "r");
   if (!f) {
      return;
   }

   while (StdIO_ReadNextLine(f, &buf, 0, NULL) == StdIO_Success) {

      if (!seenBlockSeparator) {
         if (!memcmp(buf, blockSeparator, sizeof(blockSeparator) - 1)) {
            seenBlockSeparator = TRUE;
         }
      } else if (sscanf(buf, "%d %61s\n", &major, device + 1) == 2) {

         device[0] = '|';
         device[sizeof(device) - 2] = '\0';
         Str_Strcat(device, "|", sizeof(device));

         if (strstr(diskDevNames, device)) {
            knownDiskMajor[numDiskMajors++] = major;
         }
      }

      free(buf);

      if (numDiskMajors >= MAX_DISK_MAJORS) {
         break;
      }
   }

   fclose(f);
}

#else

static void
WiperCollectDiskMajors(void)
{
}

#endif


/*
 *-----------------------------------------------------------------------------
 *
 * WiperIsDiskDevice --
 *
 *      Determines whether a device is a disk device.
 *
 * Results:
 *      TRUE if disk device, otherwise FALSE.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(sun) /* SunOS { */
static Bool
WiperIsDiskDevice(MNTINFO *mnt,         // IN: file system being considered
                  struct stat *s)       // IN: stat(2) info of fs source
{
   char resolvedPath[PATH_MAX];

   ASSERT(mnt);
   ASSERT(s);

   /*
    * resolvepath() will provide the actual path in /devices of this mount
    * point's device.  The final path component is the node name and will start
    * with "cmdk@" for IDE disks and "sd@" for SCSI disks.
    */
#  define SOL_DEVICE_ROOT  "/devices/"
#  define SOL_SCSI_STR     "sd@"
#  define SOL_IDE_STR      "cmdk@"
   if (resolvepath(MNTINFO_NAME(mnt), resolvedPath, sizeof resolvedPath) != -1) {
      if (strncmp(resolvedPath, SOL_DEVICE_ROOT, sizeof SOL_DEVICE_ROOT - 1) == 0) {
         uint32 i;
         char *name;
         WiperDiskString diskDevices[] = {
            { SOL_SCSI_STR, sizeof SOL_SCSI_STR - 1 },
            { SOL_IDE_STR,  sizeof SOL_IDE_STR - 1  },
         };

         name = basename(resolvedPath);

         for (i = 0; i < ARRAYSIZE(diskDevices); i++) {
            if (strncmp(name, diskDevices[i].name, diskDevices[i].nameLen) == 0) {
               return TRUE;
            }
         }
      }
   }

   return FALSE;
}

#elif defined(__linux__) /* } linux { */

static Bool
WiperIsDiskDevice(MNTINFO *mnt,         // IN: file system being considered
                  struct stat *s)       // IN: stat(2) info of fs source
{
   int majorN = major(s->st_rdev);
   int i;

   for (i = 0; i < numDiskMajors; i++) {
      if (majorN == knownDiskMajor[i]) {
         return TRUE;
      }
   }

   return FALSE;
}

#elif defined(__FreeBSD__) /* } FreeBSD { */

static Bool
WiperIsDiskDevice(MNTINFO *mnt,         // IN: file system being considered
                  struct stat *s)       // IN: stat(2) info of fs source
{
   Bool retval = FALSE;

   /*
    * The following code doesn't really apply to Apple's Mac OS X.  (E.g.,
    * OS X uses a different device and naming scheme.)  However, this
    * function, as a whole, does not even apply to OS X, so this caveat is
    * only minor.
    */
   /*
    * Begin by testing whether file system source is really a character
    * device node.  (FreeBSD dropped support for block devices long ago.)
    * Next, simply discriminate by device node name:
    *   /dev/ad* = ATA disk, /dev/da* = SCSI disk
    */
#define MASK_ATA_DISK    "ad"
#define MASK_SCSI_DISK   "da"
   if (S_ISCHR(s->st_mode)) {
      char *name = basename(MNTINFO_NAME(mnt));
      if ((strncmp(name, MASK_ATA_DISK, sizeof MASK_ATA_DISK - 1) == 0) ||
          (strncmp(name, MASK_SCSI_DISK, sizeof MASK_SCSI_DISK - 1) == 0)) {
         retval = TRUE;
      }
   }
#undef MASK_ATA_DISK
#undef MASK_SCSI_DISK

   return retval;
}

#elif defined(__APPLE__) /* } { */

static Bool
WiperIsDiskDevice(MNTINFO *mnt,     // IN
                  struct stat *s)   // IN
{
   /*
    * Differently from FreeBSD, Mac OS still lists disks as block devices,
    * it seems. Disks devices also seem to start with "/dev/disk".
    */
   return S_ISBLK(s->st_mode) &&
          StrUtil_StartsWith(MNTINFO_NAME(mnt), "/dev/disk");
}

#endif /* } */

/*
 *-----------------------------------------------------------------------------
 *
 * WiperPartitionFilter --
 *
 *      Determine whether or not we know how to wipe a partition.
 *
 *      When the parameter 'shrinkableOnly' is TRUE, disk will be checked
 *      if it is really shrinkable. Otherwise only the filesystem
 *      will be checked for support.
 *
 * Results:
 *      None
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
WiperPartitionFilter(WiperPartition *item,         // IN/OUT
                     MNTINFO *mnt,                 // IN
                     Bool shrinkableOnly)          // IN
{
   struct stat s;
   const char *comment = NULL;
   const char *fsname = MNTINFO_FSTYPE(mnt);
   const PartitionInfo *info;
   size_t i;

   item->type = PARTITION_UNSUPPORTED;
   item->fsType = Util_SafeStrdup(MNTINFO_FSTYPE(mnt));
   item->fsName = Util_SafeStrdup(MNTINFO_NAME(mnt));

   for (i = 0; i < ARRAYSIZE(gKnownPartitions); i++) {
      info = &gKnownPartitions[i];
      if (strcmp(info->name, fsname) == 0) {
         item->type = info->type;
         comment = info->comment;
         break;
      }
   }

   if (i == ARRAYSIZE(gKnownPartitions)) {
      comment = "Unknown filesystem. Contact VMware.";
   } else if (item->type != PARTITION_UNSUPPORTED &&
              shrinkableOnly) {
      /*
       * If the partition is supported by the wiper library, do some other
       * checks before declaring it shrinkable.
       */

      if (info->diskBacked) {
         if (Posix_Stat(MNTINFO_NAME(mnt), &s) < 0) {
            comment = "Unknown device.";
#if defined(sun) || defined(__linux__)
         } else if (!S_ISBLK(s.st_mode)) {
            comment = "Not a block device.";
#endif
         } else if (!WiperIsDiskDevice(mnt, &s)) {
            comment = "Not a disk device.";
         } else if (MNTINFO_MNT_IS_RO(mnt)) {
            comment = "Not writable.";
         }
      } else if (Posix_Access(MNTINFO_MNTPT(mnt), W_OK) != 0) {
         comment = "Mount point not writable.";
      }

      if (comment != NULL) {
         item->type = PARTITION_UNSUPPORTED;
      }
   }

   if (item->type == PARTITION_UNSUPPORTED) {
      ASSERT(comment);
      item->comment = Util_SafeStrdup(comment);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * WiperOpenMountFile --
 *
 *      Open mount file /etc/mtab or /proc/mounts.
 *
 * Results:
 *      MNTHANDLE on success.
 *      NULL on failure.
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static MNTHANDLE
WiperOpenMountFile(void)
{
   MNTHANDLE fp;

   fp = OPEN_MNTFILE("r");
   if (fp == NULL) {
#if defined(__linux__)
#define PROC_MOUNTS     "/proc/mounts"
      if (errno == ENOENT && strcmp(MNTFILE, PROC_MOUNTS) != 0) {
         /*
          * Try /proc/mounts if /etc/mtab is not available.
          */
         fp = Posix_Setmntent(PROC_MOUNTS, "r");
         if (fp == NULL) {
            Log("Could not open %s (%d)\n", PROC_MOUNTS, errno);
         }
         return fp;
      }
#endif
      Log("Could not open %s (%d)\n", MNTFILE, errno);
   }

   return fp;
}


/*
 *-----------------------------------------------------------------------------
 *
 * WiperSinglePartition_Open --
 *
 *      Return information about the input 'mountPoint' partition.
 *
 * Results:
 *      WiperPartition * on success.
 *      NULL on failure.
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

WiperPartition *
WiperSinglePartition_Open(const char *mountPoint,      // IN
                          Bool shrinkableOnly)         // IN
{
   char *mntpt = NULL;
   MNTHANDLE fp;
   int len = 0;
   DECLARE_MNTINFO(mnt);
   WiperPartition *p = NULL;

   ASSERT(initDone);

   fp = WiperOpenMountFile();
   if (fp == NULL) {
      return NULL;
   }

   mntpt = Util_SafeStrdup(mountPoint);
   /*
    * Remove any trailing DIRSEPC from mntpt
    * for correct comparison with /etc/mtab strings.
    */
   {
      char *tmp = &mntpt[strlen(mntpt) - 1];
      if (*tmp == DIRSEPC) {
         *tmp = '\0';
      }
   }

   len = strlen(mntpt);
   while (GETNEXT_MNTINFO(fp, mnt)) {
      if (strncmp(MNTINFO_MNTPT(mnt), mntpt, len) == 0) {

         p = WiperSinglePartition_Allocate();
         if (p == NULL) {
            Log("Not enough memory while opening a partition.\n");
         } else if (Str_Snprintf(p->mountPoint, NATIVE_MAX_PATH,
                                 "%s", MNTINFO_MNTPT(mnt)) == -1) {
            Log("NATIVE_MAX_PATH is too small.\n");
            WiperSinglePartition_Close(p);
            p = NULL;
         } else {
            WiperCollectDiskMajors();
            WiperPartitionFilter(p, mnt, shrinkableOnly);
         }

         goto out;
      }
   }

   Log("Could not find a mount point for %s in %s\n", mntpt, MNTFILE);

 out:
   free(mntpt);
   (void) CLOSE_MNTFILE(fp);
   return p;
}

/*
 *-----------------------------------------------------------------------------
 *
 * WiperSinglePartition_GetSpace --
 *
 *      Get the free space left and the total space (in bytes) on a partition.
 *
 * Results:
 *      "" on success.
 *      The description of the error on failure.
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

unsigned char *
WiperSinglePartition_GetSpace(const WiperPartition *p, // IN
                              uint64 *avail,           // OUT/OPT
                              uint64 *free,            // OUT/OPT
                              uint64 *total)           // OUT
{
#ifdef sun
   struct statvfs statfsbuf;
#else
   struct statfs statfsbuf;
#endif
   uint64 blockSize;

   ASSERT(p);

#ifdef sun
   if (statvfs(p->mountPoint, &statfsbuf) < 0) {
#else
   if (Posix_Statfs(p->mountPoint, &statfsbuf) < 0) {
#endif
      return "Unable to statfs() the mount point";
   }

#ifdef sun
   blockSize = statfsbuf.f_frsize;
#else
   blockSize = statfsbuf.f_bsize;
#endif

   if (avail) {
      /*
       * Free blocks available to non-superuser users.
       * This excludes reserved blocks. Mostly applicable
       * to 'ext' file systems. Newer file systems like
       * 'xfs' report same value for f_bavail and f_bfree.
       */
      *avail = (uint64)statfsbuf.f_bavail * blockSize;
   }

   if (free) {
      if (geteuid()== 0) {
         /*
          * Free blocks in the file system. This includes
          * the reserved blocks too.
          */
         *free = (uint64)statfsbuf.f_bfree * blockSize;
      } else {
         /*
          * Free blocks available to non-superuser users.
          * This excludes reserved blocks.
          */
         *free = (uint64)statfsbuf.f_bavail * blockSize;
      }
   }

   *total = (uint64)statfsbuf.f_blocks * blockSize;

   return "";
}

/*
 *-----------------------------------------------------------------------------
 *
 * WiperPartition_Open --
 *
 *      Return information about wipable and non-wipable partitions
 *
 * Results:
 *      The partition list on success
 *      NULL on failure
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
WiperPartition_Open(WiperPartition_List *pl,
                    Bool shrinkableOnly)
{
   MNTHANDLE fp;
   DECLARE_MNTINFO(mnt);
   Bool rc = TRUE;

   ASSERT(initDone);

   DblLnkLst_Init(&pl->link);

   /* Basically call functions to parse mounts table */
   fp = WiperOpenMountFile();
   if (fp == NULL) {
      return FALSE;
   }

   WiperCollectDiskMajors();

   while (GETNEXT_MNTINFO(fp, mnt)) {
      WiperPartition *part = WiperSinglePartition_Allocate();

      if (part == NULL) {
         Log("Not enough memory while opening a partition.\n");
         rc = FALSE;
         break;
      }

      if (Str_Snprintf(part->mountPoint, NATIVE_MAX_PATH, "%s",
                       MNTINFO_MNTPT(mnt)) == -1) {
         Log("NATIVE_MAX_PATH is too small.\n");
         WiperSinglePartition_Close(part);
         rc = FALSE;
         break;
      }

      WiperPartitionFilter(part, mnt, shrinkableOnly);
      DblLnkLst_LinkLast(&pl->link, &part->link);
   }

   if (!rc)
      WiperPartition_Close(pl);

   (void) CLOSE_MNTFILE(fp);
   return rc;
}


/*
 *---------------------------------------------------------------------------
 *
 * Wiper_IsWipeSupported --
 *
 *      Query if wipe is supported on the specified wiper partition.
 *
 * Results:
 *      FALSE always.
 *
 * Side Effects:
 *      None
 *
 *---------------------------------------------------------------------------
 */

Bool
Wiper_IsWipeSupported(const WiperPartition *part)
{
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Wiper_Start --
 *
 *      Allocate and initialize the wiper state
 *
 * Results:
 *      A Wiper_State on success
 *      NULL on failure
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Wiper_State *
Wiper_Start(const WiperPartition *p,             // IN
            unsigned int maxWiperFileSize)       // IN : unused
{
   WiperState *state;

   state = (WiperState *)malloc(sizeof *state);
   if (state == NULL) {
      return NULL;
   }

   /* Initialize the state */
   state->phase = WIPER_PHASE_CREATE;
   state->p = p;
   state->f = NULL;
   state->nr = 0;
   memset(state->buf, 0, WIPER_SECTOR_STEP * WIPER_SECTOR_SIZE);
   state->euid = geteuid();

   return (void *)state;
}


/*
 *-----------------------------------------------------------------------------
 *
 * WiperGetSpace --
 *
 *      Get the free space left and the total space (in bytes) on a partition.
 *
 * Results:
 *      "" on success.
 *      The description of the error on failure.
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static unsigned char *
WiperGetSpace(WiperState *state,  // IN
              uint64 *free,       // OUT
              uint64 *total)      // OUT
{
   ASSERT(state);
   return WiperSinglePartition_GetSpace(state->p, NULL, free, total);
}


/*
 *-----------------------------------------------------------------------------
 *
 * WiperClean --
 *
 *      Remove all created files.
 *
 * Results:
 *      None
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
WiperClean(WiperState *state)      // IN/OUT
{
   ASSERT(state);

   while (state->f != NULL) {
      File *next;

      FileIO_Close(&state->f->fd);
      next = state->f->next;
      free(state->f);
      state->f = next;
   }

   free(state);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Wiper_Next --
 *
 *      Do the next piece of work to wipe
 *
 * Note: Try to make sure that the execution of this function does not take
 *       more than 1/5 second, so that the user still has some feeling of
 *       interactivity
 *
 * Results:
 *      "" on success. 'progress' is the updated progress indicator (between 0
 *                   and 100 included. 100 means that the job is done, the
 *                   wiper state is destroyed)
 *      The description of the error on failure
 *
 * Side Effects:
 *      The wiper state is updated
 *
 *-----------------------------------------------------------------------------
 */

unsigned char *
Wiper_Next(Wiper_State **s,         // IN/OUT
           unsigned int *progress)  // OUT
{
   WiperState **state;
   uint64 free;
   uint64 total;
   unsigned char *error;

   ASSERT(s);
   ASSERT(*s);
   state = (WiperState **)s;

   error = WiperGetSpace(*state, &free, &total);
   if (*error != '\0') {
      WiperClean(*state);
      *state = NULL;
      return error;
   }

   /* Disk space is an important system resource. Don't fill the partition
      completely */
   if (free <= (((uint64)5) << 20) /* 5 MB */) {
      /* We are done */
      WiperClean(*state);
      *state = NULL;
      *progress = 100;
      return "";
   }

   /* We are not done */
   switch ((*state)->phase) {
   case WIPER_PHASE_CREATE:
      {
         File *new;

         new = (File *)malloc(sizeof *new);
         if (new == NULL) {
            WiperClean(*state);
            *state = NULL;
            return "Not enough memory";
         }

         /*
          * Create a new file
          */

         /* We name it just under the mount point so that we are sure that
            the file is on the right partition. */
         for (;;) {
            FileIOResult fret;
            FileIO_Invalidate(&new->fd);

            if (Str_Snprintf(new->name, NATIVE_MAX_PATH, "%s/wiper%d",
                             (*state)->p->mountPoint, (*state)->nr++) == -1) {
               Log("NATIVE_MAX_PATH is too small\n");
               ASSERT(0);
            }

            fret = FileIO_Open(&new->fd,
                               new->name,
                               FILEIO_OPEN_ACCESS_WRITE
                               | FILEIO_OPEN_DELETE_ASAP,
                               FILEIO_OPEN_CREATE_SAFE);
            if (FileIO_IsSuccess(fret)) {
               break;
            }

            if (fret != FILEIO_OPEN_ERROR_EXIST) {
               WiperClean(*state);
               *state = NULL;
               return "error.create";
            }
         }
         new->size = 0;

         new->next = (*state)->f;
         (*state)->f = new;
      }
      (*state)->phase = WIPER_PHASE_FILL;
      break;

   case WIPER_PHASE_FILL:
      {
         unsigned int i;

         /* Do several write system calls per call to Wiper_Next() */
         for (i = 0; i < (2 << 20) /* 2 MB */
                         / (WIPER_SECTOR_STEP * WIPER_SECTOR_SIZE); i++) {
            FileIOResult fret;

            if ((*state)->f->size + WIPER_SECTOR_STEP * WIPER_SECTOR_SIZE >=
                (((uint64)2) << 30) /* 2 GB */) {
               /* The file is going to be larger than what most filesystems
                  can support. Create a new file */
               (*state)->phase = WIPER_PHASE_CREATE;
               break;
            }

            fret = FileIO_Write(&(*state)->f->fd, (*state)->buf,
                                WIPER_SECTOR_STEP * WIPER_SECTOR_SIZE, NULL);

            /*
             * We distiguish errors from FilieIO_Write.
             */
            if (!FileIO_IsSuccess(fret)) {
               /* The file is too big even though its size is less than 2GB */
               if (fret == FILEIO_WRITE_ERROR_FBIG) {
                  (*state)->phase = WIPER_PHASE_CREATE;

                  break;
               }

               /*
                * The disk is full (there may be other process is consuming space),
                * or the user runs out of his disk quota.
                */
               if (fret == FILEIO_WRITE_ERROR_NOSPC) {
                  WiperClean(*state);
                  *state = NULL;
                  *progress = 100;
                  return "";
               }

               /* Otherwise, it is a real error */
               WiperClean(*state);
               *state = NULL;
               return fret==FILEIO_WRITE_ERROR_DQUOT ? "User's disk quota exceeded" :
                                                       "Unable to write to a wiper file";
            }

            (*state)->f->size += WIPER_SECTOR_STEP * WIPER_SECTOR_SIZE;
         }
      }
      break;

   default:
      Log("state is %u\n", (*state)->phase);
      ASSERT(0);
      break;
   }

   *progress = 99 - 99 * free / total;
   return "";
}


/*
 *-----------------------------------------------------------------------------
 *
 * Wiper_Cancel --
 *
 *      Cancel the wipe operation and destroy the associated wiper state
 *
 * Results:
 *      "" on success
 *      The description of the error on failure
 *
 * Side Effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

unsigned char *
Wiper_Cancel(Wiper_State **s)      // IN/OUT
{
   if (s && *s) {
      WiperClean((WiperState *)*s);
      *s = NULL;
   }
   return "";
}


/*
 *-----------------------------------------------------------------------------
 *
 * Wiper_Init --
 *
 *      On Solaris, this function is defined only to provide a uniform
 *      interface to the library.  On Linux, the /proc/devices file is
 *      read to initialize an array with device numbers that correspond
 *      to the device-mapper devices.  This is to differentiate partitions
 *      that use the device-mapper from other non-disk devices.
 *
 * Results:
 *      Always TRUE.
 *
 * Side Effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
Wiper_Init(WiperInitData *clientData)
{
   return initDone = TRUE;
}

0707010000020B000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/lib/xdg    0707010000020C000081A400000000000000000000000168225505000003F4000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/lib/xdg/Makefile.am    ################################################################################
### Copyright (C) 2010-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libXdg.la

libXdg_la_CPPFLAGS = @GLIB2_CPPFLAGS@

libXdg_la_SOURCES =
libXdg_la_SOURCES += xdg.c
0707010000020D000081A40000000000000000000000016822550500000C98000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/lib/xdg/xdg.c  /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * xdg.c --
 *
 *	vmware-xdg-* script wrapper library.
 */

#include <sys/types.h>
#include <sys/wait.h>

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#include <glib.h>

#include "vmware.h"
#include "vmstdio.h"
#include "xdg.h"


/*
 * Local data
 */


/* Name of helper script used by Xdg_DetectDesktopEnv. */
static const char xdgDetectDEExec[] = "vmware-xdg-detect-de";


/*
 * Global functions
 */


/*
 *-----------------------------------------------------------------------------
 *
 * Xdg_DetectDesktopEnv --
 *
 *      Captures output from external vmware-xdg-detect-de script to determine
 *      which desktop environment we're running under.
 *
 * Results:
 *      Returns a pointer to a string specifying the desktop environment on
 *      success or "" on failure.
 *
 *      This function only guarantees that the returned string matches the
 *      pattern ^[A-Za-z0-9]*$.
 *
 * Side effects:
 *      Allocates memory for outbuf on first call for duration of program.
 *      Uses popen(), relying on $PATH, to find and execute xdgDetectDeExec.
 *      Caller must not modify returned string.
 *
 *-----------------------------------------------------------------------------
 */

const char *
Xdg_DetectDesktopEnv(void)
{
   static char *outbuf = NULL;

   if (outbuf == NULL) {
      FILE *cmdPipe = popen(xdgDetectDEExec, "r");

      if (cmdPipe) {
         static const size_t maxSize = sizeof "TEHLONGISTDESKTOPENVEVAR";
         size_t outLen;         // Doesn't include NUL.
         int status;

         if (   StdIO_ReadNextLine(cmdPipe, &outbuf, maxSize, &outLen)
             == StdIO_Success) {
            int i;

            for (i = 0; i < outLen; i++) {
               if (!isalnum(outbuf[i])) {
                  g_debug("%s: received malformed input\n", __func__);
                  free(outbuf);
                  outbuf = NULL;
                  break;
               }
            }
         }

         status = pclose(cmdPipe);
         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
            g_debug("%s: %s did not exit cleanly (%x/%x)\n", __func__, xdgDetectDEExec,
                    status, WEXITSTATUS(status));
            free(outbuf);
            outbuf = NULL;
         }
      }

      if (outbuf == NULL) {
         outbuf = "";
      }
   }

   return outbuf;
}
0707010000020E000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg   0707010000020F000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000210000081A40000000000000000000000016822550500000AE0000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/Makefile.am   ################################################################################
### Copyright (c) 2014-2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

lib_LTLIBRARIES = libDeployPkg.la

AM_CFLAGS =
AM_CFLAGS += -I$(top_srcdir)/include
AM_CFLAGS += $(MSPACK_CPPFLAGS)
AM_CFLAGS += -DVMTOOLS_USE_GLIB
AM_CFLAGS += @GLIB2_CPPFLAGS@

libDeployPkg_la_LIBADD =
libDeployPkg_la_LIBADD += @MSPACK_LIBS@
libDeployPkg_la_LIBADD += @VMTOOLS_LIBS@

libDeployPkg_la_SOURCES =
libDeployPkg_la_SOURCES += linuxDeployment.c
libDeployPkg_la_SOURCES += mspackConfig.h
libDeployPkg_la_SOURCES += mspackWrapper.c
libDeployPkg_la_SOURCES += mspackWrapper.h
libDeployPkg_la_SOURCES += processPosix.c
libDeployPkg_la_SOURCES += linuxDeploymentUtilities.c
libDeployPkg_la_SOURCES += linuxDeploymentUtilities.h

# We require GCC, so we're fine passing compiler-specific flags.
# Needed for OS's that don't link shared libraries against libc by default, e.g. FreeBSD
libDeployPkg_la_LIBADD += -lc

libDeployPkg_includedir = $(includedir)/libDeployPkg

libDeployPkg_include_HEADERS =
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/includeCheck.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/imgcust-common/imgcust-api.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/imgcust-common/log.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/imgcust-common/process.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/vm_basic_types.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/vmware/tools/guestrpc.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/deployPkg/deployPkgFormat.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/deployPkg/linuxDeployment.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/vmware/guestrpc/deploypkg.h
libDeployPkg_include_HEADERS += $(top_srcdir)/lib/include/vmware/guestrpc/guestcust-events.h

EXTRA_DIST = libDeployPkg.pc.in

pkgconfigdir   = $(libdir)/pkgconfig
pkgconfig_DATA = libDeployPkg.pc

$(pkgconfig_DATA): $(top_builddir)/config.status
07070100000211000081A40000000000000000000000016822550500000162000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/libDeployPkg.pc.in    # VMware libDeployPkg pkgconfig data.

prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@

Name:          VMware DeployPkg library
Description:   Library for unpacking and executing VMware Guest Customization package.
Version:       @VERSION@
Libs:          -L${libdir} -lDeployPkg
Cflags:        -I${includedir}/deployPkg
  07070100000212000081A40000000000000000000000016822550500011050000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/linuxDeployment.c /*********************************************************
 * Copyright (c) 2006-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * linuxDeployment.c --
 *
 *      Implementation of libDeployPkg.so.
 */

#include <ctype.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <regex.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "str.h"

#include "mspackWrapper.h"
#include "deployPkg/deployPkgFormat.h"
#include "deployPkg/linuxDeployment.h"
#include "imgcust-common/process.h"
#include "linuxDeploymentUtilities.h"
#include "mspackWrapper.h"
#include "vmware/guestrpc/deploypkg.h"
#include "vmware/guestrpc/guestcust-events.h"
#include "vmware/tools/guestrpc.h"
#include <file.h>
#include <strutil.h>
#include <util.h>

#include "embed_version.h"
#include "vm_version.h"

#if defined(OPEN_VM_TOOLS) || defined(USERWORLD)
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING "-vmtools");
#else
VM_EMBED_VERSION(SYSIMAGE_VERSION_EXT_STR);
#endif

/*
 * These are covered by #ifndef to give the ability to change these
 * variables from makefile (mostly planned for the forthcoming Solaris
 * customization)
 */

#define CLEANUPCMD  "/bin/rm -r -f "

#ifndef TMP_PATH_VAR
#define TMP_PATH_VAR "/tmp/.vmware/linux/deploy"
#endif

#ifndef IMC_TMP_PATH_VAR
#define IMC_TMP_PATH_VAR "@@IMC_TMP_PATH_VAR@@"
#endif

// Use it to create random name folder for extracting the package
#ifndef IMC_DIR_PATH_PATTERN
#define IMC_DIR_PATH_PATTERN "/.vmware-imgcust-dXXXXXX"
#endif

#ifndef STATE_FILE_PATH_BASENAME
#define STATE_FILE_PATH_BASENAME "/var/log/.vmware-deploy"
#endif

#ifndef CABCOMMANDLOG
#define CABCOMMANDLOG "/var/log/vmware-imc/toolsDeployPkg.log"
#endif

#define MAXSTRING 2048

// the minimum version that cloud-init support raw data
#define CLOUDINIT_SUPPORT_RAW_DATA_MAJOR_VERSION 21
#define CLOUDINIT_SUPPORT_RAW_DATA_MINOR_VERSION 1

// the maximum length of cloud-init version stdout
#define MAX_LENGTH_CLOUDINIT_VERSION 256
// the maximum length of cloud-init status stdout
#define MAX_LENGTH_CLOUDINIT_STATUS 256
// the default timeout of waiting for cloud-init execution done
#define DEFAULT_TIMEOUT_WAIT_FOR_CLOUDINIT_DONE 30

/*
 * Constant definitions
 */

static const char  SPACECHAR       = ' ';
static const char  TABCHAR         = '\t';
static const char  QUOTECHAR       = '"';
static const char  BACKSLASH       = '\\';
static const char* INPROGRESS      = "INPROGRESS";
static const char* DONE            = "Done";
static const char* ERRORED         = "ERRORED";
#ifndef IMGCUST_UNITTEST
static const char* RUNDIR          = "/run";
static const char* VARRUNDIR       = "/var/run";
static const char* VARRUNIMCDIR    = "/var/run/vmware-imc";
#endif
static const char* TMPDIR          = "/tmp";

// Possible return codes from perl script
static const int CUST_SUCCESS       = 0;
static const int CUST_GENERIC_ERROR = 255;
static const int CUST_NETWORK_ERROR = 254;
static const int CUST_NIC_ERROR     = 253;
static const int CUST_DNS_ERROR     = 252;
static const int CUST_SCRIPT_DISABLED_ERROR = 6;

// the error code to use cloudinit workflow
typedef enum USE_CLOUDINIT_ERROR_CODE {
   USE_CLOUDINIT_OK = 0,
   USE_CLOUDINIT_INTERNAL_ERROR,
   USE_CLOUDINIT_WRONG_VERSION,
   USE_CLOUDINIT_NOT_INSTALLED,
   USE_CLOUDINIT_DISABLED,
   USE_CLOUDINIT_NO_CUST_CFG,
   USE_CLOUDINIT_IGNORE,
} USE_CLOUDINIT_ERROR_CODE;

// the user-visible cloud-init application status code
typedef enum CLOUDINIT_STATUS_CODE {
   CLOUDINIT_STATUS_NOT_RUN = 0,
   CLOUDINIT_STATUS_RUNNING,
   CLOUDINIT_STATUS_DONE,
   CLOUDINIT_STATUS_ERROR,
   CLOUDINIT_STATUS_DISABLED,
   CLOUDINIT_STATUS_UNKNOWN,
} CLOUDINIT_STATUS_CODE;

/*
 * Linked list definition
 *
 *    +---------+---------+
 *    | data    | Next ---+-->
 *    +---------+---------+
 *
 */

struct List {
   char*  data;
   struct List* next;
};

/*
 * Template function definition
 */

// Private functions
static Bool GetPackageInfo(const char* pkgName, char** cmd, uint8* type, uint8* flags);
static Bool ExtractZipPackage(const char* pkg, const char* dest);
static void Init(void);
static struct List* AddToList(struct List* head, const char* token);
static int ListSize(struct List* head);
static DeployPkgStatus Touch(const char*  state);
static DeployPkgStatus UnTouch(const char* state);
static DeployPkgStatus TransitionState(const char* stateFrom, const char* stateTo);
static bool CopyFileToDirectory(const char* srcPath, const char* destPath,
                                const char* fileName);
static DeployPkgStatus Deploy(const char* pkgName);
static char** GetFormattedCommandLine(const char* command);
int ForkExecAndWaitCommand(const char* command,
                           bool failIfStdErr,
                           char* forkOutput,
                           int maxOutputLen);
static void SetDeployError(const char* format, ...);
static const char* GetDeployError(void);
static void NoLogging(int level, const char* fmtstr, ...);
static Bool CheckFileExist(const char* dirPath, const char* fileName);
static Bool CopyFileIfExist(const char* sourcePath,
                            const char* targetPath,
                            const char* fileName);
static void GetCloudinitVersion(const char* versionOutput,
                                int* major,
                                int* minor);
static Bool IsTelinitASoftlinkToSystemctl(void);

/*
 * Globals
 */

static char* gDeployError = NULL;
LogFunction sLog = NoLogging;
static uint16 gProcessTimeout = DEPLOYPKG_PROCESSTIMEOUT_DEFAULT;
static bool gProcessTimeoutSetByLauncher = false;
static uint16 gWaitForCloudinitDoneTimeout =
   DEFAULT_TIMEOUT_WAIT_FOR_CLOUDINIT_DONE;

// .....................................................................................

/*
 *------------------------------------------------------------------------------
 *
 * DeployPkg_SetTimeout --
 *
 *      Give the deploy package an application specific timeout value.
 *      Package deployment engines such as tools-deployPkg-plugin or standalone program
 * linuxDeployPkg can call this API to set gProcessTimeout.
 *      This API should be called before DeployPkg_DeployPackageFromFile or
 * DeployPkg_DeployPackageFromFileEx.
 *      If the package header includes valid 'timeout' value, then that value will be
 * ignored because 'timeout' value has been provided by the package deployment engines.
 *      If no valid 'timeout' value from both package header and deployment engine, then
 * default value 100s will be used.
 *
 * @param logger [in]
 *      timeout value to be used for process execution period control
 *
 *------------------------------------------------------------------------------
 */

void
DeployPkg_SetProcessTimeout(uint16 timeout)
{
   if (timeout > 0) {
      gProcessTimeout = timeout;
      sLog(log_debug, "Process timeout value from deployment launcher: %u.",
           gProcessTimeout);
      gProcessTimeoutSetByLauncher = true;
   }
}

/*
 *------------------------------------------------------------------------------
 *
 * DeployPkg_SetWaitForCloudinitDoneTimeout
 *
 *     Set the timeout value of customization process waits for cloud-init
 *     execution done before trigger reboot and after connect network adapters.
 *     Tools deployPkg plugin reads this timeout value from tools.conf and
 *     checks if the timeout value is valid, then calls this API to set the
 *     valid timeout value to gWaitForCloudinitDoneTimeout.
 *
 * @param timeout [in]
 *     timeout value to be used for waiting for cloud-init execution done
 *
 *------------------------------------------------------------------------------
 */

void
DeployPkg_SetWaitForCloudinitDoneTimeout(uint16 timeout)
{
   gWaitForCloudinitDoneTimeout = timeout;
   sLog(log_debug, "Wait for cloud-init execution done timeout value: %d.",
        gWaitForCloudinitDoneTimeout);
}

// .....................................................................................

/**
 *
 * Panic
 *
 *    Used by VMware libraries pass PANIC signals to the parent application.
 *
 * @param   fmstr    [in]  Format to print the arguments in.
 * @param   args     [in]  Argument list.
 *
 **/
NORETURN void
Panic(const char *fmtstr, ...)
{
   char *tmp = malloc(MAXSTRING);

   if (tmp != NULL) {
      va_list args;
      va_start(args, fmtstr);
      Str_Vsnprintf(tmp, MAXSTRING, fmtstr, args);
      va_end(args);

      sLog(log_error, "Panic callback invoked: '%s'.", tmp);

      free(tmp);
   } else {
      sLog(log_error, "Error allocating memory to log panic messages");
   }

   exit(1);
}

// .....................................................................................

/**
 *
 * Debug
 *
 *    Mechanism used by VMware libraries to pass debug messages to the parent.
 *
 * @param   fmstr    [in]  Format to print the arguments in.
 * @param   args     [in]  Argument list.
 *
 **/
void
Debug(const char *fmtstr, ...)
{
#ifdef VMX86_DEBUG
   char *tmp = malloc(MAXSTRING);

   if (tmp != NULL) {
      va_list args;
      va_start(args, fmtstr);
      Str_Vsnprintf(tmp, MAXSTRING, fmtstr, args);
      va_end(args);

      sLog(log_debug, "Debug callback invoked: '%s'.", tmp);

      free(tmp);
   } else {
      sLog(log_error, "Error allocating memory to log debug messages");
   }
#endif
}

// .....................................................................................

/**
 *-----------------------------------------------------------------------------
 *
 * SetCustomizationStatusInVmxEx
 *
 *    Set the guest customization status in the VMX server, returning responses.
 *
 * @param   customizzationState  [in]  Customization state of the
 *                                     deployment/customization process
 * @param   errCode              [in]  Error code (can be success too)
 * @param   errMsg               [in]  Error message
 * @param   vmxResponse          [out] VMX response. Note that this is _not_ a
                                       success indicator but contains additional
                                       info target functionality may provide
 * @param   vmxResponseLength    [out] VMX response length
 * @param   responseBufferSize   [in]  VMX response buffer size
 *
 *-----------------------------------------------------------------------------
 **/

static Bool
SetCustomizationStatusInVmxEx(int customizationState,
                              int errCode,
                              const char* errMsg,
                              char   *vmxResponse,
                              size_t *vmxResponseLength,
                              size_t  responseBufferSize)
{
   char   *msg = NULL;
   char   *response = NULL;
   size_t  responseLength = 0;
   Bool success;

   if (errMsg != NULL) {
      int msg_size = strlen(CABCOMMANDLOG) + 1 + strlen(errMsg) + 1;
      msg = malloc(msg_size);
      if (msg == NULL) {
         sLog(log_error,
              "Error allocating memory to copy '%s' and '%s'.",
              CABCOMMANDLOG,
              errMsg);
         return false;
      }
      strcpy (msg, CABCOMMANDLOG);
      Str_Strcat(msg, "@", msg_size);
      Str_Strcat(msg, errMsg, msg_size);
   } else {
      msg = malloc(strlen(CABCOMMANDLOG) + 1);
      if (msg == NULL) {
         sLog(log_error,
              "Error allocating memory to copy '%s'.",
              CABCOMMANDLOG);
         return false;
      }
      strcpy (msg, CABCOMMANDLOG);
   }

   success = RpcChannel_SendOne(vmxResponse != NULL ? &response : NULL,
                                vmxResponse != NULL ? &responseLength : NULL,
                                "deployPkg.update.state %d %d %s",
                                customizationState,
                                errCode,
                                msg);
   free(msg);

   if (vmxResponse != NULL) {
      if (response != NULL) {
         sLog(log_debug, "Got VMX response '%s'.", response);
         if (responseLength > responseBufferSize - 1) {
            sLog(log_warning,
                 "The VMX response is too long "
                 "(only %"FMTSZ"u chars are allowed).",
                 responseBufferSize - 1);
            responseLength = responseBufferSize - 1;
         }
         memcpy(vmxResponse, response, responseLength);
         free(response);
      }
      else {
         sLog(log_debug, "Got no VMX response.");
         responseLength = 0;
      }
      vmxResponse[responseLength] = 0;
      if (vmxResponseLength != NULL) {
         *vmxResponseLength = responseLength;
      }
   }

   if (!success) {
      sLog(log_error, "Unable to set customization status in vmx.");
   }

   return success;
}


/**
 *-----------------------------------------------------------------------------
 *
 * SetCustomizationStatusInVmx
 *
 *    Set the VMX customization status in the VMX server.
 *
 * @param   customizzationState  [in]  Customization state of the
 *                                     deployment/customization process
 * @param   errCode              [in]  Error code (can be success too)
 * @param   errMsg               [in]  Error message
 *
 *-----------------------------------------------------------------------------
 **/
static void
SetCustomizationStatusInVmx(int customizationState,
                            int errCode,
                            const char* errMsg)
{
   SetCustomizationStatusInVmxEx(customizationState, errCode, errMsg, NULL, NULL, 0);
}

// .....................................................................................

/**
 *
 * DeployPkg_SetLogger
 *
 *    Set the logging function.
 *
 * @param   [in]  log   The logging function to be used
 * @returns None
 *
 **/
void
DeployPkg_SetLogger(LogFunction log)
{
   sLog = log;
}

// .....................................................................................

/**
 *
 * NoLogging
 *
 *    NOP log function.
 *
 * @param   [in]  level    Log level
 * @param   [in]  fmtstr   Format string to format the variables.
 *
 **/
static void
NoLogging(int level, const char* fmtstr, ...)
{
   // No logging
}

// ......................................................................................

/**
 *
 * SetDeployError
 *
 *    Set the deployment error in a verbose style. Can be queried using
 *    GetDeployError.
 *
 * @param   format   [in]  Format string to follow.
 * @param   ...      [in]  List of params to be formatted.
 *
 **/
static void
SetDeployError(const char* format, ...)
{
   /*
    * No Error check is employed since this is only an advisory service.
    */
   char* tmp = malloc(MAXSTRING);

   if (tmp != NULL) {
      va_list args;
      va_start(args, format);
      Str_Vsnprintf(tmp, MAXSTRING, format, args);
      va_end(args);
   }

   if (gDeployError != NULL) {
      free(gDeployError);
      gDeployError = NULL;
   }

   sLog(log_debug, "Setting deploy error: '%s'.", tmp);
   gDeployError = tmp;
}

// ......................................................................................

/**
 *
 * GetDeployError
 *
 *    Get the deploy error set using the SetDeployError method.
 *
 * @param   None
 * @returns Pointer to the deploy error string.
 *
 **/
static const char*
GetDeployError(void)
{
   return gDeployError;
}

// ......................................................................................

/**
 *
 * AddToList
 *
 *    Add an element to the specified linked list.
 *
 * List organization:
 * ------------------
 *  <<head>>
 *  +----+---+   +----+---+
 *  | D1 | x-+-> | D2 | x-+-> NULL
 *  +----+---+   +----+---+
 *               <<tail>>
 *
 * @param   head  [in]  Head of the linked list.
 * @param   token [in]  Token to be added.
 * @returns The head to the list.
 *
 **/
struct List*
AddToList(struct List* head, const char* token)
{
   struct List* l;
   struct List* tail;
   char* data;

#ifdef VMX86_DEBUG
   sLog(log_debug, "Adding to list '%s'.", token);
#endif
   data = malloc(strlen(token) + 1);
   if (data == NULL) {
      SetDeployError("Error allocating memory. (%s)", strerror(errno));
      return NULL;
   }

   strcpy(data, token);

   l = malloc(sizeof(struct List));
   if (l == NULL) {
      SetDeployError("Error allocating memory. (%s)", strerror(errno));
      // clear allocated resource
      free(data);
      return NULL;
   }

   l->data = data;
   l->next = NULL;

   tail = head;
   while (tail && tail->next) {
      tail = tail->next;
   }

   if (tail != NULL) {
      tail->next = l;
   }

   return head ? head : l;
}

// ......................................................................................

/**
 *
 * Listsize
 *
 *    Return the size of the specified linked list.
 *
 * @param      head  [in]  Head of the linked list.
 * @returns  The total size of the linked list.
 *
 **/
static int
ListSize(struct List* head)
{
   int sz = 0;
   struct List* l;

   for(l = head; l; ++sz, l = l->next);
#ifdef VMX86_DEBUG
   sLog(log_debug, "Query: List size is %i.", sz);
#endif
   return sz;
}

// ......................................................................................

/**
 *
 * DeleteList
 *
 *    Delete the complete list.
 *
 * @param   head  [in]  Head of the list to be deleted.
 * @returns None
 *
 **/
static void
DeleteList(struct List* head)
{
   struct List* t = head;
#ifdef VMX86_DEBUG
   sLog(log_debug, "Cleaning the linked list.");
#endif

   while(t) {
      struct List* tmp = t;
      t = t->next;

      // delete resource
      free(tmp->data);
      free(tmp);
   }
}

//......................................................................................

/**
 *
 * Initialize the deployment module.
 *
 * @param   None
 * @return  None
 *
 **/
static void
Init(void)
{
   // Clean up if there is any deployment locks/status before
   sLog(log_info, "Cleaning old state files.");
   UnTouch(INPROGRESS);
   UnTouch(DONE);
   UnTouch(ERRORED);

   /*
    * Set the error message as success. This will be replaced with an error
    * message when an error occours. Standard Linux practice.
    */
   SetDeployError("Success.");
}

//......................................................................................

/**
 *
 * Get the command to execute from the cab file.
 *
 * @param   [IN]  packageName   package file name
 * @param   [OUT] command       command line from header
 * @param   [OUT] archiveType   package archive format
 * @param   [OUT] flags         package header flags
 * @return  TRUE is success
 *
 **/
Bool
GetPackageInfo(const char* packageName,
               char** command,
               uint8* archiveType,
               uint8* flags)
{
   unsigned int sz;
   VMwareDeployPkgHdr hdr;
   int fd = open(packageName, O_RDONLY);

   // open errored ?
   if (fd < 0) {
      SetDeployError("Error opening file. (%s)", strerror(errno));
      return FALSE;
   }

   // read the custom header
   sz = read(fd, &hdr, sizeof(VMwareDeployPkgHdr));

   if (sz != sizeof(VMwareDeployPkgHdr)) {
      close(fd);

      SetDeployError("Error reading header. (%s)", strerror(errno));
      return FALSE;
   }

   // close the file
   close(fd);

   // Create space and copy the command
   *command = malloc(VMWAREDEPLOYPKG_CMD_LENGTH);
   if (*command == NULL) {
      SetDeployError("Error allocating memory.");
      return FALSE;
   }

   memcpy(*command, hdr.command, VMWAREDEPLOYPKG_CMD_LENGTH);
   *archiveType = hdr.payloadType;
   *flags = hdr.reserved;

   //TODO hdr->command[VMWAREDEPLOYPKG_CMD_LENGTH - 1] = '\0';

   // Get process timeout value from client
   // If gProcessTimeout has been provided by deployment launcher, then
   // ignore the value from client.
   if (hdr.pkgProcessTimeout > 0) {
      if (!gProcessTimeoutSetByLauncher) {
          sLog(log_info, "Process timeout value %u in header will be used.",
             hdr.pkgProcessTimeout);
          gProcessTimeout = hdr.pkgProcessTimeout;
      } else {
          sLog(log_info, "Process timeout value %u in header is ignored.",
             hdr.pkgProcessTimeout);
      }
   }

   return TRUE;
}

//......................................................................................

/**
 *
 * Create a lock file.
 *
 * @param   [IN]  state   The state of the system
 * @returns DEPLOYPKG_STATUS_SUCCESS on success
 *          DEPLOYPKG_STATUS_ERROR on error
 *
 **/
static DeployPkgStatus
Touch(const char* state)
{
   int fileNameSize = strlen(STATE_FILE_PATH_BASENAME) + 1 /* For '.' */ +
                      strlen(state) + 1;
   char* fileName = malloc(fileNameSize);
   int fd;

   sLog(log_info, "ENTER STATE '%s'.", state);
   if (fileName == NULL) {
      SetDeployError("Error allocating memory.");
      return DEPLOYPKG_STATUS_ERROR;
   }

   Str_Snprintf(fileName, fileNameSize, "%s.%s", STATE_FILE_PATH_BASENAME,
                state);

   fd = open(fileName, O_WRONLY|O_CREAT|O_EXCL, 0644);

   if (fd < 0) {
      SetDeployError("Error creating lock file '%s'.(%s)", fileName,
                     strerror(errno));
      free(fileName);
      return DEPLOYPKG_STATUS_ERROR;
   }

   close(fd);
   free(fileName);

   return DEPLOYPKG_STATUS_SUCCESS;
}

//......................................................................................

/**
 *
 * Delete a lock file.
 *
 * @param   [IN]  state   The state of the system
 * @returns DEPLOYPKG_STATUS_SUCCESS on success
 *          DEPLOYPKG_STATUS_ERROR on error
 *
 **/
static DeployPkgStatus
UnTouch(const char* state)
{
   int fileNameSize = strlen(STATE_FILE_PATH_BASENAME) + 1 /* For '.' */ +
                      strlen(state) + 1;
   char* fileName = malloc(fileNameSize);
   int result;

   sLog(log_info, "EXIT STATE '%s'.", state);
   if (fileName == NULL) {
      SetDeployError("Error allocating memory.");
      return DEPLOYPKG_STATUS_ERROR;
   }

   Str_Snprintf(fileName, fileNameSize, "%s.%s", STATE_FILE_PATH_BASENAME,
                state);

   result = remove(fileName);

   if (result < 0) {
      SetDeployError("Error removing lock '%s'.(%s)", fileName,
                     strerror(errno));
      free(fileName);
      return DEPLOYPKG_STATUS_ERROR;
   }

   free(fileName);
   return DEPLOYPKG_STATUS_SUCCESS;
}

//......................................................................................

/**
 *
 * Depict a transitions from one state to another. the file corresponding to the
 * the old state is deleted and a new file corresponding to the new state is
 * created. This way it is ensured that the tmp directory is left is no status
 * entry at all. The other way to do this is to rename a given. I have opted for
 * deletion and creation to represent the physical transition.
 *
 * @param   [IN] stateFrom  The state from which the transition happens
 * @param   [IN] stateTo    The state to which the transition happens
 * @returns DEPLOYPKG_STATUS_SUCCESS on success
 *          DEPLOYPKG_STATUS_ERROR on error
 *
 **/
static DeployPkgStatus
TransitionState(const char* stateFrom, const char* stateTo)
{
   sLog(log_info, "Transitioning from state '%s' to state '%s'.",
        stateFrom, stateTo);

   // Create a file to indicate state to
   if (stateTo != NULL) {
      if (Touch(stateTo) == DEPLOYPKG_STATUS_ERROR) {
         SetDeployError("Error creating new state '%s'.(%s)", stateTo, GetDeployError());
         return DEPLOYPKG_STATUS_ERROR;
      }
   }

   // Remove the old state file
   if (stateFrom != NULL) {
      if (UnTouch(stateFrom) == DEPLOYPKG_STATUS_ERROR) {
         SetDeployError("Error deleting old state '%s'.(%s)", stateFrom, GetDeployError());
         return DEPLOYPKG_STATUS_ERROR;
      }
   }

   return DEPLOYPKG_STATUS_SUCCESS;
}

/**
 *-----------------------------------------------------------------------------
 *
 * GetNicsToEnable --
 *
 *      Returns ordinal numbers of nics to enable once customization is done.
 *      Ordinal numbers are read from a file in the deployment package and are
 *      separated by ",". Nics are disabled by VC before customization to avoid
 *      ip conflict on network while this vm is being customized.
 *
 *      This method allocates memory and then reutrns the nics to caller. The
 *      caller should call free to get rid of memory allocated.
 *
 * @param  dir [in] Directory where package files were expanded.
 * @return ordinal number of nics to enable separated by ",". If no nic file is
 *         found or no nics need re-enabling NULL is returned.
 *
 *-----------------------------------------------------------------------------
 */

static char *
GetNicsToEnable(const char *dir)
{
   /*
    * The file nics.txt will list ordinal number of all nics to enable separated
    * by a ",". In current architecture we can have max 4 nics. So we just have
    * to read maximum of 7 characters. This code uses 1024 chars to make sure
    * any future needs are accomodated.
    */
   static const unsigned int NICS_SIZE = 1024;
   static const char *nicFile = "/nics.txt";

   FILE *file;

   char *ret = NULL;
   int fileNameSize = strlen(dir) + strlen(nicFile) + 1;
   char *fileName = malloc(fileNameSize);
   if (fileName == NULL) {
      SetDeployError("Error allocating memory to copy '%s'", dir);
      return ret;
   }
   strcpy(fileName, dir);
   Str_Strcat(fileName, nicFile, fileNameSize);

   file = fopen(fileName, "r");
   if (file) {
      ret = malloc(NICS_SIZE);
      if (ret == NULL) {
         SetDeployError("Error allocating memory to read nic file '%s'",
                        fileName);
         fclose(file);
         free(fileName);
         return ret;
      }
      if (fgets(ret, NICS_SIZE, file) == NULL) {
         sLog(log_warning, "fgets() failed or reached EOF.");
      }

      // Check various error condition
      if (ferror(file)) {
         SetDeployError("Error reading nic file '%s'.(%s)", fileName,
                        strerror(errno));
         free(ret);
         ret = NULL;
      }

      if (!feof(file)) {
         SetDeployError("More than expected nics to enable. Nics: '%s'.", ret);
         free(ret);
         ret = NULL;
      }

      fclose(file);
   }

   free(fileName);
   return ret;
}


/**
 *------------------------------------------------------------------------------
 *
 * TryToEnableNics --
 *
 *      Sends a command to connect network interfaces and waits synchronously
 *      for its completion. If NICs are not connected in predefined time the
 *      command is send again several times.
 *
 *      Note that since guest has no direct visibility to NIC connection status
 *      we rely on VMX to get such info.
 *
 *      Use the enableNicsX constants to fine tune behavior, if needed.
 *
 *      @param nics List of nics that need to be activated.
 *
 *------------------------------------------------------------------------------
 */
static void
TryToEnableNics(const char *nics)
{
   static const int enableNicsRetries = 5;
   static const int enableNicsWaitCount = 5;
   static const int enableNicsWaitSeconds = 1;

   char vmxResponse[64];   // buffer for responses from VMX calls

   int attempt, count;

   for (attempt = 0; attempt < enableNicsRetries; ++attempt) {
      sLog(log_debug,
           "Trying to connect network interfaces, attempt %d.",
           attempt + 1);

      if (!SetCustomizationStatusInVmxEx(TOOLSDEPLOYPKG_RUNNING,
                                         GUESTCUST_EVENT_ENABLE_NICS,
                                         nics,
                                         vmxResponse,
                                         NULL,
                                         sizeof(vmxResponse)))
      {
         sleep(enableNicsWaitCount * enableNicsWaitSeconds);
         continue;
      }

      // Note that we are checking for 'query nics' functionality in the loop to
      // protect against potential vMotion during customization process in which
      // case the new VMX could be older, i.e. not that supportive :)
      if (strcmp(vmxResponse, QUERY_NICS_SUPPORTED) != 0) {
         sLog(log_warning, "VMX doesn't support NICs connection status query.");
         return;
      }

      for (count = 0; count < enableNicsWaitCount; ++count) {
         // vMotion is unlikely between check for support above and actual call here
         if (SetCustomizationStatusInVmxEx(TOOLSDEPLOYPKG_RUNNING,
                                           GUESTCUST_EVENT_QUERY_NICS,
                                           nics,
                                           vmxResponse,
                                           NULL,
                                           sizeof(vmxResponse)) &&
             strcmp(vmxResponse, NICS_STATUS_CONNECTED) == 0)
         {
            sLog(log_info,
                 "The network interfaces are connected on %d second.",
                 (attempt * enableNicsWaitCount + count) *
                 enableNicsWaitSeconds);
            return;
         }

         sleep(enableNicsWaitSeconds);
      }
   }

   sLog(log_error,
        "Can't connect network interfaces after %d attempts, giving up.",
        enableNicsRetries);
}

/**
 *-----------------------------------------------------------------------------
 *
 * _DeployPkg_SkipReboot --
 *
 *      Controls skipping the last reboot when customization package is deployed.
 *	XXX This is a UNDOCUMENTED function and is WORKAROUND solution to PR 536688
 *
 *      @param path IN: skip - whether to skip the final reboot after customization
 *
 *-----------------------------------------------------------------------------
 */

static bool sSkipReboot = false;

IMGCUST_API void
_DeployPkg_SkipReboot(bool skip)
{
   sSkipReboot = skip;
}

/**
 *----------------------------------------------------------------------------
 *
 * CloudInitSetup --
 *
 * Function which does the setup for cloud-init if it is enabled.
 * Essentially it copies
 * - nics.tx
 * - cust.cfg to a predefined location.
 *
 * @param   [IN]  imcDirPath Path where nics.txt and cust.cfg exist
 * @returns DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED on success
 *          DEPLOYPKG_STATUS_ERROR on error
 *
 *----------------------------------------------------------------------------
 * */
static DeployPkgStatus
CloudInitSetup(const char *imcDirPath)
{
   DeployPkgStatus deployPkgStatus = DEPLOYPKG_STATUS_ERROR;
   static const char *cloudInitTmpDirPath = "/var/run/vmware-imc";
   int forkExecResult;
   char command[1024];
   Bool cloudInitTmpDirCreated = FALSE;
   char* customScriptName = NULL;
   sLog(log_info, "Creating temp directory '%s' to copy customization files.",
        cloudInitTmpDirPath);
   snprintf(command, sizeof(command),
            "/bin/mkdir -p %s", cloudInitTmpDirPath);
   command[sizeof(command) - 1] = '\0';

   forkExecResult = ForkExecAndWaitCommand(command, true, NULL, 0);
   if (forkExecResult != 0) {
      SetDeployError("Error creating '%s' dir.(%s)",
                     cloudInitTmpDirPath,
                     strerror(errno));
      goto done;
   }

   cloudInitTmpDirCreated = TRUE;

   // Copy required files for cloud-init to a temp name initially and then
   // rename in order to avoid race conditions with partial writes.
   // Regarding to metadata and userdata, we don't parse cust.cfg to check
   // if they are mandatory. That is done by cloud-init.
   if (!CopyFileIfExist(imcDirPath, cloudInitTmpDirPath, "nics.txt")) {
      goto done;
   }

   if (!CopyFileIfExist(imcDirPath, cloudInitTmpDirPath, "metadata")) {
      goto done;
   }

   if (!CopyFileIfExist(imcDirPath, cloudInitTmpDirPath, "userdata")) {
      goto done;
   }

   // Get custom script name.
   customScriptName = GetCustomScript(imcDirPath);
   if (customScriptName != NULL) {
      char scriptPath[1024];

      sLog(log_info, "Custom script present.");
      sLog(log_info, "Copying script to execute post customization.");
      snprintf(scriptPath, sizeof(scriptPath), "%s/scripts", imcDirPath);
      scriptPath[sizeof(scriptPath) - 1] = '\0';
      if (!CopyFileToDirectory(scriptPath, cloudInitTmpDirPath,
                               "post-customize-guest.sh")) {
         goto done;
      }

      sLog(log_info, "Copying user uploaded custom script '%s'.",
           customScriptName);
      if (!CopyFileToDirectory(imcDirPath, cloudInitTmpDirPath,
                               customScriptName)) {
         goto done;
      }
   }

   sLog(log_info, "Copying main configuration file cust.cfg.");
   if (!CopyFileToDirectory(imcDirPath, cloudInitTmpDirPath, "cust.cfg")) {
      goto done;
   }

   deployPkgStatus = DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED;

done:
   free(customScriptName);
   if (DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED == deployPkgStatus) {
      sLog(log_info, "Deployment for cloud-init succeeded.");
      TransitionState(INPROGRESS, DONE);
   } else {
      sLog(log_error, "Deployment for cloud-init failed.");
      if (cloudInitTmpDirCreated) {
         sLog(log_info, "Removing temporary folder '%s'.", cloudInitTmpDirPath);
         snprintf(command, sizeof(command),
                  "/bin/rm -rf %s",
                  cloudInitTmpDirPath);
         command[sizeof(command) - 1] = '\0';
         if (ForkExecAndWaitCommand(command, true, NULL, 0) != 0) {
            sLog(log_warning,
                 "Error while removing temporary folder '%s'. (%s)",
                 cloudInitTmpDirPath, strerror(errno));
         }
      }
      sLog(log_error, "Setting generic error status in vmx.");
      SetCustomizationStatusInVmx(TOOLSDEPLOYPKG_RUNNING,
                                  GUESTCUST_EVENT_CUSTOMIZE_FAILED,
                                  NULL);
      TransitionState(INPROGRESS, ERRORED);
   }

   return deployPkgStatus;
}


//......................................................................................

static bool
CopyFileToDirectory(const char* srcPath, const char* destPath,
                    const char* fileName)
{
   char command[1024];
   int forkExecResult;
   snprintf(command, sizeof(command), "/bin/cp %s/%s %s/%s.tmp", srcPath,
            fileName, destPath, fileName);
   command[sizeof(command) - 1] = '\0';
   forkExecResult = ForkExecAndWaitCommand(command, true, NULL, 0);
   if (forkExecResult != 0) {
      SetDeployError("Error while copying file '%s'.(%s)", fileName,
                     strerror(errno));
      return false;
   }
   snprintf(command, sizeof(command), "/bin/mv -f %s/%s.tmp %s/%s", destPath,
            fileName, destPath, fileName);
   command[sizeof(command) - 1] = '\0';

   forkExecResult = ForkExecAndWaitCommand(command, true, NULL, 0);
   if (forkExecResult != 0) {
      SetDeployError("Error while renaming temp file '%s'.(%s)", fileName,
                     strerror(errno));
      return false;
   }
   return true;
}


//......................................................................................

/**
 *----------------------------------------------------------------------------
 *
 * UseCloudInitWorkflow --
 *
 * Function which checks if cloud-init should be used for customization.
 * Essentially it checks if
 * - customization specificaion file (cust.cfg) is present.
 * - cloud-init is installed
 * - cloud-init is enabled.
 *
 * @param   [IN]  dirPath  Path where the package is extracted.
 * @param   [IN]  ignoreCloudInit  whether ignore cloud-init workflow.
 * @returns the error code to use cloud-init work flow
 *
 *----------------------------------------------------------------------------
 * */

static USE_CLOUDINIT_ERROR_CODE
UseCloudInitWorkflow(const char* dirPath, bool ignoreCloudInit)
{
   static const char cfgName[] = "cust.cfg";
   static const char metadataName[] = "metadata";
   static const char cloudInitCommand[] = "/usr/bin/cloud-init -v";
   char cloudInitCommandOutput[MAX_LENGTH_CLOUDINIT_VERSION];
   int forkExecResult;

   forkExecResult = ForkExecAndWaitCommand(cloudInitCommand,
                                           false,
                                           cloudInitCommandOutput,
                                           sizeof(cloudInitCommandOutput));
   if (forkExecResult != 0) {
      sLog(log_info, "Cloud-init is not installed.");
      return USE_CLOUDINIT_NOT_INSTALLED;
   } else {
      sLog(log_info, "Cloud-init is installed.");
      if (ignoreCloudInit) {
         sLog(log_info,
              "Ignoring cloud-init workflow according to header flags.");
         return USE_CLOUDINIT_IGNORE;
      }
   }

   if (NULL == dirPath) {
      return USE_CLOUDINIT_INTERNAL_ERROR;
   }

   // check if cust.cfg file exists
   if (!CheckFileExist(dirPath, cfgName)) {
      return USE_CLOUDINIT_NO_CUST_CFG;
   }

   // If cloud-init metadata exists, check if cloud-init support to handle
   // cloud-init raw data.
   // In this case, the guest customization must be delegated to cloud-init,
   // no need to check if cloud-init is enabled in cloud.cfg.
   if (CheckFileExist(dirPath, metadataName)) {
      int major, minor;
      GetCloudinitVersion(cloudInitCommandOutput, &major, &minor);
      sLog(log_info, "Metadata exists, check cloud-init version...");
      if (major < CLOUDINIT_SUPPORT_RAW_DATA_MAJOR_VERSION ||
          (major == CLOUDINIT_SUPPORT_RAW_DATA_MAJOR_VERSION &&
           minor < CLOUDINIT_SUPPORT_RAW_DATA_MINOR_VERSION)) {
          sLog(log_info,
               "Cloud-init version %d.%d is older than required version %d.%d.",
               major,
               minor,
               CLOUDINIT_SUPPORT_RAW_DATA_MAJOR_VERSION,
               CLOUDINIT_SUPPORT_RAW_DATA_MINOR_VERSION);
          return USE_CLOUDINIT_WRONG_VERSION;
      } else {
         return USE_CLOUDINIT_OK;
      }
   } else {
      if (IsCloudInitCustomizationEnabled()) {
         return USE_CLOUDINIT_OK;
      } else {
         return USE_CLOUDINIT_DISABLED;
      }
   }
}


/**
 *
 * Function which gets the current cloud-init execution status.
 * The status messages are copied from cloud-init offcial upstream
 * https://github.com/canonical/cloud-init/blob/main/cloudinit/cmd/status.py
 * These status messages are consistent since year 2017
 *
 * @returns the status code of cloud-init application
 *
 **/

static CLOUDINIT_STATUS_CODE
GetCloudinitStatus() {
   // Cloud-init execution status messages
   static const char* NOT_RUN = "not run";
   static const char* RUNNING = "running";
   static const char* DONE = "done";
   static const char* ERROR = "error";
   static const char* DISABLED = "disabled";

   static const char cloudinitStatusCmd[] = "/usr/bin/cloud-init status";
   char cloudinitStatusCmdOutput[MAX_LENGTH_CLOUDINIT_STATUS];
   int forkExecResult;

   forkExecResult = ForkExecAndWaitCommand(cloudinitStatusCmd,
                                           false,
                                           cloudinitStatusCmdOutput,
                                           MAX_LENGTH_CLOUDINIT_STATUS);
   if (forkExecResult != 0) {
      sLog(log_info, "Unable to get cloud-init status.");
      return CLOUDINIT_STATUS_UNKNOWN;
   } else {
      if (strstr(cloudinitStatusCmdOutput, NOT_RUN) != NULL) {
         sLog(log_info, "Cloud-init status is '%s'.", NOT_RUN);
         return CLOUDINIT_STATUS_NOT_RUN;
      } else if (strstr(cloudinitStatusCmdOutput, RUNNING) != NULL) {
         sLog(log_info, "Cloud-init status is '%s'.", RUNNING);
         return CLOUDINIT_STATUS_RUNNING;
      } else if (strstr(cloudinitStatusCmdOutput, DONE) != NULL) {
         sLog(log_info, "Cloud-init status is '%s'.", DONE);
         return CLOUDINIT_STATUS_DONE;
      } else if (strstr(cloudinitStatusCmdOutput, ERROR) != NULL) {
         sLog(log_info, "Cloud-init status is '%s'.", ERROR);
         return CLOUDINIT_STATUS_ERROR;
      } else if (strstr(cloudinitStatusCmdOutput, DISABLED) != NULL) {
         sLog(log_info, "Cloud-init status is '%s'.", DISABLED);
         return CLOUDINIT_STATUS_DISABLED;
      } else {
         sLog(log_warning, "Cloud-init status is unknown.");
         return CLOUDINIT_STATUS_UNKNOWN;
      }
   }
}


/**
 *
 * Function which waits for cloud-init execution done.
 *
 * This function is called only when below conditions are fulfilled:
 * - cloud-init is installed
 * - guest os reboot is not skipped (so traditional GOSC workflow only)
 * - deployment processed successfully in guest
 *
 * Default waiting timeout is DEFAULT_TIMEOUT_WAIT_FOR_CLOUDINIT_DONE seconds,
 * when the timeout is reached, reboot will be triggered no matter what the
 * cloud-init execution status is then.
 * The timeout can be overwritten by the value which is set in tools.conf,
 * if 0 is set in tools.conf, no waiting will be performed.
 *
 **/

static void
WaitForCloudinitDone() {
   const int CheckStatusInterval = 5;
   int timeoutSec = 0;
   int elapsedSec = 0;
   CLOUDINIT_STATUS_CODE cloudinitStatus = CLOUDINIT_STATUS_UNKNOWN;

   // No waiting when gWaitForCloudinitDoneTimeout is set to 0
   if (gWaitForCloudinitDoneTimeout == 0) {
      return;
   }

   timeoutSec = gWaitForCloudinitDoneTimeout;

   while (1) {
      if (elapsedSec == timeoutSec) {
         sLog(log_info, "Timed out waiting for cloud-init execution done.");
         return;
      }
      if (elapsedSec % CheckStatusInterval == 0) {
         cloudinitStatus = GetCloudinitStatus();
         // CLOUDINIT_STATUS_NOT_RUN and CLOUDINIT_STATUS_RUNNING represent
         // cloud-init execution has not finished
         if (cloudinitStatus != CLOUDINIT_STATUS_NOT_RUN &&
             cloudinitStatus != CLOUDINIT_STATUS_RUNNING) {
            sLog(log_info, "Cloud-init execution is not on-going.");
            return;
         }
      }
      sleep(1);
      elapsedSec++;
   }
}


/**
 *
 * Function which cleans up the deployment directory imcDirPath.
 * This function is called when customization deployment is completed or
 * any unexpected error happens before deployment begins.
 *
 * @param   [IN] imcDirPath  The deployment directory.
 * @returns true if cleaning up succeeds, false if cleaning up fails.
 *
 **/
static bool
DeleteTempDeploymentDirectory(const char* imcDirPath)
{
   int cleanupCommandSize;
   char* cleanupCommand;

   cleanupCommandSize = strlen(CLEANUPCMD) + strlen(imcDirPath) + 1;
   cleanupCommand = malloc(cleanupCommandSize);
   if (cleanupCommand == NULL) {
      SetDeployError("Error allocating memory."
                     "Failed to clean up imc directory '%s'", imcDirPath);
      return false;
   }

   strcpy(cleanupCommand, CLEANUPCMD);
   Str_Strcat(cleanupCommand, imcDirPath, cleanupCommandSize);

   sLog(log_info, "Launching cleanup.");
   if (ForkExecAndWaitCommand(cleanupCommand, true, NULL, 0) != 0) {
      sLog(log_warning, "Error while cleaning up imc directory '%s'. (%s)",
           imcDirPath, strerror(errno));
      free(cleanupCommand);
      return false;
   }
   free(cleanupCommand);
   return true;
}


/**
 *
 * Core function which takes care of deployment in Linux.
 * Essentially it does
 * - uncabing of the cabinet
 * - execution of the command embedded in the cabinet header
 *
 * @param   [IN]  packageName  Package file to be used for deployment
 * @returns DEPLOYPKG_STATUS_SUCCESS on success
 *          DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED if customization task is
 *          delegated to cloud-init.
 *          DEPLOYPKG_STATUS_ERROR on error
 *
 **/
static DeployPkgStatus
Deploy(const char* packageName)
{
   DeployPkgStatus deployPkgStatus = DEPLOYPKG_STATUS_SUCCESS;
   char* pkgCommand = NULL;
   char* command = NULL;
   int deploymentResult = 0;
   uint8 archiveType;
   uint8 flags;
   bool forceSkipReboot = false;
   const char *baseDirPath = NULL;
   char *imcDirPath = NULL;
   USE_CLOUDINIT_ERROR_CODE useCloudInitWorkflow = USE_CLOUDINIT_IGNORE;
   int imcDirPathSize = 0;
   bool ignoreCloudInit = false;
   TransitionState(NULL, INPROGRESS);

   // Notify the vpx of customization in-progress state
   SetCustomizationStatusInVmx(TOOLSDEPLOYPKG_RUNNING,
                               TOOLSDEPLOYPKG_ERROR_SUCCESS,
                               NULL);

   // Add this macro definition to enable using '/tmp' instead of '/var/run' as
   // the cab file deployment directory in unit test.
#ifdef IMGCUST_UNITTEST
   baseDirPath = TMPDIR;
#else
   // PR 2942062, Use /var/run/vmware-imc if the directory exists
   // PR 2127543, Use /var/run or /run but /tmp firstly
   if (File_IsDirectory(VARRUNIMCDIR)) {
      baseDirPath = VARRUNIMCDIR;
   } else if (File_IsDirectory(VARRUNDIR)) {
      baseDirPath = VARRUNDIR;
   } else if (File_IsDirectory(RUNDIR)) {
      baseDirPath = RUNDIR;
   } else {
      baseDirPath = TMPDIR;
   }
#endif

   // Create a random name dir under base dir path
   imcDirPathSize = strlen(baseDirPath) + strlen(IMC_DIR_PATH_PATTERN) + 1;
   imcDirPath = malloc(imcDirPathSize);
   if (imcDirPath == NULL) {
      SetDeployError("Error allocating memory to create imc dir.");
      return DEPLOYPKG_STATUS_ERROR;
   }
   strcpy(imcDirPath, baseDirPath);
   Str_Strcat(imcDirPath, IMC_DIR_PATH_PATTERN, imcDirPathSize);
   if (mkdtemp(imcDirPath) == NULL) {
      free(imcDirPath);
      SetDeployError("Error creating imc dir. (%s)", strerror(errno));
      return DEPLOYPKG_STATUS_ERROR;
   }

   sLog(log_info,
        "Reading cabinet file '%s' and will extract it to '%s'.",
         packageName,
         imcDirPath);

   // Get the command to execute
   if (!GetPackageInfo(packageName, &pkgCommand, &archiveType, &flags)) {
      SetDeployError("Error extracting package header information. (%s)",
                     GetDeployError());
      // Possible errors have been logged inside DeleteTempDeploymentDirectory.
      // So no need to check its return value and log error here.
      DeleteTempDeploymentDirectory(imcDirPath);
      free(imcDirPath);
      return DEPLOYPKG_STATUS_CAB_ERROR;
   }

   sLog(log_info, "Flags in the header: %d.", (int) flags);

   sLog(log_info, "Original deployment command: '%s'.", pkgCommand);
   if (strstr(pkgCommand, IMC_TMP_PATH_VAR) != NULL) {
      command = StrUtil_ReplaceAll(pkgCommand, IMC_TMP_PATH_VAR, imcDirPath);
   } else {
      command = StrUtil_ReplaceAll(pkgCommand, TMP_PATH_VAR, imcDirPath);
   }
   free(pkgCommand);

   sLog(log_info, "Actual deployment command: '%s'.", command);

   if (archiveType == VMWAREDEPLOYPKG_PAYLOAD_TYPE_CAB) {
      if (!ExtractCabPackage(packageName, imcDirPath)) {
         DeleteTempDeploymentDirectory(imcDirPath);
         free(imcDirPath);
         free(command);
         return DEPLOYPKG_STATUS_CAB_ERROR;
      }
   } else if (archiveType == VMWAREDEPLOYPKG_PAYLOAD_TYPE_ZIP) {
      if (!ExtractZipPackage(packageName, imcDirPath)) {
         DeleteTempDeploymentDirectory(imcDirPath);
         free(imcDirPath);
         free(command);
         return DEPLOYPKG_STATUS_CAB_ERROR;
      }
   }

   ignoreCloudInit = flags & VMWAREDEPLOYPKG_HEADER_FLAGS_IGNORE_CLOUD_INIT;
   useCloudInitWorkflow = UseCloudInitWorkflow(imcDirPath, ignoreCloudInit);

   sLog(log_info, "UseCloudInitWorkflow return: %d", useCloudInitWorkflow);

   if (useCloudInitWorkflow == USE_CLOUDINIT_OK) {
      sLog(log_info, "Executing cloud-init workflow.");
      sSkipReboot = TRUE;
      free(command);
      deployPkgStatus = CloudInitSetup(imcDirPath);
   } else if (useCloudInitWorkflow == USE_CLOUDINIT_WRONG_VERSION ||
              useCloudInitWorkflow == USE_CLOUDINIT_INTERNAL_ERROR) {
      int errCode = (useCloudInitWorkflow == USE_CLOUDINIT_WRONG_VERSION) ?
         TOOLSDEPLOYPKG_ERROR_CLOUDINIT_NOT_SUPPORT_RAWDATA :
         GUESTCUST_EVENT_CUSTOMIZE_FAILED;
      TransitionState(INPROGRESS, ERRORED);

      SetDeployError("Deployment failed. use cloud-init work flow return: %d",
                     useCloudInitWorkflow);
      sLog(log_error, "Deployment failed. use cloud-init work flow return: %d",
           useCloudInitWorkflow);
      SetCustomizationStatusInVmx(TOOLSDEPLOYPKG_RUNNING,
                                  errCode,
                                  "Deployment failed");
      DeleteTempDeploymentDirectory(imcDirPath);
      free(imcDirPath);
      free(command);
      return DEPLOYPKG_STATUS_ERROR;
   } else {
      sLog(log_info, "Executing traditional GOSC workflow.");
      deploymentResult = ForkExecAndWaitCommand(command, true, NULL, 0);
      free(command);

      if (deploymentResult != CUST_SUCCESS) {
         sLog(log_error, "Customization process returned with error.");
         sLog(log_debug, "Deployment result = %d.", deploymentResult);

         if (deploymentResult == CUST_NETWORK_ERROR ||
             deploymentResult == CUST_NIC_ERROR ||
             deploymentResult == CUST_DNS_ERROR) {
            sLog(log_info, "Setting network error status in vmx.");
            SetCustomizationStatusInVmx(TOOLSDEPLOYPKG_RUNNING,
                                        GUESTCUST_EVENT_NETWORK_SETUP_FAILED,
                                        NULL);
         } else if (deploymentResult == CUST_SCRIPT_DISABLED_ERROR) {
            sLog(log_info,
                 "Setting custom script disabled error status in vmx.");
            SetCustomizationStatusInVmx(TOOLSDEPLOYPKG_RUNNING,
               TOOLSDEPLOYPKG_ERROR_CUST_SCRIPT_DISABLED, NULL);
         } else {
            sLog(log_info, "Setting '%s' error status in vmx.",
                 deploymentResult == CUST_GENERIC_ERROR ? "generic" : "unknown");
            SetCustomizationStatusInVmx(TOOLSDEPLOYPKG_RUNNING,
                                        GUESTCUST_EVENT_CUSTOMIZE_FAILED,
                                        NULL);
         }

         TransitionState(INPROGRESS, ERRORED);

         deployPkgStatus = DEPLOYPKG_STATUS_ERROR;
         SetDeployError("Deployment failed."
                        "The forked off process returned error code.");
         sLog(log_error, "Deployment failed."
                         "The forked off process returned error code.");
      } else {
         char *nics = GetNicsToEnable(imcDirPath);

         if (nics != NULL) {
            // XXX: Sleep before the last SetCustomizationStatusInVmx
            //      This is a temporary-hack for PR 422790
            sleep(5);
            sLog(log_info, "Wait before set enable-nics stats in vmx.");

            TryToEnableNics(nics);

            free(nics);
         } else {
            sLog(log_info, "No nics to enable.");
         }

         SetCustomizationStatusInVmx(TOOLSDEPLOYPKG_DONE,
                                     TOOLSDEPLOYPKG_ERROR_SUCCESS,
                                     NULL);

         TransitionState(INPROGRESS, DONE);

         deployPkgStatus = DEPLOYPKG_STATUS_SUCCESS;
         sLog(log_info, "Deployment succeeded.");
      }
   }

   if (!DeleteTempDeploymentDirectory(imcDirPath)) {
      free(imcDirPath);
      return DEPLOYPKG_STATUS_ERROR;
   }
   free(imcDirPath);

   if (flags & VMWAREDEPLOYPKG_HEADER_FLAGS_SKIP_REBOOT) {
      forceSkipReboot = true;
   }
   sLog(log_info,
        "sSkipReboot: '%s', forceSkipReboot '%s'.",
        sSkipReboot ? "true" : "false",
        forceSkipReboot ? "true" : "false");
   sSkipReboot |= forceSkipReboot;

   //Reset the guest OS
   if (!sSkipReboot && !deploymentResult) {
      if (useCloudInitWorkflow != USE_CLOUDINIT_NOT_INSTALLED) {
         sLog(log_info, "Do not trigger reboot if cloud-init is executing.");
         WaitForCloudinitDone();
      }
      pid_t pid = fork();
      if (pid == -1) {
         sLog(log_error, "Failed to fork: '%s'.", strerror(errno));
      } else if (pid == 0) {
         // We're in the child
         int rebootCommandResult;
         bool isRebooting = false;
         // Retry reboot until telinit 6 succeeds to workaround PR 2716292 where
         // telinit is a soft(symbolic) link to systemctl and it could exit
         // abnormally due to systemd sends SIGTERM
         bool retryReboot = IsTelinitASoftlinkToSystemctl();
         sLog(log_info, "Trigger reboot.");
         // Repeatedly try to reboot to workaround PR 530641 where
         // telinit 6 is overwritten by a telinit 2
         do {
            if (isRebooting) {
               sLog(log_info, "Rebooting.");
            }
            rebootCommandResult =
               ForkExecAndWaitCommand("/sbin/telinit 6", true, NULL, 0);
            isRebooting = (rebootCommandResult == 0) ? true : isRebooting;
            sleep(1);
         } while (rebootCommandResult == 0 || (retryReboot && !isRebooting));
         if (!isRebooting) {
            sLog(log_error,
                 "Failed to reboot, reboot command returned error %d.",
                 rebootCommandResult);
            exit (127);
         } else {
            sLog(log_info, "Reboot has been triggered.");
         }
      }
   }

   return deployPkgStatus;
}

/**
 * Extract all files into the destination folder.
 */
Bool
ExtractCabPackage(const char* cabFileName,
                  const char* destDir)
{
   unsigned int error;

   sLog(log_info, "Extracting package files.");

   // Set log function
   MspackWrapper_SetLogger(sLog);

   // Self check library compatibility
   if ((error = SelfTestMspack()) != LINUXCAB_SUCCESS) {
      SetDeployError("mspack self test failed. (%s)", GetLinuxCabErrorMsg(error));
      return FALSE;
   }

   // check if cab file is set
   if (cabFileName == NULL) {
      SetDeployError("Cab file not set.");
      return FALSE;
   }

   // uncab the cabinet file
   if ((error = ExpandAllFilesInCab(cabFileName, destDir)) != LINUXCAB_SUCCESS) {
      SetDeployError("Error expanding cabinet. (%s)", GetLinuxCabErrorMsg(error));
      return FALSE;
   }
   return TRUE;
}

/**
 * Extract all files into the destination folder.
 */
static Bool
ExtractZipPackage(const char* pkgName,
                  const char* destDir)
{
   ProcessHandle h;
   char* args[32];
   const char* stderr;

   int pkgFd, zipFd;
   char zipName[1024];
   char copyBuf[4096];
   ssize_t rdCount;
   char* destCopy;

   Bool ret = TRUE;

   // strip the header from the file
   snprintf(zipName, sizeof zipName, "%s/%llx", destDir, (long long)time(NULL));
   zipName[(sizeof zipName) - 1] = '\0';
   if ((pkgFd = open(pkgName, O_RDONLY)) < 0) {
      sLog(log_error, "Failed to open package file '%s' for read. (%s)",
           pkgName, strerror(errno));
      return FALSE;
   }
   if ((zipFd = open(zipName, O_CREAT | O_WRONLY | O_TRUNC, 0700)) < 0) {
      sLog(log_error, "Failed to create temporary zip file '%s'. (%s)", zipName,
           strerror(errno));
      close(pkgFd);
      return FALSE;;
   }
   if (lseek(pkgFd, sizeof(VMwareDeployPkgHdr), 0) == (off_t) -1) {
      sLog(log_error,
           "Failed to set the offset for the package file '%s'. (%s)",
           pkgName, strerror(errno));
      close(pkgFd);
      close(zipFd);
      ret = FALSE;
      goto done;
   }
   while ((rdCount = read(pkgFd, copyBuf, sizeof copyBuf)) > 0) {
      if (write(zipFd, copyBuf, rdCount) < 0) {
         sLog(log_error, "Failed to write the zip file '%s'. (%s)", zipName,
              strerror(errno));
         close(pkgFd);
         close(zipFd);
         ret = FALSE;
         goto done;
      }
   }

   close(pkgFd);
   close(zipFd);

   destCopy = strdup(destDir);  // destDir is const
   args[0] = "/usr/bin/unzip";
   args[1] = "-o";
   args[2] = zipName;
   args[3] = "-d";
   args[4] = destCopy;
   args[5] = NULL;
   Process_Create(&h, args, sLog);
   free(destCopy);
   Process_RunToComplete(h, gProcessTimeout);

   sLog(log_info, "unzip output: '%s'.", Process_GetStdout(h));

   // Assume zip failed if it wrote to stderr
   stderr = Process_GetStderr(h);
   if (strlen(stderr) > 0) {
      sLog(log_error, "Package unzip failed: '%s'.", stderr);
      ret = FALSE;
   }

   Process_Destroy(h);
done:
   // Clean up the temporary zip file
   if (remove(zipName) != 0) {
      sLog(log_warning, "Failed to remove the temporary zip file '%s'. (%s)",
           zipName, strerror(errno));
   }

   return ret;
}

//......................................................................................

/**
 *
 * Coverts the string into array of "C" string with NULL as the last
 * element in the array. This is the way an "exec" in Linux expects its
 * parameters.
 *
 * @param   [IN]  command  Command to execute
 * @return  2-Dimensional array of "C" string or NULL on error
 *
 **/
static char**
GetFormattedCommandLine(const char* command)
{
   // tokenize it into program that is needed to be executed and arguments for it
   struct List* commandTokens = NULL;

   char token[strlen(command) + 1];
   unsigned int wToken = 0; // write head for token

   char** args;
   struct List* l;

   unsigned int i;
   for (i = 0; i < strlen(command); ++i) {
      if (command[i] == BACKSLASH) {// if backslash skip next char
         token[wToken++] = command[i++];
         if (i < (strlen(command) - 1)) {
            token[wToken++] = command[i++];
         }
         continue;
      } else if (command[i] == QUOTECHAR) {// if quote skip till next quote
         token[wToken++] = QUOTECHAR;
         for (++i; command[i] && (command[i] != QUOTECHAR); ++i) {
            token[wToken++] = command[i];
         }
         token[wToken++] = QUOTECHAR;
         continue;
      } else if (command[i] == SPACECHAR || command[i] == TABCHAR) {// tab or space
         token[wToken++] = 0;
         commandTokens = AddToList(commandTokens, token);

         memset(token, 0, strlen(command));
         wToken = 0;

         // seek to the next char position that is not a space or a tab
         for (;command[i] != SPACECHAR && command[i] != TABCHAR; ++i);
      } else {// add it to token
         token[wToken++] = command[i];
      }
   }

   // last token -- check and insert
   /* if (token) { */
      commandTokens = AddToList(commandTokens, token);
   /* } */

   // prefixing the start path for the commands
   args = malloc((ListSize(commandTokens) + 1) * sizeof(char*));
   if (args == NULL) {
      SetDeployError("Error allocating memory.");
      // clear resources
      DeleteList(commandTokens);
      return NULL;
   }

   for(l = commandTokens, i = 0; l; l = l->next, i++) {
      char* arg = malloc(strlen(l->data) + 1);
      if (arg == NULL) {
         unsigned int j;
         SetDeployError("Error allocating memory. (%s)", strerror(errno));
         // free allocated memories in previous iterations if any
         for (j = 0; j < i; j++) {
            free(args[j]);
         }
         free(args);
         // clear resources
         DeleteList(commandTokens);
         return NULL;
      }

      strcpy (arg, l->data);
      args[i] = arg;

#ifdef VMX86_DEBUG
      sLog(log_debug, "Arg (address & value) : %p '%s'.", args[i], args[i]);
#endif
   }

   // marks the end of params
   args[ListSize(commandTokens)] = NULL;

   // clear resources
   DeleteList(commandTokens);

   return args;
}

//......................................................................................

/**
 *
 * Fork off the command and wait for it to finish. Classical Linux/Unix
 * fork-and-exec.
 *
 * @param   [IN]  command       Command to execute
 * @param   [IN]  failIfStdErr  Whether to treat stderr as command failed when
 *                              command's return code is 0.
 * @param   [OUT] forkOutput    Return the command stdout. If stdout is empty,
 *                              return the command stderr.
 * @param   [IN]  maxOutputLen  The maximum length to return from command
 *                              output
 * @return  Return code from the process (or -1)
 *
 **/
int
ForkExecAndWaitCommand(const char* command,
                       bool failIfStdErr,
                       char* forkOutput,
                       int maxOutputLen)
{
   ProcessHandle hp;
   int retval;
   int i;
   char** args = GetFormattedCommandLine(command);
   const char* processStdOut;
   Bool isPerlCommand = (strcmp(args[0], "/usr/bin/perl") == 0) ? true : false;
   Bool isTelinitCommand =
      (strcmp(args[0], "/sbin/telinit") == 0) ? true : false;

   sLog(log_debug, "Command to exec : '%s'.", args[0]);
   Process_Create(&hp, args, sLog);

   // Free args array as Process_Create has its own private copy now.
   for (i = 0; args[i] != NULL; i++) {
      free(args[i]);
   }
   free(args);

   Process_RunToComplete(hp, gProcessTimeout);

   processStdOut = Process_GetStdout(hp);

   if (forkOutput != NULL) {
      // Copy the command stdout. If stdout is empty, copy the command stderr.
      if (strlen(processStdOut) > 0) {
         Str_Strncpy(forkOutput, maxOutputLen, processStdOut,
                     maxOutputLen - 1);
      } else {
         Str_Strncpy(forkOutput, maxOutputLen, Process_GetStderr(hp),
                     maxOutputLen - 1);
      }
   }

   if (isPerlCommand) {
      sLog(log_info, "Customization command output:\n%s\n%s\n%s",
         "=================== Perl script log start =================",
         processStdOut,
         "=================== Perl script log end =================");
   } else {
      sLog(log_info, "Customization command output:\n'%s'.",
         processStdOut);
   }
   retval = Process_GetExitCode(hp);

   if (retval == 0) {
      const char* processStdErr = Process_GetStderr(hp);
      if (strlen(processStdErr) > 0) {
         if (failIfStdErr) {
            // Assume command failed if it wrote to stderr, although exitCode
            // is 0.
            sLog(log_error,
                 "Customization command failed with stderr: '%s'.",
                 processStdErr);
            retval = -1;
         } else {
            // Assume command succeeded if exitCode is 0, although it wrote to
            // stderr. e.g, PR2148977, "cloud-init -v" will return 0
            // even there is output in stderr
            sLog(log_info, "Command succeeded despite of stderr output: '%s'.",
                 processStdErr);
         }
      }
   } else {
      if (isTelinitCommand) {
         sLog(log_info,
              "Telinit command failed with exitcode: %d, stderr: '%s'.",
              retval,
              Process_GetStderr(hp));
      } else {
         sLog(log_error,
              "Customization command failed with exitcode: %d, stderr: '%s'.",
              retval,
              Process_GetStderr(hp));
      }
   }

   Process_Destroy(hp);
   return retval;
}

//.............................................................................

/**
 *
 * Decodes a package from a file, extracts its payload,
 * expands the payload into a temporary directory, and then executes
 * the command specified in the package.
 *
 * @param   [IN]  file  The package file
 * @returns DEPLOYPKG_STATUS_SUCCESS on success
 *          DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED if customization task is
 *          delegated to cloud-init.
 *          DEPLOYPKG_STATUS_ERROR on error
 *
 **/
DeployPkgStatus
DeployPkg_DeployPackageFromFileEx(const char* file)
{
   DeployPkgStatus retStatus;

#if !defined(OPEN_VM_TOOLS) && !defined(USERWORLD)
   sLog(log_info, "libDeployPkg.so version: %s (%s)",
        SYSIMAGE_VERSION_EXT_STR, BUILD_NUMBER);
#else
   /*
    * For OPEN_VM_TOOLS and USERWORLD, the vmtoolsd version is logged in
    * function DeployPkgDeployPkgInGuest from
    * services/plugins/deployPkg/deployPkg.c
    */
#endif
   sLog(log_info, "Initializing deployment module.");
   Init();

   sLog(log_info, "Deploying cabinet file '%s'.", file);
   retStatus = Deploy(file);

   if (retStatus != DEPLOYPKG_STATUS_SUCCESS &&
       retStatus != DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED) {
      sLog(log_error, "Deploy error: '%s'.", GetDeployError());
   }

   free(gDeployError);
   gDeployError = NULL;

   return retStatus;
}

//.............................................................................

/**
 *
 * Decodes a package from a file, extracts its payload,
 * expands the payload into a temporary directory, and then executes
 * the command specified in the package.
 *
 * @param   [IN]  file  The package file
 * @returns 0 on success and -1 on error
 *
 **/
int
DeployPkg_DeployPackageFromFile(const char* file)
{
   DeployPkgStatus deployPkgStatus = DeployPkg_DeployPackageFromFileEx(file);
   int retStatus;

   switch (deployPkgStatus) {
      case DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED:
         /*
          * The return code from DeployPkg_DeployPackageFromFile should
          * be either 0 (for success) or -1 (for failure).
          * DEPLOYPKG_STATUS_CLOUD_INIT_DELEGATED should be treated as
          * success. So fallback to DEPLOYPKG_STATUS_SUCCESS.
          */
         sLog(log_info,
              "Deployment delegated to Cloud-init. Returning success.");
      case DEPLOYPKG_STATUS_SUCCESS:
         retStatus = 0;
         break;
      default:
         retStatus = -1;
         break;
   }

   return retStatus;
}

/**
 *
 * Check if the given file exists or not
 *
 * @param  [IN]  dirPath     The dir path of the given file
 * @param  [IN]  fileName    The file name of the given file
 * @returns  TRUE if file exists.
 *           FALSE if file doesn't exist or an error occured.
 *
 **/
static Bool
CheckFileExist(const char* dirPath, const char* fileName)
{
   Bool ret;
   int fullPathSize = strlen(dirPath) + strlen(fileName) + 2 /* '/' and \0 */;
   char *fullPath = (char *) malloc(fullPathSize);
   if (fullPath == NULL) {
      sLog(log_error, "Failed to allocate memory. (%s)", strerror(errno));
      return FALSE;
   }

   snprintf(fullPath, fullPathSize, "%s/%s", dirPath, fileName);
   ret = File_Exists(fullPath);
   free(fullPath);
   return ret;
}

/**
 *
 * Copy the given file to target directory if it exists
 *
 * @param  [IN]  sourcePath     The dir path to copy the file from
 * @param  [IN]  targetPath     The dir path to copy the file to
 * @param  [IN]  fileName       The file name to copy
 * @returns  TRUE if file is copied or not exist.
 *           FALSE if any error occurs.
 *
 **/
static Bool
CopyFileIfExist(const char* sourcePath,
                const char* targetPath,
                const char* fileName)
{
   sLog(log_info, "Copy file %s/%s to directory %s, return if not exist.",
        sourcePath, fileName, targetPath);

   if (CheckFileExist(sourcePath, fileName)) {
      sLog(log_info, "file %s exists. Copying...", fileName);
      if (!CopyFileToDirectory(sourcePath, targetPath, fileName)) {
         return FALSE;
       }
   } else {
      sLog(log_info, "file %s doesn't exist, skipped.", fileName);
   }
   return TRUE;
}

/**
 *
 * Get the cloudinit version from "cloud-init -v" output.
 *
 * The "cloud-init -v" output is something like:
 *    /usr/bin/cloud-init 20.3-2-g371b392c-0ubuntu1~20.04.1
 *    or
 *    cloud-init 0.7.9
 *
 * @param [IN] version    The output of command "cloud-init -v"
 * @param [OUT] major     The major version of cloud-init
 * @param [OUT] minor     The minor version of cloud-init
 *
 * examples:
 *    /usr/bin/cloud-init 20.3-2-g371b392c-0ubuntu1~20.04.1
 *       major: 20, minor: 3
 *    cloud-init 0.7.9
 *       major: 0, minor: 7
 **/
static void
GetCloudinitVersion(const char* version, int* major, int* minor)
{
   *major = *minor = 0;
   if (version == NULL || strlen(version) == 0) {
      sLog(log_warning, "Invalid cloud-init version.");
      return;
   }
   sLog(log_info, "Parse cloud-init version from :%s", version);

   if (isdigit(version[0])) {
      sscanf(version, "%d%*[-.]%d", major, minor);
   } else {
      sscanf(version, "%*[^0123456789]%d%*[-.]%d", major, minor);
   }
   sLog(log_info, "Cloud-init version major: %d, minor: %d", *major, *minor);
}

/**
 *
 * Check if "telinit" command is a soft(symbolic) link to "systemctl" command
 *
 * The fullpath of "systemctl" command could be:
 *    /bin/systemctl
 *    or
 *    /usr/bin/systemctl
 *
 * @returns TRUE if "telinit" command is a soft link to "systemctl" command
 *          FALSE if "telinit" command is not a soft link to "systemctl" command
 *
 **/
static Bool
IsTelinitASoftlinkToSystemctl(void)
{
   static const char systemctlBinPath[] = "/bin/systemctl";
   static const char readlinkCommand[] = "/bin/readlink /sbin/telinit";
   char readlinkCommandOutput[256];
   int forkExecResult;

   forkExecResult = ForkExecAndWaitCommand(readlinkCommand,
                                           true,
                                           readlinkCommandOutput,
                                           sizeof(readlinkCommandOutput));
   if (forkExecResult != 0) {
      sLog(log_debug, "readlink command result = %d.", forkExecResult);
      return FALSE;
   }

   if (strstr(readlinkCommandOutput, systemctlBinPath) != NULL) {
      sLog(log_debug, "/sbin/telinit is a soft link to systemctl");
      return TRUE;
   } else {
      sLog(log_debug, "/sbin/telinit is not a soft link to systemctl");
   }

   return FALSE;
}
07070100000213000081A4000000000000000000000001682255050000252F000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/linuxDeploymentUtilities.c    /*********************************************************
 * Copyright (c) 2016-2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include "linuxDeploymentUtilities.h"
#include "str.h"

extern LogFunction sLog;

// The status code of flag 'disable_vmware_customization'
typedef enum DISABLE_VMWARE_CUSTIOMIZATION_FLAG_STATUS_CODE {
   DISABLE_VMWARE_CUSTOMIZATION_FLAG_UNSET = 0,
   DISABLE_VMWARE_CUSTOMIZATION_FLAG_SET_TRUE,
   DISABLE_VMWARE_CUSTOMIZATION_FLAG_SET_FALSE,
} DISABLE_VMWARE_CUSTIOMIZATION_FLAG_STATUS_CODE;

// Private functions
static DISABLE_VMWARE_CUSTIOMIZATION_FLAG_STATUS_CODE
GetDisableVMwareCustomizationFlagStatus(const char* cloudInitConfigFilePath);
static int
FilterCfgExt(const struct dirent *dir);

/**
 *----------------------------------------------------------------------------
 *
 * IsCloudInitCustomizationEnabled
 *
 * Function to determine if cloud-init customization workflow is enabled.
 * Essentially it does
 *  - Read all cloud-init configuration files under /etc/cloud/cloud.cfg.d/
 *  - Read the cloud-init configuration file /etc/cloud/cloud.cfg
 *  - Find if a particular flag is enabled or disabled
 *  - Particularly, the value of flag in files under /etc/cloud/cloud.cfg.d/
 *    has higher priority than the one in file /etc/cloud/cloud.cfg, and the
 *    value of flag in file listed behind in alphabetical sort under
 *    /etc/cloud/cloud.cfg.d/ has higher priority than the one in file listed
 *    in front
 *
 * @returns TRUE if value of the flag 'disable_vmware_customization' is false
 *          FALSE otherwise
 *
 *----------------------------------------------------------------------------
 **/
bool
IsCloudInitCustomizationEnabled()
{
   DISABLE_VMWARE_CUSTIOMIZATION_FLAG_STATUS_CODE flagStatus =
      DISABLE_VMWARE_CUSTOMIZATION_FLAG_UNSET;
   static const char cloudInitBaseConfigFilePath[] = "/etc/cloud/cloud.cfg";
   static const char cloudInitConfigDirPath[] = "/etc/cloud/cloud.cfg.d/";
   struct dirent **fileList;
   int i, fileCount;
   size_t filePathLength;
   char *filePath = NULL;

   sLog(log_info, "Checking if cloud-init customization is enabled.");
   fileCount =
      scandir(cloudInitConfigDirPath, &fileList, FilterCfgExt, alphasort);
   if (fileCount < 0) {
      sLog(log_warning, "Could not scan directory %s, error: %s.",
         cloudInitConfigDirPath, strerror(errno));
   } else {
      for (i = fileCount - 1; i >= 0; i--) {
         filePathLength =
            Str_Strlen(cloudInitConfigDirPath, sizeof(cloudInitConfigDirPath))
            + Str_Strlen(fileList[i]->d_name, NAME_MAX) + 1;
         filePath = malloc(filePathLength);
         if (filePath == NULL) {
            sLog(log_warning, "Error allocating memory to copy '%s'.",
               cloudInitConfigDirPath);
            break;
         }
         Str_Strcpy(filePath, cloudInitConfigDirPath, filePathLength);
         Str_Strcat(filePath, fileList[i]->d_name, filePathLength);
         flagStatus = GetDisableVMwareCustomizationFlagStatus(filePath);
         free(filePath);
         filePath = NULL;
         if (flagStatus != DISABLE_VMWARE_CUSTOMIZATION_FLAG_UNSET) {
            break;
         }
      }
      for (i = 0; i < fileCount; i++) {
         free(fileList[i]);
      }
   }
   free(fileList);

   if (flagStatus == DISABLE_VMWARE_CUSTOMIZATION_FLAG_UNSET) {
      flagStatus =
         GetDisableVMwareCustomizationFlagStatus(cloudInitBaseConfigFilePath);
   }

   return (flagStatus == DISABLE_VMWARE_CUSTOMIZATION_FLAG_SET_FALSE);
}

/**
 *-----------------------------------------------------------------------------
 *
 * GetCustomScript
 *
 * Get custom script name if it exists.  Returns the first script found.
 *
 * @param   [IN]      dirPath     path to extracted cab files
 *
 * @returns the script name of the user uploaded custom script if it
 *          is found in dirPath.  Must be freed by caller.
 *
 *          NULL on failure or if the script does not exist
 *
 * ----------------------------------------------------------------------------
 **/
char *
GetCustomScript(const char* dirPath)
{
   char *scriptName = NULL;
   static const char *customScriptRegex = "^script[A-Za-z0-9]*\\.bat";
   DIR *tempDir;
   struct dirent *dir;
   regex_t scriptRegex;
   int regRet;

   sLog(log_info, "Check if custom script(pre/post customization) exists.");
   tempDir = opendir(dirPath);
   if (tempDir == NULL) {
      sLog(log_warning, "Could not open directory %s: error: %s.", dirPath,
           strerror(errno));
      return scriptName;
   }

   regRet = regcomp(&scriptRegex, customScriptRegex, 0);
   if (regRet != 0) {
      char buf[256];

      regerror(regRet, &scriptRegex, buf, sizeof(buf));
      sLog(log_error, "Error compiling regex for custom script: %s.", buf);
      goto done;
   }

   while ((dir = readdir(tempDir)) != NULL) {
      if (regexec(&scriptRegex, dir->d_name, 0, NULL, 0) == 0) {
         scriptName = strdup(dir->d_name);
         if (scriptName == NULL) {
            sLog(log_warning, "Could not allocate memory for scriptName: %s.",
                 strerror(errno));
            break;
         }
         break;
      }
   }
   regfree(&scriptRegex);

done:
   closedir(tempDir);
   return scriptName;
}

/**
 *----------------------------------------------------------------------------
 *
 * GetDisableVMwareCustomizationFlagStatus
 *
 * Function to get status code of the flag 'disable_vmware_customization' from
 * a cloud-init config file.
 * Essentially it does
 *  - Read a cloud-init config file
 *  - Get status code of the flag according to its value
 *
 * @param   [IN]   cloudInitConfigFilePath   path of a cloud-int config file
 * @returns The status code of this particular flag
 *
 *----------------------------------------------------------------------------
 **/
static DISABLE_VMWARE_CUSTIOMIZATION_FLAG_STATUS_CODE
GetDisableVMwareCustomizationFlagStatus(const char* cloudInitConfigFilePath)
{
   DISABLE_VMWARE_CUSTIOMIZATION_FLAG_STATUS_CODE flagStatus =
      DISABLE_VMWARE_CUSTOMIZATION_FLAG_UNSET;
   FILE *cloudInitConfigFile;
   char line[256];
   regex_t regex;
   size_t maxGroups = 2, flagValueLength = 0;
   regmatch_t groupArray[maxGroups];
   const char *flagPattern =
      "^\\s*disable_vmware_customization\\s*:\\s*(true|false)\\s*$";
   int reti;

   cloudInitConfigFile = fopen(cloudInitConfigFilePath, "r");
   if (cloudInitConfigFile == NULL) {
      sLog(log_warning, "Could not open file: %s.", strerror(errno));
      return flagStatus;
   }

   reti = regcomp(&regex, flagPattern, REG_EXTENDED);
   if (reti != 0) {
      char buf[256];
      regerror(reti, &regex, buf, sizeof(buf));
      sLog(log_error, "Error compiling regex for cloud-init flag: %s.", buf);
      goto done;
   }

   while (fgets(line, sizeof(line), cloudInitConfigFile) != NULL) {
      if (regexec(&regex, line, maxGroups, groupArray, 0) == 0) {
         flagValueLength = groupArray[1].rm_eo - groupArray[1].rm_so;
         if (flagValueLength > 0) {
            char flagValue[flagValueLength + 1];
            Str_Strncpy(flagValue, flagValueLength + 1,
               line + groupArray[1].rm_so, flagValueLength);
            sLog(log_info,
               "Flag 'disable_vmware_customization' set in %s with value: %s.",
               cloudInitConfigFilePath, flagValue);
            if (Str_Strequal(flagValue, "false")) {
               flagStatus = DISABLE_VMWARE_CUSTOMIZATION_FLAG_SET_FALSE;
            } else if (Str_Strequal(flagValue, "true")) {
               flagStatus = DISABLE_VMWARE_CUSTOMIZATION_FLAG_SET_TRUE;
            }
         }
      }
   }
   if (ferror(cloudInitConfigFile) != 0) {
      sLog(log_warning, "Error reading file: %s.", strerror(errno));
      flagStatus = DISABLE_VMWARE_CUSTOMIZATION_FLAG_UNSET;
   }
   regfree(&regex);

done:
   fclose(cloudInitConfigFile);
   return flagStatus;
}

/**
 *-----------------------------------------------------------------------------
 *
 * FilterCfgExt
 *
 * Filter files with .cfg extension when calling scandir.
 *
 * @param   [IN]   dir   struct dirent of a directory entry
 * @returns 1 if dir is a regular file and its file extension is .cfg
 *          0 otherwise
 *
 * ----------------------------------------------------------------------------
 **/
static int
FilterCfgExt(const struct dirent *dir)
{
   if (!dir)
      return 0;

   if (dir->d_type == DT_REG) {
      const char *ext = Str_Strrchr(dir->d_name, '.');
      if ((!ext) || (ext == dir->d_name)) {
         return 0;
      } else if (Str_Strequal(ext, ".cfg")) {
         return 1;
      }
   }

   return 0;
}
 07070100000214000081A400000000000000000000000168225505000004B4000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/linuxDeploymentUtilities.h    /*********************************************************
 * Copyright (c) 2016-2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _LINUXDEPLOYMENTUTILITIES_H_
#define _LINUXDEPLOYMENTUTILITIES_H_

#include <stdbool.h>
#include "imgcust-common/log.h"
#include "imgcust-common/imgcust-api.h"

IMGCUST_API bool
IsCloudInitCustomizationEnabled();

IMGCUST_API char *
GetCustomScript(const char* dirPath);
#endif //_LINUXDEPLOYMENTUTILITIES_H_

07070100000215000081A40000000000000000000000016822550500001270000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/mspackConfig.h    /*********************************************************
 * Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * mspackConfig.h --
 *
 *      Definitions of mspack constants.
 */

/*
 * This file is a version of the auto generated code. This has a bunch of
 * #definitions which are required to make the mspack library compatible with
 * the client scenario. An alternative is to use define HAVE_CONFIG_H and use
 * the auto generated file.
 *
 * The ownership has been taken into our codebase in order to be able to compile
 * for different flavors of linux/unix using -target in gcc.
 */

// Define to 1 if you have the `alarm' function.
#define HAVE_ALARM 1

// Define to 1 if you have `alloca', as a function or macro.
#define HAVE_ALLOCA 1

// Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
#define HAVE_ALLOCA_H 1

// Define to 1 if you have the <ctype.h> header file.
#define HAVE_CTYPE_H 1

// Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
#define HAVE_DIRENT_H 1

// Define to 1 if you have the <errno.h> header file.
#define HAVE_ERRNO_H 1

// Define to 1 if your system has a working POSIX `fnmatch' function.
#define HAVE_FNMATCH 1

// Define to 1 if you have the <fnmatch.h> header file.
#define HAVE_FNMATCH_H 1

// Define to 1 if fseeko (and presumably ftello) exists and is declared.
#define HAVE_FSEEKO 1

// Define to 1 if you have the <getopt.h> header file.
#define HAVE_GETOPT_H 1

// Define to 1 if you have the `getopt_long' function.
#define HAVE_GETOPT_LONG 1

// Define to 1 if you have the <inttypes.h> header file.
#define HAVE_INTTYPES_H 1

// Define to 1 if you have the <libintl.h> header file.
#define HAVE_LIBINTL_H 1

// Define to 1 if you have the <limits.h> header file.
#define HAVE_LIMITS_H 1

// Define to 1 if <wchar.h> declares mbstate_t.
#define HAVE_MBSTATE_T 1

// Define to 1 if you have the `memcpy' function.
#define HAVE_MEMCPY 1

// Define to 1 if you have the <memory.h> header file.
#define HAVE_MEMORY_H 1

// Define to 1 if you have the `mempcpy' function.
#define HAVE_MEMPCPY 1

// Define to 1 if you have the <stdarg.h> header file.
#define HAVE_STDARG_H 1

// Define to 1 if you have the <stdint.h> header file.
#define HAVE_STDINT_H 1

// Define to 1 if you have the <stdlib.h> header file.
#define HAVE_STDLIB_H 1

// Define to 1 if you have the `strcasecmp' function.
#define HAVE_STRCASECMP 1

// Define to 1 if you have the `strchr' function.
#define HAVE_STRCHR 1

// Define to 1 if you have the <strings.h> header file.
#define HAVE_STRINGS_H 1

// Define to 1 if you have the <string.h> header file.
#define HAVE_STRING_H 1

// Define to 1 if you have the <sys/stat.h> header file.
#define HAVE_SYS_STAT_H 1

// Define to 1 if you have the <sys/time.h> header file.
#define HAVE_SYS_TIME_H 1

// Define to 1 if you have the <sys/types.h> header file.
#define HAVE_SYS_TYPES_H 1

// Define to 1 if you have the <unistd.h> header file.
#define HAVE_UNISTD_H 1

// Define to 1 if you have the `utime' function.
#define HAVE_UTIME 1

// Define to 1 if you have the `utimes' function.
#define HAVE_UTIMES 1

// Define to 1 if you have the <utime.h> header file.
#define HAVE_UTIME_H 1

// Define to 1 if you have the <wchar.h> header file.
#define HAVE_WCHAR_H 1

// Define to 1 if you have the <wctype.h> header file.
#define HAVE_WCTYPE_H 1

/*
 * PACKAGE, PACKAGE_BUGREPORT, PACKAGE_NAME, PACKAGE_STRING, PACKAGE_TARNAME,
 * PACKAGE_VERSION and VERSION should not be defined here, since they will be in
 * conflict with open-vm-tools definitions.
 */

// Define to 1 if you have the ANSI C header files.
#define STDC_HEADERS 1

// Define to 1 if you can safely include both <sys/time.h> and <time.h>.
#define TIME_WITH_SYS_TIME 1

// Number of bits in a file offset, on hosts where this is settable.
#define _FILE_OFFSET_BITS 64

// Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2).
#define _LARGEFILE_SOURCE 1

07070100000216000081A400000000000000000000000168225505000029D8000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/mspackWrapper.c   /*********************************************************
 * Copyright (C) 2006-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * mspackWrapper.c --
 *
 *      Implementation of the mspack wrapper.
 */

#include "mspackWrapper.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <mspack.h>
#include <stdarg.h>
#include <errno.h>
#include "str.h"

/*
 * Template functions
 */

static void DefaultLog(int logLevel, const char* fmtstr, ...);

/*
 * String explanation for the error codes.
 * They are arranged in the same order as the corresponding error codes.
 */

static const char*  LINUXCAB_STRERR[] = {
                                        "Success.",
                                        "Unknown Error.",
                                        "Error extractinf file from cabinet.",
                                        "Error creating decompressor.",
                                        "Error opening cabinet file.",
                                        "Error seeking. Check config.h & \
                                         library compilation architecture.",
                                        "Error tyring to read the cabinet header."
                                        };

/*
 * Statics
 */

static LogFunction sLog = DefaultLog;

// .....................................................................................

/**
 *
 * Default logging mechanism to be used. Print to screen.
 *
 * @param   [in]  level    Log level
 * @param   [in]  fmtstr   Format to print the variables in
 * @param   [in]  ...      Variables to be printed
 *
 **/
static void
DefaultLog(int logLevel, const char* fmtstr, ...)
{
   va_list args;
   va_start(args, fmtstr);
   printf(fmtstr, args);
   va_end(args);
}


// .....................................................................................

/**
 *
 * Set the logging function.
 *
 * @param   [in]  log   Logging function to be used.
 * @returns None
 *
 **/
void
MspackWrapper_SetLogger(LogFunction log)
{
   sLog = log;
}

//......................................................................................

/**
 *
 * Sets up the path for exracting file. For e.g. if the file is /a/b/c/d.abc
 * then it creates /a/b/c (skips if any of the directories along the path
 * exists)
 *
 * @param path  IN: Complete path of the file
 * @return
 *  On Success  LINUXCAB_SUCCESS
 *  On Error    LINUXCAB_ERROR
 *
 **/
unsigned int
SetupPath (char* path) {
   char* token;

   // walk through the path (it employs in string replacement)
   for (token = path; *token; ++token) {
      //MS-DOC to unix path conversion
      if (*token == '\\') {
         *token = '/';
      }

      // ignore first / or subsequent characters that are not /
      if (token  == path) continue;
      if (*token != '/') continue;

      /*
       * cut it off here for e.g. on first iteration /a/b/c/d.abc will have
       * token /a, on second /a/b etc
       */
      *token = 0;

#ifdef VMX86_DEBUG
      sLog(log_debug, "Creating directory %s ", path);
#endif

      if (mkdir(path, 0777) == -1) {
         struct stat stats;
         // ignore if the directory exists
         if (!((stat(path, &stats) == 0) && S_ISDIR(stats.st_mode))) {
            sLog(log_error, "Unable to create directory %s (%s)", path,
                 strerror(errno));
            return LINUXCAB_ERROR;
         }
      }

      // restore the token
      *token = '/';
    }

    return LINUXCAB_SUCCESS;
}

//......................................................................................

/**
 *
 * Extract one given file.
 *
 * @param deflator      IN: Pointer to the cabinet decompressor
 * @param file          IN: Pointer to file under decompression
 * @param destDirector  IN: Destination directory
 * @return
 *  On Success    LINUXCAB_SUCCESS
 *  On Failure    LINUXCAB_ERROR
 *
 **/
static unsigned int
ExtractFile (struct mscab_decompressor* deflator,
             struct mscabd_file* file,
             const char* destDirectory)
{
   size_t sz;

   // copy it into a string as SetupPath will do an in place text manipulation
   char fileName[strlen(file->filename)+1];
   strcpy(fileName, file->filename);

   // add the target directory to the file path
   sz = strlen(destDirectory)+ 1 + strlen(fileName)+ 1;
   {
      char outCabFile[sz];
      Str_Sprintf (outCabFile, sz,  "%s/%s", destDirectory, fileName);

      // set up the path if it does not exist
      if (SetupPath(outCabFile) != LINUXCAB_SUCCESS) {
         return LINUXCAB_ERROR;
      }

      #ifdef VMX86_DEBUG
       sLog(log_info, "Extracting %s .... ", outCabFile );
      #endif

      // Extract File
      if (deflator->extract(deflator,file,outCabFile) != MSPACK_ERR_OK) {
         return LINUXCAB_ERR_EXTRACT;
      }
   }

   return LINUXCAB_SUCCESS;
}

//.............................................................................

/**
 *
 * Helper function for ExpandAllFilesInCab below.
 * Expands all files in the cabinet into the specified directory. Also returns
 * the command that is specified in the VMware defined header.
 *
 * @param cabFileName      IN:   Cabinet file name
 * @param destDirectory    IN:   Destination directory to uncab
 *
 * @return
 *  On success          LINUXCAB_SUCCESS
 *  On Error            LINUXCAB_ERROR, LINUXCAB_ERR_OPEN, LINUXCAB_ERR_DECOMPRESS,
 *                      LINUXCAB_EXTRACT, LINUXCAB_ERR_HEADER
 **/
static unsigned int
ExpandAllFilesInCabInt (const char* cabFileName,
                        const char* destDirectory)
{
   // set the state as success
   int returnState = LINUXCAB_SUCCESS;
   struct mscabd_cabinet* cab;
   struct mscabd_cabinet* cabToClose;

   // Create decompressor instance
   // Note : I am using NULL for reading file operations meaning requesting it
   // to use the default routines. It may be a good idea to write that up in the
   // future
   struct mscab_decompressor* deflator = mspack_create_cab_decompressor (NULL);

   // deflator error ?
   if (!deflator) {
      return LINUXCAB_ERR_DECOMPRESSOR;
   }

   // Search for the specified file
   cab = deflator->search (deflator, (char*)cabFileName);
   cabToClose = cab;

   // was the file found ?
   if (!cab) {
      return LINUXCAB_ERR_OPEN;
   }

   /*
    * Extract file by file
    * NOTE: open call cannot be used as cab can span multiple files. Hence
    * search call has to be used.
    */

   // Iterate through the cabinets.
   while(cab) {
      // Get all files in the cab
      struct mscabd_file* file = cab->files;

      // iterate through the files
      while(file) {
         returnState = ExtractFile(deflator, file, destDirectory);

         // error extracting ?
         if (returnState != LINUXCAB_SUCCESS) {
            break;
         }

         file = file->next;
      }

      // Break - if error
      if (returnState != LINUXCAB_SUCCESS) {
         break;
      }

#ifdef VMX86_DEBUG
      sLog(log_debug, "flag = %i ", cab->flags);
#endif

      // move to next cab file - in case of multi file cabs
      cab = cab->next;
   }

   deflator->close (deflator, cabToClose);

   // close & destroy instance - clean up
   mspack_destroy_cab_decompressor(deflator);

#ifdef VMX86_DEBUG
   sLog(log_info, "Done extracting files. ");
#endif

   return returnState;
}

//.............................................................................

/**
 *
 * Expands all files in the cabinet into the specified directory. Also returns
 * the command that is specified in the VMware defined header.
 * The mspack library uses and honors the current umask when setting the
 * file permission of the extracted files. Here we set the umask in a way
 * that protects them against world access.
 *
 * @param cabFileName      IN:   Cabinet file name
 * @param destDirectory    IN:   Destination directory to uncab
 *
 * @return
 *  On success          LINUXCAB_SUCCESS
 *  On Error            LINUXCAB_ERROR, LINUXCAB_ERR_OPEN,
 *                      LINUXCAB_ERR_DECOMPRESS,
 *                      LINUXCAB_EXTRACT, LINUXCAB_ERR_HEADER
 **/
unsigned int
ExpandAllFilesInCab (const char* cabFileName,
                     const char* destDirectory)
{
   unsigned int rc;
   mode_t oldMask;

   /* Turn off the world access permssion for all expanded files */
   oldMask = umask(0027);

   rc = ExpandAllFilesInCabInt(cabFileName, destDirectory);

   umask(oldMask);

   return rc;
}

//.............................................................................

/**
 *
 * Does a self check on the library parameters to make sure that the library
 * compilation is compatible with the client compilation. This is funny scenario
 * and is put in to support different flavours of UNIX operating systems.
 * Essentially the library checks of off_t size.
 *
 * @param         None
 * @return
 *  On Success    LINUXCAB_SUCCESS
 *  On Error      LINUXCAB_ERR_SEEK, LINUXCAB_ERROR
 *
 **/
unsigned int
SelfTestMspack(void)
{
   int error;
   MSPACK_SYS_SELFTEST(error);

   // test failed ?
   if (error) {
      if (error == MSPACK_ERR_SEEK) {
         /* Library has been compiled for a different bit version of
          * than the program that uses it. The other explanation
          * is inappropriate .h inclusion, can be solved using configure.h.
          * This is the most common issue, hence addressed specifically
          */
         return LINUXCAB_ERR_SEEK;
      } else {
         // Not a common error.
         return LINUXCAB_ERROR;
      }
   }

   // Perfectly compatible
   return LINUXCAB_SUCCESS;
}

//...........................................................................

/**
 *
 * Get a string error message for the given error code.
 *
 * @param   error  IN:  Error  number
 * @return  error as a string message
 *
 **/
const char*
GetLinuxCabErrorMsg ( const unsigned int error )
{
   return LINUXCAB_STRERR[error];
}


07070100000217000081A4000000000000000000000001682255050000100E000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/mspackWrapper.h   /*********************************************************
 * Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * mspackWrapper.h --
 *
 *      Definitions of the mspack wrapper.
 */

#ifndef _MSPACKWRAPPER_H_
#define _MSPACKWRAPPER_H_

/*
 * This include takes care of configuring the header files to suite the library
 * compilation.
 */

#include "mspackConfig.h"
#include "imgcust-common/log.h"

/*
 * The objective of this header file is to abstract the c99 standard 
 * of mspack.h which does not compile with C++ due to usage of keywords
 * like 'this' and 'class' in the code. "extern C" is more of a linker 
 * provision and does not handle this scenario.
 *
 * This header file abstracts away the mspack.h and exposes the same 
 * functionalities.
 *
 */

/*
 * Constant Declaration
 */

/* 
 * Error codes 
 */

#define LINUXCAB_SUCCESS 0          // Success
#define LINUXCAB_ERROR 1            // General error
#define LINUXCAB_ERR_EXTRACT 2      // Extraction error
#define LINUXCAB_ERR_DECOMPRESSOR 3 // Decompression error
#define LINUXCAB_ERR_OPEN 4         // Open error
#define LINUXCAB_ERR_SEEK 5         // Seek error 

// .....................................................................................

/**
 *
 * Set the logging function.
 *
 * @param   [in]  log   Logging function to be used.
 * @returns None
 *
 **/
void
MspackWrapper_SetLogger(LogFunction log);

//......................................................................................

/**
 * 
 * Expands all files in the cabinet into the specified directory. Also returns
 * the command that is specified in the VMware defined header.
 *
 * @param cabFileName      IN:   Cabinet file name
 * @param destDirectory    IN:   Destination directory to uncab
 *
 * @return
 *  On success          LINUXCAB_SUCCESS
 *  On Error            LINUXCAB_ERROR, LINUXCAB_ERR_OPEN, LINUXCAB_ERR_DECOMPRESS,
 *                      LINUXCAB_EXTRACT
 **/
unsigned int
ExpandAllFilesInCab(const char* cabFileName,    
                    const char* destDirectory);

//......................................................................................

/**
 * Does a self check on the library parameters to make sure that the library
 * compilation is compatible with the client compilation. This is funny scenario
 * and is put in to support different flavours of UNIX operating systems.
 * Essentially the library checks of off_t size.
 *
 * @param         None
 * @return  
 *  On Success    LINUXCAB_SUCCESS
 *  On Error      LINUXCAB_ERR_SEEK, LINUXCAB_ERROR
 *
 **/
unsigned int 
SelfTestMspack(void);

//......................................................................................

/**
 * 
 * Get a string error message for the given error code.
 *
 * @param   error  IN:  Error  number
 * @return  error as a string message
 * 
 **/
const char*
GetLinuxCabErrorMsg (const unsigned int error);

// .....................................................................................

/**
 * 
 * Sets up the path for exracting file. For e.g. if the file is /a/b/c/d.abc
 * then it creates /a/b/c (skips if any of the directories along the path
 * exists)
 *
 * @param path  IN: Complete path of the file
 * @return
 *  On Success  LINUXCAB_SUCCESS
 *  On Error    LINUXCAB_ERROR
 *  
 **/
unsigned int
SetupPath (char* path);

#endif

  07070100000218000081A4000000000000000000000001682255050000328C000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/libDeployPkg/processPosix.c    /*********************************************************
 * Copyright (c) 2007-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * processPosix.c --
 *
 *      Implementation of the POSIX process wrapper.
 */

#include "imgcust-common/log.h"
#include "imgcust-common/process.h"
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdlib.h>

#include "util.h"

typedef struct _ProcessInternal {
   pid_t pid;
   int stdoutFd;
   int stderrFd;
   char* stdoutStr;
   char* stderrStr;
   int exitCode;
   char** args;
   LogFunction log;
} ProcessInternal;

typedef enum _ReadStatus {
   READSTATUS_UNDEFINED,
   READSTATUS_DONE,
   READSTATUS_PENDING,
   READSTATUS_WAITING_EOF,
   READSTATUS_ERROR
} ReadStatus;

static void
ProcessRead(ProcessInternal *p, ReadStatus *status, Bool out, Bool readToEof);


/*
 *------------------------------------------------------------------------------
 *
 * Process_Create --
 *
 *      Create and initialize a process object.
 *
 *------------------------------------------------------------------------------
 */

ProcessError
Process_Create(ProcessHandle *h, char *args[], void *logPtr)
{
   int i, numArgs;
   int err = -1;
   ProcessInternal *p;
   LogFunction log = (LogFunction)logPtr;
   log(log_info, "sizeof ProcessInternal is %d", sizeof(ProcessInternal));
   p = (ProcessInternal*) calloc(1, sizeof(ProcessInternal));
   if (p == NULL) {
      log(log_error, "Error allocating memory for process");
      goto error;
   }
   p->stdoutStr = malloc(sizeof(char));
   if (p->stdoutStr == NULL) {
      log(log_error, "Error allocating memory for process stdout");
      goto error;
   }
   p->stdoutStr[0] = '\0';
   p->stderrStr = malloc(sizeof(char));
   if (p->stderrStr == NULL) {
      log(log_error, "Error allocating memory for process stderr");
      goto error;
   }
   p->stderrStr[0] = '\0';

   p->stdoutFd = -1;
   p->stderrFd = -1;

   numArgs = 0;
   while (args[numArgs] != NULL) {
      numArgs++;
   }

   p->args = malloc((1 + numArgs) * sizeof(char*));
   if (p->args == NULL) {
      log(log_error, "Error allocating memory for process args");
      goto error;
   }
   for (i = 0; i < numArgs; i++) {
      p->args[i] = strdup(args[i]);
      if (p->args[i] == NULL) {
         log(log_error, "Error allocating memory for duplicate args");
         goto error;
      }
   }
   p->args[numArgs] = NULL;
   p->log = log;
   *h = (ProcessHandle)p;
   err = 0;

error:
   if (err != 0) {
      if (p != NULL) {
         Process_Destroy((ProcessHandle)p);
      }
      exit(1);
   }
   return PROCESS_SUCCESS;
}


/*
 *------------------------------------------------------------------------------
 *
 * Process_RunToComplete --
 *
 *      Runs a process until complete, collecting stdout and stderr into the
 *      process object.
 *
 *------------------------------------------------------------------------------
 */

ProcessError
Process_RunToComplete(ProcessHandle h, unsigned long timeoutSec)
{
   int stdout[2];
   int stderr[2];
   int flags;
   ProcessInternal* p;
   // poll for the process to complete and read the output
   const unsigned int OneSecMicroSec = 1000000;
   const unsigned int LoopSleepMicrosec = OneSecMicroSec / 10;
   const unsigned long timeoutLoopSleeps =
      timeoutSec * (OneSecMicroSec / LoopSleepMicrosec);
   unsigned long elapsedTimeLoopSleeps;

   ReadStatus res_stdout = READSTATUS_UNDEFINED;
   ReadStatus res_stderr = READSTATUS_UNDEFINED;

   Bool processExitedAbnormally = FALSE;

   p = (ProcessInternal*)h;

   stdout[0] = stdout[1] = 0;
   if (pipe(stdout) < 0) {
      p->log(log_error, "Failed to create pipe for stdout:%s", strerror(errno));
      return PROCESS_FAILED;
   }

   stderr[0] = stderr[1] = 0;
   if (pipe(stderr) < 0) {
      p->log(log_error, "Failed to create pipe for stderr,%s", strerror(errno));
      close(stdout[0]);
      close(stdout[1]);
      return PROCESS_FAILED;
   }

   p->pid = fork();
   if (p->pid == -1) {
      p->log(log_error, "Failed to fork: %s", strerror(errno));
      close(stdout[0]);
      close(stdout[1]);
      close(stderr[0]);
      close(stderr[1]);
      return PROCESS_FAILED;
   } else if (p->pid == 0) {
      // we're in the child. close the read ends of the pipes and exec
      close(stdout[0]);
      close(stderr[0]);
      dup2(stdout[1], STDOUT_FILENO);
      dup2(stderr[1], STDERR_FILENO);
      execv(p->args[0], p->args);
      p->log(log_error, "execv failed to run (%s), errno=(%d), "
             "error message:(%s)", p->args[0], errno, strerror(errno));

      // exec failed
      close(stdout[1]);
      close(stderr[1]);
      exit(127);
   }

   // close write ends of pipes and make reads nonblocking
   close(stdout[1]);
   close(stderr[1]);

   p->stdoutFd = stdout[0];
   flags = fcntl(p->stdoutFd, F_GETFL);
   if (fcntl(p->stdoutFd, F_SETFL, flags | O_NONBLOCK) == -1) {
      p->log(log_warning, "Failed to set stdoutFd status flags, (%s)",
             strerror(errno));
   }

   p->stderrFd = stderr[0];
   flags = fcntl(p->stderrFd, F_GETFL);
   if (fcntl(p->stderrFd, F_SETFL, flags | O_NONBLOCK) == -1) {
      p->log(log_warning, "Failed to set stderrFd status flags, (%s)",
             strerror(errno));
   }

   elapsedTimeLoopSleeps = 0;

   while (1) {
      int processStatus;
      int processFinished = (waitpid(p->pid, &processStatus, WNOHANG) > 0);

      if (processFinished) {
         if (WIFEXITED(processStatus)) {
            p->exitCode = WEXITSTATUS(processStatus);
            p->log(log_info,
                   "Process exited normally after %d seconds, returned %d",
                   elapsedTimeLoopSleeps * LoopSleepMicrosec / OneSecMicroSec,
                   p->exitCode);
         } else if (WIFSIGNALED(processStatus)) {
            p->exitCode = 127;
            p->log(log_error,
                   "Process exited abnormally after %d sec, uncaught signal %d",
                   elapsedTimeLoopSleeps * LoopSleepMicrosec / OneSecMicroSec,
                   WTERMSIG(processStatus));
            processExitedAbnormally = TRUE;
         }

         break;
      } else {
         if (timeoutLoopSleeps == elapsedTimeLoopSleeps) {
            p->log(log_error, "Timed out waiting for process exit, canceling...");
            kill(p->pid, SIGKILL);
         }

         // Empty the pipes.
         ProcessRead(p, &res_stdout, TRUE, FALSE);
         if (res_stdout == READSTATUS_ERROR) {
            p->log(log_error, "Error while reading process output, canceling...");
            kill(p->pid, SIGKILL);
         }

         ProcessRead(p, &res_stderr, FALSE, FALSE);
         if (res_stderr == READSTATUS_ERROR) {
            p->log(log_error, "Error while reading process output, canceling...");
            kill(p->pid, SIGKILL);
         }

         usleep(LoopSleepMicrosec);
         elapsedTimeLoopSleeps++;
      }
   }

   // Process completed. Now read all the output to EOF.
   // PR 2367614, set readToEof to TRUE only if process exits normally.
   // Otherwise just empty the pipe to avoid being blocked by read operation.
   ProcessRead(p, &res_stdout, TRUE, !processExitedAbnormally);
   if (res_stdout == READSTATUS_ERROR) {
      p->log(log_error, "Error while reading process stdout, canceling...");
   }

   ProcessRead(p, &res_stderr, FALSE, !processExitedAbnormally);
   if (res_stderr == READSTATUS_ERROR) {
      p->log(log_error, "Error while reading process stderr, canceling...");
   }

   close(stdout[0]);
   close(stderr[0]);
   return PROCESS_SUCCESS;
}


/*
 *------------------------------------------------------------------------------
 *
 * ProcessRead --
 *
 *      Read redirected stdout or stderr.
 *
 * status - as IN  - holds the result from previous read operation.
 *          as OUT - returns the status from the read operation.
 *
 * There are two modes:
 * readToEof = TRUE - block until the read returns 0 (EOF). This signifies
 *                    that the process has exited and there's nothing left.
 * readToEof = FALSE- Just empty the pipe. This will return even if we get
 *             EAGAIN back from the read. Use this in the midst of the poll
 *             loop so the pipe doesn't fill up and block the process.
 *
 *------------------------------------------------------------------------------
 */

static void
ProcessRead(ProcessInternal *p, ReadStatus *status, Bool stdout, Bool readToEof)
{
   char buf[1024];
   size_t currSize, newSize;
   char** saveTo;
   int fd;
   char* stdstr = stdout ? "stdout" : "stderr";

   // which fd do we read and which pointer do we save to?
   fd = stdout ? p->stdoutFd : p->stderrFd;
   saveTo = stdout ? &p->stdoutStr : &p->stderrStr;

   // if there's output waiting, read it out. FDs should already be non-blocking
   do {
      ssize_t count = read(fd, buf, sizeof buf);

      if (count > 0) {
         // save output
         currSize = strlen(*saveTo);
         newSize = count + currSize;
         *saveTo = Util_SafeRealloc(*saveTo, newSize + 1);
         memcpy(*saveTo + currSize, buf, count);
         (*saveTo)[newSize] = '\0';
         p->log(log_info, "Saving output from %s", stdstr);
      } else if (count == 0) {
         if (*status != READSTATUS_DONE) {
            // we're done
            p->log(log_info, "No more output from %s", stdstr);
            *status = READSTATUS_DONE;
         }

         return;
      } else if (count < 0) {
         if (errno == EAGAIN && readToEof) {
            if (*status != READSTATUS_WAITING_EOF) {
               // waiting for more output, sleep briefly and try again
               p->log(log_info, "Pending output from %s till EOF, trying again",
                  stdstr);
               *status = READSTATUS_WAITING_EOF;
            }

            usleep(1000);
         } else if (errno == EAGAIN && !readToEof) {
            if (*status != READSTATUS_PENDING) {
               // caller doesn't want to wait until EOF
               p->log(log_info, "Returning, pending output from %s", stdstr);
               *status = READSTATUS_PENDING;
            }

            return;
         } else {
            // error
            p->log(log_error, "Failed to read from %s: %s",
                   stdstr, strerror(errno));

            *status = READSTATUS_ERROR;
            return;
         }
      }
   } while (1);
}


/*
 *------------------------------------------------------------------------------
 *
 * Process_GetStdout --
 *
 *      Returns process's standard output.
 *
 *------------------------------------------------------------------------------
 */

const char *
Process_GetStdout(ProcessHandle h)
{
   ProcessInternal *p = (ProcessInternal *)h;
   return p->stdoutStr;
}


/*
 *------------------------------------------------------------------------------
 *
 * Process_GetStderr --
 *
 *      Returns process's standard error output.
 *
 *------------------------------------------------------------------------------
 */

const char *
Process_GetStderr(ProcessHandle h)
{
   ProcessInternal *p = (ProcessInternal *)h;
   return p->stderrStr;
}


/*
 *------------------------------------------------------------------------------
 *
 * Process_GetExitCode --
 *
 *      Returns process's exit code.
 *
 *------------------------------------------------------------------------------
 */

int
Process_GetExitCode(ProcessHandle h)
{
   ProcessInternal *p = (ProcessInternal *)h;
   return p->exitCode;
}


/*
 *------------------------------------------------------------------------------
 *
 * Process_Destroy --
 *
 *      Destroys the process and returns result of the operation.
 *
 *------------------------------------------------------------------------------
 */

ProcessError
Process_Destroy(ProcessHandle h)
{
   ProcessInternal* p;
   p = (ProcessInternal*)h;
   if (p->stdoutFd >= 0) {
      close(p->stdoutFd);
   }
   if (p->stderrFd >= 0) {
      close(p->stderrFd);
   }
   free(p->stdoutStr);
   free(p->stderrStr);
   if (p->args != NULL) {
      int i;
      for (i = 0; p->args[i] != NULL; i++) {
         free(p->args[i]);
      }
      free(p->args);
   }
   free(p);
   return PROCESS_SUCCESS;
}
07070100000219000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/libappmonitor  0707010000021A000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/libappmonitor/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   0707010000021B000081A40000000000000000000000016822550500000BB3000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/libappmonitor/Makefile.am  ################################################################################
### Copyright (C) 2018, 2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

lib_LTLIBRARIES = libappmonitor.la

AM_CFLAGS =
AM_CFLAGS += -I$(top_srcdir)/lib/include

libappmonitor_la_LIBADD =
libappmonitor_la_LIBADD += ../lib/backdoor/libBackdoor.la
libappmonitor_la_LIBADD += ../lib/dataMap/libDataMap.la
libappmonitor_la_LIBADD += ../lib/err/libErr.la
libappmonitor_la_LIBADD += ../lib/hashMap/libHashMap.la
libappmonitor_la_LIBADD += ../lib/message/libMessage.la
libappmonitor_la_LIBADD += ../lib/misc/libMisc.la
libappmonitor_la_LIBADD += ../lib/rpcOut/libRpcOut.la
libappmonitor_la_LIBADD += ../lib/string/libString.la
libappmonitor_la_LIBADD += ../lib/unicode/libUnicode.la
libappmonitor_la_LIBADD += ../lib/vmCheck/libVmCheck.la
libappmonitor_la_LIBADD += ../lib/vmSignal/libVmSignal.la
libappmonitor_la_LIBADD += @ICU_LIBS@

libappmonitor_rpcchanneldir = $(top_srcdir)/lib/rpcChannel

libappmonitor_la_SOURCES =
libappmonitor_la_SOURCES += vmGuestAppMonitorLib.c
libappmonitor_la_SOURCES += stub-debug.c
libappmonitor_la_SOURCES += $(top_srcdir)/lib/stubs/stub-panic.c
libappmonitor_la_SOURCES += $(libappmonitor_rpcchanneldir)/bdoorChannel.c
libappmonitor_la_SOURCES += $(libappmonitor_rpcchanneldir)/rpcChannel.c
libappmonitor_la_SOURCES += $(libappmonitor_rpcchanneldir)/glib_stubs.c
if HAVE_VSOCK
libappmonitor_la_SOURCES += $(libappmonitor_rpcchanneldir)/vsockChannel.c
libappmonitor_la_SOURCES += $(libappmonitor_rpcchanneldir)/simpleSocket.c
endif

libappmonitor_la_LIBADD += -ldl -lrt
# We require GCC, so we're fine passing compiler-specific flags.
# Needed for OS's that don't link shared libraries against libc by default, e.g. FreeBSD
libappmonitor_la_LIBADD += -lc

libappmonitor_includedir = $(includedir)/libappmonitor

libappmonitor_include_HEADERS =
libappmonitor_include_HEADERS += $(top_srcdir)/lib/include/vmGuestAppMonitorLib.h

libappmonitor_la_CPPFLAGS =
libappmonitor_la_CPPFLAGS += -DVMTOOLS_USE_GLIB
libappmonitor_la_CPPFLAGS += -DUSE_RPCI_ONLY
libappmonitor_la_CPPFLAGS += @GLIB2_CPPFLAGS@

EXTRA_DIST = appmonitor.pc.in

pkgconfigdir   = $(libdir)/pkgconfig
pkgconfig_DATA = appmonitor.pc

$(pkgconfig_DATA): $(top_builddir)/config.status

 0707010000021C000081A40000000000000000000000016822550500000178000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/libappmonitor/appmonitor.pc.in # VMware AppMonitorLib pkgconfig data.

prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@

Name:          VMware AppMonitorLib
Description:   Library for guest app agents to communicate with vSphere HA about the guest app's liveness
Version:       @VERSION@
Libs:          -L${libdir} -lappmonitor
Cflags:        -I${includedir}/libappmonitor

0707010000021D000081A40000000000000000000000016822550500000602000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/libappmonitor/stub-debug.c /*********************************************************
 * Copyright (C) 2013 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * stub-debug.c --
 *
 *   Stub for Debug().
 *
 */

#include <stdio.h>
#include "str.h"


void
Debug(const char *fmt, ...)
{
#ifdef VMX86_LOG /* only do logging on OBJ builds */
   va_list args;

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);
#endif
}


void
Log(const char *fmt, ...)
{
#ifdef VMX86_LOG /* only do logging on OBJ builds */
   va_list args;

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);
#endif
}


void
Warning(const char *fmt, ...)
{
#ifdef VMX86_LOG /* only do logging on OBJ builds */
   va_list args;

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);
#endif
}

  0707010000021E000081A400000000000000000000000168225505000032CA000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/libappmonitor/vmGuestAppMonitorLib.c   /*********************************************************
 * Copyright (C) 2009-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmGuestAppMonitorLib.c
 *
 * @brief  This file contains the functions used by an application monitoring
 *         agent to communicate to HA via VMX that the application(s) that it
 *         is monitoring is alive. The general flow is as follows:
 *
 *  @code
 *  VMGuestAppMonitor_Enable();
 *
 *  -- Call at least every 30 seconds
 *  VMGuestAppMonitor_MarkActive();
 *
 *  -- When finished monitoring
 *  VMGuestAppMonitor_Disable();
 *  @endcode
 *
 *  To signal an application failure, simply do not call
 *  VMGuestAppMonitor_MarkActive().
 *
 *  @endcode
 *
 *  @addtogroup VMGuestAppMonitor
 *  @{
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "vmware.h"
#include "str.h"
#include "util.h"
#include "vmGuestAppMonitorLib.h"
#include "vmGuestAppMonitorLibInt.h"
#include "rpcout.h"
#include "vmcheck.h"
#include "debug.h"
#include "vmguestappmonitorlib_version.h"
#include "vm_version.h"
#include "embed_version.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/guestrpc.h"

VM_EMBED_VERSION(VMGUESTAPPMONITORLIB_VERSION_STRING);

/**
 * @cond INTERNAL
 * @{
 */

static VMGuestAppMonitorLibError
RunGuestAppMonitorCmd(const char *cmd);

static VMGuestAppMonitorLibError
RunGuestAppMonitorCmdWithResult(const char *cmd,
                                char **result);

static Bool
CreateSecRpcChannel();

static void
DestroySecRpcChannel();

static void
DestroyChannel();

#if defined(VMX86_DEBUG) && defined(__linux__)
static void
LogChannelType(const char *filePath, const char *chanType);
#endif

static RpcChannel *gChan = NULL;

static Bool isHeartbeatingOnly = FALSE;

/**
 * @}
 * @endcond
 */

/*
 ******************************************************************************
 * VMGuestAppMonitor_Enable --
 ******************************************************************************
 */

VMGuestAppMonitorLibError
VMGuestAppMonitor_Enable(void)
{
   VMGuestAppMonitorLibError rc;
   isHeartbeatingOnly = TRUE;
   rc = RunGuestAppMonitorCmd(VMGUESTAPPMONITOR_BD_CMD_ENABLE);
   return rc;
}


/*
 ******************************************************************************
 * VMGuestAppMonitor_Disable --
 ******************************************************************************
 */

VMGuestAppMonitorLibError
VMGuestAppMonitor_Disable(void)
{
   VMGuestAppMonitorLibError rc;
   rc = RunGuestAppMonitorCmd(VMGUESTAPPMONITOR_BD_CMD_DISABLE);
   if (rc == VMGUESTAPPMONITORLIB_ERROR_SUCCESS) {
      DestroySecRpcChannel();
      gChan = NULL;
      Debug("Destroyed the secure rpc channel.\n");
      isHeartbeatingOnly = FALSE;
   }
   return rc;
}


/*
 ******************************************************************************
 * VMGuestAppMonitor_IsEnabled --
 ******************************************************************************
 */

int
VMGuestAppMonitor_IsEnabled(void)
{
   VMGuestAppMonitorLibError rc;
   char *status = NULL;

   rc = RunGuestAppMonitorCmdWithResult(VMGUESTAPPMONITOR_BD_CMD_IS_ENABLED,
                                        &status);

   if (rc == VMGUESTAPPMONITORLIB_ERROR_SUCCESS && status != NULL) {
      Bool isEnabled = Str_Strncmp(status, "true", sizeof("true")) == 0;

      free(status);
      return isEnabled;
   } else {
      return FALSE;
   }
}


/*
 ******************************************************************************
 * VMGuestAppMonitor_MarkActive --
 ******************************************************************************
 */

VMGuestAppMonitorLibError
VMGuestAppMonitor_MarkActive(void)
{
   return RunGuestAppMonitorCmd(VMGUESTAPPMONITOR_BD_CMD_MARK_ACTIVE);
}


/*
 ******************************************************************************
 * VMGuestAppMonitor_GetAppStatus --
 ******************************************************************************
 */

char *
VMGuestAppMonitor_GetAppStatus(void)
{
   VMGuestAppMonitorLibError rc;
   char *status = NULL;

   rc = RunGuestAppMonitorCmdWithResult(VMGUESTAPPMONITOR_BD_CMD_GET_APP_STATUS,
                                        &status);

   return status;
}

/*
 ******************************************************************************
 * VMGuestAppMonitor_PostAppState --
 ******************************************************************************
 */
VMGuestAppMonitorLibError
VMGuestAppMonitor_PostAppState(const char *state)
{
   char *cmd = Str_Asprintf(NULL, "%s %s",
                            VMGUESTAPPMONITOR_BD_CMD_POST_APP_STATE,
                            state);
   VMGuestAppMonitorLibError rc;

   rc = RunGuestAppMonitorCmd(cmd);
   free(cmd);

   return rc;
}

/*
 ******************************************************************************
 * VMGuestAppMonitor_Free --                                            */ /**
 ******************************************************************************
 */

void
VMGuestAppMonitor_Free(char *str)
{
   if (str != NULL) {
      free(str);
   }
}

/*
 ******************************************************************************
 * DestroyChannel --                                             */ /**
 *
 * Destroy the channel if conditions are met.
 * Do not destroy the channel otherwise.
 *
 ******************************************************************************
 */
static void
DestroyChannel()
{
   Bool isBackdoorChannel;
  /*
   * Destroying the channel here if a client is using anything other
   * than markActive. Channels using markActive need to be persisted
   * since we want to prevent any false VM resets triggered by a client
   * not being able to post heartbeats because it was not able to
   * acquire a channel. We also close and destroy all backdoor channels
   * irrespective of whether they are being used for markActive or not.
   */
   isBackdoorChannel = (RpcChannel_GetType(gChan) == RPCCHANNEL_TYPE_BKDOOR);
   Debug("isBackdoorChannel is set to %d.\n", isBackdoorChannel);
   Debug("isHeartbeatingOnly is set to %d.\n", isHeartbeatingOnly);
   if(!isHeartbeatingOnly || isBackdoorChannel) {
      DestroySecRpcChannel();
      gChan = NULL;
      Debug("Destroyed the secure rpc channel.\n");
   }
}

/*
 ******************************************************************************
 * CreateSecRpcChannel --                                             */ /**
 *
 * Create a new secure RpcChannel if not already created.
 *
 * @return  TRUE if successful or FALSE otherwise.
 *
 ******************************************************************************
 */
static Bool
CreateSecRpcChannel()
{
   Bool start;
   if(gChan == NULL) {
      Debug("VMGuestAppMonitor: Creating a new Secure Rpc channel.\n");
      gChan = RpcChannel_New();
      start = RpcChannel_Start(gChan);
      /*
       * Logging the channel type so we can verify the same via the
       * VMAppmon unit tests. Changing this log message/file path
       * will break the unit tests.
       */
      #if defined(VMX86_DEBUG) && defined(__linux__)
      LogChannelType("/tmp/chanType.txt", (RpcChannel_GetType(gChan) == RPCCHANNEL_TYPE_BKDOOR ? "BACKDOOR" : "VSOCK"));
      #endif
      return start;
   }
   else {
      Debug("VMGuestAppMonitor: Secure Rpc channel already present.\n");
      return TRUE;
   }
}

/*
 ******************************************************************************
 * DestroySecRpcChannel --                                               */ /**
 *
 * Destroy a secure RpcChannel only if it has been created.
 *
 ******************************************************************************
 */
static void
DestroySecRpcChannel()
{
   if(gChan != NULL) {
      RpcChannel_Destroy(gChan);
   }
}

#if defined(VMX86_DEBUG) && defined(__linux__)
/*
 ******************************************************************************
 * LogChannelType --                                               */ /**
 *
 * Open a file on linux and log the channel type.
 *
 ******************************************************************************
 */
static void
LogChannelType(const char *filePath, const char *chanType)
{
   FILE *fp = fopen(filePath, "wb");
   if (fp != NULL)
   {
      fputs(chanType, fp);
      fclose(fp);
   }
}
#endif

/*
 ******************************************************************************
 * RunGuestAppMonitorCmd --                                             */ /**
 *
 * Execute a Guest App Monitoring RPC.
 *
 * @param[in]  cmd     The RPC command.
 *
 * @return  VMGUESTAPPMONITORLIB_ERROR_SUCCESS if successful or
 *          the error code otherwise.
 *
 ******************************************************************************
 */
static VMGuestAppMonitorLibError
RunGuestAppMonitorCmd(const char *cmd)
{
   char *reply = NULL;
   size_t replyLen;
   VMGuestAppMonitorLibError rc;

   ASSERT(cmd != NULL);

   if (!VmCheck_IsVirtualWorld()) {
      Debug("VMGuestAppMonitor: Not running in a VM.\n");
      return VMGUESTAPPMONITORLIB_ERROR_NOT_RUNNING_IN_VM;
   }

   //Init channel here if not already init
   if (!CreateSecRpcChannel()) {
      Debug("Error starting the Rpc Channel\n");
      return VMGUESTAPPMONITORLIB_ERROR_NOT_ENABLED;
   }
   Debug("VMGuestAppMonitor: Sending via secure Rpc channel.\n");
   if (!RpcChannel_Send(gChan, cmd, strlen(cmd), &reply, &replyLen)) {
      Debug("Failed to run %s command: %s\n", cmd, reply ? reply : "NULL");

      if (Str_Strncmp(reply, RPCI_UNKNOWN_COMMAND,
                      sizeof RPCI_UNKNOWN_COMMAND) == 0) {
         /* Host does not support application monitoring */
         rc = VMGUESTAPPMONITORLIB_ERROR_NOT_SUPPORTED;
      } else {
         rc = VMGUESTAPPMONITORLIB_ERROR_OTHER;
      }
   } else {
      if (Str_Strncmp(reply, VMGUESTAPPMONITOR_BD_RC_OK,
                      sizeof VMGUESTAPPMONITOR_BD_RC_OK) == 0) {
         rc = VMGUESTAPPMONITORLIB_ERROR_SUCCESS;
      } else {
         rc = VMGUESTAPPMONITORLIB_ERROR_OTHER;
      }
   }

   if (reply) {
      free(reply);
   }

   DestroyChannel();

   return rc;
}


/*
 ******************************************************************************
 * RunGuestAppMonitorCmdWithResult --                                   */ /**
 *
 * Execute a Guest App Monitoring RPC and return the command's result.
 *
 * @param[in]   cmd     The RPC command.
 * @param[out]  result  The result returned by the command or NULL if an
 *                      error occurred..
 *
 * @return  VMGUESTAPPMONITORLIB_ERROR_SUCCESS if successful or
 *          the error code otherwise.
 *
 ******************************************************************************
 */

static VMGuestAppMonitorLibError
RunGuestAppMonitorCmdWithResult(const char *cmd,
                                char **result)
{
   char *reply = NULL;
   size_t replyLen;
   VMGuestAppMonitorLibError rc;

   ASSERT(cmd != NULL);
   ASSERT(result != NULL);

   if (!VmCheck_IsVirtualWorld()) {
      Debug("VMGuestAppMonitor: Not running in a VM.\n");
      return VMGUESTAPPMONITORLIB_ERROR_NOT_RUNNING_IN_VM;
   }

   //Init channel here if not already init
   if (!CreateSecRpcChannel()) {
      Debug("Error starting the Rpc Channel\n");
      return VMGUESTAPPMONITORLIB_ERROR_NOT_ENABLED;
   }
   Debug("VMGuestAppMonitor: Sending via secure Rpc channel.\n");
   if (!RpcChannel_Send(gChan, cmd, strlen(cmd), &reply, &replyLen)) {
      Debug("Failed to run %s command: %s\n", cmd,  reply ? reply : "NULL");

      if (Str_Strncmp(reply, RPCI_UNKNOWN_COMMAND,
                      sizeof RPCI_UNKNOWN_COMMAND) == 0) {
         /* Host does not support application monitoring */
         rc = VMGUESTAPPMONITORLIB_ERROR_NOT_SUPPORTED;
      } else {
         rc = VMGUESTAPPMONITORLIB_ERROR_OTHER;
      }
   } else {
      Debug("Ran %s command, Reply is %s\n", cmd, reply ? reply : "NULL");
      rc = VMGUESTAPPMONITORLIB_ERROR_SUCCESS;
      if (replyLen > 0) {
         *result = Util_SafeMalloc(replyLen + 1);
         Str_Strcpy(*result, reply, replyLen + 1);
      } else {
         *result = NULL;
      }
   }

   if (reply) {
      free(reply);
   }

   DestroyChannel();

   return rc;
}


/**
 * @}
 */
  0707010000021F000081A400000000000000000000000168225505000007BE000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/libappmonitor/vmGuestAppMonitorLibInt.h    /*********************************************************
 * Copyright (C) 2009-2016,2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VM_GUEST_APP_MONITOR_LIB_INT_H_
#define _VM_GUEST_APP_MONITOR_LIB_INT_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "vmware.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Backdoor command
 */

#define VMGUESTAPPMONITOR_BD_CMD_ENABLE "GuestAppMonitor.Cmd.Enable"
#define VMGUESTAPPMONITOR_BD_CMD_DISABLE "GuestAppMonitor.Cmd.Disable"
#define VMGUESTAPPMONITOR_BD_CMD_IS_ENABLED "GuestAppMonitor.Cmd.IsEnabled"
#define VMGUESTAPPMONITOR_BD_CMD_MARK_ACTIVE "GuestAppMonitor.Cmd.MarkActive"
#define VMGUESTAPPMONITOR_BD_CMD_GET_APP_STATUS \
   "GuestAppMonitor.Cmd.GetAppStatus"
#define VMGUESTAPPMONITOR_BD_CMD_POST_APP_STATE \
   "GuestAppMonitor.Cmd.PostAppState"


#define VMGUESTAPPMONITOR_BD_RC_OK "0"
#define VMGUESTAPPMONITOR_BD_RC_INTERNAL_ERROR "1"
#define VMGUESTAPPMONITOR_BD_RC_NOT_ENABLED "2"
#define VMGUESTAPPMONITOR_BD_RC_ARGUMENT_EXPECTED "3"
#define VMGUESTAPPMONITOR_BD_RC_NULL_ARGUMENT "4"
#define VMGUESTAPPMONITOR_BD_RC_BAD_ARGUMENT "5"

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _VM_GUEST_APP_MONITOR_LIB_INT_H_ */
  07070100000220000081A400000000000000000000000168225505000004F3000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/libappmonitor/vmguestappmonitorlib_version.h   /*********************************************************
 * Copyright (C) 2009 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmguestappmonitorlib_version.h --
 *
 * Version definitions for VMGuestAppMonitorLib.
 */

#ifndef _VMGUESTAPPMONITORLIB_VERSION_H_
#define _VMGUESTAPPMONITORLIB_VERSION_H_

/*
 * This component's version is manually versioned.
 */

#define VMGUESTAPPMONITORLIB_VERSION_COMMAS   2,0,0
#define VMGUESTAPPMONITORLIB_VERSION_STRING   "2.0.0"

#endif /* _VMGUESTAPPMONITORLIB_VERSION_H_ */
 07070100000221000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/libguestStoreClient    07070100000222000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/libguestStoreClient/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000223000081A4000000000000000000000001682255050000066C000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/libguestStoreClient/Makefile.am    ################################################################################
### Copyright (c) 2020, 2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

lib_LTLIBRARIES = libguestStoreClient.la

libguestStoreClient_la_LIBADD =
libguestStoreClient_la_LIBADD += ../lib/err/libErr.la
libguestStoreClient_la_LIBADD += ../lib/string/libString.la
libguestStoreClient_la_LIBADD += ../lib/unicode/libUnicode.la
libguestStoreClient_la_LIBADD += ../lib/misc/libMisc.la
libguestStoreClient_la_LIBADD += -lpthread
libguestStoreClient_la_LIBADD += -ldl

libguestStoreClient_la_SOURCES =
libguestStoreClient_la_SOURCES += guestStoreClientLib.c

libguestStoreClient_la_LDFLAGS =
# We require GCC, so we're fine passing compiler-specific flags.
libguestStoreClient_la_LDFLAGS += -Wl,-z,defs
# Needed for OS's that don't link shared libraries against libc by
#default, e.g. FreeBSD
libguestStoreClient_la_LIBADD += -lc
07070100000224000081A40000000000000000000000016822550500006D0C000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/libguestStoreClient/guestStoreClientLib.c  /*********************************************************
 * Copyright (c) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file guestStoreClientLib.c
 *
 * GuestStore client library, connecting to vmtoolsd GuestStore plugin.
 *
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include<stdarg.h>
#include <errno.h>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
/*
 * #define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
 */
#define TLS_INDEX_TYPE  DWORD
#else
#define _GNU_SOURCE  // For struct ucred
#define __USE_GNU    // For struct ucred (glibc 2.17)
#include <sys/socket.h>
#undef __USE_GNU
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <pthread.h>
/*
 * typedef unsigned int pthread_key_t;
 * #define PTHREAD_KEYS_MAX	1024
 */
#define TLS_INDEX_TYPE  pthread_key_t
#define TLS_OUT_OF_INDEXES  ((pthread_key_t)-1)
#endif

#include "guestStoreClientLibInt.h"

#include "vm_version.h"
#include "embed_version.h"
#include "gueststoreclientlib_version.h"
VM_EMBED_VERSION(GUESTSTORECLIENTLIB_VERSION_STRING);

#define GSLIBLOG_TAG      "[guestStoreClientLib] "
#define GSLIBLOG_TAG_LEN  (sizeof(GSLIBLOG_TAG) - 1)


/*
 * Library Init/DeInit reference count.
 */
static Atomic_uint32 initLibCount = { 0 };

/*
 * Thread local storage index for CallCtx pointer.
 */
static TLS_INDEX_TYPE callCtxTlsIndex = TLS_OUT_OF_INDEXES;


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreAllocTls --
 *
 *      Allocate a thread local storage index for CallCtx pointer.
 *
 * Results:
 *      GSLIBERR_SUCCESS or GSLIBERR_TLS.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreAllocTls(void)
{
   ASSERT(callCtxTlsIndex == TLS_OUT_OF_INDEXES);

#ifdef _WIN32
   callCtxTlsIndex = TlsAlloc();
#else
   if (pthread_key_create(&callCtxTlsIndex, NULL) != 0) {
      callCtxTlsIndex = TLS_OUT_OF_INDEXES;
   }
#endif

   return (callCtxTlsIndex == TLS_OUT_OF_INDEXES) ? GSLIBERR_TLS :
                                                    GSLIBERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreFreeTls --
 *
 *      Free the allocated thread local storage index for CallCtx pointer.
 *
 * Results:
 *      GSLIBERR_SUCCESS or GSLIBERR_TLS.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreFreeTls(void)
{
   Bool res;

   ASSERT(callCtxTlsIndex != TLS_OUT_OF_INDEXES);

#ifdef _WIN32
   res = TlsFree(callCtxTlsIndex) ? TRUE : FALSE;
#else
   res = pthread_key_delete(callCtxTlsIndex) == 0 ? TRUE : FALSE;
#endif

   callCtxTlsIndex = TLS_OUT_OF_INDEXES;

   return res ? GSLIBERR_SUCCESS : GSLIBERR_TLS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreSetTls --
 *
 *      Set CallCtx pointer to the slot of the thread local storage index.
 *      When ctx is NULL, clear the slot.
 *
 * Results:
 *      GSLIBERR_SUCCESS or GSLIBERR_TLS.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreSetTls(CallCtx *ctx)  // IN
{
   Bool res;

   ASSERT(callCtxTlsIndex != TLS_OUT_OF_INDEXES);

#ifdef _WIN32
   res = TlsSetValue(callCtxTlsIndex, ctx) ? TRUE : FALSE;
#else
   res = pthread_setspecific(callCtxTlsIndex, ctx) == 0 ? TRUE : FALSE;
#endif

   return res ? GSLIBERR_SUCCESS : GSLIBERR_TLS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreGetTls --
 *
 *      Get CallCtx pointer from the slot of the thread local storage index.
 *
 * Results:
 *      CallCtx/NULL pointer
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static CallCtx *
GuestStoreGetTls(void)
{
   ASSERT(callCtxTlsIndex != TLS_OUT_OF_INDEXES);

#ifdef _WIN32
   return (CallCtx *)TlsGetValue(callCtxTlsIndex);
#else
   return (CallCtx *)pthread_getspecific(callCtxTlsIndex);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStore_Init --
 *
 *      GuestStore client library Init entry point function.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

GuestStoreLibError
GuestStore_Init(void)
{
   if (Atomic_ReadInc32(&initLibCount) == 0) {
      GuestStoreLibError retVal = GuestStoreAllocTls();
      if (retVal != GSLIBERR_SUCCESS) {
         Atomic_Dec32(&initLibCount);
      }
      return retVal;
   }

   return GSLIBERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStore_DeInit --
 *
 *      GuestStore client library DeInit entry point function.
 *      Call of GuestStore_DeInit should match succeeded GuestStore_Init call.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

GuestStoreLibError
GuestStore_DeInit(void)
{
   uint32 oldVal;
   uint32 newVal;

   do {
      oldVal = Atomic_Read32(&initLibCount);
      if (oldVal == 0) {
         return GSLIBERR_NOT_INITIALIZED;
      }

      newVal = oldVal - 1;
   } while (Atomic_ReadIfEqualWrite32(&initLibCount,
                                      oldVal, newVal) != oldVal);

   if (oldVal == 1) {
      return GuestStoreFreeTls();
   }

   return GSLIBERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreLogV --
 *
 *      Internal log function.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
GuestStoreLogV(CallCtx *ctx,                 // IN
               GuestStoreLibLogLevel level,  // IN
               const char *fmt,              // IN
               va_list args)                 // IN
{
   char buf[1024] = GSLIBLOG_TAG;

   ASSERT(ctx != NULL && ctx->logger != NULL);

   Str_Vsnprintf(buf + GSLIBLOG_TAG_LEN, sizeof(buf) - GSLIBLOG_TAG_LEN,
                 fmt, args);
   ctx->logger(level, buf, ctx->clientData);
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreLog --
 *
 *      Internal log function.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
GuestStoreLog(CallCtx *ctx,                 // IN
              GuestStoreLibLogLevel level,  // IN
              const char *fmt, ...)         // IN
{
   va_list args;

   ASSERT(ctx != NULL && ctx->logger != NULL);

   va_start(args, fmt);
   GuestStoreLogV(ctx, level, fmt, args);
   va_end(args);
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreFreeCtxResources --
 *
 *      Free resources allocated in each GuestStore_GetContent call.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
GuestStoreFreeCtxResources(CallCtx *ctx)  // IN / OUT
{
   int res;

   if (ctx->output != NULL) {
      fclose(ctx->output);
      ctx->output = NULL;

      /*
       * The output file was created after the content size was received.
       */
      ASSERT(ctx->contentSize >= 0);

      /*
       * Delete the output file if not all the content bytes were received.
       */
      if (ctx->contentBytesReceived != ctx->contentSize) {
         res = Posix_Unlink(ctx->outputPath);
         if (res != 0) {
            LOG_ERR(ctx, "Posix_Unlink failed: outputPath='%s', error=%d.",
                    ctx->outputPath, errno);
         }
      }
   }

   if (ctx->sd != INVALID_SOCKET) {
#ifdef _WIN32
      res = closesocket(ctx->sd);
#else
      res = close(ctx->sd);
#endif

      if (res == SOCKET_ERROR) {
         LOG_ERR(ctx, "close failed on socket %d: error=%d.",
                 ctx->sd, SocketGetLastError());
      }

      ctx->sd = INVALID_SOCKET;
   }

   free(ctx->buf);
   ctx->buf = NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreCreateOutputFile --
 *
 *      Create an output file stream for writing.
 *
 *      If the given file exists, its content is destroyed.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreCreateOutputFile(CallCtx *ctx)  // IN / OUT
{
   FILE *output = Posix_Fopen(ctx->outputPath, "wb");
   if (output == NULL) {
      LOG_ERR(ctx, "Posix_Fopen failed: outputPath='%s', error=%d.",
              ctx->outputPath, errno);
      return GSLIBERR_CREATE_OUTPUT_FILE;
   }

   ctx->output = output;
   return GSLIBERR_SUCCESS;
}


#ifndef _WIN32

/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreConnect --
 *
 *      Connect to vmtoolsd GuestStore plugin.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

GuestStoreLibError
GuestStoreConnect(CallCtx *ctx)  // IN / OUT
{
   struct sockaddr_un svcAddr;
   int res;
   int err;
   struct ucred peerCred;
   socklen_t peerCredLen;

   ctx->sd = socket(AF_UNIX, SOCK_STREAM, 0);
   if (ctx->sd == INVALID_SOCKET) {
      LOG_ERR(ctx, "socket failed: error=%d.", SocketGetLastError());
      return GSLIBERR_CONNECT_GENERIC;
   }

   svcAddr.sun_family = AF_UNIX;
   ASSERT(sizeof(GUESTSTORE_PIPE_NAME) < sizeof(svcAddr.sun_path));
   memcpy(svcAddr.sun_path, GUESTSTORE_PIPE_NAME,
          sizeof(GUESTSTORE_PIPE_NAME));

   do {
      res = connect(ctx->sd, (struct sockaddr*)&svcAddr,
                    (socklen_t)sizeof(svcAddr));
   } while (res == SOCKET_ERROR &&
            (err = SocketGetLastError()) == SYSERR_EINTR);

   if (res == SOCKET_ERROR) {
      LOG_ERR(ctx, "connect failed on socket %d: error=%d.",
              ctx->sd, err);

      if (err == SYSERR_ECONNREFUSED) {
         return GSLIBERR_CONNECT_SERVICE_NOT_RUNNING;
      } else if (err == SYSERR_EACCESS) {
         return GSLIBERR_CONNECT_PERMISSION_DENIED;
      } else {
         return GSLIBERR_CONNECT_GENERIC;
      }
   }

   /*
    * On Linux, the SO_PEERCRED socket option will give us the PID,
    * effective UID, and GID of the peer (the server in this case).
    */
   peerCredLen = (socklen_t)sizeof(peerCred);
   res = getsockopt(ctx->sd, SOL_SOCKET, SO_PEERCRED, &peerCred, &peerCredLen);
   if (res == SOCKET_ERROR) {
      LOG_ERR(ctx, "getsockopt SO_PEERCRED failed: error=%d.",
              SocketGetLastError());
      return GSLIBERR_CONNECT_GENERIC;
   } else if (peerCred.uid != 0) {
      LOG_ERR(ctx, "Peer is not supper user.");
      return GSLIBERR_CONNECT_SECURITY_VIOLATION;
   }

   return GSLIBERR_SUCCESS;
}

#endif


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreRecvBytes --
 *
 *      Partially receive bytes from vmtoolsd GuestStore plugin.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreRecvBytes(CallCtx *ctx,        // IN
                    char *buf,           // OUT
                    int bytesToRecv,     // IN
                    int *bytesReceived)  // OUT
{
   GuestStoreLibError retVal;

   do {
      int res;

      res = recv(ctx->sd,  // Synchronous recv
                 buf,
                 bytesToRecv,
                 0);
      if (res > 0) {
         *bytesReceived = res;
         retVal = GSLIBERR_SUCCESS;
         break;
      } else if (res == 0) {
         LOG_ERR(ctx, "peer closed on socket %d.", ctx->sd);
         retVal = GSLIBERR_CONNECT_PEER_RESET;
         break;
      } else {  // SOCKET_ERROR
         int err = SocketGetLastError();
         if (err == SYSERR_EINTR) {
            continue;
         }

         LOG_ERR(ctx, "recv failed on socket %d: error=%d.",
                 ctx->sd, err);
         retVal = GSLIBERR_RECV;
         break;
      }
   } while (TRUE);

   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreSendBytes --
 *
 *      Send the specified amount of bytes to vmtoolsd GuestStore plugin.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreSendBytes(CallCtx *ctx,     // IN
                    char *buf,        // IN
                    int bytesToSend)  // IN
{
   GuestStoreLibError retVal = GSLIBERR_SUCCESS;
   int bytesSent = 0;

   while (bytesSent < bytesToSend) {
      int res;
      res = send(ctx->sd,  // Synchronous send
                 buf + bytesSent,
                 bytesToSend - bytesSent,
                 0);
      if (res == SOCKET_ERROR) {
         int err = SocketGetLastError();
         if (err == SYSERR_EINTR) {
            continue;
         }

         LOG_ERR(ctx, "send failed on socket %d: error=%d.",
                 ctx->sd, err);
         retVal = GSLIBERR_SEND;
         break;
      }

      bytesSent += res;
   }

   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreSendHTTPRequest --
 *
 *      Send HTTP request for content download to vmtoolsd GuestStore plugin.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreSendHTTPRequest(const char *contentPath,  // IN
                          CallCtx *ctx)             // IN
{
   int len = 0;
   int pathLen;
   int index;

   /*
    * HTTP GET request: GET <path> HTTP/1.1\r\n\r\n
    * excluding <path>, it is 17 bytes.
    * Reserve 1024 bytes for it.
    *
    * Length of contentPath is restricted to GUESTSTORE_CONTENT_PATH_MAX (1024)
    * maximum length after URL escaping is 3 * GUESTSTORE_CONTENT_PATH_MAX.
    *
    * ctx->bufSize is GUESTSTORE_RESPONSE_BUFFER_SIZE (64 * 1024)
    */
   ASSERT((1024 + 3 * GUESTSTORE_CONTENT_PATH_MAX) < ctx->bufSize);

   #define COPY_STR_TO_BUF(str)                      \
      memcpy(ctx->buf + len, str, sizeof(str) - 1);  \
      len += (int)(sizeof(str) - 1)

   COPY_STR_TO_BUF(HTTP_REQ_METHOD_GET);
   COPY_STR_TO_BUF(" ");

   /*
    * ' ', '?' and '%' are the 3 protocol characters GuestStore plugin parses.
    */
   pathLen = (int)strlen(contentPath);
   for (index = 0; index < pathLen; index++) {
      char c = contentPath[index];
      if (c == ' ') {
         COPY_STR_TO_BUF("%20");
      } else if (c == '%') {
         COPY_STR_TO_BUF("%25");
      } else if (c == '?') {
         COPY_STR_TO_BUF("%3F");
      } else {
         *(ctx->buf + len++) = c;
      }
   }

   COPY_STR_TO_BUF(" ");
   COPY_STR_TO_BUF(HTTP_VER);
   COPY_STR_TO_BUF(HTTP_HEADER_END);

   return GuestStoreSendBytes(ctx, ctx->buf, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreRecvHTTPResponseHeader --
 *
 *      Receive HTTP response header from vmtoolsd GuestStore plugin.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreRecvHTTPResponseHeader(CallCtx *ctx)  // IN
{
   int totalBytesReceived = 0;
   int recvBufSize = ctx->bufSize - 1;  // Reserve the last byte for '\0'
   char *httpHeaderEnd;
   char *next_token;
   char *httpVer;
   char *httpStatus;
   int status;
   char *contentLengthHeader;
   char *content;
   int httpResHeaderLen;
   GuestStoreLibError retVal;

   do {
      int bytesReceived = 0;

      retVal = GuestStoreRecvBytes(ctx, ctx->buf + totalBytesReceived,
                                   recvBufSize - totalBytesReceived,
                                   &bytesReceived);
      if (retVal != GSLIBERR_SUCCESS) {
         return retVal;
      }

      totalBytesReceived += bytesReceived;
      ctx->buf[totalBytesReceived] = '\0';
      httpHeaderEnd = strstr(ctx->buf, HTTP_HEADER_END);
      if (httpHeaderEnd != NULL) {
         *httpHeaderEnd = '\0';
         break;
      }

      if (totalBytesReceived == recvBufSize) {
         LOG_ERR(ctx, "Protocol header end mark not found.");
         return GSLIBERR_SERVER;
      }
   } while (TRUE);

   httpVer = strtok_r(ctx->buf, " ", &next_token);
   if (NULL == httpVer || strcmp(httpVer, HTTP_VER) != 0) {
      LOG_ERR(ctx, "Protocol version not correct.");
      return GSLIBERR_SERVER;
   }

   httpStatus = strtok_r(NULL, " ", &next_token);
   if (NULL == httpStatus) {
      LOG_ERR(ctx, "Protocol status code not found.");
      return GSLIBERR_SERVER;
   }

   status = atoi(httpStatus);

   if (status == HTTP_STATUS_CODE_FORBIDDEN) {
      LOG_ERR(ctx, "Content forbidden.");
      return GSLIBERR_CONTENT_FORBIDDEN;
   }

   if (status == HTTP_STATUS_CODE_NOT_FOUND) {
      LOG_ERR(ctx, "Content not found.");
      return GSLIBERR_CONTENT_NOT_FOUND;
   }

   if (status != HTTP_STATUS_CODE_OK) {
      LOG_ERR(ctx, "Invalid protocol status '%s'.", httpStatus);
      return GSLIBERR_SERVER;
   }

   contentLengthHeader = strstr(httpStatus + strlen(httpStatus) + 1,
                                CONTENT_LENGTH_HEADER);
   if (NULL == contentLengthHeader) {
      LOG_ERR(ctx, "Protocol content length not found.");
      return GSLIBERR_SERVER;
   }

   contentLengthHeader += CONTENT_LENGTH_HEADER_LEN;
   while (*contentLengthHeader >= '0' && *contentLengthHeader <= '9') {
      ctx->contentSize = ctx->contentSize * 10 + (*contentLengthHeader - '0');
      contentLengthHeader++;
   }

   if (ctx->contentSize < 0) {
      LOG_ERR(ctx, "Invalid protocol content length.");
      return GSLIBERR_SERVER;
   }

   /*
    * We've got content to save, create the output file now.
    */
   retVal = GuestStoreCreateOutputFile(ctx);
   if (retVal != GSLIBERR_SUCCESS) {
      return retVal;
   }

   /*
    * Save content bytes that follow HTTP response header.
    */
   content = httpHeaderEnd + HTTP_HEADER_END_LEN;
   httpResHeaderLen = (int)(content - ctx->buf);
   if (httpResHeaderLen < totalBytesReceived) {
      int contentLen = totalBytesReceived - httpResHeaderLen;

      ctx->contentBytesReceived += contentLen;
      if (ctx->contentBytesReceived > ctx->contentSize) {
         LOG_ERR(ctx, "Bytes received exceeded content size.");
         return GSLIBERR_SERVER;
      }

      if (fwrite(content, sizeof(char), contentLen, ctx->output) != contentLen) {
         LOG_ERR(ctx, "fwrite failed: error=%d.", errno);
         return GSLIBERR_WRITE_OUTPUT_FILE;
      }

      if (!REPORT_PROGRESS(ctx)) {
         LOG_ERR(ctx, "Request cancelled.");
         return GSLIBERR_CANCELLED;
      }
   }

   return GSLIBERR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreRecvHTTPResponseBody --
 *
 *      Receive HTTP response body, i.e., content bytes, from vmtoolsd
 *      GuestStore plugin.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreLibError
GuestStoreRecvHTTPResponseBody(CallCtx *ctx)  // IN
{
   GuestStoreLibError retVal = GSLIBERR_SUCCESS;

   while (ctx->contentBytesReceived < ctx->contentSize) {
      int bytesReceived = 0;

      retVal = GuestStoreRecvBytes(ctx, ctx->buf, ctx->bufSize, &bytesReceived);
      if (retVal != GSLIBERR_SUCCESS) {
         break;
      }

      ctx->contentBytesReceived += bytesReceived;
      if (ctx->contentBytesReceived > ctx->contentSize) {
         LOG_ERR(ctx, "Bytes received exceeded content size.");
         retVal = GSLIBERR_SERVER;
         break;
      }

      if (fwrite(ctx->buf, sizeof(char), bytesReceived, ctx->output) != bytesReceived) {
         LOG_ERR(ctx, "fwrite failed: error=%d.", errno);
         retVal = GSLIBERR_WRITE_OUTPUT_FILE;
         break;
      }

      if (!REPORT_PROGRESS(ctx)) {
         LOG_ERR(ctx, "Request cancelled.");
         retVal = GSLIBERR_CANCELLED;
         break;
      }
   }

   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStore_GetContent --
 *
 *      GuestStore client library GetContent entry point function.
 *
 * Results:
 *      GSLIBERR_SUCCESS or an error code of GSLIBERR_*.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

GuestStoreLibError
GuestStore_GetContent(
   const char *contentPath,                     // IN
   const char *outputPath,                      // IN
   GuestStore_Logger logger,                    // IN, OPTIONAL
   GuestStore_Panic panic,                      // IN, OPTIONAL
   GuestStore_GetContentCallback getContentCb,  // IN, OPTIONAL
   void *clientData)                            // IN, OPTIONAL
{
   GuestStoreLibError retVal;
   CallCtx ctx = { 0 };
#ifdef _WIN32
   WSADATA wsaData;
   int res;
#endif

   /*
    * Set ctx before first LOG_ERR.
    */
   ctx.contentPath = contentPath ? contentPath : "";
   ctx.outputPath = outputPath ? outputPath : "";
   ctx.logger = logger;
   ctx.panic = panic;
   ctx.getContentCb = getContentCb;
   ctx.clientData = clientData;
   ctx.sd = INVALID_SOCKET;

   if (contentPath == NULL || *contentPath != '/' ||
      strlen(contentPath) > GUESTSTORE_CONTENT_PATH_MAX) {
      LOG_ERR(&ctx, "Invalid content path.");
      return GSLIBERR_INVALID_PARAMETER;
   }

   if (outputPath == NULL || *outputPath == '\0') {
      LOG_ERR(&ctx, "Invalid output file path.");
      return GSLIBERR_INVALID_PARAMETER;
   }

   if (Atomic_Read32(&initLibCount) == 0 ||
       callCtxTlsIndex == TLS_OUT_OF_INDEXES) {
      LOG_ERR(&ctx, "Library is not properly initialized.");
      return GSLIBERR_NOT_INITIALIZED;
   }

   retVal = GuestStoreSetTls(&ctx);
   if (retVal != GSLIBERR_SUCCESS) {
      LOG_ERR(&ctx, "GuestStoreSetTls failed.");
      return retVal;
   }

#ifdef _WIN32
   res = WSAStartup(MAKEWORD(2, 2), &wsaData);
   if (res != 0) {
      retVal = GSLIBERR_CONNECT_GENERIC;
      LOG_ERR(&ctx, "WSAStartup failed: error=%d.", res);
      goto exit;
   }
#endif

   retVal = GuestStoreConnect(&ctx);
   if (retVal != GSLIBERR_SUCCESS) {
      goto exit;
   }

   ctx.bufSize = GUESTSTORE_RESPONSE_BUFFER_SIZE;
   ctx.buf = (char *)Util_SafeMalloc(ctx.bufSize);

   retVal = GuestStoreSendHTTPRequest(contentPath, &ctx);
   if (retVal != GSLIBERR_SUCCESS) {
      goto exit;
   }

   retVal = GuestStoreRecvHTTPResponseHeader(&ctx);
   if (retVal != GSLIBERR_SUCCESS) {
      goto exit;
   }

   retVal = GuestStoreRecvHTTPResponseBody(&ctx);

exit:

   GuestStoreFreeCtxResources(&ctx);  // Should be before WSACleanup()

#ifdef _WIN32
   if (res == 0) {
      if (retVal != GSLIBERR_SUCCESS) {
         /*
          * WSASetLastError needs a successful WSAStartup call,
          * WSAGetLastError does not.
          *
          * Note: WSACleanup may change WSA last error again.
          */
         WSASetLastError(ctx.winWSAErrNum);
      }

      WSACleanup();
   }
#endif

   GuestStoreSetTls(NULL);  // Ignore its return value

   /*
    * Restore the last error in the end.
    */
   if (retVal != GSLIBERR_SUCCESS) {
      Err_SetErrno(ctx.errNum);
#ifdef _WIN32
      errno = ctx.winErrNum;
#endif
   }

   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Debug --
 *
 *      Stub for Debug.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
Debug(const char *fmt, ...)
{
   WITH_ERRNO(__errNum__,
      {
         CallCtx *ctx = GuestStoreGetTls();

         if (ctx != NULL && ctx->logger != NULL) {
            va_list args;

            va_start(args, fmt);
            GuestStoreLogV(ctx, GSLIBLOGLEVEL_DEBUG, fmt, args);
            va_end(args);
         }
      }
   );
}


/*
 *-----------------------------------------------------------------------------
 *
 * Log --
 *
 *      Stub for Log.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
Log(const char *fmt, ...)
{
   WITH_ERRNO(__errNum__,
      {
         CallCtx *ctx = GuestStoreGetTls();

         if (ctx != NULL && ctx->logger != NULL) {
            va_list args;

            va_start(args, fmt);
            GuestStoreLogV(ctx, GSLIBLOGLEVEL_INFO, fmt, args);
            va_end(args);
         }
      }
   );
}


/*
 *-----------------------------------------------------------------------------
 *
 * Warning --
 *
 *      Stub for Warning.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
Warning(const char *fmt, ...)
{
   WITH_ERRNO(__errNum__,
      {
         CallCtx *ctx = GuestStoreGetTls();

         if (ctx != NULL && ctx->logger != NULL) {
            va_list args;

            va_start(args, fmt);
            GuestStoreLogV(ctx, GSLIBLOGLEVEL_WARNING, fmt, args);
            va_end(args);
         }
      }
   );
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic --
 *
 *      Stub for Panic.
 *
 * Results:
 *      Does not return.
 *
 * Side-effects:
 *      Process exits.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic(const char *fmt, ...)
{
   va_list args;
   CallCtx *ctx;

   va_start(args, fmt);

   ctx = GuestStoreGetTls();
   if (ctx != NULL && ctx->panic != NULL) {
      char buf[1024] = GSLIBLOG_TAG;

      Str_Vsnprintf(buf + GSLIBLOG_TAG_LEN, sizeof(buf) - GSLIBLOG_TAG_LEN,
                    fmt, args);
      ctx->panic(buf, ctx->clientData);  // No return
   } else {
      fprintf(stderr, "Panic: " GSLIBLOG_TAG);
      vfprintf(stderr, fmt, args);
   }

   va_end(args);

   exit(-1);
}
07070100000225000081A4000000000000000000000001682255050000177E000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/libguestStoreClient/guestStoreClientLibInt.h   /*********************************************************
 * Copyright (c) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 *  guestStoreClientLibInt.h  --
 *    Private definitions for guestStoreClientLib.
 */

#ifndef __GUESTSTORECLIENTLIBINT_H__
#define __GUESTSTORECLIENTLIBINT_H__

#include "vm_assert.h"
#include "vm_atomic.h"
#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "err.h"
#include "str.h"
#include "util.h"
#include "posix.h"

#include "guestStoreConst.h"
#include "guestStoreDefs.h"
#include "vmware/tools/guestStoreClientLib.h"

#ifdef _WIN32

#define SYSERR_EADDRINUSE    WSAEADDRINUSE
#define SYSERR_EACCESS       WSAEACCES
#define SYSERR_EINTR         WSAEINTR
#define SYSERR_ECONNRESET    WSAECONNRESET
#define SYSERR_ECONNREFUSED  WSAECONNREFUSED

#define SHUTDOWN_RECV  SD_RECEIVE
#define SHUTDOWN_SEND  SD_SEND
#define SHUTDOWN_BOTH  SD_BOTH

typedef int socklen_t;

#else

#define SYSERR_EADDRINUSE    EADDRINUSE
#define SYSERR_EACCESS       EACCES
#define SYSERR_EINTR         EINTR
#define SYSERR_ECONNRESET    ECONNRESET
#define SYSERR_ECONNREFUSED  ECONNREFUSED

#define SHUTDOWN_RECV  SHUT_RD
#define SHUTDOWN_SEND  SHUT_WR
#define SHUTDOWN_BOTH  SHUT_RDWR

typedef int SOCKET;

#define SOCKET_ERROR    (-1)
#define INVALID_SOCKET  ((SOCKET) -1)

#endif


/*
 * Context of each GuestStore_GetContent call.
 */
typedef struct _CallCtx {
   const char *contentPath;  // Requested content path
   const char *outputPath;  // Output file path
   GuestStore_Logger logger;  // Caller provided logger function
   GuestStore_Panic panic;  // Caller provided panic function
   GuestStore_GetContentCallback getContentCb;  // Progress callback
   void *clientData;  // Parameter for caller provided functions
   FILE *output;  // Output file stream
   SOCKET sd;  // Socket descriptor connecting to vmtoolsd GuestStore plugin
   int64 contentSize;  // Total content bytes
   int64 contentBytesReceived;  // Received content bytes
   int bufSize;  // Content download buffer size
   char *buf;  // Content download buffer
   Err_Number errNum;  // Preserve the last error
#ifdef _WIN32
   int winErrNum;
   int winWSAErrNum;
#endif
} CallCtx;

/*
 * Preserve the first last error that fails API GuestStore_GetContent() and
 * restore it when the API returns in case API exit resource freeing calls
 * change the last error.
 *
 * WSASetLastError needs a successful WSAStartup call,
 * WSAGetLastError does not.
 */
#ifdef _WIN32
#define LOG_ERR(ctx, format, ...)                 \
   if ((ctx)->errNum == 0 &&                      \
       (ctx)->winErrNum == 0 &&                   \
       (ctx)->winWSAErrNum == 0) {                \
      (ctx)->errNum = Err_Errno();                \
      (ctx)->winErrNum = errno;                   \
      (ctx)->winWSAErrNum = WSAGetLastError();    \
   }                                              \
   if ((ctx)->logger != NULL) {                   \
       GuestStoreLog((ctx), GSLIBLOGLEVEL_ERROR,  \
                     format, ##__VA_ARGS__);      \
   }
#else
#define LOG_ERR(ctx, format, ...)                 \
   if ((ctx)->errNum == 0) {                      \
      (ctx)->errNum = Err_Errno();                \
   }                                              \
   if ((ctx)->logger != NULL) {                   \
       GuestStoreLog((ctx), GSLIBLOGLEVEL_ERROR,  \
                     format, ##__VA_ARGS__);      \
   }
#endif

#define LOG_WARN(ctx, format, ...)                  \
   if ((ctx)->logger != NULL) {                     \
       GuestStoreLog((ctx), GSLIBLOGLEVEL_WARNING,  \
                     format, ##__VA_ARGS__);        \
   }

#define LOG_INFO(ctx, format, ...)               \
   if ((ctx)->logger != NULL) {                  \
       GuestStoreLog((ctx), GSLIBLOGLEVEL_INFO,  \
                     format, ##__VA_ARGS__);     \
   }

#define LOG_DEBUG(ctx, format, ...)               \
   if ((ctx)->logger != NULL) {                   \
       GuestStoreLog((ctx), GSLIBLOGLEVEL_DEBUG,  \
                     format, ##__VA_ARGS__);      \
   }

#define REPORT_PROGRESS(ctx)                                         \
   (((ctx)->getContentCb != NULL) ? (ctx)->getContentCb(             \
                                       (ctx)->contentSize,           \
                                       (ctx)->contentBytesReceived,  \
                                       (ctx)->clientData)            \
                                    : TRUE)


/*
 *-----------------------------------------------------------------------------
 *
 * SocketGetLastError --
 *
 *      Get the last failed sock function error code.
 *
 * Results:
 *      error code.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static inline int
SocketGetLastError(void)
{
#ifdef _WIN32
   return WSAGetLastError();
#else
   return errno;
#endif
}


#ifdef __cplusplus
extern "C" {
#endif

void
GuestStoreLog(CallCtx *ctx,                 // IN
              GuestStoreLibLogLevel level,  // IN
              const char *fmt, ...);        // IN

GuestStoreLibError
GuestStoreConnect(CallCtx *ctx);  // IN / OUT

#ifdef __cplusplus
}
#endif

#endif /* __GUESTSTORECLIENTLIBINT_H__ */
  07070100000226000081A400000000000000000000000168225505000004A3000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/libguestStoreClient/gueststoreclientlib_version.h  /*********************************************************
 * Copyright (c) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _GUESTSTORECLIENTLIB_VERSION_H_
#define _GUESTSTORECLIENTLIB_VERSION_H_

#include "vm_tools_version.h"
#define GUESTSTORECLIENTLIB_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define GUESTSTORECLIENTLIB_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _GUESTSTORECLIENTLIB_VERSION_H_ */
 07070100000227000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/libguestlib    07070100000228000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/libguestlib/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000229000081A40000000000000000000000016822550500000F15000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/libguestlib/Makefile.am    ################################################################################
### Copyright (C) 2007-2018,2020,2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

lib_LTLIBRARIES = libguestlib.la

AM_CFLAGS =
AM_CFLAGS += -I$(top_srcdir)/include

libguestlib_la_LIBADD =
libguestlib_la_LIBADD += ../lib/backdoor/libBackdoor.la
libguestlib_la_LIBADD += ../lib/dataMap/libDataMap.la
libguestlib_la_LIBADD += ../lib/dynxdr/libDynxdr.la
libguestlib_la_LIBADD += ../lib/err/libErr.la
libguestlib_la_LIBADD += ../lib/hashMap/libHashMap.la
libguestlib_la_LIBADD += ../lib/message/libMessage.la
libguestlib_la_LIBADD += ../lib/misc/libMisc.la
libguestlib_la_LIBADD += ../lib/rpcOut/libRpcOut.la
libguestlib_la_LIBADD += ../lib/string/libString.la
libguestlib_la_LIBADD += ../lib/unicode/libUnicode.la
libguestlib_la_LIBADD += ../lib/vmCheck/libVmCheck.la
libguestlib_la_LIBADD += ../lib/vmSignal/libVmSignal.la
libguestlib_la_LIBADD += @XDR_LIBS@

libguestlib_rpcchanneldir = $(top_srcdir)/lib/rpcChannel

libguestlib_la_SOURCES =
libguestlib_la_SOURCES += guestlibV3_xdr.c
libguestlib_la_SOURCES += guestlibIoctl_xdr.c
libguestlib_la_SOURCES += guestSDKLog.c
libguestlib_la_SOURCES += vmGuestLib.c
libguestlib_la_SOURCES += $(libguestlib_rpcchanneldir)/bdoorChannel.c
libguestlib_la_SOURCES += $(libguestlib_rpcchanneldir)/rpcChannel.c
libguestlib_la_SOURCES += $(libguestlib_rpcchanneldir)/glib_stubs.c
if HAVE_VSOCK
libguestlib_la_SOURCES += $(libguestlib_rpcchanneldir)/vsockChannel.c
libguestlib_la_SOURCES += $(libguestlib_rpcchanneldir)/simpleSocket.c
endif

libguestlib_la_LIBADD += -ldl -lrt
# We require GCC, so we're fine passing compiler-specific flags.
# Needed for OS's that don't link shared libraries against libc by default, e.g. FreeBSD
libguestlib_la_LIBADD += -lc

libguestlib_includedir = $(includedir)/vmGuestLib

libguestlib_include_HEADERS =
libguestlib_include_HEADERS += $(top_srcdir)/lib/include/includeCheck.h
libguestlib_include_HEADERS += $(top_srcdir)/lib/include/vmGuestLib.h
libguestlib_include_HEADERS += $(top_srcdir)/lib/include/vmSessionId.h
libguestlib_include_HEADERS += $(top_srcdir)/lib/include/vm_basic_types.h

BUILT_SOURCES =
BUILT_SOURCES += guestlibV3.h
BUILT_SOURCES += guestlibV3_xdr.c
BUILT_SOURCES += guestlibIoctl.h
BUILT_SOURCES += guestlibIoctl_xdr.c

CLEANFILES =
CLEANFILES += $(BUILT_SOURCES)

# XXX: see explanation in lib/guestRpc/Makefile.am
CFLAGS += -Wno-unused

libguestlib_la_CPPFLAGS =
libguestlib_la_CPPFLAGS += -DVMTOOLS_USE_GLIB
libguestlib_la_CPPFLAGS += -DUSE_RPCI_ONLY
libguestlib_la_CPPFLAGS += @GLIB2_CPPFLAGS@
libguestlib_la_CPPFLAGS += @XDR_CPPFLAGS@

EXTRA_DIST = vmguestlib.pc.in

pkgconfigdir   = $(libdir)/pkgconfig
pkgconfig_DATA = vmguestlib.pc

$(pkgconfig_DATA): $(top_builddir)/config.status

guestlibIoctl.h: guestlibIoctl.x
	@RPCGEN_WRAPPER@ libguestlib/guestlibIoctl.x $@

guestlibIoctl_xdr.c: guestlibIoctl.x guestlibIoctl.h
	@RPCGEN_WRAPPER@ libguestlib/guestlibIoctl.x $@

guestlibV3.h: guestlibV3.x
	@RPCGEN_WRAPPER@ libguestlib/guestlibV3.x $@

guestlibV3_xdr.c: guestlibV3.x guestlibV3.h
	@RPCGEN_WRAPPER@ libguestlib/guestlibV3.x $@


   0707010000022A000081A40000000000000000000000016822550500000C10000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/libguestlib/guestSDKLog.c  /*********************************************************
 * Copyright (C) 2013-2015,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * guestSDKLog.c --
 *
 *   guestSDK logging stubs. In guestSDK, we only do logging in OBJ builds.
 *
 */

#include <stdio.h>
#include "str.h"


/*
 *-----------------------------------------------------------------------------
 *
 * Debug --
 *
 *    Log debug messages.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Stderr.
 *
 *-----------------------------------------------------------------------------
 */

void
Debug(const char *fmt, ...)
{
#ifdef VMX86_LOG /* only do logging on OBJ builds */
   va_list args;

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Log --
 *
 *    Log messages.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Stderr.
 *
 *-----------------------------------------------------------------------------
 */

void
Log(const char *fmt, ...)
{
#ifdef VMX86_LOG /* only do logging on OBJ builds */
   va_list args;

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Warning --
 *
 *    Log warning messages.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Stderr.
 *
 *-----------------------------------------------------------------------------
 */

void
Warning(const char *fmt, ...)
{
#ifdef VMX86_LOG /* only do logging on OBJ builds */
   va_list args;

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic --
 *
 *    Panic.  Apps have to implement this for themselves.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Death.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic(const char *fmt, ...) // IN
{
   va_list args;
   char buffer[1024];
   volatile char *p = NULL;

   va_start(args, fmt);
   Str_Vsnprintf(buffer, sizeof buffer, fmt, args);
   va_end(args);

   printf("PANIC: %s", buffer);

   /* force segfault */
   /* coverity[var_deref_op] */
   buffer[0] = *p;
   while (1) ; // avoid compiler warning
}
0707010000022B000081A400000000000000000000000168225505000007AF000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/libguestlib/guestlibIoctl.x    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * guestlibIoctl.x --
 *
 *    Data structures that encode the information exchanged over guestlib
 *    client ioctl and VMX .
 */

/* The GuestRpc command string. */
const VMGUESTLIB_IOCTL_COMMAND_STRING = "guestlib.ioctl";

const GUESTLIB_IOCTL_ATOMIC_UPDATE_COOKIE_LENGTH = 256;

enum GuestLibAtomicUpdateStatus {
   GUESTLIB_ATOMIC_UPDATE_OK_SUCCESS   = 0,
   GUESTLIB_ATOMIC_UPDATE_OK_FAIL      = 1,
   GUESTLIB_ATOMIC_UPDATE_EBADPARAM    = 2,
   GUESTLIB_ATOMIC_UPDATE_EUNAVAILABLE = 3,
   GUESTLIB_ATOMIC_UPDATE_EUNKNOWN     = 4
};

/* All supported ioctl ids */
enum GuestLibIoctlId {
   GUESTLIB_IOCTL_ATOMIC_UPDATE_COOKIE = 1,

   GUESTLIB_IOCTL_MAX = 2
};

/* Parameters for AtomicUpdateCookie ioctl. */
struct GuestLibIoctlAtomicUpdateCookie {
   string src<GUESTLIB_IOCTL_ATOMIC_UPDATE_COOKIE_LENGTH>;
   string dst<GUESTLIB_IOCTL_ATOMIC_UPDATE_COOKIE_LENGTH>;
};

/* Struct for all supported ioctls */
union GuestLibIoctlParam switch (GuestLibIoctlId d) {
   case GUESTLIB_IOCTL_ATOMIC_UPDATE_COOKIE:
      struct GuestLibIoctlAtomicUpdateCookie atomicUpdateCookie;
};
 0707010000022C000081A40000000000000000000000016822550500001A16000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/libguestlib/guestlibV3.x   /*********************************************************
 * Copyright (C) 2008-2016,2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * guestlibV3.x --
 *
 *    Data structures that encode the information sent from VMX to Guest upon a
 *    Guestlib protocol v3 request.
 */

struct GuestLibV3StatUint32 {
   Bool valid;
   uint32 value;
};

struct GuestLibV3StatUint64 {
   Bool valid;
   uint64 value;
};

const GUESTLIBV3_STRING_SIZE = 512;

struct GuestLibV3String {
   Bool valid;
   string value<GUESTLIBV3_STRING_SIZE>;
};

struct GuestLibV3ByteArray {
   Bool valid;
   opaque value<>;
};

typedef uint32 GuestLibV3StatCount;

enum GuestLibV3TypeIds {
   /* V2 statistics */
   GUESTLIB_TYPE_RESERVED           = 0,
   GUESTLIB_CPU_RESERVATION_MHZ     = 1,
   GUESTLIB_CPU_LIMIT_MHZ           = 2,
   GUESTLIB_CPU_SHARES              = 3,
   GUESTLIB_CPU_USED_MS             = 4,

   GUESTLIB_HOST_MHZ                = 5,

   GUESTLIB_MEM_RESERVATION_MB      = 6,
   GUESTLIB_MEM_LIMIT_MB            = 7,
   GUESTLIB_MEM_SHARES              = 8,
   GUESTLIB_MEM_MAPPED_MB           = 9,
   GUESTLIB_MEM_ACTIVE_MB           = 10,
   GUESTLIB_MEM_OVERHEAD_MB         = 11,
   GUESTLIB_MEM_BALLOONED_MB        = 12,
   GUESTLIB_MEM_SWAPPED_MB          = 13,
   GUESTLIB_MEM_SHARED_MB           = 14,
   GUESTLIB_MEM_SHARED_SAVED_MB     = 15,
   GUESTLIB_MEM_USED_MB             = 16,

   GUESTLIB_ELAPSED_MS              = 17,
   GUESTLIB_RESOURCE_POOL_PATH      = 18,

   GUESTLIB_CPU_STOLEN_MS           = 19,
   GUESTLIB_MEM_TARGET_SIZE_MB      = 20,

   /* Host specific counters. */
   GUESTLIB_HOST_CPU_NUM_CORES      = 21,
   GUESTLIB_HOST_CPU_USED_MS        = 22,
   GUESTLIB_HOST_MEM_SWAPPED_MB     = 23,
   GUESTLIB_HOST_MEM_SHARED_MB      = 24,
   GUESTLIB_HOST_MEM_USED_MB        = 25,
   GUESTLIB_HOST_MEM_PHYS_MB        = 26,
   GUESTLIB_HOST_MEM_PHYS_FREE_MB   = 27,

   /* GUESTLIB_HOST_MEM_KERN_OVHD_MB is deprecated in ESXi from version 7.1 */
   GUESTLIB_HOST_MEM_KERN_OVHD_MB   = 28,

   GUESTLIB_HOST_MEM_MAPPED_MB      = 29,
   GUESTLIB_HOST_MEM_UNMAPPED_MB    = 30,

   /* Counters added in ESX5.0 */
   GUESTLIB_MEM_ZIPPED_MB           = 31,
   GUESTLIB_MEM_ZIPSAVED_MB         = 32,
   GUESTLIB_MEM_LLSWAPPED_MB        = 33,
   GUESTLIB_MEM_SWAP_TARGET_MB      = 34,
   GUESTLIB_MEM_BALLOON_TARGET_MB   = 35,
   GUESTLIB_MEM_BALLOON_MAX_MB      = 36,
   GUESTLIB_RESOURCE_POOL_PATH_LONG = 37,

   /* New Counters added in ESX 7.0 */
   GUESTLIB_MEM_SHARES_64           = 38,
   /*------ Add any new statistics above this line. ------- */

   /*------ Bump this when adding to this list. -------*/
   GUESTLIB_MAX_STATISTIC_ID        = 39
};

union GuestLibV3Stat switch (GuestLibV3TypeIds d) {
   case GUESTLIB_CPU_RESERVATION_MHZ:
      struct GuestLibV3StatUint32 cpuReservationMHz;
   case GUESTLIB_CPU_LIMIT_MHZ:
      struct GuestLibV3StatUint32 cpuLimitMHz;
   case GUESTLIB_CPU_SHARES:
      struct GuestLibV3StatUint32 cpuShares;
   case GUESTLIB_CPU_USED_MS:
      struct GuestLibV3StatUint64 cpuUsedMs;

   case GUESTLIB_HOST_MHZ:
      struct GuestLibV3StatUint32 hostMHz;

   case GUESTLIB_MEM_RESERVATION_MB:
      struct GuestLibV3StatUint32 memReservationMB;
   case GUESTLIB_MEM_LIMIT_MB:
      struct GuestLibV3StatUint32 memLimitMB;
   case GUESTLIB_MEM_SHARES:
      struct GuestLibV3StatUint32 memShares;
   case GUESTLIB_MEM_MAPPED_MB:
      struct GuestLibV3StatUint32 memMappedMB;
   case GUESTLIB_MEM_ACTIVE_MB:
      struct GuestLibV3StatUint32 memActiveMB;
   case GUESTLIB_MEM_OVERHEAD_MB:
      struct GuestLibV3StatUint32 memOverheadMB;
   case GUESTLIB_MEM_BALLOONED_MB:
      struct GuestLibV3StatUint32 memBalloonedMB;
   case GUESTLIB_MEM_SWAPPED_MB:
      struct GuestLibV3StatUint32 memSwappedMB;
   case GUESTLIB_MEM_SHARED_MB:
      struct GuestLibV3StatUint32 memSharedMB;
   case GUESTLIB_MEM_SHARED_SAVED_MB:
      struct GuestLibV3StatUint32 memSharedSavedMB;
   case GUESTLIB_MEM_USED_MB:
      struct GuestLibV3StatUint32 memUsedMB;

   case GUESTLIB_ELAPSED_MS:
      struct GuestLibV3StatUint64 elapsedMs;

   case GUESTLIB_RESOURCE_POOL_PATH:
      struct GuestLibV3String resourcePoolPath;

   case GUESTLIB_CPU_STOLEN_MS:
      struct GuestLibV3StatUint64 cpuStolenMs;
   case GUESTLIB_MEM_TARGET_SIZE_MB:
      struct GuestLibV3StatUint64 memTargetSizeMB;

   case GUESTLIB_HOST_CPU_NUM_CORES:
      struct GuestLibV3StatUint32 hostCpuNumCores;
   case GUESTLIB_HOST_CPU_USED_MS:
      struct GuestLibV3StatUint64 hostCpuUsedMs;
   case GUESTLIB_HOST_MEM_SWAPPED_MB:
      struct GuestLibV3StatUint64 hostMemSwappedMB;
   case GUESTLIB_HOST_MEM_SHARED_MB:
      struct GuestLibV3StatUint64 hostMemSharedMB;
   case GUESTLIB_HOST_MEM_USED_MB:
      struct GuestLibV3StatUint64 hostMemUsedMB;
   case GUESTLIB_HOST_MEM_PHYS_MB:
      struct GuestLibV3StatUint64 hostMemPhysMB;
   case GUESTLIB_HOST_MEM_PHYS_FREE_MB:
      struct GuestLibV3StatUint64 hostMemPhysFreeMB;
   case GUESTLIB_HOST_MEM_KERN_OVHD_MB:
      struct GuestLibV3StatUint64 hostMemKernOvhdMB;
   case GUESTLIB_HOST_MEM_MAPPED_MB:
      struct GuestLibV3StatUint64 hostMemMappedMB;
   case GUESTLIB_HOST_MEM_UNMAPPED_MB:
      struct GuestLibV3StatUint64 hostMemUnmappedMB;
   case GUESTLIB_MEM_ZIPPED_MB:
      struct GuestLibV3StatUint32 memZippedMB;
   case GUESTLIB_MEM_ZIPSAVED_MB:
      struct GuestLibV3StatUint32 memZipSavedMB;
   case GUESTLIB_MEM_LLSWAPPED_MB:
      struct GuestLibV3StatUint32 memLLSwappedMB;
   case GUESTLIB_MEM_SWAP_TARGET_MB:
      struct GuestLibV3StatUint32 memSwapTargetMB;
   case GUESTLIB_MEM_BALLOON_TARGET_MB:
      struct GuestLibV3StatUint32 memBalloonTargetMB;
   case GUESTLIB_MEM_BALLOON_MAX_MB:
      struct GuestLibV3StatUint32 memBalloonMaxMB;
   case GUESTLIB_RESOURCE_POOL_PATH_LONG:
      struct GuestLibV3ByteArray resourcePoolPathLong;
   case GUESTLIB_MEM_SHARES_64:
      struct GuestLibV3StatUint64 memShares64;
};

  0707010000022D000081A4000000000000000000000001682255050000D544000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/libguestlib/vmGuestLib.c   /*********************************************************
 * Copyright (c) 2005-2016,2019-2020, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * Implementation of the guestlib library.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "vmware.h"
#include "vmGuestLib.h"
#include "vmGuestLibInt.h"
#include "str.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/guestrpc.h"
#include "vmcheck.h"
#include "util.h"
#include "debug.h"
#include "strutil.h"
#include "guestlibV3.h"
#include "guestlibIoctl.h"
#include "dynxdr.h"
#include "xdrutil.h"
#include "ctype.h"

#define GUESTLIB_NAME "VMware Guest API"

/*
 * These are client side data structures, separate from the wire data formats
 * (VMGuestLibDataV[23]).
 */

/* Layout of the buffer holding the variable length array of V3 statistics. */
typedef struct {
   GuestLibV3StatCount   numStats;
   GuestLibV3Stat        stats[0];
} VMGuestLibStatisticsV3;

/* Layout of the handle that holds information about the statistics. */
typedef struct {
   uint32 version;
   VMSessionId sessionId;

   /*
    * Statistics.
    *
    * dataSize is the size of the buffer pointed to by 'data'.
    * For v2 protocol:
    *   - 'data' points to VMGuestLibDataV2 struct,
    * For v3 protocol:
    *   - 'data' points to VMGuestLibStatisticsV3 struct.
    */
   size_t dataSize;
   void *data;
} VMGuestLibHandleType;

#define HANDLE_VERSION(h)     (((VMGuestLibHandleType *)(h))->version)
#define HANDLE_SESSIONID(h)   (((VMGuestLibHandleType *)(h))->sessionId)
#define HANDLE_DATA(h)        (((VMGuestLibHandleType *)(h))->data)
#define HANDLE_DATASIZE(h)    (((VMGuestLibHandleType *)(h))->dataSize)

#define VMGUESTLIB_GETSTAT_V2(HANDLE, ERROR, OUTPTR, FIELDNAME)      \
   do {                                                              \
      VMGuestLibDataV2 *_dataV2 = HANDLE_DATA(HANDLE);               \
      ASSERT(HANDLE_VERSION(HANDLE) == 2);                           \
      if (!_dataV2->FIELDNAME.valid) {                               \
         (ERROR) = VMGUESTLIB_ERROR_NOT_AVAILABLE;                   \
         break;                                                      \
      }                                                              \
      *(OUTPTR) = _dataV2->FIELDNAME.value;                          \
      (ERROR) = VMGUESTLIB_ERROR_SUCCESS;                            \
   } while (0)

#define VMGUESTLIB_GETSTAT_V3(HANDLE, ERROR, OUTPTR, FIELDNAME, STATID)         \
   do {                                                                         \
      void *_data;                                                              \
      GuestLibV3Stat _stat;                                                     \
                                                                                \
      (ERROR) = VMGuestLibCheckArgs((HANDLE), (OUTPTR), &_data);                \
      if (VMGUESTLIB_ERROR_SUCCESS != (ERROR)) {                                \
         break;                                                                 \
      }                                                                         \
      ASSERT(HANDLE_VERSION(HANDLE) == 3);                                      \
      (ERROR) = VMGuestLibGetStatisticsV3((HANDLE), (STATID), &_stat);          \
      if ((ERROR) != VMGUESTLIB_ERROR_SUCCESS) {                                \
         break;                                                                 \
      }                                                                         \
      if (!_stat.GuestLibV3Stat_u.FIELDNAME.valid) {                            \
         (ERROR) = VMGUESTLIB_ERROR_NOT_AVAILABLE;                              \
         break;                                                                 \
      }                                                                         \
      ASSERT(_stat.d == (STATID));                                              \
      if (sizeof *(OUTPTR) < sizeof _stat.GuestLibV3Stat_u.FIELDNAME.value) {   \
         (ERROR) = VMGUESTLIB_ERROR_BUFFER_TOO_SMALL;                           \
         break;                                                                 \
      }                                                                         \
      *(OUTPTR) = _stat.GuestLibV3Stat_u.FIELDNAME.value;                       \
      (ERROR) = VMGUESTLIB_ERROR_SUCCESS;                                       \
   } while (0)

/* Handle extraction of integral type statistics. */
#define VMGUESTLIB_GETFN_BODY(HANDLE, ERROR, OUTPTR, FIELDNAME, STATID)         \
   do {                                                                         \
      void *_data;                                                              \
                                                                                \
      (ERROR) = VMGuestLibCheckArgs((HANDLE), (OUTPTR), &_data);                \
      if (VMGUESTLIB_ERROR_SUCCESS != (ERROR)) {                                \
         break;                                                                 \
      }                                                                         \
      if (HANDLE_VERSION(HANDLE) == 2) {                                        \
         VMGUESTLIB_GETSTAT_V2(HANDLE, ERROR, OUTPTR, FIELDNAME);               \
      } else if (HANDLE_VERSION(HANDLE) == 3) {                                 \
         VMGUESTLIB_GETSTAT_V3(HANDLE, ERROR, OUTPTR, FIELDNAME, STATID);       \
      }                                                                         \
   } while (0)


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetErrorText --
 *
 *      Get the English text explanation for a given GuestLib error code.
 *
 * Results:
 *      The error string for the given error code.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char const *
VMGuestLib_GetErrorText(VMGuestLibError error) // IN: Error code
{
   switch (error) {
   case VMGUESTLIB_ERROR_SUCCESS:
      return "No error";

   case VMGUESTLIB_ERROR_NOT_RUNNING_IN_VM:
      return GUESTLIB_NAME " is not running in a Virtual Machine";

   case VMGUESTLIB_ERROR_NOT_ENABLED:
      return GUESTLIB_NAME " is not enabled on the host";

   case VMGUESTLIB_ERROR_NOT_AVAILABLE:
      return "This value is not available on this host";

   case VMGUESTLIB_ERROR_NO_INFO:
      return "VMGuestLib_UpdateInfo() has not been called";

   case VMGUESTLIB_ERROR_MEMORY:
      return "There is not enough system memory";

   case VMGUESTLIB_ERROR_BUFFER_TOO_SMALL:
      return "The provided memory buffer is too small";

   case VMGUESTLIB_ERROR_INVALID_HANDLE:
      return "The provided handle is invalid";

   case VMGUESTLIB_ERROR_INVALID_ARG:
      return "One or more arguments were invalid";

   case VMGUESTLIB_ERROR_OTHER:
      return "Other error";

   case VMGUESTLIB_ERROR_UNSUPPORTED_VERSION:
      return "Host does not support this request.";

   default:
      ASSERT(FALSE); // We want to catch this case in debug builds.
      return "Other error";
   }

   NOT_REACHED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLibCheckArgs --
 *
 *      Helper function to factor out common argument checking code.
 *
 *      If VMGUESTLIB_ERROR_SUCCESS is returned, args are valid and *data
 *      now points to the valid struct.
 *
 * Results:
 *      Error indicating results
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VMGuestLibError
VMGuestLibCheckArgs(VMGuestLibHandle handle, // IN
                    void *outArg,            // IN
                    void **data)             // OUT
{
   ASSERT(data);

   if (NULL == handle) {
      return VMGUESTLIB_ERROR_INVALID_HANDLE;
   }

   if (NULL == outArg) {
      return VMGUESTLIB_ERROR_INVALID_ARG;
   }

   *data = HANDLE_DATA(handle);

   if (0 == HANDLE_SESSIONID(handle)) {
      return VMGUESTLIB_ERROR_NO_INFO;
   }

   return VMGUESTLIB_ERROR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_OpenHandle --
 *
 *      Obtain a handle for use with GuestLib. Allocates resources and
 *      returns a handle that should be released using
 *      VMGuestLib_CloseHandle().
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      Resources are allocated.
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_OpenHandle(VMGuestLibHandle *handle) // OUT
{
   VMGuestLibHandleType *data;

   if (!VmCheck_IsVirtualWorld()) {
      Debug("%s: Not in a VM.\n", __FUNCTION__);
      return VMGUESTLIB_ERROR_NOT_RUNNING_IN_VM;
   }

   if (NULL == handle) {
      return VMGUESTLIB_ERROR_INVALID_ARG;
   }

   data = Util_SafeCalloc(1, sizeof *data);
   if (!data) {
      Debug("%s: Unable to allocate memory\n", __FUNCTION__);
      return VMGUESTLIB_ERROR_MEMORY;
   }

   *handle = (VMGuestLibHandle)data;
   return VMGUESTLIB_ERROR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_CloseHandle --
 *
 *      Release resources associated with a handle obtained from
 *      VMGuestLib_OpenHandle(). Handle is invalid after return
 *      from this call.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_CloseHandle(VMGuestLibHandle handle) // IN
{
   void *data;

   if (NULL == handle) {
      return VMGUESTLIB_ERROR_INVALID_HANDLE;
   }

   data = HANDLE_DATA(handle);
   if (data != NULL &&
       HANDLE_SESSIONID(handle) != 0 &&
       HANDLE_VERSION(handle) == 3) {
      VMGuestLibStatisticsV3 *v3stats = data;
      GuestLibV3StatCount count;

      for (count = 0; count < v3stats->numStats; count++) {
         VMX_XDR_FREE(xdr_GuestLibV3Stat, &v3stats->stats[count]);
      }
   }
   free(data);

   /* Be paranoid. */
   HANDLE_DATA(handle) = NULL;
   free(handle);

   return VMGUESTLIB_ERROR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLibUpdateInfo --
 *
 *      Retrieve the bundle of stats over the backdoor and update the pointer to
 *      the Guestlib info in the handle.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VMGuestLibError
VMGuestLibUpdateInfo(VMGuestLibHandle handle) // IN
{
   char *reply = NULL;
   size_t replyLen;
   VMGuestLibError ret = VMGUESTLIB_ERROR_INVALID_ARG;
   uint32 hostVersion = HANDLE_VERSION(handle);

   /*
    * Starting with the highest supported protocol (major) version, negotiate
    * down to the highest host supported version. Host supports minimum version
    * 2.
    */
   if (hostVersion == 0) {
      hostVersion = VMGUESTLIB_DATA_VERSION;
   }

   do {
      char commandBuf[64];
      unsigned int index = 0;

      /* Free the last reply when retrying. */
      free(reply);
      reply = NULL;

      /*
       * Construct command string with the command name and the version
       * of the data struct that we want.
       */
      Str_Sprintf(commandBuf, sizeof commandBuf, "%s %d",
                  VMGUESTLIB_BACKDOOR_COMMAND_STRING,
                  hostVersion);

      /* Send the request. */
      if (RpcChannel_SendOne(&reply, &replyLen, commandBuf)) {
         VMGuestLibDataV2 *v2reply = (VMGuestLibDataV2 *)reply;
         VMSessionId sessionId = HANDLE_SESSIONID(handle);

         ASSERT(hostVersion == v2reply->hdr.version);

         if (sessionId != 0 && sessionId != v2reply->hdr.sessionId) {
            /* Renegotiate protocol if sessionId changed. */
            hostVersion = VMGUESTLIB_DATA_VERSION;
            HANDLE_SESSIONID(handle) = 0;
            continue;
         }
         ret = VMGUESTLIB_ERROR_SUCCESS;
         break;
      }

      Debug("%s: Failed to retrieve info: %s\n",
            __FUNCTION__, reply ? reply : "NULL");

      /*
       * See why the request failed and either attempt a recovery action or
       * set an appropriate error code.  If the problem is that the host
       * is older and doesn't support the requested protocol version, then
       * try to determine the highest version the host supports and use that.
       */
      if (reply == NULL) {
         ret = VMGUESTLIB_ERROR_OTHER;
         break;
      } else if (hostVersion == 2 ||
          Str_Strncmp(reply, RPCI_UNKNOWN_COMMAND,
                      sizeof RPCI_UNKNOWN_COMMAND) == 0) {
         /*
          * Host does not support this feature. Older (v2) host would return
          * "Unsupported version" if it doesn't recognize the requested version.
          *
          * XXX: Maybe use another error code for this case where the host
          * product doesn't support this feature?
          */
         ret = VMGUESTLIB_ERROR_NOT_ENABLED;
         break;
      } else if (hostVersion == 3) {
         /*
          * Host supports v2 at a minimum. If request for v3 fails, then just
          * use v2, since v2 host does not send the highest supported version
          * in the reply.
          */
         hostVersion = 2;
         HANDLE_SESSIONID(handle) = 0;
         continue;
      } else if (!StrUtil_GetNextUintToken(&hostVersion, &index, reply, ":")) {
         /*
          * v3 and onwards, the host returns the highest major version it
          * supports, if the requested version is not supported. So parse
          * out the host version from the reply and return error if it didn't.
          */
         Debug("%s: Bad reply received from host.\n", __FUNCTION__);
         ret = VMGUESTLIB_ERROR_OTHER;
         break;
      }
      ASSERT(hostVersion < VMGUESTLIB_DATA_VERSION);
   } while (ret != VMGUESTLIB_ERROR_SUCCESS);

   if (ret != VMGUESTLIB_ERROR_SUCCESS) {
      goto done;
   }

   /* Confidence check the results. */
   if (replyLen < sizeof hostVersion) {
      Debug("%s: Unable to retrieve version\n", __FUNCTION__);
      ret = VMGUESTLIB_ERROR_OTHER;
      goto done;
   }

   if (hostVersion == 2) {
      VMGuestLibDataV2 *v2reply = (VMGuestLibDataV2 *)reply;
      size_t dataSize = sizeof *v2reply;

      /* More confidence checks. */
      if (v2reply->hdr.version != hostVersion) {
         Debug("%s: Incorrect data version returned\n", __FUNCTION__);
         ret = VMGUESTLIB_ERROR_OTHER;
         goto done;
      }
      if (replyLen != dataSize) {
         Debug("%s: Incorrect data size returned\n", __FUNCTION__);
         ret = VMGUESTLIB_ERROR_OTHER;
         goto done;
      }

      /* Store the reply in the handle. */
      HANDLE_VERSION(handle) = v2reply->hdr.version;
      HANDLE_SESSIONID(handle) = v2reply->hdr.sessionId;
      if (HANDLE_DATASIZE(handle) < dataSize) {
         /* [Re]alloc if the local handle buffer is not big enough. */
         free(HANDLE_DATA(handle));
         HANDLE_DATA(handle) = Util_SafeCalloc(1, dataSize);
         HANDLE_DATASIZE(handle) = dataSize;
      }
      memcpy(HANDLE_DATA(handle), reply, replyLen);

      /* Make sure resourcePoolPath is NUL terminated. */
      v2reply = HANDLE_DATA(handle);
      v2reply->resourcePoolPath.value[sizeof v2reply->resourcePoolPath.value - 1] = '\0';
      ret = VMGUESTLIB_ERROR_SUCCESS;
   } else if (hostVersion == 3) {
      VMGuestLibDataV3 *v3reply = (VMGuestLibDataV3 *)reply;
      size_t dataSize;
      XDR xdrs;
      GuestLibV3StatCount count;
      VMGuestLibStatisticsV3 *v3stats;

      /* More confidence checks. */
      if (v3reply->hdr.version != hostVersion) {
         Debug("%s: Incorrect data version returned\n", __FUNCTION__);
         ret = VMGUESTLIB_ERROR_OTHER;
         goto done;
      }
      if (replyLen < sizeof *v3reply) {
         Debug("%s: Incorrect data size returned\n", __FUNCTION__);
         ret = VMGUESTLIB_ERROR_OTHER;
         goto done;
      }

      /* 0. Copy the reply version and sessionId to the handle. */
      HANDLE_VERSION(handle) = v3reply->hdr.version;
      HANDLE_SESSIONID(handle) = v3reply->hdr.sessionId;

      /*
       * 1. Retrieve the length of the statistics array from the XDR encoded
       * part of the reply.
       */
      xdrmem_create(&xdrs, v3reply->data, v3reply->dataSize, XDR_DECODE);

      if (!xdr_GuestLibV3StatCount(&xdrs, &count)) {
         xdr_destroy(&xdrs);
         goto done;
      }
      if (count >= GUESTLIB_MAX_STATISTIC_ID) {
         /*
          * Host has more than we can process. So process only what this side
          * can.
          */
         count = GUESTLIB_MAX_STATISTIC_ID - 1;
      }

      /* 2. [Re]alloc if the local handle buffer is not big enough. */
      dataSize = sizeof *v3stats + (count * sizeof (GuestLibV3Stat));
      if (HANDLE_DATASIZE(handle) < dataSize) {
         free(HANDLE_DATA(handle));
         HANDLE_DATA(handle) = Util_SafeCalloc(1, dataSize);
         HANDLE_DATASIZE(handle) = dataSize;
      }

      /* 3. Unmarshal the array of statistics. */
      v3stats = HANDLE_DATA(handle);
      v3stats->numStats = count;
      for (count = 0; count < v3stats->numStats; count++) {
         GuestLibV3TypeIds statId = count + 1;

         /* Unmarshal the statistic. */
         if (!xdr_GuestLibV3Stat(&xdrs, &v3stats->stats[count])) {
            break;
         }

         /* Host sends all the V3 statistics it supports, in order. */
         if (v3stats->stats[count].d != statId) {
            break;
         }
      }
      if (count >= v3stats->numStats) {
         ret = VMGUESTLIB_ERROR_SUCCESS;
      } else {
         /*
          * Error while unmarshalling. Deep-free already unmarshalled
          * statistics and invalidate the data in the handle.
          */
         GuestLibV3StatCount c;

         for (c = 0; c < count; c++) {
            VMX_XDR_FREE(xdr_GuestLibV3Stat, &v3stats->stats[c]);
         }
         HANDLE_SESSIONID(handle) = 0;
      }

      /* 4. Free resources. */
      xdr_destroy(&xdrs);
   } else {
      /*
       * Host should never reply with a higher version protocol than requested.
       */
      ret = VMGUESTLIB_ERROR_OTHER;
   }

done:
   free(reply);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_UpdateInfo --
 *
 *      Update VMGuestLib's internal info state.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      Previous stat values will be overwritten.
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_UpdateInfo(VMGuestLibHandle handle) // IN
{
   VMGuestLibError error;

   if (NULL == handle) {
      return VMGUESTLIB_ERROR_INVALID_HANDLE;
   }

   /*
    * We check for virtual world in VMGuestLib_OpenHandle, so we don't
    * need to do the test again here.
    */

   error = VMGuestLibUpdateInfo(handle);
   if (error != VMGUESTLIB_ERROR_SUCCESS) {
      Debug("%s failed: %d\n", __FUNCTION__, error);
      HANDLE_SESSIONID(handle) = 0;
      return error;
   }

   return VMGUESTLIB_ERROR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetSessionId --
 *
 *      Retrieve the session ID for this virtual machine.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetSessionId(VMGuestLibHandle handle, // IN
                        VMSessionId *id)         // OUT
{
   void *data;
   VMGuestLibError error;

   error = VMGuestLibCheckArgs(handle, id, &data);
   if (VMGUESTLIB_ERROR_SUCCESS != error) {
      return error;
   }

   *id = HANDLE_SESSIONID(handle);

   return VMGUESTLIB_ERROR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLibGetStatisticsV3 --
 *
 *      Accessor helper to retrieve the requested V3 statistic.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VMGuestLibError
VMGuestLibGetStatisticsV3(VMGuestLibHandle handle,   // IN
                          GuestLibV3TypeIds statId,  // IN
                          GuestLibV3Stat *outStat)   // OUT
{
   VMGuestLibStatisticsV3 *stats = HANDLE_DATA(handle);
   uint32 statIdx = statId - 1;

   /*
    * Check that the requested statistic is supported by the host. V3 host sends
    * all the available statistics, in order. So any statistics that were added
    * to this version of guestlib, but unsupported by the host, would not be
    * sent by the host.
    */
   if (statIdx >= stats->numStats) {
      return VMGUESTLIB_ERROR_UNSUPPORTED_VERSION;
   }

   memcpy(outStat, &stats->stats[statIdx], sizeof *outStat);
   return VMGUESTLIB_ERROR_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetCpuReservationMHz --
 *
 *      Retrieve CPU min speed.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetCpuReservationMHz(VMGuestLibHandle handle,   // IN
                                uint32 *cpuReservationMHz) // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         cpuReservationMHz, cpuReservationMHz,
                         GUESTLIB_CPU_RESERVATION_MHZ);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetCpuLimitMHz --
 *
 *      Retrieve CPU max speed.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetCpuLimitMHz(VMGuestLibHandle handle, // IN
                          uint32 *cpuLimitMHz)     // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         cpuLimitMHz, cpuLimitMHz,
                         GUESTLIB_CPU_LIMIT_MHZ);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetCpuShares --
 *
 *      Retrieve CPU shares.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetCpuShares(VMGuestLibHandle handle, // IN
                        uint32 *cpuShares)       // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         cpuShares, cpuShares,
                         GUESTLIB_CPU_SHARES);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetCpuUsedMs --
 *
 *      Retrieve CPU used time.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetCpuUsedMs(VMGuestLibHandle handle, // IN
                        uint64 *cpuUsedMs)       // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         cpuUsedMs, cpuUsedMs,
                         GUESTLIB_CPU_USED_MS);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostProcessorSpeed --
 *
 *      Retrieve host processor speed. This can be used along with
 *      CpuUsedMs and elapsed time to estimate approximate effective
 *      VM CPU speed over a time interval.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostProcessorSpeed(VMGuestLibHandle handle, // IN
                                 uint32 *mhz)             // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         mhz, hostMHz,
                         GUESTLIB_HOST_MHZ);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VMGuestLib_GetMemReservationMB --
 *
 *      Retrieve min memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemReservationMB(VMGuestLibHandle handle,  // IN
                               uint32 *memReservationMB) // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memReservationMB, memReservationMB,
                         GUESTLIB_MEM_RESERVATION_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemLimitMB --
 *
 *      Retrieve max memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemLimitMB(VMGuestLibHandle handle, // IN
                         uint32 *memLimitMB)      // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memLimitMB, memLimitMB,
                         GUESTLIB_MEM_LIMIT_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemShares --
 *
 *      Retrieve memory shares.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemShares(VMGuestLibHandle handle, // IN
                        uint32 *memShares)       // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memShares, memShares,
                         GUESTLIB_MEM_SHARES);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemMappedMB --
 *
 *      Retrieve mapped memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemMappedMB(VMGuestLibHandle handle,  // IN
                          uint32 *memMappedMB)      // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memMappedMB, memMappedMB,
                         GUESTLIB_MEM_MAPPED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemActiveMB --
 *
 *      Retrieve active memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemActiveMB(VMGuestLibHandle handle, // IN
                          uint32 *memActiveMB)     // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memActiveMB, memActiveMB,
                         GUESTLIB_MEM_ACTIVE_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemOverheadMB --
 *
 *      Retrieve overhead memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemOverheadMB(VMGuestLibHandle handle, // IN
                            uint32 *memOverheadMB)   // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memOverheadMB, memOverheadMB,
                         GUESTLIB_MEM_OVERHEAD_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemBalloonedMB --
 *
 *      Retrieve ballooned memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemBalloonedMB(VMGuestLibHandle handle, // IN
                             uint32 *memBalloonedMB)  // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memBalloonedMB, memBalloonedMB,
                         GUESTLIB_MEM_BALLOONED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemSwappedMB --
 *
 *      Retrieve swapped memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemSwappedMB(VMGuestLibHandle handle, // IN
                           uint32 *memSwappedMB)    // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                                memSwappedMB, memSwappedMB,
                                GUESTLIB_MEM_SWAPPED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemSharedMB --
 *
 *      Retrieve shared memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemSharedMB(VMGuestLibHandle handle, // IN
                          uint32 *memSharedMB)     // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memSharedMB, memSharedMB,
                         GUESTLIB_MEM_SHARED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemSharedSavedMB --
 *
 *      Retrieve shared saved memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemSharedSavedMB(VMGuestLibHandle handle,  // IN
                               uint32 *memSharedSavedMB) // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memSharedSavedMB, memSharedSavedMB,
                         GUESTLIB_MEM_SHARED_SAVED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemUsedMB --
 *
 *      Retrieve used memory.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemUsedMB(VMGuestLibHandle handle, // IN
                        uint32 *memUsedMB)       // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         memUsedMB, memUsedMB,
                         GUESTLIB_MEM_USED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetElapsedMs --
 *
 *      Retrieve elapsed time on the host.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetElapsedMs(VMGuestLibHandle handle, // IN
                        uint64 *elapsedMs)       // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETFN_BODY(handle, error,
                         elapsedMs, elapsedMs,
                         GUESTLIB_ELAPSED_MS);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetResourcePoolPath --
 *
 *      Retrieve Resource Pool Path.
 *
 *      pathBuffer is a pointer to a buffer that will receive the
 *      resource pool path string. bufferSize is a pointer to the
 *      size of the pathBuffer in bytes. If bufferSize is not large
 *      enough to accomodate the path and NUL terminator, then
 *      VMGUESTLIB_ERROR_BUFFER_TOO_SMALL is returned and bufferSize
 *      contains the amount of memory needed (in bytes).
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetResourcePoolPath(VMGuestLibHandle handle, // IN
                               size_t *bufferSize,      // IN/OUT
                               char *pathBuffer)        // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;

   do {
      size_t size;
      if (NULL == handle) {
         error = VMGUESTLIB_ERROR_INVALID_HANDLE;
         break;
      }

      if (NULL == bufferSize || NULL == pathBuffer) {
         error = VMGUESTLIB_ERROR_INVALID_ARG;
         break;
      }

      if (0 == HANDLE_SESSIONID(handle)) {
         error = VMGUESTLIB_ERROR_NO_INFO;
         break;
      }

      if (HANDLE_VERSION(handle) == 2) {
         VMGuestLibDataV2 *dataV2;

         dataV2 = (VMGuestLibDataV2 *)HANDLE_DATA(handle);

         if (!dataV2->resourcePoolPath.valid) {
            error = VMGUESTLIB_ERROR_NOT_AVAILABLE;
            break;
         }

         /*
          * Check buffer size. It's safe to use strlen because we squash the
          * final byte of resourcePoolPath in VMGuestLibUpdateInfo.
          */
         size = strlen(dataV2->resourcePoolPath.value) + 1;
         if (*bufferSize < size) {
            *bufferSize = size;
            error = VMGUESTLIB_ERROR_BUFFER_TOO_SMALL;
            break;
         }

         memcpy(pathBuffer, dataV2->resourcePoolPath.value, size);
         error = VMGUESTLIB_ERROR_SUCCESS;
      } else if (HANDLE_VERSION(handle) == 3) {
         VMGuestLibStatisticsV3 *stats = HANDLE_DATA(handle);
         GuestLibV3Stat *stat = &stats->stats[GUESTLIB_RESOURCE_POOL_PATH - 1];

         if (!stat->GuestLibV3Stat_u.resourcePoolPath.valid) {
            error = VMGUESTLIB_ERROR_NOT_AVAILABLE;
            break;
         }

         size = strlen(stat->GuestLibV3Stat_u.resourcePoolPath.value) + 1;
         if (*bufferSize < size) {
            *bufferSize = size;
            error = VMGUESTLIB_ERROR_BUFFER_TOO_SMALL;
            break;
         }
         memcpy(pathBuffer, stat->GuestLibV3Stat_u.resourcePoolPath.value, size);
         error = VMGUESTLIB_ERROR_SUCCESS;
      }
   } while (0);

   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetCpuStolenMs --
 *
 *      Retrieve CPU stolen.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetCpuStolenMs(VMGuestLibHandle handle, // IN
                          uint64 *cpuStolenMs)     // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         cpuStolenMs, cpuStolenMs,
                         GUESTLIB_CPU_STOLEN_MS);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemTargetSizeMB --
 *
 *      Retrieve Memory Target Size.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemTargetSizeMB(VMGuestLibHandle handle, // IN
                              uint64 *memTargetSizeMB) // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         memTargetSizeMB, memTargetSizeMB,
                         GUESTLIB_MEM_TARGET_SIZE_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostNumCpuCores --
 *
 *      Number of physical CPU cores on the host machine.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostNumCpuCores(VMGuestLibHandle handle, // IN
                              uint32 *hostNumCpuCores) // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostNumCpuCores, hostCpuNumCores,
                         GUESTLIB_HOST_CPU_NUM_CORES);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostCpuUsedMs --
 *
 *      Total CPU time used by host.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostCpuUsedMs(VMGuestLibHandle handle,  // IN
                            uint64 *hostCpuUsedMs)    // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostCpuUsedMs, hostCpuUsedMs,
                         GUESTLIB_HOST_CPU_USED_MS);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostMemSwappedMB --
 *
 *      Total swapped out memory on the host.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostMemSwappedMB(VMGuestLibHandle handle,  // IN
                               uint64 *hostMemSwappedMB) // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostMemSwappedMB, hostMemSwappedMB,
                         GUESTLIB_HOST_MEM_SWAPPED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostMemSharedMB --
 *
 *      Total COW (Copy-On-Write) memory on host.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostMemSharedMB(VMGuestLibHandle handle, // IN
                              uint64 *hostMemSharedMB) // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostMemSharedMB, hostMemSharedMB,
                         GUESTLIB_HOST_MEM_SHARED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostMemUsedMB --
 *
 *      Total consumed memory on host.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostMemUsedMB(VMGuestLibHandle handle,  // IN
                            uint64 *hostMemUsedMB)    // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostMemUsedMB, hostMemUsedMB,
                         GUESTLIB_HOST_MEM_USED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostMemPhysMB --
 *
 *      Total memory available to host OS kernel.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostMemPhysMB(VMGuestLibHandle handle,  // IN
                            uint64 *hostMemPhysMB)    // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostMemPhysMB, hostMemPhysMB,
                         GUESTLIB_HOST_MEM_PHYS_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostMemPhysFreeMB --
 *
 *      Total phsyical memory free on host.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostMemPhysFreeMB(VMGuestLibHandle handle,    // IN
                                uint64 *hostMemPhysFreeMB)  // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostMemPhysFreeMB, hostMemPhysFreeMB,
                         GUESTLIB_HOST_MEM_PHYS_FREE_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostMemKernOvhdMB --
 *
 *      Total host kernel memory overhead.
 *
 *      Note: This function is deprecated. Will be removed in the future
 *      releases. hostMemKernOvhdMB is always set to 0.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostMemKernOvhdMB(VMGuestLibHandle handle,     // IN
                                uint64 *hostMemKernOvhdMB)   // OUT
{
   void *data;
   VMGuestLibError error;

   error = VMGuestLibCheckArgs(handle, hostMemKernOvhdMB, &data);
   if (VMGUESTLIB_ERROR_SUCCESS != error) {
      return error;
   }

   Debug("%s API is deprecated and will be removed in the future releases.\n",
         __FUNCTION__);

   *hostMemKernOvhdMB = 0;
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostMemMappedMB --
 *
 *      Total mapped memory on host.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostMemMappedMB(VMGuestLibHandle handle,  // IN
                              uint64 *hostMemMappedMB)  // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostMemMappedMB, hostMemMappedMB,
                         GUESTLIB_HOST_MEM_MAPPED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetHostMemUnmappedMB --
 *
 *      Total unmapped memory on host.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetHostMemUnmappedMB(VMGuestLibHandle handle,    // IN
                                uint64 *hostMemUnmappedMB)  // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         hostMemUnmappedMB, hostMemUnmappedMB,
                         GUESTLIB_HOST_MEM_UNMAPPED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemZippedMB --
 *
 *      Total zipped VM memory
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemZippedMB(VMGuestLibHandle handle,    // IN
                          uint32 *memZippedMB)        // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         memZippedMB, memZippedMB,
                         GUESTLIB_MEM_ZIPPED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemZipSavedMB --
 *
 *      Total memopry saved by zipping VM memory
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemZipSavedMB(VMGuestLibHandle handle,    // IN
                            uint32 *memZipSavedMB)      // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         memZipSavedMB, memZipSavedMB,
                         GUESTLIB_MEM_ZIPSAVED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemLLSwappedMB --
 *
 *      VM memory swapped to SSD
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemLLSwappedMB(VMGuestLibHandle handle,    // IN
                             uint32 *memLLSwappedMB)     // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         memLLSwappedMB, memLLSwappedMB,
                         GUESTLIB_MEM_LLSWAPPED_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemSwapTargetMB --
 *
 *      VM memory swap target
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemSwapTargetMB(VMGuestLibHandle handle,    // IN
                              uint32 *memSwapTargetMB)    // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         memSwapTargetMB, memSwapTargetMB,
                         GUESTLIB_MEM_SWAP_TARGET_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemBalloonTargetMB --
 *
 *      VM memory balloon target size
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemBalloonTargetMB(VMGuestLibHandle handle,    // IN
                                 uint32 *memBalloonTargetMB) // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         memBalloonTargetMB, memBalloonTargetMB,
                         GUESTLIB_MEM_BALLOON_TARGET_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemBalloonMaxMB --
 *
 *      VM memory balloon limit
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemBalloonMaxMB(VMGuestLibHandle handle,    // IN
                              uint32 *memBalloonMaxMB)    // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         memBalloonMaxMB, memBalloonMaxMB,
                         GUESTLIB_MEM_BALLOON_MAX_MB);
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_GetMemShares64 --
 *
 *      Retrieve memory shares.
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_GetMemShares64(VMGuestLibHandle handle, // IN
                          uint64 *memShares64)     // OUT
{
   VMGuestLibError error = VMGUESTLIB_ERROR_OTHER;
   VMGUESTLIB_GETSTAT_V3(handle, error,
                         memShares64, memShares64,
                         GUESTLIB_MEM_SHARES_64);
   if (VMGUESTLIB_ERROR_UNSUPPORTED_VERSION == error) {
      /*
       * GUESTLIB_MEM_SHARES_64 is available only from ESX 7.0.
       * If the host is older, then return the value of GUESTLIB_MEM_SHARES.
       */
      uint32 memShares = 0;
      if (VMGUESTLIB_ERROR_SUCCESS !=
         VMGuestLib_GetMemShares(handle, &memShares)) {
         return error;
      } else {
         *memShares64 = memShares;
         return VMGUESTLIB_ERROR_SUCCESS;
      }
   }
   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLibIoctl --
 *
 *      Marshal and invoke the guestlib ioctl.
 *
 * Results:
 *      TRUE on success, FALSE otherwise (reply contains error detail, if
 *      provided)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VMGuestLibIoctl(const GuestLibIoctlParam *param,
                char **reply,
                size_t *replySize)
{
   XDR xdrs;
   Bool ret;
   static const char *request = VMGUESTLIB_IOCTL_COMMAND_STRING " ";

   if (param == NULL || param->d >= GUESTLIB_IOCTL_MAX) {
      return FALSE;
   }
   if (NULL == DynXdr_Create(&xdrs)) {
      return FALSE;
   }
   if (!DynXdr_AppendRaw(&xdrs, request, strlen(request)) ||
       !xdr_GuestLibIoctlParam(&xdrs, (GuestLibIoctlParam *)param)) {

      /*
       * DynXdr_Destroy only tries to free storage returned by a call to
       * DynXdr_Create(NULL).
       */
      /* coverity[address_free] */
      DynXdr_Destroy(&xdrs, TRUE);
      return FALSE;
   }
   ret = RpcChannel_SendOneRaw(DynXdr_Get(&xdrs), xdr_getpos(&xdrs),
                               reply, replySize);

   /*
    * DynXdr_Destroy only tries to free storage returned by a call to
    * DynXdr_Create(NULL).
    */
   /* coverity[address_free] */
   DynXdr_Destroy(&xdrs, TRUE);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_AtomicUpdateCookie --
 *
 *      Atomically update a cookie on the host.
 *
 * Results:
 *      TRUE on success, FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
VMGuestLib_AtomicUpdateCookie(const char *src,    // IN
                              const char *dst,    // IN
                              char **reply,       // OUT
                              size_t *replySize)  // OUT
{
   GuestLibIoctlParam param;

   ASSERT(src != NULL);
   ASSERT(dst != NULL);

   param.d = GUESTLIB_IOCTL_ATOMIC_UPDATE_COOKIE;
   param.GuestLibIoctlParam_u.atomicUpdateCookie.src = (char *)src;
   param.GuestLibIoctlParam_u.atomicUpdateCookie.dst = (char *)dst;
   return VMGuestLibIoctl(&param, reply, replySize);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_StatGet --
 *
 *      Fetches semi-structured stat information from the hypervisor.
 *      Pass to VMGuestLib_StatFree() to release.
 *
 *      Encodings: 'json', 'text', 'xml', 'yaml'
 *      Stats: pass NULL or "" to receive a list of available stats
 *      Some stats are two words, e.g. "vscsi scsi0:0"
 *
 *      NOTE: stats, their meaning and availability may change from release
 *      to release as the underlying implementation evolves.
 *      This information is intended for troubleshooting only!
 *
 * Results:
 *      VMGuestLibError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VMGuestLibError
VMGuestLib_StatGet(const char *encoding,  // IN
                   const char *stat,      // IN
                   char **reply,          // OUT
                   size_t *replySize)     // OUT
{
   char commandBuf[256];
   int bufRet;
   Bool ret;

   if (encoding == NULL) {
      return VMGUESTLIB_ERROR_INVALID_ARG;
   }
   if (stat == NULL) {
      stat = "";  // Dummy value
   }

   /*
    * Construct command string with the command name and the name
    * of the stat that we want.
    */
   bufRet = Str_Snprintf(commandBuf, sizeof commandBuf, "%s %s %s",
                         VMGUESTLIB_STATDATA_COMMAND_STRING, encoding, stat);
   if (bufRet == -1 || bufRet >= sizeof commandBuf) {
      return VMGUESTLIB_ERROR_BUFFER_TOO_SMALL;
   }

   ret = RpcChannel_SendOneRaw(commandBuf, strlen(commandBuf),
                               reply, replySize);

   return ret ? VMGUESTLIB_ERROR_SUCCESS : VMGUESTLIB_ERROR_NOT_AVAILABLE;
}

/*
 *-----------------------------------------------------------------------------
 *
 * VMGuestLib_StatFree --
 *
 *      Frees a stat fetched by VMGuestLib_StatGet()
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
VMGuestLib_StatFree(char *reply,       // IN
                    size_t replySize)  // IN
{
   free(reply);
}
0707010000022E000081A400000000000000000000000168225505000015EC000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/libguestlib/vmGuestLibInt.h    /*********************************************************
 * Copyright (C) 2003-2017,2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VM_GUEST_LIB_INT_H_
#define _VM_GUEST_LIB_INT_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "vmware.h"
#include "vmGuestLib.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Backdoor string for retrieving guestlib info
 */

#define VMGUESTLIB_BACKDOOR_COMMAND_STRING "guestlib.info.get"
#define VMGUESTLIB_STATDATA_COMMAND_STRING "guestlib.stat.get"


/*
 * Current version of the VMGuestLibData structure
 */

#define   VMGUESTLIB_DATA_VERSION 3


/* Stat types with a valid bit per stat. */

typedef struct {
   Bool valid;   // Indicates whether this stat is valid on this system
   uint32 value; // Actual stat value.
} StatUint32;

/*
 * This structure comes from the backdoor and hence uses 32-bit
 * natural packing, which is 4-byte aligned. When moved over to
 * 64-bit VMs it becomes 8-byte aligned. To avoid this varying 
 * padding, adding the 3-byte "padding" field and using pack(1)
 * to make sure we always have 4-byte alignment. 
 */
#pragma pack(push, 1)
typedef struct {
   Bool valid;       // Indicates whether this stat is valid on this system
   uint8 padding[3]; 
   uint64 value;     // Actual stat value.
} StatUint64;
#pragma pack(pop)



/*
 * This is version 1 of the data structure GuestLib uses to obtain
 * stats over the backdoor from the VMX/VMKernel. It is deprecated.
 */
#if 0

#pragma pack(push, 1)
typedef struct VMGuestLibDataV1 {
   uint32 version;
   VMSessionId sessionId;

   /* Statistics */

   /* CPU statistics */
   uint32 cpuReservationMHz;
   uint32 cpuLimitMHz;
   uint32 cpuShares;
   uint64 cpuUsedMs;

   /* Host processor speed */
   uint32 hostMHz;

   /* Memory statistics */
   uint32 memReservationMB;
   uint32 memLimitMB;
   uint32 memShares;
   uint32 memMappedMB;
   uint32 memActiveMB;
   uint32 memOverheadMB;
   uint32 memBalloonedMB;
   uint32 memSwappedMB;

   /* Elapsed time */
   uint64 elapsedMs;

   /*
    * Resource pool path. See groupPathName in Sched_GuestLibInfo,
    * defined in VMKernel's sched_ext.h. This needs to be at least
    * as big as SCHED_GROUP_PATHNAME_LEN.
    */
   char resourcePoolPath[512];
} VMGuestLibDataV1;
#pragma pack(pop)

#endif // #if 0

#pragma pack(push, 1)
typedef struct {
   uint32 version;
   VMSessionId sessionId;
} VMGuestLibHeader;
#pragma pack(pop)

/*
 * This is version 2 of the data structure GuestLib uses to obtain
 * stats over the backdoor from the VMX/VMKernel. It is not
 * exposed to users of the GuestLib API.
 */

#pragma pack(push, 1)
typedef struct VMGuestLibDataV2 {
   /* Header */
   VMGuestLibHeader hdr;

   /* Statistics */

   /* CPU statistics */
   StatUint32 cpuReservationMHz;
   StatUint32 cpuLimitMHz;
   StatUint32 cpuShares;
   StatUint64 cpuUsedMs;

   /* Host processor speed */
   StatUint32 hostMHz;

   /* Memory statistics */
   StatUint32 memReservationMB;
   StatUint32 memLimitMB;
   StatUint32 memShares;
   StatUint32 memMappedMB;
   StatUint32 memActiveMB;
   StatUint32 memOverheadMB;
   StatUint32 memBalloonedMB;
   StatUint32 memSwappedMB;
   StatUint32 memSharedMB;
   StatUint32 memSharedSavedMB;
   StatUint32 memUsedMB;

   /* Elapsed time */
   StatUint64 elapsedMs;

   /*
    * Resource pool path. See groupPathName in Sched_GuestLibInfo,
    * defined in VMKernel's sched_ext.h. This needs to be at least
    * as big as SCHED_GROUP_PATHNAME_LEN.
    */
   struct {
      Bool valid;
      char value[512];
   } resourcePoolPath;
} VMGuestLibDataV2;
#pragma pack(pop)


/*
 * This is version 3 of the data structure GuestLib uses to obtain stats over
 * the backdoor from the VMX/VMKernel. It is not exposed to users of the
 * GuestLib API. This struct is sent on the wire.
 *
 * The buffer contains a variable length array of statistics, that are
 * marshalled from XDR spec generated code. Each data field has a discriminant
 * preceding the payload, which enables the client to detect fields that it
 * doesn't recognize.
 *
 * V3 is a superset of V2 and a major protocol change. Any extensions to the
 * wire protocol that just add statistics, can do so within V3. Just update the
 * .x file to add a new discriminant for the union, at the end of the list. V3
 * clients may assume that the fields are ordered on the wire in increasing order 
 * of the discriminant list. So, a client may stop processing at the first 
 * unrecognized field. V3 payload contains all available guestlib statistics
 * supported by the host.
 */

#pragma pack(push, 1)
typedef struct VMGuestLibDataV3 {
   /* Header */
   VMGuestLibHeader hdr;

   /* Statistics */
   uint32 dataSize;
   char data[0];
} VMGuestLibDataV3;
#pragma pack(pop)

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _VM_GUEST_LIB_INT_H_ */
0707010000022F000081A40000000000000000000000016822550500000144000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/libguestlib/vmguestlib.pc.in   # VMware GuestLib pkgconfig data.

prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@

Name:          VMware GuestLib
Description:   Library for retrieving information from VMware hosts.
Version:       @VERSION@
Libs:          -L${libdir} -lguestlib
Cflags:        -I${includedir}/vmGuestLib

07070100000230000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/libhgfs    07070100000231000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/libhgfs/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000232000081A4000000000000000000000001682255050000070B000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/libhgfs/Makefile.am    ################################################################################
### Copyright (C) 2010-2016,2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

lib_LTLIBRARIES = libhgfs.la

libhgfs_la_LIBADD =
libhgfs_la_LIBADD += ../lib/hgfs/libHgfs.la
libhgfs_la_LIBADD += ../lib/hgfsHelper/libHgfsHelper.la
libhgfs_la_LIBADD += ../lib/hgfsServer/libHgfsServer.la
if HAVE_GTKMM
libhgfs_la_LIBADD += ../lib/hgfsUri/libHgfsUri.la
endif
libhgfs_la_LIBADD += ../lib/hgfsServerManagerGuest/libHgfsServerManagerGuest.la
libhgfs_la_LIBADD += ../lib/hgfsServerPolicyGuest/libHgfsServerPolicyGuest.la
libhgfs_la_LIBADD += @GLIB2_LIBS@
libhgfs_la_LIBADD += @GTHREAD_LIBS@
libhgfs_la_LIBADD += @VMTOOLS_LIBS@

libhgfs_la_SOURCES =
libhgfs_la_SOURCES += hgfslib.c

libhgfs_la_CPPFLAGS =
libhgfs_la_CPPFLAGS += -I$(top_srcdir)/libvmtools

libhgfs_la_LDFLAGS =
# We require GCC, so we're fine passing compiler-specific flags.
libhgfs_la_LDFLAGS += -Wl,-z,defs
# Needed for OS's that don't link shared libraries against libc by
#default, e.g. FreeBSD
libhgfs_la_LIBADD += -lc

 07070100000233000081A40000000000000000000000016822550500000683000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/libhgfs/hgfslib.c  /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file hgfslib.c
 *
 * Library entry point.
 */

#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolslib_version.h"
VM_EMBED_VERSION(VMTOOLSLIB_VERSION_STRING);

#if defined(_WIN32)

#include <windows.h>

/*
 ******************************************************************************
 * DllMain --                                                           */ /**
 *
 * Library entry point, currently does nothing.
 *
 * @param[in] hinstDLL        Unused.
 * @param[in] fdwReason       Unused.
 * @param[in] lpvReserved     Unused.
 *
 * @return TRUE.
 *
 ******************************************************************************
 */

BOOL WINAPI
DllMain(HINSTANCE hinstDLL,
        DWORD fdwReason,
        LPVOID lpvReserved)
{
   return TRUE;
}

#endif

 07070100000234000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/libvmtools 07070100000235000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000236000081A40000000000000000000000016822550500000FA0000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/Makefile.am ################################################################################
### Copyright (c) 2008-2021,2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

lib_LTLIBRARIES = libvmtools.la

libvmtools_la_LIBADD =
libvmtools_la_LIBADD += ../lib/lock/libLock.la
libvmtools_la_LIBADD += ../lib/backdoor/libBackdoor.la
if HAVE_VSOCK
libvmtools_la_LIBADD += ../lib/asyncsocket/libAsyncSocket.la
endif
libvmtools_la_LIBADD += ../lib/sslDirect/libSslDirect.la
libvmtools_la_LIBADD += ../lib/pollGtk/libPollGtk.la
libvmtools_la_LIBADD += ../lib/poll/libPoll.la
libvmtools_la_LIBADD += ../lib/dataMap/libDataMap.la
libvmtools_la_LIBADD += ../lib/hashMap/libHashMap.la
libvmtools_la_LIBADD += ../lib/dict/libDict.la
libvmtools_la_LIBADD += ../lib/dynxdr/libDynxdr.la
libvmtools_la_LIBADD += ../lib/err/libErr.la
libvmtools_la_LIBADD += ../lib/file/libFile.la
libvmtools_la_LIBADD += ../lib/glibUtils/libGlibUtils.la
libvmtools_la_LIBADD += ../lib/guestApp/libGuestApp.la
libvmtools_la_LIBADD += ../lib/guestRpc/libGuestRpc.la
if LINUX
   libvmtools_la_LIBADD += ../lib/guestStoreClientHelper/libGuestStoreClientHelper.la
endif
libvmtools_la_LIBADD += ../lib/message/libMessage.la
libvmtools_la_LIBADD += ../lib/netUtil/libNetUtil.la
libvmtools_la_LIBADD += ../lib/nicInfo/libNicInfo.la
libvmtools_la_LIBADD += ../lib/panic/libPanic.la
libvmtools_la_LIBADD += ../lib/procMgr/libProcMgr.la
libvmtools_la_LIBADD += ../lib/rpcChannel/libRpcChannel.la
libvmtools_la_LIBADD += ../lib/rpcIn/libRpcIn.la
libvmtools_la_LIBADD += ../lib/rpcOut/libRpcOut.la
libvmtools_la_LIBADD += ../lib/rpcVmx/libRpcVmx.la
libvmtools_la_LIBADD += ../lib/string/libString.la
libvmtools_la_LIBADD += ../lib/jsmn/libJsmn.la
libvmtools_la_LIBADD += ../lib/syncDriver/libSyncDriver.la
libvmtools_la_LIBADD += ../lib/system/libSystem.la
libvmtools_la_LIBADD += ../lib/stubs/libStubsCS.la
libvmtools_la_LIBADD += ../lib/unicode/libUnicode.la
libvmtools_la_LIBADD += ../lib/user/libUser.la
libvmtools_la_LIBADD += ../lib/vmCheck/libVmCheck.la
libvmtools_la_LIBADD += ../lib/vmSignal/libVmSignal.la
libvmtools_la_LIBADD += ../lib/wiper/libWiper.la
libvmtools_la_LIBADD += ../lib/misc/libMisc.la
libvmtools_la_LIBADD += @LIBVMTOOLS_LIBADD@
libvmtools_la_LIBADD += @GLIB2_LIBS@
libvmtools_la_LIBADD += @ICU_LIBS@

if USE_SLASH_PROC
libvmtools_la_LIBADD += ../lib/slashProc/libSlashProc.la
endif

libvmtools_la_SOURCES =
libvmtools_la_SOURCES += i18n.c
libvmtools_la_SOURCES += monotonicTimer.c
libvmtools_la_SOURCES += signalSource.c
libvmtools_la_SOURCES += vmtools.c
libvmtools_la_SOURCES += vmtoolsConfig.c
libvmtools_la_SOURCES += vmtoolsLog.c
libvmtools_la_SOURCES += vmxLogger.c

# Recompile the stub for Log_* functions, but not Log() itself (see -DNO_LOG_STUB).
libvmtools_la_SOURCES += $(top_srcdir)/lib/stubs/stub-log.c

libvmtools_la_CPPFLAGS =
libvmtools_la_CPPFLAGS += -DVMTOOLS_USE_GLIB
libvmtools_la_CPPFLAGS += -DNO_LOG_STUB
libvmtools_la_CPPFLAGS += -DVMTOOLS_DATA_DIR=\"$(datadir)/open-vm-tools\"
libvmtools_la_CPPFLAGS += @GLIB2_CPPFLAGS@

libvmtools_la_LDFLAGS =
# We require GCC, so we're fine passing compiler-specific flags.
libvmtools_la_LDFLAGS += -Wl,-z,defs
# Needed for OS's that don't link shared libraries against libc by
#default, e.g. FreeBSD
libvmtools_la_LIBADD += -lc

07070100000237000081A40000000000000000000000016822550500005592000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/i18n.c  /*********************************************************
 * Copyright (C) 2010-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file i18n.c
 *
 * Implementation of the i18n-related functions of the Tools library.
 */

#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <glib.h>
#include <glib/gprintf.h>

#include "vmware.h"
#include "dictll.h"
#include "escape.h"
#include "file.h"
#include "guestApp.h"
#include "hashTable.h"
#include "str.h"
#include "unicode.h"
#include "vmtoolsInt.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/utils.h"

/* These come from msgid.h. See MsgHasMsgID for explanation. */
#define MSG_MAX_ID      128
/* The X hides MSG_MAGIC so it won't appear in the object file. */
#define MSG_MAGICAL(s)  (strncmp(s, MSG_MAGIC"X", MSG_MAGIC_LEN) == 0)

typedef struct MsgCatalog {
   HashTable  *utf8;
#if defined(_WIN32)
   HashTable  *utf16;
#endif
} MsgCatalog;


typedef struct MsgState {
   HashTable     *domains; /* List of text domains. */
   GMutex         lock;    /* Mutex to protect shared state. */
} MsgState;


static MsgState *gMsgState = NULL;


/*
 ******************************************************************************
 * MsgCatalogFree --                                                    */ /**
 *
 * Frees memory allocated for a MsgCatalog structure.
 *
 * @param[in] catalog    The catalog to free.
 *
 ******************************************************************************
 */

static void
MsgCatalogFree(MsgCatalog *catalog)
{
   ASSERT(catalog);
#if defined(_WIN32)
   if (catalog->utf16 != NULL) {
      HashTable_Free(catalog->utf16);
   }
#endif
   if (catalog->utf8 != NULL) {
      HashTable_Free(catalog->utf8);
   }
   g_free(catalog);
}


/*
 ******************************************************************************
 * MsgHasMsgID --                                                       */ /**
 *
 * Check that a string has a message ID. The full "MSG_MAGIC(...)" prefix is
 * required, not just MSG_MAGIC.
 *
 * XXX: copied from msgid.h. We can't include that file since we redefine
 * the MSGID macro in our public header, to avoid exposing internal headers.
 *
 * @param[in] s    String to check.
 *
 * @return TRUE if the string has a message id.
 *
 ******************************************************************************
 */

#ifdef VMX86_DEBUG
static INLINE gboolean
MsgHasMsgID(const gchar *s)
{
   return MSG_MAGICAL(s) &&
          *(s += MSG_MAGIC_LEN) == '(' &&
          strchr(s + 1, ')') != NULL;
}
#endif

/*
 ******************************************************************************
 * MsgInitState --                                                      */ /**
 *
 * Initializes the global message state.
 *
 * @param[in] unused   Unused.
 *
 * @return NULL.
 *
 ******************************************************************************
 */

static gpointer
MsgInitState(gpointer unused)
{
   ASSERT(gMsgState == NULL);
   gMsgState = g_new0(MsgState, 1);
   g_mutex_init(&gMsgState->lock);
   return NULL;
}


/*
 ******************************************************************************
 * MsgGetState --                                                       */ /**
 *
 * Get the internal msg state (lazily initialized if needed).
 *
 * @return The message state object.
 *
 ******************************************************************************
 */

static INLINE MsgState *
MsgGetState(void)
{
   static GOnce msgStateInit = G_ONCE_INIT;
   g_once(&msgStateInit, MsgInitState, NULL);
   return gMsgState;
}


/*
 ******************************************************************************
 * MsgGetCatalog --                                                     */ /**
 *
 * Retrives the message catalog for a specific domain. This function is not
 * thread-safe, so make sure calls to it are properly protected.
 *
 * @param[in] domain   The domain name (NULL for default).
 *
 * @return The catalog. If a specific catalog is not found, the default one
 *         is returned (which may be NULL).
 *
 ******************************************************************************
 */

static INLINE MsgCatalog *
MsgGetCatalog(const char *domain)
{
   MsgState *state = MsgGetState();
   MsgCatalog *catalog = NULL;

   ASSERT(domain != NULL);

   if (state->domains != NULL) {
      MsgCatalog *domaincat;
      if (HashTable_Lookup(state->domains, domain, (void **) &domaincat)) {
         catalog = domaincat;
      }
   }

   return catalog;
}


/*
 ******************************************************************************
 * MsgGetUserLanguage --                                                */ /**
 *
 * Returns a string describing the user's default language using the
 * "language[_territory]" format (ISO 639-1 and ISO 3166-1, respectively) as
 * described in the setlocale(3) man page.
 *
 * @return Language code (caller should free).
 *
 ******************************************************************************
 */

static gchar *
MsgGetUserLanguage(void)
{
   gchar *lang;

#if defined(_WIN32)
   /*
    * Windows implementation. Derive the ISO names from the user's current
    * locale.
    */
   wchar_t ctryName[10]; /* MSDN says: max is nine characters + terminator. */
   wchar_t langName[10]; /* MSDN says: max is nine characters + terminator. */

   if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME,
                     ctryName, ARRAYSIZE(ctryName)) == 0 ||
       GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME,
                     langName, ARRAYSIZE(langName)) == 0) {
      g_warning("Couldn't retrieve user locale data, error = %u.", GetLastError());
      lang = g_strdup("C");
   } else {
      lang = g_strdup_printf("%S_%S", langName, ctryName);
   }
#else
   /*
    * POSIX implementation: just use setlocale() to query the data. Ignore any
    * codeset information.
    */
   char *tmp = setlocale(LC_MESSAGES, NULL);
   if (tmp == NULL) {
      lang = g_strdup("C");
   } else {
      char *dot;

      lang = g_strdup(tmp);
      dot = strchr(lang, '.');
      if (dot != NULL) {
         *dot = '\0';
      }
   }
#endif

   return lang;
}


/*
 ******************************************************************************
 * MsgSetCatalog --                                                     */ /**
 *
 * Set the message catalog for a given domain. A NULL catalog clears the
 * current one. This function is not thread-safe, so make sure calls to it
 * are properly protected.
 *
 * @param[in] domain    The text domain being bound.
 * @param[in] catalog   The new message catalog.
 *
 ******************************************************************************
 */

static void
MsgSetCatalog(const char *domain,
              MsgCatalog *catalog)
{
   MsgState *state = MsgGetState();

   ASSERT(domain);

   if (state->domains == NULL) {
      state->domains = HashTable_Alloc(8, HASH_STRING_KEY | HASH_FLAG_COPYKEY,
                                       (HashTableFreeEntryFn) MsgCatalogFree);
      ASSERT_MEM_ALLOC(state->domains);
   }

   HashTable_ReplaceOrInsert(state->domains, domain, catalog);
}


/*
 ******************************************************************************
 * MsgGetString --                                                      */ /**
 *
 * Retrieves a localized string in the requested encoding.
 *
 * All messages are retrieved based on the catalog data loaded in UTF-8.
 * Strings in other encodings are lazily converted from the UTF-8 version
 * as they are queried.
 *
 * @param[in] msgid        The message id, including the default English text.
 * @param[in] catalog      Catalog from where to fetch the messages.
 * @param[in] encoding     The desired encoding. Only STRING_ENCODING_UTF8 and
 *                         STRING_ENCODING_UTF16_LE (Win32) are supported by
 *                         this function.
 *
 * @return The string in the desired encoding.
 *
 ******************************************************************************
 */

static const void *
MsgGetString(const char *domain,
             const char *msgid,
             StringEncoding encoding)
{
   const char *idp;
   const char *strp;
   char idBuf[MSG_MAX_ID];
   size_t len;
   HashTable *source = NULL;
   MsgCatalog *catalog;
   MsgState *state = MsgGetState();

   /* All message strings must be prefixed by the message ID. */
   ASSERT(domain != NULL);
   ASSERT(msgid != NULL);
   ASSERT(MsgHasMsgID(msgid));
#if defined(_WIN32)
   ASSERT(encoding == STRING_ENCODING_UTF8 ||
          encoding == STRING_ENCODING_UTF16_LE);
#else
   ASSERT(encoding == STRING_ENCODING_UTF8);
#endif

   /*
    * Find the beginning of the ID (idp) and the string (strp).
    * The string should have the correct MSG_MAGIC(...)... form.
    */

   idp = msgid + MSG_MAGIC_LEN + 1;
   strp = strchr(idp, ')') + 1;

   len = strp - idp - 1;
   ASSERT_NOT_IMPLEMENTED(len <= MSG_MAX_ID - 1);
   memcpy(idBuf, idp, len);
   idBuf[len] = '\0';

   /*
    * This lock is pretty coarse-grained, but a lot of the code below just runs
    * in exceptional situations, so it should be OK.
    */
   g_mutex_lock(&state->lock);

   catalog = MsgGetCatalog(domain);
   if (catalog != NULL) {
      switch (encoding) {
      case STRING_ENCODING_UTF8:
         source = catalog->utf8;
         break;

#if defined(_WIN32)
      case STRING_ENCODING_UTF16_LE:
         source = catalog->utf16;
         break;
#endif

      default:
         NOT_IMPLEMENTED();
      }
   }

#if defined(_WIN32)
   /*
    * Lazily create the local and UTF-16 dictionaries. This may require also
    * registering an empty message catalog for the desired domain.
    */
   if (source == NULL && encoding == STRING_ENCODING_UTF16_LE) {
      catalog = MsgGetCatalog(domain);
      if (catalog == NULL) {
         if (domain == NULL) {
            g_error("Application did not set up a default text domain.");
         }
         catalog = g_new0(MsgCatalog, 1);
         MsgSetCatalog(domain, catalog);
      }

      catalog->utf16 = HashTable_Alloc(8, HASH_STRING_KEY, g_free);
      ASSERT_MEM_ALLOC(catalog->utf16);
      source = catalog->utf16;
   }
#endif

   /*
    * Look up the localized string, converting to requested encoding as needed.
    */

   if (source != NULL) {
      const void *retval = NULL;

      if (HashTable_Lookup(source, idBuf, (void **) &retval)) {
         strp = retval;
#if defined(_WIN32)
      } else if (encoding == STRING_ENCODING_UTF16_LE) {
         gchar *converted;
         Bool success;

         /*
          * Look up the string in UTF-8, convert it and cache it.
          */
         retval = MsgGetString(domain, msgid, STRING_ENCODING_UTF8);
         ASSERT(retval);

         converted = (gchar *) g_utf8_to_utf16(retval, -1, NULL, NULL, NULL);
         ASSERT(converted != NULL);

         success = HashTable_Insert(source, idBuf, converted);
         ASSERT(success);
         strp = converted;
#endif
      }
   }

   g_mutex_unlock(&state->lock);

   return strp;
}




/*
 ******************************************************************************
 * MsgLoadCatalog --                                                    */ /**
 *
 * Loads the message catalog at the given path into a new hash table.
 *
 * This function supports an "extended" format for the catalog files. Aside
 * from the usual things you can put in a lib/dict-based dictionary, this code
 * supports multi-line messages so that long messages can be broken down.
 *
 * These lines are any lines following a key / value declaration that start with
 * a quote character (ignoring any leading spaces and tabs). So a long message
 * could look like this:
 *
 * @code
 * message.id = "This is the first part of the message. "
 *              "This is the continuation line, still part of the same message."
 * @endcode
 *
 * The complete value for the "message.id" key will be the concatenation of
 * the values in quotes.
 *
 * @param[in] path    Path containing the message catalog (encoding should be
 *                    UTF-8).
 *
 * @return A new message catalog on success, NULL otherwise.
 *
 ******************************************************************************
 */

static MsgCatalog *
MsgLoadCatalog(const char *path)
{
   gchar *localPath;
   GError *err = NULL;
   GIOChannel *stream;
   gboolean error = FALSE;
   MsgCatalog *catalog = NULL;
   HashTable *dict;

   ASSERT(path != NULL);
   localPath = VMTOOLS_GET_FILENAME_LOCAL(path, NULL);
   ASSERT(localPath != NULL);

   stream = g_io_channel_new_file(localPath, "r", &err);
   VMTOOLS_RELEASE_FILENAME_LOCAL(localPath);

   if (err != NULL) {
      g_debug("Unable to open '%s': %s\n", path, err->message);
      g_clear_error(&err);
      return NULL;
   }

   dict = HashTable_Alloc(8, HASH_STRING_KEY | HASH_FLAG_COPYKEY, g_free);
   ASSERT_MEM_ALLOC(dict);

   for (;;) {
      gboolean eof = FALSE;
      char *name = NULL;
      char *value = NULL;
      gchar *line;

      /* Read the next key / value pair. */
      for (;;) {
         gsize i;
         gsize len;
         gsize term;
         char *unused = NULL;
         gboolean cont = FALSE;

         g_io_channel_read_line(stream, &line, &len, &term, &err);

         if (err != NULL) {
            g_warning("Unable to read a line from '%s': %s\n",
                      path, err->message);
            g_clear_error(&err);
            error = TRUE;
            g_free(line);
            break;
         }

         if (line == NULL) {
            eof = TRUE;
            break;
         }

         /*
          * Fix the line break to always be Unix-style, to make lib/dict
          * happy.
          */
         if (line[term] == '\r') {
            line[term] = '\n';
            if (len > term) {
               line[term + 1] = '\0';
            }
         }

         /*
          * If currently name is not NULL, then check if this is a continuation
          * line and, if it is, just append the contents to the current value.
          */
         if (term > 0 && name != NULL && line[term - 1] == '"') {
            for (i = 0; i < len; i++) {
               if (line[i] == '"') {
                  /* OK, looks like a continuation line. */
                  char *tmp;
                  char *unescaped;

                  line[term - 1] = '\0';
                  unescaped = Escape_Undo('|', line + i + 1, len - i, NULL);
                  tmp = Str_Asprintf(NULL, "%s%s", value, unescaped);
                  free(unescaped);
                  free(value);
                  value = tmp;
                  cont = TRUE;
                  break;
               } else if (line[i] != ' ' && line[i] != '\t') {
                  break;
               }
            }
         }

         /*
          * If not a continuation line and we have a name, break out of the
          * inner loop to update the dictionary.
          */
         if (!cont && name != NULL) {
            g_free(line);
            break;
         }

         /*
          * Finally, try to parse the string using the dictionary library.
          */
         if (!cont && DictLL_UnmarshalLine(line, len, &unused, &name, &value) == NULL) {
            g_warning("Couldn't parse line from catalog: %s", line);
            error = TRUE;
         }

         g_free(line);
         free(unused);
      }

      if (error) {
         free(name);
         free(value);
         break;
      }

      if (name != NULL) {
         gchar *val;
         ASSERT(value);

         if (!Unicode_IsBufferValid(name, strlen(name) + 1, STRING_ENCODING_UTF8) ||
             !Unicode_IsBufferValid(value, strlen(value) + 1, STRING_ENCODING_UTF8)) {
            g_warning("Invalid UTF-8 string in message catalog (key = %s)\n", name);
            error = TRUE;
            free(name);
            free(value);
            break;
         }

         val = g_strcompress(value);
         free(value);
         HashTable_ReplaceOrInsert(dict, name, val);
         free(name);
      }

      if (eof) {
         break;
      }
   }

   g_io_channel_unref(stream);

   if (error) {
      HashTable_Free(dict);
      dict = NULL;
   } else {
      catalog = g_new0(MsgCatalog, 1);
      catalog->utf8 = dict;
   }

   return catalog;
}


/*
 ******************************************************************************
 * VMToolsMsgCleanup --                                                 */ /**
 *
 * Cleanup internal state, freeing up any used memory. After calling this
 * function, it's not safe to call any of the API exposed by this file, so
 * this is only called internally when the library is being unloaded.
 *
 ******************************************************************************
 */

void
VMToolsMsgCleanup(void)
{
   if (gMsgState != NULL) {
      if (gMsgState->domains != NULL) {
         HashTable_Free(gMsgState->domains);
      }
      g_mutex_clear(&gMsgState->lock);
      g_free(gMsgState);
   }
}


/*
 ******************************************************************************
 * VMTools_BindTextDomain --                                            */ /**
 *
 * Loads the message catalog for a text domain. Each text domain contains a
 * different set of messages loaded from a different catalog.
 *
 * If a catalog has already been bound to the given name, it is replaced with
 * the newly loaded data.
 *
 * @param[in] domain   Name of the text domain being loaded.
 * @param[in] lang     Language code for the text domain.
 * @param[in] catdir   Root directory of catalog files (NULL = default).
 *
 ******************************************************************************
 */

void
VMTools_BindTextDomain(const char *domain,
                       const char *lang,
                       const char *catdir)
{
   char *dfltdir = NULL;
   gchar *file;
   gchar *usrlang = NULL;
   MsgState *state = MsgGetState();
   MsgCatalog *catalog;

   ASSERT(domain);

   /*
    * If the caller has asked for the default user language, detect it and
    * translate to our internal language string representation.
    */

   if (lang == NULL || *lang == '\0') {
      usrlang = MsgGetUserLanguage();
      lang = usrlang;
   }

   g_debug("%s: user locale=%s\n", __FUNCTION__, lang);

   /*
    * Use the default install directory if none is provided.
    */

   if (catdir == NULL) {
#if defined(OPEN_VM_TOOLS)
      dfltdir = Util_SafeStrdup(VMTOOLS_DATA_DIR);
#else
      dfltdir = GuestApp_GetInstallPath();
#endif
      catdir = (dfltdir) ? dfltdir : ".";
   }

   file = g_strdup_printf("%s%smessages%s%s%s%s.vmsg",
                          catdir, DIRSEPS, DIRSEPS, lang, DIRSEPS, domain);

   if (!File_IsFile(file)) {
      /*
       * If we couldn't find the catalog file for the user's language, see if
       * we can find a more generic language (e.g., for "en_US", also try "en").
       */
      char *sep = Str_Strrchr(lang, '_');
      if (sep != NULL) {
         if (usrlang == NULL) {
            usrlang = Util_SafeStrdup(lang);
         }
         usrlang[sep - lang] = '\0';
         g_free(file);
         file = g_strdup_printf("%s%smessages%s%s%s%s.vmsg",
                                catdir, DIRSEPS, DIRSEPS, usrlang, DIRSEPS, domain);
      }
   }

   catalog = MsgLoadCatalog(file);

   if (catalog == NULL) {
      if (Str_Strncmp(lang, "en", 2)) {
         /*
          * Don't warn about english dictionary, which may not exist (it is the
          * default translation).
          */
         g_message("Cannot load message catalog for domain '%s', language '%s', "
                   "catalog dir '%s'.\n", domain, lang, catdir);
      }
   } else {
      g_mutex_lock(&state->lock);
      MsgSetCatalog(domain, catalog);
      g_mutex_unlock(&state->lock);
   }
   g_free(file);
   free(dfltdir);
   g_free(usrlang);
}


/*
 ******************************************************************************
 * VMTools_GetString --                                                 */ /**
 *
 * Returns a localized version of the requested string in UTF-8.
 *
 * @param[in] domain    Text domain.
 * @param[in] msgid     Message id (including English translation).
 *
 * @return The localized string.
 *
 ******************************************************************************
 */

const char *
VMTools_GetString(const char *domain,
                  const char *msgid)
{
   return MsgGetString(domain, msgid, STRING_ENCODING_UTF8);
}


#if defined(_WIN32)
/*
 ******************************************************************************
 * VMTools_GetUtf16String --                                            */ /**
 *
 * Returns a localized string in UTF-16LE encoding. Win32 only.
 *
 * @param[in] domain    Text domain.
 * @param[in] msgid     Message id (including English translation).
 *
 * @return The localized string.
 *
 ******************************************************************************
 */

const wchar_t *
VMTools_GetUtf16String(const char *domain,
                       const char *msgid)
{
   return MsgGetString(domain, msgid, STRING_ENCODING_UTF16_LE);
}
#endif

  07070100000238000081A400000000000000000000000168225505000014A6000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/monotonicTimer.c    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file monotonicTimer.c
 *
 * A GSource that implements a timer backed by a monotonic time source.
 */

#include <limits.h>
#include "vmware.h"
#include "system.h"
#include "vmware/tools/utils.h"

typedef struct MTimerSource {
   GSource     src;
   gint        timeout;
   uint64      last;
} MTimerSource;


/*
 *******************************************************************************
 * MTimerSourcePrepare --                                                 */ /**
 *
 * Callback for the "prepare()" event source function. Sets the timeout to
 * the number of milliseconds this timer expects to sleep for. If the timeout
 * has already expired, update the internal state tracking the last time the
 * timer was fired.
 *
 * @param[in]  src         The source.
 * @param[out] timeout     Where to store the timeout.
 *
 * @return TRUE if timeout has already expired.
 *
 *******************************************************************************
 */

static gboolean
MTimerSourcePrepare(GSource *src,
                    gint *timeout)
{
   MTimerSource *timer = (MTimerSource *) src;

   if (timer->timeout == 0) {
      *timeout = 0;
      return TRUE;
   } else {
         uint64 now = System_GetTimeMonotonic() * 10;
         uint64 diff;

         ASSERT(now >= timer->last);

         diff = now - timer->last;
         if (diff >= timer->timeout) {
            timer->last = now;
            *timeout = 0;
            return TRUE;
         }

      *timeout = MIN(INT_MAX, timer->timeout - diff);
      return FALSE;
   }
}


/*
 *******************************************************************************
 * MTimerSourceCheck --                                                   */ /**
 *
 * Checks whether the timeout has expired.
 *
 * @param[in]  src     The source.
 *
 * @return Whether the timeout has expired.
 *
 *******************************************************************************
 */

static gboolean
MTimerSourceCheck(GSource *src)
{
   gint unused;
   return MTimerSourcePrepare(src, &unused);
}


/*
 *******************************************************************************
 * MTimerSourceDispatch --                                                */ /**
 *
 * Calls the callback associated with the timer, if any.
 *
 * @param[in]  src         Unused.
 * @param[in]  callback    The callback to be called.
 * @param[in]  data        User-supplied data.
 *
 * @return The return value of the callback, or FALSE if the callback is NULL.
 *
 *******************************************************************************
 */

static gboolean
MTimerSourceDispatch(GSource *src,
                     GSourceFunc callback,
                     gpointer data)
{
   return (callback != NULL) ? callback(data) : FALSE;
}


/*
 *******************************************************************************
 * MTimerSourceFinalize --                                                */ /**
 *
 * Does nothing. The main glib code already does all the cleanup needed.
 *
 * @param[in]  src     The source.
 *
 *******************************************************************************
 */

static void
MTimerSourceFinalize(GSource *src)
{
}


/**
 *
 * @addtogroup vmtools_utils
 * @{
 */

/*
 *******************************************************************************
 * VMTools_CreateTimer --                                                 */ /**
 *
 * @brief Create a timer based on a monotonic clock source.
 *
 * This timer differs from the glib timeout source, which uses the system time.
 * It is recommended for code that needs more reliable time tracking, using a
 * clock that is not affected by changes in the system time (which can happen
 * when using NTP or the Tools time synchronization feature).
 *
 * @param[in] timeout   The timeout for the timer, must be >= 0.
 *
 * @return The new source.
 *
 *******************************************************************************
 */

GSource *
VMTools_CreateTimer(gint timeout)
{
   static GSourceFuncs srcFuncs = {
      MTimerSourcePrepare,
      MTimerSourceCheck,
      MTimerSourceDispatch,
      MTimerSourceFinalize,
      NULL,
      NULL
   };
   MTimerSource *ret;

   ASSERT(timeout >= 0);

   ret = (MTimerSource *) g_source_new(&srcFuncs, sizeof *ret);
   ret->last = System_GetTimeMonotonic() * 10;
   ret->timeout = timeout;

   return &ret->src;
}

/** @}  */

  07070100000239000081A400000000000000000000000168225505000022FF000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/signalSource.c  /*********************************************************
 * Copyright (C) 2008-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file signalSource.c
 *
 *    A GSource implementation that is activated by OS signals.
 *
 *    Caveat: if the process is receiving a lot of signals in a short period
 *    of time, it's possible that the sources will not be notified for all
 *    the instances of a particular signal. So this mechanism shouldn't be
 *    used for reliable event delivery.
 */

#include "vm_assert.h"
#include "vmware/tools/utils.h"
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

typedef enum {
   SIG_SRC_UNHANDLED = 0,
   SIG_SRC_IDLE,
   SIG_SRC_SIGNALED
} SignalState;

/* Use NSIG if it's defined, otherwise use a hardcoded limit. */
#if defined(NSIG)
#  define MAX_SIGNALS   NSIG
#else
#  define MAX_SIGNALS   64
#endif

typedef struct SignalHandler {
   gboolean                initialized;
   int                     wakeupPipe[2];
   struct sigaction        handler;
   GPollFD                 wakeupFd;
   SignalState             signals[MAX_SIGNALS];
   siginfo_t               currSignal;
} SignalHandler;

static SignalHandler gHandler;
G_LOCK_DEFINE_STATIC(gLock);

typedef struct SignalSource {
   GSource     src;
   int         signum;
} SignalSource;


/**
 * Reads one siginfo_t struct from the pipe if data is available, and
 * place it in the global state variable. This allows us to, eventually,
 * service all the processed signals, although in a not very efficient
 * way...
 */

static inline void
SignalSourceReadSigInfo(void)
{
   if (gHandler.wakeupFd.revents & G_IO_IN) {
      siginfo_t info;
      ssize_t nbytes = read(gHandler.wakeupFd.fd, &info, sizeof info);

      if (nbytes == -1) {
         g_warning("Signal source: reading from wake up fd failed.\n");
         return;
      } else if (nbytes < sizeof info) {
         g_warning("Signal source: reading from wake up fd returned %"FMTSZ"d,"
                   " expected %"FMTSZ"u.\n", nbytes, sizeof info);
         return;
      } else if (info.si_signo < 0 || info.si_signo >= MAX_SIGNALS) {
         g_warning("Signal source: bad signal number %d.\n", info.si_signo);
         return;
      }
      memcpy(&gHandler.currSignal, &info, sizeof info);
      gHandler.signals[info.si_signo] = SIG_SRC_SIGNALED;
      gHandler.wakeupFd.revents = 0;
   }
}


/**
 * Handles a signal. Writes the signal information to the wakeup pipe.
 *
 * According to signal(7), "write()" is safe to call from a signal handling
 * context. If the write fails, though, signal delivery might be delayed.
 *
 * @param[in]  signum      Signal received.
 * @param[in]  info        Information about the signal.
 * @param[in]  context     Unused.
 */

static void
SignalSourceSigHandler(int signum,
                       siginfo_t *info,
                       void *context)
{
   ssize_t bytes;
   siginfo_t dummy;
   if (signum >= MAX_SIGNALS || signum < 0) {
      return;
   }

   if (info == NULL) {
      /*
       * Solaris seems to call the handler with a NULL info struct in certain
       * situations. I noticed it when hitting CTRL-C (sending SIGINT) in a
       * terminal; calling "kill [pid]" (sending SIGTERM) seemed to work fine.
       * Still, it's better to handle this case.
       */
      memset(&dummy, 0, sizeof dummy);
      dummy.si_signo = signum;
      info = &dummy;
   }
   bytes = write(gHandler.wakeupPipe[1], info, sizeof *info);

   if (bytes == -1) {
      if (errno == EAGAIN) {
         /*
          * Pipe is full. If this ever becomes a problem, more pipes will
          * have to be created...
          */
         g_warning("Too many signals queued, this shouldn't happen.\n");
         ASSERT(FALSE);
      } else {
         g_warning("Could not queue signal %d (error %d: %s)\n",
                   signum, errno, strerror(errno));
      }
   }
}


/**
 * Does nothing.
 *
 * @param[in]  _src        Unused.
 * @param[out] timeout     Set to -1.
 *
 * @return FALSE
 */

static gboolean
SignalSourcePrepare(GSource *_src,
                    gint *timeout)
{
   *timeout = -1;
   return FALSE;
}


/**
 * Checks whether the process received the signal the source is watching.
 *
 * @param[in]  _src     The event source.
 *
 * @return Whether the source's signal was received.
 */

static gboolean
SignalSourceCheck(GSource *_src)
{
   SignalSource *src = (SignalSource *) _src;
   SignalSourceReadSigInfo();
   return (gHandler.signals[src->signum] == SIG_SRC_SIGNALED);
}


/**
 * Calls the callback associated with the handle, if any. Resets the source's
 * signal state to "not signaled".
 *
 * @param[in]  _src        The event source.
 * @param[in]  _callback   The callback to be called.
 * @param[in]  data        User-supplied data.
 *
 * @return The return value of the callback, or FALSE if the callback is NULL.
 */

static gboolean
SignalSourceDispatch(GSource *_src,
                     GSourceFunc _callback,
                     gpointer data)
{
   SignalSourceCb callback = (SignalSourceCb) _callback;
   SignalSource *src = (SignalSource *) _src;
   gHandler.signals[src->signum] = SIG_SRC_IDLE;
   return (callback != NULL) ? callback(&gHandler.currSignal, data)
                             : FALSE;
}


/**
 * Destroys the event source. Nothing needs to be done.
 *
 * @param[in]  src      The event source.
 */

static void
SignalSourceFinalize(GSource *src)
{
}

/**
 * @addtogroup vmtools_utils
 * @{
 */

/**
 * Creates a new source for the given signal.
 *
 * Rather than processing the events in the signal handling context, the main
 * loop is woken up and callbacks are processed in the main loop's thread.
 *
 * The same "wakeup" file descriptors are used for all sources, so if sources
 * are added to different main loop instances, all of them will be woken up
 * if any signal for which handlers are registered occurs.
 *
 * This code assumes that the rest of the app is not setting signal
 * handlers directly, at least for signals for which glib sources have
 * been set up.
 *
 * Also note that on older Linux systems (pre-NPTL), some real-time signals
 * are used by the pthread library and shouldn't be used by applications.
 *
 * Example of setting a handler for a signal:
 *
 * @code
 *
 *    GSource *src = VMTools_NewSignalSource(signum);
 *    g_source_set_callback(src, MyCallback, myData, NULL);
 *    g_source_attach(src, myContext);
 *
 * @endcode
 *
 * @note This API is not available on Win32.
 *
 * @param[in]  signum   Signal to watch.
 *
 * @return Pointer to the new source, NULL if failed to set signal handler.
 */

GSource *
VMTools_NewSignalSource(int signum)
{
   static GSourceFuncs srcFuncs = {
      SignalSourcePrepare,
      SignalSourceCheck,
      SignalSourceDispatch,
      SignalSourceFinalize,
      NULL,
      NULL
   };
   SignalSource *ret;

   ASSERT(0 <= signum && signum < MAX_SIGNALS);
   ASSERT(signum != SIGKILL && signum != SIGSTOP);

   G_LOCK(gLock);
   if (!gHandler.initialized) {
      if (pipe(gHandler.wakeupPipe) == -1 ||
          fcntl(gHandler.wakeupPipe[0], F_SETFL, O_RDONLY | O_NONBLOCK) < 0 ||
          fcntl(gHandler.wakeupPipe[1], F_SETFL, O_WRONLY | O_NONBLOCK) < 0) {
         ASSERT(FALSE);
      }
      gHandler.wakeupFd.fd = gHandler.wakeupPipe[0];
      gHandler.wakeupFd.events = G_IO_IN | G_IO_ERR;
      gHandler.handler.sa_sigaction = SignalSourceSigHandler;
      gHandler.handler.sa_flags = SA_SIGINFO;
      gHandler.initialized = TRUE;
   }
   G_UNLOCK(gLock);

   /*
    * Sets the signal handler if it hasn't been set yet. The code is
    * racy, but it's OK if 2 threads are trying to install the same
    * signal handler (one will win and all will be fine).
    */
   if (gHandler.signals[signum] == SIG_SRC_UNHANDLED) {
      if (sigaction(signum, &gHandler.handler, NULL) == -1) {
         g_warning("Cannot set signal handler: %s\n", strerror(errno));
         return NULL;
      }
      gHandler.signals[signum] = SIG_SRC_IDLE;
   }

   ret = (SignalSource *) g_source_new(&srcFuncs, sizeof *ret);
   ret->signum = signum;

   g_source_add_poll(&ret->src, &gHandler.wakeupFd);
   return &ret->src;
}

/** @}  */

 0707010000023A000081A40000000000000000000000016822550500000E54000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/vmtools.c   /*********************************************************
 * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmtools.c
 *
 * Library entry point, utility and memory de-allocation functions for the VMTools
 * shared library.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#if defined(_WIN32)
#  include <windows.h>
#  include "netutil.h"
#endif

#include "vmware.h"
#include "wiper.h"
#include "vmtoolsInt.h"
#include "vmware/tools/utils.h"

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolslib_version.h"
VM_EMBED_VERSION(VMTOOLSLIB_VERSION_STRING);
#endif


/**
 * A convenience function for wrapping an array with a GArray instance.
 *
 * @param[in]  data        The array data. The original data is copied into the
 *                         new array.
 * @param[in]  elemSize    The size of each element in the array.
 * @param[in]  count       The number of elements in the array.
 *
 * @return A new GArray.
 */

GArray *
VMTools_WrapArray(gconstpointer data,
                  guint elemSize,
                  guint count)
{
   GArray *array;

   array = g_array_sized_new(FALSE, TRUE, elemSize, count);
   memcpy(array->data, data, elemSize * count);
   array->len = count;

   return array;
}


/**
 * Library constructor. Calls any needed initialization functions.
 *
 * @param[in]  lib      The library handle (Win32 only).
 */

#if defined(__GNUC__)
__attribute__((constructor))
#endif
static void
VMToolsDllInit(void *lib)
{
   Bool success;
   WiperInitData wiperData;
#if defined(_WIN32)
   wiperData.resourceModule = lib;
   success = (NetUtil_LoadIpHlpApiDll() == ERROR_SUCCESS);
   ASSERT(success);
   success = Wiper_Init(&wiperData);
   ASSERT(success);
#else
   (void) wiperData;
   success = Wiper_Init(NULL);
   ASSERT_NOT_IMPLEMENTED(success);
#endif
}


/**
 * Library destructor. Uninitializes any libraries that need to be cleaned up.
 */

#if defined(__GNUC__)
__attribute__((destructor))
#endif
static void
VMToolsDllFini(void)
{
#if defined(_WIN32)
   NetUtil_FreeIpHlpApiDll();
#endif
   VMToolsMsgCleanup();
}


/**
 * Frees a pointer allocated by the vmtools library.
 *
 * @param[in] ptr Pointer to memory to be freed.
 */

void
vm_free(void *ptr)
{
   free(ptr);
}


#if defined(_WIN32)
/**
 * Windows initialization callback. Calls the library's constructor or
 * destructor, depending on what is being done.
 *
 * @param[in]  hinstDLL    The library handle.
 * @param[in]  fdwReason   Why the callback is being called.
 * @param[in]  lpvReserved Unused.
 *
 * @return TRUE.
 */

BOOL WINAPI
DllMain(HINSTANCE hinstDLL,
        DWORD fdwReason,
        LPVOID lpvReserved)
{
   switch (fdwReason) {
   case DLL_PROCESS_ATTACH:
      VMToolsDllInit(hinstDLL);
      break;

   case DLL_PROCESS_DETACH:
      VMToolsDllFini();
      break;
   }
   return TRUE;
}
#endif

0707010000023B000081A400000000000000000000000168225505000046E7000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/vmtoolsConfig.c /*********************************************************
 * Copyright (c) 2008-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmtoolsConfig.c
 *
 *    Convenience functions for loading tools configuration files, and
 *    automatically migrating from old-style tools configuration files.
 */

#include "vmware/tools/utils.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib/gstdio.h>

#include "vm_assert.h"
#include "conf.h"
#include "err.h"
#include "guestApp.h"


/**
 * Returns the path to the default tools config file.
 *
 * @return String with the default config path (should be freed by caller).
 */

static gchar *
VMToolsGetToolsConfFile(void)
{
   char *confPath = GuestApp_GetConfPath();
   gchar *confFilePath;

   /*
    * XXX: GuestApp_GetConfPath() is racy. If two different people are calling
    * the function and the conf directory doesn't exist, there's the risk that
    * one of them will fail to create the directory and return NULL. So if we
    * get NULL back, retry the call (at which point the directory will
    * hopefully exist), and assert if it's NULL (which now should only happen
    * if we fail to allocate memory).
    */
   if (confPath == NULL) {
      confPath = GuestApp_GetConfPath();
      ASSERT(confPath != NULL);
   }
   confFilePath = g_build_filename(confPath, CONF_FILE, NULL);
   free(confPath);

   return confFilePath;
}


/**
 * Loads the configuration file at the given path.
 *
 * @param[in]     path     Path to the configuration file, or NULL for default
 *                         Tools config file.
 * @param[in]     flags    Flags for opening the file.
 * @param[in,out] config   Where to store the config dictionary; when reloading
 *                         the file, the old config object will be destroyed.
 * @param[in,out] mtime    Last known modification time of the config file.
 *                         When the function succeeds, will contain the new
 *                         modification time read from the file. If NULL (or 0),
 *                         the config dictionary is always loaded.
 *
 * @return Whether a new config dictionary was loaded.
 */

gboolean
VMTools_LoadConfig(const gchar *path,
                   GKeyFileFlags flags,
                   GKeyFile **config,
                   time_t *mtime)
{
   gchar *defaultPath = NULL;
   gchar *localPath = NULL;
   /* GStatBuf was added in 2.26. */
   GStatBuf confStat;
   GError *err = NULL;
   GKeyFile *cfg = NULL;
   static gboolean hadConfFile = TRUE;

   if (config == NULL) {
      return FALSE;
   }

   if (path == NULL) {
      defaultPath = VMToolsGetToolsConfFile();
   }

   localPath = VMTOOLS_GET_FILENAME_LOCAL((path != NULL) ? path : defaultPath, &err);
   if (err != NULL) {
      g_warning("Error converting to local encoding: %s\n", err->message);
      goto exit;
   }

   if (g_stat(localPath, &confStat) == -1) {
      /*
       * If the file doesn't exist, it's not an error. Just return an
       * empty dictionary in that case. The mtime will be set to 0 if
       * the caller requested it.
       */
      memset(&confStat, 0, sizeof confStat);
      if (errno != ENOENT) {
         g_warning("Failed to stat conf file: %s\n", strerror(errno));
         goto exit;
      } else {
         /*
          * If we used to have a file, create a config.
          * Otherwise we can re-use the empty GKeyFile from before.
          */
         if (hadConfFile) {
            cfg = g_key_file_new();
         }
         hadConfFile = FALSE;
         goto exit;
      }
   }

   hadConfFile = TRUE;

   /* Check if we really need to load the data. */
   if (mtime != NULL && confStat.st_mtime <= *mtime) {
      goto exit;
   }

   /* Need to load the configuration data. */

   cfg = g_key_file_new();

   /* Empty file: just return an empty dictionary. */
   if (confStat.st_size == 0) {
      goto exit;
   }

   /* On error, 'err' will be set, null otherwise */
   /* coverity[check_return] */
   g_key_file_load_from_file(cfg, localPath, flags, &err);
   if (err == NULL || err->code == G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
      goto exit;
   }

   g_warning("Cannot load config file: %s", err->message);

   g_key_file_free(cfg);
   cfg = NULL;

exit:
   g_clear_error(&err);
   if (cfg != NULL) {
      if (*config != NULL) {
         g_key_file_free(*config);
      }
      *config = cfg;
      if (mtime != NULL) {
         *mtime = confStat.st_mtime;
      }
   }
   g_free(defaultPath);
   VMTOOLS_RELEASE_FILENAME_LOCAL(localPath);
   return (cfg != NULL);
}


/**
 * Copies the key/value pairs from one config dictionary to another config
 * dictionary. The key/value pairs are added only if they are not
 * already present in the destination configuration dictionary.
 *
 * @param[in]     srcConfig     Configuration dictionary from which the
 *                              key/value pairs should be added.
 * @param[in]     dstConfig     Configuration dictionary to which the
 *                              key/value pairs should be added..
 *
 * @return Whether any key/value pairs have been added to the config
 *         dictionary.
 */

gboolean
VMTools_AddConfig(GKeyFile *srcConfig,
                  GKeyFile *dstConfig)
{
   gsize numGroups;
   gchar **groupNames;
   gsize i;
   gboolean configAdded = FALSE;

   if (srcConfig == NULL || dstConfig == NULL) {
      return configAdded;
   }

   groupNames = g_key_file_get_groups(srcConfig, &numGroups);

   g_debug("%s: Found %d groups in config.\n", __FUNCTION__, (int) numGroups);

   for (i = 0; i < numGroups; ++i) {
      gsize numKeys;
      gchar **keyNames;
      gsize j;
      GError *gErr = NULL;
      const gchar *group = groupNames[i];

      keyNames = g_key_file_get_keys(srcConfig, group, &numKeys, &gErr);
      if (gErr != NULL) {
         g_warning("%s: g_key_file_get_keys(%s) failed: %s\n",
                   __FUNCTION__, group, gErr->message);
         g_clear_error(&gErr);
         continue;
      }

      g_debug("%s: Found %d keys for group: '%s' in config.\n",
              __FUNCTION__, (int) numKeys, group);

      for (j = 0; j < numKeys; ++j) {
         const gchar* key = keyNames[j];

         if (!g_key_file_has_key(dstConfig, group, key, NULL)) {
            gchar *value = g_key_file_get_value(srcConfig, group, key, &gErr);

            if (value == NULL && gErr != NULL) {
               g_warning("%s: g_key_file_get_value(%s:%s) failed: %s\n",
                         __FUNCTION__, group, key, gErr->message);
               g_clear_error(&gErr);
               continue;
            }

            g_key_file_set_value(dstConfig, group, key, value);
            g_debug("%s: Added (%s:%s) to the new config\n",
                    __FUNCTION__, group, key);
            configAdded = TRUE;
            g_free(value);
         } else {
            g_debug("%s: Ignoring (%s:%s)\n", __FUNCTION__, group, key);
         }
      }

      g_strfreev(keyNames);
   }

   g_debug("%s: Added the config. Return val: %d\n", __FUNCTION__, configAdded);

   g_strfreev(groupNames);
   return configAdded;
}


/**
 * Compares two configuration dictionaries. Compares all the groups and
 * key/value pairs in both the configuration dictionaries. If all the entries
 * are same, TRUE is returned. Else, FALSE is returned.
 *
 * @param[in]     config1     First configuration dictionary to compare.
 * @param[in]     config2     Second configuration dictionary to compare.
 *
 * @return TRUE if both the specified configuration dictionaries are identical.
 *         FALSE otherwise.
 */

gboolean
VMTools_CompareConfig(GKeyFile *config1,
                      GKeyFile *config2)
{
   gsize numGroups1 = 0;
   gsize numGroups2 = 0;
   gchar **groupNames1 = NULL;
   gchar **groupNames2 = NULL;
   gchar **keyNames1 = NULL;
   gchar **keyNames2 = NULL;
   gchar *value1 = NULL;
   gchar *value2 = NULL;
   gsize i;
   gboolean sameConfigs = TRUE;
   GError *gErr = NULL;

   if (config1 == NULL && config2 == NULL) {
      goto exit;
   }

   if (config1 == NULL || config2 == NULL) {
      goto mismatch;
   }

   groupNames1 = g_key_file_get_groups(config1, &numGroups1);
   groupNames2 = g_key_file_get_groups(config2, &numGroups2);

   g_debug("%s: Found %d groups in first config, "
           "%d groups in second config.\n",
           __FUNCTION__, (int) numGroups1, (int) numGroups2);

   if (numGroups1 != numGroups2) {
      goto mismatch;
   }

   for (i = 0; i < numGroups1; ++i) {
      gsize numKeys1 = 0;
      gsize numKeys2 = 0;
      gsize j;
      const gchar *group = groupNames1[i];

      g_strfreev(keyNames1);
      keyNames1 = NULL;
      g_strfreev(keyNames2);
      keyNames2 = NULL;

      if (!g_key_file_has_group(config2, group)) {
         g_debug("%s: group: '%s' not found in second config.\n",
                 __FUNCTION__, group);
         goto mismatch;
      }

      keyNames1 = g_key_file_get_keys(config1, group, &numKeys1, &gErr);
      if (gErr != NULL) {
         g_warning("%s: g_key_file_get_keys(%s) for first config failed: %s\n",
                   __FUNCTION__, group, gErr->message);
         goto mismatch;
      }

      keyNames2 = g_key_file_get_keys(config2, group, &numKeys2, &gErr);
      if (gErr != NULL) {
         g_warning("%s: g_key_file_get_keys(%s) for second config failed: %s\n",
                   __FUNCTION__, group, gErr->message);
         goto mismatch;
      }

      g_debug("%s: For group: '%s', first config has %d keys, "
              "second config has %d keys\n",
              __FUNCTION__, group, (int) numKeys1, (int) numKeys2);

      if (numKeys1 != numKeys2) {
         goto mismatch;
      }

      for (j = 0; j < numKeys1; ++j) {
         const gchar* key = keyNames1[j];

         g_free(value1);
         value1 = NULL;
         g_free(value2);
         value2 = NULL;

         if (!g_key_file_has_key(config2, group, key, NULL)) {
            g_debug("%s: key '%s' for group '%s' not found in second config.\n",
                    __FUNCTION__, key, group);
            goto mismatch;
         }

         value1 = g_key_file_get_value(config1, group, key, &gErr);
         if (value1 == NULL) {
            g_warning("%s: g_key_file_get_value(%s:%s) for first config "
                      "failed: %s\n",
                      __FUNCTION__, group, key,
                      (gErr != NULL) ? gErr->message : "");
            goto mismatch;
         }

         value2 = g_key_file_get_value(config2, group, key, &gErr);
         if (value2 == NULL) {
            g_warning("%s: g_key_file_get_value(%s:%s) for second config "
                      "failed: %s\n",
                      __FUNCTION__, group, key,
                      (gErr != NULL) ? gErr->message : "");
            goto mismatch;
         }

         if (strcmp(value1, value2) != 0) {
            g_debug("%s: Value for (%s:%s) is not same in both the configs.\n",
                    __FUNCTION__, group, key);
            goto mismatch;
         }
      }
   }

   goto exit;

mismatch:
   sameConfigs = FALSE;

exit:
   g_debug("%s: Return Value: %d\n", __FUNCTION__, sameConfigs);

   g_clear_error(&gErr);
   g_free(value1);
   g_free(value2);
   g_strfreev(keyNames1);
   g_strfreev(keyNames2);
   g_strfreev(groupNames1);
   g_strfreev(groupNames2);
   return sameConfigs;
}


/**
 * Saves the given config data to the given path.
 *
 * @param[in]  path     Where to save the data.
 * @param[in]  config   Config data.
 * @param[out] err      Where to store error information (may be NULL).
 *
 * @return Whether saving was successful.
 */

gboolean
VMTools_WriteConfig(const gchar *path,
                    GKeyFile *config,
                    GError **err)
{
   gboolean ret = FALSE;
   gchar *data = NULL;
   gchar *defaultPath = NULL;
   gchar *localPath = NULL;
   FILE *out = NULL;
   GError *lerr = NULL;

   ASSERT(config != NULL);

   if (path == NULL) {
      defaultPath = VMToolsGetToolsConfFile();
   }

   localPath = VMTOOLS_GET_FILENAME_LOCAL((path != NULL) ? path : defaultPath, &lerr);
   if (lerr != NULL) {
      g_warning("Error converting to local encoding: %s\n", lerr->message);
      goto exit;
   }

   data = g_key_file_to_data(config, NULL, &lerr);
   if (lerr != NULL) {
      g_warning("Error serializing conf data: %s\n", lerr->message);
      goto exit;
   }

   out = g_fopen(localPath, "w");

   if (out == NULL) {
      const char *errstr = strerror(errno);
      g_warning("Error opening conf file for writing: %s\n", errstr);
      g_set_error(&lerr, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s", errstr);
      goto exit;
   }

   if (g_fprintf(out, "%s", data) < 0) {
      const char *errstr = strerror(errno);
      g_warning("Error writing conf file: %s\n", errstr);
      g_set_error(&lerr, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s", errstr);
      goto exit;
   }

   ret = TRUE;

exit:
   if (out != NULL) {
      fclose(out);
   }
   if (err != NULL && lerr != NULL) {
      *err = lerr;
   } else {
      g_clear_error(&lerr);
   }
   g_free(data);
   g_free(defaultPath);
   VMTOOLS_RELEASE_FILENAME_LOCAL(localPath);
   return ret;
}


/**
 * Loads boolean value for a key from the specified config section.
 *
 * @param[in]  config   Config file to read the key from.
 * @param[in]  section  Section to look for in the config file.
 * @param[in]  key      Key to look for in the section.
 * @param[in]  defValue Default value if the key is not found or error.
 *
 * @return value of the key if value was read successfully, else defValue.
 */

gboolean
VMTools_ConfigGetBoolean(GKeyFile *config,
                         const gchar *section,
                         const gchar *key,
                         const gboolean defValue)
{
   GError *err = NULL;
   gboolean value;

   if (config == NULL || section == NULL || key == NULL) {
      g_debug("%s: Returning default value for '[%s] %s'=%s.\n",
              __FUNCTION__, section ? section : "(null)",
              key ? key : "(null)", defValue ? "TRUE" : "FALSE");
      return defValue;
   }

   value = g_key_file_get_boolean(config, section, key, &err);
   if (err != NULL) {
      if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
          err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
         g_warning("%s: Failed to get value for '[%s] %s': %s (err=%d).\n",
                   __FUNCTION__, section, key, err->message, err->code);
      }
      g_debug("%s: Returning default value for '[%s] %s'=%s "
              "(Not found err=%d).\n",
              __FUNCTION__, section, key, defValue ? "TRUE" : "FALSE",
              err->code);
      value = defValue;
      g_clear_error(&err);
   }
   return value;
}


/**
 * Loads integer value for a key from the specified config section.
 *
 * @param[in]  config   Config file to read the key from.
 * @param[in]  section  Section to look for in the config file.
 * @param[in]  key      Key to look for in the section.
 * @param[in]  defValue Default value if the key is not found or error.
 *
 * @return value of the key if value was read successfully, else defValue.
 */

gint
VMTools_ConfigGetInteger(GKeyFile *config,
                         const gchar *section,
                         const gchar *key,
                         const gint defValue)
{
   GError *err = NULL;
   gint value;

   ASSERT(config);
   ASSERT(key);
   ASSERT(section);

   value = g_key_file_get_integer(config, section, key, &err);
   if (err != NULL) {
      if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
          err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
         g_warning("%s: Failed to get value for '[%s] %s': %s (err=%d).\n",
                   __FUNCTION__, section, key, err->message, err->code);
      }
      g_debug("%s: Returning default value for '[%s] %s'=%d.\n",
              __FUNCTION__, section, key, defValue);
      value = defValue;
      g_clear_error(&err);
   }
   return value;
}


/**
 * Loads string value for a key from the specified config section.
 *
 * @param[in]  config   Config file to read the key from.
 * @param[in]  section  Section to look for in the config file.
 * @param[in]  key      Key to look for in the section.
 * @param[in]  defValue Default value if the key is not found or error.
 *
 * @return value of the key if value was read successfully, else a copy
 * of defValue unless defValue is NULL, in which case it's NULL.
 * The returned string should be freed with g_free() when no longer needed.
 */

gchar *
VMTools_ConfigGetString(GKeyFile *config,
                        const gchar *section,
                        const gchar *key,
                        const gchar *defValue)
{
   GError *err = NULL;
   gchar *value;

   ASSERT(config);
   ASSERT(key);
   ASSERT(section);

   value = g_key_file_get_string(config, section, key, &err);
   if (err != NULL) {
      if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
          err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
         g_warning("%s: Failed to get value for '[%s] %s': %s (err=%d).\n",
                   __FUNCTION__, section, key, err->message, err->code);
      }
      g_debug("%s: Returning default value for '[%s] %s'=%s.\n",
              __FUNCTION__, section, key, defValue ? defValue : "(null)");
      value = g_strdup(defValue);
      g_clear_error(&err);
   }
   return value;
}
 0707010000023C000081A400000000000000000000000168225505000007F9000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/vmtoolsInt.h    /*********************************************************
 * Copyright (C) 2010-2016,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMTOOLSINT_H_
#define _VMTOOLSINT_H_

/**
 * @file vmtoolsInt.h
 *
 * Internal definitions used by the vmtools library.
 */

#include "glibUtils.h"
#include "vmware.h"
#include "vmware/tools/utils.h"

/* ************************************************************************** *
 * Internationalization.                                                      *
 * ************************************************************************** */

void
VMToolsMsgCleanup(void);

/* ************************************************************************** *
 * Logging.                                                                   *
 * ************************************************************************** */

GlibLogger *
VMToolsCreateVMXLogger(void);

/* ************************************************************************** *
 * Miscelaneous.                                                              *
 * ************************************************************************** */

gint
VMToolsAsprintf(gchar **string,
                gchar const *format,
                ...)  PRINTF_DECL(2, 3);

#endif /* _VMTOOLSINT_H_ */

   0707010000023D000081A40000000000000000000000016822550500013F9E000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/vmtoolsLog.c    /*********************************************************
 * Copyright (c) 2008-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmtoolsLog.c
 *
 *    Defines a logging infrastructure for the vmtools library based
 *    on glib's logging facilities. Wrap the commonly used logging functions
 *    (Log/Warning/Debug), and provides configurability for where logs should
 *    go to.
 *
 *    To choose the logging domain for your source file, define G_LOG_DOMAIN
 *    before including glib.h.
 *
 *    All fatal error messages will go to the 'syslog' handler no
 *    matter what handler has been configured.
 */

#include "vmtoolsInt.h"
#include <stdio.h>
#include <stdlib.h>
#include <glib/gstdio.h>
#if defined(G_PLATFORM_WIN32)
#  include <windows.h>
#else
#  include <unistd.h>
#  include <sys/resource.h>
#  include <sys/time.h>
#endif

#include "glibUtils.h"
#include "log.h"
#if defined(G_PLATFORM_WIN32)
#  include <dbghelp.h>
#  include "coreDump.h"
#  include "w32Messages.h"
#  include "windowsu.h"
#endif
#include "str.h"
#include "system.h"
#include "vmware/tools/log.h"
#include "vmware/tools/guestrpc.h"
#include "vmware/guestrpc/tclodefs.h"
#include "err.h"
#include "logToHost.h"
#include "vthreadBase.h"
#include "hostinfo.h"
#include "vm_tools_version.h"
#include "buildNumber.h"
#include "vm_product.h"
#include "util.h"

#define LOGGING_GROUP         "logging"

#define MAX_DOMAIN_LEN                 (64)

/*
 * Default max number of log messages to be cached when log IO
 * has been frozen. In case of cache overflow, only the most
 * recent messages are preserved.
 */
#define DEFAULT_MAX_CACHE_ENTRIES      (4*1024)

/** The default handler to use if none is specified by the config data. */
#define DEFAULT_HANDLER "file+"

/** The default logfile location. */
#ifdef WIN32
// Windows log goes to %windir%\temp\vmware-<service>.log
#define DEFAULT_LOGFILE_DIR "%windir%"
#define DEFAULT_LOGFILE_NAME_PREFIX "vmware"
#else
// *ix log goes to /var/log/vmware-<service>.log
#define DEFAULT_LOGFILE_NAME_PREFIX  "/var/log/vmware"
#endif

/** The "failsafe" handler. */
#if defined(_WIN32)
#  define SAFE_HANDLER  "outputdebugstring"
#else
#  define SAFE_HANDLER  "std"
#endif

#define STD_HANDLER  "std"

/** Tells whether the given log level is a fatal error. */
#define IS_FATAL(level) ((level) & G_LOG_FLAG_FATAL)

/**
 * Tells whether a message should be logged. All fatal messages are logged,
 * regardless of what the configuration says. Otherwise, the log domain's
 * configuration is respected.
 */
#define SHOULD_LOG(level, data) (IS_FATAL(level) || \
                                 (gLogEnabled && ((data)->mask & (level))))

/** Clean up the contents of a log handler. */
#define CLEAR_LOG_HANDLER(handler) do {            \
   if ((handler) != NULL) {                        \
      if (handler->logger != NULL) {               \
         handler->logger->dtor(handler->logger);   \
      }                                            \
      g_free((handler)->domain);                   \
      g_free((handler)->type);                     \
      g_free((handler)->confData);                 \
      g_free(handler);                             \
   }                                               \
} while (0)

#define VMX_LOG_CMD     "log "
#define VMX_LOG_CMD_LEN (sizeof(VMX_LOG_CMD) - 1)

#define LOG_HEADER_MAX_ENTRIES 2


typedef struct LogHandler {
   GlibLogger    *logger;
   gchar         *domain;
   gchar         *type;
   guint          mask;
   guint          handlerId;
   gboolean       inherited;
   /**
    * The log handlers that write to files need special
    * treatment when guest has been quiesced.
    */
   gboolean       needsFileIO;
   gboolean       isSysLog;
   gchar         *confData;
} LogHandler;


/**
 * Structure for caching a log message
 */
typedef struct LogEntry {
   gchar           *domain;
   gchar           *msg;
   LogHandler      *handler;
   GLogLevelFlags   level;
} LogEntry;


static gboolean gLogInitialized = FALSE;
/*
 * This lock protects most of the static global below.
 * Note statically allocated GRecMutex does not need an explicit initialization.
 */
static GRecMutex gLogStateMutex;
static gchar *gLogDomain = NULL;
static GPtrArray *gCachedLogs = NULL;
static guint gDroppedLogCount = 0;
static gint gMaxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
static gboolean gEnableCoreDump = TRUE;
static gboolean gLogEnabled = FALSE;
static guint gPanicCount = 0;
static LogHandler *gDefaultData;
static LogHandler *gErrorData;
static LogHandler *gErrorSyslog;
static GPtrArray *gDomains = NULL;
/* Whether to stop logging using glib logging functions */
static gboolean gGlibLoggingStopped = FALSE;
/* Whether to enable proxy messages to log handler */
static gboolean gLogHandlerEnabled = TRUE;

static gboolean gLogIOSuspended = FALSE;

/* Data structures for the VMX guest logger */

/*
 * The gUseVmxGuestLog flag is turned on once and never modified later
 * Therefore, the flag can be read without a lock.
 */
static gboolean gUseVmxGuestLog;
static gchar *gAppName;

/*
 * This lock protects the gChannel, gLevelMask and gRpcMode
 */
static GRecMutex gVmxGuestLogMutex;
static RpcChannel *gChannel; /* either NULL or allocated AND started */

/*
 * 1) VMX supports guest.log.* RPCs, use the query result of guest.log.state
 * 2) Otherwise, use the tools.conf vmx handler level setting.
 */
static GLogLevelFlags gLevelMask;

static gchar *gLogHeaderBuf[LOG_HEADER_MAX_ENTRIES];
static guint gLogHeaderCount;

static enum RpcMode {
   RPC_OFF = 0,
   RPC_GUEST_LOG_TEXT,
   RPC_LOG_FALLBACK
} gRpcMode;


/* Forward function declarations */

static void VmxGuestLog(const gchar *domain,
                        GLogLevelFlags level,
                        const gchar *message);
void Debug(const char *fmt, ...);
static void LogWhereLevelV(LogWhere where,
                           GLogLevelFlags level,
                           const gchar *domain,
                           const gchar *fmt,
                           va_list args);
static LogHandler *GetLogHandlerByDomain(const gchar *domain);

/* Internal functions. */

/**
 * Convert log level flag to its string representation.
 *
 * @param[in] level        Log level.
 *
 * @return a string constant that corresponds to the log level.
 *         Should NOT be g_free()'d.
 */

static const char *
VMToolsLogLevelString(GLogLevelFlags level) {
   const char *slevel;

   switch (level & G_LOG_LEVEL_MASK) {
   case G_LOG_LEVEL_ERROR:
      slevel = "error";
      break;

   case G_LOG_LEVEL_CRITICAL:
      slevel = "critical";
      break;

   case G_LOG_LEVEL_WARNING:
      slevel = "warning";
      break;

   case G_LOG_LEVEL_MESSAGE:
      slevel = "message";
      break;

   case G_LOG_LEVEL_INFO:
      slevel = "info";
      break;

   case G_LOG_LEVEL_DEBUG:
      slevel = "debug";
      break;

   default:
      slevel = "unknown";
   }

   return slevel;
}


/**
 * Forces the program to quit, optionally creating a core dump.
 */

static INLINE NORETURN void
VMToolsLogPanic(void)
{
   gPanicCount++;

   /*
    * Probably, flush the cached logs here. It is not
    * critial though because we will have the cached
    * logs in memory anyway.
    */

   if (gEnableCoreDump) {
      /*
       * TODO: Make sure to thaw the filesystem before dumping core.
       */
#if defined(_WIN32)
      CoreDump_CoreDump();
#else
      char cwd[PATH_MAX];
      if (getcwd(cwd, sizeof cwd) != NULL) {
         if (access(cwd, W_OK) == -1) {
            /*
             * Can't write to the working dir. chdir() to the user's home
             * directory as an attempt to get a valid core dump.
             */
            const char *home = getenv("HOME");
            if (home != NULL) {
               if (chdir(home)) {
                  /* Just to make glibc headers happy. */
               }
            }
         }
      }
      abort();
#endif
   }
   /* Same behavior as Panic_Panic(). */
   exit(-1);
}


/**
 * glib-based version of Str_Asprintf().
 *
 * @param[out] string   Where to store the result.
 * @param[in]  format   String format.
 * @param[in]  ...      String arguments.
 *
 * @return Number of bytes printed.
 */

gint
VMToolsAsprintf(gchar **string,
                gchar const *format,
                ...)
{
   gint cnt;
   va_list args;
   va_start(args, format);
   cnt = g_vasprintf(string, format, args);
   va_end(args);
   return cnt;
}


/**
 * VMTools_GetTimeAsString --
 *
 *    Returns the current UTC timestamp information
 *
 *    Ex: "2018-02-22T21:17:38.517Z"
 *
 *    The caller must free the return value using g_free
 *
 * @return Properly formatted string that contains the timestamp.
 *         NULL if the timestamp cannot be retrieved.
 */

gchar *
VMTools_GetTimeAsString(void)
{
   gchar *timePrefix = NULL;
   GDateTime *utcTime = g_date_time_new_now_utc();

   if (utcTime != NULL) {
      gchar *dateFormat = g_date_time_format(utcTime, "%FT%T");

      if (dateFormat != NULL) {
         gint msec = g_date_time_get_microsecond(utcTime) / 1000;

         timePrefix = g_strdup_printf("%s.%03dZ", dateFormat, msec);

         g_free(dateFormat);
         dateFormat = NULL;
      }

      g_date_time_unref(utcTime);
   }

   return timePrefix;
}


/**
 * Creates a formatted message to be logged. The format of the message will be:
 *
 *    [timestamp] [domain] [level] [thread_id] Log message
 *
 * @param[in] message      User log message.
 * @param[in] domain       Log domain.
 * @param[in] level        Log level.
 * @param[in] data         Log handler data.
 * @param[in] cached       If the message will be cached.
 *
 * @return Formatted log message according to the log domain's config.
 *         Should be g_free()'d.
 */

static gchar *
VMToolsLogFormat(const gchar *message,
                 const gchar *domain,
                 GLogLevelFlags level,
                 LogHandler *data,
                 gboolean cached)
{
   char *msg = NULL;
   const char *slevel;
   size_t len = 0;
   gboolean shared = TRUE;
   gboolean addsTimestamp = TRUE;
   gchar *tstamp;

   if (domain == NULL) {
      domain = gLogDomain;
   }

   /*
    * glib 2.16 on Windows has a bug where its printf implementations don't
    * like NULL.
    */
   if (message == NULL) {
      message = "<null>";
   }

   slevel = VMToolsLogLevelString(level);

   if (data->logger != NULL) {
      shared = data->logger->shared;
      addsTimestamp = data->logger->addsTimestamp;
   }

   tstamp = VMTools_GetTimeAsString();

   if (!addsTimestamp) {
      if (shared) {
         len = VMToolsAsprintf(&msg, "[%s] [%8s] [%s:%s] [%"FMT64"u] %s\n",
                               (tstamp != NULL) ? tstamp : "no time",
                               slevel, gLogDomain, domain,
                               VThreadBase_GetKernelID(), message);
      } else {
         len = VMToolsAsprintf(&msg, "[%s] [%8s] [%s] [%"FMT64"u] %s\n",
                               (tstamp != NULL) ? tstamp : "no time",
                               slevel, domain, VThreadBase_GetKernelID(),
                               message);
      }
   } else {
      if (cached) {
         if (shared) {
            len = VMToolsAsprintf(&msg,
                              "[cached at %s] [%8s] [%s:%s] [%"FMT64"u] %s\n",
                              (tstamp != NULL) ? tstamp : "no time", slevel,
                              gLogDomain, domain, VThreadBase_GetKernelID(),
                              message);
         } else {
            len = VMToolsAsprintf(&msg,
                               "[cached at %s] [%8s] [%s] [%"FMT64"u] %s\n",
                               (tstamp != NULL) ? tstamp : "no time", slevel,
                               domain, VThreadBase_GetKernelID(), message);
         }
      } else {
         if (shared) {
            len = VMToolsAsprintf(&msg, "[%8s] [%s:%s] [%"FMT64"u] %s\n",
                                  slevel, gLogDomain, domain,
                                  VThreadBase_GetKernelID(), message);
         } else {
            len = VMToolsAsprintf(&msg, "[%8s] [%s] [%"FMT64"u] %s\n", slevel,
                                  domain, VThreadBase_GetKernelID(), message);
         }
      }
   }

   g_free(tstamp);

   /*
    * The log messages from glib itself (and probably other libraries based
    * on glib) do not include a trailing new line. Most of our code does. So
    * we detect whether the original message already had a new line, and
    * remove it, to avoid having two newlines when printing our log messages.
    */
   if (msg != NULL && msg[len - 2] == '\n') {
      msg[len - 1] = '\0';
   }

   if (!msg) {
      /*
       * Memory allocation failure?
       */
      VMToolsLogPanic();
   }

   return msg;
}


/**
 * Function to free a cached LogEntry.
 *
 * @param[in] data    Log entry to be freed.
 */

static void
VMToolsFreeLogEntry(gpointer data)
{
   LogEntry *entry = data;

   g_free(entry->domain);
   g_free(entry->msg);
   g_free(entry);
}


/**
 * Function that logs a cached log header of Tools version, build details
 * and Guest OS details.
 *
 * @param[in] _data     LogEntry pointer.
 */

static void
VMToolsLogHeader(gpointer _data)
{
   LogEntry *entry = _data;
   GlibLogger *logger = entry->handler->logger;
   guint i;

   for (i = 0; i < gLogHeaderCount; i++) {
      char *message = VMToolsLogFormat(gLogHeaderBuf[i], entry->domain,
                                       G_LOG_LEVEL_MESSAGE, entry->handler,
                                       FALSE);

      logger->logfn(entry->domain, G_LOG_LEVEL_MESSAGE, message, logger);
      g_free(message);
   }
}


/**
 * Function that calls the log handler.
 *
 * Also, frees the _data to avoid having separate free call.
 *
 * @param[in] _data     LogEntry pointer.
 * @param[in] userData  User data pointer.
 */

static void
VMToolsLogMsg(gpointer _data, gpointer userData)
{
   LogEntry *entry = _data;
   GlibLogger *logger = entry->handler->logger;
   gboolean usedSyslog = FALSE;

   if (logger != NULL) {
       if (logger->logHeader) {
          VMToolsLogHeader(entry);
          logger->logHeader = FALSE;
       }
       logger->logfn(entry->domain, entry->level, entry->msg, logger);
       usedSyslog = entry->handler->isSysLog;
   } else if (gErrorData->logger != NULL) {
      gErrorData->logger->logfn(entry->domain, entry->level, entry->msg,
                                gErrorData->logger);
      usedSyslog = gErrorData->isSysLog;
   }

   /*
    * Any fatal errors need to go to syslog no matter what.
    */
   if (!usedSyslog && IS_FATAL(entry->level) && gErrorSyslog) {
      gErrorSyslog->logger->logfn(entry->domain, entry->level, entry->msg,
                                  gErrorSyslog->logger);
   }

   VMToolsFreeLogEntry(entry);
}


/**
 * This is called to avoid nested glib logging.
 * For example the VMX logger calls RpcChannel code which calls
 * Debug(), Warning() functions which calls the VMToolsLogWrapper()
 * function. This variable controls whether the code path there can call
 * glib logging function or not.
 * Log messages can still be handled using lower level functions.
 * NOTE: This must be called after acquiring LogState lock.
 */

static void
StopGlibLogging(void)
{
   gGlibLoggingStopped = TRUE;
}


/**
 * This is called to reset the state so that glib logging can be used again.
 * NOTE: This must be called after acquiring LogState lock.
 */

static void
RestartGlibLogging(void)
{
   gGlibLoggingStopped = FALSE;
}


/**
 * This is called to avoid nested infinite logging loop.
 * We set the flag in the log handler proxy function, Otherwise
 * if there log handler might call another Debug()/Warning() function
 * which would end up calling the log handler again, causing an infinite loop,
 * eventually overflow the stack.
 * NOTE: This must be called after acquiring LogState lock.
 */

static void
DisableLogHandler(void)
{
   gLogHandlerEnabled = FALSE;
}


/**
 * This is called to reset the state so that we can now call a log handler.
 * NOTE: This must be called after acquiring LogState lock.
 */

static void
EnableLogHandler(void)
{
   gLogHandlerEnabled = TRUE;
}


/**
 * Internal Log handler function that does the common processing of logs,
 * and delegates the actual printing of the message to the given handler.
 *
 * @param[in] domain    Log domain.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 * @param[in] _data     LogHandler pointer.
 */

static void
VMToolsLogInt(const gchar *domain,
              GLogLevelFlags level,
              const gchar *message,
              gpointer _data)
{
   LogHandler *data = _data;

   if (!gLogHandlerEnabled) {
      return;
   }

   DisableLogHandler();

   if (SHOULD_LOG(level, data)) {
      LogEntry *entry;

      data = data->inherited ? gDefaultData : data;

      entry = g_malloc0(sizeof(LogEntry));
      entry->domain = g_strdup(domain);
      entry->handler = data;
      entry->level = level;

      if (gLogIOSuspended && data->needsFileIO) {
         if (gMaxCacheEntries == 0) {
            /* No way to log at this point, drop it */
            VMToolsFreeLogEntry(entry);
            gDroppedLogCount++;
            goto exit;
         }

         entry->msg = VMToolsLogFormat(message, domain, level, data, TRUE);

         /*
          * Cache the log message
          */
         if (!gCachedLogs) {

            /*
             * If gMaxCacheEntries > 1K, start with 1/4th size
             * to avoid frequent allocations
             */
            gCachedLogs = g_ptr_array_sized_new(gMaxCacheEntries < 1024 ?
                                                gMaxCacheEntries :
                                                gMaxCacheEntries/4);
            if (!gCachedLogs) {
               VMToolsLogPanic();
            }

            /*
             * Some builds use glib version 2.16.4 which does not
             * support g_ptr_array_set_free_func function
             */
         }

         /*
          * We don't expect logging to be suspended for a long time,
          * so we can avoid putting a cap on cache size. However, we
          * still have a default cap of 4K messages, just to be safe.
          */
         if (gCachedLogs->len < gMaxCacheEntries) {
            g_ptr_array_add(gCachedLogs, entry);
         } else {
            /*
             * Cache is full, drop the oldest log message. This is not
             * very efficient but we don't expect this to be a common
             * case anyway.
             */
            LogEntry *oldest = g_ptr_array_remove_index(gCachedLogs, 0);
            VMToolsFreeLogEntry(oldest);
            gDroppedLogCount++;

            g_ptr_array_add(gCachedLogs, entry);
         }

      } else {
         entry->msg = VMToolsLogFormat(message, domain, level, data, FALSE);
         VMToolsLogMsg(entry, NULL);
      }
   }

exit:
   if (IS_FATAL(level)) {
      VMToolsLogPanic();
   }

   EnableLogHandler();
}


/**
 * Helper function to acquire locks and log to host side.
 *
 * @param[in] domain    Log domain.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 */

static void
LogToHost(const gchar *domain,
          GLogLevelFlags level,
          const gchar *message)
{
   if (!gLogEnabled || !gUseVmxGuestLog) {
      return;
   }

   /*
    * To avoid nested logging, such as a RpcChannel error, we need to disable
    * logging here. See bug 1069390.
    * This actually stop the glib based logging only. However,
    * log messages are saved directly to the file system using a lower
    * level function.
    */
   VMTools_AcquireLogStateLock();
   StopGlibLogging();

   /*
    * Protect the RPC channel and friends data race
    * Other thread might call g_xxx() or reinitialize the vmx guest logger.
    *
    * Recursive mutex is required as the set up function might calls
    * g_xxx() functions to log messages which would call this function here.
    *
    * Always acquire the log state mutex first followed by
    * the vmxGuestLog mutex to avoid a deadlock.
    *
    * This is because Debug()/Warning() functions call VMToolsLogWrapper()
    * which acquire the log state mutex and call g_xxx function which ends
    * up here.
    *
    * If the order is reversed here, we could end up deadlock
    * between two threads, one calling g_xxx(), and the other calling
    * Debug()/Warning()
    */

   g_rec_mutex_lock(&gVmxGuestLogMutex);
   VmxGuestLog(NULL == domain ? gLogDomain : domain,
               level, message);
   g_rec_mutex_unlock(&gVmxGuestLogMutex);

   RestartGlibLogging();
   VMTools_ReleaseLogStateLock();
}


/**
 * Helper function to acquire lock and log on the guest side.
 *
 * @param[in] domain    Log domain.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 * @param[in] _data     LogHandler pointer.
 */

static void
LogToGuest(const gchar *domain,
           GLogLevelFlags level,
           const gchar *message,
           gpointer _data)
{
   if (!gLogEnabled) {
      return;
   }

   /*
    * Disable glib logging as this function could be invoked from the glib
    * log handler.
    * This is OK since the function is called either directly with no intention
    * of writing log to host or we shall write the same log to the host
    * seprately using the LogToHost() function.
    */
   VMTools_AcquireLogStateLock();
   StopGlibLogging();

   /* Proxy to the LogHandler object pointed by _data. */
   VMToolsLogInt(domain, level, message, _data);

   RestartGlibLogging();
   VMTools_ReleaseLogStateLock();
}


/**
 * Log handler function that glib invokes on each g_xxx(...) invocation.
 *
 * @param[in] domain    Log domain.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 * @param[in] _data     LogHandler pointer.
 */

static void
VMToolsLog(const gchar *domain,
           GLogLevelFlags level,
           const gchar *message,
           gpointer _data)
{
   LogToHost(domain, level, message);
   LogToGuest(domain, level, message, _data);
}


/*
 *******************************************************************************
 * VMToolsDefaultLogFilePath --                                               */ /**
 *
 * @brief Creates a default log file path
 *
 * @param[in] domain    Domain name.
 *
 * @return Default path for the log file. Caller is responsibe to g_free() it.
 *
 *******************************************************************************
 */

static gchar *
VMToolsDefaultLogFilePath(const gchar *domain)
{
   gchar *path;
#ifdef WIN32
   gchar winDir[MAX_PATH];

   Win32U_ExpandEnvironmentStrings(DEFAULT_LOGFILE_DIR,
                                   (LPSTR) winDir, sizeof winDir);
   path = g_strdup_printf("%s%sTemp%s%s-%s-%s.log",
                          winDir, DIRSEPS, DIRSEPS,
                          DEFAULT_LOGFILE_NAME_PREFIX,
                          domain,
                          g_get_user_name());
#else
   path = g_strdup_printf("%s-%s-%s.log", DEFAULT_LOGFILE_NAME_PREFIX,
                          domain,
                          g_get_user_name());
#endif
   return path;
}


/*
 *******************************************************************************
 * VMToolsGetLogFilePath --                                               */ /**
 *
 * @brief Fetches sanitized path for the log file.
 *
 * @param[in] key       The key for log file path.
 * @param[in] domain    Domain name.
 * @param[in] cfg       Config dictionary.
 *
 * @return Sanitized path for the log file.
 *
 *******************************************************************************
 */

static gchar *
VMToolsGetLogFilePath(const gchar *key,
                      const gchar *domain,
                      GKeyFile *cfg)
{
   gsize len = 0;
   gchar *origPath = NULL;
   gchar *path = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);

   if (path == NULL) {
      return VMToolsDefaultLogFilePath(domain);
   }

   g_strchomp(path);

   len = strlen(path);
   origPath = path;

   /*
    * Drop all the preceding '"' chars
    */
   while (*path == '"') {
      path++;
      len--;
   }

   /*
    * Ensure that path contains something more
    * meaningful than just '"' chars
    */
   if (len == 0) {
      g_warning("Invalid path for domain '%s'.", domain);
      g_free(origPath);
      /*
       * Coverity flags this as a possible copy-paste error but assigning
       * to path rather than origPath is correct.
       */
      /* coverity[copy_paste_error] */
      path = NULL;
   } else {
      /* Drop the trailing '"' chars */
      gchar *sanePath = g_strdup(path);
      g_free(origPath);
      path = sanePath;

      if (path != NULL) {
         while (*(path + len - 1) == '"') {
            *(path + len - 1) = '\0';
            len--;
         }

         if (len == 0) {
            g_warning("Invalid path for domain '%s'.", domain);
            g_free(path);
            path = NULL;
         }
      }
   }

   return path;
}


/*
 *******************************************************************************
 * VMToolsGetLogHandler --                                                */ /**
 *
 * @brief Instantiates the log handler for the given domain.
 *
 * @param[in] handler   Handler name.
 * @param[in] domain    Domain name.
 * @param[in] mask      The log level mask.
 * @param[in] cfg       Config dictionary.
 *
 * @return A log handler.
 *
 *******************************************************************************
 */

static LogHandler *
VMToolsGetLogHandler(const gchar *handler,
                     const gchar *domain,
                     guint mask,
                     GKeyFile *cfg)
{
   LogHandler *logger;
   GlibLogger *glogger = NULL;
   gboolean needsFileIO = FALSE;
   gchar key[MAX_DOMAIN_LEN + 64];
   gboolean isSysLog = FALSE;
   gchar *path = NULL;

   if (strcmp(handler, "file") == 0 || strcmp(handler, "file+") == 0) {
      gboolean append = strcmp(handler, "file+") == 0;
      guint maxSize;
      guint maxFiles;
      GError *err = NULL;

      /* Use the same type name for both. */
      handler = "file";

      g_snprintf(key, sizeof key, "%s.data", domain);
      path = VMToolsGetLogFilePath(key, domain, cfg);
      if (path != NULL) {
         g_snprintf(key, sizeof key, "%s.maxLogSize", domain);
         maxSize = (guint) g_key_file_get_integer(cfg, LOGGING_GROUP, key, &err);
         if (err != NULL) {
            g_clear_error(&err);
            maxSize = 5; /* 5 megabytes default max size. */
         }

         g_snprintf(key, sizeof key, "%s.maxOldLogFiles", domain);
         maxFiles = (guint) g_key_file_get_integer(cfg, LOGGING_GROUP, key, &err);
         if (err != NULL) {
            g_clear_error(&err);
            maxFiles = 10;
         }

         glogger = GlibUtils_CreateFileLogger(path, append, maxSize, maxFiles);
         needsFileIO = TRUE;
      } else {
         g_warning("Missing path for domain '%s'.", domain);
      }
   } else if (strcmp(handler, "std") == 0) {
      glogger = GlibUtils_CreateStdLogger();
      needsFileIO = FALSE;
   } else if (strcmp(handler, "vmx") == 0) {
      glogger = VMToolsCreateVMXLogger();
      needsFileIO = FALSE;
#if defined(_WIN32)
   } else if (strcmp(handler, "outputdebugstring") == 0) {
      glogger = GlibUtils_CreateDebugLogger();
      needsFileIO = FALSE;
   } else if (strcmp(handler, "syslog") == 0) {
      glogger = GlibUtils_CreateEventLogger(L"VMware Tools", VMTOOLS_EVENT_LOG_MESSAGE);
      needsFileIO = FALSE;
      isSysLog = TRUE;
#else /* !_WIN32 */
   } else if (strcmp(handler, "syslog") == 0) {
      gchar *facility;

      /* Always get the facility from the default domain, since syslog is shared. */
      g_snprintf(key, sizeof key, "%s.facility", gLogDomain);
      facility = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
      if (facility != NULL) {
         g_strchomp(facility);
      }
      glogger = GlibUtils_CreateSysLogger(domain, facility);
      /*
       * Older versions of Linux make synchronous call to syslog.
       */
      needsFileIO = TRUE;
      g_free(facility);
      isSysLog = TRUE;
#endif
   } else {
      g_warning("Invalid handler for domain '%s': %s", domain, handler);
   }

   if (NULL == glogger) {
      g_warning("Failed to create a logger for handler: '%s'", handler);
   }

   logger = g_new0(LogHandler, 1);
   logger->domain = g_strdup(domain);
   logger->logger = glogger;
   logger->mask = mask;
   logger->type = strdup(handler);
   logger->needsFileIO = needsFileIO;
   logger->isSysLog = isSysLog;
   logger->confData = g_strdup(path);
   g_free(path);

   return logger;
}


/**
 * Determine if the domain can only use vmx logging.
 * Some domain such as vmvss cannot use file logger since the disk
 * is frozen.
 *
 * @param[in]  domain      Name of domain being configured.
 * @return     TRUE if the domain can only use vmx logging.
 *             FALSE otherwise.
 */

static gboolean
IsDomainVmxLoggingOnly(const gchar *domain)
{
   static const gchar *restricted[] = { "vmvss",
                                       NULL };
   unsigned int i;

   for(i = 0; restricted[i] != NULL; ++i) {
      if (strcmp(restricted[i], domain) == 0) {
         return TRUE;
      }
   }

   return FALSE;
}


/**
 * Configures the given log domain based on the data provided in the given
 * dictionary. If the log domain being configured doesn't match the default, and
 * no specific handler is defined for the domain, the handler is inherited from
 * the default domain, instead of using the default handler. This allows reusing
 * the same log file, for example, while maintaining the ability to enable
 * different log levels for different domains.
 *
 * For the above to properly work, the default log domain has to be configured
 * before any other domains.
 *
 * @param[in]  domain      Name of domain being configured.
 * @param[in]  cfg         Dictionary with config data.
 * @param[in]  oldDefault  Old default log handler.
 * @param[in]  oldDomains  List of old log domains.
 */

static void
VMToolsConfigLogDomain(const gchar *domain,
                       GKeyFile *cfg,
                       LogHandler *oldDefault,
                       GPtrArray *oldDomains)
{
   gchar *handler = NULL;
   gchar *level = NULL;
   gchar *confData = NULL;
   gchar key[128];
   gboolean isDefault = strcmp(domain, gLogDomain) == 0;

   GLogLevelFlags levelsMask;
   LogHandler *data = NULL;

   /* Arbitrary limit. */
   if (strlen(domain) > MAX_DOMAIN_LEN) {
      g_warning("Domain name too long: %s\n", domain);
      goto exit;
   } else if (strlen(domain) == 0) {
      g_warning("Invalid domain declaration, missing name.\n");
      goto exit;
   }

   g_snprintf(key, sizeof key, "%s.level", domain);
   level = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
   if (level == NULL) {
      level = g_strdup(VMTOOLS_LOGGING_LEVEL_DEFAULT);
   } else {
      g_strchomp(level);
   }

   /* Parse the handler information. */
   g_snprintf(key, sizeof key, "%s.handler", domain);
   handler = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
   if (handler != NULL) {
      g_strchomp(handler);
   }

   g_snprintf(key, sizeof key, "%s.data", domain);
   confData = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
   if (confData != NULL) {
      g_strchomp(confData);
   }

   /*
    * Disable the old vmx handler if we are setting up the vmx guest logger
    * This avoids sending duplicate messages to VMX and also makes the
    * file logger always available, e.g. logging errors when we cannot
    * send a message over the vmx guest logger.
    * Make an exception for vmvss where it might not be able to use a file
    * system log handler at all, e.g. dllhost.exe.
    * TBD: Create a no-op LogHandler type so as not to send vmx logs
    * twice for vmvss.
    */
   if (gUseVmxGuestLog && handler != NULL &&
       !IsDomainVmxLoggingOnly(domain) &&
       strcmp(handler, "vmx") == 0) {
      g_free(handler);
      handler = g_strdup(DEFAULT_HANDLER);
      g_info("Switched %s log handler from vmx to " DEFAULT_HANDLER ".\n",
             domain);
   }

   if (handler == NULL && isDefault) {
      /*
       * If no handler defined and we're configuring the default domain,
       * then instantiate the default handler.
       */
      handler = g_strdup(DEFAULT_HANDLER);
   }

   if (confData == NULL) {
      if ((g_strcmp0(handler, "file") == 0) || (g_strcmp0(handler, "file+") == 0)) {
         confData = VMToolsDefaultLogFilePath(domain);
      }
   }

   /* Parse the log level configuration, and build the mask. */
   if (strcmp(level, "error") == 0) {
      levelsMask = G_LOG_LEVEL_ERROR;
   } else if (strcmp(level, "critical") == 0) {
      levelsMask = G_LOG_LEVEL_ERROR |
                   G_LOG_LEVEL_CRITICAL;
   } else if (strcmp(level, "warning") == 0) {
      levelsMask = G_LOG_LEVEL_ERROR |
                   G_LOG_LEVEL_CRITICAL |
                   G_LOG_LEVEL_WARNING;
   } else if (strcmp(level, "message") == 0) {
      levelsMask = G_LOG_LEVEL_ERROR |
                   G_LOG_LEVEL_CRITICAL |
                   G_LOG_LEVEL_WARNING |
                   G_LOG_LEVEL_MESSAGE;
   } else if (strcmp(level, "info") == 0) {
      levelsMask = G_LOG_LEVEL_ERROR |
                   G_LOG_LEVEL_CRITICAL |
                   G_LOG_LEVEL_WARNING |
                   G_LOG_LEVEL_MESSAGE |
                   G_LOG_LEVEL_INFO;
   } else if (strcmp(level, "debug") == 0) {
      levelsMask = G_LOG_LEVEL_MASK;
   } else if (strcmp(level, "none") == 0) {
      levelsMask = 0;
   } else {
      g_warning("Unknown log level (%s): %s\n", domain, level);
      goto exit;
   }

   if (handler != NULL) {
      /*
       * Check whether there's an old domain with the same type configured
       * for the same domain. If there is, use it instead. Otherwise,
       * instantiate a new logger. For the check, consider both file logger
       * types as the same.
       */
      const char *oldtype = oldDefault != NULL ? oldDefault->type : NULL;
      const char *oldData = oldDefault != NULL ? oldDefault->confData : NULL;

      if (g_strcmp0(oldtype, "file+") == 0) {
         oldtype = "file";
      }

      if (isDefault && g_strcmp0(oldtype, handler) == 0) {
         /* check for a filename change */
         if (g_strcmp0(oldData, confData) == 0) {
            data = oldDefault;
         }
      } else if (oldDomains != NULL) {
         guint i;
         for (i = 0; i < oldDomains->len; i++) {
            LogHandler *old = g_ptr_array_index(oldDomains, i);
            if (old != NULL && !old->inherited && strcmp(old->domain, domain) == 0) {
               if (g_strcmp0(old->type, handler) == 0 &&
                   g_strcmp0(old->confData, confData) == 0) {
                  data = old;
                  oldDomains->pdata[i] = NULL;
               }
               break;
            }
         }
      }

      if (data == NULL) {
         data = VMToolsGetLogHandler(handler, domain, levelsMask, cfg);
      } else {
         data->mask = levelsMask;
      }
   } else {
      /* An inherited handler. Just create a dummy instance. */
      ASSERT(gDefaultData != NULL);
      data = g_new0(LogHandler, 1);
      data->domain = g_strdup(domain);
      data->inherited = TRUE;
      data->mask = levelsMask;
      data->isSysLog = FALSE;
      data->confData = g_strdup(confData);
   }

   if (isDefault) {
      gDefaultData = data;
      g_log_set_default_handler(VMToolsLog, gDefaultData);
   } else {
      if (gDomains == NULL) {
         gDomains = g_ptr_array_new();
      }
      g_ptr_array_add(gDomains, data);
      if (data->handlerId == 0) {
         data->handlerId = g_log_set_handler(domain,
                                             G_LOG_LEVEL_MASK |
                                             G_LOG_FLAG_FATAL |
                                             G_LOG_FLAG_RECURSION,
                                             VMToolsLog,
                                             data);
      }
   }

exit:
   g_free(handler);
   g_free(level);
   g_free(confData);
}


/**
 * Resets the vmtools logging subsystem, freeing up data and restoring the
 * original glib configuration.
 *
 * @param[in]  hard     Whether to do a "hard" reset of the logging system
 *                      (cleaning up any log domain existing state and freeing
 *                      associated memory).
 */

static void
VMToolsResetLogging(gboolean hard)
{
   gLogEnabled = FALSE;
   g_log_set_default_handler(g_log_default_handler, NULL);

   CLEAR_LOG_HANDLER(gErrorData);
   CLEAR_LOG_HANDLER(gErrorSyslog);
   gErrorData = NULL;
   gErrorSyslog = NULL;

   if (gDomains != NULL) {
      guint i;
      for (i = 0; i < gDomains->len; i++) {
         LogHandler *data = g_ptr_array_index(gDomains, i);
         g_log_remove_handler(data->domain, data->handlerId);
         data->handlerId = 0;
         if (hard) {
            CLEAR_LOG_HANDLER(data);
         }
      }
      if (hard) {
         g_ptr_array_free(gDomains, TRUE);
         gDomains = NULL;
      }
   }

   if (hard) {
      CLEAR_LOG_HANDLER(gDefaultData);
      gDefaultData = NULL;
   }

   if (gLogDomain != NULL) {
      g_free(gLogDomain);
      gLogDomain = NULL;
   }
}


/* Public API. */


#if defined(_WIN32)

/**
 * Attaches a console to the current process. If the parent process already has
 * a console open, reuse it. Otherwise, create a new console for the current
 * process. Win32-only.
 *
 * It's safe to call this function multiple times (it won't do anything if
 * the process already has a console).
 *
 * @note Attaching to the parent process's console is only available on XP and
 * later.
 *
 * @return Whether the process is attached to a console.
 */

gboolean
VMTools_AttachConsole(void)
{
   return GlibUtils_AttachConsole();
}

#endif


/**
 * Mark log system initialized.
 *
 * Also initialize the log state lock.
 */

static void
MarkLogInitialized(void)
{
   if (!gLogInitialized) {
      gLogInitialized = TRUE;
   }
}


/**
 * Configures the logging system to log to the STDIO.
 *
 * @param[in] defaultDomain   Name of the default log domain.
 */

void
VMTools_ConfigLogToStdio(const gchar *domain)
{
   static LogHandler *gStdLogHandler = NULL;
   GKeyFile *cfg;

   g_return_if_fail(gStdLogHandler == NULL); /* Already called */

   ASSERT(domain != NULL);
   gLogDomain = g_strdup(domain);
   cfg = g_key_file_new();
   gStdLogHandler = VMToolsGetLogHandler(STD_HANDLER,
                                         gLogDomain,
                                         ~0,
                                         cfg);
   if (!gStdLogHandler) {
      fprintf(stderr, "Failed to create the STD log handler\n");
      goto exit;
   }

   g_log_set_handler(gLogDomain, ~0, VMToolsLog, gStdLogHandler);

   gLogEnabled = TRUE;

   MarkLogInitialized();

exit:
   g_key_file_free(cfg);
}


/**
 * Internal Helper function without holding the state lock.
 * Configures the logging system according to the configuration in the given
 * dictionary.
 *
 * Optionally, it's possible to reset the logging subsystem; this will shut
 * down all log handlers managed by the vmtools library before configuring
 * the log system, which means that logging will behave as if the application
 * was just started. A visible side-effect of this is that log files may be
 * rotated (if they're not configure for appending).
 *
 * @param[in] defaultDomain   Name of the default log domain.
 * @param[in] cfg             The configuration data. May be NULL.
 * @param[in] force           Whether to force logging to be enabled.
 * @param[in] reset           Whether to reset the logging subsystem first.
 */

static void
VMToolsConfigLoggingInt(const gchar *defaultDomain,
                        GKeyFile *cfg,
                        gboolean force,
                        gboolean reset)
{
   gboolean allocDict = (cfg == NULL);
   gchar **list;
   gchar **curr;
   GPtrArray *oldDomains = NULL;
   LogHandler *oldDefault = NULL;
   GError *err = NULL;
   char *gosDetails;

   g_return_if_fail(defaultDomain != NULL);

   if (allocDict) {
      cfg = g_key_file_new();
   }

   /*
    * If not resetting the logging system, keep the old domains around. After
    * we're done loading the new configuration, we'll go through the old domains
    * and restore any data that needs restoring, and clean up anything else.
    */
   VMToolsResetLogging(reset);
   if (!reset) {
      oldDefault = gDefaultData;
      oldDomains = gDomains;
      gDomains = NULL;
      gDefaultData = NULL;
   }

   gLogDomain = g_strdup(defaultDomain);
   gErrorData = VMToolsGetLogHandler(SAFE_HANDLER,
                                     gLogDomain,
                                     G_LOG_LEVEL_MASK,
                                     cfg);

   /*
    * The syslog handler used for G_LOG_FLAG_FATAL.
    * This is only used if the default handler isn't type 'syslog'.
    */
   gErrorSyslog = VMToolsGetLogHandler("syslog",
                                       gLogDomain,
                                       G_LOG_FLAG_FATAL,
                                       cfg);

   /*
    * Configure the default domain first. See function documentation for
    * VMToolsConfigLogDomain() for the reason.
    */
   VMToolsConfigLogDomain(gLogDomain, cfg, oldDefault, oldDomains);

   list = g_key_file_get_keys(cfg, LOGGING_GROUP, NULL, NULL);

   for (curr = list; curr != NULL && *curr != NULL; curr++) {
      gchar *domain = *curr;

      /* Check whether it's a domain "declaration". */
      if (!g_str_has_suffix(domain, ".level")) {
         continue;
      }

      /* Trims ".level" from the key to get the domain name. */
      domain[strlen(domain) - 6] = '\0';

      /* Skip the default domain. */
      if (strcmp(domain, gLogDomain) == 0) {
         continue;
      }

      VMToolsConfigLogDomain(domain, cfg, oldDefault, oldDomains);
   }

   g_strfreev(list);

   gLogEnabled = g_key_file_get_boolean(cfg, LOGGING_GROUP, "log", NULL);

   /*
    * Need to set these so that the code below in this function
    * can also log messages.
    */
   gLogEnabled |= force;
   MarkLogInitialized();

   /*
    * Cache Tools version, build number and guest OS details.
    * Cached log headers will be logged at log rotation and reset.
    * No need to re-init the log headers in case of config reload.
    */
   if (gLogHeaderCount == 0) {
      LogHandler *handler = GetLogHandlerByDomain(gLogDomain);
      GlibLogger *logger = handler->logger;

      logger->logHeader = TRUE;

      gLogHeaderBuf[gLogHeaderCount++] = Str_Asprintf(NULL,
                                                      "%s Version: %s (%s)",
                                                      VMWARE_TOOLS_SHORT_NAME,
                                                      TOOLS_VERSION_EXT_CURRENT_STR,
                                                      BUILD_NUMBER);

      gosDetails = Hostinfo_GetOSDetailedData();
      if (gosDetails != NULL && gLogHeaderCount < LOG_HEADER_MAX_ENTRIES) {
         gLogHeaderBuf[gLogHeaderCount++] = Str_Asprintf(NULL,
                                                         "Guest OS details: %s",
                                                         gosDetails);
      }
      free(gosDetails);

      ASSERT(gLogHeaderCount <= LOG_HEADER_MAX_ENTRIES);
   }

   gMaxCacheEntries = g_key_file_get_integer(cfg, LOGGING_GROUP,
                                             "maxCacheEntries", &err);
   if (err != NULL || gMaxCacheEntries < 0) {
      /*
       * Use default value in case of error.
       * A value '0' will turn off log caching.
       */
      gMaxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
      if (err != NULL) {
         if (err->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
             err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
            g_warning("Invalid value for maxCacheEntries key: Error %d.",
                      err->code);
         }
         g_clear_error(&err);
      }
   }

   if (gMaxCacheEntries > 0) {
      g_message("Log caching is enabled with maxCacheEntries=%d.",
                gMaxCacheEntries);
   } else {
      g_message("Log caching is disabled.");
   }

   if (g_key_file_has_key(cfg, LOGGING_GROUP, "enableCoreDump", NULL)) {
      gEnableCoreDump = g_key_file_get_boolean(cfg, LOGGING_GROUP,
                                               "enableCoreDump", NULL);
   }

   /* If needed, restore the old configuration. */
   if (!reset) {
      if (oldDomains != NULL) {
         guint i;
         for (i = 0; i < oldDomains->len; i++) {
            LogHandler *old = g_ptr_array_index(oldDomains, i);
            CLEAR_LOG_HANDLER(old);
         }
         g_ptr_array_free(oldDomains, TRUE);
      }
      if (oldDefault != NULL && oldDefault != gDefaultData) {
         CLEAR_LOG_HANDLER(oldDefault);
         oldDefault = NULL;
      }
   }

   /*
    * If core dumps are enabled (default: TRUE), then set up the exception
    * filter on Win32. On POSIX systems, try to modify the resource limit
    * to allow core dumps, but don't complain if it fails. Core dumps may
    * still fail, e.g., if the current directory is not writable by the
    * user running the process.
    *
    * On POSIX systems, if the process is itself requesting a core dump (e.g.,
    * by calling Panic() or g_error()), the core dump routine will try to find
    * a location where it can successfully create the core file. Applications
    * can try to set up core dump filters (e.g., a signal handler for SIGSEGV)
    * that can then call any of the core dumping functions handled by this
    * library.
    *
    * On POSIX systems, the maximum size of a core dump can be controlled by
    * the "maxCoreSize" config option, where "0" means "no limit". By default,
    * it's set to 5MB.
    */
   if (gEnableCoreDump) {
#if defined(_WIN32)
      if (g_key_file_has_key(cfg, LOGGING_GROUP, "coreDumpFlags", NULL)) {
         guint coreDumpFlags;
         coreDumpFlags = g_key_file_get_integer(cfg, LOGGING_GROUP, "coreDumpFlags", &err);
         if (err != NULL) {
            coreDumpFlags = 0;
            g_clear_error(&err);
         }
         /*
          * For flag values and information on their meanings see:
          * http://msdn.microsoft.com/en-us/library/windows/desktop/ms680519(v=vs.85).aspx
          */
         coreDumpFlags &= MiniDumpValidTypeFlags;
         g_message("Core dump flags set to %u", coreDumpFlags);
         Panic_SetCoreDumpFlags(coreDumpFlags);
      }
      CoreDump_SetUnhandledExceptionFilter();
#else
      struct rlimit limit = { 0, 0 };

      getrlimit(RLIMIT_CORE, &limit);
      if (limit.rlim_max != 0) {
         limit.rlim_cur = (rlim_t) g_key_file_get_integer(cfg,
                                                          LOGGING_GROUP,
                                                          "maxCoreSize",
                                                          &err);
         if (err != NULL) {
            limit.rlim_cur = 5 * 1024 * 1024;
            g_clear_error(&err);
         } else if (limit.rlim_cur == 0) {
            limit.rlim_cur = RLIM_INFINITY;
         }

         limit.rlim_cur = MAX(limit.rlim_cur, limit.rlim_max);
         if (setrlimit(RLIMIT_CORE, &limit) == -1) {
            g_message("Failed to set core dump size limit, error %d (%s)\n",
                      errno, g_strerror(errno));
         } else {
            g_message("Core dump limit set to %d", (int) limit.rlim_cur);
         }
      }
#endif
   }

   if (allocDict) {
      g_key_file_free(cfg);
   }
}


/**
 * Configures the logging system according to the configuration in the given
 * dictionary.
 *
 * Optionally, it's possible to reset the logging subsystem; this will shut
 * down all log handlers managed by the vmtools library before configuring
 * the log system, which means that logging will behave as if the application
 * was just started. A visible side-effect of this is that log files may be
 * rotated (if they're not configure for appending).
 *
 * @param[in] defaultDomain   Name of the default log domain.
 * @param[in] cfg             The configuration data. May be NULL.
 * @param[in] force           Whether to force logging to be enabled.
 * @param[in] reset           Whether to reset the logging subsystem first.
 */

void
VMTools_ConfigLogging(const gchar *defaultDomain,
                      GKeyFile *cfg,
                      gboolean force,
                      gboolean reset)
{
   VMTools_AcquireLogStateLock();

   VMToolsConfigLoggingInt(defaultDomain, cfg, force, reset);

   VMTools_ReleaseLogStateLock();
}


/* Wrappers for VMware's logging functions. */

/*
 *******************************************************************************
 * VMToolsLogWrapper --                                                   */ /**
 *
 * Generic wrapper for VMware log functions.
 *
 * CoreDump_CoreDump() may log, and glib doesn't like recursive log calls. So
 * if recursing, bypass glib and log to the default domain's log handler.
 *
 * @param[in]  level    Log level.
 * @param[in]  fmt      Message format.
 * @param[in]  args     Message arguments.
 *
 *******************************************************************************
 */

static void
VMToolsLogWrapper(GLogLevelFlags level,
                  const char *fmt,
                  va_list args)
{
   if (!gLogInitialized && !IS_FATAL(level)) {
      /*
       * Avoid logging without initialization because
       * it leads to spamming of the console output.
       * Fatal messages are exception.
       */
      return;
   }

   VMTools_AcquireLogStateLock();

   if (gPanicCount == 0) {
      char *msg = Str_Vasprintf(NULL, fmt, args);

      if (msg != NULL) {

         if (gGlibLoggingStopped) {
            /*
             * Stop logging using glib logging functions such as g_log()
             * This is to avoid nested logging in vmxLogger
             * However, we should still directly call the lower level
             * primitive to directly write to the guest file system.
             */
            VMToolsLogInt(gLogDomain, level, msg, gDefaultData);
         } else {
            g_log(gLogDomain, level, "%s", msg);
         }

         free(msg);
      }
   } else {
      /* Try to avoid malloc() since we're forcibly quitting. */
      gchar msg[256];
      Str_Vsnprintf(msg, sizeof msg, fmt, args);
      VMToolsLogInt(gLogDomain, level, msg, gDefaultData);
   }

   VMTools_ReleaseLogStateLock();
}


/*
 *******************************************************************************
 * CreateRpcChannel --                                                    */ /**
 *
 * Create the dedicated RPCI channel for querying the guest level setting
 * and sending logs.
 *
 * @return TRUE if the RPCI channel is ready.
 *         FALSE otherwise.
 *
 *******************************************************************************
 */

static gboolean
CreateRpcChannel(void)
{
   if (NULL != gChannel) {
      return TRUE; // already created and started
   }

   gChannel = BackdoorChannel_New();
   if (NULL == gChannel) {
      g_warning("Failed to create the RPCI channel for logging.\n");
      return FALSE;
   }

   if (!RpcChannel_Start(gChannel)) {
      g_warning("Failed to start the RPCI channel for logging.\n");
      RpcChannel_Destroy(gChannel);
      gChannel = NULL;
      return FALSE;
   }

   g_debug("RPCI Channel for logging is created successfully.\n");

   return TRUE;
}


/*
 *******************************************************************************
 * DestroyRpcChannel --                                                   */ /**
 *
 * Destroy the dedicated RPCI channel.
 *
 *******************************************************************************
 */

static void
DestroyRpcChannel(void)
{
   if (NULL == gChannel) {
      return;
   }

   RpcChannel_Stop(gChannel);
   RpcChannel_Destroy(gChannel);

   gChannel = NULL;

   /* Fix log recursion issue, check bug: 2607084 for more details */
   Debug("RPCI Channel for logging is destroyed successfully.\n");
}


/*
 *******************************************************************************
 * LevelMask --                                                           */ /**
 *
 * Convert level string to a level mask value.
 *
 * @param[in] level           the input level.
 * @param[in] allowDebugLog   the switch that controls whether we allow sending
 *                            debug level log messages.
 * @return the level mask
 *
 *******************************************************************************
 */

static GLogLevelFlags
LevelMask(const gchar *level,
          gboolean allowDebugLog)
{
   GLogLevelFlags result;

   /* level to glib log mask translation */

   if (strcmp(level, "error") == 0) {
      result = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
   } else if (strcmp(level, "warning") == 0) {
      result = G_LOG_LEVEL_ERROR |
         G_LOG_LEVEL_CRITICAL |
         G_LOG_LEVEL_WARNING;
   } else if (strcmp(level, "notice") == 0 ||
              strcmp(level, "info") == 0 ||
              strcmp(level, "message") == 0) {
      result = G_LOG_LEVEL_ERROR |
         G_LOG_LEVEL_CRITICAL |
         G_LOG_LEVEL_WARNING |
         G_LOG_LEVEL_MESSAGE |
         G_LOG_LEVEL_INFO;
   } else if (strcmp(level, "verbose") == 0 ||
              strcmp(level, "debug") == 0 ||
              strcmp(level, "trivia") == 0) {
      result = G_LOG_LEVEL_MASK;
      if (!allowDebugLog) {
         result &= ~G_LOG_LEVEL_DEBUG;
      }
   } else {
      /* treated as off */
      result = 0;
   }

   return result;
}


/*
 *******************************************************************************
 * LoadFallbackSetting --                                                 */ /**
 *
 * Load the log fallback setting from a tools config object.
 *
 * TBD: Extend this function load the vmx handler setting into a dictionary
 * of [domain, level] mapping instead of just a single level from the default
 * domain. The Log handler code can use the dictonary vs a single level mask.
 * The VMX side can also be extended to return an dictionary of [domain, level]
 * mapping as well. For now, let us stick with the simple.
 *
 * @param [in] cfg    a tools config object.
 *
 *******************************************************************************
 */

static void
LoadFallbackSetting(GKeyFile *cfg)
{
   gchar *handler;
   gchar *level;
   gchar key[128];

   ASSERT(NULL != cfg);

   g_snprintf(key, sizeof key, "%s.handler", gLogDomain);
   handler = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);

   if (NULL == handler) {
      g_debug("%s.handler not present in config file.\n", gLogDomain);
      return;
   }

   g_strchomp(handler);

   if (strcmp(handler, "vmx") != 0) {
      g_debug("%s.handler is not a vmx handler in config file.\n", gLogDomain);
      g_free(handler);
      return;
   }

   g_free(handler);

   /* The tools.conf use the vmx handler, let us continue to load the level */
   g_snprintf(key, sizeof key, "%s.level", gLogDomain);
   level = g_key_file_get_string(cfg, LOGGING_GROUP, key, NULL);
   if (NULL == level) {
      level = g_strdup(VMTOOLS_LOGGING_LEVEL_DEFAULT);
   } else {
      g_strchomp(level);
   }

   /* If guest admin allows debug log messages sent to host, honor it. */
   gLevelMask = LevelMask(level, TRUE);

   g_free(level);

   if (gLevelMask != 0) {
      gRpcMode = RPC_LOG_FALLBACK;
   } else {
      gRpcMode = RPC_OFF;
   }
}


/*
 *******************************************************************************
 * SetupLogLevelAndRpcMode --                                             */ /**
 *
 * Query the host to set up the log level and the RPC mode.
 * If the host does not support the guest.log.state guest RPC, we fallback
 * load the config and use the log RPC.
 *
 * @param[in/opt]  cfg                  tools config file object.
 * @param[in/opt]  level                log level string from vmx
 * @return         TRUE  if RPC channel is created and ready to send log.
 *                 FALSE otherwise.
 *
 *******************************************************************************
 */

static gboolean
SetupLogLevelAndRpcMode(GKeyFile *cfg,
                        const gchar *level)
{
   gboolean isDebugLogAllowed;
   gboolean useLogTextRpc = g_key_file_get_boolean(cfg, LOGGING_GROUP,
                                                   "useLogTextRpc", NULL);

   g_info("Configuration %s.useLogTextRpc is %s\n", LOGGING_GROUP,
          useLogTextRpc ? "TRUE" : "FALSE");
   /*
    * Perhaps it is better to have tools.conf switch that allow log debug
    * message to the host. However, this might confuse the user by allowing
    * the configuration on both sides. TBD.
    */
#ifdef ALLOW_DEBUG_LOG_TO_HOST
   isDebugLogAllowed = TRUE;
#else
   isDebugLogAllowed = FALSE;
#endif

   gRpcMode = RPC_OFF;

   if (!useLogTextRpc) {
      LoadFallbackSetting(cfg);
      if (gRpcMode != RPC_OFF) {
         CreateRpcChannel();
      }
      goto done;
   }

   /* Following are when useLogTextRpc is TRUE */
   if (!CreateRpcChannel()) {
      g_info("The LOG RPC channel is not up, skip query log state.\n");
      goto done;
   }

   if (NULL == level) {
      char *result = NULL;
      size_t resultLen;

      if (!RpcChannel_Send(gChannel, GUEST_LOG_STATE_CMD,
                           sizeof GUEST_LOG_STATE_CMD,
                           &result, &resultLen)) {
         g_warning("Failed to send " GUEST_LOG_STATE_CMD " command to VMX.\n");
         LoadFallbackSetting(cfg);
         RpcChannel_Free(result);
         goto done;
      }

      /* The RpcChannel_Send() NULL terminate the response */
      g_info("Received host log level '%s'.\n", result);

      gLevelMask = LevelMask(result, isDebugLogAllowed);

      RpcChannel_Free(result);
   } else {
      gLevelMask = LevelMask(level, isDebugLogAllowed);
   }

   if (gLevelMask != 0) {
      gRpcMode = RPC_GUEST_LOG_TEXT;
   } else {
      gRpcMode = RPC_OFF;
   }

done:

   if (NULL == gChannel) {
      gRpcMode = RPC_OFF;
   }

   if (RPC_OFF == gRpcMode) {
      /*
       * No need to keep around the RPC channel,
       * VMX can give it to other apps.
       */
      DestroyRpcChannel();
      return FALSE;
   }

   return TRUE;
}


/*
 *******************************************************************************
 * VmxGuestLog --                                                         */ /**
 *
 * Logs a message to the VMX using RpcChannel.
 *
 * @param[in] domain    Unused.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 *
 *******************************************************************************
 */

static void
VmxGuestLog(const gchar *domain,
            GLogLevelFlags level,
            const gchar *message)
{
   if (!(gLevelMask & level)) { /* level is not sufficient */
      return;
   }

   if (NULL == gChannel) {
      /* This could happen upon a toolsd reset, e.g. vMotion */
      Debug("The LOG RPC channel is not up, skip logging.\n");
      return;
   }

   if (RPC_GUEST_LOG_TEXT == gRpcMode) {
      gchar *msg = NULL;
      gint len = VMToolsAsprintf(&msg, GUEST_LOG_TEXT_CMD
                                 " [%s] [%s] [%s] %s", gAppName,
                                 VMToolsLogLevelString(level), domain, message);
      if (NULL == msg) {
         VMToolsLogPanic();
      }

      if (!RpcChannel_Send(gChannel, msg, len, NULL, NULL)) {
         Warning("Failed to send " GUEST_LOG_TEXT_CMD " command to VMX.\n");
         /* Transition to the fallback mode */
         gRpcMode = RPC_LOG_FALLBACK;
      }
      g_free(msg);
   }

   if (RPC_LOG_FALLBACK == gRpcMode) {
      gchar *msg = NULL;
      gint len = VMToolsAsprintf(&msg,
                                 "log [%s] [%s] %s",
                                 VMToolsLogLevelString(level), domain, message);
      if (NULL == msg) {
         VMToolsLogPanic();
      }

      if (!RpcChannel_Send(gChannel, msg, len, NULL, NULL)) {
         Warning("Failed to send log command to VMX.\n");
         /*
          * VMX might have done a channel reset. This needs to be tolerated.
          * Transition to the off mode, let the tools reset handle the rest
          */
         gRpcMode = RPC_OFF;
         DestroyRpcChannel();
      }
      g_free(msg);
   }
}


/**
 * Acquire the log state lock.
 */

void
VMTools_AcquireLogStateLock(void)
{
   g_rec_mutex_lock(&gLogStateMutex);
}


/**
 * Release the log state lock.
 */

void
VMTools_ReleaseLogStateLock(void)
{
   g_rec_mutex_unlock(&gLogStateMutex);
}


/**
 * Suspend IO caused by logging activity.
 */

void
VMTools_SuspendLogIO()
{
   gLogIOSuspended = TRUE;
}


/**
 * Resume IO caused by logging activity.
 */

void
VMTools_ResumeLogIO()
{
   guint cachedEntries = 0;

   /*
    * Resume the log IO first, so that we can also log messages
    * from within this function itself!
    */
   gLogIOSuspended = FALSE;

   /*
    * Flush the cached log messages, if any
    */
   if (gCachedLogs) {
      cachedEntries = gCachedLogs->len;
      g_ptr_array_foreach(gCachedLogs, VMToolsLogMsg, NULL);
      g_ptr_array_free(gCachedLogs, TRUE);
      gCachedLogs = NULL;
   }

   g_debug("Flushed %u log messages from cache after resuming log IO.",
           cachedEntries);

   if (gDroppedLogCount > 0) {
      g_warning("Dropped %u log messages from cache.", gDroppedLogCount);
      gDroppedLogCount = 0;
   }
}


/**
 * Logs a message using the G_LOG_LEVEL_DEBUG level.
 *
 * @param[in] fmt Log message format.
 */

void
Debug(const char *fmt, ...)
{
   va_list args;
   va_start(args, fmt);
   /*
    * Preserve errno/lastError.
    * This keeps compatibility with bora/lib Log(), preventing
    * Log() calls in bora/lib code from clobbering errno/lastError.
    */
   WITH_ERRNO(err, VMToolsLogWrapper(G_LOG_LEVEL_DEBUG, fmt, args));
   va_end(args);
}


/**
 * Logs a message using the G_LOG_LEVEL_INFO level.
 *
 * @param[in] fmt Log message format.
 */

void
Log(const char *fmt, ...)
{
   va_list args;
   va_start(args, fmt);
   /*
    * Preserve errno/lastError.
    * This keeps compatibility with bora/lib Log(), preventing
    * Log() calls in bora/lib code from clobbering errno/lastError.
    */
    WITH_ERRNO(err, VMToolsLogWrapper(G_LOG_LEVEL_INFO, fmt, args));
   va_end(args);
}


/**
 * Logs a message with the given log level.
 *
 * Translates lib/log levels into glib levels, and sends the message to the log
 * implementation.
 *
 * @param[in]  level    Log level.
 * @param[in]  fmt      Log message format.
 * @param[in]  args     Log message arguments.
 */

void
LogV(uint32 routing,
     const char *fmt,
     va_list args)
{
   int glevel;

   switch (routing) {
   case VMW_LOG_PANIC:
      glevel = G_LOG_LEVEL_ERROR;
      break;

   case VMW_LOG_ERROR:
      glevel = G_LOG_LEVEL_CRITICAL;
      break;

   case VMW_LOG_WARNING:
      glevel = G_LOG_LEVEL_WARNING;
      break;

   case VMW_LOG_INFO:
      glevel = G_LOG_LEVEL_MESSAGE;
      break;

   case VMW_LOG_VERBOSE:
      glevel = G_LOG_LEVEL_INFO;
      break;

   default:
      glevel = G_LOG_LEVEL_DEBUG;
   }

   /*
    * Preserve errno/lastError.
    * This keeps compatibility with bora/lib Log(), preventing
    * Log() calls in bora/lib code from clobbering errno/lastError.
    */
   WITH_ERRNO(err, VMToolsLogWrapper(glevel, fmt, args));
}


/*
 *******************************************************************************
 * Panic --                                                               */ /**
 *
 * Logs a message using the G_LOG_LEVEL_ERROR level. In the default
 * configuration, this will cause the application to terminate and,
 * if enabled, to dump core.
 *
 * @param[in] fmt Log message format.
 *
 *******************************************************************************
 */

void
Panic(const char *fmt, ...)
{
   va_list args;

   va_start(args, fmt);

   if (gPanicCount == 0) {
      char *msg = Str_Vasprintf(NULL, fmt, args);
      if (msg != NULL) {
         g_log(gLogDomain, G_LOG_LEVEL_ERROR, "%s", msg);
         free(msg);
      }
      /*
       * In case an user-installed custom handler doesn't panic on error,
       * force a core dump. Also force a dump in the recursive case.
       */
      VMToolsLogPanic();
   } else if (gPanicCount == 1) {
      /*
       * Use a stack allocated string since we're in a recursive panic, so
       * probably already in a weird state.
       */
      gchar msg[1024];
      Str_Vsnprintf(msg, sizeof msg, fmt, args);
      fprintf(stderr, "Recursive panic: %s\n", msg);
      VMToolsLogPanic();
   } else {
      fprintf(stderr, "Recursive panic, giving up.\n");
      exit(-1);
   }
   va_end(args);
   while (1) ; // avoid compiler warning
}


/*
 *******************************************************************************
 * Warning --                                                             */ /**
 *
 * Logs a message using the G_LOG_LEVEL_WARNING level.
 *
 * @param[in] fmt Log message format.
 *
 *******************************************************************************
 */

void
Warning(const char *fmt, ...)
{
   va_list args;
   va_start(args, fmt);
   /*
    * Preserve errno/lastError.
    * This keeps compatibility with bora/lib Log(), preventing
    * Log() calls in bora/lib code from clobbering errno/lastError.
    */
   WITH_ERRNO(err, VMToolsLogWrapper(G_LOG_LEVEL_WARNING, fmt, args));
   va_end(args);
}


/*
 *******************************************************************************
 * VmwareLogWrapper --                                                    */ /**
 *
 * Generic wrapper for VMware log functions to directly log to either guest
 * or host.
 *
 * @param[in]  where    Log to host or guest.
 * @param[in]  level    Log level.
 * @param[in]  fmt      Message format.
 * @param[in]  args     Message arguments.
 *
 *******************************************************************************
 */

static void
VmwareLogWrapper(LogWhere where,
                 GLogLevelFlags level,
                 const char *fmt,
                 va_list args)
{
   if (!gLogInitialized && !IS_FATAL(level)) {
      /*
       * Avoid logging without initialization because
       * it leads to spamming of the console output.
       * Fatal messages are exception.
       */
      return;
   }

   VMTools_AcquireLogStateLock();

   LogWhereLevelV(where, level, gLogDomain, fmt, args);

   VMTools_ReleaseLogStateLock();
}


/*
 *******************************************************************************
 * WarningToHost --                                                       */ /**
 *
 * Logs a message at the host side using the G_LOG_LEVEL_WARNING level
 *
 * @param[in] fmt Log message format.
 *
 *******************************************************************************
 */

void
WarningToHost(const char *fmt, ...)
{
   va_list args;
   va_start(args, fmt);
   WITH_ERRNO(err,
      VmwareLogWrapper(TO_HOST, G_LOG_LEVEL_WARNING, fmt, args));
   va_end(args);
}


/*
 *******************************************************************************
 * WarningToGuest --                                                      */ /**
 *
 * Logs a message at the guest side using the G_LOG_LEVEL_WARNING level
 *
 * @param[in] fmt Log message format.
 *
 *******************************************************************************
 */

void
WarningToGuest(const char *fmt, ...)
{
   va_list args;
   va_start(args, fmt);
   WITH_ERRNO(err,
      VmwareLogWrapper(IN_GUEST, G_LOG_LEVEL_WARNING, fmt, args));
   va_end(args);
}


/*
 *******************************************************************************
 * VMTools_ChangeLogFilePath --                                           */ /**
 *
 *     This function gets the log file location given in the config file
 *     and appends the string provided just before the delimiter specified.
 *     If more than one delimiter is present in the string, it appends just
 *     before the first delimiter. If the delimiter does not exist in the
 *     location, the string provided is appended at the end of the location.
 *
 *     NOTE: It is up to the caller to free the delimiter and append string.
 *
 *     Example:
 *     1) location - "C:\vmresset.log", delimiter - ".", appendString - "_4"
 *        location changed = "C:\vmresset_4.log"
 *     2) location - "C:\vmresset", delimiter - ".", appendString - "_4"
 *        location changed = "C:\vmresset_4"
 *     3) location - "C:\vmresset.log.log", delimiter - ".", appendString - "_4"
 *        location changed = "C:\vmresset_4.log.log"
 *
 * Results:
 *     TRUE if log file location is changed, FALSE otherwise.
 *
 * Side effects:
 *     Appends a string into the log file location.
 *
 *******************************************************************************
 */

gboolean
VMTools_ChangeLogFilePath(const gchar *delimiter,     // IN
                          const gchar *appendString,  // IN
                          const gchar *domain,        // IN
                          GKeyFile *conf)             // IN, OUT
{
   gchar key[128];
   gchar *path = NULL;
   gchar **tokens;
   gboolean retVal = FALSE;

   if (domain == NULL || conf == NULL){
      goto exit;
   }

   g_snprintf(key, sizeof key, "%s.data", domain);
   path = VMToolsGetLogFilePath(key, domain, conf);

   if (path == NULL || appendString == NULL || delimiter == NULL){
      goto exit;
   }

   tokens = g_strsplit(path, delimiter, 2);
   if (tokens != NULL && *tokens != NULL){
      char *userLogTemp = g_strjoin(appendString, *tokens, " ", NULL);

      g_strchomp(userLogTemp);
      if (*(tokens+1) != NULL){
         gchar *userLog;
         userLog = g_strjoin(delimiter, userLogTemp, *(tokens+1), NULL);
         g_key_file_set_string(conf, LOGGING_GROUP, key, userLog);
         g_free(userLog);
      } else {
         g_key_file_set_string(conf, LOGGING_GROUP, key, userLogTemp);
      }
      retVal = TRUE;
      g_free(userLogTemp);
   }
   g_strfreev(tokens);

exit:
   if (path){
      g_free(path);
   }

   return retVal;
}


/*
 *******************************************************************************
 * VMTools_UseVmxGuestLog --                                              */ /**
 *
 * Set up a global flag that indicates we shall use the vmx guest logger.
 * You might wonder why this function and the VMTools_SetVmxGuestLog() are NOT
 * one. This is because we need to call VMTools_SetupVmxGuestLog() after
 * VMTools_ConfigLogging() since if we failed to setup the vmx guest logger,
 * we can leverage the file system logging facility to log those setup failures.
 * However, VMTools_ConfigLogging() code path needs to know whether to disable
 * the old vmx log handler or not. Not all tools app have migrated to the new
 * vmx guest logger. To keep the compatibility we should not mess up with
 * the VMTools_ConfigLogging() code path by restructuring it yet.
 *
 * @param[in]   appName   the application name, also serves as a routing tag
 *                        of the log message for the host side.
 *
 *******************************************************************************
 */

void
VMTools_UseVmxGuestLog(const gchar *appName)
{
   if (!gUseVmxGuestLog) {
      gAppName = g_strdup(appName);
      g_rec_mutex_init(&gVmxGuestLogMutex);
      gUseVmxGuestLog = TRUE;
   }
}


/*
 *******************************************************************************
 * SetupVmxGuestLogInt --                                                 */ /**
 *
 * Initialize the Vmx Guest Logging.
 * If the guest.log.* RPC is supported, set the level from the RPC query.
 * Otherwise, check the vmx handler setting from the cfg.
 *
 * @param[in]      refreshRpcChannel    whether to create a new RPC channel.
 * @param[in]      cfg                  tools config file object.
 * @param[in/opt]  level                log level string from vmx
 *
 *******************************************************************************
 */


static void
SetupVmxGuestLogInt(gboolean refreshRpcChannel,   // IN
                    GKeyFile *cfg,                // IN
                    const gchar *level)           // IN
{
   if (refreshRpcChannel) {
      DestroyRpcChannel();
   }

   if (SetupLogLevelAndRpcMode(cfg, level)) {
      g_info("Initialized the vmx guest logger.\n");
   }
}


/*
 *******************************************************************************
 * VMTools_SetupVmxGuestLog --                                            */ /**
 *
 * Initialize the Vmx Guest Logging.
 * If the guest.log.* RPC is supported, set the level from the RPC query.
 * Otherwise, read tools.conf for the vmx handler setting.
 * This function is called from the tools initialization, tools reset,
 * and the tools options handler code path.
 *
 * @param[in]      refreshRpcChannel    whether to create a new RPC channel.
 * @param[in/opt]  cfg                  tools config file object.
 * @param[in/opt]  level                log level string from vmx
 *
 *******************************************************************************
 */

void
VMTools_SetupVmxGuestLog(gboolean refreshRpcChannel,   // IN
                         GKeyFile *cfg,                // IN
                         const gchar *level)           // IN
{
   /*
    * Callers need to call VMTools_UseVmxGuestLog() first.
    * See the function header of VMTools_UseVmxGuestLog for details.
    */

   VERIFY(gUseVmxGuestLog);

   /*
    * Always acquire the log state mutex first followed by
    * the vmxGuestLog mutex to avoid a deadlock.
    * This is because Debug()/Warning() functions call VMToolsLogWrapper()
    * which acquire the log state mutex and call g_xxx function.
    *
    * If the order is reversed here, we could end up deadlock between
    * two threads, one calling this function, and the other calling
    * Debug()/Warning()
    *
    */
   VMTools_AcquireLogStateLock();

   /*
    * Recursive mutex is required as the set up function might call
    * g_xxx() functions to log messages which would call VMToolsLog()
    * which acquires the same mutex.
    */
   g_rec_mutex_lock(&gVmxGuestLogMutex);

   /* Load config for the disable-switch in tools.conf */
   if (NULL == cfg) {
      if (!VMTools_LoadConfig(NULL, G_KEY_FILE_NONE, &cfg, NULL)) {
         g_warning("Failed to load the tools config file.\n");
         goto done;
      }

      SetupVmxGuestLogInt(refreshRpcChannel, cfg, level);
      g_key_file_free(cfg);
      goto done;
   }

   SetupVmxGuestLogInt(refreshRpcChannel, cfg, level);

done:
   g_rec_mutex_unlock(&gVmxGuestLogMutex);

   VMTools_ReleaseLogStateLock();
}


/*
 *******************************************************************************
 * VMTools_TeardownVmxGuestLog --                                         */ /**
 *
 * Destroy the dedicated RPCI channel set up for the Vmx Guest Logging.
 * This function is called from the tools process exit code path.
 *
 *******************************************************************************
 */

void
VMTools_TeardownVmxGuestLog(void)
{
   /*
    * In case VMTools_UseVmxGuestLog() is never called.
    */
   if (!gUseVmxGuestLog) {
      return;
   }

   /*
    * Acquire the same locks as VMTools_SetupVmxGuestLog.
    * Stop the glib based logging to avoid nested logging
    * from RpcChannel error.
    */
   VMTools_AcquireLogStateLock();
   StopGlibLogging();
   g_rec_mutex_lock(&gVmxGuestLogMutex);

   DestroyRpcChannel();

   g_rec_mutex_unlock(&gVmxGuestLogMutex);
   RestartGlibLogging();
   VMTools_ReleaseLogStateLock();
}


/**
 * Helper function to return the matching LogHandler for a domain name
 *
 * @param[in] domain    Log domain.
 * @return              The matching LogHandler if found or
 *                      the default LogHandler.
 */

static LogHandler *
GetLogHandlerByDomain(const gchar *domain)
{
   guint i;

   if (NULL == gDomains) {
      return gDefaultData;
   }

   for (i = 0; i < gDomains->len; i++) {
      LogHandler *data = g_ptr_array_index(gDomains, i);

      if (0 == strcmp(data->domain, domain)) {
         return data;
      }
   }

   return gDefaultData;
}


/**
 * Helper function to plumb the log to either host or guest.
 *
 * @param[in] where     where the log should go.
 * @param[in] level     Log level.
 * @param[in] domain    Log domain.
 * @param[in] fmt       Log message output format.
 * @param[in] args      variadic format parameter list.
 */

static void
LogWhereLevelV(LogWhere where,
               GLogLevelFlags level,
               const gchar *domain,
               const gchar *fmt,
               va_list args)
{
   gchar buf[4096];

   g_vsnprintf(buf, sizeof buf, fmt, args);
   buf[sizeof buf - 1] = '\0';

   domain = (NULL != domain) ? domain : gLogDomain;
   ASSERT(NULL != domain);

   switch (where) {
   case TO_HOST:
      LogToHost(domain, level, buf);
      break;

   case IN_GUEST: {
      LogHandler *data = GetLogHandlerByDomain(domain);

      if (NULL != data) {
         LogToGuest(domain, level, buf, data);
      }
      break;
   }
   default:
      NOT_REACHED();
   }
}


/**
 *******************************************************************************
 * VMTools_Log --                                                         */ /**
 *
 * Entry log function.
 *
 * Use this function to log different log messages on host and in guest. Usually
 * a macro is defined to invoke this function once for the host side logging and
 * the second time for the guest side logging.
 *
 * @param[in] where     where the log should go.
 * @param[in] level     Log level.
 * @param[in] domain    Log domain.
 * @param[in] fmt       Log message output format.
 */

void
VMTools_Log(LogWhere where,
            GLogLevelFlags level,
            const gchar *domain,
            const gchar *fmt,
            ...)
{
   va_list args;

   va_start(args, fmt);
   LogWhereLevelV(where, level, domain, fmt, args);
   va_end(args);
}


/*
 ******************************************************************************
 * VMToolsVmxLogV --
 *
 * Sends the log message through RPC to vmx to be logged on the host.
 * Also, logs the message to vmsvc log file inside guest.
 *
 * @param[in]  chan         The RPC channel instance.
 * @param[in]  fmt          Log message output format.
 * @param[in]  args         The argument list.
 *
 * @return None
 *
 ******************************************************************************
 */

static void
VMToolsVmxLogV(RpcChannel *chan,
               const gchar *fmt,
               va_list args)
{
   char *reply = NULL;
   size_t replyLen;
   gchar msg[4096] = VMX_LOG_CMD;
   gint len;

   len = g_vsnprintf(msg + VMX_LOG_CMD_LEN,
                     sizeof msg - VMX_LOG_CMD_LEN,
                     fmt, args);

   if (len <= 0) {
      g_warning("%s: g_vsnprintf failed: return value: %d.\n", __FUNCTION__,
                len);
      return;
   }

   len += VMX_LOG_CMD_LEN;
   if (len >= sizeof msg) {
      len = sizeof msg - 1;
      msg[len] = '\0';
   }

   if (!RpcChannel_Send(chan, msg, len + 1, &reply, &replyLen)) {
      g_warning("%s: Error sending RPC message: %s. reply: %s\n",
                __FUNCTION__, msg, VM_SAFE_STR(reply));
   }
   free(reply);

   g_message("%s\n", msg + VMX_LOG_CMD_LEN);
}


/*
 ******************************************************************************
 * VMTools_VmxLog --
 *
 * Passes through VMToolsVmxLogV but takes the arguments inline.
 *
 * @param[in]  chan         The RPC channel instance.
 * @param[in]  fmt          Log message output format.
 *
 * @return None
 *
 ******************************************************************************
 */

void
VMTools_VmxLog(RpcChannel *chan,
               const gchar *fmt,
               ...)
{
   va_list args;

   va_start(args, fmt);
   VMToolsVmxLogV(chan, fmt, args);
   va_end(args);
}


/*
 ******************************************************************************
 * VMTools_VmxLogThrottled --
 *
 * Passes through VMToolsVmxLogV to log the message after checking for the
 * throttling condition. Takes the arguments inline.
 *
 * @param[in/out]  count     Throttle count.
 * @param[in]      chan      The RPC channel instance.
 * @param[in]      fmt       Log message output format.
 *
 * @return None
 *
 ******************************************************************************
 */

void
VMTools_VmxLogThrottled(uint32 *count,
                        RpcChannel *chan,
                        const gchar *fmt,
                        ...)
{
   va_list args;
   if (Util_Throttle(++*count)) {
      va_start(args, fmt);
      VMToolsVmxLogV(chan, fmt, args);
      va_end(args);
   }
}
  0707010000023E000081A40000000000000000000000016822550500000474000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/vmtoolslib_version.h    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMTOOLSLIB_VERSION_H_
#define _VMTOOLSLIB_VERSION_H_

#include "vm_tools_version.h"
#define VMTOOLSLIB_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define VMTOOLSLIB_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _VMTOOLS_VERSION_H_ */

0707010000023F000081A40000000000000000000000016822550500000DA9000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/libvmtools/vmxLogger.c /*********************************************************
 * Copyright (C) 2010-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmxLogger.c
 *
 * A logger that writes the logs to the VMX log file.
 */

#include "vmtoolsInt.h"
#include "vmware/tools/guestrpc.h"

typedef struct VMXLoggerData {
   GlibLogger     handler;
   RpcChannel    *chan;
} VMXLoggerData;


/*
 *******************************************************************************
 * VMXLoggerLog --                                                        */ /**
 *
 * Logs a message to the VMX using RpcChannel.
 *
 * The logger uses its own RpcChannel, opening and closing the channel for each
 * log message sent. This is not optimal, especially if the application already
 * has an RpcChannel instantiated; this could be fixed by providing a way for
 * the application to provide its own RpcChannel to the logging code, if it uses
 * one, so that this logger can re-use it.
 *
 * @param[in] domain    Unused.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 * @param[in] data      VMX logger data.
 *
 *******************************************************************************
 */

static void
VMXLoggerLog(const gchar *domain,
             GLogLevelFlags level,
             const gchar *message,
             gpointer data)
{
   VMXLoggerData *logger = data;

   if (RpcChannel_Start(logger->chan)) {
      gchar *msg;
      gint cnt = VMToolsAsprintf(&msg, "log %s", message);

      RpcChannel_Send(logger->chan, msg, cnt, NULL, NULL);

      g_free(msg);
      RpcChannel_Stop(logger->chan);
   }
}


/*
 *******************************************************************************
 * VMXLoggerDestroy --                                                    */ /**
 *
 * Cleans up the internal state of a VMX logger.
 *
 * @param[in] data   VMX logger data.
 *
 *******************************************************************************
 */

static void
VMXLoggerDestroy(gpointer data)
{
   VMXLoggerData *logger = data;
   RpcChannel_Destroy(logger->chan);
   g_free(logger);
}


/*
 *******************************************************************************
 * VMToolsCreateVMXLogger --                                              */ /**
 *
 * Configures a new VMX logger.
 *
 * @return The VMX logger data.
 *
 *******************************************************************************
 */

GlibLogger *
VMToolsCreateVMXLogger(void)
{
   VMXLoggerData *data = g_new0(VMXLoggerData, 1);
   data->handler.logfn = VMXLoggerLog;
   data->handler.addsTimestamp = TRUE;
   data->handler.shared = TRUE;
   data->handler.dtor = VMXLoggerDestroy;
   data->chan = BackdoorChannel_New();
   return &data->handler;
}

   07070100000240000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002600000000open-vm-tools-12.5.2/open-vm-tools/m4 07070100000241000081A40000000000000000000000016822550500006739000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/m4/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000242000081A4000000000000000000000001682255050000292A000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/m4/vmtools.m4  ################################################################################
### Copyright (C) 2009-2019 VMware, Inc.  All rights reserved.
###
### VMware-specific macros for use with autoconf.
###
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

#
# AC_VMW_CHECK_LIB(library, lvar, pkgname, lconfig, version, header, function,
#                  [action-if-found],
#                  [action-if-not-found])
#
# Checks for the existence of a library using three different methods, in the
# following order:
#
#     - user defined CUSTOM_(LIB)_CPPFLAGS and CUSTOM_(LIB)_LIBS variables.
#     - pkg-config
#     - the library's custom "config"
#
# If a library is successfully detected, the (LIB)_CPPFLAGS and (LIB)_LIBS
# variables are set to contain the appropriate flags, and these variables are
# exported using AC_SUBST. The parameters to the macro are:
#
#  library ($1): the library name; this is used for testing whether we can link
#                to the library (with AC_CHECK_LIB), and also to modify the user
#                provided CUSTOM_(LIB)_LIBS variable to make sure the library is
#                available in the linker flags.
#  lvar ($2): root name of the variables holding the libraries CPPFLAGS / LIBS;
#             e.g., FOO means that FOO_CPPFLAGS will be set, and CUSTOM_FOO_CPPFLAGS
#             used with AC_CHECK_HEADER.
#  pkgname ($3): pkg-config name of the library.
#  lconfig ($4): library's custom "config" program for figuring out compiler and
#                linker flags (optional).
#  version ($5): minimum version of the library when using pkg-config (optional).
#  header ($6): header file (when not using pkg-config; see AC_CHECK_HEADER; optional).
#  function ($7): function (when not using pkg-config; see AC_CHECK_LIB; optional).
#  action-if-found ($8): what to execute when successfully found the library.
#  action-if-not-found ($9): what to execute when the library was not found.
#
AC_DEFUN([AC_VMW_CHECK_LIB],[
   AC_REQUIRE([AC_CHECK_LIB]) dnl
   AC_REQUIRE([AC_CHECK_HEADER]) dnl

   if test -z "$1"; then
      AC_MSG_ERROR(['library' parameter is required.'])
   fi
   if test -z "$2"; then
      AC_MSG_ERROR(['lvar' parameter is required.'])
   fi

   ac_vmw_have_lib=0
   ac_vmw_have_lib_func=0
   ac_vmw_have_lib_header=0
   ac_vmw_custom_libs=

   #
   # First, try any user-defined CUSTOM_* flags.
   #
   if test -n "${CUSTOM_$2_CPPFLAGS}" || test -n "${CUSTOM_$2_LIBS}"; then
      ac_vmw_custom_libs="${CUSTOM_$2_LIBS} -l$1"
      if test -n "$6"; then
         ORIGINAL_CPPFLAGS="$CPPFLAGS"
         CPPFLAGS="${CUSTOM_$2_CPPFLAGS} $CPPFLAGS"

         AC_CHECK_HEADER([$6],
                         [ac_vmw_have_lib_header=1])

         CPPFLAGS="$ORIGINAL_CPPFLAGS"
      else
         ac_vmw_have_lib_header=1
      fi

      # Check a specific function in the library if requested.
      # If it hasn't, just pick a random function from libc, just to make
      # sure the linker can find the library being tested.
      if test $ac_vmw_have_lib_header -eq 1; then
         if test -n "$7"; then
            ac_vmw_function=$7
         else
            ac_vmw_function=strlen
         fi
         AC_CHECK_LIB(
            [$1],
            [$ac_vmw_function],
            [ac_vmw_have_lib_func=1],
            [],
            [$ac_vmw_custom_libs])
      fi

      if test $ac_vmw_have_lib_func -eq 1 && test $ac_vmw_have_lib_header -eq 1; then
         $2_CPPFLAGS="${CUSTOM_$2_CPPFLAGS}"
         $2_LIBS="$ac_vmw_custom_libs"
         ac_vmw_have_lib=1
      fi
   fi

   # If that didn't work, try with pkg-config.
   if test $ac_vmw_have_lib -eq 0 && test "$PKG_CONFIG" != "not_found" && test -n "$3"; then
      if test -n "$5"; then
         AC_MSG_CHECKING([for $3 >= $5 (via pkg-config)])
         if $PKG_CONFIG --exists '$3 >= $5'; then
            ac_vmw_have_lib=1
         fi
      else
         AC_MSG_CHECKING([for $3 (via pkg-config)])
         if $PKG_CONFIG --exists '$3'; then
            ac_vmw_have_lib=1
         fi
      fi

      if test $ac_vmw_have_lib -eq 1; then
         # Sometimes pkg-config might fail; for example, "pkg-config gtk+-2.0 --cflags"
         # fails on OpenSolaris B71. So be pessimistic.
         ac_vmw_cppflags="`$PKG_CONFIG --cflags $3`"
         ac_vmw_ret1=$?
         ac_vmw_libs="`$PKG_CONFIG --libs $3`"
         ac_vmw_ret2=$?
         if test $ac_vmw_ret1 -eq 0 && test $ac_vmw_ret2 -eq 0; then
            AC_MSG_RESULT([yes])
            $2_CPPFLAGS="$ac_vmw_cppflags"
            $2_LIBS="$ac_vmw_libs"
         else
            AC_MSG_RESULT([no])
         fi
      else
         AC_MSG_RESULT([no])
      fi
   fi

   # If we still haven't found the lib, try with the library's custom "config" script.
   if test $ac_vmw_have_lib -eq 0 && test -n "$4"; then
      unset ac_vmw_lib_cfg_$2
      AC_PATH_TOOL([ac_vmw_lib_cfg_$2], [$4], [not_found])
      if test "${ac_vmw_lib_cfg_$2}" != "not_found"; then
         # XXX: icu-config does not follow the "--cflags" and "--libs" convention,
         # so single it out here to avoid having to replicate all the rest of the
         # logic elsewhere.
         if test "$4" = "icu-config"; then
            $2_CPPFLAGS="`${ac_vmw_lib_cfg_$2} --cppflags`"
            $2_LIBS="`${ac_vmw_lib_cfg_$2} --ldflags`"
         else
            $2_CPPFLAGS="`${ac_vmw_lib_cfg_$2} --cflags`"
            $2_LIBS="`${ac_vmw_lib_cfg_$2} --libs`"
         fi
         ac_vmw_have_lib=1
      fi
   fi

   # Finish by executing the user provided action. The call to "true" is needed
   # because the actions are optional, and we need something inside the block.
   if test $ac_vmw_have_lib -eq 1; then
      AC_SUBST([$2_CPPFLAGS])
      AC_SUBST([$2_LIBS])
      true
      $8
   else
      true
      $9
   fi
])


#
# AC_VMW_CHECK_LIBXX(library, lvar, pkgname, lconfig, version, header, function,
#                    [action-if-found],
#                    [action-if-not-found])
#
# Similar to AC_VMW_CHECK_LIB, but for C++ libraries.
#
# XXX: Getting automake to choose between the C linker and the C++ linker
# depending on whether we're linking any C++ library was a royal pain in the
# ass. The classic way to do this is to define an optional source file for a
# program with an extension of .cxx, using nodist_EXTRA_fooprogram_SOURCES. This
# causes automake's linker detection algorithm to see a C++ source file and
# automatically set up the C++ linker and link line for us. Unfortunately, said
# linker detection doesn't obey conditionals, which means that it'd always pick
# the C++ linker, regardless of whether it's linking to a C++ library or not.
# Instead, we are forced to manually set the correct linker in fooprogram_LINK.
# However, since none of our programs actually contain C++ code, automake
# doesn't make the CXXLINK variable (which contains the linker as well as all
# link flags) available to us, so we must hard-code the entire link line into
# fooprogram_LINK. Not exactly a futureproof solution...
#
# Additional references on this problem:
# http://sources.redhat.com/ml/automake/1999-10/msg00101.html
# http://lists.gnu.org/archive/html/bug-automake/2008-04/msg00010.html
# http://www.gnu.org/software/automake/manual/automake.html#Libtool-Convenience-Libraries
# http://www.gnu.org/software/automake/manual/automake.html#C_002b_002b-Support
#
AC_DEFUN([AC_VMW_CHECK_LIBXX],[
   AC_REQUIRE([AC_VMW_CHECK_LIB])
   AC_LANG_PUSH([C++])
   AC_VMW_CHECK_LIB([$1], [$2], [$3], [$4], [$5], [$6], [$7], [$8], [$9])
   AC_LANG_POP([C++])
])


#
# AC_VMW_CHECK_X11_LIB(library, header, function, action-if-not-found)
#
# Special handling for X11 library checking. This macro checks that both the
# library provides the given function, and that the header exists, making use
# of COMMON_XLIBS when linking. On success, it modifies COMMON_XLIBS to include
# the library.
#
# library  ($1):   library name (value passed to ld with -l)
# header   ($2):   header file to look for, may be empty.
# function ($3):   function to look for in the library.
# action-if-not-found ($4): code to execute if failed to find the library.
#
#
AC_DEFUN([AC_VMW_CHECK_X11_LIB],[
   have_header=1
   if test -n "$2"; then
      AC_CHECK_HEADER(
         [X11/extensions/scrnsaver.h],
         [],
         [
          have_header=0;
          $4
         ],
         [])
   fi

   if test $have_header = 1; then
      AC_CHECK_LIB(
         [$1],
         [$3],
         [COMMON_XLIBS="-l$1 $COMMON_XLIBS"],
         [$4],
         [$COMMON_XLIBS])
   fi
])


#
# AC_VMW_LIB_ERROR(library, disable)
#
# Wrapper around AC_MSG_ERROR to print a standard message about missing libraries.
#
#     library ($1): name of missing library.
#     disable ($2): configure argument to disable usage of the library.
#     feature ($3): optional name of feature to be disabled; defaults to 'library'.
#
AC_DEFUN([AC_VMW_LIB_ERROR],[
   feature="$3"
   if test -z "$feature"; then
      feature="$1"
   fi
   AC_MSG_ERROR([Cannot find $1 library. Please configure without $feature (using --without-$2), or install the $1 libraries and devel package(s).])
])


#
# AC_VMW_DEFAULT_FLAGS(library)
#
# For use with libraries that don't have config scripts or pkg-config data.
# This makes sure that CUSTOM_${LIB}_CPPFLAGS is set to a reasonable default
# so that AC_VMW_CHECK_LIB can find the library.
#
#     library ($1): library name (as in CUSTOM_${library}_CPPFLAGS)
#     subdir  ($2): optional subdirectory to append to the default path
#
AC_DEFUN([AC_VMW_DEFAULT_FLAGS],[
   if test -z "$CUSTOM_$1_CPPFLAGS"; then
      if test "$os" = freebsd; then
         CUSTOM_$1_CPPFLAGS="-I/usr/local/include"
      else
         CUSTOM_$1_CPPFLAGS="-I/usr/include"
      fi
      if test -n "$2"; then
         CUSTOM_$1_CPPFLAGS="${CUSTOM_$1_CPPFLAGS}/$2"
      fi
   fi
])

  07070100000243000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/modules    07070100000244000081A40000000000000000000000016822550500000BDF000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/modules/Makefile.am    ################################################################################
### Copyright (C) 2008-2018,2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

all: modules

.PHONY: modules $(MODULES)
modules: $(MODULES)

modulesrc = $(abs_top_srcdir)/modules

$(MODULES):
	$(MAKE) VM_UNAME=$(KERNEL_RELEASE) MV=mv RM=rm      \
	   OVT_SOURCE_DIR=$(abs_top_srcdir)                 \
	   MODULEBUILDDIR=$(modulesrc)/$(MODULES_OS)        \
	   $(EXTRA_ARGS) -C "$(modulesrc)/$(MODULES_OS)/$@"

if FREEBSD_CUSTOM_SYSDIR
EXTRA_ARGS =
EXTRA_ARGS += "SYSDIR=@SYSDIR@"
endif

if SOLARIS
export vmhgfsdir := $(MODULES_DIR)
# Solaris does not have Kbuild-like system so we need to supply
# compiler and linker and other items discovered by configure
# script.
EXTRA_ARGS =
EXTRA_ARGS += "CC=$(CC)"
EXTRA_ARGS += "CC_WARNINGS=$(CC_WARNINGS)"
EXTRA_ARGS += "GLOBAL_DEFS=$(GLOBAL_DEFS)"
EXTRA_ARGS += "LD=$(LD)"
EXTRA_ARGS += "HAVE_GNU_LD=$(HAVE_GNU_LD)"
endif

## Automake will supplement its own "clean" targets with this.
clean-local:
	for MOD in $(MODULES); do                                              \
	   if [ -d "$(modulesrc)/$(MODULES_OS)/$$MOD" ]; then                  \
	      $(MAKE) VM_UNAME=$(KERNEL_RELEASE) MV=mv RM=rm                   \
	             -C "$(modulesrc)/$(MODULES_OS)/$$MOD" clean || exit 1;    \
	   fi                                                                  \
	done
	rm -f $(modulesrc)/$(MODULES_OS)/*.o $(modulesrc)/$(MODULES_OS)/*.ko
	rm -f $(modulesrc)/$(MODULES_OS)/VMwareVMCIModule.symvers

install-exec-hook:
if SOLARIS
	for MOD in $(MODULES); do                                              \
	   $(MAKE) VM_UNAME=$(KERNEL_RELEASE) MV=mv RM=rm                      \
	      -C "$(modulesrc)/$(MODULES_OS)/$$MOD" install || exit 1;         \
	done
endif
if FREEBSD
	for MOD in $(MODULES); do                                              \
	   $(INSTALL) -d $(DESTDIR)$(MODULES_DIR);                             \
	   $(INSTALL) -m 644 $(modulesrc)/$(MODULES_OS)/$$MOD.ko               \
	      $(DESTDIR)$(MODULES_DIR);                                        \
	done
endif

uninstall-hook:
	for MOD in $(MODULES); do                                              \
	   rm -f $(DESTDIR)`eval echo '$$'$${MOD}dir`/$$MOD.ko &> /dev/null;\
	done
	eval "$$DEPMOD"
 07070100000245000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd    07070100000246000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/shared 07070100000247000081A40000000000000000000000016822550500000A55000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/shared/compat_freebsd.h    /*********************************************************
 * Copyright (C) 2008, 2023 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
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

#ifndef __COMPAT_FREEBSD_H__
#define __COMPAT_FREEBSD_H__ 1

#include <sys/param.h>
#include "compat_vop.h"
#include "compat_mount.h"
#include "compat_priv.h"

/*
 * FreeBSD version 8 and above uses the kproc API instead of the kthread API in its
 * kernel.
 */
#define compat_kthread_create kproc_create
#define compat_kthread_exit kproc_exit

#endif // __COMPAT_FREEBSD_H__
   07070100000248000081A40000000000000000000000016822550500000983000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/shared/compat_mount.h  /*********************************************************
 * Copyright (C) 2009, 2023 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
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

#ifndef __COMPAT_MOUNT_H__
#   define __COMPAT_MOUNT_H__ 1

#define COMPAT_VFS_STATFS(mp, sbp, threadvar) VFS_STATFS((mp), (sbp))
#define compat_td curthread

#endif
 07070100000249000081A4000000000000000000000001682255050000095C000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/shared/compat_priv.h   /*********************************************************
 * Copyright (C) 2009, 2023 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
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

#ifndef __COMPAT_PRIV_H__
#   define __COMPAT_PRIV_H__ 1

#define compat_priv_check(td, priv) priv_check((td), (priv))

#endif
0707010000024A000081A40000000000000000000000016822550500000C96000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/shared/compat_vop.h    /*********************************************************
 * Copyright (C) 2008, 2020, 2023 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
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

#ifndef __COMPAT_VOP_H__
#   define __COMPAT_VOP_H__ 1

#define COMPAT_THREAD_VAR(varname, varval)
#define COMPAT_VOP_LOCK(vop, flags, threadvar) VOP_LOCK((vop), (flags))
#if __FreeBSD_version >= 1300074
#define COMPAT_VOP_UNLOCK(vop, flags, threadvar) VOP_UNLOCK((vop))
#else
#define COMPAT_VOP_UNLOCK(vop, flags, threadvar) VOP_UNLOCK((vop), (flags))
#endif
#define compat_lockstatus(lock, threadvar) lockstatus((lock))
#define compat_lockmgr(lock, flags, randompointerparam, threadval) lockmgr((lock), (flags), (randompointerparam))
#define compat_vn_lock(vp, flags, threadval) vn_lock((vp), (flags))
#define compat_accmode_t accmode_t
#define compat_a_accmode a_accmode

/*
 * We use defines rather than typedefs here to avoid causing problems for files that
 * don't have a vnode_if.h available.
 */
#define compat_vop_lock_t vop_lock1_t
#define compat_vop_lock_args struct vop_lock1_args
#define COMPAT_VOP_LOCK_OP_ELEMENT vop_lock1

#endif
  0707010000024B000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock    0707010000024C000081A400000000000000000000000168225505000004CC000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock/COPYING    Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
0707010000024D000081A40000000000000000000000016822550500000912000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock/Makefile   #!/usr/bin/make -f
##########################################################
# Copyright (C) 2006-2018, 2023 VMware, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
##########################################################

####
####  VMware vmblock Makefile to be distributed externally
####

HEADERS := block.h
HEADERS += vnode_if.h
HEADERS += vmblock_k.h
HEADERS += os.h

CSRCS  := block.c
CSRCS  += os_panic.c
CSRCS  += vfsops.c
CSRCS  += vnops.c
CSRCS  += subr.c
CSRCS  += stubs.c

SRCS   := $(HEADERS) $(CSRCS)

KMOD    = vmblock
PROG    = ../$(KMOD).ko
MK_MAN  = no
KLDMOD  = t

VMBLOCK := $(MAINSRCROOT)/modules/vmblock

CFLAGS  += $(INCLUDE) -Wall -Werror

.ifdef OVT_SOURCE_DIR
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/include
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/freebsd/shared
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/freebsd/vmblock
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/shared/vmblock
   CFLAGS  += -isystem /usr/include
   VPATH  := $(OVT_SOURCE_DIR)/modules/shared/vmblock
.else
   CFLAGS += -Ishared
.endif

EXPORT_SYMS = NO

.include <bsd.kmod.mk>

  0707010000024E000081A400000000000000000000000168225505000016AA000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock/os.h   /*********************************************************
 * Copyright (C) 2007 VMware, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/


/*
 * os.h --
 *
 *      FreeBSD-specific definitions.
 */


#ifndef __OS_H__
#define __OS_H__

#include <sys/param.h>
#include <sys/queue.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sx.h>
#include <sys/proc.h>
#include <sys/condvar.h>
#include <vm/uma.h>
#include <machine/stdarg.h>

#include "vm_basic_types.h"

typedef struct sx os_rwlock_t;

typedef struct uma_zone os_kmem_cache_t;
typedef struct os_completion_t {
   Bool completed;
   struct mtx mutex;
   struct cv cv;
} os_completion_t;
/*
 * Changing the os_atomic_t type requires that the os_atomic_* macros below be
 * changed as well.
 */
typedef unsigned int os_atomic_t;
typedef struct file * os_blocker_id_t;

#define OS_UNKNOWN_BLOCKER              NULL
#define OS_ENOMEM                       ENOMEM
#define OS_ENOENT                       ENOENT
#define OS_EEXIST                       EEXIST
#define OS_PATH_MAX                     MAXPATHLEN
#define OS_KMEM_CACHE_FLAG_HWALIGN      0       // unused

#define OS_FMTTID                       "p"
#define os_threadid                     curthread

extern NORETURN void os_panic(const char *fmt, va_list args);

#define os_rwlock_init(lock)            sx_init(lock, "vmblock-sx")
#define os_rwlock_destroy(lock)         sx_destroy(lock)
#define os_assert_rwlock_held(lock)     sx_assert(lock, SX_LOCKED)
#define os_read_lock(lock)              sx_slock(lock)
#define os_write_lock(lock)             sx_xlock(lock)
#define os_read_unlock(lock)            sx_sunlock(lock)
#define os_write_unlock(lock)           sx_xunlock(lock)

/*
 * XXX linux/os.h requests alignment on HW cache lines.  Is this of
 * serious concern?  We can do that by using UMA_ALIGN_CACHE as the
 * 'align' parameter to uma_zalloc, but with slightly different
 * semantics, it sort of changes the name of the game.
 */
#define os_kmem_cache_create(name, size, align, ctor) \
   uma_zcreate(name, size, ctor, NULL, NULL, NULL, align, 0)
#define os_kmem_cache_destroy(cache)    uma_zdestroy(cache)
#define os_kmem_cache_alloc(cache)      uma_zalloc(cache, M_WAITOK)
#define os_kmem_cache_free(cache, elem) uma_zfree(cache, elem)

#define os_completion_init(comp)                                \
      do {                                                      \
         (comp)->completed = FALSE;                             \
         mtx_init(&(comp)->mutex, "vmblock-mtx", "vmblock-mtx", MTX_DEF);         \
         cv_init(&(comp)->cv, "vmblock-cv");                       \
      } while (0)
#define os_completion_destroy(comp)                             \
      do {                                                      \
         mtx_destroy(&(comp)->mutex);                           \
         cv_destroy(&(comp)->cv);                               \
      } while (0)
/*
 * This macro evaluates to non-zero only if cv_wait_sig is interrupted.
 */
#define os_wait_for_completion(comp)                            \
({                                                              \
         int error = 0;                                         \
         mtx_lock(&(comp)->mutex);                              \
         while (!(comp)->completed && !error) {                 \
            error = cv_wait_sig(&(comp)->cv, &(comp)->mutex);   \
         }                                                      \
         mtx_unlock(&(comp)->mutex);                            \
         error;                                                 \
})
#define os_complete_all(comp)                                   \
      do {                                                      \
         mtx_lock(&(comp)->mutex);                              \
         (comp)->completed = TRUE;                              \
         cv_broadcast(&(comp)->cv);                             \
         mtx_unlock(&(comp)->mutex);                            \
      } while (0)

/* atomic_fetchadd_int returns the value of atomic before addition. */
#define os_atomic_dec_and_test(atomic)  (atomic_fetchadd_int(atomic, -1) == 1)
#define os_atomic_dec(atomic)           atomic_subtract_int(atomic, 1)
#define os_atomic_inc(atomic)           atomic_add_int(atomic, 1)
#define os_atomic_set(atomic, val)      atomic_store_rel_int(atomic, val)
#define os_atomic_read(atomic)          atomic_load_acq_int(atomic)

#endif /* __OS_H__ */
  0707010000024F000081A40000000000000000000000016822550500000886000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock/os_panic.c /*********************************************************
 * Copyright (C) 2011 VMware, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/


/*
 * os_panic.c --
 *
 *     Vararg panic implementation for FreeBSD.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <machine/stdarg.h>

#include "os.h"

/*
 *----------------------------------------------------------------------------
 *
 * os_panic --
 *
 *    FreeBSD panic implementation that takes va_list argument.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    We panic.
 *
 *----------------------------------------------------------------------------
 */

void
os_panic(const char *fmt,  // IN
         va_list args)     // IN
{
   static char message[1024];

   vsnprintf(message, sizeof message - 1, fmt, args);
   message[sizeof message - 1] = '\0';

   panic("%s", message);
}
  07070100000250000081A4000000000000000000000001682255050000359E000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock/subr.c /* **********************************************************
 * Copyright 2007-2014, 2023 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * subr.c --
 *
 *	Subroutines for the VMBlock filesystem on FreeBSD.
 */


/*-
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software donated to Berkeley by
 * Jan-Simon Pendry.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)null_subr.c	8.7 (Berkeley) 5/14/95
 *
 * $FreeBSD: src/sys/fs/nullfs/null_subr.c,v 1.48.2.1 2006/03/13 03:05:17 jeff Exp $
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/file.h>

#include "compat_freebsd.h"

#include "vmblock_k.h"
#include "block.h"

#define LOG2_SIZEVNODE  8		/* log2(sizeof struct vnode) */
#define	NVMBLOCKCACHE   16              /* Number of hash buckets/chains */


/*
 * Local data
 */

/*
 * VMBlock layer cache:
 *    Each cache entry holds a reference to the lower vnode along with a
 *    pointer to the alias vnode.  When an entry is added the lower vnode
 *    is VREF'd.  When the alias is removed the lower vnode is vrele'd.
 */

#define	VMBLOCK_NHASH(vp) \
	(&nodeHashTable[(((uintptr_t)vp)>>LOG2_SIZEVNODE) & nodeHashMask])

/*
 * See hashinit(9).
 */
static LIST_HEAD(nodeHashHead, VMBlockNode) *nodeHashTable;
static u_long nodeHashMask;
static struct mtx hashMutex;

static MALLOC_DEFINE(M_VMBLOCKFSHASH, "VMBlockFS hash", "VMBlockFS hash table");
MALLOC_DEFINE(M_VMBLOCKFSNODE, "VMBlockFS node", "VMBlockFS vnode private part");

/* Defined for quick access to temporary pathname buffers. */
uma_zone_t VMBlockPathnameZone;


/*
 * Local functions
 */

static struct vnode * VMBlockHashGet(struct mount *, struct vnode *);
static struct vnode * VMBlockHashInsert(struct mount *, struct VMBlockNode *);


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockInit --
 *
 *      Initialize VMBlock file system.  Called when module first loaded into
 *      the kernel.
 *
 * Results:
 *      Zero.
 *
 * Side effects:
 *      None.
 *
 * Original comments:
 *      Initialise cache headers
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockInit(struct vfsconf *vfsp)       // ignored
{
   VMBLOCKDEBUG("VMBlockInit\n");      /* printed during system boot */
   nodeHashTable = hashinit(NVMBLOCKCACHE, M_VMBLOCKFSHASH, &nodeHashMask);
   mtx_init(&hashMutex, "vmblock-hs", NULL, MTX_DEF);
   VMBlockPathnameZone = uma_zcreate("VMBlock", MAXPATHLEN, NULL, NULL, NULL,
                                     NULL, UMA_ALIGN_PTR, 0);
   VMBlockSetupFileOps();
   BlockInit();
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockUninit --
 *
 *      Clean up when module is unloaded.
 *
 * Results:
 *      Zero always.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockUninit(struct vfsconf *vfsp)     // ignored
{
   mtx_destroy(&hashMutex);
   free(nodeHashTable, M_VMBLOCKFSHASH);
   BlockCleanup();
   uma_zdestroy(VMBlockPathnameZone);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockHashGet --
 *
 *      "Return a VREF'ed alias for lower vnode if already exists, else 0.
 *      Lower vnode should be locked on entry and will be left locked on exit."
 *
 * Results:
 *      Pointer to upper layer/alias vnode if lowervp found, otherwise NULL.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static struct vnode *
VMBlockHashGet(struct mount *mp,        // IN: vmblock file system information
               struct vnode *lowervp)   // IN: lower vnode to search for
{
   struct nodeHashHead *hd;
   struct VMBlockNode *a;
   struct vnode *vp;

   ASSERT_VOP_LOCKED(lowervp, "hashEntryget");

   /*
    * Find hash base, and then search the (two-way) linked list looking
    * for a VMBlockNode structure which is referencing the lower vnode.
    * If found, the increment the VMBlockNode reference count (but NOT the
    * lower vnode's VREF counter).
    */
   hd = VMBLOCK_NHASH(lowervp);
   mtx_lock(&hashMutex);
   LIST_FOREACH(a, hd, hashEntry) {
      if (a->lowerVnode == lowervp && VMBTOVP(a)->v_mount == mp) {
         /*
          * Since we have the lower node locked the nullfs node can not be
          * in the process of recycling.  If it had been recycled before we
          * grabed the lower lock it would not have been found on the hash.
          */
         vp = VMBTOVP(a);
         vref(vp);
         mtx_unlock(&hashMutex);
         return vp;
      }
   }
   mtx_unlock(&hashMutex);
   return NULLVP;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockHashInsert --
 *
 *      "Act like VMBlockHashGet, but add passed VMBlockNode to hash if no
 *      existing node found."
 *
 * Results:
 *      Referenced, locked alias vnode if entry already in hash.  Otherwise
 *      NULLVP.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static struct vnode *
VMBlockHashInsert(struct mount *mp,             // IN: VMBlock file system info
                  struct VMBlockNode *xp)       // IN: node to insert into hash
{
   struct nodeHashHead *hd;
   struct VMBlockNode *oxp;
   struct vnode *ovp;

   hd = VMBLOCK_NHASH(xp->lowerVnode);
   mtx_lock(&hashMutex);
   LIST_FOREACH(oxp, hd, hashEntry) {
      if (oxp->lowerVnode == xp->lowerVnode && VMBTOVP(oxp)->v_mount == mp) {
         /*
          * See hashEntryget for a description of this
          * operation.
          */
         ovp = VMBTOVP(oxp);
         vref(ovp);
         mtx_unlock(&hashMutex);
         return ovp;
      }
   }
   LIST_INSERT_HEAD(hd, xp, hashEntry);
   mtx_unlock(&hashMutex);
   return NULLVP;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockHashRem --
 *
 *      Remove a VMBlockNode from the hash.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
VMBlockHashRem(struct VMBlockNode *xp)  // IN: node to remove
{
   mtx_lock(&hashMutex);
   LIST_REMOVE(xp, hashEntry);
   mtx_unlock(&hashMutex);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockInsMntQueDtr --
 *
 *      Do filesystem specific cleanup when recycling a vnode on a failed
 *      insmntque1 call.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VMBlockInsMntQueDtr(struct vnode *vp, // IN: node to cleanup
		    void *xp)         // IN: FS private data
{
   vp->v_data = NULL;
   vp->v_vnlock = &vp->v_lock;
   free(xp, M_VMBLOCKFSNODE);
   vp->v_op = &dead_vnodeops;
   (void) compat_vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curthread);
   vgone(vp);
   vput(vp);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockNodeGet --
 *
 *      Return a VMBlockNode mapped to the given lower layer vnode.
 *
 * Results:
 *      Zero on success, an appropriate system error otherwise.
 *
 * Side effects:
 *      In case of success takes over ownership of pathname thus caller
 *      has to dispose of it only if error was signalled.
 *
 * Original function comment:
 *
 *	Make a new or get existing nullfs node.  Vp is the alias vnode,
 *	lowervp is the lower vnode.
 *
 *	The lowervp assumed to be locked and having "spare" reference. This
 *	routine vrele lowervp if nullfs node was taken from hash. Otherwise it
 *	"transfers" the caller's "spare" reference to created nullfs vnode.
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockNodeGet(struct mount *mp,        // IN: VMBlock fs info
               struct vnode *lowervp,   // IN: lower layer vnode
               struct vnode **vpp,      // OUT: upper layer/alias vnode
               char         *pathname)  // IN: Pointer to the path we took to
                                        //     reach this vnode
{
   struct VMBlockNode *xp;
   struct vnode *vp;
   int error;

   /* Lookup the hash firstly */
   *vpp = VMBlockHashGet(mp, lowervp);
   if (*vpp != NULL) {
      vrele(lowervp);
      return 0;
   }

   /*
    * We do not serialize vnode creation, instead we will check for duplicates
    * later, when adding new vnode to hash.
    *
    * Note that duplicate can only appear in hash if the lowervp is locked
    * LK_SHARED.
    */

   /*
    * Do the malloc before the getnewvnode since doing so afterward might
    * cause a bogus v_data pointer to get dereferenced elsewhere if malloc
    * should block.
    */
   xp = malloc(sizeof *xp, M_VMBLOCKFSNODE, M_WAITOK|M_ZERO);

   error = getnewvnode("vmblock", mp, &VMBlockVnodeOps, &vp);
   if (error) {
      free(xp, M_VMBLOCKFSNODE);
      return error;
   }

   xp->name = pathname;
   xp->backVnode = vp;
   xp->lowerVnode = lowervp;
   vp->v_type = lowervp->v_type;
   vp->v_data = xp;
   vp->v_vnlock = lowervp->v_vnlock;
   if (vp->v_vnlock == NULL) {
      panic("VMBlockNodeGet: Passed a NULL vnlock.\n");
   }

#if __FreeBSD_version >= 1400051
   error = insmntque1(vp, mp);
   if (error != 0) {
      VMBlockInsMntQueDtr(vp, xp);
      return error;
   }
#else
   error = insmntque1(vp, mp, VMBlockInsMntQueDtr, xp);
   if (error != 0) {
      return error;
   }
#endif

   /*
    * Atomically insert our new node into the hash or vget existing if
    * someone else has beaten us to it.
    *
    * ETA:  If a hash entry already exists, we'll be stuck with an orphaned
    * vnode and associated VMBlockNode.  By vrele'ng this vp, it'll be reclaimed
    * by the OS later.  That same process will take care of freeing the
    * VMBlockNode, too.
    */
   *vpp = VMBlockHashInsert(mp, xp);
   if (*vpp != NULL) {
      vrele(lowervp);
      vp->v_vnlock = &vp->v_lock;
      xp->lowerVnode = NULL;
      vrele(vp);
   } else {
      *vpp = vp;
   }

   return 0;
}


#ifdef DIAGNOSTIC                               /* if (DIAGNOSTIC) { */

/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockCheckVp --
 *
 *      Confidence-checking intermediary used for debugging.  When module is
 *      compiled with FreeBSD macro "DIAGNOSTIC", every instance of
 *      VMBVPTOLOWERVP() calls this function to test vnodes' and VMBlockNodes'
 *      values, printing diagnostic information before panicing.  If the kernel
 *      debugger (KDB) is enabled, then this function will break to the debugger
 *      before a panic.
 *
 * Results:
 *      Valid pointer to a VMBlockNode's lower vnode.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

struct vnode *
VMBlockCheckVp(vp, fil, lno)
	struct vnode *vp;
	char *fil;
	int lno;
{
   struct VMBlockNode *a = VPTOVMB(vp);
#ifdef notyet
   /*
    * Can't do this check because vop_reclaim runs
    * with a funny vop vector.
    */
   if (vp->v_op != null_vnodeop_p) {
      printf ("VMBlockCheckVp: on non-null-node\n");
      panic("VMBlockCheckVp");
   };
#endif
   if (a->lowerVnode == NULLVP) {
      /* Should never happen */
      int i; u_long *p;
      printf("vp = %p, ZERO ptr\n", (void *)vp);
      for (p = (u_long *) a, i = 0; i < 8; i++) {
         printf(" %lx", p[i]);
      }
      printf("\n");
      panic("VMBlockCheckVp");
   }
   if (vrefcnt(a->lowerVnode) < 1) {
      int i; u_long *p;
      printf("vp = %p, unref'ed lowervp\n", (void *)vp);
      for (p = (u_long *) a, i = 0; i < 8; i++) {
         printf(" %lx", p[i]);
      }
      printf("\n");
      panic ("null with unref'ed lowervp");
   };
#ifdef notyet
   printf("null %x/%d -> %x/%d [%s, %d]\n", VMBTOVP(a), vrefcnt(VMBTOVP(a)),
      a->lowerVnode, vrefcnt(a->lowerVnode), fil, lno);
#endif
   return a->lowerVnode;
}
#endif                                          /* } [DIAGNOSTIC] */
  07070100000251000081A40000000000000000000000016822550500003270000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock/vfsops.c   /* **********************************************************
 * Copyright 2007-2014, 2023 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * vfsops.c --
 *
 *      VFS operations for VMBlock file system on FreeBSD.
 */

/*-
 * Copyright (c) 1992, 1993, 1995
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software donated to Berkeley by
 * Jan-Simon Pendry.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)null_vfsops.c	8.2 (Berkeley) 1/21/94
 *
 * @(#)lofs_vfsops.c	1.2 (Berkeley) 6/18/92
 * $FreeBSD: src/sys/fs/nullfs/null_vfsops.c,v 1.72.2.5 2006/10/09 19:47:14 tegge Exp $
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/fcntl.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/vnode.h>

#include "vmblock_k.h"
#include "compat_freebsd.h"

static MALLOC_DEFINE(M_VMBLOCKFSMNT, "VMBlockFS mount", "VMBlockFS mount structure");


/*
 * Local data
 */

static vfs_mount_t	VMBlockVFSMount;
static vfs_root_t	VMBlockVFSRoot;
static vfs_sync_t	VMBlockVFSSync;
static vfs_statfs_t	VMBlockVFSStatFS;
static vfs_unmount_t	VMBlockVFSUnmount;
static vfs_vget_t	VMBlockVFSVGet;

/*
 * VFS operations vector
 */
static struct vfsops VMBlockVFSOps = {
   .vfs_init            = VMBlockInit,
   .vfs_uninit          = VMBlockUninit,
   .vfs_mount           = VMBlockVFSMount,
   .vfs_root            = VMBlockVFSRoot,
   .vfs_statfs          = VMBlockVFSStatFS,
   .vfs_sync            = VMBlockVFSSync,
   .vfs_unmount         = VMBlockVFSUnmount,
   .vfs_vget            = VMBlockVFSVGet,
};

/*
 * The following generates a struct vfsconf for our filesystem & grafts us into
 * the kernel's list of known filesystems at module load.
 */
VFS_SET(VMBlockVFSOps, vmblock, VFCF_LOOPBACK);


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVFSMount --
 *
 *      Mount the vmblock file system.
 *
 * Results:
 *      Zero on success, otherwise an appropriate system error.  (See
 *      VFS_MOUNT(9).)
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVFSMount(struct mount *mp)        // IN: mount(2) parameters
{
   struct VMBlockMount *xmp;
   struct nameidata nd, *ndp = &nd;
   struct vnode *lowerrootvp, *vp;
   char *target;
   char *pathname;
   int len, error = 0;

   VMBLOCKDEBUG("VMBlockVFSMount(mp = %p)\n", (void *)mp);

   /*
    * TODO:  Strip out extraneous export & other misc cruft.
    */

   /*
    * Disallow the following:
    *   1.  Mounting over the system root.
    *   2.  Mount updates/remounts.  (Reconsider for rw->ro, ro->rw?)
    *   3.  Mounting VMBlock on top of a VMBlock.
    */
   if ((mp->mnt_flag & MNT_ROOTFS) ||
       (mp->mnt_flag & MNT_UPDATE) ||
       (mp->mnt_vnodecovered->v_op == &VMBlockVnodeOps)) {
      return EOPNOTSUPP;
   }

   /*
    * XXX Should only be unlocked if mnt_flag & MNT_UPDATE.
    */
   ASSERT_VOP_UNLOCKED(mp->mnt_vnodecovered, "Covered vnode already locked!");

   /*
    * Look up path to lower layer (VMBlock source / DnD staging area).
    * (E.g., in the command "mount /tmp/VMwareDnD /var/run/vmblock",
    * /tmp/VMwareDnD is the staging area.)
    */
   error = vfs_getopt(mp->mnt_optnew, "target", (void **)&target, &len);
   if (error || target[len - 1] != '\0') {
      return EINVAL;
   }

   pathname = uma_zalloc(VMBlockPathnameZone, M_WAITOK);
   if (pathname == NULL) {
      return ENOMEM;
   }

   if (strlcpy(pathname, target, MAXPATHLEN) >= MAXPATHLEN) {
      uma_zfree(VMBlockPathnameZone, pathname);
      return ENAMETOOLONG;
   }

   /*
    * Find lower node and lock if not already locked.
    */

#if __FreeBSD_version >= 1400043
   NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target);
#else
   NDINIT(ndp, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, target, compat_td);
#endif
   error = namei(ndp);
   if (error) {
      NDFREE(ndp, 0);
      uma_zfree(VMBlockPathnameZone, pathname);
      return error;
   }
#ifdef NDF_ONLY_PNBUF
   NDFREE(ndp, NDF_ONLY_PNBUF);
#else
   NDFREE_PNBUF(ndp);
#endif

   /*
    * Check multi VMBlock mount to avoid `lock against myself' panic.
    */
   lowerrootvp = ndp->ni_vp;
   if (lowerrootvp == VPTOVMB(mp->mnt_vnodecovered)->lowerVnode) {
      VMBLOCKDEBUG("VMBlockVFSMount: multi vmblock mount?\n");
      vput(lowerrootvp);
      uma_zfree(VMBlockPathnameZone, pathname);
      return EDEADLK;
   }

   xmp = malloc(sizeof *xmp, M_VMBLOCKFSMNT, M_WAITOK);

   /*
    * Record pointer (mountVFS) to the staging area's file system.  Follow up
    * by grabbing a VMBlockNode for our layer's root.
    */
   xmp->mountVFS = lowerrootvp->v_mount;
   error = VMBlockNodeGet(mp, lowerrootvp, &vp, pathname);

   /*
    * Make sure the node alias worked
    */
   if (error) {
      COMPAT_VOP_UNLOCK(vp, 0, compat_td);
      vrele(lowerrootvp);
      free(xmp, M_VMBLOCKFSMNT);   /* XXX */
      uma_zfree(VMBlockPathnameZone, pathname);
      return error;
   }

   /*
    * Record a reference to the new filesystem's root vnode & mark it as such.
    */
   xmp->rootVnode = vp;
   xmp->rootVnode->v_vflag |= VV_ROOT;

   /*
    * Unlock the node (either the lower or the alias)
    */
   COMPAT_VOP_UNLOCK(vp, 0, compat_td);

   /*
    * If the staging area is a local filesystem, reflect that here, too.  (We
    * could potentially allow NFS staging areas.)
    */
   MNT_ILOCK(mp);
   mp->mnt_flag |= lowerrootvp->v_mount->mnt_flag & MNT_LOCAL;
   MNT_IUNLOCK(mp);

   mp->mnt_data = (qaddr_t) xmp;

   vfs_getnewfsid(mp);
   vfs_mountedfrom(mp, target);

   VMBLOCKDEBUG("VMBlockVFSMount: lower %s, alias at %s\n",
      mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVFSUnmount --
 *
 *      "VFS_UNMOUNT(9) -- unmount a filesystem."
 *
 * Results:
 *      Zero on success, else an appropriate system error.
 *
 * Side effects:
 *      VMBlocks on all filesystems are removed.  (Expecting vmblockfs to
 *      be mounted only once.)
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVFSUnmount(struct mount *mp,    // IN: filesystem to unmount
                  int mntflags)        // IN: unmount(2) flags (ex: MNT_FORCE)
{
   struct VMBlockMount *xmp;
   struct vnode *vp;
   void *mntdata;
   int error;
   int flags = 0, removed = 0;

   VMBLOCKDEBUG("VMBlockVFSUnmount: mp = %p\n", (void *)mp);

   xmp = MNTTOVMBLOCKMNT(mp);
   vp = xmp->rootVnode;

   VI_LOCK(vp);

   /*
    * VMBlocks reference the root vnode.  This check returns EBUSY if
    * VMBlocks still exist & the user isn't forcing us out.
    */
   if ((vp->v_usecount > 1) && !(mntflags & MNT_FORCE)) {
      VI_UNLOCK(vp);
      return EBUSY;
   }

   /*
    * FreeBSD forbids acquiring sleepable locks (ex: sx locks) while holding
    * non-sleepable locks (ex: mutexes).  The vnode interlock acquired above
    * is a mutex, and the Block* routines involve sx locks, so we need to
    * yield the interlock.
    *
    * In order to do this safely, we trade up to locking the entire vnode,
    * and indicate to the lock routine that we hold the interlock.  The lock
    * transfer will happen atomically.  (Er, at least within the scope of
    * the vnode subsystem.)
    */
   COMPAT_VOP_LOCK(vp, LK_EXCLUSIVE|LK_RETRY|LK_INTERLOCK, compat_td);

   removed = BlockRemoveAllBlocks(OS_UNKNOWN_BLOCKER);

   VI_LOCK(vp);
   vp->v_usecount -= removed;
   VI_UNLOCK(vp);
   COMPAT_VOP_UNLOCK(vp, 0, compat_td);

   if (mntflags & MNT_FORCE) {
      flags |= FORCECLOSE;
   }

   /* There is 1 extra root vnode reference (xmp->rootVnode). */
   error = vflush(mp, 1, flags, compat_td);
   if (error) {
      return error;
   }

   /*
    * Finally, throw away the VMBlockMount structure
    */
   mntdata = mp->mnt_data;
   mp->mnt_data = 0;
   free(mntdata, M_VMBLOCKFSMNT);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVFSRoot --
 *
 *      "VFS_ROOT -- return the root vnode of a file system."
 *
 * Results:
 *      Zero.
 *
 * Side effects:
 *      Root vnode is locked.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVFSRoot(struct mount *mp,        // IN: vmblock file system
               int flags,               // IN: lockmgr(9) flags
               struct vnode **vpp)      // OUT: root vnode
{
   struct vnode *vp;

   /*
    * Return locked reference to root.
    */
   vp = MNTTOVMBLOCKMNT(mp)->rootVnode;
   VREF(vp);
   compat_vn_lock(vp, flags | LK_RETRY, compat_td);
   *vpp = vp;
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVFSStatFS --
 *
 *      "VFS_STATFS -- return file system status."  We pass the request to the
 *      lower layer, but return only the "interesting" bits.  (E.g., fs type,
 *      sizes, usage, etc.)
 *
 * Results:
 *      Zero on success, an appropriate system error otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVFSStatFS(struct mount *mp,      // IN: vmblock file system
                 struct statfs *sbp)    // OUT: statfs(2) arg container
{
   int error;
   struct statfs mstat;

   VMBLOCKDEBUG("VMBlockVFSStatFS(mp = %p, vp = %p->%p)\n", (void *)mp,
       (void *)MNTTOVMBLOCKMNT(mp)->rootVnode,
       (void *)VMBVPTOLOWERVP(MNTTOVMBLOCKMNT(mp)->rootVnode));

   bzero(&mstat, sizeof mstat);

   error = COMPAT_VFS_STATFS(MNTTOVMBLOCKMNT(mp)->mountVFS, &mstat, compat_td);
   if (error) {
      return error;
   }

   /* now copy across the "interesting" information and fake the rest */
   sbp->f_type = mstat.f_type;
   sbp->f_flags = mstat.f_flags;
   sbp->f_bsize = mstat.f_bsize;
   sbp->f_iosize = mstat.f_iosize;
   sbp->f_blocks = mstat.f_blocks;
   sbp->f_bfree = mstat.f_bfree;
   sbp->f_bavail = mstat.f_bavail;
   sbp->f_files = mstat.f_files;
   sbp->f_ffree = mstat.f_ffree;
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVFSSync --
 *
 *      "VFS_SYNC -- flush unwritten data."  Since there's no caching at our
 *      layer, this is a no-op.
 *
 * Results:
 *      Zero.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVFSSync(struct mount *mp,        // Ignored
               int waitfor)             // Ignored
{
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVFSVGet --
 *
 *      "VFS_VGET -- convert an inode number to a vnode."
 *
 * Results:
 *      Zero on success, otherwise an appropriate system error.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVFSVGet(struct mount *mp,        // IN: vmblock file system
               ino_t ino,               // IN: requested inode number
               int flags,               // IN: vget(9) locking flags
               struct vnode **vpp)      // OUT: located vnode
{
   int error;
   error = VFS_VGET(MNTTOVMBLOCKMNT(mp)->mountVFS, ino, flags, vpp);
   if (error) {
      return error;
   }

   return VMBlockNodeGet(mp, *vpp, vpp, NULL);
}
07070100000252000081A40000000000000000000000016822550500001191000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock/vmblock_k.h    /* **********************************************************
 * Copyright (C) 2007 VMware, Inc.  All Rights Reserved.
 * **********************************************************/

/*-
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software donated to Berkeley by
 * Jan-Simon Pendry.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)null.h	8.3 (Berkeley) 8/20/94
 *
 * $FreeBSD: src/sys/fs/nullfs/null.h,v 1.23 2005/03/15 13:49:33 jeff Exp $
 */

/*
 * vmblock_k.h --
 *
 *      Defnitions for entire vmblock module.
 */

#ifndef _VMBLOCK_K_H_
#define _VMBLOCK_K_H_

#ifdef _KERNEL

#include <sys/types.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/mount.h>
#include <sys/vnode.h>

#include "vm_basic_types.h"
#include "vm_assert.h"
#include "block.h"


/*
 * Macros
 */

#ifdef VMBLOCKFS_DEBUG
# define VMBLOCKFSDEBUG(format, args...)       printf(format ,## args)
#else
# define VMBLOCKFSDEBUG(format, args...)
#endif /* VMBLOCKFS_DEBUG */

#define MNTTOVMBLOCKMNT(mp)    ((struct VMBlockMount *)((mp)->mnt_data))
#define VPTOVMB(vp)            ((struct VMBlockNode *)(vp)->v_data)
#define VMBTOVP(xp)            ((xp)->backVnode)

/*
 * Debug logging
 */
#define VMBLOCK_DEBUG           LOG_DEBUG
#define VMBLOCK_ERROR           LOG_WARNING
#define VMBLOCK_ENTRY_LOGLEVEL  LOG_DEBUG
#define Warning(fmt, args...)   log(VMBLOCK_ERROR, fmt, ##args)

#ifdef VMX86_DEVEL
#   define LOG(level, fmt, args...)     printf(fmt, ##args)
#   define VMBLOCKDEBUG(fmt, args...)   log(VMBLOCK_DEBUG, fmt, ##args)
#   define Debug(level, fmt, args...)   log(VMBLOCK_DEBUG, fmt, ##args)
#else
#   define LOG(level, fmt, args...)
#   define VMBLOCKDEBUG(fmt, args...)
#   define Debug(level, fmt, args...)
#endif


/*
 * Describes a single mount instance
 */

typedef struct VMBlockMount {
   struct mount	*mountVFS;      /* Reference to mount parameters */
   struct vnode	*rootVnode;     /* Reference to root vnode */
} VMBlockMount;


/*
 * A cache of vnode references
 */

typedef struct VMBlockNode {
   LIST_ENTRY(VMBlockNode) hashEntry;   /* Hash chain element (contains ptr to
                                           next node, etc.) */
   struct vnode *lowerVnode;            /* VREFed once */
   struct vnode *backVnode;             /* Back pointer */
   char          *name;                 /* Looked up path to vnode */
} VMBlockNode;


/*
 * Global variables
 */

extern struct vop_vector VMBlockVnodeOps;
extern uma_zone_t VMBlockPathnameZone;


/*
 * Global functions
 */

int VMBlockInit(struct vfsconf *vfsp);
int VMBlockUninit(struct vfsconf *vfsp);
int VMBlockNodeGet(struct mount *mp, struct vnode *target, struct vnode **vpp,
                   char *pathname);
void VMBlockHashRem(struct VMBlockNode *xp);
void VMBlockSetupFileOps(void);
int VMBlockVopBypass(struct vop_generic_args *ap);

#ifdef DIAGNOSTIC
struct vnode *VMBlockCheckVp(struct vnode *vp, char *fil, int lno);
# define VMBVPTOLOWERVP(vp)     VMBlockCheckVp((vp), __FILE__, __LINE__)
#else
# define VMBVPTOLOWERVP(vp)     (VPTOVMB(vp)->lowerVnode)
#endif

#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_VMBLOCKFSNODE);
#endif

#endif /* _KERNEL */
#endif /* _VMBLOCK_K_H_ */
   07070100000253000081A4000000000000000000000001682255050000B806000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmblock/vnops.c    /* **********************************************************
 * Copyright 2007-2014, 2020, 2023 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * vnops.c --
 *
 *      Vnode operations for the vmblock filesystem on FreeBSD.
 */

/*-
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * John Heidemann of the UCLA Ficus project.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)null_vnops.c	8.6 (Berkeley) 5/27/95
 *
 * Ancestors:
 *	@(#)lofs_vnops.c	1.2 (Berkeley) 6/18/92
 *	...and...
 *	@(#)nullfs_vnops.c 1.20 92/07/07 UCLA Ficus project
 *
 * $FreeBSD: src/sys/fs/nullfs/null_vnops.c,v 1.87.2.3 2006/03/13 03:05:26 jeff Exp $
 */

/*
 * NB:  The following is the introductory commentary from FreeBSD's
 * null_vnops.c, which VMBlockFS is heavily based on.  It was kept intact in
 * order to provide a better conceptual introduction to FreeBSD's approach
 * to stackable file systems.
 */

/*
 * Null Layer
 *
 * (See mount_nullfs(8) for more information.)
 *
 * The null layer duplicates a portion of the filesystem name space under
 * a new name.  In this respect, it is similar to the loopback filesystem.
 * It differs from the loopback fs in two respects:  it is implemented
 * using a stackable layers techniques, and its "null-node"s stack above
 * all lower-layer vnodes, not just over directory vnodes.
 *
 * The null layer has two purposes.  First, it serves as a demonstration
 * of layering by proving a layer which does nothing.  (It actually
 * does everything the loopback filesystem does, which is slightly more
 * than nothing.)  Second, the null layer can serve as a prototype layer.
 * Since it provides all necessary layer framework, new filesystem layers
 * can be created very easily be starting with a null layer.
 *
 * The remainder of this man page examines the null layer as a basis
 * for constructing new layers.
 *
 *
 * INSTANTIATING NEW NULL LAYERS
 *
 * New null layers are created with mount_nullfs(8).  Mount_nullfs(8)
 * takes two arguments, the pathname of the lower vfs (target-pn)
 * and the pathname where the null layer will appear in the namespace
 * (alias-pn).  After the null layer is put into place, the contents of
 * target-pn subtree will be aliased under alias-pn.
 *
 *
 * OPERATION OF A NULL LAYER
 *
 * The null layer is the minimum filesystem layer, simply bypassing
 * all possible operations to the lower layer for processing there.
 * The majority of its activity centers on the bypass routine, through
 * which nearly all vnode operations pass.
 *
 * The bypass routine accepts arbitrary vnode operations for handling by
 * the lower layer.  It begins by examing vnode operation arguments and
 * replacing any null-nodes by their lower-layer equivlants.  It then
 * invokes the operation on the lower layer.  Finally, it replaces the
 * null-nodes in the arguments and, if a vnode is return by the operation,
 * stacks a null-node on top of the returned vnode.
 *
 * Although bypass handles most operations, vop_getattr, vop_lock,
 * vop_unlock, vop_inactive, vop_reclaim, and vop_print are
 * not bypassed. Vop_getattr must change the fsid being returned.
 * Vop_lock and vop_unlock must handle any locking for the current
 * vnode as well as pass the lock request down.  Vop_inactive and
 * vop_reclaim are not bypassed so that they can handle freeing null-layer
 * specific data. Vop_print is not bypassed to avoid excessive debugging
 * information.  Also, certain vnode operations change the locking state
 * within the operation (create, mknod, remove, link, rename, mkdir,
 * rmdir, and symlink). Ideally these operations should not change the
 * lock state, but should be changed to let the caller of the function
 * unlock them. Otherwise all intermediate vnode layers (such as union,
 * umapfs, etc) must catch these functions to do the necessary locking
 * at their layer.
 *
 *
 * INSTANTIATING VNODE STACKS
 *
 * Mounting associates the null layer with a lower layer, effect
 * stacking two VFSes.  Vnode stacks are instead created on demand as
 * files are accessed.
 *
 * The initial mount creates a single vnode stack for the root of the
 * new null layer.  All other vnode stacks are created as a result of
 * vnode operations on this or other null vnode stacks.
 *
 * New vnode stacks come into existance as a result of an operation
 * which returns a vnode.  The bypass routine stacks a null-node above
 * the new vnode before returning it to the caller.
 *
 * For example, imagine mounting a null layer with "mount_nullfs
 * /usr/include /dev/layer/null".  Changing directory to /dev/layer/null
 * will assign the root null-node (which was created when the null layer
 * was mounted).  Now consider opening "sys".  A vop_lookup would be
 * done on the root null-node.  This operation would bypass through
 * to the lower layer which would return a vnode representing the UFS
 * "sys".  Null_bypass then builds a null-node aliasing the UFS "sys"
 * and returns this to the caller.  Later operations on the null-node
 * "sys" will repeat this process when constructing other vnode stacks.
 *
 *
 * CREATING OTHER FILE SYSTEM LAYERS
 *
 * One of the easiest ways to construct new filesystem layers is to make a
 * copy of the null layer, rename all files and variables, and then begin
 * modifing the copy.  Sed can be used to easily rename all variables.
 *
 * The umap layer is an example of a layer descended from the null layer.
 *
 *
 * INVOKING OPERATIONS ON LOWER LAYERS
 *
 * There are two techniques to invoke operations on a lower layer when the
 * operation cannot be completely bypassed.  Each method is appropriate
 * in different situations.  In both cases, it is the responsibility of
 * the aliasing layer to make the operation arguments "correct" for the
 * lower layer by mapping a vnode arguments to the lower layer.
 *
 * The first approach is to call the aliasing layer's bypass routine.
 * This method is most suitable when you wish to invoke the operation
 * currently being handled on the lower layer.  It has the advantage that
 * the bypass routine already must do argument mapping.  An example of
 * this is vop_getattr in the null layer.
 *
 * A second approach is to directly invoke vnode operations on the
 * lower layer with the VOP_OPERATIONNAME interface.  The advantage
 * of this method is that it is easy to invoke arbitrary operations on
 * the lower layer.  The disadvantage is that vnode arguments must be
 * manualy mapped.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/mutex.h>
#include <sys/namei.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/kdb.h>
#include "compat_freebsd.h"
#include <sys/priv.h>

#include "vmblock_k.h"
#include "vmblock.h"
#include "block.h"

#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_object.h>
#include <vm/vnode_pager.h>


/*
 * Local functions
 */

static fo_ioctl_t               VMBlockFileIoctl;
static fo_close_t               VMBlockFileClose;
static vop_access_t             VMBlockVopAccess;
static vop_getattr_t            VMBlockVopGetAttr;
static vop_getwritemount_t      VMBlockVopGetWriteMount;
static vop_inactive_t           VMBlockVopInactive;
static vop_ioctl_t              VMBlockVopIoctl;
static vop_islocked_t           VMBlockVopIsLocked;
static compat_vop_lock_t        VMBlockVopLock;
static vop_lookup_t             VMBlockVopLookup;
static vop_open_t               VMBlockVopOpen;
static vop_print_t              VMBlockVopPrint;
static vop_reclaim_t            VMBlockVopReclaim;
static vop_rename_t             VMBlockVopRename;
static vop_setattr_t            VMBlockVopSetAttr;
static vop_unlock_t             VMBlockVopUnlock;


/*
 * Local data
 */

/*
 * The following is an ioctl(2) argument wrapper for VMBlockVopIoctl.
 * See the VMBlockFileOps blurb below for details.
 */
struct VMBlockIoctlArgs {
   struct file  *fileDesc;      // file descriptor receiving ioctl request
   void         *data;          // user's ioctl argument
};

typedef struct VMBlockIoctlArgs VMBlockIoctlArgs;

/*
 * VMBlockFS vnode operations vector --
 *   Following are the file system's entry points via VFS nodes (vnodes).
 *   See vnode(9) and sys/vnode.h for more information.  For details on
 *   the locking protocol[1], have a look at kern/vnode_if.src.
 *
 * 1.  Describes for which operations a vnode should be locked before 
 *     the operation is called or after it returns.
 */

struct vop_vector VMBlockVnodeOps = {
   .vop_bypass =                  VMBlockVopBypass,
   .vop_access =                  VMBlockVopAccess,
   .vop_advlockpurge =            vop_stdadvlockpurge,
   .vop_bmap =                    VOP_EOPNOTSUPP,
   .vop_getattr =                 VMBlockVopGetAttr,
   .vop_getwritemount =           VMBlockVopGetWriteMount,
   .vop_inactive =                VMBlockVopInactive,
   .vop_ioctl =                   VMBlockVopIoctl,
   .vop_islocked =                VMBlockVopIsLocked,
   .COMPAT_VOP_LOCK_OP_ELEMENT =  VMBlockVopLock,
   .vop_lookup =                  VMBlockVopLookup,
   .vop_open =                    VMBlockVopOpen,
   .vop_print =                   VMBlockVopPrint,
   .vop_reclaim =                 VMBlockVopReclaim,
   .vop_rename =                  VMBlockVopRename,
   .vop_setattr =                 VMBlockVopSetAttr,
   .vop_strategy =                VOP_EOPNOTSUPP,
   .vop_unlock =                  VMBlockVopUnlock,
};



/*
 * VMBlockFS file descriptor operations vector --
 *   There are a few special cases where we need to control behavior beyond
 *   the file system layer.  For this we define our own fdesc op vector,
 *   install our own handlers for these special cases, and fall back to the
 *   badfileops vnode ops for everything else.
 *
 *   VMBlock instances are keyed on/indexed by the file descriptor that received
 *   the ioctl request[1].  Since the relationship between file descriptors and
 *   vnodes is N:1, we need to intercept ioctl requests at the file descriptor
 *   level, rather than at the vnode level, in order to have a record of which
 *   descriptor received the request.  Similarly, we need to remove VMBlocks
 *   issued on a file descriptor when said descriptor is closed.
 *
 *   NOTICE --
 *     This applies -only- when a user opens the FS mount point directly.  All
 *     other files'/directories' file descriptor operations vectors are left
 *     untouched.
 *
 * 1.  Keying on thread ID/process ID doesn't work because file descriptors
 *     may be shared between threads/processes.  Clients may find blocks
 *     removed unintentionally when the original issuing thread or process
 *     dies, even though the same descriptor is open.
 */

static struct fileops VMBlockFileOps;

/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockSetupFileOps --
 *
 *      Sets up secial file operations vector used for root vnode _only_
 *      (see the comment for VMBlockFileOps above).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
VMBlockSetupFileOps(void)
{
   VMBlockFileOps = badfileops;
   VMBlockFileOps.fo_stat = vnops.fo_stat;
   VMBlockFileOps.fo_flags = vnops.fo_flags;
   VMBlockFileOps.fo_ioctl = VMBlockFileIoctl;
   VMBlockFileOps.fo_close = VMBlockFileClose;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockFileIoctl --
 *
 *      Wrapper for VMBlockVopIoctl.  This is done to provide VMBlockVopIoctl
 *      with information about the file descriptor which received the user's
 *      ioctl request.
 *
 * Results:
 *      Zero on success, otherwise an appropriate system error.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockFileIoctl(struct file *fp,       // IN: user's file descriptor
                 u_long command,        // IN: encoded ioctl command
                 void *data,            // IN: opaque data argument
                 struct ucred *cred,    // IN: caller's credentials
                 struct thread *td)     // IN: caller's thread context
{
   VMBlockIoctlArgs args;
   args.fileDesc = fp;
   args.data = data;
   return vnops.fo_ioctl(fp, command, &args, cred, td);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockFileClose --
 *
 *      Called when a file descriptor is closed.  Destroy all blocks opened
 *      on this descriptor, then pass off to vn_closefile to handle any other
 *      cleanup.
 *
 * Results:
 *      Zero on success, an appropriate system error otherwise.
 *      (See vn_closefile for more information.)
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockFileClose(struct file *fp,       // IN: user's file descriptor
                 struct thread *td)     // IN: caller's thread context
{
   struct vnode *vp;
   int removed = 0;

   vp = MNTTOVMBLOCKMNT(fp->f_vnode->v_mount)->rootVnode;
   removed = BlockRemoveAllBlocks(fp);

   VI_LOCK(vp);
   vp->v_usecount -= removed;
   VI_UNLOCK(vp);

   return vnops.fo_close(fp, td);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopBypass --
 *
 *      Default routine for bypassing the VMBlockFS file system layer.
 *
 * Results:
 *      Zero on success, or an appropriate system error otherwise.
 *
 * Side effects:
 *      Parameters passed in via ap->a_desc may be modified by the lower
 *      layer's routines.
 *
 * Original function comment:
 *	This is the 10-Apr-92 bypass routine.
 *	   This version has been optimized for speed, throwing away some
 *	safety checks.  It should still always work, but it's not as
 *	robust to programmer errors.
 *
 *	In general, we map all vnodes going down and unmap them on the way back.
 *	As an exception to this, vnodes can be marked "unmapped" by setting
 *	the Nth bit in operation's vdesc_flags.
 *
 *	Also, some BSD vnode operations have the side effect of vrele'ing
 *	their arguments.  With stacking, the reference counts are held
 *	by the upper node, not the lower one, so we must handle these
 *	side-effects here.  This is not of concern in Sun-derived systems
 *	since there are no such side-effects.
 *
 *	This makes the following assumptions:
 *	- only one returned vpp
 *	- no INOUT vpp's (Sun's vop_open has one of these)
 *	- the vnode operation vector of the first vnode should be used
 *	  to determine what implementation of the op should be invoked
 *	- all mapped vnodes are of our vnode-type (NEEDSWORK:
 *	  problems on rmdir'ing mount points and renaming?)
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockVopBypass(struct vop_generic_args *ap)
/*
struct vop_generic_args {
   struct vnodeop_desc *a_desc; // IN/OUT: Vnode operation description; incl.
                                //         pointers to operand vnode(s), user
                                //         credentials, calling thread context,
                                //         etc.
};
*/
{
   struct vnode **this_vp_p;
   int error;
   struct vnode *old_vps[VDESC_MAX_VPS];
   struct vnode **vps_p[VDESC_MAX_VPS];
   struct vnode ***vppp;
   struct vnodeop_desc *descp = ap->a_desc;
   int reles, i;

#ifdef DIAGNOSTIC
   /*
    * We require at least one vp.
    */
   if (descp->vdesc_vp_offsets == NULL ||
       descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET) {
      panic ("VMBlockVopBypass: no vp's in map");
   }
#endif

   /*
    * Map the vnodes going in.  Later, we'll invoke the operation based on
    * the first mapped vnode's operation vector.
    */
   reles = descp->vdesc_flags;
   for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
      if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) {
         break;   /* bail out at end of list */
      }

      vps_p[i] = this_vp_p =
         VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap);

      /*
       * We're not guaranteed that any but the first vnode are of our type.
       * Check for and don't map any that aren't.  (We must always map first
       * vp or vclean fails.)
       */
      if (i && (*this_vp_p == NULLVP ||
          (*this_vp_p)->v_op != &VMBlockVnodeOps)) {
         old_vps[i] = NULLVP;
      } else {
         old_vps[i] = *this_vp_p;
         *(vps_p[i]) = VMBVPTOLOWERVP(*this_vp_p);
         /*
          * XXX - Several operations have the side effect of vrele'ing their
          * vp's.  We must account for that.  (This should go away in the
          * future.)
          */
         if (reles & VDESC_VP0_WILLRELE) {
            VREF(*this_vp_p);
         }
      }
   }

   /*
    * Call the operation on the lower layer with the modified argument
    * structure.
    */
   if (vps_p[0] && *vps_p[0]) {
      error = VCALL(ap);
   } else {
      printf("VMBlockVopBypass: no map for %s\n", descp->vdesc_name);
      error = EINVAL;
   }

   /*
    * Maintain the illusion of call-by-value by restoring vnodes in the
    * argument structure to their original value.
    */
   reles = descp->vdesc_flags;
   for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
      if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET) {
         break;   /* bail out at end of list */
      }
      if (old_vps[i]) {
         *(vps_p[i]) = old_vps[i];
         if (reles & VDESC_VP0_WILLRELE) {
            vrele(*(vps_p[i]));
         }
      }
   }

   /*
    * Map the possible out-going vpp (Assumes that the lower layer always
    * returns a VREF'ed vpp unless it gets an error.)
    */
   if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET && !error) {
      vppp = VOPARG_OFFSETTO(struct vnode***, descp->vdesc_vpp_offset,ap);
      if (*vppp) {
         /* FIXME: set proper name for the vnode */
         error = VMBlockNodeGet(old_vps[0]->v_mount, **vppp, *vppp, NULL);
      }
   }

   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopLookup --
 *
 *      "VOP_LOOKUP(9) -- lookup a component of a pathname"
 *
 * Results:
 *      Zero if the component name is found.  EJUSTRETURN if the namei
 *      operation is CREATE or RENAME, we're looking up the final component
 *      name, and said operation would succeed.  Otherwise returns an
 *      appropriate system error.
 *
 * Side effects:
 *      Requested vnode is locked and returned in *ap->a_vpp.
 *
 * Original function comment:
 *	We have to carry on the locking protocol on the null layer vnodes
 *	as we progress through the tree. We also have to enforce read-only
 *	if this layer is mounted read-only.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopLookup(struct vop_lookup_args *ap)
/*
struct vop_lookup_args {
   struct vnode *dvp;           // IN: pointer to searched directory
   struct vnode **vpp;          // OUT: if found, points to requested
                                //      vnode; else NULL
   struct componentname *cnp;   // IN: directory search context
};
*/
{
   struct componentname *cnp = ap->a_cnp;
   COMPAT_THREAD_VAR(td, cnp->cn_thread);
   struct vnode *dvp = ap->a_dvp;
   struct vnode *vp, *ldvp, *lvp;
   BlockHandle blockCookie;
   int flags = cnp->cn_flags;
   int error = 0;
   char *pathname;
   size_t pathname_len;

   /*
    * Fail attempts to modify a read-only filesystem w/o bothering with a
    * lower-layer lookup.
    */
   if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
       (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
      return EROFS;
   }

   /*
    * Before looking in the lower layer, determine whether the search path
    * should be blocked.  If so, do the following:
    *   1.  Make a copy of the block pathname.  (BlockWaitOnFile may make
    *       use of this, and our VMBlockNode may be destroyed while asleep
    *       if user forcibly unmounts file system.)
    *   2.  Bump up hold counts of current VMBlock directory vnode and its
    *       lower layer counterpart.  This makes sure that at least they
    *       aren't purged from memory while we sleep.
    *   3.  Unlock & relock directory vnodes around sleeping.  This prevents
    *       a cascading file system lookup deadlock.  (E.g., we have dvp locked,
    *       but another thread trying to look up dvp will block, holding /its/
    *       dvp's (dvp2) lock, and yet another thread would block looking up
    *       dvp2 while holding its dvp (dvp3), etc.
    *
    * If we find we were forcibly unmounted, fail with EIO.
    */

   pathname = uma_zalloc(VMBlockPathnameZone, M_WAITOK);
   if (pathname == NULL) {
      return ENOMEM;
   }

   /*
    * FIXME: we need to ensure that vnode always has name set up.
    * Currently VMBlockVopBypass() may produce vnodes without a name.
    */
   pathname_len = strlcpy(pathname,
                          VPTOVMB(dvp)->name ? VPTOVMB(dvp)->name : ".",
                          MAXPATHLEN);
   /*
    * Make sure we have room in the buffer to add our component.
    * + 1 is for separator (slash).
    */
   if (pathname_len + 1 + cnp->cn_namelen >= MAXPATHLEN) {
      error = ENAMETOOLONG;
      goto out;
   }

   if ((blockCookie = BlockLookup(pathname, OS_UNKNOWN_BLOCKER)) != NULL) {
      int lkflags = compat_lockstatus(dvp->v_vnlock, td) & LK_TYPE_MASK;
      lvp = VPTOVMB(dvp)->lowerVnode;
      vhold(dvp);
      vhold(lvp);
      COMPAT_VOP_UNLOCK(dvp, 0, td);

      error = BlockWaitOnFile(pathname, blockCookie);

      COMPAT_VOP_LOCK(dvp, lkflags, td);
      vdrop(lvp);
      vdrop(dvp);
      if (dvp->v_op != &VMBlockVnodeOps) {
         Debug("%s: vmblockfs forcibly unmounted?\n", __func__);
         error = EIO;
      }

      if (error) {
         goto out;
      }
   }

   /* We already verified that buffer is big enough. */
   pathname[pathname_len] = '/';
   bcopy(cnp->cn_nameptr, &pathname[pathname_len + 1], cnp->cn_namelen);
   pathname[pathname_len + 1 + cnp->cn_namelen] = 0;

   /*
    * Although it is possible to call VMBlockVopBypass(), we'll do a direct
    * call to reduce overhead
    */
   ldvp = VMBVPTOLOWERVP(dvp);
   vp = lvp = NULL;

   error = VOP_LOOKUP(ldvp, &lvp, cnp);
   if (error == EJUSTRETURN && (flags & ISLASTCN) &&
       (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
       (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME)) {
      error = EROFS;
   }

   if ((error == 0 || error == EJUSTRETURN) && lvp != NULL) {
      /*
       * Per VOP_LOOKUP(9), if looking up the current directory ("."), we bump
       * our vnode's refcount.
       */
      if (ldvp == lvp) {
         *ap->a_vpp = dvp;
         VREF(dvp);
         vrele(lvp);
      } else {
         error = VMBlockNodeGet(dvp->v_mount, lvp, &vp, pathname);
         if (error) {
            /* XXX Cleanup needed... */
            panic("VMBlockNodeGet failed");
         }
         *ap->a_vpp = vp;
         /* The vnode now owns pathname so don't try to free it below. */
         pathname = NULL;
      }
   }

out:
   if (pathname) {
      uma_zfree(VMBlockPathnameZone, pathname);
   }
   return error;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockVopOpen --
 *
 *      "The VOP_OPEN() entry point is called before a file is accessed by a
 *      process..." - VOP_OPEN(9).  If the vnode in question is the file
 *      system's root vnode, allow access only to the superuser.
 *
 * Results:
 *      Zero on success, an appropriate system error otherwise.
 *
 * Side effects:     
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockVopOpen(struct vop_open_args *ap)
/*
struct vop_open_args {
   struct vnode *vp;    // IN: vnode which ioctl issued upon
   int fflag;           // IN: fdesc flags (see fcntl(2))
   struct ucred *cred;  // IN: caller's credentials (usually real uid vs euid)
   struct thread *td;   // IN: calling thread's context      
   FreeBSD <= 6 --
      int fdidx;        // IN: file descriptor number alloc'd to this open()
   FreeBSD >= 7 --
      struct file *fp   // IN: struct associated with this particular open()
};
*/
{
   VMBlockMount *mp;
   struct vnode *vp, *ldvp;
   struct file *fp;
   int retval;

   vp = ap->a_vp;
   ldvp = VMBVPTOLOWERVP(vp);

   mp = MNTTOVMBLOCKMNT(ap->a_vp->v_mount);
   if (ap->a_vp == mp->rootVnode) {
      /*
       * Opening the mount point is a special case.  First, only allow this
       * access to the superuser.  Next, we install a custom fileops vector in
       * order to trap the ioctl() and close() operations.  (See the *FileOps'
       * descriptions for more details.)
       *
       * NB:  Allowing only the superuser to open this directory breaks
       *      readdir() of the filesystem root for non-privileged users.
       *
       * Also, on FreeBSD 8.0 and newer we check for a specific module priv
       * because none of the existing privs seemed to match very well.
       */
      if ((retval = compat_priv_check(ap->a_td, PRIV_DRIVER)) == 0) {
         fp = ap->a_fp;
         fp->f_ops = &VMBlockFileOps;
      }
   } else {
      /*
       * Pass off to the lower layer.  If lower layer mapped a VM object, copy
       * its reference.
       */
      retval = VMBlockVopBypass(&ap->a_gen);
      if (retval == 0) {
         vp->v_object = ldvp->v_object;
      }
   }

   return retval;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopSetAttr --
 *
 *      "VOP_SETATTR(9) -- set attributes on a file or directory."
 *
 *      This version is simpler than the original null_setattr as it only
 *      tests whether the user is attempting an operation in a read-only
 *      file system.  Beyond that, it defers judgment about the validity of
 *      the request to the lower layer via vop_bypass.
 *
 * Results:
 *      Zero on success, else an appropriate system error.
 *
 * Side effects:
 *      None.
 *
 * Original function comment:
 *      Setattr call. Disallow write attempts if the layer is mounted read-
 *      only.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopSetAttr(struct vop_setattr_args *ap)
/*
struct vop_setattr_args {
   struct vnode *vp;    // IN: vnode operand
   struct vattr *vap;   // IN: attributes
   struct ucred *cred;  // IN: caller's credentials
   struct thread *td;   // IN: caller's thread context
};
*/
{
   struct vnode *vp = ap->a_vp;

   if (vp->v_mount->mnt_flag & MNT_RDONLY) {
      return EROFS;
   }

   return VMBlockVopBypass((struct vop_generic_args *)ap);
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockVopIoctl --
 *
 *      Handle ioctl(2) requests to add and remove file blocks.  Ioctl
 *      commands are defined in public/vmblock.h.  
 *
 * Results:
 *      Zero on success, otherwise an appropriate error is returned.
 *
 * Side effects
 *      A block may be placed on or removed from a file.  The root vnode's
 *      reference count will be incremented when a block is successfully added,
 *      and it will be decremented when a block is removed.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockVopIoctl(struct vop_ioctl_args *ap)      // IN/OUT: ioctl parameters
/*
struct vop_ioctl_args {
   struct vnode *vp;    // IN: vnode which ioctl issued upon
   u_long command;      // IN: ioctl command
   caddr_t data;        // IN: ioctl parameter (e.g., pathname)
   int fflag;           // IN: fcntl style flags (no-op?)
   struct ucred *cred;  // IN: caller's credentials (usually real uid vs euid)
   struct thread *td;   // IN: calling thread's context      
};
*/
{
   VMBlockIoctlArgs *ioctlArgs = (VMBlockIoctlArgs *)ap->a_data;
   VMBlockMount *mp;
   COMPAT_THREAD_VAR(td, ap->a_td);
   struct vnode *vp = ap->a_vp;
   char *pathbuf = NULL;
   int ret = 0, pathlen;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockVopIoctl: entry\n");

   /*
    * The operand vnode is passed in unlocked, so test a few things before
    * proceeding.
    *   1.  Make sure we're still dealing with a VMBlock vnode.  Note
    *       that this test -must- come before the next one.  Otherwise v_mount
    *       may be invalid.
    *   2.  Make sure the filesystem isn't being unmounted.
    */
   COMPAT_VOP_LOCK(vp, LK_EXCLUSIVE|LK_RETRY, td);
   if (vp->v_op != &VMBlockVnodeOps ||
       vp->v_mount->mnt_kern_flag & MNTK_UNMOUNT) {
      COMPAT_VOP_UNLOCK(vp, 0, td);
      return EBADF;
   }

   /*
    * At this layer/in this file system, only the root vnode handles ioctls,
    * and only the superuser may open the root vnode.  If we're not given
    * the root vnode, simply bypass to the next lower layer.
    */
   mp = MNTTOVMBLOCKMNT(vp->v_mount);
   if (vp != mp->rootVnode) {
      /*
       * VMBlockFileIoctl wraps the user's data in a special structure which
       * includes the user's file descriptor, so we must unwrap the data
       * argument before passing to the lower layer.
       */
      ap->a_data = ioctlArgs->data;
      COMPAT_VOP_UNLOCK(vp, 0, td);
      return VMBlockVopBypass((struct vop_generic_args *)ap);
   }

   pathbuf = uma_zalloc(VMBlockPathnameZone, M_WAITOK);

   switch (ap->a_command) {
   case VMBLOCK_ADD_FILEBLOCK:
   case VMBLOCK_DEL_FILEBLOCK:
   {
      /*
       * Trim trailing slashes
       */
      pathlen = strlcpy(pathbuf, ioctlArgs->data, MAXPATHLEN);
      pathlen = MIN(pathlen, MAXPATHLEN);
      while (pathlen > 0 && pathbuf[pathlen - 1] == '/') {
         pathbuf[pathlen - 1] = '\0';
         pathlen--;
      }

      VMBLOCKDEBUG("%s: %s on %s\n", __func__,
                   (ap->a_command == VMBLOCK_ADD_FILEBLOCK) ? "add" : "del",
                   pathbuf);

      /*
       * Don't block the mount point!
       */
      if (!strcmp(VPTOVMB(vp)->name, pathbuf)) {
         ret = EINVAL;
      } else {
         ret = (ap->a_command == VMBLOCK_ADD_FILEBLOCK) ?
                  BlockAddFileBlock(pathbuf, ioctlArgs->fileDesc) :
                  BlockRemoveFileBlock(pathbuf, ioctlArgs->fileDesc);

         /*
          * When adding a block, bump the reference count on the root vnode.  If
          * removing a block, decrement the reference count.  Of course, only do
          * so if the action succeeds!
          */
         if (ret == 0) {
            VI_LOCK(vp);
            vp->v_usecount += (ap->a_command == VMBLOCK_ADD_FILEBLOCK) ? 1 : -1;
            VI_UNLOCK(vp);
         }
      }
      break;
   }
#ifdef VMX86_DEVEL
   case VMBLOCK_LIST_FILEBLOCKS:
      BlockListFileBlocks();
      ret = 0;
      break;
   case VMBLOCK_PURGE_FILEBLOCKS:
      {
         int removed = 0;
         removed = BlockRemoveAllBlocks(OS_UNKNOWN_BLOCKER);
         VI_LOCK(vp);
         vp->v_usecount -= removed;
         VI_UNLOCK(vp);
      }
      ret = 0;
      break;
#endif
   default:
      Warning("VMBlockVopIoctl: unknown command (%lu) received.\n", ap->a_command);
      ret = EOPNOTSUPP;
   }

   COMPAT_VOP_UNLOCK(vp, 0, td);
   if (pathbuf) {
      uma_zfree(VMBlockPathnameZone, pathbuf);
   }
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockVopGetAttr --
 *
 *      Query the underlying filesystem for file/directory information.
 *      Also fixup fsid to be ours rather than that of the underlying fs.
 *
 * Results:
 *      Zero on success, an appropriate system error otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockVopGetAttr(struct vop_getattr_args *ap)
/*
struct vop_getattr_args {
   struct vnode *vp;    // IN: operand vnode
   struct vattr *vap;   // OUT: vnode's parameters
   struct ucred *ucred; // IN: caller's credentials
   struct thread *td;   // IN: caller's thread context
};
*/
{
   int error;

   if ((error = VMBlockVopBypass((struct vop_generic_args *)ap)) != 0) {
      return error;
   }

   ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockVopAccess --
 *
 *      "VOP_ACCESS(9) -- check access permissions of a file or Unix domain
 *      socket."  We handle this to disallow write access if our layer is,
 *      for whatever reason, mounted read-only.
 *
 * Results:
 *      Zero on success, an appropriate system error otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockVopAccess(struct vop_access_args *ap)
/*
struct vop_access_args {
   struct vnode *vp;    // IN: operand vnode
   int mode;            // IN: access(2) flags
   struct vattr *vap;   // OUT: vnode's parameters
   struct ucred *ucred; // IN: caller's credentials
   struct thread *td;   // IN: caller's thread context
};
*/
{
   struct vnode *vp = ap->a_vp;
   compat_accmode_t mode = ap->compat_a_accmode;

   /*
    * Disallow write attempts on read-only layers; unless the file is a
    * socket, fifo, or a block or character device resident on the filesystem.
    */
   if (mode & VWRITE) {
      switch (vp->v_type) {
      case VDIR:
      case VLNK:
      case VREG:
         if (vp->v_mount->mnt_flag & MNT_RDONLY) {
            return EROFS;
         }
         break;
      default:
         break;
      }
   }
   return VMBlockVopBypass((struct vop_generic_args *)ap);
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockRename --
 *
 *      "VOP_RENAME(9) -- rename a file."
 *
 * Results:
 *      Zero on success, an appropriate system error otherwise.
 *
 * Side effects:
 *      None.
 *
 * Original function comment:
 *      We handle this to eliminate null FS to lower FS file moving. Don't
 *      know why we don't allow this, possibly we should.
 *----------------------------------------------------------------------------
 */

static int
VMBlockVopRename(struct vop_rename_args *ap)
/*
struct vop_rename_args {
   struct vnode *fdvp;          // IN: source directory
   struct vnode *fvp;           // IN: source file
   struct componentname *fcnp;  // IN: source's path lookup state
   struct vnode *tdvp;          // IN: destination directory
   struct vnode *tvp;           // IN: destination file
   struct componentname *tcnp;  // IN: destination's path lookup state
};
*/
{
   struct vnode *tdvp = ap->a_tdvp;
   struct vnode *fvp = ap->a_fvp;
   struct vnode *fdvp = ap->a_fdvp;
   struct vnode *tvp = ap->a_tvp;

   /* Check for cross-device rename. */
   if ((fvp->v_mount != tdvp->v_mount) ||
       (tvp && (fvp->v_mount != tvp->v_mount))) {
      if (tdvp == tvp) {
         vrele(tdvp);
      } else {
         vput(tdvp);
      }
      if (tvp) {
         vput(tvp);
      }
      vrele(fdvp);
      vrele(fvp);
      return EXDEV;
   }

   return VMBlockVopBypass((struct vop_generic_args *)ap);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopLock --
 *
 *      Acquire a vnode lock.
 *
 * Results:
 *      Zero on success, otherwise an error is returned.
 *
 * Side effects:
 *      Upper & lower layers share a lock, so both vnodes will be considered
 *      locked.
 *
 * Original function comment:
 *      We need to process our own vnode lock and then clear the interlock flag
 *      as it applies only to our vnode, not the vnodes below us on the stack.
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopLock(compat_vop_lock_args *ap)
/*
struct vop_lock_args {
   struct vnode *vp;    // IN: vnode operand
   int flags;           // IN: lockmgr(9) flags
   struct thread *td;   // IN: calling thread's context
};
*/
{
   struct vnode *vp = ap->a_vp;
   int flags = ap->a_flags;
   COMPAT_THREAD_VAR(td, ap->a_td);
   struct VMBlockNode *nn;
   struct vnode *lvp;
   int error;


   if ((flags & LK_INTERLOCK) == 0) {
      VI_LOCK(vp);
      ap->a_flags = flags |= LK_INTERLOCK;
   }
   nn = VPTOVMB(vp);
   /*
    * If we're still active we must ask the lower layer to lock as ffs
    * has special lock considerations in it's vop lock. -- FreeBSD
    */
   if (nn != NULL && (lvp = VMBVPTOLOWERVP(vp)) != NULL) {
      VI_LOCK_FLAGS(lvp, MTX_DUPOK);
      VI_UNLOCK(vp);
      /*
       * We have to hold the vnode here to solve a potential reclaim race.
       * If we're forcibly vgone'd while we still have refs, a thread
       * could be sleeping inside the lowervp's vop_lock routine.  When we
       * vgone we will drop our last ref to the lowervp, which would
       * allow it to be reclaimed.  The lowervp could then be recycled,
       * in which case it is not legal to be sleeping in it's VOP.
       * We prevent it from being recycled by holding the vnode here.
       */
      vholdl(lvp);
      error = COMPAT_VOP_LOCK(lvp, flags, td);

      /*
       * We might have slept to get the lock and someone might have clean
       * our vnode already, switching vnode lock from one in lowervp
       * to v_lock in our own vnode structure.  Handle this case by
       * reacquiring correct lock in requested mode.
       */
      if (VPTOVMB(vp) == NULL && error == 0) {
         ap->a_flags &= ~(LK_TYPE_MASK | LK_INTERLOCK);
         switch (flags & LK_TYPE_MASK) {
         case LK_SHARED:
            ap->a_flags |= LK_SHARED;
            break;
         case LK_UPGRADE:
         case LK_EXCLUSIVE:
            ap->a_flags |= LK_EXCLUSIVE;
            break;
         default:
            panic("Unsupported lock request %d\n",
                ap->a_flags);
         }
         COMPAT_VOP_UNLOCK(lvp, 0, td);
         error = vop_stdlock(ap);
      }
      vdrop(lvp);
   } else {
      error = vop_stdlock(ap);
   }

   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopUnlock --
 *
 *      Release a vnode lock.
 *
 * Results:
 *      Zero on success, an appropriate system error otherwise.
 *
 * Side effects:
 *      None.
 *
 * Original function comment:
 *      We need to process our own vnode unlock and then clear the interlock
 *      flag as it applies only to our vnode, not the vnodes below us on
 *      the stack.
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopUnlock(struct vop_unlock_args *ap)
/*
struct vop_unlock_args {
   struct vnode *vp;    // IN: vnode operand
   int flags;           // IN: lock request flags (see lockmgr(9))
   struct thread *td;   // IN: calling thread's context
};
*/
{
   struct vnode *vp = ap->a_vp;
#if __FreeBSD_version < 1300074
   int flags = ap->a_flags;
#endif
   COMPAT_THREAD_VAR(td, ap->a_td);
   struct VMBlockNode *nn;
   struct vnode *lvp;
   int error;

#if __FreeBSD_version < 1300074
   /*
    * If caller already holds interlock, drop it.  (Per VOP_UNLOCK() API.)
    * Also strip LK_INTERLOCK from flags passed to lower layer.
    */
   if ((flags & LK_INTERLOCK) != 0) {
      VI_UNLOCK(vp);
      ap->a_flags = flags &= ~LK_INTERLOCK;
   }
#endif
   nn = VPTOVMB(vp);
   if (nn != NULL && (lvp = VMBVPTOLOWERVP(vp)) != NULL) {
#if __FreeBSD_version < 1300074
      error = COMPAT_VOP_UNLOCK(lvp, flags, td);
#else
      error = COMPAT_VOP_UNLOCK(lvp, 0, td);
#endif
   } else {
      error = vop_stdunlock(ap);
   }

   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopIsLocked --
 *
 *      Test whether a vnode is locked.
 *
 * Results:
 *      Zero if locked, non-zero otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopIsLocked(struct vop_islocked_args *ap)
/*
struct vop_islocked_args {
   struct vnode *vp;    // IN: vnode operand 
   struct thread *td;   // IN: calling thread's context
};
*/
{
   struct vnode *vp = ap->a_vp;
   COMPAT_THREAD_VAR(td, ap->a_td);

   return compat_lockstatus(vp->v_vnlock, td);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopInactive --
 *
 *      "VOP_INACTIVE() is called when the kernel is no longer using the vnode.
 *      This may be because the reference count reaches zero or it may be that
 *      the file system is being forcibly unmounted while there are open files.
 *      It can be used to reclaim space for `open but deleted' files."
 *
 * Results:
 *      Zero.
 *
 * Side effects:
 *      If this vnode's reference is zero, vrecycle() will handle induce
 *      cleanup.
 *
 * Original function comment:
 *	There is no way to tell that someone issued remove/rmdir operation
 *	on the underlying filesystem. For now we just have to release lowevrp
 *	as soon as possible.
 *
 *	Note, we can't release any resources nor remove vnode from hash before
 *	appropriate VXLOCK stuff is is done because other process can find
 *	this vnode in hash during inactivation and may be sitting in vget()
 *	and waiting for VMBlockVopInactive to unlock vnode. Thus we will do
 *	all those in VOP_RECLAIM.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopInactive(struct vop_inactive_args *ap)
/*
struct vop_inactive_args {
   struct vnode *vp;    // IN: vnode operand 
   struct thread *td;   // IN: calling thread's context
};
*/
{
   struct vnode *vp = ap->a_vp;

   vp->v_object = NULL;

   /*
    * If this is the last reference, then free up the vnode so as not to
    * tie up the lower vnode.
    */
   vrecycle(vp);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopReclaim --
 *
 *      "VOP_RECLAIM() is called when a vnode is being reused for a
 *      different file system.  Any file system specific resources
 *      associated with the vnode should be freed."
 *
 * Results:
 *      Returns zero.
 *
 * Side effects:
 *      If node is an associate VMBlockNode, it's removed from
 *      the VMBlockNode hash and freed.  Reference to the lower vnode, if
 *      it exists, is also dropped.      
 *
 * Original function comment:
 *      Now, the VXLOCK is in force and we're free to destroy the null vnode.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopReclaim(struct vop_reclaim_args *ap)
/*
struct vop_reclaim_args {
   struct vnode *vp;    // IN: vnode operand 
   struct thread *td;   // IN: calling thread's context
};
*/
{
   struct vnode *vp = ap->a_vp;
   struct VMBlockNode *xp = VPTOVMB(vp);
   struct vnode *lowervp = xp->lowerVnode;

   KASSERT(lowervp != NULL, ("reclaiming node with no lower vnode"));

   VMBlockHashRem(xp);

   /*
    * Use the interlock to protect the clearing of v_data to
    * prevent faults in VMBlockVopLock().
    */
   VI_LOCK(vp);
   vp->v_data = NULL;
   vp->v_object = NULL;

   /*
    * Reassign lock pointer to this vnode's lock.  (Originally assigned
    * to the lower layer's lock.)
    */
   vp->v_vnlock = &vp->v_lock;
   compat_lockmgr(vp->v_vnlock, LK_EXCLUSIVE|LK_INTERLOCK, VI_MTX(vp), curthread);
   vput(lowervp);

   /*
    * Clean up VMBlockNode attachment.
    */
   if (xp->name) {
      uma_zfree(VMBlockPathnameZone, xp->name);
   }
   free(xp, M_VMBLOCKFSNODE);

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopPrint --
 *
 *      "VOP_PRINT -- print debugging information"
 *
 * Results:
 *      Zero.  Always.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopPrint(struct vop_print_args *ap)
/*
struct vop_print_args {
   struct vnode *vp;    // IN: vnode operand
};
*/
{
   struct vnode *vp = ap->a_vp;
   printf("\tvp=%p, lowervp=%p\n", vp, VMBVPTOLOWERVP(vp));
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockVopGetWriteMount --
 *
 *      When the caller wishes to begin a write operation, we need to bump
 *      the count of write operations on the destination file system.  This
 *      routine passes the request down.  "Real" file systems will usually
 *      call vop_stdgetwritemount().
 *
 * Results:
 *      Zero.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VMBlockVopGetWriteMount(struct vop_getwritemount_args *ap)
/*
struct vop_getwritemount_args {
   struct vnode *vp;    // IN: vnode operand
   struct mount **mpp;  // OUT: pointer to filesystem where write operation
                        //      will actually occur
};
*/
{
   struct VMBlockNode *xp;
   struct vnode *lowervp;
   struct vnode *vp;

   vp = ap->a_vp;
   VI_LOCK(vp);
   xp = VPTOVMB(vp);
   if (xp && (lowervp = xp->lowerVnode)) {
      VI_LOCK_FLAGS(lowervp, MTX_DUPOK);
      VI_UNLOCK(vp);
      vholdl(lowervp);
      VI_UNLOCK(lowervp);
      VOP_GETWRITEMOUNT(lowervp, ap->a_mpp);
      vdrop(lowervp);
   } else {
      VI_UNLOCK(vp);
      *(ap->a_mpp) = NULL;
   }
   return 0;
}

  07070100000254000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmmemctl   07070100000255000081A40000000000000000000000016822550500004643000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmmemctl/COPYING   		    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.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    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.

  <signature of Ty Coon>, 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.
 07070100000256000081A40000000000000000000000016822550500000700000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmmemctl/Makefile  #!/usr/bin/make -f
##########################################################
# Copyright (C) 2007-2018, 2021, 2023 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
#
##########################################################

####
#### vmmemctl Makefile (for FreeBSD guest OS)
####

SRCS	:= os.c
SRCS	+= vmballoon.c
SRCS	+= backdoor_balloon.c
SRCS	+= kernelStubsBSD.c

.if $(MACHINE_ARCH) == "amd64"
SRCS    += backdoorGcc64.c
.elif $(MACHINE_ARCH) == "aarch64"
SRCS    += backdoorGcc64_arm64.c
.else
SRCS    += backdoorGcc32.c
.endif
KMOD	= vmmemctl
PROG    = ../$(KMOD).ko
MK_MAN  = no
KLDMOD	= t

# Don't print a warning that the object dir wasn't specified
NOOBJ   = 1
NO_OBJ  = 1

.ifdef OVT_SOURCE_DIR
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/include
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/backdoor
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/freebsd/shared
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/shared/vmmemctl
   CFLAGS += -isystem /usr/include
   VPATH  := $(OVT_SOURCE_DIR)/lib/backdoor
   VPATH  := $(VPATH):$(OVT_SOURCE_DIR)/modules/shared/vmmemctl
.else
   CFLAGS += -Ishared
.endif

.include <bsd.kmod.mk>

.ifndef VERBOSE
.SILENT:
.endif
07070100000257000081A400000000000000000000000168225505000001AE000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmmemctl/README    The files in this directory are the source files for the VMware
Memory Control driver.  The driver will be built automatically
by the VMware Tools install script.  If you wish to build the
driver by hand, run the following commands:

	make
        make install
        
from this directory.  The module 'vmmemctl.ko' will be built and
installed in /modules.

If you have any problems or questions, send mail to support@vmware.com
  07070100000258000081A400000000000000000000000168225505000017EB000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmmemctl/kernelStubsBSD.c  /*********************************************************
 * 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
 *
 *********************************************************/

/*
 * kernelStubsBSD.c --
 *
 *      Stub functions for use by miscellaneous VMware code when brought into
 *      the FreeBSD kernel.
 */


#include <sys/types.h>
#include <sys/param.h>
#include <machine/stdarg.h>
#include <sys/systm.h>
#include <sys/kernel.h>

#include "kernelStubs.h"

MALLOC_DEFINE(M_VMWARE_TEMP, "VMwareTemp", "VMware: Temporary Allocations");

void Log (const char *fmt, ...) __attribute__ ((alias ("Debug")));


/*
 *-----------------------------------------------------------------------------
 *
 * Debug --
 *
 *      Send a debugging message to the system log and/or console.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Debug(const char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   vprintf(fmt, ap);
   va_end(ap);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Panic --
 *
 *      Print a panic message & induce a kernel panic.
 *
 * Results:
 *      Does not return.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Panic(const char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   vprintf(fmt, ap);
   va_end(ap);

   panic(" ");
}


/*
 *-----------------------------------------------------------------------------
 *
 * Str_Strcpy --
 *
 *      Wrapper around strcpy that panics if the source is too long.
 *
 * Results:
 *      Returns a pointer to buf.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

char *
Str_Strcpy(char *buf,           // OUT: destination buffer
           const char *src,     // IN : source buffer
           size_t maxSize)      // IN : size of buf
{
   size_t srcLen = strlen(src);
   if (srcLen >= maxSize) {
      panic("%s:%d Buffer too small %p\n", __FILE__, __LINE__, buf);
   }
   return memcpy(buf, src, srcLen + 1);    // Extra byte = terminator
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Vsnprintf --
 *
 *      Compatibility wrapper for vsnprintf(1).
 *
 * Results:
 *
 *      int - number of bytes stored in 'str' (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: destination buffer
              size_t size,              // IN : size of str
              const char *format,       // IN : format for vsnprintf
              va_list arguments)        // IN : variadic args for vsnprintf
{
   int retval;

   retval = vsnprintf(str, size, format, arguments);
   if (retval >= size) {
      retval = -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 tmpArgs;

      bufSize *= 2;
      buf = realloc(buf, bufSize);

      if (!buf) {
         return NULL;
      }

      va_copy(tmpArgs, arguments);
      retval = Str_Vsnprintf(buf, bufSize, format, tmpArgs);
      va_end(tmpArgs);
   } 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;
}
 07070100000259000081A40000000000000000000000016822550500004E76000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/modules/freebsd/vmmemctl/os.c  /*********************************************************
 * Copyright (C) 2000,2014,2018-2021,2023 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
 *
 *********************************************************/

/*
 * os.c --
 *
 *      Wrappers for FreeBSD system functions required by "vmmemctl".
 */

/*
 * Compile-Time Options
 */

#define	OS_DISABLE_UNLOAD   0
#define	OS_DEBUG            1

/*
 * Includes
 */

#include "vm_basic_types.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/rwlock.h>
#include <sys/sysctl.h>

#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/pmap.h>
#include <vm/vm_page.h>
#include <vm/vm_object.h>
#include <vm/vm_param.h>
#include <sys/vmmeter.h>

#include <machine/stdarg.h>

#include "os.h"
#include "vmballoon.h"

/*
 * Types
 */

typedef struct {
   /* system structures */
#if __FreeBSD_version >= 1300067
   struct callout callout_handle;
#else
   struct callout_handle callout_handle;
#endif

   /* termination flag */
   volatile int stop;
} os_timer;

typedef struct {
   unsigned long size;       /* bitmap size in bytes */
   unsigned long *bitmap;    /* bitmap words */
   unsigned int  hint;       /* start searching from this word */
} os_pmap;

typedef struct {
   os_timer    timer;
   os_pmap     pmap;
   vm_object_t vmobject;     /* vm backing object */
} os_state;

MALLOC_DEFINE(M_VMMEMCTL, BALLOON_NAME, "vmmemctl metadata");

/*
 * FreeBSD specific MACROS
 */
#define VM_PAGE_LOCK(page) vm_page_lock(page);
#define VM_PAGE_UNLOCK(page) vm_page_unlock(page)

#define VM_OBJ_LOCK(object) VM_OBJECT_WLOCK(object)
#define VM_OBJ_UNLOCK(object) VM_OBJECT_WUNLOCK(object);

#define VM_SYS_PAGES vm_cnt.v_page_count

#define KVA_ALLOC(size) kva_alloc(size);
#define KVA_FREE(offset, size) kva_free(offset, size)

#define KMEM_ALLOC(size) kmem_malloc(size, M_WAITOK | M_ZERO)
#define KMEM_FREE(offset, size) kmem_free(offset, size)

/*
 * Globals
 */

static os_state global_state;

static void vmmemctl_init_sysctl(void);
static void vmmemctl_deinit_sysctl(void);


/*
 *-----------------------------------------------------------------------------
 *
 * OS_Malloc --
 *
 *      Allocates kernel memory.
 *
 * Results:
 *      On success: Pointer to allocated memory
 *      On failure: NULL
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void *
OS_Malloc(size_t size) // IN
{
   return malloc(size, M_VMMEMCTL, M_NOWAIT);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_Free --
 *
 *      Free allocated kernel memory.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
OS_Free(void *ptr,   // IN
        size_t size) // IN
{
   free(ptr, M_VMMEMCTL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_MemZero --
 *
 *      Fill a memory location with 0s.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
OS_MemZero(void *ptr,   // OUT
           size_t size) // IN
{
   bzero(ptr, size);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_MemCopy --
 *
 *      Copy a memory portion into another location.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
OS_MemCopy(void *dest,      // OUT
           const void *src, // IN
           size_t size)     // IN
{
   memcpy(dest, src, size);
}

/* find first zero bit */
static __inline__ unsigned long os_ffz(unsigned long word)
{
#ifdef __x86_64__
   __asm__("bsfq %1,%0"
           :"=r" (word)
           :"r" (~word));
#elif defined(__aarch64__)
   __asm__("rbit %0, %1; clz %0,%0"
           :"=r" (word)
           :"r" (~word));
#else
   __asm__("bsfl %1,%0"
           :"=r" (word)
           :"r" (~word));
#endif
   return word;
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageGetLimit --
 *
 *      Predict the maximum achievable balloon size.
 *
 * Results:
 *      Total memory pages.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

unsigned long
OS_ReservedPageGetLimit(void)
{
   return VM_SYS_PAGES;
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageGetPA --
 *
 *      Convert a page handle (of a physical page previously reserved with
 *      OS_ReservedPageAlloc()) to a pa.
 *
 * Results:
 *      The pa.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

PA64
OS_ReservedPageGetPA(PageHandle handle) // IN: A valid page handle
{
   return (((vm_page_t)handle)->phys_addr);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageGetHandle --
 *
 *      Convert a pa (of a physical page previously reserved with
 *      OS_ReservedPageAlloc()) to a page handle.
 *
 * Results:
 *      The page handle.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

PageHandle
OS_ReservedPageGetHandle(PA64 pa)     // IN
{
   return (PageHandle)PHYS_TO_VM_PAGE(pa);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_MapPageHandle --
 *
 *      Map a page handle into kernel address space, and return the
 *      mapping to that page handle.
 *
 * Results:
 *      The mapping.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Mapping
OS_MapPageHandle(PageHandle handle)     // IN
{
   vm_offset_t res = KVA_ALLOC(PAGE_SIZE);

   vm_page_t page = (vm_page_t)handle;

   if (!res) {
      return MAPPING_INVALID;
   }

   pmap_qenter(res, &page, 1);

   return (Mapping)res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_Mapping2Addr --
 *
 *      Return the address of a previously mapped page handle (with
 *      OS_MapPageHandle).
 *
 * Results:
 *      The mapping address.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void *
OS_Mapping2Addr(Mapping mapping)        // IN
{
   return (void *)mapping;
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_UnmapPage --
 *
 *      Unmap a previously mapped page handle.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
OS_UnmapPage(Mapping mapping)           // IN
{
   pmap_qremove((vm_offset_t)mapping, 1);
   KVA_FREE((vm_offset_t)mapping, PAGE_SIZE);
}


static void
os_pmap_alloc(os_pmap *p) // IN
{
   /* number of pages (div. 8) */
   p->size = (VM_SYS_PAGES + 7) / 8;

   /*
    * expand to nearest word boundary
    * XXX: bitmap can be greater than total number of pages in system
    */
   p->size = (p->size + sizeof(unsigned long) - 1) &
                         ~(sizeof(unsigned long) - 1);

   p->bitmap = (unsigned long *)KMEM_ALLOC(p->size);
}


static void
os_pmap_free(os_pmap *p) // IN
{
   KMEM_FREE((vm_offset_t)p->bitmap, p->size);
   p->size = 0;
   p->bitmap = NULL;
}


static void
os_pmap_init(os_pmap *p) // IN
{
   /* alloc bitmap for pages in system */
   os_pmap_alloc(p);
   if (!p->bitmap) {
      p->size = 0;
      p->bitmap = NULL;
      return;
   }

   /* clear bitmap */
   bzero(p->bitmap, p->size);
   p->hint = 0;
}


static vm_pindex_t
os_pmap_getindex(os_pmap *p) // IN
{
   int i;
   unsigned long bitidx, wordidx;

   /* start scanning from hint */
   wordidx = p->hint;

   /* scan bitmap for unset bit */
   for (i=0; i < p->size/sizeof (unsigned long); i++) {

      if (!p->bitmap[wordidx]) {
         p->bitmap[wordidx] = 1;
         p->hint = wordidx;
         return (wordidx * sizeof(unsigned long) * 8);
      }
      else if (p->bitmap[wordidx] != ~0UL) {

         /* find first zero bit */
         bitidx = os_ffz(p->bitmap[wordidx]);
         p->bitmap[wordidx] |= (1<<bitidx);
         p->hint = wordidx;
         return (wordidx * sizeof(unsigned long) * 8) + bitidx;
      }

      wordidx = (wordidx+1) % (p->size/sizeof (unsigned long));
   }

   /* failed */
   return (vm_pindex_t)-1;
}


static void
os_pmap_putindex(os_pmap *p,         // IN
                 vm_pindex_t pindex) // IN
{
   /* unset bit */
   p->bitmap[pindex / (8*sizeof(unsigned long))] &=
                             ~(1<<(pindex % (8*sizeof(unsigned long))));
}


static void
os_kmem_free(vm_page_t page) // IN
{
   os_state *state = &global_state;
   os_pmap *pmap = &state->pmap;

   VM_OBJ_LOCK(state->vmobject);
   if (vm_page_lookup(state->vmobject, page->pindex)) {
      os_pmap_putindex(pmap, page->pindex);
      VM_PAGE_LOCK(page);
      vm_page_free(page);
      VM_PAGE_UNLOCK(page);
   }
   VM_OBJ_UNLOCK(state->vmobject);
}


static vm_page_t
os_kmem_alloc(int alloc_normal_failed) // IN
{
   vm_page_t page;
   vm_pindex_t pindex;
   os_state *state = &global_state;
   os_pmap *pmap = &state->pmap;

   VM_OBJ_LOCK(state->vmobject);

   pindex = os_pmap_getindex(pmap);
   if (pindex == (vm_pindex_t)-1) {
      VM_OBJ_UNLOCK(state->vmobject);
      return NULL;
   }

   /*
    * BSD's page allocator does not support flags that are similar to
    * KM_NOSLEEP and KM_SLEEP in Solaris. The main page allocation function
    * vm_page_alloc() does not sleep ever. It just returns NULL when it
    * cannot find a free (or cached-and-clean) page. Therefore, we use
    * VM_ALLOC_NORMAL and VM_ALLOC_SYSTEM loosely to mean KM_NOSLEEP
    * and KM_SLEEP respectively.
    */
   if (alloc_normal_failed) {
      page = vm_page_alloc(state->vmobject, pindex, VM_ALLOC_SYSTEM);
   } else {
      page = vm_page_alloc(state->vmobject, pindex, VM_ALLOC_NORMAL);
   }

   if (!page) {
      os_pmap_putindex(pmap, pindex);
   }
   VM_OBJ_UNLOCK(state->vmobject);

   return page;
}


static void
os_balloonobject_delete(void)
{
   vm_object_deallocate(global_state.vmobject);
}


static void
os_balloonobject_create(void)
{
   global_state.vmobject = vm_object_allocate(OBJT_DEFAULT,
                  OFF_TO_IDX(VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS));
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageAlloc --
 *
 *      Reserve a physical page for the exclusive use of this driver.
 *
 * Results:
 *      On success: A valid page handle that can be passed to OS_ReservedPageGetPA()
 *                  or OS_ReservedPageFree().
 *      On failure: PAGE_HANDLE_INVALID
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

PageHandle
OS_ReservedPageAlloc(int canSleep,    // IN
                     int isLargePage) // IN
{
   vm_page_t page;

   ASSERT(!isLargePage);

   page = os_kmem_alloc(canSleep);
   if (page == NULL) {
      return PAGE_HANDLE_INVALID;
   }

   return (PageHandle)page;
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageFree --
 *
 *      Unreserve a physical page previously reserved with OS_ReservedPageAlloc().
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
OS_ReservedPageFree(PageHandle handle, // IN: A valid page handle
                    int isLargePage)   // IN
{
   ASSERT(!isLargePage);

   os_kmem_free((vm_page_t)handle);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_Yield --
 *
 *      Yield the CPU, if needed.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
OS_Yield(void)
{
   /* Do nothing. */
}


/*
 * vmmemctl_poll -
 *
 *      Calls Balloon_QueryAndExecute() to perform ballooning tasks and
 *      then reschedules itself to be executed in BALLOON_POLL_PERIOD
 *      seconds.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 */

static void
vmmemctl_poll(void *data) // IN
{
   os_timer *t = data;

   if (!t->stop) {
      /* invoke registered handler, rearm timer */
      Balloon_QueryAndExecute();
#if __FreeBSD_version >= 1300067
      callout_reset(&t->callout_handle, BALLOON_POLL_PERIOD * hz, vmmemctl_poll,
          t);
#else
      t->callout_handle = timeout(vmmemctl_poll, t, BALLOON_POLL_PERIOD * hz);
#endif
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * vmmemctl_init --
 *
 *      Called at driver startup, initializes the balloon state and structures.
 *
 * Results:
 *      On success: 0
 *      On failure: standard error code
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static int
vmmemctl_init(void)
{
   os_state *state = &global_state;
   os_timer *t = &state->timer;
   os_pmap *pmap = &state->pmap;

   if (!Balloon_Init(BALLOON_GUEST_BSD)) {
      return EIO;
   }

   /* initialize timer state */
#if __FreeBSD_version >= 1300067
   callout_init(&state->timer.callout_handle, 0);
#else
   callout_handle_init(&state->timer.callout_handle);
#endif

   os_pmap_init(pmap);
   os_balloonobject_create();

   /* Set up and start polling */
   t->stop = FALSE;
#if __FreeBSD_version >= 1300067
   callout_reset(&t->callout_handle, BALLOON_POLL_PERIOD * hz, vmmemctl_poll,
       t);
#else
   t->callout_handle = timeout(vmmemctl_poll, t, BALLOON_POLL_PERIOD * hz);
#endif

   vmmemctl_init_sysctl();

   /* log device load */
   printf(BALLOON_NAME_VERBOSE " initialized\n");
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * vmmemctl_cleanup --
 *
 *      Called when the driver is terminating, cleanup initialized structures.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
vmmemctl_cleanup(void)
{
   os_state *state = &global_state;
   os_timer *t = &state->timer;
   os_pmap *pmap = &state->pmap;

   vmmemctl_deinit_sysctl();

   Balloon_Cleanup();

   /* Stop polling */
   t->stop = TRUE;
#if __FreeBSD_version >= 1300067
   callout_drain(&t->callout_handle);
#else
   untimeout(vmmemctl_poll, t, t->callout_handle);
#endif

   os_balloonobject_delete();
   os_pmap_free(pmap);

   /* log device unload */
   printf(BALLOON_NAME_VERBOSE " unloaded\n");
}


/*
 * Module Load/Unload Operations
 */

static int
vmmemctl_load(module_t mod, // IN: Unused
              int cmd,      // IN
              void *arg)    // IN: Unused
{
   int err = 0;

   switch (cmd) {
   case MOD_LOAD:
      err = vmmemctl_init();
      break;

    case MOD_UNLOAD:
       if (OS_DISABLE_UNLOAD) {
          /* prevent module unload */
          err = EBUSY;
       } else {
          vmmemctl_cleanup();
       }
       break;

   default:
      err = EINVAL;
      break;
   }

   return err;
}


static struct sysctl_oid *oid;

/*
 *-----------------------------------------------------------------------------
 *
 * vmmemctl_sysctl --
 *
 *      This gets called to provide the sysctl output when requested.
 *
 * Results:
 *      Error, if any
 *
 * Side effects:
 *      Data is written into user-provided buffer
 *
 *-----------------------------------------------------------------------------
 */

static int
vmmemctl_sysctl(SYSCTL_HANDLER_ARGS)
{
   char buf[PAGE_SIZE];
   size_t len = 0;
   const BalloonStats *stats = Balloon_GetStats();

   /* format size info */
   len += snprintf(buf + len, sizeof(buf) - len,
                   "target:             %8"FMT64"u pages\n"
                   "current:            %8"FMT64"u pages\n",
                   stats->nPagesTarget,
                   stats->nPages);

   /* format rate info */
   len += snprintf(buf + len, sizeof(buf) - len,
                   "rateNoSleepAlloc:   %8d pages/sec\n"
                   "rateSleepAlloc:     %8d pages/sec\n"
                   "rateFree:           %8d pages/sec\n",
                   stats->rateNoSleepAlloc,
                   stats->rateAlloc,
                   stats->rateFree);

   len += snprintf(buf + len, sizeof(buf) - len,
                   "\n"
                   "timer:              %8u\n"
                   "start:              %8u (%4u failed)\n"
                   "guestType:          %8u (%4u failed)\n"
                   "lock:               %8u (%4u failed)\n"
                   "unlock:             %8u (%4u failed)\n"
                   "target:             %8u (%4u failed)\n"
                   "primNoSleepAlloc:   %8u (%4u failed)\n"
                   "primCanSleepAlloc:  %8u (%4u failed)\n"
                   "primFree:           %8u\n"
                   "errAlloc:           %8u\n"
                   "errFree:            %8u\n",
                   stats->timer,
                   stats->start, stats->startFail,
                   stats->guestType, stats->guestTypeFail,
                   stats->lock[FALSE],  stats->lockFail[FALSE],
                   stats->unlock[FALSE], stats->unlockFail[FALSE],
                   stats->target, stats->targetFail,
                   stats->primAlloc[BALLOON_PAGE_ALLOC_NOSLEEP],
                   stats->primAllocFail[BALLOON_PAGE_ALLOC_NOSLEEP],
                   stats->primAlloc[BALLOON_PAGE_ALLOC_CANSLEEP],
                   stats->primAllocFail[BALLOON_PAGE_ALLOC_CANSLEEP],
                   stats->primFree[FALSE],
                   stats->primErrorPageAlloc[FALSE],
                   stats->primErrorPageFree[FALSE]);

   return SYSCTL_OUT(req, buf, len + 1);
}


/*
 *-----------------------------------------------------------------------------
 *
 * vmmemctl_init_sysctl --
 *
 *      Init out sysctl, to be used for providing driver state.
 *
 * Results:
 *      none
 *
 * Side effects:
 *      Sn OID for a sysctl is registered
 *
 *-----------------------------------------------------------------------------
 */

static void
vmmemctl_init_sysctl(void)
{
   oid =  SYSCTL_ADD_OID(NULL, SYSCTL_STATIC_CHILDREN(_vm), OID_AUTO,
                         BALLOON_NAME, CTLTYPE_STRING | CTLFLAG_RD,
                         0, 0, vmmemctl_sysctl, "A",
                         BALLOON_NAME_VERBOSE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * vmmemctl_deinit_sysctl --
 *
 *      Undo vmmemctl_init_sysctl(). Remove the sysctl we installed.
 *
 * Results:
 *      none
 *
 * Side effects:
 *      Sn OID for a sysctl is unregistered
 *
 *-----------------------------------------------------------------------------
 */

static void
vmmemctl_deinit_sysctl(void)
{
   if (oid) {
      sysctl_remove_oid(oid,1,0);
   }
}

DEV_MODULE(vmmemctl, vmmemctl_load, NULL);

  0707010000025A000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/modules/shared 0707010000025B000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmblock 0707010000025C000081A40000000000000000000000016822550500004774000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmblock/block.c /*********************************************************
 * Copyright (C) 2006,2017-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * block.c --
 *
 *      Blocking operation implementions for the vmblock driver.
 */

/* os.h includes necessary OS-specific headers. */
#include "os.h"

#if defined(vmblock_fuse)
#elif defined(__linux__)
# include "vmblockInt.h"
#elif defined(sun)
# include "module.h"
#elif defined(__FreeBSD__)
# include "vmblock_k.h"
#endif
#include "block.h"
#include "dbllnklst.h"

typedef struct BlockInfo {
   DblLnkLst_Links links;
   os_atomic_t refcount;
   os_blocker_id_t blocker;
   os_completion_t completion;
   os_completion_t notification;
   char filename[OS_PATH_MAX];
} BlockInfo;


/* XXX: Is it worth turning this into a hash table? */
static DblLnkLst_Links blockedFiles;
static os_rwlock_t blockedFilesLock;
static os_kmem_cache_t *blockInfoCache;


/*
 *----------------------------------------------------------------------------
 *
 * BlockInit --
 *
 *    Initializes blocking portion of module.
 *
 * Results:
 *    Zero on success, error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
BlockInit(void)
{
   ASSERT(!blockInfoCache);

   blockInfoCache = os_kmem_cache_create("blockInfoCache",
                                         sizeof (BlockInfo),
                                         0,
                                         NULL);
   if (!blockInfoCache) {
      return OS_ENOMEM;
   }

   DblLnkLst_Init(&blockedFiles);
   os_rwlock_init(&blockedFilesLock);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockCleanup --
 *
 *    Cleans up the blocking portion of the module.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
BlockCleanup(void)
{
   ASSERT(blockInfoCache);
   ASSERT(!DblLnkLst_IsLinked(&blockedFiles));

   os_rwlock_destroy(&blockedFilesLock);
   os_kmem_cache_destroy(blockInfoCache);
}


/*
 *----------------------------------------------------------------------------
 *
 * AllocBlock --
 *
 *    Allocates and initializes a new block structure.
 *
 * Results:
 *    Pointer to the struct on success, NULL on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static BlockInfo *
AllocBlock(os_kmem_cache_t *cache,        // IN: cache to allocate from
           const char *filename,          // IN: filname of block
           const os_blocker_id_t blocker) // IN: blocker id
{
   BlockInfo *block;
   size_t ret;

   /* Initialize this file's block structure. */
   block = os_kmem_cache_alloc(blockInfoCache);
   if (!block) {
      return NULL;
   }

   ret = strlcpy(block->filename, filename, sizeof block->filename);
   if (ret >= sizeof block->filename) {
      Warning("BlockAddFileBlock: filename is too large\n");
      os_kmem_cache_free(blockInfoCache, block);
      return NULL;
   }

   DblLnkLst_Init(&block->links);
   os_atomic_set(&block->refcount, 1);
   os_completion_init(&block->completion);
   os_completion_init(&block->notification);
   block->blocker = blocker;

   return block;
}


/*
 *----------------------------------------------------------------------------
 *
 * FreeBlock --
 *
 *    Frees the provided block structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
FreeBlock(os_kmem_cache_t *cache,       // IN: cache block was allocated from
          BlockInfo *block)             // IN: block to free
{
   ASSERT(cache);
   ASSERT(block);

   if (DblLnkLst_IsLinked(&block->links)) {
      Warning("Block on file [%s] is still in the list, "
              "not freeing, leaking memory\n", block->filename);
      return;
   }

   os_completion_destroy(&block->completion);
   os_completion_destroy(&block->notification);
   os_kmem_cache_free(cache, block);
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockGrabReference --
 *
 *    Increments reference count in the provided block structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static BlockInfo *
BlockGrabReference(BlockInfo *block)
{
   ASSERT(block);

   os_atomic_inc(&block->refcount);

   return block;
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockDropReference --
 *
 *    Increments reference count in the provided block structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    When reference count reaches 0 disposes of the block structure by
 *    calling FreeBlock().
 *
 *----------------------------------------------------------------------------
 */

static void
BlockDropReference(BlockInfo *block)
{
   if (os_atomic_dec_and_test(&block->refcount)) {
      LOG(4, "Dropped last reference for block on [%s]\n", block->filename);
      FreeBlock(blockInfoCache, block);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * GetBlock --
 *
 *    Searches for a block on the provided filename by the provided blocker.
 *    If blocker is NULL, it is ignored and any matching filename is returned.
 *
 *    Note that this assumes the proper locking has been done on the data
 *    structure holding the blocked files.
 *
 * Results:
 *    A pointer to the corresponding BlockInfo if found, NULL otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static BlockInfo *
GetBlock(const char *filename,          // IN: file to find block for
         const os_blocker_id_t blocker) // IN: blocker associated with this block
{
   struct DblLnkLst_Links *curr;

   /*
    * On FreeBSD we have a mechanism to assert (but not simply check)
    * that a lock is held. Since semantic is different (panic that
    * happens if assertion fails can not be suppressed) we are using
    * different name.
    */
#ifdef os_assert_rwlock_held
   os_assert_rwlock_held(&blockedFilesLock);
#else
   ASSERT(os_rwlock_held(&blockedFilesLock));
#endif

   DblLnkLst_ForEach(curr, &blockedFiles) {
      BlockInfo *currBlock = DblLnkLst_Container(curr, BlockInfo, links);
      if ((blocker == OS_UNKNOWN_BLOCKER || currBlock->blocker == blocker) &&
          strcmp(currBlock->filename, filename) == 0) {
         return currBlock;
      }
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockDoRemoveBlock --
 *
 *    Removes given block from the block list and notifies waiters that block
 *    is gone.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Block structure will be freed if there were no waiters.
 *
 *----------------------------------------------------------------------------
 */

static void
BlockDoRemoveBlock(BlockInfo *block)
{
   ASSERT(block);

   DblLnkLst_Unlink1(&block->links);

   /* Wake up waiters, if any */
   LOG(4, "Completing block on [%s] (%d waiters)\n",
       block->filename, os_atomic_read(&block->refcount) - 1);
   os_complete_all(&block->completion);
   os_complete_all(&block->notification);

   /* Now drop our reference */
   BlockDropReference(block);
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockAddFileBlock --
 *
 *    Adds a block for the provided filename.  filename should be the name of
 *    the actual file being blocked, not the name within our namespace.  The
 *    provided blocker ID should uniquely identify this blocker.
 *
 *    All calls to BlockWaitOnFile() with the same filename will not return
 *    until BlockRemoveFileBlock() is called.
 *
 *    Note that this function assumes a block on filename does not already
 *    exist.
 *
 * Results:
 *    Zero on success, error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
BlockAddFileBlock(const char *filename,           // IN: name of file to block
                  const os_blocker_id_t blocker)  // IN: blocker adding the block
{
   BlockInfo *block;
   int retval;

   ASSERT(filename);

   os_write_lock(&blockedFilesLock);

   if (GetBlock(filename, OS_UNKNOWN_BLOCKER)) {
      retval = OS_EEXIST;
      goto out;
   }

   block = AllocBlock(blockInfoCache, filename, blocker);
   if (!block) {
      Warning("BlockAddFileBlock: out of memory\n");
      retval = OS_ENOMEM;
      goto out;
   }

   DblLnkLst_LinkLast(&blockedFiles, &block->links);
   LOG(4, "added block for [%s]\n", filename);
   retval = 0;

out:
   os_write_unlock(&blockedFilesLock);
   return retval;
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockRemoveFileBlock --
 *
 *    Removes the provided file block and wakes up any threads waiting within
 *    BlockWaitOnFile().  Note that only the blocker that added a block can
 *    remove it.
 *
 * Results:
 *    Zero on success, error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
BlockRemoveFileBlock(const char *filename,          // IN: block to remove
                     const os_blocker_id_t blocker) // IN: blocker removing this block
{
   BlockInfo *block;
   int retval;

   ASSERT(filename);

   os_write_lock(&blockedFilesLock);

   block = GetBlock(filename, blocker);
   if (!block) {
      retval = OS_ENOENT;
      goto out;
   }

   BlockDoRemoveBlock(block);
   retval = 0;

out:
   os_write_unlock(&blockedFilesLock);
   return retval;
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockRemoveAllBlocks --
 *
 *    Removes all blocks added by the provided blocker.
 *
 * Results:
 *    Returns the number of entries removed from the blocklist.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

unsigned int
BlockRemoveAllBlocks(const os_blocker_id_t blocker)  // IN: blocker to remove blocks for
{
   struct DblLnkLst_Links *curr;
   struct DblLnkLst_Links *tmp;
   unsigned int removed = 0;

   os_write_lock(&blockedFilesLock);

   DblLnkLst_ForEachSafe(curr, tmp, &blockedFiles) {
      BlockInfo *currBlock = DblLnkLst_Container(curr, BlockInfo, links);
      if (currBlock->blocker == blocker || blocker == OS_UNKNOWN_BLOCKER) {

         BlockDoRemoveBlock(currBlock);

         /*
          * We count only entries removed from the -list-, regardless of whether
          * or not other waiters exist.
          */
         ++removed;
      }
   }

   os_write_unlock(&blockedFilesLock);

   return removed;
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockWaitFileBlock --
 *
 *    The caller will be blocked until any other thread accesses the file
 *    specified by the filename or the block on the file is removed.
 *
 * Results:
 *    Zero on success, error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
BlockWaitFileBlock(const char *filename,          // IN: block to wait
                   const os_blocker_id_t blocker) // IN: blocker
{
   BlockInfo *block;
   int retval = 0;

   ASSERT(filename);

   os_write_lock(&blockedFilesLock);
   block = GetBlock(filename, blocker);
   os_write_unlock(&blockedFilesLock);

   if (!block) {
      retval = OS_ENOENT;
      return retval;
   }

   os_wait_for_completion(&block->notification);

   return retval;
}


/*
 *----------------------------------------------------------------------------
 *
 * BlockWaitOnFile --
 *
 *    Searches for a block on the provided filename.  If one exists, this
 *    function does not return until that block has been lifted; otherwise, it
 *    returns right away.
 *
 * Results:
 *    Zero on success, otherwise an appropriate system error if our sleep/
 *    block is interrupted.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
BlockWaitOnFile(const char *filename,   // IN: file to block on
                BlockHandle cookie)     // IN: previously found block
{
   BlockInfo *block = NULL;
   int error = 0;

   ASSERT(filename);

   /*
    * Caller may have used BlockLookup to conditionally search for a
    * block before actually going to sleep.  (This allows the caller to
    * do a little housekeeping, such as releasing vnode locks, before
    * blocking here.)
    */
   if (cookie == NULL) {
      os_read_lock(&blockedFilesLock);
      block = GetBlock(filename, OS_UNKNOWN_BLOCKER);
      if (block) {
         BlockGrabReference(block);
      }
      os_read_unlock(&blockedFilesLock);

      if (!block) {
         /* This file is not blocked, just return */
         return 0;
      }
   } else {
      /*
       * Note that the "cookie's" reference count was incremented when it
       * was fetched via BlockLookup, so this is completely safe.  (We'll
       * decrement it below.)
       */
      block = cookie;
   }

   // Call the callback
   os_complete_all(&block->notification);

   LOG(4, "(%"OS_FMTTID") Waiting for completion on [%s]\n", os_threadid, filename);
   error = os_wait_for_completion(&block->completion);
   LOG(4, "(%"OS_FMTTID") Wokeup from block on [%s]\n", os_threadid, filename);

   BlockDropReference(block);

   return error;
}


/*
 *-----------------------------------------------------------------------------
 *
 * BlockLookup --
 *
 *      VFS-exported function for searching for blocks.
 *
 * Results:
 *      Opaque pointer to a blockInfo if a block is found, NULL otherwise.
 *
 * Side effects:
 *      Located blockInfo, if any, has an incremented reference count.
 *
 *-----------------------------------------------------------------------------
 */

BlockHandle
BlockLookup(const char *filename,               // IN: pathname to test for
                                                //     blocking
            const os_blocker_id_t blocker)      // IN: specific blocker to
                                                //     search for
{
   BlockInfo *block;

   os_read_lock(&blockedFilesLock);

   block = GetBlock(filename, blocker);
   if (block) {
      BlockGrabReference(block);
   }

   os_read_unlock(&blockedFilesLock);

   return block;
}


#ifdef VMX86_DEVEL
/*
 *----------------------------------------------------------------------------
 *
 * BlockListFileBlocks --
 *
 *    Lists all the current file blocks.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
BlockListFileBlocks(void)
{
   DblLnkLst_Links *curr;
   int count = 0;

   os_read_lock(&blockedFilesLock);

   DblLnkLst_ForEach(curr, &blockedFiles) {
      BlockInfo *currBlock = DblLnkLst_Container(curr, BlockInfo, links);
      LOG(1, "BlockListFileBlocks: (%d) Filename: [%s], Blocker: [%p]\n",
          count++, currBlock->filename, currBlock->blocker);
   }

   os_read_unlock(&blockedFilesLock);

   if (!count) {
      LOG(1, "BlockListFileBlocks: No blocks currently exist.\n");
   }
}
#endif

0707010000025D000081A40000000000000000000000016822550500000E29000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmblock/block.h /*********************************************************
 * Copyright (C) 2006-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * block.h --
 *
 *   Blocking operations for the vmblock driver.
 */

#ifndef __BLOCK_H__
#define __BLOCK_H__

#include "os.h"

typedef struct BlockInfo * BlockHandle;

/*
 * Global functions
 */

int BlockInit(void);
void BlockCleanup(void);
int BlockAddFileBlock(const char *filename, const os_blocker_id_t blocker);
int BlockRemoveFileBlock(const char *filename, const os_blocker_id_t blocker);
unsigned int BlockRemoveAllBlocks(const os_blocker_id_t blocker);
int BlockWaitFileBlock(const char *filename, const os_blocker_id_t blocker);
int BlockWaitOnFile(const char *filename, BlockHandle cookie);
BlockHandle BlockLookup(const char *filename, const os_blocker_id_t blocker);
#ifdef VMX86_DEVEL
void BlockListFileBlocks(void);
#endif

#endif /* __BLOCK_H__ */
   0707010000025E000081A40000000000000000000000016822550500000E41000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmblock/stubs.c /*********************************************************
 * 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 Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * stubs.c --
 *
 *      Common stubs.
 */

#if defined(__FreeBSD__)
#include <sys/cdefs.h>
#include <machine/stdarg.h>
#else
#include <stdarg.h>
#endif

#include "os.h"
#include "vm_assert.h"

/*
 *----------------------------------------------------------------------------
 *
 * Panic --
 *
 *    Panic implementation.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
Panic(const char *fmt, ...)
{
   va_list args;

   va_start(args, fmt);
   os_panic(fmt, args);
   va_end(args);

   /*
    * Solaris's vcmn_err() is not marked as NORETURN and thus generates a
    * warning. I'd use NOT_REACHED(), as recommended, except that we are
    * already in Panic().
    */
   INFINITE_LOOP();
}
   0707010000025F000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl    07070100000260000081A400000000000000000000000168225505000033FD000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/backdoor_balloon.c /*********************************************************
 * Copyright (C) 2012,2014,2018-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/
#include "backdoor_balloon.h"
#include "backdoor.h"
#include "balloon_def.h"
#include "os.h"

/*
 *----------------------------------------------------------------------
 *
 * BackdoorCmd --
 *
 *      Do the balloon hypercall to the vmkernel.
 *
 * Results:
 *      Hypercall status will be returned and out will be filled
 *      if it's not NULL, either by bx or cx depending on the command.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
BackdoorCmd(uint16 cmd,     // IN
            uint64 arg1,    // IN
            uint32 arg2,    // IN
            uint64 *out,    // OUT
            int *resetFlag) // OUT
{
   Backdoor_proto bp;
   int status;

   /* prepare backdoor args */
   bp.in.cx.halfs.low = cmd;
   bp.in.size = (size_t)arg1;
   ASSERT(bp.in.size == arg1);
   bp.in.si.word = arg2;

   /* invoke backdoor */
   bp.in.ax.word = BALLOON_BDOOR_MAGIC;
   bp.in.dx.halfs.low = BALLOON_BDOOR_PORT;
   Backdoor_InOut(&bp);

   status = bp.out.ax.word;
   /* set flag if reset requested */
   if (status == BALLOON_ERROR_RESET) {
      *resetFlag = 1;
   }

   if (out) {
#ifdef VM_X86_64
      if (cmd == BALLOON_BDOOR_CMD_START) {
         *out = bp.out.cx.quad;
      } else {
         *out = bp.out.bx.quad;
      }
#else
      if (cmd == BALLOON_BDOOR_CMD_START) {
         *out = bp.out.cx.word;
      } else {
         *out = bp.out.bx.word;
      }
#endif
   }

   return status;
}


/*
 *----------------------------------------------------------------------
 *
 * Backdoor_MonitorStart --
 *
 *      Attempts to contact monitor via backdoor to begin operation.
 *
 * Results:
 *      Returns BALLOON_SUCCESS if successful, otherwise error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Backdoor_MonitorStart(Balloon *b,               // IN/OUT
                      uint32 protoVersion)      // IN
{
   uint64 capabilities;
   int status = BackdoorCmd(BALLOON_BDOOR_CMD_START, protoVersion, 0,
                            &capabilities, &b->resetFlag);
   /*
    * If return code is BALLOON_SUCCESS_WITH_CAPABILITY, then ESX is
    * sending the common capabilities supported by the monitor and the
    * guest in cx.
    */
   if (status == BALLOON_SUCCESS_WITH_CAPABILITIES) {
      b->hypervisorCapabilities = capabilities;
      status = BALLOON_SUCCESS;
   } else if (status == BALLOON_SUCCESS) {
      b->hypervisorCapabilities = BALLOON_BASIC_CMDS;
   }

   /* update stats */
   STATS_INC(b->stats.start);
   if (status != BALLOON_SUCCESS) {
      STATS_INC(b->stats.startFail);
   }

   return status;
}


/*
 *----------------------------------------------------------------------
 *
 * Backdoor_MonitorGuestType --
 *
 *      Attempts to contact monitor and report guest OS identity.
 *
 * Results:
 *      Returns BALLOON_SUCCESS if successful, otherwise error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Backdoor_MonitorGuestType(Balloon *b) // IN/OUT
{
   int status = BackdoorCmd(BALLOON_BDOOR_CMD_GUEST_ID, b->guestType, 0,
                            NULL, &b->resetFlag);

   /* update stats */
   STATS_INC(b->stats.guestType);
   if (status != BALLOON_SUCCESS) {
      STATS_INC(b->stats.guestTypeFail);
   }

   return status;
}


static Bool
BackdoorHasCapability(Balloon *b,
                      uint32 capability)
{
   return (b->hypervisorCapabilities & capability) == capability;
}


/*
 *----------------------------------------------------------------------
 *
 * Backdoor_MonitorGetTarget --
 *
 *      Attempts to contact monitor via backdoor to obtain desired
 *      balloon size.
 *
 *      Predicts the maximum achievable balloon size and sends it
 *      to vmm => vmkernel via vEbx register.
 *
 *      OS_ReservedPageGetLimit() returns either predicted max balloon
 *      pages or BALLOON_MAX_SIZE_USE_CONFIG. In the later scenario,
 *      vmkernel uses global config options for determining a guest's max
 *      balloon size. Note that older vmballoon drivers set vEbx to
 *      BALLOON_MAX_SIZE_USE_CONFIG, i.e., value 0 (zero). So vmkernel
 *      will fallback to config-based max balloon size estimation.
 *
 * Results:
 *      If successful, sets "target" to value obtained from monitor,
 *      and returns BALLOON_SUCCESS. Otherwise returns error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Backdoor_MonitorGetTarget(Balloon *b,     // IN/OUT
                          uint64 *target) // OUT
{
   uint64 limit;
   int status;

   limit = OS_ReservedPageGetLimit();

   if (!BackdoorHasCapability(b, BALLOON_64_BIT_TARGET)) {
      if (limit != (uint32)limit) {
         limit = (uint32)limit;
      }
   }

   status = BackdoorCmd(BALLOON_BDOOR_CMD_TARGET, limit, 0, target,
                        &b->resetFlag);

   if (target != NULL && !BackdoorHasCapability(b, BALLOON_64_BIT_TARGET)) {
      *target = MIN(MAX_UINT32, *target);
   }

   /* update stats */
   STATS_INC(b->stats.target);
   if (status != BALLOON_SUCCESS) {
      STATS_INC(b->stats.targetFail);
   }

   return status;
}


/*
 *----------------------------------------------------------------------
 *
 * Backdoor_MonitorLockPage --
 *
 *      Attempts to contact monitor and add PPN corresponding to
 *      the page handle to set of "balloon locked" pages.
 *      If the current protocol support batching, it will balloon all
 *      PPNs listed in the batch page.
 *
 * Results:
 *      Returns BALLOON_SUCCESS if successful, otherwise error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Backdoor_MonitorLockPage(Balloon *b,     // IN/OUT
                         PPN64 ppn,      // IN
                         uint64 *target) // OUT
{
   int status;

   if (!BackdoorHasCapability(b, BALLOON_64_BIT_TARGET)) {
      uint32 ppn32 = (uint32)ppn;
      /* Ensure PPN fits in 32-bits, i.e. guest memory is limited to 16TB. */
      if (ppn32 != ppn) {
         return BALLOON_ERROR_PPN_INVALID;
      }
   }

   status = BackdoorCmd(BALLOON_BDOOR_CMD_LOCK, ppn, 0, target,
                        &b->resetFlag);
   if (target != NULL && !BackdoorHasCapability(b, BALLOON_64_BIT_TARGET)) {
      *target = MIN(MAX_UINT32, *target);
   }

   /* update stats */
   STATS_INC(b->stats.lock[FALSE]);
   if (status != BALLOON_SUCCESS) {
      STATS_INC(b->stats.lockFail[FALSE]);
   }

   return status;
}

/*
 *----------------------------------------------------------------------
 *
 * Backdoor_MonitorUnlockPage --
 *
 *      Attempts to contact monitor and remove PPN corresponding to
 *      the page handle from set of "balloon locked" pages.
 *      If the current protocol support batching, it will remove all
 *      the PPNs listed in the batch page.
 *
 * Results:
 *      Returns BALLOON_SUCCESS if successful, otherwise error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Backdoor_MonitorUnlockPage(Balloon *b,     // IN/OUT
                           PPN64 ppn,      // IN
                           uint64 *target) // OUT
{
   int status;

   if (!BackdoorHasCapability(b, BALLOON_64_BIT_TARGET)) {
      uint32 ppn32 = (uint32)ppn;

      /* Ensure PPN fits in 32-bits, i.e. guest memory is limited to 16TB. */
      if (ppn32 != ppn) {
         return BALLOON_ERROR_PPN_INVALID;
      }
   }

   status = BackdoorCmd(BALLOON_BDOOR_CMD_UNLOCK, ppn, 0, target,
                        &b->resetFlag);
   if (target != NULL && !BackdoorHasCapability(b, BALLOON_64_BIT_TARGET)) {
      *target = MIN(MAX_UINT32, *target);
   }

   /* update stats */
   STATS_INC(b->stats.unlock[FALSE]);
   if (status != BALLOON_SUCCESS) {
      STATS_INC(b->stats.unlockFail[FALSE]);
   }

   return status;
}

/*
 *----------------------------------------------------------------------
 *
 * Backdoor_MonitorLockPagesBatched --
 *
 *      Balloon all PPNs listed in the batch page.
 *
 * Results:
 *      Returns BALLOON_SUCCESS if successful, otherwise error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Backdoor_MonitorLockPagesBatched(Balloon *b,      // IN/OUT
                                 PPN64 ppn,       // IN
                                 uint32 nPages,   // IN
                                 int isLargePage, // IN
                                 uint64 *target)  // OUT
{
   int status;
   uint16 cmd;

   if (isLargePage) {
      cmd = BALLOON_BDOOR_CMD_BATCHED_2M_LOCK;
   } else {
      cmd = BALLOON_BDOOR_CMD_BATCHED_LOCK;
   }

   status = BackdoorCmd(cmd, (size_t)ppn, nPages, target, &b->resetFlag);
   if (target != NULL && !BackdoorHasCapability(b, BALLOON_64_BIT_TARGET)) {
      *target = MIN(MAX_UINT32, *target);
   }

   /* update stats */
   STATS_INC(b->stats.lock[isLargePage]);
   if (status != BALLOON_SUCCESS) {
      STATS_INC(b->stats.lockFail[isLargePage]);
   }

   return status;
}

/*
 *----------------------------------------------------------------------
 *
 * Backdoor_MonitorUnlockPagesBatched --
 *
 *      Unballoon all PPNs listed in the batch page.
 *
 * Results:
 *      Returns BALLOON_SUCCESS if successful, otherwise error code.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
Backdoor_MonitorUnlockPagesBatched(Balloon *b,      // IN/OUT
                                   PPN64 ppn,       // IN
                                   uint32 nPages,   // IN
                                   int isLargePage, // IN
                                   uint64 *target)  // OUT
{
   int status;
   uint16 cmd;

   if (isLargePage) {
      cmd = BALLOON_BDOOR_CMD_BATCHED_2M_UNLOCK;
   } else {
      cmd = BALLOON_BDOOR_CMD_BATCHED_UNLOCK;
   }

   status = BackdoorCmd(cmd, (size_t)ppn, nPages, target, &b->resetFlag);
   if (target != NULL && !BackdoorHasCapability(b, BALLOON_64_BIT_TARGET)) {
      *target = MIN(MAX_UINT32, *target);
   }

   /* update stats */
   STATS_INC(b->stats.unlock[isLargePage]);
   if (status != BALLOON_SUCCESS) {
      STATS_INC(b->stats.unlockFail[isLargePage]);
   }

   return status;
}
   07070100000261000081A40000000000000000000000016822550500000ED6000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/backdoor_balloon.h /*********************************************************
 * Copyright (C) 2007,2014,2018-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * backdoor_balloon.h --
 *
 *    This file provides a wrapper for using the more generic backdoor library
 *    together with the vmballoon-specific backdoor.
 */

#ifndef _BACKDOOR_BALLOON_H_
#define _BACKDOOR_BALLOON_H_

#include "vmballoon.h"
#include "backdoor.h"
#include "balloon_def.h"

int Backdoor_MonitorStart(Balloon *b, uint32 protoVersion);
int Backdoor_MonitorGuestType(Balloon *b);
int Backdoor_MonitorGetTarget(Balloon *b, uint64 *target);
int Backdoor_MonitorLockPage(Balloon *b, PPN64 ppn, uint64 *target);
int Backdoor_MonitorUnlockPage(Balloon *b, PPN64 ppn, uint64 *target);
int Backdoor_MonitorLockPagesBatched(Balloon *b, PPN64 ppn, uint32 nPages,
                                     int isLargePages, uint64 *target);
int Backdoor_MonitorUnlockPagesBatched(Balloon *b, PPN64 ppn, uint32 nPages,
                                       int isLargePages, uint64 *target);

#endif /* _BACKDOOR_BALLOON_H_ */
  07070100000262000081A4000000000000000000000001682255050000123E000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/balloonInt.h   /*********************************************************
 * Copyright (C) 2012,2014-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/
#ifndef BALLOONINT_H_
# define BALLOONINT_H_

#include "balloon_def.h"

/*
 * Compile-Time Options
 */

#define BALLOON_NAME                    "vmmemctl"
#define BALLOON_NAME_VERBOSE            "VMware memory control driver"

// Capabilities for Windows are defined at run-tine (see nt/vmballoon.c)
#if defined __linux__ || (defined __APPLE__ && defined __LP64__)
#define BALLOON_CAPABILITIES    (BALLOON_BASIC_CMDS|BALLOON_BATCHED_CMDS|\
                                 BALLOON_BATCHED_2M_CMDS)
#elif defined __FreeBSD__ || (defined __APPLE__ && !defined __LP64__)
#define BALLOON_CAPABILITIES    (BALLOON_BASIC_CMDS|BALLOON_BATCHED_CMDS)
#else
#define BALLOON_CAPABILITIES    BALLOON_BASIC_CMDS
#endif

#define BALLOON_RATE_ADAPT      1

#define BALLOON_DEBUG           1
#define BALLOON_DEBUG_VERBOSE   0

#define BALLOON_POLL_PERIOD             1 /* sec */
#define BALLOON_NOSLEEP_ALLOC_MAX       16384

#define BALLOON_RATE_ALLOC_MIN          512
#define BALLOON_RATE_ALLOC_MAX          2048
#define BALLOON_RATE_ALLOC_INC          16

#define BALLOON_RATE_FREE_MIN           512
#define BALLOON_RATE_FREE_MAX           16384
#define BALLOON_RATE_FREE_INC           16

/*
 * Move it to bora/public/balloon_def.h later, if needed. Note that
 * BALLOON_PAGE_ALLOC_FAILURE is an internal error code used for
 * distinguishing page allocation failures from monitor-backdoor errors.
 * We use value 1000 because all monitor-backdoor error codes are < 1000.
 */
#define BALLOON_PAGE_ALLOC_FAILURE      1000

#define BALLOON_STATS

#ifdef	BALLOON_STATS
#define	STATS_INC(stat)	(stat)++
#define	STATS_DEC(stat)	(stat)--
#else
#define	STATS_INC(stat)
#define	STATS_DEC(stat)
#endif

#define PPN_2_PA(_ppn)  ((PPN64)(_ppn) << PAGE_SHIFT)
#define PA_2_PPN(_pa)   ((_pa) >> PAGE_SHIFT)

#endif /* !BALLOONINT_H_ */
  07070100000263000081A400000000000000000000000168225505000028FF000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/balloon_def.h  /*********************************************************
 * Copyright (C) 2000-2012,2014,2017-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * balloon_def.h --
 *
 *      Definitions for server "balloon" mechanism for reclaiming
 *      physical memory from a VM.
 */

#ifndef _BALLOON_DEF_H
#define _BALLOON_DEF_H

#define INCLUDE_ALLOW_VMX
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_MODULE
#include "includeCheck.h"

#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "vm_assert.h"

#if defined __cplusplus
extern "C" {
#endif


/*
 * constants
 */

/* backdoor port */
#define BALLOON_BDOOR_PORT              (0x5670)
#define BALLOON_BDOOR_MAGIC             (0x456c6d6f)

/*
 * Backdoor commands availability:
 *
 * +====================+======================+
 * |    CMD             | Capabilities         |
 * +--------------------+----------------------+
 * | START              | Always available (*) |
 * | TARGET             | Always available     |
 * | LOCK               | BASIC_CMDS           |
 * | UNLOCK             | BASIC_CMDS           |
 * | GUEST_ID           | Always available     |
 * | BATCHED_LOCK       | BATCHED_CMDS         |
 * | BATCHED_UNLOCK     | BATCHED_CMDS         |
 * | BATCHED_2M_LOCK    | BATCHED_2M_CMDS      |
 * | BATCHED_2M_UNLOCK  | BATCHED_2M_CMDS      |
 * | VMCI_DOORBELL_SET  | SIGNALED_WAKEUP_CMD  |
 * +====================+======================+
 *
 * (*) The START command has been slightly modified when more than the
 * basic commands are available: It returns
 * BALLOON_SUCCESS_WITH_CAPABILITIES with the available capabilities
 * stored in %ecx. Previously, a versioned protocol was used, and the
 * protocol that should be used was also returned in %ecx. Protocol
 * version 2 was the initial version and the only one shipped. Version 3
 * was temporary used internally but has caused several issue due to
 * protocol mismatch between monitor and guest.
 *
 */

/* backdoor command numbers */
#define BALLOON_BDOOR_CMD_START             (0)
#define BALLOON_BDOOR_CMD_TARGET            (1)
#define BALLOON_BDOOR_CMD_LOCK              (2)
#define BALLOON_BDOOR_CMD_UNLOCK            (3)
#define BALLOON_BDOOR_CMD_GUEST_ID          (4)
/* The command 5 was shortly used between 1881144 and 1901153. */
#define BALLOON_BDOOR_CMD_BATCHED_LOCK      (6)
#define BALLOON_BDOOR_CMD_BATCHED_UNLOCK    (7)
#define BALLOON_BDOOR_CMD_BATCHED_2M_LOCK   (8)
#define BALLOON_BDOOR_CMD_BATCHED_2M_UNLOCK (9)
#define BALLOON_BDOOR_CMD_VMCI_DOORBELL_SET (10)

/* balloon capabilities */
typedef enum {
   /*
    * Bit 0 is not used and shouldn't be used, due to issue with
    * protocol v3, to avoid ambiguity between protocol v3 and
    * capabilities, leave this bit as 0. That way, by masking guest
    * capabilities with monitor capabilities, bit 0 will always be set
    * to 0, and buggy v3 tool will automatically switch to unbatched
    * LOCK and UNLOCK.
    */
   BALLOON_BASIC_CMDS           = (1 << 1),
   BALLOON_BATCHED_CMDS         = (1 << 2),
   BALLOON_BATCHED_2M_CMDS      = (1 << 3),
   BALLOON_SIGNALED_WAKEUP_CMD  = (1 << 4),
   BALLOON_64_BIT_TARGET        = (1 << 5),
} BalloonCapabilities;

/* use config value for max balloon size */
#define BALLOON_MAX_SIZE_USE_CONFIG     (0)

/*
 * Guest identities
 *
 *      Note : all values should fit in 32 bits
 */
typedef enum {
   BALLOON_GUEST_UNKNOWN     = 0,
   BALLOON_GUEST_LINUX       = 1,
   BALLOON_GUEST_BSD         = 2,
   BALLOON_GUEST_WINDOWS_NT4 = 3,
   BALLOON_GUEST_WINDOWS_NT5 = 4,
   BALLOON_GUEST_SOLARIS     = 5,
   BALLOON_GUEST_MACOS       = 6,
   BALLOON_GUEST_FROBOS      = 7,
} BalloonGuest;

/* error codes */
#define BALLOON_SUCCESS                         (0)
#define BALLOON_FAILURE                        (-1)
#define BALLOON_ERROR_CMD_INVALID               (1)
#define BALLOON_ERROR_PPN_INVALID               (2)
#define BALLOON_ERROR_PPN_LOCKED                (3)
#define BALLOON_ERROR_PPN_UNLOCKED              (4)
#define BALLOON_ERROR_PPN_PINNED                (5)
#define BALLOON_ERROR_PPN_NOTNEEDED             (6)
#define BALLOON_ERROR_RESET                     (7)
#define BALLOON_ERROR_BUSY                      (8)

#define BALLOON_SUCCESS_WITH_CAPABILITIES       (0x03000000)

/*
 * BatchPage.
 */
#define BALLOON_BATCH_MAX_ENTRIES       (PAGE_SIZE / sizeof(PA64))

/*
 * We are using the fact that for 4k pages, the 12LSB are set to 0, so
 * we can use them and mask those bit when we need the real PA.
 *
 * +=============+==========+========+
 * |             |          |        |
 * | Page number | Reserved | Status |
 * |             |          |        |
 * +=============+==========+========+
 * 64  PAGE_SHIFT          6         0
 *
 * The reserved field should be set to 0.
 *
 */


#define BALLOON_BATCH_STATUS_MASK       MASK64(5)
#define BALLOON_BATCH_PAGE_MASK         (~MASK64(PAGE_SHIFT))

typedef struct BalloonBatchPage {
   PA64 entries[BALLOON_BATCH_MAX_ENTRIES];
} BalloonBatchPage;


/*
 *-----------------------------------------------------------------------------
 *
 * Balloon_BatchGetPA --
 *
 *      Get the page stored in the batch page at idx.
 *
 * Results:
 *      See above.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static INLINE PA64
Balloon_BatchGetPA(BalloonBatchPage *batchPage,         // IN
                   uint16 idx)                          // IN
{
   ASSERT(idx < BALLOON_BATCH_MAX_ENTRIES);
   return batchPage->entries[idx] & BALLOON_BATCH_PAGE_MASK;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Balloon_BatchGetStatus --
 *
 *      Get the error code associated with a page.
 *
 * Results:
 *      See above.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static INLINE uint8
Balloon_BatchGetStatus(BalloonBatchPage *batchPage,     // IN
                       uint16 idx)                      // IN
{
   ASSERT(idx < BALLOON_BATCH_MAX_ENTRIES);
   return (uint8)(batchPage->entries[idx] & BALLOON_BATCH_STATUS_MASK);
}


/*
 *-----------------------------------------------------------------------------
 *
 * Balloon_BatchSetPA --
 *
 *      Store the page in the batch page at idx.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static INLINE void
Balloon_BatchSetPA(BalloonBatchPage *batchPage,            // IN
                   uint16 idx,                             // IN
                   PA64 pa)                                // IN
{
   ASSERT(idx < BALLOON_BATCH_MAX_ENTRIES);
   ASSERT((pa & ~BALLOON_BATCH_PAGE_MASK) == 0);
   batchPage->entries[idx] = pa;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Balloon_BatchSetStatus --
 *
 *      Set the error code associated with a page.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */
static INLINE void
Balloon_BatchSetStatus(BalloonBatchPage *batchPage,      // IN
                       uint16 idx,                       // IN
                       int error)                        // IN
{
   PA64 pa = Balloon_BatchGetPA(batchPage, idx);
   ASSERT(idx < BALLOON_BATCH_MAX_ENTRIES);
   ASSERT(error <= BALLOON_ERROR_BUSY && error >= BALLOON_FAILURE);
   batchPage->entries[idx] = pa | (PPN)error;
}

MY_ASSERTS(BALLOON_BATCH_SIZE,
           ASSERT_ON_COMPILE(sizeof(BalloonBatchPage) == PAGE_SIZE);
)


#if defined __cplusplus
} // extern "C"
#endif

#endif  /* _BALLOON_DEF_H */
 07070100000264000081A400000000000000000000000168225505000022F1000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/dbllnklst.h    /*********************************************************
 * Copyright (C) 1998-2017,2020,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * dbllnklst.h --
 *
 *    Double linked lists
 *
 *    Both circular and anchored (linear) lists are supported.
 *    See bora/lib/misc/dbllnklst.c for sample code showing both use cases.
 */

#ifndef _DBLLNKLST_H_
#define _DBLLNKLST_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE
#include "includeCheck.h"

#include "vm_basic_types.h"

#if defined(__cplusplus)
extern "C" {
#endif

#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
 *
 * DblLnkLst_LinkFirst, DblLnkLst_LinkLast, and DblLnkLst_Swap are specific
 * to anchored lists.  The rest are for both circular and anchored lists.
 */


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Init --
 *
 *    Initialize a member of a doubly linked list
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Init(DblLnkLst_Links *l) // OUT
{
   l->prev = l->next = l;
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Link --
 *
 *    Merge two doubly linked lists into one
 *
 *    The operation is commutative
 *    The operation is inversible (its inverse is DblLnkLst_Unlink)
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Link(DblLnkLst_Links *l1, // IN/OUT
               DblLnkLst_Links *l2) // IN/OUT
{
   DblLnkLst_Links *tmp;

   (tmp      = l1->prev)->next = l2;
   (l1->prev = l2->prev)->next = l1;
    l2->prev = tmp                 ;
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Unlink --
 *
 *    Split one doubly linked list into two
 *
 *    No check is performed: the caller must ensure that both members
 *    belong to the same doubly linked list
 *
 *    The operation is commutative
 *    The operation is inversible (its inverse is DblLnkLst_Link)
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Unlink(DblLnkLst_Links *l1, // IN/OUT
                 DblLnkLst_Links *l2) // IN/OUT
{
   DblLnkLst_Links *tmp;

   tmp       = l1->prev            ;
   (l1->prev = l2->prev)->next = l1;
   (l2->prev = tmp     )->next = l2;
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Unlink1 --
 *
 *    Unlink an element from its list.
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Unlink1(DblLnkLst_Links *l) // IN/OUT
{
   DblLnkLst_Unlink(l, l->next);
}


/*
 *----------------------------------------------------------------------------
 *
 * DblLnkLst_IsLinked --
 *
 *    Determines whether an element is linked with any other elements.
 *
 * Results:
 *    TRUE if link is linked, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE Bool
DblLnkLst_IsLinked(DblLnkLst_Links const *l) // IN
{
   /*
    * A DblLnkLst_Links is either linked to itself (not linked) or linked to
    * other elements in a list (linked).
    */
   return l->prev != l;
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_LinkFirst --
 *
 *    Insert 'l' at the beginning of the list anchored at 'head'
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_LinkFirst(DblLnkLst_Links *head, // IN/OUT
                    DblLnkLst_Links *l)    // IN/OUT
{
   DblLnkLst_Link(head->next, l);
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_LinkLast --
 *
 *    Insert 'l' at the end of the list anchored at 'head'
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_LinkLast(DblLnkLst_Links *head, // IN/OUT
                   DblLnkLst_Links *l)    // IN/OUT
{
   DblLnkLst_Link(head, l);
}


/*
 *----------------------------------------------------------------------
 *
 * DblLnkLst_Swap --
 *
 *    Swap all entries between the list anchored at 'head1' and the list
 *    anchored at 'head2'.
 *
 *    The operation is commutative
 *    The operation is inversible (its inverse is itself)
 *
 * Result
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static INLINE void
DblLnkLst_Swap(DblLnkLst_Links *head1,  // IN/OUT
               DblLnkLst_Links *head2)  // IN/OUT
{
   DblLnkLst_Links const tmp = *head1;

   if (DblLnkLst_IsLinked(head2)) {
      (head1->prev = head2->prev)->next = head1;
      (head1->next = head2->next)->prev = head1;
   } else {
      DblLnkLst_Init(head1);
   }

   if (tmp.prev != head1) {
      (head2->prev = tmp.prev)->next = head2;
      (head2->next = tmp.next)->prev = head2;
   } else {
      DblLnkLst_Init(head2);
   }
}

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _DBLLNKLST_H_ */
   07070100000265000081A40000000000000000000000016822550500002E1F000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/kernelStubs.h  /*********************************************************
 * Copyright (c) 2006-2019,2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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__

#define KRNL_STUBS_DRIVER_TYPE_POSIX      1
#define KRNL_STUBS_DRIVER_TYPE_GDI        2
#define KRNL_STUBS_DRIVER_TYPE_WDM        3
#define KRNL_STUBS_DRIVER_TYPE_NDIS       4
#define KRNL_STUBS_DRIVER_TYPE_STORPORT   5

// For now (vsphere-2015), choose a good default. Later we'll modify all the
// build files using KernelStubs to set this.
#ifndef KRNL_STUBS_DRIVER_TYPE
#  if defined(_WIN32)
#     define KRNL_STUBS_DRIVER_TYPE KRNL_STUBS_DRIVER_TYPE_WDM
#  else
#     define KRNL_STUBS_DRIVER_TYPE KRNL_STUBS_DRIVER_TYPE_POSIX
#  endif
#endif

#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 <linux/kernel.h>
#   include <linux/string.h>
#elif defined(_WIN32)
#   define _CRT_ALLOCATION_DEFINED // prevent malloc.h from defining malloc et. all
#   if KRNL_STUBS_DRIVER_TYPE == KRNL_STUBS_DRIVER_TYPE_GDI
#      include <d3d9.h>
#      include <winddi.h>
#      include <stdio.h>
#      include "vm_basic_types.h"
#      include "vm_basic_defs.h"
#      include "vm_assert.h"
#   elif KRNL_STUBS_DRIVER_TYPE == KRNL_STUBS_DRIVER_TYPE_NDIS
#      include <ntddk.h>
#      include <stdio.h>    /* for _vsnprintf, vsprintf */
#      include <stdarg.h>   /* for va_start stuff */
#      include "vm_basic_defs.h"
#      include "vm_assert.h"
#      include "kernelStubsFloorFixes.h"
#pragma warning(disable:4201) // unnamed struct/union
#      include <ndis.h>
#   elif KRNL_STUBS_DRIVER_TYPE == KRNL_STUBS_DRIVER_TYPE_STORPORT
#      include "vm_basic_types.h"
#      include <wdm.h>   /* kernel memory APIs, DbgPrintEx */
#      include <stdio.h>    /* for _vsnprintf, vsprintf */
#      include <stdarg.h>   /* for va_start stuff */
#      include <stdlib.h>   /* for min macro. */
#      include <Storport.h> /* for Storport functions */
#      include "vm_basic_defs.h"
#      include "vm_assert.h"  /* Our assert macros */
#      include "kernelStubsFloorFixes.h"
#   elif KRNL_STUBS_DRIVER_TYPE == KRNL_STUBS_DRIVER_TYPE_WDM
#      include "vm_basic_types.h"
#      if defined(NTDDI_WINXP) && (NTDDI_VERSION >= NTDDI_WINXP)
#         include <wdm.h>   /* kernel memory APIs, DbgPrintEx */
#      else
#         include <ntddk.h> /* kernel memory APIs */
#      endif
#      include <stdio.h>    /* for _vsnprintf, vsprintf */
#      include <stdarg.h>   /* for va_start stuff */
#      include <stdlib.h>   /* for min macro. */
#      include "vm_basic_defs.h"
#      include "vm_assert.h"  /* Our assert macros */
#      include "kernelStubsFloorFixes.h"
#   else
#      error Type KRNL_STUBS_DRIVER_TYPE must be defined.
#   endif
#elif defined(__FreeBSD__)
#   include "vm_basic_types.h"
#   ifndef _KERNEL
#      error "_KERNEL is not defined"
#   endif
#   include <sys/types.h>
#   include <sys/malloc.h>
#   include <sys/param.h>
#   include <sys/kernel.h>
#   include <machine/stdarg.h>
#   include <sys/libkern.h>
#elif defined(__APPLE__)
#   include "vm_basic_types.h"
#   ifndef KERNEL
#      error "KERNEL is not defined"
#   endif
#   include <stdarg.h>
#   include <string.h>
# elif defined(sun)
#   include "vm_basic_types.h"
#   include <sys/types.h>
#   include <sys/varargs.h>
#endif
#include "kernelStubsSal.h"

/*
 * Function Prototypes
 */

#if defined(__linux__) || defined(__APPLE__) || defined (sun)

#  ifdef __linux__                           /* if (__linux__) { */
#  define atoi(s) simple_strtol(((s != NULL) ? s : ""), NULL, 10)
int strcasecmp(const char *s1, const char *s2);
char *strdup(const char *source);
#  endif

#  ifdef __APPLE__                           /* if (__APPLE__) { */
int atoi(const char *);
char *STRDUP(const char *, int);
#  define strdup(s) STRDUP(s, 80)
#  endif

#  if defined(__linux__) || defined(__APPLE__) /* if (__linux__ || __APPLE__) { */
#  define Str_Strcasecmp(s1, s2) strcasecmp(s1, s2)
#  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) { */

_Ret_allocates_malloc_mem_opt_bytecap_(_Size)
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTNOALIAS _CRTRESTRICT
void * __cdecl malloc(
   _In_ size_t _Size);

_Ret_allocates_malloc_mem_opt_bytecount_(_Count*_Size)
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTNOALIAS _CRTRESTRICT
void * __cdecl calloc(
   _In_ size_t _Count,
   _In_ size_t _Size);

_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTNOALIAS
void __cdecl free(
   _In_frees_malloc_mem_opt_ void * _Memory);

_Success_(return != 0)
_When_(_Memory != 0, _Ret_reallocates_malloc_mem_opt_newbytecap_oldbytecap_(_NewSize, ((uintptr_t*)_Memory)[-1]))
_When_(_Memory == 0, _Ret_reallocates_malloc_mem_opt_newbytecap_(_NewSize))
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTNOALIAS _CRTRESTRICT
void * __cdecl realloc(
   _In_reallocates_malloc_mem_opt_oldptr_ void * _Memory,
   _In_ size_t _NewSize);

_Success_(return != 0)
_Ret_allocates_malloc_mem_opt_z_
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTIMP
char * __cdecl _strdup_impl(
   _In_opt_z_ const char * _Src);

#define strdup _strdup_impl

#elif defined(__FreeBSD__)                      /* } else if (FreeBSD) { */

/* Kernel memory on FreeBSD is tagged for statistics and confidence 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                                          /* } */

_Ret_writes_z_(maxSize)
char *Str_Strcpy(
   _Out_z_cap_(maxSize) char *buf,
   _In_z_ const char *src,
   _In_ size_t maxSize);

_Ret_writes_z_(maxSize)
char *Str_Strcat(
   _Inout_z_cap_(maxSize) char *buf,
   _In_z_ const char *src,
   _In_ size_t maxSize);

_Success_(return >= 0)
int Str_Sprintf(
   _Out_z_cap_(maxSize) _Post_z_count_(return+1) char *buf,
   _In_ size_t maxSize,
   _In_z_ _Printf_format_string_ const char *fmt,
   ...) PRINTF_DECL(3, 4);

_Success_(return != -1)
int Str_Vsnprintf(
   _Out_z_cap_(size) _Post_z_count_(return+1) char *str,
   _In_ size_t size,
   _In_z_ _Printf_format_string_ const char *format,
   _In_ va_list ap) PRINTF_DECL(3, 0);

_Success_(return != 0)
_When_(length != 0, _Ret_allocates_malloc_mem_opt_z_bytecount_(*length))
_When_(length == 0, _Ret_allocates_malloc_mem_opt_z_)
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
char *Str_Vasprintf(
   _Out_opt_ size_t *length,
   _In_z_ _Printf_format_string_ const char *format,
   _In_ va_list arguments) PRINTF_DECL(2, 0);

_Success_(return != 0)
_When_(length != 0, _Ret_allocates_malloc_mem_opt_z_bytecount_(*length))
_When_(length == 0, _Ret_allocates_malloc_mem_opt_z_)
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
char *Str_Asprintf(
   _Out_opt_ size_t *length,
   _In_z_ _Printf_format_string_ const char *format,
   ...) PRINTF_DECL(2, 3);

#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 28301) // Suppress complaint that first declaration lacked annotations
#endif

// For now (vsphere-2015), we don't implement Panic, Warning, or Debug in the
// GDI case.
#if (KRNL_STUBS_DRIVER_TYPE != KRNL_STUBS_DRIVER_TYPE_GDI) &&\
    (KRNL_STUBS_DRIVER_TYPE != KRNL_STUBS_DRIVER_TYPE_NDIS)

/*
 * Stub functions we provide.
 */
#ifdef _WIN32
NORETURN
#endif
void Panic(
   _In_z_ _Printf_format_string_ const char *fmt,
   ...) PRINTF_DECL(1, 2);

void Warning(
   _In_z_ _Printf_format_string_ const char *fmt,
   ...) PRINTF_DECL(1, 2);

/*
 * Functions the driver must implement for the stubs.
 */
EXTERN void Debug(
   _In_z_ _Printf_format_string_ const char *fmt,
   ...) PRINTF_DECL(1, 2);

#endif // KRNL_STUBS_DRIVER_TYPE != KRNL_STUBS_DRIVER_TYPE_GDI

#ifdef _WIN32
#pragma warning(pop)
#endif

#endif /* __KERNELSTUBS_H__ */
 07070100000266000081A40000000000000000000000016822550500001C54000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/kernelStubsSal.h   /*********************************************************
 * Copyright (C) 2015-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * kernelStubsSal.h
 *
 * Contains definitions source annotation language definitions for kernel drivers.
 * This solves two issues:
 * 1. Microsoft changed their annotation language from SAL 1.0 (original one
 *    widely distributed by the Windows team) to their more final SAL 2.0
 *    langauge (championed by the VS team).
 * 2. We want these annotations to do nothing during non-Win32 compiles.
 *
 * A longer term goal is to rationalize this into Bora.
 */
#ifndef __KERNELSTUBSSAL_H__
#define __KERNELSTUBSSAL_H__

#if defined(_WIN32)
#  include <DriverSpecs.h>
#  if !defined(_SAL_VERSION)
#     define _SAL_VERSION 10
#  endif
#endif

#if !defined(_SAL_VERSION) || (defined(_SAL_VERSION) && _SAL_VERSION == 10)
#define _In_
#define _In_opt_
#define _In_reads_bytes_(count)
#define _In_reads_bytes_opt_(count)
#define _In_z_
#define _In_opt_z_
#define _Out_
#define _Out_opt_
#define _Out_writes_bytes_(capcount)
#define _Out_writes_bytes_opt_(capcount)
#define _Out_writes_bytes_to_(cap, count)
#define _Out_writes_bytes_to_opt_(cap, count)
#define _Out_bytecap_post_bytecount_(cap, count)
#define _Out_writes_z_(cap)
#define _Out_writes_opt_z_(cap)
#define _Out_z_cap_(e)
#define _Outptr_result_buffer_(count)
#define _Outptr_result_bytebuffer_(count)
#define _Outptr_result_bytebuffer_maybenull_(count)
#define _Outptr_opt_result_buffer_(count)
#define _Outptr_opt_result_bytebuffer_(count)
#define _Outptr_opt_result_bytebuffer_maybenull_(count)
#define _COM_Outptr_
#define _Inout_
#define _Inout_updates_bytes_(e)
#define _Inout_z_cap_(e)
#define _Post_z_count_(e)
#define _Ret_writes_z_(e)
#define _Ret_writes_maybenull_z_(e)
#define _Ret_maybenull_
#define _Ret_maybenull_z_
#define _Ret_range_(l,h)
#define _Success_(expr)
#define _Check_return_
#define _Must_inspect_result_
#define _Group_(annos)
#define _When_(expr, annos)
#define _Always_(annos)
#define _Printf_format_string_
#define _Use_decl_annotations_
#define _Dispatch_type_(mj)
#define _Function_class_(c)
#define _Requires_lock_held_(cs)
#define _Requires_lock_not_held_(cs)
#define _Acquires_lock_(l)
#define _Releases_lock_(l)
#define _IRQL_requires_max_(i)
#define _IRQL_requires_(i)
#define _IRQL_requires_same_
#define _Analysis_assume_(e)
#define _Pre_notnull_
#define _At_(expr,annos)

#else
// Sal 2.0 path - everything is already defined.
#endif // _SAL_VERSION

// Now define our own annotations
#if !defined(_SAL_VERSION) || (defined(_SAL_VERSION) && _SAL_VERSION == 10)
#define _When_windrv_(annos)
#define _Ret_allocates_malloc_mem_opt_bytecap_(_Size)
#define _Ret_allocates_malloc_mem_opt_bytecount_(_Size)
#define _Ret_allocates_malloc_mem_opt_bytecap_post_bytecount_(_Cap,_Count)
#define _Ret_allocates_malloc_mem_opt_z_bytecount_(_Size)
#define _Ret_allocates_malloc_mem_opt_z_
#define _In_frees_malloc_mem_opt_
#else
#define _When_windrv_(annos)                                               annos
#define _Ret_allocates_malloc_mem_opt_bytecap_(_Cap)                       __drv_allocatesMem("Memory") _Must_inspect_result_ _Ret_opt_bytecap_(_Cap)
#define _Ret_allocates_malloc_mem_opt_bytecount_(_Count)                   __drv_allocatesMem("Memory") _Must_inspect_result_ _Ret_opt_bytecount_(_Count)
#define _Ret_allocates_malloc_mem_opt_bytecap_post_bytecount_(_Cap,_Count) __drv_allocatesMem("Memory") _Must_inspect_result_ _Ret_opt_bytecap_(_Cap) _Ret_opt_bytecount_(_Count)
#define _Ret_allocates_malloc_mem_opt_z_bytecount_(_Count)                 __drv_allocatesMem("Memory") _Must_inspect_result_ _Ret_opt_z_bytecount_(_Count)
#define _Ret_allocates_malloc_mem_opt_z_                                   __drv_allocatesMem("Memory") _Must_inspect_result_ _Ret_opt_z_
#define _In_frees_malloc_mem_opt_                                          __drv_freesMem("Memory") _Pre_maybenull_ _Post_invalid_
#endif // _SAL_VERSION

// Best we can do for reallocate with simple annotations: assume old size was fully initialized.
#define _Ret_reallocates_malloc_mem_opt_newbytecap_oldbytecap_(_NewSize, _OldSize) _Ret_allocates_malloc_mem_opt_bytecap_post_bytecount_(_NewSize, _OldSize <= _NewSize ? _OldSize : _NewSize)
#define _Ret_reallocates_malloc_mem_opt_newbytecap_(_NewSize)                      _Ret_allocates_malloc_mem_opt_z_bytecount_(_NewSize)
#define _In_reallocates_malloc_mem_opt_oldptr_                                     _In_frees_malloc_mem_opt_

#endif // __KERNELSTUBSSAL_H__
07070100000267000081A40000000000000000000000016822550500001042000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/os.h   /*********************************************************
 * Copyright (C) 2000,2014 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * os.h --
 *
 *      Definitions for OS-specific wrapper functions required by "vmmemctl".
 */

#ifndef	OS_H
#define	OS_H

#include "vm_basic_types.h"
#include "balloon_def.h"

/*
 * Types
 */
#if defined __APPLE__
typedef uint64 PageHandle;
#else
typedef uintptr_t PageHandle;
#endif
typedef uintptr_t Mapping;

#define PAGE_HANDLE_INVALID     0
#define MAPPING_INVALID         0

#define OS_SMALL_PAGE_ORDER (0) // 4 KB small pages
#define OS_LARGE_PAGE_ORDER (9) // 2 MB large pages
#define OS_LARGE_2_SMALL_PAGES (1 << OS_LARGE_PAGE_ORDER)

/*
 * Operations
 */

extern void OS_MemZero(void *ptr, size_t size);
extern void OS_MemCopy(void *dest, const void *src, size_t size);

extern void *OS_Malloc(size_t size);
extern void OS_Free(void *ptr, size_t size);

extern void OS_Yield(void);

extern unsigned long OS_ReservedPageGetLimit(void);
extern PA64          OS_ReservedPageGetPA(PageHandle handle);
extern PageHandle    OS_ReservedPageGetHandle(PA64 pa);
extern PageHandle    OS_ReservedPageAlloc(int canSleep, int isLargePage);
extern void          OS_ReservedPageFree(PageHandle handle, int isLargePage);

extern Mapping       OS_MapPageHandle(PageHandle handle);
extern void          *OS_Mapping2Addr(Mapping mapping);
extern void          OS_UnmapPage(Mapping mapping);

#endif  /* OS_H */
  07070100000268000081A40000000000000000000000016822550500009A3B000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/vmballoon.c    /*********************************************************
 * Copyright (C) 2000-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vmballoon.c --
 *
 *      VMware physical memory management driver for Unix-ish
 *      (Linux, FreeBSD, Solaris, Mac OS) guests. The driver acts like
 *      a "balloon" that can be inflated to reclaim physical pages by
 *      reserving them in the guest and invalidating them in the
 *      monitor, freeing up the underlying machine pages so they can
 *      be allocated to other guests.  The balloon can also be
 *      deflated to allow the guest to use more physical memory.
 *      Higher level policies can control the sizes of balloons in VMs
 *      in order to manage physical memory resources.
 */

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Includes
 */

#include "os.h"
#include "backdoor.h"
#include "backdoor_balloon.h"
#include "vmballoon.h"

/*
 * Constants
 */

#ifndef NULL
#define NULL 0
#endif

/*
 * When guest is under memory pressure, use a reduced page allocation
 * rate for next several cycles.
 */
#define SLOW_PAGE_ALLOCATION_CYCLES     4

/* Maximum number of page allocations without yielding processor */
#define BALLOON_ALLOC_YIELD_THRESHOLD   1024

/*
 * Balloon operations
 */
static void BalloonPageFree(Balloon *b, int isLargePage);
static void BalloonAdjustSize(Balloon *b, uint64 target);
static void BalloonReset(Balloon *b);

static void BalloonAddPage(Balloon *b, uint16 idx, PageHandle page);
static void BalloonAddPageBatched(Balloon *b, uint16 idx, PageHandle page);
static int  BalloonLock(Balloon *b, uint16 nPages, int isLargePage,
                        uint64 *target);
static int  BalloonLockBatched(Balloon *b, uint16 nPages, int isLargePages,
                               uint64 *target);
static int  BalloonUnlock(Balloon *b, uint16 nPages, int isLargePages,
                          uint64 *target);
static int  BalloonUnlockBatched(Balloon *b, uint16 nPages, int IsLargePages,
                                 uint64 *target);

/*
 * Globals
 */

static Balloon globalBalloon;

static const BalloonOps balloonOps = {
   .addPage = BalloonAddPage,
   .lock = BalloonLock,
   .unlock = BalloonUnlock
};

static const struct BalloonOps balloonOpsBatched = {
   .addPage = BalloonAddPageBatched,
   .lock = BalloonLockBatched,
   .unlock = BalloonUnlockBatched
};

/*
 *----------------------------------------------------------------------
 *
 * Balloon_GetStats --
 *
 *      Returns information about balloon state, including the current and
 *      target size, rates for allocating and freeing pages, and statistics
 *      about past activity.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

const BalloonStats *
Balloon_GetStats(void)
{
   Balloon *b = &globalBalloon;
   BalloonStats *stats = &b->stats;

   /*
    * Fill in additional information about size and rates, which is
    * normally kept in the Balloon structure itself.
    */
   stats->nPages = b->nPages;
   stats->nPagesTarget = b->nPagesTarget;
   stats->rateNoSleepAlloc = BALLOON_NOSLEEP_ALLOC_MAX;
   stats->rateAlloc = b->rateAlloc;
   stats->rateFree = b->rateFree;

   return stats;
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonChunk_Create --
 *
 *      Creates a new BalloonChunk object capable of tracking
 *      BALLOON_CHUNK_PAGES PAs.
 *
 *      We do not bother to define two versions (NOSLEEP and CANSLEEP)
 *      of OS_Malloc because Chunk_Create does not require a new page
 *      often.
 *
 * Results:
 *      On success: initialized BalloonChunk
 *      On failure: NULL
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

static BalloonChunk *
BalloonChunk_Create(void)
{
   BalloonChunk *chunk;

   /* allocate memory, fail if unable */
   chunk = OS_Malloc(sizeof *chunk);
   if (chunk == NULL) {
      return NULL;
   }

   /* initialize */
   OS_MemZero(chunk, sizeof *chunk);
   DblLnkLst_Init(&chunk->node);

   return chunk;
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonChunk_Destroy --
 *
 *      Reclaims all storage associated with specified BalloonChunk.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonChunk_Destroy(BalloonChunk *chunk) // IN
{
   /* reclaim storage */
   OS_Free(chunk, sizeof *chunk);
}


/*
 *----------------------------------------------------------------------
 *
 * Balloon_DeallocateChunkList --
 *
 *      Frees all allocated pages of a size
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
Balloon_DeallocateChunkList(Balloon *b,       // IN/OUT
                            int isLargePages) // IN
{
   unsigned int cnt = 0;
   BalloonChunkList *chunkList;

   chunkList = &b->pages[isLargePages];

   /* free all pages, skipping monitor unlock */
   while (chunkList->nChunks > 0) {
      BalloonPageFree(b, isLargePages);
      if (++cnt >= b->rateFree) {
         cnt = 0;
         OS_Yield();
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * Balloon_Deallocate --
 *
 *      Frees all allocated pages.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
Balloon_Deallocate(Balloon *b) // IN/OUT
{
   /* free all pages, skipping monitor unlock */
   Balloon_DeallocateChunkList(b, FALSE);
   Balloon_DeallocateChunkList(b, TRUE);

   /* Release the batch page */
   if (b->batchPageMapping != MAPPING_INVALID) {
      OS_UnmapPage(b->batchPageMapping);
      b->batchPageMapping = MAPPING_INVALID;
      b->batchPage = NULL;
   }

   if (b->pageHandle != PAGE_HANDLE_INVALID) {
      OS_ReservedPageFree(b->pageHandle, FALSE);
      b->pageHandle = PAGE_HANDLE_INVALID;
   }
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonInitBatching --
 *
 *      Allocate and map the batch page.
 *
 * Results:
 *      BALLOON_SUCCESS or an error code in case of failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
BalloonInitBatching(Balloon *b) // OUT
{
   b->batchMaxEntries = BALLOON_BATCH_MAX_ENTRIES;

   b->pageHandle = OS_ReservedPageAlloc(FALSE, FALSE);
   if (b->pageHandle == PAGE_HANDLE_INVALID) {
      return BALLOON_PAGE_ALLOC_FAILURE;
   }

   b->batchPageMapping = OS_MapPageHandle(b->pageHandle);
   if (b->batchPageMapping == MAPPING_INVALID) {
      OS_ReservedPageFree(b->pageHandle, FALSE);
      b->pageHandle = PAGE_HANDLE_INVALID;
      return BALLOON_PAGE_ALLOC_FAILURE;
   }
   b->batchPage = OS_Mapping2Addr(b->batchPageMapping);

   return BALLOON_SUCCESS;
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonReset --
 *
 *      Resets balloon "b" to empty state.  Frees all allocated pages
 *      and attempts to reset contact with the monitor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Schedules next execution of balloon timer handler.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonReset(Balloon *b) // OUT
{
   int status;

   /* free all pages, skipping monitor unlock */
   Balloon_Deallocate(b);

   status = Backdoor_MonitorStart(b, BALLOON_CAPABILITIES);
   if (status != BALLOON_SUCCESS) {
      return;
   }

   if ((b->hypervisorCapabilities & BALLOON_BATCHED_CMDS) != 0) {
      status = BalloonInitBatching(b);
      if (status != BALLOON_SUCCESS) {
         /*
          * We failed to initialize the batching in the guest, inform
          * the monitor about that by sending a null capability.
          *
          * The guest will retry to init itself in one second.
          */
         Backdoor_MonitorStart(b, 0);
         return;
      }
   }

   if ((b->hypervisorCapabilities & BALLOON_BATCHED_CMDS) != 0) {
      b->balloonOps = &balloonOpsBatched;
   } else if ((b->hypervisorCapabilities & BALLOON_BASIC_CMDS) != 0) {
      b->balloonOps = &balloonOps;
      b->batchMaxEntries = 1;
   }

   /* clear flag */
   b->resetFlag = FALSE;

   /* report guest type */
   (void) Backdoor_MonitorGuestType(b);
}


/*
 *----------------------------------------------------------------------
 *
 * Balloon_QueryAndExecute --
 *
 *      Contacts monitor via backdoor to obtain balloon size target,
 *      and starts adjusting balloon size to achieve target by allocating
 *      or deallocating pages. Resets balloon if requested by the monitor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
Balloon_QueryAndExecute(void)
{
   Balloon *b = &globalBalloon;
   uint64 target = 0; // Silence compiler warning.
   int status;

   /* update stats */
   STATS_INC(b->stats.timer);

   /* reset, if specified */
   if (b->resetFlag) {
      BalloonReset(b);
   }

   /* contact monitor via backdoor */
   status = Backdoor_MonitorGetTarget(b, &target);

   /* decrement slowPageAllocationCycles counter */
   if (b->slowPageAllocationCycles > 0) {
      b->slowPageAllocationCycles--;
   }

   if (status == BALLOON_SUCCESS) {
      /* update target, adjust size */
      b->nPagesTarget = target;
      BalloonAdjustSize(b, target);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonErrorPageStore --
 *
 *      Attempt to add "page" to list of non-balloonable pages
 *      associated with "b".
 *
 * Results:
 *      On success: BALLOON_SUCCESS
 *      On failure: BALLOON_FAILURE (non-balloonable page list is already full)
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
BalloonErrorPageStore(Balloon *b,                     // IN/OUT
                      PageHandle page,                // IN
                      int isLargePage)                // IN
{
   BalloonErrorPages *errors = &b->errors[isLargePage];

   /* fail if list already full */
   if (errors->nEntries >= BALLOON_ERROR_PAGES) {
      return BALLOON_FAILURE;
   }

   /* add page to list */
   errors->entries[errors->nEntries++] = page;
   STATS_INC(b->stats.primErrorPageAlloc[isLargePage]);
   return BALLOON_SUCCESS;
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonErrorPagesFreeInt --
 *
 *      Deallocates all pages of a size on the list of non-balloonable pages
 *      associated with "b".
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonErrorPagesFreeInt(Balloon *b,      // IN/OUT
                         int isLargePage) // IN
{
   unsigned int i;
   BalloonErrorPages *errors = &b->errors[isLargePage];

   /* free all non-balloonable "error" pages */
   for (i = 0; i < errors->nEntries; i++) {
      OS_ReservedPageFree(errors->entries[i], isLargePage);
      errors->entries[i] = PAGE_HANDLE_INVALID;
      STATS_INC(b->stats.primErrorPageFree[isLargePage]);
   }
   errors->nEntries = 0;
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonErrorPagesFree --
 *
 *      Deallocates all pages on the list of non-balloonable pages
 *      associated with "b".
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonErrorPagesFree(Balloon *b) // IN/OUT
{
   BalloonErrorPagesFreeInt(b, FALSE);
   BalloonErrorPagesFreeInt(b, TRUE);
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonGetChunk --
 *
 *      Attempt to find a "chunk" with a free slot to store locked page.
 *      Try to allocate new chunk if all existing chunks are full.
 *
 * Results:
 *      Returns NULL on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static BalloonChunk *
BalloonGetChunk(Balloon *b,         // IN/OUT
                int isLargePage)    // IN
{
   BalloonChunk *chunk;
   BalloonChunkList *chunkList = &b->pages[isLargePage];

   /* Get first chunk from the list */
   if (DblLnkLst_IsLinked(&chunkList->chunks)) {
      chunk = DblLnkLst_Container(chunkList->chunks.next, BalloonChunk, node);
      if (chunk->nEntries < BALLOON_CHUNK_ENTRIES) {
         /* This chunk has free slots, use it */
         return chunk;
      }
   }

   /* create new chunk */
   chunk = BalloonChunk_Create();
   if (chunk != NULL) {
      DblLnkLst_LinkFirst(&chunkList->chunks, &chunk->node);

      /* update stats */
      chunkList->nChunks++;
   }

   return chunk;
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonGetChunkOrFallback --
 *
 *      Attempt to find a "chunk" with a free slot to store locked page.
 *      If the allocation fails, use the previously allocated
 *      fallbackChunk.
 *
 * Results:
 *      A valid chunk.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static BalloonChunk *
BalloonGetChunkOrFallback(Balloon *b,      // IN/OUT
                          int isLargePage) // IN
{
   BalloonChunk *chunk = BalloonGetChunk(b, isLargePage);
   if (chunk == NULL) {
      BalloonChunkList *chunkList = &b->pages[isLargePage];

      ASSERT(b->fallbackChunk != NULL);
      chunk = b->fallbackChunk;
      b->fallbackChunk = NULL;

      DblLnkLst_LinkFirst(&chunkList->chunks, &chunk->node);
      chunkList->nChunks++;
   }

   return chunk;
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonPageStore --
 *
 *      Add "page" to the given "chunk".
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonPageStore(BalloonChunk *chunk,   // IN/OUT
                 PageHandle page)       // IN
{
   chunk->entries[chunk->nEntries++] = page;
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonChunkDestroyEmpty --
 *
 *      Release the chunk if it contains no pages.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonChunkDestroyEmpty(Balloon *b,            // IN/OUT
                         BalloonChunk *chunk,   // IN/OUT
                         int isLargePage)       // IN
{
   if (chunk->nEntries == 0) {
      /* destroy empty chunk */
      DblLnkLst_Unlink1(&chunk->node);
      BalloonChunk_Destroy(chunk);

      /* update stats */
      b->pages[isLargePage].nChunks--;
   }
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonPageFree --
 *
 *      Attempts to deallocate a physical page, deflating balloon "b".
 *      Never informs monitor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonPageFree(Balloon *b,      // IN/OUT
                int isLargePage) // IN
{
   BalloonChunkList *chunkList = &b->pages[isLargePage];
   BalloonChunk *chunk;
   PageHandle page;

   ASSERT(DblLnkLst_IsLinked(&chunkList->chunks));
   chunk = DblLnkLst_Container(chunkList->chunks.next, BalloonChunk, node);

   /* deallocate last page */
   page = chunk->entries[--chunk->nEntries];

   /* deallocate page */
   OS_ReservedPageFree(page, isLargePage);

   STATS_INC(b->stats.primFree[isLargePage]);

   /* update balloon size */
   b->nPages--;

   /* reclaim chunk, if empty */
   BalloonChunkDestroyEmpty(b, chunk, isLargePage);
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonInflate --
 *
 *      Attempts to allocate physical pages to inflate balloon.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonInflate(Balloon *b,      // IN/OUT
               uint64 target)   // IN
{
   uint32 nEntries;
   unsigned int rate;
   unsigned int allocations = 0;
   int status = BALLOON_SUCCESS;
   BalloonPageAllocType allocType;
   Bool isLargePages;
   unsigned numPagesPerEntry;

   if ((b->hypervisorCapabilities & BALLOON_BATCHED_2M_CMDS) != 0) {
      allocType = BALLOON_PAGE_ALLOC_LPAGE;
      isLargePages = TRUE;
      numPagesPerEntry = OS_LARGE_2_SMALL_PAGES;
   } else {
      allocType = BALLOON_PAGE_ALLOC_NOSLEEP;
      isLargePages = FALSE;
      numPagesPerEntry = 1;
   }

   /*
    * We try allocating in the following order:
    *
    * First we try to allocate large pages without sleeping. If the
    * memory becomes too fragmented to allocate whole large pages at
    * once, switch to small pages - still without sleeping.
    *
    * If we do not throttle nosleep allocations, we can drain all
    * free pages in the guest quickly (if the balloon target is high).
    * As a side-effect, draining free pages helps to inform (force)
    * the guest to start swapping if balloon target is not met yet,
    * which is a desired behavior. However, balloon driver can consume
    * all available CPU cycles if too many pages are allocated in a
    * second. Therefore, we throttle nosleep allocations even when
    * the guest is not under memory pressure. OTOH, if we have already
    * predicted that the guest is under memory pressure, then we
    * slowdown page allocations considerably.
    */

   /*
    * Start with no sleep allocation rate which may be higher
    * than sleeping allocation rate.
    */
   rate = b->slowPageAllocationCycles ?
                b->rateAlloc : BALLOON_NOSLEEP_ALLOC_MAX;

   nEntries = 0;
   while (b->nPages < target &&
          nEntries * numPagesPerEntry < target - b->nPages) {
      PageHandle handle;

      STATS_INC(b->stats.primAlloc[allocType]);

      handle = OS_ReservedPageAlloc(allocType == BALLOON_PAGE_ALLOC_CANSLEEP,
                                    isLargePages);

      if (handle == PAGE_HANDLE_INVALID) {
         STATS_INC(b->stats.primAllocFail[allocType]);

         status = BALLOON_PAGE_ALLOC_FAILURE;

         if (allocType == BALLOON_PAGE_ALLOC_LPAGE) {
            /*
             * LPAGE allocation failed. This does mean that the guest is under
             * pressure, it just means that the memory is fragmented enough that
             * we cannot allocate any more large pages.
             *
             * Lock partial set of large pages now as we continue with
             * allocating small pages and we cannot have a lock call with
             * different entry sizes.
             */
            if (nEntries > 0) {
               status = b->balloonOps->lock(b, nEntries, TRUE, &target);
               nEntries = 0;
            }

            /* Continue with small pages */
            isLargePages = FALSE;
            numPagesPerEntry = 1;
            allocType = BALLOON_PAGE_ALLOC_NOSLEEP;
         } else if (allocType == BALLOON_PAGE_ALLOC_NOSLEEP) {
            /*
             * NOSLEEP page allocation failed, so the guest is under memory
             * pressure. Let us slow down page allocations for next few cycles
             * so that the guest gets out of memory pressure. Also, if we
             * already allocated b->rateAlloc pages, let's pause, otherwise
             * switch to sleeping allocations.
             */
            b->slowPageAllocationCycles = SLOW_PAGE_ALLOCATION_CYCLES;

            /* Lower rate for sleeping allocations. */
            rate = b->rateAlloc;
            allocType = BALLOON_PAGE_ALLOC_CANSLEEP;
         } else {
            ASSERT(allocType == BALLOON_PAGE_ALLOC_CANSLEEP);
            /*
             * CANSLEEP page allocation failed, so guest is under severe
             * memory pressure. Quickly decrease allocation rate.
             */
            b->rateAlloc = MAX(b->rateAlloc / 2, BALLOON_RATE_ALLOC_MIN);

            /* Stop allocating any more memory */
            break;
         }

         if (allocations >= b->rateAlloc) {
            break;
         }

         continue;
      }
      allocations++;

      b->balloonOps->addPage(b, nEntries++, handle);
      if (nEntries == b->batchMaxEntries) {
         status = b->balloonOps->lock(b, nEntries, isLargePages, &target);
         nEntries = 0;

         if (status != BALLOON_SUCCESS) {
            break;
         }
      }

      if (allocations % BALLOON_ALLOC_YIELD_THRESHOLD == 0) {
         OS_Yield();
      }

      if (allocations >= rate) {
         /* We allocated enough pages, let's take a break. */
         break;
      }
   }

   if (nEntries > 0) {
      b->balloonOps->lock(b, nEntries, isLargePages, NULL);
   }

   /*
    * We reached our goal without failures so try increasing
    * allocation rate.
    */
   if (status == BALLOON_SUCCESS && allocations >= b->rateAlloc) {
      unsigned int mult = allocations / b->rateAlloc;

      b->rateAlloc = MIN(b->rateAlloc + mult * BALLOON_RATE_ALLOC_INC,
                         BALLOON_RATE_ALLOC_MAX);
   }

   /* release non-balloonable pages, succeed */
   BalloonErrorPagesFree(b);
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonLockBatched --
 *
 *      Lock all the batched page, previously stored by
 *      BalloonAddPageBatched.
 *
 * Results:
 *      BALLOON_SUCCESS or an error code. On success, *target is filled
 *      with the balloon target.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
static int
BalloonLockBatched(Balloon *b,       // IN/OUT
                   uint16 nEntries,  // IN
                   int isLargePages, // IN
                   uint64 *target)   // OUT
{
   int          status;
   uint32       i;
   uint32       nLockedEntries;
   PageHandle   handle;
   PPN64        batchPagePPN;
   BalloonChunk *chunk = NULL;

   batchPagePPN = PA_2_PPN(OS_ReservedPageGetPA(b->pageHandle));

   /*
    * Make sure that we will always have an available chunk before doing
    * the LOCK_BATCHED call.
    */
   ASSERT(b->batchMaxEntries < BALLOON_CHUNK_ENTRIES);
   b->fallbackChunk = BalloonChunk_Create();
   if (b->fallbackChunk == NULL) {
      status = BALLOON_PAGE_ALLOC_FAILURE;
   } else {
      status = Backdoor_MonitorLockPagesBatched(b, batchPagePPN, nEntries,
                                                isLargePages, target);
   }

   if (status != BALLOON_SUCCESS) {
      for (i = 0; i < nEntries; i++) {
         PA64 pa = Balloon_BatchGetPA(b->batchPage, i);
         handle = OS_ReservedPageGetHandle(pa);

         OS_ReservedPageFree(handle, isLargePages);
      }

      goto out;
   }

   nLockedEntries = 0;
   for (i = 0; i < nEntries; i++) {
      PA64              pa;
      int               error;

      pa = Balloon_BatchGetPA(b->batchPage, i);
      handle = OS_ReservedPageGetHandle(pa);
      error = Balloon_BatchGetStatus(b->batchPage, i);
      if (error != BALLOON_SUCCESS) {
         switch (error) {
         case BALLOON_ERROR_PPN_PINNED:
         case BALLOON_ERROR_PPN_INVALID:
            if (BalloonErrorPageStore(b, handle, isLargePages)
                == BALLOON_SUCCESS) {
               break;
            }
            // Fallthrough.
         case BALLOON_ERROR_RESET:
         case BALLOON_ERROR_PPN_NOTNEEDED:
            OS_ReservedPageFree(handle, isLargePages);
            break;
         default:
            /*
             * If we fall here, there is definitively a bug in the
             * driver that needs to be fixed, I'm not sure if
             * PINNED and INVALID PPN can be seen as a bug in the
             * driver.
             */
            ASSERT(FALSE);
         }
         continue;
      }

      if (chunk == NULL) {
         chunk = BalloonGetChunkOrFallback(b, isLargePages);
      }

      BalloonPageStore(chunk, handle);
      if (chunk->nEntries == BALLOON_CHUNK_ENTRIES) {
         chunk = NULL;
      }
      nLockedEntries++;
   }

   if (isLargePages) {
      b->nPages += nLockedEntries * OS_LARGE_2_SMALL_PAGES;
   } else {
      b->nPages += nLockedEntries;
   }

out:
   if (b->fallbackChunk != NULL) {
      BalloonChunk_Destroy(b->fallbackChunk);
      b->fallbackChunk = NULL;
   }

   return status;
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonUnlockBatched --
 *
 *      Unlock all the batched page, previously stored by
 *      BalloonAddPageBatched.
 *
 * Results:
 *      BALLOON_SUCCESS or an error code. On success, *target is filled
 *      with the balloon target.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
static int
BalloonUnlockBatched(Balloon *b,       // IN/OUT
                     uint16 nEntries,  // IN
                     int isLargePages, // IN
                     uint64 *target)   // OUT
{
   uint32 i;
   int status = BALLOON_SUCCESS;
   uint32 nUnlockedEntries;
   PPN64 batchPagePPN;
   BalloonChunk *chunk = NULL;

   batchPagePPN = PA_2_PPN(OS_ReservedPageGetPA(b->pageHandle));
   status = Backdoor_MonitorUnlockPagesBatched(b, batchPagePPN, nEntries,
                                               isLargePages, target);

   if (status != BALLOON_SUCCESS) {
      for (i = 0; i < nEntries; i++) {
         PA64 pa = Balloon_BatchGetPA(b->batchPage, i);
         PageHandle handle = OS_ReservedPageGetHandle(pa);

         chunk = BalloonGetChunkOrFallback(b, isLargePages);
         BalloonPageStore(chunk, handle);
      }
      goto out;
   }

   nUnlockedEntries = 0;
   for (i = 0; i < nEntries; i++) {
      int status = Balloon_BatchGetStatus(b->batchPage, i);
      PA64 pa = Balloon_BatchGetPA(b->batchPage, i);
      PageHandle handle = OS_ReservedPageGetHandle(pa);

      if (status != BALLOON_SUCCESS) {
         chunk = BalloonGetChunkOrFallback(b, isLargePages);
         BalloonPageStore(chunk, handle);
         continue;
      }

      OS_ReservedPageFree(handle, isLargePages);
      STATS_INC(b->stats.primFree[isLargePages]);

      nUnlockedEntries++;
   }

   if (isLargePages) {
      b->nPages -= nUnlockedEntries * OS_LARGE_2_SMALL_PAGES;
   } else {
      b->nPages -= nUnlockedEntries;
   }

out:
   if (b->fallbackChunk != NULL) {
      BalloonChunk_Destroy(b->fallbackChunk);
      b->fallbackChunk = NULL;
   }
   return status;
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonAddPageBatched --
 *
 *      Add a page to the batch page, that will be ballooned later.
 *
 * Results:
 *      Nothing.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonAddPageBatched(Balloon *b,            // IN
                      uint16 idx,            // IN
                      PageHandle page)       // IN
{
   PA64 pa = OS_ReservedPageGetPA(page);
   Balloon_BatchSetPA(b->batchPage, idx, pa);
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonLock --
 *
 *      Lock a page, previously stored with a call to BalloonAddPage,
 *      by notifying the monitor.
 *
 * Results:
 *      BALLOON_SUCCESS or an error code. On success, *target is filled
 *      with the balloon target.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
BalloonLock(Balloon *b,       // IN/OUT
            uint16 nPages,    // IN
            int isLargePage,  // IN
            uint64 *target)   // OUT
{
   PPN64 pagePPN;
   BalloonChunk *chunk;
   int status;

   ASSERT(!isLargePage);

   /* Get the chunk to store allocated page. */
   chunk = BalloonGetChunk(b, FALSE);
   if (chunk == NULL) {
      OS_ReservedPageFree(b->pageHandle, FALSE);
      status = BALLOON_PAGE_ALLOC_FAILURE;
      goto out;
   }

   /* inform monitor via backdoor */
   pagePPN = PA_2_PPN(OS_ReservedPageGetPA(b->pageHandle));
   status = Backdoor_MonitorLockPage(b, pagePPN, target);
   if (status != BALLOON_SUCCESS) {
      int old_status = status;

      /* We need to release the chunk if it was just allocated */
      BalloonChunkDestroyEmpty(b, chunk, isLargePage);

      if (status == BALLOON_ERROR_RESET ||
          status == BALLOON_ERROR_PPN_NOTNEEDED) {
         OS_ReservedPageFree(b->pageHandle, FALSE);
         goto out;
      }

      /* place on list of non-balloonable pages, retry allocation */
      status = BalloonErrorPageStore(b, b->pageHandle, FALSE);
      if (status != BALLOON_SUCCESS) {
         OS_ReservedPageFree(b->pageHandle, FALSE);
         goto out;
      }

      status = old_status;
      goto out;
   }

   /* track allocated page */
   BalloonPageStore(chunk, b->pageHandle);

   /* update balloon size */
   b->nPages++;

out:
   b->pageHandle = PAGE_HANDLE_INVALID;
   return status;
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonUnlock --
 *
 *      Unlock a page, previously stored with a call to BalloonAddPage,
 *      by notifying the monitor.
 *
 * Results:
 *      BALLOON_SUCCESS or an error code. On success, *target is filled
 *      with the balloon target.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
BalloonUnlock(Balloon *b,      // IN/OUT
              uint16 nPages,   // IN
              int isLargePage, // IN
              uint64 *target)  // OUT
{
   PPN64 pagePPN = PA_2_PPN(OS_ReservedPageGetPA(b->pageHandle));
   int status = Backdoor_MonitorUnlockPage(b, pagePPN, target);

   ASSERT(!isLargePage);

   if (status != BALLOON_SUCCESS) {
      BalloonChunk *chunk = BalloonGetChunkOrFallback(b, FALSE);
      BalloonPageStore(chunk, b->pageHandle);
      goto out;
   }

   OS_ReservedPageFree(b->pageHandle, FALSE);
   STATS_INC(b->stats.primFree[FALSE]);

   /* update balloon size */
   b->nPages--;

out:
   b->pageHandle = PAGE_HANDLE_INVALID;
   if (b->fallbackChunk != NULL) {
      BalloonChunk_Destroy(b->fallbackChunk);
      b->fallbackChunk = NULL;
   }
   return status;
}

/*
 *----------------------------------------------------------------------
 *
 * BalloonAddPage --
 *
 *      Add a page to be ballooned later.
 *
 * Results:
 *      Nothing.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonAddPage(Balloon *b,            // IN/OUT
               uint16 idx,            // IN
               PageHandle page)       // IN
{
   ASSERT(b->pageHandle == PAGE_HANDLE_INVALID);
   b->pageHandle = page;
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonDeflateInt --
 *
 *      Frees physical pages of a given size to deflate balloon.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonDeflateInt(Balloon *b,       // IN/OUT
                  uint64 target,    // IN
                  int isLargePages) // IN
{
   int                  status = BALLOON_SUCCESS;
   uint32               nPages, deallocations = 0;
   BalloonChunk         *chunk = NULL;
   BalloonChunkList     *chunkList = &b->pages[isLargePages];

   if (chunkList->nChunks == 0) {
      return;
   }

   nPages = 0;
   while (chunkList->nChunks > 0 && b->nPages > target
          && nPages < b->nPages - target) {
      PageHandle lockedHandle;

      if (chunk == NULL) {
         /*
          * The chunk should never be empty. If it is, then there is a
          * deviation between the guest balloon size, and tracked
          * pages...
          */
         ASSERT(DblLnkLst_IsLinked(&chunkList->chunks));
         chunk = DblLnkLst_Container(chunkList->chunks.next, BalloonChunk,
                                     node);
      }

      lockedHandle = chunk->entries[--chunk->nEntries];
      if (!chunk->nEntries) {
         DblLnkLst_Unlink1(&chunk->node);
         /*
          * Do not free the chunk, we may need it if the UNLOCK cmd fails
          */
         b->fallbackChunk = chunk;

         chunkList->nChunks--;
         chunk = NULL;
      }

      deallocations++;
      b->balloonOps->addPage(b, nPages++, lockedHandle);
      if (nPages == b->batchMaxEntries) {
         status = b->balloonOps->unlock(b, nPages, isLargePages, &target);
         nPages = 0;

         if (status != BALLOON_SUCCESS) {
            break;
         }
      }

      if (deallocations >= b->rateFree) {
         /* We released enough pages, let's take a break. */
         break;
      }
   }

   if (nPages) {
      b->balloonOps->unlock(b, nPages, isLargePages, NULL);
   }

   if (BALLOON_RATE_ADAPT) {
      if (status == BALLOON_SUCCESS) {
         /* slowly increase rate if no errors */
         b->rateFree = MIN(b->rateFree + BALLOON_RATE_FREE_INC,
                           BALLOON_RATE_FREE_MAX);
      } else {
         /* quickly decrease rate if error */
         b->rateFree = MAX(b->rateFree / 2, BALLOON_RATE_FREE_MIN);
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonDeflate --
 *
 *      Frees physical pages to deflate balloon.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonDeflate(Balloon *b,    // IN/OUT
               uint64 target) // IN
{
   /* Prefer to unlock small pages over unlocking large pages */
   BalloonDeflateInt(b, target, FALSE);
   BalloonDeflateInt(b, target, TRUE);
}


/*
 *----------------------------------------------------------------------
 *
 * BalloonAdjustSize --
 *
 *      Attempts to allocate or deallocate physical pages in order
 *      to reach desired "target" size for balloon "b".
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
BalloonAdjustSize(Balloon *b,    // IN/OUT
                  uint64 target) // IN
{
   /*
    * When we only deal with large pages it can happen that we overshoot our
    * target by OS_LARGE_2_SMALL_PAGES - 1 pages. To prevent a constant balloon,
    * unballoon loop, allow the target to be OS_LARGE_2_SMALL_PAGES - 1 pages
    * lower than the actual ballooned amount before we do something.
    */
   if (b->nPages < target) {
      BalloonInflate(b, target);
   } else if (target == 0 || b->nPages > target + OS_LARGE_2_SMALL_PAGES - 1) {
      BalloonDeflate(b, target);
   }
}

/*
 *----------------------------------------------------------------------
 *
 * Balloon_Init --
 *
 *      Initializes state of the balloon.
 *
 * Results:
 *      Returns TRUE on success (always).
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
Balloon_Init(BalloonGuest guestType)    // IN
{
   Balloon *b = &globalBalloon;

   DblLnkLst_Init(&b->pages[TRUE].chunks);
   DblLnkLst_Init(&b->pages[FALSE].chunks);

   b->guestType = guestType;

   /* initialize rates */
   b->rateAlloc = BALLOON_RATE_ALLOC_MAX;
   b->rateFree  = BALLOON_RATE_FREE_MAX;

   /* initialize reset flag */
   b->resetFlag = TRUE;

   b->hypervisorCapabilities = 0;

   b->pageHandle = PAGE_HANDLE_INVALID;
   b->batchPageMapping = MAPPING_INVALID;
   b->batchPage = NULL;

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * Balloon_Cleanup --
 *
 *      Cleanup after ballooning.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------
 */

void
Balloon_Cleanup(void)
{
   Balloon *b = &globalBalloon;

   /*
    * Deallocate all reserved memory, and reset connection with monitor.
    * Reset connection before deallocating memory to avoid potential for
    * additional spurious resets from guest touching deallocated pages.
    */
   Backdoor_MonitorStart(b, BALLOON_CAPABILITIES);
   Balloon_Deallocate(b);
}

#ifdef __cplusplus
}
#endif
 07070100000269000081A40000000000000000000000016822550500001730000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmmemctl/vmballoon.h    /*********************************************************
 * Copyright (C) 2000-2012,2014,2018-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*********************************************************
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vmballoon.h: Definitions and macros for vmballoon driver.
 */

#ifndef	VMBALLOON_H
#define	VMBALLOON_H

#include "balloonInt.h"
#include "vm_basic_types.h"
#include "dbllnklst.h"
#include "os.h"

/*
 * Page allocation flags
 */
typedef enum BalloonPageAllocType {
   BALLOON_PAGE_ALLOC_LPAGE = 0,
   BALLOON_PAGE_ALLOC_NOSLEEP = 1,
   BALLOON_PAGE_ALLOC_CANSLEEP = 2,
   BALLOON_PAGE_ALLOC_TYPES_NR,	// total number of alloc types
} BalloonPageAllocType;


/*
 * Types
 */

typedef struct {
   /* current status */
   uint64 nPages;
   uint64 nPagesTarget;

   /* adjustment rates */
   uint32 rateNoSleepAlloc;
   uint32 rateAlloc;
   uint32 rateFree;

   /* high-level operations */
   uint32 timer;

   /* primitives */
   uint32 primAlloc[BALLOON_PAGE_ALLOC_TYPES_NR];
   uint32 primAllocFail[BALLOON_PAGE_ALLOC_TYPES_NR];
   uint32 primFree[2];
   uint32 primErrorPageAlloc[2];
   uint32 primErrorPageFree[2];

   /* monitor operations */
   uint32 lock[2];
   uint32 lockFail[2];
   uint32 unlock[2];
   uint32 unlockFail[2];
   uint32 target;
   uint32 targetFail;
   uint32 start;
   uint32 startFail;
   uint32 guestType;
   uint32 guestTypeFail;
} BalloonStats;

#define BALLOON_ERROR_PAGES             16

typedef struct {
   PageHandle entries[BALLOON_ERROR_PAGES];
   uint32 nEntries;
} BalloonErrorPages;

#define BALLOON_CHUNK_ENTRIES             1000

typedef struct BalloonChunk {
   PageHandle entries[BALLOON_CHUNK_ENTRIES];
   uint32 nEntries;
   DblLnkLst_Links node;
} BalloonChunk;

struct BalloonOps;

typedef struct {
   DblLnkLst_Links chunks;
   int nChunks;
} BalloonChunkList;

typedef struct {
   /* sets of reserved physical pages */
   BalloonChunkList pages[2];

   /* transient list of non-balloonable pages */
   BalloonErrorPages errors[2];

   BalloonGuest guestType;

   /* balloon size (in small pages) */
   uint64 nPages;

   /* target balloon size (in small pages) */
   uint64 nPagesTarget;

   /* reset flag */
   int resetFlag;

   /* adjustment rates (pages per second) */
   int rateAlloc;
   int rateFree;

   /* slowdown page allocations for next few cycles */
   int slowPageAllocationCycles;

   /* statistics */
   BalloonStats stats;

   /* hypervisor exposed capabilities */
   BalloonCapabilities hypervisorCapabilities;

   /* balloon operations, tied to the capabilities */
   const struct BalloonOps *balloonOps;

   /* Either the batch page handle, or the page to lock on v2 */
   PageHandle pageHandle;
   Mapping batchPageMapping;
   BalloonBatchPage *batchPage;
   uint16 batchMaxEntries;

   BalloonChunk *fallbackChunk;
} Balloon;

typedef struct BalloonOps {
   void (*addPage)(Balloon *b, uint16 idx, PageHandle entries);
   int (*lock)(Balloon *b, uint16 nPages, int isLargePages, uint64 *target);
   int (*unlock)(Balloon *b, uint16 nPages, int isLargePages, uint64 *target);
} BalloonOps;

/*
 * Operations
 */

Bool Balloon_Init(BalloonGuest guestType);
void Balloon_Cleanup(void);

void Balloon_QueryAndExecute(void);

const BalloonStats *Balloon_GetStats(void);

#endif	/* VMBALLOON_H */
0707010000026A000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet  0707010000026B000081A400000000000000000000000168225505000089A5000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet/eth_public.h /*********************************************************
 * Copyright (C) 2005-2014,2017-2020,2021 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
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * eth.h  --
 *
 *    Virtual ethernet.
 */

#ifndef _ETH_PUBLIC_H_
#define _ETH_PUBLIC_H_

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMK_MODULE

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "vm_basic_defs.h"
#include "vm_assert.h"

#if defined __cplusplus
extern "C" {
#endif


#define ETH_LADRF_LEN      2
#define ETH_ADDR_LENGTH    6

typedef uint8 Eth_Address[ETH_ADDR_LENGTH];

// printf helpers
#define ETH_ADDR_FMT_STR     "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
#define ETH_ADDR_FMT_ARGS(a) ((uint8 *)a)[0], ((uint8 *)a)[1], ((uint8 *)a)[2], \
                             ((uint8 *)a)[3], ((uint8 *)a)[4], ((uint8 *)a)[5]
#define ETH_ADDR_PTR_FMT_ARGS(a) &((uint8 *)a)[0], &((uint8 *)a)[1], \
                                 &((uint8 *)a)[2], &((uint8 *)a)[3], \
                                 &((uint8 *)a)[4], &((uint8 *)a)[5]

#define ETH_MAX_EXACT_MULTICAST_ADDRS 32

typedef enum Eth_RxMode {
   ETH_FILTER_UNICAST   = 0x0001,   // pass unicast (directed) frames
   ETH_FILTER_MULTICAST = 0x0002,   // pass some multicast frames
   ETH_FILTER_ALLMULTI  = 0x0004,   // pass *all* multicast frames
   ETH_FILTER_BROADCAST = 0x0008,   // pass broadcast frames
   ETH_FILTER_PROMISC   = 0x0010,   // pass all frames (ie no filter)
   ETH_FILTER_USE_LADRF = 0x0020,   // use the LADRF for multicast filtering
   ETH_FILTER_SINK      = 0x10000   // pass not-matched unicast frames
} Eth_RxMode;

// filter flags printf helpers
#define ETH_FILTER_FLAG_FMT_STR     "%s%s%s%s%s%s%s"
#define ETH_FILTER_FLAG_FMT_ARGS(f) (f) & ETH_FILTER_UNICAST   ? "  UNICAST"   : "", \
                                    (f) & ETH_FILTER_MULTICAST ? "  MULTICAST" : "", \
                                    (f) & ETH_FILTER_ALLMULTI  ? "  ALLMULTI"  : "", \
                                    (f) & ETH_FILTER_BROADCAST ? "  BROADCAST" : "", \
                                    (f) & ETH_FILTER_PROMISC   ? "  PROMISC"   : "", \
                                    (f) & ETH_FILTER_USE_LADRF ? "  USE_LADRF" : "", \
                                    (f) & ETH_FILTER_SINK      ? "  SINK"      : ""

// Ethernet header type
typedef enum {
   ETH_HEADER_TYPE_DIX,
   ETH_HEADER_TYPE_802_1PQ,
   ETH_HEADER_TYPE_802_3,
   ETH_HEADER_TYPE_802_1PQ_802_3,
   ETH_HEADER_TYPE_NESTED_802_1PQ,
} Eth_HdrType;

// DIX type fields we care about
typedef enum {
   ETH_TYPE_IPV4        = 0x0800,
   ETH_TYPE_IPV6        = 0x86DD,
   ETH_TYPE_ARP         = 0x0806,
   ETH_TYPE_RARP        = 0x8035,
   ETH_TYPE_LLDP        = 0x88CC,
   ETH_TYPE_CDP         = 0x2000,
   ETH_TYPE_AKIMBI      = 0x88DE,
   ETH_TYPE_VMWARE      = 0x8922,
   ETH_TYPE_1588        = 0x88F7,
   ETH_TYPE_NSH         = 0x894F,
   ETH_TYPE_802_1PQ     = 0x8100,  // not really a DIX type, but used as such
   ETH_TYPE_QINQ        = 0x88A8,
   ETH_TYPE_PAE         = 0x888e,  /* EAPOL PAE/802.1x */
   ETH_TYPE_LLC         = 0xFFFF,  // 0xFFFF is IANA reserved, used to mark LLC
} Eth_DixType;
typedef enum {
   ETH_TYPE_IPV4_NBO    = 0x0008,
   ETH_TYPE_IPV6_NBO    = 0xDD86,
   ETH_TYPE_ARP_NBO     = 0x0608,
   ETH_TYPE_RARP_NBO    = 0x3580,
   ETH_TYPE_LLDP_NBO    = 0xCC88,
   ETH_TYPE_CDP_NBO     = 0x0020,
   ETH_TYPE_AKIMBI_NBO  = 0xDE88,
   ETH_TYPE_VMWARE_NBO  = 0x2289,
   ETH_TYPE_1588_NBO    = 0xF788,
   ETH_TYPE_NSH_NBO     = 0x4F89,
   ETH_TYPE_802_1PQ_NBO = 0x0081,  // not really a DIX type, but used as such
   ETH_TYPE_QINQ_NBO    = 0xA888,
   ETH_TYPE_PAE_NBO     = 0x8e88,   /* EAPOL PAE/802.1x */
   ETH_TYPE_802_3_PAUSE_NBO = 0x0888,  // pause frame based ethernet flow control
} Eth_DixTypeNBO;

// low two bits of the LLC control byte
typedef enum {
   ETH_LLC_CONTROL_IFRAME  = 0x0, // both 0x0 and 0x2, only low bit of 0 needed
   ETH_LLC_CONTROL_SFRAME  = 0x1,
   ETH_LLC_CONTROL_UFRAME  = 0x3,
} Eth_LLCControlBits;

#define ETH_LLC_CONTROL_UFRAME_MASK (0x3)

#pragma pack(push, 1)
typedef struct Eth_DIX {
   uint16  typeNBO;     // indicates the higher level protocol
} Eth_DIX;
#pragma pack(pop)

/*
 * LLC header come in two varieties:  8 bit control and 16 bit control.
 * when the lower two bits of the first byte's control are '11', this
 * indicated the 8 bit control field.
 */

#pragma pack(push, 1)
typedef struct Eth_LLC8 {
   uint8   dsap;
   uint8   ssap;
   uint8   control;
} Eth_LLC8;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Eth_LLC16 {
   uint8   dsap;
   uint8   ssap;
   uint16  control;
} Eth_LLC16;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Eth_SNAP {
   uint8   snapOrg[3];
   Eth_DIX snapType;
} Eth_SNAP;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Eth_802_3 {  
   uint16   lenNBO;      // length of the frame
   Eth_LLC8 llc;         // LLC header
   Eth_SNAP snap;        // SNAP header
} Eth_802_3;
#pragma pack(pop)

// 802.1p QOS/priority tags
// 
enum {
   ETH_802_1_P_BEST_EFFORT          = 0,
   ETH_802_1_P_BACKGROUND           = 1,
   ETH_802_1_P_EXCELLENT_EFFORT     = 2,
   ETH_802_1_P_CRITICAL_APPS        = 3,
   ETH_802_1_P_VIDEO                = 4,
   ETH_802_1_P_VOICE                = 5,
   ETH_802_1_P_INTERNETWORK_CONROL  = 6,
   ETH_802_1_P_NETWORK_CONTROL      = 7
};

#pragma pack(push, 1)
typedef struct Eth_802_1pq_Tag {
   uint16 typeNBO;            // always ETH_TYPE_802_1PQ
   uint16 vidHi:4,            // 802.1q vlan ID high nibble
          canonical:1,        // bit order? (should always be 0)
          priority:3,         // 802.1p priority tag
          vidLo:8;            // 802.1q vlan ID low byte
} Eth_802_1pq_Tag;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Eth_802_1pq {
   Eth_802_1pq_Tag tag;       // VLAN/QOS tag
   union {
      Eth_DIX      dix;       // DIX header follows
      Eth_802_3    e802_3;    // or 802.3 header follows 
      struct {
         Eth_802_1pq_Tag   tag;        // inner VLAN/QOS tag
         Eth_DIX           dix;        // DIX header follows
      } nested802_1pq;
   }; 
} Eth_802_1pq; 
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Eth_Header {
   Eth_Address     dst;       // all types of ethernet frame have dst first
   Eth_Address     src;       // and the src next (at least all the ones we'll see)
   union {
      Eth_DIX      dix;       // followed by a DIX header...
      Eth_802_3    e802_3;    // ...or an 802.3 header
      Eth_802_1pq  e802_1pq;  // ...or an 802.1[pq] tag and a header
   };
} Eth_Header;
#pragma pack(pop)

/*
 * Official VMware ethertype header format and types
 */
#define ETH_VMWARE_FRAME_MAGIC   0x026f7564

enum {
   ETH_VMWARE_FRAME_TYPE_INVALID    = 0,
   ETH_VMWARE_FRAME_TYPE_BEACON     = 1,
   ETH_VMWARE_FRAME_TYPE_COLOR      = 2,
   ETH_VMWARE_FRAME_TYPE_ECHO       = 3,
   ETH_VMWARE_FRAME_TYPE_LLC        = 4, // XXX: Just re-use COLOR?
};

#pragma pack(push, 1)
typedef struct Eth_VMWareFrameHeader {
   uint32         magic;
   uint16         lenNBO;
   uint8          type;
} Eth_VMWareFrameHeader;
#pragma pack(pop)

typedef Eth_Header Eth_802_1pq_Header; // for sizeof

#define ETH_BROADCAST_ADDRESS { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }

extern Eth_Address netEthBroadcastAddr;

/*
 * simple predicate for 1536 boundary.
 * the parameter is a network ordered uint16, which is compared to 0x06,
 * testing for "length" values greater than or equal to 0x0600 (1536)
 */

#define ETH_TYPENOT8023(x)      (((x) & 0xff) >= 0x06)

/*
 * header length macros
 *
 * first two are typical: ETH_HEADER_LEN_DIX, ETH_HEADER_LEN_802_1PQ
 * last two are suspicious, due to 802.3 incompleteness
 */

#define ETH_HEADER_LEN_DIX           (sizeof(Eth_Address) + \
                                      sizeof(Eth_Address) + \
                                      sizeof(Eth_DIX))
#define ETH_HEADER_LEN_802_1PQ       (sizeof(Eth_Address) + \
                                      sizeof(Eth_Address) + \
                                      sizeof(Eth_802_1pq_Tag) + \
                                      sizeof(Eth_DIX))
#define ETH_HEADER_LEN_802_2_LLC     (sizeof(Eth_Address) + \
                                      sizeof(Eth_Address) + \
                                      sizeof(uint16) + \
                                      sizeof(Eth_LLC8))
#define ETH_HEADER_LEN_802_2_LLC16   (sizeof(Eth_Address) + \
                                      sizeof(Eth_Address) + \
                                      sizeof(uint16) + \
                                      sizeof(Eth_LLC16))
#define ETH_HEADER_LEN_802_3         (sizeof(Eth_Address) + \
                                      sizeof(Eth_Address) + \
                                      sizeof(Eth_802_3))
#define ETH_HEADER_LEN_802_1PQ_LLC   (sizeof(Eth_Address) + \
                                      sizeof(Eth_Address) + \
                                      sizeof(Eth_802_1pq_Tag) + \
                                      sizeof(uint16) + \
                                      sizeof(Eth_LLC8))
#define ETH_HEADER_LEN_802_1PQ_LLC16 (sizeof(Eth_Address) + \
                                      sizeof(Eth_Address) + \
                                      sizeof(Eth_802_1pq_Tag) + \
                                      sizeof(uint16) + \
                                      sizeof(Eth_LLC16))
#define ETH_HEADER_LEN_802_1PQ_802_3 (sizeof(Eth_Address) + \
                                      sizeof(Eth_Address) + \
                                      sizeof(Eth_802_1pq_Tag) + \
                                      sizeof(Eth_802_3))
#define ETH_HEADER_LEN_NESTED_802_1PQ  (sizeof(Eth_Address) + \
                                        sizeof(Eth_Address) + \
                                        sizeof(Eth_802_1pq_Tag) + \
                                        sizeof(Eth_802_1pq_Tag) + \
                                        sizeof(Eth_DIX))

#define ETH_MIN_HEADER_LEN   (ETH_HEADER_LEN_DIX)
#define ETH_MAX_HEADER_LEN   (ETH_HEADER_LEN_802_1PQ_802_3)

#define ETH_MIN_FRAME_LEN                    60
#define ETH_MAX_STD_MTU                      1500
#define ETH_MAX_STD_FRAMELEN                 (ETH_MAX_STD_MTU + ETH_MAX_HEADER_LEN)
/*
 * ENS_MBUF_SLAB_9K_ALLOC_SIZE and PKT_SLAB_JUMBO_SIZE both use 9216 for L2
 * MTU. And ETH_MAX_JUMBO_MTU is L3 MTU. It is required to have
 * (ETH_MAX_JUMBO_MTU + ETH_MAX_HEADER_LEN) <= 9216 and ETH_MAX_HEADER_LEN is
 * 26. So the maximal possible ETH_MAX_JUMBO_MTU = 9216 - 26 = 9190.
 */
#define ETH_MAX_JUMBO_MTU                    9190
#define ETH_MAX_JUMBO_FRAMELEN               (ETH_MAX_JUMBO_MTU + ETH_MAX_HEADER_LEN)

#define ETH_DEFAULT_MTU                      1500

#define ETH_FCS_LEN                          4
#define ETH_VLAN_LEN                         sizeof(Eth_802_1pq_Tag)

/*
 *----------------------------------------------------------------------
 *
 * Eth_IsAddrMatch --
 *
 *      Do the two ethernet addresses match?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsAddrMatch(const Eth_Address addr1, const Eth_Address addr2)
{
#ifdef __GNUC__
   /* string.h may not exist for kernel modules, so cannot use memcmp() */
   return !__builtin_memcmp(addr1, addr2, ETH_ADDR_LENGTH);
#else
   return !memcmp(addr1, addr2, ETH_ADDR_LENGTH);
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsBroadcastAddr --
 *
 *      Is the address the broadcast address?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsBroadcastAddr(const Eth_Address addr) 
{
   return Eth_IsAddrMatch(addr, netEthBroadcastAddr);
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsUnicastAddr --
 *
 *      Is the address a unicast address?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsUnicastAddr(const Eth_Address addr) 
{
   // broadcast and multicast frames always have the low bit set in byte 0 
   return !(((char *)addr)[0] & 0x1);
}

/*
 *----------------------------------------------------------------------
 *
 * Eth_IsNullAddr --
 *
 *      Is the address the all-zeros address?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsNullAddr(const Eth_Address addr) 
{
   return ((addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]) == 0);
}

/*
 *----------------------------------------------------------------------
 *
 * Eth_HeaderType --
 *
 *      return an Eth_HdrType depending on the eth header
 *      contents.  will not work in all cases, especially since it
 *      requres ETH_HEADER_LEN_802_1PQ bytes to determine the type
 *
 *      HeaderType isn't sufficient to determine the length of
 *      the eth header.  for 802.3 header, its not clear without
 *      examination, whether a SNAP is included
 *
 *      returned type:
 *
 *      ETH_HEADER_TYPE_DIX: typical 14 byte eth header
 *      ETH_HEADER_TYPE_802_1PQ: DIX+vlan tagging
 *      ETH_HEADER_TYPE_NESTED_802_1PQ: DIX+vlan tagging+DIX+vlan tagging
 *      ETH_HEADER_TYPE_802_3: 802.3 eth header
 *      ETH_HEADER_TYPE_802_1PQ_802_3: 802.3 + vlan tag 
 *
 *      the test for DIX was moved from a 1500 boundary to a 1536
 *      boundary, since the vmxnet2 MTU was updated to 1514.  when
 *      W2K8 attempted to send LLC frames, these were interpreted
 *      as DIX frames instead of the correct 802.3 type
 *
 *      these links may help if they're valid:
 *
 *      http://standards.ieee.org/regauth/ethertype/type-tut.html
 *      http://standards.ieee.org/regauth/ethertype/type-pub.html
 *      
 *
 * Results: 
 *	Eth_HdrType value
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Eth_HdrType
Eth_HeaderType(const Eth_Header *eh)
{
   /*
    * we use 1536 (IEEE 802.3-std mentions 1536, but iana indicates
    * type of 0-0x5dc are 802.3) instead of some #def symbol to prevent
    * inadvertant reuse of the same macro for buffer size decls.
    */

   if (ETH_TYPENOT8023(eh->dix.typeNBO)) {
      if (eh->dix.typeNBO != ETH_TYPE_802_1PQ_NBO) {

         /*
          * typical case
          */

         return ETH_HEADER_TYPE_DIX;
      } 

      /*
       * some type of 802.1pq tagged frame
       */

      if (ETH_TYPENOT8023(eh->e802_1pq.dix.typeNBO)) {

         /*
          * vlan tagging with dix style type
          */

         if (UNLIKELY(eh->e802_1pq.dix.typeNBO == ETH_TYPE_802_1PQ_NBO)) {
            return ETH_HEADER_TYPE_NESTED_802_1PQ;
         }

         return ETH_HEADER_TYPE_802_1PQ;
      }

      /*
       * vlan tagging with 802.3 header
       */

      return ETH_HEADER_TYPE_802_1PQ_802_3;
   }
   
   /*
    * assume 802.3
    */

   return ETH_HEADER_TYPE_802_3;
}

/*
 *----------------------------------------------------------------------
 *
 * Eth_EncapsulatedPktType --
 *
 *      Get the encapsulated (layer 3) frame type. 
 *      for LLC frames without SNAP, we don't have
 *      an encapsulated type, and return ETH_TYPE_LLC.
 *
 *      IANA reserves 0xFFFF, which we reuse to indicate
 *      ETH_TYPE_LLC.  
 *      
 *
 * Results: 
 *	NBO frame type.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE uint16
Eth_EncapsulatedPktType(const Eth_Header *eh)
{
   Eth_HdrType type = Eth_HeaderType(eh);  

   switch (type) {
   case ETH_HEADER_TYPE_DIX :
      return eh->dix.typeNBO;

   case ETH_HEADER_TYPE_802_1PQ :
      return eh->e802_1pq.dix.typeNBO;

   case ETH_HEADER_TYPE_NESTED_802_1PQ:
      return eh->e802_1pq.nested802_1pq.dix.typeNBO;

   case ETH_HEADER_TYPE_802_3 :
      /*
       * Documentation describes SNAP headers as having ONLY
       * 0x03 as the control fields, not just the lower two bits
       * This prevents the use of Eth_IsLLCControlUFormat.
       */
      if ((eh->e802_3.llc.dsap == 0xaa) &&
           (eh->e802_3.llc.ssap == 0xaa) &&
           (eh->e802_3.llc.control == ETH_LLC_CONTROL_UFRAME)) {
               return eh->e802_3.snap.snapType.typeNBO;
      } else {
         // LLC, no snap header, then no type
         return ETH_TYPE_LLC;
      }

   case ETH_HEADER_TYPE_802_1PQ_802_3 :
      if ((eh->e802_1pq.e802_3.llc.dsap == 0xaa) &&
           (eh->e802_1pq.e802_3.llc.ssap == 0xaa) &&
           (eh->e802_1pq.e802_3.llc.control == ETH_LLC_CONTROL_UFRAME)) {
               return eh->e802_1pq.e802_3.snap.snapType.typeNBO;
      } else {
         // tagged LLC, no snap header, then no type
         return ETH_TYPE_LLC;
      }
   }

   ASSERT(FALSE);
   return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * Eth_IsDixType --
 *
 *      Is the frame of the requested protocol type or is it an 802.1[pq]
 *      encapsulation of such a frame?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsDixType(const Eth_Header *eh, const Eth_DixTypeNBO type) 
{
   return Eth_EncapsulatedPktType(eh) == type;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsBeaconSap --
 *
 *      test to validate a frame to determine if its a beacon
 *      (ncp) frame.  sap is passed in.
 *
 *      ncp beacon/color frames are LLC frames with a DSAP/SSAP
 *      set based on a config value.  non-zero llc length is 
 *      tested here to prevent the predicate from interfering
 *      with testworld etherswitch tests
 *
 *
 * Results:
 *      TRUE or FALSE.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsBeaconSap(const Eth_Header *eh, const uint8 sap)
{
   Eth_HdrType type = Eth_HeaderType(eh);

   if (type == ETH_HEADER_TYPE_802_3) {
      if ((eh->e802_3.llc.dsap == sap) && (eh->e802_3.llc.ssap == sap)) {
         if (eh->e802_3.lenNBO != 0) {
            return TRUE;
         }
      }
   } else if (type == ETH_HEADER_TYPE_802_1PQ_802_3) {
      if ((eh->e802_1pq.e802_3.llc.dsap == sap) &&
          (eh->e802_1pq.e802_3.llc.ssap == sap)) {
            if (eh->e802_1pq.e802_3.lenNBO != 0) {
               return TRUE;
            }
      }
   }
   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsIPV4 --
 *
 *      Is the frame an IPV4 frame?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsIPV4(const Eth_Header *eh) 
{
   return Eth_IsDixType(eh, ETH_TYPE_IPV4_NBO);
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsIPV6 --
 *
 *      Is the frame an IPV6 frame?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsIPV6(const Eth_Header *eh) 
{
   return Eth_IsDixType(eh, ETH_TYPE_IPV6_NBO);
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsVMWare --
 *
 *      Is the frame a VMWare frame?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsVMWare(const Eth_Header *eh) 
{
   return Eth_IsDixType(eh, ETH_TYPE_VMWARE_NBO);
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsARP --
 *
 *      Is the frame an ARP frame?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsARP(const Eth_Header *eh) 
{
   return Eth_IsDixType(eh, ETH_TYPE_ARP_NBO);
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsFrameTagged --
 *
 *      Does the frame contain an 802.1[pq] tag?
 *
 * Results: 
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsFrameTagged(const Eth_Header *eh) 
{
   return (eh->dix.typeNBO == ETH_TYPE_802_1PQ_NBO);
}

/*
 *----------------------------------------------------------------------
 *
 * Eth_IsPauseFrame --
 *
 *      Is the frame 802.3 pause frame ?
 *
 * Results:
 *	TRUE or FALSE.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsPauseFrame(const Eth_Header *eh)
{
   return (eh->dix.typeNBO == ETH_TYPE_802_3_PAUSE_NBO);
}

/*
 *----------------------------------------------------------------------
 *
 * Eth_FillVlanTag --
 *
 *      Populate the fields of a vlan tag
 *
 * Results: 
 *	The populated vlan tag
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Eth_802_1pq_Tag *
Eth_FillVlanTag(Eth_802_1pq_Tag *tag,
                const uint32 vlanId,
                const uint32 priority)
{

   ASSERT(vlanId < 4096);
   ASSERT(priority < 8);

   tag->typeNBO = ETH_TYPE_802_1PQ_NBO;
   tag->priority = (uint16)priority;
   tag->canonical = 0;                  // bit order (should be 0)
   tag->vidHi = vlanId >> 8;
   tag->vidLo = vlanId & 0xff;


   return tag;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_VlanTagGetVlanID --
 *
 *      Extract the VLAN ID from the vlanTag.
 *
 * Results: 
 *	The VLAN ID.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE uint16
Eth_VlanTagGetVlanID(const Eth_802_1pq_Tag *tag)
{
   return (tag->vidHi << 8) | tag->vidLo;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_FrameGetVlanID --
 *
 *      Extract the VLAN ID from the frame's 802.1[pq] tag.
 *
 * Results: 
 *	The VLAN ID.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE uint16
Eth_FrameGetVlanID(const Eth_Header *eh) 
{
   ASSERT(Eth_IsFrameTagged(eh));

   return Eth_VlanTagGetVlanID(&eh->e802_1pq.tag);
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_FrameSetVlanID --
 *
 *      Set the VLAN ID in the frame's 802.1[pq] tag.
 *
 * Results: 
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
Eth_FrameSetVlanID(Eth_Header *eh, uint16 vid) 
{
   ASSERT(Eth_IsFrameTagged(eh));

   eh->e802_1pq.tag.vidHi = vid >> 8;
   eh->e802_1pq.tag.vidLo = vid & 0xff;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_FrameGetPriority --
 *
 *      Extract the priority from the frame's 802.1[pq] tag.
 *
 * Results: 
 *	The priority.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE uint8
Eth_FrameGetPriority(const Eth_Header *eh) 
{
   ASSERT(Eth_IsFrameTagged(eh));

   return (uint8)eh->e802_1pq.tag.priority;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_FrameSetPriority --
 *
 *      Set the priority in the frame's 802.1[pq] tag.
 *
 * Results: 
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void
Eth_FrameSetPriority(Eth_Header *eh, const uint8 prio) 
{
   ASSERT(Eth_IsFrameTagged(eh));
   ASSERT(prio <= 7);

   eh->e802_1pq.tag.priority = prio;
}


/*
 *----------------------------------------------------------------------
 * Eth_IsLLCControlUFormat --
 *
 *      The LLC Control fields determines the lengeth of the LLC
 *      field, selecting 8 bit of 16 bit.  Thies predicate indicates
 *      whether the LLC frame is a U-Format frames.  The U-Format
 *      frame is the only LLC frame header which is 8 bits long.  
 *
 * Results
 *      Bool
 *      
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsLLCControlUFormat(const uint8 control)
{
   return (control & ETH_LLC_CONTROL_UFRAME_MASK) == ETH_LLC_CONTROL_UFRAME;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_HeaderLength_802_3 --
 *
 *      Returns the length of an 802_3 eth header without
 *      any vlan tagging.  factored out for Eth_HeaderComplete()
 *
 * Results:
 *      uint16 length
 *
 * Side Effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
static INLINE uint16
Eth_HeaderLength_802_3(const Eth_Header *eh)
{
   /*
    * Documentation describes SNAP headers as having ONLY
    * 0x03 as the control fields, not just the lower two bits
    * This prevents the use of Eth_IsLLCControlUFormat.
    */
   if ((eh->e802_3.llc.dsap == 0xaa) &&
        (eh->e802_3.llc.ssap == 0xaa) &&
        (eh->e802_3.llc.control == ETH_LLC_CONTROL_UFRAME)) {
            return ETH_HEADER_LEN_802_3;
   }
   // LLC, no snap header
   if (Eth_IsLLCControlUFormat(eh->e802_3.llc.control)) {
      return ETH_HEADER_LEN_802_2_LLC;
   }
   // Eth_LLC with a two byte control field
   return ETH_HEADER_LEN_802_2_LLC16;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_HeaderLength_802_1PQ_802_3 --
 *
 *      Returns the length of an 802_3 eth header with 
 *      vlan tagging.  factored out for Eth_HeaderComplete()
 *
 * Results:
 *      uint16 length
 *
 * Side Effects:
 *      None.
 *
 *
 *----------------------------------------------------------------------
 */
static INLINE uint16
Eth_HeaderLength_802_1PQ_802_3(const Eth_Header *eh)
{
   if ((eh->e802_1pq.e802_3.llc.dsap == 0xaa) &&
        (eh->e802_1pq.e802_3.llc.ssap == 0xaa) &&
        (eh->e802_1pq.e802_3.llc.control == ETH_LLC_CONTROL_UFRAME)) {
            return ETH_HEADER_LEN_802_1PQ_802_3;
   }
   // tagged LLC, no snap header
   if (Eth_IsLLCControlUFormat(eh->e802_1pq.e802_3.llc.control)) {
      return ETH_HEADER_LEN_802_1PQ_LLC;
   }
   // Eth_LLC with a two byte control field
   return ETH_HEADER_LEN_802_1PQ_LLC16;
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_HeaderLength --
 *
 *      Return the length of the header, taking into account
 *      different header variations.  for LLC headers,  determine
 *      whether the eth header has an associated SNAP header,
 *
 *      all references to the eth header assume the complete
 *      frame is mapped withing the frame mapped length
 *
 *      To determine the length, 17 bytes must be readable.
 *      LLC requires three bytes (after the 802.3 length) to 
 *      identify SNAP frames.
 *
 *      when the header isn't complete:
 *      Eth_HeaderType needs ETH_HEADER_LEN_DIX + sizeof(Eth_802_1pq_Tag)
 *      to completely distinguish types (18 bytes), 
 *      Eth_HeaderType correctly identifies basic untagged DIX frames
 *      with ETH_HEADER_LEN_DIX (14 bytes) bytes.
 *      Eth_HeaderLength will correctly return length of untagged LLC frames 
 *      with ETH_HEADER_LEN_DIX + sizeof(Eth_LLC) (17 bytes), but if the
 *      frame is tagged, it will need
 *      ETH_HEADER_LEN_DIX + sizeof(Eth_802_1pq_Tag) + sizeof(Eth_LLC)
 *      (21 bytes)
 *
 *
 * Results:
 *      uint16 size of the header
 *
 * Side Effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE uint16
Eth_HeaderLength(const Eth_Header *eh)
{
   Eth_HdrType type = Eth_HeaderType(eh);  

   switch (type) {
   case ETH_HEADER_TYPE_DIX :
      return ETH_HEADER_LEN_DIX;

   case ETH_HEADER_TYPE_802_1PQ :
      return ETH_HEADER_LEN_802_1PQ;

   case ETH_HEADER_TYPE_NESTED_802_1PQ :
      return ETH_HEADER_LEN_NESTED_802_1PQ;

   case ETH_HEADER_TYPE_802_3 :
      return Eth_HeaderLength_802_3(eh);

   case ETH_HEADER_TYPE_802_1PQ_802_3 :
      return Eth_HeaderLength_802_1PQ_802_3(eh);
   }
   
   ASSERT(FALSE);
   return 0;   
}

/*
 *----------------------------------------------------------------------
 *
 * Eth_GetPayloadWithLen --
 *
 *      Simple cover to comput the payload's address given the length
 *
 * Results:
 *      pointer to the frame's payload
 *
 * Side Effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void *
Eth_GetPayloadWithLen(const void *frame, const uint16 ehHdrLen)
{
   return (void *)((uint8 *)frame + ehHdrLen);
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_GetPayload --
 *
 *      Return the address of the frame's payload, taking into account
 *      different header variations.  Code assumes a complete ether
 *      header is mapped at the frame, assumption exists due to
 *      the use of Eth_HeaderLength, which isn't provided with
 *      the frame's length (how much data it can examine to determine
 *      the ethernet header length)
 *
 * Results:
 *      pointer to the frame's payload
 *
 * Side Effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE void *
Eth_GetPayload(const void *frame)
{
   return Eth_GetPayloadWithLen(frame, Eth_HeaderLength((Eth_Header *)frame));
}


/*
 *----------------------------------------------------------------------
 *
 * Eth_IsFrameHeaderComplete -- 
 *
 *      Predicate to determine whether the frame has enough length
 *      to contain a complete eth header of the correct type.
 *      If you already know/expect the length to exceed
 *      ETH_MAX_HEADER_LEN then for performance reasons you should
 *      explicitly check for that before calling this function.
 *
 * Results:
 *      returns true when a complete eth header is available
 *
 * Side Effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE Bool
Eth_IsFrameHeaderComplete(const Eth_Header *eh,
                          const uint32 len,
                          uint16 *ehHdrLen)
{
   uint16 ehLen;

   if (UNLIKELY(eh == NULL)) {
      return FALSE;
   }
   /*
    * Hard to know what's the optimal order for these checks
    * perform the most likely case first.
    * Since its not directly obvious, hex 0x06 -> 1536, 
    * (see ETH_TYPENOT8023() for 1536 details)
    */
   if (LIKELY((len >= ETH_HEADER_LEN_DIX) &&
              ETH_TYPENOT8023(eh->dix.typeNBO) &&
              (eh->dix.typeNBO != ETH_TYPE_802_1PQ_NBO))) {
   
      if (ehHdrLen != NULL) {
         *ehHdrLen = ETH_HEADER_LEN_DIX;
      }
      return TRUE;
   }
   if (len >= ETH_HEADER_LEN_802_1PQ) {
      /*
       * Eth_HeaderType will correctly enumerate all types once
       * at least ETH_HEADER_LEN_802_1PQ bytes are available except nested
       * 802.1pq tag.
       */
      Eth_HdrType type = Eth_HeaderType(eh);

      switch (type) {
      case ETH_HEADER_TYPE_802_1PQ:
         if (ehHdrLen != NULL) {
            *ehHdrLen = ETH_HEADER_LEN_802_1PQ;
         }
         return TRUE;

      case ETH_HEADER_TYPE_NESTED_802_1PQ:
         if (ehHdrLen != NULL) {
            *ehHdrLen = ETH_HEADER_LEN_NESTED_802_1PQ;
         }
         return len >= ETH_HEADER_LEN_NESTED_802_1PQ;

      case ETH_HEADER_TYPE_802_3:
         /*
          * Length could be shorter LLC or LLC+SNAP.
          * ETH_HEADER_LEN_802_2_LLC bytes are needed to disambiguate.
          * note: ASSERT_ON_COMPILE fails windows builds.
          */
         ASSERT(ETH_HEADER_LEN_802_1PQ > ETH_HEADER_LEN_802_2_LLC);
         ehLen = Eth_HeaderLength_802_3(eh);
         /* continue to common test */
         break;

      case ETH_HEADER_TYPE_802_1PQ_802_3:
         if (len < ETH_HEADER_LEN_802_1PQ_LLC) {
            return FALSE;
         }
         ehLen = Eth_HeaderLength_802_1PQ_802_3(eh);
         /* continue to common test */
         break;

      default:
         /*
          * This else clause is unreachable, but if removed
          * compiler complains about ehLen possibly being
          * uninitialized.  this else is marginally preferred
          * over an unnecessary initialization
          */
         ASSERT(type != ETH_HEADER_TYPE_DIX);
         // NOT_REACHED()
         return FALSE;
      }

      /* common test */
      if (len >= ehLen) {
         if (ehHdrLen != NULL) {
            *ehHdrLen = ehLen;
         }
         return TRUE;
      }
      return FALSE;
   }

   /*
    * Corner case...  not enough len to use Eth_HeaderType,
    * since len is shorter than ETH_HEADER_LEN_802_1PQ bytes. 
    * but with ETH_HEADER_LEN_802_2_LLC bytes, and an 802.3
    * frame, a U Format LLC frame indicates TRUE 
    * with a header length of ETH_HEADER_LEN_802_2_LLC bytes.
    *
    * The additional test for ETH_TYPENOT8023() is necessary
    * for the case where the dix frame failed due to the
    * vlan tagging test early in this procedure.
    */
   if ((len == ETH_HEADER_LEN_802_2_LLC) &&
       !ETH_TYPENOT8023(eh->dix.typeNBO) &&
       Eth_IsLLCControlUFormat(eh->e802_3.llc.control)) {

      if (ehHdrLen != NULL) {
         *ehHdrLen = ETH_HEADER_LEN_802_2_LLC;
      }
      return TRUE;
   }
   return FALSE;
}


#if defined __cplusplus
} // extern "C"
#endif

#endif // _ETH_PUBLIC_H_
   0707010000026C000081A400000000000000000000000168225505000019CF000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet/net.h    /*********************************************************
 * Copyright (C) 1998-2019,2024 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
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/************************************************************
 *
 *   net.h
 *
 *   This file should contain all network global defines.
 *   No vlance/vmxnet/vnet/vmknet specific stuff should be
 *   put here only defines used/usable by all network code.
 *   --gustav
 *
 ************************************************************/

#ifndef VMWARE_DEVICES_NET_H
#define VMWARE_DEVICES_NET_H

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMCORE

#include "includeCheck.h"
#include "vm_device_version.h"

#ifdef VMCORE
#include "config.h"
#include "str.h"
#include "strutil.h"
#endif

#define ETHERNET_MTU         1518

#ifndef ETHER_ADDR_LEN
#define ETHER_ADDR_LEN          6  /* length of MAC address */
#endif
#define ETH_HEADER_LEN	       14  /* length of Ethernet header */
#define IP_ADDR_LEN	        4  /* length of IPv4 address */
#define IP_HEADER_LEN	       20  /* minimum length of IPv4 header */

#define ETHER_MAX_QUEUED_PACKET 1600

/* Most Ethernet equipment can support jumbo frames up to 9216 bytes. */
#define ETHER_MAX_JUMBO_FRAME_LEN 9216

/*
 * State's that a NIC can be in currently we only use this
 * in VLance but if we implement/emulate new adapters that
 * we also want to be able to morph a new corresponding
 * state should be added.
 */

#define LANCE_CHIP  0x2934
#define VMXNET_CHIP 0x4392

/*
 * Size of reserved IO space needed by the LANCE adapter and
 * the VMXNET adapter. If you add more ports to Vmxnet than
 * there is reserved space you must bump VMXNET_CHIP_IO_RESV_SIZE.
 * The sizes must be powers of 2.
 */

#define LANCE_CHIP_IO_RESV_SIZE  0x20
#define VMXNET_CHIP_IO_RESV_SIZE 0x40

#define MORPH_PORT_SIZE 4

#ifdef VMCORE
typedef struct Net_AdapterCount {
   uint8 vlance;
   uint8 vmxnet2;
   uint8 vmxnet3;
   uint8 vrdma;
   uint8 e1000;
   uint8 e1000e;
} Net_AdapterCount;
#endif

#ifdef USERLEVEL

/*
 *----------------------------------------------------------------------------
 *
 * Net_AddAddrToLADRF --
 *
 *      Given a MAC address, sets the corresponding bit in the LANCE style
 *      Logical Address Filter 'ladrf'.
 *      The caller should have initialized the ladrf to all 0's, as this
 *      function only ORs on a bit in the array.
 *      'addr' is presumed to be ETHER_ADDR_LEN in size;
 *      'ladrf' is presumed to point to a 64-bit vector.
 *
 *      Derived from a long history of derivations, originally inspired by
 *      sample code from the AMD "Network Products: Ethernet Controllers 1998
 *      Data Book, Book 2", pages 1-53..1-55.
 *
 * Returns:
 *      None.
 *
 * Side effects:
 *      Updates 'ladrf'.
 *
 *----------------------------------------------------------------------------
 */

static inline void
Net_AddAddrToLadrf(const uint8 *addr,  // IN: pointer to MAC address
                   uint8 *ladrf)       // IN/OUT: pointer to ladrf
{
#define CRC_POLYNOMIAL_BE 0x04c11db7UL	/* Ethernet CRC, big endian */

   uint16 hashcode;
   int32 crc = 0xffffffff;		/* init CRC for each address */
   int32 j;
   int32 bit;
   int32 byte;

   ASSERT(addr);
   ASSERT(ladrf);

   for (byte = 0; byte < ETHER_ADDR_LEN; byte++) {  /* for each address byte */
      /* process each address bit */
      for (bit = *addr++, j = 0;
           j < 8;
           j++, bit >>= 1) {
	 crc = (crc << 1) ^ ((((crc < 0 ? 1 : 0) ^ bit) & 0x01) ?
               CRC_POLYNOMIAL_BE : 0);
      }
   }
   hashcode = (crc & 1);	       /* hashcode is 6 LSb of CRC ... */
   for (j = 0; j < 5; j++) {	       /* ... in reverse order. */
      hashcode = (hashcode << 1) | ((crc>>=1) & 1);
   }

   ladrf[hashcode >> 3] |= 1 << (hashcode & 0x07);
}
#endif // USERLEVEL

#ifdef VMCORE
/*
 *----------------------------------------------------------------------
 *
 * Net_GetNumAdapters --
 *
 *      Returns the number of each type of network adapter configured in this 
 *      VM.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static inline void
Net_GetNumAdapters(Net_AdapterCount *counts)
{
   uint32 i;

   counts->vlance = 0;
   counts->vmxnet2 = 0;
   counts->vmxnet3 = 0;
   counts->vrdma = 0;
   counts->e1000 = 0;
   counts->e1000e = 0;

   for (i = 0; i < MAX_ETHERNET_CARDS; i++) {
      char* adapterStr;

      if (!Config_GetBool(FALSE, "ethernet%d.present", i)) {
	 continue;
      }
      adapterStr = Config_GetString("vlance", "ethernet%d.virtualDev", i);
      if (Str_Strcasecmp(adapterStr, "vmxnet3") == 0) {
         counts->vmxnet3++;
      } else if (Str_Strcasecmp(adapterStr, "vrdma") == 0) {
         counts->vrdma++;
      } else if (Str_Strcasecmp(adapterStr, "vlance") == 0) {
         counts->vlance++;
      } else if (Str_Strcasecmp(adapterStr, "vmxnet") == 0) {
         counts->vmxnet2++;
      } else if (Str_Strcasecmp(adapterStr, "e1000") == 0) {
         counts->e1000++;
      } else if (Str_Strcasecmp(adapterStr, "e1000e") == 0) {
         counts->e1000e++;
      } else {
         LOG_ONCE("%s: unknown adapter: %s\n", __FUNCTION__, adapterStr);
      }
      free(adapterStr);
   }
}

#endif // VMCORE

#endif // VMWARE_DEVICES_NET_H
 0707010000026D000081A40000000000000000000000016822550500000A23000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet/net_sg.h /*********************************************************
 * Copyright (C) 2000 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
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * net_sg.h --
 *
 *	Network packet scatter gather structure.
 */


#ifndef _NET_SG_H
#define _NET_SG_H

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#include "includeCheck.h"

#define NET_SG_DEFAULT_LENGTH	16

/*
 * A single scatter-gather element for a network packet.
 * The address is split into low and high to save space.
 * If we make it 64 bits then Windows pads things out such that
 * we lose a lot of space for each scatter gather array.
 * This adds up when you have embedded scatter-gather 
 * arrays for transmit and receive ring buffers.
 */
typedef struct NetSG_Elem {
   uint32 	addrLow;
   uint16	addrHi;
   uint16	length;
} NetSG_Elem;

typedef enum NetSG_AddrType {
   NET_SG_MACH_ADDR,
   NET_SG_PHYS_ADDR,
   NET_SG_VIRT_ADDR,
} NetSG_AddrType;

typedef struct NetSG_Array {
   uint16	addrType;
   uint16	length;
   NetSG_Elem	sg[NET_SG_DEFAULT_LENGTH];
} NetSG_Array;

#define NET_SG_MAKE_PA(elem)                 (PA)QWORD(elem.addrHi, elem.addrLow)
#define NET_SG_MAKE_PTR(elem) (char *)(uintptr_t)QWORD(elem.addrHi, elem.addrLow)

#endif
 0707010000026E000081A40000000000000000000000016822550500000FCA000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet/upt1_defs.h  /*********************************************************
 * Copyright (C) 2007-2020 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
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/* upt1_defs.h
 *
 *      Definitions for UPTv1
 *
 *      Some of the defs are duplicated in vmkapi_net_upt.h, because
 *      vmkapi_net_upt.h cannot distribute with OSS yet and vmkapi headers can
 *      only include vmkapi headers. Make sure they are kept in sync!
 */

#ifndef _UPT1_DEFS_H
#define _UPT1_DEFS_H

#define UPT1_MAX_TX_QUEUES  64
#define UPT1_MAX_RX_QUEUES  64

#define UPT1_MAX_INTRS  (UPT1_MAX_TX_QUEUES + UPT1_MAX_RX_QUEUES)

#pragma pack(push, 1)
typedef struct UPT1_TxStats {
   uint64 TSOPktsTxOK;  /* TSO pkts post-segmentation */
   uint64 TSOBytesTxOK;
   uint64 ucastPktsTxOK;
   uint64 ucastBytesTxOK;
   uint64 mcastPktsTxOK;
   uint64 mcastBytesTxOK;
   uint64 bcastPktsTxOK;
   uint64 bcastBytesTxOK;
   uint64 pktsTxError;
   uint64 pktsTxDiscard;
} UPT1_TxStats;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct UPT1_RxStats {
   uint64 LROPktsRxOK;    /* LRO pkts */
   uint64 LROBytesRxOK;   /* bytes from LRO pkts */
   /* the following counters are for pkts from the wire, i.e., pre-LRO */
   uint64 ucastPktsRxOK;
   uint64 ucastBytesRxOK;
   uint64 mcastPktsRxOK;
   uint64 mcastBytesRxOK;
   uint64 bcastPktsRxOK;
   uint64 bcastBytesRxOK;
   uint64 pktsRxOutOfBuf;
   uint64 pktsRxError;
} UPT1_RxStats;
#pragma pack(pop)

/* interrupt moderation level */
#define UPT1_IML_NONE     0 /* no interrupt moderation */
#define UPT1_IML_HIGHEST  7 /* least intr generated */
#define UPT1_IML_ADAPTIVE 8 /* adpative intr moderation */

/* values for UPT1_RSSConf.hashFunc */
#define UPT1_RSS_HASH_TYPE_NONE      0x0
#define UPT1_RSS_HASH_TYPE_IPV4      0x01
#define UPT1_RSS_HASH_TYPE_TCP_IPV4  0x02
#define UPT1_RSS_HASH_TYPE_IPV6      0x04
#define UPT1_RSS_HASH_TYPE_TCP_IPV6  0x08

typedef enum {
   UPT1_RSS_HASH_FUNC_NONE      = 0x0000,
   UPT1_RSS_HASH_FUNC_TOEPLITZ  = 0x0001,
   UPT1_RSS_HASH_FUNC_CRC32     = 0x0002,

   //upper bound on max hash functions supported
   UPT1_RSS_HASH_FUNC_MAX       = 0xFFFF
} Vmxnet3_RSSHashFunc;

#define UPT1_RSS_MAX_KEY_SIZE        40
#define UPT1_RSS_MAX_IND_TABLE_SIZE  128

#pragma pack(push, 1)
typedef struct UPT1_RSSConf {
   uint16   hashType;
   uint16   hashFunc;
   uint16   hashKeySize;
   uint16   indTableSize;
   uint8    hashKey[UPT1_RSS_MAX_KEY_SIZE];
   uint8    indTable[UPT1_RSS_MAX_IND_TABLE_SIZE];
} UPT1_RSSConf;
#pragma pack(pop)

/* features */
#define UPT1_F_RXCSUM      0x0001   /* rx csum verification */
#define UPT1_F_RSS         0x0002
#define UPT1_F_RXVLAN      0x0004   /* VLAN tag stripping */
#define UPT1_F_LRO         0x0008
#define UPT1_F_INNEROFLD   0x0010   /* Geneve/VXLAN offloading */

#endif
  0707010000026F000081A40000000000000000000000016822550500002567000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet/vmnet_def.h  /*********************************************************
 * Copyright (c) 2004-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * 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
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vmnet_def.h 
 *
 *     - definitions which are (mostly) not vmxnet or vlance specific
 */

#ifndef _VMNET_DEF_H_
#define _VMNET_DEF_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMCORE

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#include "includeCheck.h"

#define VMNET_NAME_BUFFER_LEN  128 /* Increased for i18n. */
#define VMNET_COAL_STRING_LEN  128


/*
 * capabilities - not all of these are implemented in the virtual HW
 *                (eg VLAN support is in the virtual switch)  so even vlance
 *                can use them
 */
#define VMNET_CAP_SG                   CONST64U(0x0001)             /* Can do scatter-gather transmits. */
#define VMNET_CAP_IP4_CSUM             CONST64U(0x0002)             /* Can checksum only TCP/UDP over IPv4. */
#define VMNET_CAP_HW_CSUM              CONST64U(0x0004)             /* Can checksum all packets. */
#define VMNET_CAP_HIGH_DMA             CONST64U(0x0008)             /* Can DMA to high memory. */
#define VMNET_CAP_TOE                  CONST64U(0x0010)             /* Supports TCP/IP offload. */
#define VMNET_CAP_TSO                  CONST64U(0x0020)             /* Supports TCP Segmentation offload */
#define VMNET_CAP_SW_TSO               CONST64U(0x0040)             /* Supports SW TCP Segmentation */
#define VMNET_CAP_VMXNET_APROM         CONST64U(0x0080)             /* Vmxnet APROM support */
#define VMNET_CAP_HW_TX_VLAN           CONST64U(0x0100)             /* Can we do VLAN tagging in HW */
#define VMNET_CAP_HW_RX_VLAN           CONST64U(0x0200)             /* Can we do VLAN untagging in HW */
#define VMNET_CAP_SW_VLAN              CONST64U(0x0400)             /* Can we do VLAN tagging/untagging in SW */
#define VMNET_CAP_WAKE_PCKT_RCV        CONST64U(0x0800)             /* Can wake on network packet recv? */
#define VMNET_CAP_ENABLE_INT_INLINE    CONST64U(0x1000)             /* Enable Interrupt Inline */
#define VMNET_CAP_ENABLE_HEADER_COPY   CONST64U(0x2000)             /* copy header for vmkernel */
#define VMNET_CAP_TX_CHAIN             CONST64U(0x4000)             /* Guest can use multiple tx entries for a pkt */
#define VMNET_CAP_RX_CHAIN             CONST64U(0x8000)             /* a pkt can span multiple rx entries */
#define VMNET_CAP_LPD                  CONST64U(0x10000)            /* large pkt delivery */
#define VMNET_CAP_BPF                  CONST64U(0x20000)            /* BPF Support in VMXNET Virtual Hardware */
#define VMNET_CAP_AUTO_REBIND          CONST64U(0x20000)            /* BPF support is not for uplink, reuse this bit for uplink auto rebind */
#define VMNET_CAP_SG_SPAN_PAGES        CONST64U(0x40000)            /* Can do scatter-gather span multiple pages transmits. */
#define VMNET_CAP_IP6_CSUM             CONST64U(0x80000)            /* Can do IPv6 csum offload. */
#define VMNET_CAP_TSO6                 CONST64U(0x100000)           /* Can do TSO segmentation offload for IPv6 pkts. */
#define VMNET_CAP_TSO256k              CONST64U(0x200000)           /* Can do TSO segmentation offload for pkts up to 256kB. */
#define VMNET_CAP_UPT                  CONST64U(0x400000)           /* Support UPT */
#define VMNET_CAP_RDONLY_INETHDRS      CONST64U(0x800000)           /* Modifies inet headers for TSO/CSUm */
#define VMNET_CAP_ENCAP                CONST64U(0x1000000)          /* NPA not used, so redefining for ENCAP support */
#define VMNET_CAP_DCB                  CONST64U(0x2000000)          /* Support DCB */
#define VMNET_CAP_OFFSET_BASED_OFFLOAD CONST64U(0x4000000)       /* Support offload based offload */
#define VMNET_CAP_GENEVE_OFFLOAD       CONST64U(0x8000000)          /* Support Geneve encapsulation offload */
#define VMNET_CAP_IP6_CSUM_EXT_HDRS    CONST64U(0x10000000)         /* support csum of ip6 ext hdrs */
#define VMNET_CAP_TSO6_EXT_HDRS        CONST64U(0x20000000)         /* support TSO for ip6 ext hdrs */
#define VMNET_CAP_SCHED                CONST64U(0x40000000)         /* compliant with network scheduling */
#define VMNET_CAP_SRIOV                CONST64U(0x80000000)         /* Supports SR-IOV */

#define VMNET_CAP_PREFERRED_MGMT_NIC   CONST64U(0x100000000)        /* Uplink should be given preference when selecting the management NIC */
#define VMNET_CAP_SG_TX                VMNET_CAP_SG
#define VMNET_CAP_SG_RX                CONST64U(0x200000000)        /* Scatter-gather receive capability */
#define VMNET_CAP_PRIV_STATS           CONST64U(0x400000000)        /* Driver supports accessing private stats */
#define VMNET_CAP_LINK_STATUS_SET      CONST64U(0x800000000)        /* Driver supports changing link status */
#define VMNET_CAP_MAC_ADDR_SET         CONST64U(0x1000000000)       /* Driver supports changing the interface MAC address */
#define VMNET_CAP_COALESCE_PARAMS      CONST64U(0x2000000000)       /* Driver supports changing interrupt coalescing parameters */
#define VMNET_CAP_VLAN_FILTER          CONST64U(0x4000000000)       /* VLAN Filtering capability */
#define VMNET_CAP_WAKE_ON_LAN          CONST64U(0x8000000000)       /* Wake-On-LAN capability */
#define VMNET_CAP_NETWORK_DUMP         CONST64U(0x10000000000)      /* Network core dumping capability */
#define VMNET_CAP_MULTI_QUEUE          CONST64U(0x20000000000)      /* Multiple queue capability */
#define VMNET_CAP_EEPROM               CONST64U(0x40000000000)      /* EEPROM dump capability */
#define VMNET_CAP_REGDUMP              CONST64U(0x80000000000)      /* Register dump capability */
#define VMNET_CAP_SELF_TEST            CONST64U(0x100000000000)     /* Self-test capability */
#define VMNET_CAP_PAUSE_PARAMS         CONST64U(0x200000000000)     /* Pause frame parameter adjusting */
#define VMNET_CAP_RESTART_NEG          CONST64U(0x400000000000)     /* Ability to restart negotiation of link speed/duplexity */
#define VMNET_CAP_LRO                  CONST64U(0x800000000000)     /* Hardware supported LRO */
#define VMNET_CAP_OFFLOAD_ALIGN_ANY    CONST64U(0x1000000000000)    /* Nic requires no header alignment */
#define VMNET_CAP_GENERIC_OFFLOAD      CONST64U(0x2000000000000)    /* Generic hardware offloading (eg. vxlan encap offload and offset based offload) */
#define VMNET_CAP_CABLE_TYPE           CONST64U(0x4000000000000)    /* Uplink supports getting and setting cable type. */
#define VMNET_CAP_PHY_ADDRESS          CONST64U(0x8000000000000)    /* Uplink supports getting and setting PHY address. */
#define VMNET_CAP_TRANSCEIVER_TYPE     CONST64U(0x10000000000000)   /* Uplink supports getting and setting transceiver type. */
#define VMNET_CAP_MESSAGE_LEVEL        CONST64U(0x20000000000000)   /* Uplink supports getting and setting message level. */
#define VMNET_CAP_RING_PARAMS          CONST64U(0x40000000000000)   /* Support getting/setting RX/TX ring size parameters */
#define VMNET_CAP_ADVERTISE_MODES      CONST64U(0x80000000000000)   /* Support getting/setting interconnect modes */
#define VMNET_CAP_HW_DCB               CONST64U(0x100000000000000)  /* DCB capability in hardware */
#define VMNET_CAP_RX_SW_LRO            CONST64U(0x200000000000000)  /* Support SW LRO */
#define VMNET_CAP_ENS                  CONST64U(0x400000000000000)  /* Support ENS */
#define VMNET_CAP_FPO                  CONST64U(0x800000000000000)  /* Support FPO */
#define VMNET_CAP_BMC_NIC              CONST64U(0x1000000000000000) /* Nic Hardware connected to server BMC */
#define VMNET_CAP_UENS_SHIM            CONST64U(0x2000000000000000) /* Driver is compatible with UENS shimming layer */
#define VMNET_CAP_K2K_NIC              CONST64U(0x4000000000000000) /* Nic used for Kernel-to-Kernel communication */
#define VMNET_CAP_LEGACY               CONST64U(0x8000000000000000) /* Uplink is compatible with vmklinux drivers */

/* Used by strict latency enabled vNIC to disable the hardware offloads. */
typedef enum Vmxnet3OffloadDisable {
   VMXNET3_OFFLOAD_DISABLE_NONE,
   VMXNET3_OFFLOAD_DISABLE_TSO,
   VMXNET3_OFFLOAD_DISABLE_LRO,
   VMXNET3_OFFLOAD_DISABLE_TSO_LRO
} Vmxnet3OffloadDisable;
#endif // _VMNET_DEF_H_
 07070100000270000081A400000000000000000000000168225505000039D5000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet/vmxnet2_def.h    /*********************************************************
 * Copyright (C) 2004-2014, 2017, 2021 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
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef _VMXNET2_DEF_H_
#define _VMXNET2_DEF_H_

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#include "includeCheck.h"

#include "net_sg.h"
#include "vmxnet_def.h"

#if defined __cplusplus
extern "C" {
#endif


/*
 * Magic number that identifies this version of the vmxnet protocol.
 */
#define VMXNET2_MAGIC			0xbabe864f

/* size of the rx ring */
#define VMXNET2_MAX_NUM_RX_BUFFERS		128
#define VMXNET2_DEFAULT_NUM_RX_BUFFERS	        100


/* size of the rx ring when enhanced vmxnet is used */
#define ENHANCED_VMXNET2_MAX_NUM_RX_BUFFERS     512
#define ENHANCED_VMXNET2_DEFAULT_NUM_RX_BUFFERS 150

/* size of the 2nd rx ring */
#define VMXNET2_MAX_NUM_RX_BUFFERS2             2048
#define VMXNET2_DEFAULT_NUM_RX_BUFFERS2	        512

/* size of the tx ring */
#define VMXNET2_MAX_NUM_TX_BUFFERS		128
#define VMXNET2_DEFAULT_NUM_TX_BUFFERS	        100

/* size of the tx ring when tso/jf is used */
#define VMXNET2_MAX_NUM_TX_BUFFERS_TSO          512
#define VMXNET2_DEFAULT_NUM_TX_BUFFERS_TSO	256

enum {
   VMXNET2_OWNERSHIP_DRIVER,
   VMXNET2_OWNERSHIP_DRIVER_PENDING,
   VMXNET2_OWNERSHIP_NIC,
   VMXNET2_OWNERSHIP_NIC_PENDING,
   VMXNET2_OWNERSHIP_NIC_FRAG,
   VMXNET2_OWNERSHIP_DRIVER_FRAG,
};

#define VMXNET2_SG_DEFAULT_LENGTH	6

typedef struct Vmxnet2_SG_Array {
   uint16	addrType;
   uint16	length;
   NetSG_Elem	sg[VMXNET2_SG_DEFAULT_LENGTH];
} Vmxnet2_SG_Array;

typedef struct Vmxnet2_RxRingEntry {
   uint64		paddr;		/* Physical address of the packet data. */
   uint32		bufferLength;	/* The length of the data at paddr. */
   uint32		actualLength;	/* The actual length of the received data. */
   uint16               ownership;	/* Who owns the packet. */
   uint16		flags;		/* Flags as defined below. */
   uint32               index;          /*
                                         * Currently:
                                         *
                                         * This is being used as an packet index to
                                         * rx buffers.
                                         *
                                         * Originally:
                                         *
					 * was void* driverData ("Driver specific data.")
					 * which was used for sk_buf**s in Linux and
                                         * VmxnetRxBuff*s in Windows.  It could not be
					 * here because the structure needs to be the
					 * same size between architectures, and it was
					 * not used on the device side, anyway.  Look
					 * for its replacement in
					 * Vmxnet_Private.rxRingBuffPtr on Linux and
					 * VmxnetAdapter.rxRingBuffPtr on Windows.
					 */
} Vmxnet2_RxRingEntry;

/*
 * Vmxnet2_RxRingEntry flags:
 *
 * VMXNET2_RX_HW_XSUM_OK       The hardware verified the TCP/UDP checksum.
 * VMXNET2_RX_WITH_FRAG        More data is in the 2nd ring
 * VMXNET2_RX_FRAG_EOP         This is the last frag, the only valid flag for
 *                             2nd ring entry
 *
 */
#define VMXNET2_RX_HW_XSUM_OK  0x01
#define VMXNET2_RX_WITH_FRAG   0x02
#define VMXNET2_RX_FRAG_EOP    0x04

typedef struct Vmxnet2_TxRingEntry {
   uint16		flags;		/* Flags as defined below. */
   uint16 	        ownership;	/* Who owns this packet. */
   uint32               extra;          /*
					 * was void* driverData ("Driver specific data.")
					 * which was used for sk_buf*s in Linux and
                                         * VmxnetTxInfo*s in Windows.  It could not be
					 * here because the structure needs to be the
					 * same size between architectures, and it was
					 * not used on the device side, anyway.  Look
					 * for its replacement in
					 * Vmxnet_Private.txRingBuffPtr on Linux and
					 * VmxnetAdapter.txRingBuffPtr on Windows.
					 */
   uint32               tsoMss;         /* TSO pkt MSS */
   Vmxnet2_SG_Array	sg;		/* Packet data. */
} Vmxnet2_TxRingEntry;

/*
 * Vmxnet2_TxRingEntry flags:
 *
 *   VMXNET2_TX_CAN_KEEP	The implementation can return the tx ring entry
 *				to the driver when it is ready as opposed to
 *				before the transmit call from the driver completes.
 *   VMXNET2_TX_RING_LOW	The driver's transmit ring buffer is low on free
 *				slots.
 *   VMXNET2_TX_HW_XSUM         The hardware should perform the TCP/UDP checksum
 *   VMXNET2_TX_TSO             The hardware should do TCP segmentation.
 *   VMXNET2_TX_PINNED_BUFFER   The driver used one of the preallocated vmkernel
 *                              buffers *and* it has been pinned with Net_PinTxBuffers.
 *   VMXNET2_TX_MORE            This is *not* the last tx entry for the pkt.
 *                              All flags except VMXNET2_TX_MORE are ignored
 *                              for the subsequent tx entries.
 */
#define VMXNET2_TX_CAN_KEEP	     0x0001
#define VMXNET2_TX_RING_LOW	     0x0002
#define VMXNET2_TX_HW_XSUM           0x0004
#define VMXNET2_TX_TSO	             0x0008
#define VMXNET2_TX_PINNED_BUFFER     0x0010
#define VMXNET2_TX_MORE              0x0020

/*
 * Structure used by implementations.  This structure allows the inline
 * functions below to be used.
 */
typedef struct Vmxnet2_RxRingInfo {
#ifndef VMX86_VMX
   Vmxnet2_RxRingEntry    *base;       /* starting addr of the ring */
#endif
   uint32                  nicNext;    /* next entry to use in the ring */
   uint32                  ringLength; /* # of entries in the ring */
   PA                      startPA;    /* starting addr of the ring */
#ifdef VMX86_DEBUG
   const char             *name;
#endif
} Vmxnet2_RxRingInfo;

typedef struct Vmxnet2_TxRingInfo {
#ifndef VMX86_VMX
   Vmxnet2_TxRingEntry    *base;       /* starting addr of the ring */
#endif
   uint32                  nicNext;    /* next entry to use in the ring */
   uint32                  ringLength; /* # of entries in the ring */
   PA                      startPA;    /* starting addr of the ring */
#ifdef VMX86_DEBUG
   const char             *name;
#endif
} Vmxnet2_TxRingInfo;

typedef struct Vmxnet2_ImplData {
   Vmxnet2_RxRingInfo    rxRing;
   Vmxnet2_RxRingInfo    rxRing2;
   Vmxnet2_TxRingInfo    txRing;
} Vmxnet2_ImplData;

typedef struct Vmxnet2_DriverStats {
   uint32	transmits;	   /* # of times that the drivers transmit function */
				   /*   is called. The driver could transmit more */
				   /*   than one packet per call. */
   uint32	pktsTransmitted;   /* # of packets transmitted. */
   uint32	noCopyTransmits;   /* # of packets that are transmitted without */
				   /*   copying any data. */
   uint32	copyTransmits;	   /* # of packets that are transmittted by copying */
				   /*   the data into a buffer. */
   uint32	maxTxsPending;	   /* Max # of transmits outstanding. */
   uint32	txStopped;	   /* # of times that transmits got stopped because */
				   /*   the tx ring was full. */
   uint32	txRingOverflow;	   /* # of times that transmits got deferred bc */
				   /*   the tx ring was full.  This must be >= */
				   /*   txStopped since there will be one */
				   /*   txStopped when the ring fills up and then */
				   /*   one txsRingOverflow for each packet that */
				   /*   that gets deferred until there is space. */
   uint32	interrupts;	   /* # of times interrupted. */
   uint32	pktsReceived;	   /* # of packets received. */
   uint32	rxBuffersLow;	   /* # of times that the driver was low on */
} Vmxnet2_DriverStats;

/*
 * Shared data structure between the vm, the vmm, and the vmkernel.
 * This structure was originally arranged to try to group common data
 * on 32-byte cache lines, but bit rot and the fact that we no longer
 * run on many CPUs with that cacheline size made that optimization
 * ineffective. vmxnet3 should target 128 byte sizes and alignments
 * to optimize for the 64 byte cacheline pairs on P4.
 */
typedef struct Vmxnet2_DriverData {
   /*
    * Magic must be first.
    */
   Vmxnet_DDMagic       magic;

   /*
    * Receive fields.
    */
   uint32		rxRingLength;		/* Length of the receive ring. */
   uint32		rxDriverNext;		/* Index of the next packet that will */
						/*   be filled in by the impl */

   uint32		rxRingLength2;	        /* Length of the 2nd receive ring. */
   uint32		rxDriverNext2;	        /* Index of the next packet that will */
						/*   be filled in by the impl */

   uint32		notUsed1;               /* was "irq" */

   /*
    * Interface flags and multicast filter.
    */
   uint32		ifflags;
   uint32		LADRF[VMXNET_MAX_LADRF];

   /*
    * Transmit fields
    */
   uint32               txDontClusterSize;      /* All packets <= this will be transmitted */
                                                /* immediately, regardless of clustering */
                                                /* settings [was fill[1]] */
   uint32		txRingLength;		/* Length of the transmit ring. */
   uint32		txDriverCur;		/* Index of the next packet to be */
						/*   returned by the implementation.*/
   uint32		txDriverNext;		/* Index of the entry in the ring */
						/*   buffer to use for the next packet.*/
   uint32		txStopped;  		/* The driver has stopped transmitting */
						/*   because its ring buffer is full.*/
   uint32		txClusterLength;	/* Maximum number of packets to */
						/*   put in the ring buffer before */
						/*   asking the implementation to */
						/*   transmit the packets in the buffer.*/
   uint32		txNumDeferred;          /* Number of packets that have been */
						/*   queued in the ring buffer since */
						/*   the last time the implementation */
						/*   was asked to transmit. */
   uint32		notUsed3;               /* This field is deprecated but still used */
                                                /* as minXmitPhysLength on the escher branch. */
                                                /* It cannot be used for other purposes */
                                                /* until escher vms no longer are allowed */
                                                /* to install this driver. */

   uint32              totalRxBuffers;          /* used by esx for max rx buffers */
   uint64              rxBufferPhysStart;       /* used by esx for pinng rx buffers */
   /*
    * Extra fields for future expansion.
    */
   uint32		extra[2];

   uint16               maxFrags;               /* # of frags the driver can handle */
   uint16               featureCtl;             /* for driver to enable some feature */

   /*
    * The following fields are used to save the nicNext indexes part
    * of implData in the vmkernel when disconnecting the adapter, we
    * need them when we reconnect.  This mechanism is used for
    * checkpointing as well.
    */
   uint32               savedRxNICNext;
   uint32               savedRxNICNext2;
   uint32               savedTxNICNext;

   /*
    * Fields used during initialization or debugging.
    */
   uint32		length;
   uint32		rxRingOffset;
   uint32		rxRingOffset2;
   uint32		txRingOffset;
   uint32		debugLevel;
   uint32		txBufferPhysStart;
   uint32		txBufferPhysLength;
   uint32		txPktMaxSize;

   /*
    * Driver statistics.
    */
   Vmxnet2_DriverStats	stats;
} Vmxnet2_DriverData;

#ifdef VMX86_SERVER
/*
 * Shared between VMM and Vmkernel part of vmxnet2 to optimize action posting
 * VMM writes 1 (don't post) or 0 (okay to post) and vmk reads this.
 */
typedef struct VmxnetVMKShared {
   uint32    dontPostActions;
   PciHandle pciHandle;
} VmxnetVMKShared;
#endif

#if defined VMKERNEL

/*
 * Inline functions used to assist the implementation of the vmxnet interface.
 */

/*
 * Get the next empty packet out of the receive ring and move to
 * the next packet.
 */
static INLINE Vmxnet2_RxRingEntry *
Vmxnet2_GetNextRx(Vmxnet2_RxRingInfo *ri, uint16 ownership)
{
   Vmxnet2_RxRingEntry *rre = ri->base + ri->nicNext;
   if (rre->ownership == ownership) {
      VMXNET_INC(ri->nicNext, ri->ringLength);
   } else {
      rre = NULL;
   }

   return rre;
}

/*
 * Return ownership of a packet in the receive ring to the driver.
 */
static INLINE void
Vmxnet2_PutRx(Vmxnet2_RxRingEntry *rre, uint32 pktLength, uint16 ownership)
{
   rre->actualLength = pktLength;
   COMPILER_MEM_BARRIER();
   rre->ownership = ownership;
}

/*
 * Get the next pending packet out of the transmit ring.
 */
static INLINE Vmxnet2_TxRingEntry *
Vmxnet2_GetNextTx(Vmxnet2_TxRingInfo *ri)
{
   Vmxnet2_TxRingEntry *txre = ri->base + ri->nicNext;
   if (txre->ownership == VMXNET2_OWNERSHIP_NIC) {
      return txre;
   } else {
      return NULL;
   }
}

/*
 * Move to the next entry in the transmit ring.
 */
static INLINE unsigned int
Vmxnet2_IncNextTx(Vmxnet2_TxRingInfo *ri)
{
   unsigned int prev = ri->nicNext;
   Vmxnet2_TxRingEntry *txre = ri->base + ri->nicNext;

   txre->ownership = VMXNET2_OWNERSHIP_NIC_PENDING;

   VMXNET_INC(ri->nicNext, ri->ringLength);
   return prev;
}

/*
 * Get the indicated entry from transmit ring.
 */
static INLINE Vmxnet2_TxRingEntry *
Vmxnet2_GetTxEntry(Vmxnet2_TxRingInfo *ri, unsigned int idx)
{
   return ri->base + idx;
}

/*
 * Get the indicated entry from the given rx ring
 */
static INLINE Vmxnet2_RxRingEntry *
Vmxnet2_GetRxEntry(Vmxnet2_RxRingInfo *ri, unsigned int idx)
{
   return ri->base + idx;
}

#endif /* defined VMX86_VMX || defined VMKERNEL */

#if defined __cplusplus
} // extern "C"
#endif

#endif // _VMXNET2_DEF_H_
   07070100000271000081A40000000000000000000000016822550500009FFD000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet/vmxnet3_defs.h   /*********************************************************
 * Copyright (c) 2007-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * 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
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vmxnet3_defs.h --
 *
 *      Definitions shared by device emulation and guest drivers for
 *      VMXNET3 NIC
 */

#ifndef _VMXNET3_DEFS_H_
#define _VMXNET3_DEFS_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#define INCLUDE_ALLOW_VMKDRIVERS
#define INCLUDE_ALLOW_VMCORE
#define INCLUDE_ALLOW_MODULE
#include "includeCheck.h"

#include "upt1_defs.h"

/* all registers are 32 bit wide */
/* BAR 1 */
#define VMXNET3_REG_VRRS  0x0    /* Vmxnet3 Revision Report Selection */
#define VMXNET3_REG_UVRS  0x8    /* UPT Version Report Selection */
#define VMXNET3_REG_DSAL  0x10   /* Driver Shared Address Low */
#define VMXNET3_REG_DSAH  0x18   /* Driver Shared Address High */
#define VMXNET3_REG_CMD   0x20   /* Command */
#define VMXNET3_REG_MACL  0x28   /* MAC Address Low */
#define VMXNET3_REG_MACH  0x30   /* MAC Address High */
#define VMXNET3_REG_ICR   0x38   /* Interrupt Cause Register */
#define VMXNET3_REG_ECR   0x40   /* Event Cause Register */
#define VMXNET3_REG_DCR   0x48   /* Device capability register,
                                  * from 0x48 to 0x80
                                  * */
#define VMXNET3_REG_PTCR 0x88    /* Passthru capbility register
                                  * from 0x88 to 0xb0
                                  */

#define VMXNET3_REG_WSAL  0xF00  /* Wireless Shared Address Lo  */
#define VMXNET3_REG_WSAH  0xF08  /* Wireless Shared Address Hi  */
#define VMXNET3_REG_WCMD  0xF18  /* Wireless Command */

/* BAR 0 */
#define VMXNET3_REG_IMR      0x0   /* Interrupt Mask Register */
#define VMXNET3_REG_TXPROD   0x600 /* Tx Producer Index */
#define VMXNET3_REG_RXPROD   0x800 /* Rx Producer Index for ring 1 */
#define VMXNET3_REG_RXPROD2  0xA00 /* Rx Producer Index for ring 2 */

/* For Large PT BAR, the following offset to DB register */
#define VMXNET3_REG_LB_TXPROD    (PAGE_SIZE)           /* Tx Producer Index */
#define VMXNET3_REG_LB_RXPROD    ((PAGE_SIZE) + 0x400) /* Rx Producer Index for ring 1 */
#define VMXNET3_REG_LB_RXPROD2   ((PAGE_SIZE) + 0x800) /* Rx Producer Index for ring 2 */

#define VMXNET3_PT_REG_SIZE     4096    /* BAR 0 */
#define VMXNET3_LARGE_PT_REG_SIZE  (2 * (PAGE_SIZE))  /* large PT pages */
#define VMXNET3_VD_REG_SIZE     4096    /* BAR 1 */
#define VMXNET3_LARGE_BAR0_REG_SIZE (4096 * 4096)  /* LARGE BAR 0 */
#define VMXNET3_OOB_REG_SIZE  (4094 * 4096) /* OOB pages */

/*
 * The two Vmxnet3 MMIO Register PCI BARs (BAR 0 at offset 10h and BAR 1 at
 * offset 14h)  as well as the MSI-X BAR are combined into one PhysMem region:
 * <-VMXNET3_PT_REG_SIZE-><-VMXNET3_VD_REG_SIZE-><-VMXNET3_MSIX_BAR_SIZE-->
 * -------------------------------------------------------------------------
 * |Pass Thru Registers  | Virtual Dev Registers | MSI-X Vector/PBA Table  |
 * -------------------------------------------------------------------------
 * VMXNET3_MSIX_BAR_SIZE is defined in "vmxnet3Int.h"
 */

#define VMXNET3_REG_ALIGN       8  /* All registers are 8-byte aligned. */
#define VMXNET3_REG_ALIGN_MASK  0x7

/* I/O Mapped access to registers */
#define VMXNET3_IO_TYPE_PT              0
#define VMXNET3_IO_TYPE_VD              1
#define VMXNET3_IO_ADDR(type, reg)      (((type) << 24) | ((reg) & 0xFFFFFF))
#define VMXNET3_IO_TYPE(addr)           ((addr) >> 24)
#define VMXNET3_IO_REG(addr)            ((addr) & 0xFFFFFF)

#ifndef __le16
#define __le16 uint16
#endif
#ifndef __le32
#define __le32 uint32
#endif
#ifndef __le64
#define __le64 uint64
#endif

#define VMXNET3_PMC_PSEUDO_TSC  0x10003

typedef enum {
   VMXNET3_CMD_FIRST_SET = 0xCAFE0000,
   VMXNET3_CMD_ACTIVATE_DEV = VMXNET3_CMD_FIRST_SET,
   VMXNET3_CMD_QUIESCE_DEV,
   VMXNET3_CMD_RESET_DEV,
   VMXNET3_CMD_UPDATE_RX_MODE,
   VMXNET3_CMD_UPDATE_MAC_FILTERS,
   VMXNET3_CMD_UPDATE_VLAN_FILTERS,
   VMXNET3_CMD_UPDATE_RSSIDT,
   VMXNET3_CMD_UPDATE_IML,
   VMXNET3_CMD_UPDATE_PMCFG,
   VMXNET3_CMD_UPDATE_FEATURE,
   VMXNET3_CMD_STOP_EMULATION,
   VMXNET3_CMD_LOAD_PLUGIN,
   VMXNET3_CMD_SET_UPT_INTR_AFFINITY = VMXNET3_CMD_LOAD_PLUGIN,
   VMXNET3_CMD_ACTIVATE_VF,
   VMXNET3_CMD_SET_POLLING,
   VMXNET3_CMD_SET_COALESCE,
   VMXNET3_CMD_REGISTER_MEMREGS,
   VMXNET3_CMD_SET_RSS_FIELDS,
   VMXNET3_CMD_SET_PKTSTEERING, /* 0xCAFE0011 */
   VMXNET3_CMD_SET_ESP_QUEUE_SELECTION_CONF,
   VMXNET3_CMD_SET_RING_BUFFER_SIZE,

   VMXNET3_CMD_FIRST_GET = 0xF00D0000,
   VMXNET3_CMD_GET_QUEUE_STATUS = VMXNET3_CMD_FIRST_GET,
   VMXNET3_CMD_GET_STATS,
   VMXNET3_CMD_GET_LINK,
   VMXNET3_CMD_GET_PERM_MAC_LO,
   VMXNET3_CMD_GET_PERM_MAC_HI,
   VMXNET3_CMD_GET_DID_LO,
   VMXNET3_CMD_GET_DID_HI,
   VMXNET3_CMD_GET_DEV_EXTRA_INFO,
   VMXNET3_CMD_GET_CONF_INTR,
   VMXNET3_CMD_GET_ADAPTIVE_RING_INFO,
   VMXNET3_CMD_GET_TXDATA_DESC_SIZE,
   VMXNET3_CMD_GET_COALESCE,
   VMXNET3_CMD_GET_RSS_FIELDS,
   VMXNET3_CMD_GET_ENCAP_DSTPORT,
   VMXNET3_CMD_GET_PKTSTEERING, /* 0xF00D000E */
   VMXNET3_CMD_GET_MAX_QUEUES_CONF,
   VMXNET3_CMD_GET_RSS_HASH_FUNC,
   VMXNET3_CMD_GET_MAX_CAPABILITIES,
   VMXNET3_CMD_GET_DCR0_REG,
   VMXNET3_CMD_GET_TSRING_DESC_SIZE,
   VMXNET3_CMD_GET_DISABLED_OFFLOADS,
} Vmxnet3_Cmd;

/* Adaptive Ring Info Flags */
#define VMXNET3_DISABLE_ADAPTIVE_RING 1

/*
 *	Little Endian layout of bitfields -
 *      Byte 0 :        7.....len.....0
 *      Byte 1 :        oco gen 13.len.8
 *      Byte 2 :        5.msscof.0 ext1  dtype
 *      Byte 3 :        13...msscof...6
 *
 *	Big Endian layout of bitfields -
 *	Byte 0:		13...msscof...6
 *      Byte 1 :        5.msscof.0 ext1  dtype
 *	Byte 2 :	oco gen 13.len.8
 *	Byte 3 :	7.....len.....0
 *
 *	Thus, le32_to_cpu on the dword will allow the big endian driver to read
 *	the bit fields correctly. And cpu_to_le32 will convert bitfields
 *	bit fields written by big endian driver to format required by device.
 */

#pragma pack(push, 1)
typedef struct Vmxnet3_TxDesc {
   __le64 addr;

#ifdef __BIG_ENDIAN_BITFIELD
   uint32 msscof:14;  /* MSS, checksum offset, flags */
   uint32 ext1:1;     /* set to 1 to indicate inner csum/tso, vmxnet3 v7 */
   uint32 dtype:1;    /* descriptor type */
   uint32 oco:1;      /* Outer csum offload */
   uint32 gen:1;      /* generation bit */
   uint32 len:14;
#else
   uint32 len:14;
   uint32 gen:1;      /* generation bit */
   uint32 oco:1;      /* Outer csum offload */
   uint32 dtype:1;    /* descriptor type */
   uint32 ext1:1;     /* set to 1 to indicating inner csum/tso, vmxnet3 v7 */
   uint32 msscof:14;  /* MSS, checksum offset, flags */
#endif  /* __BIG_ENDIAN_BITFIELD */

#ifdef __BIG_ENDIAN_BITFIELD
   uint32 tci:16;     /* Tag to Insert */
   uint32 ti:1;       /* VLAN Tag Insertion */
   uint32 ext2:1;
   uint32 cq:1;       /* completion request */
   uint32 eop:1;      /* End Of Packet */
   uint32 om:2;       /* offload mode */
   uint32 hlen:10;    /* header len */
#else
   uint32 hlen:10;    /* header len */
   uint32 om:2;       /* offload mode */
   uint32 eop:1;      /* End Of Packet */
   uint32 cq:1;       /* completion request */
   uint32 ext2:1;
   uint32 ti:1;       /* VLAN Tag Insertion */
   uint32 tci:16;     /* Tag to Insert */
#endif  /* __BIG_ENDIAN_BITFIELD */
} Vmxnet3_TxDesc;
#pragma pack(pop)

/* TxDesc.OM values */
#define VMXNET3_OM_NONE  0          /* 0b00 */
#define VMXNET3_OM_ENCAP 1          /* 0b01 */
#define VMXNET3_OM_CSUM  2          /* 0b10 */
#define VMXNET3_OM_TSO   3          /* 0b11 */

/* fields in TxDesc we access w/o using bit fields */
#define VMXNET3_TXD_EOP_SHIFT 12
#define VMXNET3_TXD_CQ_SHIFT  13
#define VMXNET3_TXD_GEN_SHIFT 14
#define VMXNET3_TXD_EOP_DWORD_SHIFT 3
#define VMXNET3_TXD_GEN_DWORD_SHIFT 2

#define VMXNET3_TXD_CQ  (1 << VMXNET3_TXD_CQ_SHIFT)
#define VMXNET3_TXD_EOP (1 << VMXNET3_TXD_EOP_SHIFT)
#define VMXNET3_TXD_GEN (1 << VMXNET3_TXD_GEN_SHIFT)

#define VMXNET3_TXD_GEN_SIZE 1
#define VMXNET3_TXD_EOP_SIZE 1

#define VMXNET3_HDR_COPY_SIZE   128
#pragma pack(push, 1)
typedef struct Vmxnet3_TxDataDesc {
   uint8 data[VMXNET3_HDR_COPY_SIZE];
} Vmxnet3_TxDataDesc;
#pragma pack(pop)
typedef uint8 Vmxnet3_RxDataDesc;

#define VMXNET3_TCD_GEN_SHIFT	31
#define VMXNET3_TCD_GEN_SIZE	1
#define VMXNET3_TCD_TXIDX_SHIFT	0
#define VMXNET3_TCD_TXIDX_SIZE	12
#define VMXNET3_TCD_GEN_DWORD_SHIFT	3

#pragma pack(push, 1)
typedef struct Vmxnet3_TxCompDesc {
   uint32 txdIdx:12;    /* Index of the EOP TxDesc */
   uint32 ext1:20;

   __le32 ext2;
   __le32 ext3;

   uint32 rsvd:24;
   uint32 type:7;       /* completion type */
   uint32 gen:1;        /* generation bit */
} Vmxnet3_TxCompDesc;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_RxDesc {
   __le64 addr;

#ifdef __BIG_ENDIAN_BITFIELD
   uint32 gen:1;        /* Generation bit */
   uint32 rsvd:15;
   uint32 dtype:1;      /* Descriptor type */
   uint32 btype:1;      /* Buffer Type */
   uint32 len:14;
#else
   uint32 len:14;
   uint32 btype:1;      /* Buffer Type */
   uint32 dtype:1;      /* Descriptor type */
   uint32 rsvd:15;
   uint32 gen:1;        /* Generation bit */
#endif
   __le32 ext1;
} Vmxnet3_RxDesc;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3TSInfo {
   uint64    tsData:56;
   uint64    tsType:4;
   uint64    tsi:1;      //bit to indicate to set ts
   uint64    pad:3;
   uint64    pad2;
} Vmxnet3TSInfo;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_TxTSDesc {
   Vmxnet3TSInfo   ts;
   uint64          pad[14];
} Vmxnet3_TxTSDesc;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_RxTSDesc {
   Vmxnet3TSInfo   ts;
   uint64          pad[14];
} Vmxnet3_RxTSDesc;
#pragma pack(pop)

/* values of RXD.BTYPE */
#define VMXNET3_RXD_BTYPE_HEAD   0    /* head only */
#define VMXNET3_RXD_BTYPE_BODY   1    /* body only */

/* fields in RxDesc we access w/o using bit fields */
#define VMXNET3_RXD_BTYPE_SHIFT  14
#define VMXNET3_RXD_GEN_SHIFT    31

#define VMXNET3_RCD_HDR_INNER_SHIFT  13
#define VMXNET3_RCD_RSS_INNER_SHIFT  12
#define VMXNET3_RCD_TS_SHIFT  31

#pragma pack(push, 1)
typedef struct Vmxnet3_RxCompDesc {
#ifdef __BIG_ENDIAN_BITFIELD
   uint32 ext2:1;       /* Packet is timestamped */
   uint32 cnc:1;        /* Checksum Not Calculated */
   uint32 rssType:4;    /* RSS hash type used */
   uint32 rqID:10;      /* rx queue/ring ID */
   uint32 sop:1;        /* Start of Packet */
   uint32 eop:1;        /* End of Packet */
   uint32 ext1:2;       /* bit 0: indicating v4/v6/.. is for inner header */
                        /* bit 1: indicating rssType is based on inner header */
   uint32 rxdIdx:12;    /* Index of the RxDesc */
#else
   uint32 rxdIdx:12;    /* Index of the RxDesc */
   uint32 ext1:2;       /* bit 0: indicating v4/v6/.. is for inner header */
                        /* bit 1: indicating rssType is based on inner header */
   uint32 eop:1;        /* End of Packet */
   uint32 sop:1;        /* Start of Packet */
   uint32 rqID:10;      /* rx queue/ring ID */
   uint32 rssType:4;    /* RSS hash type used */
   uint32 cnc:1;        /* Checksum Not Calculated */
   uint32 ext2:1;       /* Packet is timestamped */
#endif  /* __BIG_ENDIAN_BITFIELD */

   __le32 rssHash;      /* RSS hash value */

#ifdef __BIG_ENDIAN_BITFIELD
   uint32 tci:16;       /* Tag stripped */
   uint32 ts:1;         /* Tag is stripped */
   uint32 err:1;        /* Error */
   uint32 len:14;       /* data length */
#else
   uint32 len:14;       /* data length */
   uint32 err:1;        /* Error */
   uint32 ts:1;         /* Tag is stripped */
   uint32 tci:16;       /* Tag stripped */
#endif  /* __BIG_ENDIAN_BITFIELD */


#ifdef __BIG_ENDIAN_BITFIELD
   uint32 gen:1;        /* generation bit */
   uint32 type:7;       /* completion type */
   uint32 fcs:1;        /* Frame CRC correct */
   uint32 frg:1;        /* IP Fragment */
   uint32 v4:1;         /* IPv4 */
   uint32 v6:1;         /* IPv6 */
   uint32 ipc:1;        /* IP Checksum Correct */
   uint32 tcp:1;        /* TCP packet */
   uint32 udp:1;        /* UDP packet */
   uint32 tuc:1;        /* TCP/UDP Checksum Correct */
   uint32 csum:16;
#else
   uint32 csum:16;
   uint32 tuc:1;        /* TCP/UDP Checksum Correct */
   uint32 udp:1;        /* UDP packet */
   uint32 tcp:1;        /* TCP packet */
   uint32 ipc:1;        /* IP Checksum Correct */
   uint32 v6:1;         /* IPv6 */
   uint32 v4:1;         /* IPv4 */
   uint32 frg:1;        /* IP Fragment */
   uint32 fcs:1;        /* Frame CRC correct */
   uint32 type:7;       /* completion type */
   uint32 gen:1;        /* generation bit */
#endif  /* __BIG_ENDIAN_BITFIELD */
} Vmxnet3_RxCompDesc;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_RxCompDescExt {
   __le32 dword1;
   uint8  segCnt;       /* Number of aggregated packets */
   uint8  dupAckCnt;    /* Number of duplicate Acks */
   __le16 tsDelta;      /* TCP timestamp difference */
   __le32 dword2;
#ifdef __BIG_ENDIAN_BITFIELD
   uint32 gen:1;        /* generation bit */
   uint32 type:7;       /* completion type */
   uint32 fcs:1;        /* Frame CRC correct */
   uint32 frg:1;        /* IP Fragment */
   uint32 v4:1;         /* IPv4 */
   uint32 v6:1;         /* IPv6 */
   uint32 ipc:1;        /* IP Checksum Correct */
   uint32 tcp:1;        /* TCP packet */
   uint32 udp:1;        /* UDP packet */
   uint32 tuc:1;        /* TCP/UDP Checksum Correct */
   uint32 mss:16;
#else
   uint32 mss:16;
   uint32 tuc:1;        /* TCP/UDP Checksum Correct */
   uint32 udp:1;        /* UDP packet */
   uint32 tcp:1;        /* TCP packet */
   uint32 ipc:1;        /* IP Checksum Correct */
   uint32 v6:1;         /* IPv6 */
   uint32 v4:1;         /* IPv4 */
   uint32 frg:1;        /* IP Fragment */
   uint32 fcs:1;        /* Frame CRC correct */
   uint32 type:7;       /* completion type */
   uint32 gen:1;        /* generation bit */
#endif  /* __BIG_ENDIAN_BITFIELD */
} Vmxnet3_RxCompDescExt;
#pragma pack(pop)

/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */
#define VMXNET3_RCD_TUC_SHIFT  16
#define VMXNET3_RCD_IPC_SHIFT  19

/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.qword[1] */
#define VMXNET3_RCD_TYPE_SHIFT 56
#define VMXNET3_RCD_GEN_SHIFT  63

/* csum OK for TCP/UDP pkts over IP */
#define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | 1 << VMXNET3_RCD_IPC_SHIFT)

/* value of RxCompDesc.rssType */
#define VMXNET3_RCD_RSS_TYPE_NONE     0
#define VMXNET3_RCD_RSS_TYPE_IPV4     1
#define VMXNET3_RCD_RSS_TYPE_TCPIPV4  2
#define VMXNET3_RCD_RSS_TYPE_IPV6     3
#define VMXNET3_RCD_RSS_TYPE_TCPIPV6  4
#define VMXNET3_RCD_RSS_TYPE_UDPIPV4  5
#define VMXNET3_RCD_RSS_TYPE_UDPIPV6  6
#define VMXNET3_RCD_RSS_TYPE_ESPIPV4  7
#define VMXNET3_RCD_RSS_TYPE_ESPIPV6  8

/* a union for accessing all cmd/completion descriptors */
typedef union Vmxnet3_GenericDesc {
   __le64                qword[2];
   __le32                dword[4];
   __le16                word[8];
   Vmxnet3_TxDesc        txd;
   Vmxnet3_RxDesc        rxd;
   Vmxnet3_TxCompDesc    tcd;
   Vmxnet3_RxCompDesc    rcd;
   Vmxnet3_RxCompDescExt rcdExt;
} Vmxnet3_GenericDesc;

#define VMXNET3_INIT_GEN       1

#define VMXNET3_INVALID_QUEUEID -1

/* Max size of a single tx buffer */
#define VMXNET3_MAX_TX_BUF_SIZE  (1 << 14)

/* # of tx desc needed for a tx buffer size */
#define VMXNET3_TXD_NEEDED(size) (((size) + VMXNET3_MAX_TX_BUF_SIZE - 1) / VMXNET3_MAX_TX_BUF_SIZE)

/* max # of tx descs for a non-tso pkt */
#define VMXNET3_MAX_TXD_PER_PKT 16

/* Max size of a single rx buffer */
#define VMXNET3_MAX_RX_BUF_SIZE  ((1 << 14) - 1)
/* Minimum size of a type 0 buffer */
#define VMXNET3_MIN_T0_BUF_SIZE  128
#define VMXNET3_MAX_CSUM_OFFSET  1024

/* Ring base address alignment */
#define VMXNET3_RING_BA_ALIGN   512
#define VMXNET3_RING_BA_MASK    (VMXNET3_RING_BA_ALIGN - 1)

/* Ring size must be a multiple of 32 */
#define VMXNET3_RING_SIZE_ALIGN 32
#define VMXNET3_RING_SIZE_MASK  (VMXNET3_RING_SIZE_ALIGN - 1)

/* Rx Data Ring buffer size must be a multiple of 64 bytes */
#define VMXNET3_RXDATA_DESC_SIZE_ALIGN 64
#define VMXNET3_RXDATA_DESC_SIZE_MASK  (VMXNET3_RXDATA_DESC_SIZE_ALIGN - 1)

/* Tx Data Ring buffer size must be a multiple of 64 bytes */
#define VMXNET3_TXDATA_DESC_SIZE_ALIGN 64
#define VMXNET3_TXDATA_DESC_SIZE_MASK  (VMXNET3_TXDATA_DESC_SIZE_ALIGN - 1)

/* Rx TS Ring buffer size must be a multiple of 64 bytes */
#define VMXNET3_RXTS_DESC_SIZE_ALIGN 64
#define VMXNET3_RXTS_DESC_SIZE_MASK  (VMXNET3_RXTS_DESC_SIZE_ALIGN - 1)

/* Tx TS Ring buffer size must be a multiple of 64 bytes */
#define VMXNET3_TXTS_DESC_SIZE_ALIGN 64
#define VMXNET3_TXTS_DESC_SIZE_MASK  (VMXNET3_TXTS_DESC_SIZE_ALIGN - 1)

/* Max ring size */
#define VMXNET3_TX_RING_MAX_SIZE   4096
#define VMXNET3_TC_RING_MAX_SIZE   4096
#define VMXNET3_RX_RING_MAX_SIZE   4096
#define VMXNET3_RX_RING2_MAX_SIZE  4096
#define VMXNET3_RC_RING_MAX_SIZE   8192

/* Large enough to accommodate typical payload + encap + extension header */
#define VMXNET3_RXDATA_DESC_MAX_SIZE   2048
#define VMXNET3_TXDATA_DESC_MIN_SIZE   128
#define VMXNET3_TXDATA_DESC_MAX_SIZE   2048

#define VMXNET3_TXTS_DESC_MAX_SIZE   256
#define VMXNET3_RXTS_DESC_MAX_SIZE   256

/* a list of reasons for queue stop */

#define VMXNET3_ERR_NOEOP        0x80000000  /* cannot find the EOP desc of a pkt */
#define VMXNET3_ERR_TXD_REUSE    0x80000001  /* reuse a TxDesc before tx completion */
#define VMXNET3_ERR_BIG_PKT      0x80000002  /* too many TxDesc for a pkt */
#define VMXNET3_ERR_DESC_NOT_SPT 0x80000003  /* descriptor type not supported */
#define VMXNET3_ERR_SMALL_BUF    0x80000004  /* type 0 buffer too small */
#define VMXNET3_ERR_STRESS       0x80000005  /* stress option firing in vmkernel */
#define VMXNET3_ERR_SWITCH       0x80000006  /* mode switch failure */
#define VMXNET3_ERR_TXD_INVALID  0x80000007  /* invalid TxDesc */

/* completion descriptor types */
#define VMXNET3_CDTYPE_TXCOMP      0    /* Tx Completion Descriptor */
#define VMXNET3_CDTYPE_RXCOMP      3    /* Rx Completion Descriptor */
#define VMXNET3_CDTYPE_RXCOMP_LRO  4    /* Rx Completion Descriptor for LRO */

#define VMXNET3_GOS_BITS_UNK    0   /* unknown */
#define VMXNET3_GOS_BITS_32     1
#define VMXNET3_GOS_BITS_64     2

#define VMXNET3_GOS_TYPE_UNK        0 /* unknown */
#define VMXNET3_GOS_TYPE_LINUX      1
#define VMXNET3_GOS_TYPE_WIN        2
#define VMXNET3_GOS_TYPE_SOLARIS    3
#define VMXNET3_GOS_TYPE_FREEBSD    4
#define VMXNET3_GOS_TYPE_PXE        5

/* All structures in DriverShared are padded to multiples of 8 bytes */

#pragma pack(push, 1)
typedef struct Vmxnet3_GOSInfo {
#ifdef __BIG_ENDIAN_BITFIELD
   uint32   gosMisc: 10;    /* other info about gos */
   uint32   gosVer:  16;    /* gos version */
   uint32   gosType: 4;     /* which guest */
   uint32   gosBits: 2;     /* 32-bit or 64-bit? */
#else
   uint32   gosBits: 2;     /* 32-bit or 64-bit? */
   uint32   gosType: 4;     /* which guest */
   uint32   gosVer:  16;    /* gos version */
   uint32   gosMisc: 10;    /* other info about gos */
#endif  /* __BIG_ENDIAN_BITFIELD */
} Vmxnet3_GOSInfo;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_DriverInfo {
   __le32          version;        /* driver version */
   Vmxnet3_GOSInfo gos;
   __le32          vmxnet3RevSpt;  /* vmxnet3 revision supported */
   __le32          uptVerSpt;      /* upt version supported */
} Vmxnet3_DriverInfo;
#pragma pack(pop)

#define VMXNET3_REV1_MAGIC  0xbabefee1

/* 
 * QueueDescPA must be 128 bytes aligned. It points to an array of
 * Vmxnet3_TxQueueDesc followed by an array of Vmxnet3_RxQueueDesc.
 * The number of Vmxnet3_TxQueueDesc/Vmxnet3_RxQueueDesc are specified by
 * Vmxnet3_MiscConf.numTxQueues/numRxQueues, respectively.
 */
#define VMXNET3_QUEUE_DESC_ALIGN  128

#pragma pack(push, 1)
typedef struct Vmxnet3_MiscConf {
   Vmxnet3_DriverInfo driverInfo;
   __le64             uptFeatures;
   __le64             ddPA;         /* driver data PA */
   __le64             queueDescPA;  /* queue descriptor table PA */
   __le32             ddLen;        /* driver data len */
   __le32             queueDescLen; /* queue descriptor table len, in bytes */
   __le32             mtu;
   __le16             maxNumRxSG;
   uint8              numTxQueues;
   uint8              numRxQueues;
   __le32             reserved[4];
} Vmxnet3_MiscConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_TxQueueConf {
   __le64    txRingBasePA;
   __le64    dataRingBasePA;
   __le64    compRingBasePA;
   __le64    ddPA;         /* driver data */
   __le64    reserved;
   __le32    txRingSize;   /* # of tx desc */
   __le32    dataRingSize; /* # of data desc */
   __le32    compRingSize; /* # of comp desc */
   __le32    ddLen;        /* size of driver data */
   uint8     intrIdx;
   uint8     _pad1[1];
   __le16    txDataRingDescSize;
   uint8     _pad2[4];
} Vmxnet3_TxQueueConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_RxQueueConf {
   __le64    rxRingBasePA[2];
   __le64    compRingBasePA;
   __le64    ddPA;            /* driver data */
   __le64    rxDataRingBasePA;
   __le32    rxRingSize[2];   /* # of rx desc */
   __le32    compRingSize;    /* # of rx comp desc */
   __le32    ddLen;           /* size of driver data */
   uint8     intrIdx;
   uint8     _pad1[1];
   __le16    rxDataRingDescSize; /* size of rx data ring buffer */
   uint8     _pad2[4];
} Vmxnet3_RxQueueConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_LatencyConf {
   uint16 sampleRate;
   uint16 pad;
} Vmxnet3_LatencyConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_TxQueueTSConf {
   __le64    txTSRingBasePA;
   __le16    txTSRingDescSize; /* size of tx timestamp ring buffer */
   uint16    pad;
   Vmxnet3_LatencyConf latencyConf;
} Vmxnet3_TxQueueTSConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_RxQueueTSConf {
   __le64    rxTSRingBasePA;
   __le16    rxTSRingDescSize; /* size of rx timestamp ring buffer */
   uint16    pad[3];
} Vmxnet3_RxQueueTSConf;
#pragma pack(pop)

enum vmxnet3_intr_mask_mode {
   VMXNET3_IMM_AUTO   = 0,
   VMXNET3_IMM_ACTIVE = 1,
   VMXNET3_IMM_LAZY   = 2
};

enum vmxnet3_intr_type {
   VMXNET3_IT_AUTO = 0,
   VMXNET3_IT_INTX = 1,
   VMXNET3_IT_MSI  = 2,
   VMXNET3_IT_MSIX = 3
};

#define VMXNET3_MAX_TX_QUEUES  8
#define VMXNET3_MAX_RX_QUEUES  16
/* addition 1 for events */
#define VMXNET3_MAX_INTRS      25

/* Version 6 and later will use below macros */
#define VMXNET3_EXT_MAX_TX_QUEUES  32
#define VMXNET3_EXT_MAX_RX_QUEUES  32
/* addition 1 for events */
#define VMXNET3_EXT_MAX_INTRS      65
#define VMXNET3_FIRST_SET_INTRS    64

/* value of intrCtrl */
#define VMXNET3_IC_DISABLE_ALL  0x1   /* bit 0 */

#define VMXNET3_COAL_STATIC_MAX_DEPTH        128
#define VMXNET3_COAL_RBC_MIN_RATE            100
#define VMXNET3_COAL_RBC_MAX_RATE            100000

enum Vmxnet3_CoalesceMode {
   VMXNET3_COALESCE_DISABLED   = 0,
   VMXNET3_COALESCE_ADAPT      = 1,
   VMXNET3_COALESCE_STATIC     = 2,
   VMXNET3_COALESCE_RBC        = 3
};

#pragma pack(push, 1)
typedef struct Vmxnet3_CoalesceRbc {
   uint32 rbc_rate;
} Vmxnet3_CoalesceRbc;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_CoalesceStatic {
   uint32 tx_depth;
   uint32 tx_comp_depth;
   uint32 rx_depth;
} Vmxnet3_CoalesceStatic;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_CoalesceScheme {
   enum Vmxnet3_CoalesceMode coalMode;
   union {
      Vmxnet3_CoalesceRbc    coalRbc;
      Vmxnet3_CoalesceStatic coalStatic;
   } coalPara;
} Vmxnet3_CoalesceScheme;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_IntrConf {
   uint8  autoMask;      /* on/off flag */
   uint8  numIntrs;      /* # of interrupts */
   uint8  eventIntrIdx;
   uint8  modLevels[VMXNET3_MAX_INTRS]; /* moderation level for each intr */
   __le32 intrCtrl;
   __le32 reserved[2];
} Vmxnet3_IntrConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_IntrConfExt {
   uint8  autoMask;
   uint8  numIntrs;      /* # of interrupts */
   uint8  eventIntrIdx;
   uint8  reserved;
   __le32 intrCtrl;
   __le32 reserved1;
   uint8  modLevels[VMXNET3_EXT_MAX_INTRS]; /* moderation level for each intr */
   uint8  reserved2[3];
} Vmxnet3_IntrConfExt;
#pragma pack(pop)

/* one bit per VLAN ID, the size is in the units of uint32 */
#define VMXNET3_VFT_SIZE  (4096 / (sizeof(uint32) * 8))

#pragma pack(push, 1)
typedef struct Vmxnet3_QueueStatus {
   uint8   stopped;    /* on/off flag */
   uint8   _pad[3];
   __le32  error;
} Vmxnet3_QueueStatus;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_TxQueueCtrl {
   __le32  txNumDeferred;
   __le32  txThreshold;
   __le64  reserved;
} Vmxnet3_TxQueueCtrl;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_RxQueueCtrl {
   uint8   updateRxProd;   /* on/off flag */
   uint8   _pad[7];
   __le64  reserved;
} Vmxnet3_RxQueueCtrl;
#pragma pack(pop)

#define VMXNET3_RXM_UCAST     0x01  /* unicast only */
#define VMXNET3_RXM_MCAST     0x02  /* multicast passing the filters */
#define VMXNET3_RXM_BCAST     0x04  /* broadcast only */
#define VMXNET3_RXM_ALL_MULTI 0x08  /* all multicast */
#define VMXNET3_RXM_PROMISC   0x10  /* promiscuous */

#pragma pack(push, 1)
typedef struct Vmxnet3_RxFilterConf {
   __le32   rxMode;       /* VMXNET3_RXM_xxx */
   __le16   mfTableLen;   /* size of the multicast filter table */
   __le16   _pad1;
   __le64   mfTablePA;    /* PA of the multicast filters table */
   __le32   vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */
} Vmxnet3_RxFilterConf;
#pragma pack(pop)

#define ETH_ADDR_LENGTH 6

#define VMXNET3_PKTSTEERING_VERSION_ONE    1
#define VMXNET3_PKTSTEERING_CURRENT_VERSION    VMXNET3_PKTSTEERING_VERSION_ONE

typedef uint8_t Eth_Address[ETH_ADDR_LENGTH];


typedef enum {
   VMXNET3_PKTSTEERING_NOACTION,    /* Not currently supported */
   VMXNET3_PKTSTEERING_ACCEPT,      /* Steer the packet to a specified rxQid */
   VMXNET3_PKTSTEERING_REJECT,      /* Drop the packet */
   VMXNET3_PKTSTEERING_ACTION_MAX,
} Vmxnet3_PktSteeringAction;

#pragma pack(push, 1)
typedef struct Vmxnet3_PktSteeringActionData {
   uint8_t      action; /* enum Vmxnet3PktSteeringAction */
   uint8_t      rxQid;
} Vmxnet3_PktSteeringActionData;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_PktSteeringInput {
   uint16_t     l3proto;
   uint8_t      l4proto;
   uint8_t      pad;

   uint16_t     srcPort;
   uint16_t     dstPort;

   union {
      struct {
         uint32_t     srcIPv4;
         uint32_t     dstIPv4;
      };
      struct {
         uint8_t      srcIPv6[16];
         uint8_t      dstIPv6[16];
      };
   };

   Eth_Address  dstMAC;
   Eth_Address  srcMAC;
} Vmxnet3_PktSteeringInput;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_PktSteeringGeneveInput {
   uint8   optionsLength:6;  /* Length of options (in 4 bytes multiple) */
   uint8   version:2;        /* Geneve protocol version */
   uint8   reserved1:6;       /* Reserved bits */
   uint8   criticalOptions:1; /* Critical options present flag */
   uint8   oamFrame:1;        /* OAM frame flag */
   /* Protocol type of the following header using Ethernet type values */
   uint16   protocolType;

   uint32   virtualNetworkId:24; /* Virtual network identifier */
   uint32   reserved2:8;         /* Reserved bits */
} Vmxnet3_PktSteeringGeneveInput;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_PktSteeringFilterConfExt {
   Vmxnet3_PktSteeringGeneveInput ghSpec;  /* geneve hdr spec */
   Vmxnet3_PktSteeringGeneveInput ghMask;  /* geneve hdr mask */
   Vmxnet3_PktSteeringInput       ohSpec;  /* outer hdr spec */
   Vmxnet3_PktSteeringInput       ohMask;  /* outer hdr mask */
} Vmxnet3_PktSteeringFilterConfExt;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_PktSteeringFilterConf {
   uint8_t                        version;
   uint8_t                        priority;
   Vmxnet3_PktSteeringActionData  actionData;
   Vmxnet3_PktSteeringInput       spec;
   Vmxnet3_PktSteeringInput       mask;
   union {
      uint8_t                     pad[4];
      struct {
         uint32_t                 isInnerHdr:1; // spec/mask is for inner header
         uint32_t                 isExtValid:1; // is conf extention valid
         uint32_t                 pad1:30;
      };
      uint32_t                    value;
   };
   /* Following this structure is Vmxnet3_PktSteeringFilterConfExt
    * in case isExtValid is true.
    */
} Vmxnet3_PktSteeringFilterConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_PktSteeringVerInfo {
   uint8_t      version;
   uint8_t      pad;
   uint16_t     maxMasks;
   uint32_t     maxFilters;
} Vmxnet3_PktSteeringVerInfo;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_PktSteeringFilterStats {
   uint64_t      packets;
} Vmxnet3_PktSteeringFilterStats;
#pragma pack(pop)

typedef enum {
   VMXNET3_PKTSTEERING_CMD_GET_VER = 0x0, /* start of GET commands */
   VMXNET3_PKTSTEERING_CMD_GET_FILTER_STATS,

   VMXNET3_PKTSTEERING_CMD_ADD_FILTER = 0x80, /*start of SET commands */
   VMXNET3_PKTSTEERING_CMD_DEL_FILTER,
   VMXNET3_PKTSTEERING_CMD_FLUSH,
} Vmxnet3_PktSteeringCmd;

#pragma pack(push, 1)
typedef struct Vmxnet3_PktSteeringCmdMsg {
   uint16_t                       cmd; /* enum Vmxnet3PktSteeringCmd */
   uint16_t                       msgSize;
   uint32_t                       outputLen;
   uint64_t                       outputPA;
   Vmxnet3_PktSteeringFilterConf  fConf;
} Vmxnet3_PktSteeringCmdMsg;
#pragma pack(pop)

#define VMXNET3_PM_MAX_FILTERS        6
#define VMXNET3_PM_MAX_PATTERN_SIZE   128
#define VMXNET3_PM_MAX_MASK_SIZE      (VMXNET3_PM_MAX_PATTERN_SIZE / 8)

#define VMXNET3_PM_WAKEUP_MAGIC       0x01  /* wake up on magic pkts */
#define VMXNET3_PM_WAKEUP_FILTER      0x02  /* wake up on pkts matching filters */

#pragma pack(push, 1)
typedef struct Vmxnet3_PM_PktFilter {
   uint8 maskSize;
   uint8 patternSize;
   uint8 mask[VMXNET3_PM_MAX_MASK_SIZE];
   uint8 pattern[VMXNET3_PM_MAX_PATTERN_SIZE];
   uint8 pad[6];
} Vmxnet3_PM_PktFilter;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_PMConf {
   __le16               wakeUpEvents;  /* VMXNET3_PM_WAKEUP_xxx */
   uint8                numFilters;
   uint8                pad[5];
   Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS];
} Vmxnet3_PMConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_VariableLenConfDesc {
   __le32              confVer;
   __le32              confLen;
   __le64              confPA;
} Vmxnet3_VariableLenConfDesc;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_DSDevRead {
   /* read-only region for device, read by dev in response to a SET cmd */
   Vmxnet3_MiscConf     misc;
   Vmxnet3_IntrConf     intrConf;
   Vmxnet3_RxFilterConf rxFilterConf;
   Vmxnet3_VariableLenConfDesc  rssConfDesc;
   Vmxnet3_VariableLenConfDesc  pmConfDesc;
   Vmxnet3_VariableLenConfDesc  pluginConfDesc;
} Vmxnet3_DSDevRead;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_DSDevReadExt {
   /* read-only region for device, read by dev in response to a SET cmd */
   Vmxnet3_IntrConfExt     intrConfExt;
} Vmxnet3_DSDevReadExt;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_TxQueueDesc {
   Vmxnet3_TxQueueCtrl ctrl;
   Vmxnet3_TxQueueConf conf;
   /* Driver read after a GET command */
   Vmxnet3_QueueStatus status;
   UPT1_TxStats        stats;
   Vmxnet3_TxQueueTSConf tsConf;
   uint8               _pad[72]; /* 128 aligned */
} Vmxnet3_TxQueueDesc;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_RxQueueDesc {
   Vmxnet3_RxQueueCtrl ctrl;
   Vmxnet3_RxQueueConf conf;
   /* Driver read after a GET command */
   Vmxnet3_QueueStatus status;
   UPT1_RxStats        stats;
   Vmxnet3_RxQueueTSConf tsConf;
   uint8               _pad[72]; /* 128 aligned */
} Vmxnet3_RxQueueDesc;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_SetPolling {
   uint8               enablePolling;
} Vmxnet3_SetPolling;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_MemoryRegion {
   __le64            startPA;  // starting physical address
   __le32            length;   // limit the length to be less than 4G
   /*
    * any bits is set in txQueueBits or rxQueueBits indicate this region
    * is applicable for the relevant queue
    */
   __le16            txQueueBits; // bit n corresponding to tx queue n
   __le16            rxQueueBits; // bit n corresponding to rx queueb n
} Vmxnet3_MemoryRegion;
#pragma pack(pop)

/*
 * Assume each queue can have upto 16 memory region
 * we have 8 + 8 = 16 queues. So max regions is
 * defined as 16 * 16
 * when more region is passed to backend, the handling
 * is undefined, Backend can choose to fail the the request
 * or ignore the extra region.
 */
#define MAX_MEMORY_REGION_PER_QUEUE 16
#define MAX_MEMORY_REGION_PER_DEVICE (16 * 16)

#pragma pack(push, 1)
typedef struct Vmxnet3_MemRegs {
   __le16           numRegs;
   __le16           pad[3];
   Vmxnet3_MemoryRegion memRegs[1];
} Vmxnet3_MemRegs;
#pragma pack(pop)

typedef enum Vmxnet3_RSSField {
   VMXNET3_RSS_FIELDS_TCPIP4 = 0x1UL,
   VMXNET3_RSS_FIELDS_TCPIP6 = 0x2UL,
   VMXNET3_RSS_FIELDS_UDPIP4 = 0x4UL,
   VMXNET3_RSS_FIELDS_UDPIP6 = 0x8UL,
   VMXNET3_RSS_FIELDS_ESPIP4 = 0x10UL,
   VMXNET3_RSS_FIELDS_ESPIP6 = 0x20UL,

   VMXNET3_RSS_FIELDS_INNER_IP4 = 0x100UL,
   VMXNET3_RSS_FIELDS_INNER_TCPIP4 = 0x200UL,
   VMXNET3_RSS_FIELDS_INNER_IP6 = 0x400UL,
   VMXNET3_RSS_FIELDS_INNER_TCPIP6 = 0x800UL,
   VMXNET3_RSS_FIELDS_INNER_UDPIP4 = 0x1000UL,
   VMXNET3_RSS_FIELDS_INNER_UDPIP6 = 0x2000UL,
   VMXNET3_RSS_FIELDS_INNER_ESPIP4 = 0x4000UL,
   VMXNET3_RSS_FIELDS_INNER_ESPIP6 = 0x8000UL,
} Vmxnet3_RSSField;

#pragma pack(push, 1)
typedef struct Vmxnet3_EncapDstPort {
   __le16            geneveDstPort;
   __le16            vxlanDstPort;
} Vmxnet3_EncapDstPort;
#pragma pack(pop)

/*
 * Based on index from ESP SPI, how to map the index to the rx queue ID.
 * The following two algos are defined:
 *
 * VMXNET3_ESP_QS_IND_TABLE:  the index will be used to index the RSS
 * indirection table.
 *
 * VMXNET3_ESP_QS_QUEUE_MASK: the index will be used to index to the
 * preconfigured queue mask. The index itself is treated as queue ID. If
 * the relevant bit in queue mask is set, the packet will be forwarded
 * the queue with the index as queue ID. Otherwise, the packet will not
 * be treated as ESP packet for RSS purpose.
 */

typedef enum Vmxnet3_ESPQueueSelectionAlgo {
   VMXNET3_ESP_QS_IND_TABLE = 0x01,
   VMXNET3_ESP_QS_QUEUE_MASK = 0x02,
   VMXNET3_ESP_QS_MAX,
} Vmxnet3_ESPQueueSelectionAlgo;

#pragma pack(push, 1)
typedef struct Vmxnet3_ESPQueueSelectionConf {
   uint8       spiStartBit;  /* from least significant bit of SPI. */
   uint8       spiMaskWidth; /* how many bits in SPI will be used. */
   uint16      qsAlgo;       /* see Vmxnet3_ESPQueueSelectionAlgo */
   /* queue ID mask used for ESP RSS. Valid when
    * qsAlgo is VMXNET3_ESP_QS_QUEUE_MASK.
    */
   uint32      espQueueMask;  /* max of 32 queues supported */
} Vmxnet3_ESPQueueSelectionConf;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_RingBufferSize {
   __le16             ring1BufSizeType0;
   __le16             ring1BufSizeType1;
   __le16             ring2BufSizeType1;
   __le16             pad;
} Vmxnet3_RingBufferSize;
#pragma pack(pop)

/*
 * If a command data does not exceed 16 bytes, it can use
 * the shared memory directly. Otherwise use variable length
 * configuration descriptor to pass the data.
 */
#pragma pack(push, 1)
typedef union Vmxnet3_CmdInfo {
   Vmxnet3_VariableLenConfDesc varConf;
   Vmxnet3_SetPolling          setPolling;
   Vmxnet3_RSSField            setRSSFields;
   Vmxnet3_EncapDstPort        encapDstPort;
   Vmxnet3_ESPQueueSelectionConf espQSConf;
   Vmxnet3_RingBufferSize      ringBufSize;
   __le64                      data[2];
} Vmxnet3_CmdInfo;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct Vmxnet3_DriverShared {
   __le32               magic;
   __le32               pad; /* make devRead start at 64-bit boundaries */
   Vmxnet3_DSDevRead    devRead;
   __le32               ecr;
   __le32               reserved;
   union {
      __le32            reserved1[4];
      Vmxnet3_CmdInfo   cmdInfo; /* only valid in the context of executing the relevant
                                  * command.
                                  */
   } cu;
   Vmxnet3_DSDevReadExt devReadExt;
} Vmxnet3_DriverShared;
#pragma pack(pop)

#define VMXNET3_ECR_RQERR       (1 << 0)
#define VMXNET3_ECR_TQERR       (1 << 1)
#define VMXNET3_ECR_LINK        (1 << 2)
#define VMXNET3_ECR_DIC         (1 << 3)
#define VMXNET3_ECR_DEBUG       (1 << 4)

/* flip the gen bit of a ring */
#define VMXNET3_FLIP_RING_GEN(gen) ((gen) = (gen) ^ 0x1)

/* only use this if moving the idx won't affect the gen bit */
#define VMXNET3_INC_RING_IDX_ONLY(idx, ring_size) \
do {\
   (idx)++;\
   if (UNLIKELY((idx) == (ring_size))) {\
      (idx) = 0;\
   }\
} while (0)

#define VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid) \
   (vfTable)[(vid) >> 5] |= (1 << ((vid) & 31))
#define VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid) \
   (vfTable)[(vid) >> 5] &= ~(1 << ((vid) & 31))

#define VMXNET3_VFTABLE_ENTRY_IS_SET(vfTable, vid) \
   (((vfTable)[(vid) >> 5] & (1 << ((vid) & 31))) != 0)

#define VMXNET3_MAX_MTU     9000
#define VMXNET3_MIN_MTU     60

#define VMXNET3_LINK_UP         1    //up
#define VMXNET3_LINK_DOWN       0

#define VMXWIFI_DRIVER_SHARED_LEN 8192

#define VMXNET3_DID_PASSTHRU    0xFFFF


#define VMXNET3_DCR_ERROR    31    /* error when bit 31 of DCR * is set */

#define VMXNET3_CAP_UDP_RSS           0          /* bit 0 of DCR 0 */
#define VMXNET3_CAP_ESP_RSS_IPV4      1          /* bit 1 of DCR 0 */
#define VMXNET3_CAP_GENEVE_CHECKSUM_OFFLOAD   2  /* bit 2 of DCR 0 */
#define VMXNET3_CAP_GENEVE_TSO        3          /* bit 3 of DCR 0 */
#define VMXNET3_CAP_VXLAN_CHECKSUM_OFFLOAD    4  /* bit 4 of DCR 0 */
#define VMXNET3_CAP_VXLAN_TSO         5          /* bit 5 of DCR 0 */
#define VMXENT3_CAP_GENEVE_OUTER_CHECKSUM_OFFLOAD 6 /* bit 6 of DCR 0 */
#define VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD  7 /* bit 7 of DCR 0 */
#define VMXNET3_CAP_VERSION_4_MAX \
   (VMXNET3_CAP_VXLAN_OUTER_CHECKSUM_OFFLOAD + 1)

#define VMXNET3_CAP_PKT_STEERING_IPV4 8   /* bit 8 of DCR 0 */
#define VMXNET3_CAP_VERSION_5_MAX  (VMXNET3_CAP_PKT_STEERING_IPV4 + 1)

#define VMXNET3_CAP_ESP_RSS_IPV6      9   /* bit 9 of DCR 0 */
#define VMXNET3_CAP_ESP_OVER_UDP_RSS 10   /* bit 10 of DCR 0 */
#define VMXNET3_CAP_INNER_RSS        11   /* bit 11 of DCR 0 */
#define VMXNET3_CAP_INNER_ESP_RSS    12   /* bit 12 of DCR 0 */
#define VMXNET3_CAP_VERSION_6_MAX  (VMXNET3_CAP_INNER_ESP_RSS + 1)

#define VMXNET3_CAP_CRC32_HASH_FUNC  13   /* bit 13 of DCR 0 */
#define VMXNET3_CAP_OAM_FILTER       14   /* bit 14 of DCR 0 */
#define VMXNET3_CAP_ESP_QS           15   /* bit 15 of DCR 0 */
#define VMXNET3_CAP_LARGE_BAR        16   /* bit 16 of DCR 0 */
#define VMXNET3_CAP_OOORX_COMP       17   /* bit 17 of DCR 0 */
#define VMXNET3_CAP_VERSION_7_MAX  (VMXNET3_CAP_OOORX_COMP + 1)

#define VMXNET3_CAP_PKT_STEERING_IPV6  18 /* bit 19 of DCR 0 */
#define VMXNET3_CAP_VERSION_8_MAX  (VMXNET3_CAP_PKT_STEERING_IPV6 + 1)

/* when new capability is introduced, update VMXNET3_CAP_MAX */
#define VMXNET3_CAP_MAX              VMXNET3_CAP_VERSION_8_MAX


#define VMXNET3_OFFLOAD_TSO         (1 << 0)
#define VMXNET3_OFFLOAD_LRO         (1 << 1)

#endif /* _VMXNET3_DEFS_H_ */
   07070100000272000081A40000000000000000000000016822550500001B37000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/modules/shared/vmxnet/vmxnet_def.h /*********************************************************
 * Copyright (c) 1999-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * 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
 *
 *********************************************************/

/*********************************************************
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef _VMXNET_DEF_H_
#define _VMXNET_DEF_H_

#define INCLUDE_ALLOW_USERLEVEL

#define INCLUDE_ALLOW_MODULE
#define INCLUDE_ALLOW_VMK_MODULE
#define INCLUDE_ALLOW_VMKERNEL
#define INCLUDE_ALLOW_DISTRIBUTE
#include "includeCheck.h"

#include "net_sg.h"
#include "vmnet_def.h"


/*
 *   Vmxnet I/O ports, used by both the vmxnet driver and 
 *   the device emulation code.
 */

#define VMXNET_INIT_ADDR		0x00
#define VMXNET_INIT_LENGTH		0x04
#define VMXNET_TX_ADDR		        0x08
#define VMXNET_COMMAND_ADDR		0x0c
#define VMXNET_MAC_ADDR			0x10
#define VMXNET_LOW_VERSION		0x18
#define VMXNET_HIGH_VERSION		0x1c
#define VMXNET_STATUS_ADDR		0x20
#define VMXNET_TOE_INIT_ADDR            0x24
#define VMXNET_APROM_ADDR               0x28
#define VMXNET_INT_ENABLE_ADDR          0x30
#define VMXNET_WAKE_PKT_PATTERNS        0x34

/*
 * Vmxnet command register values.
 */
#define VMXNET_CMD_INTR_ACK		0x0001
#define VMXNET_CMD_UPDATE_LADRF		0x0002
#define VMXNET_CMD_UPDATE_IFF		0x0004
#define VMXNET_CMD_UNUSED 1		0x0008
#define VMXNET_CMD_UNUSED_2		0x0010
#define VMXNET_CMD_INTR_DISABLE  	0x0020
#define VMXNET_CMD_INTR_ENABLE   	0x0040
#define VMXNET_CMD_UNUSED_3		0x0080
#define VMXNET_CMD_CHECK_TX_DONE	0x0100
#define VMXNET_CMD_GET_NUM_RX_BUFFERS	0x0200
#define VMXNET_CMD_GET_NUM_TX_BUFFERS	0x0400
#define VMXNET_CMD_PIN_TX_BUFFERS	0x0800
#define VMXNET_CMD_GET_CAPABILITIES	0x1000
#define VMXNET_CMD_GET_FEATURES		0x2000
#define VMXNET_CMD_SET_POWER_FULL       0x4000
#define VMXNET_CMD_SET_POWER_LOW        0x8000

/*
 * Vmxnet status register values.
 */
#define VMXNET_STATUS_CONNECTED		0x0001
#define VMXNET_STATUS_ENABLED		0x0002
#define VMXNET_STATUS_TX_PINNED         0x0004

/*
 * Values for the interface flags.
 */
#define VMXNET_IFF_PROMISC		0x01
#define VMXNET_IFF_BROADCAST		0x02
#define VMXNET_IFF_MULTICAST		0x04
#define VMXNET_IFF_DIRECTED             0x08

/*
 * Length of the multicast address filter.
 */
#define VMXNET_MAX_LADRF		2

/*
 * Size of Vmxnet APROM. 
 */
#define VMXNET_APROM_SIZE 6

/*
 * An invalid ring index.
 */
#define VMXNET_INVALID_RING_INDEX	(-1)

/*
 * Features that are implemented by the driver.  These are driver
 * specific so not all features will be listed here.  In addition not all
 * drivers have to pay attention to these feature flags.
 *
 *  VMXNET_FEATURE_ZERO_COPY_TX 	The driver won't do any copies as long as
 *					the packet length is > 
 *					Vmxnet_DriverData.minTxPhysLength.
 * 
 *  VMXNET_FEATURE_TSO                  The driver will use the TSO capabilities
 *                                      of the underlying hardware if available 
 *                                      and enabled.
 *
 *  VMXNET_FEATURE_JUMBO_FRAME          The driver can send/rcv jumbo frame 
 *
 *  VMXNET_FEATURE_LPD                  The backend can deliver large pkts
 */
#define VMXNET_FEATURE_ZERO_COPY_TX             0x01
#define VMXNET_FEATURE_TSO                      0x02
#define VMXNET_FEATURE_JUMBO_FRAME              0x04
#define VMXNET_FEATURE_LPD                      0x08

/*
 * Define the set of capabilities required by each feature above
 */
#define VMXNET_FEATURE_ZERO_COPY_TX_CAPS        VMXNET_CAP_SG
#define VMXNET_FEATURE_TSO_CAPS                 VMXNET_CAP_TSO
#define VMXNET_HIGHEST_FEATURE_BIT              VMXNET_FEATURE_TSO

#define VMXNET_INC(val, max)     \
   val++;                        \
   if (UNLIKELY(val == max)) {   \
      val = 0;                   \
   }

/*
 * code that just wants to switch on the different versions of the
 * guest<->implementation protocol can cast driver data to this.
 */
typedef uint32 Vmxnet_DDMagic;

/*
 * Wake packet pattern commands sent through VMXNET_WAKE_PKT_PATTERNS port
 */

#define VMXNET_PM_OPCODE_START 3 /* args: cnt of wake packet patterns */
#define VMXNET_PM_OPCODE_LEN   2 /* args: index of wake packet pattern */
                                 /*       number of pattern byte values */
#define VMXNET_PM_OPCODE_DATA  1 /* args: index of wake packet pattern */
                                 /*       offset in pattern byte values list */
                                 /*       packet byte offset */
                                 /*       packet byte value */
#define VMXNET_PM_OPCODE_END   0 /* args: <none> */

/*
 * The strict latency configuration
 */
#define VMXNET3_SL_MIN_QUEUES			1
#define VMXNET3_SL_MAX_QUEUES			32
#define VMXNET3_SL_MIN_RING_DESC_SIZE		128
#define VMXNET3_SL_MAX_RING_DESC_SIZE		2048
#define VMXNET3_SL_RING_SIZE_INCREMENT_UNIT	64
#define VMXNET3_SL_DEFAULT_LATENCY_MEASUREMENT  1
#define VMXNET3_SL_DEFAULT_TX_QUEUES		1
#define VMXNET3_SL_DEFAULT_RX_QUEUES		1
#define VMXNET3_SL_DEFAULT_TX_RING_DESC_SIZE	256
#define VMXNET3_SL_DEFAULT_RX_RING_DESC_SIZE	256
#define VMXNET3_SL_DEFAULT_DISABLE_OFFLOADS	"TSO_LRO"

typedef union Vmxnet_WakePktCmd {
   uint32 pktData : 32;
   struct {
      unsigned cmd : 2; /* wake packet pattern cmd [from list above] */
      unsigned cnt : 3; /* cnt wk pkt pttrns 1..MAX_NUM_FILTER_PTTRNS */
      unsigned ind : 3; /* ind wk pkt pttrn 0..MAX_NUM_FILTER_PTTRNS-1 */
      unsigned lenOff : 8; /* num pttrn byte vals 1..MAX_PKT_FILTER_SIZE */
                           /* OR offset in pattern byte values list */
                           /* 0..MAX_PKT_FILTER_SIZE-1 */
      unsigned byteOff : 8; /* pkt byte offset 0..MAX_PKT_FILTER_SIZE-1 */
      unsigned byteVal : 8; /* packet byte value 0..255 */
   } pktPttrn;
} Vmxnet_WakePktCmd;

#endif /* _VMXNET_DEF_H_ */
 07070100000273000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris    07070100000274000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmblock    07070100000275000081A400000000000000000000000168225505000046CA000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmblock/COPYING    
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0

1. Definitions.

    1.1. "Contributor" means each individual or entity that creates
         or contributes to the creation of Modifications.

    1.2. "Contributor Version" means the combination of the Original
         Software, prior Modifications used by a Contributor (if any),
         and the Modifications made by that particular Contributor.

    1.3. "Covered Software" means (a) the Original Software, or (b)
         Modifications, or (c) the combination of files containing
         Original Software with files containing Modifications, in
         each case including portions thereof.

    1.4. "Executable" means the Covered Software in any form other
         than Source Code.

    1.5. "Initial Developer" means the individual or entity that first
         makes Original Software available under this License.

    1.6. "Larger Work" means a work which combines Covered Software or
         portions thereof with code not governed by the terms of this
         License.

    1.7. "License" means this document.

    1.8. "Licensable" means having the right to grant, to the maximum
         extent possible, whether at the time of the initial grant or
         subsequently acquired, any and all of the rights conveyed
         herein.

    1.9. "Modifications" means the Source Code and Executable form of
         any of the following:

        A. Any file that results from an addition to, deletion from or
           modification of the contents of a file containing Original
           Software or previous Modifications;

        B. Any new file that contains any part of the Original
           Software or previous Modifications; or

        C. Any new file that is contributed or otherwise made
           available under the terms of this License.

    1.10. "Original Software" means the Source Code and Executable
          form of computer software code that is originally released
          under this License.

    1.11. "Patent Claims" means any patent claim(s), now owned or
          hereafter acquired, including without limitation, method,
          process, and apparatus claims, in any patent Licensable by
          grantor.

    1.12. "Source Code" means (a) the common form of computer software
          code in which modifications are made and (b) associated
          documentation included in or with such code.

    1.13. "You" (or "Your") means an individual or a legal entity
          exercising rights under, and complying with all of the terms
          of, this License.  For legal entities, "You" includes any
          entity which controls, is controlled by, or is under common
          control with You.  For purposes of this definition,
          "control" means (a) the power, direct or indirect, to cause
          the direction or management of such entity, whether by
          contract or otherwise, or (b) ownership of more than fifty
          percent (50%) of the outstanding shares or beneficial
          ownership of such entity.

2. License Grants.

    2.1. The Initial Developer Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, the Initial
    Developer hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Initial Developer, to use,
            reproduce, modify, display, perform, sublicense and
            distribute the Original Software (or portions thereof),
            with or without Modifications, and/or as part of a Larger
            Work; and

        (b) under Patent Claims infringed by the making, using or
            selling of Original Software, to make, have made, use,
            practice, sell, and offer for sale, and/or otherwise
            dispose of the Original Software (or portions thereof).

        (c) The licenses granted in Sections 2.1(a) and (b) are
            effective on the date Initial Developer first distributes
            or otherwise makes the Original Software available to a
            third party under the terms of this License.

        (d) Notwithstanding Section 2.1(b) above, no patent license is
            granted: (1) for code that You delete from the Original
            Software, or (2) for infringements caused by: (i) the
            modification of the Original Software, or (ii) the
            combination of the Original Software with other software
            or devices.

    2.2. Contributor Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, each
    Contributor hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Contributor to use, reproduce,
            modify, display, perform, sublicense and distribute the
            Modifications created by such Contributor (or portions
            thereof), either on an unmodified basis, with other
            Modifications, as Covered Software and/or as part of a
            Larger Work; and

        (b) under Patent Claims infringed by the making, using, or
            selling of Modifications made by that Contributor either
            alone and/or in combination with its Contributor Version
            (or portions of such combination), to make, use, sell,
            offer for sale, have made, and/or otherwise dispose of:
            (1) Modifications made by that Contributor (or portions
            thereof); and (2) the combination of Modifications made by
            that Contributor with its Contributor Version (or portions
            of such combination).

        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
            effective on the date Contributor first distributes or
            otherwise makes the Modifications available to a third
            party.

        (d) Notwithstanding Section 2.2(b) above, no patent license is
            granted: (1) for any code that Contributor has deleted
            from the Contributor Version; (2) for infringements caused
            by: (i) third party modifications of Contributor Version,
            or (ii) the combination of Modifications made by that
            Contributor with other software (except as part of the
            Contributor Version) or other devices; or (3) under Patent
            Claims infringed by Covered Software in the absence of
            Modifications made by that Contributor.

3. Distribution Obligations.

    3.1. Availability of Source Code.

    Any Covered Software that You distribute or otherwise make
    available in Executable form must also be made available in Source
    Code form and that Source Code form must be distributed only under
    the terms of this License.  You must include a copy of this
    License with every copy of the Source Code form of the Covered
    Software You distribute or otherwise make available.  You must
    inform recipients of any such Covered Software in Executable form
    as to how they can obtain such Covered Software in Source Code
    form in a reasonable manner on or through a medium customarily
    used for software exchange.

    3.2. Modifications.

    The Modifications that You create or to which You contribute are
    governed by the terms of this License.  You represent that You
    believe Your Modifications are Your original creation(s) and/or
    You have sufficient rights to grant the rights conveyed by this
    License.

    3.3. Required Notices.

    You must include a notice in each of Your Modifications that
    identifies You as the Contributor of the Modification.  You may
    not remove or alter any copyright, patent or trademark notices
    contained within the Covered Software, or any notices of licensing
    or any descriptive text giving attribution to any Contributor or
    the Initial Developer.

    3.4. Application of Additional Terms.

    You may not offer or impose any terms on any Covered Software in
    Source Code form that alters or restricts the applicable version
    of this License or the recipients' rights hereunder.  You may
    choose to offer, and to charge a fee for, warranty, support,
    indemnity or liability obligations to one or more recipients of
    Covered Software.  However, you may do so only on Your own behalf,
    and not on behalf of the Initial Developer or any Contributor.
    You must make it absolutely clear that any such warranty, support,
    indemnity or liability obligation is offered by You alone, and You
    hereby agree to indemnify the Initial Developer and every
    Contributor for any liability incurred by the Initial Developer or
    such Contributor as a result of warranty, support, indemnity or
    liability terms You offer.

    3.5. Distribution of Executable Versions.

    You may distribute the Executable form of the Covered Software
    under the terms of this License or under the terms of a license of
    Your choice, which may contain terms different from this License,
    provided that You are in compliance with the terms of this License
    and that the license for the Executable form does not attempt to
    limit or alter the recipient's rights in the Source Code form from
    the rights set forth in this License.  If You distribute the
    Covered Software in Executable form under a different license, You
    must make it absolutely clear that any terms which differ from
    this License are offered by You alone, not by the Initial
    Developer or Contributor.  You hereby agree to indemnify the
    Initial Developer and every Contributor for any liability incurred
    by the Initial Developer or such Contributor as a result of any
    such terms You offer.

    3.6. Larger Works.

    You may create a Larger Work by combining Covered Software with
    other code not governed by the terms of this License and
    distribute the Larger Work as a single product.  In such a case,
    You must make sure the requirements of this License are fulfilled
    for the Covered Software.

4. Versions of the License.

    4.1. New Versions.

    Sun Microsystems, Inc. is the initial license steward and may
    publish revised and/or new versions of this License from time to
    time.  Each version will be given a distinguishing version number.
    Except as provided in Section 4.3, no one other than the license
    steward has the right to modify this License.

    4.2. Effect of New Versions.

    You may always continue to use, distribute or otherwise make the
    Covered Software available under the terms of the version of the
    License under which You originally received the Covered Software.
    If the Initial Developer includes a notice in the Original
    Software prohibiting it from being distributed or otherwise made
    available under any subsequent version of the License, You must
    distribute and make the Covered Software available under the terms
    of the version of the License under which You originally received
    the Covered Software.  Otherwise, You may also choose to use,
    distribute or otherwise make the Covered Software available under
    the terms of any subsequent version of the License published by
    the license steward.

    4.3. Modified Versions.

    When You are an Initial Developer and You want to create a new
    license for Your Original Software, You may create and use a
    modified version of this License if You: (a) rename the license
    and remove any references to the name of the license steward
    (except to note that the license differs from this License); and
    (b) otherwise make it clear that the license contains terms which
    differ from this License.

5. DISCLAIMER OF WARRANTY.

    COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
    BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
    INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
    SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
    PURPOSE OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND
    PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU.  SHOULD ANY
    COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
    INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
    NECESSARY SERVICING, REPAIR OR CORRECTION.  THIS DISCLAIMER OF
    WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.  NO USE OF
    ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
    DISCLAIMER.

6. TERMINATION.

    6.1. This License and the rights granted hereunder will terminate
    automatically if You fail to comply with terms herein and fail to
    cure such breach within 30 days of becoming aware of the breach.
    Provisions which, by their nature, must remain in effect beyond
    the termination of this License shall survive.

    6.2. If You assert a patent infringement claim (excluding
    declaratory judgment actions) against Initial Developer or a
    Contributor (the Initial Developer or Contributor against whom You
    assert such claim is referred to as "Participant") alleging that
    the Participant Software (meaning the Contributor Version where
    the Participant is a Contributor or the Original Software where
    the Participant is the Initial Developer) directly or indirectly
    infringes any patent, then any and all rights granted directly or
    indirectly to You by such Participant, the Initial Developer (if
    the Initial Developer is not the Participant) and all Contributors
    under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
    notice from Participant terminate prospectively and automatically
    at the expiration of such 60 day notice period, unless if within
    such 60 day period You withdraw Your claim with respect to the
    Participant Software against such Participant either unilaterally
    or pursuant to a written agreement with Participant.

    6.3. In the event of termination under Sections 6.1 or 6.2 above,
    all end user licenses that have been validly granted by You or any
    distributor hereunder prior to termination (excluding licenses
    granted to You by any distributor) shall survive termination.

7. LIMITATION OF LIABILITY.

    UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
    (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
    INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
    COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
    LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
    CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
    LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
    STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
    COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
    INFORMED OF THE POSSIBILITY OF SUCH DAMAGES.  THIS LIMITATION OF
    LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
    INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
    APPLICABLE LAW PROHIBITS SUCH LIMITATION.  SOME JURISDICTIONS DO
    NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
    CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
    APPLY TO YOU.

8. U.S. GOVERNMENT END USERS.

    The Covered Software is a "commercial item," as that term is
    defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
    computer software" (as that term is defined at 48
    C.F.R. 252.227-7014(a)(1)) and "commercial computer software
    documentation" as such terms are used in 48 C.F.R. 12.212
    (Sept. 1995).  Consistent with 48 C.F.R. 12.212 and 48
    C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
    U.S. Government End Users acquire Covered Software with only those
    rights set forth herein.  This U.S. Government Rights clause is in
    lieu of, and supersedes, any other FAR, DFAR, or other clause or
    provision that addresses Government rights in computer software
    under this License.

9. MISCELLANEOUS.

    This License represents the complete agreement concerning subject
    matter hereof.  If any provision of this License is held to be
    unenforceable, such provision shall be reformed only to the extent
    necessary to make it enforceable.  This License shall be governed
    by the law of the jurisdiction specified in a notice contained
    within the Original Software (except to the extent applicable law,
    if any, provides otherwise), excluding such jurisdiction's
    conflict-of-law provisions.  Any litigation relating to this
    License shall be subject to the jurisdiction of the courts located
    in the jurisdiction and venue specified in a notice contained
    within the Original Software, with the losing party responsible
    for costs, including, without limitation, court costs and
    reasonable attorneys' fees and expenses.  The application of the
    United Nations Convention on Contracts for the International Sale
    of Goods is expressly excluded.  Any law or regulation which
    provides that the language of a contract shall be construed
    against the drafter shall not apply to this License.  You agree
    that You alone are responsible for compliance with the United
    States export administration regulations (and the export control
    laws and regulation of any other countries) when You use,
    distribute or otherwise make available any Covered Software.

10. RESPONSIBILITY FOR CLAIMS.

    As between Initial Developer and the Contributors, each party is
    responsible for claims and damages arising, directly or
    indirectly, out of its utilization of rights under this License
    and You agree to work with Initial Developer and Contributors to
    distribute such responsibility on an equitable basis.  Nothing
    herein is intended or shall be deemed to constitute any admission
    of liability.

  07070100000276000081A40000000000000000000000016822550500000C7A000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmblock/Makefile   #!/usr/bin/make -f
##########################################################
# Copyright (C) 2009 VMware, Inc. All rights reserved.
#
# The contents of this file are subject to the terms of the Common
# Development and Distribution License (the "License") version 1.0
# and no later version.  You may not use this file except in
# compliance with the License.
#
# You can obtain a copy of the License at
#         http://www.opensource.org/licenses/cddl1.php
#
# See the License for the specific language governing permissions
# and limitations under the License.
#
##########################################################

##
## General build locations and variables
##

MODULE   := vmblock
CFLAGS   :=
LDFLAGS  :=

# Solaris version defines for in-code #ifdefs (default to Solaris 9)
ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.11)
CFLAGS   += -DSOL11
else
ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.10)
CFLAGS   += -DSOL10
else
CFLAGS   += -DSOL9
# we don't support Solaris 9 (we could but we use memcpy/memset
# and we'd need stubs to get the module to load into the kernel).
$(error "This makefile is only used for Solaris 10 and 11")
endif
endif

CFLAGS   += -ffreestanding
CFLAGS   += -nodefaultlibs

CFLAGS   += -O2
CFLAGS   += -Wall -Werror
CFLAGS   += -I.

##
## Objects needed to build vmblock kernel module
##
VMBLOCK_OBJS := block.o
VMBLOCK_OBJS += module.o
VMBLOCK_OBJS += stubs.o
VMBLOCK_OBJS += vfsops.o
VMBLOCK_OBJS += vnops.o

VMBLOCK_32_OBJS := $(addprefix i386/, $(VMBLOCK_OBJS))
VMBLOCK_64_OBJS := $(addprefix amd64/, $(VMBLOCK_OBJS))

MODULE_32 := i386/$(MODULE)
MODULE_64 := amd64/$(MODULE)

ifdef OVT_SOURCE_DIR
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/include
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/shared/vmblock
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/solaris/vmblock
   VPATH  := $(OVT_SOURCE_DIR)/modules/shared/vmblock
endif

CFLAGS_32  := $(CFLAGS)
CFLAGS_32  += -m32
LDFLAGS_32 := $(LDFLAGS)

CFLAGS_64  := $(CFLAGS)
CFLAGS_64  += -m64
CFLAGS_64  += -mcmodel=kernel
CFLAGS_64  += -mno-red-zone
LDFLAGS_64 := $(LDFLAGS)
ifdef HAVE_GNU_LD
LDFLAGS_64 += -m elf_x86_64
else
LDFLAGS_64 += -64
endif

##
## Building targets
##
.PHONY: all prepare_dirs clean install

all: prepare_dirs $(MODULE_32) $(MODULE_64)

prepare_dirs:
	@echo "Creating build directories"
	mkdir -p i386
	mkdir -p amd64

# Build just the module for 32 bits kernel
$(MODULE_32): $(VMBLOCK_32_OBJS)
	@echo "Linking          $(MODULE_32)"
	$(LD) $(LDFLAGS_32) -r -o $(MODULE_32) $(VMBLOCK_32_OBJS)

$(VMBLOCK_32_OBJS): i386/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_32) -D_KERNEL -c $< -o $@

# Now the same for 64 bits kernel
$(MODULE_64): $(VMBLOCK_64_OBJS)
	@echo "Linking          $(MODULE_64)"
	$(LD) $(LDFLAGS_64) -r -o $(MODULE_64) $(VMBLOCK_64_OBJS)

$(VMBLOCK_64_OBJS): amd64/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_64) -D_KERNEL -c $< -o $@

clean:
	@echo "Cleaning directories"
	@rm -rf $(MODULE_32) $(VMBLOCK_32_OBJS) i386
	@rm -rf $(MODULE_64) $(VMBLOCK_32_OBJS) amd64

install:
	@echo "Installing modules"
	cp $(MODULE_32) /kernel/drv/
	chmod 755 /kernel/drv/$(MODULE)
	cp $(MODULE_64) /kernel/drv/amd64
	chmod 755 /kernel/drv/amd64/$(MODULE)
  07070100000277000081A40000000000000000000000016822550500000FAA000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmblock/module.c   /*********************************************************
 * Copyright (C) 2006 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * module.c --
 *
 *      Main loading and unloading of kernel module.
 */

#include <sys/errno.h>

#include "module.h"
#include "block.h"

static vfsdef_t VMBlockVfsDef = {
   VFSDEF_VERSION,         /* Structure version: defined in <sys/vfs.h> */
   VMBLOCK_FS_NAME,        /* Name of file system */
   VMBlockInit,            /* File system initialization routine */
   VMBLOCK_VFSSW_FLAGS,    /* File system flags */
   NULL                    /* No mount options */
};

/* Filesystem module structure */
static struct modlfs VMBlockModlfs = {
   &mod_fsops,             /* Module operation structure: for
                            * auto loading/unloading */
   "VMBlock File system",  /* Name */
   &VMBlockVfsDef          /* Filesystem type definition record */
};

static struct modlinkage VMBlockModlinkage = {
   MODREV_1,            /* Module revision: must be MODREV_1 */
   {
      &VMBlockModlfs,   /* FS module structure */
      NULL,
   }
};

#ifdef VMX86_DEBUG
/* XXX: Figure out how to pass this in at module load time. */
int LOGLEVEL = 4;
#else
int LOGLEVEL = 0;
#endif

int vmblockType;
vnodeops_t *vmblockVnodeOps;




/*
 * Module loading/unloading/info functions.
 */


/*
 *----------------------------------------------------------------------------
 *
 * _init --
 *    Invoked when module is being loaded into kernel, and is called before
 *    any function in the module.  Any state that spans all instances of the
 *    driver should be allocated and initialized here.
 *
 * Results:
 *    Returns the result of mod_install(9F), which is zero on success and a
 *    non-zero value on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
_init(void)
{
   int error;

   error = mod_install(&VMBlockModlinkage);
   if (error) {
      Warning("Could not install vmblock module.\n");
      return error;
   }

   error = BlockInit();
   if (error) {
      Warning("Could not initialize blocking.\n");
      mod_remove(&VMBlockModlinkage);
      return error;
   }

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * _fini --
 *    Invoked when a module is being removed from the kernel.
 *
 * Results:
 *    Returns the result of mod_remove(9F), which is zero on success, and a
 *    non-zero value on failure.
 *
 * Side effects:
 *    The module will be removed from the kernel.
 *
 *----------------------------------------------------------------------------
 */

int
_fini(void)
{
   int error;

   error = mod_remove(&VMBlockModlinkage);
   if (error) {
      Warning("Could not remove vmblock module.\n");
      return error;
   }

   BlockCleanup();
   vfs_freevfsops_by_type(vmblockType);
   vn_freevnodeops(vmblockVnodeOps);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * _info --
 *
 *    Invoked when the modinfo(1M) command is executed.  mod_info(9F) handles
 *    this for us.
 *
 * Results:
 *    Returns mod_info(9F)'s results, which are a non-zero value on success, and
 *    zero on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
_info(struct modinfo *modinfop) // OUT: Filled in with module's information
{
   return mod_info(&VMBlockModlinkage, modinfop);
}
  07070100000278000081A40000000000000000000000016822550500000CBA000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmblock/module.h   /*********************************************************
 * Copyright (C) 2006 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * module.h --
 *
 *      Defnitions for entire vmblock module.
 */

#ifndef __MODULE_H_
#define __MODULE_H_

#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/pathname.h>
#include <sys/cmn_err.h>
#include <sys/modctl.h>
#include <sys/param.h>

#if SOL11
#include <sys/vfs_opreg.h>      /* fs_operation_def_t, ... */
#endif

#include "vm_basic_types.h"

/*
 * Macros
 */
#define VMBLOCK_FS_NAME     "vmblock"
#define VMBLOCK_VFS_FLAGS   0
#define VMBLOCK_VFSSW_FLAGS 0
#define VFSPTOMIP(vfsp)     ((VMBlockMountInfo *)(vfsp)->vfs_data)
#define VPTOMIP(vp)         VFSPTOMIP((vp)->v_vfsp)
#define VPTOVIP(vp)         ((VMBlockVnodeInfo *)(vp)->v_data)

/*
 * Debug logging
 */
#define VMBLOCK_DEBUG CE_WARN
#define VMBLOCK_ERROR CE_WARN
#define VMBLOCK_ENTRY_LOGLEVEL 7
#undef ASSERT
#ifdef VMX86_DEVEL
# define Debug(level, fmt, args...)    level > LOGLEVEL ?                       \
                                          0 :                                   \
                                          cmn_err(VMBLOCK_DEBUG, fmt, ##args)
# define LOG(level, fmt, args...)      Debug(level, fmt, ##args)
# define ASSERT(expr)                  (expr) ?                                 \
                                          0 :                                   \
                                          cmn_err(CE_PANIC, "ASSERT: %s:%d\n",  \
                                                  __FILE__, __LINE__)
#else
# define Debug(level, fmt, args...)
# define LOG(level, fmt, args...)
# define ASSERT(expr)
#endif
#define Warning(fmt, args...)  cmn_err(VMBLOCK_ERROR, fmt, ##args)

#if defined(SOL9)
# define OS_VFS_VERSION    2
#elif defined(SOL10)
# define OS_VFS_VERSION    3
#elif defined(SOL11)
# define OS_VFS_VERSION    5
#else
# error "Unknown Solaris version, can't set OS_VFS_VERSION"
#endif



#if OS_VFS_VERSION <= 3
# define VMBLOCK_VOP(vopName, vopFn, vmblkFn) { vopName, vmblkFn }
#else
# define VMBLOCK_VOP(vopName, vopFn, vmblkFn) { vopName, { .vopFn = vmblkFn } }
#endif

/*
 * Types
 */
typedef struct VMBlockMountInfo {
   struct vnode *root;
   struct vnode *redirectVnode;
   struct pathname redirectPath;
} VMBlockMountInfo;

typedef struct VMBlockVnodeInfo {
   struct vnode *realVnode;
   char name[MAXNAMELEN];
   size_t nameLen;
} VMBlockVnodeInfo;


/*
 * Externs
 */

/* Filesystem initialization routine (see vnops.c) */
EXTERN int VMBlockInit(int, char *);

/* Needed in struct modlfs */
EXTERN struct mod_ops mod_fsops;

EXTERN const fs_operation_def_t vnodeOpsArr[];
EXTERN vnodeops_t *vmblockVnodeOps;
EXTERN int vmblockType;

EXTERN int LOGLEVEL;

#endif /* __MODULE_H_ */
  07070100000279000081A4000000000000000000000001682255050000139D000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmblock/os.h   /*********************************************************
 * Copyright (C) 2007 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * os.h --
 *
 *      OS-specific definitions.
 */


#ifndef __OS_H__
#define __OS_H__

#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/ksynch.h>
#include <sys/atomic.h>
#include <sys/errno.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/varargs.h>
#include <sys/sunddi.h>

#include "vm_basic_types.h"

typedef krwlock_t os_rwlock_t;
typedef kmem_cache_t os_kmem_cache_t;
typedef struct os_completion_t {
   Bool completed;
   kmutex_t mutex;
   kcondvar_t cv;
} os_completion_t;
/*
 * Changing the os_atomic_t type requires that the os_atomic_* macros below be
 * changed as well.
 */
typedef uint_t os_atomic_t;
typedef kthread_t * os_blocker_id_t;

#define OS_UNKNOWN_BLOCKER              NULL
#define OS_ENOMEM                       ENOMEM
#define OS_ENOENT                       ENOENT
#define OS_EEXIST                       EEXIST
#define OS_PATH_MAX                     MAXPATHLEN
#define OS_KMEM_CACHE_FLAG_HWALIGN      0

#define OS_FMTTID                       "lu"
#define os_threadid                     (uintptr_t)curthread

#define os_panic(fmt, args)             vcmn_err(CE_PANIC, fmt, args)

#define os_rwlock_init(lock)            rw_init(lock, NULL, RW_DRIVER, NULL)
#define os_rwlock_destroy(lock)         rw_destroy(lock)
/*
 * rw_lock_held() returns 1 if the lock is read locked, and rw_owner() returns
 * the current lock owner if it's write locked.  In the read locked case, we
 * just have to assume we're one of the readers.
 */
#define os_rwlock_held(lock)            (rw_lock_held(lock) || \
                                         rw_owner(lock) == curthread)
#define os_read_lock(lock)              rw_enter(lock, RW_READER)
#define os_write_lock(lock)             rw_enter(lock, RW_WRITER)
#define os_read_unlock(lock)            rw_exit(lock)
#define os_write_unlock(lock)           rw_exit(lock)

#define os_kmem_cache_create(name, size, align, ctor) \
   kmem_cache_create(name, size, align, ctor, NULL, NULL, NULL, NULL, 0)
#define os_kmem_cache_destroy(cache)    kmem_cache_destroy(cache)
#define os_kmem_cache_alloc(cache)      kmem_cache_alloc(cache, KM_SLEEP)
#define os_kmem_cache_free(cache, elem) kmem_cache_free(cache, elem)

#define os_completion_init(comp)                                \
      do {                                                      \
         (comp)->completed = FALSE;                             \
         mutex_init(&(comp)->mutex, NULL, MUTEX_DRIVER, NULL);  \
         cv_init(&(comp)->cv, NULL, CV_DRIVER, NULL);           \
      } while (0)
#define os_completion_destroy(comp)                             \
      do {                                                      \
         mutex_destroy(&(comp)->mutex);                         \
         cv_destroy(&(comp)->cv);                               \
      } while (0)
/*
 * XXX The following should be made interruptible such as via
 * cv_wait_sig, and returning that function's return value.  In
 * the meantime, fake "success" by evaluating to 0.
 */
#define os_wait_for_completion(comp)                            \
({                                                              \
    mutex_enter(&(comp)->mutex);                                \
    while (!(comp)->completed) {                                \
       cv_wait(&(comp)->cv, &(comp)->mutex);                    \
    }                                                           \
    mutex_exit(&(comp)->mutex);                                 \
    0;                                                          \
})
#define os_complete_all(comp)                                   \
      do {                                                      \
         mutex_enter(&(comp)->mutex);                           \
         (comp)->completed = TRUE;                              \
         mutex_exit(&(comp)->mutex);                            \
         cv_broadcast(&(comp)->cv);                             \
      } while (0)

/* These will need to change if os_atomic_t is changed from uint_t. */
#define os_atomic_dec_and_test(atomic)  (atomic_dec_uint_nv(atomic) == 0)
#define os_atomic_dec(atomic)           atomic_dec_uint(atomic)
#define os_atomic_set(atomic, val)      atomic_swap_uint(atomic, val)
#define os_atomic_inc(atomic)           atomic_inc_uint(atomic)
#define os_atomic_read(atomic)          atomic_add_int_nv(atomic, 0)

#endif /* __OS_H__ */
   0707010000027A000081A40000000000000000000000016822550500004330000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmblock/vfsops.c   /*********************************************************
 * Copyright (C) 2006 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * vfsops.c --
 *
 *      VFS operations for vmblock file system.
 */

#include <sys/types.h>
#include <sys/kmem.h>      /* kmem_zalloc() */
#include <sys/errno.h>     /* error codes */
#include <sys/mount.h>     /* MS_OVERLAY */
#include <sys/sysmacros.h> /* makedevice macro */
#include <sys/systm.h>     /* cmpldev() */
#include <sys/policy.h>    /* secpolicy_fs_mount() */

#include "module.h"


/*
 * Variables
 */
vfsops_t *vmblockVfsOps;

static major_t vmblockMajor;
static minor_t vmblockMinor;
static kmutex_t vmblockMutex;


/*
 * Prototypes
 */
int VMBlockVnodeGet(struct vnode **vpp, struct vnode *realVp,
                    const char *name, size_t nameLen,
                    struct vnode *dvp, struct vfs *vfsp, Bool isRoot);
int VMBlockVnodePut(struct vnode *vp);
static int VMBlockMount(struct vfs *vfsp, struct vnode *vnodep,
                        struct mounta *mntp, struct cred *credp);
static int VMBlockUnmount(struct vfs *vfsp, int mflag, struct cred *credp);
static int VMBlockRoot(struct vfs *vfsp, struct vnode **vnodepp);
static int VMBlockStatvfs(struct vfs *vfsp, struct statvfs64 *stats);



/*
 *----------------------------------------------------------------------------
 *
 * VMBlockVnodeGet --
 *
 *    Creates a vnode.
 *
 *    Note that realVp is assumed to be held (see the comment in the function
 *    for further explanation).
 *
 * Results:
 *    Returns zero on success and a non-zero error code on failure.  On
 *    success, vpp is filled in with a new, held vnode.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
VMBlockVnodeGet(struct vnode **vpp,        // OUT: Filled with address of new vnode
                struct vnode *realVp,      // IN:  Real vnode (assumed held)
                const char *name,          // IN:  Relative name of the file
                size_t nameLen,            // IN:  Size of name
                struct vnode *dvp,         // IN:  Parent directory's vnode
                struct vfs *vfsp,          // IN:  Filesystem structure
                Bool isRoot)               // IN:  If is root directory of fs
{
   VMBlockVnodeInfo *vip;
   struct vnode *vp;
   char *curr;
   int ret;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockVnodeGet: entry\n");

   ASSERT(vpp);
   ASSERT(realVp);
   ASSERT(vfsp);
   ASSERT(name);
   ASSERT(dvp || isRoot);

   vp = vn_alloc(KM_SLEEP);
   if (!vp) {
      return ENOMEM;
   }

   vip = kmem_zalloc(sizeof *vip, KM_SLEEP);
   vp->v_data = (void *)vip;

   /*
    * Store the path that this file redirects to.  For the root vnode we just
    * store the provided path, but for all others we first copy in the parent
    * directory's path.
    */
   curr = vip->name;

   if (!isRoot) {
      VMBlockVnodeInfo *dvip = VPTOVIP(dvp);
      if (dvip->nameLen + 1 + nameLen + 1 >= sizeof vip->name) {
         ret = ENAMETOOLONG;
         goto error;
      }

      memcpy(vip->name, dvip->name, dvip->nameLen);
      vip->name[dvip->nameLen] = '/';
      curr = vip->name + dvip->nameLen + 1;
   }

   if (nameLen + 1 > (sizeof vip->name - (curr - vip->name))) {
      ret = ENAMETOOLONG;
      goto error;
   }

   memcpy(curr, name, nameLen);
   curr[nameLen] = '\0';
   vip->nameLen = nameLen + (curr - vip->name);

   /*
    * We require the caller to have held realVp so we don't need VN_HOLD() it
    * here here even though we VN_RELE() this vnode in VMBlockVnodePut().
    * Despite seeming awkward, this is more natural since the function that our
    * caller obtained realVp from provided a held vnode.
    */
   vip->realVnode = realVp;

   /*
    * Now we'll initialize the vnode.  We need to set the file type, vnode
    * operations, flags, filesystem pointer, reference count, and device.
    */
   /* The root directory is our only directory; the rest are symlinks. */
   vp->v_type = isRoot ? VDIR : VLNK;

   vn_setops(vp, vmblockVnodeOps);

   vp->v_flag  = VNOMAP | VNOMOUNT | VNOSWAP | isRoot ? VROOT : 0;
   vp->v_vfsp  = vfsp;
   vp->v_rdev  = NODEV;

   /* Fill in the provided address with the new vnode. */
   *vpp = vp;

   return 0;

error:
   kmem_free(vip, sizeof *vip);
   vn_free(vp);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockVnodePut --
 *
 *    Frees state associated with provided vnode.
 *
 * Results:
 *    Zero on success, non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
VMBlockVnodePut(struct vnode *vp)
{
   VMBlockVnodeInfo *vip;
   struct vnode *realVnode;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockVnodePut: entry (%p)\n", vp);

   mutex_enter(&vp->v_lock);
   if (vp->v_count > 1) {
      vp->v_count--;
      mutex_exit(&vp->v_lock);
      return 0;
   }
   mutex_exit(&vp->v_lock);

   vip = (VMBlockVnodeInfo *)vp->v_data;
   realVnode = vip->realVnode;

   kmem_free(vip, sizeof *vip);
   vn_free(vp);
   /*
    * VMBlockVnodeGet() doesn't VN_HOLD() the real vnode, but all callers of it
    * will have the vnode held, so we need to VN_RELE() here.
    */
   VN_RELE(realVnode);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockInit --
 *
 *    This is the file system initialization routine.  It creates an array of
 *    fs_operation_def_t for all the vfs operations, then calls vfs_makefsops()
 *    and vfs_setfsops() to assign them to the file system properly.
 *
 * Results:
 *    Returns zero on success and a non-zero error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
VMBlockInit(int fstype,    // IN: file system type
            char *name)    // IN: Name of the file system
{
   int ret;
   static const fs_operation_def_t vfsOpsArr[] = {
      VMBLOCK_VOP(VFSNAME_MOUNT, vfs_mount, VMBlockMount),
      VMBLOCK_VOP(VFSNAME_UNMOUNT, vfs_unmount, VMBlockUnmount),
      VMBLOCK_VOP(VFSNAME_ROOT, vfs_root, VMBlockRoot),
      VMBLOCK_VOP(VFSNAME_STATVFS, vfs_statvfs, VMBlockStatvfs),
      { NULL }
   };

   if (!name) {
      Warning("VMBlockInit: received NULL input from kernel.\n");
      return EINVAL;
   }

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockInit: fstype=%d, name=\"%s\"\n", fstype, name);

   /*
    * Set our file system type and the vfs operations in the kernel's VFS
    * switch table.
    */
   vmblockType = fstype;

   ret = vfs_setfsops(vmblockType, vfsOpsArr, &vmblockVfsOps);
   if (ret) {
      Warning("VMBlockInit: could not set vfs operations.\n");
      return ret;
   }

   ret = vn_make_ops(name, vnodeOpsArr, &vmblockVnodeOps);
   if (ret) {
      Warning("VMBlockInit: could not create vnode operations.\n");
      /*
       * It's important not to call vfs_freevfsops() here; that's only for
       * freeing ops created with vfs_makefsops().
       */
      vfs_freevfsops_by_type(vmblockType);
      return ret;
   }

   /*
    * We need to find a unique device number for this instance of the module;
    * it will be used at each mount to secure a unique device number and file
    * system identifier.  If one cannot be located, we'll just use zero like
    * other Solaris file systems.
    */
   if ((vmblockMajor = getudev()) == (major_t)-1) {
        Warning("VMBlockInit: could not obtain unique device.\n");
        vmblockMajor = 0;
   }
   vmblockMinor = 0;
   mutex_init(&vmblockMutex, NULL, MUTEX_DEFAULT, NULL);

   return 0;
}


/*
 * VFS Entry Points
 */

/*
 *----------------------------------------------------------------------------
 *
 * VMBlockMount --
 *
 *   This function is invoked when mount(2) is called on our file system.
 *   The file system is mounted on the supplied vnode.
 *
 * Results:
 *   Returns zero on success and an appropriate error code on error.
 *
 * Side effects:
 *   The file system is mounted on top of vnodep.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockMount(struct vfs *vfsp,     // IN: file system to mount
             struct vnode *vnodep, // IN: Vnode that we are mounting on
             struct mounta *mntp,  // IN: Arguments to mount(2) from user
             struct cred *credp)   // IN: Credentials of caller
{
   VMBlockMountInfo *mip;
   int ret;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockMount: entry\n");

   /*
    * These next few checks are done by all other Solaris file systems, so
    * let's follow their lead.
    */
   ret = secpolicy_fs_mount(credp, vnodep, vfsp);
   if (ret) {
      Warning("VMBlockMount: mounting security check failed.\n");
      return ret;
   }

   if (vnodep->v_type != VDIR) {
      Warning("VMBlockMount: not mounting on a directory.\n");
      return ENOTDIR;
   }

   mutex_enter(&vnodep->v_lock);
   if ((mntp->flags & MS_OVERLAY) == 0 &&
       (vnodep->v_count != 1 || (vnodep->v_flag & VROOT))) {
      mutex_exit(&vnodep->v_lock);
      Warning("VMBlockMount: cannot allow unrequested overlay mount.\n");
      return EBUSY;
   }
   mutex_exit(&vnodep->v_lock);

   /*
    * The directory we are redirecting to is specified as the special file
    * since we have no actual device to mount on.  We store that path in the
    * mount information structure (note that there's another allocation inside
    * pn_get() so we must pn_free() that path at unmount time). KM_SLEEP
    * guarantees our memory allocation will succeed (pn_get() uses this flag
    * too).
    */
   mip = kmem_zalloc(sizeof *mip, KM_SLEEP);
   ret = pn_get(mntp->spec,
                (mntp->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE,
                &mip->redirectPath);
   if (ret) {
      Warning("VMBlockMount: could not obtain redirecting directory.\n");
      kmem_free(mip, sizeof *mip);
      return ret;
   }

   /* Do a lookup on the specified path. */
   ret = lookupname(mntp->spec,
                    (mntp->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE,
                    FOLLOW,
                    NULLVPP,
                    &mip->redirectVnode);
   if (ret) {
      Warning("VMBlockMount: could not obtain redirecting directory.\n");
      goto error_lookup;
   }

   if (mip->redirectVnode->v_type != VDIR) {
      Warning("VMBlockMount: not redirecting to a directory.\n");
      ret = ENOTDIR;
      goto error;
   }

   /*
    * Initialize our vfs structure.
    */
   vfsp->vfs_vnodecovered = vnodep;
   vfsp->vfs_flag &= ~VFS_UNMOUNTED;
   vfsp->vfs_flag |= VMBLOCK_VFS_FLAGS;
   vfsp->vfs_bsize = PAGESIZE;
   vfsp->vfs_fstype = vmblockType;
   vfsp->vfs_bcount = 0;
   /* If we had mount options, we'd call vfs_setmntopt with vfsp->vfs_mntopts */

   /* Locate a unique device minor number for this mount. */
   mutex_enter(&vmblockMutex);
   do {
      vfsp->vfs_dev = makedevice(vmblockMajor, vmblockMinor);
      vmblockMinor = (vmblockMinor + 1) & L_MAXMIN32;
   } while (vfs_devismounted(vfsp->vfs_dev));
   mutex_exit(&vmblockMutex);

   vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, vmblockType);
   vfsp->vfs_data = (caddr_t)mip;

   /*
    * Now create the root vnode of the file system.
    */
   ret = VMBlockVnodeGet(&mip->root, mip->redirectVnode,
                         mip->redirectPath.pn_path,
                         mip->redirectPath.pn_pathlen,
                         NULL, vfsp, TRUE);
   if (ret) {
      Warning("VMBlockMount: couldn't create root vnode.\n");
      ret = EFAULT;
      goto error;
   }

   VN_HOLD(vfsp->vfs_vnodecovered);
   return 0;

error:
   /* lookupname() provides a held vnode. */
   VN_RELE(mip->redirectVnode);
error_lookup:
   pn_free(&mip->redirectPath);
   kmem_free(mip, sizeof *mip);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockUnmount --
 *
 *    This function is invoked when umount(2) is called on our file system.
 *
 * Results:
 *    Returns zero on success and an error code on error.
 *
 * Side effects:
 *    The root vnode will be freed.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockUnmount(struct vfs *vfsp,   // IN: This file system
               int flag,           // IN: Unmount flags
               struct cred *credp) // IN: Credentials of caller
{
   VMBlockMountInfo *mip;
   int ret;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockUnmount: entry\n");

   ret = secpolicy_fs_unmount(credp, vfsp);
   if (ret) {
      return ret;
   }

   mip = (VMBlockMountInfo *)vfsp->vfs_data;

   mutex_enter(&mip->root->v_lock);
   if (mip->root->v_count > 1) {
      mutex_exit(&mip->root->v_lock);
      return EBUSY;
   }
   mutex_exit(&mip->root->v_lock);

   VN_RELE(vfsp->vfs_vnodecovered);
   /*
    * We don't need to VN_RELE() mip->redirectVnode since it's the realVnode
    * for mip->root.  That means when we VN_RELE() mip->root and
    * VMBlockInactive() is called, VMBlockVnodePut() will VN_RELE()
    * mip->redirectVnode for us.  It's like magic, but better.
    */
   VN_RELE(mip->root);

   pn_free(&mip->redirectPath);
   kmem_free(mip, sizeof *mip);

   vfsp->vfs_flag |= VFS_UNMOUNTED;

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockRoot --
 *
 *    This supplies the root vnode for the file system.
 *
 * Results:
 *    Returns zero on success and an error code on error.  On success vnodepp
 *    is set to the pointer of the root vnode.
 *
 * Side effects:
 *    The root vnode's reference count is incremented by one.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockRoot(struct vfs *vfsp,       // IN: file system to find root vnode of
            struct vnode **vnodepp) // OUT: Set to pointer to root vnode of this fs
{
   VMBlockMountInfo *mip;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockRoot: entry\n");

   mip = (VMBlockMountInfo *)vfsp->vfs_data;

   VN_HOLD(mip->root);
   *vnodepp = mip->root;

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockStatvfs --
 *
 *    Provides statistics for the provided file system.  The values provided
 *    by this function are fake.
 *
 * Results:
 *    Returns zero on success and a non-zero error code on exit.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
VMBlockStatvfs(struct vfs *vfsp,         // IN: file system to get statistics for
               struct statvfs64 *stats)  // OUT: Statistics are placed into this struct
{
   dev32_t dev32;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockStatvfs: entry\n");

   /* Clear stats struct, then fill it in with our values. */
   memset(stats, 0, sizeof *stats);

   /*
    * Macros in case we need these elsewhere later.
    *
    * Since vmblock does not provide any actual storage, we use zero so that
    * the output of df(1) is pleasant for users.
    */
   #define VMBLOCK_BLOCKSIZE       PAGESIZE
   #define VMBLOCK_BLOCKS_TOTAL    0
   #define VMBLOCK_BLOCKS_FREE     0
   #define VMBLOCK_BLOCKS_AVAIL    0
   #define VMBLOCK_FILES_TOTAL     0
   #define VMBLOCK_FILES_FREE      0
   #define VMBLOCK_FILES_AVAIL     0

   /* Compress the device number to 32-bits for consistency on 64-bit systems. */
   cmpldev(&dev32, vfsp->vfs_dev);

   stats->f_bsize   = VMBLOCK_BLOCKSIZE;        /* Preferred fs block size */
   stats->f_frsize  = VMBLOCK_BLOCKSIZE;        /* Fundamental fs block size */
   /* Next six are u_longlong_t */
   stats->f_blocks  = VMBLOCK_BLOCKS_TOTAL;     /* Total blocks on fs */
   stats->f_bfree   = VMBLOCK_BLOCKS_FREE;      /* Total free blocks */
   stats->f_bavail  = VMBLOCK_BLOCKS_AVAIL;     /* Total blocks avail to non-root */
   stats->f_files   = VMBLOCK_FILES_TOTAL;      /* Total files (inodes) */
   stats->f_ffree   = VMBLOCK_FILES_FREE;       /* Total files free */
   stats->f_favail  = VMBLOCK_FILES_AVAIL;      /* Total files avail to non-root */
   stats->f_fsid    = dev32;                    /* file system id */
   stats->f_flag   &= ST_NOSUID;                /* Flags: we don't support setuid. */
   stats->f_namemax = MAXNAMELEN;               /* Max filename; use Solaris default. */

   /* Memset above and -1 of array size as n below ensure NUL termination. */
   strncpy(stats->f_basetype, VMBLOCK_FS_NAME, sizeof stats->f_basetype - 1);
   strncpy(stats->f_fstr, VMBLOCK_FS_NAME, sizeof stats->f_fstr - 1);

   return 0;
}
0707010000027B000081A40000000000000000000000016822550500003DD7000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmblock/vnops.c    /*********************************************************
 * Copyright (C) 2006 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * vnops.c --
 *
 *      vnode operations for vmblock file system.
 *
 */

#include <sys/types.h>
#include <sys/dirent.h>
#include <sys/sysmacros.h>
#include <sys/file.h>           /* FREAD, FWRITE, etc flags */
#include <sys/fs_subr.h>        /* fs_fab_acl */
#include <sys/stat.h>           /* S_IRWXU and friends */
#include <sys/dnlc.h>           /* Directory name lookup cache */
#include <sys/signal.h>         /* k_sigset_t and signal macros */
#include <sys/proc.h>           /* practive (the active process) */
#include <sys/user.h>           /* u macro for current user area */
#include <sys/thread.h>         /* curthread and curproc macros */
#include <vm/seg_vn.h>          /* segment vnode mapping for mmap() */
#include <sys/vmsystm.h>        /* VM address mapping functions */
#include <sys/systm.h>          /* strlen() */

#include "vmblock.h"
#include "module.h"
#include "block.h"


extern int VMBlockVnodePut(struct vnode *vp);
extern int VMBlockVnodeGet(struct vnode **vpp, struct vnode *realVp,
                           const char *name, size_t nameLen,
                           struct vnode *dvp, struct vfs *vfsp, Bool isRoot);

/*
 * Vnode Entry Points
 */

/*
 *----------------------------------------------------------------------------
 *
 * VMBlockOpen --
 *
 *    Invoked when open(2) is called on a file in our filesystem.
 *
 *    "Opens a file referenced by the supplied vnode.  The open() system call
 *    has already done a vop_lookup() on the path name, which returned a vnode
 *    pointer and then calls to vop_open().  This function typically does very
 *    little since most of the real work was performed by vop_lookup()."
 *    (Solaris Internals, p537)
 *
 * Results:
 *    Zero on success, error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockOpen(struct vnode **vpp,    // IN: Vnode for file to open
            int flag,              // IN: Open flags
            struct cred *cr        // IN: Credentials of caller
#if OS_VFS_VERSION >= 5
          , caller_context_t *ctx  // IN: Caller's context
#endif
           )
{
   VMBlockMountInfo *mip;
   Bool isRoot = TRUE;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockOpen: entry\n");

   /*
    * The opened vnode is held for us, so we don't need to do anything here
    * except make sure only root opens the mount point.
    */
   mip = VPTOMIP(*vpp);
   if (mip->root == *vpp) {
      isRoot = crgetuid(cr) == 0;
   }

   return isRoot ? 0 : EACCES;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockClose --
 *
 *    Invoked when a user calls close(2) on a file in our filesystem.
 *
 *    "Closes the file given by the supplied vnode.  When this is the last
 *    close, some filesystems use vop_close() to initiate a writeback of
 *    outstanding dirty pages by checking the reference cound in the vnode."
 *    (Solaris Internals, p536)
 *
 * Results:
 *    Zero on success, error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockClose(struct vnode *vp,     // IN: Vnode of file that is being closed
             int flag,             // IN: Flags file was opened with
             int count,            // IN: Reference count on this vnode
             offset_t offset,      // IN: File offset
             struct cred *cr       // IN :Credentials of caller
#if OS_VFS_VERSION >= 5
           , caller_context_t *ctx // IN: Caller's context
#endif
            )
{
   VMBlockMountInfo *mip;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockClose: entry\n");

   /*
    * If someone is closing the root of our file system (the mount point), then
    * we need to remove all blocks that were added by this thread.  Note that
    * Solaris calls close with counts greater than one, but we only want to
    * actually close the file when the count reaches one.
    */
   mip = VPTOMIP(vp);
   if (count == 1 && vp == mip->root) {
      BlockRemoveAllBlocks(curthread);
   }

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockIoctl --
 *
 *    Invoked when a user calls ioctl(2) on a file in our filesystem.
 *    Performs a specified operation on the file.
 *
 * Results:
 *    Zero on success, error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockIoctl(struct vnode *vp,     // IN:  Vnode of file to operate on
             int cmd,              // IN:  Requested command from user
             intptr_t arg,         // IN:  Arguments for command
             int flag,             // IN:  File pointer flags and data model
             struct cred *cr,      // IN:  Credentials of caller
             int *rvalp            // OUT: Return value on success
#if OS_VFS_VERSION >= 5
           , caller_context_t *ctx // IN: Caller's context
#endif
            )
{
   VMBlockMountInfo *mip;
   int ret;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockIoctl: entry\n");

   mip = VPTOMIP(vp);
   if (vp != mip->root) {
      return ENOTSUP;
   }

   if (rvalp) {
      *rvalp = 0;
   }

   switch (cmd) {
   case VMBLOCK_ADD_FILEBLOCK:
   case VMBLOCK_DEL_FILEBLOCK:
   {
      struct pathname pn;

      ret = pn_get((char *)arg, UIO_USERSPACE, &pn);
      if (ret) {
         goto out;
      }

      /* Remove all trailing path separators. */
      while (pn.pn_pathlen > 0 && pn.pn_path[pn.pn_pathlen - 1] == '/') {
         pn.pn_path[pn.pn_pathlen - 1] = '\0';
         pn.pn_pathlen--;
      }

      ret = cmd == VMBLOCK_ADD_FILEBLOCK ?
               BlockAddFileBlock(pn.pn_path, curthread) :
               BlockRemoveFileBlock(pn.pn_path, curthread);
      pn_free(&pn);
      break;
   }
#ifdef VMX86_DEVEL
   case VMBLOCK_LIST_FILEBLOCKS:
      BlockListFileBlocks();
      ret = 0;
      break;
#endif
   default:
      Warning("VMBlockIoctl: unknown command (%d) received.\n", cmd);
      return ENOTSUP;
   }

out:
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockGetattr --
 *
 *    "Gets the attributes for the supplied vnode." (Solaris Internals, p536)
 *
 * Results:
 *    Zero on success, error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockGetattr(struct vnode *vp,       // IN:  Vnode of file to get attributes for
               struct vattr *vap,      // OUT: Filled in with attributes of file
               int flags,              // IN:  Getattr flags (see ATTR_ in vnode.h)
               struct cred *cr         // IN:  Credentials of caller
#if OS_VFS_VERSION >= 5
             , caller_context_t *ctx   // IN: Caller's context
#endif
              )
{
   VMBlockMountInfo *mip;
   VMBlockVnodeInfo *vip;
   int ret;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockGetattr: entry\n");

   mip = VPTOMIP(vp);
   vip = VPTOVIP(vp);

   ASSERT(mip);
   ASSERT(vip);

   ret = VOP_GETATTR(vip->realVnode, vap, flags, cr
#if OS_VFS_VERSION >= 5
                     , ctx
#endif
                    );
   if (ret) {
      return ret;
   }

   if (vp == mip->root) {
      vap->va_type = VDIR;
   } else {
      vap->va_type = VLNK;
   }

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockAccess --
 *
 *    This function is invoked when the user calls access(2) on a file in our
 *    filesystem.  It checks to ensure the user has the specified type of
 *    access to the file.
 *
 * Results:
 *    Zero if access is allowed, error code otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockAccess(struct vnode *vp,     // IN: Vnode of file to check access for
              int mode,             // IN: Mode of access
              int flags,            // IN: Flags
              struct cred *cr       // IN: Credentials of caller
#if OS_VFS_VERSION >= 5
            , caller_context_t *ctx // IN: Caller's context
#endif
              )
{
   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockAccess: entry\n");

   /* Success */
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockLookup --
 *
 *    Looks in the provided directory for the specified filename.  Only
 *    succeeds and creates a vmblock vnode if nm exists in the redirect path.
 *
 *    "Looks up the path name for the supplied vnode.  The vop_lookup() does
 *    file-name translation for the open, stat system calls." (Solaris
 *    Internals, p537)
 *
 * Results:
 *   Returns zero on success and ENOENT if the file cannot be found
 *   If file is found, a vnode representing the file is returned in vpp.
 *
 * Side effects:
 *   None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockLookup(struct vnode *dvp,   // IN:  Directory to look in
              char *nm,            // IN:  Name of component to lookup in directory
              struct vnode **vpp,  // OUT: Pointer to vnode representing found file
              struct pathname *pnp,// IN:  Full pathname being looked up
              int flags,           // IN:  Lookup flags (see vnode.h)
              struct vnode *rdir,  // IN:  Vnode of root device
              struct cred *cr          // IN:  Credentials of caller
#if OS_VFS_VERSION >= 5
           ,  caller_context_t *ctx    // IN: Caller's context
           ,  int *direntflags         // IN:
           ,  struct pathname *rpnp    // IN:
#endif
            )
{
   struct vnode *realVp;
   VMBlockMountInfo *mip;
   int ret;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMblockLookup: entry\n");

   /* First ensure that we are looking in a directory. */
   if (dvp->v_type != VDIR) {
      return ENOTDIR;
   }

   /* Don't invoke lookup for ourself. */
   if (nm[0] == '\0' || (nm[0] == '.' && nm[1] == '\0')) {
      VN_HOLD(dvp);
      *vpp = dvp;
      return 0;
   }

   *vpp = NULL;

   /* Make sure nm exists before creating our link to it. */
   mip = VPTOMIP(dvp);
   ret = VOP_LOOKUP(mip->redirectVnode, nm, &realVp, pnp, flags, rdir, cr
#if OS_VFS_VERSION >= 5
                    , ctx, direntflags, rpnp
#endif
                   );
   if (ret) {
      return ret;
   }

   ret = VMBlockVnodeGet(vpp, realVp, nm, strlen(nm), dvp, dvp->v_vfsp, FALSE);
   if (ret) {
      VN_RELE(realVp);
      return ret;
   }

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockReaddir --
 *
 *    Reads as many entries from the directory as will fit in to the provided
 *    buffer.
 *
 *    "The vop_readdir() method reads chunks of the directory into a uio
 *    structure.  Each chunk can contain as many entries as will fit within
 *    the size supplied by the uio structure.  The uio_resid structure member
 *    shows the size of the getdents request in bytes, which is divided by the
 *    size of the directory entry made by the vop_readdir() method to
 *    calculate how many directory entries to return." (Solaris Internals,
 *    p555)
 *
 * Results:
 *    Zero on success, error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockReaddir(struct vnode *vp,       // IN: Vnode of directory to read
               struct uio *uiop,       // IN: User's read request
               struct cred *cr,        // IN: Credentials of caller
               int *eofp               // OUT: Indicates we are done
#if OS_VFS_VERSION >= 5
             , caller_context_t *ctx   // IN: Caller's context
             , int flags               // IN: flags
#endif
               )
{
   VMBlockMountInfo *mip;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockReaddir: entry\n");

   mip = (VMBlockMountInfo *)vp->v_vfsp->vfs_data;
   return VOP_READDIR(mip->redirectVnode, uiop, cr, eofp
#if OS_VFS_VERSION >= 5
                     , ctx, flags
#endif
                     );
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockReadlink --
 *
 *    "Follows the symlink in the supplied vnode." (Solaris Internals, p537)
 *
 * Results:
 *    Zero on success, error code on failure.
 *
 * Side effects:
 *    Blocks if a block has been placed on this file.
 *
 *----------------------------------------------------------------------------
 */

static int
VMBlockReadlink(struct vnode *vp,      // IN: Vnode for the symlink
                struct uio *uiop,      // IN: IO request structure
                struct cred *cr        // IN: Credentials of caller
#if OS_VFS_VERSION >= 5
              , caller_context_t *ctx  // IN: Caller's context
#endif
               )
{
   VMBlockMountInfo *mip;
   VMBlockVnodeInfo *vip;
   int ret;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockReadlink: entry\n");

   mip = VPTOMIP(vp);
   vip = VPTOVIP(vp);

   if (vip->nameLen + 1 >= uiop->uio_resid) {
      Warning("VMBlockReadlink: name is too long for provided buffer\n");
      return ENAMETOOLONG;
   }

   BlockWaitOnFile(vip->name, NULL);

   /* Copy path to user space. */
   ASSERT(vip->name[vip->nameLen] == '\0');
   ret = uiomove(vip->name, vip->nameLen + 1, UIO_READ, uiop);
   if (ret) {
      return ret;
   }

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMBlockInactive --
 *
 *    Frees a vnode that is no longer referenced.
 *
 *    "Free resources and releases the supplied vnode.  The file system can
 *    choose to destroy the vnode or put it onto an inactive list, which is
 *    managed by the file system implementation." (Solaris Internals, p536)
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
VMBlockInactive(struct vnode *vp,      // IN: Vnode to operate on
                struct cred *cr        // IN: Credentials of the caller
#if OS_VFS_VERSION >= 5
              , caller_context_t *ctx  // IN: Caller's context
#endif
               )
{
   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockInactive: entry\n");

   VMBlockVnodePut(vp);
}

const fs_operation_def_t vnodeOpsArr[] = {
   VMBLOCK_VOP(VOPNAME_OPEN, vop_open, VMBlockOpen),
   VMBLOCK_VOP(VOPNAME_CLOSE, vop_close, VMBlockClose),
   VMBLOCK_VOP(VOPNAME_IOCTL, vop_ioctl, VMBlockIoctl),
   VMBLOCK_VOP(VOPNAME_GETATTR, vop_getattr, VMBlockGetattr),
   VMBLOCK_VOP(VOPNAME_ACCESS, vop_access, VMBlockAccess),
   VMBLOCK_VOP(VOPNAME_LOOKUP, vop_lookup, VMBlockLookup),
   VMBLOCK_VOP(VOPNAME_READDIR, vop_readdir, VMBlockReaddir),
   VMBLOCK_VOP(VOPNAME_READLINK, vop_readlink, VMBlockReadlink),
#if OS_VFS_VERSION <=3
   VMBLOCK_VOP(VOPNAME_INACTIVE, vop_inactive,
               (fs_generic_func_p)VMBlockInactive),
#else
   VMBLOCK_VOP(VOPNAME_INACTIVE, vop_inactive, VMBlockInactive),
#endif
   { NULL }
};

 0707010000027C000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs 0707010000027D000081A400000000000000000000000168225505000046CA000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/COPYING 
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0

1. Definitions.

    1.1. "Contributor" means each individual or entity that creates
         or contributes to the creation of Modifications.

    1.2. "Contributor Version" means the combination of the Original
         Software, prior Modifications used by a Contributor (if any),
         and the Modifications made by that particular Contributor.

    1.3. "Covered Software" means (a) the Original Software, or (b)
         Modifications, or (c) the combination of files containing
         Original Software with files containing Modifications, in
         each case including portions thereof.

    1.4. "Executable" means the Covered Software in any form other
         than Source Code.

    1.5. "Initial Developer" means the individual or entity that first
         makes Original Software available under this License.

    1.6. "Larger Work" means a work which combines Covered Software or
         portions thereof with code not governed by the terms of this
         License.

    1.7. "License" means this document.

    1.8. "Licensable" means having the right to grant, to the maximum
         extent possible, whether at the time of the initial grant or
         subsequently acquired, any and all of the rights conveyed
         herein.

    1.9. "Modifications" means the Source Code and Executable form of
         any of the following:

        A. Any file that results from an addition to, deletion from or
           modification of the contents of a file containing Original
           Software or previous Modifications;

        B. Any new file that contains any part of the Original
           Software or previous Modifications; or

        C. Any new file that is contributed or otherwise made
           available under the terms of this License.

    1.10. "Original Software" means the Source Code and Executable
          form of computer software code that is originally released
          under this License.

    1.11. "Patent Claims" means any patent claim(s), now owned or
          hereafter acquired, including without limitation, method,
          process, and apparatus claims, in any patent Licensable by
          grantor.

    1.12. "Source Code" means (a) the common form of computer software
          code in which modifications are made and (b) associated
          documentation included in or with such code.

    1.13. "You" (or "Your") means an individual or a legal entity
          exercising rights under, and complying with all of the terms
          of, this License.  For legal entities, "You" includes any
          entity which controls, is controlled by, or is under common
          control with You.  For purposes of this definition,
          "control" means (a) the power, direct or indirect, to cause
          the direction or management of such entity, whether by
          contract or otherwise, or (b) ownership of more than fifty
          percent (50%) of the outstanding shares or beneficial
          ownership of such entity.

2. License Grants.

    2.1. The Initial Developer Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, the Initial
    Developer hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Initial Developer, to use,
            reproduce, modify, display, perform, sublicense and
            distribute the Original Software (or portions thereof),
            with or without Modifications, and/or as part of a Larger
            Work; and

        (b) under Patent Claims infringed by the making, using or
            selling of Original Software, to make, have made, use,
            practice, sell, and offer for sale, and/or otherwise
            dispose of the Original Software (or portions thereof).

        (c) The licenses granted in Sections 2.1(a) and (b) are
            effective on the date Initial Developer first distributes
            or otherwise makes the Original Software available to a
            third party under the terms of this License.

        (d) Notwithstanding Section 2.1(b) above, no patent license is
            granted: (1) for code that You delete from the Original
            Software, or (2) for infringements caused by: (i) the
            modification of the Original Software, or (ii) the
            combination of the Original Software with other software
            or devices.

    2.2. Contributor Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, each
    Contributor hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Contributor to use, reproduce,
            modify, display, perform, sublicense and distribute the
            Modifications created by such Contributor (or portions
            thereof), either on an unmodified basis, with other
            Modifications, as Covered Software and/or as part of a
            Larger Work; and

        (b) under Patent Claims infringed by the making, using, or
            selling of Modifications made by that Contributor either
            alone and/or in combination with its Contributor Version
            (or portions of such combination), to make, use, sell,
            offer for sale, have made, and/or otherwise dispose of:
            (1) Modifications made by that Contributor (or portions
            thereof); and (2) the combination of Modifications made by
            that Contributor with its Contributor Version (or portions
            of such combination).

        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
            effective on the date Contributor first distributes or
            otherwise makes the Modifications available to a third
            party.

        (d) Notwithstanding Section 2.2(b) above, no patent license is
            granted: (1) for any code that Contributor has deleted
            from the Contributor Version; (2) for infringements caused
            by: (i) third party modifications of Contributor Version,
            or (ii) the combination of Modifications made by that
            Contributor with other software (except as part of the
            Contributor Version) or other devices; or (3) under Patent
            Claims infringed by Covered Software in the absence of
            Modifications made by that Contributor.

3. Distribution Obligations.

    3.1. Availability of Source Code.

    Any Covered Software that You distribute or otherwise make
    available in Executable form must also be made available in Source
    Code form and that Source Code form must be distributed only under
    the terms of this License.  You must include a copy of this
    License with every copy of the Source Code form of the Covered
    Software You distribute or otherwise make available.  You must
    inform recipients of any such Covered Software in Executable form
    as to how they can obtain such Covered Software in Source Code
    form in a reasonable manner on or through a medium customarily
    used for software exchange.

    3.2. Modifications.

    The Modifications that You create or to which You contribute are
    governed by the terms of this License.  You represent that You
    believe Your Modifications are Your original creation(s) and/or
    You have sufficient rights to grant the rights conveyed by this
    License.

    3.3. Required Notices.

    You must include a notice in each of Your Modifications that
    identifies You as the Contributor of the Modification.  You may
    not remove or alter any copyright, patent or trademark notices
    contained within the Covered Software, or any notices of licensing
    or any descriptive text giving attribution to any Contributor or
    the Initial Developer.

    3.4. Application of Additional Terms.

    You may not offer or impose any terms on any Covered Software in
    Source Code form that alters or restricts the applicable version
    of this License or the recipients' rights hereunder.  You may
    choose to offer, and to charge a fee for, warranty, support,
    indemnity or liability obligations to one or more recipients of
    Covered Software.  However, you may do so only on Your own behalf,
    and not on behalf of the Initial Developer or any Contributor.
    You must make it absolutely clear that any such warranty, support,
    indemnity or liability obligation is offered by You alone, and You
    hereby agree to indemnify the Initial Developer and every
    Contributor for any liability incurred by the Initial Developer or
    such Contributor as a result of warranty, support, indemnity or
    liability terms You offer.

    3.5. Distribution of Executable Versions.

    You may distribute the Executable form of the Covered Software
    under the terms of this License or under the terms of a license of
    Your choice, which may contain terms different from this License,
    provided that You are in compliance with the terms of this License
    and that the license for the Executable form does not attempt to
    limit or alter the recipient's rights in the Source Code form from
    the rights set forth in this License.  If You distribute the
    Covered Software in Executable form under a different license, You
    must make it absolutely clear that any terms which differ from
    this License are offered by You alone, not by the Initial
    Developer or Contributor.  You hereby agree to indemnify the
    Initial Developer and every Contributor for any liability incurred
    by the Initial Developer or such Contributor as a result of any
    such terms You offer.

    3.6. Larger Works.

    You may create a Larger Work by combining Covered Software with
    other code not governed by the terms of this License and
    distribute the Larger Work as a single product.  In such a case,
    You must make sure the requirements of this License are fulfilled
    for the Covered Software.

4. Versions of the License.

    4.1. New Versions.

    Sun Microsystems, Inc. is the initial license steward and may
    publish revised and/or new versions of this License from time to
    time.  Each version will be given a distinguishing version number.
    Except as provided in Section 4.3, no one other than the license
    steward has the right to modify this License.

    4.2. Effect of New Versions.

    You may always continue to use, distribute or otherwise make the
    Covered Software available under the terms of the version of the
    License under which You originally received the Covered Software.
    If the Initial Developer includes a notice in the Original
    Software prohibiting it from being distributed or otherwise made
    available under any subsequent version of the License, You must
    distribute and make the Covered Software available under the terms
    of the version of the License under which You originally received
    the Covered Software.  Otherwise, You may also choose to use,
    distribute or otherwise make the Covered Software available under
    the terms of any subsequent version of the License published by
    the license steward.

    4.3. Modified Versions.

    When You are an Initial Developer and You want to create a new
    license for Your Original Software, You may create and use a
    modified version of this License if You: (a) rename the license
    and remove any references to the name of the license steward
    (except to note that the license differs from this License); and
    (b) otherwise make it clear that the license contains terms which
    differ from this License.

5. DISCLAIMER OF WARRANTY.

    COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
    BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
    INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
    SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
    PURPOSE OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND
    PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU.  SHOULD ANY
    COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
    INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
    NECESSARY SERVICING, REPAIR OR CORRECTION.  THIS DISCLAIMER OF
    WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.  NO USE OF
    ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
    DISCLAIMER.

6. TERMINATION.

    6.1. This License and the rights granted hereunder will terminate
    automatically if You fail to comply with terms herein and fail to
    cure such breach within 30 days of becoming aware of the breach.
    Provisions which, by their nature, must remain in effect beyond
    the termination of this License shall survive.

    6.2. If You assert a patent infringement claim (excluding
    declaratory judgment actions) against Initial Developer or a
    Contributor (the Initial Developer or Contributor against whom You
    assert such claim is referred to as "Participant") alleging that
    the Participant Software (meaning the Contributor Version where
    the Participant is a Contributor or the Original Software where
    the Participant is the Initial Developer) directly or indirectly
    infringes any patent, then any and all rights granted directly or
    indirectly to You by such Participant, the Initial Developer (if
    the Initial Developer is not the Participant) and all Contributors
    under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
    notice from Participant terminate prospectively and automatically
    at the expiration of such 60 day notice period, unless if within
    such 60 day period You withdraw Your claim with respect to the
    Participant Software against such Participant either unilaterally
    or pursuant to a written agreement with Participant.

    6.3. In the event of termination under Sections 6.1 or 6.2 above,
    all end user licenses that have been validly granted by You or any
    distributor hereunder prior to termination (excluding licenses
    granted to You by any distributor) shall survive termination.

7. LIMITATION OF LIABILITY.

    UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
    (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
    INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
    COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
    LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
    CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
    LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
    STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
    COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
    INFORMED OF THE POSSIBILITY OF SUCH DAMAGES.  THIS LIMITATION OF
    LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
    INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
    APPLICABLE LAW PROHIBITS SUCH LIMITATION.  SOME JURISDICTIONS DO
    NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
    CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
    APPLY TO YOU.

8. U.S. GOVERNMENT END USERS.

    The Covered Software is a "commercial item," as that term is
    defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
    computer software" (as that term is defined at 48
    C.F.R. 252.227-7014(a)(1)) and "commercial computer software
    documentation" as such terms are used in 48 C.F.R. 12.212
    (Sept. 1995).  Consistent with 48 C.F.R. 12.212 and 48
    C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
    U.S. Government End Users acquire Covered Software with only those
    rights set forth herein.  This U.S. Government Rights clause is in
    lieu of, and supersedes, any other FAR, DFAR, or other clause or
    provision that addresses Government rights in computer software
    under this License.

9. MISCELLANEOUS.

    This License represents the complete agreement concerning subject
    matter hereof.  If any provision of this License is held to be
    unenforceable, such provision shall be reformed only to the extent
    necessary to make it enforceable.  This License shall be governed
    by the law of the jurisdiction specified in a notice contained
    within the Original Software (except to the extent applicable law,
    if any, provides otherwise), excluding such jurisdiction's
    conflict-of-law provisions.  Any litigation relating to this
    License shall be subject to the jurisdiction of the courts located
    in the jurisdiction and venue specified in a notice contained
    within the Original Software, with the losing party responsible
    for costs, including, without limitation, court costs and
    reasonable attorneys' fees and expenses.  The application of the
    United Nations Convention on Contracts for the International Sale
    of Goods is expressly excluded.  Any law or regulation which
    provides that the language of a contract shall be construed
    against the drafter shall not apply to this License.  You agree
    that You alone are responsible for compliance with the United
    States export administration regulations (and the export control
    laws and regulation of any other countries) when You use,
    distribute or otherwise make available any Covered Software.

10. RESPONSIBILITY FOR CLAIMS.

    As between Initial Developer and the Contributors, each party is
    responsible for claims and damages arising, directly or
    indirectly, out of its utilization of rights under this License
    and You agree to work with Initial Developer and Contributors to
    distribute such responsibility on an equitable basis.  Nothing
    herein is intended or shall be deemed to constitute any admission
    of liability.

  0707010000027E000081A4000000000000000000000001682255050000125F000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/Makefile    #!/usr/bin/make -f
##########################################################
# Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
#
# The contents of this file are subject to the terms of the Common
# Development and Distribution License (the "License") version 1.0
# and no later version.  You may not use this file except in
# compliance with the License.
#
# You can obtain a copy of the License at
#         http://www.opensource.org/licenses/cddl1.php
#
# See the License for the specific language governing permissions
# and limitations under the License.
#
##########################################################

##
## General build locations and variables
##

MODULE   := vmhgfs
CFLAGS   :=
LDFLAGS  :=

# Solaris version defines for in-code #ifdefs (default to Solaris 9)
ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.11)
CFLAGS   += -DSOL11
else
ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.10)
CFLAGS   += -DSOL10
else
CFLAGS   += -DSOL9
# we don't support Solaris 9 (we could but we use memcpy/memset
# and we'd need stubs to get the module to load into the kernel).
$(error "This makefile is only used for Solaris 10 and 11")
endif
endif

CFLAGS   += -ffreestanding
CFLAGS   += -nodefaultlibs

CFLAGS   += -O2
CFLAGS   += -Wall -Werror
CFLAGS   += $(GLOBAL_DEFS)
CFLAGS   += $(CC_WARNINGS)
CFLAGS   += -DVMX86_TOOLS
CFLAGS   += -I.

ifdef VMX86_DEVEL
CFLAGS   += -DVMX86_DEVEL
endif
ifdef VMX86_DEBUG               # For assertions
CFLAGS   += -DVMX86_DEBUG
endif
ifdef VMX86_LOG                 # For debug logging
CFLAGS   += -DVM_DEBUGGING_ON
endif

##
## Objects needed to build the HGFS kernel module
##
SOLHGFS_KRN_OBJS := debug.o
SOLHGFS_KRN_OBJS += filesystem.o
SOLHGFS_KRN_OBJS += hgfsBdGlue.o
SOLHGFS_KRN_OBJS += hgfsState.o
SOLHGFS_KRN_OBJS += kernelStubsSolaris.o
SOLHGFS_KRN_OBJS += module.o
SOLHGFS_KRN_OBJS += request.o
SOLHGFS_KRN_OBJS += vnode.o

SOLHGFS_32_KRN_OBJS := $(addprefix i386/, $(SOLHGFS_KRN_OBJS))
SOLHGFS_64_KRN_OBJS := $(addprefix amd64/, $(SOLHGFS_KRN_OBJS))

SOLHGFS_LIB_OBJS := backdoor.o
SOLHGFS_LIB_OBJS += cpName.o
SOLHGFS_LIB_OBJS += cpNameLinux.o
SOLHGFS_LIB_OBJS += hgfsBd.o
SOLHGFS_LIB_OBJS += hgfsEscape.o
SOLHGFS_LIB_OBJS += hgfsUtil.o
SOLHGFS_LIB_OBJS += message.o
SOLHGFS_LIB_OBJS += rpcout.o
SOLHGFS_LIB_OBJS += sha1.o

SOLHGFS_32_LIB_OBJS := $(addprefix i386/, $(SOLHGFS_LIB_OBJS))
SOLHGFS_32_LIB_OBJS += i386/backdoorGcc32.o

SOLHGFS_64_LIB_OBJS := $(addprefix amd64/, $(SOLHGFS_LIB_OBJS))
SOLHGFS_64_LIB_OBJS += amd64/backdoorGcc64.o

SOLHGFS_32_OBJS := $(SOLHGFS_32_KRN_OBJS) $(SOLHGFS_32_LIB_OBJS)
SOLHGFS_64_OBJS := $(SOLHGFS_64_KRN_OBJS) $(SOLHGFS_64_LIB_OBJS)

MODULE_32 := i386/$(MODULE)
MODULE_64 := amd64/$(MODULE)

ifdef OVT_SOURCE_DIR
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/include
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/backdoor
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/hgfs

   VPATH  := $(OVT_SOURCE_DIR)/lib/backdoor
   VPATH  := $(VPATH):$(OVT_SOURCE_DIR)/lib/hgfs
   VPATH  := $(VPATH):$(OVT_SOURCE_DIR)/lib/hgfsBd
   VPATH  := $(VPATH):$(OVT_SOURCE_DIR)/lib/message
   VPATH  := $(VPATH):$(OVT_SOURCE_DIR)/lib/misc
   VPATH  := $(VPATH):$(OVT_SOURCE_DIR)/lib/rpcOut
endif

CFLAGS_32  := $(CFLAGS)
CFLAGS_32  += -m32
LDFLAGS_32 := $(LDFLAGS)

CFLAGS_64  := $(CFLAGS)
CFLAGS_64  += -m64
CFLAGS_64  += -mcmodel=kernel
CFLAGS_64  += -mno-red-zone
LDFLAGS_64 := $(LDFLAGS)
ifdef HAVE_GNU_LD
LDFLAGS_64 += -m elf_x86_64
else
LDFLAGS_64 += -64
endif

##
## Building targets
##
.PHONY: all prepare_dirs clean install

all: prepare_dirs $(MODULE_32) $(MODULE_64)

prepare_dirs:
	@echo "Creating build directories"
	mkdir -p i386
	mkdir -p amd64

# Build just the module for 32 bits kernel
$(MODULE_32): $(SOLHGFS_32_OBJS)
	@echo "Linking          $(MODULE_32)"
	$(LD) $(LDFLAGS_32) -r -o $(MODULE_32) $(SOLHGFS_32_OBJS)

$(SOLHGFS_32_KRN_OBJS): i386/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_32) -D_KERNEL -c $< -o $@

$(SOLHGFS_32_LIB_OBJS): i386/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_32) -c $< -o $@

# Now the same for 64 bits kernel
$(MODULE_64): $(SOLHGFS_64_OBJS)
	@echo "Linking          $(MODULE_64)"
	$(LD) $(LDFLAGS_64) -r -o $(MODULE_64) $(SOLHGFS_64_OBJS)

$(SOLHGFS_64_KRN_OBJS): amd64/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_64) -D_KERNEL -c $< -o $@

$(SOLHGFS_64_LIB_OBJS): amd64/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_64) -c $< -o $@

clean:
	@echo "Cleaning directories"
	@rm -rf $(MODULE_32) $(SOLHGFS_32_OBJS) i386
	@rm -rf $(MODULE_64) $(SOLHGFS_32_OBJS) amd64

install:
	@echo "Installing modules"
	cp $(MODULE_32) /kernel/drv/
	chmod 755 /kernel/drv/$(MODULE)
	cp $(MODULE_64) /kernel/drv/amd64
	chmod 755 /kernel/drv/amd64/$(MODULE)
 0707010000027F000081A400000000000000000000000168225505000035BF000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/debug.c /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * debug.c --
 *
 * Routines for debugging Solaris kernel module.
 *
 */


#include "debug.h"
#include "filesystem.h"
#ifndef SOL9
#include <sys/cred_impl.h>
#endif


/*
 * Functions
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintVfssw --
 *
 *    Prints the provided VFS Switch structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

INLINE void
HgfsDebugPrintVfssw(char *str, struct vfssw *vfsswp)
{
   ASSERT(str);
   ASSERT(vfsswp);

   DEBUG(VM_DEBUG_STRUCT, "struct vfssw from %s\n", str);
   DEBUG(VM_DEBUG_STRUCT, " vsw_name    : %s\n",
	 (vfsswp->vsw_name) ? vfsswp->vsw_name : "NULL");
   DEBUG(VM_DEBUG_STRUCT, " vsw_init    : %p\n", vfsswp->vsw_init);
   DEBUG(VM_DEBUG_STRUCT, " vsw_flag    : %x\n", vfsswp->vsw_flag);
#  ifdef SOL9
   DEBUG(VM_DEBUG_STRUCT, " vsw_vfsops  : %x\n", vfsswp->vsw_vfsops);
   DEBUG(VM_DEBUG_STRUCT, " vsw_optproto: %x\n", vfsswp->vsw_optproto);
#  endif
   DEBUG(VM_DEBUG_STRUCT, " vsw_count   : %d\n", vfsswp->vsw_count);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintVfs --
 *
 *    Prints the provided VFS structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

INLINE void
HgfsDebugPrintVfs(char *str, struct vfs *vfsp)
{
   ASSERT(str);
   ASSERT(vfsp);

   DEBUG(VM_DEBUG_STRUCT, "struct vfs from %s\n", str);
   DEBUG(VM_DEBUG_STRUCT, " vfs_next        : %p\n", vfsp->vfs_next);
   DEBUG(VM_DEBUG_STRUCT, " vfs_op          : %p\n", vfsp->vfs_op);
   DEBUG(VM_DEBUG_STRUCT, " vfs_vnodecovered: %p\n", vfsp->vfs_vnodecovered);
   DEBUG(VM_DEBUG_STRUCT, " vfs_flag        : %d\n", vfsp->vfs_flag);
   DEBUG(VM_DEBUG_STRUCT, " vfs_bsize       : %d\n", vfsp->vfs_bsize);
   DEBUG(VM_DEBUG_STRUCT, " vfs_fstype      : %d\n", vfsp->vfs_fstype);
#  ifdef SOL9
   DEBUG(VM_DEBUG_STRUCT, " vfs_fsid        : %d\n", vfsp->vfs_fsid);
#  else
   DEBUG(VM_DEBUG_STRUCT, " vfs_fsid.val[0] : %d\n", vfsp->vfs_fsid.val[0]);
   DEBUG(VM_DEBUG_STRUCT, " vfs_fsid.val[1] : %d\n", vfsp->vfs_fsid.val[1]);
#  endif
   DEBUG(VM_DEBUG_STRUCT, " vfs_vadata      : %p\n", vfsp->vfs_data);
   DEBUG(VM_DEBUG_STRUCT, " vfs_dev         : %lu\n", vfsp->vfs_dev);
   DEBUG(VM_DEBUG_STRUCT, " vfs_bcount      : %lu\n", vfsp->vfs_bcount);
#  ifdef SOL9
   DEBUG(VM_DEBUG_STRUCT, " vfs_nsubmounts  : %d\n", vfsp->vfs_nsubmounts);
#  endif
   DEBUG(VM_DEBUG_STRUCT, " vfs_list        : %p\n", vfsp->vfs_list);
   DEBUG(VM_DEBUG_STRUCT, " vfs_hash        : %p\n", vfsp->vfs_hash);
   DEBUG(VM_DEBUG_STRUCT, " vfs_reflock     : %p\n", &vfsp->vfs_reflock);
   DEBUG(VM_DEBUG_STRUCT, " vfs_count       : %d\n", vfsp->vfs_count);
#  ifdef SOL9
   DEBUG(VM_DEBUG_STRUCT, " vfs_mntopts     : %x\n", vfsp->vfs_mntopts);
   DEBUG(VM_DEBUG_STRUCT, " vfs_resource    : %s\n",
	 (vfsp->vfs_resource) ? vfsp->vfs_resource : "NULL");
#  endif
   DEBUG(VM_DEBUG_STRUCT, " vfs_mtime       : %ld\n", vfsp->vfs_mtime);

}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintVnode --
 *
 *    Prints the provided vnode structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

INLINE void
HgfsDebugPrintVnode(uint32 level, char *str,
                    struct vnode *vnodep, Bool printFileName)
{
   ASSERT(str);
   ASSERT(vnodep);

   DEBUG(level, "struct vnode from %s located at %p\n", str, vnodep);
   DEBUG(level, " v_lock          : %p\n", &vnodep->v_lock);
   DEBUG(level, " v_flag          : %d\n", vnodep->v_flag);
   DEBUG(level, " v_count         : %d\n", vnodep->v_count);
   DEBUG(level, " v_vfsmountedhere: %p\n", vnodep->v_vfsmountedhere);
   DEBUG(level, " v_op            : %p\n", vnodep->v_op);
   DEBUG(level, " v_vfsp          : %p\n", vnodep->v_vfsp);
   DEBUG(level, " v_stream        : %p\n", vnodep->v_stream);
   DEBUG(level, " v_pages         : %p\n", vnodep->v_pages);
#  ifdef SOL9
   DEBUG(level, " v_next          : %p\n", vnodep->v_next);
   DEBUG(level, " v_prev          : %p\n", vnodep->v_prev);
#  endif
   DEBUG(level, " v_type          : %d\n", vnodep->v_type);
   DEBUG(level, " v_rdev          : %lu\n", vnodep->v_rdev);
   DEBUG(level, " v_data          : %p\n", vnodep->v_data);
   DEBUG(level, " v_filocks       : %p\n", vnodep->v_filocks);
   DEBUG(level, " v_shrlocks      : %p\n", vnodep->v_shrlocks);
   DEBUG(level, " v_cv            : %p\n", &vnodep->v_cv);
   DEBUG(level, " v_locality      : %p\n", vnodep->v_locality);
   DEBUG(level, " v_nbllock       : %p\n", &vnodep->v_nbllock);

   if (printFileName && HGFS_VP_TO_OFP(vnodep) && HGFS_VP_TO_FP(vnodep)) {
      DEBUG(level, " filename        : %s\n", HGFS_VP_TO_FILENAME(vnodep));
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintCred --
 *
 *    Prints the provided cred structure the describes the credentials of the
 *    caller.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

INLINE void
HgfsDebugPrintCred(char *str, struct cred *credp)
{
   DEBUG(VM_DEBUG_STRUCT, "struct cred from %s\n", str);
   DEBUG(VM_DEBUG_STRUCT, " cr_ref    : %d\n", credp->cr_ref);
   DEBUG(VM_DEBUG_STRUCT, " cr_uid    : %d\n", credp->cr_uid);
   DEBUG(VM_DEBUG_STRUCT, " cr_gid    : %d\n", credp->cr_gid);
   DEBUG(VM_DEBUG_STRUCT, " cr_ruid   : %d\n", credp->cr_ruid);
   DEBUG(VM_DEBUG_STRUCT, " cr_rgid   : %d\n", credp->cr_rgid);
   DEBUG(VM_DEBUG_STRUCT, " cr_suid   : %d\n", credp->cr_suid);
   DEBUG(VM_DEBUG_STRUCT, " cr_sgid   : %d\n", credp->cr_sgid);
   DEBUG(VM_DEBUG_STRUCT, " cr_ngroups: %d\n", credp->cr_ngroups);
   DEBUG(VM_DEBUG_STRUCT, " cr_groups : %p\n", credp->cr_groups);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugMounta --
 *
 *    Prints the provided mounta structure that describes the arguments
 *    provided to users.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

INLINE void
HgfsDebugPrintMounta(char *str, struct mounta *mntp)
{
   ASSERT(str);
   ASSERT(mntp);

   DEBUG(VM_DEBUG_STRUCT, "struct mounta from %s\n", str);
   DEBUG(VM_DEBUG_STRUCT, " spec    : %s\n",
	 (mntp->spec) ? mntp->spec : "NULL");
   DEBUG(VM_DEBUG_STRUCT, " dir     : %s\n",
	 (mntp->dir) ? mntp->dir : "NULL");
   DEBUG(VM_DEBUG_STRUCT, " flags   : %x\n", mntp->flags);
   DEBUG(VM_DEBUG_STRUCT, " fstype  : %s\n",
	 (mntp->fstype) ? mntp->fstype : "NULL");
   DEBUG(VM_DEBUG_STRUCT, " dataptr : %p\n", mntp->dataptr);
   DEBUG(VM_DEBUG_STRUCT, " datalen : %d\n", mntp->datalen);
   DEBUG(VM_DEBUG_STRUCT, " optptr  : %p\n", mntp->optptr);
   DEBUG(VM_DEBUG_STRUCT, " optlen  : %d\n", mntp->optlen);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintVattr --
 *
 *    Prints the contents of an attributes structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

INLINE void
HgfsDebugPrintVattr(const struct vattr *vap)
{
   DEBUG(VM_DEBUG_STRUCT, " va_mask: %x\n", vap->va_mask);
   DEBUG(VM_DEBUG_STRUCT, " va_type: %d\n", vap->va_type);
   DEBUG(VM_DEBUG_STRUCT, " va_mode: %x\n", vap->va_mode);
   DEBUG(VM_DEBUG_STRUCT, " va_uid:  %u\n", vap->va_uid);
   DEBUG(VM_DEBUG_STRUCT, " va_gid: %u\n", vap->va_gid);
   DEBUG(VM_DEBUG_STRUCT, " va_fsid: %lu\n", vap->va_fsid);
   DEBUG(VM_DEBUG_STRUCT, " va_nodeid: %llu\n", vap->va_nodeid);
   DEBUG(VM_DEBUG_STRUCT, " va_nlink: %x\n", vap->va_nlink);
   DEBUG(VM_DEBUG_STRUCT, " va_size: %llu\n", vap->va_size);
   DEBUG(VM_DEBUG_STRUCT, " va_atime.tv_sec: %ld\n", vap->va_atime.tv_sec);
   DEBUG(VM_DEBUG_STRUCT, " va_atime.tv_nsec: %ld\n", vap->va_atime.tv_nsec);
   DEBUG(VM_DEBUG_STRUCT, " va_mtime.tv_sec: %ld\n", vap->va_mtime.tv_sec);
   DEBUG(VM_DEBUG_STRUCT, " va_mtime.tv_nsec: %ld\n", vap->va_mtime.tv_nsec);
   DEBUG(VM_DEBUG_STRUCT, " va_ctime.tv_sec: %ld\n", vap->va_ctime.tv_sec);
   DEBUG(VM_DEBUG_STRUCT, " va_ctime.tv_nsec: %ld\n", vap->va_ctime.tv_nsec);
   DEBUG(VM_DEBUG_STRUCT, " va_rdev: %lu\n", vap->va_rdev);
   DEBUG(VM_DEBUG_STRUCT, " va_blksize: %u\n", vap->va_blksize);
   DEBUG(VM_DEBUG_STRUCT, " va_nblocks: %llu\n", vap->va_nblocks);
#ifdef SOL9
   DEBUG(VM_DEBUG_STRUCT, " va_vcode: %u\n", vap->va_vcode);
#else
   DEBUG(VM_DEBUG_STRUCT, " va_seq: %u\n", vap->va_seq);
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintReqList --
 *
 *    For debugging.  Prints out the request list for the provided list
 *    anchor.
 *    Note: Assumes called with the list lock held.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsDebugPrintReqList(DblLnkLst_Links *listAnchor)   // IN: Anchor of list to print
{
   DblLnkLst_Links *currNode;
   HgfsReq *currReq;

   ASSERT(listAnchor);

   DEBUG(VM_DEBUG_STRUCT, "Request List:\n");
   DEBUG(VM_DEBUG_STRUCT, " anchor: %p\n", listAnchor);

   for (currNode = listAnchor->next; currNode != listAnchor; currNode = currNode->next)
   {
      currReq = DblLnkLst_Container(currNode, HgfsReq, listNode);
      DEBUG(VM_DEBUG_STRUCT, " address: %p (id=%d)\n",
            currReq, currReq->id);
   }

   DEBUG(VM_DEBUG_STRUCT, "--DONE--\n");
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintReq --
 *
 *    Prints the relevant portions of the provided HgfsReq structure.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

void
HgfsDebugPrintReq(const char *str,
                  HgfsReq *req)
{
   ASSERT(str);
   ASSERT(req);

   DEBUG(VM_DEBUG_STRUCT, "struct HgfsReq from %s\n", str);
   DEBUG(VM_DEBUG_STRUCT, " id: %d\n", req->id);
   DEBUG(VM_DEBUG_STRUCT, " listNode: %p\n", &req->listNode);
   DEBUG(VM_DEBUG_STRUCT, "  next=%p\n", req->listNode.next);
   DEBUG(VM_DEBUG_STRUCT, "  prev=%p\n", req->listNode.prev);
   DEBUG(VM_DEBUG_STRUCT, " packetSize: %d\n", req->packetSize);
   DEBUG(VM_DEBUG_STRUCT, " state: %d (see hgfsSolaris.h)\n", req->state);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintReqPool --
 *
 *    Prints the contents if the request pool.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

void
HgfsDebugPrintReqPool(const char *str)
{
   int i;

   ASSERT(str);

   DEBUG(VM_DEBUG_STRUCT, "Request pool from %s\n", str);

   for (i = 0; i < ARRAYSIZE(requestPool); i++) {
      DEBUG(VM_DEBUG_STRUCT, " Index: %d, ID: %d\n", i, requestPool[i].id);
      DEBUG(VM_DEBUG_STRUCT, " listNode: %p\n", &requestPool[i].listNode);
      DEBUG(VM_DEBUG_STRUCT, "  next=%p\n", requestPool[i].listNode.next);
      DEBUG(VM_DEBUG_STRUCT, "  prev=%p\n", requestPool[i].listNode.prev);
      DEBUG(VM_DEBUG_STRUCT, " packetSize: %d\n", requestPool[i].packetSize);
      DEBUG(VM_DEBUG_STRUCT, " state: %d (see hgfsSolaris.h)\n", requestPool[i].state);
   }

   DEBUG(VM_DEBUG_STRUCT, "--request pool done--\n");
}


/*
 * There is a problem in Solaris 9's header files when using the va_start
 * and va_end macros, so we manually do what the preprocessor would have
 * done here.
 *
 * Note, the line using __builtin_next_arg is equivalent to:
 * args = ((char *)(&fmt) + sizeof (char *));
 *
 * That is, it just provides a pointer to the unnamed first variable
 * argument.
 */
#ifdef SOL9
# define compat_va_start(arg, fmt) arg = ((char *)__builtin_next_arg(fmt))
# define compat_va_end(arg)
#else
# define compat_va_start(arg, fmt) va_start(arg, fmt)
# define compat_va_end(arg)        va_end(arg)
#endif

static void
vLog(const char *fmt,
     va_list args)
{
#ifdef VM_DEBUG_LEV
   char buffer[1024];

   /*
    * We check this here to avoid unnecessarily manipulating buffer if we
    * aren't even going to print the log.
    */
   if (VM_DEBUG_LOG & VM_DEBUG_LEV) {
      vsprintf(buffer, fmt, args);
      cmn_err(HGFS_DEBUG, "%s", buffer);
   }
#endif
}


/*
 * For compatibility with existing code.
 */

void
Log(const char *fmt, ...)     // IN: format string, etc
{
   va_list args;

   compat_va_start(args, fmt);
   vLog(fmt, args);
   compat_va_end(args);
}


/*
 * For compatibility with existing code.
 */

void
Debug(const char *fmt, ...)   // IN: format string, etc.
{
   va_list args;

   compat_va_start(args, fmt);
   vLog(fmt, args);
   compat_va_end(args);
}


#undef compat_va_list
#undef compat_va_start
#undef compat_va_end
 07070100000280000081A40000000000000000000000016822550500000C5F000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/debug.h /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * debug.h --
 *
 * Macros and includes for debugging.
 *
 */

#ifndef __DEBUG_H_
#define __DEBUG_H_

#ifdef _KERNEL

#include <sys/vnode.h>
#include <sys/vfs.h>

#include "hgfsSolaris.h"
#include "filesystem.h"

#endif

/*
 * Debugging
 */
#define HGFS_DEBUG	        (CE_NOTE)

#define VM_DEBUG_ALWAYS         (1)
#define VM_DEBUG_FAIL	        VM_DEBUG_ALWAYS
#define VM_DEBUG_NOTSUP         VM_DEBUG_ALWAYS
#define VM_DEBUG_ENTRY          (1 << 1)
#define VM_DEBUG_DONE	        (1 << 2)
#define VM_DEBUG_LOAD	        (1 << 3)
#define VM_DEBUG_INFO           (1 << 4)
#define VM_DEBUG_STRUCT         (1 << 5)
#define VM_DEBUG_LIST           (1 << 6)
#define VM_DEBUG_CHPOLL         (1 << 7)
#define VM_DEBUG_RARE           (1 << 8)
#define VM_DEBUG_COMM           (1 << 9)
#define VM_DEBUG_REQUEST        (1 << 10)
#define VM_DEBUG_LOG            (1 << 11)
#define VM_DEBUG_ATTR           (1 << 12)
#define VM_DEBUG_DEVENTRY       (1 << 13)
#define VM_DEBUG_DEVDONE        (1 << 14)
#define VM_DEBUG_SIG            (1 << 15)
#define VM_DEBUG_ERROR          (1 << 16)
#define VM_DEBUG_HSHTBL         (1 << 17)
#define VM_DEBUG_HANDLE         (1 << 18)
#define VM_DEBUG_STATE          (1 << 19)

#ifdef VM_DEBUGGING_ON
/*#define VM_DEBUG_LEV    (VM_DEBUG_ALWAYS | VM_DEBUG_ENTRY | VM_DEBUG_DONE |     \
                         VM_DEBUG_LOAD | VM_DEBUG_COMM |                        \
                         VM_DEBUG_LOG | VM_DEBUG_ATTR)
*/
/*#define VM_DEBUG_LEV    (VM_DEBUG_ALWAYS | VM_DEBUG_FAIL | VM_DEBUG_ERROR |	\
			 VM_DEBUG_COMM | VM_DEBUG_DONE)
*/
#define VM_DEBUG_LEV    (VM_DEBUG_ALWAYS | VM_DEBUG_FAIL)
#endif

#ifdef VM_DEBUG_LEV
#define DEBUG(type, args...)    \
             ((type & VM_DEBUG_LEV) ? (cmn_err(HGFS_DEBUG, args)) : 0)
#else
#define DEBUG(type, args...)
#endif


/*
 * Prototypes
 */

#ifdef _KERNEL

INLINE void HgfsDebugPrintVfssw(char *str, struct vfssw *vfsswp);
INLINE void HgfsDebugPrintVfs(char *str, struct vfs *vfsp);
INLINE void HgfsDebugPrintVnode(uint32 level, char *str,
                                struct vnode *vnodep, Bool printFileName);
INLINE void HgfsDebugPrintCred(char *str, struct cred *credp);
INLINE void HgfsDebugPrintMounta(char *str, struct mounta *mntp);
INLINE void HgfsDebugPrintVattr(const struct vattr *vap);
void HgfsDebugPrintReqList(DblLnkLst_Links *listAnchor);
void HgfsDebugPrintReq(const char *str, HgfsReq *req);
void HgfsDebugPrintReqPool(const char *str);

#endif

void Log(const char *fmt, ...);
void Debug(const char *fmt, ...);


#endif /* __DEBUG_H_ */
 07070100000281000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs    07070100000282000081A40000000000000000000000016822550500002265000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs/BUILD  Solaris Hgfs Build Instructions
===============================

Last Updated: 11/22/2005 (biggadike@vmware.com)

Hgfs Kernel Module
------------------
0. One-time Guest Operations
1. Building with the Linux cross-compiler (Solaris 9)
2. Building from the Guest (Solaris 10)

Tools
-----
0. Building guestd and the toolbox
1. Running guestd
2. Running the toolbox


Hgfs Kernel Module
------------------

 0. One-time Guest Operations
 -------------------------
 Requirements: just a Solaris 9 or 10 guest

 o First, to build you'll need to make sure gmake, gcc, and binutils are
   installed.  The easiest way to install these is to use the pkg-get script
   from http://www.blastwave.org/pkg-get.php.  Follow the instructions there
   then do:

   # pkg-get -U
   # pkg-get -i gmake gcc2 binutils   
   # export PATH=$PATH:/opt/csw/bin     (for Bash)
   # alias make='gmake'                 (for Bash)

 o You'll need to add an entry to the device link table so that when the driver
   is loaded a symlink called /dev/vmware-hgfs is created that points to the
   entry in the Kernel's device tree (/devices).

   # cat docs/append-to-devlink.tab >> /etc/devlink.tab

 o You'll also need a copy of the driver's configuration file where the kernel
   looks for it.

   # cp docs/hgfs.conf /usr/kernel/drv

 o You also need to create the directory where guestd will mount the
   filesystem:

   # mkdir -p /mnt/hgfs

 These only need to be done once.


 1. Building with the Linux cross-compiler (Solaris 9)
 -----------------------------------------------------
 
 Note that the cross-compiler is currently only for Solaris 9.

 o From top of bora-vmsoft:
   
   # make tools-for-solaris

 o Respected build variables:
   VMX86_DEVEL : gets passed on to code
   VMX86_DEBUG : enables assertion checking
   VMX86_LOG   : enables logging
   SOL_VERSION : defaults to 9 because the Solaris 10 cross-compiler doesn't
                 exist yet

 o Installing Targets (run these from the guest):
   rem         : removes the driver from the system
   install     : installs the driver and driver config file to the proper
                 directories
   add         : adds the driver to the system
   rotate      : invokes rem, install, then add.

 Note that most of the time rotate will take care of everything for you, but if
 guestd is running this may cause problems because the driver will be added
 even if the module cannot be removed.  Just "kill" guestd then "make rotate".


 2. Building from the Guest (Solaris 10)
 ---------------------------------------

 Note that this is currently the only way to build in Solaris 10.

 o Make sure the BORA, BUILD, and BUILDROOT variables are set properly in the
   Makefile.guest.

   It's likely all you need know is:
    make -f Makefile.guest module
    make -f Makefile.guest install add

   If you want to disable debugging, use:
    make -f Makefile.guest module DEBUG=OFF

   If you get a "module already loaded" error, try:
    make -f Makefile.guest rotate
 
 o Building targets:
   module      : builds only the Kernel module (current default)
   clean       : cleans build files
   
 o Building variables:
   DEBUG=OFF  : turns off all debugging output from the module (faster!)
   ASSERT=OFF  : turns off ASSERT()s
   SOL_VERSION : defaults to 10 and automatically determines which build number
                 this is with uname -v (52 and 58 are the only builds tested)

 o Installing Targets (run these from the guest):
   rem         : removes the driver from the system
   install     : installs the driver and driver config file to the proper
                 directories
   add         : adds the driver to the system
   rotate      : invokes rem, install, then add.
   once        : appends necessary lines to /etc/devlink.tab (only needed once)
   initbuilddir: creates the build directory
 

Tools
-----

 0. Building guestd and the toolbox
 ------------------------------

 o guestd has been ported to Solaris and is built using the Solaris 9 cross
   compiler mentioned above.  The toolbox has not been ported to Solaris and is
   run using lxrun.  Building both is handled by the Makefiles in bora-vmsoft:

   # cd /path/to/bora-vmsoft
   # make tools-for-solaris

   The binaries will be located at $(BUILD)/obj/ws/tools-for-solaris/guestd/guestd
   and $(BUILD)/obj/ws/tools-for-solaris/toolbox/toolbox.

 1. Running guestd
 -----------------

 o guestd runs natively on Solaris 9.  It will mount Hgfs at /mnt/hgfs and your
   "Shared Folders" will be visible there.

 o I have not been able to use the bora-vmsoft Makefile infrastructure from
   within a Solaris 10 guest, so it is not currently possible to build guestd
   for Solaris 10.  Porting the Makefiles to run inside the guest should not be
   too difficult and a Solaris 10 cross compiler would allow guestd to be built
   for Solaris 10.
   
 2. Running the toolbox
 ----------------------

 o It is first necessary to set up lxrun on the guestd

   Setting up lxrun
   ----------------

   o lxrun enables you to run Linux binaries in Solaris by catching and
     translating all of their system calls.  This works relatively well for
     running guestd and toolbox, but you must install lxrun and build versions of
     the programs that have been "ported" to lxrun.

   o Install lxrun on the guest:
  
     The easiest way is to see if ~biggadike/public_html/lxrun/lxrun-environ.tgz
     exists.  If so, extract this to the root of your guest's filesystem:

     # cd /
     # gunzip /path/to/lxrun-environ.tgz
     # tar xvf /path/to/lxrun-environ.tar

     This will create two directories: /lx and /usr/local/lxrun.  /lx contains
     a linux environment (circa Red Hat 6.2) and the lxrun executable.
     /usr/local/lxrun contains a file called PATHMAP which tells lxrun where to
     get its environment files from (/lx).

     If the precompiled environment doesn't work, you'll need to get lxrun
     0.9.6pre1, compile it, and then use a Red Hat 6.2 Installation CD with the
     INSTALL-RH6 script in the lxrun directory.

     o Try ~biggadike/public_html/lxrun/lxrun-0.9.6pre1.tar.gz or
       http://www.ugcs.caltech.edu/~steven/lxrun/ for the lxrun source
     o Try exit15:/home/ISO-Images/vol1/redhat/RedHat6.2.iso for the Red Hat disc

     **Note that it is not necessary to patch lxrun for the toolbox.  The patch
       in the directory mentioned above is to enable lxrun to run guestd, which
       is no longer necessary since it has been ported.  The lxrun binary in
       the lxrun-environ.tgz file is a patched version, but this does also work
       for running the toolbox.

   o Set necessary environment variables:

     You will also need a few environment variables set.  For bash, add these
     lines to your .bashrc:
   
     export LXRUN_ROOT=/lx
     export PATH=$PATH:/lx

 o The following steps outline running the toolbox with lxrun

    Running the toolbox
    -------------------

    o Just copy (or make available) the above binaries on your guest and invoke
      them with lxrun:

      # lxrun /path/to/toolbox &

      (It is usually best to background this process.)

    o lxrun can also log the system calls it translates using its trace feature:

      # lxrun -t all /path/to/toolbox

      This will trace all the system calls guestd makes.  The output file is always
      placed in /tmp/lxrun.<pid>.  You can also restrict the system calls that are
      traced to a particular subset, run lxrun without any options to see the usage.

      It also appears possible to chroot the running process with -r, to specify the
      linux root directory with -R, and specify the uname of the emulated Linux
      system with -U.  I haven't ever needed to use these options.

 o Mouse grab/ungrab and device connection/disconnection are both known to work.




Notes from the old BUILD file that may still be helpful
-------------------------------------------------------
   
 o You can verify the module is installed with:

   # modinfo | grep hgfs

   This should output text of this format:
   <id#> <loadaddr> <size> <info> <revision#> hgfs (HGFS Device Interface)
   <id#> <loadaddr> <size> <info> <revision#> hgfs (Host/Guest Filesystem)

   There are two entries for the same ID number because we've placed both the
   device driver and the filesystem in a single loadable kernel module.

 o You can load the driver into the Kernel by hand with:
 
   # /usr/sbin/add_drv -v -m '* 0644 root sys' hgfs

   This should output something similar to:
   devfsadm[1032]: verbose: mknod /devices/pseudo/hgfs@0:vmware-hgfs 0l/3l/20644
   devfsadm[1032]: verbose: symlink /dev/vmware-hgfs -> ../devices/pseudo/hgfs@0:vmware-hgfs

 o You can remove the driver from the system by hand with:

   # /usr/sbin/rem_drv hgfs
   07070100000283000081A4000000000000000000000001682255050000056E000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs/FILES  Files
=====

Driver (hgfs/solaris/)
----------------------------------
hgfsSolaris.h    - definitions global to all parts of module
module.{c,h}     - module initialization routines (_init(), _fini()) and
                   structures (struct modlinkage, struct modlfs, ...)
filesystem.{c,h} - filesystem level functions (mount(), unmount())
hgfsState.{c,h}  - maintains internal state of filesystem (HgfsOpenFile, HgfsFile)
vnode.c          - file level functions (open(), read(), close())
device.{c,h}     - device interface (attach(), detach(), open(), read(),
                   close())
request.{c,h}    - functions that initialize, allocate, and manipulate requests
                   and the lists that they are on
debug.{c,h}      - debugging routines and macros

Misc (hgfs/solaris/docs)
------------------------------------
hgfs.conf             - must be place in /usr/kernel/drv/
append-to-devlink.tab - must append this text to /etc/devlink.tab to generate
                        symlink /dev/vmware-hgfs

Docs (hgfs/solaris/docs)
------------------------------------
BUILD                 - build instructions
synchronization.txt   - outline of how components are synchronized
request-lifecycle.txt - outline of how requests travel through driver
kadb.txt              - cheat sheet for Solaris' kernel debugger
hgfs-9-to-10.txt      - notes for porting from Solaris 9 to 10
  07070100000284000081A40000000000000000000000016822550500000039000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs/append-to-devlink.tab  type=ddi_pseudo;name=vmhgfs;addr=0;minor=vmware-hgfs	\M0
   07070100000285000081A40000000000000000000000016822550500001557000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs/hgfs-9-to-10.txt   Notes for porting Hgfs from Solaris 9 to 10
===========================================

Note that Solaris 10 is a beta and still in development, so these notes may
become outdated.  They are based on the headers in build 52.

vnode.h
-------
o Now use vn_make_ops() to create vnodeops
   int vn_make_ops(const char *, const fs_operation_def_t *, vnodeops_t **);
   void vn_freevnodeops(vnodeops_t *);

o Now use vfs_makefsops()/vfs_setfsops() to create vfsops
   (see vfs.h notes)

o There is a new structure struct fs_operation_def_t:
  
   typedef int (*fs_generic_func_p) ();
   
   typedef struct fs_operation_def {
      char *name;                /* name of operation (NUL terminated) */
      fs_generic_func_p func;    /* function implementing operation */
   } fs_operation_def_t;

  The name is likely one of the VOPNAME_* macros.

o vnodes should only be created by calls to vn_alloc(); they may not be embedded
  in fs-specific structures; vnodes may change size.  Definitions for related
  vnode allocation/freeing functions are:
   vnode_t *vn_alloc(int);
   void    vn_reinit(vnode_t *);
   void    vn_free(vnode_t *);

  Note that the vn_free() function destroys the vnode's mutex so doing so
  yourself will crash the system.

o There are a quite a few new vnode manipulation functions; see vnode.h.
  
o v_vfsmountedhere member of struct vnode is private, perhaps we shouldn't touch
  it; it is also protected by vn_vfswlock in vnode.c

o A number of private members were added to struct vnode -- shouldn't affect us

o New structure struct caller_context contains PID of caller, system ID, and
  a caller identifier.  This structure is now passed in as the last argument to:
   o vop_read
   o vop_write
   o vop_setattr
   o vop_rwlock
   o vop_rwunlock
   o vop_space

o vop_shrlock now takes an additional cred_t * argument as its last argument

o A new vnodeop was added:
   int (*vop_vnevent)(vnode_t *, vnevent_t);
  
  This likely has to do "something" to the vnode depending on the value of
  vnevent_t.  It is likely that this is required since the vnode now stores the
  cached path of the file in its private char *v_path member.  Can we use
  vnevent_*() for help, or do these call us?  How about the new vn_setpath*(),
  vn_path(), and vn_copypath() functions?  (We should use the VN_SETPATH() macro
  instead of calling vn_setpath() directly.)

o vnevent_t is a new enum with the following definition:
   typedef enum vnevent {
      VE_SUPPORT     = 0,        /* Query */
      VE_RENAME_SRC  = 1,        /* Rename, with vnode as source */
      VE_RENAME_DEST = 2,        /* Rename, with vnode as target/dest */
      VE_REMOVE      = 3,        /* Remove of vnode's name */
      VE_RMDIR       = 4         /* Remove of directory vnode's name */
   } vnevent_t;

o A new macro VN_SET_VFS_TYPE_DEV(vp, vfsp, type, dev) was created, but it is
  trivial.

o VN_CMP() now calls vn_getops() rather than dereferencing the vnode's vn_ops
  member.  (We should use this to access that variable too, it seems.)

o There is no more VN_INIT() macro, but it was trivial anyhow.


vfs.h
-----
o Filesystems now must supply their list of vfs operations using:
   int vfs_setfsops(int, const fs_operation_def_t *, vfsops_t **);

o There are also functions for filesystems to make, free, etc vfs operation
  structures:
   int  vfs_makefsops(const fs_operation_def_t *, vfsops_t **);
   void vfs_freevfsops(vfsops_t *);
   int  vfs_freevfsops_by_type(int);
   void vfs_setops(vfs_t *, vfsops_t *);
   vfsops_t *vfs_getops(vfs_t *vfsp);
   <there are some more: see vfs.h>

  The names placed in fs_operation_def_t->name are likely one of the VFSNAME_*
  macros.

o This isn't relevant to our layer, but the vfs now keeps filesystems in
  a double linked circular list rather than a singly linked list.  There are
  also issues with zones: each zone has a list of its own mounts.

o The vfs_op member of struct vfs should never be directly accessed; use the
  accessor functions described above.

o There is a new function in struct vfsops:
   int (*vfs_vnstate)(vfs_t *, vnode_t *, vntrans_t);

o vntrans_t is a new enum that is either VNTRANS_EXISTS, VNTRANS_IDLED,
  VNTRANS_RECLAIMED, or VNTRANS_DESTROYED.

o The function pointer vsw_init member of struct vfssw now has the definition:
   int (*vsw_init)(int, char *)

  Also, the vsw_optproto and vsw_vfsops members are no longer pointers.

  Since we will not be directly including this structure in our modlfs anymore
  (see modctl.h notes), these differences probably don't matter.

o There is a new structure (that will be used in our modlfs now instead of
  struct vfssw).
  
  typedef struct vfsdef_v2 {
     int       def_version;             /* structure version, must be first */
     char      *name;                   /* filesystem type name */
     int       (*init) (int, char *);   /* init routine */
     int       flags;                   /* fs flags */
     mntopts_t *optproto;               /* mount options table prototype */
  } vfsdef_v2;

  Note that build 58 contains the same structure but it is called vfsdef_v3.

o There are a few other additional macros and functions that likely won't matter
  to us.
  
modctl.h
--------
o struct modlfs now has a struct vfsdef_v3 instead of a struct vfssw
o other changes don't seem relevant

devops.h
--------
o There are no changes to struct cb_ops, struct dev_ops, or any of the command
  enums taken by these functions.
 07070100000286000081A4000000000000000000000001682255050000005C000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs/hgfs.conf  # name=<driver name> parent="pseudo" instance=0;
name="vmhgfs" parent="pseudo" instance=0;

07070100000287000081A4000000000000000000000001682255050000087B000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs/kadb.txt   Solaris kadb cheat sheet
=========================

Setting up
----------
 See Chapter 18 of Solaris' 9 "Writing Device Drivers"
 (http://docs.sun.com/db/doc/806-5222)

 If using a tip connection, boot with 'b kadb -d' and at the break for __start,
 create a deferred breakpoint with this command: hgfs# HgfsDevAttach:b

Breakpoints
-----------
 Deferred: <module># <symbol>:b
 Regular : <symbol>:b
 
 - Regular will complain if symbol isn't found.

 Examples:
  hgfs# HgfsDevAttach:b  --> deferred breakpoint for HgfsDevAttach
                             takes effect when hgfs module is loaded
  HgfsGetNewReq:b        --> breakpoint for HgfsGetNewReq
 
  Commands at breakpoints:
  :c : continue
  :s : single step
  :e : single step over function calls
  :u : stop after return to caller of current function
  :d : delete breakpoint
  :z : delete allbreakpoints
  

Viewing
-------

 <symbol>,<count>/<modifier>

 <symbol>  : symbol name, as in HgfsDevAttach, or address
 <count>   : number of items to show
 <modifier>: see pg. 348 "Writing Device Drivers" for complete list
             Common:
              d,D : 2,4-byte decimal
              x,X : 2,4-byte hex
              K   : 4-byte hex for 32 bit progs (use this for pointers)
              u,U : 2,4-byte unsigned decimal
              c,C : character, without or with ^escape notation
              s,S : string, without or with ^escape notation
              i   : instructions
              a   : prints address in symbolic form
              w,W : 2,4-byte write

 Examples:
  HgfsGetNewReq,24/ai --> see first 24 instructions of HgfsGetNewReq
  0x01234567,24/X     --> see 24 4-byte words at that address in hex
  *0x1234567,24/X     --> see the 24 4-byte words at memory address
                          specified in 0x1234567
  
Macros
------
$r : view registers
$c : stack trace
$b : display breakpoints


Pager (pause after <lines> lines)
-----
 0t<lines>::more


Registers
---------

 Display a single register: <(register)=K
 Set value of a register  : (value)>(register)

 Examples:
  <eip=K          Display the current contents of eip
  0xdf78b1f8>eip  Set the value of eip to 0xdf78b1f8
 07070100000288000081A40000000000000000000000016822550500001216000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs/request-lifecycle.txt  The lifecycle of a request in the Solaris HGFS code
===================================================

Note that this design is based almost entirely on the Linux HGFS code.

Data Structures
---------------
- A preallocated request pool array. (requestPool[])
- A list of the current free requests. (reqFreeList)
- A list of the currently pending requests (reqList)

Request States
--------------
- Unused:
   This is a request's initial state and it guarantees that the
   request is on the free request list.  Requests are placed back in
   this state after the Abandoned, Error, or Completed states.
   
- Allocated: 
   The request has been allocated to a client and is not on any of
   the lists.
   
- Submitted:
   The request has been filled out and has been placed on the pending
   request list.  The request may leave the pending request list while
   still in this state.
   
- Abandoned:
   The client was interrupted while waiting for the reply and has
   left the request to be cleaned up when a reply comes.  The request 
   is either still on the pending list or is not on any list, and will
   be accessed by its index into the requestPool array.
   
- Error:
   An error occurred while submitting the request or awaiting the
   reply.  The request is not on any lists.

- Completed: 
   The reply for this request has been received correctly.  The
   request is not on any lists.
             

State Transition
-----------------

Each request starts as Unused and is then Allocated and Submitted.  After the
request is submitted, the request can be Abandoned, can have an Error occur, or
can Complete successfully.  Each of these states transitions back to the Unused
state.

--> Unused --> Allocated --> Submitted ---> Abandoned ------> Unused
                                       \                   /
                                        --> Error -------->
                                        \                /
                                         -> Completed -->

Functions
---------
These functions will affect the state of requests and their locations on the
lists.

- HgfsInitRequestList():
   This will initialize each request's state to UNUSED and ID to its index into
   the requestPool array.  Each request will also be added to the free request
   list (reqFreeList).

- HgfsCancelAllRequests():
   Iterates over the pending request list and the request pool and cancels each
   request.  If a request is abandoned it must clean up for the interrupted
   process; if the requester is waiting, set the request state to error and wake
   up the client.

- HgfsGetNewReq():
   Takes an UNUSED request off the free list (reqFreeList) and sets its state
   to ALLOCATED.  The request will not be on any lists after this function is
   completed.

- HgfsEnqueueRequest():
   Puts an ALLOCATED request on to the pending request list (reqList) and sets
   its state to SUBMITTED.  The request should not be on any of the lists when
   this function is called.  This function will also assume that the list lock
   is held since most callers of this function will need to enqueue and do
   other operations atomically.

- HgfsDequeueRequest():
   Takes a SUBMITTED or ERROR request from the pending request list (reqList).
   The request will not be on any lists after this function is completed.  For
   the reason noted above, this function also assumes the list lock is held by
   the caller.

- HgfsDestroyReq():
   Puts a COMPLETED, ERROR, or ABANDONED request on to the free list
   (reqFreeList) and sets its state to UNUSED.  The request should not be on
   any lists when this function is called.

- HgfsReqSetState():
   Will set the state of the specified request to the specified value.  This
   abstraction is to help prevent forgetting to lock the state's mutex.

- HgfsReqGetState():
   Will return the state of the specified request.  This abstraction is to help
   prevent forgetting to lock the state's mutex.


Submitting a request
--------------------

{
   request = HgfsGetNewReq(superinfo);
   if (request is NULL) {
      return error
   }

   Fill in specific request information in request->packet
      - header.id and header.op of specific request struct
        should be set
      - request specific information should be filled in
      - request->packetSize should be set
   
   if (HgfsSubmitRequest(superinfo, request) fails) {
      return error
   }

   if (HgfsValidateReply(request, minimum size) fails) {
      HgfsDestroyReq(superinfo, request);
      return error
   }

   Process reply and give to client

   HgfsDestroyReq(superinfo, request);
}
  07070100000289000081A40000000000000000000000016822550500000CE0000000000000000000000000000000000000005300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/docs/synchronization.txt    Solaris HGFS Synchronization
============================

The Solaris synchronization primitives that will be used are mutexes and
condition variables.  Mutexes will protect access to shared resources
and condition variables will synchronize the order of the operations by
those accessing these resources.

Synchronization must be achieved in two directions.  First, a client
that requests an operation on a HGFS file must wait for the reply from
the HGFS server.  Second, guestd must wait for requests to be made when
it queries the HGFS device and there are no pending requests.

To enable this synchronization, the superinfo structure will contain
these elements:
- a list of outgoing requests (reqList)
- a condition variable for waiting until and signaling when requests are put
  on an empty list (reqCondVar)
- a flag which indicates whether guestd is waiting for a request
  (guestdWaiting)
- a mutex to protect access to these components (reqMutex)

Additionally, each request structure should contain:
- a condition variable for waiting for and signaling of replies to
  /this/ request (condVar)
- a flag indicating whether the requesting thread has been interrupted
  and is no longer waiting for the reply (abandoned)

The filesystem half of the driver will place requests onto reqList with
the following pseudocode:
{
 Acquire and initialize request structure from free request list
 mutex_enter(superinfo's reqMutex)
 
 HgfsEnqueueRequest(request list, request)
 
 if (superinfo's guestdWaiting is set) {
   cv_signal(superinfo's reqCondVar)
 }
 
 cv_wait_sig(request's condVar, superinfo's reqMutex)
 if (cv_wait_sig() was interrupted) {
   request's abandoned = TRUE
   release mutex and return EINTR
 }
 
 mutex_exit(superinfo's reqMutex)
 
 Read reply in request's packet
 When done, put request structure back on free list
}

Note that the calls to cv_signal() and cv_wait_sig() above must be atomic,
otherwise a reply could come and incur a signal before cv_wait_sig() is
called.  Therefore, the superinfo's reqMutex must also be passed to the
cv_wait_sig() on the request structure's condition variable.

The device half of the driver will check for and take requests of the
request list with the following pseudocode (HgfsDevRead()):
{
   mutex_enter(superinfo's reqMutex)
   
   if (request list is empty) {
      superinfo's guestdWaiting = TRUE
      cv_wait_sig(superinfo's reqCondVar, superinfo's reqMutex)
      if (cv_wait_sig() was interrupted) {
         superinfo's guestdWaiting = FALSE
         release mutex and return EINTR
      }
      superinfo's guestdWaiting = FALSE
   }

   Take next request off request list
   
   mutex_exit(superinfo's reqMutex)

   Submit request to guestd
}

When a reply is received, the device half of the driver will handle it
with the following pseudocode (HgfsDevWrite()):
{
   Read in and verify the reply from guestd
   Get pointer to the request structure from the handle in reply
   Copy reply payload to the request packet

   mutex_enter(superinfo's reqMutex)
   if (request's abandoned is set) {
      Clean up on behalf of interrupted thread
      - place request back on free list
      return <guestd doesn't care about this "error">
   }
   cv_signal(request's condVar)
   mutex_exit(superinfo's reqMutex)
}






0707010000028A000081A40000000000000000000000016822550500005D53000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/filesystem.c    /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * filesystem.c --
 *
 * Implementation of the filesystem level routines.  These include functions
 * that intialize, mount, unmount, and provide other various filesystem
 * information.
 */

#include <sys/param.h>          /* MAXNAMELEN */
#include <sys/file.h>           /* FKIOCTL */

#include "hgfsSolaris.h"
#include "hgfsState.h"
#include "filesystem.h"
#include "vnode.h"
#include "request.h"
#include "debug.h"

/*
 * Prototypes
 *
 * These are only needed because Solaris 10 requires that we create the vfsops
 * structure through their provided interface (vfs_setfsops()).
 */
#if HGFS_VFS_VERSION > 2
int HgfsMount(struct vfs *vfsp, struct vnode *vnodep,
              struct mounta *mntp, struct cred *credp);
int HgfsUnmount(struct vfs *vfsp, int mflag, struct cred *credp);
int HgfsRoot(struct vfs *vfsp, struct vnode **vnodepp);
int HgfsStatvfs(struct vfs *vfsp, struct statvfs64 *stats);
int HgfsSync(struct vfs *vfsp, short flags, struct cred *credp);
int HgfsVget(struct vfs *vfsp, struct vnode **vnodepp, struct fid *fidp);
int HgfsMountroot(struct vfs *vfsp, enum whymountroot reason);
int HgfsVnstate(vfs_t *vfsp, vnode_t *vp, vntrans_t trans);
#if HGFS_VFS_VERSION == 3
int HgfsFreevfs(struct vfs *vfsp);
#else
void HgfsFreevfs(struct vfs *vfsp);
#endif
#endif

#if HGFS_VFS_VERSION > 2
static vfsops_t *hgfsVfsOpsP;
#endif

/*
 * Fileystem type number given to us upon initialization.
 */
static int hgfsType;


#if HGFS_VFS_VERSION == 2
/*
 *----------------------------------------------------------------------------
 *
 * HgfsInit --
 *
 *    This is the filesystem initialization routine which is run when the
 *    filesystem is placed in the VFS switch table.
 *
 * Results:
 *    Returns zero on success.
 *
 * Side effects:
 *    The filesystem type (index number) is set to the provided value.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsInit(struct vfssw *vfsswp,  // IN: VFS Switch table struct
         int fstype)            // IN: Index into the vfssw table for this filesystem
{
   if (!vfsswp) {
      cmn_err(HGFS_ERROR, "HgfsInit: received NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsInit().\n");

   /*
    * Hook VFS operations into switch table and save
    * filesystem type number and pointer to vfsswp.
    */
   vfsswp->vsw_vfsops = &HgfsVfsOps;
   hgfsType = fstype;

   mutex_init(&vfsswp->vsw_lock, NULL, MUTEX_DRIVER, NULL);

   DEBUG(VM_DEBUG_LOAD, "fstype: %d\n", hgfsType);
   HgfsDebugPrintVfssw("HgfsInit()", vfsswp);

   DEBUG(VM_DEBUG_DONE, "HgfsInit() done.\n");

   return 0;
}

#else   /* Implies Solaris > 9 */

/*
 *----------------------------------------------------------------------------
 *
 * HgfsInit --
 *
 *    This is the filesystem initialization routine for Solaris 10.  It creates
 *    an array of fs_operation_def_t for all the vfs operations, then calls
 *    vfs_setfsops() to assign them to the filesystem properly.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsInit(int fstype,    // IN: Filesystem type
         char *name)    // IN: Name of the filesystem
{
   int ret;

   /* Construct the VFS operations array to give to vfs_setfsops() */
   static fs_operation_def_t vfsOpsArr[] = {
      HGFS_VOP(VFSNAME_MOUNT, vfs_mount, HgfsMount),
      HGFS_VOP(VFSNAME_UNMOUNT, vfs_unmount, HgfsUnmount),
      HGFS_VOP(VFSNAME_ROOT, vfs_root, HgfsRoot),
      HGFS_VOP(VFSNAME_STATVFS, vfs_statvfs, HgfsStatvfs),
      HGFS_VOP(VFSNAME_VGET, vfs_vget, HgfsVget),
      HGFS_VOP(VFSNAME_MOUNTROOT, vfs_mountroot, HgfsMountroot),
      HGFS_VOP(VFSNAME_FREEVFS, vfs_freevfs, HgfsFreevfs),
      HGFS_VOP(VFSNAME_VNSTATE, vfs_vnstate, HgfsVnstate),
#if HGFS_VFS_VERSION <= 3
      HGFS_VOP(VFSNAME_SYNC,  vfs_vnstate, (fs_generic_func_p)HgfsSync),
      { NULL,               NULL                        }
#else
      HGFS_VOP(VFSNAME_SYNC, vfs_sync, HgfsSync),
      { NULL,               { NULL }},
#endif
   };

   if (!name) {
      cmn_err(HGFS_ERROR, "HgfsInit: received NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsInit: fstype=%d, name=\"%s\"\n", fstype, name);

   /* Assign VFS operations to our filesystem. */
   ret = vfs_setfsops(fstype, vfsOpsArr, &hgfsVfsOpsP);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsInit: vfs_setfsops returned %d\n", ret);
      return ret;
   }

   ret = HgfsMakeVnodeOps();
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsInit: could not register HGFS Vnode Ops.\n");
      vfs_freevfsops_by_type(fstype);
      ret = EIO;
      return ret;
   }

   /* Set our filesystem type. */
   hgfsType = fstype;
   DEBUG(VM_DEBUG_DONE, "HgfsInit: done. (fstype=%d)\n", hgfsType);

   return 0;
}

#endif

/*
 *----------------------------------------------------------------------------
 *
 * HgfsFreeVfsOps --
 *
 *    Free VFS Ops created when we initialized the filesystem.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Resets hgfsVfsOpsP back to NULL.
 *
 *----------------------------------------------------------------------------
 */


void
HgfsFreeVfsOps(void)
{
#if HGFS_VFS_VERSION > 2
   if (hgfsVfsOpsP) {
      vfs_freevfsops_by_type(hgfsType);
   }
#endif
}


/*
 * VFS Entry Points
 */

/*
 *----------------------------------------------------------------------------
 *
 * HgfsMount --
 *
 *   This function is invoked when mount(2) is called on our filesystem.
 *   The filesystem is mounted on the supplied vnode.
 *
 * Results:
 *   Returns zero on success and an appropriate error code on error.
 *
 * Side effects:
 *   The filesystem is mounted on top of vnodep.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsMount(struct vfs *vfsp,     // IN: Filesystem to mount
          struct vnode *vnodep, // IN: Vnode that we are mounting on
          struct mounta *mntp,  // IN: Arguments to mount(2) from user
          struct cred *credp)   // IN: Credentials of caller
{
   HgfsSuperInfo *sip;
   HgfsMountData *mountData;
   int ret;
   dev_t dev;

   if (!vfsp || !vnodep || !mntp || !credp) {
      cmn_err(HGFS_ERROR, "HgfsMount: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsMount().\n");

   //HgfsDebugPrintVfs("HgfsMount", vfsp);
   //HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsMount", vnodep, FALSE);
   //HgfsDebugPrintMounta("HgfsMount", mntp);
   //HgfsDebugPrintCred("HgfsMount", credp);

   if (!HgfsSuser(credp)) {
      return EPERM;
   }

   if (mntp->datalen != sizeof *mountData) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMount: bad data size (%lu vs %lu).\n",
            (unsigned long) mntp->datalen,
            (unsigned long) sizeof *mountData);
      return EINVAL;
   }

   mountData = kmem_zalloc(sizeof *mountData, HGFS_ALLOC_FLAG);
   if (!mountData) {
      return ENOMEM;
   }

   if (ddi_copyin(mntp->dataptr, mountData, sizeof *mountData,
                  mntp->flags & MS_SYSSPACE ? FKIOCTL : 0) == -1) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't copy mount data.\n");
      ret = EFAULT;
      goto out;
   }

   /*
    * Make sure mount data matches what mount program will send us.
    */
   if (mountData->magic != HGFS_MAGIC) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMount: received invalid magic value: %x\n",
            mountData->magic);
      ret = EINVAL;
      goto out;
   }

   if (mountData->size != sizeof *mountData) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMount: received invalid size value: %x\n",
            mountData->magic);
      ret = EINVAL;
      goto out;
   }

   /* We support only one instance of hgfs, at least for now */
   if (HgfsGetSuperInfo()) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMount: HGFS is already mounted somewhere\n");
      ret = EBUSY;
      goto out;
   }

   /*
    * We need to find a unique device number for this VFS that will be used to
    * construct the filesystem id.
    */
   if ((dev = getudev()) == -1) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMount(): getudev() failed.\n");
      ret = ENXIO;
      goto out;
   }

   DEBUG(VM_DEBUG_LOAD, "HgfsMount: dev=%lu\n", dev);

   if (vfs_devismounted(dev)) {
      DEBUG(VM_DEBUG_FAIL,
            "HgfsMount(): dev is not unique. We should loop on this.\n");
      ret = ENXIO;
      goto out;
   }

   /*
    * Fill in values of the VFS structure for the kernel.
    *
    * There are several important values that must be set.  In particular, we
    * need to create a chain of pointers so the Kernel can easily move between
    * the various filesystems mounted on the system.
    *
    *  o Each filesystem must set its vfs_vnodecovered to the vnode of the
    *    directory it is mounted upon.
    *  o Each directory that is a mount point must set v_vfsmountedhere to
    *    point to the vfs struct of the filesystem mounted there.
    *  o The root vnode of each filesystem must have the VROOT flag set in its
    *    vnode's v_flag so that the Kernel knows to consult the two previously
    *    mentioned pointers.
    */
   vfsp->vfs_vnodecovered = vnodep;
   vfsp->vfs_flag &= ~VFS_UNMOUNTED;
   vfsp->vfs_flag |= HGFS_VFS_FLAGS;
   vfsp->vfs_bsize = HGFS_VFS_BSIZE;
   vfsp->vfs_fstype = hgfsType;
   vfsp->vfs_bcount = 0;
   /* If we had mount options, we'd call vfs_setmntopt with vfsp->vfs_mntopts */

   vfsp->vfs_dev = dev;
   vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, hgfsType);

   /*
    * Fill in value(s) of the vnode structure we are mounted on top of.  We
    * aren't allowed to modify this ourselves in Solaris 10.
    */
#  if HGFS_VFS_VERSION == 2
   vnodep->v_vfsmountedhere = vfsp;
#  endif

   HgfsInitSuperInfo(vfsp);

   sip = HgfsGetSuperInfo();
   vfsp->vfs_data = (caddr_t)sip;

   if (!sip->transportInit()) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMount: failed to start transport.\n");
      HgfsClearSuperInfo();
      ret = EIO;
      goto out;
   }

   /*
    * Now create the root vnode of the filesystem.
    *
    * Note: do not change the name from "/" here without first checking that
    * HgfsMakeFullName() in vnode.c will still do the right thing.  (See the
    * comment for the ".." special case.)
    */
   ret = HgfsVnodeGet(&sip->rootVnode,                  // vnode to fill in
                      sip,                              // Superinfo
                      vfsp,                             // This filesystem
                      "/",                              // File name
                      HGFS_FILE_TYPE_DIRECTORY,         // File type
                      &sip->fileHashTable);             // File hash table
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMount: couldn't get root vnode.\n");
      sip->transportCleanup();
      HgfsClearSuperInfo();
      ret = EIO;
      goto out;
   }

   /* We must signify that this is the root of our filesystem. */
   mutex_enter(&sip->rootVnode->v_lock);
   sip->rootVnode->v_flag |= VROOT;
   mutex_exit(&sip->rootVnode->v_lock);

   /* XXX do this? */
   VN_HOLD(vnodep);

   DEBUG(VM_DEBUG_DONE, "HgfsMount() done.\n");
   ret = 0;     /* Return success */

out:
   kmem_free(mountData, sizeof *mountData);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsUnmount --
 *
 *    This function is invoked when umount(2) is called on our filesystem.
 *
 * Results:
 *    Returns 0 on success and an error code on error.
 *
 * Side effects:
 *    The root vnode will be freed.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsUnmount(struct vfs *vfsp,   // IN: This filesystem
            int mflag,          // IN: Unmount flags
            struct cred *credp) // IN: Credentials of caller
{
   HgfsSuperInfo *sip;
   int ret;

   DEBUG(VM_DEBUG_ENTRY, "HgfsUnmount().\n");

   if (!vfsp || !credp) {
      cmn_err(HGFS_ERROR, "HgfsUnmount: NULL input from Kernel.\n");
      return EINVAL;
   }

   /*
    * Initial check to ensure caller is root.
    */
   if (!HgfsSuser(credp)) {
      return EPERM;
   }

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EINVAL;
   }

   if (vfsp != sip->vfsp) {
      DEBUG(VM_DEBUG_ALWAYS, "HgfsUnmount: vfsp != sip->vfsp.\n");
   }

   HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsUnmount",
                       vfsp->vfs_vnodecovered, FALSE);

   /* Take the request lock to prevent submitting new requests */
   mutex_enter(&sip->reqMutex);

   /*
    * Make sure there are no active files (besides the root vnode which we
    * release at the end of the function).
    */
   HgfsDebugPrintFileHashTable(&sip->fileHashTable, VM_DEBUG_STATE);

   if (!HgfsFileHashTableIsEmpty(sip, &sip->fileHashTable) &&
       !(mflag & MS_FORCE)) {
      DEBUG(VM_DEBUG_FAIL, "HgfsUnmount: there are still active files.\n");
      ret = EBUSY;
      goto out;
   }

   HgfsCancelAllRequests(sip);

   /*
    * Set unmounted flag in vfs structure.
    */
   vfsp->vfs_flag |= VFS_UNMOUNTED;

   /*
    * Close transport channel, we should not be gettign any more requests.
    */
   sip->transportCleanup();

   /*
    * Clean up fields in vnode structure of mount point and release hold on
    * vnodes for mount.
    */
#  if HGFS_VFS_VERSION == 2
   vfsp->vfs_vnodecovered->v_vfsmountedhere = NULL;
#  endif
   VN_RELE(vfsp->vfs_vnodecovered);
   VN_RELE(HGFS_ROOT_VNODE(sip));

   /*
    * Signify to the device half that the filesystem has been unmounted.
    */
   HGFS_ROOT_VNODE(sip) = NULL;
   HgfsClearSuperInfo();

   ret = 0;

out:
   mutex_exit(&sip->reqMutex);

   DEBUG(VM_DEBUG_DONE, "HgfsUnmount() done.\n");
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRoot --
 *
 *    This supplies the root vnode for the filesystem.
 *
 * Results:
 *    Returns zero on success and an error code on error.  On success vnodepp
 *    is set to the pointer of the root vnode.
 *
 * Side effects:
 *    The root vnode's reference count is incremented by one.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsRoot(struct vfs *vfsp,              // IN: Filesystem to find root vnode of
         struct vnode **vnodepp)        // OUT: Set to pointer to root vnode of this fs
{
   HgfsSuperInfo *sip;

   if (!vfsp || !vnodepp) {
      cmn_err(HGFS_ERROR, "HgfsRoot: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsRoot().\n");

   /*
    * Get the root vnode from the superinfo structure.
    */
   sip = HgfsGetSuperInfo();
   if (!sip) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRoot() failed to find superinfo.\n");
      return EIO;
   }

   if (vfsp != sip->vfsp) {
      DEBUG(VM_DEBUG_ALWAYS, "HgfsRoot: vfsp != sip->vfsp.\n");
   }

   VN_HOLD( HGFS_ROOT_VNODE(sip) );
   *vnodepp = HGFS_ROOT_VNODE(sip);

   DEBUG(VM_DEBUG_LOAD, " rootvnode=%p", HGFS_ROOT_VNODE(sip));

   DEBUG(VM_DEBUG_DONE, "HgfsRoot() done.\n");
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsStatvfs --
 *
 *    Provides statistics for the provided filesystem.  The values provided
 *    by this function are fake.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on exit.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsStatvfs(struct vfs *vfsp,           // IN: Filesystem to get statistics for
            struct statvfs64 *stats)    // OUT: Statistics are placed into this struct
{
   dev32_t dev32;

   DEBUG(VM_DEBUG_ENTRY, "HgfsStatvfs().\n");

   if (!stats) {
      cmn_err(HGFS_ERROR, "HgfsStatvfs: NULL input from Kernel.\n");
      return EINVAL;
   }

   /* Clear stats struct, then fill it in with our values. */
   memset(stats, 0, sizeof *stats);

   /*
    * Macros in case we need these elsewhere later.
    *
    * These were selected pretty randomly: the numbers should be large enough
    * so a user can attempt to create any reasonably sized file, but small
    * enough to be so the Kernel doesn't give callers who are using statvfs32
    * an EOVERFLOW.
    */
   #define HGFS_BLOCKS_TOTAL    0x00ffffff
   #define HGFS_BLOCKS_FREE     0x00ffefff
   #define HGFS_BLOCKS_AVAIL    0x00ffef00
   #define HGFS_FILES_TOTAL     0x00ffffff
   #define HGFS_FILES_FREE      0x00ffefff
   #define HGFS_FILES_AVAIL     0x00ffef00

   /* Compress the device number to 32-bits for consistency on 64-bit systems. */
   cmpldev(&dev32, vfsp->vfs_dev);

   stats->f_bsize   = HGFS_BLOCKSIZE;           /* Preferred fs block size */
   stats->f_frsize  = HGFS_BLOCKSIZE;           /* Fundamental fs block size */
   /* Next six are u_longlong_t */
   stats->f_blocks  = HGFS_BLOCKS_TOTAL;        /* Total blocks on fs */
   stats->f_bfree   = HGFS_BLOCKS_FREE;         /* Total free blocks */
   stats->f_bavail  = HGFS_BLOCKS_AVAIL;        /* Total blocks avail to non-root */
   stats->f_files   = HGFS_FILES_TOTAL;         /* Total files (inodes) */
   stats->f_ffree   = HGFS_FILES_FREE;          /* Total files free */
   stats->f_favail  = HGFS_FILES_AVAIL;         /* Total files avail to non-root */
   stats->f_fsid    = dev32;                    /* Filesystem id */
   stats->f_flag   &= ST_NOSUID;                /* Flags: we don't support setuid. */
   stats->f_namemax = MAXNAMELEN;               /* Max filename; use Solaris default. */

   /* Memset above and -1 of array size as n below ensure NUL termination. */
   strncpy(stats->f_basetype, HGFS_FS_NAME, sizeof stats->f_basetype - 1);
   strncpy(stats->f_fstr, HGFS_FS_NAME, sizeof stats->f_fstr - 1);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSync --
 *
 *    Flushes the filesystem cache.
 *
 * Results:
 *    Returns zero on success and an error code on failure.  Currently this
 *    always succeeds.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsSync(struct vfs *vfsp,      // IN: Filesystem to flush
         short flags,           // XXX: ?
         struct cred *credp)    // IN: Credentials of caller
{
   //DEBUG(VM_DEBUG_ENTRY, "HgfsSync().\n");

   /*
    * We just return success and hope the host OS calls its filesystem sync
    * operation periodically as well.
    */
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsVget --
 *
 *    Finds the vnode that matches the unique file identifier.
 *
 *    XXX: Come back to this when figure out how/if to store fidp to vnode
 *    mappings.
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

int
HgfsVget(struct vfs *vfsp,              // IN: Filesystem to operate on
         struct vnode **vnodepp,        // OUT: Set to pointer of found vnode
         struct fid *fidp)              // IN: Unique file identifier for vnode
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsVget() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsMountroot --
 *
 *    Mounts the file system on the root directory.
 *
 *    XXX: Still need to figure out when this is invoked.
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

int
HgfsMountroot(struct vfs *vfsp,         // IN: Filesystem to mount
              enum whymountroot reason) // IN: Reason why mounting on root
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsMountroot() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsReserved --
 *
 *    XXX: Is this a placeholder function in the struct?
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

int
HgfsReserved(struct vfs *vfsp,
             struct vnode **vnodepp,
             char *charp)
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsReserved() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFreevfs --
 *
 *    Called when a filesystem is unmounted to free the resources held by the
 *    filesystem.
 *
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION == 3
int HgfsFreevfs(vfs_t *vfsp)
#else
void HgfsFreevfs(vfs_t *vfsp)
#endif
{
   DEBUG(VM_DEBUG_ENTRY, "HgfsFreevfs().\n");

   /*
    * The only allocation to undo here is call mutex_destroy() on the vsw_lock
    * for our filesystem's struct vfssw.  Doing this causes a system crash,
    * from a call to a mutex free function within the Kernel (that is, not from
    * our code), so we are assured that the Kernel cleans this up for us.
    *
    * In Solaris 10 it seemed that we needed to free the vnode and vfs
    * operations we had made earlier (vn_freevnodeops() and
    * vfs_freevfsops_by_type()), but this is not so.  Freeing these prevents
    * 1) multiple mounts without first reloading the module, and 2) unloading
    * the module from the Kernel.  The combination of these two meant that
    * the guest would have to be rebooted to remount the filesystem.  Because
    * of all this, the assumption is made that the Kernel takes care of
    * removing these structures for us.
    */

#if HGFS_VFS_VERSION == 3
   return 0;
#endif
}


#if HGFS_VFS_VERSION > 2
/*
 *----------------------------------------------------------------------------
 *
 * HgfsVnstate --
 *
 *    Performs the necessary operations on the provided vnode given the state
 *    transfer that has occurred.
 *
 *    The possible transfers are VNTRANS_EXISTS, VNTRANS_IDLED,
 *    VNTRANS_RECLAIMED, and VNTRANS_DESTROYED (see <sys/vfs.h>).
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsVnstate(vfs_t *vfsp,        // IN: Pointer to our filesystem
            vnode_t *vp,        // IN: Vnode to change state of
            vntrans_t trans)    // IN: Type of state transfer
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsVnstate: entry.\n");

   return ENOTSUP;
}
#endif


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSuser --
 *
 *    Correctly implements the superuser check depending on the version of
 *    Solaris.
 *
 * Results:
 *    Returns zero if this user is not superuser, returns non-zero otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsSuser(struct cred *cr)      // IN: Credentials of the caller
{
   ASSERT(cr);
#if HGFS_VFS_VERSION == 2
   return suser(cr);
#else
   /*
    * I am assuming the crgetuid() is the effective uid, since the other two
    * related functions are crgetruid() and crgetsuid().
    */
   return (crgetuid(cr) == 0);
#endif
}

 0707010000028B000081A40000000000000000000000016822550500000BAF000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/filesystem.h    /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * filesystem.h --
 *
 * Includes and definitions for filesystem code.
 *
 */



#ifndef __FILESYSTEM_H_
#define __FILESYSTEM_H_


/*
 * Solaris includes
 */
#include <sys/mntent.h>		/* mount flags */
#include <sys/mount.h>

/*
 * Kernel only includes
 */
#ifdef _KERNEL

#include <sys/modctl.h>         /* mod_fsops, ... */
#include <sys/cmn_err.h>        /* cmn_err() */
#include <sys/ddi.h>            /* cmn_err() */
#include <sys/sunddi.h>         /* cmn_err() */
#include <sys/vfs.h>            /* struct vfs, ... */
#include <sys/vnode.h>          /* struct vnode, ... */

#if SOL11
#include <sys/vfs_opreg.h>      /* fs_operation_def_t, ... */
#endif

#endif /* _KERNEL */

#include "hgfsSolaris.h"

/*
 * Macros
 *
 * XXX - Must place these into a common header file that can be
 * used here and by the Linux client and OS X clients (user and kernel)
 * components.
 */
#define HGFS_MAGIC           (0xbacbacbc)
#define HGFS_FSTYPE          HGFS_FS_NAME

/*
 * Struct passed from mount program to kernel (fs module)
 *
 * ******************* IMPORTANT ****************************
 * XXX - This must be kept compatible with the HgfsMountInfo
 * structure which is defined in hgfsDevLinux.h
 * ******************* IMPORTANT ****************************
 */
typedef struct HgfsMountData {
   uint32_t magic;
   uint32_t size;
   uint32_t version;
   uint32_t fd;
   uint32_t flags;
} HgfsMountData;


#ifdef _KERNEL
/*
 * Macros
 */
#ifdef SOL9
 #define HGFS_VFS_FLAGS         VFS_NOSUID
#else
 #define HGFS_VFS_FLAGS         VFS_NOSETUID
#endif

#ifdef SOL9
 #define HGFS_VFS_VERSION       2
#elif defined SOL10
 #define HGFS_VFS_VERSION       3
#else
 #define HGFS_VFS_VERSION       5
#endif

#define HGFS_VFS_BSIZE          HGFS_PACKET_MAX
#define HGFS_COPYIN_FLAGS	(0)
#define HGFS_VFS_TO_SI(vfsp)	((HgfsSuperInfo *)vfsp->vfs_data)

/* This macro is used for both vnode ops and vfs ops. */
#if defined SOL9 || defined SOL10
#define HGFS_VOP(vopName, vopFn, hgfsFn) { vopName, hgfsFn }
#else
#define HGFS_VOP(vopName, vopFn, hgfsFn) { vopName, { .vopFn = hgfsFn } }
#endif

/*
 * Functions
 */
/* To abstract Solaris 9 and 10 differences in suser() calls */
int HgfsSuser(struct cred *cr);

void HgfsFreeVfsOps(void);

/*
 * Extern variables
 */
#ifdef SOL9
EXTERN struct vfsops HgfsVfsOps;
#endif

#endif /* _KERNEL */


#endif /* __FILESYSTEM_H_ */
 0707010000028C000081A4000000000000000000000001682255050000121C000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/hgfsBdGlue.c    /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * hgfsBdGlue.c --
 *
 * Glue to communicate directly with backdoor code instead of offloading
 * it to guestd
 */

#include "hgfsSolaris.h"
#include "request.h"
#include "hgfsBdGlue.h"
#include "hgfsBd.h"
#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "debug.h"

static RpcOut *hgfsRpcOut;
static void *packetBuffer;

/*
 * Public function implementations.
 */

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsBackdoorSendRequest --
 *
 *    Send one request through backdoor and wait for the result.
 *
 * Results:
 *    0 on success, standard UNIX error code upon failure.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

int
HgfsBackdoorSendRequest(HgfsReq *req)     // IN/OUT: Request to be sent
{
   char const *replyPacket;
   size_t packetSize;

   ASSERT(req->state == HGFS_REQ_SUBMITTED);

   ASSERT(req->packetSize <= HGFS_PACKET_MAX);
   bcopy(req->packet, packetBuffer, req->packetSize);
   packetSize = req->packetSize;

   DEBUG(VM_DEBUG_COMM,
         "HgfsBackdoorSendRequest: 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)) {

      DEBUG(VM_DEBUG_COMM,
            "HgfsBackdoorSendRequest: HGFS is disabled in the host\n");

      req->state = HGFS_REQ_ERROR;
      return ENOSYS;

   } else if (HgfsBd_Dispatch(hgfsRpcOut, packetBuffer,
                              &packetSize, &replyPacket) == 0) {

      DEBUG(VM_DEBUG_COMM,
            "HgfsBackdoorSendRequest: backdoor reply received\n");

      /* Request was sent successfully. Copy the reply and return to the client. */
      ASSERT(packetSize <= HGFS_PACKET_MAX);

      bcopy(replyPacket, req->packet, packetSize);
      req->packetSize = packetSize;
      req->state = HGFS_REQ_COMPLETED;

   } else {

      DEBUG(VM_DEBUG_COMM,
            "HgfsBackdoorSendRequest: backdoor error\n");

      /* Pass the error into the request. */
      req->state = HGFS_REQ_ERROR;

      /*
       * 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);
   }

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsBackdoorCancelRequest --
 *
 *    Cancel request stub. Since backdoor is synchronous transport this
 *    function should never be called in practice.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsBackdoorCancelRequest(HgfsReq *req)      // IN: Request to be cancelled
{
   DEBUG(VM_DEBUG_COMM, "HgfsBackdoorCancelRequest: %p\n", req);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsBackdoorInit --
 *
 *    This function initializes backdoor transport by allocating transfer
 *    buffer.
 *
 * Results:
 *    TRUE if buffer was allocated succsessfully, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

Bool
HgfsBackdoorInit(void)
{
   packetBuffer = HgfsBd_GetBuf();

   return packetBuffer != NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsBackdoorCleanup --
 *
 *    This function closes backdoor channel. It is supposed to be
 *    called when we unmount the filesystem.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

void
HgfsBackdoorCleanup(void)
{
   DEBUG(VM_DEBUG_COMM, "HgfsBackdoorCleanup: Closing backdoor\n");

   HgfsBd_CloseBackdoor(&hgfsRpcOut);
   HgfsBd_PutBuf(packetBuffer);
}

0707010000028D000081A4000000000000000000000001682255050000039C000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/hgfsBdGlue.h    /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * bdhandler.h --
 *
 * Glue for backdoor library.
 */

#ifndef _HGFS_BD_GLUE_H_
#define _HGFS_BD_GLUE_H_

int HgfsBackdoorSendRequest(HgfsReq *req);
void HgfsBackdoorCancelRequest(HgfsReq *req);
Bool HgfsBackdoorInit(void);
void HgfsBackdoorCleanup(void);

#endif // _HGFS_DRIVER_BDHANDLER_H_
0707010000028E000081A40000000000000000000000016822550500001730000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/hgfsSolaris.h   /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * hgfsSolaris.h --
 *
 * Contains declarations needed in entire Solaris HGFS module.
 *
 */

/* XXX: Split the non-kernel parts off into another header */

#ifndef __HGFS_H_
#define __HGFS_H_

/*
 * System includes
 */

/*
 * Kernel system includes
 */
#ifdef _KERNEL

/*
 * These must be in this order, so we include them here, and only here, so
 * that this ordering is guaranteed.
 *
 * <sys/types.h> defines min and max macros (that shouldn't be used because
 * they're broken) and <sys/conf.h> includes <sys/systm.h> which defines
 * functions named min and max.  The function definitions must come first.
 */
#include <sys/conf.h>           /* D_NEW flag and other includes */
#include <sys/types.h>          /* various typedefs */

#endif /* _KERNEL */

/*
 * VMware includes
 */
#include "hgfsProto.h"
#include "hgfsState.h"
#include "filesystem.h"

/*
 * Kernel VMware includes
 */
#ifdef _KERNEL

#include "dbllnklst.h"
#include "vm_basic_defs.h"
#include "vm_assert.h"

#endif /* _KERNEL */


/*
 * Macros
 */

#define HGFS_PAYLOAD_MAX(reply)         (HGFS_PACKET_MAX - sizeof *reply)
#define HGFS_FS_NAME                    "vmhgfs"
#define HGFS_BLOCKSIZE                  1024


/*
 * Kernel only macros
 */
#ifdef _KERNEL

/* Determines size of request pool */
#define HGFS_MAX_OUTSTANDING_REQS       4

/* HGFS cmn_err() levels */
#define HGFS_ERROR                      (CE_WARN)

/* Internal error code(s) */
#define HGFS_ERR                        (-1)
#define HGFS_ERR_NULL_INPUT             (-50)
#define HGFS_ERR_NODEV                  (-51)
#define HGFS_ERR_INVAL                  (-52)

/*
 * Don't change this to KM_NOSLEEP without first making sure that we handle
 * the possibility of kmem_zalloc() failing: KM_SLEEP guarantees it won't fail
 */
#define HGFS_ALLOC_FLAG                 (KM_SLEEP)

/* Accessing root inode and vnode */
#define HGFS_ROOT_VNODE(sip)            (sip->rootVnode)


#endif /* _KERNEL */


/*
 * Structures
 */

/* We call them *Header in the Solaris code for clarity. */
typedef HgfsReply HgfsReplyHeader;
typedef HgfsRequest HgfsRequestHeader;

/*
 * Kernel only structures and variables
 */
#ifdef _KERNEL

/*
 * Each request will traverse through this set of states.  See
 * docs/request-lifecycle.txt for an explanation of these and the API for
 * interacting with requests.
 */
typedef enum {
   HGFS_REQ_UNUSED = 1,
   HGFS_REQ_ALLOCATED,
   HGFS_REQ_SUBMITTED,
   HGFS_REQ_ABANDONED,
   HGFS_REQ_ERROR,
   HGFS_REQ_COMPLETED
} HgfsReqState;


/*
 * General request structure.  Specific requests and replies are placed in the
 * packet of this structure.
 */
typedef struct HgfsReq {
   DblLnkLst_Links listNode;            /* Node to connect the request to one of
                                         * the lists (free or pending) */
   kcondvar_t condVar;                  /* Condition variable to wait for and
                                           signal presence of reply. Used with
                                           the reqMutex in HgfsSuperInfo. */
   HgfsReqState state;                  /* Indicates state of request */
   uint32_t id;                         /* The unique identifier of this request */
   uint32_t packetSize;                 /* Total size of packet */
   char packet[HGFS_PACKET_MAX];        /* Contains both requests and replies */
} HgfsReq;


/*
 * The global state structure for the entire module.  This is allocated in
 * HgfsDevAttach() and deallocated in HgfsDevDetach().
 *
 * Note that reqMutex and reqFreeList are also used for synchronization between
 * the filesystem and driver.  See docs/synchronization.txt for details.
 */
typedef struct HgfsSuperInfo {
   kmutex_t reqMutex;                   /* Serializes sending of requests */

   /* Free request list */
   DblLnkLst_Links reqFreeList;         /* Anchor for free request list */
   kmutex_t reqFreeMutex;               /* For protection of reqFreeList */
   kcondvar_t reqFreeCondVar;           /* For waiting on free request list */

   /* For filesystem */
   struct vfs *vfsp;                    /* Our filesystem structure */
   struct vnode *rootVnode;             /* Root vnode of the filesystem */
   HgfsFileHashTable fileHashTable;     /* File hash table */

   int (*sendRequest)(HgfsReq *req);    /* Current transport's sent method */
   void (*cancelRequest)(HgfsReq *req); /* Current transport's cancel method */
   Bool (*transportInit)(void);
   void (*transportCleanup)(void);
} HgfsSuperInfo;


/*
 * Global Variables
 */

/* Pool of request structures */
HgfsReq requestPool[HGFS_MAX_OUTSTANDING_REQS];

/*
 * Used to access shared state of driver and filesystem.  superInfoHead is
 * a pointer to state managed by Solaris, hgfsInstance is the index into this state
 * list, and is set in HgfsDevAttach().
 *
 * Note that both the driver and filesystem use ddi_get_soft_state() to get
 * a pointer to the superinfo.  Both use superInfoHead, but the device uses the
 * instance number derived from passed in arguments and the filesystem uses
 * hgfsInstance.  This is not a problem as long as the instance number cannot
 * change, which /should/ be guaranteed, and there is only a single instance,
 * which cannot happen.
 */
void *superInfoHead;
int hgfsInstance;

#endif /* _KERNEL */

#endif /* __HGFS_H_ */
0707010000028F000081A4000000000000000000000001682255050000A1E2000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/hgfsState.c /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * hgfsState.c --
 *
 * This implements the functions that provide all the filesystem specific state
 * that is placed underneath the vnodes.
 *
 */


/*
 * Includes
 */

#include "vnode.h"
#include "hgfsState.h"
#include "debug.h"

#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "sha1.h"               /* SHA-1 for Node ID calculation */


/*
 * Macros
 */
#define HGFS_VNODE_INIT_FLAG    VNOMAP  /* So we don't have to implement mmap() */
#define HGFS_VNODE_INIT_COUNT   1
#define HGFS_VNODE_INIT_RDEV    VBLK    /* We pretend to be a block device */

#define HGFS_FILE_HT_HEAD(ht, index)    (ht->hashTable[index]).next
#define HGFS_FILE_HT_BUCKET(ht, index)  (&ht->hashTable[index])

#define HGFS_IS_ROOT_FILE(sip, file)    (HGFS_VP_TO_FP(sip->rootVnode) == file)

/*
 * Prototypes for internal functions
 */

/* Allocation/initialization/free of open file state */
static HgfsOpenFile *HgfsAllocOpenFile(const char *fileName, HgfsFileType fileType,
                                       HgfsFileHashTable *htp);
static void HgfsFreeOpenFile(HgfsOpenFile *ofp, HgfsFileHashTable *htp);

/* Acquiring/releasing file state */
static HgfsFile *HgfsGetFile(const char *fileName, HgfsFileType fileType,
                             HgfsFileHashTable *htp);
static void HgfsReleaseFile(HgfsFile *fp, HgfsFileHashTable *htp);
static int HgfsInitFile(HgfsFile *fp, const char *fileName, HgfsFileType fileType);

/* Adding/finding/removing file state from hash table */
static inline void HgfsAddFile(HgfsFile *fp, HgfsFileHashTable *htp);
static inline void HgfsRemoveFile(HgfsFile *fp, HgfsFileHashTable *htp);
static HgfsFile *HgfsFindFile(const char *fileName, HgfsFileHashTable *htp);

/* Other utility functions */
static unsigned int HgfsFileNameHash(const char *fileName);
static void HgfsNodeIdHash(const char *fileName, uint32_t fileNameLength,
                           ino64_t *outHash);


/*
 * Public functions
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsVnodeGet --
 *
 *    Creates a vnode for the provided filename.
 *
 *    This will always allocate a vnode and HgfsOpenFile.  If a HgfsFile
 *    already exists for this filename then that is used, if a HgfsFile doesn't
 *    exist, one is created.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    If the HgfsFile already exists, its reference count is incremented;
 *    otherwise a HgfsFile is created.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsVnodeGet(struct vnode **vpp,        // OUT: Filled with address of created vnode
             HgfsSuperInfo *sip,        // IN:  Superinfo
             struct vfs *vfsp,          // IN:  Filesystem structure
             const char *fileName,      // IN:  Name of this file
             HgfsFileType fileType,     // IN:  Type of file
             HgfsFileHashTable *htp)    // IN:  File hash table
{
   struct vnode *vp;

   ASSERT(vpp);
   ASSERT(sip);
   ASSERT(vfsp);
   ASSERT(fileName);
   ASSERT(htp);

   /*
    * Here we need to construct the vnode for the kernel as well as our
    * internal file system state.  Our internal state consists of
    * a HgfsOpenFile and a HgfsFile.  The HgfsOpenFile is state kept per-open
    * file; the HgfsFile state is kept per-file.  We have a one-to-one mapping
    * between vnodes and HgfsOpenFiles, and a many-to-one mapping from each of
    * those to a HgfsFile.
    *
    * Note that it appears the vnode is intended to be used as a per-file
    * structure, but we are using it as a per-open-file. The sole exception
    * for this is the root vnode because it is returned by HgfsRoot().  This
    * also means that reference counts for all vnodes except the root should
    * be one; the reference count in our HgfsFile takes on the role of the
    * vnode reference count.
    */
   /*
    * Note that we no longer embed the vnode in our private structure in
    * Solaris 9.  This was done to simplify this code path and decrease the
    * differences between Solaris 9 and 10.
    */
#  ifdef SOL9
   vp = kmem_zalloc(sizeof *vp, HGFS_ALLOC_FLAG);
#  else
   vp = vn_alloc(HGFS_ALLOC_FLAG);
#  endif
   if (!vp) {
      return HGFS_ERR;
   }

   /*
    * Now we'll initialize the vnode.  We need to set the file type, vnode
    * operations, flags, filesystem pointer, reference count, and device.
    * After that we'll create our private structures and hang them from the
    * vnode's v_data pointer.
    */
   switch (fileType) {
   case HGFS_FILE_TYPE_REGULAR:
      vp->v_type = VREG;
      break;

   case HGFS_FILE_TYPE_DIRECTORY:
      vp->v_type = VDIR;
      break;

   default:
      /* Hgfs only supports directories and regular files */
      goto vnode_error;
   }

   /*
    * Now set the vnode operations.  This is handled differently on Solaris
    * 9 and 10, and we call HgfsSetVnodeOps() to take care of this for us.
    */
   if (HgfsSetVnodeOps(vp) != 0) {
      goto vnode_error;
   }

   /*
    * The vnode cache constructor will have initialized the mutex for us on
    * Solaris 10, so we only do it ourselves on Solaris 9.
    */
#  ifdef SOL9
   mutex_init(&vp->v_lock, NULL, MUTEX_DRIVER, NULL);
#  endif

   vp->v_flag  = HGFS_VNODE_INIT_FLAG;
   vp->v_count = HGFS_VNODE_INIT_COUNT;
   vp->v_vfsp  = vfsp;
   vp->v_rdev  = HGFS_VNODE_INIT_RDEV;

   /*
    * We now allocate our private open file structure.  This will correctly
    * initialize the per-open-file state, as well as locate (or create if
    * necessary) the per-file state.
    */
   vp->v_data = (void *)HgfsAllocOpenFile(fileName, fileType, htp);
   if (!vp->v_data) {
      goto openfile_error;
   }

   /* Fill in the provided address with the new vnode. */
   *vpp = vp;

   /* Return success */
   return 0;

   /* Cleanup points for errors. */
openfile_error:
#  ifdef SOL9
   mutex_destroy(&vp->v_lock);
#  endif
vnode_error:
#  ifdef SOL9
   kmem_free(vp, sizeof *vp);
#  else
   vn_free(vp);
#  endif
   return HGFS_ERR;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsVnodePut --
 *
 *    Releases the provided vnode.
 *
 *    This will always free both the vnode and its associated HgfsOpenFile.
 *    The HgfsFile's reference count is decremented and, if 0, freed.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

int
HgfsVnodePut(struct vnode *vp,          // IN: Vnode to release
             HgfsFileHashTable *htp)    // IN: Hash table pointer
{
   HgfsOpenFile *ofp;

   ASSERT(vp);
   ASSERT(htp);

   /* Get our private open-file state. */
   ofp = HGFS_VP_TO_OFP(vp);
   if (!ofp) {          // XXX Maybe ASSERT() this?
      return HGFS_ERR;
   }

   /*
    * We need to free the open file structure.  This takes care of releasing
    * our reference on the underlying file structure (and freeing it if
    * necessary).
    */
   HgfsFreeOpenFile(ofp, htp);

   /*
    * Now we clean up the vnode.
    */
#  ifdef SOL9
   mutex_destroy(&vp->v_lock);
#  endif

#  ifdef SOL9
   kmem_free(vp, sizeof *vp);
#  else
   vn_free(vp);
#  endif

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsVnodeDup --
 *
 *    Duplicates the vnode and HgfsOpenFile (per-open state) of a file and
 *    increments the reference count of the underlying HgfsFile.  This function
 *    just calls HgfsVnodeGet with the right arguments.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.  On success
 *    the address of the duplicated vnode is written to newVpp.
 *
 * Side effects:
 *    The HgfsFile for origVp will have an additional reference.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsVnodeDup(struct vnode **newVpp,             // OUT: Given address of new vnode
             struct vnode *origVp,              // IN:  Vnode to duplicate
             struct HgfsSuperInfo *sip,         // IN:  Superinfo pointer
             HgfsFileHashTable *htp)            // IN:  File hash table
{
   ASSERT(newVpp);
   ASSERT(origVp);
   ASSERT(sip);
   ASSERT(htp);

   DEBUG(VM_DEBUG_ALWAYS, "HgfsVnodeDup: duping %s\n", HGFS_VP_TO_FILENAME(origVp));

   return HgfsVnodeGet(newVpp, sip, origVp->v_vfsp, HGFS_VP_TO_FILENAME(origVp),
                       HGFS_VP_TO_HGFSFILETYPE(origVp), &sip->fileHashTable);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFileNameToVnode --
 *
 *    Allocates new per-open-file state if a HgfsFile for fileName exists in
 *    the provided file hash table.
 *
 * Results:
 *    Returns 0 on success or a non-zero error code on failure.  On success,
 *    vpp is filled with the address of the new per-open state.
 *
 * Side effects:
 *    The reference count of the HgfsFile for fileName is incremented if it
 *    exists.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsFileNameToVnode(const char *fileName,
                    struct vnode **vpp,
                    struct HgfsSuperInfo *sip,
                    struct vfs *vfsp,
                    HgfsFileHashTable *htp)
{
   HgfsFile *fp;
   HgfsFileType fileType;

   ASSERT(vpp);
   ASSERT(sip);
   ASSERT(vfsp);
   ASSERT(fileName);
   ASSERT(htp);

   /*
    * XXX: This locking here is not totally correct.  Because we are calling
    * HgfsVnodeGet(), which does its own locking on the hash table, we must
    * make finding out the file is in the hash table and then creating our
    * internal state that increments that file's reference count non-atomic.
    *
    * Because of this, it is possible for the file to be in the hash table when
    * we look, then be removed by the time we look for it again.  As
    * a consquence, we will add the file to the hash table then.  This is
    * partially correct in the fact that the file was in the hash table when we
    * looked, but it is partially incorrect since it wasn't in the hash table
    * when we looked again.  In practice this shouldn't cause any problems, but
    * it is possible for a file that is deleted on the host to remain in our
    * hash table longer than it should.
    *
    * A more correct locking scheme was not used because the complexity of
    * doing so outweighed the problems that can occur from this more simple
    * approach.  This approach was also left as is because it provides an
    * optimization to the filesystem by decreasing the number of requests that
    * must be sent significantly.  This optimization can be easily turned off
    * by commenting out the single call to this function in HgfsLookup() in
    * vnode.c.
    *
    * Possible solutions to this are: 1) adding new locks to the top-level
    * public functions (HgfsVnodeGet(), FileNameToVnode(), and NodeIdGet()), 2)
    * bringing the hash table locking up to those same functions, or 3)
    * reimplementing much of the call sequence down to the calls to FindFile()
    * and AddFile() in HgfsGetFile() that is specific to this function.  1 is
    * likely the best option as 2 uses hash table locks for an incorrect
    * purpose and 3 will create a significant amount of repeated code.
    */

   DEBUG(VM_DEBUG_ALWAYS, "HgfsFileNameToVnode: looking for %s\n", fileName);

   mutex_enter(&htp->mutex);

   fp = HgfsFindFile(fileName, htp);
   if (!fp) {
      mutex_exit(&htp->mutex);
      return HGFS_ERR;
   }

   /* Guaranteed by HgfsFindFile(). */
   ASSERT(strcmp(fileName, fp->fileName) == 0);

   /*
    * We save the type of this file with the lock held in case it goes away:
    * see the above comment about locking.
    */
   fileType = fp->fileType;

   mutex_exit(&htp->mutex);

   DEBUG(VM_DEBUG_ALWAYS, "HgfsFileNameToVnode: found %s\n", fileName);

   return HgfsVnodeGet(vpp, sip, vfsp, fileName, fileType, htp);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsNodeIdGet --
 *
 *    Gets the node id for the provided file.  This will only calculate the
 *    node id again if a per-file state structure doesn't yet exist for this
 *    file.  (This situation exists on a readdir since dentries are filled in
 *    rather than creating vnodes.)
 *
 *    In Solaris, node ids are provided in vnodes and inode numbers are
 *    provided in dentries.  For applications to work correctly, we must make
 *    sure that the inode number of a file's dentry and the node id in a file's
 *    vnode match one another.  This poses a problem since vnodes typically do
 *    not exist when dentries need to be created, and once a dentry is created
 *    we have no reference to it since it is copied to the user and freed from
 *    kernel space.  An example of a program that breaks when these values
 *    don't match is /usr/bin/pwd.  This program first acquires the node id of
 *    "." from its vnode, then traverses backwards to ".." and looks for the
 *    dentry in that directory with the inode number matching the node id.
 *    (This is how it obtains the name of the directory it was just in.)
 *    /usr/bin/pwd repeats this until it reaches the root directory, at which
 *    point it concatenates the filenames it acquired along the way and
 *    displays them to the user.  When inode numbers don't match the node id,
 *    /usr/bin/pwd displays an error saying it cannot determine the directory.
 *
 *    The Hgfs protocol does not provide us with unique identifiers for files
 *    since it must support filesystems that do not have the concept of inode
 *    numbers.  Therefore, we must maintain a mapping from filename to node id/
 *    inode numbers.  This is done in a stateless manner by calculating the
 *    SHA-1 hash of the filename.  All points in the Hgfs code that need a node
 *    id/inode number obtain it by either calling this function or directly
 *    referencing the saved node id value in the vnode, if one is available.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsNodeIdGet(HgfsFileHashTable *htp,   // IN:  File hash table
              const char *fileName,     // IN:  Filename to get node id for
              uint32_t fileNameLength,  // IN:  Length of filename
              ino64_t *outNodeId)       // OUT: Destination for nodeid
{
   HgfsFile *fp;

   ASSERT(htp);
   ASSERT(fileName);
   ASSERT(outNodeId);

   mutex_enter(&htp->mutex);

   fp = HgfsFindFile(fileName, htp);
   if (fp) {
      *outNodeId = fp->nodeId;
   } else {
      HgfsNodeIdHash(fileName, fileNameLength, outNodeId);
   }

   mutex_exit(&htp->mutex);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsInitFileHashTable --
 *
 *    Initializes the hash table used to track per-file state.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsInitFileHashTable(HgfsFileHashTable *htp)   // IN: Hash table to initialize
{
   int i;

   ASSERT(htp);

   mutex_init(&htp->mutex, NULL, MUTEX_DRIVER, NULL);

   for (i = 0; i < ARRAYSIZE(htp->hashTable); i++) {
      DblLnkLst_Init(&htp->hashTable[i]);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFileHashTableIsEmpty --
 *
 *    Determines whether the hash table is in an acceptable state to unmount
 *    the file system.
 *
 *    Note that this is not strictly empty: if the only file in the table is
 *    the root of the filesystem and its reference count is 1, this is
 *    considered empty since this is part of the operation of unmounting the
 *    filesystem.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

Bool
HgfsFileHashTableIsEmpty(HgfsSuperInfo *sip,            // IN: Superinfo
                         HgfsFileHashTable *htp)        // IN: File hash table
{
   int i;

   ASSERT(sip);
   ASSERT(htp);

   mutex_enter(&htp->mutex);

   /* Traverse each bucket. */
   for (i = 0; i < ARRAYSIZE(htp->hashTable); i++) {
      DblLnkLst_Links *currNode = HGFS_FILE_HT_HEAD(htp, i);

      /* Visit each file in this bucket */
      while (currNode != HGFS_FILE_HT_BUCKET(htp, i)) {
         HgfsFile *currFile = DblLnkLst_Container(currNode, HgfsFile, listNode);

         /*
          * Here we special case the root of our filesystem.  In a correct
          * unmount, the root vnode of the filesystem will have an entry in the
          * hash table and will have a reference count of 1.  We check if the
          * current entry is the root file, and if so, make sure its vnode's
          * reference count is not > 1.  Note that we are not mapping from file
          * to vnode here (which is not possible), we are using the root vnode
          * stored in the superinfo structure.  This is the only vnode that
          * should have multiple references associated with it because whenever
          * someone calls HgfsRoot(), we return that vnode.
          */
         if (HGFS_IS_ROOT_FILE(sip, currFile)) {
            mutex_enter(&HGFS_ROOT_VNODE(sip)->v_lock);
            if (HGFS_ROOT_VNODE(sip)->v_count <= 1) {
               mutex_exit(&HGFS_ROOT_VNODE(sip)->v_lock);

               /* This file is okay; skip to the next one. */
               currNode = currNode->next;
               continue;
            }

            DEBUG(VM_DEBUG_FAIL, "HgfsFileHashTableIsEmpty: %s has count of %d.\n",
                  currFile->fileName, HGFS_ROOT_VNODE(sip)->v_count);

            mutex_exit(&HGFS_ROOT_VNODE(sip)->v_lock);
            /* Fall through to failure case */
         }

         /* Fail if a file is found. */
         mutex_exit(&htp->mutex);
         DEBUG(VM_DEBUG_FAIL, "HgfsFileHashTableIsEmpty: %s "
               "still in use (file count=%d).\n",
               currFile->fileName, currFile->refCount);
         return FALSE;
      }
   }

   mutex_exit(&htp->mutex);

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDebugPrintFileHashTable --
 *
 *    This will print out all the HgfsFiles that we have in the hash table, as
 *    well as the HgfsFile reference count.  This should help finding places
 *    where there may be loose references on files that prevent an unmount
 *    (EBUSY) when it should be allowed.
 *
 * Results:
 *    Void.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsDebugPrintFileHashTable(HgfsFileHashTable *htp,     // IN: Hash table to print
                            int level)                  // IN: Debugging level
{
   int bucket;

   ASSERT(htp);

   mutex_enter(&htp->mutex);

   for (bucket = 0; bucket < ARRAYSIZE(htp->hashTable); bucket++) {
      DblLnkLst_Links *currNode = HGFS_FILE_HT_HEAD(htp, bucket);

      while (currNode != HGFS_FILE_HT_BUCKET(htp, bucket)) {
         HgfsFile *currFile = DblLnkLst_Container(currNode, HgfsFile, listNode);

         mutex_enter(&currFile->mutex);
         DEBUG(level, "HgfsDebugPrintFileHashTable: "
               "file: %s, count: %d (bucket %d)\n",
               currFile->fileName, currFile->refCount, bucket);
         mutex_exit(&currFile->mutex);

         currNode = currNode->next;
      }
   }

   mutex_exit(&htp->mutex);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsHandleIsSet --
 *
 *    Determines whether handle of the vnode's open file is currently set.
 *
 * Results:
 *    Returns TRUE if the handle is set, FALSE if the handle is not set.
 *    HGFS_ERR is returned on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
HgfsHandleIsSet(struct vnode *vp)       // IN: Vnode to check handle of
{
   HgfsOpenFile *ofp;
   Bool isSet;

   ASSERT(vp);

   ofp = HGFS_VP_TO_OFP(vp);
   if (!ofp) {
      return HGFS_ERR;
   }

   mutex_enter(&ofp->handleMutex);
   isSet = ofp->handleIsSet;
   mutex_exit(&ofp->handleMutex);

   return isSet;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSetOpenFileHandle --
 *
 *    Sets the open file handle for the provided vnode.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    The handle may not be set again until it is cleared.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsSetOpenFileHandle(struct vnode *vp,         // IN: Vnode to set handle for
                      HgfsHandle handle)        // IN: Value of handle
{
   HgfsOpenFile *ofp;

   ASSERT(vp);

   ofp = HGFS_VP_TO_OFP(vp);
   if (!ofp) {
      return HGFS_ERR;
   }

   mutex_enter(&ofp->handleMutex);

   if (ofp->handleIsSet) {
      DEBUG(VM_DEBUG_FAIL, "**HgfsSetOpenFileHandle: handle for %s already set to %d; "
            "cannot set to %d\n", HGFS_VP_TO_FILENAME(vp), ofp->handle, handle);
      mutex_exit(&ofp->handleMutex);
      return HGFS_ERR;
   }

   ofp->handle = handle;
   ofp->handleIsSet = TRUE;

   DEBUG(VM_DEBUG_STATE, "HgfsSetOpenFileHandle: set handle for %s to %d\n",
         HGFS_VP_TO_FILENAME(vp), ofp->handle);

   mutex_exit(&ofp->handleMutex);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetOpenFileHandle --
 *
 *    Gets the open file handle for the provided vnode.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.  On success,
 *    the value of the vnode's handle is placed in outHandle.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsGetOpenFileHandle(struct vnode *vp,         // IN:  Vnode to get handle for
                      HgfsHandle *outHandle)    // OUT: Filled with value of handle
{
   HgfsOpenFile *ofp;

   ASSERT(vp);
   ASSERT(outHandle);

   ofp = HGFS_VP_TO_OFP(vp);
   if (!ofp) {
      return HGFS_ERR;
   }

   mutex_enter(&ofp->handleMutex);

   if (!ofp->handleIsSet) {
      DEBUG(VM_DEBUG_FAIL, "**HgfsGetOpenFileHandle: handle for %s is not set.\n",
            HGFS_VP_TO_FILENAME(vp));
      mutex_exit(&ofp->handleMutex);
      return HGFS_ERR;
   }

   *outHandle = ofp->handle;

   mutex_exit(&ofp->handleMutex);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsClearOpenFileHandle --
 *
 *    Clears the open file handle for the provided vnode.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    The handle may be set.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsClearOpenFileHandle(struct vnode *vp)       // IN: Vnode to clear handle for
{
   HgfsOpenFile *ofp;

   ASSERT(vp);

   ofp = HGFS_VP_TO_OFP(vp);
   if (!ofp) {
      return HGFS_ERR;
   }

   mutex_enter(&ofp->handleMutex);

   ofp->handle = 0;
   ofp->handleIsSet = FALSE;

   DEBUG(VM_DEBUG_STATE, "HgfsClearOpenFileHandle: cleared %s's handle\n",
         HGFS_VP_TO_FILENAME(vp));

   mutex_exit(&ofp->handleMutex);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSetOpenFileMode --
 *
 *    Sets the mode of the open file for the provided vnode.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    The mode may not be set again until cleared.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsSetOpenFileMode(struct vnode *vp,   // IN: Vnode to set mode for
                    HgfsMode mode)      // IN: Mode to set to
{
   HgfsOpenFile *ofp;

   ASSERT(vp);

   ofp = HGFS_VP_TO_OFP(vp);
   if (!ofp) {
      return HGFS_ERR;
   }

   mutex_enter(&ofp->modeMutex);

   if (ofp->modeIsSet) {
      DEBUG(VM_DEBUG_FAIL, "**HgfsSetOpenFileMode: mode for %s already set to %d; "
            "cannot set to %d\n", HGFS_VP_TO_FILENAME(vp), ofp->mode, mode);
      mutex_exit(&ofp->modeMutex);
      return HGFS_ERR;
   }

   ofp->mode = mode;
   ofp->modeIsSet = TRUE;

   DEBUG(VM_DEBUG_STATE, "HgfsSetOpenFileMode: set mode for %s to %d\n",
         HGFS_VP_TO_FILENAME(vp), ofp->mode);

   mutex_exit(&ofp->modeMutex);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetOpenFileMode --
 *
 *    Gets the mode of the open file for the provided vnode.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsGetOpenFileMode(struct vnode *vp,   // IN:  Vnode to get mode for
                    HgfsMode *outMode)  // OUT: Filled with mode
{
   HgfsOpenFile *ofp;

   ASSERT(vp);
   ASSERT(outMode);

   ofp = HGFS_VP_TO_OFP(vp);
   if (!ofp) {
      return HGFS_ERR;
   }

   mutex_enter(&ofp->modeMutex);

   if (!ofp->modeIsSet) {
//      DEBUG(VM_DEBUG_FAIL, "**HgfsGetOpenFileMode: mode for %s is not set.\n",
//            HGFS_VP_TO_FILENAME(vp));
      mutex_exit(&ofp->modeMutex);
      return HGFS_ERR;
   }

   *outMode = ofp->mode;

   mutex_exit(&ofp->modeMutex);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsClearOpenFileMode --
 *
 *    Clears the mode of the open file for the provided vnode.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    The mode may be set again.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsClearOpenFileMode(struct vnode *vp) // IN: Vnode to clear mode for
{
   HgfsOpenFile *ofp;

   ASSERT(vp);

   ofp = HGFS_VP_TO_OFP(vp);
   if (!ofp) {
      return HGFS_ERR;
   }

   mutex_enter(&ofp->modeMutex);

   ofp->mode = 0;
   ofp->modeIsSet = FALSE;
   
   DEBUG(VM_DEBUG_STATE, "HgfsClearOpenFileMode: cleared %s's mode\n",
         HGFS_VP_TO_FILENAME(vp));

   mutex_exit(&ofp->modeMutex);

   return 0;
}


/*
 * Internal functions
 */


/* Allocation/initialization/free of open file state */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsAllocOpenFile --
 *
 *    Allocates and initializes an open file structure.  Also finds or, if
 *    necessary, creates the underlying HgfsFile per-file state.
 *
 * Results:
 *    Returns a pointer to the open file on success, NULL on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static HgfsOpenFile *
HgfsAllocOpenFile(const char *fileName,         // IN: Name of file
                  HgfsFileType fileType,        // IN: Type of file
                  HgfsFileHashTable *htp)       // IN: Hash table
{
   HgfsOpenFile *ofp;

   ASSERT(fileName);
   ASSERT(htp);

   /*
    * We allocate and initialize our open-file state.
    */
   ofp = (HgfsOpenFile *)kmem_zalloc(sizeof *ofp, HGFS_ALLOC_FLAG);
   /* kmem_zalloc() cannot fail if given KM_SLEEP; check otherwise */
#  if HGFS_ALLOC_FLAG != KM_SLEEP
   if (!ofp) {
      return NULL;
   }
#  endif

   /* Manually set these since the public functions need the lock. */
   ofp->handle = 0;
   ofp->handleIsSet = FALSE;

   ofp->mode = 0;
   ofp->modeIsSet = FALSE;

   mutex_init(&ofp->handleMutex, NULL, MUTEX_DRIVER, NULL);
   mutex_init(&ofp->handleMutex, NULL, MUTEX_DRIVER, NULL);

   /*
    * Now we get a reference to the underlying per-file state.
    */
   ofp->hgfsFile = HgfsGetFile(fileName, fileType, htp);
   if (!ofp->hgfsFile) {
      kmem_free(ofp, sizeof *ofp);
      return NULL;
   }

   return ofp;

}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFreeOpenFile --
 *
 *    Frees the provided open file.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsFreeOpenFile(HgfsOpenFile *ofp,             // IN: Open file to free
                 HgfsFileHashTable *htp)        // IN: File hash table
{
   ASSERT(ofp);
   ASSERT(htp);

   /*
    * First we release our reference to the underlying per-file state.
    */
   HgfsReleaseFile(ofp->hgfsFile, htp);

   /*
    * Then we destroy anything initialized and free the open file.
    */
   mutex_destroy(&ofp->handleMutex);
   mutex_destroy(&ofp->modeMutex);

   kmem_free(ofp, sizeof *ofp);
}


/* Acquiring/releasing file state */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetFile --
 *
 *    Gets the file for the provided filename.
 *
 *    If no file structure exists for this filename, one is created and added
 *    to the hash table.
 *
 * Results:
 *    Returns a pointer to the file on success, NULL on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static HgfsFile *
HgfsGetFile(const char *fileName,       // IN: Filename to get file for
            HgfsFileType fileType,      // IN: Type of file
            HgfsFileHashTable *htp)     // IN: Hash table to look in
{
   HgfsFile *fp;
   int err;

   ASSERT(fileName);
   ASSERT(htp);

   /*
    * We try to find the file in the hash table.  If it exists we increment its
    * reference count and return it.
    */
   mutex_enter(&htp->mutex);

   fp = HgfsFindFile(fileName, htp);
   if (fp) {
      /* Signify our reference to this file. */
      mutex_enter(&fp->mutex);
      fp->refCount++;
      mutex_exit(&fp->mutex);

      mutex_exit(&htp->mutex);
      return fp;
   }

   DEBUG(VM_DEBUG_ALWAYS, "HgfsGetFile: allocated HgfsFile for %s.\n", fileName);

   /*
    * If it doesn't exist we create one, initialize it, and add it to the hash
    * table.
    */
   fp = (HgfsFile *)kmem_zalloc(sizeof *fp, HGFS_ALLOC_FLAG);
#  if HGFS_ALLOC_FLAG != KM_SLEEP
   if (!fp) {
      /* fp is NULL already */
      goto out;
   }
#  endif

   err = HgfsInitFile(fp, fileName, fileType);
   if (err) {
      kmem_free(fp, sizeof *fp);
      fp = NULL;
      goto out;
   }

   /*
    * This is guaranteed to not add a duplicate since we checked above and have
    * held the lock until now.
    */
   HgfsAddFile(fp, htp);

out:
   mutex_exit(&htp->mutex);
   DEBUG(VM_DEBUG_DONE, "HgfsGetFile: done\n");
   return fp;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsReleaseFile --
 *
 *    Releases a reference to the provided file.  If the reference count of
 *    this file becomes zero, the file structure is removed from the hash table
 *    and freed.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsReleaseFile(HgfsFile *fp,           // IN: File to release
                HgfsFileHashTable *htp) // IN: Hash table to look in/remove from
{
   ASSERT(fp);
   ASSERT(htp);

   /*
    * Decrement this file's reference count.  If it becomes zero, then we
    * remove it from the hash table and free it.
    */
   mutex_enter(&fp->mutex);

   if ( !(--fp->refCount) ) {
      mutex_exit(&fp->mutex);

      /* Remove file from hash table, then clean up. */
      HgfsRemoveFile(fp, htp);

      DEBUG(VM_DEBUG_ALWAYS, "HgfsReleaseFile: freeing HgfsFile for %s.\n",
            fp->fileName);

      rw_destroy(&fp->rwlock);
      mutex_destroy(&fp->mutex);
      kmem_free(fp, sizeof *fp);
      return;
   }

   DEBUG(VM_DEBUG_ALWAYS, "HgfsReleaseFile: %s has %d references.\n",
         fp->fileName, fp->refCount);

   mutex_exit(&fp->mutex);
}


/* Allocation/initialization/free of file state */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsInitFile --
 *
 *    Initializes a file structure.
 *
 *    This sets the filename of the file and initializes other structure
 *    elements.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsInitFile(HgfsFile *fp,              // IN: File to initialize
             const char *fileName,      // IN: Name of file
             HgfsFileType fileType)     // IN: Type of file
{
   int len;

   ASSERT(fp);
   ASSERT(fileName);

   /* Make sure the filename will fit. */
   len = strlen(fileName);
   if (len > sizeof fp->fileName - 1) {
      return HGFS_ERR;
   }

   fp->fileNameLength = len;
   memcpy(fp->fileName, fileName, len + 1);
   fp->fileName[fp->fileNameLength] = '\0';

   /*
    * We save the file type so we can recreate a vnode for the HgfsFile without
    * sending a request to the Hgfs Server.
    */
   fp->fileType = fileType;

   /* Initialize the links to place this file in our hash table. */
   DblLnkLst_Init(&fp->listNode);

   /*
    * Fill in the node id.  This serves as the inode number in directory
    * entries and the node id in vnode attributes.
    */
   HgfsNodeIdHash(fp->fileName, fp->fileNameLength, &fp->nodeId);

   /*
    * The reader/write lock is for the rwlock/rwunlock vnode entry points and
    * the mutex is to protect the reference count on this structure.
    */
   rw_init(&fp->rwlock, NULL, RW_DRIVER, NULL);
   mutex_init(&fp->mutex, NULL, MUTEX_DRIVER, NULL);

   /* The caller is the single reference. */
   fp->refCount = 1;

   return 0;
}


/* Adding/finding/removing file state from hash table */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsAddFile --
 *
 *    Adds the file to the hash table.
 *
 *    This function must be called with the hash table lock held.  This is done
 *    so adding the file in the hash table can be made with any other
 *    operations (such as previously finding out that this file wasn't in the
 *    hash table).
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static inline void
HgfsAddFile(HgfsFile *fp,               // IN: File to add
            HgfsFileHashTable *htp)     // IN: Hash table to add to
{
   unsigned int index;

   ASSERT(fp);
   ASSERT(htp);

   index = HgfsFileNameHash(fp->fileName);

   /* Add this file to the end of the bucket's list */
   DblLnkLst_LinkLast(HGFS_FILE_HT_HEAD(htp, index), &fp->listNode);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRemoveFile --
 *
 *    Removes file from the hash table.
 *
 *    Note that unlike the other two hash functions, this one performs its own
 *    locking since the removal doesn't need to be atomic with other
 *    operations.  (This could change in the future if the functions that use
 *    this one are reorganized.)
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static inline void
HgfsRemoveFile(HgfsFile *fp,            // IN: File to remove
               HgfsFileHashTable *htp)  // IN: Hash table to remove from
{
   ASSERT(fp);
   ASSERT(htp);

   mutex_enter(&htp->mutex);

   /* Take this file off its list */
   DblLnkLst_Unlink1(&fp->listNode);

   mutex_exit(&htp->mutex);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFindFile --
 *
 *    Looks for a filename in the hash table.
 *
 *    This function must be called with the hash table lock held.  This is done
 *    so finding the file in the hash table and using it (after this function
 *    returns) can be atomic.
 *
 * Results:
 *    Returns a pointer to the file if found, NULL otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static HgfsFile *
HgfsFindFile(const char *fileName,      // IN: Filename to look for
             HgfsFileHashTable *htp)    // IN: Hash table to look in
{
   HgfsFile *found = NULL;
   DblLnkLst_Links *currNode;
   unsigned int index;

   ASSERT(fileName);
   ASSERT(htp);

   /* Determine which bucket. */
   index = HgfsFileNameHash(fileName);

   /* Traverse the bucket's list. */
   for (currNode = HGFS_FILE_HT_HEAD(htp, index);
        currNode != HGFS_FILE_HT_BUCKET(htp, index);
        currNode = currNode->next) {
      HgfsFile *curr;
      curr = DblLnkLst_Container(currNode, HgfsFile, listNode);

      if (strcmp(curr->fileName, fileName) == 0) {
         /* We found the file we want. */
         found = curr;
         break;
      }
   }

   /* Return file if found. */
   return found;
}


/* Other utility functions */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFileNameHash --
 *
 *    Hashes the filename to get an index into the hash table.  This is known
 *    as the PJW string hash function and it was taken from "Mastering
 *    Algorithms in C".
 *
 * Results:
 *    Returns an index between 0 and HGFS_HT_NR_BUCKETS.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static unsigned int
HgfsFileNameHash(const char *fileName)  // IN: Filename to hash
{
   unsigned int val = 0;

   ASSERT(fileName);

   while (*fileName != '\0') {
      unsigned int tmp;

      val = (val << 4) + (*fileName);
      if ((tmp = (val & 0xf0000000))) {
        val = val ^ (tmp >> 24);
        val = val ^ tmp;
      }

      fileName++;
   }

   return val % HGFS_HT_NR_BUCKETS;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsNodeIdHash --
 *
 *    Hashes the provided filename to generate a node id.
 *
 * Results:
 *    None.  The value of the hash is filled into outHash.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsNodeIdHash(const char *fileName,    // IN:  Filename to hash
               uint32_t fileNameLength, // IN:  Length of the filename
               ino64_t *outHash)   // OUT: Location to write hash to
{
   SHA1_CTX hashContext;
   unsigned char digest[SHA1_HASH_LEN];
   int i;

   ASSERT(fileName);
   ASSERT(outHash);

   /* Make sure we start at a consistent state. */
   memset(&hashContext, 0, sizeof hashContext);
   memset(digest, 0, sizeof digest);
   memset(outHash, 0, sizeof *outHash);

   /* Generate a SHA1 hash of the filename */
   SHA1Init(&hashContext);
   SHA1Update(&hashContext, (unsigned char *)fileName, fileNameLength);
   SHA1Final(digest, &hashContext);

   /*
    * Fold the digest into the allowable size of our hash.
    *
    * For each group of bytes the same size as our output hash, xor the
    * contents of the digest together.  If there are less than that many bytes
    * left in the digest, xor each byte that's left.
    */
   for(i = 0; i < sizeof digest; i += sizeof *outHash) {
      int bytesLeft = sizeof digest - i;

      /* Do a byte-by-byte xor if there aren't enough bytes left in the digest */
      if (bytesLeft < sizeof *outHash) {
         int j;

         for (j = 0; j < bytesLeft; j++) {
            uint8 *outByte = (uint8 *)outHash + j;
            uint8 *inByte = (uint8 *)((u_longlong_t *)(digest + i)) + j;
            *outByte ^= *inByte;
         }
         break;
      }

      /* Block xor */
      *outHash ^= *((u_longlong_t *)(digest + i));
   }

   /*
    * Clear the most significant byte so that user space apps depending on
    * a node id/inode number that's only 32 bits won't break.  (For example,
    * gedit's call to stat(2) returns EOVERFLOW if we don't do this.)
    */
#  ifndef HGFS_BREAK_32BIT_USER_APPS
   *((uint32_t *)outHash) ^= *((uint32_t *)outHash + 1);
   *((uint32_t *)outHash + 1) = 0;
#  endif

   DEBUG(VM_DEBUG_INFO, "Hash of: %s (%d) is %"FMT64"u\n", fileName, fileNameLength, *outHash);

   return;
}
  07070100000290000081A400000000000000000000000168225505000018EF000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/hgfsState.h /*********************************************************
 * Copyright (c) 2004-2016, 2021, 2023 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * hgfsState.h --
 *
 * Public functions, types, and macros for Hgfs state that is attached to
 * vnodes.
 */

#ifndef __HGFS_STATE_H_
#define __HGFS_STATE_H_

/*
 * Includes
 */
#include <sys/param.h>          /* MAXPATHLEN */
#include <sys/rwlock.h>         /* krwlock_t */
#include <sys/ksynch.h>         /* kmutex_t */
#include <sys/vnode.h>          /* struct vnode */

#include "hgfsProto.h"
#include "dbllnklst.h"


/*
 * Macros
 */
/* Number of buckets for the HgfsInode hash table */
#define HGFS_HT_NR_BUCKETS             5

/* Conversion between different state structures */
#define HGFS_VP_TO_OFP(vp)      ((HgfsOpenFile *)(vp)->v_data)
#define HGFS_VP_TO_FP(vp)       ((HgfsFile *)(HGFS_VP_TO_OFP(vp))->hgfsFile)
#define HGFS_OFP_TO_VP(ofp)     (ofp)->vnodep

#define HGFS_VP_TO_FILENAME(vp)                         \
         HGFS_VP_TO_FP(vp)->fileName

#define HGFS_VP_TO_FILENAME_LENGTH(vp)                  \
         HGFS_VP_TO_FP(vp)->fileNameLength

#define HGFS_VP_TO_NODEID(vp)                           \
         HGFS_VP_TO_FP(vp)->nodeId

#define HGFS_VP_TO_RWLOCK(vp)                           \
         HGFS_VP_TO_FP(vp)->rwlock

#define HGFS_VP_TO_RWLOCKP(vp)  &(HGFS_VP_TO_RWLOCK(vp))

#define HGFS_VP_TO_HGFSFILETYPE(vp)                     \
         HGFS_VP_TO_FP(vp)->fileType

/*
 * This macro is used for confidence checking the fact that Solaris won't ever call
 * one of our vnode operations without first calling lookup(), which is the
 * place where we acquire the filename.  I have never seen a function fail
 * because of this, so it is likely that we can remove this macro and the checks
 * on it throughout vnode.c.
 */
#define HGFS_KNOW_FILENAME(vp)                          \
         (                                              \
          vp &&                                         \
          HGFS_VP_TO_FP(vp) &&                          \
          HGFS_VP_TO_FP(vp)->fileName[0] != '\0'        \
         )

/*
 * Types
 */

typedef uint32_t HgfsMode;

/*
 * State kept per shared file from the host.
 *
 * All fields are read-only after initialization except reference count, which
 * is protected by the mutex.
 */
typedef struct HgfsFile {
   /* Link to place this state on the file state hash table */
   DblLnkLst_Links listNode;
   /*
    * Full path of file within the filesystem (that is, taking /mnt/hgfs as /).
    * These are built from / in HgfsMount() and appending names as provided to
    * HgfsLookup(). Saving the length in HgfsIget() saves having to calculate
    * it in each HgfsMakeFullName().
    */
   char fileName[MAXPATHLEN + 1];
   uint32_t fileNameLength;
   ino64_t nodeId;
   /*
    * The file type is saved so additional per-open-file vnodes can be
    * recreated from a Hgfs file without sending a request to the Hgfs server.
    */
   HgfsFileType fileType;
   /*
    * Readers/writers lock for this file.  Needed by rwlock() and rwunlock()
    * entry points.
    */
   krwlock_t rwlock;
   /* Lock to protect the reference count of this file state. */
   kmutex_t mutex;
   uint32_t refCount;
} HgfsFile;


/*
 * State kept per vnode, which implies per open file within a process.
 *
 * Once created, the hgfsFile and vnode/vnodep values are read-only.  The
 * handle and mode will change throughout this structure's existence, and are
 * both protected by their own mutex.
 */
typedef struct HgfsOpenFile {
   /* Handle provided by reply to a request */
   HgfsHandle handle;
   Bool handleIsSet;
   kmutex_t handleMutex;
   /*
    * Mode specified for this file to be created with.  This is necessary
    * because create is called with the mode then open is called without it.
    * We save this value in create and access it in open.
    */
   HgfsMode mode;
   Bool modeIsSet;
   kmutex_t modeMutex;
   /*
    * Pointer to the single Hgfs file state structure shared amongst all open
    * instances of this file.
    */
   HgfsFile *hgfsFile;
   /*
    * A pointer back to the vnode this open-file state is for.
    */
   struct vnode *vnodep;
} HgfsOpenFile;

/* The hash table for file state. */
typedef struct HgfsFileHashTable {
   kmutex_t mutex;
   DblLnkLst_Links hashTable[HGFS_HT_NR_BUCKETS];
} HgfsFileHashTable;

/* Forward declaration to prevent circular dependency between this and hgfs.h. */
struct HgfsSuperInfo;


/*
 * Functions
 */
int HgfsVnodeGet(struct vnode **vpp, struct HgfsSuperInfo *sip, struct vfs *vfsp,
                 const char *fileName, HgfsFileType fileType, HgfsFileHashTable *htp);
int HgfsVnodePut(struct vnode *vp, HgfsFileHashTable *ht);
int HgfsVnodeDup(struct vnode **newVpp, struct vnode *origVp, struct HgfsSuperInfo *sip,
                 HgfsFileHashTable *htp);
int HgfsFileNameToVnode(const char *fileName, struct vnode **vpp,
                        struct HgfsSuperInfo *sip, struct vfs *vfsp,
                        HgfsFileHashTable *htp);
void HgfsNodeIdGet(HgfsFileHashTable *ht, const char *fileName,
                   uint32_t fileNameLength, ino64_t *outNodeId);
void HgfsInitFileHashTable(HgfsFileHashTable *htp);
Bool HgfsFileHashTableIsEmpty(struct HgfsSuperInfo *sip, HgfsFileHashTable *htp);

/* Handle get/set/clear functions */
int HgfsSetOpenFileHandle(struct vnode *vp, HgfsHandle handle);
int HgfsGetOpenFileHandle(struct vnode *vp, HgfsHandle *outHandle);
int HgfsClearOpenFileHandle(struct vnode *vp);
Bool HgfsHandleIsSet(struct vnode *vp);

/* Mode get/set/clear functions */
int HgfsSetOpenFileMode(struct vnode *vp, HgfsMode mode);
int HgfsGetOpenFileMode(struct vnode *vp, HgfsMode *outMode);
int HgfsClearOpenFileMode(struct vnode *vp);

/* Debugging function */
void HgfsDebugPrintFileHashTable(HgfsFileHashTable *htp, int level);

#endif /* __HGFS_STATE_H_ */
 07070100000291000081A4000000000000000000000001682255050000256B000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/kernelStubs.h   /*********************************************************
 * Copyright (c) 2006-2019,2021, 2023 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * 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__

#define KRNL_STUBS_DRIVER_TYPE_POSIX      1
#define KRNL_STUBS_DRIVER_TYPE_GDI        2
#define KRNL_STUBS_DRIVER_TYPE_WDM        3
#define KRNL_STUBS_DRIVER_TYPE_NDIS       4
#define KRNL_STUBS_DRIVER_TYPE_STORPORT   5

// For now (vsphere-2015), choose a good default. Later we'll modify all the
// build files using KernelStubs to set this.
#ifndef KRNL_STUBS_DRIVER_TYPE
#  if defined(_WIN32)
#     define KRNL_STUBS_DRIVER_TYPE KRNL_STUBS_DRIVER_TYPE_WDM
#  else
#     define KRNL_STUBS_DRIVER_TYPE KRNL_STUBS_DRIVER_TYPE_POSIX
#  endif
#endif

#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 <linux/kernel.h>
#   include <linux/string.h>
#elif defined(_WIN32)
#   define _CRT_ALLOCATION_DEFINED // prevent malloc.h from defining malloc et. all
#   if KRNL_STUBS_DRIVER_TYPE == KRNL_STUBS_DRIVER_TYPE_GDI
#      include <d3d9.h>
#      include <winddi.h>
#      include <stdio.h>
#      include "vm_basic_types.h"
#      include "vm_basic_defs.h"
#      include "vm_assert.h"
#   elif KRNL_STUBS_DRIVER_TYPE == KRNL_STUBS_DRIVER_TYPE_NDIS
#      include <ntddk.h>
#      include <stdio.h>    /* for _vsnprintf, vsprintf */
#      include <stdarg.h>   /* for va_start stuff */
#      include "vm_basic_defs.h"
#      include "vm_assert.h"
#      include "kernelStubsFloorFixes.h"
#pragma warning(disable:4201) // unnamed struct/union
#      include <ndis.h>
#   elif KRNL_STUBS_DRIVER_TYPE == KRNL_STUBS_DRIVER_TYPE_STORPORT
#      include "vm_basic_types.h"
#      include <wdm.h>   /* kernel memory APIs, DbgPrintEx */
#      include <stdio.h>    /* for _vsnprintf, vsprintf */
#      include <stdarg.h>   /* for va_start stuff */
#      include <stdlib.h>   /* for min macro. */
#      include <Storport.h> /* for Storport functions */
#      include "vm_basic_defs.h"
#      include "vm_assert.h"  /* Our assert macros */
#      include "kernelStubsFloorFixes.h"
#   elif KRNL_STUBS_DRIVER_TYPE == KRNL_STUBS_DRIVER_TYPE_WDM
#      include "vm_basic_types.h"
#      if defined(NTDDI_WINXP) && (NTDDI_VERSION >= NTDDI_WINXP)
#         include <wdm.h>   /* kernel memory APIs, DbgPrintEx */
#      else
#         include <ntddk.h> /* kernel memory APIs */
#      endif
#      include <stdio.h>    /* for _vsnprintf, vsprintf */
#      include <stdarg.h>   /* for va_start stuff */
#      include <stdlib.h>   /* for min macro. */
#      include "vm_basic_defs.h"
#      include "vm_assert.h"  /* Our assert macros */
#      include "kernelStubsFloorFixes.h"
#   else
#      error Type KRNL_STUBS_DRIVER_TYPE must be defined.
#   endif
#elif defined(__FreeBSD__)
#   include "vm_basic_types.h"
#   ifndef _KERNEL
#      error "_KERNEL is not defined"
#   endif
#   include <sys/types.h>
#   include <sys/malloc.h>
#   include <sys/param.h>
#   include <sys/kernel.h>
#   include <machine/stdarg.h>
#   include <sys/libkern.h>
#elif defined(__APPLE__)
#   include "vm_basic_types.h"
#   ifndef KERNEL
#      error "KERNEL is not defined"
#   endif
#   include <stdarg.h>
#   include <string.h>
# elif defined(sun)
#   include "vm_basic_types.h"
#   include <sys/types.h>
#   include <sys/varargs.h>
#endif
#include "kernelStubsSal.h"

/*
 * Function Prototypes
 */

#if defined(__linux__) || defined(__APPLE__) || defined (sun)

#  ifdef __linux__                           /* if (__linux__) { */
#  define atoi(s) simple_strtol(((s != NULL) ? s : ""), NULL, 10)
int strcasecmp(const char *s1, const char *s2);
char *strdup(const char *source);
#  endif

#  ifdef __APPLE__                           /* if (__APPLE__) { */
int atoi(const char *);
char *STRDUP(const char *, int);
#  define strdup(s) STRDUP(s, 80)
#  endif

#  if defined(__linux__) || defined(__APPLE__) /* if (__linux__ || __APPLE__) { */
#  define Str_Strcasecmp(s1, s2) strcasecmp(s1, s2)
#  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) { */

_Ret_allocates_malloc_mem_opt_bytecap_(_Size)
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTNOALIAS _CRTRESTRICT
void * __cdecl malloc(
   _In_ size_t _Size);

_Ret_allocates_malloc_mem_opt_bytecount_(_Count*_Size)
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTNOALIAS _CRTRESTRICT
void * __cdecl calloc(
   _In_ size_t _Count,
   _In_ size_t _Size);

_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTNOALIAS
void __cdecl free(
   _In_frees_malloc_mem_opt_ void * _Memory);

_Success_(return != 0)
_When_(_Memory != 0, _Ret_reallocates_malloc_mem_opt_newbytecap_oldbytecap_(_NewSize, ((uintptr_t*)_Memory)[-1]))
_When_(_Memory == 0, _Ret_reallocates_malloc_mem_opt_newbytecap_(_NewSize))
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTNOALIAS _CRTRESTRICT
void * __cdecl realloc(
   _In_reallocates_malloc_mem_opt_oldptr_ void * _Memory,
   _In_ size_t _NewSize);

_Success_(return != 0)
_Ret_allocates_malloc_mem_opt_z_
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
_CRTIMP
char * __cdecl _strdup_impl(
   _In_opt_z_ const char * _Src);

#define strdup _strdup_impl

#elif defined(__FreeBSD__)                      /* } else if (FreeBSD) { */

/* Kernel memory on FreeBSD is tagged for statistics and confidence 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                                          /* } */

_Ret_writes_z_(maxSize)
char *Str_Strcpy(
   _Out_z_cap_(maxSize) char *buf,
   _In_z_ const char *src,
   _In_ size_t maxSize);

_Ret_writes_z_(maxSize)
char *Str_Strcat(
   _Inout_z_cap_(maxSize) char *buf,
   _In_z_ const char *src,
   _In_ size_t maxSize);

_Success_(return >= 0)
int Str_Sprintf(
   _Out_z_cap_(maxSize) _Post_z_count_(return+1) char *buf,
   _In_ size_t maxSize,
   _In_z_ _Printf_format_string_ const char *fmt,
   ...) PRINTF_DECL(3, 4);

_Success_(return != -1)
int Str_Vsnprintf(
   _Out_z_cap_(size) _Post_z_count_(return+1) char *str,
   _In_ size_t size,
   _In_z_ _Printf_format_string_ const char *format,
   _In_ va_list ap) PRINTF_DECL(3, 0);

_Success_(return != 0)
_When_(length != 0, _Ret_allocates_malloc_mem_opt_z_bytecount_(*length))
_When_(length == 0, _Ret_allocates_malloc_mem_opt_z_)
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
char *Str_Vasprintf(
   _Out_opt_ size_t *length,
   _In_z_ _Printf_format_string_ const char *format,
   _In_ va_list arguments) PRINTF_DECL(2, 0);

_Success_(return != 0)
_When_(length != 0, _Ret_allocates_malloc_mem_opt_z_bytecount_(*length))
_When_(length == 0, _Ret_allocates_malloc_mem_opt_z_)
_When_windrv_(_IRQL_requires_max_(DISPATCH_LEVEL))
char *Str_Asprintf(
   _Out_opt_ size_t *length,
   _In_z_ _Printf_format_string_ const char *format,
   ...) PRINTF_DECL(2, 3);

#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 28301) // Suppress complaint that first declaration lacked annotations
#endif

// For now (vsphere-2015), we don't implement Panic, Warning, or Debug in the
// GDI case.
#if (KRNL_STUBS_DRIVER_TYPE != KRNL_STUBS_DRIVER_TYPE_GDI) &&\
    (KRNL_STUBS_DRIVER_TYPE != KRNL_STUBS_DRIVER_TYPE_NDIS)

/*
 * Stub functions we provide.
 */
#ifdef _WIN32
NORETURN
#endif
void Panic(
   _In_z_ _Printf_format_string_ const char *fmt,
   ...) PRINTF_DECL(1, 2);

void Warning(
   _In_z_ _Printf_format_string_ const char *fmt,
   ...) PRINTF_DECL(1, 2);

/*
 * Functions the driver must implement for the stubs.
 */
EXTERN void Debug(
   _In_z_ _Printf_format_string_ const char *fmt,
   ...) PRINTF_DECL(1, 2);

#endif // KRNL_STUBS_DRIVER_TYPE != KRNL_STUBS_DRIVER_TYPE_GDI

#ifdef _WIN32
#pragma warning(pop)
#endif

#endif /* __KERNELSTUBS_H__ */
 07070100000292000081A400000000000000000000000168225505000021CE000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/kernelStubsSolaris.c    /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * kernelStubsSolaris.c
 *
 * This file contains implementations of common userspace functions in terms
 * that the Solaris kernel can understand.
 */

#include "kernelStubs.h"
#include <sys/types.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

#ifdef SOL9
# define compat_va_start(arg, fmt)  arg = ((char *)__builtin_next_arg(fmt))
# define compat_va_end(arg)
# define compat_va_copy(arg1, arg2) va_copy(arg1, arg2)
#else
# define compat_va_start(arg, fmt)  va_start(arg, fmt)
# define compat_va_end(arg)         va_end(arg)
# define compat_va_copy(arg1, arg2) va_copy(arg1, arg2)
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * 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;

   compat_va_start(args, fmt);
   result = Str_Vasprintf(NULL, fmt, args);
   compat_va_end(args);

   cmn_err(CE_PANIC, "%s",
           result ? result : "Unable to format PANIC message");
}

/*
 *----------------------------------------------------------------------
 *
 * 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
{
   size_t len;

   len = strlen(src);
   if (len >= maxSize) {
      Panic("%s:%d Buffer too small %p\n", __FILE__, __LINE__, buf);
   }
   bcopy(src, buf, len + 1);
   return buf;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Vsnprintf --
 *
 * Compatability wrapper b/w different libc versions
 *
 * Results:
 *    int - number of bytes written (not including NULL terminator),
 *          -1 on overflow (insufficient space for NULL terminator
 *          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 = 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;
      }

      compat_va_copy(args2, arguments);
      retval = Str_Vsnprintf(buf, bufSize, format, args2);
      compat_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;

   compat_va_start(arguments, format);
   result = Str_Vasprintf(length, format, arguments);
   compat_va_end(arguments);

   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * 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 = kmem_alloc(size + sizeof(size), KM_SLEEP);

   if (ptr) {
      *ptr++ = size;
   }
   return ptr;
}


/*
 *---------------------------------------------------------------------------
 *
 * free --
 *
 *     Free memory allocated by a previous call to malloc, calloc or realloc.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     Calls kmem_free to free the real (base) pointer.
 *
 *---------------------------------------------------------------------------
 */

void
free(void *mem) // IN
{
   size_t *dataPtr = mem;

   if (mem) {
      --dataPtr;
      kmem_free(dataPtr, *dataPtr + sizeof(*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;
}


  07070100000293000081A40000000000000000000000016822550500001E27000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/module.c    /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * module.c --
 *
 *      Structures and modules for loading and unloading of the HGFS module.
 *
 */


/*
 * For reference: Solaris Structures
 * =================================
 *
 * VFS module struct hierarchy:
 * ----------------------------
 *
 * (Solaris 9)
 *
 * modlinkage --> modlfs --> mod_ops
 *                       \
 *                        -> vfssw --> vfsops
 *                                 \
 *                                  -> (*fs_init_routine)()
 *
 *
 * modlfs: - points to module loading/unloading operations structure (mod_ops)
 *         - points to VFS Switch strucutre (vfssw)
 *         - contains extended name of filesystem
 *
 * mod_ops: - contains pointers to _init(), _fini(), and _info(), which handle
 *            loading/unloading module into kernel and providing information
 *
 * vfssw: - points to filesystem initialization routine that is called once at
 *          module load time (not mount time)
 *        - points to vfsops struct that points to fs-specific operations
 *        - contains name of fs (what you would put in /etc/vfstab)
 *        - also contains fs mount options, flags, and mutex
 *
 * vfsops: - points to fs-level functions (mount(), umount(), etc.)
 *
 * (Solaris 10)
 *
 * modlinkage --> modlfs --> mod_ops
 *                       \
 *                        -> vfsdef_v2 --> (*init)()
 *
 * vfsdef_v2:  - this contains a pointer to an initialization routine for the
 *               filesystem that takes different arguments
 *             - we no longer provide an address to a vfsops; now we need to
 *               call vfs_makevfsops() with a preconstructed array of
 *               fs_operation_t that defines each vfs op.  This needs to occur
 *               in the initialization routine.
 *
 * (Build 58 contains the same structure named vfsdev_v3)
 *
 */

#include "hgfsSolaris.h"
#include "module.h"
#include "vnode.h"
#include "filesystem.h"
#include "debug.h"


/*
 * Macros
 */
#define HGFS_VFSSW_FLAGS        0


/*
 * Filesystem structures
 */

#if HGFS_VFS_VERSION == 2
/* VFS Operations Structure */
struct vfsops HgfsVfsOps = {
   HgfsMount,           /* vfs_mount() */
   HgfsUnmount,         /* vfs_unmount() */
   HgfsRoot,            /* vfs_root() */
   HgfsStatvfs,         /* vfs_statvfs() */
   HgfsSync,            /* vfs_sync() */
   HgfsVget,            /* vfs_vget() */
   HgfsMountroot,       /* vfs_mountroot() */
   HgfsReserved,        /* vfs_reserved() */
   HgfsFreevfs          /* vfs_freevfs() */
};


/* VFS Switch structure */
static struct vfssw HgfsVfsSw = {
   HGFS_FS_NAME,        /* Name of filesystem */
   HgfsInit,            /* Initialization routine */
   &HgfsVfsOps,         /* VFS Operations struct */
   HGFS_VFSSW_FLAGS,    /* Flags: see <sys/vfs.h> */
   NULL,                /* Mount options table prototype */
   1,                   /* Count of references */
   { { 0 } }            /* Lock to protect count */
};
#else

/*
 * Different beta builds of Solaris have different versions of this structure.
 * We currently don't support v4 which was out in intermediate beta builds of
 * Solaris 11. Instead we choose to update to the latest revision. But if needed,
 * v4 could be added without significant effort.
 */
#if HGFS_VFS_VERSION == 2
static struct vfsdef_v2 HgfsVfsDef = {
#elif HGFS_VFS_VERSION == 3
static struct vfsdef_v3 HgfsVfsDef = {
#else
static struct vfsdef_v5 HgfsVfsDef = {
#endif
   VFSDEF_VERSION,      /* Structure version: defined in <sys/vfs.h> */
   HGFS_FS_NAME,        /* Name of filesystem */
   HgfsInit,            /* Initialization routine: note this is a different
                         * routine than the one used in 9 */
   HGFS_VFSSW_FLAGS,    /* Filesystem flags */
   NULL                 /* No mount options */
};

#endif  /* Solaris version */


/* Filesystem module structure */
static struct modlfs HgfsModlfs = {
   &mod_fsops,                  /* Module operation structure: for
                                   auto loading/unloading */
   "Host/Guest Filesystem",     /* Name */
#if HGFS_VFS_VERSION== 2
   &HgfsVfsSw                   /* VFS Switch structure */
#else
   &HgfsVfsDef                  /* Filesystem type definition record */
#endif
};


/*
 * Modlinkage containing filesystem.
 */
static struct modlinkage HgfsModlinkage = {
   MODREV_1,            /* Module revision: must be MODREV_1 */
   {
      &HgfsModlfs,      /* FS module structure */
      NULL,             /* NULL terminator */
   }
};


/*
 * Driver autoload functions
 */


/*
 *----------------------------------------------------------------------------
 *
 * _init --
 *    Invoked when module is being loaded into kernel, and is called before
 *    any function in the module.  Any state that spans all instances of the
 *    driver should be allocated and initialized here.
 *
 * Results:
 *    Returns the result of mod_install(9F), which is zero on success and a
 *    non-zero value on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
_init(void)
{
   int ret;

   DEBUG(VM_DEBUG_ENTRY, "_init() for HGFS.\n");

   ret = mod_install(&HgfsModlinkage);
   if (ret) {
      cmn_err(HGFS_ERROR, "could not install HGFS module.\n");
      return ret;
   }

   DEBUG(VM_DEBUG_DONE, "_init() done.\n");
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * _fini --
 *    Invoked when a module is being removed from the kernel.
 *
 * Results:
 *    Returns the result of mod_remove(9F), which is zero on success, and a
 *    non-zero value on failure.
 *
 * Side effects:
 *    The module will be removed from the kernel.
 *
 *----------------------------------------------------------------------------
 */

int
_fini(void)
{
   int error;

   DEBUG(VM_DEBUG_ENTRY, "_fini() for HGFS.\n");

   /*
    * Make sure that the fs is not mounted.
    */
   if (HgfsGetSuperInfo()) {
      DEBUG(VM_DEBUG_FAIL,
            "Cannot unload module because file system is mounted\n");
      return EBUSY;
   }

   error = mod_remove(&HgfsModlinkage);
   if (error) {
      cmn_err(HGFS_ERROR, "could not remove HGFS module.\n");
      return error;
   }

   HgfsFreeVnodeOps();
   HgfsFreeVfsOps();

   DEBUG(VM_DEBUG_DONE, "_fini() done.\n");
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * _info --
 *
 *    Invoked when the modinfo(1M) command is executed.  mod_info(9F) handles
 *    this for us.
 *
 * Results:
 *    Returns mod_info(9F)'s results, which are a non-zero value on success, and
 *    zero on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
_info(struct modinfo *modinfop) // OUT: Filled in with module's information by
{
   DEBUG(VM_DEBUG_ENTRY, "_info().\n");
   ASSERT(modinfop);

   if (!modinfop) {
        cmn_err(HGFS_ERROR, "NULL input in _info\n");
        return EINVAL;
   }

   return mod_info(&HgfsModlinkage, modinfop);
}
 07070100000294000081A40000000000000000000000016822550500000719000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/module.h    /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * module.h --
 *
 *      EXTERNs needed by structures in module.c
 */

#ifndef __MODULE_H_
#define __MODULE_H_

#include "filesystem.h"

/*
 * Filesystem EXTERNs
 */

/* Filesystem initialization routine (see filesystem.c) */
#if HGFS_VFS_VERSION == 2
EXTERN int HgfsInit(struct vfssw *vfsswp, int fstype);
#else
EXTERN int HgfsInit(int, char *);
#endif

/* Functions for the vfsops structure (see filesystem.c) */
EXTERN int HgfsMount(struct vfs *vfsp, struct vnode *vnodep,
                 struct mounta *mntp, struct cred *credp);
EXTERN int HgfsUnmount(struct vfs *vfsp, int mflag, struct cred *credp);
EXTERN int HgfsRoot(struct vfs *vfsp, struct vnode **vnodepp);
EXTERN int HgfsStatvfs(struct vfs *vfsp, struct statvfs64 *stats);
EXTERN int HgfsSync(struct vfs* vfsp, short flags, struct cred *credp);
EXTERN int HgfsVget(struct vfs *vfsp, struct vnode **vnodepp, struct fid *fidp);
EXTERN int HgfsMountroot(struct vfs *vfsp, enum whymountroot reason);
EXTERN int HgfsReserved(struct vfs *vfsp, struct vnode **vnodepp, char * /* ?? */);
EXTERN void HgfsFreevfs(struct vfs *vfsp);

/* Struct needed in struct modlfs */
EXTERN struct mod_ops mod_fsops;


#endif /* __MODULE_H_ */
   07070100000295000081A40000000000000000000000016822550500002727000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/request.c   /*********************************************************
 * Copyright (C) 2004-2016, 2021 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * request.c --
 *
 * Implementation of routines used to initialize, allocate, and move requests
 * between lists.
 *
 */

/*
 * Includes
 */
#include "hgfsSolaris.h"
#include "request.h"


/*
 *----------------------------------------------------------------------------
 *
 *  HgfsInitRequestList --
 *
 *   Initializes the request list related members of the HgfsSuperInfo for
 *   this instance of the driver state.
 *
 * Results:
 *   The pending request list, free request list, and associated
 *   synchronization primitives are initialized.
 *
 * Side effects:
 *   Each request is now in the free list and is set to UNUSED.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsInitRequestList(HgfsSuperInfo *sip) // IN: Pointer to superinfo structure
{
   int i;

   DEBUG(VM_DEBUG_REQUEST, "HgfsInitRequestList().\n");

   ASSERT(sip);

   mutex_init(&sip->reqMutex, NULL, MUTEX_DRIVER, NULL);

   /* Initialize free request list */
   DblLnkLst_Init(&sip->reqFreeList);
   mutex_init(&sip->reqFreeMutex, NULL, MUTEX_DRIVER, NULL);
   cv_init(&sip->reqFreeCondVar, NULL, CV_DRIVER, NULL);

   /*
    * Initialize pool of requests
    *
    * Here we are setting each request's id to its index into the requestPool
    * so this can be used as an identifier in reply packets.  Each request's
    * state is also set to UNUSED and is added to the free list.
    */
   for (i = 0; i < ARRAYSIZE(requestPool); i++) {
      requestPool[i].id = i;
      requestPool[i].state = HGFS_REQ_UNUSED;

      DblLnkLst_Init(&requestPool[i].listNode);
      DblLnkLst_LinkLast(&sip->reqFreeList, &requestPool[i].listNode);
   }

   //HgfsDebugPrintReqList(&sip->reqFreeList);
   DEBUG(VM_DEBUG_REQUEST, "HgfsInitRequestList() done.\n");
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsCancelAllRequests  --
 *
 *    Cancels all pending (SUBMITTED) requests by signalling the transport
 *    code that they should be forcibly ended.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Threads waiting on requests are woken up with error conditions.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsCancelAllRequests(HgfsSuperInfo *sip)       // IN: Superinfo containing
                                                //     request list
{
   int i;

   DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests().\n");

   ASSERT(sip);
   ASSERT(mutex_owned(&sip->reqMutex));

   for (i = 0; i < ARRAYSIZE(requestPool); i++) {
      /*
       * Signal that all submitted requests need to be cancelled.
       * We expect that transport implementation wakes up processes
       * waiting on requests
       */
      if (requestPool[i].state == HGFS_REQ_SUBMITTED)
         sip->cancelRequest(&requestPool[i]);
   }

   DEBUG(VM_DEBUG_REQUEST, "HgfsCancelAllRequests() done.\n");
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsListIsEmpty --
 *
 *    Determines whether the provided list is empty.
 *
 *    Note: this assumes it is called with the list lock held because often
 *    callers will need this function to be atomic with other operations.
 *
 * Results:
 *    Returns zero if list is not empty, a positive integer if it is empty.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

INLINE Bool
HgfsListIsEmpty(DblLnkLst_Links *listAnchor)    // IN: Anchor of list to check
{
   ASSERT(listAnchor);

   return (listAnchor == listAnchor->next);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetNewReq --
 *
 *    Allocates and initializes a new request structure from the request pool.
 *    This function blocks until a request is available or it has been
 *    interrupted by a signal.
 *
 * Results:
 *    Returns pointer to allocated HgfsReq on success, and NULL if interrupted
 *    while waiting to be allocated a request structure.
 *
 * Side effects:
 *    Request's state is set to HGFS_REQ_ALLOCATED.
 *    Request is removed from the free list.
 *
 *----------------------------------------------------------------------------
 */

HgfsReq *
HgfsGetNewReq(HgfsSuperInfo *sip)       // IN: Superinfo containing free list
{
   HgfsReq *newReq;

   DEBUG(VM_DEBUG_REQUEST, "HgfsGetNewReq().\n");

   ASSERT(sip);

   /*
    * Here we atomically get the next free request from the free list and set
    * that request's state to ALLOCATED.
    */
   mutex_enter(&sip->reqFreeMutex);

   /* Wait for a request structure if there aren't any free */
   while (HgfsListIsEmpty(&sip->reqFreeList)) {
      /*
       * If the list is empty, we wait on the condition variable which is
       * unconditionally signaled whenever a request is destroyed.
       */
      if (cv_wait_sig(&sip->reqFreeCondVar, &sip->reqFreeMutex) == 0) {
         /*
          * We were interrupted while waiting for a request, so we must return
          * NULL and release the mutex.
          */
         newReq = NULL;
         goto out;
      }
   }

   newReq = HGFS_FREE_REQ_LIST_HEAD(sip);

   HgfsDebugPrintReq("HgfsGetNewReq", newReq);

   /* Failure of these indicates error in program's logic */
   ASSERT(newReq && newReq->state == HGFS_REQ_UNUSED);

   /* Take request off the free list and indicate it has been ALLOCATED */
   DblLnkLst_Unlink1(&newReq->listNode);
   newReq->state = HGFS_REQ_ALLOCATED;

   /* Clear packet of request before allocating to clients. */
   bzero(newReq->packet, sizeof newReq->packet);

   DEBUG(VM_DEBUG_LIST, "Dequeued from free list: %s", newReq->packet);
   HgfsDebugPrintReqList(&sip->reqFreeList);

out:
   mutex_exit(&sip->reqFreeMutex);

   DEBUG(VM_DEBUG_REQUEST, "HgfsGetNewReq() done.\n");
   return newReq;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDestroyReq --
 *
 *    Deallocates a request structure.
 *
 * Results:
 *    Returns void.
 *
 * Side effects:
 *    Request's state is set to HGFS_REQ_UNUSED.
 *    Request is placed on the free list.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsDestroyReq(HgfsSuperInfo *sip,      // IN: Superinfo containing free list
               HgfsReq *oldReq)         // IN: Request to destroy
{
   DEBUG(VM_DEBUG_ENTRY, "HgfsDestroyReq().\n");

   /* XXX This should go away later, just for testing */
   if (oldReq->state != HGFS_REQ_COMPLETED) {
      DEBUG(VM_DEBUG_ALWAYS, "HgfsDestroyReq() (oldReq state=%d).\n",
            oldReq->state);
   }

   ASSERT(sip);
   ASSERT(oldReq);
   /* Failure of this check indicates an error in program logic */
   ASSERT(oldReq->state == HGFS_REQ_COMPLETED ||
          oldReq->state == HGFS_REQ_ABANDONED ||
          oldReq->state == HGFS_REQ_ERROR);

   /*
    * To make the request available for other clients we change its state to
    * UNUSED and place it back on the free list.
    */
   mutex_enter(&sip->reqFreeMutex);

   oldReq->state = HGFS_REQ_UNUSED;
   DblLnkLst_LinkLast(&sip->reqFreeList, &oldReq->listNode);
   /* Wake up clients waiting for a request structure */
   cv_signal(&sip->reqFreeCondVar);

   mutex_exit(&sip->reqFreeMutex);

   HgfsDebugPrintReqList(&sip->reqFreeList);

   DEBUG(VM_DEBUG_REQUEST, "HgfsDestroyReq() done.\n");
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSendRequest --
 *
 *    Sends request for execution. The exact details depend on transport used
 *    to communicate with the host.
 *
 *    Note: this assumes it is called with the list lock held because often
 *    callers will need this function to be atomic with other operations.
 *
 * Results:
 *    Returns void.
 *
 * Side effects:
 *    Request's state is set to HGFS_REQ_SUBMITTED.
 *    Request is added to the pending request list.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsSendRequest(HgfsSuperInfo *sip,    // IN: Superinfo sructure with methods
                HgfsReq *req)          // IN/OUT: Request to be sent
{
   int ret;

   ASSERT(sip);
   ASSERT(req);
   /* Failure of this check indicates error in program logic */
   ASSERT(req->state == HGFS_REQ_ALLOCATED);

   req->state = HGFS_REQ_SUBMITTED;
   ret = sip->sendRequest(req);
   if (ret) {
      req->state = HGFS_REQ_ERROR;
   }

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsWakeWaitingClient --
 *
 *    Wakes up the client waiting on the specified request.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

INLINE void
HgfsWakeWaitingClient(HgfsSuperInfo *sip,       // IN: Superinfo with request mutex
                      HgfsReq *req)             // IN: Request to wake client for
{
//   DEBUG(VM_DEBUG_REQUEST, "HgfsWakeWaitingClient().\n");

   ASSERT(sip);
   ASSERT(req);

   /*
    * We need to acquire the mutex before signaling the request's condition
    * variable since it was acquired before sleeping in HgfsSubmitRequest().
    */
   mutex_enter(&sip->reqMutex);

   cv_signal(&req->condVar);

   mutex_exit(&sip->reqMutex);

//   DEBUG(VM_DEBUG_REQUEST, "HgfsWakeWaitingClient() done.\n");
}

 07070100000296000081A40000000000000000000000016822550500000780000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/request.h   /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * request.h --
 *
 * Prototypes for request initialization, allocation, and list manipulation
 * functions.
 *
 */

#ifndef __REQUEST_H_
#define __REQUEST_H_


#include <sys/ksynch.h> /* mutexes and condition variables */

#include "hgfsSolaris.h"
#include "debug.h"

#include "dbllnklst.h"  /* Double link list types */

/*
 * Macros
 */

/* Since the DblLnkLst_Links in the superinfo is just an anchor, we want to
 * skip it (e.g., get the container for the next element) */
#define HGFS_REQ_LIST_HEAD(si)           \
            (DblLnkLst_Container(si->reqList.next, HgfsReq, listNode))
#define HGFS_REQ_LIST_HEAD_NODE(si)      \
            (si->reqList.next)
#define HGFS_FREE_REQ_LIST_HEAD(si)      \
            (DblLnkLst_Container(si->reqFreeList.next, HgfsReq, listNode))
#define HGFS_FREE_REQ_LIST_HEAD_NODE(si) \
            (si->reqFreeList.next)


/*
 * Functions
 */
void HgfsInitRequestList(HgfsSuperInfo *sip);
void HgfsCancelAllRequests(HgfsSuperInfo *sip);
INLINE Bool HgfsListIsEmpty(DblLnkLst_Links *listAnchor);
HgfsReq *HgfsGetNewReq(HgfsSuperInfo *sip);
void HgfsDestroyReq(HgfsSuperInfo *sip, HgfsReq *oldReq);
int HgfsSendRequest(HgfsSuperInfo *sip, HgfsReq *req);
INLINE void HgfsWakeWaitingClient(HgfsSuperInfo *sip, HgfsReq *req);

#endif /* __REQUEST_H_ */
07070100000297000081A4000000000000000000000001682255050002C0F3000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/vnode.c /*********************************************************
 * Copyright (c) 2004-2016, 2023 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/


/*
 * vnode.c --
 *
 * Implementaiton of entry points for operations on files (vnodes) and
 * definition of the vnodeops structure.
 */

#include <sys/types.h>
#include <sys/dirent.h>
#include <sys/sysmacros.h>
#include <sys/file.h>           /* FREAD, FWRITE, etc flags */
#include <sys/fs_subr.h>        /* fs_fab_acl */
#include <sys/stat.h>           /* S_IRWXU and friends */
#include <sys/dnlc.h>           /* Directory name lookup cache */
#include <sys/signal.h>         /* k_sigset_t and signal macros */
#include <sys/proc.h>           /* practive (the active process) */
#include <sys/user.h>           /* u macro for current user area */
#include <sys/thread.h>         /* curthread and curproc macros */
#include <vm/seg_vn.h>          /* segment vnode mapping for mmap() */
#include <sys/vmsystm.h>        /* VM address mapping functions */

#include "hgfsSolaris.h"
#include "hgfsState.h"
#include "hgfsBdGlue.h"
#include "vnode.h"
#include "filesystem.h"
#include "request.h"
#include "debug.h"

#include "hgfsEscape.h"         /* Escaping/unescaping buffers */
#include "cpName.h"             /* Cross-platform name conversion */
#include "hgfsUtil.h"           /* Cross-platform time conversion */
#include "sha1.h"               /* SHA-1 for Node ID calculation */

/*
 * Macros
 */
#define HGFS_ATTR_MODE_SHIFT    6

/* Sets the values of request headers properly */
#define HGFS_INIT_REQUEST_HDR(request, req, _op)        \
         do {                                           \
            request->header.id = req->id;               \
            request->header.op = _op;                   \
         } while(0)

/* Solaris times support nsecs, so only use these functions directly */
#define HGFS_SET_TIME(unixtm, nttime)                   \
         HgfsConvertFromNtTimeNsec(&unixtm, nttime)
#define HGFS_GET_TIME(unixtm)                           \
         HgfsConvertTimeSpecToNtTime(&unixtm)

/* Determine if this is the root vnode. */
#define HGFS_IS_ROOT_VNODE(sip, vp)                     \
                (sip->rootVnode == vp)
//             ((vp->v_flag & VROOT) && (vp->v_vfsp == sip->vfsp))


/*
 * Prototypes
 */

/* Local vnode functions */
static int HgfsDirOpen(HgfsSuperInfo *sip, struct vnode *vp);
static int HgfsFileOpen(HgfsSuperInfo *sip, struct vnode *vp,
                        int flag, int permissions);
static int HgfsDirClose(HgfsSuperInfo *sip, struct vnode *vp);
static int HgfsFileClose(HgfsSuperInfo *sip, struct vnode *vp);
static int HgfsGetNextDirEntry(HgfsSuperInfo *sip, HgfsHandle handle,
                               uint32_t offset, char *nameOut, Bool *done);
static int HgfsDoRead(HgfsSuperInfo *sip, HgfsHandle handle, uint64_t offset,
                      uint32_t size, uio_t *uiop, uint32_t *count);
static int HgfsDoWrite(HgfsSuperInfo *sip, HgfsHandle handle, int ioflag,
                       uint64_t offset, uint32_t size, uio_t *uiop,
                       uint32_t *count);
static int HgfsDelete(HgfsSuperInfo *sip, char *filename, HgfsOp op);

static int HgfsSubmitRequest(HgfsSuperInfo *sip, HgfsReq *req);
static int HgfsValidateReply(HgfsReq *req, uint32_t minSize);
static int HgfsStatusConvertToSolaris(HgfsStatus hgfsStatus);
static void HgfsAttrToSolaris(struct vnode *vp, const HgfsAttr *hgfsAttr,
                              struct vattr *solAttr);
static Bool HgfsSetattrCopy(struct vattr *solAttr, int flags,
                            HgfsAttr *hgfsAttr, HgfsAttrChanges *update);
static int HgfsMakeFullName(const char *path, uint32_t pathLen, const char *file,
                            char *outBuf, ssize_t bufSize);
static int HgfsGetOpenMode(uint32 flags);
static int HgfsGetOpenFlags(uint32 flags);
INLINE static void HgfsDisableSignals(k_sigset_t *oldIgnoreSet);
INLINE static void HgfsRestoreSignals(k_sigset_t *oldIgnoreSet);


/* vnode Operation prototypes */
#if HGFS_VFS_VERSION <= 3
static int HgfsOpen(struct vnode **vpp, int flag, struct cred *cr);
static int HgfsClose(struct vnode *vp, int flag, int count, offset_t offset,
                     struct cred *cr);
#else
static int HgfsOpen(struct vnode **vpp, int flag, struct cred *cr, caller_context_t *ctx);
static int HgfsClose(struct vnode *vp, int flag, int count, offset_t offset,
                     struct cred *cr, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION == 2
static int HgfsRead(struct vnode *vp, struct uio *uiop, int ioflag,
                    struct cred *cr);
static int HgfsWrite(struct vnode *vp, struct uio *uiop, int ioflag,
                     struct cred *cr);
#else
static int HgfsRead(struct vnode *vp, struct uio *uiop, int ioflag,
                    struct cred *cr, caller_context_t *ctx);
static int HgfsWrite(struct vnode *vp, struct uio *uiop, int ioflag,
                     struct cred *cr, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION <= 3
static int HgfsIoctl(struct vnode *vp, int cmd, intptr_t arg, int flag,
                     struct cred *cr, int *rvalp);
static int HgfsGetattr(struct vnode *vp, struct vattr *vap, int flags,
                       struct cred *cr);
#else
static int HgfsIoctl(struct vnode *vp, int cmd, intptr_t arg, int flag,
                     struct cred *cr, int *rvalp, caller_context_t *ctx);
static int HgfsGetattr(struct vnode *vp, struct vattr *vap, int flags,
                       struct cred *cr, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION == 2
static int HgfsSetattr(struct vnode *vp, struct vattr *vap, int flags,
                       struct cred *cr);
#else
static int HgfsSetattr(struct vnode *vp, struct vattr *vap, int flags,
                       struct cred *cr, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION <= 3
static int HgfsAccess(struct vnode *vp, int mode, int flags,
                      struct cred *cr);
static int HgfsLookup(struct vnode *dvp, char *nm, struct vnode **vpp,
                      struct pathname *pnp, int flags,
                      struct vnode *rdir, struct cred *cr);
static int HgfsCreate(struct vnode *dvp, char *name, struct vattr *vap,
                      vcexcl_t excl, int mode, struct vnode **vpp,
                      struct cred *cr, int flag);
static int HgfsRemove(struct vnode *vp, char *nm, struct cred *cr);
static int HgfsLink(struct vnode *tdvp, struct vnode *svp, char *tnm,
                    struct cred *cr);
static int HgfsRename(struct vnode *sdvp, char *snm, struct vnode *tdvp,
                      char *tnm, struct cred *cr);
static int HgfsMkdir(struct vnode *dvp, char *dirname, struct vattr *vap,
                     struct vnode **vpp, struct cred *cr);
static int HgfsRmdir(struct vnode *vp, char *nm, struct vnode *cdir,
                     struct cred *cr);
static int HgfsReaddir(struct vnode *vp, struct uio *uiop, struct cred *cr,
                       int *eofp);
static int HgfsSymlink(struct vnode *dvp, char *linkname, struct vattr *vap,
                       char *target, struct cred *cr);
static int HgfsReadlink(struct vnode *vp, struct uio *uiop, struct cred *cr);
static int HgfsFsync(struct vnode *vp, int syncflag, struct cred *cr);
static void HgfsInactive(struct vnode *vp, struct cred *cr);
static int HgfsFid(struct vnode *vp, struct fid *fidp);
#else
static int HgfsAccess(struct vnode *vp, int mode, int flags,
                      struct cred *cr, caller_context_t *ctx);
static int HgfsLookup(struct vnode *dvp, char *nm, struct vnode **vpp,
                      struct pathname *pnp, int flags,
                      struct vnode *rdir, struct cred *cr, caller_context_t *ctx,
                      int *direntflags, pathname_t *realpnp);
static int HgfsCreate(struct vnode *dvp, char *name, struct vattr *vap,
                      vcexcl_t excl, int mode, struct vnode **vpp,
                      struct cred *cr, int flag, caller_context_t *ctx,
                      vsecattr_t *vsecp);
static int HgfsRemove(struct vnode *vp, char *nm, struct cred *cr,
                      caller_context_t *ctx, int flags);
static int HgfsLink(struct vnode *tdvp, struct vnode *svp, char *tnm,
                    struct cred *cr, caller_context_t *ctx, int flags);
static int HgfsRename(struct vnode *sdvp, char *snm, struct vnode *tdvp,
                      char *tnm, struct cred *cr, caller_context_t *ctx, int flags);
static int HgfsMkdir(struct vnode *dvp, char *dirname, struct vattr *vap,
                     struct vnode **vpp, struct cred *cr, caller_context_t *ctx,
                     int flags, vsecattr_t *vsecp);
static int HgfsRmdir(struct vnode *vp, char *nm, struct vnode *cdir,
                     struct cred *cr, caller_context_t *ctx, int flags);
static int HgfsReaddir(struct vnode *vp, struct uio *uiop, struct cred *cr,
                       int *eofp, caller_context_t *ctx, int flags);
static int HgfsSymlink(struct vnode *dvp, char *linkname, struct vattr *vap,
                       char *target, struct cred *cr, caller_context_t *ctx,
                       int flags);
static int HgfsReadlink(struct vnode *vp, struct uio *uiop, struct cred *cr,
                        caller_context_t *ctx);
static int HgfsFsync(struct vnode *vp, int syncflag, struct cred *cr,
                     caller_context_t *ctx);
static void HgfsInactive(struct vnode *vp, struct cred *cr, caller_context_t *ctx);
static int HgfsFid(struct vnode *vp, struct fid *fidp, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION == 2
static void HgfsRwlock(struct vnode *vp, int write_lock);
#elif HGFS_VFS_VERSION == 3
static void HgfsRwlock(struct vnode *vp, int write_lock, caller_context_t *ctx);
#else
static int HgfsRwlock(struct vnode *vp, int write_lock, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION == 2
static void HgfsRwunlock(struct vnode *vp, int write_lock);
#else
static void HgfsRwunlock(struct vnode *vp, int write_lock, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION <= 3
static int HgfsSeek(struct vnode *vp, offset_t ooff, offset_t *noffp);
static int HgfsCmp(struct vnode *vp1, struct vnode *vp2);
static int HgfsFrlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
                      offset_t offset, struct flk_callback *, struct cred *cr);
#else
static int HgfsSeek(struct vnode *vp, offset_t ooff, offset_t *noffp,
                    caller_context_t *ctx);
static int HgfsCmp(struct vnode *vp1, struct vnode *vp2, caller_context_t *ctx);
static int HgfsFrlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
                      offset_t offset, struct flk_callback *, struct cred *cr,
                      caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION == 2
static int HgfsSpace(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
                     offset_t offset, struct cred *cr);
#else
static int HgfsSpace(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
                     offset_t offset, struct cred *cr, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION <= 3
static int HgfsRealvp(struct vnode *vp, struct vnode **vpp);
static int HgfsGetpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp,
                       struct page **plarr, size_t plsz, struct seg *seg,
                       caddr_t addr, enum seg_rw rw, struct cred *cr);
static int HgfsPutpage(struct vnode *vp, offset_t off, size_t len, int flags,
                       struct cred *cr);
static int HgfsMap(struct vnode *vp, offset_t off, struct as *as,
                   caddr_t *addrp, size_t len, uchar_t prot,
                   uchar_t maxprot, uint_t flags, struct cred *cr);
static int HgfsAddmap(struct vnode *vp, offset_t off, struct as *as,
                      caddr_t addr, size_t len, uchar_t prot,
                      uchar_t maxprot, uint_t flags, struct cred *cr);
static int HgfsDelmap(struct vnode *vp, offset_t off, struct as *as,
                      caddr_t addr, size_t len, uint_t prot,
                      uint_t maxprot, uint_t flags, struct cred *cr);
static int HgfsDump(struct vnode *vp, caddr_t addr, int lbdn, int dblks);
static int HgfsPathconf(struct vnode *vp, int cmd, ulong_t *valp,
                        struct cred *cr);
static int HgfsPageio(struct vnode *vp, struct page *pp, u_offset_t io_off,
                      size_t io_len, int flags, struct cred *cr);
static int HgfsDumpctl(struct vnode *vp, int action, int *blkp);
static void HgfsDispose(struct vnode *vp, struct page *pp, int flag, int dn,
                        struct cred *cr);
static int HgfsSetsecattr(struct vnode *vp, vsecattr_t *vsap, int flag,
                          struct cred *cr);
#else
static int HgfsRealvp(struct vnode *vp, struct vnode **vpp, caller_context_t *ctx);
static int HgfsGetpage(struct vnode *vp, offset_t off, size_t len, uint_t *protp,
                       struct page **plarr, size_t plsz, struct seg *seg,
                       caddr_t addr, enum seg_rw rw, struct cred *cr,
                       caller_context_t *ctx);
static int HgfsPutpage(struct vnode *vp, offset_t off, size_t len, int flags,
                       struct cred *cr, caller_context_t *ctx);
static int HgfsMap(struct vnode *vp, offset_t off, struct as *as,
                   caddr_t *addrp, size_t len, uchar_t prot,
                   uchar_t maxprot, uint_t flags, struct cred *cr, caller_context_t *ctx);
static int HgfsAddmap(struct vnode *vp, offset_t off, struct as *as,
                      caddr_t addr, size_t len, uchar_t prot,
                      uchar_t maxprot, uint_t flags, struct cred *cr,
                      caller_context_t *ctx);
static int HgfsDelmap(struct vnode *vp, offset_t off, struct as *as,
                      caddr_t addr, size_t len, uint_t prot,
                      uint_t maxprot, uint_t flags, struct cred *cr,
                      caller_context_t *ctx);
static int HgfsDump(struct vnode *vp, caddr_t addr, offset_t lbdn, offset_t dblks,
                    caller_context_t *ctx);
static int HgfsPathconf(struct vnode *vp, int cmd, ulong_t *valp,
                        struct cred *cr, caller_context_t *ctx);
static int HgfsPageio(struct vnode *vp, struct page *pp, u_offset_t io_off,
                      size_t io_len, int flags, struct cred *cr, caller_context_t *ctx);
static int HgfsDumpctl(struct vnode *vp, int action, offset_t *blkp,
                       caller_context_t *ctx);
static void HgfsDispose(struct vnode *vp, struct page *pp, int flag, int dn,
                        struct cred *cr, caller_context_t *ctx);
static int HgfsSetsecattr(struct vnode *vp, vsecattr_t *vsap, int flag,
                          struct cred *cr, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION == 2
static int HgfsShrlock(struct vnode *vp, int cmd, struct shrlock *chr,
                       int flag);
#elif HGFS_VFS_VERSION == 3
static int HgfsShrlock(struct vnode *vp, int cmd, struct shrlock *chr,
                       int flag, cred_t *cr);
#else /* SOL11 */
static int HgfsShrlock(struct vnode *vp, int cmd, struct shrlock *chr,
                       int flag, cred_t *cr, caller_context_t *ctx);
#endif

#if HGFS_VFS_VERSION == 3
static int HgfsVnevent(struct vnode *vp, vnevent_t event);
#elif HGFS_VFS_VERSION == 5
static int HgfsVnevent(struct vnode *vp, vnevent_t event, vnode_t *dvp,
                       char *fnm, caller_context_t *ctx);
#endif


#if HGFS_VFS_VERSION == 2
/* vnode Operations Structure */
struct vnodeops hgfsVnodeOps = {
   HgfsOpen,            /* vop_open() */
   HgfsClose,           /* vop_close() */
   HgfsRead,            /* vop_read() */
   HgfsWrite,           /* vop_write() */
   HgfsIoctl,           /* vop_ioctl() */
   fs_setfl,            /* vop_setfl() */
   HgfsGetattr,         /* vop_getattr() */
   HgfsSetattr,         /* vop_setattr() */
   HgfsAccess,          /* vop_access() */
   HgfsLookup,          /* vop_lookup() */
   HgfsCreate,          /* vop_create() */
   HgfsRemove,          /* vop_remove() */
   HgfsLink,            /* vop_link() */
   HgfsRename,          /* vop_rename() */
   HgfsMkdir,           /* vop_mkdir() */
   HgfsRmdir,           /* vop_rmdir() */
   HgfsReaddir,         /* vop_readdir() */
   HgfsSymlink,         /* vop_symlink() */
   HgfsReadlink,        /* vop_readlink() */
   HgfsFsync,           /* vop_fsync() */
   HgfsInactive,        /* vop_inactive() */
   HgfsFid,             /* vop_fid() */
   HgfsRwlock,          /* vop_rwlock() */
   HgfsRwunlock,        /* vop_rwunlock() */
   HgfsSeek,            /* vop_seek() */
   HgfsCmp,             /* vop_cmp() */
   HgfsFrlock,          /* vop_frlock() */
   HgfsSpace,           /* vop_space() */
   HgfsRealvp,          /* vop_realvp() */
   HgfsGetpage,         /* vop_getpage() */
   HgfsPutpage,         /* vop_putpage() */
   HgfsMap,             /* vop_map() */
   HgfsAddmap,          /* vop_addmap() */
   HgfsDelmap,          /* vop_delmap() */
   fs_poll,             /* vop_poll() */
   HgfsDump,            /* vop_dump() */
   HgfsPathconf,        /* vop_pathconf() */
   HgfsPageio,          /* vop_pageio() */
   HgfsDumpctl,         /* vop_dumpctl() */
   HgfsDispose,         /* vop_dispose() */
   HgfsSetsecattr,      /* vop_setsecattr() */
   fs_fab_acl,          /* vop_getsecattr() */
   HgfsShrlock          /* vop_shrlock() */
};

#else

/* Will be set up during HGFS initialization (see HgfsInit) */
static struct vnodeops *hgfsVnodeOpsP;

#endif

static HgfsSuperInfo hgfsSuperInfo;


/*
 * Vnode Entry Points
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsOpen --
 *
 *    Invoked when open(2) is called on a file in our filesystem.  Sends an
 *    OPEN request to the Hgfs server with the filename of this vnode.
 *
 *    "Opens a file referenced by the supplied vnode.  The open() system call
 *    has already done a vop_lookup() on the path name, which returned a vnode
 *    pointer and then calls to vop_open().  This function typically does very
 *    little since most of the real work was performed by vop_lookup()."
 *    (Solaris Internals, p537)
 *
 * Results:
 *    Returns 0 on success and an error code on error.
 *
 * Side effects:
 *    The HgfsOpenFile for this file is given a handle that can be used on
 *    future read and write requests.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsOpen(struct vnode **vpp,    // IN: Vnode for file to open
         int flag,              // IN: Open flags
         struct cred *cr)       // IN: Credentials of caller
#else
static int
HgfsOpen(struct vnode **vpp,    // IN: Vnode for file to open
         int flag,              // IN: Open flags
         struct cred *cr,       // IN: Credentials of caller
         caller_context_t *ctx) // IN: Context of caller
#endif
{
   HgfsSuperInfo *sip;

   if (!vpp || !cr) {
      cmn_err(HGFS_ERROR, "HgfsOpen: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsOpen().\n");


   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   /* Make sure we know the filename. */
   ASSERT(HGFS_KNOW_FILENAME(*vpp));

   /*
    * Make sure the handle is not already set.  If it is, this means the file
    * has already been opened so we'll need to create a new vnode since we keep
    * a vnode for each open instance of a file.  This ensures that the handle
    * we'll create now won't clobber the other one's open-file state.
    */
   if (HgfsHandleIsSet(*vpp)) {
      int ret;
      struct vnode *origVp = *vpp;

      ret = HgfsVnodeDup(vpp, origVp, sip, &sip->fileHashTable);
      if (ret) {
         return EIO;
      }
   }

   switch((*vpp)->v_type) {
   case VDIR:
      DEBUG(VM_DEBUG_COMM, "HgfsOpen: opening a directory\n");
      return HgfsDirOpen(sip, *vpp);

   case VREG:
      {
         HgfsMode mode = 0;

         /*
          * If HgfsCreate() was called prior to this, this fills in the mode we
          * saved there.  It's okay if this fails since often HgfsCreate()
          * won't have been called.
          */
         HgfsGetOpenFileMode(*vpp, &mode);

         DEBUG(VM_DEBUG_COMM, "HgfsOpen: opening a file with flag %x\n", flag);
         return HgfsFileOpen(sip, *vpp, flag, mode);
      }

   default:
      DEBUG(VM_DEBUG_FAIL,
            "HgfsOpen: unrecognized file of type %d.\n", (*vpp)->v_type);
      return EINVAL;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsClose --
 *
 *    Invoked when a user calls close(2) on a file in our filesystem.  Sends
 *    a CLOSE request to the Hgfs server with the filename of this vnode.
 *
 *    "Closes the file given by the supplied vnode.  When this is the last
 *    close, some filesystems use vop_close() to initiate a writeback of
 *    outstanding dirty pages by checking the reference cound in the vnode."
 *    (Solaris Internals, p536)
 *
 * Results:
 *    Returns 0 on success and an error code on error.
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsClose(struct vnode *vp,     // IN: Vnode of file that is being closed
          int flag,             // IN: Flags file was opened with
          int count,            // IN: Reference count on this vnode
          offset_t offset,      // IN: XXX
          struct cred *cr)      // IN: Credentials of caller
#else
static int
HgfsClose(struct vnode *vp,        // IN: Vnode of file that is being closed
          int flag,                // IN: Flags file was opened with
          int count,               // IN: Reference count on this vnode
          offset_t offset,         // IN: XXX
          struct cred *cr,         // IN: Credentials of caller
          caller_context_t *ctx)   // IN: Context of caller
#endif
{
   HgfsSuperInfo *sip;

   if (!vp) {
      cmn_err(HGFS_ERROR, "HgfsClose: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsClose(). (vp=%p)\n", vp);
   DEBUG(VM_DEBUG_INFO, "HgfsClose: flag=%x, count=%x, offset=%lld\n",
         flag, count, offset);

   /*
    * Solaris calls this function with a count greater than one at certain
    * times.  We only want to actually close it on the last close.
    */
   if (count > 1) {
      return 0;
   }

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   if ( !HGFS_KNOW_FILENAME(vp) ) {
      DEBUG(VM_DEBUG_FAIL, "HgfsClose: we don't know the filename of:\n");
      HgfsDebugPrintVnode(VM_DEBUG_STRUCT, "HgfsClose", vp, TRUE);
      return EINVAL;
   }

   /*
    * If we are closing a directory we need to send a SEARCH_CLOSE request,
    * but if we are closing a regular file we need to send a CLOSE request.
    * Other file types are not supported by the Hgfs protocol.
    */

   switch (vp->v_type) {
   case VDIR:
      return HgfsDirClose(sip, vp);

   case VREG:
      return HgfsFileClose(sip, vp);

   default:
      DEBUG(VM_DEBUG_FAIL, "HgfsClose: unsupported filetype %d.\n", vp->v_type);
      return EINVAL;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRead --
 *
 *    Invoked when a user calls read(2) on a file in our filesystem.
 *
 *    We call HgfsDoRead() to fill the user's buffer until the request is met
 *    or the file has no more data.  This is done since we can only transfer
 *    HGFS_IO_MAX bytes in any one request.
 *
 *    "Reads the range supplied for the given vnode.  vop_read() typically
 *    maps the requested range of a file into kernel memory and then uses
 *    vop_getpage() to do the real work." (Solaris Internals, p537)
 *
 * Results:
 *    Returns zero on success and an error code on failure.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION == 2
static int
HgfsRead(struct vnode *vp,      // IN: Vnode of file to read
         struct uio *uiop,      // IN: User's read request
         int ioflag,            // IN: XXX
         struct cred *cr)       // IN: Credentials of caller
#else
static int
HgfsRead(struct vnode *vp,              // IN: Vnode of file to read
         struct uio *uiop,              // IN: User's read request
         int ioflag,                    // IN: XXX
         struct cred *cr,               // IN: Credentials of caller
         caller_context_t *ctx)         // IN: Context of caller
#endif
{
   HgfsSuperInfo *sip;
   HgfsHandle handle;
   uint64_t offset;
   int ret;

   if (!vp || !uiop) {
      cmn_err(HGFS_ERROR, "HgfsRead: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsRead: entry.\n");

   /* We can't read from directories, that's what readdir() is for. */
   if (vp->v_type == VDIR) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRead: cannot read directories.\n");
      return EISDIR;
   }

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   /* This is where the user wants to start reading from in the file. */
   offset = uiop->uio_loffset;

   /*
    * We need to get the handle for the requests sent to the Hgfs server.  Note
    * that this is guaranteed to not change until a close(2) is called on this
    * vnode, so it's safe and correct to acquire it outside the loop below.
    */
   ret = HgfsGetOpenFileHandle(vp, &handle);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRead: could not get handle.\n");
      return EINVAL;
   }

   /*
    * Here we loop around HgfsDoRead with requests less than or equal to
    * HGFS_IO_MAX until one of the following conditions is met:
    *  (1) All the requested data has been read
    *  (2) The file has no more data
    *  (3) An error occurred
    *
    * Since HgfsDoRead() calls uiomove(9F), we know condition (1) is met when
    * the uio structure's uio_resid is decremented to zero.  If HgfsDoRead()
    * returns 0 we know condition (2) was met, and if it returns less than 0 we
    * know condtion (3) was met.
    */
   do {
      uint32_t size;
      uint32_t count;

      DEBUG(VM_DEBUG_INFO, "%s: offset=%"FMT64"d, uio_loffset=%lld\n",
            __func__, offset, uiop->uio_loffset);
      DEBUG(VM_DEBUG_HANDLE, "%s: ** handle=%d, file=%s\n",
            __func__, handle, HGFS_VP_TO_FILENAME(vp));

      /* Request at most HGFS_IO_MAX bytes */
      size = (uiop->uio_resid > HGFS_IO_MAX) ? HGFS_IO_MAX : uiop->uio_resid;

      /* Send one read request. */
      ret = HgfsDoRead(sip, handle, offset, size, uiop, &count);
      if (ret) {
         DEBUG(VM_DEBUG_FAIL, "%s: HgfsDoRead() failed.\n", __func__);
         return ret;
      }

      if (count == 0) {
         /* On end of file we return success */
         DEBUG(VM_DEBUG_DONE, "%s: end of file reached.\n", __func__);
         return 0;
      }

      /* Bump the offset past where we have already read. */
      offset += count;
   } while (uiop->uio_resid);

   /* We fulfilled the user's read request, so return success. */
   DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsWrite --
 *
 *    This is invoked when a user calls write(2) on a file in our filesystem.
 *
 *    We call HgfsDoWrite() once with requests less than or equal to
 *    HGFS_IO_MAX bytes until the user's write request has completed.
 *
 *    "Writes the range supplied for the given vnode.  The write system call
 *    typically maps the requested range of a file into kernel memory and then
 *    uses vop_putpage() to do the real work." (Solaris Internals, p538)
 *
 * Results:
 *    Returns 0 on success and error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION < 3
static int
HgfsWrite(struct vnode *vp,     // IN: Vnode of file to write to
          struct uio *uiop,     // IN: User's write request
          int ioflag,           // IN: XXX
          struct cred *cr)      // IN: Credentials of caller
#else
static int
HgfsWrite(struct vnode *vp,             // IN: Vnode of file to write to
          struct uio *uiop,             // IN: User's write request
          int ioflag,                   // IN: XXX
          struct cred *cr,              // IN: Credentials of caller
          caller_context_t *ctx)        // IN: Context of caller
#endif
{
   HgfsSuperInfo *sip;
   HgfsHandle handle;
   uint64_t offset;
   int ret;

   if (!vp || !uiop) {
      cmn_err(HGFS_ERROR, "HgfsWrite: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsWrite: entry. (vp=%p)\n", vp);
   DEBUG(VM_DEBUG_INFO, "HgfsWrite: ***ioflag=%x, uio_resid=%ld\n",
         ioflag, uiop->uio_resid);

   /* Skip write requests for 0 bytes. */
   if (uiop->uio_resid == 0) {
      DEBUG(VM_DEBUG_INFO, "HgfsWrite: write of 0 bytes requested.\n");
      return 0;
   }

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   DEBUG(VM_DEBUG_INFO, "HgfsWrite: file is %s\n", HGFS_VP_TO_FILENAME(vp));

   /* This is where the user will begin writing into the file. */
   offset = uiop->uio_loffset;

   /* Get the handle we need to supply the Hgfs server. */
   ret = HgfsGetOpenFileHandle(vp, &handle);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsWrite: could not get handle.\n");
      return EINVAL;
   }

   /*
    * We loop around calls to HgfsDoWrite() until either (1) we have written all
    * of our data or (2) an error has occurred.  uiop->uio_resid is decremented
    * by uiomove(9F) inside HgfsDoWrite(), so condition (1) is met when it
    * reaches zero.  Condition (2) occurs when HgfsDoWrite() returns less than
    * zero.
    */
   do {
      uint32_t size;
      uint32_t count;

      DEBUG(VM_DEBUG_INFO, "HgfsWrite: ** offset=%"FMT64"d, uio_loffset=%lld\n",
            offset, uiop->uio_loffset);
      DEBUG(VM_DEBUG_HANDLE, "HgfsWrite: ** handle=%d, file=%s\n",
            handle, HGFS_VP_TO_FILENAME(vp));

      /* Write at most HGFS_IO_MAX bytes. */
      size = (uiop->uio_resid > HGFS_IO_MAX) ? HGFS_IO_MAX : uiop->uio_resid;

      /* Send one write request. */
      ret = HgfsDoWrite(sip, handle, ioflag, offset, size, uiop, &count);
      if (ret) {
         DEBUG(VM_DEBUG_FAIL, "%s: HgfsDoRead() failed.\n", __func__);
         return ret;
      }

      /* Increment the offest by the amount already written. */
      offset += count;
   } while (uiop->uio_resid);

   /* We have completed the user's write request, so return success. */
   DEBUG(VM_DEBUG_DONE, "HgfsWrite: done.\n");
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsIoctl --
 *
 *    Invoked when a user calls ioctl(2) on a file in our filesystem.
 *    Performs a specified operation on the file.
 *
 * Results:
 *    ENOTSUP
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsIoctl(struct vnode *vp,     // IN: Vnode of file to operate on
          int cmd,              // IN: Requested command from user
          intptr_t arg,         // IN: Arguments for command
          int flag,             // IN: XXX
          struct cred *cr,      // IN: Credentials of caller
          int *rvalp)           //XXX
#else
static int
HgfsIoctl(struct vnode *vp,        // IN: Vnode of file to operate on
          int cmd,                 // IN: Requested command from user
          intptr_t arg,            // IN: Arguments for command
          int flag,                // IN: XXX
          struct cred *cr,         // IN: Credentials of caller
          int *rvalp,              //XXX
          caller_context_t *ctx)   // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsIoctl() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSetfl --
 *
 *    "Sets file locks on the supplied vnode." (Solaris Internals, p538)
 *
 *    Use fs_setfl from <sys/fs_subr.h>?  Do we need this?
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */
/*
int
HgfsSetfl(struct vnode *vp,
          int oflags,
          int nflags,
          struct cred *cr)
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsSetfl() NOTSUP.\n");

   return ENOTSUP;
}*/


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetattr --
 *
 *    "Gets the attributes for the supplied vnode." (Solaris Internals, p536)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsGetattr(struct vnode *vp,   // IN: Vnode of file to get attributes for
            struct vattr *vap,  // OUT: Filled in with attributes of file
            int flags,          // IN: XXX
            struct cred *cr)    // IN: Credentials of caller
#else
static int
HgfsGetattr(struct vnode *vp,      // IN: Vnode of file to get attributes for
            struct vattr *vap,     // OUT: Filled in with attributes of file
            int flags,             // IN: XXX
            struct cred *cr,       // IN: Credentials of caller
            caller_context_t *ctx) // IN: Context of caller
#endif
{
   HgfsSuperInfo *sip;
   HgfsReq *req;
   HgfsRequestGetattr *request;
   HgfsReplyGetattr *reply;
   int ret;

   if (!vp || !vap) {
      cmn_err(HGFS_ERROR, "HgfsGetattr: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsGetattr().\n");

   /*
    * Here we should send a Getattr request then examine vap->va_mask to
    * retun the values the user asked for.  HgfsAttrToSolaris() handles filling
    * in the Solaris structure with the correct values based on the Hgfs type.
    */

   sip = HgfsGetSuperInfo();
   if (!sip) {
      DEBUG(VM_DEBUG_FAIL, "HgfsGetattr() couldn't get superinfo.\n");
      return EIO;
   }

   ASSERT(HGFS_KNOW_FILENAME(vp));

   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   request = (HgfsRequestGetattr *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_GETATTR);

   /*
    * Now we need to convert the filename to cross-platform and unescaped
    * format.
    */
   ret = CPName_ConvertTo(HGFS_VP_TO_FILENAME(vp), MAXPATHLEN, request->fileName.name);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsGetattr: CPName_ConvertTo failed.\n");
      ret = ENAMETOOLONG;
      /* We need to set the request's state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   request->fileName.length = ret;

   req->packetSize = sizeof *request + request->fileName.length;

   /*
    * Now submit request and wait for reply.  The request's state will be
    * properly set to COMPLETED, ERROR, or ABANDONED after calling
    * HgfsSubmitRequest()
    */
   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      goto out;
   }

   reply = (HgfsReplyGetattr *)req->packet;

   if (HgfsValidateReply(req, sizeof reply->header) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsGetattr: reply not valid.\n");
      ret = EPROTO;
      goto out;
   }

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
   } else {
      /* Make sure we got all of the attributes */
      if (req->packetSize != sizeof *reply) {
         DEBUG(VM_DEBUG_FAIL, "%s: packet too small.\n", __func__);
         ret = EIO;
      } else {

         DEBUG(VM_DEBUG_COMM, "%s: received reply for ID %d\n",
               __func__, reply->header.id);
         DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n",
               reply->header.status);
         DEBUG(VM_DEBUG_COMM, " file type: %d\n", reply->attr.type);
         DEBUG(VM_DEBUG_COMM, " file size: %"FMT64"u\n", reply->attr.size);
         DEBUG(VM_DEBUG_COMM, " permissions: %o\n", reply->attr.permissions);
         DEBUG(VM_DEBUG_COMM, "%s: filename %s\n", __func__, HGFS_VP_TO_FILENAME(vp));

         /* Map the Hgfs attributes into the Solaris attributes */
         HgfsAttrToSolaris(vp, &reply->attr, vap);

         DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);
      }
   }

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSetattr --
 *
 *    Maps the Solaris attributes to Hgfs attributes (by calling
 *    HgfsSetattrCopy()) and sends a set attribute request to the Hgfs server.
 *
 *    "Sets the attributes for the supplied vnode." (Solaris Internals, p537)
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on error.
 *
 * Side effects:
 *    The file on the host will have new attributes.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION == 2
static int
HgfsSetattr(struct vnode *vp,   // IN: Vnode of file to get attributes for
            struct vattr *vap,  // IN: New attributes for the file
            int flags,          // IN: Flags for this call (from sys/vnode.h)
            struct cred *cr)    // IN: Credentials of caller
#else
static int
HgfsSetattr(struct vnode *vp,       // IN: Vnode of file to get attributes for
            struct vattr *vap,      // IN: New attributes for the file
            int flags,              // IN: Flags for this call (from sys/vnode.h)
            struct cred *cr,        // IN: Credentials of caller
            caller_context_t *ctx)  // IN: Context of caller
#endif
{
   HgfsSuperInfo *sip;
   HgfsReq *req;
   HgfsRequestSetattr *request;
   HgfsReplySetattr *reply;
   int ret;

   DEBUG(VM_DEBUG_ENTRY, "HgfsSetattr().\n");

   if (!vp || !vap) {
      cmn_err(HGFS_ERROR, "HgfsSetattr: NULL input from Kernel.\n");
      return EINVAL;
   }

   if ( !HGFS_KNOW_FILENAME(vp) ) {
      DEBUG(VM_DEBUG_FAIL,
            "HgfsSetattr: we don't know filename to set attributes for.\n");
      return EINVAL;
   }

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   request = (HgfsRequestSetattr *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_SETATTR);

   /*
    * Fill the attributes and update fields of the request.  If no updates are
    * needed then we will just return success without sending the request.
    */
   if (HgfsSetattrCopy(vap, flags, &request->attr, &request->update) == FALSE) {
      DEBUG(VM_DEBUG_DONE, "HgfsSetattr: don't need to update attributes.\n");
      ret = 0;
      /* We need to set the request state to completed before destroying. */
      req->state = HGFS_REQ_COMPLETED;
      goto out;
   }

   /* Convert the filename to cross platform and escape its buffer. */
   ret = CPName_ConvertTo(HGFS_VP_TO_FILENAME(vp), MAXPATHLEN, request->fileName.name);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: CPName_ConvertTo failed.\n");
      ret = ENAMETOOLONG;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   request->fileName.length = ret;

   /* The request's size includes the request and filename. */
   req->packetSize = sizeof *request + request->fileName.length;

   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      goto out;
   }

   reply = (HgfsReplySetattr *)req->packet;

   if (HgfsValidateReply(req, sizeof *reply) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsSetattr: invalid reply received.\n");
      ret = EPROTO;
      goto out;
   }

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
   } else {
      DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);
   }

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsAccess --
 *
 *    This function is invoked when the user calls access(2) on a file in our
 *    filesystem.  It checks to ensure the user has the specified type of
 *    access to the file.
 *
 *    We send a GET_ATTRIBUTE request by calling HgfsGetattr() to get the mode
 *    (permissions) for the provided vnode.
 *
 * Results:
 *    Returns 0 if access is allowed and a non-zero error code otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsAccess(struct vnode *vp,    // IN: Vnode of file to check access for
           int mode,            // IN: Mode of access
           int flags,           // IN: XXX
           struct cred *cr)     // IN: Credentials of caller
#else
static int
HgfsAccess(struct vnode *vp,       // IN: Vnode of file to check access for
           int mode,               // IN: Mode of access
           int flags,              // IN: XXX
           struct cred *cr,        // IN: Credentials of caller
           caller_context_t *ctx)  // IN: Context of caller
#endif
{
   int ret;
   struct vattr vap;

   if (!vp | !cr) {
      cmn_err(HGFS_ERROR, "HgfsAccess: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsAccess(). (vp=%p, mode=%o, flags=%x)\n",
         vp, mode, flags);

   /* We only care about the file's mode (permissions).  That is, not the owner */
   vap.va_mask = AT_MODE;

   /* Get the attributes for this file from the Hgfs server. */
#if HGFS_VFS_VERSION <= 3
   ret = HgfsGetattr(vp, &vap, flags, cr);
#else
   ret = HgfsGetattr(vp, &vap, flags, cr, NULL);
#endif
   if (ret) {
      return ret;
   }

   DEBUG(VM_DEBUG_INFO, "HgfsAccess: vp's mode: %o\n", vap.va_mode);

   /*
    * mode is the desired access from the caller, and is composed of S_IREAD,
    * S_IWRITE, and S_IEXEC from <sys/stat.h>.  Since the mode of the file is
    * guaranteed to only contain owner permissions (by the Hgfs server), we
    * don't need to shift any bits.
    */
   if ((mode & S_IREAD) && !(vap.va_mode & S_IREAD)) {
      DEBUG(VM_DEBUG_FAIL, "HgfsAccess: read access not allowed (%s).\n",
            HGFS_VP_TO_FILENAME(vp));
      return EPERM;
   }

   if ((mode & S_IWRITE) && !(vap.va_mode & S_IWRITE)) {
      DEBUG(VM_DEBUG_FAIL, "HgfsAccess: write access not allowed (%s).\n",
            HGFS_VP_TO_FILENAME(vp));
      return EPERM;
   }

   if ((mode & S_IEXEC) && !(vap.va_mode & S_IEXEC)) {
      DEBUG(VM_DEBUG_FAIL, "HgfsAccess: execute access not allowed (%s).\n",
            HGFS_VP_TO_FILENAME(vp));
      return EPERM;
   }

   /* Success */
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsLookup --
 *
 *    Looks in the provided directory for the specified filename.  If we cannot
 *    determine the vnode locally (i.e, the vnode is not the root vnode of the
 *    filesystem or the provided dvp), we send a getattr request to the server
 *    and allocate a vnode and internal filesystem state for this file.
 *
 *    "Looks up the path name for the supplied vnode.  The vop_lookup() does
 *    file-name translation for the open, stat system calls." (Solaris
 *    Internals, p537)
 *
 * Results:
 *   Returns zero on success and ENOENT if the file cannot be found
 *   If file is found, a vnode representing the file is returned in vpp.
 *
 * Side effects:
 *   None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsLookup(struct vnode *dvp,   // IN:  Directory to look in
           char *nm,            // IN:  Name to lookup in directory
           struct vnode **vpp,  // OUT: Pointer to vnode representing found file
           struct pathname *pnp,// IN:  XXX
           int flags,           // IN:  XXX
           struct vnode *rdir,  // IN:  XXX
           struct cred *cr)     // IN:  Credentials of caller

#else
static int
HgfsLookup(struct vnode *dvp,      // IN:  Directory to look in
           char *nm,               // IN:  Name to lookup in directory
           struct vnode **vpp,     // OUT: Pointer to vnode representing found file
           struct pathname *pnp,   // IN:  XXX
           int flags,              // IN:  XXX
           struct vnode *rdir,     // IN:  XXX
           struct cred *cr,        // IN: Credentials of caller
           caller_context_t *ctx,  // IN: Context of caller
           int *direntflags,       // IN: XXX
           pathname_t *realpnp)    // IN: XXX
#endif
{
   int ret;
   HgfsReq *req;
   HgfsRequestGetattr *request;
   HgfsReplyGetattr *reply;
   HgfsSuperInfo *sip;
   char path[MAXPATHLEN + 1];   /* Temporary buffer for full path */

   if (!dvp || !nm || !vpp || !cr) {
      cmn_err(HGFS_ERROR, "HgfsLookup: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsLookup(). (nm=%s)\n", nm);
   //DEBUG(VM_DEBUG_ENTRY, "HgfsLookup: pnp=%x, rdir=%x\n", pnp, rdir);

   /* First ensure that we are looking in a directory. */
   if (dvp->v_type != VDIR) {
      return ENOTDIR;
   }

   DEBUG(VM_DEBUG_COMM, " looking up \"%s\"\n", nm);

   /*
    * Get pointer to the superinfo.  If the device is not attached,
    * hgfsInstance will not be valid and we immediately return an error.
    */
   sip = HgfsGetSuperInfo();
   if (!sip) {
      DEBUG(VM_DEBUG_FAIL, "HgfsLookup: couldn't acquire superinfo "
                           "(hgfsInstance=%x).\n", hgfsInstance);
      return EIO;
   }

   /* Construct the full path for this lookup. */
   ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp),             // Path to this file
                          HGFS_VP_TO_FILENAME_LENGTH(dvp),      // Length of path
                          nm,                                   // File's name
                          path,                                 // Destination buffer
                          sizeof path);                         // Size of dest buffer
   if (ret < 0) {
      return EINVAL;
   }

   DEBUG(VM_DEBUG_LOAD, "HgfsLookup: full path is \"%s\"\n", path);

   /* See if the lookup is really for the root vnode. */
   if (strcmp(path, "/") == 0) {
      DEBUG(VM_DEBUG_INFO, "HgfsLookup: returning the root vnode.\n");
      *vpp = HGFS_ROOT_VNODE(sip);
      /*
       * Note that this is the only vnode we maintain a reference count on; all
       * others are per-open-file and should only be given to the Kernel once.
       */
      VN_HOLD(*vpp);
      return  0;
   }

   /*
    * Now that we know the full filename, we can check our hash table for this
    * file to prevent having to send a request to the Hgfs Server.  If we do
    * find this file in the hash table, this function will correctly create
    * a vnode and other per-open state for us.
    *
    * On an 'ls -l', this saves sending two requests for each file in the
    * directory.
    *
    * XXX
    * Note that this optimization leaves open the possibility that a file that
    * has been removed on the host will not be noticed as promptly by the
    * filesystem.  This shouldn't cause any problems, though, because as far
    * as we can tell this function is invoked internally by the kernel before
    * other operations.  That is, this function is called implicitly for path
    * traversal when user applications issue other system calls.  The operation
    * next performed on the vnode we create here should happen prior to
    * returning to the user application, so if that next operation fails
    * because the file has been deleted, the user won't see different behavior
    * than if this optimization was not included.  Nonetheless, the #if 1 below
    * is provided to make it easy to turn off.
    */
#if 1
   ret = HgfsFileNameToVnode(path, vpp, sip, sip->vfsp, &sip->fileHashTable);
   if (ret == 0) {
      /*
       * The filename was in our hash table and we successfully created new
       * per-open state for it.
       */
      DEBUG(VM_DEBUG_DONE, "HgfsLookup: created per-open state from filename.\n");
      return 0;
   }
#endif

   /*
    * We don't have any reference to this vnode, so we must send a get
    * attribute request to see if the file exists and create one.
    */
   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   /* Fill in the header of this request. */
   request = (HgfsRequestGetattr *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_GETATTR);

   /* Fill in the filename portion of the request. */
   ret = CPName_ConvertTo(path, MAXPATHLEN, request->fileName.name);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsLookup: CPName_ConvertTo failed.\n");
      ret = ENAMETOOLONG;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }
   request->fileName.length = ret;

   /* Packet size includes the request and its payload. */
   req->packetSize = request->fileName.length + sizeof *request;

   DEBUG(VM_DEBUG_COMM, "HgfsLookup: sending getattr request for ID %d\n",
         request->header.id);
   DEBUG(VM_DEBUG_COMM, " fileName.length: %d\n", request->fileName.length);
   DEBUG(VM_DEBUG_COMM, " fileName.name: \"%s\"\n", request->fileName.name);

   /*
    * Submit the request and wait for the reply.
    */
   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      goto out;
   }

   /* The reply is in the request's packet */
   reply = (HgfsReplyGetattr *)req->packet;

   /* Validate the reply was COMPLETED and at least contains a header */
   if (HgfsValidateReply(req, sizeof reply->header) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsLookup(): invalid reply received for ID %d "
            "with status %d.\n", reply->header.id, reply->header.status);
      ret = EPROTO;
      goto out;
   }

   DEBUG(VM_DEBUG_COMM, "HgfsLookup: received reply for ID %d\n", reply->header.id);
   DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", reply->header.status);
   DEBUG(VM_DEBUG_COMM, " file type: %d\n", reply->attr.type);
   DEBUG(VM_DEBUG_COMM, " file size: %"FMT64"u\n", reply->attr.size);
   DEBUG(VM_DEBUG_COMM, " permissions: %o\n", reply->attr.permissions);

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed for [%s] with error %d.\n",
            __func__, nm, ret);
      goto out;
   }

   /* Ensure packet contains correct amount of data */
   if (req->packetSize != sizeof *reply) {
      DEBUG(VM_DEBUG_COMM, "%s: invalid packet size received for [%s].\n",
            __func__, nm);
      ret = EIO;
      goto out;
   }

   /*
    * We need to create a vnode for this found file to give back to the Kernel.
    * Note that v_vfsp of the filesystem's root vnode was set properly in
    * HgfsMount(), so that value (dvp->v_vfsp) propagates down to each vnode.
    *
    */
   ret = HgfsVnodeGet(vpp,                      // Location to write vnode's address
                      sip,                      // Superinfo
                      dvp->v_vfsp,              // VFS for our filesystem
                      path,                     // Full name of the file
                      reply->attr.type,         // Type of file
                      &sip->fileHashTable);     // File hash table

   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsLookup: couldn't create vnode for \"%s\".\n", path);
      ret = EIO;
      goto out;
   }

   /* HgfsVnodeGet guarantees this. */
   ASSERT(*vpp);

   DEBUG(VM_DEBUG_LOAD, "HgfsLookup: assigned vnode %p to %s\n", *vpp, path);

   ret = 0;     /* Return success */

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsCreate --
 *
 *    This entry point is invoked when a user calls open(2) with the O_CREAT
 *    flag specified.  The kernel calls our open entry point (HgfsOpen()) after
 *    calling this function, so here all we do is consruct the vnode and
 *    save the filename and permission bits for the file to be created within
 *    our filesystem internal state.
 *
 *    "Creates the supplied pathname." (Solaris Internals, p536)
 *
 * Results:
 *    Returns zero on success and an appropriate error code on error.
 *
 * Side effects:
 *    If the file exists, the vnode is duplicated since they are kepy per-open.
 *    If the file doesn't exist, a vnode will be created.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsCreate(struct vnode *dvp,   // IN: Directory to create file in
           char *name,          // IN: Name of file to create
           struct vattr *vap,   // IN: Attributes for file
           vcexcl_t excl,       // IN: Exclusive creation flag: either NONEXCL or EXCL
           int mode,            // IN: Permissions to create with
           struct vnode **vpp,  // OUT: Vnode of file to create
           struct cred *cr,     // IN: Credentials of caller
           int flag)            // IN: XXX
#else
static int
HgfsCreate(struct vnode *dvp,     // IN: Directory to create file in
           char *name,            // IN: Name of file to create
           struct vattr *vap,     // IN: Attributes for file
           vcexcl_t excl,         // IN: Exclusive creation flag: either NONEXCL or EXCL
           int mode,              // IN: Permissions to create with
           struct vnode **vpp,    // OUT: Vnode of file to create
           struct cred *cr,       // IN: Credentials of caller
           int flag,              // IN: XXX
           caller_context_t *ctx, // IN: Context of caller
           vsecattr_t *vsecp)     // IN: XXX
#endif
{
   HgfsSuperInfo *sip;
   int ret = 0;

   DEBUG(VM_DEBUG_ENTRY, "HgfsCreate(): entry for \"%s\"\n", name);

   if (!dvp || !name || !vap || !vpp || !cr) {
      cmn_err(HGFS_ERROR, "HgfsCreate: NULL input from Kernel.\n");
      return EINVAL;
   }

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   if (dvp->v_type != VDIR) {
      DEBUG(VM_DEBUG_FAIL, "HgfsCreate: files must be created in directories.\n");
      return ENOTDIR;
   }

   /*
    * There are two cases: either the file already exists or it doesn't.  If
    * the file exists already then *vpp points to its vnode that was allocated
    * in HgfsLookup().  In both cases we need to create a new vnode (since our
    * vnodes are per-open-file, not per-file), but we don't need to construct
    * the full name again if we already have it in the existing vnode.
    */
   if ( !(*vpp) ) {
      char fullname[MAXPATHLEN + 1];

      ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp), // Name of directory to create in
                             HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of name
                             name,                    // Name of file to create
                             fullname,                // Buffer to write full name
                             sizeof fullname);        // Size of this buffer

      if (ret < 0) {
         DEBUG(VM_DEBUG_FAIL, "HgfsCreate: couldn't create full path name.\n");
         return ENAMETOOLONG;
      }

      /* Create the vnode for this file. */
      ret = HgfsVnodeGet(vpp, sip, dvp->v_vfsp, fullname,
                         HGFS_FILE_TYPE_REGULAR, &sip->fileHashTable);
      if (ret) {
         return EIO;
      }
   } else {
      struct vnode *origVp = *vpp;

      ASSERT(origVp->v_type != VDIR);   /* HgfsMkdir() should have been invoked */

      ret = HgfsVnodeDup(vpp, origVp, sip, &sip->fileHashTable);
      if (ret) {
         return EIO;
      }

      /* These cannot be the same. */
      ASSERT(*vpp != origVp);
   }

   /* HgfsVnodeGet() guarantees this. */
   ASSERT(*vpp);

   /* Save the mode so when open is called we can reference it. */
   HgfsSetOpenFileMode(*vpp, vap->va_mode);

   /* Solaris automatically calls open after this, so our work is done. */
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRemove --
 *
 *    Composes the full pathname of this file and sends a DELETE_FILE request
 *    by calling HgfsDelete().
 *
 *    "Removes the file for the supplied vnode." (Solaris Internals, p537)
 *
 * Results:
 *    Returns 0 on success or a non-zero error code on error.
 *
 * Side effects:
 *    If successful, the file specified will be deleted from the host's
 *    filesystem.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsRemove(struct vnode *vp,    // IN: Directory containing file to remove
           char *nm,            // IN: Name of file to remove
           struct cred *cr)     // IN: Credentials of caller
#else
static int
HgfsRemove(struct vnode *vp,       // IN: Directory containing file to remove
           char *nm,               // IN: Name of file to remove
           struct cred *cr,        // IN: Credentials of caller
           caller_context_t *ctx,  // IN: Context of caller
           int flags)              // IN: XXX
#endif
{
   HgfsSuperInfo *sip;
   char fullpath[MAXPATHLEN + 1];
   int ret;

   DEBUG(VM_DEBUG_ENTRY, "HgfsRemove().\n");

   if (!vp || !nm) {
      cmn_err(HGFS_ERROR, "HgfsRemove: NULL input from Kernel.\n");
      return EINVAL;
   }

   /* Ensure parent is a directory. */
   if (vp->v_type != VDIR) {
      DEBUG(VM_DEBUG_FAIL,
            "HgfsRemove: provided parent is a file, not a directory.\n");
      return ENOTDIR;
   }

   /* Ensure we know the name of the parent. */
   ASSERT(HGFS_KNOW_FILENAME(vp));

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   /*
    * We must construct the full name of the file to remove then call
    * HgfsDelete() to send the deletion request.
    */
   ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp), // Name of directory to create in
                          HGFS_VP_TO_FILENAME_LENGTH(vp), // Length of name
                          nm,                      // Name of file to create
                          fullpath,                // Buffer to write full name
                          sizeof fullpath);        // Size of this buffer
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRemove: could not construct full name.\n");
      return ENAMETOOLONG;
   }

   DEBUG(VM_DEBUG_COMM, "HgfsRemove: removing \"%s\".\n", fullpath);

   /* We can now send the delete request. */
   return HgfsDelete(sip, fullpath, HGFS_OP_DELETE_FILE);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsLink --
 *
 *    "Creates a hard link to the supplied vnode." (Solaris Internals, p537)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsLink(struct vnode *tdvp,    //XXX: Vnode of directory to create link in
         struct vnode *svp,     //XXX: Vnode of file to link to
         char *tnm,             //XXX: Name of link in directory
         struct cred *cr)       // IN: Credentials of caller
#else
static int
HgfsLink(struct vnode *tdvp,     //XXX: Vnode of directory to create link in
         struct vnode *svp,      //XXX: Vnode of file to link to
         char *tnm,              //XXX: Name of link in directory
         struct cred *cr,        // IN: Credentials of caller
         caller_context_t *ctx,  // IN: Context of caller
         int flags)              // IN: XXX
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsLink() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRename --
 *
 *    Renames the provided source name in the source directory with the
 *    destination name in the destination directory.  A RENAME request is sent
 *    to the Hgfs server.
 *
 * Results:
 *    Returns 0 on success and an error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsRename(struct vnode *sdvp,  // IN: Source directory that contains file to move
           char *snm,           // IN: File to move
           struct vnode *tdvp,  // IN: Destination directory of file
           char *tnm,           // IN: Destination name of file
           struct cred *cr)     // IN: Credentials of caller
#else
static int
HgfsRename(struct vnode *sdvp,     // IN: Source directory that contains file to move
           char *snm,              // IN: File to move
           struct vnode *tdvp,     // IN: Destination directory of file
           char *tnm,              // IN: Destination name of file
           struct cred *cr,        // IN: Credentials of caller
           caller_context_t *ctx,  // IN: Context of caller
           int flags)              // IN: XXX
#endif
{
   HgfsSuperInfo *sip;
   HgfsReq *req;
   HgfsRequestRename *request;
   HgfsReplyRename *reply;
   HgfsFileName *newNameP;
   char srcFullPath[MAXPATHLEN + 1];
   char dstFullPath[MAXPATHLEN + 1];
   int ret;

   if (!sdvp || !snm || !tdvp || !tnm) {
      cmn_err(HGFS_ERROR, "HgfsRename: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsRename().\n");

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   /* Make sure we know the names of both parent directories. */
   ASSERT(HGFS_KNOW_FILENAME(sdvp) && HGFS_KNOW_FILENAME(tdvp));

   /* Make the full path of the source. */
   ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(sdvp), HGFS_VP_TO_FILENAME_LENGTH(sdvp),
                          snm,
                          srcFullPath, sizeof srcFullPath);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRename: could not construct full path of source.\n");
      return ENAMETOOLONG;
   }

   /* Make the full path of the destination. */
   ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(tdvp), HGFS_VP_TO_FILENAME_LENGTH(tdvp),
                          tnm,
                          dstFullPath, sizeof dstFullPath);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRename: could not construct full path of dest.\n");
      return ENAMETOOLONG;
   }

   /* Ensure both names will fit in one request. */
   if ((sizeof *request + strlen(srcFullPath) + strlen(dstFullPath))
       > HGFS_PACKET_MAX) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRename: names too big for one request.\n");
      return EPROTO;
   }

   /*
    * Now we can prepare and send the request.
    */
   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   request = (HgfsRequestRename *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_RENAME);

   /* Convert the source to cross platform and unescape its buffer. */
   ret = CPName_ConvertTo(srcFullPath, MAXPATHLEN, request->oldName.name);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL,
            "HgfsRename: couldn't convert source to cross platform name.\n");
      ret = ENAMETOOLONG;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   request->oldName.length = ret;

   /*
    * The new name is placed directly after the old name in the packet and we
    * access it through this pointer.
    */
   newNameP = (HgfsFileName *)((char *)&request->oldName +
                               sizeof request->oldName +
                               request->oldName.length);

   /* Convert the destination to cross platform and unescape its buffer. */
   ret = CPName_ConvertTo(dstFullPath, MAXPATHLEN, newNameP->name);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL,
            "HgfsRename: couldn't convert destination to cross platform name.\n");
      ret = ENAMETOOLONG;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   newNameP->length = ret;

   /* The request's size includes the request and both filenames. */
   req->packetSize = sizeof *request + request->oldName.length + newNameP->length;

   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      goto out;
   }

   reply = (HgfsReplyRename *)req->packet;

   /* Validate the reply's state and size. */
   if (HgfsValidateReply(req, sizeof *reply) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRename: invalid reply received.\n");
      ret = EPROTO;
      goto out;
   }

   /* Return appropriate value. */
   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
   } else {
      DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);
   }

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsMkdir --
 *
 *    Makes a directory named dirname in the directory specified by the dvp
 *    vnode by sending a CREATE_DIR request, then allocates a vnode for this
 *    new directory and writes its address into vpp.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    If successful, a directory is created on the host's filesystem.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsMkdir(struct vnode *dvp,    // IN: Vnode of directory to create directory in
          char *dirname,        // IN: Name of directory to create
          struct vattr *vap,    // IN: Attributes of new directory
          struct vnode **vpp,   // OUT: Set to point to vnode for new directory
          struct cred *cr)      // IN: Credentials of caller
#else
static int
HgfsMkdir(struct vnode *dvp,      // IN: Vnode of directory to create directory in
          char *dirname,          // IN: Name of directory to create
          struct vattr *vap,      // IN: Attributes of new directory
          struct vnode **vpp,     // OUT: Set to point to vnode for new directory
          struct cred *cr,        // IN: Credentials of caller
          caller_context_t *ctx,  // IN: Context of caller
          int flags,              // IN: XXX
          vsecattr_t *vsecp)      // IN: XXX
#endif
{
   HgfsSuperInfo *sip;
   HgfsReq *req;
   HgfsRequestCreateDir *request;
   HgfsReplyCreateDir *reply;
   char fullname[MAXPATHLEN + 1];
   int ret;

   if (!dvp || !dirname || !vap || !vpp) {
      cmn_err(HGFS_ERROR, "HgfsMkdir: NULL input from Kernel.\n");
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsMkdir: dvp=%p (%s), dirname=%s, vap=%p, vpp=%p\n",
                         dvp, HGFS_VP_TO_FILENAME(dvp), dirname, vap,
                         *vpp);

   /*
    * We need to construct the full path of the directory to create then send
    * a CREATE_DIR request.  If successful we will create a vnode and fill in
    * vpp with a pointer to it.
    *
    * Note that unlike in HgfsCreate(), *vpp is always NULL.
    */

   if (dvp->v_type != VDIR) {
        DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: must create directory in directory.\n");
        return ENOTDIR;
   }

   /* Construct the complete path of the directory to create. */
   ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(dvp),// Name of directory to create in
                          HGFS_VP_TO_FILENAME_LENGTH(dvp), // Length of name
                          dirname,                 // Name of file to create
                          fullname,                // Buffer to write full name
                          sizeof fullname);        // Size of this buffer

   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsCreate: couldn't create full path name.\n");
      return ENAMETOOLONG;
   }

   /* Get pointer to our Superinfo */
   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   /* Initialize the request's contents. */
   request = (HgfsRequestCreateDir *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_CREATE_DIR);

   request->permissions = (vap->va_mode & S_IRWXU) >> HGFS_ATTR_MODE_SHIFT;

   ret = CPName_ConvertTo(fullname, MAXPATHLEN, request->fileName.name);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: cross-platform name is too long.\n");
      ret = ENAMETOOLONG;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   request->fileName.length = ret;

   /* Set the size of this request. */
   req->packetSize = sizeof *request + request->fileName.length;

   /* Send the request. */
   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      goto out;
   }

   reply = (HgfsReplyCreateDir *)req->packet;

   if (HgfsValidateReply(req, sizeof *reply) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMkdir: invalid reply received.\n");
      ret = EPROTO;
      goto out;
   }

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
      goto out;
   }

   /* We now create the vnode for the new directory. */
   ret = HgfsVnodeGet(vpp, sip, dvp->v_vfsp, fullname,
                      HGFS_FILE_TYPE_DIRECTORY, &sip->fileHashTable);
   if (ret) {
      ret = EIO;
      goto out;
   }

   ASSERT(*vpp);        /* HgfsIget guarantees this. */
   ret = 0;

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRmdir --
 *
 *    Removes the specified name from the provided vnode.  Sends a DELETE
 *    request by calling HgfsDelete() with the filename and correct opcode to
 *    indicate deletion of a directory.
 *
 *    "Removes the directory pointed to by the supplied vnode." (Solaris
 *    Internals, p537)
 *
 * Results:
 *    Returns 0 on success and an error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsRmdir(struct vnode *vp,     // IN: Dir containing dir to remove
          char *nm,             // IN: Name of directory to remove
          struct vnode *cdir,   // XXX?
          struct cred *cr)      // IN: Credentials of caller
#else
static int
HgfsRmdir(struct vnode *vp,       // IN: Dir containing dir to remove
          char *nm,               // IN: Name of directory to remove
          struct vnode *cdir,     // XXX?
          struct cred *cr,        // IN: Credentials of caller
          caller_context_t *ctx,  // IN: Context of caller
          int flags)              // IN: XXX
#endif
{
   HgfsSuperInfo *sip;
   char fullpath[MAXPATHLEN + 1];
   int ret;

   DEBUG(VM_DEBUG_ENTRY, "HgfsRmdir().\n");

   if (!vp || !nm) {
      cmn_err(HGFS_ERROR, "HgfsRmdir: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsRmdir: vp=%p (%s), nm=%s, cdir=%p (%s)\n",
         vp, (HGFS_VP_TO_FP(vp)) ? HGFS_VP_TO_FILENAME(vp) : "vp->v_data null",
         nm, cdir, (HGFS_VP_TO_FP(cdir)) ? HGFS_VP_TO_FILENAME(cdir) : "cdir->v_data null");

   /* A few checks to ensure we can remove the directory. */
   if (vp->v_type != VDIR) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRmdir: provided parent is a file, not a directory.\n");
      return ENOTDIR;
   }

   ASSERT(HGFS_KNOW_FILENAME(vp));

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return EIO;
   }

   /*
    * We need to construct the full name of the directory to remove then call
    * HgfsDelete with the proper opcode.
    */
   ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp), // Name of directory to create in
                          HGFS_VP_TO_FILENAME_LENGTH(vp), // Length of name
                          nm,                      // Name of file to create
                          fullpath,                // Buffer to write full name
                          sizeof fullpath);        // Size of this buffer
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsRmdir: could not construct full name.\n");
      return ENAMETOOLONG;
   }

   DEBUG(VM_DEBUG_COMM, "HgfsRmdir: removing \"%s\".\n", fullpath);

   /* We can now send the delete request. */
   return HgfsDelete(sip, fullpath, HGFS_OP_DELETE_DIR);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsReaddir --
 *
 *    Reads as many entries from the directory as will fit in to the provided
 *    buffer.  Each directory entry is read by calling HgfsGetNextDirEntry().
 *
 *    "The vop_readdir() method reads chunks of the directory into a uio
 *    structure.  Each chunk can contain as many entries as will fit within
 *    the size supplied by the uio structure.  The uio_resid structure member
 *    shows the size of the getdents request in bytes, which is divided by the
 *    size of the directory entry made by the vop_readdir() method to
 *    calculate how many directory entries to return." (Solaris Internals,
 *    p555)
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsReaddir(struct vnode *vp,   // IN: Vnode of directory to read
            struct uio *uiop,   // IN: User's read request
            struct cred *cr,    // IN: Credentials of caller
            int *eofp)          // OUT: Indicates we are done
#else
static int
HgfsReaddir(struct vnode *vp,       // IN: Vnode of directory to read
            struct uio *uiop,       // IN: User's read request
            struct cred *cr,        // IN: Credentials of caller
            int *eofp,              // OUT: Indicates we are done
            caller_context_t *ctx,  // IN: Context of caller
            int flags)              // IN: XXX
#endif
{
   HgfsSuperInfo *sip;
   HgfsHandle handle;
   struct dirent64 *dirp, *origdirp;
   ssize_t readSize;
   uint64_t offset;
   Bool done;
   int ret;

   DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir().\n");

   if (!vp || !uiop || !cr || !eofp) {
      cmn_err(HGFS_ERROR, "HgfsReaddir: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir: uiop->uio_resid=%ld, "
         "uiop->uio_loffset=%lld\n",
         uiop->uio_resid, uiop->uio_loffset);


   /*
    * XXX: If would be nice if we could perform some sort of confidence check on
    * the handle here.  Perhaps make sure handle <= NUM_SEARCHES in
    * hgfsServer.c since the handle is the index number in searchArray.
    */
   if ( !HGFS_KNOW_FILENAME(vp) ) {
      DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: we don't know the filename.\n");
      return EBADF;
   }


   sip = HgfsGetSuperInfo();
   if (!sip) {
      DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: we can't get the superinfo.\n");
      return EIO;
   }
   /*
    * In order to fill the user's buffer with directory entries, we must
    * iterate on HGFS_OP_SEARCH_READ requests until either the user's buffer is
    * full or there are no more entries.  Each call to HgfsGetNextDirEntry()
    * fills in the name and attribute structure for the next entry.  We then
    * escape that name and place it in a kernel buffer that's the same size as
    * the user's buffer.  Once there are no more entries or no more room in the
    * buffer, we copy it to user space.
    */

   /*
    * XXX
    * Note that I allocate a large buffer in kernel space so I can do only one
    * copy to user space, otherwise we would need to do a copy for each
    * directory entry.  This approach is potentially bad since readSize is
    * as big as the buffer the user called us with, and therefore in their
    * control.  (Actually, it's likely that the user can just say it has a
    * huge buffer without really having it.)  For this reason, I call
    * kmem_zalloc() with the KM_NOSLEEP flag which fails if it cannot allocate
    * memory rather than sleeping until it can (as KM_SLEEP does).
    *
    * This approach may want to be changed in the future.
    */

   readSize = uiop->uio_resid;
   origdirp = dirp = (struct dirent64 *)kmem_zalloc(readSize, KM_NOSLEEP);
   if (!origdirp) {
      DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: couldn't allocate memory.\n");
      return ENOMEM;
   }

   /*
    * We need to get the handle for this open directory to send to the Hgfs
    * server in our requests.
    */
   ret = HgfsGetOpenFileHandle(vp, &handle);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: could not get handle.\n");
      return EINVAL;
   }


   /*
    * Loop until one of the following conditions is met:
    *  o An error occurs while reading a directory entry
    *  o There are no more directory entries to read
    *  o The buffer is full and cannot hold the next entry
    *
    * We request dentries from the Hgfs server based on their index in the
    * directory.  The offset value is initialized to the value specified in
    * the user's io request and is incremented each time through the loop.
    *
    * dirp is incremented by the record length each time through the loop and
    * is used to determine where in the kernel buffer we write to.
    */
   for (offset = uiop->uio_loffset, done = 0; /* Nothing */ ; offset++) {
      char nameBuf[MAXNAMELEN + 1];
      char escName[MAXNAMELEN + 1];
      char fullName[MAXPATHLEN + 1];

      DEBUG(VM_DEBUG_COMM,
            "HgfsReaddir: getting directory entry at offset %"FMT64"u.\n", offset);

      DEBUG(VM_DEBUG_HANDLE, "HgfsReaddir: ** handle=%d, file=%s\n",
            handle, HGFS_VP_TO_FILENAME(vp));

      ret = HgfsGetNextDirEntry(sip, handle, offset, nameBuf, &done);
      /* If the filename was too long, we skip to the next entry ... */
      if (ret == EOVERFLOW) {
         continue;
      /* ... but if another error occurred, we return that error code ... */
      } else if (ret) {
         DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: failure occurred in HgfsGetNextDirEntry\n");
         goto out;
      /*
       * ... and if there are no more entries, we set the end of file pointer
       * and break out of the loop.
       */
      } else if (done == TRUE) {
         DEBUG(VM_DEBUG_COMM, "HgfsReaddir: Done reading directory entries.\n");
         *eofp = TRUE;
         break;
      }

      /*
       * We now have the directory entry, so we sanitize the name and try to
       * put it in our buffer.
       */
      DEBUG(VM_DEBUG_COMM, "HgfsReaddir: received filename \"%s\"\n", nameBuf);

      ret = HgfsEscape_Do(nameBuf, strlen(nameBuf), sizeof escName, escName);
      /* If the escaped name didn't fit in the buffer, skip to the next entry. */
      if (ret < 0) {
         DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: HgfsEscape_Do failed.\n");
         continue;
      }

      /*
       * Make sure there is enough room in the buffer for the entire directory
       * entry.  If not, we just break out of the loop and copy what we have.
       */
      if (DIRENT64_RECLEN(ret) > (readSize - ((uintptr_t)dirp - (uintptr_t)origdirp))) {
         DEBUG(VM_DEBUG_INFO, "HgfsReaddir: ran out of room in the buffer.\n");
         break;
      }

      /* Fill in the directory entry. */
      dirp->d_reclen = DIRENT64_RECLEN(ret);
      dirp->d_off = offset;
      memcpy(dirp->d_name, escName, ret);
      dirp->d_name[ret] = '\0';

      ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp),           // Directorie's name
                             HGFS_VP_TO_FILENAME_LENGTH(vp),    // Length
                             dirp->d_name,                      // Name of file
                             fullName,                          // Destination buffer
                             sizeof fullName);                  // Size of this buffer
      /* Skip this entry if the full path was too long. */
      if (ret < 0) {
         continue;
      }

      /*
       * Place the node id, which serves the purpose of inode number, for this
       * filename directory entry.  As long as we are using a dirent64, this is
       * okay since ino_t is also a u_longlong_t.
       */
      HgfsNodeIdGet(&sip->fileHashTable, fullName, (uint32_t)ret,
                    &dirp->d_ino);

      /* Advance to the location for the next directory entry */
      dirp = (struct dirent64 *)((intptr_t)dirp + dirp->d_reclen);
   }

   /*
    * Now that we've filled our buffer with as many dentries as fit, we copy it
    * into the user's buffer.
    */
   ret = uiomove(origdirp,                                // Source buffer
                 ((uintptr_t)dirp - (uintptr_t)origdirp), // Size of this buffer
                 UIO_READ,                                // Read flag
                 uiop);                                   // User's request struct

   /*
    * uiomove(9F) will have incremented the uio offset by the number of bytes
    * written.  We reset it here to the fs-specific offset in our directory so
    * the next time we are called it is correct.  (Note, this does not break
    * anything and /is/ how this field is intended to be used.)
    */
   uiop->uio_loffset = offset;

   DEBUG(VM_DEBUG_DONE, "HgfsReaddir: done (ret=%d, *eofp=%d).\n", ret, *eofp);
out:
   kmem_free(origdirp, readSize);
   DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir: exiting.\n");
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSymlink --
 *
 *    "Creates a symbolic link between the two pathnames" (Solaris Internals,
 *    p538)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <=3
static int
HgfsSymlink(struct vnode *dvp,  // IN: Directory to create link in
            char *linkname,     // IN: Name of link
            struct vattr *vap,  // IN: Attributes for the symlink
            char *target,       // IN: Name of target for symlink
            struct cred *cr)    // IN: Credentials of caller
#else
static int
HgfsSymlink(struct vnode *dvp,      // IN: Directory to create link in
            char *linkname,         // IN: Name of link
            struct vattr *vap,      // IN: Attributes for the symlink
            char *target,           // IN: Name of target for symlink
            struct cred *cr,        // IN: Credentials of caller
            caller_context_t *ctx,  // IN: Context of caller
            int flags)              // IN: XXX
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsSymlink() NOTSUP.\n");

   /*
    * Hgfs doesn't support links.
    */
   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsReadlink --
 *
 *    "Follows the symlink in the supplied vnode." (Solaris Internals, p537)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsReadlink(struct vnode *vp,  // IN: Vnode for the symlink
             struct uio *uiop,  // IN: XXX User's request?
             struct cred *cr)   // IN: Credentials of caller
#else
static int
HgfsReadlink(struct vnode *vp,       // IN: Vnode for the symlink
             struct uio *uiop,       // IN: XXX User's request?
             struct cred *cr,        // IN: Credentials of caller
             caller_context_t *ctx)  // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsReadlink() NOTSUP.\n");

   /*
    * Hgfs doesn't support links
    */
   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFsync --
 *
 *    We don't map any memory so we can safely return success.
 *
 *    "Flushes out any dirty pages for the supplied vnode." (Solaris
 *    Internals, p536)
 *
 * Results:
 *    Returns 0 on success and non-zero on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsFsync(struct vnode *vp,     // IN: Vnode for the file to sync
          int syncflag,         // IN: XXX?
          struct cred *cr)      // IN: Credentials of the caller
#else
static int
HgfsFsync(struct vnode *vp,       // IN: Vnode for the file to sync
          int syncflag,           // IN: XXX?
          struct cred *cr,        // IN: Credentials of the caller
          caller_context_t *ctx)  // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_ENTRY, "HgfsFsync().\n");

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsInactive --
 *
 *    Frees a vnode that is no longer referenced.  This is done by calling
 *    HgfsVnodePut() from hgfsState.c, which also cleans up our internal
 *    filesystem state.
 *
 *    "Free resources and releases the supplied vnode.  The file system can
 *    choose to destroy the vnode or put it onto an inactive list, which is
 *    managed by the file system implementation." (Solaris Internals, p536)
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static void
HgfsInactive(struct vnode *vp,  // IN: Vnode to operate on
             struct cred *cr)   // IN: Credentials of the caller
#else
static void
HgfsInactive(struct vnode *vp,       // IN: Vnode to operate on
             struct cred *cr,        // IN: Credentials of the caller
             caller_context_t *ctx)  // IN: Context of caller
#endif
{
   HgfsSuperInfo *sip;

   if (!vp) {
      cmn_err(HGFS_ERROR, "HgfsInactive: NULL input from Kernel.\n");
      return;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsInactive().\n");

   sip = HgfsGetSuperInfo();
   if (!sip) {
      return;
   }

   /* We need the check and decrement of v_count to be atomic */
   mutex_enter(&vp->v_lock);

   if (vp->v_count > 1) {
      vp->v_count--;
      mutex_exit(&vp->v_lock);

      DEBUG(VM_DEBUG_LOAD, "--> decremented count of vnode %p to %d\n",
            vp, vp->v_count);

      /*
       * XXX This should only ever happen for the root vnode with our new state
       * organization.
       */
      if (vp != sip->rootVnode) {
         DEBUG(VM_DEBUG_ALWAYS, "HgfsInactive: v_count of vnode for %s too high!\n",
               HGFS_VP_TO_FILENAME(vp));
      }
      ASSERT(vp == sip->rootVnode);

   } else {
      mutex_exit(&vp->v_lock);

      DEBUG(VM_DEBUG_LOAD, "--> freeing vnode %p - \"%s\"\n",
            vp, HGFS_VP_TO_FILENAME(vp));

      /* Deallocate this vnode. */
      HgfsVnodePut(vp, &sip->fileHashTable);

   }

}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFid --
 *
 *    Provide a unique file identifier for this vnode.  Note that I have never
 *    seen this function called by the Kernel.
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsFid(struct vnode *vp,       // IN: File to generate file identifier for
        struct fid *fidp)       // XXX: IN/OUT?: File identifier
#else
static int
HgfsFid(struct vnode *vp,       // IN: File to generate file identifier for
        struct fid *fidp,       // XXX: IN/OUT?: File identifier
        caller_context_t *ctx)  // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_ENTRY, "HgfsFid().\n");

   if (!vp || !fidp) {
      cmn_err(HGFS_ERROR, "HgfsFid: NULL input from Kernel.\n");
   }

   /*
    * Make sure we can fit our node id in the provided structure.  This allows
    * us to call memcpy() with the sizeof the source below.
    */
   if (sizeof fidp->fid_data < sizeof HGFS_VP_TO_NODEID(vp)) {
      return EOVERFLOW;
   }

   memset(fidp, 0, sizeof *fidp);
   memcpy(&fidp->fid_data, &HGFS_VP_TO_NODEID(vp), sizeof HGFS_VP_TO_NODEID(vp));
   fidp->fid_len  = sizeof HGFS_VP_TO_NODEID(vp);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRwlock --
 *
 *    Acquires either a readers or writers lock.
 *
 *    "Holds the reader/writer lock for the supplied vnode.  This method is
 *    called for each vnode, with the rwflag set to 0 inside a read() system
 *    call and the rwflag set to 1 inside a write() at a time.  Some file
 *    system implementations have opetions to ignore the writer lock inside
 *    vop_rwlock()." (Solaris Internals, p537)
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The file's readers/writers lock is held after this function.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION == 2
static void
HgfsRwlock(struct vnode *vp,    // IN: Vnode to get lock for
           int write_lock)      // IN: Set if wants a write lock, cleared for read lock
#elif HGFS_VFS_VERSION == 3
static void
HgfsRwlock(struct vnode *vp,            // IN: Vnode to get lock for
           int write_lock,              // IN: Set for write lock, cleared for read lock
           caller_context_t *context)   // IN: Context of caller
#else /* SOL11 */
static int
HgfsRwlock(struct vnode *vp,            // IN: Vnode to get lock for
           int write_lock,              // IN: Set for write lock, cleared for read lock
           caller_context_t *context)   // IN: Context of caller
#endif
{
   if (write_lock) {
      rw_enter(HGFS_VP_TO_RWLOCKP(vp), RW_WRITER);
   } else {
      rw_enter(HGFS_VP_TO_RWLOCKP(vp), RW_READER);
   }

#if HGFS_VFS_VERSION == 5
   return write_lock ? V_WRITELOCK_TRUE : V_WRITELOCK_FALSE;
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRwunlock --
 *
 *    "Releases the reader/writer lock for the supplied vnode." (Solaris
 *    Internals, p537)
 *
 * Results:
 *    Void.
 *
 * Side effects:
 *    This file's readers/writer lock is unlocked.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION == 2
static void
HgfsRwunlock(struct vnode *vp,  // IN: Vnode to release lock for
             int write_lock)    // IN: Set for write lock, cleared for read lock
#else
static void
HgfsRwunlock(struct vnode *vp,          // IN: Vnode to release lock for
             int write_lock,            // IN: Set for write lock, cleared for read lock
             caller_context_t *context) // IN: Context of caller
#endif
{
   //DEBUG(VM_DEBUG_ENTRY, "HgfsRwunlock().\n");

   if (!vp) {
      cmn_err(HGFS_ERROR, "HgfsRwunlock: NULL input from Kernel.\n");
      return;
   }

   rw_exit(HGFS_VP_TO_RWLOCKP(vp));
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSeek --
 *
 *    Checks to ensure that the specified offset is valid.  Actual manipulation
 *    of the file position is handled by the Kernel.
 *
 *    "Seeks within the supplied vnode." (Solaris Internals, p537)
 *
 * Results:
 *    Returns zero if this offset is valid and EINVAL if it isn't.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsSeek(struct vnode *vp,      // IN:     Vnode to seek within
         offset_t ooff,         // IN:     Current offset in this vnode
         offset_t *noffp)       // IN/OUT: Requested new offset within file
#else
static int
HgfsSeek(struct vnode *vp,      // IN:     Vnode to seek within
         offset_t ooff,         // IN:     Current offset in this vnode
         offset_t *noffp,       // IN/OUT: Requested new offset within file
         caller_context_t *ctx) // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_ENTRY, "HgfsSeek().\n");

   if (!noffp) {
      DEBUG(VM_DEBUG_FAIL, "HgfsSeek: noffp is NULL\n");
      return EINVAL;
   }

   if (*noffp < 0) {
      return EINVAL;
   }

   DEBUG(VM_DEBUG_INFO, "HgfsSeek: file   %s\n", HGFS_VP_TO_FILENAME(vp));
   DEBUG(VM_DEBUG_INFO, "HgfsSeek: ooff   %llu\n", ooff);
   DEBUG(VM_DEBUG_INFO, "HgfsSeek: *noffp %llu\n", *noffp);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsCmp --
 *
 *    Compares two vnodes to see if they are for the same file.  Our
 *    filesystem-specific check is to compare the filenames, file type, and
 *    file flags.  Since we keep vnodes per-open-file, rather than per-file,
 *    this function has significance.
 *
 *    This function is invoked by the VN_CMP macro only if the two given
 *    pointers are different and each has the same operations (v_op).
 *
 * Results:
 *    TRUE if vnodes are the same, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsCmp(struct vnode *vp1,      // IN: First vnode
        struct vnode *vp2)      // IN: Second vnode
#else
static int
HgfsCmp(struct vnode *vp1,      // IN: First vnode
        struct vnode *vp2,      // IN: Second vnode
        caller_context_t *ctx)  // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_ENTRY, "HgfsCmp: vp1=%p, vp2=%p.\n", vp1, vp2);

   /*
    * This function is only called if:
    * ((vp1 != vp2) && (vp1->v_op == vp2->v_op))
    *
    * We also care if the filenames are the same.
    */

   if (vp1->v_type != vp2->v_type) {
      DEBUG(VM_DEBUG_FAIL, "HgfsCmp: %s != %s",
            (vp1->v_type == VDIR) ? "VDIR" : "VREG",
            (vp2->v_type == VDIR) ? "VDIR" : "VREG");
      return FALSE;
   }

   if (vp1->v_flag != vp2->v_flag) {
      DEBUG(VM_DEBUG_FAIL, "HgfsCmp: flags: %x != %x\n", vp1->v_flag, vp2->v_flag);
      return FALSE;
   }

   if (strcmp(HGFS_VP_TO_FILENAME(vp1), HGFS_VP_TO_FILENAME(vp2)) != 0) {
      return FALSE;
   }

   DEBUG(VM_DEBUG_DONE, "HgfsCmp: for \"%s\", vp1 == vp2\n", HGFS_VP_TO_FILENAME(vp1));
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFrlock --
 *
 *    "Does file and record locking for the supplied vnode." (Solaris
 *    Internals, p536)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsFrlock(struct vnode *vp,                    // XXX
           int cmd,                             // IN: Command to carry out
           struct flock64 *bfp,                 // XXX
           int flag,                            // XXX
           offset_t offset,                     // XXX
           struct flk_callback *flk_callbackp,  // XXX
           struct cred *cr)                     // IN: Credentials of caller
#else
static int
HgfsFrlock(struct vnode *vp,                    // XXX
           int cmd,                             // IN: Command to carry out
           struct flock64 *bfp,                 // XXX
           int flag,                            // XXX
           offset_t offset,                     // XXX
           struct flk_callback *flk_callbackp,  // XXX
           struct cred *cr,                     // IN: Credentials of caller
           caller_context_t *ctx)               // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsFrlock() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSpace --
 *
 *    "Frees space for the supplied vnode." (Solaris Internals, p538)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION == 2
static int
HgfsSpace(struct vnode *vp,     // IN: Vnode to free space for
          int cmd,              // IN: XXX Command
          struct flock64 *bfp,  // IN: XXX
          int flag,             // IN: XXX
          offset_t offset,      // IN: XXX
          struct cred *cr)      // IN: Credentials of caller
#else
static int
HgfsSpace(struct vnode *vp,             // IN: Vnode to free space for
          int cmd,                      // IN: XXX Command
          struct flock64 *bfp,          // IN: XXX
          int flag,                     // IN: XXX
          offset_t offset,              // IN: XXX
          struct cred *cr,              // IN: Credentials of caller
          caller_context_t *ctx)        // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsSpace() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRealvp --
 *
 *    "Gets the real vnode from the supplied vnode." (Solaris Internals, p537)
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on error.  On success, vpp
 *    is given the value of the real vnode.  Currently this always returns
 *    success.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsRealvp(struct vnode *vp,    // IN: Vnode to find real vnode for
           struct vnode **vpp)  // OUT: Set to point to real vnode
#else
static int
HgfsRealvp(struct vnode *vp,    // IN: Vnode to find real vnode for
           struct vnode **vpp,  // OUT: Set to point to real vnode
           caller_context_t *ctx)
#endif
{
   DEBUG(VM_DEBUG_ENTRY, "HgfsRealvp().\n");

   DEBUG(VM_DEBUG_ENTRY, "HgfsRealvp: vp=%p\n", vp);
   DEBUG(VM_DEBUG_ENTRY, "HgfsRealvp: vp's name=%s\n", HGFS_VP_TO_FILENAME(vp));

   /*
    * Here we just supply the vnode we were provided.  This behavior is correct
    * since we maintain vnodes per-open-file rather than per-file.  The "real"
    * vnode /is/ the provided one since any other one belongs to a different
    * "open" file.
    */
   *vpp = vp;

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetpage --
 *
 *    HgfsRead() does not map file data into the Kernel's address space, so we
 *    shouldn't need to support this (that is, page faults will never occur).
 *
 *    "Gets pages in the range offset and length for the vnode from the
 *    backing store of the file system.  Does the real work of reading a
 *    vnode.  This method is often called as a result of read(), which causes
 *    a page fault in seg_map, which calls vop_getpage()." (Solaris Internals,
 *    p536)
 *
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsGetpage(struct vnode *vp,           // IN: Vnode of file to get page for
            offset_t off,               // IN: Offset in file/page to retrieve
            size_t len,                 // IN: Length of range to retrieve
            uint_t *protp,              // XXX
            struct page **plarr,        // XXX
            size_t plsz,                // IN: XXX
            struct seg *seg,            // XXX: Segment
            caddr_t addr,               // IN: Address of XXX
            enum seg_rw rw,             // IN: XXX
            struct cred *cr)            //IN Credentials of caller
#else
static int
HgfsGetpage(struct vnode *vp,           // IN: Vnode of file to get page for
            offset_t off,               // IN: Offset in file/page to retrieve
            size_t len,                 // IN: Length of range to retrieve
            uint_t *protp,              // XXX
            struct page **plarr,        // XXX
            size_t plsz,                // IN: XXX
            struct seg *seg,            // XXX: Segment
            caddr_t addr,               // IN: Address of XXX
            enum seg_rw rw,             // IN: XXX
            struct cred *cr,            //IN Credentials of caller
            caller_context_t *ctx)
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsGetpage() NOTSUP.\n");

   /* We don't currently need this; see the comment above. */

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsPutpage --
 *
 *    HgfsWrite() does not map file data into the Kernel's address space, so we
 *    shouldn't need to support this (that is, page faults will never occur).
 *
 *    "Writes pages in the range offset and length for the vnode to the
 *    backing store of the file system.  Does the real work of reading a
 *    vnode."  (Solaris Internals, p537)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsPutpage(struct vnode *vp,   // IN: Vnode of file to put page for
            offset_t off,       // IN: Offset to begin writing
            size_t len,         // IN: Amount of data to write
            int flags,          // IN: XXX
            struct cred *cr)    // IN: Credentials of caller
#else
static int
HgfsPutpage(struct vnode *vp,      // IN: Vnode of file to put page for
            offset_t off,          // IN: Offset to begin writing
            size_t len,            // IN: Amount of data to write
            int flags,             // IN: XXX
            struct cred *cr,       // IN: Credentials of caller
            caller_context_t *ctx) // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsPutpage() NOTSUP.\n");

   /* We don't currently need this; see the comment above. */

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsMap --
 *
 *    Each file has its VNOMAP flag set so this shouldn't be invoked.  Most
 *    applications seem to handle this so, if this becomes a problem this
 *    function will need to be implemented.
 *
 *    "Maps a range of pages into an address space by doing the appropriate
 *    checks and calline as_map()" (Solaris Internals, p537)
 *
 *
 * Results:
 *    Returns 0 on success and a non-zero error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsMap(struct vnode *vp,       // IN: Vnode of file to map page for
        offset_t off,           // IN: XXX
        struct as *as,          // IN: Address space
        caddr_t *addrp,         // IN: XXX: Address in this space?
        size_t len,             // IN: XXX:Length of area to map?
        uchar_t prot,           // IN: XXX
        uchar_t maxprot,        // IN: XXX
        uint_t flags,           // IN: XXX
        struct cred *cr)        // IN: Credentials of caller
#else
static int
HgfsMap(struct vnode *vp,       // IN: Vnode of file to map page for
        offset_t off,           // IN: XXX
        struct as *as,          // IN: Address space
        caddr_t *addrp,         // IN: XXX: Address in this space?
        size_t len,             // IN: XXX:Length of area to map?
        uchar_t prot,           // IN: XXX
        uchar_t maxprot,        // IN: XXX
        uint_t flags,           // IN: XXX
        struct cred *cr,        // IN: Credentials of caller
        caller_context_t *ctx)  // IN: Context of caller
#endif
{
   /* We specify VNOMAP for each file, so this shouldn't be called. */
   DEBUG(VM_DEBUG_NOTSUP, "HgfsMap() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsAddmap --
 *
 *    Since HgfsMap() above is ENOTSUP, this is not needed.
 *
 *    "Increments the map count." (Solaris Internals, p536)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsAddmap(struct vnode *vp,    // IN: Vnode to increment map count for
           offset_t off,        //XXX
           struct as *as,       // IN: Address space
           caddr_t addrp,       // IN: XXX: Address in this space?
           size_t len,          // IN: XXX: Length of this mapping?
           uchar_t prot,        // IN: XXX
           uchar_t maxprot,     // IN: XXX
           uint_t flags,        // IN: XXX
           struct cred *cr)     // IN: Credentials of caller
#else
static int
HgfsAddmap(struct vnode *vp,       // IN: Vnode to increment map count for
           offset_t off,           //XXX
           struct as *as,          // IN: Address space
           caddr_t addrp,          // IN: XXX: Address in this space?
           size_t len,             // IN: XXX: Length of this mapping?
           uchar_t prot,           // IN: XXX
           uchar_t maxprot,        // IN: XXX
           uint_t flags,           // IN: XXX
           struct cred *cr,        // IN: Credentials of caller
           caller_context_t *ctx)  // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsAddmap() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDelmap --
 *
 *    Since HgfsMap() above is ENOTSUP, this is not needed.
 *
 *    "Decrements the map count." (Solaris Internals, p536)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsDelmap(struct vnode *vp,    // IN: Vnode of file to decrement map count for
           offset_t off,        // XXX
           struct as *as,       // IN: Address space
           caddr_t addr,        // IN: XXX: Address in this space?
           size_t len,          // IN: XXX: Length of this mapping?
           uint_t prot,         // IN: XXX
           uint_t maxprot,      // IN: XXX
           uint_t flags,        // IN: XXX
           struct cred *cr)     // IN: Credentials of caller
#else
static int
HgfsDelmap(struct vnode *vp,    // IN: Vnode of file to decrement map count for
           offset_t off,        // XXX
           struct as *as,       // IN: Address space
           caddr_t addr,        // IN: XXX: Address in this space?
           size_t len,          // IN: XXX: Length of this mapping?
           uint_t prot,         // IN: XXX
           uint_t maxprot,      // IN: XXX
           uint_t flags,        // IN: XXX
           struct cred *cr,     // IN: Credentials of caller
           caller_context_t *ctx)
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsDelmap() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsPoll --
 *
 *    We are using fs_poll() instead of this, which seems acceptable so far.
 *
 *    Invoked when user calls poll(2) on a file in our filesystem.
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */
/*
int
HgfsPoll(struct vnode *vp,      // IN: Vnode of file to poll
         short ev,              // IN: Requested events
         int any,               // IN: Whether other file descriptors have had events
         short *revp,           // OUT: Filled in with events that have occurred
         struct pollhead **phpp)// OUT: Set to a pollhead if necessary
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsPoll() NOTSUP.\n");

   return ENOTSUP;
}*/


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDump --
 *
 *    "Dumps data when the kernel is in a frozen state." (Solaris Internals,
 *    p536)
 *
 * Results:
 *
 * Side effects:
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsDump(struct vnode *vp,      // IN: Vnode of XXX
         caddr_t addr,          // IN: XXX: Location to dump to?
         int lbdn,              // IN: XXX
         int dblks)             // IN: XXX
#else
static int
HgfsDump(struct vnode *vp,         // IN: Vnode of XXX
         caddr_t addr,             // IN: XXX: Location to dump to?
         offset_t lbdn,            // IN: XXX
         offset_t dblks,           // IN: XXX
         caller_context_t *ctx)    // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsDump() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsPathconf --
 *
 *    "Establishes file system parameters with the pathconf system call."
 *    (Solaris Internals, p537)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsPathconf(struct vnode *vp,  // IN: Vnode of file to establish parameters for
             int cmd,           // IN: Command
             ulong_t *valp,     // OUT: XXX: Returned value?
             struct cred *cr)   // IN: Credentials of caller
#else
static int
HgfsPathconf(struct vnode *vp,       // IN: Vnode of file to establish parameters for
             int cmd,                // IN: Command
             ulong_t *valp,          // OUT: XXX: Returned value?
             struct cred *cr,        // IN: Credentials of caller
             caller_context_t *ctx)  // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsPathconf() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsPageio --
 *
 *    "Paged I/O supprt for file system swap files." (Solaris Internals, p537)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsPageio(struct vnode *vp,
           struct page *pp,
           u_offset_t io_off,
           size_t io_len,
           int flags,
           struct cred *cr)
#else
static int
HgfsPageio(struct vnode *vp,
           struct page *pp,
           u_offset_t io_off,
           size_t io_len,
           int flags,
           struct cred *cr,
           caller_context_t *ctx)
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsPageio() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDumpctl --
 *
 *    "Prepares the file system before and after a dump" (Solaris Internals,
 *    p536)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsDumpctl(struct vnode *vp,
            int action,
            int *blkp)
#else
static int
HgfsDumpctl(struct vnode *vp,
            int action,
            offset_t *blkp,
            caller_context_t *ctx)
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsDumpctl() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDispose --
 *
 *    Since we don't map any parts of files to pages, this isn't needed.
 *
 *    "Frees the given page from the vnode." (Solaris Internals, p536)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static void
HgfsDispose(struct vnode *vp,   // IN: Vnode to free page for
            struct page *pp,    // IN: Page to free
            int flag,           // IN: XXX
            int dn,             // IN: XXX
            struct cred *cr)    // IN: Credentials of caller
#else
static void
HgfsDispose(struct vnode *vp,       // IN: Vnode to free page for
            struct page *pp,        // IN: Page to free
            int flag,               // IN: XXX
            int dn,                 // IN: XXX
            struct cred *cr,        // IN: Credentials of caller
            caller_context_t *ctx)  // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_ENTRY, "HgfsDispose().\n");
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSetsecattr --
 *
 *    "Sets security access control list attributes." (Solaris Internals,
 *    p538)
 *
 *    We almost certainly won't support this.
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION <= 3
static int
HgfsSetsecattr(struct vnode *vp,
               vsecattr_t *vsap,
               int flag,
               struct cred *cr)
#else
static int
HgfsSetsecattr(struct vnode *vp,
               vsecattr_t *vsap,
               int flag,
               struct cred *cr,
               caller_context_t *ctx)
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsSetsecattr() NOTSUP.\n");

   return ENOTSUP;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetsecattr --
 *
 *    We are using fs_fab_acl() instead of this, which seems to do the right
 *    thing.
 *
 *    "Gets security access control list attributes" (Solaris Internals, p536)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */
/*
int
HgfsGetsecattr(struct vnode *vp,
               vsecattr_t *vsap,
               int flag,
               struct cred *cr)
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsGetsecattr() NOTSUP.\n");

   return ENOTSUP;
}*/


/*
 *----------------------------------------------------------------------------
 *
 * HgfsShrlock --
 *
 *    "ONC shared lock support." (Solaris Internals, p538)
 *
 * Results:
 *
 * Side effects:
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION == 2
static int
HgfsShrlock(struct vnode *vp,           // IN: Vnode of file to lock
            int cmd,                    // IN: Command
            struct shrlock *chr,        // IN: Lock
            int flag)                   // IN: Flags for XXX
#elif HGFS_VFS_VERSION == 3
static int
HgfsShrlock(struct vnode *vp,           // IN: Vnode of file to lock
            int cmd,                    // IN: Command
            struct shrlock *chr,        // IN: Lock
            int flag,                   // IN: Flags for XXX
            cred_t *cr)                 // IN: Credentials of caller
#else
static int
HgfsShrlock(struct vnode *vp,           // IN: Vnode of file to lock
            int cmd,                    // IN: Command
            struct shrlock *chr,        // IN: Lock
            int flag,                   // IN: Flags for XXX
            cred_t *cr,                 // IN: Credentials of caller
            caller_context_t *ctx)      // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsShrlock() NOTSUP.\n");

   return ENOTSUP;
}



/*
 *----------------------------------------------------------------------------
 *
 * HgfsVnevent --
 *
 *    Handles an event for the provided vnode.
 *
 *    Events can be VE_SUPPORT, VE_RENAME_SRC, VE_RENAME_DEST, VE_REMOVE,
 *    VE_RMDIR.
 *
 *    Note that this function showed up at some point after Build 52 (02/2004)
 *    of Solaris 10 but before (or at) Build 58 (06/2004).  We only compile
 *    this in if the driver is being built for Builds greater than 52.
 *    XXX We should find out what build this function first showed up on.
 *
 * Results:
 *    Returns zero on success and a non-zero error code on error.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

#if HGFS_VFS_VERSION > 2
#if HGFS_VFS_VERSION == 3
static int
HgfsVnevent(struct vnode *vp,        // IN: Vnode the event is occuring to
            vnevent_t event)         // IN: Event that has occurred
#else
static int
HgfsVnevent(struct vnode *vp,        // IN: Vnode the event is occuring to
            vnevent_t event,         // IN: Event that has occurred
            vnode_t *dvp,            // IN: XXX
            char *fnm,               // IN: XXX
            caller_context_t *ctx)   // IN: Context of caller
#endif
{
   DEBUG(VM_DEBUG_NOTSUP, "HgfsVnevent: ENOTSUP\n");

   return ENOTSUP;
}
#endif


/*
 * Local vnode functions.
 *
 * (The rest of the functions in this file are only invoked by our code so they
 *  ASSERT() their pointer arguments.)
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDirOpen --
 *
 *    Invoked when HgfsOpen() is called with a vnode of type VDIR.
 *
 *    Sends a SEARCH_OPEN request to the Hgfs server.
 *
 * Results:
 *    Returns zero on success and an error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsDirOpen(HgfsSuperInfo *sip, // IN: Superinfo pointer
            struct vnode *vp)   // IN: Vnode of directory to open
{
   int ret;
   HgfsReq *req;
   HgfsRequestSearchOpen *request;
   HgfsReplySearchOpen *reply;

   ASSERT(sip);
   ASSERT(vp);

   DEBUG(VM_DEBUG_ENTRY, "HgfsDirOpen: opening \"%s\"\n", HGFS_VP_TO_FILENAME(vp));

   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   /* Set the correct header values */
   request = (HgfsRequestSearchOpen *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_SEARCH_OPEN);

   /*
    * Convert name to cross-platform and unescape.  If the vnode is the root of
    * our filesystem the Hgfs server expects an empty string.
    */
   ret = CPName_ConvertTo((HGFS_IS_ROOT_VNODE(sip, vp)) ? "" : HGFS_VP_TO_FILENAME(vp),
                          MAXPATHLEN, request->dirName.name);
   if (ret < 0) {
      ret = ENAMETOOLONG;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   request->dirName.length = ret;

   req->packetSize = request->dirName.length + sizeof *request;

   /* Submit the request to the Hgfs server */
   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      goto out;
   }

   /* Our reply is in the request packet */
   reply = (HgfsReplySearchOpen *)req->packet;

   /* Perform basic validation of packet transfer */
   if (HgfsValidateReply(req, sizeof reply->header) != 0) {
         DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen(): invalid reply received.\n");
         ret = EPROTO;
         goto out;
   }

   DEBUG(VM_DEBUG_COMM, "HgfsDirOpen: received reply for ID %d\n", reply->header.id);
   DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", reply->header.status);
   DEBUG(VM_DEBUG_COMM, " handle: %d\n", reply->search);

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed for [%s] with error %d.\n",
            __func__, HGFS_VP_TO_FILENAME(vp), ret);
      goto out;
   }

   if (req->packetSize != sizeof *reply) {
      DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: incorrect packet size.\n");
      ret = EIO;
      goto out;
   }

   /* Set the search open handle for use in HgfsReaddir() */
   ret = HgfsSetOpenFileHandle(vp, reply->search);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsDirOpen: couldn't assign handle=%d to %s\n",
         reply->search, HGFS_VP_TO_FILENAME(vp));
      req->state = HGFS_REQ_ERROR;
      ret = EINVAL;
      goto out;
   }

   DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);

out:
   /* Make sure we put the request back on the list */
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFileOpen --
 *
 *    Invoked when HgfsOpen() is called with a vnode of type VREG.  Sends
 *    a OPEN request to the Hgfs server.
 *
 *    Note that this function doesn't need to handle creations since the
 *    HgfsCreate() entry point is called by the kernel for that.
 *
 * Results:
 *    Returns zero on success and an error code on error.
 *
 * Side effects:
 *    None.
 *
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsFileOpen(HgfsSuperInfo *sip,        // IN: Superinfo pointer
             struct vnode *vp,        // IN: Vnode of file to open
             int flag,                  // IN: Flags of open
             int permissions)           // IN: Permissions of open (only when creating)
{
   HgfsReq *req;
   HgfsRequestOpen *request;
   HgfsReplyOpen *reply;
   int ret;

   ASSERT(sip);
   ASSERT(vp);

   DEBUG(VM_DEBUG_ENTRY, "HgfsFileOpen: opening \"%s\"\n", HGFS_VP_TO_FILENAME(vp));

   req = HgfsGetNewReq(sip);
   if (!req) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: HgfsGetNewReq failed.\n");
      return EIO;
   }

   request = (HgfsRequestOpen *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_OPEN);

   /* Convert Solaris modes to Hgfs modes */
   ret = HgfsGetOpenMode((uint32_t)flag);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: HgfsGetOpenMode failed.\n");
      ret = EINVAL;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   request->mode = ret;
   DEBUG(VM_DEBUG_COMM, "HgfsFileOpen: open mode is %x\n", request->mode);

   /* Convert Solaris flags to Hgfs flags */
   ret = HgfsGetOpenFlags((uint32_t)flag);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: HgfsGetOpenFlags failed.\n");
      ret = EINVAL;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   request->flags = ret;
   DEBUG(VM_DEBUG_COMM, "HgfsFileOpen: open flags are %x\n", request->flags);

   request->permissions = (permissions & S_IRWXU) >> HGFS_ATTR_MODE_SHIFT;
   DEBUG(VM_DEBUG_COMM, "HgfsFileOpen: permissions are %o\n", request->permissions);

   /* Convert the file name to cross platform format. */
   ret = CPName_ConvertTo(HGFS_VP_TO_FILENAME(vp), MAXPATHLEN, request->fileName.name);
   if (ret < 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: CPName_ConvertTo failed.\n");
      ret = ENAMETOOLONG;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }
   request->fileName.length = ret;

   /* Packet size includes the request and its payload. */
   req->packetSize = request->fileName.length + sizeof *request;

   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: could not submit request.\n");
      goto out;
   }

   reply = (HgfsReplyOpen *)req->packet;

   if (HgfsValidateReply(req, sizeof reply->header) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: request not valid.\n");
      ret = EPROTO;
      goto out;
   }

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed for [%s] with error %d.\n",
            __func__, HGFS_VP_TO_FILENAME(vp), ret);
      goto out;
   }

   if (req->packetSize != sizeof *reply) {
      DEBUG(VM_DEBUG_FAIL, "%s: size of reply is incorrect.\n", __func__);
      ret = EIO;
      goto out;
   }

   /*
    * We successfully received a reply, so we need to save the handle in
    * this file's HgfsOpenFile and return success.
    */
   ret = HgfsSetOpenFileHandle(vp, reply->file);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileOpen: couldn't assign handle %d (%s)\n",
            reply->file, HGFS_VP_TO_FILENAME(vp));
      req->state = HGFS_REQ_ERROR;
      ret = EINVAL;
      goto out;
   }

   DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDirClose --
 *
 *    Invoked when HgfsClose() is called with a vnode of type VDIR.
 *
 *    Sends an SEARCH_CLOSE request to the Hgfs server.
 *
 * Results:
 *    Returns zero on success and an error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsDirClose(HgfsSuperInfo *sip,        // IN: Superinfo pointer
             struct vnode *vp)          // IN: Vnode of directory to close
{
   HgfsReq *req;
   HgfsRequestSearchClose *request;
   HgfsReplySearchClose *reply;
   int ret;

   ASSERT(sip);
   ASSERT(vp);

   req = HgfsGetNewReq(sip);
   if (!sip) {
      return EIO;
   }

   /*
    * Prepare the request structure.  Of note here is that the request is
    * always the same size so we just set the packetSize to that.
    */
   request = (HgfsRequestSearchClose *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_SEARCH_CLOSE);

   /* Get this open file's handle, since that is what we want to close. */
   ret = HgfsGetOpenFileHandle(vp, &request->search);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsDirClose: couldn't get handle for %s\n",
            HGFS_VP_TO_FILENAME(vp));
      ret = EINVAL;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }
   req->packetSize = sizeof *request;

   /* Submit the request to the Hgfs server */
   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      goto out;
   }

   reply = (HgfsReplySearchClose *)req->packet;

   /* Ensure reply was received correctly and is necessary size. */
   if (HgfsValidateReply(req, sizeof reply->header) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsDirClose: invalid reply received.\n");
      ret = EPROTO;
      goto out;
   }

   DEBUG(VM_DEBUG_COMM, "HgfsDirClose: received reply for ID %d\n", reply->header.id);
   DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", reply->header.status);

   /* Ensure server was able to close directory. */
   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
      goto out;
   }

   /* Now clear this open file's handle for future use. */
   ret = HgfsClearOpenFileHandle(vp);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: couldn't clear handle.\n", __func__);
      ret = EINVAL;
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   /* The directory was closed successfully so we return success. */
   DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFileClose --
 *
 *    Invoked when HgfsClose() is called with a vnode of type VREG.
 *
 *    Sends a CLOSE request to the Hgfs server.
 *
 * Results:
 *    Returns zero on success and an error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsFileClose(HgfsSuperInfo *sip,       // IN: Superinfo pointer
              struct vnode *vp)         // IN: Vnode of file to close
{
   HgfsReq *req;
   HgfsRequestClose *request;
   HgfsReplyClose *reply;
   int ret;

   ASSERT(sip);
   ASSERT(vp);

   req = HgfsGetNewReq(sip);
   if (!req) {
      ret = EIO;
      goto out;
   }

   request = (HgfsRequestClose *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_CLOSE);

   /* Tell the Hgfs server which handle to close */
   ret = HgfsGetOpenFileHandle(vp, &request->file);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: couldn't get handle.\n");
      ret = EINVAL;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   req->packetSize = sizeof *request;

   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: submit request failed.\n");
      goto out;
   }

   if (HgfsValidateReply(req, sizeof *reply) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsFileClose: reply was invalid.\n");
      ret = EPROTO;
      goto out;
   }

   reply = (HgfsReplyClose *)req->packet;

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
      goto out;
   }

   /*
    * We already verified the size of the reply above since this reply type
    * only contains a header, so we just clear the handle and return success.
    */
   ret = HgfsClearOpenFileHandle(vp);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: couldn't clear handle.\n", __func__);
      ret = EINVAL;
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);

out:
   HgfsDestroyReq(sip, req);
   DEBUG(VM_DEBUG_DONE, "HgfsFileClose: returning %d\n", ret);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetNextDirEntry --
 *
 *    Writes the name of the directory entry matching the handle and offset to
 *    nameOut.  This requires sending a SEARCH_READ request.
 *
 * Results:
 *    Returns zero on success and an error code on error.  The done value is
 *    set if there are no more directory entries.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsGetNextDirEntry(HgfsSuperInfo *sip,         // IN: Superinfo pointer
                    HgfsHandle handle,          // IN: Handle for request
                    uint32_t offset,            // IN: Offset
                    char *nameOut,              // OUT: Location to write name
                    Bool *done)                 // OUT: Whether there are any more
{
   HgfsReq *req;
   HgfsRequestSearchRead *request;
   HgfsReplySearchRead *reply;
   int ret;

   DEBUG(VM_DEBUG_ENTRY,
         "HgfsGetNextDirEntry: handle=%d, offset=%d.\n", handle, offset);

   ASSERT(sip);
   ASSERT(nameOut);
   ASSERT(done);

   req = HgfsGetNewReq(sip);
   if (!req) {
      DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: couldn't get req.\n");
      return EIO;
   }

   /*
    * Fill out the search read request that will return a single directory
    * entry for the provided handle at the given offset.
    */
   request = (HgfsRequestSearchRead *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_SEARCH_READ);

   request->search = handle;
   request->offset = offset;

   req->packetSize = sizeof *request;

   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: HgfsSubmitRequest failed.\n");
      goto out;
   }

   reply = (HgfsReplySearchRead *)req->packet;

   /* Validate the request state and ensure we have at least a header */
   if (HgfsValidateReply(req, sizeof reply->header) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsGetNextDirEntry: reply not valid.\n");
      ret = EPROTO;
      goto out;
   }

   DEBUG(VM_DEBUG_COMM, "HgfsGetNextDirEntry: received reply for ID %d\n",
         reply->header.id);
   DEBUG(VM_DEBUG_COMM, " status: %d (see hgfsProto.h)\n", reply->header.status);

   /* Now ensure the server didn't have an error */
   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
      goto out;
   }

   /* Make sure we got an entire reply (excluding filename) */
   if (req->packetSize < sizeof *reply) {
      DEBUG(VM_DEBUG_FAIL, "%s: server didn't provide entire reply.\n",
            __func__);
      ret = EIO;
      goto out;
   }

   /* See if there are no more filenames to read */
   if (reply->fileName.length <= 0) {
      DEBUG(VM_DEBUG_DONE, "%s: no more directory entries.\n", __func__);
      *done = TRUE;
      ret = 0;         /* return success */
      goto out;
   }

   /* Make sure filename isn't too long */
   if ((reply->fileName.length > MAXNAMELEN) ||
       (reply->fileName.length > HGFS_PAYLOAD_MAX(reply)) ) {
      DEBUG(VM_DEBUG_FAIL, "%s: filename is too long.\n", __func__);
      ret = EOVERFLOW;
      goto out;
   }

   /*
    * Everything is all right, copy filename to caller's buffer.  Note that
    * Solaris directory entries don't need the attribute information in the
    * reply.
    */
   memcpy(nameOut, reply->fileName.name, reply->fileName.length);
   nameOut[reply->fileName.length] = '\0';
   ret = 0;

   DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDoRead --
 *
 *    Sends a single READ request to the Hgfs server and writes the contents
 *    into the user's buffer if successful.
 *
 *    This function is called repeatedly by HgfsRead() with requests of size
 *    less than or equal to HGFS_IO_MAX.
 *
 * Results:
 *   Returns 0 on success and a positive value on error.
 *
 * Side effects:
 *   On success, up to 'size' bytes are written into the user's buffer.
 *   Actual number of bytes written passed back in 'count' argument.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsDoRead(HgfsSuperInfo *sip,  // IN: Superinfo pointer
           HgfsHandle handle,   // IN: Server's handle to read from
           uint64_t offset,     // IN: File offset to read at
           uint32_t size,       // IN: Number of bytes to read
           uio_t *uiop,         // IN: Defines user's read request
           uint32_t *count)     // OUT: Number of bytes read
{
   HgfsReq *req;
   HgfsRequestRead *request;
   HgfsReplyRead *reply;
   int ret;

   ASSERT(sip);
   ASSERT(uiop);
   ASSERT(size <= HGFS_IO_MAX); // HgfsRead() should guarantee this
   ASSERT(count);

   DEBUG(VM_DEBUG_ENTRY, "%s: entry.\n", __func__);

   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   request = (HgfsRequestRead *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_READ);

   /* Indicate which file, where in the file, and how much to read. */
   request->file = handle;
   request->offset = offset;
   request->requiredSize = size;

   req->packetSize = sizeof *request;

   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: HgfsSubmitRequest failed.\n", __func__);
      goto out;
   }

   reply = (HgfsReplyRead *)req->packet;

   /* Ensure we got an entire header. */
   if (HgfsValidateReply(req, sizeof reply->header) != 0) {
      DEBUG(VM_DEBUG_FAIL, "%s: invalid reply received.\n", __func__);
      ret = EPROTO;
      goto out;
   }

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
      goto out;
   }

   /*
    * Now perform checks on the actualSize.  There are three cases:
    *  o actualSize is less than or equal to size, which indicates success
    *  o actualSize is zero, which indicates the end of the file (and success)
    *  o actualSize is greater than size, which indicates a server error
    */
   if (reply->actualSize > size) {
      /* We got too much data: server error. */
      DEBUG(VM_DEBUG_FAIL, "%s: received too much data in payload.\n",
            __func__);
      ret = EPROTO;
      goto out;
   }

   /* Perform the copy to the user if we have something to copy */
   if (reply->actualSize > 0) {
      ret = uiomove(reply->payload, reply->actualSize, UIO_READ, uiop);
      if (ret) {
         DEBUG(VM_DEBUG_FAIL, "%s: uiomove failed, rc: %d\n.",
               __func__, ret);
         goto out;
      }
   }

   *count = reply->actualSize;
   DEBUG(VM_DEBUG_DONE, "%s: successfully read %d bytes to user.\n",
         __func__, *count);

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDoWrite --
 *
 *    Sends a single WRITE request to the Hgfs server with the contents of
 *    the user's buffer.
 *
 *    This function is called repeatedly by HgfsWrite() with requests of size
 *    less than or equal to HGFS_IO_MAX.
 *
 * Results:
 *   Returns number 0 on success and a positive value on error.
 *
 * Side effects:
 *   On success, up to 'size' bytes are written to the file specified by the
 *   handle. Actual number of bytes written passed back in 'count' argument.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsDoWrite(HgfsSuperInfo *sip, // IN: Superinfo pointer
            HgfsHandle handle,  // IN: Handle representing file to write to
            int ioflag,         // IN: Flags for write
            uint64_t offset,    // IN: Where in the file to begin writing
            uint32_t size,      // IN: How much data to write
            uio_t *uiop,        // IN: Describes user's write request
            uint32_t *count)    // OUT: number of bytes written
{
   HgfsReq *req;
   HgfsRequestWrite *request;
   HgfsReplyWrite *reply;
   int ret;

   ASSERT(sip);
   ASSERT(uiop);
   ASSERT(size <= HGFS_IO_MAX); // HgfsWrite() guarantees this
   ASSERT(count);

   DEBUG(VM_DEBUG_ENTRY, "%s: entry.\n", __func__);

   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   request = (HgfsRequestWrite *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, HGFS_OP_WRITE);

   request->file = handle;
   request->flags = 0;
   request->offset = offset;
   request->requiredSize = size;

   if (ioflag & FAPPEND) {
      DEBUG(VM_DEBUG_COMM, "%s: writing in append mode.\n", __func__);
      request->flags |= HGFS_WRITE_APPEND;
   }

   DEBUG(VM_DEBUG_COMM, "%s: requesting write of %d bytes.\n",
         __func__, size);

   /* Copy the data the user wants to write into the payload. */
   ret = uiomove(request->payload, request->requiredSize, UIO_WRITE, uiop);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL,
            "%s: uiomove(9F) failed copying data from user.\n", __func__);
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   /* We subtract one so request's 'char payload[1]' member isn't double counted. */
   req->packetSize = sizeof *request + request->requiredSize - 1;

   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: HgfsSubmitRequest failed.\n", __func__);
      goto out;
   }

   reply = (HgfsReplyWrite *)req->packet;

   if (HgfsValidateReply(req, sizeof reply->header) != 0) {
      DEBUG(VM_DEBUG_FAIL, "%s: invalid reply received.\n", __func__);
      ret = EPROTO;
      goto out;
   }

   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
      goto out;
   }

   if (req->packetSize != sizeof *reply) {
      DEBUG(VM_DEBUG_FAIL, "%s: invalid size of reply on successful reply.\n",
            __func__);
      ret = EPROTO;
      goto out;
   }

   /* The write was completed successfully, so return the amount written. */
   *count = reply->actualSize;
   DEBUG(VM_DEBUG_DONE, "%s: wrote %d bytes.\n", __func__, *count);

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDelete --
 *
 *    Sends a request to delete a file or directory.
 *
 * Results:
 *    Returns 0 on success or an error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsDelete(HgfsSuperInfo *sip,          // IN: Superinfo
           char *filename,              // IN: Full name of file to remove
           HgfsOp op)                   // IN: Hgfs operation this delete is for
{
   HgfsReq *req;
   HgfsRequestDelete *request;
   HgfsReplyDelete *reply;
   int ret;

   ASSERT(sip);
   ASSERT(filename);
   ASSERT((op == HGFS_OP_DELETE_FILE) || (op == HGFS_OP_DELETE_DIR));

   DEBUG(VM_DEBUG_ENTRY, "HgfsDelete().\n");

   req = HgfsGetNewReq(sip);
   if (!req) {
      return EIO;
   }

   /* Initialize the request's contents. */
   request = (HgfsRequestDelete *)req->packet;
   HGFS_INIT_REQUEST_HDR(request, req, op);

   /* Convert filename to cross platform and unescape. */
   ret = CPName_ConvertTo(filename, MAXPATHLEN, request->fileName.name);
   if (ret < 0) {
      ret = ENAMETOOLONG;
      /* We need to set the request state to error before destroying. */
      req->state = HGFS_REQ_ERROR;
      goto out;
   }

   request->fileName.length = ret;

   /* Set the size of our request. (XXX should this be - 1 for char[1]?) */
   req->packetSize = sizeof *request + request->fileName.length;

   DEBUG(VM_DEBUG_COMM, "HgfsDelete: deleting \"%s\"\n", filename);

   /* Submit our request. */
   ret = HgfsSubmitRequest(sip, req);
   if (ret) {
      goto out;
   }

   reply = (HgfsReplyDelete *)req->packet;

   /* Check the request status and size of reply. */
   if (HgfsValidateReply(req, sizeof *reply) != 0) {
      DEBUG(VM_DEBUG_FAIL, "HgfsDelete: invalid reply received.\n");
      ret = EPROTO;
      goto out;
   }

   /* Return the appropriate value. */
   ret = HgfsStatusConvertToSolaris(reply->header.status);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "%s: failed with error %d.\n",
            __func__, ret);
   } else {
      DEBUG(VM_DEBUG_DONE, "%s: done.\n", __func__);
   }

out:
   HgfsDestroyReq(sip, req);
   return ret;
}


/*
 * Function(s) exported to Solaris Hgfs code
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsGetSuperInfo --
 *
 *    Provides a pointer to the superinfo structure as long as the filesystem
 *    is mounted.
 *
 * Results:
 *    Pointer to the superinfo on success, NULL on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

inline HgfsSuperInfo *
HgfsGetSuperInfo(void)
{
   return hgfsSuperInfo.vfsp ? &hgfsSuperInfo : NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsInitSuperInfo --
 *
 *    Initializes superinfo structure to indicate that filesystem has been
 *    mounted and can be used now.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsInitSuperInfo(struct vfs *vfsp) // IN: pointer to vfs being mounted
{
   hgfsSuperInfo.vfsp = vfsp;

   /* For now we are only using the backdoor transport. */
   hgfsSuperInfo.sendRequest = HgfsBackdoorSendRequest;
   hgfsSuperInfo.cancelRequest = HgfsBackdoorCancelRequest;
   hgfsSuperInfo.transportInit = HgfsBackdoorInit;
   hgfsSuperInfo.transportCleanup = HgfsBackdoorCleanup;

   HgfsInitRequestList(&hgfsSuperInfo);
   HgfsInitFileHashTable(&hgfsSuperInfo.fileHashTable);
}

/*
 *----------------------------------------------------------------------------
 *
 * HgfsClearSuperInfo --
 *
 *    Clears superinfo structure to indicate that filesystem has been
 *    unmounted.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsClearSuperInfo(void)
{
   hgfsSuperInfo.vfsp = NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSetVnodeOps --
 *
 *    Sets the vnode operations for the provided vnode.
 *
 * Results:
 *    Returns zero on success and a non-zero error code on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsSetVnodeOps(struct vnode *vp)       // IN: vnode for this file
{
   ASSERT(vp);

#if HGFS_VFS_VERSION == 2
   vp->v_op = &hgfsVnodeOps;
#else
   /* hgfsVnodeOpsP is set up when we mounted HGFS volume. */
   if (vn_getops(vp) == hgfsVnodeOpsP) {
      DEBUG(VM_DEBUG_INFO, "HgfsSetVnodeOps: vnode ops already set.\n");
   } else {
      DEBUG(VM_DEBUG_INFO, "HgfsSetVnodeOps: we had to set the vnode ops.\n");
      /* Set the operations for this vnode. */
      vn_setops(vp, hgfsVnodeOpsP);
   }
#endif

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsMakeVnodeOps --
 *
 *    Registers our vnode operations with the kernel.  After this function
 *    completes, all calls to vn_alloc() for our filesystem should return vnodes
 *    with the correct operations.
 *
 * Results:
 *    Return 0 on success and non-zero on failure.
 *
 * Side effects:
 *    The kernel allocates memory for our operations structure.
 *
 *----------------------------------------------------------------------------
 */

int
HgfsMakeVnodeOps(void)
{
#if HGFS_VFS_VERSION > 2
   int ret;
   static fs_operation_def_t vnodeOpsArr[] = {
      HGFS_VOP(VOPNAME_OPEN, vop_open, HgfsOpen),
      HGFS_VOP(VOPNAME_CLOSE, vop_close, HgfsClose),
      HGFS_VOP(VOPNAME_READ, vop_read, HgfsRead),
      HGFS_VOP(VOPNAME_WRITE, vop_write, HgfsWrite),
      HGFS_VOP(VOPNAME_IOCTL, vop_ioctl, HgfsIoctl),
      HGFS_VOP(VOPNAME_SETFL, vop_setfl, fs_setfl),
      HGFS_VOP(VOPNAME_GETATTR, vop_getattr, HgfsGetattr),
      HGFS_VOP(VOPNAME_SETATTR, vop_setattr, HgfsSetattr),
      HGFS_VOP(VOPNAME_ACCESS, vop_access, HgfsAccess),
      HGFS_VOP(VOPNAME_LOOKUP, vop_lookup, HgfsLookup),
      HGFS_VOP(VOPNAME_CREATE, vop_create, HgfsCreate),
      HGFS_VOP(VOPNAME_REMOVE, vop_remove, HgfsRemove),
      HGFS_VOP(VOPNAME_LINK, vop_link, HgfsLink),
      HGFS_VOP(VOPNAME_RENAME, vop_rename, HgfsRename),
      HGFS_VOP(VOPNAME_MKDIR, vop_mkdir, HgfsMkdir),
      HGFS_VOP(VOPNAME_RMDIR, vop_rmdir, HgfsRmdir),
      HGFS_VOP(VOPNAME_READDIR, vop_readdir, HgfsReaddir),
      HGFS_VOP(VOPNAME_SYMLINK, vop_symlink, HgfsSymlink),
      HGFS_VOP(VOPNAME_READLINK, vop_readlink, HgfsReadlink),
      HGFS_VOP(VOPNAME_FSYNC, vop_fsync, HgfsFsync),
#if HGFS_VFS_VERSION <= 3
      HGFS_VOP(VOPNAME_INACTIVE, vop_inactive, (fs_generic_func_p)HgfsInactive),
      HGFS_VOP(VOPNAME_RWLOCK, vop_rwlock, (fs_generic_func_p)HgfsRwlock),
      HGFS_VOP(VOPNAME_RWUNLOCK, vop_rwunlock, (fs_generic_func_p)HgfsRwunlock),
      HGFS_VOP(VOPNAME_MAP, vop_map, (fs_generic_func_p)HgfsMap),
      HGFS_VOP(VOPNAME_ADDMAP, vop_addmap, (fs_generic_func_p)HgfsAddmap),
      HGFS_VOP(VOPNAME_POLL, vop_poll, (fs_generic_func_p)fs_poll),
      HGFS_VOP(VOPNAME_DISPOSE, vop_dispose, (fs_generic_func_p)HgfsDispose),
#else
      HGFS_VOP(VOPNAME_INACTIVE, vop_inactive, HgfsInactive),
      HGFS_VOP(VOPNAME_RWLOCK, vop_rwlock, HgfsRwlock),
      HGFS_VOP(VOPNAME_RWUNLOCK, vop_rwunlock, HgfsRwunlock),
      HGFS_VOP(VOPNAME_MAP, vop_map, HgfsMap),
      HGFS_VOP(VOPNAME_ADDMAP, vop_addmap, HgfsAddmap),
      HGFS_VOP(VOPNAME_POLL, vop_poll, fs_poll),
      HGFS_VOP(VOPNAME_DISPOSE, vop_dispose, HgfsDispose),
#endif
      HGFS_VOP(VOPNAME_FID, vop_fid, HgfsFid),
      HGFS_VOP(VOPNAME_SEEK, vop_seek, HgfsSeek),
      HGFS_VOP(VOPNAME_CMP, vop_cmp, HgfsCmp),
      HGFS_VOP(VOPNAME_FRLOCK, vop_frlock, HgfsFrlock),
      HGFS_VOP(VOPNAME_SPACE, vop_space, HgfsSpace),
      HGFS_VOP(VOPNAME_REALVP, vop_realvp, HgfsRealvp),
      HGFS_VOP(VOPNAME_GETPAGE, vop_getpage, HgfsGetpage),
      HGFS_VOP(VOPNAME_PUTPAGE, vop_putpage, HgfsPutpage),
      HGFS_VOP(VOPNAME_DELMAP, vop_delmap, HgfsDelmap),
      HGFS_VOP(VOPNAME_DUMP, vop_dump, HgfsDump),
      HGFS_VOP(VOPNAME_PATHCONF, vop_pathconf, HgfsPathconf),
      HGFS_VOP(VOPNAME_PAGEIO, vop_pageio, HgfsPageio),
      HGFS_VOP(VOPNAME_DUMPCTL, vop_dumpctl, HgfsDumpctl),
      HGFS_VOP(VOPNAME_GETSECATTR, vop_getsecattr, fs_fab_acl),
      HGFS_VOP(VOPNAME_SETSECATTR, vop_setsecattr, HgfsSetsecattr),
      HGFS_VOP(VOPNAME_SHRLOCK, vop_shrlock, HgfsShrlock),
      HGFS_VOP(VOPNAME_VNEVENT, vop_vnevent, HgfsVnevent),
#if HGFS_VFS_VERSION <= 3
      { NULL, NULL }
#else
      { NULL, { NULL }                             }
#endif
   };

   DEBUG(VM_DEBUG_ENTRY, "HgfsMakeVnodeOps: making vnode ops.\n");

   /*
    * Create a vnodeops structure and register it with the kernel.
    * We save the operations structure so it can be assigned in the
    * future.
    */
   ret = vn_make_ops(HGFS_FS_NAME, vnodeOpsArr, &hgfsVnodeOpsP);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsMakeVnodeOps: vn_make_ops returned %d\n", ret);
      return ret;
   }

   DEBUG(VM_DEBUG_DONE, "HgfsMakeVnodeOps: hgfsVnodeOpsP=%p\n", hgfsVnodeOpsP);

#endif
   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsFreeVnodeOps --
 *
 *    Unregisters vnode operations from the kernel.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The kernel frees memory allocated for our operations structure.
 *
 *----------------------------------------------------------------------------
 */

void
HgfsFreeVnodeOps(void)
{
#if HGFS_VFS_VERSION > 2
   if (hgfsVnodeOpsP)
      vn_freevnodeops(hgfsVnodeOpsP);
#endif
}


/*
 * Local utility functions.
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSubmitRequest --
 *
 *    Sends request through the transport channel, then waits for
 *    the response.
 *
 *    Both submitting request and waiting for reply are in this function
 *    because the signaling of the request list's condition variable and
 *    waiting on the request's condition variable must be atomic.
 *
 * Results:
 *    Returns zero on success, and an appropriate error code on error.
 *    Note: EINTR is returned if cv_wait_sig() is interrupted.
 *
 * Side effects:
 *    The request list's condition variable is signaled.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsSubmitRequest(HgfsSuperInfo *sip,   // IN: Superinfo containing request list,
                                        //     condition variable, and mutex
                  HgfsReq *req)         // IN: Request to submit
{
   int ret = 0;

   ASSERT(sip);
   ASSERT(req);

   mutex_enter(&sip->reqMutex);

   if (sip->vfsp->vfs_flag & VFS_UNMOUNTED) {
      DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): filesystem not mounted.\n");
      ret = ENODEV;
      goto out;
   }

   ret = HgfsSendRequest(sip, req);
   if (ret) {
      DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): transport failed.\n");
      goto out;
   }

   /*
    * If we are using synchronous transport we should have the result right
    * here and status will not be equal HGFS_REQ_SUBMITTED. If we are using
    * async transport we'll sleep till somebody wakes us up.
    */

   while (req->state == HGFS_REQ_SUBMITTED) {
      k_sigset_t oldIgnoreSet;

      //DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest: currproc is %s.\n", u.u_comm);

      HgfsDisableSignals(&oldIgnoreSet);

      if (cv_wait_sig(&req->condVar, &sip->reqMutex) == 0) {
         /*
          * We received a system signal (e.g., SIGKILL) while waiting for the
          * reply.
          *
          * Since we gave up the mutex while waiting on the condition
          * variable, we must make sure the reply didn't come /after/ we were
          * signaled but /before/ we reacquired the mutex.  We do this by
          * checking the state to make sure it is still SUBMITTED.  (Note that
          * this case should be quite rare, but is possible.)
          *
          * If the reply has come, we ignore it (since we were interrupted) and
          * clean up the request.  Otherwise we set the state to ABANDONED so
          * the device half knows we are no longer waiting for the reply and it
          * can clean up for us.
          */
         HgfsRestoreSignals(&oldIgnoreSet);

         DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest(): interrupted while waiting for reply.\n");

         if (req->state != HGFS_REQ_SUBMITTED) {
            /* It it's not SUBMITTED, it must be COMPLETED or ERROR */
            ASSERT(req->state == HGFS_REQ_COMPLETED ||
                   req->state == HGFS_REQ_ERROR);
            DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): request not in submitted status.\n");
         } else {
            DEBUG(VM_DEBUG_REQUEST, "HgfsSubmitRequest(): setting request state to abandoned.\n");
            req->state = HGFS_REQ_ABANDONED;
         }

         ret = EINTR;
         goto out;
      }

      HgfsRestoreSignals(&oldIgnoreSet);
   }

   /* The reply should now be in req->packet. */
   DEBUG(VM_DEBUG_SIG, "HgfsSubmitRequest(): awoken because reply received.\n");

 out:
   mutex_exit(&sip->reqMutex);
   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsValidateReply --
 *
 *    Validates a reply to ensure that its state is set appropriately and the
 *    reply is at least the minimum expected size and not greater than the
 *    maximum allowed packet size.
 *
 * Results:
 *    Returns zero on success, and a non-zero on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsValidateReply(HgfsReq *req,         // IN: Request that contains reply data
                  uint32_t minSize)     // IN: Minimum size expected for the reply
{
   ASSERT(req);
   ASSERT(minSize <= HGFS_PACKET_MAX);  /* we want to know if this fails */

   switch (req->state) {
   case HGFS_REQ_ERROR:
      DEBUG(VM_DEBUG_FAIL, "HgfsValidateReply(): received reply with error.\n");
      return -1;

   case HGFS_REQ_COMPLETED:
      if ((req->packetSize < minSize) || (req->packetSize > HGFS_PACKET_MAX)) {
         DEBUG(VM_DEBUG_FAIL, "HgfsValidateReply(): successfully "
               "completed reply is too small/big: !(%d < %d < %d).\n",
               minSize, req->packetSize, HGFS_PACKET_MAX);
         return -1;
      } else {
         return 0;
      }
   /*
    * If we get here then there is a programming error in this module:
    *  HGFS_REQ_UNUSED should be for requests in the free list
    *  HGFS_REQ_SUBMITTED should be for requests only that are awaiting
    *                     a response
    *  HGFS_REQ_ABANDONED should have returned an error to the client
    */
   default:
      NOT_REACHED();
      return -1;        /* avoid compiler warning */
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsStatusConvertToSolaris --
 *
 *    Convert a cross-platform HGFS status code to its 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 Solaris status codes.
 *
 * Results:
 *    Zero if the converted status code represents success, positive error
 *    otherwise. Unknown status codes are converted to EPROTO.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsStatusConvertToSolaris(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:
      DEBUG(VM_DEBUG_LOG,
            "VMware hgfs: %s: unknown error: %u\n", __func__, hgfsStatus);
      return EPROTO;
   }
}


/*
 * XXX
 * These were taken and slightly modified from hgfs/driver/linux/driver.c.
 * Should we move them into a hgfs/driver/posix/driver.c?
 */


/*
 *----------------------------------------------------------------------
 *
 * 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
 *
 *----------------------------------------------------------------------
 */

static int
HgfsGetOpenMode(uint32 flags) // IN: Open flags
{
#ifdef sun
   /*
    * Sun uses different values in the kernel.  These are defined in
    * <sys/file.h>.
    */
   #undef O_RDONLY
   #undef O_WRONLY
   #undef O_RDWR

   #define O_RDONLY     FREAD
   #define O_WRONLY     FWRITE
   #define O_RDWR       (FREAD | FWRITE)
#endif

   uint32 mask = O_RDONLY|O_WRONLY|O_RDWR;
   int result = -1;

#ifndef sun
   LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenMode: entered\n"));
#else
   DEBUG(VM_DEBUG_LOG, "HgfsGetOpenMode: entered\n");
#endif

   /*
    * Mask the flags to only look at the access type.
    */
   flags &= mask;

   /* Pick the correct HgfsOpenMode. */
   switch (flags) {

   case O_RDONLY:
      DEBUG(VM_DEBUG_COMM, "HgfsGetOpenMode: O_RDONLY\n");
      result = HGFS_OPEN_MODE_READ_ONLY;
      break;

   case O_WRONLY:
      DEBUG(VM_DEBUG_COMM, "HgfsGetOpenMode: O_WRONLY\n");
      result = HGFS_OPEN_MODE_WRITE_ONLY;
      break;

   case O_RDWR:
      DEBUG(VM_DEBUG_COMM, "HgfsGetOpenMode: O_RDWR\n");
      result = HGFS_OPEN_MODE_READ_WRITE;
      break;

   default:
      /* This should never happen. */
      NOT_REACHED();
#ifndef sun
      LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenMode: invalid open flags %o\n",
             flags));
#else
      DEBUG(VM_DEBUG_LOG, "HgfsGetOpenMode: invalid open flags %o\n", flags);
#endif
      result = -1;
      break;
   }

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * 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
{
#ifdef sun
   /*
    * Sun uses different values inside the kernel.  These are defined in
    * <sys/file.h>.
    */
   #undef O_CREAT       // Must undef b/c included <sys/fs_subr.h>
   #undef O_TRUNC
   #undef O_EXCL

   #define O_CREAT      FCREAT
   #define O_TRUNC      FTRUNC
   #define O_EXCL       FEXCL
#endif

   uint32 mask = O_CREAT | O_TRUNC | O_EXCL;
   int result = -1;

#ifndef sun
   LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: entered\n"));
#else
   DEBUG(VM_DEBUG_INFO, "HgfsGetOpenFlags: entered\n");
#endif

   /*
    * 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. */
      DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: 0\n");
      result = HGFS_OPEN;
      break;

   case O_CREAT:
      /* Create file; if it exists already just open it. */
      DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: O_CREAT\n");
      result = HGFS_OPEN_CREATE;
      break;

   case O_TRUNC:
      /* Truncate existing file; fails if nonexistant. */
      DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: O_TRUNC\n");
      result = HGFS_OPEN_EMPTY;
      break;

   case (O_CREAT | O_EXCL):
      /* Create file; fail if it exists already. */
      DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: O_CREAT | O_EXCL\n");
      result = HGFS_OPEN_CREATE_SAFE;
      break;

   case (O_CREAT | O_TRUNC):
      /* Create file; if it exists already, truncate it. */
      DEBUG(VM_DEBUG_COMM, "HgfsGetOpenFlags: O_CREAT | O_TRUNC\n");
      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.
       */
#ifndef sun
      LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: invalid open "
              "flags %o. Ignoring the O_TRUNC flag.\n", flags));
#else
      DEBUG(VM_DEBUG_INFO, "HgfsGetOpenFlags: invalid open flags %o.  "
            "Ignoring the O_TRUNC flag.\n", flags);
#endif
      result = HGFS_OPEN_CREATE_SAFE;
      break;
   }

   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsAttrToSolaris --
 *
 *    Maps Hgfs attributes to Solaris attributes, filling the provided Solaris
 *    attribute structure appropriately.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
HgfsAttrToSolaris(struct vnode *vp,             // IN:  The vnode for this file
                  const HgfsAttr *hgfsAttr,     // IN:  Hgfs attributes to copy from
                  struct vattr *solAttr)        // OUT: Solaris attributes to fill
{
   ASSERT(vp);
   ASSERT(hgfsAttr);
   ASSERT(solAttr);

   DEBUG(VM_DEBUG_ENTRY, "HgfsAttrToSolaris: %p -> %p", hgfsAttr, solAttr);

   /*
    * We only fill in those fields that va_mask tells us to.
    */

   if (solAttr->va_mask & AT_TYPE) {
      /* Set the file type. */
      switch (hgfsAttr->type) {
      case HGFS_FILE_TYPE_REGULAR:
         solAttr->va_type = VREG;
         DEBUG(VM_DEBUG_ATTR, " Type: VREG\n");
         break;

      case HGFS_FILE_TYPE_DIRECTORY:
         solAttr->va_type = VDIR;
         DEBUG(VM_DEBUG_ATTR, " Type: VDIR\n");
         break;

      default:
         /*
          * There are only the above two filetypes.  If there is an error
          * elsewhere that provides another value, we set the Solaris type to
          * none and ASSERT in devel builds.
          */
         solAttr->va_type = VNON;
         DEBUG(VM_DEBUG_FAIL, "HgfsAttrToSolaris: invalid HgfsFileType provided.\n");
         ASSERT(0);
      }
   }

   if (solAttr->va_mask & AT_MODE) {
      /* We only have permissions for owners. */
      solAttr->va_mode = (hgfsAttr->permissions << HGFS_ATTR_MODE_SHIFT);
      DEBUG(VM_DEBUG_ATTR, " Owner's permissions: %o\n",
            solAttr->va_mode >> HGFS_ATTR_MODE_SHIFT);
   }

   if (solAttr->va_mask & AT_UID) {
      DEBUG(VM_DEBUG_ATTR, " Setting uid\n");
      solAttr->va_uid = 0;                 /* XXX root? */
   }

   if (solAttr->va_mask & AT_GID) {
      DEBUG(VM_DEBUG_ATTR, " Setting gid\n");
      solAttr->va_gid = 0;                 /* XXX root? */
   }

   if (solAttr->va_mask & AT_FSID) {
      DEBUG(VM_DEBUG_ATTR, " Setting fsid\n");
      solAttr->va_fsid = vp->v_vfsp->vfs_dev;
   }

   if (solAttr->va_mask & AT_NODEID) {
      /* Get the node id calculated for this file in HgfsVnodeGet() */
      solAttr->va_nodeid = HGFS_VP_TO_NODEID(vp);
      DEBUG(VM_DEBUG_ATTR, "*HgfsAttrToSolaris: fileName %s\n",
            HGFS_VP_TO_FILENAME(vp));
      DEBUG(VM_DEBUG_ATTR, " Node ID: %llu\n", solAttr->va_nodeid);
   }

   if (solAttr->va_mask & AT_NLINK) {
      DEBUG(VM_DEBUG_ATTR, " Setting nlink\n");
      solAttr->va_nlink = 1;               /* fake */
   }

   if (solAttr->va_mask & AT_SIZE) {
      DEBUG(VM_DEBUG_ATTR, " Setting size\n");
      solAttr->va_size = hgfsAttr->size;
   }

   if (solAttr->va_mask & AT_ATIME) {
      DEBUG(VM_DEBUG_ATTR, " Setting atime\n");
      HGFS_SET_TIME(solAttr->va_atime, hgfsAttr->accessTime);
   }

   if (solAttr->va_mask & AT_MTIME) {
      DEBUG(VM_DEBUG_ATTR, " Setting mtime\n");
      HGFS_SET_TIME(solAttr->va_mtime, hgfsAttr->writeTime);
   }

   if (solAttr->va_mask & AT_CTIME) {
      DEBUG(VM_DEBUG_ATTR, " Setting ctime\n");
      /* Since Windows doesn't keep ctime, we may need to use mtime instead. */
      if (HGFS_SET_TIME(solAttr->va_ctime, hgfsAttr->attrChangeTime)) {
         solAttr->va_ctime = solAttr->va_mtime;
      }
   }

   if (solAttr->va_mask & AT_RDEV) {
      DEBUG(VM_DEBUG_ATTR, " Setting rdev\n");
      /* Since Windows doesn't keep ctime, we may need to use mtime instead. */
      solAttr->va_rdev = 0;                /* devices aren't allowed in Hgfs */
   }

   if (solAttr->va_mask & AT_BLKSIZE) {
      DEBUG(VM_DEBUG_ATTR, " Setting blksize\n");
      /* Since Windows doesn't keep ctime, we may need to use mtime instead. */
      solAttr->va_blksize = HGFS_BLOCKSIZE;
   }

   if (solAttr->va_mask & AT_NBLOCKS) {
      DEBUG(VM_DEBUG_ATTR, " Setting nblocks\n");
      solAttr->va_nblocks = (solAttr->va_size / HGFS_BLOCKSIZE) + 1;
   }

#if HGFS_VFS_VERSION == 2
   if (solAttr->va_mask & AT_VCODE) {
      DEBUG(VM_DEBUG_ATTR, " Setting vcode\n");
      solAttr->va_vcode = 0;               /* fake */
   }
#else
   if (solAttr->va_mask & AT_SEQ) {
      DEBUG(VM_DEBUG_ATTR, " Setting seq\n");
      solAttr->va_seq = 0;                /* fake */
   }
#endif

   HgfsDebugPrintVattr(solAttr);
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsSetattrCopy --
 *
 *    Sets the Hgfs attributes that need to be modified based on the provided
 *    Solaris attribute structure.
 *
 * Results:
 *    Returns TRUE if changes need to be made, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
HgfsSetattrCopy(struct vattr *solAttr,          // IN:  Attributes to change to
                int flags,                      // IN:  Context of HgfsSetattr call
                HgfsAttr *hgfsAttr,             // OUT: Hgfs attributes to fill in
                HgfsAttrChanges *update)        // OUT: Hgfs attribute changes to make
{
   uint32_t mask;
   Bool ret = FALSE;

   ASSERT(solAttr);
   ASSERT(hgfsAttr);
   ASSERT(update);

   memset(hgfsAttr, 0, sizeof *hgfsAttr);
   memset(update, 0, sizeof *update);

   /* This is the mask of attributes to change. */
   mask = solAttr->va_mask;

   /*
    * Hgfs supports changing these attributes:
    * o mode bits (permissions)
    * o size
    * o access/write times
    */

   if (mask & AT_MODE) {
      DEBUG(VM_DEBUG_COMM, "HgfsSetattrCopy: updating permissions.\n");
      *update |= HGFS_ATTR_PERMISSIONS;
      hgfsAttr->permissions = (solAttr->va_mode & S_IRWXU) >> HGFS_ATTR_MODE_SHIFT;
      ret = TRUE;
   }

   if (mask & AT_SIZE) {
      DEBUG(VM_DEBUG_COMM, "HgfsSetattrCopy: updating size.\n");
      *update |= HGFS_ATTR_SIZE;
      hgfsAttr->size = solAttr->va_size;
      ret = TRUE;
   }

   if (mask & AT_ATIME) {
      DEBUG(VM_DEBUG_COMM, "HgfsSetattrCopy: updating access time.\n");
      *update |= HGFS_ATTR_ACCESS_TIME |
                 ((flags & ATTR_UTIME) ? HGFS_ATTR_ACCESS_TIME_SET : 0);
      hgfsAttr->accessTime = HGFS_GET_TIME(solAttr->va_atime);
      ret = TRUE;
   }

   if (mask & AT_MTIME) {
      DEBUG(VM_DEBUG_COMM, "HgfsSetattrCopy: updating write time.\n");
      *update |= HGFS_ATTR_WRITE_TIME |
                 ((flags & ATTR_UTIME) ? HGFS_ATTR_WRITE_TIME_SET : 0);
      hgfsAttr->writeTime = HGFS_GET_TIME(solAttr->va_mtime);
      ret = TRUE;
   }

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsMakeFullName --
 *
 *    Concatenates the path and filename to construct the full path.  This
 *    handles the special cases of . and .. filenames so the Hgfs server
 *    doesn't return an error.
 *
 * Results:
 *    Returns the length of the full path on success, and a negative value on
 *    error.  The full pathname is placed in outBuf.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
HgfsMakeFullName(const char *path,      // IN:  Path of directory containing file
                 uint32_t pathLen,      // IN:  Length of path
                 const char *file,      // IN:  Name of file
                 char *outBuf,          // OUT: Location to write full path
                 ssize_t bufSize)       // IN:  Size of the out buffer
{

   ASSERT(path);
   ASSERT(file);
   ASSERT(outBuf);


   DEBUG(VM_DEBUG_INFO, "HgfsMakeFullName:\n"
         " path: \"%s\" (%d)\n"
         " file: \"%s\" (%ld)\n",
         path, pathLen, file, (long) strlen(file));

   /*
    * Here there are three possibilities:
    *  o file is ".", in which case we just place path in outBuf
    *  o file is "..", in which case we strip the last component from path and
    *    put that in outBuf
    *  o for all other cases, we concatenate path, a path separator, file, and
    *    a NUL terminator and place it in outBuf
    */

   /* Make sure that the path and a NUL terminator will fit. */
   if (bufSize < pathLen + 1) {
      return HGFS_ERR_INVAL;
   }


   /* Copy path for this file into the caller's buffer. */
   memset(outBuf, 0, bufSize);
   memcpy(outBuf, path, pathLen);

   /* Handle three cases. */
   if (strcmp(file, ".") == 0) {
      /* NUL terminate and return provided length. */
      outBuf[pathLen] = '\0';
      return pathLen;

   } else if (strcmp(file, "..") == 0) {
      /*
       * Replace the last path separator with a NUL terminator, then return the
       * size of the buffer.
       */
      char *newEnd = strrchr(outBuf, '/');
      if (!newEnd) {
         /*
          * We should never get here since we name the root vnode "/" in
          * HgfsMount().
          */
         return HGFS_ERR_INVAL;
      }

      *newEnd = '\0';
      return ((uintptr_t)newEnd - (uintptr_t)outBuf);

   } else {

      /*
       * The full path consists of path, the path separator, file, plus a NUL
       * terminator.  Make sure it will all fit.
       */
      int fileLen = strlen(file);
      if (bufSize < pathLen + 1 + fileLen + 1) {
         return HGFS_ERR_INVAL;
      }

      /*
       * The CPName_ConvertTo function handles multiple path separators
       * at the beginning of the filename, so we skip the checks to limit
       * them to one.  This also enables clobbering newEnd above to work
       * properly on base shares (named "//sharename") that need to turn into
       * "/".
       */
      outBuf[pathLen] = '/';

      /* Now append the filename and NUL terminator. */
      memcpy(outBuf + pathLen + 1, file, fileLen);
      outBuf[pathLen + 1 + fileLen] = '\0';

      return pathLen + 1 + fileLen;
   }
}


/*
 * Process signal mask manipulation
 */


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDisableSignals --
 *
 *    Disables signals of current thread by calling sigintr().
 *
 * Results:
 *    Returns the old set of signals this process ignores.
 *
 * Side effects:
 *    This process is now only delivered SIGKILL sinals.
 *
 *----------------------------------------------------------------------------
 */

INLINE static void
HgfsDisableSignals(k_sigset_t *oldIgnoreSet)    // OUT: Current thread's ignore set
{
   ASSERT(oldIgnoreSet);

   /*
    * Passing sigintr() a 1 ensures that SIGINT will not be blocked.
    */
   sigintr(oldIgnoreSet, 1);

   /*
    * Note that the following alone works for Netscape ...
    * sigaddset(&curthread->t_hold, SIGALRM);
    */
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsRestoreSignals --
 *
 *    Restores the current process' set of signals to ignore to the provided
 *    signal set.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The process will now be delivered signals as dictated by the oldSigSet.
 *
 *----------------------------------------------------------------------------
 */

INLINE static void
HgfsRestoreSignals(k_sigset_t *oldIgnoreSet)
{
   ASSERT(oldIgnoreSet);

   /*
    * sigunintr() will replace the current thread's blocked signals with the
    * provided signal set.
    */
   sigunintr(oldIgnoreSet);

   /*
    * Note that the following alone works for Netscape ...
    * sigdelset(&curthread->t_hold, SIGALRM);
    */
}


/*
 * This is a less-tested, alternate implementation of HgfsReaddir().  The
 * difference is that this one copies each entry individually so it doesn't
 * have to a malloc() a buffer of size readSize (see the XXX comment in the
 * HgfsReaddir() implementation above).  The thinking is that this approach is
 * likely safer, but has the potential to be slower.  Initial tests show that
 * this implementation "feels" the same speed as the other one.
 */
#if 0
static int
HgfsReaddir(struct vnode *vp,   // IN: Vnode of directory to read
            struct uio *uiop,   // IN: User's read request
            struct cred *cr,    // IN: Credentials of caller
            int *eofp)          // OUT: Indicates we are done
{
   HgfsSuperInfo *sip;
   HgfsHandle handle;
   struct dirent64 *dirp;
   char buf[sizeof *dirp + MAXNAMELEN];
   ssize_t readSize;
   uint64_t offset;
   Bool done;
   int ret;

   DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir().\n");

   if (!vp || !uiop || !cr || !eofp) {
      cmn_err(HGFS_ERROR, "HgfsReaddir: NULL input from Kernel.\n");
      return EINVAL;
   }

   DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir: uiop->uio_resid=%d, uiop->uio_loffset=%d\n",
         uiop->uio_resid, uiop->uio_loffset);


   /*
    * XXX: If would be nice if we could perform some sort of confidence check on
    * the handle here.  Perhaps make sure handle <= NUM_SEARCHES in
    * hgfsServer.c since the handle is the index number in searchArray.
    */
   if ( !HGFS_KNOW_FILENAME(vp) ) {
      DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: we don't know the filename.\n");
      return EBADF;
   }

   sip = HgfsGetSuperInfo();
   if (!sip) {
      DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: we can't get the superinfo.\n");
      return EIO;
   }

   /*
    * In order to fill the user's buffer with directory entries, we must
    * iterate on HGFS_OP_SEARCH_READ requests until either the user's buffer is
    * full or there are no more entries.  Each call to HgfsGetNextDirEntry()
    * fills in the name and attribute structure for the next entry.  We then
    * escape the name, create the directory entry in our temporary buf, and
    * copy the entry to the user's buffer.
    */

   readSize = uiop->uio_resid;
   dirp = (struct dirent64 *)buf;

   /*
    * We need to get the handle for this open directory to send to the Hgfs
    * server in our requests.
    */
   ret = HgfsGetOpenFileHandle(vp, &handle);
   if (ret) {
      DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: could not get handle.\n");
      return EINVAL;
   }

   /*
    * Loop until one of the following conditions is met:
    *  o An error occurs while reading a directory entry
    *  o There are no more directory entries to read
    *  o The buffer is full and cannot hold the next entry
    *
    * We request dentries from the Hgfs server based on their index in the
    * directory.  The offset value is initialized to the value specified in
    * the user's io request and is incremented each time through the loop.
    *
    * We decrement readSize by the size of the directory entry each time we
    * successfully copy one into the user's buffer.
    */
   for (offset = uiop->uio_loffset, done = 0; /* Nothing */ ; offset++) {
      char nameBuf[MAXNAMELEN + 1];
      char escName[MAXNAMELEN + 1];
      char fullName[MAXPATHLEN + 1];

      DEBUG(VM_DEBUG_COMM,
            "HgfsReaddir: getting directory entry at offset %d.\n", offset);

      memset(nameBuf, 0, sizeof nameBuf);
      memset(buf, 0, sizeof buf);

      ret = HgfsGetNextDirEntry(sip, handle, offset, nameBuf, &done);
      /* If the filename was too long, we skip to the next entry ... */
      if (ret == EOVERFLOW) {
         continue;
      /* ... but if another error occurred, we return that error code ... */
      } else if (ret) {
         DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: failure occurred in HgfsGetNextDirEntry\n");
         goto out;
      /*
       * ... and if there are no more entries, we set the end of file pointer
       * and break out of the loop.
       */
      } else if (done == TRUE) {
         DEBUG(VM_DEBUG_COMM, "HgfsReaddir: Done reading directory entries.\n");
         *eofp = TRUE;
         break;
      }

      /*
       * We now have the directory entry, so we sanitize the name and try to
       * put it in our buffer.
       */
      DEBUG(VM_DEBUG_COMM, "HgfsReaddir: received filename \"%s\"\n", nameBuf);

      memset(escName, 0, sizeof escName);

      ret = HgfsEscape_Do(nameBuf, strlen(nameBuf), MAXNAMELEN, escName);
      /* If the escaped name didn't fit in the buffer, skip to the next entry. */
      if (ret < 0 || ret > MAXNAMELEN) {
         DEBUG(VM_DEBUG_FAIL, "HgfsReaddir: HgfsEscape_Do failed.\n");
         continue;
      }

      /*
       * Make sure there is enough room in the buffer for the entire directory
       * entry.  If not, we just break out of the loop and copy what we have.
       */
      if (DIRENT64_RECLEN(ret) > readSize) {
         DEBUG(VM_DEBUG_INFO, "HgfsReaddir: ran out of room in the buffer.\n");
         break;
      }

      /* Fill in the directory entry. */
      dirp->d_reclen = DIRENT64_RECLEN(ret);
      dirp->d_off = offset;
      memcpy(dirp->d_name, escName, ret);
      dirp->d_name[ret] = '\0';

      ret = HgfsMakeFullName(HGFS_VP_TO_FILENAME(vp),           // Directorie's name
                             HGFS_VP_TO_FILENAME_LENGTH(vp),    // Length
                             dirp->d_name,                      // Name of file
                             fullName,                          // Destination buffer
                             sizeof fullName);                  // Size of this buffer
      /* Skip this entry if the full path was too long. */
      if (ret < 0) {
         continue;
      }

      /*
       * Place the node id, which serves the purpose of inode number, for this
       * filename directory entry.  As long as we are using a dirent64, this is
       * okay since ino_t is also a u_longlong_t.
       */
      HgfsNodeIdGet(&sip->fileHashTable, fullName, (uint32_t)ret,
                    (u_longlong_t *)&dirp->d_ino);

      /*
       * Now that we've filled our buffer with as many dentries as fit, we copy it
       * into the user's buffer.
       */
      ret = uiomove(dirp,               // Source buffer
                    dirp->d_reclen,     // Size of this buffer
                    UIO_READ,           // Read flag
                    uiop);              // User's request struct

      /* Break the loop if we can't copy this dentry into the user's buffer. */
      if (ret) {
         goto out;
      }

      /* Decrement the number of bytes copied on success */
      readSize -= dirp->d_reclen;
   }

   /* Return success */
   ret = 0;

out:
   /*
    * uiomove(9F) will have incremented the uio offset by the number of bytes
    * written.  We reset it here to the fs-specific offset in our directory so
    * the next time we are called it is correct.  (Note, this does not break
    * anything and /is/ how this field is intended to be used.)
    */
   uiop->uio_loffset = offset;  // XXX ok to do this on error too?

   DEBUG(VM_DEBUG_DONE, "HgfsReaddir: done (ret=%d, *eofp=%d).\n", ret, *eofp);
   DEBUG(VM_DEBUG_ENTRY, "HgfsReaddir: exiting.\n");
   return ret;
}
#endif
 07070100000298000081A400000000000000000000000168225505000003CD000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmhgfs/vnode.h /*********************************************************
 * Copyright (C) 2004-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vnode.h --
 *
 * Functions exported by vnode.c
 */

#ifndef __VNODE_H_
#define __VNODE_H_

#include "hgfsSolaris.h"

int HgfsSetVnodeOps(struct vnode *vp);
inline HgfsSuperInfo *HgfsGetSuperInfo(void);
void HgfsInitSuperInfo(struct vfs *vfsp);
void HgfsClearSuperInfo(void);
int HgfsMakeVnodeOps(void);
void HgfsFreeVnodeOps(void);

#endif
   07070100000299000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmmemctl   0707010000029A000081A400000000000000000000000168225505000046CA000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmmemctl/COPYING   
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0

1. Definitions.

    1.1. "Contributor" means each individual or entity that creates
         or contributes to the creation of Modifications.

    1.2. "Contributor Version" means the combination of the Original
         Software, prior Modifications used by a Contributor (if any),
         and the Modifications made by that particular Contributor.

    1.3. "Covered Software" means (a) the Original Software, or (b)
         Modifications, or (c) the combination of files containing
         Original Software with files containing Modifications, in
         each case including portions thereof.

    1.4. "Executable" means the Covered Software in any form other
         than Source Code.

    1.5. "Initial Developer" means the individual or entity that first
         makes Original Software available under this License.

    1.6. "Larger Work" means a work which combines Covered Software or
         portions thereof with code not governed by the terms of this
         License.

    1.7. "License" means this document.

    1.8. "Licensable" means having the right to grant, to the maximum
         extent possible, whether at the time of the initial grant or
         subsequently acquired, any and all of the rights conveyed
         herein.

    1.9. "Modifications" means the Source Code and Executable form of
         any of the following:

        A. Any file that results from an addition to, deletion from or
           modification of the contents of a file containing Original
           Software or previous Modifications;

        B. Any new file that contains any part of the Original
           Software or previous Modifications; or

        C. Any new file that is contributed or otherwise made
           available under the terms of this License.

    1.10. "Original Software" means the Source Code and Executable
          form of computer software code that is originally released
          under this License.

    1.11. "Patent Claims" means any patent claim(s), now owned or
          hereafter acquired, including without limitation, method,
          process, and apparatus claims, in any patent Licensable by
          grantor.

    1.12. "Source Code" means (a) the common form of computer software
          code in which modifications are made and (b) associated
          documentation included in or with such code.

    1.13. "You" (or "Your") means an individual or a legal entity
          exercising rights under, and complying with all of the terms
          of, this License.  For legal entities, "You" includes any
          entity which controls, is controlled by, or is under common
          control with You.  For purposes of this definition,
          "control" means (a) the power, direct or indirect, to cause
          the direction or management of such entity, whether by
          contract or otherwise, or (b) ownership of more than fifty
          percent (50%) of the outstanding shares or beneficial
          ownership of such entity.

2. License Grants.

    2.1. The Initial Developer Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, the Initial
    Developer hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Initial Developer, to use,
            reproduce, modify, display, perform, sublicense and
            distribute the Original Software (or portions thereof),
            with or without Modifications, and/or as part of a Larger
            Work; and

        (b) under Patent Claims infringed by the making, using or
            selling of Original Software, to make, have made, use,
            practice, sell, and offer for sale, and/or otherwise
            dispose of the Original Software (or portions thereof).

        (c) The licenses granted in Sections 2.1(a) and (b) are
            effective on the date Initial Developer first distributes
            or otherwise makes the Original Software available to a
            third party under the terms of this License.

        (d) Notwithstanding Section 2.1(b) above, no patent license is
            granted: (1) for code that You delete from the Original
            Software, or (2) for infringements caused by: (i) the
            modification of the Original Software, or (ii) the
            combination of the Original Software with other software
            or devices.

    2.2. Contributor Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, each
    Contributor hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Contributor to use, reproduce,
            modify, display, perform, sublicense and distribute the
            Modifications created by such Contributor (or portions
            thereof), either on an unmodified basis, with other
            Modifications, as Covered Software and/or as part of a
            Larger Work; and

        (b) under Patent Claims infringed by the making, using, or
            selling of Modifications made by that Contributor either
            alone and/or in combination with its Contributor Version
            (or portions of such combination), to make, use, sell,
            offer for sale, have made, and/or otherwise dispose of:
            (1) Modifications made by that Contributor (or portions
            thereof); and (2) the combination of Modifications made by
            that Contributor with its Contributor Version (or portions
            of such combination).

        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
            effective on the date Contributor first distributes or
            otherwise makes the Modifications available to a third
            party.

        (d) Notwithstanding Section 2.2(b) above, no patent license is
            granted: (1) for any code that Contributor has deleted
            from the Contributor Version; (2) for infringements caused
            by: (i) third party modifications of Contributor Version,
            or (ii) the combination of Modifications made by that
            Contributor with other software (except as part of the
            Contributor Version) or other devices; or (3) under Patent
            Claims infringed by Covered Software in the absence of
            Modifications made by that Contributor.

3. Distribution Obligations.

    3.1. Availability of Source Code.

    Any Covered Software that You distribute or otherwise make
    available in Executable form must also be made available in Source
    Code form and that Source Code form must be distributed only under
    the terms of this License.  You must include a copy of this
    License with every copy of the Source Code form of the Covered
    Software You distribute or otherwise make available.  You must
    inform recipients of any such Covered Software in Executable form
    as to how they can obtain such Covered Software in Source Code
    form in a reasonable manner on or through a medium customarily
    used for software exchange.

    3.2. Modifications.

    The Modifications that You create or to which You contribute are
    governed by the terms of this License.  You represent that You
    believe Your Modifications are Your original creation(s) and/or
    You have sufficient rights to grant the rights conveyed by this
    License.

    3.3. Required Notices.

    You must include a notice in each of Your Modifications that
    identifies You as the Contributor of the Modification.  You may
    not remove or alter any copyright, patent or trademark notices
    contained within the Covered Software, or any notices of licensing
    or any descriptive text giving attribution to any Contributor or
    the Initial Developer.

    3.4. Application of Additional Terms.

    You may not offer or impose any terms on any Covered Software in
    Source Code form that alters or restricts the applicable version
    of this License or the recipients' rights hereunder.  You may
    choose to offer, and to charge a fee for, warranty, support,
    indemnity or liability obligations to one or more recipients of
    Covered Software.  However, you may do so only on Your own behalf,
    and not on behalf of the Initial Developer or any Contributor.
    You must make it absolutely clear that any such warranty, support,
    indemnity or liability obligation is offered by You alone, and You
    hereby agree to indemnify the Initial Developer and every
    Contributor for any liability incurred by the Initial Developer or
    such Contributor as a result of warranty, support, indemnity or
    liability terms You offer.

    3.5. Distribution of Executable Versions.

    You may distribute the Executable form of the Covered Software
    under the terms of this License or under the terms of a license of
    Your choice, which may contain terms different from this License,
    provided that You are in compliance with the terms of this License
    and that the license for the Executable form does not attempt to
    limit or alter the recipient's rights in the Source Code form from
    the rights set forth in this License.  If You distribute the
    Covered Software in Executable form under a different license, You
    must make it absolutely clear that any terms which differ from
    this License are offered by You alone, not by the Initial
    Developer or Contributor.  You hereby agree to indemnify the
    Initial Developer and every Contributor for any liability incurred
    by the Initial Developer or such Contributor as a result of any
    such terms You offer.

    3.6. Larger Works.

    You may create a Larger Work by combining Covered Software with
    other code not governed by the terms of this License and
    distribute the Larger Work as a single product.  In such a case,
    You must make sure the requirements of this License are fulfilled
    for the Covered Software.

4. Versions of the License.

    4.1. New Versions.

    Sun Microsystems, Inc. is the initial license steward and may
    publish revised and/or new versions of this License from time to
    time.  Each version will be given a distinguishing version number.
    Except as provided in Section 4.3, no one other than the license
    steward has the right to modify this License.

    4.2. Effect of New Versions.

    You may always continue to use, distribute or otherwise make the
    Covered Software available under the terms of the version of the
    License under which You originally received the Covered Software.
    If the Initial Developer includes a notice in the Original
    Software prohibiting it from being distributed or otherwise made
    available under any subsequent version of the License, You must
    distribute and make the Covered Software available under the terms
    of the version of the License under which You originally received
    the Covered Software.  Otherwise, You may also choose to use,
    distribute or otherwise make the Covered Software available under
    the terms of any subsequent version of the License published by
    the license steward.

    4.3. Modified Versions.

    When You are an Initial Developer and You want to create a new
    license for Your Original Software, You may create and use a
    modified version of this License if You: (a) rename the license
    and remove any references to the name of the license steward
    (except to note that the license differs from this License); and
    (b) otherwise make it clear that the license contains terms which
    differ from this License.

5. DISCLAIMER OF WARRANTY.

    COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
    BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
    INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
    SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
    PURPOSE OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND
    PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU.  SHOULD ANY
    COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
    INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
    NECESSARY SERVICING, REPAIR OR CORRECTION.  THIS DISCLAIMER OF
    WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.  NO USE OF
    ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
    DISCLAIMER.

6. TERMINATION.

    6.1. This License and the rights granted hereunder will terminate
    automatically if You fail to comply with terms herein and fail to
    cure such breach within 30 days of becoming aware of the breach.
    Provisions which, by their nature, must remain in effect beyond
    the termination of this License shall survive.

    6.2. If You assert a patent infringement claim (excluding
    declaratory judgment actions) against Initial Developer or a
    Contributor (the Initial Developer or Contributor against whom You
    assert such claim is referred to as "Participant") alleging that
    the Participant Software (meaning the Contributor Version where
    the Participant is a Contributor or the Original Software where
    the Participant is the Initial Developer) directly or indirectly
    infringes any patent, then any and all rights granted directly or
    indirectly to You by such Participant, the Initial Developer (if
    the Initial Developer is not the Participant) and all Contributors
    under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
    notice from Participant terminate prospectively and automatically
    at the expiration of such 60 day notice period, unless if within
    such 60 day period You withdraw Your claim with respect to the
    Participant Software against such Participant either unilaterally
    or pursuant to a written agreement with Participant.

    6.3. In the event of termination under Sections 6.1 or 6.2 above,
    all end user licenses that have been validly granted by You or any
    distributor hereunder prior to termination (excluding licenses
    granted to You by any distributor) shall survive termination.

7. LIMITATION OF LIABILITY.

    UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
    (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
    INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
    COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
    LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
    CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
    LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
    STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
    COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
    INFORMED OF THE POSSIBILITY OF SUCH DAMAGES.  THIS LIMITATION OF
    LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
    INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
    APPLICABLE LAW PROHIBITS SUCH LIMITATION.  SOME JURISDICTIONS DO
    NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
    CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
    APPLY TO YOU.

8. U.S. GOVERNMENT END USERS.

    The Covered Software is a "commercial item," as that term is
    defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
    computer software" (as that term is defined at 48
    C.F.R. 252.227-7014(a)(1)) and "commercial computer software
    documentation" as such terms are used in 48 C.F.R. 12.212
    (Sept. 1995).  Consistent with 48 C.F.R. 12.212 and 48
    C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
    U.S. Government End Users acquire Covered Software with only those
    rights set forth herein.  This U.S. Government Rights clause is in
    lieu of, and supersedes, any other FAR, DFAR, or other clause or
    provision that addresses Government rights in computer software
    under this License.

9. MISCELLANEOUS.

    This License represents the complete agreement concerning subject
    matter hereof.  If any provision of this License is held to be
    unenforceable, such provision shall be reformed only to the extent
    necessary to make it enforceable.  This License shall be governed
    by the law of the jurisdiction specified in a notice contained
    within the Original Software (except to the extent applicable law,
    if any, provides otherwise), excluding such jurisdiction's
    conflict-of-law provisions.  Any litigation relating to this
    License shall be subject to the jurisdiction of the courts located
    in the jurisdiction and venue specified in a notice contained
    within the Original Software, with the losing party responsible
    for costs, including, without limitation, court costs and
    reasonable attorneys' fees and expenses.  The application of the
    United Nations Convention on Contracts for the International Sale
    of Goods is expressly excluded.  Any law or regulation which
    provides that the language of a contract shall be construed
    against the drafter shall not apply to this License.  You agree
    that You alone are responsible for compliance with the United
    States export administration regulations (and the export control
    laws and regulation of any other countries) when You use,
    distribute or otherwise make available any Covered Software.

10. RESPONSIBILITY FOR CLAIMS.

    As between Initial Developer and the Contributors, each party is
    responsible for claims and damages arising, directly or
    indirectly, out of its utilization of rights under this License
    and You agree to work with Initial Developer and Contributors to
    distribute such responsibility on an equitable basis.  Nothing
    herein is intended or shall be deemed to constitute any admission
    of liability.

  0707010000029B000081A40000000000000000000000016822550500000D09000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmmemctl/Makefile  #!/usr/bin/make -f
##########################################################
# Copyright (C) 2009 VMware, Inc. All rights reserved.
#
# The contents of this file are subject to the terms of the Common
# Development and Distribution License (the "License") version 1.0
# and no later version.  You may not use this file except in
# compliance with the License.
#
# You can obtain a copy of the License at
#         http://www.opensource.org/licenses/cddl1.php
#
# See the License for the specific language governing permissions
# and limitations under the License.
#
##########################################################

##
## General build locations and variables
##

MODULE    := vmmemctl
MODULE_32 := i386/$(MODULE)
MODULE_64 := amd64/$(MODULE)

CFLAGS    :=
KFLAGS    :=
LDFLAGS   :=

CFLAGS    += -O
CFLAGS    += -Wall -Wno-unknown-pragmas -Werror
CFLAGS    += -I../../../lib/include    # for buildNumber.h

KFLAGS    += -D_KERNEL

##
## Objects needed to build the vmmemctl kernel module
##
VMMCTL_OBJS := vmballoon.o
VMMCTL_OBJS += vmballoon_kstats.o
VMMCTL_OBJS += os.o
VMMCTL_OBJS += backdoor_balloon.o
VMMCTL_OBJS += kernelStubsSolaris.o

VMMCTL_32_OBJS := $(addprefix i386/, $(VMMCTL_OBJS))
VMMCTL_32_OBJS += i386/backdoorGcc32.o

VMMCTL_64_OBJS := $(addprefix amd64/, $(VMMCTL_OBJS))
VMMCTL_64_OBJS += amd64/backdoorGcc64.o

ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.10)

# Solaris 10
SUPPORTED := 1
MODULES := $(MODULE_32) $(MODULE_64)
INSTALL := install32 install64
CFLAGS  += -DSOL10
KFLAGS  += -ffreestanding
KFLAGS  += -nodefaultlibs

endif

ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.11)

# Solaris 11
SUPPORTED := 1
MODULES := $(MODULE_32) $(MODULE_64)
INSTALL := install32 install64
CFLAGS  += -DSOL11
KFLAGS  += -ffreestanding
KFLAGS  += -nodefaultlibs

endif

$(if $(SUPPORTED),,$(error "Unsupported Solaris version: $(VM_UNAME)"))


ifdef OVT_SOURCE_DIR
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/include
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/backdoor
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/shared/vmmemctl

   VPATH  := $(OVT_SOURCE_DIR)/lib/backdoor
   VPATH  := $(VPATH):$(OVT_SOURCE_DIR)/modules/shared/vmmemctl
endif

CFLAGS_32  := $(CFLAGS)
CFLAGS_32  += -m32
LDFLAGS_32 := $(LDFLAGS)

CFLAGS_64  := $(CFLAGS)
CFLAGS_64  += -m64

KFLAGS_32  := $(KFLAGS)

KFLAGS_64  := $(KFLAGS)
KFLAGS_64  += -mcmodel=kernel
KFLAGS_64  += -mno-red-zone

LDFLAGS_64 := $(LDFLAGS)
ifdef HAVE_GNU_LD
LDFLAGS_64 += -m elf_x86_64
else
LDFLAGS_64 += -64
endif

all: prepare_dirs $(MODULES)

prepare_dirs:
	@echo "Creating build directories"
	mkdir -p i386
	mkdir -p amd64

$(MODULE_32): $(VMMCTL_32_OBJS)
	@echo "Linking          $@"
	$(LD) $(LDFLAGS_32) -r $(VMMCTL_32_OBJS) -o $@

$(VMMCTL_32_OBJS): i386/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_32) $(KFLAGS_32) -c $< -o $@

$(MODULE_64): $(VMMCTL_64_OBJS)
	@echo "Linking          $@"
	$(LD) $(LDFLAGS_64) -r $(VMMCTL_64_OBJS) -o $@

$(VMMCTL_64_OBJS): amd64/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_64) $(KFLAGS_64) -c $< -o $@

clean:
	@echo "Cleaning directories"
	@rm -rf $(MODULE_32) $(VMMCTL_32_OBJS) i386
	@rm -rf $(MODULE_64) $(VMMCTL_64_OBJS) amd64

install32:
	cp $(MODULE_32) /kernel/drv/
	chmod 755 /kernel/drv/$(MODULE)

install64:
	chmod 755 /kernel/drv/$(MODULE)
	cp $(MODULE_64) /kernel/drv/amd64

install: $(INSTALL)
   0707010000029C000081A40000000000000000000000016822550500000116000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmmemctl/README    The files in this directory are components of the VMware Memory
Control driver.  This version of the driver will only work on an x86
system running Solaris 9 or later with a 32-bit kernel.  This driver
will be installed by the installer of the VMware Tools for Solaris
package.
  0707010000029D000081A400000000000000000000000168225505000021CE000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmmemctl/kernelStubsSolaris.c  /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * kernelStubsSolaris.c
 *
 * This file contains implementations of common userspace functions in terms
 * that the Solaris kernel can understand.
 */

#include "kernelStubs.h"
#include <sys/types.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

#ifdef SOL9
# define compat_va_start(arg, fmt)  arg = ((char *)__builtin_next_arg(fmt))
# define compat_va_end(arg)
# define compat_va_copy(arg1, arg2) va_copy(arg1, arg2)
#else
# define compat_va_start(arg, fmt)  va_start(arg, fmt)
# define compat_va_end(arg)         va_end(arg)
# define compat_va_copy(arg1, arg2) va_copy(arg1, arg2)
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * 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;

   compat_va_start(args, fmt);
   result = Str_Vasprintf(NULL, fmt, args);
   compat_va_end(args);

   cmn_err(CE_PANIC, "%s",
           result ? result : "Unable to format PANIC message");
}

/*
 *----------------------------------------------------------------------
 *
 * 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
{
   size_t len;

   len = strlen(src);
   if (len >= maxSize) {
      Panic("%s:%d Buffer too small %p\n", __FILE__, __LINE__, buf);
   }
   bcopy(src, buf, len + 1);
   return buf;
}


/*
 *----------------------------------------------------------------------
 *
 * Str_Vsnprintf --
 *
 * Compatability wrapper b/w different libc versions
 *
 * Results:
 *    int - number of bytes written (not including NULL terminator),
 *          -1 on overflow (insufficient space for NULL terminator
 *          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 = 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;
      }

      compat_va_copy(args2, arguments);
      retval = Str_Vsnprintf(buf, bufSize, format, args2);
      compat_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;

   compat_va_start(arguments, format);
   result = Str_Vasprintf(length, format, arguments);
   compat_va_end(arguments);

   return result;
}


/*
 *----------------------------------------------------------------------------
 *
 * 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 = kmem_alloc(size + sizeof(size), KM_SLEEP);

   if (ptr) {
      *ptr++ = size;
   }
   return ptr;
}


/*
 *---------------------------------------------------------------------------
 *
 * free --
 *
 *     Free memory allocated by a previous call to malloc, calloc or realloc.
 *
 * Results:
 *     None.
 *
 * Side effects:
 *     Calls kmem_free to free the real (base) pointer.
 *
 *---------------------------------------------------------------------------
 */

void
free(void *mem) // IN
{
   size_t *dataPtr = mem;

   if (mem) {
      --dataPtr;
      kmem_free(dataPtr, *dataPtr + sizeof(*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;
}


  0707010000029E000081A40000000000000000000000016822550500003B3C000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmmemctl/os.c  /*********************************************************
 * Copyright (C) 2005,2014 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * os.c --
 *
 *      Wrappers for Solaris system functions required by "vmmemctl".
 */

#include <sys/types.h>
#include <sys/cred.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/kstat.h>
#include <sys/id_space.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/disp.h>
#include <sys/ksynch.h>

#include "os.h"
#include "vmballoon.h"
#include "balloon_def.h"
#include "vm_assert.h"
#include "vmballoon_kstats.h"
#include "buildNumber.h"

extern void memscrub_disable(void);

/*
 * Constants
 */

#define ONE_SECOND_IN_MICROSECONDS 1000000

/*
 * Types
 */

typedef struct {
   timeout_id_t id;

   /* Worker thread ID */
   kt_did_t thread_id;

   /* termination flag */
   volatile int stop;

   /* synchronization with worker thread */
   kmutex_t lock;
   kcondvar_t cv;

   /* registered state */
   void *data;
   int period;
} os_timer;

/*
 * Keep track of offset here rather than peeking inside the page_t to
 * avoid dependencies on the page structure layout (which changes from
 * release to release).
 */
typedef struct {
   page_t *pp;
   u_offset_t offset;
} os_page;

typedef struct {
   os_timer	timer;
   kstat_t	*kstats;
   id_space_t	*id_space;
   vnode_t	vnode;
} os_state;

/*
 * Globals
 */

static os_state global_state;


/*
 *-----------------------------------------------------------------------------
 *
 * OS_Malloc --
 *
 *      Allocates kernel memory.
 *
 * Results:
 *      On success: Pointer to allocated memory
 *      On failure: NULL
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void *
OS_Malloc(size_t size) // IN
{
   return (kmem_alloc(size, KM_NOSLEEP));
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_Free --
 *
 *      Free allocated kernel memory.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
OS_Free(void *ptr,   // IN
        size_t size) // IN
{
   kmem_free(ptr, size);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_MemZero --
 *
 *      Fill a memory location with 0s.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
OS_MemZero(void *ptr,   // OUT
           size_t size) // IN
{
   bzero(ptr, size);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_MemCopy --
 *
 *      Copy a memory portion into another location.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
OS_MemCopy(void *dest,      // OUT
           const void *src, // IN
           size_t size)     // IN
{
   bcopy(src, dest, size);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageGetLimit --
 *
 *      Predict the maximum achievable balloon size.
 *
 * Results:
 *      Currently we just return the total memory pages.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

unsigned long
OS_ReservedPageGetLimit(void)
{
   return maxmem;
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageGetPA --
 *
 *      Convert a page handle (of a physical page previously reserved with
 *      OS_ReservedPageAlloc()) to a pa.
 *
 * Results:
 *      The pa.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

PA64
OS_ReservedPageGetPA(PageHandle handle) // IN: A valid page handle
{
   return ptob(page_pptonum(((os_page *)handle)->pp));
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageGetHandle --
 *
 *      Convert a pa (of a physical page previously reserved with
 *      OS_ReservedPageAlloc()) to a page handle.
 *
 * Results:
 *      The page handle.
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      Currently not implemented on Solaris.
 *
 *-----------------------------------------------------------------------------
 */

PageHandle
OS_ReservedPageGetHandle(PA64 pa)     // IN
{
   // Solaris does not use batched commands.
   NOT_IMPLEMENTED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_MapPageHandle --
 *
 *      Map a page handle into kernel address space, and return the
 *      mapping to that page handle.
 *
 * Results:
 *      The mapping.
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      Currently not implemented on Solaris.
 *
 *-----------------------------------------------------------------------------
 */

Mapping
OS_MapPageHandle(PageHandle handle)     // IN
{
   // Solaris does not use batched commands.
   NOT_IMPLEMENTED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_Mapping2Addr --
 *
 *      Return the address of a previously mapped page handle (with
 *      OS_MapPageHandle).
 *
 * Results:
 *      The mapping address.
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      Currently not implemented on Solaris.
 *
 *-----------------------------------------------------------------------------
 */

void *
OS_Mapping2Addr(Mapping mapping)        // IN
{
   // Solaris does not use batched commands.
   NOT_IMPLEMENTED();
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_UnmapPage --
 *
 *      Unmap a previously mapped page handle.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 * Note:
 *      Currently not implemented on Solaris.
 *
 *-----------------------------------------------------------------------------
 */

void
OS_UnmapPage(Mapping mapping)   // IN
{
   // Solaris does not use protocol v3.
   NOT_IMPLEMENTED();
}

/*
 * NOTE: cast id before shifting to avoid overflow (id_t is 32 bits,
 * u_offset_t is 64 bits).  Also, can't use ptob because it will
 * overflow in a 32-bit kernel (since ptob returns a ulong_t, and the
 * physical address may be larger than 2^32).
 */
#define idtooff(id)	((u_offset_t)(id) << PAGESHIFT)
#define offtoid(off)	((id_t)((off) >> PAGESHIFT))


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageAlloc --
 *
 *      Reserve a physical page for the exclusive use of this driver.
 *
 *      This is a bit ugly.  In order to allocate a page, we need a vnode to
 *      hang it from and a unique offset within that vnode.  We do this by
 *      using our own vnode (used only to hang pages from) and allocating
 *      offsets by use of the id space allocator.  The id allocator hands
 *      us back unique integers between 0 and INT_MAX; we can then use those
 *      as page indices into our fake vnode space.
 *
 *      Future versions of Solaris will have a devmap_pmem_alloc/free
 *      interface for allocating physical pages that may allow us to
 *      eliminate some of this.
 *
 * Results:
 *      On success: A valid page handle that can be passed to OS_ReservedPageGetPA()
 *                  or OS_ReservedPageFree().
 *      On failure: PAGE_HANDLE_INVALID
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

PageHandle
OS_ReservedPageAlloc(int canSleep,    // IN
                     int isLargePage) // IN
{
   os_state *state = &global_state;
   page_t *pp;
   u_offset_t off;
   struct seg kseg;
   os_page *page;
   id_space_t *idp = state->id_space;
   vnode_t *vp = &state->vnode;
   uint_t flags;

   ASSERT(!isLargePage);

   /*
    * Reserve space for the page.
    */
   flags = canSleep ? KM_SLEEP : KM_NOSLEEP;
   if (!page_resv(1, flags))
      return PAGE_HANDLE_INVALID; /* no space! */

   /*
    * Allocating space for os_page early simplifies error handling.
    */
   if ((page = kmem_alloc(sizeof (os_page), flags)) == NULL) {
      page_unresv(1);
      return PAGE_HANDLE_INVALID;
   }

   /*
    * Construct an offset for page_create.
    */
   off = idtooff(id_alloc(idp));

   /*
    * Allocate the page itself.  Note that this can fail.
    */
   kseg.s_as = &kas;
   flags = canSleep ? PG_EXCL | PG_WAIT : PG_EXCL;
   pp = page_create_va(vp, off, PAGESIZE, flags, &kseg,
		       (caddr_t)(ulong_t)off);
   if (pp != NULL) {
      /*
       * We got a page. We keep the PG_EXCL lock to prohibit
       * anyone (swrand, memscrubber) touching the page. Return the
       * pointer to structure describing page.
       */
      page_io_unlock(pp);
      page_hashout(pp, NULL);
      page->pp = pp;
      page->offset = off;
   } else {
      /*
       * Oops, didn't get a page.  Undo everything and return.
       */
      id_free(idp, offtoid(off));
      kmem_free(page, sizeof (os_page));
      page_unresv(1);
      page = NULL;
   }

   return (PageHandle)page;
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_ReservedPageFree --
 *
 *      Unreserve a physical page previously reserved with OS_ReservedPageAlloc().
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
OS_ReservedPageFree(PageHandle handle, // IN: A valid page handle
                    int isLargePage)   // IN
{
   os_state *state = &global_state;
   os_page *page = (os_page *)handle;
   page_t *pp = page->pp;
   u_offset_t off = page->offset;
   id_space_t *idp = state->id_space;

   ASSERT(!isLargePage);

   page_free(pp, 1);
   page_unresv(1);
   id_free(idp, offtoid(off));
   kmem_free(page, sizeof (os_page));
}


/*
 *-----------------------------------------------------------------------------
 *
 * vmememctl_poll_worker --
 *
 *      Worker thread that periodically calls the timer handler.  This is
 *      executed by a user context thread so that it can block waiting for
 *      memory without fear of deadlock.
 *
 * Results:
 *      On success: 0
 *      On failure: error code
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
vmmemctl_poll_worker(os_timer *t) // IN
{
   clock_t timeout;

   mutex_enter(&t->lock);

   while (!t->stop) {
      mutex_exit(&t->lock);

      Balloon_QueryAndExecute();

      mutex_enter(&t->lock);
      /* check again whether we should stop */
      if (t->stop)
         break;

      /* wait for timeout */
      (void) drv_getparm(LBOLT, &timeout);
      timeout += t->period;
      cv_timedwait_sig(&t->cv, &t->lock, timeout);
   }

   mutex_exit(&t->lock);

   thread_exit();
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_TimerStart --
 *
 *      Setup the timer callback function, then start it.
 *
 * Results:
 *      Always TRUE, cannot fail.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static int
vmmemctl_poll_start(void)
{
   os_timer *t = &global_state.timer;
   kthread_t *tp;

   /* setup the timer structure */
   t->id = 0;
   t->stop = 0;
   t->period = drv_usectohz(BALLOON_POLL_PERIOD * ONE_SECOND_IN_MICROSECONDS);

   mutex_init(&t->lock, NULL, MUTEX_DRIVER, NULL);
   cv_init(&t->cv, NULL, CV_DRIVER, NULL);

   /*
    * All Solaris drivers that I checked assume that thread_create() will
    * succeed, let's follow the suit.
    */
   tp = thread_create(NULL, 0, vmmemctl_poll_worker, (void *)t,
                      0, &p0, TS_RUN, minclsyspri);
   t->thread_id = tp->t_did;

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * vmmemctl_poll_stop --
 *
 *      Signal polling thread to stop and wait till it exists.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
vmmemctl_poll_stop(void)
{
   os_timer *t = &global_state.timer;

   mutex_enter(&t->lock);

   /* Set termination flag. */
   t->stop = 1;

   /* Wake up worker thread so it can exit. */
   cv_signal(&t->cv);

   mutex_exit(&t->lock);

   /* Wait for the worker thread to complete. */
   if (t->thread_id != 0) {
      thread_join(t->thread_id);
      t->thread_id = 0;
   }

   mutex_destroy(&t->lock);
   cv_destroy(&t->cv);
}


/*
 *-----------------------------------------------------------------------------
 *
 * OS_Yield --
 *
 *      Yield the CPU, if needed.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
OS_Yield(void)
{
   /* Do nothing. */
}


/*
 * Module linkage
 */

static struct modldrv vmmodldrv = {
   &mod_miscops,
   "VMware Memory Control b" BUILD_NUMBER_NUMERIC_STRING,
};

static struct modlinkage vmmodlinkage = {
   MODREV_1,
   { &vmmodldrv, NULL }
};


int
_init(void)
{
   os_state *state = &global_state;
   int error;

   if (!Balloon_Init(BALLOON_GUEST_SOLARIS)) {
      return EIO;
   }

   state->kstats = BalloonKstatCreate();
   state->id_space = id_space_create(BALLOON_NAME, 0, INT_MAX);

   /* disable memscrubber */
   memscrub_disable();

   error = vmmemctl_poll_start();
   if (error) {
      goto err_do_cleanup;
   }

   error = mod_install(&vmmodlinkage);
   if (error) {
      goto err_stop_poll;
   }

   cmn_err(CE_CONT, "!%s initialized\n", BALLOON_NAME_VERBOSE);
   return 0;

err_stop_poll:
   vmmemctl_poll_stop();

err_do_cleanup:
   Balloon_Cleanup();
   id_space_destroy(state->id_space);
   BalloonKstatDelete(state->kstats);

   return error;
}


int
_info(struct modinfo *modinfop) // IN
{
   return mod_info(&vmmodlinkage, modinfop);
}


int
_fini(void)
{
   os_state *state = &global_state;
   int error;

   /*
    * Check if the module is busy before cleaning up.
    */
   error = mod_remove(&vmmodlinkage);
   if (error) {
      return error;
   }

   vmmemctl_poll_stop();
   Balloon_Cleanup();
   id_space_destroy(state->id_space);
   BalloonKstatDelete(state->kstats);

   cmn_err(CE_CONT, "!%s unloaded\n", BALLOON_NAME_VERBOSE);

   return 0;
}


0707010000029F000081A40000000000000000000000016822550500001872000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmmemctl/vmballoon_kstats.c    /*********************************************************
 * Copyright (C) 2005 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/* 
 * vmballoon_kstats.c --
 *
 * 	Functions reporting status for vmballoon driver in the form of
 *	kstats.
 */

/*
 * Compile-Time Options
 */

#include <sys/types.h>
#include <sys/kstat.h>
#include <sys/errno.h>
#include "os.h"
#include "vmballoon.h"
#include "vmballoon_kstats.h"

/*
 * Information to be reported to user level through kstats.  This
 * table should be kept in sync with the corresponding info reported
 * through procfs by the linux driver.  To display this information
 * on a Solaris system with the driver loaded, run "kstat -m vmmemctl".
 */
typedef struct {
   kstat_named_t nPagesTarget;
   kstat_named_t nPages;
   kstat_named_t rateAlloc;
   kstat_named_t rateFree;
   kstat_named_t timer;
   kstat_named_t start;
   kstat_named_t startFail;
   kstat_named_t guestType;
   kstat_named_t guestTypeFail;
   kstat_named_t lock;
   kstat_named_t lockFail;
   kstat_named_t unlock;
   kstat_named_t unlockFail;
   kstat_named_t target;
   kstat_named_t targetFail;
   kstat_named_t primAlloc[BALLOON_PAGE_ALLOC_TYPES_NR];
   kstat_named_t primAllocFail[BALLOON_PAGE_ALLOC_TYPES_NR];
   kstat_named_t primFree;
   kstat_named_t primErrorPageAlloc;
   kstat_named_t primErrorPageFree;
} BalloonKstats;

/*
 *----------------------------------------------------------------------
 *
 * BalloonKstatUpdate --
 *
 *      Ballon driver status reporting routine.
 *
 * Results:
 *	Copies current driver status and statistics into kstat structure
 *	for reporting to user level.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
static int
BalloonKstatUpdate(kstat_t *ksp, int rw)
{
   int i;
   const BalloonStats *stats;
   BalloonKstats *bkp = ksp->ks_data;

   if (rw == KSTAT_WRITE)
      return (EACCES);

   stats = Balloon_GetStats();

   /* size info */
   bkp->nPagesTarget.value.ui32 = stats->nPagesTarget;
   bkp->nPages.value.ui32 = stats->nPages;

   /* rate info */
   bkp->rateAlloc.value.ui32 = stats->rateAlloc;
   bkp->rateFree.value.ui32 = stats->rateFree;

   /* statistics */
   bkp->timer.value.ui32 = stats->timer;
   bkp->start.value.ui32 = stats->start;
   bkp->startFail.value.ui32 = stats->startFail;
   bkp->guestType.value.ui32 = stats->guestType;
   bkp->guestTypeFail.value.ui32 = stats->guestTypeFail;
   bkp->lock.value.ui32 = stats->lock[FALSE];
   bkp->lockFail.value.ui32 = stats->lockFail[FALSE];
   bkp->unlock.value.ui32 = stats->unlock[FALSE];
   bkp->unlockFail.value.ui32 = stats->unlockFail[FALSE];
   bkp->target.value.ui32 = stats->target;
   bkp->targetFail.value.ui32 = stats->targetFail;
   for (i = 0; i < BALLOON_PAGE_ALLOC_TYPES_NR; i++) {
      bkp->primAlloc[i].value.ui32 = stats->primAlloc[i];
      bkp->primAllocFail[i].value.ui32 = stats->primAllocFail[i];
   }
   bkp->primFree.value.ui32 = stats->primFree[FALSE];
   bkp->primErrorPageAlloc.value.ui32 = stats->primErrorPageAlloc[FALSE];
   bkp->primErrorPageFree.value.ui32 = stats->primErrorPageFree[FALSE];

   return 0;
}

/*
 * Set up statistics for the balloon driver.  Creates and initializes
 * the kstats structure.
 */
kstat_t *
BalloonKstatCreate(void)
{
   kstat_t *ksp;
   BalloonKstats *bkp;

   ksp = kstat_create("vmmemctl", 0, "vmmemctl", "vm", KSTAT_TYPE_NAMED,
		      sizeof (BalloonKstats) / sizeof (kstat_named_t), 0);

   if (ksp == NULL)
      return (NULL);	/* can't allocate space, give up (no kstats) */

   bkp = ksp->ks_data;
   kstat_named_init(&bkp->nPagesTarget, "targetPages", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->nPages, "currentPages", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->rateAlloc, "rateAlloc", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->rateFree, "rateFree", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->timer, "timer", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->start, "start", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->startFail, "startFail", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->guestType, "guestType", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->guestTypeFail, "guestTypeFail", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->lock, "lock", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->lockFail, "lockFail", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->unlock, "unlock", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->unlockFail, "unlockFail", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->target, "target", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->targetFail, "targetFail", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primAlloc[BALLOON_PAGE_ALLOC_LPAGE],
                        "primAllocLPage", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primAlloc[BALLOON_PAGE_ALLOC_NOSLEEP], 
			"primAllocNoSleep", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primAlloc[BALLOON_PAGE_ALLOC_CANSLEEP],
			"primAllocCanSleep", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primAllocFail[BALLOON_PAGE_ALLOC_LPAGE],
                        "primAllocLPageFail", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primAllocFail[BALLOON_PAGE_ALLOC_NOSLEEP],
			"primAllocNoSleepFail", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primAllocFail[BALLOON_PAGE_ALLOC_CANSLEEP],
			"primAllocCanSleepFail", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primFree, "primFree", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primErrorPageAlloc, "errAlloc", KSTAT_DATA_UINT32);
   kstat_named_init(&bkp->primErrorPageFree, "errFree", KSTAT_DATA_UINT32);

   /* set update function to be run when kstats are read */
   ksp->ks_update = BalloonKstatUpdate;

   kstat_install(ksp);

   return ksp;
}

void BalloonKstatDelete(kstat_t *ksp)
{
   if (ksp != NULL)
      kstat_delete(ksp);
}
  070701000002A0000081A400000000000000000000000168225505000003A2000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmmemctl/vmballoon_kstats.h    /*********************************************************
 * Copyright (C) 2005 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

/*
 * vmballoon_kstats.h --
 *
 *	External definitions associated with the functions providing
 *	kstats for the vmmemctl driver.
 */

#ifndef VMBALLOON_KSTATS_H
#define VMBALLOON_KSTATS_H

extern kstat_t *BalloonKstatCreate(void);
extern void BalloonKstatDelete(kstat_t *);

#endif /* VMBALLOON_KSTATS_H */
  070701000002A1000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet 070701000002A2000081A400000000000000000000000168225505000046CA000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet/COPYING 
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0

1. Definitions.

    1.1. "Contributor" means each individual or entity that creates
         or contributes to the creation of Modifications.

    1.2. "Contributor Version" means the combination of the Original
         Software, prior Modifications used by a Contributor (if any),
         and the Modifications made by that particular Contributor.

    1.3. "Covered Software" means (a) the Original Software, or (b)
         Modifications, or (c) the combination of files containing
         Original Software with files containing Modifications, in
         each case including portions thereof.

    1.4. "Executable" means the Covered Software in any form other
         than Source Code.

    1.5. "Initial Developer" means the individual or entity that first
         makes Original Software available under this License.

    1.6. "Larger Work" means a work which combines Covered Software or
         portions thereof with code not governed by the terms of this
         License.

    1.7. "License" means this document.

    1.8. "Licensable" means having the right to grant, to the maximum
         extent possible, whether at the time of the initial grant or
         subsequently acquired, any and all of the rights conveyed
         herein.

    1.9. "Modifications" means the Source Code and Executable form of
         any of the following:

        A. Any file that results from an addition to, deletion from or
           modification of the contents of a file containing Original
           Software or previous Modifications;

        B. Any new file that contains any part of the Original
           Software or previous Modifications; or

        C. Any new file that is contributed or otherwise made
           available under the terms of this License.

    1.10. "Original Software" means the Source Code and Executable
          form of computer software code that is originally released
          under this License.

    1.11. "Patent Claims" means any patent claim(s), now owned or
          hereafter acquired, including without limitation, method,
          process, and apparatus claims, in any patent Licensable by
          grantor.

    1.12. "Source Code" means (a) the common form of computer software
          code in which modifications are made and (b) associated
          documentation included in or with such code.

    1.13. "You" (or "Your") means an individual or a legal entity
          exercising rights under, and complying with all of the terms
          of, this License.  For legal entities, "You" includes any
          entity which controls, is controlled by, or is under common
          control with You.  For purposes of this definition,
          "control" means (a) the power, direct or indirect, to cause
          the direction or management of such entity, whether by
          contract or otherwise, or (b) ownership of more than fifty
          percent (50%) of the outstanding shares or beneficial
          ownership of such entity.

2. License Grants.

    2.1. The Initial Developer Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, the Initial
    Developer hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Initial Developer, to use,
            reproduce, modify, display, perform, sublicense and
            distribute the Original Software (or portions thereof),
            with or without Modifications, and/or as part of a Larger
            Work; and

        (b) under Patent Claims infringed by the making, using or
            selling of Original Software, to make, have made, use,
            practice, sell, and offer for sale, and/or otherwise
            dispose of the Original Software (or portions thereof).

        (c) The licenses granted in Sections 2.1(a) and (b) are
            effective on the date Initial Developer first distributes
            or otherwise makes the Original Software available to a
            third party under the terms of this License.

        (d) Notwithstanding Section 2.1(b) above, no patent license is
            granted: (1) for code that You delete from the Original
            Software, or (2) for infringements caused by: (i) the
            modification of the Original Software, or (ii) the
            combination of the Original Software with other software
            or devices.

    2.2. Contributor Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, each
    Contributor hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Contributor to use, reproduce,
            modify, display, perform, sublicense and distribute the
            Modifications created by such Contributor (or portions
            thereof), either on an unmodified basis, with other
            Modifications, as Covered Software and/or as part of a
            Larger Work; and

        (b) under Patent Claims infringed by the making, using, or
            selling of Modifications made by that Contributor either
            alone and/or in combination with its Contributor Version
            (or portions of such combination), to make, use, sell,
            offer for sale, have made, and/or otherwise dispose of:
            (1) Modifications made by that Contributor (or portions
            thereof); and (2) the combination of Modifications made by
            that Contributor with its Contributor Version (or portions
            of such combination).

        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
            effective on the date Contributor first distributes or
            otherwise makes the Modifications available to a third
            party.

        (d) Notwithstanding Section 2.2(b) above, no patent license is
            granted: (1) for any code that Contributor has deleted
            from the Contributor Version; (2) for infringements caused
            by: (i) third party modifications of Contributor Version,
            or (ii) the combination of Modifications made by that
            Contributor with other software (except as part of the
            Contributor Version) or other devices; or (3) under Patent
            Claims infringed by Covered Software in the absence of
            Modifications made by that Contributor.

3. Distribution Obligations.

    3.1. Availability of Source Code.

    Any Covered Software that You distribute or otherwise make
    available in Executable form must also be made available in Source
    Code form and that Source Code form must be distributed only under
    the terms of this License.  You must include a copy of this
    License with every copy of the Source Code form of the Covered
    Software You distribute or otherwise make available.  You must
    inform recipients of any such Covered Software in Executable form
    as to how they can obtain such Covered Software in Source Code
    form in a reasonable manner on or through a medium customarily
    used for software exchange.

    3.2. Modifications.

    The Modifications that You create or to which You contribute are
    governed by the terms of this License.  You represent that You
    believe Your Modifications are Your original creation(s) and/or
    You have sufficient rights to grant the rights conveyed by this
    License.

    3.3. Required Notices.

    You must include a notice in each of Your Modifications that
    identifies You as the Contributor of the Modification.  You may
    not remove or alter any copyright, patent or trademark notices
    contained within the Covered Software, or any notices of licensing
    or any descriptive text giving attribution to any Contributor or
    the Initial Developer.

    3.4. Application of Additional Terms.

    You may not offer or impose any terms on any Covered Software in
    Source Code form that alters or restricts the applicable version
    of this License or the recipients' rights hereunder.  You may
    choose to offer, and to charge a fee for, warranty, support,
    indemnity or liability obligations to one or more recipients of
    Covered Software.  However, you may do so only on Your own behalf,
    and not on behalf of the Initial Developer or any Contributor.
    You must make it absolutely clear that any such warranty, support,
    indemnity or liability obligation is offered by You alone, and You
    hereby agree to indemnify the Initial Developer and every
    Contributor for any liability incurred by the Initial Developer or
    such Contributor as a result of warranty, support, indemnity or
    liability terms You offer.

    3.5. Distribution of Executable Versions.

    You may distribute the Executable form of the Covered Software
    under the terms of this License or under the terms of a license of
    Your choice, which may contain terms different from this License,
    provided that You are in compliance with the terms of this License
    and that the license for the Executable form does not attempt to
    limit or alter the recipient's rights in the Source Code form from
    the rights set forth in this License.  If You distribute the
    Covered Software in Executable form under a different license, You
    must make it absolutely clear that any terms which differ from
    this License are offered by You alone, not by the Initial
    Developer or Contributor.  You hereby agree to indemnify the
    Initial Developer and every Contributor for any liability incurred
    by the Initial Developer or such Contributor as a result of any
    such terms You offer.

    3.6. Larger Works.

    You may create a Larger Work by combining Covered Software with
    other code not governed by the terms of this License and
    distribute the Larger Work as a single product.  In such a case,
    You must make sure the requirements of this License are fulfilled
    for the Covered Software.

4. Versions of the License.

    4.1. New Versions.

    Sun Microsystems, Inc. is the initial license steward and may
    publish revised and/or new versions of this License from time to
    time.  Each version will be given a distinguishing version number.
    Except as provided in Section 4.3, no one other than the license
    steward has the right to modify this License.

    4.2. Effect of New Versions.

    You may always continue to use, distribute or otherwise make the
    Covered Software available under the terms of the version of the
    License under which You originally received the Covered Software.
    If the Initial Developer includes a notice in the Original
    Software prohibiting it from being distributed or otherwise made
    available under any subsequent version of the License, You must
    distribute and make the Covered Software available under the terms
    of the version of the License under which You originally received
    the Covered Software.  Otherwise, You may also choose to use,
    distribute or otherwise make the Covered Software available under
    the terms of any subsequent version of the License published by
    the license steward.

    4.3. Modified Versions.

    When You are an Initial Developer and You want to create a new
    license for Your Original Software, You may create and use a
    modified version of this License if You: (a) rename the license
    and remove any references to the name of the license steward
    (except to note that the license differs from this License); and
    (b) otherwise make it clear that the license contains terms which
    differ from this License.

5. DISCLAIMER OF WARRANTY.

    COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
    BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
    INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
    SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
    PURPOSE OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND
    PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU.  SHOULD ANY
    COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
    INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
    NECESSARY SERVICING, REPAIR OR CORRECTION.  THIS DISCLAIMER OF
    WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.  NO USE OF
    ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
    DISCLAIMER.

6. TERMINATION.

    6.1. This License and the rights granted hereunder will terminate
    automatically if You fail to comply with terms herein and fail to
    cure such breach within 30 days of becoming aware of the breach.
    Provisions which, by their nature, must remain in effect beyond
    the termination of this License shall survive.

    6.2. If You assert a patent infringement claim (excluding
    declaratory judgment actions) against Initial Developer or a
    Contributor (the Initial Developer or Contributor against whom You
    assert such claim is referred to as "Participant") alleging that
    the Participant Software (meaning the Contributor Version where
    the Participant is a Contributor or the Original Software where
    the Participant is the Initial Developer) directly or indirectly
    infringes any patent, then any and all rights granted directly or
    indirectly to You by such Participant, the Initial Developer (if
    the Initial Developer is not the Participant) and all Contributors
    under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
    notice from Participant terminate prospectively and automatically
    at the expiration of such 60 day notice period, unless if within
    such 60 day period You withdraw Your claim with respect to the
    Participant Software against such Participant either unilaterally
    or pursuant to a written agreement with Participant.

    6.3. In the event of termination under Sections 6.1 or 6.2 above,
    all end user licenses that have been validly granted by You or any
    distributor hereunder prior to termination (excluding licenses
    granted to You by any distributor) shall survive termination.

7. LIMITATION OF LIABILITY.

    UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
    (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
    INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
    COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
    LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
    CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
    LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
    STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
    COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
    INFORMED OF THE POSSIBILITY OF SUCH DAMAGES.  THIS LIMITATION OF
    LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
    INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
    APPLICABLE LAW PROHIBITS SUCH LIMITATION.  SOME JURISDICTIONS DO
    NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
    CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
    APPLY TO YOU.

8. U.S. GOVERNMENT END USERS.

    The Covered Software is a "commercial item," as that term is
    defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
    computer software" (as that term is defined at 48
    C.F.R. 252.227-7014(a)(1)) and "commercial computer software
    documentation" as such terms are used in 48 C.F.R. 12.212
    (Sept. 1995).  Consistent with 48 C.F.R. 12.212 and 48
    C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
    U.S. Government End Users acquire Covered Software with only those
    rights set forth herein.  This U.S. Government Rights clause is in
    lieu of, and supersedes, any other FAR, DFAR, or other clause or
    provision that addresses Government rights in computer software
    under this License.

9. MISCELLANEOUS.

    This License represents the complete agreement concerning subject
    matter hereof.  If any provision of this License is held to be
    unenforceable, such provision shall be reformed only to the extent
    necessary to make it enforceable.  This License shall be governed
    by the law of the jurisdiction specified in a notice contained
    within the Original Software (except to the extent applicable law,
    if any, provides otherwise), excluding such jurisdiction's
    conflict-of-law provisions.  Any litigation relating to this
    License shall be subject to the jurisdiction of the courts located
    in the jurisdiction and venue specified in a notice contained
    within the Original Software, with the losing party responsible
    for costs, including, without limitation, court costs and
    reasonable attorneys' fees and expenses.  The application of the
    United Nations Convention on Contracts for the International Sale
    of Goods is expressly excluded.  Any law or regulation which
    provides that the language of a contract shall be construed
    against the drafter shall not apply to this License.  You agree
    that You alone are responsible for compliance with the United
    States export administration regulations (and the export control
    laws and regulation of any other countries) when You use,
    distribute or otherwise make available any Covered Software.

10. RESPONSIBILITY FOR CLAIMS.

    As between Initial Developer and the Contributors, each party is
    responsible for claims and damages arising, directly or
    indirectly, out of its utilization of rights under this License
    and You agree to work with Initial Developer and Contributors to
    distribute such responsibility on an equitable basis.  Nothing
    herein is intended or shall be deemed to constitute any admission
    of liability.

  070701000002A3000081A40000000000000000000000016822550500000A1B000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet/Makefile    #!/usr/bin/make -f
##########################################################
# Copyright (C) 2009 VMware, Inc. All rights reserved.
#
# The contents of this file are subject to the terms of the Common
# Development and Distribution License (the "License") version 1.0
# and no later version.  You may not use this file except in
# compliance with the License.
#
# You can obtain a copy of the License at
#         http://www.opensource.org/licenses/cddl1.php
#
# See the License for the specific language governing permissions
# and limitations under the License.
#
##########################################################

##
## General build locations and variables
##

MODULE    := vmxnet
MODULE_32 := i386/$(MODULE)
MODULE_64 := amd64/$(MODULE)

CFLAGS    :=
LDFLAGS   :=

CFLAGS    += -O2
CFLAGS    += -Wall -Werror
CFLAGS    += -U_NO_LONGLONG
CFLAGS    += -D_KERNEL
CFLAGS    += -I../../../lib/include    # for buildNumber.h

##
## Objects needed to build the HGFS kernel module
##
VMXNET_OBJS := vmxnet.o

VMXNET_32_OBJS := $(addprefix i386/, $(VMXNET_OBJS))
VMXNET_64_OBJS := $(addprefix amd64/, $(VMXNET_OBJS))

ifeq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.9) # {

# Solaris 9
MODULES := $(MODULE_32)

else # } {

# Assume Solaris 10 or 11.
MODULES := $(MODULE_32) $(MODULE_64)
CFLAGS  += -ffreestanding
CFLAGS  += -nodefaultlibs

endif # }

ifdef OVT_SOURCE_DIR
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/include
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/shared/vmxnet
endif

CFLAGS_32  := $(CFLAGS)
CFLAGS_32  += -m32
LDFLAGS_32 := $(LDFLAGS)

CFLAGS_64  := $(CFLAGS)
CFLAGS_64  += -m64
CFLAGS_64  += -mcmodel=kernel
CFLAGS_64  += -mno-red-zone
LDFLAGS_64 := $(LDFLAGS)
ifdef HAVE_GNU_LD
LDFLAGS_64 += -m elf_x86_64
else
LDFLAGS_64 += -64
endif

all: prepare_dirs $(MODULES)

prepare_dirs:
	@echo "Creating build directories"
	mkdir -p i386
	mkdir -p amd64

$(MODULE_32): $(VMXNET_32_OBJS)
	@echo "Linking          $@"
	$(LD) $(LDFLAGS_32) -r $(VMXNET_32_OBJS) -o $@

$(VMXNET_32_OBJS): i386/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_32) -c $< -o $@

$(MODULE_64): $(VMXNET_64_OBJS)
	@echo "Linking          $@"
	$(LD) $(LDFLAGS_64) -r $(VMXNET_64_OBJS) -o $@

$(VMXNET_64_OBJS): amd64/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_64) -c $< -o $@

clean:
	@echo "Cleaning directories"
	@rm -rf $(MODULE_32) $(VMXNET_32_OBJS) i386
	@rm -rf $(MODULE_64) $(VMXNET_64_OBJS) amd64

install:
	@echo "Installing modules"
	cp $(MODULE_32) /kernel/drv/
	chmod 755 /kernel/drv/$(MODULE)
	cp $(MODULE_64) /kernel/drv/amd64
	chmod 755 /kernel/drv/amd64/$(MODULE)
 070701000002A4000081A40000000000000000000000016822550500010AD5000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet/vmxnet.c    /*********************************************************
 * Copyright (C) 2004,2019 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#include <sys/types.h>
#include <sys/conf.h>
#include <sys/debug.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strlog.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/kstat.h>
#include <sys/vtrace.h>
#include <sys/dlpi.h>
#include <sys/strsun.h>
#include <sys/ethernet.h>
#include <sys/modctl.h>
#include <sys/errno.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/gld.h>
#include <sys/pci.h>
#include <sys/strsubr.h>

/*
 * This used to be defined in sys/gld.h, but was flagged as private,
 * and we used it anyway.  Now it no longer exists, and we're stuck
 * with it for the time being.
 */
#ifndef GLD_MAX_MULTICAST
#define GLD_MAX_MULTICAST 64
#endif

#define __intptr_t_defined
#define _STDINT_H
#include "vm_basic_types.h"
#include "vmxnet2_def.h"
#include "vm_device_version.h"
#include "net.h"
#include "buildNumber.h"

#define SOLVMXNET_SUCCESS 1
#define SOLVMXNET_FAILURE 0

#ifdef SOLVMXNET_DEBUG_LEVEL
static int vxn_debug = SOLVMXNET_DEBUG_LEVEL;
#define  DPRINTF(n, args)  if (vxn_debug>(n)) cmn_err args
#else
#define  DPRINTF(n, args)
#endif

static char ident[] = "VMware Ethernet Adapter b" BUILD_NUMBER_NUMERIC_STRING;
char _depends_on[] = {"misc/gld"};

#define MAX_NUM_RECV_BUFFERS            128
#define DEFAULT_NUM_RECV_BUFFERS        100
#define MAX_NUM_XMIT_BUFFERS            128
#define DEFAULT_NUM_XMIT_BUFFERS        100
#define CRC_POLYNOMIAL_LE               0xedb88320UL
#define SOLVMXNET_MAXNAME               20
#define MAX_TX_WAIT_ON_STOP             2000

#define ETHERALIGN  2
#define SLACKBYTES  4
#define MAXPKTBUF   (14 + ETHERALIGN + ETHERMTU + SLACKBYTES)


#define QHIWATER (MAX_NUM_RECV_BUFFERS*ETHERMTU)

#define OUTB(dp, p, v)  \
        ddi_put8((dp)->vxnIOHdl, \
                (uint8_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
#define OUTW(dp, p, v)  \
        ddi_put16((dp)->vxnIOHdl, \
                (uint16_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
#define OUTL(dp, p, v)  \
        ddi_put32((dp)->vxnIOHdl, \
                (uint32_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
#define INB(dp, p)      \
        ddi_get8((dp)->vxnIOHdl, \
                (uint8_t *)(((caddr_t)(dp)->vxnIOp) + (p)))
#define INW(dp, p)      \
        ddi_get16((dp)->vxnIOHdl, \
                (uint16_t *)(((caddr_t)(dp)->vxnIOp) + (p)))
#define INL(dp, p)      \
        ddi_get32((dp)->vxnIOHdl, \
                (uint32_t *)(((caddr_t)(dp)->vxnIOp) + (p)))

#define VMXNET_INC(val, max) \
   val++; \
   if (UNLIKELY(val == max)) { \
      val = 0; \
   }

#define TX_RINGBUF_MBLK(dp, idx) (dp->txRingBuf[idx].mblk)
#define TX_RINGBUF_DMAMEM(dp, idx) (dp->txRingBuf[idx].dmaMem)

typedef struct {
   caddr_t           buf;             /* Virtual address */
   uint32_t          phyBuf;          /* Physical address */
   size_t            bufLen;          /* Buffer length */
   ddi_dma_cookie_t  cookie;          /* Dma cookie */
   uint_t            cookieCount;     /* Cookie count */
   ddi_dma_handle_t  dmaHdl;          /* Dma handle */ 
   ddi_acc_handle_t  dataAccHdl;      /* Dada access handle */
} dma_buf_t;

typedef struct rx_dma_buf {
   dma_buf_t           dmaDesc;       /* Dma descriptor */
   mblk_t              *mblk;         /* Streams message block */
   frtn_t              freeCB;        /* Free callback */
   struct vxn_softc    *softc;        /* Back pointer to softc */
   struct rx_dma_buf   *next;         /* Next one in list */
} rx_dma_buf_t;

typedef struct vxn_stats {
   uint32_t    errxmt;               /* Transmit errors */
   uint32_t    errrcv;               /* Receive errors */
   uint32_t    runt;                 /* Runt packets */
   uint32_t    norcvbuf;             /* Buffer alloc errors */
   uint32_t    interrupts;	     /* Interrupts */
   uint32_t    defer;		     /* Deferred transmits */
} vxn_stats_t;

typedef struct tx_ring_buf {
   mblk_t      *mblk;
   dma_buf_t   dmaMem;
} tx_ring_buf_t;

typedef struct vxn_softc {
   char                    drvName[SOLVMXNET_MAXNAME]; /* Driver name string */
   int                     unit;               /* Driver instance */
   vxn_stats_t             stats;              /* Stats */

   dev_info_t              *dip;               /* Info pointer */
   ddi_iblock_cookie_t     iblockCookie;       /* Interrupt block cookie */
   gld_mac_info_t          *macInfo;           /* GLD mac info */
   ddi_acc_handle_t        confHdl;            /* Configuration space handle */
   ddi_acc_handle_t        vxnIOHdl;           /* I/O space handle */
   caddr_t                 vxnIOp;             /* I/O space pointer */
   boolean_t               morphed;            /* Adapter morphed ? */

   kmutex_t                intrlock;           /* Interrupt lock */
   kmutex_t                xmitlock;           /* Transmit lock */
   kmutex_t                rxlistlock;         /* Rx free pool lock */

   boolean_t               nicActive;          /* NIC active flag */
   boolean_t               inIntr;             /* Interrupt processing flag */

   struct ether_addr       devAddr;            /* MAC address */

   uint32_t                vxnNumRxBufs;       /* Number of reveice buffers */
   uint32_t                vxnNumTxBufs;       /* Number of transmit buffers */

   dma_buf_t               driverDataDmaMem;   /* Driver Data (dma handle) */
   Vmxnet2_DriverData      *driverData;        /* Driver Data */
   void                    *driverDataPhy;     /* Driver Data busaddr pointer */
   Vmxnet2_RxRingEntry     *rxRing;            /* Receive ring */
   Vmxnet2_TxRingEntry     *txRing;            /* Transmit ring */
   ddi_dma_handle_t        txDmaHdl;           /* Tx buffers dma handle */
   rx_dma_buf_t            *rxRingBuffPtr[MAX_NUM_RECV_BUFFERS];
                                               /* DMA buffers associated with rxRing */
   tx_ring_buf_t           txRingBuf[MAX_NUM_XMIT_BUFFERS]; /* tx Ring buffers */

   rx_dma_buf_t            *rxFreeBufList;
   uint32_t                rxNumFreeBufs;      /* current # of buffers in pool */
   uint32_t                rxMaxFreeBufs;      /* max # of buffers in pool */

   uint32_t                txPending;          /* Pending transmits */
   uint32_t                maxTxFrags;         /* Max Tx fragments */

   int                     multiCount;         /* Multicast address count */
   struct ether_addr       multicastList[GLD_MAX_MULTICAST]; /* Multicast list */

   struct vxn_softc        *next;              /* Circular list of instances */
   struct vxn_softc        *prev;
} vxn_softc_t;

/* used for rx buffers or buffers allocated by ddi_dma_mem_alloc() */
static ddi_dma_attr_t vxn_dma_attrs = {
        DMA_ATTR_V0,            /* dma_attr version */
        0,                      /* dma_attr_addr_lo */
        (uint64_t)0xFFFFFFFF,   /* dma_attr_addr_hi */
        0x7FFFFFFF,             /* dma_attr_count_max */
        4,                      /* dma_attr_align */
        0x3F,                   /* dma_attr_burstsizes */
        1,                      /* dma_attr_minxfer */
        (uint64_t)0xFFFFFFFF,   /* dma_attr_maxxfer */
        (uint64_t)0xFFFFFFFF,   /* dma_attr_seg */
        1,                      /* dma_attr_sgllen */
        1,                      /* dma_attr_granular */
        0,                      /* dma_attr_flags */
};

/* used for tx buffers */
static ddi_dma_attr_t vxn_dma_attrs_tx = {
        DMA_ATTR_V0,            /* dma_attr version */
        0,                      /* dma_attr_addr_lo */
        (uint64_t)0xFFFFFFFF,   /* dma_attr_addr_hi */
        0x7FFFFFFF,             /* dma_attr_count_max */
        1,                      /* dma_attr_align */
        0x3F,                   /* dma_attr_burstsizes */
        1,                      /* dma_attr_minxfer */
        (uint64_t)0xFFFFFFFF,   /* dma_attr_maxxfer */
        (uint64_t)0xFFFFFFFF,   /* dma_attr_seg */
        1,                      /* dma_attr_sgllen */
        1,                      /* dma_attr_granular */
        0,                      /* dma_attr_flags */
};


static   struct ether_addr etherbroadcastaddr = {
   {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
};

static struct ddi_device_acc_attr vxn_buf_attrs = {
        DDI_DEVICE_ATTR_V0,
        DDI_STRUCTURE_LE_ACC,
        DDI_STRICTORDER_ACC
};

static struct ddi_device_acc_attr dev_attr = {
        DDI_DEVICE_ATTR_V0,
        DDI_STRUCTURE_LE_ACC,
        DDI_STRICTORDER_ACC
};

static vxn_softc_t vxnList;		/* for debugging */
static kmutex_t vxnListLock;

static void *Vxn_Memset(void *s, int c, size_t n);
static int Vxn_Reset(gld_mac_info_t *macInfo);
static int Vxn_SetPromiscuous(gld_mac_info_t *macInfo, int flag);
static int Vxn_GetStats(gld_mac_info_t *macInfo, struct gld_stats *gs);
static void Vxn_ApplyAddressFilter(vxn_softc_t *dp);
static int Vxn_SetMulticast(gld_mac_info_t *macinfo, uint8_t *ep, int flag);
static int Vxn_SetMacAddress(gld_mac_info_t *macInfo, uint8_t *mac);
static int Vxn_Start(gld_mac_info_t *macInfo);
static int Vxn_Stop(gld_mac_info_t *macInfo);
static void Vxn_FreeTxBuf(vxn_softc_t *dp, int idx);
static int Vxn_EncapTxBuf(vxn_softc_t *dp, mblk_t *mp, Vmxnet2_TxRingEntry *xre,
                          tx_ring_buf_t *txBuf);
static int Vxn_Send(gld_mac_info_t *macinfo, mblk_t *mp);
static boolean_t Vxn_TxComplete(vxn_softc_t *dp, boolean_t *reschedp);
static boolean_t Vxn_Receive(vxn_softc_t *dp);
static u_int Vxn_Interrupt(gld_mac_info_t *macInfo);
static void Vxn_ReclaimRxBuf(rx_dma_buf_t *rxDesc);
static void Vxn_FreeRxBuf(rx_dma_buf_t *rxDesc);
static rx_dma_buf_t *Vxn_AllocRxBuf(vxn_softc_t *dp, int cansleep);
static void Vxn_FreeInitBuffers(vxn_softc_t *dp);
static int Vxn_AllocInitBuffers(vxn_softc_t *dp);
static void Vxn_FreeDmaMem(dma_buf_t *dma);
static int Vxn_AllocDmaMem(vxn_softc_t *dp, int size, int cansleep, dma_buf_t *dma);
static void Vxn_FreeDriverData(vxn_softc_t *dp);
static int Vxn_AllocDriverData(vxn_softc_t *dp);
static int Vxn_Attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int Vxn_Detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int Vxn_AllocRxBufPool(vxn_softc_t *dp);
static void Vxn_FreeRxBufPool(vxn_softc_t *dp);
static rx_dma_buf_t * Vxn_AllocRxBufFromPool(vxn_softc_t *dp);
static void Vxn_FreeRxBufToPool(rx_dma_buf_t *rxDesc);

/*
 *-----------------------------------------------------------------------------
 * Vxn_Memset --
 *    memset() (Because bzero does not get resolved by module loader) 
 *
 * Results:
 *    pointer to the memory area s
 *
 * Side effects:
 *    None  
 *-----------------------------------------------------------------------------
 */
static void *
Vxn_Memset(void *s, int c, size_t n)
{
   while (n--) {
      ((uint8_t *)s)[n] = c;
   }

   return s;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_Reset -- 
 *    Stub routine to reset hardware. Presently does nothing. Start/Stop should
 *    take care of resets.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static int
Vxn_Reset(gld_mac_info_t *macInfo)
{
   return GLD_SUCCESS;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_SetPromiscuous --
 *    Set/Reset NIC to/from promiscuous mode
 *
 * Results:
 *    GLD_SUCCESS
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static int
Vxn_SetPromiscuous(gld_mac_info_t *macInfo, int flag)
{
   vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;
   Vmxnet2_DriverData *dd = dp->driverData;

   mutex_enter(&dp->intrlock);
   if (flag == GLD_MAC_PROMISC_PHYS) {
      dd->ifflags |= VMXNET_IFF_PROMISC;
   } else if (flag == GLD_MAC_PROMISC_MULTI) {
     /*
      * This should really set VMXNET_IFF_ALLMULTI,
      * but unfortunately it doesn't exist.  The next
      * best thing would be to set the LADRFs to all
      * 0xFFs and set VMXNET_IFF_MULTICAST, but that
      * opens up a whole new set of potential pitfalls,
      * so this is a reasonable temporary solution.
      */
      dd->ifflags |= VMXNET_IFF_PROMISC;
   } else if (flag == GLD_MAC_PROMISC_NONE) {
      dd->ifflags &= ~VMXNET_IFF_PROMISC;
   } else {
     /* This could be GLD_MAC_PROMISC_NOOP? */
     mutex_exit(&dp->intrlock);
     cmn_err(CE_WARN, "%s%d: Vxn_SetPromiscuous: Unexpected mode flag: 0x%x", 
             dp->drvName, dp->unit, flag);

     return GLD_FAILURE;
   }

   OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
   mutex_exit(&dp->intrlock);

   return GLD_SUCCESS;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_GetStats --
 *    Get driver specific stats
 *
 * Results:
 *    GLD_SUCCESS
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static int
Vxn_GetStats(gld_mac_info_t *macInfo, struct gld_stats *gs)
{
   vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;

   gs->glds_errxmt    = dp->stats.errxmt;
   gs->glds_errrcv    = dp->stats.errrcv;
   gs->glds_short     = dp->stats.runt;
   gs->glds_norcvbuf  = dp->stats.norcvbuf;
   gs->glds_intr      = dp->stats.interrupts;
   gs->glds_defer     = dp->stats.defer;

   return GLD_SUCCESS;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_ApplyAddressFilter --
 *    Go over multicast list and compute/apply address filter
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_ApplyAddressFilter(vxn_softc_t *dp)
{
   int i, j, bit, byte;
   Vmxnet2_DriverData *dd = dp->driverData;
   volatile uint16_t *mcastTable = (uint16_t *)dd->LADRF;

   ASSERT(MUTEX_HELD(&dp->intrlock));

   /* clear the multicast filter */
   dd->LADRF[0] = 0;
   dd->LADRF[1] = 0;

   for (i = 0; i < dp->multiCount; i++) {
      uint32_t crc = 0xffffffff;
      uint32_t poly = CRC_POLYNOMIAL_LE;
      uint8_t *ep = (uint8_t *)&dp->multicastList[i].ether_addr_octet;

      for (byte = 0; byte < 6; byte++) {
         for (bit = *ep++, j = 0; j < 8; j++, bit >>= 1) {
            int test;

            test = ((bit ^ crc) & 0x01);
            crc >>= 1;

            if (test) {
               crc = crc ^ poly;
            }
         }
      }

      crc = crc >> 26;
      mcastTable[crc >> 4] |= 1 << (crc & 0xf);
   }
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_SetMulticast --
 *    Add delete entry from multicast list
 *
 * Results:
 *    GLD_FAILURE on failure
 *    GLD_SUCCESS on success
 *
 * Side effects:
 *    None  
 *-----------------------------------------------------------------------------
 */
static int
Vxn_SetMulticast(gld_mac_info_t *macinfo, uint8_t *ep, int flag)
{
   int i;
   int copyLen;
   vxn_softc_t *dp = (vxn_softc_t *)macinfo->gldm_private;
   Vmxnet2_DriverData *dd = dp->driverData;

   if (flag == GLD_MULTI_ENABLE) {
      /*
       * Exceeded multicast address limit
       */
      if (dp->multiCount >= GLD_MAX_MULTICAST) {
         return GLD_FAILURE;
      }

      /* 
       * Add mac address to multicast list
       */
      bcopy(ep, dp->multicastList[dp->multiCount].ether_addr_octet, 
                ETHERADDRL);
      dp->multiCount++;
   }
   else {
      for (i=0; i<dp->multiCount; i++) {
         if (bcmp(ep, dp->multicastList[i].ether_addr_octet, ETHERADDRL) == 0) {
            goto found;
         }
      }
      return GLD_FAILURE;

   found:
      /*
       * Delete mac address from multicast list
       */
      copyLen = (dp->multiCount - (i+1)) * sizeof(struct ether_addr);
      if (copyLen > 0) {
        bcopy(&dp->multicastList[i+1], &dp->multicastList[i], copyLen);
      }
      dp->multiCount--;
   }

   /*
    * Compute address filter from list of addressed and apply it
    */
   mutex_enter(&dp->intrlock);
   Vxn_ApplyAddressFilter(dp);

   if (dp->multiCount) {
     ASSERT(dd->LADRF[0] || dd->LADRF[1]);
     dd->ifflags |= VMXNET_IFF_MULTICAST;
   } else {
     ASSERT(!(dd->LADRF[0] || dd->LADRF[1]));
     dd->ifflags &= ~VMXNET_IFF_MULTICAST;
   }

   OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
   OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_LADRF);
   mutex_exit(&dp->intrlock);   

   return GLD_SUCCESS;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_SetMacAddress --
 *    Change device MAC address  
 *
 * Results:
 *    GLD_SUCCESS
 *    GLD_FAILURE
 *
 * Side effects:
 *    None 
 *-----------------------------------------------------------------------------
 */
static int
Vxn_SetMacAddress(gld_mac_info_t *macInfo, uint8_t *mac)
{
   int i;
   int err = GLD_SUCCESS;
   vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;

   mutex_enter(&dp->intrlock);
   mutex_enter(&dp->xmitlock);

   /*
    * Don't change MAC address on a running NIC
    */
   if (dp->nicActive) {
      err = GLD_FAILURE;
      goto out;
   }

   /*
    * Save new MAC address 
    */
   for (i = 0; i < 6; i++) {
      dp->devAddr.ether_addr_octet[i] = mac[i];
   }
   
   /*
    * Push new MAC address down into hardware
    */
   for (i = 0; i < 6; i++) {
      OUTB(dp, VMXNET_MAC_ADDR + i, mac[i]);
   }

out:
   mutex_exit(&dp->xmitlock);
   mutex_exit(&dp->intrlock);
   return err;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_Start --
 *    Device start routine. Called on "ifconfig plumb"
 *
 * Results:
 *    GLD_SUCCESS
 *    GLD_FAILURE
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static int
Vxn_Start(gld_mac_info_t *macInfo)
{
   int err = GLD_SUCCESS;
   uint32_t r, capabilities, features;
   vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;

   mutex_enter(&dp->intrlock);
   mutex_enter(&dp->xmitlock);

   if (!dp->nicActive) {
      /*
       * Register ring structure with hardware
       *
       * This downcast is OK because we requested a 32-bit physical address
       */
      OUTL(dp, VMXNET_INIT_ADDR, (uint32_t)(uintptr_t)dp->driverDataPhy);
      OUTL(dp, VMXNET_INIT_LENGTH, dp->driverData->length);

      /* 
       * Make sure registeration succeded 
       */
      r = INL(dp, VMXNET_INIT_LENGTH);
      if (!r) {
         cmn_err(CE_WARN, "%s%d: Vxn_Start: failed to register ring", 
                          dp->drvName, dp->unit);
         err = GLD_FAILURE;
         goto out;
      }

      /*
       * Get maximum tx fragments supported
       */
      OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_CAPABILITIES);
      capabilities = INL(dp, VMXNET_COMMAND_ADDR);

      OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_FEATURES);
      features = INL(dp, VMXNET_COMMAND_ADDR);

      DPRINTF(3, (CE_CONT, "%s%d: chip capabilities=0x%x features=0x%x\n", 
              dp->drvName, dp->unit, capabilities, features));

      if ((capabilities & VMNET_CAP_SG) &&
          (features & VMXNET_FEATURE_ZERO_COPY_TX)) {
         dp->maxTxFrags = VMXNET2_SG_DEFAULT_LENGTH;
      } else {
         dp->maxTxFrags = 1;
      }
      ASSERT(dp->maxTxFrags >= 1);

      /*
       * Alloc Tx DMA handle
       */
      vxn_dma_attrs_tx.dma_attr_sgllen = dp->maxTxFrags;
      if (ddi_dma_alloc_handle(dp->dip, &vxn_dma_attrs_tx, DDI_DMA_SLEEP,
                               NULL, &dp->txDmaHdl) != DDI_SUCCESS)  {
         cmn_err(CE_WARN, "%s%d: Vxn_Start: failed to alloc tx dma handle", 
                 dp->drvName, dp->unit);
         err = GLD_FAILURE;
         goto out;
      }

      /*
       * Enable interrupts on the card
       */
      dp->driverData->ifflags |= VMXNET_IFF_BROADCAST | VMXNET_IFF_DIRECTED;

      OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_ENABLE);
      OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
      OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_LADRF);

      dp->nicActive = TRUE;
  }

out:
   mutex_exit(&dp->xmitlock);
   mutex_exit(&dp->intrlock);
   return err;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_Stop --
 *    Device stop routine. Called on "ifconfig unplumb"
 *
 * Results:
 *    GLD_SUCCESS
 *    GLD_FAILURE
 *
 * Side effects:
 *    None   
 *-----------------------------------------------------------------------------
 */
static int
Vxn_Stop(gld_mac_info_t *macInfo)
{
   int i;
   int err = GLD_SUCCESS;
   vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;
   boolean_t resched;

   mutex_enter(&dp->intrlock);
   mutex_enter(&dp->xmitlock);

   if (!dp->nicActive) {
      goto out;
   }

   /*
    * Disable interrupts
    */
   OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_DISABLE);

   /*
    * Wait for pending transmits
    */
   if (dp->txPending) {
      for (i=0; i < MAX_TX_WAIT_ON_STOP && dp->txPending; i++) {
         delay(drv_usectohz(1000));
         OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_CHECK_TX_DONE);
         (void) Vxn_TxComplete(dp, &resched);
         /*
          * Don't worry about rescheduling transmits - GLD handles
          * this automatically.
          */
      }
   }
   if (dp->txPending) {
      cmn_err(CE_WARN, "%s%d: Vxn_Stop: giving up on %d pending transmits", 
                       dp->drvName, dp->unit, dp->txPending);      
   }

   OUTL(dp, VMXNET_INIT_ADDR, 0);
   dp->nicActive = FALSE;

   /*
    * Free Tx DMA handle
    *
    * The ddi_dma_free_handle() man page says that ddi_dma_unbind_handle() must be called
    * prior to calling ddi_dma_free_handle().
    * However, call to ddi_dma_unbind_handle() is not required here, because
    * ddi_dma_addr_bind_handle() and matching ddi_dma_unbind_handle() are called from
    * Vxn_EncapTxBuf().
    * xmitlock is held in Vxn_EncapTxBuf() as well as acquired above in Vxn_Stop().
    */
   ddi_dma_free_handle(&dp->txDmaHdl);
   dp->txDmaHdl = NULL;

out:
   mutex_exit(&dp->xmitlock);
   mutex_exit(&dp->intrlock);
   return err;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_FreeTxBuf --
 *    Free transmit buffer
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_FreeTxBuf(vxn_softc_t *dp, int idx)
{
   mblk_t **txMblkp = &TX_RINGBUF_MBLK(dp, idx);
   dma_buf_t *dmaMem = &TX_RINGBUF_DMAMEM(dp, idx);

   if (*txMblkp) {
      freemsg(*txMblkp);
      *txMblkp = NULL;
   }

   if (dmaMem->buf) {
      Vxn_FreeDmaMem(dmaMem);
      ASSERT(dmaMem->buf == NULL);
   }
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_EncapTxBuf -- 
 *    Go over dma mappings of Tx buffers and drop buffer physical address 
 *    into ring entry
 *
 * Results:
 *    SOLVMXNET_SUCCESS on success
 *    SOLVMXNET_FAILURE on failure
 *
 * Side effects:
 *    None  
 *---------------- -------------------------------------------------------------
 */
static int
Vxn_EncapTxBuf(vxn_softc_t *dp, 
               mblk_t *mp, 
               Vmxnet2_TxRingEntry *xre, 
               tx_ring_buf_t *txBuf)
{
   int frag;
   int fragcount;
   int rval;
   mblk_t *tp;
   mblk_t *mblk;
   boolean_t needPullup = FALSE;
   boolean_t dmaMemAlloced = FALSE;

   ASSERT(txBuf);
   ASSERT(txBuf->mblk == NULL);
   ASSERT(MUTEX_HELD(&dp->xmitlock));

   xre->sg.length = 0;
   xre->flags = 0;

   fragcount = 0;
   for (tp = mp; tp != NULL; tp = tp->b_cont) {
      fragcount++;
   }
   if (fragcount > dp->maxTxFrags) {
      needPullup = TRUE;
   }

pullup:
   frag = 0;
   if (needPullup) {
      if (!(mblk = msgpullup(mp, -1))) {
         cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: msgpullup failed",
                          dp->drvName, dp->unit);
         goto err;
      }
   } else {
      mblk = mp;
   }

   /*
    * Go through message chain and drop packet pointers into ring
    * scatter/gather array
    */
   for (tp = mblk; tp != NULL; tp = tp->b_cont) {

      uint_t nCookies;
      ddi_dma_cookie_t dmaCookie;
      int len = tp->b_wptr - tp->b_rptr;

      if (len) {
         /*
          * Associate tx buffer with dma handle
          */
         ASSERT(dp->txDmaHdl);
         if ((rval = ddi_dma_addr_bind_handle(dp->txDmaHdl, NULL, (caddr_t)tp->b_rptr,
                                      len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
                                      DDI_DMA_DONTWAIT, NULL,
                                      &dmaCookie, &nCookies))
             != DDI_DMA_MAPPED) {

            /*
             *  Try to handle bind failure caused by a page boundary spill
             *  by allocating a private dma buffer and copying data into it
             */
            if ((rval == DDI_DMA_TOOBIG) && !dmaMemAlloced ) {
               /*
                * Force pullup 
                */
               if (!needPullup && (dp->maxTxFrags > 1)) {
                  needPullup = TRUE;
                  goto pullup;
               }

               if (Vxn_AllocDmaMem(dp, len, FALSE, &txBuf->dmaMem) 
                                             != SOLVMXNET_SUCCESS) {
                  goto err;
               }

               dmaMemAlloced = TRUE;

               /*
                * Copy data into DMA capable buffer
                */
               bcopy(tp->b_rptr, txBuf->dmaMem.buf, len);

               /*
                * Stick buffer physical addr in the ring
                */
               xre->sg.sg[frag].addrLow = txBuf->dmaMem.phyBuf;
               xre->sg.sg[frag].length = len;
               frag++;

               continue;

            } else {
               cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: failed (%d) to bind dma "
                                "handle for len %d. [dmaMemAlloced=%d]", 
                                dp->drvName, dp->unit, rval, len, dmaMemAlloced);
               goto err;
            }
         }

         /*
          * Extract tx buffer physical addresses from cookie
          */
         while (nCookies) {
            if (UNLIKELY(frag == dp->maxTxFrags)) {
               (void)ddi_dma_unbind_handle(dp->txDmaHdl);

               if (!needPullup) {
                  ASSERT(!dmaMemAlloced);
                  needPullup = TRUE;
                  goto pullup;
               } else {
                  cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: "
                          "exceeded max (%d) fragments in message",
                          dp->drvName, dp->unit, dp->maxTxFrags);
                  goto err;
               }
            }

            /*
             * Stick it in the ring
             */
            xre->sg.sg[frag].addrLow = dmaCookie.dmac_address;
            xre->sg.sg[frag].length = dmaCookie.dmac_size;
            frag++;

            if (--nCookies) {
               ddi_dma_nextcookie(dp->txDmaHdl, &dmaCookie);
            }
         }

         (void)ddi_dma_unbind_handle(dp->txDmaHdl);
      }
   }

   if (frag > 0) {
      xre->sg.length = frag;

      /* Give ownership to NIC */
      xre->sg.addrType = NET_SG_PHYS_ADDR;
      xre->ownership = VMXNET2_OWNERSHIP_NIC;
      xre->flags |= VMXNET2_TX_CAN_KEEP;
      txBuf->mblk = mblk;

      /*
       * If we called msgpullup to concatenate fragments, free
       * original mblk now since we're going to return success.
       */
      if (mblk != mp) {
         freemsg(mp);
      }

      return SOLVMXNET_SUCCESS;
   }

err:
   if (mblk != NULL && mblk != mp) {
      /*
       * Free mblk allocated by msgpullup.
       */
      freemsg(mblk);
   }

   if (dmaMemAlloced) {
      ASSERT(txBuf->dmaMem.buf);
      Vxn_FreeDmaMem(&txBuf->dmaMem);
   }

   return SOLVMXNET_FAILURE;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_Send --
 *    GLD Transmit routine. Starts packet hard tx.
 *
 * Results:
 *    GLD_SUCCESS on success
 *    GLD_FAILURE on failure
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static int
Vxn_Send(gld_mac_info_t *macinfo, mblk_t *mp)
{
   Vmxnet2_TxRingEntry *xre;
   int err = GLD_SUCCESS;
   vxn_softc_t *dp = (vxn_softc_t *)macinfo->gldm_private;
   Vmxnet2_DriverData *dd = dp->driverData;
   boolean_t resched = FALSE;

   mutex_enter(&dp->xmitlock);

   /*
    * Check if ring entry at drop pointer is available
    */
   if (TX_RINGBUF_MBLK(dp, dd->txDriverNext) != NULL) {
      DPRINTF(3, (CE_NOTE, "%s%d: Vxn_Send: tx ring full",
                  dp->drvName, dp->unit));
      err = GLD_NORESOURCES;
      dd->txStopped = TRUE;
      dp->stats.defer++;
      goto out;
   }

   xre = &dp->txRing[dd->txDriverNext];

   /*
    * Drop packet into ring entry
    */
   if (Vxn_EncapTxBuf(dp, mp, xre, &dp->txRingBuf[dd->txDriverNext])
       != SOLVMXNET_SUCCESS) {
      err = GLD_FAILURE;
      dp->stats.errxmt++;
      goto out;
   }

   /*
    * Increment drop pointer
    */
   VMXNET_INC(dd->txDriverNext, dd->txRingLength);
   dd->txNumDeferred++;
   dp->txPending++;

   /*
    * Transmit, if number of pending packets > tx cluster length
    */
   if (dd->txNumDeferred >= dd->txClusterLength) {
      dd->txNumDeferred = 0;

      /* 
       * Call hardware transmit 
       */
      INL(dp, VMXNET_TX_ADDR);
   }

   /*
    * Clean up transmit ring. TX completion interrupts are not guaranteed
    */
   (void) Vxn_TxComplete(dp, &resched);

out:
   mutex_exit(&dp->xmitlock);
   if (resched) {
      /* Tell GLD to retry any deferred packets */
      gld_sched(dp->macInfo);
   }
   return err;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_TxComplete --
 *    Scan Tx ring for completed transmits. Reclaim Tx buffers.
 *
 * Results:
 *    Returns TRUE if it found a completed transmit, FALSE otherwise.
 *    Also sets *reschedp to TRUE if the caller should call gld_sched
 *    to reschedule transmits (once all locks are dropped).
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static boolean_t
Vxn_TxComplete(vxn_softc_t *dp, boolean_t *reschedp)
{
   Vmxnet2_DriverData *dd = dp->driverData;
   boolean_t found = FALSE;
   boolean_t needresched = FALSE;

   ASSERT(MUTEX_HELD(&dp->xmitlock));

   while (1) {
      Vmxnet2_TxRingEntry *xre = &dp->txRing[dd->txDriverCur];

      if (xre->ownership != VMXNET2_OWNERSHIP_DRIVER || 
          (TX_RINGBUF_MBLK(dp, dd->txDriverCur) == NULL)) {
         break;
      }

      found = TRUE;
      Vxn_FreeTxBuf(dp, dd->txDriverCur);

      dp->txPending--;
      VMXNET_INC(dd->txDriverCur, dd->txRingLength);
      if (dd->txStopped) {
         needresched = TRUE;
         dd->txStopped = FALSE;
      }
   }

   *reschedp = needresched;
   return found;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_Receive --
 *    Rx handler. First assembles the packets into a chain of mblks,
 *    then drops locks and passes them up the stack to GLD.
 *
 * Results:
 *    Returns TRUE if it find a packet ready for processing, FALSE
 *    otherwise.
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static boolean_t
Vxn_Receive(vxn_softc_t *dp)
{
   Vmxnet2_DriverData *dd = dp->driverData;
   rx_dma_buf_t *rxDesc;
   rx_dma_buf_t *newRxDesc;
   mblk_t *mblk;
   mblk_t *head = NULL;
   mblk_t **tail = &head;
   mblk_t *next;
   boolean_t found = FALSE;	/* Did we find at least one packet? */

   ASSERT(MUTEX_HELD(&dp->intrlock));

   /*
    * Walk receive ring looking for entries with ownership
    * reverted back to driver
    */
   while (1) {
      Vmxnet2_RxRingEntry *rre;
      rx_dma_buf_t **rbuf;
      short pktlen;
      int ringnext = dd->rxDriverNext;

      rre = &dp->rxRing[ringnext];
      rbuf = &dp->rxRingBuffPtr[ringnext];

      if (rre->ownership != VMXNET2_OWNERSHIP_DRIVER) {
         break;
      }

      found = TRUE;

      pktlen = rre->actualLength;

      if (pktlen < (60 - 4)) {
         /*
          * Ethernet header vlan tags are 4 bytes.  Some vendors generate
          *  60byte frames including vlan tags.  When vlan tag
          *  is stripped, such frames become 60 - 4. (PR106153)
          */
         dp->stats.errrcv++;
         if (pktlen != 0) {
            DPRINTF(3, (CE_CONT, "%s%d: runt packet\n", dp->drvName, dp->unit));
            dp->stats.runt++;
         }
      } else {
         /*
          * Alloc new Rx buffer to replace current one
          */
         newRxDesc = Vxn_AllocRxBufFromPool(dp);

         if (newRxDesc) {
            rxDesc = *rbuf;
            mblk = rxDesc->mblk;
            
            *rbuf = newRxDesc;
            rre->paddr = newRxDesc->dmaDesc.phyBuf + ETHERALIGN;
            rre->bufferLength = MAXPKTBUF - ETHERALIGN;
            rre->actualLength = 0;

            /*
             * Advance write pointer past packet length
             */
            mblk->b_wptr = mblk->b_rptr + pktlen;
            
            /*
             * Add to end of chain.
             */
            mblk->b_next = NULL;
            *tail = mblk;
            tail = &mblk->b_next;
         } else {
            dp->stats.errrcv++;
            dp->stats.norcvbuf++;
         }
      }

      /* Give the descriptor back to NIC */
      rre->ownership = VMXNET2_OWNERSHIP_NIC;
      VMXNET_INC(dd->rxDriverNext, dd->rxRingLength);
   }

   /*
    * Walk chain and pass mblks up to gld_recv one by one.
    */
   mutex_exit(&dp->intrlock);
   for (mblk = head; mblk != NULL; mblk = next) {
      next = mblk->b_next;
      mblk->b_next = NULL;
      gld_recv(dp->macInfo, mblk);
   }
   mutex_enter(&dp->intrlock);

   return (found);
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_Interrupt --
 *    GLD interrupt handler. Scan: Rx ring for received packets, Tx ring for
 *    completed transmits
 *
 * Results:
 *    - DDI_INTR_CLAIMED (if we found something to do)
 *    - DDI_INTR_UNCLAIMED (if not)
 *
 * Side effects:
 *    None     
 *-----------------------------------------------------------------------------
 */
static u_int
Vxn_Interrupt(gld_mac_info_t *macInfo)
{
   u_int ret = DDI_INTR_UNCLAIMED;
   vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;
   boolean_t foundRx, foundTx;
   boolean_t resched = FALSE;

   mutex_enter(&dp->intrlock);
   dp->inIntr = TRUE;

   if (!dp->nicActive) {
      goto out;
   }

   /*
    * Ack interrupt
    */
   OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_ACK);

   foundRx = Vxn_Receive(dp);

   mutex_enter(&dp->xmitlock);
   foundTx = Vxn_TxComplete(dp, &resched);
   mutex_exit(&dp->xmitlock);

   if (foundRx || foundTx) {
      ret = DDI_INTR_CLAIMED;
      dp->stats.interrupts++;
   }

out:
   dp->inIntr = FALSE;
   mutex_exit(&dp->intrlock);

   if (resched) {
      gld_sched(dp->macInfo);
   }

   return ret; 
}


/*
 *-----------------------------------------------------------------------------
 * Vxn_ReclaimRxBuf --
 *    Callback handler invoked by freemsg(). Frees Rx buffer memory and mappings
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_ReclaimRxBuf(rx_dma_buf_t *rxDesc)
{
   Vxn_FreeRxBufToPool(rxDesc);
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_FreeRxBuf --
 *    Free allocated Rx buffer
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_FreeRxBuf(rx_dma_buf_t *rxDesc)
{
   ASSERT(rxDesc);

   if (rxDesc->mblk) {
      freemsg(rxDesc->mblk);
   } else {
      Vxn_FreeDmaMem(&rxDesc->dmaDesc);
      kmem_free(rxDesc, sizeof(rx_dma_buf_t));
   }
}


/*
 *-----------------------------------------------------------------------------
 * Vxn_AllocRxBuf --
 *    Allocate Rx buffer
 *
 * Results:
 *    Pointer to Rx buffer descriptor - on success
 *    NULL - on failure
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static rx_dma_buf_t *
Vxn_AllocRxBuf(vxn_softc_t *dp, int cansleep)
{
   rx_dma_buf_t *rxDesc;

   rxDesc = (rx_dma_buf_t *)kmem_zalloc(sizeof(rx_dma_buf_t), 
                                  cansleep ? KM_SLEEP : KM_NOSLEEP);
   if (!rxDesc) {
      cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBuf: kmem_zalloc failed", 
                       dp->drvName, dp->unit);
      return NULL;
   }

   rxDesc->softc = dp;

   /*
    * Alloc dma-able packet memory
    */
   if (Vxn_AllocDmaMem(dp, MAXPKTBUF, cansleep, &rxDesc->dmaDesc) 
         != SOLVMXNET_SUCCESS) {
      kmem_free(rxDesc, sizeof(rx_dma_buf_t));
      return NULL;
   }

   /*
    * Fill in free callback; fired by freemsg()
    */
   rxDesc->freeCB.free_func = &Vxn_ReclaimRxBuf;
   rxDesc->freeCB.free_arg = (caddr_t) rxDesc;

   rxDesc->mblk = NULL;
   return rxDesc;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_FreeInitBuffers --
 *    Free allocated Tx and Rx buffers 
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_FreeInitBuffers(vxn_softc_t *dp)
{
   int i;

   for (i=0; i<dp->vxnNumRxBufs; i++) {
      if (dp->rxRingBuffPtr[i]) {
         Vxn_FreeRxBuf(dp->rxRingBuffPtr[i]);
         dp->rxRingBuffPtr[i] = NULL;
      }
   }

   for (i=0; i<dp->vxnNumTxBufs; i++) {
      if (TX_RINGBUF_MBLK(dp, i)) {
         Vxn_FreeTxBuf(dp, i);
      }
   }

   /*
    * Rx pool must get freed last. Rx buffers above will
    * show up on the pool when freemsg callback fires.
    */
   Vxn_FreeRxBufPool(dp);
}


/*
 *-----------------------------------------------------------------------------
 * Vxn_AllocRxBufPool --
 *    Allocate pool of rx buffers - 3 * configured Rx buffers
 *
 * Results:
 *    SOLVMXNET_SUCCESS/SOLVMXNET_FAILURE
 *    
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static int
Vxn_AllocRxBufPool(vxn_softc_t *dp)
{
   int i;

   dp->rxFreeBufList = NULL;

   // Allow list to double in size if needed.  Any additional buffers
   // that are allocated on the fly will be freed back to main memory.
   dp->rxMaxFreeBufs = dp->vxnNumRxBufs * 6;

   for (i = 0; i < dp->vxnNumRxBufs * 3; i++) {
      rx_dma_buf_t *rxDesc;

      /*
       * Alloc rx buffer
       */
      if (!(rxDesc = Vxn_AllocRxBuf(dp, TRUE))) {
         cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBufPool: failed to allocate memory",
                 dp->drvName, dp->unit);
         dp->rxNumFreeBufs = i;
         return SOLVMXNET_FAILURE;
      }
      /*
       * Add to free list
       */
      rxDesc->next = dp->rxFreeBufList;
      dp->rxFreeBufList = rxDesc;
   }

   dp->rxNumFreeBufs = i;
   return SOLVMXNET_SUCCESS;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_FreeRxBufPool --
 *    Free rx buffers pool
 *
 * Results:
 *    None    
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_FreeRxBufPool(vxn_softc_t *dp)
{
   while (dp->rxFreeBufList) {
      rx_dma_buf_t *rxDesc = dp->rxFreeBufList;

      /* unlink */
      dp->rxFreeBufList = rxDesc->next;

      ASSERT(rxDesc->mblk == NULL);
      Vxn_FreeDmaMem(&rxDesc->dmaDesc);
      kmem_free(rxDesc, sizeof(rx_dma_buf_t));
   }
   dp->rxNumFreeBufs = 0;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_AllocRxBufFromPool --
 *    Allocate Rx buffer from free pool
 *
 * Results:
 *    Pointer to Rx buffer descriptor - on success
 *    NULL - on failure
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static rx_dma_buf_t *
Vxn_AllocRxBufFromPool(vxn_softc_t *dp)
{
   rx_dma_buf_t *rxDesc = NULL;

   mutex_enter(&dp->rxlistlock);
   if (dp->rxFreeBufList) {
      rxDesc = dp->rxFreeBufList;
      dp->rxFreeBufList = rxDesc->next;
      ASSERT(dp->rxNumFreeBufs >= 1);
      dp->rxNumFreeBufs--;
   }
   mutex_exit(&dp->rxlistlock);

   if (!rxDesc) {
      /*
       * Try to allocate new descriptor from memory.  Can't block here
       * since we could be being called from interrupt context.
       */
      DPRINTF(5, (CE_NOTE, "%s%d: allocating rx buf from memory",
                  dp->drvName, dp->unit)); 
      if (!(rxDesc = Vxn_AllocRxBuf(dp, FALSE))) {
         cmn_err(CE_WARN,
                 "%s%d: Vxn_AllocRxBufFromPool : pool rx alloc failed",
                 dp->drvName, dp->unit);
         return NULL;
      }
   }

   /*
    * Allocate new message block for this buffer
    */
   rxDesc->mblk = desballoc((uchar_t *)rxDesc->dmaDesc.buf + ETHERALIGN,
                           rxDesc->dmaDesc.bufLen - ETHERALIGN, 
                           BPRI_MED, &rxDesc->freeCB);
   if (!rxDesc->mblk) {
      cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBufFromPool : desballoc failed", 
                        dp->drvName, dp->unit);

      /* put back on free list */
      Vxn_FreeRxBufToPool(rxDesc);
      return NULL;
   }

   return rxDesc;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_FreeRxBufToPool --
 *    Return rx buffer to free pool
 *
 * Results:
 *    None   
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_FreeRxBufToPool(rx_dma_buf_t *rxDesc)
{
   vxn_softc_t *dp = rxDesc->softc;

   rxDesc->mblk = NULL;

   /*
    * Insert on free list, or free if the list is full
    */
   mutex_enter(&dp->rxlistlock);
   if (dp->rxNumFreeBufs >= dp->rxMaxFreeBufs) {
      DPRINTF(5, (CE_NOTE, "%s%d: freeing rx buf to memory", 
                  dp->drvName, dp->unit));
      Vxn_FreeRxBuf(rxDesc);
   } else {
      rxDesc->next = dp->rxFreeBufList;
      dp->rxFreeBufList = rxDesc;
      dp->rxNumFreeBufs++;
   }
   mutex_exit(&dp->rxlistlock);
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_AllocInitBuffers --
 *    Allocated Rx buffers and init ring entries
 *
 * Results:
 *    SOLVMXNET_SUCCESS - on success
 *    SOLVMXNET_FAILURE - on failure
 *
 * Side effects:
 *    None      
 *-----------------------------------------------------------------------------
 */
static int
Vxn_AllocInitBuffers(vxn_softc_t *dp)
{
   Vmxnet2_DriverData  *dd;
   uint32_t            i, offset;

   dd = dp->driverData;
   offset = sizeof(*dd);

   /*
    * Init shared structures
    */
   dd->rxRingLength = dp->vxnNumRxBufs;
   dd->rxRingOffset = offset;
   dp->rxRing = (Vmxnet2_RxRingEntry *)((uintptr_t)dd + offset);
   offset += dp->vxnNumRxBufs * sizeof(Vmxnet2_RxRingEntry);

   dd->rxRingLength2 = 1;
   dd->rxRingOffset2 = offset;
   offset += sizeof(Vmxnet2_RxRingEntry);

   dd->txRingLength = dp->vxnNumTxBufs;
   dd->txRingOffset = offset;
   dp->txRing = (Vmxnet2_TxRingEntry *)((uintptr_t)dd + offset);
   offset += dp->vxnNumTxBufs * sizeof(Vmxnet2_TxRingEntry);

   /*
    * Alloc Rx buffers pool
    */
   if ( Vxn_AllocRxBufPool(dp) != SOLVMXNET_SUCCESS) {
      cmn_err(CE_WARN, "%s%d: Vxn_AllocInitBuffers: failed to alloc buf pool", 
                       dp->drvName, dp->unit);
      return SOLVMXNET_FAILURE;
   }

   /*
    * Allocate receive buffers
    */
   for (i = 0; i < dp->vxnNumRxBufs; i++) {
      rx_dma_buf_t *rxDesc;
      Vmxnet2_RxRingEntry *rre = &dp->rxRing[i];

      if (!(rxDesc = Vxn_AllocRxBufFromPool(dp))) {
         cmn_err(CE_WARN, "%s%d: Vxn_AllocInitBuffers: "
                          "failed to alloc buf from pool", dp->drvName, dp->unit);
         goto err;
      }

      /*
       * Init ring entries
       */
      rre->paddr = rxDesc->dmaDesc.phyBuf + ETHERALIGN;
      rre->bufferLength = MAXPKTBUF - ETHERALIGN;
      rre->actualLength = 0;
      dp->rxRingBuffPtr[i] = rxDesc;
      rre->ownership = VMXNET2_OWNERSHIP_NIC;
   }

   dp->txDmaHdl = NULL;

   /* 
    * Dummy recvRing2 tacked on to the end, with a single unusable entry 
    */
   dp->rxRing[i].paddr = 0;
   dp->rxRing[i].bufferLength = 0;
   dp->rxRing[i].actualLength = 0;
   dp->rxRingBuffPtr[i] = NULL;
   dp->rxRing[i].ownership = VMXNET2_OWNERSHIP_DRIVER;
   
   dd->rxDriverNext = 0;
                                                                               
   /*
    * Give xmit ring ownership to DRIVER
    */
   for (i = 0; i < dp->vxnNumTxBufs; i++) {
      dp->txRing[i].ownership = VMXNET2_OWNERSHIP_DRIVER;
      dp->txRingBuf[i].mblk = NULL;
      dp->txRingBuf[i].dmaMem.buf = NULL;
      dp->txRing[i].sg.sg[0].addrHi = 0;
   }
   
   dd->txDriverCur = dd->txDriverNext = 0;
   dd->txStopped = FALSE;

   return SOLVMXNET_SUCCESS;

err:
   for (i=0; i<dp->vxnNumRxBufs; i++) {
      if (dp->rxRingBuffPtr[i]) {
         Vxn_FreeRxBuf(dp->rxRingBuffPtr[i]);
         dp->rxRingBuffPtr[i] = NULL;
      }
   }

   Vxn_FreeRxBufPool(dp);
   return SOLVMXNET_FAILURE;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_FreeDmaMem --
 *    Free allocated dma memory
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_FreeDmaMem(dma_buf_t *dma)
{
   ddi_dma_unbind_handle(dma->dmaHdl);
   ddi_dma_mem_free(&dma->dataAccHdl);
   ddi_dma_free_handle(&dma->dmaHdl);

   dma->buf = NULL;
   dma->phyBuf = NULL;
   dma->bufLen = 0;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_AllocDmaMem --
 *    Allocate dma-able memory and fill passed in dma descriptor pointer 
 *    if successful
 *
 * Results:
 *    SOLVMXNET_SUCCESS on success
 *    SOLVMXNET_FAILURE on failure
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static int
Vxn_AllocDmaMem(vxn_softc_t *dp, int size, int cansleep, dma_buf_t *dma)
{
   /* 
    * Allocate handle
    */
   if (ddi_dma_alloc_handle(dp->dip, &vxn_dma_attrs,
                            cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT, 
                            NULL, &dma->dmaHdl) != DDI_SUCCESS) {
      cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: failed to allocate handle", 
              dp->drvName, dp->unit);
      return SOLVMXNET_FAILURE;
   }

   /* 
    * Allocate memory 
    */
   if (ddi_dma_mem_alloc(dma->dmaHdl, size, &vxn_buf_attrs, DDI_DMA_CONSISTENT,
                         cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT, NULL, 
                         &dma->buf, &dma->bufLen, &dma->dataAccHdl) 
       != DDI_SUCCESS) {
      cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: "
                       "ddi_dma_mem_alloc %d bytes failed", 
                       dp->drvName, dp->unit, size);
      ddi_dma_free_handle(&dma->dmaHdl);
      return SOLVMXNET_FAILURE;
   }

   /*
    * Mapin memory
    */
   if (ddi_dma_addr_bind_handle(dma->dmaHdl, NULL, dma->buf, dma->bufLen,
                                DDI_DMA_RDWR | DDI_DMA_STREAMING,
                                cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT, 
                                NULL, &dma->cookie, &dma->cookieCount) 
       != DDI_DMA_MAPPED) {
      cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: failed to bind handle",
              dp->drvName, dp->unit);
      ddi_dma_mem_free(&dma->dataAccHdl);
      ddi_dma_free_handle(&dma->dmaHdl);
      return SOLVMXNET_FAILURE;
   }

   if (dma->cookieCount != 1) {
      cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: too many DMA cookies", 
                       dp->drvName, dp->unit);
      Vxn_FreeDmaMem(dma);
      return SOLVMXNET_FAILURE;
   }

   /*
    * Save physical address (for easy use)
    */
   dma->phyBuf = dma->cookie.dmac_address;

   return SOLVMXNET_SUCCESS;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_FreeDriverData --
 *   Free driver data structures and Tx Rx buffers
 *
 * Results:
 *   None
 *
 * Side effects:
 *   None
 *-----------------------------------------------------------------------------
 */
static void
Vxn_FreeDriverData(vxn_softc_t *dp)
{
   Vxn_FreeInitBuffers(dp);
   Vxn_FreeDmaMem(&dp->driverDataDmaMem);
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_AllocDriverData --
 *    Allocate driver data structures and Tx Rx buffers on init
 *
 * Results:
 *    SOLVMXNET_SUCCESS on success
 *    SOLVMXNET_FAILURE on failure
 *
 * Side effects:
 *    None
 *-----------------------------------------------------------------------------
 */
static int
Vxn_AllocDriverData(vxn_softc_t *dp)
{
   uint32_t r, driverDataSize;

   /*
    * Get configured receive buffers
    */
   OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_NUM_RX_BUFFERS);
   r = INL(dp, VMXNET_COMMAND_ADDR);
   if (r == 0 || r > MAX_NUM_RECV_BUFFERS) {
      r = DEFAULT_NUM_RECV_BUFFERS;
   }
   dp->vxnNumRxBufs = r;

   /*
    * Get configured transmit buffers
    */
   OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_NUM_TX_BUFFERS);
   r = INL(dp, VMXNET_COMMAND_ADDR);
   if (r == 0 || r > MAX_NUM_XMIT_BUFFERS) {
      r = DEFAULT_NUM_XMIT_BUFFERS;
   }
   dp->vxnNumTxBufs = r;

   /*
    * Calculate shared data size and allocate memory for it
    */
   driverDataSize =
      sizeof(Vmxnet2_DriverData) +
      /* numRecvBuffers + 1 for the dummy recvRing2 (used only by Windows) */
      (dp->vxnNumRxBufs + 1) * sizeof(Vmxnet2_RxRingEntry) +
      dp->vxnNumTxBufs * sizeof(Vmxnet2_TxRingEntry);

   if (Vxn_AllocDmaMem(dp, driverDataSize, TRUE, &dp->driverDataDmaMem) 
        != SOLVMXNET_SUCCESS) {
      return SOLVMXNET_FAILURE;
   }

   /*
    * Clear memory (bzero isn't resolved by module loader for some reason) 
    */
   ASSERT(dp->driverDataDmaMem.buf && dp->driverDataDmaMem.bufLen);
   Vxn_Memset(dp->driverDataDmaMem.buf, 0, dp->driverDataDmaMem.bufLen);

   dp->driverData = (Vmxnet2_DriverData *)dp->driverDataDmaMem.buf;
   dp->driverDataPhy = (void *)(uintptr_t)dp->driverDataDmaMem.phyBuf;

   /* So that the vmkernel can check it is compatible */
   dp->driverData->magic = VMXNET2_MAGIC;
   dp->driverData->length = driverDataSize;

   /*
    * Alloc rx/tx buffers, init ring, register with hardware etc.
    */
   if (Vxn_AllocInitBuffers(dp) != SOLVMXNET_SUCCESS) {
      Vxn_FreeDmaMem(&dp->driverDataDmaMem);
      return SOLVMXNET_FAILURE;
   }

   DPRINTF(3, (CE_CONT, "%s%d: numRxBufs=(%d*%"FMT64"d) numTxBufs=(%d*%"FMT64"d)" 
               " driverDataSize=%d driverDataPhy=0x%p\n", 
               dp->drvName, dp->unit, 
               dp->vxnNumRxBufs, (uint64_t)sizeof(Vmxnet2_RxRingEntry),
               dp->vxnNumTxBufs, (uint64_t)sizeof(Vmxnet2_TxRingEntry),
               driverDataSize, dp->driverDataPhy));

   return SOLVMXNET_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 * Vxn_Attach --
 *    Probe and attach driver to stack
 *
 * Results:
 *    DDI_SUCCESS
 *    DDI_FAILURE
 *
 * Side effects:
 *    None 
 *-----------------------------------------------------------------------------
 */
static int
Vxn_Attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
   int                  i, ret, len, unit;
   const char           *drvName;
   ddi_acc_handle_t     confHdl;
   uint16_t             vid, did;
   uint8_t              revid;
   struct pci_phys_spec *regs;
   caddr_t              vxnIOp;
   ddi_acc_handle_t     vxnIOHdl;
   uint32_t             vLow, vHigh;
   gld_mac_info_t       *macInfo;
   vxn_softc_t          *dp;
   boolean_t            morphed = FALSE;
   uint_t               regSpaceSize;
   uint_t               chip;
   uint_t               vxnIOSize;

   if (cmd != DDI_ATTACH) {
      return DDI_FAILURE;
   }

   unit = ddi_get_instance(dip);
   drvName = ddi_driver_name(dip);

   /*
    * Check if chip is supported.
    */
   if (pci_config_setup(dip, &confHdl) != DDI_SUCCESS) {
      cmn_err(CE_WARN, "%s%d: pci_config_setup() failed", drvName, unit);
      return DDI_FAILURE;
   }

   vid   = pci_config_get16(confHdl, PCI_CONF_VENID);
   did   = pci_config_get16(confHdl, PCI_CONF_DEVID);
   revid = pci_config_get8(confHdl, PCI_CONF_REVID);

   if (vid == PCI_VENDOR_ID_VMWARE && did == PCI_DEVICE_ID_VMWARE_NET) {
      /* Found vmxnet */
      chip = VMXNET_CHIP;
   }
   else if (vid == PCI_VENDOR_ID_AMD && did == PCI_DEVICE_ID_AMD_VLANCE) {
      /* Found vlance (maybe a vmxnet disguise) */
      chip = LANCE_CHIP;
   }
   else {
      /* Not Found */
      DPRINTF(3, (CE_WARN, "%s: Vxn_Attach: wrong PCI venid/devid (0x%x, 0x%x)",
              drvName, vid, did));
      goto err;
   }

   DPRINTF(3, (CE_CONT, "%s%d: (vid: 0x%04x, did: 0x%04x, revid: 0x%02x)\n",
           drvName, unit, vid, did, revid));

   /* 
    * Get device properties
    */
   regs = NULL;
   len  = 0;
   if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
                       "reg", (caddr_t)&regs, &len) != DDI_PROP_SUCCESS) {
      cmn_err(CE_WARN, "%s%d: Vxn_Attach: failed to get reg property", 
              drvName, unit);
      goto err;
   }

   ASSERT(regs != NULL && len > 0);

   /* 
    * Search device properties for IO-space 
    */
   for (i = 0; i <len / sizeof(struct pci_phys_spec); i++) {
      if ((regs[i].pci_phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_IO) {
         regSpaceSize = regs[i].pci_size_low;
         DPRINTF(5, (CE_CONT, "%s%d: Vxn_Attach: regSpaceSize=%d\n", 
                     drvName, unit, regSpaceSize));
         kmem_free(regs, len);
         goto map_space_found;
      }
   }

   cmn_err(CE_WARN, "%s%d: Vxn_Attach: failed to find IO space", drvName, unit);
   kmem_free(regs, len);
   goto err;

map_space_found:

   /* 
    * Ensure we can access registers through IO space. 
    */
   ret = pci_config_get16(confHdl, PCI_CONF_COMM);
   ret |= PCI_COMM_IO | PCI_COMM_ME;
   pci_config_put16(confHdl, PCI_CONF_COMM, ret);

   if (ddi_regs_map_setup(dip, i, (caddr_t *)&vxnIOp, 0, 0, &dev_attr, 
                          &vxnIOHdl) != DDI_SUCCESS) {
      cmn_err(CE_WARN, "%s%d: Vxn_Attach: ddi_regs_map_setup failed", 
              drvName, unit);
      goto err;
   }

   if (chip == VMXNET_CHIP) {
      vxnIOSize = VMXNET_CHIP_IO_RESV_SIZE;
   }
   else {
      /*
       * Since this is a vlance adapter we can only use it if
       * its I/0 space is big enough for the adapter to be
       * capable of morphing. This is the first requirement
       * for this adapter to potentially be morphable. The
       * layout of a morphable LANCE adapter is
       *
       * I/O space:
       *
       * |------------------| 
       * | LANCE IO PORTS   |
       * |------------------|
       * | MORPH PORT       |
       * |------------------|
       * | VMXNET IO PORTS  |
       * |------------------|
       *
       * VLance has 8 ports of size 4 bytes, the morph port is 4 bytes, and
       * Vmxnet has 10 ports of size 4 bytes.
       *
       * We shift up the ioaddr with the size of the LANCE I/O space since
       * we want to access the vmxnet ports. We also shift the ioaddr up by
       * the MORPH_PORT_SIZE so other port access can be independent of
       * whether we are Vmxnet or a morphed VLance. This means that when
       * we want to access the MORPH port we need to subtract the size
       * from ioaddr to get to it.
       */
      vxnIOp += LANCE_CHIP_IO_RESV_SIZE + MORPH_PORT_SIZE;
      vxnIOSize = LANCE_CHIP_IO_RESV_SIZE + MORPH_PORT_SIZE +
                  VMXNET_CHIP_IO_RESV_SIZE;
   }

   /*
    * Do not attempt to morph non-morphable AMD PCnet
    */
   if (vxnIOSize > regSpaceSize) {
      cmn_err(CE_WARN, "%s%d: Vxn_Attach: "
              "vlance device is not supported by this driver", drvName, unit);
      goto err_free_regs_map;
   }

   /*
    * Morph, if we found a vlance adapter
    */
   if (chip == LANCE_CHIP) {
      uint16_t magic;

      /* Read morph port to verify that we can morph the adapter */
      magic = ddi_get16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE));
      if (magic != LANCE_CHIP && magic != VMXNET_CHIP) {
         cmn_err(CE_WARN, "%s%d: Vxn_Attach: Invalid magic, read: 0x%08X",
                 drvName, unit, magic);
         goto err_free_regs_map;
      }

      /* Morph */
      ddi_put16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE), VMXNET_CHIP);
      morphed = TRUE;

      /* Verify that we morphed correctly */
      magic = ddi_get16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE));
      if (magic != VMXNET_CHIP) {
         cmn_err(CE_WARN, "%s%d: Vxn_Attach: Couldn't morph adapter."
                 " Invalid magic, read:: 0x%08X", drvName, unit, magic);
         goto err_morph_back;
      }
   }

   /*
    * Check the version number of the device implementation
    */ 
   vLow  = (uint32_t)ddi_get32(vxnIOHdl, 
                   (uint32_t *)(vxnIOp+VMXNET_LOW_VERSION));
   vHigh = (uint32_t)ddi_get32(vxnIOHdl, 
                   (uint32_t *)(vxnIOp+VMXNET_HIGH_VERSION));

   if ((vLow & 0xffff0000) != (VMXNET2_MAGIC & 0xffff0000) || 
       ((VMXNET2_MAGIC < vLow) || (VMXNET2_MAGIC > vHigh))) {
      cmn_err(CE_WARN, "%s%d: Vxn_Attach: driver version 0x%08X doesn't "
                       "match device 0x%08X:0x%08X", 
              drvName, unit, VMXNET2_MAGIC, vLow, vHigh);
      goto err_version_mismatch;
   }

   /*
    * Alloc soft state
    */
   macInfo = gld_mac_alloc(dip);
   if (!macInfo) {
      cmn_err(CE_WARN, "%s%d: Vxn_Attach: gld_mac_alloc failed",
              drvName, unit);
      goto err_gld_mac_alloc;
   }

   dp = (vxn_softc_t *) kmem_zalloc(sizeof(vxn_softc_t), KM_SLEEP);
   ASSERT(dp);

   /*
    * Get interrupt cookie
    */
   if (ddi_get_iblock_cookie(dip, 0, &dp->iblockCookie) != DDI_SUCCESS) {
      cmn_err(CE_WARN, "%s%d: Vxn_Attach: ddi_get_iblock_cookie failed",
              drvName, unit);
      goto err_get_iblock_cookie;
   }

   /*
    * kmem_zalloc above memsets drvName to 0. Use array size - 1 below
    * to ensure NUL termination.
    */
   strncpy(dp->drvName, drvName, sizeof dp->drvName - 1);
   dp->unit = unit;
   dp->dip = dip;
   dp->macInfo = macInfo;
   dp->confHdl = confHdl;
   dp->vxnIOHdl = vxnIOHdl;
   dp->vxnIOp = vxnIOp;
   dp->morphed = morphed;
   dp->nicActive = FALSE;
   dp->txPending = 0;
   dp->maxTxFrags = 1;

   /*
    * Initialize mutexes
    */
   mutex_init(&dp->intrlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);
   mutex_init(&dp->xmitlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);
   mutex_init(&dp->rxlistlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);

   /* 
    * Allocate and initialize our private and shared data structures
    */
   if (Vxn_AllocDriverData(dp) != SOLVMXNET_SUCCESS) {
      goto err_alloc_driverdata;
   }

   /*
    * Read the MAC address from the device
    */
   for (i = 0; i < 6; i++) {
      dp->devAddr.ether_addr_octet[i] = 
         (uint8_t)ddi_get8(vxnIOHdl, (uint8_t *)(vxnIOp + VMXNET_MAC_ADDR + i));
   }
   macInfo->gldm_vendor_addr = dp->devAddr.ether_addr_octet;
   macInfo->gldm_broadcast_addr = etherbroadcastaddr.ether_addr_octet;

   DPRINTF(3, (CE_CONT,
           "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
           dp->devAddr.ether_addr_octet[0],
           dp->devAddr.ether_addr_octet[1],
           dp->devAddr.ether_addr_octet[2],
           dp->devAddr.ether_addr_octet[3],
           dp->devAddr.ether_addr_octet[4],
           dp->devAddr.ether_addr_octet[5]));

   /*
    * Configure GLD entry points
    */
   macInfo->gldm_devinfo      = dip;
   macInfo->gldm_private      = (caddr_t)dp;
   macInfo->gldm_cookie       = dp->iblockCookie;
   macInfo->gldm_reset        = Vxn_Reset;
   macInfo->gldm_start        = Vxn_Start;
   macInfo->gldm_stop         = Vxn_Stop;
   macInfo->gldm_set_mac_addr = Vxn_SetMacAddress;
   macInfo->gldm_send         = Vxn_Send;
   macInfo->gldm_set_promiscuous = Vxn_SetPromiscuous;
   macInfo->gldm_get_stats    = Vxn_GetStats;
   macInfo->gldm_ioctl        = NULL;
   macInfo->gldm_set_multicast= Vxn_SetMulticast;
   macInfo->gldm_intr         = Vxn_Interrupt;
   macInfo->gldm_mctl         = NULL;
   
   macInfo->gldm_ident        = (char *)ddi_driver_name(dip);
   macInfo->gldm_type         = DL_ETHER;
   macInfo->gldm_minpkt       = 0;
   macInfo->gldm_maxpkt       = ETHERMTU;
   macInfo->gldm_addrlen      = ETHERADDRL;
   macInfo->gldm_saplen       = -2;
   macInfo->gldm_ppa          = unit;

   /*
    * Register with GLD (Generic Lan Driver) framework
    */
   if (gld_register(dip,
            (char *)ddi_driver_name(dip), macInfo) != DDI_SUCCESS) {
      goto err_gld_register;
   }

   /*
    * Add interrupt to system.
    */
   if (ddi_add_intr(dip, 0, NULL, NULL, gld_intr,
                    (caddr_t)macInfo) != DDI_SUCCESS) {
      cmn_err(CE_WARN, "%s%d: ddi_add_intr failed", drvName, unit);
      goto err_ddi_add_intr;
   }

   /*
    * Add to list of interfaces.
    */
   mutex_enter(&vxnListLock);
   dp->next = &vxnList;
   dp->prev = vxnList.prev;
   vxnList.prev->next = dp;
   vxnList.prev = dp;
   mutex_exit(&vxnListLock);

   /*
    * Success
    */
   return DDI_SUCCESS;

err_ddi_add_intr:
   gld_unregister(macInfo);

err_gld_register:
   Vxn_FreeDriverData(dp);

err_alloc_driverdata:
   mutex_destroy(&dp->intrlock);
   mutex_destroy(&dp->xmitlock);

err_get_iblock_cookie:
   kmem_free(dp, sizeof(*dp));
   gld_mac_free(macInfo);

err_gld_mac_alloc:
err_version_mismatch:
err_morph_back:
   if (morphed) {
      ddi_put16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE), LANCE_CHIP);
   }

err_free_regs_map:
   ddi_regs_map_free(&vxnIOHdl);

err:
   pci_config_teardown(&confHdl);
   return DDI_FAILURE;
}

/*
 *-----------------------------------------------------------------------------
 * Vxn_Detach --
 *    Called on module unload
 *
 * Results:
 *    DDI_SUCCESS
 *    DDI_FAILURE
 *
 * Side effects:
 *    None   
 *-----------------------------------------------------------------------------
 */
static int
Vxn_Detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
   gld_mac_info_t *macInfo;
   vxn_softc_t    *dp;

   macInfo = (gld_mac_info_t *)ddi_get_driver_private(dip);
   dp = (vxn_softc_t *)macInfo->gldm_private;

   if (cmd == DDI_DETACH) {
      /*
       * Tear down interrupt
       */
      ddi_remove_intr(dip, 0,  macInfo->gldm_cookie);
      gld_unregister(macInfo);

      /*
       * Quiesce hardware
       */
      Vxn_Stop(macInfo);

      /*
       * Free driver-data, tx/rx buffers etc
       */
      Vxn_FreeDriverData(dp);

      /*
       * Destroy locks
       */
      mutex_destroy(&dp->intrlock);
      mutex_destroy(&dp->xmitlock);

      /*
       * Unmorph
       */
      if (dp->morphed) {
         uint16_t magic;

         /* Verify that we had morphed earlier */
         magic = ddi_get16(dp->vxnIOHdl, 
                           (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE));
         if (magic != VMXNET_CHIP) {
            cmn_err(CE_WARN, "%s%d: Vxn_Detach: Adapter not morphed"
                             " magic=0x%08X", dp->drvName, dp->unit, magic);
         }
         else {
            /* Unmorph */
            ddi_put16(dp->vxnIOHdl, 
                      (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE), LANCE_CHIP);

            /* Verify */
            magic = ddi_get16(dp->vxnIOHdl, 
                              (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE));
            if (magic != LANCE_CHIP) {
               cmn_err(CE_WARN, "%s%d: Vxn_Detach: Unable to unmorph adapter"
                             " magic=0x%08X", dp->drvName, dp->unit, magic);
            }
         }
      }

      /*
       * Release resister mappings
       */ 
      ddi_regs_map_free(&dp->vxnIOHdl);
      pci_config_teardown(&dp->confHdl);

      /*
       * Remove from list of interfaces.
       */
      mutex_enter(&vxnListLock);
      ASSERT(dp != &vxnList);
      dp->prev->next = dp->next;
      dp->next->prev = dp->prev;
      mutex_exit(&vxnListLock);

      /*
       * Release memory
       */
      kmem_free(dp, sizeof(*dp));
      gld_mac_free(macInfo);

      return DDI_SUCCESS;
   }
   else {
      return DDI_FAILURE;
   }
}

static   struct module_info vxnminfo = {
  0,                    /* mi_idnum */
  "vmxnet",             /* mi_idname */
  0,                    /* mi_minpsz */
  ETHERMTU,             /* mi_maxpsz */
  QHIWATER,             /* mi_hiwat */
  1,                    /* mi_lowat */
};

static   struct qinit vxnrinit = {
   NULL,                /* qi_putp */
   gld_rsrv,            /* qi_srvp */
   gld_open,            /* qi_qopen */
   gld_close,           /* qi_qclose */
   NULL,                /* qi_qadmin */
   &vxnminfo,           /* qi_minfo */
   NULL                 /* qi_mstat */
};

static   struct qinit vxnwinit = {
   gld_wput,            /* qi_putp */
   gld_wsrv,            /* qi_srvp */
   NULL,                /* qi_qopen */
   NULL,                /* qi_qclose */
   NULL,                /* qi_qadmin */
   &vxnminfo,           /* qi_minfo */
   NULL                 /* qi_mstat */
};

static struct streamtab vxn_info = {
   &vxnrinit,           /* st_rdinit */
   &vxnwinit,           /* st_wrinit */
   NULL,                /* st_muxrinit */
   NULL                 /* st_muxwrinit */
};

static   struct cb_ops cb_vxn_ops = {
   nulldev,             /* cb_open */
   nulldev,             /* cb_close */
   nodev,               /* cb_strategy */
   nodev,               /* cb_print */
   nodev,               /* cb_dump */
   nodev,               /* cb_read */
   nodev,               /* cb_write */
   nodev,               /* cb_ioctl */
   nodev,               /* cb_devmap */
   nodev,               /* cb_mmap */
   nodev,               /* cb_segmap */
   nochpoll,            /* cb_chpoll */
   ddi_prop_op,         /* cb_prop_op */
   &vxn_info,           /* cb_stream */
   D_NEW|D_MP           /* cb_flag */
};

static   struct dev_ops vxn_ops = {
   DEVO_REV,            /* devo_rev */
   0,                   /* devo_refcnt */
   gld_getinfo,         /* devo_getinfo */
   nulldev,             /* devo_identify */
   nulldev,             /* devo_probe */
   Vxn_Attach,          /* devo_attach */
   Vxn_Detach,          /* devo_detach */
   nodev,               /* devo_reset */
   &cb_vxn_ops,         /* devo_cb_ops */
   NULL,                /* devo_bus_ops */
#ifdef SOL11
	NULL,
#else
   ddi_power            /* devo_power */
#endif
};

static struct modldrv modldrv = {
   &mod_driverops,
   ident,
   &vxn_ops,
};

static struct modlinkage modlinkage = {
   MODREV_1, {&modldrv, NULL,}
};


/*
 * Module load entry point
 */
int
_init(void)
{
   int err;

   DPRINTF(5, (CE_CONT, "vxn: _init:\n"));
   /* Initialize interface list */
   vxnList.next = vxnList.prev = &vxnList;
   mutex_init(&vxnListLock, NULL, MUTEX_DRIVER, NULL);
   if ((err = mod_install(&modlinkage)) != 0) {
      mutex_destroy(&vxnListLock);
   }
   return err;
}

/*
 * Module unload entry point
 */
int
_fini(void)
{
   int err;

   DPRINTF(5, (CE_CONT, "vxn: _fini:\n"));
   if ((err = mod_remove(&modlinkage)) == 0) {
      mutex_destroy(&vxnListLock);
   }
   return err;
}

/*
 * Module info entry point
 */
int
_info(struct modinfo *modinfop)
{
   return (mod_info(&modlinkage, modinfop));
}

   070701000002A5000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3    070701000002A6000081A400000000000000000000000168225505000046CA000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/COPYING    
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0

1. Definitions.

    1.1. "Contributor" means each individual or entity that creates
         or contributes to the creation of Modifications.

    1.2. "Contributor Version" means the combination of the Original
         Software, prior Modifications used by a Contributor (if any),
         and the Modifications made by that particular Contributor.

    1.3. "Covered Software" means (a) the Original Software, or (b)
         Modifications, or (c) the combination of files containing
         Original Software with files containing Modifications, in
         each case including portions thereof.

    1.4. "Executable" means the Covered Software in any form other
         than Source Code.

    1.5. "Initial Developer" means the individual or entity that first
         makes Original Software available under this License.

    1.6. "Larger Work" means a work which combines Covered Software or
         portions thereof with code not governed by the terms of this
         License.

    1.7. "License" means this document.

    1.8. "Licensable" means having the right to grant, to the maximum
         extent possible, whether at the time of the initial grant or
         subsequently acquired, any and all of the rights conveyed
         herein.

    1.9. "Modifications" means the Source Code and Executable form of
         any of the following:

        A. Any file that results from an addition to, deletion from or
           modification of the contents of a file containing Original
           Software or previous Modifications;

        B. Any new file that contains any part of the Original
           Software or previous Modifications; or

        C. Any new file that is contributed or otherwise made
           available under the terms of this License.

    1.10. "Original Software" means the Source Code and Executable
          form of computer software code that is originally released
          under this License.

    1.11. "Patent Claims" means any patent claim(s), now owned or
          hereafter acquired, including without limitation, method,
          process, and apparatus claims, in any patent Licensable by
          grantor.

    1.12. "Source Code" means (a) the common form of computer software
          code in which modifications are made and (b) associated
          documentation included in or with such code.

    1.13. "You" (or "Your") means an individual or a legal entity
          exercising rights under, and complying with all of the terms
          of, this License.  For legal entities, "You" includes any
          entity which controls, is controlled by, or is under common
          control with You.  For purposes of this definition,
          "control" means (a) the power, direct or indirect, to cause
          the direction or management of such entity, whether by
          contract or otherwise, or (b) ownership of more than fifty
          percent (50%) of the outstanding shares or beneficial
          ownership of such entity.

2. License Grants.

    2.1. The Initial Developer Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, the Initial
    Developer hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Initial Developer, to use,
            reproduce, modify, display, perform, sublicense and
            distribute the Original Software (or portions thereof),
            with or without Modifications, and/or as part of a Larger
            Work; and

        (b) under Patent Claims infringed by the making, using or
            selling of Original Software, to make, have made, use,
            practice, sell, and offer for sale, and/or otherwise
            dispose of the Original Software (or portions thereof).

        (c) The licenses granted in Sections 2.1(a) and (b) are
            effective on the date Initial Developer first distributes
            or otherwise makes the Original Software available to a
            third party under the terms of this License.

        (d) Notwithstanding Section 2.1(b) above, no patent license is
            granted: (1) for code that You delete from the Original
            Software, or (2) for infringements caused by: (i) the
            modification of the Original Software, or (ii) the
            combination of the Original Software with other software
            or devices.

    2.2. Contributor Grant.

    Conditioned upon Your compliance with Section 3.1 below and
    subject to third party intellectual property claims, each
    Contributor hereby grants You a world-wide, royalty-free,
    non-exclusive license:

        (a) under intellectual property rights (other than patent or
            trademark) Licensable by Contributor to use, reproduce,
            modify, display, perform, sublicense and distribute the
            Modifications created by such Contributor (or portions
            thereof), either on an unmodified basis, with other
            Modifications, as Covered Software and/or as part of a
            Larger Work; and

        (b) under Patent Claims infringed by the making, using, or
            selling of Modifications made by that Contributor either
            alone and/or in combination with its Contributor Version
            (or portions of such combination), to make, use, sell,
            offer for sale, have made, and/or otherwise dispose of:
            (1) Modifications made by that Contributor (or portions
            thereof); and (2) the combination of Modifications made by
            that Contributor with its Contributor Version (or portions
            of such combination).

        (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
            effective on the date Contributor first distributes or
            otherwise makes the Modifications available to a third
            party.

        (d) Notwithstanding Section 2.2(b) above, no patent license is
            granted: (1) for any code that Contributor has deleted
            from the Contributor Version; (2) for infringements caused
            by: (i) third party modifications of Contributor Version,
            or (ii) the combination of Modifications made by that
            Contributor with other software (except as part of the
            Contributor Version) or other devices; or (3) under Patent
            Claims infringed by Covered Software in the absence of
            Modifications made by that Contributor.

3. Distribution Obligations.

    3.1. Availability of Source Code.

    Any Covered Software that You distribute or otherwise make
    available in Executable form must also be made available in Source
    Code form and that Source Code form must be distributed only under
    the terms of this License.  You must include a copy of this
    License with every copy of the Source Code form of the Covered
    Software You distribute or otherwise make available.  You must
    inform recipients of any such Covered Software in Executable form
    as to how they can obtain such Covered Software in Source Code
    form in a reasonable manner on or through a medium customarily
    used for software exchange.

    3.2. Modifications.

    The Modifications that You create or to which You contribute are
    governed by the terms of this License.  You represent that You
    believe Your Modifications are Your original creation(s) and/or
    You have sufficient rights to grant the rights conveyed by this
    License.

    3.3. Required Notices.

    You must include a notice in each of Your Modifications that
    identifies You as the Contributor of the Modification.  You may
    not remove or alter any copyright, patent or trademark notices
    contained within the Covered Software, or any notices of licensing
    or any descriptive text giving attribution to any Contributor or
    the Initial Developer.

    3.4. Application of Additional Terms.

    You may not offer or impose any terms on any Covered Software in
    Source Code form that alters or restricts the applicable version
    of this License or the recipients' rights hereunder.  You may
    choose to offer, and to charge a fee for, warranty, support,
    indemnity or liability obligations to one or more recipients of
    Covered Software.  However, you may do so only on Your own behalf,
    and not on behalf of the Initial Developer or any Contributor.
    You must make it absolutely clear that any such warranty, support,
    indemnity or liability obligation is offered by You alone, and You
    hereby agree to indemnify the Initial Developer and every
    Contributor for any liability incurred by the Initial Developer or
    such Contributor as a result of warranty, support, indemnity or
    liability terms You offer.

    3.5. Distribution of Executable Versions.

    You may distribute the Executable form of the Covered Software
    under the terms of this License or under the terms of a license of
    Your choice, which may contain terms different from this License,
    provided that You are in compliance with the terms of this License
    and that the license for the Executable form does not attempt to
    limit or alter the recipient's rights in the Source Code form from
    the rights set forth in this License.  If You distribute the
    Covered Software in Executable form under a different license, You
    must make it absolutely clear that any terms which differ from
    this License are offered by You alone, not by the Initial
    Developer or Contributor.  You hereby agree to indemnify the
    Initial Developer and every Contributor for any liability incurred
    by the Initial Developer or such Contributor as a result of any
    such terms You offer.

    3.6. Larger Works.

    You may create a Larger Work by combining Covered Software with
    other code not governed by the terms of this License and
    distribute the Larger Work as a single product.  In such a case,
    You must make sure the requirements of this License are fulfilled
    for the Covered Software.

4. Versions of the License.

    4.1. New Versions.

    Sun Microsystems, Inc. is the initial license steward and may
    publish revised and/or new versions of this License from time to
    time.  Each version will be given a distinguishing version number.
    Except as provided in Section 4.3, no one other than the license
    steward has the right to modify this License.

    4.2. Effect of New Versions.

    You may always continue to use, distribute or otherwise make the
    Covered Software available under the terms of the version of the
    License under which You originally received the Covered Software.
    If the Initial Developer includes a notice in the Original
    Software prohibiting it from being distributed or otherwise made
    available under any subsequent version of the License, You must
    distribute and make the Covered Software available under the terms
    of the version of the License under which You originally received
    the Covered Software.  Otherwise, You may also choose to use,
    distribute or otherwise make the Covered Software available under
    the terms of any subsequent version of the License published by
    the license steward.

    4.3. Modified Versions.

    When You are an Initial Developer and You want to create a new
    license for Your Original Software, You may create and use a
    modified version of this License if You: (a) rename the license
    and remove any references to the name of the license steward
    (except to note that the license differs from this License); and
    (b) otherwise make it clear that the license contains terms which
    differ from this License.

5. DISCLAIMER OF WARRANTY.

    COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
    BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
    INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
    SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
    PURPOSE OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND
    PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU.  SHOULD ANY
    COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
    INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
    NECESSARY SERVICING, REPAIR OR CORRECTION.  THIS DISCLAIMER OF
    WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.  NO USE OF
    ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
    DISCLAIMER.

6. TERMINATION.

    6.1. This License and the rights granted hereunder will terminate
    automatically if You fail to comply with terms herein and fail to
    cure such breach within 30 days of becoming aware of the breach.
    Provisions which, by their nature, must remain in effect beyond
    the termination of this License shall survive.

    6.2. If You assert a patent infringement claim (excluding
    declaratory judgment actions) against Initial Developer or a
    Contributor (the Initial Developer or Contributor against whom You
    assert such claim is referred to as "Participant") alleging that
    the Participant Software (meaning the Contributor Version where
    the Participant is a Contributor or the Original Software where
    the Participant is the Initial Developer) directly or indirectly
    infringes any patent, then any and all rights granted directly or
    indirectly to You by such Participant, the Initial Developer (if
    the Initial Developer is not the Participant) and all Contributors
    under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
    notice from Participant terminate prospectively and automatically
    at the expiration of such 60 day notice period, unless if within
    such 60 day period You withdraw Your claim with respect to the
    Participant Software against such Participant either unilaterally
    or pursuant to a written agreement with Participant.

    6.3. In the event of termination under Sections 6.1 or 6.2 above,
    all end user licenses that have been validly granted by You or any
    distributor hereunder prior to termination (excluding licenses
    granted to You by any distributor) shall survive termination.

7. LIMITATION OF LIABILITY.

    UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
    (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
    INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
    COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
    LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
    CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
    LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
    STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
    COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
    INFORMED OF THE POSSIBILITY OF SUCH DAMAGES.  THIS LIMITATION OF
    LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
    INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
    APPLICABLE LAW PROHIBITS SUCH LIMITATION.  SOME JURISDICTIONS DO
    NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
    CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
    APPLY TO YOU.

8. U.S. GOVERNMENT END USERS.

    The Covered Software is a "commercial item," as that term is
    defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
    computer software" (as that term is defined at 48
    C.F.R. 252.227-7014(a)(1)) and "commercial computer software
    documentation" as such terms are used in 48 C.F.R. 12.212
    (Sept. 1995).  Consistent with 48 C.F.R. 12.212 and 48
    C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
    U.S. Government End Users acquire Covered Software with only those
    rights set forth herein.  This U.S. Government Rights clause is in
    lieu of, and supersedes, any other FAR, DFAR, or other clause or
    provision that addresses Government rights in computer software
    under this License.

9. MISCELLANEOUS.

    This License represents the complete agreement concerning subject
    matter hereof.  If any provision of this License is held to be
    unenforceable, such provision shall be reformed only to the extent
    necessary to make it enforceable.  This License shall be governed
    by the law of the jurisdiction specified in a notice contained
    within the Original Software (except to the extent applicable law,
    if any, provides otherwise), excluding such jurisdiction's
    conflict-of-law provisions.  Any litigation relating to this
    License shall be subject to the jurisdiction of the courts located
    in the jurisdiction and venue specified in a notice contained
    within the Original Software, with the losing party responsible
    for costs, including, without limitation, court costs and
    reasonable attorneys' fees and expenses.  The application of the
    United Nations Convention on Contracts for the International Sale
    of Goods is expressly excluded.  Any law or regulation which
    provides that the language of a contract shall be construed
    against the drafter shall not apply to this License.  You agree
    that You alone are responsible for compliance with the United
    States export administration regulations (and the export control
    laws and regulation of any other countries) when You use,
    distribute or otherwise make available any Covered Software.

10. RESPONSIBILITY FOR CLAIMS.

    As between Initial Developer and the Contributors, each party is
    responsible for claims and damages arising, directly or
    indirectly, out of its utilization of rights under this License
    and You agree to work with Initial Developer and Contributors to
    distribute such responsibility on an equitable basis.  Nothing
    herein is intended or shall be deemed to constitute any admission
    of liability.

  070701000002A7000081A40000000000000000000000016822550500000B2C000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/Makefile   #!/usr/bin/make -f
##########################################################
# Copyright (C) 2009 VMware, Inc. All rights reserved.
#
# The contents of this file are subject to the terms of the Common
# Development and Distribution License (the "License") version 1.0
# and no later version.  You may not use this file except in
# compliance with the License.
#
# You can obtain a copy of the License at
#         http://www.opensource.org/licenses/cddl1.php
#
# See the License for the specific language governing permissions
# and limitations under the License.
#
##########################################################

ifneq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.10)
SUPPORTED := 1
endif

ifneq ($(shell echo "$(VM_UNAME)" | cut -c-4),5.11)
SUPPORTED := 1
endif

$(if $(SUPPORTED),,$(error "Unsupported Solaris version: $(VM_UNAME)"))

##
## General build locations and variables
##

MODULE    := vmxnet3
CFLAGS    :=
LDFLAGS   :=

CFLAGS    += -O2
CFLAGS    += -Wall -Werror -Wno-unknown-pragmas
CFLAGS    += -U_NO_LONGLONG
CFLAGS    += -D_KERNEL
CFLAGS    += -I../../../lib/include    # for buildNumber.h

CFLAGS    += -ffreestanding
CFLAGS    += -nodefaultlibs

ifdef OVT_SOURCE_DIR
   CFLAGS += -I$(OVT_SOURCE_DIR)/lib/include
   CFLAGS += -I$(OVT_SOURCE_DIR)/modules/shared/vmxnet
endif

CFLAGS_32  := $(CFLAGS)
CFLAGS_32  += -m32
LDFLAGS_32 := $(LDFLAGS)

CFLAGS_64  := $(CFLAGS)
CFLAGS_64  += -m64
CFLAGS_64  += -mcmodel=kernel
CFLAGS_64  += -mno-red-zone
LDFLAGS_64 := $(LDFLAGS)
ifdef HAVE_GNU_LD
LDFLAGS_64 += -m elf_x86_64
else
LDFLAGS_64 += -64
endif

##
## Objects needed to build the HGFS kernel module
##
VMXNET3_OBJS := vmxnet3_main.o
VMXNET3_OBJS += vmxnet3_utils.o
VMXNET3_OBJS += vmxnet3_tx.o
VMXNET3_OBJS += vmxnet3_rx.o

VMXNET3_32_OBJS := $(addprefix i386/, $(VMXNET3_OBJS))
VMXNET3_64_OBJS := $(addprefix amd64/, $(VMXNET3_OBJS))

MODULE_32 := i386/$(MODULE)
MODULE_64 := amd64/$(MODULE)

all: prepare_dirs $(MODULE_32) $(MODULE_64)

prepare_dirs:
	@echo "Creating build directories"
	mkdir -p i386
	mkdir -p amd64

$(MODULE_32): $(VMXNET3_32_OBJS)
	@echo "Linking          $@"
	$(LD) $(LDFLAGS_32) -r $(VMXNET3_32_OBJS) -o $@

$(VMXNET3_32_OBJS): i386/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_32) -c $< -o $@

$(MODULE_64): $(VMXNET3_64_OBJS)
	@echo "Linking          $@"
	$(LD) $(LDFLAGS_64) -r $(VMXNET3_64_OBJS) -o $@

$(VMXNET3_64_OBJS): amd64/%.o: %.c
	@echo "Compiling        $(<F)"
	$(CC) $(CFLAGS_64) -c $< -o $@

clean:
	@echo "Cleaning directories"
	@rm -rf $(MODULE_32) $(VMXNET3_32_OBJS) i386
	@rm -rf $(MODULE_64) $(VMXNET3_64_OBJS) amd64

install:
	@echo "Installing modules"
	cp $(MODULE_32) /kernel/drv/
	chmod 755 /kernel/drv/$(MODULE)
	cp $(MODULE_64) /kernel/drv/amd64
	chmod 755 /kernel/drv/amd64/$(MODULE)
	cp vmxnet3s.conf /kernel/drv/vmxnet3s.conf
	chmod 644 /kernel/drv/vmxnet3s.conf
070701000002A8000081A4000000000000000000000001682255050000BB21000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/vmxnet3_main.c /*********************************************************
 * Copyright (C) 2007-2019 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#include "vmxnet3_solaris.h"

/*
 * TODO:
 *    - Tx data ring
 *    - MAC_CAPAB_POLL support
 *    - JF support
 *    - Dynamic RX pool
 */

/*
 * Forward declarations
 */
static int       vmxnet3_getstat(void *, uint_t, uint64_t *);
static int       vmxnet3_start(void *);
static void      vmxnet3_stop(void *);
static int       vmxnet3_setpromisc(void *, boolean_t);
static void      vmxnet3_ioctl(void *arg, queue_t *wq, mblk_t *mp);
static int       vmxnet3_multicst(void *, boolean_t, const uint8_t *);
static int       vmxnet3_unicst(void *, const uint8_t *);
static boolean_t vmxnet3_getcapab(void *, mac_capab_t, void *);

#ifdef SOL11
static int       vmxnet3_get_prop(void *,
				const char *,
				mac_prop_id_t,
				uint_t,
				void *);
static int       vmxnet3_set_prop(void *,
				const char *,
				mac_prop_id_t,
				uint_t,
				const void *);
static void      vmxnet3_prop_info(void *,
				const char *,
				mac_prop_id_t,
				mac_prop_info_handle_t);
#endif

/* MAC callbacks */
static mac_callbacks_t vmxnet3_mac_callbacks = {
#ifdef SOL11
   .mc_callbacks = MC_GETCAPAB | MC_IOCTL | MC_GETPROP | MC_SETPROP | MC_PROPINFO,
#else
   .mc_callbacks = MC_GETCAPAB | MC_IOCTL,
#endif
   .mc_getstat = vmxnet3_getstat,
   .mc_start = vmxnet3_start,
   .mc_stop = vmxnet3_stop,
   .mc_setpromisc = vmxnet3_setpromisc,
   .mc_multicst = vmxnet3_multicst,
   .mc_unicst = vmxnet3_unicst,
   .mc_tx = vmxnet3_tx,
#ifdef SOL11
   .mc_getprop = vmxnet3_get_prop,
   .mc_setprop = vmxnet3_set_prop,
   .mc_propinfo = vmxnet3_prop_info,
#endif
#ifndef OPEN_SOLARIS
#ifndef SOL11
   .mc_resources = NULL,
#endif
#endif
   .mc_ioctl = vmxnet3_ioctl,
   .mc_getcapab = *vmxnet3_getcapab,
};

/* Tx DMA engine description */
static ddi_dma_attr_t vmxnet3_dma_attrs_tx = {
   DMA_ATTR_V0,           /* dma_attr_version */
   0x0000000000000000ull, /* dma_attr_addr_lo */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_count_max */
   0x0000000000000001ull, /* dma_attr_align */
   0x0000000000000001ull, /* dma_attr_burstsizes */
   0x00000001,            /* dma_attr_minxfer */
   0x000000000000FFFFull, /* dma_attr_maxxfer */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */
   -1,                    /* dma_attr_sgllen */
   0x00000001,            /* dma_attr_granular */
   0                      /* dma_attr_flags */
};

/* --- */

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_getstat --
 *
 *    Fetch the statistics of a vmxnet3 device.
 *
 * Results:
 *    DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_getstat(void *data, uint_t stat, uint64_t *val)
{
   vmxnet3_softc_t *dp = data;
   UPT1_TxStats *txStats;
   UPT1_RxStats *rxStats;

   VMXNET3_DEBUG(dp, 3, "getstat(%u)\n", stat);

   if (!dp->devEnabled) {
      return DDI_FAILURE;
   }

   txStats = &VMXNET3_TQDESC(dp)->stats;
   rxStats = &VMXNET3_RQDESC(dp)->stats;

   /*
    * First touch the related register
    */
   switch (stat) {
      case MAC_STAT_MULTIRCV:
      case MAC_STAT_BRDCSTRCV:
      case MAC_STAT_MULTIXMT:
      case MAC_STAT_BRDCSTXMT:
      case MAC_STAT_NORCVBUF:
      case MAC_STAT_IERRORS:
      case MAC_STAT_NOXMTBUF:
      case MAC_STAT_OERRORS:
      case MAC_STAT_RBYTES:
      case MAC_STAT_IPACKETS:
      case MAC_STAT_OBYTES:
      case MAC_STAT_OPACKETS:
         VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS);
         break;
      case MAC_STAT_IFSPEED:
      case MAC_STAT_COLLISIONS:
      case ETHER_STAT_LINK_DUPLEX:
         /* nothing */
         break;
      default:
         return DDI_FAILURE;
   }

   /*
    * Then fetch the corresponding stat
    */
   switch (stat) {
      case MAC_STAT_IFSPEED:
         *val = dp->linkSpeed;
         break;
      case MAC_STAT_MULTIRCV:
         *val = rxStats->mcastPktsRxOK;
         break;
      case MAC_STAT_BRDCSTRCV:
         *val = rxStats->bcastPktsRxOK;
         break;
      case MAC_STAT_MULTIXMT:
         *val = txStats->mcastPktsTxOK;
         break;
      case MAC_STAT_BRDCSTXMT:
         *val = txStats->bcastPktsTxOK;
         break;
      case MAC_STAT_NORCVBUF:
         *val = rxStats->pktsRxOutOfBuf;
         break;
      case MAC_STAT_IERRORS:
         *val = rxStats->pktsRxError;
         break;
      case MAC_STAT_NOXMTBUF:
         *val = txStats->pktsTxDiscard;
         break;
      case MAC_STAT_OERRORS:
         *val = txStats->pktsTxError;
         break;
      case MAC_STAT_COLLISIONS:
         *val = 0;
         break;
      case MAC_STAT_RBYTES:
         *val = rxStats->LROBytesRxOK +
                rxStats->ucastBytesRxOK +
                rxStats->mcastBytesRxOK +
                rxStats->bcastBytesRxOK;
         break;
      case MAC_STAT_IPACKETS:
         *val = rxStats->LROPktsRxOK +
                rxStats->ucastPktsRxOK +
                rxStats->mcastPktsRxOK +
                rxStats->bcastPktsRxOK;
         break;
      case MAC_STAT_OBYTES:
         *val = txStats->TSOBytesTxOK +
                txStats->ucastBytesTxOK +
                txStats->mcastBytesTxOK +
                txStats->bcastBytesTxOK;
         break;
      case MAC_STAT_OPACKETS:
         *val = txStats->TSOPktsTxOK +
                txStats->ucastPktsTxOK +
                txStats->mcastPktsTxOK +
                txStats->bcastPktsTxOK;
         break;
      case ETHER_STAT_LINK_DUPLEX:
         *val = LINK_DUPLEX_FULL;
         break;
      default:
         ASSERT(B_FALSE);
   }

   return DDI_SUCCESS;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_prepare_drivershared --
 *
 *    Allocate and initialize the shared data structures
 *    of a vmxnet3 device.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_prepare_drivershared(vmxnet3_softc_t *dp)
{
   Vmxnet3_DriverShared *ds;
   size_t allocSize = sizeof(Vmxnet3_DriverShared);

   if (vmxnet3_alloc_dma_mem_1(dp, &dp->sharedData, allocSize,
                               B_TRUE) != DDI_SUCCESS) {
      return DDI_FAILURE;
   }
   ds = VMXNET3_DS(dp);
   memset(ds, 0, allocSize);

   allocSize = sizeof(Vmxnet3_TxQueueDesc) + sizeof(Vmxnet3_RxQueueDesc);
   if (vmxnet3_alloc_dma_mem_128(dp, &dp->queueDescs, allocSize,
                                 B_TRUE) != DDI_SUCCESS) {
      vmxnet3_free_dma_mem(&dp->sharedData);
      return DDI_FAILURE;
   }
   memset(dp->queueDescs.buf, 0, allocSize);

   ds->magic = VMXNET3_REV1_MAGIC;

   /* Take care of most of devRead */
   ds->devRead.misc.driverInfo.version = BUILD_NUMBER_NUMERIC;
   if (sizeof(void *) == 4) {
      ds->devRead.misc.driverInfo.gos.gosBits = VMXNET3_GOS_BITS_32;
   } else if (sizeof(void *) == 8) {
      ds->devRead.misc.driverInfo.gos.gosBits = VMXNET3_GOS_BITS_64;
   } else {
      ASSERT(B_FALSE);
   }
   ds->devRead.misc.driverInfo.gos.gosType = VMXNET3_GOS_TYPE_SOLARIS;
   ds->devRead.misc.driverInfo.gos.gosVer = 10;
   ds->devRead.misc.driverInfo.vmxnet3RevSpt = 1;
   ds->devRead.misc.driverInfo.uptVerSpt = 1;

   ds->devRead.misc.uptFeatures = UPT1_F_RXCSUM;
   ds->devRead.misc.mtu = dp->cur_mtu;

   // XXX: ds->devRead.misc.maxNumRxSG
   ds->devRead.misc.numTxQueues = 1;
   ds->devRead.misc.numRxQueues = 1;
   ds->devRead.misc.queueDescPA = dp->queueDescs.bufPA;
   ds->devRead.misc.queueDescLen = allocSize;

   /* TxQueue and RxQueue information is filled in other functions */

   ds->devRead.intrConf.autoMask = (dp->intrMaskMode == VMXNET3_IMM_AUTO);
   ds->devRead.intrConf.numIntrs = 1;
   // XXX: ds->intr.modLevels
   ds->devRead.intrConf.eventIntrIdx = 0;

   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_DSAL, VMXNET3_ADDR_LO(dp->sharedData.bufPA));
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_DSAH, VMXNET3_ADDR_HI(dp->sharedData.bufPA));

   return DDI_SUCCESS;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_destroy_drivershared --
 *
 *    Destroy the shared data structures of a vmxnet3 device.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_destroy_drivershared(vmxnet3_softc_t *dp)
{
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_DSAL, 0);
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_DSAH, 0);

   vmxnet3_free_dma_mem(&dp->queueDescs);
   vmxnet3_free_dma_mem(&dp->sharedData);
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_alloc_cmdring --
 *
 *    Allocate and initialize the command ring of a queue.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_alloc_cmdring(vmxnet3_softc_t *dp, vmxnet3_cmdring_t *cmdRing)
{
   size_t ringSize = cmdRing->size * sizeof(Vmxnet3_TxDesc);

   if (vmxnet3_alloc_dma_mem_512(dp, &cmdRing->dma, ringSize,
                                 B_TRUE) != DDI_SUCCESS) {
      return DDI_FAILURE;
   }
   memset(cmdRing->dma.buf, 0, ringSize);
   cmdRing->avail = cmdRing->size;
   cmdRing->next2fill = 0;
   cmdRing->gen = VMXNET3_INIT_GEN;

   return DDI_SUCCESS;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_alloc_compring --
 *
 *    Allocate and initialize the completion ring of a queue.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_alloc_compring(vmxnet3_softc_t *dp, vmxnet3_compring_t *compRing)
{
   size_t ringSize = compRing->size * sizeof(Vmxnet3_TxCompDesc);

   if (vmxnet3_alloc_dma_mem_512(dp, &compRing->dma, ringSize,
                                 B_TRUE) != DDI_SUCCESS) {
      return DDI_FAILURE;
   }
   memset(compRing->dma.buf, 0, ringSize);
   compRing->next2comp = 0;
   compRing->gen = VMXNET3_INIT_GEN;

   return DDI_SUCCESS;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_prepare_txqueue --
 *
 *    Initialize the tx queue of a vmxnet3 device.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_prepare_txqueue(vmxnet3_softc_t *dp)
{
   Vmxnet3_TxQueueDesc *tqdesc = VMXNET3_TQDESC(dp);
   vmxnet3_txqueue_t *txq = &dp->txQueue;

   ASSERT(!(txq->cmdRing.size & VMXNET3_RING_SIZE_MASK));
   ASSERT(!(txq->compRing.size & VMXNET3_RING_SIZE_MASK));
   ASSERT(!txq->cmdRing.dma.buf && !txq->compRing.dma.buf);

   if (vmxnet3_alloc_cmdring(dp, &txq->cmdRing) != DDI_SUCCESS) {
      goto error;
   }
   tqdesc->conf.txRingBasePA = txq->cmdRing.dma.bufPA;
   tqdesc->conf.txRingSize = txq->cmdRing.size;
   tqdesc->conf.dataRingBasePA = 0;
   tqdesc->conf.dataRingSize = 0;

   if (vmxnet3_alloc_compring(dp, &txq->compRing) != DDI_SUCCESS) {
      goto error_cmdring;
   }
   tqdesc->conf.compRingBasePA = txq->compRing.dma.bufPA;
   tqdesc->conf.compRingSize = txq->compRing.size;

   txq->metaRing = kmem_zalloc(txq->cmdRing.size*sizeof(vmxnet3_metatx_t),
                               KM_SLEEP);
   ASSERT(txq->metaRing);

   if (vmxnet3_txqueue_init(dp, txq) != DDI_SUCCESS) {
      goto error_mpring;
   }

   return DDI_SUCCESS;

error_mpring:
   kmem_free(txq->metaRing, txq->cmdRing.size*sizeof(vmxnet3_metatx_t));
   vmxnet3_free_dma_mem(&txq->compRing.dma);
error_cmdring:
   vmxnet3_free_dma_mem(&txq->cmdRing.dma);
error:
   return DDI_FAILURE;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_prepare_rxqueue --
 *
 *    Initialize the rx queue of a vmxnet3 device.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_prepare_rxqueue(vmxnet3_softc_t *dp)
{
   Vmxnet3_RxQueueDesc *rqdesc = VMXNET3_RQDESC(dp);
   vmxnet3_rxqueue_t *rxq = &dp->rxQueue;

   ASSERT(!(rxq->cmdRing.size & VMXNET3_RING_SIZE_MASK));
   ASSERT(!(rxq->compRing.size & VMXNET3_RING_SIZE_MASK));
   ASSERT(!rxq->cmdRing.dma.buf && !rxq->compRing.dma.buf);

   if (vmxnet3_alloc_cmdring(dp, &rxq->cmdRing) != DDI_SUCCESS) {
      goto error;
   }
   rqdesc->conf.rxRingBasePA[0] = rxq->cmdRing.dma.bufPA;
   rqdesc->conf.rxRingSize[0] = rxq->cmdRing.size;
   rqdesc->conf.rxRingBasePA[1] = 0;
   rqdesc->conf.rxRingSize[1] = 0;

   if (vmxnet3_alloc_compring(dp, &rxq->compRing) != DDI_SUCCESS) {
      goto error_cmdring;
   }
   rqdesc->conf.compRingBasePA = rxq->compRing.dma.bufPA;
   rqdesc->conf.compRingSize = rxq->compRing.size;

   rxq->bufRing = kmem_zalloc(rxq->cmdRing.size*sizeof(vmxnet3_bufdesc_t),
                              KM_SLEEP);
   ASSERT(rxq->bufRing);

   if (vmxnet3_rxqueue_init(dp, rxq) != DDI_SUCCESS) {
      goto error_bufring;
   }

   return DDI_SUCCESS;

error_bufring:
   kmem_free(rxq->bufRing, rxq->cmdRing.size*sizeof(vmxnet3_bufdesc_t));
   vmxnet3_free_dma_mem(&rxq->compRing.dma);
error_cmdring:
   vmxnet3_free_dma_mem(&rxq->cmdRing.dma);
error:
   return DDI_FAILURE;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_destroy_txqueue --
 *
 *    Destroy the tx queue of a vmxnet3 device.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_destroy_txqueue(vmxnet3_softc_t *dp)
{
   vmxnet3_txqueue_t *txq = &dp->txQueue;

   ASSERT(txq->metaRing);
   ASSERT(txq->cmdRing.dma.buf && txq->compRing.dma.buf);

   vmxnet3_txqueue_fini(dp, txq);

   kmem_free(txq->metaRing, txq->cmdRing.size*sizeof(vmxnet3_metatx_t));

   vmxnet3_free_dma_mem(&txq->cmdRing.dma);
   vmxnet3_free_dma_mem(&txq->compRing.dma);
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_destroy_rxqueue --
 *
 *    Destroy the rx queue of a vmxnet3 device.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_destroy_rxqueue(vmxnet3_softc_t *dp)
{
   vmxnet3_rxqueue_t *rxq = &dp->rxQueue;

   ASSERT(rxq->bufRing);
   ASSERT(rxq->cmdRing.dma.buf && rxq->compRing.dma.buf);

   vmxnet3_rxqueue_fini(dp, rxq);

   kmem_free(rxq->bufRing, rxq->cmdRing.size*sizeof(vmxnet3_bufdesc_t));

   vmxnet3_free_dma_mem(&rxq->cmdRing.dma);
   vmxnet3_free_dma_mem(&rxq->compRing.dma);
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_refresh_rxfilter --
 *
 *    Apply new RX filters settings to a vmxnet3 device.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_refresh_rxfilter(vmxnet3_softc_t *dp)
{
   Vmxnet3_DriverShared *ds = VMXNET3_DS(dp);

   ds->devRead.rxFilterConf.rxMode = dp->rxMode;
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_UPDATE_RX_MODE);
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_refresh_linkstate --
 *
 *    Fetch the link state of a vmxnet3 device.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_refresh_linkstate(vmxnet3_softc_t *dp)
{
   uint32_t ret32;

   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_GET_LINK);
   ret32 = VMXNET3_BAR1_GET32(dp, VMXNET3_REG_CMD);
   if (ret32 & 1) {
      dp->linkState = LINK_STATE_UP;
      dp->linkSpeed = (ret32 >> 16) * 1000000ULL;
   } else {
      dp->linkState = LINK_STATE_DOWN;
      dp->linkSpeed = 0;
   }
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_start --
 *
 *    Start a vmxnet3 device: allocate and initialize the shared data
 *    structures and send a start command to the device.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_start(void *data)
{
   vmxnet3_softc_t *dp = data;
   Vmxnet3_TxQueueDesc *tqdesc;
   Vmxnet3_RxQueueDesc *rqdesc;
   int txQueueSize, rxQueueSize;
   uint32_t ret32;

   VMXNET3_DEBUG(dp, 1, "start()\n");

   /*
    * Allocate vmxnet3's shared data and advertise its PA
    */
   if (vmxnet3_prepare_drivershared(dp) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "vmxnet3_prepare_drivershared() failed\n");
      goto error;
   }
   tqdesc = VMXNET3_TQDESC(dp);
   rqdesc = VMXNET3_RQDESC(dp);

   /*
    * Create and initialize the tx queue
    */
   txQueueSize = vmxnet3_getprop(dp, "TxRingSize", 32, 4096,
                                 VMXNET3_DEF_TX_RING_SIZE);
   if (!(txQueueSize & VMXNET3_RING_SIZE_MASK)) {
      dp->txQueue.cmdRing.size = txQueueSize;
      dp->txQueue.compRing.size = txQueueSize;
      dp->txQueue.sharedCtrl = &tqdesc->ctrl;
      if (vmxnet3_prepare_txqueue(dp) != DDI_SUCCESS) {
         VMXNET3_WARN(dp, "vmxnet3_prepare_txqueue() failed\n");
         goto error_shared_data;
      }
   } else {
      VMXNET3_WARN(dp, "invalid tx ring size (%d)\n", txQueueSize);
      goto error_shared_data;
   }

   /*
    * Create and initialize the rx queue
    */
   rxQueueSize = vmxnet3_getprop(dp, "RxRingSize", 32, 4096,
                                 VMXNET3_DEF_RX_RING_SIZE);
   if (!(rxQueueSize & VMXNET3_RING_SIZE_MASK)) {
      dp->rxQueue.cmdRing.size = rxQueueSize;
      dp->rxQueue.compRing.size = rxQueueSize;
      dp->rxQueue.sharedCtrl = &rqdesc->ctrl;
      if (vmxnet3_prepare_rxqueue(dp) != DDI_SUCCESS) {
         VMXNET3_WARN(dp, "vmxnet3_prepare_rxqueue() failed\n");
         goto error_tx_queue;
      }
   } else {
      VMXNET3_WARN(dp, "invalid rx ring size (%d)\n", rxQueueSize);
      goto error_tx_queue;
   }

   /*
    * Allocate the Tx DMA handle
    */
   if (ddi_dma_alloc_handle(dp->dip, &vmxnet3_dma_attrs_tx, DDI_DMA_SLEEP,
                            NULL, &dp->txDmaHandle) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "ddi_dma_alloc_handle() failed\n");
      goto error_rx_queue;
   }

   /*
    * Activate the device
    */
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_ACTIVATE_DEV);
   ret32 = VMXNET3_BAR1_GET32(dp, VMXNET3_REG_CMD);
   if (ret32) {
      VMXNET3_WARN(dp, "ACTIVATE_DEV failed: 0x%x\n", ret32);
      goto error_txhandle;
   }
   dp->devEnabled = B_TRUE;

   VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_RXPROD, dp->txQueue.cmdRing.size - 1);

   /*
    * Update the RX filters, must be done after ACTIVATE_DEV
    */
   dp->rxMode = VMXNET3_RXM_UCAST | VMXNET3_RXM_BCAST;
   vmxnet3_refresh_rxfilter(dp);

   /*
    * Get the link state now because no events will be generated
    */
   vmxnet3_refresh_linkstate(dp);
   mac_link_update(dp->mac, dp->linkState);

   /*
    * Finally, unmask the interrupt
    */
   VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_IMR, 0);

   return DDI_SUCCESS;

error_txhandle:
   ddi_dma_free_handle(&dp->txDmaHandle);
error_rx_queue:
   vmxnet3_destroy_rxqueue(dp);
error_tx_queue:
   vmxnet3_destroy_txqueue(dp);
error_shared_data:
   vmxnet3_destroy_drivershared(dp);
error:
   return DDI_FAILURE;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_stop --
 *
 *    Stop a vmxnet3 device: send a stop command to the device and
 *    de-allocate the shared data structures.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_stop(void *data)
{
   vmxnet3_softc_t *dp = data;

   VMXNET3_DEBUG(dp, 1, "stop()\n");

   /*
    * Take the 3 locks related to asynchronous events.
    * These events should always check dp->devEnabled before poking dp.
    */
   mutex_enter(&dp->intrLock);
   mutex_enter(&dp->rxPoolLock);
   mutex_enter(&dp->txLock);
   VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_IMR, 1);
   dp->devEnabled = B_FALSE;
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_QUIESCE_DEV);
   mutex_exit(&dp->txLock);
   mutex_exit(&dp->rxPoolLock);
   mutex_exit(&dp->intrLock);

   ddi_dma_free_handle(&dp->txDmaHandle);

   vmxnet3_destroy_rxqueue(dp);
   vmxnet3_destroy_txqueue(dp);

   vmxnet3_destroy_drivershared(dp);
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_setpromisc --
 *
 *    Set or unset promiscuous mode on a vmxnet3 device.
 *
 * Results:
 *    DDI_SUCCESS.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_setpromisc(void *data, boolean_t promisc)
{
   vmxnet3_softc_t *dp = data;

   VMXNET3_DEBUG(dp, 2, "setpromisc(%s)\n", promisc ? "TRUE" : "FALSE");

   if (promisc) {
      dp->rxMode |= VMXNET3_RXM_PROMISC;
   } else {
      dp->rxMode &= ~VMXNET3_RXM_PROMISC;
   }

   vmxnet3_refresh_rxfilter(dp);

   return DDI_SUCCESS;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_multicst --
 *
 *    Add or remove a multicast address from/to a vmxnet3 device.
 *
 * Results:
 *    DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_multicst(void *data, boolean_t add, const uint8_t *macaddr)
{
   vmxnet3_softc_t *dp = data;
   vmxnet3_dmabuf_t newMfTable;
   int ret = DDI_SUCCESS;
   uint16_t macIdx;

   VMXNET3_DEBUG(dp, 2, "multicst(%s, "MACADDR_FMT")\n",
                 add ? "add" : "remove",
                 MACADDR_FMT_ARGS(macaddr));

   /*
    * First lookup the position of the given MAC to check if it is
    * present in the existing MF table.
    */
   for (macIdx = 0; macIdx < dp->mfTable.bufLen; macIdx += 6) {
      if (memcmp(&dp->mfTable.buf[macIdx], macaddr, 6) == 0) {
         break;
      }
   }

   /*
    * Check for 2 situations we can handle gracefully by bailing out:
    * Adding an already existing filter or removing a non-existing one.
    */
   if (add && macIdx < dp->mfTable.bufLen) {
      VMXNET3_WARN(dp, MACADDR_FMT " already in MC filter list @ %u\n",
                   MACADDR_FMT_ARGS(macaddr), macIdx / 6);
      ASSERT(B_FALSE);
      goto done;
   }
   if (!add && macIdx == dp->mfTable.bufLen) {
      VMXNET3_WARN(dp, MACADDR_FMT " not in MC filter list @ %u\n",
                   MACADDR_FMT_ARGS(macaddr), macIdx / 6);
      ASSERT(B_FALSE);
      goto done;
   }

   /*
    * Create the new MF table
    */
   {
      size_t allocSize = dp->mfTable.bufLen + (add ? 6 : -6);
      if (allocSize) {
         ret = vmxnet3_alloc_dma_mem_1(dp, &newMfTable, allocSize, B_TRUE);
         ASSERT(ret == DDI_SUCCESS);
         if (add) {
            memcpy(newMfTable.buf, dp->mfTable.buf, dp->mfTable.bufLen);
            memcpy(newMfTable.buf + dp->mfTable.bufLen, macaddr, 6);
         } else {
            memcpy(newMfTable.buf, dp->mfTable.buf, macIdx);
            memcpy(newMfTable.buf + macIdx,
                   dp->mfTable.buf + macIdx + 6,
                   dp->mfTable.bufLen - macIdx - 6);
         }
      } else {
         newMfTable.buf = NULL;
         newMfTable.bufPA = 0;
         newMfTable.bufLen = 0;
      }
   }

   /*
    * Now handle 2 corner cases: if we're creating the first filter or
    * removing the last one, we have to update rxMode accordingly.
    */
   if (add && newMfTable.bufLen == 6) {
      ASSERT(!(dp->rxMode & VMXNET3_RXM_MCAST));
      dp->rxMode |= VMXNET3_RXM_MCAST;
      vmxnet3_refresh_rxfilter(dp);
   }
   if (!add && dp->mfTable.bufLen == 6) {
      ASSERT(newMfTable.buf == NULL);
      ASSERT(dp->rxMode & VMXNET3_RXM_MCAST);
      dp->rxMode &= ~VMXNET3_RXM_MCAST;
      vmxnet3_refresh_rxfilter(dp);
   }

   /*
    * Now replace the old MF table with the new one
    */
   if (dp->mfTable.buf) {
      vmxnet3_free_dma_mem(&dp->mfTable);
   }
   dp->mfTable = newMfTable;
   VMXNET3_DS(dp)->devRead.rxFilterConf.mfTablePA = newMfTable.bufPA;
   VMXNET3_DS(dp)->devRead.rxFilterConf.mfTableLen = newMfTable.bufLen;

done:
   /* Always update the filters */
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_UPDATE_MAC_FILTERS);

   return ret;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_unicst --
 *
 *    Set the mac address of a vmxnet3 device.
 *
 * Results:
 *    DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_unicst(void *data, const uint8_t *macaddr)
{
   vmxnet3_softc_t *dp = data;
   uint32_t val32;

   VMXNET3_DEBUG(dp, 2, "unicst("MACADDR_FMT")\n",
                 MACADDR_FMT_ARGS(macaddr));

   val32 = *((uint32_t *) (macaddr + 0));
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_MACL, val32);
   val32 = *((uint16_t *) (macaddr + 4));
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_MACH, val32);

   memcpy(dp->macaddr, macaddr, 6);

   return DDI_SUCCESS;
}


/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_change_mtu --
 *
 *    Change the MTU as seen by the driver. Reset the device and tx/rx queues
 *    so that buffers of right size are posted in rx queues.
 *
 * Results:
 *    EINVAL for invalid MTUs or other failures. 0 for success.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */

static int
vmxnet3_change_mtu(vmxnet3_softc_t *dp, uint32_t new_mtu)
{
   int ret = 0, do_reset = 0;
   ASSERT(dp);

   VMXNET3_DEBUG(dp, 2, "New MTU: %d current MTU: %d\n", new_mtu, dp->cur_mtu);

   if (new_mtu == dp->cur_mtu) {
      VMXNET3_WARN(dp, "New MTU is same as old mtu : %d.\n", new_mtu);
      return 0;
   }

   if (new_mtu < VMXNET3_MIN_MTU || new_mtu > VMXNET3_MAX_MTU) {
      VMXNET3_WARN(dp, "New MTU not in valid range [%d, %d].\n",
                   VMXNET3_MIN_MTU, VMXNET3_MAX_MTU);
      return EINVAL;
   }

#if defined(SOL9) || defined (SOL10) || defined (OPEN_SOLARIS)
   if (new_mtu > ETHERMTU && !dp->allow_jumbo) {
      VMXNET3_WARN(dp, "MTU cannot be greater than %d because accept-jumbo "
                   "is not enabled.\n", ETHERMTU);
      return EINVAL;
   }
#endif

   if (dp->devEnabled) {
      do_reset = 1;
      vmxnet3_stop(dp);
      VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_RESET_DEV);
   }

   dp->cur_mtu = new_mtu;

#ifdef OPEN_SOLARIS
   mac_maxsdu_update(dp->mac, new_mtu);
#endif

   if (do_reset)
      ret = vmxnet3_start(dp);

   return ret;
}


/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_ioctl --
 *
 *    DDI/DDK callback to handle IOCTL in driver. Currently it only handles
 *    ND_SET ioctl. Rest all are ignored. The ND_SET is used to set/reset
 *    accept-jumbo ndd parameter for the interface.
 *
 * Results:
 *    Nothing is returned directly. An ACK or NACK is conveyed to the calling
 *    function from the mblk which was used to call this function.
 *
 * Side effects:
 *    MTU can be changed and device can be reset.
 *
 *---------------------------------------------------------------------------
 */

static void
vmxnet3_ioctl(void *arg, queue_t *wq, mblk_t *mp)
{
   vmxnet3_softc_t *dp = arg;
   int             ret = EINVAL;
   IOCP            iocp;
   mblk_t          *mp1;
   char            *valp, *param;
   int             data;

   iocp = (void *)mp->b_rptr;
   iocp->ioc_error = 0;

   switch (iocp->ioc_cmd) {
   case ND_SET:

      /* the mblk in continuation would contain the ndd parameter name
       * and data value to be set
       */
      mp1 = mp->b_cont;
      if (!mp1) {
         VMXNET3_WARN(dp, "Error locating parameter name.\n");
         ret = EINVAL;
         break;
      }

      mp1->b_datap->db_lim[-1] = '\0';	/* Force null termination */

      /*
       * From /usr/src/uts/common/inet/nd.c : nd_getset()
       * "logic throughout nd_xxx assumes single data block for ioctl.
       *  However, existing code sends in some big buffers."
       */
      if (mp1->b_cont) {
         freemsg(mp1->b_cont);
         mp1->b_cont = NULL;
      }

      valp = (char *)mp1->b_rptr;	/* Points to param name*/
      ASSERT(valp);
      param = valp;
      VMXNET3_DEBUG(dp, 3, "ND Set ioctl for %s\n", param);

      /* Go past the end of this null terminated string to get the data value.*/
      while (*valp && valp <= (char *)mp1->b_wptr)
         valp++;

      if (valp > (char *)mp1->b_wptr) {
         /* We are already beyond the readable area of mblk and still havent
          * found the end of param string.
          */
         VMXNET3_WARN(dp, "No data value found to be set to param.\n");
         data = -1;
      } else {
         valp++;                        /* Now this points to data string */
         data = (int)*valp - (int)'0';  /* Get numeric value of first letter */
      }

      if (strcmp("accept-jumbo", param) == 0) {
         if (data == 1) {
            VMXNET3_DEBUG(dp, 1, "Accepting jumbo frames\n");
            dp->allow_jumbo = 1;
            vmxnet3_change_mtu(dp, VMXNET3_MAX_MTU);
            ret = 0;
         } else if (data == 0) {
            dp->allow_jumbo = 0;
            vmxnet3_change_mtu(dp, ETHERMTU);
            VMXNET3_DEBUG(dp, 1, "Rejecting jumbo frames\n");
            ret = 0;
         } else {
            VMXNET3_WARN(dp, "Invalid data value to be set, use 1 or 0.\n");
            ret = -1;
         }
      }
      freemsg(mp1);
      mp->b_cont = NULL;
      break;

   default:
      if (mp->b_cont) {
         freemsg(mp->b_cont);
         mp->b_cont = NULL;
      }
      ret = -1;
      break;
   }

   if (ret == 0)
      miocack(wq, mp, 0, 0);
   else
      miocnak(wq, mp, 0, EINVAL);
}


/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_getcapab --
 *
 *    Get the capabilities of a vmxnet3 device.
 *
 * Results:
 *    B_TRUE or B_FALSE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static boolean_t
vmxnet3_getcapab(void *data, mac_capab_t capab, void *arg)
{
   vmxnet3_softc_t *dp = data;
   boolean_t ret;

   switch (capab) {
      case MAC_CAPAB_HCKSUM: {
         uint32_t *txflags = arg;
         *txflags = HCKSUM_INET_PARTIAL;
         ret = B_TRUE;
         break;
      }
      case MAC_CAPAB_LSO: {
         mac_capab_lso_t *lso = arg;
         lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
         lso->lso_basic_tcp_ipv4.lso_max = IP_MAXPACKET;
         ret = vmxnet3_getprop(dp, "EnableLSO", 0, 1, 1);
         break;
      }
      default:
         ret = B_FALSE;
   }

   VMXNET3_DEBUG(dp, 3, "getcapab(0x%x) -> %s\n", capab, ret ? "yes" : "no");

   return ret;
}

#ifdef SOL11
static int
vmxnet3_get_prop(void *data,
		const char *prop_name,
		mac_prop_id_t prop_id,
		uint_t prop_val_size,
		void *prop_val)
{
   vmxnet3_softc_t *dp = data;

   switch (prop_id) {
      case MAC_PROP_MTU: {
         ASSERT(prop_val_size >= sizeof (uint32_t));
         bcopy(&dp->cur_mtu, prop_val, sizeof (uint32_t));
         break;
      }
      default: {
         VMXNET3_WARN(dp, "vmxnet3_get_prop property %d not supported", prop_id);
      }
   }
   return (0);
}


static int
vmxnet3_set_prop(void *data,
		const char *prop_name,
		mac_prop_id_t prop_id,
		uint_t prop_val_size,
		const void *prop_val)
{
   vmxnet3_softc_t *dp = data;
   int ret;

   switch (prop_id) {
      case MAC_PROP_MTU: {
         uint32_t new_mtu;
         ASSERT(prop_val_size >= sizeof (uint32_t));
         bcopy(prop_val, &new_mtu, sizeof (new_mtu));
         ret = vmxnet3_change_mtu(dp, new_mtu);
         break;
      }
      default: {
         VMXNET3_WARN(dp, "vmxnet3_set_prop property %d not supported", prop_id);
         ret = ENOTSUP;
      }
   }

   return ret;
}


static void
vmxnet3_prop_info(void *data,
		const char *prop_name,
		mac_prop_id_t prop_id,
		mac_prop_info_handle_t prop_handle)
{
   vmxnet3_softc_t *dp = data;

   switch (prop_id) {
      case MAC_PROP_MTU: {
         mac_prop_info_set_range_uint32(prop_handle, VMXNET3_MIN_MTU, VMXNET3_MAX_MTU);
         break;
      }
      default: {
         VMXNET3_WARN(dp, "vmxnet3_prop_info: property %d not supported", prop_id);
      }
  }
}
#endif


/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_reset --
 *
 *    Reset a vmxnet3 device. Only to be used when the device is wedged.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The device is reset.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_reset(void *data)
{
   vmxnet3_softc_t *dp = data;

   VMXNET3_DEBUG(dp, 1, "vmxnet3_reset()\n");

   vmxnet3_stop(dp);
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_RESET_DEV);
   vmxnet3_start(dp);
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_intr_events --
 *
 *    Process pending events on a vmxnet3 device.
 *
 * Results:
 *    B_TRUE if the link state changed, B_FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static boolean_t
vmxnet3_intr_events(vmxnet3_softc_t *dp)
{
   Vmxnet3_DriverShared *ds = VMXNET3_DS(dp);
   boolean_t linkStateChanged = B_FALSE;
   uint32_t events = ds->ecr;

   if (events) {
      VMXNET3_DEBUG(dp, 2, "events(0x%x)\n", events);
      if (events & (VMXNET3_ECR_RQERR | VMXNET3_ECR_TQERR)) {
         Vmxnet3_TxQueueDesc *tqdesc = VMXNET3_TQDESC(dp);
         Vmxnet3_RxQueueDesc *rqdesc = VMXNET3_RQDESC(dp);

         VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_GET_QUEUE_STATUS);
         if (tqdesc->status.stopped) {
            VMXNET3_WARN(dp, "tq error 0x%x\n", tqdesc->status.error);
         }
         if (rqdesc->status.stopped) {
            VMXNET3_WARN(dp, "rq error 0x%x\n", rqdesc->status.error);
         }

         if (ddi_taskq_dispatch(dp->resetTask, vmxnet3_reset,
                                dp, DDI_NOSLEEP) == DDI_SUCCESS) {
            VMXNET3_WARN(dp, "reset scheduled\n");
         } else {
            VMXNET3_WARN(dp, "ddi_taskq_dispatch() failed()\n");
         }
      }
      if (events & VMXNET3_ECR_LINK) {
         vmxnet3_refresh_linkstate(dp);
         linkStateChanged = B_TRUE;
      }
      if (events & VMXNET3_ECR_DIC) {
         VMXNET3_DEBUG(dp, 1, "device implementation change\n");
      }
      VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_ECR, events);
   }

   return linkStateChanged;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_intr --
 *
 *    Interrupt handler of a vmxnet3 device.
 *
 * Results:
 *    DDI_INTR_CLAIMED or DDI_INTR_UNCLAIMED.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static u_int
vmxnet3_intr(caddr_t data1, caddr_t data2)
{
   vmxnet3_softc_t *dp = (void *) data1;

   VMXNET3_DEBUG(dp, 3, "intr()\n");

   mutex_enter(&dp->intrLock);

   if (dp->devEnabled) {
      boolean_t linkStateChanged;
      boolean_t mustUpdateTx;
      mblk_t *mps;

      if (dp->intrType == DDI_INTR_TYPE_FIXED &&
          !VMXNET3_BAR1_GET32(dp, VMXNET3_REG_ICR)) {
         goto intr_unclaimed;
      }

      if (dp->intrMaskMode == VMXNET3_IMM_ACTIVE) {
         VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_IMR, 1);
      }

      linkStateChanged = vmxnet3_intr_events(dp);
      mustUpdateTx = vmxnet3_tx_complete(dp, &dp->txQueue);
      mps = vmxnet3_rx_intr(dp, &dp->rxQueue);

      mutex_exit(&dp->intrLock);
      VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_IMR, 0);

      if (linkStateChanged) {
         mac_link_update(dp->mac, dp->linkState);
      }
      if (mustUpdateTx) {
         mac_tx_update(dp->mac);
      }
      if (mps) {
         mac_rx(dp->mac, NULL, mps);
      }

      return DDI_INTR_CLAIMED;
   }

intr_unclaimed:
   mutex_exit(&dp->intrLock);
   return DDI_INTR_UNCLAIMED;
}


/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_attach --
 *
 *    Probe and attach a vmxnet3 instance to the stack.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
   vmxnet3_softc_t *dp;
   mac_register_t *macr;
   uint16_t vendorId, devId, ret16;
   uint32_t ret32;
   int ret, err;
   uint_t uret;

   if (cmd != DDI_ATTACH) {
      goto error;
   }

   /*
    * Allocate the soft state
    */
   dp = kmem_zalloc(sizeof(vmxnet3_softc_t), KM_SLEEP);
   ASSERT(dp);

   dp->dip = dip;
   dp->instance = ddi_get_instance(dip);
   dp->cur_mtu = ETHERMTU;

   VMXNET3_DEBUG(dp, 1, "attach()\n");

   ddi_set_driver_private(dip, dp);

   /*
    * Get access to the PCI bus configuration space
    */
   if (pci_config_setup(dip, &dp->pciHandle) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "pci_config_setup() failed\n");
      goto error_soft_state;
   }

   /*
    * Make sure the chip is a vmxnet3 device
    */
   vendorId = pci_config_get16(dp->pciHandle, PCI_CONF_VENID);
   devId = pci_config_get16(dp->pciHandle, PCI_CONF_DEVID);
   if (vendorId != PCI_VENDOR_ID_VMWARE ||
       devId != PCI_DEVICE_ID_VMWARE_VMXNET3) {
      VMXNET3_WARN(dp, "wrong PCI venid/devid (0x%x, 0x%x)\n",
                   vendorId, devId);
      goto error_pci_config;
   }

   /*
    * Make sure we can access the registers through the I/O space
    */
   ret16 = pci_config_get16(dp->pciHandle, PCI_CONF_COMM);
   ret16 |= PCI_COMM_IO | PCI_COMM_ME;
   pci_config_put16(dp->pciHandle, PCI_CONF_COMM, ret16);

   /*
    * Map the I/O space in memory
    */
   if (ddi_regs_map_setup(dip, 1, &dp->bar0, 0, 0, &vmxnet3_dev_attr,
                          &dp->bar0Handle) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "ddi_regs_map_setup() for BAR0 failed\n");
      goto error_pci_config;
   }

   if (ddi_regs_map_setup(dip, 2, &dp->bar1, 0, 0, &vmxnet3_dev_attr,
                          &dp->bar1Handle) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "ddi_regs_map_setup() for BAR1 failed\n");
      goto error_regs_map_0;
   }

   /*
    * Check the version number of the virtual device
    */
   if (VMXNET3_BAR1_GET32(dp, VMXNET3_REG_VRRS) & 1) {
      VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_VRRS, 1);
   } else {
      VMXNET3_WARN(dp, "incompatible h/w version\n");
      goto error_regs_map_1;
   }

   if (VMXNET3_BAR1_GET32(dp, VMXNET3_REG_UVRS) & 1) {
      VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_UVRS, 1);
   } else {
      VMXNET3_WARN(dp, "incompatible upt version\n");
      goto error_regs_map_1;
   }

   /*
    * Read the MAC address from the device
    */
   ret32 = VMXNET3_BAR1_GET32(dp, VMXNET3_REG_MACL);
   *((uint32_t *) (dp->macaddr + 0)) = ret32;
   ret32 = VMXNET3_BAR1_GET32(dp, VMXNET3_REG_MACH);
   *((uint16_t *) (dp->macaddr + 4)) = ret32;

   /*
    * Register with the MAC framework
    */
   if (!(macr = mac_alloc(MAC_VERSION))) {
      VMXNET3_WARN(dp, "mac_alloc() failed.\n");
      goto error_regs_map_1;
   }

   macr->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
   macr->m_driver = dp;
   macr->m_dip = dip;
   macr->m_instance = 0;
   macr->m_src_addr = dp->macaddr;
   macr->m_dst_addr = NULL;
   macr->m_callbacks = &vmxnet3_mac_callbacks;
   macr->m_min_sdu = VMXNET3_MIN_MTU;
#if defined(SOL9) || defined (SOL10)
   macr->m_max_sdu = vmxnet3_getprop(dp, "MTU", VMXNET3_MIN_MTU,
                                     VMXNET3_MAX_MTU, ETHERMTU);
#else
   macr->m_max_sdu = ETHERMTU;
#endif
   macr->m_pdata = NULL;
   macr->m_pdata_size = 0;

   ret = mac_register(macr, &dp->mac);
   mac_free(macr);
   if (ret != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "mac_register() failed\n");
      goto error_regs_map_1;
   }

   /*
    * Register the interrupt(s) in this order of preference:
    * MSI-X, MSI, INTx
    */
   VMXNET3_BAR1_PUT32(dp, VMXNET3_REG_CMD, VMXNET3_CMD_GET_CONF_INTR);
   ret32 = VMXNET3_BAR1_GET32(dp, VMXNET3_REG_CMD);
   switch (ret32 & 0x3) {
      case VMXNET3_IT_AUTO:
      case VMXNET3_IT_MSIX:
         dp->intrType = DDI_INTR_TYPE_MSIX;
         err = ddi_intr_alloc(dip, &dp->intrHandle, dp->intrType, 0, 1,
                              &ret, DDI_INTR_ALLOC_STRICT);
         if (err == DDI_SUCCESS)
            break;
         VMXNET3_DEBUG(dp, 2, "DDI_INTR_TYPE_MSIX failed, err:%d\n", err);
      case VMXNET3_IT_MSI:
         dp->intrType = DDI_INTR_TYPE_MSI;
         if (ddi_intr_alloc(dip, &dp->intrHandle, dp->intrType, 0, 1,
                            &ret, DDI_INTR_ALLOC_STRICT) == DDI_SUCCESS)
            break;
         VMXNET3_DEBUG(dp, 2, "DDI_INTR_TYPE_MSI failed\n");
      case VMXNET3_IT_INTX:
         dp->intrType = DDI_INTR_TYPE_FIXED;
         if (ddi_intr_alloc(dip, &dp->intrHandle, dp->intrType, 0, 1,
                            &ret, DDI_INTR_ALLOC_STRICT) == DDI_SUCCESS) {
            break;
         }
         VMXNET3_DEBUG(dp, 2, "DDI_INTR_TYPE_INTX failed\n");
      default:
         VMXNET3_WARN(dp, "ddi_intr_alloc() failed\n");
         goto error_mac;
   }
   dp->intrMaskMode = (ret32 >> 2) & 0x3;
   if (dp->intrMaskMode == VMXNET3_IMM_LAZY) {
      VMXNET3_WARN(dp, "Lazy masking is not supported\n");
      goto error_intr;
   }

   if (ddi_intr_get_pri(dp->intrHandle, &uret) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "ddi_intr_get_pri() failed\n");
      goto error_intr;
   }

   VMXNET3_DEBUG(dp, 2, "intrType=0x%x, intrMaskMode=0x%x, intrPrio=%u\n",
           dp->intrType, dp->intrMaskMode, uret);

   /*
    * Create a task queue to reset the device if it wedges.
    */
   dp->resetTask = ddi_taskq_create(dip, "vmxnet3_reset_task", 1,
                                    TASKQ_DEFAULTPRI, 0);
   if (!dp->resetTask) {
      VMXNET3_WARN(dp, "ddi_taskq_create() failed()\n");
      goto error_intr;
   }

   /*
    * Initialize our mutexes now that we know the interrupt priority
    * This _must_ be done before ddi_intr_enable()
    */
   mutex_init(&dp->intrLock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(uret));
   mutex_init(&dp->txLock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(uret));
   mutex_init(&dp->rxPoolLock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(uret));

   if (ddi_intr_add_handler(dp->intrHandle, vmxnet3_intr,
                            dp, NULL) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "ddi_intr_add_handler() failed\n");
      goto error_mutexes;
   }

   err = ddi_intr_get_cap(dp->intrHandle, &dp->intrCap);
   if (err != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "ddi_intr_get_cap() failed %d", err);
      goto error_intr_handler;
   }

   if (dp->intrCap & DDI_INTR_FLAG_BLOCK) {
      err = ddi_intr_block_enable(&dp->intrHandle, 1);
      if (err != DDI_SUCCESS) {
         VMXNET3_WARN(dp, "ddi_intr_block_enable() failed, err:%d\n", err);
         goto error_intr_handler;
      }
   } else {
      err = ddi_intr_enable(dp->intrHandle);
      if ((err != DDI_SUCCESS)) {
         VMXNET3_WARN(dp, "ddi_intr_enable() failed, err:%d\n", err);
         goto error_intr_handler;
      }
   }

   return DDI_SUCCESS;

error_intr_handler:
   ddi_intr_remove_handler(dp->intrHandle);
error_mutexes:
   mutex_destroy(&dp->rxPoolLock);
   mutex_destroy(&dp->txLock);
   mutex_destroy(&dp->intrLock);
   ddi_taskq_destroy(dp->resetTask);
error_intr:
   ddi_intr_free(dp->intrHandle);
error_mac:
   mac_unregister(dp->mac);
error_regs_map_1:
   ddi_regs_map_free(&dp->bar1Handle);
error_regs_map_0:
   ddi_regs_map_free(&dp->bar0Handle);
error_pci_config:
   pci_config_teardown(&dp->pciHandle);
error_soft_state:
   kmem_free(dp, sizeof(vmxnet3_softc_t));
error:
   return DDI_FAILURE;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_detach --
 *
 *    Detach a vmxnet3 instance from the stack.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
   vmxnet3_softc_t *dp = ddi_get_driver_private(dip);
   unsigned int retries = 0;

   VMXNET3_DEBUG(dp, 1, "detach()\n");

   if (cmd != DDI_DETACH) {
      return DDI_FAILURE;
   }

   while (dp->rxNumBufs) {
      if (retries++ < 10) {
         VMXNET3_WARN(dp, "rx pending (%u), waiting 1 second...\n",
                      dp->rxNumBufs);
         delay(drv_usectohz(1000000));
      } else {
         VMXNET3_WARN(dp, "giving up...\n");
         return DDI_FAILURE;
      }
   }

   if (dp->intrCap & DDI_INTR_FLAG_BLOCK) {
      ddi_intr_block_disable(&dp->intrHandle, 1);
   } else {
      ddi_intr_disable(dp->intrHandle);
   }
   ddi_intr_remove_handler(dp->intrHandle);
   ddi_intr_free(dp->intrHandle);

   mac_unregister(dp->mac);

   if (dp->mfTable.buf) {
      vmxnet3_free_dma_mem(&dp->mfTable);
   }

   mutex_destroy(&dp->rxPoolLock);
   mutex_destroy(&dp->txLock);
   mutex_destroy(&dp->intrLock);
   ddi_taskq_destroy(dp->resetTask);

   ddi_regs_map_free(&dp->bar1Handle);
   ddi_regs_map_free(&dp->bar0Handle);
   pci_config_teardown(&dp->pciHandle);

   kmem_free(dp, sizeof(vmxnet3_softc_t));

   return DDI_SUCCESS;
}

/* --- */

/*
 * Structures used by the Solaris module loader
 */

#define VMXNET3_IDENT "VMware EtherAdapter v3 " VMXNET3_DRIVER_VERSION_STRING

COMPAT_DDI_DEFINE_STREAM_OPS(vmxnet3_dev_ops,
                             nulldev,
                             nulldev,
                             vmxnet3_attach,
                             vmxnet3_detach,
                             nodev,
                             NULL,
                             D_NEW | D_MP,
                             NULL);

static struct modldrv vmxnet3_modldrv = {
   &mod_driverops,             /* drv_modops */
   VMXNET3_IDENT,              /* drv_linkinfo */
   &vmxnet3_dev_ops            /* drv_dev_ops */
};

static struct modlinkage vmxnet3_modlinkage = {
    MODREV_1,                  /* ml_rev */
    { &vmxnet3_modldrv, NULL } /* ml_linkage */
};

/* Module load entry point */
int _init(void)
{
   int ret;

#ifdef DEBUG
   cmn_err(CE_CONT, "_init()\n");
#endif

   mac_init_ops(&vmxnet3_dev_ops, VMXNET3_MODNAME);
   ret = mod_install(&vmxnet3_modlinkage);
   if (ret != DDI_SUCCESS) {
      mac_fini_ops(&vmxnet3_dev_ops);
   }

   return ret;
}

/* Module unload entry point */
int _fini(void)
{
   int ret;

#ifdef DEBUG
   cmn_err(CE_CONT, "_fini()\n");
#endif

   ret = mod_remove(&vmxnet3_modlinkage);
   if (ret == DDI_SUCCESS) {
      mac_fini_ops(&vmxnet3_dev_ops);
   }

   return ret;
}

/* Module info entry point */
int _info(struct modinfo *modinfop)
{
#ifdef DEBUG
   cmn_err(CE_CONT, "_info()\n");
#endif

   return mod_info(&vmxnet3_modlinkage, modinfop);
}
   070701000002A9000081A400000000000000000000000168225505000030C9000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/vmxnet3_rx.c   /*********************************************************
 * Copyright (C) 2007,2019 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#include "vmxnet3_solaris.h"

static void vmxnet3_put_rxbuf(vmxnet3_rxbuf_t *rxBuf);

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_alloc_rxbuf --
 *
 *    Allocate a new rxBuf from memory. All its fields are set except
 *    for its associated mblk which has to be allocated later.
 *
 * Results:
 *    A new rxBuf or NULL.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static vmxnet3_rxbuf_t *
vmxnet3_alloc_rxbuf(vmxnet3_softc_t *dp, boolean_t canSleep)
{
   vmxnet3_rxbuf_t *rxBuf;
   int flag = canSleep ? KM_SLEEP : KM_NOSLEEP;
   int err;

   rxBuf = kmem_zalloc(sizeof(vmxnet3_rxbuf_t), flag);
   if (!rxBuf) {
      return NULL;
   }

   if ((err = vmxnet3_alloc_dma_mem_1(dp, &rxBuf->dma, (dp->cur_mtu + 18),
                                      canSleep)) != DDI_SUCCESS) {

      VMXNET3_DEBUG(dp, 0, "Failed to allocate %d bytes for rx buf, err:%d.\n",
                    (dp->cur_mtu + 18), err);
      kmem_free(rxBuf, sizeof(vmxnet3_rxbuf_t));
      return NULL;
   }

   rxBuf->freeCB.free_func = vmxnet3_put_rxbuf;
   rxBuf->freeCB.free_arg = (caddr_t) rxBuf;
   rxBuf->dp = dp;

   atomic_inc_32(&dp->rxNumBufs);

   return rxBuf;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_free_rxbuf --
 *
 *    Free a rxBuf.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_free_rxbuf(vmxnet3_softc_t *dp, vmxnet3_rxbuf_t *rxBuf)
{
   vmxnet3_free_dma_mem(&rxBuf->dma);
   kmem_free(rxBuf, sizeof(vmxnet3_rxbuf_t));

#ifndef DEBUG
   atomic_dec_32(&dp->rxNumBufs);
#else
   {
      uint32_t nv = atomic_dec_32_nv(&dp->rxNumBufs);
      ASSERT(nv != -1);
   }
#endif
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_put_rxbuf --
 *
 *    Return a rxBuf to the pool or free it.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_put_rxbuf(vmxnet3_rxbuf_t *rxBuf)
{
   vmxnet3_softc_t *dp = rxBuf->dp;
   vmxnet3_rxpool_t *rxPool = &dp->rxPool;

   VMXNET3_DEBUG(dp, 5, "free 0x%p\n", rxBuf);

   mutex_enter(&dp->rxPoolLock);
   if (dp->devEnabled && rxPool->nBufs < rxPool->nBufsLimit) {
      rxBuf->next = rxPool->listHead;
      rxPool->listHead = rxBuf;
      mutex_exit(&dp->rxPoolLock);
   } else {
      mutex_exit(&dp->rxPoolLock);
      vmxnet3_free_rxbuf(dp, rxBuf);
   }
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_get_rxbuf --
 *
 *    Get an unused rxBuf from either the pool or from memory.
 *    The returned rxBuf has a mblk associated with it.
 *
 * Results:
 *    A rxBuf or NULL.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static vmxnet3_rxbuf_t *
vmxnet3_get_rxbuf(vmxnet3_softc_t *dp, boolean_t canSleep)
{
   vmxnet3_rxbuf_t *rxBuf;
   vmxnet3_rxpool_t *rxPool = &dp->rxPool;

   mutex_enter(&dp->rxPoolLock);
   if (rxPool->listHead) {
      rxBuf = rxPool->listHead;
      rxPool->listHead = rxBuf->next;
      mutex_exit(&dp->rxPoolLock);
      VMXNET3_DEBUG(dp, 5, "alloc 0x%p from pool\n", rxBuf);
   } else {
      mutex_exit(&dp->rxPoolLock);
      rxBuf = vmxnet3_alloc_rxbuf(dp, canSleep);
      if (!rxBuf) {
         goto done;
      }
      VMXNET3_DEBUG(dp, 5, "alloc 0x%p from mem\n", rxBuf);
   }

   ASSERT(rxBuf);

   rxBuf->mblk = desballoc((uchar_t *) rxBuf->dma.buf,
                           rxBuf->dma.bufLen, BPRI_MED,
                           &rxBuf->freeCB);
   if (!rxBuf->mblk) {
      vmxnet3_put_rxbuf(rxBuf);
      rxBuf = NULL;
   }

done:
   return rxBuf;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_rx_populate --
 *
 *    Populate a Rx descriptor with a new rxBuf.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_rx_populate(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq,
                    uint16_t idx, boolean_t canSleep)
{
   int ret = DDI_SUCCESS;
   vmxnet3_rxbuf_t *rxBuf = vmxnet3_get_rxbuf(dp, canSleep);

   if (rxBuf) {
      vmxnet3_cmdring_t *cmdRing = &rxq->cmdRing;
      Vmxnet3_GenericDesc *rxDesc = VMXNET3_GET_DESC(cmdRing, idx);;

      rxq->bufRing[idx].rxBuf = rxBuf;
      rxDesc->rxd.addr = rxBuf->dma.bufPA;
      rxDesc->rxd.len = rxBuf->dma.bufLen;
      // rxDesc->rxd.btype = 0;
      membar_producer();
      rxDesc->rxd.gen = cmdRing->gen;
   } else {
      ret = DDI_FAILURE;
   }

   return ret;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_rxqueue_init --
 *
 *    Initialize a RxQueue by populating the whole Rx ring with rxBufs.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
int
vmxnet3_rxqueue_init(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq)
{
   vmxnet3_cmdring_t *cmdRing = &rxq->cmdRing;

   do {
      if (vmxnet3_rx_populate(dp, rxq, cmdRing->next2fill,
                              B_TRUE) != DDI_SUCCESS) {
         goto error;
      }
      VMXNET3_INC_RING_IDX(cmdRing, cmdRing->next2fill);
   } while (cmdRing->next2fill);

   dp->rxPool.nBufsLimit = vmxnet3_getprop(dp, "RxBufPoolLimit",
                                           0, cmdRing->size * 10,
                                           cmdRing->size * 2);

   return DDI_SUCCESS;

error:
   while (cmdRing->next2fill) {
      VMXNET3_DEC_RING_IDX(cmdRing, cmdRing->next2fill);
      vmxnet3_free_rxbuf(dp, rxq->bufRing[cmdRing->next2fill].rxBuf);
   }

   return DDI_FAILURE;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_rxqueue_fini --
 *
 *    Finish a RxQueue by freeing all the related rxBufs.
 *
 * Results:
 *    DDI_SUCCESS.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
void
vmxnet3_rxqueue_fini(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq)
{
   vmxnet3_rxpool_t *rxPool = &dp->rxPool;
   vmxnet3_rxbuf_t *rxBuf;
   unsigned int i;

   ASSERT(!dp->devEnabled);

   /* First the rxPool */
   while (rxPool->listHead) {
      rxBuf = rxPool->listHead;
      rxPool->listHead = rxBuf->next;
      vmxnet3_free_rxbuf(dp, rxBuf);
   }

   /* Then the ring */
   for (i = 0; i < rxq->cmdRing.size; i++) {
      rxBuf = rxq->bufRing[i].rxBuf;
      ASSERT(rxBuf);
      ASSERT(rxBuf->mblk);
      /*
       * Here, freemsg() will trigger a call to vmxnet3_put_rxbuf() which
       * will then call vmxnet3_free_rxbuf() because the underlying
       * device is disabled.
       */
      freemsg(rxBuf->mblk);
   }
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_rx_hwcksum --
 *
 *    Determine if a received packet was checksummed by the Vmxnet3
 *    device and tag the mp appropriately.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    The mp may get tagged.
 *
 *---------------------------------------------------------------------------
 */
static void
vmxnet3_rx_hwcksum(vmxnet3_softc_t *dp, mblk_t *mp,
                   Vmxnet3_GenericDesc *compDesc)
{
   if (!compDesc->rcd.cnc) {
      uint32_t flags = 0;

      if (compDesc->rcd.v4 && compDesc->rcd.ipc) {
         flags |= HCK_IPV4_HDRCKSUM;
         if ((compDesc->rcd.tcp || compDesc->rcd.udp) &&
              compDesc->rcd.tuc) {
            flags |= HCK_FULLCKSUM | HCK_FULLCKSUM_OK;
         }
      }

      VMXNET3_DEBUG(dp, 3, "rx cksum flags = 0x%x\n", flags);

      hcksum_assoc(mp, NULL, NULL, 0, 0, 0, 0, flags, 0);
   }
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_rx_intr --
 *
 *    Interrupt handler for Rx. Look if there are any pending Rx and
 *    put them in mplist.
 *
 * Results:
 *    A list of messages to pass to the MAC subystem.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
mblk_t *
vmxnet3_rx_intr(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq)
{
   vmxnet3_compring_t *compRing = &rxq->compRing;
   vmxnet3_cmdring_t *cmdRing = &rxq->cmdRing;
   Vmxnet3_RxQueueCtrl *rxqCtrl = rxq->sharedCtrl;
   Vmxnet3_GenericDesc *compDesc;
   mblk_t *mplist = NULL, **mplistTail = &mplist;

   ASSERT(mutex_owned(&dp->intrLock));

   compDesc = VMXNET3_GET_DESC(compRing, compRing->next2comp);
   while (compDesc->rcd.gen == compRing->gen) {
      mblk_t *mp = NULL, **mpTail = &mp;
      boolean_t mpValid = B_TRUE;
      boolean_t eop;

      ASSERT(compDesc->rcd.sop);

      do {
         uint16_t rxdIdx = compDesc->rcd.rxdIdx;
         vmxnet3_rxbuf_t *rxBuf = rxq->bufRing[rxdIdx].rxBuf;
         mblk_t *mblk = rxBuf->mblk;
         Vmxnet3_GenericDesc *rxDesc;

         while (compDesc->rcd.gen != compRing->gen) {
            /*
             * H/W may be still be in the middle of generating this entry,
             * so hold on until the gen bit is flipped.
             */
            membar_consumer();
         }
         ASSERT(compDesc->rcd.gen == compRing->gen);
         ASSERT(rxBuf);
         ASSERT(mblk);

         /* Some Rx descriptors may have been skipped */
         while (cmdRing->next2fill != rxdIdx) {
            rxDesc = VMXNET3_GET_DESC(cmdRing, cmdRing->next2fill);
            rxDesc->rxd.gen = cmdRing->gen;
            VMXNET3_INC_RING_IDX(cmdRing, cmdRing->next2fill);
         }

         eop = compDesc->rcd.eop;

         /*
          * Now we have a piece of the packet in the rxdIdx descriptor.
          * Grab it only if we achieve to replace it with a fresh buffer.
          */
         if (vmxnet3_rx_populate(dp, rxq, rxdIdx, B_FALSE) == DDI_SUCCESS) {
            /* Success, we can chain the mblk with the mp */
            mblk->b_wptr = mblk->b_rptr + compDesc->rcd.len;
            *mpTail = mblk;
            mpTail = &mblk->b_cont;
            ASSERT(*mpTail == NULL);

            VMXNET3_DEBUG(dp, 3, "rx 0x%p on [%u]\n", mblk, rxdIdx);

            if (eop) {
               if (!compDesc->rcd.err) {
                  /* Tag the mp if it was checksummed by the H/W */
                  vmxnet3_rx_hwcksum(dp, mp, compDesc);
               } else {
                  mpValid = B_FALSE;
               }
            }
         } else {
            /* Keep the same buffer, we still need to flip the gen bit */
            rxDesc = VMXNET3_GET_DESC(cmdRing, rxdIdx);
            rxDesc->rxd.gen = cmdRing->gen;
            mpValid = B_FALSE;
         }

         VMXNET3_INC_RING_IDX(compRing, compRing->next2comp);
         VMXNET3_INC_RING_IDX(cmdRing, cmdRing->next2fill);
         compDesc = VMXNET3_GET_DESC(compRing, compRing->next2comp);
      } while (!eop);

      if (mp) {
         if (mpValid) {
            *mplistTail = mp;
            mplistTail = &mp->b_next;
            ASSERT(*mplistTail == NULL);
         } else {
            /* This message got holes, drop it */
            freemsg(mp);
         }
      }
   }

   if (rxqCtrl->updateRxProd) {
      uint32_t rxprod;

      /*
       * All buffers are actually available, but we can't tell that to
       * the device because it may interpret that as an empty ring.
       * So skip one buffer.
       */
      if (cmdRing->next2fill) {
         rxprod = cmdRing->next2fill - 1;
      } else {
         rxprod = cmdRing->size - 1;
      }
      VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_RXPROD, rxprod);
   }

   return mplist;
}
   070701000002AA000081A40000000000000000000000016822550500001FDF000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/vmxnet3_solaris.h  /*********************************************************
 * Copyright (C) 2007-2014 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef _VMXNET3_SOLARIS_H_
#define _VMXNET3_SOLARIS_H_

#include <sys/types.h>
#include <sys/conf.h>
#include <sys/debug.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strlog.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/kstat.h>
#include <sys/vtrace.h>
#include <sys/dlpi.h>
#include <sys/strsun.h>
#include <sys/ethernet.h>
#include <sys/modctl.h>
#include <sys/errno.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/pci.h>
#include <sys/strsubr.h>
#include <sys/pattr.h>
#include <sys/mac.h>
#include <sys/sockio.h>
#if defined(OPEN_SOLARIS) || defined(SOL11)
#  include <sys/mac_provider.h>
#endif
#include <sys/mac_ether.h>
#include <inet/common.h>
#include <inet/ip.h>
#include <inet/tcp.h>

#include "vm_basic_types.h"
#include "vm_device_version.h"
#include "buildNumber.h"

#include "vmxnet3_defs.h"
#include "vmxnet3_solaris_compat.h"

typedef struct vmxnet3_dmabuf_t {
   caddr_t              buf;
   uint64_t             bufPA;
   size_t               bufLen;
   ddi_dma_handle_t     dmaHandle;
   ddi_acc_handle_t     dataHandle;
} vmxnet3_dmabuf_t;

typedef struct vmxnet3_cmdring_t {
   vmxnet3_dmabuf_t     dma;
   uint16_t             size;
   uint16_t             next2fill;
   uint16_t             avail;
   uint8_t              gen;
} vmxnet3_cmdring_t;

typedef struct vmxnet3_compring_t {
   vmxnet3_dmabuf_t     dma;
   uint16_t             size;
   uint16_t             next2comp;
   uint8_t              gen;
} vmxnet3_compring_t;

typedef struct vmxnet3_metatx_t {
   mblk_t              *mp;
   uint16_t             sopIdx;
   uint16_t             frags;
} vmxnet3_metatx_t;

typedef struct vmxnet3_txqueue_t {
   vmxnet3_cmdring_t    cmdRing;
   vmxnet3_compring_t   compRing;
   vmxnet3_metatx_t    *metaRing;
   Vmxnet3_TxQueueCtrl *sharedCtrl;
} vmxnet3_txqueue_t;

typedef struct vmxnet3_rxbuf_t {
   vmxnet3_dmabuf_t        dma;
   mblk_t                 *mblk;
   frtn_t                  freeCB;
   struct vmxnet3_softc_t *dp;
   struct vmxnet3_rxbuf_t *next;
} vmxnet3_rxbuf_t;

typedef struct vmxnet3_bufdesc_t {
   vmxnet3_rxbuf_t     *rxBuf;
} vmxnet3_bufdesc_t;

typedef struct vmxnet3_rxpool_t {
   vmxnet3_rxbuf_t     *listHead;
   unsigned int         nBufs;
   unsigned int         nBufsLimit;
} vmxnet3_rxpool_t;

typedef struct vmxnet3_rxqueue_t {
   vmxnet3_cmdring_t    cmdRing;
   vmxnet3_compring_t   compRing;
   vmxnet3_bufdesc_t   *bufRing;
   Vmxnet3_RxQueueCtrl *sharedCtrl;
} vmxnet3_rxqueue_t;


typedef struct vmxnet3_softc_t {
   dev_info_t          *dip;
   int                  instance;
   mac_handle_t         mac;

   ddi_acc_handle_t     pciHandle;
   ddi_acc_handle_t     bar0Handle, bar1Handle;
   caddr_t              bar0, bar1;

   boolean_t            devEnabled;
   uint8_t              macaddr[6];
   uint32_t             cur_mtu;
   uint8_t		allow_jumbo;
   link_state_t         linkState;
   uint64_t             linkSpeed;
   vmxnet3_dmabuf_t     sharedData;
   vmxnet3_dmabuf_t     queueDescs;

   kmutex_t             intrLock;
   int                  intrType;
   int                  intrMaskMode;
   int                  intrCap;
   ddi_intr_handle_t    intrHandle;
   ddi_taskq_t         *resetTask;

   kmutex_t             txLock;
   vmxnet3_txqueue_t    txQueue;
   ddi_dma_handle_t     txDmaHandle;
   boolean_t            txMustResched;

   vmxnet3_rxqueue_t    rxQueue;
   kmutex_t             rxPoolLock;
   vmxnet3_rxpool_t     rxPool;
   volatile uint32_t    rxNumBufs;
   uint32_t             rxMode;

   vmxnet3_dmabuf_t     mfTable;
} vmxnet3_softc_t;

int       vmxnet3_alloc_dma_mem_1(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma,
                                  size_t size, boolean_t canSleep);
int       vmxnet3_alloc_dma_mem_128(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma,
                                    size_t size, boolean_t canSleep);
int       vmxnet3_alloc_dma_mem_512(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma,
                                    size_t size, boolean_t canSleep);
void      vmxnet3_free_dma_mem(vmxnet3_dmabuf_t *dma);
int       vmxnet3_getprop(vmxnet3_softc_t *dp, char *name,
                          int min, int max, int def);

int       vmxnet3_txqueue_init(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq);
mblk_t   *vmxnet3_tx(void *data, mblk_t *mps);
boolean_t vmxnet3_tx_complete(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq);
void      vmxnet3_txqueue_fini(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq);

int       vmxnet3_rxqueue_init(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq);
mblk_t   *vmxnet3_rx_intr(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq);
void      vmxnet3_rxqueue_fini(vmxnet3_softc_t *dp, vmxnet3_rxqueue_t *rxq);

extern ddi_device_acc_attr_t vmxnet3_dev_attr;

#define VMXNET3_MODNAME "vmxnet3s"
#define VMXNET3_DRIVER_VERSION_STRING "1.1.0.0"

/* Logging stuff */
#define VMXNET3_LOG(Level, Device, Format, Args...)   \
   cmn_err(Level, VMXNET3_MODNAME ":%d: " Format,      \
           Device->instance, ##Args);

#define VMXNET3_WARN(Device, Format, Args...)         \
   VMXNET3_LOG(CE_WARN, Device, Format, ##Args)

#define VMXNET3_DEBUG_LEVEL 2
#ifdef VMXNET3_DEBUG_LEVEL
#define VMXNET3_DEBUG(Device, Level, Format, Args...) \
   do {                                               \
      if (Level <= VMXNET3_DEBUG_LEVEL) {             \
         VMXNET3_LOG(CE_CONT, Device, Format, ##Args) \
      }                                               \
   } while (0)
#else
#define VMXNET3_DEBUG(Device, Level, Format, Args...)
#endif

#define MACADDR_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
#define MACADDR_FMT_ARGS(mac) \
   mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]

/* Default ring size */
#define VMXNET3_DEF_TX_RING_SIZE 256
#define VMXNET3_DEF_RX_RING_SIZE 256

/* Register access helpers */
#define VMXNET3_BAR0_GET32(Device, Reg) \
   ddi_get32((Device)->bar0Handle, (uint32_t *) ((Device)->bar0 + (Reg)))
#define VMXNET3_BAR0_PUT32(Device, Reg, Value) \
   ddi_put32((Device)->bar0Handle, (uint32_t *) ((Device)->bar0 + (Reg)), (Value))
#define VMXNET3_BAR1_GET32(Device, Reg) \
   ddi_get32((Device)->bar1Handle, (uint32_t *) ((Device)->bar1 + (Reg)))
#define VMXNET3_BAR1_PUT32(Device, Reg, Value) \
   ddi_put32((Device)->bar1Handle, (uint32_t *) ((Device)->bar1 + (Reg)), (Value))

/* Misc helpers */
#define VMXNET3_DS(Device) \
   ((Vmxnet3_DriverShared *) (Device)->sharedData.buf)
#define VMXNET3_TQDESC(Device) \
   ((Vmxnet3_TxQueueDesc *) (Device)->queueDescs.buf)
#define VMXNET3_RQDESC(Device) \
   ((Vmxnet3_RxQueueDesc *) ((Device)->queueDescs.buf + sizeof(Vmxnet3_TxQueueDesc)))

#define VMXNET3_ADDR_LO(addr) ((uint32_t) (addr))
#define VMXNET3_ADDR_HI(addr) ((uint32_t) (((uint64_t) (addr)) >> 32))

#define VMXNET3_GET_DESC(Ring, Idx) \
   (((Vmxnet3_GenericDesc *) (Ring)->dma.buf) + Idx)

/* Rings handling */
#define VMXNET3_INC_RING_IDX(Ring, Idx) \
   do {                                 \
      (Idx)++;                          \
      if ((Idx) == (Ring)->size) {      \
         (Idx) = 0;                     \
         (Ring)->gen ^= 1;              \
      }                                 \
   } while (0)

#define VMXNET3_DEC_RING_IDX(Ring, Idx) \
   do {                                 \
      if ((Idx) == 0) {                 \
         (Idx) = (Ring)->size;          \
         (Ring)->gen ^= 1;              \
      }                                 \
      (Idx)--;                          \
   } while (0)

#endif /* _VMXNET3_SOLARIS_H_ */
 070701000002AB000081A40000000000000000000000016822550500000623000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/vmxnet3_solaris_compat.h   /*********************************************************
 * Copyright (C) 2008 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#ifndef _VMXNET3_SOLARIS_COMPAT_H_
#define _VMXNET3_SOLARIS_COMPAT_H_

/*
 * In this file are stored all the definitions/helpers that the
 * DDI/DDK provides but that our toolchain currently lacks.
 * Nuke them the day the toolchain is refreshed.
 */

#define DDI_INTR_PRI(pri) (void *)((uintptr_t)(pri))

/* OPEN_SOLARIS and SOLARIS 11 */
#if defined(OPEN_SOLARIS) || defined(SOL11)
#define COMPAT_DDI_DEFINE_STREAM_OPS(XXname, XXidentify, XXprobe, XXattach, XXdetach, XXreset, XXgetinfo, XXflag, XXstream_tab) \
   DDI_DEFINE_STREAM_OPS(XXname, XXidentify, XXprobe, XXattach, XXdetach, \
   XXreset, XXgetinfo, XXflag, XXstream_tab, ddi_quiesce_not_supported)
#else
/* All other Solari */
#define LSO_TX_BASIC_TCP_IPV4 0x02
#define COMPAT_DDI_DEFINE_STREAM_OPS DDI_DEFINE_STREAM_OPS
#endif

#define HW_LSO 0x10

#define DB_LSOMSS(mp) ((mp)->b_datap->db_struioun.cksum.pad)

#define ETHERTYPE_VLAN (0x8100)

#endif /* _VMXNET3_SOLARIS_COMPAT_H_ */
 070701000002AC000081A40000000000000000000000016822550500003720000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/vmxnet3_tx.c   /*********************************************************
 * Copyright (C) 2007,2017-2019 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/

#include "vmxnet3_solaris.h"

typedef enum vmxnet3_txstatus {
   VMXNET3_TX_OK,
   VMXNET3_TX_FAILURE,
   VMXNET3_TX_PULLUP,
   VMXNET3_TX_RINGFULL
} vmxnet3_txstatus;

typedef struct vmxnet3_offload_t {
   uint16_t om;
   uint16_t hlen;
   uint16_t msscof;
} vmxnet3_offload_t;

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_txqueue_init --
 *
 *    Initialize a TxQueue. Currently nothing needs to be done.
 *
 * Results:
 *    DDI_SUCCESS.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
int
vmxnet3_txqueue_init(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq)
{
   return DDI_SUCCESS;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_txqueue_fini --
 *
 *    Finish a TxQueue by freeing all pending Tx.
 *
 * Results:
 *    DDI_SUCCESS.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
void
vmxnet3_txqueue_fini(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq)
{
   unsigned int i;

   ASSERT(!dp->devEnabled);

   for (i = 0; i < txq->cmdRing.size; i++) {
      mblk_t *mp = txq->metaRing[i].mp;
      if (mp) {
         freemsg(mp);
      }
   }
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_tx_prepare_offload --
 *
 *    Build the offload context of a msg.
 *
 * Results:
 *    0 if everything went well.
 *    +n if n bytes need to be pulled up.
 *    -1 in case of error (not used).
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_tx_prepare_offload(vmxnet3_softc_t *dp,
                           vmxnet3_offload_t *ol,
                           mblk_t *mp)
{
   int ret = 0;
   uint32_t start, stuff, value, flags, lso_flag, mss;

   ol->om = VMXNET3_OM_NONE;
   ol->hlen = 0;
   ol->msscof = 0;

   hcksum_retrieve(mp, NULL, NULL, &start, &stuff, NULL, &value, &flags);
   mac_lso_get(mp, &mss, &lso_flag);

   if (flags || lso_flag) {
      struct ether_vlan_header *eth = (void *) mp->b_rptr;
      uint8_t ethLen;

      if (eth->ether_tpid == htons(ETHERTYPE_VLAN)) {
         ethLen = sizeof(struct ether_vlan_header);
      } else {
         ethLen = sizeof(struct ether_header);
      }

      VMXNET3_DEBUG(dp, 4, "flags=0x%x, ethLen=%u, start=%u, stuff=%u, value=%u\n",
                            flags,      ethLen,    start,    stuff,    value);

      if (lso_flag & HW_LSO) {
         mblk_t *mblk = mp;
         uint8_t *ip, *tcp;
         uint8_t ipLen, tcpLen;

         /*
          * Copy e1000g's behavior:
          * - Do not assume all the headers are in the same mblk.
          * - Assume each header is always within one mblk.
          * - Assume the ethernet header is in the first mblk.
          */
         ip = mblk->b_rptr + ethLen;
         if (ip >= mblk->b_wptr) {
            mblk = mblk->b_cont;
            ip = mblk->b_rptr;
         }
         ipLen = IPH_HDR_LENGTH((ipha_t *) ip);
         tcp = ip + ipLen;
         if (tcp >= mblk->b_wptr) {
            mblk = mblk->b_cont;
            tcp = mblk->b_rptr;
         }
         tcpLen = TCP_HDR_LENGTH((tcph_t *) tcp);
         if (tcp + tcpLen > mblk->b_wptr) { // careful, '>' instead of '>=' here
            mblk = mblk->b_cont;
         }

         ol->om = VMXNET3_OM_TSO;
         ol->hlen = ethLen + ipLen + tcpLen;
         ol->msscof = mss;

         if (mblk != mp) {
            ret = ol->hlen;
         }
      } else if (flags & HCK_PARTIALCKSUM) {
         ol->om = VMXNET3_OM_CSUM;
         ol->hlen = start + ethLen;
         ol->msscof = stuff + ethLen;
      }

   }

   return ret;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_tx_one --
 *
 *    Map a msg into the Tx command ring of a vmxnet3 device.
 *
 * Results:
 *    VMXNET3_TX_OK if everything went well.
 *    VMXNET3_TX_RINGFULL if the ring is nearly full.
 *    VMXNET3_TX_PULLUP if the msg is overfragmented.
 *    VMXNET3_TX_FAILURE if there was a DMA or offload error.
 *
 * Side effects:
 *    The ring is filled if VMXNET3_TX_OK is returned.
 *
 *---------------------------------------------------------------------------
 */
static vmxnet3_txstatus
vmxnet3_tx_one(vmxnet3_softc_t *dp,
               vmxnet3_txqueue_t *txq,
               vmxnet3_offload_t *ol,
               mblk_t *mp,
               boolean_t retry)
{
   int ret = VMXNET3_TX_OK;
   unsigned int frags = 0, totLen = 0;
   vmxnet3_cmdring_t *cmdRing = &txq->cmdRing;
   Vmxnet3_TxQueueCtrl *txqCtrl = txq->sharedCtrl;
   Vmxnet3_GenericDesc *txDesc;
   uint16_t sopIdx, eopIdx;
   uint8_t sopGen, curGen;
   mblk_t *mblk;

   ASSERT(mutex_owned(&dp->txLock));

   sopIdx = eopIdx = cmdRing->next2fill;
   sopGen = cmdRing->gen;
   curGen = !cmdRing->gen;

   for (mblk = mp; mblk != NULL; mblk = mblk->b_cont) {
      unsigned int len = MBLKL(mblk);
      ddi_dma_cookie_t cookie;
      uint_t cookieCount;

      if (len) {
         totLen += len;
      } else {
         continue;
      }

      if (ddi_dma_addr_bind_handle(dp->txDmaHandle, NULL,
                                   (caddr_t) mblk->b_rptr, len,
                                   DDI_DMA_RDWR | DDI_DMA_STREAMING,
                                   DDI_DMA_DONTWAIT, NULL,
                                   &cookie, &cookieCount) != DDI_DMA_MAPPED) {
         VMXNET3_WARN(dp, "ddi_dma_addr_bind_handle() failed\n");
         ret = VMXNET3_TX_FAILURE;
         goto error;
      }

      ASSERT(cookieCount);

      do {
         uint64_t addr = cookie.dmac_laddress;
         size_t len = cookie.dmac_size;

         do {
            uint32_t dw2, dw3;
            size_t chunkLen;

            ASSERT(!txq->metaRing[eopIdx].mp);
            ASSERT(cmdRing->avail - frags);

            if (frags >= cmdRing->size - 1 ||
                (ol->om != VMXNET3_OM_TSO && frags >= VMXNET3_MAX_TXD_PER_PKT)) {

               if (retry) {
                  VMXNET3_DEBUG(dp, 2, "overfragmented, frags=%u ring=%hu om=%hu\n",
                                frags, cmdRing->size, ol->om);
               }
               ddi_dma_unbind_handle(dp->txDmaHandle);
               ret = VMXNET3_TX_PULLUP;
               goto error;
            }
            if (cmdRing->avail - frags <= 1) {
               dp->txMustResched = B_TRUE;
               ddi_dma_unbind_handle(dp->txDmaHandle);
               ret = VMXNET3_TX_RINGFULL;
               goto error;
            }

            if (len > VMXNET3_MAX_TX_BUF_SIZE) {
               chunkLen = VMXNET3_MAX_TX_BUF_SIZE;
            } else {
               chunkLen = len;
            }

            frags++;
            eopIdx = cmdRing->next2fill;

            txDesc = VMXNET3_GET_DESC(cmdRing, eopIdx);
            ASSERT(txDesc->txd.gen != cmdRing->gen);

            // txd.addr
            txDesc->txd.addr = addr;
            // txd.dw2
            dw2 = chunkLen == VMXNET3_MAX_TX_BUF_SIZE ? 0 : chunkLen;
            dw2 |= curGen << VMXNET3_TXD_GEN_SHIFT;
            txDesc->dword[2] = dw2;
            ASSERT(txDesc->txd.len == len || txDesc->txd.len == 0);
            // txd.dw3
            dw3 = 0;
            txDesc->dword[3] = dw3;

            VMXNET3_INC_RING_IDX(cmdRing, cmdRing->next2fill);
            curGen = cmdRing->gen;

            addr += chunkLen;
            len -= chunkLen;
         } while (len);

         if (--cookieCount) {
            ddi_dma_nextcookie(dp->txDmaHandle, &cookie);
         }
      } while (cookieCount);

      ddi_dma_unbind_handle(dp->txDmaHandle);
   }

   /* Update the EOP descriptor */
   txDesc = VMXNET3_GET_DESC(cmdRing, eopIdx);
   txDesc->dword[3] |= VMXNET3_TXD_CQ | VMXNET3_TXD_EOP;

   /* Update the SOP descriptor. Must be done last */
   txDesc = VMXNET3_GET_DESC(cmdRing, sopIdx);
   if (ol->om == VMXNET3_OM_TSO &&
       txDesc->txd.len != 0 &&
       txDesc->txd.len < ol->hlen) {
      ret = VMXNET3_TX_FAILURE;
      goto error;
   }
   txDesc->txd.om = ol->om;
   txDesc->txd.hlen = ol->hlen;
   txDesc->txd.msscof = ol->msscof;
   membar_producer();
   txDesc->txd.gen = sopGen;

   /* Update the meta ring & metadata */
   txq->metaRing[sopIdx].mp = mp;
   txq->metaRing[eopIdx].sopIdx = sopIdx;
   txq->metaRing[eopIdx].frags = frags;
   cmdRing->avail -= frags;
   if (ol->om == VMXNET3_OM_TSO) {
      txqCtrl->txNumDeferred +=
         (totLen - ol->hlen + ol->msscof - 1) / ol->msscof;
   } else {
      txqCtrl->txNumDeferred++;
   }

   VMXNET3_DEBUG(dp, 3, "tx 0x%p on [%u;%u]\n", mp, sopIdx, eopIdx);

   goto done;

error:
   /* Reverse the generation bits */
   while (sopIdx != cmdRing->next2fill) {
      VMXNET3_DEC_RING_IDX(cmdRing, cmdRing->next2fill);
      txDesc = VMXNET3_GET_DESC(cmdRing, cmdRing->next2fill);
      txDesc->txd.gen = !cmdRing->gen;
   }

done:
   return ret;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_tx --
 *
 *    Send packets on a vmxnet3 device.
 *
 * Results:
 *    NULL in case of success or failure.
 *    The mps to be retransmitted later if the ring is full.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
mblk_t *
vmxnet3_tx(void *data, mblk_t *mps)
{
   vmxnet3_softc_t *dp = data;
   vmxnet3_txqueue_t *txq = &dp->txQueue;
   vmxnet3_cmdring_t *cmdRing = &txq->cmdRing;
   Vmxnet3_TxQueueCtrl *txqCtrl = txq->sharedCtrl;
   vmxnet3_txstatus status = VMXNET3_TX_OK;
   mblk_t *mp;

   ASSERT(mps != NULL);
   mutex_enter(&dp->txLock);
   if (!dp->devEnabled) {
      goto done;
   }

   do {
      vmxnet3_offload_t ol;
      int pullup;

      mp = mps;
      mps = mp->b_next;
      mp->b_next = NULL;

      if (DB_TYPE(mp) != M_DATA) {
         /*
          * PR #315560: Solaris might pass M_PROTO mblks for some reason.
          * Drop them because we don't understand them and because their
          * contents are not Ethernet frames anyway.
          */
         ASSERT(B_FALSE);
         freemsg(mp);
         continue;
      }

      /*
       * Prepare the offload while we're still handling the original
       * message -- msgpullup() discards the metadata afterwards.
       */
      pullup = vmxnet3_tx_prepare_offload(dp, &ol, mp);
      if (pullup) {
         mblk_t *new_mp = msgpullup(mp, pullup);
         freemsg(mp);
         if (new_mp) {
            mp = new_mp;
         } else {
            continue;
         }
      }

      /*
       * Try to map the message in the Tx ring.
       * This call might fail for non-fatal reasons.
       */
      status = vmxnet3_tx_one(dp, txq, &ol, mp, B_FALSE);
      if (status == VMXNET3_TX_PULLUP) {
         /*
          * Try one more time after flattening
          * the message with msgpullup().
          */
         if (mp->b_cont != NULL) {
            mblk_t *new_mp = msgpullup(mp, -1);
            freemsg(mp);
            if (new_mp) {
               mp = new_mp;
               status = vmxnet3_tx_one(dp, txq, &ol, mp, B_TRUE);
            } else {
               continue;
            }
         }
      }
      if (status != VMXNET3_TX_OK && status != VMXNET3_TX_RINGFULL) {
         /* Fatal failure, drop it */
         freemsg(mp);
      }
   } while (mps && status != VMXNET3_TX_RINGFULL);

   if (status == VMXNET3_TX_RINGFULL) {
      mp->b_next = mps;
      mps = mp;
   } else {
      ASSERT(!mps);
   }

   /* Notify the device */
   if (txqCtrl->txNumDeferred >= txqCtrl->txThreshold) {
      txqCtrl->txNumDeferred = 0;
      VMXNET3_BAR0_PUT32(dp, VMXNET3_REG_TXPROD, cmdRing->next2fill);
   }

done:
   mutex_exit(&dp->txLock);

   return mps;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_tx_complete --
 *
 *    Parse a transmit queue and complete packets.
 *
 * Results:
 *    B_TRUE if Tx must be updated or B_FALSE if no action is required.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
boolean_t
vmxnet3_tx_complete(vmxnet3_softc_t *dp, vmxnet3_txqueue_t *txq)
{
   vmxnet3_cmdring_t *cmdRing = &txq->cmdRing;
   vmxnet3_compring_t *compRing = &txq->compRing;
   Vmxnet3_GenericDesc *compDesc;
   boolean_t completedTx = B_FALSE;
   boolean_t ret = B_FALSE;

   mutex_enter(&dp->txLock);

   compDesc = VMXNET3_GET_DESC(compRing, compRing->next2comp);
   while (compDesc->tcd.gen == compRing->gen) {
      vmxnet3_metatx_t *sopMetaDesc, *eopMetaDesc;
      uint16_t sopIdx, eopIdx;
      mblk_t *mp;

      eopIdx = compDesc->tcd.txdIdx;
      eopMetaDesc = &txq->metaRing[eopIdx];
      sopIdx = eopMetaDesc->sopIdx;
      sopMetaDesc = &txq->metaRing[sopIdx];

      ASSERT(eopMetaDesc->frags);
      cmdRing->avail += eopMetaDesc->frags;

      ASSERT(sopMetaDesc->mp);
      mp = sopMetaDesc->mp;
      freemsg(mp);

      eopMetaDesc->sopIdx = 0;
      eopMetaDesc->frags = 0;
      sopMetaDesc->mp = NULL;

      completedTx = B_TRUE;

      VMXNET3_DEBUG(dp, 3, "cp 0x%p on [%u;%u]\n", mp, sopIdx, eopIdx);

      VMXNET3_INC_RING_IDX(compRing, compRing->next2comp);
      compDesc = VMXNET3_GET_DESC(compRing, compRing->next2comp);
   }

   if (dp->txMustResched && completedTx) {
      dp->txMustResched = B_FALSE;
      ret = B_TRUE;
   }

   mutex_exit(&dp->txLock);

   return ret;
}
070701000002AD000081A40000000000000000000000016822550500001D9B000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/vmxnet3_utils.c    /*********************************************************
 * Copyright (C) 2007 VMware, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Common
 * Development and Distribution License (the "License") version 1.0
 * and no later version.  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 *         http://www.opensource.org/licenses/cddl1.php
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 *********************************************************/
 
#include "vmxnet3_solaris.h"

/* This symbol is needed by the Solaris dynamic loader */
char _depends_on[] = {"misc/mac"};

/* Used by ddi_regs_map_setup() and ddi_dma_mem_alloc() */
ddi_device_acc_attr_t vmxnet3_dev_attr = {
   DDI_DEVICE_ATTR_V0,
   DDI_STRUCTURE_LE_ACC,
   DDI_STRICTORDER_ACC
};

/* Buffers with no alignment constraint DMA description */
static ddi_dma_attr_t vmxnet3_dma_attrs_1 = {
   DMA_ATTR_V0,           /* dma_attr_version */
   0x0000000000000000ull, /* dma_attr_addr_lo */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_count_max */
   0x0000000000000001ull, /* dma_attr_align */
   0x0000000000000001ull, /* dma_attr_burstsizes */
   0x00000001,            /* dma_attr_minxfer */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_maxxfer */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */
   1,                     /* dma_attr_sgllen */
   0x00000001,            /* dma_attr_granular */
   0                      /* dma_attr_flags */
};

/* Buffers with a 128-bytes alignment constraint DMA description */
static ddi_dma_attr_t vmxnet3_dma_attrs_128 = {
   DMA_ATTR_V0,           /* dma_attr_version */
   0x0000000000000000ull, /* dma_attr_addr_lo */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_count_max */
   0x0000000000000080ull, /* dma_attr_align */
   0x0000000000000001ull, /* dma_attr_burstsizes */
   0x00000001,            /* dma_attr_minxfer */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_maxxfer */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */
   1,                     /* dma_attr_sgllen */
   0x00000001,            /* dma_attr_granular */
   0                      /* dma_attr_flags */
};

/* Buffers with a 512-bytes alignment constraint DMA description */
static ddi_dma_attr_t vmxnet3_dma_attrs_512 = {
   DMA_ATTR_V0,           /* dma_attr_version */
   0x0000000000000000ull, /* dma_attr_addr_lo */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_count_max */
   0x0000000000000200ull, /* dma_attr_align */
   0x0000000000000001ull, /* dma_attr_burstsizes */
   0x00000001,            /* dma_attr_minxfer */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_maxxfer */
   0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */
   1,                     /* dma_attr_sgllen */
   0x00000001,            /* dma_attr_granular */
   0                      /* dma_attr_flags */
};

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_alloc_dma_mem --
 *
 *    Allocate /size/ bytes of contiguous DMA-ble memory.
 *
 * Results:
 *    DDI_SUCCESS or DDI_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
static int
vmxnet3_alloc_dma_mem(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma,
                      size_t size, boolean_t canSleep,
                      ddi_dma_attr_t *dma_attrs)
{
   ddi_dma_cookie_t cookie;
   uint_t cookieCount;
   int (*cb) (caddr_t) = canSleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT;

   ASSERT(size != 0);

   /*
    * Allocate a DMA handle
    */
   if (ddi_dma_alloc_handle(dp->dip, dma_attrs, cb, NULL,
                            &dma->dmaHandle) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "ddi_dma_alloc_handle() failed\n");
      goto error;
   }

   /*
    * Allocate memory
    */
   if (ddi_dma_mem_alloc(dma->dmaHandle, size, &vmxnet3_dev_attr,
                         DDI_DMA_CONSISTENT, cb, NULL, &dma->buf,
                         &dma->bufLen, &dma->dataHandle) != DDI_SUCCESS) {
      VMXNET3_WARN(dp, "ddi_dma_mem_alloc() failed\n");
      goto error_dma_handle;
   }

   /*
    * Map the memory
    */
   if (ddi_dma_addr_bind_handle(dma->dmaHandle, NULL,
                                dma->buf, dma->bufLen,
                                DDI_DMA_RDWR | DDI_DMA_STREAMING,
                                cb, NULL, &cookie,
                                &cookieCount) != DDI_DMA_MAPPED) {
      VMXNET3_WARN(dp, "ddi_dma_addr_bind_handle() failed\n");
      goto error_dma_mem;
   }

   ASSERT(cookieCount == 1);
   dma->bufPA = cookie.dmac_laddress;

   return DDI_SUCCESS;

error_dma_mem:
   ddi_dma_mem_free(&dma->dataHandle);
error_dma_handle:
   ddi_dma_free_handle(&dma->dmaHandle);
error:
   dma->buf = NULL;
   dma->bufPA = NULL;
   dma->bufLen = 0;
   return DDI_FAILURE;
}

int
vmxnet3_alloc_dma_mem_1(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma,
                        size_t size, boolean_t canSleep)
{
   return vmxnet3_alloc_dma_mem(dp, dma, size, canSleep,
                                &vmxnet3_dma_attrs_1);
}

int
vmxnet3_alloc_dma_mem_512(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma,
                          size_t size, boolean_t canSleep)
{
   return vmxnet3_alloc_dma_mem(dp, dma, size, canSleep,
                                &vmxnet3_dma_attrs_512);
}

int
vmxnet3_alloc_dma_mem_128(vmxnet3_softc_t *dp, vmxnet3_dmabuf_t *dma,
                          size_t size, boolean_t canSleep)
{
   return vmxnet3_alloc_dma_mem(dp, dma, size, canSleep,
                                &vmxnet3_dma_attrs_128);
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_free_dma_mem --
 *
 *    Free DMA-ble memory.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
void
vmxnet3_free_dma_mem(vmxnet3_dmabuf_t *dma)
{
   ddi_dma_unbind_handle(dma->dmaHandle);
   ddi_dma_mem_free(&dma->dataHandle);
   ddi_dma_free_handle(&dma->dmaHandle);

   dma->buf = NULL;
   dma->bufPA = NULL;
   dma->bufLen = 0;
}

/*
 *---------------------------------------------------------------------------
 *
 * vmxnet3_getprop --
 *
 *    Get the numeric value of the property "name" in vmxnet3s.conf for
 *    the corresponding device instance.
 *    If the property isn't found or if it doesn't satisfy the conditions,
 *    "def" is returned.
 *
 * Results:
 *    The value of the property or "def".
 *
 * Side effects:
 *    None.
 *
 *---------------------------------------------------------------------------
 */
int
vmxnet3_getprop(vmxnet3_softc_t *dp, char *name,
                int min, int max, int def)
{
   int ret = def;
   int *props;
   uint_t nprops;

   if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dp->dip, DDI_PROP_DONTPASS,
                                 name, &props, &nprops) == DDI_PROP_SUCCESS) {
      if (dp->instance < nprops) {
         ret = props[dp->instance];
      } else {
         VMXNET3_WARN(dp, "property %s not available for this device\n",
                      name);
      }
      ddi_prop_free(props);
   }

   if (ret < min || ret > max) {
      ASSERT(def >= min && def <= max);
      VMXNET3_WARN(dp, "property %s invalid (%d <= %d <= %d)\n",
                   name, min, ret, max);
      ret = def;
   }

   VMXNET3_DEBUG(dp, 2, "getprop(%s) -> %d\n", name, ret);

   return ret;
}
 070701000002AE000081A4000000000000000000000001682255050000042F000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/modules/solaris/vmxnet3/vmxnet3s.conf  # Driver.conf(4) file for VMware Vmxnet Generation 3 adapters.

# TxRingSize --
#
#    Tx ring size for each vmxnet3s# adapter. Must be a multiple of 32.
#
#    Minimum value: 32
#    Maximum value: 4096
#
TxRingSize=256,256,256,256,256,256,256,256,256,256;

# RxRingSize --
#
#    Rx ring size for each vmxnet3s# adapter. Must be a multiple of 32.
#
#    Minimum value: 32
#    Maximum value: 4096
#
RxRingSize=256,256,256,256,256,256,256,256,256,256;

# RxBufPoolLimit --
#
#    Limit the number of Rx buffers cached for each vmxnet3s# adapter.
#    Increasing the limit might improve performance but increases the
#    memory footprint.
#
#    Minimum value: 0
#    Maximum value: RxRingSize * 10
#
RxBufPoolLimit=512,512,512,512,512,512,512,512,512,512;

# EnableLSO --
#
#    Enable or disable LSO for each vmxnet3s# adapter.
#
#    Minimum value: 0
#    Maximum value: 1
#
EnableLSO=1,1,1,1,1,1,1,1,1,1;

# MTU --
#
#    Set MTU for each vmxnet3s# adapter.
#
#    Minimum value: 60
#    Maximum value: 9000
#
#MTU=1500,1500,1500,1500,1500,1500,1500,1500,1500,1500;
 070701000002AF000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/namespacetool  070701000002B0000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/namespacetool/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002B1000081A4000000000000000000000001682255050000069E000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/namespacetool/Makefile.am  ################################################################################
### Copyright (C) 2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-namespace-cmd

vmware_namespace_cmd_SOURCES =
vmware_namespace_cmd_SOURCES += namespacetool.c

vmware_namespace_cmd_CPPFLAGS =
vmware_namespace_cmd_CPPFLAGS += -DVMTOOLS_USE_GLIB
vmware_namespace_cmd_CPPFLAGS += @GLIB2_CPPFLAGS@
vmware_namespace_cmd_CPPFLAGS += -I$(top_srcdir)/libvmtools

vmware_namespace_cmd_LDADD =
vmware_namespace_cmd_LDADD += @VMTOOLS_LIBS@
vmware_namespace_cmd_LDADD += @GLIB2_LIBS@

if HAVE_ICU
   vmware_namespace_cmd_LDADD += @ICU_LIBS@
   vmware_namespace_cmd_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS)  \
                               $(LIBTOOLFLAGS) --mode=link $(CXX)       \
                               $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
                               $(LDFLAGS) -o $@
else
   vmware_namespace_cmd_LINK = $(LINK)
endif  070701000002B2000081A40000000000000000000000016822550500005D11000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/namespacetool/namespacetool.c  /*********************************************************
 * Copyright (c) 2016-2020,2022-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * namespacetool.c --
 *
 *      Command line tool to communicate namespace DB.
 */

#include <stdio.h>
#include <glib.h>
#include "vmware.h"
#include "vmcheck.h"
#include "str.h"
#include "util.h"
#include "dynbuf.h"
#include "vmware/tools/guestrpc.h"
#include "vmware/tools/log.h"
#if defined(_WIN32)
#include "vmware/tools/win32util.h"
#endif
#include "debug.h"

// Core Namespace commands
#define NSDB_PRIV_GET_VALUES_CMD  "namespace-priv-get-values"
#define NSDB_PRIV_SET_KEYS_CMD    "namespace-priv-set-keys"

// namespace commands for end user
#define NSDB_GET_VALUE_USER_CMD  "get-value"
#define NSDB_SET_KEY_USER_CMD    "set-key"
#define NSDB_DEL_KEY_USER_CMD    "delete-key"

#define SUPPORTED_FILE_SIZE_IN_BYTES (16 * 1024) // Refer to namespaceDb.h

/*
 * Aggregation of command options.
 */

typedef struct NamespaceOptionsState {
   gchar *cmdName;
   gchar *nsName;
   gchar *keyName;    //for command set-key or delete-key or get-value
   gchar *valueToSet;
   gchar *oldValueToSet;
   gchar *getValueFromFile;
   gboolean verboseLogFlag;
   gboolean standardInput;
} NamespaceOptionsState;

static gchar *gAppName = NULL;


/*
 ******************************************************************************
 * PrintUsage --
 *
 * Prints the usage mesage.
 *
 ******************************************************************************
 */

static void
PrintUsage(GOptionContext *optCtx)
{
   gchar *usage;

   usage = g_option_context_get_help(optCtx, TRUE, NULL);
   g_printerr("%s", usage);
   g_free(usage);
}


/*
 ******************************************************************************
 * GetInternalNamespaceCommand --
 *
 * Namespacetool should allow only privilege namespaceDB.
 *
 * @param[in] cmd     namespace command name.
 *
 * @return internal namespace command
 *
 ******************************************************************************
 */

static char*
GetInternalNamespaceCommand(const gchar *cmd)
{
   if (g_strcmp0(cmd, NSDB_GET_VALUE_USER_CMD) == 0) {
      return NSDB_PRIV_GET_VALUES_CMD;
   } else if (g_strcmp0(cmd, NSDB_SET_KEY_USER_CMD) == 0) {
      return NSDB_PRIV_SET_KEYS_CMD;
   } else if (g_strcmp0(cmd, NSDB_DEL_KEY_USER_CMD) == 0) {
      return NSDB_PRIV_SET_KEYS_CMD;
   } else {
      return NULL;
   }
}


/*
 ******************************************************************************
 * ValidateNSCommands --
 *
 * Namespacetool should allow only privilege and non privilege commands of
 * namespaceDB.
 * @param[in] cmdName     namespace command name.
 *
 * @return TRUE if successful, FALSE otherwise.
 *
 ******************************************************************************
 */

static gboolean
ValidateNSCommands(const gchar *cmdName)
{
   if ((g_strcmp0(cmdName, NSDB_GET_VALUE_USER_CMD) == 0) ||
       (g_strcmp0(cmdName, NSDB_SET_KEY_USER_CMD) == 0) ||
       (g_strcmp0(cmdName, NSDB_DEL_KEY_USER_CMD) == 0)) {
      return TRUE;
   } else {
      fprintf(stderr, "Invalid command \"%s\"\n", cmdName);
      return FALSE;
   }
}


/*
 *******************************************************************************
 * PrintInternalCommand --
 *
 * Prints internal command in verbose mode
 *
 * @param[in] data      set of strings which has '\0' as a delimiter.
 * @param[in] dataSize  size of data.
 *
 *******************************************************************************
 */

static void
PrintInternalCommand(const char *data, size_t dataSize)
{
   char *tmp = NULL;
   char *printBuf = NULL;
   if (dataSize > 0) {
      int readCounter = 0;

      printBuf = (char *) calloc((int)dataSize, sizeof(char));
      if (printBuf == NULL) {
         fprintf(stderr, "Out of memory error");
         return;
      }
      tmp = printBuf;
      while (dataSize > readCounter) {
         if (*data != '\0') {
            *printBuf++ = *data;
         } else if (readCounter < dataSize - 1) {
            *printBuf++ = ',';
         }
         ++data;
         ++readCounter;
      }
      fprintf(stdout, "Internal command is %s\n", tmp);
   }
   free(tmp);
}


/*
 ******************************************************************************
 * GetValueFromStdin --
 *
 * Get standard input as a value
 *
 * @param[Out]  data     return standard input strings. This data is terminated
 *                       by an extra nul character, but there may be other
 *                       nuls in the intervening data.
 * @param[Out]  length   return length of standard input strings.
 *
 * @return TRUE on success or FALSE if stdin data is empty or more than Max
 *         limit
 *
 ******************************************************************************
 */

static Bool
GetValueFromStdin(gchar **data, gsize *length)
{
   GError *gErr = NULL;
   GIOStatus status;
   GIOChannel* iochannel;
   Bool retVal = TRUE;
#if defined(_WIN32)
   iochannel = g_io_channel_win32_new_fd(0);
#else
   iochannel = g_io_channel_unix_new(0);
#endif
   /**
    * This function never returns EOF
    */
   status =  g_io_channel_read_to_end(iochannel, data, length, &gErr);

   if (status != G_IO_STATUS_NORMAL) {
      fprintf(stderr, "%s: Read failed from stdin with status code %d. %s\n",
              gAppName, status, (gErr != NULL ? gErr->message : ""));
      retVal = FALSE;
   } else {
      if (*length > SUPPORTED_FILE_SIZE_IN_BYTES) {
         retVal = FALSE;
         fprintf(stderr, "%s: stdin data must not exceed %d bytes\n",
               gAppName, SUPPORTED_FILE_SIZE_IN_BYTES);
      } else  if (*length == 0) {
         retVal = FALSE;
         fprintf(stderr, "%s: stdin data must not be empty\n", gAppName);
      }
   }
   if (retVal == FALSE) {
      g_free(*data);
      *data = NULL;
      *length = 0;
   }
   g_free(gErr);
   g_io_channel_unref(iochannel);
   return retVal;
}


/*
 ******************************************************************************
 * GetValueFromFile --
 *
 * Read file contents as a string.
 *
 * @param[in]  filePath       file path
 * @param[out] fileContents   file contents
 * @param[out] length         length of file
 *
 * @return TRUE on success or FALSE if file is empty or more than Max limit
 *
 ******************************************************************************
 */

static Bool
GetValueFromFile(const char *filePath, char **fileContents, gsize *length)
{
   GError *gErr = NULL;
   Bool retVal = g_file_get_contents(filePath, fileContents, length, &gErr);
   if (retVal == FALSE) {
      fprintf(stderr, "%s: %s: %s\n", gAppName,
              (gErr != NULL ? gErr->message : "Failed while reading file"),
              filePath);
   } else {
      if (*length > SUPPORTED_FILE_SIZE_IN_BYTES) {
         retVal = FALSE;
         fprintf(stderr, "%s: File size must not exceed %d bytes\n",
                 gAppName, SUPPORTED_FILE_SIZE_IN_BYTES);
      } else  if (*length == 0) {
         retVal = FALSE;
         fprintf(stderr, "%s: File must not be empty\n", gAppName);
      }
   }
   if (retVal == FALSE) {
      g_free(*fileContents);
      *fileContents = NULL;
      *length = 0;
   }
   g_error_free(gErr);
   return retVal;
}


/*
 ******************************************************************************
 * RunNamespaceCommand --
 *
 * Processes the namespace command for get/set/delete key.
 * @param[in]  nsOptions     Parsed namespace command line options will
 *                           be placed in this struct.
 *
 * @return TRUE if successful, FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
RunNamespaceCommand(NamespaceOptionsState *nsOptions)
{
   char *result = NULL;
   size_t resultLen = 0;
   Bool status = FALSE;
   gchar *opCode = NULL;
   gchar *keyValueData = NULL;
   gsize keyValueLength = 0;

   const char *nscmd = GetInternalNamespaceCommand(nsOptions->cmdName);

   DynBuf buf;
   ASSERT(nscmd);
   DynBuf_Init(&buf);
   if (!DynBuf_Append(&buf, nscmd, strlen(nscmd)) ||
       !DynBuf_Append(&buf, " ", 1) ||
       !DynBuf_AppendString(&buf, nsOptions->nsName)) {
      fprintf(stderr, "Could not construct request buffer\n");
      goto exit;
   }
   if (strlen(nsOptions->oldValueToSet) == 0) {
      opCode = g_strdup("0");
   } else {
      opCode = g_strdup("1");
   }
   if (g_strcmp0(nsOptions->cmdName, NSDB_GET_VALUE_USER_CMD) == 0) {
      ASSERT(nsOptions->keyName);
      if (!DynBuf_AppendString(&buf, nsOptions->keyName)) {
         fprintf(stderr, "Could not construct request buffer\n");
         goto exit;
      }
   } else if (g_strcmp0(nsOptions->cmdName, NSDB_SET_KEY_USER_CMD) == 0) {
      ASSERT(nsOptions->keyName);
      if (!DynBuf_AppendString(&buf, "1") || // numOps
          !DynBuf_AppendString(&buf, opCode)||
          !DynBuf_AppendString(&buf, nsOptions->keyName)) {
         fprintf(stderr, "Could not construct request buffer\n");
         goto exit;
      }
      if (nsOptions->getValueFromFile == NULL) {
         if (nsOptions->valueToSet != NULL) {
            if (strlen(nsOptions->valueToSet) == 0) {
               fprintf(stderr, "%s: Key value must not be empty\n", gAppName);
               goto exit;
            }
            if (!DynBuf_AppendString(&buf, nsOptions->valueToSet) ||
                !DynBuf_AppendString(&buf, nsOptions->oldValueToSet)) {
               fprintf(stderr, "Could not construct request buffer\n");
               goto exit;
            }
         } else {
            if (GetValueFromStdin(&keyValueData, &keyValueLength) == FALSE) {
               goto exit;
            }
            if (!DynBuf_Append(&buf, keyValueData, keyValueLength + 1) ||
                !DynBuf_AppendString(&buf, nsOptions->oldValueToSet)) {
               fprintf(stderr, "Could not construct request buffer\n");
               goto exit;
            }
         }
      } else {
         if (GetValueFromFile(nsOptions->getValueFromFile,
                              &keyValueData, &keyValueLength) == FALSE) {
            goto exit;
         }
         if (!DynBuf_Append(&buf, keyValueData, keyValueLength + 1) ||
             !DynBuf_AppendString(&buf, nsOptions->oldValueToSet)) {
            fprintf(stderr, "Could not construct request buffer\n");
            goto exit;
         }
      }
   } else if (g_strcmp0(nsOptions->cmdName, NSDB_DEL_KEY_USER_CMD) == 0) {
      ASSERT(nsOptions->keyName);
      if (!DynBuf_AppendString(&buf, "1") ||
          !DynBuf_AppendString(&buf, opCode) ||
          !DynBuf_AppendString(&buf, nsOptions->keyName) ||
          !DynBuf_AppendString(&buf, "") || // zero length for value to delete
          !DynBuf_AppendString(&buf, nsOptions->oldValueToSet)) {
         fprintf(stderr, "Could not construct request buffer\n");
         goto exit;
      }
   }

   if (nsOptions->verboseLogFlag) {
      PrintInternalCommand(DynBuf_Get(&buf), DynBuf_GetSize(&buf));
   }

   status = RpcChannel_SendOneRaw(DynBuf_Get(&buf),
                                  DynBuf_GetSize(&buf), &result, &resultLen);
   if (!status) {
      fprintf(stderr, "failure: %s\n",
            result && *result ? result : "unknown");
   } else {
      char *p = result;
      if (resultLen == 0) {
         if (nsOptions->verboseLogFlag) {
            printf("success\n");
         }
      } else {
         if (nsOptions->verboseLogFlag) {
            printf("success - result:");
         }
         while (p < result + resultLen) {
            printf("%s", p);
            p += strlen(p) + 1;
         }
      }
      fflush(stdout);
   }
   free( result);

 exit:
   DynBuf_Destroy(&buf);
   fflush(stderr);
   g_free(keyValueData);
   g_free(opCode);
   return status;
}


/*
 ******************************************************************************
 * PostVerifyGetValueOptions --
 *
 * Post parse hook to verify namespace command get-value
 *
 * @param[in]  context    Unused.
 * @param[in]  group      Unused.
 * @param[in]  data       Unused.
 * @param[out] error      Setting error message to display in case of invalid
 *                        command line options.
 *
 * @return TRUE if successful, FALSE if option is invalid.
 *
 ******************************************************************************
 */

static gboolean
PostVerifyGetValueOptions(GOptionContext *context, GOptionGroup *group,
                          gpointer data, GError **error)
{
   NamespaceOptionsState *nsOptions;
   ASSERT(data);
   nsOptions = (NamespaceOptionsState *) data;
   if (nsOptions->cmdName == NULL) {
      g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                  "Namespace command must be specified");
      return FALSE;
   }
   if (nsOptions->nsName == NULL) {
      g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                  "Namespace name must be specified");
      return FALSE;
   }
   if (g_strcmp0(nsOptions->cmdName, NSDB_GET_VALUE_USER_CMD) == 0) {
      if (nsOptions->keyName == NULL) {
         g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                      "Key name must be specified");
         return FALSE;
      }
   }
   return TRUE;
}


/*
 ******************************************************************************
 * PostVerifyDeleteKeyOptions --
 *
 * Post parse hook to verify namespace command delete-key
 *
 * @param[in]  context    Unused.
 * @param[in]  group      Unused.
 * @param[in]  data       Unused.
 * @param[out] error      Setting error message to display in case of invalid
 *                        command line options.
 *
 * @return TRUE if successful, FALSE if option is invalid.
 *
 ******************************************************************************
 */

static gboolean
PostVerifyDeleteKeyOptions(GOptionContext *context, GOptionGroup *group,
                           gpointer data, GError **error)
{
   NamespaceOptionsState *nsOptions;
   ASSERT(data);
   nsOptions = (NamespaceOptionsState *) data;

   if (nsOptions->cmdName == NULL) {
      g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                  "Namespace command must be specified");
      return FALSE;
   }
   if (nsOptions->nsName == NULL) {
      g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                  "Namespace name must be specified");
      return FALSE;
   }
   if (g_strcmp0(nsOptions->cmdName, NSDB_DEL_KEY_USER_CMD) == 0) {
      if (nsOptions->keyName == NULL) {
         g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
               "Key name must be specified");
         return FALSE;
      }
   }
   return TRUE;
}


/*
 ******************************************************************************
 * PostVerifySetKeyOptions --
 *
 * Post parse hook to verify namespace command set-key
 *
 * @param[in]  context    Unused.
 * @param[in]  group      Unused.
 * @param[in]  data       Unused.
 * @param[out] error      Setting error message to display in case of invalid
 *                        command line options.
 *
 * @return TRUE if successful, FALSE if option is invalid.
 *
 ******************************************************************************
 */

static gboolean
PostVerifySetKeyOptions(GOptionContext *context, GOptionGroup *group,
                        gpointer data, GError **error)
{
   NamespaceOptionsState *nsOptions;

   ASSERT(data);
   nsOptions = (NamespaceOptionsState *) data;

   if (nsOptions->cmdName == NULL) {
      g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                  "Namespace command must be specified");
      return FALSE;
   }
   if (nsOptions->nsName == NULL) {
      g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                  "Namespace name must be specified");
      return FALSE;
   }
   if (g_strcmp0(nsOptions->cmdName, NSDB_SET_KEY_USER_CMD) == 0) {
      int usedOptions = 0;

      if (nsOptions->keyName == NULL) {
         g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                     "Key name must be specified");
         return FALSE;
      }
      if (nsOptions->valueToSet != NULL) {
         ++usedOptions;
      }
      if (nsOptions->getValueFromFile != NULL) {
         ++usedOptions;
      }
      if (nsOptions->standardInput == 1) {
         ++usedOptions;
      }
      if ((usedOptions > 1) || (usedOptions == 0)) {
         g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
                     "Key value must be specified with either -f or -v or -s");
         return FALSE;
      }
   }
   return TRUE;
}


/*
 ******************************************************************************
 * main --
 *
 * Main entry point.
 *
 * @param[in]  argc  Number of args.
 * @param[in]  argv  The args.
 *
 ******************************************************************************
 */

int
main(int argc, char *argv[])
{
   int success = -1;
   GError *gErr = NULL;
   gchar *descriptionBuf;
   gchar *helpBuf;
   GOptionContext *optCtx;
   GOptionGroup *gr;
   gchar *summary;
   NamespaceOptionsState nsOptions = { NULL, NULL, NULL, NULL, "",
                                       NULL, FALSE, FALSE};

   //Options for namespacetool commands

   GOptionEntry mainEntry[] = {
      { "verbose", 'V', 0, G_OPTION_ARG_NONE, &nsOptions.verboseLogFlag,
        "Verbose logging mode", NULL },
      { NULL }
   };
   GOptionEntry getValuesEntry[] = {
      { "key", 'k', G_OPTION_FLAG_NOALIAS, G_OPTION_ARG_STRING, &nsOptions.keyName,
        "Key value to return", "<key-name>" },
      { NULL }
   };
   GOptionEntry setKeysEntry[] = {
      { "key", 'k', G_OPTION_FLAG_NOALIAS, G_OPTION_ARG_STRING,
         &nsOptions.keyName, "Key name to use",  "<key-name>" },
      { "value", 'v', 0, G_OPTION_ARG_STRING, &nsOptions.valueToSet,
        "Value to set", "<value>"},
      { "oldValue", 'o', 0, G_OPTION_ARG_STRING, &nsOptions.oldValueToSet,
        "Value must match with current key value in the "
        "namespace for update operation to proceed", "<old-value>"},
      { "fromFile", 'f', 0, G_OPTION_ARG_STRING, &nsOptions.getValueFromFile,
        "Value to use from file path", "<file-path>"},
      { "stdin", 's', 0, G_OPTION_ARG_NONE, &nsOptions.standardInput,
        "Value to use from standard input", NULL},
      { NULL }
   };
   GOptionEntry deleteKeyEntry[] = {
      { "key", 'k', G_OPTION_FLAG_NOALIAS, G_OPTION_ARG_STRING,
         &nsOptions.keyName, "Key name to use", "<key-name>"},
      { "oldValue", 'o', G_OPTION_FLAG_NOALIAS, G_OPTION_ARG_STRING,
         &nsOptions.oldValueToSet,
        "Value must match with current key value in "
        "the namespace for delete operation to proceed", "<old-value>"},
      { NULL }
   };

#if defined(_WIN32)
   WinUtil_EnableSafePathSearching(TRUE);
#if defined(VMX86_RELEASE)
   WinUtil_VerifyExePathW();
#endif
#endif

   gAppName = g_path_get_basename(argv[0]);
   g_set_prgname(gAppName);

   /*
    * Checking if environment is VM
    */
   if (!VmCheck_IsVirtualWorld()) {
      g_printerr("Error: %s must be run inside a virtual machine"
                 " on a VMware hypervisor product.\n", gAppName);
      g_free(gAppName);
      return success;
   }

   optCtx = g_option_context_new("[get-value | set-key | delete-key] "
                                  "[<namespace-name>]");

   gr = g_option_group_new("namespace commands", "", "", optCtx, NULL);
   g_option_group_add_entries(gr, mainEntry);
   g_option_context_set_main_group(optCtx, gr);

   summary = g_strdup_printf("Example:\n  %s set-key <namespace-name> "
                             "-k <key-name> -v <value>\n  %s set-key "
                             "<namespace-name> -k <key-name> -f <file-path>"
                             "\n  echo \"<value>\" | %s set-key "
                             "<namespace-name> -k <key-name> -s\n  %s "
                             "delete-key  <namespace-name> -k <key-name>"
                             "\n  %s get-value <namespace-name> "
                             "-k <key-name>\n", gAppName,
                              gAppName, gAppName, gAppName, gAppName);
   g_option_context_set_summary(optCtx, summary);

   if (argc > 1) {
      nsOptions.cmdName = g_strdup(argv[1]);
   }
   if (argc > 2) {
      nsOptions.nsName = g_strdup(argv[2]);
   }
   //Namespacetool command - namespace-get-values
   descriptionBuf = g_strdup_printf("%s command %s:", gAppName,
                                    NSDB_GET_VALUE_USER_CMD);
   helpBuf = g_strdup_printf("Show help for command \"%s\"",
                              NSDB_GET_VALUE_USER_CMD);
   gr = g_option_group_new(NSDB_GET_VALUE_USER_CMD, descriptionBuf,
                           helpBuf, &nsOptions, NULL);
   g_free(descriptionBuf);
   g_free(helpBuf);
   g_option_group_add_entries(gr, getValuesEntry);
   g_option_context_add_group(optCtx, gr);
   g_option_group_set_parse_hooks(gr, NULL, PostVerifyGetValueOptions);

   //Namespacetool command - namespace-set-keys
   descriptionBuf = g_strdup_printf("%s command %s: - "
                                    "Create or update key value pair\n",
                                     gAppName, NSDB_SET_KEY_USER_CMD);
   helpBuf = g_strdup_printf ("Show help for command \"%s\"",
                              NSDB_SET_KEY_USER_CMD);

   gr = g_option_group_new(NSDB_SET_KEY_USER_CMD, descriptionBuf, helpBuf,
                           &nsOptions, NULL);
   g_free(descriptionBuf);
   g_free(helpBuf);
   g_option_group_add_entries(gr, setKeysEntry);
   g_option_context_add_group(optCtx, gr);
   g_option_group_set_parse_hooks(gr, NULL, PostVerifySetKeyOptions);

   //Namespacetool command - namespace-set-key for deleting key
   descriptionBuf = g_strdup_printf("%s command %s:- Delete key value pair\n",
                                    gAppName, NSDB_DEL_KEY_USER_CMD);
   helpBuf = g_strdup_printf("Show help for command \"%s\"",
                              NSDB_DEL_KEY_USER_CMD);

   gr = g_option_group_new(NSDB_DEL_KEY_USER_CMD, descriptionBuf, helpBuf,
                           &nsOptions, NULL);
   g_free(descriptionBuf);
   g_free(helpBuf);
   g_option_group_add_entries(gr, deleteKeyEntry);
   g_option_context_add_group(optCtx, gr);
   g_option_group_set_parse_hooks(gr, NULL, PostVerifyDeleteKeyOptions);

   if (!g_option_context_parse(optCtx, &argc, &argv, &gErr)) {
      PrintUsage(optCtx);
      fprintf(stderr, "%s: %s\n", gAppName, (gErr != NULL ? gErr->message : ""));
      g_error_free(gErr);
      goto exit;
   }

   if (nsOptions.verboseLogFlag) {
      VMTools_ConfigLogToStdio(gAppName);
   }

   /*
    *  Validating namespace command name after g_context_parser
    *  because argv[1] can have string "--help"
    */
   if (argc > 1 && ValidateNSCommands(nsOptions.cmdName) == FALSE) {
      goto exit;
   }

   success = RunNamespaceCommand(&nsOptions) ? 0 : 1;

 exit:
   g_option_context_free(optCtx);
   g_free(summary);
   g_free(gAppName);
   return success;
}
   070701000002B3000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002700000000open-vm-tools-12.5.2/open-vm-tools/pam    070701000002B4000081A40000000000000000000000016822550500000077000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/pam/debian # PAM configuration for vmtoolsd

@include common-auth

account     required    pam_shells.so
@include common-account

 070701000002B5000081A400000000000000000000000168225505000001A4000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/pam/generic    # This is a generic pam config file for open-vm-tools
# See https://kb.vmware.com/s/article/78251 for advice to use
# common authentication mechanisms.
auth       required         pam_shells.so
auth       sufficient       pam_unix.so shadow
auth       required         pam_unix_auth.so shadow
account    required         pam_shells.so
account    sufficient       pam_unix.so
account    required         pam_unix_acct.so
070701000002B6000081A400000000000000000000000168225505000000BC000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/pam/redhat auth       required     pam_shells.so
auth       substack     password-auth
auth       include      postlogin
account    required     pam_nologin.so
account    include      password-auth

070701000002B7000081A40000000000000000000000016822550500000099000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/pam/suse   auth     required       pam_shells.so
auth     requisite      pam_nologin.so
auth     include        common-auth
account  include        common-account

   070701000002B8000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/rpctool    070701000002B9000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/rpctool/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002BA000081A40000000000000000000000016822550500000596000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/rpctool/Makefile.am    ################################################################################
### Copyright (C) 2009-2016,2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-rpctool

vmware_rpctool_SOURCES =
vmware_rpctool_SOURCES += rpctool.c

vmware_rpctool_LDADD =
vmware_rpctool_LDADD += ../lib/rpcOut/libRpcOut.la
vmware_rpctool_LDADD += ../lib/message/libMessage.la
vmware_rpctool_LDADD += ../lib/backdoor/libBackdoor.la
vmware_rpctool_LDADD += ../lib/string/libString.la
vmware_rpctool_LDADD += ../lib/vmCheck/libVmCheck.la
vmware_rpctool_LDADD += ../lib/vmSignal/libVmSignal.la
vmware_rpctool_LDADD += ../lib/misc/libMisc.la
vmware_rpctool_LDADD += ../lib/stubs/libStubs.la

  070701000002BB000081A40000000000000000000000016822550500001617000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/rpctool/rpctool.c  /*********************************************************
 * Copyright (c) 2002-2020, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * rpcTool.c --
 *
 *      Simple program to drive the guest RPC library.
 */

#ifndef _WIN32
#include "sigPosixRegs.h"
#include <errno.h>
#include <stdint.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "vmware.h"
#include "rpcout.h"
#include "vmcheck.h"
#include "str.h"
#include "backdoor_def.h"
#ifdef _WIN32
#include "vmware/tools/win32util.h"
#endif

#define NOT_VMWARE_ERROR "Failed sending message to VMware.\n"

int RpcToolCommand(int argc, char *argv[]);

#ifndef _WIN32
static Bool SetSignalHandler(int sig,
                             void (*handler)(int, siginfo_t *, void *),
                             Bool reset);
#endif

void
PrintUsage(void)
{
   fprintf(stderr, "rpctool syntax:\n\n");
   fprintf(stderr, "  -h | --help\tprint usage.\n");
   fprintf(stderr, "  rpctool <text>\tsend <text> as an RPC command.\n");
}


#ifdef _WIN32
static Bool
ExceptionIsBackdoor(PEXCEPTION_POINTERS excInfo)
{
#ifdef _WIN64
   uint32 magic = excInfo->ContextRecord->Rax & 0xffffffff;
   uint16 port = excInfo->ContextRecord->Rdx & 0xffff;
#else
   uint32 magic = excInfo->ContextRecord->Eax & 0xffffffff;
   uint16 port = excInfo->ContextRecord->Edx & 0xffff;
#endif

   return (magic == BDOOR_MAGIC &&
           (port == BDOOR_PORT || port == BDOORHB_PORT));
}


#else
static void
SignalHandler(int sig,
              siginfo_t *sip,
              void *data)
{
   ucontext_t *ucp = (ucontext_t *) data;
#ifdef VM_ARM_64
   uint16 port = SC_X(ucp, 3);
   uint32 magic = SC_X(ucp, 0);
#else
   uint16 port = SC_EDX(ucp);
   uint32 magic = SC_EAX(ucp);
#endif

   if (magic == BDOOR_MAGIC &&
       (port == BDOORHB_PORT || port == BDOOR_PORT)) {
      fprintf(stderr, NOT_VMWARE_ERROR);
      exit(1);
   } else {
      SetSignalHandler(sig, NULL, TRUE);
      raise(sig);
   }
}


static Bool
SetSignalHandler(int sig,
                 void (*handler)(int, siginfo_t *, void *),
                 Bool reset)
{
   static struct sigaction old;

   if (reset) {
      struct sigaction tmp = old;

      memset(&old, 0, sizeof old);
      return sigaction(sig, &tmp, NULL) ? FALSE : TRUE;
   } else {
      struct sigaction new;

      /* Setup the handler and flags to get exception information. */
      new.sa_sigaction = handler;
      new.sa_flags = SA_SIGINFO;

      /* Block all signals when handling this. */
      if (sigfillset(&new.sa_mask) == -1) {
         fprintf(stderr, "Unable to initialize a signal set: %s.\n\n",
                 strerror(errno));
         return FALSE;
      }

      if (sigaction(sig, &new, &old) == -1) {
         fprintf(stderr, "Unable to initialize a signal handler: %s.\n\n",
                 strerror(errno));
         return FALSE;
      }
   }
   return TRUE;
}
#endif


int
main(int argc, char *argv[])
{
   int ret = 1;

#ifdef _WIN32
   WinUtil_EnableSafePathSearching(TRUE);
#if defined(VMX86_RELEASE)
   WinUtil_VerifyExePathW();
#endif
#endif

   /*
    * Check if environment is VM
    */
   if (!VmCheck_IsVirtualWorld()) {
      fprintf(stderr, "Error: %s must be run inside a virtual machine"
                      " on a VMware hypervisor product.\n", argv[0]);
      return -1;
   }

   if (argc <= 1) {
      PrintUsage();
      return 1;
   }

   if (!strncmp(argv[1], "-h", 2) ||
       !strncmp(argv[1], "--help", 6)) {
      PrintUsage();
      return 0;
   }

   argc--;
   argv++;

#ifdef _WIN32
   __try {
      ret = RpcToolCommand(argc, argv);
   } __except(ExceptionIsBackdoor(GetExceptionInformation()) ?
               EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
      fprintf(stderr, NOT_VMWARE_ERROR);
      return 1;
   }
#else
#ifdef __FreeBSD__
#  define ERROR_SIGNAL SIGBUS
#else
#  define ERROR_SIGNAL SIGSEGV
#endif
   if (SetSignalHandler(ERROR_SIGNAL, SignalHandler, FALSE)) {
      ret = RpcToolCommand(argc, argv);
   }
   SetSignalHandler(ERROR_SIGNAL, NULL, TRUE);
#endif
   return ret;
}

int
RpcToolCommand(int argc, char *argv[])
{
   char *result = NULL;
   Bool status = RpcOut_sendOne(&result, NULL, "%s", argv[0]);

   if (!status) {
      fprintf(stderr, "%s\n", result ? result : "NULL");
   } else {
      printf("%s\n", result);
   }
   free(result);
   return (status == TRUE ? 0 : 1);
}

void
Panic(const char *fmt, ...)
{
   va_list args;
   static char buf[1024];

   va_start(args, fmt);
   Str_Vsnprintf(buf, sizeof buf, fmt, args);
   va_end(args);

   fputs(buf, stderr);
   abort();
}


void
Debug(char *fmt, ...)
{
#if defined(VMX86_DEBUG) || defined(VMX86_DEVEL)
   va_list args;
   char *buf;

   va_start(args, fmt);
   buf = Str_Vasprintf(NULL, fmt, args);
   va_end(args);

   fprintf(stderr, "rpctool: %s\n", buf);
   fflush(stderr);
#else
   /* No output in non-debug/non-developer builds */
#endif
}


 070701000002BC000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/scripts    070701000002BD000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/scripts/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002BE000081A4000000000000000000000001682255050000057B000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/scripts/Makefile.am    ################################################################################
### Copyright (C) 2019 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

defaultscripts  = poweron-vm-default
defaultscripts += poweroff-vm-default
defaultscripts += suspend-vm-default
defaultscripts += resume-vm-default

confdir = /etc/vmware-tools

conf_SCRIPTS = ./common/statechange.subr
conf_SCRIPTS += $(defaultscripts)

vmsupportdir = /usr/bin

vmsupport_SCRIPTS = ./common/vm-support

vmwsrcdir = $(confdir)/scripts/vmware

vmwsrc_SCRIPTS = $(MODULES_OS)/network

$(defaultscripts): $(top_srcdir)/scripts/common/statechange.sh
	cp $(top_srcdir)/scripts/common/statechange.sh $@

 070701000002BF000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/scripts/build  070701000002C0000081A40000000000000000000000016822550500000698000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/scripts/build/instvmsg.sh  #!/bin/sh
################################################################################
### Copyright (C) 2011-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

#
# instvmsg.sh - VMSG catalog install script.
#
# This script installs a set of VMSG catalogs into their correct location. It
# avoids having to copy and paste this logic into several makefiles, especially
# since non-GNU make does not support $(call ...).
#
# Arguments:
# prog   ($1): program name (name of target catalog file)
# src    ($2): directory where catalogs are stored in the source tree.
# dest   ($3): $(datadir)
# $@         : language codes
#

prog=$1
src=$2
dest=$3

if test -z $dest; then
   echo "Missing parameters." 1>&2
   exit 1
fi

for i in $src/*.vmsg; do
   ldest=`basename $i`
   ldest=${dest}/open-vm-tools/messages/`echo $ldest | cut -d . -f 1 -`
   if ! test -d $ldest; then
      mkdir -p $ldest
   fi
   cp -f $i ${ldest}/${prog}.vmsg || exit 1
done

070701000002C1000081A40000000000000000000000016822550500000CF5000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/scripts/build/rpcgen_wrapper.sh.in #!/bin/sh
################################################################################
### Copyright (C) 2011-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

#
# rpcgen_wrapper.sh - executes rpcgen.
#
# rpcgen is very finicky in the way it's invoked. It tends to pollute the
# generated files with all sorts of weird stuff if you specify full paths to the
# files being compiled. So this script copies the source file (plus any other
# needed files) to the build directory before invoking rpcgen.
#
# The pattern to use in Makefile.am is:
#
# . have separate rules for generating the header and the source files, both
#   invoking rpcgen_wrapper.sh
# . make the source depend on the header
#
# The script assumes that $(builddir) is ".". The generated header is copied
# to the "lib/include" directory under $(top_builddir).
#
# Arguments:
#  input       ($1): path of .x file, relative to $(top_srcdir)
#  output      ($2): name of output header file
#  extra       ($@): other files to copy into the build directory; paths
#                    relative to $(top_srcdir)
#

set -e

rpcgen=@RPCGEN@
rpcgenflags="@RPCGENFLAGS@"
top_srcdir="@abs_top_srcdir@"
top_builddir="@abs_top_builddir@"
input=$1
output=$2

if [ -z "$output" ]; then
   echo "Invalid arguments." >&2
   exit 1
fi

shift 2

# need_update($target, $source)
need_update()
{
   test ! -f $1 -o $1 -ot $2
}


# do_rpcgen($type)
# type is either "-h" or "-c"
do_rpcgen()
{
   if need_update $output `basename $input`; then
      rm -f $output
   fi

   $rpcgen $rpcgenflags $1 -o $output `basename $input`
   sed 's,rpc/rpc\.h,vmxrpc.h,' $output |
      sed '/register int32_t .*buf;/d' > ${output}.tmp
   mv ${output}.tmp $output
}


do_header()
{
   #
   # Check both if srcdir != blddir, and also if we're copying a .x file from
   # a different component.
   #
   top_builddir=`cd $top_builddir && pwd`
   top_srcdir=`cd $top_srcdir && pwd`

   if test $top_builddir != $top_srcdir -o \
      `pwd` != `dirname $top_builddir/$input`; then
      for f in $@; do
         if need_update `basename $f` $top_srcdir/$f; then
            cp -f $top_srcdir/$f .
         fi
      done
      if need_update `basename $input` $top_srcdir/$input; then
         cp -f $top_srcdir/$input .
      fi
   fi

   do_rpcgen "-h"

   # Export the generated header.
   mkdir -p $top_builddir/lib/include/guestrpc
   cp $output $top_builddir/lib/include/guestrpc
}


do_impl()
{
   do_rpcgen "-c"
}


case $output in
   *.h) do_header "$@" ;;
   *.c) do_impl "$@" ;;
   *) echo "Unknown output file type: $output" ;;
esac

   070701000002C2000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/scripts/common 070701000002C3000081A400000000000000000000000168225505000001C5000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/scripts/common/guestproxy-ssl.conf [ req ]
default_bits            = 2048
distinguished_name      = req_distinguished_name

encrypt_key             = no
prompt                  = no

string_mask             = nombstr
x509_extensions         = x509

[ req_distinguished_name ]
countryName             = US
stateOrProvinceName     = California
localityName            = Palo Alto
organizationName      = VMware, Inc
organizationalUnitName  = PMI

[ x509 ]
basicConstraints        = CA:true
   070701000002C4000081A4000000000000000000000001682255050000112A000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/scripts/common/statechange.sh  #!/bin/sh
##########################################################
# Copyright (c) 2010-2016, 2023 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

##########################################################################
# DO NOT modify this file directly as it will be overwritten the next
# time the VMware Tools are installed.
##########################################################################

#
# statechange.sh
#
# This script is a refactored version of the legacy power scripts (e.g.,
# poweron-vm-default).  It expects to be installed in their places --
# in other words, `basename "$0"` might be poweron-vm-default.
#
# Handy reference/shorthand used in this doc/scripts:
#    TOOLS_CONFDIR ::= Depends on platform and installation settings.  Likely
#                      "/etc/vmware-tools" or
#                      "/Library/Application Support/VMware Tools"
#    powerOp       ::= One of "poweron-vm", "poweroff-vm", "suspend-vm", and
#                      "resume-vm".
#    vmwScriptDir  ::= $TOOLS_CONFDIR/scripts/vmware
#    userScriptDir ::= $TOOLS_CONFDIR/scripts/${powerOp}-default.d
#
# End users may install scripts of their own under $userScriptDir.  They
# are executed in alphabetical order with "$powerOp" as the only argument.
#
# NB:  This directory layout remains to preserve backwards compatibility. End
# users are free to write a single script which uses its only parameter
# (${powerOp}) as a discriminator, and then install symlinks to it in each
# of the ${powerOp}-default.d directories.
#
# On power-on and resume, VMware's scripts execute before the end user's.  On
# suspend and power-off, the end user's execute before VMware's.  (This way,
# VMware stops services only after the user's scripts have finished their
# work, and conversely restores the same services before the user's scripts
# attempt to use them.)
#
# Should any script exit non-zero, only its value will be saved to exitCode.
# (Any further non-zero exits will have no effect on exitCode.)  This script
# exits with $exitCode.
#
# XXX Consider using the available/enabled pattern for VMware's scripts.
#
# XXX This should be staged as a single executable whereby the desired
# power operation is passed in as a parameter.  (I.e., one would run
# "/path/to/statechange.sh suspend-vm" rather than having to install
# statechange.sh as suspend-vm-default.)
#

echo `date` ": Executing '$0'"

# See above.
TOOLS_CONFDIR=`dirname "$0"`
export TOOLS_CONFDIR

# Pull in subroutines like Panic.
. "$TOOLS_CONFDIR"/statechange.subr


#
# RunScripts --
#
#    Executes scripts installed under $scriptDir.
#
# Side effects:
#    exitCode may be incremented.
#

RunScripts() {
   scriptDir="$1"

   if [ -d "$scriptDir" ]; then
      for scriptFile in "$scriptDir"/*; do
         if [ -f "$scriptFile" -a -x "$scriptFile" ]; then
            "$scriptFile" $powerOp
            exitCode=`expr $exitCode \| $?`
         fi
      done
   fi
}


#
# main --
#
#    Entry point.  See comments at top of file for details.
#
# Results:
#    Exits with $exitCode.
#

main() {
   # This is confidence checked in the case/esac bit below.
   powerOp=`basename "$0" | sed 's,-default,,'`
   exitCode=0

   vmwScriptDir="$TOOLS_CONFDIR/scripts/vmware"
   userScriptDir="$TOOLS_CONFDIR/scripts/${powerOp}-default.d"

   case "$powerOp" in
      poweron-vm|resume-vm)
         RunScripts "$vmwScriptDir"
         RunScripts "$userScriptDir"
         ;;
      poweroff-vm|suspend-vm)
         RunScripts "$userScriptDir"
         RunScripts "$vmwScriptDir"
         ;;
      *)
         Panic "Invalid argument: $powerOp"
         ;;
   esac

   return $exitCode
}

main
  070701000002C5000081A400000000000000000000000168225505000005C6000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/scripts/common/statechange.subr    #!/bin/sh
##########################################################
# Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

##########################################################################
# DO NOT modify this file directly as it will be overwritten the next
# time the VMware Tools are installed.
##########################################################################


#
# Panic --
#
#    Write a formatted error message to stderr and exit.
#
# Results:
#    Stderr is spammed, program exits with exit code 1.
#
# Side effects:
#    None.
#

Panic() {
   fmt="`date '+%b %d %H:%M:%S'` `basename \"$0\"`"
   if [ -n "$1" ]; then
      fmt="${fmt}: $1"
      shift
   fi

   printf >&2 "${fmt}\n" "$@"
   exit 1
}
  070701000002C6000081A40000000000000000000000016822550500002752000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/scripts/common/vm-support  #!/bin/sh
##########################################################
# Copyright (c) 2006-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

# usage(): prints how to use this script
usage()
{
        echo ""
        echo "Usage: $0 [-hux]"
        echo "  -h prints this usage statement"
        echo "  -u updates the host with the state of support data" \
             "collection process"
        echo "  -x transfers support data to the host"
        exit 1
}


# banner(): prints any number of strings padded with
# newlines before and after.
banner()
{
	echo
	for option in "$@"
	do
		echo $option
	done
	echo
}


# The status constants are important and have to be kept
# in sync with VMware Workstation implementation

#	vm-support script is not running
VMSUPPORT_NOT_RUNNING=0
#	vm-support script is beginning
VMSUPPORT_BEGINNING=1
#	vm-support script running in progress
VMSUPPORT_RUNNING=2
#	vm-support script is ending
VMSUPPORT_ENDING=3
#	vm-support script failed
VMSUPPORT_ERROR=10
#	vm-support collection not supported
VMSUPPORT_UNKNOWN=100

#internal state machine state for update
update=0

#internal state machine state for transfering logs to host
transfer=0

# UpdateState($state): Updates the VM with the given state.
UpdateState()
{
   if [ $update -eq 1 ]; then
     vmware-xferlogs upd $1
   fi
}


# checkOutputDir(): checks for a self contained output
# directory for later tar'ing and creates it if needed

checkOutputDir()
{
   dir="$1"

   if [ ! -d "${OUTPUT_DIR}$dir" ]; then
      mkdir -p "${OUTPUT_DIR}$dir"

      if [ $? != 0 ]; then
         banner "Could not create ${OUTPUT_DIR}$dir... " \
                "Have you run out of disk space?" "Continuing"
         return -1
      fi
   fi
   return 0
}


# addfile(): copies whatever files and directories you give it to
# a self contained output directory for later tar'ing
# Working on copies could slow this down with VERY large files but:
# 1) We don't expect VERY large files
# 2) Since /proc files can be copied this preserves the tree without
#    having to cat them all into a file.
# 3) tar barfs on open files like logs if it changes while it's tar'ing.
#    Copying file first makes sure tar doesn't complain
addfile()
{
   file="$1"

   if [ ! -e "$file" ]; then
      return 2
   fi

   dir=`dirname "$file"`
   checkOutputDir "$dir"
   if [ $? != 0 ]; then
      return $?
   fi

   # Ignore stdout and handle errors.
   cp -pRP "$file" "${OUTPUT_DIR}$dir" 2>/dev/null
   if [ $? != 0 ]; then
      banner "Could not copy '$file' to the tar area."
   fi
}


# addfiles(): adds a list of files to the archive.
addfiles()
{
   for i in "$@"; do
      addfile $i
   done
}


# runcmd($out, $cmd): executes the command redirected to a file
runcmd()
{
   outFileRelPath="$1"
   shift # The command arguments are in "$@".

   dir=`dirname "$outFileRelPath"`
   checkOutputDir "$dir"
   if [ $? != 0 ]; then
      return
   fi

   "$@" > "$OUTPUT_DIR$outFileRelPath" 2>/dev/null
   if [ $? != 0 ]; then
      echo 3
         banner "Either could not run $@ or could not write to" \
                "${OUTPUT_DIR}$outFileRelPath" \
                "Do you have a full disk? Continuing..."
   fi
}


# stageLinux(): gather information for troubleshooting Linux guests.
stageLinux()
{
   # Try to collect bootloader config.
   addfile /etc/lilo.conf

   # Old linux kernel use modules.conf while new kernel use modprobe.conf and modprobe.d
   addfile /etc/modules.conf
   addfile /etc/modprobe.conf
   addfile /etc/modprobe.d

   addfile /etc/cron.daily
   addfile /etc/cron.hourly
   addfile /etc/cron.monthly
   addfile /etc/cron.weekly
   addfile /etc/crontab
   addfile /etc/ntp.conf
   addfile /etc/services
   addfile /proc/interrupts
   addfile /proc/irq

   # Commands to run ($2) and redirect to logs ($1) for inclusion.
   runcmd "/tmp/ps-auwwx.txt" ps auwwx
   runcmd "/tmp/lspci1.txt" lspci -M -vvv -nn -xxxx
   runcmd "/tmp/lspci2.txt" lspci -t -v -nn -F "${OUTPUT_DIR}/tmp/lspci1.txt"
   runcmd "/tmp/lspci3.txt" lspci -vvv -nn
   runcmd "/tmp/modules.txt" /sbin/lsmod
   runcmd "/tmp/uname.txt" uname -a
   runcmd "/tmp/issue.txt" cat /etc/issue
   if which rpm > /dev/null; then
      runcmd "/tmp/rpm-qa.txt" rpm -qa
   fi
   if which apt > /dev/null; then
      runcmd "/tmp/apt-list.txt" apt list
   fi
   runcmd "/tmp/free.txt" free
}


# stageFreeBSD(): gather information for troubleshooting FreeBSD guests.
stageFreeBSD()
{
   runcmd "/tmp/ps-auwwx.txt" ps auwwx
}

# stageSolaris(): gather information for troubleshooting Solaris guests.
stageSolaris()
{
   runcmd "/tmp/ps-eaf.txt" ps -eaf
}


# error(): prints an error message using the "banner" funtion and quits.
error()
{
   banner "$@"
   UpdateState $VMSUPPORT_ERROR
   exit 1
}


# Cleanup our temp folder and optionally exit.
cleanup()
{
   exitCode="$1"

   rm -rf "$OUTPUT_DIR"
   if [ $? != 0 ]; then
      banner "$OUTPUT_DIR was not successfully removed." \
             "Please remove manually."
   fi

   if [ "$exitCode" ]; then
      exit "$exitCode"
   fi
}

collectNetworkDetails()
{
    if which ip >/dev/null; then
        runcmd "/tmp/ip-addr.txt" ip addr
        runcmd "/tmp/ip-route.txt" ip route
    else
        runcmd "/tmp/ifconfig.txt" ifconfig -a
        runcmd "/tmp/route.txt" route
    fi

    if which ss >/dev/null; then
        runcmd "/tmp/ss-lan.txt" ss -lan
    else
        runcmd "/tmp/netstat-lan.txt" netstat -lan
    fi
}

# This executable may run with root privileges, so hardcode a PATH where
# unprivileged users cannot write.
export PATH=/bin:/sbin:/usr/bin:/usr/sbin

TARFILE=vm-`date +%Y-%m-%d`.$$.tar.gz
VER=0.95

# Parse args
for option in $@
do
   case $option in
   "-h")
      usage
      ;;
   "-u")
      update=1
      ;;
   "-x")
      transfer=1
      ;;
   *)
      usage
      ;;
   esac
done

#	Start message

UpdateState $VMSUPPORT_BEGINNING

banner "VMware UNIX Support Script $VER"

#	Check for root privledge

if [ `whoami` != 'root' ]; then
   error "Please re-run this program as root. "
fi

# Source /etc/profile.  If we can't find it, it's the users problem to get
# their paths straight.
if [ -f /etc/profile ]; then
	. /etc/profile
fi

# Protect against non-default values of $IFS (Not all scripts in /etc/profile.d/
# are good citizens).
if [ `uname` != 'SunOS' ]; then
   unset IFS 2>/dev/null
fi

# Create a temporary directory to place the files to archive.
#
# o mktemp creates a new directory, with a random name, and gives it
#   permissions 700 so only the current user (and root) can access the
#   contents of the directory. This prevents a rogue user from:
#   1) Guessing the name of the directory.
#   2) Performing a symlink attack inside the directory.
#   3) Reading data inside the directory, that only the current user (and root)
#      are supposed to access.
# o The directory is created inside /tmp, so only the current user (and root)
#   can delete the directory. This prevents a rogue user from wholesale
#   replacing the directory after we have created it, with a directory that has
#   more lenient permissions.
OUTPUT_DIR="`mktemp -d /tmp/vm-support.XXXXXX`"
if [ $? != 0 ]; then
   banner "Could not create a secure temporary directory. Exiting..."
   exit 1
fi

# Cleanup our temp folder if the process is signalled midway.
trap "cleanup 1" HUP INT QUIT TERM ABRT

banner "Collecting support information..."

# Common stuff that we gather for all OSes.
runcmd "/tmp/vm-support-version.txt" echo vm-support version: $VER

addfiles /etc/vmware-tools
addfiles /var/log/boot*
addfiles /var/log/secure*
addfiles /var/log/messages*
addfiles /var/log/syslog*
addfiles /var/log/vmware-*
addfiles /var/run/vmware-*
addfile /var/log/cloud-init.log
addfile /var/log/cloud-init-output.log
addfile /etc/cloud/cloud.cfg
addfile /etc/default/locale
addfile /etc/locale.conf

runcmd "/tmp/df.txt" df
collectNetworkDetails
runcmd "/tmp/mount.txt" mount
runcmd "/tmp/dmesg.txt" dmesg
runcmd "/tmp/ulimit-a.txt" ulimit -a
runcmd "/tmp/uptime.txt" uptime
runcmd "/tmp/date.txt" date
runcmd "/tmp/umask.txt" umask
runcmd "/tmp/locale-current.txt" locale
runcmd "/tmp/locale-list.txt" locale -a

case `uname` in
Linux)
   stageLinux
   #	tar options: 'S' for sparse core files.
   TAR_OPTS=-czSf
   ;;
FreeBSD)
   stageFreeBSD
   TAR_OPTS=-czf
   ;;
SunOS)
   stageSolaris
   TAR_OPTS=-czf
   ;;
esac

UpdateState $VMSUPPORT_RUNNING

banner "Creating tar archive..."
# Set umask to make diagnostic information unreadable to other users to avoid
# possible information leakage.
(umask 0077 && tar $TAR_OPTS $TARFILE $OUTPUT_DIR)
if [ $? != 0 ]; then
	banner "The tar process did not successfully complete!" \
	       "If tar reports that a file changed while reading, please attempt " \
	       "to rerun this script."
fi

# Clean up temporary files
trap - HUP INT QUIT TERM ABRT; cleanup

if [ $transfer -eq 1 ]; then
   banner "Transferring support data to the host..."
   vmware-xferlogs enc $TARFILE 2>/dev/null

   if [ $? != 0 ]; then
      banner "Could not transfer the support data  successfully: either " \
             "vmware-xferlogs binary is not in the path, or you are not " \
             "in a virtual machine."
   fi
else
   banner "Skipping support data transfer to the host."
fi

UpdateState $VMSUPPORT_ENDING
banner "Done, support data available in '$TARFILE'."
  070701000002C7000081A40000000000000000000000016822550500000115000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/scripts/common/vmware-toolbox.desktop  [Desktop Entry]
Encoding=UTF-8
Name=VMware Toolbox
Comment=VMware Guest Toolbox utility
# have to use the full path here otherwise help won't work
Exec=/usr/lib/vmware-tools/bin/vmware-toolbox
Icon=vmware-toolbox
Terminal=false
Type=Application
Categories=Application;Utility;
   070701000002C8000081A40000000000000000000000016822550500000CE0000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/scripts/common/vmware-xdg-detect-de    #!/bin/sh
#
# vmware-xdg-detect-de --
#
#    Determines which Freedesktop.Org compliant desktop environment we're
#    running under.
#
#    usage: vmware-xdg-detect-de
#
#    Simply prints the detected environment (GNOME, KDE, or XFCE).  Shamelessly
#    lifted from detectDE() routine found in many Portland xdg-* scripts.
#

#   Copyright 2010-2011, VMware, Inc.  All rights reserved.
#   Copyright 2009-2010, Fathi Boudra <fabo@freedesktop.org>
#   Copyright 2009-2010, Rex Dieter <rdieter@fedoraproject.org>
#   Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>
#   Copyright 2006, Jeremy White <jwhite@codeweavers.com>
#
#   LICENSE:
#
#   Permission is hereby granted, free of charge, to any person obtaining a
#   copy of this software and associated documentation files (the "Software"),
#   to deal in the Software without restriction, including without limitation
#   the rights to use, copy, modify, merge, publish, distribute, sublicense,
#   and/or sell copies of the Software, and to permit persons to whom the
#   Software is furnished to do so, subject to the following conditions:
#
#   The above copyright notice and this permission notice shall be included
#   in all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
#   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
#   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
#   OTHER DEALINGS IN THE SOFTWARE.
#
#---------------------------------------------

#--------------------------------------
# Checks for known desktop environments
# set variable DE to the desktop environments name, lowercase

# see https://bugs.freedesktop.org/show_bug.cgi?id=34164
unset GREP_OPTIONS

if [ -n "${XDG_CURRENT_DESKTOP}" ]; then
  #
  # This script deals in (case sensitive) desktop environment names as found in
  # menu-spec (http://standards.freedesktop.org/menu-spec/latest/apb.html),
  #
  xdgDE="${XDG_CURRENT_DESKTOP}"
  DE=`echo "${XDG_CURRENT_DESKTOP}" | tr '[:upper:]' '[:lower:]'`
fi

if [ x"$DE" = x"" ]; then
  # classic fallbacks
  if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
  elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
  elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;
  elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
  elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce
  fi
fi

if [ x"$DE" = x"" ]; then
  # fallback to checking $DESKTOP_SESSION
  case "$DESKTOP_SESSION" in
     gnome)
       DE=gnome;
       ;;
     LXDE)
       DE=lxde;
       ;;
     xfce|xfce4)
       DE=xfce;
       ;;
  esac
fi

if [ x"$DE" = x ]; then
   exit 1
fi

if [ -z "$xdgDE" ]; then
   case "$DE" in
   gnome)
      xdgDE=GNOME
      ;;
   kde)
      xdgDE=KDE
      ;;
   xfce)
      xdgDE=XFCE
      ;;
   esac
fi

echo $xdgDE
070701000002C9000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/scripts/freebsd    070701000002CA000081A40000000000000000000000016822550500000B1C000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/scripts/freebsd/network    #!/bin/sh
##########################################################
# Copyright (c) 2010-2017, 2023 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

##########################################################################
# DO NOT modify this file directly as it will be overwritten the next
# time the VMware Tools are installed.
##########################################################################

#
# network (FreeBSD 6.3 and above)
#
# This script uses FreeBSD's rc(8) scripts to stop and restart networking
# services in response to suspend and resume events, respectively.
#


echo `date` ": Executing '$0'"
echo

. `dirname "$0"`/../../statechange.subr


#
# ToggleNetwork --
#
#    Sources native configuration files in a subshell and executes native
#    scripts to either start or stop networking services associated with
#    a single interface.
#
# Results:
#    See description above.
#
# Side effects:
#    All side effects implied by FreeBSD's netif script.
#

ToggleNetwork() {
   (
      . /etc/rc.subr
      . /etc/network.subr

      load_rc_config network

      for intf in `list_net_interfaces dhcp`; do
         /etc/rc.d/netif $1 $intf
         /etc/rc.d/dhclient $1 $intf
         ec=$?

         # Failure to stop an interface should not interfere with suspend.
         if [ "$1" != "stop" ]; then
            exitCode=`expr $exitCode \| $ec`
         fi
      done
   )
}


#
# main --
#
#    Main entry point.  Perform some confidence checking, then map state change
#    events to relevant networking operations.
#
# Results:
#    See comment at top of file.
#

main() {
   exitCode=0

   [ -r /etc/rc.subr ] || Panic "Cannot read /etc/rc.subr."
   [ -r /etc/network.subr ] || Panic "Cannot read /etc/network.subr"
   [ -x /etc/rc.d/netif ] || Panic "Cannot find an executable /etc/rc.d/netif"
   [ -x /etc/rc.d/dhclient ] || \
      Panic "Cannot find an executable /etc/rc.d/dhclient"

   case "$1" in
      suspend-vm)
         ToggleNetwork stop
         ;;
      resume-vm)
         ToggleNetwork start
         ;;
      *) ;;
   esac

   return $exitCode
}

main "$@"
070701000002CB000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/scripts/linux  070701000002CC000081A400000000000000000000000168225505000046DE000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/scripts/linux/network  #!/bin/sh -x
##########################################################
# Copyright (c) 2001-2018, 2021, 2023-2024 Broadcom. All rights reserved.
# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################


#
# network (Linux)
#
# Using a combination of a system networking script, ifconfig, ifup, ifdown
# and the ip command, attempt to release and renew DHCP leases upon receipt
# of suspend and resume events, respectively.
#

SOURCE=$0
logdir=/var/log
logbase=$logdir/vmware-network
logfile=$logbase.log

# Defines logging mode enabled (1) or disabled (0)
logmode=1

# Defines whether to rotate logs (1) or not (0)
logrotate=1

# Defines whether to set log file permissions (1) or not (0)
logsetperms=1

#
# Get log file path
#
get_logfile() {
   file=`vmware-toolbox-cmd config get logging network.data | \
        sed -e 's/.*= *//' -e 's/ *$//'`
   if [ -n "${file##*"UNSET"*}" ]; then
      logfile=$file
      logdir=`dirname $logfile`
      logbase=`echo $logfile | sed 's/\..*$//'`
   fi
}

#
# Get Network logging config
#
get_logconfig() {
   handler=`vmware-toolbox-cmd config get logging network.handler | \
           sed -e 's/.*= *//' -e 's/ *$//'`
   if [ -z "${handler##*"UNSET"*}" ]; then
      # Default unset to file handler
      handler=file
   fi
   case $handler in
      "file")
         get_logfile
         ;;
      "file+")
         # Append to a file instead of recreating each time
         get_logfile
         logrotate=0
         ;;
      "vmx"|"std")
         logrotate=0
         logsetperms=0
         ;;
      "syslog")
         logfile=/var/log/syslog
         logdir=`dirname $logfile`
         logrotate=0
         logsetperms=0
         ;;
      *)
         # Default unknown to 'file' handler, log the issue.
         vmtoolsd --cmd "log WARNING: [$SOURCE] Logging unknown network.handler: $handler"
         get_logfile
         ;;
   esac
}

#
# Rotate any logs
#
rotate_logfile() {
   if [ $logrotate -eq 1 ]; then
      max=`vmware-toolbox-cmd config get logging network.maxOldLogFiles | \
          sed -e 's/.*= *//' -e 's/ *$//'`
      if [ `expr "$max" : '[0-9]\+$'` -eq 0 ]; then
         # max is not numeric (UNSET or else), use default.
         max=9
      fi
      if [ $max -lt 1 ]; then
         # max must be > 0, use default.
         max=9
      fi
      max=`expr $max - 1`
      for s in `seq $max -1 1`; do
         d=`expr $s + 1`
         mv -f $logbase.$s.log $logbase.$d.log
      done
      mv -f $logbase.log $logbase.1.log
   fi
}

#
# Logging api
#
log() {
   if [ $logmode -eq 1 ]; then
      if [ "$handler" = "vmx" ]; then
         vmtoolsd --cmd "log $*"
      elif [ "$handler" = "std" ]; then
         echo `date` ": $*"
      elif [ -w $logdir ]; then
         space=`df -k $logdir | awk 'NR == 2 { print $4 }'`
         if [ $space -gt 1024 ]; then
            echo `date` ": $*" >> $logfile
         else
            vmtoolsd --cmd "log WARNING: [$SOURCE] Logging disabled. No space left in $logdir"
            logmode=0
         fi
      else
         vmtoolsd --cmd "log WARNING: [$SOURCE] Logging disabled. $logdir is not writable"
         logmode=0
      fi
   fi
}

get_logconfig
rotate_logfile

if [ $logsetperms -eq 1 ]; then
   # Create/Recreate logfile
   if [ ! -e $logfile ]; then
      touch $logfile
   fi

   # Set logfile permissions before writing first log to file.
   # ** When handler is 'file+' and logfile existed prior to execution, this
   #    updates the permissions before appending to logfile.
   # ** Otherwise sets permission on new file.
   chmod 0600 $logfile
fi

log "Executing '$0 $*'"

. `dirname "$0"`/../../statechange.subr


#
# find_networking_script --
#
#    Searches common Linux distro init/rc paths to find a singular network
#    services script.
#
# Result:
#    Returns a valid networking script path on success or "error" on failure.
#
# Side effects:
#    None.
#

find_networking_script() {
   local script="error"
   for dir in "/etc/init.d" "/sbin/init.d" "/etc" "/etc/rc.d" ; do
      if [ -d "$dir/rc0.d" ] &&
         [ -d "$dir/rc1.d" ] &&
         [ -d "$dir/rc2.d" ] &&
         [ -d "$dir/rc3.d" ] &&
         [ -d "$dir/rc4.d" ] &&
         [ -d "$dir/rc5.d" ] &&
         [ -d "$dir/rc6.d" ]; then

         # Now find the appropriate networking script.
         if [ -d "$dir/init.d" ]; then
            if [ -x "$dir/init.d/network" ]; then
               script="$dir/init.d/network"
            elif [ -x "$dir/init.d/networking" ]; then
               script="$dir/init.d/networking"
            fi
         else
            if [ -x "$dir/network" ]; then
               script="$dir/network"
            elif [ -x "$dir/networking" ]; then
               script="$dir/networking"
            fi
         fi
      fi
   done

   log "$script"
}


#
# exec_networking_script --
#
#    Execute the networking script to bring network interfaces up or down
#    based on the given input action argument.
#

exec_networking_script()
{
   local script=$1
   local action=$2

   # Using SysV "service" if it exists, otherwise fall back to run the
   # script directly
   service=`which service 2>/dev/null`
   if [ $? = 0 -a -n "$service" ]; then
      serviceName=`basename "$script"`
      "$service" "$serviceName" "$action"
   else
      "$script" "$action"
   fi

   return $?
}


#
# exec_systemctl_service --
#
#    Handle linux distributions that use systemd to replace the legacy
#    system V startup scripts. The previous network script searching
#    approach is no longer viable in these systems. Invoke the systemctl
#    command to control the network service instead.
#

exec_systemctl_service()
{
   local rc=1
   local action=$1
   local ctlcmd=$(which systemctl 2>/dev/null)
   local service

   [ -z "$ctlcmd" ] && return $rc

   for svc in systemd-networkd network; do
      if ! $ctlcmd status $svc | grep -iq 'not-found'; then
         service=$svc && break
      fi
   done

   [ -z "$service" ] && return $rc

   $ctlcmd $action $service; rc=$?

   # When use the systemd-networkd service to shut down interfaces, interface
   # address and state remain unchanged. Need to use ip command to change its
   # address and state.
   if [ $rc = 0 -a $service = 'systemd-networkd' -a $action = 'stop' ]; then
      config_network_intfs $action; rc=$?
   fi

   return $rc
}


#
# del_intf_ip --
#
#    Use the ip command to remove all the addresses of an interface.
#

del_intf_ip()
{
   local nic=$1

   $ip_cmd addr flush dev $nic
   return $?
}


#
# ip_intf_ops --
#
#    Use the ip command to change the state of an interface to up or down.
#

ip_intf_ops()
{
   local rc=1
   local nic=$1
   local ops=$2

   [ -z "$ip_cmd" ] && return $rc

   $ip_cmd link set $nic $ops; rc=$?

   # Remove interface addresses when taking an interface down.
   if [ $rc = 0 -a $ops = down ]; then
      del_intf_ip $nic; rc=$?
   fi

   return $rc
}


#
# intf_ops --
#
#    Execute the specified command (ifup or ifdown) if available, otherwise use
#    the ip command as fallback. If ifup or ifdown fails, run the ip command to
#    retry the intended operation.
#

intf_ops()
{
   local rc=0
   local cmd=$1
   local ops=$2
   local nic=$3
   local tmp

   if [ ! -z "$cmd" ]; then
      tmp=$($cmd $nic 2>&1); rc=$?

      # Some systems still return a successful status even the command fails
      # because the interface is not configured in the configuration file. So
      # have to examine the command output to determine the actual status.
      if [ $rc = 0 ]; then
         echo $tmp | egrep -iq 'not configured|ignoring unknown' && rc=1
      fi
   fi

   # If ifup/ifdown fails, try the ip fallback.
   if [ -z "$cmd" -o $rc != 0 ]; then
      ip_intf_ops $nic $ops; rc=$?
   fi

   return $rc
}


#
# exec_intf_ops --
#
#    Perform an operation to bring an individual interface up or down.
#

exec_intf_ops()
{
   local rc=0
   local action=$1
   local nic=$2

   case $action in
      start)
         intf_ops "$ifup_cmd" up $nic; rc=$?
         ;;
      stop)
         intf_ops "$ifdown_cmd" down $nic; rc=$?
         ;;
      *)
         Panic "Illegal interface action: $action"
         ;;
   esac

   return $rc
}


#
# config_network_intfs --
#
#    For Linux systems not supporting networking scripts to bring interfaces
#    up or down, provide a way to change the interface state individually.
#

config_network_intfs()
{
   local rc=0
   local action=$1

   if [ -f "$activeList" ]; then

      while read nic; do
         exec_intf_ops $action $nic
         rc=$(expr $rc \| $?)
      done < $activeList
   fi

   return $rc
}


#
# run_network_script --
#
#    Finds out how to run the system's script used to control networking, and
#    runs it with the given argument (which should be one of the usual SysV
#    init script arguments). If it does not work, tries the other alternatives.
#    So far, our alternatives are (a) systemctl (b) network script (c) perform
#    an individual interface state change.
#

run_network_script()
{
   local action=$1
   local rc=0
   local script

   while true; do

      exec_systemctl_service $action
      [ $? != 0 ] || break

      script=`find_networking_script`

      if [ $script != "error" ]; then
         exec_networking_script $script $action
         [ $? != 0 ] || break
      fi

      # Since all the other alternatives fail, need to manually change
      # individual interface state.
      config_network_intfs $action; rc=$?
      break
   done

   return $rc
}


#
# save_active_NIC_list --
#
#    Records a list of every active NIC to /var/run/vmware-active-nics.
#
#    XXX What's the story on aliases?  Should they still be included, or will
#    they be recreated automatically upon resume?
#
# Results:
#    $activeList has, one per line, a list of all active NICs.
#
# Side effects:
#    None.
#

save_active_NIC_list()
{
   local intf_out

   >$activeList

   # Find out all the non-loopback up interfaces. Use ip if available
   # otherwise fall back to the ifconfig command.
   # ifconfig is buggy on some platforms and truncates long
   # network names
   if [ -n "$ip_cmd" ]; then
      for nic in $($ip_cmd link show up | egrep '\bUP\b' | awk -F: '{print $2}'); do
         $ip_cmd link show ${nic%@*} | grep -iq 'link/ether' && echo ${nic%@*} >> $activeList
      done
   else
      for nic in $($ifconfig_cmd | sed -n 's/^\([^: \t]*\).*$/\1/p'); do
         intf_out=$($ifconfig_cmd $nic)
         echo $intf_out | grep -iq loopback && continue
         echo $intf_out | egrep -q '\bUP\b' && echo $nic >> $activeList
      done
   fi
}


#
# rescue_NIC --
#
#    For each NIC recorded in $activeList that is not currently "up", run
#    "ifup $nic" or "ip link set $nic up" to bring the interface up.
#
# Results:
#    All downed NICs should be active.
#

rescue_NIC()
{
   local rc=0
   local intf_out

   if [ -f "$activeList" ]; then
      while read nic; do
         if [ -n "$ip_cmd" ]; then
            intf_out=$($ip_cmd link show $nic up)
         else
            intf_out=$($ifconfig_cmd $nic)
         fi

         if echo $intf_out | grep -q 'UP'; then
            log "[rescue_nic] $nic is already active."
         else
            log "[rescue_nic] activating $nic ..."

            # Our best effort to activate interfaces, use ifup if available
            # otherwise use the ip command as fallback.
            intf_ops "$ifup_cmd" up $nic
            rc=$(expr $rc \| $?)
         fi
      done < $activeList

      rm -f $activeList
   fi

   return $rc
}


#
# TranquilizeNetworkManager --
#
#    Put the NetworkManager daemon to sleep (maybe).
#
#    See http://projects.gnome.org/NetworkManager/developers/spec.html .
#
# Results:
#    Sleep(true) request is sent to the NetworkManager D-Bus interface.
#
# Side effects:
#    None.
#

TranquilizeNetworkManager()
{
   # 'which' may be a bit noisy, so we'll shush it.
   dbusSend=`which dbus-send 2>/dev/null`
   rc=$?
   if [ $rc -ne 0 ]; then
      return $rc
   fi

   # Check NetworkManager state before disabling it.
   nm_state=`$dbusSend --system --print-reply		\
             --dest=org.freedesktop.NetworkManager	\
             /org/freedesktop/NetworkManager		\
             org.freedesktop.DBus.Properties.Get	\
             string:'org.freedesktop.NetworkManager'	\
             string:'State'				\
             | awk '/variant/ {print $3;}'`
   if [ -z "$nm_state" ]; then
      return 1
   fi
   # NetworkManager API     0.7/0.8   0.9
   # NM_STATE_ASLEEP           1      10
   # NM_STATE_DISCONNECTED     4      20
   case $nm_state in
      1|4|10|20)
         # Nothing needs to be done.
         return 0
         ;;
   esac

   # NetworkManager 0.9.1 and above + earlier versions that honor
   # the "Sleep" config boolean.
   $dbusSend --system --print-reply          \
      --dest=org.freedesktop.NetworkManager  \
      /org/freedesktop/NetworkManager        \
      org.freedesktop.NetworkManager.Sleep boolean:true
   rc=$?
   if [ $rc -eq 0 ]; then
      return $rc
   fi
   # NetworkManager 0.9.0
   $dbusSend --system --print-reply          \
      --dest=org.freedesktop.NetworkManager  \
      /org/freedesktop/NetworkManager        \
      org.freedesktop.NetworkManager.Enable boolean:false
   rc=$?
   if [ $rc -eq 0 ]; then
      return $rc
   fi
   # NetworkManager 0.6
   $dbusSend --system --print-reply          \
      --dest=org.freedesktop.NetworkManager  \
      /org/freedesktop/NetworkManager        \
      org.freedesktop.NetworkManager.sleep
   rc=$?

   return $rc
}


#
# WakeNetworkManager --
#
#    Wake the NetworkManager daemon (maybe).
#
#    See http://projects.gnome.org/NetworkManager/developers/spec.html .
#
# Results:
#    Sleep(false)request is sent to the NetworkManager D-Bus interface.
#
# Side effects:
#    None.
#

WakeNetworkManager()
{
   # 'which' may be a bit noisy, so we'll shush it.
   dbusSend=`which dbus-send 2>/dev/null`
   rc=$?
   if [ $rc = 0 ]; then
      # NetworkManager 0.9.1 and above + earlier versions that honor
      # the "Sleep" config boolean.
      $dbusSend --system --print-reply          \
         --dest=org.freedesktop.NetworkManager  \
         /org/freedesktop/NetworkManager        \
         org.freedesktop.NetworkManager.Sleep boolean:false
      rc=$?
      if [ $rc = 0 ]; then
         return $rc
      fi
      # NetworkManager 0.9.0
      $dbusSend --system --print-reply          \
         --dest=org.freedesktop.NetworkManager  \
         /org/freedesktop/NetworkManager        \
         org.freedesktop.NetworkManager.Enable boolean:true
      rc=$?
      if [ $rc = 0 ]; then
         return $rc
      fi
      # NetworkManager 0.6
      $dbusSend --system --print-reply          \
         --dest=org.freedesktop.NetworkManager  \
         /org/freedesktop/NetworkManager        \
         org.freedesktop.NetworkManager.wake
      rc=$?
   fi
   return $rc
}


#
# confidence_check --
#
#    Check if the script has all the commands it needs to carry out the
#    request. So far, it requires either ip or ifconfig command to read
#    interface configuration. Ifup is not checked here. It is checked at
#    the place where we need to do individual interface state change.
#

confidence_check()
{
   ip_cmd=$(which ip 2>/dev/null)
   ifconfig_cmd=$(which ifconfig 2>/dev/null)
   ifup_cmd=$(which ifup 2>/dev/null)
   ifdown_cmd=$(which ifdown 2>/dev/null)

   [ -z "$ifconfig_cmd" -a -z "$ip_cmd" ] && \
       Panic "ip and ifconfig not in search path."
}


#
# main --
#
#    Main entry point.  Perform some confidence checking, then map state change
#    events to relevant networking operations.
#
# Results:
#    See comment at top of file.
#

main() {
   exitCode=0
   activeList=/var/run/vmware-active-nics

   case "$1" in
      poweron-vm)
         rm -f $activeList
         ;;
      suspend-vm)
         TranquilizeNetworkManager
         exitCode=$?
         if [ $exitCode != 0 ]; then
            confidence_check suspend-vm
            save_active_NIC_list
            run_network_script stop
            exitCode=$?
         fi
         ;;
      resume-vm)
         WakeNetworkManager
         exitCode=$?
         if [ $exitCode != 0 ]; then
            confidence_check resume-vm
            # According to hfu, "/etc/init.d/networking restart" on Debian 5.0
            # may bring down ethernet interfaces tagged as "allow-hotplug" without
            # bringing them back up.
            #
            # This is especially a problem when reverting to a live, running
            # VM snapshot where an active NIC list hadn't yet been generated,
            # resulting in sudden loss of an otherwise operational NIC.
            #
            # So, if the active list doesn't exist, assume we're coming back to
            # a live snapshot and capture the current active list now for
            # rescue later.
            if [ ! -s $activeList ]; then
               save_active_NIC_list
            fi

            # We shall use start not restart here. Otherwise we may not be able
            # to bring back active list on distros like sles11sp2
            # -- PR 816791
            run_network_script start
            rescue_NIC
            exitCode=$?
         fi
         ;;
      *)
         log "No argument supplied."
         ;;
   esac

   return $exitCode
}

main "$@"
log "Finished '$0 $*'"
  070701000002CD000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/scripts/solaris    070701000002CE000081A40000000000000000000000016822550500000B2C000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/scripts/solaris/network    #!/bin/sh
##########################################################
# Copyright (C) 2006-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

##########################################################################
# DO NOT modify this file directly as it will be overwritten the next
# time the VMware Tools are installed.
##########################################################################

#
# network (Solaris 10+)
#
# Solaris's ifconfig conveniently handles DHCP arguments directly, so we
# may use it to release and renew DHCP leases upon receipt of suspend
# and resume events, respectively.
#


echo `date` ": Executing '$0'"
echo


#
# main --
#
#    Script entry point.
#
# Results:
#
# Side effects:
#

main() {
   activeList=/var/run/vmware-active-nics
   exitCode=0

   case "$1" in
      poweron-vm)
         rm -f $activeList
         ;;
      suspend-vm)
         >$activeList

         # Release DHCP addresses and note each interface in our active list
         # so it can be brought back up on resume
         for nic in `ifconfig -a | awk -F: '/DHCP/ { print $1; }'`; do
            # Sometimes interfaces will claim DHCP and not actually be "under
            # DHCP control".  Let's double check the status to ensure this
            # isn't the case.
            if ifconfig "$nic" dhcp status > /dev/null 2>&1; then
               echo "$0: releasing DHCP address for $nic"
               echo "$nic" >> $activeList
               ifconfig "$nic" dhcp release
            fi
         done
      ;;
      resume-vm)
         if [ -s $activeList ]; then
            while read nic; do
               echo "$0: bringing up DHCP on $nic"
               ifconfig "$nic" dhcp
               # Can safely ignore the error from the previous command if we
               # know the interface is managed by the DHCP agent and is working
               # properly.
               rc=$? && [[ $rc != 0 ]] && { ifconfig "$nic" dhcp ping; rc=$?; }
               exitCode=`expr $exitCode \| $rc`
            done < $activeList
         fi
      ;;
      *)
      ;;
   esac

   return $exitCode
}

main "$@"
070701000002CF000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/services   070701000002D0000081A400000000000000000000000168225505000003B0000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/services/Makefile.am   ################################################################################
### Copyright (C) 2009-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =
SUBDIRS += vmtoolsd
SUBDIRS += plugins

070701000002D1000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins   070701000002D2000081A4000000000000000000000001682255050000087E000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/Makefile.am   ################################################################################
### Copyright (c) 2009-2022 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =
if LINUX
   SUBDIRS += appInfo
   SUBDIRS += gdp
   SUBDIRS += guestStore
   SUBDIRS += componentMgr
endif
if ENABLE_CONTAINERINFO
   SUBDIRS += containerInfo
endif
if ENABLE_SDMP
   SUBDIRS += serviceDiscovery
endif
if HAVE_X11
   SUBDIRS += desktopEvents
endif
if HAVE_GTKMM
   SUBDIRS += dndcp
endif
SUBDIRS += guestInfo
SUBDIRS += hgfsServer
SUBDIRS += powerOps
if ENABLE_DEPLOYPKG
   SUBDIRS += deployPkg
endif
if HAVE_X11
   SUBDIRS += resolutionSet
endif
if ENABLE_RESOLUTIONKMS
if !HAVE_X11
# resolutionKMS needs resolutionCommon, which lives in
# the resolutionSet sub directory
   SUBDIRS += resolutionSet
endif
SUBDIRS += resolutionKMS
endif
SUBDIRS += timeSync
SUBDIRS += vix
SUBDIRS += vmbackup


#
# plugin_LTLIBRARIES causes both .la and .so files to be installed to the
# plugin directories. Clean up the .la files and keep just the shared
# libraries around. Sometimes, even though we are passing "-shared" to
# libtool, .a files are also generated, so clean up those too.
#

STATICLIBS = *.a
LTLIBS = *.la

install-exec-local:
	rm -f $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR)/$(STATICLIBS)
	rm -f $(DESTDIR)$(VMSVC_PLUGIN_INSTALLDIR)/$(LTLIBS)
	rm -f $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR)/$(STATICLIBS)
	rm -f $(DESTDIR)$(VMUSR_PLUGIN_INSTALLDIR)/$(LTLIBS)

  070701000002D3000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/appInfo   070701000002D4000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/appInfo/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002D5000081A40000000000000000000000016822550500000572000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/appInfo/Makefile.am   ################################################################################
### Copyright (c) 2019,2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libappInfo.la

libappInfo_la_CPPFLAGS =
libappInfo_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libappInfo_la_LDFLAGS =
libappInfo_la_LDFLAGS += @PLUGIN_LDFLAGS@

libappInfo_la_LIBADD =
libappInfo_la_LIBADD += @VMTOOLS_LIBS@
libappInfo_la_LIBADD += @GOBJECT_LIBS@

libappInfo_la_SOURCES =
libappInfo_la_SOURCES += appInfo.c
libappInfo_la_SOURCES += appInfoInt.h
libappInfo_la_SOURCES += appInfoUtil.c
libappInfo_la_SOURCES += appInfoPosix.c
  070701000002D6000081A40000000000000000000000016822550500005940000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/appInfo/appInfo.c /*********************************************************
 * Copyright (C) 2019-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * appInfo.c --
 *
 *      Captures the information about running applications inside the guest
 *      and publishes it to a guest variable.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "appInfoInt.h"
#include "vmware.h"
#include "codeset.h"
#include "conf.h"
#include "dynbuf.h"
#include "escape.h"
#include "str.h"
#include "util.h"
#include "vm_atomic.h"
#include "vmcheck.h"
#include "vmware/guestrpc/appInfo.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/log.h"
#include "vmware/tools/threadPool.h"
#include "vmware/tools/utils.h"

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif

/**
 * Maximum size of the packet size that appInfo plugin should send
 * to the VMX. Currently, this is set to 62 KB.
 */
#define MAX_APP_INFO_SIZE (62 * 1024)

/**
 * Default poll interval for appInfo is 6 hours
 */
#define APP_INFO_POLL_INTERVAL (360 * 60)

/**
 * Default value for CONFNAME_APPINFO_DISABLED setting in
 * tools configuration file.
 *
 * FALSE will activate the plugin. TRUE will deactivate the plugin.
 */
#define APP_INFO_CONF_DEFAULT_DEACTIVATED_VALUE FALSE

/**
 * Default value for CONFNAME_APPINFO_REMOVE_DUPLICATES setting in
 * tools configuration file.
 *
 * TRUE will remove duplicate applications.
 */
#define APP_INFO_CONF_DEFAULT_REMOVE_DUPLICATES TRUE

/**
 * Default value for CONFNAME_APPINFO_USE_WMI setting in
 * tools configuration file.
 *
 * TRUE will force the plugin to use WMI for getting
 * the application version information.
 */
#define APP_INFO_CONF_USE_WMI_DEFAULT_VALUE    FALSE

/**
 * Defines the current poll interval (in seconds).
 *
 * This value is controlled by the appinfo.poll-interval config file option.
 */
static guint gAppInfoPollInterval = 0;

/**
 * Defines the state of the App Info at the host side.
 */
static gboolean gAppInfoEnabledInHost = TRUE;

/**
 * AppInfo gather loop timeout source.
 */
static GSource *gAppInfoTimeoutSource = NULL;

static void TweakGatherLoop(ToolsAppCtx *ctx, gboolean force);


/*
 *****************************************************************************
 * SetGuestInfo --
 *
 * Sends a simple key-value update request to the VMX.
 *
 * @param[in] ctx       Application context.
 * @param[in] key       Key sent to the VMX
 * @param[in] value     GuestInfo data sent to the VMX
 *
 * @retval TRUE  RPCI succeeded.
 * @retval FALSE RPCI failed.
 *
 *****************************************************************************
 */

static Bool
SetGuestInfo(ToolsAppCtx *ctx,              // IN:
             const char *guestVariableName, // IN:
             const char *value)             // IN:
{
   Bool status;
   char *reply = NULL;
   gchar *msg;
   size_t replyLen;

   ASSERT(guestVariableName);
   ASSERT(value);

   msg = g_strdup_printf("info-set guestinfo.%s %s",
                         guestVariableName,
                         value);

   status = RpcChannel_Send(ctx->rpc,
                            msg,
                            strlen(msg) + 1,
                            &reply,
                            &replyLen);
   g_free(msg);

   if (!status) {
      g_warning("%s: Error sending RPC message: %s\n", __FUNCTION__,
                reply ? reply : "NULL");
      vm_free(reply);
      return FALSE;
   } else {
      g_info("%s: Successfully sent the app information.\n", __FUNCTION__);
   }

   status = (*reply == '\0');
   vm_free(reply);
   return status;
}


/*
 *----------------------------------------------------------------------
 * AppInfo_GetAppList --
 *
 * Generates the application information list.
 *
 * @param[in] config   Tools configuration dictionary.
 *
 * @retval Pointer to the newly allocated application list. The caller must
 *         free the memory using AppInfoDestroyAppList function.
 *         NULL if any error occurs.
 *
 *----------------------------------------------------------------------
 */

GSList *
AppInfo_GetAppList(GKeyFile *config)     // IN
{
   GSList *appList = NULL;
   int i;
   ProcMgrProcInfoArray *procList = NULL;
   size_t procCount;

#ifdef _WIN32
   Bool useWMI;
#endif

   procList = ProcMgr_ListProcesses();

   if (procList == NULL) {
      g_warning("%s: Failed to get the list of processes.\n", __FUNCTION__);
      return appList;
   }

#ifdef _WIN32
   useWMI =  VMTools_ConfigGetBoolean(config,
                                      CONFGROUPNAME_APPINFO,
                                      CONFNAME_APPINFO_USE_WMI,
                                      APP_INFO_CONF_USE_WMI_DEFAULT_VALUE);

   g_debug("%s: useWMI: %d", __FUNCTION__, useWMI);
#endif

   procCount = ProcMgrProcInfoArray_Count(procList);
   for (i = 0; i < procCount; i++) {
      AppInfo *appInfo;
      ProcMgrProcInfo *procInfo = ProcMgrProcInfoArray_AddressOf(procList, i);
#ifdef _WIN32
      appInfo = AppInfo_GetAppInfo(procInfo, useWMI);
#else
      appInfo = AppInfo_GetAppInfo(procInfo);
#endif
      if (NULL != appInfo) {
         appList = g_slist_prepend(appList, appInfo);
      }
   }

   ProcMgr_FreeProcList(procList);

   return appList;
}


/*
 *****************************************************************************
 * AppInfoGatherTask --
 *
 * Collects all the desired application related information and updates VMX.
 *
 * @param[in]  ctx     The application context.
 * @param[in]  data    Unused
 *
 *****************************************************************************
 */

static void
AppInfoGatherTask(ToolsAppCtx *ctx,    // IN
                  gpointer data)       // IN
{
   DynBuf dynBuffer;
   char tmpBuf[1024];
   int len;
   gchar *tstamp = NULL;
   char *escapedCmd = NULL;
   char *escapedVersion = NULL;
   GSList *appList = NULL;
   GSList *appNode;
   static Atomic_uint64 updateCounter = {0};
   uint64 counter = (uint64) Atomic_ReadInc64(&updateCounter) + 1;
   GHashTable *appsAdded = NULL;
   gchar *key = NULL;

   static char headerFmt[] = "{\n"
                     "\"" APP_INFO_KEY_VERSION        "\":\"%d\", \n"
                     "\"" APP_INFO_KEY_UPDATE_COUNTER "\":\"%"FMT64"d\", \n"
                     "\"" APP_INFO_KEY_PUBLISHTIME    "\":\"%s\", \n"
                     "\"" APP_INFO_KEY_APPS           "\":[";
   static char jsonPerAppFmt[] =
                           "%s\n" // , for all the elements after the first one
                           "{"
                           "\"" APP_INFO_KEY_APP_NAME    "\":\"%s\""
                           ","
                           "\"" APP_INFO_KEY_APP_VERSION "\":\"%s\""
                           "}";
   static char jsonSuffix[] = "]}";
   gboolean removeDup =
      VMTools_ConfigGetBoolean(ctx->config,
                               CONFGROUPNAME_APPINFO,
                               CONFNAME_APPINFO_REMOVE_DUPLICATES,
                               APP_INFO_CONF_DEFAULT_REMOVE_DUPLICATES);

   DynBuf_Init(&dynBuffer);

   tstamp = VMTools_GetTimeAsString();

   len = Str_Snprintf(tmpBuf, sizeof tmpBuf, headerFmt,
                      APP_INFO_VERSION_1,
                      counter,
                      tstamp != NULL ? tstamp : "");

   if (len < 0) {
      g_warning("%s: Insufficient space for the header.\n", __FUNCTION__);
      goto quit;
   }

   DynBuf_Append(&dynBuffer, tmpBuf, len);

   appList = AppInfo_SortAppList(AppInfo_GetAppList(ctx->config));
   if (removeDup) {
      appsAdded = g_hash_table_new_full(g_str_hash, g_str_equal,
                                        g_free, NULL);
   }

   for (appNode = appList; appNode != NULL; appNode = appNode->next) {
      size_t currentBufferSize = DynBuf_GetSize(&dynBuffer);
      AppInfo *appInfo = (AppInfo *) appNode->data;

      if (appInfo->appName == NULL ||
          appInfo->version == NULL) {
         goto next_entry;
      }

      if (removeDup) {
         key = g_strdup_printf("%s|%s", appInfo->appName, appInfo->version);
         /*
          * If the key already exists, then this app is a duplicate. Free
          * the key and move to the next application.
          */
         if (g_hash_table_contains(appsAdded, key)) {
            goto next_entry;
         }
      }
      escapedCmd = CodeSet_JsonEscape(appInfo->appName);

      if (NULL == escapedCmd) {
         g_warning("%s: Failed to escape the content of cmdName.\n",
                   __FUNCTION__);
         goto quit;
      }

      escapedVersion = CodeSet_JsonEscape(appInfo->version);
      if (NULL == escapedVersion) {
         g_warning("%s: Failed to escape the content of version information.\n",
                   __FUNCTION__);
         goto quit;
      }

      if (appNode == appList) {
         len = Str_Snprintf(tmpBuf, sizeof tmpBuf, jsonPerAppFmt,
                            "", escapedCmd, escapedVersion);
      } else {
         /*
          * If this is not the first element, then add ',' at the beginning.
          */
         len = Str_Snprintf(tmpBuf, sizeof tmpBuf, jsonPerAppFmt,
                            ",", escapedCmd, escapedVersion);
      }

      if (len < 0) {
         g_warning("%s: Insufficient space for the application information.\n",
                   __FUNCTION__);
         goto next_entry;
      }

      if (currentBufferSize + len + sizeof jsonSuffix > MAX_APP_INFO_SIZE) {
         g_warning("%s: Exceeded the max info packet size."
                   " Truncating the rest of the applications.\n",
                   __FUNCTION__);
         break;
      }

      DynBuf_Append(&dynBuffer, tmpBuf, len);
      if (removeDup) {
         g_hash_table_add(appsAdded, key);
         key = NULL;
      }

next_entry:
      g_free(key);
      key = NULL;
      free(escapedCmd);
      escapedCmd = NULL;
      free(escapedVersion);
      escapedVersion = NULL;
   }

   DynBuf_Append(&dynBuffer, jsonSuffix, sizeof jsonSuffix - 1);
   SetGuestInfo(ctx, APP_INFO_GUESTVAR_KEY, DynBuf_GetString(&dynBuffer));

quit:
   free(escapedCmd);
   free(escapedVersion);
   AppInfo_DestroyAppList(appList);
   if (appsAdded != NULL) {
      g_hash_table_destroy(appsAdded);
   }
   g_free(key);
   g_free(tstamp);
   DynBuf_Destroy(&dynBuffer);
}


/*
 *****************************************************************************
 * AppInfoGather --
 *
 * Creates a new thread that collects all the desired application related
 * information and updates the VMX. Tweaks the poll gather loop as per the
 * tools configuration after creating the thread.
 *
 * @param[in]  data     The application context.
 *
 * @return G_SOURCE_REMOVE to indicate that the timer should be removed.
 *
 *****************************************************************************
 */

static gboolean
AppInfoGather(gpointer data)      // IN
{
   ToolsAppCtx *ctx = data;

   g_debug("%s: Submitting a task to capture application information.\n",
           __FUNCTION__);

   if (!ToolsCorePool_SubmitTask(ctx, AppInfoGatherTask, NULL, NULL)) {
      g_warning("%s: Failed to submit the task for capturing application "
                "information\n", __FUNCTION__);
   }

   TweakGatherLoop(ctx, TRUE);

   return G_SOURCE_REMOVE;
}


/*
 *****************************************************************************
 * TweakGatherLoopEx --
 *
 * Start, stop, reconfigure a AppInfo Gather poll loop.
 *
 * This function is responsible for creating, manipulating, and resetting a
 * AppInfo Gather loop timeout source. The poll loop will be deactivated if
 * the poll interval is 0.
 *
 * @param[in]     ctx           The application context.
 * @param[in]     pollInterval  Poll interval in seconds. A value of 0 will
 *                              deactivate the loop.
 *
 *****************************************************************************
 */

static void
TweakGatherLoopEx(ToolsAppCtx *ctx,       // IN
                  guint pollInterval)     // IN
{
   if (gAppInfoTimeoutSource != NULL) {
      /*
       * Destroy the existing timeout source.
       */
      g_source_destroy(gAppInfoTimeoutSource);
      gAppInfoTimeoutSource = NULL;
   }

   if (pollInterval > 0) {
      if (gAppInfoPollInterval != pollInterval) {
         g_info("%s: New value for %s is %us.\n",
                __FUNCTION__,
                CONFNAME_APPINFO_POLLINTERVAL,
                pollInterval);
      }

      gAppInfoTimeoutSource = g_timeout_source_new(pollInterval * 1000);
      VMTOOLSAPP_ATTACH_SOURCE(ctx, gAppInfoTimeoutSource,
                               AppInfoGather, ctx, NULL);
      g_source_unref(gAppInfoTimeoutSource);
   } else if (gAppInfoPollInterval > 0) {
      g_info("%s: Poll loop for %s deactivated.\n",
             __FUNCTION__, CONFNAME_APPINFO_POLLINTERVAL);
      SetGuestInfo(ctx, APP_INFO_GUESTVAR_KEY, "");
   }

   gAppInfoPollInterval = pollInterval;
}


/*
 *****************************************************************************
 * TweakGatherLoop --
 *
 * Configues the AppInfo Gather poll loop based on the settings in the
 * tools configuration.
 *
 * This function is responsible for creating, manipulating, and resetting a
 * AppInfo Gather loop timeout source.
 *
 * @param[in]     ctx           The application context.
 * @param[in]     force         If set to TRUE, the poll loop will be
 *                              tweaked even if the poll interval hasn't
 *                              changed from the previous value.
 *
 *****************************************************************************
 */

static void
TweakGatherLoop(ToolsAppCtx *ctx,  // IN
                gboolean force)    // IN
{
   gboolean deactivated =
      VMTools_ConfigGetBoolean(ctx->config,
                               CONFGROUPNAME_APPINFO,
                               CONFNAME_APPINFO_DISABLED,
                               APP_INFO_CONF_DEFAULT_DEACTIVATED_VALUE);

   gint pollInterval;

   if (gAppInfoEnabledInHost && !deactivated) {
      pollInterval = VMTools_ConfigGetInteger(ctx->config,
                                              CONFGROUPNAME_APPINFO,
                                              CONFNAME_APPINFO_POLLINTERVAL,
                                              APP_INFO_POLL_INTERVAL);

      if (pollInterval < 0 || pollInterval > (G_MAXINT / 1000)) {
         g_warning("%s: Invalid poll interval %d. Using default %us.\n",
                   __FUNCTION__, pollInterval, APP_INFO_POLL_INTERVAL);
         pollInterval = APP_INFO_POLL_INTERVAL;
      }
   } else {
      pollInterval = 0;
   }

   if (force || (gAppInfoPollInterval != pollInterval)) {
      /*
       * pollInterval can never be a negative value. Typecasting into
       * guint should not be a problem.
       */
      TweakGatherLoopEx(ctx, (guint) pollInterval);
   }
}


/*
 *****************************************************************************
 * AppInfoServerConfReload --
 *
 * Reconfigures the poll loop interval upon config file reload.
 *
 * @param[in]  src     The source object.
 * @param[in]  ctx     The application context.
 * @param[in]  data    Unused.
 *
 *****************************************************************************
 */

static void
AppInfoServerConfReload(gpointer src,       // IN
                        ToolsAppCtx *ctx,   // IN
                        gpointer data)      // IN
{
   g_info("%s: Reloading the tools configuration.\n", __FUNCTION__);

   TweakGatherLoop(ctx, FALSE);
}


/*
 *****************************************************************************
 * AppInfoServerShutdown --
 *
 * Cleanup internal data on shutdown.
 *
 * @param[in]  src     The source object.
 * @param[in]  ctx     Application context.
 * @param[in]  data    Unused.
 *
 *****************************************************************************
 */

static void
AppInfoServerShutdown(gpointer src,          // IN
                      ToolsAppCtx *ctx,      // IN
                      gpointer data)         // IN
{
   if (gAppInfoTimeoutSource != NULL) {
      g_source_destroy(gAppInfoTimeoutSource);
      gAppInfoTimeoutSource = NULL;
   }

   SetGuestInfo(ctx, APP_INFO_GUESTVAR_KEY, "");
}


/*
 *----------------------------------------------------------------------------
 *
 * AppInfoServerSetOption --
 *
 * Handle TOOLSOPTION_ENABLE_APPINFO Set_Option callback.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  option   Option being set.
 * @param[in]  value    Option value.
 * @param[in]  plugin   Plugin registration data.
 *
 * @return  TRUE  if the specified option is TOOLSOPTION_ENABLE_APPINFO and
 *                the AppInfo Gather poll loop is reconfigured.
 *          FALSE if the specified option is not TOOLSOPTION_ENABLE_APPINFO
 *                or AppInfo Gather poll loop is not reconfigured.
 *----------------------------------------------------------------------------
 */

static gboolean
AppInfoServerSetOption(gpointer src,         // IN
                       ToolsAppCtx *ctx,     // IN
                       const gchar *option,  // IN
                       const gchar *value,   // IN
                       gpointer data)        // IN
{
   gboolean retVal = FALSE;

   if (strcmp(option, TOOLSOPTION_ENABLE_APPINFO) == 0) {
      g_debug("%s: Tools set option %s=%s.\n",
              __FUNCTION__, TOOLSOPTION_ENABLE_APPINFO, value);

      if (strcmp(value, "1") == 0 && !gAppInfoEnabledInHost) {
         gAppInfoEnabledInHost = TRUE;
         retVal = TRUE;
      } else if (strcmp(value, "0") == 0 && gAppInfoEnabledInHost) {
         gAppInfoEnabledInHost = FALSE;
         retVal = TRUE;
      }

      if (retVal) {
         g_info("%s: State of AppInfo is changed to '%s' at host side.\n",
                __FUNCTION__, gAppInfoEnabledInHost ? "enabled" : "deactivated" );

         TweakGatherLoop(ctx, TRUE);
      }
   }

   return retVal;
}


/*
 ******************************************************************************
 * AppInfoServerReset --
 *
 * Callback function that gets called whenever the RPC channel gets reset.
 * Disables the poll loop and sets a one time poll.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Application context.
 * @param[in]  data     Unused.
 *
 ******************************************************************************
 */

static void
AppInfoServerReset(gpointer src,
                   ToolsAppCtx *ctx,
                   gpointer data)
{
   /*
    * gAppInfoTimeoutSource is used to figure out if the poll loop is
    * enabled or not. If the poll loop is deactivated, then
    * gAppInfoTimeoutSource will be set to NULL.
    */
   if (gAppInfoTimeoutSource != NULL) {
      guint interval;

      ASSERT(gAppInfoPollInterval != 0);

#define MIN_APPINFO_INTERVAL 30

      if (gAppInfoPollInterval > MIN_APPINFO_INTERVAL) {
         GRand *gRand = g_rand_new();

         /*
          * The RPC channel may get reset due to various conditions like
          * snapshotting the VM, vmotion the VM, instant cloning of the VM.
          * In order to avoid potential load spikes in case of instant clones,
          * randomize the poll interval after a channel reset.
          */

         interval = g_rand_int_range(gRand,
                                     MIN_APPINFO_INTERVAL,
                                     gAppInfoPollInterval);
         g_rand_free(gRand);
      } else {
         interval = gAppInfoPollInterval;
      }

#undef MIN_APPINFO_INTERVAL

      g_info("%s: Using poll interval: %u.\n", __FUNCTION__, interval);

      TweakGatherLoopEx(ctx, interval);
   } else {
      /*
       * Channel got reset. VM might have vMotioned to an older host
       * that doesn't send the 'Set_Option enableAppInfo'.
       * Set gAppInfoEnabledInHost to TRUE and tweak the gather loop.
       * Else, the application information may never be captured.
       */
      if (!gAppInfoEnabledInHost) {
         gAppInfoEnabledInHost = TRUE;
         TweakGatherLoop(ctx, TRUE);
      } else {
         g_debug("%s: Poll loop deactivated. Ignoring.\n", __FUNCTION__);
      }
   }
}


/*
 *****************************************************************************
 * ToolsOnLoad --
 *
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in]  ctx   The app context.
 *
 * @return The registration data.
 *
 *****************************************************************************
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)    // IN
{
   static ToolsPluginData regData = {
      "appInfo",
      NULL,
      NULL
   };

   /*
    * Return NULL to deactivate the plugin if not running in a VMware VM.
    */
   if (!ctx->isVMware) {
      g_info("%s: Not running in a VMware VM.\n", __FUNCTION__);
      return NULL;
   }

   /*
    * Return NULL to deactivate the plugin if not running in vmsvc daemon.
    */
   if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
      g_info("%s: Not running in vmsvc daemon: container name='%s'.\n",
             __FUNCTION__, ctx->name);
      return NULL;
   }

   /*
    * This plugin is useless without an RpcChannel.  If we don't have one,
    * just bail.
    */
   if (ctx->rpc != NULL) {
      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_CONF_RELOAD, AppInfoServerConfReload, NULL },
         { TOOLS_CORE_SIG_SHUTDOWN, AppInfoServerShutdown, NULL },
         { TOOLS_CORE_SIG_RESET, AppInfoServerReset, NULL },
         { TOOLS_CORE_SIG_SET_OPTION, AppInfoServerSetOption, NULL }
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_SIGNALS,
           VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs))
         }
      };

      regData.regs = VMTools_WrapArray(regs,
                                       sizeof *regs,
                                       ARRAYSIZE(regs));

      /*
       * Set up the AppInfo gather loop.
       */
      TweakGatherLoop(ctx, TRUE);

      return &regData;
   }

   return NULL;
}
070701000002D7000081A400000000000000000000000168225505000007A6000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/appInfo/appInfoInt.h  /*********************************************************
 * Copyright (C) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _APPINFOINT_H_
#define _APPINFOINT_H_

/**
 * @file appInfoInt.h
 *
 * Header file with few functions that are internal to appInfo plugin.
 */

#define G_LOG_DOMAIN "appInfo"
#include "vm_basic_types.h"
#include <glib.h>
#include "vmware/tools/plugin.h"
#include "procMgr.h"

#if defined(_WIN32)

#include <windows.h>
typedef DWORD AppInfo_Pid;

#else /* POSIX */

#include <sys/types.h>
typedef pid_t AppInfo_Pid;

#endif

/*
 * Application information structure.
 * This holds basic information we return per process
 * when listing process information inside the guest.
 */

typedef struct AppInfo {
   AppInfo_Pid procId;
   char *appName;             // UTF-8
   char *version;
#if defined(_WIN32)
   size_t memoryUsed;
#endif
} AppInfo;

GSList *AppInfo_GetAppList(GKeyFile *config);
GSList *AppInfo_SortAppList(GSList *appList);

void AppInfo_DestroyAppList(GSList *appList);

#if defined(WIN32)
AppInfo *AppInfo_GetAppInfo(ProcMgrProcInfo *procInfo, Bool useWMI);
#else
AppInfo *AppInfo_GetAppInfo(ProcMgrProcInfo *procInfo);
#endif

#endif /* _APPINFOINT_H_ */
  070701000002D8000081A40000000000000000000000016822550500000800000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/appInfo/appInfoPosix.c    /*********************************************************
 * Copyright (C) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * appInfoPosix.c --
 *
 *      Functions to capture the information about running applications inside
 *      a Linux guest.
 *
 */

#ifndef __linux__
#   error This file should not be compiled.
#endif

#include "appInfoInt.h"
#include "util.h"


/*
 *----------------------------------------------------------------------
 * AppInfo_GetAppInfo --
 *
 * Retrieves the application information for a specified process.
 *
 * @param[in] procInfo   Information about a process.
 *
 * @retval The application information. The caller must free the memory
 *         allocated for the application information.
 *         NULL if any error happens.
 *
 *----------------------------------------------------------------------
 */

AppInfo *
AppInfo_GetAppInfo(ProcMgrProcInfo *procInfo)        // IN
{
   AppInfo *appInfo = NULL;

   if (procInfo == NULL) {
      return appInfo;
   }

   if (procInfo->procCmdName == NULL) {
      return appInfo;
   }

   appInfo = Util_SafeMalloc(sizeof (*appInfo) * 1);
   appInfo->procId = procInfo->procId;
   appInfo->appName = Util_SafeStrdup(procInfo->procCmdName);
   appInfo->version = Util_SafeStrdup("");

   return appInfo;
}
070701000002D9000081A40000000000000000000000016822550500000F26000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/appInfo/appInfoUtil.c /*********************************************************
 * Copyright (C) 2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * appInfoUtil.c --
 *
 *      Utility functions for the application list information.
 */


#include <stdio.h>
#include <stdlib.h>
#include "appInfoInt.h"


/*
 ******************************************************************************
 * AppInfoDestroyAppData --
 *
 * Free function for application data. This function is called by the glib
 * for each element in the application list while freeing.
 *
 * @param[in] data      Pointer to the application data.
 *
 * @retval NONE
 *
 ******************************************************************************
 */

void
AppInfoDestroyAppData(gpointer data) {
   AppInfo *appInfo = (AppInfo *) data;

   if (appInfo == NULL) {
      return;
   }

   free(appInfo->appName);
   free(appInfo->version);
   free(appInfo);
}


/*
 ******************************************************************************
 * AppInfoDestroyAppList --
 *
 * Frees the entire memory allocated for the application list.
 *
 * @param[in] appList      Pointer to the application list.
 *
 * @retval NONE
 *
 ******************************************************************************
 */

void
AppInfo_DestroyAppList(GSList *appList) {
   if (appList == NULL) {
      return;
   }

   g_slist_free_full(appList, AppInfoDestroyAppData);
}


/*
 ******************************************************************************
 * AppInfoCompareApps --
 *
 * Compare function used by glib while sorting the application list.
 * For windows guests, the memory used by the application is used for comparing.
 * For linux guests, no comparison is done and hence 0 is returned.
 *
 * @param[in] a      Pointer to the first application that should be compared.
 * @param[in] b      Pointer to the second application that should be compared.
 *
 * @retval -1 if a should be kept before b in the sorted list.
 *          0 if both the elements are same.
 *          1 if b should be kept before a in the sorted list.
 *
 ******************************************************************************
 */

static gint
AppInfoCompareApps(gconstpointer a,
                   gconstpointer b) {
#if defined(_WIN32)
   if (a != NULL && b != NULL) {
      size_t aMemory = ((AppInfo *) a)->memoryUsed;
      size_t bMemory = ((AppInfo *) b)->memoryUsed;
      if (aMemory < bMemory) {
         return 1;
      } else if (aMemory == bMemory) {
         return 0;
      } else {
         return -1;
      }
   }
   return 0;
#else
   return 0;
#endif
}


/*
 ******************************************************************************
 * AppInfo_SortAppList --
 *
 * Sorts the provided list of applications.
 *
 * @param[in] appList List of applications that need to be sorted.
 *
 * @retval The sorted list of applications.
 *
 ******************************************************************************
 */

GSList *
AppInfo_SortAppList(GSList *appList) // IN/OUT
{
   if (appList == NULL) {
      return appList;
   }

   return g_slist_sort(appList, AppInfoCompareApps);
}  070701000002DA000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr  070701000002DB000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002DC000081A40000000000000000000000016822550500000749000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr/Makefile.am  ################################################################################
### Copyright (c) 2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libcomponentMgr.la

libcomponentMgr_la_CPPFLAGS =
libcomponentMgr_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libcomponentMgr_la_CPPFLAGS += -DVMTOOLS_COMPONENTMGR_PATH=\"$(pkglibdir)/componentMgr/\"

libcomponentMgr_la_LDFLAGS =
libcomponentMgr_la_LDFLAGS += @PLUGIN_LDFLAGS@

libcomponentMgr_la_LIBADD =
libcomponentMgr_la_LIBADD += @VMTOOLS_LIBS@
libcomponentMgr_la_LIBADD += @GOBJECT_LIBS@

libcomponentMgr_la_SOURCES =
libcomponentMgr_la_SOURCES += componentMgr.c
libcomponentMgr_la_SOURCES += componentMgrPlugin.h
libcomponentMgr_la_SOURCES += componentMgrUtil.c
libcomponentMgr_la_SOURCES += componentMgrInstallAction.c
libcomponentMgr_la_SOURCES += componentMgrInstallManager.c

install-data-local:
if ENABLE_SALTMINION
	$(INSTALL) -d $(DESTDIR)/$(pkglibdir)/componentMgr/saltMinion/
	$(INSTALL) -m 700 $(srcdir)/svtminion.sh \
                          $(DESTDIR)/$(pkglibdir)/componentMgr/saltMinion/
endif
   070701000002DD000081A40000000000000000000000016822550500002E34000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr/componentMgr.c   /*********************************************************
 * Copyright (c) 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * componentMgr.c --
 *
 *      Adds/removes components in the guest OS. Periodically polls
 *      for all the components managed by the plugin and fetches the guestVar
 *      guestinfo./vmware.components.<comp_name>.desiredstate for present or
 *      absent action and accordingly adds/removes the component from the
 *      system.
 *      All actions on a component runs as an async process.
 */

#include "componentMgrPlugin.h"
#include "str.h"
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);


/*
 * componentMgr plugin poll interval timeout source.
 */
static GSource *gComponentMgrTimeoutSource = NULL;

/*
 * Tools application context.
 */
static ToolsAppCtx *gCtx;

/*
 * componentMgr plugin poll interval.
 */
static guint gComponentMgrPollInterval = 0;

static gboolean ComponentMgrCb(gpointer data);


/*
 *****************************************************************************
 * ComponentMgr_GetToolsAppCtx --
 *
 * A getter function to fetch the tools application context in all points of
 * the plugin.
 *
 * @return
 *      Main ToolsAppCtx of the plugin.
 *
 *****************************************************************************
 */

ToolsAppCtx*
ComponentMgr_GetToolsAppCtx()
{
   return gCtx;
}


/*
 *****************************************************************************
 * ReconfigureComponentMgrPollLoopEx --
 *
 * Start, stop and reconfigure componentMgr plugin poll loop.
 * This function is responsible for creating, handling, and resetting the
 * componentMgr loop timeout source.
 *
 * @param[in] ctx Tools application context.
 * @param[in] pollInterval Poll interval in seconds.
 *
 * @return
 *      None
 *
 * Side effects:
 *      Deletes the existing timeout source and recreates a new one.
 *
 *****************************************************************************
 */

static void
ReconfigureComponentMgrPollLoopEx(ToolsAppCtx *ctx,  // IN
                                  gint pollInterval) // IN
{
   if (gComponentMgrPollInterval == pollInterval) {
      g_debug("%s: ComponentMgr poll interval has not been changed since"
              " last time.\n", __FUNCTION__);
      return;
   }

   if (gComponentMgrTimeoutSource != NULL) {
      /*
       * Destroy the existing timeout source.
       */
      g_source_destroy(gComponentMgrTimeoutSource);
      gComponentMgrTimeoutSource = NULL;
   }

   if (pollInterval != 0) {
      if (pollInterval < COMPONENTMGR_MIN_POLL_INTERVAL ||
         pollInterval > (G_MAXINT / 1000)) {
         g_warning("%s: Invalid poll interval. Using default %us.\n",
                   __FUNCTION__, COMPONENTMGR_DEFAULT_POLL_INTERVAL);
         pollInterval = COMPONENTMGR_DEFAULT_POLL_INTERVAL;
      }

      g_info("%s: New value for %s is %us.\n", __FUNCTION__,
             COMPONENTMGR_CONF_POLLINTERVAL,
             pollInterval);

      gComponentMgrTimeoutSource = g_timeout_source_new(pollInterval * 1000);
      VMTOOLSAPP_ATTACH_SOURCE(ctx, gComponentMgrTimeoutSource,
                               ComponentMgrCb, ctx, NULL);
      g_source_unref(gComponentMgrTimeoutSource);
   } else {
      /*
       * Plugin will be disabled since poll interval configured is 0.
       * No components will be managed. Publish guestVar available.
       */
      g_info("%s: ComponentMgr plugin disabled.\n", __FUNCTION__);
      ComponentMgr_PublishAvailableComponents(ctx,
                                              COMPONENTMGR_NONECOMPONENTS);
   }

   gComponentMgrPollInterval = pollInterval;
}


/*
 *****************************************************************************
 * ComponentMgrCb --
 *
 * This function updates the component status managed by the plugin.
 * This function internally calls present/absent actions on the respective
 * components.
 *
 * @param[in] data Tools application context.
 *
 * @return
 *      G_SOURCE_CONTINUE To continue polling.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

static gboolean
ComponentMgrCb(gpointer data) // IN
{
   ToolsAppCtx *ctx = data;

   if (ComponentMgr_CheckAnyAsyncProcessRunning()) {
      g_debug("%s: A component has an async process running. Skipping "
              "component status update.\n", __FUNCTION__);
      return G_SOURCE_CONTINUE;
   }

   /*
    * Update the enabled components managed by the plugin and publish
    * guestVar for all the available components.
    */
   ComponentMgr_UpdateComponentEnableStatus(ctx);

   /*
    * Main function which handles the core logic of taking actions present or
    * absent on the components by reading action from the component guestVars.
    */
   ComponentMgr_UpdateComponentStatus(ctx);

   return G_SOURCE_CONTINUE;
}


/*
 *****************************************************************************
 * ComponentMgrPollLoop --
 *
 * This function reads the poll interval and included configurations from the
 * config file and reconfigures the poll loop of the plugin.
 *
 * @param[in] data Tools application context.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

static void
ComponentMgrPollLoop(ToolsAppCtx *ctx) // IN
{
   gint pollInterval;
   gchar *listString;

   pollInterval = VMTools_ConfigGetInteger(ctx->config,
                                           COMPONENTMGR_CONF_GROUPNAME,
                                           COMPONENTMGR_CONF_POLLINTERVAL,
                                           COMPONENTMGR_DEFAULT_POLL_INTERVAL);

   listString = VMTools_ConfigGetString(ctx->config,
                                        COMPONENTMGR_CONF_GROUPNAME,
                                        COMPONENTMGR_CONF_INCLUDEDCOMPONENTS,
                                        COMPONENTMGR_ALLCOMPONENTS);

   /*
    * If the included conf value is not present or is empty, no components will
    * be enabled and plugin will be disabled until further change.
    */
   if (listString == NULL || *listString == '\0' ||
       Str_Strcmp(listString, COMPONENTMGR_NONECOMPONENTS) == 0) {
      g_info("%s: No components managed by the plugin. Plugin disabled."
              " Set value included in configuration.\n",  __FUNCTION__);
      pollInterval = 0;
   }

   g_free(listString);
   ReconfigureComponentMgrPollLoopEx(ctx, pollInterval);
}


/*
 ******************************************************************************
 * ComponentMgrServerShutdown --
 *
 * This function cleans up plugin internal data on shutdown.
 *
 * @param[in] src The source object.
 * @param[in] ctx Tools application context.
 * @param[in] data Unused.
 *
 * @return
 *      None
 *
 * Side effects:
 *      Destroys all timeout sources for all the components.
 *      Destroys all running async process for all the components.
 *
 ******************************************************************************
 */

static void
ComponentMgrServerShutdown(gpointer src,     // IN
                           ToolsAppCtx *ctx, // IN
                           gpointer data)    // IN
{
   if (gComponentMgrTimeoutSource != NULL) {
      /*
       * Destroy the existing timeout source.
       */
      g_source_destroy(gComponentMgrTimeoutSource);
      gComponentMgrTimeoutSource = NULL;
   }

   // Destroy all GSource timers for all managed components.
   ComponentMgr_Destroytimers();

   // Destroy and free all running async process for all components.
   ComponentMgr_DestroyAsyncProcess();
}


/*
 ******************************************************************************
 * ComponentMgrServerConfReload --
 *
 * This function reconfigures the poll loop interval upon config file reload.
 *
 * @param[in] src The source object.
 * @param[in] ctx Tools application context.
 * @param[in] data Unused.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      None.
 *
 ******************************************************************************
 */

static void
ComponentMgrServerConfReload(gpointer src,     // IN
                             ToolsAppCtx *ctx, // IN
                             gpointer data)    // IN
{
   ComponentMgrPollLoop(ctx);
}


/*
 ******************************************************************************
 * ComponentMgrServerReset --
 *
 * Callback function that gets called whenever the RPC channel gets reset.
 *
 * @param[in] src The source object.
 * @param[in] ctx Tools application context.
 * @param[in] data Unused.
 *
 * @return
 *     None.
 *
 * Side effects:
 *     Reinitializes the plugin timeout source.
 *
 ******************************************************************************
 */

static void
ComponentMgrServerReset(gpointer src,     // IN
                        ToolsAppCtx *ctx, // IN
                        gpointer data)    // IN
{
   /*
    * Handle reset for componentMgr loop.
    */
   if (gComponentMgrTimeoutSource != NULL) {
      ASSERT(gComponentMgrPollInterval != 0);

      ReconfigureComponentMgrPollLoopEx(ctx, gComponentMgrPollInterval);
   } else {
      ComponentMgrPollLoop(ctx);
   }
}

/*
 *****************************************************************************
 * ToolsOnLoad --
 *
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in] ctx Tools application context.
 *
 * @return
 *      Plugin registration data.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx) // IN
{
   static ToolsPluginData regData = {
      "componentMgr",
      NULL,
      NULL
   };

   /*
    * Return NULL to disable the plugin if not running in a VMware VM.
    */
   if (!ctx->isVMware) {
      g_info("%s: Not running in a VMware VM.\n", __FUNCTION__);
      return NULL;
   }

   /*
    * Return NULL to disable the plugin if not running in vmsvc daemon.
    */
   if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
      g_info("%s: Not running in vmsvc daemon: container name='%s'.\n",
             __FUNCTION__, ctx->name);
      return NULL;
   }

   gCtx = ctx;

   /*
    * This plugin is useless without an RpcChannel.  If we don't have one,
    * just bail.
    */
   if (ctx->rpc != NULL) {
      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_CONF_RELOAD, ComponentMgrServerConfReload, NULL },
         { TOOLS_CORE_SIG_RESET, ComponentMgrServerReset, NULL },
         { TOOLS_CORE_SIG_SHUTDOWN, ComponentMgrServerShutdown, NULL }
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_SIGNALS,
           VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs))
         }
      };

      regData.regs = VMTools_WrapArray(regs,
                                       sizeof *regs,
                                       ARRAYSIZE(regs));

      return &regData;
   }

   return NULL;
}
070701000002DE000081A4000000000000000000000001682255050000A043000000000000000000000000000000000000005D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr/componentMgrInstallAction.c  /*********************************************************
 * Copyright (c) 2021-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * componentMgrInstallAction.c --
 *
 * Functions to manage the known and enabled components by the componentMgr.
 * Consists of functions periodically handling adding/removing of
 * components in the guest OS. Periodically read the guestVar:
 * "guestinfo./vmware.components.<comp_name>.desiredstate" and take present or
 * absent action on the components.
 * Adding/removing a component managed by the plugin is performed
 * asynchronously using Proc_Manager API's.
 *
 */


#include "componentMgrPlugin.h"
#include "str.h"
#include "file.h"
#include "util.h"
#include "guestApp.h"
#include "codeset.h"


/*
 * Structure to store information about the scripts to be invoked for
 * present/absent action on the components by the plugin.
 * The script to be invoked shall be predefined with default arguments
 * in the structure according to actions present/absent/checkstatus.
 */

typedef struct ComponentAction {
   const char *componentName;              /* The name of the enabled
                                            * component.
                                            */

   const char *scriptName;                 /* The default script to be invoked
                                            * to take actions for a particular
                                            * component
                                            */

   const char *addActionArguments;         /* Default arguments to the script
                                            * to execute present action towards
                                            * the component in the guest OS.
                                            */

   const char *removeActionArguments;      /* Default arguments to the script
                                            * to execute absent action towards
                                            * the component in the guest OS.
                                            */

   const char *checkStatusActionArguments; /* Default arguments to the script
                                            * to execute checkstatus towards
                                            * the component in the guest OS.
                                            */

   const char* mandatoryParameters;        /* Arguments that are mandatory to
                                            * be passed to script.
                                            */

   const char *componentDirectory;         /* The name of directory in which
                                            * scripts will be installed.
                                            */

   char* (*customizeRemoveAction)();       /* A custom callback function
                                            * to customize arguments for
                                            * absent action on the component
                                            * script.
                                            */

   char* (*customizeAddAction)();          /* A custom callback function
                                            * to customize arguments for
                                            * present action on the component
                                            * script.
                                            */
} ComponentAction;


static char*
ComponentMgrCustomizeSaltAddAction();


/*
 * An array to store all the state information of the component over the
 * life time for the plugin. The array consists of cached values of enabled
 * status, async process info, GSource timers, status count down counter and
 * current action to be run on the component.
 */

static struct ComponentInfo components[] = {
   {SALT_MINION, TRUE, NOTINSTALLED, NULL, NULL, COMPONENTMGR_CHECK_STATUS_COUNT_DOWN, INVALIDACTION}
};

/*
 * An array containing information of the component and its related
 * scripts and respective default arguments for actions present/absent
 * or checkstatus.
 */
#if defined(_WIN32)
static const char powershellExecutable[] = "\\WindowsPowerShell\\v1.0\\PowerShell.exe";

static const char componentMgrExecutionPolicy[] = "-ExecutionPolicy RemoteSigned -File";

static ComponentAction executionScripts[] = {
   {SALT_MINION,"svtminion.ps1", "-Install", "-Remove", "-Status", "-Loglevel debug", "saltMinion", NULL, &ComponentMgrCustomizeSaltAddAction}
};
#else
static ComponentAction executionScripts[] = {
   {SALT_MINION,"svtminion.sh", "--install", "--remove", "--status", "--loglevel debug", "saltMinion", NULL, &ComponentMgrCustomizeSaltAddAction}
};
#endif


/*
 *****************************************************************************
 * ComponentMgr_GetComponentName --
 *
 * This function fetches the name of component from the components array.
 *
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 *
 * @return
 *      Name of component.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

const char*
ComponentMgr_GetComponentName(int componentIndex) // IN
{
   return components[componentIndex].name;
}


/*
 *****************************************************************************
 * ComponentMgr_CheckAnyAsyncProcessRunning --
 *
 * This function checks if any async process is running for any component
 * inside the plugin.
 *
 * @return
 *      FALSE if no async process is running for a component.
 *      TRUE if an async process is running for a component.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

gboolean
ComponentMgr_CheckAnyAsyncProcessRunning() // IN
{
   int i;
   for (i = 0; i < ARRAYSIZE(components); i++) {
      if (ComponentMgr_IsAsyncProcessRunning(i)) {
         return TRUE;
      }
   }
   return FALSE;
}


/*
 *****************************************************************************
 * ComponentMgr_IsAsyncProcessRunning --
 *
 * This function indicates whether any async process is already running for
 * a component.
 *
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 *
 * @return
 *      FALSE if no async process is running for a component.
 *      TRUE if an async process is running for a component.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

gboolean
ComponentMgr_IsAsyncProcessRunning(int componentIndex) // IN
{
   if (components[componentIndex].procInfo != NULL) {
      g_info("%s: Component %s has an async process still running.\n",
             __FUNCTION__, components[componentIndex].name);
      return TRUE;
   } else {
      return FALSE;
   }
}


/*
 *****************************************************************************
 * ComponentMgr_SetComponentAsyncProcInfo --
 *
 * This function caches info of the async process currently running for a
 * component.
 *
 * @param[in] asyncProcInfo  An asyncProcInfo object of the currently running
 *                           async process.
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

void
ComponentMgr_SetComponentAsyncProcInfo(AsyncProcessInfo *asyncProcInfo, // IN
                                       int componentIndex)              // IN
{
   ASSERT(components[componentIndex].procInfo == NULL);
   components[componentIndex].procInfo = asyncProcInfo;
}


/*
 *****************************************************************************
 * ComponentMgr_ResetComponentAsyncProcInfo --
 *
 * This function resets the state of any async process running for a component.
 *
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

void
ComponentMgr_ResetComponentAsyncProcInfo(int componentIndex) // IN
{
   components[componentIndex].procInfo = NULL;
}


/*
 *****************************************************************************
 * ComponentMgr_SetComponentGSourceTimer --
 *
 * This function caches the GSource timer running for an async process for a
 * component. The timer is used to monitor the state of async process.
 *
 * @param[in] componentTimer A GSource timer created when performing present
 *                           or absent action on a component.
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      Sets the GSource timer of an async process running for a component.
 *
 *****************************************************************************
 */

void
ComponentMgr_SetComponentGSourceTimer(GSource *componentTimer, // IN
                                      int componentIndex)      // IN
{
   ASSERT(components[componentIndex].sourceTimer == NULL);
   components[componentIndex].sourceTimer = componentTimer;
}


/*
 *****************************************************************************
 * ComponentMgr_ResetComponentGSourceTimer --
 *
 * This function resets the component GSource timer to make way for creation
 * a new async process.
 *
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 * @return
 *      None.
 *
 * Side effects:
 *      Reset the GSource timer of an async process running for a component.
 *
 *****************************************************************************
 */

void
ComponentMgr_ResetComponentGSourceTimer(int componentIndex) // IN
{
   components[componentIndex].sourceTimer = NULL;
}


/*
 *****************************************************************************
 * ComponentMgrGetScriptFullPath --
 *
 * This function returns a full path to the component script based on the
 * installed path of open-vm-tools or VMware Tools.
 *
 * @param[in] scriptName Name of the component script.
 * @param[in] componentDir Directory of the component.
 *
 * @return
 *      A full path to the script file based on open-vm-tools or VMware Tools
 *      installation.
 *
 * Side effects:
 *      Caller needs to free the full script path value.
 *
 *****************************************************************************
 */

static gchar*
ComponentMgrGetScriptFullPath(const char *scriptName,   // IN
                              const char *componentDir) // IN
{
   gchar *scriptInstallDir;
   gchar *toolsInstallDir;

#if defined(OPEN_VM_TOOLS)
   toolsInstallDir = Util_SafeStrdup(VMTOOLS_COMPONENTMGR_PATH);
   scriptInstallDir = g_strdup_printf("%s%s%s%s", toolsInstallDir,
                                      componentDir, DIRSEPS, scriptName);
#else
   toolsInstallDir = GuestApp_GetInstallPath();
   scriptInstallDir = g_strdup_printf("%s%s%s%s%s%s%s", toolsInstallDir, DIRSEPS,
                                      COMPONENTMGR_DIRECTORY, DIRSEPS,
                                      componentDir, DIRSEPS, scriptName);
#endif

   g_free(toolsInstallDir);
   return scriptInstallDir;
}


/*
 *****************************************************************************
 * ComponentMgrCustomizeSaltAddAction --
 *
 * This function customizes the custom arguments for the present action for the
 * salt component script.
 *
 * @return
 *      A customized argument string for the salt component script.
 *
 * Side effects:
 *      Caller has to free the returned custom arguments.
 *
 *****************************************************************************
 */

static char*
ComponentMgrCustomizeSaltAddAction()
{
   size_t replylen;
   gchar *msg;
   gboolean status;
   char *actionArguments = NULL;

   msg = g_strdup_printf("%s.%s.args", COMPONENTMGR_ACTION, SALT_MINION);
   status = ComponentMgr_SendRpc(ComponentMgr_GetToolsAppCtx(), msg,
                                 &actionArguments, &replylen);
   g_free(msg);

   if (!status) {
      vm_free(actionArguments);
      return NULL;
   }

   return actionArguments;
}


/*
 *****************************************************************************
 * ComponentMgrConstructCommandline --
 *
 * The function constructs the commandline for linux and windows to execute
 * the script as an async process to perform present/absent action on a
 * component.
 *
 * The windows counterpart is constructed as:
 * <path to powershell.exe> -ExecutionPolicy RemoteSigned -File \
 * <path to component script> <args to component script>
 *
 * The linux counterpart is constructed as:
 * <path to component script> <arguments to the script>
 *
 * @param[in] scriptName Name of the component script.
 * @param[in] defaultArguments Default arguments to the component script.
 * @param[in] mandatoryParams mandatory params to the component script.
 * @param[in] customizeAction A callback function to customize the arguments
 *                            for the component script.
 *
 * @return
 *      A commandline to be directly run as an async process.
 *
 * Side effects:
 *      Caller needs to free the commandline.
 *
 *****************************************************************************
 */

static char *
ComponentMgrConstructCommandline(gchar *scriptPath,            // IN
                                 const char *defaultArguments, // IN
                                 const char *mandatoryParams,  // IN
                                 char* (*customizeAction)())   // IN
{
   char *commandline = NULL;
   const char *mandatoryParamsExists = NULL;
   char *customArguments = NULL;
#if defined(_WIN32)
   WCHAR sysDirW[MAX_PATH];
   UINT retLen;
   char *sysDir = NULL;
#endif

   // Customize the arguments for the specific action via the callback function
   if (customizeAction != NULL) {
      g_info("%s: Customizing arguments with function.\n", __FUNCTION__);
      customArguments = customizeAction();
   }

#if defined(_WIN32)
   retLen = GetSystemDirectoryW(sysDirW, _countof(sysDirW));
   if (retLen == 0 || retLen >= _countof(sysDirW)) {
      g_warning("%s: Unable to get system directory.\n", __FUNCTION__);
      goto proceedexit;
   }

   if (!CodeSet_Utf16leToUtf8((const char *)sysDirW,
                              wcslen(sysDirW) * sizeof sysDirW[0],
                              &sysDir,
                              NULL)) {
      g_warning("%s: Could not convert system directory to UTF-8.\n",
                __FUNCTION__);
      goto proceedexit;
   }

   if (customArguments != NULL) {
      mandatoryParamsExists = strstr(customArguments, mandatoryParams);
      if (mandatoryParamsExists == NULL) {
         commandline = Str_SafeAsprintf(NULL, "\"%s%s\" %s \"%s\" %s %s %s",
                                        sysDir, powershellExecutable,
                                        componentMgrExecutionPolicy,
                                        scriptPath,
                                        defaultArguments, customArguments,
                                        mandatoryParams);
      } else {
         commandline = Str_SafeAsprintf(NULL, "\"%s%s\" %s \"%s\" %s %s",
                                        sysDir, powershellExecutable,
                                        componentMgrExecutionPolicy,
                                        scriptPath,
                                        defaultArguments, customArguments);

      }
   } else {
      commandline = Str_SafeAsprintf(NULL, "\"%s%s\" %s \"%s\" %s %s",
                                     sysDir, powershellExecutable,
                                     componentMgrExecutionPolicy,
                                     scriptPath,
                                     defaultArguments,
                                     mandatoryParams);
   }

   free(sysDir);
#else
   if (customArguments != NULL) {
      mandatoryParamsExists = strstr(customArguments, mandatoryParams);
      if (mandatoryParamsExists == NULL) {
         commandline = Str_SafeAsprintf(NULL, "%s %s %s %s", scriptPath,
                                        defaultArguments, customArguments,
                                        mandatoryParams);
      } else {
         commandline = Str_SafeAsprintf(NULL, "%s %s %s", scriptPath,
                                        defaultArguments, customArguments);

      }
   } else {
      commandline = Str_SafeAsprintf(NULL, "%s %s %s", scriptPath,
                                     defaultArguments, mandatoryParams);
   }
   // To satisfy a complier warning of missing label.
   goto proceedexit;
#endif

proceedexit:
   if (customArguments != NULL) {
      vm_free(customArguments);
   }
   return commandline;
}


/*
 *****************************************************************************
 * ComponentMgr_CheckStatusCommandLine --
 *
 * This function fetches the commandline needed to be executed to check the
 * current status of component installation. It provides the commandline as
 * <component_script> <checkstatus_arguments>
 *
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 *
 * @return
 *      Commandline to check current status of component.
 *
 * Side effects:
 *      Caller needs to free the commandline value.
 *
 *****************************************************************************
 */

char *
ComponentMgr_CheckStatusCommandLine(int componentIndex) // IN
{
   char *commandline;
   gchar *scriptFullPath;

   /*
    * Always check for component enabled state before proceeding, since check
    * status can be called at any part of component action.
    */
   if (!components[componentIndex].isEnabled) {
      g_info("%s: Component %s is disabled.\n", __FUNCTION__,
             components[componentIndex].name);
      return NULL;
   }

   scriptFullPath = ComponentMgrGetScriptFullPath(executionScripts[componentIndex].scriptName,
                                                  executionScripts[componentIndex].componentDirectory);
   if (!File_Exists(scriptFullPath)) {
      g_info("%s: Script file for component %s does not exist at path %s.\n",
             __FUNCTION__, components[componentIndex].name, scriptFullPath);
      return NULL;
   }

   commandline = ComponentMgrConstructCommandline(scriptFullPath,
                                                  executionScripts[componentIndex].checkStatusActionArguments,
                                                  executionScripts[componentIndex].mandatoryParameters,
                                                  NULL);

   g_free(scriptFullPath);

   return commandline;
}


/*
 *****************************************************************************
 * ComponentMgrSetEnabledComponentInfo --
 *
 * This function sets the isEnabled state of the component which determines
 * whether the component managed by the plugin is enabled or disabled.
 *
 * @param[in] componentName Name of the component to enable or disable.
 * @param[in] enabled A boolean value indicating component enabled status.
 *
 * @return
 *      None
 * Side effects:
 *      Sets the enabled/disabled status of a component.
 *
 *****************************************************************************
 */

static void
ComponentMgrSetEnabledComponentInfo(const char *componentName, // IN
                                    gboolean enabled)          // IN
{
   int i;
   gboolean componentFound = FALSE;

   for (i = 0; i < ARRAYSIZE(components); i++) {
      if (Str_Strcmp(components[i].name, componentName) == 0) {
         components[i].isEnabled = enabled;
         componentFound = TRUE;
         break;
      }
   }

   if (!componentFound) {
      g_info("%s: Invalid component name %s.\n",
             __FUNCTION__, componentName);
   }
}


/*
 *****************************************************************************
 * ComponentMgr_SetStatusComponentInfo --
 *
 * This function sets the status of the component managed by the plugin and
 * publishes guestVar guestinfo.vmware.components.<comp_name>.laststatus.
 *
 * @param[in] ctx Tools application context.
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 * @param[in] exitCode The exit code of the async process running check status
 *                     operation on a component.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      Sets the current status for a component.
 *
 *****************************************************************************
 */

void
ComponentMgr_SetStatusComponentInfo(ToolsAppCtx *ctx,   // IN
                                    int exitCode,       // IN
                                    int componentIndex) // IN
{
   gchar *msg;
   gboolean status;

   msg = g_strdup_printf("%s.%s.%s %d", COMPONENTMGR_PUBLISH_COMPONENTS,
                         components[componentIndex].name,
                         COMPONENTMGR_INFOLASTSTATUS,
                         exitCode);

   status = ComponentMgr_SendRpc(ctx, msg, NULL, NULL);
   g_free(msg);
   components[componentIndex].status = exitCode;
}


/*
 *****************************************************************************
 * ComponentMgrSetEnabledAllComponents --
 *
 * This function enables/disables all the components managed by the plugin.
 *
 * @param[in] enabled A boolean value to enable/disable all components.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

static void
ComponentMgrSetEnabledAllComponents(gboolean enabled) // IN
{
   int i;
   for (i = 0; i < ARRAYSIZE(components); i++) {
      components[i].isEnabled = enabled;
   }
}


/*
 *****************************************************************************
 * ComponentMgr_ExecuteComponentAction --
 *
 * This function validates the current status of the component against
 * current action be to be taken for a component and constructs a commandline
 * to execute present/absent action on a component as an async process.
 *
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

void
ComponentMgr_ExecuteComponentAction(int componentIndex) // IN
{
   gchar *scriptFullPath;
   const char *action = NULL;
   const char *defaultArguments = NULL;
   char *commandline = NULL;
   char* (*customizeAction)() = NULL;
   Action installaction = components[componentIndex].action;

   if (!components[componentIndex].isEnabled) {
      g_debug("%s: Component %s is disabled", __FUNCTION__,
              components[componentIndex].name);
      return;
   }

   action = ComponentMgr_GetComponentAction(installaction);
   if((Str_Strcmp(action, COMPONENTMGR_COMPONENTPRESENT) == 0) &&
      (components[componentIndex].status == NOTINSTALLED ||
      components[componentIndex].status == INSTALLFAILED ||
      components[componentIndex].status == REMOVEFAILED)) {
      installaction = PRESENT;
   } else if((Str_Strcmp(action, COMPONENTMGR_COMPONENTABSENT) == 0) &&
             (components[componentIndex].status == INSTALLED ||
             components[componentIndex].status == INSTALLFAILED ||
             components[componentIndex].status == REMOVEFAILED)) {
      installaction = ABSENT;
   } else {
      g_debug("%s: Action %s will not be executed for component %s with "
              "current status %s.\n", __FUNCTION__, action,
              components[componentIndex].name,
              ComponentMgr_GetComponentInstallStatus(components[componentIndex].status));
      return;
   }

   g_info("%s: Executing action %s for component %s current status %s.\n",
          __FUNCTION__, action, components[componentIndex].name,
          ComponentMgr_GetComponentInstallStatus(components[componentIndex].status));

   /*
    * The main logic which handles the present/absent action on a component.
    * Internally spins off async process to add/remove the component
    * on the guest.
    */
   switch(installaction) {
      case PRESENT:
         defaultArguments = executionScripts[componentIndex].addActionArguments;
         customizeAction = executionScripts[componentIndex].customizeAddAction;
         break;
      case ABSENT:
         defaultArguments = executionScripts[componentIndex].removeActionArguments;
         customizeAction = executionScripts[componentIndex].customizeRemoveAction;
         break;
      default:
         break;
   } // end switch
   scriptFullPath = ComponentMgrGetScriptFullPath(executionScripts[componentIndex].scriptName,
                                                  executionScripts[componentIndex].componentDirectory);

   commandline = ComponentMgrConstructCommandline(scriptFullPath,
                                                  defaultArguments,
                                                  executionScripts[componentIndex].mandatoryParameters,
                                                  customizeAction);
    g_free(scriptFullPath);

   if (commandline == NULL) {
      g_info("%s: Construction of command line failed for component %s.\n",
             __FUNCTION__, components[componentIndex].name);
      return;
   }

   g_info("%s: Commandline %s to perform %s action on component %s.\n",
          __FUNCTION__, commandline, action, components[componentIndex].name);
   ComponentMgr_AsynchronousComponentActionStart(ComponentMgr_GetToolsAppCtx(),
                                                 commandline, componentIndex);
   free(commandline);
}


/*
 *****************************************************************************
 * ComponentMgrPublishKnownComponents --
 *
 * This function publishes guestVar guestinfo.vmware.components.available
 * with all the components managed by the plugin.
 *
 * @param[in] ctx Tools application context
 *
 * @return
 *      None.
 *
 * Side effects:
 *      Updates the enabled/disabled status of the all the components managed
 *      by the plugin.
 *
 *****************************************************************************
 */

static void
ComponentMgrPublishKnownComponents(ToolsAppCtx *ctx) // IN
{
   int i;
   DynBuf enabledComponents;
   DynBuf_Init(&enabledComponents);

   for (i = 0; i < ARRAYSIZE(components); i++) {
      if (components[i].isEnabled) {
         gchar *scriptFullPath;
         /*
          * We need to check the existence of the script for a component before
          * we begin the present/absent action on the component.
          * Skipping the component if no script is installed.
          */
         scriptFullPath = ComponentMgrGetScriptFullPath(executionScripts[i].scriptName,
                                                        executionScripts[i].componentDirectory);

         if (!File_Exists(scriptFullPath)) {
            g_debug("%s: Script file for component %s does not exist "
                    "under path %s.\n", __FUNCTION__, components[i].name,
                    scriptFullPath);
            g_free(scriptFullPath);
            components[i].isEnabled = FALSE;
            continue;
         }

         g_free(scriptFullPath);

         if (DynBuf_GetSize(&enabledComponents) != 0) {
            DynBuf_Append(&enabledComponents, ",", 1);
         }
         DynBuf_Append(&enabledComponents, components[i].name,
                       strlen(components[i].name));
      }
   }

   if (DynBuf_GetSize(&enabledComponents) == 0) {
      ComponentMgr_PublishAvailableComponents(ctx, COMPONENTMGR_NONECOMPONENTS);
   } else {
      ComponentMgr_PublishAvailableComponents(ctx,
                                              DynBuf_GetString(&enabledComponents));
   }

   DynBuf_Destroy(&enabledComponents);
}


/*
 *****************************************************************************
 * ComponentMgrIncludedComponents --
 *
 * This function checks and validates the comma separated list fetched from
 * included tools.conf configuration and classifies the first occurrence of
 * all or none which are special values and returns the result.
 *
 * @param[in] componentString Comma separated string from the included
 *                            tools.conf configuration.
 *
 * @retun
 *      Classify first occurrence of all or none in the string and return.
 *
 * Side effects
 *      None.
 *
 *****************************************************************************
 */

static IncludedComponents
ComponentMgrIncludedComponents(const char* componentString) // IN
{
   int i;
   gchar **componentList = NULL;
   IncludedComponents include = NOSPECIALVALUES;

   if (componentString == NULL || *componentString == '\0') {
      g_info("%s: No components included in the ComponentMgr plugin.\n",
             __FUNCTION__);
      return NONECOMPONENTS;
   }

   componentList = g_strsplit(componentString, ",", 0);
   for (i = 0; componentList[i] != NULL; i++ ) {
      g_strstrip(componentList[i]);

      if (strcmp(componentList[i], COMPONENTMGR_ALLCOMPONENTS) == 0) {
         include = ALLCOMPONENTS;
         break;
      }
      if (strcmp(componentList[i], COMPONENTMGR_NONECOMPONENTS) == 0) {
         include = NONECOMPONENTS;
         break;
      }
   }

   g_strfreev(componentList);
   return include;
}


/*
 *****************************************************************************
 * ComponentMgr_UpdateComponentEnableStatus --
 *
 * This functions reads the comma separated list of components in the included
 * tools.conf configuration and sets the enabled/disabled status for all the
 * components managed by the plugin.
 * It also publishes guestvar guestinfo.vmware.components.available with
 * info of all the components managed by the plugin.
 *
 * @param[in] ctx Tools application context.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      Updates the enabled/disabled status of the all the components managed
 *      by the plugin.
 *
 *****************************************************************************
 */

void
ComponentMgr_UpdateComponentEnableStatus(ToolsAppCtx *ctx) // IN
{
   gchar *listString;
   IncludedComponents included;
   char *token;
   char *context = NULL;

   listString = VMTools_ConfigGetString(ctx->config,
                                        COMPONENTMGR_CONF_GROUPNAME,
                                        COMPONENTMGR_CONF_INCLUDEDCOMPONENTS,
                                        COMPONENTMGR_ALLCOMPONENTS);

   included = ComponentMgrIncludedComponents(listString);
   switch (included) {
      case ALLCOMPONENTS:
         ComponentMgrSetEnabledAllComponents(TRUE);
         goto publishComponents;
      case NONECOMPONENTS:
         ComponentMgrSetEnabledAllComponents(FALSE);
         goto publishComponents;
      default:
         break;
   }

   /*
    * Setting all components to disabled state.
    */
   ComponentMgrSetEnabledAllComponents(FALSE);

   /*
    * Split the comma separated list of included components and individually
    * set the status of each component as TRUE.
    */
   token = strtok_r(listString, ",", &context);
   while (token != NULL) {
      ComponentMgrSetEnabledComponentInfo(token, TRUE);
      token = strtok_r(NULL, ",", &context);
   }

publishComponents:
   g_free(listString);
   ComponentMgrPublishKnownComponents(ctx);
}


/*
 *****************************************************************************
 * ComponentMgrCheckExecuteComponentAction --
 *
 * This function validates the current status of the component against
 * current action for the component and waits for status update counter
 * to reach zero to run a check status operation if the component status
 * and component action are not compliant.
 * If the component action and component status are compliant, it spins off
 * an async check status operation.
 *
 * @param[in] ctx Tools application context.
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 * @param[in] action The action that shall be performed on a component.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      Run an asynchronous process to check current status of the component.
 *
 *****************************************************************************
 */

static void
ComponentMgrCheckExecuteComponentAction(ToolsAppCtx *ctx,   // IN
                                        int componentIndex, // IN
                                        const char *action) // IN
{
   char* commandline;
   void (*callbackFunction)(int) = &ComponentMgr_ExecuteComponentAction;
   Action installaction = INVALIDACTION;

   /*
    * It is possible at this stage, an async process for checkstatus or
    * present/absent action may be running for the component. In such a scenario
    * the plugin shall not trigger any other async process.
    */
   ASSERT(components[componentIndex].isEnabled);
   ASSERT(!ComponentMgr_IsAsyncProcessRunning(componentIndex));

   commandline = ComponentMgr_CheckStatusCommandLine(componentIndex);
   if (commandline == NULL) {
      g_info("%s: Unable to construct commandline instruction to run check "
             "status for the component %s\n", __FUNCTION__,
             components[componentIndex].name);
      return;
   }

   /*
    * Add the component to the guest only if it is NOTINSTALLED,
    * INSTALLFAILED or REMOVEFAILED.
    * Remove the component on the guest only if it is INSTALLED,
    * INSTALLFAILED or REMOVEFAILED.
    */
   if((Str_Strcmp(action, COMPONENTMGR_COMPONENTPRESENT) == 0) &&
      (components[componentIndex].status == NOTINSTALLED ||
      components[componentIndex].status == INSTALLFAILED ||
      components[componentIndex].status == REMOVEFAILED)) {
      installaction = PRESENT;
   } else if((Str_Strcmp(action, COMPONENTMGR_COMPONENTABSENT) == 0) &&
             (components[componentIndex].status == INSTALLED ||
             components[componentIndex].status == INSTALLFAILED ||
             components[componentIndex].status == REMOVEFAILED)) {
      installaction = ABSENT;
   } else {
      components[componentIndex].statuscount -= 1;
      if (components[componentIndex].statuscount != 0) {
         /*
          * Status count down for the component has not reached 0
          * come back again in next interval.
          */
         g_debug("%s: Status count down for component %s is %d.\n",
                 __FUNCTION__, components[componentIndex].name,
                 components[componentIndex].statuscount);
         free(commandline);
         return;
      } else {
         /*
          * Status count down has reached 0. We need to call the async
          * check status once and update the last status of the component.
          * Set the callback function for the async check status call NULL,
          * since it's a single check status operation.
          */
         callbackFunction = NULL;
      }
   }

   /*
    * Resetting the value of status count of a component, since the action
    * might have changed or status count down has reached 0.
    */
   components[componentIndex].action = installaction;
   components[componentIndex].statuscount = COMPONENTMGR_CHECK_STATUS_COUNT_DOWN;

   /*
    * Before invoking any action for a component, we need to check the current
    * status for that component. We run the pre configured script with pre
    * configured check status arguments to the script.
    * An async process will be spun off to perform check status of a component
    * with an option of sequenced operation after check status call.
    */
   g_debug("%s: Checking current status of component %s with commandline %s.\n",
           __FUNCTION__, components[componentIndex].name, commandline);
   ComponentMgr_AsynchronousComponentCheckStatus(ctx, commandline,
                                                 componentIndex,
                                                 callbackFunction);
   free(commandline);
}


/*
 *****************************************************************************
 * ComponentMgr_DestroyAsyncProcess --
 *
 * Destroy and free any or all async process running for a component.
 *
 * @return
 *      None.
 *
 * Side  effects:
 *       Kills the async process running any action for a component instantly.
 *
 *****************************************************************************
 */

void
ComponentMgr_DestroyAsyncProcess()
{
   int i;

   for (i = 0; i < ARRAYSIZE(components); i++) {
      if (components[i].procInfo != NULL) {
         g_debug("%s: Destroying running async process for component %s.\n",
                 __FUNCTION__, components[i].name);
         ComponentMgr_FreeAsyncProc(components[i].procInfo);
      } else {
         g_debug("%s: No async process running for component %s.\n",
                 __FUNCTION__, components[i].name);
      }
   }
}


/*
 *****************************************************************************
 * ComponentMgr_Destroytimers --
 *
 * This function destroys the GSource timers for all components.
 *
 * @return
 *      None.
 *
 * Side  effects:
 *       Destroys the timeout GSource timers for all the components.
 *
 *****************************************************************************
 */

void
ComponentMgr_Destroytimers(void)
{
   int i;

   for (i = 0; i < ARRAYSIZE(components); i++) {
      if (components[i].sourceTimer != NULL) {
         g_debug("%s: Destroying timers for component %s.\n", __FUNCTION__,
                 components[i].name);
         g_source_destroy(components[i].sourceTimer);
         components[i].sourceTimer = NULL;
      } else {
         g_debug("%s: Source timers for component %s has already been "
                 "destroyed.\n", __FUNCTION__, components[i].name);
      }
   }
}


/*
 *****************************************************************************
 * ComponentMgr_UpdateComponentStatus --
 *
 * This function loops through all the enabled components in the plugin and
 * fetches the action for individual components from the guestVar
 * guestinfo./vmware.components.<comp_name>.desiredstate and triggers
 * check status and execute action for a component.
 *
 * @param[in] ctx Tools application context.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

void
ComponentMgr_UpdateComponentStatus(ToolsAppCtx *ctx) // IN
{
   int i;

   for (i = 0; i < ARRAYSIZE(components); i++) {
      gboolean status;
      char *componentDesiredState = NULL;
      size_t replylen;
      gchar *msg;

      /*
       * Proceed only if the component script is installed and
       * the component is enabled by the plugin.
       */
      if (!components[i].isEnabled) {
         continue;
      }

      msg = g_strdup_printf("%s.%s.%s", COMPONENTMGR_ACTION,
                            components[i].name,
                            COMPONENTMGR_INFODESIREDSTATE);
      /*
       * Fetch the action for a component from the guestVar
       * guestinfo./vmware.components.<comp_name>.desiredstate
       */
      status = ComponentMgr_SendRpc(ctx, msg, &componentDesiredState, &replylen);
      g_free(msg);

      if (!status) {
         g_info("%s: Install action not available for component %s.\n",
                __FUNCTION__, components[i].name);
         vm_free(componentDesiredState);
         componentDesiredState = NULL;
         continue;
      }

      if (componentDesiredState != NULL &&
         (Str_Strcmp(componentDesiredState, COMPONENTMGR_COMPONENTPRESENT) == 0 ||
         Str_Strcmp(componentDesiredState, COMPONENTMGR_COMPONENTABSENT) == 0)) {
         ComponentMgrCheckExecuteComponentAction(ctx, i, componentDesiredState);
      }

      vm_free(componentDesiredState);
      componentDesiredState = NULL;
   }
}
 070701000002DF000081A40000000000000000000000016822550500004B80000000000000000000000000000000000000005E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr/componentMgrInstallManager.c /*********************************************************
 * Copyright (c) 2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * componentMgrInstallManager.c --
 *
 * This file contains all the necessary functions and handling of performing
 * check status operation and add/remove of a component.
 * The operations are triggered as an async process and GSource timers are
 * created to monitor the execution status of the async process.
 * After successful completion of the async process, it's resources are
 * released to make way for a new async process.
 * Contains functions related to creation of new async process, monitoring of
 * an async process and freeing of a async process resources.
 *
 */

#include "componentMgrPlugin.h"


/*
 *****************************************************************************
 * ComponentMgr_FreeAsyncProc --
 *
 * This function frees the async process resources.
 * First we kill the commandline process which is child's child by pid.
 * Then we kill the child process which is vmtoolsd.
 *
 * @param[in] asyncProcessInfo Asynchronous process resources to be freed.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      Destroys and frees the async process resources.
 *
 *****************************************************************************
 */

void
ComponentMgr_FreeAsyncProc(AsyncProcessInfo *procInfo) // IN
{
   int componentIndex = procInfo->componentIndex;
#if defined(__linux__)
   if (ProcMgr_IsAsyncProcRunning(procInfo->asyncProc)) {
      ProcMgr_Pid procPid = ProcMgr_GetPid(procInfo->asyncProc);
      ProcMgr_KillByPid(procPid);
   }
#endif
   ProcMgr_Kill(procInfo->asyncProc);
   ProcMgr_Free(procInfo->asyncProc);
   g_free(procInfo);

   // Reset the async process info in the components array since the async
   // process is no longer available.
   ComponentMgr_ResetComponentAsyncProcInfo(componentIndex);
}


/*
 *****************************************************************************
 * ComponentMgrCheckStatusMonitor --
 *
 * This function monitors the state the async process running check status
 * command for a component. On completion of async process, the exit code
 * is captured and set in the components structure for that component.
 * On expiry of timer the async process will be killed.
 *
 * @param[in] data asyncProcessInfo pointer containing async process and
                   component info.
 *
 * @return
 *      G_SOURCE_CONTINUE To continue polling.
 *      G_SOURCE_REMOVE to stop polling.
 *
 * Side effects:
 *      Monitor the asyncProcess for its successful termination by polling.
 *      Kill the asyncProcess if it's taking long to terminate.
 *
 *****************************************************************************
 */

static gboolean
ComponentMgrCheckStatusMonitor(void *data) // IN
{
   ProcMgr_Pid procPid;
   int componentIndex;
   const char *componentName;
   void (*callbackFunction)(int compIndex) = NULL;

   AsyncProcessInfo *procInfo = (AsyncProcessInfo*)data;
   ASSERT(procInfo->asyncProc != NULL);

   /*
    * For every timeout callback from the timeout source, decrease
    * the remaining execution time for the component.
    */
   procInfo->backoffTimer -= COMPONENTMGR_ASYNC_CHECK_STATUS_POLL_INTERVAL;
   procPid = ProcMgr_GetPid(procInfo->asyncProc);
   componentIndex = procInfo->componentIndex;
   componentName = ComponentMgr_GetComponentName(componentIndex);

   g_debug("%s: Callback received for process ID %d and component %s."
           " Remaining time before termination %ds.\n", __FUNCTION__, procPid,
           componentName, procInfo->backoffTimer);

   if (!ProcMgr_IsAsyncProcRunning(procInfo->asyncProc)) {
      int exitCode = -1;
#if defined(__linux__)
      if (ProcMgr_GetExitCode(procInfo->asyncProc, &exitCode) || exitCode == -1) {
         exitCode = SCRIPTFAILED;
      }
#else
      if (ProcMgr_GetExitCode(procInfo->asyncProc, &exitCode)) {
         exitCode = SCRIPTFAILED;
      }
#endif
     g_debug("%s: Checking status of a component has terminated gracefully"
             " with exit code %d.\n", __FUNCTION__, exitCode);

     ComponentMgr_SetStatusComponentInfo(procInfo->ctx,
                                         exitCode,
                                         procInfo->componentIndex);
     callbackFunction = procInfo->callbackFunction;

     /*
      * At this stage free the asyncProcInfo object to make way for new async
      * process. Source timer for a component will be no longer valid from here
      * set it to NULL for next async process.
      */
     ComponentMgr_FreeAsyncProc(procInfo);
     ComponentMgr_ResetComponentGSourceTimer(componentIndex);

     /*
      * After checkstatus operation has completed successfully, we can have a
      * next sequence of operations to be executed on a component.
      */
     if (callbackFunction != NULL) {
        callbackFunction(componentIndex);
     }

     return G_SOURCE_REMOVE;
   } else {
      /*
       * At this stage the async process seems to be still running check
       * status operation on a component. If the backoff timer value is not
       * reached for a component, we proceed and wait for the async process
       * to terminate. If the backoff timer has reached 0, the timed wait
       * for the component is completed hence we kill the async process.
       * First we kill the command process which is child's child by pid.
       * Then we kill the child process which is vmtoolsd.
       */
      g_debug("%s: Process still running for component %s.\n", __FUNCTION__,
              componentName);

      if (procInfo->backoffTimer == 0) {
         g_warning("%s: Backoff timer expired for process %d running check "
                   "status for component %s. Async process will be killed.",
                   __FUNCTION__, procPid, componentName);

         ComponentMgr_SetStatusComponentInfo(procInfo->ctx,
                                             SCRIPTTERMINATED,
                                             componentIndex);

         /*
          * At this point the async process has timed out, so we need to
          * kill the async process to make way for a new async process.
          * Source timer for a component will be no longer valid from here
          * set it to NULL for the next async process.
          */
         ComponentMgr_FreeAsyncProc(procInfo);
         ComponentMgr_ResetComponentGSourceTimer(componentIndex);
         return G_SOURCE_REMOVE;
      }
   }

   return G_SOURCE_CONTINUE;
}


/*
 *****************************************************************************
 * ComponentMgrProcessMonitor --
 *
 * This function monitors the async process running the present/absent action
 * on a component.
 *
 * @param[in] data asyncProcessInfo pointer containing async process and
                   component info.
 *
 * @return
 *      G_SOURCE_CONTINUE To continue polling.
 *      G_SOURCE_REMOVE To stop polling.
 *
 * Side effects:
 *      Monitor the async process for its successful termination by polling.
 *      Kill the async process if it's taking long to terminate.
 *
 *****************************************************************************
 */

static gboolean
ComponentMgrProcessMonitor(void *data) // IN
{
   ProcMgr_Pid procPid;
   int componentIndex;
   char *commandline;
   const char *componentName;

   AsyncProcessInfo *procInfo = (AsyncProcessInfo*)data;
   ASSERT(procInfo->asyncProc != NULL);

   procInfo->backoffTimer -= COMPONENTMGR_ASYNCPROCESS_POLL_INTERVAL;
   procPid = ProcMgr_GetPid(procInfo->asyncProc);
   componentIndex = procInfo->componentIndex;
   componentName = ComponentMgr_GetComponentName(componentIndex);

   g_debug("%s: Callback received for process ID %d and component %s."
           " Remaining time before termination %ds.\n", __FUNCTION__, procPid,
           componentName, procInfo->backoffTimer);

   if (!ProcMgr_IsAsyncProcRunning(procInfo->asyncProc)) {
      /*
       * At this stage the async process has completed its execution.
       * Free all the async process resources and destroy the GSource timer.
       */
      g_debug("%s: Async process has exited.\n", __FUNCTION__);

      ComponentMgr_FreeAsyncProc(procInfo);
      ComponentMgr_ResetComponentGSourceTimer(componentIndex);

      commandline = ComponentMgr_CheckStatusCommandLine(componentIndex);
      if (commandline == NULL) {
         g_info("%s: Unable to construct commandline instruction to run check "
                "status for the component %s\n", __FUNCTION__,
                ComponentMgr_GetComponentName(componentIndex));
         ComponentMgr_SetStatusComponentInfo(ComponentMgr_GetToolsAppCtx(),
                                             SCRIPTTERMINATED, componentIndex);
         return G_SOURCE_REMOVE;
      }

      /*
       * At this stage the async process running present/absent has completed.
       * We need to check the status of a component asynchronously and set the
       * component status.
       */
      ComponentMgr_AsynchronousComponentCheckStatus(ComponentMgr_GetToolsAppCtx(),
                                                    commandline,
                                                    componentIndex,
                                                    NULL);
      free(commandline);
      return G_SOURCE_REMOVE;
   } else {
      /*
       * At this stage the async process seems to be still running for a
       * component. If the backoff timer value has not reached 0,
       * we proceed and wait for the async process to terminate.
       * If the backoff timer has reached 0, the timed wait
       * has reached limit for the component and we kill the async process.
       */
      g_debug("%s: Process still running for component %s.\n", __FUNCTION__,
              componentName);

      if (procInfo->backoffTimer == 0) {
         g_warning("%s: Backoff timer expired for process %d running action for"
                   "component %s. Async process will be killed.",
                   __FUNCTION__, procPid, componentName);

         /*
          * At this point the async process has to be terminated. We can
          * free the structure to make way for newer async process.
          * Source timer for a component will be no longer valid from here
          * set it to NULL for next async process.
          */
         ComponentMgr_FreeAsyncProc(procInfo);
         ComponentMgr_ResetComponentGSourceTimer(componentIndex);

         commandline = ComponentMgr_CheckStatusCommandLine(componentIndex);
         if (commandline == NULL) {
            g_info("%s: Unable to construct commandline instruction to run check "
                   "status for the component %s\n", __FUNCTION__,
                   ComponentMgr_GetComponentName(componentIndex));
            ComponentMgr_SetStatusComponentInfo(ComponentMgr_GetToolsAppCtx(),
                                                SCRIPTTERMINATED, componentIndex);
            return G_SOURCE_REMOVE;
         }

         ComponentMgr_AsynchronousComponentCheckStatus(ComponentMgr_GetToolsAppCtx(),
                                                       commandline,
                                                       componentIndex,
                                                       NULL);

         free(commandline);
         return G_SOURCE_REMOVE;
      }
   }
   /*
    * The async process has not yet completed its action. So poll again
    * using the same GSource timer for that particular component.
    */
   return G_SOURCE_CONTINUE;
}


/*
 *****************************************************************************
 * ComponentMgrCreateAsyncProcessInfo --
 *
 * This function creates the asynProcessInfo object related to an async process
 *
 * @param[in] asyncProc A ProcMgr_AsyncProc pointer containing information
                        about the created async process.
 * @param[in] ctx Tools application context.
 * @param[in] backoffTimer A timer value after expiry of which the async
                           process will be killed.
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 * @param[in] callbackFunction A callback function to sequence the operation
 *                             after async process finishes.
 *
 * @return
 *      An asyncProcessInfo object.
 *
 * Side effects:
 *      The created asyncProcesInfo object should be freed after process
 *      is completed or terminated.
 *
 *****************************************************************************
 */

static AsyncProcessInfo*
ComponentMgrCreateAsyncProcessInfo(ProcMgr_AsyncProc *asyncProc,                 // IN
                                   ToolsAppCtx *ctx,                             // IN
                                   int backoffTimer,                             // IN
                                   int componentIndex,                           // IN
                                   void (*callbackFunction)(int componentIndex)) // IN
{
   AsyncProcessInfo *procInfo;
   procInfo = g_malloc(sizeof *procInfo);
   procInfo->asyncProc = asyncProc;
   procInfo->ctx = ctx;
   procInfo->backoffTimer = backoffTimer;
   procInfo->componentIndex = componentIndex;
   procInfo->callbackFunction = callbackFunction;

   return procInfo;
}


/*
 *****************************************************************************
 * ComponentMgr_AsynchronousComponentCheckStatus --
 *
 * This function launches an async process to check the current status of the
 * component on the system.
 *
 * @param[in] ctx Tools application context.
 * @param[in] commandline <component_script> <action_arguments> command to be
 *                        executed to add/remove the component.
 * @param[in] componentIndex Index of the component in the global array of
 *                           components.
 * @param[in] callbackFunction A callback function to sequence the operation
 *                             after async call completes.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      Async process creation may fail for many reasons.
 *
 *****************************************************************************
 */

void
ComponentMgr_AsynchronousComponentCheckStatus(ToolsAppCtx *ctx,                // IN
                                              const char *commandline,         // IN
                                              int componentIndex,              // IN
                                              void (*callback)(int compIndex)) // IN
{
   ProcMgr_ProcArgs userArgs;
   GSource *sourceTimer;
   AsyncProcessInfo *procInfo;
   ProcMgr_AsyncProc *asyncProc;

   /*
    * If an async process is already running for the component.
    * Do not spin another async process.
    */
   ASSERT(commandline != NULL);
   ASSERT(!ComponentMgr_IsAsyncProcessRunning(componentIndex));

   memset(&userArgs, 0, sizeof userArgs);
   asyncProc = ProcMgr_ExecAsync(commandline, &userArgs);
   if (asyncProc == NULL) {
      g_warning("%s: Failed to create process", __FUNCTION__);
      return;
   }

   /*
    * We need information about the async process, component and backoff
    * timer value, hence populate the same and store for future references.
    */
   procInfo = ComponentMgrCreateAsyncProcessInfo(asyncProc, ctx,
                                                 COMPONENTMGR_ASYNC_CHECK_STATUS_TERMINATE_PERIOD,
                                                 componentIndex,
                                                 callback);
   /*
    * Set the asyncProcInfo field and GSource timer in the components array
    * to cache information of a running async process for a component.
    */
   sourceTimer = g_timeout_source_new(COMPONENTMGR_ASYNC_CHECK_STATUS_POLL_INTERVAL * 1000);
   ComponentMgr_SetComponentAsyncProcInfo(procInfo, componentIndex);
   ComponentMgr_SetComponentGSourceTimer(sourceTimer, componentIndex);
   VMTOOLSAPP_ATTACH_SOURCE(ctx, sourceTimer, ComponentMgrCheckStatusMonitor,
                            procInfo, NULL);
   g_source_unref(sourceTimer);
}


/*
 *****************************************************************************
 * ComponentMgr_AsynchronousComponentActionStart --
 *
 * This function invokes the component script as an async process to perform
 * present/absent action and a GSource timer to poll the progress.
 *
 * @param[in] ctx Tools application context.
 * @param[in] commandline <component_script> <action_arguments> command to be
 *                        executed to add/remove the component.
 * @param[in] componentIndex Index of component in global array of components.
 *
 * @return
 *      None.
 *
 * Side effects:
 *      Async process creation may fail for many reasons.
 *
 *****************************************************************************
 */

void
ComponentMgr_AsynchronousComponentActionStart(ToolsAppCtx *ctx,        // IN
                                              const char *commandline, // IN
                                              int componentIndex)      // IN
{
   ProcMgr_ProcArgs userArgs;
   GSource *sourceTimer;
   AsyncProcessInfo *procInfo;
   ProcMgr_AsyncProc *asyncProc;

   /*
    * If an async process is already running for the component.
    * Do not spin another async process.
    */
   ASSERT(commandline != NULL);
   ASSERT(!ComponentMgr_IsAsyncProcessRunning(componentIndex));

   memset(&userArgs, 0, sizeof userArgs);
   asyncProc = ProcMgr_ExecAsync(commandline, &userArgs);
   if (asyncProc == NULL) {
      g_warning("%s: Failed to create process", __FUNCTION__);
      return;
   }

   /*
    * We need information about the async process, component and backoff
    * timer value, hence populate the same and store for future references.
    */
   procInfo = ComponentMgrCreateAsyncProcessInfo(asyncProc, ctx,
                                                 COMPONENTMGR_ASYNCPROCESS_TERMINATE_PERIOD,
                                                 componentIndex, NULL);
   /*
    * Set the asyncProcInfo field and GSource timer field in the components
    * array to cache info of running async process for a component.
    */
   sourceTimer = g_timeout_source_new(COMPONENTMGR_ASYNCPROCESS_POLL_INTERVAL * 1000);
   ComponentMgr_SetComponentAsyncProcInfo(procInfo, componentIndex);
   ComponentMgr_SetComponentGSourceTimer(sourceTimer, componentIndex);
   VMTOOLSAPP_ATTACH_SOURCE(ctx, sourceTimer,
                            ComponentMgrProcessMonitor, procInfo, NULL);
   g_source_unref(sourceTimer);
}
070701000002E0000081A40000000000000000000000016822550500002CD8000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr/componentMgrPlugin.h /*********************************************************
 * Copyright (c) 2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _COMPONENTMGRPlugin_H_
#define _COMPONENTMGRPlugin_H_

/*
 * componentMgrPlugin.h --
 *
 * This file contains macros used by the componentMgr plugin having references
 * for timer related information, guestVar information, component information
 * and configuration related information.
 * Defines functions shared across the componentMgr plugin.
 * Defines states structures to be used to cache and store information related
 * to a component and async process.
 *
 */


#include "conf.h"

#define G_LOG_DOMAIN COMPONENTMGR_CONF_GROUPNAME

#include "vm_basic_defs.h"
#include "vmware/tools/plugin.h"
#include "procMgr.h"

#if defined(_WIN32)
#include <windows.h>
#endif


//********************** Timer Definitions ****************************

/**
 * Default and minimum poll interval for componentMgr in seconds.
 */
#define COMPONENTMGR_DEFAULT_POLL_INTERVAL 180

/**
 * Minimum poll interval for componentMgr in seconds.
 * For development and beta builds the poll-interval can be configured
 * lower than the default poll-interval.
 */
#ifdef VMX86_DEBUG
#define COMPONENTMGR_MIN_POLL_INTERVAL 5
#else
#define COMPONENTMGR_MIN_POLL_INTERVAL COMPONENTMGR_DEFAULT_POLL_INTERVAL
#endif

/*
 * Poll interval between 2 consecutive check status operation in seconds.
 */
#define COMPONENTMGR_ASYNC_CHECK_STATUS_POLL_INTERVAL 1

/*
 * Max time in seconds after which the async process running check status
 * command will be terminated.
 */
#define COMPONENTMGR_ASYNC_CHECK_STATUS_TERMINATE_PERIOD 15

/*
 * Poll interval for waiting on the async process running the action for a
 * component in seconds.
 */
#define COMPONENTMGR_ASYNCPROCESS_POLL_INTERVAL 5

/*
 * The wait period after which the async process needs to be killed for a
 * component in seconds.
 */
#define COMPONENTMGR_ASYNCPROCESS_TERMINATE_PERIOD 600

/*
 * The amount of times the check status operation needs to wait before any
 * change in the guetsVar to trigger another checkstatus operation.
 */
#define COMPONENTMGR_CHECK_STATUS_COUNT_DOWN 10

//********************** Component Action Definitions *********************

/**
 * Defines check status action on the component.
 */
#define COMPONENTMGR_COMPONENTCHECKSTATUS "checkstatus"

/**
 * Defines an invalid action on the component.
 */
#define COMPONENTMGR_COMPONENINVALIDACTION "invalidaction"

/**
 * Defines present action for a component to be installed on a system.
 */
#define COMPONENTMGR_COMPONENTPRESENT "present"

/**
 * Defines absent action for a component to be removed from a system.
 */
#define COMPONENTMGR_COMPONENTABSENT "absent"

//********************** Guest Variable Definitions *********************

/**
 * Defines argument to publish installed and enabled components.
 */
#define COMPONENTMGR_INFOAVAILABLE "available"

/**
 * Defines argument to publish last status of a particular component.
 */
#define COMPONENTMGR_INFOLASTSTATUS "laststatus"

/**
 * Defines action to be taken on a component.
 * guestinfo./vmware.components.<comp_name>.desiredstate
 */
#define COMPONENTMGR_INFODESIREDSTATE "desiredstate"

/*
 * GuestVar prefix string to fetch the action required for a component.
 */
#define COMPONENTMGR_ACTION "info-get guestinfo./vmware.components"

/**
 * String to set informational guestVar exposed by the plugin.
 */
#define COMPONENTMGR_PUBLISH_COMPONENTS "info-set guestinfo.vmware.components"

//********************** Component Definitions *********************

/**
 * Defines the directory for the plugin to host the scripts.
 */
#define COMPONENTMGR_DIRECTORY "componentMgr"

/**
 * Defines none to indicate no component is managed by the plugin.
 */
#define COMPONENTMGR_NONECOMPONENTS "none"

/**
 * Define all the names of components under this section.
 */
#define SALT_MINION "salt_minion"

/**
 * Defines a config all in included to indicate all the components are
 * managed by the plugin.
 */
#define COMPONENTMGR_ALLCOMPONENTS "all"

/*
 * The included param in the tools.conf contains comma separated list
 * of components and can have special values.
 * Defines various special values present in the included tools.conf param.
 */

typedef enum IncludedComponents
{
  ALLCOMPONENTS,
  NONECOMPONENTS,
  NOSPECIALVALUES
} IncludedComponents;

//*******************************************************************

/*
 * Installation status of the components managed by the componentMgr plugin.
 * The status for each component will be updated based on the exit code
 * returned by the script executing check status operation.
 */

typedef enum InstallStatus
{
   INSTALLED = 100,         /* The component is installed on the guest OS. */
   INSTALLING,              /* The component is being installed on the guest
                             * OS.
                             */
   NOTINSTALLED,            /* The component is not installed on the guest OS.
                             */
   INSTALLFAILED,           /* The component install failed on the guest OS. */
   REMOVING,                /* The component is being removed on the guest OS.
                             */
   REMOVEFAILED,            /* The component remove failed on the guest OS. */
   UNMANAGED,               /* The component is installed on the guest OS, but
                             * is not managed (or manageable), through the
                             * component manager plugin.
                             */
   SCRIPTFAILED = 126,      /* The component script failed for some reason. */
   SCRIPTTERMINATED = 130   /* The component script terminated for some reason.
                             */
} InstallStatus;


/*
 * Actions currently supported by the componentMgr plugin for the known and
 * enabled components.
 */

typedef enum Action
{
   PRESENT,      /* The action adds/installs the components on the guest. */
   ABSENT,       /* The action removes/uninstalls the components on the guest.*/
   CHECKSTATUS,  /* The action calls the preconfigured script to check the
                  * current status of the component.
                  */
   INVALIDACTION /* Action not recognised by the plugin. */
} Action;


/*
 * Structure to store information about the asynchronous process being run
 * for a particular component.
 */

typedef struct AsyncProcessInfo {
   ProcMgr_AsyncProc *asyncProc; /* ProcMgr_AsyncProc structure consisting of
                                  * the process data running an action on the
                                  * component.
                                  */
   ToolsAppCtx *ctx;             /* Tools application context. */
   int backoffTimer;             /* Backoff timer to wait until timeout
                                  * to kill the asynchronous process.
                                  */
   int componentIndex;           /* The index of the component in the global
                                  * array of components.
                                  */
   void (*callbackFunction)(int componentIndex); /* A callback function to
                                                  * sequence a new operation
                                                  */
} AsyncProcessInfo;


/*
 * This structure contains all the information related to all the components
 * managed by the plugin. The component states is maintained in this structure.
 */

typedef struct ComponentInfo
{
   const char *name;     /* The name of the component. */
   gboolean isEnabled;   /* Component enabled/disabled by the plugin. */
   InstallStatus status; /* Contains current status of the component. */
   GSource *sourceTimer; /* A GSource timer for async process monitoring running
                          * an operation for a component.
                          */
   AsyncProcessInfo *procInfo; /* A structure to store information about the
                                * current running async process for a component.
                                */
   int statuscount;      /* A counter value to store max number of times to
                          * wait before starting another checkstatus operation
                          */
   Action action;        /* Contains information about the action to be
                          * performed on a component.
                          */
} ComponentInfo;


void
ComponentMgrUpdateComponentEnableStatus(ToolsAppCtx *ctx);


void
ComponentMgr_UpdateComponentStatus(ToolsAppCtx *ctx);


gboolean
ComponentMgr_SendRpc(ToolsAppCtx *ctx,
                     const char *guestInfoCmd,
                     char **outBuffer,
                     size_t *outBufferLen);


void
ComponentMgr_Destroytimers();


const char*
ComponentMgr_GetComponentInstallStatus(InstallStatus installStatus);


const char*
ComponentMgr_GetComponentAction(Action action);


void
ComponentMgr_AsynchronousComponentActionStart(ToolsAppCtx *ctx,
                                              const char *commandline,
                                              int componetIndex);


void
ComponentMgr_SetStatusComponentInfo(ToolsAppCtx *ctx,
                                    int exitCode,
                                    int componentIndex);


char *
ComponentMgr_CheckStatusCommandLine(int componentIndex);


void
ComponentMgr_UpdateComponentEnableStatus(ToolsAppCtx *ctx);


void
ComponentMgr_SetComponentGSourceTimer(GSource *componentTimer,
                                      int componentIndex);

void
ComponentMgr_ResetComponentGSourceTimer(int componentIndex);


void
ComponentMgr_ExecuteComponentAction(int componentIndex);


void
ComponentMgr_AsynchronousComponentCheckStatus(ToolsAppCtx *ctx,
                                              const char *commandline,
                                              int componentIndex,
                                              void (*callback)(int compIndex));


ToolsAppCtx*
ComponentMgr_GetToolsAppCtx();


const char*
ComponentMgr_GetIncludedComponents(IncludedComponents pos);


void
ComponentMgr_SetComponentAsyncProcInfo(AsyncProcessInfo *asyncProcInfo,
                                       int componentIndex);


void
ComponentMgr_ResetComponentAsyncProcInfo(int componentIndex);


gboolean
ComponentMgr_IsAsyncProcessRunning(int componentIndex);


const char*
ComponentMgr_GetComponentName(int componentIndex);


void
ComponentMgr_FreeAsyncProc(AsyncProcessInfo *procInfo);


void
ComponentMgr_DestroyAsyncProcess();


void
ComponentMgr_PublishAvailableComponents(ToolsAppCtx *ctx,
                                        const char *components);


gboolean
ComponentMgr_CheckAnyAsyncProcessRunning();
#endif /* _ComponentMgrPlugin_H_ */
070701000002E1000081A40000000000000000000000016822550500001978000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr/componentMgrUtil.c   /*********************************************************
 * Copyright (c) 2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * componentMgrUtil.c --
 *
 * Common utility functions used by the componentMgr plugin.
 *
 */


#include "componentMgrPlugin.h"
#include "vmware/tools/log.h"


/*
 ******************************************************************************
 * ComponentMgr_SendRpc --
 *
 * Sends RPC message to fetch the guestVars.
 *
 * @param[in] ctx Tools application context.
 * @param[in] guestInfoCmd Guestinfo command to fetch the guestVar.
 * @param[out] outBuffer Output buffer to hold RPC result (optional).
 * @param[out] outBufferLen Output buffer length (optional).
 *
 * @return
 *      TRUE if cmd executed successfully, otherwise FALSE
 *
 * @note: outBuffer and outBufferLen are optional parameters and be used when
 * RPC command output required. Both output parameters are required for output.
 * Users have to call RpcChannel_Free on output buffer if supplied.
 *
 * Side effects:
 *      None.
 *
 ******************************************************************************
 */

gboolean
ComponentMgr_SendRpc(ToolsAppCtx *ctx,         // IN
                     const char *guestInfoCmd, // IN
                     char **outBuffer,         // OUT
                     size_t *outBufferLen)     // OUT
{
   gboolean status;
   size_t replyLen;
   char *reply = NULL;

   ASSERT(ctx != NULL);
   ASSERT(ctx->rpc != NULL);
   ASSERT(guestInfoCmd != NULL);

   status = RpcChannel_Send(ctx->rpc,
                            guestInfoCmd,
                            strlen(guestInfoCmd) + 1,
                            &reply,
                            &replyLen);
   if (!status) {
      g_info("%s: Failed to send RPC message, request: \'%s\',"
             " reply: \'%s\'.\n", __FUNCTION__, guestInfoCmd,
             VM_SAFE_STR(reply));
   }

   if (outBuffer != NULL && outBufferLen != NULL) {
      *outBuffer = reply;
      *outBufferLen = replyLen;
   } else {
      RpcChannel_Free(reply);
   }
   return status;
}


/*
 **************************************************************************
 * ComponentMgr_GetComponentInstallStatus --
 *
 * This function returns an enum equivalent of the current status of the
 * component.
 *
 * @param[in] installStatus Enum value of status of component.
 *
 * @return
 *       String equivalent of the component current status.
 *
 * @Side effects
 *       None.
 *
 **************************************************************************
 */

const char*
ComponentMgr_GetComponentInstallStatus(InstallStatus installStatus) // IN
{
   switch (installStatus) {
      case NOTINSTALLED:      return "NOTINSTALLED";
      case INSTALLING:        return "INSTALLING";
      case INSTALLED:         return "INSTALLED";
      case REMOVING:          return "REMOVING";
      case INSTALLFAILED:     return "INSTALLFAILED";
      case REMOVEFAILED:      return "REMOVEFAILED";
      case UNMANAGED:         return "UNMANAGED";
      case SCRIPTFAILED:      return "SCRIPTFAILED";
      case SCRIPTTERMINATED:  return "SCRIPTTERMINATED";
   }
   return "INVALIDSTATUS";
}


/*
 **************************************************************************
 * ComponentMgr_GetComponentAction --
 *
 * This function returns an enum equivalent of component action to be executed.
 *
 * @param[in] action The action to be taken on the component.
 *
 * @return
 *       String equivalent of the component action.
 *
 * @Side effects
 *       None.
 *
 **************************************************************************
 */

const char*
ComponentMgr_GetComponentAction(Action action) // IN
{
   switch (action) {
      case PRESENT:       return COMPONENTMGR_COMPONENTPRESENT;
      case ABSENT:        return COMPONENTMGR_COMPONENTABSENT;
      case CHECKSTATUS:   return COMPONENTMGR_COMPONENTCHECKSTATUS;
      case INVALIDACTION: return COMPONENTMGR_COMPONENINVALIDACTION;
   }
   return COMPONENTMGR_COMPONENINVALIDACTION;
}


/*
 **************************************************************************
 * ComponentMgr_GetIncludedComponents --
 *
 * This function returns an enum equivalent of the special values in the
 * included tools.conf param.
 *
 * @param[in] specialvalue The enum value of special value in the param.
 *
 * @return
 *       String equivalent of the special value in the param.
 *
 * @Side effects
 *       None.
 *
 **************************************************************************
 */


const char*
ComponentMgr_GetIncludedComponents(IncludedComponents specialValue) // IN
{
   switch (specialValue) {
      case ALLCOMPONENTS:      return "ALLCOMPONENTS";
      case NONECOMPONENTS:     return "NONECOMPONENTS";
      case NOSPECIALVALUES:    return "NOSPECIALVALUES";
   }
   return "NOSPECIALVALUES";
}


/*
 *****************************************************************************
 * ComponentMgr_PublishAvailableComponents --
 *
 * This function publishes guestVar guestinfo.vmware.components.available with
 * requested components.
 *
 * @param[in] ctx Tools application context.
 * @param[in] components Comma separated list of available components.
 *
 * @return
 *      None
 *
 * Side effects:
 *      None.
 *
 *****************************************************************************
 */

void
ComponentMgr_PublishAvailableComponents(ToolsAppCtx *ctx,       // IN
                                        const char *components) // IN
{
   gboolean status;
   gchar *msg = g_strdup_printf("%s.%s %s", COMPONENTMGR_PUBLISH_COMPONENTS,
                                COMPONENTMGR_INFOAVAILABLE,
                                components);

   status = ComponentMgr_SendRpc(ctx, msg, NULL, NULL);
   g_free(msg);
}
070701000002E2000081A40000000000000000000000016822550500014381000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/componentMgr/svtminion.sh #!/usr/bin/env bash

# Copyright 2021-2023 VMware, Inc.
# SPDX-License-Identifier: Apache-2

## Salt VMware Tools Integration script
##  integration with Component Manager and GuestStore Helper

# latest shellcheck 0.9.0-1 is showing false negatives
# which 0.8.0-2 does not, disabling since using 0.9.0-1
# shellcheck disable=SC2317,SC2004,SC2320,SC2086

## set -u
## set -xT
set -o functrace
set -o pipefail
## set -o errexit

# using bash for now
# run this script as root, as needed to run Salt

## readonly SCRIPT_VERSION='SCRIPT_VERSION_REPLACE'
readonly SCRIPT_VERSION="1.6"

# definitions

CURL_DOWNLOAD_RETRY_COUNT=5

## Repository locations and naming
readonly default_salt_url_version="latest"
readonly salt_name="salt"
readonly repo_json_file="repo.json"
salt_url_version="${default_salt_url_version}"
pre_3006_base_url="https://repo.saltproject.io/salt/vmware-tools-onedir"
# Release
post_3005_base_url="https://repo.saltproject.io/salt/py3/onedir"
base_url=""

# Salt file and directory locations
readonly base_salt_location="/opt/saltstack"
readonly salt_dir="${base_salt_location}/${salt_name}"
readonly salt_conf_dir="/etc/salt"
readonly salt_minion_conf_name="minion"
readonly salt_minion_conf_file="${salt_conf_dir}/${salt_minion_conf_name}"
readonly salt_master_sign_dir="${salt_conf_dir}/pki/${salt_minion_conf_name}"

readonly log_dir="/var/log"

readonly list_files_systemd_to_remove="/lib/systemd/system/salt-minion.service
/usr/lib/systemd/system/salt-minion.service
/usr/local/lib/systemd/system/salt-minion.service
/etc/systemd/system/salt-minion.service
"

readonly list_file_dirs_to_remove="${base_salt_location}
/etc/salt
/var/run/salt
/var/cache/salt
/var/log/salt
/usr/bin/salt-*
${list_files_systemd_to_remove}
"
## /var/log/vmware-${SCRIPTNAME}-*

readonly salt_dep_file_list="systemctl
curl
sha512sum
vmtoolsd
grep
awk
sed
cut
wget
"

readonly allowed_log_file_action_names="status
depend
install
clear
remove
default
"

readonly salt_wrapper_file_list="minion
call
"

readonly salt_minion_service_wrapper=\
"# Copyright 2021-2023 VMware, Inc.
# SPDX-License-Identifier: Apache-2

[Unit]
Description=The Salt Minion
Documentation=man:salt-minion(1) file:///usr/share/doc/salt/html/contents.html https://docs.saltproject.io/en/latest/contents.html
After=network.target
# After=ConnMgr.service ProcMgr.service sockets.target

[Service]
KillMode=process
Type=notify
NotifyAccess=all
LimitNOFILE=8192
MemoryLimit=250M
Nice=19
ExecStart=/usr/bin/salt-minion

[Install]
WantedBy=multi-user.target
"

# Onedir detection locations
readonly onedir_post_3005_location="${salt_dir}/salt-minion"
readonly onedir_pre_3006_location="${salt_dir}/run/run"

declare -a list_of_onedir_locations_check
list_of_onedir_locations_check[0]="${onedir_pre_3006_location}"
list_of_onedir_locations_check[1]="${onedir_post_3005_location}"

## VMware file and directory locations
readonly vmtools_base_dir_etc="/etc/vmware-tools"
readonly vmtools_conf_file="tools.conf"
readonly vmtools_salt_minion_section_name="salt_minion"

## VMware guestVars file and directory locations
readonly guestvars_base_dir="guestinfo./vmware.components"
readonly \
guestvars_salt_dir="${guestvars_base_dir}.${vmtools_salt_minion_section_name}"
readonly guestvars_salt_args="${guestvars_salt_dir}.args"
readonly guestvars_salt_desiredstate="${guestvars_salt_dir}.desiredstate"


# Array for minion configuration keys and values
# allows for updates from number of configuration sources before final
# write to /etc/salt/minion
declare -a m_cfg_keys
declare -a m_cfg_values


## Component Manager Installer/Script return/exit status codes
# return/exit Status codes
#  100 + 0 => installed
#  100 + 1 => installing
#  100 + 2 => notInstalled
#  100 + 3 => installFailed
#  100 + 4 => removing
#  100 + 5 => removeFailed
#  100 + 6 => externalInstall
#  126 => scriptFailed
#  130 => scriptTerminated
declare -A STATUS_CODES_ARY
STATUS_CODES_ARY[installed]=100
STATUS_CODES_ARY[installing]=101
STATUS_CODES_ARY[notInstalled]=102
STATUS_CODES_ARY[installFailed]=103
STATUS_CODES_ARY[removing]=104
STATUS_CODES_ARY[removeFailed]=105
STATUS_CODES_ARY[externalInstall]=106
STATUS_CODES_ARY[scriptFailed]=126
STATUS_CODES_ARY[scriptTerminated]=130

# log levels available for logging, order sensitive
readonly LOG_MODES_AVAILABLE=(silent error warning info debug)
declare -A LOG_LEVELS_ARY
LOG_LEVELS_ARY[silent]=0
LOG_LEVELS_ARY[error]=1
LOG_LEVELS_ARY[warning]=2
LOG_LEVELS_ARY[info]=3
LOG_LEVELS_ARY[debug]=4


STATUS_CHK=0
DEPS_CHK=0
USAGE_HELP=0
UNINSTALL_FLAG=0
VERBOSE_FLAG=0
VERSION_FLAG=0

CLEAR_ID_KEYS_FLAG=0
CLEAR_ID_KEYS_PARAMS=""

INSTALL_FLAG=0
INSTALL_PARAMS=""

MINION_VERSION_FLAG=0
MINION_VERSION_PARAMS=""

LOG_LEVEL_FLAG=0
LOG_LEVEL_PARAMS=""

#default logging level to errors, similar to Windows script
LOG_LEVEL=${LOG_LEVELS_ARY[warning]}

SOURCE_FLAG=0
SOURCE_PARAMS=""

# Flag for pre_3006 and post_3005, 0 => pre_3006, 1 => post_3005
POST_3005_FLAG=0
POST_3005_MAJOR_VER_FLAG=0


# helper functions

_timestamp() {
    date -u "+%Y-%m-%d %H:%M:%S"
}

_log() {
    echo "$(_timestamp) $*" >> \
        "${log_dir}/vmware-${SCRIPTNAME}-${LOG_ACTION}-${logdate}.log"
}

_display() {
    if [[ ${VERBOSE_FLAG} -eq 1 ]]; then echo "$1"; fi
    _log "$*"
}

_error_log() {
    if [[ ${LOG_LEVELS_ARY[error]} -le ${LOG_LEVEL} ]]; then
        local log_file=""
        log_file="${log_dir}/vmware-${SCRIPTNAME}-${LOG_ACTION}-${logdate}.log"
        msg="ERROR: $*"
        echo "$msg" 1>&2
        echo "$(_timestamp) $msg" >> "${log_file}"
        echo "One or more errors found. See ${log_file} for details." 1>&2
        CURRENT_STATUS=${STATUS_CODES_ARY[scriptFailed]}
        exit ${STATUS_CODES_ARY[scriptFailed]}
    fi
}

_info_log() {
    if [[ ${LOG_LEVELS_ARY[info]} -le ${LOG_LEVEL} ]]; then
        msg="INFO: $*"
        _log "${msg}"
    fi
}

_warning_log() {
    if [[ ${LOG_LEVELS_ARY[error]} -le ${LOG_LEVEL} ]]; then
        msg="WARNING: $*"
        _log "${msg}"
    fi
}

_debug_log() {
    if [[ ${LOG_LEVELS_ARY[debug]} -le ${LOG_LEVEL} ]]; then
        msg="DEBUG: $*"
        _log "${msg}"
    fi
}

_yesno() {
read -r -p "Continue (y/n)?" choice
case "$choice" in
  y|Y ) echo "yes";;
  n|N ) echo "no";;
  * ) echo "invalid";;
esac
}


#
# _usage
#
#   Prints out help text
#

 _usage() {
     echo ""
     echo "usage: ${0}"
     echo "             [-c|--clear] [-d|--depend] [-h|--help] [-i|--install]"
     echo "             [-j|--source] [-l|--loglevel] [-m|--minionversion]"
     echo "             [-r|--remove] [-s|--status] [-v|--version]"
     echo ""
     echo "  -c, --clear     clear previous minion identifier and keys,"
     echo "                     and set specified identifier if present"
     echo "  -d, --depend    check dependencies required to run script exist"
     echo "  -h, --help      this message"
     echo "  -i, --install   install and activate salt-minion configuration"
     echo "                     parameters key=value can also be passed on CLI"
     echo "  -j, --source   specify location to install Salt Minion from"
     echo "                     default is repo.saltproject.io location"
     echo "                 for example: url location"
     echo "                     http://my_web_server.com/my_salt_onedir"
     echo "                     https://my_web_server.com/my_salt_onedir"
     echo "                     file://my_path/my_salt_onedir"
     echo "                     //my_path/my_salt_onedir"
     echo "                 if specific version of Salt Minion specified, -m"
     echo "                 then its appended to source, default[latest]"
     echo "  -l, --loglevel  set log level for logging,"
     echo "                     silent error warning debug info"
     echo "                     default loglevel is warning"
     echo "  -m, --minionversion install salt-minion version, default[latest]"
     echo "  -r, --remove    deactivate and remove the salt-minion"
     echo "  -s, --status    return status for this script"
     echo "  -v, --version   version of this script"
     echo ""
     echo "  salt-minion VMTools integration script"
     echo "      example: $0 --status"
}


# work functions

#
# _cleanup_int
#
#   Cleanups any running process and areas on control-C
#
#
# Results:
#   Exits with hard-coded value 130
#

_cleanup_int() {
    rm -rf "$WORK_DIR"
    _debug_log "$0:${FUNCNAME[0]} Deleted temp working directory $WORK_DIR"

    exit ${STATUS_CODES_ARY[scriptTerminated]}
}

#
# _cleanup_exit
#
#   Cleanups any running process and areas on exit
#
_cleanup_exit() {
    rm -rf "$WORK_DIR"
    _debug_log "$0:${FUNCNAME[0]} Deleted temp working directory $WORK_DIR"
    ## exit ${CURRENT_STATUS}
}

trap _cleanup_int INT
trap _cleanup_exit EXIT


# cheap trim relying on echo to convert tabs to spaces and
# all multiple spaces to a single space
_trim() {
    echo "$1"
}


#
# _set_log_level
#
#   Set log_level for logging,
#       log_level 'silent','error','warning','info','debug'
#       default 'warning'
#
# Results:
#   Returns with exit code
#

_set_log_level() {

    _info_log "$0:${FUNCNAME[0]} processing setting set log_level for logging"

    local ip_level=""
    local valid_level=0
    local old_log_level=${LOG_LEVEL}

    ip_level=$( echo "$1" | cut -d ' ' -f 1)
    scam=${#LOG_MODES_AVAILABLE[@]}
    for ((i=0; i<scam; i++)); do
        name=${LOG_MODES_AVAILABLE[i]}
        if [[ "${ip_level}" = "${name}" ]]; then
            valid_level=1
            break
        fi
    done
    if [[ ${valid_level} -ne 1 ]]; then
        _warning_log "$0:${FUNCNAME[0]} attempted to set log_level with "\
            "invalid input, log_level unchanged, currently "\
            "'${LOG_MODES_AVAILABLE[${LOG_LEVEL}]}'"
    else
        LOG_LEVEL=${LOG_LEVELS_ARY[${ip_level}]}
        _info_log "$0:${FUNCNAME[0]} changed log_level from "\
            "'${LOG_MODES_AVAILABLE[${old_log_level}]}' to "\
            "'${LOG_MODES_AVAILABLE[${LOG_LEVEL}]}'"
    fi
    return 0
}


#
# _set_install_minion_version_fn
#
#   Set the version of Salt Minion wanted to install
#       default 'latest'
#
#   Note: typically Salt version includes the release number in addition to
#         version number or 'latest' for the most recent release
#
#           for example: 3003.3-1
#
# Results:
#   Returns with exit code
#

_set_install_minion_version_fn() {

    if [[ "$#" -ne 1 ]]; then
        _error_log "$0:${FUNCNAME[0]} error expected one parameter "\
            "specifying the version of the salt-minion to install or 'latest'"
    fi

    _info_log "$0:${FUNCNAME[0]} processing setting Salt version for "\
        "salt-minion to install"
    local salt_version=""

    salt_version=$(echo "$1" | cut -d ' ' -f 1)
    if [[ "latest" = "${salt_version}" ]]; then
        _debug_log "$0:${FUNCNAME[0]} input Salt version for salt-minion to "\
            "install is 'latest', leaving as default "\
            "'${default_salt_url_version}' for now"

    else
        _debug_log "$0:${FUNCNAME[0]} input Salt version for salt-minion to "\
            "install is '${salt_version}'"

        salt_url_version="${salt_version}"
        _debug_log "$0:${FUNCNAME[0]} set Salt version for salt-minion to "\
            "install to '${salt_url_version}'"
    fi

    return 0
}

#
# _set_post_3005_flags_from_version
#
#   Sets the POST_3005_FLAG and POST_3005_MAJOR_VER_FLAG
#       from the version currently present in salt_url_version
#
#   Will also set base_url if not already defined by --source option
#
# Results:
#   Returns with exit code
#
_set_post_3005_flags_from_version() {
    _info_log "$0:${FUNCNAME[0]} setting POST_3005_FLAG and "\
        "POST_3005_MAJOR_VER_FLAG from Salt version '${salt_url_version}'"

    if [[ "latest" = "${salt_url_version}" ]]; then
        POST_3005_FLAG=1
        POST_3005_MAJOR_VER_FLAG=1
        base_url="${post_3005_base_url}"
        # done, already have url for latest & major versions
        _debug_log "$0:${FUNCNAME[0]} post-3005 install, using latest "\
            "base_url '${base_url}'"
    else
        ver_chk=$(echo "${salt_url_version}" | cut -d '.' -f 1)
        if [[ ${ver_chk} -ge 3006 ]]; then
            POST_3005_FLAG=1
            ver_chk_major=$(echo "${salt_url_version}" | cut -d '.' -f 1)
            ver_chk_minor=$(echo "${salt_url_version}" | cut -d '.' -f 2)
            _debug_log "$0:${FUNCNAME[0]} post-3005 install, checking "\
                "for major version only '${ver_chk_major}', minor "\
                "'${ver_chk_minor}'"
            if [[ "${ver_chk_major}" = "${ver_chk_minor}" ]]; then
                POST_3005_MAJOR_VER_FLAG=1
                base_url="${post_3005_base_url}"
            else
                base_url="${post_3005_base_url}/minor"
            fi
            _debug_log "$0:${FUNCNAME[0]} post-3005 install, for "\
                "'${salt_url_version}' using base_url '${base_url}'"
        else
            # install pre-3006, use older url
            base_url="${pre_3006_base_url}"
            _debug_log "$0:${FUNCNAME[0]} pre-3006 install, for "\
                "'${salt_url_version}' using base_url '${base_url}'"
        fi
    fi
}


#
# _update_minion_conf_ary
#
#   Updates the running minion_conf array with input key and value
#   updating with the new value if the key is already found
#
# Results:
#   Updated array
#

_update_minion_conf_ary() {
    local cfg_key="$1"
    local cfg_value="$2"
    local _retn=0

    if [[ "$#" -ne 2 ]]; then
        _error_log "$0:${FUNCNAME[0]} error expect two parameters, "\
            "a key and a value"
    fi

    # now search m_cfg_keys array to see if new key
    key_ary_sz=${#m_cfg_keys[@]}
    if [[ ${key_ary_sz} -ne 0 ]]; then
        # need to check if array has same key
        local chk_found=0
        for ((chk_idx=0; chk_idx<key_ary_sz; chk_idx++))
        do
            if [[ "${m_cfg_keys[${chk_idx}]}" = "${cfg_key}" ]]; then
                m_cfg_values[${chk_idx}]="${cfg_value}"
                _debug_log "$0:${FUNCNAME[0]} updating minion configuration "\
                    "array key '${m_cfg_keys[${chk_idx}]}' with "\
                    "value '${cfg_value}'"
                chk_found=1
                break;
            fi
        done
        if [[ ${chk_found} -eq 0 ]]; then
            # new key for array
            m_cfg_keys[${key_ary_sz}]="${cfg_key}"
            m_cfg_values[${key_ary_sz}]="${cfg_value}"
            _debug_log "$0:${FUNCNAME[0]} adding to minion configuration "\
                "array new key '${cfg_key}' and value '${cfg_value}'"
        fi
    else
        # initial entry
        m_cfg_keys[0]="${cfg_key}"
        m_cfg_values[0]="${cfg_value}"
        _debug_log "$0:${FUNCNAME[0]} adding initial minion configuration "\
            "array, key '${cfg_key}' and value '${cfg_value}'"
    fi
    return ${_retn}
}


#
# _fetch_vmtools_salt_minion_conf_tools_conf
#
#   Retrieve the configuration for salt-minion from VMTools
#                                           configuration file tools.conf
#
# Results:
#   Exits with new VMTools configuration file if none found or salt-minion
#   configuration file updated with configuration read from VMTools
#   configuration file section for salt_minion
#

_fetch_vmtools_salt_minion_conf_tools_conf() {
    # fetch the current configuration for section salt_minion
    # from vmtoolsd configuration file
    local _retn=0
    if [[ ! -f "${vmtools_base_dir_etc}/${vmtools_conf_file}" ]]; then
        # conf file doesn't exist, create it
        mkdir -p "${vmtools_base_dir_etc}"
        echo "[${vmtools_salt_minion_section_name}]" \
            > "${vmtools_base_dir_etc}/${vmtools_conf_file}"
        _warning_log "$0:${FUNCNAME[0]} creating empty configuration "\
            "file ${vmtools_base_dir_etc}/${vmtools_conf_file}"
    else
        # need to extract configuration for salt-minion
        # find section name ${vmtools_salt_minion_section_name}
        # read configuration till next section, output salt-minion conf file

        local salt_config_flag=0
        while IFS= read -r line
        do
            line_value=$(_trim "${line}")
            if [[ -n "${line_value}" ]]; then
                _debug_log "$0:${FUNCNAME[0]} processing tools.conf "\
                    "line '${line}'"
                if echo "${line_value}" | grep -q '^\[' ; then
                    if [[ ${salt_config_flag} -eq 1 ]]; then
                        # if new section after doing Salt config, we are done
                        break;
                    fi
                    if [[ ${line_value} = \
                        "[${vmtools_salt_minion_section_name}]" ]]; then
                        # have section, get configuration values, set flag and
                        #  start fresh salt-minion configuration file
                        salt_config_flag=1
                    fi
                elif [[ ${salt_config_flag} -eq 1 ]]; then
                    # read config ahead of section check, better logic flow
                    cfg_key=$(echo "${line}" | cut -d '=' -f 1)
                    cfg_value=$(echo "${line}" | cut -d '=' -f 2)
                    _update_minion_conf_ary "${cfg_key}" "${cfg_value}" || {
                        _error_log "$0:${FUNCNAME[0]} error updating minion "\
                            "configuration array with key '${cfg_key}' and "\
                            "value '${cfg_value}', retcode '$?'";
                    }
                else
                    _debug_log "$0:${FUNCNAME[0]} skipping tools.conf "\
                        "line '${line}'"
                fi
            fi
        done < "${vmtools_base_dir_etc}/${vmtools_conf_file}"
    fi
    return ${_retn}
}


#
# _fetch_vmtools_salt_minion_conf_guestvars
#
#   Retrieve the configuration for salt-minion from VMTools guest variables
#
# Results:
#   salt-minion configuration file updated with configuration read
#                                           from VMTools guest variables
#   configuration file section for salt_minion
#

_fetch_vmtools_salt_minion_conf_guestvars() {
    # fetch the current configuration for section salt_minion
    # from guest variables args

    local _retn=0
    local gvar_args=""

    gvar_args=$(vmtoolsd --cmd "info-get ${guestvars_salt_args}" 2>/dev/null)\
        || { _warning_log "$0:${FUNCNAME[0]} unable to retrieve arguments "\
            "from guest variables location ${guestvars_salt_args}, "\
            "retcode '$?'";
    }

    if [[ -z "${gvar_args}" ]]; then return ${_retn}; fi

    _debug_log "$0:${FUNCNAME[0]} processing arguments from guest variables "\
        "location ${guestvars_salt_args}"

    for idx in ${gvar_args}
    do
        cfg_key=$(echo "${idx}" | cut -d '=' -f 1)
        cfg_value=$(echo "${idx}" | cut -d '=' -f 2)
        _update_minion_conf_ary "${cfg_key}" "${cfg_value}" || {
            _error_log "$0:${FUNCNAME[0]} error updating minion configuration "\
                "array with key '${cfg_key}' and value '${cfg_value}', "\
                "retcode '$?'";
        }
    done

    return ${_retn}
}


#
# _fetch_vmtools_salt_minion_conf_cli_args
#
#   Retrieve the configuration for salt-minion from any args '$@' passed
#                                               on the command line
#
# Results:
#   Exits with new VMTools configuration file if none found
#   or salt-minion configuration file updated with configuration read
#   from VMTools configuration file section for salt_minion
#

_fetch_vmtools_salt_minion_conf_cli_args() {
    local _retn=0
    local cli_args=""
    local cli_no_args=0

    cli_args="$*"
    cli_no_args=$#
    if [[ ${cli_no_args} -ne 0 ]]; then
        _debug_log "$0:${FUNCNAME[0]} processing command line "\
            "arguments '${cli_args}'"
        for idx in ${cli_args}
        do
            # check for start of next option, idx starts with '-' (covers '--')
            if [[ "${idx}" = --* ]]; then
                break
            fi
            cfg_key=$(echo "${idx}" | cut -d '=' -f 1)
            cfg_value=$(echo "${idx}" | cut -d '=' -f 2)
            _update_minion_conf_ary "${cfg_key}" "${cfg_value}" || {
                _error_log "$0:${FUNCNAME[0]} error updating minion "\
                "configuration array with key '${cfg_key}' and "\
                "value '${cfg_value}', retcode '$?'";
            }
        done
    fi
    return ${_retn}
}


#
# _randomize_minion_id
#
#   Added 5 digit random number to input minion identifier
#
# Input:
#       String to add random number to
#       if no input, default string 'minion_' used
#
# Results:
#   exit, return value etc
#

_randomize_minion_id() {

    local ran_minion=""
    local ip_string="$1"

    if [[ -z "${ip_string}" ]]; then
        ran_minion="minion_${RANDOM:0:5}"
    else
        #provided input
        ran_minion="${ip_string}_${RANDOM:0:5}"
    fi
    _debug_log "$0:${FUNCNAME[0]} generated randomized minion "\
            "identifier '${ran_minion}'"
    echo "${ran_minion}"
}


#
# _fetch_vmtools_salt_minion_conf
#
#   Retrieve the configuration for salt-minion
#       precedence order: L -> H
#           from VMware Tools guest Variables
#           from VMware Tools configuration file tools.conf
#           from any command line parameters
#
# Results:
#   Exits with new salt-minion configuration file written
#

_fetch_vmtools_salt_minion_conf() {
    # fetch the current configuration for section salt_minion
    # from vmtoolsd configuration file

    _debug_log "$0:${FUNCNAME[0]} retrieving minion configuration parameters"
    _fetch_vmtools_salt_minion_conf_guestvars || {
        _error_log "$0:${FUNCNAME[0]} failed to process guest variable "\
            "arguments, retcode '$?'";
    }
    _fetch_vmtools_salt_minion_conf_tools_conf || {
        _error_log "$0:${FUNCNAME[0]} failed to process tools.conf file, "\
            "retcode '$?'";
    }
    _fetch_vmtools_salt_minion_conf_cli_args "$*" || {
        _error_log "$0:${FUNCNAME[0]} failed to process command line "\
            "arguments, retcode '$?'";
    }

    # now write minion conf array to salt-minion configuration file
    local mykey_ary_sz=${#m_cfg_keys[@]}
    local myvalue_ary_sz=${#m_cfg_values[@]}
    if [[ "${mykey_ary_sz}" -ne "${myvalue_ary_sz}" ]]; then
        _error_log "$0:${FUNCNAME[0]} key '${mykey_ary_sz}' and "\
            "value '${myvalue_ary_sz}' array sizes for minion_conf "\
            "don't match"
    else
        mkdir -p "${salt_conf_dir}"
        echo "# Minion configuration file - created by VMTools Salt script" \
            > "${salt_minion_conf_file}"
        echo "enable_fqdns_grains: False" >> "${salt_minion_conf_file}"
        for ((chk_idx=0; chk_idx<mykey_ary_sz; chk_idx++))
        do
            # appending to salt-minion configuration file since it
            # should be new and no configuration set

            # check for special case of signed master's public key
            # verify_master_pubkey_sign=master_sign.pub
            if [[ "${m_cfg_keys[${chk_idx}]}" \
                    = "verify_master_pubkey_sign" ]]; then
                _debug_log "$0:${FUNCNAME[0]} processing minion "\
                    "configuration parameters for master public signed key"
                echo "${m_cfg_keys[${chk_idx}]}: True" \
                    >> "${salt_minion_conf_file}"
                mkdir -p "/etc/salt/pki/minion"
                cp -f "${m_cfg_values[${chk_idx}]}" \
                    "${salt_master_sign_dir}/"
            else
                echo "${m_cfg_keys[${chk_idx}]}: ${m_cfg_values[${chk_idx}]}" \
                    >> "${salt_minion_conf_file}"
            fi
        done
    fi

    _info_log "$0:${FUNCNAME[0]} successfully retrieved the salt-minion "\
        "configuration from configuration sources"
    return 0
}


#
# _curl_download
#
#   Retrieve file from specified url to specific file
#
# Results:
#   Exits with 0 or error code
#

_curl_download() {
    local file_name="$1"
    local file_url="$2"
    local download_retry_failed=1       # assume issues
    local _retn=0

    _info_log "$0:${FUNCNAME[0]} attempting download of file '${file_name}'"

    for ((i=0; i<CURL_DOWNLOAD_RETRY_COUNT; i++))
    do
        # ensure minimum version of TLS used is v1.2
        curl -o "${file_name}" --tlsv1.2 -fsSL "${file_url}"
        _retn=$?
        if [[ ${_retn} -ne 0 ]]; then
            _warning_log "$0:${FUNCNAME[0]} failed to download file "\
                "'${file_name}' from '${file_url}' on '${i}' attempt, "\
                "retcode '${_retn}'"
        else
            download_retry_failed=0
            _debug_log "$0:${FUNCNAME[0]} successfully downloaded file "\
                "'${file_name}' from '${file_url}' after '${i}' attempts"
            break
        fi
    done
    if [[ ${download_retry_failed} -ne 0 ]]; then
        _error_log "$0:${FUNCNAME[0]} failed to download file '${file_name}' "\
            "from '${file_url}' after '${CURL_DOWNLOAD_RETRY_COUNT}' attempts"
    fi

    _info_log "$0:${FUNCNAME[0]} successfully downloaded file "\
        "'${file_name}' from '${file_url}'"
    return 0
}


#
# _parse_json_specd_ver
#
#   Retrieve the salt-minion from Salt repository
#
# Results:
#   Echos string containing colon separated version, name and sha512
#   from parsed input repo json file
#   Echos empty '' if 'salt_url_version' is not found in repo json file
#
#   Note: salt_url_version defaults to 'latest' unless set to a specific
#       Salt minion version, for example: 3004.1-1
#

 _parse_json_specd_ver() {
    local file_name="$1"
    local file_value=""
    local blk_count=0
    local specd_ver_blk_count=0
    local specd_ver_flag=0
    local found_specd_ver_linux=0

    local var1=""
    local var2=""
    local machine_arch_chk="${MACHINE_ARCH}"
    declare -A rdict

    _info_log "$0:${FUNCNAME[0]} parsing of repo json file '${file_name}'"

    if [[ ${POST_3005_FLAG} -eq 0 ]]; then
        machine_arch_chk="amd64"    # pre_3006 used amd64
    fi

    file_value=$(<"${file_name}")

    # limit 80 cols
    var1=$(echo "${file_value}" | sed 's/,/,\n/g' | sed 's/{/\n{\n/g')
    var2=$(echo "${var1}" | sed 's/}/\n}\n/g' | sed 's/,//g' | sed 's/"//g')

    while IFS= read -r line
    do
        _debug_log "$0:${FUNCNAME[0]} parsing line '${line}'"
        if [[ -z "${line}" ]]; then
            continue
        fi
        if [[ "${line}" = "{" ]]; then
            (( blk_count++ ))
        elif [[ "${line}" = "}" ]]; then
            # examine directory just read in
            if [[  ${specd_ver_flag} -eq 1 ]]; then
                if [[ "${rdict['os']}" = "linux" \
                    && "${rdict['arch']}" = "${machine_arch_chk}" ]]; then
                    # have linux values for specd_ver
                    _debug_log "$0:${FUNCNAME[0]} parsed following linux for "\
                    "specified version '${salt_url_version}' from repo json "\
                    "file '${file_name}', os ${rdict['os']}, version "\
                    "${rdict['version']}, name ${rdict['name']}, sha512 "\
                    "${rdict['SHA512']}"
                    found_specd_ver_linux=1
                    break
                fi
            fi

            if [[ ${blk_count} -eq ${specd_ver_blk_count} ]]; then
                specd_ver_flag=0
                ## break
            fi
            (( blk_count-- ))
        else
            if [[ ${POST_3005_FLAG} -eq 1 \
                && ${POST_3005_MAJOR_VER_FLAG} -eq 1 ]]; then
                # doing major version check
                line_major_key=$(echo "${line}" | cut -d ':' -f 1 | cut -d '-' -f 2 | cut -d '.' -f 1 |xargs)
                line_key=$(echo "${line}" | cut -d ':' -f 1 | xargs)
                line_value=$(echo "${line}" | cut -d ':' -f 2 | xargs)
                _debug_log "$0:${FUNCNAME[0]} check line_major_key "\
                    "'${line_major_key}' again salt_url_version "\
                    "'${salt_url_version}', line_key '${line_key}', "\
                    "line_value '${line_value}'"
                if [[ "${line_major_key}" = "${salt_url_version}" ]]; then
                    # blk_count encountered 'specd_ver', closing brace check
                    specd_ver_flag=1
                    specd_ver_blk_count=${blk_count}
                    (( specd_ver_blk_count++ ))
                    _debug_log "$0:${FUNCNAME[0]} found specd version, "\
                        "version '${salt_url_version}' and line_major_key "\
                        "'${line_major_key}', line_key '${line_key}' "\
                        "specd_ver_blk_count '${specd_ver_blk_count}'"
                else
                    rdict["${line_key}"]="${line_value}"
                    _debug_log "$0:${FUNCNAME[0]} updated dictionary for "\
                        "major version with line_key '${line_key}' and "\
                        "line_value '${line_value}'"
                fi
            else
                line_key=$(echo "${line}" | cut -d ':' -f 1 | xargs)
                line_value=$(echo "${line}" | cut -d ':' -f 2 | xargs)
                _debug_log "$0:${FUNCNAME[0]} check line_key '${line_key}' "\
                    "again salt_url_version '${salt_url_version}', "\
                    "line_value '${line_value}'"
                if [[ "${line_key}" = "${salt_url_version}" ]]; then
                    # blk_count encountered 'specd_ver', closing brace check
                    specd_ver_flag=1
                    specd_ver_blk_count=${blk_count}
                    (( specd_ver_blk_count++ ))
                    _debug_log "$0:${FUNCNAME[0]} found specd version, "\
                        "version '${salt_url_version}' and line_key "\
                        "'${line_key}' and specd_ver_blk_count "\
                        "'${specd_ver_blk_count}'"
                else
                    rdict["${line_key}"]="${line_value}"
                    _debug_log "$0:${FUNCNAME[0]} updated dictionary with "\
                    "line_key '${line_key}' and line_value '${line_value}'"
                fi
            fi
        fi
    done <<< "${var2}"

    if [[ ${found_specd_ver_linux} -eq 1 ]]; then
        echo "${rdict['version']}:${rdict['name']}:${rdict['SHA512']}"
    else
        _error_log "$0:${FUNCNAME[0]} unable to parse version, name and "\
        "sha512 from repo json file '${file_name}'"
        # echo ""
    fi
    return 0
}


#
# _fetch_salt_minion
#
#   Retrieve the salt-minion from Salt repository
#
# Note:
#   pre_3006    The repo.json file only existed in one place (salt/onedir)
#               and contained everything in the directory and sub-directories
#               where the repo.json file resides.
#   post_3005   There are two repo.json files:
#                   top level (salt/py3/onedir):
#                       repo.json contains 'latest' and 'major versions' only.
#                   minor level (salt/py3/onedir/minor):
#                       repo.json contains 'latest' and 'minor versions' only.
#
# With the 3006 release a breaking change in directory structure was introduced
# to bring conformity with directory structure used for packages.
#
# Side Effects:
#   CURRENT_STATUS updated
#
# Results:
#   Exits with 0 or error code
#

_fetch_salt_minion() {
    # fetch the current salt-minion into specified location
    # could check if already there but by always getting it
    # ensure we are not using stale versions
    local _retn=0
    local calc_sha512sum=1
    local download_retry_failed=1       # assume issues

    local salt_pkg_name=""
    local salt_url=""
    local json_version_name_sha=""

    local local_base_url=""
    local local_file_flag=0
    local local_count_repo_json=0

    local salt_tarball=""
    local salt_tarball_SHA512=""

    local salt_json_version=""
    local salt_json_name=""
    local salt_json_sha512=""
    local salt_pkg512=""

    local install_onedir_chk=0
    local sys_arch=""

    local ver_chk=""
    local ver_chk_major=""
    local ver_chk_minor=""

    _debug_log "$0:${FUNCNAME[0]} retrieve the salt-minion and check "\
        "its validity"

    CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
    mkdir -p ${base_salt_location}
    ## cd ${base_salt_location} || return $?
    cd "${WORK_DIR}" || return $?

    # check for pre-3006 or post-3005 and adjust base_url
    # unless already defined by --source option
    if [[ -z "${base_url}" ]]; then
        _debug_log "$0:${FUNCNAME[0]} no source option used, determine "\
            "version attempting to install, version '${salt_url_version}"
        _set_post_3005_flags_from_version
    else
        _debug_log "$0:${FUNCNAME[0]} source url provided, need to scan for "\
            "pre 3006 / post 3005, and local file using base_url '${base_url}'"

        # curl on Linux doesn't support file:// support
        if echo "${base_url}" | grep -q '^/' ; then
            local_base_url="${base_url}"
            local_file_flag=1
            _debug_log "$0:${FUNCNAME[0]} using source '${local_base_url}'"\
            "from '${base_url}'"
        elif echo "${base_url}" | grep -q '^file://' ; then
            local_base_url="${base_url//file:/}"
            local_file_flag=1
            _debug_log "$0:${FUNCNAME[0]} using source '${local_base_url}'"\
            "from '${base_url}'"
        else
            _debug_log "$0:${FUNCNAME[0]} using non-local source '${base_url}'"

            ver_chk=$(echo "${base_url}" | grep  'salt/py3/onedir')
            if [[ -n "${ver_chk}" ]]; then
                POST_3005_FLAG=1
                _set_post_3005_flags_from_version
            fi
        fi
    fi

    if [[ ${local_file_flag} -eq 1 ]]; then
        # local absolute path
        # and allow for Linux handling multiple slashes

        # need to determine if pre 3005 or post 3006
        local_count_repo_json=$(find "${local_base_url}" -name repo.json|wc -l)
        if [[ ${local_count_repo_json} -eq 2 ]]; then
            POST_3005_FLAG=1
            _set_post_3005_flags_from_version
        else
            _debug_log "$0:${FUNCNAME[0]} pre-3006 local install, for "\
                "'${salt_url_version}' using specified source "\
                "'${local_base_url}'"
        fi

        if [[ ${POST_3005_FLAG} -eq 1 ]]; then
            if [[ ${POST_3005_MAJOR_VER_FLAG} -eq 1 ]]; then
                salt_url="${local_base_url}"
            else
                salt_url="${local_base_url}/minor"
            fi
        else
            salt_url="${local_base_url}/${salt_url_version}"
        fi

        if [[ -f "${salt_url}/${repo_json_file}"  ]]; then
            _debug_log "$0:${FUNCNAME[0]} successfully found file "\
            "'${repo_json_file}' in '${salt_url}/${repo_json_file}'"

            cp -a "${salt_url}/${repo_json_file}" .
            _retn=$?
            if [[ ${_retn} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} failed to find file "\
                "'${repo_json_file}' in specified location ${base_url}, "\
                "error '${_retn}'"
            fi

            # use latest from repo.json file, (version:name:sha512)
            json_version_name_sha=$(_parse_json_specd_ver "${repo_json_file}")
            salt_json_version=$(\
                echo "${json_version_name_sha}" | awk -F":" '{print $1}')
            salt_json_name=$(\
                echo "${json_version_name_sha}" | awk -F":" '{print $2}')
            salt_json_sha512=$(\
                echo "${json_version_name_sha}" | awk -F":" '{print $3}')
            _debug_log "$0:${FUNCNAME[0]} using repo.json values version "\
                "'${salt_json_version}', name '${salt_json_name}, sha512 "\
                "'${salt_json_sha512}'"

            salt_pkg_name="${salt_json_name}"
            if [[ ${POST_3005_FLAG} -eq 1 \
                && ${POST_3005_MAJOR_VER_FLAG} -eq 1 ]]; then
                cp -a "${salt_url}/minor/${salt_json_version}/${salt_pkg_name}" .
            else
                cp -a "${salt_url}/${salt_json_version}/${salt_pkg_name}" .
            fi
            _retn=$?
            if [[ ${_retn} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} failed to find file "\
                "'${salt_pkg_name}' in specified location "\
                "${salt_url}/${salt_json_version}, error '${_retn}'"
            fi
            _debug_log "$0:${FUNCNAME[0]} successfully copied from "\
                "'${salt_url}/${salt_json_version}' to file '${salt_pkg_name}'"

            salt_pkg512=$(sha512sum "${salt_pkg_name}" |awk -F" " '{print $1}')
            if [[ "${salt_pkg512}" != "${salt_json_sha512}" ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} copied file "\
                "'${salt_url}/${salt_json_version}' failed to match "\
                "checksum in file '${repo_json_file}'"
            fi
        else
            # use defaults
            # repo.json file is missing, look for 'latest'
            # directory with onedir files and retrieve files from it
            salt_url="${local_base_url}/${salt_url_version}"

            _debug_log "$0:${FUNCNAME[0]} current directory $(pwd)"

            cp -a "${salt_url}/${salt_name}"*-linux-*.tar.?z .
            _retn=$?
            if [[ ${_retn} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} failed to find file "\
                "for Linux in specified location ${salt_url}, "\
                "error '${_retn}'"
            fi

            cp -a "${salt_url}/${salt_name}"*-linux-*.tar.?z.sha512 . 2>/dev/null
            cp -a "${salt_url}/${salt_name}"*_SHA512 .
            _retn=$?
            if [[ ${_retn} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} failed to find file "\
                "sha512 in specified location ${salt_url}, "\
                "error '${_retn}'"
            fi

            ## shellcheck
            salt_chksum_file=$(ls "${salt_name}"*_SHA512)
            salt_pkg_name=$(ls "${salt_name}"*-linux-amd64.tar.gz 2>/dev/null)
            if  [[ -z "${salt_pkg_name}" ]]; then
                # failed to find pre-3006 linux tarball,
                # attempt to find post-3005 with appro. arch
                sys_arch="${MACHINE_ARCH}"
                salt_chksum_file=$(ls "${salt_name}"*-linux-"${sys_arch}".tar.xz.sha512)
                salt_pkg_name=$(ls "${salt_name}"*-linux-"${sys_arch}".tar.xz)
            fi
            _debug_log "$0:${FUNCNAME[0]} successfully copied tarball from "\
                "'${salt_url}' file '${salt_pkg_name}'"
            _debug_log "$0:${FUNCNAME[0]} successfully coped checksum from "\
                "'${salt_url}' file '${salt_chksum_file}'"
            calc_sha512sum=$(grep "${salt_pkg_name}" \
                "${salt_chksum_file}" | sha512sum --check --status)
            if [[ ${calc_sha512sum} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} downloaded file "\
                "'${salt_pkg_name}' failed to match checksum in file "\
                "'${salt_chksum_file}'"
            fi
        fi
    else
        # assume use curl for local or remote URI
        _curl_download "${repo_json_file}" "${base_url}/${repo_json_file}"
        _debug_log "$0:${FUNCNAME[0]} successfully downloaded from "\
            "'${base_url}/${repo_json_file}' into file '${repo_json_file}'"

        if [[ -f "${repo_json_file}" ]]; then
            # use latest from repo.json file, (version:name:sha512)
            json_version_name_sha=$(_parse_json_specd_ver "${repo_json_file}")
            salt_json_version=$(\
                echo "${json_version_name_sha}" | awk -F":" '{print $1}')
            salt_json_name=$(\
                echo "${json_version_name_sha}" | awk -F":" '{print $2}')
            salt_json_sha512=$(\
                echo "${json_version_name_sha}" | awk -F":" '{print $3}')
            _debug_log "$0:${FUNCNAME[0]} using repo.json values version "\
                "'${salt_json_version}', name '${salt_json_name}, sha512 "\
                "'${salt_json_sha512}'"/

            salt_pkg_name="${salt_json_name}"
            if [[ ${POST_3005_FLAG} -eq 1 \
                && ${POST_3005_MAJOR_VER_FLAG} -eq 1 ]]; then
                salt_url="${base_url}/minor/${salt_json_version}/${salt_pkg_name}"
            else
                salt_url="${base_url}/${salt_json_version}/${salt_pkg_name}"
            fi
            _curl_download "${salt_pkg_name}" "${salt_url}"
            _debug_log "$0:${FUNCNAME[0]} successfully downloaded from "\
                "'${salt_url}' into file '${salt_pkg_name}'"

            salt_pkg512=$(sha512sum "${salt_pkg_name}" |awk -F" " '{print $1}')
            if [[ "${salt_pkg512}" != "${salt_json_sha512}" ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} downloaded file '${salt_url}' "\
                    "failed to match checksum in file '${repo_json_file}'"
            fi
        else
            # use defaults
            # repo.json file is missing, look for 'latest'
            # directory with onedir files and retrieve files from it
            salt_url="${base_url}/${salt_url_version}"
            salt_tarball="${salt_name}*-linux-*.tar.?z"
            salt_tarball_SHA512="${salt_name}*_SHA512"

            # assume http://, https:// or similar
            wget -q -r -l1 -nd -np -A "${salt_tarball}" "${salt_url}"
            _retn=$?
            if [[ ${_retn} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} downloaded file "\
                "'${salt_tarball}' failed to download, error '${_retn}'"
            fi
            wget -q -r -l1 -nd -np -A "${salt_name}*_SHA512" "${salt_url}"
            _retn=$?
            if [[ ${_retn} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} downloaded file "\
                "'${salt_tarball_SHA512}' failed to download, error '${_retn}'"
            fi

            ## shellcheck
            salt_chksum_file=$(ls "${salt_name}"*_SHA512)
            salt_pkg_name=$(ls "${salt_name}"*-linux-amd64.tar.gz)
            if  [[ -z "${salt_pkg_name}" ]]; then
                # failed to find pre-3006 linux tarball,
                # attempt to find post-3005 with appro. arch
                sys_arch="${MACHINE_ARCH}"
                salt_chksum_file=$(ls "${salt_name}"*-linux-"${sys_arch}".tar.xz.sha512)
                salt_pkg_name=$(ls "${salt_name}"*-linux-"${sys_arch}".tar.xz)
            fi
            _debug_log "$0:${FUNCNAME[0]} successfully downloaded tarball "\
                "from '${salt_url}' into file '${salt_pkg_name}'"
            _debug_log "$0:${FUNCNAME[0]} successfully downloaded checksum "\
                "from '${salt_url}' into file '${salt_chksum_file}'"

            calc_sha512sum=$(grep "${salt_pkg_name}" \
                "${salt_chksum_file}" | sha512sum --check --status)
            if [[ ${calc_sha512sum} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _error_log "$0:${FUNCNAME[0]} downloaded file "\
                    "'${salt_pkg_name}' failed to match checksum in file "\
                    "'${salt_chksum_file}'"
            fi
        fi
    fi

    _debug_log "$0:${FUNCNAME[0]} sha512sum match was successful"
    if [[ ${POST_3005_FLAG} -eq 1 ]]; then
        # need to setup salt user and group if not already existing
        _debug_log "$0:${FUNCNAME[0]} setup salt user and group if not "\
            "already existing"
        _SALT_GROUP=salt
        _SALT_USER=salt
        _SALT_NAME=Salt
        # 1. create group if not existing
        if getent group "${_SALT_GROUP}" 1>/dev/null; then
            _debug_log "$0:${FUNCNAME[0]} already group salt, assume user "\
                "and group setup for Salt"
        else
            _debug_log "$0:${FUNCNAME[0]} setup group and user salt"
            # create user to avoid running server as root
            # 1. create group if not existing
            groupadd --system "${_SALT_GROUP}" 2>/dev/null
            # 2. create homedir if not existing
            if [[ ! -d "${salt_dir}" ]]; then
                mkdir -p "${salt_dir}"
            fi
            # 3. create user if not existing
            if ! getent passwd | grep -q "^${_SALT_USER}:"; then
              useradd --system --no-create-home -s /sbin/nologin -g \
                "${_SALT_GROUP}" "${_SALT_USER}" 2>/dev/null
            fi
            # 4. adjust passwd entry
            usermod -c "${_SALT_NAME}" -d "${salt_dir}" -g "${_SALT_GROUP}" \
                "${_SALT_USER}" 2>/dev/null
        fi
        tar xf "${salt_pkg_name}" -C "${base_salt_location}" 1>/dev/null
        # 5. adjust file and directory permissions
        chown -R "${_SALT_USER}":"${_SALT_GROUP}" "${salt_dir}"
    else
        tar xf "${salt_pkg_name}" -C "${base_salt_location}" 1>/dev/null
    fi
    _retn=$?
    if [[ ${_retn} -ne 0 ]]; then
        CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
        _error_log "$0:${FUNCNAME[0]} tar xf expansion of downloaded "\
            "file '${salt_pkg_name}' failed, return code '${_retn}'"
    fi
    install_onedir_chk=$(_check_onedir_minion_install)
    if [[ ${install_onedir_chk} -eq 0 ]]; then
        CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
        _error_log "$0:${FUNCNAME[0]} expansion of downloaded file "\
            "'${salt_url}' failed to provide any onedir installed "\
            "critical files for salt-minion"
    fi
    CURRENT_STATUS=${STATUS_CODES_ARY[installed]}
    cd "${CURRDIR}" || return $?

    _info_log "$0:${FUNCNAME[0]} successfully retrieved salt-minion"
    return 0
}


#
# _check_multiple_script_running
#
#   check if more than one version of the script is running
#
# Results:
#   Checks the number of scripts running, allowing for forks etc
#   from bash etc, as root a single instance of the script returns 3
#   as sudo root a single instance of the script returns 4
#

_check_multiple_script_running() {
    local count=0
    local procs_found=""

    _info_log "$0:${FUNCNAME[0]} checking how many versions of the "\
        "script are running"

    procs_found=$(pgrep -f "${SCRIPTNAME}")
    count=$(echo "${procs_found}" | wc -l)

    _debug_log "$0:${FUNCNAME[0]} checking versions of script are running, "\
        "bashpid '${BASHPID}', processes found '${procs_found}', "\
        "and count '${count}'"

    if [[ ${count} -gt 4 ]]; then
        _error_log "$0:${FUNCNAME[0]} failed to check status, "\
            "multiple versions of the script are running"
    fi

    return 0
}


#
# _check_classic_minion_install
#
# Check if classic salt-minion is installed for the OS
#   for example: install salt-minion from rpm or deb package
#
# Results:
#   0 - No standard install found and empty string output
#   !0 - Standard install found and Salt version found output
#

_check_classic_minion_install() {

    # checks for /usr/bin, then /usr/local/bin
    # this catches 80% to 90%  of the regular cases
    # if salt-call is there, then so is a salt-minion
    # as they are installed together

    local _retn=0
    local max_file_sz=200
    local list_of_files_check="
/usr/bin/salt-call
/usr/local/bin/salt-call
"
    _info_log "$0:${FUNCNAME[0]} check if standard salt-minion installed"

    for idx in ${list_of_files_check}
    do
        if [[ -h "${idx}" ]]; then
            _debug_log "$0:${FUNCNAME[0]} found file '${idx}' "\
                "symbolic link, post-3005 installation"
            break
        elif [[ -f "${idx}" ]]; then
            #check size of file, if larger than 200, not script wrapper file
            local file_sz=0
            file_sz=$(( $(wc -c < "${idx}") ))
            _debug_log "$0:${FUNCNAME[0]} found file '${idx}', "\
                "size '${file_sz}'"
            if [[ ${file_sz} -gt ${max_file_sz} ]]; then
                # get salt-version
                local s_ver=""
                s_ver=$("${idx}" --local test.version |grep -v 'local:' |xargs)
                _debug_log "$0:${FUNCNAME[0]} found standard salt-minion, "\
                    "Salt version: '${s_ver}'"
                echo "${s_ver}"
                _retn=1
                break
            fi
        fi
    done
    echo ""
    return ${_retn}
}


#
# _check_onedir_minion_install
#
# Check if onedir pre_3006 or post_3005 salt-minion is installed on the OS
#   for example: install salt-minion from rpm or deb package
#
# Results:
#   Echos the following values:
#   0 - No onedir install found and empty string output
#   1 - pre_3006 onedir install found
#   2 - post_3005 onedir install found
#

_check_onedir_minion_install() {

    # checks for following executables:
    # post_3005 - /opt/saltstack/salt/salt-minion
    # pre_3006  - /opt/saltstack/salt/run/run

    local _retn=0
    local pre_3006=1
    local post_3005=2

    _info_log "$0:${FUNCNAME[0]} check if standard onedir-minion installed"

    if [[ -f "${list_of_onedir_locations_check[0]}" ]]; then
        _debug_log "$0:${FUNCNAME[0]} found pre 3006 version of Salt, "\
                    "at location ${list_of_onedir_locations_check[0]}"
        _retn=${pre_3006}
    elif [[ -f "${list_of_onedir_locations_check[1]}" ]]; then
        _debug_log "$0:${FUNCNAME[0]} found post 3005 version of Salt, "\
                    "at location ${list_of_onedir_locations_check[1]}"
        _retn=${post_3005}
    else
        _debug_log "$0:${FUNCNAME[0]} failed to find a onedir installation"
    fi
    echo ${_retn}
}


#
# _find_salt_pid
#
#   finds the pid for the Salt process
#
# Results:
#   Echos ${salt_pid} which could be empty '' if Salt process not found
#

_find_salt_pid() {
    # find the pid for salt-minion if active
    local salt_pid=0
    if [[ ${POST_3005_FLAG} -eq 1 ]]; then
        salt_pid=$(pgrep -f "\/usr\/bin\/salt-minion" | head -n 1)
    else
        salt_pid=$(pgrep -f "${salt_name}\/run\/run minion" | head -n 1 |
            awk -F " " '{print $1}')
    fi
    _debug_log "$0:${FUNCNAME[0]} checking for salt-minion process id, "\
        "found '${salt_pid}'"
    echo "${salt_pid}"
}

#
# _ensure_id_or_fqdn
#
#   Ensures that a valid minion identifier has been specified, and if not a
#   valid Fully Qualified Domain Name exists (not default Unknown.example.org)
#   else generates a minion id to use.
#
# Note: this function should only be run before starting the salt-minion
#       via systemd after it has been installed
#
# Side Effect:
#   Updates salt-minion configuration file with generated identifier
#       if no valid FQDN
#
# Results:
#   salt-minion configuration contains a valid identifier or FQDN to use.
#   Exits with 0
#

_ensure_id_or_fqdn () {
    # ensure minion id or fqdn for salt-minion

    local minion_fqdn=""

    # quick check if id specified
    if grep -q '^id:' < "${salt_minion_conf_file}"; then
        _debug_log "$0:${FUNCNAME[0]} salt-minion identifier found, no "\
            "need to check further"
        return 0
    fi

    _debug_log "$0:${FUNCNAME[0]} ensuring salt-minion identifier or "\
        "FQDN is specified for salt-minion configuration"
    minion_fqdn=$(/usr/bin/salt-call --local grains.get fqdn |
        grep -v 'local:' | xargs)
    if [[ -n "${minion_fqdn}" &&
        "${minion_fqdn}" != "Unknown.example.org" ]]; then
        _debug_log "$0:${FUNCNAME[0]} non-default salt-minion FQDN "\
            "'${minion_fqdn}' is specified for salt-minion configuration"
        return 0
    fi

    # default FQDN, no id is specified, generate one and update conf file
    local minion_genid=""
    minion_genid=$(_generate_minion_id)
    echo "id: ${minion_genid}" >> "${salt_minion_conf_file}"
    _debug_log "$0:${FUNCNAME[0]} no salt-minion identifier found, "\
        "generated identifier '${minion_genid}'"

    return 0
}


#
# _create_pre_3006_helper_scripts
#
#   Create helper scripts for salt-call and salt-minion
#
#       Example: _create_pre_3006_helper_scripts
#
# Results:
#   Exits with 0 or error code
#

_create_pre_3006_helper_scripts() {

    for idx in ${salt_wrapper_file_list}
    do
        local abs_filepath=""
        abs_filepath="/usr/bin/salt-${idx}"

        _debug_log "$0:${FUNCNAME[0]} creating helper file 'salt-${idx}' "\
            "in directory /usr/bin"

        echo "#!/usr/bin/env bash

# Copyright (c) 2021 VMware, Inc. All rights reserved.
" > "${abs_filepath}" || {
            _error_log "$0:${FUNCNAME[0]} failed to create helper file "\
                "'salt-${idx}' in directory /usr/bin, retcode '$?'";
        }
        {
            echo -n "exec /opt/saltstack/salt/run/run ${idx}";
            echo -n "\"$";
            echo -n "{";
            echo -n "@";
            echo -n ":";
            echo -n "1}";
            echo -n "\"";
        } >> "${abs_filepath}" || {
            _error_log "$0:${FUNCNAME[0]} failed to finish creating helper "\
                "file 'salt-${idx}' in directory /usr/bin, retcode '$?'";
        }
        echo  "" >> "${abs_filepath}"

        # ensure executable
        chmod 755 "${abs_filepath}" || {
            _error_log "$0:${FUNCNAME[0]} failed to make helper file "\
                "'salt-${idx}' executable in directory /usr/bin, retcode '$?'";
        }
    done

}


#
# _status_fn
#
#   discover and return the current status
#
#       0 => installed
#       1 => installing
#       2 => notInstalled
#       3 => installFailed
#       4 => removing
#       5 => removeFailed
#       6 => externalInstall
#       126 => scriptFailed
#
# Side Effects:
#   CURRENT_STATUS updated
#
# Results:
#   Exits numerical status
#

_status_fn() {
    # return status
    local _retn_status=${STATUS_CODES_ARY[notInstalled]}
    local install_onedir_chk=0
    local found_salt_ver=""

    _info_log "$0:${FUNCNAME[0]} checking status for script"

    _check_multiple_script_running

    found_salt_ver=$(_check_classic_minion_install)
    if [[ -n "${found_salt_ver}" ]]; then
        _debug_log "$0:${FUNCNAME[0]}" \
            "existing Standard Salt Installation detected, "\
            "Salt version: '${found_salt_ver}'"
            CURRENT_STATUS=${STATUS_CODES_ARY[externalInstall]}
            _retn_status=${STATUS_CODES_ARY[externalInstall]}
    else
        _debug_log "$0:${FUNCNAME[0]} no standardized install found"

        install_onedir_chk=$(_check_onedir_minion_install)
        if [[ ${install_onedir_chk} -eq 2 ]]; then
            POST_3005_FLAG=1    # ensure note 3006 and above
        fi

        svpid=$(_find_salt_pid)
        if [[ ${install_onedir_chk} -eq 0 && -z ${svpid} ]]; then
            # check not installed and no process id
            CURRENT_STATUS=${STATUS_CODES_ARY[notInstalled]}
            _retn_status=${STATUS_CODES_ARY[notInstalled]}
        elif [[ ${install_onedir_chk} -ne 0 ]]; then
            # check installed
            CURRENT_STATUS=${STATUS_CODES_ARY[installed]}
            _retn_status=${STATUS_CODES_ARY[installed]}
            # normal case but double-check
            svpid=$(_find_salt_pid)
            if [[ -z ${svpid} ]]; then
                # Note: someone could have stopped the salt-minion,
                # so installed but not running,
                # status codes don't allow for that case
                CURRENT_STATUS=${STATUS_CODES_ARY[installFailed]}
                _retn_status=${STATUS_CODES_ARY[installFailed]}
            fi
        elif [[ -z ${svpid} ]]; then
            # check no process id and main directory still left, =>removeFailed
            if [[ ${install_onedir_chk} -ne 0 ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[removeFailed]}
                _retn_status=${STATUS_CODES_ARY[removeFailed]}
            fi
        fi
    fi

    return ${_retn_status}
}


#
# _deps_chk_fn
#
#   Check dependencies for using salt-minion
#
# Side Effects:
# Results:
#   Exits with 0 or error code
#
_deps_chk_fn() {
    # return dependency check
    local error_missing_deps=""

    _info_log "$0:${FUNCNAME[0]} checking script dependencies"
    for idx in ${salt_dep_file_list}
    do
        command -v "${idx}" 1>/dev/null || {
            if [[ -z "${error_missing_deps}" ]]; then
                error_missing_deps="${idx}"
            else
                error_missing_deps="${error_missing_deps} ${idx}"
            fi
        }
    done
    if [[ -n "${error_missing_deps}" ]]; then
        _error_log "$0:${FUNCNAME[0]} failed to find required "\
            "dependencies '${error_missing_deps}'";
    fi
    return 0
}

#
# _find_system_lib_path
#
# find with systemd library path to use
#
# Result:
#   echos the systemd library path
#   will error if no systemd library path can be determined
#
# Note:
#   /lib/systemd/system
#       System units installed by the distribution package manage
#   /usr/lib/systemd/system
#       System units installed by the Administrator
#   /usr/local/lib/systemd/system
#       System units installed by the Administrator (possible on some OS)
#
# Will use /usr/lib/systemd/system available, since this is generally
# the default used on modern Linux OS by salt-minion, some earlier OS's
# (Debian 9, Ubuntu 18.04) use /lib/systemd/system
#
_find_system_lib_path () {

    local path_found=""
    _info_log "$0:${FUNCNAME[0]} finding systemd library path to use"
    if [[ -d "/usr/lib/systemd/system" ]]; then
        path_found="/usr/lib/systemd/system"
    elif [[ -d "/lib/systemd/system" ]]; then
        path_found="/lib/systemd/system"
    elif [[ -d "/usr/local/lib/systemd/system" ]]; then
        path_found="/usr/local/lib/systemd/system"
    else
        _error_log "$0:${FUNCNAME[0]} unable to determine systemd "\
        "library path to use"
    fi
    _debug_log "$0:${FUNCNAME[0]} found library path to use ${path_found}"
    echo "${path_found}"
}


#
#  _install_fn
#
#   Executes scripts to install Salt from Salt repository
#       and start the salt-minion using systemd
#
# Results:
#   Exits with 0 or error code
#

_install_fn () {
    # execute install of Salt minion
    local _retn=0
    local existing_chk=""
    local found_salt_ver=""
    local install_onedir_chk=0

    _info_log "$0:${FUNCNAME[0]} processing script install"

    _check_multiple_script_running

    found_salt_ver=$(_check_classic_minion_install)
    if [[ -n "${found_salt_ver}" ]]; then
        _warning_log "$0:${FUNCNAME[0]} failed to install, "\
            "existing Standard Salt Installation detected, "\
            "Salt version: '${found_salt_ver}'"
        CURRENT_STATUS=${STATUS_CODES_ARY[externalInstall]}
        exit ${STATUS_CODES_ARY[externalInstall]}
    else
        _debug_log "$0:${FUNCNAME[0]} no standardized install found"
    fi

    # check if salt-minion or salt-master (salt-cloud etc req master)
    # and log warning that they will be overwritten
    existing_chk=$(pgrep -l "salt-minion|salt-master" | cut -d ' ' -f 2 | uniq)
    if [[ -n  "${existing_chk}" ]]; then
        for idx in ${existing_chk}
        do
            local salt_fn=""
            salt_fn="$(basename "${idx}")"
            _warning_log "$0:${FUNCNAME[0]} existing Salt functionality "\
                "${salt_fn} shall be stopped and replaced when new "\
                "salt-minion is installed"
        done
    fi

    # fetch salt-minion from repository
    _fetch_salt_minion || {
        _error_log "$0:${FUNCNAME[0]} failed to fetch salt-minion "\
            "from repository , retcode '$?'";
    }

    # get configuration for salt-minion
    _fetch_vmtools_salt_minion_conf "$@" || {
        _error_log "$0:${FUNCNAME[0]} failed, read configuration for "\
            "salt-minion, retcode '$?'";
    }

    if [[ ${_retn} -eq 0 && -f "${onedir_pre_3006_location}" ]]; then
        # create helper scripts for /usr/bin to ensure they are present
        # before attempting to use them in _ensure_id_or_fqdn
        # this is for earlier than 3006 versions of Salt onedir
        _debug_log "$0:${FUNCNAME[0]} creating helper files salt-call "\
            "and salt-minion in directory /usr/bin"
        _create_pre_3006_helper_scripts || {
            _error_log "$0:${FUNCNAME[0]} failed to create helper files "\
                "salt-call or salt-minion in directory /usr/bin, retcode '$?'";
        }
    elif [[ ${_retn} -eq 0 && -f "${onedir_post_3005_location}" ]]; then
        # create symbolic links for /usr/bin to ensure they are present
        _debug_log "$0:${FUNCNAME[0]} creating symbolic links for salt-call "\
            "and salt-minion in directory /usr/bin"
        ln -s -f "${salt_dir}/salt-minion" "/usr/bin/salt-minion" || {
            _error_log "$0:${FUNCNAME[0]} failed to create symbolic link "\
                "for salt-minion in directory /usr/bin, retcode '$?'";
        }
        ln -s -f "${salt_dir}/salt-call" "/usr/bin/salt-call" || {
            _error_log "$0:${FUNCNAME[0]} failed to create symbolic link "\
                "for salt-call in directory /usr/bin, retcode '$?'";
        }
    else
        _error_log "$0:${FUNCNAME[0]} problems creating helper files "\
            "or symbolic links for salt-call or salt-minion in "\
            "directory /usr/bin, should not have reached this code point";
    fi

    # ensure minion id or fqdn for salt-minion
    _ensure_id_or_fqdn
    install_onedir_chk=$(_check_onedir_minion_install)

    if [[ ${_retn} -eq 0 && ${install_onedir_chk} -ne 0 ]]; then
        if [[ -n  "${existing_chk}" ]]; then
            # be nice and stop any current Salt functionality found
            for idx in ${existing_chk}
            do
                local salt_fn=""
                salt_fn="$(basename "${idx}")"
                _warning_log "$0:${FUNCNAME[0]} stopping Salt functionality "\
                    "${salt_fn} it's replaced with new installed salt-minion"
                systemctl stop "${salt_fn}" || {
                    _warning_log "$0:${FUNCNAME[0]} stopping existing Salt "\
                        "functionality ${salt_fn} encountered difficulties "\
                        "using systemctl, it will be over-written with the "\
                        "new installed salt-minion regardless, retcode '$?'";
                }
            done
        fi

        # install salt-minion systemd service script
        # first find with systemd library path to use
        local systemd_lib_path=""
        systemd_lib_path=$(_find_system_lib_path)
        local name_service="salt-minion.service"
        _debug_log "$0:${FUNCNAME[0]} copying systemd service script "\
            "${name_service} to directory ${systemd_lib_path}"
        echo "${salt_minion_service_wrapper}" \
            > "${systemd_lib_path}/${name_service}" || {
            _error_log "$0:${FUNCNAME[0]} failed to copy systemd service "\
                "file ${name_service} to directory "\
                "${systemd_lib_path}, retcode '$?'";
        }

        cd "${CURRDIR}" || return $?

        # start the salt-minion using systemd
        systemctl daemon-reload || {
            _error_log "$0:${FUNCNAME[0]} reloading the systemd daemon "\
                "failed , retcode '$?'";
        }
        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "daemon-reload"
        systemctl restart "${name_service}" || {
            _error_log "$0:${FUNCNAME[0]} starting the salt-minion using "\
                "systemctl failed , retcode '$?'";
        }
        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "restart '${name_service}'"
        systemctl enable "${name_service}" || {
            _error_log "$0:${FUNCNAME[0]} enabling the salt-minion using "\
                "systemctl failed , retcode '$?'";
        }
        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "enable '${name_service}'"
    fi
    return ${_retn}
}


#
#  _source_fn
#
#   Set the location to retrieve the Salt Minion from
#       default is to use the Salt Project repository
#
#   Set the version of Salt Minion wanted to install
#       default 'latest'
#
#   Note: handle all protocols (http, https, ftp, file, unc, etc)
#           for example:
#               http://my_web_server.com/my_salt_onedir
#               https://my_web_server.com/my_salt_onedir
#               ftp://my_ftp_server.com/my_salt_onedir
#               file://mytopdir/mymiddledir/my_salt_onedir
#               ///mytopdir/mymiddledir/my_salt_onedir
#
#           If a specific version of the Salt Minion is specified
#           then it will be appended to the specified source location
#           otherwise a default of 'latest' is applied.
#
# Results:
#   Exits with 0 or error code
#

_source_fn () {
    local _retn=0
    local salt_source=""

    if [[ $# -ne 1 ]]; then
        _error_log "$0:${FUNCNAME[0]} error expected one parameter "\
            "specifying the source for location of onedir files"
    fi

    _info_log "$0:${FUNCNAME[0]} processing script source for location "\
        "of onedir files"

    salt_source=$(echo "$1" | cut -d ' ' -f 1)
    _debug_log "$0:${FUNCNAME[0]} input Salt source is '${salt_source}'"

    if [[ -n "${salt_source}" ]]; then
        base_url=${salt_source}
    fi
    _debug_log "$0:${FUNCNAME[0]} input Salt source for salt-minion to "\
            "install from is '${base_url}'"

    return ${_retn}
}


#
# _generate_minion_id
#
#   Searches salt-minion configuration file for current id, and disables it
#   and generates a new id based on the existing id found,
#   or an older commented out id, and provides it with a randomized 5 digit
#   post-pended to it, for example: myminion_12345
#
#   If no previous id found, a generated minion_<random number> is output
#
# Side Effects:
#   Disables any id found in minion configuration file
#
# Result:
#   Outputs randomized minion id for use in a minion configuration file
#   Exits with 0 or error code
#

_generate_minion_id () {

    local salt_id_flag=0
    local minion_id=""
    local cfg_value=""
    local ifield=""
    local tfields=""

    _debug_log "$0:${FUNCNAME[0]} generating a salt-minion identifier"

    # always comment out what was there
    sed -i 's/^id:/# id:/g' "${salt_minion_conf_file}"

    while IFS= read -r line
    do
        line_value=$(_trim "${line}")
        if [[ -n "${line_value}" ]]; then
            if echo "${line_value}" | grep -q '^# id:' ; then
                # get value and write out value_<random>
                cfg_value=$(echo "${line_value}" | cut -d ' ' -f 3)
                if [[ -n "${cfg_value}" ]]; then
                    salt_id_flag=1
                    minion_id=$(_randomize_minion_id "${cfg_value}")
                    _debug_log "$0:${FUNCNAME[0]} found previously used id "\
                        "field, randomizing it"
                fi
            elif echo "${line_value}" | grep -q -w 'id:' ; then
                # might have commented out id, get value and
                # write out value_<random>
                tfields=$(echo "${line_value}"|awk -F ':' '{print $2}'|xargs)
                ifield=$(echo "${tfields}" | cut -d ' ' -f 1)
                if [[ -n ${ifield} ]]; then
                    minion_id=$(_randomize_minion_id "${ifield}")
                    salt_id_flag=1
                    _debug_log "$0:${FUNCNAME[0]} found previously used "\
                        "id field, randomizing it"
                fi
            else
                _debug_log "$0:${FUNCNAME[0]} skipping line '${line}'"
            fi
        fi
    done < "${salt_minion_conf_file}"

    if [[ ${salt_id_flag} -eq 0 ]]; then
        # no id field found, write minion_<random?
        _debug_log "$0:${FUNCNAME[0]} no previous id field found, "\
            "generating new identifier"
        minion_id=$(_randomize_minion_id)
    fi
    _debug_log "$0:${FUNCNAME[0]} generated a salt-minion "\
        "identifier '${minion_id}'"
    echo "${minion_id}"
    return 0
}


#
# _clear_id_key_fn
#
#   Executes scripts to clear the minion identifier and keys and
#   re-generates new identifier, allows for a VM containing a salt-minion,
#   to be cloned and not have conflicting id and keys
#   salt-minion is stopped, id and keys cleared, and restarted
#   if it was previously running
#
# Input:
#   Optional specified input ID to be used, default generate randomized value
#
# Note:
#   Normally a salt-minion if no id is specified will rely on
#   it's Fully Qualified Domain Name but with VM Cloning, there is no surety
#   that the FQDN will have been altered, and duplicates can occur.
#   Also if there is no FQDN, then default 'Unknown.example.org' is used,
#   again with the issue of duplicates for multiple salt-minions
#   with no FQDN specified
#
# Side Effects:
#   New minion identifier in configuration file and keys for the salt-minion
#
# Results:
#   Exits with 0 or error code
#

_clear_id_key_fn () {
    # execute clearing of Salt minion id and keys
    local _retn=0
    local salt_minion_pre_active_flag=0
    local salt_id_flag=0
    local minion_id=""
    local minion_ip_id=""
    local install_onedir_chk=0

    _info_log "$0:${FUNCNAME[0]} processing clearing of salt-minion "\
        "identifier and its keys"

    _check_multiple_script_running

    install_onedir_chk=$(_check_onedir_minion_install)
    if [[ ${install_onedir_chk} -eq 0 ]]; then
        _debug_log "$0:${FUNCNAME[0]} salt-minion is not installed, "\
            "nothing to do"
        return ${_retn}
    fi

    # get any minion identifier in case specified
    minion_ip_id=$(echo "$1" | cut -d ' ' -f 1)
    svpid=$(_find_salt_pid)
    if [[ -n ${svpid} ]]; then
        # stop the active salt-minion using systemd
        # and give it a little time to stop
        systemctl stop salt-minion || {
            _error_log "$0:${FUNCNAME[0]} failed to stop salt-minion "\
                "using systemctl, retcode '$?'";
        }
        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "stop salt-minion"
        salt_minion_pre_active_flag=1
    fi

    rm -fR "${salt_conf_dir}/minion_id"
    rm -fR "${salt_conf_dir}/pki/${salt_minion_conf_name}"
    # always comment out what was there
    sed -i 's/^id/# id/g' "${salt_minion_conf_file}"
    _debug_log "$0:${FUNCNAME[0]} removed '${salt_conf_dir}/minion_id' "\
        "and '${salt_conf_dir}/pki/${salt_minion_conf_name}', and "\
        "commented out id in '${salt_minion_conf_file}'"

    if [[ -z "${minion_ip_id}" ]] ;then
        minion_id=$(_generate_minion_id)
    else
        minion_id="${minion_ip_id}"
    fi

    # add new minion id to bottom of minion configuration file
    echo "id: ${minion_id}" >> "${salt_minion_conf_file}"
    _debug_log "$0:${FUNCNAME[0]} updated salt-minion identifier "\
        "'${minion_id}' in configuration file '${salt_minion_conf_file}'"

    if [[ ${salt_minion_pre_active_flag} -eq 1 ]]; then
        # restart the stopped salt-minion using systemd
        systemctl restart salt-minion || {
            _error_log "$0:${FUNCNAME[0]} failed to restart salt-minion "\
                "using systemctl, retcode '$?'";
        }

        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "restart salt-minion"
    fi

    return ${_retn}
}


#
# _remove_installed_files_dirs
#
#   Removes all Salt files and directories that may be used
#
# Results:
#   Exits with 0 or error code
#

_remove_installed_files_dirs() {
    _debug_log "$0:${FUNCNAME[0]} removing directories and files "\
        "in '${list_file_dirs_to_remove}'"
    for idx in ${list_file_dirs_to_remove}
    do
        rm -fR "${idx}" || {
            _error_log "$0:${FUNCNAME[0]} failed to remove file or "\
                "directory '${idx}' , retcode '$?'";
        }
    done
    return 0
}


#
#  _uninstall_fn
#
#   Executes scripts to uninstall Salt from system
#       stopping the salt-minion using systemd
#
# Side Effects:
#   CURRENT_STATUS updated
#
# Results:
#   Exits with 0 or error code
#

_uninstall_fn () {
    # remove Salt minion
    local _retn=0
    local found_salt_ver=""
    local install_onedir_chk=0

    _info_log "$0:${FUNCNAME[0]} processing script remove"

    _check_multiple_script_running

    found_salt_ver=$(_check_classic_minion_install)
    if [[ -n "${found_salt_ver}" ]]; then
        _warning_log "$0:${FUNCNAME[0]} failed to install, "\
            "existing Standard Salt Installation detected, "\
            "Salt version: '${found_salt_ver}'"
        CURRENT_STATUS=${STATUS_CODES_ARY[externalInstall]}
        exit ${STATUS_CODES_ARY[externalInstall]}
    else
        _debug_log "$0:${FUNCNAME[0]} no standardized install found"
    fi

    install_onedir_chk=$(_check_onedir_minion_install)
    if [[ ${install_onedir_chk} -eq 0 ]]; then
        CURRENT_STATUS=${STATUS_CODES_ARY[notInstalled]}

        # assume rest is gone
        # TBD enhancement, could loop thru and check all of files to remove
        # and if salt_pid empty but we error out if issues when uninstalling,
        # so safe for now.
        _retn=0
    else
        CURRENT_STATUS=${STATUS_CODES_ARY[removing]}
        # remove salt-minion from systemd
        # and give it a little time to stop
        systemctl stop salt-minion || {
            _error_log "$0:${FUNCNAME[0]} failed to stop salt-minion "\
                "using systemctl, retcode '$?'";
        }
        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "stop salt-minion"
        systemctl disable salt-minion || {
            _error_log "$0:${FUNCNAME[0]} disabling the salt-minion "\
                "using systemctl failed , retcode '$?'";
        }
        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "disable salt-minion"

        _debug_log "$0:${FUNCNAME[0]} removing systemd directories and files "\
            "in '${list_files_systemd_to_remove}'"
        for idx in ${list_files_systemd_to_remove}
        do
            rm -fR "${idx}" || {
                _error_log "$0:${FUNCNAME[0]} failed to remove file or "\
                    "directory '${idx}' , retcode '$?'";
            }
        done

        systemctl daemon-reload || {
            _error_log "$0:${FUNCNAME[0]} reloading the systemd daemon "\
                "failed , retcode '$?'";
        }
        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "daemon-reload"
        systemctl reset-failed || {
            _error_log "$0:${FUNCNAME[0]} reloading the systemd daemon "\
                "failed , retcode '$?'";
        }
        _debug_log "$0:${FUNCNAME[0]} successfully executed systemctl "\
            "reset-failed"

        if [[ ${_retn} -eq 0 ]]; then
            svpid=$(_find_salt_pid)
            if [[ -n ${svpid} ]]; then
                _debug_log "$0:${FUNCNAME[0]} found salt-minion process "\
                    "id '${salt_pid}', systemctl stop should have "\
                    "eliminated it, killing it now"
                kill "${svpid}"
                ## given it a little time
                sleep 5
            fi
            svpid=$(_find_salt_pid)
            if [[ -n ${svpid} ]]; then
                CURRENT_STATUS=${STATUS_CODES_ARY[removeFailed]}
                _error_log "$0:${FUNCNAME[0]} failed to kill the "\
                    "salt-minion, pid '${svpid}' during uninstall"
            else
                _remove_installed_files_dirs || {
                    _error_log "$0:${FUNCNAME[0]} failed to remove all "\
                        "installed salt-minion files and directories, "\
                        "retcode '$?'";
                }
                CURRENT_STATUS=${STATUS_CODES_ARY[notInstalled]}
            fi
        fi
    fi

    _info_log "$0:${FUNCNAME[0]} successfully removed salt-minion and "\
        "associated files and directories"
    return ${_retn}
}


#
#  _clean_up_log_files
#
#   Limits number of log files by removing oldest log files which exceed
#   limit LOG_FILE_NUMBER
#
# Results:
#   Exits with 0 or error code
#
_clean_up_log_files() {

    _info_log "$0:${FUNCNAME[0]} removing and limiting log files"
    for idx in ${allowed_log_file_action_names}
    do
        local count_f=0
        local found_f=""
        local -a found_f_ary
        found_f=$(ls -t "${log_dir}/vmware-${SCRIPTNAME}-${idx}"* 2>/dev/null)
        count_f=$(echo "${found_f}" | wc | awk -F" " '{print $2}')
        mapfile -t found_f_ary <<< "${found_f}"

        if [[ ${count_f} -gt ${LOG_FILE_NUMBER} ]]; then
            # allow for org-0
            for ((i=count_f-1; i>=LOG_FILE_NUMBER; i--)); do
                _debug_log "$0:${FUNCNAME[0]} removing log file "\
                    "'${found_f_ary[i]}', for count '${i}', "\
                    "limit '${LOG_FILE_NUMBER}'"
                rm -f "${found_f_ary[i]}" || {
                    _error_log "$0:${FUNCNAME[0]} failed to remove file "\
                    "'${found_f_ary[i]}', for count '${i}', "\
                    "limit '${LOG_FILE_NUMBER}'"
                }
            done
        else
            _debug_log "$0:${FUNCNAME[0]} found '${count_f}' "\
                "log files starting with "\
                "${log_dir}/vmware-${SCRIPTNAME}-${idx}-, "\
                "limit '${LOG_FILE_NUMBER}'"
        fi
    done
    return 0
}

################################### MAIN ####################################

# static definitions

CURRDIR=$(pwd)

# get machine architecture once
MACHINE_ARCH=$(uname -m)

# setup work-area
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# the temp working directory used, within $DIR
WORK_DIR=$(mktemp -d -p "$DIR")

# check if temp working dir was created
if [[ ! "${WORK_DIR}" || ! -d "${WORK_DIR}" ]]; then
  echo "Could not create temp dir"
  exit 1
fi

# default status is notInstalled
CURRENT_STATUS=${STATUS_CODES_ARY[notInstalled]}
export CURRENT_STATUS

## build date-time tag used for logging UTC YYYYMMDDhhmmss
## YearMontDayHourMinuteSecondMicrosecond aka jid
logdate=$(date -u +%Y%m%d%H%M%S)

# set logging information
LOG_FILE_NUMBER=5
SCRIPTNAME=$(basename "$0")
mkdir -p "${log_dir}"

# set to action e.g. 'remove', 'install'
# default is for any logging not associated with a specific action
# for example: debug logging and --version
LOG_ACTION="default"

CLI_ACTION=0

while true; do
    if [[ -z "$1" ]]; then break; fi
    case "$1" in
        -c | --clear )
            CLEAR_ID_KEYS_FLAG=1;
            shift;
            CLEAR_ID_KEYS_PARAMS=$*;
            ;;
        -d | --depend )
            DEPS_CHK=1;
            shift;
            ;;
        -h | --help )
            USAGE_HELP=1;
            shift;
            ;;
        -i | --install )
            INSTALL_FLAG=1;
            shift;
            INSTALL_PARAMS="$*";
            ;;
        -j | --source )
            SOURCE_FLAG=1;
            shift;
            SOURCE_PARAMS="$*";
            ;;
        -l | --loglevel )
            LOG_LEVEL_FLAG=1;
            shift;
            LOG_LEVEL_PARAMS="$*";
            ;;
        -m | --minionversion )
            MINION_VERSION_FLAG=1;
            shift;
            MINION_VERSION_PARAMS="$*";
            ;;
        -r | --remove )
            UNINSTALL_FLAG=1;
            shift;
            ;;
        -s | --status )
            STATUS_CHK=1;
            shift;
            ;;
        -v | --version )
            VERSION_FLAG=1;
            shift;
            ;;
        -- )
            shift;
            break;
            ;;
        * )
            shift;
            ;;
    esac
done

## check if want help, display usage and exit
if [[ ${USAGE_HELP} -eq 1 ]]; then
  _usage
  exit 0
fi


##  MAIN BODY OF SCRIPT

retn=0

if [[ ${LOG_LEVEL_FLAG} -eq 1 ]]; then
    # ensure logging level changes are processed before any actions
    CLI_ACTION=1
    _set_log_level "${LOG_LEVEL_PARAMS}"
    retn=$?
fi
if [[ ${STATUS_CHK} -eq 1 ]]; then
    CLI_ACTION=1
    LOG_ACTION="status"
    _status_fn
    retn=$?
fi
if [[ ${DEPS_CHK} -eq 1 ]]; then
    CLI_ACTION=1
    LOG_ACTION="depend"
    _deps_chk_fn
    retn=$?
fi
if [[ ${SOURCE_FLAG} -eq 1 ]]; then
    CLI_ACTION=1
    LOG_ACTION="install"
    # ensure this is processed before install
    _source_fn "${SOURCE_PARAMS}"
    retn=$?
fi
if [[ ${MINION_VERSION_FLAG} -eq 1 ]]; then
    CLI_ACTION=1
    # ensure this is processed before install
    _set_install_minion_version_fn "${MINION_VERSION_PARAMS}"
    retn=$?
fi
if [[ ${INSTALL_FLAG} -eq 1 ]]; then
    CLI_ACTION=1
    LOG_ACTION="install"
    _install_fn "${INSTALL_PARAMS}"
    retn=$?
fi
if [[ ${CLEAR_ID_KEYS_FLAG} -eq 1 ]]; then
    CLI_ACTION=1
    LOG_ACTION="clear"
    _clear_id_key_fn "${CLEAR_ID_KEYS_PARAMS}"
    retn=$?
fi
if [[ ${UNINSTALL_FLAG} -eq 1 ]]; then
    CLI_ACTION=1
    LOG_ACTION="remove"
    _uninstall_fn
    retn=$?
fi
if [[ ${VERSION_FLAG} -eq 1 ]]; then
    CLI_ACTION=1
    echo "${SCRIPT_VERSION}"
    retn=0
fi

if [[ ${CLI_ACTION} -eq 0 ]]; then
    # check if guest variables have an action since none from CLI
    # since none presented on the command line
    gvar_action=$(vmtoolsd --cmd "info-get ${guestvars_salt_desiredstate}" \
        2>/dev/null) || {
            _warning_log "$0 unable to retrieve any action arguments from "\
                "guest variables ${guestvars_salt_desiredstate}, retcode '$?'";
    }

    if [[ -n "${gvar_action}" ]]; then
        case "${gvar_action}" in
            depend)
                LOG_ACTION="depend"
                _deps_chk_fn
                retn=$?
                ;;
            present)
                LOG_ACTION="install"
                _install_fn
                retn=$?
                ;;
            absent)
                LOG_ACTION="remove"
                _uninstall_fn
                retn=$?
                ;;
            status)
                LOG_ACTION="status"
                _status_fn
                retn=$?
                ;;
            *)
                ;;
        esac
    fi
fi

_clean_up_log_files

exit ${retn}
   070701000002E3000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/containerInfo 070701000002E4000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/containerInfo/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002E5000081A40000000000000000000000016822550500000EF8000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/containerInfo/Makefile.am ################################################################################
### Copyright (c) 2021-2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libcontainerInfo.la

libcontainerInfo_la_CPPFLAGS =
libcontainerInfo_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libcontainerInfo_la_LDFLAGS =
libcontainerInfo_la_LDFLAGS += @PLUGIN_LDFLAGS@

libcontainerInfo_la_LIBADD =
libcontainerInfo_la_LIBADD += @VMTOOLS_LIBS@
libcontainerInfo_la_LIBADD += @GOBJECT_LIBS@

libcontainerInfo_la_SOURCES =
libcontainerInfo_la_SOURCES += containerInfo.h
libcontainerInfo_la_SOURCES += containerInfoInt.h
libcontainerInfo_la_SOURCES += containerInfo.c

libcontainerInfo_la_SOURCES += containerInfo_docker.c
libcontainerInfo_la_LIBADD += -lcurl
libcontainerInfo_la_CPPFLAGS += @CURL_CPPFLAGS@
libcontainerInfo_la_LIBADD += ../../../lib/jsmn/libJsmn.la

libcontainerInfo_la_SOURCES += gogoproto/gogo.pb.h
libcontainerInfo_la_SOURCES += gogoproto/gogo.pb.cc
libcontainerInfo_la_SOURCES += $(TYPES_DIR)/mount.pb.h
libcontainerInfo_la_SOURCES += $(TYPES_DIR)/mount.pb.cc
libcontainerInfo_la_SOURCES += $(TYPES_DIR)/metrics.pb.h
libcontainerInfo_la_SOURCES += $(TYPES_DIR)/metrics.pb.cc
libcontainerInfo_la_SOURCES += $(TYPES_DIR)/descriptor.pb.h
libcontainerInfo_la_SOURCES += $(TYPES_DIR)/descriptor.pb.cc
libcontainerInfo_la_SOURCES += $(TYPES_DIR)/task/task.pb.h
libcontainerInfo_la_SOURCES += $(TYPES_DIR)/task/task.pb.cc
libcontainerInfo_la_SOURCES += tasks.pb.h
libcontainerInfo_la_SOURCES += tasks.pb.cc
libcontainerInfo_la_SOURCES += containers.pb.h
libcontainerInfo_la_SOURCES += containers.pb.cc
libcontainerInfo_la_SOURCES += tasks.grpc.pb.h
libcontainerInfo_la_SOURCES += tasks.grpc.pb.cc
libcontainerInfo_la_SOURCES += containers.grpc.pb.h
libcontainerInfo_la_SOURCES += containers.grpc.pb.cc
libcontainerInfo_la_SOURCES += containerInfo_grpc.cc

libcontainerInfo_la_CPPFLAGS += ${grpcxx_CFLAGS}
libcontainerInfo_la_LIBADD += -lprotobuf
libcontainerInfo_la_LIBADD += ${grpcxx_LIBS}

tasks.grpc.pb.cc containers.grpc.pb.cc: %.grpc.pb.cc : %.proto %.pb.cc
	$(PROTOC) -I. -I$(GOGO_PROTOPATH) \
             --grpc_out=. --plugin=protoc-gen-grpc=`which $(GRPC_CPP)` $<

containerInfo_grpc.cc : containers.grpc.pb.cc tasks.grpc.pb.cc

$(TYPES_DIR)/mount.proto \
$(TYPES_DIR)/metrics.proto \
$(TYPES_DIR)/descriptor.proto \
$(TYPES_DIR)/task/task.proto: %.proto : $(DEP_PROTOPATH)/%.proto
	$(MKDIR_P) $(@D)
	sed 's/import weak /import /' $< > $@

$(TYPES_DIR)/mount.pb.cc \
$(TYPES_DIR)/metrics.pb.cc \
$(TYPES_DIR)/descriptor.pb.cc \
$(TYPES_DIR)/task/task.pb.cc: %.pb.cc : %.proto
	$(PROTOC) --cpp_out=. -I$(GOGO_PROTOPATH) -I. $<

tasks.proto: $(TASKS_PROTOPATH)/tasks.proto
	sed 's/import weak /import /' $< > $@

containers.proto: $(CONTAINERD_PROTOPATH)/containers.proto
	sed 's/import weak /import /' $< > $@

tasks.pb.cc containers.pb.cc: %.pb.cc : %.proto
	$(PROTOC) --cpp_out=. -I. -I$(GOGO_PROTOPATH) $<

gogoproto/gogo.pb.cc: $(GOGO_PROTOPATH)/gogoproto/gogo.proto
	$(MKDIR_P) $(@D)
	$(PROTOC) --cpp_out=. -I$(GOGO_PROTOPATH) $<

070701000002E6000081A40000000000000000000000016822550500007A8D000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/containerInfo/containerInfo.c /*********************************************************
 * Copyright (c) 2021-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * containerInfo.c --
 *
 *      Captures the information about running containers inside the guest
 *      and publishes it to a guest variable.
 */

#ifndef __linux__
#   error This file should not be compiled.
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "containerInfoInt.h"
#include "codeset.h"
#include "procMgr.h"
#include "str.h"
#include "strutil.h"
#include "conf.h"
#include "util.h"
#include "vm_atomic.h"
#include "vmware/guestrpc/containerInfo.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/log.h"
#include "vmware/tools/threadPool.h"

#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);

/**
 * Default poll interval for containerInfo is 6 hours
 */
#define CONTAINERINFO_DEFAULT_POLL_INTERVAL (6 * 60 * 60)

/**
 * Name of the containerd process. This is used to figure
 * out if the containerd process is running in the list
 * of processes.
 */
#define CONTAINERD_PROCESS_NAME "containerd"

/**
 * Default value for containerinfo query-limit conf key.
 */
#define CONTAINERINFO_DEFAULT_CONTAINER_MAX 256

/**
 * Default value for CONFNAME_CONTAINERINFO_REMOVE_DUPLICATES setting in
 * tools configuration file.
 *
 * TRUE will remove duplicate containers.
 */
#define CONTAINERINFO_DEFAULT_REMOVE_DUPLICATES TRUE

/**
 * Default value for containerd-unix-socket conf key.
 */
#define CONTAINERINFO_DEFAULT_CONTAINERDSOCKET "/run/containerd/containerd.sock"

/**
 * Default value for docker-unix-socket conf key.
 */
#define CONTAINERINFO_DEFAULT_DOCKER_SOCKET "/var/run/docker.sock"

/**
 * Default value for allowed-namespaces conf key.
 */
#define CONTAINERINFO_DEFAULT_ALLOWED_NAMESPACES "moby,k8s.io,default"

/**
 * Name of the 'moby' namespace used by docker.
 */
#define CONTAINERINFO_DOCKER_NAMESPACE_NAME "moby"

/**
 * Maximum size of the guestinfo packet that holds the containerinfo
 * information.
 */
#define CONTAINERINFO_MAX_GUESTINFO_PACKET_SIZE (63 * 1024)

/**
 * Defines current containerinfo poll interval (in seconds).
 *
 * Controlled by containerinfo.poll-interval config file option.
 */
static Atomic_uint32 gContainerInfoPollInterval = { 0 };

/**
 * ContainerInfo gather loop timeout source.
 */
static GSource *gContainerInfoTimeoutSource = NULL;

/**
 * ContainerInfo and AppInfo share the same host side switch so this
 * defines the state of the AppInfo at the host side.
 */
static gboolean gAppInfoEnabledInHost = TRUE;

/**
 * Defines whether task is currently in progress.  libcurl initialization
 * is not thread safe so this atomic bool will ensure only one task is
 * running at a time.
 */
static Atomic_Bool gTaskSubmitted = { FALSE }; // Task has not been submitted.

static void TweakGatherLoop(ToolsAppCtx *ctx, gboolean force);


/*
 *****************************************************************************
 * SetGuestInfo --
 *
 * Sends a simple key-value update request to the VMX.
 *
 * @param[in] ctx       Application context.
 * @param[in] key       Key sent to the VMX
 * @param[in] value     GuestInfo data sent to the VMX
 *
 *****************************************************************************
 */

static void
SetGuestInfo(ToolsAppCtx *ctx,                // IN
             const char *guestVariableName,   // IN
             const char *value)               // IN
{
   char *reply = NULL;
   gchar *msg;
   size_t replyLen;

   ASSERT(guestVariableName != NULL);
   ASSERT(value != NULL);

   msg = g_strdup_printf("info-set guestinfo.%s %s",
                         guestVariableName,
                         value);

   if (!RpcChannel_Send(ctx->rpc,
                        msg,
                        strlen(msg) + 1,
                        &reply,
                        &replyLen)) {
      g_warning("%s: Error sending RPC message: %s\n", __FUNCTION__,
                VM_SAFE_STR(reply));
   } else {
      g_info("%s: Successfully published the container information.\n",
             __FUNCTION__);
   }

   g_free(msg);
   vm_free(reply);
   return;
}


/*
 *****************************************************************************
 * CheckContainerdRunning --
 *
 * When containers are run, the containerd-shim process gets called.
 * This function checks if containerd process exists in the list of processes,
 * which will signal that the containerinfo loop should be started.
 *
 * @retval TRUE        found containerd process.
 * @retval FALSE       not found.
 *
 *****************************************************************************
 */

static gboolean
CheckContainerdRunning(void)
{
   ProcMgrProcInfoArray *procList;
   size_t procCount;
   int i;
   gboolean result = FALSE;

   procList = ProcMgr_ListProcesses();
   if (procList == NULL) {
      g_warning("%s: Failed to get the list of processes.\n",
                __FUNCTION__);
      return result;
   }

   procCount = ProcMgrProcInfoArray_Count(procList);
   for (i = 0; i < procCount; i++) {
      ProcMgrProcInfo *procInfo = ProcMgrProcInfoArray_AddressOf(procList, i);
      if (procInfo->procCmdName != NULL &&
          strstr(procInfo->procCmdName, CONTAINERD_PROCESS_NAME)) {
         result = TRUE;
         break;
      }
   }

   ProcMgr_FreeProcList(procList);
   return result;
}


/*
 ******************************************************************************
 * ContainerInfo_DestroyContainerData --
 *
 * Free function for container data. This function is called by the glib
 * for each element in the container list while freeing.
 *
 * @param[in] data      Pointer to the container data.
 *
 * @retval NONE
 *
 ******************************************************************************
 */

void
ContainerInfo_DestroyContainerData(void *pointer)
{
   ContainerInfo *info = pointer;

   if (info == NULL) {
      return;
   }

   g_free(info->id);
   g_free(info->image);
   g_free(info);
}


/*
 *****************************************************************************
 * ContainerInfo_DestroyContainerList --
 *
 * Frees the entire memory allocated for the container list.
 *
 * @param[in]  containerList     Pointer to the container list.
 *
 * @retval NONE
 *
 *****************************************************************************
 */

void
ContainerInfo_DestroyContainerList(GSList *containerList)
{
   if (containerList == NULL) {
      return;
   }

   g_slist_free_full(containerList, ContainerInfo_DestroyContainerData);
}


/*
 *****************************************************************************
 * ContainerInfoGetNsJson --
 *
 * Iterates through the list of containers and prepares the JSON string for
 * a specified namespace. The caller must free the resulting JSON string.
 *
 * @param[in]  ns                The name of the namespace
 * @param[in]  containerList     The list of the running containers
 * @param[in]  dockerSocketPath  The path to the unix socket used by docker.
 * @param[in]  removeDuplicates  Remove duplicate containers from the output.
 * @param[in]  maxSize           Maximum size of the JSON output
 * @param[out] resultJson        JSON string that is prepared.
 *
 * @retVal The size of the JSON string returned.
 *
 *****************************************************************************
 */

size_t
ContainerInfoGetNsJson(const char *ns,                 // IN
                       GSList *containerList,          // IN
                       const char *dockerSocketPath,   // IN
                       gboolean removeDuplicates,      // IN
                       unsigned int maxSize,           // IN
                       char **resultJson)              // OUT
{
   static const char headerFmt[] = "\"%s\": [";
   static const char footer[] = "]";

   GSList *info;
   gboolean nodeAdded;
   DynBuf dynBuffer;
   size_t resultSize = 0;
   GHashTable *dockerContainerTable = NULL;
   GHashTable *imagesAdded = NULL;
   gchar *escapedImageName = NULL;

   ASSERT(resultJson != NULL);

   DynBuf_Init(&dynBuffer);
   StrUtil_SafeDynBufPrintf(&dynBuffer, headerFmt, ns);

   nodeAdded = FALSE;

   /*
    * The image name may not be set for containers managed by docker.
    * To handle such cases, get the list of containers using Docker APIs.
    */
   if (strcmp(ns, CONTAINERINFO_DOCKER_NAMESPACE_NAME) == 0) {
      dockerContainerTable =
         ContainerInfo_GetDockerContainers(dockerSocketPath);
   }

   if (removeDuplicates) {
      imagesAdded = g_hash_table_new_full(g_str_hash, g_str_equal,
                                          g_free, NULL);
   }

   for (info = containerList; info != NULL; info = info->next) {
      static const char *nodeFmt = "%s{\""
                                   CONTAINERINFO_KEY_IMAGE
                                   "\":\"%s\"}";
      size_t currentBufferSize = DynBuf_GetSize(&dynBuffer);
      gchar *tmpNode;
      size_t len;
      ContainerInfo *node = (ContainerInfo *) info->data;

      g_free(escapedImageName);
      escapedImageName = NULL;

      if (node->image == NULL || node->image[0] == '\0') {
         const char *newImage = NULL;

         if (dockerContainerTable != NULL) {
            newImage = g_hash_table_lookup(dockerContainerTable, node->id);
         }

         if (newImage != NULL) {
            escapedImageName = g_strdup(newImage);
         } else {
            g_warning("%s: Skipping '%s' since image name couldn't "
                      "be retrieved.\n", __FUNCTION__, node->id);
            continue;
         }
      } else {
         escapedImageName = CodeSet_JsonEscape(node->image);
         if (NULL == escapedImageName) {
            g_warning("%s: Failed to escape the image. Skipping '%s'\n",
                      __FUNCTION__, node->id);
            continue;
         }
      }

      if (removeDuplicates) {
         /*
          * Check if the container was already added. If already added, just
          * skip to the next container.
          */
         if (g_hash_table_contains(imagesAdded, escapedImageName)) {
            continue;
         }
      }

      tmpNode = Str_Asprintf(&len, nodeFmt,
                             nodeAdded ? "," : "", escapedImageName);
      if (tmpNode == NULL) {
         g_warning("%s: Out of memory. Skipping '%s'\n",
                   __FUNCTION__, node->id);
         break;
      }

      if (currentBufferSize + len + sizeof footer > maxSize) {
         g_warning("%s: Skipping '%s' due to insufficient size.\n",
                   __FUNCTION__, node->id);
      } else {
         if (removeDuplicates) {
            g_hash_table_add(imagesAdded, escapedImageName);
            escapedImageName = NULL;
         }
         DynBuf_Append(&dynBuffer, tmpNode, len);
         nodeAdded = TRUE;
      }
      g_free(tmpNode);
   }

   if (nodeAdded) {
      DynBuf_Append(&dynBuffer, footer, strlen(footer));
      resultSize = DynBuf_GetSize(&dynBuffer);
      *resultJson = DynBuf_DetachString(&dynBuffer);
   } else {
      resultSize = 0;
      *resultJson = NULL;
   }

   g_free(escapedImageName);

   if (imagesAdded != NULL) {
      g_hash_table_destroy(imagesAdded);
   }

   if (dockerContainerTable != NULL) {
      g_hash_table_destroy(dockerContainerTable);
   }

   DynBuf_Destroy(&dynBuffer);
   return resultSize;
}


/*
 *****************************************************************************
 * ContainerInfoGatherTask --
 *
 * Collects all the desired container related information.
 *
 * @param[in]  ctx     The application context.
 * @param[in]  data    Unused
 *
 *****************************************************************************
 */

static void
ContainerInfoGatherTask(ToolsAppCtx *ctx,   // IN
                        gpointer data)      // IN
{
   gchar *timeStampString = NULL;
   int limit;
   gint64 startInfoGatherTime;
   gint64 endInfoGatherTime;
   gchar *containerdSocketPath = NULL;
   gchar *nsConfValue = NULL;
   gchar **nsList;
   static Atomic_uint64 updateCounter = {1};
   uint64 counter;
   int i;
   DynBuf dynBuffer;
   gchar tmpBuf[256];
   size_t len;
   gboolean nsAdded;
   char *dockerSocketPath = NULL;
   GHashTable *nsParsed;
   gboolean removeDuplicates;

   static char headerFmt[] = "{"
                     "\"" CONTAINERINFO_KEY_VERSION  "\":\"%d\","
                     "\"" CONTAINERINFO_KEY_UPDATE_COUNTER "\":%"FMT64"u,"
                     "\"" CONTAINERINFO_KEY_PUBLISHTIME    "\":\"%s\","
                     "\"" CONTAINERINFO_KEY "\":{";
   static char footer[] = "}}";

   if (Atomic_ReadIfEqualWriteBool(&gTaskSubmitted, FALSE, TRUE)) {
      g_info("%s: Previously submitted task is not completed\n", __FUNCTION__);
      return;
   }

   timeStampString = VMTools_GetTimeAsString();
   counter = (uint64) Atomic_ReadInc64(&updateCounter);

   DynBuf_Init(&dynBuffer);
   len = Str_Snprintf(tmpBuf, sizeof tmpBuf,
                      headerFmt,
                      CONTAINERINFO_VERSION_1,
                      counter,
                      (timeStampString != NULL) ? timeStampString : "");
   ASSERT(len > 0);

   DynBuf_Append(&dynBuffer, tmpBuf, len);

   if (!CheckContainerdRunning()) {
      g_info("%s: Could not find running containerd process on the system.\n",
             __FUNCTION__);
      goto exit;
   }

   limit =
      VMTools_ConfigGetInteger(ctx->config,
                               CONFGROUPNAME_CONTAINERINFO,
                               CONFNAME_CONTAINERINFO_LIMIT,
                               CONTAINERINFO_DEFAULT_CONTAINER_MAX);

   if (limit < 1) {
      g_warning("%s: invalid max-containers %d. Using default %d.\n",
                __FUNCTION__,
                limit,
                CONTAINERINFO_DEFAULT_CONTAINER_MAX);
      limit = CONTAINERINFO_DEFAULT_CONTAINER_MAX;
   }

   nsConfValue =
      VMTools_ConfigGetString(ctx->config,
                              CONFGROUPNAME_CONTAINERINFO,
                              CONFNAME_CONTAINERINFO_ALLOWED_NAMESPACES,
                              CONTAINERINFO_DEFAULT_ALLOWED_NAMESPACES);
   g_strstrip(nsConfValue);

   if (nsConfValue[0] == '\0') {
      g_warning("%s: Empty value found for %s.%s key. Ignoring.",
                __FUNCTION__, CONFGROUPNAME_CONTAINERINFO,
                CONFNAME_CONTAINERINFO_ALLOWED_NAMESPACES);
      goto exit;
   }

   containerdSocketPath =
      VMTools_ConfigGetString(ctx->config,
                              CONFGROUPNAME_CONTAINERINFO,
                              CONFNAME_CONTAINERINFO_CONTAINERDSOCKET,
                              CONTAINERINFO_DEFAULT_CONTAINERDSOCKET);
   g_strstrip(containerdSocketPath);

   dockerSocketPath =
      VMTools_ConfigGetString(ctx->config,
                              CONFGROUPNAME_CONTAINERINFO,
                              CONFNAME_CONTAINERINFO_DOCKERSOCKET,
                              CONTAINERINFO_DEFAULT_DOCKER_SOCKET);
   g_strstrip(dockerSocketPath);

   if (dockerSocketPath[0] == '\0') {
      g_warning("%s: Empty value found for %s.%s key. Using default %s.",
                __FUNCTION__, CONFGROUPNAME_CONTAINERINFO,
                CONFNAME_CONTAINERINFO_DOCKERSOCKET,
                CONTAINERINFO_DEFAULT_DOCKER_SOCKET);
      g_free(dockerSocketPath);
      dockerSocketPath = g_strdup(CONTAINERINFO_DEFAULT_DOCKER_SOCKET);
   }

   removeDuplicates =
      VMTools_ConfigGetBoolean(ctx->config,
                               CONFGROUPNAME_CONTAINERINFO,
                               CONFNAME_CONTAINERINFO_REMOVE_DUPLICATES,
                               CONTAINERINFO_DEFAULT_REMOVE_DUPLICATES);

   startInfoGatherTime = g_get_monotonic_time();

   nsList = g_strsplit(nsConfValue, ",", 0);
   nsAdded = FALSE;
   nsParsed = g_hash_table_new(g_str_hash, g_str_equal);

   for (i = 0; nsList[i] != NULL; i++) {
      size_t currentBufferSize = DynBuf_GetSize(&dynBuffer);
      size_t maxSizeRemaining = CONTAINERINFO_MAX_GUESTINFO_PACKET_SIZE -
                                currentBufferSize - sizeof(footer);

      gchar *nsJsonString;
      size_t nsJsonSize;
      GSList *containerList;

      g_strstrip(nsList[i]);
      if (nsList[i][0] == '\0') {
         g_warning("%s: Empty value found for the namespace. Skipping.",
                   __FUNCTION__);
         continue;
      }

      if (g_hash_table_contains(nsParsed, nsList[i])) {
         g_debug("%s: Skipping the duplicate namespace: %s",
                 __FUNCTION__, nsList[i]);
         continue;
      }

      if (nsAdded) {
         maxSizeRemaining--; // Minus size of ','
      }

      if (maxSizeRemaining == 0 ||
          maxSizeRemaining > CONTAINERINFO_MAX_GUESTINFO_PACKET_SIZE) {
         break;
      }

      containerList =
         ContainerInfo_GetContainerList(nsList[i], containerdSocketPath,
                                        (unsigned int) limit);
      g_hash_table_add(nsParsed, nsList[i]);
      if (containerList == NULL) {
         continue;
      }

      nsJsonSize = ContainerInfoGetNsJson(nsList[i], containerList,
                   dockerSocketPath, removeDuplicates, maxSizeRemaining,
                   &nsJsonString);
      if (nsJsonSize > 0 && nsJsonSize <= maxSizeRemaining) {
         if (nsAdded) {
            DynBuf_Append(&dynBuffer, ",", 1);
         }
         DynBuf_Append(&dynBuffer, nsJsonString, nsJsonSize);
         nsAdded = TRUE;
      }
      g_free(nsJsonString);
      ContainerInfo_DestroyContainerList(containerList);
   }

   g_hash_table_destroy(nsParsed);
   g_strfreev(nsList);

   endInfoGatherTime = g_get_monotonic_time();

   g_info("%s: time to complete containerInfo gather = %" G_GINT64_FORMAT " us\n",
          __FUNCTION__, endInfoGatherTime - startInfoGatherTime);

exit:
   if (Atomic_Read32(&gContainerInfoPollInterval) == 0) {
      /*
       * If gatherLoop is disabled then make sure this thread
       * did not overwrite the guestVar. The guestVar should be
       * cleared out in this case.
       */
      SetGuestInfo(ctx, CONTAINERINFO_GUESTVAR_KEY, "");
   } else {
      DynBuf_Append(&dynBuffer, footer, sizeof(footer));
      SetGuestInfo(ctx,
                   CONTAINERINFO_GUESTVAR_KEY,
                   DynBuf_GetString(&dynBuffer));
   }

   DynBuf_Destroy(&dynBuffer);
   g_free(dockerSocketPath);
   g_free(containerdSocketPath);
   g_free(nsConfValue);
   g_free(timeStampString);
   Atomic_WriteBool(&gTaskSubmitted, FALSE);
}


/*
 *****************************************************************************
 * ContainerInfoGather --
 *
 * Creates a new thread that collects all the desired container related
 * information and updates the VMX. Tweaks the poll gather loop as per the
 * tools configuration after creating the thread.
 *
 * @param[in]  data     The application context.
 *
 * @retval  G_SOURCE_REMOVE to indicate that the timer should be removed.
 *
 *****************************************************************************
 */

static gboolean
ContainerInfoGather(gpointer data)   // IN
{
   ToolsAppCtx *ctx = data;

   g_debug("%s: Submitting a task to capture container information.\n",
           __FUNCTION__);

   if (!ToolsCorePool_SubmitTask(ctx, ContainerInfoGatherTask, NULL, NULL)) {
      g_warning("%s: Failed to submit the task for capturing container "
                "information\n", __FUNCTION__);
   }

   TweakGatherLoop(ctx, TRUE);

   return G_SOURCE_REMOVE;
}


/*
 *****************************************************************************
 * TweakGatherLoopEx --
 *
 * Start, stop, reconfigure a ContainerInfo Gather poll loop.
 *
 * This function is responsible for creating, manipulating, and resetting a
 * ContainerInfo Gather loop timeout source. The poll loop will be disabled if
 * the poll interval is 0.
 *
 * @param[in]     ctx           The application context.
 * @param[in]     pollInterval  Poll interval in seconds. A value of 0 will
 *                              disable the loop.
 *
 *****************************************************************************
 */

static void
TweakGatherLoopEx(ToolsAppCtx *ctx,     // IN
                  guint pollInterval)   // IN
{
   if (gContainerInfoTimeoutSource != NULL) {
      /*
       * Destroy the existing timeout source.
       */
      g_source_destroy(gContainerInfoTimeoutSource);
      gContainerInfoTimeoutSource = NULL;
   }

   if (pollInterval > 0) {
      if (Atomic_Read32(&gContainerInfoPollInterval) != pollInterval) {
         g_info("%s: New value for %s is %us.\n",
                __FUNCTION__,
                CONFNAME_CONTAINERINFO_POLLINTERVAL,
                pollInterval);
      }

      gContainerInfoTimeoutSource = g_timeout_source_new(pollInterval * 1000);
      VMTOOLSAPP_ATTACH_SOURCE(ctx, gContainerInfoTimeoutSource,
                               ContainerInfoGather, ctx, NULL);
      g_source_unref(gContainerInfoTimeoutSource);
      Atomic_Write32(&gContainerInfoPollInterval, pollInterval);
   } else if (Atomic_Read32(&gContainerInfoPollInterval) > 0) {
      g_info("%s: Poll loop for %s disabled.\n",
             __FUNCTION__, CONFNAME_CONTAINERINFO_POLLINTERVAL);
      Atomic_Write32(&gContainerInfoPollInterval, 0);
      SetGuestInfo(ctx, CONTAINERINFO_GUESTVAR_KEY, "");
   }
}


/*
 *****************************************************************************
 * TweakGatherLoop --
 *
 * Configures the ContainerInfo Gather poll loop based on the settings in the
 * tools configuration.
 *
 * This function is responsible for creating, manipulating, and resetting a
 * ContainerInfo Gather loop timeout source.
 *
 * @param[in]     ctx           The application context.
 * @param[in]     force         If set to TRUE, the poll loop will be
 *                              tweaked even if the poll interval hasn't
 *                              changed from the previous value.
 *
 *****************************************************************************
 */

static void
TweakGatherLoop(ToolsAppCtx *ctx,   // IN
                gboolean force)     // IN
{
   gint pollInterval;

   if (gAppInfoEnabledInHost) {
      pollInterval =
         VMTools_ConfigGetInteger(ctx->config,
                                  CONFGROUPNAME_CONTAINERINFO,
                                  CONFNAME_CONTAINERINFO_POLLINTERVAL,
                                  CONTAINERINFO_DEFAULT_POLL_INTERVAL);

      if (pollInterval < 0 || pollInterval > (G_MAXINT / 1000)) {
         g_warning("%s: Invalid poll interval %d. Using default %us.\n",
                   __FUNCTION__, pollInterval,
                   CONTAINERINFO_DEFAULT_POLL_INTERVAL);
         pollInterval = CONTAINERINFO_DEFAULT_POLL_INTERVAL;
      }
   } else {
      pollInterval = 0;
   }

   if (force || (Atomic_Read32(&gContainerInfoPollInterval) != pollInterval)) {
      /*
       * pollInterval can never be a negative value. Typecasting into
       * guint should not be a problem.
       */
      TweakGatherLoopEx(ctx, (guint) pollInterval);
   }
}


/*
 *****************************************************************************
 * ContainerInfoServerConfReload --
 *
 * Reconfigures the poll loop interval upon config file reload.
 *
 * @param[in]  src     The source object.
 * @param[in]  ctx     The application context.
 * @param[in]  data    Unused.
 *
 *****************************************************************************
 */

static void
ContainerInfoServerConfReload(gpointer src,       // IN
                              ToolsAppCtx *ctx,   // IN
                              gpointer data)      // IN
{
   g_info("%s: Reloading the tools configuration.\n", __FUNCTION__);

   TweakGatherLoop(ctx, FALSE);
}


/*
 *****************************************************************************
 * ContainerInfoServerShutdown --
 *
 * Cleanup internal data on shutdown.
 *
 * @param[in]  src     The source object.
 * @param[in]  ctx     Application context.
 * @param[in]  data    Unused.
 *
 *****************************************************************************
 */

static void
ContainerInfoServerShutdown(gpointer src,       // IN
                            ToolsAppCtx *ctx,   // IN
                            gpointer data)      // IN
{
   if (gContainerInfoTimeoutSource != NULL) {
      g_source_destroy(gContainerInfoTimeoutSource);
      gContainerInfoTimeoutSource = NULL;
   }

   SetGuestInfo(ctx, CONTAINERINFO_GUESTVAR_KEY, "");
}


/*
 *----------------------------------------------------------------------------
 *
 * ContainerInfoServerSetOption --
 *
 * Handle TOOLSOPTION_ENABLE_APPINFO Set_Option callback. This callback is
 * necessary because containerInfo shares AppInfo's host side switch.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  option   Option being set.
 * @param[in]  value    Option value.
 * @param[in]  plugin   Plugin registration data.
 *
 * @retval  TRUE  if the specified option is TOOLSOPTION_ENABLE_APPINFO and
 *                the containerInfo Gather poll loop is reconfigured.
 * @retval  FALSE if the specified option is not TOOLSOPTION_ENABLE_APPINFO
 *                or containerInfo Gather poll loop is not reconfigured.
 *----------------------------------------------------------------------------
 */

static gboolean
ContainerInfoServerSetOption(gpointer src,          // IN
                             ToolsAppCtx *ctx,      // IN
                             const gchar *option,   // IN
                             const gchar *value,    // IN
                             gpointer data)         // IN
{
   gboolean stateChanged = FALSE;

   if (strcmp(option, TOOLSOPTION_ENABLE_APPINFO) == 0) {
      g_debug("%s: Tools set option %s=%s.\n",
              __FUNCTION__, TOOLSOPTION_ENABLE_APPINFO, value);

      if (strcmp(value, "1") == 0 && !gAppInfoEnabledInHost) {
         gAppInfoEnabledInHost = TRUE;
         stateChanged = TRUE;
      } else if (strcmp(value, "0") == 0 && gAppInfoEnabledInHost) {
         gAppInfoEnabledInHost = FALSE;
         stateChanged = TRUE;
      }

      if (stateChanged) {
         g_info("%s: State of AppInfo is changed to '%s' at host side.\n",
                __FUNCTION__, gAppInfoEnabledInHost ? "enabled" : "disabled");
         TweakGatherLoop(ctx, TRUE);
      }
   }

   return stateChanged;
}


/*
 *****************************************************************************
 * ContainerInfoServerReset --
 *
 * Callback function that gets called whenever the RPC channel gets reset.
 * Disable the current timer and set a timer for random interval; after that
 * interval, the timer will be resumed at the standard interval.
 * The one time random interval is intended to avoid the possibility that the
 * containerinfo plugin might run at the same time in a collection of
 * VMs - such as might be created by instant clone - which could in turn cause
 * a load spike on the host.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Application context.
 * @param[in]  data     Unused.
 *
 *****************************************************************************
 */

static void
ContainerInfoServerReset(gpointer src,       // IN
                         ToolsAppCtx *ctx,   // IN
                         gpointer data)      // IN
{
   /*
    * Handle reset for containerinfo loop.
    */
   if (gContainerInfoTimeoutSource != NULL) {
      guint interval;

      ASSERT(Atomic_Read32(&gContainerInfoPollInterval) != 0);

#define MIN_CONTAINERINFO_INTERVAL 30

      if (Atomic_Read32(&gContainerInfoPollInterval) >
          MIN_CONTAINERINFO_INTERVAL) {
         GRand *gRand = g_rand_new();

         interval = g_rand_int_range(gRand,
                           MIN_CONTAINERINFO_INTERVAL,
                           Atomic_Read32(&gContainerInfoPollInterval));
         g_rand_free(gRand);
      } else {
         interval = Atomic_Read32(&gContainerInfoPollInterval);
      }

#undef MIN_CONTAINERINFO_INTERVAL

      g_info("%s: Using poll interval for containerinfo loop: %u.\n",
             __FUNCTION__, interval);

      TweakGatherLoopEx(ctx, interval);
   } else {
      /*
       * Channel got reset. VM might have vMotioned to an older host
       * that doesn't send the 'Set_Option enableAppInfo'.
       * Set gAppInfoEnabledInHost to TRUE and tweak the gather loop.
       * Else, the application information may never be captured.
       */
      if (!gAppInfoEnabledInHost) {
         gAppInfoEnabledInHost = TRUE;
         TweakGatherLoop(ctx, TRUE);
      } else {
         g_debug("%s: Poll loop disabled. Ignoring.\n", __FUNCTION__);
      }
   }
}


/*
 *****************************************************************************
 * ToolsOnLoad --
 *
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in]  ctx   The app context.
 *
 * @retval  The registration data.
 *
 *****************************************************************************
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)   // IN
{
   static ToolsPluginData regData = {
      "containerInfo",
      NULL,
      NULL
   };

   /*
    * Return NULL to disable the plugin if not running in a VMware VM.
    */
   if (!ctx->isVMware) {
      g_info("%s: Not running in a VMware VM.\n", __FUNCTION__);
      return NULL;
   }

   /*
    * Return NULL to disable the plugin if not running in vmsvc daemon.
    */
   if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
      g_info("%s: Not running in vmsvc daemon: container name='%s'.\n",
             __FUNCTION__, ctx->name);
      return NULL;
   }

   /*
    * This plugin is useless without an RpcChannel.  If we don't have one,
    * just bail.
    */
   if (ctx->rpc != NULL) {
      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_CONF_RELOAD, ContainerInfoServerConfReload, NULL },
         { TOOLS_CORE_SIG_SHUTDOWN, ContainerInfoServerShutdown, NULL },
         { TOOLS_CORE_SIG_RESET, ContainerInfoServerReset, NULL },
         { TOOLS_CORE_SIG_SET_OPTION, ContainerInfoServerSetOption, NULL }
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_SIGNALS,
           VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs))
         }
      };

      regData.regs = VMTools_WrapArray(regs,
                                       sizeof *regs,
                                       ARRAYSIZE(regs));

      /*
       * Set up the containerInfo gather loop.
       */
      TweakGatherLoop(ctx, TRUE);

      return &regData;
   }

   return NULL;
}
   070701000002E7000081A400000000000000000000000168225505000006B4000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/containerInfo/containerInfoInt.h  /*********************************************************
 * Copyright (c) 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _CONTAINERINFOINT_H_
#define _CONTAINERINFOINT_H_

#include "conf.h"

#define G_LOG_DOMAIN CONFGROUPNAME_CONTAINERINFO

#include <glib.h>

/**
 * @file containerInfoInt.h
 *
 * Header file with few functions that are internal to containerInfo plugin.
 */

#ifdef __cplusplus
extern "C" {
#endif

typedef struct ContainerInfo {
   char *id;
   char *image;
} ContainerInfo;

void ContainerInfo_DestroyContainerData(void *pointer);
void ContainerInfo_DestroyContainerList(GSList *containerList);

GHashTable *ContainerInfo_GetDockerContainers(const char *dockerSocketPath);

GSList *ContainerInfo_GetContainerList(const char *ns,
                                       const char *containerdSocketPath,
                                       unsigned int maxContainers);

#ifdef __cplusplus
}
#endif

#endif /* _CONTAINERINFOINT_H_ */
070701000002E8000081A4000000000000000000000001682255050000403B000000000000000000000000000000000000005900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/containerInfo/containerInfo_docker.c  /*********************************************************
 * Copyright (c) 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * containerInfo_docker.c --
 *
 *    This file defines docker specific functions which are needed by
 *    containerInfo. Docker API is called using libcurl to find runnning
 *    docker containers and collect relevant info.
 */

#include <stdio.h>
#include <stdlib.h>
#include "containerInfoInt.h"
#include "jsmn.h"
#include <curl/curl.h>
#include "vm_assert.h"

#define HTTP_HEADER "HTTP"
#define HTTP_HEADER_LENGTH (sizeof HTTP_HEADER - 1)
#define HTTP_STATUS_SUCCESS "200"
#define HTTP_STATUS_SUCCESS_LENGTH (sizeof HTTP_STATUS_SUCCESS - 1)
#define TOKENS_PER_ALLOC 500
#define MAX_TOKENS 100000

/*
 * docker API versions are backwards compatible with older docker Engine
 * versions so this is the oldest API version that is documented by docker
 * at https://docs.docker.com/engine/api/
 */
#define DOCKER_API_VERSION "v1.18"

typedef struct DockerBuffer {
  char *response;
  size_t size;
} DockerBuffer;


/*
 ******************************************************************************
 * ContainerInfoJsonEq --
 *
 * @brief Utility function to check whether a string jsmn token has value
 * equal to @param s
 *
 * @param[in] json The json string
 * @param[in] tok The jsmn token structure pointer
 * @param[in] s The string to match in the token
 *
 * @retval TRUE successfully matched the string in the json token
 * @retval FALSE did not match the string in the json token
 *
 ******************************************************************************
 */

static gboolean
ContainerInfoJsonEq(const char *json,
                    jsmntok_t *tok,
                    const char *s)
{
   if (tok->type == JSMN_STRING &&
       (int) strlen(s) == tok->end - tok->start &&
       tok->start >= 0 && tok->end < strlen(json) &&
       strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
      return TRUE;
   }
   return FALSE;
}


/*
 ******************************************************************************
 * ContainerInfoJsonEqIsKey --
 *
 * @brief Utility function that is same as ContainerInfoJsonEq() but also
 *        checks token is of the key type in the json.
 *
 * @param[in] json The json string
 * @param[in] tok The jsmn token structure pointer
 * @param[in] s The string to match in the token
 *
 * @retval TRUE successfully matched the string and token is a key
 * @retval FALSE did not match the string or token is not a key
 *
 ******************************************************************************
 */

static gboolean
ContainerInfoJsonEqIsKey(const char *json,
                         jsmntok_t *tok,
                         const char *s)
{
   /*
    * Check tok->size. If tok is a key, tok->size will be 1.
    */
   return tok->size == 1 && ContainerInfoJsonEq(json, tok, s);;
}


/*
 ******************************************************************************
 * DockerWriteCB --
 *
 * @brief Sets callback for writing received data when using libcurl to access
 *        docker API. This function prototype is based on
 *        https://curl.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
 *
 * @param[in] data       info received from API
 * @param[in] size       this value is always 1 (according to curl docs)
 * @param[in] nitems     size of data
 * @param[in] userdata   pointer to DockerBuffer
 *
 * @retval   number of bytes successfully written
 *
 ******************************************************************************
 */

static size_t
DockerWriteCB(void *data,                    // IN
              size_t size,                   // IN
              size_t nitems,                 // IN
              void *userdata)                // IN
{
   size_t realsize = size * nitems;
   DockerBuffer *mem = (DockerBuffer *) userdata;
   char *realptr;
   size_t newsize = mem->size + realsize + 1;

   if (newsize < mem->size) {
      g_warning("%s:%d: size overflow\n", __FUNCTION__, __LINE__);
      g_free(mem->response);
      mem->response = NULL;
      return 0;
   }

   realptr = g_try_realloc(mem->response, newsize);
   if (realptr == NULL) {
      g_warning("%s:%d: out of memory\n", __FUNCTION__, __LINE__);
      g_free(mem->response);
      mem->response = NULL;
      return 0;
   }

   mem->response = realptr;
   memcpy(&mem->response[mem->size], data, realsize);
   mem->size += realsize;
   mem->response[mem->size] = '\0';

   return realsize;
}


/*
 ******************************************************************************
 * DockerHeaderCB --
 *
 * @brief Sets callback for receiving header data and saving HTTP status
 *        when using libcurl to access docker API. For more info see
 *        https://curl.se/libcurl/c/CURLOPT_HEADERFUNCTION.html
 *
 * @param[in] buffer     info received from API
 * @param[in] size       this value is always 1 (according to curl docs)
 * @param[in] nitems     size of buffer
 * @param[in] userdata   pointer to string to store docker status code
 *
 * @retval   number of bytes of header data successfully received
 *
 ******************************************************************************
 */

static size_t
DockerHeaderCB(char *buffer,                     // IN
               size_t size,                      // IN
               size_t nitems,                    // IN
               void *userdata)                   // IN
{
   size_t realSize = size * nitems;
   char **statusCode = (char **) userdata;
   char *statusStart;
   char *statusEnd;
   char *bufPtr;
   char *bufEnd;

   /*
    * Example of buffer: HTTP/1.1 404 Not Found\r\n
    * Do not assume that buffer is null-terminated!
    */
   if (realSize <= HTTP_HEADER_LENGTH ||
       memcmp(buffer, HTTP_HEADER, HTTP_HEADER_LENGTH) != 0) {
      /*
       * This is a separated header line, like: Api-Version: 1.41
       */
      return realSize;
   }

   bufEnd = buffer + realSize;
   bufPtr = buffer + HTTP_HEADER_LENGTH;
   statusStart = memchr(bufPtr, ' ', bufEnd - bufPtr);

   if (statusStart == NULL) {
      g_debug("%s:%d: HTTP header has unexpected format: %.*s\n",
              __FUNCTION__, __LINE__, (int) realSize, buffer);
      return 0;
   }

   bufPtr = ++statusStart;
   statusEnd = memchr(bufPtr, ' ', bufEnd - bufPtr);

   if (statusEnd == NULL) {
      g_debug("%s:%d: HTTP header has unexpected format: %.*s\n",
              __FUNCTION__, __LINE__, (int) realSize, buffer);
      return 0;
   }

   *statusCode = g_strndup(statusStart, statusEnd - statusStart);
   return realSize;
}


/*
 ******************************************************************************
 * DockerCallAPI --
 *
 * @brief Uses libcurl to access docker API and loads response to jsonString.
 *
 * @param[in] url              url of docker API endpoint.
 *                              e.g. http://v1.18/containers/json
 * @param[in] unixSocket       unix socket to communicate with docker.
 * @param[in/out] jsonString   stores the response from docker API.
 *
 * @retval TRUE   successfully wrote valid response to jsonString
 * @retval FALSE  on failure
 *
 ******************************************************************************
 */

static gboolean
DockerCallAPI(const char *url,                     // IN
              const char *unixSocket,              // IN
              char **jsonString)                   // OUT
{
   DockerBuffer result = {0};
   char *dockerStatus = NULL;
   CURLcode ret;
   char errBuf[CURL_ERROR_SIZE] = {'\0'};
   gboolean retVal = FALSE;
   CURL *curl = curl_easy_init();

   if (curl == NULL) {
      g_warning("%s:%d: curl failed to initialize\n",
                __FUNCTION__, __LINE__);
      return retVal;
   }

   curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, unixSocket);
   curl_easy_setopt(curl, CURLOPT_URL, url);
   curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errBuf);
   curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, DockerHeaderCB);
   curl_easy_setopt(curl, CURLOPT_HEADERDATA, &dockerStatus);
   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (void *) DockerWriteCB);
   curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &result);

   ret = curl_easy_perform(curl);

   if (ret == CURLE_OK && result.size > 0) {
      /*
       * might receive CURLE_OK from libcurl but dockerStatus does not
       * equal 200.  e.g. when page is not found by docker engine.
       */
      if (dockerStatus != NULL &&
          strncmp(dockerStatus, HTTP_STATUS_SUCCESS,
                  HTTP_STATUS_SUCCESS_LENGTH) == 0) {
         *jsonString = result.response;
         retVal = TRUE;
      } else {
         g_warning("%s:%d: error response from docker engine. response: %s",
                   __FUNCTION__, __LINE__, result.response != NULL ?
                   result.response : "No response from docker engine.");
         g_free(result.response);
      }
   } else {
      if (errBuf[0] != '\0') {
         g_warning("%s:%d: %s\n", __FUNCTION__, __LINE__, errBuf);
      } else {
         g_warning("%s:%d: docker request unsuccessful. strerror: %s\n",
                   __FUNCTION__, __LINE__, curl_easy_strerror(ret));
      }
      g_free(result.response);
   }

   g_free(dockerStatus);
   curl_easy_cleanup(curl);
   return retVal;
}


/*
 ******************************************************************************
 * ContainerInfoParseString --
 *
 * @brief  parses and stores input string as tokens.
 *
 * @param[in] jsonString  The string to parse.
 * @param[in/out] tokens  Stores tokenized version of jsonString.
 *
 * @retval  number of tokens parsed from jsonString or -1 on failure.
 ******************************************************************************
 */

static int
ContainerInfoParseString(char *jsonString,            // IN
                         jsmntok_t **tokens)          // IN/OUT
{
   gsize jsonLength;
   jsmn_parser parser;
   int numTokens;
   int ret;

   ASSERT(jsonString);
   jsmn_init(&parser);
   jsonLength = strlen(jsonString);
   numTokens = TOKENS_PER_ALLOC;
   *tokens = (jsmntok_t *) g_malloc0(numTokens * sizeof(jsmntok_t));

   while ((ret = jsmn_parse(&parser, jsonString, jsonLength,
                            *tokens, numTokens)) == JSMN_ERROR_NOMEM) {
      numTokens += TOKENS_PER_ALLOC;
      if (numTokens > MAX_TOKENS) {
         g_warning("%s:%d: number of jsmn tokens: %d exceeded max :%d",
                   __FUNCTION__, __LINE__,
                   numTokens, MAX_TOKENS);
         g_free(*tokens);
         *tokens = NULL;
         return -1;
      }
      *tokens = g_realloc(*tokens, numTokens * sizeof(jsmntok_t));
   }

   if (ret < 0) {
      g_warning("%s:%d: jsmn error: %d parsing failed at character %d\n",
                __FUNCTION__, __LINE__, ret, parser.pos);
      g_free(*tokens);
      *tokens = NULL;
   }

   return ret;
}


/*
 *****************************************************************************
 * ContainerInfo_GetDockerContainers --
 *
 * @brief  Entry point for gathering running docker container info
 *
 *
 * @retval TRUE   successfully collected docker container info
 * @retval FALSE  on failure
 *
 *****************************************************************************
 */

GHashTable *
ContainerInfo_GetDockerContainers(const char *dockerSocketPath)          // IN
{
   jsmntok_t *t = NULL;
   int i;
   int numTokens;
   char *dockerContainerString = NULL;
   char *endpt = g_strdup_printf("http://%s/containers/json?"
                                 "filters={\"status\":[\"running\"]}",
                                 DOCKER_API_VERSION);
   GHashTable *containerTable = NULL;

   if (!DockerCallAPI(endpt,
                      dockerSocketPath,
                      &dockerContainerString)) {
       g_warning("%s: Failed to get the list of containers.", __FUNCTION__);
       goto exit;
   }

   numTokens = ContainerInfoParseString(dockerContainerString, &t);

   if (numTokens <= 0 || t[0].type != JSMN_ARRAY) {
      g_warning("%s: invalid json response\n",
                __FUNCTION__);
      goto exit;
   }

   containerTable = g_hash_table_new_full(g_str_hash, g_str_equal,
                                          g_free, g_free);

   /* Example of "GET containers/json" response.
    * Each item in the array is a running container.
    * [{"Id":"370a480816ec5207c620fe628bd162925b85d150b3303601f76c3fe47ed863de",
    *   "Names":["/fervent_goldwasser"],
    *   "Image":"redis",
    *   "ImageID":"sha256:de974760ddb2f32dbddb74b7bb8cff4c1eee06d43d36d11bbc",
    *   "Command":"docker-entrypoint.sh redis-server",
    *   "Created":1623742538,
    *   "Ports":[{"PrivatePort":6379,"Type":"tcp"}],
    *   "Labels":{},
    *   "State":"running",
    *   "Status":"Up 29 minutes",
    *   "HostConfig":{"NetworkMode":"default"},
    *   "NetworkSettings":{...},
    *   "Mounts":[...]},
    *  {"Id":"b3ba5ed8b84816c66a6b6fe5903565164ea953ecdddf190263d52ed6ad0f6088",
    *   "Names":["/bold_solomon"],
    *   "Image":"nginx",
    *   "ImageID":"sha256:62d49f9bab67f7c70ac3395855bf01389eb3175b374e621f6f19",
    *   "Command":"/docker-entrypoint.sh nginx -g 'daemon off;'",
    *   "Created":1623742533,
    *   "Ports":[{"PrivatePort":80,"Type":"tcp"}],
    *   "Labels":{"maintainer":"NGINX Docker Maintainers"},
    *   "State":"running",
    *   "Status":"Up 29 minutes",
    *   "HostConfig":{"NetworkMode":"default"},
    *   "NetworkSettings":{...},
    *   "Mounts":[]}]
    */
   i = 1;
   while (i < numTokens) {
      if (t[i].type == JSMN_OBJECT) {
         char *id = NULL;
         char *image = NULL;
         int end = t[i].end;

         i++;
         while (i < numTokens - 1 && t[i + 1].start < end) {
            if (t[i].type == JSMN_STRING &&
                t[i + 1].type == JSMN_STRING) {
               if (ContainerInfoJsonEqIsKey(dockerContainerString,
                                            &t[i], "Id")) {
                  if (id != NULL) {
                     g_warning("%s:%d: found duplicate key for \"Id\". Json"
                               "has improper format\n", __FUNCTION__, __LINE__);
                     break;
                  }

                  id = g_strdup_printf("%.*s",
                                       t[i + 1].end - t[i + 1].start,
                                       dockerContainerString + t[i + 1].start);
               } else if (ContainerInfoJsonEqIsKey(dockerContainerString,
                                                   &t[i], "Image")) {
                  if (image != NULL) {
                     g_warning("%s:%d: found duplicate key for \"Image\". Json"
                               "has improper format\n", __FUNCTION__, __LINE__);
                     break;
                  }

                  image =
                     g_strdup_printf("%.*s",
                                     t[i + 1].end - t[i + 1].start,
                                     dockerContainerString + t[i + 1].start);
               }
            }

            if (image != NULL && id != NULL) {
               g_debug("%s: Found docker container id: %s and image: %s",
                       __FUNCTION__, id, image);
               g_hash_table_insert(containerTable, id, image);
               id = NULL;
               image = NULL;
               break;
            }
            i++;
         }

         /*
          * Check id and image in the case of (image && !id) and (!image && id)
          */
         if (id != NULL) {
            g_free(id);
         }
         if (image != NULL) {
            g_free(image);
         }
      }
      i++;
   }

exit:
   g_free(t);
   g_free(endpt);
   g_free(dockerContainerString);
   return containerTable;
}
 070701000002E9000081A40000000000000000000000016822550500001570000000000000000000000000000000000000005800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/containerInfo/containerInfo_grpc.cc   /*********************************************************
 * Copyright (c) 2021-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * containerInfo_grpc.cc --
 *
 *    This file defines specific functions which are needed to query
 *    the containerd daemon and retrieve the list of running
 *    containers. A gRPC connection is created using the containerd unix
 *    socket and the specified namespace is queried for any running containers.
 */

#include "containerInfoInt.h"
#include "containers.grpc.pb.h"
#include "tasks.grpc.pb.h"
#include <grpc++/grpc++.h>
#include <stdio.h>

using namespace containerd::services::containers::v1;
using namespace containerd::services::tasks::v1;
using namespace containerd::v1::types;
using namespace google::protobuf;


/*
 ******************************************************************************
 * ContainerInfo_GetContainerList --
 *
 * @brief   A gRPC connection is created with the containerd unix
 *          socket and specified namespace is inspected for
 *          running containers.
 *
 * @param[in] ns                       Namespace to be queried.
 * @param[in] containerdSocketPath     Path of the socket.
 * @param[in] maxContainers            Maximum number of containers to be
 *                                     returned in the list.
 *
 * @retval the list of running containers.
 *         NULL if any error occurs or no containers are running.
 *
 ******************************************************************************
 */

GSList *
ContainerInfo_GetContainerList(const char *ns,                   // IN
                               const char *containerdSocketPath, // IN
                               unsigned int maxContainers)       // IN
{
   GSList *containerList = NULL;
   std::shared_ptr<grpc::ChannelInterface> channel;
   std::unique_ptr<Containers::Stub> containerStub;
   std::unique_ptr<Tasks::Stub> taskStub;
   grpc::Status status;
   int i;
   unsigned int containersAdded;
   const ListContainersRequest req;
   std::unique_ptr<ListContainersResponse> res;
   grpc::ClientContext containerContext;
   static const std::string namespaceKey = "containerd-namespace";
   gchar *unixSocket = NULL;

   if (ns == NULL || containerdSocketPath == NULL) {
      g_warning("%s: Invalid arguments specified.\n", __FUNCTION__);
      goto exit;
   }

   unixSocket = g_strdup_printf("unix://%s", containerdSocketPath);

   containerContext.AddMetadata(namespaceKey, ns);

   channel =
      grpc::CreateChannel(unixSocket, grpc::InsecureChannelCredentials());

   if (channel == nullptr) {
      g_warning("%s: Failed to create gRPC channel\n", __FUNCTION__);
      goto exit;
   }

   containerStub = Containers::NewStub(channel);
   if (containerStub == nullptr) {
      g_warning("%s: Failed to create containerStub\n", __FUNCTION__);
      goto exit;
   }

   taskStub = Tasks::NewStub(channel);
   if (taskStub == nullptr) {
      g_warning("%s: Failed to create taskStub\n", __FUNCTION__);
      goto exit;
   }

   res = std::make_unique<ListContainersResponse>();
   status = containerStub->List(&containerContext, req, res.get());

   if (!status.ok()) {
      g_warning("%s: Failed to list containers. Error: %s\n", __FUNCTION__,
                status.error_message().c_str());
      goto exit;
   }

   g_debug("%s: Namespace: '%s', number of containers found: %d", __FUNCTION__,
           ns, res->containers_size());

   for (i = 0, containersAdded = 0;
        i < res->containers_size() && containersAdded < maxContainers; i++) {
      Container curContainer = res->containers(i);
      std::string id = curContainer.id();
      std::string image = curContainer.image();
      ContainerInfo *info;
      GetRequest taskReq;
      std::unique_ptr<GetResponse> taskRes;
      grpc::Status taskStatus;
      grpc::ClientContext taskContext;

      taskContext.AddMetadata(namespaceKey, ns);

      /*
       * Get pid for container using taskStub.
       */
      taskRes = std::make_unique<GetResponse>();
      taskReq.set_container_id(id);
      taskStatus = taskStub->Get(&taskContext, taskReq, taskRes.get());
      if (!taskStatus.ok()) {
         g_debug("%s: Task get service failed: %s. skipping container: %s\n",
                 __FUNCTION__, taskStatus.error_message().c_str(), id.c_str());
         continue;
      }

      info = (ContainerInfo *)g_malloc(sizeof(*info));
      info->id = g_strdup(id.c_str());
      info->image = g_strdup(image.c_str());

      g_debug("%s: Found container id: %s and image: %s\n", __FUNCTION__,
              info->id, info->image);
      containerList = g_slist_prepend(containerList, info);
      containersAdded++;
   }

exit:
   g_free(unixSocket);
   return containerList;
}
070701000002EA000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/deployPkg 070701000002EB000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/deployPkg/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002EC000081A4000000000000000000000001682255050000062F000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/deployPkg/Makefile.am ################################################################################
### Copyright (C) 2014-2017 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libdeployPkgPlugin.la

libdeployPkgPlugin_la_CPPFLAGS =
libdeployPkgPlugin_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libdeployPkgPlugin_la_LDFLAGS =
libdeployPkgPlugin_la_LDFLAGS += @PLUGIN_LDFLAGS@

libdeployPkgPlugin_la_LIBADD =
libdeployPkgPlugin_la_LIBADD += @VMTOOLS_LIBS@
libdeployPkgPlugin_la_LIBADD += @GOBJECT_LIBS@
libdeployPkgPlugin_la_LIBADD += $(top_builddir)/libDeployPkg/libDeployPkg.la

libdeployPkgPlugin_la_SOURCES =
libdeployPkgPlugin_la_SOURCES += deployPkg.c
libdeployPkgPlugin_la_SOURCES += deployPkgInt.h
libdeployPkgPlugin_la_SOURCES += deployPkgLog.c
libdeployPkgPlugin_la_SOURCES += deployPkgPlugin.c
 070701000002ED000081A40000000000000000000000016822550500004680000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/deployPkg/deployPkg.c /*********************************************************
 * Copyright (C) 2006-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * deployPkg.c --
 *
 *    Support functions for guest package deployment.
 *
 */

#include "deployPkgInt.h"
#ifdef __cplusplus
#include "deployPkg/deployPkgDll.h"
#else
#include "deployPkg/linuxDeployment.h"
#endif

#include "file.h"
#include "random.h"
#include "str.h"
#include "util.h"
#include "unicodeBase.h"
#include "conf.h"

#include "vm_tools_version.h"
#include "buildNumber.h"
#include "vm_product.h"

#ifdef __cplusplus
extern "C" {
#endif
   #include "vmware/tools/plugin.h"
   #include "vmware/tools/threadPool.h"
#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
// deployPkg.c is compiled using c++ in Windows.
// For c++, LogLevel enum is defined in ImgCustCommon namespace.
using namespace ImgCustCommon;
#endif

// Using 3600s as the upper limit of timeout value in tools.conf.
#define MAX_TIMEOUT_FROM_TOOLCONF 3600
// Using 1800s as the upper limit of waiting for cloud-init execution done
// timeout value in tools.conf.
#define MAX_TIMEOUT_WAIT_FOR_CLOUDINIT_DONE 1800

static char *DeployPkgGetTempDir(void);


/*
 *----------------------------------------------------------------------
 *
 * DeployPkgDeployPkgInGuest --
 *
 *    Load the deployPkg so, setup logging and do the job.
 *
 * Results:
 *    TOOLSDEPLOYPKG_ERROR_SUCCESS on success
 *    TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED on failure
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static ToolsDeployPkgError
DeployPkgDeployPkgInGuest(ToolsAppCtx *ctx,    // IN: app context
                          const char* pkgFile, // IN: the package filename
                          char* errBuf,        // OUT: buffer for msg on fail
                          int errBufSize)      // IN: size of errBuf
{
   char *tempFileName = NULL;
   ToolsDeployPkgError ret = TOOLSDEPLOYPKG_ERROR_SUCCESS;
#ifndef _WIN32
   int processTimeout;
   int waitForCloudinitDoneTimeout;
#endif

   /*
    * Init the logger
    * PR 2109109. If the deployPkg log handler has been configured explicitly in
    * tools.conf, then output deployPkg log through the specified handler.
    * https://wiki.eng.vmware.com/Configuring_Logging_for_the_VMware_Tools
    * If not, output the log to the default log file defined in
    * function DeployPkgLog_Open.
    * The deployPkg log handler is mainly configured for debugging purpose.
    */
   char key[128];
   char *handler;
   snprintf(key, sizeof key, "%s.handler", G_LOG_DOMAIN);
   handler = VMTools_ConfigGetString(ctx->config,
                                     CONFGROUPNAME_LOGGING,
                                     key,
                                     NULL);
   if (handler != NULL &&
       (strcmp(handler, "vmx") == 0 || strcmp(handler, "file") == 0 ||
        strcmp(handler, "file+") == 0)) {
      g_debug("Using deployPkg log handler: %s", handler);
      free(handler);
   } else {
      DeployPkgLog_Open();

      if (handler != NULL) {
         DeployPkgLog_Log(log_debug,
                          "Log handler %s is not applicable for deployPkg,"
                          " ignore it and ouput the log in GOS customization"
                          " default log path.",
                          handler);
         free(handler);
      }
   }
   DeployPkg_SetLogger(DeployPkgLog_Log);

   DeployPkgLog_Log(log_info, "%s Version: %s (%s)", VMWARE_TOOLS_SHORT_NAME,
                    TOOLS_VERSION_EXT_CURRENT_STR, BUILD_NUMBER);
   DeployPkgLog_Log(log_debug, "Deploying %s", pkgFile);

#ifdef _WIN32
   /*
    * Because DeployPkg_DeployPackageFromFile can only accept file path of
    * local code page under Windows, convert pkgFile from utf8 to local code
    * page.
    *
    * PR 962946
    */
   tempFileName = (char *)Unicode_GetAllocBytes(pkgFile, STRING_ENCODING_DEFAULT);
   if (tempFileName == NULL) {
      Str_Snprintf(errBuf, errBufSize,
                   "Package deploy failed in Unicode_GetAllocBytes");
      DeployPkgLog_Log(log_error, errBuf);
      ret = TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED;
      goto ExitPoint;
   }
   pkgFile = tempFileName;
#else
   /*
    * Get processTimeout from tools.conf.
    * Only when we get valid 'timeout' value from tools.conf, we will call
    * DeployPkg_SetProcessTimeout to over-write the processTimeout in deployPkg
    * Using 0 as the default value of CONFNAME_DEPLOYPKG_PROCESSTIMEOUT in tools.conf
    */
   processTimeout =
      VMTools_ConfigGetInteger(ctx->config,
                               CONFGROUPNAME_DEPLOYPKG,
                               CONFNAME_DEPLOYPKG_PROCESSTIMEOUT,
                               0);
   if (processTimeout > 0 && processTimeout <= MAX_TIMEOUT_FROM_TOOLCONF) {
      DeployPkgLog_Log(log_debug, "[%s] %s in tools.conf: %d",
                       CONFGROUPNAME_DEPLOYPKG,
                       CONFNAME_DEPLOYPKG_PROCESSTIMEOUT,
                       processTimeout);
      DeployPkg_SetProcessTimeout(processTimeout);
   } else if (processTimeout != 0) {
      DeployPkgLog_Log(log_debug, "Invalid value %d from tools.conf [%s] %s",
                       processTimeout,
                       CONFGROUPNAME_DEPLOYPKG,
                       CONFNAME_DEPLOYPKG_PROCESSTIMEOUT);
      DeployPkgLog_Log(log_debug, "The valid timeout value range: 1 ~ %d",
                       MAX_TIMEOUT_FROM_TOOLCONF);
   }

   /*
    * Get timeout of waiting for cloud-init execution done from tools.conf.
    * Only when a valid 'timeout' got from tools.conf, deployPkg will call
    * DeployPkg_SetWaitForCloudinitDoneTimeout to overwrite the default timeout
    * of waiting for cloud-init execution done.
    * The valid value range is from 0 to MAX_TIMEOUT_WAIT_FOR_CLOUDINIT_DONE.
    * Return an invalid value -1 if CONFNAME_DEPLOYPKG_WAIT_CLOUDINIT_TIMEOUT is
    * not set in tools.conf.
    */
   waitForCloudinitDoneTimeout =
      VMTools_ConfigGetInteger(ctx->config,
                               CONFGROUPNAME_DEPLOYPKG,
                               CONFNAME_DEPLOYPKG_WAIT_CLOUDINIT_TIMEOUT,
                               -1);
   if (waitForCloudinitDoneTimeout >= 0 &&
       waitForCloudinitDoneTimeout <= MAX_TIMEOUT_WAIT_FOR_CLOUDINIT_DONE) {
      DeployPkgLog_Log(log_debug, "[%s] %s in tools.conf: %d",
                       CONFGROUPNAME_DEPLOYPKG,
                       CONFNAME_DEPLOYPKG_WAIT_CLOUDINIT_TIMEOUT,
                       waitForCloudinitDoneTimeout);
      DeployPkg_SetWaitForCloudinitDoneTimeout(waitForCloudinitDoneTimeout);
   } else {
      if (waitForCloudinitDoneTimeout != -1) {
         DeployPkgLog_Log(log_debug,
                          "Ignore invalid value %d from tools.conf [%s] %s",
                          waitForCloudinitDoneTimeout,
                          CONFGROUPNAME_DEPLOYPKG,
                          CONFNAME_DEPLOYPKG_WAIT_CLOUDINIT_TIMEOUT);
      }
      DeployPkgLog_Log(log_debug, "The valid [%s] %s value range: 0 ~ %d",
                       CONFGROUPNAME_DEPLOYPKG,
                       CONFNAME_DEPLOYPKG_WAIT_CLOUDINIT_TIMEOUT,
                       MAX_TIMEOUT_WAIT_FOR_CLOUDINIT_DONE);
   }
#endif

   if (0 != DeployPkg_DeployPackageFromFile(pkgFile)) {
      Str_Snprintf(errBuf, errBufSize,
                   "Package deploy failed in DeployPkg_DeployPackageFromFile");
      DeployPkgLog_Log(log_error, errBuf);
      ret = TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED;
      goto ExitPoint;
   }
   DeployPkgLog_Log(log_debug, "Ran DeployPkg_DeployPackageFromFile successfully");

ExitPoint:
   free(tempFileName);
   DeployPkgLog_Close();
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DeployPkgTcloBegin --
 *
 *    TCLO handler for "deployPkg.begin". Try to get temporary directory for file
 *    copy and return the directory name to vmx as result.
 *
 * Return value:
 *    TRUE if get temp dir and send it back
 *    FALSE if something failed
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

gboolean
DeployPkg_TcloBegin(RpcInData *data)   // IN
{
   char *tempDir = DeployPkgGetTempDir();

   g_debug("DeployPkgTcloBegin got call\n");

   if (tempDir) {
      static char resultBuffer[FILE_MAXPATH];

      Str_Strcpy(resultBuffer, tempDir, sizeof resultBuffer);
      free(tempDir);
      return RPCIN_SETRETVALS(data, resultBuffer, TRUE);
   }
   return RPCIN_SETRETVALS(data, "failed to get temp dir", FALSE);
}


/*
 * ---------------------------------------------------------------------------
 * DeployPkgExecDeploy --
 *
 *    Start the deploy execution in a new thread.
 *
 * Return Value:
 *    None.
 *
 * Side effects:
 *    None.
 *
 * ---------------------------------------------------------------------------
 */

void
DeployPkgExecDeploy(ToolsAppCtx *ctx,   // IN: app context
                    void *pkgName)      // IN: pkg file name
{
   ToolsDeployPkgError ret;
   char *pkgNameStr = (char *) pkgName;
   Bool enableCust;

   g_debug("%s: Deploypkg deploy task started.\n", __FUNCTION__);

   /*
    * Check whether guest customization is enabled by VM Tools,
    * by default it is enabled.
    */
   enableCust = VMTools_ConfigGetBoolean(ctx->config,
                                         CONFGROUPNAME_DEPLOYPKG,
                                         CONFNAME_DEPLOYPKG_ENABLE_CUST,
                                         TRUE);
   if (!enableCust) {
      char *result = NULL;
      size_t resultLen;
      gchar *msg = g_strdup_printf("deployPkg.update.state %d %d %s",
                                   TOOLSDEPLOYPKG_DEPLOYING,
                                   TOOLSDEPLOYPKG_ERROR_CUST_DISABLED,
                                   "Customization is disabled by guest admin");

      g_warning("%s: Customization is disabled by guest admin.\n",
                __FUNCTION__);

      if (!RpcChannel_Send(ctx->rpc, msg, strlen(msg), &result, &resultLen)) {
         g_warning("%s: failed to send error code %d for state "
                   "TOOLSDEPLOYPKG_DEPLOYING, result: %s\n",
                   __FUNCTION__,
                   TOOLSDEPLOYPKG_ERROR_CUST_DISABLED,
                   result != NULL ? result : "");
      }
      g_free(msg);
      vm_free(result);
   } else {
      char errMsg[2048];
      /* Unpack the package and run the command. */
      ret = DeployPkgDeployPkgInGuest(ctx, pkgNameStr, errMsg, sizeof errMsg);
      if (ret != TOOLSDEPLOYPKG_ERROR_SUCCESS) {
#ifdef _WIN32
        /*
         * PR 1631160. for Linux, sysimage has sent failure status in vmx when
         * deploy pkg failed, to avoid sending failure events repeatedly, here
         * is only sending status in the case of windows.
         */
         gchar *msg = g_strdup_printf("deployPkg.update.state %d %d %s",
                                      TOOLSDEPLOYPKG_DEPLOYING,
                                      TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED,
                                      errMsg);

         if (!RpcChannel_Send(ctx->rpc, msg, strlen(msg), NULL, NULL)) {
            g_warning("%s: failed to send error code %d for state "
                      "TOOLSDEPLOYPKG_DEPLOYING\n",
                      __FUNCTION__,
                      TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED);
         }
         g_free(msg);
#endif
         g_warning("DeployPkgInGuest failed, error = %d\n", ret);
      }
   }

   /* Attempt to delete the package file and tempdir. */
   g_debug("Deleting file %s\n", pkgNameStr);
   if (File_Unlink(pkgNameStr) == 0) {
      char *vol, *dir, *path;
      File_SplitName(pkgNameStr, &vol, &dir, NULL);
      path = Str_Asprintf(NULL, "%s%s", vol, dir);
      if (path != NULL) {
         g_debug("Deleting directory %s\n", path);
         File_DeleteEmptyDirectory(path);
         free(path);
      }
      free(vol);
      free(dir);
   } else {
      g_warning("Unable to delete the file: %s\n", pkgNameStr);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DeployPkg_TcloDeploy --
 *
 *    TCLO handler for "deployPkg.deploy". Start image guest package deployment.
 *
 * Return value:
 *    TRUE if success
 *    FALSE if pkg file path is not valid
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
DeployPkg_TcloDeploy(RpcInData *data)  // IN
{
   char *argCopy, *pkgStart, *pkgEnd, *pkgName;
   const char *white = " \t\r\n";

   /* Set state to DEPLOYING. */
   gchar *msg;
   ToolsAppCtx *ctx = (ToolsAppCtx *)(data->appCtx);

   msg = g_strdup_printf("deployPkg.update.state %d",
                         TOOLSDEPLOYPKG_DEPLOYING);
   if (!RpcChannel_Send(ctx->rpc, msg, strlen(msg), NULL, NULL)) {
      g_warning("%s: failed to update state to TOOLSDEPLOYPKG_DEPLOYING\n",
                __FUNCTION__);
   }
   g_free(msg);

   /* The package filename is in args. First clean up extra whitespace: */
   argCopy = Util_SafeStrdup(data->args);
   pkgStart = argCopy;
   while (*pkgStart != '\0' && Str_Strchr(white, *pkgStart) != NULL) {
      pkgStart++;
   }

   pkgEnd = pkgStart + strlen(pkgStart);
   while (pkgEnd != pkgStart && Str_Strchr(white, *pkgEnd) != NULL) {
      *pkgEnd-- = '\0';
   }

   /* Now make sure the package exists */
   if (!File_Exists(pkgStart)) {
      msg = g_strdup_printf("deployPkg.update.state %d %d Package file %s not found",
                            TOOLSDEPLOYPKG_DEPLOYING,
                            TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED,
                            pkgStart);
      if (!RpcChannel_Send(ctx->rpc, msg, strlen(msg), NULL, NULL)) {
         g_warning("%s: failed to send error code %d for state TOOLSDEPLOYPKG_DEPLOYING\n",
                   __FUNCTION__,
                   TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED);
      }
      g_free(msg);
      g_warning("Package file '%s' doesn't exist!!\n", pkgStart);

      free(argCopy);
      return RPCIN_SETRETVALS(data, "failed to get package file", FALSE);
   }

   pkgName = Util_SafeStrdup(pkgStart);
   if (!ToolsCorePool_SubmitTask(ctx, DeployPkgExecDeploy, pkgName, free)) {
      g_warning("%s: failed to start deploy execution thread\n",
                __FUNCTION__);
      msg = g_strdup_printf("deployPkg.update.state %d %d %s",
                            TOOLSDEPLOYPKG_DEPLOYING,
                            TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED,
                            "failed to spawn deploy execution thread");
      if (!RpcChannel_Send(ctx->rpc, msg, strlen(msg), NULL, NULL)) {
         g_warning("%s: failed to send error code %d for state TOOLSDEPLOYPKG_DEPLOYING\n",
                   __FUNCTION__,
                   TOOLSDEPLOYPKG_ERROR_DEPLOY_FAILED);
      }
      g_free(msg);
      free(pkgName);
   }

   free(argCopy);
   return RPCIN_SETRETVALS(data, "", TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DeployPkgGetTempDir --
 *
 *    Try to create a staging directory for a package deployment.
 *
 * Results:
 *    Temporary directory path name in utf8 if success, NULL otherwise
 *
 * Side effects:
 *    Memory may be allocated for result.
 *
 *-----------------------------------------------------------------------------
 */

char *
DeployPkgGetTempDir(void)
{
   int i = 0;
   char *dir = NULL;
   char *newDir = NULL;
   Bool found = FALSE;
   int randIndex;
#ifndef _WIN32
   /*
    * The directories in the array must be sorted by priority from high to low.
    * PR 2942062. On Nested ESXi, use /var/run/vmware-imc directory to hold the
    * package if it exists.
    * PR 2115630. On Linux, use /var/run or /run directory to hold the package.
    */
   const char *searchDirs[] = {
      "/var/run/vmware-imc",         // The highest priority
      "/var/run",
      "/run"                         // The lowest priority
   };

   size_t index;
   for (index = 0; index < ARRAYSIZE(searchDirs); index++) {
      if (File_IsDirectory(searchDirs[index])) {
         dir = strdup(searchDirs[index]);
         if (dir == NULL) {
            g_warning("%s: strdup failed\n", __FUNCTION__);
            goto exit;
         }
         break;
      }
   }
#endif

   /*
    * Get system temporary directory.
    */

   if (dir == NULL && (dir = File_GetSafeRandomTmpDir(TRUE)) == NULL) {
      g_warning("%s: File_GetSafeRandomTmpDir failed\n", __FUNCTION__);
      goto exit;
   }

   /* Make a temporary directory to hold the package. */
   while (!found && i < 10) {
      free(newDir);
      if (!Random_Crypto(sizeof(randIndex), &randIndex)) {
         g_warning("%s: Random_Crypto failed\n", __FUNCTION__);
         newDir = NULL;
         goto exit;
      }
      newDir = Str_Asprintf(NULL, "%s%s%08x%s",
                            dir, DIRSEPS, randIndex, DIRSEPS);
      if (newDir == NULL) {
         g_warning("%s: Str_Asprintf failed\n", __FUNCTION__);
         goto exit;
      }
      found = File_CreateDirectory(newDir);
      i++;
   }

   if (!found) {
      g_warning("%s: could not create temp directory\n", __FUNCTION__);
      free(newDir);
      newDir = NULL;
   }
exit:
   free(dir);
   return newDir;
}
070701000002EE000081A400000000000000000000000168225505000005AD000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/deployPkg/deployPkgInt.h  /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _DEPLOYPKGINT_H_
#define _DEPLOYPKGINT_H_

/**
 * @file deployPkgInt.h
 *
 * Internal definitions for the deployPkg plugin.
 */

#define G_LOG_DOMAIN "deployPkg"
#include <glib.h>
#include "vmware/guestrpc/deploypkg.h"
#include "vmware/tools/guestrpc.h"

G_BEGIN_DECLS

gboolean DeployPkg_TcloBegin(RpcInData *data);
gboolean DeployPkg_TcloDeploy(RpcInData *data);

/* Functions to manage the log */
void DeployPkgLog_Open(void);
void DeployPkgLog_Close(void);
void DeployPkgLog_Log(int level, const char *fmtstr, ...);

G_END_DECLS

#endif /* _DEPLOYPKGINT_H_ */

   070701000002EF000081A400000000000000000000000168225505000014DE000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/deployPkg/deployPkgLog.c  /*********************************************************
 * Copyright (C) 2006-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * deployPkgLog.c --
 *
 *    logger for both windows and posix versions of deployPkg
 */

#include "deployPkgInt.h"
#include "imgcust-common/log.h"
#include "util.h"
#include "file.h"
#include "str.h"
#include "vmware/tools/utils.h"

#include <stdio.h>
#include <sys/stat.h>

#ifdef _WIN32
#include <windows.h>
#include "win32Access.h"
#endif

#define G_LOG_DOMAIN "deployPkg"

static FILE* _file = NULL;


/*
 *----------------------------------------------------------------------
 *
 * DeployPkgLog_Open --
 *
 *    Init the log. Creates a file in %temp%/vmware and
 *    opens it for writing. On linux, only root own r/w right.
 *    On error, the file will not be opened and logging
 *    will be disabled.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    On success, the log file is opened and managed by _file.
 *
 *----------------------------------------------------------------------
 */

void
DeployPkgLog_Open()
{
   char logPath[2048];

#ifdef _WIN32
   DWORD ret = GetTempPathA(sizeof logPath, logPath);

   if (ret == 0) {
      return;
   }

   Str_Strcat(logPath, "vmware-imc", sizeof logPath);
#else
   Str_Strcpy(logPath, "/var/log/vmware-imc", sizeof logPath);
#endif

   if (File_CreateDirectoryHierarchy(logPath, NULL)) {
      Str_Strcat(logPath, DIRSEPS "toolsDeployPkg.log", sizeof logPath);
      _file = fopen(logPath, "w");
      if (_file != NULL) {
#ifndef _WIN32
         setlinebuf(_file);
         (void) chmod(logPath, 0600);
#else
         (void)Win32Access_SetFileOwnerRW(logPath);
#endif
         DeployPkgLog_Log(log_debug, "## Starting deploy pkg operation");
      } else {
         g_debug("%s: failed to open DeployPkg log file: %s\n",
                   __FUNCTION__, logPath);
      }
   } else {
      g_debug("%s: failed to create DeployPkg log directory: %s\n",
                   __FUNCTION__, logPath);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * DeployPkgLog_Close --
 *
 *    Close the log.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    File descriptor managed by _s is closed
 *
 *----------------------------------------------------------------------
 */

void
DeployPkgLog_Close()
{
   if (_file != NULL) {
      DeployPkgLog_Log(log_debug, "## Closing log");
      fclose(_file);
      _file = NULL;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * DeployPkgLog_Log --
 *
 *    If the log file was opened successfully, write to it.
 *    Otherwise call the glib logger, messages are logged
 *    per tools logging configuration.
 *    Note: since g_error() is always fatal and terminate the application,
 *    log_error will be logged as g_warning to avoid terminating the
 *    application.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
DeployPkgLog_Log(int level,          // IN
                 const char *fmtstr, // IN
                 ...)                // IN
{
   va_list args;

   if (fmtstr == NULL) {
      return;
   }

   va_start(args, fmtstr);

   if (_file != NULL) {
      const char *logLevel;
      gchar *tstamp;
      size_t fmtstrLen = strlen(fmtstr);
      switch (level) {
         case log_debug:
            logLevel = "debug";
            break;
         case log_info:
            logLevel = "info";
            break;
         case log_warning:
            logLevel = "warning";
            break;
         case log_error:
            logLevel = "error";
            break;
         default:
            logLevel = "unknown";
            break;
      }

      tstamp = VMTools_GetTimeAsString();
      fprintf(_file, "[%s] [%8s] ",
              (tstamp != NULL) ? tstamp : "no time", logLevel);
      vfprintf(_file, fmtstr, args);
      if (fmtstrLen > 0 && fmtstr[fmtstrLen - 1] != '\n') {
         fprintf(_file, "\n");
      }
      g_free(tstamp);
   } else {
      GLogLevelFlags glogLevel;
      switch (level) {
         case log_debug:
            glogLevel = G_LOG_LEVEL_DEBUG;
            break;
         case log_info:
            glogLevel = G_LOG_LEVEL_INFO;
            break;
         case log_warning:
         case log_error:
            glogLevel = G_LOG_LEVEL_WARNING;
            break;
         default:
            glogLevel = G_LOG_LEVEL_INFO;
            break;
      }

      g_logv(G_LOG_DOMAIN, glogLevel, fmtstr, args);
   }

   va_end(args);
}

  070701000002F0000081A40000000000000000000000016822550500000C2A000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/deployPkg/deployPkgPlugin.c   /*********************************************************
 * Copyright (C) 2008-2016,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file deployPkgPlugin.c
 *
 * Wrapper around the deployPkg library for doing image customization.
 */

#include <stdlib.h>
#include <string.h>

#include "vm_basic_defs.h"
#include "deployPkgInt.h"
#include "vmcheck.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"

#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);


/**
 * Called by the Tools service when loading the deployPkg plugin.
 *
 * @param[in]  ctx   The application context.
 *
 * @return Registration data for the plugin.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "deployPkg",
      NULL,
      NULL
   };

   uint32 vmxVersion = 0;
   uint32 vmxType = VMX_TYPE_UNSET;

   /*
    * Return NULL to disable the plugin if not running in a VMware VM.
    */
   if (!ctx->isVMware) {
      g_info("%s: Not running in a VMware VM.\n", __FUNCTION__);
      return NULL;
   }

   /*
    * Return NULL to disable the plugin if VM is not running on ESX host.
    */
   if (!VmCheck_GetVersion(&vmxVersion, &vmxType) ||
       vmxType != VMX_TYPE_SCALABLE_SERVER) {
      g_info("%s, VM is not running on ESX host.\n", __FUNCTION__);
      return NULL;
   }

   /*
    * Return NULL to disable the plugin if not running in vmsvc daemon.
    */
   if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
      g_info("%s: Not running in vmsvc daemon: container name='%s'.\n",
         __FUNCTION__, ctx->name);
      return NULL;
   }

   /*
    * RpcChannel is neccessary for DeployPkg plugin.
    */
   if (ctx->rpc != NULL) {
      RpcChannelCallback rpcs[] = {
         { "deployPkg.begin", DeployPkg_TcloBegin, NULL, NULL, NULL, 0 },
         { "deployPkg.deploy", DeployPkg_TcloDeploy, NULL, NULL, NULL, 0 }
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) }
      };

      srand(time(NULL));
      regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));

      return &regData;
   } else {
      g_info("%s: Do not load DeployPkg plugin because RpcChannel is unavailable.\n",
         __FUNCTION__);
   }

   return NULL;
}

  070701000002F1000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents 070701000002F2000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002F3000081A4000000000000000000000001682255050000092C000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/Makefile.am ################################################################################
### Copyright (C) 2010-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @VMUSR_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libdesktopEvents.la

libdesktopEvents_la_CPPFLAGS =
libdesktopEvents_la_CPPFLAGS += @GTK_CPPFLAGS@
libdesktopEvents_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libdesktopEvents_la_LDFLAGS =
libdesktopEvents_la_LDFLAGS += @PLUGIN_LDFLAGS@

libdesktopEvents_la_LIBADD =
libdesktopEvents_la_LIBADD += @COMMON_XLIBS@
libdesktopEvents_la_LIBADD += @GTK_LIBS@
libdesktopEvents_la_LIBADD += @VMTOOLS_LIBS@

libdesktopEvents_la_SOURCES =
libdesktopEvents_la_SOURCES += desktopEvents.c
libdesktopEvents_la_SOURCES += reload.c
libdesktopEvents_la_SOURCES += x11Lock.c
libdesktopEvents_la_SOURCES += xioError.c

if HAVE_XSM
libdesktopEvents_la_LIBADD += @XSM_LIBS@
libdesktopEvents_la_SOURCES += sessionMgr.c
libdesktopEvents_la_SOURCES += sessionMgrSignals.c

BUILT_SOURCES =
BUILT_SOURCES += sessionMgrSignals.c
BUILT_SOURCES += sessionMgrSignals.h

CLEANFILES =
CLEANFILES += sessionMgrSignals.c
CLEANFILES += sessionMgrSignals.h

EXTRA_DIST =
EXTRA_DIST += sessionMgrSignals.gm

sessionMgrSignals.c: $(top_srcdir)/services/plugins/desktopEvents/sessionMgrSignals.gm
	glib-genmarshal --body $(top_srcdir)/services/plugins/desktopEvents/sessionMgrSignals.gm > \
	   $@ || (rm -f $@ && exit 1)

sessionMgrSignals.h: $(top_srcdir)/services/plugins/desktopEvents/sessionMgrSignals.gm
	glib-genmarshal --header $(top_srcdir)/services/plugins/desktopEvents/sessionMgrSignals.gm > \
	   $@ || (rm -f $@ && exit 1)
endif
070701000002F4000081A40000000000000000000000016822550500000841000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/deFeatures.h    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _DEFEATURES_H_
#define _DEFEATURES_H_

/**
 * @file deFeatures.h
 *
 * X11-specific featurs of the plugin. Don't include this file directly -
 * include desktopEventsInt.h instead.
 */

#define VMUSER_TITLE    "vmware-user"

void
Reload_Do(void);

gboolean
Reload_Init(ToolsAppCtx *ctx,
            ToolsPluginData *pdata);

void
Reload_Shutdown(ToolsAppCtx *ctx,
                ToolsPluginData *pdata);

#ifndef NO_XSM
gboolean
SessionMgr_Init(ToolsAppCtx *ctx,
                ToolsPluginData *pdata);

void
SessionMgr_Shutdown(ToolsAppCtx *ctx,
                    ToolsPluginData *pdata);
#endif

gboolean
X11Lock_Init(ToolsAppCtx *ctx,
             ToolsPluginData *pdata);

gboolean
XIOError_Init(ToolsAppCtx *ctx,
              ToolsPluginData *pdata);

void
XIOError_Shutdown(ToolsAppCtx *ctx,
                  ToolsPluginData *pdata);


#if defined(DE_MAIN)
static DesktopEventFuncs gFeatures[] = {
   { X11Lock_Init,         NULL,                      FALSE },
   { Reload_Init,          Reload_Shutdown,           FALSE },
#ifndef NO_XSM
   { SessionMgr_Init,      SessionMgr_Shutdown,       FALSE },
#endif
   { XIOError_Init,        XIOError_Shutdown,         FALSE },
};
#endif


#endif /* _DEFEATURES_H_ */

   070701000002F5000081A40000000000000000000000016822550500001144000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/desktopEvents.c /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file desktopEvents.c
 *
 * Entry point for the desktop events plugin. Initializes all the individual
 * features of the plugin.
 */

/*
 * DE_MAIN enables the definition of the list of features of this plugin
 * (gFeatures) defined in the platform-specific "deFeatures.h" files.
 */
#define DE_MAIN
#include "vmware.h"
#include "desktopEventsInt.h"

/*
 ******************************************************************************
 * DesktopEventsShutdown --                                             */ /**
 *
 * Description of DesktopEventsShutdown.
 *
 * Calls the shutdown function of the available features.
 *
 * @param[in]  obj      Unused.
 * @param[in]  ctx      The application context.
 * @param[in]  plugin   Plugin data.
 *
 * @return TRUE.
 *
 ******************************************************************************
 */

static gboolean
DesktopEventsShutdown(gpointer serviceObj,
                      ToolsAppCtx *ctx,
                      ToolsPluginData *plugin)
{
   size_t i;

   for (i = 0; i < ARRAYSIZE(gFeatures); i++) {
      DesktopEventFuncs *f = &gFeatures[i];
      if (f->initialized && f->shutdownFn != NULL) {
         f->shutdownFn(ctx, plugin);
      }
   }

   if (plugin->_private) {
      g_hash_table_remove(plugin->_private, DE_PRIVATE_CTX);
      g_hash_table_unref(plugin->_private);
      plugin->_private = NULL;
   }

   return TRUE;
}


/*
 ******************************************************************************
 * ToolsOnLoad --                                                       */ /**
 *
 * Returns the registration data for the plugin.
 *
 * @param[in]  ctx   The application context.
 *
 * @return The registration data.
 *
 ******************************************************************************
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "desktopEvents",
      NULL,
      NULL,
      NULL
   };

   size_t i;

#if defined(_WIN32)
   /*
    * If we aren't running in a VM (e.g., running in bootcamp natively on
    * a Mac), then return NULL to disable the plugin.
    */
   if (!ctx->isVMware) {
      return NULL;
   }

   g_return_val_if_fail(gPluginHandle != NULL, NULL);
#endif

   regData.regs = g_array_new(FALSE, TRUE, sizeof (ToolsAppReg));
   regData._private = g_hash_table_new(g_str_hash, g_str_equal);
   g_hash_table_insert(regData._private, DE_PRIVATE_CTX, ctx);

   for (i = 0; i < ARRAYSIZE(gFeatures); i++) {
      DesktopEventFuncs *f = &gFeatures[i];
      if (!f->initFn(ctx, &regData)) {
         break;
      }
      f->initialized = TRUE;
   }

   /*
    * Register the shutdown callback and return if all features were
    * initialized successfully.
    */
   if (i == ARRAYSIZE(gFeatures)) {
      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_SHUTDOWN, DesktopEventsShutdown, &regData }
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) },
      };
      g_array_append_vals(regData.regs, regs, ARRAYSIZE(regs));
      return &regData;
   }

   /* Failed to initialize something, clean up and unload. */
   DesktopEventsShutdown(NULL, ctx, &regData);

   /* Cleanup regData to make sure memory is freed. */
   for (i = 0; i < regData.regs->len; i++) {
      ToolsAppReg *reg = &g_array_index(regData.regs, ToolsAppReg, i);
      if (reg->data != NULL) {
         g_array_free(reg->data, TRUE);
      }
   }
   g_array_free(regData.regs, TRUE);

   return NULL;
}
070701000002F6000081A400000000000000000000000168225505000006DC000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/desktopEventsInt.h  /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _DESKTOPEVENTSINT_H_
#define _DESKTOPEVENTSINT_H_

/**
 * @file destopEventsInt.h
 *
 * Internal plugin definitions.
 */

#define VMW_TEXT_DOMAIN "desktopEvents"
#define G_LOG_DOMAIN    VMW_TEXT_DOMAIN
#include "vmware/tools/plugin.h"

typedef struct DesktopEventFuncs {
   gboolean (*initFn)(ToolsAppCtx *, ToolsPluginData *);
   void     (*shutdownFn)(ToolsAppCtx *ctx, ToolsPluginData *);
   gboolean initialized;
} DesktopEventFuncs;


/*
 * This plugin's private data field is a GHashTable*.  Each sub-feature may use
 * that table to keep its own private state.  The following key is reserved and
 * points to the hosting application's ToolsAppCtx.
 */
#define DE_PRIVATE_CTX  "ctx"


/*
 * This platform-specific file defines the list of features available
 * for the current platform being built.
 */
#include "deFeatures.h"

#endif /* _DESKTOPEVENTSINT_H_ */
070701000002F7000081A40000000000000000000000016822550500000DE9000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/reload.c    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file reload.c
 *
 * Code to respond to SIGUSR2 and restart the vmtoolsd instance.
 */

#include "desktopEventsInt.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static GSource *gReloadSrc;


/*
 ******************************************************************************
 * ReloadSelf --                                                        */ /**
 *
 * Signal handler for SIGUSR2. Stops the RPC channel and reloads the vmusr
 * instance.
 *
 * @param[in] info    Unused.
 * @param[in] data    The application context.
 *
 * @return FALSE.
 *
 ******************************************************************************
 */

static gboolean
ReloadSelf(const siginfo_t *info,
           gpointer data)
{
   ToolsAppCtx *ctx = data;
   if (ctx->rpc != NULL) {
      RpcChannel_Stop(ctx->rpc);
   }
   Reload_Do();
   return FALSE;
}


/*
 ******************************************************************************
 * Reload_Do --                                                         */ /**
 *
 * Re-launch vmware-user by attempting to execute VMUSER_TITLE ('vmware-user'),
 * relying on the user's search path.
 *
 * On success, vmware-user is relaunched in our stead.  On failure, we exit with
 * EXIT_FAILURE.
 *
 ******************************************************************************
 */

void
Reload_Do(void)
{
   g_debug("Reloading the vmusr instance.");
   execlp(VMUSER_TITLE, VMUSER_TITLE, NULL);
   _exit(EXIT_FAILURE);
}


/*
 ******************************************************************************
 * Reload_Init --                                                       */ /**
 *
 * Registers a signal handler for SIGUSR2 that reloads the container.
 *
 * @param[in] ctx       Application context.
 * @param[in] pdata     Registration data.
 *
 * @return TRUE.
 *
 ******************************************************************************
 */

gboolean
Reload_Init(ToolsAppCtx *ctx,
            ToolsPluginData *pdata)
{
   gReloadSrc = VMTools_NewSignalSource(SIGUSR2);
   VMTOOLSAPP_ATTACH_SOURCE(ctx, gReloadSrc, ReloadSelf, ctx, NULL);
   return TRUE;
}


/*
 ******************************************************************************
 * Reload_Shutdown --                                                   */ /**
 *
 * Unregisters the SIGUSR2 signal handler.
 *
 * @param[in] ctx   Application context.
 * @param[in] pdata Plugin data (unused).
 *
 ******************************************************************************
 */

void
Reload_Shutdown(ToolsAppCtx *ctx,
                ToolsPluginData *pdata)
{
   g_source_destroy(gReloadSrc);
   g_source_unref(gReloadSrc);
   gReloadSrc = NULL;
}

   070701000002F8000081A400000000000000000000000168225505000053CE000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/sessionMgr.c    /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file sessionMgr.c
 *
 * Support for X session management using the, well, X Session Management
 * Library (libSM) with a little help from the Inter-Client Exchange Library
 * (libICE).  This allows vmusr to receive X session lifecycle events and clean
 * up appropriately upon session termination.  For now, cleanup upon shutdown
 * is the only activity we're interested in.
 *
 * A custom event source is used to bind libICE connections with GLib's main
 * loop and dispatch messages.  As this is the first (and hopefully only)
 * libICE client, all ICE interaction is handled here (as opposed to in a
 * provider application).
 *
 * This should work with any session manager implementing XSMP, covering all
 * of our supported desktop environments.
 *
 * This plugin also maps the XSM callbacks to GLib signals.  See desktopevents.h
 * for details.
 *
 * @todo Scrutinize libICE error handling.  I/O errors should be handled here,
 *       but errors handled by libICE's default may exit().
 */

/*
 * PR 957938 - Handling libICE I/O Errors
 *
 * “Before the application I/O error handler is invoked, protocol libraries
 * that were interested in being notiﬁed of I/O errors will have their Ice-
 * IOErrorProc handlers invoked.
 *
 * “[...]
 *
 * “There are two ways of handling IO errors in ICElib: [...] The next time
 * IceProcessMessages is called it will return a status of IceProcessMessages-
 * IOError. At that time, the application should call IceCloseConnection.”¹
 *
 * Unfortunately libSM, while creating ICE connections of its own, does NOT
 * register an I/O error handler.  So, when such an error occurs, libSM does
 * NOT shut itself down as it should, and so we must take care NOT to close
 * connections ourselves, even while advised by the libICE spec quoted above.
 *
 * Instead, when fed an I/O error, we'll simply log its occurrence and
 * inform GLib to no longer monitor the ICE connection.  This will still
 * allow our application to exit cleanly when receiving SIGTERM, a fatal
 * X server I/O error, etc.
 *
 * 1. Inter-Client Exchange Protocol standard, §13. Error Handling
 *    http://www.x.org/docs/ICE/ICElib.pdf
 */


/* Include first.  Sets G_LOG_DOMAIN. */
#include "desktopEventsInt.h"

#include "vmware.h"
#include "vmware/tools/desktopevents.h"
#include "sessionMgrSignals.h"

#include <errno.h>
#include <glib.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/SM/SMlib.h>
#include <X11/ICE/ICElib.h>


/*
 * Identifier used for SessionMgr private data in DesktopEvents private hash
 * table.
 */
#define DE_FEATURE_KEY  "sessionMgr"


static void InitSignals(ToolsAppCtx* ctx);
static void InitSMProperties(SmcConn smcCnx);


/*
 * libICE integration declarations.
 */


typedef struct {
   GSource *iceSource;
   IceConn  iceCnx;
} ICEWatchCtx;

static gboolean ICEDispatch(GIOChannel *chn, GIOCondition cond, gpointer cbData);
static void ICEIOErrorHandler(IceConn iceCnx);
static void ICEWatch(IceConn iceCnx, IcePointer clientData, Bool opening,
                     IcePointer *watchData);


/*
 * libSM callbacks.
 */


static void SMDieCb(SmcConn smcCnx, SmPointer cbData);
static void SMSaveYourselfCb(SmcConn smcCnx, SmPointer cbData, int saveType,
                             Bool shutdown, int interactStyle, Bool fast);
static void SMSaveCompleteCb(SmcConn smcCnx, SmPointer cbData);
static void SMShutdownCancelledCb(SmcConn smcCnx, SmPointer cbData);


/*
 ******************************************************************************
 * SessionMgr_Init --                                                    */ /**
 *
 * Register custom ICE connection source and sign up with the session manager.
 *
 * @param[in] ctx   Application context.
 * @param[in] pdata Plugin data.
 *
 * @retval TRUE Always "succeeds".  Session management is optional.
 *
 ******************************************************************************
 */

gboolean
SessionMgr_Init(ToolsAppCtx *ctx,
                ToolsPluginData *pdata)
{
   SmcCallbacks smCallbacks;
   unsigned long cbMask =
        SmcSaveYourselfProcMask
      | SmcDieProcMask
      | SmcSaveCompleteProcMask
      | SmcShutdownCancelledProcMask;
   SmcConn smcCnx;
   char errorBuf[128];
   char *clientID = NULL;

   IceSetIOErrorHandler(ICEIOErrorHandler);
   IceAddConnectionWatch(ICEWatch, pdata);

   memset(&smCallbacks, 0, sizeof smCallbacks);
   smCallbacks.save_yourself.callback = &SMSaveYourselfCb;
   smCallbacks.save_yourself.client_data = pdata;
   smCallbacks.save_complete.callback = &SMSaveCompleteCb;
   smCallbacks.save_complete.client_data = pdata;
   smCallbacks.shutdown_cancelled.callback = &SMShutdownCancelledCb;
   smCallbacks.shutdown_cancelled.client_data = pdata;
   smCallbacks.die.callback = &SMDieCb;
   smCallbacks.die.client_data = pdata;

   smcCnx =
      SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor, cbMask,
                        &smCallbacks, NULL, &clientID, sizeof errorBuf, errorBuf);
   if (smcCnx != NULL) {
      InitSignals(ctx);
      InitSMProperties(smcCnx);
      g_hash_table_insert(pdata->_private, DE_FEATURE_KEY, smcCnx);
      g_debug("Registered with session manager as %s\n", clientID);
      free(clientID);
   } else {
      g_message("Failed to register with session manager.\n");
      g_message("SmcOpenConnection: %s\n", errorBuf);
      IceRemoveConnectionWatch(ICEWatch, pdata);
   }

   return TRUE;
}


/*
 ******************************************************************************
 * SessionMgr_Shutdown --                                                */ /**
 *
 * Shuts down XSM and ICE interfaces and frees other resources.
 *
 * @param[in] ctx   Application context.
 * @param[in] pdata Plugin data.
 *
 ******************************************************************************
 */

void
SessionMgr_Shutdown(ToolsAppCtx *ctx,
                    ToolsPluginData *pdata)
{
   GHashTable *desktopData = pdata->_private;
   SmcConn smcCnx = g_hash_table_lookup(desktopData, DE_FEATURE_KEY);
   if (smcCnx != NULL) {
      SmcCloseConnection(smcCnx, 0, NULL);
      IceRemoveConnectionWatch(ICEWatch, pdata);
      g_hash_table_remove(desktopData, DE_FEATURE_KEY);
   }
}


/*
 ******************************************************************************
 * InitSignals --                                                        */ /**
 *
 * Creates new signals for XSM events.
 *
 * @param[in]  ctx      ToolsAppCtx*: Application context.
 *
 ******************************************************************************
 */

static void
InitSignals(ToolsAppCtx *ctx)
{
   /* SmcCallbacks::save_yourself */
   g_signal_new(TOOLS_CORE_SIG_XSM_SAVE_YOURSELF,
                G_OBJECT_TYPE(ctx->serviceObj),
                0,      // GSignalFlags
                0,      // class offset
                NULL,   // accumulator
                NULL,   // accu_data
                g_cclosure_user_marshal_VOID__POINTER_INT_BOOLEAN_INT_BOOLEAN,
                G_TYPE_NONE,
                5,
                G_TYPE_POINTER,
                G_TYPE_INT,
                G_TYPE_BOOLEAN,
                G_TYPE_INT,
                G_TYPE_BOOLEAN);

   /* SmcCallbacks::die */
   g_signal_new(TOOLS_CORE_SIG_XSM_DIE,
                G_OBJECT_TYPE(ctx->serviceObj),
                0,      // GSignalFlags
                0,      // class offset
                NULL,   // accumulator
                NULL,   // accu_data
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);

   /* SmcCallbacks::save_complete */
   g_signal_new(TOOLS_CORE_SIG_XSM_SAVE_COMPLETE,
                G_OBJECT_TYPE(ctx->serviceObj),
                0,      // GSignalFlags
                0,      // class offset
                NULL,   // accumulator
                NULL,   // accu_data
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);

   /* SmcCallbacks::shutdown_cancelled */
   g_signal_new(TOOLS_CORE_SIG_XSM_SHUTDOWN_CANCELLED,
                G_OBJECT_TYPE(ctx->serviceObj),
                0,      // GSignalFlags
                0,      // class offset
                NULL,   // accumulator
                NULL,   // accu_data
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);
}


/*
 ******************************************************************************
 * InitSMProperties --                                                   */ /**
 *
 * Tell the session manager a little bit about ourself.
 *
 * The most important property to us is SmRestartStyleHint, where we hint to
 * the session manager that it shouldn't attempt to restore the vmusr container
 * as part of a session.  (Instead, that job is handled by our XDG autostart
 * entry.)
 *
 * The other properties are set only because SMlib docs claim they're
 * mandatory.  Dummy values are used where possible.
 *
 * @param[in]  smcCnx   SM client connection.
 *
 ******************************************************************************
 */

static void
InitSMProperties(SmcConn smcCnx)
{
   enum {
      PROP_ID_CLONE_CMD,
      PROP_ID_PROGRAM,
      PROP_ID_RESTART_CMD,
      PROP_ID_RESTART_STYLE,
      PROP_ID_USER_ID,
   };
   static uint8 restartHint = SmRestartNever;
   static SmPropValue values[] = {
      { sizeof "/bin/false" - 1, "/bin/false" },
      { sizeof "vmware-user" - 1, "vmware-user" },
      { sizeof "/bin/false" - 1, "/bin/false" },
      { sizeof restartHint, &restartHint },
      { 0, NULL },
   };
   static SmProp properties[] = {
      { SmCloneCommand, SmLISTofARRAY8, 1, &values[PROP_ID_CLONE_CMD] },
      { SmProgram, SmARRAY8, 1, &values[PROP_ID_PROGRAM] },
      { SmRestartCommand, SmLISTofARRAY8, 1, &values[PROP_ID_RESTART_CMD] },
      { SmRestartStyleHint, SmCARD8, 1, &values[PROP_ID_RESTART_STYLE] },
      { SmUserID, SmARRAY8, 1, &values[PROP_ID_USER_ID] },
   };
   static SmProp *propp[] = {
      &properties[0], &properties[1], &properties[2], &properties[3],
      &properties[4]
   };

   struct passwd *pw = getpwuid(getuid());
   values[PROP_ID_USER_ID].length = strlen(pw->pw_name);
   values[PROP_ID_USER_ID].value = pw->pw_name;

   SmcSetProperties(smcCnx, ARRAYSIZE(propp), (SmProp**)propp);
}


/*
 ******************************************************************************
 * BEGIN libICE stuff.
 */


/*
 * A note on source reference counting:
 *
 * There are two entities that maintain references on ICEWatchCtx's GSource.
 *    - GLib's GMainContext, and
 *    - libICE's IceConn (via ICEWatch's watchData).
 *
 * A source's initial reference created in ICEWatch may be considered as
 * transferred to libICE until ICEWatch is again called upon connection close.
 * The second reference comes from attaching the source to the GLib main loop.
 */


/*
 ******************************************************************************
 * ICEIOErrorHandler --                                                  */ /**
 *
 * Handler for libICE I/O errors.
 *
 * Does nothing but replaces libICE's default handler which would've caused us
 * to exit.
 *
 * @param[in]  iceCnx    Opaque ICE connection descriptor.
 *
 ******************************************************************************
 */

static void
ICEIOErrorHandler(IceConn iceCnx)
{
   g_message("%s: %s\n", __func__, strerror(errno));
}


/*
 ******************************************************************************
 * ICEDispatch --                                                        */ /**
 *
 * GSource dispatch routine.  Calls IceProcessMessages on the ICE connection.
 *
 * @param[in]  chn      GIOChannel event source
 * @param[in]  cond     condition satisfied (ignored)
 * @param[in]  cbData   (ICEWatchCtx*) Channel context.
 *
 * @retval TRUE  Event loop should continue to monitoring event source.
 * @retval FALSE Event loop should cease monitoring event source.
 *
 ******************************************************************************
 */

static gboolean
ICEDispatch(GIOChannel *chn,
            GIOCondition cond,
            gpointer cbData)
{
   IceProcessMessagesStatus status;
   ICEWatchCtx *watchCtx = cbData;
   ASSERT(watchCtx);

   /*
    * We ignore the error conditions here and let IceProcessMessages return
    * an IceProcessMessagesIOError, resulting in a single, shared error code
    * path.
    */

   status = IceProcessMessages(watchCtx->iceCnx, NULL, NULL);
   switch (status) {
   case IceProcessMessagesSuccess:
      return TRUE;
   case IceProcessMessagesIOError:
      /*
       * See “Handling libICE I/O Errors” above.  watchCtx will float around
       * until libSM calls IceCloseConnection, upon which ICEWatch will free
       * those resources.
       */
      g_message("%s: encountered IceProcessMessagesIOError\n", __func__);
      g_message("%s: detaching fd %d from application event loop\n",
                __func__, IceConnectionNumber(watchCtx->iceCnx));
      return FALSE;
   case IceProcessMessagesConnectionClosed:
      /*
       * iceCnx was invalidated, so we won't see another call to ICEWatch,
       * so we'll return FALSE and let GLib destroy the source for us.
       */
      watchCtx->iceCnx = NULL;
      g_source_unref(watchCtx->iceSource);
      return FALSE;
   }

   NOT_REACHED();
}


/*
 ******************************************************************************
 * ICEWatch --                                                           */ /**
 *
 * libICE connection watching callback.
 *
 * Creates or removes GLib event sources upon ICE connection creation/
 * destruction.
 *
 * From ICElib.xml:
 *    "If opening is True the client should set the *watch_data pointer to
 *    any data it may need to save until the connection is closed and the
 *    watch procedure is invoked again with opening set to False."
 *
 * @param[in]      iceCnx    Opaque ICE connection descriptor.
 * @parma[in]      cbData    (ToolsPluginData*) plugin data
 * @param[in]      opening   True if creating a connection, False if closing.
 * @param[in,out]  watchData See above.  New source will be stored here.
 *
 ******************************************************************************
 */

static void
ICEWatch(IceConn iceCnx,
         IcePointer cbData,
         Bool opening,
         IcePointer *watchData)
{
   ToolsPluginData *pdata = cbData;
   ToolsAppCtx *ctx;

   ctx = g_hash_table_lookup(pdata->_private, DE_PRIVATE_CTX);
   ASSERT(ctx);

   g_debug("%s: fd %d opening %d\n", __func__, IceConnectionNumber(iceCnx),
           opening);

   if (opening) {
      GIOChannel *iceChannel;
      GSource *iceSource;
      ICEWatchCtx *watchCtx;
      GError *error = NULL;

      iceChannel = g_io_channel_unix_new(IceConnectionNumber(iceCnx));
      if (g_io_channel_set_encoding(iceChannel, NULL, &error) != G_IO_STATUS_NORMAL) {
         g_warning("%s: g_io_channel_set_encoding: %s\n", __func__, error->message);
         g_clear_error(&error);
         g_io_channel_unref(iceChannel);
         return;
      }
      g_io_channel_set_buffered(iceChannel, FALSE);

      iceSource = g_io_create_watch(iceChannel, G_IO_IN|G_IO_HUP|G_IO_ERR);
      g_io_channel_unref(iceChannel);   // Ownership transferred to iceSource.

      watchCtx = g_new(ICEWatchCtx, 1);
      watchCtx->iceSource = iceSource;
      watchCtx->iceCnx = iceCnx;
      *watchData = watchCtx;

      VMTOOLSAPP_ATTACH_SOURCE(ctx, iceSource, &ICEDispatch, watchCtx, NULL);
   } else {
      ICEWatchCtx *watchCtx = *watchData;
      if (watchCtx) {
         watchCtx->iceCnx = NULL;
         if (watchCtx->iceSource) {
            g_source_destroy(watchCtx->iceSource);
            g_source_unref(watchCtx->iceSource);
         }
         g_free(watchCtx);
         *watchData = NULL;
      }
   }
}


/*
 * END libICE stuff.
 ******************************************************************************
 */


/*
 ******************************************************************************
 * BEGIN libSM stuff.
 */


/*
 ******************************************************************************
 * SMDieCb --                                                            */ /**
 *
 * Callback for a XSM "Die" event.
 *
 * Instructs the main loop to quit.  We "acknowledge" the callback by closing
 * the connection in our shutdown handler.
 *
 * @param[in]  smcCnx   Opaque XSM connection object.
 * @param[in]  cbData   (ToolsPluginData*) Plugin data.
 *
 ******************************************************************************
 */

static void
SMDieCb(SmcConn smcCnx,
        SmPointer cbData)
{
   ToolsPluginData *pdata = cbData;
   ToolsAppCtx *ctx;

   ASSERT(pdata);

   ctx = g_hash_table_lookup(pdata->_private, DE_PRIVATE_CTX);
   ASSERT(ctx);

   g_message("Session manager says our time is up.  Exiting.\n");
   g_signal_emit_by_name(ctx->serviceObj, TOOLS_CORE_SIG_XSM_DIE, ctx);
   g_main_loop_quit(ctx->mainLoop);
}


/*
 ******************************************************************************
 * SMSaveYourselfCb --                                                   */ /**
 *
 * Callback for XSM "SaveYourself" event.
 *
 * This event is sent to all XSM clients either to checkpoint a session
 * or in advance of a (cancellable) session  shutdown event.  If we needed
 * time to persist state, now would be the time to do it.  Since we don't,
 * however, this is nearly a no-op -- we only acknowledge the manager.
 *
 * @param[in]  smcCnx   Opaque XSM connection object.
 * @param[in]  cbData   (ToolsPluginData*) Plugin data.
 * @param[in]  saveType Refer to SMlib.xml.
 * @param[in]  shutdown Checkpoint or shutdown?
 * @param[in]  interactStyle May interact with user?
 * @param[in]  fast     Shutdown as quickly as possible.
 *
 * @todo Consider whether it'd make sense to unregister capabilities and pause
 *       other plugins if shutdown == True until either we receive a 'die' or
 *       'shutdown cancelled' event.
 *
 ******************************************************************************
 */

static void
SMSaveYourselfCb(SmcConn smcCnx,
                 SmPointer cbData,
                 int saveType,
                 Bool shutdown,
                 int interactStyle,
                 Bool fast)
{
   ToolsPluginData *pdata = cbData;
   ToolsAppCtx *ctx;

   ASSERT(pdata);

   ctx = g_hash_table_lookup(pdata->_private, DE_PRIVATE_CTX);
   ASSERT(ctx);

   g_signal_emit_by_name(ctx->serviceObj, TOOLS_CORE_SIG_XSM_SAVE_YOURSELF,
                         ctx, saveType, shutdown, interactStyle, fast);
   SmcSaveYourselfDone(smcCnx, True);
}


/*
 ******************************************************************************
 * SMSaveCompleteCb --                                                   */ /**
 *
 * Callback for XSM "SaveComplete" event.
 *
 * State has been checkpointed.  Application may resume normal operations.
 * Total no-op.
 *
 * @param[in]  smcCnx   Opaque XSM connection object.
 * @param[in]  cbData   (ToolsPluginData*) Plugin data.
 *
 ******************************************************************************
 */

static void
SMSaveCompleteCb(SmcConn smcCnx,
                 SmPointer cbData)
{
   ToolsPluginData *pdata = cbData;
   ToolsAppCtx *ctx;

   ASSERT(pdata);

   ctx = g_hash_table_lookup(pdata->_private, DE_PRIVATE_CTX);
   ASSERT(ctx);

   g_signal_emit_by_name(ctx->serviceObj, TOOLS_CORE_SIG_XSM_SAVE_COMPLETE, ctx);
}


/*
 ******************************************************************************
 * SMShutdownCancelledCb --                                              */ /**
 *
 * Callback for XSM "ShutdownCancelled" event.
 *
 * User cancelled shutdown.  May resume normal operations.  Again, total no-op.
 *
 * @param[in]  smcCnx   Opaque XSM connection object.
 * @param[in]  cbData   (ToolsPluginData*) Plugin data.
 *
 ******************************************************************************
 */

static void
SMShutdownCancelledCb(SmcConn smcCnx,
                      SmPointer cbData)
{
   ToolsPluginData *pdata = cbData;
   ToolsAppCtx *ctx;

   ASSERT(pdata);

   ctx = g_hash_table_lookup(pdata->_private, DE_PRIVATE_CTX);
   ASSERT(ctx);

   g_signal_emit_by_name(ctx->serviceObj, TOOLS_CORE_SIG_XSM_SHUTDOWN_CANCELLED,
                         ctx);
}


/*
 * END libSM stuff.
 ******************************************************************************
 */
  070701000002F9000081A4000000000000000000000001682255050000043B000000000000000000000000000000000000005700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/sessionMgrSignals.gm    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

##
# @file sessionMgrSignals.gm
#
# Defines the custom GClosure marshal functions for the desktopEvents::sessionMgr
# signals.
#
# @see desktopEvents.h
#

# XSM SaveYourself signal.
VOID:POINTER,INT,BOOLEAN,INT,BOOLEAN
 070701000002FA000081A40000000000000000000000016822550500003937000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/x11Lock.c   /*********************************************************
 * Copyright (c) 2010-2018,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file x11lock.c
 *
 * Sets up an X11 lock atom and check it to avoid multiple running instances
 * of vmusr.
 */

#include "desktopEventsInt.h"
#include "vmware.h"

#include <stdlib.h>
#include <string.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#include <X11/Xlib.h>

#define LOCK_ATOM_NAME  "vmware-user-lock"


/*
 ******************************************************************************
 * InitGroupLeader --                                                   */ /**
 *
 * This routine sets a few properties related to our main window created
 * by {gdk,gtk}_init.  Specifically this routine sets the window title,
 * sets the override_redirect X11 property, and reparents it to the root
 * window,
 *
 * In addition, this routine will return Xlib handles for the following
 * objects:
 *   - Main or group leader window
 *   - Display's root window
 *
 * As a result of this function:
 *   - groupLeader will have a title of VMUSER_TITLE.
 *   - groupLeader, if not already directly parented by the root, will be.
 *
 *   - dpy will point to our default display (ex: $DISPLAY).
 *   - groupLeader will point to the window created by gtk_init().
 *   - rootWindow will point to the root window on $DISPLAY.
 *
 * @param[out] groupLeader    Group leader window.
 * @param[out] rootWindow     Root window.
 *
 * @return TRUE on success, FALSE on failure.
 *
 ******************************************************************************
 */

static gboolean
InitGroupLeader(Window *groupLeader,
                Window *rootWindow)
{
   Window myGroupLeader;
   Window myRootWindow;
   XSetWindowAttributes attr;
   GdkDisplay *gdkDisplay;
   GdkWindow *gdkLeader;

   attr.override_redirect = True;

   ASSERT(groupLeader);
   ASSERT(rootWindow);

   gdkDisplay = gdk_display_get_default();
   gdkLeader = gdk_display_get_default_group(gdkDisplay);
   myGroupLeader = GDK_WINDOW_XID(gdkLeader);
   myRootWindow = GDK_ROOT_WINDOW();

   ASSERT(myGroupLeader);
   ASSERT(myRootWindow);

   /* XXX: With g_set_prgname() being called, this can probably go away. */
   XStoreName(gdk_x11_get_default_xdisplay(), myGroupLeader, VMUSER_TITLE);

   /*
    * Confidence check:  Set the override redirect property on our group leader
    * window (not default), then re-parent it to the root window (default).
    * This makes sure that (a) a window manager can't re-parent our window,
    * and (b) that we remain a top-level window.
    */
   XChangeWindowAttributes(gdk_x11_get_default_xdisplay(), myGroupLeader, CWOverrideRedirect,
                           &attr);
   XReparentWindow(gdk_x11_get_default_xdisplay(), myGroupLeader, myRootWindow, 10, 10);
   XSync(gdk_x11_get_default_xdisplay(), FALSE);

   *groupLeader = myGroupLeader;
   *rootWindow = myRootWindow;

   return TRUE;
}


/*
 ******************************************************************************
 * QueryX11Lock --                                                      */ /**
 *
 * This is just a wrapper around XGetWindowProperty which queries the
 * window described by <dpy,w> for the property described by lockAtom.
 *
 * @param[in] dpy          X11 display to query
 * @param[in] w            Window to query
 * @param[in] lockAtom     Atom used for locking
 *
 * @return TRUE if property defined by parameters exists; FALSE otherwise.
 *
 ******************************************************************************
 */

static gboolean
QueryX11Lock(Display *dpy,
             Window w,
             Atom lockAtom)
{
   Atom ptype;                  // returned property type
   int pfmt;                    // returned property format
   unsigned long np;            // returned # of properties
   unsigned long remaining;     // amount of data remaining in property
   unsigned char *data = NULL;

   if (XGetWindowProperty(dpy, w, lockAtom, 0, 1, False, lockAtom,
                          &ptype, &pfmt, &np, &remaining, &data) != Success) {
      g_warning("%s: Unable to query window %lx for property %s\n", __func__, w,
                LOCK_ATOM_NAME);
      return FALSE;
   }

   /*
    * Xlib is wacky.  If the following test is true, then our property
    * didn't exist for the window in question.  As a result, `data' is
    * unset, so don't worry about the lack of XFree(data) here.
    */
   if (ptype == None) {
      return FALSE;
   }

   /*
    * We care only about the existence of the property, not its value.
    */
   XFree(data);

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FetchNameErrorHandler --
 *
 *      According to XFetchName document, XFetchName may generate a BadWindow
 *      error. In this case, the pointer we pass to XFetchName doesn't name a
 *      defined window. X is asynchronous, there isn't a gurantee that the
 *      window is still alive between the time the window is obtained and
 *      the time a event is sent to the window. So, for this scenario, since
 *      the window doesn't exist, we can ignore checking its name. This can
 *      avoid this plugin from crashing. Refer to PR 1871141 for details.
 *
 * Results:
 *      Logs error.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
FetchNameErrorHandler(Display *display,
                      XErrorEvent *error)
{
   /* 256 is enough for the error description. */
   char msg[256];
   XGetErrorText(display, error->error_code, msg, sizeof(msg));

   g_warning("X Error %d (%s): request %d.%d\n",
             error->error_code, msg, error->request_code, error->minor_code);

   return 0;
}


/*
 ******************************************************************************
 * AcquireDisplayLock --                                                */ /**
 *
 * This function "locks" the display against being "claimed" by another
 * instance of vmware-user.  It will succeed if we're the first/only
 * instance of vmware-user, and fail otherwise.
 *
 * NB: This routine must be called -after- gtk_init().
 *
 * Vmware-user enjoys per-display exclusivity using the following algorithm:
 *
 *   1.  Grab X server.  (I.e., get exclusive access.)
 *   2.  Search for top-level X windows meeting the following criteria:
 *       a.  named "vmware-user"
 *       b.  has the property "vmware-user-lock" set.
 *   3a. If any such windows described above found, then another vmware-user
 *       process is attached to this display, so we consider the display
 *       locked.
 *   3b. Else we're the only one.  Set the "vmware-user-lock" property on
 *       our top-level window.
 *   4.  Ungrab the X server.
 *
 * The first time this routine is ever called during the lifetime of an X
 * session, a new X11 Atom, "vmware-user-lock" is created for the lifetime
 * of the X server.
 *
 * The "vmware-user-lock" property may be set on this process's group leader
 * window.
 *
 * @return TRUE if "lock" acquired (i.e., we're the first/only vmware-user
 *         process); otherwise FALSE.
 *
 ******************************************************************************
 */

static gboolean
AcquireDisplayLock(void)
{
   Display *defaultDisplay;     // Current default X11 display.
   Window rootWindow;           // Root window of defaultDisplay; used as root node
                                // passed to XQueryTree().
   Window groupLeader;          // Our instance's window group leader.  This is
                                // implicitly created by gtk_init().

   Window *children = NULL;     // Array of windows returned by XQueryTree().
   unsigned int nchildren;      // Length of children.

   Window dummy1, dummy2;       // Throwaway window IDs for XQueryTree().
   Atom lockAtom;               // Refers to the "vmware-user-lock" X11 Atom.

   unsigned int index;
   Bool alreadyLocked = FALSE;  // Set to TRUE if we discover lock is held.
   Bool retval = FALSE;

   defaultDisplay = gdk_x11_get_default_xdisplay();

   /*
    * Reset some of our main window's settings & fetch Xlib handles for
    * the GDK group leader and root windows.
    */
   if (InitGroupLeader(&groupLeader, &rootWindow) == FALSE) {
      g_warning("%s: unable to initialize main window.\n", __func__);
      return FALSE;
   }

   /*
    * Look up the lock atom, creating it if it doesn't already exist.
    */
   lockAtom = XInternAtom(defaultDisplay, LOCK_ATOM_NAME, False);
   if (lockAtom == None) {
      g_warning("%s: unable to create X11 atom: " LOCK_ATOM_NAME "\n", __func__);
      return FALSE;
   }

   /*
    * Okay, so at this point the following is done:
    *
    *   1.  Our top-level / group leader window is a child of the display's
    *       root window.
    *   2.  The window manager can't get its hands on said window.
    *   3.  We have a handle on the X11 atom which will be used to identify
    *       the X11 property used as our lock.
    */

   g_debug("%s: Grabbing X server.\n", __func__);

   /*
    * Neither of these can fail, or at least not in the sense that they'd
    * return an error.  Instead we'd likely see an X11 I/O error, tearing
    * the connection down.
    *
    * XSync simply blocks until the XGrabServer request is acknowledged
    * by the server.  It makes sure that we don't continue issuing requests,
    * such as XQueryTree, until the server grants our "grab".
    */
   XGrabServer(defaultDisplay);
   XSync(defaultDisplay, False);

   /*
    * WARNING:  At this point, we have grabbed the X server.  Consider the
    * UI to be completely frozen.  Under -no- circumstances should we return
    * without ungrabbing the server first.
    */

   if (XQueryTree(defaultDisplay, rootWindow, &dummy1, &dummy2, &children,
                  &nchildren) == 0) {
      g_warning("%s: XQueryTree failed\n", __func__);
      goto out;
   }

   /*
    * Iterate over array of top-level windows.  Search for those named
    * vmware-user and with the property "vmware-user-lock" set.
    *
    * If any such windows are found, then another process has already
    * claimed this X session.
    */
   for (index = 0; (index < nchildren) && !alreadyLocked; index++) {
      char *name = NULL;
      /* Use customized error handler to suppress BadWindow error. */
      int (*oldXErrorHandler)(Display*, XErrorEvent*) =
         XSetErrorHandler(FetchNameErrorHandler);

      /* Skip unless window is named vmware-user. */
      if ((XFetchName(defaultDisplay, children[index], &name) == 0) ||
          (name == NULL) ||
          strcmp(name, VMUSER_TITLE)) {
         XFree(name);

         /* Restore default error handler. */
         XSetErrorHandler(oldXErrorHandler);
         continue;
      }

      /* Restore default error handler. */
      XSetErrorHandler(oldXErrorHandler);

      /*
       * Query the window for the "vmware-user-lock" property.
       */
      alreadyLocked = QueryX11Lock(defaultDisplay, children[index], lockAtom);
      XFree(name);
   }

   /*
    * Yay.  Lock isn't held, so go ahead and acquire it.
    */
   if (!alreadyLocked) {
      unsigned char dummy[] = "1";
      g_debug("%s: Setting property " LOCK_ATOM_NAME "\n", __func__);
      /*
       * NB: Current Xlib always returns one.  This may generate a -fatal- IO
       * error, though.
       */
      XChangeProperty(defaultDisplay, groupLeader, lockAtom, lockAtom, 8,
                      PropModeReplace, dummy, sizeof dummy);
      retval = TRUE;
   }

out:
   XUngrabServer(defaultDisplay);
   XSync(defaultDisplay, False);
   XFree(children);

   return retval;
}


/*
 ******************************************************************************
 * X11Lock_Init --                                                      */ /**
 *
 * Initializes GTK, and sets up a lock atom to make sure only one vmusr
 * instance is running.
 *
 * On error, this function will request that the application's main loop stop
 * running.
 *
 * @param[in] ctx       Application context.
 * @param[in] pdata     Registration data.
 *
 * @return TRUE on success, FALSE if another vmusr instance owns the display or
 *         not running in the right container (vmusr).
 *
 ******************************************************************************
 */

gboolean
X11Lock_Init(ToolsAppCtx *ctx,
             ToolsPluginData *pdata)
{
   int argc = 0;
   char *argv[] = { NULL, NULL };

   if (!TOOLS_IS_USER_SERVICE(ctx)) {
      VMTOOLSAPP_ERROR(ctx, EXIT_FAILURE);
      return FALSE;
   }

   /*
    * We depend on the window title when performing (primitive) vmware-user
    * session detection, and unfortunately for us, GTK has a nasty habit of
    * retitling toplevel windows.  That said, we can control GTK's default
    * title by setting Glib's application or program name.
    *
    * XXX Consider using g_set_application_name("VMware User Agent") or
    * similar.
    */
   g_set_prgname(VMUSER_TITLE);
   argv[0] = VMUSER_TITLE;

#if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 10)
   /*
    * On recent distros, Wayland is the default display server. If the obtained
    * display or window is a wayland one, applying X11 specific functions on them
    * will result in crashes. Before migrating the X11 specific code to Wayland,
    * force using X11 as the backend of Gtk+3. gdk_set_allowed_backends() is
    * introduced since Gtk+3.10 and Wayland is supported from Gtk+3.10.
    */
   gdk_set_allowed_backends("x11");
#endif
   /* XXX: is calling gtk_init() multiple times safe? */
   gtk_init(&argc, (char ***) &argv);

   if (!AcquireDisplayLock()) {
      g_warning("Another instance of vmware-user already running. Exiting.\n");
      VMTOOLSAPP_ERROR(ctx, EXIT_FAILURE);
      return FALSE;
   }

   return TRUE;
}

 070701000002FB000081A40000000000000000000000016822550500001454000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/desktopEvents/xioError.c  /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file xioError.c
 *
 * Handles responding to X11 I/O errors.
 */

/* Include first.  Sets G_LOG_DOMAIN. */
#include "desktopEventsInt.h"

#include <sys/types.h>
#include <X11/Xlib.h>

#include <glib.h>
#include <glib-object.h>
#include <stdlib.h>
#include <unistd.h>

#include "vmware/tools/desktopevents.h"


static int gParentPid;
static ToolsAppCtx *gCtx;
static XIOErrorHandler gOrigHandler;


/*
 ******************************************************************************
 * DEXIOErrorHandler --                                                 */ /**
 *
 * Handler for all X I/O errors. Xlib documentation says we should not
 * return when handling I/O errors.
 *
 * @param[in] dpy    Unused.
 *
 * @return 1 (but doesn't really return).
 *
 ******************************************************************************
 */

static int
DEXIOErrorHandler(Display *dpy)
{
   pid_t my_pid = getpid();

   /*
    * ProcMgr_ExecAsync() needs to fork off a child to handle watching the
    * process being run.  When it dies, it will come through here, so we don't
    * want to let it shut down the RPC channel.
    */
   if (my_pid == gParentPid) {
      g_debug("%s", __func__);

      /*
       * Inform clients capable of/interested in quick'n'dirty cleanup upon an
       * X I/O error.
       */
      g_message("Emitting %s due to X I/O error.\n", TOOLS_CORE_SIG_XIOERROR);
      g_signal_emit_by_name(gCtx->serviceObj, TOOLS_CORE_SIG_XIOERROR, gCtx);

      /*
       * XXX: the really correct thing to do here would be to properly stop all
       * plugins so that capabilities are unset and all other "clean shutdown"
       * tasks are performed. Unfortunately two things currently prevent that:
       *
       * . we can't rely on g_main_loop_quit() because we can't return from this
       *   function (well, we can, but Xlib will exit() before vmtoolsd is able
       *   to clean up things), so the main loop will never regain control off
       *   the app.
       *
       * . we can't access the internal vmtoolsd functions that cleanly shuts
       *   down plugins.
       *
       * So, right now, let's stick with just stopping the RPC channel so that
       * the host is notified the application is gone. This may cause temporary
       * issues with clients that only look at capabilities and not at the
       * status of vmusr.
       */
      if (gCtx->rpc != NULL) {
         RpcChannel_Stop(gCtx->rpc);
      }
      exit(EXIT_FAILURE);
   } else {
      /*
       * _exit is used here so that any atexit() registered routines don't
       * interfere with any resources shared with the parent.
       */
      g_debug("%s hit from forked() child", __func__);
      _exit(EXIT_FAILURE);
   }

   return 1;
}


/*
 ******************************************************************************
 * XIOError_Init --                                                     */ /**
 *
 * Sets up an X11 I/O error callback that stops the daemon.
 *
 * @param[in] ctx       Application context.
 * @param[in] pdata     Registration data.
 *
 * @return TRUE.
 *
 ******************************************************************************
 */

gboolean
XIOError_Init(ToolsAppCtx *ctx,
              ToolsPluginData *pdata)
{
   gCtx = ctx;
   gParentPid = getpid();
   gOrigHandler = XSetIOErrorHandler(DEXIOErrorHandler);

   g_signal_new(TOOLS_CORE_SIG_XIOERROR,
                G_OBJECT_TYPE(ctx->serviceObj),
                0,      // GSignalFlags
                0,      // class offset
                NULL,   // accumulator
                NULL,   // accu_data
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);

   return TRUE;
}


/*
 ******************************************************************************
 * XIOError_Shutdown --                                                 */ /**
 *
 * Shutdown function, restores the original X I/O error handler.
 *
 * @param[in] ctx   Application context.
 * @param[in] pdata Plugin data (unused).
 *
 ******************************************************************************
 */

void
XIOError_Shutdown(ToolsAppCtx *ctx,
                  ToolsPluginData *pdata)
{
   XSetIOErrorHandler(gOrigHandler);
   gCtx = NULL;
   gOrigHandler = NULL;
}

070701000002FC000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp 070701000002FD000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000002FE000081A4000000000000000000000001682255050000128E000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/Makefile.am ################################################################################
### Copyright (C) 2009-2019 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

AUTOMAKE_OPTIONS = subdir-objects

plugindir = @VMUSR_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libdndcp.la

libdndcp_la_CPPFLAGS =
libdndcp_la_CPPFLAGS += @GTK_CPPFLAGS@
libdndcp_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/dnd
libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/dndGuest
libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/dndGuestBase
libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/stringxx
libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/xutils
libdndcp_la_CPPFLAGS += -I$(top_srcdir)/include
libdndcp_la_CPPFLAGS += @XDR_CPPFLAGS@

# Passing C++ related flags to CPPFLAGS generates error.
# So, we need to pass these to C++ compilation only.
libdndcp_la_CXXFLAGS =
libdndcp_la_CXXFLAGS += @SIGCXX_CPPFLAGS@
libdndcp_la_CXXFLAGS += @GTKMM_CPPFLAGS@

libdndcp_la_LDFLAGS =
libdndcp_la_LDFLAGS += @PLUGIN_LDFLAGS@

libdndcp_la_LIBADD =
libdndcp_la_LIBADD += @COMMON_XLIBS@
libdndcp_la_LIBADD += @GTK_LIBS@
libdndcp_la_LIBADD += @GTKMM_LIBS@
libdndcp_la_LIBADD += @VMTOOLS_LIBS@
libdndcp_la_LIBADD += @HGFS_LIBS@
libdndcp_la_LIBADD += @THREAD_LIBS@
libdndcp_la_LIBADD += $(top_builddir)/lib/hgfsUri/hgfsUriPosix.lo
libdndcp_la_LIBADD += @XDR_LIBS@

libdndcp_la_SOURCES =

libdndcp_la_SOURCES += dnd/dndClipboard.c
libdndcp_la_SOURCES += dnd/dndCommon.c
libdndcp_la_SOURCES += dnd/dndCPMsgV4.c
libdndcp_la_SOURCES += dnd/dndLinux.c
libdndcp_la_SOURCES += dnd/dndMsg.c

libdndcp_la_SOURCES += dndGuestBase/copyPasteDnDWrapper.cpp
libdndcp_la_SOURCES += dndGuestBase/dndFileList.cc
libdndcp_la_SOURCES += dndGuestBase/dndRpcV3.cc
libdndcp_la_SOURCES += dndGuestBase/guestDnDCPMgr.cc
libdndcp_la_SOURCES += dndGuestBase/guestDnDDest.cc
libdndcp_la_SOURCES += dndGuestBase/guestDnDSrc.cc
libdndcp_la_SOURCES += dndGuestBase/guestDnDMgr.cc
libdndcp_la_SOURCES += dndGuestBase/guestFileTransfer.cc

libdndcp_la_SOURCES += dndGuest/copyPasteRpcV3.cc
libdndcp_la_SOURCES += dndGuest/guestCopyPasteDest.cc
libdndcp_la_SOURCES += dndGuest/guestCopyPasteMgr.cc
libdndcp_la_SOURCES += dndGuest/guestCopyPasteSrc.cc
libdndcp_la_SOURCES += dndGuest/vmGuestDnDCPMgr.cc
libdndcp_la_SOURCES += dndGuest/vmGuestFileTransfer.cc
libdndcp_la_SOURCES += dndGuest/vmGuestDnDSrc.cc
libdndcp_la_SOURCES += dndGuest/copyPasteRpcV4.cc
libdndcp_la_SOURCES += dndGuest/vmGuestDnDMgr.cc
libdndcp_la_SOURCES += dndGuest/dndRpcV4.cc
libdndcp_la_SOURCES += dndGuest/fileTransferRpcV4.cc
libdndcp_la_SOURCES += dndGuest/rpcV3Util.cpp
libdndcp_la_SOURCES += dndGuest/rpcV4Util.cpp
libdndcp_la_SOURCES += dndGuest/dndCPTransportGuestRpc.cpp

libdndcp_la_SOURCES += stringxx/string.cc
libdndcp_la_SOURCES += xutils/xutils.cc

libdndcp_la_SOURCES += copyPasteCompat.c
libdndcp_la_SOURCES += copyPasteCompatX11.c
libdndcp_la_SOURCES += vmCopyPasteDnDWrapper.cpp
libdndcp_la_SOURCES += copyPasteDnDX11.cpp
libdndcp_la_SOURCES += copyPasteUIX11.cpp
libdndcp_la_SOURCES += dndUIX11.cpp
libdndcp_la_SOURCES += dndcp.cpp
libdndcp_la_SOURCES += dragDetWndX11.cpp
libdndcp_la_SOURCES += pointer.cpp

if LINUX
libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/fakeMouse
libdndcp_la_SOURCES  += fakeMouseWayland/fakeMouseWayland.cpp
libdndcp_la_CPPFLAGS += -DUSE_UINPUT
endif

if HAVE_MKDTEMP
libdndcp_la_CPPFLAGS += -DDND_IS_XDG
libdndcp_la_SOURCES  += dnd/dndXdg.c
endif

# DND_VM is used to distinguish the dnd feature between WS/FS and View
libdndcp_la_CPPFLAGS += -DDND_VM

BUILT_SOURCES =
BUILT_SOURCES += cpFileContents.h
BUILT_SOURCES += cpFileContents_xdr.c

CLEANFILES =
CLEANFILES += $(BUILT_SOURCES)

libdndcp_la_SOURCES += cpFileContents_xdr.c

cpFileContents.h: cpFileContents.x
	@RPCGEN_WRAPPER@ services/plugins/dndcp/cpFileContents.x $@

cpFileContents_xdr.c: cpFileContents.x cpFileContents.h
	@RPCGEN_WRAPPER@ services/plugins/dndcp/cpFileContents.x $@
  070701000002FF000081A40000000000000000000000016822550500000F9D000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/copyPasteCompat.c   /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * copyPasteCompat.c --
 *
 *    Legacy copy/paste functions.
 */

#include "backdoor.h"
#include "backdoor_def.h"
#include "copyPasteCompat.h"

/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste_GetHostSelectionLen --
 *
 *      Retrieve the length of the clipboard (if any) to receive from the
 *      VMX.
 *
 * Results:
 *      Length >= 0 if a clipboard must be retrieved from the host.
 *      < 0 on error (VMWARE_DONT_EXCHANGE_SELECTIONS or
 *                    VMWARE_SELECTION_NOT_READY currently)
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

int32
CopyPaste_GetHostSelectionLen(void)
{
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_GETSELLENGTH;
   Backdoor(&bp);
   return bp.out.ax.word;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteGetNextPiece --
 *
 *      Retrieve the next 4 bytes of the host clipboard.
 *
 * Results:
 *      The next 4 bytes of the host clipboard.
 *
 * Side effects:
 *	     None
 *
 *-----------------------------------------------------------------------------
 */

static uint32
CopyPasteGetNextPiece(void)
{
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_GETNEXTPIECE;
   Backdoor(&bp);
   return bp.out.ax.word;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste_GetHostSelection --
 *
 *      Retrieve the host clipboard. 'data' must be a buffer whose size is at
 *      least (('size' + 4 - 1) / 4) * 4 bytes.
 *
 * Results:
 *      The host clipboard in 'data'.
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPaste_GetHostSelection(unsigned int size, // IN
                           char *data)        // OUT
{
   uint32 *current;
   uint32 const *end;

   current = (uint32 *)data;
   end = current + (size + sizeof *current - 1) / sizeof *current;
   for (; current < end; current++) {
      *current = CopyPasteGetNextPiece();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste_SetSelLength --
 *
 *      Tell the VMX about the length of the clipboard we are about to send
 *      to it.
 *
 * Results:
 *      None
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPaste_SetSelLength(uint32 length) // IN
{
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_SETSELLENGTH;
   bp.in.size = length;
   Backdoor(&bp);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste_SetNextPiece --
 *
 *      Send the next 4 bytes of the guest clipboard.
 *
 * Results:
 *      None
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPaste_SetNextPiece(uint32 data) // IN
{
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_SETNEXTPIECE;
   bp.in.size = data;
   Backdoor(&bp);
}

   07070100000300000081A40000000000000000000000016822550500000624000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/copyPasteCompat.h   /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * copyPasteCompat.h --
 *
 *    copyPaste header file.
 *
 */

#if !defined _COPYPASTE_COMPAT_H
#define _COPYPASTE_COMPAT_H

#include "vmware.h"

/* Functions to be compatible with old text copy/paste directly based on backdoor cmd. */
Bool CopyPaste_RequestSelection(void);
Bool CopyPaste_GetBackdoorSelections(void);
Bool CopyPaste_IsRpcCPSupported(void);
void CopyPaste_Init(void);
void CopyPaste_SetVersion(int version);

int32
CopyPaste_GetHostSelectionLen(void);

void
CopyPaste_GetHostSelection(unsigned int size, // IN
                           char *data);       // OUT

void
CopyPaste_SetSelLength(uint32 length); // IN

void
CopyPaste_SetNextPiece(uint32 data); // IN

#endif
07070100000301000081A40000000000000000000000016822550500007A43000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/copyPasteCompatX11.c    /*********************************************************
 * Copyright (c) 2005-2019,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * copyPasteCompatX11.c --
 *
 *    Set of functions in guest side for copy/paste (both file and text).
 *    Currently there are 2 versions copy/paste. Version 1 only supports
 *    text copy/paste, and based on backdoor cmd. Version 2 supports both
 *    text and file copy/paste, and based on guestRPC.
 *
 *    G->H Text Copy/Paste (version 1)
 *    --------------------
 *    When Ungrab, CopyPaste_RequestSelection got called, which try to get
 *    selection text and send to backdoor.
 *
 *    H->G Text Copy/Paste (version 1)
 *    --------------------
 *    When grab, CopyPaste_GetBackdoorSelections got called, which first
 *    get host selection text, then claim as selection owner. If some app
 *    asks for selection, CopyPasteSelectionGetCB will reply with host
 *    selection text.
 */

#define G_LOG_DOMAIN "dndcp"

#include "dndPluginIntX11.h"
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>

#include "vm_assert.h"
#include "copyPasteCompat.h"
#include "str.h"
#include "strutil.h"
#include "dnd.h"
#include "util.h"
#include "cpName.h"
#include "cpNameUtil.h"
#include "vmblock.h"
#include "file.h"
#include "codeset.h"
#include "escape.h"
#include "hostinfo.h"
#include "wiper.h"
#include "vmware/guestrpc/tclodefs.h"

#include "vmware/tools/plugin.h"

/*
 * Gtk 1.2 doesn't know about the CLIPBOARD selection, but that doesn't matter, we
 * just create the atom we need directly in main().
 */
#ifndef GDK_SELECTION_CLIPBOARD
GdkAtom GDK_SELECTION_CLIPBOARD;
#endif

#ifndef GDK_SELECTION_TYPE_TIMESTAMP
GdkAtom GDK_SELECTION_TYPE_TIMESTAMP;
#endif

#ifndef GDK_SELECTION_TYPE_UTF8_STRING
GdkAtom GDK_SELECTION_TYPE_UTF8_STRING;
#endif

/*
 * Currently there are 2 versions copy/paste.
 * Key points in copy/paste version 1:
 * 1. Only text copy/paste
 * 2. copy/paste is based on backdoor directly
 *
 * Key points in copy/paste version 2:
 * 1. Support both file/text copy/paste
 * 2. Both file/text copy/paste are based on guestRPC
 */
static int32 gVmxCopyPasteVersion = 1;

/*
 * Getting a selection is an asyncronous event, so we have to keep track of both
 * selections globablly in order to decide which one to use.
 */
static Bool gWaitingOnGuestSelection = FALSE;
static char gGuestSelPrimaryBuf[MAX_SELECTION_BUFFER_LENGTH];
static char gGuestSelClipboardBuf[MAX_SELECTION_BUFFER_LENGTH];
static uint64 gGuestSelPrimaryTime = 0;
static uint64 gGuestSelClipboardTime = 0;
static char gHostClipboardBuf[MAX_SELECTION_BUFFER_LENGTH + 1];

static Bool gIsOwner;
static ToolsAppCtx *gCtx = NULL;

/*
 * Forward Declarations
 */
static gboolean IsCtxMainLoopActive(void);
static INLINE void CopyPasteStateInit(void);
static void CopyPasteSelectionReceivedCB(GtkWidget *widget,
                                         GtkSelectionData *selection_data,
                                         gpointer data);
static void CopyPasteSelectionGetCB(GtkWidget *widget,
                                    GtkSelectionData *selection_data,
                                    guint info,
                                    guint time_stamp,
                                    gpointer data);
static gint CopyPasteSelectionClearCB(GtkWidget *widget,
                                      GdkEventSelection *event,
                                      gpointer data);

static void CopyPasteSetBackdoorSelections(void);

/* This struct is only used by CopyPasteSelectionRemoveTarget. */
struct SelectionTargetList {
  GdkAtom selection;
  GtkTargetList *list;
};


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteSelectionRemoveTarget --
 *
 *      To remove a target from a selection target list. The reason to develop
 *      this function is that in gtk there is only gtk_selection_add_target to
 *      add supported target to selection list, but no function to remove one.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      If no more target, the selection list will be removed too.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteSelectionRemoveTarget(GtkWidget *widget,
                               GdkAtom selection,
                               GdkAtom target)
{
   const char *selection_handler_key = "gtk-selection-handlers";
   GList *tempList;
   GList *selectionLists;

   /* Get selection list. */
#ifndef GTK3
   selectionLists = gtk_object_get_data(GTK_OBJECT (widget), selection_handler_key);
#else
   selectionLists = g_object_get_data(G_OBJECT (widget), selection_handler_key);
#endif
   tempList = selectionLists;
   while (tempList) {
      /* Enumerate the list to find the selection. */
      struct SelectionTargetList *targetList = tempList->data;
      if (targetList->selection == selection) {
         /* Remove target. */
         gtk_target_list_remove(targetList->list, target);
         /* If no more target, remove selection from list. */
#ifndef GTK3
         /* the following code does not build with gtk3 - it may not be
            necessary and gtk_target_list_remove() takes care of it,
            or we create a memory leak. */
         if (!targetList->list->list) {
            /* Free target list. */
            gtk_target_list_unref(targetList->list);
            g_free(targetList);
            /* Remove and free selection node. */
            selectionLists = g_list_remove_link(selectionLists, tempList);
            g_list_free_1(tempList);
         }
#endif
         break;
      }
      tempList = tempList->next;
   }
   /* Put new selection list back. */
#ifndef GTK3
   gtk_object_set_data (GTK_OBJECT (widget), selection_handler_key, selectionLists);
#else
   g_object_set_data (G_OBJECT (widget), selection_handler_key, selectionLists);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste_RequestSelection --
 *
 *      Request the guest's text clipboard (asynchronously), we'll give it to 
 *      the host when the request completes. For version 1 guest->host text
 *      copy/paste.
 *
 * Results:
 *      TRUE on success, FALSE otherwise.
 *
 * Side effects:
 *      The owner of the clipboard will get a request from our application.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CopyPaste_RequestSelection(void)
{
   if (gVmxCopyPasteVersion > 1) {
      return FALSE;
   }

   /*
    * Ask for both the PRIMARY and CLIPBOARD selections.
    */
   gGuestSelPrimaryBuf[0] = '\0';
   gGuestSelClipboardBuf[0] = '\0';

   /* Only send out request if we are not the owner. */
   if (!gIsOwner) {
      /* Try to get timestamp for primary and clipboard. */
      gWaitingOnGuestSelection = TRUE;
      gtk_selection_convert(gUserMainWidget,
                            GDK_SELECTION_PRIMARY,
                            GDK_SELECTION_TYPE_TIMESTAMP,
                            GDK_CURRENT_TIME);
      while (IsCtxMainLoopActive() && gWaitingOnGuestSelection) {
         gtk_main_iteration();
      }

      gWaitingOnGuestSelection = TRUE;
      gtk_selection_convert(gUserMainWidget,
                            GDK_SELECTION_CLIPBOARD,
                            GDK_SELECTION_TYPE_TIMESTAMP,
                            GDK_CURRENT_TIME);
      while (IsCtxMainLoopActive() && gWaitingOnGuestSelection) {
         gtk_main_iteration();
      }

      /* Try to get utf8 text from primary and clipboard. */
      gWaitingOnGuestSelection = TRUE;
      gtk_selection_convert(gUserMainWidget,
                            GDK_SELECTION_PRIMARY,
                            GDK_SELECTION_TYPE_UTF8_STRING,
                            GDK_CURRENT_TIME);
      while (IsCtxMainLoopActive() && gWaitingOnGuestSelection) {
         gtk_main_iteration();
      }

      gWaitingOnGuestSelection = TRUE;
      gtk_selection_convert(gUserMainWidget,
                            GDK_SELECTION_CLIPBOARD,
                            GDK_SELECTION_TYPE_UTF8_STRING,
                            GDK_CURRENT_TIME);
      while (IsCtxMainLoopActive() && gWaitingOnGuestSelection) {
         gtk_main_iteration();
      }

      if (gGuestSelPrimaryBuf[0] == '\0' && gGuestSelClipboardBuf[0] == '\0') {
         /*
          * If we cannot get utf8 text, try to get localized text from primary
          * and clipboard.
          */
         gWaitingOnGuestSelection = TRUE;
         gtk_selection_convert(gUserMainWidget,
                               GDK_SELECTION_PRIMARY,
                               GDK_SELECTION_TYPE_STRING,
                               GDK_CURRENT_TIME);
         while (IsCtxMainLoopActive() && gWaitingOnGuestSelection) {
            gtk_main_iteration();
         }

         gWaitingOnGuestSelection = TRUE;
         gtk_selection_convert(gUserMainWidget,
                               GDK_SELECTION_CLIPBOARD,
                               GDK_SELECTION_TYPE_STRING,
                               GDK_CURRENT_TIME);
         while (IsCtxMainLoopActive() && gWaitingOnGuestSelection) {
            gtk_main_iteration();
         }
      }
   }
   /* Send text to host. */
   g_debug("CopyPaste_RequestSelection: Prim is [%s], Clip is [%s]\n",
         gGuestSelPrimaryBuf, gGuestSelClipboardBuf);
   CopyPasteSetBackdoorSelections();
   return TRUE;
}


/**
 * Check to see if we are running in tools main loop.
 *
 * @return TRUE if we are, FALSE if we are not running in main loop.
 */

static gboolean
IsCtxMainLoopActive(void)
{
   ASSERT(gCtx);
   return g_main_loop_is_running(gCtx->mainLoop);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteSelectionReceivedCB --
 *
 *      Callback for the gtk signal "selection_received".
 *      Called because we previously requested a copy/paste selection and
 *      finally got results of that asynchronous operation. After some basic
 *      confidence checks, send the result (in selection_data) thru the backdoor
 *      (version 1) or guestRPC (version 2) so the vmx can copy it to host
 *      clipboard.
 *
 *      We made several requests for selections, the string (actual data) and
 *      file list for each of PRIMARY and CLIPBOARD selections. So this funtion
 *      will get called several times, once for each request.
 *
 *      For guest->host copy/paste (both text and file).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteSelectionReceivedCB(GtkWidget *widget,                // IN: unused
                             GtkSelectionData *selection_data, // IN: requested data
                             gpointer data)                    // IN: unused
{
   char *target;
#ifndef GTK3
   char *utf8Str = NULL;
#else
   const char *utf8Str = NULL;
   char *utf8Str_cvt = NULL;
#endif
   size_t len;
   size_t aligned_len;

   if ((widget == NULL) || (selection_data == NULL)) {
      g_debug("CopyPasteSelectionReceivedCB: Error, widget or selection_data is invalid\n");
      goto exit;
   }

#ifndef GTK3
   if (selection_data->length < 0) {
#else
   if (gtk_selection_data_get_length(selection_data) < 0) {
#endif
      g_debug("CopyPasteSelectionReceivedCB: Error, length less than 0\n");
      goto exit;
   }

   /* Try to get clipboard or selection timestamp. */
#ifndef GTK3
   if (selection_data->target == GDK_SELECTION_TYPE_TIMESTAMP) {
      if (selection_data->selection == GDK_SELECTION_PRIMARY) {
         if (selection_data->length == 4) {
            gGuestSelPrimaryTime = *(uint32 *)selection_data->data;
#else
   if (gtk_selection_data_get_target(selection_data) == GDK_SELECTION_TYPE_TIMESTAMP) {
      if (gtk_selection_data_get_selection(selection_data) == GDK_SELECTION_PRIMARY) {
         if (gtk_selection_data_get_length(selection_data) == 4) {
            gGuestSelPrimaryTime = *(uint32 *)gtk_selection_data_get_data(selection_data);
#endif
            g_debug("CopyPasteSelectionReceivedCB: Got pri time [%"FMT64"u]\n",
                  gGuestSelPrimaryTime);
#ifndef GTK3
         } else if (selection_data->length == 8) {
            gGuestSelPrimaryTime = *(uint64 *)selection_data->data;
#else
         } else if (gtk_selection_data_get_length(selection_data) == 8) {
            gGuestSelPrimaryTime = *(uint64 *)gtk_selection_data_get_data(selection_data);
#endif
            g_debug("CopyPasteSelectionReceivedCB: Got pri time [%"FMT64"u]\n",
                  gGuestSelPrimaryTime);
         } else {
            g_debug("CopyPasteSelectionReceivedCB: Unknown pri time. Size %d\n",
#ifndef GTK3
                  selection_data->length);
#else
                  gtk_selection_data_get_length(selection_data));
#endif
         }
      }
#ifndef GTK3
      if (selection_data->selection == GDK_SELECTION_CLIPBOARD) {
         if (selection_data->length == 4) {
            gGuestSelClipboardTime = *(uint32 *)selection_data->data;
#else
      if (gtk_selection_data_get_selection(selection_data) == GDK_SELECTION_CLIPBOARD) {
         if (gtk_selection_data_get_length(selection_data) == 4) {
            gGuestSelClipboardTime = *(uint32 *)gtk_selection_data_get_data(selection_data);
#endif
            g_debug("CopyPasteSelectionReceivedCB: Got clip time [%"FMT64"u]\n",
                  gGuestSelClipboardTime);
#ifndef GTK3
         } else if (selection_data->length == 8) {
            gGuestSelClipboardTime = *(uint64 *)selection_data->data;
#else
         } else if (gtk_selection_data_get_length(selection_data) == 8) {
            gGuestSelClipboardTime = *(uint64 *)gtk_selection_data_get_data(selection_data);
#endif
            g_debug("CopyPasteSelectionReceivedCB: Got clip time [%"FMT64"u]\n",
                  gGuestSelClipboardTime);
         } else {
            g_debug("CopyPasteSelectionReceivedCB: Unknown clip time. Size %d\n",
#ifndef GTK3
                  selection_data->length);
#else
                  gtk_selection_data_get_length(selection_data));
#endif
         }
      }
      goto exit;
   }

#ifndef GTK3
   if (selection_data->selection == GDK_SELECTION_PRIMARY) {
#else
   if (gtk_selection_data_get_selection(selection_data) == GDK_SELECTION_PRIMARY) {
#endif
      target = gGuestSelPrimaryBuf;
#ifndef GTK3
   } else if (selection_data->selection == GDK_SELECTION_CLIPBOARD) {
#else
   } else if (gtk_selection_data_get_selection(selection_data) == GDK_SELECTION_CLIPBOARD) {
#endif
      target = gGuestSelClipboardBuf;
   } else {
      goto exit;
   }

#ifndef GTK3
   utf8Str = selection_data->data;
   len = strlen(selection_data->data);

   if (selection_data->target != GDK_SELECTION_TYPE_STRING &&
       selection_data->target != GDK_SELECTION_TYPE_UTF8_STRING) {
      /* It is a file list. */
      if (len >= MAX_SELECTION_BUFFER_LENGTH - 1) {
         Warning("CopyPasteSelectionReceivedCB file list too long\n");
      } else {
         memcpy(target, selection_data->data, len + 1);
      }
      goto exit;
   }
#else
   utf8Str = gtk_selection_data_get_data(selection_data);
   len = strlen(gtk_selection_data_get_data(selection_data));

   if (gtk_selection_data_get_target(selection_data) != GDK_SELECTION_TYPE_STRING &&
       gtk_selection_data_get_target(selection_data) != GDK_SELECTION_TYPE_UTF8_STRING) {
      /* It is a file list. */
      if (len >= MAX_SELECTION_BUFFER_LENGTH - 1) {
         Warning("CopyPasteSelectionReceivedCB file list too long\n");
      } else {
         memcpy(target, gtk_selection_data_get_data(selection_data), len + 1);
      }
      goto exit;
   }
#endif

   /*
    * If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code
    * set. Convert to utf8 before send to vmx.
    */
#ifndef GTK3
   if (selection_data->target == GDK_SELECTION_TYPE_STRING &&
       !CodeSet_CurrentToUtf8(selection_data->data,
                              selection_data->length,
                              &utf8Str,
                              &len)) {
      g_debug("CopyPasteSelectionReceivedCB: Couldn't convert to utf8 code set\n");
      gWaitingOnGuestSelection = FALSE;
      return;
   }
#else
   if (gtk_selection_data_get_target(selection_data) == GDK_SELECTION_TYPE_STRING) {
      if (!CodeSet_CurrentToUtf8(gtk_selection_data_get_data(selection_data),
                              gtk_selection_data_get_length(selection_data),
                              &utf8Str_cvt,
                              &len)) {
         g_debug("CopyPasteSelectionReceivedCB: Couldn't convert to utf8 code set\n");
         gWaitingOnGuestSelection = FALSE;
         return;
      } else {
         utf8Str = utf8Str_cvt;
      }
   }
#endif

   /*
    * String in backdoor communication is 4 bytes by 4 bytes, so the len
    * should be aligned to 4;
    */
   aligned_len = (len + 4) & ~3;
   if (aligned_len >= MAX_SELECTION_BUFFER_LENGTH) {
      /* With alignment, len is still possible to be less than max. */
      if (len < (MAX_SELECTION_BUFFER_LENGTH - 1)) {
         memcpy(target, utf8Str, len + 1);
      } else {
         memcpy(target, utf8Str, MAX_SELECTION_BUFFER_LENGTH - 1);
         target[MAX_SELECTION_BUFFER_LENGTH - 1] ='\0';
      }
   } else {
      memcpy(target, utf8Str, len + 1);
   }

exit:
#ifndef GTK3
   if (selection_data->target == GDK_SELECTION_TYPE_STRING) {
      free(utf8Str);
#else
   if (gtk_selection_data_get_target(selection_data) == GDK_SELECTION_TYPE_STRING) {
      free(utf8Str_cvt);
#endif
   }
   gWaitingOnGuestSelection = FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteSelectionGetCB --
 *
 *      Callback for the gtk signal "selection_get".
 *      This is called when some other app requests the copy/paste selection,
 *      probably because we declare oursleves the selection owner on mouse
 *      grab. In text copy/paste case, we simply respond with contents of
 *      gHostClipboardBuf, which should have been set on mouse grab. In file
 *      copy/paste case, send file transfer request to host vmx, then return
 *      file list with right format according to different request.
 *      For host->guest copy/paste (both text and file).
 *
 * Results:
 *      None
 *
 * Side effects:
 *      An X message is sent to the requesting app containing the data, it
 *      will likely act on it in some way. In FCP case, may first start a
 *      host->guest file transfer. Add block if blocking driver is available,
 *      otherwise wait till file copy done.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteSelectionGetCB(GtkWidget        *widget,         // IN: unused
                        GtkSelectionData *selection_data, // IN: requested type
                                                          // OUT:the data to be sent
                        guint            info,            // IN: unused
                        guint            time_stamp,      // IN: unsued
                        gpointer         data)            // IN: unused
{
   if ((widget == NULL) || (selection_data == NULL)) {
      g_debug("CopyPasteSelectionGetCB: Error, widget or selection_data is invalid\n");
      return;
   }

   GdkAtom target;
#ifndef GTK3
   target = selection_data->target;
#else
   target = gtk_selection_data_get_target(selection_data);
#endif

   /* If it is text copy paste, return gHostClipboardBuf. */
   if (GDK_SELECTION_TYPE_STRING == target ||
       GDK_SELECTION_TYPE_UTF8_STRING == target) {
      char *outBuf = gHostClipboardBuf;
      char *outStringBuf = NULL;
      size_t len = strlen(gHostClipboardBuf);

      /*
       * If target is GDK_SELECTION_TYPE_STRING, assume encoding is local code
       * set. Convert from utf8 to local one.
       */
      if (GDK_SELECTION_TYPE_STRING == target &&
          !CodeSet_Utf8ToCurrent(gHostClipboardBuf,
                                 strlen(gHostClipboardBuf),
                                 &outStringBuf,
                                 &len)) {
         g_debug("CopyPasteSelectionGetCB: can not convert to current codeset\n");
         return;
      }

      if (outStringBuf != NULL) {
         outBuf = outStringBuf;
      }

      gtk_selection_data_set(selection_data, target, 8, outBuf, len);
      g_debug("CopyPasteSelectionGetCB: Set text [%s]\n", outBuf);

      free(outStringBuf);
      return;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteSelectionClearCB --
 *
 *      Callback for the gtk signal "selection_clear".
 *
 * Results:
 *      Always TRUE.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static gint
CopyPasteSelectionClearCB(GtkWidget          *widget,         // IN: unused
                          GdkEventSelection  *event,          // IN: unused
                          gpointer           data)            // IN: unused
{
   g_debug("CopyPasteSelectionClearCB got clear signal\n");
   gIsOwner = FALSE;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteSetBackdoorSelections --
 *
 *      Set the clipboard one of two ways, the old way or the new way.
 *      The old way uses CopyPaste_SetSelLength and there's only one selection.
 *      Set backdoor selection with either primary selection or clipboard.
 *      The primary selection is the first priority, then clipboard.
 *      If both unavailable, set backdoor selection length to be 0.
 *      This will be used by older VMXs or VMXs on Windows hosts (which
 *      has only one clipboard). Doing this gives us backwards
 *      compatibility.
 *
 *      The new way uses new sets both PRIMARY and CLIPBOARD. Newer Linux
 *      VMXs will use these rather than the above method and have the two
 *      selections set separately.
 *
 *      XXX: The "new way" doesn't exist yet, the vmx has no support for it.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The VMX probably changes some string buffers.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteSetBackdoorSelections(void)
{
   uint32 const *p;
   size_t primaryLen;
   size_t clipboardLen;

   primaryLen = strlen(gGuestSelPrimaryBuf);
   clipboardLen = strlen(gGuestSelClipboardBuf);

   if (primaryLen && clipboardLen) {
      /* Pick latest one if both are available. */
      p = gGuestSelPrimaryTime >= gGuestSelClipboardTime ?
         (uint32 const *)gGuestSelPrimaryBuf :
         (uint32 const *)gGuestSelClipboardBuf;
   } else if (primaryLen) {
      /*
       * Send primary selection to backdoor if it exists.
       */
      p = (uint32 const *)gGuestSelPrimaryBuf;
   } else if (clipboardLen) {
      /*
       * Otherwise send clipboard to backdoor if it exists.
       */
      p = (uint32 const *)gGuestSelClipboardBuf;
   } else {
      /*
       * Neither selection is set
       */
      p = NULL;
   }

   if (p == NULL) {
      CopyPaste_SetSelLength(0);
      g_debug("CopyPasteSetBackdoorSelections Set empty text.\n");
   } else {
      size_t len = strlen((char *)p);
      size_t aligned_len;
      unsigned int i;

      g_debug("CopyPasteSetBackdoorSelections Set text [%s].\n", (char *)p);
      aligned_len = (len + 4) & ~3;

      /* Here long string should already be truncated. */
      ASSERT(aligned_len <= MAX_SELECTION_BUFFER_LENGTH);

      CopyPaste_SetSelLength(len);
      for (i = 0; i < len; i += 4, p++) {
         CopyPaste_SetNextPiece(*p);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste_GetBackdoorSelections --
 *
 *      Get the clipboard "the old way".
 *      The old way uses CopyPaste_GetHostSelectionLen and there's only one
 *      selection. We don't have to do anything for the "new way", since the
 *      host will just push PRIMARY and/or CLIPBOARD when they are available
 *      on the host.
 *
 *      XXX: the "new way" isn't availble yet because the vmx doesn't
 *           implement separate clipboards. Even when it does this
 *           function will still exist for backward compatibility
 *
 * Results:
 *      TRUE if selection length>=0, FALSE otherwise.
 *
 * Side effects:
 *      This application becomes the selection owner for PRIMARY and/or
        CLIPBOARD selections.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CopyPaste_GetBackdoorSelections(void)
{
   int selLength;

   if (gVmxCopyPasteVersion > 1) {
      return TRUE;
   }

   selLength = CopyPaste_GetHostSelectionLen();
   if (selLength < 0 || selLength > MAX_SELECTION_BUFFER_LENGTH) {
      return FALSE;
   } else if (selLength > 0) {
      CopyPaste_GetHostSelection(selLength, gHostClipboardBuf);
      gHostClipboardBuf[selLength] = 0;
      g_debug("CopyPaste_GetBackdoorSelections Get text [%s].\n", gHostClipboardBuf);
      gtk_selection_owner_set(gUserMainWidget,
                              GDK_SELECTION_CLIPBOARD,
                              GDK_CURRENT_TIME);
      gtk_selection_owner_set(gUserMainWidget,
                              GDK_SELECTION_PRIMARY,
                              GDK_CURRENT_TIME);
      gIsOwner = TRUE;
   }
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste_Register --
 *
 *      Setup callbacks, initialize.
 *
 * Results:
 *      Always TRUE.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

Bool
CopyPaste_Register(GtkWidget* mainWnd,   // IN
                   ToolsAppCtx *ctx)     // IN
{
   g_debug("%s: enter\n", __FUNCTION__);
   ASSERT(mainWnd);
   ASSERT(ctx);

   gCtx = ctx;
   /* Text copy/paste initialization for all versions. */
#ifndef GDK_SELECTION_CLIPBOARD
   GDK_SELECTION_CLIPBOARD = gdk_atom_intern("CLIPBOARD", FALSE);
#endif

#ifndef GDK_SELECTION_TYPE_TIMESTAMP
   GDK_SELECTION_TYPE_TIMESTAMP = gdk_atom_intern("TIMESTAMP", FALSE);
#endif

#ifndef GDK_SELECTION_TYPE_UTF8_STRING
   GDK_SELECTION_TYPE_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE);
#endif

   /*
    * String is always in supported list. FCP atoms will dynamically be
    * added and removed.
    */
   gtk_selection_add_target(mainWnd, GDK_SELECTION_PRIMARY,
                            GDK_SELECTION_TYPE_STRING, 0);
   gtk_selection_add_target(mainWnd, GDK_SELECTION_CLIPBOARD,
                            GDK_SELECTION_TYPE_STRING, 0);
   gtk_selection_add_target(mainWnd, GDK_SELECTION_PRIMARY,
                            GDK_SELECTION_TYPE_UTF8_STRING, 0);
   gtk_selection_add_target(mainWnd, GDK_SELECTION_CLIPBOARD,
                            GDK_SELECTION_TYPE_UTF8_STRING, 0);

#ifndef GTK3
   gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_received",
                      GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB), mainWnd);
   gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_get",
                      GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB), mainWnd);
   gtk_signal_connect(GTK_OBJECT(mainWnd), "selection_clear_event",
                      GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB), mainWnd);
#else
   g_signal_connect(G_OBJECT(mainWnd), "selection_received",
                    G_CALLBACK(CopyPasteSelectionReceivedCB), mainWnd);
   g_signal_connect(G_OBJECT(mainWnd), "selection_get",
                    G_CALLBACK(CopyPasteSelectionGetCB), mainWnd);
   g_signal_connect(G_OBJECT(mainWnd), "selection_clear_event",
                    G_CALLBACK(CopyPasteSelectionClearCB), mainWnd);
#endif

   CopyPasteStateInit();

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste_Unregister --
 *
 *      Cleanup copy/paste related things.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      copy/paste is stopped, the rpc channel to the vmx is closed.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPaste_Unregister(GtkWidget* mainWnd)
{
   g_debug("%s: enter\n", __FUNCTION__);
#ifndef GTK3
   gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
                                 GTK_SIGNAL_FUNC(CopyPasteSelectionReceivedCB),
                                 mainWnd);
   gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
                                 GTK_SIGNAL_FUNC(CopyPasteSelectionGetCB),
                                 mainWnd);
   gtk_signal_disconnect_by_func(GTK_OBJECT(mainWnd),
                                 GTK_SIGNAL_FUNC(CopyPasteSelectionClearCB),
                                 mainWnd);
#else
   g_signal_handlers_disconnect_by_func(G_OBJECT(mainWnd),
                               G_CALLBACK(CopyPasteSelectionReceivedCB),
                               mainWnd);
   g_signal_handlers_disconnect_by_func(G_OBJECT(mainWnd),
                               G_CALLBACK(CopyPasteSelectionGetCB),
                               mainWnd);
   g_signal_handlers_disconnect_by_func(G_OBJECT(mainWnd),
                               G_CALLBACK(CopyPasteSelectionClearCB),
                               mainWnd);
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * CopyPaste_IsRpcCPSupported --
 *
 *    Check if RPC copy/paste is supported by vmx or not.
 *
 * Results:
 *    FALSE always.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
CopyPaste_IsRpcCPSupported(void)
{
   return gVmxCopyPasteVersion > 1;
}


/*
 *----------------------------------------------------------------------------
 *
 * CopyPasteStateInit --
 *
 *    Initalialize CopyPaste State.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

void
CopyPasteStateInit(void)
{
   g_debug("%s: enter\n", __FUNCTION__);
   gHostClipboardBuf[0] = '\0';
   gGuestSelPrimaryBuf[0] = '\0';
   gGuestSelClipboardBuf[0] = '\0';
   gIsOwner = FALSE;
}


/**
 * Set the copy paste version.
 *
 * @param[in] version version to set.
 */

void
CopyPaste_SetVersion(int version)
{
   g_debug("%s: enter version %d\n", __FUNCTION__, version);
   gVmxCopyPasteVersion = version;
}
 07070100000302000081A40000000000000000000000016822550500002BFC000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/copyPasteDnDX11.cpp /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file copyPasteDnDX11.cpp
 *
 * Implementation class for DnD and copy paste on X11 platform.
 */

#define G_LOG_DOMAIN "dndcp"

#include "copyPasteDnDWrapper.h"
#include "copyPasteDnDX11.h"
#include "copyPasteUIX11.h"
#include "dndPluginIntX11.h"
#include "tracer.hh"

Window gXRoot;
Display *gXDisplay;
GtkWidget *gUserMainWidget;


extern "C" {
#include "copyPasteCompat.h"
#include "vmware/tools/plugin.h"

void CopyPaste_Register(GtkWidget *mainWnd, ToolsAppCtx *ctx);
void CopyPaste_Unregister(GtkWidget *mainWnd);
}

#include "pointer.h"


/**
 *
 * BlockingService - a singleton class responsible for initializing and
 * cleaning up blocking state (vmblock).
 */

class BlockService {
public:
   static BlockService *GetInstance();
   void Init(ToolsAppCtx *);
   DnDBlockControl *GetBlockCtrl() { return &m_blockCtrl; }

private:
   BlockService();
   ~BlockService();

   void Shutdown();
   static gboolean ShutdownSignalHandler(const siginfo_t *, gpointer);

   GSource *m_shutdownSrc;
   DnDBlockControl m_blockCtrl;
   bool m_initialized;

   static BlockService *m_instance;
};

BlockService *BlockService::m_instance = 0;


/**
 *
 * Constructor.
 */

BlockService::BlockService() :
   m_shutdownSrc(0),
   m_initialized(false)
{
   memset(&m_blockCtrl, 0, sizeof m_blockCtrl);
   m_blockCtrl.fd = -1;
}


/**
 *
 * Get an instance of BlockService, which is an application singleton.
 *
 * @return a pointer to the singleton BlockService object, or NULL if
 * for some reason it could not be allocated.
 */

BlockService *
BlockService::GetInstance()
{
   TRACE_CALL();

   if (!m_instance) {
      m_instance = new BlockService();
   }

   ASSERT(m_instance);
   return m_instance;
}


/**
 *
 * Initialize blocking subsystem so that GTK+ DnD operations won't
 * time out. Also install SIGUSR1 handler so we can disconnect from
 * blcoing subsystem upon request.
 *
 * @param[in] ctx tools app context.
 */

void
BlockService::Init(ToolsAppCtx *ctx)
{
   TRACE_CALL();

   if (!m_initialized && ctx) {
      m_blockCtrl.fd = ctx->blockFD;
      m_blockCtrl.fd >= 0 ?
         DnD_CompleteBlockInitialization(m_blockCtrl.fd, &m_blockCtrl) :
         DnD_InitializeBlocking(&m_blockCtrl);

      m_shutdownSrc = VMTools_NewSignalSource(SIGUSR1);
      VMTOOLSAPP_ATTACH_SOURCE(ctx, m_shutdownSrc, ShutdownSignalHandler,
                               ctx, NULL);

      m_initialized = true;
   }
}


/**
 *
 * Signal handler called when we receive SIGUSR1 which is a hint for us
 * to disconnect from blocking subsystem so that it can be upgraded.
 *
 * @param[in] siginfo unused.
 * @param[in] data    unused.
 *
 * @return always TRUE.
 */

gboolean
BlockService::ShutdownSignalHandler(const siginfo_t *siginfo,
                                    gpointer data)
{
   TRACE_CALL();

   g_debug("Shutting down block service on SIGUSR1 ...\n");
   GetInstance()->Shutdown();

   return FALSE;
}


/**
 *
 * Shut down blocking susbsystem so that we can perform upgrade.
 */

void
BlockService::Shutdown()
{
   TRACE_CALL();

   if (m_initialized) {
      g_source_destroy(m_shutdownSrc);
      g_source_unref(m_shutdownSrc);
      m_shutdownSrc = 0;

      if (DnD_BlockIsReady(&m_blockCtrl)) {
         DnD_UninitializeBlocking(&m_blockCtrl);
      }

      m_initialized = false;
   }
}


/**
 *
 * Constructor.
 */

CopyPasteDnDX11::CopyPasteDnDX11() :
   m_main(NULL),
   m_copyPasteUI(NULL),
   m_dndUI(NULL)
{
}


/**
 *
 * Initialize Win32 platform DnD/CP. Initialize Gtk+, and create detection
 * windows.
 *
 * @param[in] ctx tools app context.
 */

gboolean
CopyPasteDnDX11::Init(ToolsAppCtx *ctx)
{
   TRACE_CALL();

#if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 10)
   /*
    * On recent distros, Wayland is the default display server. If the obtained
    * display or window is a wayland one, applying X11 specific functions on them
    * will result in crashes. Before migrating the X11 specific code to Wayland,
    * force using X11 as the backend of Gtk+3. gdk_set_allowed_backends() is
    * introduced since Gtk+3.10 and Wayland is supported from Gtk+3.10.
    */
   gdk_set_allowed_backends("x11");
#endif

   CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();

   ASSERT(ctx);
   int argc = 1;
   const char *argv[] = {"", NULL};
   m_main = new Gtk::Main(&argc, (char ***) &argv, false);

   if (wrapper) {
      BlockService::GetInstance()->Init(ctx);
   }

   gUserMainWidget = gtk_invisible_new();
#ifndef GTK3
   gXDisplay = GDK_WINDOW_XDISPLAY(gUserMainWidget->window);
#else
   gXDisplay = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gUserMainWidget));
#endif
   gXRoot = RootWindow(gXDisplay, DefaultScreen(gXDisplay));

   /*
    * Register legacy (backdoor) version of copy paste.
    */
   CopyPaste_SetVersion(1);
   CopyPaste_Register(gUserMainWidget, ctx);

   return TRUE;
}


/**
 *
 * Destructor.
 */

CopyPasteDnDX11::~CopyPasteDnDX11()
{
   if (m_copyPasteUI) {
      delete m_copyPasteUI;
   }
   if (m_dndUI) {
      delete m_dndUI;
   }
   if (m_main) {
      delete m_main;
   }

   /*
    * Legacy CP.
    */
   CopyPaste_Unregister(gUserMainWidget);

   if (gUserMainWidget) {
      gtk_widget_destroy(gUserMainWidget);
   }
}


/**
 *
 * Register copy and paste capabilities with the VMX.
 *
 * @return TRUE on success, FALSE on failure
 */

gboolean
CopyPasteDnDX11::RegisterCP()
{
   TRACE_CALL();
   CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();

   if (wrapper->IsCPRegistered()) {
      return TRUE;
   }

   if (!wrapper->IsCPEnabled()) {
      return FALSE;
   }

   m_copyPasteUI = new CopyPasteUIX11();
   if (m_copyPasteUI) {
      if (m_copyPasteUI->Init()) {
         BlockService *bs = BlockService::GetInstance();
         m_copyPasteUI->SetBlockControl(bs->GetBlockCtrl());
         wrapper->SetCPIsRegistered(TRUE);
         int version = wrapper->GetCPVersion();
         g_debug("%s: version is %d\n", __FUNCTION__, version);

         if (version >= 3) {
            CopyPasteVersionChanged(version);
            m_copyPasteUI->SetCopyPasteAllowed(TRUE);
         }
         /*
          * Set legacy copy/paste version.
          */
         CopyPaste_SetVersion(version);
      } else {
         delete m_copyPasteUI;
         m_copyPasteUI = NULL;
      }
   }
   return wrapper->IsCPRegistered();
}


/**
 *
 * Register DnD capabilities with the VMX.
 *
 * @return TRUE on success, FALSE on failure
 */

gboolean
CopyPasteDnDX11::RegisterDnD()
{
   TRACE_CALL();
   CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();

   if (!wrapper->IsDnDEnabled()) {
      return FALSE;
   }

   if (!wrapper->IsDnDRegistered()) {
      m_dndUI = new DnDUIX11(wrapper->GetToolsAppCtx());
      if (m_dndUI) {
         BlockService *bs = BlockService::GetInstance();
         m_dndUI->SetBlockControl(bs->GetBlockCtrl());
         if (m_dndUI->Init()) {
            wrapper->SetDnDIsRegistered(TRUE);
            m_dndUI->SetDnDAllowed(TRUE);
            int version = wrapper->GetDnDVersion();
            g_debug("%s: dnd version is %d\n", __FUNCTION__, version);
            if (version >= 3) {
               DnDVersionChanged(version);
            }
         } else {
            delete m_dndUI;
            m_dndUI = NULL;
         }
      }
   }

   g_debug("%s: dnd is registered? %d\n", __FUNCTION__, (int) wrapper->IsDnDRegistered());
   return wrapper->IsDnDRegistered();
}


/**
 *
 * Unregister copy paste capabilities and do general cleanup.
 */

void
CopyPasteDnDX11::UnregisterCP()
{
   TRACE_CALL();
   CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();
   if (wrapper->IsCPRegistered()) {
      if (m_copyPasteUI) {
         delete m_copyPasteUI;
         m_copyPasteUI = NULL;
      }
      wrapper->SetCPIsRegistered(FALSE);
      wrapper->SetCPVersion(-1);
   }
}


/**
 *
 * Unregister DnD capabilities and do general cleanup.
 */

void
CopyPasteDnDX11::UnregisterDnD()
{
   TRACE_CALL();
   CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();
   if (wrapper->IsDnDRegistered()) {
      if (m_dndUI) {
         delete m_dndUI;
         m_dndUI = NULL;
      }
      wrapper->SetDnDIsRegistered(false);
      wrapper->SetDnDVersion(-1);
      return;
   }
}


/**
 *
 * Communicate dnd allowed to platform implementation.
 *
 * @param[allowed] if TRUE, dnd allowed.
 */

void
CopyPasteDnDX11::SetDnDAllowed(bool allowed)
{
   ASSERT(m_dndUI);
   TRACE_CALL();
   m_dndUI->SetDnDAllowed(allowed);
}


/**
 *
 * Communicate copypaste allowed to platform implementation.
 *
 * @param[allowed] if TRUE, copy paste allowed.
 */

void
CopyPasteDnDX11::SetCopyPasteAllowed(bool allowed)
{
   ASSERT(m_copyPasteUI);
   TRACE_CALL();
   m_copyPasteUI->SetCopyPasteAllowed(allowed);
}


/**
 * Communicate copy paste version change to the platform implementation.
 *
 * @param[in] version the new version
 */

void
CopyPasteDnDX11::CopyPasteVersionChanged(int version)
{
   TRACE_CALL();
   CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();
   ToolsAppCtx *ctx = wrapper->GetToolsAppCtx();
   g_debug("%s: calling VmxCopyPasteVersionChanged (version %d)\n",
          __FUNCTION__, version);
   if (ctx) {
      m_copyPasteUI->VmxCopyPasteVersionChanged(ctx->rpc, version);
   }
}


/**
 * Communicate DnD version change by calling the platform implementation.
 *
 * @param[in] version the new version.
 */

void
CopyPasteDnDX11::DnDVersionChanged(int version)
{
   TRACE_CALL();
   CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();
   ToolsAppCtx *ctx = wrapper->GetToolsAppCtx();
   g_debug("%s: calling VmxDnDVersionChanged (version %d)\n",
          __FUNCTION__, version);
   ASSERT(ctx);
   ASSERT(m_dndUI);
   m_dndUI->VmxDnDVersionChanged(ctx->rpc, version);
}


/**
 *
 * Initialize pointer code.
 */

void
CopyPasteDnDX11::PointerInit()
{
   TRACE_CALL();
   CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();

   ASSERT(wrapper);

   ToolsAppCtx *ctx = wrapper->GetToolsAppCtx();
   ASSERT(ctx);

   Pointer_Init(ctx);
}


/**
 * Return platform DnD/CP caps.
 *
 * @return 32-bit caps vector.
 */

uint32
CopyPasteDnDX11::GetCaps()
{
   return DND_CP_CAP_VALID |
          DND_CP_CAP_DND |
          DND_CP_CAP_CP |
          DND_CP_CAP_FORMATS_ALL |
          DND_CP_CAP_ACTIVE_CP |
          DND_CP_CAP_BIG_BUFFER;
}
07070100000303000081A4000000000000000000000001682255050000CF0F000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/copyPasteUIX11.cpp  /*********************************************************
 * Copyright (c) 2009-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * copyPasteUIX11.cpp --
 *
 *    This class implements the methods that allows CopyPaste between host
 *    and guest.
 *
 *    For a perspective on X copy/paste, see
 *    http://www.jwz.org/doc/x-cut-and-paste.html
 */


/*
 * A Word on Selection Timestamps
 *
 * ICCCM §2.6.2 Target Atoms
 *    The TIMESTAMP property is an INTEGER.
 *
 * ICCCM §2.7 Use of Selection Properties
 *    The format of INTEGER is 32.
 *
 * XGetWindowProperty(3)
 *    “If the returned format is 32, the property will be stored as an
 *    array of longs (which in a 64-bit application will be 64-bit values
 *    that are padded in the upper 4 bytes).”
 *
 * For all intents and purposes, on x86 and x86_64 X selection timestamps
 * are a 32-bit quantity. (X11/Xproto.h's xSetSelectionOwnerReq defines the
 * “time” member as the lower 32 bits of type Time.) X clients, on the
 * other hand, operate on Time as either a CARD32 (uint32) or an unsigned
 * long (i.e., on a 64-bit machine Time may occupy 8 bytes).
 *
 * Breaking it down:
 *   · When Gtk+ provides an X11 selection via Gtk::SelectionData, on a
 *     32-bit machine we'll have 4 bytes of raw data.  Everything's copacetic.
 *   · On a 64-bit machine, even if the source client provides 32 bits of
 *     timestamp data, Gtk+ will decode as an unsigned long and provide 8
 *     bytes of raw data.
 *   · On a 64-bit machine with a wacky application which actually tries
 *     to record a full 64 bits of timestamp data, Gtk+ will provide 16 bytes:
 *     <low 32 bits> <32 bits of 0s> <high 32 bits> <32 bits of 0s>.  (See
 *     PR 882322, mrxvt.)
 *
 *   In all instances, we're interested in _only_ the lowest 32 bits, so we'll
 *   ignore everything else.
 */


#define G_LOG_DOMAIN "dndcp"

#include <sys/time.h>
#include <time.h>
#include <cxxabi.h>
#include "copyPasteDnDWrapper.h"
#include "copyPasteUIX11.h"
#include "dndFileList.hh"
#include "guestDnDCPMgr.hh"
#include "tracer.hh"
#include "vmblock.h"
#include "fcntl.h"
#include "file.h"
#include "dnd.h"
#include "dndMsg.h"

extern "C" {
   #include "dndClipboard.h"
   #include "cpName.h"
   #include "cpNameUtil.h"
   #include "rpcout.h"
   #include "vmware/guestrpc/tclodefs.h"
}

/*
 * Gtk 1.2 doesn't know about the CLIPBOARD selection, but that doesn't
 * matter, we just create the atom we need directly in main().
 *
 * This is for V1 text copy paste only!
 */
#ifndef GDK_SELECTION_CLIPBOARD
GdkAtom GDK_SELECTION_CLIPBOARD;
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::CopyPasteUIX11 --
 *
 *    Constructor.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

CopyPasteUIX11::CopyPasteUIX11()
 : mClipboardEmpty(true),
   mHGStagingDir(""),
   mIsClipboardOwner(false),
   mClipTime(0),
   mPrimTime(0),
   mLastTimestamp(0),
   mThread(0),
   mHGGetListTime(0),
   mHGGetFileStatus(DND_FILE_TRANSFER_NOT_STARTED),
   mBlockAdded(false),
   mBlockCtrl(0),
   mInited(false),
   mTotalFileSize(0),
   mGetTimestampOnly(false)
{
   TRACE_CALL();
   GuestDnDCPMgr *p = GuestDnDCPMgr::GetInstance();
   ASSERT(p);
   mCP = p->GetCopyPasteMgr();
   ASSERT(mCP);

   mThreadParams.fileBlockCondExit = false;
   pthread_mutex_init(&mThreadParams.fileBlockMutex, NULL);
   pthread_cond_init(&mThreadParams.fileBlockCond, NULL);
   mThreadParams.cp = this;
   int ret = pthread_create(&mThread,
                            NULL,
                            FileBlockMonitorThread,
                            (void *)&(this->mThreadParams));
   if (ret != 0) {
      Warning("%s: Create thread failed, errno:%d.\n", __FUNCTION__, ret);
      mThread = 0;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::Init --
 *
 *    Initialize copy paste UI class and register for V3 or greater copy
 *    paste.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

bool
CopyPasteUIX11::Init()
{
   TRACE_CALL();
   if (mInited) {
      g_debug("%s: mInited is true\n", __FUNCTION__);
      return true;
   }

   CPClipboard_Init(&mClipboard);

   Gtk::TargetEntry gnome(FCP_TARGET_NAME_GNOME_COPIED_FILES);
   Gtk::TargetEntry kde(FCP_TARGET_NAME_URI_LIST);
   Gtk::TargetEntry nautilus(FCP_TARGET_NAME_NAUTILUS_FILES);
   gnome.set_info(FCP_TARGET_INFO_GNOME_COPIED_FILES);
   kde.set_info(FCP_TARGET_INFO_URI_LIST);
   nautilus.set_info(FCP_TARGET_INFO_NAUTILUS_FILES);

   mListTargets.push_back(gnome);
   mListTargets.push_back(kde);
   mListTargets.push_back(nautilus);

   mCP->srcRecvClipChanged.connect(
      sigc::mem_fun(this, &CopyPasteUIX11::GetRemoteClipboardCB));
   mCP->destRequestClipChanged.connect(
      sigc::mem_fun(this, &CopyPasteUIX11::GetLocalClipboard));
   mCP->getFilesDoneChanged.connect(
      sigc::mem_fun(this, &CopyPasteUIX11::GetLocalFilesDone));

   mInited = true;
   return true;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::~CopyPaste --
 *
 *    Destructor.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

CopyPasteUIX11::~CopyPasteUIX11()
{
   TRACE_CALL();
   CPClipboard_Destroy(&mClipboard);
   /* Any files from last unfinished file transfer should be deleted. */
   if (DND_FILE_TRANSFER_IN_PROGRESS == mHGGetFileStatus &&
       !mHGStagingDir.empty()) {
      uint64 totalSize = File_GetSizeEx(mHGStagingDir.c_str());
      if (mTotalFileSize != totalSize) {
         g_debug("%s: deleting %s, expecting %" FMT64 "u, finished %" FMT64 "u\n",
                 __FUNCTION__, mHGStagingDir.c_str(),
                 mTotalFileSize, totalSize);
         DnD_DeleteStagingFiles(mHGStagingDir.c_str(), FALSE);
      } else {
         g_debug("%s: file size match %s\n",
                 __FUNCTION__, mHGStagingDir.c_str());
      }
   }
   if (mBlockAdded) {
      g_debug("%s: removing block for %s\n", __FUNCTION__, mHGStagingDir.c_str());
      /* We need to make sure block subsystem has not been shut off. */
      mBlockAdded = false;
      if (DnD_BlockIsReady(mBlockCtrl)) {
         mBlockCtrl->RemoveBlock(mBlockCtrl->fd, mHGStagingDir.c_str());
      }
   }

   TerminateThread();
   pthread_mutex_destroy(&mThreadParams.fileBlockMutex);
   pthread_cond_destroy(&mThreadParams.fileBlockCond);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::VmxCopyPasteVersionChanged --
 *
 *      Update version information in mCP.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::VmxCopyPasteVersionChanged(RpcChannel *chan,    // IN
                                           uint32 version)      // IN
{
   ASSERT(mCP);
   g_debug("%s: new version is %d\n", __FUNCTION__, version);
   mCP->VmxCopyPasteVersionChanged(version);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::GetLocalClipboard --
 *
 *    Retrives the data from local clipboard and sends it to host. Send empty
 *    data back if there is no data or can not get data successfully. For
 *    guest->host copy/paste.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::GetLocalClipboard(void)
{
   g_debug("%s: enter.\n", __FUNCTION__);

   if (mIsClipboardOwner) {
      /* If we are clipboard owner, send a not-changed clip to host. */
      g_debug("%s: we are owner, send unchanged clip back.\n", __FUNCTION__);
      SendClipNotChanged();
      return;
   }

   if (!mCP->IsCopyPasteAllowed()) {
      g_debug("%s: copyPaste is not allowed\n", __FUNCTION__);
      return;
   }

   Glib::RefPtr<Gtk::Clipboard> refClipboard =
      Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD);

   mClipTime = 0;
   mPrimTime = 0;
   mGHSelection = GDK_SELECTION_CLIPBOARD;
   mGetTimestampOnly = false;
   g_debug("%s: retrieving timestamps\n", __FUNCTION__);
   refClipboard->request_contents(TARGET_NAME_TIMESTAMP,
      sigc::mem_fun(this, &CopyPasteUIX11::LocalClipboardTimestampCB));
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::GetCurrentTime --
 *
 *    Get current time in microseconds.
 *
 * Results:
 *    Time in microseconds.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

VmTimeType
CopyPasteUIX11::GetCurrentTime(void)
{
   struct timeval tv;
   VmTimeType curTime;

   if (gettimeofday(&tv, NULL) != 0) {
      g_debug("%s: gettimeofday failed!\n", __FUNCTION__);
      return (VmTimeType) 0;
   }
   curTime = (tv.tv_sec * 1000000 + tv.tv_usec);
   return curTime;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::LocalGetFileRequestCB --
 *
 *      Callback from a file paste request from another guest application.
 *      Begins copying the files from host to guest and return the file list.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::LocalGetFileRequestCB(Gtk::SelectionData& sd,        // IN:
                                      guint info)                    // IN:
{
   g_debug("%s: enter.\n", __FUNCTION__);

   if (!mIsClipboardOwner || !mCP->IsCopyPasteAllowed()) {
      g_debug("%s: not clipboard ownder, or copy paste not allowed, returning.\n",
            __FUNCTION__);
      sd.set(sd.get_target().c_str(), "");
      return;
   }

   g_debug("%s: Got paste request, target is %s\n", __FUNCTION__,
         sd.get_target().c_str());

   if (mHGGetFileStatus != DND_FILE_TRANSFER_NOT_STARTED) {
      /*
       * On KDE (at least), we can see this multiple times, so ignore if
       * we are already getting files.
       */
      g_debug("%s: GetFiles already started, returning uriList [%s]\n",
              __FUNCTION__, mHGCopiedUriList.c_str());
      sd.set(sd.get_target().c_str(), mHGCopiedUriList.c_str());
      return;
   } else {
      utf::string str;
      utf::string hgStagingDir;
      utf::string stagingDirName;
      utf::string pre;
      utf::string post;
      size_t index = 0;

      hgStagingDir = utf::CopyAndFree(DnD_CreateStagingDirectory());
      g_debug("%s: Getting files. Staging dir: %s", __FUNCTION__,
            hgStagingDir.c_str());

      if (0 == hgStagingDir.bytes()) {
         g_debug("%s: Can not create staging directory\n", __FUNCTION__);
         sd.set(sd.get_target().c_str(), "");
         return;
      }
      mHGGetFileStatus = DND_FILE_TRANSFER_IN_PROGRESS;

      mBlockAdded = false;
      if (DnD_BlockIsReady(mBlockCtrl) && mBlockCtrl->AddBlock(mBlockCtrl->fd, hgStagingDir.c_str())) {
         g_debug("%s: add block for %s.\n",
               __FUNCTION__, hgStagingDir.c_str());
         mBlockAdded = true;
         pthread_mutex_lock(&mThreadParams.fileBlockMutex);
         mThreadParams.fileBlockCondExit = false;
         mThreadParams.fileBlockName = VMBLOCK_FUSE_NOTIFY_ROOT;
         mThreadParams.fileBlockName += DIRSEPS;
         mThreadParams.fileBlockName += GetLastDirName(hgStagingDir);
         pthread_cond_signal(&mThreadParams.fileBlockCond);
         pthread_mutex_unlock(&mThreadParams.fileBlockMutex);
      } else {
         g_debug("%s: unable to add block for %s.\n",
               __FUNCTION__, hgStagingDir.c_str());
      }

      mHGStagingDir = hgStagingDir;

      /* Provide URIs for each path in the guest's file list. */
      if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) {
         mHGCopiedUriList = "copy\n";
         pre = FCP_GNOME_LIST_PRE;
         post = FCP_GNOME_LIST_POST;
      } else if (FCP_TARGET_INFO_URI_LIST == info) {
         pre = DND_URI_LIST_PRE_KDE;
         post = DND_URI_LIST_POST;
      } else if (FCP_TARGET_INFO_NAUTILUS_FILES == info) {
         mHGCopiedUriList =
            utf::string(FCP_TARGET_MIME_NAUTILUS_FILES) + "\ncopy\n";
         pre = FCP_GNOME_LIST_PRE;
         post = FCP_GNOME_LIST_POST;
      } else {
         g_debug("%s: Unknown request target: %s\n", __FUNCTION__,
               sd.get_target().c_str());
         sd.set(sd.get_target().c_str(), "");
         return;
      }

      /* Provide path within vmblock file system instead of actual path. */
      stagingDirName = GetLastDirName(hgStagingDir);
      if (0 == stagingDirName.bytes()) {
         g_debug("%s: Can not get staging directory name\n", __FUNCTION__);
         sd.set(sd.get_target().c_str(), "");
         return;
      }

      while ((str = GetNextPath(mHGFCPData, index).c_str()).bytes() != 0) {
         g_debug("%s: Path: %s", __FUNCTION__, str.c_str());
         mHGCopiedUriList += pre;
         if (mBlockAdded) {
            mHGCopiedUriList += mBlockCtrl->blockRoot;
            mHGCopiedUriList += DIRSEPS + stagingDirName + DIRSEPS + str + post;
         } else {
            mHGCopiedUriList += DIRSEPS + hgStagingDir + DIRSEPS + str + post;
         }
      }

      /* Nautilus does not expect FCP_GNOME_LIST_POST after the last uri. See bug 143147. */
      if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) {
         mHGCopiedUriList.erase(mHGCopiedUriList.size() - 1, 1);
      }
   }

   if (0 == mHGCopiedUriList.bytes()) {
      g_debug("%s: Can not get uri list\n", __FUNCTION__);
      sd.set(sd.get_target().c_str(), "");
      return;
   }

   if (!mBlockAdded) {
      /*
       * If there is no blocking driver, wait here till file copy is done.
       * 2 reasons to keep this:
       * 1. If run vmware-user stand-alone as non-root, blocking driver can
       *    not be opened. g_debug purpose only.
       * 2. Other platforms (Solaris, etc) may also use this code,
       *    and there is no blocking driver yet.
       *
       * Polling here will not be sufficient for large files (experiments
       * showed it was sufficient for a 256MB file, and failed for a 1GB
       * file, but those numbers are of course context-sensitive and so YMMV).
       * The reason is we are executing in the context of gtkmm callback, and
       * apparently it only has so much patience regarding how quickly we
       * return.
       */
      CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();
      ToolsAppCtx *ctx = wrapper->GetToolsAppCtx();
      while (mHGGetFileStatus == DND_FILE_TRANSFER_IN_PROGRESS) {
         struct timeval tv;

         tv.tv_sec = 0;
         g_main_context_iteration(g_main_loop_get_context(ctx->mainLoop),
                                  FALSE);
         if (select(0, NULL, NULL, NULL, &tv) == -1) {
            g_debug("%s: error in select (%s).\n", __FUNCTION__,
                  strerror(errno));
            sd.set(sd.get_target().c_str(), "");
            return;
         }
      }
      g_debug("%s: file transfer done!\n", __FUNCTION__);
   }

   g_debug("%s: providing file list [%s]\n", __FUNCTION__,
         mHGCopiedUriList.c_str());

   sd.set(sd.get_target().c_str(), mHGCopiedUriList.c_str());
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::LocalGetTextOrRTFRequestCB --
 *
 *      Callback from a text or RTF paste request from another guest application.
 *      H->G copy paste only.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::LocalGetTextOrRTFRequestCB(Gtk::SelectionData& sd, // IN/OUT
                                           guint info)             // Ignored
{
   sd.set(sd.get_target().c_str(), "");

   if (!mCP->IsCopyPasteAllowed()) {
      return;
   }

   const utf::string target = sd.get_target().c_str();

   g_debug("%s: Got paste request, target is %s\n",
         __FUNCTION__, target.c_str());

   if (target == TARGET_NAME_APPLICATION_RTF ||
       target == TARGET_NAME_TEXT_RICHTEXT ||
       target == TARGET_NAME_TEXT_RTF) {
      if (0 == mHGRTFData.size()) {
         g_debug("%s: Can not get valid RTF data\n", __FUNCTION__);
         return;
      }

      g_debug("%s: providing RTF data, size %" FMTSZ "u\n",
            __FUNCTION__, mHGRTFData.size());

      sd.set(target.c_str(), mHGRTFData.c_str());
   }

   if (target == TARGET_NAME_STRING ||
       target == TARGET_NAME_TEXT_PLAIN ||
       target == TARGET_NAME_UTF8_STRING ||
       target == TARGET_NAME_COMPOUND_TEXT) {
      if (0 == mHGTextData.bytes()) {
         g_debug("%s: Can not get valid text data\n", __FUNCTION__);
         return;
      }
      g_debug("%s: providing plain text, size %" FMTSZ "u\n",
            __FUNCTION__, mHGTextData.bytes());

      sd.set(target.c_str(), mHGTextData.c_str());
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPaste::LocalClearClipboardCB --
 *
 *      Clear clipboard request from another host application.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::LocalClearClipboardCB(void)
{
   g_debug("%s: got clear callback\n", __FUNCTION__);
   mIsClipboardOwner = FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * CopyPasteUIX11::LocalClipboardTimestampCB --
 *
 *      Got the local clipboard timestamp. Ask for the primary timestamp.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
CopyPasteUIX11::LocalClipboardTimestampCB(const Gtk::SelectionData& sd)  // IN
{
   int length = sd.get_length();

   /*
    * See “A Word on Selection Timestamps” above.
    */
   if (   (   sd.get_data_type().compare("INTEGER") == 0
           || sd.get_data_type().compare("TIMESTAMP") == 0)
       && sd.get_format() == 32
       && length >= 4 /* sizeof uint32 */) {
      mClipTime = reinterpret_cast<const uint32*>(sd.get_data())[0];
   } else {
      g_debug("%s: Unable to get mClipTime (sd: len %d, type %s, fmt %d).",
              __FUNCTION__, length,
              length >= 0 ? sd.get_data_type().c_str() : "(n/a)",
              sd.get_format());
   }

   Glib::RefPtr<Gtk::Clipboard> refClipboard
      = Gtk::Clipboard::get(GDK_SELECTION_PRIMARY);
   refClipboard->request_contents(TARGET_NAME_TIMESTAMP,
      sigc::mem_fun(this, &CopyPasteUIX11::LocalPrimTimestampCB));
}


/*
 *----------------------------------------------------------------------
 *
 * CopyPasteUIX11::LocalPrimTimestampCB --
 *
 *      Got the local primary timestamp. Choose the most recently changed
 *      clipboard and get the selection from it.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
CopyPasteUIX11::LocalPrimTimestampCB(const Gtk::SelectionData& sd)  // IN
{
   int length = sd.get_length();

   /*
    * See “A Word on Selection Timestamps” above.
    */
   if (   (   sd.get_data_type().compare("INTEGER") == 0
           || sd.get_data_type().compare("TIMESTAMP") == 0)
       && sd.get_format() == 32
       && length >= 4 /* sizeof uint32 */) {
      mPrimTime = reinterpret_cast<const uint32*>(sd.get_data())[0];
   } else {
      g_debug("%s: Unable to get mPrimTime (sd: len %d, type %s, fmt %d).",
              __FUNCTION__, length,
              length >= 0 ? sd.get_data_type().c_str() : "(n/a)",
              sd.get_format());
   }

   if (mGetTimestampOnly) {
      mLastTimestamp = mClipTime > mPrimTime ? mClipTime : mPrimTime;
      return;
   }

   /* After got both timestamp, choose latest one as active selection. */
   if (mClipTime > mPrimTime) {
      mGHSelection = GDK_SELECTION_CLIPBOARD;
      if (mClipTime > 0 && mClipTime == mLastTimestamp) {
         g_debug("%s: clip is not changed\n", __FUNCTION__);
         SendClipNotChanged();
         return;
      }
      mLastTimestamp = mClipTime;
   } else {
      mGHSelection = GDK_SELECTION_PRIMARY;
      if (mPrimTime > 0 && mPrimTime == mLastTimestamp) {
         g_debug("%s: clip is not changed\n", __FUNCTION__);
         SendClipNotChanged();
         return;
      }
      mLastTimestamp = mPrimTime;
   }

   Glib::RefPtr<Gtk::Clipboard> refClipboard;
   bool flipped = false;
again:
   bool validDataInClip = false;
   refClipboard = Gtk::Clipboard::get(mGHSelection);

   g_debug("%s: trying %s selection.\n", __FUNCTION__,
          mGHSelection == GDK_SELECTION_PRIMARY ? "Primary" : "Clip");

   CPClipboard_Clear(&mClipboard);

   /* First check for URIs. This must always be done first */
   bool haveURIs = false;
   std::string format;
   if (mCP->CheckCapability(DND_CP_CAP_FILE_CP) && refClipboard->wait_is_target_available(FCP_TARGET_NAME_GNOME_COPIED_FILES)) {
      format = FCP_TARGET_NAME_GNOME_COPIED_FILES;
      haveURIs = true;
   } else if (mCP->CheckCapability(DND_CP_CAP_FILE_CP) && refClipboard->wait_is_target_available(FCP_TARGET_NAME_URI_LIST)) {
      format = FCP_TARGET_NAME_URI_LIST;
      haveURIs = true;
   }

   if (haveURIs) {
      refClipboard->request_contents(format,
                                     sigc::mem_fun(this,
                                                   &CopyPasteUIX11::LocalReceivedFileListCB));
      return;
   }

   /* Try to get image data from clipboard. */
   Glib::RefPtr<Gdk::Pixbuf> img = refClipboard->wait_for_image();
   gsize bufSize;
   if (mCP->CheckCapability(DND_CP_CAP_IMAGE_CP) && img) {
      gchar *buf = NULL;

      img->save_to_buffer(buf, bufSize, Glib::ustring("png"));
      if (bufSize > 0  &&
          bufSize <= (int)CPCLIPITEM_MAX_SIZE_V3 &&
          CPClipboard_SetItem(&mClipboard, CPFORMAT_IMG_PNG,
                              buf, bufSize)) {
         validDataInClip = true;
         g_debug("%s: Got PNG: %" FMTSZ "u\n", __FUNCTION__, bufSize);
      } else {
         g_debug("%s: Failed to get PNG\n", __FUNCTION__);
      }
      g_free(buf);
   }

   /* Try to get RTF data from clipboard. */
   bool haveRTF = false;
   if (refClipboard->wait_is_target_available(TARGET_NAME_APPLICATION_RTF)) {
      g_debug("%s: APP RTF is available\n", __FUNCTION__);
      format = TARGET_NAME_APPLICATION_RTF;
      haveRTF = true;
   }
   if (refClipboard->wait_is_target_available(TARGET_NAME_TEXT_RICHTEXT)) {
      g_debug("%s: RICHTEXT is available\n", __FUNCTION__);
      format = TARGET_NAME_TEXT_RICHTEXT;
      haveRTF = true;
   }
   if (refClipboard->wait_is_target_available(TARGET_NAME_TEXT_RTF)) {
      g_debug("%s: TEXT_RTF is available\n", __FUNCTION__);
      format = TARGET_NAME_TEXT_RTF;
      haveRTF = true;
   }

   if (mCP->CheckCapability(DND_CP_CAP_RTF_CP) && haveRTF) {
      /*
       * There is a function for waiting for rtf data, but that was leading
       * to crashes. It's use required we instantiate a class that implements
       * Gtk::TextBuffer and then query that class for a reference to it's
       * TextBuffer instance. This all compiled fine but crashed in testing
       * so we opt to use the more generic API here which seemed more stable.
       */
      Gtk::SelectionData sdata = refClipboard->wait_for_contents(format);
      bufSize = sdata.get_length();
      if (bufSize > 0  &&
          bufSize <= (int)CPCLIPITEM_MAX_SIZE_V3 &&
          CPClipboard_SetItem(&mClipboard, CPFORMAT_RTF,
                              (const void *)sdata.get_data(), bufSize + 1)) {
         validDataInClip = true;
         g_debug("%s: Got RTF\n", __FUNCTION__);
      } else {
         g_debug("%s: Failed to get RTF size %d max %d\n",
               __FUNCTION__, (int) bufSize, (int)CPCLIPITEM_MAX_SIZE_V3);
      }
   }

   /* Try to get Text data from clipboard. */
   if (mCP->CheckCapability(DND_CP_CAP_PLAIN_TEXT_CP) &&
       refClipboard->wait_is_text_available()) {
      g_debug("%s: ask for text\n", __FUNCTION__);
      Glib::ustring str = refClipboard->wait_for_text();
      bufSize = str.bytes();
      if (bufSize > 0  &&
          bufSize <= (int)CPCLIPITEM_MAX_SIZE_V3 &&
          CPClipboard_SetItem(&mClipboard, CPFORMAT_TEXT,
                              (const void *)str.data(), bufSize + 1)) {
         validDataInClip = true;
         g_debug("%s: Got TEXT: %" FMTSZ "u\n", __FUNCTION__, bufSize);
      } else {
         g_debug("%s: Failed to get TEXT\n", __FUNCTION__);
      }
   }

   if (validDataInClip) {
      /*
       * RTF or text data (or both) in the clipboard.
       */
      mCP->DestUISendClip(&mClipboard);
   } else if (!flipped) {
      /*
       * If we get here, we got nothing (no image, URI, text) so
       * try the other selection.
       */
      g_debug("%s: got nothing for this selection, try the other.\n",
            __FUNCTION__);
      mGHSelection = mGHSelection == GDK_SELECTION_PRIMARY ?
                     GDK_SELECTION_CLIPBOARD : GDK_SELECTION_PRIMARY;
      flipped = true;
      goto again;
   } else {
      g_debug("%s: got nothing, send empty clip back.\n",
            __FUNCTION__);
      mCP->DestUISendClip(&mClipboard);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * CopyPasteUIX11::LocalReceivedFileListCB --
 *
 *      Got clipboard or primary selection file list. Parse it and add
 *      it to the crossplaform clipboard. Send clipboard to the host.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
CopyPasteUIX11::LocalReceivedFileListCB(const Gtk::SelectionData& sd)        // IN
{
   g_debug("%s: enter", __FUNCTION__);
   const utf::string target = sd.get_target().c_str();

   if (!mCP->CheckCapability(DND_CP_CAP_FILE_CP)) {
      /*
       * Disallowed based on caps settings, return.
       */
      return;
   }
   if (target == FCP_TARGET_NAME_GNOME_COPIED_FILES ||
       target == FCP_TARGET_NAME_URI_LIST) {
      LocalGetSelectionFileList(sd);
      mCP->DestUISendClip(&mClipboard);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::LocalGetFileContentsRequestCB --
 *
 *      Callback from a file paste request from another guest application.
 *      Return the file list.
 *
 *      H->G only.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::LocalGetFileContentsRequestCB(Gtk::SelectionData& sd, // IN
                                              guint info)             // IN
{
   std::vector<utf::string>::const_iterator iter;
   utf::string uriList = "";
   utf::string pre;
   utf::string post;

   if (!mCP->CheckCapability(DND_CP_CAP_FILE_CONTENT_CP)) {
      /*
       * Disallowed based on caps settings, return.
       */
      return;
   }

   sd.set(sd.get_target().c_str(), "");

   /* Provide URIs for each path in the guest's file list. */
   if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) {
      uriList = "copy\n";
      pre = FCP_GNOME_LIST_PRE;
      post = FCP_GNOME_LIST_POST;
   } else if (FCP_TARGET_INFO_URI_LIST == info) {
      pre = DND_URI_LIST_PRE_KDE;
      post = DND_URI_LIST_POST;
   } else if (FCP_TARGET_INFO_NAUTILUS_FILES == info) {
      uriList = utf::string(FCP_TARGET_MIME_NAUTILUS_FILES) + "\ncopy\n";
      pre = FCP_GNOME_LIST_PRE;
      post = FCP_GNOME_LIST_POST;
   } else {
      g_debug("%s: Unknown request target: %s\n",
            __FUNCTION__, sd.get_target().c_str());
      return;
   }

   for (iter = mHGFileContentsList.begin();
        iter != mHGFileContentsList.end();
        iter++) {
      uriList += pre + *iter + post;
   }

   /* Nautilus does not expect FCP_GNOME_LIST_POST after the last uri. See bug 143147. */
   if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) {
      uriList.erase(uriList.size() - 1, 1);
   }

   if (0 == uriList.bytes()) {
      g_debug("%s: Can not get uri list\n", __FUNCTION__);
      return;
   }

   g_debug("%s: providing file list [%s]\n", __FUNCTION__, uriList.c_str());

   sd.set(sd.get_target().c_str(), uriList.c_str());
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::LocalGetSelectionFileList --
 *
 *      Construct local file list and remote file list from selection data.
 *      Called by both DnD and FCP.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::LocalGetSelectionFileList(const Gtk::SelectionData& sd)      // IN
{
   utf::string source;
   char *newPath;
   size_t newPathLen;
   size_t index = 0;
   DnDFileList fileList;
   DynBuf buf;
   uint64 totalSize = 0;
   int64 size;

   /*
    * Turn the uri list into two \0  delimited lists. One for full paths and
    * one for just the last path component.
    */
   source = sd.get_data_as_string().c_str();
   g_debug("%s: Got file list: [%s]\n", __FUNCTION__, source.c_str());

   /*
    * In gnome, before file list there may be a extra line indicating it
    * is a copy or cut.
    */
   if (source.startsWith("copy\n")) {
      source = source.erase(0, 5);
   }

   if (source.startsWith("cut\n")) {
      source = source.erase(0, 4);
   }

   while (source.bytes() > 0 &&
          (source[0] == '\n' || source[0] == '\r' || source[0] == ' ')) {
      source = source.erase(0, 1);
   }

   while ((newPath = DnD_UriListGetNextFile(source.c_str(),
                                            &index,
                                            &newPathLen)) != NULL) {
      char *newRelPath;

#if defined(__linux__)
      if (DnD_UriIsNonFileSchemes(newPath)) {
         /* Try to get local file path for non file uri. */
         GFile *file = g_file_new_for_uri(newPath);
         free(newPath);
         if (!file) {
            g_debug("%s: g_file_new_for_uri failed\n", __FUNCTION__);
            return;
         }
         newPath = g_file_get_path(file);
         g_object_unref(file);
         if (!newPath) {
            g_debug("%s: g_file_get_path failed\n", __FUNCTION__);
            return;
         }
      }
#endif

      /*
       * Parse relative path.
       */
      newRelPath = Str_Strrchr(newPath, DIRSEPC) + 1; // Point to char after '/'

      /* Keep track of how big the fcp files are. */
      if ((size = File_GetSizeEx(newPath)) >= 0) {
         totalSize += size;
      } else {
         g_debug("%s: Unable to get file size for %s\n", __FUNCTION__, newPath);
      }

      g_debug("%s: Adding newPath '%s' newRelPath '%s'\n", __FUNCTION__,
            newPath, newRelPath);
      fileList.AddFile(newPath, newRelPath);
      free(newPath);
   }

   DynBuf_Init(&buf);
   fileList.SetFileSize(totalSize);
   g_debug("%s: totalSize is %" FMT64 "u\n", __FUNCTION__, totalSize);
   fileList.ToCPClipboard(&buf, false);
   CPClipboard_SetItem(&mClipboard, CPFORMAT_FILELIST, DynBuf_Get(&buf),
                       DynBuf_GetSize(&buf));
   DynBuf_Destroy(&buf);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::GetLastDirName --
 *
 *      Try to get last directory name from a full path name.
 *
 * Results:
 *      Last dir name in the full path name if sucess, empty str otherwise
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

utf::string
CopyPasteUIX11::GetLastDirName(const utf::string &str)     // IN
{
   char *baseName;
   File_GetPathName(str.c_str(), NULL, &baseName);
   utf::string s(baseName);
   free(baseName);
   return s;
}


/*
 *----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::GetNextPath --
 *
 *      Provides a substring containing the next path from the provided
 *      NUL-delimited string starting at the provided index.
 *
 * Results:
 *      A string with the next path or "" if there are no more paths.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

utf::utf8string
CopyPasteUIX11::GetNextPath(utf::utf8string& str, // IN: NUL-delimited path list
                            size_t& index)        // IN/OUT: current index into string
{
   utf::utf8string ret;
   size_t start;

   if (index >= str.length()) {
      return "";
   }

   for (start = index; str[index] != '\0' && index < str.length(); index++) {
      /*
       * Escape reserved characters according to RFC 1630.  We'd use
       * Escape_Do() if this wasn't a utf::string, but let's use the same table
       * replacement approach.
       */
      static char const Dec2Hex[] = {
         '0', '1', '2', '3', '4', '5', '6', '7',
         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
      };

      unsigned char ubyte = str[index];

      if (ubyte == '#' ||   /* Fragment identifier delimiter */
          ubyte == '?' ||   /* Query string delimiter */
          ubyte == '*' ||   /* "Special significance within specific schemes" */
          ubyte == '!' ||   /* "Special significance within specific schemes" */
          ubyte == '%' ||   /* Escape character */
          ubyte >= 0x80) {  /* UTF-8 encoding bytes */
         str.replace(index, 1, "%");
         str.insert(index + 1, 1, Dec2Hex[ubyte >> 4]);
         str.insert(index + 2, 1, Dec2Hex[ubyte & 0xF]);
         index += 2;
      }
   }

   ret = str.substr(start, index - start);
   g_debug("%s: nextpath: %s", __FUNCTION__, ret.c_str());
   index++;
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::GetRemoteClipboardCB --
 *
 *    Invoked when got data from host. Update the internal data to get the file
 *    names or the text that needs to be transferred.
 *
 *    Method for copy and paste from host to guest.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::GetRemoteClipboardCB(const CPClipboard *clip) // IN
{
   Glib::RefPtr<Gtk::Clipboard> refClipboard =
      Gtk::Clipboard::get(GDK_SELECTION_CLIPBOARD);
   Glib::RefPtr<Gtk::Clipboard> refPrimary =
      Gtk::Clipboard::get(GDK_SELECTION_PRIMARY);
   void *buf;
   size_t sz;

   TRACE_CALL();
   if (!clip) {
      g_debug("%s: No clipboard contents.", __FUNCTION__);
      return;
   }

   if (mBlockAdded) {
      mBlockAdded = false;
      if (DnD_BlockIsReady(mBlockCtrl)) {
         mBlockCtrl->RemoveBlock(mBlockCtrl->fd, mHGStagingDir.c_str());
      }
   }

   /* Clear the clipboard contents if we are the owner. */
   if (mIsClipboardOwner) {
      refClipboard->clear();
      refPrimary->clear();
      mIsClipboardOwner = false;
      g_debug("%s: Cleared local clipboard", __FUNCTION__);
   }

   mHGTextData.clear();
   mHGRTFData.clear();
   mHGFCPData.clear();

   if (CPClipboard_ItemExists(clip, CPFORMAT_TEXT) ||
       CPClipboard_ItemExists(clip, CPFORMAT_RTF)) {
      std::vector<Gtk::TargetEntry> targets;

      /*
       * rtf should be first in the target list otherwise OpenOffice may not
       * accept paste.
       */
      if (CPClipboard_GetItem(clip, CPFORMAT_RTF, &buf, &sz)) {
         g_debug("%s: RTF data, size %" FMTSZ "u.\n", __FUNCTION__, sz);
         Gtk::TargetEntry appRtf(TARGET_NAME_APPLICATION_RTF);
         Gtk::TargetEntry textRichText(TARGET_NAME_TEXT_RICHTEXT);
         Gtk::TargetEntry textRtf(TARGET_NAME_TEXT_RTF);

         targets.push_back(appRtf);
         targets.push_back(textRichText);
         targets.push_back(textRtf);
         mHGRTFData = std::string((const char *)buf);
         mIsClipboardOwner = true;
      }

      if (CPClipboard_GetItem(clip, CPFORMAT_TEXT, &buf, &sz)) {
         Gtk::TargetEntry stringText(TARGET_NAME_STRING);
         Gtk::TargetEntry plainText(TARGET_NAME_TEXT_PLAIN);
         Gtk::TargetEntry utf8Text(TARGET_NAME_UTF8_STRING);
         Gtk::TargetEntry compountText(TARGET_NAME_COMPOUND_TEXT);

         g_debug("%s: Text data, size %" FMTSZ "u.\n", __FUNCTION__, sz);
         targets.push_back(stringText);
         targets.push_back(plainText);
         targets.push_back(utf8Text);
         targets.push_back(compountText);
         mHGTextData = utf::string(reinterpret_cast<char *>(buf),
                                   STRING_ENCODING_UTF8);
         mIsClipboardOwner = true;
      }

      refClipboard->set(targets,
                        sigc::mem_fun(this, &CopyPasteUIX11::LocalGetTextOrRTFRequestCB),
                        sigc::mem_fun(this, &CopyPasteUIX11::LocalClearClipboardCB));
      refPrimary->set(targets,
                      sigc::mem_fun(this, &CopyPasteUIX11::LocalGetTextOrRTFRequestCB),
                      sigc::mem_fun(this, &CopyPasteUIX11::LocalClearClipboardCB));
      return;
   }

   if (CPClipboard_GetItem(clip, CPFORMAT_IMG_PNG, &buf, &sz)) {
      g_debug("%s: PNG data, size %" FMTSZ "u.\n", __FUNCTION__, sz);
      /* Try to load buf into pixbuf, and write to local clipboard. */
      try {
         Glib::RefPtr<Gdk::PixbufLoader> loader = Gdk::PixbufLoader::create();
         loader->write((const guint8 *)buf, sz);
         loader->close();

         refClipboard->set_image(loader->get_pixbuf());
         refPrimary->set_image(loader->get_pixbuf());

         /*
          * Record current clipboard timestamp to prevent unexpected clipboard
          * exchange.
          *
          * XXX We should do this for all formats.
          */
         mClipTime = 0;
         mPrimTime = 0;
         mGetTimestampOnly = true;
         refClipboard->request_contents(TARGET_NAME_TIMESTAMP,
            sigc::mem_fun(this, &CopyPasteUIX11::LocalClipboardTimestampCB));
      } catch (const Gdk::PixbufError& e) {
         g_message("%s: caught Gdk::PixbufError %s\n", __FUNCTION__, e.what().c_str());
      } catch (std::exception& e) {
         g_message("%s: caught std::exception %s\n", __FUNCTION__, e.what());
      } catch (...) {
         g_message("%s: caught unknown exception (typename %s)\n", __FUNCTION__,
                   __cxxabiv1::__cxa_current_exception_type()->name());
      }
      return;
   }

   if (CPClipboard_GetItem(clip, CPFORMAT_FILELIST, &buf, &sz)) {
      g_debug("%s: File data.\n", __FUNCTION__);
      DnDFileList flist;
      flist.FromCPClipboard(buf, sz);
      mTotalFileSize = flist.GetFileSize();
      mHGFCPData = flist.GetRelPathsStr();

      refClipboard->set(mListTargets,
                        sigc::mem_fun(this, &CopyPasteUIX11::LocalGetFileRequestCB),
                        sigc::mem_fun(this, &CopyPasteUIX11::LocalClearClipboardCB));
      refPrimary->set(mListTargets,
                      sigc::mem_fun(this, &CopyPasteUIX11::LocalGetFileRequestCB),
                      sigc::mem_fun(this, &CopyPasteUIX11::LocalClearClipboardCB));

      mIsClipboardOwner = true;
      mHGGetListTime = GetCurrentTime();
      mHGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
      mHGCopiedUriList = "";
   }

   if (CPClipboard_ItemExists(clip, CPFORMAT_FILECONTENTS)) {
      g_debug("%s: File contents data\n", __FUNCTION__);
      if (LocalPrepareFileContents(clip)) {
         refClipboard->set(mListTargets,
                           sigc::mem_fun(this, &CopyPasteUIX11::LocalGetFileContentsRequestCB),
                           sigc::mem_fun(this, &CopyPasteUIX11::LocalClearClipboardCB));
         refPrimary->set(mListTargets,
                         sigc::mem_fun(this, &CopyPasteUIX11::LocalGetFileContentsRequestCB),
                         sigc::mem_fun(this, &CopyPasteUIX11::LocalClearClipboardCB));
         mIsClipboardOwner = true;
      }
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::LocalPrepareFileContents --
 *
 *      Try to extract file contents from mClipboard. Write all files to a
 *      temporary staging directory. Construct uri list.
 *
 * Results:
 *      true if success, false otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

bool
CopyPasteUIX11::LocalPrepareFileContents(const CPClipboard *clip) // IN
{
   void *buf = NULL;
   size_t sz = 0;
   XDR xdrs;
   CPFileContents fileContents;
   CPFileContentsList *contentsList = NULL;
   size_t nFiles = 0;
   CPFileItem *fileItem = NULL;
   char *tempDir = NULL;
   size_t i = 0;
   bool ret = false;

   if (!CPClipboard_GetItem(clip, CPFORMAT_FILECONTENTS, &buf, &sz)) {
      g_debug("%s: CPClipboard_GetItem failed\n", __FUNCTION__);
      return false;
   }

   /* Extract file contents from buf. */
   xdrmem_create(&xdrs, (char *)buf, sz, XDR_DECODE);
   memset(&fileContents, 0, sizeof fileContents);

   if (!xdr_CPFileContents(&xdrs, &fileContents)) {
      g_debug("%s: xdr_CPFileContents failed.\n", __FUNCTION__);
      xdr_destroy(&xdrs);
      return false;
   }
   xdr_destroy(&xdrs);

   contentsList = fileContents.CPFileContents_u.fileContentsV1;
   if (!contentsList) {
      g_debug("%s: invalid contentsList.\n", __FUNCTION__);
      goto exit;
   }

   nFiles = contentsList->fileItem.fileItem_len;
   if (0 == nFiles) {
      g_debug("%s: invalid nFiles.\n", __FUNCTION__);
      goto exit;
   }

   fileItem = contentsList->fileItem.fileItem_val;
   if (!fileItem) {
      g_debug("%s: invalid fileItem.\n", __FUNCTION__);
      goto exit;
   }

   /*
    * Write files into a temporary staging directory. These files will be moved
    * to final destination, or deleted on next reboot.
    */
   tempDir = DnD_CreateStagingDirectory();
   if (!tempDir) {
      g_debug("%s: DnD_CreateStagingDirectory failed.\n", __FUNCTION__);
      goto exit;
   }

   mHGFileContentsList.clear();

   for (i = 0; i < nFiles; i++) {
      utf::string fileName;
      utf::string filePathName;
      VmTimeType createTime = -1;
      VmTimeType accessTime = -1;
      VmTimeType writeTime = -1;
      VmTimeType attrChangeTime = -1;

      if (!fileItem[i].cpName.cpName_val ||
          0 == fileItem[i].cpName.cpName_len) {
         g_debug("%s: invalid fileItem[%" FMTSZ "u].cpName.\n", __FUNCTION__, i);
         goto exit;
      }

      /*
       * '\0' is used as directory separator in cross-platform name. Now turn
       * '\0' in data into DIRSEPC.
       *
       * Note that we don't convert the final '\0' into DIRSEPC so the string
       * is NUL terminated.
       */
      CPNameUtil_CharReplace(fileItem[i].cpName.cpName_val,
                             fileItem[i].cpName.cpName_len - 1,
                             '\0',
                             DIRSEPC);
      fileName = fileItem[i].cpName.cpName_val;
      filePathName = tempDir;
      filePathName += DIRSEPS + fileName;

      if (fileItem[i].validFlags & CP_FILE_VALID_TYPE &&
          CP_FILE_TYPE_DIRECTORY == fileItem[i].type) {
         if (!File_CreateDirectory(filePathName.c_str())) {
            goto exit;
         }
         g_debug("%s: created directory [%s].\n",
                   __FUNCTION__, filePathName.c_str());
      } else if (fileItem[i].validFlags & CP_FILE_VALID_TYPE &&
                 CP_FILE_TYPE_REGULAR == fileItem[i].type) {
         FileIODescriptor file;
         FileIOResult fileErr;

         FileIO_Invalidate(&file);

         fileErr = FileIO_Open(&file,
                               filePathName.c_str(),
                               FILEIO_ACCESS_WRITE,
                               FILEIO_OPEN_CREATE_EMPTY);
         if (!FileIO_IsSuccess(fileErr)) {
            goto exit;
         }

         fileErr = FileIO_Write(&file,
                                fileItem[i].content.content_val,
                                fileItem[i].content.content_len,
                                NULL);

         FileIO_Close(&file);
         g_debug("%s: created file [%s].\n", __FUNCTION__, filePathName.c_str());
      } else {
         /*
          * Right now only Windows can provide CPFORMAT_FILECONTENTS data.
          * Symlink file is not expected. Continue with next file if the
          * type is not valid.
          */
         continue;
      }

      /* Update file time attributes. */
      createTime = fileItem->validFlags & CP_FILE_VALID_CREATE_TIME ?
         fileItem->createTime: -1;
      accessTime = fileItem->validFlags & CP_FILE_VALID_ACCESS_TIME ?
         fileItem->accessTime: -1;
      writeTime = fileItem->validFlags & CP_FILE_VALID_WRITE_TIME ?
         fileItem->writeTime: -1;
      attrChangeTime = fileItem->validFlags & CP_FILE_VALID_CHANGE_TIME ?
         fileItem->attrChangeTime: -1;

      if (!File_SetTimes(filePathName.c_str(),
                         createTime,
                         accessTime,
                         writeTime,
                         attrChangeTime)) {
         /* Not a critical error, only log it. */
         g_debug("%s: File_SetTimes failed with file [%s].\n",
               __FUNCTION__, filePathName.c_str());
      }

      /* Update file permission attributes. */
      if (fileItem->validFlags & CP_FILE_VALID_PERMS) {
         if (Posix_Chmod(filePathName.c_str(),
                         fileItem->permissions) < 0) {
            /* Not a critical error, only log it. */
            g_debug("%s: Posix_Chmod failed with file [%s].\n",
                  __FUNCTION__, filePathName.c_str());
         }
      }

      /*
       * If there is no DIRSEPC inside the fileName, this file/directory is a
       * top level one. We only put top level name into uri list.
       */
      if (fileName.find(DIRSEPS, 0) == utf::string::npos) {
         mHGFileContentsList.push_back(filePathName);
      }
   }
   g_debug("%s: created uri list\n", __FUNCTION__);
   ret = true;

exit:
   xdr_free((xdrproc_t)xdr_CPFileContents, (char *)&fileContents);
   if (tempDir && !ret) {
      DnD_DeleteStagingFiles(tempDir, FALSE);
   }
   free(tempDir);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::GetLocalFilesDone --
 *
 *    Callback when CopyPasteUIX11::GetLocalFiles is done, which finishes the file
 *    copying from host to guest staging directory. This function notifies
 *    the Copy/Paste data object and end its waiting state in order to continue
 *    the file copying from local staging directory to local target directory.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::GetLocalFilesDone(bool success)
{
   g_debug("%s: enter success %d\n", __FUNCTION__, success);

   if (mBlockAdded) {
      g_debug("%s: removing block for %s\n", __FUNCTION__, mHGStagingDir.c_str());
      /* We need to make sure block subsystem has not been shut off. */
      mBlockAdded = false;
      if (DnD_BlockIsReady(mBlockCtrl)) {
         mBlockCtrl->RemoveBlock(mBlockCtrl->fd, mHGStagingDir.c_str());
      }
   }

   mHGGetFileStatus = DND_FILE_TRANSFER_FINISHED;
   if (success) {
      /*
       * Mark current staging dir to be deleted on next reboot for FCP. The
       * file will not be deleted after reboot if it is moved to another
       * location by target application.
       */
      DnD_DeleteStagingFiles(mHGStagingDir.c_str(), TRUE);
   } else {
      /* Copied files are already removed in common layer. */
      mHGStagingDir.clear();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::SendClipNotChanged --
 *
 *    Send a not-changed clip to host.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::SendClipNotChanged(void)
{
   CPClipboard clip;

   g_debug("%s: enter.\n", __FUNCTION__);
   CPClipboard_Init(&clip);
   CPClipboard_SetChanged(&clip, FALSE);
   mCP->DestUISendClip(&clip);
   CPClipboard_Destroy(&clip);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::Reset --
 *
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::Reset(void)
{
   TRACE_CALL();
   /* Cancel any pending file transfer. */
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::FileBlockMonitorThread --
 *
 *    This thread monitors the access to the files in the clipboard which owns
 *    by VMTools by using the notification mechanism of VMBlock.
 *    If any access to the file is detected, then this thread requests the file
 *    transfer from host to guest.
 *
 * Results:
 *    Always return NULL
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void*
CopyPasteUIX11::FileBlockMonitorThread(void *arg)   // IN
{
   TRACE_CALL();
   ThreadParams *params = (ThreadParams *)arg;
   pthread_mutex_lock(&params->fileBlockMutex);
   while (true) {
      g_debug("%s: waiting signal\n", __FUNCTION__);
      pthread_cond_wait(&params->fileBlockCond, &params->fileBlockMutex);
      g_debug("%s: received signal. Exit:%d\n",
              __FUNCTION__,
              params->fileBlockCondExit);
      if (params->fileBlockCondExit) {
         break;
      }
      if (params->fileBlockName.bytes() == 0) {
        continue;
      }

      int fd = open(params->fileBlockName.c_str(), O_RDONLY);
      if (fd < 0) {
         g_debug("%s: Failed to open %s, errno is %d\n",
                 __FUNCTION__,
                 params->fileBlockName.c_str(),
                 errno);
         continue;
      }

      char buf[sizeof(VMBLOCK_FUSE_READ_RESPONSE)];
      ssize_t size;
      size = read(fd, buf, sizeof(VMBLOCK_FUSE_READ_RESPONSE));
      g_debug("%s: Number of bytes read : %" FMTSZ "u\n", __FUNCTION__, size);
      /*
       * The current thread will block in read function until
       * any other application accesses the file params->fileBlockName
       * or the block on the file params->fileBlockName is removed.
       * Currently we don't need to check the response in buf, so
       * just ignore it.
       */

      if (params->cp->IsBlockAdded()) {
         g_debug("%s: Request files\n", __FUNCTION__);
         params->cp->RequestFiles();
      } else {
         g_debug("%s: Block is not added\n", __FUNCTION__);
      }

      if (close(fd) < 0) {
         g_debug("%s: Failed to close %s, errno is %d\n",
                 __FUNCTION__,
                 params->fileBlockName.c_str(),
                 errno);
      }
   }
   pthread_mutex_unlock(&params->fileBlockMutex);
   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CopyPasteUIX11::TerminateThread --
 *
 *    This is called when the monitor thread is asked to exit.
 *    Sets the global state and signals the monitor thread to exit and wait
 *    for the monitor thread to exit.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
CopyPasteUIX11::TerminateThread()
{
   TRACE_CALL();
   if (mThread == 0) {
      return;
   }

   pthread_mutex_lock(&mThreadParams.fileBlockMutex);
   mThreadParams.fileBlockCondExit = true;
   pthread_cond_signal(&mThreadParams.fileBlockCond);
   pthread_mutex_unlock(&mThreadParams.fileBlockMutex);

   pthread_join(mThread, NULL);

   mThread = 0;
}
 07070100000304000081A400000000000000000000000168225505000011CB000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/copyPasteUIX11.h    /*********************************************************
 * Copyright (c) 2009-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file copyPasteUIX11.h
 *
 * This class implements the methods that allows Copy/Paste
 * between host and guest using version 3+ of the protocol.
 *
 */

#ifndef __COPYPASTE_UI_X11_H__
#define __COPYPASTE_UI_X11_H__

#include "stringxx/string.hh"
#include "dnd.h"
#include "str.h"
#include "dynbuf.h"

extern "C" {
#include "debug.h"
#include "dndClipboard.h"
#include "../dnd/dndFileContentsUtil.h"
#include "cpNameUtil.h"
#include "vmware/tools/guestrpc.h"
}

#include "dynxdr.h"
#include "posix.h"
#include "unicodeOperations.h"
#include "guestCopyPaste.hh"

/*
 * Make sure exception types are public and therefore shared between libg*mm
 * and this plugin. 
 *
 * See
 * http://gcc.gnu.org/wiki/Visibility#Problems_with_C.2B-.2B-_exceptions_.28please_read.21.29
 */
#pragma GCC visibility push(default)
#include <gtkmm.h>
#pragma GCC visibility pop

#include <list>
#include <vector>

#include <X11/Xlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include "vmware/guestrpc/tclodefs.h"

class CopyPasteUIX11;

struct ThreadParams
{
   pthread_mutex_t fileBlockMutex;
   pthread_cond_t fileBlockCond;
   bool fileBlockCondExit;
   CopyPasteUIX11 *cp;
   utf::string fileBlockName;
};

class CopyPasteUIX11 : public sigc::trackable
{
public:
   CopyPasteUIX11();
   virtual ~CopyPasteUIX11();
   bool Init();
   void VmxCopyPasteVersionChanged(RpcChannel *chan,
                                   uint32 version);
   void SetCopyPasteAllowed(bool isCopyPasteAllowed)
   { mCP->SetCopyPasteAllowed(isCopyPasteAllowed); }
   void Reset(void);
   void SetBlockControl(DnDBlockControl *blockCtrl)
      { Debug("Setting mBlockCtrl to %p\n", blockCtrl);
        mBlockCtrl = blockCtrl; }
   bool IsBlockAdded() const
   { return mBlockAdded; }
   void RequestFiles()
   { mCP->SrcUIRequestFiles(); }

private:

   /* hg */
   void GetRemoteClipboardCB(const CPClipboard *clip);
   void RemoteGetFilesDone(void);
   void LocalGetFileRequestCB(Gtk::SelectionData& selection_data, guint info);
   void LocalGetTextOrRTFRequestCB(Gtk::SelectionData& sd, guint info);
   void LocalGetSelectionFileList(const Gtk::SelectionData& sd);
   void LocalGetFileContentsRequestCB(Gtk::SelectionData& sd, guint info);
   void LocalClearClipboardCB(void);

   /* gh */
   void GetLocalClipboard(void);
   void LocalClipboardTimestampCB(const Gtk::SelectionData& sd);
   void LocalPrimTimestampCB(const Gtk::SelectionData& sd);
   void LocalReceivedFileListCB(const Gtk::SelectionData& selection_data);
   void GetLocalFilesDone(bool success);
   void SendClipNotChanged(void);

   /* Conversion methods. */
   utf::utf8string GetNextPath(utf::utf8string &str, size_t& index);
   utf::string GetLastDirName(const utf::string &str);
   bool LocalPrepareFileContents(const CPClipboard *clip);

   VmTimeType GetCurrentTime(void);

   static void* FileBlockMonitorThread(void *arg);
   void TerminateThread();

   // Member variables
   GuestCopyPasteMgr *mCP;
   bool mClipboardEmpty;
   utf::string mHGStagingDir;
   std::vector<Gtk::TargetEntry> mListTargets;
   bool mIsClipboardOwner;
   uint64 mClipTime;
   uint64 mPrimTime;
   uint64 mLastTimestamp;
   GdkAtom mGHSelection;
   CPClipboard mClipboard;
   ThreadParams mThreadParams;
   pthread_t mThread;

   /* File vars. */
   VmTimeType mHGGetListTime;
   utf::string mHGCopiedUriList;
   utf::utf8string mHGFCPData;
   utf::string mHGTextData;
   std::string mHGRTFData;
   std::vector<utf::string> mHGFileContentsList;
   DND_FILE_TRANSFER_STATUS mHGGetFileStatus;
   bool mBlockAdded;
   DnDBlockControl *mBlockCtrl;
   bool mInited;
   uint64 mTotalFileSize;
   bool mGetTimestampOnly;
};

#endif // __COPYPASTE_UI_X11_H__
 07070100000305000081A400000000000000000000000168225505000009C6000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/cpFileContents.x    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * cpFileContents.x --
 *
 *    Definition of the data structures used in the GuestRpc commands to
 *    provide information about file contents for cross platform clipboard.
 */

/*
 * Enumerates the different versions of the messages.
 */
enum CPFileContentsVersion {
   CP_FILE_CONTENTS_V1 = 1
};

struct CPFileItem {
   uint64 validFlags;     /* Valid members. */
   uint32 type;           /* File type */
   uint64 size;           /* File size (in bytes) */
   uint64 createTime;     /* 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 */
   uint64 permissions;    /* Permissions bits */
   uint64 attributes;     /* File attributes. */
   opaque cpName<>;       /* File name in cross-platform format. */
   opaque content<>;      /* File contents. */
};

struct CPFileContentsList {
   uint64 totalFileSize;
   struct CPFileItem fileItem<>;
};

/*
 * This defines the protocol for cross-platform file contents format. The union
 * allows us to create new versions of the protocol later by creating new values
 * in the CPFileContentsVersion enumeration, without having to change much of
 * the code calling the (de)serialization functions.
 *
 * Since the union doesn't have a default case, de-serialization will fail
 * if an unknown version is provided on the wire.
 */
union CPFileContents switch (CPFileContentsVersion ver) {
case CP_FILE_CONTENTS_V1:
   struct CPFileContentsList *fileContentsV1;
};

  07070100000306000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd 07070100000307000081A40000000000000000000000016822550500000A64000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/copyPasteRpc.hh /*********************************************************
 * Copyright (C) 2007-2017,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @copyPasteRpc.hh --
 *
 * Rpc layer object for CopyPaste.
 */

#ifndef COPY_PASTE_RPC_HH
#define COPY_PASTE_RPC_HH

#include <sigc++/sigc++.h>
#include <sigc++2to3.h>
#include "dndCPLibExport.hh"
#include "rpcBase.h"

#include "dnd.h"

class LIB_EXPORT CopyPasteRpc
   : public RpcBase
{
public:
   virtual ~CopyPasteRpc(void) {};

   /* sigc signals for CopyPaste source callback. */
   sigc::signal<void, uint32, bool, const CPClipboard*> srcRecvClipChanged;
   sigc::signal<void, uint32, const uint8 *, uint32> requestFilesChanged;
   sigc::signal<void, uint32, bool, const uint8 *, uint32> getFilesDoneChanged;

   /* sigc signal for CopyPaste destination callback. */
   sigc::signal<void, uint32, bool> destRequestClipChanged;

   /* sigc signal for ping reply callback. */
   sigc::signal<void, uint32> pingReplyChanged;

   /* sigc signal for rpc command reply received. */
   sigc::signal<void, uint32, uint32> cmdReplyChanged;

   virtual void Init(void) = 0;
   virtual void SendPing(uint32 caps) = 0;

   /* CopyPaste Rpc functions. */
   virtual bool SrcRequestClip(uint32 sessionId,
                               bool isActive) = 0;
   virtual bool DestSendClip(uint32 sessionId,
                             bool isActive,
                             const CPClipboard* clip) = 0;
   virtual bool RequestFiles(uint32 sessionId,
                             const uint8 *stagingDirCP,
                             uint32 sz) = 0;
   virtual bool SendFilesDone(uint32 sessionId,
                              bool success,
                              const uint8 *stagingDirCP,
                              uint32 sz) = 0;
   virtual bool GetFilesDone(uint32 sessionId,
                             bool success) = 0;
};

#endif // COPY_PASTE_RPC_HH
07070100000308000081A40000000000000000000000016822550500000A79000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/copyPasteRpcV4.hh   /*********************************************************
 * Copyright (C) 2010-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @copyPasteRpcV4.hh --
 *
 * Rpc layer object for CopyPaste version 4.
 */

#ifndef COPY_PASTE_RPC_V4_HH
#define COPY_PASTE_RPC_V4_HH

#include <sigc++/trackable.h>
#include "copyPasteRpc.hh"
#include "dndCPTransport.h"
#include "rpcV4Util.hpp"

#include "dnd.h"
#include "dndMsg.h"

extern "C" {
   #include "dndCPMsgV4.h"
}

class LIB_EXPORT CopyPasteRpcV4
   : public CopyPasteRpc,
     public sigc::trackable
{
public:
   CopyPasteRpcV4(DnDCPTransport *transport);
   virtual void Init(void);
   virtual void SendPing(uint32 caps);

   /* CopyPaste Rpc functions. */
   virtual bool SrcRequestClip(uint32 sessionId,
                               bool isActive);
   virtual bool DestSendClip(uint32 sessionId,
                             bool isActive,
                             const CPClipboard* clip);
   virtual bool RequestFiles(uint32 sessionId,
                             const uint8 *stagingDirCP,
                             uint32 sz);
   virtual bool SendFilesDone(uint32 sessionId,
                              bool success,
                              const uint8 *stagingDirCP,
                              uint32 sz);
   virtual bool GetFilesDone(uint32 sessionId,
                             bool success);
   virtual void HandleMsg(RpcParams *params,
                          const uint8 *binary,
                          uint32 binarySize);
   virtual bool SendPacket(uint32 destId,
                           const uint8 *packet,
                           size_t length);
   virtual void OnRecvPacket(uint32 srcId,
                             const uint8 *packet,
                             size_t packetSize);

private:
   DnDCPTransport *mTransport;
   TransportInterfaceType mTransportInterface;
   RpcV4Util mUtil;
};

#endif // COPY_PASTE_RPC_V4_HH
   07070100000309000081A40000000000000000000000016822550500002E76000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dnd.h   /*********************************************************
 * Copyright (C) 2005-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dnd.h --
 *
 *    Drag and Drop library
 *
 */

#ifndef _DND_H_
#define _DND_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#ifdef _WIN32
#   include <windows.h>
#   include <shellapi.h>
#endif

#include "vm_basic_types.h"
#include "unicodeTypes.h"
#include "dynarray.h"


#if defined(__cplusplus)
extern "C" {
#endif

/* Error value returned when data contains illegal characters */
#define DND_ILLEGAL_CHARACTERS  "data contains illegal characters"
/*
 * Use the same maximum path length as Hgfs.
 * XXX: Move HGFS_PATH_MAX to some header file which is more public
 *      and use it here.
 */
#define DND_MAX_PATH        6144

#define DNDMSG_HEADERSIZE_V3 ((3 * sizeof (uint32)) + (1 * sizeof (uint8)))
/*
 * Hard limits we never want to exceed. The maximum size of a serializied
 * DnDMsg. Close to 4M for Workstion/Fusion, 4G for Horzion.
 */
#ifdef VMX86_HORIZON_VIEW
#define DNDMSG_MAX_ARGSZ (0xffffffff - DNDMSG_HEADERSIZE_V3)
#else
#define DNDMSG_MAX_ARGSZ ((1 << 22) - DNDMSG_HEADERSIZE_V3)
#endif
/* The maximum number of arguments we can hold */
#define DNDMSG_MAX_ARGS 64

/* Linux only defines. Should be in separate dndLinux.h */
/* Strings used for formatting various types of data */
#define DND_URI_LIST_PRE     "file://"
#define DND_URI_LIST_PRE_KDE "file:"
#define DND_URI_NON_FILE_SCHEMES {"ssh", "sftp", "smb", "dav", "davs", "ftp",  NULL}
#define DND_URI_LIST_POST    "\r\n"
#define DND_TEXT_PLAIN_PRE   ""
#define DND_TEXT_PLAIN_POST  ""
#define DND_STRING_PRE       ""
#define DND_STRING_POST      ""
#define FCP_GNOME_LIST_PRE   "file://"
#define FCP_GNOME_LIST_POST  "\n"

/* FCP target used in gnome. */
#define FCP_TARGET_NAME_GNOME_COPIED_FILES   "x-special/gnome-copied-files"
#define FCP_TARGET_INFO_GNOME_COPIED_FILES   0
/* FCP target used in KDE. */
#define FCP_TARGET_NAME_URI_LIST             "text/uri-list"
#define FCP_TARGET_INFO_URI_LIST             1
/* FCP target used for nautilus 3.30 or later. */
#define FCP_TARGET_NAME_NAUTILUS_FILES       "UTF8_STRING"
#define FCP_TARGET_MIME_NAUTILUS_FILES       "x-special/nautilus-clipboard"
#define FCP_TARGET_INFO_NAUTILUS_FILES       2
/* Number of FCP targets. */
#define NR_FCP_TARGETS                       3

#define VMWARE_TARGET                        "vmware-target"

#define FCP_COPY_DELAY                       1000000  // 1 second
#define TARGET_NAME_TIMESTAMP                "TIMESTAMP"
#define TARGET_NAME_STRING                   "STRING"
#define TARGET_NAME_TEXT_PLAIN               "text/plain"
#define TARGET_NAME_UTF8_STRING              "UTF8_STRING"
#define TARGET_NAME_COMPOUND_TEXT            "COMPOUND_TEXT"
#define TARGET_NAME_APPLICATION_RTF          "application/rtf"
#define TARGET_NAME_TEXT_RICHTEXT            "text/richtext"
#define TARGET_NAME_TEXT_RTF                 "text/rtf"

#define DRAG_TARGET_NAME_URI_LIST  "text/uri-list"
#define DRAG_LEAVE_TIMEOUT         500

/* Guest detection window width and height. */
#define DRAG_DET_WINDOW_WIDTH 31

/* Clipboard image size limit. */
#define CLIPBOARD_IMAGE_MAX_WIDTH  4000
#define CLIPBOARD_IMAGE_MAX_HEIGHT 4000

typedef enum
{
   CPFORMAT_UNKNOWN = 0,
   CPFORMAT_TEXT,       /* NUL terminated UTF-8. */
   CPFORMAT_FILELIST,
   CPFORMAT_RTF,
   CPFORMAT_FILELIST_URI,
   CPFORMAT_FILECONTENTS,
   CPFORMAT_IMG_PNG,
   CPFORMAT_FILEATTRIBUTES,
   CPFORMAT_BIFF12,
   CPFORMAT_ART_GVML_CLIPFORMAT,
   CPFORMAT_HTML_FORMAT,
   CPFORMAT_MAX,
} DND_CPFORMAT;

enum DND_DROPEFFECT
{
   DROP_UNKNOWN = 1<<31,
   DROP_NONE = 0,
   DROP_COPY = 1<<0,
   DROP_MOVE = 1<<1,
   DROP_LINK = 1<<2,
};

/* Clipboard item. */
typedef struct CPClipItem {
   void *buf;
   uint32 size;
   Bool exists;
} CPClipItem;

/*
 * Cross platform clipboard. The native UI will convert host clipboard content
 * into cross platform clipboards.
 */
typedef struct {
   Bool changed;
   Bool isInitialized;
   uint32 maxSize;
   CPClipItem items[CPFORMAT_MAX - 1];
} CPClipboard;

#if !defined(SWIG)

typedef enum {
   DND_FILE_TRANSFER_NOT_STARTED = 0,
   DND_FILE_TRANSFER_IN_PROGRESS,
   DND_FILE_TRANSFER_FINISHED,
} DND_FILE_TRANSFER_STATUS;

/*
 * Comment out the following for SWIG. We don't currently need to use any of
 * these data structures or call any of these functions from test scripts, and
 * we would have to link in extra libraries if so. Only DnD V3 transport layer
 * will call these functions. At some later time, may want to refactor this
 * file to separate CPClipboard definitions from all these transport-related
 * stuff (it is just the CPClipboard code that test scripts need).
 */

/* Definitions for transport layer big buffer support (>= V3). */
typedef enum
{
   DND_TRANSPORT_PACKET_TYPE_UNKNOWN = 0,
   DND_TRANSPORT_PACKET_TYPE_SINGLE,
   DND_TRANSPORT_PACKET_TYPE_REQUEST,
   DND_TRANSPORT_PACKET_TYPE_PAYLOAD,
} DND_TRANSPORT_PACKET_TYPE;

#pragma pack(push, 1)
typedef struct DnDTransportPacketHeader {
   uint32 type;
   uint32 seqNum;
   uint32 totalSize;
   uint32 payloadSize;
   uint32 offset;
   uint8 payload[1];
} DnDTransportPacketHeader;
#pragma pack(pop)

typedef struct DnDTransportBuffer {
   size_t seqNum;
   uint8 *buffer;
   size_t totalSize;
   size_t offset;
   VmTimeType lastUpdateTime;
} DnDTransportBuffer;

#define DND_TRANSPORT_PACKET_HEADER_SIZE      (5 * sizeof(uint32))
#ifdef VMX86_HORIZON_VIEW
/*
 * For Horizon DnD, expand the message size to almost 16M, which provides
 * better DnD Performance on text/rich text/image etc. dragging and dropping
 * per current performance tuning.
 */
#define DND_MAX_TRANSPORT_PACKET_SIZE         ((1 << 24) - 100)
#else
/* Close to 64k (maximum guestRpc message size). Leave some space for guestRpc header. */
#define DND_MAX_TRANSPORT_PACKET_SIZE         ((1 << 16) - 100)
#endif

#define DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE (DND_MAX_TRANSPORT_PACKET_SIZE - \
                                               DND_TRANSPORT_PACKET_HEADER_SIZE)
#define DND_MAX_TRANSPORT_LATENCY_TIME        3 * 1000000 /* 3 seconds. */

/*
 * Structure to access methods of currently used blocking mechanism.
 */
typedef struct DnDBlockControl {
   int fd;
   const char *blockRoot;
   Bool (*AddBlock)(int blockFd, const char *blockPath);
   Bool (*RemoveBlock)(int blockFd, const char *blockedPath);
} DnDBlockControl;

#ifdef _WIN32
#ifdef METRO
DECLARE_HANDLE(HDROP);
#endif
/*
 * Windows-specific functions
 */
char *DnD_GetClipboardFormatName(UINT cf);
HGLOBAL DnD_CopyStringToGlobal(const char *str);
HGLOBAL DnD_CopyDWORDToGlobal(DWORD *pDWORD);
HGLOBAL DnD_CreateHDrop(const char *path, const char *fileList);
HGLOBAL DnD_CreateHDropForGuest(const char *path,
                                const char *fileList);
size_t DnD_CPStringToLocalString(const char *bufIn,
                                 utf16_t **bufOut);
size_t DnD_LocalStringToCPString(utf16_t *bufIn,
                                 char **bufOut);
Bool DnD_SetCPClipboardFromLocalText(CPClipboard *clip,
                                     utf16_t *bufIn);
Bool DnD_SetCPClipboardAndTruncateLocalText(CPClipboard *clip,
                                            utf16_t *bufIn);
Bool DnD_SetCPClipboardFromLocalRtf(CPClipboard *clip,
                                    char *bufIn);
Bool DnD_SetCPClipboardFromSpecifiedFormat(CPClipboard *clip,
                                           const DND_CPFORMAT fmt,
                                           char *bufIn,
                                           unsigned int len);
Bool DnD_SetCPClipboardFromBMPInfo(CPClipboard *clip,
                                   const LPBITMAPINFOHEADER bmi,
                                   DND_CPFORMAT fmt);
Bool DnD_SetCPClipboardFromHBITMAP(CPClipboard *clip,
                                   HBITMAP hBitmap,
                                   DND_CPFORMAT fmt);
Bool DnD_PNGToLocalFormat(const unsigned char *pngData,
                          unsigned int pngDataLen,
                          int pngReadFlags,
                          DynBuf *bmpData,
                          HBITMAP *hBitmap);
Bool DnD_FakeMouseEvent(DWORD flag);
Bool DnD_FakeMouseState(DWORD key, Bool isDown);
Bool DnD_FakeEscapeKey(void);
Bool DnD_DeleteLocalDirectory(const char *localDir);
Bool DnD_SetClipboard(UINT format, char *buffer, int len);
Bool DnD_GetFileList(HDROP hDrop,
                     char **remoteFiles,
                     int *remoteLength,
                     char **localFiles,
                     int *localLength,
                     uint64 *totalSize);

#else
/*
 * Posix-specific functions
 */

char *DnD_UriListGetNextFile(char const *uriList,
                             size_t *index,
                             size_t *length);
Bool DnD_UriIsNonFileSchemes(char const *uri);
#endif

/*
 * Shared functions
 */
const char *DnD_GetFileRoot(void);
char *DnD_CreateStagingDirectory(void);
char *DnD_AppendPrefixToStagingDir(const char *oldName, const char *newName);
Bool DnD_DeleteStagingFiles(const char *stagingDir, Bool onReboot);
Bool DnD_RemoveTempDirs(const char *dndTempDir, const char *prefix);
int DnD_LegacyConvertToCPName(const char *nameIn,
                              size_t bufOutSize,
                              char *bufOut);
Bool DnD_CPNameListToDynBufArray(char *fileList,
                                 size_t listSize,
                                 DynBufArray *dynBufArray);
char *DnD_GetLastDirName(const char *str);

void DnD_SetCPClipboardAndTruncateText(CPClipboard *clip,
                                       char *destBuf,
                                       size_t len);

/* vmblock support functions. */
Bool DnD_InitializeBlocking(DnDBlockControl *blkCtrl);
Bool DnD_UninitializeBlocking(DnDBlockControl *blkCtrl);
Bool DnD_CompleteBlockInitialization(int fd, DnDBlockControl *blkCtrl);

static INLINE Bool
DnD_BlockIsReady(DnDBlockControl *blkCtrl)   // IN: blocking control structure
{
   if (blkCtrl->fd >= 0) {
      ASSERT(blkCtrl->AddBlock && blkCtrl->RemoveBlock);
      return TRUE;
   }
   return FALSE;
}

/* Transport layer big buffer support functions. */
void DnD_TransportBufInit(DnDTransportBuffer *buf,
                          uint8 *msg,
                          size_t msgSize,
                          uint32 seqNum);
void DnD_TransportBufReset(DnDTransportBuffer *buf);
size_t DnD_TransportBufGetPacket(DnDTransportBuffer *buf,
                                 DnDTransportPacketHeader **packet);
Bool DnD_TransportBufAppendPacket(DnDTransportBuffer *buf,
                                  DnDTransportPacketHeader *packet,
                                  size_t packetSize);
size_t DnD_TransportMsgToPacket(uint8 *msg,
                                size_t msgSize,
                                uint32 seqNum,
                                DnDTransportPacketHeader **packet);
size_t DnD_TransportReqPacket(DnDTransportBuffer *buf,
                              DnDTransportPacketHeader **packet);
#endif // !SWIG

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _DND_H_
  0707010000030A000081A400000000000000000000000168225505000004A4000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndCPLibExport.hh   /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @dndCPLibExport.hh --
 *
 * LibExport definition.
 */

#ifndef DND_CP_LIB_EXPORT_HH
#define DND_CP_LIB_EXPORT_HH

#if defined VMX86_TOOLS || COMPILE_WITHOUT_CUI
#   ifdef LIB_EXPORT
#   undef LIB_EXPORT
#   endif
#define LIB_EXPORT
#else
#include "libExport.hh"
#endif

#endif // DND_CP_LIB_EXPORT_HH
0707010000030B000081A4000000000000000000000001682255050000302F000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndCPMsgV4.c    /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @dndCPMsgV4.c --
 *
 * Helper functions for DnDCPMsgV4.
 */

#include "vm_assert.h"
#include "dnd.h"
#include "dndClipboard.h"
#include "dndCPMsgV4.h"
#include "util.h"


/**
 * Check if received packet is valid or not.
 *
 * @param[in] packet
 * @param[in] packetSize
 *
 * @return TRUE if the packet is valid, FALSE otherwise.
 */

static Bool
DnDCPMsgV4IsPacketValid(const uint8 *packet,
                        size_t packetSize)
{
   DnDCPMsgHdrV4 *msgHdr = NULL;
   ASSERT(packet);

   if (packetSize < DND_CP_MSG_HEADERSIZE_V4 ||
       packetSize > DND_MAX_TRANSPORT_PACKET_SIZE) {
      return FALSE;
   }

   msgHdr = (DnDCPMsgHdrV4 *)packet;

   /* Payload size is not valid. */
   if (msgHdr->payloadSize > DND_CP_PACKET_MAX_PAYLOAD_SIZE_V4) {
      return FALSE;
   }

   /* Payload size plus header size should be equal to packet size. */
   if (msgHdr->payloadSize + DND_CP_MSG_HEADERSIZE_V4 != packetSize) {
      return FALSE;
   }

   /* Binary size is not valid. */
   if (msgHdr->binarySize > DND_CP_MSG_MAX_BINARY_SIZE_V4) {
      return FALSE;
   }

   /*
    * For Workstation/Fusion Payload should be smaller than the whole binary
    * and should not be put beyond the binary tail.
    *
    * binarySize should be smaller than DND_CP_MSG_MAX_BINARY_SIZE_V4, so that
    * integer overflow is not possible since DND_CP_MSG_MAX_BINARY_SIZE_V4 * 2
    * is guaranteed to be less than MAX_UINT32. Horizon removes this limitation
    */
#ifndef VMX86_HORIZON_VIEW
   ASSERT_ON_COMPILE(DND_CP_MSG_MAX_BINARY_SIZE_V4 <= MAX_UINT32 / 2);
#endif
   if (msgHdr->payloadOffset > msgHdr->binarySize ||
       msgHdr->payloadSize > msgHdr->binarySize ||
       msgHdr->payloadOffset + msgHdr->payloadSize > msgHdr->binarySize) {
      return FALSE;
   }

   return TRUE;
}


/**
 * Initialize the DnDCPMsgV4.
 *
 * @param[in/out] msg DnDCPMsgV4 to be initialized.
 */

void
DnDCPMsgV4_Init(DnDCPMsgV4 *msg)
{
   ASSERT(msg);
   memset(msg, 0, sizeof *msg);
}

/**
 * Destroy the DnDCPMsgV4.
 *
 * @param[in/out] msg DnDCPMsgV4 to be destroyed.
 */

void
DnDCPMsgV4_Destroy(DnDCPMsgV4 *msg)
{
   if (msg) {
      free(msg->binary);
      DnDCPMsgV4_Init(msg);
   }
}


/**
 * Check the packet type.
 *
 * @param[in] packet
 * @param[in] packetSize
 * @param[in] allowed max packet Size
 *
 * @return DnDCPMsgPacketType
 */

DnDCPMsgPacketType DnDCPMsgV4_GetPacketType(const uint8 *packet,
                                            size_t packetSize,
                                            const uint32 maxPacketPayloadSize)
{
   DnDCPMsgHdrV4 *msgHdr = NULL;
   ASSERT(packet);

   if (!DnDCPMsgV4IsPacketValid(packet, packetSize)) {
      return DND_CP_MSG_PACKET_TYPE_INVALID;
   }

   msgHdr = (DnDCPMsgHdrV4 *)packet;
   if (msgHdr->binarySize <= maxPacketPayloadSize) {
      return DND_CP_MSG_PACKET_TYPE_SINGLE;
   }

   if (0 == msgHdr->payloadOffset) {
      return DND_CP_MSG_PACKET_TYPE_MULTIPLE_NEW;
   }

   if (msgHdr->payloadOffset + msgHdr->payloadSize == msgHdr->binarySize) {
      return DND_CP_MSG_PACKET_TYPE_MULTIPLE_END;
   }

   return DND_CP_MSG_PACKET_TYPE_MULTIPLE_CONTINUE;
}


/**
 * Serialize the msg to packet.
 *
 * @param[in/out] msg DnDCPMsgV4 to be serialized from.
 * @param[out] packet DnDCPMsgV4 to be serialized to.
 * @param[out] packetSize serialized packet size.
 *
 * @return TRUE if succeed, FALSE otherwise.
 */

Bool
DnDCPMsgV4_Serialize(DnDCPMsgV4 *msg,
                     uint8 **packet,
                     size_t *packetSize)
{
   return DnDCPMsgV4_SerializeWithInputPayloadSizeCheck(
      msg, packet, packetSize, DND_CP_PACKET_MAX_PAYLOAD_SIZE_V4);
}


Bool
DnDCPMsgV4_SerializeWithInputPayloadSizeCheck(DnDCPMsgV4 *msg,
                                              uint8 **packet,
                                              size_t *packetSize,
                                              const uint32 maxPacketPayloadSize)
{
   size_t payloadSize = 0;

   ASSERT(msg);
   ASSERT(packet);
   ASSERT(packetSize);
   ASSERT(msg->hdr.binarySize >= msg->hdr.payloadOffset);

   if (msg->hdr.binarySize <= maxPacketPayloadSize) {
      /*
       * One single packet is enough for the message. For short message, the
       * payloadOffset should always be 0.
       */
      ASSERT(msg->hdr.payloadOffset == 0);
      payloadSize = msg->hdr.binarySize;
   } else {
      /* For big message, payloadOffset means binary size we already sent out. */
      if (msg->hdr.binarySize - msg->hdr.payloadOffset > maxPacketPayloadSize) {
         payloadSize = maxPacketPayloadSize;
      } else {
         payloadSize = msg->hdr.binarySize - msg->hdr.payloadOffset;
      }
   }

   *packetSize = DND_CP_MSG_HEADERSIZE_V4 + payloadSize;
   *packet = Util_SafeMalloc(*packetSize);
   memcpy(*packet, msg, DND_CP_MSG_HEADERSIZE_V4);
   if (payloadSize > 0) {
      memcpy(*packet + DND_CP_MSG_HEADERSIZE_V4,
             msg->binary + msg->hdr.payloadOffset,
             payloadSize);
   }
   ((DnDCPMsgHdrV4 *)(*packet))->payloadSize = payloadSize;
   /* Next DnDCPMsgV4_Serialize will use this payloadOffset to get unsent binary. */
   msg->hdr.payloadOffset += payloadSize;
   return TRUE;
}


/**
 * Unserialize the packet to DnDCPMsgV4 for short messsage.
 *
 * @param[in/out] msg DnDCPMsgV4 to be unserialized to.
 * @param[in] packet DnDCPMsgV4 to be unserialized from.
 * @param[in] packetSize
 *
 * @return TRUE if succeed, FALSE otherwise.
 */

Bool
DnDCPMsgV4_UnserializeSingle(DnDCPMsgV4 *msg,
                             const uint8 *packet,
                             size_t packetSize)
{
   DnDCPMsgHdrV4 *msgHdr = NULL;
   ASSERT(msg);
   ASSERT(packet);

   if (!DnDCPMsgV4IsPacketValid(packet, packetSize)) {
      return FALSE;
   }

   msgHdr = (DnDCPMsgHdrV4 *)packet;

   /* Offset should be 0 for short message. */
   if (msgHdr->payloadOffset != 0) {
      return FALSE;
   }

   memcpy(msg, msgHdr, DND_CP_MSG_HEADERSIZE_V4);

   if (msg->hdr.binarySize != 0) {
      msg->binary = Util_SafeMalloc(msg->hdr.binarySize);

      memcpy(msg->binary,
             packet + DND_CP_MSG_HEADERSIZE_V4,
             msg->hdr.payloadSize);
      msg->hdr.payloadOffset = msg->hdr.payloadSize;
   }
   return TRUE;
}


/**
 * Unserialize the packet to DnDCPMsgV4 for big messsage.
 *
 * @param[in/out] msg DnDCPMsgV4 to be unserialized to.
 * @param[in] packet DnDCPMsgV4 to be unserialized from.
 * @param[in] packetSize
 *
 * @return TRUE if succeed, FALSE otherwise.
 */

Bool
DnDCPMsgV4_UnserializeMultiple(DnDCPMsgV4 *msg,
                               const uint8 *packet,
                               size_t packetSize)
{
   DnDCPMsgHdrV4 *msgHdr = NULL;
   ASSERT(msg);
   ASSERT(packet);

   if (!DnDCPMsgV4IsPacketValid(packet, packetSize)) {
      return FALSE;
   }

   msgHdr = (DnDCPMsgHdrV4 *)packet;

   /*
    * For each session, there is at most 1 big packet. If the received
    * sessionId is different with buffered one, the received packet is for
    * another another new packet. Destroy old buffered packet.
    */
   if (msg->hdr.sessionId != msgHdr->sessionId) {
      DnDCPMsgV4_Destroy(msg);
   }

   if (msg->binary == NULL) {
      /* New packet */

      /* Offset should be 0 for new packet. */
      if (msgHdr->payloadOffset != 0) {
         return FALSE;
      }
      msg->hdr = *msgHdr;
      msg->hdr.payloadSize = 0; // unused; initialize to zero for safety
      msg->binary = Util_SafeMalloc(msg->hdr.binarySize);
   } else {
      /* Existing buffered packet */

      /* Binary size should match.
       * DnDCPMsgV4IsPacketValid() can only validate packets individually.
       * For multiple packets in a session, it requires that all packets
       * have the same binarySize.
       *
       * We alloc buffer only when the first packet arrives, using the
       * binarySize from the first packet. The binarySize in the following
       * packets must be the same as the first packet.
       */
      if (msg->hdr.binarySize != msgHdr->binarySize) {
         return FALSE;
      }

      /* Payload offset must match expected offset after earlier payload */
      if (msg->hdr.payloadOffset != msgHdr->payloadOffset) {
         return FALSE;
      }
   }

   /* msg->hdr.payloadOffset is used as received binary size. */
   memcpy(msg->binary + msg->hdr.payloadOffset,
          packet + DND_CP_MSG_HEADERSIZE_V4,
          msgHdr->payloadSize);
   msg->hdr.payloadOffset += msgHdr->payloadSize;
   return TRUE;
}


/**
 * Map a command to a string.
 *
 * @param[in] cmd the DnD V4 command
 *
 * @return a valid command string if the command is valid, "invalid command"
 *         otherwise.
 */

const char *
DnDCPMsgV4_LookupCmd(uint32 cmd)
{
   static const struct {
      uint32 cmd;
      const char *cmdStr;
   } cmdStringTable[] = {
      { DNDCP_CMD_PING,                "DNDCP_CMD_PING" },
      { DNDCP_CMD_PING_REPLY,          "DNDCP_CMD_PING_REPLY" },
      { DNDCP_CMD_REQUEST_NEXT,        "DNDCP_CMD_REQUEST_NEXT" },
      { DNDCP_CMP_REPLY,               "DNDCP_CMP_REPLY" },
      { DNDCP_CMD_TEST_BIG_BINARY,     "DNDCP_CMD_TEST_BIG_BINARY" },
      { DNDCP_CMD_TEST_BIG_BINARY_REPLY, "DNDCP_CMD_TEST_BIG_BINARY_REPLY" },

      { DND_CMD_DEST_DRAG_ENTER,       "DND_CMD_DEST_DRAG_ENTER" },
      { DND_CMD_DEST_DRAG_ENTER_REPLY, "DND_CMD_DEST_DRAG_ENTER_REPLY" },
      { DND_CMD_DEST_SEND_CLIPBOARD,   "DND_CMD_DEST_SEND_CLIPBOARD" },
      { DND_CMD_DEST_DRAG_LEAVE,       "DND_CMD_DEST_DRAG_LEAVE" },
      { DND_CMD_DEST_DROP,             "DND_CMD_DEST_DROP" },
      { DND_CMD_SRC_DRAG_BEGIN,        "DND_CMD_SRC_DRAG_BEGIN" },
      { DND_CMD_SRC_DRAG_BEGIN_DONE,   "DND_CMD_SRC_DRAG_BEGIN_DONE" },
      { DND_CMD_SRC_DROP,              "DND_CMD_SRC_DROP" },
      { DND_CMD_SRC_DROP_DONE,         "DND_CMD_SRC_DROP_DONE" },
      { DND_CMD_SRC_CANCEL,            "DND_CMD_SRC_CANCEL" },
      { DND_CMD_PRIV_DRAG_ENTER,       "DND_CMD_PRIV_DRAG_ENTER" },
      { DND_CMD_PRIV_DRAG_LEAVE,       "DND_CMD_PRIV_DRAG_LEAVE" },
      { DND_CMD_PRIV_DROP,             "DND_CMD_PRIV_DROP" },
      { DND_CMD_MOVE_MOUSE,            "DND_CMD_MOVE_MOUSE" },
      { DND_CMD_UPDATE_FEEDBACK,       "DND_CMD_UPDATE_FEEDBACK" },
      { DND_CMD_REQUEST_FILES,         "DND_CMD_REQUEST_FILES" },
      { DND_CMD_GET_FILES_DONE,        "DND_CMD_GET_FILES_DONE" },
      { DND_CMD_SEND_FILES_DONE,       "DND_CMD_SEND_FILES_DONE" },
      { DND_CMD_QUERY_EXITING,         "DND_CMD_QUERY_EXITING" },
      { DND_CMD_DRAG_NOT_PENDING,      "DND_CMD_DRAG_NOT_PENDING" },
      { DND_CMD_UPDATE_UNITY_DET_WND,  "DND_CMD_UPDATE_UNITY_DET_WND" },

      { CP_CMD_REQUEST_CLIPBOARD,      "CP_CMD_REQUEST_CLIPBOARD" },
      { CP_CMD_REQUEST_FILES,          "CP_CMD_REQUEST_FILES" },
      { CP_CMD_RECV_CLIPBOARD,         "CP_CMD_RECV_CLIPBOARD" },
      { CP_CMD_SEND_CLIPBOARD,         "CP_CMD_SEND_CLIPBOARD" },
      { CP_CMD_GET_FILES_DONE,         "CP_CMD_GET_FILES_DONE" },
      { CP_CMD_SEND_FILES_DONE,        "CP_CMD_SEND_FILES_DONE" },

      { FT_CMD_HGFS_REQUEST,           "FT_CMD_HGFS_REQUEST" },
      { FT_CMD_HGFS_REPLY,             "FT_CMD_HGFS_REPLY" },
      { FT_CMD_UPDATE_PROGRESS,        "FT_CMD_UPDATE_PROGRESS" },
      { FT_CMD_PROGRESS_REPLY,         "FT_CMD_PROGRESS_REPLY" },
   };
   size_t i;

   for (i = 0; i < ARRAYSIZE(cmdStringTable); i++) {
      if (cmdStringTable[i].cmd == cmd) {
         return cmdStringTable[i].cmdStr;
      }
   }
   return "invalid command";
}

 0707010000030C000081A40000000000000000000000016822550500002354000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndCPMsgV4.h    /*********************************************************
 * Copyright (C) 2010-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @dndCPMsgV4.h --
 *
 * DnDCPMsgV4 represents version 4 rpc message/packet for DnD/Copy/Paste.
 * DnD/CP message/packet is used to pass command between 2 spots (between host
 * and controller, or between controller and guest). The message/packet is
 * cross-platform. In sender side, RPC layer should construct dnd/cp message,
 * serialize it to packet, and transport layer will send the packet to another
 * side. In receiver side, transport layer will dispatch the packet to right
 * RPC, and the RPC should unpack the packet into message and send up to common
 * state machine layer.
 */

#ifndef DND_CP_MSG_V4_H
#define DND_CP_MSG_V4_H

#define INCLUDE_ALLOW_USERLEVEL

#include "includeCheck.h"
#include "vm_basic_types.h"

/*
 * These commands will be used as cross-platform DnD/CopyPaste communication.
 * All common commands start from 0. DnD commands start from 1000. CopyPaste
 * commands start from 2000. FileTransfer commands start from 3000.
 */

/* Commands for all channels. */
typedef enum {
   DNDCP_CMD_INVALID = 0,
   /*
    * These 2 commands are used right after any communication channel is
    * established to exchange some initial information such as version,
    * capability.
    */
   DNDCP_CMD_PING,
   DNDCP_CMD_PING_REPLY,
   /* This command is used for big binary transfer. */
   DNDCP_CMD_REQUEST_NEXT,
   /*
    * This command is used for general reply for any command. The reply is
    * optional. Most commands do not need it.
    */
   DNDCP_CMP_REPLY,
   /* These 2 commands are used for testing big binary transport. */
   DNDCP_CMD_TEST_BIG_BINARY,
   DNDCP_CMD_TEST_BIG_BINARY_REPLY,
} DnDCPCmdV4;

/* DnD Commands. */
typedef enum {
   /* For DnD destination. */
   DND_CMD_DEST_DRAG_ENTER = 1000,
   DND_CMD_DEST_DRAG_ENTER_REPLY,
   DND_CMD_DEST_SEND_CLIPBOARD,
   DND_CMD_DEST_DRAG_LEAVE,
   DND_CMD_DEST_DROP,

   /* For DnD source. */
   DND_CMD_SRC_DRAG_BEGIN,
   DND_CMD_SRC_DRAG_BEGIN_DONE,
   DND_CMD_SRC_DROP,
   DND_CMD_SRC_DROP_DONE,
   DND_CMD_SRC_CANCEL,

   /* For private DnD. */
   DND_CMD_PRIV_DRAG_ENTER,
   DND_CMD_PRIV_DRAG_LEAVE,
   DND_CMD_PRIV_DROP,

   /* For some common DnD operation. */
   DND_CMD_MOVE_MOUSE,
   DND_CMD_UPDATE_FEEDBACK,
   DND_CMD_REQUEST_FILES,
   DND_CMD_GET_FILES_DONE,
   DND_CMD_SEND_FILES_DONE,
   DND_CMD_QUERY_EXITING,
   DND_CMD_DRAG_NOT_PENDING,
   DND_CMD_UPDATE_UNITY_DET_WND,
   DND_CMD_DEST_CANCEL
} DnDCmdV4;

/* Copy/Paste commands. */
typedef enum {
   CP_CMD_REQUEST_CLIPBOARD = 2000,
   CP_CMD_REQUEST_FILES,
   CP_CMD_RECV_CLIPBOARD,
   CP_CMD_SEND_CLIPBOARD,
   CP_CMD_GET_FILES_DONE,
   CP_CMD_SEND_FILES_DONE,
} CopyPasteCmdV4;

/* File transfer commands. */
typedef enum {
   FT_CMD_HGFS_REQUEST = 3000,
   FT_CMD_HGFS_REPLY,
   FT_CMD_UPDATE_PROGRESS,
   FT_CMD_PROGRESS_REPLY
} FileTransferCmdV4;

/* Message types. */
typedef enum DnDCPMsgType {
   DND_CP_MSG_TYPE_INVALID = 0,
   DND_CP_MSG_TYPE_DND,
   DND_CP_MSG_TYPE_CP,
   DND_CP_MSG_TYPE_FT
} DnDCPMsgType;

/* Message source. */
typedef enum DnDCPMsgSrc {
   DND_CP_MSG_SRC_INVALID = 0,
   DND_CP_MSG_SRC_HOST,
   DND_CP_MSG_SRC_CONTROLLER,
   DND_CP_MSG_SRC_GUEST
} DnDCPMsgSrc;

/* Command reply status. */
typedef enum DnDCPMsgStatus {
   DND_CP_MSG_STATUS_SUCCESS,
   DND_CP_MSG_STATUS_ERROR,
   DND_CP_MSG_STATUS_CANCEL,
   DND_CP_MSG_STATUS_BUSY,
   DND_CP_MSG_STATUS_ACCEPTED,
   DND_CP_MSG_STATUS_INVALID_PACKET,
   DND_CP_MSG_STATUS_INVALID_SESSION_ID,
   DND_CP_MSG_STATUS_INVALID_FORMAT,
} DnDCPMsgStatus;

/* Packet types. */
typedef enum DnDCPMsgPacketType {
   DND_CP_MSG_PACKET_TYPE_SINGLE,
   DND_CP_MSG_PACKET_TYPE_MULTIPLE_NEW,
   DND_CP_MSG_PACKET_TYPE_MULTIPLE_CONTINUE,
   DND_CP_MSG_PACKET_TYPE_MULTIPLE_END,
   DND_CP_MSG_PACKET_TYPE_INVALID
} DnDCPMsgPacketType;

/*
 * Definitions for DnD/CP capabilities. DND_CP_CAP_VALID is used to specify if
 * the capability itself is valid or not.
 */
#define DND_CP_CAP_VALID            (1 << 0)
#define DND_CP_CAP_DND              (1 << 1)
#define DND_CP_CAP_CP               (1 << 2)
#define DND_CP_CAP_PLAIN_TEXT_DND   (1 << 3)
#define DND_CP_CAP_PLAIN_TEXT_CP    (1 << 4)
#define DND_CP_CAP_RTF_DND          (1 << 5)
#define DND_CP_CAP_RTF_CP           (1 << 6)
#define DND_CP_CAP_IMAGE_DND        (1 << 7)
#define DND_CP_CAP_IMAGE_CP         (1 << 8)
#define DND_CP_CAP_FILE_DND         (1 << 9)
#define DND_CP_CAP_FILE_CP          (1 << 10)
#define DND_CP_CAP_FILE_CONTENT_DND (1 << 11)
#define DND_CP_CAP_FILE_CONTENT_CP  (1 << 12)
#define DND_CP_CAP_ACTIVE_CP        (1 << 13)
#define DND_CP_CAP_GUEST_PROGRESS   (1 << 14)
#define DND_CP_CAP_BIG_BUFFER       (1 << 15)

#define DND_CP_CAP_FORMATS_CP       (DND_CP_CAP_PLAIN_TEXT_CP   | \
                                     DND_CP_CAP_RTF_CP          | \
                                     DND_CP_CAP_IMAGE_CP        | \
                                     DND_CP_CAP_FILE_CP         | \
                                     DND_CP_CAP_FILE_CONTENT_CP)

#define DND_CP_CAP_FORMATS_DND      (DND_CP_CAP_PLAIN_TEXT_DND  | \
                                     DND_CP_CAP_RTF_DND         | \
                                     DND_CP_CAP_IMAGE_DND       | \
                                     DND_CP_CAP_FILE_DND        | \
                                     DND_CP_CAP_FILE_CONTENT_DND)

#define DND_CP_CAP_FORMATS_ALL      (DND_CP_CAP_FORMATS_CP      | \
                                     DND_CP_CAP_FORMATS_DND)

/*
 * Header definition for DnD version 4 packet. Any DnD version 4 packet has 2
 * parts: fixed header and payload. payload is optional.
 */
#pragma pack(push, 1)
typedef struct DnDCPMsgHdrV4 {
   uint32 cmd;             /* DnD/CP message command. */
   uint32 type;            /* DnD/CP message type. */
   uint32 src;             /* Message sender. */
   uint32 sessionId;       /* DnD/CP session ID. */
   uint32 status;          /* Status for last operation. */
   uint32 param1;          /* Optional parameter. Optional. */
   uint32 param2;          /* Optional parameter. Optional. */
   uint32 param3;          /* Optional parameter. Optional. */
   uint32 param4;          /* Optional parameter. Optional. */
   uint32 param5;          /* Optional parameter. Optional. */
   uint32 param6;          /* Optional parameter. Optional. */
   uint32 binarySize;      /* Binary size. */
   uint32 payloadOffset;   /* Payload offset. */
   uint32 payloadSize;     /* Payload size. */
} DnDCPMsgHdrV4;
#pragma pack(pop)

/* Some important definitions for DnDCPMsgV4. */
#define DND_CP_MSG_HEADERSIZE_V4 (sizeof (DnDCPMsgHdrV4))
#define DND_CP_PACKET_MAX_PAYLOAD_SIZE_V4 (DND_MAX_TRANSPORT_PACKET_SIZE - \
                                           DND_CP_MSG_HEADERSIZE_V4)
#ifdef VMX86_HORIZON_VIEW
/*
 * Horizon has no hard limit, but the size field is type of uint32,
 * 4G-1(0xffffffff) is the maximum size represented.
 */
#define DND_CP_MSG_MAX_BINARY_SIZE_V4 0xffffffff
#else
// Workstation/fusion have hard limit in size(4M) of DnD Msg, refer to dnd.h
#define DND_CP_MSG_MAX_BINARY_SIZE_V4 (1 << 22)
#endif

/* DnD version 4 message. */
typedef struct DnDCPMsgV4 {
   DnDCPMsgHdrV4 hdr;
   uint32 addrId;
   uint8 *binary;
} DnDCPMsgV4;

#if !defined(SWIG)
void DnDCPMsgV4_Init(DnDCPMsgV4 *msg);
void DnDCPMsgV4_Destroy(DnDCPMsgV4 *msg);
DnDCPMsgPacketType DnDCPMsgV4_GetPacketType(const uint8 *packet,
                                            size_t packetSize,
                                            const uint32 maxPacketPayloadSize);
Bool DnDCPMsgV4_Serialize(DnDCPMsgV4 *msg,
                          uint8 **packet,
                          size_t *packetSize);
Bool DnDCPMsgV4_SerializeWithInputPayloadSizeCheck(DnDCPMsgV4 *msg,
   uint8 **packet, size_t *packetSize, const uint32 maxPacketPayloadSize);
Bool DnDCPMsgV4_UnserializeSingle(DnDCPMsgV4 *msg,
                                  const uint8 *packet,
                                  size_t packetSize);
Bool DnDCPMsgV4_UnserializeMultiple(DnDCPMsgV4 *msg,
                                    const uint8 *packet,
                                    size_t packetSize);
const char *DnDCPMsgV4_LookupCmd(uint32 cmd);
#endif
#endif // DND_CP_MSG_V4_H
0707010000030D000081A400000000000000000000000168225505000008CF000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndCPTransport.h    /*********************************************************
 * Copyright (C) 2010-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndCPTransport.h --
 *
 *      DnDCPTransport provides a data transportation interface for both dnd and copyPaste.
 */

#ifndef DND_CP_TRANSPORT_H
#define DND_CP_TRANSPORT_H

#ifndef LIB_EXPORT
#define LIB_EXPORT
#endif

#include "vm_basic_types.h"

/* Some definitions for addressId. */
#define MAX_NUM_OF_CONNECTIONS  50
#define BROADCAST_CONNECTION_ID 10000
#define DEFAULT_CONNECTION_ID   10001
#define INVALID_CONNECTION_ID   99999

typedef enum TransportInterfaceType {
   TRANSPORT_HOST_CONTROLLER_DND = 0,
   TRANSPORT_HOST_CONTROLLER_CP,
   TRANSPORT_HOST_CONTROLLER_FT,
   TRANSPORT_GUEST_CONTROLLER_DND,
   TRANSPORT_GUEST_CONTROLLER_CP,
   TRANSPORT_GUEST_CONTROLLER_FT,
   TRANSPORT_INTERFACE_MAX,
} TransportInterfaceType;

class RpcBase;
#if defined(SWIG)
class DnDCPTransport
#else
class LIB_EXPORT DnDCPTransport
#endif
{
public:
   virtual ~DnDCPTransport() {};

   virtual void StartLoop() {};
   virtual void EndLoop() {};
   virtual void IterateLoop() {};
   virtual bool RegisterRpc(RpcBase *rpc,
                            TransportInterfaceType type) = 0;
   virtual bool UnregisterRpc(TransportInterfaceType type) = 0;

   virtual bool SendPacket(uint32 destId,
                           TransportInterfaceType type,
                           const uint8 *msg,
                           size_t length) = 0;
};

#endif // DND_CP_TRANSPORT_H
 0707010000030E000081A40000000000000000000000016822550500004D5E000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndClipboard.c  /*********************************************************
 * Copyright (C) 2007-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * dndClipboard.c
 *
 *      Implements dndClipboard.h
 */

#include <stdlib.h>
#include <string.h>

#include "vm_assert.h"

#include "dndClipboard.h"
#include "dndInt.h"
#include "dndCPMsgV4.h"
#include "unicode.h"

#define CPFormatToIndex(x) ((unsigned int)(x) - 1)

/*
 *----------------------------------------------------------------------------
 *
 * CPClipItemInit --
 *
 *      CPClipboardItem constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
CPClipItemInit(CPClipItem *item)        // IN/OUT: the clipboard item
{
   ASSERT(item);

   item->buf = NULL;
   item->size = 0;
   item->exists = FALSE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipItemDestroy --
 *
 *      CPCilpboardItem destructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

static INLINE void
CPClipItemDestroy(CPClipItem *item)     // IN/OUT: the clipboard item
{
   ASSERT(item);

   free(item->buf);
   item->buf = NULL;
   item->size = 0;
   item->exists = FALSE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipItemCopy --
 *
 *      Copy clipboard item from src to dest.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipItemCopy(CPClipItem *dest,        // IN: dest clipboard item
               const CPClipItem *src)   // IN: source clipboard item
{
   ASSERT(dest);
   ASSERT(src);

   if (src->buf) {
      void *tmp = dest->buf;
      dest->buf = realloc(dest->buf, src->size + 1);
      if (!dest->buf) {
         dest->buf = tmp;
         return FALSE;
      }
      ((uint8 *)dest->buf)[src->size] = 0;
      memcpy(dest->buf, src->buf, src->size);
   }

   dest->size = src->size;
   dest->exists = src->exists;

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_Init --
 *
 *      Constructor for CPClipboard.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
CPClipboard_Init(CPClipboard *clip)     // IN/OUT: the clipboard
{
   unsigned int i;

   ASSERT(clip);

   clip->changed = TRUE;
   clip->maxSize = CPCLIPITEM_MAX_SIZE_V3;
   for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) {
      CPClipItemInit(&clip->items[CPFormatToIndex(i)]);
   }
   clip->isInitialized = TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_InitWithSize --
 *
 *      Call CPClipboard_Init and set the clipboard size.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
CPClipboard_InitWithSize(CPClipboard *clip,  // IN/OUT: the clipboard
                         uint32 size)        // IN: clipboard size
{
   ASSERT(clip);

   CPClipboard_Init(clip);
   clip->maxSize = size;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_Destroy --
 *
 *      Destroys a clipboard.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
CPClipboard_Destroy(CPClipboard *clip)  // IN/OUT: the clipboard
{
   unsigned int i;

   ASSERT(clip);

   for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) {
      CPClipItemDestroy(&clip->items[CPFormatToIndex(i)]);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_Clear --
 *
 *      Clears the items in CPClipboard.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
CPClipboard_Clear(CPClipboard *clip)    // IN/OUT: the clipboard
{
   unsigned int i;

   ASSERT(clip);

   clip->changed = TRUE;
   for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) {
      CPClipboard_ClearItem(clip, i);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 *  CPClipboard_SetItem --
 *
 *      Makes a copy of the item adds it to the clipboard. If something
 *      already exists for the format it is overwritten. To set a promised
 *      type, pass in NULL for buffer and 0 for the size.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_SetItem(CPClipboard *clip,          // IN/OUT: the clipboard
                    const DND_CPFORMAT fmt,     // IN: the item format
                    const void *clipitem,       // IN: the item
                    const size_t size)          // IN: the item size
{
   CPClipItem *item;
   uint8 *newBuf = NULL;
   /*
    * Microsoft Office Text Effects i.e. HTML Format, BIFF, GVML, image, rtf
    * and text may be put into a clipboard at same time, and total size may be
    * more than limit. The order in filterList determines the order in which
    * the format will be fiiltered, e.g. HTML format will be first dropped,
    * then GVML,..., at last TEXT
    * File content won't appear tegother with other formats, if exceeding size
    * limit, the format will be dropped
    */
   DND_CPFORMAT filterList[] = {CPFORMAT_FILECONTENTS,
                                CPFORMAT_ART_GVML_CLIPFORMAT,
                                CPFORMAT_BIFF12,
                                CPFORMAT_HTML_FORMAT,
                                CPFORMAT_IMG_PNG,
                                CPFORMAT_RTF,
                                CPFORMAT_TEXT};
   int filterIndex = 0;

   ASSERT(clip);

   if (!(CPFORMAT_UNKNOWN < fmt && fmt < CPFORMAT_MAX)) {
      return FALSE;
   }

   if (!CPClipboard_ClearItem(clip, fmt)) {
      return FALSE;
   }

   if (size >= (size_t) clip->maxSize) {
      return FALSE;
   }

   Log("%s: Set CPClipboard struct with data of size:%zu, format:%d.\n",
       __FUNCTION__, size, (int)fmt);

   item = &clip->items[CPFormatToIndex(fmt)];

   if (clipitem) {
      /* It has to be valid utf8 for plain text format. */
      if (CPFORMAT_TEXT == fmt) {
         char *str = (char *)clipitem;
         if (!Unicode_IsBufferValid(str,
                                    size,
                                    STRING_ENCODING_UTF8)) {
            return FALSE;
         }
      }

      newBuf = malloc(size + 1);
      if (!newBuf) {
         return FALSE;
      }
      memcpy(newBuf, clipitem, size);
      newBuf[size] = 0;
   }

   item->buf = newBuf;
   item->size = size;
   item->exists = TRUE;

   /* Drop some data if total size is more than limit. */
   while (CPClipboard_GetTotalSize(clip) >= (size_t) clip->maxSize &&
          filterIndex < ARRAYSIZE(filterList)) {
      if (!CPClipboard_ClearItem(clip, filterList[filterIndex])) {
         return FALSE;
      }
      filterIndex++;
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_ClearItem --
 *
 *      Clears the item in the clipboard.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_ClearItem(CPClipboard *clip,        // IN: the clipboard
                      DND_CPFORMAT fmt)         // IN: item to be cleared
{
   CPClipItem *item;

   ASSERT(clip);

   if (!(CPFORMAT_UNKNOWN < fmt && fmt < CPFORMAT_MAX)) {
      return FALSE;
   }

   item = &clip->items[CPFormatToIndex(fmt)];

   free(item->buf);
   item->buf = NULL;
   item->size = 0;
   item->exists = FALSE;

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_GetItem  --
 *
 *      Get the clipboard item of format fmt from the clipboard. The clipboard
 *      maintains ownership of the data. If the item is promised, the buffer
 *      will contain NULL and the size will be 0.
 *
 * Results:
 *      TRUE if item exists, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_GetItem(const CPClipboard *clip,    // IN: the clipboard
                    DND_CPFORMAT fmt,           // IN: the format
                    void **buf,                 // OUT
                    size_t *size)               // OUT
{
   ASSERT(clip);
   ASSERT(size);
   ASSERT(buf);

   if(!(CPFORMAT_UNKNOWN < fmt && fmt < CPFORMAT_MAX)) {
      return FALSE;
   }

   if (clip->items[CPFormatToIndex(fmt)].exists) {
      *buf = clip->items[CPFormatToIndex(fmt)].buf;
      *size = clip->items[CPFormatToIndex(fmt)].size;
      ASSERT(*buf);
      ASSERT((*size > 0) && (*size < clip->maxSize));
      return TRUE;
   } else {
      ASSERT(!clip->items[CPFormatToIndex(fmt)].size);
      return FALSE;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_ItemExists  --
 *
 *      Check if the clipboard item of format fmt exists or not.
 *
 * Results:
 *      TRUE if item exists, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_ItemExists(const CPClipboard *clip,    // IN: the clipboard
                       DND_CPFORMAT fmt)           // IN: the format
{
   ASSERT(clip);

   if(!(CPFORMAT_UNKNOWN < fmt && fmt < CPFORMAT_MAX)) {
      return FALSE;
   }

   return (clip->items[CPFormatToIndex(fmt)].exists &&
           clip->items[CPFormatToIndex(fmt)].size > 0);
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_IsEmpty  --
 *
 *      Check if the clipboard item of format fmt exists or not.
 *
 * Results:
 *      TRUE if item exists, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_IsEmpty(const CPClipboard *clip)    // IN: the clipboard
{
   unsigned int i;

   ASSERT(clip);

   for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) {
      if (clip->items[CPFormatToIndex(i)].exists &&
          clip->items[CPFormatToIndex(i)].size > 0) {
         return FALSE;
      }
   }
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_GetTotalSize --
 *
 *      Get total buffer size of the clipboard.
 *
 * Results:
 *      Total buffer size of the clipboard.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

size_t
CPClipboard_GetTotalSize(const CPClipboard *clip) // IN: the clipboard
{
   unsigned int i = 0;
   size_t totalSize = 0;

   ASSERT(clip);

   for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) {
      if (clip->items[CPFormatToIndex(i)].exists &&
          clip->items[CPFormatToIndex(i)].size > 0) {
         totalSize += clip->items[CPFormatToIndex(i)].size;
      }
   }
   return totalSize;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_SetChanged  --
 *
 *      Set clip->changed.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
CPClipboard_SetChanged(CPClipboard *clip, // IN/OUT: the clipboard
                       Bool changed)      // IN
{
   clip->changed = changed;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_Changed  --
 *
 *      Return clip->changed.
 *
 * Results:
 *      Return clip->changed.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_Changed(const CPClipboard *clip)    // IN: the clipboard
{
   return clip->changed;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_Copy --
 *
 *      Copies the clipboard contents from src to dest. Assumes that dest has
 *      been initialized and contains no data.
 *
 * Results:
 *      TRUE on success, FALSE on failure. On failure the caller should
 *      destroy the object to ensure memory is cleaned up.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_Copy(CPClipboard *dest,             // IN: the desination clipboard
                 const CPClipboard *src)        // IN: the source clipboard
{
   unsigned int i;

   ASSERT(dest);
   ASSERT(src);

   for (i = CPFORMAT_MIN; i < CPFORMAT_MAX; ++i) {
      if (!CPClipItemCopy(&dest->items[CPFormatToIndex(i)],
                         &src->items[CPFormatToIndex(i)])) {
         return FALSE;
      }
   }
   dest->changed = src->changed;
   dest->maxSize = src->maxSize;
   dest->isInitialized = TRUE;

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_Serialize --
 *
 *      Serialize the contents of the CPClipboard out to the provided dynbuf.
 *
 * Results:
 *      TRUE on success.
 *      FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_Serialize(const CPClipboard *clip, // IN
                      DynBuf *buf)             // OUT: the output buffer
{
   DND_CPFORMAT fmt;
   uint32 maxFmt = CPFORMAT_MAX;

   ASSERT(clip);
   ASSERT(buf);
   ASSERT(clip->isInitialized);

   /* Return FALSE if not initialized. */
   if (!clip->isInitialized) {
      return FALSE;
   }

   /* First append number of formats in clip. */
   if (!DynBuf_Append(buf, &maxFmt, sizeof maxFmt)) {
      return FALSE;
   }

   /* Append format data one by one. */
   for (fmt = CPFORMAT_MIN; fmt < CPFORMAT_MAX; ++fmt) {
      CPClipItem *item = (CPClipItem *)&(clip->items[CPFormatToIndex(fmt)]);
      if (!DynBuf_Append(buf, &item->exists, sizeof item->exists) ||
          !DynBuf_Append(buf, &item->size, sizeof item->size)) {
         return FALSE;
      }
      if (item->exists && (item->size > 0) &&
          !DynBuf_Append(buf, item->buf, item->size)) {
         return FALSE;
      }
   }

   if (!DynBuf_Append(buf, &clip->changed, sizeof clip->changed)) {
      return FALSE;
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_Unserialize --
 *
 *      Unserialize the arguments of the CPClipboard provided by the buffer.
 *      On failure the clip will be destroyed.
 *
 * Results:
 *      TRUE if success, FALSE otherwise
 *
 * Side effects:
 *      The clip passed in should be empty, otherwise will cause memory leakage.
 *      On success, arguments found in buf are unserialized into clip.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_Unserialize(CPClipboard *clip, // OUT: the clipboard
                        const void *buf,   // IN: input buffer
                        size_t len)        // IN: buffer length
{
   DND_CPFORMAT fmt;
   BufRead r;
   uint32 maxFmt;

   ASSERT(clip);
   ASSERT(buf);
   ASSERT(clip->isInitialized);

   /* Return FALSE if not initialized. */
   if (!clip->isInitialized) {
      goto error;
   }

   r.pos = buf;
   r.unreadLen = len;

   /* First get number of formats in the buf. */
   if (!DnDReadBuffer(&r, &maxFmt, sizeof maxFmt)) {
      goto error;
   }

   /* This version only supports number of formats up to CPFORMAT_MAX. */
   maxFmt = MIN(CPFORMAT_MAX, maxFmt);

   for (fmt = CPFORMAT_MIN; fmt < maxFmt; ++fmt) {
      Bool exists = FALSE;
      uint32 size = 0;

      if (!DnDReadBuffer(&r, &exists, sizeof exists) ||
          !DnDReadBuffer(&r, &size, sizeof size)) {
         Log("%s: Error: exists:%d, size:%d, format:%d.\n", __FUNCTION__,
             exists, size, (int)fmt);
         goto error;
      }

      if (exists && size) {
         if (size > r.unreadLen) {
            Log("%s: Error: size:%d, unreadLen:%d, format:%d.\n", __FUNCTION__,
                size, (int)r.unreadLen, (int)fmt);
            goto error;
         }

         if (!CPClipboard_SetItem(clip, fmt, r.pos, size)) {
            goto error;
         }
         if (!DnDSlideBuffer(&r, size)) {
            goto error;
         }
      }
   }

   /* It is possible that clip->changed is missing in some beta products. */
   if (r.unreadLen == sizeof clip->changed &&
       !DnDReadBuffer(&r, &clip->changed, sizeof clip->changed)) {
      goto error;
   }

   return TRUE;

error:
   CPClipboard_Destroy(clip);
   return FALSE;
}


/*
 *----------------------------------------------------------------------------
 *
 * CPClipboard_Strip --
 *
 *      Remove clipboard items based on the passed in capabilities mask.
 *      Introduced in DnDV4.
 *
 *      XXX This function assumes that the bits in mask are such that if the
 *      check is being made for copy paste, that the corresponding bit for
 *      DnD is set to zero. Otherwise, the format cleared by copy paste will
 *      not be removed. Similar for the other case. A way to make this clearer
 *      would be to pass a flag to this function that tells it which bits
 *      to check, with no dependencies on the other bits being in proper
 *      state.
 *
 * Results:
 *      TRUE if clipboard is empty as a result, else FALSE.
 *
 *----------------------------------------------------------------------------
 */

Bool
CPClipboard_Strip(CPClipboard *clip,    // IN/OUT: the clipboard
                  uint32 mask)          // IN: if TRUE, DnD.
{
   if (!(mask & DND_CP_CAP_PLAIN_TEXT_DND) &&
       !(mask & DND_CP_CAP_PLAIN_TEXT_CP)) {
      CPClipboard_ClearItem(clip, CPFORMAT_TEXT);
   }
   if (!(mask & DND_CP_CAP_RTF_DND) && !(mask & DND_CP_CAP_RTF_CP)) {
      CPClipboard_ClearItem(clip, CPFORMAT_RTF);
   }
   if (!(mask & DND_CP_CAP_IMAGE_DND) && !(mask & DND_CP_CAP_IMAGE_CP)) {
      CPClipboard_ClearItem(clip, CPFORMAT_IMG_PNG);
   }
   if (!(mask & DND_CP_CAP_FILE_DND) && !(mask & DND_CP_CAP_FILE_CP)) {
      CPClipboard_ClearItem(clip, CPFORMAT_FILELIST);
      CPClipboard_ClearItem(clip, CPFORMAT_FILELIST_URI);
   }
   if (!(mask & DND_CP_CAP_FILE_CONTENT_DND) &&
       !(mask & DND_CP_CAP_FILE_CONTENT_CP)) {
      CPClipboard_ClearItem(clip, CPFORMAT_FILECONTENTS);
   }
   return CPClipboard_IsEmpty(clip);
}
  0707010000030F000081A4000000000000000000000001682255050000113F000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndClipboard.h  /*********************************************************
 * Copyright (C) 2007-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * dndClipboard.h
 *
 *      This file maintains an interface for the clipboard object. The object
 *      may contain several representations of the same object. For
 *      example, we could have a plain text filename as well as the file's
 *      contents on the clipboard at the same time.
 *
 *      The purpose of this structure is to store cross platform clipboard
 *      data for further processing. The UI is responsible for converting local
 *      clipboard data to a crossplatform format and inserting it into the
 *      cross platform clipboard.
 */

#ifndef _DND_CLIPBOARD_H_
#define _DND_CLIPBOARD_H_

#include "vm_basic_types.h"

#include "dnd.h"
#include "dynbuf.h"

#if defined(__cplusplus)
extern "C" {
#endif

/*
 * Make sure each clipboard item is at most 64kb - 100 b. This limitation is
 * copied from the current text copy paste limit.
 */
#define CPCLIPITEM_MAX_SIZE_V1 ((1 << 16) - 100)
#define CPCLIPITEM_MAX_SIZE_V2 ((1 << 16) - 100)
#define CPCLIPITEM_MAX_SIZE_V3 (DNDMSG_MAX_ARGSZ - 100)

/* Cross platform formats */
#pragma pack(push, 1)
typedef struct CPFileList {
   uint64 fileSize;
   uint32 relPathsLen;
   uint32 fulPathsLen;
   uint8 filelists[1];
} CPFileList;
#pragma pack(pop)

#define CPFILELIST_HEADER_SIZE (1* sizeof(uint64) + 2 * sizeof(uint32))

#pragma pack(push, 1)
typedef struct UriFileList {
   uint64 fileSize;
   uint32 uriPathsLen;
   uint8 filelists[1];
} UriFileList;
#pragma pack(pop)

#define URI_FILELIST_HEADER_SIZE (1* sizeof(uint64) + 1 * sizeof(uint32))

#pragma pack(push, 1)
typedef struct CPFileAttributes {
   // File, Directory, or link. See HgfsFileType.
   uint64 fileType;
   // Read, write, execute permissions. See File_GetFilePermissions().
   uint64 filePermissions;
} CPFileAttributes;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct CPAttributeList {
   uint32 attributesLen;
   CPFileAttributes attributeList[1];
} CPAttributeList;
#pragma pack(pop)

#define URI_ATTRIBUTES_LIST_HEADER_SIZE (1* sizeof(uint32))

/* Types which can be stored on the clipboard. */
/*
 * XXX
 * This is currently in dnd.h, but should be moved over to dndClipboard.h
 * Ling's vmx code reorg should handle this.
 */
/*
typedef enum
{
   CPFORMAT_UNKNOWN = 0
   CPFORMAT_TEXT,
   CPFORMAT_FILELIST,
   CPFORMAT_MAX,
} DND_CPFORMAT;
*/

#define CPFORMAT_MIN CPFORMAT_TEXT

/* CPClipboard */
void CPClipboard_Init(CPClipboard *clip);
void CPClipboard_InitWithSize(CPClipboard *clip, uint32 size);
void CPClipboard_Destroy(CPClipboard *clip);

void CPClipboard_Clear(CPClipboard *clip);
Bool CPClipboard_SetItem(CPClipboard *clip, const DND_CPFORMAT fmt, const void *buf,
                         const size_t size);
void CPClipboard_SetChanged(CPClipboard *clip, Bool changed);
Bool CPClipboard_Changed(const CPClipboard *clip);
Bool CPClipboard_ClearItem(CPClipboard *clip, DND_CPFORMAT fmt);
Bool CPClipboard_GetItem(const CPClipboard *clip, DND_CPFORMAT fmt,
                         void **buf, size_t *size);
Bool CPClipboard_ItemExists(const CPClipboard *clip, DND_CPFORMAT fmt);
Bool CPClipboard_IsEmpty(const CPClipboard *clip);
#if !defined(SWIG)
size_t CPClipboard_GetTotalSize(const CPClipboard *clip);
#endif
Bool CPClipboard_Copy(CPClipboard *dest, const CPClipboard *src);
Bool CPClipboard_Serialize(const CPClipboard *clip, DynBuf *buf);
Bool CPClipboard_Unserialize(CPClipboard *clip, const void *buf, size_t len);
Bool CPClipboard_Strip(CPClipboard *clip, uint32 caps);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif // _DND_CLIPBOARD_H_
 07070100000310000081A400000000000000000000000168225505000069FA000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndCommon.c /*********************************************************
 * Copyright (C) 2005-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndCommon.c --
 *
 *     Implementation of bora/lib/public/dnd.h functions that are common to
 *     Linux and Windows platforms
 */


#include <stdlib.h>
#include <time.h>
#include <limits.h>

#include "vmware.h"
#include "codeset.h"
#include "dndInt.h"
#include "dnd.h"
#include "dndClipboard.h"
#include "file.h"
#include "str.h"
#include "random.h"
#include "util.h"
#include "cpNameUtil.h"
#include "hgfsEscape.h"
#include "hgfsServerPolicy.h"
#include "hgfsVirtualDir.h"
#include "unicodeOperations.h"
#include "hostinfo.h"

#define LOGLEVEL_MODULE dnd
#include "loglevel_user.h"

#define WIN_DIRSEPC     '\\'
#define WIN_DIRSEPS     "\\"


#ifndef DND_IS_XDG
static const char *DnDCreateRootStagingDirectory(void);

/*
 *-----------------------------------------------------------------------------
 *
 * DnD_CreateStagingDirectory --
 *
 *    Generate a unique staging directory name, create the directory, and
 *    return the name. The caller is responsible for freeing the returned
 *    string.
 *
 *    Our staging directory structure is comprised of a "root" staging
 *    directory that itself contains multiple staging directories that are
 *    intended to be used on a per-DnD and per-user basis.  That is, each DnD
 *    by a particular user will have its own staging directory within the root.
 *    Sometimes these directories are emptied after the DnD (either because it
 *    was cancelled or the destination application told us to), and we resuse
 *    any empty directories that we can.  This function will return a directory
 *    to be reused if possible and fall back on creating a new one if
 *    necessary.
 *
 * Results:
 *    A string containing the newly created name, or NULL on failure.
 *
 * Side effects:
 *    A directory is created
 *
 *-----------------------------------------------------------------------------
 */

char *
DnD_CreateStagingDirectory(void)
{
   const char *root;
   char **stagingDirList;
   int numStagingDirs;
   int i;
   char *ret = NULL;
   Bool found = FALSE;

   /*
    * Make sure the root staging directory is created with the correct
    * permissions.
    */
   root = DnDCreateRootStagingDirectory();
   if (!root) {
      return NULL;
   }

   /* Look for an existing, empty staging directory */
   numStagingDirs = File_ListDirectory(root, &stagingDirList);
   if (numStagingDirs < 0) {
      goto exit;
   }

   for (i = 0; i < numStagingDirs; i++) {
      if (!found) {
         char *stagingDir;

         stagingDir = Unicode_Append(root, stagingDirList[i]);

         if (File_IsEmptyDirectory(stagingDir) &&
             DnDStagingDirectoryUsable(stagingDir)) {
               ret = Unicode_Append(stagingDir, DIRSEPS);
               /*
                * We can use this directory.  Make sure to continue to loop
                * so we don't leak the remaining stagindDirList[i]s.
                */
               found = TRUE;
         }

         free(stagingDir);
      }
   }

   Util_FreeStringList(stagingDirList, numStagingDirs);

   /* Only create a directory if we didn't find one above. */
   if (!found) {
      rqContext *context;

      /* We're just using the time as a random seed, not as a timestamp. */
      /* coverity[store_truncates_time_t] */
      context = Random_QuickSeed((unsigned)time(NULL));

      for (i = 0; i < 10; i++) {
         char *temp;

         /* Each staging directory is given a random name. */
         free(ret);
         temp = Unicode_Format("%08x%c", Random_Quick(context), DIRSEPC);
         VERIFY(temp);
         ret = Unicode_Append(root, temp);
         free(temp);

         if (File_CreateDirectory(ret) &&
             DnDSetPermissionsOnStagingDir(ret)) {
            found = TRUE;
            break;
         }
      }

      free(context);
   }

exit:
   if (!found && ret != NULL) {
      free(ret);
      ret = NULL;
   }

   return ret;
}
#endif // ifndef DND_IS_XDG


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_AppendPrefixToStagingDir --
 *
 *    Append prefix to a DnD staging directory
 *
 * Results:
 *    Return new DnD staging directory for success, NULL otherwise.
 *
 * Side effects:
 *    Caller must free the retrun string with free
 *
 *-----------------------------------------------------------------------------
 */
char *
DnD_AppendPrefixToStagingDir(const char *stagingDir, // IN:
                             const char *prefix)     // IN:
{
   const char *dndRoot = NULL;
   char *newDir = NULL;

   dndRoot = DnD_GetFileRoot();
   if (Unicode_Find(stagingDir, dndRoot) == UNICODE_INDEX_NOT_FOUND) {
      // incorrect staging directory
      Log("%s: Not find root = %s\n", __FUNCTION__, dndRoot);
      return NULL;
   }

   newDir = Unicode_Insert(stagingDir, Unicode_LengthInCodePoints(dndRoot), prefix);
   if (!File_Move(stagingDir, newDir, NULL)) {
      free(newDir);
      newDir = NULL;
   }
   return newDir;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_DeleteStagingFiles --
 *
 *    Attempts to delete all files in the staging directory. This does not
 *    delete the directory itself.
 *
 * Results:
 *    TRUE if all files were deleted. FALSE if there was an error.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DnD_DeleteStagingFiles(const char *stagingDir,  // IN:
                       Bool onReboot)           // IN:
{
   Bool ret = TRUE;

   ASSERT(stagingDir);

   if (!File_Exists(stagingDir)) {
      /* The stagingDir is already gone. */
      return TRUE;
   }

   if (!File_IsDirectory(stagingDir)) {
      return FALSE;
   }

   if (onReboot) {
      if (File_UnlinkDelayed(stagingDir)) {
         ret = FALSE;
      }
   } else {
      int i;
      int numFiles;
      char *base;
      char **fileList = NULL;

      /* get list of files in current directory */
      numFiles = File_ListDirectory(stagingDir, &fileList);

      if (numFiles == -1) {
         return FALSE;
      } else if (numFiles == 0) {
         return TRUE;
      }

      /* delete everything in the directory */
      base = Unicode_Append(stagingDir, DIRSEPS);

      for (i = 0; i < numFiles; i++) {
         char *curPath;

         curPath = Unicode_Append(base, fileList[i]);

         if (File_IsDirectory(curPath)) {
            if (!File_DeleteDirectoryTree(curPath)) {
               ret = FALSE;
            }
         } else {
            if (File_Unlink(curPath) != 0) {
               ret = FALSE;
            }
         }

         free(curPath);
      }

      free(base);
      Util_FreeStringList(fileList, numFiles);
   }

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_RemoveTempDirs --
 *
 *    Remove all directories with the specific prefix in the staging directory.
 *
 * Results:
 *    TRUE if the specific directories were deleted. FALSE if there was an error.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
DnD_RemoveTempDirs(const char *dndTempDir,  // IN:
                   const char *prefix)      // IN:
{
   Bool ret = TRUE;
   int i = 0;
   int numFiles = 0;
   char *base = NULL;
   char **fileList = NULL;

   ASSERT(dndTempDir);

   if (!File_Exists(dndTempDir)) {
      /* The dndTempDir doesn't exist. */
      return TRUE;
   }

   if (!File_IsDirectory(dndTempDir)) {
      return FALSE;
   }

   /* get list of files in current directory */
   numFiles = File_ListDirectory(dndTempDir, &fileList);
   if (numFiles == -1) {
      return FALSE;
   } else if (numFiles == 0) {
      return TRUE;
   }

   base = Unicode_Append(dndTempDir, DIRSEPS);
   for (i = 0; i < numFiles; i++) {
      char *curPath = Unicode_Append(base, fileList[i]);
      if (File_IsDirectory(curPath) &&
          (UNICODE_INDEX_NOT_FOUND != Unicode_Find(curPath, prefix))) {
         if (!File_DeleteDirectoryTree(curPath)) {
            ret = FALSE;
         }
      }
      free(curPath);
   }
   free(base);
   Util_FreeStringList(fileList, numFiles);
   return ret;
}


#ifndef DND_IS_XDG
/*
 *----------------------------------------------------------------------------
 *
 * DnDCreateRootStagingDirectory --
 *
 *    Checks if the root staging directory exists with the correct permissions,
 *    or creates it if necessary.
 *
 * Results:
 *    The path of the root directory on success, NULL on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static const char *
DnDCreateRootStagingDirectory(void)
{
   const char *root;

   /*
    * DnD_GetFileRoot() gives us a pointer to a static string, so there's no
    * need to free anything.
    */
   root = DnD_GetFileRoot();
   if (!root) {
      return NULL;
   }

   if (File_Exists(root)) {
      if (!DnDRootDirUsable(root)) {
         /*
          * The directory already exists and its permissions are wrong.
          */
         Log("%s: The root dir is not usable.\n", __FUNCTION__);
         return NULL;
      }
   } else {
      if (!File_CreateDirectory(root) ||
          !DnDSetPermissionsOnRootDir(root)) {
         /* We couldn't create the directory or set the permissions. */
         return NULL;
      }
   }

   return root;
}
#endif // ifndef DND_IS_XDG


/*
 *----------------------------------------------------------------------------
 *
 * DnD_LegacyConvertToCPName --
 *
 *    Converts paths received from older tools that do not send data in CPName
 *    format across the backdoor.  Older tools send paths in Windows format so
 *    this implementation must always convert from Windows path to CPName path,
 *    regardless of the platform we are running on.
 *
 *    The logic here and in the called functions appears to be UTF8-safe.
 *
 * 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
DnD_LegacyConvertToCPName(const char *nameIn,   // IN:  Buffer to convert
                          size_t bufOutSize,    // IN:  Size of output buffer
                          char *bufOut)         // OUT: Output buffer
{
   const char partialName[] = HGFS_SERVER_POLICY_ROOT_SHARE_NAME;
   const size_t partialNameLen = HGFS_STR_LEN(HGFS_SERVER_POLICY_ROOT_SHARE_NAME);
   const char *partialNameSuffix = "";
   size_t partialNameSuffixLen;
   char *fullName;
   size_t fullNameSize;
   size_t nameSize;
   int result;

   ASSERT(nameIn);
   ASSERT(bufOut);

   /*
    * Create the full name. Note that Str_Asprintf should not be
    * used here as it uses FormatMessages which interprets 'data', a UTF-8
    * string, as a string in the current locale giving wrong results.
    */

   /*
    * Is this file path a UNC path?
    */
   if (nameIn[0] == WIN_DIRSEPC && nameIn[1] == WIN_DIRSEPC) {
      partialNameSuffix    = WIN_DIRSEPS HGFS_UNC_DIR_NAME WIN_DIRSEPS;
      partialNameSuffixLen = HGFS_STR_LEN(WIN_DIRSEPS) +
                             HGFS_STR_LEN(HGFS_UNC_DIR_NAME) +
                             HGFS_STR_LEN(WIN_DIRSEPS);
   } else {
      partialNameSuffix    = WIN_DIRSEPS HGFS_DRIVE_DIR_NAME WIN_DIRSEPS;
      partialNameSuffixLen = HGFS_STR_LEN(WIN_DIRSEPS) +
                             HGFS_STR_LEN(HGFS_DRIVE_DIR_NAME) +
                             HGFS_STR_LEN(WIN_DIRSEPS);
   }

   /* Skip any path separators at the beginning of the input string */
   while (*nameIn == WIN_DIRSEPC) {
      nameIn++;
   }

   nameSize = strlen(nameIn);
   fullNameSize = partialNameLen + partialNameSuffixLen + nameSize;
   fullName = Util_SafeMalloc(fullNameSize + 1);

   memcpy(fullName, partialName, partialNameLen);
   memcpy(fullName + partialNameLen, partialNameSuffix, partialNameSuffixLen);
   memcpy(fullName + partialNameLen + partialNameSuffixLen, nameIn, nameSize);
   fullName[fullNameSize] = '\0';

   LOG(4, "%s: generated name is \"%s\"\n", __FUNCTION__, fullName);

   /*
    * CPName_ConvertTo implementation is performed here without calling any
    * CPName_ functions.  This is safer since those functions might change, but
    * the legacy behavior we are special casing here will not.
    */

   {
      char const *winNameIn = fullName;
      char const *origOut = bufOut;
      char const *endOut = bufOut + bufOutSize;
      char const pathSep = WIN_DIRSEPC;
      char *ignores = ":";

      /* Skip any path separators at the beginning of the input string */
      while (*winNameIn == pathSep) {
         winNameIn++;
      }

      /*
       * Copy the string to the output buf, converting all path separators into
       * '\0' and ignoring the specified characters.
       */

      for (; *winNameIn != '\0' && bufOut < endOut; winNameIn++) {
         char *currIgnore = ignores;
         Bool ignore = FALSE;

         while (*currIgnore != '\0') {
            if (*winNameIn == *currIgnore) {
               ignore = TRUE;
               break;
            }
            currIgnore++;
         }

         if (!ignore) {
            *bufOut = (*winNameIn == pathSep) ? '\0' : *winNameIn;
            bufOut++;
         }
      }

      /*
       * NUL terminate. XXX This should go away.
       *
       * When we get rid of NUL termination here, this test should
       * also change to "if (*winNameIn != '\0')".
       */

      if (bufOut == endOut) {
         result = -1;
         goto out;
      }
      *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] */
      result = (int)(bufOut - origOut);
      while ((result >= 1) && (origOut[result - 1] == 0)) {
         result--;
      }

      /*
       * Make exception and call CPName_Print() here, since it's only for
       * logging
       */

      LOG(4, "%s: CPName is \"%s\"\n", __FUNCTION__, 
          CPName_Print(origOut, result));
   }

out:
   free(fullName);

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_CPNameListToDynBufArray --
 *
 *    Export CPName file list from binary buffer to DynBufArray.
 *
 * Results:
 *    TRUE if success, FALSE otherwise.
 *
 * Side effects:
 *    Memory may allocated for DynBufArray if success.
 *
 *-----------------------------------------------------------------------------
 */

Bool
DnD_CPNameListToDynBufArray(char *fileList,           // IN: CPName format
                            size_t listSize,          // IN
                            DynBufArray *dynBufArray) // OUT
{
   DynBuf buf;
   BufRead r;
   int32 pathLen;
   size_t count;
   size_t i;

   ASSERT(fileList);
   r.pos = fileList;
   r.unreadLen = listSize;

   DynBufArray_Init(dynBufArray, 0);

   while (r.unreadLen > 0) {
      DynBuf_Init(&buf);
      if (!DnDReadBuffer(&r, &pathLen, sizeof pathLen) ||
          (pathLen > r.unreadLen) ||
          !DynBuf_Append(&buf, r.pos, pathLen)) {
         goto error;
      }

      if (!DnDSlideBuffer(&r, pathLen)) {
         goto error;
      }

      if (!DynBufArray_Push(dynBufArray, buf)) {
         goto error;
      }
   }
   return TRUE;

error:
   DynBuf_Destroy(&buf);

   count = DynBufArray_Count(dynBufArray);
   for (i = 0; i < count; i++) {
      DynBuf *b = DynArray_AddressOf(dynBufArray, i);
      DynBuf_Destroy(b);
   }
   DynBufArray_SetCount(dynBufArray, 0);
   DynBufArray_Destroy(dynBufArray);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_GetLastDirName --
 *
 *    Try to get last directory name from a full path name.
 *
 * Results:
 *    The allocated UTF8 string, or NULL on failure.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

char *
DnD_GetLastDirName(const char *str) // IN
{
   size_t end = strlen(str);
   size_t start;
   size_t res = 0;

   if (end != 0 && DIRSEPC == str[end - 1]) {
      end--;
   }

   if (end == 0) {
      return 0;
   }

   start = end;

   while (start && DIRSEPC != str[start - 1]) {
      start--;
   }

   /* There should be at lease 1 DIRSEPC before end. */
   if (start == 0) {
      return 0;
   }

   res = end - start;
   return Unicode_AllocWithLength(str + start, res, STRING_ENCODING_UTF8);
}

/*
 *-----------------------------------------------------------------------------
 *
 * DnD_SetCPClipboardAndTruncateText --
 *
 *    Truncate the text if the size exceeds the maximum size and then put it
 *    into clip.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    The destBuf should be released by the caller where the destBuf allocated.
 *
 *-----------------------------------------------------------------------------
 */

void
DnD_SetCPClipboardAndTruncateText(CPClipboard *clip, // IN/OUT
                                  char *destBuf,     // IN
                                  size_t len)        // IN
{
   size_t bytesLeft = clip->maxSize - CPClipboard_GetTotalSize(clip) - 1;

   if (bytesLeft < 2 || len == 1) {
      /*
       * Less than 2 bytes left ( 1 byte needed for ending NULL ) or
       * input buffer only contains ending NULL
       */
      return;
   }
   // Truncate if the length is greater than max allowed.
   if (len > bytesLeft) {
      size_t boundaryPoint =
         CodeSet_Utf8FindCodePointBoundary(destBuf, bytesLeft - 1);
      destBuf[boundaryPoint] = '\0';
      ASSERT(Unicode_IsBufferValid(destBuf, -1, STRING_ENCODING_UTF8));
      Log("%s: Truncating text from %" FMTSZ "d chars to %" FMTSZ "d chars.\n",
          __FUNCTION__, len - 1, boundaryPoint);
      len = boundaryPoint + 1;
   }

   CPClipboard_SetItem(clip, CPFORMAT_TEXT, destBuf, len);
   Log("%s: retrieved text (%" FMTSZ "d bytes) from clipboard.\n",
      __FUNCTION__, len);
}


/* Transport layer big buffer support functions. */

/*
 *-----------------------------------------------------------------------------
 *
 * DnD_TransportBufInit --
 *
 *    Initialize transport layer buffer with DnD message.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Buffer memory is allocated.
 *
 *-----------------------------------------------------------------------------
 */

void
DnD_TransportBufInit(DnDTransportBuffer *buf, // OUT
                     uint8 *msg,              // IN
                     size_t msgSize,          // IN
                     uint32 seqNum)           // IN
{
   ASSERT(buf);
   ASSERT(msgSize <= DNDMSG_MAX_ARGSZ);

   free(buf->buffer);
   buf->buffer = Util_SafeMalloc(msgSize);
   memcpy(buf->buffer, msg, msgSize);
   buf->seqNum = seqNum;
   buf->totalSize = msgSize;
   buf->offset = 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_TransportBufReset --
 *
 *    Reset transport layer buffer.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
DnD_TransportBufReset(DnDTransportBuffer *buf) // IN/OUT
{
   ASSERT(buf);

   free(buf->buffer);
   buf->buffer = NULL;

   buf->seqNum = 0;
   buf->totalSize = 0;
   buf->offset = 0;
   buf->lastUpdateTime = 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_TransportBufGetPacket --
 *
 *    Get a transport layer packet from transport layer buffer.
 *
 * Results:
 *    Transport layer packet size, or 0 if failed.
 *
 * Side effects:
 *    Memory may be allocated for packet.
 *
 *-----------------------------------------------------------------------------
 */

size_t
DnD_TransportBufGetPacket(DnDTransportBuffer *buf,           // IN/OUT
                          DnDTransportPacketHeader **packet) // OUT
{
   size_t payloadSize;

   ASSERT(buf);

   if (buf->totalSize < buf->offset) {
      return 0;
   }

   if ((buf->totalSize - buf->offset) > DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE) {
      payloadSize = DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE;
   } else {
      payloadSize = buf->totalSize - buf->offset;
   }

   *packet = Util_SafeMalloc(payloadSize + DND_TRANSPORT_PACKET_HEADER_SIZE);
   (*packet)->type = DND_TRANSPORT_PACKET_TYPE_PAYLOAD;
   (*packet)->seqNum = buf->seqNum;
   (*packet)->totalSize = buf->totalSize;
   (*packet)->payloadSize = payloadSize;
   (*packet)->offset = buf->offset;

   memcpy((*packet)->payload,
          buf->buffer + buf->offset,
          payloadSize);
   buf->offset += payloadSize;

   /* This time is used for timeout purpose. */
   buf->lastUpdateTime = Hostinfo_SystemTimerUS();

   return payloadSize + DND_TRANSPORT_PACKET_HEADER_SIZE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_TransportBufAppendPacket --
 *
 *    Put a received packet into transport layer buffer.
 *    This function should be called after validate the packet and packetSize!
 *    See: RpcV3Util::OnRecvPacket()
 *
 * Results:
 *    TRUE if success, FALSE otherwise.
 *
 * Side effects:
 *    Memory may be allocated for transport layer buffer.
 *
 *-----------------------------------------------------------------------------
 */

Bool
DnD_TransportBufAppendPacket(DnDTransportBuffer *buf,          // IN/OUT
                             DnDTransportPacketHeader *packet, // IN
                             size_t packetSize)                // IN
{
   ASSERT(buf);

   /*
    * If seqNum does not match, it means either this is the first packet, or there
    * is a timeout in another side. Reset the buffer in all cases.
    */
   if (buf->seqNum != packet->seqNum) {
      DnD_TransportBufReset(buf);
   }

   if (!buf->buffer) {
      ASSERT(!packet->offset);
      if (packet->offset) {
         goto error;
      }
      buf->buffer = Util_SafeMalloc(packet->totalSize);
      buf->totalSize = packet->totalSize;
      buf->seqNum = packet->seqNum;
      buf->offset = 0;
   }

   if (buf->offset != packet->offset) {
      goto error;
   }

   memcpy(buf->buffer + buf->offset,
          packet->payload,
          packet->payloadSize);
   buf->offset += packet->payloadSize;
   return TRUE;

error:
   DnD_TransportBufReset(buf);
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_TransportMsgToPacket --
 *
 *    Get a packet from small size message.
 *
 * Results:
 *    Transport layer packet size, or 0 if failed.
 *
 * Side effects:
 *    Memory may be allocated for packet.
 *
 *-----------------------------------------------------------------------------
 */

size_t
DnD_TransportMsgToPacket(uint8 *msg,                        // IN
                         size_t msgSize,                    // IN
                         uint32 seqNum,                     // IN
                         DnDTransportPacketHeader **packet) // OUT
{
   size_t packetSize;

   ASSERT(msgSize > 0 && msgSize <= DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE);
   ASSERT(msg);
   ASSERT(packet);

   if (msgSize <=0 ||
       msgSize > DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE ||
       !msg || !packet) {
      return 0;
   }

   packetSize = msgSize + DND_TRANSPORT_PACKET_HEADER_SIZE;

   *packet = Util_SafeMalloc(packetSize);

   (*packet)->type = DND_TRANSPORT_PACKET_TYPE_SINGLE;
   (*packet)->seqNum = seqNum;
   (*packet)->totalSize = msgSize;
   (*packet)->payloadSize = msgSize;
   (*packet)->offset = 0;

   memcpy((*packet)->payload, msg, msgSize);

   return packetSize;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_TransportReqPacket --
 *
 *    Generate a request packet with empty payload. After got a payload, receive
 *    side should send a DND_TRANSPORT_PACKET_TYPE_REQUEST packet to ask for
 *    next payload packet.
 *
 * Results:
 *    Transport layer packet size.
 *
 * Side effects:
 *    Memory is allocated for packet.
 *
 *-----------------------------------------------------------------------------
 */

size_t
DnD_TransportReqPacket(DnDTransportBuffer *buf,           // IN
                       DnDTransportPacketHeader **packet) // OUT
{
   *packet = Util_SafeMalloc(DND_TRANSPORT_PACKET_HEADER_SIZE);

   (*packet)->type = DND_TRANSPORT_PACKET_TYPE_REQUEST;
   (*packet)->seqNum = buf->seqNum;
   (*packet)->totalSize = buf->totalSize;
   (*packet)->payloadSize = 0;
   (*packet)->offset = buf->offset;
   return DND_TRANSPORT_PACKET_HEADER_SIZE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDReadBuffer --
 *
 *      Copies len bytes of data from b to out. Subsequent calls to this
 *      function will copy data from the last unread point.
 *
 * Results:
 *      TRUE when data is successfully copies to out, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnDReadBuffer(BufRead *b,       // IN/OUT: buffer to read from
              void *out,        // OUT: the output buffer
              size_t len)       // IN: the amount to read
{
   ASSERT(b);
   ASSERT(out);

   if (len > b->unreadLen) {
      return FALSE;
   }

   memcpy(out, b->pos, len);
   if (!DnDSlideBuffer(b, len)) {
      return FALSE;
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDSlideBuffer --
 *
 *      Ignore len bytes of data in b. Subsequent calls to DnDReadBuffer will
 *      copy data from the last point.
 *
 * Results:
 *      TRUE when pos is successfully changed, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnDSlideBuffer(BufRead *b, // IN/OUT: buffer to read from
               size_t len) // IN: the amount to read
{
   ASSERT(b);

   if (len > b->unreadLen) {
      return FALSE;
   }

   b->pos += len;
   b->unreadLen -= len;

   return TRUE;
}

  07070100000311000081A40000000000000000000000016822550500000A64000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndFileContentsUtil.h   /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * dndFileContentsUtil.h
 *
 *      Helper functions for format conversion for file contents.
 */

#ifndef _DND_FILE_CONTENTS_UTIL_H_
#define _DND_FILE_CONTENTS_UTIL_H_

#include "guestrpc/cpFileContents.h"

#define CP_FILE_VALID_NONE              0
#define CP_FILE_VALID_TYPE              (1 << 0)
#define CP_FILE_VALID_SIZE              (1 << 1)
#define CP_FILE_VALID_CREATE_TIME       (1 << 2)
#define CP_FILE_VALID_ACCESS_TIME       (1 << 3)
#define CP_FILE_VALID_WRITE_TIME        (1 << 4)
#define CP_FILE_VALID_CHANGE_TIME       (1 << 5)
#define CP_FILE_VALID_PERMS             (1 << 6)
#define CP_FILE_VALID_ATTR              (1 << 7)
#define CP_FILE_VALID_NAME              (1 << 8)
#define CP_FILE_VALID_CONTENT           (1 << 9)

#define CP_FILE_ATTR_NONE               0
#define CP_FILE_ATTR_HIDDEN             (1 << 0)
#define CP_FILE_ATTR_SYSTEM             (1 << 1)
#define CP_FILE_ATTR_ARCHIVE            (1 << 2)
#define CP_FILE_ATTR_HIDDEN_FORCED      (1 << 3)
#define CP_FILE_ATTR_READONLY           (1 << 4)

#define CP_FILE_TYPE_REGULAR            1
#define CP_FILE_TYPE_DIRECTORY          2
#define CP_FILE_TYPE_SYMLINK            3

#ifdef _WIN32

#include <shlobj.h>

Bool DnD_FileDescAToCPFileContents(FILEDESCRIPTORA *fileDescA,
                                   CPFileItem* cpItem);
Bool DnD_FileDescWToCPFileContents(FILEDESCRIPTORW *fileDescW,
                                   CPFileItem* cpItem);
Bool DnD_CPFileContentsToFileDescA(CPFileItem* cpItem,
                                   FILEDESCRIPTORA *fileDescA,
                                   Bool processName);
Bool DnD_CPFileContentsToFileDescW(CPFileItem* cpItem,
                                   FILEDESCRIPTORW *fileDescW);
#endif // _WIN32

#endif // _DND_FILE_CONTENTS_UTIL_H_
07070100000312000081A4000000000000000000000001682255050000067C000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndInt.h    /*********************************************************
 * Copyright (C) 2005-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndInt.h --
 *
 *   Private functions for the Drag and Drop library.
 */

#ifndef __DND_INT_H__
#define __DND_INT_H__

#include "vm_basic_types.h"
#include "unicodeTypes.h"

typedef struct {
   const uint8 *pos;
   size_t unreadLen;
} BufRead;

Bool DnDDataContainsIllegalCharacters(const char *data,
                                      const size_t dataSize,
                                      const char *illegalChars);

Bool DnDRootDirUsable(const char *pathName);

Bool DnDSetPermissionsOnRootDir(const char *pathName);

Bool DnDStagingDirectoryUsable(const char *pathName);

Bool DnDSetPermissionsOnStagingDir(const char *pathName);

Bool DnDReadBuffer(BufRead *b, void *out, size_t len);

Bool DnDSlideBuffer(BufRead *b, size_t len);

#endif /*  __DND_INT_H__ */
07070100000313000081A400000000000000000000000168225505000054AF000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndLinux.c  /*********************************************************
 * Copyright (C) 2005-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndLinux.c --
 *
 *     Some common dnd functions for UNIX guests and hosts.
 */


#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "vmware.h"
#include "dndInt.h"
#include "err.h"
#include "dnd.h"
#include "posix.h"
#include "file.h"
#include "strutil.h"
#include "vm_assert.h"
#include "util.h"
#include "escape.h"
#include "su.h"
#if defined(__linux__) || defined(sun) || defined(__FreeBSD__)
#include "vmblock_user.h"
#include "mntinfo.h"
#endif

#define LOGLEVEL_MODULE dnd
#include "loglevel_user.h"

#include "unicodeOperations.h"


#define DND_ROOTDIR_PERMS     (S_IRWXU | S_IRWXG | S_IRWXO)
#define DND_STAGINGDIR_PERMS  (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
#ifdef sun
#define ACCESSPERMS           (S_IRWXU | S_IRWXG | S_IRWXO)
#endif
#ifdef __ANDROID__
/*
 * Android doesn't support setmntent(), endmntent() or MOUNTED.
 */
#define NO_SETMNTENT
#define NO_ENDMNTENT
#define ACCESSPERMS           (S_IRWXU | S_IRWXG | S_IRWXO)
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_GetFileRoot --
 *
 *       Gets the root path of the staging directory for DnD file transfers.
 *
 * Results:
 *       The path to the staging directory.
 *
 * Side effects:
 *	 None
 *
 *-----------------------------------------------------------------------------
 */

const char *
DnD_GetFileRoot(void)
{
   return "/tmp/VMwareDnD/";
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDUriListGetFile --
 *
 *    Retrieves the filename and length from the file scheme (file://) entry at
 *    the specified index in a text/uri-list string.
 *
 * Results:
 *    The address of the beginning of the next filename on success, NULL if
 *    there are no more entries or on error.  index is updated with the
 *    location of the next entry in the list, and length is updated with the
 *    size of the filename starting at the returned value.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static char *
DnDUriListGetFile(char const *uriList,  // IN    : text/uri-list string
                  size_t *index,        // IN/OUT: current index
                  size_t *length)       // OUT   : length of returned string
{
   char const *nameStart;
   char const *nameEnd;
   char const *curr;

   ASSERT(uriList);
   ASSERT(index);
   ASSERT(length);

   /* The common case on the last entry */
   if (uriList[*index] == '\0') {
      return NULL;
   }

   /*
    * Ensure the URI list is formatted properly.  This is ugly, but we have to
    * special case for KDE (which doesn't follow the standard).
    *
    * XXX Note that this assumes we only support dropping files, based on the
    * definition of the macro that is used.
    */

   nameStart = &uriList[*index];

   if (strncmp(nameStart,
               DND_URI_LIST_PRE,
               sizeof DND_URI_LIST_PRE - 1) == 0) {
      nameStart += sizeof DND_URI_LIST_PRE - 1;
   } else if (strncmp(nameStart,
                      DND_URI_LIST_PRE_KDE,
                      sizeof DND_URI_LIST_PRE_KDE - 1) == 0) {
      nameStart += sizeof DND_URI_LIST_PRE_KDE - 1;
#if defined(__linux__)
   } else if (DnD_UriIsNonFileSchemes(nameStart)) {
      /* Do nothing. */
#endif
   } else {
      Warning("%s: the URI list did not begin with %s or %s\n", __func__,
              DND_URI_LIST_PRE, DND_URI_LIST_PRE_KDE);

      return NULL;
    }

   nameEnd = NULL;

   /* Walk the filename looking for the end */
   curr = nameStart;
   while (*curr != '\0' && *curr != '\r' && *curr != '\n') {
      curr++;
   }

   nameEnd = curr - 1;

   /* Bump curr based on trailing newline characters found */
   while (*curr == '\r' || *curr == '\n') {
      curr++;
   }

   *index = curr - uriList;
   *length = nameEnd - nameStart + 1;

   return (char *)nameStart;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_UriListGetNextFile --
 *
 *    Retrieves and unescapes the next file from the provided test/uri-list
 *    mime type string.  The index provided is used to iteratively retrieve
 *    successive files from the list.
 *
 * Results:
 *    An allocated, unescaped, NUL-terminated string containing the filename
 *    within the uri-list after the specified index.  index is updated to point
 *    to the next entry of the uri-list, and length (if provided) is set to the
 *    length of the allocated string (not including the NUL terminator).
 *    NULL if there are no more entries in the list, or on error.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

char *
DnD_UriListGetNextFile(char const *uriList,  // IN    : text/uri-list string
                       size_t *index,        // IN/OUT: current index
                       size_t *length)       // OUT   : length of returned string
{
   char const *file;
   size_t nextIndex;
   size_t fileLength = 0;
   char *unescapedName;
   size_t unescapedLength;

   ASSERT(uriList);
   ASSERT(index);

   nextIndex = *index;

   /* Get pointer to and length of next filename */
   file = DnDUriListGetFile(uriList, &nextIndex, &fileLength);
   if (!file) {
      return NULL;
   }

   /*
    * Retrieve an allocated, unescaped name.  This undoes the ' ' -> "%20"
    * escaping as required by RFC 1630 for entries in a uri-list.
    */

   unescapedName = Escape_Undo('%', file, fileLength, &unescapedLength);
   if (!unescapedName) {
      Warning("%s: error unescaping filename\n", __func__);

      return NULL;
   }

   *index = nextIndex;
   if (length) {
      *length = unescapedLength;
   }

   return unescapedName;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_UriIsNonFileSchemes --
 *
 *    Check if the uri contains supported non-file scheme.
 *
 * Results:
 *    TRUE if the uri contains supported non-file scheme. FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnD_UriIsNonFileSchemes(const char *uri)
{
   const char *schemes[] = DND_URI_NON_FILE_SCHEMES;
   int i = 0;

   while (schemes[i] != NULL) {
      if (strncmp(uri,
                  schemes[i],
                  strlen(schemes[i])) == 0) {
         return TRUE;
      }
      i++;
   }
   return FALSE;
}


/* We need to make this suck less. */
#if defined(__linux__) || defined(sun) || defined(__FreeBSD__)

/*
 *----------------------------------------------------------------------------
 *
 * DnD_AddBlockLegacy --
 *
 *    Adds a block to blockPath.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    Processes trying to access this path will block until DnD_RemoveBlock
 *    is called.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnD_AddBlockLegacy(int blockFd,                    // IN
                   const char *blockPath)          // IN
{
   LOG(1, "%s: placing block on %s\n", __func__, blockPath);

   ASSERT(blockFd >= 0);

   if (VMBLOCK_CONTROL(blockFd, VMBLOCK_ADD_FILEBLOCK, blockPath) != 0) {
      LOG(1, "%s: Cannot add block on %s (%s)\n",
          __func__, blockPath, Err_Errno2String(errno));

      return FALSE;
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_RemoveBlockLegacy --
 *
 *    Removes block on blockedPath.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    Processes blocked on accessing this path will continue.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnD_RemoveBlockLegacy(int blockFd,                    // IN
                      const char *blockedPath)        // IN
{
   LOG(1, "%s: removing block on %s\n", __func__, blockedPath);

   if (blockFd >= 0) {
      if (VMBLOCK_CONTROL(blockFd, VMBLOCK_DEL_FILEBLOCK, blockedPath) != 0) {
         Log("%s: Cannot delete block on %s (%s)\n",
             __func__, blockedPath, Err_Errno2String(errno));

         return FALSE;
      }
   } else {
      LOG(4, "%s: Could not remove block on %s: "
          "fd to vmblock no longer exists.\n", __func__, blockedPath);
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_CheckBlockLegacy --
 *
 *    Verifies that given file descriptor is truly a control file of
 *    kernel-based vmblock implementation. Just a stub, for now at
 *    least since we don't have a good way to check.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
DnD_CheckBlockLegacy(int blockFd)                    // IN
{
   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_AddBlockFuse --
 *
 *    Adds a block to blockPath.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    Processes trying to access this path will block until DnD_RemoveBlock
 *    is called.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnD_AddBlockFuse(int blockFd,                    // IN
                 const char *blockPath)          // IN
{
   LOG(1, "%s: placing block on %s\n", __func__, blockPath);

   ASSERT(blockFd >= 0);

   if (VMBLOCK_CONTROL_FUSE(blockFd, VMBLOCK_FUSE_ADD_FILEBLOCK,
                            blockPath) != 0) {
      LOG(1, "%s: Cannot add block on %s (%s)\n",
          __func__, blockPath, Err_Errno2String(errno));

      return FALSE;
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_RemoveBlockFuse --
 *
 *    Removes block on blockedPath.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    Processes blocked on accessing this path will continue.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnD_RemoveBlockFuse(int blockFd,                    // IN
                    const char *blockedPath)        // IN
{
   LOG(1, "%s: removing block on %s\n", __func__, blockedPath);

   if (blockFd >= 0) {
      if (VMBLOCK_CONTROL_FUSE(blockFd, VMBLOCK_FUSE_DEL_FILEBLOCK,
                               blockedPath) != 0) {
         Log("%s: Cannot delete block on %s (%s)\n",
             __func__, blockedPath, Err_Errno2String(errno));

         return FALSE;
      }
   } else {
      LOG(4, "%s: Could not remove block on %s: "
          "fd to vmblock no longer exists.\n", __func__, blockedPath);
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_CheckBlockFuse --
 *
 *    Verifies that given file descriptor is truly a control file of
 *    FUSE-based vmblock implementation.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
DnD_CheckBlockFuse(int blockFd)                    // IN
{
   char buf[sizeof(VMBLOCK_FUSE_READ_RESPONSE)];
   ssize_t size;

   size = read(blockFd, buf, sizeof(VMBLOCK_FUSE_READ_RESPONSE));
   if (size < 0) {
      LOG(4, "%s: read failed, error %s.\n", __func__, Err_Errno2String(errno));

      return FALSE;
   }

   if (size != sizeof(VMBLOCK_FUSE_READ_RESPONSE)) {
      /*
       * Refer to bug 817761 of casting size to size_t.
       */
      LOG(4, "%s: Response too short (%"FMTSZ"u vs. %"FMTSZ"u).\n",
          __func__, (size_t)size, sizeof(VMBLOCK_FUSE_READ_RESPONSE));

      return FALSE;
   }

   if (memcmp(buf, VMBLOCK_FUSE_READ_RESPONSE,
              sizeof(VMBLOCK_FUSE_READ_RESPONSE))) {
      LOG(4, "%s: Invalid response %.*s\n",
          __func__, (int)sizeof(VMBLOCK_FUSE_READ_RESPONSE) - 1, buf);

      return FALSE;
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_TryInitVmblock --
 *
 *    Initializes file blocking needed to prevent access to file before
 *    transfer has finished.
 *
 * Results:
 *    Block descriptor on success, -1 on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static int
DnD_TryInitVmblock(const char *vmbFsName,          // IN
                   const char *vmbMntPoint,        // IN
                   const char *vmbDevice,          // IN
                   mode_t vmbDeviceMode,           // IN
                   Bool (*verifyBlock)(int fd))    // IN
{
#if defined NO_SETMNTENT || defined NO_ENDMNTENT
   NOT_IMPLEMENTED();
   errno = ENOSYS;
   return -1;
#else
   Bool found = FALSE;
   int blockFd = -1;
   char *realMntPoint;
   MNTHANDLE fp;
   DECLARE_MNTINFO(mnt);

   ASSERT(vmbFsName);
   ASSERT(vmbMntPoint);
   ASSERT(vmbDevice);

   /* Resolve desired mount point in case it is symlinked somewhere */
   realMntPoint = Posix_RealPath(vmbMntPoint);
   if (!realMntPoint) {
      /*
       * If resolve failed for some reason try to fall back to
       * original mount point specification.
       */
      realMntPoint = Util_SafeStrdup(vmbMntPoint);
   }

   /* Make sure the vmblock file system is mounted. */
   fp = OPEN_MNTFILE("r");
   if (fp == NULL) {
      LOG(1, "%s: could not open mount file\n", __func__);
      goto out;
   }

   while (GETNEXT_MNTINFO(fp, mnt)) {
      /*
       * In the future we can publish the mount point in VMDB so that the UI
       * can use it rather than enforcing the VMBLOCK_MOUNT_POINT check here.
       */

      if (strcmp(MNTINFO_FSTYPE(mnt), vmbFsName) == 0 &&
          strcmp(MNTINFO_MNTPT(mnt), realMntPoint) == 0) {
         found = TRUE;
         break;
      }
   }

   (void) CLOSE_MNTFILE(fp);

   if (found) {
      /* Open device node for communication with vmblock. */
      blockFd = Posix_Open(vmbDevice, vmbDeviceMode);
      if (blockFd < 0) {
         LOG(1, "%s: Can not open blocker device (%s)\n",
             __func__, Err_Errno2String(errno));
      } else {
         LOG(4, "%s: Opened blocker device at %s\n", __func__, VMBLOCK_DEVICE);
         if (verifyBlock && !verifyBlock(blockFd)) {
            LOG(4, "%s: Blocker device at %s did not pass checks, closing.\n",
                __func__, VMBLOCK_DEVICE);
            close(blockFd);
            blockFd = -1;
         }
      }
   }

out:
   free(realMntPoint);
   return blockFd;
#endif
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_InitializeBlocking --
 *
 *    Initializes file blocking needed to prevent access to file before
 *    transfer has finished.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnD_InitializeBlocking(DnDBlockControl *blkCtrl)   // OUT
{
   uid_t uid;
   int blockFd;

   /* Root access is needed for opening the vmblock device. */
   uid = Id_BeginSuperUser();

   /* Fitrst try FUSE and see if it is available. */
   blockFd = DnD_TryInitVmblock(VMBLOCK_FUSE_FS_NAME, VMBLOCK_FUSE_MOUNT_POINT,
                                VMBLOCK_FUSE_DEVICE, VMBLOCK_FUSE_DEVICE_MODE,
                                DnD_CheckBlockFuse);
   if (blockFd != -1) {
      blkCtrl->fd = blockFd;
      /* Setup FUSE methods. */
      blkCtrl->blockRoot = VMBLOCK_FUSE_FS_ROOT;
      blkCtrl->AddBlock = DnD_AddBlockFuse;
      blkCtrl->RemoveBlock = DnD_RemoveBlockFuse;
      goto out;
   }

   /* Now try OS-specific VMBlock driver. */
   blockFd = DnD_TryInitVmblock(VMBLOCK_FS_NAME, VMBLOCK_MOUNT_POINT,
                                VMBLOCK_DEVICE, VMBLOCK_DEVICE_MODE,
                                NULL);
   if (blockFd != -1) {
      blkCtrl->fd = blockFd;
      /* Setup legacy in-kernel methods. */
      blkCtrl->blockRoot = VMBLOCK_FS_ROOT;
      blkCtrl->AddBlock = DnD_AddBlockLegacy;
      blkCtrl->RemoveBlock = DnD_RemoveBlockLegacy;
      goto out;
   }

   LOG(4, "%s: could not find vmblock mounted\n", __func__);
out:
   Id_EndSuperUser(uid);

   return blockFd != -1;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnD_UninitializeBlocking --
 *
 *    Uninitialize file blocking.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    All existing blocks will be removed.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnD_UninitializeBlocking(DnDBlockControl *blkCtrl)    // IN
{
   Bool ret = TRUE;

   if (blkCtrl->fd >= 0) {
      if (close(blkCtrl->fd) < 0) {
         Log("%s: Can not close blocker device (%s)\n",
             __func__, Err_Errno2String(errno));
         ret = FALSE;
      } else {
         blkCtrl->fd = -1;
      }
   }

   return ret;
}

/*
 * DnD_CompleteBlockInitialization --
 *
 *    Complete block initialization in case when we were handed
 *    blocking file descriptor (presumably opened for us by a
 *    suid application).
 *
 * Results:
 *    TRUE on success, FALSE on failure (invalid type).
 *
 * Side effects:
 *    Adjusts blkCtrl to match blocking device type (legacy or fuse).
 *
 */

Bool
DnD_CompleteBlockInitialization(int fd,                     // IN
                                DnDBlockControl *blkCtrl)   // OUT
{
   blkCtrl->fd = fd;

   if (DnD_CheckBlockFuse(fd)) {
      /* Setup FUSE methods. */
      blkCtrl->blockRoot = VMBLOCK_FUSE_FS_ROOT;
      blkCtrl->AddBlock = DnD_AddBlockFuse;
      blkCtrl->RemoveBlock = DnD_RemoveBlockFuse;
   } else if (DnD_CheckBlockLegacy(fd)) {
      /* Setup legacy methods. */
      blkCtrl->blockRoot = VMBLOCK_FS_ROOT;
      blkCtrl->AddBlock = DnD_AddBlockLegacy;
      blkCtrl->RemoveBlock = DnD_RemoveBlockLegacy;
   } else {
      Log("%s: Can't determine block type.\n", __func__);

      return FALSE;
   }

   return TRUE;
}

#endif /* linux || sun || FreeBSD */


/*
 *----------------------------------------------------------------------------
 *
 * DnDRootDirUsable --
 *
 *    Determines whether the provided directory is usable as the root for
 *    staging directories.
 *
 * Results:
 *    TRUE if the root directory is usable, FALSE otherwise.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnDRootDirUsable(const char *pathName)  // IN:
{
   struct stat buf;

   if (Posix_Stat(pathName, &buf) < 0) {
      return FALSE;
   }

   return S_ISDIR(buf.st_mode) &&
          (buf.st_mode & S_ISVTX) == S_ISVTX &&
          (buf.st_mode & ACCESSPERMS) == DND_ROOTDIR_PERMS;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDSetPermissionsOnRootDir --
 *
 *    Sets the correct permissions for the root staging directory.  We set the
 *    root directory to 1777 so that all users can create their own staging
 *    directories within it and that other users cannot delete that directory.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnDSetPermissionsOnRootDir(const char *pathName)  // IN:
{
   return Posix_Chmod(pathName, S_ISVTX | DND_ROOTDIR_PERMS) == 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDStagingDirectoryUsable --
 *
 *    Determines whether a staging directory is usable for the current
 *    process.  A directory is only usable by the current process if it is
 *    owned by the effective uid of the current process.
 *
 * Results:
 *    TRUE if the directory is usable, FALSE if it is not.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnDStagingDirectoryUsable(const char *pathName)  // IN:
{
   struct stat buf;

   if (Posix_Stat(pathName, &buf) < 0) {
      return FALSE;
   }

   return buf.st_uid == Id_GetEUid();
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDSetPermissionsOnStagingDir --
 *
 *    Sets the correct permissions for staging directories.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnDSetPermissionsOnStagingDir(const char *pathName)  // IN:
{
   return Posix_Chmod(pathName, DND_STAGINGDIR_PERMS) == 0;
}
 07070100000314000081A400000000000000000000000168225505000030B9000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndMsg.c    /*********************************************************
 * Copyright (c) 2007-2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndMsg.c --
 *
 *      DnDMsg represents an rpc message which is sent across the
 *      wire. Any args that it holds will be written out exactly as stored.
 *
 *      To protect itself there are many checks to ensure the data which is
 *      serialized and unserialized is sane. Defines and asserts are used to
 *      ensure the message stays under these limits when serializing out and
 *      checks are enforced to ensure that the data to be unserialized remains
 *      under these limits.
 */

#include <stdlib.h>
#include <string.h>
#include "vm_assert.h"

#include "dndMsg.h"
#include "dndInt.h"

#define LOGLEVEL_MODULE dnd
#include "loglevel_user.h"


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_Init --
 *
 *      DnDMsg constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

void
DnDMsg_Init(DnDMsg *msg)   // IN/OUT: the message
{
   ASSERT(msg);

   msg->ver = 3;
   msg->cmd = 0;
   msg->nargs = 0;
   DynBufArray_Init(&msg->args, 0);
   msg->expectedArgsSz = 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_Destroy --
 *
 *      Destroys a message by clearing any of the data that is contained in it.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Frees the arguments' memory.
 *
 *----------------------------------------------------------------------------
 */

void
DnDMsg_Destroy(DnDMsg *msg)  // IN/OUT: the message
{
   uint32 i;
   uint32 count;

   ASSERT(msg);

   count = DynArray_Count(&msg->args);

   msg->ver = 0;
   msg->cmd = 0;
   msg->nargs = 0;
   msg->expectedArgsSz = 0;

   for (i = 0; i < count; ++i) {
      DynBuf *b = DynArray_AddressOf(&msg->args, i);
      DynBuf_Destroy(b);
   }
   DynArray_SetCount(&msg->args, 0);
   DynBufArray_Destroy(&msg->args);
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_Cmd --
 *
 *      Gets the dnd/copy paste command from the header.
 *
 * Results:
 *      An uint32 representing the command.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

uint32
DnDMsg_GetCmd(DnDMsg *msg)      // IN/OUT: the message
{
   ASSERT(msg);

   return msg->cmd;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_SetCmd --
 *
 *      Sets the command for the message.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
DnDMsg_SetCmd(DnDMsg *msg,      // IN/OUT: the message
              uint32 cmd)       // IN: the command
{
   ASSERT(msg);
   ASSERT((DND_INVALID < cmd && cmd < DND_MAX) ||
          (CP_INVALID < cmd && cmd < CP_MAX));

   msg->cmd = cmd;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_NumArgs --
 *
 *      Determines the number of arguments currently in the DnDMsg.
 *
 * Results:
 *      The number of arguments.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

uint32
DnDMsg_NumArgs(DnDMsg *msg)     // IN/OUT: the message
{
   ASSERT(msg);

   return DynBufArray_Count(&msg->args);
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_GetArg --
 *
 *      Gets an argument stored in DnDMsg.
 *
 * Results:
 *      Null if the argument is out of bounds, otherwise a pointer to a dynbuf
 *      containing the argument.
 *       This dynbuf is still
 *      managed by the DnDMsg and should NOT be destroyed.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

DynBuf *
DnDMsg_GetArg(DnDMsg *msg,      // IN/OUT: the message
              uint32 idx)       // IN: the argument to return
{
   ASSERT(msg);
   ASSERT(0 <= idx && idx < DynBufArray_Count(&msg->args));

   return DynArray_AddressOf(&msg->args, idx);
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_AppendArg --
 *
 *      Adds the data to the end of the argument list in the message. It will
 *      create a copy of the data to be mananged by DnDMsg until the message is
 *      destroyed.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      Increases the internal arg size counter.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnDMsg_AppendArg(DnDMsg *msg,   // IN/OUT: the message
                 void *buf,     // IN: the input buffer
                 size_t len)    // IN: the length of the input buffer
{
   DynBuf clonebuf;

   ASSERT(msg);
   ASSERT(buf);

   if (DynBufArray_Count(&msg->args) >= DNDMSG_MAX_ARGS) {
      return FALSE;
   }

   DynBuf_Init(&clonebuf);
   if (!DynBuf_Append(&clonebuf, buf, len)) {
      goto err;
   }

   /* The dynbufarray now owns the clonebuf data. */
   if (!DynBufArray_Push(&msg->args, clonebuf)) {
      goto err;
   }
   return TRUE;

err:
   DynBuf_Destroy(&clonebuf);
   return FALSE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_Serialize --
 *
 *      Serialize the contents of the DnDMsg out to the provided dynbuf. It
 *      will ASSERT if any invariants are broken.
 *
 * Results:
 *      TRUE on success.
 *      FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
DnDMsg_Serialize(DnDMsg *msg,   // IN/OUT: the message
                 DynBuf* buf)   // OUT: the output buffer
{
   uint32 nargs;
   uint32 i;
   uint32 serializeArgsSz = 0;

   ASSERT(msg);
   ASSERT(buf);
   ASSERT((DND_INVALID < msg->cmd && msg->cmd < DND_MAX) ||
          (CP_INVALID < msg->cmd && msg->cmd < CP_MAX));

   nargs = DynBufArray_Count(&msg->args);

   for (i = 0; i < nargs; ++i) {
      DynBuf *b = DynArray_AddressOf(&msg->args, i);
      serializeArgsSz += sizeof(uint32) + DynBuf_GetSize(b);
   }

   if (DynBuf_Append(buf, &msg->ver, sizeof msg->ver) &&
       DynBuf_Append(buf, &msg->cmd, sizeof msg->cmd) &&
       DynBuf_Append(buf, &nargs, sizeof nargs) &&
       DynBuf_Append(buf, &serializeArgsSz, sizeof serializeArgsSz)) {
      int i;
      uint32 curArgsSz;

      for (i = 0; i < nargs; i++) {
         DynBuf *curArg = DynBufArray_AddressOf(&msg->args, i);

         curArgsSz = DynBuf_GetSize(curArg);

         if (!DynBuf_Append(buf, &curArgsSz, sizeof curArgsSz) ||
             !DynBuf_Append(buf, DynBuf_Get(curArg), curArgsSz)) {
            return FALSE;
         }
      }
   } else {
      return FALSE;
   }

   return TRUE;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_UnserializeHeader --
 *
 *      Read the header from the buffer into a DnDMsg. Any contents in the
 *      DnDMsg will be destroyed. This allows you to retrieve header
 *      information. These functions are specified in the dndMsg.h. Most
 *      notably, you can retrieve the size of the arguments so that you can
 *      pass a properly sized buffer to DnDMsg_UnserializeArgs.
 *
 *      This is the one of the two places that nargs is set. The other is
 *      implicitly set by DnDMsg_AppendArg with the push and only ever
 *      realized through the DnDMsg_Serialize function. expectedArgsSz,
 *      curArgSz follows the same idea.
 *
 * Results:
 *      DNDMSG_SUCCESS on success.
 *      DNDMSG_INPUT_TOO_SMALL when provided buffer is too small.
 *      DNDMSG_INPUT_ERR when the provided buffer is inconsistant.
 *      DNDMSG_NOMEM when we run out of memory.
 *      DNDMSG_ERR on any other error.
 *
 * Side effects:
 *      On success the msg's header will be filled. On failure the msg will be
 *      destroyed.
 *
 *----------------------------------------------------------------------------
 */

DnDMsgErr
DnDMsg_UnserializeHeader(DnDMsg *msg,   // IN/OUT: the message
                         void *buf,     // IN: the input buffer
                         size_t len)    // IN: the buffer length
{
   BufRead r;

   ASSERT(msg);
   ASSERT(buf);

   r.pos = buf;
   r.unreadLen = len;

   if (len < DNDMSG_HEADERSIZE_V3) {
      return DNDMSG_INPUT_TOO_SMALL;
   }

   /* Read buffer into msg. */
   if (DnDReadBuffer(&r, &msg->ver, sizeof msg->ver) &&
       DnDReadBuffer(&r, &msg->cmd, sizeof msg->cmd) &&
       DnDReadBuffer(&r, &msg->nargs, sizeof msg->nargs) &&
       DnDReadBuffer(&r, &msg->expectedArgsSz, sizeof msg->expectedArgsSz)) {
      /* Confidence checks. */
      if (msg->expectedArgsSz < DNDMSG_MAX_ARGSZ &&
          (msg->cmd < DND_MAX || msg->cmd < CP_MAX) &&
          0 < msg->cmd &&
          msg->ver >= 3 &&
          msg->nargs < DNDMSG_MAX_ARGS) {
         return DNDMSG_SUCCESS;
      } else {
         return DNDMSG_INPUT_ERR;
      }
   } else {
      return DNDMSG_INPUT_TOO_SMALL;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDMsg_UnserializeArgs --
 *
 *      Unserialize the arguments of the message provided by the buffer.
 *      Each argument is a uint32 of the size followed by the buffer. On
 *      failure the message will revert to the state which was passed into the
 *      function.
 *
 * Results:
 *      DNDMSG_SUCCESS on success.
 *      DNDMSG_INPUT_TOO_SMALL when provided buffer is too small.
 *      DNDMSG_INPUT_ERR when the provided buffer is inconsistant.
 *      DNDMSG_NOMEM when we run out of memory.
 *      DNDMSG_ERR on any other error.
 *
 * Side effects:
 *      On success, arguments found in buf are unserialized into msg.
 *
 *----------------------------------------------------------------------------
 */

DnDMsgErr
DnDMsg_UnserializeArgs(DnDMsg *msg,     // IN/OUT: the message
                       void *buf,       // IN: input buffer
                       size_t len)      // IN: buffer length
{
   uint32 i;
   uint32 count;
   BufRead r;
   uint32 readArgsSz = 0;

   void *data = NULL;
   DnDMsgErr ret = DNDMSG_SUCCESS;

   ASSERT(msg);
   ASSERT(DynBufArray_Count(&msg->args) == 0);
   ASSERT(buf);

   r.pos = buf;
   r.unreadLen = len;

   if (len < msg->expectedArgsSz) {
      return DNDMSG_INPUT_TOO_SMALL;
   }

   for (i = 0; i < msg->nargs; ++i) {
      uint32 argSz;
      if (!DnDReadBuffer(&r, &argSz, sizeof argSz)) {
         ret = DNDMSG_INPUT_TOO_SMALL;
         goto err;
      }

      if (argSz > DNDMSG_MAX_ARGSZ ||
          readArgsSz + sizeof (uint32) + argSz > msg->expectedArgsSz) {
         ret = DNDMSG_INPUT_ERR;
         goto err;
      }

      data = malloc(argSz);
      if (!data) {
         ret = DNDMSG_NOMEM;
         goto err;
      }

      if (!DnDReadBuffer(&r, data, argSz)) {
         ret = DNDMSG_ERR;
         goto err;
      }

      if (!DnDMsg_AppendArg(msg, data, argSz)) {
         ret = DNDMSG_NOMEM;
         goto err;
      }
      readArgsSz += argSz + sizeof (uint32);

      free(data);
      data = NULL;
   }

   ASSERT(ret == DNDMSG_SUCCESS);
   return ret;

err:
   free(data);

   count = DynBufArray_Count(&msg->args);
   for (i = 0; i < count; ++i) {
      DynBuf *b = DynArray_AddressOf(&msg->args, i);
      DynBuf_Destroy(b);
   }
   /*
    * DnDMsg_AppendArg relies on DynBufArray_Push, hence the count needs to be
    * reset.
    */
   DynBufArray_SetCount(&msg->args, 0);

   return ret;
}
   07070100000315000081A4000000000000000000000001682255050000105F000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndMsg.h    /*********************************************************
 * Copyright (c) 2007-2017, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndMsg.h --
 *
 *      DnDMsg represents an rpc message which is sent across the
 *      wire. Any args that it holds will be written out exactly as stored.
 */

#ifndef _DNDMSG_H_
#define _DNDMSG_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#include "vm_basic_types.h"

#include "dynbuf.h"
#include "dynarray.h"
#include "dnd.h"

#if defined(__cplusplus)
extern "C" {
#endif

/* Various return types serialization/unserialization functions can return. */

typedef enum {
   DNDMSG_SUCCESS = 0,
   DNDMSG_ERR,
   DNDMSG_NOMEM,
   DNDMSG_INPUT_TOO_SMALL, /* Input buffer needs to be bigger. */
   DNDMSG_INPUT_ERR, /* Serialize/unserialized failed confidence checks. */
} DnDMsgErr;

/*
 * DnD Commands.
 */

typedef enum {
   DND_INVALID = 0,

   /*
    * We need to send mouse packets for old protocols because old guest tools
    * manipulate the mouse directly, but in DnD Version 3+, the host controls the
    * mouse pointer via foundry.
    */
   /* DnD Ver 1/2 Commands */
   DND_HG_SEND_MOUSE_PACKET,

   /* DnD version 3 commands */
   // GHDnD (h->g)
   DND_GH_QUERY_PENDING_DRAG,
   DND_GH_CANCEL,
   DND_GH_COPY_DONE,
   // GHDnD (g->h)
   DND_GH_DRAG_ENTER,
   DND_GH_NOT_PENDING,

   // HGDnD (h->g)
   DND_HG_DRAG_ENTER,
   DND_HG_DRAG_START,
   DND_HG_CANCEL,
   DND_HG_DROP,
   DND_HG_FILE_COPY_DONE,
   // HGDnD (g->h)
   DND_HG_DRAG_ENTER_DONE,
   DND_HG_DRAG_READY,
   DND_HG_UPDATE_FEEDBACK,
   DND_HG_DROP_DONE,
   DND_HG_START_FILE_COPY,

   // Add future commands here.
   DND_GH_UPDATE_UNITY_DET_WND,

   // New command after DnD version 3.1
   DND_UPDATE_HOST_VERSION,
   DND_UPDATE_GUEST_VERSION,
   DND_UPDATE_MOUSE,
   DND_GH_PRIVATE_DROP,
   DND_GH_TRANSPORT_TEST,
   DND_MOVE_DET_WND_TO_MOUSE_POS,
   DND_GH_SET_CLIPBOARD,
   DND_GH_GET_NEXT_NAME,
   DND_HG_SET_GUEST_FILE_ROOT,
   DND_MAX,
} DnDCommand;

/*
 * Copy/Paste commands.
 */

typedef enum {
   CP_INVALID = 0,

   /* DnD version 3 commands. */
   // GHCopyPaste (h->g)
   CP_GH_GET_CLIPBOARD,
   // GHCopyPaste (g->h)
   CP_GH_GET_CLIPBOARD_DONE, // FORMAT DATA(property list?)

   // HGCopyPaste (h->g)
   CP_HG_SET_CLIPBOARD,
   CP_HG_FILE_COPY_DONE,
   // HGCopyPaste(g->h)
   CP_HG_START_FILE_COPY,

   // Add future commands here.
   CP_GH_TRANSPORT_TEST,
   CP_MAX,
} CopyPasteCommand;

/*
 * Opaque data structure.
 * Members are listed in order of unserialization.
 */

typedef struct DnDMsg {
   /* Header */
   uint8 ver; // Must be first across all versions.
   uint32 cmd;
   uint32 nargs;
   /* The expected size of the buffer needed to unserialize the arguments. */
   uint32 expectedArgsSz;

   /* Body */
   DynBufArray args;
} DnDMsg;

#define DnDMsg_Success(x) ((x) == DNDMSG_SUCCESS)

void DnDMsg_Init(DnDMsg *msg);
void DnDMsg_Destroy(DnDMsg *msg);

/* Header information. */
uint32 DnDMsg_GetCmd(DnDMsg *msg);
void DnDMsg_SetCmd(DnDMsg *msg, uint32 cmd);
uint32 DnDMsg_NumArgs(DnDMsg *msg);

DynBuf *DnDMsg_GetArg(DnDMsg *msg, uint32 arg);

Bool DnDMsg_AppendArg(DnDMsg *msg, void *buf, size_t len);
Bool DnDMsg_Serialize(DnDMsg *msg, DynBuf *buf);

DnDMsgErr DnDMsg_UnserializeHeader(DnDMsg *msg, void *buf, size_t len);
DnDMsgErr DnDMsg_UnserializeArgs(DnDMsg *msg, void *buf, size_t len);

#if defined(__cplusplus)
}  // extern "C"
#endif

#endif /* _DNDMSG_H_ */
 07070100000316000081A40000000000000000000000016822550500001134000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndRpc.hh   /*********************************************************
 * Copyright (C) 2007-2017,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @dndRpc.hh --
 *
 * Rpc layer object for DnD.
 */

#ifndef DND_RPC_HH
#define DND_RPC_HH

#include <sigc++/sigc++.h>
#include <sigc++2to3.h>
#include "dndCPLibExport.hh"
#include "rpcBase.h"

#include "dnd.h"

class LIB_EXPORT DnDRpc
   : public RpcBase
{
public:
   virtual ~DnDRpc(void) {};

   /* sigc signals for DnD source callback. */
   sigc::signal<void, uint32, const CPClipboard *> srcDragBeginChanged;
   sigc::signal<void, uint32> srcCancelChanged;
   sigc::signal<void, uint32, uint32, uint32> srcDropChanged;

   /* sigc signals for DnD destination callback. */
   sigc::signal<void, uint32, int32> destDragEnterReplyChanged;
   sigc::signal<void, uint32> destPrivDragEnterChanged;
   sigc::signal<void, uint32, int32, int32> destPrivDragLeaveChanged;
   sigc::signal<void, uint32, int32, int32> destPrivDropChanged;
   sigc::signal<void, uint32, int32, int32> destDropChanged;
   sigc::signal<void, uint32> destCancelChanged;

   sigc::signal<void, uint32, int32, int32> moveMouseChanged;
   sigc::signal<void, uint32, uint32> updateFeedbackChanged;
   sigc::signal<void, uint32, int32, int32> queryExitingChanged;
   sigc::signal<void, uint32> dragNotPendingChanged;
   sigc::signal<void, uint32, bool, uint32> updateUnityDetWndChanged;
   sigc::signal<void, uint32, const uint8 *, uint32> requestFileChanged;
   sigc::signal<void, uint32, bool, const uint8 *, uint32> getFilesDoneChanged;

   /* sigc signal for responding to ping reply */
   sigc::signal<void, uint32> pingReplyChanged;

   /* sigc signal for rpc cmd reply received. */
   sigc::signal<void, uint32, uint32> cmdReplyChanged;

   /* DnD source. */
   virtual bool SrcDragBeginDone(uint32 sessionId) = 0;
   virtual bool SrcDrop(uint32 sessionId, int32 x, int32 y) = 0;
   virtual bool SrcDropDone(uint32 sessionId,
                            const uint8 *stagingDirCP,
                            uint32 sz) = 0;

   virtual bool SrcPrivDragEnter(uint32 sessionId) = 0;
   virtual bool SrcPrivDragLeave(uint32 sessionId, int32 x, int32 y) = 0;
   virtual bool SrcPrivDrop(uint32 sessionId, int32 x, int32 y) = 0;
   virtual bool SrcCancel(uint32 sessionId) = 0;

   /* DnD destination. */
   virtual bool DestDragEnter(uint32 sessionId,
                              const CPClipboard *clip) = 0;
   virtual bool DestSendClip(uint32 sessionId,
                             const CPClipboard *clip) = 0;
   virtual bool DestDragLeave(uint32 sessionId, int32 x, int32 y) = 0;
   virtual bool DestDrop(uint32 sessionId, int32 x, int32 y) = 0;
   virtual bool DestCancel(uint32 sessionId) = 0;

   /* Common. */
   virtual void Init(void) = 0;
   virtual void SendPing(uint32 caps) = 0;
   virtual bool UpdateFeedback(uint32 sessionId, DND_DROPEFFECT feedback) = 0;
   virtual bool MoveMouse(uint32 sessionId,
                          int32 x,
                          int32 y) = 0;
   virtual bool QueryExiting(uint32 sessionId, int32 x, int32 y) = 0;
   virtual bool DragNotPending(uint32 sessionId) = 0;
   virtual bool UpdateUnityDetWnd(uint32 sessionId,
                                  bool show,
                                  uint32 unityWndId) = 0;
   virtual bool RequestFiles(uint32 sessionId) = 0;
   virtual bool SendFilesDone(uint32 sessionId,
                              bool success,
                              const uint8 *stagingDirCP,
                              uint32 sz) = 0;
   virtual bool GetFilesDone(uint32 sessionId,
                             bool success) = 0;
};

#endif // DND_RPC_HH
07070100000317000081A40000000000000000000000016822550500000F7E000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndRpcV4.hh /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @dndRpcV4.hh --
 *
 * Rpc layer object for DnD version 4.
 */

#ifndef DND_RPC_V4_HH
#define DND_RPC_V4_HH

#include <sigc++/trackable.h>
#include "dndRpc.hh"
#include "dndCPTransport.h"
#include "rpcV4Util.hpp"

#include "dnd.h"
#include "dndMsg.h"

extern "C" {
   #include "dndCPMsgV4.h"
}

class LIB_EXPORT DnDRpcV4
   : public DnDRpc,
     public sigc::trackable
{
public:
   DnDRpcV4(DnDCPTransport *transport);

   virtual void Init(void);
   virtual void SendPing(uint32 caps);

   /* DnD source. */
   virtual bool SrcDragBeginDone(uint32 sessionId);
   virtual bool SrcDrop(uint32 sessionId, int32 x, int32 y);
   virtual bool SrcDropDone(uint32 sessionId, const uint8 *stagingDirCP, uint32 sz);

   virtual bool SrcPrivDragEnter(uint32 sessionId);
   virtual bool SrcPrivDragLeave(uint32 sessionId, int32 x, int32 y);
   virtual bool SrcPrivDrop(uint32 sessionId, int32 x, int32 y);
   virtual bool SrcCancel(uint32 sessionId);

   /* DnD destination. */
   virtual bool DestDragEnter(uint32 sessionId,
                              const CPClipboard *clip);
   virtual bool DestSendClip(uint32 sessionId,
                             const CPClipboard *clip);
   virtual bool DestDragLeave(uint32 sessionId,
                              int32 x,
                              int32 y);
   virtual bool DestDrop(uint32 sessionId,
                         int32 x,
                         int32 y);
   virtual bool DestCancel(uint32 sessionId);

   /* Common. */
   virtual bool UpdateFeedback(uint32 sessionId, DND_DROPEFFECT feedback);
   virtual bool MoveMouse(uint32 sessionId,
                          int32 x,
                          int32 y);
   virtual bool QueryExiting(uint32 sessionId, int32 x, int32 y);
   virtual bool DragNotPending(uint32 sessionId);
   virtual bool UpdateUnityDetWnd(uint32 sessionId,
                                  bool bShow,
                                  uint32 unityWndId);
   virtual bool RequestFiles(uint32 sessionId);
   virtual bool SendFilesDone(uint32 sessionId,
                              bool success,
                              const uint8 *stagingDirCP,
                              uint32 sz);
   virtual bool GetFilesDone(uint32 sessionId,
                             bool success);

   virtual void HandleMsg(RpcParams *params,
                          const uint8 *binary,
                          uint32 binarySize);
   virtual bool SendPacket(uint32 destId,
                           const uint8 *packet,
                           size_t length);
   virtual void OnRecvPacket(uint32 srcId,
                             const uint8 *packet,
                             size_t packetSize);

   void AddRpcReceivedListener(DnDRpcListener *obj);
   void RemoveRpcReceivedListener(DnDRpcListener *obj);
   void AddRpcSentListener(DnDRpcListener *obj);
   void RemoveRpcSentListener(DnDRpcListener *obj);

   void SetMaxTransportPacketSize(const uint32 size);

private:
   DnDCPTransport *mTransport;
   TransportInterfaceType mTransportInterface;
   RpcV4Util mUtil;
};

#endif // DND_RPC_V4_HH
  07070100000318000081A40000000000000000000000016822550500003003000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/dndXdg.c    /*********************************************************
 * Copyright (C) 2014-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndXdg.c --
 *
 *      Drag and drop routines for X Desktop Group (XDG) / freedesktop.org
 *      platforms.
 */


#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <stdlib.h>

#if defined __FreeBSD__
#include <unistd.h>
#endif

#include "dndInt.h"
#include "dnd.h"

#include "err.h"
#include "file.h"
#include "log.h"
#include "posix.h"
#include "str.h"
#include "strutil.h"
#include "su.h"
#include "unicode.h"
#include "vm_atomic.h"


static const char *CreateRealRootDirectory(void);
static const char *CreateApparentRootDirectory(void);
static char *FindSuitableExistingDirectory(const char *realRoot,
                                           const char *apparentRoot);
static char *CreateStagingDirectory(const char *realRoot,
                                    const char *apparentRoot);


/*
 *-----------------------------------------------------------------------------
 *
 * Xdg_GetCacheHome --
 *
 *      Determine path appropriate for "user-specific non-essential (cached)
 *      data"[1].
 *
 *      1. <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>
 *
 * Results:
 *      Returns a pointer to a valid path on success or NULL on failure.
 *
 * Side effects:
 *      First call may allocate memory which remains allocated for the life
 *      of the program.
 *
 *      NOT thread-safe.
 *
 * TODO:
 *      Move bora-vmsoft/lib/xdg to bora/lib, then move this to bora/lib/xdg.
 *      Unit tests.  (There is no good home for these at the moment.)
 *
 *-----------------------------------------------------------------------------
 */

const char *
Xdg_GetCacheHome(void)
{
   static char *result = NULL;

   if (result == NULL) {
      do {
         struct passwd *pw;

         if (!Id_IsSetUGid()) {
            const char *base = NULL;

            /*
             * Paranoia:  Avoid environment variables if running in a sensitive
             *            context.  sudo or other loader should've sanitized the
             *            environment, but, well, we're paranoid, remember?
             */

            // 1. $XDG_CACHE_HOME
            base = Posix_Getenv("XDG_CACHE_HOME");
            if (Util_IsAbsolutePath(base)) {
               result = Util_SafeStrdup(base);
               break;
            }

            // 2. $HOME/.cache
            base = Posix_Getenv("HOME");
            if (Util_IsAbsolutePath(base)) {
               result = Util_SafeStrdup(base);
               StrUtil_SafeStrcat(&result, "/.cache");
               break;
            }
         }

         // 3. <pw_dir>/.cache
         pw = Posix_Getpwuid(geteuid());

         if (pw != NULL && Util_IsAbsolutePath(pw->pw_dir)) {
            result = Str_Asprintf(NULL, "%s/.cache", pw->pw_dir);
         }
      } while(0);
   }

   VERIFY(result == NULL || result[0] == '/');
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnD_CreateStagingDirectory --
 *
 *    See dndCommon.c.
 *
 * Results:
 *    A string containing the newly created name, or NULL on failure.
 *
 * Side effects:
 *    A directory is created
 *
 *    Caller is responsible for freeing returned pointer.
 *
 *    NOT thread-safe.
 *
 *-----------------------------------------------------------------------------
 */

char *
DnD_CreateStagingDirectory(void)
{
   const char *realRoot;
   const char *apparentRoot;
   char *existingDir;

   /*
    * On XDG platforms, there are two roots:
    *
    * 1. Per-user real root ($HOME/.cache/vmware/drag_and_drop)
    *
    *    Files are stored here, leaving cleanup to discretion of users and
    *    adminisitrators, and may count against users' storage quotas.  Most
    *    importantly, however, is that it means avoiding tmpfs-backed /tmp
    *    which may be too small for large files.
    *
    * 2. Apparent root - /tmp/VMwareDnD
    *
    *    Path known to vmblock implementations.  Contains only symlinks to
    *    users' real root.
    *
    * Therefore DnD targets may access paths via
    *    /var/run/vmblock (vmblock file system) ->
    *    /tmp/VMwareDnD   (apparent root) ->
    *    $HOME/.cache/vmware/drag_and_drop (real root).
    */


   /*
    * Lookup or create real root.
    *
    * We don't worry about cleaning up or removing this directory in case
    * of failure later in this function.
    */

   realRoot = CreateRealRootDirectory();
   if (realRoot == NULL) {
      return NULL;
   }


   /*
    * Lookup or create apparent root.
    */

   apparentRoot = CreateApparentRootDirectory();
   if (apparentRoot == NULL) {
      return NULL;
   }


   /*
    * Search real root for empty directory.  If found, (re)use it.
    */

   existingDir = FindSuitableExistingDirectory(realRoot, apparentRoot);
   if (existingDir != NULL) {
      return existingDir;
   }


   /*
    * Generate new temporary directory.
    *    - Attempt to symlink $apparent/XXXXXX to $real/XXXXXX.
    *    - mkdir -p $real/XXXXXX.
    */

   return CreateStagingDirectory(realRoot, apparentRoot);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DetermineRealRootDirectory --
 * CreateRealRootDirectory --
 *
 *      Produces the path of the real staging directory root (e.g.
 *      $HOME/.cache/vmware/drag_and_drop).
 *
 * Results:
 *      !NULL   Success. Points to real staging root path.
 *      NULL    Failure.
 *
 * Side effects:
 *      May create the .cache/vmware/drag_and_drop hierarchy.
 *
 *-----------------------------------------------------------------------------
 */

static const char *
DetermineRealRootDirectory(void)
{
   static char *completePath;
   const char *cachePath;

   if (completePath != NULL) {
      return completePath;
   }

   cachePath = Xdg_GetCacheHome();
   if (cachePath != NULL) {
      const char dndSuffix[] = "/vmware/drag_and_drop/";

      completePath = Unicode_Duplicate(cachePath);
      StrUtil_SafeStrcat(&completePath, dndSuffix);
      VERIFY(strlen(completePath) < PATH_MAX);

      Log_Trivia("dnd: will stage to %s\n", completePath);
      return completePath;
   }

   Log_Trivia("dnd: failed to determine path\n");
   return NULL;
}

static const char *
CreateRealRootDirectory(void)
{
   const char *realRoot = DetermineRealRootDirectory();

   if (   realRoot != NULL
       && (   File_IsDirectory(realRoot)
           || File_CreateDirectoryHierarchyEx(realRoot, 0700, NULL))) {
      return realRoot;
   }

   return NULL;
}


/*
 *----------------------------------------------------------------------------
 *
 * CreateApparentRootDirectory --
 *
 *    Checks if the root staging directory exists with the correct permissions,
 *    or creates it if necessary.
 *
 * Results:
 *    !NULL     Success. String containing apparent root directory path.
 *    NULL      failure.
 *
 * Side effects:
 *    May create apparent root (/tmp/VMwareDnD) and/or change its permissions.
 *
 *----------------------------------------------------------------------------
 */

static const char *
CreateApparentRootDirectory(void)
{
   const char *root;

   /*
    * DnD_GetFileRoot() gives us a pointer to a static string, so there's no
    * need to free anything.
    *
    * XXX On XDG platforms this path ("/tmp/VMwareDnD") is created by an
    *     init script, so we could remove some of the code below and just bail
    *     if the user deletes it.
    */

   root = DnD_GetFileRoot();
   if (!root) {
      return NULL;
   }

   if (File_Exists(root)) {
      if (!DnDRootDirUsable(root)) {
         /*
          * The directory already exists and its permissions are wrong.
          */
         Log_Trivia("dnd: The root dir is not usable.\n");
         return NULL;
      }
   } else {
      if (   !File_CreateDirectory(root)
          || !DnDSetPermissionsOnRootDir(root)) {
         /* We couldn't create the directory or set the permissions. */
         return NULL;
      }
   }

   return root;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FindSuitableExistingDirectory --
 *
 *      Given the pair of <realRoot, apparentRoot>, search for an existing
 *      directory within `realRoot` satisfying the following conditions:
 *
 *         1. it's empty;
 *         2. it's pointed to by a symlink of the same name from apparentRoot.
 *
 * Results:
 *      !NULL   Success.  Caller must free result.
 *      NULL    Failure.
 *
 * Side effects:
 *      May create a symlink.
 *
 *-----------------------------------------------------------------------------
 */

static char *
FindSuitableExistingDirectory(
   const char *realRoot,       // IN: e.g. $HOME/.cache/vmware/drag_and_drop/
   const char *apparentRoot)   // IN: e.g. /tmp/VMwareDnD/
{
   char *result = NULL;

   char **stagingDirList;
   int numStagingDirs = File_ListDirectory(realRoot, &stagingDirList);
   int i;

   for (i = 0; i < numStagingDirs && result == NULL; i++) {
      char *stagingDir = Unicode_Append(realRoot, stagingDirList[i]);
      char *apparentStagingDir = Unicode_Append(apparentRoot, stagingDirList[i]);
      char *temp = NULL;
      struct stat sb;

      if (   File_IsEmptyDirectory(stagingDir)
          && (   Posix_Symlink(stagingDir, apparentStagingDir) == 0
              || (   Posix_Lstat(apparentStagingDir, &sb) == 0
                  && sb.st_uid == getuid()
                  && (temp = Posix_ReadLink(apparentStagingDir)) != NULL
                  && strcmp(stagingDir, temp) == 0))) {
         result = apparentStagingDir;
         apparentStagingDir = NULL;
      }

      free(stagingDir);
      free(apparentStagingDir);
      free(temp);
   }

   Util_FreeStringList(stagingDirList, numStagingDirs);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CreateStagingDirectory --
 *
 *      Creates a fresh staging directory within `realRoot` and creates a
 *      symlink to it within `apparentRoot`.
 *
 * Results:
 *      !NULL   Success.  Caller must free result.
 *      NULL    Failure.
 *
 * Side effects:
 *      Creates directories and stuff.
 *
 *-----------------------------------------------------------------------------
 */

static char *
CreateStagingDirectory(
   const char *realRoot,       // IN: e.g. $HOME/.cache/vmware/drag_and_drop/
   const char *apparentRoot)   // IN: e.g. /tmp/VMwareDnD/
{
   char *result = NULL;
   int i;

   for (i = 0; i < 10 && result == NULL; i++) {
      char *apparentStagingDir = NULL;
      // Reminder: mkdtemp updates its arg in-place.
      char *realStagingDir = Str_SafeAsprintf(NULL, "%sXXXXXX", realRoot);

      if (mkdtemp(realStagingDir) != NULL) {
         char *randomPart = strrchr(realStagingDir, '/') + 1;
         VERIFY(*randomPart != '\0');

         apparentStagingDir = Unicode_Append(apparentRoot, randomPart);

         if (Posix_Symlink(realStagingDir, apparentStagingDir) == 0) {
            // Transfer ownership to caller.
            result = apparentStagingDir;
            apparentStagingDir = NULL;
         } else {
            Warning("dnd: symlink(%s): %s", apparentStagingDir, Err_ErrString());
            Posix_Rmdir(realStagingDir);
         }
      } else {
         Warning("dnd: mkdtemp(%s): %s", realStagingDir, Err_ErrString());
      }

      free(realStagingDir);
      free(apparentStagingDir);
   }

   return result;
}
 07070100000319000081A400000000000000000000000168225505000006F8000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/fileTransferRpc.hh  /*********************************************************
 * Copyright (C) 2010-2017,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @fileTransferRpc.hh --
 *
 * File transfer roc object for DnD/CopyPaste.
 */

#ifndef FILE_TRANSFER_RPC_HH
#define FILE_TRANSFER_RPC_HH

#include <sigc++/sigc++.h>
#include <sigc++2to3.h>
#include "dndCPLibExport.hh"
#include "rpcBase.h"

#include "vm_basic_types.h"

class LIB_EXPORT FileTransferRpc
   : public RpcBase
{
public:
   virtual ~FileTransferRpc(void) {};

   sigc::signal<void, uint32, const uint8 *, size_t> HgfsPacketReceived;
   sigc::signal<void, uint32, const uint8 *, size_t> HgfsReplyReceived;

   virtual void Init(void) = 0;
   virtual bool SendHgfsPacket(uint32 sessionId,
                               const uint8 *packet,
                               uint32 packetSize) = 0;
   virtual bool SendHgfsReply(uint32 sessionId,
                              const uint8 *packet,
                              uint32 packetSize) = 0;
};

#endif // FILE_TRANSFER_RPC_HH
0707010000031A000081A400000000000000000000000168225505000008A2000000000000000000000000000000000000005300000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/fileTransferRpcV4.hh    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @fileTransferRpcV4.hh --
 *
 * File transfer rpc version 4 object for DnD/CopyPaste.
 */

#ifndef FILE_TRANSFER_RPC_V4_HH
#define FILE_TRANSFER_RPC_V4_HH

#include "fileTransferRpc.hh"
#include "dndCPTransport.h"
#include "rpcV4Util.hpp"

extern "C" {
   #include "dndCPMsgV4.h"
}

class LIB_EXPORT FileTransferRpcV4
   : public FileTransferRpc,
     public sigc::trackable
{
public:
   FileTransferRpcV4(DnDCPTransport *transport);

   virtual void Init(void);

   virtual bool SendHgfsPacket(uint32 sessionId,
                               const uint8 *packet,
                               uint32 packetSize);
   virtual bool SendHgfsReply(uint32 sessionId,
                              const uint8 *packet,
                              uint32 packetSize);
   virtual void HandleMsg(RpcParams *params,
                          const uint8 *binary,
                          uint32 binarySize);
   virtual bool SendPacket(uint32 destId,
                           const uint8 *packet,
                           size_t length);
   virtual void OnRecvPacket(uint32 srcId,
                             const uint8 *packet,
                             size_t packetSize);

private:
   DnDCPTransport *mTransport;
   TransportInterfaceType mTransportInterface;
   RpcV4Util mUtil;
};

#endif // FILE_TRANSFER_RPC_V4_HH
  0707010000031B000081A40000000000000000000000016822550500000B61000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/rpcBase.h   /*********************************************************
 * Copyright (C) 2010-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * rpcBase.h --
 *
 *     Rpc layer object for DnD/CP.
 */

#ifndef RPC_BASE_H
#define RPC_BASE_H

#ifndef LIB_EXPORT
#define LIB_EXPORT
#endif

#include "vm_basic_types.h"

typedef struct RpcParams {
   uint32 addrId;          /* Destination address id. */
   uint32 cmd;             /* DnD/CP message command. */
   uint32 sessionId;       /* DnD/CP session ID. */
   uint32 status;          /* Status for last operation. */
   union {
      struct {
         uint32 major;
         uint32 minor;
         uint32 capability;
      } version;

      struct {
         uint32 x;
         uint32 y;
      } mouseInfo;

      struct {
         uint32 cmd;
      } replyToCmd;

      struct {
         uint32 cmd;
         uint32 binarySize;
         uint32 payloadOffset;
      } requestNextCmd;

      struct {
         uint32 feedback;
      } feedback;

      struct {
         uint32 major;
         uint32 minor;
         uint32 capability;
         uint32 x;
         uint32 y;
      } queryExiting;

      struct {
         uint32 major;
         uint32 minor;
         uint32 capability;
         uint32 show;
         uint32 unityWndId;
      } updateUnityDetWnd;

      struct {
         uint32 major;
         uint32 minor;
         uint32 capability;
         uint32 isActive;
      } cpInfo;

      struct {
         uint32 param1;
         uint32 param2;
         uint32 param3;
         uint32 param4;
         uint32 param5;
         uint32 param6;
      } genericParams;
   } optional;
} RpcParams;


class LIB_EXPORT RpcBase {
public:
   virtual ~RpcBase(void) {};
   virtual void OnRecvPacket(uint32 srcId,
                             const uint8 *packet,
                             size_t packetSize) = 0;
   virtual bool SendPacket(uint32 destId,
                           const uint8 *packet,
                           size_t length) = 0;
   virtual void HandleMsg(RpcParams *params,
                          const uint8 *binary,
                          uint32 binarySize) = 0;
};
#endif // RPC_BASE_H
   0707010000031C000081A40000000000000000000000016822550500000786000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dnd/rpcV3Util.hpp   /*********************************************************
 * Copyright (C) 2010-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @rpcV3Util.hpp --
 *
 * Rpc layer object for DnD version 4.
 */

#ifndef RPC_V3_UTIL_HPP
#define RPC_V3_UTIL_HPP

#ifndef LIB_EXPORT
#define LIB_EXPORT
#endif

#include "rpcBase.h"
#include "dnd.h"

struct DnDMsg;

class LIB_EXPORT RpcV3Util
{
public:
   RpcV3Util(void);
   virtual ~RpcV3Util(void);

   void Init(RpcBase *rpc);

   void OnRecvPacket(uint32 srcId,
                     const uint8 *packet,
                     size_t packetSize);
   bool SendMsg(uint32 cmd);
   bool SendMsg(uint32 cmd,
                const CPClipboard *clip);
   bool SendMsg(uint32 cmd, int32 x, int32 y); // For cmd with mouse info.
   bool SendMsg(const DnDMsg *msg);
   uint32 GetVersionMajor(void) { return mVersionMajor; }
   uint32 GetVersionMinor(void) { return mVersionMinor; }

private:
   bool SendMsg(const uint8 *binary,
                uint32 binarySize);
   RpcBase *mRpc;
   uint32 mVersionMajor;
   uint32 mVersionMinor;
   DnDTransportBuffer mSendBuf;
   DnDTransportBuffer mRecvBuf;
   uint32 mSeqNum;
};

#endif // RPC_V3_UTIL_HPP
  0707010000031D000081A40000000000000000000000016822550500000A5B000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndFileList.hh  /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndFileList.hh
 *
 *      Translates filelists to different formats.
 */

#ifndef DND_FILELIST_HH
#define DND_FILELIST_HH

#include <string>
#include <vector>

#include "vm_basic_types.h"

extern "C" {
#include "dndClipboard.h"
}

#include "dynbuf.h"

class DnDFileList {
   public:
      DnDFileList();
      ~DnDFileList() {};

      void SetFileSize(uint64 fsize);
      uint64 GetFileSize() const;
      void AddFile(const std::string fullPath,
                   const std::string relPath);
      void AddFileUri(const std::string uriPath);
      void AddFiles(const std::vector<std::string> fullPathList,
                    const std::vector<std::string> relPathList);
      void AddFileAttributes(const CPFileAttributes& attributes);

      /* Copy paste/dndV2 V2 rpc */
      void SetRelPathsStr(const std::string inpath);

      /* DnDFileTransfer & V2 RPC */
      std::string GetRelPathsStr() const;
      std::string GetFullPathsStr(bool local) const;
      std::string GetUriPathsStr() const;

      /* UI Local clipboard */
      std::vector<std::string> GetRelPaths() const;
      std::vector<CPFileAttributes> GetFileAttributes() const;

      /* CPClipboard */
      bool ToCPClipboard(DynBuf *out, bool local) const;
      bool ToUriClipboard(DynBuf *out) const;
      bool AttributesToCPClipboard(DynBuf *out) const;
      bool FromCPClipboard(const void *buf, size_t len);
      bool AttributesFromCPClipboard(const void *buf, size_t len);

      void Clear();

   private:

      std::vector<std::string> mRelPaths;
      std::vector<std::string> mFullPaths;
      std::vector<std::string> mUriPaths;
      std::vector<CPFileAttributes> mAttributeList;
      std::string mFullPathsBinary;
      uint64 mFileSize;
};

#endif // DND_FILELIST_HH
 0707010000031E000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest    0707010000031F000081A40000000000000000000000016822550500001DAA000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/copyPasteRpcV3.cc  /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * CopyPasteRpcV3.cc --
 *
 *     Implementation of the CopyPasteRpcV3 interface.
 */


#include "copyPasteRpcV3.hh"
#include "tracer.hh"
#include "dndMsg.h"

extern "C" {
   #include "debug.h"
   #include "dndClipboard.h"
}

/**
 * Constructor.
 *
 * @param[in] transport for sending packets.
 */

CopyPasteRpcV3::CopyPasteRpcV3(DnDCPTransport *transport)
   : mTransport(transport),
     mTransportInterface(TRANSPORT_GUEST_CONTROLLER_CP)
{
   ASSERT(mTransport);

   mUtil.Init(this);
}


/**
 * Destructor.
 */

CopyPasteRpcV3::~CopyPasteRpcV3(void)
{
}


/**
 * Init.
 */

void
CopyPasteRpcV3::Init(void)
{
   TRACE_CALL();
   ASSERT(mTransport);
   mTransport->RegisterRpc(this, mTransportInterface);
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] caps capabilities mask
 */

void
CopyPasteRpcV3::SendPing(uint32 caps)
{
   TRACE_CALL();
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned
 * @param[ignored] isActive active or passive CopyPaste
 *
 * @return always true.
 */

bool
CopyPasteRpcV3::SrcRequestClip(uint32 sessionId,
                               bool isActive)
{
   TRACE_CALL();
   return true;
}


/**
 * Send cmd CP_GH_GET_CLIPBOARD_DONE to controller.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] isActive active or passive CopyPaste
 * @param[in] clip cross-platform clipboard data.
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV3::DestSendClip(uint32 sessionId,
                             bool isActive,
                             const CPClipboard* clip)
{
   TRACE_CALL();
   return mUtil.SendMsg(CP_GH_GET_CLIPBOARD_DONE, clip);
}


/**
 * Send cmd CP_HG_START_FILE_COPY to controller.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[in] stagingDirCP staging dir name in cross-platform format
 * @param[in] sz the staging dir name size in bytes
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV3::RequestFiles(uint32 sessionId,
                             const uint8 *stagingDirCP,
                             uint32 sz)
{
   DnDMsg msg;
   bool ret = false;

   TRACE_CALL();

   DnDMsg_Init(&msg);

   /* Construct msg with both cmd CP_HG_START_FILE_COPY and stagingDirCP. */
   DnDMsg_SetCmd(&msg, CP_HG_START_FILE_COPY);
   if (!DnDMsg_AppendArg(&msg, (void *)stagingDirCP, sz)) {
      g_debug("%s: DnDMsg_AppendData failed.\n", __FUNCTION__);
      goto exit;
   }

   ret = mUtil.SendMsg(&msg);

exit:
   DnDMsg_Destroy(&msg);
   return ret;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] success the file transfer operation was successful or not
 * @param[ignored] stagingDirCP staging dir name in cross-platform format
 * @param[ignored] sz the staging dir name size in bytes
 *
 * @return always true.
 */

bool
CopyPasteRpcV3::SendFilesDone(uint32 sessionId,
                              bool success,
                              const uint8 *stagingDirCP,
                              uint32 sz)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] success the file transfer operation was successful or not
 *
 * @return always true.
 */

bool
CopyPasteRpcV3::GetFilesDone(uint32 sessionId,
                             bool success)
{
   TRACE_CALL();
   return true;
}


/**
 * Send a packet.
 *
 * @param[in] destId destination address id.
 * @param[in] packet
 * @param[in] length packet length in byte
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV3::SendPacket(uint32 destId,
                           const uint8 *packet,
                           size_t length)
{
   TRACE_CALL();
   return mTransport->SendPacket(destId,
                                 mTransportInterface,
                                 packet,
                                 length);
}


/**
 * Handle a received message.
 *
 * @param[in] params parameter list for received message.
 * @param[in] binary attached binary data for received message.
 * @param[in] binarySize in byte
 */

void
CopyPasteRpcV3::HandleMsg(RpcParams *params,
                          const uint8 *binary,
                          uint32 binarySize)
{
   DnDMsg msg;
   DnDMsgErr ret;
   DynBuf *buf = NULL;

   DnDMsg_Init(&msg);

   ret = DnDMsg_UnserializeHeader(&msg, (void *)binary, binarySize);
   if (DNDMSG_SUCCESS != ret) {
      g_debug("%s: DnDMsg_UnserializeHeader failed %d\n", __FUNCTION__, ret);
      goto exit;
   }

   ret = DnDMsg_UnserializeArgs(&msg,
                                (void *)(binary + DNDMSG_HEADERSIZE_V3),
                                binarySize - DNDMSG_HEADERSIZE_V3);
   if (DNDMSG_SUCCESS != ret) {
      g_debug("%s: DnDMsg_UnserializeArgs failed with %d\n", __FUNCTION__, ret);
      goto exit;
   }

   g_debug("%s: Got %d, binary size %d.\n", __FUNCTION__, DnDMsg_GetCmd(&msg),
           binarySize);

   /*
    * Translate command and emit signal. Session Id 1 is used because version
    * 3 command does not provide session Id.
    */
   switch (DnDMsg_GetCmd(&msg)) {
   case CP_HG_SET_CLIPBOARD:
   {
      CPClipboard clip;

      /* Unserialize clipboard data for the command. */
      buf = DnDMsg_GetArg(&msg, 0);
      CPClipboard_Init(&clip);
      if (!CPClipboard_Unserialize(&clip, DynBuf_Get(buf), DynBuf_GetSize(buf))) {
         g_debug("%s: CPClipboard_Unserialize failed.\n", __FUNCTION__);
         goto exit;
      }
      srcRecvClipChanged.emit(1, false, &clip);
      CPClipboard_Destroy(&clip);
      break;
   }
   case CP_HG_FILE_COPY_DONE:
   {
      bool success = false;
      buf = DnDMsg_GetArg(&msg, 0);
      if (sizeof success == DynBuf_GetSize(buf)) {
         memcpy(&success, DynBuf_Get(buf), DynBuf_GetSize(buf));
      }
      getFilesDoneChanged.emit(1, success, NULL, 0);
      break;
   }
   case CP_GH_GET_CLIPBOARD:
   {
      destRequestClipChanged.emit(1, false);
      break;
   }
   default:
      g_debug("%s: got unsupported new command %d.\n", __FUNCTION__,
              DnDMsg_GetCmd(&msg));
   }
exit:
   DnDMsg_Destroy(&msg);
}


/**
 * Callback from transport layer after received a packet from srcId.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 */

void
CopyPasteRpcV3::OnRecvPacket(uint32 srcId,
                             const uint8 *packet,
                             size_t packetSize)
{
   TRACE_CALL();
   mUtil.OnRecvPacket(srcId, packet, packetSize);
}
  07070100000320000081A40000000000000000000000016822550500000AA1000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/copyPasteRpcV3.hh  /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file copyPasteRpcV3.hh --
 *
 * Rpc layer object for CopyPaste version 3.
 */

#ifndef COPY_PASTE_RPC_V3_HH
#define COPY_PASTE_RPC_V3_HH

#include <sigc++/trackable.h>
#include "copyPasteRpc.hh"
#include "dndCPTransport.h"
#include "rpcV3Util.hpp"

extern "C" {
   #include "vmware/tools/guestrpc.h"
}

#include "dnd.h"
#include "dndMsg.h"

class CopyPasteRpcV3
   : public CopyPasteRpc,
     public sigc::trackable
{
public:
   CopyPasteRpcV3(DnDCPTransport *transport);
   virtual ~CopyPasteRpcV3(void);

   virtual void Init(void);
   virtual void SendPing(uint32 caps);

   /* CopyPaste Rpc functions. */
   virtual bool SrcRequestClip(uint32 sessionId,
                               bool isActive);
   virtual bool DestSendClip(uint32 sessionId,
                             bool isActive,
                             const CPClipboard* clip);
   virtual bool RequestFiles(uint32 sessionId,
                             const uint8 *stagingDirCP,
                             uint32 sz);
   virtual bool SendFilesDone(uint32 sessionId,
                              bool success,
                              const uint8 *stagingDirCP,
                              uint32 sz);
   virtual bool GetFilesDone(uint32 sessionId,
                             bool success);
   virtual void HandleMsg(RpcParams *params,
                          const uint8 *binary,
                          uint32 binarySize);
   virtual bool SendPacket(uint32 destId,
                           const uint8 *packet,
                           size_t length);
   virtual void OnRecvPacket(uint32 srcId,
                             const uint8 *packet,
                             size_t packetSize);

private:
   DnDCPTransport *mTransport;
   TransportInterfaceType mTransportInterface;
   RpcV3Util mUtil;
};

#endif // COPY_PASTE_RPC_V3_HH
   07070100000321000081A4000000000000000000000001682255050000221B000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/copyPasteRpcV4.cc  /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @copyPasteRpcV4.cc --
 *
 * Rpc layer object for CopyPaste version 4.
 */

#include "copyPasteRpcV4.hh"

extern "C" {
#if defined VMX86_TOOLS
   #include "debug.h"

   #define LOG(level, ...) Debug(__VA_ARGS__)
#else
   #define LOGLEVEL_MODULE dnd
   #include "loglevel_user.h"
#endif

   #include "dndClipboard.h"
}

#include "util.h"


/**
 * Constructor.
 *
 * @param[in] transport for sending packets.
 */

CopyPasteRpcV4::CopyPasteRpcV4(DnDCPTransport *transport)
   : mTransport(transport),
#ifdef VMX86_TOOLS
     mTransportInterface(TRANSPORT_GUEST_CONTROLLER_CP)
#else
     mTransportInterface(TRANSPORT_HOST_CONTROLLER_CP)
#endif
{
   ASSERT(mTransport);

#ifdef VMX86_TOOLS
   mUtil.Init(this, DND_CP_MSG_SRC_GUEST, DND_CP_MSG_TYPE_CP);
#else
   mUtil.Init(this, DND_CP_MSG_SRC_HOST, DND_CP_MSG_TYPE_CP);
#endif
}


/**
 * Init.
 */

void
CopyPasteRpcV4::Init()
{
   ASSERT(mTransport);
   mTransport->RegisterRpc(this, mTransportInterface);
}


/**
 * Send Ping message to controller.
 *
 * @param[in] caps capabilities value.
 */

void
CopyPasteRpcV4::SendPing(uint32 caps)
{
   mUtil.SendPingMsg(DEFAULT_CONNECTION_ID, caps);
}


/**
 * Send cmd CP_CMD_REQUEST_CLIPBOARD to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] isActive active or passive CopyPaste
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV4::SrcRequestClip(uint32 sessionId,
                               bool isActive)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = CP_CMD_REQUEST_CLIPBOARD;
   params.sessionId = sessionId;
   params.optional.cpInfo.major = mUtil.GetVersionMajor();
   params.optional.cpInfo.minor = mUtil.GetVersionMinor();
   params.optional.cpInfo.isActive = isActive;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd CP_CMD_SEND_CLIPBOARD to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] isActive active or passive CopyPaste
 * @param[in] clip cross-platform clipboard data.
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV4::DestSendClip(uint32 sessionId,
                             bool isActive,
                             const CPClipboard* clip)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = CP_CMD_SEND_CLIPBOARD;
   params.sessionId = sessionId;
   params.optional.cpInfo.major = mUtil.GetVersionMajor();
   params.optional.cpInfo.minor = mUtil.GetVersionMinor();
   params.optional.cpInfo.isActive = isActive;

   return mUtil.SendMsg(&params, clip);
}


/**
 * Send cmd CP_CMD_REQUEST_FILE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] stagingDirCP staging dir name in cross-platform format
 * @param[in] sz the staging dir name size in bytes
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV4::RequestFiles(uint32 sessionId,
                             const uint8 *stagingDirCP,
                             uint32 sz)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = CP_CMD_REQUEST_FILES;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params, stagingDirCP, sz);
}


/**
 * Send cmd CP_CMD_SEND_FILES_DONE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] success the file transfer operation was successful or not
 * @param[in] stagingDirCP staging dir name in cross-platform format
 * @param[in] sz the staging dir name size in bytes
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV4::SendFilesDone(uint32 sessionId,
                              bool success,
                              const uint8 *stagingDirCP,
                              uint32 sz)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = CP_CMD_SEND_FILES_DONE;
   params.status = success ?
      DND_CP_MSG_STATUS_SUCCESS :
      DND_CP_MSG_STATUS_ERROR;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params, stagingDirCP, sz);
}


/**
 * Send cmd CP_CMD_GET_FILES_DONE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] success the file transfer operation was successful or not
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV4::GetFilesDone(uint32 sessionId,
                             bool success)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = CP_CMD_GET_FILES_DONE;
   params.status = success ?
      DND_CP_MSG_STATUS_SUCCESS :
      DND_CP_MSG_STATUS_ERROR;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params);
}


/**
 * Send a packet.
 *
 * @param[in] destId destination address id.
 * @param[in] packet
 * @param[in] length packet length in bytes
 *
 * @return true on success, false otherwise.
 */

bool
CopyPasteRpcV4::SendPacket(uint32 destId,
                           const uint8 *packet,
                           size_t length)
{
   ASSERT(mTransport);
   return mTransport->SendPacket(destId, mTransportInterface, packet, length);
}


/**
 * Handle a received message.
 *
 * @param[in] params parameter list for received message.
 * @param[in] binary attached binary data for received message.
 * @param[in] binarySize in bytes
 */

void
CopyPasteRpcV4::HandleMsg(RpcParams *params,
                          const uint8 *binary,
                          uint32 binarySize)
{
   ASSERT(params);

   LOG(4, "%s: Got %s[%d], sessionId %d, srcId %d, binary size %d.\n",
       __FUNCTION__, DnDCPMsgV4_LookupCmd(params->cmd), params->cmd,
       params->sessionId, params->addrId, binarySize);

   switch (params->cmd) {
   case CP_CMD_RECV_CLIPBOARD:
      CPClipboard clip;
      if (!binary || binarySize == 0) {
         LOG(0, "%s: invalid clipboard data.\n", __FUNCTION__);
         break;
      }
      CPClipboard_Init(&clip);
      if (!CPClipboard_Unserialize(&clip, (void *)binary, binarySize)) {
         LOG(0, "%s: CPClipboard_Unserialize failed.\n", __FUNCTION__);
         break;
      }
      srcRecvClipChanged.emit(params->sessionId,
                              1 == params->optional.cpInfo.isActive,
                              &clip);
      CPClipboard_Destroy(&clip);
      break;
   case CP_CMD_REQUEST_CLIPBOARD:
      destRequestClipChanged.emit(params->sessionId,
                                  1 == params->optional.cpInfo.isActive);
      break;
   case CP_CMD_REQUEST_FILES:
      requestFilesChanged.emit(params->sessionId, binary, binarySize);
      break;
   case CP_CMD_GET_FILES_DONE:
      getFilesDoneChanged.emit(params->sessionId,
                               DND_CP_MSG_STATUS_SUCCESS == params->status,
                               binary,
                               binarySize);
      break;
   case DNDCP_CMD_PING_REPLY:
      pingReplyChanged.emit(params->optional.version.capability);
      break;
   case DNDCP_CMP_REPLY:
      LOG(0, "%s: Got cmp reply command %d.\n", __FUNCTION__, params->cmd);
      cmdReplyChanged.emit(params->cmd, params->status);
      break;
   default:
      LOG(0, "%s: Got unknown command %d.\n", __FUNCTION__, params->cmd);
      break;
   }
}


/**
 * Callback from transport layer after received a packet from srcId.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 */

void
CopyPasteRpcV4::OnRecvPacket(uint32 srcId,
                             const uint8 *packet,
                             size_t packetSize)
{
   mUtil.OnRecvPacket(srcId, packet, packetSize);
}
 07070100000322000081A40000000000000000000000016822550500002B54000000000000000000000000000000000000005E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/dndCPTransportGuestRpc.cpp /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @dndCPTransportGuestRpc.cpp --
 *
 * GuestRpc implementation of the dndCPTransport interface.
 *
 * XXX Move this to a common lib or source location.
 */

#include "dndCPTransportGuestRpc.hpp"
#include "rpcBase.h"

#include "str.h"
#include "hostinfo.h"
#include "dndMsg.h"
#include "util.h"

extern "C" {
#ifdef VMX86_TOOLS
   #include "debug.h"
   #include "rpcout.h"
   #include "rpcin.h"
   #define LOG(level, ...) Debug(__VA_ARGS__)
#else
   #include "dndCPInt.h"
   #include "guest_rpc.h"
   #include "tclodefs.h"

   #define LOGLEVEL_MODULE dnd
   #include "loglevel_user.h"
#endif
}

#ifdef VMX86_TOOLS
/**
 * Received a message from guestRpc. Forward the message to transport class.
 *
 * @param[out] result reply string.
 * @param[out] resultLen reply string length in byte.
 * @param[in] name guestRpc cmd name.
 * @param[in] args argument list.
 * @param[in] argsSize argument list size in byte.
 * @param[in] clientData callback client data.
 *
 * @return TRUE if success, FALSE otherwise.
 */

static gboolean
RecvMsgCB(RpcInData *data) // IN/OUT
{
   LOG(4, "%s: receiving\n", __FUNCTION__);

   const uint8 *packet = (const uint8 *)(data->args + 1);
   size_t packetSize = data->argsSize - 1;

   /* '- 1' is to ignore empty space between command and args. */
   if ((data->argsSize - 1) <= 0) {
      Debug("%s: invalid argsSize\n", __FUNCTION__);
      return RPCIN_SETRETVALS(data, "invalid arg size", FALSE);
   }
   GuestRpcCBCtx *ctx = (GuestRpcCBCtx *)data->clientData;
#else
/**
 * Received a message from guestRpc. Forward the message to transport class.
 *
 * @param[in] clientData callback client data.
 * @param[ignored] channelId
 * @param[in] args argument list.
 * @param[in] argsSize argument list size in byte.
 * @param[out] result reply string.
 * @param[out] resultSize reply string length in byte.
 *
 * @return TRUE if success, FALSE otherwise.
 */

static Bool
RecvMsgCB(void *clientData,
          GuestRpcChannel *chan,
          const unsigned char *args,
          uint32 argsSize,
          unsigned char **result,
          uint32 *resultSize)
{
   const uint8 *packet = args;
   size_t packetSize = argsSize;
   GuestRpcCBCtx *ctx = (GuestRpcCBCtx *)clientData;
#endif

   ASSERT(ctx);
   ASSERT(ctx->transport);
   ctx->transport->OnRecvPacket(ctx->type, packet, packetSize);

#ifdef VMX86_TOOLS
   return RPCIN_SETRETVALS(data, "", TRUE);
#else
   return GuestRpc_SetRetVals(result, resultSize, (char *)"", TRUE);
#endif
}


/**
 * Constructor.
 */

TransportGuestRpcTables::TransportGuestRpcTables(void)
{
   for (int i = 0; i < TRANSPORT_INTERFACE_MAX; i++) {
      mRpcList[i] = NULL;
      mCmdStrTable[i] = NULL;
      mDisableStrTable[i] = NULL;
#ifdef VMX86_TOOLS
   }
#else
      mCmdTable[i] = GUESTRPC_CMD_MAX;
   }
   mCmdTable[TRANSPORT_GUEST_CONTROLLER_DND] = GUESTRPC_CMD_DND_TRANSPORT;
   mCmdTable[TRANSPORT_GUEST_CONTROLLER_CP] = GUESTRPC_CMD_COPYPASTE_TRANSPORT;
#endif

   mCmdStrTable[TRANSPORT_GUEST_CONTROLLER_DND] = (char *)GUEST_RPC_CMD_STR_DND;
   mCmdStrTable[TRANSPORT_GUEST_CONTROLLER_CP] = (char *)GUEST_RPC_CMD_STR_CP;

   mDisableStrTable[TRANSPORT_GUEST_CONTROLLER_DND] = (char *)GUEST_RPC_DND_DISABLE;
   mDisableStrTable[TRANSPORT_GUEST_CONTROLLER_CP] = (char *)GUEST_RPC_CP_DISABLE;
}


/**
 * Destructor.
 */

TransportGuestRpcTables::~TransportGuestRpcTables(void)
{
}


/**
 * Get an rpc object by interface type.
 *
 * @param[in] type transport interface type
 *
 * @return a registered rpc, or NULL if the rpc for the type is not registered.
 */

RpcBase *
TransportGuestRpcTables::GetRpc(TransportInterfaceType type)
{
   ASSERT(type == TRANSPORT_GUEST_CONTROLLER_DND ||
          type == TRANSPORT_GUEST_CONTROLLER_CP ||
          type == TRANSPORT_GUEST_CONTROLLER_FT);
   return mRpcList[type];
}


/**
 * Add a rpc into rpc table.
 *
 * @param[in] type transport interface type
 * @param[in] rpc rpc which is added into the table.
 */

void
TransportGuestRpcTables::SetRpc(TransportInterfaceType type,
                                RpcBase *rpc)
{
   ASSERT(type == TRANSPORT_GUEST_CONTROLLER_DND ||
          type == TRANSPORT_GUEST_CONTROLLER_CP ||
          type == TRANSPORT_GUEST_CONTROLLER_FT);
   mRpcList[type] = rpc;
}


#ifndef VMX86_TOOLS
/**
 * Get a guestRpc cmd by interface type.
 *
 * @param[in] type transport interface type
 *
 * @return a guestRpc cmd.
 */

GuestRpcCmd
TransportGuestRpcTables::GetCmd(TransportInterfaceType type)
{
   ASSERT(type == TRANSPORT_GUEST_CONTROLLER_DND ||
          type == TRANSPORT_GUEST_CONTROLLER_CP ||
          type == TRANSPORT_GUEST_CONTROLLER_FT);

   return mCmdTable[type];
}
#endif


/**
 * Get a guestRpc cmd string by interface type.
 *
 * @param[in] type transport interface type
 *
 * @return a guestRpc cmd string.
 */

const char *
TransportGuestRpcTables::GetCmdStr(TransportInterfaceType type)
{
   ASSERT(type == TRANSPORT_GUEST_CONTROLLER_DND ||
          type == TRANSPORT_GUEST_CONTROLLER_CP ||
          type == TRANSPORT_GUEST_CONTROLLER_FT);

   return mCmdStrTable[type];
}


/**
 * Get a guestRpc cmd disable string by interface type.
 *
 * @param[in] type transport interface type
 *
 * @return a guestRpc cmd disable string.
 */

const char *
TransportGuestRpcTables::GetDisableStr(TransportInterfaceType type)
{
   ASSERT(type == TRANSPORT_GUEST_CONTROLLER_DND ||
          type == TRANSPORT_GUEST_CONTROLLER_CP ||
          type == TRANSPORT_GUEST_CONTROLLER_FT);

   return mDisableStrTable[type];
}


/**
 * Constructor.
 *
 * @param[in] rpcIn
 */

#ifdef VMX86_TOOLS
DnDCPTransportGuestRpc::DnDCPTransportGuestRpc(RpcChannel *chan)
   : mRpcChannel(chan)
#else
DnDCPTransportGuestRpc::DnDCPTransportGuestRpc(void)
#endif
{
   for (int i = 0; i < TRANSPORT_INTERFACE_MAX; i++) {
      mCBCtx[i].transport = this;
      mCBCtx[i].type = (TransportInterfaceType)i;
#ifdef VMX86_TOOLS
      mRpcChanCBList[i].xdrInSize = 0;
#endif
   }
}


/**
 * Register a rpc callback to an interface.
 *
 * @param[in] rpc rpc which is listening to the message.
 * @param[in] type the interface type rpc is listening to.
 *
 * @return true on success, false otherwise.
 */

bool
DnDCPTransportGuestRpc::RegisterRpc(RpcBase *rpc,
                                    TransportInterfaceType type)
{
   if (mTables.GetRpc(type)) {
      LOG(0, "%s: the type %d is already registered\n", __FUNCTION__, type);
      UnregisterRpc(type);
   }
   const char *cmdStr = (const char *)mTables.GetCmdStr(type);
   const char *disableStr = mTables.GetDisableStr(type);

   if (!cmdStr || !disableStr) {
      LOG(0, "%s: can not find valid cmd for %d, cmdStr %s disableStr %s\n",
          __FUNCTION__, type, (cmdStr ? cmdStr : "NULL"),
          (disableStr ? disableStr : "NULL"));
      return false;
   }

   ASSERT(mCBCtx);
   ASSERT(type == TRANSPORT_GUEST_CONTROLLER_DND ||
          type == TRANSPORT_GUEST_CONTROLLER_CP ||
          type == TRANSPORT_GUEST_CONTROLLER_FT);
   LOG(4, "%s: for %s\n", __FUNCTION__, cmdStr);

#ifdef VMX86_TOOLS
   ASSERT(mRpcChannel);
   mRpcChanCBList[type].name = cmdStr;
   mRpcChanCBList[type].callback = RecvMsgCB;
   mRpcChanCBList[type].clientData = &mCBCtx[type];
   mRpcChanCBList[type].xdrIn = NULL;
   mRpcChanCBList[type].xdrOut = NULL;
   mRpcChanCBList[type].xdrInSize = 0;
   RpcChannel_RegisterCallback(mRpcChannel, &mRpcChanCBList[type]);
#else
   GuestRpc_RegisterCommand(mTables.GetCmd(type), disableStr,
                            (const unsigned char *)cmdStr, RecvMsgCB, &mCBCtx[type]);
#endif
   mTables.SetRpc(type, rpc);
   return true;
}


/**
 * Unregister a rpc callback.
 *
 * @param[in] type the interface type rpc is listening to.
 *
 * @return true on success, false otherwise.
 */

bool
DnDCPTransportGuestRpc::UnregisterRpc(TransportInterfaceType type)
{
   if (!mTables.GetRpc(type)) {
      LOG(0, "%s: the type %d is not registered\n", __FUNCTION__, type);
      return false;
   }
#ifdef VMX86_TOOLS
   ASSERT(mRpcChannel);
   RpcChannel_UnregisterCallback(mRpcChannel, &mRpcChanCBList[type]);
#else
   GuestRpc_UnregisterCommand(mTables.GetCmd(type));
#endif
   mTables.SetRpc(type, NULL);
   return true;
}


/**
 * Wrap the buffer into an rpc and send it to the peer.
 *
 * @param[ignored] destId destination address id
 * @param[in] type transport interface type
 * @param[in] data Payload buffer
 * @param[in] dataSize Payload buffer size
 *
 * @return true on success, false otherwise.
 */

bool
DnDCPTransportGuestRpc::SendPacket(uint32 destId,
                                   TransportInterfaceType type,
                                   const uint8 *msg,
                                   size_t length)
{
   char *rpc = NULL;
   size_t rpcSize = 0;
   size_t nrWritten = 0;
   const char *cmd = mTables.GetCmdStr(type);
   bool ret = true;

   if (!cmd) {
      LOG(0, "%s: can not find valid cmd for %d\n", __FUNCTION__, type);
      return false;
   }
   rpcSize = strlen(cmd) + 1 + length;
   rpc = (char *)Util_SafeMalloc(rpcSize);
   nrWritten = Str_Sprintf(rpc, rpcSize, "%s ", cmd);

   if (length > 0) {
      ASSERT(nrWritten + length <= rpcSize);
      memcpy(rpc + nrWritten, msg, length);
   }

#ifdef VMX86_TOOLS
   ret = (TRUE == RpcChannel_Send(mRpcChannel, rpc, rpcSize, NULL, NULL));

   if (!ret) {
      LOG(0, "%s: failed to send msg to host\n", __FUNCTION__);
   }

   free(rpc);
#else
   GuestRpc_SendWithTimeOut((const unsigned char *)TOOLS_DND_NAME,
                            (const unsigned char *)rpc, rpcSize,
                            GuestRpc_GenericCompletionRoutine, rpc,
                            DND_TIMEOUT);
#endif
   return ret;
}


/**
 * Callback after receiving a guestRpc message.
 *
 * @param[in] type transport interface type
 * @param[in] packet Payload buffer
 * @param[in] packetSize Payload buffer size
 */

void
DnDCPTransportGuestRpc::OnRecvPacket(TransportInterfaceType type,
                                     const uint8 *packet,
                                     size_t packetSize)
{
   RpcBase *rpc = mTables.GetRpc(type);
   if (!rpc) {
      LOG(0, "%s: can not find valid rpc for %d\n", __FUNCTION__, type);
      return;
   }
   rpc->OnRecvPacket(DEFAULT_CONNECTION_ID, packet, packetSize);
}
07070100000323000081A40000000000000000000000016822550500000C98000000000000000000000000000000000000005E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/dndCPTransportGuestRpc.hpp /*********************************************************
 * Copyright (c) 2010-2017,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @dndCPTransportGuestRpc.hpp --
 *
 * GuestRpc implementation of the dndCPTransport interface. Both vmx and guest
 * tools use this class for DnD version 4.
 */


#ifndef DND_CP_TRANSPORT_GUEST_RPC_HPP
#define DND_CP_TRANSPORT_GUEST_RPC_HPP

#include "dndCPTransport.h"

#include "dnd.h"

#ifdef VMX86_TOOLS
   #include "vmware/tools/guestrpc.h"
#else
extern "C" {
   #include "guest_rpc.h"
}
#endif

#define GUEST_RPC_CMD_STR_DND "dnd.transport"
#define GUEST_RPC_CMD_STR_CP  "copypaste.transport"
#define GUEST_RPC_DND_DISABLE "dndDisable"
#define GUEST_RPC_CP_DISABLE  "copyDisable"

class DnDCPTransportGuestRpc;
typedef struct GuestRpcCBCtx {
   DnDCPTransportGuestRpc *transport;
   TransportInterfaceType type;
} GuestRpcCBCtx;

class TransportGuestRpcTables
{
public:
   TransportGuestRpcTables(void);
   ~TransportGuestRpcTables(void);

   RpcBase *GetRpc(TransportInterfaceType type);
   void SetRpc(TransportInterfaceType type, RpcBase *rpc);
#ifndef VMX86_TOOLS
   GuestRpcCmd GetCmd(TransportInterfaceType type);
#endif
   const char *GetCmdStr(TransportInterfaceType type);
   const char *GetDisableStr(TransportInterfaceType type);

private:
   RpcBase *mRpcList[TRANSPORT_INTERFACE_MAX];
#ifndef VMX86_TOOLS
   GuestRpcCmd mCmdTable[TRANSPORT_INTERFACE_MAX];
#endif
   const char *mCmdStrTable[TRANSPORT_INTERFACE_MAX];
   const char *mDisableStrTable[TRANSPORT_INTERFACE_MAX];
};


class DnDCPTransportGuestRpc
   : public DnDCPTransport
{
public:
#ifdef VMX86_TOOLS
   DnDCPTransportGuestRpc(RpcChannel *chan);
#else
   DnDCPTransportGuestRpc(void);
#endif

   bool Init(void);
   virtual bool RegisterRpc(RpcBase *rpc,
                            TransportInterfaceType type);
   virtual bool UnregisterRpc(TransportInterfaceType type);

   virtual bool SendPacket(uint32 destId,
                           TransportInterfaceType type,
                           const uint8 *msg,
                           size_t length);
   void OnRecvPacket(TransportInterfaceType type,
                     const uint8 *packet,
                     size_t packetSize);
private:
   TransportGuestRpcTables mTables;
   GuestRpcCBCtx mCBCtx[TRANSPORT_INTERFACE_MAX];
#ifdef VMX86_TOOLS
   RpcChannel *mRpcChannel;
   RpcChannelCallback mRpcChanCBList[TRANSPORT_INTERFACE_MAX];
#endif
};

#endif // DND_CP_TRANSPORT_GUEST_RPC_HPP
07070100000324000081A40000000000000000000000016822550500000581000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/dndRpcListener.hpp /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @dndRpcListener.hpp --
 *
 * Interface for objects that receive rpc send and received notifications
 * from the vmx dnd controller. These signals are used for introspection
 * during unit testing and simulation.
 */

#ifndef DND_RPC_LISTENER_HPP
#define DND_RPC_LISTENER_HPP

class DnDRpcListener
{
public:
   virtual ~DnDRpcListener() {};
   virtual void OnRpcReceived(uint32 cmd, uint32 src, uint32 session) = 0;
   virtual void OnRpcSent(uint32 cmd, uint32 dest, uint32 session) = 0;
};

#endif // DND_RPC_LISTENER_HPP
   07070100000325000081A40000000000000000000000016822550500005343000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/dndRpcV4.cc    /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @DnDRpcV4.cc --
 *
 * Implementation of the DnDRpcV4 object.
 */

#include "dndRpcV4.hh"

extern "C" {
#if defined VMX86_TOOLS
   #include "debug.h"

   #define LOG(level, ...) Debug(__VA_ARGS__)
#else
   #define LOGLEVEL_MODULE dnd
   #include "loglevel_user.h"
#endif

   #include "dndClipboard.h"
}

#include "util.h"


/**
 * Constructor.
 *
 * @param[in] transport for sending packets.
 */

DnDRpcV4::DnDRpcV4(DnDCPTransport *transport)
   : mTransport(transport),
#ifdef VMX86_TOOLS
     mTransportInterface(TRANSPORT_GUEST_CONTROLLER_DND)
#else
     mTransportInterface(TRANSPORT_HOST_CONTROLLER_DND)
#endif
{
   ASSERT(mTransport);

#ifdef VMX86_TOOLS
   mUtil.Init(this, DND_CP_MSG_SRC_GUEST, DND_CP_MSG_TYPE_DND);
#else
   mUtil.Init(this, DND_CP_MSG_SRC_HOST, DND_CP_MSG_TYPE_DND);
#endif
}


/**
 * Init.
 */

void
DnDRpcV4::Init()
{
   ASSERT(mTransport);
   mTransport->RegisterRpc(this, mTransportInterface);
}


/**
 * Send Ping message to controller.
 *
 * @param[in] caps capabilities value.
 */

void
DnDRpcV4::SendPing(uint32 caps)
{
   mUtil.SendPingMsg(DEFAULT_CONNECTION_ID, caps);
}


/**
 * Send cmd DND_CMD_SRC_DRAG_BEGIN_DONE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SrcDragBeginDone(uint32 sessionId)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_SRC_DRAG_BEGIN_DONE;
   params.sessionId = sessionId;
   params.optional.version.major = 4;
   params.optional.version.minor = 0;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_UPDATE_FEEDBACK to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] feedback current dnd operation feedback.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::UpdateFeedback(uint32 sessionId,
                         DND_DROPEFFECT feedback)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_UPDATE_FEEDBACK;
   params.sessionId = sessionId;
   params.optional.feedback.feedback = feedback;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_PRIV_DRAG_ENTER to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SrcPrivDragEnter(uint32 sessionId)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_PRIV_DRAG_ENTER;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_PRIV_DRAG_LEAVE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SrcPrivDragLeave(uint32 sessionId,
                           int32 x,
                           int32 y)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_PRIV_DRAG_LEAVE;
   params.sessionId = sessionId;
   params.optional.mouseInfo.x = x;
   params.optional.mouseInfo.y = y;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_PRIV_DROP to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SrcPrivDrop(uint32 sessionId,
                      int32 x,
                      int32 y)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_PRIV_DROP;
   params.sessionId = sessionId;
   params.optional.mouseInfo.x = x;
   params.optional.mouseInfo.y = y;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_SRC_DROP to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SrcDrop(uint32 sessionId,
                  int32 x,
                  int32 y)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_SRC_DROP;
   params.sessionId = sessionId;
   params.optional.mouseInfo.x = x;
   params.optional.mouseInfo.y = y;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_SRC_DROP_DONE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] stagingDirCP staging dir name in cross-platform format
 * @param[in] sz the staging dir name size in bytes
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SrcDropDone(uint32 sessionId,
                      const uint8 *stagingDirCP,
                      uint32 sz)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_SRC_DROP_DONE;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params, stagingDirCP, sz);
}


/**
 * Send cmd DND_CMD_SRC_CANCEL to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SrcCancel(uint32 sessionId)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_SRC_CANCEL;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_DEST_DRAG_ENTER to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] clip cross-platform clipboard data.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::DestDragEnter(uint32 sessionId,
                        const CPClipboard *clip)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_DEST_DRAG_ENTER;
   params.sessionId = sessionId;
   params.optional.version.major = mUtil.GetVersionMajor();
   params.optional.version.minor = mUtil.GetVersionMinor();

   if (clip) {
      return mUtil.SendMsg(&params, clip);
   } else {
      return mUtil.SendMsg(&params);
   }
}


/**
 * Send cmd DND_CMD_DEST_SEND_CLIPBOARD to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] clip cross-platform clipboard data.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::DestSendClip(uint32 sessionId,
                       const CPClipboard *clip)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_DEST_SEND_CLIPBOARD;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params, clip);
}


/**
 * Send cmd DND_CMD_DRAG_NOT_PENDING to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::DragNotPending(uint32 sessionId)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_DRAG_NOT_PENDING;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_DEST_DRAG_LEAVE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::DestDragLeave(uint32 sessionId,
                        int32 x,
                        int32 y)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_DEST_DRAG_LEAVE;
   params.sessionId = sessionId;
   params.optional.mouseInfo.x = x;
   params.optional.mouseInfo.y = y;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_DEST_DROP to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::DestDrop(uint32 sessionId,
                   int32 x,
                   int32 y)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_DEST_DROP;
   params.sessionId = sessionId;
   params.optional.mouseInfo.x = x;
   params.optional.mouseInfo.y = y;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_DEST_CANCEL to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::DestCancel(uint32 sessionId)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_DEST_CANCEL;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_QUERY_EXITING to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::QueryExiting(uint32 sessionId,
                       int32 x,
                       int32 y)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_QUERY_EXITING;
   params.sessionId = sessionId;
   params.optional.queryExiting.major = mUtil.GetVersionMajor();
   params.optional.queryExiting.minor = mUtil.GetVersionMinor();
   params.optional.queryExiting.x = x;
   params.optional.queryExiting.y = y;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_UPDATE_UNITY_DET_WND to controller.
 *
 * @param[in] sessionId  Active session id the controller assigned earlier.
 * @param[in] show       Show or hide unity DnD detection window.
 * @param[in] unityWndId The unity window id.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::UpdateUnityDetWnd(uint32 sessionId,
                            bool show,
                            uint32 unityWndId)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_UPDATE_UNITY_DET_WND;
   params.sessionId = sessionId;
   params.optional.updateUnityDetWnd.major = mUtil.GetVersionMajor();
   params.optional.updateUnityDetWnd.minor = mUtil.GetVersionMinor();
   params.optional.updateUnityDetWnd.show = show ? 1 : 0;
   params.optional.updateUnityDetWnd.unityWndId = unityWndId;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_MOVE_MOUSE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::MoveMouse(uint32 sessionId,
                    int32 x,
                    int32 y)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_MOVE_MOUSE;
   params.sessionId = sessionId;
   params.optional.mouseInfo.x = x;
   params.optional.mouseInfo.y = y;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_REQUEST_FILES to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::RequestFiles(uint32 sessionId)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_REQUEST_FILES;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params);
}


/**
 * Send cmd DND_CMD_SEND_FILES_DONE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] success the file transfer operation was successful or not
 * @param[in] stagingDirCP staging dir name in cross-platform format
 * @param[in] sz the staging dir name size in bytes
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SendFilesDone(uint32 sessionId,
                        bool success,
                        const uint8 *stagingDirCP,
                        uint32 sz)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_SEND_FILES_DONE;
   params.status = success ?
      DND_CP_MSG_STATUS_SUCCESS :
      DND_CP_MSG_STATUS_ERROR;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params, stagingDirCP, sz);
}


/**
 * Send cmd DND_CMD_GET_FILES_DONE to controller.
 *
 * @param[in] sessionId active session id the controller assigned earlier.
 * @param[in] success the file transfer operation was successful or not
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::GetFilesDone(uint32 sessionId,
                       bool success)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = DND_CMD_GET_FILES_DONE;
   params.status = success ?
      DND_CP_MSG_STATUS_SUCCESS :
      DND_CP_MSG_STATUS_ERROR;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params);
}


/**
 * Send a packet.
 *
 * @param[in] destId destination address id.
 * @param[in] packet
 * @param[in] length packet length
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV4::SendPacket(uint32 destId,
                     const uint8 *packet,
                     size_t length)
{
   return mTransport->SendPacket(destId, mTransportInterface, packet, length);
}


/**
 * Handle a received message.
 *
 * @param[in] params parameter list for received message.
 * @param[in] binary attached binary data for received message.
 * @param[in] binarySize
 */

void
DnDRpcV4::HandleMsg(RpcParams *params,
                    const uint8 *binary,
                    uint32 binarySize)
{
   ASSERT(params);

   LOG(4, "%s: Got %s[%d], sessionId %d, srcId %d, binary size %d.\n",
       __FUNCTION__, DnDCPMsgV4_LookupCmd(params->cmd), params->cmd,
       params->sessionId, params->addrId, binarySize);

   switch (params->cmd) {
   case DND_CMD_SRC_DRAG_BEGIN:
      CPClipboard clip;
      if (!binary || binarySize == 0) {
         LOG(0, "%s: invalid clipboard data.\n", __FUNCTION__);
         break;
      }
      CPClipboard_Init(&clip);
      if (!CPClipboard_Unserialize(&clip, (void *)binary, binarySize)) {
         LOG(0, "%s: CPClipboard_Unserialize failed.\n", __FUNCTION__);
         break;
      }
      srcDragBeginChanged.emit(params->sessionId, &clip);
      CPClipboard_Destroy(&clip);
      break;
   case DND_CMD_SRC_CANCEL:
      srcCancelChanged.emit(params->sessionId);
      break;
   case DND_CMD_SRC_DROP:
      srcDropChanged.emit(params->sessionId,
                          params->optional.mouseInfo.x,
                          params->optional.mouseInfo.y);
      break;
   case DND_CMD_DEST_DRAG_ENTER_REPLY:
      destDragEnterReplyChanged.emit(params->sessionId,
                                     params->status);
      break;
   case DND_CMD_DEST_DROP:
      destDropChanged.emit(params->sessionId,
                           params->optional.mouseInfo.x,
                           params->optional.mouseInfo.y);
      break;
   case DND_CMD_DEST_CANCEL:
      destCancelChanged.emit(params->sessionId);
      break;
   case DND_CMD_PRIV_DRAG_ENTER:
      destPrivDragEnterChanged.emit(params->sessionId);
      break;
   case DND_CMD_PRIV_DRAG_LEAVE:
      destPrivDragLeaveChanged.emit(params->sessionId,
                                    params->optional.mouseInfo.x,
                                    params->optional.mouseInfo.y);
      break;
   case DND_CMD_PRIV_DROP:
      destPrivDropChanged.emit(params->sessionId,
                               params->optional.mouseInfo.x,
                               params->optional.mouseInfo.y);
      break;
   case DND_CMD_QUERY_EXITING:
      queryExitingChanged.emit(params->sessionId,
                               params->optional.queryExiting.x,
                               params->optional.queryExiting.y);
      break;
   case DND_CMD_DRAG_NOT_PENDING:
      dragNotPendingChanged.emit(params->sessionId);
      break;
   case DND_CMD_UPDATE_UNITY_DET_WND:
      updateUnityDetWndChanged.emit(params->sessionId,
                                    1 == params->optional.updateUnityDetWnd.show,
                                    params->optional.updateUnityDetWnd.unityWndId);
      break;
   case DND_CMD_MOVE_MOUSE:
      moveMouseChanged.emit(params->sessionId,
                            params->optional.mouseInfo.x,
                            params->optional.mouseInfo.y);
      break;
   case DND_CMD_UPDATE_FEEDBACK:
      updateFeedbackChanged.emit(params->sessionId,
                                 params->optional.feedback.feedback);
      break;
   case DND_CMD_REQUEST_FILES:
      requestFileChanged.emit(params->sessionId, binary, binarySize);
      break;
   case DND_CMD_GET_FILES_DONE:
      getFilesDoneChanged.emit(params->sessionId,
                               DND_CP_MSG_STATUS_SUCCESS == params->status,
                               binary,
                               binarySize);
      break;
   case DNDCP_CMD_PING_REPLY:
      pingReplyChanged.emit(params->optional.version.capability);
      break;
   case DNDCP_CMD_TEST_BIG_BINARY:
   {
      if (binarySize != DND_CP_MSG_MAX_BINARY_SIZE_V4) {
         LOG(0, "%s: msg size is not right, should be %u.\n",
             __FUNCTION__, DND_CP_MSG_MAX_BINARY_SIZE_V4);
      }

      uint32 *testBinary = (uint32 *)binary;
      for (uint32 i = 0; i < DND_CP_MSG_MAX_BINARY_SIZE_V4 / sizeof *testBinary; i++) {
         if (testBinary[i] != i) {
            LOG(0, "%s: msg wrong in position %u. Expect %u, but got %u.\n",
                __FUNCTION__, i, i, testBinary[i]);
            return;
         }
      }
      LOG(4, "%s: successfully got big binary, sending back.\n", __FUNCTION__);
      RpcParams outParams;
      memset(&outParams, 0, sizeof outParams);
      outParams.addrId = params->addrId;
      outParams.cmd = DNDCP_CMD_TEST_BIG_BINARY_REPLY;
      mUtil.SendMsg(&outParams, binary, DND_CP_MSG_MAX_BINARY_SIZE_V4);

      break;
   }
   case DNDCP_CMP_REPLY:
      LOG(0, "%s: Got cmp reply command %d.\n", __FUNCTION__, params->cmd);
      cmdReplyChanged.emit(params->cmd, params->status);
      break;
   default:
      LOG(0, "%s: Got unknown command %d.\n", __FUNCTION__, params->cmd);
      break;
   }
}


/**
 * Callback from transport layer after received a packet from srcId.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 */

void
DnDRpcV4::OnRecvPacket(uint32 srcId,
                       const uint8 *packet,
                       size_t packetSize)
{
   mUtil.OnRecvPacket(srcId, packet, packetSize);
}


/**
 * Register a listener that will fire when RPCs are received.
 *
 * @param[in] obj an instance of a class that derives DnDRpcListener.
 */

void
DnDRpcV4::AddRpcReceivedListener(DnDRpcListener *obj)
{
   mUtil.AddRpcReceivedListener(obj);
}


/**
 * Remove a listener that will fire when RPCs are received.
 *
 * @param[in] obj an instance of a class that derives DnDRpcListener.
 */

void
DnDRpcV4::RemoveRpcReceivedListener(DnDRpcListener *obj)
{
   mUtil.RemoveRpcReceivedListener(obj);
}


/**
 * Add a listener that will fire when RPCs are sent.
 *
 * @param[in] obj an instance of a class that derives DnDRpcListener.
 */

void
DnDRpcV4::AddRpcSentListener(DnDRpcListener *obj)
{
   mUtil.AddRpcSentListener(obj);
}


/**
 * Remove a listener that will fire when RPCs are sent.
 *
 * @param[in] obj an instance of a class that derives DnDRpcListener.
 */

void
DnDRpcV4::RemoveRpcSentListener(DnDRpcListener *obj)
{
   mUtil.RemoveRpcSentListener(obj);
}


/**
 * Set the max transport packet size of RPC messages.
 *
 * @param[in] size the new max packet size.
 */

void
DnDRpcV4::SetMaxTransportPacketSize(const uint32 size)
{
   mUtil.SetMaxTransportPacketSize(size);
}
 07070100000326000081A40000000000000000000000016822550500001431000000000000000000000000000000000000005800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/fileTransferRpcV4.cc   /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @fileTransferRpcV4.cc --
 *
 * Implementation of common layer file transfer rpc version 4 object.
 */

#include "fileTransferRpcV4.hh"
#include "dndCPTransport.h"

#include "dndMsg.h"
#include "hgfsServer.h"
#include "str.h"
#include "util.h"

extern "C" {
   #include "dndCPMsgV4.h"

#if defined VMX86_TOOLS
   #include "debug.h"

   #define LOG(level, ...) Debug(__VA_ARGS__)
#else
   #define LOGLEVEL_MODULE dnd
   #include "loglevel_user.h"
#endif
}


/**
 * Create transport object and register callback.
 *
 * @param[in] transport for sending/receiving messages.
 */

FileTransferRpcV4::FileTransferRpcV4(DnDCPTransport *transport)
   : mTransport(transport),
#ifdef VMX86_TOOLS
     mTransportInterface(TRANSPORT_GUEST_CONTROLLER_FT)
#else
     mTransportInterface(TRANSPORT_HOST_CONTROLLER_FT)
#endif
{
   ASSERT(mTransport);

#ifdef VMX86_TOOLS
   mUtil.Init(this, DND_CP_MSG_SRC_GUEST, DND_CP_MSG_TYPE_FT);
#else
   mUtil.Init(this, DND_CP_MSG_SRC_HOST, DND_CP_MSG_TYPE_FT);
#endif
}


/**
 * Init. Register the rpc with transport. Send a ping message to controller to
 * to let it know our version and capability.
 *
 * XXX Capability is not implemented yet.
 */

void
FileTransferRpcV4::Init(void)
{
   ASSERT(mTransport);
   mTransport->RegisterRpc(this, mTransportInterface);
}


/**
 * Sends hgfs packet to peer.
 *
 * @param[in] sessionId DnD/CopyPaste session id.
 * @param[in] packet packet data.
 * @param[in] packetSize packet size.
 *
 * @return true on success, false otherwise.
 */

bool
FileTransferRpcV4::SendHgfsPacket(uint32 sessionId,
                                  const uint8 *packet,
                                  uint32 packetSize)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = FT_CMD_HGFS_REQUEST;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params, packet, packetSize);
}


/**
 * Sends hgfs reply back to peer.
 *
 * @param[in] sessionId DnD/CopyPaste session id.
 * @param[in] packet reply packet data.
 * @param[in] packetSize reply packet size.
 *
 * @return true on success, false otherwise.
 */

bool
FileTransferRpcV4::SendHgfsReply(uint32 sessionId,
                                 const uint8 *packet,
                                 uint32 packetSize)
{
   RpcParams params;

   memset(&params, 0, sizeof params);
   params.addrId = DEFAULT_CONNECTION_ID;
   params.cmd = FT_CMD_HGFS_REPLY;
   params.sessionId = sessionId;

   return mUtil.SendMsg(&params, packet, packetSize);
}


/**
 * Send a packet.
 *
 * @param[in] destId destination address id.
 * @param[in] packet
 * @param[in] length packet length
 *
 * @return true on success, false otherwise.
 */

bool
FileTransferRpcV4::SendPacket(uint32 destId,
                              const uint8 *packet,
                              size_t length)
{
   return mTransport->SendPacket(destId, mTransportInterface, packet, length);
}


/**
 * Handle a received message.
 *
 * @param[in] params parameter list for received message.
 * @param[in] binary attached binary data for received message.
 * @param[in] binarySize
 */

void
FileTransferRpcV4::HandleMsg(RpcParams *params,
                             const uint8 *binary,
                             uint32 binarySize)
{
   ASSERT(params);

   LOG(4, "%s: Got %s[%d], sessionId %d, srcId %d, binary size %d.\n",
       __FUNCTION__, DnDCPMsgV4_LookupCmd(params->cmd), params->cmd,
       params->sessionId, params->addrId, binarySize);

   switch (params->cmd) {
   case FT_CMD_HGFS_REQUEST:
      HgfsPacketReceived.emit(params->sessionId, binary, binarySize);
      break;
   case FT_CMD_HGFS_REPLY:
      HgfsReplyReceived.emit(params->sessionId, binary, binarySize);
      break;
   case DNDCP_CMD_PING_REPLY:
      break;
   default:
      LOG(0, "%s: Got unknown command %d.\n", __FUNCTION__, params->cmd);
      break;
   }
}


/**
 * Callback from transport layer after received a packet from srcId.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 */

void
FileTransferRpcV4::OnRecvPacket(uint32 srcId,
                                const uint8 *packet,
                                size_t packetSize)
{
   mUtil.OnRecvPacket(srcId, packet, packetSize);
}
   07070100000327000081A400000000000000000000000168225505000008F9000000000000000000000000000000000000005900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/guestCopyPasteDest.cc  /*********************************************************
 * Copyright (C) 2010-2016,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestCopyPasteDest.cc --
 *
 * Implementation of common layer GuestCopyPasteDest object for guest.
 */

#include "guestCopyPaste.hh"
#include <glib.h>

extern "C" {
   #include "dndClipboard.h"
   #include "debug.h"
}


/**
 * Constructor.
 *
 * @param[in] mgr guest CP manager
 */

GuestCopyPasteDest::GuestCopyPasteDest(GuestCopyPasteMgr *mgr)
 : mMgr(mgr),
   mIsActive(false)
{
   ASSERT(mMgr);
}


/**
 * Got valid clipboard data from UI. Send sendClip cmd to controller.
 *
 * @param[in] clip cross-platform clipboard data.
 */

void
GuestCopyPasteDest::UISendClip(const CPClipboard *clip)
{
   ASSERT(clip);

   g_debug("%s: state is %d\n", __FUNCTION__, mMgr->GetState());
   if (mMgr->GetState() != GUEST_CP_READY) {
      /* Reset DnD for any wrong state. */
      g_debug("%s: Bad state: %d\n", __FUNCTION__, mMgr->GetState());
      goto error;
   }

   if (!mMgr->GetRpc()->DestSendClip(mMgr->GetSessionId(), mIsActive, clip)) {
      g_debug("%s: DestSendClip failed\n", __FUNCTION__);
      goto error;
   }

   return;

error:
   mMgr->ResetCopyPaste();
}


/**
 * Host is asking for clipboard data. Emit destRequestClipChanged signal.
 *
 * @param[in] isActive active or passive CopyPaste.
 */

void
GuestCopyPasteDest::OnRpcRequestClip(bool isActive)
{
   mIsActive = isActive;
   g_debug("%s: state is %d\n", __FUNCTION__, mMgr->GetState());
   mMgr->destRequestClipChanged.emit();
}

   07070100000328000081A40000000000000000000000016822550500001B31000000000000000000000000000000000000005800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/guestCopyPasteMgr.cc   /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestCopyPasteMgr.cc --
 *
 * Implementation of common layer GuestCopyPasteMgr object for guest.
 */

#include "tracer.hh"
#include "guestCopyPaste.hh"
#include "copyPasteRpcV3.hh"
#include "copyPasteRpcV4.hh"
#include "guestDnDCPMgr.hh"

extern "C" {
   #include "debug.h"
}


/**
 * Constructor.
 *
 * @param[in] transport for sending/receiving packets.
 */

GuestCopyPasteMgr::GuestCopyPasteMgr(DnDCPTransport *transport)
 : mSrc(NULL),
   mDest(NULL),
   mRpc(NULL),
   mCPState(GUEST_CP_READY),
   mTransport(transport),
   mSessionId(0),
   mCopyPasteAllowed(false),
   mResolvedCaps(0xffffffff)
{
   ASSERT(transport);
}


/**
 * Destructor.
 */

GuestCopyPasteMgr::~GuestCopyPasteMgr(void)
{
   delete mRpc;
   mRpc = NULL;
}


/**
 * Reset state machine and session id. Delete mSrc and mDest.
 */

void
GuestCopyPasteMgr::ResetCopyPaste(void)
{
   TRACE_CALL();

   if (mSrc) {
      delete mSrc;
      mSrc = NULL;
   }
   if (mDest) {
      delete mDest;
      mDest = NULL;
   }
   SetState(GUEST_CP_READY);
   SetSessionId(0);
}


/**
 * Session ID change and bookkeeping.
 *
 * @param[in] id Next session ID.
 */
void
GuestCopyPasteMgr::SetSessionId(uint32 id)
{
   DEVEL_ONLY(g_debug("%s: %u => %u\n", __FUNCTION__, mSessionId, id));
   mSessionId = id;
}


/**
 * State change and bookkeeping.
 *
 * @param[in] state Next state.
 */

void
GuestCopyPasteMgr::SetState(GUEST_CP_STATE state)
{
#ifdef VMX86_DEVEL
   static const char* states[] = {
      "GUEST_CP_INVALID",
      "GUEST_CP_READY",
      "GUEST_CP_HG_FILE_COPYING",
   };
   g_debug("%s: %s => %s\n", __FUNCTION__, states[mCPState], states[state]);
#endif

   mCPState = state;
}


/**
 * Got valid clipboard data from host. Create mSrc if the state machine
 * is ready.
 *
 * @param[in] sessionId active session id
 * @param[in] isActive active or passive CopyPaste.
 * @param[in] clip cross-platform clipboard data.
 */

void
GuestCopyPasteMgr::OnRpcSrcRecvClip(uint32 sessionId,
                                    bool isActive,
                                    const CPClipboard *clip)
{
   ASSERT(clip);

   TRACE_CALL();

   if (!mCopyPasteAllowed) {
      g_debug("%s: CopyPaste is not allowed.\n", __FUNCTION__);
      return;
   }

   if  (GUEST_CP_READY != mCPState) {
      g_debug("%s: Bad state: %d, reset\n", __FUNCTION__, mCPState);
      /* XXX Should reset DnD here. */
      return;
   }

   if (mSrc) {
      g_debug("%s: mSrc is not NULL\n", __FUNCTION__);
      delete mSrc;
      mSrc = NULL;
   }

   SetSessionId(sessionId);

   mSrc = new GuestCopyPasteSrc(this);
   mSrc->OnRpcRecvClip(isActive, clip);
}


/**
 * Wrapper for mSrc->UIRequestFiles.
 *
 * @param[in] dir staging directory in local format.
 *
 * @return The staging directory if succeed, otherwise empty string.
 */

const std::string
GuestCopyPasteMgr::SrcUIRequestFiles(const std::string &dir)
{
   if (mSrc) {
      return mSrc->UIRequestFiles(dir);
   } else {
      g_debug("%s: mSrc is NULL\n", __FUNCTION__);
      return std::string("");
   }
}


/**
 * Host is asking for clipboard data. Create mDest if the state machine
 * is ready.
 *
 * @param[in] sessionId active session id
 * @param[in] isActive active or passive CopyPaste.
 */

void
GuestCopyPasteMgr::OnRpcDestRequestClip(uint32 sessionId,
                                        bool isActive)
{
   TRACE_CALL();

   if (!mCopyPasteAllowed) {
      g_debug("%s: CopyPaste is not allowed.\n", __FUNCTION__);
      return;
   }

   if  (GUEST_CP_READY != mCPState) {
      g_debug("%s: Bad state: %d, reset\n", __FUNCTION__, mCPState);
      /* XXX Should reset CP here. */
      return;
   }

   if (mDest) {
      g_debug("%s: mDest is not NULL\n", __FUNCTION__);
      delete mDest;
      mDest = NULL;
   }

   SetSessionId(sessionId);

   mDest = new GuestCopyPasteDest(this);
   mDest->OnRpcRequestClip(isActive);
}


/**
 * Wrapper for mDest->UISendClip.
 *
 * @param[in] clip cross-platform clipboard data.
 */

void
GuestCopyPasteMgr::DestUISendClip(const CPClipboard *clip)
{
   if (mDest) {
      mDest->UISendClip(clip);
   } else {
      g_debug("%s: mDest is NULL\n", __FUNCTION__);
   }
}


/**
 * Handle version change in VMX.
 *
 * @param[in] version negotiated CP version.
 */

void
GuestCopyPasteMgr::VmxCopyPasteVersionChanged(uint32 version)
{
   g_debug("GuestCopyPasteMgr::%s: enter version %d\n", __FUNCTION__, version);
   ASSERT(version >= 3);
   ASSERT(mTransport);

   if (mRpc) {
      delete mRpc;
      mRpc = NULL;
   }

   switch(version) {
   case 4:
      mRpc = new CopyPasteRpcV4(mTransport);
      break;
   case 3:
      mRpc = new CopyPasteRpcV3(mTransport);
      break;
   default:
      g_debug("%s: unsupported CP version\n", __FUNCTION__);
      break;
   }
   if (mRpc) {
      g_debug("GuestCopyPasteMgr::%s: register ping reply changed %d\n",
              __FUNCTION__, version);
      mRpc->pingReplyChanged.connect(
         sigc::mem_fun(this, &GuestCopyPasteMgr::OnPingReply));
      mRpc->srcRecvClipChanged.connect(
         sigc::mem_fun(this, &GuestCopyPasteMgr::OnRpcSrcRecvClip));
      mRpc->destRequestClipChanged.connect(
         sigc::mem_fun(this, &GuestCopyPasteMgr::OnRpcDestRequestClip));
      mRpc->Init();
      mRpc->SendPing(GuestDnDCPMgr::GetInstance()->GetCaps() &
                     (DND_CP_CAP_CP | DND_CP_CAP_FORMATS_CP | DND_CP_CAP_VALID));
   }

   ResetCopyPaste();
}


/**
 * Check if a request is allowed based on resolved capabilities.
 *
 * @param[in] capsRequest requested capabilities.
 *
 * @return TRUE if allowed, FALSE otherwise.
 */

Bool
GuestCopyPasteMgr::CheckCapability(uint32 capsRequest)
{
   Bool allowed = FALSE;

   if ((mResolvedCaps & capsRequest) == capsRequest) {
      allowed = TRUE;
   }
   return allowed;
}


/**
 * Got pingReplyChanged message. Update capabilities.
 *
 * @param[in] capability modified capabilities from VMX controller.
 */

void
GuestCopyPasteMgr::OnPingReply(uint32 capabilities)
{
   g_debug("%s: copypaste ping reply caps are %x\n", __FUNCTION__, capabilities);
   mResolvedCaps = capabilities;
}

   07070100000329000081A40000000000000000000000016822550500001653000000000000000000000000000000000000005800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/guestCopyPasteSrc.cc   /*********************************************************
 * Copyright (C) 2010-2017,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestCopyPasteSrc.cc --
 *
 * Implementation of common layer GuestCopyPasteSrc object for guest.
 */

#include "guestCopyPaste.hh"
#include <glib.h>

extern "C" {
   #include "dndClipboard.h"
   #include "debug.h"
   #include "cpNameUtil.h"
}

#include "file.h"
#include "util.h"
#include "str.h"


/**
 * Constructor.
 *
 * @param[in] mgr guest CP manager
 */

GuestCopyPasteSrc::GuestCopyPasteSrc(GuestCopyPasteMgr *mgr)
 : mMgr(mgr)
{
   ASSERT(mMgr);
   mMgr->GetRpc()->getFilesDoneChanged.connect(
      sigc::mem_fun(this, &GuestCopyPasteSrc::OnRpcGetFilesDone));
   CPClipboard_Init(&mClipboard);
}


/**
 * Destructor.
 */

GuestCopyPasteSrc::~GuestCopyPasteSrc(void)
{
   ASSERT(mMgr);
   CPClipboard_Destroy(&mClipboard);
   /* Reset current session id after finished. */
   mMgr->SetSessionId(0);
}


/**
 * Got valid clipboard data from host. Emit srcRecvClipChanged signal if state
 * machine is right.
 *
 * @param[in] isActive active or passive CopyPaste.
 * @param[in] clip cross-platform clipboard data.
 */

void
GuestCopyPasteSrc::OnRpcRecvClip(bool isActive,
                                 const CPClipboard *clip)
{
   ASSERT(mMgr);
   ASSERT(clip);

   g_debug("%s: state is %d\n", __FUNCTION__, mMgr->GetState());

   CPClipboard_Clear(&mClipboard);
   CPClipboard_Copy(&mClipboard, clip);

   mMgr->srcRecvClipChanged.emit(clip);
}


/**
 * UI is asking for files. Send requestFiles cmd to controller.
 *
 * @param[in] dir staging directory in local format.
 *
 * @return The staging directory if succeed, otherwise empty string.
 */

const std::string
GuestCopyPasteSrc::UIRequestFiles(const std::string &dir)
{
   std::string destDir;
   char cpName[FILE_MAXPATH];
   int32 cpNameSize;

   if (mMgr->GetState() != GUEST_CP_READY) {
      /* Reset DnD for any wrong state. */
      g_debug("%s: Bad state: %d\n", __FUNCTION__, mMgr->GetState());
      goto error;
   }

   /* Setup staging directory. */
   destDir = SetupDestDir(dir);
   if (destDir.empty()) {
      goto error;
   }

   /* Convert staging name to CP format. */
   cpNameSize = CPNameUtil_ConvertToRoot(destDir.c_str(),
                                         sizeof cpName,
                                         cpName);
   if (cpNameSize < 0) {
      g_debug("%s: Error, could not convert to CPName.\n", __FUNCTION__);
      goto error;
   }

   if (!mMgr->GetRpc()->RequestFiles(mMgr->GetSessionId(),
                                     (const uint8 *)cpName,
                                     cpNameSize)) {
      goto error;
   }

   mStagingDir = destDir;
   mMgr->SetState(GUEST_CP_HG_FILE_COPYING);
   g_debug("%s: state changed to GUEST_CP_HG_FILE_COPYING\n", __FUNCTION__);

   return destDir;

error:
   mMgr->ResetCopyPaste();
   return "";
}


/**
 * The file transfer is finished. Emit getFilesDoneChanged signal and reset
 * local state.
 *
 * @param[in] sessionId active DnD session id
 * @param[in] success if the file transfer is successful or not
 * @param[in] stagingDirCP staging dir name in cross-platform format
 * @param[in] sz the staging dir name size
 */

void
GuestCopyPasteSrc::OnRpcGetFilesDone(uint32 sessionId,
                                     bool success,
                                     const uint8 *stagingDirCP,
                                     uint32 sz)
{
   if (!success && !mStagingDir.empty()) {
      /* Delete all files if host canceled the file transfer. */
      DnD_DeleteStagingFiles(mStagingDir.c_str(), FALSE);
      mStagingDir.clear();
   }
   /* UI should remove block with this signal. */
   mMgr->getFilesDoneChanged.emit(success);
   mMgr->SetState(GUEST_CP_READY);
   g_debug("%s: state changed to READY\n", __FUNCTION__);
}


/**
 * Creates a directory for file transfer. If the destination dir is provided,
 * we will attempt to copy files to that directory.
 *
 * @param[in] destDir the preferred destination directory
 *
 * @return the destination directory on success, an empty string on failure.
 */

const std::string &
GuestCopyPasteSrc::SetupDestDir(const std::string &destDir)
{
   mStagingDir = "";

   if (!destDir.empty() && File_Exists(destDir.c_str())) {
      mStagingDir = destDir;
      const char *lastSep = Str_Strrchr(mStagingDir.c_str(), DIRSEPC);
      if (lastSep && lastSep[1] != '\0') {
         mStagingDir += DIRSEPS;
      }
   } else {
      char *newDir;

      newDir = DnD_CreateStagingDirectory();
      if (newDir != NULL) {
         mStagingDir = newDir;

         char *lastSep = Str_Strrchr(newDir, DIRSEPC);
         if (lastSep && lastSep[1] != '\0') {
            mStagingDir += DIRSEPS;
         }
         free(newDir);
         g_debug("%s: destdir: %s", __FUNCTION__, mStagingDir.c_str());
      } else {
         g_debug("%s: destdir not created", __FUNCTION__);
      }
   }
   return mStagingDir;
}


 0707010000032A000081A40000000000000000000000016822550500002BF1000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/rpcV3Util.cpp  /*********************************************************
 * Copyright (C) 2010-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @rpcV3Util.cpp --
 *
 * Implementation of common utility object for DnD/CP version 3 rpc object.
 * It is shared by vmx and guest implementation. Some common utilities
 * including
 * *packet marshalling/un-marshalling
 * *big buffer support
 * are implemented here.
 */


#include "rpcV3Util.hpp"

#ifdef VMX86_TOOLS
extern "C" {      
   #include "debug.h"
   #define LOG(level, ...) Debug(__VA_ARGS__)
}
#else
   #define LOGLEVEL_MODULE dnd
   #include "loglevel_user.h"
#endif

extern "C" {      
#include "dndClipboard.h"
}

#include "util.h"
#include "dndMsg.h"
#include "hostinfo.h"

/**
 * Constructor.
 */

RpcV3Util::RpcV3Util(void)
   : mRpc(NULL),
     mVersionMajor(3),
     mVersionMinor(0),
     mSeqNum(1)
{
   mSendBuf.buffer = NULL;
   mRecvBuf.buffer = NULL;
   DnD_TransportBufReset(&mSendBuf);
   DnD_TransportBufReset(&mRecvBuf);
}


/**
 * Destructor.
 */

RpcV3Util::~RpcV3Util(void)
{
   free(mSendBuf.buffer);
   free(mRecvBuf.buffer);
}


/**
 * Initialize the RpcV3Util object. All owner should call this first before
 * calling any other utility function.
 *
 * @param[in] rpc the owner of this utility object
 * @param[in] msgType the type of message (DnD/CP/FT)
 * @param[in] msgSrc source of the message (host/guest/controller)
 */

void
RpcV3Util::Init(RpcBase *rpc)
{
   ASSERT(rpc);
   mRpc = rpc;
}


/**
 * Serialize command, then send the message.
 *
 * @param[in] cmd version 3 command
 *
 * @return true on success, false otherwise.
 */

bool
RpcV3Util::SendMsg(uint32 cmd)
{
   DnDMsg msg;
   bool ret;

   DnDMsg_Init(&msg);
   DnDMsg_SetCmd(&msg, cmd);
   ret = SendMsg(&msg);
   DnDMsg_Destroy(&msg);

   return ret;
}


/**
 * Serialize the clipboard item if there is one, then send the message to
 * destId.
 *
 * @param[in] cmd version 3 command
 * @param[in] clip the clipboard item.
 *
 * @return true on success, false otherwise.
 */

bool
RpcV3Util::SendMsg(uint32 cmd,
                   const CPClipboard *clip)
{
   DnDMsg msg;
   DynBuf buf;
   bool ret = false;

   DnDMsg_Init(&msg);
   DynBuf_Init(&buf);

   /* Serialize clip and output into buf. */
   if (!CPClipboard_Serialize(clip, &buf)) {
      LOG(0, "%s: CPClipboard_Serialize failed.\n", __FUNCTION__);
      goto exit;
   }

   /* Construct msg with both cmd CP_HG_SET_CLIPBOARD and buf. */
   DnDMsg_SetCmd(&msg, cmd);
   if (!DnDMsg_AppendArg(&msg, DynBuf_Get(&buf), DynBuf_GetSize(&buf))) {
      LOG(0, "%s: DnDMsg_AppendData failed.\n", __FUNCTION__);
      goto exit;
   }

   ret = SendMsg(&msg);

exit:
   DynBuf_Destroy(&buf);
   DnDMsg_Destroy(&msg);
   return ret;
}


/**
 * Serialize command with mouse position, and send the message.
 *
 * @param[in] cmd version 3 command
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
RpcV3Util::SendMsg(uint32 cmd,
                   int32 x,
                   int32 y)
{
   bool ret = false;

   DnDMsg msg;

   DnDMsg_Init(&msg);

   DnDMsg_SetCmd(&msg, cmd);

   if (!DnDMsg_AppendArg(&msg, &x, sizeof x) ||
       !DnDMsg_AppendArg(&msg, &y, sizeof y)) {
      LOG(0, "%s: DnDMsg_AppendData failed.\n", __FUNCTION__);
      goto exit;
   }

   ret = SendMsg(&msg);

exit:
   DnDMsg_Destroy(&msg);
   return ret;
}


/**
 * Serialize and send the message.
 *
 * @param[in] msg the message to be serialized and sent.
 *
 * @return true on success, false otherwise.
 */

bool
RpcV3Util::SendMsg(const DnDMsg *msg)
{
   DynBuf buf;
   bool ret = false;

   DynBuf_Init(&buf);

   /* Serialize msg and output to buf. */
   if (!DnDMsg_Serialize((DnDMsg *)msg, &buf)) {
      LOG(0, "%s: DnDMsg_Serialize failed.\n", __FUNCTION__);
      goto exit;
   }

   ret = SendMsg((uint8 *)DynBuf_Get(&buf), DynBuf_GetSize(&buf));

exit:
   DynBuf_Destroy(&buf);
   return ret;
}


/**
 * Serialize the message and send it to destId.
 *
 * @param[in] binary
 * @param[in] binarySize
 *
 * @return true on success, false otherwise.
 */

bool
RpcV3Util::SendMsg(const uint8 *binary,
                   uint32 binarySize)
{
   DnDTransportPacketHeader *packet = NULL;
   size_t packetSize;
   bool ret = FALSE;

   if (binarySize > DNDMSG_MAX_ARGSZ) {
      LOG(1, "%s: message is too big, quit.\n", __FUNCTION__);
      return false;
   }

   LOG(4, "%s: got message, size %d.\n", __FUNCTION__, binarySize);

   if (binarySize <= DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE) {
      /*
       * It is a small size message, so it is not needed to buffer it. Just
       * put message into a packet and send it.
       */
      packetSize = DnD_TransportMsgToPacket((uint8 *)binary, binarySize,
                                            mSeqNum, &packet);
   } else {
      /*
       * It is a big size message. First buffer it and send it with multiple
       * packets.
       */
      if (mSendBuf.buffer) {
         /*
          * There is a pending big size message. If there is no update time
          * more than DND_MAX_TRANSPORT_LATENCY_TIME, remove old message and
          * send new message. Otherwise ignore this message.
          */

         if ((Hostinfo_SystemTimerUS() - mSendBuf.lastUpdateTime) <
             DND_MAX_TRANSPORT_LATENCY_TIME) {
            LOG(1, "%s: got a big buffer, but there is another pending one, drop it\n",
                __FUNCTION__);
            return false;
         }
      }
      DnD_TransportBufInit(&mSendBuf, (uint8 *)binary, binarySize, mSeqNum);
      packetSize = DnD_TransportBufGetPacket(&mSendBuf, &packet);
   }

   /* Increase sequence number for next message. */
   mSeqNum++;
   if (packetSize) {
      ret = mRpc->SendPacket(0, (const uint8 *)packet, packetSize);
   }
   free(packet);
   return ret;
}



/**
 * Callback from transport layer after received a packet from srcId.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 */

void
RpcV3Util::OnRecvPacket(uint32 srcId,
                        const uint8 *packet,
                        size_t packetSize)
{
   DnDTransportPacketHeader *packetV3 = (DnDTransportPacketHeader *)packet;
   ASSERT(packetV3);
   /*
    * Adding extra check to verify the validity of packetSize,
    * In case payload is corrupted its causing illegal access exceptions.
    * bug: 2639178
    */
   if (packetSize < sizeof(DnDTransportPacketHeader) ||
       packetSize > DND_MAX_TRANSPORT_PACKET_SIZE ||
       packetV3->payloadSize > DND_MAX_TRANSPORT_PACKET_PAYLOAD_SIZE ||
       (packetV3->payloadSize + DND_TRANSPORT_PACKET_HEADER_SIZE) != packetSize) {
      goto invalid_packet;
   }

   switch (packetV3->type) {
   case DND_TRANSPORT_PACKET_TYPE_SINGLE:
      if (packetV3->payloadSize != packetV3->totalSize) {
         goto invalid_packet;
      }
      /* This is a single packet. Forward to rpc layer for further processing. */
      mRpc->HandleMsg(NULL, packetV3->payload, packetV3->payloadSize);
      break;
   case DND_TRANSPORT_PACKET_TYPE_REQUEST:
      {
         /* Peer is asking for next packet. */
         DnDTransportPacketHeader *replyPacket = NULL;
         size_t replyPacketSize;

         if (packetV3->payloadSize ||
             packetV3->seqNum != mSendBuf.seqNum ||
             packetV3->offset != mSendBuf.offset) {
            LOG(0, "%s: received packet does not match local buffer.\n",
                __FUNCTION__);
            return;
         }

         replyPacketSize = DnD_TransportBufGetPacket(&mSendBuf, &replyPacket);

         if (!replyPacketSize) {
            /*
             * Not needed to reset mSendBuf because DnD_TransportBufGetPacket already
             * did that.
             */
            LOG(0, "%s: DnD_TransportBufGetPacket failed.\n", __FUNCTION__);
            return;
         }

         if (!mRpc->SendPacket(0, (const uint8 *)replyPacket, replyPacketSize) ||
             mSendBuf.offset == mSendBuf.totalSize) {
            /* Reset mSendBuf if whole buffer is sent or there is any error. */
            DnD_TransportBufReset(&mSendBuf);
         }

         free(replyPacket);

         break;
      }
   case DND_TRANSPORT_PACKET_TYPE_PAYLOAD:
      /*
       * If seqNum does not match, it means either this is the first packet, or there
       * is a timeout in another side. The buffer will be reset in all cases later.
       * For the first packet, the totalSize should not larger than DNDMSG_MAX_ARGSZ.
       * For the rest packets, the totalSize should be the same as the first packet.
       */
      if (mRecvBuf.seqNum != packetV3->seqNum) {
         if (packetV3->totalSize > DNDMSG_MAX_ARGSZ) {
            goto invalid_packet;
         }
      } else {
         if (packetV3->totalSize != mRecvBuf.totalSize) {
            goto invalid_packet;
         }
      }

      /*
       * The totalSize has been validated.
       * We need to make sure the  payloadSize and offset are in right range.
       */
      if (packetV3->payloadSize > packetV3->totalSize ||
          packetV3->offset > packetV3->totalSize ||
          (packetV3->payloadSize + packetV3->offset) > packetV3->totalSize) {
         goto invalid_packet;
      }

      /* Received next packet for big binary buffer. */
      if (!DnD_TransportBufAppendPacket(&mRecvBuf, packetV3, packetSize)) {
         LOG(0, "%s: DnD_TransportBufAppendPacket failed.\n", __FUNCTION__);
         return;
      }

      if (mRecvBuf.offset == mRecvBuf.totalSize) {
         /*
          * Received all packets for the messge, forward it to rpc layer for
          * further processing.
          */
         mRpc->HandleMsg(NULL, mRecvBuf.buffer, mRecvBuf.totalSize);
         DnD_TransportBufReset(&mRecvBuf);
      } else {
         /* Send request for next packet. */
         DnDTransportPacketHeader *replyPacket = NULL;
         size_t replyPacketSize;

         replyPacketSize = DnD_TransportReqPacket(&mRecvBuf, &replyPacket);

         if (!replyPacketSize) {
            LOG(0, "%s: DnD_TransportReqPacket failed.\n", __FUNCTION__);
            return;
         }

         if (!mRpc->SendPacket(0, (const uint8 *)replyPacket, replyPacketSize)) {
            DnD_TransportBufReset(&mRecvBuf);
         }
         free(replyPacket);
      }
      break;
   default:
      LOG(0, "%s: unknown packet.\n", __FUNCTION__);
      break;
   }

invalid_packet:
   LOG(0, "%s: received invalid data.\n", __FUNCTION__);
}


   0707010000032B000081A400000000000000000000000168225505000048F5000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/rpcV4Util.cpp  /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @rpcV4Util.cpp --
 *
 * Implementation of common utility object for DnD/CP version 4 rpc object.
 * It is shared by host, guest and UI implementation. Some common utilities
 * including
 * *packet marshalling/un-marshalling
 * *common rpc (ping, pingReply, etc)
 * *big buffer support
 * are implemented here.
 */


#include "rpcV4Util.hpp"

#ifdef VMX86_TOOLS
extern "C" {
   #include "debug.h"
   #define LOG(level, ...) Debug(__VA_ARGS__)
}
#else
   #define LOGLEVEL_MODULE dnd
   #include "loglevel_user.h"
#endif

extern "C" {
#include "dndClipboard.h"
}

#include "util.h"

/**
 * Constructor.
 */

RpcV4Util::RpcV4Util(void)
   : mRpc(NULL),
     mVersionMajor(4),
     mVersionMinor(0),
     mMsgType(0),
     mMsgSrc(0),
     mMaxTransportPacketPayloadSize(DND_CP_PACKET_MAX_PAYLOAD_SIZE_V4)
{
   DnDCPMsgV4_Init(&mBigMsgIn);
   DnDCPMsgV4_Init(&mBigMsgOut);
   DblLnkLst_Init(&mRpcSentListeners);
   DblLnkLst_Init(&mRpcReceivedListeners);
}


/**
 * Destructor.
 */

RpcV4Util::~RpcV4Util(void)
{
   DnDCPMsgV4_Destroy(&mBigMsgIn);
   DnDCPMsgV4_Destroy(&mBigMsgOut);

   while (DblLnkLst_IsLinked(&mRpcSentListeners)) {
      DnDRpcSentListenerNode *node =
         DblLnkLst_Container(mRpcSentListeners.next,
                             DnDRpcSentListenerNode, l);

      ASSERT(node);
      DblLnkLst_Unlink1(&node->l);
      free(node);
   }

   while (DblLnkLst_IsLinked(&mRpcReceivedListeners)) {
      DnDRpcReceivedListenerNode *node =
         DblLnkLst_Container(mRpcReceivedListeners.next,
                             DnDRpcReceivedListenerNode, l);

      ASSERT(node);
      DblLnkLst_Unlink1(&node->l);
      free(node);
   }
}


/**
 * Initialize the RpcV4Util object. All owner should call this first before
 * calling any other utility function.
 *
 * @param[in] rpc the owner of this utility object
 * @param[in] msgType the type of message (DnD/CP/FT)
 * @param[in] msgSrc source of the message (host/guest/controller)
 */

void
RpcV4Util::Init(RpcBase *rpc,
                uint32 msgType,
                uint32 msgSrc)
{
   ASSERT(rpc);
   mRpc = rpc;
   mMsgType = msgType;
   mMsgSrc = msgSrc;
}


/**
 * Serialize the clipboard item if there is one, then send the message to
 * destId.
 *
 * @param[in] params parameter list for the message
 * @param[in] clip the clipboard item.
 *
 * @return true on success, false otherwise.
 */

bool
RpcV4Util::SendMsg(RpcParams *params,
                   const CPClipboard *clip)
{
   DynBuf buf;
   bool ret = false;

   ASSERT(params);

   if (!clip) {
      return SendMsg(params);
   }

   DynBuf_Init(&buf);

   if (!CPClipboard_Serialize(clip, &buf)) {
      LOG(0, "%s: CPClipboard_Serialize failed.\n", __FUNCTION__);
      goto exit;
   }

   ret = SendMsg(params,
                 (const uint8 *)DynBuf_Get(&buf),
                 (uint32)DynBuf_GetSize(&buf));

exit:
   DynBuf_Destroy(&buf);
   return ret;
}


/**
 * Serialize the message and send it to destId.
 *
 * @param[in] params parameter list for the message
 * @param[in] binary
 * @param[in] binarySize
 *
 * @return true on success, false otherwise.
 */

bool
RpcV4Util::SendMsg(RpcParams *params,
                   const uint8 *binary,
                   uint32 binarySize)
{
   bool ret = false;
   DnDCPMsgV4 *msgOut = NULL;
   DnDCPMsgV4 shortMsg;

   ASSERT(params);

   DnDCPMsgV4_Init(&shortMsg);

   if (binarySize > mMaxTransportPacketPayloadSize) {
      /*
       * For big message, all information should be cached in mBigMsgOut
       * because multiple packets and sends are needed.
       */
      DnDCPMsgV4_Destroy(&mBigMsgOut);
      msgOut = &mBigMsgOut;
   } else {
      /* For short message, the temporary shortMsg is enough. */
      msgOut = &shortMsg;
   }

   msgOut->addrId = params->addrId;
   msgOut->hdr.cmd = params->cmd;
   msgOut->hdr.type = mMsgType;
   msgOut->hdr.src = mMsgSrc;
   msgOut->hdr.sessionId = params->sessionId;
   msgOut->hdr.status = params->status;
   msgOut->hdr.param1 = params->optional.genericParams.param1;
   msgOut->hdr.param2 = params->optional.genericParams.param2;
   msgOut->hdr.param3 = params->optional.genericParams.param3;
   msgOut->hdr.param4 = params->optional.genericParams.param4;
   msgOut->hdr.param5 = params->optional.genericParams.param5;
   msgOut->hdr.param6 = params->optional.genericParams.param6;
   msgOut->hdr.binarySize = binarySize;
   msgOut->hdr.payloadOffset = 0;
   msgOut->hdr.payloadSize = 0;
   msgOut->binary = NULL;
   if (binarySize > 0) {
      msgOut->binary = (uint8 *)(Util_SafeMalloc(binarySize));
      memcpy(msgOut->binary, binary,binarySize);
   }

   /*
    * Coverity reports a potential null dereference on msgOut->binary
    * in DnDCPMsgV4_SerializeWithInputPayloadSizeCheck, which is called
    * from RPCV4Util::SendMsg.  However, it's a false positive, since
    * msgOut->binary is dereferenced only if msgOut->hdr.binarySize > 0,
    * which in turn guarantees msgOut->binary will be non-NULL.
    */
   /* coverity[var_deref_model] */
   ret = SendMsg(msgOut);
   /* The mBigMsgOut is destroyed when the message sending was failed. */
   if (!ret && msgOut == &mBigMsgOut) {
      DnDCPMsgV4_Destroy(&mBigMsgOut);
   }
   DnDCPMsgV4_Destroy(&shortMsg);
   return ret;
}


/**
 * Construct a DNDCP_CMD_PING message and send it to destId.
 *
 * @param[in] destId destination address id
 * @param[in] capability
 *
 * @return true on success, false otherwise.
 */

bool
RpcV4Util::SendPingMsg(uint32 destId,
                       uint32 capability)
{
   RpcParams params;
   memset(&params, 0, sizeof params);
   params.addrId = destId;
   params.cmd = DNDCP_CMD_PING;
   params.optional.version.major = mVersionMajor;
   params.optional.version.minor = mVersionMinor;
   params.optional.version.capability = capability;

   return SendMsg(&params);
}


/**
 * Construct a DNDCP_CMD_PING_REPLY message and send it to destId.
 *
 * @param[in] destId destination address id
 * @param[in] capability
 *
 * @return true on success, false otherwise.
 */

bool
RpcV4Util::SendPingReplyMsg(uint32 destId,
                            uint32 capability)
{
   RpcParams params;
   memset(&params, 0, sizeof params);
   params.addrId = destId;
   params.cmd = DNDCP_CMD_PING_REPLY;
   params.optional.version.major = mVersionMajor;
   params.optional.version.minor = mVersionMinor;
   params.optional.version.capability = capability;

   return SendMsg(&params);
}


/**
 * Construct a DNDCP_CMP_REPLY message and send it to destId.
 *
 * @param[in] destId destination address id
 * @param[in] cmd the command to be replied
 * @param[in] status
 *
 * @return true on success, false otherwise.
 */

bool
RpcV4Util::SendCmdReplyMsg(uint32 destId,
                            uint32 cmd,
                            uint32 status)
{
   RpcParams params;
   memset(&params, 0, sizeof params);
   params.addrId = destId;
   params.cmd = DNDCP_CMP_REPLY;
   params.status = status;
   params.optional.replyToCmd.cmd = cmd;

   return SendMsg(&params);
}


/**
 * Construct a DNDCP_CMD_REQUEST_NEXT message and send it to mBigMsgIn.addrId.
 * This is used for big message receiving. After received a packet, receiver
 * side should send this message to ask for next piece of binary.
 *
 * @return true on success, false otherwise.
 */

bool
RpcV4Util::RequestNextPacket(void)
{
   RpcParams params;
   memset(&params, 0, sizeof params);
   params.addrId = mBigMsgIn.addrId;
   params.cmd = DNDCP_CMD_REQUEST_NEXT;
   params.sessionId = mBigMsgIn.hdr.sessionId;
   params.optional.requestNextCmd.cmd = mBigMsgIn.hdr.cmd;
   params.optional.requestNextCmd.binarySize = mBigMsgIn.hdr.binarySize;
   params.optional.requestNextCmd.payloadOffset = mBigMsgIn.hdr.payloadOffset;

   return SendMsg(&params);
}


/**
 * Serialize a message and send it to msg->addrId.
 *
 * @param[in] msg the message to be serialized
 *
 * @return true on success, false otherwise.
 */

bool
RpcV4Util::SendMsg(DnDCPMsgV4 *msg)
{
   uint8 *packet = NULL;
   size_t packetSize = 0;
   bool ret = false;

   if (!DnDCPMsgV4_SerializeWithInputPayloadSizeCheck(msg, &packet,
      &packetSize, mMaxTransportPacketPayloadSize)) {
      LOG(1, "%s: DnDCPMsgV4_Serialize failed. \n", __FUNCTION__);
      return false;
   }

   ret = mRpc->SendPacket(msg->addrId, packet, packetSize);
   if (ret == true) {
      FireRpcSentCallbacks(msg->hdr.cmd,
                           msg->addrId,
                           msg->hdr.sessionId);
   }
   free(packet);
   return ret;
}


/**
 * Callback from transport layer after received a packet from srcId.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 */

void
RpcV4Util::OnRecvPacket(uint32 srcId,
                        const uint8 *packet,
                        size_t packetSize)
{
   DnDCPMsgPacketType packetType = DND_CP_MSG_PACKET_TYPE_INVALID;

   if (packetSize <= mMaxTransportPacketPayloadSize + DND_CP_MSG_HEADERSIZE_V4) {
      packetType = DnDCPMsgV4_GetPacketType(packet, packetSize, mMaxTransportPacketPayloadSize);
   }

   switch (packetType) {
   case DND_CP_MSG_PACKET_TYPE_SINGLE:
      HandlePacket(srcId, packet, packetSize);
      break;
   case DND_CP_MSG_PACKET_TYPE_MULTIPLE_NEW:
   case DND_CP_MSG_PACKET_TYPE_MULTIPLE_CONTINUE:
   case DND_CP_MSG_PACKET_TYPE_MULTIPLE_END:
      HandlePacket(srcId, packet, packetSize, packetType);
      break;
   default:
      LOG(1, "%s: invalid packet. \n", __FUNCTION__);
      SendCmdReplyMsg(srcId, DNDCP_CMD_INVALID, DND_CP_MSG_STATUS_INVALID_PACKET);
      break;
   }
}


/**
 * Handle a packet for short message.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 */

void
RpcV4Util::HandlePacket(uint32 srcId,
                    const uint8 *packet,
                    size_t packetSize)
{
   DnDCPMsgV4 msgIn;

   DnDCPMsgV4_Init(&msgIn);

   if (!DnDCPMsgV4_UnserializeSingle(&msgIn, packet, packetSize)) {
      LOG(1, "%s: invalid packet. \n", __FUNCTION__);
      SendCmdReplyMsg(srcId, DNDCP_CMD_INVALID, DND_CP_MSG_STATUS_INVALID_PACKET);
      return;
   }

   msgIn.addrId = srcId;
   HandleMsg(&msgIn);

   DnDCPMsgV4_Destroy(&msgIn);
}


/**
 * Handle a packet for long message.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 * @param[in] packetType
 */

void
RpcV4Util::HandlePacket(uint32 srcId,
                    const uint8 *packet,
                    size_t packetSize,
                    DnDCPMsgPacketType packetType)
{
   if (!DnDCPMsgV4_UnserializeMultiple(&mBigMsgIn, packet, packetSize)) {
      LOG(1, "%s: invalid packet. \n", __FUNCTION__);
      SendCmdReplyMsg(srcId, DNDCP_CMD_INVALID, DND_CP_MSG_STATUS_INVALID_PACKET);
      goto cleanup;
   }

   mBigMsgIn.addrId = srcId;

   /*
    * If there are multiple packets for the message, sends DNDCP_REQUEST_NEXT
    * back to sender to ask for next packet.
    */
   if (DND_CP_MSG_PACKET_TYPE_MULTIPLE_END != packetType) {
      if (!RequestNextPacket()) {
         LOG(1, "%s: RequestNextPacket failed.\n", __FUNCTION__);
         goto cleanup;
      }
      /*
       * Do not destroy mBigMsgIn here because more packets are expected for
       * this message.
       */
      return;
   }

   HandleMsg(&mBigMsgIn);

cleanup:
   DnDCPMsgV4_Destroy(&mBigMsgIn);
}


/**
 * Handle a received message.
 *
 * @param[in] msgIn received message.
 */

void
RpcV4Util::HandleMsg(DnDCPMsgV4 *msgIn)
{
   RpcParams params;

   ASSERT(msgIn);

   if (DNDCP_CMD_REQUEST_NEXT == msgIn->hdr.cmd) {
      /*
       * This is for big buffer support. The receiver is asking for next piece
       * of data. For details about big buffer support, please refer to
       * https://wiki.eng.vmware.com/DnDVersion4Message#Binary_Buffer
       */
      bool ret = SendMsg(&mBigMsgOut);

      if (!ret) {
         LOG(1, "%s: SendMsg failed. \n", __FUNCTION__);
      }

      /*
       * mBigMsgOut will be destroyed if SendMsg failed or whole message has
       * been sent.
       */
      if (!ret || mBigMsgOut.hdr.payloadOffset == mBigMsgOut.hdr.binarySize) {
         DnDCPMsgV4_Destroy(&mBigMsgOut);
      }
      return;
   }

   params.addrId = msgIn->addrId;
   params.cmd = msgIn->hdr.cmd;
   params.sessionId = msgIn->hdr.sessionId;
   params.status = msgIn->hdr.status;
   params.optional.genericParams.param1 = msgIn->hdr.param1;
   params.optional.genericParams.param2 = msgIn->hdr.param2;
   params.optional.genericParams.param3 = msgIn->hdr.param3;
   params.optional.genericParams.param4 = msgIn->hdr.param4;
   params.optional.genericParams.param5 = msgIn->hdr.param5;
   params.optional.genericParams.param6 = msgIn->hdr.param6;

   mRpc->HandleMsg(&params, msgIn->binary, msgIn->hdr.binarySize);
   FireRpcReceivedCallbacks(msgIn->hdr.cmd, msgIn->addrId, msgIn->hdr.sessionId);
}


/**
 * Add an rpc received listener to the list.
 *
 * @param[in] listener class instance that implements DnDRpcListener
 *
 * @note generally, needs to be matched with a call to
 * RemoveRpcReceivedListener, but if not, cleanup happens in destructor.
 */

bool
RpcV4Util::AddRpcReceivedListener(const DnDRpcListener *listener)
{
   ASSERT(listener);

   DnDRpcReceivedListenerNode *node =
      (DnDRpcReceivedListenerNode *) Util_SafeMalloc(sizeof(DnDRpcReceivedListenerNode));
   DblLnkLst_Init(&node->l);
   node->listener = listener;
   DblLnkLst_LinkLast(&mRpcReceivedListeners, &node->l);

   /*
    * Storage of node is not leaking since the above call to
    * DblLnkLst_LinkLast adds it to the end of the
    * mRpcReceivedListeners list.
    */
   /* coverity[leaked_storage] */
   return true;
}


/**
 * Remove an rpc received listener from the list.
 *
 * @param[in] listener class instance that implements DnDRpcReceivedListener
 *
 * @note only the first instance of the listener will be removed.
 */

bool
RpcV4Util::RemoveRpcReceivedListener(const DnDRpcListener *listener)
{
   ASSERT(listener);

   DblLnkLst_Links *curr;

   DblLnkLst_ForEach(curr, &mRpcReceivedListeners) {
      DnDRpcReceivedListenerNode *p =
         DblLnkLst_Container(curr, DnDRpcReceivedListenerNode, l);
      if (p && p->listener == listener) {
         DblLnkLst_Unlink1(&p->l);
         free(p);
         return true;
      }
   }
   return false;
}


/**
 * Fire all registered rpc received callbacks.
 *
 * @param[in] cmd rpc command
 * @param[in] src src ID
 * @param[in] session session ID
 */

void
RpcV4Util::FireRpcReceivedCallbacks(uint32 cmd,
                                    uint32 src,
                                    uint32 session)
{
   DblLnkLst_Links *curr = NULL;
   DnDRpcReceivedListenerNode *p = NULL;
   DnDRpcListener *listener = NULL;

   DblLnkLst_ForEach(curr, &mRpcReceivedListeners) {
      p = DblLnkLst_Container(curr, DnDRpcReceivedListenerNode, l);
      if (p) {
         listener = const_cast<DnDRpcListener *>(p->listener);
         listener->OnRpcReceived(cmd, src, session);
      }
   }
}


/**
 * Add an rpc sent listener to the list.
 *
 * @param[in] listener class instance that implements DnDRpcListener
 *
 * @note generally, needs to be matched with a call to
 * RemoveRpcSentListener, but if not, cleanup happens in destructor.
 */

bool
RpcV4Util::AddRpcSentListener(const DnDRpcListener *listener)
{
   ASSERT(listener);

   DnDRpcSentListenerNode *node =
      (DnDRpcSentListenerNode *) Util_SafeMalloc(sizeof *node);
   DblLnkLst_Init(&node->l);
   node->listener = listener;
   DblLnkLst_LinkLast(&mRpcSentListeners, &node->l);

   /*
    * Storage of node is not leaking since the above call to
    * DblLnkLst_LinkLast adds it to the end of the
    * mRpcSentListeners list.
    */
   /* coverity[leaked_storage] */
   return true;
}


/**
 * Remove an rpc sent listener from the list.
 *
 * @param[in] listener class instance that implements DnDRpcSentListener
 *
 * @note only the first instance of the listener will be removed.
 */

bool
RpcV4Util::RemoveRpcSentListener(const DnDRpcListener *listener)
{
   ASSERT(listener);

   DblLnkLst_Links *curr;

   DblLnkLst_ForEach(curr, &mRpcSentListeners) {
      DnDRpcSentListenerNode *p =
         DblLnkLst_Container(curr, DnDRpcSentListenerNode, l);
      if (p && p->listener == listener) {
         DblLnkLst_Unlink1(&p->l);
         free(p);
         return true;
      }
   }
   return false;
}


/**
 * Fire all registered rpc sent callbacks.
 *
 * @param[in] cmd rpc command
 * @param[in] dest destination ID
 * @param[in] session session ID
 */

void
RpcV4Util::FireRpcSentCallbacks(uint32 cmd,
                                uint32 dest,
                                uint32 session)
{
   DblLnkLst_Links *curr = NULL;

   DblLnkLst_ForEach(curr, &mRpcSentListeners) {
      DnDRpcSentListenerNode *p =
         DblLnkLst_Container(curr, DnDRpcSentListenerNode, l);
      if (p && p->listener) {
         const_cast<DnDRpcListener *>(p->listener)->OnRpcSent(cmd,
                                                              dest,
                                                              session);
      }
   }
}


/**
 * Set the max transport packet size of RPC messages.
 *
 * @param[in] size the new max packet size.
 */

void
RpcV4Util::SetMaxTransportPacketSize(const uint32 size)
{
   ASSERT(size > DND_CP_MSG_HEADERSIZE_V4);

   uint32 newProposedPayloadSize = size - DND_CP_MSG_HEADERSIZE_V4;
   if (newProposedPayloadSize < DND_CP_PACKET_MAX_PAYLOAD_SIZE_V4) {
      /*
       * Reset the max transport packet payload size
       * if the new size is stricter than the default one.
       */
      mMaxTransportPacketPayloadSize = newProposedPayloadSize;
      LOG(1, "%s: The packet size is set to %u. \n", __FUNCTION__,
          mMaxTransportPacketPayloadSize);
   }
}


   0707010000032C000081A40000000000000000000000016822550500000D83000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/rpcV4Util.hpp  /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @rpcV4Util.hpp --
 *
 * Rpc layer object for DnD version 4.
 */

#ifndef RPC_V4_UTIL_HPP
#define RPC_V4_UTIL_HPP

#ifndef LIB_EXPORT
#define LIB_EXPORT
#endif

#include "rpcBase.h"
#include "dndRpcListener.hpp"
#include "dbllnklst.h"
#include "dnd.h"

extern "C" {
   #include "dndCPMsgV4.h"
}


typedef struct DnDRpcReceivedListenerNode
{
   DblLnkLst_Links l;
   const DnDRpcListener *listener;
} DnDRpcReceivedListenerNode;


typedef struct DnDRpcSentListenerNode
{
   DblLnkLst_Links l;
   const DnDRpcListener *listener;
} DnDRpcSentListenerNode;


class LIB_EXPORT RpcV4Util
{
public:
   RpcV4Util(void);
   virtual ~RpcV4Util(void);

   void Init(RpcBase *rpc, uint32 msgType, uint32 msgSrc);

   void OnRecvPacket(uint32 srcId,
                     const uint8 *packet,
                     size_t packetSize);
   bool SendPingMsg(uint32 destId,
                    uint32 capability);
   bool SendPingReplyMsg(uint32 destId,
                         uint32 capability);
   bool SendCmdReplyMsg(uint32 destId, uint32 cmd, uint32 status);
   bool SendMsg(RpcParams *params,
                const uint8 *binary,
                uint32 binarySize);
   bool SendMsg(RpcParams *params,
                const CPClipboard *clip);
   bool SendMsg(RpcParams *params)
      { return SendMsg(params, NULL, 0); }
   uint32 GetVersionMajor(void) { return mVersionMajor; }
   uint32 GetVersionMinor(void) { return mVersionMinor; }

   bool AddRpcReceivedListener(const DnDRpcListener *obj);
   bool RemoveRpcReceivedListener(const DnDRpcListener *obj);
   bool AddRpcSentListener(const DnDRpcListener *obj);
   bool RemoveRpcSentListener(const DnDRpcListener *obj);

   void SetMaxTransportPacketSize(const uint32 size);

private:
   void FireRpcReceivedCallbacks(uint32 cmd, uint32 src, uint32 session);
   void FireRpcSentCallbacks(uint32 cmd, uint32 dest, uint32 session);
   bool SendMsg(DnDCPMsgV4 *msg);
   bool RequestNextPacket(void);
   void HandlePacket(uint32 srcId,
                     const uint8 *packet,
                     size_t packetSize);
   void HandlePacket(uint32 srcId,
                     const uint8 *packet,
                     size_t packetSize,
                     DnDCPMsgPacketType packetType);
   void HandleMsg(DnDCPMsgV4 *msg);

   RpcBase *mRpc;
   uint32 mVersionMajor;
   uint32 mVersionMinor;
   DnDCPMsgV4 mBigMsgIn;
   DnDCPMsgV4 mBigMsgOut;
   uint32 mMsgType;
   uint32 mMsgSrc;
   DblLnkLst_Links mRpcSentListeners;
   DblLnkLst_Links mRpcReceivedListeners;
   uint32 mMaxTransportPacketPayloadSize;
};

#endif // RPC_V4_UTIL_HPP
 0707010000032D000081A40000000000000000000000016822550500000B5D000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/vmGuestDnDCPMgr.cc /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @vmGuestDnDCPMgr.cc --
 *
 * The inherited implementation of common class GuestDnDCPMgr in VM side.
 */

#include "dndCPTransportGuestRpc.hpp"
#include "vmGuestDnDCPMgr.hh"
#include "vmGuestDnDMgr.hh"
#include "vmGuestFileTransfer.hh"


/**
 *
 * Destructor.
 */

VMGuestDnDCPMgr::~VMGuestDnDCPMgr()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   delete mDnDMgr;
   mDnDMgr = NULL;
   delete mFileTransfer;
   mFileTransfer = NULL;
   delete mTransport;
   mTransport = NULL;
}


/**
 * Initialize the VMGuestDnDCPMgr object. All owner should call this first before
 * calling any other function.
 *
 * @param[in] ctx ToolsAppCtx
 */

void
VMGuestDnDCPMgr::Init(ToolsAppCtx *ctx)
{
   mToolsAppCtx = ctx;

   ASSERT(mToolsAppCtx);

   if (mFileTransfer) {
      delete mFileTransfer;
   }
   mFileTransfer = new VMGuestFileTransfer(GetTransport());
}


/**
 * Get the DnDCPTransport object.
 *
 * XXX Implementation here is temporary and should be replaced with rpcChannel.
 *
 * @return a pointer to the manager's DnDCPTransport instance.
 */

DnDCPTransport *
VMGuestDnDCPMgr::GetTransport(void)
{
   if (!mTransport) {
      ASSERT(mToolsAppCtx);
      mTransport = new DnDCPTransportGuestRpc(mToolsAppCtx->rpc);
   }
   return mTransport;
}


/**
 * Get the GuestDnDMgr object.
 *
 * @return a pointer to the GuestDnDMgr instance.
 */

GuestDnDMgr *
VMGuestDnDCPMgr::GetDnDMgr(void)
{
   if (!mDnDMgr) {
      /* mEventQueue must be set before this call. */
      mDnDMgr = new VMGuestDnDMgr(GetTransport(), mToolsAppCtx);
   }
   return mDnDMgr;
}


/**
 * Create an instance of class VMGuestDnDCPMgr.
 *
 * @return a pointer to the VMGuestDnDCPMgr instance.
 */

VMGuestDnDCPMgr *
VMGuestDnDCPMgr::CreateInstance(void)
{
   return new VMGuestDnDCPMgr();
}


/**
 * Get the GuestCopyPasteMgr object.
 *
 * @return a pointer to the GuestCopyPasteMgr instance.
 */

GuestCopyPasteMgr *
VMGuestDnDCPMgr::GetCopyPasteMgr(void)
{
   if (!mCPMgr) {
      mCPMgr = new GuestCopyPasteMgr(GetTransport());
   }
   return mCPMgr;
}
   0707010000032E000081A400000000000000000000000168225505000006D3000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/vmGuestDnDCPMgr.hh /*********************************************************
 * Copyright (C) 2018-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @vmGuestDnDCPMgr.hh --
 *
 * The inherited implementation of common class GuestDnDCPMgr in VM side.
 */

#ifndef VM_GUEST_DND_CP_MGR_HH
#define VM_GUEST_DND_CP_MGR_HH

#include "guestDnDCPMgr.hh"

extern "C" {
   #include "vmware/tools/guestrpc.h"
   #include "vmware/tools/plugin.h"
}

class VMGuestDnDCPMgr
   : public GuestDnDCPMgr
{
public:
   virtual ~VMGuestDnDCPMgr();
   void Init(ToolsAppCtx *ctx);
   virtual GuestDnDMgr *GetDnDMgr(void);
   virtual DnDCPTransport *GetTransport(void);
   virtual GuestCopyPasteMgr *GetCopyPasteMgr(void);

   static VMGuestDnDCPMgr *CreateInstance(void);

private:
   VMGuestDnDCPMgr(void) : GuestDnDCPMgr(), mToolsAppCtx(NULL) { }
   VMGuestDnDCPMgr(const VMGuestDnDCPMgr &mgr);
   VMGuestDnDCPMgr& operator=(const VMGuestDnDCPMgr &mgr);

private:
   ToolsAppCtx *mToolsAppCtx;
};

#endif // VM_GUEST_DND_CP_MGR_HH
 0707010000032F000081A40000000000000000000000016822550500001010000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/vmGuestDnDMgr.cc   /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @vmGuestDnDMgr.cc --
 *
 * The inherited implementation of common class GuestDnDMgr in VM side.
 */

#include "dndRpcV3.hh"
#include "dndRpcV4.hh"
#include "tracer.hh"
#include "vmGuestDnDMgr.hh"
#include "vmGuestDnDSrc.hh"

extern "C" {
#include "debug.h"
}


/**
 * Constructor.
 *
 * @param[in] transport for sending/receiving packets.
 * @param[in] ctx ToolsAppCtx
 */

VMGuestDnDMgr::VMGuestDnDMgr(DnDCPTransport *transport,
                             ToolsAppCtx *ctx)
   : GuestDnDMgr(transport, ctx),
     mToolsAppCtx(ctx)
{
   ASSERT(mToolsAppCtx);
}


/**
 * Add DnD un-grab timeout event.
 */

void
VMGuestDnDMgr::AddDnDUngrabTimeoutEvent()
{
   if (NULL == mUngrabTimeout) {
      g_debug("%s: adding UngrabTimeout\n", __FUNCTION__);
      mUngrabTimeout = g_timeout_source_new(UNGRAB_TIMEOUT);
      VMTOOLSAPP_ATTACH_SOURCE(mToolsAppCtx, mUngrabTimeout,
                               GuestDnDMgr::DnDUngrabTimeout, this, NULL);
      g_source_unref(mUngrabTimeout);
   }
}


/**
 * Add Unity DnD detect timeout event.
 */

void
VMGuestDnDMgr::AddUnityDnDDetTimeoutEvent()
{
   mUnityDnDDetTimeout = g_timeout_source_new(UNITY_DND_DET_TIMEOUT);
   VMTOOLSAPP_ATTACH_SOURCE(mToolsAppCtx,
                            mUnityDnDDetTimeout,
                            GuestDnDMgr::DnDUnityDetTimeout,
                            this,
                            NULL);
   g_source_unref(mUnityDnDDetTimeout);
}


/**
 * Add hide detect window timer.
 */

void
VMGuestDnDMgr::AddHideDetWndTimerEvent()
{
   if (NULL == mHideDetWndTimer) {
      g_debug("%s: add timer to hide detection window.\n", __FUNCTION__);
      mHideDetWndTimer = g_timeout_source_new(HIDE_DET_WND_TIMER);
      VMTOOLSAPP_ATTACH_SOURCE(mToolsAppCtx, mHideDetWndTimer,
                               GuestDnDMgr::DnDHideDetWndTimer, this, NULL);
      g_source_unref(mHideDetWndTimer);
   } else {
      g_debug("%s: mHideDetWndTimer is not NULL, quit.\n", __FUNCTION__);
   }
}


/**
 * Create DnD rpc with input version.
 *
 * @param[in] version input version number
 */

void
VMGuestDnDMgr::CreateDnDRpcWithVersion(uint32 version)
{
   switch(version) {
   case 4:
      mRpc = new DnDRpcV4(mDnDTransport);
      break;
   case 3:
      mRpc = new DnDRpcV3(mDnDTransport);
      break;
   default:
      g_debug("%s: unsupported DnD version\n", __FUNCTION__);
      break;
   }
}


/**
 * Got srcDragBegin from rpc with valid data. Create mSrc if the state machine
 * is ready.
 *
 * @param[in] sessionId active DnD session id
 * @param[in] capability controller capability
 * @param[in] clip cross-platform clipboard data.
 */

void
VMGuestDnDMgr::OnRpcSrcDragBegin(uint32 sessionId,
                                 const CPClipboard *clip)
{
   TRACE_CALL();

   if (!mDnDAllowed) {
      g_debug("%s: DnD is not allowed.\n", __FUNCTION__);
      return;
   }

   if (GUEST_DND_READY != mDnDState) {
      g_debug("%s: Bad state: %d, reset\n", __FUNCTION__, mDnDState);
      ResetDnD();
      return;
   }

   if (mSrc) {
      g_debug("%s: mSrc is not NULL\n", __FUNCTION__);
      delete mSrc;
      mSrc = NULL;
   }

   SetSessionId(sessionId);

   ASSERT(clip);

   mSrc = new VMGuestDnDSrc(this);
   mSrc->OnRpcDragBegin(clip);
}
07070100000330000081A40000000000000000000000016822550500000666000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/vmGuestDnDMgr.hh   /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @vmGuestDnDMgr.hh --
 *
 * The inherited implementation of common class GuestDnDMgr in VM side.
 */

#ifndef VM_GUEST_DND_MGR_HH
#define VM_GUEST_DND_MGR_HH

#include "guestDnD.hh"

extern "C" {
   #include "vmware/tools/plugin.h"
}

class VMGuestDnDMgr
   : public GuestDnDMgr
{
public:
   VMGuestDnDMgr(DnDCPTransport *transport,
                 ToolsAppCtx *ctx);

protected:
   virtual void AddDnDUngrabTimeoutEvent();
   virtual void AddUnityDnDDetTimeoutEvent();
   virtual void AddHideDetWndTimerEvent();
   virtual void CreateDnDRpcWithVersion(uint32 version);
   virtual void OnRpcSrcDragBegin(uint32 sessionId,
                                  const CPClipboard *clip);

private:
   ToolsAppCtx *mToolsAppCtx;
};

#endif // VM_GUEST_DND_MGR_HH
  07070100000331000081A40000000000000000000000016822550500000ADE000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/vmGuestDnDSrc.cc   /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @vmGuestDnDSrc.cc --
 *
 * The inherited implementation of common class GuestDnDSrc in VM side.
 */

#include "util.h"
#include "vmGuestDnDSrc.hh"

#include "file.h"
#include "str.h"


/**
 * Constructor.
 *
 * @param[in] mgr guest DnD manager
 */

VMGuestDnDSrc::VMGuestDnDSrc(GuestDnDMgr *mgr)
   : GuestDnDSrc(mgr)
{
}


/**
 * Creates a directory for file transfer. If the destination dir is provided,
 * we will attempt to copy files to that directory.
 *
 * @param[in] destDir the preferred destination directory
 *
 * @return the destination directory on success, an empty string on failure.
 */

const std::string&
VMGuestDnDSrc::SetupDestDir(const std::string &destDir)
{
   mStagingDir = "";

   if (!destDir.empty() && File_Exists(destDir.c_str())) {
      mStagingDir = destDir;
      const char *lastSep = Str_Strrchr(mStagingDir.c_str(), DIRSEPC);
      if (lastSep && lastSep[1] != '\0') {
         mStagingDir += DIRSEPS;
      }

      return mStagingDir;
   } else {
      char *newDir;

      newDir = DnD_CreateStagingDirectory();
      if (newDir != NULL) {
         mStagingDir = newDir;

         char *lastSep = Str_Strrchr(newDir, DIRSEPC);
         if (lastSep && lastSep[1] != '\0') {
            mStagingDir += DIRSEPS;
         }
         free(newDir);
         g_debug("%s: destination dir is: %s", __FUNCTION__, mStagingDir.c_str());

         return mStagingDir;
      } else {
         g_debug("%s: destination dir is not created", __FUNCTION__);
         return mStagingDir;
      }
   }
}


/**
 * Clean the staging files.
 *
 * @param[in] fileTransferResult the file transfer result
 */

void
VMGuestDnDSrc::CleanStagingFiles(bool fileTransferResult)
{
   if (!fileTransferResult && !mStagingDir.empty()) {
      /* Delete all files if host canceled the file transfer. */
      DnD_DeleteStagingFiles(mStagingDir.c_str(), FALSE);
      mStagingDir.clear();
   }
}
  07070100000332000081A40000000000000000000000016822550500000534000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/vmGuestDnDSrc.hh   /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @vmGuestDnDSrc.hh --
 *
 * The inherited implementation of common class GuestDnDSrc in VM side.
 */

#ifndef VM_GUEST_DND_SRC_HH
#define VM_GUEST_DND_SRC_HH

#include "guestDnD.hh"

class VMGuestDnDSrc
   : public GuestDnDSrc
{
public:
   VMGuestDnDSrc(GuestDnDMgr *mgr);

protected:
   virtual const std::string& SetupDestDir(const std::string &destDir);
   virtual void CleanStagingFiles(bool fileTransferResult);
};

#endif // VM_GUEST_DND_SRC_HH
07070100000333000081A40000000000000000000000016822550500000B1D000000000000000000000000000000000000005A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/vmGuestFileTransfer.cc /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @vmGuestFileTransfer.cc --
 *
 * VM side implementation of file transfer object for guest.
 */

#include "fileTransferRpcV4.hh"
#include "vmGuestFileTransfer.hh"

extern "C" {
   #include "debug.h"
}

#include "hgfsServer.h"


/**
 * Create transport object and register callback.
 *
 * @param[in] transport pointer to a transport object.
 */

VMGuestFileTransfer::VMGuestFileTransfer(DnDCPTransport *transport)
   : GuestFileTransfer(transport)
{
   ASSERT(transport);
   mRpc = new FileTransferRpcV4(transport);
   mRpc->Init();
   mRpc->HgfsPacketReceived.connect(
      sigc::mem_fun(this, &VMGuestFileTransfer::OnRpcRecvHgfsPacket));
   HgfsServerManager_DataInit(&mHgfsServerMgrData,
                              "DnDGuestHgfsMgr",
                              NULL,
                              NULL);
   HgfsServerManager_Register(&mHgfsServerMgrData);
}


/**
 * Destructor.
 */
VMGuestFileTransfer::~VMGuestFileTransfer(void)
{
   HgfsServerManager_Unregister(&mHgfsServerMgrData);

   if (NULL != mRpc) {
      delete mRpc;
      mRpc = NULL;
   }
}


/**
 * Callback after received message.
 *
 * @param[in] sessionId dnd session id.
 * @param[in] packet hgfs packet from the peer.
 * @param[in] packetSize hgfs pack size.
 */

void
VMGuestFileTransfer::OnRpcRecvHgfsPacket(uint32 sessionId,
                                         const uint8 *packet,
                                         size_t packetSize)
{
   char replyPacket[HGFS_LARGE_PACKET_MAX];
   size_t replyPacketSize;

   ASSERT(packet);
   ASSERT(mRpc);

   /* Call hgfs server to process the request, and send reply back to peer. */
   HgfsServerManager_ProcessPacket(&mHgfsServerMgrData,
                                   (const char *)packet,
                                   packetSize,
                                   replyPacket,
                                   &replyPacketSize);
   mRpc->SendHgfsReply(sessionId, (const uint8 *)replyPacket, replyPacketSize);
}
   07070100000334000081A4000000000000000000000001682255050000063D000000000000000000000000000000000000005A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuest/vmGuestFileTransfer.hh /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @vmGuestFileTransfer.hh --
 *
 * VM side implementation of file transfer object for guest.
 */

#ifndef VM_GUEST_FILE_TRANSFER_HH
#define VM_GUEST_FILE_TRANSFER_HH

#include <sigc++/trackable.h>
#include "fileTransferRpc.hh"
#include "dndCPTransport.h"
#include "guestFileTransfer.hh"
extern "C" {
#include "hgfsServerManager.h"
}

class VMGuestFileTransfer
   : public GuestFileTransfer
{
public:
   VMGuestFileTransfer(DnDCPTransport *transport);
   ~VMGuestFileTransfer(void);

private:
   void OnRpcRecvHgfsPacket(uint32 sessionId,
                            const uint8 *packet,
                            size_t packetSize);

   HgfsServerMgrData mHgfsServerMgrData;
};

#endif // VM_GUEST_FILE_TRANSFER_HH
   07070100000335000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase    07070100000336000081A4000000000000000000000001682255050000077C000000000000000000000000000000000000005A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/copyPasteDnDImpl.h /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file copyPasteDnDImpl.h
 *
 * Interface for concrete classes that implement the DnD, Copy paste plugin
 * abstraction. Implementing DnD/CP for a new guest platform involves creating
 * a class that inherits CopyPasteDnDImpl.
 */

#ifndef __COPYPASTEDNDIMPL_H__
#define __COPYPASTEDNDIMPL_H__

#include "vmware.h"
#include "vm_basic_types.h"

extern "C" {
#include "vmware/tools/plugin.h"
}

typedef struct ToolsAppCtx ToolsAppCtx;
class CopyPasteDnDImpl
{
public:
   virtual ~CopyPasteDnDImpl() {};
   virtual gboolean Init(ToolsAppCtx *ctx) = 0;
   virtual void PointerInit() = 0;
   virtual gboolean RegisterCP() = 0;
   virtual void UnregisterCP() = 0;
   virtual gboolean RegisterDnD() = 0;
   virtual void UnregisterDnD() = 0;
   virtual void CopyPasteVersionChanged(const int version) = 0;
   virtual void DnDVersionChanged(const int version) = 0;
   virtual void SetCopyPasteAllowed(bool allowed) = 0;
   virtual void SetDnDAllowed(bool allowed) = 0;
   virtual uint32 GetCaps() = 0;
};

#endif // __COPYPASTEDNDIMPL_H__
07070100000337000081A400000000000000000000000168225505000025E0000000000000000000000000000000000000005F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/copyPasteDnDWrapper.cpp    /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file copyPasteDnDWrapper.cpp
 *
 * This singleton class implements a wrapper around various versions of
 * copy and paste and dnd protocols, and provides some convenience functions
 * that help to make VMwareUser a bit cleaner.
 */

#define G_LOG_DOMAIN "dndcp"

#if defined(HAVE_GTKMM)
#include "copyPasteDnDX11.h"
#endif

#if defined(WIN32)
#include "copyPasteDnDWin32.h"
#ifdef DND_VM
#include "vmCopyPasteDnDWin32.h"
#else
#include "crtCopyPasteDnDWin32.h"
#endif
#endif

#if defined(__APPLE__)
#include "copyPasteDnDMac.h"
#endif

#ifdef DND_VM
#include "vmCopyPasteDnDWrapper.h"
#else
#include "crtCopyPasteDnDWrapper.h"
#endif

#include "copyPasteDnDWrapper.h"
#include "guestDnDCPMgr.hh"
#include "vmware.h"


/**
 * CopyPasteDnDWrapper is a singleton, here is a pointer to its only instance.
 */

CopyPasteDnDWrapper *CopyPasteDnDWrapper::m_instance = NULL;

/**
 *
 * Get an instance of CopyPasteDnDWrapper, which is an application singleton.
 *
 * @return a pointer to the singleton CopyPasteDnDWrapper object, or NULL if
 * for some reason it could not be allocated.
 */

CopyPasteDnDWrapper *
CopyPasteDnDWrapper::GetInstance()
{
   if (!m_instance) {
#ifdef DND_VM
      m_instance = VMCopyPasteDnDWrapper::CreateInstance();
#else
      m_instance = CRTCopyPasteDnDWrapper::CreateInstance();
#endif
   }
   ASSERT(m_instance);
   return m_instance;
}


/**
 *
 * Destroy the singleton object.
 */

void
CopyPasteDnDWrapper::Destroy()
{
   if (m_instance) {
      g_debug("%s: destroying self\n", __FUNCTION__);
      delete m_instance;
      m_instance = NULL;
   }
}


/**
 *
 * Constructor.
 */

CopyPasteDnDWrapper::CopyPasteDnDWrapper() :
   m_isCPEnabled(FALSE),
   m_isDnDEnabled(FALSE),
   m_isCPRegistered(FALSE),
   m_isDnDRegistered(FALSE),
   m_cpVersion(0),
   m_dndVersion(0),
   m_pimpl(NULL)
{
}


/**
 *
 * Call the implementation class pointer/grab initialization code. See
 * the implementation code for further details.
 */

void
CopyPasteDnDWrapper::PointerInit()
{
   ASSERT(m_pimpl);

   m_pimpl->PointerInit();
}


/**
 *
 * Initialize the wrapper by instantiating the platform specific impl
 * class. Effectively, this function is a factory that produces a
 * platform implementation of the DnD/Copy Paste UI layer.
 *
 * @param[in] ctx tools app context.
 */

void
CopyPasteDnDWrapper::Init(ToolsAppCtx *ctx)
{
   GuestDnDCPMgr *p = GuestDnDCPMgr::GetInstance();
   ASSERT(p);
   p->Init(ctx);

   if (!m_pimpl) {
#if defined(HAVE_GTKMM)
      m_pimpl = new CopyPasteDnDX11();
#endif
#if defined(WIN32)
#ifdef DND_VM
      m_pimpl = new VMCopyPasteDnDWin32();
#else
      m_pimpl = new CRTCopyPasteDnDWin32();
#endif
#endif
#if defined(__APPLE__)
      m_pimpl = new CopyPasteDnDMac();
#endif

      if (m_pimpl) {
         m_pimpl->Init(ctx);
         /*
          * Tell the Guest DnD Manager what capabilities we support.
          */
         p->SetCaps(m_pimpl->GetCaps());
      }
   }
}


/**
 *
 * Destructor.
 */

CopyPasteDnDWrapper::~CopyPasteDnDWrapper()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   if (m_pimpl) {
      if (IsCPRegistered()) {
         m_pimpl->UnregisterCP();
      }
      if (IsDnDRegistered()) {
         m_pimpl->UnregisterDnD();
      }
      delete m_pimpl;
   }
   GuestDnDCPMgr::Destroy();
}


/**
 *
 * Register copy and paste capabilities with the VMX. Try newest version
 * first, then fall back to the legacy implementation.
 *
 * @return TRUE on success, FALSE on failure
 */

gboolean
CopyPasteDnDWrapper::RegisterCP()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   ASSERT(m_pimpl);
   if (IsCPEnabled()) {
      return m_pimpl->RegisterCP();
   }
   return false;
}


/**
 *
 * Register DnD capabilities with the VMX. Handled by the platform layer.
 *
 * @return TRUE on success, FALSE on failure
 */

gboolean
CopyPasteDnDWrapper::RegisterDnD()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   ASSERT(m_pimpl);
   if (IsDnDEnabled()) {
      return m_pimpl->RegisterDnD();
   }
   return false;
}


/**
 *
 * Unregister copy paste capabilities. Handled by platform layer.
 */

void
CopyPasteDnDWrapper::UnregisterCP()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   ASSERT(m_pimpl);
   return m_pimpl->UnregisterCP();
}


/**
 *
 * Unregister DnD capabilities. Handled by platform layer.
 */

void
CopyPasteDnDWrapper::UnregisterDnD()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   ASSERT(m_pimpl);
   return m_pimpl->UnregisterDnD();
}


/**
 *
 * Get the version of the copy paste protocol being wrapped.
 *
 * @return copy paste protocol version.
 */

int
CopyPasteDnDWrapper::GetCPVersion()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   return m_cpVersion;
}


/**
 *
 * Get the version of the DnD protocol being wrapped.
 *
 * @return DnD protocol version.
 */

int
CopyPasteDnDWrapper::GetDnDVersion()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   return m_dndVersion;
}


/**
 *
 * Set a flag indicating that we are wrapping an initialized and registered
 * copy paste implementation, or not.
 *
 * @param[in] isRegistered If TRUE, protocol is registered, otherwise FALSE.
 */

void
CopyPasteDnDWrapper::SetCPIsRegistered(gboolean isRegistered)
{
   g_debug("%s: enter.\n", __FUNCTION__);
   m_isCPRegistered = isRegistered;
}


/**
 *
 * Get the flag indicating that we are wrapping an initialized and registered
 * copy paste implementation, or not.
 *
 * @return TRUE if copy paste is initialized, otherwise FALSE.
 */

gboolean
CopyPasteDnDWrapper::IsCPRegistered()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   return m_isCPRegistered;
}


/**
 *
 * Set a flag indicating that we are wrapping an initialized and registered
 * DnD implementation, or not.
 *
 * @param[in] isRegistered If TRUE, protocol is registered, otherwise FALSE.
 */

void
CopyPasteDnDWrapper::SetDnDIsRegistered(gboolean isRegistered)
{
   m_isDnDRegistered = isRegistered;
}


/**
 *
 * Get the flag indicating that we are wrapping an initialized and registered
 * DnD implementation, or not.
 *
 * @return TRUE if DnD is initialized, otherwise FALSE.
 */

gboolean
CopyPasteDnDWrapper::IsDnDRegistered()
{
   return m_isDnDRegistered;
}


/**
 *
 * Set a flag indicating that copy paste is enabled, or not. This is called
 * in response to SetOption RPC being received.
 *
 * @param[in] isEnabled If TRUE, copy paste is enabled, otherwise FALSE.
 */

void
CopyPasteDnDWrapper::SetCPIsEnabled(gboolean isEnabled)
{
   g_debug("%s: enter.\n", __FUNCTION__);
   m_isCPEnabled = isEnabled;
   if (!isEnabled && IsCPRegistered()) {
      UnregisterCP();
   } else if (isEnabled && !IsCPRegistered()) {
      RegisterCP();
   }
}


/**
 *
 * Get the flag indicating that copy paste was enabled (via SetOption RPC).
 *
 * @return TRUE if copy paste is enabled, otherwise FALSE.
 */

gboolean
CopyPasteDnDWrapper::IsCPEnabled()
{
   return m_isCPEnabled;
}


/**
 *
 * Set a flag indicating that DnD is enabled, or not. This is called
 * in response to SetOption RPC being received.
 *
 * @param[in] isEnabled If TRUE, DnD is enabled, otherwise FALSE.
 */

void
CopyPasteDnDWrapper::SetDnDIsEnabled(gboolean isEnabled)
{
   g_debug("%s: enter.\n", __FUNCTION__);
   m_isDnDEnabled = isEnabled;
   if (!isEnabled && IsDnDRegistered()) {
      UnregisterDnD();
   } else if (isEnabled && !IsDnDRegistered()) {
      RegisterDnD();
   }
}


/**
 *
 * Get the flag indicating that DnD was enabled (via SetOption) or not.
 *
 * @return TRUE if DnD is enabled, otherwise FALSE.
 */

gboolean
CopyPasteDnDWrapper::IsDnDEnabled()
{
   return m_isDnDEnabled;
}


/**
 *
 * Handle reset.
 */

void
CopyPasteDnDWrapper::OnResetInternal()
{
   g_debug("%s: enter.\n", __FUNCTION__);
}


/**
 *
 * Handle reset.
 *
 * Schedule the post-reset actions to happen a little after one cycle of the
 * RpcIn loop. This will give vmware a chance to receive the ATR
 * reinitialize the channel if appropriate.
 */

void
CopyPasteDnDWrapper::OnReset()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   AddDnDPluginResetTimer();
}


/**
 *
 * Handle no_rpc.
 *
 * Remove any actions that would need RPC channel.
 */

void
CopyPasteDnDWrapper::OnNoRpc()
{
   g_debug("%s: enter.\n", __FUNCTION__);
   RemoveDnDPluginResetTimer();
}


/**
 *
 * Handle cap reg. This is cross-platform so handle here instead of the
 * platform implementation.
 */

void
CopyPasteDnDWrapper::OnCapReg(gboolean set)
{
   g_debug("%s: enter.\n", __FUNCTION__);
}


/**
 *
 * Handle SetOption
 */

gboolean
CopyPasteDnDWrapper::OnSetOption(const char *option, const char *value)
{
   g_debug("%s: enter.\n", __FUNCTION__);
   return TRUE;
}


/**
 * Get capabilities by calling platform implementation.
 *
 * @return 32-bit mask of DnD/CP capabilities.
 */

uint32
CopyPasteDnDWrapper::GetCaps()
{
   ASSERT(m_pimpl);

   g_debug("%s: enter.\n", __FUNCTION__);
   return m_pimpl->GetCaps();
}
07070100000338000081A40000000000000000000000016822550500000B64000000000000000000000000000000000000005D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/copyPasteDnDWrapper.h  /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file copyPasteDnDWrapper.h
 *
 * This singleton class implements an abstraction around the concrete classes
 * that implement DnD, and copy and paste.
 */

#ifndef __COPYPASTEDNDWRAPPER_H__
#define __COPYPASTEDNDWRAPPER_H__

#include "vmware/tools/plugin.h"
#include "copyPasteDnDImpl.h"
#include "dndPluginInt.h"

class CopyPasteDnDWrapper
{
public:
   virtual ~CopyPasteDnDWrapper();
   static CopyPasteDnDWrapper *GetInstance();
   static void Destroy();
   gboolean RegisterCP();
   void UnregisterCP();
   gboolean RegisterDnD();
   void UnregisterDnD();
   void SetDnDVersion(int version) {m_dndVersion = version;};
   virtual int GetDnDVersion();
   void SetCPVersion(int version) {m_cpVersion = version;};
   virtual int GetCPVersion();
   void SetCPIsRegistered(gboolean isRegistered);
   gboolean IsCPRegistered();
   void SetDnDIsRegistered(gboolean isRegistered);
   gboolean IsDnDRegistered();
   void SetDnDIsEnabled(gboolean isEnabled);
   gboolean IsDnDEnabled();
   void SetCPIsEnabled(gboolean isEnabled);
   gboolean IsCPEnabled();
   void OnReset();
   void OnNoRpc();
   virtual void OnResetInternal();
   virtual void OnCapReg(gboolean set);
   virtual gboolean OnSetOption(const char *option, const char *value);
   virtual void Init(ToolsAppCtx *ctx);
   void PointerInit(void);
   virtual ToolsAppCtx *GetToolsAppCtx() {return NULL;};
   uint32 GetCaps();
   virtual void RemoveDnDPluginResetTimer(void) { }

protected:
   /*
    * We're a singleton, so it is a compile time error to call these.
    */
   CopyPasteDnDWrapper();
   CopyPasteDnDWrapper(const CopyPasteDnDWrapper &wrapper);
   CopyPasteDnDWrapper& operator=(const CopyPasteDnDWrapper &wrapper);

protected:
   virtual void AddDnDPluginResetTimer(void) { }

protected:
   gboolean m_isCPEnabled;
   gboolean m_isDnDEnabled;
   gboolean m_isCPRegistered;
   gboolean m_isDnDRegistered;
   int m_cpVersion;
   int m_dndVersion;
   static CopyPasteDnDWrapper *m_instance;
   CopyPasteDnDImpl *m_pimpl;
};

#endif // __COPYPASTEDNDWRAPPER_H__
07070100000339000081A400000000000000000000000168225505000007FF000000000000000000000000000000000000005900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/copyPasteDnDX11.h  /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file copyPasteDnDX11.h
 *
 * This class provides the concrete UI implementation for the DnD and
 * copy paste abstraction, for the X11 platform.
 */

#ifndef __COPYPASTEDNDX11_H__
#define __COPYPASTEDNDX11_H__

#include "dnd.h"     /* for DnDBlockControl */
#include "dndUIX11.h"
#include <gtk/gtk.h>
#include "vm_basic_types.h"
#include "copyPasteDnDImpl.h"

extern "C" {
void CopyPasteDnDWrapper_SetUnityMode(Bool mode);
}

class CopyPasteUIX11;

class CopyPasteDnDX11 : public CopyPasteDnDImpl
{
public:
   CopyPasteDnDX11();
   ~CopyPasteDnDX11();
   virtual gboolean Init(ToolsAppCtx *ctx);
   virtual void PointerInit();
   virtual gboolean RegisterCP();
   virtual void UnregisterCP();
   virtual gboolean RegisterDnD();
   virtual void UnregisterDnD();
   virtual void DnDVersionChanged(int version);
   virtual void CopyPasteVersionChanged(int version);
   virtual uint32 GetCaps();
   void SetUnityMode(Bool mode) {m_dndUI->SetUnityMode(mode);};
   void SetDnDAllowed(bool allowed);
   void SetCopyPasteAllowed(bool allowed);
private:
   Gtk::Main *m_main;
   CopyPasteUIX11 *m_copyPasteUI;
   DnDUIX11 *m_dndUI;
};

#endif // __COPYPASTEDNDX11_H__
 0707010000033A000081A40000000000000000000000016822550500003FDC000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/dndFileList.cc /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndFileList.cc
 *
 *      Handles filelists for CPClipboard.
 *      Relative paths are read/write to allow placing data on the clipboard,
 *      but full paths are write only. Full path parsing depends on g->h/h->g
 *      as well as dnd/fcp versions. Given that the host ui never needs to
 *      parse it, full paths are only stored in binary format for consumption
 *      by the vmx.
 *
 *      Local relative paths are expected to be encoded in Normalized UTF8 in
 *      local format.
 *
 *      XXX This file is almost identical to bora/apps/lib/cui/dndFileList.cc
 *      but right now we can not find a way to share the file.
 */

#if defined (_WIN32)
/*
 * When compile this file for Windows dnd plugin dll, there may be a conflict
 * between CRT and MFC libraries.  From
 * http://support.microsoft.com/default.aspx?scid=kb;en-us;q148652: The CRT
 * libraries use weak external linkage for the DllMain function. The MFC
 * libraries also contain this function. The function requires the MFC
 * libraries to be linked before the CRT library. The Afx.h include file
 * forces the correct order of the libraries.
 */
#include <afx.h>
#endif

#include "dndFileList.hh"

extern "C" {
   #include "dndClipboard.h"
   #include "cpNameUtil.h"
}

#include "vm_assert.h"
#include "file.h"

/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList --
 *
 *      Constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

DnDFileList::DnDFileList()
{
   mFileSize = 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::SetFileSize --
 *
 *      Sets the expected size of the files.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
DnDFileList::SetFileSize(uint64 fsize)  // IN: The size of the files.
{
   mFileSize = fsize;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::GetFileSize --
 *
 *      Gets the size of the files if known.
 *
 * Results:
 *      0 if the size of the files is not known, otherwise the expected size of
 *      the files.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

uint64
DnDFileList::GetFileSize() const
{
   return mFileSize;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::AddFile --
 *
 *      Add the full path and the relative path to the file list. Both strings
 *      should be normalized UTF8.
 *      Will not add file if the file list was created from a clipboard buffer.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
DnDFileList::AddFile(const std::string fullPath,        // IN: filename
                     const std::string relPath)         // IN: filename
{
   ASSERT(mFullPathsBinary.empty());

   if (!mFullPathsBinary.empty()) {
      return;
   }

   mRelPaths.push_back(relPath);
   mFullPaths.push_back(fullPath);
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::AddFileUri --
 *
 *      Add the full uri UTF8 path the file list.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
DnDFileList::AddFileUri(const std::string uriPath) // IN
{
   mUriPaths.push_back(uriPath);
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::AddFiles --
 *
 *      Add the full path list and the relative path list. Both lists should be
 *      normalized UTF8.
 *
 *      Will not add lists if the file list was created from a clipboard buffer.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
DnDFileList::AddFiles(const std::vector<std::string> fullPathList, // IN: filenames
                      const std::vector<std::string> relPathList)  // IN: filenames
{
   ASSERT(mFullPathsBinary.empty());

   if (!mFullPathsBinary.empty()) {
      return;
   }

   mRelPaths = relPathList;
   mFullPaths = fullPathList;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::AddFileAttributes --
 *
 *      Adds files attributes.
 *
 *      Will not add attributes if the file list was created from a clipboard
 *      buffer.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
DnDFileList::AddFileAttributes(const CPFileAttributes& attributes)  // IN
{
   ASSERT(mFullPathsBinary.empty());

   if (!mFullPathsBinary.empty()) {
      return;
   }

   mAttributeList.push_back(attributes);
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::SetRelPathsStr --
 *
 *      Set the relative paths based on the serialized input string. Paths are
 *      in normalized utf-8 with local path separators. Exposed for dnd/cp
 *      version 2.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
DnDFileList::SetRelPathsStr(const std::string inpath)   // IN:
{
   std::string::size_type mPos = 0;
   std::string curFile;
   std::string path;
   const char* cpath;

   if (inpath.empty()) {
      return;
   }

   if (inpath[inpath.size()-1] != '\0') {
      path = inpath + '\0';
   } else {
      path = inpath;
   }

   cpath = path.c_str();
   mRelPaths.clear();
   curFile = cpath;
   mPos = path.find('\0', mPos);

   while (mPos != std::string::npos) {
      mPos++;
      mRelPaths.push_back(curFile);
      curFile = cpath + mPos;
      mPos = path.find('\0', mPos);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::GetRelPaths --
 *
 *      Gets a vector containing the reletive paths of the files.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

std::vector<std::string>
DnDFileList::GetRelPaths() const
{
   return mRelPaths;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::GetFileAttributes --
 *
 *      Gets a vector containing file attributes.
 *
 * Results:
 *      File attributes.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

std::vector<CPFileAttributes>
DnDFileList::GetFileAttributes()
   const
{
   return mAttributeList;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::GetFullPathsStr --
 *
 *      Gets the serialized version of the full paths. If the file list was
 *      created from a CPClipboard, use the serialized input instead.
 *
 *      Local paths are serialized inot NUL separated pathes.
 *      CPName paths are converted from local to CPName and serialized into
 *      uint32,cpname pairs.
 *
 * Results:
 *      An std::string containing the serialized full paths. Empty string on
 *      error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

std::string
DnDFileList::GetFullPathsStr(bool local)        // IN: Use local format
   const
{
   std::string stringList;
   std::vector<std::string>::const_iterator i;

   if (mFullPathsBinary.empty() && !mFullPaths.empty()) {
      for (i = mFullPaths.begin(); i != mFullPaths.end(); ++i) {
         if (local) {
            stringList.append(i->c_str());
            stringList.push_back('\0');
         } else {
            char outPath[FILE_MAXPATH + 100];
            int32 outPathLen;

            outPathLen = CPNameUtil_ConvertToRoot(i->c_str(),
                                                  sizeof outPath,
                                                  outPath);
            if (outPathLen < 0) {
               continue;
            }

            stringList.append(reinterpret_cast<char *>(&outPathLen),
                              sizeof outPathLen);
            stringList.append(outPath, outPathLen);
         }
      }

      return stringList;
   } else if (!mFullPathsBinary.empty() && mFullPaths.empty()) {
      return mFullPathsBinary;
   } else {
      return "";
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::GetRelPathsStr --
 *
 *      Gets the serialized version of the relative paths. Primarily used for
 *      dnd/cp v2.
 *
 * Results:
 *      An std::string contatin the serialized full paths.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

std::string
DnDFileList::GetRelPathsStr()
   const
{
   std::string stringList("");
   std::vector<std::string>::const_iterator i;

   for (i = mRelPaths.begin(); i != mRelPaths.end(); ++i) {
      stringList.append(i->c_str());
      stringList.push_back('\0');
   }
   return stringList;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::GetUriPathsStr --
 *
 *      Gets the serialized version of the uri paths.
 *
 * Results:
 *      An std::string contatin the serialized uri paths.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

std::string
DnDFileList::GetUriPathsStr(void)
   const
{
   std::string stringList;
   std::vector<std::string>::const_iterator i;

   for (i = mUriPaths.begin(); i != mUriPaths.end(); i++) {
      stringList.append(i->c_str());
      stringList.push_back('\0');
   }
   return stringList;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::FromCPClipboard --
 *
 *      Loads the filelist from a buffer, typically from CPClipboard.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

bool
DnDFileList::FromCPClipboard(const void *buf,   // IN: Source buffer
                             size_t len)        // IN: Buffer length
{
   const CPFileList *flist;
   std::string relPaths;

   ASSERT(buf);
   ASSERT(len);
   if (!buf || !len) {
      return false;
   }

   flist = reinterpret_cast<const CPFileList *>(buf);
   relPaths.assign(
      reinterpret_cast<const char *>(flist->filelists), flist->relPathsLen);

   mRelPaths.clear();
   mFullPaths.clear();

   mFileSize = flist->fileSize;

   SetRelPathsStr(relPaths);
   mFullPathsBinary.assign(
      reinterpret_cast<const char *>(flist->filelists + flist->relPathsLen),
      flist->fulPathsLen);

   return true;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::AttributesFromCPClipboard --
 *
 *      Loads the attribute list from a buffer, typically from CPClipboard.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

bool
DnDFileList::AttributesFromCPClipboard(const void *buf, // IN: Source buffer
                                       size_t len)      // IN: Buffer length
{
   const CPAttributeList *alist;

   ASSERT(buf);
   ASSERT(len);
   if (!buf || !len) {
      return false;
   }

   alist = reinterpret_cast<const CPAttributeList *>(buf);

   mAttributeList.resize(alist->attributesLen);
   for (uint32 i = 0; i < alist->attributesLen; i++) {
      mAttributeList[i] = alist->attributeList[i];
   }

   return true;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::ToCPClipboard --
 *
 *      Serializes contents for CPClipboard in eithr CPFormat or local format.
 *
 * Results:
 *      false on error, true on success.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

bool
DnDFileList::ToCPClipboard(DynBuf *out, // OUT: Initialized output buffer
                           bool local)  // IN: Use local format
   const
{
   std::string strListRel;
   std::string strListFul;
   CPFileList header;

   strListRel = GetRelPathsStr();
   strListFul = GetFullPathsStr(local);

   if (!out) {
      return false;
   }

   /* Check if the size is too big. */
   if (strListRel.size() > MAX_UINT32 ||
       strListFul.size() > MAX_UINT32) {
      return false;
   }

   header.fileSize = mFileSize;
   header.relPathsLen = strListRel.size();
   header.fulPathsLen = strListFul.size();

   DynBuf_Append(out, &header, CPFILELIST_HEADER_SIZE);
   DynBuf_Append(out, strListRel.c_str(), header.relPathsLen);
   DynBuf_Append(out, strListFul.c_str(), header.fulPathsLen);

   return true;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::ToCPClipboard --
 *
 *      Serializes uri paths for CPClipboard in URI Format.
 *
 * Results:
 *      false on error, true on success.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

bool
DnDFileList::ToUriClipboard(DynBuf *out) // OUT: Initialized output buffer
   const
{
   std::string strListUri;
   UriFileList header;

   if (!out) {
      return false;
   }

   strListUri = GetUriPathsStr();

   /* Check if the size is too big. */
   if (strListUri.size() > MAX_UINT32) {
      return false;
   }

   header.fileSize = mFileSize;
   header.uriPathsLen = strListUri.size();

   DynBuf_Append(out, &header, URI_FILELIST_HEADER_SIZE);
   DynBuf_Append(out, strListUri.c_str(), header.uriPathsLen);

   return true;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::AttributesToCPClipboard --
 *
 *      Serializes attributes for CPClipboard in Attributes Format.
 *
 * Results:
 *      false on error, true on success.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

bool
DnDFileList::AttributesToCPClipboard(DynBuf *out) // IN
   const
{
   CPAttributeList header;

   if (!out) {
      return false;
   }

   header.attributesLen = mAttributeList.size();

   DynBuf_Append(out, &header, URI_ATTRIBUTES_LIST_HEADER_SIZE);
   if (header.attributesLen > 0) {
      DynBuf_Append(out,
                    &mAttributeList[0],
                    header.attributesLen * sizeof(CPFileAttributes));
   }

   return true;
}


/*
 *----------------------------------------------------------------------------
 *
 * DnDFileList::Clear --
 *
 *      Clears the contents of the filelist.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

void
DnDFileList::Clear()
{
   mRelPaths.clear();
   mFullPaths.clear();
   mUriPaths.clear();
   mAttributeList.clear();
   mFullPathsBinary.clear();
   mFileSize = 0;
}

0707010000033B000081A40000000000000000000000016822550500000A5B000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/dndFileList.hh /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndFileList.hh
 *
 *      Translates filelists to different formats.
 */

#ifndef DND_FILELIST_HH
#define DND_FILELIST_HH

#include <string>
#include <vector>

#include "vm_basic_types.h"

extern "C" {
#include "dndClipboard.h"
}

#include "dynbuf.h"

class DnDFileList {
   public:
      DnDFileList();
      ~DnDFileList() {};

      void SetFileSize(uint64 fsize);
      uint64 GetFileSize() const;
      void AddFile(const std::string fullPath,
                   const std::string relPath);
      void AddFileUri(const std::string uriPath);
      void AddFiles(const std::vector<std::string> fullPathList,
                    const std::vector<std::string> relPathList);
      void AddFileAttributes(const CPFileAttributes& attributes);

      /* Copy paste/dndV2 V2 rpc */
      void SetRelPathsStr(const std::string inpath);

      /* DnDFileTransfer & V2 RPC */
      std::string GetRelPathsStr() const;
      std::string GetFullPathsStr(bool local) const;
      std::string GetUriPathsStr() const;

      /* UI Local clipboard */
      std::vector<std::string> GetRelPaths() const;
      std::vector<CPFileAttributes> GetFileAttributes() const;

      /* CPClipboard */
      bool ToCPClipboard(DynBuf *out, bool local) const;
      bool ToUriClipboard(DynBuf *out) const;
      bool AttributesToCPClipboard(DynBuf *out) const;
      bool FromCPClipboard(const void *buf, size_t len);
      bool AttributesFromCPClipboard(const void *buf, size_t len);

      void Clear();

   private:

      std::vector<std::string> mRelPaths;
      std::vector<std::string> mFullPaths;
      std::vector<std::string> mUriPaths;
      std::vector<CPFileAttributes> mAttributeList;
      std::string mFullPathsBinary;
      uint64 mFileSize;
};

#endif // DND_FILELIST_HH
 0707010000033C000081A40000000000000000000000016822550500000640000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/dndPluginInt.h /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndPluginInt.h
 *
 *    Various DnD plugin defines.
 *
 */

#if !defined (__DNDPLUGIN_INT_H__)
#define __DNDPLUGIN_INT_H__

extern "C" {
   #include "conf.h"
}

#define DEBUG_PREFIX             "vmusr"
#define RPC_POLL_TIME            10
#define POINTER_POLL_TIME        10
#define UNGRABBED_POS (-100)
#define VMWARE_CLIP_FORMAT_NAME     L"VMwareClipFormat"
#define TOOLS_DND_VERSION_3         "tools.capability.dnd_version 3"
#define TOOLS_DND_VERSION_4         "tools.capability.dnd_version 4"
#define QUERY_VMX_DND_VERSION       "vmx.capability.dnd_version"
#define TOOLS_COPYPASTE_VERSION     "tools.capability.copypaste_version"
#define QUERY_VMX_COPYPASTE_VERSION "vmx.capability.copypaste_version"

#endif
0707010000033D000081A40000000000000000000000016822550500003B58000000000000000000000000000000000000005300000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/dndRpcV3.cc    /*********************************************************
 * Copyright (C) 2007-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @DnDRpcV3.cc --
 *
 * Implementation of the DnDRpcV3 class.
 */

#if defined (_WIN32)
/*
 * When compile this file for Windows dnd plugin dll, there may be a conflict
 * between CRT and MFC libraries.  From
 * http://support.microsoft.com/default.aspx?scid=kb;en-us;q148652: The CRT
 * libraries use weak external linkage for the DllMain function. The MFC
 * libraries also contain this function. The function requires the MFC
 * libraries to be linked before the CRT library. The Afx.h include file
 * forces the correct order of the libraries.
 */
#include <afx.h>
#endif

#include "dndRpcV3.hh"
#include "tracer.hh"

extern "C" {
   #include "debug.h"
   #include "dndClipboard.h"
}

#include "util.h"
#include "dndMsg.h"


/**
 * Constructor.
 *
 * @param[in] transport for sending packets.
 */

DnDRpcV3::DnDRpcV3(DnDCPTransport *transport)
   : mTransport(transport),
     mTransportInterface(TRANSPORT_GUEST_CONTROLLER_DND)
{
   ASSERT(mTransport);

   mUtil.Init(this);
   CPClipboard_Init(&mClipboard);
}


/**
 * Destructor.
 */

DnDRpcV3::~DnDRpcV3(void)
{
   CPClipboard_Destroy(&mClipboard);
}


/**
 * Init.
 */

void
DnDRpcV3::Init(void)
{
   TRACE_CALL();
   ASSERT(mTransport);
   mTransport->RegisterRpc(this, mTransportInterface);
}


/**
 * Send DND_HG_DRAG_ENTER_DONE message.
 *
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV3::SrcDragEnterDone(int32 x,
                           int32 y)
{
   TRACE_CALL();
   return mUtil.SendMsg(DND_HG_DRAG_ENTER_DONE, x, y);

}


/**
 * Send DND_HG_DRAG_READY message.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV3::SrcDragBeginDone(uint32 sessionId)
{
   TRACE_CALL();
   return mUtil.SendMsg(DND_HG_DRAG_READY);
}


/**
 * Send DND_HG_UPDATE_FEEDBACK message.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[in] feedback current dnd operation feedback.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV3::UpdateFeedback(uint32 sessionId,
                         DND_DROPEFFECT feedback)
{
   DnDMsg msg;
   bool ret = false;

   DnDMsg_Init(&msg);

   DnDMsg_SetCmd(&msg, DND_HG_UPDATE_FEEDBACK);

   if (!DnDMsg_AppendArg(&msg, &feedback, sizeof feedback)) {
      g_debug("%s: DnDMsg_AppendData failed.\n", __FUNCTION__);
      goto exit;
   }

   ret = mUtil.SendMsg(&msg);

exit:
   DnDMsg_Destroy(&msg);
   return ret;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 *
 * @return always true.
 */

bool
DnDRpcV3::SrcPrivDragEnter(uint32 sessionId)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] x mouse position x.
 * @param[ignored] y mouse position y.
 *
 * @return always true.
 */

bool
DnDRpcV3::SrcPrivDragLeave(uint32 sessionId,
                           int32 x,
                           int32 y)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] x mouse position x.
 * @param[ignored] y mouse position y.
 *
 * @return always true.
 */

bool
DnDRpcV3::SrcPrivDrop(uint32 sessionId,
                      int32 x,
                      int32 y)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] x mouse position x.
 * @param[ignored] y mouse position y.
 *
 * @return always true.
 */

bool
DnDRpcV3::SrcDrop(uint32 sessionId,
                  int32 x,
                  int32 y)
{
   TRACE_CALL();
   return true;
}


/**
 * Send DND_HG_DROP_DONE message.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[in] stagingDirCP staging dir name in cross-platform format
 * @param[in] sz the staging dir name size in bytes
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV3::SrcDropDone(uint32 sessionId,
                      const uint8 *stagingDirCP,
                      uint32 sz)
{
   DnDMsg msg;
   bool ret = false;

   TRACE_CALL();

   DnDMsg_Init(&msg);

   /* Construct msg with both cmd CP_HG_START_FILE_COPY and stagingDirCP. */
   DnDMsg_SetCmd(&msg, DND_HG_DROP_DONE);
   if (!DnDMsg_AppendArg(&msg, (void *)stagingDirCP, sz)) {
      g_debug("%s: DnDMsg_AppendData failed.\n", __FUNCTION__);
      goto exit;
   }

   ret = mUtil.SendMsg(&msg);

exit:
   DnDMsg_Destroy(&msg);
   return ret;
}


/**
 * Send DND_GH_DRAG_ENTER message.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[in] clip cross-platform clipboard data.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV3::DestDragEnter(uint32 sessionId,
                        const CPClipboard *clip)
{
   TRACE_CALL();
   return mUtil.SendMsg(DND_GH_DRAG_ENTER, clip);
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] clip cross-platform clipboard data.
 *
 * @return always true.
 */

bool
DnDRpcV3::DestSendClip(uint32 sessionId,
                       const CPClipboard *clip)
{
   TRACE_CALL();
   return true;
}


/**
 * Send DND_GH_NOT_PENDING message.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV3::DragNotPending(uint32 sessionId)
{
   TRACE_CALL();
   return mUtil.SendMsg(DND_GH_NOT_PENDING);
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] x mouse position x.
 * @param[ignored] y mouse position y.
 *
 * @return always true.
 */

bool
DnDRpcV3::DestDragLeave(uint32 sessionId,
                        int32 x,
                        int32 y)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] x mouse position x.
 * @param[ignored] y mouse position y.
 *
 * @return always true.
 */

bool
DnDRpcV3::DestDrop(uint32 sessionId,
                   int32 x,
                   int32 y)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] x mouse position x.
 * @param[ignored] y mouse position y.
 *
 * @return always true.
 */

bool
DnDRpcV3::QueryExiting(uint32 sessionId,
                       int32 x,
                       int32 y)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId  Active session id the controller assigned earlier.
 * @param[ignored] show       Show or hide unity DnD detection window.
 * @param[ignored] unityWndId The unity windows id.
 *
 * @return always true.
 */

bool
DnDRpcV3::UpdateUnityDetWnd(uint32 sessionId,
                            bool show,
                            uint32 unityWndId)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] x mouse position x.
 * @param[ignored] y mouse position y.
 *
 * @return always true.
 */

bool
DnDRpcV3::MoveMouse(uint32 sessionId,
                    int32 x,
                    int32 y)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 *
 * @return always true.
 */

bool
DnDRpcV3::RequestFiles(uint32 sessionId)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] success the file transfer operation was successful or not
 * @param[ignored] stagingDirCP staging dir name in cross-platform format
 * @param[ignored] sz the staging dir name size in bytes
 *
 * @return always true.
 */

bool
DnDRpcV3::SendFilesDone(uint32 sessionId,
                        bool success,
                        const uint8 *stagingDirCP,
                        uint32 sz)
{
   TRACE_CALL();
   return true;
}


/**
 * Not needed for version 3.
 *
 * @param[ignored] sessionId active session id the controller assigned earlier.
 * @param[ignored] success the file transfer operation was successful or not
 *
 * @return always true.
 */

bool
DnDRpcV3::GetFilesDone(uint32 sessionId,
                       bool success)
{
   TRACE_CALL();
   return true;
}


/**
 * Send a packet.
 *
 * @param[in] destId destination address id.
 * @param[in] packet
 * @param[in] length packet length
 *
 * @return true on success, false otherwise.
 */

bool
DnDRpcV3::SendPacket(uint32 destId,
                     const uint8 *packet,
                     size_t length)
{
   TRACE_CALL();
   return mTransport->SendPacket(destId, mTransportInterface, packet, length);
}


/**
 * Handle a received version 3 message.
 *
 * @param[in] params parameter list for received message.
 * @param[in] binary attached binary data for received message.
 * @param[in] binarySize
 */

void
DnDRpcV3::HandleMsg(RpcParams *params,
                    const uint8 *binary,
                    uint32 binarySize)
{
   DnDMsg msg;
   DnDMsgErr ret;
   DynBuf *buf = NULL;

   DnDMsg_Init(&msg);

   ret = DnDMsg_UnserializeHeader(&msg, (void *)binary, binarySize);
   if (DNDMSG_SUCCESS != ret) {
      g_debug("%s: DnDMsg_UnserializeHeader failed %d\n", __FUNCTION__, ret);
      goto exit;
   }

   ret = DnDMsg_UnserializeArgs(&msg,
                                (void *)(binary + DNDMSG_HEADERSIZE_V3),
                                binarySize - DNDMSG_HEADERSIZE_V3);
   if (DNDMSG_SUCCESS != ret) {
      g_debug("%s: DnDMsg_UnserializeArgs failed with %d\n", __FUNCTION__, ret);
      goto exit;
   }

   g_debug("%s: Got %d, binary size %d.\n", __FUNCTION__, DnDMsg_GetCmd(&msg),
           binarySize);

   /*
    * Translate command and emit signal. Session Id 1 is used because version
    * 3 command does not provide session Id.
    */
   switch (DnDMsg_GetCmd(&msg)) {
   case DND_HG_DRAG_ENTER:
   {
      CPClipboard_Clear(&mClipboard);

      /* Unserialize clipboard data for the command. */
      buf = DnDMsg_GetArg(&msg, 0);
      if (!CPClipboard_Unserialize(&mClipboard, DynBuf_Get(buf), DynBuf_GetSize(buf))) {
         g_debug("%s: CPClipboard_Unserialize failed.\n", __FUNCTION__);
         break;
      }
      SrcDragEnterDone(DRAG_DET_WINDOW_WIDTH / 2,
                       DRAG_DET_WINDOW_WIDTH / 2);
      break;
   }
   case DND_HG_DRAG_START:
      srcDragBeginChanged.emit(1, &mClipboard);
      CPClipboard_Clear(&mClipboard);
      break;
   case DND_HG_CANCEL:
      srcCancelChanged.emit(1);
      break;
   case DND_HG_DROP:
      srcDropChanged.emit(1, 0, 0);
      break;
   case DND_GH_CANCEL:
      destCancelChanged.emit(1);
      break;
   case DND_GH_PRIVATE_DROP:
   {
      int32 x = 0;
      int32 y = 0;

      buf = DnDMsg_GetArg(&msg, 0);
      if (DynBuf_GetSize(buf) == sizeof(int32)) {
         memcpy(&x, (const char *)DynBuf_Get(buf), sizeof(int32));
      } else {
         break;
      }

      buf = DnDMsg_GetArg(&msg, 1);
      if (DynBuf_GetSize(buf) == sizeof(int32)) {
         memcpy(&y, (const char *)DynBuf_Get(buf), sizeof(int32));
      } else {
         break;
      }

      destPrivDropChanged.emit(1, x, y);
      break;
   }
   case DND_GH_UPDATE_UNITY_DET_WND:
   {
      bool show = false;
      uint32 unityWndId;

      buf = DnDMsg_GetArg(&msg, 0);
      if (DynBuf_GetSize(buf) == sizeof(show)) {
         memcpy(&show, (const char *)DynBuf_Get(buf), sizeof(show));
      } else {
         break;
      }

      buf = DnDMsg_GetArg(&msg, 1);
      if (DynBuf_GetSize(buf) == sizeof(unityWndId)) {
         memcpy(&unityWndId, (const char *)DynBuf_Get(buf), sizeof(unityWndId));
      } else {
         break;
      }
      updateUnityDetWndChanged.emit(1, show, unityWndId);

      break;
   }
   case DND_GH_QUERY_PENDING_DRAG:
   {
      int32 x = 0;
      int32 y = 0;

      buf = DnDMsg_GetArg(&msg, 0);
      if (DynBuf_GetSize(buf) == sizeof(int32)) {
         memcpy(&x, (const char *)DynBuf_Get(buf), sizeof(int32));
      } else {
         break;
      }

      buf = DnDMsg_GetArg(&msg, 1);
      if (DynBuf_GetSize(buf) == sizeof(int32)) {
         memcpy(&y, (const char *)DynBuf_Get(buf), sizeof(int32));
      } else {
         break;
      }
      queryExitingChanged.emit(1, x, y);
      break;
   }
   case DND_UPDATE_MOUSE:
   {
      int32 x = 0;
      int32 y = 0;

      buf = DnDMsg_GetArg(&msg, 0);
      if (DynBuf_GetSize(buf) == sizeof(x)) {
         memcpy(&x, (const char *)DynBuf_Get(buf), sizeof(x));
      } else {
         break;
      }

      buf = DnDMsg_GetArg(&msg, 1);
      if (DynBuf_GetSize(buf) == sizeof(y)) {
         memcpy(&y, (const char *)DynBuf_Get(buf), sizeof(y));
      } else {
         break;
      }

      moveMouseChanged.emit(1, x, y);
      break;
   }
   case DND_HG_FILE_COPY_DONE:
   {
      bool success;

      buf = DnDMsg_GetArg(&msg, 0);
      if (DynBuf_GetSize(buf) == sizeof(success)) {
         memcpy(&success, (const char *)DynBuf_Get(buf), sizeof(success));
      } else {
         break;
      }

      buf = DnDMsg_GetArg(&msg, 1);
      getFilesDoneChanged.emit(1, success, (const uint8 *)DynBuf_Get(buf), DynBuf_GetSize(buf));
      break;
   }
   default:
      g_debug("%s: got unsupported new command %d.\n", __FUNCTION__,
              DnDMsg_GetCmd(&msg));
   }
exit:
   DnDMsg_Destroy(&msg);
}


/**
 * Callback from transport layer after received a packet from srcId.
 *
 * @param[in] srcId addressId where the packet is from
 * @param[in] packet
 * @param[in] packetSize
 */

void
DnDRpcV3::OnRecvPacket(uint32 srcId,
                       const uint8 *packet,
                       size_t packetSize)
{
   TRACE_CALL();
   mUtil.OnRecvPacket(srcId, packet, packetSize);
}
0707010000033E000081A40000000000000000000000016822550500000F0B000000000000000000000000000000000000005300000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/dndRpcV3.hh    /*********************************************************
 * Copyright (C) 2010-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file dndRpcV3.hh --
 *
 * Rpc layer object for DnD version 3.
 */

#ifndef DND_RPC_V3_HH
#define DND_RPC_V3_HH

#include <sigc++/trackable.h>
#include "dndRpc.hh"
#include "dndCPTransport.h"
#include "rpcV3Util.hpp"

#include "dnd.h"
#include "dndMsg.h"

extern "C" {
   #include "vmware/tools/guestrpc.h"
}

class LIB_EXPORT DnDRpcV3
   : public DnDRpc,
     public sigc::trackable
{
public:
   DnDRpcV3(DnDCPTransport *transport);
   virtual ~DnDRpcV3(void);

   virtual void Init(void);
   virtual void SendPing(uint32 caps) {};

   /* DnD source. */
   virtual bool SrcDragBeginDone(uint32 sessionId);
   virtual bool SrcDrop(uint32 sessionId, int32 x, int32 y);
   virtual bool SrcDropDone(uint32 sessionId, const uint8 *stagingDirCP, uint32 sz);

   virtual bool SrcPrivDragEnter(uint32 sessionId);
   virtual bool SrcPrivDragLeave(uint32 sessionId, int32 x, int32 y);
   virtual bool SrcPrivDrop(uint32 sessionId, int32 x, int32 y);
   virtual bool SrcCancel(uint32 sessionId) { return true; };

   /* DnD destination. */
   virtual bool DestDragEnter(uint32 sessionId,
                              const CPClipboard *clip);
   virtual bool DestSendClip(uint32 sessionId,
                             const CPClipboard *clip);
   virtual bool DestDragLeave(uint32 sessionId,
                              int32 x,
                              int32 y);
   virtual bool DestDrop(uint32 sessionId,
                         int32 x,
                         int32 y);
   virtual bool DestCancel(uint32 sessionId) { return true; };

   /* Common. */
   virtual bool UpdateFeedback(uint32 sessionId, DND_DROPEFFECT feedback);
   virtual bool MoveMouse(uint32 sessionId,
                          int32 x,
                          int32 y);
   virtual bool QueryExiting(uint32 sessionId, int32 x, int32 y);
   virtual bool DragNotPending(uint32 sessionId);
   virtual bool UpdateUnityDetWnd(uint32 sessionId,
                                  bool show,
                                  uint32 unityWndId);
   virtual bool RequestFiles(uint32 sessionId);
   virtual bool SendFilesDone(uint32 sessionId,
                              bool success,
                              const uint8 *stagingDirCP,
                              uint32 sz);
   virtual bool GetFilesDone(uint32 sessionId,
                             bool success);

   virtual void HandleMsg(RpcParams *params,
                          const uint8 *binary,
                          uint32 binarySize);
   virtual bool SendPacket(uint32 destId,
                           const uint8 *packet,
                           size_t length);
   virtual void OnRecvPacket(uint32 srcId,
                             const uint8 *packet,
                             size_t packetSize);

private:
   bool SrcDragEnterDone(int32 x, int32 y);
   DnDCPTransport *mTransport;
   TransportInterfaceType mTransportInterface;
   CPClipboard mClipboard;
   RpcV3Util mUtil;
};

#endif // DND_RPC_V3_HH
 0707010000033F000081A40000000000000000000000016822550500001BF8000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/dndUIX11.h /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file dndUIX11.h
 *
 *    Implement the methods that allow DnD between host and guest for
 *    protocols V3 or greater.
 *
 */

#ifndef __DND_UI_X11_H__
#define __DND_UI_X11_H__

#include "stringxx/string.hh"
#include "dnd.h"
#include "str.h"
#include "util.h"
#include "vmblock.h"
#include "dynbuf.h"
#include "dynxdr.h"
#include "posix.h"

extern "C" {
#include "debug.h"
#include "dndClipboard.h"
#include "../dnd/dndFileContentsUtil.h"
#include "cpNameUtil.h"
#include "vmware/tools/guestrpc.h"
#include "vmware/tools/plugin.h"
}

#include "guestDnD.hh"
#include "dndFileList.hh"
#include "dragDetWndX11.h"

struct DblLnkLst_Links;

/**
 * The DnDUI class implements the UI portion of DnD V3 and greater
 * versions of the protocol.
 */
class DnDUIX11
   : public sigc::trackable
{
public:
   DnDUIX11(ToolsAppCtx *ctx);
   ~DnDUIX11();
   bool Init();
   void VmxDnDVersionChanged(RpcChannel *chan, uint32 version);
   void SetDnDAllowed(bool isDnDAllowed)
      {ASSERT(mDnD); mDnD->SetDnDAllowed(isDnDAllowed);}
   void SetBlockControl(DnDBlockControl *blockCtrl);
   void SetUnityMode(Bool mode)
      {mUnityMode = mode;};

   DragDetWnd *GetFullDetWnd() {return mDetWnd;}
   GtkWidget *GetDetWndAsWidget();

private:
   /*
    * Blocking FS Helper Functions.
    */
   void AddBlock();
   void RemoveBlock();

   bool TryXTestFakeDeviceButtonEvent();

   /*
    * Callbacks from Common DnD layer.
    */
   void ResetUI();
   void OnMoveMouse(int32 x, int32 y);

   /*
    * Source functions for HG DnD.
    */
   void OnSrcDragBegin(const CPClipboard *clip, std::string stagingDir);
   void OnSrcDrop();

   /*
    * Called when HG Dnd is completed.
    */
   void OnSrcCancel();

   /*
    * Called when GH DnD is completed.
    */
   void OnPrivateDrop(int32 x, int32 y);
   void OnDestCancel();

   /*
    * Source functions for file transfer.
    */
   void OnGetFilesDone(bool success);

   /*
    * Callbacks for showing/hiding detection window.
    */
   void OnUpdateDetWnd(bool bShow, int32 x, int32 y);
   void OnUpdateUnityDetWnd(bool bShow, uint32 unityWndId, bool bottom);
   void OnDestMoveDetWndToMousePos();

   /*
    * Gtk+ Callbacks: Drag Destination.
    */
   void OnGtkDragDataReceived(const Glib::RefPtr<Gdk::DragContext> &dc,
                              int x, int y, const Gtk::SelectionData &sd,
                              guint info, guint time);
   bool OnGtkDragDrop(const Glib::RefPtr<Gdk::DragContext> &dc, int x, int y,
                      guint time);
   void OnGtkDragLeave(const Glib::RefPtr<Gdk::DragContext> &dc, guint time);
   bool OnGtkDragMotion(const Glib::RefPtr<Gdk::DragContext> &dc, int x,
                        int y, guint time);

   /*
    * Gtk+ Callbacks: Drag Source.
    */
   void OnGtkDragBegin(const Glib::RefPtr<Gdk::DragContext>& context);
   void OnGtkDragDataGet(const Glib::RefPtr<Gdk::DragContext>& context,
                         Gtk::SelectionData& selection_data, guint info,
                         guint time);
   void OnGtkDragEnd(const Glib::RefPtr<Gdk::DragContext>& context);
   /*
    * Source functions for HG DnD. Makes calls to common layer.
    */
   void SourceDragStartDone();
   void SourceUpdateFeedback(DND_DROPEFFECT effect);

   /*
    * Target function for GH DnD. Makes call to common layer.
    */
   void TargetDragEnter();

   /*
    * Other signal handlers for tracing.
    */

   bool GtkEnterEventCB(GdkEventCrossing *event);
   bool GtkLeaveEventCB(GdkEventCrossing *event);
   bool GtkMapEventCB(GdkEventAny *event);
   bool GtkUnmapEventCB(GdkEventAny *event);
   void GtkRealizeEventCB();
   void GtkUnrealizeEventCB();
   bool GtkMotionNotifyEventCB(GdkEventMotion *event);
   bool GtkConfigureEventCB(GdkEventConfigure *event);
   bool GtkButtonPressEventCB(GdkEventButton *event);
   bool GtkButtonReleaseEventCB(GdkEventButton *event);

   /*
    * Misc methods.
    */
   void InitGtk();

   bool SetCPClipboardFromGtk(const Gtk::SelectionData& sd);
   bool RequestData(const Glib::RefPtr<Gdk::DragContext> &dc,
                    guint timeValue);
   std::string GetLastDirName(const std::string &str);
   utf::utf8string GetNextPath(utf::utf8string &str, size_t& index);

   static DND_DROPEFFECT ToDropEffect(const Gdk::DragAction action);
   static unsigned long GetTimeInMillis();

   bool SendFakeXEvents(const bool showWidget, const bool buttonEvent,
                        const bool buttonPress, const bool moveWindow,
                        const bool coordsProvided,
                        const int xCoord, const int yCoord);
   bool SendFakeMouseMove(const int x, const int y);
   bool WriteFileContentsToStagingDir();

   static inline bool TargetIsPlainText(const utf::string& target) {
      return    target == TARGET_NAME_STRING
             || target == TARGET_NAME_TEXT_PLAIN
             || target == TARGET_NAME_UTF8_STRING
             || target == TARGET_NAME_COMPOUND_TEXT;
   }

   static inline bool TargetIsRichText(const utf::string& target) {
      return    target == TARGET_NAME_APPLICATION_RTF
             || target == TARGET_NAME_TEXT_RICHTEXT
             || target == TARGET_NAME_TEXT_RTF;
   }

   void OnWorkAreaChanged(Glib::RefPtr<Gdk::Screen> screen);

   ToolsAppCtx *mCtx;
   GuestDnDMgr *mDnD;
   std::string mHGStagingDir;
   utf::string mHGFileContentsUriList;
   DragDetWnd *mDetWnd;
   CPClipboard mClipboard;
   DnDBlockControl *mBlockCtrl;
   DND_FILE_TRANSFER_STATUS mHGGetFileStatus;
   bool mBlockAdded;

   /* State to determine if drag motion is a drag enter. */
   bool mGHDnDInProgress;
   /* Icon updates from the guest. */
   /* Only update mouse when we have clipboard contents from the host. */
   bool mGHDnDDataReceived;
   bool mGHDnDDropOccurred;
   bool mUnityMode;
   bool mInHGDrag;
   DND_DROPEFFECT mEffect;
   int32 mMousePosX;
   int32 mMousePosY;
   GdkDragContext *mDragCtx;
   int mNumPendingRequest;
   unsigned long mDestDropTime;
   uint64 mTotalFileSize;

   /*
    * Upper left corner of our work area, a safe place for us to place
    * our detection window without clashing with a windows parented to the
    * composite overlay window.
    */
   Gdk::Point mOrigin;

   bool mUseUInput;
   int mScreenWidth;
   int mScreenHeight;
};

#endif // __DND_UI_X11_H__
07070100000340000081A40000000000000000000000016822550500000A93000000000000000000000000000000000000005700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/dragDetWndX11.h    /*********************************************************
 * Copyright (C) 2009-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file dragDetWnd.h
 *
 *    Header for the DragDetWnd class.
 */

#ifndef DRAG_DET_WND_H
#define DRAG_DET_WND_H

#include <gtkmm.h>

#if !defined(DETWNDTEST)
#include "dnd.h"
#endif

class DnDUI;

/*
 * DragDetWnd is an invisible window, there are two ways to create an
 * invisible window:
 * 1. inherits from class Gtk::Invisible;
 * 2. inherits from class Gtk::Window and set the opacity to 1%;
 * Class Gtk::Invisible cannot be used for Wayland because
 * the Gtk::Invisible cannot receive the mouse event with Wayland,
 * it seems this is a bug of Wayland.
 * So two drag detection windows are created, one inherits from
 * Gtk::Invisible which is used for the X11, and another window
 * inherits from Gtk::Window which is used for the Wayland.
 * After Wayland fixes the bug of Gtk::Invisible, the window
 * inherits from Gtk::Window will be removed.
 */
template<class TBase>
class DragDetWndImpl : public TBase
{
public:
   DragDetWndImpl() {}
   virtual ~DragDetWndImpl() {}
};

class DragDetWnd
{
public:
   DragDetWnd();
   virtual ~DragDetWnd();

   Gtk::Widget *GetWnd();
   void Show();
   void Hide();
   void Raise();
   void Lower();
   int GetScreenWidth();
   int GetScreenHeight();
   void SetGeometry(const int x, const int y,
                    const int width, const int height);
   void GetGeometry(int &x, int &y, int &width, int &height);
   void SetIsVisible(const bool isVisible) {m_isVisible = isVisible;};
   bool GetIsVisible() {return m_isVisible;};
#if defined(DETWNDEBUG)
   void DebugSetAttributes();
#endif
private:
   void Flush();
   bool m_isVisible;
   Gtk::Widget *mWnd;
};

#if defined(DETWNDTEST)
class DragDetWndTest : public Gtk::Window
{
public:
   void CreateTestUI();
private:
   virtual void RunUnitTests();
   Gtk::Button m_button;
};

#endif

#endif // DRAG_DET_WND_H
 07070100000341000081A40000000000000000000000016822550500000FF9000000000000000000000000000000000000005900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestCopyPaste.hh  /*********************************************************
 * Copyright (C) 2010-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestCopyPaste.hh --
 *
 * CopyPaste common layer classes for guest.
 */

#ifndef GUEST_COPY_PASTE_HH
#define GUEST_COPY_PASTE_HH

#include <sigc++/trackable.h>
#include <string>
#include "copyPasteRpc.hh"
#include "guestFileTransfer.hh"

#include "dnd.h"

enum GUEST_CP_STATE {
   GUEST_CP_INVALID = 0,
   GUEST_CP_READY,
   GUEST_CP_HG_FILE_COPYING,
};

class GuestCopyPasteSrc;
class GuestCopyPasteDest;

class GuestCopyPasteMgr
   : public sigc::trackable
{
public:
   GuestCopyPasteMgr(DnDCPTransport *transport);
   ~GuestCopyPasteMgr(void);

   sigc::signal<void, const CPClipboard*> srcRecvClipChanged;
   sigc::signal<void> destRequestClipChanged;
   sigc::signal<void, bool> getFilesDoneChanged;

   GUEST_CP_STATE GetState(void) { return mCPState; }
   void SetState(GUEST_CP_STATE state);
   CopyPasteRpc *GetRpc(void) { return mRpc; }
   GuestCopyPasteSrc *GetCopyPasteSrc(void)
      { return mSrc; }
   GuestCopyPasteDest *GetCopyPasteDest(void)
      { return mDest; }
   void ResetCopyPaste(void);

   uint32 GetSessionId(void) { return mSessionId; }
   void SetSessionId(uint32 id);

   void DestUISendClip(const CPClipboard *clip);
   const std::string SrcUIRequestFiles(const std::string &dir = "");
   bool IsCopyPasteAllowed (void) { return mCopyPasteAllowed; }
   void SetCopyPasteAllowed(bool isCopyPasteAllowed)
   { mCopyPasteAllowed = isCopyPasteAllowed; }
   void VmxCopyPasteVersionChanged(uint32 version);
   Bool CheckCapability(uint32 capsRequest);
private:
   void OnRpcSrcRecvClip(uint32 sessionId,
                         bool isActive,
                         const CPClipboard *clip);
   void OnRpcDestRequestClip(uint32 sessionId,
                             bool isActive);
   void OnPingReply(uint32 capabilities);
   GuestCopyPasteSrc *mSrc;
   GuestCopyPasteDest *mDest;
   CopyPasteRpc *mRpc;
   GUEST_CP_STATE mCPState;
   DnDCPTransport *mTransport;
   uint32 mSessionId;
   bool mCopyPasteAllowed;
   uint32 mResolvedCaps;       // caps as returned in ping reply, or default.
};


class GuestCopyPasteSrc
   : public sigc::trackable
{
public:
   GuestCopyPasteSrc(GuestCopyPasteMgr *mgr);
   ~GuestCopyPasteSrc(void);

   /* Common CopyPaste layer API exposed to UI for CopyPaste source. */
   const std::string UIRequestFiles(const std::string &dir = "");
   void OnRpcRecvClip(bool isActive,
                      const CPClipboard *clip);

private:
   /* Callbacks from rpc for CopyPaste source. */
   void OnRpcGetFilesDone(uint32 sessionId,
                          bool success,
                          const uint8 *stagingDirCP,
                          uint32 sz);
   const std::string &SetupDestDir(const std::string &destDir);

   GuestCopyPasteMgr *mMgr;
   CPClipboard mClipboard;
   std::string mStagingDir;
};


class GuestCopyPasteDest
   : public sigc::trackable
{
public:
   GuestCopyPasteDest(GuestCopyPasteMgr *mgr);

   /* Common CopyPaste layer API exposed to UI for CopyPaste destination. */
   void UISendClip(const CPClipboard *clip);
   /* Callbacks from rpc for CopyPaste destination. */
   void OnRpcRequestClip(bool isActive);

private:
   GuestCopyPasteMgr *mMgr;
   bool mIsActive;
};


#endif // GUEST_COPY_PASTE_HH

   07070100000342000081A400000000000000000000000168225505000019AE000000000000000000000000000000000000005300000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestDnD.hh    /*********************************************************
 * Copyright (C) 2010-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestDnD.hh --
 *
 * DnD common layer classes for guest.
 */

#ifndef GUEST_DND_HH
#define GUEST_DND_HH

#include <sigc++/trackable.h>
#include "dndRpcV4.hh"
#include "guestFileTransfer.hh"

#include "capsProvider.h"

#include <string>

#include "dnd.h"

extern "C" {
   #include "vmware/tools/plugin.h"
}

#define UNGRAB_TIMEOUT 500        // 0.5s
#define HIDE_DET_WND_TIMER 500    // 0.5s
#define UNITY_DND_DET_TIMEOUT 500 // 0.5s

enum GUEST_DND_STATE {
   GUEST_DND_INVALID = 0,
   GUEST_DND_READY,
   /* As destination. */
   GUEST_DND_QUERY_EXITING,
   GUEST_DND_DEST_DRAGGING,
   /* In private dragging mode. */
   GUEST_DND_PRIV_DRAGGING,
   /* As source. */
   GUEST_DND_SRC_DRAGBEGIN_PENDING,
   GUEST_DND_SRC_CANCEL_PENDING,
   GUEST_DND_SRC_DRAGGING,
};

class GuestDnDSrc;
class GuestDnDDest;

class GuestDnDMgr
   : public sigc::trackable, public CapsProvider
{
public:
   GuestDnDMgr(DnDCPTransport *transport,
               ToolsAppCtx *ctx);
   virtual ~GuestDnDMgr(void);

   sigc::signal<void, int, int> moveMouseChanged;
   sigc::signal<void, bool, int, int> updateDetWndChanged;
   sigc::signal<void, bool, uint32, bool> updateUnityDetWndChanged;
   sigc::signal<void, GUEST_DND_STATE> stateChanged;

   sigc::signal<void, const CPClipboard*, std::string> srcDragBeginChanged;
   sigc::signal<void> srcDropChanged;
   sigc::signal<void> srcCancelChanged;
   sigc::signal<void, bool> getFilesDoneChanged;

   sigc::signal<void> destCancelChanged;
   sigc::signal<void, int32, int32> privDropChanged;
   sigc::signal<void> destMoveDetWndToMousePosChanged;

   GuestDnDSrc *GetDnDSrc(void) { return mSrc; }
   GuestDnDDest *GetDnDDest(void) { return mDest; }

   /* Common DnD layer API exposed to UI (all platforms) for DnD source. */
   void SrcUIDragBeginDone(void);
   void SrcUIUpdateFeedback(DND_DROPEFFECT feedback);

   void DestUIDragEnter(const CPClipboard *clip);
   DnDRpc *GetRpc(void) { return mRpc; }

   GUEST_DND_STATE GetState(void) { return mDnDState; }
   void SetState(GUEST_DND_STATE state);
   void UpdateDetWnd(bool show, int32 x, int32 y);
   void HideDetWnd(void) { UpdateDetWnd(false, 0, 0); }
   void ShowDetWnd(int32 x, int32 y) { UpdateDetWnd(true, x, y); }
   void UnityDnDDetTimeout(void);
   uint32 GetSessionId(void) { return mSessionId; }
   void SetSessionId(uint32 id) { mSessionId = id; }
   void ResetDnD(void);
   void DelayHideDetWnd(void);
   void SetHideDetWndTimer(GSource *gs) { mHideDetWndTimer = gs; }
   void UngrabTimeout(void);
   void RemoveUngrabTimeout(void);
   bool IsDnDAllowed (void) { return mDnDAllowed; }
   void SetDnDAllowed(bool isDnDAllowed)
   { mDnDAllowed = isDnDAllowed;}
   void VmxDnDVersionChanged(uint32 version);
   bool IsDragEnterAllowed(void);
   Bool CheckCapability(uint32 capsRequest);
   virtual bool NeedDoMouseCoordinateConversion() { return true; }

   static gboolean DnDUngrabTimeout(void *clientData);
   static gboolean DnDHideDetWndTimer(void *clientData);
   static gboolean DnDUnityDetTimeout(void *clientData);

protected:
   virtual void OnRpcSrcDragBegin(uint32 sessionId,
                                  const CPClipboard *clip) = 0;
   virtual void OnRpcQueryExiting(uint32 sessionId, int32 x, int32 y);
   void OnRpcUpdateUnityDetWnd(uint32 sessionId,
                               bool show,
                               uint32 unityWndId);
   void OnRpcMoveMouse(uint32 sessionId,
                       int32 x,
                       int32 y);
   void OnPingReply(uint32 capabilities);

   virtual void AddDnDUngrabTimeoutEvent() = 0;
   virtual void AddUnityDnDDetTimeoutEvent() = 0;
   virtual void AddHideDetWndTimerEvent() = 0;
   virtual void CreateDnDRpcWithVersion(uint32 version) = 0;

   GuestDnDSrc *mSrc;
   GuestDnDDest *mDest;
   DnDRpc *mRpc;
   GUEST_DND_STATE mDnDState;
   uint32 mSessionId;
   GSource *mHideDetWndTimer;
   GSource *mUnityDnDDetTimeout;
   GSource *mUngrabTimeout;
   bool mDnDAllowed;
   DnDCPTransport *mDnDTransport;
   uint32 mCapabilities;
};


class GuestDnDSrc
   : public sigc::trackable
{
public:
   GuestDnDSrc(GuestDnDMgr *mgr);
   virtual ~GuestDnDSrc(void);

   /* Common DnD layer API exposed to UI (all platforms) for DnD source. */
   void UIDragBeginDone(void);
   void UIUpdateFeedback(DND_DROPEFFECT feedback);
   void OnRpcDragBegin(const CPClipboard *clip);

protected:
   /* Callbacks from rpc for DnD source. */
   void OnRpcUpdateMouse(uint32 sessionId, int32 x, int32 y);
   void OnRpcDrop(uint32 sessionId, int32 x, int32 y);
   virtual void OnRpcCancel(uint32 sessionId);
   void OnRpcGetFilesDone(uint32 sessionId,
                          bool success,
                          const uint8 *stagingDirCP,
                          uint32 sz);
   virtual const std::string& SetupDestDir(const std::string &destDir);
   virtual void CleanStagingFiles(bool fileTransferResult) { }
   virtual bool NeedSetupDestDir(const CPClipboard *clip) { return true; }

   GuestDnDMgr *mMgr;
   DnDRpc *mRpc;
   std::string mStagingDir;
   CPClipboard mClipboard;
};

class GuestDnDDest
   : public sigc::trackable
{
public:
   GuestDnDDest(GuestDnDMgr *mgr);
   ~GuestDnDDest(void);

   /* Common DnD layer API exposed to UI (all platforms) for DnD destination. */
   void UIDragEnter(const CPClipboard *clip);

private:
   /* Callbacks from rpc for DnD destination. */
   void OnRpcPrivDragEnter(uint32 sessionId);
   void OnRpcPrivDragLeave(uint32 sessionId, int32 x, int32 y);
   void OnRpcPrivDrop(uint32 sessionId, int32 x, int32 y);
   void OnRpcDrop(uint32 sessionId, int32 x, int32 y);
   void OnRpcCancel(uint32 sessionId);

   GuestDnDMgr *mMgr;
   CPClipboard mClipboard;
};

#endif // GUEST_DND_HH

  07070100000343000081A40000000000000000000000016822550500000A8B000000000000000000000000000000000000005800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestDnDCPMgr.cc   /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @GuestDnDCPMgr.cc --
 *
 * Implementation of common layer GuestDnDCPMgr object.
 */

#include "guestDnDCPMgr.hh"

// The MACRO DND_VM is only used for WS/FS
#ifdef DND_VM
#include "vmGuestDnDCPMgr.hh"
#else
#include "crtGuestDnDCPMgr.hh"
#endif

extern "C" {
   #include "debug.h"
   #include "guestApp.h"
}

GuestDnDCPMgr *GuestDnDCPMgr::m_instance = NULL;


/**
 *
 * Constructor.
 */

GuestDnDCPMgr::GuestDnDCPMgr()
   : mDnDMgr(NULL),
     mCPMgr(NULL),
     mFileTransfer(NULL),
     mTransport(NULL),
     mLocalCaps(0xffffffff)
{
}


/**
 * Destructor.
 */

GuestDnDCPMgr::~GuestDnDCPMgr(void)
{
   g_debug("%s: enter\n", __FUNCTION__);
}


/**
 * Get an instance of GuestDnDCPMgr, which is an application singleton.
 *
 * @return a pointer to the singleton GuestDnDCPMgr object, or NULL if
 * for some reason it could not be allocated.
 */

GuestDnDCPMgr *
GuestDnDCPMgr::GetInstance(void)
{
   if (!m_instance) {
#ifdef DND_VM
      m_instance = VMGuestDnDCPMgr::CreateInstance();
#else
      m_instance = CRTGuestDnDCPMgr::CreateInstance();
#endif
   }
   return m_instance;
}


/**
 * Destroy the GuestDnDCPMgr singleton.
 */

void
GuestDnDCPMgr::Destroy(void)
{
   if (m_instance) {
      delete m_instance;
      m_instance = NULL;
   }
}


/**
 * API for starting the transport main loop from python.
 */

void
GuestDnDCPMgr::StartLoop()
{
   (void) GetTransport();
   if (mTransport) {
      mTransport->StartLoop();
   }
}


/**
 * API for iterating the transport main loop from python.
 */

void
GuestDnDCPMgr::IterateLoop()
{
   (void) GetTransport();
   if (mTransport) {
      mTransport->IterateLoop();
   }
}


/**
 * API for ending the transport main loop from python.
 */

void
GuestDnDCPMgr::EndLoop()
{
   (void) GetTransport();
   if (mTransport) {
      mTransport->EndLoop();
   }
}
 07070100000344000081A40000000000000000000000016822550500000839000000000000000000000000000000000000005800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestDnDCPMgr.hh   /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestDnDCPMgr.hh --
 *
 * Common layer management object for guest DnD/CP. It is a singleton.
 */

#ifndef GUEST_DND_CP_HH
#define GUEST_DND_CP_HH

#include "guestDnD.hh"
#include "guestCopyPaste.hh"
#include "guestFileTransfer.hh"

struct DblLnkLst_Links;
struct ToolsAppCtx;

class GuestDnDCPMgr
{
public:
   virtual ~GuestDnDCPMgr();
   static GuestDnDCPMgr *GetInstance(void);
   static void Destroy();
   virtual GuestDnDMgr *GetDnDMgr(void) { return NULL; }
   virtual GuestCopyPasteMgr *GetCopyPasteMgr(void) { return NULL; }
   virtual DnDCPTransport *GetTransport(void) { return NULL; }
   void StartLoop();
   void EndLoop();
   void IterateLoop();
   virtual void Init(ToolsAppCtx *ctx) { }
   void SetCaps(uint32 caps) {mLocalCaps = caps;};
   uint32 GetCaps() {return mLocalCaps;};

protected:
   /* We're a singleton, so it is a compile time error to call these. */
   GuestDnDCPMgr(void);
   GuestDnDCPMgr(const GuestDnDCPMgr &mgr);
   GuestDnDCPMgr& operator=(const GuestDnDCPMgr &mgr);

   static GuestDnDCPMgr *m_instance;
   GuestDnDMgr *mDnDMgr;
   GuestCopyPasteMgr *mCPMgr;
   GuestFileTransfer *mFileTransfer;
   DnDCPTransport *mTransport;
   uint32 mLocalCaps;
};

#endif // GUEST_DND_CP_HH

   07070100000345000081A400000000000000000000000168225505000019A9000000000000000000000000000000000000005700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestDnDDest.cc    /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestDnDDest.cc --
 *
 * Implementation of common layer GuestDnDDest object for guest.
 */


#include "guestDnD.hh"
#include "tracer.hh"

extern "C" {
   #include "dndClipboard.h"
   #include "debug.h"
}


/**
 * Constructor.
 *
 * @param[in] mgr guest DnD manager
 */

GuestDnDDest::GuestDnDDest(GuestDnDMgr *mgr)
 : mMgr(mgr)
{
   ASSERT(mMgr);
   mMgr->GetRpc()->destPrivDragEnterChanged.connect(
      sigc::mem_fun(this, &GuestDnDDest::OnRpcPrivDragEnter));
   mMgr->GetRpc()->destPrivDragLeaveChanged.connect(
      sigc::mem_fun(this, &GuestDnDDest::OnRpcPrivDragLeave));
   mMgr->GetRpc()->destPrivDropChanged.connect(
      sigc::mem_fun(this, &GuestDnDDest::OnRpcPrivDrop));
   mMgr->GetRpc()->destDropChanged.connect(
      sigc::mem_fun(this, &GuestDnDDest::OnRpcDrop));
   mMgr->GetRpc()->destCancelChanged.connect(
      sigc::mem_fun(this, &GuestDnDDest::OnRpcCancel));

   CPClipboard_Init(&mClipboard);
}


/**
 * Destructor.
 */

GuestDnDDest::~GuestDnDDest(void)
{
   CPClipboard_Destroy(&mClipboard);
}


/**
 * Guest UI got dragEnter with valid data. Send dragEnter cmd to controller.
 *
 * @param[in] clip cross-platform clipboard data.
 */

void
GuestDnDDest::UIDragEnter(const CPClipboard *clip)
{
   if (!mMgr->IsDragEnterAllowed()) {
      g_debug("%s: not allowed.\n", __FUNCTION__);
      return;
   }

   TRACE_CALL();

   if (GUEST_DND_DEST_DRAGGING == mMgr->GetState() ||
       GUEST_DND_PRIV_DRAGGING == mMgr->GetState()) {
      /*
       * In GH DnD case, if DnD already happened, user may drag back into guest
       * VM and drag into the detection window again, and trigger the
       * DragEnter. In this case, ignore the DragEnter.
       */
      g_debug("%s: already in state %d for GH DnD, ignoring.\n", __FUNCTION__,
              mMgr->GetState());
      return;
   }

   if (GUEST_DND_SRC_DRAGGING == mMgr->GetState()) {
      /*
       * In HG DnD case, if DnD already happened, user may also drag into the
       * detection window again. The DragEnter should also be ignored.
       */
      g_debug("%s: already in SRC_DRAGGING state, ignoring\n", __FUNCTION__);
      return;
   }

   /*
    * In Unity mode, there is no QueryPendingDrag signal, so may get called
    * with state READY.
    */
   if (mMgr->GetState() != GUEST_DND_QUERY_EXITING &&
       mMgr->GetState() != GUEST_DND_READY) {
      g_debug("%s: Bad state: %d, reset\n", __FUNCTION__, mMgr->GetState());
      goto error;
   }

   CPClipboard_Clear(&mClipboard);
   CPClipboard_Copy(&mClipboard, clip);

   if (!mMgr->GetRpc()->DestDragEnter(mMgr->GetSessionId(), clip)) {
      g_debug("%s: DestDragEnter failed\n", __FUNCTION__);
      goto error;
   }

   mMgr->SetState(GUEST_DND_DEST_DRAGGING);
   g_debug("%s: state changed to DEST_DRAGGING\n", __FUNCTION__);
   return;

error:
   mMgr->ResetDnD();
}


/**
 * User drags back to guest during GH DnD. Change state machine to
 * PRIV_DRAGGING state.
 *
 * @param[in] sessionId active session id the controller assigned.
 */

void
GuestDnDDest::OnRpcPrivDragEnter(uint32 sessionId)
{
   TRACE_CALL();

   if (GUEST_DND_DEST_DRAGGING != mMgr->GetState()) {
      g_debug("%s: Bad state: %d, reset\n", __FUNCTION__, mMgr->GetState());
      goto error;
   }

   mMgr->SetState(GUEST_DND_PRIV_DRAGGING);
   g_debug("%s: state changed to PRIV_DRAGGING\n", __FUNCTION__);
   return;

error:
   mMgr->ResetDnD();
}


/**
 * User drags away from guest during GH DnD. Change state machine to
 * SRC_DRAGGING state.
 *
 * @param[in] sessionId active session id the controller assigned.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 */

void
GuestDnDDest::OnRpcPrivDragLeave(uint32 sessionId,
                                 int32 x,
                                 int32 y)
{
   TRACE_CALL();

   if (GUEST_DND_PRIV_DRAGGING != mMgr->GetState()) {
      g_debug("%s: Bad state: %d, reset\n", __FUNCTION__, mMgr->GetState());
      goto error;
   }

   mMgr->SetState(GUEST_DND_DEST_DRAGGING);
   mMgr->destMoveDetWndToMousePosChanged.emit();
   g_debug("%s: state changed to DEST_DRAGGING\n", __FUNCTION__);
   return;

error:
   mMgr->ResetDnD();
}


/**
 * User drops inside guest during GH DnD. Simulate the mouse drop, hide
 * detection window, and reset state machine.
 *
 * @param[in] sessionId active session id the controller assigned.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 */

void
GuestDnDDest::OnRpcPrivDrop(uint32 sessionId,
                            int32 x,
                            int32 y)
{
   mMgr->privDropChanged.emit(x, y);
   mMgr->HideDetWnd();
   mMgr->SetState(GUEST_DND_READY);
   // XXX Trace.
   g_debug("%s: state changed to GUEST_DND_READY, session id changed to 0\n",
           __FUNCTION__);
}


/**
 * User drops outside of guest during GH DnD. Simply cancel the local DnD.
 *
 * @param[in] sessionId active session id the controller assigned.
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 */

void
GuestDnDDest::OnRpcDrop(uint32 sessionId,
                        int32 x,
                        int32 y)
{
   OnRpcCancel(sessionId);
}


/**
 * Cancel current GH DnD.
 *
 * @param[in] sessionId active session id the controller assigned.
 */

void
GuestDnDDest::OnRpcCancel(uint32 sessionId)
{
   /*
    * For Windows, the detection window will hide when the drop
    * event occurs.
    * Please see bug 1750683.
    */
#if !defined(_WIN32)
   mMgr->DelayHideDetWnd();
#endif // _WIN32
   mMgr->RemoveUngrabTimeout();
   mMgr->destCancelChanged.emit();
   mMgr->SetState(GUEST_DND_READY);
   g_debug("%s: state changed to GUEST_DND_READY, session id changed to 0\n",
           __FUNCTION__);
}

   07070100000346000081A4000000000000000000000001682255050000376F000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestDnDMgr.cc /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestDnDMgr.cc --
 *
 * Implementation of common layer GuestDnDMgr object for guest.
 */

#include "tracer.hh"
#include "guestDnD.hh"
#include "guestDnDCPMgr.hh"

extern "C" {
   #include "debug.h"
}


/**
 * Constructor.
 *
 * @param[in] transport for sending/receiving packets.
 * @param[in] eventQueue for event management
 */

GuestDnDMgr::GuestDnDMgr(DnDCPTransport *transport,
                         ToolsAppCtx *ctx)
 : mSrc(NULL),
   mDest(NULL),
   mRpc(NULL),
   mDnDState(GUEST_DND_READY),
   mSessionId(0),
   mHideDetWndTimer(NULL),
   mUnityDnDDetTimeout(NULL),
   mUngrabTimeout(NULL),
   mDnDAllowed(false),
   mDnDTransport(transport),
   mCapabilities(0xffffffff)
{
   ASSERT(transport);
}


/**
 * Destructor.
 */

GuestDnDMgr::~GuestDnDMgr(void)
{
   delete mRpc;
   mRpc = NULL;

   /* Remove untriggered timers. */
   if (mHideDetWndTimer) {
      g_source_destroy(mHideDetWndTimer);
      mHideDetWndTimer = NULL;
   }
   if (mUnityDnDDetTimeout) {
      g_source_destroy(mUnityDnDDetTimeout);
      mUnityDnDDetTimeout = NULL;
   }
   RemoveUngrabTimeout();
}


/**
 * Reset state machine and session id. Delete mSrc and mDest.
 */

void
GuestDnDMgr::ResetDnD(void)
{
   TRACE_CALL();

   if (mSrc) {
      srcCancelChanged.emit();
      DelayHideDetWnd();
      delete mSrc;
      mSrc = NULL;
   }
   if (mDest) {
      DelayHideDetWnd();
      RemoveUngrabTimeout();
      destCancelChanged.emit();
      delete mDest;
      mDest = NULL;
   }

   SetState(GUEST_DND_READY);

   g_debug("%s: change to state %d, session id %d\n", __FUNCTION__, mDnDState,
           mSessionId);
}


/**
 * Guest UI got dragBeginDone. Wrapper for mSrc->UIDragBeginDone.
 */

void
GuestDnDMgr::SrcUIDragBeginDone(void)
{
   TRACE_CALL();

   if (mSrc) {
      mSrc->UIDragBeginDone();
   } else {
      g_debug("%s: mSrc is NULL\n", __FUNCTION__);
   }
}


/**
 * Guest UI got DnD feedback. Wrapper for mSrc->UIUpdateFeedback.
 *
 * @param[in] feedback
 */

void
GuestDnDMgr::SrcUIUpdateFeedback(DND_DROPEFFECT feedback)
{
   TRACE_CALL();

   if (mSrc) {
      mSrc->UIUpdateFeedback(feedback);
   } else {
      g_debug("%s: mSrc is NULL\n", __FUNCTION__);
   }
}


/**
 * Guest UI got dragEnter with valid data. Create mDest if the state machine
 * is ready.
 *
 * @param[in] clip cross-platform clipboard data.
 */

void
GuestDnDMgr::DestUIDragEnter(const CPClipboard *clip)
{
   TRACE_CALL();

   /* Remove untriggered ungrab timer. */
   RemoveUngrabTimeout();

   if (GUEST_DND_SRC_DRAGGING == mDnDState ||
       GUEST_DND_DEST_DRAGGING == mDnDState) {
      /*
       * In GH DnD case, if DnD already happened, user may drag back into guest
       * VM and drag into the detection window again, and trigger the
       * DragEnter. In this case, ignore the DragEnter.
       *
       * In HG DnD case, if DnD already happened, user may also drag into the
       * detection window again. The DragEnter should also be ignored.
       */
      return;
   }

   /*
    * In Unity mode, there is no QueryPendingDrag signal, so may get called
    * with state READY.
    */
   if (mDnDState != GUEST_DND_QUERY_EXITING &&
       mDnDState != GUEST_DND_READY) {
      g_debug("%s: Bad state: %d, reset\n", __FUNCTION__, mDnDState);
      ResetDnD();
      return;
   }

   /* Remove untriggered ungrab timer. */
   if (mUngrabTimeout) {
      g_source_destroy(mUngrabTimeout);
      mUngrabTimeout = NULL;
   }

   if (mDest) {
      g_debug("%s: mDest is not NULL\n", __FUNCTION__);
      delete mDest;
      mDest = NULL;
   }

   ASSERT(clip);
   mDest = new GuestDnDDest(this);
   mDest->UIDragEnter(clip);
}


/**
 * Got queryExiting from rpc. Show the detection window on (x, y) to try to
 * detect any pending GH DnD.
 *
 * @param[in] sessionId active DnD session id
 * @param[in] x detection window position x.
 * @param[in] y detection window position y.
 */

void
GuestDnDMgr::OnRpcQueryExiting(uint32 sessionId,
                               int32 x,
                               int32 y)
{
   TRACE_CALL();

   if (!mDnDAllowed) {
      g_debug("%s: DnD is not allowed.\n", __FUNCTION__);
      return;
   }

   if (GUEST_DND_READY != mDnDState) {
      /* Reset DnD for any wrong state. */
      g_debug("%s: Bad state: %d\n", __FUNCTION__, mDnDState);
      ResetDnD();
      return;
   }

   /* Show detection window to detect pending GH DnD. */
   ShowDetWnd(x, y);
   SetSessionId(sessionId);
   SetState(GUEST_DND_QUERY_EXITING);

   /*
    * Add event to fire and hide our window if a DnD is not pending.  Note that
    * this is here in case the drag isn't picked up by our drag detection window
    * for some reason.
    */
   AddDnDUngrabTimeoutEvent();
}


/**
 * Callback for DnDUngrab timeout. This will be called if there is no pending
 * GH DnD when user dragged leaving the guest. Send dragNotPending command to
 * controller and reset local state machine.
 */

void
GuestDnDMgr::UngrabTimeout(void)
{
   TRACE_CALL();

   mUngrabTimeout = NULL;

   if (mDnDState != GUEST_DND_QUERY_EXITING) {
      /* Reset DnD for any wrong state. */
      g_debug("%s: Bad state: %d\n", __FUNCTION__, mDnDState);
      ResetDnD();
      return;
   }

   ASSERT(mRpc);
   mRpc->DragNotPending(mSessionId);

   HideDetWnd();
   SetState(GUEST_DND_READY);
}


/**
 * This callback is trigged when a user clicks into any Unity window or just
 * releases the mouse button. Either show the full-screen detection window
 * right after the Unity window, or hide the detection window.
 *
 * @param[in] sessionId  Active session id the controller assigned earlier.
 * @param[in] show       Show or hide unity DnD detection window.
 * @param[in] unityWndId The unity window id.
 */

void
GuestDnDMgr::OnRpcUpdateUnityDetWnd(uint32 sessionId,
                                    bool show,
                                    uint32 unityWndId)
{
   TRACE_CALL();

   if (show && mDnDState != GUEST_DND_READY) {
      /*
       * Reset DnD for any wrong state. Only do this when host asked to
       * show the window.
       */
      g_debug("%s: Bad state: %d\n", __FUNCTION__, mDnDState);
      ResetDnD();
      return;
   }

   if (mUnityDnDDetTimeout) {
      g_source_destroy(mUnityDnDDetTimeout);
      mUnityDnDDetTimeout = NULL;
   }

   if (show) {
      /*
       * When showing full screen window, also show the small top-most
       * window at (1, 1). After detected a GH DnD, the full screen
       * window will be hidden to avoid blocking other windows. So use
       * this window to accept drop in cancel case.
       */
      UpdateDetWnd(show, 1, 1);
      AddUnityDnDDetTimeoutEvent();
      SetSessionId(sessionId);
   } else {
      /*
       * If there is active DnD, the regular detection window will be hidden
       * after DnD is done.
       */
      if (mDnDState == GUEST_DND_READY) {
         UpdateDetWnd(false, 0, 0);
      }
   }

   /* Show/hide the full screen detection window. */
   updateUnityDetWndChanged.emit(show, unityWndId, false);
   g_debug("%s: updating Unity detection window, show %d, id %u\n",
           __FUNCTION__, show, unityWndId);
}


/**
 * Can not detect pending GH DnD within UNITY_DND_DET_TIMEOUT, put the full
 * screen detection window to bottom most.
 */

void
GuestDnDMgr::UnityDnDDetTimeout(void)
{
   TRACE_CALL();

   mUnityDnDDetTimeout = NULL;
   updateUnityDetWndChanged.emit(true, 0, true);
}


/**
 * Got moveMouse from rpc. Ask UI to update mouse position.
 *
 * @param[in] sessionId active DnD session id
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 * @param[in] isButtonDown
 */

void
GuestDnDMgr::OnRpcMoveMouse(uint32 sessionId,
                            int32 x,
                            int32 y)
{
   TRACE_CALL();

   if (GUEST_DND_SRC_DRAGGING != mDnDState &&
       GUEST_DND_PRIV_DRAGGING != mDnDState) {
      g_debug("%s: not in valid state %d, ignoring\n", __FUNCTION__, mDnDState);
      return;
   }
   g_debug("%s: move to %d, %d\n", __FUNCTION__, x, y);
   moveMouseChanged.emit(x, y);
}


/**
 * Update the detection window.
 *
 * @param[in] show show/hide the detection window
 * @param[in] x detection window position x.
 * @param[in] y detection window position y.
 */

void
GuestDnDMgr::UpdateDetWnd(bool show,
                          int32 x,
                          int32 y)
{
   TRACE_CALL();

   if (mHideDetWndTimer) {
      g_source_destroy(mHideDetWndTimer);
      mHideDetWndTimer = NULL;
   }

   g_debug("%s: %s window at %d, %d\n", __FUNCTION__, show ? "show" : "hide",
           x, y);
   updateDetWndChanged.emit(show, x, y);
}


/**
 * Update the detection window.
 *
 * @param[in] show show/hide the detection window
 * @param[in] x detection window position x.
 * @param[in] y detection window position y.
 */

void
GuestDnDMgr::DelayHideDetWnd(void)
{
   TRACE_CALL();

   AddHideDetWndTimerEvent();
}


/**
 * Remove any pending mUngrabTimeout.
 */

void
GuestDnDMgr::RemoveUngrabTimeout(void)
{
   TRACE_CALL();

   if (mUngrabTimeout) {
      g_source_destroy(mUngrabTimeout);
      mUngrabTimeout = NULL;
   }
}


/**
 * Set state machine state.
 *
 * @param[in] state
 */

void
GuestDnDMgr::SetState(GUEST_DND_STATE state)
{
#ifdef VMX86_DEVEL
   static const char* states[] = {
      "GUEST_DND_INVALID",
      "GUEST_DND_READY",
      /* As destination. */
      "GUEST_DND_QUERY_EXITING",
      "GUEST_DND_DEST_DRAGGING",
      /* In private dragging mode. */
      "GUEST_DND_PRIV_DRAGGING",
      /* As source. */
      "GUEST_DND_SRC_DRAGBEGIN_PENDING",
      "GUEST_DND_SRC_CANCEL_PENDING",
      "GUEST_DND_SRC_DRAGGING",
   };
   g_debug("%s: %s => %s\n", __FUNCTION__, states[mDnDState], states[state]);
#endif

   mDnDState = state;
   stateChanged.emit(state);
   if (GUEST_DND_READY == state) {
      /* Reset sessionId if the state is reset. */
      SetSessionId(0);
   }
}


/**
 * Check if DragEnter is allowed.
 *
 * @return true if DragEnter is allowed, false otherwise.
 */

bool
GuestDnDMgr::IsDragEnterAllowed(void)
{
   /*
    * Right after any DnD is finished, there may be some unexpected
    * DragEnter from UI, and may disturb our state machine. The
    * mHideDetWndTimer will only be valid for 0.5 second after each
    * DnD, and during this time UI DragEnter is not allowed.
    */
   return mHideDetWndTimer == NULL;
}


/**
 * Handle version change in VMX.
 *
 * @param[in] version negotiated DnD version.
 */

void
GuestDnDMgr::VmxDnDVersionChanged(uint32 version)
{
   TRACE_CALL();

   g_debug("GuestDnDMgr::%s: enter version %d\n", __FUNCTION__, version);
   ASSERT(version >= 3);

   /* Remove untriggered timers. */
   if (mHideDetWndTimer) {
      g_source_destroy(mHideDetWndTimer);
      mHideDetWndTimer = NULL;
   }
   if (mRpc) {
      delete mRpc;
      mRpc = NULL;
   }

   CreateDnDRpcWithVersion(version);

   if (mRpc) {
      mRpc->pingReplyChanged.connect(
         sigc::mem_fun(this, &GuestDnDMgr::OnPingReply));
      mRpc->srcDragBeginChanged.connect(
         sigc::mem_fun(this, &GuestDnDMgr::OnRpcSrcDragBegin));
      mRpc->queryExitingChanged.connect(
         sigc::mem_fun(this, &GuestDnDMgr::OnRpcQueryExiting));
      mRpc->updateUnityDetWndChanged.connect(
         sigc::mem_fun(this, &GuestDnDMgr::OnRpcUpdateUnityDetWnd));
      mRpc->moveMouseChanged.connect(
         sigc::mem_fun(this, &GuestDnDMgr::OnRpcMoveMouse));
      mRpc->Init();
      mRpc->SendPing(GuestDnDCPMgr::GetInstance()->GetCaps() &
                     (DND_CP_CAP_DND | DND_CP_CAP_FORMATS_DND |
                      DND_CP_CAP_VALID));
   }

   ResetDnD();
}


/**
 * Check if a request is allowed based on resolved capabilities.
 *
 * @param[in] capsRequest requested capabilities.
 *
 * @return TRUE if allowed, FALSE otherwise.
 */

Bool
GuestDnDMgr::CheckCapability(uint32 capsRequest)
{
   Bool allowed = FALSE;

   if ((mCapabilities & capsRequest) == capsRequest) {
      allowed = TRUE;
   }
   return allowed;
}


/**
 * Got pingReplyChanged message. Update capabilities.
 *
 * @param[in] capability modified capabilities from VMX controller.
 */

void
GuestDnDMgr::OnPingReply(uint32 capabilities)
{
   TRACE_CALL();

   g_debug("%s: dnd ping reply caps are %x\n", __FUNCTION__, capabilities);
   mCapabilities = capabilities;
}


/**
 * Callback for DnDUngrab timeout. This will be called if there is no pending
 * GH DnD when user dragged leaving the guest.
 *
 * @param[in] clientData
 *
 * @return FALSE always
 */

gboolean
GuestDnDMgr::DnDUngrabTimeout(void *clientData)
{
   TRACE_CALL();

   ASSERT(clientData);
   GuestDnDMgr *dnd = (GuestDnDMgr *)clientData;
   /* Call actual callback. */
   dnd->UngrabTimeout();
   return FALSE;
}


/**
 * Callback for HideDetWndTimer.
 *
 * @param[in] clientData
 *
 * @return FALSE always
 */

gboolean
GuestDnDMgr::DnDHideDetWndTimer(void *clientData)
{
   TRACE_CALL();

   ASSERT(clientData);
   GuestDnDMgr *dnd = (GuestDnDMgr *)clientData;
   dnd->SetHideDetWndTimer(NULL);
   dnd->HideDetWnd();
   return FALSE;
}


/**
 * Callback for UnityDnDDetTimeout.
 *
 * @param[in] clientData
 *
 * @return FALSE always
 */

gboolean
GuestDnDMgr::DnDUnityDetTimeout(void *clientData)
{
   TRACE_CALL();

   ASSERT(clientData);
   GuestDnDMgr *dnd = (GuestDnDMgr *)clientData;
   dnd->UnityDnDDetTimeout();
   return FALSE;
}

 07070100000347000081A40000000000000000000000016822550500001C4C000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestDnDSrc.cc /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestDnDSrc.cc --
 *
 * Implementation of common layer GuestDnDSrc object for guest.
 */

#include "guestDnD.hh"
#include "util.h"

extern "C" {
   #include "dndClipboard.h"
   #include "debug.h"
   #include "cpNameUtil.h"
}

#include "file.h"
#include "str.h"


/**
 * Constructor.
 *
 * @param[in] mgr guest DnD manager
 */

GuestDnDSrc::GuestDnDSrc(GuestDnDMgr *mgr)
 : mMgr(mgr),
   mRpc(NULL)
{
   ASSERT(mMgr);
   mMgr->GetRpc()->srcDropChanged.connect(
      sigc::mem_fun(this, &GuestDnDSrc::OnRpcDrop));
   mMgr->GetRpc()->srcCancelChanged.connect(
      sigc::mem_fun(this, &GuestDnDSrc::OnRpcCancel));
   mMgr->GetRpc()->getFilesDoneChanged.connect(
      sigc::mem_fun(this, &GuestDnDSrc::OnRpcGetFilesDone));

   CPClipboard_Init(&mClipboard);
}


/**
 * Destructor.
 */

GuestDnDSrc::~GuestDnDSrc(void)
{
   ASSERT(mMgr);
   CPClipboard_Destroy(&mClipboard);
   /* Reset current session id after finished. */
   mMgr->SetSessionId(0);
}


/**
 * Rpc got dragBegin with valid data. Ask UI to show the detection window and
 * start host->guest DnD inside guest.
 *
 * @param[in] clip cross-platform clipboard data.
 */

void
GuestDnDSrc::OnRpcDragBegin(const CPClipboard *clip)
{
   ASSERT(mMgr);
   ASSERT(clip);

   g_debug("%s: state is %d\n", __FUNCTION__, mMgr->GetState());

   if (NeedSetupDestDir(clip)) {
      /* Setup staging directory. */
      mStagingDir = SetupDestDir("");
      if (mStagingDir.empty()) {
         g_debug("%s: SetupDestDir failed.\n", __FUNCTION__);
         return;
      }
   }

   /* Show detection window in (0, 0). */
   mMgr->ShowDetWnd(0, 0);

   CPClipboard_Clear(&mClipboard);
   CPClipboard_Copy(&mClipboard, clip);

   mMgr->SetState(GUEST_DND_SRC_DRAGBEGIN_PENDING);
   g_debug("%s: state changed to DRAGBEGIN_PENDING\n", __FUNCTION__);

   mMgr->srcDragBeginChanged.emit(&mClipboard, mStagingDir);
}


/**
 * Guest UI got dragBeginDone. Send dragBeginDone cmd to controller.
 */

void
GuestDnDSrc::UIDragBeginDone(void)
{
   ASSERT(mMgr);

   g_debug("%s: state is %d\n", __FUNCTION__, mMgr->GetState());
   if (mMgr->GetState() != GUEST_DND_SRC_DRAGBEGIN_PENDING) {
      /* Reset DnD for any wrong state. */
      g_debug("%s: Bad state: %d\n", __FUNCTION__, mMgr->GetState());
      goto error;
   }

   if (!mMgr->GetRpc()->SrcDragBeginDone(mMgr->GetSessionId())) {
      g_debug("%s: SrcDragBeginDone failed\n", __FUNCTION__);
      goto error;
   }

   mMgr->SetState(GUEST_DND_SRC_DRAGGING);
   g_debug("%s: state changed to DRAGGING\n", __FUNCTION__);
   return;

error:
   mMgr->ResetDnD();
}


/**
 * Guest UI got DnD feedback. Send updateFeedback cmd to controller.
 *
 * @param[in] feedback
 */

void
GuestDnDSrc::UIUpdateFeedback(DND_DROPEFFECT feedback)
{
   ASSERT(mMgr);

   g_debug("%s: state is %d\n", __FUNCTION__, mMgr->GetState());

   /* This operation needs a valid session id from controller. */
   if (0 == mMgr->GetSessionId()) {
      g_debug("%s: can not get a valid session id from controller.\n",
              __FUNCTION__);
      return;
   }
   if (!mMgr->GetRpc()->UpdateFeedback(mMgr->GetSessionId(), feedback)) {
      g_debug("%s: UpdateFeedback failed\n", __FUNCTION__);
      mMgr->ResetDnD();
   }
}


/**
 * Got drop cmd from rpc. Ask UI to simulate the drop at (x, y).
 *
 * @param[in] sessionId active DnD session id
 * @param[in] x mouse position x.
 * @param[in] y mouse position y.
 */

void
GuestDnDSrc::OnRpcDrop(uint32 sessionId,
                       int32 x,
                       int32 y)
{
   char cpName[FILE_MAXPATH];
   int32 cpNameSize;

   ASSERT(mMgr);

   g_debug("%s: state is %d\n", __FUNCTION__, mMgr->GetState());
   if (mMgr->GetState() != GUEST_DND_SRC_DRAGGING) {
      /* Reset DnD for any wrong state. */
      g_debug("%s: Bad state: %d\n", __FUNCTION__, mMgr->GetState());
      goto error;
   }
   mMgr->srcDropChanged.emit();

   if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_FILELIST)) {
      /* Convert staging name to CP format. */
      cpNameSize = CPNameUtil_ConvertToRoot(mStagingDir.c_str(),
                                            sizeof cpName,
                                            cpName);
      if (cpNameSize < 0) {
         g_debug("%s: Error, could not convert to CPName.\n", __FUNCTION__);
         goto error;
      }

      if (!mMgr->GetRpc()->SrcDropDone(sessionId,
                                       (const uint8 *)cpName,
                                       cpNameSize)) {
         g_debug("%s: SrcDropDone failed\n", __FUNCTION__);
         goto error;
      }
   } else {
      /* For non-file formats, the DnD is done. Hide detection window. */
      mMgr->HideDetWnd();
      mMgr->SetState(GUEST_DND_READY);
      g_debug("%s: state changed to READY\n", __FUNCTION__);
   }
   return;

error:
   mMgr->ResetDnD();
}


/**
 * Got cancel cmd from rpc. Ask UI to cancel the DnD as source.
 *
 * @param[in] sessionId active DnD session id
 */

void
GuestDnDSrc::OnRpcCancel(uint32 sessionId)
{
   ASSERT(mMgr);

   g_debug("%s: state is %d\n", __FUNCTION__, mMgr->GetState());
   mMgr->srcCancelChanged.emit();
   mMgr->DelayHideDetWnd();
   mMgr->SetState(GUEST_DND_READY);
   g_debug("%s: state changed to READY\n", __FUNCTION__);
}


/**
 * Got getFileDone cmd from rpc. Reset state machine and hide detection window.
 *
 * @param[in] sessionId active DnD session id
 * @param[in] success if the file transfer is successful or not
 * @param[in] stagingCP staging dir name in cross-platform format
 * @param[in] sz the staging dir name size
 */

void
GuestDnDSrc::OnRpcGetFilesDone(uint32 sessionId,
                               bool success,
                               const uint8 *stagingDirCP,
                               uint32 sz)
{
   CleanStagingFiles(success);

   /* UI should remove block with this signal. */
   mMgr->getFilesDoneChanged.emit(success);
   mMgr->HideDetWnd();
   mMgr->SetState(GUEST_DND_READY);
   g_debug("%s: state changed to READY\n", __FUNCTION__);
}


/**
 * Creates a directory for file transfer. If the destination dir is provided,
 * we will attempt to copy files to that directory.
 *
 * @param[in] destDir the preferred destination directory
 *
 * @return the destination directory on success, an empty string on failure.
 */

const std::string &
GuestDnDSrc::SetupDestDir(const std::string &destDir)
{
   return mStagingDir;
}
07070100000348000081A40000000000000000000000016822550500000563000000000000000000000000000000000000005C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestFileTransfer.cc   /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestFileTransfer.cc --
 *
 * Implementation of common layer file transfer object for guest.
 */

#include "guestFileTransfer.hh"

extern "C" {
   #include "debug.h"
}

#include "hgfsServer.h"


/**
 * Create transport object and register callback.
 *
 * @param[in] transport pointer to a transport object.
 */

GuestFileTransfer::GuestFileTransfer(DnDCPTransport *transport)
   : mRpc(NULL)
{
   ASSERT(transport);
}


/**
 * Destructor.
 */
GuestFileTransfer::~GuestFileTransfer(void)
{
}
 07070100000349000081A40000000000000000000000016822550500000532000000000000000000000000000000000000005C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndGuestBase/guestFileTransfer.hh   /*********************************************************
 * Copyright (C) 2010-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @guestFileTransfer.hh --
 *
 * File transfer object for guest.
 */

#ifndef GUEST_FILE_TRANSFER_HH
#define GUEST_FILE_TRANSFER_HH

#include <sigc++/trackable.h>
#include "fileTransferRpc.hh"
#include "dndCPTransport.h"

class GuestFileTransfer
   : public sigc::trackable
{
public:
   GuestFileTransfer(DnDCPTransport *transport);
   virtual ~GuestFileTransfer(void);

protected:
   FileTransferRpc *mRpc;
};

#endif // GUEST_FILE_TRANSFER_HH
  0707010000034A000081A40000000000000000000000016822550500000505000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndPluginIntX11.h   /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dndPluginX11Int.h --
 *
 *     Common defines used by Linux DnD plugin implementation.
 */
#ifndef __DNDPLUGIN_INTX11_H__
#define __DNDPLUGIN_INTX11_H__

#include <X11/Xlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#undef Bool
#include "vm_basic_types.h"
#include "dnd.h"

#define UNGRABBED_POS (-100)

extern Display *gXDisplay;
extern Window gXRoot;
extern GtkWidget *gUserMainWidget;

#endif
   0707010000034B000081A400000000000000000000000168225505000138D8000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndUIX11.cpp    /*********************************************************
 * Copyright (c) 2009-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file dndUIX11.cpp --
 *
 * This class implements stubs for the methods that allow DnD between
 * host and guest.
 */

#define G_LOG_DOMAIN "dndcp"

#include "xutils/xutils.hh"

#include "dndUIX11.h"
#include "guestDnDCPMgr.hh"
#include "tracer.hh"

extern "C" {
#include <X11/extensions/XTest.h>       /* for XTest*() */
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>

#include "vmware/guestrpc/tclodefs.h"

#include "copyPasteCompat.h"
#include "cpName.h"
#include "cpNameUtil.h"
#include "dndClipboard.h"
#include "hgfsUri.h"
#include "rpcout.h"
}

#ifdef USE_UINPUT
#include "fakeMouseWayland/fakeMouseWayland.h"
#endif
#include "dnd.h"
#include "dndMsg.h"
#include "hostinfo.h"
#include "file.h"
#include "vmblock.h"

/* IsXExtensionPointer may be not defined with old Xorg. */
#ifndef IsXExtensionPointer
#define IsXExtensionPointer 4
#endif

#include "copyPasteDnDWrapper.h"


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::DnDUIX11 --
 *
 *      Constructor.
 *
 *-----------------------------------------------------------------------------
 */

DnDUIX11::DnDUIX11(ToolsAppCtx *ctx)
    : mCtx(ctx),
      mDnD(NULL),
      mDetWnd(NULL),
      mClipboard(),
      mBlockCtrl(NULL),
      mHGGetFileStatus(DND_FILE_TRANSFER_NOT_STARTED),
      mBlockAdded(false),
      mGHDnDInProgress(false),
      mGHDnDDataReceived(false),
      mGHDnDDropOccurred(false),
      mUnityMode(false),
      mInHGDrag(false),
      mEffect(DROP_NONE),
      mMousePosX(0),
      mMousePosY(0),
      mDragCtx(NULL),
      mNumPendingRequest(0),
      mDestDropTime(0),
      mTotalFileSize(0),
      mOrigin(0, 0),
      mUseUInput(false),
      mScreenWidth(0),
      mScreenHeight(0)
{
   TRACE_CALL();

   xutils::Init();
   xutils::workAreaChanged.connect(sigc::mem_fun(this, &DnDUIX11::OnWorkAreaChanged));

   /*
    * XXX Hard coded use of default screen means this doesn't work in dual-
    * headed setups (e.g. DISPLAY=:0.1).  However, the number of people running
    * such setups in VMs is expected to be, like, hella small, so I'mma cut
    * corners for now.
    */
   OnWorkAreaChanged(Gdk::Screen::get_default());

#ifdef USE_UINPUT
   //Initialize the uinput device if available
   if (ctx->uinputFD != -1) {
      Screen * scrn = DefaultScreenOfDisplay(XOpenDisplay(NULL));
      if (FakeMouse_Init(ctx->uinputFD, scrn->width, scrn->height)) {
         mUseUInput = true;
         mScreenWidth = scrn->width;
         mScreenHeight = scrn->height;
      }
   }
#endif

   g_debug("%s: Use UInput? %d.\n", __FUNCTION__, mUseUInput);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::~DnDUIX11 --
 *
 *      Destructor.
 *
 *-----------------------------------------------------------------------------
 */

DnDUIX11::~DnDUIX11()
{
   TRACE_CALL();

   if (mDetWnd) {
      delete mDetWnd;
   }
   CPClipboard_Destroy(&mClipboard);
   /* Any files from last unfinished file transfer should be deleted. */
   if (   DND_FILE_TRANSFER_IN_PROGRESS == mHGGetFileStatus
       && !mHGStagingDir.empty()) {
      uint64 totalSize = File_GetSizeEx(mHGStagingDir.c_str());
      if (mTotalFileSize != totalSize) {
         g_debug("%s: deleting %s, expecting %" FMT64 "u, finished %" FMT64 "u\n",
                 __FUNCTION__, mHGStagingDir.c_str(),
                 mTotalFileSize, totalSize);
         DnD_DeleteStagingFiles(mHGStagingDir.c_str(), FALSE);
      } else {
         g_debug("%s: file size match %s\n",
                 __FUNCTION__, mHGStagingDir.c_str());
      }
   }
   ResetUI();
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::Init --
 *
 *      Initialize DnDUIX11 object.
 *
 * Results:
 *      Returns true on success and false on failure.
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::Init()
{
   TRACE_CALL();
   bool ret = true;

   CPClipboard_Init(&mClipboard);

   GuestDnDCPMgr *p = GuestDnDCPMgr::GetInstance();
   ASSERT(p);
   mDnD = p->GetDnDMgr();
   ASSERT(mDnD);

   mDetWnd = new DragDetWnd();
   if (!mDetWnd) {
      g_debug("%s: unable to allocate DragDetWnd object\n", __FUNCTION__);
      goto fail;
   }

#if defined(DETWNDDEBUG)
   /*
    * This code can only be called when DragDetWnd is derived from
    * Gtk::Window. The normal case is that DragDetWnd is an instance of
    * Gtk::Invisible, which doesn't implement the methods that SetAttributes
    * relies upon.
    */
   mDetWnd->SetAttributes();
#endif

   InitGtk();

#define CONNECT_SIGNAL(_obj, _sig, _cb) \
   _obj->_sig.connect(sigc::mem_fun(this, &DnDUIX11::_cb))

   /* Set common layer callbacks. */
   CONNECT_SIGNAL(mDnD, srcDragBeginChanged,   OnSrcDragBegin);
   CONNECT_SIGNAL(mDnD, srcDropChanged,        OnSrcDrop);
   CONNECT_SIGNAL(mDnD, srcCancelChanged,      OnSrcCancel);
   CONNECT_SIGNAL(mDnD, destCancelChanged,     OnDestCancel);
   CONNECT_SIGNAL(mDnD, destMoveDetWndToMousePosChanged, OnDestMoveDetWndToMousePos);
   CONNECT_SIGNAL(mDnD, getFilesDoneChanged,   OnGetFilesDone);
   CONNECT_SIGNAL(mDnD, moveMouseChanged,      OnMoveMouse);
   CONNECT_SIGNAL(mDnD, privDropChanged,       OnPrivateDrop);
   CONNECT_SIGNAL(mDnD, updateDetWndChanged,   OnUpdateDetWnd);
   CONNECT_SIGNAL(mDnD, updateUnityDetWndChanged, OnUpdateUnityDetWnd);

   /* Set Gtk+ callbacks for source. */
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_drag_begin(),        OnGtkDragBegin);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_drag_data_get(),     OnGtkDragDataGet);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_drag_end(),          OnGtkDragEnd);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_enter_notify_event(), GtkEnterEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_leave_notify_event(), GtkLeaveEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_map_event(),         GtkMapEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_unmap_event(),       GtkUnmapEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_realize(),           GtkRealizeEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_unrealize(),         GtkUnrealizeEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_motion_notify_event(), GtkMotionNotifyEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_configure_event(),   GtkConfigureEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_button_press_event(), GtkButtonPressEventCB);
   CONNECT_SIGNAL(mDetWnd->GetWnd(), signal_button_release_event(), GtkButtonReleaseEventCB);

#undef CONNECT_SIGNAL

   OnUpdateDetWnd(false, 0, 0);
   OnUpdateUnityDetWnd(false, 0, false);
   goto out;
fail:
   ret = false;
   if (mDnD) {
      delete mDnD;
      mDnD = NULL;
   }
   if (mDetWnd) {
      delete mDetWnd;
      mDetWnd = NULL;
   }
out:
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::InitGtk --
 *
 *      Register supported DND target types and signal handlers with GTK+.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::InitGtk()
{
   TRACE_CALL();

   /* Construct supported target list for HG DnD. */
   std::vector<Gtk::TargetEntry> targets;

   /* File DnD. */
   targets.push_back(Gtk::TargetEntry(DRAG_TARGET_NAME_URI_LIST));

   /* RTF text DnD. */
   targets.push_back(Gtk::TargetEntry(TARGET_NAME_APPLICATION_RTF));
   targets.push_back(Gtk::TargetEntry(TARGET_NAME_TEXT_RICHTEXT));
   targets.push_back(Gtk::TargetEntry(TARGET_NAME_TEXT_RTF));

   /* Plain text DnD. */
   targets.push_back(Gtk::TargetEntry(TARGET_NAME_UTF8_STRING));
   targets.push_back(Gtk::TargetEntry(TARGET_NAME_STRING));
   targets.push_back(Gtk::TargetEntry(TARGET_NAME_TEXT_PLAIN));
   targets.push_back(Gtk::TargetEntry(TARGET_NAME_COMPOUND_TEXT));

   /*
    * We don't want Gtk handling any signals for us, we want to
    * do it ourselves based on the results from the guest.
    *
    * Second argument in drag_dest_set defines the automatic behaviour options
    * of the destination widget. We used to not define it (0) and in some
    * distributions (like Ubuntu 6.10) DragMotion only get called once,
    * and not send updated mouse position to guest, and also got cancel
    * signal when user drop the file (bug 175754). With flag DEST_DEFAULT_MOTION
    * the bug is fixed. Almost all other example codes use DEST_DEFAULT_ALL
    * but in our case, we will call drag_get_data during DragMotion, and
    * will cause X dead with DEST_DEFAULT_ALL. The reason is unclear.
    */
   mDetWnd->GetWnd()->drag_dest_set(targets, Gtk::DEST_DEFAULT_MOTION,
                                    Gdk::ACTION_COPY | Gdk::ACTION_MOVE);

   mDetWnd->GetWnd()->signal_drag_leave().connect(
      sigc::mem_fun(this, &DnDUIX11::OnGtkDragLeave));
   mDetWnd->GetWnd()->signal_drag_motion().connect(
      sigc::mem_fun(this, &DnDUIX11::OnGtkDragMotion));
   mDetWnd->GetWnd()->signal_drag_drop().connect(
      sigc::mem_fun(this, &DnDUIX11::OnGtkDragDrop));
   mDetWnd->GetWnd()->signal_drag_data_received().connect(
      sigc::mem_fun(this, &DnDUIX11::OnGtkDragDataReceived));
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::ResetUI --
 *
 *      Reset UI state variables.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      May remove a vmblock blocking entry.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::ResetUI()
{
   TRACE_CALL();
   mGHDnDDataReceived = false;
   mHGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
   mGHDnDInProgress = false;
   mEffect = DROP_NONE;
   mInHGDrag = false;
   mDragCtx = NULL;
   RemoveBlock();
}


/* Source functions for HG DnD. */


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnSrcDragBegin --
 *
 *      Called when host successfully detected a pending HG drag.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Calls mDetWnd->drag_begin().
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnSrcDragBegin(const CPClipboard *clip,       // IN
                         const std::string stagingDir)  // IN
{
   Glib::RefPtr<Gtk::TargetList> targets;
   Gdk::DragAction actions;
   GdkEventMotion event;
   int mouseX = mOrigin.get_x() + DRAG_DET_WINDOW_WIDTH / 2;
   int mouseY = mOrigin.get_y() + DRAG_DET_WINDOW_WIDTH / 2;

   TRACE_CALL();

   CPClipboard_Clear(&mClipboard);
   CPClipboard_Copy(&mClipboard, clip);

#ifdef USE_UINPUT
   if (mUseUInput) {
      /*
       * Check if the screen size changes, if so then update the
       * uinput device.
       */
      Screen * scrn = DefaultScreenOfDisplay(XOpenDisplay(NULL));
      if (   (scrn->width != mScreenWidth)
          || (scrn->height != mScreenHeight)) {
         g_debug("%s: Update uinput device. prew:%d, preh:%d, w:%d, h:%d\n",
                 __FUNCTION__,
                 mScreenWidth,
                 mScreenHeight,
                 scrn->width,
                 scrn->height);
         mScreenWidth = scrn->width;
         mScreenHeight = scrn->height;
         FakeMouse_Update(mScreenWidth, mScreenHeight);
      }
   }
#endif

   /*
    * Before the DnD, we should make sure that the mouse is released
    * otherwise it may be another DnD, not ours. Send a release, then
    * a press here to cover this case.
    */

   SendFakeXEvents(true, true, false, true, true, mouseX, mouseY);
   SendFakeXEvents(false, true, true, false, true, mouseX, mouseY);

   /*
    * Construct the target and action list, as well as a fake motion notify
    * event that's consistent with one that would typically start a drag.
    */
   targets = Gtk::TargetList::create(std::vector<Gtk::TargetEntry>());

   if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_FILELIST)) {
      mHGStagingDir = stagingDir;
      if (!mHGStagingDir.empty()) {
         targets->add(Glib::ustring(DRAG_TARGET_NAME_URI_LIST));
         /* Add private data to tag dnd as originating from this vm. */
         char *pid;
         g_debug("%s: adding re-entrant drop target, pid %d\n", __FUNCTION__, (int)getpid());
         pid = Str_Asprintf(NULL, "guest-dnd-target %d", static_cast<int>(getpid()));
         if (pid) {
            targets->add(Glib::ustring(pid));
            free(pid);
         }
      }
   }

   if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_FILECONTENTS)) {
      if (WriteFileContentsToStagingDir()) {
         targets->add(Glib::ustring(DRAG_TARGET_NAME_URI_LIST));
      }
   }

   if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_TEXT)) {
      targets->add(Glib::ustring(TARGET_NAME_STRING));
      targets->add(Glib::ustring(TARGET_NAME_TEXT_PLAIN));
      targets->add(Glib::ustring(TARGET_NAME_UTF8_STRING));
      targets->add(Glib::ustring(TARGET_NAME_COMPOUND_TEXT));
   }

   if (CPClipboard_ItemExists(&mClipboard, CPFORMAT_RTF)) {
      targets->add(Glib::ustring(TARGET_NAME_APPLICATION_RTF));
      targets->add(Glib::ustring(TARGET_NAME_TEXT_RICHTEXT));
      targets->add(Glib::ustring(TARGET_NAME_TEXT_RTF));
   }

   actions = Gdk::ACTION_COPY | Gdk::ACTION_MOVE;

   /* TODO set the x/y coords to the actual drag initialization point. */
   event.type = GDK_MOTION_NOTIFY;
   event.window = mDetWnd->GetWnd()->get_window()->gobj();
   event.send_event = false;
   event.time = GDK_CURRENT_TIME;
   event.x = 10;
   event.y = 10;
   event.axes = NULL;
   event.state = GDK_BUTTON1_MASK;
   event.is_hint = 0;
#ifndef GTK3
   event.device = gdk_device_get_core_pointer();
#else
#   if GTK_MINOR_VERSION >= 20
   GdkSeat *seat =
      gdk_display_get_default_seat(gdk_window_get_display(event.window));
   event.device = gdk_seat_get_pointer(seat);
#   else
   GdkDeviceManager *manager =
      gdk_display_get_device_manager(gdk_window_get_display(event.window));
   event.device = gdk_device_manager_get_client_pointer(manager);
#   endif
#endif
   event.x_root = mOrigin.get_x();
   event.y_root = mOrigin.get_y();

   /* Tell Gtk that a drag should be started from this widget. */
   mDetWnd->GetWnd()->drag_begin(targets, actions, 1, (GdkEvent *)&event);
   mBlockAdded = false;
   mHGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
   SourceDragStartDone();
   /* Initialize host hide feedback to DROP_NONE. */
   mEffect = DROP_NONE;
   SourceUpdateFeedback(mEffect);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnSrcCancel --
 *
 *      Handler for when host cancels HG drag.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Detection window and fake mouse events.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnSrcCancel()
{
   TRACE_CALL();

   /*
    * Force the window to show, position the mouse over it, and release.
    * Seems like moving the window to 0, 0 eliminates frequently observed
    * flybacks when we cancel as user moves mouse in and out of destination
    * window in a H->G DnD.
    */
   OnUpdateDetWnd(true, mOrigin.get_x(), mOrigin.get_y());
   SendFakeXEvents(true, true, false, true, true,
                   mOrigin.get_x() + DRAG_DET_WINDOW_WIDTH / 2,
                   mOrigin.get_y() + DRAG_DET_WINDOW_WIDTH / 2);
   OnUpdateDetWnd(false, 0, 0);
   SendFakeXEvents(false, false, false, false, true,
                   mMousePosX,
                   mMousePosY);
   mInHGDrag = false;
   mHGGetFileStatus = DND_FILE_TRANSFER_NOT_STARTED;
   mEffect = DROP_NONE;
   RemoveBlock();
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnPrivateDrop --
 *
 *      Handler for private drop event.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Releases mouse button at current position.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnPrivateDrop(int32 x,        // UNUSED
                        int32 y)        // UNUSED
{
   TRACE_CALL();

   /* Unity manager in host side may already send the drop into guest. */
   if (mGHDnDInProgress) {

      /*
       * Release the mouse button.
       */
      SendFakeXEvents(false, true, false, false, false, 0, 0);
   }
   ResetUI();
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnDestCancel --
 *
 *      Handler for GH drag cancellation.
 *
 *      Note: This event fires as part of the complete guest-to-host sequence,
 *      not just error or user cancellation.
 *
 * Results:
 *      Uses detection window and fake mouse events to intercept drop.
 *
 * Side effects:
 *      Reinitializes UI state.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnDestCancel()
{
   TRACE_CALL();

   /* Unity manager in host side may already send the drop into guest. */
   if (mGHDnDInProgress) {
      /*
       * Show the window, move it to the mouse position, and release the
       * mouse button.
       */
      SendFakeXEvents(true, true, false, true, false, mOrigin.get_x(),
                      mOrigin.get_y());
   }
   mDestDropTime = GetTimeInMillis();
   ResetUI();
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnSrcDrop --
 *
 *      Callback when host signals drop.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Release the mouse button in the detection window.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnSrcDrop()
{
   TRACE_CALL();
   OnUpdateDetWnd(true, mOrigin.get_x(), mOrigin.get_y());

   /*
    * Move the mouse to the saved coordinates, and release the mouse button.
    */
   SendFakeXEvents(false, true, false, false, true, mMousePosX, mMousePosY);
   OnUpdateDetWnd(false, 0, 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnGetFilesDone --
 *
 *      Callback when HG file transfer completes.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Releases vmblock blocking entry.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnGetFilesDone(bool success)  // IN: true if transfer succeeded
{
   g_debug("%s: %s\n", __FUNCTION__, success ? "success" : "failed");

   /*
    * If hg drag is not done yet, only remove block. OnGtkDragEnd will
    * call ResetUI(). Otherwise destination may miss the data because
     * we are already reset.
    */

   mHGGetFileStatus = DND_FILE_TRANSFER_FINISHED;

   if (!mInHGDrag) {
      ResetUI();
   } else {
      RemoveBlock();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnUpdateDetWnd --
 *
 *      Callback to show/hide drag detection window.
 *
 * Results:
 *      Shows/hides and moves detection window.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnUpdateDetWnd(bool show,     // IN: show (true) or hide (false)
                         int32 x,       // IN: detection window's destination x-coord
                         int32 y)       // IN: detection window's destination y-coord
{
   g_debug("%s: enter 0x%lx show %d x %d y %d\n",
         __FUNCTION__,
         (unsigned long) mDetWnd->GetWnd()->get_window()->gobj(), show, x, y);

   /* If the window is being shown, move it to the right place. */
   if (show) {
      x = MAX(x - DRAG_DET_WINDOW_WIDTH / 2, mOrigin.get_x());
      y = MAX(y - DRAG_DET_WINDOW_WIDTH / 2, mOrigin.get_y());

      mDetWnd->Show();
      mDetWnd->Raise();
      mDetWnd->SetGeometry(x, y, DRAG_DET_WINDOW_WIDTH * 2, DRAG_DET_WINDOW_WIDTH * 2);
      g_debug("%s: show at (%d, %d, %d, %d)\n", __FUNCTION__, x, y, DRAG_DET_WINDOW_WIDTH * 2, DRAG_DET_WINDOW_WIDTH * 2);
      /*
       * Wiggle the mouse here. Especially for G->H DnD, this improves
       * reliability of making the drag escape the guest window immensly.
       * Stolen from the legacy V2 DnD code.
       */

      SendFakeMouseMove(x + 2, y + 2);
      mDetWnd->SetIsVisible(true);
   } else {
      g_debug("%s: hide\n", __FUNCTION__);
      mDetWnd->Hide();
      mDetWnd->SetIsVisible(false);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnUpdateUnityDetWnd --
 *
 *      Callback to show/hide fullscreen Unity drag detection window.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Detection window shown, hidden.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnUpdateUnityDetWnd(bool show,         // IN: show (true) or hide (false)
                              uint32 unityWndId, // IN: XXX ?
                              bool bottom)       // IN: place window at bottom of stack?
{
   g_debug("%s: enter 0x%lx unityID 0x%x\n",
         __FUNCTION__,
         (unsigned long) mDetWnd->GetWnd()->get_window()->gobj(),
         unityWndId);

   if (show && ((unityWndId > 0) || bottom)) {
      int width = mDetWnd->GetScreenWidth();
      int height = mDetWnd->GetScreenHeight();
      mDetWnd->SetGeometry(0, 0, width, height);
      mDetWnd->Show();
      if (bottom) {
         mDetWnd->Lower();
      }

      g_debug("%s: show, (0, 0, %d, %d)\n", __FUNCTION__, width, height);
   } else {
      if (mDetWnd->GetIsVisible() == true) {
         if (mUnityMode) {

            /*
             * Show and move detection window to current mouse position
             * and resize.
             */
            SendFakeXEvents(true, false, true, true, false, 0, 0);
         }
      } else {
         mDetWnd->Hide();
         g_debug("%s: hide\n", __FUNCTION__);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnDestMoveDetWndToMousePos --
 *
 *      Callback to move detection window to current moue position.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Detection window is moved, shown.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnDestMoveDetWndToMousePos()
{
   SendFakeXEvents(true, false, true, true, false, 0, 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnMoveMouse --
 *
 *      Callback to update mouse position.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Moves mouse.  Duh.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnMoveMouse(int32 x,  // IN: Pointer x-coord
                      int32 y)  // IN: Pointer y-coord
{
   // Position the pointer, and record its position.

   SendFakeXEvents(false, false, false, false, true, x, y);
   mMousePosX = x;
   mMousePosY = y;

   if (mDragCtx && !mGHDnDInProgress) {

      // If we are the context of a DnD, send DnD feedback to the source.

      DND_DROPEFFECT effect;
#ifndef GTK3
      effect = ToDropEffect((Gdk::DragAction)(mDragCtx->action));
#else
      effect = ToDropEffect((Gdk::DragAction)(gdk_drag_context_get_selected_action(mDragCtx)));
#endif
      if (effect != mEffect) {
         mEffect = effect;
         g_debug("%s: Updating feedback\n", __FUNCTION__);
         SourceUpdateFeedback(mEffect);
      }
   }
}


/*
 ****************************************************************************
 * BEGIN GTK+ Callbacks (dndcp as drag source: host-to-guest)
 */


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnGtkDragMotion --
 *
 *      GTK "drag_motion" signal handler.
 *
 *      We should respond by setting drag status. Note that there is no drag
 *      enter signal. We need to figure out if a new drag is happening on
 *      our own. Also, we don't respond with a "allowed" drag status right
 *      away, we start a new drag operation with the host (which tries to
 *      notify the host of the new operation). Once the host has responded),
 *      we respond with a proper drag status.
 *
 *      Note: You may see this callback during DnD when detection window
 *      is acting as a source. In that case it will be ignored. In a future
 *      refactoring, we will try and avoid this.
 *
 * Results:
 *      Returns true unless we don't recognize the types offered.
 *
 * Side effects:
 *      Via RequestData issues a Gtk::Widget::drag_get_data.
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::OnGtkDragMotion(
   const Glib::RefPtr<Gdk::DragContext> &dc,    // IN: GTK drag context
   int x,                                       // IN: drag motion x-coord
   int y,                                       // IN: drag motion y-coord
   guint timeValue)                             // IN: event timestamp
{
   /*
    * If this is a Host to Guest drag, we are done here, so return.
    */
   unsigned long curTime = GetTimeInMillis();
   g_debug("%s: enter dc %p, mDragCtx %p\n", __FUNCTION__,
         dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL);
   if (curTime - mDestDropTime <= 1000) {
      g_debug("%s: ignored %ld %ld %ld\n", __FUNCTION__,
            curTime, mDestDropTime, curTime - mDestDropTime);
      return true;
   }

   g_debug("%s: not ignored %ld %ld %ld\n", __FUNCTION__,
         curTime, mDestDropTime, curTime - mDestDropTime);

   if (mInHGDrag || (mHGGetFileStatus != DND_FILE_TRANSFER_NOT_STARTED)) {
      g_debug("%s: ignored not in hg drag or not getting hg data\n", __FUNCTION__);
      return true;
   }

   Gdk::DragAction srcActions;
   Gdk::DragAction suggestedAction;
   Gdk::DragAction dndAction = (Gdk::DragAction)0;
   Glib::ustring target = mDetWnd->GetWnd()->drag_dest_find_target(dc);

   if (!mDnD->IsDnDAllowed()) {
      g_debug("%s: No dnd allowed!\n", __FUNCTION__);
      dc->drag_status(dndAction, timeValue);
      return true;
   }

   /* Check if dnd began from this vm. */

   /*
    * TODO: Once we upgrade to shipping gtkmm 2.12, we can go back to
    *       Gdk::DragContext::get_targets, but API/ABI broke between 2.10 and
    *       2.12, so we work around it like this for now.
    */
#ifndef GTK3
   Glib::ListHandle<std::string, Gdk::AtomStringTraits> targets(
      dc->gobj()->targets, Glib::OWNERSHIP_NONE);
#else
   Glib::ListHandle<std::string, Gdk::AtomStringTraits> targets(
      gdk_drag_context_list_targets(dc->gobj()), Glib::OWNERSHIP_NONE);
#endif

   std::vector<Glib::ustring> as = targets;
   std::vector<Glib::ustring>::iterator result;
   char *pid;
   pid = Str_Asprintf(NULL, "guest-dnd-target %d", static_cast<int>(getpid()));
   if (pid) {
      result = std::find(as.begin(), as.end(), std::string(pid));
      free(pid);
   } else {
      result = as.end();
   }
   if (result != as.end()) {
      g_debug("%s: found re-entrant drop target, pid %s\n", __FUNCTION__, pid );
      return true;
   }

   mDragCtx = dc->gobj();

   if (target != Gdk::AtomString::to_cpp_type(GDK_NONE)) {
      /*
       * We give preference to the suggested action from the source, and prefer
       * copy over move.
       */
      suggestedAction = dc->get_suggested_action();
      srcActions = dc->get_actions();
      if (suggestedAction == Gdk::ACTION_COPY || suggestedAction == Gdk::ACTION_MOVE) {
         dndAction = suggestedAction;
      } else if (srcActions & Gdk::ACTION_COPY) {
         dndAction= Gdk::ACTION_COPY;
      } else if (srcActions & Gdk::ACTION_MOVE) {
         dndAction = Gdk::ACTION_MOVE;
      } else {
         dndAction = (Gdk::DragAction)0;
      }
   } else {
      dndAction = (Gdk::DragAction)0;
   }

   if (dndAction != (Gdk::DragAction)0) {
      dc->drag_status(dndAction, timeValue);
      if (!mGHDnDInProgress) {
         g_debug("%s: new drag, need to get data for host\n", __FUNCTION__);
         /*
          * This is a new drag operation. We need to start a drag thru the
          * backdoor, and to the host. Before we can tell the host, we have to
          * retrieve the drop data.
          */
         mGHDnDInProgress = true;
         /* only begin drag enter after we get the data */
         /* Need to grab all of the data. */
         if (!RequestData(dc, timeValue)) {
            g_debug("%s: RequestData failed.\n", __FUNCTION__);
            return false;
         }
      } else {
         g_debug("%s: Multiple drag motions before gh data has been received.\n",
               __FUNCTION__);
      }
   } else {
      g_debug("%s: Invalid drag\n", __FUNCTION__);
      return false;
   }
   return true;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnGtkDragLeave --
 *
 *      GTK+ "drag_leave" signal handler.
 *
 *      Logs event.  Acknowledges, finishes outdated sequence if drag context
 *      is not the same as we're currently interested in (i.e. != mDragCtx).
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      May cancel dnd associated with this drag context.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnGtkDragLeave(
   const Glib::RefPtr<Gdk::DragContext> &dc,    // IN: GTK drag context
   guint time)                                  // IN: event timestamp
{
   g_debug("%s: enter dc %p, mDragCtx %p\n", __FUNCTION__,
         dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL);

   /*
    * If we reach here after reset DnD, or we are getting a late
    * DnD drag leave signal (we have started another DnD), then
    * finish the old DnD. Otherwise, Gtk will not reset and a new
    * DnD will not start until Gtk+ times out (which appears to
    * be 5 minutes).
    * See http://bugzilla.eng.vmware.com/show_bug.cgi?id=528320
    */
   if (!mDragCtx || dc->gobj() != mDragCtx) {
      g_debug("%s: calling drag_finish\n", __FUNCTION__);
      dc->drag_finish(true, false, time);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnGtkDragBegin --
 *
 *      GTK+ "drag_begin" signal handler.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Records drag context for later use.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnGtkDragBegin(
   const Glib::RefPtr<Gdk::DragContext>& context)       // IN: GTK drag context
{
   g_debug("%s: enter dc %p, mDragCtx %p\n", __FUNCTION__,
         context ? context->gobj() : NULL, mDragCtx ? mDragCtx : NULL);
   mDragCtx = context->gobj();
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnGtkDragDataGet --
 *
 *      GTK+ "drag_data_get" signal handler.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      May insert vmblock blocking entry and request host-to-guest file transfer from
 *      host.
 *
 *      If unable to obtain drag information, may instead cancel the DND
 *      operation.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnGtkDragDataGet(
   const Glib::RefPtr<Gdk::DragContext> &dc,    // IN: GTK drag context
   Gtk::SelectionData& selection_data,          // IN: drag details
   guint info,                                  // UNUSED
   guint time)                                  // IN: event timestamp
{
   std::string str;
   std::string uriList;
   std::string stagingDirName;
   void *buf;
   size_t sz;
   utf::utf8string hgData;
   DnDFileList fList;
   std::string pre;
   std::string post;

   const utf::string target = selection_data.get_target().c_str();

   selection_data.set(target.c_str(), "");

   g_debug("%s: enter dc %p, mDragCtx %p with target %s\n", __FUNCTION__,
           dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL,
           target.c_str());

   if (!mInHGDrag) {
      g_debug("%s: not in drag, return\n", __FUNCTION__);
      return;
   }

   if (   target == DRAG_TARGET_NAME_URI_LIST
       && CPClipboard_GetItem(&mClipboard, CPFORMAT_FILELIST, &buf, &sz)) {
      size_t index = 0;

      /* Provide path within vmblock file system instead of actual path. */
      stagingDirName = GetLastDirName(mHGStagingDir);
      if (stagingDirName.length() == 0) {
         g_debug("%s: Cannot get staging directory name, stagingDir: %s\n",
                 __FUNCTION__, mHGStagingDir.c_str());
         return;
      }

      if (!fList.FromCPClipboard(buf, sz)) {
         g_debug("%s: Can't get data from clipboard\n", __FUNCTION__);
         return;
      }

      mTotalFileSize = fList.GetFileSize();

      /* Provide URIs for each path in the guest's file list. */
      if (FCP_TARGET_INFO_GNOME_COPIED_FILES == info) {
         pre = FCP_GNOME_LIST_PRE;
         post = FCP_GNOME_LIST_POST;
      } else if (FCP_TARGET_INFO_URI_LIST == info) {
         pre = DND_URI_LIST_PRE_KDE;
         post = DND_URI_LIST_POST;
      } else {
         g_debug("%s: Unknown request target: %s\n", __FUNCTION__,
                 selection_data.get_target().c_str());
         return;
      }

      /* Provide path within vmblock file system instead of actual path. */
      hgData = fList.GetRelPathsStr();

      /* Provide URIs for each path in the guest's file list. */
      while ((str = GetNextPath(hgData, index).c_str()).length() != 0) {
         uriList += pre;
         if (DnD_BlockIsReady(mBlockCtrl)) {
            uriList += mBlockCtrl->blockRoot;
            uriList += DIRSEPS + stagingDirName + DIRSEPS + str + post;
         } else {
            uriList += DIRSEPS + mHGStagingDir + DIRSEPS + str + post;
         }
      }

      /*
       * This seems to be the best place to do the blocking. If we do
       * it in the source drop callback from the DnD layer, we often
       * find ourselves adding the block too late; the user will (in
       * GNOME, in the dest) be told that it could not find the file,
       * and if you click retry, it is there, meaning the block was
       * added too late).
       *
       * We find ourselves in this callback twice for each H->G DnD.
       * We *must* always set the selection data, when called, or else
       * the DnD for that context will fail, but we *must not* add the
       * block twice or else things get confused. So we add a check to
       * see if we are in the right state (no block yet added, and we
       * are in a HG drag still, both must be true) when adding the block.
       * Doing both of these addresses bug
       * http://bugzilla.eng.vmware.com/show_bug.cgi?id=391661.
       */
      if (   !mBlockAdded
          &&  mInHGDrag
          && (mHGGetFileStatus == DND_FILE_TRANSFER_NOT_STARTED)) {
         mHGGetFileStatus = DND_FILE_TRANSFER_IN_PROGRESS;
         AddBlock();
      } else {
         g_debug("%s: not calling AddBlock\n", __FUNCTION__);
      }
      selection_data.set(DRAG_TARGET_NAME_URI_LIST, uriList.c_str());
      g_debug("%s: providing uriList [%s]\n", __FUNCTION__, uriList.c_str());
      return;
   }

   if (   target == DRAG_TARGET_NAME_URI_LIST
       && CPClipboard_ItemExists(&mClipboard, CPFORMAT_FILECONTENTS)) {
      g_debug("%s: Providing uriList [%s] for file contents DnD\n",
            __FUNCTION__, mHGFileContentsUriList.c_str());

      selection_data.set(DRAG_TARGET_NAME_URI_LIST,
                         mHGFileContentsUriList.c_str());
      return;
   }

   if (   TargetIsPlainText(target)
       && CPClipboard_GetItem(&mClipboard, CPFORMAT_TEXT, &buf, &sz)) {
      g_debug("%s: providing plain text, size %" FMTSZ "u\n", __FUNCTION__, sz);
      selection_data.set(target.c_str(), (const char *)buf);
      return;
   }

   if (   TargetIsRichText(target)
       && CPClipboard_GetItem(&mClipboard, CPFORMAT_RTF, &buf, &sz)) {
      g_debug("%s: providing rtf text, size %" FMTSZ "u\n", __FUNCTION__, sz);
      selection_data.set(target.c_str(), (const char *)buf);
      return;
   }

   /* Can not get any valid data, cancel this HG DnD. */
   g_debug("%s: no valid data for HG DnD\n", __FUNCTION__);
   ResetUI();
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnGtkDragEnd --
 *
 *      GTK+ "drag_end" signal handler.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      May reset UI state.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnGtkDragEnd(
   const Glib::RefPtr<Gdk::DragContext> &dc)    // IN: GTK drag context
{
   g_debug("%s: entering dc %p, mDragCtx %p\n", __FUNCTION__,
         dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL);

   /*
    * We may see a drag end for the previous DnD, but after a new
    * DnD has started. If so, ignore it.
    */
   if (mDragCtx && dc && (mDragCtx != dc->gobj())) {
      g_debug("%s: got old dc (new DnD started), ignoring\n", __FUNCTION__);
      return;
   }

   /*
    * If we are a file DnD and file transfer is not done yet, don't call
    * ResetUI() here, since we will do so in the fileCopyDoneChanged
    * callback.
    */
   if (DND_FILE_TRANSFER_IN_PROGRESS != mHGGetFileStatus) {
      ResetUI();
   }
   mInHGDrag = false;
}


/*
 * END GTK+ Callbacks (dndcp as drag source: host-to-guest)
 ****************************************************************************
 */


/*
 ****************************************************************************
 * BEGIN GTK+ Callbacks (dndcp as drag destination: guest-to-host)
 */


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnGtkDragDataReceived --
 *
 *      GTK+ "drag_data_received" signal handler.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      May signal to host beginning of guest-to-host DND.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnGtkDragDataReceived(
   const Glib::RefPtr<Gdk::DragContext> &dc,    // IN: GTK drag context
   int x,                                       // IN: drop location x-coord
   int y,                                       // IN: drop location y-coord
   const Gtk::SelectionData& sd,                // IN: drag content details
   guint info,                                  // UNUSED
   guint time)                                  // IN: event timestamp
{
   g_debug("%s: enter dc %p, mDragCtx %p\n", __FUNCTION__,
         dc ? dc->gobj() : NULL, mDragCtx ? mDragCtx : NULL);
   /* The GH DnD may already finish before we got response. */
   if (!mGHDnDInProgress) {
      g_debug("%s: not valid\n", __FUNCTION__);
      return;
   }

   /*
    * Try to get data provided from the source.  If we cannot get any data,
    * there is no need to inform the guest of anything. If there is no data,
    * reset, so that the next drag_motion callback that we see will be allowed
    * to request data again.
    */
   if (SetCPClipboardFromGtk(sd) == false) {
      g_debug("%s: Failed to set CP clipboard.\n", __FUNCTION__);
      ResetUI();
      return;
   }

   mNumPendingRequest--;
   if (mNumPendingRequest > 0) {
      return;
   }

   if (CPClipboard_IsEmpty(&mClipboard)) {
      g_debug("%s: Failed getting item.\n", __FUNCTION__);
      ResetUI();
      return;
   }

   /*
    * There are two points in the DnD process at which this is called, and both
    * are in response to us calling drag_data_get().  The first occurs on the
    * first "drag_motion" received and is used to start a drag; at that point
    * we need to provide the file list to the guest so we request the data from
    * the target.  The second occurs when the "drag_drop" signal is received
    * and we confirm this data with the target before starting the drop.
    *
    * Note that we prevent against sending multiple "dragStart"s or "drop"s for
    * each DnD.
    */
   if (!mGHDnDDataReceived) {
      g_debug("%s: Drag entering.\n", __FUNCTION__);
      mGHDnDDataReceived = true;
      TargetDragEnter();
   } else {
      g_debug("%s: not !mGHDnDDataReceived\n", __FUNCTION__);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnGtkDragDrop --
 *
 *      GTK+ "drag_drop" signal handler.
 *
 * Results:
 *      Returns true so long as drag target and data are (at one point)
 *      provided (i.e. not a spurious event).
 *
 * Side effects:
 *      Signals to drag source that drop is finished.
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::OnGtkDragDrop(
   const Glib::RefPtr<Gdk::DragContext> &dc,    // IN: GTK drag context
   int x,                                       // IN: drop location x-coord
   int y,                                       // IN: drop location y-coord
   guint time)                                  // IN: motion event timestamp
{
   g_debug("%s: enter dc %p, mDragCtx %p x %d y %d\n", __FUNCTION__,
         (dc ? dc->gobj() : NULL), (mDragCtx ? mDragCtx : NULL), x, y);

   Glib::ustring target;

   target = mDetWnd->GetWnd()->drag_dest_find_target(dc);
   g_debug("%s: calling drag_finish\n", __FUNCTION__);
   dc->drag_finish(true, false, time);

   if (target == Gdk::AtomString::to_cpp_type(GDK_NONE)) {
      g_debug("%s: No valid data on clipboard.\n", __FUNCTION__);
      return false;
   }

   if (CPClipboard_IsEmpty(&mClipboard)) {
      g_debug("%s: No valid data on mClipboard.\n", __FUNCTION__);
      return false;
   }

   return true;
}


/*
 * END GTK+ Callbacks (dndcp as drag destination: guest-to-host)
 ****************************************************************************
 */


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::SetCPClipboardFromGtk --
 *
 *      Construct cross-platform clipboard from GTK+ selection_data.
 *
 * Results:
 *      Returns true if conversion succeeded, false otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::SetCPClipboardFromGtk(const Gtk::SelectionData& sd) // IN
{
   DnDFileList fileList;
   DynBuf buf;
   uint64 totalSize = 0;
   int64 size;

   const utf::string target = sd.get_target().c_str();

   /* Try to get file list. */
   if (mDnD->CheckCapability(DND_CP_CAP_FILE_DND) && target == DRAG_TARGET_NAME_URI_LIST) {
      /*
       * Turn the uri list into two \0  delimited lists. One for full paths and
       * one for just the last path component.
       */
      utf::string source = sd.get_data_as_string().c_str();
      size_t index = 0;
      char *newPath;
      size_t newPathLen;

      g_debug("%s: Got file list: [%s]\n", __FUNCTION__, source.c_str());

      if (sd.get_data_as_string().length() == 0) {
         g_debug("%s: empty file list!\n", __FUNCTION__);
         return false;
      }

      /*
       * In gnome, before file list there may be a extra line indicating it
       * is a copy or cut.
       */
      if (source.length() >= 5 && source.compare(0, 5, "copy\n") == 0) {
         source = source.erase(0, 5);
      }

      if (source.length() >= 4 && source.compare(0, 4, "cut\n") == 0) {
         source = source.erase(0, 4);
      }

      while (source.length() > 0 &&
             (source[0] == '\n' || source[0] == '\r' || source[0] == ' ')) {
         source = source.erase(0, 1);
      }

      while ((newPath = DnD_UriListGetNextFile(source.c_str(),
                                               &index,
                                               &newPathLen)) != NULL) {
         char *newRelPath;

#if defined(__linux__)
         if (DnD_UriIsNonFileSchemes(newPath)) {
            /* Try to get local file path for non file uri. */
            GFile *file = g_file_new_for_uri(newPath);
            free(newPath);
            if (!file) {
               g_debug("%s: g_file_new_for_uri failed\n", __FUNCTION__);
               return false;
            }
            newPath = g_file_get_path(file);
            g_object_unref(file);
            if (!newPath) {
               g_debug("%s: g_file_get_path failed\n", __FUNCTION__);
               return false;
            }
         }
#endif
         /*
          * Parse relative path.
          */
         newRelPath = Str_Strrchr(newPath, DIRSEPC) + 1; // Point to char after '/'

         /* Keep track of how big the dnd files are. */
         if ((size = File_GetSizeEx(newPath)) >= 0) {
            totalSize += size;
         } else {
            g_debug("%s: unable to get file size for %s\n", __FUNCTION__, newPath);
         }
         g_debug("%s: Adding newPath '%s' newRelPath '%s'\n", __FUNCTION__,
               newPath, newRelPath);
         fileList.AddFile(newPath, newRelPath);
#if defined(__linux__)
         char *newUri = HgfsUri_ConvertFromPathToHgfsUri(newPath, false);
         fileList.AddFileUri(newUri);
         free(newUri);
#endif
         free(newPath);
      }

      DynBuf_Init(&buf);
      fileList.SetFileSize(totalSize);
      if (fileList.ToCPClipboard(&buf, false)) {
          CPClipboard_SetItem(&mClipboard, CPFORMAT_FILELIST, DynBuf_Get(&buf),
                              DynBuf_GetSize(&buf));
      }
      DynBuf_Destroy(&buf);
#if defined(__linux__)
      if (fileList.ToUriClipboard(&buf)) {
         CPClipboard_SetItem(&mClipboard, CPFORMAT_FILELIST_URI, DynBuf_Get(&buf),
                             DynBuf_GetSize(&buf));
      }
      DynBuf_Destroy(&buf);
#endif
      return true;
   }

   /* Try to get plain text. */
   if (   mDnD->CheckCapability(DND_CP_CAP_PLAIN_TEXT_DND)
       && TargetIsPlainText(target)) {
      std::string source = sd.get_data_as_string();
      if (   source.size() > 0
          && source.size() < DNDMSG_MAX_ARGSZ
          && CPClipboard_SetItem(&mClipboard, CPFORMAT_TEXT, source.c_str(),
                                 source.size() + 1)) {
         g_debug("%s: Got text, size %" FMTSZ "u\n", __FUNCTION__, source.size());
      } else {
         g_debug("%s: Failed to get text\n", __FUNCTION__);
         return false;
      }
      return true;
   }

   /* Try to get RTF string. */
   if (   mDnD->CheckCapability(DND_CP_CAP_RTF_DND)
       && TargetIsRichText(target)) {
      std::string source = sd.get_data_as_string();
      if (   source.size() > 0
          && source.size() < DNDMSG_MAX_ARGSZ
          && CPClipboard_SetItem(&mClipboard, CPFORMAT_RTF, source.c_str(),
                                 source.size() + 1)) {
         g_debug("%s: Got RTF, size %" FMTSZ "u\n", __FUNCTION__, source.size());
         return true;
      } else {
         g_debug("%s: Failed to get text\n", __FUNCTION__ );
         return false;
      }
   }
   return true;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::RequestData --
 *
 *      Requests clipboard data from a drag source.
 *
 *      Evaluates targets (think MIME types) offered by the drag source, and
 *      if we support any, requests the contents.
 *
 * Results:
 *      Returns true if we found a supported type.
 *
 * Side effects:
 *      May call drag_get_data.
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::RequestData(
   const Glib::RefPtr<Gdk::DragContext> &dc, // IN: GTK drag context
   guint time)                               // IN: event timestamp
{
   Glib::RefPtr<Gtk::TargetList> targets;
   targets = Gtk::TargetList::create(std::vector<Gtk::TargetEntry>());

   CPClipboard_Clear(&mClipboard);
   mNumPendingRequest = 0;

   Glib::ustring noneType = Gdk::AtomString::to_cpp_type(GDK_NONE);

   /*
    * First check file list. If file list is available, all other formats will
    * be ignored.
    */
   targets->add(Glib::ustring(DRAG_TARGET_NAME_URI_LIST));
   Glib::ustring target = mDetWnd->GetWnd()->drag_dest_find_target(dc, targets);
   targets->remove(Glib::ustring(DRAG_TARGET_NAME_URI_LIST));
   if (target != noneType) {
      mDetWnd->GetWnd()->drag_get_data(dc, target, time);
      mNumPendingRequest++;
      return true;
   }

   /* Then check plain text. */
   targets->add(Glib::ustring(TARGET_NAME_UTF8_STRING));
   targets->add(Glib::ustring(TARGET_NAME_STRING));
   targets->add(Glib::ustring(TARGET_NAME_TEXT_PLAIN));
   targets->add(Glib::ustring(TARGET_NAME_COMPOUND_TEXT));
   target = mDetWnd->GetWnd()->drag_dest_find_target(dc, targets);
   targets->remove(Glib::ustring(TARGET_NAME_STRING));
   targets->remove(Glib::ustring(TARGET_NAME_TEXT_PLAIN));
   targets->remove(Glib::ustring(TARGET_NAME_UTF8_STRING));
   targets->remove(Glib::ustring(TARGET_NAME_COMPOUND_TEXT));
   if (target != noneType) {
      mDetWnd->GetWnd()->drag_get_data(dc, target, time);
      mNumPendingRequest++;
   }

   /* Then check RTF. */
   targets->add(Glib::ustring(TARGET_NAME_APPLICATION_RTF));
   targets->add(Glib::ustring(TARGET_NAME_TEXT_RICHTEXT));
   targets->add(Glib::ustring(TARGET_NAME_TEXT_RTF));
   target = mDetWnd->GetWnd()->drag_dest_find_target(dc, targets);
   targets->remove(Glib::ustring(TARGET_NAME_APPLICATION_RTF));
   targets->remove(Glib::ustring(TARGET_NAME_TEXT_RICHTEXT));
   targets->remove(Glib::ustring(TARGET_NAME_TEXT_RTF));
   if (target != noneType) {
      mDetWnd->GetWnd()->drag_get_data(dc, target, time);
      mNumPendingRequest++;
   }
   return (mNumPendingRequest > 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::GetLastDirName --
 *
 *      Try to get last directory name from a full path name.
 *
 *      What this really means is to get the basename of the parent's directory
 *      name, intended to isolate an individual DND operation's staging directory
 *      name.
 *
 *         E.g. /tmp/VMwareDnD/abcd137 → abcd137
 *
 * Results:
 *      Returns session directory name on success, empty string otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

std::string
DnDUIX11::GetLastDirName(const std::string &str)
{
   char *baseName;
   std::string stripSlash = str;
   char *path = File_StripSlashes(stripSlash.c_str());
   if (path) {
      stripSlash = path;
      free(path);
   }

   File_GetPathName(stripSlash.c_str(), NULL, &baseName);
   if (baseName) {
      std::string s(baseName);
      free(baseName);
      return s;
   } else {
      return std::string();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::GetNextPath --
 *
 *      Convoluted somethingerother.
 *
 *      XXX Something here involves URI parsing and encoding.  Get to the bottom
 *      of this and use shared URI code.
 *
 *      Original description:
 *         Provide a substring containing the next path from the provided
 *         NUL-delimited string starting at the provided index.
 *
 * Results:
 *      Returns "a string with the next path or empty string if there are no
 *      more paths".
 *
 * Side effects:
 *      Updates index.
 *
 *-----------------------------------------------------------------------------
 */

utf::utf8string
DnDUIX11::GetNextPath(utf::utf8string& str,     // IN: NUL-delimited path list
                      size_t& index)            // IN/OUT: index into string
{
   utf::utf8string ret;
   size_t start;

   if (index >= str.length()) {
      return "";
   }

   for (start = index; str[index] != '\0' && index < str.length(); index++) {
      /*
       * Escape reserved characters according to RFC 1630.  We'd use
       * Escape_Do() if this wasn't a utf::string, but let's use the same table
       * replacement approach.
       */
      static char const Dec2Hex[] = {
         '0', '1', '2', '3', '4', '5', '6', '7',
         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
      };

      unsigned char ubyte = str[index];

      if (ubyte == '#' ||   /* Fragment identifier delimiter */
          ubyte == '?' ||   /* Query string delimiter */
          ubyte == '*' ||   /* "Special significance within specific schemes" */
          ubyte == '!' ||   /* "Special significance within specific schemes" */
          ubyte == '%' ||   /* Escape character */
          ubyte >= 0x80) {  /* UTF-8 encoding bytes */
         str.replace(index, 1, "%");
         str.insert(index + 1, 1, Dec2Hex[ubyte >> 4]);
         str.insert(index + 2, 1, Dec2Hex[ubyte & 0xF]);
         index += 2;
      }
   }

   ret = str.substr(start, index - start);
   g_debug("%s: nextpath: %s", __FUNCTION__, ret.c_str());
   index++;
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::SendFakeMouseMove --
 *
 *      Issue a fake mouse move event to the detection window.
 *
 * Results:
 *      Returns true on success, false on failure.
 *
 * Side effects:
 *      Generates mouse events.
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::SendFakeMouseMove(const int x,        // IN: x-coord
                            const int y)        // IN: y-coord
{
   return SendFakeXEvents(false, false, false, false, true, x, y);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::SendFakeXEvents --
 *
 *      Fake X mouse events and window movement for the detection window.
 *
 *      This function shows the detection window and generates button
 *      press/release and pointer motion events.
 *
 *      XXX This code should be implemented using GDK APIs.
 *          (gdk_display_warp_pointer?)
 *
 *      XXX This code should be moved into the detection window class
 *
 * Results:
 *      Returns true if generated X events, false on failure.
 *
 * Side effects:
 *      A ton of things.
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::SendFakeXEvents(
   const bool showWidget,       // IN: whether to show widget
   const bool buttonEvent,      // IN: whether to send a button event
   const bool buttonPress,      // IN: whether button event is press or release
   const bool moveWindow,       // IN: whether to move detection window
   const bool coordsProvided,   // IN: whether coords provided, else will
                                //     query current mouse position
   const int xCoord,            // IN: destination x-coord
   const int yCoord)            // IN: destination y-coord
{
   GtkWidget *widget;
   Window rootWnd;
   bool ret = false;
   Display *dndXDisplay;
   Window dndXWindow;
   Window rootReturn;
   int x;
   int y;
   Window childReturn;
   int rootXReturn;
   int rootYReturn;
   int winXReturn;
   int winYReturn;
   unsigned int maskReturn;

   TRACE_CALL();

   x = xCoord;
   y = yCoord;

   widget = GetDetWndAsWidget();

   if (!widget) {
      g_debug("%s: unable to get widget\n", __FUNCTION__);
      return false;
   }
#ifndef GTK3
   dndXDisplay = GDK_WINDOW_XDISPLAY(widget->window);
   dndXWindow = GDK_WINDOW_XWINDOW(widget->window);
#else
   dndXDisplay = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget));
   dndXWindow = GDK_WINDOW_XID(gtk_widget_get_window(widget));
#endif
   rootWnd = RootWindow(dndXDisplay, DefaultScreen(dndXDisplay));

   /*
    * Turn on X synchronization in order to ensure that our X events occur in
    * the order called.  In particular, we want the window movement to occur
    * before the mouse movement so that the events we are coercing do in fact
    * happen.
    */
   XSynchronize(dndXDisplay, True);

   if (showWidget) {
      g_debug("%s: showing Gtk widget\n", __FUNCTION__);
      gtk_widget_show(widget);
#ifndef GTK3
      gdk_window_show(widget->window);
#else
      gdk_window_show(gtk_widget_get_window(widget));
#endif
   }

   /* Get the current location of the mouse if coordinates weren't provided. */
   if (!coordsProvided) {
      if (!XQueryPointer(dndXDisplay, rootWnd, &rootReturn, &childReturn,
                          &rootXReturn, &rootYReturn, &winXReturn, &winYReturn,
                          &maskReturn)) {
         Warning("%s: XQueryPointer() returned False.\n", __FUNCTION__);
         goto exit;
      }

      g_debug("%s: current mouse is at (%d, %d)\n", __FUNCTION__,
            rootXReturn, rootYReturn);

      /*
       * Position away from the edge of the window.
       */
      int width = mDetWnd->GetScreenWidth();
      int height = mDetWnd->GetScreenHeight();
      bool change = false;

      x = rootXReturn;
      y = rootYReturn;

      /*
       * first do left and top edges.
       */

      if (x <= 5) {
         x = 6;
         change = true;
      }

      if (y <= 5) {
         y = 6;
         change = true;
      }

      /*
       * next, move result away from right and bottom edges.
       */
      if (x > width - 5) {
         x = width - 6;
         change = true;
      }
      if (y > height - 5) {
         y = height - 6;
         change = true;
      }

      if (change) {
         g_debug("%s: adjusting mouse position. root %d, %d, adjusted %d, %d\n",
               __FUNCTION__, rootXReturn, rootYReturn, x, y);
      }
   }

   if (moveWindow) {
      /*
       * Make sure the window is at this point and at the top (raised).  The
       * window is resized to be a bit larger than we would like to increase
       * the likelihood that mouse events are attributed to our window -- this
       * is okay since the window is invisible and hidden on cancels and DnD
       * finish.
       */
      XMoveResizeWindow(dndXDisplay,
                        dndXWindow,
                        x - DRAG_DET_WINDOW_WIDTH / 2 ,
                        y - DRAG_DET_WINDOW_WIDTH / 2,
                        DRAG_DET_WINDOW_WIDTH,
                        DRAG_DET_WINDOW_WIDTH);
      XRaiseWindow(dndXDisplay, dndXWindow);
      g_debug("%s: move wnd to (%d, %d, %d, %d)\n",
              __FUNCTION__,
              x - DRAG_DET_WINDOW_WIDTH / 2 ,
              y - DRAG_DET_WINDOW_WIDTH / 2,
              DRAG_DET_WINDOW_WIDTH,
              DRAG_DET_WINDOW_WIDTH);
   }

   /*
    * Generate mouse movements over the window.  The second one makes ungrabs
    * happen more reliably on KDE, but isn't necessary on GNOME.
    */
   if (mUseUInput) {
#ifdef USE_UINPUT
      FakeMouse_Move(x, y);
      FakeMouse_Move(x + 1, y + 1);
#endif
   } else {
      XTestFakeMotionEvent(dndXDisplay, -1, x, y, CurrentTime);
      XTestFakeMotionEvent(dndXDisplay, -1, x + 1, y + 1, CurrentTime);
   }
   g_debug("%s: move mouse to (%d, %d) and (%d, %d)\n", __FUNCTION__, x, y, x + 1, y + 1);

   if (buttonEvent) {
      g_debug("%s: faking left mouse button %s\n", __FUNCTION__,
              buttonPress ? "press" : "release");
      if (mUseUInput) {
#ifdef USE_UINPUT
         FakeMouse_Click(buttonPress);
#endif
      } else {
         XTestFakeButtonEvent(dndXDisplay, 1, buttonPress, CurrentTime);
         XSync(dndXDisplay, False);
      }

      if (!buttonPress) {
         /*
          * The button release simulation may be failed with some distributions
          * like Ubuntu 10.4 and RHEL 6 for guest->host DnD. So first query
          * mouse button status. If some button is still down, we will try
          * mouse device level event simulation. For details please refer
          * to bug 552807.
          */
         if (!XQueryPointer(dndXDisplay, rootWnd, &rootReturn, &childReturn,
                            &rootXReturn, &rootYReturn, &winXReturn,
                            &winYReturn, &maskReturn)) {
            Warning("%s: XQueryPointer returned False.\n", __FUNCTION__);
            goto exit;
         }

         if (   (maskReturn & Button1Mask)
             || (maskReturn & Button2Mask)
             || (maskReturn & Button3Mask)
             || (maskReturn & Button4Mask)
             || (maskReturn & Button5Mask)) {
            Debug("%s: XTestFakeButtonEvent was not working for button "
                  "release, trying XTestFakeDeviceButtonEvent now.\n",
                  __FUNCTION__);
            ret = TryXTestFakeDeviceButtonEvent();
         } else {
            g_debug("%s: XTestFakeButtonEvent was working for button release.\n",
                    __FUNCTION__);
            ret = true;
         }
      } else {
         ret = true;
      }
   }

exit:
   XSynchronize(dndXDisplay, False);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::TryXTestFakeDeviceButtonEvent --
 *
 *      Fake X mouse events in device level.
 *
 *      XXX The function will only be called if XTestFakeButtonEvent does
 *      not work for mouse button release. Later on we may only call this
 *      one for mouse button simulation if this is more reliable.
 *
 * Results:
 *      Returns true on success, false on failure.
 *
 * Side effects:
 *      Generates mouse events.
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::TryXTestFakeDeviceButtonEvent()
{
   XDeviceInfo *list = NULL;
   XDeviceInfo *list2 = NULL;
   XDevice *tdev = NULL;
   XDevice *buttonDevice = NULL;
   int numDevices = 0;
   int i = 0;
   int j = 0;
   XInputClassInfo *ip = NULL;
   GtkWidget *widget;
   Display *dndXDisplay;

   widget = GetDetWndAsWidget();

   if (!widget) {
      g_debug("%s: unable to get widget\n", __FUNCTION__);
      return false;
   }
#ifndef GTK3
   dndXDisplay = GDK_WINDOW_XDISPLAY(widget->window);
#else
   dndXDisplay = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget));
#endif

   /* First get list of all input device. */
   if (!(list = XListInputDevices (dndXDisplay, &numDevices))) {
      g_debug("%s: XListInputDevices failed\n", __FUNCTION__);
      return false;
   } else {
      g_debug("%s: XListInputDevices got %d devices\n", __FUNCTION__, numDevices);
   }

   list2 = list;

   for (i = 0; i < numDevices; i++, list++) {
      /* We only care about mouse device. */
      if (list->use != IsXExtensionPointer) {
         continue;
      }

      tdev = XOpenDevice(dndXDisplay, list->id);
      if (!tdev) {
         g_debug("%s: XOpenDevice failed\n", __FUNCTION__);
         continue;
      }

      for (ip = tdev->classes, j = 0; j < tdev->num_classes; j++, ip++) {
         if (ip->input_class == ButtonClass) {
            buttonDevice = tdev;
            break;
         }
      }

      if (buttonDevice) {
         g_debug("%s: calling XTestFakeDeviceButtonEvent for %s\n",
               __FUNCTION__, list->name);
         XTestFakeDeviceButtonEvent(dndXDisplay, buttonDevice, 1, False,
                                    NULL, 0, CurrentTime);
         buttonDevice = NULL;
      }
      XCloseDevice(dndXDisplay, tdev);
   }
   XFreeDeviceList(list2);
   return true;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::GetDetWndAsWidget --
 *
 *      Get the GtkWidget pointer for a DragDetWnd object.
 *
 *      The X11 Unity implementation requires access to the drag detection
 *      window as a GtkWindow pointer, which it uses to show and hide the
 *      detection window.
 *
 *      This function is also called by the code that issues fake X events
 *      to the detection window.
 *
 * Results:
 *      A GtkWidget* on success or NULL on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

GtkWidget *
DnDUIX11::GetDetWndAsWidget()
{
   GtkWidget *widget = NULL;

   if (!mDetWnd) {
      return NULL;
   }

   if (mDetWnd->GetWnd()->gobj()) {
      widget = GTK_WIDGET(mDetWnd->GetWnd()->gobj());
   }
   return widget;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::AddBlock --
 *
 *      Insert a vmblock blocking entry.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Caller must pair with RemoveBlock() upon dnd completion/cancellation.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::AddBlock()
{
   TRACE_CALL();

   if (mBlockAdded) {
      g_debug("%s: block already added\n", __FUNCTION__);
      return;
   }

   g_debug("%s: DnDBlockIsReady %d fd %d\n", __FUNCTION__,
           DnD_BlockIsReady(mBlockCtrl), mBlockCtrl->fd);

   if (   DnD_BlockIsReady(mBlockCtrl)
       && mBlockCtrl->AddBlock(mBlockCtrl->fd, mHGStagingDir.c_str())) {
      mBlockAdded = true;
      g_debug("%s: add block for %s.\n", __FUNCTION__, mHGStagingDir.c_str());
   } else {
      mBlockAdded = false;
      g_debug("%s: unable to add block dir %s.\n", __FUNCTION__, mHGStagingDir.c_str());
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::RemoveBlock --
 *
 *      Remove a vmblock blocking entry.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::RemoveBlock()
{
   TRACE_CALL();

   if (mBlockAdded && (DND_FILE_TRANSFER_IN_PROGRESS != mHGGetFileStatus)) {
      g_debug("%s: removing block for %s\n", __FUNCTION__, mHGStagingDir.c_str());
      /* We need to make sure block subsystem has not been shut off. */
      if (DnD_BlockIsReady(mBlockCtrl)) {
         mBlockCtrl->RemoveBlock(mBlockCtrl->fd, mHGStagingDir.c_str());
      }
      mBlockAdded = false;
   } else {
      g_debug("%s: not removing block mBlockAdded %d mHGGetFileStatus %d\n",
            __FUNCTION__,
            mBlockAdded,
            mHGGetFileStatus);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::ToDropEffect --
 *
 *      Convert a Gdk::DragAction value to its corresponding DND_DROPEFFECT.
 *
 * Results:
 *      Returns corresponding DND_DROPEFFECT or DROP_UNKNOWN if a match isn't
 *      found.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

/* static */ DND_DROPEFFECT
DnDUIX11::ToDropEffect(const Gdk::DragAction action)
{
   DND_DROPEFFECT effect;

   switch(action) {
   case Gdk::ACTION_COPY:
   case Gdk::ACTION_DEFAULT:
      effect = DROP_COPY;
      break;
   case Gdk::ACTION_MOVE:
      effect = DROP_MOVE;
      break;
   case Gdk::ACTION_LINK:
      effect = DROP_LINK;
      break;
   case Gdk::ACTION_PRIVATE:
   case Gdk::ACTION_ASK:
   default:
      effect = DROP_UNKNOWN;
      break;
   }
   return effect;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::WriteFileContentsToStagingDir --
 *
 *      Try to extract file contents from mClipboard. Write all files to a
 *      temporary staging directory. Construct uri list.
 *
 * Results:
 *      Returns true on success, false on failure.
 *
 * Side effects:
 *      Hm?
 *
 *-----------------------------------------------------------------------------
 */

bool
DnDUIX11::WriteFileContentsToStagingDir()
{
   void *buf = NULL;
   size_t sz = 0;
   XDR xdrs;
   CPFileContents fileContents;
   CPFileContentsList *contentsList = NULL;
   size_t nFiles = 0;
   CPFileItem *fileItem = NULL;
   char *tempDir = NULL;
   size_t i = 0;
   bool ret = false;

   if (!CPClipboard_GetItem(&mClipboard, CPFORMAT_FILECONTENTS, &buf, &sz)) {
      return false;
   }

   /* Extract file contents from buf. */
   xdrmem_create(&xdrs, (char *)buf, sz, XDR_DECODE);
   memset(&fileContents, 0, sizeof fileContents);

   if (!xdr_CPFileContents(&xdrs, &fileContents)) {
      g_debug("%s: xdr_CPFileContents failed.\n", __FUNCTION__);
      xdr_destroy(&xdrs);
      return false;
   }
   xdr_destroy(&xdrs);

   contentsList = fileContents.CPFileContents_u.fileContentsV1;
   if (!contentsList) {
      g_debug("%s: invalid contentsList.\n", __FUNCTION__);
      goto exit;
   }

   nFiles = contentsList->fileItem.fileItem_len;
   if (0 == nFiles) {
      g_debug("%s: invalid nFiles.\n", __FUNCTION__);
      goto exit;
   }

   fileItem = contentsList->fileItem.fileItem_val;
   if (!fileItem) {
      g_debug("%s: invalid fileItem.\n", __FUNCTION__);
      goto exit;
   }

   /*
    * Write files into a temporary staging directory. These files will be moved
    * to final destination, or deleted on next reboot.
    */
   tempDir = DnD_CreateStagingDirectory();
   if (!tempDir) {
      g_debug("%s: DnD_CreateStagingDirectory failed.\n", __FUNCTION__);
      goto exit;
   }

   mHGFileContentsUriList = "";

   for (i = 0; i < nFiles; i++) {
      utf::string fileName;
      utf::string filePathName;
      VmTimeType createTime = -1;
      VmTimeType accessTime = -1;
      VmTimeType writeTime = -1;
      VmTimeType attrChangeTime = -1;

      if (!fileItem[i].cpName.cpName_val ||
          0 == fileItem[i].cpName.cpName_len) {
         g_debug("%s: invalid fileItem[%" FMTSZ "u].cpName.\n", __FUNCTION__, i);
         goto exit;
      }

      /*
       * '\0' is used as directory separator in cross-platform name. Now turn
       * '\0' in data into DIRSEPC.
       *
       * Note that we don't convert the final '\0' into DIRSEPC so the string
       * is NUL terminated.
       */
      CPNameUtil_CharReplace(fileItem[i].cpName.cpName_val,
                             fileItem[i].cpName.cpName_len - 1,
                             '\0',
                             DIRSEPC);
      fileName = fileItem[i].cpName.cpName_val;
      filePathName = tempDir;
      filePathName += DIRSEPS + fileName;

      if (   fileItem[i].validFlags & CP_FILE_VALID_TYPE
          && CP_FILE_TYPE_DIRECTORY == fileItem[i].type) {
         if (!File_CreateDirectory(filePathName.c_str())) {
            goto exit;
         }
         g_debug("%s: created directory [%s].\n", __FUNCTION__, filePathName.c_str());
      } else if (   fileItem[i].validFlags & CP_FILE_VALID_TYPE
                 && CP_FILE_TYPE_REGULAR == fileItem[i].type) {
         FileIODescriptor file;
         FileIOResult fileErr;

         FileIO_Invalidate(&file);

         fileErr = FileIO_Open(&file,
                               filePathName.c_str(),
                               FILEIO_ACCESS_WRITE,
                               FILEIO_OPEN_CREATE_EMPTY);
         if (!FileIO_IsSuccess(fileErr)) {
            goto exit;
         }

         fileErr = FileIO_Write(&file,
                                fileItem[i].content.content_val,
                                fileItem[i].content.content_len,
                                NULL);

         FileIO_Close(&file);
         g_debug("%s: created file [%s].\n", __FUNCTION__, filePathName.c_str());
      } else {
         /*
          * Right now only Windows can provide CPFORMAT_FILECONTENTS data.
          * Symlink file is not expected. Continue with next file if the
          * type is not valid.
          */
         continue;
      }

      /* Update file time attributes. */
      createTime = fileItem->validFlags & CP_FILE_VALID_CREATE_TIME ?
         fileItem->createTime: -1;
      accessTime = fileItem->validFlags & CP_FILE_VALID_ACCESS_TIME ?
         fileItem->accessTime: -1;
      writeTime = fileItem->validFlags & CP_FILE_VALID_WRITE_TIME ?
         fileItem->writeTime: -1;
      attrChangeTime = fileItem->validFlags & CP_FILE_VALID_CHANGE_TIME ?
         fileItem->attrChangeTime: -1;

      if (!File_SetTimes(filePathName.c_str(),
                         createTime,
                         accessTime,
                         writeTime,
                         attrChangeTime)) {
         /* Not a critical error, only log it. */
         g_debug("%s: File_SetTimes failed with file [%s].\n", __FUNCTION__,
                 filePathName.c_str());
      }

      /* Update file permission attributes. */
      if (fileItem->validFlags & CP_FILE_VALID_PERMS) {
         if (Posix_Chmod(filePathName.c_str(), fileItem->permissions) < 0) {
            /* Not a critical error, only log it. */
            g_debug("%s: Posix_Chmod failed with file [%s].\n", __FUNCTION__,
                    filePathName.c_str());
         }
      }

      /*
       * If there is no DIRSEPC inside the fileName, this file/directory is a
       * top level one. We only put top level name into uri list.
       */
      if (fileName.find(DIRSEPS, 0) == utf::string::npos) {
         mHGFileContentsUriList += "file://" + filePathName + "\r\n";
      }
   }
   g_debug("%s: created uri list [%s].\n", __FUNCTION__, mHGFileContentsUriList.c_str());
   ret = true;

exit:
   xdr_free((xdrproc_t) xdr_CPFileContents, (char *)&fileContents);
   if (tempDir && !ret) {
      DnD_DeleteStagingFiles(tempDir, false);
   }
   free(tempDir);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::SourceDragStartDone --
 *
 *      Tell host that we're done with host-to-guest drag-and-drop
 *      initialization.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::SourceDragStartDone()
{
   TRACE_CALL();
   mInHGDrag = true;
   mDnD->SrcUIDragBeginDone();
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::SetBlockControl --
 *
 *      Set block control member.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::SetBlockControl(DnDBlockControl *blockCtrl)   // IN
{
   mBlockCtrl = blockCtrl;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::SourceUpdateFeedback --
 *
 *      Forward feedback from our drop source to the host.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::SourceUpdateFeedback(
   DND_DROPEFFECT effect) // IN: feedback to send to the UI-independent DnD layer.
{
   TRACE_CALL();
   mDnD->SrcUIUpdateFeedback(effect);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::TargetDragEnter --
 *
 *      With the source's drag selection data on the clipboard, signal to
 *      host to begin guest-to-host drag-and-drop.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::TargetDragEnter()
{
   TRACE_CALL();

   /* Check if there is valid data with current detection window. */
   if (!CPClipboard_IsEmpty(&mClipboard)) {
      g_debug("%s: got valid data from detWnd.\n", __FUNCTION__);
      mDnD->DestUIDragEnter(&mClipboard);
   }

   /*
    * Show the window, and position it under the current mouse position.
    * This is particularly important for KDE 3.5 guests.
    */
   SendFakeXEvents(true, false, true, true, false, 0, 0);
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::GetTimeInMillis --
 *
 *      Get Unix time in milliseconds.
 *
 *-----------------------------------------------------------------------------
 */

/* static */ unsigned long
DnDUIX11::GetTimeInMillis()
{
   VmTimeType atime;

   Hostinfo_GetTimeOfDay(&atime);
   return((unsigned long)(atime / 1000));
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::VmXDnDVersionChanged --
 *
 *      Update version information in mDnD.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::VmxDnDVersionChanged(RpcChannel *chan,        // IN
                               uint32 version)          // IN
{
   ASSERT(mDnD);
   mDnD->VmxDnDVersionChanged(version);
}


/**
 * Track enter events on detection window.
 *
 * @param[ignored] event event data (ignored)
 *
 * @return always true.
 */

bool
DnDUIX11::GtkEnterEventCB(GdkEventCrossing *ignored)
{
   TRACE_CALL();
   return true;
}

/**
 * Track enter events on detection window.
 *
 * @param[ignored] event event data (ignored)
 *
 * @return always true.
 */

bool
DnDUIX11::GtkLeaveEventCB(GdkEventCrossing *ignored)
{
   TRACE_CALL();
   return true;
}

/**
 * Track enter events on detection window.
 *
 * @param[ignored] event event data (ignored)
 *
 * @return always true.
 */

bool
DnDUIX11::GtkMapEventCB(GdkEventAny *event)
{
   TRACE_CALL();
   return true;
}


/**
 * Track enter events on detection window.
 *
 * @param[ignored] event event data (ignored)
 *
 * @return always true.
 */

bool
DnDUIX11::GtkUnmapEventCB(GdkEventAny *event)
{
   TRACE_CALL();
   return true;
}


/**
 * Track realize events on detection window.
 */

void
DnDUIX11::GtkRealizeEventCB()
{
   TRACE_CALL();
}


/**
 * Track unrealize events on detection window.
 */

void
DnDUIX11::GtkUnrealizeEventCB()
{
   TRACE_CALL();
}

/**
 * Track motion notify events on detection window.
 *
 * @param[in] event event data
 *
 * @return always true.
 */

bool
DnDUIX11::GtkMotionNotifyEventCB(GdkEventMotion *event)
{
   g_debug("%s: enter x %f y %f state 0x%x\n", __FUNCTION__,
           event->x, event->y, event->state);
   return true;
}


/**
 * Track configure events on detection window.
 *
 * @param[in] event event data
 *
 * @return always true.
 */

bool
DnDUIX11::GtkConfigureEventCB(GdkEventConfigure *event)
{
   g_debug("%s: enter x %d y %d width %d height %d\n",
           __FUNCTION__, event->x, event->y, event->width, event->height);
   return true;
}


/**
 * Track button press events on detection window.
 *
 * @param[ignored] event event data (ignored)
 *
 * @return always true.
 */

bool
DnDUIX11::GtkButtonPressEventCB(GdkEventButton *event)
{
   TRACE_CALL();
   return true;
}


/**
 * Track button release events on detection window.
 *
 * @param[ignored] event event data (ignored)
 *
 * @return always true.
 */

bool
DnDUIX11::GtkButtonReleaseEventCB(GdkEventButton *event)
{
   TRACE_CALL();
   return true;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DnDUIX11::OnWorkAreaChanged --
 *
 *      Updates mOrigin in response to changes to _NET_workArea.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
DnDUIX11::OnWorkAreaChanged(Glib::RefPtr<Gdk::Screen> screen)    // IN
{
   TRACE_CALL();

   std::vector<unsigned long> values;
   if (   xutils::GetCardinalList(screen->get_root_window(), "_NET_WORKAREA", values)
       && values.size() > 0
       && values.size() % 4 == 0) {
      /*
       * wm-spec: _NET_WORKAREA, x, y, width, height CARDINAL[][4]/32
       *
       * For the purposes of drag-and-drop, we're okay with using the screen-
       * agnostic _NET_WORKAREA atom, as the guest VM really deals with only
       * one logical monitor.
       */
      unsigned long desktop = 0;
      xutils::GetCardinal(screen->get_root_window(), "_NET_CURRENT_DESKTOP", desktop);

      mOrigin.set_x(values[0 + 4 * desktop]);
      mOrigin.set_y(values[1 + 4 * desktop]);
   } else {
      mOrigin.set_x(0);
      mOrigin.set_y(0);
   }

   g_debug("%s: new origin at (%d, %d)\n", __FUNCTION__, mOrigin.get_x(),
           mOrigin.get_y());
}
0707010000034C000081A400000000000000000000000168225505000015EC000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dndcp.cpp   /*********************************************************
 * Copyright (c) 2010-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file dndcp.cpp --
 *
 * Entry points for DnD and CP plugin.
 *
 * No platform-specific code belongs here. See copyPasteDnDWrapper.[h|cpp]
 * for abstraction API to platform implementations, and copyPasteDnDImpl.h
 * for implementation class inteface. To add a new platform, derive from
 * CopyPasteDnDImpl.
 */

#include "vmware.h"

#define G_LOG_DOMAIN "dndcp"

/**
 * Include glib.h before encountering any extern "C".
 */
#include <glib.h>

extern "C" {
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"
}

#include <string.h>
#include "copyPasteDnDWrapper.h"

extern "C" {

/**
 * Cleanup internal data on shutdown.
 *
 * @param[in]  src      Unused.
 * @param[in]  ctx      Unused.
 * @param[in]  data     Unused.
 */

static void
DnDCPShutdown(gpointer src,
              ToolsAppCtx *ctx,
              gpointer data)
{
   g_debug("%s: enter\n", __FUNCTION__);
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
   if (p) {
      p->UnregisterCP();
      p->UnregisterDnD();
   }
   CopyPasteDnDWrapper::Destroy();
}


/**
 * Handle a reset signal.
 *
 * @param[in]  src      Unused.
 * @param[in]  ctx      Unused.
 * @param[in]  data     Unused.
 */

static void
DnDCPReset(gpointer src,
           ToolsAppCtx *ctx,
           gpointer data)
{
   g_debug("%s: enter\n", __FUNCTION__);
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
   if (p) {
      p->OnReset();
   }
}


/**
 * Handle a no_rpc signal.
 *
 * @param[in]  src      Unused.
 * @param[in]  ctx      Unused.
 * @param[in]  data     Unused.
 */

static void
DnDCPNoRpc(gpointer src,
           ToolsAppCtx *ctx,
           gpointer data)
{
   g_debug("%s: enter\n", __FUNCTION__);
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
   if (p) {
      p->OnNoRpc();
   }
}


/**
 * Returns the list of the plugin's capabilities.
 *
 *
 * @param[in]  src      Unused.
 * @param[in]  ctx      Unused.
 * @param[in]  set      Whether setting or unsetting the capability.
 * @param[in]  data     Unused.
 *
 * @return A list of capabilities.
 */

static GArray *
DnDCPCapabilities(gpointer src,
                  ToolsAppCtx *ctx,
                  gboolean set,
                  gpointer data)
{
   g_debug("%s: enter\n", __FUNCTION__);
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
   if (p) {
      p->OnCapReg(set);
   }
   return NULL;
}


/**
 * Handles SetOption callback.
 *
 *
 * @param[in]  src       Unused.
 * @param[in]  ctx       Unused.
 * @param[in]  data      Unused.
 * @param[in]  option    The option being set.
 * @param[in]  value     The value the option is being set to.
 *
 * @return TRUE on success, FALSE otherwise.
 */

static gboolean
DnDCPSetOption(gpointer src,
               ToolsAppCtx *ctx,
               const gchar *option,
               const gchar *value,
               gpointer data)
{
   gboolean ret = FALSE;

   ASSERT(option);
   ASSERT(value);
   g_debug("%s: enter option %s value %s\n", __FUNCTION__, option, value);
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();

   if (option == NULL || (strcmp(option, TOOLSOPTION_ENABLEDND) != 0 &&
                         strcmp(option, TOOLSOPTION_COPYPASTE) != 0)) {
      goto out;
   }

   if (value == NULL || (strcmp(value, "2") != 0 &&
                         strcmp(value, "1") != 0 &&
                         strcmp(value, "0") != 0)) {
      goto out;
   }

   if (p) {
      p->Init(ctx);
      ret = p->OnSetOption(option, value);
   }
out:
   return ret;
}


/**
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in]  ctx   The app context.
 *
 * @return The registration data.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "dndCP",
      NULL,
      NULL
   };

   if (ctx->rpc != NULL) {
      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_CAPABILITIES, (void *) DnDCPCapabilities, NULL },
         { TOOLS_CORE_SIG_RESET, (void *) DnDCPReset, NULL },
         { TOOLS_CORE_SIG_NO_RPC, (void *) DnDCPNoRpc, NULL },
         { TOOLS_CORE_SIG_SET_OPTION, (void *) DnDCPSetOption, NULL },
         { TOOLS_CORE_SIG_SHUTDOWN, (void *) DnDCPShutdown, NULL }
      };

      ToolsAppReg regs[] = {
         { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
      };

      /*
       * DnD/CP Initialization here.
       */

      CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
      if (p) {
         p->Init(ctx);
         p->PointerInit();
      }

      regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
      return &regData;
   }

   return NULL;
}

}
0707010000034D000081A40000000000000000000000016822550500001DCD000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/dragDetWndX11.cpp   /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file dragDetWndX11.cpp
 *
 * Detection window code for Linux/X11, based on Gtkmm. Includes unit test
 * code.
 */

#define G_LOG_DOMAIN "dndcp"

#include "dragDetWndX11.h"
#include "dndUIX11.h"
#include <list>

/**
 *
 * Constructor.
 */

DragDetWnd::DragDetWnd() :
   m_isVisible(false)
{
#if defined(DETWNDDEBUG)
   DebugSetAttributes();
#endif

   bool useInvisible = true;

#ifdef GTK3
   char *xdgSessionType = getenv("XDG_SESSION_TYPE");
   if (   (xdgSessionType != NULL)
       && (strstr(xdgSessionType, "wayland") != NULL)) {
      useInvisible = false;
   }
#endif

   if (useInvisible) {
      mWnd = new DragDetWndImpl<Gtk::Invisible>();
   } else {
      DragDetWndImpl<Gtk::Window> *win = new DragDetWndImpl<Gtk::Window>();
      win->set_accept_focus(false);
      win->set_decorated(false);
      win->set_keep_above();
      // Makes this window transparent because we don't want user to see it.
      win->set_opacity(0.01);
      // Calls 'Show' to create the Gtk::Window object.
      win->show();
      win->hide();

      mWnd = win;
   }
}


/**
 *
 * Destructor.
 */

DragDetWnd::~DragDetWnd()
{
}


/**
 * Get the actual widget.
 */

Gtk::Widget *
DragDetWnd::GetWnd()
{
   return mWnd;
}


/**
 * Flush the X connection.
 */

void
DragDetWnd::Flush()
{
   Glib::RefPtr<Gdk::Display> gdkdisplay = Gdk::Display::get_default();
   if (gdkdisplay) {
      gdkdisplay->sync();
      gdkdisplay->flush();
   }
}


/**
 *
 * Show the window.
 */

void
DragDetWnd::Show(void)
{
   GetWnd()->show_now();
   Flush();
}


/**
 *
 * Hide the window.
 */

void
DragDetWnd::Hide(void)
{
   GetWnd()->hide();
   Flush();
}


/**
 *
 * Raise the window.
 */

void
DragDetWnd::Raise(void)
{
   Glib::RefPtr<Gdk::Window> gdkwin = GetWnd()->get_window();
   if (gdkwin) {
      gdkwin->raise();
   }
   Flush();
}


/**
 *
 * Lower the window.
 */

void
DragDetWnd::Lower(void)
{
   Glib::RefPtr<Gdk::Window> gdkwin = GetWnd()->get_window();
   if (gdkwin) {
      gdkwin->lower();
   }
   Flush();
}


/**
 *
 * Get the width of the screen associated with this window.
 *
 * @return width of screen, in pixels.
 */

int
DragDetWnd::GetScreenWidth(void)
{
   Glib::RefPtr<Gdk::Screen> gdkscreen = GetWnd()->get_screen();
   return gdkscreen->get_width();
}


/**
 *
 * Get the height of the screen associated with this window.
 *
 * @return height of screen, in pixels.
 */

int
DragDetWnd::GetScreenHeight(void)
{
   Glib::RefPtr<Gdk::Screen> gdkscreen = GetWnd()->get_screen();
   return gdkscreen->get_height();
}


#if defined(DETWNDDEBUG)
/**
 *
 * Set default window attributes appropriate for debugging detection windows.
 *
 * @note This only applies to instances of DragDetWnd that are derived from
 * GTK::Window.
 */

void
DragDetWnd::DebugSetAttributes(void)
{
   GetWnd()->set_default_size(1, 1);
   GetWnd()->set_resizable(true);
   GetWnd()->set_decorated(false);
   GetWnd()->set_type_hint(Gdk::WINDOW_TYPE_HINT_DOCK);
}
#endif


/**
 *
 * Set the geometry of the window.
 *
 * @param[in] x desired x-coordinate of the window.
 * @param[in] y desired y-coordinate of the window.
 * @param[in] width desired width of the window.
 * @param[in] height desired height of the window.
 */

void
DragDetWnd::SetGeometry(const int x,
                        const int y,
                        const int width,
                        const int height)
{
   Glib::RefPtr<Gdk::Window> gdkwin = GetWnd()->get_window();

   if (gdkwin) {
      gdkwin->move_resize(x, y, width, height);
      Flush();
   }
}


/**
 *
 * Get the current geometry of the window.
 *
 * @param[out] x current x-coordinate of the window.
 * @param[out] y current y-coordinate of the window.
 * @param[out] width current width of the window.
 * @param[out] height current height of the window.
 *
 * @note The current geometry may be inaccurate if retrieved too quickly
 * after a change made by SetGeometry(). This is due to the realities of
 * X and window managers. Some of this is mitigated by the use of flush()
 * and sync() calls in SetGeometry(), but these are no guarantee.
 */

void
DragDetWnd::GetGeometry(int &x, int &y, int &width, int &height)
{
   Glib::RefPtr<Gdk::Window> gdkwin = GetWnd()->get_window();
   if (gdkwin) {
#ifndef GTK3
      int dummy;
      gdkwin->get_geometry(x, y, width, height, dummy);
#else
      gdkwin->get_geometry(x, y, width, height);
#endif
#if defined(DETWNDTEST)
      Flush();
#endif
   }
}

/*
 * Code below here is for unit tests.
 */

#if defined(DETWNDTEST)

/**
 *
 * Add a button to launch unit tests to the drag detection window.
 */

void
DragDetWndTest::CreateTestUI()
{
   m_button.set_label("Start Unit Tests");
   add(m_button);
   m_button.signal_clicked().connect(sigc::mem_fun(*this, &DragDetWndTest::RunUnitTests));
   m_button.show();
}


/**
 *
 * Run some unit tests, then exit. Requires a main program, refer to
 * bora-vmsoft/toolbox/linux/vmwareuser/detWndTest/main.cpp for an
 * example.
 */

void
DragDetWndTest::RunUnitTests()
{
   DragDetWnd testWnd;
   int testCount = 0;
   int failCount = 0;

#if defined(DETWNDDEBUG)
   testWnd.SetAttributes();
#endif
   testWnd.Show();
   int x, y, width, height;
   testWnd.GetGeometry(x, y, width, height);
   printf("Geometry is x %d y %d width %d height %d\n", x, y, width, height);

   for (int i = 10; i < 50; Gtk::Main::iteration(), i++) {
      testCount++;
      printf("Setting geometry to x %d y %d w %d h %d\n", i * 10, i * 10, i * 10, i * 10);
      testWnd.SetGeometry(i * 10, i * 10, i * 10, i * 10);
      sleep(1);
      testWnd.GetGeometry(x, y, width, height);
      printf("Geometry is x %d y %d width %d height %d\n", x, y, width, height);
      if (x != i * 10 || y != i * 10 || width != i * 10) {
         printf("FAIL x or y not correct\n");
         failCount++;
      }
   }

   for (int i = 49; i > 0; Gtk::Main::iteration(), i--) {
      testCount++;
      printf("Setting geometry to x %d y %d w %d h %d\n", i * 10, i * 10, i * 10, i * 10);
      testWnd.SetGeometry(i * 10, i * 10, i * 10, i * 10);
      sleep(1);
      testWnd.GetGeometry(x, y, width, height);
      printf("Geometry is x %d y %d width %d height %d\n", x, y, width, height);
      if (x != i * 10 || y != i * 10 || width != i * 10) {
         printf("FAIL width or height not correct\n");
         failCount++;
      }
   }

   testWnd.SetGeometry(500, 500, 300, 300);

   for (int i = 0; i < 60; Gtk::Main::iteration(), i++) {
      if (i % 2) {
         printf("Hide\n");
         testWnd.Hide();
      } else {
         printf("Show\n");
         testWnd.Show();
         testWnd.Raise();
      }
      sleep(1);
   }

   printf("Done fail count %d (%.2f%%)\n", failCount, 100.0 * failCount/testCount);
   Gtk::Main::quit();
}
#endif
   0707010000034E000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/fakeMouseWayland    0707010000034F000081A40000000000000000000000016822550500002099000000000000000000000000000000000000006000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/fakeMouseWayland/fakeMouseWayland.cpp   /*********************************************************
 * Copyright (C) 2018-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file fakeMouseWayland.cpp --
 *
 *    Implement the methods that simulates the mouse motion.
 */

#define G_LOG_DOMAIN "dndcp"

#include "fakeMouseWayland.h"

#include <linux/input.h>
#include <linux/ioctl.h>
#include <linux/uinput.h>
#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define UINPUT_DND_POINTER_NAME "VMware DnD UInput pointer"


// The handle for the uinput device.
static int uinput_fd = -1;

// Indicates if the uinput device is created
static bool isInit = false;

/*
 *-----------------------------------------------------------------------------
 *
 * FakeMouse_IsInit --
 *
 *      Check if the uinput device is created.
 *
 * Results:
 *      True if the uinput device is created.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool
FakeMouse_IsInit()
{
   return isInit;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FakeMouse_Init --
 *
 *      Initialize the uinput device.
 *
 * Results:
 *      True if the uinput device is created successfully.
 *
 * Side effects:
 *      uinput device is created if succeed.
 *
 *-----------------------------------------------------------------------------
 */

bool
FakeMouse_Init(int fd,       // fd for uinput
               int width,    // width of the screen
               int height)   // height of the screen
{
   if (FakeMouse_IsInit()) {
      return true;
   }

   g_debug("%s: Init the uinput device. fd:%d, w:%d, h:%d\n",
           __FUNCTION__, fd, width, height);

   uinput_fd = fd;
   if (uinput_fd == -1) {
      return false;
   }

   /*
    * The uinput old interface is used for compatibility.
    * For more information please refer to:
    * https://www.kernel.org/doc/html/v4.12/input/uinput.html
    */
   struct uinput_user_dev dev;
   memset(&dev, 0, sizeof(dev));
   snprintf(dev.name, UINPUT_MAX_NAME_SIZE, UINPUT_DND_POINTER_NAME);

   dev.absmin[ABS_X] = 0;
   dev.absmax[ABS_X] = width - 1;
   dev.absmin[ABS_Y] = 0;
   dev.absmax[ABS_Y] = height - 1;

   if (write(uinput_fd, &dev, sizeof(dev)) < 0) {
      g_debug("%s: Failed to write\n", __FUNCTION__);
      goto exit;
   }
   if (ioctl(uinput_fd, UI_SET_EVBIT, EV_ABS) < 0) {
      g_debug("%s: Failed to register UI_SET_EVBIT EV_ABS\n", __FUNCTION__);
      goto exit;
   }
   if (ioctl(uinput_fd, UI_SET_ABSBIT, ABS_X) < 0) {
      g_debug("%s: Failed to register UI_SET_ABSBIT ABS_X\n", __FUNCTION__);
      goto exit;
   }
   if (ioctl(uinput_fd, UI_SET_ABSBIT, ABS_Y) < 0) {
      g_debug("%s: Failed to register UI_SET_ABSBIT ABS_Y\n", __FUNCTION__);
      goto exit;
   }
   if (ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY) < 0) {
      g_debug("%s: Failed to register UI_SET_EVBIT EV_KEY\n", __FUNCTION__);
      goto exit;
   }
   if (ioctl(uinput_fd, UI_SET_KEYBIT, BTN_MOUSE) < 0) {
      g_debug("%s: Failed to register UI_SET_KEYBIT BTN_MOUSE\n", __FUNCTION__);
      goto exit;
   }
   if (ioctl(uinput_fd, UI_SET_KEYBIT, BTN_LEFT) < 0) {
      g_debug("%s: Failed to register UI_SET_KEYBIT BTN_LEFT\n", __FUNCTION__);
      goto exit;
   }
   if (ioctl(uinput_fd, UI_DEV_CREATE, 0) < 0) {
      g_debug("%s: Failed to create UInput device\n", __FUNCTION__);
      goto exit;
   }

   /*
    * On UI_DEV_CREATE the kernel will create the device node for this
    * device. Insert a pause here so that userspace has time
    * to detect, initialize the new device, and can start listening to
    * the event, otherwise it will not notice the event we are about
    * to send.
    */
   usleep(100 * 1000);

   isInit = true;
   return true;

exit:
   FakeMouse_Destory();
   return false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FakeMouse_Update --
 *
 *      Update the width and height properties of  the uinput device.
 *
 * Results:
 *      True if the uinput device is updated successfully.
 *
 * Side effects:
 *      uinput device is updated.
 *
 *-----------------------------------------------------------------------------
 */

bool
FakeMouse_Update(int width,    // width of the screen
                 int height)   // height of the screen
{
   if (!FakeMouse_IsInit()) {
      return false;
   }

   FakeMouse_Destory();
   return FakeMouse_Init(uinput_fd, width, height);
}


/*
 *-----------------------------------------------------------------------------
 *
 * FakeMouse_Destory --
 *
 *      Destory the uinput device.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      uinput device is destory.
 *
 *-----------------------------------------------------------------------------
 */

void
FakeMouse_Destory()
{
   if (!FakeMouse_IsInit()) {
      return;
   }

   if (ioctl(uinput_fd, UI_DEV_DESTROY, 0) < 0) {
      g_debug("%s: Failed to destroy uinput device\n", __FUNCTION__);
   }
   isInit = false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FakeMouse_Move --
 *
 *      Move the pointer to (x, y).
 *
 * Results:
 *      True if success.
 *
 * Side effects:
 *      Pointer position is updated.
 *
 *-----------------------------------------------------------------------------
 */

bool
FakeMouse_Move(int x,    // IN
               int y)    // IN
{
   if (!FakeMouse_IsInit()) {
      return false;
   }

   bool retValue = true;
   struct input_event event;

   event.type = EV_ABS;
   event.code = ABS_X;
   event.value = x;
   gettimeofday(&event.time, NULL);
   if (write(uinput_fd, &event, sizeof(event)) < 0) {
      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
      retValue = false;
   }

   event.type = EV_ABS;
   event.code = ABS_Y;
   event.value = y;
   gettimeofday(&event.time, NULL);
   if (write(uinput_fd, &event, sizeof(event)) < 0) {
      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
      retValue = false;
   }

   event.type = EV_SYN;
   event.code = SYN_REPORT;
   event.value = 0;
   gettimeofday(&event.time, NULL);
   if (write(uinput_fd, &event, sizeof(event)) < 0) {
      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
      retValue = false;
   }

   return retValue;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FakeMouse_Click --
 *
 *      Simulate the pointer down/up event.
 *
 * Results:
 *      True if success.
 *
 * Side effects:
 *      Pointer button event is updated.
 *
 *-----------------------------------------------------------------------------
 */

bool
FakeMouse_Click(bool down)  // IN
{
   if (!FakeMouse_IsInit()) {
      return false;
   }

   bool retValue = true;
   struct input_event event;

   event.type = EV_KEY;
   event.code = BTN_LEFT;
   event.value = down;
   gettimeofday(&event.time, NULL);
   if (write(uinput_fd, &event, sizeof(event)) < 0) {
      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
      retValue = false;
   }

   event.type = EV_SYN;
   event.code = SYN_REPORT;
   event.value = 0;
   if (write(uinput_fd, &event, sizeof(event)) < 0) {
      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
      retValue = false;
   }

   /*
    * Insert a pause here so that userspace has time to detect this event,
    * otherwise it will not notice the event we are about to send.
    */
   usleep(100 * 1000);
   return retValue;
}
   07070100000350000081A40000000000000000000000016822550500000513000000000000000000000000000000000000005E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/fakeMouseWayland/fakeMouseWayland.h /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file fakeMouseWayland.h
 *
 *    Implement the methods that simulates the mouse motion.
 *
 */

#ifndef __FAKE_MOUSE_WAYLAND_H__
#define __FAKE_MOUSE_WAYLAND_H__

bool FakeMouse_Init(int fd, int width, int height);
bool FakeMouse_IsInit();
bool FakeMouse_Update(int width, int height);
void FakeMouse_Destory();
bool FakeMouse_Move(int x, int y);
bool FakeMouse_Click(bool down);

#endif // __FAKE_MOUSE_WAYLAND_H__
 07070100000351000081A40000000000000000000000016822550500002D54000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/pointer.cpp /*********************************************************
 * Copyright (C) 2006-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * pointer.cpp --
 *
 *    Pointer functions.
 */

#define G_LOG_DOMAIN "dndcp"

#if defined(WIN32)
#include "dndPluginInt.h"
#elif defined(__APPLE__)
extern void PointerGetMacCursorPos(int *rootX, int *rootY);
extern void PointerSetMacCursorPos(int x, int y);
#else
#include "dndPluginIntX11.h"
#endif

extern "C" {
#include "copyPasteCompat.h"
}

#include "copyPasteDnDWrapper.h"
#include "pointer.h"
#include "vmware/tools/utils.h"
#include "vm_assert.h"
#include "backdoor_def.h"

extern "C" {
   #include "backdoor.h"
   #include "rpcvmx.h"
}


typedef enum {
   ABSMOUSE_UNAVAILABLE,
   ABSMOUSE_AVAILABLE,
   ABSMOUSE_UNKNOWN
} AbsoluteMouseState;


static Bool mouseIsGrabbed;
static AbsoluteMouseState absoluteMouseState = ABSMOUSE_UNKNOWN;
static uint8 gHostClipboardTries = 0;

#if defined(WIN32)
extern BOOL vmx86WantsSelection;
#endif

static void PointerGrabbed(void);
static void PointerUngrabbed(void);
static gboolean PointerUpdatePointerLoop(gpointer clientData);

#define POINTER_UPDATE_TIMEOUT 100


/*
 *----------------------------------------------------------------------------
 *
 * PointerGetAbsoluteMouseState
 *
 *    Are the host/guest capable of using absolute mouse mode?
 *
 * Results:
 *    TRUE if host is in absolute mouse mode, FALSE otherwise.
 *
 * Side effects:
 *    Issues Tools RPC.
 *
 *----------------------------------------------------------------------------
 */

static AbsoluteMouseState
PointerGetAbsoluteMouseState(void)
{
   Backdoor_proto bp;
   AbsoluteMouseState state = ABSMOUSE_UNKNOWN;

   bp.in.cx.halfs.low = BDOOR_CMD_ISMOUSEABSOLUTE;
   Backdoor(&bp);
   if (bp.out.ax.word == 0) {
      state = ABSMOUSE_UNAVAILABLE;
   } else if (bp.out.ax.word == 1) {
      state = ABSMOUSE_AVAILABLE;
   }

   return state;
}


#if !defined(WIN32) && !defined(__APPLE__)
/*
 *-----------------------------------------------------------------------------
 *
 * PointerGetXCursorPos --
 *
 *      Return the position in pixels of the X (mouse) pointer in the root
 *      window.
 *
 * Results:
 *      x and y coordinates.
 *
 * Side effects:
 *      None.
 *-----------------------------------------------------------------------------
 */

void
PointerGetXCursorPos(int *rootX, int *rootY)
{
   Window rootWin;
   Window childWin;
   int x;
   int y;
   unsigned int mask;

   XQueryPointer(gXDisplay, gXRoot, &rootWin, &childWin, rootX, rootY, &x, &y, &mask);
}


/*
 *-----------------------------------------------------------------------------
 *
 * PointerSetXCursorPos
 *
 *      Set the position in pixels of the X (mouse) pointer in the root window
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
PointerSetXCursorPos(int x, int y)
{
   XWarpPointer(gXDisplay, None, gXRoot, 0, 0, 0, 0, x, y);
}

#endif


/*
 *-----------------------------------------------------------------------------
 *
 * PointerGetPos --
 *
 *      Retrieve the host notion of the guest pointer location.
 *
 * Results:
 *      '*x' and '*y' are the coordinates (top left corner is 0, 0) of the
 *      host notion of the guest pointer location. (-100, -100) means that the
 *      mouse is not grabbed on the host.
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

static void
PointerGetPos(int16 *x, // OUT
              int16 *y) // OUT
{
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_GETPTRLOCATION;
   Backdoor(&bp);
   *x = bp.out.ax.word >> 16;
   *y = bp.out.ax.word;
}


/*
 *-----------------------------------------------------------------------------
 *
 * PointerSetPos --
 *
 *      Update the host notion of the guest pointer location. 'x' and 'y' are
 *      the coordinates (top left corner is 0, 0).
 *
 * Results:
 *      None
 *
 * Side effects:
 *	None
 *
 *-----------------------------------------------------------------------------
 */

static void
PointerSetPos(uint16 x, // IN
              uint16 y) // IN
{
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_SETPTRLOCATION;
   bp.in.size = (uint32) ((x << 16) | y);
   Backdoor(&bp);
}


/*
 *-----------------------------------------------------------------------------
 *
 * PointerGrabbed --
 *
 *      Called when the pointer's state switches from released to grabbed.
 *      We warp the cursor to whatever position the vmx tells us, and then
 *      setup the loop which attempts to get the host clipboard.
 *
 * Result:
 *      None..
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
PointerGrabbed(void)
{
   short hostPosX;
   short hostPosY;

   PointerGetPos(&hostPosX, &hostPosY);
#if defined(WIN32)
   SetCursorPos(hostPosX, hostPosY);
#elif defined(__APPLE__)
   if (!CopyPaste_IsRpcCPSupported()) {
      if(absoluteMouseState != ABSMOUSE_AVAILABLE) {
        PointerSetMacCursorPos(hostPosX, hostPosY);
      }
   }
#else
   PointerSetXCursorPos(hostPosX, hostPosY);
#endif
   gHostClipboardTries = 9;
}


/*
 *-----------------------------------------------------------------------------
 *
 * PointerUngrabbed --
 *
 *    Called by the background thread when the pointer's state switches
 *    from grabbed to ungrabbed
 *
 * Result:
 *      None..
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
PointerUngrabbed()
{
#if defined(WIN32)
   if (vmx86WantsSelection) {
      /*
       * vmx agrees to exchange selections. This is a little
       * optimization to avoid an unnecessary backdoor call if vmx
       * disagrees.
       */
      CopyPaste_RequestSelection();
   }
#else
   CopyPaste_RequestSelection();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * PointerUpdatePointerLoop  --
 *
 *      Event Manager function for tracking the mouse/pointer/clipboard state.
 *      Manage grabbed/ungrab state based on x/y data from backdoor. On the
 *      transition to grabbed, call PointerHasBeenGrabbed(). While grabbed,
 *      send guest pointer coordinates thru the backdoor. Also, make several
 *      attempts to get the host clipboard from the backdoor. When changing
 *      to ungrabbed, call PointerHasBeenUngrabbed, which will push our
 *      clipboard thru the backdoor. While ungrabbed, don't do a thing.
 *
 *      This function is queued in Event Manager only when vmx doesn't support
 *      RPC copy/paste because newer vmx initiates copy/paste from UI through
 *      RPC, and doesn't need cursor grab/ungrab state to start copy/paste.
 *
 * Results:
 *      FALSE.
 *
 * Side effects:
 *      Lots. The vmx's notion of guest cursor position could change, the
 *      vmx's clipboard could change, and the guest's clipboard could change.
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
PointerUpdatePointerLoop(gpointer clientData) // IN: unused
{
   int16 hostPosX, hostPosY;
#if defined(WIN32)
   POINT guestPos;
#else
   int guestX, guestY;
#endif

   PointerGetPos(&hostPosX, &hostPosY);
   if (mouseIsGrabbed) {
      if (hostPosX == UNGRABBED_POS) {
         /* We transitioned from grabbed to ungrabbed */
         mouseIsGrabbed = FALSE;
         g_debug("PointerUpdatePointerLoop: ungrabbed\n");
         PointerUngrabbed();
      } else {
#if defined(WIN32)
         /*
          * We used to return early if GetCursorPos() failed, but we at least want to
          * continue and do the selection work. Also, I'm not sure we need this code anymore
          * since all new tools have absolute pointing device
          */
         if (!GetCursorPos(&guestPos)) {
            g_debug("PointerIsGrabbed: GetCursorPos() failed!\n");
         } else {
            if ( hostPosX != guestPos.x || hostPosY != guestPos.y) {
               /*
                * Report the new guest pointer location (It is used to teach VMware
                * where to position the outside pointer if the user releases the guest
                * pointer via the key combination).
                */
               PointerSetPos(guestPos.x, guestPos.y);
            }
         }
#elif defined(__APPLE__)
         if (!CopyPaste_IsRpcCPSupported()) {
            if(absoluteMouseState != ABSMOUSE_AVAILABLE) {
                PointerGetMacCursorPos(&guestX, &guestY);
                if ( hostPosX != guestX || hostPosY != guestY) {
                    PointerSetPos(guestX, guestY);
                }
            }
         }
#else
         PointerGetXCursorPos(&guestX, &guestY);
         if ( hostPosX != guestX || hostPosY != guestY) {
            PointerSetPos(guestX, guestY);
         }
#endif
         CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();
         ASSERT(wrapper);
         if (gHostClipboardTries > 0) {
            gHostClipboardTries--;
            if (wrapper->IsCPEnabled() && gHostClipboardTries < 6 &&
               CopyPaste_GetBackdoorSelections()) {
               gHostClipboardTries = 0;
            }
         }
      }
   } else {
      if (hostPosX != UNGRABBED_POS) {
         mouseIsGrabbed = TRUE;
         g_debug("PointerUpdatePointerLoop: grabbed\n");
         PointerGrabbed();
      }
   }

   if (!CopyPaste_IsRpcCPSupported() ||
       (absoluteMouseState == ABSMOUSE_UNAVAILABLE)) {
      CopyPasteDnDWrapper *wrapper = CopyPasteDnDWrapper::GetInstance();
      ToolsAppCtx *ctx = wrapper->GetToolsAppCtx();

      if (ctx) {
         GSource *src = VMTools_CreateTimer(POINTER_UPDATE_TIMEOUT);

         VMTOOLSAPP_ATTACH_SOURCE(ctx, src, PointerUpdatePointerLoop, NULL, NULL);
         g_source_unref(src);
      }
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Pointer_Init  --
 *
 *     One time pointer initialization stuff. Enter the pointer update
 *     loop which will check mouse position and put pointer in grabbed
 *     and ungrabbed state, accordingly (see PointerUpdatePointerLoop()
 *     for details.)
 *
 * Results:
 *      None..
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Pointer_Init(ToolsAppCtx *ctx)
{
   absoluteMouseState = PointerGetAbsoluteMouseState();
   g_debug("%s:absoluteMouseState:%s\n", __FUNCTION__,
           ((absoluteMouseState == ABSMOUSE_UNAVAILABLE)?
           "ABSMOUSE_UNAVAILABLE":((absoluteMouseState == ABSMOUSE_AVAILABLE)?
           "ABSMOUSE_AVAILABLE":"ABSMOUSE_UNKNOWN")));
   PointerUpdatePointerLoop(NULL);
   mouseIsGrabbed = FALSE;
}
07070100000352000081A400000000000000000000000168225505000003F6000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/pointer.h   /*********************************************************
 * Copyright (C) 1999-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * pointer.h --
 *
 *    Commands for pointer control.
 */

#include "vmware/tools/plugin.h"

void Pointer_Init(ToolsAppCtx *ctx);
  07070100000353000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/stringxx    07070100000354000081A40000000000000000000000016822550500000691000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/stringxx/autoCPtr.hh    /*********************************************************
 * Copyright (c) 2014-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef AUTOCPTR_HH
#define AUTOCPTR_HH

#include <memory>


/*
 *-----------------------------------------------------------------------------
 *
 * auto_unique --
 *
 *      A helper function to create and return std::unique_ptr objects with
 *      deduced types.
 *
 * Returns:
 *      Returns the constructed std::unique_ptr.
 *
 * Usage:
 *      auto foo = auto_unique(AllocateFoo(), DeleteFoo);
 *
 *-----------------------------------------------------------------------------
 */

template<typename T, typename Deleter = std::default_delete<T>>
std::unique_ptr<T, Deleter>
auto_unique(T* p,                        // IN
            Deleter deleter = Deleter()) // IN/OPT
{
   return {p, deleter};
}


#endif // AUTOCPTR_HH
   07070100000355000081A4000000000000000000000001682255050000B9BD000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/stringxx/string.cc  /*********************************************************
 * Copyright (c) 2008-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * string.cc --
 *
 *     A string wrapper for bora/lib/unicode. This class is intended to provide
 *     more c++ features such as operator overloading, automatic string conversion
 *     between different types of string classes.
 */


#include <sstream>
#include <iostream>

#include "autoCPtr.hh"
#include "string.hh"
#include "unicode.h"
#include "util.h"


namespace utf {

/*
 * Initialize static scope variables,
 *
 * Note that with the way this is done, it's important not to delay load glib
 * libraries. See bug 397373 for more details. If you're getting crazy values
 * for utf::string::npos, check your linker flags.
 */
const string::size_type string::npos = Glib::ustring::npos;


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::string()
   : mUstr(),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::string(const char *s) // IN
   : mUstr(),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
   // If the input is NULL, then there's nothing to do.
   if (UNLIKELY(s == NULL)) {
      return;
   }

   mUstr = s;
   ASSERT(Validate(mUstr));
}


#ifdef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Constructor from a ubstr_t object.  Copies the UTF-16 representation of
 *      the ubstr_t.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Makes a copy of the ubstr_t data and frees that data when the
 *      utf::string is destroyed.
 *
 * Note:
 *      WIN32 only call
 *
 *-----------------------------------------------------------------------------
 */

string::string(const ubstr_t &s) // IN
   : mUstr(),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
   // If the input is empty, then there's nothing to do.
   if (s.length() == 0) {
      return;
   }

   mUstr = static_cast<const char *>(s);
   ASSERT(Validate(mUstr));
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Constructor from a _bstr_t object.  Copies the UTF-16 representation of
 *      the _bstr_t.  Needed for dealing with _com_error::Description().
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Makes a copy of the _bstr_t data and frees that data when
 *      the utf::string is destroyed.
 *
 * Note:
 *      WIN32 only call
 *
 *-----------------------------------------------------------------------------
 */

string::string(const _bstr_t &s) // IN
   : mUstr(),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
   // If the input is empty, then there's nothing to do.
   if (s.length() == 0) {
      return;
   }

   mUstr = auto_unique(
      Unicode_AllocWithUTF16(static_cast<const utf16_t *>(s)),
      free).get();
   ASSERT(Validate(mUstr));
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::string(const utf16string &s) // IN
   : mUstr(),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
   // If the input is empty, then there's nothing to do.
   if (s.empty()) {
      return;
   }

   string copy(s.c_str());
   swap(copy);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::string(const utf16_t *s) // IN
   : mUstr(),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
   // If the input is NULL, then there's nothing to do.
   if (UNLIKELY(s == NULL)) {
      return;
   }

   /*
    * Since we already have a UTF-16 representation of the string, copy it
    * now.
    */
   mUtf16Cache = Unicode_UTF16Strdup(s);

   mUstr = auto_unique(Unicode_AllocWithUTF16(s), free).get();
   ASSERT(Validate(mUstr));
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::string(const char *s,           // IN
               StringEncoding encoding) // IN
   : mUstr(),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
   // If the input is NULL, then there's nothing to do.
   if (UNLIKELY(s == NULL)) {
      return;
   }

   mUstr = auto_unique(Unicode_Alloc(s, encoding), free).get();
   ASSERT(Validate(mUstr));
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::string(const Glib::ustring& s) // IN
   : mUstr(s),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
   ASSERT(Validate(s));
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::string --
 *
 *      Copy constructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::string(const string& s) // IN
   : mUstr(s.mUstr),
     mUtf16Cache(NULL),
     mUtf16Length(npos)
{
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::~string --
 *
 *      Destructor.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

string::~string()
{
   InvalidateCache();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator Glib::ustring --
 *
 *      Implicit conversion to Glib::ustring operator
 *
 * Results:
 *      The internal Glib::ustring object.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

string::operator const Glib::ustring& ()
   const
{
   return ustr();
}


#ifdef _WIN32

/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator ubstr_t --
 *
 *      Implicit conversion to ubstr_t
 *
 * Results:
 *      The current ubstr_t string. NUL-terminated.
 *
 * Side effects:
 *      None
 *
 * Note:
 *      This function is only defined in _WIN32
 *
 *-----------------------------------------------------------------------------
 */

string::operator const ubstr_t()
   const
{
   return ubstr_t(GetUtf16Cache());
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::t_str --
 *
 *      Get the TCHAR representation of this string.
 *
 * Results:
 *      The TCHAR representation.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

const TCHAR*
string::t_str() const
{
#ifdef UNICODE
   return w_str();
#else
   return c_str();
#endif
}

#endif


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator= --
 *
 *      Assignment operator.
 *
 * Results:
 *      A reference to this string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string&
string::operator=(string copy) // IN
{
   swap(copy);
   return *this;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator+= --
 *
 *      Append operator of the utf::string class.
 *
 * Results:
 *      A reference to this string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string&
string::operator+=(const string &s) // IN
{
   return append(s);
}


string&
string::operator+=(value_type uc) // IN
{
   push_back(uc);
   return *this;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::swap --
 *
 *      Swaps the contents with a given utf::string.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
string::swap(string& s) // IN/OUT
{
   using std::swap;
   mUstr.swap(s.mUstr);
   swap(mUtf16Cache, s.mUtf16Cache);
   swap(mUtf16Length, s.mUtf16Length);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::resize --
 *
 *      Change the size of this utf::string.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
string::resize(size_type n,  // IN
               value_type c) // IN/OPT
{
   InvalidateCache();
   mUstr.resize(n, c);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::reserve --
 *
 *      Change the amount of memory allocated for the utf::string.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
string::reserve(size_type n) // IN/OPT
{
   mUstr.reserve(n);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::c_str --
 *
 *      Get the UTF-8 representation of this string.
 *
 * Results:
 *      The current string with UTF-8 encoding. NUL-terminated.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

const char *
string::c_str()
   const
{
   return mUstr.c_str();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::w_str --
 *
 *      Get the UTF-16 representation of this string.
 *
 * Results:
 *      The current string with UTF-16 (host-endian) encoding. NUL-terminated.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

const utf16_t *
string::w_str()
   const
{
   return GetUtf16Cache();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::ustr --
 *
 *      Get the Glib::ustring backing of this string.
 *
 * Results:
 *      The internal Glib::ustring object.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

const Glib::ustring&
string::ustr()
   const
{
   return mUstr;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::empty --
 *
 *      Test if this is an empty string.
 *
 * Results:
 *      true if it's an empty string, otherwise false.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::empty()
   const
{
   return mUstr.empty();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::size --
 *
 * Results:
 *      Returns the length of this string, in characters (code points),
 *      excluding NUL.
 *      If length in bytes is wanted, please refer to bytes() method.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::size()
   const
{
   return mUstr.size();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::w_size --
 *
 * Results:
 *      Returns the length of this string, in UTF-16 code units,
 *      excluding NUL.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::w_size()
   const
{
   if (mUtf16Length == npos) {
      mUtf16Length = Unicode_UTF16Strlen(GetUtf16Cache());
   }

   return mUtf16Length;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::length --
 *
 * Results:
 *      Returns the length of this string, in characters (code points),
 *      excluding NUL. (Same as size().)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::length()
   const
{
   return size();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::bytes --
 *
 * Results:
 *      Returns the number of bytes used by the UTF-8 representation of this
 *      string, excluding NUL.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::bytes()
   const
{
   return mUstr.bytes();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::foldCase --
 *
 *      Returns the case-folded string of this string.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::foldCase()
   const
{
   return string(mUstr.casefold());
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::trim --
 *
 *      Returns the whitespace-trimmed version of this string.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::trim()
   const
{
   return CopyAndFree(Unicode_Trim(c_str()), free);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::trimLeft --
 *
 *      Get the left-trimmed version of this string.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::trimLeft()
   const
{
   return CopyAndFree(Unicode_TrimLeft(c_str()), free);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::trimRight --
 *
 *      Get the right-trimmed version of this string.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::trimRight()
   const
{
   return CopyAndFree(Unicode_TrimRight(c_str()), free);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::normalize --
 *
 *      Creates a new string by normalizing the input string.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::normalize(NormalizeMode mode) // IN
   const
{
   return mUstr.normalize((Glib::NormalizeMode)mode);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::toLower --
 *
 *      Creates a new string by lower-casing the input string using
 *      the rules of the specified locale.  If no locale is specified,
 *      uses the process's default locale.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::toLower(const char *locale) // IN/OPT
   const
{
#ifdef USE_ICU
   return CopyAndFree(Unicode_ToLower(c_str(), locale), free);
#else
   return mUstr.lowercase();
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::toUpper --
 *
 *      Creates a new string by upper-casing the input string using
 *      the rules of the specified locale.  If no locale is specified,
 *      uses the process's default locale.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::toUpper(const char *locale) // IN/OPT
   const
{
#ifdef USE_ICU
   return CopyAndFree(Unicode_ToUpper(c_str(), locale), free);
#else
   return mUstr.uppercase();
#endif
}


#ifdef USE_ICU
/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::toTitle --
 *
 *      Creates a new string by title-casing the input string using
 *      the rules of the specified locale.  If no locale is specified,
 *      uses the process's default locale.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::toTitle(const char *locale) // IN/OPT
   const
{
   return CopyAndFree(Unicode_ToTitle(c_str(), locale), free);
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::append --
 *
 *      Appends the argument string to this utf::string.
 *
 * Results:
 *      A reference to this object.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string&
string::append(const string &s) // IN
{
   InvalidateCache();
   mUstr.append(s.mUstr);

   return *this;
}


string&
string::append(const string &s, // IN
               size_type i,     // IN
               size_type n)     // IN
{
   InvalidateCache();
   mUstr.append(s.mUstr, i, n);

   return *this;
}


string&
string::append(const char *s,   // IN
               size_type n)     // IN
{
   InvalidateCache();
   mUstr.append(s, n);

   return *this;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::push_back --
 *
 *      Appends the character at the end of this string.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
string::push_back(value_type uc) // IN
{
   InvalidateCache();
   mUstr.push_back(uc);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::assign --
 *
 *      Assigns the passed in string to this string.
 *
 *      Callers should prefer using operator= instead of assign().
 *
 * Results:
 *      A reference to this object
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string&
string::assign(const string &s) // IN
{
   return operator=(s);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::insert --
 *
 *      Inserts the argument string to this string at index i, return this
 *      string.
 *
 *      These are passthrough calls to the Glib::insert calls.
 *
 * Results:
 *      A reference to this object
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string&
string::insert(size_type i,      // IN
               const string& s)  // IN
{
   InvalidateCache();
   mUstr.insert(i, s.mUstr);
   return *this;
}


string&
string::insert(size_type i,      // IN
               size_type n,      // IN
               value_type uc)
{
   InvalidateCache();
   mUstr.insert(i, n, uc);
   return *this;
}


string&
string::insert(iterator p,    // IN
               value_type uc) // IN
{
   InvalidateCache();
   mUstr.insert(p, uc);
   return *this;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::clear --
 *
 *      Clears this string.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
string::clear()
{
   InvalidateCache();
   mUstr.clear();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::erase --
 *
 *      Erase the contents of this string in the specified index range.
 *
 * Results:
 *      A reference to this object
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string&
string::erase(size_type i,    // IN
              size_type n)    // IN
{
   InvalidateCache();
   mUstr.erase(i, n);
   return *this;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::erase --
 *
 *      Erase the contents of this string with given iterator.
 *
 * Results:
 *      The current iterator.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::iterator
string::erase(iterator p)    // IN
{
   InvalidateCache();
   return mUstr.erase(p);
}


string::iterator
string::erase(iterator pbegin,    // IN
              iterator pend)      // IN
{
   InvalidateCache();
   return mUstr.erase(pbegin, pend);
}

/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::replace --
 *
 *      Replace the string contents specified by the range, with the passed in
 *      string.
 *
 * Results:
 *      A reference to this object.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string&
string::replace(size_type i,     // IN
                size_type n,     // IN
                const string& s) // IN
{
   InvalidateCache();
   mUstr.replace(i, n, s.mUstr);
   return *this;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::replace --
 *
 *      Mutates this string by replacing all occurrences of one string with
 *      another.
 *
 *      Does nothing if the `from` string is empty.
 *
 * Results:
 *      A reference to this object.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string&
string::replace(const string& from, // IN
                const string& to)   // IN
{
   if (from.empty()) {
      return *this;
   }

   size_type end;
   size_type start = 0;
   size_type fromSize = from.length();
   string result;
   result.reserve(bytes() * to.bytes() / from.bytes());

   while ((end = find(from, start)) != string::npos) {
      result += substr(start, end - start);
      result += to;

      start = end + fromSize;
   }

   if (start < length()) {
      result += substr(start);
   }

   result.reserve(0);

   swap(result);
   return *this;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::replace_copy --
 *
 * Results:
 *      Returns a new string with all occurrences of one string replaced by
 *      another.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::replace_copy(const string& from, // IN
                     const string& to)   // IN
   const
{
   return string(*this).replace(from, to);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::compare --
 *
 *      A 3-way (output -1, 0, or 1) string comparison. Compares each Unicode
 *      code point of this string to the argument string.
 *
 * Results:
 *      -1 if *this < s, 0 if *this == s, 1 if *this > s.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
string::compare(const string &s, // IN
                bool ignoreCase) // IN/OPT: false by default
   const
{
   return ignoreCase
          ? Unicode_CompareIgnoreCase(c_str(), s.c_str())
          : Unicode_Compare(c_str(), s.c_str());
}


int
string::compare(size_type i,     // IN
                size_type n,     // IN
                const string &s) // IN
   const
{
   return mUstr.compare(i, n, s.mUstr);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::compareLength --
 *
 *      A 3-way (output -1, 0, or 1) string comparison with given length.
 *      Compares only the first len characters (in code units) of the strings.
 *
 * Results:
 *      -1 if *this < s, 0 if *this == s, 1 if *this > s.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
string::compareLength(const string &s, // IN
                      size_type len,   // IN: length in code-point
                      bool ignoreCase) // IN/OPT: false by default
   const
{
   return substr(0, len).compare(s.substr(0, len), ignoreCase);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::compareRange --
 *
 *      A 3-way (output -1, 0, or 1) string comparison with given length.
 *      Compares the substrings from this string [thisStart ~ thisStart + thisLength-1]
 *      with the input string str [strStart ~ strStart + strLength - 1].
 *
 * Results:
 *      -1 if *this < s, 0 if *this == s, 1 if *this > s.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
string::compareRange(size_type thisStart,  // IN: index in code-point
                     size_type thisLength, // IN: length in code-point
                     const string &str,    // IN
                     size_type strStart,   // IN: index in code-point
                     size_type strLength,  // IN: length in code-point
                     bool ignoreCase)      // IN/OPT: false by default
   const
{
   return substr(thisStart, thisLength).compare(str.substr(strStart, strLength), ignoreCase);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::find --
 *
 *      Searches for the first occurrence of the input string inside this string.
 *
 * Results:
 *      If s is found, then, it returns the first starting index of the input string.
 *      Otherwise, returns npos.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::find(const string &s,   // IN
             size_type pos)     // IN/OPT
   const
{
   return mUstr.find(s.mUstr, pos);
}


string::size_type
string::find(value_type uc, // IN
             size_type pos) // IN/OPT
   const
{
   return mUstr.find(uc, pos);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::rfind --
 *
 *      Searches for the last occurrence of the input string inside this string.
 *
 * Results:
 *      If s is found, then, it returns the last starting index of the input string.
 *      Otherwise, returns npos.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::rfind(const string &s,   // IN
              size_type pos)     // IN/OPT
   const
{
   return mUstr.rfind(s.mUstr, pos);
}


string::size_type
string::rfind(value_type uc,     // IN
              size_type pos)     // IN/OPT
   const
{
   return mUstr.rfind(uc, pos);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::find_first_of --
 *
 *      Find the first occurrence of 's' in this string.  'i' determines where in
 *      the current string we start searching for 's'
 *
 * Results:
 *      If s is found, then, it returns the index where s occurs in this
 *      string.
 *      Otherwise, returns npos.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::find_first_of(const string &s, // IN
                      size_type i)     // IN/OPT
   const
{
   return mUstr.find_first_of(s.mUstr, i);
}


string::size_type
string::find_first_of(value_type uc,   // IN
                      size_type i)     // IN/OPT
   const
{
   return mUstr.find_first_of(uc, i);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::find_first_not_of --
 *
 *      Find the first occurrence of a string NOT in 's' in this string.  'i'
 *      determines where in this string we start searching to NOT 's'.
 *
 * Results:
 *      Returns the index of the first sequence in this string that is not 's'
 *      Otherwise, returns npos.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::find_first_not_of(const string &s, // IN
                          size_type i)     // IN/OPT
   const
{
   return mUstr.find_first_not_of(s.mUstr, i);
}


string::size_type
string::find_first_not_of(value_type uc,   // IN
                          size_type i)     // IN/OPT
   const
{
   return mUstr.find_first_not_of(uc, i);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::find_last_of --
 *
 *      Does a reverse search in this string for 's'.  'i' determines where we
 *      start the search for in this string.
 *
 * Results:
 *      If s is found, then, it returns the index where s occurs in this
 *      string.
 *      Otherwise, returns npos.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::find_last_of(const string &s, // IN
                     size_type i)     // IN/OPT
   const
{
   return mUstr.find_last_of(s.mUstr, i);
}


string::size_type
string::find_last_of(value_type uc,   // IN
                     size_type i)     // IN/OPT
   const
{
   return mUstr.find_last_of(uc, i);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::find_last_not_of --
 *
 *      Searches for the last character within the current string that does
 *      not match any characters in 's'.  'i' determines where we start the
 *      search for in this string.  (moving backwards).
 *
 * Results:
 *      If NOT 's' is found, then, it returns the index where s does not occurs
 *      in this string.
 *      Otherwise, returns npos.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::size_type
string::find_last_not_of(const string &s, // IN
                         size_type i)     // IN/OPT
   const
{
   return mUstr.find_last_not_of(s.mUstr, i);
}


string::size_type
string::find_last_not_of(value_type uc,   // IN
                         size_type i)     // IN/OPT
   const
{
   return mUstr.find_last_not_of(uc, i);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::substr --
 *
 *      Create a substring of this string with given range.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::substr(size_type start, // IN
               size_type len)   // IN: The substring length in code points.
   const
{
   return string(mUstr.substr(start, len));
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator[] --
 *
 *      Get the UTF-32 character at given index in this string.
 *
 * Results:
 *      UTF-32 character (gunichar).
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::value_type
string::operator[](size_type i)  // IN
   const
{
   return mUstr[i];
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::startsWith --
 *
 *      Tests if the current string starts with 's'
 *
 * Results:
 *      true if current string starts with 's', false otherwise
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::startsWith(const string &s, // IN
                   bool ignoreCase) // IN/OPT: false by default
   const
{
   return UnicodeStartsWith(c_str(), s.c_str(), ignoreCase);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::endsWith --
 *
 *      Tests if the current string ends with 's'
 *
 * Results:
 *      true if current string ends with 's', false otherwise
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::endsWith(const string &s, // IN
                 bool ignoreCase) // IN/OPT: false by default
   const
{
   return UnicodeEndsWith(c_str(), s.c_str(), ignoreCase);
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::split --
 *
 *      Return a vector of utf::strings.  The vector contains the elements of
 *      the string split by the passed in separator. Empty tokens are not
 *      skipped. If maxStrings is zero, any number of strings will be returned,
 *      otherwise parsing stops after maxStrings - 1 matches of the separator.
 *      In that case, the last string returned includes the rest of the
 *      original string.
 *
 *      "1,2,3".split(",") -> ["1", "2", "3"]
 *      "1,,".split(",") -> ["1", "", ""]
 *      "1".split(",") -> ["1"]
 *      "1,2,3".split(",", 2) -> ["1", "2,3"]
 *
 *      XXX If this is to be used for things like command line parsing, support
 *      for quoted strings needs to be added.
 *
 * Results:
 *      A vector of utf::strings
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

std::vector<string>
string::split(const string &sep, // IN
              size_t maxStrings) // IN/OPT
   const
{
   std::vector<string> splitStrings;
   size_type sIndex = 0;
   size_type sepLen = sep.length();
   size_t count = 0;

   ASSERT(sepLen > 0);

   while (true) {
      size_type index = find(sep, sIndex);
      count++;
      if (count == maxStrings || index == npos) {
         splitStrings.push_back(substr(sIndex));
         break;
      }

      splitStrings.push_back(substr(sIndex, index - sIndex));
      sIndex = index + sepLen;
   }

   return splitStrings;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::GetUtf16Cache --
 *
 *      Return the UTF-16 representation of the current string, this value is
 *      cached, in the object.  If the cache is not valid (NULL), then create
 *      the cache value
 *
 * Results:
 *      A UTF-16 representation of the current string
 *
 * Side effects:
 *      Allocates a UTF16 string
 *
 *-----------------------------------------------------------------------------
 */

const utf16_t *
string::GetUtf16Cache()
   const
{
   if (mUtf16Cache == NULL) {
      mUtf16Cache = Unicode_GetAllocUTF16(c_str());
   }

   return mUtf16Cache;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::InvalidateCache --
 *
 *      Frees the cache in this string.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
string::InvalidateCache()
{
   free(mUtf16Cache);
   mUtf16Cache = NULL;
   mUtf16Length = npos;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator+ --
 *
 *      Create a new string by appending the input string to this string.
 *
 *      NOTE: This is not the same as append.  append() will modify the
 *      current object, while this will return a new object.
 *
 * Results:
 *      The newly created string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
string::operator+(const string &rhs)     // IN
   const
{
   return mUstr + rhs.mUstr;
}


string
string::operator+(value_type uc)        // IN
   const
{
   return mUstr + uc;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator== --
 *
 *      Equality operator for string objects
 *
 * Results:
 *      true or false (true if equal)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::operator==(const string &rhs)     // IN
   const
{
   return compare(rhs) == 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator!= --
 *
 *      Inequality operator for string objects
 *
 * Results:
 *      true or false (true if not equal)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::operator!=(const string &rhs)     // IN
   const
{
   return compare(rhs) != 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator< --
 *
 *      Less than operator for string objects
 *
 * Results:
 *      true or false (true if lhs is < rhs)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::operator<(const string &rhs)     // IN
   const
{
   return compare(rhs) < 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator> --
 *
 *      Greater than operator for string objects
 *
 * Results:
 *      true or false (true if lhs is > rhs)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::operator>(const string &rhs)     // IN
   const
{
   return compare(rhs) > 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator<= --
 *
 *      Less than or equal than operator for string objects
 *
 * Results:
 *      true or false (true if lhs is <= rhs)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::operator<=(const string &rhs)     // IN
   const
{
   return compare(rhs) <= 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::operator>= --
 *
 *      Greater than or equal than operator for string objects
 *
 * Results:
 *      true or false (true if lhs is >= rhs)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
string::operator>=(const string &rhs)     // IN
   const
{
   return compare(rhs) >= 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::begin --
 *
 *      Returns an iterator to the start of the string.
 *
 * Results:
 *      iterator
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::iterator
string::begin()
{
   return mUstr.begin();
}


string::const_iterator
string::begin()
   const
{
   return mUstr.begin();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::string::end --
 *
 *      Returns an iterator to the end of the string.
 *
 * Results:
 *      iterator
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string::iterator
string::end()
{
   return mUstr.end();
}


string::const_iterator
string::end()
   const
{
   return mUstr.end();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::Validate --
 *
 *      Validates the string.
 *
 * Results:
 *      true if the string contains is valid UTF-8, false otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

bool
Validate(const Glib::ustring& s) // IN
{
   bool isValid = Unicode_IsBufferValid(s.c_str(), s.bytes(),
                                        STRING_ENCODING_UTF8);
   if (!isValid) {
      char *escaped = Unicode_EscapeBuffer(s.c_str(), s.bytes(),
                                           STRING_ENCODING_UTF8);
      Warning("Invalid UTF-8 string: \"%s\"\n", escaped);
      free(escaped);
   }
   return isValid;
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::CreateWithLength --
 *
 *      A wrapper function for Unicode_AllocWithLength() that returns a utf::string.
 *
 * Results:
 *      A utf::string created with given parameters.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
CreateWithLength(const void *buffer,      // IN
                 ssize_t lengthInBytes,   // IN: NUL not included
                 StringEncoding encoding) // IN
{
   if (!Unicode_IsBufferValid(buffer, lengthInBytes, encoding)) {
      throw ConversionError();
   }

   return CopyAndFree(Unicode_AllocWithLength(buffer, lengthInBytes, encoding),
                      free);
}


/*
 *----------------------------------------------------------------------
 *
 * utf::CreateWithBOMBuffer --
 *
 *       Convert a text buffer with BOM (byte-order mark) to utf::string.
 *       If BOM not present, assume it's UTF-8.
 *
 * Results:
 *       A utf::string containing the text buffer.
 *
 * Side Effect:
 *       None.
 *
 *----------------------------------------------------------------------
 */

string
CreateWithBOMBuffer(const void *buffer,      // IN
                    ssize_t lengthInBytes)   // IN: NUL not included
{
   struct BOMMap {
      uint8 bom[4];              // BOM with max size.
      ssize_t len;               // Length of BOM.
      StringEncoding encoding;   // Encoding if a BOM is present.
   };

   static const BOMMap mapBOM[] = {
      {{0}, 0,                      STRING_ENCODING_UTF8     }, // Default encoding.
      {{0xEF, 0xBB, 0xBF}, 3,       STRING_ENCODING_UTF8     },
      {{0xFE, 0xFF}, 2,             STRING_ENCODING_UTF16_BE },
      {{0xFF, 0xFE}, 2,             STRING_ENCODING_UTF16_LE },
      {{0x00, 0x00, 0xFE, 0xFF}, 4, STRING_ENCODING_UTF32_BE },
      {{0xFF, 0xFE, 0x00, 0x00}, 4, STRING_ENCODING_UTF32_LE }
   };

   ASSERT(lengthInBytes >= 0);
   unsigned int index = 0; // Default encoding, no need to check.
   for (unsigned int i = 1; i < ARRAYSIZE(mapBOM); i++) {
      if (   lengthInBytes >= mapBOM[i].len
          && memcmp(mapBOM[i].bom, buffer, mapBOM[i].len) == 0) {
         index = i;
         break;
      }
   }

   return CreateWithLength(reinterpret_cast<const char*>(buffer) + mapBOM[index].len,
                           lengthInBytes - mapBOM[index].len,
                           mapBOM[index].encoding);
}


/*
 *----------------------------------------------------------------------
 *
 * utf::CopyAndFree --
 *
 *       Creates a utf::string from an allocated UTF-8 C string,
 *       automatically freeing it afterward.
 *
 * Results:
 *       A copy of the string.
 *
 * Side Effect:
 *       None.
 *
 *----------------------------------------------------------------------
 */

string
CopyAndFree(char* utf8,              // IN
            void (*freeFunc)(void*)) // IN/OPT
{
   ASSERT(utf8 != NULL);
   return auto_unique(utf8, freeFunc).get();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::IntToStr --
 *
 *      Converts an integer to a utf::string.
 *
 * Results:
 *      A utf::string created with the given integer.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

string
IntToStr(int64 val) // IN
{
   std::ostringstream ostream;
   ostream << val;
   return ostream.str().c_str();
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::CopyArray --
 *
 *      Copies an array to a vector.
 *      Guaranteed to not shrink the vector.
 *
 * Results:
 *      A vector containing a shallow copy of the array.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

template<typename T>
static void
CopyArray(const T *p,          // IN:
          size_t n,            // IN: The number of array elements to copy.
          std::vector<T>& buf) // OUT:
{
   if (n > buf.size()) {
      buf.resize(n);
   }

   if (!buf.empty()) {
      ASSERT(p != NULL);
      memcpy(&buf[0], p, n * sizeof buf[0]);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * utf::CreateWritableBuffer --
 *
 *      Copies a utf::string to a writable buffer.
 *      Guaranteed to never shrink the size of the destination buffer.
 *
 * Results:
 *      A std::vector containing the NUL-terminated string data.
 *      The size of the resulting vector may exceed the length of the
 *      NUL-terminated string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
CreateWritableBuffer(const string& s,        // IN:
                     std::vector<char>& buf) // OUT: A copy of the string, as UTF-8.
{
   CopyArray(s.c_str(), s.bytes() + 1, buf);
}


void
CreateWritableBuffer(const string& s,           // IN:
                     std::vector<utf16_t>& buf) // OUT: A copy of the string, as UTF-16.
{
   CopyArray(s.w_str(), s.w_size() + 1, buf);
}


} // namespace utf
   07070100000356000081A40000000000000000000000016822550500002C16000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/stringxx/string.hh  /*********************************************************
 * Copyright (C) 2008-2019,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * string.hh --
 *
 *     A string wrapper for bora/lib/unicode. This class is intended to provide
 *     more c++ features such as operator overloading, automatic string conversion
 *     between different types of string classes.
 *
 *     This class uses glib::ustring as the underlying storage for its data,
 *     we chose this object because of its internal support for unicode.
 *
 */

#ifndef UTF_STRING_HH
#define UTF_STRING_HH

#include <string>
#include <vector>
#include <algorithm>

#ifdef _WIN32
#pragma pack(push, 8)
#endif // _WIN32

/*
 * Include glib.h here as a work-around - hopefully temporary - for a
 * compilation error that would otherwise occur as a result of the
 * inclusion of <glibmm/ustring.h> on the next line.  The compilation
 * error that would otherwise occur is the result of:
 *  (1) nested includes in <glibmm/ustring.h> ultimately include a
 *      glib-related header file other than glib.h; but
 *  (2) including a glib header file other than glib.h outside
 *      of glib code is not allowed with the latest glib.
 *
 * Although including glib.h here does not actually fix the underlying
 * problem, it does turn off the complaint.  It's believed (hoped?) that
 * an upgrade of glibmm will fix the issue properly and eliminate the
 * need to include <glib.h> here.
 */
#include <glib.h>
#include <giommconfig.h> // For GIOMM_*_VERSION macros
#include <glibmm/ustring.h>

#ifdef _WIN32
#pragma pack(pop)
#endif // _WIN32

#ifdef _WIN32
#include "ubstr_t.hh"
#endif

#include <libExport.hh>

#include <unicodeTypes.h>

#ifdef _WIN32
/*
 * Disabling Windows warning 4251
 *    This is a warning msg about requiring Glib::ustring to be DLL
 *    exportable.
 */
#pragma warning(push)
#pragma warning(disable:4251)
#endif

namespace utf {


/* utf8string should be replaced with an opaque type. It is temporarily used
 * to replace the std::string in our codebase.
 */
typedef std::string utf8string;
typedef std::basic_string<utf16_t> utf16string;

class VMSTRING_EXPORT string
{
public:
   // type definitions
   typedef Glib::ustring::size_type size_type;
   typedef Glib::ustring::value_type value_type;
   typedef Glib::ustring::iterator iterator;
   typedef Glib::ustring::const_iterator const_iterator;

   // constant definitions
   static const size_type npos;

   // Normalize mode map to Glib::NormalizeMode
#if GIOMM_MAJOR_VERSION >= 2 && GIOMM_MINOR_VERSION >= 68
   typedef enum {
      NORMALIZE_DEFAULT          = (int)Glib::NormalizeMode::DEFAULT,
      NORMALIZE_NFD              = (int)Glib::NormalizeMode::NFD,
      NORMALIZE_DEFAULT_COMPOSE  = (int)Glib::NormalizeMode::DEFAULT_COMPOSE,
      NORMALIZE_NFC              = (int)Glib::NormalizeMode::NFC,
      NORMALIZE_ALL              = (int)Glib::NormalizeMode::ALL,
      NORMALIZE_NFKD             = (int)Glib::NormalizeMode::NFKD,
      NORMALIZE_ALL_COMPOSE      = (int)Glib::NormalizeMode::ALL_COMPOSE,
      NORMALIZE_NFKC             = (int)Glib::NormalizeMode::NFKC
   } NormalizeMode;
#else
   typedef enum {
      NORMALIZE_DEFAULT          = Glib::NORMALIZE_DEFAULT,
      NORMALIZE_NFD              = Glib::NORMALIZE_NFD,
      NORMALIZE_DEFAULT_COMPOSE  = Glib::NORMALIZE_DEFAULT_COMPOSE,
      NORMALIZE_NFC              = Glib::NORMALIZE_NFC,
      NORMALIZE_ALL              = Glib::NORMALIZE_ALL,
      NORMALIZE_NFKD             = Glib::NORMALIZE_NFKD,
      NORMALIZE_ALL_COMPOSE      = Glib::NORMALIZE_ALL_COMPOSE,
      NORMALIZE_NFKC             = Glib::NORMALIZE_NFKC
   } NormalizeMode;
#endif

   string();
   string(const char *s);

#ifdef _WIN32
   string(const ubstr_t &s);
   explicit string(const _bstr_t &s);
#endif

   string(const utf16string &s);
   string(const utf16_t *s);
   string(const char *s, StringEncoding encoding);
   string(const Glib::ustring &s);
   string(const string &s);

   ~string();

   // Implicit conversions
   operator const Glib::ustring& () const;
#ifdef _WIN32
   operator const ubstr_t() const;
#endif

   // Conversions to other i18n types (utf8, utf16, unicode)
   const char *c_str() const;
   const utf16_t *w_str() const;
   const Glib::ustring& ustr() const;
#ifdef _WIN32
   const TCHAR* t_str() const;
#endif

   // Mapping functions to Glib::ustring
   void swap(string &s);
   void resize(size_type n, value_type c = '\0');
   void reserve(size_type n = 0);
   bool empty() const;
   size_type size() const;
   size_type w_size() const;
   size_type length() const;
   size_type bytes() const;
   string foldCase() const;
   string trim() const;
   string trimLeft() const;
   string trimRight() const;
   string normalize(NormalizeMode mode = NORMALIZE_DEFAULT_COMPOSE) const;

   string toLower(const char *locale = NULL) const;
   string toUpper(const char *locale = NULL) const;
#ifdef USE_ICU
   string toTitle(const char *locale = NULL) const;
#endif

   // String-level member methods.
   string& append(const string &s);
   string& append(const string &s, size_type i, size_type n);
   string& append(const char *s, size_type n);
   string& assign(const string &s);
   void push_back(value_type uc);
   void clear();
   string& insert(size_type i, const string& s);
   string& insert(size_type i, size_type n, value_type uc);
   string& insert(iterator p, value_type uc);
   string& erase(size_type i, size_type n = npos);
   iterator erase(iterator p);
   iterator erase(iterator pbegin, iterator pend);
   string& replace(size_type i, size_type n, const string& s);
   string& replace(const string& from, const string& to);
   string replace_copy(const string& from, const string& to) const;

   int compare(const string &s, bool ignoreCase = false) const;
   int compare(size_type i, size_type n, const string &s) const;
   int compareLength(const string &s, size_type len, bool ignoreCase = false) const;
   int compareRange(size_type thisStart, size_type thisLength, const string &str,
                    size_type strStart, size_type strLength,
                    bool ignoreCase = false) const;
   size_type find(const string &s, size_type pos = 0) const;
   size_type rfind(const string &s, size_type pos = npos) const;
   size_type find_first_of(const string &s, size_type i = 0) const;
   size_type find_first_not_of(const string &s, size_type i = 0) const;
   size_type find_last_of(const string &s, size_type i = npos) const;
   size_type find_last_not_of(const string &s, size_type i = npos) const;
   string substr(size_type start = 0, size_type len = npos) const;

   // Character-level member methods.
   value_type operator[](size_type i) const;
   size_type find(value_type uc, size_type pos = 0) const;
   size_type rfind(value_type uc, size_type pos = npos) const;
   size_type find_first_of(value_type uc, size_type i = 0) const;
   size_type find_first_not_of(value_type uc, size_type i = 0) const;
   size_type find_last_of(value_type uc, size_type i = npos) const;
   size_type find_last_not_of(value_type uc, size_type i = npos) const;

   // Sequence accessor.
   iterator begin();
   iterator end();
   const_iterator begin() const;
   const_iterator end() const;

   // Operator overloads
   string& operator=(string copy);
   string& operator+=(const string &s);
   string& operator+=(value_type uc);

   // Some helper functions that are nice to have
   bool startsWith(const string &s, bool ignoreCase = false) const;
   bool endsWith(const string &s, bool ignoreCase = false) const;
   std::vector<string> split(const string &sep, size_t maxStrings = 0) const;

   // Overloaded operators
   string operator+(const string &rhs) const;
   string operator+(value_type uc) const;
   bool operator==(const string &rhs) const;
   bool operator!=(const string &rhs) const;
   bool operator<(const string &rhs) const;
   bool operator>(const string &rhs) const;
   bool operator<=(const string &rhs) const;
   bool operator>=(const string &rhs) const;

private:
   // Cache operations
   void InvalidateCache();

   // Cache accessors
   const utf16_t *GetUtf16Cache() const;

   // utf::string is internally backed by Glib::ustring.
   Glib::ustring mUstr;

   // Cached representations.
   mutable utf16_t *mUtf16Cache;
   mutable size_type mUtf16Length;

   /*
    * All added members need to be initialized in all constructors and need
    * to be handled in swap().
    */
};

// Helper operators

string inline
operator+(const char *lhs, const string &rhs) {
   return string(lhs) + rhs;
}

string inline
operator+(const string& lhs, const char *rhs) {
   return lhs + string(rhs);
}

bool inline
operator==(const char *lhs, const string &rhs) {
   return string(lhs) == rhs;
}

bool inline
operator!=(const char *lhs, const string &rhs) {
   return string(lhs) != rhs;
}

bool inline
operator<(const char *lhs, const string &rhs) {
   return string(lhs) < rhs;
}

bool inline
operator>(const char *lhs, const string &rhs) {
   return string(lhs) > rhs;
}

bool inline
operator<=(const char *lhs, const string &rhs) {
   return string(lhs) <= rhs;
}

bool inline
operator>=(const char *lhs, const string &rhs) {
   return string(lhs) >= rhs;
}

// This lets a utf::string appear on the right side of a stream insertion operator.
inline std::ostream&
operator<<(std::ostream& strm, const string& s)
{
   strm << s.c_str();
   return strm;
}

inline std::wostream&
operator<<(std::wostream& strm, const string& s)
{
   strm << s.w_str();
   return strm;
}

// ConversionError class for exception

class ConversionError {};


// Helper functions

VMSTRING_EXPORT bool Validate(const Glib::ustring& s);

VMSTRING_EXPORT string
CreateWithLength(const void *buffer, ssize_t lengthInBytes, StringEncoding encoding);

VMSTRING_EXPORT string
CreateWithBOMBuffer(const void *buffer, ssize_t lengthInBytes);

VMSTRING_EXPORT string CopyAndFree(char* utf8, void (*freeFunc)(void*) = free);

VMSTRING_EXPORT string IntToStr(int64 val);

VMSTRING_EXPORT void CreateWritableBuffer(const string& s,
                                          std::vector<char>& buf);
VMSTRING_EXPORT void CreateWritableBuffer(const string& s,
                                          std::vector<utf16_t>& buf);


} // namespace utf



// Template specializations for utf::string.
namespace std {

template<>
inline void
swap<utf::string>(utf::string& s1, // IN/OUT
                  utf::string& s2) // IN/OUT
{
   s1.swap(s2);
}

} // namespace std


#ifdef _WIN32
#pragma warning(pop)
#endif

#endif
  07070100000357000081A400000000000000000000000168225505000049F5000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/stringxx/ubstr_t.hh /*********************************************************
 * Copyright (c) 2008-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * ubstr_t.hh --
 *
 *     A string wrapper for _bstr_t.  _bstr_t assumes all char* strings use
 *     the local MBCS encoding, but we want to require that char* strings be
 *     interpreted as UTF-8.
 */

#ifndef _WIN32
#error This file should be built on Windows only.
#endif

#pragma once

#include <algorithm>
#include <comutil.h>
#include <memory>

#include "autoCPtr.hh"

#include <unicode.h>
#include <util.h>


#ifdef I_LOVE_BSTR_T_CLASSIC

typedef _bstr_t ubstr_t;
typedef _variant_t uvariant_t;

#else

class ubstr_t
{
public:
   ubstr_t();
   ubstr_t(const char *s);
   ubstr_t(const wchar_t *s);
   ubstr_t(const _variant_t& var);
   explicit ubstr_t(const _bstr_t& bstr);
   ubstr_t(BSTR bstr, bool copy);
   ubstr_t(const ubstr_t& s);

   ubstr_t& operator=(ubstr_t copy);
   ubstr_t& operator=(const char *s);
   ubstr_t& operator=(const wchar_t *s);
   ubstr_t& operator=(const _variant_t& var);

   // Wrappers around standard _bstr_t methods.
   void Assign(BSTR s);
   BSTR copy(bool copy = true) const;

   void Attach(BSTR s);
   BSTR Detach();

   BSTR *GetAddress();
   BSTR& GetBSTR();

   unsigned int length() const;

   ubstr_t& operator+=(const ubstr_t& s);
   ubstr_t operator+(const ubstr_t& s) const;
   ubstr_t operator+(const char *s) const;
   ubstr_t operator+(const wchar_t *s) const;

   bool operator!() const;
   bool operator==(const ubstr_t& s) const;
   bool operator!=(const ubstr_t& s) const;
   bool operator<(const ubstr_t& s) const;
   bool operator>(const ubstr_t& s) const;
   bool operator<=(const ubstr_t& s) const;
   bool operator>=(const ubstr_t& s) const;

   operator const wchar_t*() const;
   operator wchar_t*() const; // XXX: Get rid of this.
   operator const char*() const;
#ifndef I_HATE_NON_CONST_BSTR_T_CASTS
   operator char*() const; // XXX: Get rid of this.
#endif

   /*
    * _variant_t is implicitly constructible from a _bstr_t.  Since we can't
    * add a constructor to _variant_t, instead provide an implicit conversion
    * from ubstr_t to _variant_t.
    */
   operator _variant_t() const;

   void swap(ubstr_t& s);

private:
   class UTF8Data
   {
   public:
      // Takes ownership of the input string.
      UTF8Data(char *utf8String = NULL) // IN/OUT: May be NULL
         : mUTF8String(utf8String)
      {
      }

      // Takes ownership of the input string.
      void Set(char *utf8String) // IN/OUT: May be NULL.
      {
         if (mUTF8String == utf8String) {
            return;
         }
         free(mUTF8String);
         mUTF8String = utf8String;
      }

      char *Get()
      {
         return mUTF8String;
      }

      ~UTF8Data()
      {
         free(mUTF8String);
      }

   private:
      char *mUTF8String;

   private:
      // Intentionally unimplemented.
      UTF8Data(const UTF8Data&);
      UTF8Data& operator=(const UTF8Data&);
   };

   char *GetUTF8Cache() const;
   void InvalidateCache();

   /*
    * Anything that mutates mBstr (all non-const methods) must call
    * InvalidateCache().
    */
   _bstr_t mBstr;

   // mUTF8 is allocated and initialized lazily.
   mutable std::shared_ptr<UTF8Data> mUTF8;
};


/*
 * _variant_t does string conversions too, so we also need to wrap that.
 * Since _variant_t doesn't keep a cached version of the locally-encoded
 * MBCS string and since we don't use _variant_t nearly as much as _bstr_t,
 * substitutability isn't as much of a concern, so we can use inheritance.
 */
class uvariant_t
   : public _variant_t
{
public:
   /*
    * Wrappers around standard _variant_t constructors.  Unfortunately we
    * have to wrap all the constructors since they aren't inherited.
    */
   uvariant_t() : _variant_t() { }
   uvariant_t(const VARIANT& var) : _variant_t(var) { }
   uvariant_t(const VARIANT *var) : _variant_t(var) { }
   uvariant_t(const _variant_t& var) : _variant_t(var) { }
   uvariant_t(VARIANT& var, bool copy) : _variant_t(var, copy) { }
   uvariant_t(short i, VARTYPE vt = VT_I2) : _variant_t(i, vt) { }
   uvariant_t(long i, VARTYPE vt = VT_I4) : _variant_t(i, vt) { }
   uvariant_t(float f) : _variant_t(f) { }
   uvariant_t(double d, VARTYPE vt = VT_R8) : _variant_t(d, vt) { }
   uvariant_t(const CY& cy) : _variant_t(cy) { }
   uvariant_t(const _bstr_t& s) : _variant_t(s) { }
   uvariant_t(const wchar_t *s) : _variant_t(s) { }
   uvariant_t(IDispatch *dispatch, bool addRef = true) : _variant_t(dispatch, addRef) { }
   uvariant_t(bool b) : _variant_t(b) { }
   uvariant_t(IUnknown *unknown, bool addRef = true) : _variant_t(unknown, addRef) { }
   uvariant_t(const DECIMAL& dec) : _variant_t(dec) { }
   uvariant_t(BYTE i) : _variant_t(i) { }
   uvariant_t(char c) : _variant_t(c) { }
   uvariant_t(unsigned short i) : _variant_t(i) { }
   uvariant_t(unsigned long i) : _variant_t(i) { }
   uvariant_t(int i) : _variant_t(i) { }
   uvariant_t(unsigned int i) : _variant_t(i) { }
#if _WIN32_WINNT >= 0x0501
   uvariant_t(__int64 i) : _variant_t(i) { }
   uvariant_t(unsigned __int64 i) : _variant_t(i) { }
#endif

   // Override the _variant_t constructor that assumes locally-encoded strings.
   uvariant_t(const char *s) : _variant_t(ubstr_t(s)) { }

   // Provide conversion from our ubstr_t wrapper.
   uvariant_t(const ubstr_t& s) : _variant_t(s) { }
};


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::ubstr_t --
 *
 *      ubstr_t constructors.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline
ubstr_t::ubstr_t()
   : mBstr(),
     mUTF8()
{
}


inline
ubstr_t::ubstr_t(const char *s) // IN: A UTF-8-encoded string.
   : mBstr(),
     mUTF8()
{
   if (s != NULL) {
      // Since we already have the UTF-8 version of the string, cache it now.
      mUTF8 = std::shared_ptr<UTF8Data>(new UTF8Data(Util_SafeStrdup(s)));
      mBstr = auto_unique(Unicode_GetAllocUTF16(s), free).get();
   }
}


inline
ubstr_t::ubstr_t(const wchar_t *s) // IN
   : mBstr(s),
     mUTF8()
{
}


inline
ubstr_t::ubstr_t(const _variant_t& var) // IN
   : mBstr(var),
     mUTF8()
{
}


inline
ubstr_t::ubstr_t(const _bstr_t& bstr) // IN
   : mBstr(bstr),
     mUTF8()
{
}


inline
ubstr_t::ubstr_t(BSTR bstr, // IN
                 bool copy) // IN
   : mBstr(bstr, copy),
     mUTF8()
{
}


inline
ubstr_t::ubstr_t(const ubstr_t& s) // IN
   : mBstr(s.mBstr),
     mUTF8(s.mUTF8)
{
   if (static_cast<wchar_t *>(mBstr) != NULL && !mUTF8) {
      mUTF8 = s.mUTF8 = std::shared_ptr<UTF8Data>(new UTF8Data());
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::operator= --
 *
 *      ubstr_t assignment operators.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline ubstr_t&
ubstr_t::operator=(ubstr_t copy) // IN
{
   swap(copy);
   return *this;
}


inline ubstr_t&
ubstr_t::operator=(const char *s) // IN: A UTF-8-encoded string.
{
   return operator=(ubstr_t(s));
}


inline ubstr_t&
ubstr_t::operator=(const wchar_t *s) // IN
{
   return operator=(ubstr_t(s));
}


inline ubstr_t&
ubstr_t::operator=(const _variant_t& var) // IN
{
   return operator=(ubstr_t(var));
}



/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::Assign --
 *
 *      Wrapper around _bstr_t::Assign.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline void
ubstr_t::Assign(BSTR s) // IN
{
   InvalidateCache();
   mBstr.Assign(s);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::copy --
 *
 *      Wrapper around _bstr_t::copy.
 *
 * Results:
 *      A copy of the underlying BSTR.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline BSTR
ubstr_t::copy(bool copy) // IN/OPT
   const
{
   return mBstr.copy(copy);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::Attach --
 *
 *      Wrapper around _bstr_t::Attach.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Invalidates the UTF-8 cache.
 *
 *-----------------------------------------------------------------------------
 */

inline void
ubstr_t::Attach(BSTR s) // IN
{
   InvalidateCache();
   mBstr.Attach(s);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::Detach --
 *
 *      Wrapper around _bstr_t::Detach.
 *
 * Results:
 *      The underlying BSTR, which is no longer managed.
 *
 * Side effects:
 *      Invalidates the UTF-8 cache.
 *
 *-----------------------------------------------------------------------------
 */

inline BSTR
ubstr_t::Detach()
{
   InvalidateCache();
   return mBstr.Detach();
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::GetAddress --
 *
 *      Wrapper around _bstr_t::GetAddress.
 *
 * Results:
 *      A pointer to the underlying BSTR.
 *
 * Side effects:
 *      Invalidates the UTF-8 cache.
 *
 *-----------------------------------------------------------------------------
 */

inline BSTR *
ubstr_t::GetAddress()
{
   /*
    * We don't know if the underlying BSTR will be modified via the returned
    * pointer.  We can only assume it will.
    */
   InvalidateCache();
   return mBstr.GetAddress();
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::GetBSTR --
 *
 *      Wrapper around _bstr_t::GetBSTR.
 *
 * Results:
 *      A reference to the underlying BSTR.
 *
 * Side effects:
 *      Invalidates the UTF-8 cache.
 *
 *-----------------------------------------------------------------------------
 */

inline BSTR&
ubstr_t::GetBSTR()
{
   /*
    * We don't know if the underlying BSTR will be modified via the returned
    * reference.  We can only assume it will.
    */
   InvalidateCache();
   return mBstr.GetBSTR();
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::length --
 *
 *      Wrapper around _bstr_t::length.
 *
 * Results:
 *      The length of the string, in TCHARs.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline unsigned int
ubstr_t::length()
   const
{
   return mBstr.length();
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::operator+= --
 *
 *      Mutating concatenation operator.
 *
 * Results:
 *      A reference to this ubstr_t object.
 *
 * Side effects:
 *      Invalidates the UTF-8 cache.
 *
 *-----------------------------------------------------------------------------
 */

inline ubstr_t&
ubstr_t::operator+=(const ubstr_t& s) // IN
{
   InvalidateCache();
   mBstr += s.mBstr;
   return *this;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::operator+ --
 *
 *      Concatenation operators.
 *
 * Results:
 *      A copy of the concatenated string.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline ubstr_t
ubstr_t::operator+(const ubstr_t& s) // IN
   const
{
   ubstr_t copy(*this);
   copy += s;
   return copy;
}


inline ubstr_t
ubstr_t::operator+(const char *s) // IN
   const
{
   return operator+(ubstr_t(s));
}


inline ubstr_t
ubstr_t::operator+(const wchar_t *s) // IN
   const
{
   return operator+(ubstr_t(s));
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::operator! --
 *
 *      Wrapper around _bstr_t::operator!.
 *
 * Results:
 *      true if the underlying _bstr_t is NULL, false otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline bool
ubstr_t::operator!()
   const
{
   return !mBstr;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::operator== --
 * ubstr_t::operator!= --
 * ubstr_t::operator< --
 * ubstr_t::operator> --
 * ubstr_t::operator<= --
 * ubstr_t::operator>= --
 *
 *      Wrappers around _bstr_t's comparison operators.
 *
 * Results:
 *      true if the comparisons are hold, false otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline bool
ubstr_t::operator==(const ubstr_t& s) // IN
   const
{
   return mBstr == s.mBstr;
}


inline bool
ubstr_t::operator!=(const ubstr_t& s) // IN
   const
{
   return mBstr != s.mBstr;
}


inline bool
ubstr_t::operator<(const ubstr_t& s) // IN
   const
{
   return mBstr < s.mBstr;
}


inline bool
ubstr_t::operator>(const ubstr_t& s) // IN
   const
{
   return mBstr > s.mBstr;
}


inline bool
ubstr_t::operator<=(const ubstr_t& s) // IN
   const
{
   return mBstr <= s.mBstr;
}


inline bool
ubstr_t::operator>=(const ubstr_t& s) // IN
   const
{
   return mBstr >= s.mBstr;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::operator const wchar_t* --
 * ubstr_t::operator wchar_t* --
 *
 *      Wrappers around _bstr_t's cast operators.
 *
 * Results:
 *      A pointer to the underlying UTF-16 string.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline
ubstr_t::operator const wchar_t*()
   const
{
   return static_cast<const wchar_t *>(mBstr);
}


inline
ubstr_t::operator wchar_t*()
   const
{
   return static_cast<wchar_t *>(mBstr);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::operator const char_t* --
 * ubstr_t::operator char_t* --
 *
 *      Cast operators to UTF-8 strings.
 *
 * Results:
 *      A pointer to a UTF-8 string.
 *
 * Side effects:
 *      Might initializes the UTF-8 cache.
 *
 *-----------------------------------------------------------------------------
 */

inline
ubstr_t::operator const char*()
   const
{
   return GetUTF8Cache();
}


#ifndef I_HATE_NON_CONST_BSTR_T_CASTS
inline
ubstr_t::operator char*()
   const
{
   return GetUTF8Cache();
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::operator _variant_t --
 *
 *      Cast operator to _variant_t.
 *
 * Results:
 *      A _variant_t equivalent to this ubstr_t object.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline
ubstr_t::operator _variant_t()
   const
{
   return static_cast<const wchar_t *>(mBstr);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::swap --
 *
 *      Swaps this ubstr_t object with another.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline void
ubstr_t::swap(ubstr_t& s) // IN/OUT
{
   std::swap(mBstr, s.mBstr);
   mUTF8.swap(s.mUTF8);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::GetUTF8Cache --
 *
 *      Retrieves the UTF-8 cache, initializing it if necessary.
 *
 * Results:
 *      The cached UTF-8 string.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline char *
ubstr_t::GetUTF8Cache()
   const
{
   if (static_cast<wchar_t *>(mBstr) == NULL) {
      // Nothing to do.
      return NULL;
   }

   if (!mUTF8) {
      mUTF8 = std::shared_ptr<UTF8Data>(new UTF8Data());
   }

   if (mUTF8->Get() == NULL) {
      auto utf8Str = auto_unique(
         Unicode_AllocWithUTF16(static_cast<wchar_t *>(mBstr)),
         free);
      mUTF8->Set(utf8Str.get());
      utf8Str.release();
   }

   return mUTF8->Get();
}


/*
 *-----------------------------------------------------------------------------
 *
 * ubstr_t::InvalidateCache --
 *
 *      Clears the UTF-8 cache.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline void
ubstr_t::InvalidateCache()
{
   mUTF8.reset();
}


/*
 *-----------------------------------------------------------------------------
 *
 * operator+ --
 *
 *      Non-member concatenation operators.
 *
 * Results:
 *      A copy of the concatenated string.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

inline ubstr_t
operator+(const char *s1,    // IN: A UTF-8-encoded sting.
          const ubstr_t& s2) // IN
{
   return ubstr_t(s1) + s2;
}


inline ubstr_t
operator+(const wchar_t *s1, // IN
          const ubstr_t& s2) // IN
{
   return ubstr_t(s1) + s2;
}


/*
 * These are not part of the _bstr_t interface but are provided to catch
 * misuse.  They are intentionally unimplemented to avoid a maintenance
 * burden, and they intentionally return nothing so that accidental usage
 * normally results in compile-time errors instead of link-time ones.
 */

void operator==(const char *s1, const ubstr_t& s2);
void operator!=(const char *s1, const ubstr_t& s2);
void operator<(const char *s1, const ubstr_t& s2);
void operator>(const char *s1, const ubstr_t& s2);
void operator<=(const char *s1, const ubstr_t& s2);
void operator>=(const char *s1, const ubstr_t& s2);

void operator==(const WCHAR *s1, const ubstr_t& s2);
void operator!=(const WCHAR *s1, const ubstr_t& s2);
void operator<(const WCHAR *s1, const ubstr_t& s2);
void operator>(const WCHAR *s1, const ubstr_t& s2);
void operator<=(const WCHAR *s1, const ubstr_t& s2);
void operator>=(const WCHAR *s1, const ubstr_t& s2);


#endif // I_LOVE_BSTR_T_CLASSIC
   07070100000358000081A400000000000000000000000168225505000029CE000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/vmCopyPasteDnDWrapper.cpp   /*********************************************************
 * Copyright (C) 2018-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmCopyPasteDnDWrapper.cpp
 *
 * The inherited implementation of common class CopyPasteDnDWrapper in VM side.
 */

#define G_LOG_DOMAIN "dndcp"

#include "guestDnDCPMgr.hh"
#include "vmCopyPasteDnDWrapper.h"
#include "vmware.h"

extern "C" {
   #include "rpcout.h"
   #include "vmware/guestrpc/tclodefs.h"
   #include "vmware/tools/plugin.h"
   #include "vmware/tools/utils.h"
   #include <string.h>
}


/**
 *
 * Timer callback for reset. Handle it by calling the member function
 * that handles reset.
 *
 * @param[in] clientData pointer to the VMCopyPasteDnDWrapper instance that
 * issued the timer.
 *
 * @return FALSE always.
 */

static gboolean
DnDPluginResetSent(void *clientData)
{
   VMCopyPasteDnDWrapper *p = reinterpret_cast<VMCopyPasteDnDWrapper *>(clientData);

   g_debug("%s: enter\n", __FUNCTION__);
   ASSERT(p);
   p->OnResetInternal();
   p->RemoveDnDPluginResetTimer();
   return FALSE;
}


/**
 *
 * Create an instance for class VMCopyPasteDnDWrapper.
 *
 * @return a pointer to the VMCopyPasteDnDWrapper instance.
 */

VMCopyPasteDnDWrapper *
VMCopyPasteDnDWrapper::CreateInstance(void)
{
   return new VMCopyPasteDnDWrapper();
}


/**
 *
 * Initialize the wrapper by instantiating the platform specific impl
 * class. Effectively, this function is a factory that produces a
 * platform implementation of the DnD/Copy Paste UI layer.
 *
 * @param[in] ctx tools app context.
 */

void
VMCopyPasteDnDWrapper::Init(ToolsAppCtx *ctx)
{
   m_ctx = ctx;
   CopyPasteDnDWrapper::Init(ctx);
}


/**
 *
 * Add the DnD plugin reset timer.
 */

void
VMCopyPasteDnDWrapper::AddDnDPluginResetTimer(void)
{
   g_debug("%s: enter\n", __FUNCTION__);

   ASSERT(m_resetTimer == NULL);

   m_resetTimer = VMTools_CreateTimer(RPC_POLL_TIME * 30);
   if (m_resetTimer) {
      VMTOOLSAPP_ATTACH_SOURCE(m_ctx, m_resetTimer,
                               DnDPluginResetSent, this, NULL);
   }
}


/**
 *
 * Remove the DnD plugin reset timer.
 */

void
VMCopyPasteDnDWrapper::RemoveDnDPluginResetTimer(void)
{
   g_debug("%s: enter\n", __FUNCTION__);
   if (m_resetTimer) {
      g_source_destroy(m_resetTimer);
      g_source_unref(m_resetTimer);
      m_resetTimer = NULL;
   }
}


/**
 *
 * Handle cap reg. This is cross-platform so handle here instead of the
 * platform implementation.
 */

void
VMCopyPasteDnDWrapper::OnCapReg(gboolean set)
{
   ToolsAppCtx *ctx;

   g_debug("%s: enter\n", __FUNCTION__);

   ctx = GetToolsAppCtx();
   if (ctx) {
      char *reply = NULL;
      size_t replyLen;
      const char *toolsDnDVersion = TOOLS_DND_VERSION_4;
      char *toolsCopyPasteVersion = NULL;
      int version;

      /*
       * First DnD.
       */
      if (!RpcChannel_Send(ctx->rpc, toolsDnDVersion, strlen(toolsDnDVersion),
                           NULL, NULL)) {
         g_debug("%s: could not set guest dnd version capability\n",
               __FUNCTION__);
         version = 1;
         SetDnDVersion(version);
      } else {
         char const *vmxDnDVersion = QUERY_VMX_DND_VERSION;

         if (!RpcChannel_Send(ctx->rpc, vmxDnDVersion,
                              strlen(vmxDnDVersion), &reply, &replyLen)) {
            g_debug("%s: could not get VMX dnd version capability, assuming v1\n",
                  __FUNCTION__);
            version = 1;
            SetDnDVersion(version);
         } else {
            int version = atoi(reply);
            ASSERT(version >= 1);
            SetDnDVersion(version);
            g_debug("%s: VMX is dnd version %d\n", __FUNCTION__, GetDnDVersion());
            if (version == 3) {
               /*
                * VMDB still has version 4 in it, which will cause a V3
                * host to fail. So, change to version 3. Since we don't
                * support any other version, we only do this for V3.
                */
               toolsDnDVersion = TOOLS_DND_VERSION_3;
               if (!RpcChannel_Send(ctx->rpc, toolsDnDVersion,
                                    strlen(toolsDnDVersion), NULL, NULL)) {

                  g_debug("%s: could not set VMX dnd version capability, assuming v1\n",
                           __FUNCTION__);
                  version = 1;
                  SetDnDVersion(version);
               }
            }
         }
         vm_free(reply);
         reply = NULL;
       }

      /*
       * Now CopyPaste.
       */

      toolsCopyPasteVersion = g_strdup_printf(TOOLS_COPYPASTE_VERSION" %d", 4);
      if (!RpcChannel_Send(ctx->rpc, toolsCopyPasteVersion,
                           strlen(toolsCopyPasteVersion),
                           NULL, NULL)) {
         g_debug("%s: could not set guest copypaste version capability\n",
               __FUNCTION__);
         version = 1;
         SetCPVersion(version);
      } else {
         char const *vmxCopyPasteVersion = QUERY_VMX_COPYPASTE_VERSION;

         if (!RpcChannel_Send(ctx->rpc, vmxCopyPasteVersion,
                              strlen(vmxCopyPasteVersion), &reply, &replyLen)) {
            g_debug("%s: could not get VMX copypaste version capability, assuming v1\n",
                  __FUNCTION__);
            version = 1;
            SetCPVersion(version);
         } else {
            version = atoi(reply);
            ASSERT(version >= 1);
            SetCPVersion(version);
            g_debug("%s: VMX is copypaste version %d\n", __FUNCTION__,
                    GetCPVersion());
            if (version == 3) {
               /*
                * VMDB still has version 4 in it, which will cause a V3
                * host to fail. So, change to version 3. Since we don't
                * support any other version, we only do this for V3.
                */
               g_free(toolsCopyPasteVersion);
               toolsCopyPasteVersion = g_strdup_printf(TOOLS_COPYPASTE_VERSION" %d", 3);
               if (!RpcChannel_Send(ctx->rpc, toolsCopyPasteVersion,
                                    strlen(toolsCopyPasteVersion), NULL, NULL)) {

                  g_debug("%s: could not set VMX copypaste version, assuming v1\n",
                           __FUNCTION__);
                  version = 1;
                  SetCPVersion(version);
               }
            }
         }
         vm_free(reply);
      }
      g_free(toolsCopyPasteVersion);
   }
}


/**
 *
 * Get the version of the copy paste protocol being wrapped.
 *
 * @return copy paste protocol version.
 */

int
VMCopyPasteDnDWrapper::GetCPVersion()
{
   g_debug("%s: enter\n", __FUNCTION__);
   if (IsCPRegistered()) {
      char *reply = NULL;
      size_t replyLen;

      ToolsAppCtx *ctx = GetToolsAppCtx();
      if (!RpcChannel_Send(ctx->rpc, QUERY_VMX_COPYPASTE_VERSION,
         strlen(QUERY_VMX_COPYPASTE_VERSION), &reply, &replyLen)) {
            g_debug("%s: could not get VMX copyPaste "
                    "version capability: %s\n", __FUNCTION__,
                    reply ? reply : "NULL");
            m_cpVersion = 1;
      } else {
         m_cpVersion = atoi(reply);
      }
      free(reply);
   }
   g_debug("%s: got version %d\n", __FUNCTION__, m_cpVersion);
   return m_cpVersion;
}


/**
 *
 * Get the version of the DnD protocol being wrapped.
 *
 * @return DnD protocol version.
 */

int
VMCopyPasteDnDWrapper::GetDnDVersion()
{
   g_debug("%s: enter\n", __FUNCTION__);
   if (IsDnDRegistered()) {
      char *reply = NULL;
      size_t replyLen;

      ToolsAppCtx *ctx = GetToolsAppCtx();
      if (!RpcChannel_Send(ctx->rpc, QUERY_VMX_DND_VERSION,
         strlen(QUERY_VMX_DND_VERSION), &reply, &replyLen)) {
            g_debug("%s: could not get VMX dnd "
                    "version capability: %s\n", __FUNCTION__,
                    reply ? reply : "NULL");
            m_dndVersion = 1;
      } else {
         m_dndVersion = atoi(reply);
      }
      free(reply);
   }
   g_debug("%s: got version %d\n", __FUNCTION__, m_dndVersion);
   return m_dndVersion;
}


/**
 *
 * Handle reset.
 */

void
VMCopyPasteDnDWrapper::OnResetInternal()
{
   g_debug("%s: enter\n", __FUNCTION__);

   /*
    * Reset DnD/Copy/Paste only if vmx said we can. The reason is that
    * we may also get reset request from vmx when user is taking snapshot
    * or recording. If there is an ongoing DnD/copy/paste, we should not
    * reset here. For details please refer to bug 375928.
    */
   char *reply = NULL;
   size_t replyLen;
   ToolsAppCtx *ctx = GetToolsAppCtx();
   if (RpcChannel_Send(ctx->rpc, "dnd.is.active",
                       strlen("dnd.is.active"), &reply, &replyLen) &&
       (1 == atoi(reply))) {
      g_debug("%s: ignore reset while file transfer is busy.\n", __FUNCTION__);
      goto exit;
   }

   if (IsDnDRegistered()) {
      UnregisterDnD();
   }
   if (IsCPRegistered()) {
      UnregisterCP();
   }
   if (IsCPEnabled() && !IsCPRegistered()) {
      RegisterCP();
   }
   if (IsDnDEnabled() && !IsDnDRegistered()) {
      RegisterDnD();
   }
   if (!IsDnDRegistered() || !IsCPRegistered()) {
      g_debug("%s: unable to reset fully DnD %d CP %d!\n",
              __FUNCTION__, IsDnDRegistered(), IsCPRegistered());
   }

exit:
   free(reply);
}


/**
 *
 * Handle SetOption.
 *
 * @param[in] option option name
 * @param[in] option option value
 * @return TRUE on success, FALSE on failure
 */

gboolean
VMCopyPasteDnDWrapper::OnSetOption(const char *option,
                                   const char *value)
{
   gboolean ret = false;
   bool bEnable;

   ASSERT(option);
   ASSERT(value);

   bEnable = strcmp(value, "1") ? false : true;
   g_debug("%s: setting option '%s' to '%s'\n", __FUNCTION__, option, value);
   if (strcmp(option, TOOLSOPTION_ENABLEDND) == 0) {
      SetDnDIsEnabled(bEnable);
      ret = true;
   } else if (strcmp(option, TOOLSOPTION_COPYPASTE) == 0) {
      SetCPIsEnabled(bEnable);
      ret = true;
   }

   return ret;
}
  07070100000359000081A400000000000000000000000168225505000007BF000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/vmCopyPasteDnDWrapper.h /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmCopyPasteDnDWrapper.h
 *
 * The inherited implementation of common class CopyPasteDnDWrapper in VM side.
 */

#ifndef __VM_COPYPASTEDNDWRAPPER_H__
#define __VM_COPYPASTEDNDWRAPPER_H__

#include "copyPasteDnDWrapper.h"

class VMCopyPasteDnDWrapper
   : public CopyPasteDnDWrapper
{
public:
   ~VMCopyPasteDnDWrapper() { }

   static VMCopyPasteDnDWrapper *CreateInstance(void);
   virtual void Init(ToolsAppCtx *ctx);
   virtual ToolsAppCtx *GetToolsAppCtx() { return m_ctx; }
   virtual void OnCapReg(gboolean set);
   virtual int GetCPVersion();
   virtual int GetDnDVersion();
   virtual void OnResetInternal();
   virtual gboolean OnSetOption(const char *option, const char *value);
   void RemoveDnDPluginResetTimer(void);

protected:
   void AddDnDPluginResetTimer(void);

private:
   VMCopyPasteDnDWrapper() : m_ctx(NULL), m_resetTimer(NULL) { }
   VMCopyPasteDnDWrapper(const VMCopyPasteDnDWrapper &wrapper);
   VMCopyPasteDnDWrapper& operator=(const VMCopyPasteDnDWrapper &wrapper);

private:
   ToolsAppCtx *m_ctx;
   GSource *m_resetTimer;
};

#endif // __VM_COPYPASTEDNDWRAPPER_H__
 0707010000035A000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/xutils  0707010000035B000081A4000000000000000000000001682255050000B808000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/xutils/xutils.cc    /*********************************************************
 * Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


#include <cairomm/cairomm.h>
#include <gdkmm.h>

#include "xutils/xutils.hh"

#if GTK_MAJOR_VERSION == 3
#include <gdkmm/devicemanager.h>
#endif

/* These must be after the gtkmm includes, as gtkmm is quite picky. */
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <gdk/gdkx.h>
#include <cstring>

#include "vm_assert.h"


namespace xutils {


/* Actual definitions of the signals in this class. */
sigc::signal<void, Glib::RefPtr<Gdk::Screen> > currentDesktopChanged;
sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopLayoutChanged;
sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopGeometryChanged;
sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopViewportChanged;
sigc::signal<void, Glib::RefPtr<Gdk::Screen> > windowStackChanged;
sigc::signal<void, Glib::RefPtr<Gdk::Screen> > windowManagerChanged;
sigc::signal<void, Glib::RefPtr<Gdk::Screen> > activeWindowChanged;
sigc::signal<void, Glib::RefPtr<Gdk::Screen> > workAreaChanged;

/* Necessary for calculating per-monitor _NET_WORKAREA in GetMonitorWorkArea() */
struct NETWMStrutPartial {
   NETWMStrutPartial()
      : left_width(0),
        left_start(0),
        left_end(0),
        right_width(0),
        right_start(0),
        right_end(0),
        top_height(0),
        top_start(0),
        top_end(0),
        bottom_height(0),
        bottom_start(0),
        bottom_end(0) {}

   int left_width, left_start, left_end;
   int right_width, right_start, right_end;
   int top_height, top_start, top_end;
   int bottom_height, bottom_start, bottom_end;
};


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::OnWindowFilter --
 *
 *      Window filter handler that listens for changes to the properties we
 *      care about and emits the appropriate signals.
 *
 * Results:
 *      GDK_FILTER_CONTINUE, always.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

GdkFilterReturn
OnWindowFilter(XEvent* xevent,     // IN: Incoming event
               GdkEvent* event,    // OUT/UNUSED
               GdkScreen* _screen) // IN: The screen
{
   Glib::RefPtr<Gdk::Screen> screen = Glib::wrap(_screen, true);
   ::Display* xdisplay = xevent->xany.display;
   GdkDisplay* display = gdk_x11_lookup_xdisplay(xdisplay);
   Window rootWin = GDK_WINDOW_XID(screen->get_root_window()->gobj());

#define ATOM(name) gdk_x11_get_xatom_by_name_for_display(display, (name))

   if (xevent->type == PropertyNotify &&
       xevent->xproperty.window == rootWin) {
      if (xevent->xproperty.atom == ATOM("_NET_CLIENT_LIST_STACKING")) {
         windowStackChanged.emit(screen);
      } else if (xevent->xproperty.atom == ATOM("_NET_DESKTOP_LAYOUT")) {
         desktopLayoutChanged.emit(screen);
      } else if (xevent->xproperty.atom == ATOM("_NET_NUMBER_OF_DESKTOPS")) {
         desktopLayoutChanged.emit(screen);
      } else if (xevent->xproperty.atom == ATOM("_NET_CURRENT_DESKTOP")) {
         currentDesktopChanged.emit(screen);
      } else if (xevent->xproperty.atom == ATOM("_NET_DESKTOP_GEOMETRY")) {
         desktopGeometryChanged.emit(screen);
      } else if (xevent->xproperty.atom == ATOM("_NET_DESKTOP_VIEWPORT")) {
         desktopViewportChanged.emit(screen);
      } else if (xevent->xproperty.atom == ATOM("_NET_SUPPORTING_WM_CHECK")) {
         windowManagerChanged.emit(screen);
      } else if (xevent->xproperty.atom == ATOM("_NET_ACTIVE_WINDOW")) {
         activeWindowChanged.emit(screen);
      } else if (xevent->xproperty.atom == ATOM("_NET_WORKAREA")) {
         workAreaChanged.emit(screen);
      }
   }

#undef ATOM

   return GDK_FILTER_CONTINUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::Init --
 *
 *      Base initialization function that sets up the window filter. This
 *      is required if any signals are to be used.
 *
 *      This can be called more than once.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Init()
{
   static bool initialized = false;

   if (!initialized) {
      initialized = true;

      /*
       * Select PropertyChange events on the root window so that we can
       * listen for when the host window stack changes and update our
       * copy.
       */
      Glib::RefPtr<Gdk::Display> display = Gdk::Display::get_default();
      ::Display* xdisplay = GDK_DISPLAY_XDISPLAY(display->gobj());

      for (int i = 0; i < display->get_n_screens(); i++) {
         Glib::RefPtr<Gdk::Screen> screen = display->get_screen(i);
         Glib::RefPtr<Gdk::Window> rootWin = screen->get_root_window();
         Window xRootWin = GDK_WINDOW_XID(rootWin->gobj());

         int mask = PropertyChangeMask;

#if GTK_MAJOR_VERSION == 3
         if (gdk_x11_window_lookup_for_display(
                display->gobj(), xRootWin) != NULL) {
#else
         if (gdk_xid_table_lookup(xRootWin) != NULL) {
#endif
            /* Make sure we don't interfere with GDK. */
            XWindowAttributes attrs;
            XGetWindowAttributes(xdisplay, xRootWin, &attrs);
            mask |= attrs.your_event_mask;
         }

         XSelectInput(xdisplay, xRootWin, mask);

         gdk_window_add_filter(rootWin->gobj(), (GdkFilterFunc)OnWindowFilter,
                               screen->gobj());
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetCardinal --
 *
 *      Utility function to get a cardinal from a window property.
 *
 * Results:
 *      true if the function succeeded, along with a value for retValue.
 *      Otherwise false.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool
GetCardinal(Glib::RefPtr<const Gdk::Window> window, // IN: Window
            const utf::string& atomName,            // IN: Atom name
            unsigned long& retValue)                        // OUT: Return value
{
   ASSERT(window);
   ASSERT(!atomName.empty());

   std::vector<unsigned long> retValues;
   bool result = GetCardinalList(window, atomName, retValues);

   if (result && retValues.size() == 1) {
      retValue = retValues[0];
      return true;
   }

   return false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetCardinalList --
 *
 *      Utility function to get a cardinal list from a window property.
 *
 * Results:
 *      true if the function succeeded, along with return values for retValue.
 *      Otherwise false.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool
GetCardinalList(Glib::RefPtr<const Gdk::Window> window, // IN: Window
                const utf::string& atomName,            // IN: Atom name
                std::vector<unsigned long>& retValues)  // IN: Return values
{
   ASSERT(window);
   ASSERT(window->get_display());
   ASSERT(!atomName.empty());

   GdkDisplay* display = const_cast<GdkDisplay*>(window->get_display()->gobj());
   GdkWindow* gdkwin = const_cast<GdkWindow*>(window->gobj());

   Atom atom = gdk_x11_get_xatom_by_name_for_display(display, atomName.c_str());

   Atom type;
   int format;
   unsigned long nitems;
   unsigned long bytes_after;
   uint8* values;

   gdk_error_trap_push();
   int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
                                GDK_WINDOW_XID(gdkwin),
                                atom, 0, G_MAXLONG, False, XA_CARDINAL, &type,
                                &format, &nitems, &bytes_after, &values);
   int err = gdk_error_trap_pop();

   if (ret == Success && !err) {
      if (type == XA_CARDINAL && nitems > 0) {
         retValues.resize(nitems);

         if (format == 8) {
            for (unsigned long i = 0; i < nitems; i++) {
               retValues[i] = values[i];
            }
         } else if (format == 16) {
            uint16* shortValues = (uint16*)values;
            for (unsigned long i = 0; i < nitems; i++) {
               retValues[i] = shortValues[i];
            }
         } else if (format == 32) {
            unsigned long* longValues = (unsigned long*)values;
            for (unsigned long i = 0; i < nitems; i++) {
               retValues[i] = longValues[i];
            }
         } else {
            NOT_IMPLEMENTED();
         }

         XFree(values);
         return true;
      }

      XFree(values);
   }

   return false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::CheckDockPanel --
 *
 *      Utility function to check dock or panel window.
 *
 * Results:
 *      true if the dock or panel window found.
 *      Otherwise false.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool
CheckDockPanel(Glib::RefPtr<const Gdk::Window> window)
{
   ASSERT(window);
   ASSERT(window->get_display());

   GdkDisplay* display = const_cast<GdkDisplay*>(window->get_display()->gobj());
   GdkWindow* gdkwin = const_cast<GdkWindow*>(window->gobj());

   Atom atom = gdk_x11_get_xatom_by_name_for_display(display, "_NET_WM_WINDOW_TYPE");
   Atom type;
   int format;
   unsigned long nitems;
   unsigned long bytes_after;
   uint8* values;

   gdk_error_trap_push();
   int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
                                GDK_WINDOW_XID(gdkwin),
                                atom, 0, G_MAXLONG, False, AnyPropertyType, &type,
                                &format, &nitems, &bytes_after, &values);
   int err = gdk_error_trap_pop();
   if (err) {
      Log("Ignore xerror in XGetWindowProperty. Error code %d", err);
      return false;
   }
   if (ret == Success && type == XA_ATOM
       && format == 32 && values != NULL && nitems > 0) {
      unsigned long* stack = (unsigned long*)values;
      if (stack[0] == XInternAtom(GDK_DISPLAY_XDISPLAY(display),
                                  "_NET_WM_WINDOW_TYPE_DOCK", false)) {
         Log("found dock window: %ld.\n",GDK_WINDOW_XID(gdkwin));
         XFree(values);
         return true;
      }
   }
   XFree(values);
   return false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::SetDesktopForWindow --
 *
 *      Sets the virtual desktop that a window is on. This takes care of
 *      the workspace part of the desktop. Viewports must be handled
 *      separately by moving the window.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
SetDesktopForWindow(Glib::RefPtr<Gdk::Window> window, // IN:
                    uint32 desktop)                   // IN:
{
   GdkScreen* screen = window->get_screen()->gobj();
   Atom tempDesktop = desktop; // Cast for 64-bit correctness.
   Window win = GDK_WINDOW_XID(window->gobj());
   Display* display = GDK_WINDOW_XDISPLAY(window->gobj());

   Atom atom = gdk_x11_get_xatom_by_name_for_display(
      window->get_display()->gobj(), "_NET_WM_DESKTOP");

   gdk_error_trap_push();
   XChangeProperty(display, win, atom,
                   XA_CARDINAL, 32, PropModeReplace,
                   (unsigned char*)&tempDesktop, 1);
   gdk_flush();
   int err = gdk_error_trap_pop();

   if (err) {
      Warning("Unable to move host window (XID %d) to desktop %d\n",
          (int)GDK_WINDOW_XID(window->gobj()), desktop);
   }

   XEvent ev;
   ev.xclient.type = ClientMessage;
   ev.xclient.serial = 0;
   ev.xclient.send_event = True;
   ev.xclient.window = win;
   ev.xclient.message_type = atom;
   ev.xclient.format = 32;
   ev.xclient.data.l[0] = desktop;
   ev.xclient.data.l[1] = 2; // source (2 gives full control)
   ev.xclient.data.l[2] = 0; // unused
   ev.xclient.data.l[3] = 0; // unused
   ev.xclient.data.l[4] = 0; // unused

   gdk_error_trap_push();
   XSendEvent(display,
              GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
              False, SubstructureRedirectMask | SubstructureNotifyMask,
              &ev);
   gdk_flush();
   err = gdk_error_trap_pop();

   if (err) {
      Warning("Unable to move host window (XID %d) to desktop %d\n",
          (int)GDK_WINDOW_XID(window->gobj()), desktop);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::SetFullscreenMonitorsHint --
 *
 *      Sets the _NET_WM_FULLSCREEN_MONITORS hint for the passed in window and
 *      monitor indices.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
SetFullscreenMonitorsHint(Glib::RefPtr<Gdk::Window> window, // IN:
                          std::vector<long> monitors)       // IN:
{
   // monitors contains 4 monitor indices, per EWMH spec
   ASSERT(monitors.size() == 4);

   Display* display = GDK_WINDOW_XDISPLAY(window->gobj());

   XClientMessageEvent xclient;
   memset(&xclient, 0, sizeof xclient);
   xclient.type = ClientMessage;
   xclient.window = GDK_WINDOW_XID(window->gobj());
   xclient.message_type = XInternAtom(display,
                                      "_NET_WM_FULLSCREEN_MONITORS",
                                      False);
   xclient.format = 32;

   for (int i = 0; i < 4; i++) {
      xclient.data.l[i] = monitors[i];
   }

   xclient.data.l[4] = 1;

   XSendEvent(display,
              GDK_WINDOW_XID(gdk_get_default_root_window()),
              False,
              SubstructureRedirectMask | SubstructureNotifyMask,
              (XEvent *) &xclient);

   XSync(display, False);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetDesktopForWindow --
 *
 *      Retrieve the virtual desktop that a given window is shown on.
 *
 * Results:
 *      The index of the virtual desktop.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

uint32
GetDesktopForWindow(Glib::RefPtr<const Gdk::Window> window) // IN:
{
   unsigned long result = 0;
   GetCardinal(window, "_NET_WM_DESKTOP", result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetNumDesktops --
 *
 *      Returns the number of virtual desktops.
 *
 * Results:
 *      The number of virtual desktops.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

uint32
GetNumDesktops(Glib::RefPtr<Gdk::Screen> screen) // IN:
{
   unsigned long result = 0;
   GetCardinal(screen->get_root_window(), "_NET_NUMBER_OF_DESKTOPS", result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetCurrentDesktop --
 *
 *      Retrieve the current virtual desktop for the screen.
 *
 * Results:
 *      The index of the virtual desktop.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

uint32
GetCurrentDesktop(Glib::RefPtr<Gdk::Screen> screen) // IN
{
   unsigned long result = 0;
   GetCardinal(screen->get_root_window(), "_NET_CURRENT_DESKTOP", result);
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetDesktopLayout --
 *
 *      Retrieves the current virtual desktop layout for the screen.
 *
 * Results:
 *      The virtual desktop layout information is set and the function
 *      returns true if successful. Otherwise false is returned.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool
GetDesktopLayout(Glib::RefPtr<Gdk::Screen> screen, // IN: Screen
                 uint32& rows,                     // OUT: Rows
                 uint32& columns,                  // OUT: Columns
                 Gtk::CornerType& corner,          // OUT: Corner of the first
                                                   //      desktop.
                 Gtk::Orientation& orientation)    // OUT: Desktop orientation
{
   std::vector<unsigned long> values;

   if (GetCardinalList(screen->get_root_window(),
                       "_NET_DESKTOP_LAYOUT", values)) {
      switch (values[0]) {
      case 0:
         orientation = Gtk::ORIENTATION_HORIZONTAL;
         break;

      case 1:
         orientation = Gtk::ORIENTATION_VERTICAL;
         break;

      default:
         Warning("Unsupported orientation in _NET_DESKTOP_LAYOUT\n");
         return false;
      }

      columns = static_cast<uint32>(values[1]);
      rows = static_cast<uint32>(values[2]);

      if (columns == 0 && rows == 0) {
         Warning("Invalid desktop configuration in _NET_DESKTOP_LAYOUT. "
                 "Rows and columns are both 0!\n");
         return false;
      } else if (columns == 0 || rows == 0) {
         uint32 numDesktops = GetNumDesktops(screen);

         if (columns == 0) {
            columns = numDesktops / rows +
                      ((numDesktops % rows) > 0 ? 1 : 0);
         } else if (rows == 0) {
            rows = numDesktops / columns +
                   ((numDesktops % columns) > 0 ? 1 : 0);
         }
      }

      corner = Gtk::CORNER_TOP_LEFT;

      if (values.size() == 4) {
         switch (values[3]) {
         case 0:
            corner = Gtk::CORNER_TOP_LEFT;
            break;

         case 1:
            corner = Gtk::CORNER_TOP_RIGHT;
            break;

         case 2:
            corner = Gtk::CORNER_BOTTOM_RIGHT;
            break;

         case 3:
            corner = Gtk::CORNER_BOTTOM_LEFT;
            break;

         default:
            Warning("Unsupported corner in _NET_DESKTOP_LAYOUT\n");
            return false;
         }
      }

      return true;
   }

   return false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetDesktopGeometry --
 *
 *      Retrieves the desktop geometry for this screen.
 *
 * Results:
 *      The desktop geometry is set and the function returns true if
 *      successful. Otherwise false is returned.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool
GetDesktopGeometry(Glib::RefPtr<Gdk::Screen> screen, // IN: The screen
                   uint32& width,                    // OUT: Width
                   uint32& height)                   // OUT: Height
{
   std::vector<unsigned long> values;

   if (GetCardinalList(screen->get_root_window(),
                       "_NET_DESKTOP_GEOMETRY", values)) {
      if (values.size() == 2) {
         width = values[0];
         height = values[1];
         return true;
      }
   }

   return false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetDesktopViewport --
 *
 *      Retrieves the viewport of the specified virtual desktop.
 *
 * Results:
 *      The viewport is set and the function returns true if successful.
 *      Otherwise false is returned.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool
GetDesktopViewport(Glib::RefPtr<Gdk::Screen> screen, // IN: The screen
                   uint32 desktopIndex,              // IN: Desktop index
                   VMPoint& viewport)                // OUT: Viewport
{
   std::vector<unsigned long> values;

   if (GetCardinalList(screen->get_root_window(),
                       "_NET_DESKTOP_VIEWPORT", values)) {
      uint32 numDesktops = GetNumDesktops(screen);

      if (values.size() == numDesktops * 2) {
         viewport.x = values[desktopIndex * 2];
         viewport.y = values[desktopIndex * 2 + 1];
         return true;
      }
   }

   return false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::RaiseWindowInternal --
 *
 *      Internal function to handle the restack operation.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
RaiseWindowInternal(Glib::RefPtr<Gdk::Window> window,  // IN: Window to raise
                    Glib::RefPtr<Gdk::Window> sibling, // IN: The sibling
                    guint32 timestamp)                 // IN: Event timestamp
{
   GdkScreen* screen = window->get_screen()->gobj();

   if (gdk_x11_screen_supports_net_wm_hint(screen,
         gdk_atom_intern_static_string("_NET_RESTACK_WINDOW"))) {
      XEvent ev;
      ev.xclient.type = ClientMessage;
      ev.xclient.serial = 0;
      ev.xclient.send_event = True;
      ev.xclient.window = GDK_WINDOW_XID(window->gobj());
      ev.xclient.message_type =
         gdk_x11_get_xatom_by_name_for_display(window->get_display()->gobj(),
                                               "_NET_RESTACK_WINDOW");
      ev.xclient.format = 32;
      ev.xclient.data.l[0] = 2;        // source (2 gives full control)
      ev.xclient.data.l[1] = (sibling
                              ? GDK_WINDOW_XID(sibling->gobj())
                              : None); // sibling
      ev.xclient.data.l[2] = Above;    // direction
      ev.xclient.data.l[3] = 0;        // unused
      ev.xclient.data.l[4] = 0;        // unused

      XSendEvent(GDK_WINDOW_XDISPLAY(window->gobj()),
                 GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
                 False, SubstructureRedirectMask | SubstructureNotifyMask,
                 &ev);
   } else {
      /*
       * As of writing (2011-03-08), Metacity doesn't support
       * _NET_RESTACK_WINDOW and will block our attempt to raise a window unless
       * it's active, so we activate the window first.
       */
      if (gdk_x11_screen_supports_net_wm_hint(
             screen,
             gdk_atom_intern_static_string("_NET_ACTIVE_WINDOW"))) {

         XClientMessageEvent xclient;
         memset (&xclient, 0, sizeof (xclient));
         xclient.type = ClientMessage;
         xclient.window = GDK_WINDOW_XID(window->gobj());
         xclient.message_type =
            gdk_x11_get_xatom_by_name_for_display(window->get_display()->gobj(),
                                                  "_NET_ACTIVE_WINDOW");
         xclient.format = 32;
         xclient.data.l[0] = 2; // source (2 gives full control)
         xclient.data.l[1] = timestamp;
         xclient.data.l[2] = None; // currently active window
         xclient.data.l[3] = 0;
         xclient.data.l[4] = 0;

         XSendEvent(GDK_WINDOW_XDISPLAY(window->gobj()),
                    GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
                    False, SubstructureRedirectMask | SubstructureNotifyMask,
                    (XEvent*)&xclient);
      }

      int flags = CWStackMode;
      XWindowChanges changes;
      changes.stack_mode = Above;

      if (sibling) {
         changes.sibling = GDK_WINDOW_XID(sibling->gobj());
         flags |= CWSibling;
      }

      XReconfigureWMWindow(GDK_WINDOW_XDISPLAY(window->gobj()),
                           GDK_WINDOW_XID(window->gobj()),
                           DefaultScreen(GDK_WINDOW_XDISPLAY(window->gobj())),
                           flags, &changes);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::RaiseWindow --
 *
 *      Raises a window to the top of the window stack. This version
 *      accepts a timestamp instead of fetching it, useful when being
 *      called from an event handler or when using a common timestamp.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
RaiseWindow(Glib::RefPtr<Gdk::Window> window,  // IN: Window to raise
            Glib::RefPtr<Gdk::Window> sibling, // IN/OPT: The sibling
            guint32 timestamp)                 // IN/OPT: Event timestamp
{
   /*
    * Fake an input event timestamp so that the window manager
    * will allow a restacking of this window.
    */
   gdk_x11_window_set_user_time(window->gobj(),
      timestamp == 0
      ?  gdk_x11_display_get_user_time(gdk_display_get_default())
      : timestamp);

   gdk_error_trap_push();
   RaiseWindowInternal(window, sibling, timestamp);
   gdk_flush();
   int err = gdk_error_trap_pop();

   if (err && sibling) {
      /*
       * This could be due to sibling not being a sibling window.
       * Apparently, this is possible in our case. Ignore the "sibling."
       */
      gdk_error_trap_push();
      RaiseWindowInternal(window, Glib::RefPtr<Gdk::Window>(), timestamp);
      err = gdk_error_trap_pop();
   }

   if (err) {
      /* We still have an error. Log it and continue on. */
      Glib::ustring method;

      if (gdk_x11_screen_supports_net_wm_hint(
            window->get_screen()->gobj(),
            gdk_atom_intern_static_string("_NET_RESTACK_WINDOW"))) {
         method = "_NET_RESTACK_WINDOW";
      } else {
         method = "XReconfigureWMWindow";
      }

      if (sibling) {
         Log("Unable to raise window (XID %d) over sibling (XID %d) using %s. "
             "Error code = %d\n",
             (int)GDK_WINDOW_XID(window->gobj()),
             (int)GDK_WINDOW_XID(sibling->gobj()), method.c_str(), err);
      } else {
         Log("Unable to raise window (XID %d) using %s. Error code = %d\n",
             (int)GDK_WINDOW_XID(window->gobj()), method.c_str(), err);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetHostWindowStack --
 *
 *      Returns the window stack as recorded by the window manager.
 *      This is the equivalent of gdk_screen_get_window_stack, except
 *      that function is broken on 64-bit platforms, so for the time
 *      being we need to provide our own.
 *
 * Results:
 *      The host window stack.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

HostWindowList
GetHostWindowStack()
{
   HostWindowList windows;
   GdkScreen* screen = gdk_screen_get_default();

   if (!gdk_x11_screen_supports_net_wm_hint(screen,
          gdk_atom_intern_static_string("_NET_CLIENT_LIST_STACKING"))) {
      /*
       * This is bad. We don't really have an alternative. We might want to
       * just disable Unity.
       */
      return windows;
   }

   GdkDisplay* display = gdk_display_get_default();
   unsigned long numItems = 0;
   unsigned long bytesAfter = 0;
   gint format = 0;
   Atom type = 0;
   guchar* data = NULL;

   GdkWindow* rootWin = gdk_screen_get_root_window(screen);

   gdk_error_trap_push();
   int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
                                GDK_WINDOW_XID(rootWin),
                                gdk_x11_get_xatom_by_name_for_display(display,
                                   "_NET_CLIENT_LIST_STACKING"),
                                0, G_MAXLONG, False, XA_WINDOW,
                                &type, &format, &numItems, &bytesAfter,
                                &data);
   int err = gdk_error_trap_pop();

   if (ret == Success && !err &&
       type == XA_WINDOW && format == 32 && data != NULL && numItems > 0) {
      long* stack = (long*)data;

      for (unsigned long i = 0; i < numItems; i++) {
#if GTK_MAJOR_VERSION == 3
         GdkWindow* win =
            gdk_x11_window_foreign_new_for_display(display, stack[i]);
#else
         GdkWindow* win =
            gdk_window_foreign_new_for_display(display, stack[i]);
#endif

         if (win != NULL) {
#if GTK_MAJOR_VERSION == 3
            windows.push_back(Glib::wrap(win));
#else
            windows.push_back(Glib::wrap((GdkWindowObject*)win));
#endif
         }
      }
   }

   return windows;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetMonitorWorkArea --
 *
 *      Gets the work area on a monitor. This is the area excluding docks,
 *      which a window would size to when maximized.
 *
 *      While the window manager typically provides a work area spanning all
 *      monitors (_NET_WORKAREA), it does not provide per-monitor work areas, so
 *      we must compute our own.
 *
 * Results:
 *      The work area of the specified monitor.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
GetMonitorWorkArea(Glib::RefPtr<Gdk::Screen> screen,    // IN:
                   int monitor,                         // IN:
                   Gdk::Rectangle& workArea)            // OUT:
{
#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1,10,0)
   /*
    * Relies on Cairo::Region support available in cairo 1.10+, which is
    * not available in our FreeBSD and Solaris toolchains.  Not called by
    * Tools code, so it's fine to just ifdef this out.
    */
   NOT_IMPLEMENTED();
#else


   /*
    * Start off by getting the size of the monitor. We're going to subtract
    * from this.
    */
   Gdk::Rectangle screenGeom;
   screen->get_monitor_geometry(monitor, screenGeom);
   Cairo::RectangleInt rect;
   Cairo::RefPtr<Cairo::Region> workAreaRegion = Cairo::Region::create();

   rect.x = screenGeom.get_x();
   rect.y = screenGeom.get_y();
   rect.width = screenGeom.get_width();
   rect.height = screenGeom.get_height();
   workAreaRegion->do_union(rect);

   /*
    * If we're dealing with a reparenting window manager, then using XQueryTree
    * will _not_ give us client windows, so to get client windows reliably, we
    * need to either use XQueryTree (get all top level windows) and iterate
    * hierarchy (root -> frame -> client) to be able to test for application
    * window properties, since these are only set on the client windows and not
    * on reparenting frames, or just use _NET_CLIENT_LIST. In practice, WMs put
    * docks and panels into _NET_CLIENT_LIST, so this should give us what we
    * need.
    */
   HostWindowList windows = GetHostWindowStack();
   bool haveStrut = false;
   for (HostWindowList::const_iterator iter = windows.begin();
        iter != windows.end();
        iter++) {

      Glib::RefPtr<Gdk::Window> gdkWindow = *iter;
      std::vector<unsigned long> values;
      NETWMStrutPartial strut = NETWMStrutPartial();

      gdk_error_trap_push();
      if (monitor != screen->get_monitor_at_window(gdkWindow)) {
         continue;
      }
      int err = gdk_error_trap_pop();
      if (err) {
         Log("Ignore xerror in get_monitor_at_window. Error code %d", err);
         continue;
      }

      /*
       * The EWMH spec says that the new _NET_WM_STRUT_PARTIAL takes precedence
       * over the older _NET_WM_STRUT API.
       */
      if (   GetCardinalList(gdkWindow, "_NET_WM_STRUT_PARTIAL", values)
          && values.size() == 12) {
         haveStrut = true;
         strut.left_width = values[0];
         strut.right_width = values[1];
         strut.top_height = values[2];
         strut.bottom_height = values[3];
         strut.left_start = values[4];
         strut.left_end = values[5];
         strut.right_start = values[6];
         strut.right_end = values[7];
         strut.top_start = values[8];
         strut.top_end = values[9];
         strut.bottom_start = values[10];
         strut.bottom_end = values[11];
      } else if (   GetCardinalList(gdkWindow, "_NET_WM_STRUT", values)
                 && values.size() == 4) {
         haveStrut = true;
         strut.left_width = values[0];
         strut.right_width = values[1];
         strut.top_height = values[2];
         strut.bottom_height = values[3];

         /*
          * Per EWMH spec: "This property (_NET_WM_STRUT) is equivalent to a
          * _NET_WM_STRUT_PARTIAL property where all start values are 0 and all
          * end values are the height or width of the logical screen."
          */
         strut.left_start = 0;
         strut.left_end = screen->get_height();
         strut.right_start = 0;
         strut.right_end = screen->get_height();
         strut.top_start = 0;
         strut.top_end = screen->get_width();
         strut.bottom_start = 0;
         strut.bottom_end = screen->get_width();
      } else {
         continue;
      }

      ASSERT(haveStrut);

      /*
       * Struts can be defined on one or more of the screen edges, so we create
       * 4 rectangles and subtract each from the work area.
       *
       * Per the EWMH spec: "Struts MUST be specified in root window
       * coordinates, that is, they are not relative to the edges of any view
       * port or Xinerama monitor.... Note that the strut is relative to the
       * screen edge, and not the edge of the xinerama monitor."
       */
      Gdk::Rectangle top(strut.top_start,
                         0,
                         strut.top_end - strut.top_start,
                         strut.top_height);
      Gdk::Rectangle bottom(strut.bottom_start,
                            screen->get_height() - strut.bottom_height,
                            strut.bottom_end - strut.bottom_start,
                            strut.bottom_height);
      Gdk::Rectangle left(0,
                          strut.left_start,
                          strut.left_width,
                          strut.left_end - strut.left_start);
      Gdk::Rectangle right(screen->get_width() - strut.right_width,
                           strut.right_start,
                           strut.right_width,
                           strut.right_end - strut.right_start);

      /*
       * We want each strut's rectangle to be used as if it was taking up the
       * entire edge of the monitor, so artificially inflate height or width as
       * need be. This means that instead of using the start and end strut
       * values, we take the whole edge.
       */
      Gdk::Rectangle edge;
      bool intersects = false;

      edge = top.intersect(screenGeom, intersects);

      if (top.get_height() > 0 && intersects && !edge.has_zero_area()) {
         rect.x = screenGeom.get_x();
         rect.y = screenGeom.get_y();
         rect.width = screenGeom.get_width();
         rect.height = edge.get_height();
         workAreaRegion->subtract(rect);
      }

      edge = bottom.intersect(screenGeom, intersects);

      if (bottom.get_height() > 0 && intersects && !edge.has_zero_area()) {
         rect.x = screenGeom.get_x();
         rect.y = edge.get_y();
         rect.width = screenGeom.get_width();
         rect.height = edge.get_height();
         workAreaRegion->subtract(rect);
      }

      edge = left.intersect(screenGeom, intersects);

      if (left.get_width() > 0 && intersects && !edge.has_zero_area()) {
         rect.x = screenGeom.get_x();
         rect.y = screenGeom.get_y();
         rect.width = edge.get_width();
         rect.height = screenGeom.get_height();
         workAreaRegion->subtract(rect);
      }

      edge = right.intersect(screenGeom, intersects);

      if (right.get_width() > 0 && intersects && !edge.has_zero_area()) {
         rect.x = edge.get_x();
         rect.y = screenGeom.get_y();
         rect.width = edge.get_width();
         rect.height = screenGeom.get_height();
         workAreaRegion->subtract(rect);
      }
   }
   /* bug:2163225: _NET_WM_STRUT_PARTIAL and _NET_WM_STRUT could not be retrived in redhat 7.4,7.5,
    * root cause unknown, have to use _NET_WORKAREA to get Work area directly in redhat 7.4,7.5, note
    * this fix only works in single monitor.
    */
   int monitorNum = screen->get_n_monitors();
   if ((!haveStrut) && (1 == monitorNum)) {
      std::vector<unsigned long> values;
      if (GetCardinalList(screen->get_root_window(), "_NET_WORKAREA", values) && values.size() >= 4 ) {
         rect.x=values[0];
         rect.y=values[1];
         rect.width=values[2];
         rect.height=values[3];
      } else {
         //Property: _NET_WORKAREA not found, workArea set keeps screen size
         Log("Property:_NET_WORKAREA unable to get or in multi monitor.");
         rect = workAreaRegion->get_extents();
      }
   } else {
      rect = workAreaRegion->get_extents();
   }
   workArea.set_x(rect.x);
   workArea.set_y(rect.y);
   workArea.set_width(rect.width);
   workArea.set_height(rect.height);
#endif // if CAIRO_VERSION
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetWindowManagerName --
 *
 *      Retrieves the current Window Manager name, if we can find it. This
 *      mimics the behavior of gdk_x11_screen_get_window_manager_name(), but
 *      there seem to be issues with that method returning its cached window
 *      manager name when it shouldn't
 *      (http://bugzilla.redhat.com/show_bug.cgi?id=471927).
 *
 * Results:
 *      If we can find the current window manager name, we'll return it. If we
 *      can't, then we'll return "unknown" -- same behavior as
 *      gdk_x11_screen_get_window_manager_name().
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

utf::string
GetWindowManagerName(Glib::RefPtr<Gdk::Screen> screen) // IN: Screen
{
   utf::string wmName = "unknown";
   GdkDisplay* display = gdk_display_get_default();
   unsigned long numItems = 0;
   unsigned long bytesAfter = 0;
   gint format = 0;
   Atom type = 0;
   guchar* data = NULL;
   ::Window* window;

   GdkWindow* rootWin = gdk_screen_get_root_window(screen->gobj());

   /*
    * First, we need to get the window that our EWMH-compliant WM is using to
    * communicate its properties with.
    */
   gdk_error_trap_push();
   int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
                                GDK_WINDOW_XID(rootWin),
                                gdk_x11_get_xatom_by_name_for_display(display,
                                   "_NET_SUPPORTING_WM_CHECK"),
                                0, G_MAXLONG, False, XA_WINDOW,
                                &type, &format, &numItems, &bytesAfter,
                                &data);
   int err = gdk_error_trap_pop();

   if (ret != Success || err || type != XA_WINDOW || data == NULL) {
      if (data) {
         XFree(data);
      }
      return wmName;
   }

   window = (::Window*)data;
   gchar* name = NULL;

   /*
    * Now, using the window provided in _NET_SUPPORTING_WM_CHECK, look for the
    * _NET_WM_NAME on it.
    */
   gdk_error_trap_push();
   ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
                            *window,
                            gdk_x11_get_xatom_by_name_for_display(display,
                                "_NET_WM_NAME"),
                            0, G_MAXLONG, False,
                            gdk_x11_get_xatom_by_name_for_display(display,
                                "UTF8_STRING"),
                            &type, &format, &numItems, &bytesAfter,
                            (guchar**)&name);
   err = gdk_error_trap_pop();

   XFree(window);

   if (ret != Success || err || name == NULL) {
      if (name != NULL) {
         XFree(name);
      }
      return wmName;
   }

   wmName = name;
   XFree(name);

   return wmName;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::ChangeEWMHWindowState --
 *
 *      Sends the requested _NET_WM_STATE change through to the root window for
 *      the Window Manager to act on.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
ChangeEWMHWindowState(bool add,                         // IN
                      Glib::RefPtr<Gdk::Window> window, // IN
                      GdkAtom state1,                   // IN
                      GdkAtom state2)                   // IN
{
   GdkScreen* screen = window->get_screen()->gobj();
   GdkDisplay* display = const_cast<GdkDisplay*>(window->get_display()->gobj());
   Window win = GDK_WINDOW_XID(window->gobj());

   XClientMessageEvent xclient;

/* Straight from http://standards.freedesktop.org/wm-spec/wm-spec-latest.html */
#define _NET_WM_STATE_REMOVE        0    /* remove/unset property */
#define _NET_WM_STATE_ADD           1    /* add/set property */
#define _NET_WM_STATE_TOGGLE        2    /* toggle property  */

   memset(&xclient, 0, sizeof xclient);
   xclient.type = ClientMessage;
   xclient.window = win;
   xclient.message_type = gdk_x11_get_xatom_by_name_for_display(display,
                                                                "_NET_WM_STATE");
   xclient.format = 32;
   xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   xclient.data.l[1] = gdk_x11_atom_to_xatom_for_display(display, state1);
   xclient.data.l[2] = gdk_x11_atom_to_xatom_for_display(display, state2);
   xclient.data.l[3] = 0;
   xclient.data.l[4] = 0;

   XSendEvent(GDK_DISPLAY_XDISPLAY(display),
              GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
              False, SubstructureRedirectMask | SubstructureNotifyMask,
              (XEvent*)&xclient);
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetEWMHWindowState --
 *
 *      Queries _NET_WM_STATE on the provided window and returns a std::list of
 *      utf::strings which contain the X Atom names that are set as
 *      _NET_WM_STATE for the given window.
 *
 * Results:
 *      An std::list containing the utf::string names of all _NET_WM_STATE atoms
 *      that are set on the given window.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

std::list<utf::string>
GetEWMHWindowState(Glib::RefPtr<Gdk::Window> window) // IN
{
   std::list<utf::string> atomStrings;

   GdkDisplay* display = const_cast<GdkDisplay*>(window->get_display()->gobj());
   GdkWindow* gdkwin = const_cast<GdkWindow*>(window->gobj());

   Atom type = None;
   gint format;
   gulong nitems;
   gulong bytes_after;
   guchar* data;
   Atom* atoms = NULL;

   gdk_error_trap_push();
   int ret = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(display),
                                GDK_WINDOW_XID(gdkwin),
                                gdk_x11_get_xatom_by_name_for_display(
                                   display, "_NET_WM_STATE"),
                                0, G_MAXLONG, False, XA_ATOM, &type, &format,
                                &nitems, &bytes_after, &data);
   int err = gdk_error_trap_pop();

   if (ret != Success || err) {
      atomStrings.push_back("Error calling XGetWindowProperty");
      return atomStrings;
   }

   if (type != XA_ATOM) {
      XFree(data);
      atomStrings.push_back("Error: type != XA_ATOM");
      return atomStrings;
   }

   atoms = reinterpret_cast<Atom*>(data);

   for (gulong i = 0; i < nitems; ++i) {
      atomStrings.push_back(gdk_x11_get_xatom_name(atoms[i]));
   }

   XFree(atoms);

   return atomStrings;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetPointerLocation --
 *
 *      Get the location of the pointer relative to the root window.
 *
 * Results:
 *      OUT parameters are filled in.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
GetPointerLocation(const Glib::RefPtr<Gdk::Window>& window, // IN
                   int& x,                                  // OUT
                   int& y,                                  // OUT
                   Gdk::ModifierType& mask)                 // OUT
{
#if GTK_MAJOR_VERSION == 3
   Glib::RefPtr<Gdk::DeviceManager> deviceManager =
      window->get_display()->get_device_manager();
   Glib::RefPtr<Gdk::Device> device = deviceManager->get_client_pointer();

   window->get_device_position(device, x, y, mask);
   window->get_root_coords(x, y, x, y);
#else
   window->get_display()->get_pointer(x, y, mask);
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetXWindowSize --
 *
 *      Get the width and height of the given window.
 *
 * Results:
 *      true if success, false otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool GetXWindowSize(const Glib::RefPtr<Gdk::Window>& window, // IN
                    int& width,                              // OUT
                    int& height)                             // OUT
{
   Glib::RefPtr<Gdk::Display> display = Gdk::Display::get_default();
   ::Display *xdisplay = GDK_DISPLAY_XDISPLAY(display->gobj());
   XWindowAttributes attr;
   GdkWindow *gdkwin = const_cast<GdkWindow *>(window->gobj());

   if (XGetWindowAttributes(xdisplay, GDK_WINDOW_XID(gdkwin), &attr)) {
      width = attr.width;
      height = attr.height;
      return true;
   }
   return false;
}


/*
 *-----------------------------------------------------------------------------
 *
 * xutils::GetXWindowOrigin --
 *
 *      Get the x and y of the given window.
 *
 * Results:
 *      true if success, false otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

bool GetXWindowOrigin(const Glib::RefPtr<Gdk::Window>& window, // IN
                      int& x,                                  // OUT
                      int& y)                                  // OUT
{
   Glib::RefPtr<Gdk::Display> display = Gdk::Display::get_default();
   ::Display *xdisplay = GDK_DISPLAY_XDISPLAY(display->gobj());
   GdkWindow *gdkwin = const_cast<GdkWindow *>(window->gobj());
   Window child;
   if (XTranslateCoordinates(xdisplay, GDK_WINDOW_XID(gdkwin),
                             XDefaultRootWindow(xdisplay), 0, 0, &x, &y,
                             &child)) {
      return true;
   }
   return false;
}


} // namespace xutils
0707010000035C000081A4000000000000000000000001682255050000127C000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/dndcp/xutils/xutils.hh    /*********************************************************
 * Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


#ifndef XUTILS_XUTILS_HH
#define XUTILS_XUTILS_HH


#include <gdkmm.h>
#include <gtkmm.h>

#include "stringxx/string.hh"


namespace xutils {

typedef std::list<Glib::RefPtr<Gdk::Window> > HostWindowList;

/* General initialization */
void Init();


/* General property helpers */
bool GetCardinal(Glib::RefPtr<const Gdk::Window> window,
                 const utf::string& atomName, unsigned long& retValue);
bool GetCardinalList(Glib::RefPtr<const Gdk::Window> window,
                     const utf::string& atomName,
                     std::vector<unsigned long>& retValues);
bool CheckDockPanel(Glib::RefPtr<const Gdk::Window> window);

/*
 * Utility functions for virtual desktops.
 *
 * There are two components to virtual desktops: Workspaces and Viewports.
 *
 * Workspaces can contain one or more viewports. Workspace layouts have a
 * corner origin and a direction in which the workspaces are ordered.
 *
 * Viewports exist inside a workspace and are essentially one large screen
 * containing windows. The current viewport has an X, Y offset on this
 * large screen containing the physical screen full of content to display.
 *
 * Some window managers (Metacity, for example) use workspaces exclusively to
 * represent virtual desktops, while others (Enlightenment) may use workspaces
 * and viewports combined. Compiz uses multiple viewports in a single
 * workspace.
 */
void SetDesktopForWindow(Glib::RefPtr<Gdk::Window> window, uint32 desktop);
uint32 GetDesktopForWindow(Glib::RefPtr<const Gdk::Window> window);

uint32 GetNumDesktops(Glib::RefPtr<Gdk::Screen> screen);
uint32 GetCurrentDesktop(Glib::RefPtr<Gdk::Screen> screen);
extern sigc::signal<void, Glib::RefPtr<Gdk::Screen> > currentDesktopChanged;

bool GetDesktopLayout(Glib::RefPtr<Gdk::Screen> screen,
                      uint32& rows, uint32& columns, Gtk::CornerType& corner,
                      Gtk::Orientation& orientation);
extern sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopLayoutChanged;

bool GetDesktopGeometry(Glib::RefPtr<Gdk::Screen> screen,
                        uint32& width, uint32& height);
extern sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopGeometryChanged;

bool GetDesktopViewport(Glib::RefPtr<Gdk::Screen> screen,
                        uint32 desktopIndex, VMPoint& viewport);
extern sigc::signal<void, Glib::RefPtr<Gdk::Screen> > desktopViewportChanged;

extern sigc::signal<void, Glib::RefPtr<Gdk::Screen> > activeWindowChanged;

/* Window stacking */
void RaiseWindow(Glib::RefPtr<Gdk::Window> window,
                 Glib::RefPtr<Gdk::Window> sibling =
                    Glib::RefPtr<Gdk::Window>(),
                 guint32 timestamp = 0);
HostWindowList GetHostWindowStack();
extern sigc::signal<void, Glib::RefPtr<Gdk::Screen> > windowStackChanged;


/* Multi-head */
void GetMonitorWorkArea(Glib::RefPtr<Gdk::Screen> screen,
                        int monitor, Gdk::Rectangle& rect);

utf::string GetWindowManagerName(Glib::RefPtr<Gdk::Screen> screen);
extern sigc::signal<void, Glib::RefPtr<Gdk::Screen> > windowManagerChanged;

void SetFullscreenMonitorsHint(Glib::RefPtr<Gdk::Window> window,
                               std::vector<long> topology);

void ChangeEWMHWindowState(bool add, Glib::RefPtr<Gdk::Window> window,
                           GdkAtom state1, GdkAtom state2);
std::list<utf::string> GetEWMHWindowState(Glib::RefPtr<Gdk::Window> window);

void GetPointerLocation(const Glib::RefPtr<Gdk::Window>& window,
                        int& x, int& y, Gdk::ModifierType& mask);


extern sigc::signal<void, Glib::RefPtr<Gdk::Screen> > workAreaChanged;

bool GetXWindowSize(const Glib::RefPtr<Gdk::Window>& window, int& width,
                    int& height);
bool GetXWindowOrigin(const Glib::RefPtr<Gdk::Window>& window, int& x, int& y);
} // namespace xutils


#endif // XUTILS_XUTILS_HH
0707010000035D000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/gdp   0707010000035E000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/gdp/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   0707010000035F000081A400000000000000000000000168225505000004D2000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/gdp/Makefile.am   ################################################################################
### Copyright (c) 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libgdp.la

libgdp_la_CPPFLAGS =
libgdp_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libgdp_la_LDFLAGS =
libgdp_la_LDFLAGS += @PLUGIN_LDFLAGS@

libgdp_la_LIBADD =
libgdp_la_LIBADD += @VMTOOLS_LIBS@
libgdp_la_LIBADD += @GOBJECT_LIBS@

libgdp_la_SOURCES =
libgdp_la_SOURCES += gdpPlugin.c
  07070100000360000081A4000000000000000000000001682255050001A04E000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/gdp/gdpPlugin.c   /*********************************************************
 * Copyright (c) 2020-2021,2023-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * gdpPlugin.c --
 *
 * Publishes guest data to host side gdp daemon.
 */

#include <string.h>
#include <stdlib.h>
#include <errno.h>

#if defined(_WIN32)
#include <winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/eventfd.h>
#include <sys/poll.h>
#include <unistd.h>
#endif

#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "vm_atomic.h"
#define  G_LOG_DOMAIN  "gdp"
#include "vmware/tools/log.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/threadPool.h"
#include "vmware/tools/gdp.h"
#include "vmci_defs.h"
#include "vmci_sockets.h"
#include "vmcheck.h"
#include "base64.h"
#include "util.h"
#include "str.h"
#include "jsmn.h"
#include "conf.h"

#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);


#if defined(_WIN32)
#   define GetSysErr() WSAGetLastError()

#   define SYSERR_EADDRINUSE    WSAEADDRINUSE
#   define SYSERR_EHOSTUNREACH  WSAEHOSTUNREACH
#   define SYSERR_EINTR         WSAEINTR
#   define SYSERR_EMSGSIZE      WSAEMSGSIZE
#   define SYSERR_WOULDBLOCK(e) (e == WSAEWOULDBLOCK)

    typedef int socklen_t;
#   define CloseSocket closesocket

    typedef WSAEVENT GdpEvent;
#   define GDP_INVALID_EVENT WSA_INVALID_EVENT
#else
#   define GetSysErr() errno

#   define SYSERR_EADDRINUSE    EADDRINUSE
#   define SYSERR_EHOSTUNREACH  EHOSTUNREACH
#   define SYSERR_EINTR         EINTR
#   define SYSERR_EMSGSIZE      EMSGSIZE
#   define SYSERR_WOULDBLOCK(e) (e == EAGAIN || e == EWOULDBLOCK)

    typedef int SOCKET;
#   define SOCKET_ERROR   (-1)
#   define INVALID_SOCKET ((SOCKET) -1)
#   define CloseSocket    close

    typedef int GdpEvent;
#   define GDP_INVALID_EVENT (-1)
#endif

#define GDP_TIMEOUT_AT_INFINITE (-1)
#define GDP_WAIT_INFINITE       (-1)
#define GDP_SEND_AFTER_ANY_TIME (-1)

#define USEC_PER_SECOND      (G_GINT64_CONSTANT(1000000))
#define USEC_PER_MILLISECOND (G_GINT64_CONSTANT(1000))

/*
 * Macros to read values from tools config
 */
#define GDP_CONFIG_GET_BOOL(key, defVal) \
   VMTools_ConfigGetBoolean(gPluginState.ctx->config, \
                            CONFGROUPNAME_GDP, key, defVal)

#define GDP_CONFIG_GET_INT(key, defVal) \
   VMTools_ConfigGetInteger(gPluginState.ctx->config, \
                            CONFGROUPNAME_GDP, key, defVal)

#define GDPD_RECV_PORT 7777
#define GDP_RECV_PORT  766

/*
 * The minimum rate limit is 1 pps, corresponding wait interval is 1 second.
 */
#define GDP_WAIT_RESULT_TIMEOUT 1500 // ms

/*
 * GDP Protocol
 *
 * The Producer (Guest) to Publisher (Host) protocol specification and
 * implementation aim to be backward compatible.
 *
 * Newer gdp plugin and clients using it must handle operation with hosts using
 * older versions of the protocol and implementation.
 *
 * The GDP protocol messages supported (newest to oldest) are described below:
 *
 * V2 --- Data Message
 * {
 *    "header": {
 *       "sequence":uint64,
 *       "version":int,
 *       "subscribers":[int-array],
 *       "requireSubs":Boolean,
 *       "createTime":"time-string",
 *       "topic":"string",
 *       "token":"string"
 *    }
 *    "payload": {
 *       "category":"string",
 *       "base64":"base64-encoded-string"
 *    }
 * }
 * NOTES:
 *   - Required attributes:
 *       o In header: sequence, version (new in V2), createTime, topic, token
 *       o In payload: category, base64
 *   - Optional fields
 *       o In header:
 *           + subscribers: Present only in History Data Message.
 *           + requireSubs: Present only when Data Producer requires subscriber
 *                          to be present/subscribed (new in V2).
 *
 * V2 --- Response
 * {
 *    "sequence":uint64,
 *    "version":int,
 *    "status":Boolean,
 *    "error-id":"id-string",
 *    "error-text":"string",
 *    "rateLimit":int
 * }
 * NOTES:
 *   - Required attributes:
 *       o sequence, status, rateLimit
 *   - Required but optional for Backward compatibility.
 *       o version: Required when the request header was version >= 2 AND the
 *                  host gdp daemon supports versions >= 2.
 *   - Optional fields:
 *       o error-id: Required when version >= 2 AND status==false; else ignored.
 *       o error-text: Same conditions as error-id.
 *
 * -------------------------------------
 *
 * Unversioned/original -- Data Message
 * {
 *    "header": {
 *       "sequence":uint64,
 *       "subscribers":[int-array],
 *       "createTime":"time-string",
 *       "topic":"string",
 *       "token":"string"
 *    }
 *    "payload": {
 *       "category":"string",
 *       "base64":"base64-encoded-string"
 *    }
 * }
 * NOTES:
 *   - Required attributes:
 *       o In header: sequence, createTime, topic, token
 *       o In payload: category, base64
 *   - Optional fields
 *       o In header:
 *           + subscribers: Present only in History Data Message.
 *
 * Unversioned/original -- Response
 * {
 *    "sequence":uint64,
 *    "status":"string-{ok|bad}",
 *    "diagnosis":"formatted-string",
 *    "rateLimit":int
 * }
 * NOTES:
 *   - Required attributes:
 *       o sequence, status, rateLimit
 *   - Optional attributes:
 *       o diagnosis: Present when error (status is 'bad') or for diagnostics
 *                    (debug only)
 *   - The 'status' field type changes from 'string' in V1 to 'boolean' in V2.
 *     This adds some complexity in format validation.
 *
 * -------------------------------------
 * Backward Compatibility
 *   Response parsing can handle all message version, required attributes
 *   expectations must be met and rely on the provided version field (or the
 *   default version) to evaluate if a valid response was received.
 *
 *   A V2 request can receive an unversioned ('V1') response from an old host
 *   and must handle the format. The impact is some functionality will not be
 *   available to the gdp plugin clients.
 *
 * Forward Compatibility
 *   Unknown attributes are ignored.
 *
 *   A host will not send a response with an higher version field value than the
 *   data message it is responding to. But if it did, the gdp plugin will ignore
 *   unknown attributes.
 *
 *   When receiving a response with a version field value lower then what the
 *   gdp plugin can support, this indicate the highest protocol version the host
 *   supports.
 */

#define GDP_PACKET_JSON \
   "{\n" \
   "   \"header\": {\n" \
   "      \"sequence\":%" G_GUINT64_FORMAT ",\n" \
   "      \"version\":%" G_GUINT64_FORMAT ",\n" \
   "%s" \
   "%s" \
   "      \"createTime\":\"%s\",\n" \
   "      \"topic\":\"%s\",\n" \
   "      \"token\":\"%s\"\n" \
   "   },\n" \
   "   \"payload\":{\n" \
   "      \"category\":\"%s\",\n" \
   "      \"base64\":\"%s\"\n" \
   "   }\n" \
   "}"

#define GDP_PACKET_JSON_LINE_SUBSCRIBERS \
   "      \"subscribers\":[%s],\n"

#define GDP_PACKET_JSON_LINE_REQUIRE_SUBS \
   "      \"requireSubs\":%s,\n"

#define GDP_RESULT_SEQUENCE   "sequence"  // Required, <uint64> e.g. 12345
#define GDP_RESULT_VERSION    "version"   // V2: Required, <uint> e.g. 2
#define GDP_RESULT_STATUS     "status"    // V1: Required,  "ok", or "bad" for
                                          // malformed and rejected packet
                                          // V2: Required, true, or false for
                                          // malformed and rejected packet, and
                                          // other errors (see error-id and
                                          // error-text).
#define GDP_RESULT_DIAGNOSIS  "diagnosis" // Optional
#define GDP_RESULT_ERROR_ID   "error-id"  // V2: Required Error ID to map to
                                          // GdpError enum.
#define GDP_RESULT_ERROR_TEXT "error-text"// V2: Optional Provided error message.
#define GDP_RESULT_RATE_LIMIT "rateLimit" // Required,  <int>
                                          // e.g. 2 packets per second

#define GDP_RESULT_STATUS_OK  "ok"        // V1: possible value for status
#define GDP_RESULT_STATUS_BAD "bad"       // V1: possible value for status

#define GDP_RESULT_REQUIRED_KEYS 3
#define GDP_RESULT_V2_REQUIRED_KEYS 4

#define GDP_HISTORY_REQUEST_PAST_SECONDS   "pastSeconds"   // <uint64>,
                                                           // Required
#define GDP_HISTORY_REQUEST_ID             "id"            // <uint64>,
                                                           // Subscription ID,
                                                           // Required
#define GDP_HISTORY_REQUEST_TOPIC_PREFIXES "topicPrefixes" // <String array>,
                                                           // Optional
#define GDP_HISTORY_REQUEST_REQUIRED_KEYS 2

/*
 * History guest data cache buffer size limit
 */
#define GDP_MAX_CACHE_SIZE_LIMIT     (1 << 22) // 4M
#define GDP_DEFAULT_CACHE_SIZE_LIMIT (1 << 20) // 1M
#define GDP_MIN_CACHE_SIZE_LIMIT     (1 << 18) // 256K

/*
 * History guest data cache item count limit
 */
#define GDP_MAX_CACHE_COUNT_LIMIT     (1 << 10) // 1024
#define GDP_DEFAULT_CACHE_COUNT_LIMIT (1 << 8)  // 256
#define GDP_MIN_CACHE_COUNT_LIMIT     (1 << 6)  // 64

#define GDP_STR_SIZE(s) ((guint32) (s != NULL ? (strlen(s) + 1) : 0))

#define GDP_TOKENS_PER_ALLOC 50


/*
 * Gdp protocol 'error-id' response attribute table.
 * From GDP_ERR_ITEM tuple:
 *   - GdpEnum name
 *   - error-id string id
 *   - Default error message string
 */
#define GDP_ERR_ITEM(a, b, c) b,
static const char * const gdpErrIds[] = {
GDP_ERR_LIST
};
#undef GDP_ERR_ITEM

/*
 * GdpError message table.
 * From GDP_ERR_ITEM tuple:
 *   - GdpEnum name
 *   - error-id string id
 *   - Default error message string
 */
#define GDP_ERR_ITEM(a, b, c) c,
static const char * const gdpErrMsgs[] = {
GDP_ERR_LIST
};
#undef GDP_ERR_ITEM

static GdpError GetGdpErrorFromErrorId(const char *erroIdStr); // IN


typedef struct PluginState {
   ToolsAppCtx *ctx;       /* Tools application context */

   Atomic_Bool started;    /* TRUE : Guest data publishing is started
                            * FALSE: otherwise
                            * Transitions from FALSE to TRUE only */

#if defined(_WIN32)
   Bool wsaStarted;        /* TRUE : WSAStartup succeeded, WSACleanup required
                            * FALSE: otherwise */
#endif
   int vmciFd;             /* vSocket address family value fd */
   int vmciFamily;         /* vSocket address family value */
   SOCKET sock;            /* Datagram socket for publishing guest data */
#if defined(_WIN32)
   GdpEvent eventNetwork;  /* The network event object:
                            * To be associated with network
                            * send/recv ready event */
#endif

   GdpEvent eventStop;     /* The stop event object:
                            * Signalled to stop guest data publishing */
   Atomic_Bool stopped;    /* TRUE : Guest data publishing is stopped
                            * FALSE: otherwise
                            * Transitions from FALSE to TRUE only */

   GdpEvent eventConfig;   /* The config event object:
                            * Signalled to update config */
} PluginState;

static PluginState gPluginState;


typedef struct PublishState {
   GMutex mutex; /* To sync incoming publish calls */

   /*
    * Data passed from the active incoming publish thread to
    * the gdp task thread.
    */
   gint64 createTime; /* Real wall-clock time,
                       * in microseconds since January 1, 1970 UTC. */
   const gchar *topic;
   const gchar *token;
   const gchar *category;
   const gchar *data;
   guint32 dataLen;
   gboolean cacheData;
   gboolean requireSubs; /* Subscriber presence required by publisher */

   /*
    * The publish event object:
    * The active incoming publish thread signals this event object
    * to notify the gdp task thread to publish new data.
    */
   GdpEvent eventPublish;

   /*
    * The get-result event object:
    * The gdp task thread signals this event object to notify
    * the active incoming publish thread to get publish result.
    */
   GdpEvent eventGetResult;

   GdpError gdpErr; /* The publish result */

} PublishState;

static PublishState gPublishState;


typedef enum GdpTaskEvent {
   GDP_TASK_EVENT_NONE,    /* No event */
   GDP_TASK_EVENT_STOP,    /* Stop event */
   GDP_TASK_EVENT_CONFIG,  /* Config event */
   GDP_TASK_EVENT_NETWORK, /* Network event */
   GDP_TASK_EVENT_PUBLISH, /* Publish event */
   GDP_TASK_EVENT_TIMEOUT, /* Wait timed out */
} GdpTaskEvent;

typedef enum GdpTaskMode {
   GDP_TASK_MODE_NONE,    /* Not publishing
                           * valid with GDP_TASK_STATE_IDLE only */
   GDP_TASK_MODE_PUBLISH, /* Publishing new data */
   GDP_TASK_MODE_HISTORY, /* Publishing history data */
} GdpTaskMode;

typedef enum GdpTaskState {
   GDP_TASK_STATE_IDLE,             /* Not started
                                     * valid with GDP_TASK_MODE_NONE only */
   GDP_TASK_STATE_WAIT_TO_SEND,     /* Wait to send JSON packet */
   GDP_TASK_STATE_WAIT_FOR_RESULT1, /* Wait for publish result from daemon */
   GDP_TASK_STATE_WAIT_FOR_RESULT2, /* Wait for publish result after re-send */
} GdpTaskState;

typedef struct PublishResult {
   guint64 sequence; /* Result for the packet with this sequence number */
   Bool statusOk;    /* TRUE: ok|true, FALSE: bad|false */
   gchar *diagnosis; /* Diagnosis message if statusOk is FALSE */
   gint32 rateLimit; /* VMCI peer rate limit */
   guint64 version;  /* Response message protocol version, since v2 */
   GdpError errorId; /* GdpError from error-id when statusOk is FALSE,
                      * GDP_ERROR_SUCCESS otherwise */
   gchar *errorText; /* error-text; can be provided when when statusOk is
                      * FALSE; NULL otherwise */
} PublishResult;

typedef struct HistoryRequest {
   gint64 beginCacheTime;    /* Begin cacheTime */
   gint64 endCacheTime;      /* End cacheTime */
   guint64 id;               /* Subscription ID */
   GPtrArray *topicPrefixes; /* Topic prefixes */
} HistoryRequest;

typedef struct HistoryCacheItem {
   gint64 createTime; /* Guest data - begin */
   gchar *topic;
   gchar *token;
   gchar *category;
   gchar *data;
   guint32 dataLen;   /* Guest data - end */
   gboolean requireSubs; /* Publisher requires subscriber to publish Guest Data.
                          * Requires a V2 (and up) protocol compatible host;
                          * ignored otherwise */
   gint64 cacheTime;  /* Monotonic time point when item is cached */
   guint32 itemSize;  /* Item size in bytes */
} HistoryCacheItem;

typedef struct HistoryCache {
   GQueue queue;       /* Container for HistoryCacheItem */
   guint32 sizeLimit;  /* Cache buffer size limit */
   guint32 countLimit; /* Cache item count limit */
   guint32 size;       /* Current cache buffer size */
   GList *currentLink; /* Pointer to the history cache queue link
                        * currently being published */
} HistoryCache;

typedef struct TaskContext {
   /*
    * Legal mode & state combination
    *
    * GDP_TASK_MODE_NONE   : GDP_TASK_STATE_IDLE
    *                        (Idle state will never time out)
    *
    * GDP_TASK_MODE_PUBLISH: GDP_TASK_STATE_WAIT_TO_SEND
    *                        GDP_TASK_STATE_WAIT_FOR_RESULT1
    *                        GDP_TASK_STATE_WAIT_FOR_RESULT2
    *                        (All the above wait states will time out)
    *
    * GDP_TASK_MODE_HISTORY: GDP_TASK_STATE_WAIT_TO_SEND
    *                        GDP_TASK_STATE_WAIT_FOR_RESULT1
    *                        GDP_TASK_STATE_WAIT_FOR_RESULT2
    *                        (All the above wait states will time out)
    */
   GdpTaskMode mode;
   GdpTaskState state;

   /*
    * Publish event object signalled while mode is GDP_TASK_MODE_HISTORY.
    */
   Bool publishPending;
   /*
    * History request can be received at any time,
    * non-empty requests queue means history request pending.
    */

   HistoryCache cache;  /* Container for history cache items */
   GQueue requests;     /* Container for HistoryRequest */

   guint64 sequence;    /* Sequence number */
   gchar *packet;       /* Formatted JSON packet */
   guint32 packetLen;   /* JSON packet length */

   gint64 timeoutAt;    /* Time out at this monotonic time point,
                         * in microseconds. */
   gint64 sendAfter;    /* Send to daemon after this monotonic time point,
                         * in microseconds. */
} TaskContext;


static inline GdpEvent
GdpCreateEvent(void);

static inline void
GdpCloseEvent(GdpEvent *event); // IN/OUT

static inline void
GdpSetEvent(GdpEvent event); // IN

static inline void
GdpResetEvent(GdpEvent event); // IN

static GdpError
GdpWaitForEvent(GdpEvent event, // IN
                int timeout);   // IN

static Bool
GdpCreateSocket(void);

static void
GdpCloseSocket(void);

static GdpError
GdpSendTo(SOCKET sock,                         // IN
          const char *buf,                     // IN
          int bufLen,                          // IN
          const struct sockaddr_vm *destAddr); // IN

static GdpError
GdpRecvFrom(SOCKET sock,                  // IN
            char *buf,                    // OUT
            int *bufLen,                  // IN/OUT
            struct sockaddr_vm *srcAddr); // OUT

static inline void
GdpTopicPrefixFree(gpointer data); // IN

static inline void
GdpHistoryRequestFree(HistoryRequest *request); // IN

static void
GdpHistoryRequestFreeGFunc(gpointer item_data,  // IN
                           gpointer user_data); // IN

static inline void
GdpTaskClearHistoryRequestQueue(TaskContext *taskCtx); // IN/OUT

static guint32
GdpGetHistoryCacheSizeLimit();

static guint32
GdpGetHistoryCacheCountLimit();

static inline Bool
GdpTaskIsHistoryCacheEnabled(TaskContext *taskCtx); // IN

static void
GdpHistoryCacheItemFree(HistoryCacheItem *item); // IN

static void
GdpHistoryCacheItemFreeGFunc(gpointer item_data,  // IN
                             gpointer user_data); // IN

static inline void
GdpTaskClearHistoryCacheQueue(TaskContext *taskCtx); // IN/OUT

static void
GdpTaskDeleteHistoryCacheTail(TaskContext *taskCtx); // IN/OUT

static void
GdpTaskHistoryCachePushItem(TaskContext *taskCtx,  // IN/OUT
                            gint64 createTime,     // IN
                            const gchar *topic,    // IN
                            const gchar *token,    // IN
                            const gchar *category, // IN
                            const gchar *data,     // IN
                            guint32 dataLen,       // IN
                            gboolean requireSubs); // IN

static gchar *
GdpGetFormattedUtcTime(gint64 utcTime); // IN

static GdpError
GdpTaskBuildPacket(TaskContext *taskCtx,      // IN/OUT
                   gint64 createTime,         // IN
                   const gchar *topic,        // IN
                   const gchar *token,        // IN, OPTIONAL
                   const gchar *category,     // IN, OPTIONAL
                   const gchar *data,         // IN
                   guint32 dataLen,           // IN
                   gboolean requireSubs,      // IN
                   const gchar *subscribers); // IN, OPTIONAL

static inline void
GdpTaskDestroyPacket(TaskContext *taskCtx); // IN/OUT

static inline Bool
GdpTaskOkToSend(TaskContext *taskCtx); // IN

static GdpError
GdpTaskSendPacket(TaskContext *taskCtx); // IN/OUT

static Bool
GdpMatchTopicPrefixes(const gchar *topic,              // IN
                      const GPtrArray *topicPrefixes); // IN

static gchar *
GdpTaskUpdateHistoryCachePointerAndGetSubscribers(
   TaskContext *taskCtx); // IN/OUT

static void
GdpTaskPublishHistory(TaskContext *taskCtx); // IN/OUT

static void
GdpTaskProcessConfigChange(TaskContext *taskCtx); // IN/OUT

static Bool
GdpJsonIsTokenOfKey(const char *json,       // IN
                    const jsmntok_t *token, // IN
                    const char *key);       // IN

static Bool
GdpJsonIsPublishResult(const char *json,        // IN
                       const jsmntok_t *tokens, // IN
                       int count,               // IN
                       PublishResult *result);  // OUT

static void
GdpTaskProcessPublishResult(TaskContext *taskCtx,         // IN/OUT
                            const PublishResult *result); // IN

static Bool
GdpJsonIsHistoryRequest(const char *json,         // IN
                        const jsmntok_t *tokens,  // IN
                        int count,                // IN
                        HistoryRequest *request); // OUT

static void
GdpTaskProcessHistoryRequest(TaskContext *taskCtx,     // IN/OUT
                             HistoryRequest *request); // IN/OUT

static void
GdpTaskProcessNetwork(TaskContext *taskCtx); // IN/OUT

static void
GdpTaskProcessPublish(TaskContext *taskCtx); // IN/OUT

static void
GdpTaskProcessTimeout(TaskContext *taskCtx); // IN/OUT

static int
GdpTaskGetTimeout(TaskContext *taskCtx); // IN

static void
GdpTaskProcessEvents(TaskContext *taskCtx,    // IN/OUT
                     GdpTaskEvent taskEvent); // IN

static GdpError
GdpTaskWaitForEvents(int timeout,              // IN
                     GdpTaskEvent *taskEvent); // OUT

static void
GdpTaskCtxInit(TaskContext *taskCtx); // OUT

static void
GdpTaskCtxDestroy(TaskContext *taskCtx); // IN/OUT

static void
GdpThreadTask(ToolsAppCtx *ctx, // IN
              void *data);      // IN

static void
GdpThreadInterrupt(ToolsAppCtx *ctx, // IN
                   void *data);      // IN

static void
GdpInit(ToolsAppCtx *ctx); // IN

static Bool
GdpStart(void);

static void
GdpDestroy(void);


/*
 ******************************************************************************
 * GetGdpErrorFromErrorId --
 *
 * Translate an error-id string to a GdpError enumeration.
 *
 * @return The GdpError match
 * @return GDP_ERR_MAX otherwise.
 *
 ******************************************************************************
 */

static GdpError
GetGdpErrorFromErrorId(const char *errorIdStr) // IN
{
   if (errorIdStr != NULL && errorIdStr[0] != '\0') {

      int i; // Err: 'for' loop initial declarations only allowed in C99 mode

      for (i = 0; i < GDP_ERR_MAX; i++) {
         if (strcmp(gdpErrIds[i], errorIdStr) == 0) {
            // Match
            return i;
         }
      }
   }

   // Return GDP_ERR_MAX for not found/invalid
   return GDP_ERR_MAX;
}


/*
 ******************************************************************************
 * GdpCreateEvent --
 *
 * Creates a new event object/fd.
 *
 * @return The new event object handle or fd on success.
 * @return GDP_INVALID_EVENT otherwise.
 *
 ******************************************************************************
 */

static inline GdpEvent
GdpCreateEvent(void)
{
#if defined(_WIN32)
   return WSACreateEvent();
#else
   return eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
#endif
}


/*
 ******************************************************************************
 * GdpCloseEvent --
 *
 * Closes the event object/fd.
 *
 * @param[in,out] event  The event object/fd pointer
 *
 ******************************************************************************
 */

static inline void
GdpCloseEvent(GdpEvent *event) // IN/OUT
{
   ASSERT(event != NULL);

   if (*event != GDP_INVALID_EVENT) {
#if defined(_WIN32)
      if (!WSACloseEvent(*event)) {
         g_warning("%s: WSACloseEvent failed: error=%d.\n",
                   __FUNCTION__, WSAGetLastError());
      }
#else
      if (close(*event) != 0) {
         g_warning("%s: close failed: error=%d.\n",
                   __FUNCTION__, errno);
      }
#endif
      *event = GDP_INVALID_EVENT;
   }
}


/*
 ******************************************************************************
 * GdpSetEvent --
 *
 * Signals the event object/fd.
 *
 * @param[in] event  The event object/fd
 *
 ******************************************************************************
 */

static inline void
GdpSetEvent(GdpEvent event) // IN
{
#if defined(_WIN32)
   ASSERT(event != GDP_INVALID_EVENT);
   if (!WSASetEvent(event)) {
      g_warning("%s: WSASetEvent failed: error=%d.\n",
                __FUNCTION__, WSAGetLastError());
   }
#else
   eventfd_t val = 1;
   ASSERT(event != GDP_INVALID_EVENT);
   if (eventfd_write(event, val) != 0) {
      g_warning("%s: eventfd_write failed: error=%d.\n", __FUNCTION__, errno);
   }
#endif
}


/*
 ******************************************************************************
 * GdpResetEvent --
 *
 * Resets the event object/fd.
 *
 * @param[in] event  The event object/fd
 *
 ******************************************************************************
 */

static inline void
GdpResetEvent(GdpEvent event) // IN
{
#if defined(_WIN32)
   ASSERT(event != GDP_INVALID_EVENT);
   if (!WSAResetEvent(event)) {
      g_warning("%s: WSAResetEvent failed: error=%d.\n",
                __FUNCTION__, WSAGetLastError());
   }
#else
   eventfd_t val;
   ASSERT(event != GDP_INVALID_EVENT);
   if (eventfd_read(event, &val) != 0) {
      g_warning("%s: eventfd_read failed: error=%d.\n", __FUNCTION__, errno);
   }
#endif
}


/*
 ******************************************************************************
 * GdpWaitForEvent --
 *
 * Waits for the event object/fd or timeout.
 *
 * @param[in] event    The event object/fd
 * @param[in] timeout  Timeout value in milliseconds,
 *                     negative value means an infinite timeout,
 *                     zero means no wait.
 *
 * @return GDP_ERROR_SUCCESS on event signalled.
 * @return GDP_ERROR_TIMEOUT on timeout.
 * @return Other GdpError code otherwise.
 *
 ******************************************************************************
 */

static GdpError
GdpWaitForEvent(GdpEvent event, // IN
                int timeout)    // IN
{
#if defined(_WIN32)
   DWORD localTimeout;
   gint64 startTime;
   GdpError retVal;

   localTimeout = (DWORD)(timeout >= 0 ? timeout : WSA_INFINITE);
   if (timeout > 0) {
      startTime = g_get_monotonic_time();
   } else {
      startTime = 0; // Deals with [-Werror=maybe-uninitialized]
   }

   while (TRUE) {
      WSAEVENT eventObjects[] = { event };
      DWORD waitRes;

      waitRes = WSAWaitForMultipleEvents((DWORD) ARRAYSIZE(eventObjects),
                                         eventObjects,
                                         FALSE, localTimeout, TRUE);
      if (waitRes == WSA_WAIT_EVENT_0) {
         retVal = GDP_ERROR_SUCCESS;
         break;
      } else if (waitRes == WSA_WAIT_IO_COMPLETION) {
         gint64 curTime;
         gint64 passedTime;

         if (localTimeout == 0 ||
             localTimeout == WSA_INFINITE) {
            continue;
         }

         curTime = g_get_monotonic_time();
         passedTime = (curTime - startTime) / USEC_PER_MILLISECOND;
         if (passedTime >= localTimeout) {
            retVal = GDP_ERROR_TIMEOUT;
            break;
         }

         startTime = curTime;
         localTimeout -= (DWORD) passedTime;
         continue;
      } else if (waitRes == WSA_WAIT_TIMEOUT) {
         retVal = GDP_ERROR_TIMEOUT;
         break;
      } else { // WSA_WAIT_FAILED
         g_warning("%s: WSAWaitForMultipleEvents failed: error=%d.\n",
                   __FUNCTION__, WSAGetLastError());
         retVal = GDP_ERROR_GENERAL;
         break;
      }
   }

   return retVal;

#else

   gint64 startTime;
   GdpError retVal;

   if (timeout > 0) {
      startTime = g_get_monotonic_time();
   } else {
      startTime = 0; // Deals with [-Werror=maybe-uninitialized]
   }

   while (TRUE) {
      struct pollfd fds[1];
      int res;

      fds[0].fd = event;
      fds[0].events = POLLIN;
      fds[0].revents = 0;

      res = poll(fds, ARRAYSIZE(fds), timeout);
      if (res > 0) {
         if (fds[0].revents & POLLIN) {
            retVal = GDP_ERROR_SUCCESS;
         } else { // Not expected
            g_warning("%s: Unexpected event.\n", __FUNCTION__);
            retVal = GDP_ERROR_GENERAL;
         }

         break;
      } else if (res == -1) {
         int err = errno;
         if (err == EINTR) {
            gint64 curTime;
            gint64 passedTime;

            if (timeout <= 0) {
               continue;
            }

            curTime = g_get_monotonic_time();
            passedTime = (curTime - startTime) / USEC_PER_MILLISECOND;
            if (passedTime >= timeout) {
               retVal = GDP_ERROR_TIMEOUT;
               break;
            }

            startTime = curTime;
            timeout -= (int) passedTime;
            continue;
         } else {
            g_warning("%s: poll failed: error=%d.\n", __FUNCTION__, err);
            retVal = GDP_ERROR_GENERAL;
            break;
         }
      } else if (res == 0) {
         retVal = GDP_ERROR_TIMEOUT;
         break;
      } else {
         g_warning("%s: Unexpected poll return: %d.\n", __FUNCTION__, res);
         retVal = GDP_ERROR_GENERAL;
         break;
      }
   }

   return retVal;
#endif
}


/*
 ******************************************************************************
 * GdpCreateSocket --
 *
 * Creates a non-blocking datagram socket for guest data publishing.
 *
 * The socket is bound to the gdp guest receive port.
 *
 * @return TRUE on success.
 * @return FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
GdpCreateSocket(void)
{
   Bool retVal;
   struct sockaddr_vm localAddr;
#if defined(_WIN32)
   u_long nbMode = 1; // Non-blocking mode
#  define SOCKET_TYPE_PARAM SOCK_DGRAM
#else
   /*
    * Requires Linux kernel version >= 2.6.27.
    */
#  define SOCKET_TYPE_PARAM SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC
#endif

   ASSERT(gPluginState.sock == INVALID_SOCKET);
   ASSERT(gPluginState.vmciFamily != -1);

   gPluginState.sock = socket(gPluginState.vmciFamily, SOCKET_TYPE_PARAM, 0);
#undef SOCKET_TYPE_PARAM

   if (gPluginState.sock == INVALID_SOCKET) {
      g_critical("%s: socket failed: error=%d.\n", __FUNCTION__, GetSysErr());
      return FALSE;
   }

   retVal = FALSE;

#if defined(_WIN32)
   /*
    * Sets socket to nonblocking mode.
    * Note: WSAEventSelect automatically does this if not done.
    */
   if (ioctlsocket(gPluginState.sock, FIONBIO, &nbMode) != 0) {
      g_critical("%s: ioctlsocket failed: error=%d.\n",
                 __FUNCTION__, GetSysErr());
      goto exit;
   }
#endif

   memset(&localAddr, 0, sizeof localAddr);
   localAddr.svm_family = gPluginState.vmciFamily;
   localAddr.svm_cid = VMCISock_GetLocalCID();
   localAddr.svm_port = GDP_RECV_PORT; // No htons

   if (bind(gPluginState.sock, (struct sockaddr *) &localAddr,
            (socklen_t) sizeof localAddr) != 0) {
      g_critical("%s: bind failed: error=%d.\n",
                 __FUNCTION__, GetSysErr());
      goto exit;
   }

   g_debug("%s: Socket created and bound to local port %d.\n",
           __FUNCTION__, localAddr.svm_port);

   retVal = TRUE;

exit:
   if (!retVal) {
      CloseSocket(gPluginState.sock);
      gPluginState.sock = INVALID_SOCKET;
   }

   return retVal;
}


/*
 ******************************************************************************
 * GdpCloseSocket --
 *
 * Closes the guest data publishing datagram socket.
 *
 ******************************************************************************
 */

static void
GdpCloseSocket(void)
{
   if (gPluginState.sock != INVALID_SOCKET) {
      g_debug("%s: Closing socket.\n", __FUNCTION__);
      if (CloseSocket(gPluginState.sock) != 0) {
         g_warning("%s: CloseSocket failed: fd=%d, error=%d.\n",
                   __FUNCTION__, gPluginState.sock, GetSysErr());
      }

      gPluginState.sock = INVALID_SOCKET;
   }
}


/*
 ******************************************************************************
 * GdpSendTo --
 *
 * Wrapper of sendto() for VMCI datagram socket.
 *
 * Datagram send is not buffered, it will not return EWOULDBLOCK.
 * If host daemon is not running, error EHOSTUNREACH is returned.
 *
 * @param[in] sock      VMCI datagram socket descriptor
 * @param[in] buf       Data buffer pointer
 * @param[in] bufLen    Data length
 * @param[in] destAddr  Destination VMCI datagram socket address
 *
 * @return GDP_ERROR_SUCCESS on success.
 * @return Other GdpError code otherwise.
 *
 ******************************************************************************
 */

static GdpError
GdpSendTo(SOCKET sock,                        // IN
          const char *buf,                    // IN
          int bufLen,                         // IN
          const struct sockaddr_vm *destAddr) // IN
{
   GdpError retVal;

   ASSERT(sock != INVALID_SOCKET);
   ASSERT(buf != NULL && bufLen > 0);
   ASSERT(destAddr != NULL);

   do {
      long res;
      int err;
      socklen_t destAddrLen = (socklen_t) sizeof *destAddr;

      res = sendto(sock, buf, bufLen, 0,
                   (const struct sockaddr *) destAddr, destAddrLen);
      if (res == bufLen) {
         retVal = GDP_ERROR_SUCCESS;
         break;
      } else if (res != SOCKET_ERROR) { // No partial send shall happen
         g_warning("%s: sendto returned unexpected value %d.\n",
                   __FUNCTION__, (int) res);
         retVal = GDP_ERROR_GENERAL;
         break;
      }

      err = GetSysErr();
      if (err == SYSERR_EINTR) {
         continue;
      } else if (err == SYSERR_EHOSTUNREACH) {
         g_info("%s: sendto failed: host daemon unreachable.\n", __FUNCTION__);
         retVal = GDP_ERROR_UNREACH;
      } else if (err == SYSERR_EMSGSIZE) {
         g_warning("%s: sendto failed: message too large.\n", __FUNCTION__);
         retVal = GDP_ERROR_DATA_SIZE;
      } else {
         g_warning("%s: sendto failed: error=%d.\n", __FUNCTION__, err);
         retVal = GDP_ERROR_GENERAL;
      }

      break;

   } while (TRUE);

   return retVal;
}


/*
 ******************************************************************************
 * GdpRecvFrom --
 *
 * Wrapper of recvfrom() for VMCI datagram socket.
 *
 * @param[in]     sock     VMCI datagram socket descriptor
 * @param[out]    buf      Buffer pointer
 * @param[in,out] bufLen   Buffer length on input,
 *                         received data length on output
 * @param[out]    srcAddr  Source VMCI datagram socket address
 *
 * @return GDP_ERROR_SUCCESS on success.
 * @return Other GdpError code otherwise.
 *
 ******************************************************************************
 */

static GdpError
GdpRecvFrom(SOCKET sock,                 // IN
            char *buf,                   // OUT
            int *bufLen,                 // IN/OUT
            struct sockaddr_vm *srcAddr) // OUT
{
   GdpError retVal;

   ASSERT(sock != INVALID_SOCKET);
   ASSERT(buf != NULL && bufLen != NULL && *bufLen > 0);
   ASSERT(srcAddr != NULL);

   do {
      long res;
      int err;
      socklen_t srcAddrLen = (socklen_t) sizeof *srcAddr;

      res = recvfrom(sock, buf, *bufLen, 0,
                     (struct sockaddr *) srcAddr, &srcAddrLen);
      if (res >= 0) {
         *bufLen = (int) res;
         retVal = GDP_ERROR_SUCCESS;
         break;
      }

      ASSERT(res == SOCKET_ERROR);
      err = GetSysErr();
      if (err == SYSERR_EINTR) {
         continue;
      } else if (err == SYSERR_EMSGSIZE) {
         g_warning("%s: recvfrom failed: buffer size too small.\n",
                   __FUNCTION__);
         retVal = GDP_ERROR_DATA_SIZE;
      } else { // Including EWOULDBLOCK
         g_warning("%s: recvfrom failed: error=%d.\n", __FUNCTION__, err);
         retVal = GDP_ERROR_GENERAL;
      }

      break;

   } while (TRUE);

   return retVal;
}


/*
 *****************************************************************************
 * GdpTopicPrefixFree --
 *
 * Frees topic prefix string buffer.
 *
 * @param[in] data  Topic prefix string buffer pointer
 *
 *****************************************************************************
 */

static inline void
GdpTopicPrefixFree(gpointer data) // IN
{
   g_debug("%s: Freeing buffer for topic prefix \"%s\".\n",
           __FUNCTION__, (const char *) data);
   free(data);
}


/*
 *****************************************************************************
 * GdpHistoryRequestFree --
 *
 * Frees history request resources.
 *
 * @param[in] request  History request pointer
 *
 *****************************************************************************
 */

static inline void
GdpHistoryRequestFree(HistoryRequest *request) // IN
{
   g_debug("%s: Entering ...\n", __FUNCTION__);

   if (request->topicPrefixes != NULL) {
      g_ptr_array_free(request->topicPrefixes, TRUE);
   }

   free(request);
}


/*
 *****************************************************************************
 * GdpHistoryRequestFreeGFunc --
 *
 * GFunc called by GdpTaskClearHistoryRequestQueue.
 *
 * @param[in] item_data  History request pointer
 * @param[in] user_data  Not used
 *
 *****************************************************************************
 */

static void
GdpHistoryRequestFreeGFunc(gpointer item_data, // IN
                           gpointer user_data) // IN
{
   GdpHistoryRequestFree((HistoryRequest *) item_data);
}


/*
 *****************************************************************************
 * GdpTaskClearHistoryRequestQueue --
 *
 * Removes all the elements in history request queue.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static inline void
GdpTaskClearHistoryRequestQueue(TaskContext *taskCtx) // IN/OUT
{
   g_queue_foreach(&taskCtx->requests, GdpHistoryRequestFreeGFunc, NULL);
   g_queue_clear(&taskCtx->requests);
}


/*
 ******************************************************************************
 * GdpGetHistoryCacheSizeLimit --
 *
 * Gets history cache buffer size limit from tools config.
 *
 ******************************************************************************
 */

static guint32
GdpGetHistoryCacheSizeLimit()
{
   gint sizeLimit;

   sizeLimit = GDP_CONFIG_GET_INT(CONFNAME_GDP_CACHE_SIZE,
                                  GDP_DEFAULT_CACHE_SIZE_LIMIT);
   if (sizeLimit != 0 &&
       (sizeLimit < GDP_MIN_CACHE_SIZE_LIMIT ||
        sizeLimit > GDP_MAX_CACHE_SIZE_LIMIT)) {
      g_warning("%s: Configured history cache buffer size limit %d "
                "exceeds range, set to default value %d.\n",
                __FUNCTION__, sizeLimit, GDP_DEFAULT_CACHE_SIZE_LIMIT);
      sizeLimit = GDP_DEFAULT_CACHE_SIZE_LIMIT;
   }

   return (guint32) sizeLimit;
}


/*
 ******************************************************************************
 * GdpGetHistoryCacheCountLimit --
 *
 * Gets history cache item count limit from tools config.
 *
 ******************************************************************************
 */

static guint32
GdpGetHistoryCacheCountLimit()
{
   gint countLimit;

   countLimit = GDP_CONFIG_GET_INT(CONFNAME_GDP_CACHE_COUNT,
                                   GDP_DEFAULT_CACHE_COUNT_LIMIT);
   if (countLimit < GDP_MIN_CACHE_COUNT_LIMIT ||
       countLimit > GDP_MAX_CACHE_COUNT_LIMIT) {
      g_warning("%s: Configured history cache item count limit %d "
                "exceeds range, set to default value %d.\n",
                __FUNCTION__, countLimit, GDP_DEFAULT_CACHE_COUNT_LIMIT);
      countLimit = GDP_DEFAULT_CACHE_COUNT_LIMIT;
   }

   return (guint32) countLimit;
}


/*
 ******************************************************************************
 * GdpTaskIsHistoryCacheEnabled --
 *
 * Checks if history cache is enabled.
 *
 * @param[in] taskCtx  The task context
 *
 * @return TRUE if history cache buffer size limit is not zero.
 * @return FALSE otherwise.
 *
 ******************************************************************************
 */

static inline Bool
GdpTaskIsHistoryCacheEnabled(TaskContext *taskCtx) // IN
{
   return taskCtx->cache.sizeLimit != 0 ? TRUE : FALSE;
}


/*
 *****************************************************************************
 * GdpHistoryCacheItemFree --
 *
 * Frees history cache item resources.
 *
 * @param[in] item  History cache item pointer
 *
 *****************************************************************************
 */

static void
GdpHistoryCacheItemFree(HistoryCacheItem *item) // IN
{
   g_debug("%s: Entering ...\n", __FUNCTION__);
   ASSERT(item != NULL);
   free(item->topic);
   free(item->token);
   free(item->category);
   free(item->data);
   free(item);
}


/*
 *****************************************************************************
 * GdpHistoryCacheItemFreeGFunc --
 *
 * GFunc called by GdpTaskClearHistoryCacheQueue.
 *
 * @param[in] item_data  History cache item pointer
 * @param[in] user_data  Not used
 *
 *****************************************************************************
 */

static void
GdpHistoryCacheItemFreeGFunc(gpointer item_data, // IN
                             gpointer user_data) // IN
{
   GdpHistoryCacheItemFree((HistoryCacheItem *) item_data);
}


/*
 *****************************************************************************
 * GdpTaskClearHistoryCacheQueue --
 *
 * Removes all the elements in history cache queue.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static inline void
GdpTaskClearHistoryCacheQueue(TaskContext *taskCtx) // IN/OUT
{
   g_queue_foreach(&taskCtx->cache.queue, GdpHistoryCacheItemFreeGFunc, NULL);
   g_queue_clear(&taskCtx->cache.queue);

}


/*
 *****************************************************************************
 * GdpTaskDeleteHistoryCacheTail --
 *
 * Deletes the tail of history cache queue and removes its reference.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static void
GdpTaskDeleteHistoryCacheTail(TaskContext *taskCtx) // IN/OUT
{
   HistoryCacheItem *item;

   ASSERT(!g_queue_is_empty(&taskCtx->cache.queue));

   if (taskCtx->cache.currentLink ==
       g_queue_peek_tail_link(&taskCtx->cache.queue)) {
      taskCtx->cache.currentLink = NULL;
   }

   item = (HistoryCacheItem *) g_queue_pop_tail(&taskCtx->cache.queue);
   ASSERT(item != NULL);
   taskCtx->cache.size -= item->itemSize;
   GdpHistoryCacheItemFree(item);
}


/*
 ******************************************************************************
 * GdpTaskHistoryCachePushItem --
 *
 * Pushes the published guest data item into history cache queue.
 *
 * @param[in,out]      taskCtx     The task context
 * @param[in]          createTime  UTC timestamp, in number of micro-
 *                                 seconds since January 1, 1970 UTC.
 * @param[in]          topic       Topic
 * @param[in,optional] token       Token, can be NULL
 * @param[in,optional] category    Category, can be NULL that defaults to
 *                                 "application"
 * @param[in]          data        Buffer containing data to publish
 * @param[in]          dataLen     Buffer length
 * @param[in]          requireSubs Subscriber required flag
 *
 ******************************************************************************
 */

static void
GdpTaskHistoryCachePushItem(TaskContext *taskCtx,  // IN/OUT
                            gint64 createTime,     // IN
                            const gchar *topic,    // IN
                            const gchar *token,    // IN
                            const gchar *category, // IN
                            const gchar *data,     // IN
                            guint32 dataLen,       // IN
                            gboolean requireSubs)  // IN
{
   HistoryCacheItem *item;

   ASSERT(topic != NULL);
   ASSERT(data != NULL && dataLen > 0);
   ASSERT(GdpTaskIsHistoryCacheEnabled(taskCtx));

   item = (HistoryCacheItem *) Util_SafeMalloc(sizeof(HistoryCacheItem));
   item->createTime = createTime;
   item->topic = Util_SafeStrdup(topic);
   item->token = Util_SafeStrdup(token);
   item->category = Util_SafeStrdup(category);
   item->data = (gchar *) Util_SafeMalloc(dataLen);
   Util_Memcpy(item->data, data, dataLen);
   item->dataLen = dataLen;
   item->requireSubs = requireSubs;

   item->cacheTime = g_get_monotonic_time();
   item->itemSize = ((guint32) sizeof(HistoryCacheItem))
                    + GDP_STR_SIZE(item->topic)
                    + GDP_STR_SIZE(item->token)
                    + GDP_STR_SIZE(item->category)
                    + item->dataLen;

   while (taskCtx->cache.size + item->itemSize >
             taskCtx->cache.sizeLimit ||
          g_queue_get_length(&taskCtx->cache.queue) >=
             taskCtx->cache.countLimit) {
      GdpTaskDeleteHistoryCacheTail(taskCtx);
   }

   g_queue_push_head(&taskCtx->cache.queue, item);
   taskCtx->cache.size += item->itemSize;

   g_debug("%s: Current history cache size in bytes: %u, item count: %u.\n",
           __FUNCTION__, taskCtx->cache.size,
           g_queue_get_length(&taskCtx->cache.queue));
}


/*
 ******************************************************************************
 * GdpGetFormattedUtcTime --
 *
 * Converts UTC time to string in format like "2018-02-22T21:17:38.517Z".
 *
 * The caller must free the return value using g_free.
 *
 * @param[in] utcTime  Real wall-clock time
 *                     Number of microseconds since January 1, 1970 UTC.
 *
 * @return Formatted timestamp string on success.
 * @return NULL otherwise.
 *
 ******************************************************************************
 */

static gchar *
GdpGetFormattedUtcTime(gint64 utcTime) // IN
{
   gchar *retVal = NULL;
   GDateTime *utcDateTime;

   utcDateTime = g_date_time_new_from_unix_utc(utcTime / USEC_PER_SECOND);
   if (utcDateTime != NULL) {
      gchar *dateToSecond; // YYYY-MM-DDTHH:MM:SS

      dateToSecond = g_date_time_format(utcDateTime, "%FT%T");
      if (dateToSecond != NULL) {
         gint msec; // milliseconds

         msec = (utcTime % USEC_PER_SECOND) / USEC_PER_MILLISECOND;
         retVal = g_strdup_printf("%s.%03dZ", dateToSecond, msec);
         g_free(dateToSecond);
      }

      g_date_time_unref(utcDateTime);
   }

   return retVal;
}


/*
 ******************************************************************************
 * GdpTaskBuildPacket --
 *
 * Builds JSON packet in the task context.
 *
 * @param[in,out]      taskCtx      The task context
 * @param[in]          createTime   UTC timestamp, in number of micro-
 *                                  seconds since January 1, 1970 UTC.
 * @param[in]          topic        Topic
 * @param[in,optional] token        Token, can be NULL
 * @param[in,optional] category     Category, can be NULL that defaults to
 *                                  "application"
 * @param[in]          data         Buffer containing data to publish
 * @param[in]          dataLen      Buffer length
 * @param[in]          requireSubs  Indicate whether the publisher requires or
 *                                  not that subscribers be present. Expected to
 *                                  be false for history data. Ignored by pre-V2
 *                                  protocol host.
 * @param[in,optional] subscribers  For history data only, NULL for new data
 *
 * @return GDP_ERROR_SUCCESS on success.
 * @return Other GdpError code otherwise.
 *
 ******************************************************************************
 */

static GdpError
GdpTaskBuildPacket(TaskContext *taskCtx,     // IN/OUT
                   gint64 createTime,        // IN
                   const gchar *topic,       // IN
                   const gchar *token,       // IN, OPTIONAL
                   const gchar *category,    // IN, OPTIONAL
                   const gchar *data,        // IN
                   guint32 dataLen,          // IN
                   const gboolean requireSubs, // IN
                   const gchar *subscribers) // IN, OPTIONAL
{
   gchar base64Data[GDP_MAX_PACKET_LEN + 1]; // Add a space for NULL
   gchar *subscribersLine = NULL;
   gchar *subscribersRequiredLine = NULL;
   gchar *formattedTime;
   GdpError gdpErr;

   ASSERT(topic != NULL);

   if (data == NULL || dataLen == 0) {
      /*
       * ZeroData: Empty/no data is allowed only if requireSubs=true AND
       *           gPublishState.cacheData=false.
       */
      ASSERT(requireSubs && !gPublishState.cacheData); /* see: GdpPublish() */
      base64Data[0] = '\0';  // ZeroData: set empty payload.
   } else if (!Base64_Encode(data, dataLen,
                             base64Data, sizeof base64Data, NULL)) {
      g_info("%s: Base64_Encode failed, data length is %u.\n",
             __FUNCTION__, dataLen);
      return GDP_ERROR_DATA_SIZE;
   }

   if (subscribers != NULL && *subscribers != '\0') {
      subscribersLine = g_strdup_printf(GDP_PACKET_JSON_LINE_SUBSCRIBERS,
                                        subscribers);
   }

   // subscribers required is optional in (V2).
   // since: GDP_PROTOCOL_VERSIONED_VERSION
   if (requireSubs) {
      subscribersRequiredLine =
         g_strdup_printf(GDP_PACKET_JSON_LINE_REQUIRE_SUBS,
                         "true");
   }

   formattedTime = GdpGetFormattedUtcTime(createTime);

   ASSERT(taskCtx->packet == NULL);
   taskCtx->packet = g_strdup_printf(GDP_PACKET_JSON,
                                     ++taskCtx->sequence,
                                     (uint64) GDP_PROTOCOL_VERSION,
                                     subscribersLine != NULL ?
                                        subscribersLine : "",
                                     subscribersRequiredLine != NULL ?
                                        subscribersRequiredLine : "",
                                     formattedTime != NULL ?
                                        formattedTime : "",
                                     topic,
                                     token != NULL ?
                                        token : "",
                                     category != NULL ?
                                        category : "application",
                                     base64Data);
   taskCtx->packetLen = (guint32) strlen(taskCtx->packet);
   if (taskCtx->packetLen > GDP_MAX_PACKET_LEN) {
      g_info("%s: Packet length (%u) exceeds maximum limit (%u).\n",
             __FUNCTION__, taskCtx->packetLen, GDP_MAX_PACKET_LEN);
      GdpTaskDestroyPacket(taskCtx);
      gdpErr = GDP_ERROR_DATA_SIZE;
   } else {
      gdpErr = GDP_ERROR_SUCCESS;
   }

   g_free(formattedTime);
   g_free(subscribersLine);
   g_free(subscribersRequiredLine);
   return gdpErr;
}


/*
 *****************************************************************************
 * GdpTaskDestroyPacket --
 *
 * Destroys JSON packet in the task context.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static inline void
GdpTaskDestroyPacket(TaskContext *taskCtx) // IN/OUT
{
   g_free(taskCtx->packet);
   taskCtx->packet = NULL;
   taskCtx->packetLen = 0;
}


/*
 ******************************************************************************
 * GdpTaskOkToSend --
 *
 * Checks if current time is OK to send JSON packet to host side gdp daemon.
 *
 * @param[in] taskCtx  The task context
 *
 * @return TRUE if current time has passed the task context sendAfter time.
 * @return FALSE otherwise.
 *
 ******************************************************************************
 */

static inline Bool
GdpTaskOkToSend(TaskContext *taskCtx) // IN
{
   return g_get_monotonic_time() >= taskCtx->sendAfter ?
             TRUE : FALSE;
}


/*
 ******************************************************************************
 * GdpTaskSendPacket --
 *
 * Sends JSON packet in the task context to host side gdp daemon.
 *
 * Datagram send is not buffered, it will not return EWOULDBLOCK.
 * If host daemon is not running, error EHOSTUNREACH is returned.
 *
 * @param[in,out] taskCtx  The task context
 *
 * @return GDP_ERROR_SUCCESS on success.
 * @return Other GdpError code otherwise.
 *
 ******************************************************************************
 */

static GdpError
GdpTaskSendPacket(TaskContext *taskCtx) // IN/OUT
{
   struct sockaddr_vm destAddr;
   GdpError gdpErr;

   ASSERT(gPluginState.sock != INVALID_SOCKET);
   ASSERT(taskCtx->packet != NULL && taskCtx->packetLen > 0);

   memset(&destAddr, 0, sizeof destAddr);
   destAddr.svm_family = gPluginState.vmciFamily;
   destAddr.svm_cid = VMCI_HOST_CONTEXT_ID;
   destAddr.svm_port = GDPD_RECV_PORT; // No htons

   gdpErr = GdpSendTo(gPluginState.sock, taskCtx->packet,
                      (int) taskCtx->packetLen, &destAddr);
   if (gdpErr == GDP_ERROR_SUCCESS) {
      /*
       * Updates sendAfter for next send, phase 1 / 2.
       */
      taskCtx->sendAfter = g_get_monotonic_time();
      taskCtx->timeoutAt = taskCtx->sendAfter +
                           GDP_WAIT_RESULT_TIMEOUT * USEC_PER_MILLISECOND;
   } else {
      GdpTaskDestroyPacket(taskCtx);
      taskCtx->timeoutAt = GDP_TIMEOUT_AT_INFINITE;
   }

   return gdpErr;
}


/*
 *****************************************************************************
 * GdpMatchTopicPrefixes --
 *
 * Matches a topic against prefixes.
 *
 * @param[in] topic          Topic
 * @param[in] topicPrefixes  Topic prefixes
 *
 * @return TRUE if topic matches any prefix.
 * @return FALSE otherwise.
 *
 *****************************************************************************
 */

static Bool
GdpMatchTopicPrefixes(const gchar *topic,             // IN
                      const GPtrArray *topicPrefixes) // IN
{
   guint index;

   ASSERT(topic != NULL);
   ASSERT(topicPrefixes != NULL);

   for (index = 0; index < topicPrefixes->len; index++) {
      const gchar *prefix = g_ptr_array_index(topicPrefixes, index);
      guint prefixLen = (guint) strlen(prefix);

      if (strncmp(topic, prefix, prefixLen) == 0) {
         if (topic[prefixLen] == '.' || topic[prefixLen] == '\0') {
            return TRUE;
         }
      }
   }

   return FALSE;
}


/*
 *****************************************************************************
 * GdpTaskUpdateHistoryCachePointerAndGetSubscribers --
 *
 * Updates history cache pointer and gets subscribers of the pointed item.
 *
 * @param[in,out] taskCtx  The task context
 *
 * @return A string of subscription IDs separated by comma
           (to be freed by caller),
 *         or NULL if no match.
 *
 *****************************************************************************
 */

static gchar *
GdpTaskUpdateHistoryCachePointerAndGetSubscribers(
   TaskContext *taskCtx) // IN/OUT
{
   gchar *subscribers = NULL;

   if (taskCtx->cache.currentLink == NULL) {
      /*
       * Starts with the earliest history cache link.
       */
      taskCtx->cache.currentLink = g_queue_peek_tail_link(
                                      &taskCtx->cache.queue);
   }

   while (taskCtx->cache.currentLink != NULL &&
          !g_queue_is_empty(&taskCtx->requests)) {
      HistoryCacheItem *item;
      GList *requestList;

      item = (HistoryCacheItem *) taskCtx->cache.currentLink->data;
      requestList = g_queue_peek_tail_link(&taskCtx->requests);
      while (requestList != NULL) {
         HistoryRequest *request = (HistoryRequest *) requestList->data;
         Bool requestDone = FALSE;

         if (item->cacheTime > request->endCacheTime) {
            requestDone = TRUE;
         } else if (request->beginCacheTime < item->cacheTime &&
                    item->cacheTime <= request->endCacheTime) {
            if (request->topicPrefixes == NULL ||
                GdpMatchTopicPrefixes(item->topic, request->topicPrefixes)) {
               if (subscribers == NULL) {
                  subscribers = g_strdup_printf("%" G_GUINT64_FORMAT,
                                                request->id);
               } else {
                  gchar *subscribersNew;
                  subscribersNew = g_strdup_printf("%s,%" G_GUINT64_FORMAT,
                                                   subscribers, request->id);
                  g_free(subscribers);
                  subscribers = subscribersNew;
               }
            }

            if (item->cacheTime == request->endCacheTime) {
               requestDone = TRUE;
            } else {
               request->beginCacheTime = item->cacheTime;
            }
         }

         if (requestDone) {
            GList *requestListPrev = requestList->prev;
            GdpHistoryRequestFree(request);
            g_queue_delete_link(&taskCtx->requests, requestList);
            requestList = requestListPrev;
         } else {
            requestList = requestList->prev;
         }
      }

      if (subscribers != NULL) {
         break;
      }

      taskCtx->cache.currentLink = taskCtx->cache.currentLink->prev;
   }

   return subscribers;
}


/*
 *****************************************************************************
 * GdpTaskPublishHistory --
 *
 * Publishes cached history guest data.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static void
GdpTaskPublishHistory(TaskContext *taskCtx) // IN/OUT
{
   GdpError gdpErr;
   gchar *subscribers;
   HistoryCacheItem *item;

   g_debug("%s: Entering ...\n", __FUNCTION__);

   ASSERT(taskCtx->mode == GDP_TASK_MODE_NONE &&
          taskCtx->state == GDP_TASK_STATE_IDLE);

   subscribers = GdpTaskUpdateHistoryCachePointerAndGetSubscribers(taskCtx);
   if (subscribers == NULL) {
      g_debug("%s: No history data to publish now.\n",
              __FUNCTION__);
      goto cleanup;
   }

   ASSERT(taskCtx->cache.currentLink != NULL);
   item = (HistoryCacheItem *) taskCtx->cache.currentLink->data;
   /*
    * Updates history cache pointer.
    */
   taskCtx->cache.currentLink = taskCtx->cache.currentLink->prev;

   gdpErr = GdpTaskBuildPacket(taskCtx,
                               item->createTime,
                               item->topic,
                               item->token,
                               item->category,
                               item->data,
                               item->dataLen,
                               item->requireSubs,
                               subscribers);
   if (gdpErr != GDP_ERROR_SUCCESS) {
      /*
       * Theoretically speaking, too many subscribers could cause JSON packet
       * length exceed maximum limit and fail GdpTaskBuildPacket with
       * GDP_ERROR_DATA_SIZE.
       */
      g_info("%s: Failed to build JSON packet for subscribers: [%s].\n",
             __FUNCTION__, subscribers);
      g_free(subscribers);
      goto cleanup;
   }
   g_free(subscribers);

   if (GdpTaskOkToSend(taskCtx)) {
      gdpErr = GdpTaskSendPacket(taskCtx);
      if (gdpErr != GDP_ERROR_SUCCESS) {
         g_info("%s: Failed to send history JSON packet.\n",
                __FUNCTION__);
         goto cleanup;
      }

      taskCtx->state = GDP_TASK_STATE_WAIT_FOR_RESULT1;
   } else {
      taskCtx->timeoutAt = taskCtx->sendAfter;
      taskCtx->state = GDP_TASK_STATE_WAIT_TO_SEND;
   }

   taskCtx->mode = GDP_TASK_MODE_HISTORY;
   g_debug("%s: Updated mode=%d, state=%d.\n",
           __FUNCTION__, taskCtx->mode, taskCtx->state);
   return;

cleanup:
   taskCtx->cache.currentLink = NULL;
   GdpTaskClearHistoryRequestQueue(taskCtx);
}


/*
 *****************************************************************************
 * GdpTaskProcessConfigChange --
 *
 * Processes config change.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static void
GdpTaskProcessConfigChange(TaskContext *taskCtx) // IN/OUT
{
   guint32 sizeLimit = GdpGetHistoryCacheSizeLimit();
   guint32 countLimit = GdpGetHistoryCacheCountLimit();

   if (taskCtx->cache.sizeLimit == sizeLimit &&
       taskCtx->cache.countLimit == countLimit) {
      return;
   }

   g_debug("%s: Current history cache buffer size limit: %u, new value: %u.\n",
           __FUNCTION__, taskCtx->cache.sizeLimit, sizeLimit);
   taskCtx->cache.sizeLimit = sizeLimit;

   g_debug("%s: Current history cache item count limit: %u, new value: %u.\n",
           __FUNCTION__, taskCtx->cache.countLimit, countLimit);
   taskCtx->cache.countLimit = countLimit;

   while (taskCtx->cache.size > taskCtx->cache.sizeLimit ||
          g_queue_get_length(&taskCtx->cache.queue) >
             taskCtx->cache.countLimit) {
      GdpTaskDeleteHistoryCacheTail(taskCtx);
   }
}


/*
 ******************************************************************************
 * GdpJsonIsTokenOfKey --
 *
 * Checks if the JSON token represents the key.
 *
 * @param[in]  json   The JSON text
 * @param[in]  token  Token
 * @param[in]  key    Key name
 *
 * @return TRUE if token represents the key.
 * @return FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
GdpJsonIsTokenOfKey(const char *json,       // IN
                    const jsmntok_t *token, // IN
                    const char *key)        // IN
{
   int tokenLen = token->end - token->start;
   if (token->type == JSMN_STRING &&
       token->size == 1 && // The token represents a key
       (int) strlen(key) == tokenLen &&
       strncmp(json + token->start, key, tokenLen) == 0) {
      return TRUE;
   }
   return FALSE;
}


/*
 ******************************************************************************
 * GdpJsonIsPublishResult --
 *
 * Checks if the JSON text represents a publish result.
 *
 * @param[in]  json     The JSON text
 * @param[in]  tokens   Token array
 * @param[in]  count    Token array count
 * @param[out] request  Pointer to publish result
 *
 * @return TRUE if json represents a publish result.
 * @return FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
GdpJsonIsPublishResult(const char *json,        // IN
                       const jsmntok_t *tokens, // IN
                       int count,               // IN
                       PublishResult *result)   // OUT
{
   int index;
   int requiredKeysCount = 0;
   gchar *diagnosis = NULL;
   Bool handledRateLimit = FALSE;
   Bool handledStatus = FALSE;

   result->errorId = GDP_ERR_MAX; /* result usually zeroed in caller */

   /*
    * Loops over all keys of the root object.
    */
   for (index = 1; index < count; index++) {
      int tokenLen;

      if (GdpJsonIsTokenOfKey(json, &tokens[index], GDP_RESULT_SEQUENCE)) {
         ASSERT(result->sequence == 0); /* duplicate check */
         requiredKeysCount++;
         index++;
         result->sequence = g_ascii_strtoull(json + tokens[index].start,
                                             NULL, 10);
         ASSERT(result->sequence != 0); /* no! */
      } else if (GdpJsonIsTokenOfKey(json, &tokens[index],
                                     GDP_RESULT_VERSION)) {
         ASSERT(result->version == 0); /* duplicate check */
         requiredKeysCount++;
         index++;
         result->version = g_ascii_strtoull(json + tokens[index].start,
                                            NULL, 10);
         ASSERT(result->version > 0); /* version == 0 is bad */
      } else if (GdpJsonIsTokenOfKey(json, &tokens[index],
                                     GDP_RESULT_STATUS)) {
         ASSERT(handledStatus == FALSE); /* duplicate check */
         handledStatus = TRUE;
         requiredKeysCount++;
         index++;
         tokenLen = tokens[index].end - tokens[index].start;
         /*
          * V1 status: 'ok' or V2 status: 'true'
          */
         if ((int) strlen(GDP_RESULT_STATUS_OK) == tokenLen &&
             strncmp(json + tokens[index].start,
                     GDP_RESULT_STATUS_OK, tokenLen) == 0) {
            /*
             * Success V1
             */
            result->statusOk = TRUE;
         } else if (4 == tokenLen &&
                    strncmp(json + tokens[index].start,
                            "true", tokenLen) == 0) {
            /*
             * Success V2
             */
            result->statusOk = TRUE;
         } else {
            /*
             * Anything else is error response
             */
            result->statusOk = FALSE;
         }
      } else if (GdpJsonIsTokenOfKey(json, &tokens[index],
                                     GDP_RESULT_DIAGNOSIS)) {
         ASSERT(diagnosis == NULL); /* duplicate check */
         index++;
         tokenLen = tokens[index].end - tokens[index].start;
         diagnosis = g_strndup(json + tokens[index].start, tokenLen);
      } else if (GdpJsonIsTokenOfKey(json, &tokens[index],
                                     GDP_RESULT_RATE_LIMIT)) {
         ASSERT(handledRateLimit == FALSE); /* duplicate check */
         handledRateLimit = TRUE;
         requiredKeysCount++;
         index++;
         result->rateLimit = atoi(json + tokens[index].start);
      } else if (GdpJsonIsTokenOfKey(json, &tokens[index],
                                     GDP_RESULT_ERROR_ID)) {
         gchar *errorIdStr = NULL;
         ASSERT(result->errorId == GDP_ERR_MAX); /* duplicate check */
         /*
          * Forward compatible error-id lookup, set to a valid error
          */
         index++;
         tokenLen = tokens[index].end - tokens[index].start;
         errorIdStr = g_strndup(json + tokens[index].start, tokenLen);
         result->errorId = GetGdpErrorFromErrorId(errorIdStr);
         if (result->errorId == GDP_ERR_MAX) {
            /*
             * Unknown error-id error
             */
            g_warning("%s: Unknown error-id: '%s' converting to '%s'",
                      __FUNCTION__, errorIdStr, gdpErrIds[GDP_ERROR_GENERAL]);
            result->errorId = GDP_ERROR_GENERAL;
         }
         g_free(errorIdStr);
      } else if (GdpJsonIsTokenOfKey(json, &tokens[index],
                                     GDP_RESULT_ERROR_TEXT)) {
         ASSERT(result->errorText == NULL); /* duplicate check */
         index++;
         tokenLen = tokens[index].end - tokens[index].start;
         result->errorText = g_strndup(json + tokens[index].start, tokenLen);
      }
   } /* for index */

   /*
    * Make sure requiredKeysCount for Unversioned or versioned protocol matches
    * their respective expectations.
    */
   if (result->version < GDP_PROTOCOL_VERSIONED_VERSION &&
       requiredKeysCount == GDP_RESULT_REQUIRED_KEYS) {
      /*
       * Assign diagnosis for unversioned protocol
       */
      result->diagnosis = diagnosis;
      return TRUE;
   } else if (result->version >= GDP_PROTOCOL_VERSIONED_VERSION &&
              requiredKeysCount == GDP_RESULT_V2_REQUIRED_KEYS) {
      /*
       * diagnosis not used for versioned protocol, clean it up;
       * error-text is assigned during parsing when present.
       */
      g_free(diagnosis);
      return TRUE;
   } else {
      g_free(diagnosis);
      if (result->errorText != NULL) {
         g_free(result->errorText);
         result->errorText = NULL;
      }
      return FALSE;
   }
}


/*
 *****************************************************************************
 * GdpTaskProcessPublishResult --
 *
 * Processes publish result.
 *
 * @param[in,out] taskCtx  The task context
 * @param[in]     result   Publish result
 *
 *****************************************************************************
 */

static void
GdpTaskProcessPublishResult(TaskContext *taskCtx,        // IN/OUT
                            const PublishResult *result) // IN
{
   g_debug("%s: Entering ...\n", __FUNCTION__);

   if (taskCtx->mode == GDP_TASK_MODE_NONE ||
       (taskCtx->state != GDP_TASK_STATE_WAIT_FOR_RESULT1 &&
        taskCtx->state != GDP_TASK_STATE_WAIT_FOR_RESULT2)) {
      g_info("%s: Publish result not expected at mode=%d, state=%d.\n",
             __FUNCTION__, taskCtx->mode, taskCtx->state);
      return;
   }

   if (taskCtx->sequence != result->sequence) {
      g_info("%s: Publish result sequence number not match.\n",
             __FUNCTION__);
      return;
   }

   if (!result->statusOk) {
      if (result->version >= GDP_PROTOCOL_VERSIONED_VERSION) {
         // V2 and up; use result->errorId and errorText
         g_info("%s: Publish failed: Id(%d), Message: %s\n", __FUNCTION__,
                result->errorId, result->errorText ? result->errorText : "");
      } else {
         g_info("%s: Publish failed: %s\n", __FUNCTION__,
                result->diagnosis ? result->diagnosis : "");
      }
   }

   if (taskCtx->mode == GDP_TASK_MODE_PUBLISH) {
      Bool addToHistory = FALSE;

      if (result->statusOk) {
         gPublishState.gdpErr = GDP_ERROR_SUCCESS;
         addToHistory = TRUE;
      } else if (result->version >= GDP_PROTOCOL_VERSIONED_VERSION) {
         // V2 and up; use result->errorId
         gPublishState.gdpErr = result->errorId;

         if (gPublishState.requireSubs &&
             result->errorId == GDP_ERROR_NO_SUBSCRIBERS) {
            // Add Data Message to history on no-subscriber error.
            addToHistory = TRUE;
         }
      } else {
         // Unversioned/original - default error response.
         gPublishState.gdpErr = GDP_ERROR_INVALID_DATA;
      }

      if (addToHistory &&
          GdpTaskIsHistoryCacheEnabled(taskCtx) &&
          gPublishState.cacheData) {
         GdpTaskHistoryCachePushItem(taskCtx,
                                     gPublishState.createTime,
                                     gPublishState.topic,
                                     gPublishState.token,
                                     gPublishState.category,
                                     gPublishState.data,
                                     gPublishState.dataLen,
                                     gPublishState.requireSubs);
      }

      GdpSetEvent(gPublishState.eventGetResult);
   }

   GdpTaskDestroyPacket(taskCtx);
   if (result->rateLimit > 0) {
      /*
       * Updates sendAfter for next send, phase 2 / 2.
       */
      taskCtx->sendAfter += (USEC_PER_SECOND / result->rateLimit);
   }
   taskCtx->timeoutAt = GDP_TIMEOUT_AT_INFINITE;
   taskCtx->mode = GDP_TASK_MODE_NONE;
   taskCtx->state = GDP_TASK_STATE_IDLE;
   g_debug("%s: Reset mode=%d, state=%d.\n",
           __FUNCTION__, taskCtx->mode, taskCtx->state);
}


/*
 ******************************************************************************
 * GdpJsonIsHistoryRequest --
 *
 * Checks if the JSON text represents a history request.
 *
 * @param[in]  json     The JSON text
 * @param[in]  tokens   Token array
 * @param[in]  count    Token array count
 * @param[out] request  Pointer to history request
 *
 * @return TRUE if json represents a history request.
 * @return FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
GdpJsonIsHistoryRequest(const char *json,        // IN
                        const jsmntok_t *tokens, // IN
                        int count,               // IN
                        HistoryRequest *request) // OUT
{
   int index;
   int requiredKeys = GDP_HISTORY_REQUEST_REQUIRED_KEYS;
   guint64 pastSeconds = 0;

   request->topicPrefixes = NULL;

   /*
    * Loops over all keys of the root object
    */
   for (index = 1; index < count; index++) {
      if (GdpJsonIsTokenOfKey(json, &tokens[index],
                              GDP_HISTORY_REQUEST_PAST_SECONDS)) {
         requiredKeys--;
         index++;
         pastSeconds = g_ascii_strtoull(json + tokens[index].start, NULL, 10);
      } else if (GdpJsonIsTokenOfKey(json, &tokens[index],
                                     GDP_HISTORY_REQUEST_ID)) {
         requiredKeys--;
         index++;
         request->id = g_ascii_strtoull(json + tokens[index].start, NULL, 10);
      } else if (GdpJsonIsTokenOfKey(json, &tokens[index],
                                     GDP_HISTORY_REQUEST_TOPIC_PREFIXES) &&
                 tokens[index + 1].type == JSMN_ARRAY) {
         int prefixCount;
         int prefixIndex;

         prefixCount = tokens[index + 1].size;
         request->topicPrefixes = g_ptr_array_new_full(prefixCount,
                                                       GdpTopicPrefixFree);
         index += 2;
         for (prefixIndex = 0; prefixIndex < prefixCount; prefixIndex++) {
            const jsmntok_t *token = &tokens[index + prefixIndex];
            g_ptr_array_add(request->topicPrefixes,
                            Util_SafeStrndup(json + token->start,
                                             token->end - token->start));
         }

         index += (prefixCount - 1);
      }
   }

   if (requiredKeys == 0) {
      request->endCacheTime = g_get_monotonic_time();
      request->beginCacheTime = request->endCacheTime -
                                (gint64)pastSeconds * USEC_PER_SECOND;
      return TRUE;
   }

   if (request->topicPrefixes != NULL) {
      g_ptr_array_free(request->topicPrefixes, TRUE);
      request->topicPrefixes = NULL;
   }
   return FALSE;
}


/*
 *****************************************************************************
 * GdpTaskProcessHistoryRequest --
 *
 * Validates new history request, pushes the request to queue and
 * resets history cache pointer.
 *
 * @param[in,out] taskCtx  The task context
 * @param[in,out] request  New history request
 *
 *****************************************************************************
 */

static void
GdpTaskProcessHistoryRequest(TaskContext *taskCtx,    // IN/OUT
                             HistoryRequest *request) // IN/OUT
{
   HistoryRequest *requestCopy;

   g_debug("%s: Entering ...\n", __FUNCTION__);

   if (!GdpTaskIsHistoryCacheEnabled(taskCtx)) {
      g_info("%s: History cache not enabled.\n", __FUNCTION__);
      goto fail;
   }

   if (request->beginCacheTime >= request->endCacheTime) {
      g_info("%s: Invalid history request.\n", __FUNCTION__);
      goto fail;
   }

   if (request->beginCacheTime < 0) {
      request->beginCacheTime = 0;
   }

   requestCopy = (HistoryRequest *) Util_SafeMalloc(sizeof *request);
   requestCopy->beginCacheTime = request->beginCacheTime;
   requestCopy->endCacheTime = request->endCacheTime;
   requestCopy->id = request->id;
   requestCopy->topicPrefixes = request->topicPrefixes;
   request->topicPrefixes = NULL;

   /*
    * Note: each request comes with a unique subscription ID.
    */
   g_queue_push_head(&taskCtx->requests, requestCopy);
   /*
    * Resets history cache pointer.
    */
   taskCtx->cache.currentLink = NULL;

   return;

fail:
   if (request->topicPrefixes != NULL) {
      g_ptr_array_free(request->topicPrefixes, TRUE);
      request->topicPrefixes = NULL;
   }
}


/*
 *****************************************************************************
 * GdpTaskProcessNetwork --
 *
 * Processes the network event.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static void
GdpTaskProcessNetwork(TaskContext *taskCtx) // IN/OUT
{
   GdpError gdpErr;
   char buf[GDP_MAX_PACKET_LEN + 1]; // Adds a space for NULL
   int bufLen = (int) sizeof buf - 1;
   struct sockaddr_vm srcAddr;
   unsigned int numTokens;
   jsmntok_t *tokens;
   jsmn_parser parser;
   int retVal;
   Bool isPublishResult;
   Bool isHistoryRequest;
   PublishResult result = { 0 };
   HistoryRequest request = { 0 };

   g_debug("%s: Entering ...\n", __FUNCTION__);

   gdpErr = GdpRecvFrom(gPluginState.sock, buf, &bufLen, &srcAddr);
   if (gdpErr != GDP_ERROR_SUCCESS || bufLen <= 0) {
      return;
   }

   if (srcAddr.svm_cid != VMCI_HOST_CONTEXT_ID) {
      g_info("%s: Unexpected source svm_cid: %u.\n",
             __FUNCTION__, srcAddr.svm_cid);
      return;
   }

   buf[bufLen] = '\0';

   jsmn_init(&parser);

   numTokens = GDP_TOKENS_PER_ALLOC;
   tokens = Util_SafeMalloc(numTokens * sizeof *tokens);
   while ((retVal = jsmn_parse(&parser, buf, bufLen, tokens, numTokens))
          == JSMN_ERROR_NOMEM) {
      numTokens += GDP_TOKENS_PER_ALLOC;
      tokens = Util_SafeRealloc(tokens, numTokens * sizeof *tokens);
   }

   if (retVal < 0) {
      g_info("%s: Error %d while parsing JSON:\n%s\n",
             __FUNCTION__, retVal, buf);
      free(tokens);
      return;
   }

   /*
    * The top-level element should be an object.
    */
   if (retVal < 1 || tokens[0].type != JSMN_OBJECT) {
      g_info("%s: Invalid JSON:\n%s\n", __FUNCTION__, buf);
      free(tokens);
      return;
   }

   isPublishResult = FALSE;
   isHistoryRequest = FALSE;
   if (srcAddr.svm_port == GDPD_RECV_PORT) {
      isPublishResult = GdpJsonIsPublishResult(buf, tokens,
                                               retVal, &result);
      if (!isPublishResult) {
         isHistoryRequest = GdpJsonIsHistoryRequest(buf, tokens,
                                                    retVal, &request);
      }
   } else {
      isHistoryRequest = GdpJsonIsHistoryRequest(buf, tokens,
                                                 retVal, &request);
      if (!isHistoryRequest) {
         isPublishResult = GdpJsonIsPublishResult(buf, tokens,
                                                  retVal, &result);
      }
   }

   if (isPublishResult) {
      GdpTaskProcessPublishResult(taskCtx, &result);
      g_free(result.diagnosis);
      g_free(result.errorText);
   } else if (isHistoryRequest) {
      g_debug("%s: Received history request:\n%s\n", __FUNCTION__, buf);
      GdpTaskProcessHistoryRequest(taskCtx, &request);
   } else {
      g_info("%s: Unknown JSON:\n%s\n", __FUNCTION__, buf);
   }

   free(tokens);
}


/*
 *****************************************************************************
 * GdpTaskProcessPublish --
 *
 * Processes the publish event.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static void
GdpTaskProcessPublish(TaskContext *taskCtx) // IN/OUT
{
   GdpError gdpErr;

   g_debug("%s: Entering ...\n", __FUNCTION__);

   ASSERT(taskCtx->mode != GDP_TASK_MODE_PUBLISH);

   if (taskCtx->mode != GDP_TASK_MODE_NONE) {
      g_debug("%s: Set publish pending.\n", __FUNCTION__);
      ASSERT(!taskCtx->publishPending);
      taskCtx->publishPending = TRUE;
      return;
   }

   ASSERT(taskCtx->state == GDP_TASK_STATE_IDLE);

   gdpErr = GdpTaskBuildPacket(taskCtx,
                               gPublishState.createTime,
                               gPublishState.topic,
                               gPublishState.token,
                               gPublishState.category,
                               gPublishState.data,
                               gPublishState.dataLen,
                               gPublishState.requireSubs,
                               NULL);
   if (gdpErr != GDP_ERROR_SUCCESS) {
      goto fail;
   }

   if (GdpTaskOkToSend(taskCtx)) {
      gdpErr = GdpTaskSendPacket(taskCtx);
      if (gdpErr != GDP_ERROR_SUCCESS) {
         goto fail;
      }

      taskCtx->state = GDP_TASK_STATE_WAIT_FOR_RESULT1;
   } else {
      taskCtx->timeoutAt = taskCtx->sendAfter;
      taskCtx->state = GDP_TASK_STATE_WAIT_TO_SEND;
   }

   taskCtx->mode = GDP_TASK_MODE_PUBLISH;
   g_debug("%s: Updated mode=%d, state=%d.\n",
           __FUNCTION__, taskCtx->mode, taskCtx->state);
   return;

fail:
   gPublishState.gdpErr = gdpErr;
   GdpSetEvent(gPublishState.eventGetResult);
}


/*
 *****************************************************************************
 * GdpTaskProcessTimeout --
 *
 * Processes wait timeout.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static void
GdpTaskProcessTimeout(TaskContext *taskCtx) // IN/OUT
{
   GdpError gdpErr;

   g_debug("%s: Entering ...\n", __FUNCTION__);

   ASSERT(taskCtx->mode != GDP_TASK_MODE_NONE &&
          taskCtx->state != GDP_TASK_STATE_IDLE);

   if (taskCtx->state == GDP_TASK_STATE_WAIT_TO_SEND ||
       taskCtx->state == GDP_TASK_STATE_WAIT_FOR_RESULT1) {
      gdpErr = GdpTaskSendPacket(taskCtx);
      if (gdpErr != GDP_ERROR_SUCCESS) {
         goto fail;
      }

      if (taskCtx->state == GDP_TASK_STATE_WAIT_TO_SEND) {
         taskCtx->state = GDP_TASK_STATE_WAIT_FOR_RESULT1;
      } else {
         taskCtx->state = GDP_TASK_STATE_WAIT_FOR_RESULT2;
      }

      g_debug("%s: Updated mode=%d, state=%d.\n",
              __FUNCTION__, taskCtx->mode, taskCtx->state);
   } else if (taskCtx->state == GDP_TASK_STATE_WAIT_FOR_RESULT2) {
      g_warning("%s: Wait for publish result timed out.\n", __FUNCTION__);
      GdpTaskDestroyPacket(taskCtx);
      taskCtx->timeoutAt = GDP_TIMEOUT_AT_INFINITE;
      gdpErr = GDP_ERROR_TIMEOUT;
      goto fail;
   } else {
      NOT_REACHED();
   }

   return;

fail:
   if (taskCtx->mode == GDP_TASK_MODE_PUBLISH) {
      gPublishState.gdpErr = gdpErr;
      GdpSetEvent(gPublishState.eventGetResult);
   }

   taskCtx->state = GDP_TASK_STATE_IDLE;
   taskCtx->mode = GDP_TASK_MODE_NONE;
   g_debug("%s: Reset mode=%d, state=%d.\n",
           __FUNCTION__, taskCtx->mode, taskCtx->state);
}


/*
 *****************************************************************************
 * GdpTaskGetTimeout --
 *
 * Gets timeout value for the next wait call.
 *
 * @param[in] taskCtx  The task context
 *
 *****************************************************************************
 */

static int
GdpTaskGetTimeout(TaskContext *taskCtx) // IN
{
   gint64 curTime;
   gint64 timeout;

   if (taskCtx->timeoutAt == GDP_TIMEOUT_AT_INFINITE) {
      return GDP_WAIT_INFINITE;
   }

   curTime = g_get_monotonic_time();
   if (curTime >= taskCtx->timeoutAt) {
      return 0;
   }

   timeout = (taskCtx->timeoutAt - curTime) / USEC_PER_MILLISECOND;
   return (int)timeout;
}


/*
 *****************************************************************************
 * GdpTaskProcessEvents --
 *
 * Processes task events.
 *
 * @param[in,out] taskCtx    The task context
 * @param[in]     taskEvent  Task event to process
 *
 *****************************************************************************
 */

static void
GdpTaskProcessEvents(TaskContext *taskCtx,   // IN/OUT
                     GdpTaskEvent taskEvent) // IN
{
   switch (taskEvent) {
      case GDP_TASK_EVENT_CONFIG:
         GdpResetEvent(gPluginState.eventConfig);
         GdpTaskProcessConfigChange(taskCtx);
         break;
      case GDP_TASK_EVENT_NETWORK:
         GdpTaskProcessNetwork(taskCtx);
         break;
      case GDP_TASK_EVENT_PUBLISH:
         GdpResetEvent(gPublishState.eventPublish);
         GdpTaskProcessPublish(taskCtx);
         break;
      case GDP_TASK_EVENT_TIMEOUT:
         GdpTaskProcessTimeout(taskCtx);
         break;
      default:
         //GDP_TASK_EVENT_NONE
         //GDP_TASK_EVENT_STOP
         NOT_REACHED();
   }
}


/*
 ******************************************************************************
 * GdpTaskWaitForEvents --
 *
 * Waits for the following events or timeout:
 *    The stop event
 *    The config event
 *    The network event
 *    The publish event
 *
 * @param[in]  timeout    Timeout value in milliseconds,
 *                        negative value means an infinite timeout,
 *                        zero means no wait.
 * @param[out] taskEvent  Return which event is signalled
 *                        if no error happens.
 *
 * @return GDP_ERROR_SUCCESS if no error happens.
 * @return Other GdpError code otherwise.
 *
 ******************************************************************************
 */

static GdpError
GdpTaskWaitForEvents(int timeout,             // IN
                     GdpTaskEvent *taskEvent) // OUT
{
#if defined(_WIN32)
   int res;
   DWORD localTimeout;
   gint64 startTime;
   GdpError retVal;

   ASSERT(Atomic_ReadBool(&gPluginState.started));

   /*
    * Resets the network event object.
    */
   WSAResetEvent(gPluginState.eventNetwork);

   /*
    * Associates the network event object with FD_READ network event
    * in the socket.
    */
   res = WSAEventSelect(gPluginState.sock, gPluginState.eventNetwork, FD_READ);
   if (res != 0) {
      g_warning("%s: WSAEventSelect failed: error=%d.\n",
                __FUNCTION__, WSAGetLastError());
      return GDP_ERROR_GENERAL;
   }

   retVal = GDP_ERROR_SUCCESS;

   localTimeout = (timeout >= 0 ? (DWORD) timeout : WSA_INFINITE);
   if (timeout > 0) {
      startTime = g_get_monotonic_time();
   } else {
      startTime = 0; // Deals with [-Werror=maybe-uninitialized]
   }

   while (TRUE) {
      WSAEVENT eventObjects[] = { gPluginState.eventStop,
                                  gPluginState.eventConfig,
                                  gPluginState.eventNetwork,
                                  gPublishState.eventPublish };
      DWORD waitRes;

      waitRes = WSAWaitForMultipleEvents((DWORD)ARRAYSIZE(eventObjects),
                                         eventObjects,
                                         FALSE, localTimeout, TRUE);
      if (waitRes == WSA_WAIT_EVENT_0) {
         *taskEvent = GDP_TASK_EVENT_STOP;
         break;
      } else if (waitRes == (WSA_WAIT_EVENT_0 + 1)) {
         *taskEvent = GDP_TASK_EVENT_CONFIG;
         break;
      } else if (waitRes == (WSA_WAIT_EVENT_0 + 2)) {
         WSANETWORKEVENTS networkEvents;
         res = WSAEnumNetworkEvents(gPluginState.sock, NULL, &networkEvents);
         if (res != 0) {
            g_warning("%s: WSAEnumNetworkEvents failed: error=%d.\n",
                      __FUNCTION__, WSAGetLastError());
            retVal = GDP_ERROR_GENERAL;
            break;
         }

         /*
          * Not checking networkEvents.iErrorCode[FD_READ_BIT]/
          * networkEvents.iErrorCode[FD_WRITE_BIT] for WSAENETDOWN,
          * since WSAEnumNetworkEvents should have returned WSAENETDOWN
          * if the error condition exists.
          */
         if (networkEvents.lNetworkEvents & FD_READ) {
            *taskEvent = GDP_TASK_EVENT_NETWORK;
         } else { // Not expected
            g_warning("%s: Unexpected network event.\n",
                      __FUNCTION__);
            retVal = GDP_ERROR_GENERAL;
         }

         break;
      } else if (waitRes == (WSA_WAIT_EVENT_0 + 3)) {
         *taskEvent = GDP_TASK_EVENT_PUBLISH;
         break;
      } else if (waitRes == WSA_WAIT_IO_COMPLETION) {
         gint64 curTime;
         gint64 passedTime;

         if (localTimeout == 0 ||
             localTimeout == WSA_INFINITE) {
            continue;
         }

         curTime = g_get_monotonic_time();
         passedTime = (curTime - startTime) / USEC_PER_MILLISECOND;
         if (passedTime >= localTimeout) {
            *taskEvent = GDP_TASK_EVENT_TIMEOUT;
            break;
         }

         startTime = curTime;
         localTimeout -= (DWORD)passedTime;
         continue;
      } else if (waitRes == WSA_WAIT_TIMEOUT) {
         *taskEvent = GDP_TASK_EVENT_TIMEOUT;
         break;
      } else { // WSA_WAIT_FAILED
         g_warning("%s: WSAWaitForMultipleEvents failed: error=%d.\n",
                   __FUNCTION__, WSAGetLastError());
         retVal = GDP_ERROR_GENERAL;
         break;
      }
   }

   /*
    * Cancels the association.
    */
   WSAEventSelect(gPluginState.sock, NULL, 0);

   return retVal;

#else

   gint64 startTime;
   GdpError retVal = GDP_ERROR_SUCCESS;

   ASSERT(Atomic_ReadBool(&gPluginState.started));

   if (timeout > 0) {
      startTime = g_get_monotonic_time();
   } else {
      startTime = 0; // Deals with [-Werror=maybe-uninitialized]
   }

   while (TRUE) {
      struct pollfd fds[4];
      int res;

      fds[0].fd = gPluginState.eventStop;
      fds[0].events = POLLIN;
      fds[0].revents = 0;
      fds[1].fd = gPluginState.eventConfig;
      fds[1].events = POLLIN;
      fds[1].revents = 0;
      fds[2].fd = gPluginState.sock;
      fds[2].events = POLLIN;
      fds[2].revents = 0;
      fds[3].fd = gPublishState.eventPublish;
      fds[3].events = POLLIN;
      fds[3].revents = 0;

      res = poll(fds, ARRAYSIZE(fds), timeout);
      if (res > 0) {
         if (fds[0].revents & POLLIN) {
            *taskEvent = GDP_TASK_EVENT_STOP;
         } else if (fds[1].revents & POLLIN) {
            *taskEvent = GDP_TASK_EVENT_CONFIG;
         } else if (fds[2].revents & POLLIN) {
            *taskEvent = GDP_TASK_EVENT_NETWORK;
         } else if (fds[3].revents & POLLIN) {
            *taskEvent = GDP_TASK_EVENT_PUBLISH;
         } else { // Not expected
            g_warning("%s: Unexpected event.\n", __FUNCTION__);
            retVal = GDP_ERROR_GENERAL;
         }

         break;
      } else if (res == -1) {
         int err = errno;
         if (err == EINTR) {
            gint64 curTime;
            gint64 passedTime;

            if (timeout <= 0) {
               continue;
            }

            curTime = g_get_monotonic_time();
            passedTime = (curTime - startTime) / USEC_PER_MILLISECOND;
            if (passedTime >= timeout) {
               *taskEvent = GDP_TASK_EVENT_TIMEOUT;
               break;
            }

            startTime = curTime;
            timeout -= (int) passedTime;
            continue;
         } else {
            g_warning("%s: poll failed: error=%d.\n", __FUNCTION__, err);
            retVal = GDP_ERROR_GENERAL;
            break;
         }
      } else if (res == 0) {
         *taskEvent = GDP_TASK_EVENT_TIMEOUT;
         break;
      } else {
         g_warning("%s: Unexpected poll return: %d.\n", __FUNCTION__, res);
         retVal = GDP_ERROR_GENERAL;
         break;
      }
   }

   return retVal;
#endif
}


/*
 *****************************************************************************
 * GdpTaskCtxInit --
 *
 * Initializes the task context states and resources.
 *
 * @param[out] taskCtx  The task context
 *
 *****************************************************************************
 */

static void
GdpTaskCtxInit(TaskContext *taskCtx) // OUT
{
   taskCtx->mode = GDP_TASK_MODE_NONE;
   taskCtx->state = GDP_TASK_STATE_IDLE;
   taskCtx->publishPending = FALSE;

   g_queue_init(&taskCtx->cache.queue);
   taskCtx->cache.sizeLimit = GdpGetHistoryCacheSizeLimit();
   taskCtx->cache.countLimit = GdpGetHistoryCacheCountLimit();
   taskCtx->cache.size = 0;
   taskCtx->cache.currentLink = NULL;

   g_queue_init(&taskCtx->requests);

   taskCtx->sequence = 0;
   taskCtx->packet = NULL;
   taskCtx->packetLen = 0;

   taskCtx->timeoutAt = GDP_TIMEOUT_AT_INFINITE;
   taskCtx->sendAfter = GDP_SEND_AFTER_ANY_TIME;
}


/*
 *****************************************************************************
 * GdpTaskCtxDestroy --
 *
 * Destroys the task context resources.
 *
 * @param[in,out] taskCtx  The task context
 *
 *****************************************************************************
 */

static void
GdpTaskCtxDestroy(TaskContext *taskCtx) // IN/OUT
{
   GdpTaskClearHistoryCacheQueue(taskCtx);
   GdpTaskClearHistoryRequestQueue(taskCtx);
   GdpTaskDestroyPacket(taskCtx);
}


/*
 *****************************************************************************
 * GdpThreadTask --
 *
 * The gdp task thread routine.
 *
 * @param[in] ctx   The application context
 * @param[in] data  Data pointer, not used
 *
 *****************************************************************************
 */

static void
GdpThreadTask(ToolsAppCtx *ctx, // IN
              void *data)       // IN
{
   TaskContext taskCtx;

   g_debug("%s: Entering ...\n", __FUNCTION__);

   GdpTaskCtxInit(&taskCtx);

   do {
      int timeout;
      GdpError gdpErr;
      GdpTaskEvent taskEvent = GDP_TASK_EVENT_NONE;

      /*
       * Part 1 inside the loop:
       * Checks and processes one pending event at idle state.
       */

      if (taskCtx.mode == GDP_TASK_MODE_NONE) {
         ASSERT(taskCtx.state == GDP_TASK_STATE_IDLE);
         ASSERT(taskCtx.timeoutAt == GDP_TIMEOUT_AT_INFINITE);

         if (taskCtx.publishPending) { // Higher priority
            taskCtx.publishPending = FALSE;
            GdpTaskProcessPublish(&taskCtx);
         } else if (!g_queue_is_empty(&taskCtx.requests)) {
            /*
             * History request pending.
             *
             * GdpTaskPublishHistory clears history request queue if it fails
             * to kick off the state machine.
             */
            GdpTaskPublishHistory(&taskCtx);
         }
      }

      /*
       * Part 2 inside the loop:
       * Gets and processes one event at a time, then loops back to part 1
       * except for the stop event.
       */

      timeout = GdpTaskGetTimeout(&taskCtx);
      if (timeout == 0) {
         GdpTaskProcessEvents(&taskCtx, GDP_TASK_EVENT_TIMEOUT);
         continue;
      }

      /*
       * timeout == GDP_WAIT_INFINITE means taskCtx.mode == GDP_TASK_MODE_NONE
       * and taskCtx.state == GDP_TASK_STATE_IDLE. There should be no pending
       * publish event or history request in this case.
       */
      ASSERT(timeout != GDP_WAIT_INFINITE ||
             (!taskCtx.publishPending &&
              g_queue_is_empty(&taskCtx.requests)));

      gdpErr = GdpTaskWaitForEvents(timeout, &taskEvent);
      if (gdpErr != GDP_ERROR_SUCCESS) {
         continue;
      }

      if (taskEvent == GDP_TASK_EVENT_STOP) {
         /*
          * In case the publish event comes at the same time,
          * only the stop event is handled, so do not check
          * (taskCtx.mode == GDP_TASK_MODE_PUBLISH) here.
          */
         gPublishState.gdpErr = GDP_ERROR_STOP;
         GdpSetEvent(gPublishState.eventGetResult);
         break;
      }

      GdpTaskProcessEvents(&taskCtx, taskEvent);

   } while (!Atomic_ReadBool(&gPluginState.stopped));

   GdpTaskCtxDestroy(&taskCtx);

   g_debug("%s: Exiting ...\n", __FUNCTION__);
}


/*
 *****************************************************************************
 * GdpThreadInterrupt --
 *
 * Interrupts the gdp task thread to exit.
 *
 * @param[in] ctx   The application context
 * @param[in] data  Data pointer, not used
 *
 *****************************************************************************
 */

static void
GdpThreadInterrupt(ToolsAppCtx *ctx, // IN
                   void *data)       // IN
{
   g_debug("%s: Entering ...\n", __FUNCTION__);
   ASSERT(Atomic_ReadBool(&gPluginState.started));
   Atomic_WriteBool(&gPluginState.stopped, TRUE);
   GdpSetEvent(gPluginState.eventStop);
}


/*
 ******************************************************************************
 * GdpInit --
 *
 * Initializes plugin state data and resources.
 *
 * @param[in] ctx  The application context
 *
 ******************************************************************************
 */

static void
GdpInit(ToolsAppCtx *ctx) // IN
{
   gPluginState.ctx = ctx;
   Atomic_WriteBool(&gPluginState.started, FALSE);

#if defined(_WIN32)
   gPluginState.wsaStarted = FALSE;
#endif

   gPluginState.vmciFd = -1;
   gPluginState.vmciFamily = -1;
   gPluginState.sock = INVALID_SOCKET;
#if defined(_WIN32)
   gPluginState.eventNetwork = GDP_INVALID_EVENT;
#endif

   gPluginState.eventStop = GDP_INVALID_EVENT;
   Atomic_WriteBool(&gPluginState.stopped, FALSE);

   gPluginState.eventConfig = GDP_INVALID_EVENT;

   gPublishState.eventPublish = GDP_INVALID_EVENT;

   gPublishState.eventGetResult = GDP_INVALID_EVENT;
}


/*
 ******************************************************************************
 * GdpStart --
 *
 * Creates plugin resources and starts the task thread for data publishing.
 *
 * @return TRUE on success.
 * @return FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
GdpStart(void)
{
   Bool retVal = FALSE;

   g_debug("%s: Entering ...\n", __FUNCTION__);

   ASSERT(!Atomic_ReadBool(&gPluginState.started));

#if defined(_WIN32)
   {
      WSADATA wsaData;
      int res = WSAStartup(MAKEWORD(2, 2), &wsaData);
      if (res != 0) {
         g_critical("%s: WSAStartup failed: error=%d.\n",
                    __FUNCTION__, res);
         return FALSE;
      }

      gPluginState.wsaStarted = TRUE;
   }
#endif

   gPluginState.vmciFamily = VMCISock_GetAFValueFd(&gPluginState.vmciFd);
   if (gPluginState.vmciFamily == -1) {
      g_critical("%s: Failed to get vSocket address family value.\n",
                 __FUNCTION__);
      goto exit;
   }

   if (!GdpCreateSocket()) {
      g_critical("%s: Failed to create VMCI datagram socket.\n",
                 __FUNCTION__);
      goto exit;
   }

#if defined(_WIN32)
   gPluginState.eventNetwork = GdpCreateEvent();
   if (gPluginState.eventNetwork == GDP_INVALID_EVENT) {
      g_critical("%s: GdpCreateEvent for network failed: error=%d.\n",
                 __FUNCTION__, GetSysErr());
      goto exit;
   }
#endif

   gPluginState.eventStop = GdpCreateEvent();
   if (gPluginState.eventStop == GDP_INVALID_EVENT) {
      g_critical("%s: GdpCreateEvent for stop failed: error=%d.\n",
                 __FUNCTION__, GetSysErr());
      goto exit;
   }

   gPluginState.eventConfig = GdpCreateEvent();
   if (gPluginState.eventConfig == GDP_INVALID_EVENT) {
      g_critical("%s: GdpCreateEvent for config failed: error=%d.\n",
                 __FUNCTION__, GetSysErr());
      goto exit;
   }

   gPublishState.eventPublish = GdpCreateEvent();
   if (gPublishState.eventPublish == GDP_INVALID_EVENT) {
      g_critical("%s: GdpCreateEvent for publish failed: error=%d.\n",
                 __FUNCTION__, GetSysErr());
      goto exit;
   }

   gPublishState.eventGetResult = GdpCreateEvent();
   if (gPublishState.eventGetResult == GDP_INVALID_EVENT) {
      g_critical("%s: GdpCreateEvent for get-result failed: error=%d.\n",
                 __FUNCTION__, GetSysErr());
      goto exit;
   }

   if (!ToolsCorePool_StartThread(gPluginState.ctx, "GdpThread",
                                  GdpThreadTask,
                                  GdpThreadInterrupt,
                                  NULL, NULL)) {
      g_critical("%s: Failed to start the gdp task thread.\n", __FUNCTION__);
      goto exit;
   }

   Atomic_WriteBool(&gPluginState.started, TRUE);
   retVal = TRUE;

exit:
   if (!retVal) {
      GdpDestroy();
   }

   return retVal;
}


/*
 ******************************************************************************
 * GdpDestroy --
 *
 * Destroys plugin resources.
 *
 ******************************************************************************
 */

static void
GdpDestroy(void)
{
   g_debug("%s: Entering ...\n", __FUNCTION__);

   GdpCloseSocket();

   if (gPluginState.vmciFd != -1) {
      VMCISock_ReleaseAFValueFd(gPluginState.vmciFd);
      gPluginState.vmciFd = -1;
   }

#if defined(_WIN32)
   GdpCloseEvent(&gPluginState.eventNetwork);
#endif

   GdpCloseEvent(&gPluginState.eventStop);

   GdpCloseEvent(&gPluginState.eventConfig);

   GdpCloseEvent(&gPublishState.eventPublish);

   GdpCloseEvent(&gPublishState.eventGetResult);

#if defined(_WIN32)
   if (gPluginState.wsaStarted) {
      WSACleanup();
      gPluginState.wsaStarted = FALSE;
   }
#endif
}


/*
 ******************************************************************************
 * GdpPublish --
 *
 * Publishes guest data to host side gdp daemon.
 *
 * @param[in]          createTime  UTC timestamp, in number of micro-
 *                                 seconds since January 1, 1970 UTC.
 * @param[in]          topic       Topic
 * @param[in,optional] token       Token, can be NULL
 * @param[in,optional] category    Category, can be NULL that defaults to
 *                                 "application"
 * @param[in]          data        Buffer containing data to publish
 * @param[in]          dataLen     Buffer length
 * @param[in]          cacheData   Cache the data if TRUE
 * @param[in]          requireSubs Require subscribers if TRUE
 *
 * @return GDP_ERROR_SUCCESS on success.
 * @return Other GdpError code otherwise.
 *
 ******************************************************************************
 */

static GdpError
GdpPublish(gint64 createTime,     // IN
           const gchar *topic,    // IN
           const gchar *token,    // IN, OPTIONAL
           const gchar *category, // IN, OPTIONAL
           const gchar *data,     // IN
           guint32 dataLen,       // IN
           gboolean cacheData,    // IN
           gboolean requireSubs)  // IN
{
   GdpError gdpErr;

   g_debug("%s: Entering ...\n", __FUNCTION__);

   if (topic == NULL || *topic == '\0') {
      g_info("%s: Missing topic.\n", __FUNCTION__);
      return GDP_ERROR_INVALID_DATA;
   }

   if (data == NULL || dataLen == 0) {
      /*
       * ZeroData: Allow empty/no data when requireSubs=true and cacheData=false
       */
      if (!requireSubs || cacheData) {
         g_info("%s: Topic '%s' has no data.\n", __FUNCTION__, topic);
         return GDP_ERROR_INVALID_DATA;
      }
   }

   if (token != NULL && *token == '\0') {
      token = NULL;
   }

   if (category != NULL && *category == '\0') {
      category = NULL;
   }

   g_mutex_lock(&gPublishState.mutex);

   if (Atomic_ReadBool(&gPluginState.stopped)) {
      gdpErr = GDP_ERROR_STOP;
      goto exit;
   }

   if (!Atomic_ReadBool(&gPluginState.started) &&
       !GdpStart()) {
      gdpErr = GDP_ERROR_GENERAL;
      goto exit;
   }

   gPublishState.createTime = createTime;
   gPublishState.topic = topic;
   gPublishState.token = token;
   gPublishState.category = category;
   gPublishState.data = data;
   gPublishState.dataLen = dataLen;
   gPublishState.cacheData = cacheData;
   gPublishState.requireSubs = requireSubs;

   GdpSetEvent(gPublishState.eventPublish);

   do {
      gdpErr = GdpWaitForEvent(gPublishState.eventGetResult,
                               GDP_WAIT_INFINITE);
      if (gdpErr == GDP_ERROR_SUCCESS) {
         gdpErr = gPublishState.gdpErr;
         break;
      }
   } while (!Atomic_ReadBool(&gPluginState.stopped));

   GdpResetEvent(gPublishState.eventGetResult);

exit:
   g_mutex_unlock(&gPublishState.mutex);
   g_debug("%s: Exiting with gdp error: %s.\n",
           __FUNCTION__, gdpErrMsgs[gdpErr]);
   return gdpErr;
}


/*
 *-----------------------------------------------------------------------------
 * GdpConfReload --
 *
 * Handles gdp config change.
 *
 * @param[in] src   The source object, unused
 * @param[in] ctx   The application context
 * @param[in] data  Unused
 *
 *-----------------------------------------------------------------------------
 */

static void
GdpConfReload(gpointer src,     // IN
              ToolsAppCtx *ctx, // IN
              gpointer data)    // IN
{
   if (!Atomic_ReadBool(&gPluginState.started)) {
      return;
   }

   GdpSetEvent(gPluginState.eventConfig);
}


/*
 ******************************************************************************
 * GdpShutdown --
 *
 * Cleans up on shutdown.
 *
 * @param[in] src   The source object, unused
 * @param[in] ctx   The application context
 * @param[in] data  Unused
 *
 ******************************************************************************
 */

static void
GdpShutdown(gpointer src,     // IN
            ToolsAppCtx *ctx, // IN
            gpointer data)    // IN
{
   g_debug("%s: Entering ...\n", __FUNCTION__);
   ASSERT(!Atomic_ReadBool(&gPluginState.started) ||
          Atomic_ReadBool(&gPluginState.stopped));
   g_object_set(ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GDP, NULL, NULL);
   GdpDestroy();
}


/*
 ******************************************************************************
 * ToolsOnLoad --
 *
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in] ctx  The application context
 *
 * @return The registration data.
 *
 ******************************************************************************
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx) // IN
{
   /*
    * Returns NULL to disable the plugin if not running in vmsvc daemon.
    */
   if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
      g_info("%s: Not running in vmsvc daemon: container name='%s'.\n",
             __FUNCTION__, ctx->name);
      return NULL;
   }

   /*
    * Returns NULL to disable the plugin if not running in a VMware VM.
    */
   if (!ctx->isVMware) {
      g_info("%s: Not running in a VMware VM.\n", __FUNCTION__);
      return NULL;
   }

   /*
    * Returns NULL to disable the plugin if VM is not running on ESX host.
    */
   {
      uint32 vmxVersion = 0;
      uint32 vmxType = VMX_TYPE_UNSET;
      if (!VmCheck_GetVersion(&vmxVersion, &vmxType) ||
          vmxType != VMX_TYPE_SCALABLE_SERVER) {
         g_info("%s: VM is not running on ESX host.\n", __FUNCTION__);
         return NULL;
      }
   }

   GdpInit(ctx);

   {
      static ToolsPluginSvcGdp svcGdp = { GdpPublish };
      static ToolsPluginData regData = { "gdp", NULL, NULL, NULL };

      ToolsServiceProperty propGdp = { TOOLS_PLUGIN_SVC_PROP_GDP };

      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_CONF_RELOAD, GdpConfReload, NULL },
         { TOOLS_CORE_SIG_SHUTDOWN, GdpShutdown, NULL },
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_SIGNALS,
           VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
      };

      ctx->registerServiceProperty(ctx->serviceObj, &propGdp);
      g_object_set(ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GDP, &svcGdp, NULL);

      regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
      return &regData;
   }
}
  07070100000361000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestInfo 07070100000362000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestInfo/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000363000081A400000000000000000000000168225505000005E2000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestInfo/Makefile.am ################################################################################
### Copyright (C) 2009-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libguestInfo.la

libguestInfo_la_CPPFLAGS =
libguestInfo_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libguestInfo_la_CPPFLAGS += @XDR_CPPFLAGS@

libguestInfo_la_LDFLAGS =
libguestInfo_la_LDFLAGS += @PLUGIN_LDFLAGS@

libguestInfo_la_LIBADD =
libguestInfo_la_LIBADD += @VMTOOLS_LIBS@
libguestInfo_la_LIBADD += @XDR_LIBS@

libguestInfo_la_LIBADD += @DNET_LIBS@

libguestInfo_la_SOURCES =
libguestInfo_la_SOURCES += guestInfoServer.c
libguestInfo_la_SOURCES += perfMonLinux.c
libguestInfo_la_SOURCES += diskInfo.c
libguestInfo_la_SOURCES += diskInfoPosix.c
  07070100000364000081A400000000000000000000000168225505000098FD000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestInfo/diskInfo.c  /*********************************************************
 * Copyright (C) 2014-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file diskInfo.c
 *
 *      Get disk information.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vm_assert.h"
#include "debug.h"
#include "guestInfoInt.h"
#include "str.h"
#include "strutil.h"
#include "posix.h"
#include "file.h"
#include "util.h"
#include "wiper.h"

/*
 * TODO: A general reorganization of or removal of diskInfo.c is needed
 *
 * Presumably diskInfo.c is meant to contain routines common to both Windows
 * and *nix guests; but that has not been the case.  Up until the addition
 * of OS disk device mapping, this source file contained only two functions.
 *
 *  GuestInfo_FreeDiskInfo() -    4 line function called for Windows and *nix
 *                                to free the partitionList used to collect
 *                                disk information in the guest.
 *  GuestInfoGetDiskInfoWiper() - Function only called from diskInfoPosix.c
 *                                to use the disk wiper routines to query
 *                                "known" file systems on a *nix guest.
 *
 * As a result, the Windows guestInfo plugin has included the unreferenced
 * GuestInfoGetDiskInfoWiper() function in releases.
 *
 * As of this change, diskInfoWin32.c has its own copy of
 * GuestInfo_FreeDiskInfo() as that function has changed for *nix disk
 * info.  Only non-Windows tools builds will include diskInfo.c.
 *
 * As device-based disc mapping is implemented for FreeBSD, MacOS and
 * Solaris, diskInfo.c could be folded into diskInfoPosix.c.  Disk device
 * lookup could be separate modules based on OS type or implemented
 * with conditional compilation.  Both methods have been used elsewhere
 * in tools.
 *
 * PR 2350224 has been filed to track this TODO item.
 */

#if defined (__linux__)
#ifndef PATH_MAX
   # define PATH_MAX 1024
#endif

#define LINUX_SYS_CLASS_BLOCK_DIR "/sys/class/block"
#define LINUX_SYS_DEV_BLOCK_DIR "/sys/dev/block"
#define LINUX_PROC_SELF_MOUNTINFO "/proc/self/mountinfo"

#define PCI_IDE         0x010100
#define PCI_SATA_AHCI_1 0x010601

#define PCI_SUBCLASS    0xFFFF00
#endif

#define COMP_STATIC_REGEX(gregex, mypattern, gerr, errorout)        \
   if (gregex == NULL) {                                            \
      gregex = g_regex_new(mypattern, 0, 0, &gerr);                 \
      if (gregex == NULL) {                                         \
         g_warning("%s: bad regex pattern \"" mypattern "\" (%s);"  \
                   " failing with INVALID_ARG\n",  __FUNCTION__,    \
                   gerr != NULL ? gerr->message : "");              \
         goto errorout;                                             \
      }                                                             \
   }



/*
 ******************************************************************************
 * GuestInfo_FreeDiskInfo --                                             */ /**
 *
 * @brief Frees memory allocated by GuestInfoGetDiskInfo.
 *
 * @param[in] di    DiskInfo container.
 *
 ******************************************************************************
 */

void
GuestInfo_FreeDiskInfo(GuestDiskInfoInt *di)
{
   if (di) {
      int indx;

      for (indx = 0; indx < di->numEntries; indx++) {
         free(di->partitionList[indx].diskDevNames);
      }
      free(di->partitionList);
      free(di);
   }
}


/*
 * Private library functions.
 */

#if defined (__linux__)

/*
 ******************************************************************************
 * GuestInfoAddDeviceName --                                             */ /**
 *
 * Add the disk device name into the array of anticipated devices for
 * the specified filesystem.
 *
 * @param[in]     devName    The device name being added.  May be an empty
 *                           string.
 * @param[in/out] partEntry  Pointer to the PartitionEntryInt structure to
 *                           receive the disk device names.
 * @param[in]     devNum     The number of the DiskDevName to be updated.
 *                           The diskDevNames is numbered  1, 2, ... as opposed
 *                           to array indexes which start at 0.
 *
 ******************************************************************************
 */

static void
GuestInfoAddDeviceName(char *devName,
                       PartitionEntryInt *partEntry,
                       int devNum)
{
   ASSERT(devName);
   ASSERT(partEntry);

   /* Add the device name to the device name array at the specified slot. */
   Str_ToLower(devName);
   if (devNum > partEntry->diskDevCnt) {
      partEntry->diskDevCnt = devNum;
      partEntry->diskDevNames = Util_SafeRealloc(partEntry->diskDevNames,
                                                 devNum *
                                                 sizeof *partEntry->diskDevNames);
   }
   Str_Strncpy(partEntry->diskDevNames[devNum - 1],
               sizeof *partEntry->diskDevNames,
               devName, strlen(devName));

   if (devName[0] == '\0') {
      g_debug("Empty disk device name in slot %d\n", devNum);
   }
}


/*
 ******************************************************************************
 * GuestInfoCheckPci0 --                                                */ /**
 *
 * Check if this PCI device path does not contain a "label" file and happens
 * to be PCI bus 0.
 *
 * @param[in]  pciDevPath  Path of the PCI device of interest.
 * @param[in]  unit        Disk unit or device number (previously determined).
 * @param[out] devName     Address of the buffer to receive device name.
 * @param[in]  devMaxLen   Maximum length of the device buffer available.
 *
 * @return True if this disk device is on PCI bus 0 and the devName has
 *         been filled in as scsi0:unit.
 *
 ******************************************************************************
 */

static Bool
GuestInfoCheckPci0(const char *pciDevPath,
                   const char *unit,
                   char *devName,
                   size_t devMaxLen)
{
   char *realPath = NULL;
   Bool result = FALSE;

   if((realPath = Posix_RealPath(pciDevPath)) == NULL) {
      goto exit;
   }
   if (StrUtil_EndsWith(realPath, "/0000:00:10.0")) {
      Str_Snprintf (devName, devMaxLen, "scsi0:%s", unit);
      result = TRUE;
   }
   free(realPath);
exit:
   return result;
}


/*
 ******************************************************************************
 * GuestInfoGetPCIName --                                                */ /**
 *
 * Extract the controller class and controller number from the "label" of
 * the specified PCI device.  Combine these with the previously determined
 * device or unit number.  The device name will be constructed in the format of
 * <class> <controller> : <unit> such as scsi0:0 or sata0:0.  The devName
 * will be left "blank" if unable to process the contents of the "label" file.
 *
 * @param[in]  pciDevPath  Path of the PCI device of interest.
 * @param[in]  unit        Disk unit or device number (previously determined).
 * @param[out] devName     Address of the buffer to receive device name.
 * @param[in]  devMaxLen   Maximum length of the device buffer available.
 *
 ******************************************************************************
 */

static void
GuestInfoGetPCIName(const char *pciDevPath,
                    const char *unit,
                    char *devName,
                    size_t devMaxLen)
{
   char labelPath[PATH_MAX];
   FILE *labelFile;
   char buffer[25];
   char *cPtr;

   Str_Snprintf(labelPath, PATH_MAX, "%s/%s", pciDevPath, "label");

   if ((labelFile = fopen(labelPath, "r")) == NULL) {
      /*
       * Need to check if this might be the LSI Logic Parallel or BusLogic
       * SCSI controller on PCI bus 0, in which case "label" is not expected.
       */
      if (errno != ENOENT ||
          !GuestInfoCheckPci0(pciDevPath, unit, devName, devMaxLen)) {
         g_debug("%s: unable to open \"label\" file for device %s.\n",
                 __FUNCTION__, pciDevPath);
      }
      return;
   }
   if (fgets(buffer, sizeof buffer, labelFile) == NULL) {
      g_debug("%s: unable to read \"label\" file for device %s.\n",
              __FUNCTION__, pciDevPath);
      goto exit;
   }

   /*
    * The "label" contents should already be in the form of SCSIn or satan.
    * A '\0' is stored after the last character in the buffer by fgets().
    * Check if the last character read is a new line and strip if found. */
   cPtr = &buffer[strlen(buffer) -1];
   if (*cPtr == '\n') {
      *cPtr = '\0';
   }
   Str_Snprintf (devName, devMaxLen, "%s:%s", buffer, unit);

exit:
   fclose(labelFile);
}


/*
 ******************************************************************************
 * GuestInfoGetIdeSataDev --                                             */ /**
 *
 * Determine the IDE controller or the SATA device number of the specified disk
 * device.
 *
 * @param[in]  tgtHostPath  Path of the PCI device of interest.
 * @param[in]  pciDevPath   Path of the PCI device of interest.
 *
 * @return   The unit number of the specified disk device.  A return value
 *           of -1 indicates that the unit number could not be determined.
 *
 ******************************************************************************
 */

static int
GuestInfoGetIdeSataDev(const char *tgtHostPath,
                       const char *pciDevPath)
{
   char *realPath = NULL;
   char **fileNameList = NULL;
   static GRegex *regexHostPath = NULL;
   static GRegex *regexHost = NULL;
   static GRegex *regexAtaPath = NULL;
   static GRegex *regexAta = NULL;
   GRegex *regexAtaOrHost;
   GError *gErr = NULL;
   GMatchInfo *matchInfo = NULL;
   char *charHost = NULL;
   int result = -1;
   int numFiles = 0;
   int number = 0;
   int fileNum;
   int host;

   /*
    * Depending on the Linux kernel version, the IDE device or SATA
    * controller notation may begin with "host" or "ata".  More recent
    * Linux releases are using "ata".
    */
   COMP_STATIC_REGEX(regexAtaPath, "^.*/ata(\\d+)$", gErr, exit)
   realPath = Posix_RealPath(tgtHostPath);
   if (g_regex_match(regexAtaPath, realPath, 0, &matchInfo)) {
      COMP_STATIC_REGEX(regexAta, "^ata(\\d+)$", gErr, exit)
      regexAtaOrHost = regexAta;

   } else {
      g_match_info_free(matchInfo);
      COMP_STATIC_REGEX(regexHostPath, "^.*/host(\\d+)$", gErr, exit)
      if (g_regex_match(regexHostPath, realPath, 0, &matchInfo)) {
         COMP_STATIC_REGEX(regexHost, "^host(\\d+)$", gErr, exit)
         regexAtaOrHost = regexHost;

      } else {
         g_debug("%s: Unable to locate IDE/SATA \"ata\" or \"host\" node "
                 "directory.\n", __FUNCTION__);
         goto exit;
      }
   }

   charHost = g_match_info_fetch(matchInfo, 1);
   if (sscanf(charHost, "%d", &host) != 1) {
      g_debug("%s: Unable to read host number.\n", __FUNCTION__);
      goto exit;
   }

   numFiles = File_ListDirectory(pciDevPath, &fileNameList);
   if (numFiles < 0) {
      g_debug("%s: Unable to list files in \"%s\" directory.\n",
              __FUNCTION__, pciDevPath);
   }
   for (fileNum = 0; fileNum < numFiles; fileNum++) {
      int currHost;

      g_match_info_free(matchInfo);
      if (g_regex_match(regexAtaOrHost, fileNameList[fileNum], 0, &matchInfo)) {
         g_free(charHost);
         charHost = g_match_info_fetch(matchInfo, 1);
         if (sscanf(charHost, "%d", &currHost) != 1) {
            g_debug("%s: Unable to read current host number.\n",
                    __FUNCTION__);
            goto exit;
         }
         if (currHost < host) {
            number++;
         }
      }
   }
   result = number;

exit:
   g_match_info_free(matchInfo);
   g_free(charHost);
   g_clear_error(&gErr);
   if (fileNameList != NULL) {
      Util_FreeStringList(fileNameList, numFiles);
   }
   free(realPath);
   return result;
}


/*
 ******************************************************************************
 * GuestInfoGetDevClass --                                               */ /**
 *
 * Locate and extract the value from the "class" file of the specified disk
 * device.  This file is typically located in the directory provided by the
 * parameter "pciDevPath".
 *
 * An IDE device layout change that occurred between 2.x and 3.x Linux kernels
 * may require a check in the parent directory.  If that is necessary, the
 * "pciDevPath" and "devPath" will be updated to reflect that difference.
 *
 * @param[in/out]  pciDevPath  Path to the IDE, SCSI or SAS disk device of
 *                             interest; expecting "class" file.
 * @param[in/out]  devPath     Associated directory which will need to be
 *                             adjusted if pciDevPath is altered.
 *
 * @return     The disk device class value.
 *
 ******************************************************************************
 */

static unsigned int
GuestInfoGetDevClass(char *pciDevPath,
                     char *devPath)
{
   char devClassPath[PATH_MAX];
   FILE *devClass = NULL;
   unsigned int classValue = 0;

   ASSERT(pciDevPath);
   Str_Snprintf(devClassPath, PATH_MAX, "%s/%s", pciDevPath, "class");
   if (!File_Exists(devClassPath)) {
      /* Check if "class" exists in the parent directory. */
      Str_Snprintf(devClassPath, PATH_MAX, "%s/../%s", pciDevPath, "class");
      if (File_Exists(devClassPath)) {
         /* "class" found in parent directory; probably IDE/SATA device.*/
         Str_Strcat(pciDevPath, "/..", PATH_MAX);
         Str_Strcat(devPath, "/..", PATH_MAX);
      } else {
         g_debug("%s: Unable to locate device 'class' file.\n", __FUNCTION__);
         goto exit;
      }
   }

   devClass = fopen((const char *)devClassPath, "r");
   if (devClass == NULL) {
      g_debug("%s: Error opening device 'class' file.\n", __FUNCTION__);
      goto exit;
   }
   if (fscanf(devClass, "%x", &classValue) != 1) {
      classValue = 0;
      g_debug("%s: Unable to read expected hex class setting.\n", __FUNCTION__);
   }
   fclose(devClass);

exit:
   return classValue;
}


/*
 ******************************************************************************
 * GuestInfoCheckSASDevice --                                            */ /**
 *
 * Check if the referenced disk device is a SAS device and if so, recalulate
 * the device (unit) number and update the paths as needed to continue
 * processing a SAS device.
 *
 * @param[in/out] pciDevPath  Path of the disk device to be checked.
 * @param[out]    tgtHostPath Address of the "host" path to be updated if this
 *                            is a SAS device.
 * @param[out]    unit        Pointer to the address of the unit buffer to be
 *                            updated if this is a SAS device.
 *
 ******************************************************************************
 */

static void
GuestInfoCheckSASDevice(char *pciDevPath,
                        char *tgtHostPath,
                        char **unit)
{
   char sas_portPath[PATH_MAX];
   char **fileNameList = NULL;
   static GRegex *regexSas = NULL;
   GError *gErr = NULL;
   GMatchInfo *matchInfo = NULL;
   int numFiles = 0;
   int fileNum;

   Str_Snprintf(sas_portPath, PATH_MAX, "%s/%s", pciDevPath, "sas_port");
   if (!File_IsDirectory(sas_portPath)) {
      return;
   }
   g_debug("%s: located a \"sas_port\" directory - %s.\n", __FUNCTION__,
           sas_portPath);

   /* Expecting to find a new "unit" number; scribble over old value.. */
   **unit = '?';
   COMP_STATIC_REGEX(regexSas, "^phy-\\d+:(\\d+)$", gErr, exit)

   numFiles = File_ListDirectory(pciDevPath, &fileNameList);
   if (numFiles < 0) {
      g_debug("%s: Unable to list files in \"%s\" directory.\n", __FUNCTION__,
              pciDevPath);
   }
   for (fileNum = 0; fileNum < numFiles; fileNum++) {
      g_match_info_free(matchInfo);
      if (g_regex_match(regexSas, fileNameList[fileNum], 0, &matchInfo)) {
         free(*unit);     /* free previous "unit" string */
         *unit = g_match_info_fetch(matchInfo, 1);
         break;
      }
   }

   /*
    * Adjust the tgtHostPath and pciDevPath for continued processing of
    * a SAS disk device.
    */
   Str_Snprintf(tgtHostPath, PATH_MAX, "%s/..", pciDevPath);
   Str_Snprintf(pciDevPath, PATH_MAX, "%s/..", tgtHostPath);

exit:
   g_match_info_free(matchInfo);
   g_clear_error(&gErr);
   if (fileNameList != NULL) {
      Util_FreeStringList(fileNameList, numFiles);
   }
}


/*
 ******************************************************************************
 * GuestInfoNvmeDevice --                                                */ /**
 *
 * Extract the NVMe disk unit number for the specified disk device.  In
 * Linux kernels 3.x and later, the NVME device is contained in the "nsid"
 * file in the sysfs filesystem.  On earlier kernels such as 2.6, the NVME
 * device number is the last number group in the node name.
 *
 * NVMe devices are number 1 and up; the "physical" disk unit numbering
 * starts at zero.
 *
 *  @param[in]    devPath    Path of the "device" file for this NVMe device.
 *                           The "nsid" file, if it exists, will be in the
 *                           same directory.
 * @param[out]    pciDevPath Path to be updated with the directory containing
 *                           the device "label" file.   This path will also
 *                           contain the device "class" file.
 * @param[out]    unit       Pointer to receive the address of the disk device
 *                           number dereived from the contents of the "nsid"
 *                           file or from the node name in 2.6 Linux kernels.
 *
 * @return TRUE if the NVMe disk device number has been determined and the
 *         device number is passed back as a character string through "unit".
 *         The "pciDevPath will be updated (replaced) with the directory which
 *         should contain the device "label" file.
 *
 ******************************************************************************
 */

static Bool
GuestInfoNvmeDevice(const char *devPath,
                    char *pciDevPath,
                    char **unit)
{
   char nsidPath[PATH_MAX];
   FILE *devNsid = NULL;
   int dirPathLen;
   int nsid;

   ASSERT(devPath);
   ASSERT(pciDevPath);
   dirPathLen = strrchr(devPath, '/') - devPath;
   Str_Strncpy(nsidPath, sizeof nsidPath, devPath, dirPathLen);
   Str_Strcat(nsidPath, "/nsid", PATH_MAX);
   if (File_Exists(nsidPath)) {
      devNsid = fopen((const char *)nsidPath, "r");
      if (devNsid == NULL) {
         g_debug("%s: Error opening NVMe device \"nsid\" file.\n",
                 __FUNCTION__);
         return FALSE;
      }
      if (fscanf(devNsid, "%d", &nsid) != 1) {
         g_debug("%s: Unable to read the nsid device number.\n", __FUNCTION__);
         fclose(devNsid);
         return FALSE;
      }
      fclose(devNsid);
   } else {
      static GRegex *regexNvmeNode = NULL;
      GError *gErr = NULL;
      GMatchInfo *matchInfo = NULL;
      char *nsidStr = NULL;
      char * realPath;
      Bool match_result = FALSE;

      /*
       * Earlier NVMe kernel implementation; no "nsid" file available.
       * The nsid equivalent can be derived from the last number in the
       * directory containing the "device" symbolic link.  Need real path
       * name.
       */
      Str_Strncpy(nsidPath, sizeof nsidPath, devPath, dirPathLen);
      realPath = Posix_RealPath(nsidPath);

      /* Check for NVMe device. */
      COMP_STATIC_REGEX(regexNvmeNode, "^.*/nvme\\d+n(\\d+)$", gErr, finished)

      if (!g_regex_match(regexNvmeNode, realPath, 0, &matchInfo)) {
         goto finished;
      }
      nsidStr = g_match_info_fetch(matchInfo, 1);
      nsid = atoi(nsidStr);
      match_result = TRUE;

finished:
      /* Some clean-up for this block before possibly returning FALSE. */
      g_match_info_free(matchInfo);
      g_clear_error(&gErr);
      g_free(nsidStr);
      free(realPath);

      if (!match_result) {
         return FALSE;
      }
   }

   /* NVMe device numbers start at 1; hardware device numbers start at zero. */
   *unit = g_strdup_printf("%d", nsid - 1);

   /* Set the pciDevPath to the directory containing the "label" file, */
   Str_Snprintf(pciDevPath, PATH_MAX, "%s/%s", devPath, "../..");
   return TRUE;
}


/*
 ******************************************************************************
 * GuestInfoLinuxBlockDevice --                                          */ /**
 *
 * Determine if this is a block device and if so add the disk device name
 * to the specified PartitionEntryInt structure.  If the startDevPath
 * represents a full disk, i.e. no partition table on the disk, the
 * "device" file will be in the starting directory.  For filesystems
 * created on a disk partition, the "device" file will be in the parent node.
 *
 * @param[in]     startPath  Starting path to begin the search for the "device"
 *                           file.
 * @param[in/out] partEntry  Pointer to the PartitionEntryInt structure to
 *                           receive the disk device names.
 * @param[in]     devNum     The number of the DiskDevName to be updated.
 *                           The diskDevNames is numbered  1, 2, ... as opposed
 *                           to array indexes which start at 0.
 * @return TRUE if the device name for the disk has been determined.
 *
 ******************************************************************************
 */

static Bool
GuestInfoLinuxBlockDevice(const char *startPath,
                          PartitionEntryInt *partEntry,
                          int devNum)
{
   char devPath[PATH_MAX];
   char pciDevPath[PATH_MAX];
   char *realPath = NULL;
   static GRegex *regex = NULL;
   static GRegex *regexNvme = NULL;
   GError *gErr = NULL;
   GMatchInfo *matchInfo = NULL;
   char *unit = NULL;
   unsigned int devClass;
   char devName[DISK_DEVICE_NAME_SIZE];
   Bool devNameSet;

   ASSERT(startPath);
   ASSERT(partEntry);
   ASSERT(devNum > 0);
   devName[0] = '\0';   /* Empty string for the device name until determined. */
   g_debug("%s: looking up device for file system on \"%s\"\n", __FUNCTION__,
           startPath);

   /* Check for "device" file in the starting path provided. */
   Str_Snprintf(devPath, PATH_MAX,  "%s/device", startPath);
   if (!File_Exists(devPath)) {
      /*
       * If working with a filesystem on a disk partition, we will need
       * to check in the parent node.
       */
      Str_Snprintf(devPath, PATH_MAX,  "%s/../device", startPath);
      if (!File_Exists(devPath)) {
         goto finished;
      }
   }

   realPath = Posix_RealPath(devPath);
   COMP_STATIC_REGEX(regex, "^.*/\\d+:\\d+:(\\d+):\\d+$", gErr, finished)

   if (g_regex_match(regex, realPath, 0, &matchInfo)) {
      unit = g_match_info_fetch(matchInfo, 1);

      Str_Strcat(devPath, "/../..", PATH_MAX);
      Str_Snprintf(pciDevPath, PATH_MAX, "%s/%s", devPath, "..");
      /*
       * Check if this is a SAS device.  The contents of "unit", "devPath" and
       * "pciDevPath" will be altered if a SAS device is detected..
       */
      GuestInfoCheckSASDevice(pciDevPath, devPath, &unit);

      /* Getting the disk device class. */
      devClass = GuestInfoGetDevClass(pciDevPath, devPath);

      /*
       * IDE and SATA devices need different handling.
       */
      if ((devClass & PCI_SUBCLASS) == PCI_IDE || devClass == PCI_SATA_AHCI_1) {
         int cnt;

         cnt = GuestInfoGetIdeSataDev(devPath, pciDevPath);
         if (cnt < 0) {
            g_debug("%s: ERROR, unable to determine IDE controller or SATA "
                    "device.\n", __FUNCTION__);
            goto finished;
         }
         if ((devClass & PCI_SUBCLASS) == PCI_IDE) {
            /* IDE - full device representation can be constructed. */
            Str_Snprintf(devName, sizeof devName, "ide%d:%s", cnt, unit);
         } else {
            /* SATA - The "host cnt" obtained becomes the "unit" number. */
            g_free(unit);
            unit = g_strdup_printf("%d", cnt);
         }
      }
   } else {
      /* Check for NVMe device. */
      COMP_STATIC_REGEX(regexNvme, "^.*/nvme\\d+$", gErr, finished)

      g_match_info_free(matchInfo);
      if (!g_regex_match(regexNvme, realPath, 0, &matchInfo)) {
         g_debug("%s: block disk device pattern not found\n", __FUNCTION__);
         goto finished;
      }
      if (!GuestInfoNvmeDevice(devPath, pciDevPath, &unit)) {
         g_debug("%s: NVMe disk device could not be determined.\n",
                 __FUNCTION__);
         goto finished;
      }
   }

   /* At this point only IDE disks would have a completed device name. */
   if (devName[0] == '\0') {
      /* Access the PCI device "label" file for the controller name. */
      GuestInfoGetPCIName(pciDevPath, unit, devName, sizeof devName);
   }

finished:
   /*
    * Add the device name, whether found or not, to the device list for
    * this mounted file system.  After processing the single partition of
    * a file system mounted on a block device or all the subordinate devices of
    * an LVM, the presence of a zero-length name indicates not all devices
    * have been correctly determined.
    */
   GuestInfoAddDeviceName(devName, partEntry, devNum);

   g_match_info_free(matchInfo);
   g_clear_error(&gErr);
   g_free(unit);
   free(realPath);
   devNameSet = devName[0] != '\0';
   g_debug("%s: Filesystem of interest found on device \"%s\"\n",
           __FUNCTION__, devNameSet ? devName : "** unknown **");
   return devNameSet;
}


/*
 ******************************************************************************
 * GuestInfoIsLinuxLvmDevice --                                          */ /**
 *
 * Determine if the fsName is a Linux LVM and if so, determine the disk
 * device or devices associated with this LVM based filesystem.  If for
 * any reason the full set of LVM subordinates cannot be determined, an
 * incomplete list of disk device names will not be provided.
 *
 * @param[in]     fsName     Name of the block device or LVM mapper name of
 *                           the filesystem of interest.
 * @param[in/out] partEntry  Pointer to the PartitionEntryInt structure to
 *                           receive the disk device names.
 * @return TRUE if the "slaves" directory has been located.  It does not
 *         indicate that LVM processing was successful, only that this
 *         does appear to be an LVM filesystem.  If the disk device names
 *         for all subordinates cannot be located, partial disk mapping is not
 *         reported.
 *
 ******************************************************************************
 */

static Bool
GuestInfoIsLinuxLvmDevice(const char *fsName,
                          PartitionEntryInt *partEntry)
{
   char *realPath;
   char **fileNameList = NULL;
   int numFiles = 0;
   int devIndx;
   char subordinatesPath[PATH_MAX];
   char devPath[PATH_MAX];

   /*
    * If a logical volume, the fsName will be a symbolic link to the
    * /dev/dm-<n>.  Use the "dm-<n>" name to access the logical volume
    * entry in the sysfs/device manager (/sys/class/block).
    */
   if ((realPath = Posix_RealPath(fsName)) == NULL) {
      return FALSE;
   }
   Str_Snprintf(subordinatesPath, PATH_MAX, "%s/%s/slaves", LINUX_SYS_CLASS_BLOCK_DIR,
                strrchr(realPath, '/') + 1);
   free(realPath);
   if (!File_IsDirectory(subordinatesPath)) {
      return FALSE;
   }
   numFiles = File_ListDirectory(subordinatesPath, &fileNameList);
   if (numFiles == 0) {
      /* An empty "slaves" directory happens at a disk device node; this
       * certainly is not an LVM.
       */
      return FALSE;
   }
   if (numFiles < 0) {
      g_debug("%s: Unable to list entries in \"%s\" directory.\n", __FUNCTION__,
              subordinatesPath);
      return TRUE;
   }

   /* Create a device name entry for each subordinate device found. */
   partEntry->diskDevCnt = numFiles;
   partEntry->diskDevNames = Util_SafeRealloc(partEntry->diskDevNames,
                                              numFiles *
                                              sizeof *partEntry->diskDevNames);

   for (devIndx = 0; devIndx < numFiles; devIndx++) {
      /*
       * Each subordinate device will be based on the disk or disk partition of
       * a virtual disk device.  Start the block device search from the
       * "slaves" path.
       */
      Str_Snprintf(devPath, PATH_MAX, "%s/%s", subordinatesPath,
                   fileNameList[devIndx]);
      GuestInfoLinuxBlockDevice(devPath, partEntry, devIndx + 1);
   }

   if (fileNameList != NULL) {
      Util_FreeStringList(fileNameList, numFiles);
   }
   return TRUE;
}


/*
 ******************************************************************************
 * GuestInfoCheckDevRoot --
 *
 * Disks mounted on /dev/root are setup at boot time before the OS enumerates
 * the PCI devices in /sys/class/block.  The device for /dev/root does not
 * appear there.   Instead the PCI device info needs to be accessed through
 * /sys/dev/block and the major and minor device number.  That can be extracted
 * from the contents of /proc/self/mountinfo.
 *
 * The format of the lines in /proc/self/mountinfo can vary between Linux
 * versions.
 *
 *    Field
 *    -----
 *      3     The major and minor number of the device in "m:n" format.
 *   9 or 10  The fsName for this specific entry.
 *
 * @param[in]  fsName       Name of the filesystem device - i.e. /dev/root
 * @param[in]  pathLen      Maximum size including terminating NUL character
 *                          for the starting search path.
 * @param[out] blockDevPath Starting path to begin the search for the "device"
 *                          file if the major and minor number can be found.
 * @return TRUE if the device major and minor numbers have been located and
 *         blockDevPath has been filled in.
 *
 ******************************************************************************
 */

static Bool
GuestInfoCheckDevRoot(const char *fsName,
                      int pathLen,
                      char *blockDevPath)
{
   FILE *mountinfo;
   char buffer[BUFSIZ];
   char pattern[128];

   if ((mountinfo = fopen(LINUX_PROC_SELF_MOUNTINFO, "r")) == NULL) {
      g_debug("%s: unable to open \"" LINUX_PROC_SELF_MOUNTINFO
              "\": (%d) %s\n", __FUNCTION__, errno, strerror(errno));
      return FALSE;
   }
   snprintf(pattern, sizeof pattern, " %s ", fsName);
   /* Locate the "/dev/root" entry in /proc/self/mountinfo. */
   while ((fgets(buffer, sizeof buffer, mountinfo)) != NULL) {
      if (strstr(buffer, pattern) != NULL) {
         char *savedPtr = NULL;
         const char *majMinNo;

         (void) strtok_r(buffer, " ", &savedPtr);
         (void) strtok_r(NULL, " ", &savedPtr);
         majMinNo = strtok_r(NULL, " ", &savedPtr);
         Str_Snprintf(blockDevPath, pathLen, "%s/%s", LINUX_SYS_DEV_BLOCK_DIR,
                      majMinNo);
         fclose(mountinfo);
         return TRUE;
      }
   }
   fclose(mountinfo);
   return FALSE;
}

#endif /* __linux__ */

/*
 ******************************************************************************
 * GuestInfoGetDiskDevice --                                             */ /**
 *
 * Determine the OS disk device for the block device or the disk devices of
 * the logical volume mapper name provided.
 *
 * @param[in]     fsName     Name of the block device or LVM mapper name of
 *                           the filesystem of interest.
 * @param[in/out] partEntry  Pointer to the PartitionEntryInt structure to
 *                           receive the disk device names.
 *
 * Currently only processing disks on a Linux guest.
 *
 * TODO: Other available controllers and their importance on vSphere hosted
 *       guests were discussed in review board posting:
 *          https://reviewboard.eng.vmware.com/r/1520060/
 *       PR 2356195 has been filed to track these issues post 11.0.0 FC.
 ******************************************************************************
 */

static void
GuestInfoGetDiskDevice(const char *fsName,
                       PartitionEntryInt *partEntry)
{
#if defined (__linux__)
   int indx;
#endif /* __linux__ */

   ASSERT(fsName);
   ASSERT(partEntry);
   g_debug("%s: looking up device(s) for file system on \"%s\".\n",
           __FUNCTION__, fsName);

#if defined (__linux__)
   /*
    * Determine if this is a filesystem on a block device or on a logical
    * volume (such as /dev/mapper/...).
    */

   /* Check first for an LVM filesystem. */
   if (!GuestInfoIsLinuxLvmDevice(fsName, partEntry)) {

      /* Not an LVM; check if a basic block device. */
      const char *baseDevName = strrchr(fsName, '/');

      /*
       * ZFS pools are not currently handled and may consist of a simple file
       * name.  Neither situation can currently be handled in the PCI device
       * lookup; avoid at this time.
       */
      if (baseDevName != NULL && strcmp(partEntry->fsType, "zfs") != 0) {
         char blockDevPath[PATH_MAX];

         /*
          * Have a single disk device associated with this mount point.  The
          * majority of these will be handled by the basic Linux block device
          * lookup.
          */
         Str_Snprintf(blockDevPath, sizeof blockDevPath,  "%s/%s",
                      LINUX_SYS_CLASS_BLOCK_DIR, baseDevName + 1);
         if (!GuestInfoLinuxBlockDevice(blockDevPath, partEntry, 1)) {
            /*
             * No device name was located.  This may be a pseudo device
             * such as Photon's /dev/root.  Try a lookup based on the device
             * major and minor number.
             */
            if (GuestInfoCheckDevRoot(fsName, sizeof blockDevPath,
                                      blockDevPath)) {
               GuestInfoLinuxBlockDevice(blockDevPath, partEntry, 1);
            }
         }
      }
   }

   /*
    * Check that all expected devices have been found.  If not, reset the
    * device count to zero so that partial disk mapping will not happen.
    */
   for (indx = 0; indx < partEntry->diskDevCnt; indx++) {
      if (partEntry->diskDevNames[indx][0] == '\0') {
         g_debug("%s: Missing disk device name; VMDK mapping unavailable "
                 "for \"%s\", fsName: \"%s\"\n", __FUNCTION__,
                 partEntry->name, fsName);
         partEntry->diskDevCnt = 0;
         free(partEntry->diskDevNames);
         partEntry->diskDevNames = NULL;
         break;
      }
   }
#endif /* __linux__ */

   g_debug("%s: found %d devices(s) for file system on \"%s\".\n",
           __FUNCTION__, partEntry->diskDevCnt, fsName);
}


/*
 ******************************************************************************
 * GuestInfoGetDiskInfoWiper --                                          */ /**
 *
 * Uses wiper library to enumerate fixed volumes and lookup utilization data.
 *
 * @return Pointer to a GuestDiskInfoInt structure on success or NULL on failure.
 *         Caller should free returned pointer with GuestInfo_FreeDiskInfo.
 *
 ******************************************************************************
 */

GuestDiskInfoInt *
GuestInfoGetDiskInfoWiper(Bool includeReserved,  // IN
                          Bool reportDevices)    // IN
{
   WiperPartition_List pl;
   DblLnkLst_Links *curr;
   unsigned int partCount = 0;
   uint64 freeBytes = 0;
   uint64 totalBytes = 0;
   size_t partNameSize = 0;
   Bool success = FALSE;
   GuestDiskInfoInt *di;

   /* Get partition list. */
   if (!WiperPartition_Open(&pl, FALSE)) {
      g_warning("GetDiskInfo: ERROR: could not get partition list\n");
      return FALSE;
   }

   di = Util_SafeCalloc(1, sizeof *di);
   partNameSize = sizeof (di->partitionList)[0].name;

   DblLnkLst_ForEach(curr, &pl.link) {
      WiperPartition *part = DblLnkLst_Container(curr, WiperPartition, link);

      if (part->type != PARTITION_UNSUPPORTED) {
         PartitionEntryInt *newPartitionList;
         PartitionEntryInt *partEntry;
         unsigned char *error;
         if (includeReserved) {
            error = WiperSinglePartition_GetSpace(part, NULL,
                                                  &freeBytes, &totalBytes);
         } else {
            error = WiperSinglePartition_GetSpace(part, &freeBytes,
                                                  NULL, &totalBytes);
         }
         if (strlen(error)) {
            g_warning("GetDiskInfo: ERROR: could not get space info for "
                      "partition %s: %s\n", part->mountPoint, error);
            goto out;
         }

         if (strlen(part->mountPoint) + 1 > partNameSize) {
            g_debug("GetDiskInfo: Partition name '%s' too large, truncating\n",
                    part->mountPoint);
         }

         newPartitionList = Util_SafeRealloc(di->partitionList,
                                             (partCount + 1) *
                                             sizeof *di->partitionList);

         partEntry = &newPartitionList[partCount++];
         Str_Strncpy(partEntry->name, partNameSize,
                     part->mountPoint, partNameSize - 1);
         partEntry->freeBytes = freeBytes;
         partEntry->totalBytes = totalBytes;
         Str_Strncpy(partEntry->fsType, sizeof (di->partitionList)[0].fsType,
                     part->fsType, strlen(part->fsType));

         /* Start with an empty set of disk device names. */
         partEntry->diskDevCnt = 0;
         partEntry->diskDevNames = NULL;

         if (reportDevices) {
            GuestInfoGetDiskDevice(part->fsName, partEntry);
         }

         di->partitionList = newPartitionList;
         g_debug("%s added partition #%d %s type %d fstype %s (mount point %s) "
                 "free %"FMT64"u total %"FMT64"u\n",
                 __FUNCTION__, partCount, partEntry->name, part->type,
                 partEntry->fsType, part->fsName,
                 partEntry->freeBytes, partEntry->totalBytes);
      } else {
         g_debug("%s ignoring unsupported partition %s %s\n",
                 __FUNCTION__, part->mountPoint,
                 part->comment ? part->comment : "");
      }
   }

   di->numEntries = partCount;
   success = TRUE;

out:
   if (!success) {
      GuestInfo_FreeDiskInfo(di);
      di = NULL;
   }
   WiperPartition_Close(&pl);
   return di;
}
   07070100000365000081A40000000000000000000000016822550500000B8F000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestInfo/diskInfoPosix.c /*********************************************************
 * Copyright (C) 2014-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file diskInfoPosix.c
 *
 * Contains POSIX-specific bits of gettting disk information.
 */

#include "conf.h"
#include "util.h"
#include "vmware.h"
#include "guestInfoInt.h"


/*
 ******************************************************************************
 * GuestInfo_GetDiskInfo --                                              */ /**
 *
 * Uses wiper library to enumerate fixed volumes and lookup utilization data.
 *
 * @return Pointer to a GuestDiskInfoInt structure on success or NULL on failure.
 *         Caller should free returned pointer with GuestInfo_FreeDiskInfo.
 *
 ******************************************************************************
 */

GuestDiskInfoInt *
GuestInfo_GetDiskInfo(const ToolsAppCtx *ctx)
{
   gboolean includeReserved;
   gboolean reportDevices;

   /*
    * In order to be consistent with the way 'df' reports
    * disk free space, we don't include the reserved space
    * while reporting the disk free space by default.
    */
   includeReserved = VMTools_ConfigGetBoolean(ctx->config,
                                              CONFGROUPNAME_GUESTINFO,
                                              CONFNAME_DISKINFO_INCLUDERESERVED,
                                              FALSE);
   if (includeReserved) {
      g_debug("Including reserved space in diskInfo stats.\n");
   } else {
      g_debug("Excluding reserved space from diskInfo stats.\n");
   }

   reportDevices = VMTools_ConfigGetBoolean(ctx->config,
                                            CONFGROUPNAME_GUESTINFO,
                                            CONFNAME_DISKINFO_REPORT_DEVICE,
                                            CONFIG_GUESTINFO_REPORT_DEVICE_DEFAULT);

   /*
    * TODO: Future performance consideration.  If the ESX host cannot accept
    *       the new DiskInfo V1, then there really is no value to collecting
    *       disk device names.  Consider factoring in the setting of
    *       gInfoCache.diskInfoUseJson in guestInfoServer.c
    */
   return GuestInfoGetDiskInfoWiper(includeReserved, reportDevices);
}
 07070100000366000081A40000000000000000000000016822550500000A9F000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestInfo/guestInfoInt.h  /*********************************************************
 * Copyright (C) 2008-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _GUESTINFOINT_H_
#define _GUESTINFOINT_H_

/**
 * @file guestInfoInt.h
 *
 * Declares internal functions and data structures of the guestInfo plugin.
 */

#define G_LOG_DOMAIN "guestinfo"
#include "vmware/tools/log.h"
#include "vmware/tools/plugin.h"

#include "nicInfo.h"
#include "dynbuf.h"

/* Default for whether to query and report disk UUIDs */
#define CONFIG_GUESTINFO_REPORT_UUID_DEFAULT TRUE

/* Default for whether to query and report disk devices */
#define CONFIG_GUESTINFO_REPORT_DEVICE_DEFAULT TRUE

/*
 * Plugin-specific data structures for the DiskGuestInfo.
 *
 * These expand upon the GuestDiskInfo in bora/public/guestInfo.h,
 * but are not shared and need not maintain any version compatibility.
 */

typedef char DiskDevName[DISK_DEVICE_NAME_SIZE];

typedef struct _PartitionEntryInt {
   uint64 freeBytes;
   uint64 totalBytes;
   char name[PARTITION_NAME_SIZE];
   char fsType[FSTYPE_SIZE];
#ifdef _WIN32
   /* UUID of the disk, if known.  Currently only Windows */
   char uuid[PARTITION_NAME_SIZE];
#else
   /* Linux LVM mounted filesystems can span multiple disk devices. */
   int diskDevCnt;
   DiskDevName *diskDevNames;
#endif
} PartitionEntryInt;

typedef struct _GuestDiskInfoInt {
   unsigned int numEntries;
   PartitionEntryInt *partitionList;
} GuestDiskInfoInt;

extern int guestInfoPollInterval;

Bool
GuestInfo_ServerReportStats(ToolsAppCtx *ctx,  // IN
                            DynBuf *stats);    // IN

gboolean
GuestInfo_StatProviderPoll(gpointer data);

#ifndef _WIN32
GuestDiskInfoInt *
GuestInfoGetDiskInfoWiper(Bool includeReserved,
                          Bool reportDevices);
#endif

GuestDiskInfoInt *
GuestInfo_GetDiskInfo(const ToolsAppCtx *ctx);

void
GuestInfo_FreeDiskInfo(GuestDiskInfoInt *di);

void
GuestInfo_StatProviderShutdown(void);

#endif /* _GUESTINFOINT_H_ */

 07070100000367000081A4000000000000000000000001682255050001202D000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestInfo/guestInfoServer.c   /*********************************************************
 * Copyright (c) 1998-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * guestInfoServer.c --
 *
 *      This is the implementation of the common code in the guest tools
 *      to send out guest information to the host. The guest info server
 *      runs in the context of the tools daemon's event loop and periodically
 *      gathers all guest information and sends updates to the host if required.
 *      This file implements the platform independent framework for this.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>

#ifndef WIN32
#   include <arpa/inet.h>
#endif

#include "vmware.h"
#include "buildNumber.h"
#include "conf.h"
#include "guestApp.h"
#include "debug.h"
#include "dynxdr.h"
#include "hostinfo.h"
#include "guestInfoInt.h"
#include "guest_msg_def.h" // For GUESTMSG_MAX_IN_SIZE
#include "netutil.h"
#include "rpcvmx.h"
#include "procMgr.h"
#include "str.h"
#include "strutil.h"
#include "system.h"
#include "util.h"
#include "xdrutil.h"
#include "vmsupport.h"
#include "dynbuf.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/log.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/vmbackup.h"

#if defined(_WIN32)
#include "guestStats.h"
#include "win32/guestInfoWin32.h"
#include <time.h>
#endif

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif

/**
 * Default poll interval for guestInfo is 30s
 */
#define GUESTINFO_POLL_INTERVAL 30

/**
 * Default poll interval for guestStats is 20s
 */
#define GUESTINFO_STATS_INTERVAL 20

#define GUESTINFO_DEFAULT_DELIMITER ' '

/**
 * The default setting for exclude-nics
 */
#if defined(_WIN32)
#define GUESTINFO_DEFAULT_IFACE_EXCLUDES "vEthernet*"
#else
#define GUESTINFO_DEFAULT_IFACE_EXCLUDES "docker*,veth*,virbr*,antrea-*,cali*"
#endif

/*
 * Define what guest info types and NIC info versions could be sent to update
 * the NIC info in the VMX. The order defines a sequence of fallback paths to
 * provide backward compatibility for ESX running with NIC info version older
 * than the guest OS.
 */
typedef enum NicInfoMethod {
   NIC_INFO_V3_WITH_INFO_IPADDRESS_V3,
   NIC_INFO_V3_WITH_INFO_IPADDRESS_V2,
   NIC_INFO_V2_WITH_INFO_IPADDRESS_V2,
   NIC_INFO_V1_WITH_INFO_IPADDRESS,
   NIC_INFO_METHOD_MAX
} NicInfoMethod;

/*
 * Stores information about all guest information sent to the vmx.
 */

typedef struct _GuestInfoCache {
   /* Stores values of all key-value pairs. */
   char                        *value[INFO_MAX];
   HostinfoDetailedDataHeader  *detailedData;
   NicInfoV3                   *nicInfo;
   NicInfoMethod                method;
   GuestDiskInfoInt            *diskInfo;
   Bool                         diskInfoUseJson;
} GuestInfoCache;


/**
 * Defines the current poll interval (in milliseconds).
 *
 * This value is controlled by the guestinfo.poll-interval config file option.
 */
int guestInfoPollInterval = 0;

/**
 * The time when the guestInfo was last gathered.
 */
time_t gGuestInfoLastGatherTime = 0;

/**
 * Defines the current stats interval (in milliseconds).
 *
 * This value is controlled by the guestinfo.stats-interval config file option.
 */
int guestInfoStatsInterval = 0;

/*
 * Detailed guest OS data sending. Reset on channel reset.
 */

static Bool gSendDetailedGosData = TRUE;

/**
 * GuestInfo gather loop timeout source.
 */
static GSource *gatherInfoTimeoutSource = NULL;

/**
 * GuestStats gather loop timeout source.
 */
static GSource *gatherStatsTimeoutSource = NULL;

/* Local cache of the guest information that was last sent to vmx. */
static GuestInfoCache gInfoCache;

/*
 * A boolean flag that specifies whether the state of the VM was
 * changed since the last time guest info was sent to the VMX.
 * Tools daemon sets it to TRUE after the VM was resumed.
 */

static Bool gVMResumed;


/*
 * Local functions
 */


static Bool GuestInfoUpdateVMX(ToolsAppCtx *ctx,
                               GuestInfoType infoType,
                               void *info,
                               size_t infoSize);
static Bool SetGuestInfo(ToolsAppCtx *ctx,
                         GuestInfoType key,
                         const char *value);
static void SendUptime(ToolsAppCtx *ctx);
static Bool DiskInfoChanged(const GuestDiskInfoInt *diskInfo);
static void GuestInfoClearCache(void);
static GuestNicList *NicInfoV3ToV2(const NicInfoV3 *infoV3);
static void TweakGatherLoops(ToolsAppCtx *ctx,
                             gboolean enable);


/*
 ******************************************************************************
 * GuestInfoVMSupport --
 *
 * Launches the vm-support process.  Data returned asynchronously via RPCI.
 *
 * @param[in]   data     RPC request data.
 *
 * @return      TRUE if able to launch script, FALSE if script failed.
 *
 ******************************************************************************
 */

static gboolean
GuestInfoVMSupport(RpcInData *data)
{
#if defined(_WIN32)

    char vmSupportCmd[] = "vm-support.vbs";
    char *vmSupportPath;
    gchar *vmSupport;

    SECURITY_ATTRIBUTES saProcess = {0}, saThread = {0};

    ProcMgr_AsyncProc *vmSupportProc = NULL;
    ProcMgr_ProcArgs vmSupportProcArgs = {0};

    /*
     * Construct the commandline to be passed during execution
     * This will be the path of our vm-support.vbs
     */
    vmSupportPath = GuestApp_GetInstallPath();

    if (vmSupportPath == NULL) {
       return RPCIN_SETRETVALS(data,
                               "GuestApp_GetInstallPath failed", FALSE);
    }

    /* Put together absolute vm-support filename. */
    vmSupport = g_strdup_printf("cscript \"%s%s%s\" -u -x",
                                vmSupportPath, DIRSEPS, vmSupportCmd);
    vm_free(vmSupportPath);

    saProcess.nLength = sizeof saProcess;
    saProcess.bInheritHandle = TRUE;

    saThread.nLength = sizeof saThread;

    vmSupportProcArgs.lpProcessAttributes = &saProcess;
    vmSupportProcArgs.lpThreadAttributes = &saThread;
    vmSupportProcArgs.dwCreationFlags = CREATE_NO_WINDOW;

    g_message("Starting vm-support script - %s\n", vmSupport);
    vmSupportProc = ProcMgr_ExecAsync(vmSupport, &vmSupportProcArgs);
    g_free(vmSupport);

    if (vmSupportProc == NULL) {
       g_warning("Error starting vm-support script\n");
       return RPCIN_SETRETVALS(data,
                               "Error starting vm-support script", FALSE);
    }

    ProcMgr_Free(vmSupportProc);
    return RPCIN_SETRETVALS(data, "", TRUE);

#else

     gchar *vmSupportCmdArgv[] = {"vm-support", "-u", "-x", NULL};

     g_message("Starting vm-support script - %s\n", vmSupportCmdArgv[0]);
     if (!g_spawn_async(NULL, vmSupportCmdArgv, NULL,
                        G_SPAWN_SEARCH_PATH |
                        G_SPAWN_STDOUT_TO_DEV_NULL |
                        G_SPAWN_STDERR_TO_DEV_NULL,
                        NULL, NULL, NULL, NULL)) {
        g_warning("Error starting vm-support script\n");
        return RPCIN_SETRETVALS(data,
                                "Error starting vm-support script", FALSE);
     }

     return RPCIN_SETRETVALS(data, "", TRUE);

#endif
}


/*
 ******************************************************************************
 * GuestInfoCheckIfRunningSlow --
 *
 * Checks the time when the guestInfo was last collected.
 * Logs a warning message and sends a RPC message to the VMX if
 * the function was called after longer than expected interval.
 *
 * @param[in]  ctx     The application context.
 *
 * @return None
 *
 ******************************************************************************
 */

static void
GuestInfoCheckIfRunningSlow(ToolsAppCtx *ctx)
{
   time_t now = time(NULL);

   if (gGuestInfoLastGatherTime != 0) {
      time_t delta = now - gGuestInfoLastGatherTime;
      /*
       * Have a long enough delta to ensure that we have really missed a
       * collection.
       */
      if (((int64)delta * 1000) >= ((int64) 2 * guestInfoPollInterval)) {
         gchar *msg, *rpcMsg;

         msg = g_strdup_printf(
                   "*** WARNING: GuestInfo collection interval longer than "
                   "expected; actual=%"FMT64"d sec, expected=%d sec. ***\n",
                   (int64) delta, guestInfoPollInterval / 1000);

         rpcMsg = g_strdup_printf("log %s", msg);

         if (!RpcChannel_Send(ctx->rpc, rpcMsg, strlen(rpcMsg) + 1,
                              NULL, NULL)) {
            g_warning("%s: Error sending RPC message.\n", __FUNCTION__);
         }

         g_warning("%s", msg);

         g_free(rpcMsg);
         g_free(msg);
      }
   }

   gGuestInfoLastGatherTime = now;
}


/*
 ******************************************************************************
 * GuestInfoSetConfigList --
 *
 * Gets a list setting from the config, and sets the list pointed to by pList.
 *
 * @param[in]  ctx           the application context.
 * @param[in]  pCachedValue  a pointer to the location of the old value
 * @param[in]  configName    the configuration name
 * @param[in]  defaultValue  the default value
 * @param[out] pList         pointer to the pointer list, set when the value has
 *                           changed, otherwise this value will stay unchanged.
 *                           Caller needs to free this with g_strfreev().
 *
 * @return TRUE to indicate that the value has changed, FALSE otherwise
 ******************************************************************************
 */

static Bool
GuestInfoSetConfigList(ToolsAppCtx *ctx,
                       gchar **pCachedValue,
                       const gchar *configName,
                       const gchar *defaultValue,
                       gchar ***pList)
{
   gchar *listString = VMTools_ConfigGetString(ctx->config,
                                               CONFGROUPNAME_GUESTINFO,
                                               configName, defaultValue);
   if (g_strcmp0(listString, *pCachedValue) != 0) {
      gchar **list = NULL;

      if (listString != NULL && listString[0] != '\0') {
         list = g_strsplit(listString, ",", 0);
      }
      g_free(*pCachedValue);
      *pCachedValue = listString;
      *pList = list;

      return TRUE;
   } else {
      g_free(listString);
   }
   return FALSE;
}


/*
 * ****************************************************************************
 * GuestInfoResetNicPrimaryList --
 *
 * Gets primary-nics setting from the config, and sets the list of primary
 * patterns.
 *
 * @param[in]  ctx     The application context.
 *
 * @return TRUE to indicate that the value has changed, FALSE otherwise
 ******************************************************************************
 */

static Bool
GuestInfoResetNicPrimaryList(ToolsAppCtx *ctx)
{
   static gchar *ifacePrimaryStringCached = NULL;
   gchar **list;

   if (GuestInfoSetConfigList(ctx, &ifacePrimaryStringCached,
                             CONFNAME_GUESTINFO_PRIMARYNICS, NULL,
                             &list)) {
      GuestInfo_SetIfacePrimaryList(list);
      g_strfreev(list);
      return TRUE;
   }
   return FALSE;
}


/*
 * ****************************************************************************
 * GuestInfoResetNicLowPriorityList --
 *
 * Gets low-priority-nics setting from the config, and sets the list of low
 * priority patterns.
 *
 * @param[in]  ctx     The application context.
 *
 * @return TRUE to indicate that the value has changed, FALSE otherwise
 ******************************************************************************
 */

static Bool
GuestInfoResetNicLowPriorityList(ToolsAppCtx *ctx)
{
   static gchar *ifaceLowPriorityStringCached = NULL;
   gchar **list;

   if (GuestInfoSetConfigList(ctx, &ifaceLowPriorityStringCached,
                             CONFNAME_GUESTINFO_LOWPRIORITYNICS, NULL,
                             &list)) {
      GuestInfo_SetIfaceLowPriorityList(list);
      g_strfreev(list);
      return TRUE;
   }
   return FALSE;
}


/*
 * ****************************************************************************
 * GuestInfoResetNicExcludeList --
 *
 * Gets exclude-nics setting from the config, and sets the list of exclude
 * patterns.
 *
 * @param[in]  ctx     The application context.
 *
 * @return TRUE to indicate that the value has changed, FALSE otherwise
 ******************************************************************************
 */

static Bool
GuestInfoResetNicExcludeList(ToolsAppCtx *ctx)
{
   static gchar *ifaceExcludeStringCached = NULL;
   gchar **list;

   if (GuestInfoSetConfigList(ctx, &ifaceExcludeStringCached,
                             CONFNAME_GUESTINFO_EXCLUDENICS,
                             GUESTINFO_DEFAULT_IFACE_EXCLUDES,
                             &list)) {
      GuestInfo_SetIfaceExcludeList(list);
      g_strfreev(list);
      return TRUE;
   }
   return FALSE;
}


/*
 ******************************************************************************
 * GuestInfoDetailedDataIsEqual --
 *
 *    Compares two HostinfoDetailedDataHeader and the detailed data that
 *    follows each header.
 *
 * @returns True if equal
 *
 ******************************************************************************
 */

static Bool
GuestInfoDetailedDataIsEqual(const HostinfoDetailedDataHeader *info1,  // IN:
                             const HostinfoDetailedDataHeader *info2)  // IN:
{
   ASSERT(info1 != NULL);
   ASSERT(info2 != NULL);

   return (strncmp(info1->shortName, info2->shortName,
                   sizeof info1->shortName) == 0 &&
           strncmp(info1->fullName, info2->fullName,
                   sizeof info1->fullName) == 0 &&
           strcmp((char *)info1 + sizeof *info1,
                  (char *)info2 + sizeof *info2) == 0);
}


/*
 ******************************************************************************
 * GuestInfoFreeDetailedData --
 *
 * Free the HostinfoStructuredHeader and space allocated for the detailed
 * data.
 *
 * @returns None
 *
 ******************************************************************************
 */

static void
GuestInfoFreeDetailedData(HostinfoDetailedDataHeader *info)  // IN/OUT:
{
   free(info);
}


/*
 ******************************************************************************
 * GuestInfoGather --
 *
 * Collects all the desired guest information and updates the VMX.
 *
 * @param[in]  data     The application context.
 *
 * @return TRUE to indicate that the timer should be rescheduled.
 *
 ******************************************************************************
 */

static gboolean
GuestInfoGather(gpointer data)
{
   char name[256];  // Size is derived from the SUS2 specification
                    // "Host names are limited to 255 bytes"
#if !defined(USERWORLD)
   gboolean disableQueryDiskInfo;
   GuestDiskInfoInt *diskInfo = NULL;
#endif
   NicInfoV3 *nicInfo = NULL;
   ToolsAppCtx *ctx = data;
   Bool primaryChanged;
   Bool lowPriorityChanged;
   int maxIPv4RoutesToGather;
   int maxIPv6RoutesToGather;
   gchar *osNameOverride;
   gchar *osNameFullOverride = NULL;
   Bool maxNicsError = FALSE;
   static uint32 logThrottleCount = 0;
   Bool sendOsNames = FALSE;
   char *osName = NULL;
   char *osFullName = NULL;
   char *detailedGosData = NULL;

   g_debug("Entered guest info gather.\n");

   GuestInfoCheckIfRunningSlow(ctx);

   /* Send tools version. */
   if (!GuestInfoUpdateVMX(ctx, INFO_BUILD_NUMBER, BUILD_NUMBER, 0)) {
      /*
       * An older vmx talking to new tools wont be able to handle
       * this message. Continue, if thats the case.
       */

      g_warning("Failed to update the VMX with tools version.\n");
   }

   /* Check for manual override of guest information in the config file */
   osNameOverride = VMTools_ConfigGetString(ctx->config,
                                            CONFGROUPNAME_GUESTOSINFO,
                                            CONFNAME_GUESTOSINFO_SHORTNAME,
                                            NULL);
   /*
    * CONFNAME_GUESTINFO_LONGNAME is ignored if CONFNAME_GUESTINFO_SHORTNAME
    * is not provided
    */
   if (osNameOverride) {
      osNameFullOverride = VMTools_ConfigGetString(ctx->config,
                                                   CONFGROUPNAME_GUESTOSINFO,
                                                   CONFNAME_GUESTOSINFO_LONGNAME,
                                                   NULL);
   }

   /* Gather all the relevant guest information. */
   osFullName = Hostinfo_GetOSName();
   osName = Hostinfo_GetOSGuestString();

   if (gSendDetailedGosData) {
      detailedGosData = Hostinfo_GetOSDetailedData();
   }

   if (detailedGosData == NULL) {
      g_debug("No detailed data.\n");
      sendOsNames = TRUE;
      gSendDetailedGosData = FALSE;
   } else {
      /* Build and attempt to send the detailed data */
      HostinfoDetailedDataHeader *detailedDataHeader = NULL;
      size_t infoHeaderSize;
      size_t detailedGosDataLen;
      size_t infoSize;

      g_debug("Sending detailed data.\n");
      detailedGosDataLen = strlen(detailedGosData);
      infoHeaderSize = sizeof *detailedDataHeader;
      infoSize = infoHeaderSize + detailedGosDataLen + 1; // cover NUL

      detailedDataHeader = g_malloc(infoSize);
      /* Clear struct and memory allocated for detailed data */
      memset(detailedDataHeader, 0, infoSize);

      /* Set the version of the detailed data header used */
      detailedDataHeader->version = HOSTINFO_STRUCT_HEADER_VERSION;

      if (osNameOverride) {
         Str_Strcpy(detailedDataHeader->shortName, osNameOverride,
                    sizeof detailedDataHeader->shortName);
         if (osNameFullOverride == NULL) {
            g_debug(CONFNAME_GUESTOSINFO_LONGNAME " was not set in "
                    "tools.conf.\n");
         } else {
            Str_Strcpy(detailedDataHeader->fullName, osNameFullOverride,
                       sizeof detailedDataHeader->fullName);
         }
      } else {
         if (osName == NULL) {
            g_warning("Failed to get OS name.\n");
         } else {
            Str_Strcpy(detailedDataHeader->shortName, osName,
                       sizeof detailedDataHeader->shortName);
         }
         if (osFullName == NULL) {
            g_warning("Failed to get OS full name.\n");
         } else {
            Str_Strcpy(detailedDataHeader->fullName, osFullName,
                       sizeof detailedDataHeader->fullName);
         }
      }

      Str_Strcpy((char *)detailedDataHeader + infoHeaderSize,
                 detailedGosData, infoSize - infoHeaderSize);

      if (GuestInfoUpdateVMX(ctx, INFO_OS_DETAILED, detailedDataHeader,
                             infoSize)) {
         GuestInfoFreeDetailedData(gInfoCache.detailedData);
         gInfoCache.detailedData = detailedDataHeader;
         g_debug("Detailed data was sent successfully.\n");
      } else {
         /*
          * Only send the OS Name if the VMX failed to receive the detailed
          * data
          */
         gSendDetailedGosData = FALSE;
         sendOsNames = TRUE;
         g_debug("Detailed data was not sent successfully.\n");
      }
   }

   if (sendOsNames) {
      if (osNameOverride) {
         /* Use osName and osNameFull provided in config file */
         if (osNameFullOverride == NULL) {
            g_debug(CONFNAME_GUESTOSINFO_LONGNAME " was not set in "
                    "tools.conf, using empty string.\n");
         }
         if (!GuestInfoUpdateVMX(ctx,
                                 INFO_OS_NAME_FULL,
                                 (osNameFullOverride == NULL) ? "" :
                                 osNameFullOverride,
                                 0)) {
            g_warning("Failed to send INFO_OS_NAME_FULL\n");
         }
         if (!GuestInfoUpdateVMX(ctx, INFO_OS_NAME, osNameOverride, 0)) {
            g_warning("Failed to send INFO_OS_NAME\n");
         }
         g_debug("Using values in tools.conf to override OS Name.\n");
      } else {
         g_debug("Sending the short and long name\n");
         if (osFullName == NULL) {
            g_warning("Failed to get OS info.\n");
         } else {
            if (!GuestInfoUpdateVMX(ctx, INFO_OS_NAME_FULL, osFullName, 0)) {
               g_warning("Failed to update INFO_OS_NAME_FULL\n");
            }
         }
         if (osName == NULL) {
            g_warning("Failed to get OS info.\n");
         } else {
            if (!GuestInfoUpdateVMX(ctx, INFO_OS_NAME, osName, 0)) {
               g_warning("Failed to update INFO_OS_NAME\n");
            }
         }
      }
   }

   free(detailedGosData);
   free(osFullName);
   free(osName);
   g_free(osNameFullOverride);
   g_free(osNameOverride);

#if !defined(USERWORLD)
   disableQueryDiskInfo =
      g_key_file_get_boolean(ctx->config, CONFGROUPNAME_GUESTINFO,
                             CONFNAME_GUESTINFO_DISABLEQUERYDISKINFO, NULL);
   if (!disableQueryDiskInfo) {
      if ((diskInfo = GuestInfo_GetDiskInfo(ctx)) == NULL) {
         g_warning("Failed to get disk info.\n");
      } else {
         if (GuestInfoUpdateVMX(ctx, INFO_DISK_FREE_SPACE, diskInfo, 0)) {
            GuestInfo_FreeDiskInfo(gInfoCache.diskInfo);
            gInfoCache.diskInfo = diskInfo;
         } else {
            g_warning("Failed to update INFO_DISK_FREE_SPACE\n.");
            GuestInfo_FreeDiskInfo(diskInfo);
         }
      }
   }
#endif

   if (!System_GetNodeName(sizeof name, name)) {
      g_warning("Failed to get netbios name.\n");
   } else if (!GuestInfoUpdateVMX(ctx, INFO_DNS_NAME, name, 0)) {
      g_warning("Failed to update INFO_DNS_NAME.\n");
   }

   /* Get NIC information. */

   primaryChanged = GuestInfoResetNicPrimaryList(ctx);
   lowPriorityChanged = GuestInfoResetNicLowPriorityList(ctx);
   GuestInfoResetNicExcludeList(ctx);

   /*
    * Check the config registry for max IPv4/6 routes to gather
    */
   maxIPv4RoutesToGather =
         VMTools_ConfigGetInteger(ctx->config,
                                  CONFGROUPNAME_GUESTINFO,
                                  CONFNAME_GUESTINFO_MAXIPV4ROUTES,
                                  NICINFO_MAX_ROUTES);
   if (maxIPv4RoutesToGather < 0 ||
       maxIPv4RoutesToGather > NICINFO_MAX_ROUTES) {
      g_warning("Invalid %s.%s value: %d. Using default %u.\n",
                CONFGROUPNAME_GUESTINFO,
                CONFNAME_GUESTINFO_MAXIPV4ROUTES,
                maxIPv4RoutesToGather,
                NICINFO_MAX_ROUTES);
      maxIPv4RoutesToGather = NICINFO_MAX_ROUTES;
   }

   maxIPv6RoutesToGather =
         VMTools_ConfigGetInteger(ctx->config,
                                  CONFGROUPNAME_GUESTINFO,
                                  CONFNAME_GUESTINFO_MAXIPV6ROUTES,
                                  NICINFO_MAX_ROUTES);
   if (maxIPv6RoutesToGather < 0 ||
       maxIPv6RoutesToGather > NICINFO_MAX_ROUTES) {
      g_warning("Invalid %s.%s value: %d. Using default %u.\n",
                CONFGROUPNAME_GUESTINFO,
                CONFNAME_GUESTINFO_MAXIPV6ROUTES,
                maxIPv6RoutesToGather,
                NICINFO_MAX_ROUTES);
      maxIPv6RoutesToGather = NICINFO_MAX_ROUTES;
   }

   if (!GuestInfo_GetNicInfo(maxIPv4RoutesToGather,
                             maxIPv6RoutesToGather,
                             &nicInfo, &maxNicsError)) {
      g_warning("Failed to get NIC info.\n");
      /*
       * Return an empty NIC info.
       */
      nicInfo = Util_SafeCalloc(1, sizeof (struct NicInfoV3));
   }
   if (maxNicsError) {
      VMTools_VmxLogThrottled(&logThrottleCount, ctx->rpc,
                              "%s: NIC limit (%d) reached.",
                              __FUNCTION__, NICINFO_MAX_NICS);
   }

   /*
    * We need to check if the setting for the primary interfaces or low
    * priority NICs have changed, because GuestInfo_IsEqual_NicInfoV3 does not
    * detect a change in the order.
    */
   if (!primaryChanged && !lowPriorityChanged &&
       GuestInfo_IsEqual_NicInfoV3(nicInfo, gInfoCache.nicInfo)) {
      g_debug("NIC info not changed.\n");
      GuestInfo_FreeNicInfo(nicInfo);
   } else if (GuestInfoUpdateVMX(ctx, INFO_IPADDRESS, nicInfo, 0)) {
      /*
       * Since the update succeeded, free the old cached object, and assign
       * ours to the cache.
       */
      GuestInfo_FreeNicInfo(gInfoCache.nicInfo);
      gInfoCache.nicInfo = nicInfo;
   } else {
      g_warning("Failed to update INFO_IPADDRESS.\n");
      GuestInfo_FreeNicInfo(nicInfo);
   }

   /* Send the uptime to the VMX so that it can detect soft resets. */
   SendUptime(ctx);

   return TRUE;
}


/*
 ******************************************************************************
 * GuestInfoConvertNicInfoToNicInfoV1 --                                 */ /**
 *
 * Converts V3 XDR NicInfo to hand-packed GuestNicInfoV1.
 *
 * @note Any NICs above MAX_NICS or IPs above MAX_IPS will be truncated.
 *
 * @param[in]  info   V3 input data.
 * @param[out] infoV1 V1 output data.
 *
 * @retval TRUE Conversion succeeded.
 * @retval FALSE Conversion failed.
 *
 ******************************************************************************
 */

void
GuestInfoConvertNicInfoToNicInfoV1(NicInfoV3 *info,
                                   GuestNicInfoV1 *infoV1)
{
   uint32 maxNics;
   u_int i;

   ASSERT(info);
   ASSERT(infoV1);

   maxNics = MIN(info->nics.nics_len, MAX_NICS);
   infoV1->numNicEntries = maxNics;
   if (maxNics < info->nics.nics_len) {
      g_debug("Truncating NIC list for backwards compatibility.\n");
   }

   XDRUTIL_FOREACH(i, info, nics) {
      u_int j;
      uint32 maxIPs;
      GuestNicV3 *nic = XDRUTIL_GETITEM(info, nics, i);

      Str_Strcpy(infoV1->nicList[i].macAddress,
                 nic->macAddress,
                 sizeof infoV1->nicList[i].macAddress);

      maxIPs = MIN(nic->ips.ips_len, MAX_IPS);
      infoV1->nicList[i].numIPs = 0;

      XDRUTIL_FOREACH(j, nic, ips) {
         IpAddressEntry *ip = XDRUTIL_GETITEM(nic, ips, j);
         TypedIpAddress *typedIp = &ip->ipAddressAddr;

         if (typedIp->ipAddressAddrType != IAT_IPV4) {
            continue;
         }

         if (NetUtil_InetNToP(AF_INET, typedIp->ipAddressAddr.InetAddress_val,
                              infoV1->nicList[i].ipAddress[j],
                              sizeof infoV1->nicList[i].ipAddress[j])) {
            infoV1->nicList[i].numIPs++;
            if (infoV1->nicList[i].numIPs == maxIPs) {
               break;
            }
         }
      }

      if (infoV1->nicList[i].numIPs != nic->ips.ips_len) {
         g_debug("Some IP addresses were ignored for compatibility.\n");
      }
      if (i == maxNics) {
         break;
      }
   }
}


/*
 ******************************************************************************
 * GuestInfoSendMemoryInfo --                                            */ /**
 *
 * Push memory informations about the guest to the vmx
 *
 * @param[in] ctx       Application context.
 * @param[in] infoSize  Size of the struct to send
 * @param[in] info      Struct that contains memory info
 *
 * @retval TRUE  Update sent successfully.
 * @retval FALSE Had trouble with transmission.
 *
 ******************************************************************************
 */

static Bool
GuestInfoSendMemoryInfo(ToolsAppCtx *ctx,  // IN: Application context
                        uint64 infoSize,   // IN: Size of the following struct
                        void* info)        // IN: Pointer to GuestMemInfoRPC
{
   char *request;
   char header[32];
   size_t headerLen;
   size_t requestSize;
   Bool success = FALSE;

   Str_Sprintf(header, sizeof header, "%s  %d ", GUEST_INFO_COMMAND,
               INFO_MEMORY);

   headerLen = strlen(header);
   requestSize = headerLen + infoSize;

   request = g_malloc(requestSize);

   if (request == NULL) {
      g_warning("Failed to allocate GuestMemInfo memory.\n");
   } else {
      g_debug("Sending GuestMemInfo message.\n");

      /* Yes, stomp on the NUL at the end of the header */
      memcpy(request, header, headerLen);
      memcpy(request + headerLen, info, infoSize);

      /* Send all the information in the message. */
      success = RpcChannel_Send(ctx->rpc, request, requestSize, NULL, NULL);

      g_free(request);
   }

   if (success) {
      g_debug("GuestMemInfo sent successfully.\n");
   } else {
      g_warning("Error sending GuestMemInfo.\n");
   }

   return success;
}


/*
 ******************************************************************************
 *
 * NIC_INFO_V3_WITH_INFO_IPADDRESS_V3: Bump up the NICINFO_MAX_IPS to 2048
 * NIC_INFO_V3_WITH_INFO_IPADDRESS_V2: NICINFO_MAX_IPS to 64
 *
 * The current fallback paths:
 * +---------------+-------------------+-------------------+------------------+
 * |               | INFO_IPADDRESS_V3 | INFO_IPADDRESS_V2 | INFO_IPADDRESS   |
 * +---------------+-------------------+-------------------+------------------+
 * |  NIC_INFO_V3  |        (0)        |        (1)        |                  |
 * +---------------+-------------------+-------------------+------------------+
 * |  NIC_INFO_V2  |                   |        (2)        |                  |
 * +---------------+-------------------+-------------------+------------------+
 * |  NIC_INFO_V1  |                   |                   |        (3)       |
 * +---------------+-------------------+-------------------+------------------+
 *
 ******************************************************************************
 */


/*
 ******************************************************************************
 * GuestInfoSendNicInfoXdr --
 *
 * Push updated nic info to the VMX using XDR data format.
 *
 * @param[in] ctx      Application context.
 * @param[in] message  The protocol for a 'nic info' message.
 * @param[in] type     Guest information type.
 *
 * @retval TRUE  Update sent successfully.
 * @retval FALSE Had trouble with serialization or transmission.
 *
 ******************************************************************************
 */

static Bool
GuestInfoSendNicInfoXdr(ToolsAppCtx *ctx,          // IN
                        GuestNicProto *message,    // IN
                        GuestInfoType type)        // IN
{
   Bool status = FALSE;
   XDR xdrs;
   gchar *request;
   char *reply = NULL;
   size_t replyLen;

   /* Add the RPC preamble: message name, and type. */
   request = g_strdup_printf("%s  %d ", GUEST_INFO_COMMAND, type);

   if (DynXdr_Create(&xdrs) == NULL) {
      goto exit;
   }

   /* Write preamble and serialized NIC info to XDR stream. */
   if (!DynXdr_AppendRaw(&xdrs, request, strlen(request)) ||
       !xdr_GuestNicProto(&xdrs, message)) {
      g_warning("Error serializing NIC info v%d data.", message->ver);
   } else {
      status = RpcChannel_Send(ctx->rpc, DynXdr_Get(&xdrs), xdr_getpos(&xdrs),
                               &reply, &replyLen);
      if (!status) {
         g_warning("%s: update failed: request \"%s\", reply \"%s\".\n",
                    __FUNCTION__, request, VM_SAFE_STR(reply));
      }
      vm_free(reply);
   }

   /*
    * DynXdr_Destroy only tries to free storage returned by a call to
    * DynXdr_Create(NULL).
    */
   /* coverity[address_free] */
   DynXdr_Destroy(&xdrs, TRUE);

exit:
   g_free(request);
   return status;
}


/*
 ******************************************************************************
 * GuestInfoSendData --
 *
 * Push GuestInfo information to the VMX. So far, it is mainly used to
 * send the fixed NIC info V1 data.
 *
 * @param[in] ctx         Application context.
 * @param[in] info        Guest information data.
 * @param[in] infoLength  Length of guest information.
 * @param[in] type        Guest information type.
 *
 * @retval TRUE  Update sent successfully.
 * @retval FALSE Had trouble with transmission.
 *
 ******************************************************************************
 */

static Bool
GuestInfoSendData(ToolsAppCtx *ctx,                // IN
                  void *info,                      // IN
                  u_int infoLength,                // IN
                  GuestInfoType type)              // IN
{
   Bool status;
   gchar *request;
   u_int msgLength = infoLength;
   gchar *message;
   char *reply = NULL;
   size_t replyLen;

   /* Add the RPC preamble: message name, and type. */
   request = g_strdup_printf("%s  %d ", GUEST_INFO_COMMAND, type);

   msgLength += strlen(request);
   message = g_malloc(msgLength);

   memcpy(message, request, strlen(request));
   memcpy(message + strlen(request), info, infoLength);

   status = RpcChannel_Send(ctx->rpc, message, msgLength, &reply, &replyLen);
   if (!status) {
      g_warning("%s: update failed: request \"%s\", reply \"%s\".\n",
                __FUNCTION__, request, VM_SAFE_STR(reply));
   }
   vm_free(reply);

   g_free(message);
   g_free(request);

   return status;
}


/*
 ******************************************************************************
 * GuestNicInfoV3ToV3_64 --                                              */ /**
 *
 * New NicInfoV3 allows more IP addresses than the previous version. Preseve
 * the backward compatibility such that NicInfoV3 with old limit could still
 * go through without falling all the way to V2 or V1. It reduces the number
 * of IP addresses at each interface to the previous max limit 64.
 *
 * @param[in] info  NicInfoV3 container.
 *
 * @retval Pointer to a shallow copy of NicInfoV3 with reduced max IPs.
 *
 ******************************************************************************
 */

static NicInfoV3*
GuestNicInfoV3ToV3_64(NicInfoV3 *info)             // IN
{
   u_int idx;

   NicInfoV3 *info64 = g_malloc(sizeof(struct NicInfoV3));

   memcpy(info64, info, sizeof(struct NicInfoV3));
   info64->nics.nics_val =
      g_malloc(info64->nics.nics_len * sizeof(struct GuestNicV3));

   for (idx = 0; idx < info64->nics.nics_len; idx++) {
      memcpy(&info64->nics.nics_val[idx], &info->nics.nics_val[idx],
             sizeof(struct GuestNicV3));

      if (info64->nics.nics_val[idx].ips.ips_len > INFO_IPADDRESS_V2_MAX_IPS) {
         info64->nics.nics_val[idx].ips.ips_len = INFO_IPADDRESS_V2_MAX_IPS;
      }
   }

   return info64;
}


/*
 ******************************************************************************
 * GuestInfoSendNicInfo --
 *
 * Push updated NIC info to the VMX. Take care of failed transmissions or
 * unknown guest information types. Use a fixed sequence of fallback paths
 * to retry.
 *
 * @param[in] ctx   Application context.
 * @param[in] info  NicInfoV3 container.
 *
 * @retval TRUE  Update sent successfully.
 * @retval FALSE Had trouble with serialization or transmission.
 *
 ******************************************************************************
 */

static Bool
GuestInfoSendNicInfo(ToolsAppCtx *ctx,             // IN
                     NicInfoV3 *info)              // IN
{
   Bool status = FALSE;
   GuestNicProto message = {0};
   NicInfoV3 *info64 = NULL;

   do {
      switch (gInfoCache.method) {
      case NIC_INFO_V3_WITH_INFO_IPADDRESS_V3:
         message.ver = NIC_INFO_V3;
         message.GuestNicProto_u.nicInfoV3 = info;
         if (GuestInfoSendNicInfoXdr(ctx, &message, INFO_IPADDRESS_V3)) {
            status = TRUE;
         }
         break;
      case NIC_INFO_V3_WITH_INFO_IPADDRESS_V2:
         info64 = GuestNicInfoV3ToV3_64(info);
         message.ver = NIC_INFO_V3;
         message.GuestNicProto_u.nicInfoV3 = info64;
         if (GuestInfoSendNicInfoXdr(ctx, &message, INFO_IPADDRESS_V2)) {
            status = TRUE;
         }
         break;
      case NIC_INFO_V2_WITH_INFO_IPADDRESS_V2:
         if (!info64) {
            info64 = GuestNicInfoV3ToV3_64(info);
         }
         {
            GuestNicList *nicList = NicInfoV3ToV2(info64);

            message.ver = NIC_INFO_V2;
            message.GuestNicProto_u.nicsV2 = nicList;
            if (GuestInfoSendNicInfoXdr(ctx, &message, INFO_IPADDRESS_V2)) {
               status = TRUE;
            }
            VMX_XDR_FREE(xdr_GuestNicList, nicList);
            free(nicList);
         }
         break;
      case NIC_INFO_V1_WITH_INFO_IPADDRESS:
         {
            GuestNicInfoV1 infoV1;

            GuestInfoConvertNicInfoToNicInfoV1(info, &infoV1);
            if (GuestInfoSendData(ctx, &infoV1, sizeof(infoV1),
                                  INFO_IPADDRESS)) {
               status = TRUE;
            }
         }
         break;
      default:
         g_error("Invalid nicInfo send method: %d\n", gInfoCache.method);
         break;
      }
   } while (!status && (++gInfoCache.method < NIC_INFO_METHOD_MAX));

   if (info64) {
      g_free(info64->nics.nics_val);
      g_free(info64);
   }

   if (status) {
      g_debug("Updating nicInfo successfully: method=%d\n", gInfoCache.method);
   } else {
      gInfoCache.method = NIC_INFO_V3_WITH_INFO_IPADDRESS_V3;
      g_warning("Fail to send nicInfo: method=%d status=%d\n",
                gInfoCache.method, status);
   }

   return status;
}


/*
 ******************************************************************************
 * GuestInfoSendDiskInfoV1 --
 *
 * Push updated Disk info to the VMX, using the json GUEST_DISK_INFO_COMMAND
 * RPC
 *
 * @param[in] ctx       Application context.
 * @param[in] pdi       GuestDiskInfoInt *
 * @param[in] infoSize  Size of disk info.
 *
 * @retval TRUE  Update sent successfully.
 * @retval FALSE Had trouble with json string formatting, serialization or
 *               transmission.
 *
 ******************************************************************************
 */

static Bool
GuestInfoSendDiskInfoV1(ToolsAppCtx *ctx,             // IN
                        GuestDiskInfoInt *pdi,        // IN
                        size_t infoSize)              // IN
{
   DynBuf dynBuffer;
   char tmpBuf[1024];
   int len;
   char *infoReq;
   char *reply = NULL;
   size_t replyLen;
   Bool status = FALSE;
#ifdef _WIN32
   Bool reportUUID = VMTools_ConfigGetBoolean(ctx->config,
                                              CONFGROUPNAME_GUESTINFO,
                                              CONFNAME_DISKINFO_REPORT_UUID,
                                              CONFIG_GUESTINFO_REPORT_UUID_DEFAULT);
#endif

   /*
    * Currently this format is fixed; the order should not be changed.
    * If a change is required, then DISK_INFO_VERSION must be bumped.
    * Older versions cannot be removed to maintain backwards compatibility
    * with older VMXs.
    */
   static char headerFmt[] = "%s {\n"
                             "\"" DISK_INFO_KEY_VERSION "\":\"%d\",\n"
                             "\"" DISK_INFO_KEY_DISKS "\":[\n";
   static char jsonPerDiskFmt[] = "{"
                                  "\"" DISK_INFO_KEY_DISK_NAME "\":\"%s\","
                                  "\"" DISK_INFO_KEY_DISK_FREE "\":\"%"FMT64"u\","
                                  "\"" DISK_INFO_KEY_DISK_SIZE "\":\"%"FMT64"u\"";
   static char jsonPerDiskFsTypeFmt[] = ",\"" DISK_INFO_KEY_DISK_FSTYPE "\":\"%s\"";
#ifdef _WIN32
   static char jsonPerDiskUUIDFmt[] = ",\"" DISK_INFO_KEY_DISK_UUID "\":\"%s\"";
#else
   static char jsonPerDiskDevArrHdrFmt[] =
                                    ",\"" DISK_INFO_KEY_DISK_DEVICE_ARR "\":[";
   static char jsonPerDiskDeviceFmt[] = "%s\"%s\"";
   static char jsonPerDiskDeviceSep[] = ",";
   static char jsonPerDiskDevArrFmtFooter[] = "]";
#endif
   static char jsonPerDiskFmtFooter[] = "},\n";
   static char jsonPerDiskFmtFooterLast[] = "}\n";
   static char jsonSuffix[] = "]}";
   int i;

   // 20 bytes per number for ascii representation
   // PARTITION_NAME_SIZE * 2 for name and (optional) uuid
   ASSERT_ON_COMPILE(sizeof tmpBuf > sizeof jsonPerDiskFmt +
                     PARTITION_NAME_SIZE * 2 + 20 + 20);

   DynBuf_Init(&dynBuffer);

   len = Str_Snprintf(tmpBuf, sizeof tmpBuf, headerFmt,
                      GUEST_DISK_INFO_COMMAND, DISK_INFO_VERSION_1);
   if (len <= 0) {
      goto exit;
   }

   DynBuf_Append(&dynBuffer, tmpBuf, len);
   for (i = 0; i < pdi->numEntries; i++) {
      gchar *b64name;

      /*
       * If more than a single disk partition or filesystem to be reported,
       * terminate the previous partition element in the disk array.
       */
      if (i != 0) {
         DynBuf_Append(&dynBuffer, jsonPerDiskFmtFooter,
                       sizeof jsonPerDiskFmtFooter - 1);
      }

      /*
       * The '\' in Windows drive names needs escaping for json,
       * so use base64 since its simple and will cover other weird
       * cases like quotes, as well as avoid any utf-8 concerns.
       */
      b64name = g_base64_encode(pdi->partitionList[i].name,
                                strlen(pdi->partitionList[i].name));

      len = Str_Snprintf(tmpBuf, sizeof tmpBuf, jsonPerDiskFmt,
                         b64name,
                         pdi->partitionList[i].freeBytes,
                         pdi->partitionList[i].totalBytes);
      g_free(b64name);
      if (len <= 0) {
         goto exit;
      }

      DynBuf_Append(&dynBuffer, tmpBuf, len);

      if (pdi->partitionList[i].fsType[0] != '\0') {
         len = Str_Snprintf(tmpBuf, sizeof tmpBuf, jsonPerDiskFsTypeFmt,
                            pdi->partitionList[i].fsType);
         if (len <= 0) {
            goto exit;
         }

         DynBuf_Append(&dynBuffer, tmpBuf, len);
      }
#ifdef _WIN32
      if (reportUUID) {
         if (pdi->partitionList[i].uuid[0] != '\0') {
            len = Str_Snprintf(tmpBuf, sizeof tmpBuf, jsonPerDiskUUIDFmt,
                               pdi->partitionList[i].uuid);
            if (len <= 0) {
               goto exit;
            }

            DynBuf_Append(&dynBuffer, tmpBuf, len);
         }
      }
#else
      if (pdi->partitionList[i].diskDevCnt > 0) {
         int idx;

         DynBuf_Append(&dynBuffer, jsonPerDiskDevArrHdrFmt,
                       sizeof jsonPerDiskDevArrHdrFmt - 1);
         len = Str_Snprintf(tmpBuf, sizeof tmpBuf, jsonPerDiskDeviceFmt,
                            "", pdi->partitionList[i].diskDevNames[0]);
         if (len <= 0) {
            goto exit;
         }

         DynBuf_Append(&dynBuffer, tmpBuf, len);
         for (idx = 1; idx < pdi->partitionList[i].diskDevCnt; idx++) {
            len = Str_Snprintf(tmpBuf, sizeof tmpBuf, jsonPerDiskDeviceFmt,
                               jsonPerDiskDeviceSep,
                               pdi->partitionList[i].diskDevNames[idx]);
            if (len <= 0) {
               goto exit;
            }

            DynBuf_Append(&dynBuffer, tmpBuf, len);
         }
         DynBuf_Append(&dynBuffer, jsonPerDiskDevArrFmtFooter,
                       sizeof jsonPerDiskDevArrFmtFooter - 1);
      }
#endif
   }
   if (pdi->numEntries > 0) {
      /* Terminate the last element of the disk partition JSON array. */
      DynBuf_Append(&dynBuffer, jsonPerDiskFmtFooterLast,
                    sizeof jsonPerDiskFmtFooterLast - 1);
   }

   DynBuf_Append(&dynBuffer, jsonSuffix, sizeof jsonSuffix - 1);

   infoReq = DynBuf_GetString(&dynBuffer);

   g_debug("%s: sending diskInfo RPC: '%s'\n", __FUNCTION__, infoReq);

   status = RpcChannel_Send(ctx->rpc, infoReq, strlen(infoReq) + 1, &reply,
                            &replyLen);
   if (status) {
      status = (*reply == '\0');
      if (!status) {
         g_debug("%s: unexpected reply '%s'\n", __FUNCTION__, reply);
      }
   } else {
      g_debug("%s: RPC failed (%d) reply '%s'\n",
              __FUNCTION__, status, VM_SAFE_STR(reply));
   }

   vm_free(reply);

exit:
   DynBuf_Destroy(&dynBuffer);
   return status;
}


#if defined(_WIN64) && (_MSC_VER == 1500) && GLIB_CHECK_VERSION(2, 46, 0)
/*
 * Turn off optimizer for this compiler, since something with new glib
 * makes it go into an infinite loop, only on 64bit.
 */
#pragma optimize("", off)
#endif

/*
 ******************************************************************************
 * GuestInfoSendDiskInfoV0 --
 *
 * Push updated Disk info to the VMX, using the binary INFO_DISK_FREE_SPACE
 * format.
 *
 * @param[in] ctx       Application context.
 * @param[in] pdiInt    GuestDiskInfoInt *
 * @param[in] infoSize  Size of disk info.
 *
 * @retval TRUE  Update sent successfully.
 * @retval FALSE Had trouble with serialization or transmission.
 *
 ******************************************************************************
 */

static Bool
GuestInfoSendDiskInfoV0(ToolsAppCtx *ctx,             // IN
                        GuestDiskInfoInt *pdiInt,     // IN
                        size_t infoSize)              // IN
{
   /*
    * 2 accounts for the digits of infotype and 3 for the three
    * spaces.
    */
   unsigned int requestSize = sizeof GUEST_INFO_COMMAND + 2 +
                              3 * sizeof (char);
   uint8 partitionCount;
   size_t offset;
   char *request;
   char *reply;
   size_t replyLen;
   Bool status;
   GuestDiskInfo *pdi;
   int i;

   ASSERT((pdiInt->numEntries && pdiInt->partitionList) ||
          (!pdiInt->numEntries && !pdiInt->partitionList));

   /*
    * Build a GuestDiskInfo structure to provide the expected binary
    * format for the binary RPC, copying out the V0 elements.
    */
   pdi = Util_SafeCalloc(1, sizeof *pdi);
   pdi->numEntries = pdiInt->numEntries;
   pdi->partitionList = Util_SafeCalloc(pdi->numEntries,
                                        sizeof *pdi->partitionList);
   for (i = 0; i < pdi->numEntries; i++) {
      pdi->partitionList[i].freeBytes = pdiInt->partitionList[i].freeBytes;
      pdi->partitionList[i].totalBytes = pdiInt->partitionList[i].totalBytes;
      Str_Strcpy(pdi->partitionList[i].name,
                 pdiInt->partitionList[i].name,
                 PARTITION_NAME_SIZE);
   }

   /* partitionCount is a uint8 and cannot be larger than UCHAR_MAX. */
   if (pdi->numEntries > UCHAR_MAX) {
      g_message("%s: Too many local filesystems (%d); truncating to %d entries\n",
                __FUNCTION__, pdi->numEntries, UCHAR_MAX);
      partitionCount = UCHAR_MAX;
   } else {
      partitionCount = pdi->numEntries;
   }

   requestSize += sizeof partitionCount +
                  sizeof *pdi->partitionList * partitionCount;
   request = Util_SafeCalloc(requestSize, sizeof *request);

   Str_Sprintf(request, requestSize, "%s  %d ", GUEST_INFO_COMMAND,
               INFO_DISK_FREE_SPACE);

   offset = strlen(request);

   /*
    * Construct the disk information message to send to the host.  This
    * contains a single byte indicating the number partitions followed by
    * the PartitionEntry structure for each one.
    *
    * Note that the use of a uint8 to specify the partitionCount is the
    * result of a bug (see bug 117224) but should not cause a problem
    * since UCHAR_MAX is 255.  Also note that PartitionEntry is packed so
    * it's safe to send it from 64-bit Tools to a 32-bit VMX, etc.
    */
   memcpy(request + offset, &partitionCount, sizeof partitionCount);

   /*
    * Conditioned because memcpy(dst, NULL, 0) -may- lead to undefined
    * behavior.
    */
   if (pdi->partitionList) {
      memcpy(request + offset + sizeof partitionCount, pdi->partitionList,
             sizeof *pdi->partitionList * partitionCount);
   }

   g_debug("%s: sizeof request is %d\n", __FUNCTION__, requestSize);
   status = RpcChannel_Send(ctx->rpc, request, requestSize, &reply,
                            &replyLen);
   if (status) {
      status = (*reply == '\0');
      if (!status) {
         g_debug("%s: unexpected reply '%s'\n", __FUNCTION__, reply);
      }
   }

   vm_free(request);
   vm_free(reply);

   vm_free(pdi->partitionList);
   vm_free(pdi);

   return status;
}


/*
 ******************************************************************************
 * GuestInfoSendDiskInfo --
 *
 * Push updated Disk info to the VMX.
 *
 * @param[in] ctx       Application context.
 * @param[in] info      GuestDiskInfoInt *
 * @param[in] infoSize  Size of disk info.
 *
 * @retval TRUE  Update sent successfully.
 * @retval FALSE Had trouble with serialization or transmission.
 *
 ******************************************************************************
 */

static Bool
GuestInfoSendDiskInfo(ToolsAppCtx *ctx,             // IN
                      GuestDiskInfoInt *info,       // IN
                      size_t infoSize)              // IN
{
   if (gInfoCache.diskInfoUseJson &&
       GuestInfoSendDiskInfoV1(ctx, info, infoSize)) {
      return TRUE;
   } else {
      gInfoCache.diskInfoUseJson = FALSE;
   }

   /* fail over to unversioned format */
   g_debug("%s: using V0 (unversioned) diskinfo format\n", __FUNCTION__);

   return GuestInfoSendDiskInfoV0(ctx, info, infoSize);
}


/*
 ******************************************************************************
 * GuestInfoUpdateVMX --
 *
 * Push singular GuestInfo snippets to the VMX.
 *
 * @note Data are cached, so updates are sent only if they have changed.
 *
 * @param[in] ctx       Application context.
 * @param[in] infoType  Guest information type.
 * @param[in] info      Type-specific information.
 *
 * @retval TRUE  Update sent successfully.
 * @retval FALSE Had trouble with serialization or transmission.
 *
 ******************************************************************************
 */

static Bool
GuestInfoUpdateVMX(ToolsAppCtx *ctx,        // IN: Application context
                   GuestInfoType infoType,  // IN: guest information type
                   void *info,              // IN: type specific information
                   size_t infoSize)         // IN: size of *info
{
   ASSERT(info);
   g_debug("Entered update the VMX: %d.\n", infoType);

   if (gVMResumed) {
      gVMResumed = FALSE;
      GuestInfoClearCache();
   }

   switch (infoType) {
   case INFO_DNS_NAME:
   case INFO_BUILD_NUMBER:
   case INFO_OS_NAME:
   case INFO_OS_NAME_FULL:
   case INFO_UPTIME:
      /*
       * This is one of our key value pairs. Update it if it has changed.
       * Above fall-through is intentional.
       */

      if (gInfoCache.value[infoType] != NULL &&
          strcmp(gInfoCache.value[infoType], (char *)info) == 0) {
         /* The value has not changed */
         g_debug("Value unchanged for infotype %d.\n", infoType);
         break;
      }

      if (!SetGuestInfo(ctx, infoType, (char *)info)) {
         g_warning("Failed to update key/value pair for type %d.\n", infoType);
         return FALSE;
      }

      if (infoType == INFO_OS_NAME) {
         g_message("Updated Guest OS name to %s\n", (char *) info);
      } else if (infoType == INFO_OS_NAME_FULL) {
         g_message("Updated Guest OS full name to %s\n", (char *) info);
      }

      /* Update the value in the cache as well. */
      free(gInfoCache.value[infoType]);
      gInfoCache.value[infoType] = Util_SafeStrdup((char *) info);
      break;

   case INFO_OS_DETAILED:
      {
         if (gInfoCache.detailedData != NULL &&
             GuestInfoDetailedDataIsEqual(gInfoCache.detailedData,
                                          (HostinfoDetailedDataHeader *)info)) {
            /* The value has not changed */
            g_debug("Value unchanged for detailedData.\n");
            break;
         }

         if (!GuestInfoSendData(ctx, info, infoSize, INFO_OS_DETAILED)) {
            g_warning("Failed to update detailed data");
            return FALSE;
         }
         break;
      }

   case INFO_IPADDRESS:
      {
         if (!GuestInfoSendNicInfo(ctx, (NicInfoV3 *) info)) {
            g_warning("Failed to update NIC information.\n");
            return FALSE;
         }
         break;
      }

   case INFO_MEMORY:
      {
         if (!GuestInfoSendMemoryInfo(ctx, infoSize, info)) {
            g_warning("Unable to send GuestMemInfo\n");
            return FALSE;
         }
         break;
      }

   case INFO_DISK_FREE_SPACE:
      {
         if (!DiskInfoChanged(info)) {
            g_debug("Disk info not changed.\n");
            break;
         }

         if (!GuestInfoSendDiskInfo(ctx, info, infoSize)) {
            g_warning("Failed to update disk information.\n");
            return FALSE;
         }

         g_debug("Updated disk info information\n");

         break;
      }
   default:
      g_error("Invalid info type: %d\n", infoType);
      break;
   }

   g_debug("Returning after updating guest information: %d\n", infoType);
   return TRUE;
}
#if defined(_WIN64) && (_MSC_VER == 1500) && GLIB_CHECK_VERSION(2, 46, 0)
/*
 * Restore optimizer.
 */
#pragma optimize("", on)
#endif


/*
 ******************************************************************************
 * SendUptime --                                                         */ /**
 *
 * Send the guest uptime through the backdoor.
 *
 * @param[in]  ctx      The application context.
 *
 ******************************************************************************
 */

static void
SendUptime(ToolsAppCtx *ctx)
{
   gchar *uptime = g_strdup_printf("%"FMT64"u", System_Uptime());
   g_debug("Setting guest uptime to '%s'\n", uptime);
   GuestInfoUpdateVMX(ctx, INFO_UPTIME, uptime, 0);
   g_free(uptime);
}


/*
 ******************************************************************************
 * SetGuestInfo --
 *
 * Sends a simple key-value update request to the VMX.
 *
 * @param[in] ctx       Application context.
 * @param[in] key       Key sent to the VMX
 * @param[in] value     GuestInfo data sent to the VMX
 *
 * @retval TRUE  RPCI succeeded.
 * @retval FALSE RPCI failed.
 *
 ******************************************************************************
 */

Bool
SetGuestInfo(ToolsAppCtx *ctx,   // IN:
             GuestInfoType key,  // IN:
             const char *value)  // IN:
{
   Bool status;
   char *reply;
   gchar *msg;
   size_t replyLen;

   ASSERT(key);
   ASSERT(value);

   /*
    * XXX Consider retiring this runtime "delimiter" business and just
    * insert raw spaces into the format string.
    */
   msg = g_strdup_printf("%s %c%d%c%s", GUEST_INFO_COMMAND,
                         GUESTINFO_DEFAULT_DELIMITER, key,
                         GUESTINFO_DEFAULT_DELIMITER, value);

   status = RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, &reply, &replyLen);
   g_free(msg);

   if (!status) {
      g_warning("Error sending RPC message: %s\n", reply ? reply : "NULL");
      vm_free(reply);
      return FALSE;
   }

   /* The reply indicates whether the key,value pair was updated in the VMX. */
   status = (*reply == '\0');
   vm_free(reply);
   return status;
}


/*
 ******************************************************************************
 * GuestInfoFindMacAddress --
 *
 * Locates a NIC with the given MAC address in the NIC list.
 *
 * @param[in] nicInfo    NicInfoV3 container.
 * @param[in] macAddress Requested MAC address.
 *
 * @return Valid pointer if NIC found, else NULL.
 *
 ******************************************************************************
 */


GuestNicV3 *
GuestInfoFindMacAddress(NicInfoV3 *nicInfo,     // IN/OUT
                        const char *macAddress) // IN
{
   u_int i;

   for (i = 0; i < nicInfo->nics.nics_len; i++) {
      GuestNicV3 *nic = &nicInfo->nics.nics_val[i];

      if (strncmp(nic->macAddress, macAddress, NICINFO_MAC_LEN) == 0) {
         return nic;
      }
   }

   return NULL;
}


/*
 ******************************************************************************
 * DiskInfoChanged --
 *
 * Checks whether disk info information just obtained is different from the
 * information last sent to the VMX.
 *
 * @param[in]  diskInfo New disk info.
 *
 * @retval TRUE  Data has changed.
 * @retval FALSE Data has not changed.
 *
 ******************************************************************************
 */

static Bool
DiskInfoChanged(const GuestDiskInfoInt *diskInfo)
{
   int index;
   int i;
   GuestDiskInfoInt *cachedDiskInfo;

   cachedDiskInfo = gInfoCache.diskInfo;

   if (cachedDiskInfo == diskInfo) {
      return FALSE;
      /* Implies that either cachedDiskInfo or diskInfo != NULL. */
   } else if (!cachedDiskInfo || !diskInfo) {
      return TRUE;
   }

   if (cachedDiskInfo->numEntries != diskInfo->numEntries) {
      g_debug("Number of disks has changed\n");
      return TRUE;
   }

   /* Have any disks been modified? */
   for (index = 0; index < cachedDiskInfo->numEntries; index++) {
      int matchedPartition;
      char *name = cachedDiskInfo->partitionList[index].name;

      /* Find the corresponding partition in the new partition info. */
      for (i = 0; i < diskInfo->numEntries; i++) {
         if (!strncmp(diskInfo->partitionList[i].name, name, PARTITION_NAME_SIZE)) {
            break;
         }
      }

      matchedPartition = i;
      if (matchedPartition == diskInfo->numEntries) {
         /* This partition has been deleted. */
         g_debug("Partition %s deleted\n", name);
         return TRUE;
      } else {
         /* Compare the free space. */
         if (diskInfo->partitionList[matchedPartition].freeBytes !=
             cachedDiskInfo->partitionList[index].freeBytes) {
            g_debug("Free space changed\n");
            return TRUE;
         }
         if (diskInfo->partitionList[matchedPartition].totalBytes !=
            cachedDiskInfo->partitionList[index].totalBytes) {
            g_debug("Total space changed\n");
            return TRUE;
         }
      }
   }

   return FALSE;
}


/*
 ******************************************************************************
 * GuestInfoClearCache --
 *
 * Clears the cached guest info data.
 *
 ******************************************************************************
 */

static void
GuestInfoClearCache(void)
{
   int i;

   for (i = 0; i < INFO_MAX; i++) {
      free(gInfoCache.value[i]);
      gInfoCache.value[i] = NULL;
   }

   GuestInfoFreeDetailedData(gInfoCache.detailedData);
   gInfoCache.detailedData = NULL;

   GuestInfo_FreeDiskInfo(gInfoCache.diskInfo);
   gInfoCache.diskInfo = NULL;
   gInfoCache.diskInfoUseJson = TRUE;

   GuestInfo_FreeNicInfo(gInfoCache.nicInfo);
   gInfoCache.nicInfo = NULL;

   gInfoCache.method = NIC_INFO_V3_WITH_INFO_IPADDRESS_V3;
}


/*
 ***********************************************************************
 * NicInfoV3ToV2 --
 *
 * @brief Converts the NicInfoV3 NIC list to a GuestNicList.
 *
 * @note  This function performs @e shallow copies of things such as
 *        IP address array, making it depend on the source NicInfoV3.
 *        In other words, do @e not free NicInfoV3 before freeing the
 *        returned pointer.
 *
 * @param[in]  infoV3   Source NicInfoV3 container.
 *
 * @return Pointer to a GuestNicList.  Caller should free it using
 *         plain ol' @c free.
 *
 ***********************************************************************
 */

static GuestNicList *
NicInfoV3ToV2(const NicInfoV3 *infoV3)
{
   unsigned int i, j;
   GuestNicList *nicList = Util_SafeCalloc(sizeof *nicList, 1);

   (void)XDRUTIL_ARRAYAPPEND(nicList, nics, infoV3->nics.nics_len);
   XDRUTIL_FOREACH(i, infoV3, nics) {
      GuestNicV3 *nic = XDRUTIL_GETITEM(infoV3, nics, i);
      GuestNic *oldNic = XDRUTIL_GETITEM(nicList, nics, i);

      Str_Strcpy(oldNic->macAddress, nic->macAddress,
                 sizeof oldNic->macAddress);

      (void)XDRUTIL_ARRAYAPPEND(oldNic, ips, nic->ips.ips_len);

      XDRUTIL_FOREACH(j, nic, ips) {
         IpAddressEntry *ipEntry = XDRUTIL_GETITEM(nic, ips, j);
         TypedIpAddress *ip = &ipEntry->ipAddressAddr;
         VmIpAddress *oldIp = XDRUTIL_GETITEM(oldNic, ips, j);

         /* XXX */
         oldIp->addressFamily = (ip->ipAddressAddrType == IAT_IPV4) ?
                                NICINFO_ADDR_IPV4 : NICINFO_ADDR_IPV6;

         NetUtil_InetNToP(ip->ipAddressAddrType == IAT_IPV4 ?
                          AF_INET : AF_INET6,
                          ip->ipAddressAddr.InetAddress_val, oldIp->ipAddress,
                          sizeof oldIp->ipAddress);

         Str_Sprintf(oldIp->subnetMask, sizeof oldIp->subnetMask, "%u",
                     ipEntry->ipAddressPrefixLength);
      }
   }

   return nicList;
}


/*
 ******************************************************************************
 * TweakGatherLoop --
 *
 * @brief Start, stop, reconfigure a GuestInfoGather poll loop.
 *
 * This function is responsible for creating, manipulating, and resetting a
 * GuestInfoGather loop timeout source.
 *
 * @param[in]     ctx           The app context.
 * @param[in]     enable        Whether to enable the gather loop.
 * @param[in]     cfgKey        Config key to fetch user pref.
 * @param[in]     defInterval   Default interval value in seconds.
 * @param[in]     callback      Function to be called on expiry of interval.
 * @param[in,out] currInterval  Current interval value in seconds.
 * @param[out]    timeoutSource GSource object created.
 *
 ******************************************************************************
 */

static void
TweakGatherLoop(ToolsAppCtx *ctx,
                gboolean enable,
                gchar *cfgKey,
                gint defInterval,
                GSourceFunc callback,
                gint *currInterval,
                GSource **timeoutSource)
{
   gint pollInterval = 0;

   if (enable) {
      /*
       * Check the config registry for custom poll interval,
       * converting from seconds to milliseconds.
       */
      pollInterval = VMTools_ConfigGetInteger(ctx->config,
                                              CONFGROUPNAME_GUESTINFO,
                                              cfgKey, defInterval);
      if (pollInterval < 0 || pollInterval > (G_MAXINT / 1000)) {
         g_warning("Invalid %s.%s value. Using default %us.\n",
                   CONFGROUPNAME_GUESTINFO, cfgKey, defInterval);
         pollInterval = defInterval;
      }

      pollInterval *= 1000;
   }

   if (*timeoutSource != NULL) {
      /*
       * If the interval hasn't changed, let's not interfere with the existing
       * timeout source.
       */
      if (pollInterval == *currInterval) {
         ASSERT(pollInterval);
         return;
      }

      /*
       * Destroy the existing timeout source since the interval has changed.
       */

      g_source_destroy(*timeoutSource);
      *timeoutSource = NULL;
   }

   /*
    * All checks have passed.  Create a new timeout source and attach it.
    */
   *currInterval = pollInterval;

   if (*currInterval) {
      g_info("New value for %s is %us.\n", cfgKey, *currInterval / 1000);

      *timeoutSource = g_timeout_source_new(*currInterval);
      VMTOOLSAPP_ATTACH_SOURCE(ctx, *timeoutSource, callback, ctx, NULL);
      g_source_unref(*timeoutSource);
   } else {
      g_info("Poll loop for %s disabled.\n", cfgKey);
   }
}


/*
 ******************************************************************************
 * TweakGatherLoops --
 *
 * @brief Start, stop, reconfigure the GuestInfoGather loops.
 *
 * This function is responsible for creating, manipulating, and resetting
 * the GuestInfoGather loops (info and stats) timeout sources.
 *
 * @param[in]  ctx      The app context.
 * @param[in]  enable   Whether to enable the gather loops.
 *
 * @sa CONFNAME_GUESTINFO_POLLINTERVAL
 * @sa CONFNAME_GUESTINFO_STATSINTERVAL
 *
 ******************************************************************************
 */

static void
TweakGatherLoops(ToolsAppCtx *ctx,
                 gboolean enable)
{
#if defined(__linux__) || defined(USERWORLD) || defined(_WIN32)
   gboolean perfmonEnabled;

   perfmonEnabled = !g_key_file_get_boolean(ctx->config,
                                            CONFGROUPNAME_GUESTINFO,
                                            CONFNAME_GUESTINFO_DISABLEPERFMON,
                                            NULL);

   if (perfmonEnabled) {
      /*
       * Tweak GuestStats gather loop
       */
      TweakGatherLoop(ctx, enable,
                      CONFNAME_GUESTINFO_STATSINTERVAL,
                      GUESTINFO_STATS_INTERVAL,
                      GuestInfo_StatProviderPoll,
                      &guestInfoStatsInterval,
                      &gatherStatsTimeoutSource);
   } else {
      /*
       * Destroy the existing timeout source, if it exists.
       */
      if (gatherStatsTimeoutSource != NULL) {
         g_source_destroy(gatherStatsTimeoutSource);
         gatherStatsTimeoutSource = NULL;

         g_info("PerfMon gather loop disabled.\n");
      }
   }
#endif

   /*
    * Tweak GuestInfo gather loop
    */
   TweakGatherLoop(ctx, enable,
                   CONFNAME_GUESTINFO_POLLINTERVAL,
                   GUESTINFO_POLL_INTERVAL,
                   GuestInfoGather,
                   &guestInfoPollInterval,
                   &gatherInfoTimeoutSource);
}


/*
 ******************************************************************************
 *
 * GuestInfo_ServerReportStats --
 *
 *      Report gathered stats.
 *
 * Results:
 *      Stats reported to the VMX. Returns FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 ******************************************************************************
 */

Bool
GuestInfo_ServerReportStats(ToolsAppCtx *ctx,  // IN
                            DynBuf *stats)     // IN
{
   return GuestInfoUpdateVMX(ctx,
                             INFO_MEMORY,
                             DynBuf_Get(stats),
                             DynBuf_GetSize(stats));
}


/*
 ******************************************************************************
 * BEGIN Tools Core Services goodies.
 */


/*
 ******************************************************************************
 * GuestInfoServerConfReload --
 *
 * @brief Reconfigures the poll loop interval upon config file reload.
 *
 * @param[in]  src     The source object.
 * @param[in]  ctx     The application context.
 * @param[in]  data    Unused.
 *
 ******************************************************************************
 */

static void
GuestInfoServerConfReload(gpointer src,
                          ToolsAppCtx *ctx,
                          gpointer data)
{
   TweakGatherLoops(ctx, TRUE);
}


/*
 ******************************************************************************
 * GuestInfoServerIOFreeze --
 *
 * IO freeze signal handler. Disables info gathering while I/O is frozen.
 * See bug 529653.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The application context.
 * @param[in]  freeze   Whether I/O is being frozen.
 * @param[in]  data     Unused.
 *
 ******************************************************************************
 */
#if !defined(USERWORLD)
static void
GuestInfoServerIOFreeze(gpointer src,
                        ToolsAppCtx *ctx,
                        gboolean freeze,
                        gpointer data)
{
   TweakGatherLoops(ctx, !freeze);
}
#endif

/*
 ******************************************************************************
 * GuestInfoServerShutdown --
 *
 * Cleanup internal data on shutdown.
 *
 * @param[in]  src     The source object.
 * @param[in]  ctx     Unused.
 * @param[in]  data    Unused.
 *
 ******************************************************************************
 */

static void
GuestInfoServerShutdown(gpointer src,
                        ToolsAppCtx *ctx,
                        gpointer data)
{
   GuestInfoClearCache();

   GuestInfo_SetIfaceExcludeList(NULL);

   if (gatherInfoTimeoutSource != NULL) {
      g_source_destroy(gatherInfoTimeoutSource);
      gatherInfoTimeoutSource = NULL;
   }

   if (gatherStatsTimeoutSource != NULL) {
      g_source_destroy(gatherStatsTimeoutSource);
      gatherStatsTimeoutSource = NULL;
   }

#if defined(__linux__) || defined(USERWORLD) || defined(_WIN32)
   GuestInfo_StatProviderShutdown();
#endif

#ifdef _WIN32
   NetUtil_FreeIpHlpApiDll();
#endif
}


/*
 ******************************************************************************
 * GuestInfoServerReset --
 *
 * Reset callback - sets the internal flag that says we should purge all
 * caches.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Unused.
 * @param[in]  data     Unused.
 *
 ******************************************************************************
 */

static void
GuestInfoServerReset(gpointer src,
                     ToolsAppCtx *ctx,
                     gpointer data)
{
   gVMResumed = TRUE;

   /* Reset the last gather time */
   gGuestInfoLastGatherTime = 0;

   /* Reset detailed guest OS data sending */
   gSendDetailedGosData = TRUE;
}


/*
 ******************************************************************************
 * GuestInfoServerSendCaps --
 *
 * Send capabilities callback.  If setting capabilities, sends VM's uptime.
 *
 * This is weird.  There's sort of an old Tools <-> VMX understanding that
 * vmsvc should report the guest's uptime in response to a "what're your
 * capabilities?" RPC.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The application context.
 * @param[in]  set      TRUE if setting capabilities, FALSE if unsetting them.
 * @param[in]  data     Client data.
 *
 * @retval NULL This function returns no capabilities.
 *
 ******************************************************************************
 */

static GArray *
GuestInfoServerSendCaps(gpointer src,
                        ToolsAppCtx *ctx,
                        gboolean set,
                        gpointer data)
{
   if (set) {
      SendUptime(ctx);
   }
   return NULL;
}


/*
 ******************************************************************************
 * GuestInfoServerSetOption --
 *
 * Responds to a "broadcastIP" Set_Option command, by sending the primary IP
 * back to the VMX.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The application context.
 * @param[in]  option   Option name.
 * @param[in]  value    Option value.
 * @param[in]  data     Unused.
 *
 ******************************************************************************
 */

static gboolean
GuestInfoServerSetOption(gpointer src,
                         ToolsAppCtx *ctx,
                         const gchar *option,
                         const gchar *value,
                         gpointer data)
{
   char *ip;
   Bool ret = FALSE;
   gchar *msg;

   if (strcmp(option, TOOLSOPTION_BROADCASTIP) != 0) {
      goto exit;
   }

   if (strcmp(value, "0") == 0) {
      ret = TRUE;
      goto exit;
   }

   if (strcmp(value, "1") != 0) {
      goto exit;
   }

   ip = GuestInfo_GetPrimaryIP();

   msg = g_strdup_printf("info-set guestinfo.ip %s", ip);
   ret = RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, NULL, NULL);
   vm_free(ip);
   g_free(msg);

exit:
   return (gboolean) ret;
}


/*
 ******************************************************************************
 * ToolsOnLoad --
 *
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in]  ctx   The app context.
 *
 * @return The registration data.
 *
 ******************************************************************************
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "guestInfo",
      NULL,
      NULL
   };

   /*
    * This plugin is useless without an RpcChannel.  If we don't have one,
    * just bail.
    */
   if (ctx->rpc != NULL) {
      RpcChannelCallback rpcs[] = {
         { RPC_VMSUPPORT_START, GuestInfoVMSupport, &regData, NULL, NULL, 0 }
      };
      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_CAPABILITIES, GuestInfoServerSendCaps, NULL },
         { TOOLS_CORE_SIG_CONF_RELOAD, GuestInfoServerConfReload, NULL },
#if !defined(USERWORLD)
         { TOOLS_CORE_SIG_IO_FREEZE, GuestInfoServerIOFreeze, NULL },
#endif
         { TOOLS_CORE_SIG_RESET, GuestInfoServerReset, NULL },
         { TOOLS_CORE_SIG_SET_OPTION, GuestInfoServerSetOption, NULL },
         { TOOLS_CORE_SIG_SHUTDOWN, GuestInfoServerShutdown, NULL }
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) },
         { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
      };

#ifdef _WIN32
      if (NetUtil_LoadIpHlpApiDll() != ERROR_SUCCESS) {
         g_warning("Failed to load iphlpapi.dll.  Cannot report networking details.\n");
         return NULL;
      }

      ToolsCore_InitializeCOM(ctx);
#endif

      regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));

      memset(&gInfoCache, 0, sizeof gInfoCache);
      gVMResumed = FALSE;
      gInfoCache.method = NIC_INFO_V3_WITH_INFO_IPADDRESS_V3;

      /*
       * Set up the GuestInfo gather loops.
       */
      TweakGatherLoops(ctx, TRUE);

      return &regData;
   }

   return NULL;
}


/*
 * END Tools Core Services goodies.
 ******************************************************************************
 */
   07070100000368000081A4000000000000000000000001682255050000CCFB000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestInfo/perfMonLinux.c  /*********************************************************
 * Copyright (c) 2008-2019,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * Get GNU locale_t.
 * Alternatively, use _XOPEN_SOURCE >= 700 to get Posix locale_t.
 */
#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <locale.h>
#include <string.h>

#include "vm_basic_defs.h"
#include "vmware.h"
#include "str.h"
#include "strutil.h"
#include "debug.h"
#include "guestInfoInt.h"
#include "guestStats.h"
#include "posix.h"
#include "hashTable.h"
#include "conf.h"

#define GUEST_INFO_PREALLOC_SIZE 4096
#define INT_AS_HASHKEY(x) ((const void *)(uintptr_t)(x))
#define KEY_FORMAT  "%s|%s"

#define STAT_FILE        "/proc/stat"
#define VMSTAT_FILE      "/proc/vmstat"
#define UPTIME_FILE      "/proc/uptime"
#define MEMINFO_FILE     "/proc/meminfo"
#define ZONEINFO_FILE    "/proc/zoneinfo"
#define SWAPPINESS_FILE  "/proc/sys/vm/swappiness"
#define DISKSTATS_FILE   "/proc/diskstats"

#define SYSFS_BLOCK_FOLDER  "/sys/block"

/*
 * For now, all data collection is of uint64 values. Rates are always returned
 * as a double, derived from the uint64 data.
 */

static Bool gReleased = TRUE;
static Bool gInternal = FALSE;
#if PUBLISH_EXPERIMENTAL_STATS
static Bool gExperimental = PUBLISH_EXPERIMENTAL_STATS;
#endif
#if ADD_NEW_STATS
static Bool gUnstable = FALSE;
#endif

#define DECLARE_STAT(publish, file, isRegExp, locatorString, reportID, units, dataType) \
   { file, publish, isRegExp, locatorString, reportID, units, dataType }

typedef struct {
   const char         *sourceFile;
   const Bool         *publish;
   Bool                isRegExp;
   const char         *locatorString;
   GuestStatToolsID    reportID;
   GuestValueUnits     units;
   GuestValueType      dataType;
} GuestInfoQuery;

GuestInfoQuery guestInfoQuerySpecTable[] = {
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "Hugepagesize",    GuestStatID_HugePageSize,              GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  ZONEINFO_FILE,   TRUE,  "present",         GuestStatID_MemPhysUsable,             GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "MemFree",         GuestStatID_MemFree,                   GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "Active(file)",    GuestStatID_MemActiveFileCache,        GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "SwapFree",        GuestStatID_SwapSpaceRemaining,        GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "HugePages_Total", GuestStatID_Linux_HugePagesTotal,      GuestUnitsHugePages,       GuestTypeUint64),
   DECLARE_STAT(&gReleased,  VMSTAT_FILE,     FALSE, "pgpgin",          GuestStatID_PageInRate,                GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gReleased,  VMSTAT_FILE,     FALSE, "pgpgout",         GuestStatID_PageOutRate,               GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gReleased,  STAT_FILE,       FALSE, "ctxt",            GuestStatID_ContextSwapRate,           GuestUnitsNumberPerSecond, GuestTypeDouble),
   DECLARE_STAT(&gReleased,  NULL,            FALSE, NULL,              GuestStatID_PhysicalPageSize,          GuestUnitsBytes,           GuestTypeUint64),
   DECLARE_STAT(&gReleased,  NULL,            FALSE, NULL,              GuestStatID_MemNeeded,                 GuestUnitsKiB,             GuestTypeUint64),

   DECLARE_STAT(&gReleased,  NULL,            FALSE, NULL,              GuestStatID_MemNeededReservation,      GuestUnitsKiB,             GuestTypeUint64),

   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "MemAvailable",    GuestStatID_Linux_MemAvailable,        GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "SReclaimable",    GuestStatID_Linux_MemSlabReclaim,      GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "Buffers",         GuestStatID_Linux_MemBuffers,          GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "Cached",          GuestStatID_Linux_MemCached,           GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gReleased,  MEMINFO_FILE,    FALSE, "MemTotal",        GuestStatID_Linux_MemTotal,            GuestUnitsKiB,             GuestTypeUint64),

   DECLARE_STAT(&gInternal,  MEMINFO_FILE,    FALSE, "Inactive(file)",  GuestStatID_Linux_MemInactiveFile,     GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gInternal,  ZONEINFO_FILE,   TRUE,  "low",             GuestStatID_Linux_LowWaterMark,        GuestUnitsPages,           GuestTypeUint64),

#if PUBLISH_EXPERIMENTAL_STATS
   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "SwapTotal",       GuestStatID_SwapFilesCurrent,          GuestUnitsKiB,             GuestTypeUint64),
   /* GuestStatID_SwapSpaceUsed depends on GuestStatID_SwapFilesCurrent */
   DECLARE_STAT(&gExperimental,  NULL,            FALSE, NULL,              GuestStatID_SwapSpaceUsed,             GuestUnitsKiB,             GuestTypeUint64),
   /* GuestStatID_SwapFilesMax depends on GuestStatID_SwapFilesCurrent */
   DECLARE_STAT(&gExperimental,  NULL,            FALSE, NULL,              GuestStatID_SwapFilesMax,              GuestUnitsKiB,             GuestTypeUint64),

   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Active(anon)",    GuestStatID_Linux_MemActiveAnon,       GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Inactive(anon)",  GuestStatID_Linux_MemInactiveAnon,     GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Inactive",        GuestStatID_Linux_MemInactive,         GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Active",          GuestStatID_Linux_MemActive,           GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Unevictable",     GuestStatID_Linux_MemPinned,           GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Dirty",           GuestStatID_Linux_MemDirty,            GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pswpin",          GuestStatID_PageSwapInRate,            GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pswpout",         GuestStatID_PageSwapOutRate,           GuestUnitsPagesPerSecond,  GuestTypeDouble),
   /* Not implemented
   DECLARE_STAT(&gExperimental,  NULL,            FALSE, NULL,              GuestStatID_ThreadCreationRate,        GuestUnitsNumberPerSecond, GuestTypeDouble),
    */
   DECLARE_STAT(&gExperimental,  SWAPPINESS_FILE, FALSE, NULL,              GuestStatID_Linux_Swappiness,          GuestUnitsPercent,         GuestTypeUint64),

   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "SwapCached",      GuestStatID_Linux_MemSwapCached,       GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "Committed_AS",    GuestStatID_Linux_MemCommitted,        GuestUnitsKiB,             GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  MEMINFO_FILE,    FALSE, "HugePages_Free",  GuestStatID_Linux_HugePagesFree,       GuestUnitsHugePages,       GuestTypeUint64),
   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pgfault",         GuestStatID_Linux_PageFaultRate,       GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pgmajfault",      GuestStatID_Linux_PageMajorFaultRate,  GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     FALSE, "pgfree",          GuestStatID_Linux_PageFreeRate,        GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     TRUE,  "pgsteal_",        GuestStatID_Linux_PageStealRate,       GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     TRUE,  "pgscan_kswapd_",  GuestStatID_Linux_PageSwapScanRate,    GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gExperimental,  VMSTAT_FILE,     TRUE,  "pgscan_direct_",  GuestStatID_Linux_PageDirectScanRate,  GuestUnitsPagesPerSecond,  GuestTypeDouble),
   DECLARE_STAT(&gExperimental,  STAT_FILE,       FALSE, "processes",       GuestStatID_ProcessCreationRate,       GuestUnitsNumberPerSecond, GuestTypeDouble),
#endif

   DECLARE_STAT(&gReleased,  STAT_FILE,       FALSE, "procs_running",   GuestStatID_Linux_CpuRunQueue,         GuestUnitsNumber,          GuestTypeUint64),
   DECLARE_STAT(&gReleased,  NULL,            FALSE, NULL,              GuestStatID_Linux_DiskRequestQueue,    GuestUnitsNumber,          GuestTypeUint64),
   DECLARE_STAT(&gReleased,  NULL,            FALSE, NULL,              GuestStatID_Linux_DiskRequestQueueAvg, GuestUnitsNumber,          GuestTypeDouble),
};

#define N_QUERIES (sizeof guestInfoQuerySpecTable / sizeof(GuestInfoQuery))

typedef struct {
   int              err;    // An errno value
   uint32           count;  // Number of instances found
   uint64           value;
   GuestInfoQuery  *query;
} GuestInfoStat;

typedef struct {
   HashTable       *exactMatches;

   uint32           numRegExps;
   GuestInfoStat  **regExps;

   uint32           numStats;
   GuestInfoStat   *stats;

   HashTable       *reportMap;

   Bool             timeData;
   double           timeStamp;
} GuestInfoCollector;

static GuestInfoCollector *gCurrentCollector = NULL;
static GuestInfoCollector *gPreviousCollector = NULL;

static void
GuestInfoDeriveMemNeeded(GuestInfoCollector *collector);

typedef struct GuestInfoDiskStatsList {
   struct GuestInfoDiskStatsList *next;
   char                          *diskName;
   unsigned int                   weightedTime[2];  // In milliseconds
} GuestInfoDiskStatsList;

static GuestInfoDiskStatsList *gDiskStatsList = NULL;


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoDeleteDiskStatsList --
 *
 *      Delete disk device stats list.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoDeleteDiskStatsList(GuestInfoDiskStatsList *head)  // IN/OUT:
{
   GuestInfoDiskStatsList *curr = head;

   while (curr != NULL) {
      GuestInfoDiskStatsList *next = curr->next;
      free(curr->diskName);
      free(curr);
      curr = next;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoIsBlockDevice --
 *
 *      Verify block device name.
 *
 * Results:
 *      TRUE   name is block device name
 *      FALSE  otherwise
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
GuestInfoIsBlockDevice(const char *name)  // IN:
{
   char path[PATH_MAX]; // PATH_MAX is defined to 4096

   Str_Sprintf(path, sizeof path, "%s/%s", SYSFS_BLOCK_FOLDER, name);

   return (access(path, F_OK) == 0);
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoGetUpTime --
 *
 *      What time is it?
 *
 * Results:
 *      TRUE   Success! *now is populated
 *      FALSE  Failure! *now remains unchanged
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
GuestInfoGetUpTime(double *now)  // OUT:
{
   char line[512];
   Bool result = FALSE;
   FILE *fp = Posix_Fopen(UPTIME_FILE, "r");

   if (fp == NULL) {
      g_warning("%s: Failed to open %s, error=%d.\n",
                __FUNCTION__, UPTIME_FILE, errno);
      return result;
   }

   if (fgets(line, sizeof line, fp) == line) {
      int assignedCount;
      double idle;

      assignedCount = sscanf(line, "%lf %lf", now, &idle);
      if (assignedCount == 2) {
         result = TRUE;
      } else {
         g_warning("%s: sscanf \"%s\" failed, return=%d, error=%d.\n",
                   __FUNCTION__, line, assignedCount, errno);
      }
   }

   fclose(fp);

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoStoreStat --
 *
 *      Store a stat.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Handles overflow detection.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoStoreStat(GuestInfoStat *stat,   // IN/OUT: stat
                   uint64 value)          // IN: value to be added to stat
{
   ASSERT(stat);

   // TODO: consider supporting regexp here.
   switch (stat->err) {
   case 0:
      ASSERT(stat->count != 0);

      if (((stat->count + 1) < stat->count) ||
          ((stat->value + value) < stat->value)) {
         stat->err = EOVERFLOW;
      } else {
         stat->count++;
         stat->value += value;
      }
      break;

   case ENOENT:
      ASSERT(stat->count == 0);

      stat->err = 0;
      stat->count = 1;
      stat->value = value;
      break;

   default:  // Some sort of error - sorry, thank you for playing...
      break;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoStoreStatByID --
 *
 *      Store a stat value by its ID.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoStoreStatByID(GuestStatToolsID reportID,      // IN:
                       GuestInfoCollector *collector,  // IN/OUT:
                       uint64 value)                   // IN:
{
   GuestInfoStat *stat = NULL;

   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(reportID),
                    (void **) &stat);

   /*
    * Caller must not pass in a reportID that does not exist in the table.
    */
   ASSERT(stat != NULL);

   /* coverity[var_deref_model] */
   GuestInfoStoreStat(stat, value);
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoCollectStat --
 *
 *      Collect a stat.
 *
 *      NOTE: Exact match data cannot be used in a regExp. This is a
 *            performance choice.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoCollectStat(const char *pathName,           // IN:
                     GuestInfoCollector *collector,  // IN/OUT:
                     const char *fieldName,          // IN:
                     uint64 value)                   // IN:
{
   GuestInfoStat *stat = NULL;
   char *key = Str_SafeAsprintf(NULL, KEY_FORMAT, pathName, fieldName);

   if (!HashTable_Lookup(collector->exactMatches, key, (void **) &stat)) {
      uint32 i;

      for (i = 0; i < collector->numRegExps; i++) {
         GuestInfoStat *thisOne = collector->regExps[i];

         if (strcmp(pathName, thisOne->query->sourceFile) == 0 &&
             StrUtil_StartsWith(fieldName, thisOne->query->locatorString)) {
            stat = thisOne;
            break;
         }
      }
   }

   free(key);

   if (stat != NULL) {
      GuestInfoStoreStat(stat, value);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoProcData --
 *
 *      Reads a "stat file" and contributes to the collection.
 *
 *      NOTE: If caller specifies a fieldSeparator, it has to be present
 *            in the fieldName being parsed. '\0' represents an unspecified
 *            fieldSeparator.
 *
 * Results:
 *      TRUE   Success!
 *      FALSE  Failure!
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
GuestInfoProcData(const char *pathName,           // IN: path name
                  char fieldSeparator,            // IN/OPT:
                  GuestInfoCollector *collector)  // IN/OUT:
{
   char line[4096];  // Close to length of /proc/stat intr line
   FILE *fp = Posix_Fopen(pathName, "r");

   if (fp == NULL) {
      g_warning("%s: Failed to open %s, error=%d.\n",
                __FUNCTION__, pathName, errno);
      return FALSE;
   }

   /*
    * If a line inside the file is longer than what the buffer can hold,
    * fgets still succeeds, the next fgets call continues at the previously
    * stopped location in the line.
    */
   while (fgets(line, sizeof line, fp) == line) {
      uint64 value = 0;
      char *savedPtr = NULL;
      char *fieldName = strtok_r(line, " \t", &savedPtr);
      char *fieldData = strtok_r(NULL, " \t", &savedPtr);

      if (fieldName == NULL) {
         continue;
      }

      if (fieldSeparator != '\0') {
         char *p = strrchr(fieldName, fieldSeparator);
         if (p == NULL) {
            /*
             * When fieldSeparator is specified, fieldName is expected
             * to have it.
             */
            continue;
         } else {
            *p = '\0';
         }
      }

      if ((fieldData == NULL) ||
          (sscanf(fieldData, "%"FMT64"u", &value) != 1)) {
         continue;
      }

      GuestInfoCollectStat(pathName, collector, fieldName, value);
   }

   fclose(fp);

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoProcSimpleValue --
 *
 *      Reads the stat /proc file, extracts a single, simple value and
 *      adds it to the collection.
 *
 * Results:
 *      TRUE   Success!
 *      FALSE  Failure!
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
#if PUBLISH_EXPERIMENTAL_STATS

static Bool
GuestInfoProcSimpleValue(GuestStatToolsID reportID,      // IN:
                         GuestInfoCollector *collector)  // IN/OUT:
{
   char line[512];
   uint64 value;
   FILE *fp;
   Bool success = FALSE;
   GuestInfoStat *stat = NULL;

   HashTable_Lookup(collector->reportMap, INT_AS_HASHKEY(reportID),
                    (void **) &stat);

   /*
    * Caller must not pass in a reportID that does not exist in the table.
    */
   ASSERT(stat != NULL);
   ASSERT(stat->query != NULL);
   ASSERT(stat->query->sourceFile);

   /* coverity[var_deref_op] */
   fp = Posix_Fopen(stat->query->sourceFile, "r");
   if (fp == NULL) {
      g_warning("%s: Failed to open %s, error=%d.\n",
                __FUNCTION__, stat->query->sourceFile, errno);

      return success;
   }

   if (fgets(line, sizeof line, fp) == line) {
      value = 0;
      if (sscanf(line, "%"FMT64"u", &value) == 1) {
         stat->err = 0;
         stat->count = 1;
         stat->value = value;

         success = TRUE;
      }
   }

   fclose(fp);

   return success;
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoDeriveSwapData --
 *
 *      Update the swap stats that are calculated rather than fetched.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
#if PUBLISH_EXPERIMENTAL_STATS

static void
GuestInfoDeriveSwapData(GuestInfoCollector *collector)  // IN/OUT:
{
   uint64 swapFree = 0;
   uint64 swapTotal = 0;
   uint64 swapUsed = 0;
   GuestInfoStat *swapSpaceRemaining = NULL;
   GuestInfoStat *swapSpaceUsed = NULL;
   GuestInfoStat *swapFilesCurrent = NULL;
   GuestInfoStat *swapFilesMax = NULL;

   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(GuestStatID_SwapFilesMax),
                    (void **) &swapFilesMax);

   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(GuestStatID_SwapFilesCurrent),
                    (void **) &swapFilesCurrent);

   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(GuestStatID_SwapSpaceUsed),
                    (void **) &swapSpaceUsed);

   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(GuestStatID_SwapSpaceRemaining),
                    (void **) &swapSpaceRemaining);

   ASSERT(swapFilesMax != NULL &&
          swapFilesCurrent != NULL &&
          swapSpaceUsed != NULL &&
          swapSpaceRemaining != NULL);  // Must be in the table

   /*
    * Start by getting SwapTotal (from Id_SwapFilesCurrent).
    * Set Id_SwapFilesMax to that if it doesn't have its own opinion.
    */
   /* coverity[var_deref_op] */
   if (swapFilesCurrent->err == 0) {
      swapTotal = swapFilesCurrent->value;

      /* coverity[var_deref_op] */
      if (swapFilesMax->err != 0) {
         swapFilesMax->value = swapTotal;
         swapFilesMax->count = 1;
         swapFilesMax->err = 0;
      }

      /*
       * Get SwapFree (from Id_SwapSpaceRemaining)
       * Set Id_SwapSpaceUsed to SwapTotal-SwapFree if it doesn't have its
       * own opinion.
       */
      /* coverity[var_deref_op] */
      if (swapSpaceRemaining->err == 0) {
         swapFree = swapSpaceRemaining->value;

         ASSERT(swapTotal >= swapFree);
         swapUsed = (swapTotal >= swapFree) ? swapTotal - swapFree : 0;

         /* coverity[var_deref_op] */
         if (swapSpaceUsed->err != 0) {
            swapSpaceUsed->value = swapUsed;
            swapSpaceUsed->count = 1;
            swapSpaceUsed->err = 0;
         }
      }
   }
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoDecreaseCpuRunQueueByOne --
 *
 *      Exclude the collector thread, make the result be consistent with
 *      "sar -q".
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoDecreaseCpuRunQueueByOne(GuestInfoCollector *collector)  // IN/OUT:
{
   GuestInfoStat *stat = NULL;

   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(GuestStatID_Linux_CpuRunQueue),
                    (void **) &stat);

   ASSERT(stat != NULL);  // Must be in the table
   ASSERT(stat->err == 0);
   ASSERT(stat->count == 1);

   /* coverity[var_deref_op] */
   if (stat->value > 0) {
      stat->value--;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoProcDiskStatsData --
 *
 *      Reads /proc/diskstats, extract disk request queue stats and
 *      adds them to the collection.
 *
 * Results:
 *      TRUE   Success!
 *      FALSE  Failure!
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
GuestInfoProcDiskStatsData(GuestInfoCollector *collector)  // IN/OUT:
{
   static int curr = 0;

   int prev;
   GuestInfoDiskStatsList **listItem;
   uint64 inflightIOsSum;
   Bool setStats; // Only when no disk device change in between

   char line[512];
   FILE *fp = Posix_Fopen(DISKSTATS_FILE, "r");

   if (fp == NULL) {
      g_warning("%s: Failed to open %s, error=%d.\n",
                __FUNCTION__, DISKSTATS_FILE, errno);
      return FALSE;
   }

   prev = curr ^ 1;  // curr = 0 => prev = 1; curr = 1 => prev = 0
   listItem = &gDiskStatsList;
   inflightIOsSum = 0;
   setStats = (gDiskStatsList != NULL) ? TRUE : FALSE;

   while (fgets(line, sizeof line, fp) == line) {
      /*
       * Linux kernel diskstats_show format string:
       * "%4d %7d %s %lu %lu %lu %u %lu %lu %lu %u %u %u %u\n"
       *
       * Matching data types are selected to calculate accumulating fields'
       * increment correctly in case of overflow for both 32 and 64 bit
       * Linux systems.
       */
      int assignedCount;
      char diskName[NAME_MAX + 1]; // NAME_MAX is defined to 255
      long unsigned int readIOs;   // # of reads completed
      long unsigned int writeIOs;  // # of writes completed
      unsigned int inflightIOs;    // # of I/Os currently in progress
      unsigned int weightedTime;   // Weighted # of milliseconds
                                   // spent in doing I/Os
      assignedCount = sscanf(line,
                             "%*d %*d %" XSTR(NAME_MAX) "s "
                             "%lu %*u %*u %*u "
                             "%lu %*u %*u %*u "
                             "%u %*u %u",
                             diskName, // '\0' is added automatically
                             &readIOs,
                             &writeIOs,
                             &inflightIOs, &weightedTime);
      if (assignedCount != 5 ||
          (readIOs == 0 && writeIOs == 0) ||
          !GuestInfoIsBlockDevice(diskName)) {
         continue;
      }

      inflightIOsSum += inflightIOs;

      if (*listItem != NULL) {
         if (strcmp((*listItem)->diskName, diskName) == 0) {
            (*listItem)->weightedTime[curr] = weightedTime;
         } else {
            /* Disk hot plug/unplug, rebuild the rest of the list. */
            GuestInfoDeleteDiskStatsList(*listItem);
            *listItem = NULL;
         }
      }

      if (*listItem == NULL) {
         *listItem = (GuestInfoDiskStatsList *)
                         Util_SafeMalloc(sizeof **listItem);
         (*listItem)->next = NULL;
         (*listItem)->diskName = Util_SafeStrdup(diskName);
         (*listItem)->weightedTime[curr] = weightedTime;
         (*listItem)->weightedTime[prev] = 0;

         /* Also covers disk hot plug at the end of the list. */
         setStats = FALSE;
      }

      listItem = &((*listItem)->next);
   }

   fclose(fp);

   if (listItem == &gDiskStatsList // No qualified disk device found
       || *listItem != NULL) {     // Disk hot unplug at the end of the list
      GuestInfoDeleteDiskStatsList(*listItem);
      *listItem = NULL;
      setStats = FALSE;
   }

   if (setStats) {
      GuestInfoDiskStatsList *currDiskStats = gDiskStatsList;
      uint64 weightedTimeDeltaSum = 0;

      while (currDiskStats != NULL) {
         unsigned int weightedTimeDelta = currDiskStats->weightedTime[curr] -
                                          currDiskStats->weightedTime[prev];
         weightedTimeDeltaSum += weightedTimeDelta;
         currDiskStats = currDiskStats->next;
      }

      GuestInfoStoreStatByID(GuestStatID_Linux_DiskRequestQueue,
                             collector,
                             inflightIOsSum);
      GuestInfoStoreStatByID(GuestStatID_Linux_DiskRequestQueueAvg,
                             collector,
                             weightedTimeDeltaSum);
   }

   curr = prev;

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoCollect --
 *
 *      Fill the specified collector with as much sampled data as possible.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoCollect(GuestInfoCollector *collector)  // IN/OUT:
{
   uint32 i;
   GuestInfoStat *stat;
   uint64 pageSize = sysconf(_SC_PAGESIZE);

   /* Reset all values */
   for (i = 0; i < collector->numStats; i++) {
      stat = &collector->stats[i];

      stat->err = ENOENT;  // There is no data here
      stat->count = 0;
      stat->value = 0;
   }

   /* Collect new values */
   GuestInfoProcData(MEMINFO_FILE, ':', collector);
   GuestInfoProcData(VMSTAT_FILE, '\0', collector);
   GuestInfoProcData(STAT_FILE, '\0', collector);
   GuestInfoProcData(ZONEINFO_FILE, '\0', collector);
#if PUBLISH_EXPERIMENTAL_STATS
   GuestInfoProcSimpleValue(GuestStatID_Linux_Swappiness, collector);
   GuestInfoDeriveSwapData(collector);
#endif

   collector->timeData = GuestInfoGetUpTime(&collector->timeStamp);

   /*
    * We make sure physical page size is always present.
    */
   GuestInfoStoreStatByID(GuestStatID_PhysicalPageSize,
                          collector,
                          pageSize);

   /*
    * Attempt to fix up memPhysUsable if it is not available.
    */

   stat = NULL;
   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(GuestStatID_MemPhysUsable),
                    (void **) &stat);

   ASSERT(stat != NULL);  // Must be in the table

   /* coverity[var_deref_op] */
   if (stat->err == 0) {
      stat->value *= (pageSize / 1024); // Convert pages to KiB
   } else {
      GuestInfoStat *memTotal = NULL;

      HashTable_Lookup(collector->reportMap,
                       INT_AS_HASHKEY(GuestStatID_Linux_MemTotal),
                       (void **) &memTotal);

      ASSERT(memTotal != NULL);  // Must be in the table

      /* coverity[var_deref_op] */
      if (memTotal->err == 0) {
         stat->err = 0;
         stat->count = 1;
         stat->value = memTotal->value;
      }
   }

   GuestInfoDeriveMemNeeded(collector);
   GuestInfoDecreaseCpuRunQueueByOne(collector);
   GuestInfoProcDiskStatsData(collector);
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoLegacy --
 *
 *      Fill in the legacy portion of the data to be returned.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoLegacy(GuestInfoCollector *current,  // IN: current collection
                GuestMemInfoLegacy *legacy)   // OUT: data filled out
{
   GuestInfoStat *stat;

   memset(legacy, 0, sizeof *legacy);

   legacy->version = GUESTMEMINFO_V5;
   legacy->flags   = 0;

   stat = NULL;
   HashTable_Lookup(current->reportMap,
                    INT_AS_HASHKEY(GuestStatID_MemPhysUsable),
                    (void **) &stat);

   ASSERT(stat != NULL);  // Must be in the table

   /* coverity[var_deref_op] */
   if (stat->err == 0) {
      legacy->memTotal = stat->value;
      legacy->flags |= MEMINFO_MEMTOTAL;
   }

   stat = NULL;
   HashTable_Lookup(current->reportMap,
                    INT_AS_HASHKEY(GuestStatID_Linux_HugePagesTotal),
                    (void **) &stat);

   ASSERT(stat != NULL);  // Must be in the table

   /* coverity[var_deref_op] */
   if (stat->err == 0) {
      legacy->hugePagesTotal = stat->value;
      legacy->flags |= MEMINFO_HUGEPAGESTOTAL;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoBytesNeededUIntDatum --
 *
 * Results:
 *      Returns the number of bytes needed to encode a UInt.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static uint16
GuestInfoBytesNeededUIntDatum(uint64 value)  // IN:
{
   if (value == 0) {
      return 0;
   } else if (value <= MAX_UINT8) {
      return sizeof(uint8);
   } else if (value <= MAX_UINT16) {
      return sizeof(uint16);
   } else if (value <= MAX_UINT32) {
      return sizeof(uint32);
   } else {
      return sizeof(uint64);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoAppendStat --
 *
 *      Append information about the specified stat to the DynBuf of stat
 *      data.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Memory may be dynamically allocated (via DynBuf).
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoAppendStat(int errnoValue,                // IN:
                    Bool emitNameSpace,            // IN:
                    GuestStatToolsID reportID,     // IN:
                    GuestValueUnits units,         // IN:
                    GuestValueType valueType,      // IN:
                    void *value,                   // IN:
                    size_t valueSize,              // IN:
                    DynBuf *stats)                 // IN/OUT:
{
   const char *NameSpace = GUEST_TOOLS_NAMESPACE;
   uint64 value64;
   GuestStatHeader header;
   GuestDatumHeader datum;

   header.datumFlags = GUEST_DATUM_ID |
                       GUEST_DATUM_VALUE_TYPE_ENUM |
                       GUEST_DATUM_VALUE_UNIT_ENUM;
   if (emitNameSpace) {
      header.datumFlags |= GUEST_DATUM_NAMESPACE;
   }
   if (errnoValue == 0) {
      header.datumFlags |= GUEST_DATUM_VALUE;
   }
   DynBuf_Append(stats, &header, sizeof header);

   if (header.datumFlags & GUEST_DATUM_NAMESPACE) {
      size_t nameSpaceLen = strlen(NameSpace) + 1;
      datum.dataSize = nameSpaceLen;
      DynBuf_Append(stats, &datum, sizeof datum);
      DynBuf_Append(stats, NameSpace, nameSpaceLen);
   }

   if (header.datumFlags & GUEST_DATUM_ID) {
      value64 = reportID;
      datum.dataSize = GuestInfoBytesNeededUIntDatum(value64);
      DynBuf_Append(stats, &datum, sizeof datum);
      DynBuf_Append(stats, &value64, datum.dataSize);
   }

   if (header.datumFlags & GUEST_DATUM_VALUE_TYPE_ENUM) {
      value64 = valueType;
      datum.dataSize = GuestInfoBytesNeededUIntDatum(value64);
      DynBuf_Append(stats, &datum, sizeof datum);
      DynBuf_Append(stats, &value64, datum.dataSize);
   }

   if (header.datumFlags & GUEST_DATUM_VALUE_UNIT_ENUM) {
      value64 = units;
      datum.dataSize = GuestInfoBytesNeededUIntDatum(value64);
      DynBuf_Append(stats, &datum, sizeof datum);
      DynBuf_Append(stats, &value64, datum.dataSize);
   }

   if (header.datumFlags & GUEST_DATUM_VALUE) {
      datum.dataSize = valueSize;
      DynBuf_Append(stats, &datum, sizeof datum);
      DynBuf_Append(stats, value, valueSize);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoAppendRate --
 *
 *      Compute a rate and then append it to the stat buffer.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoAppendRate(Bool emitNameSpace,             // IN:
                    GuestStatToolsID reportID,      // IN: ID of the stat
                    GuestInfoCollector *current,    // IN: current collection
                    GuestInfoCollector *previous,   // IN: previous collection
                    DynBuf *statBuf)                // IN/OUT: stat data
{
   double valueDouble = 0.0;
   int errnoValue = ENOENT;
   GuestInfoStat *currentStat = NULL;
   GuestInfoStat *previousStat = NULL;

   HashTable_Lookup(current->reportMap,
                    INT_AS_HASHKEY(reportID),
                    (void **) &currentStat);

   HashTable_Lookup(previous->reportMap,
                    INT_AS_HASHKEY(reportID),
                    (void **) &previousStat);

   ASSERT(currentStat != NULL &&
          previousStat != NULL);  // Must be in the table

   /* coverity[var_deref_op] */
   if (current->timeData &&
       previous->timeData &&
       currentStat->err == 0 &&
       previousStat->err == 0) {
      double timeDelta = current->timeStamp - previous->timeStamp;
      double valueDelta;

      /*
       * DiskRequestQueueAvg GuestInfoStat::value is weighted number of
       * milliseconds delta in uint64 type, need to divide it by 1000 to
       * turn the number in seconds.
       *
       * Host side drops the fraction part of double data type. Therefore,
       * we preserve 2 decimal points by scaling up the value 100x.
       * The consumers of this stat need to divide it by 100 to retrieve
       * two digits after decimal point.
       *
       * (value / 1000) * 100 = value / 10
       */
      if (reportID == GuestStatID_Linux_DiskRequestQueueAvg) {
         valueDelta = ((double)(currentStat->value)) / 10;
      } else {
         /*
          * The /proc FS stat can be uint32 type in the kernel on both x86
          * and x64 Linux, it is parsed and stored as uint64 in tools, so we
          * also need to handle uint32 overflow here.
          */
         if (currentStat->value < previousStat->value &&
             previousStat->value <= MAX_UINT32) {
            valueDelta = (uint32)(currentStat->value) -
                         (uint32)(previousStat->value);
         } else {
            valueDelta = currentStat->value - previousStat->value;
         }
      }

      valueDouble = valueDelta / timeDelta;

      errnoValue = 0;
   }

   {
      float valueFloat;
      void *valuePointer;
      size_t valueSize;

      if (valueDouble == 0) {
         valuePointer = NULL;
         valueSize = 0;
      } else {
         valueFloat = (float)valueDouble;
         if ((double)valueFloat == valueDouble) {
            valuePointer = &valueFloat;
            valueSize = sizeof valueFloat;
         } else {
            valuePointer = &valueDouble;
            valueSize = sizeof valueDouble;
         }
      }

      GuestInfoAppendStat(errnoValue, emitNameSpace, reportID,
                          currentStat->query->units, GuestTypeDouble,
                          valuePointer, valueSize, statBuf);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoDeriveMemNeeded --
 *
 *      Update memory needed stats that are calculated
 *      rather than fetched.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoDeriveMemNeeded(GuestInfoCollector *collector)  // IN/OUT:
{
   uint64 memNeeded;
   uint64 memNeededReservation;
   uint64 memAvailable = 0;
   GuestInfoStat *memAvail = NULL;
   GuestInfoStat *memPhysUsable = NULL;

   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(GuestStatID_Linux_MemAvailable),
                    (void **) &memAvail);

   ASSERT(memAvail != NULL);  // Must be in the table

   /* coverity[var_deref_op] */
   if (memAvail->err == 0) {
      memAvailable = memAvail->value;
   } else {
      GuestInfoStat *memFree = NULL;
      GuestInfoStat *memCache = NULL;
      GuestInfoStat *memBuffers = NULL;
      GuestInfoStat *memActiveFile = NULL;
      GuestInfoStat *memSlabReclaim = NULL;
      GuestInfoStat *memInactiveFile = NULL;
      GuestInfoStat *memLowWaterMark = NULL;

      HashTable_Lookup(collector->reportMap,
                       INT_AS_HASHKEY(GuestStatID_MemFree),
                       (void **) &memFree);
      HashTable_Lookup(collector->reportMap,
                       INT_AS_HASHKEY(GuestStatID_Linux_MemCached),
                       (void **) &memCache);
      HashTable_Lookup(collector->reportMap,
                       INT_AS_HASHKEY(GuestStatID_Linux_MemBuffers),
                       (void **) &memBuffers);
      HashTable_Lookup(collector->reportMap,
                       INT_AS_HASHKEY(GuestStatID_MemActiveFileCache),
                       (void **) &memActiveFile);
      HashTable_Lookup(collector->reportMap,
                       INT_AS_HASHKEY(GuestStatID_Linux_MemSlabReclaim),
                       (void **) &memSlabReclaim);
      HashTable_Lookup(collector->reportMap,
                       INT_AS_HASHKEY(GuestStatID_Linux_MemInactiveFile),
                       (void **) &memInactiveFile);
      HashTable_Lookup(collector->reportMap,
                       INT_AS_HASHKEY(GuestStatID_Linux_LowWaterMark),
                       (void **) &memLowWaterMark);

      ASSERT(memFree != NULL &&
             memCache != NULL &&
             memBuffers != NULL &&
             memActiveFile != NULL &&
             memSlabReclaim != NULL &&
             memInactiveFile != NULL &&
             memLowWaterMark != NULL);  // Must be in the table

      /* coverity[var_deref_op] */
      if (memFree->err == 0 &&
          memCache->err == 0 &&
          memBuffers->err == 0 &&
          memLowWaterMark->err == 0) {
         uint64 pageCache;
         unsigned long kbPerPage = sysconf(_SC_PAGESIZE) / 1024UL;
         uint64 lowWaterMarkValue = memLowWaterMark->value * kbPerPage;

         memAvailable = memFree->value - lowWaterMarkValue;

         /* coverity[var_deref_op] */
         if ((memActiveFile->err == 0) && (memInactiveFile->err == 0)) {
            pageCache = memActiveFile->value + memInactiveFile->value;
         } else {
            /*
             * If the kernel is too old to expose Active/Inactive file,
             * this is the best approxmation for pageCache.
             */
            pageCache = memCache->value + memBuffers->value;
         }

         pageCache -= MIN(pageCache / 2, lowWaterMarkValue);
         memAvailable += pageCache;

         /* coverity[var_deref_op] */
         if (memSlabReclaim->err == 0) {
            memAvailable += memSlabReclaim->value -
                            MIN(memSlabReclaim->value / 2, lowWaterMarkValue);
         }

         if ((int64)memAvailable < 0) {
            memAvailable = 0;
         }

         GuestInfoStoreStatByID(GuestStatID_Linux_MemAvailable,
                                collector,
                                memAvailable);
      }
   }

   HashTable_Lookup(collector->reportMap,
                    INT_AS_HASHKEY(GuestStatID_MemPhysUsable),
                    (void **) &memPhysUsable);

   ASSERT(memPhysUsable != NULL);  // Must be in the table

   /* coverity[var_deref_op] */
   if (memPhysUsable->err == 0) {
      /*
       * Reserve 5% of physical RAM for surges.
       */
      memNeededReservation = memPhysUsable->value / 20;

      if (memAvailable > memNeededReservation) {
         memAvailable -= memNeededReservation;
      } else {
         memAvailable = 0;
      }

      /*
       * We got these values from one read of /proc/meminfo. Everything should
       * really be coherent.
       */
      ASSERT(memPhysUsable->value >= memAvailable);
      memNeeded = memPhysUsable->value - memAvailable;
   } else {
      memNeeded = 0;
      memNeededReservation = 0;
   }

   GuestInfoStoreStatByID(GuestStatID_MemNeeded,
                          collector,
                          memNeeded);

   GuestInfoStoreStatByID(GuestStatID_MemNeededReservation,
                          collector,
                          memNeededReservation);
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoEncodeStats --
 *
 *      Encode the guest stats.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoEncodeStats(GuestInfoCollector *current,   // IN: current collection
                     GuestInfoCollector *previous,  // IN: previous collection
                     DynBuf *statBuf)               // IN/OUT: stats data
{
   uint32 i;
   GuestMemInfoLegacy legacy;
   Bool emitNameSpace = TRUE;

   /* Provide legacy data for backwards compatibility */
   GuestInfoLegacy(current, &legacy);

   DynBuf_Append(statBuf, &legacy, sizeof legacy);

   /* Provide data in the new, extensible format. */
   for (i = 0; i < current->numStats; i++) {
      GuestInfoStat *stat = &current->stats[i];

      if (!*(stat->query->publish)) {
         continue;
      }

      if (stat->query->dataType == GuestTypeDouble) {
         GuestInfoAppendRate(emitNameSpace, stat->query->reportID,
                             current, previous, statBuf);
      } else {
         ASSERT(stat->query->dataType == GuestTypeUint64);
         ASSERT((stat->query->units & GuestUnitsModifier_Rate) == 0);
         GuestInfoAppendStat(stat->err,
                             emitNameSpace,
                             stat->query->reportID,
                             stat->query->units,
                             stat->query->dataType,
                             &stat->value,
                             GuestInfoBytesNeededUIntDatum(stat->value),
                             statBuf);
      }

      emitNameSpace = FALSE; // use the smallest representation
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoDestroyCollector --
 *
 *      Destroy the collector representation.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
GuestInfoDestroyCollector(GuestInfoCollector *collector)  // IN:
{
   if (collector != NULL) {
      HashTable_Free(collector->exactMatches);
      HashTable_Free(collector->reportMap);
      free(collector->regExps);
      free(collector->stats);
      free(collector);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoConstructCollector --
 *
 *      Construct a collector.
 *
 * Results:
 *      NULL Failure!
 *     !NULL Success! Collector representation.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static GuestInfoCollector *
GuestInfoConstructCollector(GuestInfoQuery *queries,  // IN:
                            uint32 numQueries)        // IN:
{
   uint32 i;
   uint32 regExp = 0;
   GuestInfoCollector *collector = Util_SafeCalloc(1, sizeof *collector);

   if (collector == NULL) {
      return NULL;
   }

   collector->reportMap = HashTable_Alloc(256, HASH_INT_KEY, NULL);

   collector->exactMatches = HashTable_Alloc(256,
                                           HASH_STRING_KEY | HASH_FLAG_COPYKEY,
                                             NULL);

   collector->numRegExps = 0;
   for (i = 0; i < numQueries; i++) {
      if (queries[i].isRegExp) {
         collector->numRegExps++;
      }
   }

   collector->numStats = numQueries;
   collector->stats = Util_SafeCalloc(numQueries, sizeof *collector->stats);
   collector->regExps = Util_SafeCalloc(collector->numRegExps,
                                        sizeof(GuestInfoStat *));

   if ((collector->exactMatches == NULL) ||
       (collector->reportMap == NULL) ||
       ((collector->numRegExps != 0) && (collector->regExps == NULL)) ||
       ((collector->numStats != 0) && (collector->stats == NULL))) {
      GuestInfoDestroyCollector(collector);
      return NULL;
   }

   regExp = 0;

   for (i = 0; i < numQueries; i++) {
      GuestInfoQuery *query = &queries[i];
      GuestInfoStat *stat = &collector->stats[i];

      ASSERT(query->reportID);

      stat->query = query;

      if (query->isRegExp) {
         ASSERT(query->sourceFile);
         ASSERT(query->locatorString);

         collector->regExps[regExp++] = stat;
      } else {
         if (query->sourceFile != NULL && query->locatorString != NULL) {
            char *key = Str_SafeAsprintf(NULL, KEY_FORMAT, query->sourceFile,
                                         query->locatorString);
            HashTable_Insert(collector->exactMatches, key, stat);
            free(key);
         }
      }

      /* The report lookup */
      HashTable_Insert(collector->reportMap, INT_AS_HASHKEY(query->reportID),
                       stat);
   }

   return collector;
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfoTakeSample --
 *
 *      Gather performance stats.
 *
 * Results:
 *      TRUE   Success! statBuf contains collected data
 *      FALSE  Failure! statBuf contains no collected data
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
GuestInfoTakeSample(DynBuf *statBuf)  // IN/OUT: inited, ready to fill
{
   GuestInfoCollector *temp;
   locale_t newLoc;
   locale_t prevLoc;

   ASSERT(statBuf && DynBuf_GetSize(statBuf) == 0);

   /* Preallocate space to minimize realloc operations. */
   if (!DynBuf_Enlarge(statBuf, GUEST_INFO_PREALLOC_SIZE)) {
      return FALSE;
   }

   /* First time through, allocate all necessary memory */
   if (gPreviousCollector == NULL) {
      gCurrentCollector = GuestInfoConstructCollector(guestInfoQuerySpecTable,
                                            N_QUERIES);

      gPreviousCollector = GuestInfoConstructCollector(guestInfoQuerySpecTable,
                                             N_QUERIES);
   }

   if ((gCurrentCollector == NULL) ||
       (gPreviousCollector == NULL)) {
      GuestInfoDestroyCollector(gCurrentCollector);
      gCurrentCollector = NULL;
      GuestInfoDestroyCollector(gPreviousCollector);
      gPreviousCollector = NULL;
      return FALSE;
   }

   /*
    * Switch the current thread to "C" locale to parse /proc files.
    *
    * vmtoolsd process calls setlocale(LC_ALL, "") to switch process locale
    * from default "C" locale to that set in environment variable, like
    * LC_ALL or LANG, at startup time.
    *
    * Linux /proc file floats are using "C" locale decimal separator ".".
    *
    * Case in point, on a system with es_ES locale, its LC_NUMERIC is using
    * decimal separator ",", when parsing /proc/uptime, sscanf("%lf %lf")
    * stops at the first "." and returns 1, instead of 2 with en_US locale
    * and "C" locale.
    *
    * Restore the current thread to its previous locale once done with the
    * stats collection and encoding.
    */
   newLoc = newlocale(LC_ALL_MASK, "C", (locale_t)0);
   if (newLoc != (locale_t)0) {
      prevLoc = uselocale(newLoc);
   } else {
      g_warning("%s: newlocale failed, error=%d.\n", __FUNCTION__, errno);
   }

   /* Collect the current data */
   GuestInfoCollect(gCurrentCollector);

   /* Encode the captured data */
   GuestInfoEncodeStats(gCurrentCollector, gPreviousCollector, statBuf);

   if (newLoc != (locale_t)0) {
      /* Restore thread previous locale */
      uselocale(prevLoc);
      freelocale(newLoc);
   }

   /* Switch the collections for next time. */
   temp = gCurrentCollector;
   gCurrentCollector = gPreviousCollector;
   gPreviousCollector = temp;

   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfo_StatProviderPoll --
 *
 *      Called when a new stat sample is requested. GuestInfo_ReportStats
 * should be called once the sample is available. If gathering is taking
 * longer than sampling frequency, the request may be ignored.
 *
 * @param[in]  data     The application context.
 *
 * @return TRUE to indicate that the timer should be rescheduled.
 *
 *----------------------------------------------------------------------
 */

gboolean
GuestInfo_StatProviderPoll(gpointer data)
{
   ToolsAppCtx *ctx = data;
   DynBuf stats;

   g_debug("%s: Entered guest info stats gather.\n", __FUNCTION__);

#if ADD_NEW_STATS
   gUnstable = g_key_file_get_boolean(ctx->config,
                                      CONFGROUPNAME_GUESTINFO,
                                      "enable-unstable-stats",
                                      NULL);
#endif

   /* Send the vmstats to the VMX. */
   DynBuf_Init(&stats);

   if (!GuestInfoTakeSample(&stats)) {
      g_warning("%s: Failed to get vmstats.\n", __FUNCTION__);
   } else if (!GuestInfo_ServerReportStats(ctx, &stats)) {
      g_warning("%s: Failed to send vmstats.\n", __FUNCTION__);
   }

   DynBuf_Destroy(&stats);
   return TRUE;
}


/*
 *----------------------------------------------------------------------
 *
 * GuestInfo_StatProviderShutdown --
 *
 *      Clean up the resource acquired by perfMonLinux.
 *      Nothing to do at the moment.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
GuestInfo_StatProviderShutdown(void)
{
   GuestInfoDeleteDiskStatsList(gDiskStatsList);
   gDiskStatsList = NULL;

   GuestInfoDestroyCollector(gCurrentCollector);
   gCurrentCollector = NULL;
   GuestInfoDestroyCollector(gPreviousCollector);
   gPreviousCollector = NULL;
}
 07070100000369000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestStore    0707010000036A000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestStore/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   0707010000036B000081A4000000000000000000000001682255050000051F000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestStore/Makefile.am    ################################################################################
### Copyright (c) 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libguestStore.la

libguestStore_la_CPPFLAGS =
libguestStore_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libguestStore_la_LDFLAGS =
libguestStore_la_LDFLAGS += @PLUGIN_LDFLAGS@

libguestStore_la_LIBADD =
libguestStore_la_LIBADD += @VMTOOLS_LIBS@
libguestStore_la_LIBADD += @GOBJECT_LIBS@

libguestStore_la_SOURCES =
libguestStore_la_SOURCES += guestStorePlugin.c
 0707010000036C000081A400000000000000000000000168225505000135CA000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/guestStore/guestStorePlugin.c /*********************************************************
 * Copyright (c) 2019-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file guestStorePlugin.c
 *
 * GuestStore plugin, allow client to download content from GuestStore.
 */


#define G_LOG_DOMAIN  "guestStore"

#include <glib-object.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#else
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "posix.h"
#include "file.h"
#endif

#include "vm_assert.h"
#include "vm_basic_types.h"
#include "vmware/tools/guestStore.h"
#include "vmware/tools/log.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
#include "dataMap.h"
#include "guestStoreConst.h"
#include "guestStoreDefs.h"
#include "rpcout.h"
#include "poll.h"
#ifdef OPEN_VM_TOOLS
#include "vmci_sockets.h"
#else
#include "vsockCommon.h"
#endif
#include "asyncsocket.h"
#include "str.h"
#include "util.h"
#include "guestApp.h"
#include "vmcheck.h"
#ifdef _WIN32
#include "guestStoreWin32.h"
#endif

VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);

#ifndef sockerr
#ifdef _WIN32
#define sockerr()  WSAGetLastError()
#else
#define sockerr()  errno
#endif
#endif  // sockerr

/*
 * User level send/recv buffers
 */

/*
 * Client connection send/recv buffer size
 */
#define CLIENT_CONN_SEND_RECV_BUF_SIZE  GUESTSTORE_REQUEST_BUFFER_SIZE

/*
 * VMX connection send/recv buffer size
 */
#define VMX_CONN_SEND_RECV_BUF_SIZE  GUESTSTORE_RESPONSE_BUFFER_SIZE

/*
 * Maximum concurrent client connections
 */
#define DEFAULT_MAX_CLIENT_CONNECTIONS  8

/*
 * Default timeout value in seconds for receiving from client connections
 */
#define DEFAULT_CLIENT_RECV_TIMEOUT  3  // seconds


/*
 * Client connection details
 */
typedef struct _ClientConnInfo {
   AsyncSocket *asock;

   char *buf;     // Send/recv buffer for HTTP request/response head
   int32 bufLen;  // Send/recv buffer length

   Bool shutDown;  // Close connection in send callback.

   Bool isCurrent;  // True for the current client connection
   char *requestPath;  // Requested GuestStore content path
   GSource *timeoutSource;  // Timeout source for receiving HTTP request
} ClientConnInfo;

/*
 * VMX connection details
 */
typedef struct _VmxConnInfo {
   AsyncSocket *asock;

   char *buf;     // Send/recv buffer for content transfer
   int32 bufLen;  // Send/recv buffer length

   Bool shutDown;  // Close connection in send callback.

   int32 dataMapLen;  // Recv buffer for VMX data map size
   int32 connTimeout;  // Connection inactivity timeout
   int64 bytesRemaining;  // Track remaining content size to transfer
   GSource *timeoutSource;  // Timeout source for connection inactivity
} VmxConnInfo;

typedef struct {
   AsyncSocket *vmxListenSock;     // For vsocket connections from VMX
   AsyncSocket *clientListenSock;  // For connections from clients

   GList *clientConnWaitList;  // Client connections in waiting list

   ClientConnInfo *clientConn;  // The current client connection being served
   VmxConnInfo    *vmxConn;     // The VMX connection providing service

   ToolsAppCtx *ctx;  // vmtoolsd application context

   Bool featureDisabled;  // Track tools.conf [guestStore]disabled change
   Bool adminOnly;  // Track tools.conf [guestStore]adminOnly change

   Bool guestStoreAccessEnabled;  // VMX GuestStore access enable status

   Bool vmxConnectRequested;  // VMX connect request sent status
   GSource *timeoutSource;  // Timeout source for VMX to guest connection
   Bool shutdown;  // vmtoolsd shutdown
} PluginData;

static PluginData pluginData = {0};

#define currentClientConn  pluginData.clientConn
#define theVmxConn         pluginData.vmxConn

#define ReceivedHttpRequestFromCurrentClientConn()  \
   (currentClientConn->requestPath != NULL)

/*
 * Macros to read values from config file
 */
#define GUESTSTORE_CONFIG_GET_BOOL(key, defVal)  \
   VMTools_ConfigGetBoolean(pluginData.ctx->config, "guestStore", key, defVal)

#define GUESTSTORE_CONFIG_GET_INT(key, defVal)  \
   VMTools_ConfigGetInteger(pluginData.ctx->config, "guestStore", key, defVal)


/*
 *-----------------------------------------------------------------------------
 *
 * GetCurrentUtcStr --
 *
 *      Get the current UTC time in a string format suitable for usage
 *      in HTTP response.
 *
 * Results:
 *      Return the current UTC time string, e.g.
 *
 *      Wed, 07 Nov 2018 20:50:11 GMT
 *
 *      The caller should call g_free() to free it.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static gchar *
GetCurrentUtcStr(void)
{
   gchar *res = NULL;
   GDateTime *utcTime = g_date_time_new_now_utc();

   if (NULL != utcTime) {
      res = g_date_time_format(utcTime, "%a, %d %b %Y %T GMT");
      g_date_time_unref(utcTime);
   }

   return res;
}


/*
 *-----------------------------------------------------------------------------
 *
 * IsFeatureDisabled --
 *
 *      Check if guest admin/root has deactivated GuestStore access.
 *
 * Results:
 *      Return the configured boolean value, default is FALSE.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline Bool
IsFeatureDisabled(void)
{
   return GUESTSTORE_CONFIG_GET_BOOL("disabled", FALSE);
}

#define CheckAndUpdateFeatureDisabled()  \
   (pluginData.featureDisabled = IsFeatureDisabled())


/*
 *-----------------------------------------------------------------------------
 *
 * IsAdminOnly --
 *
 *      Check if only guest admin/root has access to GuestStore.
 *
 * Results:
 *      Return the configured boolean value, default is FALSE.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline Bool
IsAdminOnly(void)
{
   return GUESTSTORE_CONFIG_GET_BOOL("adminOnly", FALSE);
}

#define CheckAndUpdateAdminOnly()  \
   (pluginData.adminOnly = IsAdminOnly())


static void
StartServeNextClientConn(void);

static void
CloseClientConn(ClientConnInfo *clientConn);  // IN

#define CloseCurrentClientConn()           \
   if (currentClientConn != NULL) {        \
      CloseClientConn(currentClientConn);  \
   }

#define CloseCurrentClientConnIfReceivedHttpRequest()  \
   if (currentClientConn != NULL &&                    \
       currentClientConn->requestPath != NULL) {       \
      CloseClientConn(currentClientConn);              \
   }

#define CloseClientConnsInWait()                                   \
   while (pluginData.clientConnWaitList != NULL) {                 \
      ClientConnInfo *clientConn =                                 \
         (ClientConnInfo *)(pluginData.clientConnWaitList->data);  \
      CloseClientConn(clientConn);                                 \
   }

static void
CloseVmxConn(void);

static void
CloseActiveConnections(void);

static void
HandleCurrentClientConnError(void);

static void
HandleVmxConnError(void);

static Bool
RecvHttpRequestFromCurrentClientConn(void *buf,  // OUT
                                     int len);   // IN

static Bool
StartRecvHttpRequestFromCurrentClientConn(void);

static inline void
StopRecvFromCurrentClientConn(void);

static Bool
SendToCurrentClientConn(void *buf,  // IN
                        int len);   // IN

static Bool
SendHttpResponseToCurrentClientConn(const char *headFmt,  // IN
                                    int64 contentLen,     // IN
                                    Bool shutdown);       // IN

#define SendHttpResponseOKToCurrentClientConn(contentSize)  \
   SendHttpResponseToCurrentClientConn(                     \
      HTTP_RES_OK,                                          \
      contentSize,                                          \
      (0 == contentSize ? TRUE : FALSE))

#define SendHttpResponseForbiddenToCurrentClientConn()   \
   SendHttpResponseToCurrentClientConn(                  \
      HTTP_RES_FORBIDDEN,                                \
      0,                                                 \
      TRUE)

#define SendHttpResponseNotFoundToCurrentClientConn()    \
   SendHttpResponseToCurrentClientConn(                  \
      HTTP_RES_NOT_FOUND,                                \
      0,                                                 \
      TRUE)

static Bool
SendConnectRequestToVmx(void);

static Bool
SendDataMapToVmxConn(void);

#define CheckSendShutdownDataMapToVmxConn()            \
   ASSERT(currentClientConn == NULL);                  \
   if (theVmxConn != NULL && !theVmxConn->shutDown) {  \
      SendDataMapToVmxConn();                          \
   }

#define CheckSendRequestDataMapToVmxConn()             \
   ASSERT(currentClientConn != NULL);                  \
   if (ReceivedHttpRequestFromCurrentClientConn() &&   \
       theVmxConn != NULL && !theVmxConn->shutDown) {  \
      SendDataMapToVmxConn();                          \
   }

static Bool
RecvDataMapFromVmxConn(void *buf,  // OUT
                       int len);   // IN

static inline void
StopRecvFromVmxConn(void);

static Bool
ProcessVmxDataMap(const DataMap *map);  // IN

static Bool
RecvContentFromVmxConn(void);

static void
StartCurrentClientConnRecvTimeout(void);

static inline void
StopClientConnRecvTimeout(ClientConnInfo *clientConn);  // IN

static inline void
StopCurrentClientConnRecvTimeout(void);

static Bool
CurrentClientConnRecvTimeoutCb(gpointer clientData);  // IN

static inline void
StartVmxToGuestConnTimeout(void);

static inline void
StopVmxToGuestConnTimeout(void);

static Bool
VmxToGuestConnTimeoutCb(gpointer clientData);  // IN

static inline void
StartConnInactivityTimeout(void);

static inline void
StopConnInactivityTimeout(void);

static Bool
ConnInactivityTimeoutCb(gpointer clientData);  // IN

static void
ClientConnErrorCb(int err,             // IN
                  AsyncSocket *asock,  // IN
                  void *clientData);   // IN

static void
CurrentClientConnSendCb(void *buf,           // IN
                        int len,             // IN
                        AsyncSocket *asock,  // IN
                        void *clientData);   // IN

static void
CurrentClientConnRecvHttpRequestCb(void *buf,           // IN
                                   int len,             // IN
                                   AsyncSocket *asock,  // IN
                                   void *clientData);   // IN

static void
ClientConnectCb(AsyncSocket *asock,  // IN
                void *clientData);   // IN

static void
VmxConnErrorCb(int err,             // IN
               AsyncSocket *asock,  // IN
               void *clientData);   // IN

static void
VmxConnSendDataMapCb(void *buf,           // IN
                     int len,             // IN
                     AsyncSocket *asock,  // IN
                     void *clientData);   // IN

static void
VmxConnRecvDataMapCb(void *buf,           // IN
                     int len,             // IN
                     AsyncSocket *asock,  // IN
                     void *clientData);   // IN

static void
VmxConnRecvContentCb(void *buf,           // IN
                     int len,             // IN
                     AsyncSocket *asock,  // IN
                     void *clientData);   // IN

static void
VmxConnectCb(AsyncSocket *asock,  // IN
             void *clientData);   // IN


/*
 *-----------------------------------------------------------------------------
 *
 * StartServeNextClientConn --
 *
 *      Remove the next client connection from the waiting list, make it
 *      the current client connection and start receiving HTTP request
 *      from it.
 *
 *      If the waiting list is empty, initiate shutdown VMX connection.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
StartServeNextClientConn(void)
{
   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(currentClientConn == NULL);

   if (pluginData.clientConnWaitList != NULL) {
      currentClientConn = (ClientConnInfo *)
         (pluginData.clientConnWaitList->data);
      pluginData.clientConnWaitList = g_list_remove(
         pluginData.clientConnWaitList, currentClientConn);
      currentClientConn->isCurrent = TRUE;

      StartRecvHttpRequestFromCurrentClientConn();
   } else {
      CheckSendShutdownDataMapToVmxConn();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CloseClientConn --
 *
 *      Close a client connection and remove its reference.
 *
 *      Note: AsyncSocket does not differentiate read/write errors yet and
 *      does not try to send any data to the other end on close, so pending
 *      send data is dropped when a connection is closed even the socket may
 *      be still good for write.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
CloseClientConn(ClientConnInfo *clientConn)  // IN
{
   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(clientConn != NULL);
   ASSERT(clientConn->asock != NULL);

   g_info("Closing client connection %d.\n",
          AsyncSocket_GetFd(clientConn->asock));

   AsyncSocket_Close(clientConn->asock);
   clientConn->asock = NULL;

   if (clientConn->buf != NULL) {
      free(clientConn->buf);
      clientConn->buf = NULL;
   }

   if (clientConn->requestPath != NULL) {
      free(clientConn->requestPath);
      clientConn->requestPath = NULL;
   }

   StopClientConnRecvTimeout(clientConn);

   if (clientConn->isCurrent) {
      ASSERT(currentClientConn == clientConn);
      /*
       * AsyncSocketSendFn (CurrentClientConnSendCb) can be invoked inside
       * AsyncSocket_Close().
       */
      currentClientConn = NULL;
   } else {
      /*
       * This client connection is in the waiting list.
       */
      pluginData.clientConnWaitList =
         g_list_remove(pluginData.clientConnWaitList, clientConn);
   }

   free(clientConn);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CloseVmxConn --
 *
 *      Close the VMX connection.
 *
 *      Note: AsyncSocket does not differentiate read/write errors yet and
 *      does not try to send any data to the other end on close, so pending
 *      send data is dropped when a connection is closed even the socket may
 *      be still good for write.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
CloseVmxConn(void)
{
   g_debug("Entering %s.\n", __FUNCTION__);

   if (theVmxConn == NULL) {
      return;
   }

   ASSERT(theVmxConn->asock != NULL);

   g_info("Closing VMX connection %d.\n",
          AsyncSocket_GetFd(theVmxConn->asock));

   /*
    * AsyncSocketSendFn (VmxConnSendDataMapCb) can be invoked inside
    * AsyncSocket_Close().
    */
   AsyncSocket_Close(theVmxConn->asock);
   theVmxConn->asock = NULL;

   if (theVmxConn->buf != NULL) {
      free(theVmxConn->buf);
      theVmxConn->buf = NULL;
   }

   StopConnInactivityTimeout();

   free(theVmxConn);
   theVmxConn = NULL;
   pluginData.vmxConnectRequested = FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CloseActiveConnections --
 *
 *      Close the current client connection and the VMX connection, force to
 *      restart from the next client connection in the waiting list if it
 *      exists.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
CloseActiveConnections(void)
{
   g_debug("Entering %s.\n", __FUNCTION__);

   CloseCurrentClientConn();

   if (theVmxConn != NULL && !theVmxConn->shutDown) {
      /*
       * After CloseCurrentClientConn(), send shutdown data map to VMX.
       */
      SendDataMapToVmxConn();
   } else {
      /*
       * Force to restart.
       */
      CloseVmxConn();
      StartServeNextClientConn();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HandleCurrentClientConnError --
 *
 *      Handle the current client connection error.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HandleCurrentClientConnError(void)
{
   Bool requestReceived;

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);

   requestReceived = ReceivedHttpRequestFromCurrentClientConn();

   CloseCurrentClientConn();

   if (requestReceived) {
      /*
       * The VMX connection that serves the current client connection after
       * it has received HTTP request has to be reset too.
       */
      CheckSendShutdownDataMapToVmxConn();
   } else {
      /*
       * HTTP request not received from the current client connection yet,
       * the VMX connection is still clean.
       */
      StartServeNextClientConn();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * HandleVmxConnError --
 *
 *      Handle the VMX connection error.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HandleVmxConnError(void)
{
   g_debug("Entering %s.\n", __FUNCTION__);

   CloseVmxConn();

   /*
    * The current client connection being served after received HTTP request
    * has to be reset too.
    */
   CloseCurrentClientConnIfReceivedHttpRequest();

   if (pluginData.guestStoreAccessEnabled &&
       currentClientConn == NULL) {
      StartServeNextClientConn();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RecvHttpRequestFromCurrentClientConn --
 *
 *      Receive HTTP request from the current client connection.
 *
 * Results:
 *      TURE on success, FALSE otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RecvHttpRequestFromCurrentClientConn(void *buf,  // OUT
                                     int len)    // IN
{
   int res;

   g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);

   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);

   res = AsyncSocket_RecvPartial(currentClientConn->asock, buf, len,
                                 CurrentClientConnRecvHttpRequestCb,
                                 currentClientConn);
   if (res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_RecvPartial failed "
                "on current client connection %d: %s\n",
                AsyncSocket_GetFd(currentClientConn->asock),
                AsyncSocket_Err2String(res));
      HandleCurrentClientConnError();
      return FALSE;
   }

   if (currentClientConn->timeoutSource == NULL) {
      StartCurrentClientConnRecvTimeout();
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StartRecvHttpRequestFromCurrentClientConn --
 *
 *      Start receiving HTTP request, with timeout, from
 *      the current client connection.
 *
 * Results:
 *      TURE on success, FALSE otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
StartRecvHttpRequestFromCurrentClientConn(void)
{
   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);
   ASSERT(currentClientConn->buf == NULL);

   currentClientConn->bufLen = CLIENT_CONN_SEND_RECV_BUF_SIZE;
   currentClientConn->buf = Util_SafeMalloc(currentClientConn->bufLen);

   return RecvHttpRequestFromCurrentClientConn(currentClientConn->buf,
                                               currentClientConn->bufLen);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StopRecvFromCurrentClientConn --
 *
 *      Stop receiving from the current client connection, safe to call in
 *      the same connection recv callback.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
StopRecvFromCurrentClientConn(void)
{
   int res = AsyncSocket_CancelRecvEx(currentClientConn->asock,
                                      NULL, NULL, NULL, TRUE);
   if (res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_CancelRecvEx failed "
                "on current client connection %d: %s\n",
                AsyncSocket_GetFd(currentClientConn->asock),
                AsyncSocket_Err2String(res));
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * SendToCurrentClientConn --
 *
 *      Send to the current client connection.
 *
 * Results:
 *      TRUE on success, FALSE otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
SendToCurrentClientConn(void *buf,  // IN
                        int len)    // IN
{
   int res;

   //g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);

   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);

   res = AsyncSocket_Send(currentClientConn->asock, buf, len,
                          CurrentClientConnSendCb, currentClientConn);
   if (res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_Send failed "
                "on current client connection %d: %s\n",
                AsyncSocket_GetFd(currentClientConn->asock),
                AsyncSocket_Err2String(res));
      HandleCurrentClientConnError();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SendHttpResponseToCurrentClientConn --
 *
 *      Send HTTP response head to the current client connection.
 *
 * Results:
 *      TRUE on success, FALSE otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
SendHttpResponseToCurrentClientConn(const char *headFmt,  // IN
                                    int64 contentLen,     // IN
                                    Bool shutdown)        // IN
{
   gchar *utcStr;
   int len;

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);

   utcStr = GetCurrentUtcStr();
   len = Str_Sprintf(currentClientConn->buf, currentClientConn->bufLen,
                     headFmt,
                     utcStr != NULL ? utcStr : "", contentLen);
   g_free(utcStr);

   currentClientConn->shutDown = shutdown;
   return SendToCurrentClientConn(currentClientConn->buf, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * SendConnectRequestToVmx --
 *
 *      Request VMX to connect to our VSOCK listening port via RPC command.
 *
 *      This function should be called when pluginData.vmxConnectRequested
 *      is FALSE.
 *
 * Results:
 *      TRUE on success, FALSE otherwise
 *
 * Side-effects:
 *      All outstanding client connections are closed if failed.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
SendConnectRequestToVmx(void)
{
   Bool retVal;
   int fd;
   struct sockaddr_vm addr;
#ifdef _WIN32
   int       addrLen = (int)sizeof(addr);
#else
   socklen_t addrLen = (socklen_t)sizeof(addr);
#endif
   char msg[32]; // Longest string: "guestStore.connect 4294967295" (29 chars)
   int msgLen;
   char *result;
   size_t resultLen;
   RpcChannelType rpcChannelType;

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(!pluginData.vmxConnectRequested);
   ASSERT(theVmxConn == NULL);
   ASSERT(pluginData.vmxListenSock != NULL);

   fd = AsyncSocket_GetFd(pluginData.vmxListenSock);

   /*
    * Get the listening port.
    */
#ifdef _WIN32
   /* coverity[negative_returns] */
   if (0 != getsockname((SOCKET)fd, (struct sockaddr *)&addr, &addrLen))
#else
   /* coverity[negative_returns] */
   if (0 != getsockname(fd, (struct sockaddr *)&addr, &addrLen))
#endif
   {
      g_warning("getsockname failed on VMX listening socket %d: sockerr=%d.\n",
                fd, sockerr());
      retVal = FALSE;
      goto exit;
   }

   msgLen = Str_Sprintf(msg, sizeof msg,
                        "guestStore.connect %u", addr.svm_port);
   result = NULL;
   rpcChannelType = RpcChannel_GetType(pluginData.ctx->rpc);
   g_debug("Current guest RPC channel type: %d.\n", rpcChannelType);

   /*
    * "guestStore.connect" is a privileged guest RPC that should
    * go through a privileged vSock RPC channel.
    */
   if (rpcChannelType == RPCCHANNEL_TYPE_PRIV_VSOCK) {
      retVal = RpcChannel_Send(pluginData.ctx->rpc, msg, msgLen,
                               &result, &resultLen);
   } else {
      /*
       * After the vmsvc RPC channel falls back to backdoor, it could not
       * send through privileged guest RPC any more.
       */
      retVal = RpcChannel_SendOneRawPriv(msg, msgLen,
                                         &result, &resultLen);
   }

   if (retVal) {
      g_info("Connect request sent to VMX (svm_port = %u).\n", addr.svm_port);
   } else {
      g_warning("Failed to send connect request to VMX (svm_port = %u): %s.\n",
                addr.svm_port,
                result != NULL ? result : "");
   }
   vm_free(result);

exit:
   if (!retVal) {
      CloseCurrentClientConn();
      CloseClientConnsInWait();
   } else {
      StartVmxToGuestConnTimeout();
   }

   pluginData.vmxConnectRequested = retVal;
   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SendDataMapToVmxConn --
 *
 *      Send a data map to the VMX connection.
 *
 *      After received request path from the current client connection, data
 *      map field GUESTSTORE_REQ_FLD_PATH with the request path is sent to
 *      the VMX connection. VMX will send back a response data map with error
 *      code.
 *
 *      When no more client to serve, initiate shutdown VMX connection by
 *      sending data map field GUESTSTORE_REQ_FLD_NONE to the VMX connection
 *      so that VMX side can close its vsocket.
 *
 * Results:
 *      TRUE on success, FALSE otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
SendDataMapToVmxConn(void)
{
   int fd;
   ErrorCode res;
   DataMap map;
   Bool mapCreated = FALSE;
   int cmdType;
   char *serBuf = NULL;
   uint32 serBufLen;
   int resSock;
   Bool retVal = FALSE;

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);

   fd = AsyncSocket_GetFd(theVmxConn->asock);

   res = DataMap_Create(&map);
   if (res != DMERR_SUCCESS) {
      g_warning("DataMap_Create failed for VMX connection %d: error=%d.\n",
                fd, res);
      goto exit;
   }

   mapCreated = TRUE;

   if (currentClientConn == NULL) {
      /*
       * No client to serve, inform VMX side to close its vsocket proactively,
       * rather than waiting for ASOCKERR_REMOTE_DISCONNECT (4) error callback
       * which may never happen.
       */
      ASSERT(!theVmxConn->shutDown);

      theVmxConn->shutDown = TRUE;
      StopRecvFromVmxConn();
      cmdType = GUESTSTORE_REQ_CMD_CLOSE;
   } else {
      char *str;

      ASSERT(ReceivedHttpRequestFromCurrentClientConn());

      str = Util_SafeStrdup(currentClientConn->requestPath);
      res = DataMap_SetString(&map, GUESTSTORE_REQ_FLD_PATH, str, -1, TRUE);
      if (res != DMERR_SUCCESS) {
         g_warning("DataMap_SetString (field path) failed "
                   "for VMX connection %d: error=%d.\n", fd, res);
         free(str);
         goto exit;
      }

      cmdType = GUESTSTORE_REQ_CMD_GET;
   }

   res = DataMap_SetInt64(&map, GUESTSTORE_REQ_FLD_CMD, cmdType, TRUE);
   if (res != DMERR_SUCCESS) {
      g_warning("DataMap_SetInt64 (field cmd) failed "
                "for VMX connection %d: error=%d.\n", fd, res);
      goto exit;
   }

   res = DataMap_Serialize(&map, &serBuf, &serBufLen);
   if (res != DMERR_SUCCESS) {
      g_warning("DataMap_Serialize failed "
                "for VMX connection %d: error=%d.\n", fd, res);
      goto exit;
   }

   if (serBufLen > theVmxConn->bufLen) {
      g_warning("Data map to VMX connection %d is too large: length=%d.\n",
                fd, serBufLen);
      goto exit;
   }

   memcpy(theVmxConn->buf, serBuf, serBufLen);
   resSock = AsyncSocket_Send(theVmxConn->asock,
                              theVmxConn->buf, serBufLen,
                              VmxConnSendDataMapCb, theVmxConn);
   if (resSock != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_Send failed on VMX connection %d: %s\n",
                fd, AsyncSocket_Err2String(resSock));
      goto exit;
   }

   retVal = TRUE;

exit:
   if (mapCreated) {
      free(serBuf);
      DataMap_Destroy(&map);
   }

   if (!retVal) {
      HandleVmxConnError();
   }

   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RecvDataMapFromVmxConn --
 *
 *      Start receiving data map from the VMX connection.
 *
 * Results:
 *      TURE on success, FALSE otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RecvDataMapFromVmxConn(void *buf,  // OUT
                       int len)    // IN
{
   int res;

   g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);

   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);

   res = AsyncSocket_Recv(theVmxConn->asock, buf, len,
                          VmxConnRecvDataMapCb, theVmxConn);
   if (res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_Recv failed on VMX connection %d: %s\n",
                AsyncSocket_GetFd(theVmxConn->asock),
                AsyncSocket_Err2String(res));
      HandleVmxConnError();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StopRecvFromVmxConn --
 *
 *      Stop receiving from the VMX connection, safe to call in the same
 *      connection recv callback.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
StopRecvFromVmxConn(void)
{
   int res = AsyncSocket_CancelRecvEx(theVmxConn->asock,
                                      NULL, NULL, NULL, TRUE);
   if (res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_CancelRecvEx failed on VMX connection %d: %s\n",
                AsyncSocket_GetFd(theVmxConn->asock),
                AsyncSocket_Err2String(res));
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * ProcessVmxDataMap --
 *
 *      Process the data map received from the VMX connection.
 *
 *      The data map should contain field GUESTSTORE_RES_FLD_ERROR_CODE. In
 *      success case, field GUESTSTORE_RES_FLD_CONTENT_SIZE should also exist
 *      with the content size and the data map is followed by content bytes.
 *
 * Results:
 *      TRUE on success, FALSE on error.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
ProcessVmxDataMap(const DataMap *map)  // IN
{
   int fd;
   ErrorCode res;
   int64 errorCode;

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);

   fd = AsyncSocket_GetFd(theVmxConn->asock);

   res = DataMap_GetInt64(map, GUESTSTORE_RES_FLD_ERROR_CODE, &errorCode);
   if (res != DMERR_SUCCESS) {
      g_warning("DataMap_GetInt64 (field error code) failed in data map "
                "from VMX connection %d: error=%d.\n", fd, res);
      goto error;
   }

   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);

   switch ((int32)errorCode) {
      case 0: // ERROR_SUCCESS
         {
            int64 contentSize;
            res = DataMap_GetInt64(map, GUESTSTORE_RES_FLD_CONTENT_SIZE,
                                   &contentSize);
            if (res != DMERR_SUCCESS) {
               g_warning("DataMap_GetInt64 (field content size) failed "
                         "in data map from VMX connection %d: error=%d.\n",
                         fd, res);
               goto error;
            }

            if (contentSize < 0) {
               g_warning("Invalid content size in data map "
                         "from VMX connection %d: contentSize=%" FMT64 "d.\n",
                         fd, contentSize);
               goto error;
            }

            theVmxConn->bytesRemaining = contentSize;
            return SendHttpResponseOKToCurrentClientConn(contentSize);
         }
      case EPERM:
         {
            return SendHttpResponseForbiddenToCurrentClientConn();
         }
      case ENOENT:
         {
            return SendHttpResponseNotFoundToCurrentClientConn();
         }
      default:
         g_warning("Unexpected error code value %" FMT64 "d in data map "
                   "from VMX connection %d.\n", errorCode, fd);
         break;
   }

error:
   HandleVmxConnError();
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RecvContentFromVmxConn --
 *
 *      Start receiving content bytes from the VMX connection.
 *
 * Results:
 *      TURE on success, FALSE otherwise.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RecvContentFromVmxConn(void)
{
   int res;

   //g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);

   res = AsyncSocket_RecvPartial(theVmxConn->asock,
                                 theVmxConn->buf,
                                 theVmxConn->bufLen,
                                 VmxConnRecvContentCb,
                                 theVmxConn);
   if (res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_RecvPartial failed on VMX connection %d: %s\n",
                AsyncSocket_GetFd(theVmxConn->asock),
                AsyncSocket_Err2String(res));
      HandleVmxConnError();
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StartCurrentClientConnRecvTimeout --
 *
 *      Start the current client connection recv timeout.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
StartCurrentClientConnRecvTimeout(void)
{
   int clientRecvTimeout;

   ASSERT(currentClientConn->timeoutSource == NULL);

   clientRecvTimeout = GUESTSTORE_CONFIG_GET_INT("clientRecvTimeout",
      DEFAULT_CLIENT_RECV_TIMEOUT);
   if (clientRecvTimeout <= 0 || clientRecvTimeout > (G_MAXINT / 1000)) {
      g_warning("Invalid clientRecvTimeout (%d); Using default (%d).\n",
                clientRecvTimeout, DEFAULT_CLIENT_RECV_TIMEOUT);
      clientRecvTimeout = DEFAULT_CLIENT_RECV_TIMEOUT;
   }

   currentClientConn->timeoutSource = g_timeout_source_new(
      clientRecvTimeout * 1000);
   VMTOOLSAPP_ATTACH_SOURCE(pluginData.ctx,
                            currentClientConn->timeoutSource,
                            CurrentClientConnRecvTimeoutCb,
                            currentClientConn, NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StopClientConnRecvTimeout --
 *
 *      Stop client connection recv timeout.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
StopClientConnRecvTimeout(ClientConnInfo *clientConn)  // IN
{
   ASSERT(clientConn != NULL);

   if (clientConn->timeoutSource != NULL) {
      g_source_destroy(clientConn->timeoutSource);
      g_source_unref(clientConn->timeoutSource);
      clientConn->timeoutSource = NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * StopCurrentClientConnRecvTimeout --
 *
 *      Stop the current client connection recv timeout.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
StopCurrentClientConnRecvTimeout(void)
{
   StopClientConnRecvTimeout(currentClientConn);
}


/*
 *-----------------------------------------------------------------------------
 *
 * CurrentClientConnRecvTimeoutCb --
 *
 *      Poll callback function for the current client connection recv timeout.
 *
 * Results:
 *      The current client connection is closed.
 *      The timeout source is removed from poll.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CurrentClientConnRecvTimeoutCb(gpointer clientData)  // IN
{
   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(currentClientConn == clientData);
   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);

   g_warning("The current client connection %d recv timed out.\n",
             AsyncSocket_GetFd(currentClientConn->asock));

   /*
    * Follow the pattern in ConnInactivityTimeoutCb()
    */
   StopCurrentClientConnRecvTimeout();

   HandleCurrentClientConnError();

   return G_SOURCE_REMOVE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StartVmxToGuestConnTimeout --
 *
 *      Start VMX to guest connection timeout.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
StartVmxToGuestConnTimeout(void)
{
   ASSERT(pluginData.timeoutSource == NULL);

   pluginData.timeoutSource = g_timeout_source_new(
      GUESTSTORE_VMX_TO_GUEST_CONN_TIMEOUT * 1000);
   VMTOOLSAPP_ATTACH_SOURCE(pluginData.ctx,
                            pluginData.timeoutSource,
                            VmxToGuestConnTimeoutCb,
                            NULL, NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StopVmxToGuestConnTimeout --
 *
 *      Stop VMX to guest connection timeout.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
StopVmxToGuestConnTimeout(void)
{
   if (pluginData.timeoutSource != NULL) {
      g_source_destroy(pluginData.timeoutSource);
      g_source_unref(pluginData.timeoutSource);
      pluginData.timeoutSource = NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VmxToGuestConnTimeoutCb --
 *
 *      Poll callback function for VMX to guest connection timeout.
 *
 * Results:
 *      All outstanding client connections are closed.
 *      The timeout source is removed from poll.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmxToGuestConnTimeoutCb(gpointer clientData)  // IN
{
   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(theVmxConn == NULL);

   g_warning("VMX to guest connection timed out.\n");

   StopVmxToGuestConnTimeout();

   CloseCurrentClientConn();
   CloseClientConnsInWait();

   pluginData.vmxConnectRequested = FALSE;

   return G_SOURCE_REMOVE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StartConnInactivityTimeout --
 *
 *      Start connection inactivity timeout.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
StartConnInactivityTimeout(void)
{
   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->timeoutSource == NULL);
   ASSERT(theVmxConn->connTimeout != 0);

   theVmxConn->timeoutSource = g_timeout_source_new(
      theVmxConn->connTimeout * 1000);
   VMTOOLSAPP_ATTACH_SOURCE(pluginData.ctx,
                            theVmxConn->timeoutSource,
                            ConnInactivityTimeoutCb,
                            theVmxConn, NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * StopConnInactivityTimeout --
 *
 *      Stop connection inactivity timeout.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static inline void
StopConnInactivityTimeout(void)
{
   ASSERT(theVmxConn != NULL);

   if (theVmxConn->timeoutSource != NULL) {
      g_source_destroy(theVmxConn->timeoutSource);
      g_source_unref(theVmxConn->timeoutSource);
      theVmxConn->timeoutSource = NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * ConnInactivityTimeoutCb --
 *
 *      Poll callback function for connection inactivity timeout.
 *
 * Results:
 *      The VMX connection and the current client connection are closed.
 *      The timeout source is removed from poll.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
ConnInactivityTimeoutCb(gpointer clientData)  // IN
{
   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(theVmxConn == clientData);
   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);

   g_warning("Connection inactivity timed out.\n");

   /*
    * Issue observed:
    * If g_source_destroy() is not called on the inactivity timeout source
    * and the next client connection in the waiting list becomes current
    * and starts its new recv timeout source, g_main_dispatch() does not
    * remove the inactivity timeout source after this callback returns
    * G_SOURCE_REMOVE (FALSE).
    *
    * Solution:
    * Call g_source_destroy() before the new timeout source starts.
    * After this callback returns G_SOURCE_REMOVE (FALSE), g_main_dispatch()
    * detects the inactivity timeout source destroyed and skips same action.
    */
   StopConnInactivityTimeout();

   CloseActiveConnections();

   return G_SOURCE_REMOVE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ClientConnErrorCb --
 *
 *      Client connection error handler for asyncsocket.
 *
 * Results:
 *      The connection is closed.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ClientConnErrorCb(int err,             // IN
                  AsyncSocket *asock,  // IN
                  void *clientData)    // IN
{
   ClientConnInfo *clientConn = (ClientConnInfo *)clientData;

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(clientConn->asock != NULL);
   g_info("Client connection %d error callback: %s\n",
          AsyncSocket_GetFd(clientConn->asock),
          AsyncSocket_Err2String(err));

   if (clientConn->isCurrent) {
      ASSERT(currentClientConn == clientConn);
      HandleCurrentClientConnError();
   } else {
      CloseClientConn(clientConn);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CurrentClientConnSendCb --
 *
 *      Callback function after sent to the current client connection.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
CurrentClientConnSendCb(void *buf,           // IN
                        int len,             // IN
                        AsyncSocket *asock,  // IN
                        void *clientData)    // IN
{
   //g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);

   ASSERT(currentClientConn == clientData);
   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);

   if (AsyncSocket_GetState(currentClientConn->asock) != 
       AsyncSocketConnected) {
      /*
       * This callback may be called after the connection is closed for
       * freeing the send buffer.
       */
      return;
   }

   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->timeoutSource != NULL);

   /*
    * Restart connection inactivity timeout.
    */
   StopConnInactivityTimeout();
   StartConnInactivityTimeout();

   if (currentClientConn->shutDown) {
      g_info("Finished with current client connection %d.\n",
             AsyncSocket_GetFd(currentClientConn->asock));

      CloseCurrentClientConn();
      StartServeNextClientConn();
   } else {
      ASSERT(theVmxConn->bytesRemaining > 0);

      RecvContentFromVmxConn();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * CurrentClientConnRecvHttpRequestCb --
 *
 *      Callback function after received from the current client connection.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
CurrentClientConnRecvHttpRequestCb(void *buf,           // IN
                                   int len,             // IN
                                   AsyncSocket *asock,  // IN
                                   void *clientData)    // IN
{
   int fd;
   int recvLen;
   char *next_token;
   char *requestMethod;
   char *requestPath;

   g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);

   ASSERT(currentClientConn == clientData);
   ASSERT(currentClientConn != NULL);
   ASSERT(currentClientConn->asock != NULL);

   fd = AsyncSocket_GetFd(currentClientConn->asock);

   recvLen = (int)((char *)buf - currentClientConn->buf) + len;
   if (recvLen >= currentClientConn->bufLen) {
      g_warning("Recv from current client connection %d "
                "reached buffer limit.\n", fd);
      goto error;
   }

   /*
    * Check for HTTP request end.
    */
   if (recvLen < HTTP_HEADER_END_LEN ||
       strncmp(currentClientConn->buf + recvLen - HTTP_HEADER_END_LEN,
               HTTP_HEADER_END, HTTP_HEADER_END_LEN) != 0) {
      RecvHttpRequestFromCurrentClientConn(currentClientConn->buf +
                                           recvLen,
                                           currentClientConn->bufLen -
                                           recvLen);
      return;
   }

   StopCurrentClientConnRecvTimeout();

   *(currentClientConn->buf + recvLen) = '\0';
   g_debug("HTTP request from current client connection %d:\n%s\n",
           fd, currentClientConn->buf);

   requestMethod = strtok_r(currentClientConn->buf, " ", &next_token);
   if (NULL == requestMethod ||
       strcmp(requestMethod, HTTP_REQ_METHOD_GET) != 0) {
      g_warning("Invalid HTTP request method.\n");
      goto error;
   }

   /*
    * Ignore HTTP query part.
    */
   requestPath = strtok_r(NULL, "? ", &next_token);
   if (NULL == requestPath) {
      g_warning("HTTP request path not found.\n");
      goto error;
   }

   currentClientConn->requestPath = g_uri_unescape_string(requestPath, NULL);
   if (NULL == currentClientConn->requestPath ||
       '/' != *currentClientConn->requestPath ||
       strlen(currentClientConn->requestPath) > GUESTSTORE_CONTENT_PATH_MAX) {
      g_warning("Invalid HTTP request path.\n");
      goto error;
   }

   g_info("HTTP request path from current client connection %d: \"%s\"",
          fd, currentClientConn->requestPath);

   StopRecvFromCurrentClientConn();

   if (!pluginData.vmxConnectRequested) {
      ASSERT(theVmxConn == NULL);
      SendConnectRequestToVmx();
   } else {
      CheckSendRequestDataMapToVmxConn();
   }

   return;

error:
   HandleCurrentClientConnError();
}


/*
 *-----------------------------------------------------------------------------
 *
 * ClientConnectCb --
 *
 *      Poll callback function for a new client connection.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
ClientConnectCb(AsyncSocket *asock,  // IN
                void *clientData)    // IN
{
   int fd = AsyncSocket_GetFd(asock);
   int maxConnections;
   ClientConnInfo *clientConn = NULL;
   int res;

   g_debug("Entering %s.\n", __FUNCTION__);
   g_info("Got new client connection %d.\n", fd);

   if (AsyncSocket_GetState(asock) != AsyncSocketConnected) {
      g_info("Client connection %d is not in connected state.\n", fd);
      goto error;
   }

   maxConnections = GUESTSTORE_CONFIG_GET_INT("maxConnections",
      DEFAULT_MAX_CLIENT_CONNECTIONS);
   if ((g_list_length(pluginData.clientConnWaitList) +
        ((currentClientConn != NULL) ? 1 : 0)) >= maxConnections) {
      g_info("Client connection %d has exceeded maximum limit "
             "of %d client connections.\n", fd, maxConnections);
      goto error;
   }

#ifdef _WIN32
   CheckAndUpdateAdminOnly();

   /* coverity[negative_returns] */
   if (pluginData.adminOnly && !IsAdminClient(fd)) {
      g_info("Decline non admin/root client connection %d.\n", fd);
      goto error;
   }
#endif

   if (!AsyncSocket_EstablishMinBufferSizes(asock,
           GUESTSTORE_RESPONSE_BUFFER_SIZE, // sendSz
           GUESTSTORE_REQUEST_BUFFER_SIZE)) { // recvSz
      g_warning("AsyncSocket_EstablishMinBufferSizes failed "
                "on client connection %d.\n", fd);
      goto error;
   }

   clientConn = (ClientConnInfo *)Util_SafeCalloc(1, sizeof *clientConn);
   clientConn->asock = asock;

   res = AsyncSocket_SetErrorFn(asock, ClientConnErrorCb, clientConn);
   if (res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_SetErrorFn failed on client connection %d: %s\n",
                fd, AsyncSocket_Err2String(res));
      goto error;
   }

   if (currentClientConn == NULL) {
      /*
       * Make the first client connection be the current client connection.
       */
      currentClientConn = clientConn;
      currentClientConn->isCurrent = TRUE;
      StartRecvHttpRequestFromCurrentClientConn();
   } else {
      pluginData.clientConnWaitList = g_list_append(
         pluginData.clientConnWaitList, clientConn);
   }

   return;

error:
   g_info("Closing client connection %d.\n", fd);
   AsyncSocket_Close(asock);
   free(clientConn);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VmxConnErrorCb --
 *
 *      The VMX connection error handler for asyncsocket.
 *
 * Results:
 *      The connection is closed.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
VmxConnErrorCb(int err,             // IN
               AsyncSocket *asock,  // IN
               void *clientData)    // IN
{
   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(theVmxConn == clientData);
   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);
   g_info("VMX connection %d error callback: %s\n",
          AsyncSocket_GetFd(theVmxConn->asock),
          AsyncSocket_Err2String(err));

   HandleVmxConnError();
}


/*
 *-----------------------------------------------------------------------------
 *
 * VmxConnSendDataMapCb --
 *
 *      Callback function after sent to the VMX connection.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
VmxConnSendDataMapCb(void *buf,           // IN
                     int len,             // IN
                     AsyncSocket *asock,  // IN
                     void *clientData)    // IN
{
   int fd;

   g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);

   ASSERT(theVmxConn == clientData);
   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);

   fd = AsyncSocket_GetFd(theVmxConn->asock);

   if (AsyncSocket_GetState(theVmxConn->asock) != AsyncSocketConnected) {
      /*
       * This callback may be called after the connection is closed for
       * freeing the send buffer.
       */
      return;
   }

   if (theVmxConn->shutDown) {
      g_info("Shut down VMX connection %d.\n", fd);
      CloseVmxConn();

      if (pluginData.guestStoreAccessEnabled) {
         if (currentClientConn == NULL) {
            StartServeNextClientConn();
         } else if (ReceivedHttpRequestFromCurrentClientConn()) {
            SendConnectRequestToVmx();
         }
      }
   } else {
      RecvDataMapFromVmxConn(&theVmxConn->dataMapLen,
                             (int)sizeof(theVmxConn->dataMapLen));
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VmxConnRecvDataMapCb --
 *
 *      Callback function after received data map from the VMX connection.
 *
 *      VMX responds with a data map, followed by content bytes if no error.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
VmxConnRecvDataMapCb(void *buf,           // IN
                     int len,             // IN
                     AsyncSocket *asock,  // IN
                     void *clientData)    // IN
{
   int fd;

   g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);

   ASSERT(theVmxConn == clientData);
   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);

   fd = AsyncSocket_GetFd(theVmxConn->asock);

   if (buf == &theVmxConn->dataMapLen) {
      int dataMapLen = ntohl(theVmxConn->dataMapLen);

      ASSERT(len == sizeof theVmxConn->dataMapLen);

      if (dataMapLen > (theVmxConn->bufLen - sizeof theVmxConn->dataMapLen)) {
         g_warning("Data map from VMX connection %d "
                   "is too large: length=%d.\n", fd, dataMapLen);
         goto error;
      }

      *((int32 *)(theVmxConn->buf)) = theVmxConn->dataMapLen;
      RecvDataMapFromVmxConn(theVmxConn->buf + sizeof theVmxConn->dataMapLen,
                             dataMapLen);
   } else {
      ErrorCode res;
      DataMap map;

      ASSERT(buf == (theVmxConn->buf + sizeof theVmxConn->dataMapLen));
      ASSERT(len == ntohl(theVmxConn->dataMapLen));

      res = DataMap_Deserialize(theVmxConn->buf,
                                len + (int)sizeof(theVmxConn->dataMapLen),
                                &map);
      if (res != DMERR_SUCCESS) {
         g_warning("DataMap_Deserialize failed for data map "
                   "from VMX connection %d: error=%d.\n", fd, res);
         goto error;
      }

      StopRecvFromVmxConn();
      ProcessVmxDataMap(&map);
      DataMap_Destroy(&map);
   }

   return;

error:
   HandleVmxConnError();
}


/*
 *-----------------------------------------------------------------------------
 *
 * VmxConnRecvContentCb --
 *
 *      Callback function after received content bytes from the VMX connection.
 *
 *      VMX responds with a data map, followed by content bytes if no error.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
VmxConnRecvContentCb(void *buf,           // IN
                     int len,             // IN
                     AsyncSocket *asock,  // IN
                     void *clientData)    // IN
{
   //g_debug("Entering %s: len=%d.\n", __FUNCTION__, len);

   ASSERT(theVmxConn == clientData);
   ASSERT(theVmxConn != NULL);
   ASSERT(theVmxConn->asock != NULL);

   theVmxConn->bytesRemaining -= len;
   if (theVmxConn->bytesRemaining < 0) {
      g_warning("Recv from VMX connection %d exceeded content size.\n",
                AsyncSocket_GetFd(theVmxConn->asock));
      HandleVmxConnError();
      return;
   }

   StopRecvFromVmxConn();

   if (theVmxConn->bytesRemaining == 0) {
      currentClientConn->shutDown = TRUE;
   }

   SendToCurrentClientConn(buf, len);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VmxConnectCb --
 *
 *      Poll callback function for a new VMX connection.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *-----------------------------------------------------------------------------
 */

static void
VmxConnectCb(AsyncSocket *asock,  // IN
             void *clientData)    // IN
{
   int fd = AsyncSocket_GetFd(asock);
   int res;

   g_debug("Entering %s.\n", __FUNCTION__);
   g_info("Got new VMX connection %d.\n", fd);

   StopVmxToGuestConnTimeout();

   if (!pluginData.vmxConnectRequested) {
      g_warning("Closing the unexpected VMX connection %d.\n", fd);
      AsyncSocket_Close(asock);
      return;
   }

   if (theVmxConn != NULL) {
      g_warning("The VMX connection already exists, closing the extra "
                "VMX connection %d.\n", fd);
      AsyncSocket_Close(asock);
      return;
   }

   if (AsyncSocket_GetState(asock) != AsyncSocketConnected) {
      g_info("VMX connection %d is not in connected state.\n", fd);
      goto error;
   }

   if (!AsyncSocket_EstablishMinBufferSizes(asock,
           GUESTSTORE_REQUEST_BUFFER_SIZE, // sendSz
           GUESTSTORE_RESPONSE_BUFFER_SIZE)) { // recvSz
      g_warning("AsyncSocket_EstablishMinBufferSizes failed "
                "on VMX connection %d.\n", fd);
      goto error;
   }

   theVmxConn = (VmxConnInfo *)Util_SafeCalloc(1, sizeof *theVmxConn);

   theVmxConn->asock = asock;

   res = AsyncSocket_SetErrorFn(asock, VmxConnErrorCb, theVmxConn);
   if (res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_SetErrorFn failed "
                "on VMX connection %d: %s\n",
                fd, AsyncSocket_Err2String(res));
      goto error;
   }

   theVmxConn->bufLen = VMX_CONN_SEND_RECV_BUF_SIZE;
   theVmxConn->buf = Util_SafeMalloc(theVmxConn->bufLen);

   theVmxConn->connTimeout = GUESTSTORE_CONFIG_GET_INT("connTimeout",
      GUESTSTORE_DEFAULT_CONN_TIMEOUT);
   if (theVmxConn->connTimeout <= 0 ||
       theVmxConn->connTimeout > (G_MAXINT / 1000)) {
      g_warning("Invalid connTimeout (%d); Using default (%d).\n",
                theVmxConn->connTimeout, GUESTSTORE_DEFAULT_CONN_TIMEOUT);
      theVmxConn->connTimeout = GUESTSTORE_DEFAULT_CONN_TIMEOUT;
   }

   StartConnInactivityTimeout();

   if (currentClientConn == NULL) {
      StartServeNextClientConn();
   } else {
      CheckSendRequestDataMapToVmxConn();
   }

   return;

error:
   g_info("Closing VMX connection %d.\n", fd);
   AsyncSocket_Close(asock);
   if (theVmxConn != NULL) {
      free(theVmxConn);
      theVmxConn = NULL;
   }

   CloseCurrentClientConn();
   CloseClientConnsInWait();
   pluginData.vmxConnectRequested = FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CreateVmxListenSocket --
 *
 *      Create listening vsocket to accept connection from VMX.
 *      The auto-assigned port number will be sent to VMX via guest RPC.
 *
 * Results:
 *      TRUE on success, FALSE otherwise
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CreateVmxListenSocket(void)
{
   int res = ASOCKERR_SUCCESS;
   AsyncSocket *asock;

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(pluginData.vmxListenSock == NULL);

   asock = AsyncSocket_ListenVMCI(VMCISock_GetLocalCID(), VMADDR_PORT_ANY,
                                  VmxConnectCb, NULL, NULL, &res);
   if (NULL == asock || res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_ListenVMCI failed: %s\n",
                AsyncSocket_Err2String(res));
      if (asock != NULL) {
         AsyncSocket_Close(asock);
      }
      return FALSE;
   }

   pluginData.vmxListenSock = asock;

   return TRUE;
}


#ifdef _WIN32

/*
 *-----------------------------------------------------------------------------
 *
 * CreateClientListenSocket --
 *
 *      Create listening socket to accept connections from clients.
 *
 * Results:
 *      TRUE on success, FALSE otherwise
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CreateClientListenSocket(void)
{
   AsyncSocket *asock = NULL;
   uint16 port;
   PortUsage portUseMap[GUESTSTORE_LOOPBACK_PORT_MAX -
                        GUESTSTORE_LOOPBACK_PORT_MIN + 1] = { 0 };

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(pluginData.clientListenSock == NULL);

   /*
    * Use output of GetPortUseMap as a hint, it does not matter
    * if GetPortUseMap fails.
    */
   GetPortUseMap(GUESTSTORE_LOOPBACK_PORT_MIN,
                 GUESTSTORE_LOOPBACK_PORT_MAX,
                 portUseMap);

   for (port = GUESTSTORE_LOOPBACK_PORT_MIN;
        port <= GUESTSTORE_LOOPBACK_PORT_MAX;
        port++) {
      int res = ASOCKERR_SUCCESS;
      PortUsage *portUse = &portUseMap[port - GUESTSTORE_LOOPBACK_PORT_MIN];

      /*
       * Use || instead of && to avoid confusion to see a port used by
       * one service on tcp but another service on tcp6.
       */
      if (portUse->inet4 || portUse->inet6) {
         continue;
      }

      asock = AsyncSocket_ListenLoopback(port, ClientConnectCb, NULL, NULL, &res);
      if (asock != NULL) {
         break;
      }

      if (res == ASOCKERR_BINDADDRINUSE || res == ASOCK_EADDRINUSE) {
         g_info("Port %u is already in use.\n", port);
      } else {
         g_warning("AsyncSocket_ListenLoopback failed on port %u: %s\n",
                   port, AsyncSocket_Err2String(res));
         break;
      }
   }

   if (asock == NULL) {
      return FALSE;
   }

   pluginData.clientListenSock = asock;
   return TRUE;
}

#else

/*
 *-----------------------------------------------------------------------------
 *
 * CreateSocketDir --
 *
 *      Create the UNIX domain socket directory with proper permissions.
 *
 * Results:
 *      Return TRUE if the directory is created or already exists, and the
 *      permissions are set properly. Otherwise FALSE.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CreateSocketDir(const char *sockDir)  // IN
{
   Bool retVal;
   int mode = 0755;  // Same mode as VGAuth service socket dir
   struct stat st;

   ASSERT(sockDir != NULL && *sockDir != '\0');

   retVal = File_EnsureDirectoryEx(sockDir, mode);
   if (!retVal) {
      g_warning("Unable to create folder %s: error=%d.\n",
                sockDir, errno);
      goto exit;
   }

   /*
    * Verify the directory owner and permissions.
    */
   if (Posix_Lstat(sockDir, &st) != 0) {
      g_warning("Unable to retrieve the attributes of %s: error=%d.\n",
                sockDir, errno);
      retVal = FALSE;
      goto exit;
   }

   if (st.st_uid != getuid()) {
      g_warning("%s has the wrong owner.\n", sockDir);
      retVal = FALSE;
      goto exit;
   }

   if ((st.st_mode & 0777) != mode &&
       !File_SetFilePermissions(sockDir, (st.st_mode & 07000) | mode)) {
      g_warning("%s has improper permissions.\n", sockDir);
      retVal = FALSE;
   }

exit:
   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * AdjustSocketFilePermissions --
 *
 *      Adjust the UNIX domain socket file permissions to control who can
 *      connect.
 *
 * Results:
 *      Return TRUE if the permissions are set properly. Otherwise FALSE.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
AdjustSocketFilePermissions(const char *sockFile,     // IN
                            Bool onlyRootCanConnect)  // IN
{
   Bool retVal = FALSE;
   int mode;
   struct stat st;

   ASSERT(sockFile != NULL && *sockFile != '\0');

   /*
    * Add sticky bit if everyone can connect.
    */
   mode = (onlyRootCanConnect ? 0755 : 01777);

   if (Posix_Lstat(sockFile, &st) != 0) {
      g_warning("Unable to retrieve the attributes of %s: error=%d.\n",
                sockFile, errno);
      goto exit;
   }

   if ((st.st_mode & 01777) != mode &&
       !File_SetFilePermissions(sockFile, (st.st_mode & 07000) | mode)) {
      g_warning("%s has improper permissions.\n", sockFile);
      goto exit;
   }

   retVal = TRUE;

exit:
   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * CreateClientListenSocket --
 *
 *      Create listening socket to accept connections from clients.
 *
 * Results:
 *      TRUE on success, FALSE otherwise
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
CreateClientListenSocket(void)
{
   int res = ASOCKERR_SUCCESS;
   AsyncSocket *asock;

   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(pluginData.clientListenSock == NULL);

   CheckAndUpdateAdminOnly();

   if (!CreateSocketDir(GUESTSTORE_PIPE_DIR)) {
      g_warning("CreateSocketDir failed.\n");
      return FALSE;
   }

   File_Unlink(GUESTSTORE_PIPE_NAME);

   asock = AsyncSocket_ListenSocketUDS(
      GUESTSTORE_PIPE_NAME,
      ClientConnectCb, NULL,
      NULL, &res);

   if (asock == NULL || res != ASOCKERR_SUCCESS) {
      g_warning("AsyncSocket_ListenSocketUDS failed: %s\n",
                AsyncSocket_Err2String(res));

      if (asock != NULL) {
         AsyncSocket_Close(asock);
      }

      return FALSE;
   }

   /*
    * Ideally, this should be done after bind() and before listen() and
    * accept(). Since asyncsocket library shares TCP socket implementation
    * code, there is no such interface to do it. Doing it here is fine,
    * because the initial permission settings allow root to connect only.
    */
   if (!AdjustSocketFilePermissions(GUESTSTORE_PIPE_NAME,
                                    pluginData.adminOnly)) {
      g_warning("AdjustSocketFilePermissions failed.\n");
      AsyncSocket_Close(asock);
      return FALSE;
   }

   pluginData.clientListenSock = asock;
   return TRUE;
}

#endif


/*
 *-----------------------------------------------------------------------------
 *
 * InitPluginData --
 *
 *      Init pluginData structure.
 *      - 'ctx': ToolsAppCtx
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
InitPluginData(ToolsAppCtx *ctx)  // IN
{
   pluginData.ctx = ctx;
   CheckAndUpdateFeatureDisabled();
   CheckAndUpdateAdminOnly();
}


/*
 *-----------------------------------------------------------------------------
 *
 * InitPluginSignals --
 *
 *      Init signals for notification.
 *      - 'ctx': ToolsAppCtx
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
InitPluginSignals(ToolsAppCtx* ctx)  // IN
{
   /* Register the signals we'll use to notify people interested in this event. */
   g_signal_new(TOOLS_CORE_SIG_GUESTSTORE_STATE,
                G_OBJECT_TYPE(ctx->serviceObj),
                0,
                0,
                NULL,
                NULL,
                g_cclosure_marshal_VOID__BOOLEAN,
                G_TYPE_NONE,
                1,
                G_TYPE_BOOLEAN);
}


/*
 *----------------------------------------------------------------------------
 *
 * GuestStoreAccessDisable --
 *
 *     Close all sockets/connections and reset plugin internal states.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

static void
GuestStoreAccessDisable(void)
{
   g_debug("Entering %s.\n", __FUNCTION__);

   if (!pluginData.shutdown) {
      g_signal_emit_by_name(pluginData.ctx->serviceObj,
                            TOOLS_CORE_SIG_GUESTSTORE_STATE,
                            FALSE);
   }

   pluginData.guestStoreAccessEnabled = FALSE;

   if (pluginData.vmxListenSock) {
      AsyncSocket_Close(pluginData.vmxListenSock);
      pluginData.vmxListenSock = NULL;
   }

   if (pluginData.clientListenSock) {
      AsyncSocket_Close(pluginData.clientListenSock);
      pluginData.clientListenSock = NULL;
   }

   CloseCurrentClientConn();
   CloseClientConnsInWait();

   if (theVmxConn != NULL && !theVmxConn->shutDown) {
      /*
       * After CloseCurrentClientConn(), send shutdown data map to VMX.
       */
      SendDataMapToVmxConn();
   } else {
      /*
       * Force to stop.
       */
      CloseVmxConn();
      StopVmxToGuestConnTimeout();
      pluginData.vmxConnectRequested = FALSE;  // To make sure
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * GuestStoreAccessEnable --
 *
 *     Create the sockets and start listening.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

static void
GuestStoreAccessEnable(void)
{
   g_debug("Entering %s.\n", __FUNCTION__);

   ASSERT(!pluginData.guestStoreAccessEnabled);

   if (!CreateVmxListenSocket() || !CreateClientListenSocket()) {
      g_warning("GuestStore access is deactivated "
                "due to initialization error.\n");
      GuestStoreAccessDisable();
      return;
   }

   pluginData.guestStoreAccessEnabled = TRUE;
   g_signal_emit_by_name(pluginData.ctx->serviceObj,
                         TOOLS_CORE_SIG_GUESTSTORE_STATE,
                         TRUE);
}


/*
 *-----------------------------------------------------------------------------
 *
 * GetVmxGuestStoreAccessEnabledState --
 *
 *      Send a GuestRpc command to VMX to retrieve guestStore.accessEnabled
 *      state.
 *
 * Result:
 *      TRUE : access enabled
 *      FALSE: access disabled
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
GetVmxGuestStoreAccessEnabledState(void)
{
   Bool retVal;
   const char *msg = "guestStore.accessEnabled";
   char *result = NULL;
   size_t resultLen;

   retVal = RpcChannel_Send(pluginData.ctx->rpc, msg, strlen(msg),
                            &result, &resultLen);
   if (retVal) {
      retVal = (strcmp(result, "true") == 0 ? TRUE : FALSE);
   } else {
      g_warning("Failed to send accessEnabled message to VMX: %s.\n",
                result != NULL ? result : "");
   }

   vm_free(result);

   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreShutdown --
 *
 *      Deactivate GuestStore access before shutdown.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
GuestStoreShutdown(void)
{
   pluginData.shutdown = TRUE;
   g_object_set(pluginData.ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GUESTSTORE,
                NULL, NULL);

   if (pluginData.guestStoreAccessEnabled) {
      GuestStoreAccessDisable();
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreConfReload --
 *
 *      Disable/enable GuestStore access after guest side config change.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
GuestStoreConfReload(gpointer src,      // IN
                     ToolsAppCtx *ctx,  // IN
                     gpointer data)     // IN
{
   Bool featureDisabled = IsFeatureDisabled();

   if (pluginData.featureDisabled != featureDisabled) {
      pluginData.featureDisabled = featureDisabled;

      if (pluginData.guestStoreAccessEnabled && featureDisabled) {
         g_info("Disable GuestStore access after guest side "
                "config change.\n");
         GuestStoreAccessDisable();
      } else if (!pluginData.guestStoreAccessEnabled &&
                 !featureDisabled &&
                 GetVmxGuestStoreAccessEnabledState()) {
         g_info("Enable GuestStore access after guest side "
                "config change.\n");
         GuestStoreAccessEnable();
      }
   } else {
      Bool adminOnly = IsAdminOnly();

      if (pluginData.adminOnly != adminOnly) {
         pluginData.adminOnly = adminOnly;

         if (pluginData.guestStoreAccessEnabled) {
            g_info("Reset GuestStore access after guest side "
                   "config change.\n");
            GuestStoreAccessDisable();
            GuestStoreAccessEnable();
         }
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreReset --
 *
 *      VMX connection ASOCKERR_REMOTE_DISCONNECT (4) error callback is not
 *      seen on Windows guests after suspend/resume, address this in tools
 *      reset signal handler.
 *
 * Results:
 *      None
 *
 * Side-effects:
 *      Client connections will also be interrupted by tools reset due to
 *      sporadic guest hang.
 *
 *-----------------------------------------------------------------------------
 */

static void
GuestStoreReset(gpointer src,      // IN
                ToolsAppCtx *ctx,  // IN
                gpointer data)     // IN
{
   if (theVmxConn != NULL) {
#ifdef _WIN32
      /*
       * After suspend/resume, VMX side vsocket is closed, VMX connection is
       * broken, but VmxConnErrorCb() is not called on Windows guests.
       * We still send shutdown data map to VMX connection here. We see
       * AsyncSocket_Send() succeeds and either VmxConnSendDataMapCb or
       * VmxConnErrorCb() is called in tests. This minimizes impact on
       * sporadic guest hang case where VMX connection is not broken and
       * we want VMX to close its side vsocket proactively.
       */
      g_info("Perform tools reset by closing active connections.\n");
      CloseActiveConnections();
#endif
   } else if (pluginData.vmxConnectRequested) {
      /*
       * GuestStoreAccessDisable() closes pluginData.vmxListensock, which
       * cancels any pending VmxConnectCb() call.
       * GuestStoreAccessDisable() also calls StopVmxToGuestConnTimeout().
       */
      g_info("Perform tools reset without VMX connection "
             "but VMX connect request was made.\n");
      GuestStoreAccessDisable();
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * GuestStoreSetOption --
 *
 *      Handle TOOLSOPTION_ENABLE_GUESTSTORE_ACCESS Set_Option callback.
 *
 * Results:
 *      TRUE if action is taken on the signal.
 *
 * Side-effects:
 *      None
 *
 *----------------------------------------------------------------------------
 */

static gboolean
GuestStoreSetOption(gpointer src,         // IN
                    ToolsAppCtx *ctx,     // IN
                    const gchar *option,  // IN
                    const gchar *value,   // IN
                    gpointer data)        // IN
{
   gboolean retVal = FALSE;

   if (strcmp(option, TOOLSOPTION_ENABLE_GUESTSTORE_ACCESS) == 0) {
      g_debug("Tools set option %s=%s.\n",
              TOOLSOPTION_ENABLE_GUESTSTORE_ACCESS, value);

      if (strcmp(value, "1") == 0 &&
          !pluginData.guestStoreAccessEnabled) {
         if (pluginData.featureDisabled) { // Use cached state here
            g_info("GuestStore access is deactivated on guest side.\n");
         } else {
            GuestStoreAccessEnable();
            retVal = TRUE;
         }
      } else if (strcmp(value, "0") == 0 &&
                 pluginData.guestStoreAccessEnabled) {
         GuestStoreAccessDisable();
         retVal = TRUE;
      }
   }

   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsOnLoad --
 *
 *      Return the registration data for the GuestStore plugin.
 *
 * Results:
 *      Return the registration data.
 *
 * Side-effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)  // IN
{
   static ToolsPluginData regData = { "guestStore", NULL, NULL, NULL };
   static ToolsPluginSvcGuestStore svcGuestStore = { GuestStoreShutdown };

   ToolsServiceProperty propGuestStore = { TOOLS_PLUGIN_SVC_PROP_GUESTSTORE };

   uint32 vmxVersion = 0;
   uint32 vmxType = VMX_TYPE_UNSET;

   ToolsPluginSignalCb sigs[] = {
      { TOOLS_CORE_SIG_CONF_RELOAD, GuestStoreConfReload, NULL },
      { TOOLS_CORE_SIG_RESET, GuestStoreReset, NULL },
      { TOOLS_CORE_SIG_SET_OPTION, GuestStoreSetOption, NULL }
   };
   ToolsAppReg regs[] = {
      { TOOLS_APP_SIGNALS,
        VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
   };

   /*
    * Return NULL to deactivate the plugin if not running in vmsvc daemon.
    */
   if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
      g_info("Not running in vmsvc daemon: container name='%s'.\n",
             ctx->name);
      return NULL;
   }

   /*
    * Return NULL to deactivate the plugin if not running in a VMware VM.
    */
   if (!ctx->isVMware) {
      g_info("Not running in a VMware VM.\n");
      return NULL;
   }

   /*
    * Return NULL to deactivate the plugin if VM is not running on ESX host.
    */
   if (!VmCheck_GetVersion(&vmxVersion, &vmxType) ||
       vmxType != VMX_TYPE_SCALABLE_SERVER) {
      g_info("VM is not running on ESX host.\n");
      return NULL;
   }

   InitPluginData(ctx);
   InitPluginSignals(ctx);
   Poll_InitGtk();

   ctx->registerServiceProperty(ctx->serviceObj, &propGuestStore);
   g_object_set(ctx->serviceObj, TOOLS_PLUGIN_SVC_PROP_GUESTSTORE,
                &svcGuestStore, NULL);

   regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));

   return &regData;
}
  0707010000036D000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/hgfsServer    0707010000036E000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/hgfsServer/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   0707010000036F000081A40000000000000000000000016822550500000512000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/hgfsServer/Makefile.am    ################################################################################
### Copyright (C) 2009-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @COMMON_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libhgfsServer.la

libhgfsServer_la_CPPFLAGS =
libhgfsServer_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libhgfsServer_la_LDFLAGS =
libhgfsServer_la_LDFLAGS += @PLUGIN_LDFLAGS@

libhgfsServer_la_LIBADD =
libhgfsServer_la_LIBADD += @VMTOOLS_LIBS@
libhgfsServer_la_LIBADD += @HGFS_LIBS@

libhgfsServer_la_SOURCES =
libhgfsServer_la_SOURCES += hgfsPlugin.c

  07070100000370000081A40000000000000000000000016822550500008598000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/hgfsServer/hgfsPlugin.c   /*********************************************************
 * Copyright (c) 2008-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file hgfsPlugin.c
 *
 * Functionality to utilize the hgfs server in bora/lib as a tools plugin.
 */

#if defined(_WIN32)
#include <windows.h>
#include <strsafe.h>
#include <aclapi.h>
#endif // defined(_WIN32)
#include <string.h>

#define G_LOG_DOMAIN "hgfsd"

#include "hgfs.h"
#include "hgfsServerManager.h"
#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "vm_vmx_type.h"
#include "vmcheck.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/log.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"

#if defined(_WIN32)
#include "hgfsWinNtInternal.h"  // For reconnection of drives

#define NET_BUFFER_SIZE        3000
#endif // defined(_WIN32)

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif

#if defined(_WIN32)
typedef enum HgfsClientRdrServiceOp {
   HGFS_CLIENTRDR_SERVICE_START = 0,
   HGFS_CLIENTRDR_SERVICE_QUERY_STARTED,
} HgfsClientRdrServiceOp;

static void HgfsServerDestroyClientRdrSA(PSID *everyoneSID,
                                         PSID *adminSID,
                                         PACL *accessControlList,
                                         PSECURITY_DESCRIPTOR *securityDescriptor);

static DWORD HgfsServerClientRdrWaitForEvent(DWORD millSecTimeout);
static void HgfsServerClientRdrSetEvent(void);
#endif // defined(_WIN32)
static void HgfsServerCloseClientRdrEvent(void);

/**
 * Clean up internal state on shutdown.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Unused.
 * @param[in]  plugin   Plugin registration data.
 */

static void
HgfsServerShutdown(gpointer src,
                   ToolsAppCtx *ctx,
                   ToolsPluginData *plugin)
{
   HgfsServerMgrData *mgrData = plugin->_private;
   HgfsServerManager_Unregister(mgrData);
   g_free(mgrData);
   HgfsServerCloseClientRdrEvent();
}


/**
 * Handles hgfs requests.
 *
 * @param[in]  data  RPC request data.
 *
 * @return TRUE on success, FALSE on error.
 */

static gboolean
HgfsServerRpcDispatch(RpcInData *data)
{
   HgfsServerMgrData *mgrData;
   size_t replySize;
   static char reply[HGFS_LARGE_PACKET_MAX];


   ASSERT(data->clientData != NULL);
   mgrData = data->clientData;

   if (data->argsSize == 0) {
      return RPCIN_SETRETVALS(data, "1 argument required", FALSE);
   }

   replySize = sizeof reply;
   HgfsServerManager_ProcessPacket(mgrData, data->args + 1, data->argsSize - 1, reply, &replySize);

   data->result = reply;
   data->resultLen = replySize;
   return TRUE;
}


/**
 * Sends the HGFS capability to the VMX.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  set      Whether setting or unsetting the capability.
 * @param[in]  data     Unused.
 *
 * @return NULL. The function sends the capability directly.
 */

static GArray *
HgfsServerCapReg(gpointer src,
                 ToolsAppCtx *ctx,
                 gboolean set,
                 ToolsPluginData *plugin)
{
   gchar *msg;
   const char *appName = NULL;

   if (TOOLS_IS_MAIN_SERVICE(ctx)) {
      appName = TOOLS_DAEMON_NAME;
   } else if (TOOLS_IS_USER_SERVICE(ctx)) {
      appName = TOOLS_DND_NAME;
   } else {
      NOT_REACHED();
   }

   msg = g_strdup_printf("tools.capability.hgfs_server %s %d",
                         appName,
                         set ? 1 : 0);

   /*
    * Prior to WS55, the VMX did not know about the "hgfs_server"
    * capability. This doesn't mean that the HGFS server wasn't needed, it's
    * just that the capability was introduced in CS 225439 so that the VMX
    * could decide which HGFS server to communicate with.
    *
    * Long story short, we shouldn't care if this function fails.
    */
   if (ctx->rpc && !RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, NULL, NULL)) {
      g_warning("Setting HGFS server capability failed!\n");
   }

   g_free(msg);
   return NULL;
}


#if defined(_WIN32)

#define HGFS_CLIENT_START_EVENT_NAME   L"HGFS_CLIENT_START_EVENT"
#define LOCAL_PREFIX                   L"Local\\%s"
#define GLOBAL_PREFIX                  L"Global\\%s"

static HANDLE gHgfsServerStartClientEvent = NULL;


/**
 * Starts the client driver service.
 *
 * @param[in]  serviceControlManager   Service Control Manager handle.
 * @param[in]  accessFlags             Service desired access.
 * @param[in]  serviceName             Name of the driver service to start.
 *
 * @return ERROR_SUCCESS on success, an appropriate error otherwise.
 */

static DWORD
HgfsServerStartClientService(SC_HANDLE serviceControlManager,
                             DWORD accessFlags,
                             PCWSTR serviceName)
{
   SC_HANDLE   service;
   DWORD       result = ERROR_SUCCESS;

   g_info("%s: Info: start service %S\n", __FUNCTION__, serviceName);

   /*
    * Open the handle to the existing service.
    */
   service = OpenServiceW(serviceControlManager, serviceName, accessFlags);
   if (NULL == service) {
      result = GetLastError();
      g_warning("%s: Error: open service %S = %d \n", __FUNCTION__, serviceName, result);
      goto exit;
   }

   /*
    * Start the execution of the service (i.e. start the driver).
    */
   if (!StartServiceW(service, 0, NULL)) {
      result = GetLastError();

      if (ERROR_SERVICE_ALREADY_RUNNING == result) {
         result = ERROR_SUCCESS;
      } else {
         g_warning("%s: Error: start service %S = %d \n", __FUNCTION__, serviceName, result);
      }
      goto exit;
   }

exit:
   if (NULL != service) {
      CloseServiceHandle(service);
   }

   g_info("%s: Info: Done %S: %u\n", __FUNCTION__, serviceName, result);
   return result;
}


/**
 * Query the client driver service status.
 *
 * @param[in]  serviceControlManager   Service Control Manager handle.
 * @param[in]  accessFlags             Service desired access.
 * @param[in]  serviceName             Name of the driver service to query.
 * @param[out] currentState            Current state of the driver service.
 *
 * @return ERROR_SUCCESS on success, an appropriate error otherwise.
 */

static DWORD
HgfsServerQueryClientService(SC_HANDLE serviceControlManager,
                             DWORD accessFlags,
                             PCWSTR serviceName,
                             DWORD *currentState)
{
   SC_HANDLE      service;
   SERVICE_STATUS serviceStatus;
   DWORD          result = ERROR_SUCCESS;

   g_info("%s: query service %S\n", __FUNCTION__, serviceName);

   /*
    * Open the handle to the existing service.
    */
   service = OpenServiceW(serviceControlManager, serviceName, accessFlags);
   if (NULL == service) {
      result = GetLastError();
      g_warning("%s: Error: open service %S = %u\n", __FUNCTION__, serviceName, result);
      goto exit;
   }

   /*
    * Query the status of the service (i.e. is it stopped or started).
    */
   if (!QueryServiceStatus(service, &serviceStatus)) {
      result = GetLastError();
      g_warning("%s: Error: start service %S = %u\n", __FUNCTION__, serviceName, result);
      goto exit;
   }

   *currentState = serviceStatus.dwCurrentState;

exit:
   if (NULL != service) {
      CloseServiceHandle(service);
   }

   g_info("%s: Done %S: %u\n", __FUNCTION__, serviceName, result);
   return result;
}


/**
 * Service the HGFS client redirector.
 *
 * @param[in]  serviceOp      Service operation to perform - start or query.
 * @param[in]  accessFlags    Service access flags.
 *
 * @return ERROR_SUCCESS or an appropriate error.
 */

static DWORD
HgfsServerClientRedirectorExecOpImpl(HgfsClientRdrServiceOp serviceOp,
                                     DWORD accessFlags)
{
   SC_HANDLE   serviceControlManager;
   PCWSTR      serviceName = HGFS_SERVICE_NAME_U;
   DWORD       result = ERROR_SUCCESS;

   g_info("%s: Info: Op %d on client redirector %S\n",
          __FUNCTION__, serviceOp, serviceName);

   serviceControlManager = OpenSCManagerW(NULL, NULL, accessFlags);
   if (NULL == serviceControlManager) {
      result = GetLastError();
      g_warning("%s: Error: Open SC Manager = %u\n", __FUNCTION__, result);
      goto exit;
   }

   switch (serviceOp) {
      case HGFS_CLIENTRDR_SERVICE_START:
         result = HgfsServerStartClientService(serviceControlManager,
                                               accessFlags,
                                               serviceName);
         break;
      case HGFS_CLIENTRDR_SERVICE_QUERY_STARTED: {
         DWORD currentState;
         result = HgfsServerQueryClientService(serviceControlManager,
                                               accessFlags,
                                               serviceName,
                                               &currentState);
         if (ERROR_SUCCESS == result) {
            if (SERVICE_RUNNING == currentState ||
               SERVICE_START_PENDING == currentState) {
               result = ERROR_SUCCESS;
            } else {
               /* Queried but not actually starting or started. */
               result = ERROR_SERVICE_NOT_ACTIVE;
            }
         }
         break;
      }
      default:
      NOT_REACHED();
   }

exit:
   if (NULL != serviceControlManager) {
      CloseServiceHandle(serviceControlManager);
   }
   g_info("%s: Info: Op %d Done %u\n", __FUNCTION__, serviceOp, result);
   return result;
}


/**
 * Service the HGFS client redirector.
 *
 * @param[in]  serviceOp      Service operation to perform - start or query.
 *
 * @return ERROR_SUCCESS or an appropriate error.
 */

static DWORD
HgfsServerClientRedirectorExecOp(HgfsClientRdrServiceOp serviceOp)
{
   DWORD       result = ERROR_SUCCESS;
   DWORD       accessFlags = 0;

   g_info("%s: Info: Service client redirector op %d\n",
          __FUNCTION__, serviceOp);

   switch (serviceOp) {
      case HGFS_CLIENTRDR_SERVICE_START:
         accessFlags |= SERVICE_START;
         break;
      case HGFS_CLIENTRDR_SERVICE_QUERY_STARTED:
         accessFlags |= SERVICE_QUERY_STATUS;
         break;
      default:
         NOT_REACHED();
   }

   result = HgfsServerClientRedirectorExecOpImpl(serviceOp,
                                                 accessFlags);

   g_info("%s: Info: Op %d Done %u\n", __FUNCTION__, serviceOp, result);
   return result;
}


/*
 * The calback is called for each network resource that the VMware Shared Folders
 * provides to the user.
 */

typedef BOOL
HgfsServerNetResourceCb(NETRESOURCEW *netRes);

/**
 * Enumerates all mapped network devices matching on the HGFS provider name.
 *
 * Any remembered disk network resources are passed to the callback.
 *
 * @param[in]  hgfsProvider  HGFS provider name.
 * @param[in]  hgfsResCb     Callback to process HGFS resource.
 *
 * @return TRUE on success and matched a network resource provided by
 *         the VMware Shared Folders client, FALSE otherwise.
 */

static BOOL
HgfsServerEnumerateDrives(LPCWSTR hgfsProvider,
                          HgfsServerNetResourceCb *hgfsResCb)
{
   BOOL success = FALSE;
   HANDLE enumHandle = INVALID_HANDLE_VALUE;
   DWORD callResult;

   callResult = WNetOpenEnum(RESOURCE_REMEMBERED,
                             RESOURCETYPE_DISK,
                             0 /*ignored*/,
                             NULL /*MBZ*/,
                             &enumHandle);

   if (callResult != NO_ERROR) {
      g_warning("%s: Failed to enumerate drives: %u\n", __FUNCTION__, callResult);
      return FALSE;
   }

   for (;;) {
      char buffer[NET_BUFFER_SIZE];
      NETRESOURCEW *netRes = (NETRESOURCEW *)&buffer;
      DWORD netResLen = sizeof buffer;
      DWORD count = 1;

      callResult = WNetEnumResource(enumHandle, &count, netRes, &netResLen);
      if (callResult != NO_ERROR || count != 1) {
         if (callResult != ERROR_NO_MORE_ITEMS) {
            g_warning("%s: Enumeration failed with %u %u\n", __FUNCTION__,
                     callResult, count);
         }
         break;
      }

      if (netRes->lpLocalName != NULL) {
         /*
          * If the local path is a drive letter - now check the provider.
          */
         if (hgfsProvider != NULL &&
             _wcsicmp(netRes->lpProvider, hgfsProvider) == 0) {

            g_info("%s: Processing %S -> %S\n", __FUNCTION__,
                   netRes->lpLocalName, netRes->lpRemoteName);

            if (hgfsResCb(netRes)) {
               success = TRUE;
            }
         }
      }
   }

   WNetCloseEnum(enumHandle);

   return success;
}


/**
 * Reconnect the HGFS provided network resource.
 *
 * @param[in]  netRes   VMware Shared Folders netork resource to connect.
 *
 * @return TRUE on success and matched a network resource provided by
 *         the VMware Shared Folders client, FALSE otherwise.
 */

static BOOL
HgfsServerReconnectNetResource(NETRESOURCEW *netRes)
{
   DWORD remoteNameCharSize = wcslen(netRes->lpRemoteName) + 1;
   DWORD callResult;
   BOOL isConnected = FALSE;

   g_info("%s: Get connection for %S -> %S\n", __FUNCTION__,
            netRes->lpLocalName, netRes->lpRemoteName);

   /* Get the current connection state of the resource. */
   callResult = WNetGetConnectionW(netRes->lpLocalName,
                                   netRes->lpRemoteName,
                                   &remoteNameCharSize);

   if (callResult == NO_ERROR) {
      /* Network resource is connected. */
      isConnected = TRUE;
      goto exit;
   }

   /* Network resource is remembered but not connected. */
   if (callResult == ERROR_CONNECTION_UNAVAIL) {
      /* Found a disconnected VMware Shared Folders network resource. */
      callResult = WNetAddConnection2W(netRes,
                                       NULL,
                                       NULL,
                                       0);
      g_info("%s: Reconnection of %S to %S -> %u\n", __FUNCTION__,
               netRes->lpLocalName, netRes->lpRemoteName, callResult);
      if (callResult == NO_ERROR) {
         isConnected = TRUE;
      }
   }

exit:
   g_info("%s: Connection %S to %S is %u\n", __FUNCTION__,
           netRes->lpLocalName, netRes->lpRemoteName, callResult);
   return isConnected;
}


/**
 * Reads a value from the registry.
 *
 * @param[in]    hInitialKey   initial key to start traversing from
 * @param[in]    subKey        name of subkey to open
 * @param[in]    value         name of the registry value
 * @param[inout] outBufSize  size of output buffer/result
 * @param[out]   type          type of value
 * @param[out]   outputBuffer  contents of value
 *
 * @return TRUE on success and retrieved the registry value,
 *         FALSE otherwise.
 */

static DWORD
HgfsServerGetRegistryValue(HKEY hInitialKey,
                           LPCWSTR subKey,
                           LPCWSTR value,
                           LPDWORD outBufSize,
                           LPDWORD type,
                           LPWSTR outputBuffer)
{
   DWORD regResult;
   regResult = RegGetValueW(hInitialKey,
                            subKey,
                            value,
                            RRF_RT_REG_SZ,
                            type,
                            (LPBYTE)outputBuffer,
                            outBufSize);
   if (regResult != ERROR_SUCCESS) {
      g_warning("%s: Error: querying value %S %u\n", __FUNCTION__,
                value, regResult);
   }

   return regResult;
}


/**
 * Gets the VMWare Shared Folders provider name.
 *
 * @param[inout]  hgfsProviderName        Shared Folders provider name buffer.
 * @param[inout]  hgfsProviderNameSize    Shared Folders provider name buffer size.
 *
 * @return TRUE on success and matched a network resource provided by
 *         the VMware Shared Folders client, FALSE otherwise.
 */

static BOOL
HgfsServerGetProviderName(LPWSTR hgfsProviderName,
                          DWORD *hgfsProviderNameSize)
{
   DWORD regKeyType;
   BOOL result = TRUE;

   /* Get the provider name from the Shared Folders client. */
   if (HgfsServerGetRegistryValue(HKEY_LOCAL_MACHINE,
                                  HGFS_PROVIDER_REGISTRY_KEY,
                                  HGFS_PROVIDER_VALUE_NAME,
                                  hgfsProviderNameSize,
                                  &regKeyType,
                                  hgfsProviderName) != ERROR_SUCCESS) {
      g_warning("%s: Error: querying registry shared folders not installed\n",
                __FUNCTION__);
      result = FALSE;
   }
   return result;
}


/**
 * Reconnect the HGFS mapped drives.
 *
 * @return None.
 */

static void
HgfsServerReconnectDrives(void)
{
   WCHAR hgfsProviderName[MAX_PATH] = {0};
   DWORD hgfsProviderNameSize = sizeof hgfsProviderName;

   g_info("%s: Start connecting drives\n", __FUNCTION__);

   /* Obtain the HGFS network provider name. */
   if (HgfsServerGetProviderName(hgfsProviderName,
                                 &hgfsProviderNameSize)) {
      /*
       * Scan the network resources provided by the HGFS network provider.
       * Check if they are connected or try to reconnect them.
       */
      if (HgfsServerEnumerateDrives(hgfsProviderName,
                                    HgfsServerReconnectNetResource)) {
         g_info("%s: Reconnected %S provides drives\n", __FUNCTION__, hgfsProviderName);
      }
   }
}


/**
 * Create the security attributes for Everyone and the Admininstrators group.
 *
 * @param[out]   everyoneSID        Everyone SID.
 * @param[out]   adminSID           Administrators group SID.
 * @param[out]   accessControlList  ACL.
 * @param[out]   securityDescriptor Security descriptor.
 * @param[out]   ea                 Explicit access array.
 * @param[in]    numEAEntries       Explicit access array size.
 * @param[inout] sa                 Security attributes.
 *
 * @return ERROR_SUCCESS on success and an appropriate error otherwise.
 */

static DWORD
HgfsServerCreateClientRdrSA(PSID *everyoneSID,
                            PSID *adminSID,
                            PACL *accessControlList,
                            PSECURITY_DESCRIPTOR *securityDescriptor,
                            EXPLICIT_ACCESSW *ea,
                            ULONG numEAEntries,
                            SECURITY_ATTRIBUTES *sa)
{
   SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
   SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
   DWORD result;

   /* Initialize the out arguments. */
   *everyoneSID = NULL;
   *adminSID = NULL;
   *accessControlList = NULL;
   *securityDescriptor = NULL;
   ZeroMemory(ea, numEAEntries * sizeof *ea);
   ZeroMemory(sa, sizeof *sa);

   /* Create a well-known SID for the Everyone group. */
   if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
                                 SECURITY_WORLD_RID,
                                 0, 0, 0, 0, 0, 0, 0,
                                 everyoneSID)) {
      result = GetLastError();
      g_warning("%s: Error: AllocateAndInitializeSid %u\n",
                __FUNCTION__, result);
      goto cleanup;
   }

   /*
    * Initialize an EXPLICIT_ACCESS structure for an ACE.
    * The ACE will allow Everyone read access to the key.
    */
   ea->grfAccessPermissions = SYNCHRONIZE | EVENT_MODIFY_STATE;
   ea->grfAccessMode = SET_ACCESS;
   ea->grfInheritance= NO_INHERITANCE;
   ea->Trustee.TrusteeForm = TRUSTEE_IS_SID;
   ea->Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
   ea->Trustee.ptstrName  = (LPWSTR) *everyoneSID;

   /* Create a SID for the BUILTIN\Administrators group. */
   if (!AllocateAndInitializeSid(&SIDAuthNT, 2,
                                 SECURITY_BUILTIN_DOMAIN_RID,
                                 DOMAIN_ALIAS_RID_ADMINS,
                                 0, 0, 0, 0, 0, 0,
                                 adminSID)) {
      result = GetLastError();
      g_warning("%s: Error: AllocateAndInitializeSid %u\n",
                __FUNCTION__, GetLastError());
      goto cleanup;
   }

   if (numEAEntries == 2) {
      /*
       * Initialize an EXPLICIT_ACCESS structure for an ACE.
       * The ACE will allow the Administrators group full access to
       * the key.
       */
      (ea+1)->grfAccessPermissions =  EVENT_ALL_ACCESS;
      (ea+1)->grfAccessMode = SET_ACCESS;
      (ea+1)->grfInheritance= NO_INHERITANCE;
      (ea+1)->Trustee.TrusteeForm = TRUSTEE_IS_SID;
      (ea+1)->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
      (ea+1)->Trustee.ptstrName  = (LPWSTR) *adminSID;
   }

   /* Create a new ACL that contains the new ACEs. */
   result = SetEntriesInAclW(numEAEntries, ea, NULL, accessControlList);
   if (ERROR_SUCCESS != result) {
      result = GetLastError();
      g_warning("%s: Error: SetEntriesInAcl Error %u\n", __FUNCTION__, result);
      goto cleanup;
   }

   /* Initialize a security descriptor. */
   *securityDescriptor = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
   if (NULL == *securityDescriptor) {
      result = GetLastError();
      g_warning("%s: Error: LocalAlloc Error %u\n", __FUNCTION__, result);
      goto cleanup;
   }

   if (!InitializeSecurityDescriptor(*securityDescriptor,
                                     SECURITY_DESCRIPTOR_REVISION)) {
      result = GetLastError();
      g_warning("%s: Error: InitializeSecurityDescriptor Error %u\n",
               __FUNCTION__, result);
      goto cleanup;
   }

   /* Add the ACL to the security descriptor. */
   if (!SetSecurityDescriptorDacl(*securityDescriptor,
                                  TRUE,     // dacl present
                                  *accessControlList,
                                  FALSE)) {
      result = GetLastError();
      g_warning("%S: Error: SetSecurityDescriptorDacl Error %u\n",
               __FUNCTION__, result);
      goto cleanup;
   }

   /* Initialize a security attributes structure. */
   sa->nLength = sizeof *sa;
   sa->lpSecurityDescriptor = *securityDescriptor;
   sa->bInheritHandle = FALSE;
   result = ERROR_SUCCESS;

cleanup:
   if (ERROR_SUCCESS != result) {
      HgfsServerDestroyClientRdrSA(everyoneSID,
                                   adminSID,
                                   accessControlList,
                                   securityDescriptor);
      sa->lpSecurityDescriptor = NULL;
      sa->nLength = 0;
      ZeroMemory(ea, numEAEntries * sizeof *ea);
   }
   return result;
}


/**
 * Destroy all the security attributes resources.
 *
 * @param[inout]  everyoneSID        Everyone SID.
 * @param[inout]  adminSID           Administrators group SID.
 * @param[inout]  accessControlList  ACL.
 * @param[inout]  securityDescriptor Security descriptor.
 *
 * @return None
 */

static void
HgfsServerDestroyClientRdrSA(PSID *everyoneSID,
                             PSID *adminSID,
                             PACL *accessControlList,
                             PSECURITY_DESCRIPTOR *securityDescriptor)
{
   if (*everyoneSID) {
      FreeSid(*everyoneSID);
      *everyoneSID = NULL;
   }
   if (*adminSID) {
      FreeSid(*adminSID);
      *adminSID = NULL;
   }
   if (*accessControlList) {
      LocalFree(*accessControlList);
      *accessControlList = NULL;
   }
   if (*securityDescriptor) {
      LocalFree(*securityDescriptor);
      *securityDescriptor = NULL;
   }
}


/**
 * Create the client start synchronization lock.
 *
 * @param[in]  syncEventName       Synchronization event name.
 *
 * @return ERROR_SUCCESS on success, an error otherwise
 */

static DWORD
HgfsServerClientRdrCreateEvent(LPCWSTR syncEventName)
{
   DWORD result;
   PSID everyoneSID = NULL, adminSID = NULL;
   PACL accessControlList = NULL;
   PSECURITY_DESCRIPTOR securityDescriptor = NULL;
   EXPLICIT_ACCESSW ea[2];
   SECURITY_ATTRIBUTES sa;

   ASSERT(NULL == gHgfsServerStartClientEvent);
   ASSERT(NULL != syncEventName);

   result = HgfsServerCreateClientRdrSA(&everyoneSID,
                                        &adminSID,
                                        &accessControlList,
                                        &securityDescriptor,
                                        &ea[0],
                                        ARRAYSIZE(ea),
                                        &sa);
   if (ERROR_SUCCESS != result) {
      /* Error result already logged. */
      goto exit;
   }

   gHgfsServerStartClientEvent = CreateEventW(&sa, TRUE, FALSE, syncEventName);
   if (NULL == gHgfsServerStartClientEvent) {
      result = GetLastError();
      g_warning("%s: Error: Creating %S = %u\n", __FUNCTION__, syncEventName, result);
   }

   HgfsServerDestroyClientRdrSA(&everyoneSID,
                                 &adminSID,
                                 &accessControlList,
                                 &securityDescriptor);

exit:
   return result;
}


/**
 * Open the client start synchronization event.
 *
 * @param[in]  syncEventName       Synchronization event name.
 *
 * @return ERROR_SUCCESS on success, an error otherwise
 */

static uint32
HgfsServerClientRdrOpenEvent(LPCWSTR syncEventName)
{
   uint32 result = ERROR_SUCCESS;

   ASSERT(NULL == gHgfsServerStartClientEvent);
   ASSERT(NULL != syncEventName);

   gHgfsServerStartClientEvent = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE,
                                            FALSE,
                                            syncEventName);
   if (NULL == gHgfsServerStartClientEvent) {
      result = GetLastError();
      g_warning("%s: Error: Opening %S = %u\n", __FUNCTION__, syncEventName, result);
   }

   return result;
}


/**
 * Wait for the client start synchronization event.
 *
 * @param[in]  millisecTimeout     Wait time out in ms.
 *
 * @return WAIT_OBJECT_0 or a wait error
 */

static DWORD
HgfsServerClientRdrWaitForEvent(DWORD millisecTimeout)
{
   DWORD result;
   ASSERT(NULL != gHgfsServerStartClientEvent);

   if (NULL != gHgfsServerStartClientEvent) {
      result = WaitForSingleObject(gHgfsServerStartClientEvent,
                                   millisecTimeout);
      if (result != WAIT_OBJECT_0) {
         g_warning("%s: Error: Wait for event = %u\n", __FUNCTION__, result);
      }
   }
   return result;
}


/**
 * Set the client start synchronization event.
 *
 * @return None
 */

static void
HgfsServerClientRdrSetEvent(void)
{
   ASSERT(gHgfsServerStartClientEvent != NULL);

   if (NULL != gHgfsServerStartClientEvent) {
      if (!SetEvent(gHgfsServerStartClientEvent)) {
         g_warning("%s: Error: Set event = %u\n", __FUNCTION__, GetLastError());
      }
   }
}


/**
 * Get the client driver synchronization event name.
 *
 * @param[out]    syncEventName      Synchronization mutex name
 * @param[in]     syncEventNameSize  Synchronization mutex name size in bytes.
 *
 * @return ERROR_SUCCESS on success, an appropriate error otherwise.
 */

static DWORD
HgfsServerGetEventName(LPWSTR syncEventName,
                       size_t syncEventNameSize)
{
   DWORD result = ERROR_SUCCESS;

   /* Place the mutex name into the global name space. */
   if (StringCbPrintfW(syncEventName,
                       syncEventNameSize,
                       GLOBAL_PREFIX,
                       HGFS_CLIENT_START_EVENT_NAME) != S_OK) {
      result = ERROR_INSUFFICIENT_BUFFER;
      g_warning("%s: Error: StringCbPrintf failed (%u)\n", __FUNCTION__, result);
   }

   return result;
}


/**
 * Create or Open the client start synchronization event.
 *
 * @return ERROR_SUCCESS on success, an error otherwise
 */

static DWORD
HgfsServerClientRdrGetEvent(void)
{
   DWORD result = ERROR_SUCCESS;
   WCHAR syncEventName[MAX_PATH] = {0};

   ASSERT(NULL == gHgfsServerStartClientEvent);

   result = HgfsServerGetEventName(syncEventName, sizeof syncEventName);
   if (ERROR_SUCCESS != result) {
      goto exit;
   }

   /* Try to create the event incase we are first. */
   result = HgfsServerClientRdrCreateEvent(syncEventName);
   if (ERROR_ACCESS_DENIED == result) {
      /* Already created and we only have permission to open the event. */
      g_info("%s: Info: Opening existing %S\n", __FUNCTION__, syncEventName);
      result = HgfsServerClientRdrOpenEvent(syncEventName);
      if (ERROR_SUCCESS != result) {
         g_warning("%s: Error: Opening %S = %u\n", __FUNCTION__, syncEventName, result);
         ASSERT(NULL == gHgfsServerStartClientEvent);
      }
   }

exit:
   g_info("%s: Done getting %S = %u\n", __FUNCTION__, syncEventName, result);
   return result;
}
#endif // defined(_WIN32)


/**
 * Close the client start synchronization mutex.
 *
 * @return None.
 */

static void
HgfsServerCloseClientRdrEvent(void)
{
#if defined(_WIN32)
   if (NULL != gHgfsServerStartClientEvent) {
      CloseHandle(gHgfsServerStartClientEvent);
      gHgfsServerStartClientEvent = NULL;
      g_info("%s: Info: Closed handle\n", __FUNCTION__);
   }
#endif // defined(_WIN32)
}


/**
 * Start the client redirector.
 *
 * @return None.
 */

static void
HgfsServerClientRdrStart(void)
{
#if defined(_WIN32)
   DWORD eventResult;
   DWORD startResult;

   /* Obtain the client redirector synchronization event. */
   eventResult = HgfsServerClientRdrGetEvent();
   /* Always start the client redirector service. */
   startResult = HgfsServerClientRedirectorExecOp(HGFS_CLIENTRDR_SERVICE_START);
   if (ERROR_SUCCESS == startResult &&
       ERROR_SUCCESS == eventResult) {
      /* Client has started so signal the synchronization event. */
      HgfsServerClientRdrSetEvent();
   }
   g_info("%s: Info: Done start %u notify %u\n", __FUNCTION__, startResult, eventResult);
#endif // defined(_WIN32)
}


/**
 * Reconnect the client redirector's mapped drives.
 *
 * @return None.
 */

static void
HgfsServerClientRdrConnectDrives(void)
{
#if defined(_WIN32)
   DWORD result;

#define HGFS_SERVER_WAIT_FOR_CLIENT_COUNT    5
#define HGFS_SERVER_WAIT_FOR_CLIENT_PERIOD   2000

   result = HgfsServerClientRdrGetEvent();
   /* Obtain the client redirector synchronization event. */
   if (ERROR_SUCCESS == result) {
      DWORD retries = HGFS_SERVER_WAIT_FOR_CLIENT_COUNT;

      do {
         /* Check if the client redirector service has started. */
         result = HgfsServerClientRedirectorExecOp(HGFS_CLIENTRDR_SERVICE_QUERY_STARTED);
         /* Check if the main service created the event yet. If not we wait and retry. */
         if (ERROR_SERVICE_NOT_ACTIVE == result) {
            retries--;
            g_info("%s: client rdr not active will retry %u times\n", __FUNCTION__, retries);
            HgfsServerClientRdrWaitForEvent(HGFS_SERVER_WAIT_FOR_CLIENT_PERIOD);
         }
      } while (ERROR_SERVICE_NOT_ACTIVE == result && retries > 0);
   }

   if (ERROR_SUCCESS == result) {
      HgfsServerReconnectDrives();
   }
   g_info("%s: Done %u\n", __FUNCTION__, result);
#endif // defined(_WIN32)
}



/**
 * Returns the registration data for the HGFS server.
 *
 * @param[in]  ctx   The application context.
 *
 * @return The registration data.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "hgfsServer",
      NULL,
      NULL
   };
   HgfsServerMgrData *mgrData;
   uint32 vmxVersion = 0;
   uint32 vmxType = VMX_TYPE_UNSET;

   if (!TOOLS_IS_MAIN_SERVICE(ctx) && !TOOLS_IS_USER_SERVICE(ctx)) {
      g_info("Unknown container '%s', not loading HGFS plugin.", ctx->name);
      return NULL;
   }

   /*
    * If not running in a VMware VM, return NULL to deactivate the plugin.
    */
   if (!ctx->isVMware) {
      return NULL;
   }

   /*
    * Check for VM is running in a hosted environment and if so initialize
    * the Shared Folders HGFS client redirector.
    */
   if (VmCheck_GetVersion(&vmxVersion, &vmxType) &&
       vmxType != VMX_TYPE_SCALABLE_SERVER) {
      if (TOOLS_IS_MAIN_SERVICE(ctx)) {
         /* Start the Shared Folders redirector client. */
         HgfsServerClientRdrStart();
      } else if (TOOLS_IS_USER_SERVICE(ctx)) {
         /*
          * If Explorer recreated the mapped drives prior to the client being up and
          * running by the main service, we will need to reconnect the Shared Folders
          * drives.
          */
         HgfsServerClientRdrConnectDrives();
      } else {
         NOT_REACHED();
      }
   } else {
      g_debug("VM is not running in a hosted product skip HGFS client redirector initialization.");
   }

   mgrData = g_malloc0(sizeof *mgrData);
   HgfsServerManager_DataInit(mgrData,
                              ctx->name,
                              NULL,       // rpc channel unused
                              NULL);      // no rpc callback

   if (!HgfsServerManager_Register(mgrData)) {
      g_warning("HgfsServer_InitState() failed, canceling HGFS server init.\n");
      g_free(mgrData);
      return NULL;
   }

   {
      RpcChannelCallback rpcs[] = {
         { HGFS_SYNC_REQREP_CMD, HgfsServerRpcDispatch, mgrData, NULL, NULL, 0 }
      };
      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_CAPABILITIES, HgfsServerCapReg, &regData },
         { TOOLS_CORE_SIG_SHUTDOWN, HgfsServerShutdown, &regData }
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) },
         { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
      };

      regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
   }
   regData._private = mgrData;

   return &regData;
}
07070100000371000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/powerOps  07070100000372000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/powerOps/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000373000081A400000000000000000000000168225505000004D6000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/powerOps/Makefile.am  ################################################################################
### Copyright (C) 2009-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libpowerOps.la

libpowerOps_la_CPPFLAGS =
libpowerOps_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libpowerOps_la_LDFLAGS =
libpowerOps_la_LDFLAGS += @PLUGIN_LDFLAGS@

libpowerOps_la_LIBADD =
libpowerOps_la_LIBADD += @VMTOOLS_LIBS@

libpowerOps_la_SOURCES =
libpowerOps_la_SOURCES += powerOps.c

  07070100000374000081A4000000000000000000000001682255050000441D000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/powerOps/powerOps.c   /*********************************************************
 * Copyright (c) 2008-2016, 2018-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file powerOps.c
 *
 * Plugin that handles power operation events from the VMX.
 */

#define G_LOG_DOMAIN "powerops"

#include "vm_assert.h"
#include "vm_basic_defs.h"

#include "conf.h"
#include "guestApp.h"
#include "procMgr.h"
#include "system.h"
#include "vmware/guestrpc/powerops.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/log.h"

#if defined(G_PLATFORM_WIN32)
#  define INVALID_PID NULL
#else
#  define INVALID_PID (GPid) -1
#include <sys/wait.h>
#endif

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif

static const char *stateChgConfNames[] = {
   NULL,
   CONFNAME_POWEROFFSCRIPT,
   CONFNAME_POWEROFFSCRIPT,
   CONFNAME_POWERONSCRIPT,
   CONFNAME_RESUMESCRIPT,
   CONFNAME_SUSPENDSCRIPT,
};


/** Internal plugin state. */
typedef struct PowerOpState {
   GuestOsState         stateChgInProgress;
   GuestOsState         lastFailedStateChg;
#if defined(G_PLATFORM_WIN32)
   ProcMgr_AsyncProc   *pid;
#else
   GPid                 pid;
#endif
   ToolsAppCtx         *ctx;
   gboolean             scriptEnabled[GUESTOS_STATECHANGE_LAST];
} PowerOpState;


/**
 * Returns the capabilities of the power ops plugin.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The application context.
 * @param[in]  set      Whether capabilities are being set.
 * @param[in]  data     Unused.
 *
 * @return List of capabilities.
 */

static GArray *
PowerOpsCapabilityCb(gpointer src,
                     ToolsAppCtx *ctx,
                     gboolean set,
                     gpointer data)
{
   const ToolsAppCapability caps[] = {
      { TOOLS_CAP_OLD_NOVAL, "statechange", 0, 1 },
      { TOOLS_CAP_OLD_NOVAL, "softpowerop_retry", 0, 1 },
   };

   return VMTools_WrapArray(caps, sizeof *caps, ARRAYSIZE(caps));
}


/**
 * Handles power ops-related options.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  option   Option being set.
 * @param[in]  value    Option value.
 * @param[in]  plugin   Plugin registration data.
 *
 * @return TRUE on success.
 */

static gboolean
PowerOpsSetOption(gpointer src,
                  ToolsAppCtx *ctx,
                  const gchar *option,
                  const gchar *value,
                  ToolsPluginData *plugin)
{
   gboolean enabled;
   PowerOpState *state = plugin->_private;

   if (strcmp(value, "1") != 0 && strcmp(value, "0") != 0) {
      return FALSE;
   }

   enabled = (strcmp(value, "1") == 0);

   if (strcmp(option, TOOLSOPTION_SCRIPTS_POWERON) == 0) {
      state->scriptEnabled[GUESTOS_STATECHANGE_POWERON] = enabled;
   } else if (strcmp(option, TOOLSOPTION_SCRIPTS_POWEROFF) == 0) {
      state->scriptEnabled[GUESTOS_STATECHANGE_HALT] =
         state->scriptEnabled[GUESTOS_STATECHANGE_REBOOT] = enabled;
   } else if (strcmp(option, TOOLSOPTION_SCRIPTS_SUSPEND) == 0) {
      state->scriptEnabled[GUESTOS_STATECHANGE_SUSPEND] = enabled;
   } else if (strcmp(option, TOOLSOPTION_SCRIPTS_RESUME) == 0) {
      state->scriptEnabled[GUESTOS_STATECHANGE_RESUME] = enabled;
   } else {
      return FALSE;
   }

   return TRUE;
}


/**
 * Clean up internal state on shutdown.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Unused.
 * @param[in]  plugin   Plugin registration data.
 */

static void
PowerOpsShutdown(gpointer src,
                 ToolsAppCtx *ctx,
                 ToolsPluginData *plugin)
{
   PowerOpState *state = plugin->_private;
   g_free(state);
}


/**
 * Called when a state change script is done running. Sends the state change
 * status to the VMX.
 *
 * @note This may halt/reboot the VM. Also the VMX may suspend the VM upon
 * receipt of a positive status.
 *
 * @param[in]  state       Plugin state.
 * @param[in]  success     Whether the script was successful.
 */

static void
PowerOpsStateChangeDone(PowerOpState *state,
                        gboolean success)
{
   gchar *msg;
   char *reply = NULL;
   size_t repLen = 0;

   g_debug("State change complete, success = %d.\n", success);

   /*
    * We execute the requested action if the script succeeded, or if the
    * same action was tried before but didn't finish due to a script failure.
    * See bug 168568 for discussion.
    */
   if (success || state->lastFailedStateChg == state->stateChgInProgress) {
      success = TRUE;
      state->lastFailedStateChg = GUESTOS_STATECHANGE_NONE;
   }

   if (!success) {
      state->lastFailedStateChg = state->stateChgInProgress;
   }

   /* Send the status message to the VMX. */
   msg = g_strdup_printf("tools.os.statechange.status %d %d",
                         success,
                         state->stateChgInProgress);
   if (!RpcChannel_Send(state->ctx->rpc, msg, strlen(msg) + 1,
                        &reply, &repLen)) {
      g_warning("Unable to send the status RPC. Reply: '%s', Reply len: '%" FMTSZ "u'",
                (reply != NULL) ? reply : "(null)", repLen);
   }

   RpcChannel_Free(reply);
   g_free(msg);

   /* Finally, perform the requested operation. */
   if (success) {
      if (state->stateChgInProgress == GUESTOS_STATECHANGE_REBOOT) {
         VMTools_VmxLog(state->ctx->rpc, "Initiating reboot.");
         System_Shutdown(TRUE);
      } else if (state->stateChgInProgress == GUESTOS_STATECHANGE_HALT) {
         VMTools_VmxLog(state->ctx->rpc, "Initiating halt.");
         System_Shutdown(FALSE);
      }
   }

   state->stateChgInProgress = GUESTOS_STATECHANGE_NONE;
}


#if defined(G_PLATFORM_WIN32)
/**
 * Callback for when the script process finishes on Win32 systems.
 *
 * @param[in]  _state      Plugin state.
 *
 * @return TRUE if the process is not finished yet.
 */

static gboolean
PowerOpsScriptCallback(gpointer _state)
{
   PowerOpState *state = _state;

   ASSERT(state->pid != INVALID_PID);

   if (!ProcMgr_IsAsyncProcRunning(state->pid)) {
      int exitcode;
      gboolean success;

      success = (ProcMgr_GetExitCode(state->pid, &exitcode) == 0 &&
                 exitcode == 0);
      VMTools_VmxLog(state->ctx->rpc, "Script exit code: %d, success = %d",
                     exitcode, success);
      PowerOpsStateChangeDone(state, success);
      ProcMgr_Free(state->pid);
      state->pid = INVALID_PID;
      return FALSE;
   }
   return TRUE;
}


/**
 * Starts a process using the ProcMgr library. For some reason the glib
 * spawn functions are a pain to use under Windows, and ProcMgr works
 * fine since we can use the "selectable" handle to monitor the child
 * process.
 *
 * XXX: as soon as I figure out what's wrong with the "gspawn-win32-helper"
 * process and why it's not working, this should probably be merged with the
 * POSIX code below.
 *
 * @param[in]  state    Plugin state.
 * @param[in]  script   Path to the script to be run.
 *
 * @return Whether started the process successfully.
 */

static gboolean
PowerOpsRunScript(PowerOpState *state,
                  gchar *script)
{
   gchar *quoted = NULL;
   ProcMgr_ProcArgs procArgs;

   /*
    * Pass the CREATE_NO_WINDOW flag to CreateProcess so that the
    * cmd.exe window will not be visible to the user in the guest.
    */
   memset(&procArgs, 0, sizeof procArgs);
   procArgs.bInheritHandles = TRUE;
   procArgs.dwCreationFlags = CREATE_NO_WINDOW;

   /* Quote the path if it's not yet quoted. */
   if (script[0] != '"') {
      quoted = g_strdup_printf("\"%s\"", script);
   }

   g_message("Executing script: %s\n", (quoted != NULL) ? quoted : script);
   state->pid = ProcMgr_ExecAsync((quoted != NULL) ? quoted : script, &procArgs);
   g_free(quoted);

   if (state->pid != NULL) {
      HANDLE h = ProcMgr_GetAsyncProcSelectable(state->pid);
      GSource *watch = VMTools_NewHandleSource(h);
      VMTOOLSAPP_ATTACH_SOURCE(state->ctx, watch, PowerOpsScriptCallback, state, NULL);
      g_source_unref(watch);
      return TRUE;
   } else {
      g_warning("Failed to start script: out of memory?\n");
      return FALSE;
   }
}

#else

/**
 * Callback for when the script process finishes on POSIX systems.
 *
 * @param[in]  pid         Child pid.
 * @param[in]  exitStatus  Exit status of script.
 * @param[in]  _state      Plugin state.
 *
 * @return FALSE.
 */

static gboolean
PowerOpsScriptCallback(GPid pid,
                       gint exitStatus,
                       gpointer _state)
{
   PowerOpState *state = _state;
   gboolean success = exitStatus == 0;

   ASSERT(state->pid != INVALID_PID);

   if (WIFEXITED(exitStatus)) {
      VMTools_VmxLog(state->ctx->rpc, "Script exit code: %d, success = %d",
                     WEXITSTATUS(exitStatus), success);
   } else if (WIFSIGNALED(exitStatus)) {
      VMTools_VmxLog(state->ctx->rpc, "Script canceled by signal: %d, "
                     "success = %d", WTERMSIG(exitStatus), success);
   } else if (WIFSTOPPED(exitStatus)) {
      VMTools_VmxLog(state->ctx->rpc, "Script stopped by signal: %d, "
                     "success = %d", WSTOPSIG(exitStatus), success);
   } else {
      VMTools_VmxLog(state->ctx->rpc, "Script exit status: %d, success = %d",
                     exitStatus, success);
   }
   PowerOpsStateChangeDone(_state, success);
   g_spawn_close_pid(state->pid);
   state->pid = INVALID_PID;
   return FALSE;
}


/**
 * Starts a process using glib. This works better in the non-Windows case,
 * since glib provides a nice API for monitoring when a process exits
 * (instead of having to poll the process every once in a while when using
 * ProcMgr.)
 *
 * @param[in]  state    Plugin state.
 * @param[in]  script   Path to the script to be run.
 *
 * @return Whether started the process successfully.
 */

static gboolean
PowerOpsRunScript(PowerOpState *state,
                  gchar *script)
{
   gchar *argv[2];
   GSource *watch;
   GError *err = NULL;

   argv[0] = g_locale_from_utf8(script, -1, NULL, NULL, &err);
   if (err != NULL) {
      g_debug("Conversion error: %s\n", err->message);
      g_clear_error(&err);
      /*
       * If we could not convert to current locate let's hope that
       * what we have is a useable script name and use it directly.
       */
      argv[0] = g_strdup(script);
   }
   argv[1] = NULL;

   g_message("Executing script: '%s'\n", script);
   if (!g_spawn_async(NULL,
                      argv,
                      NULL,
                      G_SPAWN_DO_NOT_REAP_CHILD |
                      G_SPAWN_STDOUT_TO_DEV_NULL |
                      G_SPAWN_STDERR_TO_DEV_NULL,
                      NULL,
                      NULL,
                      &state->pid,
                      &err)) {
         g_warning("Error starting script: %s\n", err->message);
         g_clear_error(&err);
         g_free(argv[0]);
         return FALSE;
   }

   /* Setup a watch for when the child is done. */
   watch = g_child_watch_source_new(state->pid);
   VMTOOLSAPP_ATTACH_SOURCE(state->ctx, watch, PowerOpsScriptCallback, state, NULL);
   g_source_unref(watch);
   g_free(argv[0]);
   return TRUE;
}
#endif


/**
 * Handler for commands which invoke state change scripts. Runs the configured
 * script for the power operation signaled by the host.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE on success.
 */

static gboolean
PowerOpsStateChange(RpcInData *data)
{
   size_t i;
   PowerOpState *state = data->clientData;

   if (state->pid != INVALID_PID) {
      g_message("State change already in progress: %d, requested = '%s'\n",
                state->stateChgInProgress,
                data->name);
      return RPCIN_SETRETVALS(data, "State change already in progress", FALSE);
   }

   g_debug("State change: %s\n", data->name);

   for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) {
      if (strcmp(data->name, stateChangeCmdTable[i].tcloCmd) == 0) {
         gchar *script;
         const char *result;
         const char *confName;
         Bool ret;

         state->stateChgInProgress = stateChangeCmdTable[i].id;

         /* Check for the toolScripts option. */
         if (!state->scriptEnabled[stateChangeCmdTable[i].id]) {
            PowerOpsStateChangeDone(state, TRUE);
            g_debug("Script for %s not configured to run\n",
                    stateChangeCmdTable[i].tcloCmd);
            return RPCIN_SETRETVALS(data, "", TRUE);
         }

         confName = stateChgConfNames[stateChangeCmdTable[i].id];
         script = g_key_file_get_string(state->ctx->config,
                                        "powerops",
                                        confName,
                                        NULL);

         if (script == NULL) {
            /* Use default script if not set in config file. */
            const char *dfltScript = GuestApp_GetDefaultScript(confName);
            if (dfltScript == NULL) {
               g_debug("No default script to run for state change %s.\n",
                       stateChangeCmdTable[i].name);
               PowerOpsStateChangeDone(state, TRUE);
               return RPCIN_SETRETVALS(data, "", TRUE);
            }
            script = g_strdup(dfltScript);
         } else if (strlen(script) == 0) {
            g_debug("No script to run for state change %s.\n",
                    stateChangeCmdTable[i].name);
            g_free(script);
            PowerOpsStateChangeDone(state, TRUE);
            return RPCIN_SETRETVALS(data, "", TRUE);
         }

         /* If script path is not absolute, assume the Tools install path. */
         if (!g_path_is_absolute(script)) {
            char *dfltPath;
            char *tmp;

            dfltPath = GuestApp_GetInstallPath();
            ASSERT(dfltPath != NULL);

            /*
             * Before the switch to vmtoolsd, the config file was saved with
             * quotes around the script path to make the old VMware dict code
             * happy. Now we need to undo that when modifying the script path.
             *
             * PowerOpsRunScript will "re-quote" the script path.
             */
            if (script[0] == '"') {
                script[strlen(script) - 1] = '\0';
                tmp = g_strdup_printf("%s%c%s", dfltPath, DIRSEPC, script + 1);
            } else {
               tmp = g_strdup_printf("%s%c%s", dfltPath, DIRSEPC, script);
            }

            g_free(script);
            vm_free(dfltPath);
            script = tmp;
         }

         VMTools_VmxLog(state->ctx->rpc, "Executing script for state change "
                        "'%s'.", stateChangeCmdTable[i].tcloCmd);
         if (PowerOpsRunScript(state, script)) {
            result = "";
            ret = TRUE;
         } else {
            PowerOpsStateChangeDone(state, FALSE);
            result = "Error starting script";
            ret = FALSE;
         }

         g_free(script);
         return RPCIN_SETRETVALS(data, (char *) result, ret);
      }
   }

   g_warning("Invalid state change command.\n");
   return RPCIN_SETRETVALS(data, "Invalid state change command", FALSE);
}


/**
 * Plugin entry point. Returns the registration data.
 *
 * @param[in]  ctx   Unused.
 *
 * @return The registration data.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "powerops",
      NULL,
      NULL
   };

   size_t i;
   PowerOpState *state;

   ToolsPluginSignalCb sigs[] = {
      { TOOLS_CORE_SIG_CAPABILITIES, PowerOpsCapabilityCb, NULL },
      { TOOLS_CORE_SIG_SET_OPTION, PowerOpsSetOption, &regData },
      { TOOLS_CORE_SIG_SHUTDOWN, PowerOpsShutdown, &regData }
   };
   ToolsAppReg regs[] = {
      { TOOLS_APP_GUESTRPC, NULL },
      { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
   };

   state = g_malloc0(sizeof *state);
   state->ctx = ctx;
   state->pid = INVALID_PID;

   for (i = 0; i < GUESTOS_STATECHANGE_LAST; i++) {
      state->scriptEnabled[i] = TRUE;
   }

   regs[0].data = g_array_sized_new(FALSE,
                                    TRUE,
                                    sizeof (RpcChannelCallback),
                                    ARRAYSIZE(stateChangeCmdTable));

   for (i = 0; i < ARRAYSIZE(stateChangeCmdTable); i++) {
      RpcChannelCallback cb = { stateChangeCmdTable[i].tcloCmd,
                                PowerOpsStateChange,
                                state, NULL, NULL, 0 };
      g_array_append_val(regs[0].data, cb);
   }

   regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
   regData._private = state;
   return &regData;
}

   07070100000375000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionKMS 07070100000376000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionKMS/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000377000081A4000000000000000000000001682255050000065F000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionKMS/Makefile.am ################################################################################
### Copyright (C) 2009-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libresolutionKMS.la

libresolutionKMS_la_CPPFLAGS =
libresolutionKMS_la_CPPFLAGS += @LIBUDEV_CFLAGS@
libresolutionKMS_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libresolutionKMS_la_LDFLAGS =
libresolutionKMS_la_LDFLAGS += @PLUGIN_LDFLAGS@

libresolutionKMS_la_LIBADD =
libresolutionKMS_la_LIBADD += @VMTOOLS_LIBS@
libresolutionKMS_la_LIBADD += @RESOLUTIONSET_LIBADD@
libresolutionKMS_la_LIBADD += $(top_builddir)/services/plugins/resolutionSet/libresolutionCommon.la

libresolutionKMS_la_SOURCES =
libresolutionKMS_la_SOURCES += resolutionKMS.c

libresolutionKMS_la_DEPENDENCIES =
libresolutionKMS_la_DEPENDENCIES += $(top_builddir)/services/plugins/resolutionSet/libresolutionCommon.la
 07070100000378000081A40000000000000000000000016822550500003779000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionKMS/resolutionKMS.c /*********************************************************
 * Copyright (C) 2008-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file resolutionKMS.c --
 *
 * Plugin to communicate GUI topology to the vmwgfx drm device through a
 * control node. This file is a modified version of resolutionSet.c
 */

#define G_LOG_DOMAIN "resolutionKMS"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "vmware.h"
#include "strutil.h"

#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"
#include "../resolutionSet/resolutionCommon.h"
#include "../resolutionSet/resolutionDL.h"

#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);

/*
 * The maximum number of capabilities we can set.
 *
 * See ResolutionSetCapabilities().
 */
#define RESOLUTION_SET_CAPABILITIES_MAX 5

/*
 * Global information about the communication state
 */
typedef struct {
   gboolean initialized;       // Whether the plugin is already initialized.
   int fd;                     // File descriptor to the DRM device.
} KMSInfoType;

/*
 * Internal global variables
 */
KMSInfoType kmsInfo;

/*
 * The name of the RPC channel we're using, e.g. TOOLS_DAEMON_NAME. Used by
 * ResolutionSet_SetServerCapability() to determine which capability to set.
 */
static const char *rpcChannelName = NULL;

/*
 *-----------------------------------------------------------------------------
 *
 * ResolutionWriteToKernel --
 *
 *     Write GUI topology info to the drm device.
 *
 * Results:
 *     TRUE on success, FALSE on failure.
 *
 * Side effects:
 *     The drm device will send an uevent and expose the new
 *     topology.
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
ResolutionWriteToKernel(const struct drm_vmw_rect *rects, // IN: Screen rects
			unsigned int num_rects)           // IN: Number of
                                                          // rects
{
   struct drm_vmw_update_layout_arg arg;
   int ret;

   memset(&arg, 0, sizeof arg);
   arg.num_outputs = num_rects;
   arg.rects = (unsigned long) rects;

   ret = drmCommandWrite(kmsInfo.fd, DRM_VMW_UPDATE_LAYOUT, &arg, sizeof arg);
   if (ret < 0) {
      g_debug("%s: FAIL! Resolutionset write to kernel failed: %d\n",
              __FUNCTION__, ret);
      return FALSE;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ResolutionResolutionSetCB --
 *
 *     Handler for TCLO 'Resolution_Set'.
 *     Routine unmarshals RPC arguments and passes over to back-end
 *     ResolutionWriteToKernel().
 *
 * Results:
 *     TRUE if we can reply, FALSE otherwise.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
ResolutionResolutionSetCB(RpcInData *data) // IN: The RPC data
{
   struct drm_vmw_rect rect;
   unsigned int index = 0;
   gboolean retval = FALSE;

   if (!kmsInfo.initialized) {
      g_debug("%s: FAIL! Request for resolution set but plugin is not initialized\n",
              __FUNCTION__);
      return RPCIN_SETRETVALS(data, "Invalid guest state: resolution set not initialized", FALSE);
   }

   rect.x = 0;
   rect.y = 0;

   /* parse the width and height */
   if (!StrUtil_GetNextUintToken(&rect.w, &index, data->args, " ")) {
      goto invalid_arguments;
   }
   if (!StrUtil_GetNextUintToken(&rect.h, &index, data->args, "")) {
      goto invalid_arguments;
   }

   retval = ResolutionWriteToKernel(&rect, 1);

invalid_arguments:
   return RPCIN_SETRETVALS(data, retval ? "" : "Invalid arguments", retval);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ResolutionDisplayTopologySetCB --
 *
 *     Handler for TCLO 'DisplayTopology_Set'.
 *     Routine unmarshals RPC arguments and passes over to back-end
 *     ResolutionWriteToKernel().
 *
 * Results:
 *     TRUE if we can reply, FALSE otherwise.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
ResolutionDisplayTopologySetCB(RpcInData *data)
{
   struct drm_vmw_rect *rects = NULL;
   unsigned int count, i;
   gboolean success = FALSE;
   const char *p;

   if (!kmsInfo.initialized) {
      g_debug("%s: FAIL! Request for topology set but plugin is not initialized\n",
              __FUNCTION__);
      RPCIN_SETRETVALS(data, "Invalid guest state: topology set not initialized", FALSE);
      goto out;
   }

   /*
    * The argument string will look something like:
    *   <count> [ , <x> <y> <w> <h> ] * count.
    *
    * e.g.
    *    3 , 0 0 640 480 , 640 0 800 600 , 0 480 640 480
    */

   if (sscanf(data->args, "%u", &count) != 1) {
      return RPCIN_SETRETVALS(data,
                              "Invalid arguments. Expected \"count\"",
                              FALSE);
   }

   rects = malloc(sizeof *rects * count);
   if (!rects) {
      RPCIN_SETRETVALS(data,
                       "Failed to alloc buffer for display info",
                       FALSE);
      return FALSE;
   }

   for (p = data->args, i = 0; i < count; i++) {
      p = strchr(p, ',');
      if (!p) {
         RPCIN_SETRETVALS(data,
                          "Expected comma separated display list",
                          FALSE);
         goto out;
      }
      p++; /* Skip past the , */

      if (sscanf(p, " %d %d %d %d ", &rects[i].x,
                 &rects[i].y, &rects[i].w, &rects[i].h) != 4) {
         RPCIN_SETRETVALS(data,
                          "Expected x, y, w, h in display entry",
                          FALSE);
         goto out;
      }
   }

   success = ResolutionWriteToKernel(rects, count);
   RPCIN_SETRETVALS(data, success ? "" : "ResolutionSetTopology failed", success);
out:
   free(rects);
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ResolutionKMSServerCapability --
 *
 * Sends the tools.capability.resolution_server RPC to the VMX.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static void
ResolutionKMSServerCapability(RpcChannel *chan,   // IN: The RPC channel.
                              unsigned int value) // IN: The value to send for
                                                  // the capability bit.
{
   gchar *msg;

   if (!rpcChannelName) {
      g_debug("Channel name is null, RPC not sent.\n");
      return;
   }

   msg = g_strdup_printf("tools.capability.resolution_server %s %d",
			 rpcChannelName,
			 value);
   if (!RpcChannel_Send(chan, msg, strlen(msg), NULL, NULL)) {
      g_warning("%s: Unable to set tools.capability.resolution_server\n",
                __FUNCTION__);
   }

   if (value == 1) {
      /*
       * Whenever resolutionKMS is enabled, send
       * "tools.capability.resolution_server toolbox-dnd 0" to clear
       * resolutionSet as resolution server.
       *
       * Note: The below rpc is sent to TOOLS_DND_NAME if rpcChannelName is
       * TOOLS_DAEMON_NAME and vice versa (to clear the opposite channel).
       * This is how rpcChannelName is selected in ToolsOnLoad():
       *
       *    if (strcmp(ctx->name, VMTOOLS_GUEST_SERVICE) == 0) {
       *       rpcChannelName = TOOLS_DAEMON_NAME;
       *    } else if (strcmp(ctx->name, VMTOOLS_USER_SERVICE) == 0) {
       *       rpcChannelName = TOOLS_DND_NAME;
       *    }
       */
      gchar *msgClear;
      msgClear = g_strdup_printf("tools.capability.resolution_server %s 0",
                                 (strcmp(rpcChannelName, TOOLS_DAEMON_NAME) == 0 ?
                                 TOOLS_DND_NAME : TOOLS_DAEMON_NAME));
      if (!RpcChannel_Send(chan, msgClear, strlen(msgClear), NULL, NULL)) {
         g_warning("%s: Unable to clear tools.capability.resolution_server\n",
                   __FUNCTION__);
      }
      g_free(msgClear);
   }

   g_free(msg);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ResolutionKMSShutdown --
 *
 * Cleans up internal data on shutdown.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static void
ResolutionKMSShutdown(gpointer src,     // IN: Unused.
                      ToolsAppCtx *ctx, // IN: The app context.
                      gpointer data)    // IN: Unused.
{
   if (kmsInfo.initialized && ctx && ctx->rpc && ctx->isVMware) {
      ResolutionKMSServerCapability(ctx->rpc, 0);
   }

   if (kmsInfo.initialized) {
      resolutionDRMClose(kmsInfo.fd);
      kmsInfo.initialized = FALSE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * ResolutionKMSCapabilities --
 *
 * Cleans up internal data on shutdown.
 *
 * Results:
 *     An array of capabilities
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */

static GArray *
ResolutionKMSCapabilities(gpointer src,     // IN: The source object.
                          ToolsAppCtx *ctx, // IN: The app context.
                          gboolean set,     // IN: Whether setting or unsetting
			                    // the capability.
                          gpointer data)    // Unused.
{
   /* The array of capabilities to return to the tools service. */
   ToolsAppCapability capabilityArray[RESOLUTION_SET_CAPABILITIES_MAX];

   /* The next unused entry in the capabilities array. */
   unsigned int capabilityCount = 0;

   g_debug("%s: enter\n", __FUNCTION__);

   /*
    * We must register display_topology_set before resolution_set to avoid
    * a race condition in the host. See bug 472343.
    */

   /*
    * We use a value of '2' here because, for historical reasons, the
    * Workstation/Fusion UI will treat a value of 1 for this capability
    * as unsupported. See bug 149541.
    */
   capabilityArray[capabilityCount].type  = TOOLS_CAP_OLD;
   capabilityArray[capabilityCount].name  = "display_topology_set";
   capabilityArray[capabilityCount].index = 0;
   capabilityArray[capabilityCount].value = set ? 2 : 0;
   capabilityCount++;

   capabilityArray[capabilityCount].type  = TOOLS_CAP_OLD;
   capabilityArray[capabilityCount].name  = "display_global_offset";
   capabilityArray[capabilityCount].index = 0;
   capabilityArray[capabilityCount].value = set ? 1 : 0;
   capabilityCount++;

   capabilityArray[capabilityCount].type  = TOOLS_CAP_OLD;
   capabilityArray[capabilityCount].name  = "resolution_set";
   capabilityArray[capabilityCount].index = 0;
   capabilityArray[capabilityCount].value = set ? 1 : 0;
   capabilityCount++;

      /*
       * Send the resolution_server RPC to the VMX.
       *
       * XXX: We need to send this ourselves, instead of including it in the
       *      capability array, because the resolution_server RPC includes the
       *      name of the RPC channel that the VMX should use when sending
       *      resolution set RPCs as an argument.
       */

   if (kmsInfo.initialized && ctx && ctx->rpc && ctx->isVMware) {
      ResolutionKMSServerCapability(ctx->rpc, set ? 1:0);
   }

   ASSERT(capabilityCount <= RESOLUTION_SET_CAPABILITIES_MAX);

   return VMTools_WrapArray(capabilityArray,
                            sizeof *capabilityArray,
                            capabilityCount);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsOnLoad --
 *
 * Plugin entry point
 *
 * Results:
 *     The registration data.
 *
 * Side effects:
 *     Initializes internal plugin state.
 *
 *-----------------------------------------------------------------------------
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   RpcChannelCallback rpcs[] = {
      { "Resolution_Set",               &ResolutionResolutionSetCB },
      { "DisplayTopology_Set",          &ResolutionDisplayTopologySetCB },
   };

   static ToolsPluginData regData = {
      "resolutionKMS",
      NULL,
      NULL
   };

   ToolsPluginSignalCb sigs[] = {
      { TOOLS_CORE_SIG_CAPABILITIES, ResolutionKMSCapabilities, &regData },
      { TOOLS_CORE_SIG_SHUTDOWN, ResolutionKMSShutdown, &regData }
   };

   ToolsAppReg regs[] = {
      { TOOLS_APP_GUESTRPC, NULL },
      { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
   };

   /*
    * If we aren't running in a VM (e.g., running in bootcamp natively on
    * a Mac), then just return NULL.
    */
   if (!ctx->isVMware) {
      return NULL;
   }

   kmsInfo.fd = resolutionCheckForKMS(ctx);
   if (kmsInfo.fd < 0) {
      return NULL;
   }
   kmsInfo.initialized = TRUE;

   /*
    * Save the RPC channel name from the ToolsAppCtx so that we can use it later
    * in calls to ResolutionKMSServerCapability().
    */

   if (strcmp(ctx->name, VMTOOLS_GUEST_SERVICE) == 0) {
      rpcChannelName = TOOLS_DAEMON_NAME;
   } else if (strcmp(ctx->name, VMTOOLS_USER_SERVICE) == 0) {
      rpcChannelName = TOOLS_DND_NAME;
   } else {
      NOT_REACHED();
   }

   regs[0].data = VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs));
   regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
   return &regData;
}
   07070100000379000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet 0707010000037A000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   0707010000037B000081A40000000000000000000000016822550500000916000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/Makefile.am ################################################################################
### Copyright (C) 2009-2017 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @VMUSR_PLUGIN_INSTALLDIR@
noinst_LTLIBRARIES = libresolutionCommon.la

if HAVE_X11
plugin_LTLIBRARIES = libresolutionSet.la

libresolutionSet_la_CPPFLAGS =
libresolutionSet_la_CPPFLAGS += @GTK_CPPFLAGS@
libresolutionSet_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libresolutionSet_la_CPPFLAGS += -DRESOLUTION_X11

libresolutionSet_la_LDFLAGS =
libresolutionSet_la_LDFLAGS += @PLUGIN_LDFLAGS@

libresolutionSet_la_LIBADD =
libresolutionSet_la_LIBADD += @COMMON_XLIBS@
libresolutionSet_la_LIBADD += @GTK_LIBS@
libresolutionSet_la_LIBADD += @VMTOOLS_LIBS@
libresolutionSet_la_LIBADD += @RESOLUTIONSET_LIBADD@
libresolutionSet_la_LIBADD += libresolutionCommon.la

libresolutionSet_la_SOURCES =
libresolutionSet_la_SOURCES += libvmwarectrl.c
libresolutionSet_la_SOURCES += resolutionSet.c
libresolutionSet_la_SOURCES += resolutionX11.c
libresolutionSet_la_SOURCES += resolutionRandR12.c
endif

libresolutionCommon_la_CPPFLAGS =
libresolutionCommon_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libresolutionCommon_la_CPPFLAGS += @LIBUDEV_CFLAGS@

libresolutionCommon_la_SOURCES =
libresolutionCommon_la_SOURCES += resolutionCommon.c
libresolutionCommon_la_SOURCES += resolutionCommon.h
libresolutionCommon_la_SOURCES += resolutionDL.c
libresolutionCommon_la_SOURCES += resolutionDL.h
libresolutionCommon_la_LDFLAGS =
libresolutionCommon_la_LDFLAGS += @PLUGIN_LDFLAGS@

libresolutionCommon_la_LIBADD =
libresolutionCommon_la_LIBADD += @LIBUDEV_LIBS@
  0707010000037C000081A40000000000000000000000016822550500001D36000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/libvmwarectrl.c /*
 * Copyright 2006 by VMware, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the copyright holder(s)
 * and author(s) shall not be used in advertising or otherwise to promote
 * the sale, use or other dealings in this Software without prior written
 * authorization from the copyright holder(s) and author(s).
 */

/*
 * libvmwarectrl.c --
 *
 *      The VMWARE_CTRL client library.
 */


#define NEED_EVENTS
#define NEED_REPLIES
#include <X11/Xlibint.h>
#include "libvmwarectrl.h"
#include "vmwarectrlproto.h"
#include <X11/extensions/Xext.h>
#include <X11/extensions/extutil.h>


/*
 * Static data and functions.
 */
 
static XExtensionInfo _vmwarectrl_info_data;
static XExtensionInfo *vmwarectrl_info = &_vmwarectrl_info_data;
static char *vmwarectrl_extension_name = VMWARE_CTRL_PROTOCOL_NAME;

#define VMwareCtrlCheckExtension(dpy, i, val) \
  XextCheckExtension(dpy, i, vmwarectrl_extension_name, val)

static int close_display(Display *dpy, XExtCodes *codes);

static /* const */ XExtensionHooks vmwarectrl_extension_hooks = {
    NULL,          /* create_gc */
    NULL,          /* copy_gc */
    NULL,          /* flush_gc */
    NULL,          /* free_gc */
    NULL,          /* create_font */
    NULL,          /* free_font */
    close_display, /* close_display */
    NULL,          /* wire_to_event */
    NULL,          /* event_to_wire */
    NULL,          /* error */
    NULL,          /* error_string */
};

static XEXT_GENERATE_CLOSE_DISPLAY (close_display, vmwarectrl_info)

static XEXT_GENERATE_FIND_DISPLAY (find_display, vmwarectrl_info,
				   vmwarectrl_extension_name,
				   &vmwarectrl_extension_hooks,
				   0, NULL)

/*
 *----------------------------------------------------------------------------
 *
 * VMwareCtrl_QueryExtension --
 *
 *      Standard QueryExtension implementation. Not very interesting for
 *      VMWARE_CTRL as it doesn't define any events or errors.
 *
 * Results:
 *      True if information is successfully retrieved. False otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
VMwareCtrl_QueryExtension(Display *dpy,     // IN:
                          int *event_basep, // OUT:
                          int *error_basep) // OUT:
{
   XExtDisplayInfo *info = find_display(dpy);

   if (XextHasExtension(info)) {
      *event_basep = info->codes->first_event;
      *error_basep = info->codes->first_error;
      return True;
   } else {
      return False;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * VMwareCtrl_QueryVersion --
 *
 *      Send the QueryVersion command to the driver and return the results.
 *
 * Results:
 *      True if information is successfully retrieved. False otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
VMwareCtrl_QueryVersion(Display *dpy,      // IN:
                        int *majorVersion, // OUT:
                        int *minorVersion) // OUT:
{
   xVMwareCtrlQueryVersionReply rep;
   xVMwareCtrlQueryVersionReq *req;
   XExtDisplayInfo *info = find_display(dpy);
   Bool ret = False;

   VMwareCtrlCheckExtension(dpy, info, False);
   LockDisplay(dpy);

   GetReq(VMwareCtrlQueryVersion, req);
   req->reqType = info->codes->major_opcode;
   req->VMwareCtrlReqType = X_VMwareCtrlQueryVersion;

   if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
      goto exit;
   }
   *majorVersion = rep.majorVersion;
   *minorVersion = rep.minorVersion;

   ret = True;

exit:
   UnlockDisplay(dpy);
   SyncHandle();

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMwareCtrl_SetRes --
 *
 *      Send the SetRes command to the driver.
 *
 * Results:
 *      True if the resolution was set. False otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

Bool
VMwareCtrl_SetRes(Display *dpy, // IN:
                  int screen,   // IN:
                  int x,        // IN:
                  int y)        // IN:
{
   xVMwareCtrlSetResReply rep;
   xVMwareCtrlSetResReq *req;
   XExtDisplayInfo *info = find_display(dpy);
   Bool ret = False;

   VMwareCtrlCheckExtension(dpy, info, False);
   LockDisplay(dpy);

   GetReq(VMwareCtrlSetRes, req);
   req->reqType = info->codes->major_opcode;
   req->VMwareCtrlReqType = X_VMwareCtrlSetRes;
   req->screen = screen;
   req->x = x;
   req->y = y;

   if (!_XReply(dpy, (xReply *)&rep,
                (SIZEOF(xVMwareCtrlSetResReply) - SIZEOF(xReply)) >> 2,
                xFalse)) {
      goto exit;
   }

   ret = True;

exit:
   UnlockDisplay(dpy);
   SyncHandle();

   return ret;
}


/*
 *----------------------------------------------------------------------------
 *
 * VMwareCtrl_SetTopology --
 *
 *      Send the SetTopology command to the driver.
 *
 *      Solaris 10 uses a different Xinerama standard than expected here. As a
 *      result, topology set is not supported and this function is excluded from
 *      Solaris builds.
 *
 * Results:
 *      True if the resolution and xinerama topology were set. False otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------------
 */

#ifndef NO_MULTIMON
Bool
VMwareCtrl_SetTopology(Display *dpy,          // IN:
                       int screen,            // IN:
                       xXineramaScreenInfo extents[], // IN:
                       int number)            // IN:
{
   xVMwareCtrlSetTopologyReply rep;
   xVMwareCtrlSetTopologyReq *req;
   XExtDisplayInfo *info = find_display(dpy);
   Bool ret = False;
   long len;

   VMwareCtrlCheckExtension(dpy, info, False);
   LockDisplay(dpy);

   GetReq(VMwareCtrlSetTopology, req);
   req->reqType = info->codes->major_opcode;
   req->VMwareCtrlReqType = X_VMwareCtrlSetTopology;
   req->screen = screen;
   req->number = number;

   len = ((long) number) << 1;
   SetReqLen(req, len, len);
   len <<= 2;
   _XSend(dpy, (char *)extents, len);

   if (!_XReply(dpy, (xReply *)&rep,
                (SIZEOF(xVMwareCtrlSetTopologyReply) - SIZEOF(xReply)) >> 2,
                xFalse)) {
      goto exit;
   }

   ret = True;

exit:
   UnlockDisplay(dpy);
   SyncHandle();

   return ret;
}
#endif
  0707010000037D000081A400000000000000000000000168225505000007EF000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/libvmwarectrl.h /*
 * Copyright 2006 by VMware, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the copyright holder(s)
 * and author(s) shall not be used in advertising or otherwise to promote
 * the sale, use or other dealings in this Software without prior written
 * authorization from the copyright holder(s) and author(s).
 */

/*
 * libvmwarectrl.c --
 *
 *      The VMWARE_CTRL client library.
 */


#ifndef _LIB_VMWARE_CTRL_H_
#define _LIB_VMWARE_CTRL_H_

#include <X11/X.h>
#include <X11/Xmd.h>
#ifndef NO_MULTIMON
#include <X11/extensions/panoramiXproto.h>
#endif

Bool VMwareCtrl_QueryExtension(Display *dpy, int *event_basep, int *error_basep);
Bool VMwareCtrl_QueryVersion(Display *dpy, int *majorVersion, int *minorVersion);
Bool VMwareCtrl_SetRes(Display *dpy, int screen, int x, int y);
#ifndef  NO_MULTIMON
Bool VMwareCtrl_SetTopology(Display *dpy, int screen, xXineramaScreenInfo[], int number);
#endif

#endif /* _LIB_VMWARE_CTRL_H_ */
 0707010000037E000081A400000000000000000000000168225505000024D8000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.c  /*********************************************************
 * Copyright (c) 2016-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/
/* Authors:
 * Thomas Hellstrom <thellstrom@vmware.com>
 */

#define G_LOG_DOMAIN "resolutionCommon"

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include "resolutionDL.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"
#include "resolutionCommon.h"

#ifdef ENABLE_RESOLUTIONKMS

/* The DRM device we are looking for */
#define RESOLUTION_VENDOR     "0x15ad"
#define RESOLUTION_SVGA2_DEVICE     "0x0405"
#define RESOLUTION_SVGA3_DEVICE     "0x0406"
#define RESOLUTION_KERNELNAME "vmwgfx"

/* Required DRM version for resolutionKMS */
#define RESOLUTION_DRM_MAJOR  2
#define RESOLUTION_DRM_MINOR  14


/*
 *-----------------------------------------------------------------------------
 *
 * resolutionOpenDRM --
 *
 *     Opens a file descriptor on the indicated node to the first SVGA2 device.
 *
 * Results:
 *     Returns a positive file descriptor on success. Otherwise returns -1.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */
static int
resolutionOpenDRM(const char *node) // IN: Device node base name.
{
    struct udev *udev;
    struct udev_enumerate *enumerate;
    struct udev_list_entry *devices, *devListEntry;
    struct udev_device *dev;
    int fd = -1;
    int drmFd;
    const char *devNode = NULL;

    /* Force load the kernel module. */
    drmFd = drmOpen(RESOLUTION_KERNELNAME, NULL);
    if (drmFd >= 0) {
       (void) drmDropMaster(drmFd);
    }

    udev = udev_new();
    if (!udev) {
        goto outNoUdev;
    }

    /*
     * Udev error return codes that are not caught immediately are
     * typically caught in the input argument check in the udev
     * function calls following the failing call!
     */
    enumerate = udev_enumerate_new(udev);
    if (udev_enumerate_add_match_subsystem(enumerate, "drm"))
       goto outErr;
    if (udev_enumerate_add_match_property(enumerate, "DEVTYPE", "drm_minor"))
       goto outErr;
    if (udev_enumerate_scan_devices(enumerate))
       goto outErr;

    devices = udev_enumerate_get_list_entry(enumerate);
    udev_list_entry_foreach(devListEntry, devices) {
       const char *path, *vendor, *device;
       struct udev_device *parent;

       path = udev_list_entry_get_name(devListEntry);
       if (!path)
          continue;
       if (!strstr(path, node))
          continue;

       dev = udev_device_new_from_syspath(udev, path);
       if (!dev)
          goto outErr;

       parent = udev_device_get_parent_with_subsystem_devtype(dev,
                                                              "pci",
                                                              NULL);
       if (!parent)
          goto skipCheck;

       vendor = udev_device_get_sysattr_value(parent, "vendor");
       device = udev_device_get_sysattr_value(parent, "device");
       if (!vendor || !device)
          goto skipCheck;

       if (strcmp(vendor, RESOLUTION_VENDOR) != 0 ||
           (strcmp(device, RESOLUTION_SVGA2_DEVICE) != 0 &&
             strcmp(device, RESOLUTION_SVGA3_DEVICE) != 0))
          goto skipCheck;

       devNode = udev_device_get_devnode(dev);
       if (!devNode)
          goto outFound;

       fd = open(devNode, O_RDWR);
       udev_device_unref(dev);
       break;

skipCheck:
       udev_device_unref(dev);
    }

    udev_enumerate_unref(enumerate);
    udev_unref(udev);

    if (drmFd >= 0) {
       drmClose(drmFd);
    }

    return fd;

  outFound:
    udev_device_unref(dev);
  outErr:
    udev_enumerate_unref(enumerate);
    udev_unref(udev);
  outNoUdev:
    if (drmFd >= 0) {
       drmClose(drmFd);
    }

    return -1;
}

/*
 *-----------------------------------------------------------------------------
 *
 * resolutionDRMCheckVersion --
 *
 *     Check that the drm version supports GUI topology communication.
 *
 *     Checks that the DRM device supports setting GUI topology from the
 *     control node, and also that the topology is communicated on the
 *     modesetting connectors.
 *
 * Results:
 *     0 if DRM device is usable for communicating GUI topology.
 *     -1 otherwise.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */
static int
resolutionDRMCheckVersion(int fd)  // IN: An open DRM file descriptor.
{
   drmVersionPtr ver = drmGetVersion(fd);

   if (!ver) {
      g_debug("%s: Failed to get DRM version.\n", __func__);
      return -1;
   }

   if (ver->version_major != RESOLUTION_DRM_MAJOR ||
       ver->version_minor < RESOLUTION_DRM_MINOR) {
      g_debug("%s: Insufficient DRM version %d.%d for resolutionKMS.\n",
              __func__, ver->version_major, ver->version_minor);
      drmFreeVersion(ver);
      return -1;
   }
   g_debug("%s: DRM version %d.%d.\n",
           __func__, ver->version_major, ver->version_minor);

   drmFreeVersion(ver);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * resolutionDRMRPrimaryCheckOpen --
 *
 *     First tries to open a render node to DRM, and if that fails opens a
 *     primary node, and drops master. Then checks that drm supports GUI
 *     topology communication.
 *
 * Results:
 *     If succesful returns a positive open file descriptor. Otherwise
 *     returns -1.
 *
 * Side effects:
 *      May temporarily become drm master if render nodes are not available,
 *      and thus race with the X server.
 *
 *-----------------------------------------------------------------------------
 */
static int
resolutionDRMRPrimaryCheckOpen(void)
{
   int fd = -1;

   fd = resolutionOpenDRM("renderD");
   if (fd < 0) {
      g_debug("%s: Failed to open DRM render node.\n", __func__);
      fd = resolutionOpenDRM("card");
      if (fd >= 0)
         (void) drmDropMaster(fd);
   }
   if (fd < 0) {
      g_debug("%s: Failed to open DRM card node.\n", __func__);
      goto outErr;
   }

   if (!resolutionDRMCheckVersion(fd)) {
      return fd;
   }

   close(fd);
outErr:
   return -1;
}


/*
 *-----------------------------------------------------------------------------
 *
 * resolutionCheckForKMS --
 *
 *     Checks whether the vmwgfx DRM is present and supports exposing
 *     layout information through connector properties and preferred modes.
 *
 * Results:
 *     If succesful returns a positive number representing an open file
 *     descriptor to the node indicated by the control argument. If
 *     unsuccessful returns -1.
 *
 * Side effects:
 *     Opens a file to DRM. The file descriptor should be closed with
 *     resolutionDRMClose() when needed.
 *
 *-----------------------------------------------------------------------------
 */
int
resolutionCheckForKMS(ToolsAppCtx *ctx)  // IN: The ToolsAppCtx for
		                         // configuration db access.
{
   GError *err = NULL;
   gboolean doResolutionKMS;
   int fd;

   doResolutionKMS = g_key_file_get_boolean(ctx->config, "resolutionKMS",
                                            "enable", &err);
   if (err) {
      g_clear_error(&err);
      /*
       * We are going to try to see if we can load a valid vmwgfx
       */
      doResolutionKMS = TRUE;
   } else {
      g_debug("%s: ResolutionKMS %s using configuration file info.\n",
              __func__, (doResolutionKMS) ? "enabled" : "disabled");
   }

   if (!doResolutionKMS)
      return -1;

   if (resolutionDLOpen()) {
      g_warning("%s: Failed to find needed system libraries for "
                "resolutionKMS.\n", __func__);
      return -1;
   } else {
      g_message("%s: dlopen succeeded.\n", __func__);
   }

   fd = resolutionDRMRPrimaryCheckOpen();

   if (fd < 0)
      g_warning("%s: No system support for resolutionKMS.\n", __func__);
   else
      g_message("%s: System support available for resolutionKMS.\n",
                __func__);

   return fd;
}

/*
 *-----------------------------------------------------------------------------
 *
 * resolutionDRMClose --
 *
 *     Scans for VMWare Xorg driver files and tries to determine the Xorg
 *     driver version.
 *
 * Results:
 *     If succesful returns zero and outputs the driver version in the
 *     parameters major, minor and level. If not successful, returns -1.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */
void
resolutionDRMClose(int fd)
{
   close(fd);
   resolutionDLClose();
}

#endif /* ENABLE_RESOLUTIONKMS */
0707010000037F000081A400000000000000000000000168225505000005A0000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionCommon.h  /*********************************************************
 * Copyright (C) 2016-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/
/* Authors:
 * Thomas Hellstrom <thellstrom@vmware.com>
 */

#ifndef _RESOLUTION_COMMON_H_
#define _RESOLUTION_COMMON_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_DISTRIBUTE
#include "includeCheck.h"
#include "vmware/tools/plugin.h"
#ifdef ENABLE_RESOLUTIONKMS

int resolutionCheckForKMS(ToolsAppCtx *ctx);
void resolutionDRMClose(int fd);

#else

static inline int resolutionCheckForKMS(ToolsAppCtx *ctx)
{
    return -1;
}

static inline void resolutionDRMClose(int fd) {}

#endif /* !ENABLE_RESOLUTIONKMS */

#endif /*  _RESOLUTION_COMMON_H_ */
07070100000380000081A40000000000000000000000016822550500001651000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionDL.c  /*********************************************************
 * Copyright (C) 2016-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/
/* Authors:
 * Thomas Hellstrom <thellstrom@vmware.com>
 */

#ifdef ENABLE_RESOLUTIONKMS
#ifndef HAVE_LIBUDEV

#include "resolutionDL.h"
#include "vmware.h"
#include <dlfcn.h>
#include <stddef.h>
#include <stdlib.h>

#define G_LOG_DOMAIN "resolutionCommon"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"


struct FuncToResolv {
   size_t offset;
   const char *name;
};

#define UDEV_RESOLV(_name) \
   {.offset = offsetof(struct Udev1Interface, _name), \
    .name = "udev"#_name}

#define LIBDRM_RESOLV(_name) \
   {.offset = offsetof(struct Drm2Interface, _name), \
    .name = "drm"#_name}


static struct FuncToResolv udev1Table[] = {
   UDEV_RESOLV(_device_get_devnode),
   UDEV_RESOLV(_device_get_parent_with_subsystem_devtype),
   UDEV_RESOLV(_device_get_sysattr_value),
   UDEV_RESOLV(_device_new_from_syspath),
   UDEV_RESOLV(_device_unref),
   UDEV_RESOLV(_enumerate_add_match_property),
   UDEV_RESOLV(_enumerate_add_match_subsystem),
   UDEV_RESOLV(_enumerate_get_list_entry),
   UDEV_RESOLV(_enumerate_new),
   UDEV_RESOLV(_enumerate_scan_devices),
   UDEV_RESOLV(_enumerate_unref),
   UDEV_RESOLV(_list_entry_get_name),
   UDEV_RESOLV(_list_entry_get_next),
   UDEV_RESOLV(_new),
   UDEV_RESOLV(_unref)
};

static struct FuncToResolv drm2Table[] = {
   LIBDRM_RESOLV(Open),
   LIBDRM_RESOLV(Close),
   LIBDRM_RESOLV(GetVersion),
   LIBDRM_RESOLV(FreeVersion),
   LIBDRM_RESOLV(DropMaster),
   LIBDRM_RESOLV(CommandWrite)
};

struct Udev1Interface *udevi = NULL;
struct Drm2Interface *drmi = NULL;

static void *dlhandle;

/*
 *-----------------------------------------------------------------------------
 *
 * resolutionDLClose --
 *
 *     Removes any dynamic library reference and frees any resource
 *     allocated by resolutionDLOpen.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */
void
resolutionDLClose(void)
{
   if (udevi) {
      free(udevi);
      udevi = NULL;
   }

   if (drmi) {
      free(drmi);
      drmi = NULL;
   }

   if (dlhandle) {
      dlclose(dlhandle);
      dlhandle = NULL;
   }
}

/*
 *-----------------------------------------------------------------------------
 *
 * resolutionDLResolve --
 *
 *     Tries to open and resolve needed symbols of a single shared library.
 *
 * Results:
 *     If succesful returns zero, otherwise returns -1.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */
static int
resolutionDLResolve(void **ptr,                        // OUT: pointer to
                                                       // function table.
                    size_t size,                       // IN: Size of ft.
                    const char name[],                 // IN: Library name.
                    const struct FuncToResolv table[], // IN: Table of name-
                                                       // offset pairs
                    int numEntries)                    // IN: Num entries in
                                                       // table.
{
   void **func_ptr;
   int i;

   if (*ptr)
      return 0;

   *ptr = malloc(size);
   if (!*ptr)
      return -1;

   dlhandle = dlopen(name, RTLD_NOW);
   if (!dlhandle) {
      g_debug("%s: Failed to open shared library \"%s\".\n", __func__,
              name);
      goto out_err;
   }

   for (i = 0; i < numEntries; ++i) {
      func_ptr = (void *) ((unsigned long) *ptr + table[i].offset);
      *func_ptr = dlsym(dlhandle, table[i].name);
      if (!*func_ptr) {
         g_debug("%s: Failed to resolve %s symbol \"%s\".\n", __func__,
                 name,table[i].name);
         goto out_err;
      }
   }

   return 0;

out_err:
   resolutionDLClose();

   return -1;
}

/*
 *-----------------------------------------------------------------------------
 *
 * resolutionDLOpen --
 *
 *     Tries to open and create a reference to distribution shared libraries
 *     needed for the resolutionKMS functionality.
 *
 * Results:
 *     If succesful returns zero, otherwise returns -1.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */
int
resolutionDLOpen(void)
{
   /* We support libudev major versions 0 and 1 for now. */
   if (resolutionDLResolve((void **)&udevi, sizeof(*udevi), "libudev.so.1",
                           udev1Table, ARRAYSIZE(udev1Table)) &&
       resolutionDLResolve((void **)&udevi, sizeof(*udevi), "libudev.so.0",
                           udev1Table, ARRAYSIZE(udev1Table)))
      return -1;

   if (resolutionDLResolve((void **)&drmi, sizeof(*drmi), "libdrm.so.2",
                           drm2Table, ARRAYSIZE(drm2Table)))
      return -1;

   return 0;
}

#endif /* !HAVE_LIBUDEV */
#endif /* ENABLE_RESOLUTIONKMS */
   07070100000381000081A40000000000000000000000016822550500001C3D000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionDL.h  /*********************************************************
 * Copyright (C) 2016-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/
/* Authors:
 * Thomas Hellstrom <thellstrom@vmware.com>
 */

#ifndef _RESOLUTION_DL_H_
#define _RESOLUTION_DL_H_
#ifdef ENABLE_RESOLUTIONKMS

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_DISTRIBUTE
#include "includeCheck.h"

#ifndef HAVE_LIBUDEV

#include <stdint.h>

struct udev;
struct udev_device;
struct udev_enumerate;

/*
 * This struct holds the function pointers we use in libudev.1. Libudev is
 * Lgpl 2.1 (or later) licensed, and according to lgpl 2.1, section 5, while
 * we use materials copied from header files, we restrict ourselves to
 * data structure layouts and small macros and thus, while this should be
 * considered a derivative work, the use of the object file is unrestricted.
 * An executable linked with libudev is however subject to lgpl license
 * restrictions.
 */
struct Udev1Interface {
    const char *
    (*_device_get_devnode)(struct udev_device *);
    struct udev_device *
    (*_device_get_parent_with_subsystem_devtype)(struct udev_device *,
						 const char *, const char *);
    const char *
    (*_device_get_sysattr_value)(struct udev_device *, const char *);
    struct udev_device *
    (*_device_new_from_syspath)(struct udev *, const char *);
    struct udev_device *
    (*_device_unref)(struct udev_device *);
    int
    (*_enumerate_add_match_property)(struct udev_enumerate *,const char *,
				      const char *);
    int
    (*_enumerate_add_match_subsystem)(struct udev_enumerate *,const char *);
    struct udev_list_entry *
    (*_enumerate_get_list_entry)(struct udev_enumerate *);
    struct udev_enumerate *
    (*_enumerate_new)(struct udev *udev);
    int
    (*_enumerate_scan_devices)(struct udev_enumerate *);
    struct udev_enumerate *
    (*_enumerate_unref)(struct udev_enumerate *);
    const char *
    (*_list_entry_get_name)(struct udev_list_entry *);
    struct udev_list_entry *
    (*_list_entry_get_next)(struct udev_list_entry *);
    struct udev *
    (*_new)(void);
    struct udev *
    (*_unref)(struct udev *);
};

#define udevi_list_entry_foreach(_udevi, list_entry, first_entry)	\
    for (list_entry = first_entry;					\
	 list_entry != NULL;						\
	 list_entry = (_udevi)->_list_entry_get_next(list_entry))

/*
 * This struct is originally defined in xf86drm.h which is MIT licenced.
 * However, this should not be considered a substantial part of the software,
 * and this struct is not subject to the license header of this file.
 */
typedef struct _drmVersion {
    int     version_major;        /* Major version */
    int     version_minor;        /* Minor version */
    int     version_patchlevel;   /* Patch level */
    int     name_len;	          /* Length of name buffer */
    char    *name;	          /* Name of driver */
    int     date_len;             /* Length of date buffer */
    char    *date;                /* User-space buffer to hold date */
    int     desc_len;	          /* Length of desc buffer */
    char    *desc;                /* User-space buffer to hold desc */
} drmVersion, *drmVersionPtr;

#define DRM_VMW_UPDATE_LAYOUT        20

/*
 * These structs are originally defined in vmwgfx_drm.h, which is dual
 * MIT and GPLv2 licenced. However, VMWare Inc. owns the copyright and it's
 * should not be considered a substantial part of the software.
 * However this struct is not subject to the license header of this file.
 */
/**
 * struct drm_vmw_rect
 *
 * Defines a rectangle. Used in the overlay ioctl to define
 * source and destination rectangle.
 */

struct drm_vmw_rect {
	int32_t x;
	int32_t y;
	uint32_t w;
	uint32_t h;
};

/**
 * struct drm_vmw_update_layout_arg
 *
 * @num_outputs: number of active connectors
 * @rects: pointer to array of drm_vmw_rect cast to an uint64_t
 *
 * Input argument to the DRM_VMW_UPDATE_LAYOUT Ioctl.
 */
struct drm_vmw_update_layout_arg {
	uint32_t num_outputs;
	uint32_t pad64;
	uint64_t rects;
};

/*
 * This struct holds the function pointers we use in libdrm.2. The
 * libdrm API is MIT licenced and what we do here should not be considered
 * a substantial part of the software:
 * However this struct is not subject to the license header of this file.
 */
struct Drm2Interface {
    int (*Open)(const char *, const char *);
    int (*Close)(int);
    drmVersionPtr (*GetVersion)(int fd);
    void (*FreeVersion)(drmVersionPtr);
    int (*DropMaster)(int fd);
    int (*CommandWrite)(int fd, unsigned long drmCommandIndex, void *data,
			unsigned long size);
};

extern struct Udev1Interface *udevi;
extern struct Drm2Interface *drmi;

void resolutionDLClose(void);
int resolutionDLOpen(void);

#define udev_device_get_devnode \
    udevi->_device_get_devnode
#define udev_device_get_parent_with_subsystem_devtype \
    udevi->_device_get_parent_with_subsystem_devtype
#define udev_device_get_sysattr_value \
    udevi->_device_get_sysattr_value
#define udev_device_new_from_syspath \
    udevi->_device_new_from_syspath
#define udev_device_unref \
    udevi->_device_unref
#define udev_enumerate_add_match_property \
    udevi->_enumerate_add_match_property
#define udev_enumerate_add_match_subsystem \
    udevi->_enumerate_add_match_subsystem
#define udev_enumerate_get_list_entry \
    udevi->_enumerate_get_list_entry
#define udev_enumerate_new \
    udevi->_enumerate_new
#define udev_enumerate_scan_devices \
    udevi->_enumerate_scan_devices
#define udev_enumerate_unref \
    udevi->_enumerate_unref
#define udev_list_entry_get_name \
    udevi->_list_entry_get_name
#define udev_list_entry_get_next \
    udevi->_list_entry_get_next
#define udev_new \
    udevi->_new
#define udev_unref \
    udevi->_unref
#define udev_list_entry_foreach(_a, _b)\
    udevi_list_entry_foreach(udevi, _a, _b)

#define drmOpen \
    drmi->Open
#define drmClose \
    drmi->Close
#define drmGetVersion \
    drmi->GetVersion
#define drmFreeVersion \
    drmi->FreeVersion
#define drmDropMaster \
    drmi->DropMaster
#define drmCommandWrite \
    drmi->CommandWrite

#else /* HAVE_LIBUDEV */

#include <libudev.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <vmwgfx_drm.h>

/* Work around an incorrect define in old libdrm */
#undef DRM_VMW_UPDATE_LAYOUT
#define DRM_VMW_UPDATE_LAYOUT        20

static inline void resolutionDLClose(void) {}
static inline int resolutionDLOpen(void) {return 0;}

#endif /* HAVE_LIBUDEV */
#endif /* ENABLE_RESOLUTIONKMS */
#endif
   07070100000382000081A40000000000000000000000016822550500000BF9000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionInt.h /*********************************************************
 * Copyright (C) 2008-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file resolutionInt.h --
 *
 * Internal header file for lib/resolution.
 */

#ifndef _LIB_RESOLUTIONINT_H_
#define _LIB_RESOLUTIONINT_H_

#define INCLUDE_ALLOW_USERLEVEL
#define INCLUDE_ALLOW_DISTRIBUTE
#include "includeCheck.h"
#include "vmware.h"

#if defined(_WIN32)
#  include "resolutionWinCommon.h"
#endif

#if defined(RESOLUTION_X11)
#  include <X11/Xlib.h>
typedef Display *       InitHandle;
#elif defined(__APPLE__) || defined(RESOLUTION_WIN32)
typedef void *          InitHandle;
#else
#  error Unknown display backend
#endif

/*
 * Describes internal state of the resolution library.  I.e., tracks whether
 * a capability is supported, enabled, etc.
 */
typedef struct {
   Bool initialized;                    // TRUE if successfully initialized.
   Bool canSetResolution;               // TRUE if back-end supports Resolution_Set.
   Bool canSetTopology;                 // TRUE if back-end supports DisplayTopology_Set.
} ResolutionInfoType;

#if !defined(_WIN32)
/*
 * Describes the size and offset of a display.  An array of these
 * structures describes the entire topology of the guest desktop.
 *
 * XXX: For Win32, this is already defined in resolutionWinCommon.h.
 */
typedef struct {
   int x;
   int y;
   int width;
   int height;
} DisplayTopologyInfo;
#endif

/*
 * Global variables
 */

extern ResolutionInfoType resolutionInfo;

#if defined(G_LOG_DOMAIN)
#error Please include this file before glib.h
#endif
#define G_LOG_DOMAIN "resolutionSet"

#include "vmware/tools/plugin.h"

/*
 * Global functions
 */

/* Functions defined by back-end. */
Bool ResolutionBackendInit(InitHandle handle);
InitHandle ResolutionToolkitInit(ToolsAppCtx *ctx);
void ResolutionBackendCleanup(void);
Bool ResolutionSetResolution(uint32 width, uint32 height);
#if defined(RESOLUTION_WIN32)
Bool ResolutionChangeHost3DAvailabilityHint(Bool enable);
void ResolutionSetSessionChange(DWORD code, DWORD sessionID);
#endif
Bool ResolutionSetTopology(unsigned int ndisplays, DisplayTopologyInfo displays[]);
Bool ResolutionSetTopologyModes(unsigned int screen, unsigned int cmd, unsigned int ndisplays, DisplayTopologyInfo displays[]);

#endif // ifndef _LIB_RESOLUTIONINT_H_
   07070100000383000081A40000000000000000000000016822550500007F04000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionRandR12.c /*********************************************************
 * Copyright (c) 2010-2017,2019-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * resolutionRandR12.c --
 *
 * Set of functions to handle RandR12 guest screen resizing and topology change
 * for vmusr
 *
 */

/*
 *
 * The RandR12 API lacks good documentation. To avoid poor bug fixes, please
 * refer to the Xrandr.h header file and perhaps
 *
 * http://www.x.org/wiki/Development/Documentation/HowVideoCardsWork
 *
 * And become familiar with the following concepts:
 *
 * * Output  An output is a physical monitor connector on the machine, and
 *           the associated physical device. For the vmwgfx driver,
 *           it's a logical entry point to which a VMware display may be
 *           attached.
 *
 * * Mode    A mode describes a resolution and associated timing information.
 *           The timing information is never used for the vmwgfx display driver
 *           itself, but may be used by the X server to purge modes whose
 *           timing limits lie outside of its specification. The
 *           X server keeps a global list of modes, and each output carries
 *           a list of a subset of these modes that are suitable for that
 *           output.
 *
 * * Crtc    In a physical machine, a crtc is the device that scans out
 *           data from a given portion of display memory and feeds it to
 *           one or more outputs. The crtc and its outputs need to agree about
 *           timing and they are therefore programmed with the same mode.
 *           In the vmwgfx driver, there is one and only one output per
 *           logical crtc and the crtc and its output may be viewed as a
 *           single entity.
 *
 * * Fb      Or framebuffer is the display storage area from which the
 *           crtcs scan out. It needs to be at least the size of the union
 *           of all crtc scanout areas, but may be larger.
 */

#ifndef NO_MULTIMON

#include "resolutionInt.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/X.h>
#include <X11/Xmd.h>
#include <X11/extensions/panoramiXproto.h>
#include <X11/extensions/Xrandr.h>

#include "resolutionRandR12.h"
#include "str.h"
#include "strutil.h"
#include "util.h"

#define RR12_MODE_FORMAT "vmw-autofit-%ux%u"
#define RR12_MODE_MAXLEN (sizeof RR12_MODE_FORMAT + 2 * (10 - 2) + 1)
#define RR12_DEFAULT_DPI (96.0)
#define MILLIS_PER_INCH (25.4)


/*
 * Local data
 */

/*
 * The output structure.
 * Contains detailed information about each output and its connectivity.
 */

typedef struct RandR12Output {
   XRROutputInfo *output;       // Pointer to detailed info
   RROutput id;                 // XID of the output
   int crtc;                    // crtc entry in the RandR12Info array
   RRMode mode;                 // XID of current mode
} RandR12Output;

/*
 * The RandR12Info context. Contains info about the current topology state
 * and enough information to revert to the previous state.
 */

typedef struct RandR12Info {
   unsigned int nCrtc;          // Number of crtcs in the crtcs array
   unsigned int nOutput;        // Number of outputs in the outputs array
   unsigned int nNewModes;      // Number of new modes in the newModes array
   XRRCrtcInfo **crtcs;
   RandR12Output *outputs;
   XRRModeInfo **newModes;      // Newly created autofit modes
   XRRScreenResources *xrrRes;  // Screen resuources obtained from the server
   unsigned int xdpi;           // Current dpi in x direction
   unsigned int ydpi;           // Current dpi in y direction
   unsigned int origWidth;      // Used for reverting on failure
   unsigned int origHeight;     // Used for reverting on failure
   int event_base;              // For the RandR extension
   int error_base;
} RandR12Info;

/*
 * Optionally dump RandR12 log messages to a local logfile rather than to the
 * gtk logfile.
 */

/* #define _LOCAL_LOG_ "/tmp/randr12.log" */
#ifdef _LOCAL_LOG_
static FILE *_ofile;

#define LOG_START _ofile = fopen(_LOCAL_LOG_, "a")
#define g_debug(...) do {                       \
      fprintf(_ofile, "Debug:   " __VA_ARGS__); \
      fflush(_ofile); } while (0)
#define g_warning(...) do {                     \
      fprintf(_ofile, "Warning: " __VA_ARGS__); \
      fflush(_ofile); } while (0)
#define LOG_STOP fclose(_ofile)
#else
#define LOG_START
#include <glib.h>
#define LOG_STOP
#endif


/*
 * Local functions
 */

static void RandR12FreeInfo(RandR12Info *info);
static RandR12Info *RandR12GetInfo(Display *display, Window rootWin);
static Bool RandR12CrtcDisable(Display *display, unsigned int ndisplays,
                               RandR12Info *info, unsigned int width,
                               unsigned int height);
static unsigned int RandR12Dpi(unsigned int pixels, unsigned int mm);
static void RandR12CurrentSize(Display *display, int screen,
                               XRRScreenSize *cSize);
static Bool RandR12SetSizeVerify(Display *display,  Window rootWin,
                                 int screen, RandR12Info *info,
                                 int width, int height);
static Bool RandR12OutputHasMode(XRROutputInfo *output,
                                 XRRModeInfo *modeInfo);
static XRRModeInfo *RandR12MatchMode(Display *display, Window rootWin,
                                     RandR12Output *rrOutput,
                                     RandR12Info *info,
                                     int width, int height);
static Bool RandR12SetupOutput(Display *display, Window rootWin,
                               RandR12Info *info, RandR12Output *rrOutput,
                               int x, int y, int width, int height);
static void RandR12DeleteModes(Display *display, RandR12Info *info);


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12FreeInfo --
 *
 *      Free all allocations associated with a RandR12Info context
 *
 * Results:
 *      The RandR12Info context is freed, and the pointer may no
 *      longer be dereferenced.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
RandR12FreeInfo(RandR12Info *info)     // IN/OUT the RandR12Info context
{
   unsigned int i;

   if (!info) {
      return;
   }

   if (!info->xrrRes) {
      goto out_freeinfo;
   }

   for (i = 0; i < info->nNewModes; ++i) {
      XRRFreeModeInfo(info->newModes[i]);
   }

   for (i = 0; i < info->nCrtc; ++i) {
      if (info->crtcs[i]) {
         XRRFreeCrtcInfo(info->crtcs[i]);
      }
   }

   for (i = 0; i < info->nOutput; ++i) {
      if (info->outputs[i].output) {
         XRRFreeOutputInfo(info->outputs[i].output);
      }
   }

   free(info->newModes);
   free(info->outputs);
   free(info->crtcs);
   XRRFreeScreenResources(info->xrrRes);
 out_freeinfo:
   free(info);
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12GetInfo --
 *
 *      Allocate and initialize a RandR12Info context.
 *      Get the current X server configuration and info about outputs and
 *      crtcs. When done with the context, it should be freed using
 *      RandR12FreeInfo.
 *
 * Results:
 *      A pointer to a RandR12Info context on success. NULL on failure.
 *
 * Side effects:
 *      The info structure is setup for subsequent use of other functions.
 *      Space is allocated as needed and outputs are ordered in the
 *      info->outputs array with LVDS0 first and LVDS8 last.
 *
 *-----------------------------------------------------------------------------
 */

static RandR12Info *
RandR12GetInfo(Display *display,     // IN: Pointer to our display connection
               Window rootWin)       // IN: ID of the root window
{
   unsigned int i, j, num, numVMWCrtc;
   XRROutputInfo *output;
   RandR12Output *rrOutput;
   XRRCrtcInfo *crtc;
   XRRScreenResources *xrrRes;
   unsigned int nVMWOutput = 0;
   RandR12Info *info = Util_SafeCalloc(1, sizeof *info);

   /*
    * XRRQueryExtension is only used to get info->event_base,
    */

   if (!XRRQueryExtension(display, &info->event_base, &info->error_base)) {
      g_warning("%s: XRRQueryExtension failed.\n", __func__);
      goto out_err;
   }

   info->xrrRes = xrrRes = XRRGetScreenResources(display, rootWin);
   if (!xrrRes) {
      goto out_err;
   }

   info->nCrtc = xrrRes->ncrtc;
   info->nOutput = xrrRes->noutput;
   info->crtcs = Util_SafeCalloc(info->nCrtc, sizeof *info->crtcs);
   info->outputs = Util_SafeCalloc(info->nOutput, sizeof *info->outputs);
   info->newModes = Util_SafeCalloc(info->nOutput, sizeof *info->newModes);

   for (i = 0; i < info->nOutput; ++i) {
      output = XRRGetOutputInfo(display, xrrRes, xrrRes->outputs[i]);
      if (!output) {
         goto out_err;
      }

      if (sscanf(output->name, RR12_OUTPUT_FORMAT, &num) != 1) {
         XRRFreeOutputInfo(output);
         continue;
      }

      if (num > info->nOutput) {
         XRRFreeOutputInfo(output);
         goto out_err;
      }

      info->outputs[num - 1].output = output;
      info->outputs[num - 1].id = xrrRes->outputs[i];
      info->outputs[num - 1].crtc = -1;
      if (num > nVMWOutput) {
         nVMWOutput = num;
      }
   }

   /*
    * Confidence checks. This should never really happen with current drivers.
    */

   if (nVMWOutput != info->nOutput) {
      g_warning("%s: Not all outputs were VMW outputs.\n", __func__);
      goto out_err;
   }

   for (i = 0; i < nVMWOutput; ++i) {
      if (!info->outputs[i].output) {
         g_warning("%s: Missing output. %d\n", __func__, i);
         goto out_err;
      }
   }

   numVMWCrtc = 0;
   for (i = 0; i < info->nCrtc; ++i) {
      crtc = XRRGetCrtcInfo(display, xrrRes, xrrRes->crtcs[i]);
      if (!crtc) {
         goto out_err;
      }

      info->crtcs[i] = crtc;

      for (j = 0; j < nVMWOutput; ++j) {
         rrOutput = &info->outputs[j];
         if (crtc->npossible > 0 &&
             crtc->possible[0] == rrOutput->id && rrOutput->crtc == -1) {
            rrOutput->crtc = i;
            rrOutput->mode = crtc->mode;
            numVMWCrtc++;
            break;
         }
      }
   }

   /*
    * Confidence check. This should never really happen with our drivers.
    */

   if (numVMWCrtc != nVMWOutput) {
      g_warning("%s: Crtc / Output number mismatch.\n", __func__);
      goto out_err;
   }

   return info;
 out_err:
   RandR12FreeInfo(info);
   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12CrtcDisable --
 *
 *      Deactivate crtcs and associated outputs before an fb size change.
 *      The function deactivates crtcs and associated outputs
 *      1) whose scanout area is too big for the new fb size.
 *      2) that are going to be deactivated with the new topology.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      The RandR12info context is modified.
 *      The current mode of deactivated outputs is set to "None".
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RandR12CrtcDisable(Display *display,       // IN: Pointer to display
                                           // connection
                   unsigned int ndisplays, // IN: Number of VMware display in
                                           // the new topology
                   RandR12Info *info,      // IN/OUT: The RandR12Info context
                   unsigned int width,     // IN: Width of new topology
                   unsigned int height)    // IN: Height of new topology
{
   XRRScreenResources *xrrRes = info->xrrRes;
   XRRCrtcInfo *crtc;
   unsigned int i;

   for (i = 0; i < info->nCrtc; ++i) {
      crtc = info->crtcs[i];

      if (crtc->mode != None &&
          (crtc->x + crtc->width > width || crtc->y + crtc->height > height)) {

         if (XRRSetCrtcConfig(display, xrrRes, xrrRes->crtcs[i],
                              CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0) !=
             Success) {
            return FALSE;
         }
      }
   }

   for (i = ndisplays; i < info->nOutput; ++i) {
      crtc = info->crtcs[info->outputs[i].crtc];

      if (crtc->mode != None &&
          XRRSetCrtcConfig(display, xrrRes, xrrRes->crtcs[i],
                           CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0) !=
          Success) {
         return FALSE;
      }
      info->outputs[i].mode = None;
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12Dpi --
 *
 *      Given a number of pixels and a width in mm, compute the DPI value.
 *      If input or output looks suspicious (zero), revert to a default
 *      DPI value.
 *
 * Results:
 *      Returns the DPI value.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static unsigned int
RandR12Dpi(unsigned int pixels,  // IN: Dimension in pixels
           unsigned int mm)      // IN: Dimension in mm
{
   unsigned int dpi = 0;

   if (mm > 0) {
      dpi = (unsigned int)((double)pixels * MILLIS_PER_INCH /
                           (double)mm + 0.5);
   }

   return (dpi > 0) ? dpi : RR12_DEFAULT_DPI;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12CurrentSize --
 *
 *      Return the current dimensions of the fb, as cached in the display
 *      structure.
 *
 * Results:
 *      cSize is filled with the dimensions in pixels and mm.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
RandR12CurrentSize(Display *display,      // IN: Pointer to the display
                                          // connection
                   int screen,            // IN: The X screen
                   XRRScreenSize *cSize)  // OUT: The fb size
{
   memset(cSize, 0, sizeof *cSize);

   cSize->width = DisplayWidth(display, screen);
   cSize->mwidth = DisplayWidthMM(display, screen);
   cSize->height = DisplayHeight(display, screen);
   cSize->mheight = DisplayHeightMM(display, screen);
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12GetDpi --
 *
 *      Save the width, height and dpi of the current fb setup.
 *      This is used when reverting on failure and the dpi is used
 *      to calculate the new fb dimesions in mm.
 *
 * Results:
 *      The info structure is populated with the computed values.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
RandR12GetDpi(Display *display,   // IN: Pointer to the display connection
              int screen,         // IN: The X server screen
              RandR12Info *info)  // OUT: The RandR12Info context
{
   XRRScreenSize cSize;

   RandR12CurrentSize(display, screen, &cSize);

   info->origWidth = cSize.width;
   info->origHeight = cSize.height;
   info->xdpi = RandR12Dpi(cSize.width, cSize.mwidth);
   info->ydpi = RandR12Dpi(cSize.height, cSize.mheight);

   g_debug("%s: DPI is %u %u\n", __func__, info->xdpi, info->ydpi);
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12SetSizeVerify --
 *
 *      Set a new fb size, verify that the change went through and update
 *      the display structure.
 *
 * Results:
 *      Returns TRUE if function succeeds, FALSE otherwise. Upon failure,
 *      the function will make an attempt to restore the previous dimensions.
 *      The display structure is updated to hold the new dimensions.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RandR12SetSizeVerify(Display *display,  // IN/OUT: Pointer to the display
                                        // connection
                     Window rootWin,    // IN: ID of root window
                     int screen,        // IN: ID of X screen
                     RandR12Info *info, // IN: The RandR12Info context
                     int width,         // IN: New width
                     int height)        // IN: New height
{
   XRRScreenSize cSize;
   XEvent configEvent;
   unsigned int xmm;
   unsigned int ymm;
   Bool event = FALSE;

   xmm = (int)(MILLIS_PER_INCH * width / ((double)info->xdpi) + 0.5);
   ymm = (int)(MILLIS_PER_INCH * height / ((double)info->ydpi) + 0.5);

   g_debug("%s: Setting screenSize to %d %d %d %d\n", __func__,
           width, height, xmm, ymm);

   XRRSelectInput(display, rootWin, RRScreenChangeNotifyMask);
   XRRSetScreenSize(display, rootWin, width, height, xmm, ymm);

   /*
    * We need to sync and parse these events to update our display
    * structure with the new size. Nobody else does this for us.
    */

   XSync(display, FALSE);
   while (XCheckTypedEvent(display, RRScreenChangeNotify + info->event_base,
                           &configEvent)) {
      (void)XRRUpdateConfiguration(&configEvent);
      event = TRUE;
   }
   XRRSelectInput(display, rootWin, 0);

   if (!event) {
      g_warning("%s: Received no size change events.\n", __func__);
   }

   RandR12CurrentSize(display, screen, &cSize);
   if (cSize.width == width && cSize.height == height) {
      return TRUE;
   }

   /*
    * On failure, try to revert to the original size in preparation for
    * also reverting the Crtcs.
    */

   if (cSize.width != info->origWidth || cSize.height != info->origHeight) {

      xmm = (int)(MILLIS_PER_INCH * info->origWidth /
                  ((double)info->xdpi) + 0.5);
      ymm = (int)(MILLIS_PER_INCH * info->origHeight /
                  ((double)info->ydpi) + 0.5);

      XRRSelectInput(display, rootWin, RRScreenChangeNotifyMask);
      XRRSetScreenSize(display, rootWin, info->origWidth,
                       info->origHeight, xmm, ymm);
      XSync(display, FALSE);
      while (XCheckTypedEvent(display, RRScreenChangeNotify + info->event_base,
                              &configEvent)) {
         (void)XRRUpdateConfiguration(&configEvent);
      }
      XRRSelectInput(display, rootWin, 0);
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12OutputHasMode --
 *
 *      Determine whether a mode is registered with an output.
 *
 * Results:
 *      TRUE if the mode is registered, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RandR12OutputHasMode(XRROutputInfo *output,       // IN: Output
                     XRRModeInfo *modeInfo)       // IN: Mode.
{
   unsigned int i;

   for (i = 0; i < output->nmode; ++i) {
      if (output->modes[i] == modeInfo->id) {
         return TRUE;
      }
   }
   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12MatchMode --
 *
 *      Lookup an already exising mode, or register a new mode for the
 *      given size and the given output.
 *
 * Results:
 *      Returns a pointer to the mode's XRRModeInfo structure on success.
 *      NULL on failure.
 *
 * Side effects:
 *      If a new mode is created, it is registered with the RandR12Info
 *      structure for cached lookup, and with the X server.
 *
 *-----------------------------------------------------------------------------
 */

static XRRModeInfo *
RandR12MatchMode(Display *display,        // IN: Pointer to display connection
                 Window rootWin,          // IN: ID of root window
                 RandR12Output *rrOutput, // IN: The output
                 RandR12Info *info,       // IN/OUT: RandR12Info context
                 int width,               // IN: Width of sought mode
                 int height)              // IN: Height of sought mode.
{
   XRROutputInfo *output = rrOutput->output;
   XRRScreenResources *xrrRes = info->xrrRes;
   XRRModeInfo *modeInfo = NULL;
   char name[RR12_MODE_MAXLEN];
   int i;
   RRMode newMode;

   g_debug("%s: Trying to find a mode for resolution %dx%d.\n",
           __func__, width, height);

   for (i = 0; i < xrrRes->nmode; ++i) {
      modeInfo = &xrrRes->modes[i];
      if (modeInfo->width == width && modeInfo->height == height) {
         unsigned int w, h;

         /*
          * An autofit mode will work with any output
          */

         if (sscanf(modeInfo->name, RR12_MODE_FORMAT, &w, &h) == 2) {
            return modeInfo;
         }

         /*
          * Otherwise, make sure the mode is registered with the given output,
          * to avoid issues with timing incompatibilities.
          */

         if (RandR12OutputHasMode(output, modeInfo)) {
            g_debug("%s: Found an existing mode. Mode name is %s\n",
                    __func__, modeInfo->name);
            return modeInfo;
         }
      }
   }

   /*
    * Check for recent autofit modes. If the mode is not in the
    * output's modelist, then add it.
    */

   for (i = 0; i < info->nNewModes; ++i) {
      modeInfo = info->newModes[i];
      if (modeInfo->width == width && modeInfo->height == height) {

         if (!RandR12OutputHasMode(output, modeInfo)) {
            XRRAddOutputMode(display, rrOutput->id, modeInfo->id);
         }
         g_debug("%s: Found a recent autofit mode. Mode name is %s\n",
                 __func__, modeInfo->name);
         return modeInfo;
      }
   }

   /*
    * Create a new mode.
    */

   snprintf(name, sizeof name, RR12_MODE_FORMAT, width, height);
   modeInfo = XRRAllocModeInfo(name, strlen(name));
   modeInfo->width = width;
   modeInfo->height = height;
   newMode = XRRCreateMode(display, rootWin, modeInfo);
   if (newMode == None) {
      XRRFreeModeInfo(modeInfo);
      return NULL;
   }
   modeInfo->id = newMode;
   info->newModes[info->nNewModes++] = modeInfo;
   XRRAddOutputMode(display, rrOutput->id, modeInfo->id);

   g_debug("%s: Set up a new mode. Mode name is %s\n",
           __func__, modeInfo->name);

   return modeInfo;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12SetupOutput --
 *
 *      Set up an output and it's associated CRTC to scanout and show a
 *      specified region of the frame-buffer.
 *
 * Results:
 *      Returns TRUE on success. FALSE on failure.
 *      May add new modes to the RandR12Info context.
 *      Sets up rrOutput->mode to point to the new mode.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
RandR12SetupOutput(Display *display,        // IN: The display connection
                   Window rootWin,          // IN: ID of root window
                   RandR12Info *info,       // IN/OUT: RandR12Info context
                   RandR12Output *rrOutput, // IN/OUT: Identifies the output
                   int x,                   // IN: X coordinate of upper
                                            // left corner of scanout area
                   int y,                   // IN: Corresponding Y coordinate
                   int width,               // IN: Width of scanout area
                   int height)              // IN: Height of scanout area
{
   RRCrtc crtcID = info->xrrRes->crtcs[rrOutput->crtc];
   XRRCrtcInfo *crtcInfo = info->crtcs[rrOutput->crtc];
   XRRModeInfo *mode;
   Status ret;

   mode = RandR12MatchMode(display, rootWin, rrOutput, info, width, height);

   g_debug("%s: Setting up RandR Crtc %d. %dx%d@%d,%d: \"%s\"\n",
           __func__, (int)crtcID, width, height, x, y,
           (mode) ? mode->name : "NULL");

   if (!mode) {
      return FALSE;
   }
   if (!crtcInfo) {
       g_warning("%s: Wasn't able to find crtc info for crtc id %d.\n", __func__,
                   (int)crtcID);
       return FALSE;
   }

   ret = XRRSetCrtcConfig(display, info->xrrRes, crtcID, CurrentTime, x, y,
                          mode->id, crtcInfo->rotation, &rrOutput->id, 1);
   if (ret == Success) {
      rrOutput->mode = mode->id;
      return TRUE;
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12DeleteModes --
 *
 *      Delete unused autofit modes from outputs not using them and
 *      unregister those modes from the X server if no utput is using
 *      them.
 *
 * Results:
 *      Nothing immediately visible to the caller.
 *
 * Side effects:
 *      Invalidates the RandR12Info context for subsequent mode lookups.
 *      The RandR12Info context should be destroyed after this operation.
 *
 *-----------------------------------------------------------------------------
 */

static void
RandR12DeleteModes(Display *display,  // IN: The display connection
                   RandR12Info *info) // IN: RandR12Info context
{
   XRRScreenResources *xrrRes = info->xrrRes;
   unsigned int i, j;
   unsigned int w, h;
   Bool used;

   /*
    * Loop over the global X server mode list skipping modes that are not
    * our autofit modes.
    */

   for (i = 0; i < xrrRes->nmode; ++i) {
      XRRModeInfo *modeInfo = &xrrRes->modes[i];

      if (sscanf(modeInfo->name, RR12_MODE_FORMAT, &w, &h) != 2) {
         continue;
      }

      used = FALSE;

      /*
       * Loop over all outputs and see if the outfit mode is used by any
       * output. In that case mark it as used,
       * otherwise check if the mode is in the output's mode list.
       * In that case remove it from the output mode list.
       */

      for (j = 0; j < info->nOutput; ++j) {
         RandR12Output *rrOutput = &info->outputs[j];

         if (rrOutput->mode != modeInfo->id) {
            if (RandR12OutputHasMode(rrOutput->output, modeInfo)) {
                  g_debug("%s: Deleting mode %s.\n", __func__,
                          modeInfo->name);
                  XRRDeleteOutputMode(display, rrOutput->id, modeInfo->id);
            }
         } else {
            used = TRUE;
         }
      }

      /*
       * If the mode wasn't used by any output, remove it from the X server's
       * global modelist.
       */

      if (!used) {
         g_debug("%s: Destroying mode %s.\n", __func__, modeInfo->name);
         XRRDestroyMode(display, modeInfo->id);
      }
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12Revert --
 *
 *      Attempt to revert crtcs and outputs to the previous topology.
 *      Delete unused autofit modes.
 *
 * Results:
 *      Nothing immediately visible to the caller.
 *      The RandR12Info context pointer may be replaced with a
 *      pointer to a new context. In that case the old context
 *      will have been freed.
 *
 * Side effects:
 *      The RandR12Info context will be invalidated for subsequent
 *      mode lookups and should be destroyed after this operation.
 *
 *-----------------------------------------------------------------------------
 */

static void
RandR12Revert(Display *display,    // IN: The display connection
              Window rootWin,      // IN: ID of the root window
              RandR12Info **pInfo) // IN/OUT: RandR12Info context pointer
{
   unsigned int i;
   RandR12Info *info = *pInfo;
   XRRScreenResources *xrrRes = info->xrrRes;
   XRRCrtcInfo *crtc;
   RRCrtc crtcID;

   g_debug("%s: Reverting to original setup.\n", __func__);

   for (i = 0; i < info->nOutput; ++i) {

      RandR12Output *rrOutput = &info->outputs[i];
      crtc = info->crtcs[rrOutput->crtc];
      crtcID = xrrRes->crtcs[rrOutput->crtc];

      if (XRRSetCrtcConfig(display, info->xrrRes, crtcID,
                           CurrentTime, crtc->x, crtc->y,
                           crtc->mode, crtc->rotation,
                           crtc->outputs, crtc->noutput) != Success) {

         g_warning("%s: Reverting crtc id %d failed.\n", __func__,
                   (int)crtcID);
      } else {
         rrOutput->mode = crtc->mode;
      }
   }

   *pInfo = RandR12GetInfo(display, rootWin);
   if (*pInfo) {
      RandR12FreeInfo(info);
      info = *pInfo;
      RandR12DeleteModes(display, info);
   } else {
      *pInfo = info;
      g_warning("%s: Deleting unused modes after revert failed.\n", __func__);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * RandR12_SetTopology --
 *
 *      Employs the RandR 1.2 extension to set a new display topology.
 *      This is for the new vmwgfx X driver, which uses RandR 1.2 to
 *      program multiple outputs.
 *      Delete unused autofit modes.
 *
 * Results:
 *      Returns TRUE on success, FALSE on failure.
 *      The Display structure will be updated with the new fb dimensions.
 *      On failure, the function will have made an attempt to restore the
 *      old dimensions and topology.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
RandR12_SetTopology(Display *dpy,           // IN/OUT: The display connection
                    int screen,             // IN: The X screen
                    Window rootWin,         // IN: ID of root window
                    unsigned int ndisplays, // IN: Number of VMware displays
                                            // in the topology.
                    xXineramaScreenInfo *displays, // IN: Array describing the
                                            // topology
                    unsigned int width,     // IN: Topology width
                    unsigned int height)    // IN: Topology height
{
   int minWidth, minHeight, maxWidth, maxHeight;
   RandR12Info *info;
   Bool retVal = FALSE;
   unsigned int i;
   static unsigned long sequence = 0;

   LOG_START;

   g_debug("%s: New request. Sequence is %lu\n", __func__, sequence++);

   if (!XRRGetScreenSizeRange(dpy, rootWin, &minWidth, &minHeight, &maxWidth,
                              &maxHeight) ||
       width < minWidth || height < minHeight ||
       width > maxWidth || height > maxHeight) {
      g_warning("%s: Invalid size request.\n", __func__);
      LOG_STOP;
      return FALSE;
   }

   info = RandR12GetInfo(dpy, rootWin);
   if (!info) {
      g_warning("%s: Setup info struct failed.\n", __func__);
      return FALSE;
   }

   RandR12GetDpi(dpy, screen, info);

   if (!RandR12CrtcDisable(dpy, ndisplays, info, width, height)) {
      g_warning("%s: Failed disabling unused crtcs.\n", __func__);
      RandR12Revert(dpy, rootWin, &info);
      goto out_ungrab;
   }

   if (!RandR12SetSizeVerify(dpy, rootWin, screen, info, width, height)) {
      g_warning("%s: Failed setting new framebuffer size.\n", __func__);
      RandR12Revert(dpy, rootWin, &info);
      goto out_ungrab;
   }

   g_debug("%s: Setting up %d VMware displays.\n", __func__, ndisplays);
   for (i = 0; i < ndisplays; ++i) {
      xXineramaScreenInfo *vmwin = &displays[i];

      if (i >= info->nOutput)
         break;

      if (!RandR12SetupOutput(dpy, rootWin, info, &info->outputs[i],
                              vmwin->x_org, vmwin->y_org, vmwin->width,
                              vmwin->height)) {

         /*
          * If this fails, something is seriously wrong, so
          * we don't try to revert at this point.
          */

         g_warning("%s: Setup VMware display %d failed, "
                   "but we're not reverting the operation.\n", __func__, i);
      }
   }

   retVal = TRUE;

 out_ungrab:

   g_debug("%s: Deleting unused autofit modes.\n", __func__);
   RandR12DeleteModes(dpy, info);

   XSync(dpy, FALSE);
   RandR12FreeInfo(info);

   LOG_STOP;
   return retVal;
}

#endif // ifndef NO_MULTIMON
07070100000384000081A4000000000000000000000001682255050000063F000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionRandR12.h /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * resolutionRandR12.h --
 *
 *      This header file exports the minimalistic RandR12 utilities
 *      interface.
 */

#ifndef _RESOLUTIONRANDR12_H_
#define _RESOLUTIONRANDR12_H_

#define INCLUDE_ALLOW_USERLEVEL
#include "includeCheck.h"

#ifndef NO_MULTIMON

#include <X11/Xlib.h>
#include <X11/X.h>
#include <X11/Xmd.h>
#include <X11/extensions/panoramiXproto.h>

#define RR12_OUTPUT_FORMAT "Virtual%u"

/*
 * Global functions
 */

extern Bool
RandR12_SetTopology(Display *dpy, int screen, Window rootWin,
                    unsigned int ndisplays, xXineramaScreenInfo *displays,
                    unsigned int width, unsigned int height);

#endif // ifndef NO_MULTIMON
#endif // ifndef _RESOLUTIONRANDR12_H_
 07070100000385000081A400000000000000000000000168225505000040BF000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionSet.c /*********************************************************
 * Copyright (c) 2008-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file resolution.c --
 *
 * Set of functions to handle guest screen resizing for vmware-{user,guestd}.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "vmware.h"
#include "debug.h"
#include "rpcout.h"
#include "str.h"
#include "strutil.h"

#include "resolutionInt.h"

#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"


#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);

/*
 * The maximum number of capabilities we can set.
 *
 * See ResolutionSetCapabilities().
 */
#define RESOLUTION_SET_CAPABILITIES_MAX 5

/*
 * Internal global variables
 */

/**
 * The name of the RPC channel we're using, e.g. TOOLS_DAEMON_NAME. Used by
 * ResolutionSet_SetServerCapability() to determine which capability to set.
 */
static const char *rpcChannelName = NULL;

/**
 * Describes current state of the library.
 */
ResolutionInfoType resolutionInfo;


/*
 * Global function definitions
 */

/**
 *
 * Initialize the guest resolution library.
 *
 * @param[in] handle  Back-end specific handle, if needed.
 * @return TRUE on success, FALSE on failure
 */

static Bool
ResolutionInit(InitHandle handle)
{
   // Shorter-named convenience alias.  I expect this to be optimized out.
   ResolutionInfoType *resInfo = &resolutionInfo;

   ASSERT(resInfo->initialized == FALSE);

   if (!ResolutionBackendInit(handle)) {
      return FALSE;
   }

   resInfo->initialized = TRUE;

   return TRUE;
}


/**
 *
 * Shutdown the plugin, free resources, etc.
 * Resolution_* calls will fail until user next calls ResolutionInit().
 */

static void
ResolutionCleanup(void)
{
   ResolutionInfoType *resInfo = &resolutionInfo;

   if (!resInfo->initialized) {
      return;
   }

   ResolutionBackendCleanup();
}


/**
 *
 * Handler for TCLO 'Resolution_Set'.
 *
 * Routine unmarshals RPC arguments and passes over to back-end ResolutionSet().
 *
 * @param[in] data RPC data
 * @return TRUE if we can reply, FALSE otherwise.
 */

static gboolean
ResolutionResolutionSetCB(RpcInData *data)
{
   uint32 width = 0 ;
   uint32 height = 0;
   unsigned int index = 0;
   gboolean retval = FALSE;

   ResolutionInfoType *resInfo = &resolutionInfo;

   if (!resInfo->initialized) {
      g_debug("%s: FAIL! Request for resolution set but plugin is not initialized\n",
              __FUNCTION__);
      return RPCIN_SETRETVALS(data, "Invalid guest state: resolution set not initialized", FALSE);
   }

   /* parse the width and height */
   if (!StrUtil_GetNextUintToken(&width, &index, data->args, " ")) {
      goto invalid_arguments;
   }
   if (!StrUtil_GetNextUintToken(&height, &index, data->args, "")) {
      goto invalid_arguments;
   }

   retval = ResolutionSetResolution(width, height);

invalid_arguments:
   return RPCIN_SETRETVALS(data, retval ? "" : "Invalid arguments", retval);
}


#if defined(RESOLUTION_WIN32)
/**
 *
 * Handler for TCLO 'ChangeHost3DAvailabilityHint'.
 *
 * Routine unmarshals RPC arguments and passes over to back-end for handling.
 *
 * @param[in] data RPC data
 * @return TRUE if we can reply, FALSE otherwise.
 */

static gboolean
ResolutionChangeHost3DAvailabilityHintCB(RpcInData *data)
{
   unsigned int set;
   gboolean success = FALSE;
   unsigned int index = 0;

   g_debug("%s: enter\n", __FUNCTION__);

   if (!StrUtil_GetNextUintToken(&set, &index, data->args, " ")) {
      g_debug("%s: invalid arguments\n", __FUNCTION__);
      return RPCIN_SETRETVALS(data,
                              "Invalid arguments. Expected \"set\"",
                              FALSE);
   }

   success = ResolutionChangeHost3DAvailabilityHint(set?TRUE:FALSE);

   RPCIN_SETRETVALS(data, success ? "" : "ResolutionChangeHost3DAvailabilityHint failed", success);

   g_debug("%s: leave\n", __FUNCTION__);
   return success;
}


/**
 *
 * Handler for TCLO 'DisplayTopologyModes_Set'.
 *
 * Routine unmarshals RPC arguments and passes over to back-end
 * ModesTopologySet().
 *
 * @note the following can be added as a unit test:
 *
 * RpcInData testdata;
 * testdata.args = "10 0 1, 1111 111, 2222 222, 3333 333, 4444 444, 5555 555, 6666 666, 7777 777, 8888 888, 9999 999, 0000 000";
 * ResolutionDisplayTopologyModesSetCB(&testdata);
 *
 * @param[in] data RPC data
 * @return TRUE if we can reply, FALSE otherwise.
 */

static gboolean
ResolutionDisplayTopologyModesSetCB(RpcInData *data)
{
   DisplayTopologyInfo *displays = NULL;
   unsigned int count;
   unsigned int i;
   unsigned int cmd;
   unsigned int screen;
   gboolean success = FALSE;
   const char *p;

   g_debug("%s: enter\n", __FUNCTION__);

   /*
    * The argument string will look something like:
    *   <count> <screen> <cmd> [ , <w> <h> ] * count.
    *
    * e.g.
    *    3 0 1, 640 480 , 800 600 , 1024 768
    */

   if (sscanf(data->args, "%u %u %u", &count, &screen, &cmd) != 3) {
      g_debug("%s: invalid arguments\n", __FUNCTION__);
      return RPCIN_SETRETVALS(data,
                              "Invalid arguments. Expected \"count\", \"screen\",  and \"cmd\"",
                              FALSE);
   }

   displays = malloc(sizeof *displays * count);
   if (!displays) {
      g_debug("%s: alloc failed\n", __FUNCTION__);
      RPCIN_SETRETVALS(data,
                       "Failed to alloc buffer for display modes",
                       FALSE);
      goto out;
   }

   for (p = data->args, i = 0; i < count; i++) {
      p = strchr(p, ',');
      if (!p) {
         g_debug("%s: expected comma separated display modes list\n", __FUNCTION__);
         RPCIN_SETRETVALS(data,
                          "Expected comma separated display modes list",
                          FALSE);
         goto out;
      }
      p++; /* Skip past the , */

      if (sscanf(p, " %d %d ", &displays[i].width, &displays[i].height) != 2) {
         g_debug("%s: expected w, h in display modes entry\n", __FUNCTION__);
         RPCIN_SETRETVALS(data,
                          "Expected w, h in display modes entry",
                          FALSE);
         goto out;
      }
   }

   success = ResolutionSetTopologyModes(screen, cmd, count, displays);

   RPCIN_SETRETVALS(data, success ? "" : "ResolutionSetTopologyModes failed", success);

out:
   free(displays);
   g_debug("%s: leave\n", __FUNCTION__);
   return success;
}
#endif


/**
 *
 * Handler for TCLO 'DisplayTopology_Set'.
 *
 * Routine unmarshals RPC arguments and passes over to back-end TopologySet().
 *
 * @param[in] data RPC data
 * @return TRUE if we can reply, FALSE otherwise.
 */

static gboolean
ResolutionDisplayTopologySetCB(RpcInData *data)
{
   DisplayTopologyInfo *displays = NULL;
   unsigned int count, i;
   gboolean success = FALSE;
   const char *p;

   ResolutionInfoType *resInfo = &resolutionInfo;

   if (!resInfo->initialized) {
      g_debug("%s: FAIL! Request for topology set but plugin is not initialized\n",
              __FUNCTION__);
      RPCIN_SETRETVALS(data, "Invalid guest state: topology set not initialized", FALSE);
      goto out;
   }

   /*
    * The argument string will look something like:
    *   <count> [ , <x> <y> <w> <h> ] * count.
    *
    * e.g.
    *    3 , 0 0 640 480 , 640 0 800 600 , 0 480 640 480
    */

   if (sscanf(data->args, "%u", &count) != 1) {
      return RPCIN_SETRETVALS(data,
                              "Invalid arguments. Expected \"count\"",
                              FALSE);
   }

   displays = malloc(sizeof *displays * count);
   if (!displays) {
      RPCIN_SETRETVALS(data,
                       "Failed to alloc buffer for display info",
                       FALSE);
      goto out;
   }

   for (p = data->args, i = 0; i < count; i++) {
      p = strchr(p, ',');
      if (!p) {
         RPCIN_SETRETVALS(data,
                          "Expected comma separated display list",
                          FALSE);
         goto out;
      }
      p++; /* Skip past the , */

      if (sscanf(p, " %d %d %d %d ", &displays[i].x,
                 &displays[i].y, &displays[i].width, &displays[i].height) != 4) {
         RPCIN_SETRETVALS(data,
                          "Expected x, y, w, h in display entry",
                          FALSE);
         goto out;
      }
   }

   success = ResolutionSetTopology(count, displays);

   RPCIN_SETRETVALS(data, success ? "" : "ResolutionSetTopology failed", success);

out:
   free(displays);
   return success;
}


/**
 * Cleanup internal data on shutdown.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Unused.
 * @param[in]  data     Unused.
 */

static void
ResolutionSetShutdown(gpointer src,
                      ToolsAppCtx *ctx,
                      gpointer data)
{
   ResolutionCleanup();
}


/**
 * Sends the tools.capability.resolution_server RPC to the VMX.
 *
 * @param[in]  chan     The RPC channel.
 * @param[in]  value    The value to send for the capability bit.
 */

static void
ResolutionSetServerCapability(RpcChannel *chan,
                              unsigned int value)
{
   gchar *msg;

   if (!rpcChannelName) {
      g_debug("Channel name is null, RPC not sent.\n");
      return;
   }

   msg = g_strdup_printf("tools.capability.resolution_server %s %d",
                         rpcChannelName,
                         value);
   if (!RpcChannel_Send(chan, msg, strlen(msg), NULL, NULL)) {
      g_warning("%s: Unable to set tools.capability.resolution_server\n",
                __FUNCTION__);
   }
   g_free(msg);
}


/**
 * Returns the list of the plugin's capabilities.
 *
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  set      Whether setting or unsetting the capability.
 * @param[in]  data     Unused.
 *
 * @return A list of capabilities.
 */

static GArray *
ResolutionSetCapabilities(gpointer src,
                          ToolsAppCtx *ctx,
                          gboolean set,
                          gpointer data)
{
   /* The array of capabilities to return to the tools service. */
   ToolsAppCapability capabilityArray[RESOLUTION_SET_CAPABILITIES_MAX];

   /* The next unused entry in the capabilities array. */
   unsigned int capabilityCount = 0;

   ResolutionInfoType *resInfo = &resolutionInfo;

   g_debug("%s: enter\n", __FUNCTION__);

   if (!resInfo->initialized) {
      return FALSE;
   }

   /*
    * XXX: We must register display_topology_set before resolution_set to avoid
    *      a race condition in the host. See bug 472343.
    */
   /*
    * If we can set the guest topology, add the display_topology_set and
    * display_global_offset capabilities to our array.
    */
   if (resInfo->canSetTopology) {
      /*
       * XXX: We use a value of '2' here because, for historical reasons, the
       *      Workstation/Fusion UI will treat a value of 1 for this capability
       *      as unsupported. See bug 149541.
       */
      capabilityArray[capabilityCount].type  = TOOLS_CAP_OLD;
      capabilityArray[capabilityCount].name  = "display_topology_set";
      capabilityArray[capabilityCount].index = 0;
      capabilityArray[capabilityCount].value = set ? 2 : 0;
      capabilityCount++;

      capabilityArray[capabilityCount].type  = TOOLS_CAP_OLD;
      capabilityArray[capabilityCount].name  = "display_global_offset";
      capabilityArray[capabilityCount].index = 0;
      capabilityArray[capabilityCount].value = set ? 1 : 0;
      capabilityCount++;
   }

   /*
    * If we can set the guest resolution, add the resolution_set capability to
    * our array.
    */
   if (resInfo->canSetResolution) {
      capabilityArray[capabilityCount].type  = TOOLS_CAP_OLD;
      capabilityArray[capabilityCount].name  = "resolution_set";
      capabilityArray[capabilityCount].index = 0;
      capabilityArray[capabilityCount].value = set ? 1 : 0;
      capabilityCount++;

      /*
       * Send the resolution_server RPC to the VMX.
       *
       * XXX: We need to send this ourselves, instead of including it in the
       *      capability array, because the resolution_server RPC includes the
       *      name of the RPC channel that the VMX should use when sending
       *      resolution set RPCs as an argument.
       */
      if (ctx && ctx->rpc && ctx->isVMware) {
         ResolutionSetServerCapability(ctx->rpc, set ? 1 : 0);
      }
   }

#if defined(RESOLUTION_WIN32)
   /*
    * XXX: I believe we can always handle these RPCs from the service, even on
    *      Vista, so we always set the capabilities here, regardless of the
    *      value of resInfo->canSetTopology.
    */
   g_debug("%s: setting DPY_TOPO_MODES_SET_IDX to %u\n", __FUNCTION__,
           set ? 1 : 0);

   capabilityArray[capabilityCount].type  = TOOLS_CAP_NEW;
   capabilityArray[capabilityCount].name  = NULL;
   capabilityArray[capabilityCount].index = CAP_SET_TOPO_MODES;
   capabilityArray[capabilityCount].value = set ? 1 : 0;
   capabilityCount++;

   capabilityArray[capabilityCount].type  = TOOLS_CAP_NEW;
   capabilityArray[capabilityCount].name  = NULL;
   capabilityArray[capabilityCount].index = CAP_CHANGE_HOST_3D_AVAILABILITY_HINT;
   capabilityArray[capabilityCount].value = set ? 1 : 0;
   capabilityCount++;
#endif

   ASSERT(capabilityCount <= RESOLUTION_SET_CAPABILITIES_MAX);

   /*
    * VMTools_WrapArray copies the first capabilityCount elements from
    * capabilityArray to the returned GArray. The uninitialized elements are not
    * used.
    */
   /* coverity[uninit_use_in_call] */
   return VMTools_WrapArray(capabilityArray,
                            sizeof *capabilityArray,
                            capabilityCount);
}


/**
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in]  ctx   The app context.
 *
 * @return The registration data.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   RpcChannelCallback rpcs[] = {
      { "Resolution_Set",               &ResolutionResolutionSetCB },
      { "DisplayTopology_Set",          &ResolutionDisplayTopologySetCB },
#if defined(RESOLUTION_WIN32)
      { "DisplayTopologyModes_Set",     &ResolutionDisplayTopologyModesSetCB },
      { "ChangeHost3DAvailabilityHint", &ResolutionChangeHost3DAvailabilityHintCB },
#endif
   };

   InitHandle handle;

   static ToolsPluginData regData = {
      "resolutionSet",
      NULL,
      NULL
   };

   ToolsPluginSignalCb sigs[] = {
      { TOOLS_CORE_SIG_CAPABILITIES, ResolutionSetCapabilities, &regData },
      { TOOLS_CORE_SIG_SHUTDOWN, ResolutionSetShutdown, &regData }
   };

   ToolsAppReg regs[] = {
      { TOOLS_APP_GUESTRPC, NULL },
      { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
   };

   ResolutionInfoType *resInfo = &resolutionInfo;

   /*
    * If we aren't running in a VM (e.g., running in bootcamp natively on
    * a Mac), then just return NULL.
    */
   if (!ctx->isVMware) {
      return NULL;
   }

   /*
    * Save the RPC channel name from the ToolsAppCtx so that we can use it later
    * in calls to ResolutionSetServerCapability().
    */

   if (TOOLS_IS_MAIN_SERVICE(ctx)) {
      rpcChannelName = TOOLS_DAEMON_NAME;
   } else if (TOOLS_IS_USER_SERVICE(ctx)) {
      rpcChannelName = TOOLS_DND_NAME;
   } else {
      NOT_REACHED();
   }

   resInfo->initialized = FALSE;

   /*
    * XXX move to some shared lib or plugin
    */
   handle = ResolutionToolkitInit(ctx);

   if (!ResolutionInit(handle))
      return NULL;

   regs[0].data = VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs));
   regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
   return &regData;
}
 07070100000386000081A400000000000000000000000168225505000050E5000000000000000000000000000000000000005200000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c /*********************************************************
 * Copyright (c) 2008-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file resolutionX11.c
 *
 * X11 backend for resolutionSet plugin.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#include "resolutionInt.h"
#include "resolutionRandR12.h"

#include <X11/extensions/Xrandr.h>
#ifndef NO_MULTIMON
#include <X11/extensions/Xinerama.h>
#endif

#include "vmware.h"
#include "debug.h"
#include "libvmwarectrl.h"
#include "str.h"
#include "strutil.h"
#include "util.h"
#include "posix.h"
#include "resolutionCommon.h"

#define VMWAREDRV_PATH_64   "/usr/X11R6/lib64/modules/drivers/vmware_drv.o"
#define VMWAREDRV_PATH      "/usr/X11R6/lib/modules/drivers/vmware_drv.o"
#define VERSION_STRING      "VMware Guest X Server"

/**
 * Describes the state of the X11 back-end of lib/resolution.
 */
typedef struct {
   Display      *display;       // X11 connection / display context
   Window       rootWindow;     // points to display's root window
   Bool         canUseVMwareCtrl;
                                // TRUE if VMwareCtrl extension available
   Bool         canUseVMwareCtrlTopologySet;
                                // TRUE if VMwareCtrl extension supports topology set
   Bool         canUseRandR12;  // TRUE if RandR extension >= 1.2 available

   Bool         canUseResolutionKMS;    // TRUE if backing off for resolutionKMS
} ResolutionInfoX11Type;


/*
 * Global variables
 */

ResolutionInfoX11Type resolutionInfoX11;

/*
 * Local function prototypes
 */

static Bool ResolutionCanSet(void);
static Bool TopologyCanSet(void);
static Bool SelectResolution(uint32 width, uint32 height);
static int ResolutionX11ErrorHandler(Display *d, XErrorEvent *e);


/*
 *-----------------------------------------------------------------------------
 *
 * resolutionXorgDriverVersion --
 *
 *     Scans for VMWare Xorg driver files and tries to determine the Xorg
 *     driver version.
 *
 * Results:
 *     If succesful returns zero and outputs the driver version in the
 *     parameters major, minor and level. If not successful, returns -1.
 *
 * Side effects:
 *     None.
 *
 *-----------------------------------------------------------------------------
 */
static int
resolutionXorgDriverVersion(int numPaths,               // IN: Number of strings
                                                        // in paths.
                            const char *paths[],        // IN: Possible driver
                                                        // paths.
                            const char versionString[], // IN: Version token.
                            int *major,                 // OUT: Major version #
                            int *minor,                 // OUT: Minor version #
                            int *level)                 // OUT: Patchlevel
                                                        // version #
{
   FILE *driver = NULL;
   const char *curMatch;
   int curFileChar;
   int i;

   g_debug("%s: Scanning for VMWare Xorg drivers.\n", __func__);
   for(i = 0; i < numPaths; ++i) {
      g_debug("%s: Looking for \"%s\".\n", __func__, paths[i]);
      driver = fopen(paths[i], "r");
      if (driver)
         break;
   }

   if (!driver) {
      g_debug("%s: No driver found.\n",  __func__);
      return -1;
   }

   g_debug("%s: Driver found. Looking for version info.\n", __func__);
   curMatch = versionString;
   while (*curMatch) {
      if (feof(driver))
         goto outNotFound;

      curFileChar = fgetc(driver);
      if (curFileChar != EOF && curFileChar == *curMatch) {
         curMatch++;
         continue;
      } else if (curMatch != versionString) {
         curMatch = versionString;
         (void) ungetc(curFileChar, driver);
      }
   }

   if (fscanf(driver, "%d.%d.%d", major, minor, level) != 3)
      goto outNotFound;

   fclose(driver);
   g_debug("%s: Version info found: %d.%d.%d\n", __func__, *major, *minor,
           *level);
   return 0;

outNotFound:
   fclose(driver);
   g_debug("%s: No version info found.\n", __func__);
   return -1;
}


/*
 * Global function definitions
 */


/**
 * X11 back-end initializer.  Records caller's X11 display, then determines
 * which capabilities are available.
 *
 * @param[in] handle (ResolutionInfoX11Type is used as backend specific handle)
 * @return TRUE on success, FALSE on failure.
 */

Bool
ResolutionBackendInit(InitHandle handle)
{
   ResolutionInfoX11Type *resInfoX = (ResolutionInfoX11Type *)handle;
   ResolutionInfoType *resInfo = &resolutionInfo;
   int dummy1;
   int dummy2;

   if (resInfoX->canUseResolutionKMS == TRUE) {
      resInfo->canSetResolution = FALSE;
      resInfo->canSetTopology = FALSE;
      return FALSE;
   }

   XSetErrorHandler(ResolutionX11ErrorHandler);
   resInfoX->display = XOpenDisplay(NULL);

   /*
    * In case display is NULL, we do not load resolutionSet
    * as it serve no purpose. Also avoids SEGFAULT issue
    * like BZ1880932.
    *
    * VMX currently remembers the settings across a reboot,
    * so let's say someone replaces our Xorg driver with
    * xf86-video-modesetting, and then rebooted, we'd end up here,
    * but the VMX would still send resolution / topology events
    * and we'd hit the same segfault.
    */
   if (resInfoX->display == NULL) {
      g_error("%s: Invalid display detected.\n", __func__);
      resInfo->canSetResolution = FALSE;
      resInfo->canSetTopology = FALSE;
      return FALSE;
   }

   resInfoX->rootWindow = DefaultRootWindow(resInfoX->display);
   resInfoX->canUseVMwareCtrl = VMwareCtrl_QueryVersion(resInfoX->display, &dummy1,
                                                        &dummy2);
   resInfoX->canUseVMwareCtrlTopologySet = FALSE;
   resInfoX->canUseRandR12 = FALSE;

   resInfo->canSetResolution = ResolutionCanSet();
   resInfo->canSetTopology = TopologyCanSet();

   return TRUE;
}


/**
 * Stub implementation of ResolutionBackendCleanup for the X11 back-end.
 */

void
ResolutionBackendCleanup(void)
{
   ResolutionInfoX11Type *resInfoX = &resolutionInfoX11;
   if (resInfoX->display) {
      XCloseDisplay(resInfoX->display);
   }
   return;
}


/**
 * Given a width and height, define a custom resolution (if VMwareCtrl is
 * available), then issue a change resolution request via XRandR.
 *
 * This is called as a result of the Resolution_Set request from the vmx.
 *
 * @param[in] width requested width
 * @param[in] height requested height
 * @return TRUE if we are able to set to the exact size requested, FALSE otherwise.
 */

Bool
ResolutionSetResolution(uint32 width,
                        uint32 height)
{
   ResolutionInfoX11Type *resInfoX = &resolutionInfoX11;
   Bool ret;

   ASSERT(resolutionInfo.canSetResolution);

   XGrabServer(resInfoX->display);
   if (resInfoX->canUseVMwareCtrl) {
      /*
       * If so, use the VMWARE_CTRL extension to provide a custom resolution
       * which we'll find as an exact match from XRRConfigSizes() (unless
       * the resolution is too large).
       *
       * As such, we don't care if this succeeds or fails, we'll make a best
       * effort attempt to change resolution anyway.
       *
       * On vmwgfx, this is routed through the X server down to the
       * kernel modesetting system to provide a preferred mode with
       * correcte width and height.
       */
      VMwareCtrl_SetRes(resInfoX->display, DefaultScreen(resInfoX->display),
			width, height);
   }

   /*
    * Use legacy RandR (vmwlegacy) or RandR12 (vmwgfx) to select the
    * desired mode.
    */
   ret = SelectResolution(width, height);
   XUngrabServer(resInfoX->display);
   XFlush(resInfoX->display);

   return ret;
}


/**
 * Employs the Xinerama extension to declare a new display topology.
 *
 * @note Solaris 10 uses a different Xinerama standard than expected here. As a
 * result, topology set is not supported and this function is excluded from
 * Solaris builds. With Solaris 10 shipping X.org, perhaps we should revisit 
 * this decision.
 *
 * @param[in] ndisplays number of elements in topology
 * @param[in] topology array of display geometries
 * @return TRUE if operation succeeded, FALSE otherwise.
 */

Bool
ResolutionSetTopology(unsigned int ndisplays,
                      DisplayTopologyInfo *topology)
{
#ifdef NO_MULTIMON
   return FALSE;
#else
   ResolutionInfoX11Type *resInfoX = &resolutionInfoX11;
   Bool success = FALSE;
   unsigned int i;
   xXineramaScreenInfo *displays = NULL;
   short maxX = 0;
   short maxY = 0;
   int minX = 0x7FFF;
   int minY = 0x7FFF;

   ASSERT(resolutionInfo.canSetTopology);

   /*
    * Allocate xXineramaScreenInfo array & translate from DisplayTopologyInfo.
    * Iterate over displays looking for minimum, maximum dimensions.
    * Warn if min isn't at (0,0).
    * Transform to (0,0).
    * Call out to VMwareCtrl_SetTopology.
    * Set new jumbotron resolution.
    */

   displays = malloc(sizeof *displays * ndisplays);
   if (!displays) {
      goto out;
   }

   for (i = 0; i < ndisplays; i++) {
      displays[i].x_org = topology[i].x;
      displays[i].y_org = topology[i].y;
      displays[i].width = topology[i].width;
      displays[i].height = topology[i].height;

      maxX = MAX(maxX, displays[i].x_org + displays[i].width);
      maxY = MAX(maxY, displays[i].y_org + displays[i].height);
      minX = MIN(minX, displays[i].x_org);
      minY = MIN(minY, displays[i].y_org);
   }

   if (minX != 0 || minY != 0) {
      g_warning("The bounding box of the display topology does not have an "
                "origin of (0,0)\n");
   }

   /*
    * Transform the topology so that the bounding box has an origin of (0,0). Since the
    * host is already supposed to pass a normalized topology, this should not have any
    * effect.
    */
   for (i = 0; i < ndisplays; i++) {
      displays[i].x_org -= minX;
      displays[i].y_org -= minY;
   }

   /*
    * Grab server to avoid potential races between setting GUI topology
    * and setting FB topology.
    */
   XGrabServer(resInfoX->display);

   /*
    * First, call vmwarectrl to update the connection info
    * and resolution capabilities of connected monitors,
    * according to the host GUI layout on vmwgfx. On vmwlegacy this
    * sets the driver's exported Xinerama topology.
    *
    * For vmwgfx, this might be replaced with a direct kernel driver call
    * in upcoming versions.
    */
   if (resInfoX->canUseVMwareCtrlTopologySet) {
      if (!VMwareCtrl_SetTopology(resInfoX->display,
				  DefaultScreen(resInfoX->display),
                                  displays, ndisplays)) {
         g_debug("Failed to set topology in the driver.\n");
         goto out;
      }
   }

   if (resInfoX->canUseRandR12) {
       /*
	* For vmwgfx, use RandR12 to set the FB layout to a 1:1 mapping
	* of the host GUI layout.
	*/
      success = RandR12_SetTopology(resInfoX->display,
                                    DefaultScreen(resInfoX->display),
                                    resInfoX->rootWindow,
                                    ndisplays, displays,
                                    maxX - minX, maxY - minY);
   } else if (resInfoX->canUseVMwareCtrlTopologySet) {
      /*
       * For vmwlegacy, use legacy RandR to set the backing framebuffer
       * size. We don't do this unless we were able to set a new
       * topology using vmwarectrl.
       */
      if (!SelectResolution(maxX - minX, maxY - minY)) {
         g_debug("Failed to set new resolution.\n");
         goto out;
      }

      success = TRUE;
   }

out:
   XUngrabServer(resInfoX->display);
   XFlush(resInfoX->display);

   free(displays);
   return success;
#endif
}


/*
 * Local function definitions
 */


/**
 * Does VMware SVGA driver support resolution changing? We check by
 * testing RandR version and the availability of VMWCTRL extension. It
 * also check the output names for RandR 1.2 and above which is used for
 * the vmwgfx driver. Finally it searches the driver binary for a known
 * version string.
 *
 * resInfoX->canUseRandR12 will be set if RandR12 is usable.
 *
 * @return TRUE if the driver version is high enough, FALSE otherwise.
 */

static Bool
ResolutionCanSet(void)
{
   ResolutionInfoX11Type *resInfoX = &resolutionInfoX11;
   int major, minor, level;
   static const char *driverPaths[] = {
      VMWAREDRV_PATH_64,
      VMWAREDRV_PATH};

   /* See if the randr X module is loaded */
   if (!XRRQueryVersion(resInfoX->display, &major, &minor) ) {
      return FALSE;
   }

#ifndef NO_MULTIMON
   /*
    * See if RandR >= 1.2 can be used: The extension version is high enough and
    * all output names match the expected format.
    */
   if (major > 1 || (major == 1 && minor >= 2)) {
      XRRScreenResources* xrrRes;
      unsigned int num;

      xrrRes = XRRGetScreenResources(resInfoX->display, resInfoX->rootWindow);

      if (xrrRes) {
         int i;

         for (i = 0; i < xrrRes->noutput; i++) {
            XRROutputInfo* xrrOutput =
               XRRGetOutputInfo(resInfoX->display, xrrRes,
                                xrrRes->outputs[i]);

            if (!xrrOutput) {
               break;
            }

            if (sscanf(xrrOutput->name, RR12_OUTPUT_FORMAT, &num) != 1 ||
                num < 1) {
               XRRFreeOutputInfo(xrrOutput);
               break;
            }

            XRRFreeOutputInfo(xrrOutput);
         }

         if (i == xrrRes->noutput) {
            resInfoX->canUseRandR12 = TRUE;
         } else {
            g_debug("RandR >= 1.2 not usable\n");
         }

         XRRFreeScreenResources(xrrRes);
      }

      if (resInfoX->canUseRandR12) {
         return TRUE;
      }
   }

#endif // ifndef NO_MULTIMON

   /*
    * See if the VMWARE_CTRL extension is supported.
    */

   if (resInfoX->canUseVMwareCtrl) {
      return TRUE;
   }

   /*
    * XXX: This check does not work with XOrg 6.9/7.0 for two reasons: Both
    * versions now use .so for the driver extension and 7.0 moves the drivers
    * to a completely different directory. As long as we ship a driver for
    * 6.9/7.0, we can instead just use the VMWARE_CTRL check.
    */

   if (!resolutionXorgDriverVersion(2, driverPaths, VERSION_STRING,
				    &major, &minor, &level)) {
      return ((major > 10) || (major == 10 && minor >= 11));
   }
   return FALSE;
}


/**
 * Tests whether or not we can change display topology.
 *
 * resInfoX->canUseVMwareCtrlTopologySet will be set to TRUE if we should
 * use the old driver path when setting topology.
 *
 * @return TRUE if we're able to reset topology, otherwise FALSE.
 * @note resInfoX->canUseVMwareCtrlTopologySet will be set to TRUE on success.
 */

static Bool
TopologyCanSet(void)
{
   ResolutionInfoX11Type *resInfoX = &resolutionInfoX11;

   /**
    * Note: For some strange reason, an early call to XineramaQueryVersion in
    * in this function stops vmtoolsd from deadlocking and freezing the X
    * display. Might be a call to XGrabServer() in and X library init
    * function that is called when we've already grabbed the server....
    */

#ifdef NO_MULTIMON
   resInfoX->canUseVMwareCtrlTopologySet = FALSE;
   return FALSE;
#else
   int major;
   int minor;

   if (resInfoX->canUseVMwareCtrl && XineramaQueryVersion(resInfoX->display, &major,
                                                          &minor)) {
      /*
       * We need both a new enough VMWARE_CTRL and Xinerama for this to work.
       */
      resInfoX->canUseVMwareCtrlTopologySet = (major > 0) || (major == 0 && minor >= 2);
   } else {
      resInfoX->canUseVMwareCtrlTopologySet = FALSE;
   }

   return resInfoX->canUseVMwareCtrlTopologySet ||
      (resInfoX->canUseRandR12 && resInfoX->canUseVMwareCtrl);
#endif
}

/**
 * Given a width and height, find the biggest resolution that will "fit".
 * This is called as a result of the resolution set request from the vmx.
 *
 * @param[in] width
 * @param[in] height
 *
 * @return TRUE if we are able to set to the exact size requested, FALSE otherwise.
 */

Bool
SelectResolution(uint32 width,
                 uint32 height)
{
   ResolutionInfoX11Type *resInfoX = &resolutionInfoX11;
   XRRScreenConfiguration* xrrConfig;
   XRRScreenSize *xrrSizes;
   Rotation xrrCurRotation;
   uint32  xrrNumSizes;
   uint32 i;
   uint32 bestFitIndex = 0;
   uint64 bestFitSize = 0;
   uint64 potentialSize;
   Bool perfectMatch;

#ifndef NO_MULTIMON
   if (resInfoX->canUseRandR12) {
      xXineramaScreenInfo display;

      display.x_org = 0;
      display.y_org = 0;
      display.width = width;
      display.height = height;

      return RandR12_SetTopology(resInfoX->display,
                                 DefaultScreen(resInfoX->display),
                                 resInfoX->rootWindow,
                                 1, &display, width, height);
   }
#endif

   xrrConfig = XRRGetScreenInfo(resInfoX->display, resInfoX->rootWindow);
   xrrSizes = XRRConfigSizes(xrrConfig, &xrrNumSizes);
   bestFitIndex = XRRConfigCurrentConfiguration(xrrConfig, &xrrCurRotation);

   /*
    * Iterate thru the list finding the best fit that is still <= in both width
    * and height.
    */
   for (i = 0; i < xrrNumSizes; i++) {
      potentialSize = (uint64)xrrSizes[i].width * xrrSizes[i].height;
      if (xrrSizes[i].width <= width && xrrSizes[i].height <= height &&
          potentialSize > bestFitSize ) {
         bestFitSize = potentialSize;
         bestFitIndex = i;
      }
   }

   if (bestFitSize > 0) {
      Status rc;

      g_debug("Setting guest resolution to: %dx%d (requested: %d, %d)\n",
              xrrSizes[bestFitIndex].width, xrrSizes[bestFitIndex].height, width, height);
      rc = XRRSetScreenConfig(resInfoX->display, xrrConfig, resInfoX->rootWindow,
                              bestFitIndex, xrrCurRotation, CurrentTime);
      g_debug("XRRSetScreenConfig returned %d (result: %dx%d)\n", rc,
              xrrSizes[bestFitIndex].width, xrrSizes[bestFitIndex].height);
   } else {
      g_debug("Can't find a suitable guest resolution, ignoring request for %dx%d\n",
              width, height);
   }

   perfectMatch = xrrSizes[bestFitIndex].width == width &&
                  xrrSizes[bestFitIndex].height == height;
   XRRFreeScreenConfigInfo(xrrConfig);

   return perfectMatch;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ResolutionX11ErrorHandler --
 *
 *      Logs X non-fatal error events. This backend assumes that
 *      errors are checked within the functions that may generate
 *      them, not relying on X error events. Thus we just log and
 *      discard the events to prevent the tools daemon from crashing.
 *
 * Results:
 *      Logs error.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
ResolutionX11ErrorHandler(Display *d,      // IN: Pointer to display connection
			  XErrorEvent *e)  // IN: Pointer to the error event
{
   char msg[200];

   XGetErrorText(d, e->error_code, msg, sizeof(msg));

   g_warning("X Error %d (%s): request %d.%d\n",
	     e->error_code, msg, e->request_code, e->minor_code);

   return 0;
}


/**
 * Obtain a "handle".
 *
 * @note We will have to move this out of the resolution plugin soon, I am
 * just landing this here now for convenience as I port resolution set over
 * to the new service architecture.
 *
 * @return ResolutionInfoX11Type as backend specific handle
 */

InitHandle
ResolutionToolkitInit(ToolsAppCtx *ctx) // IN: For config database access
{
   ResolutionInfoX11Type *resInfoX = &resolutionInfoX11;
   int fd;

   memset(resInfoX, 0, sizeof *resInfoX);

   fd = resolutionCheckForKMS(ctx);
   if (fd >= 0) {
      resolutionDRMClose(fd);
      g_message("%s: Backing off for resolutionKMS.\n", __func__);
      resInfoX->canUseResolutionKMS = TRUE;
   }
   return (InitHandle) resInfoX;
}
   07070100000387000081A4000000000000000000000001682255050000074C000000000000000000000000000000000000004F00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/vmwarectrl.h    /*
 * Copyright 2006 by VMware, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the copyright holder(s)
 * and author(s) shall not be used in advertising or otherwise to promote
 * the sale, use or other dealings in this Software without prior written
 * authorization from the copyright holder(s) and author(s).
 */

/*
 * vmwarectrl.h --
 *
 *      The definitions used by the VMWARE_CTRL protocol extension that
 *      allows X clients to communicate with the driver.
 */


#ifndef _VMWARE_CTRL_H_
#define _VMWARE_CTRL_H_

#define VMWARE_CTRL_PROTOCOL_NAME "VMWARE_CTRL"

#define VMWARE_CTRL_MAJOR_VERSION 0
#define VMWARE_CTRL_MINOR_VERSION 2

#define X_VMwareCtrlQueryVersion 0
#define X_VMwareCtrlSetRes 1
#define X_VMwareCtrlSetTopology 2

#endif /* _VMWARE_CTRL_H_ */
07070100000388000081A40000000000000000000000016822550500000E34000000000000000000000000000000000000005400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/resolutionSet/vmwarectrlproto.h   /*
 * Copyright 2006 by VMware, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the copyright holder(s)
 * and author(s) shall not be used in advertising or otherwise to promote
 * the sale, use or other dealings in this Software without prior written
 * authorization from the copyright holder(s) and author(s).
 */

/*
 * vmwarectrlproto.h --
 *
 *      The description of the VMWARE_CTRL protocol extension that
 *      allows X clients to communicate with the driver.
 */

#ifndef _VMWARE_CTRL_PROTO_H_
#define _VMWARE_CTRL_PROTO_H_


#include <X11/X.h>
#include "vmwarectrl.h"


/*
 * Requests and Replies
 */

/* Version 0.1 definitions. */

typedef struct {
   CARD8  reqType;           /* always X_VMwareCtrlReqCode */
   CARD8  VMwareCtrlReqType; /* always X_VMwareCtrlQueryVersion */
   CARD16 length B16;
   CARD32 majorVersion B32;
   CARD32 minorVersion B32;
} xVMwareCtrlQueryVersionReq;
#define sz_xVMwareCtrlQueryVersionReq 12

typedef struct {
   BYTE    type; /* X_Reply */
   BYTE    pad1;
   CARD16  sequenceNumber B16;
   CARD32  length B32;
   CARD32  majorVersion B32;
   CARD32  minorVersion B32;
   CARD32  pad2 B32;
   CARD32  pad3 B32;
   CARD32  pad4 B32;
   CARD32  pad5 B32;
} xVMwareCtrlQueryVersionReply;
#define sz_xVMwareCtrlQueryVersionReply 32

typedef struct {
   CARD8  reqType;           /* always X_VMwareCtrlReqCode */
   CARD8  VMwareCtrlReqType; /* always X_VMwareCtrlSetRes */
   CARD16 length B16;
   CARD32 screen B32;
   CARD32 x B32;
   CARD32 y B32;
} xVMwareCtrlSetResReq;
#define sz_xVMwareCtrlSetResReq 16

typedef struct {
   BYTE   type; /* X_Reply */
   BYTE   pad1;
   CARD16 sequenceNumber B16;
   CARD32 length B32;
   CARD32 screen B32;
   CARD32 x B32;
   CARD32 y B32;
   CARD32 pad2 B32;
   CARD32 pad3 B32;
   CARD32 pad4 B32;
} xVMwareCtrlSetResReply;
#define sz_xVMwareCtrlSetResReply 32

/* Version 0.2 definitions. */

typedef struct {
   CARD8  reqType;           /* always X_VMwareCtrlReqCode */
   CARD8  VMwareCtrlReqType; /* always X_VMwareCtrlSetTopology */
   CARD16 length B16;
   CARD32 screen B32;
   CARD32 number B32;
   CARD32 pad1   B32;
} xVMwareCtrlSetTopologyReq;
#define sz_xVMwareCtrlSetTopologyReq 16

typedef struct {
   BYTE   type; /* X_Reply */
   BYTE   pad1;
   CARD16 sequenceNumber B16;
   CARD32 length B32;
   CARD32 screen B32;
   CARD32 pad2   B32;
   CARD32 pad3   B32;
   CARD32 pad4   B32;
   CARD32 pad5   B32;
   CARD32 pad6   B32;
} xVMwareCtrlSetTopologyReply;
#define sz_xVMwareCtrlSetTopologyReply 32

#endif /* _VMWARE_CTRL_PROTO_H_ */
07070100000389000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery  0707010000038A000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   0707010000038B000081A4000000000000000000000001682255050000085E000000000000000000000000000000000000005100000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/Makefile.am  ################################################################################
### Copyright (c) 2020-2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libserviceDiscovery.la

libserviceDiscovery_la_CPPFLAGS =
libserviceDiscovery_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libserviceDiscovery_la_CPPFLAGS += -DVMTOOLS_SERVICE_DISCOVERY_SCRIPTS=\"$(pkglibdir)/serviceDiscovery/scripts\"

libserviceDiscovery_la_LDFLAGS =
libserviceDiscovery_la_LDFLAGS += @PLUGIN_LDFLAGS@

libserviceDiscovery_la_LIBADD =
libserviceDiscovery_la_LIBADD += @VMTOOLS_LIBS@
libserviceDiscovery_la_LIBADD += @GOBJECT_LIBS@

libserviceDiscovery_la_SOURCES =
libserviceDiscovery_la_SOURCES += serviceDiscovery.c
libserviceDiscovery_la_SOURCES += serviceDiscoveryPosix.c
libserviceDiscovery_la_SOURCES += serviceDiscoveryInt.h

install-data-local:
	$(INSTALL) -d $(DESTDIR)/$(pkglibdir)/serviceDiscovery/scripts/
	$(INSTALL) -m 755 $(srcdir)/get-connection-info.sh \
			  $(DESTDIR)/$(pkglibdir)/serviceDiscovery/scripts/
	$(INSTALL) -m 755 $(srcdir)/get-listening-process-info.sh \
			  $(DESTDIR)/$(pkglibdir)/serviceDiscovery/scripts/
	$(INSTALL) -m 755 $(srcdir)/get-listening-process-perf-metrics.sh \
			  $(DESTDIR)/$(pkglibdir)/serviceDiscovery/scripts/
	$(INSTALL) -m 755 $(srcdir)/get-versions.sh \
			  $(DESTDIR)/$(pkglibdir)/serviceDiscovery/scripts/


  0707010000038C000081A4000000000000000000000001682255050000035B000000000000000000000000000000000000005C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/get-connection-info.sh   #!/bin/sh

####################################################################
# Copyright (c) 2020-2024 Broadcom. All Rights Reserved.
# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
####################################################################

# check if necesary commands exist
command -v ss >/dev/null 2>&1 || { echo >&2 "ss doesn't exist"; exit 1; }
command -v grep >/dev/null 2>&1 || { echo >&2 "grep doesn't exist"; exit 1; }
command -v sort >/dev/null 2>&1 || { echo >&2 "sort doesn't exist"; exit 1; }
command -v tr >/dev/null 2>&1 || { echo >&2 "tr doesn't exist"; exit 1; }

# get pids of listening processes
space_separated_pids=$(ss -lntup | grep -Eo "pid=[0-9]+" | sort -u)

# grep pattern
pattern=$(echo $space_separated_pids | tr ' ' '|')

# get matching lines
ss -antup | grep -E $pattern  | sed 's/[ \t]*$//'
 0707010000038D000081A40000000000000000000000016822550500000329000000000000000000000000000000000000006300000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/get-listening-process-info.sh    #!/bin/sh

# Copyright (C) 2020 VMware, Inc.  All rights reserved.

# check if necesary commands exist
command -v ss >/dev/null 2>&1 || { echo >&2 "ss doesn't exist"; exit 1; }
command -v grep >/dev/null 2>&1 || { echo >&2 "grep doesn't exist"; exit 1; }
command -v sort >/dev/null 2>&1 || { echo >&2 "sort doesn't exist"; exit 1; }
command -v tr >/dev/null 2>&1 || { echo >&2 "tr doesn't exist"; exit 1; }
command -v ps >/dev/null 2>&1 || { echo >&2 "ps doesn't exist"; exit 1; }

# get pids of listening processes
space_separated_pids=$(ss -lntup | grep -Eo "pid=[0-9]+" | grep -Eo "[0-9]*" | sort -u)

# ps accepts comma separated pids
comma_separated_pids=$(echo $space_separated_pids | tr ' ' ',')

# get information for each listening process
ps --pid $comma_separated_pids -o pid=,ppid=,comm=,command=
   0707010000038E000081A4000000000000000000000001682255050000107A000000000000000000000000000000000000006B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/get-listening-process-perf-metrics.sh    #!/bin/sh

# Copyright (C) 2020 VMware, Inc.  All rights reserved.

# check if necesary commands exist
command -v ss >/dev/null 2>&1 || { echo >&2 "ss doesn't exist"; exit 1; }
command -v grep >/dev/null 2>&1 || { echo >&2 "grep doesn't exist"; exit 1; }
command -v sort >/dev/null 2>&1 || { echo >&2 "sort doesn't exist"; exit 1; }
command -v awk >/dev/null 2>&1 || { echo >&2 "awk doesn't exist"; exit 1; }
command -v cat >/dev/null 2>&1 || { echo >&2 "cat doesn't exist"; exit 1; }
command -v cut >/dev/null 2>&1 || { echo >&2 "cut doesn't exist"; exit 1; }
command -v pgrep >/dev/null 2>&1 || { echo >&2 "pgrep doesn't exist"; exit 1; }
command -v getconf >/dev/null 2>&1 || { echo >&2 "getconf doesn't exist"; exit 1; }

ECHO="$(which echo)"

if [ "$ECHO" = "" ]; then
  ECHO=echo
fi

calculateCpuTimeForProcess() {
  pidArray="$@"
  cpuUsage=0
  for i in $pidArray; do
    pidStatArray=$(cat /proc/$i/stat)
    uptimes=$(cat /proc/uptime)
    uptime=$($ECHO ${uptimes} | cut -d' ' -f1)
    utime=$($ECHO ${pidStatArray} | cut -d' ' -f14)
    stime=$($ECHO ${pidStatArray} | cut -d' ' -f15)
    cutime=$($ECHO ${pidStatArray} | cut -d' ' -f16)
    cstime=$($ECHO ${pidStatArray} | cut -d' ' -f17)
    starttime=$($ECHO ${pidStatArray} | cut -d' ' -f22)
    pidCPU=$($ECHO - | awk -v ut=$utime -v st=$stime -v cut=$cutime -v cst=$cstime -v start=$starttime -v clk=$CLK_TCK -v up=$uptime '{print (ut+st+cut+cst)}')
    cpuUsage=$($ECHO - | awk -v ti=$timeout -v clk=$CLK_TCK -v cpu=$cpuUsage -v pcpu=$pidCPU -v ccpu=$PROC_COUNT '{print (((cpu+pcpu)/(ti*clk)*100)/ccpu)}')
  done
  $ECHO $cpuUsage
}

calculateMemForAllProcesses() {
  pids=$@
  for k in $pids; do
    pidArray="$k $(pgrep -P $k)"
    length=$($ECHO -n $pidArray | wc -w)
    PGPID=$k
    if [ $length -eq 1 ]; then
      memUsage=$(awk '/^Pss:/{A+=$2} END {print A}' $(for n in $($ECHO "$PGPID" | awk '{if (NR > 1 || $n!="") print "/proc/"$n"/smaps"}'); do $ECHO $n ; done) || exit 1)
    else
      memUsage=$(awk '/^Pss:/{A+=$2} END {print A}' $(for n in $($ECHO -e "\n$PGPID\n$(pgrep -P$PGPID)" | awk '{if (NR > 1 || $n !="") print "/proc/"$n"/smaps"}'); do $ECHO $n ; done) || exit 1)
    fi
    $ECHO MEM: $k  $memUsage
  done
}

calculateIOForAllProcesses() {
  pids=$@
  for l in $pids; do
    pidArray="$l $(pgrep -P $l)"
    length=$($ECHO -n $pidArray | wc -w)
    PGPID=$l
    if [ $length -eq 1 ]; then
      readBytes=$(awk '/^read_bytes:/{A+=$2} END {print A}' $(for n in $($ECHO "$PGPID" | awk '{if (NR > 1 || $n!="") print "/proc/"$n"/io"}'); do $ECHO "$n" ; done) || exit 1)
      writeBytes=$(awk '/^write_bytes:/{A+=$2} END {print A}' $(for m in $($ECHO "$PGPID" | awk '{if (NR > 1 || $m!="") print "/proc/"$m"/io"}'); do $ECHO "$m" ; done) || exit 1)
    else
      readBytes=$(awk '/^read_bytes:/{A+=$2} END {print A}' $(for n in $($ECHO -e "\n$PGPID\n$(pgrep -P$PGPID)" | awk '{if (NR > 1 || $n !="") print "/proc/"$n"/io"}'); do $ECHO $n ; done) || exit 1)
      writeBytes=$(awk '/^write_bytes:/{A+=$2} END {print A}' $(for m in $($ECHO -e "\n$PGPID\n$(pgrep -P$PGPID)" | awk '{if (NR > 1 || $m !="") print "/proc/"$m"/io"}'); do $ECHO $m ; done) || exit 1)
    fi
    $ECHO IO: $l $readBytes $writeBytes
  done
}

calculateCpuForAllProcesses() {
  pids=$@
  for j in $pids; do
    pidArray="$j $(pgrep -P $j)"
    cpuUsage=$(calculateCpuTimeForProcess $pidArray)
    $ECHO CPU: $j $cpuUsage
  done
}

run() {
  calculateCpuForAllProcesses $@

  calculateIOForAllProcesses $@

  sleep $timeout

  # need to get CPU and IO for two different timestamps to calculate delta
  calculateCpuForAllProcesses $@

  calculateIOForAllProcesses $@

  calculateMemForAllProcesses $@
}

get_performance_metrics() {
  if [ $# -lt 1 ]; then
    echo "No process id has been provided."
    exit 1
  fi

  pids=$@
  echo "#PIDs: - $pids"

  timeout=1
  CLK_TCK=$(getconf CLK_TCK)
  [ -z "$CLK_TCK" ] && { echo "Failed to get conf variable CLK_TCK"; exit 1; }
  PROC_COUNT=$(getconf _NPROCESSORS_ONLN)
  [ -z "$PROC_COUNT" ] && { echo "Failed to get conf variable _NPROCESSORS_ONLN"; exit 1; }

  run $pids
}

space_separated_pids=$(ss -lntup | grep -Eo "pid=[0-9]+" | grep -Eo "[0-9]+" | sort -u)

get_performance_metrics $space_separated_pids  0707010000038F000081A4000000000000000000000001682255050000141C000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/get-versions.sh  #!/bin/sh

# Copyright (c) 2021-2022 VMware, Inc.  All rights reserved.

# check if necesary commands exist
command -v ss >/dev/null 2>&1 || { echo >&2 "ss doesn't exist"; exit 1; }
command -v grep >/dev/null 2>&1 || { echo >&2 "grep doesn't exist"; exit 1; }
command -v sort >/dev/null 2>&1 || { echo >&2 "sort doesn't exist"; exit 1; }
command -v ps >/dev/null 2>&1 || { echo >&2 "ps doesn't exist"; exit 1; }
command -v awk>/dev/null 2>&1 || { echo >&2 "awk doesn't exist"; exit 1; }

space_separated_pids=$(ss -lntup | grep -Eo "pid=[0-9]+" | grep -Eo "[0-9]*" | sort -u)

get_command_line() {
  ps --pid $1 -o command
}

get_version() {
  PATTERN=$1
  VERSION_OPTION=$2
  for p in $space_separated_pids
  do
    COMMAND=$(get_command_line $p | grep -Eo "$PATTERN")
    [ ! -z "$COMMAND" ] && echo VERSIONSTART "$p" "$("${COMMAND%%[[:space:]]*}" $VERSION_OPTION 2>&1)" VERSIONEND
  done
}

get_vcops_version() {
  cat /usr/lib/vmware-vcops/user/conf/lastbuildversion.txt 2>/dev/null
}

get_srm_mgt_server_version() {
  grep -oE "<version>.*</version>" /opt/vmware/etc/appliance-manifest.xml 2>/dev/null
}

get_vcenter_appliance_version() {
  $(which vpxd 2>/dev/null) -v 2>/dev/null
}

get_vcloud_director_version() {
  PATTERN="\-DVCLOUD_HOME=\S+"
  for p in $space_separated_pids
  do
    VCLOUD_HOME=$(get_command_line $p | grep -Eo "$PATTERN" | cut -d'=' -f2)
    [ ! -z "$VCLOUD_HOME" ] && echo VERSIONSTART "$p" "$(grep product.version "${VCLOUD_HOME}/etc/global.properties" 2>/dev/null | cut -d'=' -f2 2>/dev/null)" VERSIONEND
  done
}

get_weblogic_version() {
  PATTERN="java\.security\.policy=.+/server/lib/weblogic\.policy"
  for p in $space_separated_pids
  do
    WEBLOGIC_HOME=$(get_command_line $p | grep -Eo "$PATTERN" | cut -d'=' -f2)
    WEBLOGIC_HOME="${WEBLOGIC_HOME%%/server/lib/weblogic.policy*}"
    [ ! -z "$WEBLOGIC_HOME" ] && echo VERSIONSTART "$p" "$(java -cp "${WEBLOGIC_HOME}/server/lib/weblogic.jar" weblogic.version 2>/dev/null)" VERSIONEND
  done
}

get_apache_tomcat_version() {
  PATTERN="/\S*tomcat\S*/bin/bootstrap\.jar.*\s+org\.apache\.catalina\.startup\.Bootstrap"
  for p in $space_separated_pids
  do
    TOMCAT_HOME=$(get_command_line $p | grep -Eo "$PATTERN")
    TOMCAT_HOME="${TOMCAT_HOME%%/bin/bootstrap.jar*}"
    [ ! -z "$TOMCAT_HOME" ] && echo VERSIONSTART "$p" "$(java -cp "${TOMCAT_HOME}/lib/catalina.jar" org.apache.catalina.util.ServerInfo 2>/dev/null)" VERSIONEND
  done
}

get_jboss_version() {
  PATTERN="jboss.home.dir=\S+"
  for p in $space_separated_pids
  do
    JBOSS_HOME=$(get_command_line $p | grep -Eo "$PATTERN" | cut -d'=' -f2)
    [ ! -z "$JBOSS_HOME" ] && echo VERSIONSTART "$p" "$("${JBOSS_HOME}/bin/standalone.sh" --version 2>/dev/null)" VERSIONEND
  done
}

get_db2_version() {
  db2level 2>/dev/null | grep "DB2 v"
}

get_tcserver_version() {
  command -v tcserver >/dev/null 2>&1 && { tcserver version 2>/dev/null; }
}

get_cassandra_version() {
  PATTERN="/usr/lib/.*apache-cassandra"
  for p in $space_separated_pids
  do
    CASSANDRA_CMD=$(get_command_line $p | grep -Eo "$PATTERN")
    CASSANDRA_INSTALL_PATH=`echo $CASSANDRA_CMD | awk 'match($0,/\/usr\/lib\/[a-zA-Z-]*\/[a-zA-Z\/]*\/apache-cassandra-[0-9.]*\//) {print substr($0,RSTART,RLENGTH)}'`
    if [ ! -z "$CASSANDRA_INSTALL_PATH" ]
    then
      IS_LI_CASSANDRA=`echo $CASSANDRA_INSTALL_PATH | grep "loginsight"`
      [ ! -z "$IS_LI_CASSANDRA" ] &&  export CASSANDRA_CONF="/storage/core/loginsight/cidata/cassandra/config"
      echo VERSIONSTART cassandra_version "$("${CASSANDRA_INSTALL_PATH}/bin/cassandra" -v 2>/dev/null)" VERSIONEND
    else
      CASSANDRA_CMD=$(get_command_line $p | grep -Eo "CassandraDaemon")
      [ ! -z "$CASSANDRA_CMD" ] && echo VERSIONSTART cassandra_version  $($(which cassandra) 2>/dev/null -v) VERSIONEND
   fi
  done
}

get_vrli_version() {
  FILE_PATH_TEMPLATE=/storage/core/loginsight/config/loginsight-config.xml#
  FILE_NAMES=$(ls /storage/core/loginsight/config/ 2>/dev/null | grep -o "[0-9].*")
  [ ! -z "$FILE_NAMES" ] && LATEST_FILE_NUM=$(printf '%s\n' "${FILE_NAMES[@]}" | awk '$1 > m || NR == 1 { m = $1 } END { print m }')
  VERSION_FILE_NAME="$FILE_PATH_TEMPLATE$LATEST_FILE_NUM"
  VERSION=$(cat $VERSION_FILE_NAME 2>/dev/null | grep 'strata-version value=' | grep -oE "[0-9.]+" | head -1)
  [ ! -z "$VERSION" ] && echo VERSIONSTART vrli_version $VERSION VERSIONEND
}

echo VERSIONSTART "vcops_version" "$(get_vcops_version)" VERSIONEND
echo VERSIONSTART "srm_mgt_server_version" "$(get_srm_mgt_server_version)" VERSIONEND
echo VERSIONSTART "vcenter_appliance_version" "$(get_vcenter_appliance_version)" VERSIONEND
echo VERSIONSTART "db2_version" "$(get_db2_version)" VERSIONEND
echo VERSIONSTART "tcserver_version" "$(get_tcserver_version)" VERSIONEND

get_version "/\S+/(httpd-prefork|httpd|httpd2-prefork)($|\s)" -v
get_version "/usr/(bin|sbin)/apache\S*" -v
get_version "/\S+/mysqld($|\s)" -V
get_version "\.?/\S*nginx($|\s)" -v
get_version "/\S+/srm/bin/vmware-dr($|\s)" --version
get_version "/\S+/dataserver($|\s)" -v

get_vcloud_director_version
get_weblogic_version
get_apache_tomcat_version
get_jboss_version
get_cassandra_version
get_vrli_version
07070100000390000081A4000000000000000000000001682255050000A538000000000000000000000000000000000000005800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/serviceDiscovery.c   /*********************************************************
 * Copyright (c) 2020-2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * serviceDiscovery.c --
 *
 * Captures the information about services inside the guest
 * and writes it to either host-side gdp daemon or Namespace DB
 */

#include <string.h>
#include "str.h"

#include "serviceDiscoveryInt.h"
#include "vmware.h"
#include "conf.h"
#include "guestApp.h"
#include "dynbuf.h"
#include "util.h"
#include "vmcheck.h"
#include "vmware/guestrpc/serviceDiscovery.h"
#include "vmware/tools/threadPool.h"
#include "vmware/tools/utils.h"

#include "vmware/tools/guestrpc.h"

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif

#if defined(_WIN32)
#include <windows.h>
#endif


#define NSDB_PRIV_GET_VALUES_CMD "namespace-priv-get-values"
#define NSDB_PRIV_SET_KEYS_CMD "namespace-priv-set-keys"

#if defined (_WIN32)

#define SCRIPT_EXTN ".ps1"

/*
 * Scripts used by plugin in Windows guests to capture information about
 * running services.
 */
#define SERVICE_DISCOVERY_SCRIPT_PERFORMANCE_METRICS "get-performance-metrics" SCRIPT_EXTN
#define SERVICE_DISCOVERY_WIN_SCRIPT_RELATIONSHIP "get-parent-child-rels" SCRIPT_EXTN
#define SERVICE_DISCOVERY_WIN_SCRIPT_NET "net-share" SCRIPT_EXTN
#define SERVICE_DISCOVERY_WIN_SCRIPT_IIS_PORTS "get-iis-ports-info" SCRIPT_EXTN
#define SERVICE_DISCOVERY_WIN_SCRIPT_SHAREPOINT_PORTS "get-sharepoint-ports-info" SCRIPT_EXTN

#else

#define SCRIPT_EXTN ".sh"

/*
 * Scripts used by plugin in Linux guests to capture information about
 * running services.
 */
#define SERVICE_DISCOVERY_SCRIPT_PERFORMANCE_METRICS \
        "get-listening-process-perf-metrics" SCRIPT_EXTN

#define _get_errno(p) (*p = errno)

#endif

static gchar* scriptInstallDir = NULL;

/*
 * Scripts used by plugin in both Windows and Linux guests to capture
 * information about running services.
 */
#define SERVICE_DISCOVERY_SCRIPT_PROCESSES "get-listening-process-info" SCRIPT_EXTN
#define SERVICE_DISCOVERY_SCRIPT_CONNECTIONS "get-connection-info" SCRIPT_EXTN
#define SERVICE_DISCOVERY_SCRIPT_VERSIONS "get-versions" SCRIPT_EXTN

/*
 * Default value for CONFNAME_SERVICE_DISCOVERY_DISABLED setting in
 * tools configuration file.
 */
#define SERVICE_DISCOVERY_CONF_DEFAULT_DISABLED_VALUE FALSE

/*
 * Polling interval of service discovery plugin in milliseconds
 */
#define SERVICE_DISCOVERY_POLL_INTERVAL 300000

/*
 * Time shift for comparison of time read from the signal and
 * current system time in milliseconds.
 */
#define SERVICE_DISCOVERY_WRITE_DELTA 60000

/*
 * Time to wait in milliseconds before RPC operation
 */
#define SERVICE_DISCOVERY_RPC_WAIT_TIME 100

/*
 * Defines the configuration to cache data in gdp plugin
 */
#define CONFNAME_SERVICEDISCOVERY_CACHEDATA "cache-data"
#define SERVICE_DISCOVERY_CONF_DEFAULT_CACHEDATA TRUE

/*
 * Define the configuration to require at least one subscriber subscribed for
 * the gdp message.
 *
 * TODO: SD maintainer to update default to TRUE when ready.
 */
#define CONFNAME_SERVICEDISCOVERY_REQUIRESUBS "require-subscribers"
#define SERVICE_DISCOVERY_CONF_DEFAULT_REQUIRESUBS FALSE

#define SERVICE_DISCOVERY_TOPIC_PREFIX "serviceDiscovery"

#if defined(VMX86_DEBUG)
/*
 * Defines the configuration to identify whether is in GDP debug mode
 *
 * Tools daemon restart is required to apply this setting's change
 */
#define CONFNAME_SERVICEDISCOVERY_GDP_DEBUG "gdp-debug"

/*
 * Defines the configuration to customize polling interval for GDP debug
 *
 * Tools daemon restart is required to apply this setting's change
 */
#define CONFNAME_SERVICEDISCOVERY_GDP_POLL_INTERVAL "poll-interval"

/*
 * Default polling interval of service discovery plugin for GDP debug in seconds
 */
#define SERVICE_DISCOVERY_CONF_GDP_DEBUG_POLL_INTERVAL 15

/*
 * Minimum polling interval of service discovery plugin for GDP debug in seconds
 */
#define SERVICE_DISCOVERY_CONF_MIN_POLL_INTERVAL 1

static Bool isGDPDebug = FALSE;
#endif

/*
 * Maximum number of keys that can be deleted by one operation
 */
#define SERVICE_DISCOVERY_DELETE_CHUNK_SIZE 25

/*
 * GdpError message table.
 * From GDP_ERR_ITEM tuple:
 *   - GdpEnum name
 *   - error-id string id
 *   - Default error message string
 */
#define GDP_ERR_ITEM(a, b, c) c,
static const char * const gdpErrMsgs[] = {
GDP_ERR_LIST
};
#undef GDP_ERR_ITEM

typedef struct {
   gchar *keyName;
   gchar *val;
} KeyNameValue;

static KeyNameValue gKeyScripts[] = {
   { SERVICE_DISCOVERY_KEY_PROCESSES, SERVICE_DISCOVERY_SCRIPT_PROCESSES },
   { SERVICE_DISCOVERY_KEY_CONNECTIONS,
     SERVICE_DISCOVERY_SCRIPT_CONNECTIONS },
   { SERVICE_DISCOVERY_KEY_PERFORMANCE_METRICS,
     SERVICE_DISCOVERY_SCRIPT_PERFORMANCE_METRICS },
   { SERVICE_DISCOVERY_KEY_VERSIONS, SERVICE_DISCOVERY_SCRIPT_VERSIONS },
#if defined(_WIN32)
   { SERVICE_DISCOVERY_WIN_KEY_RELATIONSHIP,
     SERVICE_DISCOVERY_WIN_SCRIPT_RELATIONSHIP },
   { SERVICE_DISCOVERY_WIN_KEY_IIS_PORTS,
     SERVICE_DISCOVERY_WIN_SCRIPT_IIS_PORTS },
   { SERVICE_DISCOVERY_WIN_KEY_SHAREPOINT_PORTS,
     SERVICE_DISCOVERY_WIN_SCRIPT_SHAREPOINT_PORTS },
   { SERVICE_DISCOVERY_WIN_KEY_NET, SERVICE_DISCOVERY_WIN_SCRIPT_NET },
#endif
};

static GSource *gServiceDiscoveryTimeoutSource = NULL;
static gint64 gLastWriteTime = 0;

static GArray *gFullPaths = NULL;
static Atomic_Bool gTaskSubmitted = { FALSE }; // Task has been submitted.

static size_t readBytesPerCycle = 0;
static size_t cycle = 0;
static Bool isGDPWriteReady = TRUE;
static Bool isNDBWriteReady = TRUE;

static Bool gSkipThisTask = FALSE; // Skip this task on some gdp errors.

/*
 *****************************************************************************
 * GetGuestTimeInMillis --
 *
 * Get system current time in millis.
 *
 * @retval time in millis.
 *
 *****************************************************************************
 */

static gint64
GetGuestTimeInMillis(void)
{
   return g_get_real_time() / 1000;
}

#if defined(VMX86_DEBUG)
/*
 *****************************************************************************
 * GetGDPPollInterval --
 *
 * @brief Get poll interval from tools config for debugging the GDP
 *
 * @param[in]  ctx     The application context
 *
 * @retval Poll interval in milli-seconds.
 *
 *****************************************************************************
 */

static gint
GetGDPPollInterval(ToolsAppCtx *ctx)
{
   gint pollInterval =
      VMTools_ConfigGetInteger(ctx->config,
                               CONFGROUPNAME_SERVICEDISCOVERY,
                               CONFNAME_SERVICEDISCOVERY_GDP_POLL_INTERVAL,
                               SERVICE_DISCOVERY_CONF_GDP_DEBUG_POLL_INTERVAL);
   if (pollInterval < SERVICE_DISCOVERY_CONF_MIN_POLL_INTERVAL) {
      g_info("%s: Service discovery minimum poll interval is enforced.\n",
             __FUNCTION__);
      pollInterval = SERVICE_DISCOVERY_CONF_MIN_POLL_INTERVAL;
   }

   return pollInterval * 1000;
}
#endif

/*
 *****************************************************************************
 * SendRpcMessage --
 *
 * Sends message over RPC channel.
 *
 * @param[in] ctx         Application context.
 * @param[in] msg         Message to send
 * @param[in] msgLen      Length of the message to send
 * @param[out] result     Rpc operation result, freed by callers
 * @param[out] resultLen  Length of Rpc operation result
 *
 * @retval TRUE  RPC message send succeeded.
 * @retval FALSE RPC message send failed.
 *
 *****************************************************************************
 */

static Bool
SendRpcMessage(ToolsAppCtx *ctx,
               char const *msg,
               size_t msgLen,
               char **result,
               size_t *resultLen)
{
   Bool status;
   RpcChannelType rpcChannelType = RpcChannel_GetType(ctx->rpc);

   g_debug("%s: Current RPC channel type: %d\n", __FUNCTION__, rpcChannelType);

   if (rpcChannelType == RPCCHANNEL_TYPE_PRIV_VSOCK) {
      status = RpcChannel_Send(ctx->rpc, msg, msgLen, result, resultLen);
   } else {
      /*
       * After the vmsvc RPC channel falls back to backdoor, it could not
       * send through privileged guest RPC any more.
       */
      g_usleep(SERVICE_DISCOVERY_RPC_WAIT_TIME * 1000);
      status = RpcChannel_SendOneRawPriv(msg, msgLen, result, resultLen);

      /*
       * RpcChannel_SendOneRawPriv returns RPCCHANNEL_SEND_PERMISSION_DENIED
       * if the privileged vsocket can not be established.
       */
      if (!status && result != NULL &&
          strcmp(*result, RPCCHANNEL_SEND_PERMISSION_DENIED) == 0) {
         g_debug("%s: Retrying RPC send\n", __FUNCTION__);
         free(*result);
         g_usleep(SERVICE_DISCOVERY_RPC_WAIT_TIME * 1000);
         status = RpcChannel_SendOneRawPriv(msg, msgLen, result, resultLen);
      }
   }

   return status;
}

/*
 *****************************************************************************
 * SendData --
 *
 * Sends guest data to host-side gdp daemon.
 *
 * @param[in] ctx         The application context
 * @param[in] createTime  Data create time
 * @param[in] topic       Data topic
 * @param[in] data        Service data
 * @param[in] len         Service data length
 *
 * @retval TRUE  On success.
 * @retval FALSE Failed.
 *
 *****************************************************************************
 */

Bool
SendData(ToolsAppCtx *ctx,
         gint64 createTime,
         const char *topic,
         const char *data,
         const int len)
{
   GdpError gdpErr;
   Bool status = FALSE;
   Bool cacheData = VMTools_ConfigGetBoolean(
                       ctx->config,
                       CONFGROUPNAME_SERVICEDISCOVERY,
                       CONFNAME_SERVICEDISCOVERY_CACHEDATA,
                       SERVICE_DISCOVERY_CONF_DEFAULT_CACHEDATA);
   Bool requireSubs = VMTools_ConfigGetBoolean(
                         ctx->config,
                         CONFGROUPNAME_SERVICEDISCOVERY,
                         CONFNAME_SERVICEDISCOVERY_REQUIRESUBS,
                         SERVICE_DISCOVERY_CONF_DEFAULT_REQUIRESUBS);

   gdpErr = ToolsPluginSvcGdp_Publish(ctx,
                                      createTime,
                                      topic,
                                      NULL, /* token (optional) */
                                      NULL, /* category (optional) */
                                      data,
                                      len,
                                      cacheData,
                                      requireSubs);
   if (gdpErr != GDP_ERROR_SUCCESS) {
      g_info("%s: ToolsPluginSvcGdp_Publish error: %s\n",
             __FUNCTION__, gdpErrMsgs[gdpErr]);
      /* NOTE to SD maintainer: gdpErr == GDP_ERROR_NO_SUBSCRIBERS to be handled here when ready*/
      if (gdpErr == GDP_ERROR_STOP ||
          gdpErr == GDP_ERROR_UNREACH ||
          gdpErr == GDP_ERROR_TIMEOUT) {
         gSkipThisTask = TRUE;
      }
   } else {
      status = TRUE;
   }

   return status;
}

/*
 *****************************************************************************
 * fread_safe --
 *
 * A wrapper of C runtime library fread() with almost same signature except
 * the item size is always 1 byte. It ensures that when the returned number
 * of bytes is less than the input buffer size in bytes, an error has occurred
 * or the end of the file is encountered.
 *
 * @param [out] buf     Pointer to a block of memory with a size of at least
 *                      (size) bytes, converted to a void*.
 * @param [in] size     Size, in bytes, of each element to be read.
 * @param [in] stream   Pointer to a FILE object that specifies an input stream.
 * @param [out] eof     Indicates whether end of file is reached.
 *
 * @retval The total number of elements successfully read is returned.
 *
 *****************************************************************************
 */

static size_t
fread_safe(void *buf,
           size_t size,
           FILE *stream,
           Bool *eof)
{
   size_t readBytes = 0;

   while (readBytes < size) {
      size_t localReadBytes;

      /*
       * fread is a blocking call.
       */
      localReadBytes = fread((char *)buf + readBytes, 1,
                             size - readBytes, stream);

      if (ferror(stream)) {
         int error_code = 0;
         _get_errno(&error_code);
         g_info("%s: fread returned %"FMTSZ"u with errno=%d\n",
                __FUNCTION__, localReadBytes, error_code);
         break;
      }

      readBytes += localReadBytes;

      if (feof(stream)) {
         g_debug("%s: fread reached end of file\n",
                 __FUNCTION__);
         *eof = TRUE;
         break;
      }
   }

   return readBytes;
}

/*
 *****************************************************************************
 * WriteData --
 *
 * Sends key-value update request to the Namespace DB.
 *
 * @param[in] ctx       Application context.
 * @param[in] key       Key sent to the Namespace DB
 * @param[in] value     Service data sent to the Namespace DB
 * @param[in] len       Service data len
 *
 * @retval TRUE  Namespace DB write over RPC succeeded.
 * @retval FALSE Namespace DB write over RPC failed.
 *
 *****************************************************************************
 */

Bool
WriteData(ToolsAppCtx *ctx,
          const char *key,
          const char *data,
          const size_t len)
{
   Bool status = FALSE;
   DynBuf buf;
   gchar *timeStamp = NULL;

   if (data != NULL) {
      timeStamp = g_strdup_printf("%" G_GINT64_FORMAT, gLastWriteTime);
   }

   DynBuf_Init(&buf);

   /*
    * Format is:
    *
    * namespace-set-keys <namespace>\0<numOps>\0<op>\0<key>\0<value>\0<oldVal>
    *
    * We have just a single op, and want to always set the value, clobbering
    * anything already there.
    */
   if (!DynBuf_Append(&buf, NSDB_PRIV_SET_KEYS_CMD,
                      strlen(NSDB_PRIV_SET_KEYS_CMD)) ||
       !DynBuf_Append(&buf, " ", 1) ||
       !DynBuf_AppendString(&buf, SERVICE_DISCOVERY_NAMESPACE_DB_NAME) ||
       !DynBuf_AppendString(&buf, "1") || // numOps
       !DynBuf_AppendString(&buf, "0") || // op 0 == setAlways
       !DynBuf_AppendString(&buf, key)) {
      g_warning("%s: Could not construct buffer header\n", __FUNCTION__);
      goto out;
   }

   if (data != NULL) {
      if (!DynBuf_Append(&buf, timeStamp, strlen(timeStamp)) ||
          !DynBuf_Append(&buf, ",", 1) ||
          !DynBuf_Append(&buf, data, len) ||
          !DynBuf_Append(&buf, "", 1)) {
         g_warning("%s: Could not construct write buffer\n", __FUNCTION__);
         goto out;
      }
   } else {
      if (!DynBuf_Append(&buf, "", 1)) {
         g_warning("%s: Could not construct delete buffer\n", __FUNCTION__);
         goto out;
      }
   }

   if (!DynBuf_Append(&buf, "", 1)) {
      g_warning("%s: Could not construct buffer footer\n", __FUNCTION__);
      goto out;
   } else {
      char *result = NULL;
      size_t resultLen;

      status = SendRpcMessage(ctx, DynBuf_Get(&buf), DynBuf_GetSize(&buf),
                              &result, &resultLen);
      if (!status) {
         g_warning("%s: Failed to update %s, result: %s resultLen: %" FMTSZ
                   "u\n", __FUNCTION__, key, (result != NULL) ?
                   result : "(null)", resultLen);
      }

      if (result != NULL) {
         free(result);
      }
   }

out:
   DynBuf_Destroy(&buf);
   g_free(timeStamp);

   return status;
}


/*
 *****************************************************************************
 * ReadData --
 *
 * Reads value from Namespace DB by given key.
 *
 * @param[in] ctx             Application context.
 * @param[in] key             Key sent to the Namespace DB
 * @param[out] resultData     Data fetched from Namespace DB, freed by callers
 * @param[out] resultDataLen  Length of data fetched from Namespace DB
 *
 * @retval TRUE  Namespace DB read over RPC succeeded.
 * @retval FALSE Namespace DB read over RPC failed.
 *
 *****************************************************************************
 */

static Bool
ReadData(ToolsAppCtx *ctx,
         const char *key,
         char **resultData,
         size_t *resultDataLen)
{
   DynBuf buf;
   Bool status = FALSE;

   ASSERT(key);

   *resultData = NULL;
   *resultDataLen = 0;

   DynBuf_Init(&buf);

   /*
    * Format is
    *
    * namespace-get-values <namespace>\0<key>\0...
    *
    */
   if (!DynBuf_Append(&buf, NSDB_PRIV_GET_VALUES_CMD,
                      strlen(NSDB_PRIV_GET_VALUES_CMD)) ||
       !DynBuf_Append(&buf, " ", 1) ||
       !DynBuf_AppendString(&buf, SERVICE_DISCOVERY_NAMESPACE_DB_NAME) ||
       !DynBuf_AppendString(&buf, key)) {
      g_warning("%s: Could not construct request buffer\n", __FUNCTION__);
      goto done;
    }

   status = SendRpcMessage(ctx, DynBuf_Get(&buf), DynBuf_GetSize(&buf),
                           resultData, resultDataLen);
   if (!status) {
      g_debug("%s: Read over RPC failed, result: %s, resultDataLen: %" FMTSZ
              "u\n", __FUNCTION__, (*resultData != NULL) ?
              *resultData : "(null)", *resultDataLen);
   }
done:
   DynBuf_Destroy(&buf);
   return status;
}

/*
 *****************************************************************************
 * DepleteReadFromStream --
 *
 * Reads from stream and appends to the output dynamic buffer
 *
 * @param[in]  s            Stream
 * @param[out] out          Output buffer
 *
 *****************************************************************************
 */

void
DepleteReadFromStream(FILE *s,
                      DynBuf *out)
{
   for (;;) {
      size_t readBytes;
      char buf[SERVICE_DISCOVERY_VALUE_MAX_SIZE];

      readBytes = fread(buf, 1, sizeof(buf), s);

      g_debug("%s: readBytes = %"FMTSZ"u\n", __FUNCTION__, readBytes);

      if (readBytes > 0) {
         DynBuf_Append(out, buf, readBytes);
      }

      if (readBytes < sizeof(buf)) {
         break;
      }
   }
}


/*
 *****************************************************************************
 * DeleteData --
 *
 * Deletes keys/values from Namespace DB.
 *
 * @param[in] ctx       Application context.
 * @param[in] keys      Keys of entries to be deleted from the Namespace DB
 *
 * @retval TRUE  Namespace DB delete over RPC succeeded.
 * @retval FALSE Namespace DB delete over RPC failed or command buffer has not
 *               been constructed correctly.
 *
 *****************************************************************************
 */

static Bool
DeleteData(ToolsAppCtx *ctx,
           const GPtrArray* keys)
{
   Bool status = FALSE;
   DynBuf buf;
   int i;
   gchar *numKeys = g_strdup_printf("%d", keys->len);

   DynBuf_Init(&buf);

   /*
    * Format is:
    *
    * namespace-set-keys <namespace>\0<numOps>\0<op>\0<key>\0<value>\0<oldVal>
    *
    */
   if (!DynBuf_Append(&buf, NSDB_PRIV_SET_KEYS_CMD,
                      strlen(NSDB_PRIV_SET_KEYS_CMD)) ||
       !DynBuf_Append(&buf, " ", 1) ||
       !DynBuf_AppendString(&buf, SERVICE_DISCOVERY_NAMESPACE_DB_NAME) ||
       !DynBuf_AppendString(&buf, numKeys)) { // numOps
      g_warning("%s: Could not construct buffer header\n", __FUNCTION__);
      goto out;
   }
   for (i = 0; i < keys->len; ++i) {
      const char *key = (const char *) g_ptr_array_index(keys, i);
      g_debug("%s: Adding key %s to buffer\n", __FUNCTION__, key);
      if (!DynBuf_AppendString(&buf, "0") ||
          !DynBuf_AppendString(&buf, key) ||
          !DynBuf_Append(&buf, "", 1) ||
          !DynBuf_Append(&buf, "", 1)) {
         g_warning("%s: Could not construct delete buffer\n", __FUNCTION__);
         goto out;
      }
   }
   if (!DynBuf_Append(&buf, "", 1)) {
      g_warning("%s: Could not construct buffer footer\n", __FUNCTION__);
      goto out;
   } else {
      char *result = NULL;
      size_t resultLen;

      status = SendRpcMessage(ctx, DynBuf_Get(&buf), DynBuf_GetSize(&buf),
                              &result, &resultLen);
      if (!status) {
         g_warning("%s: Failed to delete keys, result: %s resultLen: %" FMTSZ
                   "u\n", __FUNCTION__, (result != NULL) ? result : "(null)",
                   resultLen);
      }

      free(result);
   }

out:
   DynBuf_Destroy(&buf);
   g_free(numKeys);
   return status;
}

/*
 *****************************************************************************
 * SendScriptOutput --
 *
 * Reads script child process stdout stream, sends output to
 * host-side gdp daemon and/or namespace DB.
 *
 * Stream output data are cut into chunks with chunk size of 16K for namespace
 * DB and 48K for gdp daemon. If there are multiple chunks of data, each chunk
 * is sent to gdp daemon/namespace db separately with its chunk number in the
 * topic.
 *
 * @param[in] ctx             The application context
 * @param[in] key             Script name
 * @param[in] childStdout     Stream to read child process stdout
 *
 * @retval TRUE  Successfully sent output.
 * @retval FALSE Otherwise.
 *
 *****************************************************************************
 */

Bool
SendScriptOutput(ToolsAppCtx *ctx,
                 const char *key,
                 FILE* childStdout)
{
   Bool status = TRUE;
   Bool gdp_status = TRUE;
   int i = 0;
   size_t totalReadBytes = 0;
   gint64 createTime = g_get_real_time();
   size_t ndbBufSize = SERVICE_DISCOVERY_VALUE_MAX_SIZE * sizeof(char);
   for (;;) {
      size_t readBytes;
      char buf[GDP_USER_DATA_LEN];
      Bool eof = FALSE;
      readBytes = fread_safe(buf, sizeof(buf), childStdout, &eof);

      totalReadBytes += readBytes;
      g_debug("%s: DB readBytes = %"FMTSZ"u\n", __FUNCTION__,
              readBytes);
      if (isGDPWriteReady && gdp_status && readBytes > 0) {
         g_debug("%s:%s Write to GDP readBytes = %"FMTSZ"u\n",
                 __FUNCTION__, key, readBytes);
         gchar* topic;

         if (eof || readBytes < sizeof(buf)) {
            topic = g_strdup_printf(SERVICE_DISCOVERY_TOPIC_PREFIX ".%s.%"FMTSZ
                                    "u.%"FMTSZ"u", key, cycle, totalReadBytes);
         } else {
            topic = g_strdup_printf(SERVICE_DISCOVERY_TOPIC_PREFIX ".%s.%"FMTSZ
                                    "u", key, cycle);
         }
         gdp_status = SendData(ctx, createTime, topic, buf, (int)readBytes);
         readBytesPerCycle += readBytes;
         g_free(topic);
      }

      if (isNDBWriteReady) {
         size_t ndbReadBytes = 0;
         size_t j;
         for (j = 0; j < readBytes; j += ndbReadBytes) {
            if (j + ndbBufSize > readBytes) {
               ndbReadBytes = readBytes - j;
            } else {
               ndbReadBytes = ndbBufSize;
            }
            if (status && ndbReadBytes > 0) {
               g_debug("%s:%s Write to Namespace DB readBytes = %"FMTSZ"u\n",
                       __FUNCTION__, key, ndbReadBytes);

               gchar* msg = g_strdup_printf("%s-%d", key, ++i);
               status = WriteData(ctx, msg, buf + j, ndbReadBytes);
               if (!status) {
                   g_warning("%s: Failed to store data\n", __FUNCTION__);
               }
               g_free(msg);
            }
         }
      }

      /*
       * Exit the loop only after childStdout is not readable any more.
       * Otherwise, the child process may be blocked in writing its stdout
       * and hang.
       */
      if (eof || readBytes < sizeof(buf)) {
         break;
      }

   }

   if (isNDBWriteReady && status) {
      gchar *chunkCount = g_strdup_printf("%d", i);
      status = WriteData(ctx, key, chunkCount, strlen(chunkCount));
      if (status) {
         g_debug("%s: Written key %s chunks %s\n", __FUNCTION__, key, chunkCount);
      }
      g_free(chunkCount);
   }

   return status && gdp_status;
}

/*
 *****************************************************************************
 * DeleteDataAndFree --
 *
 * Deletes the specified keys in Namespace DB and frees memory
 * for every key.
 *
 * @param[in] ctx           Application context.
 * @param[in/out] keys      Keys to be deleted.
 *
 *****************************************************************************
 */

static void
DeleteDataAndFree(ToolsAppCtx *ctx,
                  GPtrArray *keys) {
   int j;

   if (!DeleteData(ctx, keys)) {
      g_warning("%s: Failed to delete data\n", __FUNCTION__);
   }
   for (j = 0; j < keys->len; ++j) {
      g_free((gchar *) g_ptr_array_index(keys, j));
   }
   g_ptr_array_set_size(keys, 0);
}


/*
 *****************************************************************************
 * CleanupNamespaceDB --
 *
 * Deletes all the chunks written to the Namespace DB in previous cycle.
 *
 * @param[in] ctx       Application context.
 *
 *****************************************************************************
 */

static void
CleanupNamespaceDB(ToolsAppCtx *ctx) {
   int i;
   GPtrArray *keys = g_ptr_array_new();

   g_debug("%s: Performing cleanup of previous data\n", __FUNCTION__);

   ASSERT(gFullPaths);

   for (i = 0; i < gFullPaths->len; i++) {
      char *value = NULL;
      size_t len = 0;
      KeyNameValue tmp = g_array_index(gFullPaths, KeyNameValue, i);
      /*
       * Read count of chunks, ignore timestamp, iterate over chunks
       * and remove them.
       */
      if (ReadData(ctx, tmp.keyName, &value, &len) && len > 1) {
         char *token = NULL;
         g_debug("%s: Read %s from Namespace DB\n", __FUNCTION__, value);

         g_ptr_array_add(keys, g_strdup(tmp.keyName));
         if (keys->len >= SERVICE_DISCOVERY_DELETE_CHUNK_SIZE) {
            DeleteDataAndFree(ctx, keys);
         }

         if (NULL == strtok(value, ",")) {
            g_warning("%s: Malformed data for %s in Namespace DB",
                      __FUNCTION__, tmp.keyName);
            if (value) {
               free(value);
               value = NULL;
            }
            continue;
         }
         token = strtok(NULL, ",");
         if (token != NULL) {
            int count = (int) g_ascii_strtoll(token, NULL, 10);
            int j;

            for (j = 0; j < count; j++) {
               gchar *msg = g_strdup_printf("%s-%d", tmp.keyName, j + 1);
               g_ptr_array_add(keys, msg);
               if (keys->len >= SERVICE_DISCOVERY_DELETE_CHUNK_SIZE) {
                  DeleteDataAndFree(ctx, keys);
               }
            }
         } else {
            g_warning("%s: Chunk count has invalid value %s", __FUNCTION__,
                      value);
         }
      } else {
         g_warning("%s: Key %s not found in Namespace DB\n", __FUNCTION__,
                   tmp.keyName);
      }
      if (value) {
         free(value);
         value = NULL;
      }
   }
   if (keys->len >= 1) {
      DeleteDataAndFree(ctx, keys);
   }
   g_ptr_array_free(keys, TRUE);
}


/*
 *****************************************************************************
 * ServiceDiscoveryTask --
 *
 * Task to gather discovered services' data and write to Namespace DB.
 *
 * @param[in] ctx             Application context.
 * @param[in] data            Data pointer, not used.
 *
 *****************************************************************************
 */

static void
ServiceDiscoveryTask(ToolsAppCtx *ctx,
                     void *data)
{
   int i;
   Bool status = FALSE;
   Atomic_WriteBool(&gTaskSubmitted, TRUE);
   if (isGDPWriteReady) {
      gSkipThisTask = FALSE;
   }
   if (isNDBWriteReady) {
      gint64 previousWriteTime = gLastWriteTime;

      /*
       * We are going to write to Namespace DB, update gLastWriteTime
       */
      gLastWriteTime = GetGuestTimeInMillis();

      /*
       * Reset "ready" flag to stop readers until all data is written
       */
      status = WriteData(ctx, SERVICE_DISCOVERY_KEY_READY, "FALSE", 5);
      if (!status) {
         gLastWriteTime = previousWriteTime;
         g_warning("%s: Failed to reset %s flag", __FUNCTION__,
                   SERVICE_DISCOVERY_KEY_READY);
         if (!isGDPWriteReady) {
            Atomic_WriteBool(&gTaskSubmitted, FALSE);
            return;
         }
      }

      /*
       * Remove chunks written to DB in the previous iteration
       */
      CleanupNamespaceDB(ctx);
   }

   readBytesPerCycle = 0;
   cycle++;
   for (i = 0; i < gFullPaths->len; i++) {
      KeyNameValue tmp = g_array_index(gFullPaths, KeyNameValue, i);
      if (!ExecuteScript(ctx, tmp.keyName, tmp.val, scriptInstallDir)) {
         g_debug("%s: ExecuteScript failed for script %s\n",
                __FUNCTION__, tmp.val);
         if (isGDPWriteReady && gSkipThisTask && !isNDBWriteReady) {
            break;
         }
      }
   }

   if (isGDPWriteReady && !gSkipThisTask) {
      gchar* readyData = g_strdup_printf("%"FMTSZ"u", readBytesPerCycle);
      g_debug("%s: Sending ready flag with number of read bytes :%s\n",
             __FUNCTION__, readyData);
      gchar* topic = g_strdup_printf(SERVICE_DISCOVERY_TOPIC_PREFIX ".%s.%"
                                     FMTSZ"u", "ready", cycle);
      SendData(ctx, g_get_real_time(), topic, readyData, strlen(readyData));
      g_free(topic);
      g_free(readyData);
   }

   if (isNDBWriteReady) {
      /*
       * Update ready flag
       */
      status = WriteData(ctx, SERVICE_DISCOVERY_KEY_READY, "TRUE", 4);
      if (!status) {
         g_warning("%s: Failed to update ready flag", __FUNCTION__);
      }
   }

   Atomic_WriteBool(&gTaskSubmitted, FALSE);
}

/*
 *****************************************************************************
 * checkForWrite --
 *
 * Performs needed checks to decide if Data should be written to Namespace DB
 * or not.
 *
 * First check - checks if interval related information, stored in Namespace DB
 * under key "signal" and in format of "interval,timestamp" is outdated or not.
 *
 * Second check - checks if time greater than interval read from Namespace DB
 * has elapsed since the last write operation.
 *
 * @param[in] ctx           The application context.
 * @param[in] signalKey     Signal key to check the write readiness of
 *                          Namespace DB or gdp.
 *
 * @retval TRUE  Execute scripts and write service data to Namespace DB or gdp
 * @retval FALSE Omit this cycle wihtout any script running.
 *
 *****************************************************************************
 */

static Bool
checkForWrite(ToolsAppCtx *ctx, const char *signalKey)
{
   char *signal = NULL;
   size_t signalLen = 0;
   Bool result = FALSE;

   /*
    * Read signal from Namespace DB
    */
   if (!ReadData(ctx, signalKey, &signal, &signalLen)) {
      g_debug("%s: Failed to read necessary information from Namespace DB\n",
              __FUNCTION__);
   } else {
      if ((signal != NULL) && (strcmp(signal, "")) && signalLen > 0) {
         char *token1;
         char *token2;
         g_debug("%s: signal = %s last write time = %" G_GINT64_FORMAT "\n",
                 __FUNCTION__, signal, gLastWriteTime);
         /*
          * parse signal, it should be in "interval,timestamp" format
          */
         token1 = strtok(signal, ",");
         token2 = strtok(NULL, ",");
         if (token1 != NULL && token2 != NULL) {
            gint64 currentTime = GetGuestTimeInMillis();
            int clientInterval = (int) g_ascii_strtoll(token1, NULL, 10);
            gint64 clientTimestamp = g_ascii_strtoll(token2, NULL, 10);

            if (clientInterval == 0 || clientTimestamp == 0) {
               g_warning("%s: Wrong value of interval and timestamp",
                         __FUNCTION__);
            } else if ((currentTime - clientTimestamp) < (5 * clientInterval)) {
               if ((currentTime - gLastWriteTime +
                   SERVICE_DISCOVERY_WRITE_DELTA) >= clientInterval) {
                  result = TRUE;
               }
            } else {
               /*
               * Signal is outdated, reset the last write time
               */
               gLastWriteTime = 0;
            }

            g_debug("%s: result=%s client interval = %d "
                    "client timestamp =% " G_GINT64_FORMAT
                    " system time = %" G_GINT64_FORMAT
                    " previous write time = %" G_GINT64_FORMAT "\n",
                    __FUNCTION__, result ? "true" : "false", clientInterval,
                    clientTimestamp, currentTime, gLastWriteTime);
         } else {
            g_warning("%s: Wrong value of signal", __FUNCTION__);
         }
      } else {
         g_warning("%s: signal was NULL or empty", __FUNCTION__);
      }
   }

   if (signal) {
      free(signal);
   }

   return result;
}

/*
 *****************************************************************************
 * ServiceDiscoveryThread --
 *
 * Creates a new task thread that gathers discovered services' data and
 * publishes the data to either Namespace DB or host-side gdp daemon.
 *
 * @param[in]  data     The application context.
 *
 * @return TRUE to indicate that the timer should be rescheduled.
 *
 *****************************************************************************
 */

static Bool
ServiceDiscoveryThread(gpointer data)
{
   ToolsAppCtx *ctx = data;
   #if defined(VMX86_DEBUG)
   if (isGDPDebug) {
      isGDPWriteReady = TRUE;
      isNDBWriteReady = FALSE;
   } else {
      isGDPWriteReady = checkForWrite(ctx, SERVICE_DISCOVERY_KEY_GDP_SIGNAL);
      isNDBWriteReady = checkForWrite(ctx, SERVICE_DISCOVERY_KEY_SIGNAL);
   }
   #else
   isGDPWriteReady = checkForWrite(ctx, SERVICE_DISCOVERY_KEY_GDP_SIGNAL);
   isNDBWriteReady = checkForWrite(ctx, SERVICE_DISCOVERY_KEY_SIGNAL);
   #endif

   /*
    * First check for taskSubmitted, if it is true automatically omit this
    * cycle even without checking for write to avoid resetting last write
    * time.
    */
   if (Atomic_ReadBool(&gTaskSubmitted)) {
      g_debug("%s: Previously submitted task is not completed\n", __FUNCTION__);
   } else if (!isNDBWriteReady && !isGDPWriteReady) {
      g_debug("%s: Neither Namespace DB nor GDP is ready for writing\n",
              __FUNCTION__);
   } else {
      g_debug("%s: Submitting task to write\n", __FUNCTION__);
      if (!ToolsCorePool_SubmitTask(ctx, ServiceDiscoveryTask, NULL, NULL)) {
         g_warning("%s: failed to start information gather thread\n",
                   __FUNCTION__);
      }
   }

   return TRUE;
}


/*
 *****************************************************************************
 * TweakDiscoveryLoop --
 *
 * @brief Start service discovery poll loop.
 *
 * @param[in] ctx  The app context.
 *
 *****************************************************************************
 */

static void
TweakDiscoveryLoop(ToolsAppCtx *ctx)
{
   if (gServiceDiscoveryTimeoutSource == NULL) {
      gint pollInterval = SERVICE_DISCOVERY_POLL_INTERVAL;
      #if defined(VMX86_DEBUG)
      isGDPDebug =
               VMTools_ConfigGetBoolean(ctx->config,
                                        CONFGROUPNAME_SERVICEDISCOVERY,
                                        CONFNAME_SERVICEDISCOVERY_GDP_DEBUG,
                                        FALSE);
      if (isGDPDebug) {
         pollInterval = GetGDPPollInterval(ctx);
         g_info("%s: GDP Debug is enabled with %d ms polling interval\n",
                __FUNCTION__, pollInterval);
      }
      #endif
      gServiceDiscoveryTimeoutSource =
                          g_timeout_source_new(pollInterval);
      VMTOOLSAPP_ATTACH_SOURCE(ctx, gServiceDiscoveryTimeoutSource,
                               ServiceDiscoveryThread, ctx, NULL);
      g_source_unref(gServiceDiscoveryTimeoutSource);
   }
}


/*
 ******************************************************************************
 * ServiceDiscoveryServerConfReload --
 *
 * @brief Reconfigures the poll loop interval upon config file reload.
 *
 * @param[in]  src     Unused.
 * @param[in]  ctx     The application context.
 * @param[in]  data    Unused.
 *
 ******************************************************************************
 */

static void
ServiceDiscoveryServerConfReload(gpointer src,
                                 ToolsAppCtx *ctx,
                                 gpointer data)
{
   gboolean disabled =
      VMTools_ConfigGetBoolean(ctx->config,
                               CONFGROUPNAME_SERVICEDISCOVERY,
                               CONFNAME_SERVICEDISCOVERY_DISABLED,
                               SERVICE_DISCOVERY_CONF_DEFAULT_DISABLED_VALUE);
   if (!disabled) {
      g_info("%s: Service discovery loop started\n", __FUNCTION__);
      TweakDiscoveryLoop(ctx);
   } else if (gServiceDiscoveryTimeoutSource != NULL) {
      gLastWriteTime = 0;
      g_source_destroy(gServiceDiscoveryTimeoutSource);
      gServiceDiscoveryTimeoutSource = NULL;
      g_info("%s: Service discovery loop disabled\n", __FUNCTION__);
   }
}


/*
 *****************************************************************************
 * ServiceDiscoveryServerShutdown --
 *
 * Cleanup internal data on shutdown.
 *
 * @param[in]  src     The source object.
 * @param[in]  ctx     Unused.
 * @param[in]  data    Unused.
 *
 *****************************************************************************
 */

static void
ServiceDiscoveryServerShutdown(gpointer src,
                               ToolsAppCtx *ctx,
                               gpointer data)
{
   if (gServiceDiscoveryTimeoutSource != NULL) {
      g_source_destroy(gServiceDiscoveryTimeoutSource);
      gServiceDiscoveryTimeoutSource = NULL;
   }

   g_free(scriptInstallDir);
   scriptInstallDir = NULL;

   if (gFullPaths != NULL) {
      int i = 0;
      guint len = gFullPaths->len;
      for (i = 0; i < len; ++i) {
         g_free(g_array_index(gFullPaths, KeyNameValue, i).keyName);
         g_free(g_array_index(gFullPaths, KeyNameValue, i).val);
      }
      g_array_free(gFullPaths, TRUE);
   }
}

/*
 *****************************************************************************
 * ConstructScriptPaths --
 *
 * Construct final paths of the scripts that will be used for execution.
 *
 *****************************************************************************
 */

static void
ConstructScriptPaths(void)
{
   int i;
#if !defined(OPEN_VM_TOOLS)
   gchar *toolsInstallDir;
#endif

   if (gFullPaths != NULL) {
      return;
   }

   gFullPaths = g_array_sized_new(FALSE, TRUE, sizeof(KeyNameValue),
                                  ARRAYSIZE(gKeyScripts));
   if (scriptInstallDir == NULL) {
#if defined(OPEN_VM_TOOLS)
      scriptInstallDir = Util_SafeStrdup(VMTOOLS_SERVICE_DISCOVERY_SCRIPTS);
#else
      toolsInstallDir = GuestApp_GetInstallPath();
      scriptInstallDir = g_strdup_printf("%s%s%s%s%s", toolsInstallDir, DIRSEPS,
                                      "serviceDiscovery", DIRSEPS, "scripts");
      g_free(toolsInstallDir);
#endif
   }
   for (i = 0; i < ARRAYSIZE(gKeyScripts); ++i) {
      KeyNameValue tmp;
      tmp.keyName = g_strdup_printf("%s", gKeyScripts[i].keyName);
#if defined(_WIN32)
      tmp.val = ConstructPWSScriptCommand(gKeyScripts[i].val);
#else
      tmp.val = g_strdup_printf("%s%s%s", scriptInstallDir, DIRSEPS, gKeyScripts[i].val);
#endif
      g_array_insert_val(gFullPaths, i, tmp);
   }
}


/*
 *****************************************************************************
 * ToolsOnLoad --
 *
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in]  ctx   The app context.
 *
 * @return The registration data.
 *
 *****************************************************************************
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "serviceDiscovery",
      NULL,
      NULL
   };

   uint32 vmxVersion = 0;
   uint32 vmxType = VMX_TYPE_UNSET;

   /*
    * Return NULL to disable the plugin if not running in a VMware VM.
    */
   if (!ctx->isVMware) {
      g_info("%s: Not running in a VMware VM.\n", __FUNCTION__);
      return NULL;
   }

   /*
    * Return NULL to disable the plugin if VM is not running on ESX host.
    */
   if (!VmCheck_GetVersion(&vmxVersion, &vmxType) ||
       vmxType != VMX_TYPE_SCALABLE_SERVER) {
      g_info("%s, VM is not running on ESX host.\n", __FUNCTION__);
      return NULL;
   }

   /*
    * Return NULL to disable the plugin if not running in vmsvc daemon.
    */
   if (!TOOLS_IS_MAIN_SERVICE(ctx)) {
      g_info("%s: Not running in vmsvc daemon: container name='%s'.\n",
             __FUNCTION__, ctx->name);
      return NULL;
   }
   if (ctx->rpc != NULL) {
      ToolsPluginSignalCb sigs[] = {
         { TOOLS_CORE_SIG_SHUTDOWN, ServiceDiscoveryServerShutdown, NULL },
         { TOOLS_CORE_SIG_CONF_RELOAD, ServiceDiscoveryServerConfReload, NULL }
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_SIGNALS,
           VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs))
         }
      };
      gboolean disabled;

      regData.regs = VMTools_WrapArray(regs,
                                       sizeof *regs,
                                       ARRAYSIZE(regs));
      /*
       * Append scripts execution command line
       */
      ConstructScriptPaths();

      disabled =
         VMTools_ConfigGetBoolean(ctx->config,
                                  CONFGROUPNAME_SERVICEDISCOVERY,
                                  CONFNAME_SERVICEDISCOVERY_DISABLED,
                                  SERVICE_DISCOVERY_CONF_DEFAULT_DISABLED_VALUE);
      if (!disabled) {
         TweakDiscoveryLoop(ctx);
      }

      return &regData;
   }

   return NULL;
}
07070100000391000081A4000000000000000000000001682255050000088A000000000000000000000000000000000000005B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/serviceDiscoveryInt.h    /*********************************************************
 * Copyright (C) 2020-2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _SERVICEDISCOVERYINT_H_
#define _SERVICEDISCOVERYINT_H_

/**
 * @file serviceDiscoveryInt.h
 *
 * Declares few constants for the serviceDiscovery plugin.
 */

#define G_LOG_DOMAIN "serviceDiscovery"

#include <stdlib.h>
#include <stdio.h>

#include "vm_basic_types.h"
#include "vm_atomic.h"
#include "dynbuf.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/gdp.h"

/*
 * Maximum length of the data (either key or value) written to Namespace DB
 */
#define SERVICE_DISCOVERY_VALUE_MAX_SIZE (1024*15)

Bool WriteData(ToolsAppCtx *ctx,
               const char *key,
               const char *data,
               const size_t len);

void DepleteReadFromStream(FILE *stream,
                           DynBuf *out);

Bool SendData(ToolsAppCtx *ctx,
              gint64 createTime,
              const char *key,
              const char *data,
              const int len);

Bool SendScriptOutput(ToolsAppCtx *ctx,
                      const char *key,
                      FILE *childStdout);

Bool ExecuteScript(ToolsAppCtx *ctx,
                   const char *key,
                   const char *script,
                   const char *workingDir);

#if defined (_WIN32)

char* ConstructPWSScriptCommand(const char *scriptFileName);

#endif

#endif /* _SERVICEDISCOVERYINT_H_ */
  07070100000392000081A400000000000000000000000168225505000015D9000000000000000000000000000000000000005D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/serviceDiscovery/serviceDiscoveryPosix.c  /*********************************************************
 * Copyright (C) 2020-2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * serviceDiscoveryPosix.c --
 *
 * Captures the information about services inside the guest
 * and writes it to Namespace DB.
 */

#ifndef __linux__
#   error This file should not be compiled.
#endif

#include "serviceDiscoveryInt.h"
#include "dynbuf.h"

#include <stdio.h>
#include <errno.h>
#include <unistd.h>


/*
 *****************************************************************************
 * ReadFromHandle --
 *
 * Reads from handle and appends to provided buffer. Handle is closed inside the
 * function after read operation.
 *
 * @param[in] h             handle.
 * @param[out] out          output buffer.
 *
 *****************************************************************************
 */

static void
ReadFromHandle(gint h,
               DynBuf* out)
{
   FILE* f = fdopen(h, "r");

   if (f == NULL) {
      g_warning("%s: Failed to create file stream, errno=%d",
                __FUNCTION__, errno);
      if (close(h) != 0) {
         g_warning("%s: Failed to close file handle, errno=%d",
                   __FUNCTION__, errno);
      }
   } else {
      DepleteReadFromStream(f, out);
      if (fclose(f) != 0) {
         g_warning("%s: Failed to close file stream, errno=%d",
                   __FUNCTION__, errno);
      }
   }
}

/*
 *****************************************************************************
 * ExecuteScript --
 *
 * Spawns child process for script, reads child process stdout stream and
 * sends data to Namespace DB and/or host-side gdp daemon.
 *
 * @param[in] ctx             The application context
 * @param[in] key             Script name
 * @param[in] script          Script to be executed
 * @param[in] workingDir      Script working dir
 *
 * @retval TRUE  Successfully executed script and sent output to gdp daemon.
 * @retval FALSE Otherwise.
 *
 *****************************************************************************
 */

Bool
ExecuteScript(ToolsAppCtx *ctx,
              const char *key,
              const char *script,
              const char *workingDir)
{
   Bool status;
   gchar *command = g_strdup(script);
   gchar *cmd[] = { command, NULL };
   gint child_stdout = -1;
   gint child_stderr = -1;
   FILE *child_stdout_f;
   GError *p_error = NULL;
   DynBuf err;

   status = g_spawn_async_with_pipes(workingDir, // const gchar *working_directory
                                     cmd, // gchar **argv
                                     NULL, // gchar **envp
                                     G_SPAWN_DEFAULT, // GSpawnFlags flags
                                     NULL, // GSpawnChildSetupFunc child_setup
                                     NULL, // gpointer user_data
                                     NULL, // GPid *child_pid
                                     NULL, // gint *standard_input
                                     &child_stdout, // gint *standard_output
                                     &child_stderr, // gint *standard_error
                                     &p_error); // GError **error
   if (!status) {
      if (p_error != NULL) {
         g_warning("%s: Error during script exec %s\n",
                   __FUNCTION__, p_error->message);
         g_clear_error(&p_error);
      } else {
         g_warning("%s: Command not run\n", __FUNCTION__);
      }

      /*
       * If an error occurs, child_pid, standard_input, standard_output,
       * and standard_error will not be filled with valid values.
       */
      g_free(command);
      return status;
   }

   g_debug("%s: Child process spawned for %s\n", __FUNCTION__, key);

   status = FALSE;

   child_stdout_f = fdopen(child_stdout, "r");
   if (child_stdout_f == NULL) {
      g_warning("%s: Failed to create file stream for child stdout, errno=%d",
             __FUNCTION__, errno);
      goto out;
   }

   status = SendScriptOutput(ctx, key, child_stdout_f);

   DynBuf_Init(&err);
   ReadFromHandle(child_stderr, &err);
   child_stderr = -1;
   if (DynBuf_GetSize(&err) != 0) {
      DynBuf_AppendString(&err, "");
      g_debug("%s: stderr=%s\n",
             __FUNCTION__, (const char *) DynBuf_Get(&err));
   }
   DynBuf_Destroy(&err);

out:
   if (child_stdout_f != NULL) {
      if (fclose(child_stdout_f) != 0) {
         g_warning("%s: Failed to close child stdout file stream, errno=%d",
                __FUNCTION__, errno);
      }
   } else if (close(child_stdout) != 0) {
      g_warning("%s: Failed to close child stdout handle, errno=%d",
             __FUNCTION__, errno);
   }
   if (child_stderr != -1 && close(child_stderr) != 0) {
      g_warning("%s: Failed to close child process stderr handle, errno=%d",
             __FUNCTION__, errno);
   }

   g_free(command);
   return status;
}   07070100000393000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync  07070100000394000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000395000081A40000000000000000000000016822550500000668000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/Makefile.am  ################################################################################
### Copyright (C) 2009-2016, 2022 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libtimeSync.la

libtimeSync_la_CPPFLAGS =
libtimeSync_la_CPPFLAGS += @PLUGIN_CPPFLAGS@

libtimeSync_la_LDFLAGS =
libtimeSync_la_LDFLAGS += @PLUGIN_LDFLAGS@

libtimeSync_la_LIBADD =
libtimeSync_la_LIBADD += @VMTOOLS_LIBS@
libtimeSync_la_LIBADD += @GOBJECT_LIBS@

libtimeSync_la_SOURCES =
libtimeSync_la_SOURCES += timeSync.c
libtimeSync_la_SOURCES += timeSyncPosix.c

if SOLARIS
libtimeSync_la_SOURCES += slewAdjtime.c
libtimeSync_la_SOURCES += pllNone.c
endif
if FREEBSD
libtimeSync_la_SOURCES += slewAdjtime.c
libtimeSync_la_SOURCES += pllNone.c
endif
if LINUX
libtimeSync_la_SOURCES += slewLinux.c
libtimeSync_la_SOURCES += pllLinux.c
libtimeSync_la_SOURCES += timeInfo.c
endif

07070100000396000081A40000000000000000000000016822550500001744000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/pllLinux.c   /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file pllLinux.c
 *
 * Implementation of the NTP PLL using Linux's adjtimex system call.
 */

#include "timeSync.h"
#include "timeSyncPosix.h"

#include <errno.h>
#include <glib.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/timex.h>
#include "vm_assert.h"


static void
TimeSyncLogPLLState(const char *prefix, struct timex *tx)
{
   g_debug("%s : off %ld freq %ld maxerr %ld esterr %ld status %d "
           "const %ld precision %ld tolerance %ld tick %ld\n",
           prefix, tx->offset, tx->freq, tx->maxerror, tx->esterror, 
           tx->status, tx->constant, tx->precision, tx->tolerance, tx->tick);
}

/*
 ******************************************************************************
 * TimeSync_PLLSupported --                                             */ /**
 *
 * Report whether the platform supports an NTP style Type-II Phase Locked
 * Loop for correcting the time.
 *
 * @return TRUE iff NTP Phase Locked Loop is supported.
 *
 ******************************************************************************
 */

Bool
TimeSync_PLLSupported(void)
{
   return TRUE;
}


/*
 ******************************************************************************
 * TimeSync_PLLSetFrequency --                                          */ /**
 *
 * Set the frequency of the PLL.  
 *
 * @param[in] ppmCorrection  The parts per million error that should be 
 *                           corrected.  This value is the ppm shifted 
 *                           left by 16 to match NTP.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

Bool
TimeSync_PLLSetFrequency(int64 ppmCorrection)
{
   struct timex tx;
   int error;

   tx.modes = ADJ_FREQUENCY;
   tx.freq = ppmCorrection;

   error = adjtimex(&tx);
   if (error == -1) {
      g_debug("%s: adjtimex failed: %d %s\n", __FUNCTION__,
              error, strerror(errno));
         return FALSE;
   }
   TimeSyncLogPLLState(__FUNCTION__, &tx);

   return TRUE;
}


/*
 ******************************************************************************
 * TimeSync_PLLUpdate --                                                */ /**
 *
 * Updates the PLL with a new offset.
 *
 * @param[in] offset         The offset between the host and the guest.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

Bool
TimeSync_PLLUpdate(int64 offset)
{
   struct timex tx;
   int error;

   if (offset < -500000) {
      offset = -500000;
      g_debug("%s: clamped offset at -500000\n", __FUNCTION__);
   }
   if (offset > 500000) {
      offset = 500000;
      g_debug("%s: clamped offset at 500000\n", __FUNCTION__);
   }


   tx.modes = ADJ_OFFSET | ADJ_MAXERROR | ADJ_ESTERROR;
   tx.offset = offset;
   tx.esterror = 0;
   tx.maxerror = 0;

   error = adjtimex(&tx);
   if (error == -1) {
      g_debug("%s: adjtimex set offset failed: %d %s\n", __FUNCTION__,
              error, strerror(errno));
         return FALSE;
   }
   TimeSyncLogPLLState(__FUNCTION__, &tx);

   /* Ensure that the kernel discipline is in the right mode.  STA_PLLs
    * should be set and STA_UNSYNC should not be set.  
    * 
    * The time constant is a bit trickier.  In "Computer Network Time
    * Synchronization" the terms used are "time constant" and "poll
    * exponent" where time constant = 2 ^ poll exponent.  Valid values for
    * the poll exponent are 4 through 17, corresponding to a range of 16s
    * to 131072s (36 hours).  On linux things are a bit different though:
    * tx.constant appears to be the poll exponent and when trying to set
    * the poll exponent, tx.constant should be set to poll exponent - 4.
    *
    * We want to set the time constant to as low a value as possible.  The
    * core NTP PLL that the kernel discipline implements is built assuming
    * that there is a clock filter with a variable delay of up to 8.
    * Since TimeSyncReadHostAndGuest retries if the error is large, we
    * don't need to implement the clock filter.  Hence we want a time
    * constant of 60/8 = 7, but settle for the lowest available: 16.  This
    * allows us to react to changes relatively fast.
    */
   if (tx.constant != 4) {
      tx.modes = ADJ_TIMECONST;
      tx.constant = 0;
      error = adjtimex(&tx);
      if (error == -1) {
         g_debug("%s: adjtimex set time constant failed: %d %s\n", __FUNCTION__,
                 error, strerror(errno));
         return FALSE;
      }
      g_debug("Set PLL time constant\n");
      TimeSyncLogPLLState(__FUNCTION__, &tx);
   }
   if ((tx.status & STA_PLL) != STA_PLL || (tx.status & STA_UNSYNC) != 0) {
      tx.modes = ADJ_STATUS;
      tx.status = STA_PLL;
      error = adjtimex(&tx);
      if (error == -1) {
         g_debug("%s: adjtimex set status failed: %d %s\n", __FUNCTION__,
                 error, strerror(errno));
         return FALSE;
      }
      g_debug("Set PLL status\n");
      TimeSyncLogPLLState(__FUNCTION__, &tx);
   }
   return TRUE;
}
07070100000397000081A400000000000000000000000168225505000009F2000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/pllNone.c    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file pllNone.c
 *
 * Null implementation of the NTP PLL.
 */

#include "timeSync.h"

#include "vm_assert.h"


/*
 ******************************************************************************
 * TimeSync_PLLSupported --                                             */ /**
 *
 * Report whether the platform supports an NTP style Type-II Phase Locked
 * Loop for correcting the time.
 *
 * @return TRUE iff NTP Phase Locked Loop is supported.
 *
 ******************************************************************************
 */

Bool
TimeSync_PLLSupported(void)
{
   return FALSE;
}


/*
 ******************************************************************************
 * TimeSync_PLLSetFrequency --                                          */ /**
 *
 * Set the frequency of the PLL.  
 *
 * @param[in] ppmCorrection  The parts per million error that should be 
 *                           corrected.  This value is the ppm shifted 
 *                           left by 16 to match NTP.
 *
 * @return FALSE
 *
 ******************************************************************************
 */

Bool
TimeSync_PLLSetFrequency(int64 ppmCorrection)
{
   NOT_IMPLEMENTED();
   return FALSE;
}


/*
 ******************************************************************************
 * TimeSync_PLLUpdate --                                                */ /**
 *
 * Updates the PLL with a new offset.
 *
 * @param[in] offset         The offset between the host and the guest.
 *
 * @return FALSE
 *
 ******************************************************************************
 */

Bool
TimeSync_PLLUpdate(int64 offset)
{
   NOT_IMPLEMENTED();
   return FALSE;
}
  07070100000398000081A40000000000000000000000016822550500000B4D000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/slewAdjtime.c    /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file slewAdjtime.c
 *
 * Implementation of slewing using Posix adjtime system call.
 */

#include "timeSync.h"
#include "timeSyncPosix.h"

#include <errno.h>
#include <glib.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/timex.h>
#include "vm_assert.h"


/*
 ******************************************************************************
 * TimeSync_DisableTimeSlew --                                          */ /**
 *
 * Disable time slewing, canceling any pending slew.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

Bool
TimeSync_DisableTimeSlew(void)
{
   struct timeval tx = {0};
   int error;

   error = adjtime(&tx, NULL);
   if (error == -1) {
      g_debug("adjtime failed: %s\n", strerror(errno));
      return FALSE;
   }
   return TRUE;
}


/*
 ******************************************************************************
 * TimeSync_Slew --                                                     */ /**
 *
 * Slew the clock, correcting 'delta' microseconds.  timeSyncPeriod is
 * ignored by this implementation.  Report the amount of the previous
 * correction that has not been applied.
 *
 * @param[in]  delta              Correction to apply in us.
 * @param[in]  timeSyncPeriod     Time interval in us.
 * @param[out] remaining          Amount of previous correction not applied.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

Bool
TimeSync_Slew(int64 delta,
              int64 timeSyncPeriod,
              int64 *remaining)
{
   struct timeval tx;
   struct timeval oldTx;
   int error;

   TimeSyncWriteTimeVal(delta, &tx);

   error = adjtime(&tx, &oldTx);
   if (error == -1) {
      g_debug("adjtime failed: %s\n", strerror(errno));
      return FALSE;
   }
   g_debug("time slew start.\n");

   *remaining = (int64)oldTx.tv_sec * US_PER_SEC + (int64)oldTx.tv_usec;

   return TRUE;
}
   07070100000399000081A40000000000000000000000016822550500001339000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/slewLinux.c  /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file slewLinux.c
 *
 * Implementation of slewing using Linux's adjtimex system call to alter
 * the tick length.
 */

#include "timeSync.h"
#include "timeSyncPosix.h"

#include <errno.h>
#include <glib.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/timex.h>
#include "vm_assert.h"

/*
 * The interval between two ticks (in usecs) can only be altered by 10%,
 * and the default value is 10000. So the values 900000L and 1000000L
 * divided by USER_HZ, which is 100.
 */
#ifdef __linux__
#   define USER_HZ             100                  /* from asm/param.h  */
#   define TICK_INCR_NOMINAL  (1000000L / USER_HZ)  /* nominal tick increment */
#   define TICK_INCR_MAX      (1100000L / USER_HZ)  /* maximum tick increment */
#   define TICK_INCR_MIN      (900000L / USER_HZ)   /* minimum tick increment */
#endif


/*
 ******************************************************************************
 * TimeSync_DisableTimeSlew --                                          */ /**
 *
 * Disable time slewing, setting the tick frequency to default. If failed to
 * disable the tick frequency, system time will not reflect the actual time -
 * it will be behind.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

Bool
TimeSync_DisableTimeSlew(void)
{
   struct timex tx;
   int error;

   tx.modes = ADJ_TICK;
   tx.tick = TICK_INCR_NOMINAL;

   error = adjtimex(&tx);
   if (error == -1) {
      g_debug("adjtimex failed: %s\n", strerror(errno));
      return FALSE;
   }
   g_debug("time slew end\n");
   return TRUE;
}


/*
 ******************************************************************************
 * TimeSync_Slew --                                                     */ /**
 *
 * Slew the clock so that the time difference is covered within the
 * timeSyncPeriod. timeSyncPeriod is the interval of the time sync loop
 * and we intend to catch up delta us.  Report the amount of the previous
 * correction that has not been applied (this may be negative if more than
 * timeSyncPeriod elapsed since the last call).
 *
 * This changes the tick frequency and hence needs to be reset after the time
 * sync is achieved.
 *
 * All times are in microseconds.
 *
 * @param[in]  delta              Correction to apply.
 * @param[in]  timeSyncPeriod     Time interval.
 * @param[out] remaining          Amount of previous correction not applied.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

Bool
TimeSync_Slew(int64 delta,
              int64 timeSyncPeriod,
              int64 *remaining)
{
   static int64 startTime = 0;
   static int64 tickLength;
   static int64 deltaRequested;

   struct timex tx;
   int error;
   int64 now;

   ASSERT(timeSyncPeriod > 0);

   if (!TimeSync_GetCurrentTime(&now)) {
      return FALSE;
   }

   if (startTime != 0) {
      int64 ticksElapsed = (now - startTime) / tickLength;
      int64 deltaApplied = ticksElapsed * (tickLength - TICK_INCR_NOMINAL);
      *remaining = deltaRequested - deltaApplied;
   }

   /*
    * Set the tick length so that delta time is corrected in
    * timeSyncPeriod period.  tick is the number of microseconds added per
    * clock tick. We adjust this so that we get the desired delta + the
    * timeSyncPeriod in timeSyncPeriod interval.
    */
   tickLength = 
      (timeSyncPeriod + delta) / ((timeSyncPeriod / US_PER_SEC) * USER_HZ);
   if (tickLength > TICK_INCR_MAX) {
      tickLength = TICK_INCR_MAX;
   } else if (tickLength < TICK_INCR_MIN) {
      tickLength = TICK_INCR_MIN;
   }
   tx.tick = tickLength;
   tx.modes = ADJ_TICK;

   ASSERT(delta != 0 || tickLength == TICK_INCR_NOMINAL);
   
   startTime = now;
   deltaRequested = delta;

   error = adjtimex(&tx);
   if (error == -1) {
      startTime = 0;
      g_debug("adjtimex failed: %s\n", strerror(errno));
      return FALSE;
   }
   g_debug("time slew start: %ld\n", tx.tick);
   return TRUE;
}
   0707010000039A000081A400000000000000000000000168225505000021A4000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/timeInfo.c   /*********************************************************
 * Copyright (C) 2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file timeInfo.c
 *
 * The feature allows tools to subscribe and receive updates from VMX when
 * time related properties of the host change.
 */

#include "conf.h"
#include "timeSync.h"
#include "system.h"
#include "strutil.h"
#include "dynarray.h"
#include "vmware/tools/log.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/threadPool.h"

typedef struct TimeInfoData {
   char *timestamp;
   char *key;
   char *value;
} TimeInfoData;

DEFINE_DYNARRAY_TYPE(TimeInfoData);

typedef struct TimeInfoVmxRpcCtx {
   char *request;
   struct {
      char *reply;
      size_t replyLen;
      TimeInfoDataArray data;
   } response;
} TimeInfoVmxRpcCtx;

// TODO: Move common definitions to a shared header with VMX.
static const char *TIMEINFO_VMXRPC_CLOCKID         = "precisionclock0";
static const char *TIMEINFO_VMXRPC_CMD_GETUPDATES  = "get-updates";
static const char *TIMEINFO_VMXRPC_CMD_SUBSCRIBE   = "subscribe";
static const char *TIMEINFO_VMXRPC_CMD_UNSUBSCRIBE = "unsubscribe";
static const char *TIMEINFO_VMXRPC_STATUS_OK       = "OK";
static ToolsAppCtx *gToolsAppCtx;


/**
 * Cleanup routine after performing GuestRPC.
 *
 * @param[in] rpc TimeInfo RPC context.
 */

static void
TimeInfoVmxRpcDone(TimeInfoVmxRpcCtx *rpc)
{
   free(rpc->request);
   RpcChannel_Free(rpc->response.reply);
   TimeInfoDataArray_Destroy(&rpc->response.data);
   memset(rpc, 0, sizeof *rpc);
}


/**
 * Perform given GuestRPC.
 *
 * @param[in] rpc    TimeInfo RPC context
 * @param[in] method GuestRPC method to invoke.
 * @param[in] argv   List of arguments to GuestRPC
 * @param[in] argc   Number of arguments to GuestRPC
 *
 * @return TRUE on successful invocation of GuestRPC, FALSE otherwise.
 */

static gboolean
TimeInfoVmxRpcDo(TimeInfoVmxRpcCtx *rpc,
                 const char *method,
                 const char *argv[],
                 size_t argc)
{
   Bool ok;
   char *next;
   int i;
   char *status;

   memset(rpc, 0, sizeof *rpc);
   TimeInfoDataArray_Init(&rpc->response.data, 0);

   StrUtil_SafeStrcatF(&rpc->request, "timeInfo.%s", method);
   for (i = 0; i < argc; ++i) {
      StrUtil_SafeStrcatF(&rpc->request, " %s", argv[i]);
   }

   g_debug("%s: Sending RPC: '%s'", __FUNCTION__, rpc->request);
   ok = RpcChannel_Send(gToolsAppCtx->rpc, rpc->request,
                        strlen(rpc->request), &rpc->response.reply,
                        &rpc->response.replyLen);
   if (!ok) {
      g_warning("%s: RpcChannel_Send failed.", __FUNCTION__);
      return FALSE;
   }

   if (rpc->response.reply == NULL || rpc->response.replyLen == 0) {
      g_warning("%s: Empty response received from VMX.", __FUNCTION__);
      return FALSE;
   }
   g_debug("%s: RPC response: %s\n", __FUNCTION__, rpc->response.reply);

   next = rpc->response.reply;
   status = StrUtil_GetNextItem(&next, '\n');

   if (status == NULL || strcmp(status, TIMEINFO_VMXRPC_STATUS_OK) != 0) {
      g_warning("%s: RPC was unsuccessful.", __FUNCTION__);
      return FALSE;
   }

   /* On success, extract payload. */
   while (next != NULL) {
      TimeInfoData data;
      char *line = StrUtil_GetNextItem(&next, '\n');
      g_debug("%s: > Response: data: %s", __FUNCTION__, VM_SAFE_STR(line));
      data.key = StrUtil_GetNextItem(&line, ' ');
      data.value = StrUtil_GetNextItem(&line, ' ');
      data.timestamp = StrUtil_GetNextItem(&line, '\n');
      if (data.timestamp == NULL || data.key == NULL || data.value == NULL) {
         g_warning("%s: Invalid result payload.", __FUNCTION__);
         return FALSE;
      }
      TimeInfoDataArray_Push(&rpc->response.data, data);
   }
   return TRUE;
}


/**
 * Subscribe to TimeInfo updates. If successful, VMX will send UPDATE
 * GuestRPCs to tools when host's time related properties change.
 */

static void
TimeInfoVmxSubscribe(void)
{
   TimeInfoVmxRpcCtx vmxRpc;
   const char *argv[1] = { TIMEINFO_VMXRPC_CLOCKID };

   g_debug("%s: Subscribing for notifications from VMX.", __FUNCTION__);
   if (!TimeInfoVmxRpcDo(&vmxRpc, TIMEINFO_VMXRPC_CMD_SUBSCRIBE,
                         argv, ARRAYSIZE(argv))) {
      g_warning("%s: Failed to subscribe with VMX for notifications.",
                __FUNCTION__);
   }
   TimeInfoVmxRpcDone(&vmxRpc);
}


/**
 * Unsubscribe from TimeInfo updates. If successful, VMX will no longer
 * send UPDATE GuestRPC to the tools.
 */

static void
TimeInfoVmxUnsubscribe(void)
{
   TimeInfoVmxRpcCtx vmxRpc;
   const char *argv[1] = { TIMEINFO_VMXRPC_CLOCKID };

   g_debug("%s: Unsubscribing from notifications from VMX.", __FUNCTION__);
   if (!TimeInfoVmxRpcDo(&vmxRpc, TIMEINFO_VMXRPC_CMD_UNSUBSCRIBE,
                         argv, ARRAYSIZE(argv))) {
      g_warning("%s: Failed to unsubscribe from VMX notifications.",
                __FUNCTION__);
   }
   TimeInfoVmxRpcDone(&vmxRpc);
}


/**
 * Fetch TimeInfo updates from the platform with GuestRPC.
 *
 * @param[in] vmxRpc TimeInfo VMX RPC context
 *
 * @return TRUE on successful invocation of GuestRPC, FALSE otherwise.
 */

static gboolean
TimeInfoVmxGetUpdates(TimeInfoVmxRpcCtx *vmxRpc)
{
   const char *argv[1] = { TIMEINFO_VMXRPC_CLOCKID };

   g_debug("%s: Fetching updates from VMX.", __FUNCTION__);
   if (!TimeInfoVmxRpcDo(vmxRpc, TIMEINFO_VMXRPC_CMD_GETUPDATES,
                             argv, ARRAYSIZE(argv))) {
      g_warning("%s: Failed to fetch updates.", __FUNCTION__);
      return FALSE;
   }
   return TRUE;
}


/**
 * Fetch and log TimeInfo updates.
 */

static void
TimeInfoGetAndLogUpdates(void)
{
   TimeInfoVmxRpcCtx vmxRpc;

   if (TimeInfoVmxGetUpdates(&vmxRpc)) {
      int i;
      for (i = 0; i < TimeInfoDataArray_Count(&vmxRpc.response.data); ++i) {
         const TimeInfoData *data =
            TimeInfoDataArray_AddressOf(&vmxRpc.response.data, i);
         g_info("update: key %s value %s time %s", data->key, data->value,
                data->timestamp);
      }
   } else {
      g_warning("%s: Failed to perform get-updates.", __FUNCTION__);
   }
   TimeInfoVmxRpcDone(&vmxRpc);
}


/**
 * Handler for async task when a TimeInfo update is received. Fetch updates
 * from the platform and log them.
 *
 * @param[in] ctx  The application context.
 * @param[in] data data pointer.
 */

static void
TimeInfoHandleNotificationTask(ToolsAppCtx *ctx, gpointer data)
{
   g_debug("%s: Notification received.", __FUNCTION__);
   TimeInfoGetAndLogUpdates();
}


/**
 * GuestRPC handler for TimeInfo_Update. Submits an async task to fetch
 * and log updates.
 *
 * @param[in] data RPC request data.
 *
 * @return TRUE on success.
 */

gboolean
TimeInfo_TcloHandler(RpcInData *data)
{
   if (gToolsAppCtx == NULL) {
      return RPCIN_SETRETVALS(data, "TimeInfo not enabled", FALSE);
   }
   ToolsCorePool_SubmitTask(gToolsAppCtx,
                            TimeInfoHandleNotificationTask,
                            NULL,
                            NULL);
   return RPCIN_SETRETVALS(data, "", TRUE);
}


/**
 * Initialize TimeInfo in TimeSync.
 *
 * @param[in] ctx The application context.
 */

void
TimeInfo_Init(ToolsAppCtx *ctx)
{
   gboolean timeInfoEnabled =
      g_key_file_get_boolean(ctx->config,
                             CONFGROUPNAME_TIMESYNC,
                             CONFNAME_TIMESYNC_TIMEINFO_ENABLED,
                             NULL);
   ASSERT(vmx86_linux);
   g_debug("%s: TimeInfo support is %senabled.\n",
           __FUNCTION__, !timeInfoEnabled ? "not " : "");
   if (timeInfoEnabled) {
      gToolsAppCtx = ctx;
      /* Flush initial updates. */
      TimeInfoGetAndLogUpdates();
      TimeInfoVmxSubscribe();
   }
}


/**
 * Cleans up internal TimeInfo state.
 */

void
TimeInfo_Shutdown(void)
{
   if (gToolsAppCtx != NULL) {
      TimeInfoVmxUnsubscribe();
      gToolsAppCtx = NULL;
   }
}
0707010000039B000081A40000000000000000000000016822550500000478000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/timeInfo.h   /*********************************************************
 * Copyright (C) 2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _TIMEINFO_H_
#define _TIMEINFO_H_

/**
 * @file timeInfo.h
 *
 * Functions and definitions related to TimeInfo.
 */

void TimeInfo_Init(ToolsAppCtx *ctx);
void TimeInfo_Shutdown(void);
gboolean TimeInfo_TcloHandler(RpcInData *data);

#endif /* _TIMEINFO_H_ */

0707010000039C000081A40000000000000000000000016822550500008C11000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/timeSync.c   /*********************************************************
 * Copyright (C) 2008-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file timeSync.c
 *
 * Plugin to handle time synchronization between the guest and host.
 *
 * There are two types of corrections this plugin makes: one time and periodic.
 *
 * Periodic time synchronization is done when tools.timeSync is enabled
 * (this corresponds with the Synchronize Host and Guest Time checkbox in
 * the toolbox).  When it is active time is corrected once per period
 * (typically every 60 seconds).
 *
 * One time corrections are done: at tools startup, resuming from suspend,
 * after disk shrink and other times when the guest has not been running
 * for a while.
 *
 * There are two basic methods for correcting the time: stepping and slewing.
 *
 * Stepping the time explictly sets the time in the guest to the time on
 * the host.  This a brute force approach that isn't very accurate.  Any
 * delay between deciding what to set the time to and actually setting the
 * time introduces error into the new time.  Additionally setting the time
 * backwards can confuse some applications.  During normal operation this
 * plugin only steps the time forward and only if the error is greater
 * than one second.
 *
 * Slewing time changes the rate of time advancement allowing errors to be
 * corrected smoothly (thus it is possible to correct time in the guest
 * being ahead of time on the host without time in the guest ever going
 * backwards).  An additional advantage is that only a relative change is
 * made, so delays in effecting a change don't introduce a large error
 * like they might with stepping the time.  One thing to note is that
 * windows has a notion of slewing being enabled/disabled independant of
 * whether the slew is set to nominal, so we track three states: disabled,
 * enabled-nominal, and enabled-active.
 *
 * Interacting with other time sync agents:
 *
 * When stepping it is relatively easy to co-exist with another time sync
 * agent.  We will only run into issues when we try to step the time at
 * exactly the same time as the other agent.  Though we are relatively
 * conservative about when to step and race with another time sync agent to
 * step time is unlikely, race is still possible.
 *
 * We have provided vmx options to eliminate or reduce the race conditions with
 * another time sync agent. Basic idea here to eliminate the race is to utilize
 * native time service agent (like w32time or NTP) to do the step correction
 * for us. We request a resync service from the native time service to
 * recalibrate and resynchronize its parameters and force it to do a step
 * correction. We have two vmx options (time.synchronize.guest.resync and
 * time.synchronize.guest.resync.timeout) that can be used to utilize native
 * time service resync instead of tools timeSync doing onetime step correction.
 * Please see the configuration knobs section below for more details.
 *
 * When slewing the time we will conflict much more directly with any
 * other time sync agent that is trying to slew the time since only one
 * slew rate can be active at any given time.  To play as nicely as
 * possible we only change the slew when necessary:
 *
 * 1. When starting the timesync loop reset the slew to nominal to clean
 *    up any odd state left behind a previous time sync agent.  For
 *    example vmware tools could have been running with a slew and then
 *    crashed.  Reseting to nominal gives us a reasonable starting point.
 *    An additional bonus is that on Windows turning on slewing (even when
 *    left at nominal) turns off windows' built in time synchronization
 *    according to MSDN.
 *
 * 2. When stopping the timesync loop disable slewing.  
 *
 * 3. When we stop slewing (either because we move to a host that doesn't
 *    support BDOOR_CMD_GETTIMEFULL_WITH_LAG or slew correction was
 *    disabled), reset the slew rate to nominal.
 *
 * 4. When stepping the time, reset slewing to nominal if it isn't
 *    already.
 *
 * 5. Avoid changing the slew in any other circumstance.  This allows a
 *    another agent to slew the time when we are not actively slewing.
 *
 * Configuration knobs:
 *
 * 1. time.synchronize.guest.resync
 *    If enabled, on supported guests, use guest timesync agent (such as
 *    w32time) to perform step correction by forcing resynchronization of
 *    its parameters.
 *
 * 2. time.synchronize.guest.resync.timeout
 *    If non-zero, on supported guests, perform VMtools steps correction
 *    following guest timesync resynchronization, after the specified number
 *    of seconds. This additional correction mitigates potential failures
 *    in guest time sync agent.
 *
 */

#include "timeSync.h"
#include "backdoor.h"
#include "backdoor_def.h"
#include "conf.h"
#include "msg.h"
#include "strutil.h"
#include "system.h"
#include "vmware/guestrpc/timesync.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/utils.h"
#include "timeInfo.h"

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif


/* Sync the time once a minute. */
#define TIMESYNC_TIME 60
/* Correct PERCENT_CORRECTION percent of the error each period. */
#define TIMESYNC_PERCENT_CORRECTION 50

/* When measuring the difference between time on the host and time in the
 * guest we try up to TIMESYNC_MAX_SAMPLES times to read a sample
 * where the two host reads are within TIMESYNC_GOOD_SAMPLE_THRESHOLD
 * microseconds. */
#define TIMESYNC_MAX_SAMPLES 4
#define TIMESYNC_GOOD_SAMPLE_THRESHOLD 2000

/* Once the error drops below TIMESYNC_PLL_ACTIVATE, activate the PLL.
 * 500ppm error acumulated over a 60 second interval can produce 30ms of
 * error. */
#define TIMESYNC_PLL_ACTIVATE (30 * 1000) /* 30ms. */
/* If the error goes above TIMESYNC_PLL_UNSYNC, deactivate the PLL. */
#define TIMESYNC_PLL_UNSYNC (2 * TIMESYNC_PLL_ACTIVATE)
/* Period during which the frequency error of guest time is measured. */
#define TIMESYNC_CALIBRATION_DURATION (15 * 60 * US_PER_SEC) /* 15min. */

typedef enum TimeSyncState {
   TIMESYNC_INITIALIZING,
   TIMESYNC_STOPPED,
   TIMESYNC_RUNNING,
} TimeSyncState;

typedef enum TimeSyncSlewState {
   TimeSyncUncalibrated,
   TimeSyncCalibrating,
   TimeSyncPLL,
} TimeSyncSlewState;

typedef enum TimeSyncType {
   TIMESYNC_STEP,
   TIMESYNC_PERIODIC,
   TIMESYNC_STEP_NORESYNC,
} TimeSyncType;

typedef struct TimeSyncData {
   gboolean           slewActive;
   gboolean           slewCorrection;
   uint32             slewPercentCorrection;
   uint32             timeSyncPeriod;         /* In seconds. */
   TimeSyncState      state;
   TimeSyncSlewState  slewState;
   GSource           *timer;
   gboolean           guestResync;
   uint32             guestResyncTimeout;
   GSource           *guestResyncTimer;
   ToolsAppCtx       *ctx;
} TimeSyncData;

/*
 * See bug 1395378
 * Set default value to FALSE. This serves two purposes.
 * 1) If the VMX is current, it would send the value derived from the vmx
 *    configuration and override this value.
 * 2) If the VMX is old, the default value shall allow us not to roll back
 *    the guest clock when tools starts up.
 */
gboolean gTimeSyncToolsStartupAllowBackward = FALSE;

static void TimeSyncSetSlewState(TimeSyncData *data, gboolean active);
static void TimeSyncResetSlew(TimeSyncData *data);
static gboolean TimeSyncDoSync(Bool slewCorrection, TimeSyncType syncType,
                               Bool allowBackwardSync, void *_data);
/**
 * Read the time reported by the Host OS.
 *
 * @param[out]  host                Time on the Host.
 * @param[out]  apparentError       Apparent time error = apparent - real.
 * @param[out]  apparentErrorValid  Did the platform inform us of apparentError.
 * @param[out]  maxTimeError        Maximum amount of error than can go.
 *                                  uncorrected.
 *
 * @return TRUE on success.
 */

static gboolean
TimeSyncReadHost(int64 *host, int64 *apparentError, Bool *apparentErrorValid,
                 int64 *maxTimeError)
{
   Backdoor_proto bp;
   int64 maxTimeLag;
   int64 interruptLag;
   int64 hostSecs;
   int64 hostUsecs;
   Bool timeLagCall;

   /*
    * We need 3 things from the host, and there exist 3 different versions of
    * the calls (described further below):
    * 1) host time
    * 2) maximum time lag allowed (config option), which is a
    *    threshold that keeps the tools from being over eager about
    *    resetting the time when it is only a little bit off.
    * 3) interrupt lag (the amount that apparent time lags real time)
    *
    * First 2 versions of the call add interrupt lag to the maximum allowed
    * time lag, where as in the last call it is returned separately.
    *
    * Three versions of the call:
    *
    * - BDOOR_CMD_GETTIME: suffers from a 136-year overflow problem that
    *   cannot be corrected without breaking backwards compatibility with
    *   older Tools. So, we have the newer BDOOR_CMD_GETTIMEFULL, which is
    *   overflow safe.
    *
    * - BDOOR_CMD_GETTIMEFULL: overcomes the problem above.
    *
    * - BDOOR_CMD_GETTIMEFULL_WITH_LAG: Both BDOOR_CMD_GETTIMEFULL and
    *   BDOOR_CMD_GETTIME returns max lag limit as interrupt lag + the maximum
    *   allowed time lag. BDOOR_CMD_GETTIMEFULL_WITH_LAG separates these two
    *   values. This is helpful when synchronizing time backwards by slewing
    *   the clock.
    *
    * We use BDOOR_CMD_GETTIMEFULL_WITH_LAG first and fall back to
    * BDOOR_CMD_GETTIMEFULL or BDOOR_CMD_GETTIME.
    *
    * Note that BDOOR_CMD_GETTIMEFULL and BDOOR_CMD_GETTIMEFULL_WITH_LAG will
    * not touch EAX when it succeeds. So we check for errors by comparing EAX to
    * BDOOR_MAGIC, which was set by the call to Backdoor() prior to touching the
    * backdoor port.
    */
   bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL_WITH_LAG;
   Backdoor(&bp);
   if (bp.out.ax.word == BDOOR_MAGIC) {
      hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word;
      interruptLag = bp.out.di.word;
      timeLagCall = TRUE;
      g_debug("Using BDOOR_CMD_GETTIMEFULL_WITH_LAG\n");
   } else {
      g_debug("BDOOR_CMD_GETTIMEFULL_WITH_LAG not supported by current host, "
              "attempting BDOOR_CMD_GETTIMEFULL\n");
      interruptLag = 0;
      timeLagCall = FALSE;
      bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL;
      Backdoor(&bp);
      if (bp.out.ax.word == BDOOR_MAGIC) {
         hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word;
      } else {
         g_debug("BDOOR_CMD_GETTIMEFULL not supported by current host, "
                 "attempting BDOOR_CMD_GETTIME\n");
         bp.in.cx.halfs.low = BDOOR_CMD_GETTIME;
         Backdoor(&bp);
         /*
          * This backdoor returns uint32 time value in bp.out.ax.word or
          * MAX_UINT32 in case of error.
          */
         if (bp.out.ax.word == MAX_UINT32) {
            hostSecs = -1;
         } else {
            hostSecs = bp.out.ax.word;
         }
      }
   }
   hostUsecs = bp.out.bx.word;
   maxTimeLag = bp.out.cx.word;

   *host = hostSecs * US_PER_SEC + hostUsecs;
   *apparentError = -interruptLag;
   *apparentErrorValid = timeLagCall;
   *maxTimeError = maxTimeLag;

   if (hostSecs <= 0) {
      g_warning("Invalid host OS time: %"FMT64"d secs, %"FMT64"d usecs.\n\n",
                hostSecs, hostUsecs);
      return FALSE;
   }

   return TRUE;
}


/**
 * Read the Guest OS time and the Host OS time.
 *
 * There are three time domains that are revelant here:
 * 1. Guest time     - the time reported by the guest
 * 2. Apparent time  - the time reported by the virtualization layer
 * 3. Host time      - the time reported by the host operating system.
 *
 * This function reports the host time, the guest time and the difference
 * between apparent time and host time (apparentError).  The host and
 * guest time may be sampled multiple times to ensure an accurate reading.
 *
 * @param[out]  host                Time on the Host.
 * @param[out]  guest               Time in the Guest.
 * @param[out]  apparentError       Apparent time error = apparent - real.
 * @param[out]  apparentErrorValid  Did the platform inform us of apparentError.
 * @param[out]  maxTimeError        Maximum amount of error than can go.
 *                                  uncorrected.
 *
 * @return TRUE on success.
 */

static gboolean
TimeSyncReadHostAndGuest(int64 *host, int64 *guest, 
                         int64 *apparentError, Bool *apparentErrorValid,
                         int64 *maxTimeError)
{
   int64 host1, host2, hostDiff;
   int64 tmpGuest, tmpApparentError, tmpMaxTimeError;
   Bool tmpApparentErrorValid;
   int64 bestHostDiff = MAX_INT64;
   int iter = 0;
   DEBUG_ONLY(static int64 lastHost = 0);

   *apparentErrorValid = FALSE;
   *host = *guest = *apparentError = *maxTimeError = 0;

   if (!TimeSyncReadHost(&host2, &tmpApparentError, 
                         &tmpApparentErrorValid, &tmpMaxTimeError)) {
      return FALSE;
   }

   do {
      iter++;
      host1 = host2;

      if (!TimeSync_GetCurrentTime(&tmpGuest)) {
         g_warning("Unable to retrieve the guest OS time: %s.\n\n", 
                   Msg_ErrString());
         return FALSE;
      }
      
      if (!TimeSyncReadHost(&host2, &tmpApparentError, 
                            &tmpApparentErrorValid, &tmpMaxTimeError)) {
         return FALSE;
      }
      
      if (host1 < host2) {
         hostDiff = host2 - host1;
      } else {
         hostDiff = 0;
      }

      if (hostDiff <= bestHostDiff) {
         bestHostDiff = hostDiff;
         *host = host1 + hostDiff / 2;
         *guest = tmpGuest;
         *apparentError = tmpApparentError;
         *apparentErrorValid = tmpApparentErrorValid;
         *maxTimeError = tmpMaxTimeError;
      }
   } while (iter < TIMESYNC_MAX_SAMPLES && 
            bestHostDiff > TIMESYNC_GOOD_SAMPLE_THRESHOLD);

   ASSERT(*host != 0 && *guest != 0);

#ifdef VMX86_DEBUG
   g_debug("Daemon: Guest vs host error %.6fs; guest vs apparent error %.6fs; "
           "limit=%.2fs; apparentError %.6fs; iter=%d error=%.6fs; "
           "%.6f secs since last update\n",
           (*guest - *host) / 1000000.0, 
           (*guest - *host - *apparentError) / 1000000.0, 
           *maxTimeError / 1000000.0, *apparentError / 1000000.0,
           iter, bestHostDiff / 1000000.0,
           (*host - lastHost) / 1000000.0);
   lastHost = *host;
#endif

   return TRUE;
}


/**
 * Set the guest OS time to the host OS time by stepping the time.
 *
 * @param[in]  data              Structure tracking time sync state.
 * @param[in]  adjustment        Amount to correct the guest time.
 */

gboolean
TimeSyncStepTime(TimeSyncData *data, int64 adjustment)
{
   Backdoor_proto bp;
   int64 before;
   int64 after;

   TimeSync_GetCurrentTime(&before);

   /* Stepping invalidates the current slew, reset to nominal. */
   TimeSyncSetSlewState(data, FALSE);

   if (!TimeSync_AddToCurrentTime(adjustment)) {
      return FALSE;
   }

   /*
    * Tell timetracker to stop trying to catch up, since we have corrected
    * both the guest OS error and the apparent time error.
    */
   bp.in.cx.halfs.low = BDOOR_CMD_STOPCATCHUP;
   Backdoor(&bp);

   TimeSync_GetCurrentTime(&after);

   g_debug("Time changed by %"FMT64"dus from %"FMT64"d.%06"FMT64"d -> "
           "%"FMT64"d.%06"FMT64"d\n", adjustment,
           before / US_PER_SEC, before % US_PER_SEC,
           after / US_PER_SEC, after % US_PER_SEC);

   return TRUE;
}


/**
 * Slew the guest OS time advancement to correct the time.
 *
 * In addition to standard slewing (implemented via TimeSync_Slew), we
 * also support using an NTP style PLL to slew the time.  The PLL can take
 * a while to end up with an accurate measurement of the frequency error,
 * so before entering PLL mode we calibrate the frequency error over a
 * period of TIMESYNC_PLL_ACTIVATE seconds.  
 *
 * When using standard slewing, only correct slewPercentCorrection of the
 * error.  This is to avoid overcorrection when the error is mis-measured,
 * or overcorrection caused by the daemon waking up later than it is
 * supposed to leaving the slew in place for longer than anticpiated.
 *
 * @param[in]  data              Structure tracking time sync state.
 * @param[in]  adjustment        Amount to correct the guest time.
 */

static gboolean
TimeSyncSlewTime(TimeSyncData *data, int64 adjustment)
{
   static int64 calibrationStart;
   static int64 calibrationAdjustment;

   int64 now;
   int64 remaining = 0;
   int64 timeSyncPeriodUS = (int64)data->timeSyncPeriod * US_PER_SEC;
   int64 slewDiff = (adjustment * data->slewPercentCorrection) / 100;

   if (!TimeSync_GetCurrentTime(&now)) {
      return FALSE;
   }

   if (ABS(adjustment) > TIMESYNC_PLL_UNSYNC && 
       data->slewState != TimeSyncUncalibrated) {
      g_debug("Adjustment too large (%"FMT64"d), resetting PLL state.\n", 
              adjustment);
      data->slewState = TimeSyncUncalibrated;
   }

   if (data->slewState == TimeSyncUncalibrated) {
      g_debug("Slewing time: adjustment %"FMT64"d\n", adjustment);
      if (!TimeSync_Slew(slewDiff, timeSyncPeriodUS, &remaining)) {
         data->slewState = TimeSyncUncalibrated;
         return FALSE;
      }
      if (ABS(adjustment) < TIMESYNC_PLL_ACTIVATE && TimeSync_PLLSupported()) {
         g_debug("Starting PLL calibration.\n");
         calibrationStart = now;
         /* Starting out the calibration period we are adjustment behind,
          * but have already requested to correct slewDiff of that. */
         calibrationAdjustment = slewDiff - adjustment;
         data->slewState = TimeSyncCalibrating;
      }
   } else if (data->slewState == TimeSyncCalibrating) {
      if (now > calibrationStart + TIMESYNC_CALIBRATION_DURATION) {
         int64 ppmErr;
         /* Reset slewing to nominal and find out remaining slew. */
         TimeSync_Slew(0, timeSyncPeriodUS, &remaining);
         calibrationAdjustment += adjustment;
         calibrationAdjustment -= remaining;
         ppmErr = ((1000000 * calibrationAdjustment) << 16) / 
                   (now - calibrationStart);
         if (ppmErr >> 16 < 500 && ppmErr >> 16 > -500) {
            g_debug("Activating PLL ppmEst=%"FMT64"d (%"FMT64"d)\n", 
                    ppmErr >> 16, ppmErr);
            TimeSync_PLLUpdate(adjustment);
            TimeSync_PLLSetFrequency(ppmErr);
            data->slewState = TimeSyncPLL;
         } else {
            /* PPM error is too large to try the PLL. */
            g_debug("PPM error too large: %"FMT64"d (%"FMT64"d) "
                    "not activating PLL\n", ppmErr >> 16, ppmErr);
            data->slewState = TimeSyncUncalibrated;
         }
      } else {
         g_debug("Calibrating error: adjustment %"FMT64"d\n", adjustment);
         if (!TimeSync_Slew(slewDiff, timeSyncPeriodUS, &remaining)) {
            return FALSE;
         }
         calibrationAdjustment += slewDiff;
         calibrationAdjustment -= remaining;
      }
   } else {
      ASSERT(data->slewState == TimeSyncPLL);
      g_debug("Updating PLL: adjustment %"FMT64"d\n", adjustment);
      if (!TimeSync_PLLUpdate(adjustment)) {
         TimeSyncResetSlew(data);
      }
   }
   return TRUE;
}


/**
 * Reset the slew to nominal.
 *
 * @param[in]  data              Structure tracking time sync state.
 */

static void
TimeSyncResetSlew(TimeSyncData *data)
{
   int64 remaining;
   int64 timeSyncPeriodUS = (int64)data->timeSyncPeriod * US_PER_SEC;
   data->slewState = TimeSyncUncalibrated;
   TimeSync_Slew(0, timeSyncPeriodUS, &remaining);
   if (TimeSync_PLLSupported()) {
      TimeSync_PLLUpdate(0);
      TimeSync_PLLSetFrequency(0);
   }
}


/**
 * Update whether slewing is used for time correction.
 *
 * @param[in]  data              Structure tracking time sync state.
 * @param[in]  active            Is slewing active.
 */

static void
TimeSyncSetSlewState(TimeSyncData *data, gboolean active)
{
   if (active != data->slewActive) {
      g_debug(active ? "Starting slew.\n" : "Stopping slew.\n");
      if (!active) {
         TimeSyncResetSlew(data);
      }
      data->slewActive = active;
   }
}


/**
 * Guest resync timeout handler to step correct guest time. This is a callback
 * handler that requests for step correction after a preconfigured timeout.
 *
 * @param[in]  _data    Time sync data.
 *
 * @return TRUE on success.
 */

static gboolean
TimeSyncGuestResyncTimeoutHandler(gpointer _data)
{
   TimeSyncData *data = _data;

   ASSERT(data != NULL);
   ASSERT(data->guestResyncTimer != NULL);

   g_source_destroy(data->guestResyncTimer);
   g_source_unref(data->guestResyncTimer);
   data->guestResyncTimer = NULL;

   g_debug("Guest resync timeout handler: stepping time.\n");
   return TimeSyncDoSync(data->slewCorrection, TIMESYNC_STEP_NORESYNC,
                         TRUE, data);
}


/**
 * Set the guest OS time to the host OS time.
 *
 * @param[in]  slewCorrection    Is clock slewing enabled?
 * @param[in]  syncType          Type of synchronization requested.
 * @param[in]  allowBackwardSync Can we sync time backwards when doing
 *    step/resync correction?
 * @param[in]  _data             Time sync data.
 *
 * @return TRUE on success.
 */

static gboolean
TimeSyncDoSync(Bool slewCorrection,
               TimeSyncType syncType,
               Bool allowBackwardSync,
               void *_data)
{
   int64 guest, host;
   int64 gosError, apparentError, maxTimeError;
   Bool apparentErrorValid;
   TimeSyncData *data = _data;

   g_debug("Synchronizing time: "
           "syncType %d, slewCorrection %d, allowBackwardSync %d "
           "guestResync %d, guestResyncTimeout %d.\n",
           syncType, slewCorrection, allowBackwardSync,
           data->guestResync, data->guestResyncTimeout);

   if (!TimeSyncReadHostAndGuest(&host, &guest, &apparentError, 
                                 &apparentErrorValid, &maxTimeError)) {
      return FALSE;
   }

   gosError = guest - host - apparentError;

   if (syncType == TIMESYNC_STEP || syncType == TIMESYNC_STEP_NORESYNC) {
      /*
       * Non-loop behavior:
       *
       * Perform a step correction if:
       * 1) The guest OS error is behind by more than maxTimeError.
       * 2) The guest OS is ahead of the host OS.
       *
       * There are 2 ways of step correction:
       * 1) If guest resync flag is enabled and if a guest resync service is
       * running, we request guest service to do a resync. If a timeout is
       * configured, we set up a callback to do legacy step correction.
       * 2) If conditions to do guest resync are not met, we rely on legacy
       * step correction.
       */

      if (gosError < -maxTimeError || 
          (gosError + apparentError > 0 && allowBackwardSync)) {
         if (syncType == TIMESYNC_STEP && data->guestResync &&
             TimeSync_IsGuestSyncServiceRunning()) {
            if (data->guestResyncTimer == NULL) {
               g_debug("Guest resync: stepping time.\n");
               ASSERT(data->ctx != NULL);
               if (!TimeSync_DoGuestResync(data->ctx)) {
                  g_warning("Guest resync operation failed.\n");
                  return TimeSyncDoSync(data->slewCorrection,
                                        TIMESYNC_STEP_NORESYNC,
                                        allowBackwardSync, data);
               }
               if (data->guestResyncTimeout > 0) {
                  data->guestResyncTimer =
                     g_timeout_source_new(data->guestResyncTimeout);
                  VMTOOLSAPP_ATTACH_SOURCE(data->ctx, data->guestResyncTimer,
                                           TimeSyncGuestResyncTimeoutHandler,
                                           data, NULL);
               }
            } else {
               g_warning("Guest resync is in progress, ignoring one-time "
                         "synchronization event.\n");
               return FALSE;
            }
         } else {
            g_debug("One time synchronization: stepping time.\n");
            if (!TimeSyncStepTime(data, -gosError + -apparentError)) {
               return FALSE;
            }
         }
      } else {
         g_debug("One time synchronization: correction not needed.\n");
      }
   } else {

      /*
       * Loop behavior:
       *
       * If guest error is more than maxTimeError behind perform a step
       * correction.  Otherwise, if we can distinguish guest error from
       * apparent time error perform a slew correction .
       */

      ASSERT(syncType == TIMESYNC_PERIODIC);
      TimeSyncSetSlewState(data, apparentErrorValid && slewCorrection);

      if (gosError < -maxTimeError) {
         g_debug("Periodic synchronization: stepping time.\n");
         if (!TimeSyncStepTime(data, -gosError + -apparentError)) {
            return FALSE;
         }
      } else if (slewCorrection && apparentErrorValid) {
         g_debug("Periodic synchronization: slewing time.\n");
         if (!TimeSyncSlewTime(data, -gosError)) {
            return FALSE;
         }
      }
   }

   return TRUE;
}


/**
 * Run the "time synchronization" loop.
 *
 * @param[in]  _data    Time sync data.
 *
 * @return always TRUE.
 */

static gboolean
ToolsDaemonTimeSyncLoop(gpointer _data)
{
   TimeSyncData *data = _data;

   ASSERT(data != NULL);

   if (!TimeSyncDoSync(data->slewCorrection, TIMESYNC_PERIODIC, FALSE, data)) {
      g_warning("Unable to synchronize time.\n");
   }

   return TRUE;
}


/**
 * Start the "time synchronization" loop.
 *
 * @param[in]  ctx      The application context.
 * @param[in]  data     Time sync data.
 *
 * @return TRUE on success.
 */

static Bool
TimeSyncStartLoop(ToolsAppCtx *ctx,
                  TimeSyncData *data)
{
   ASSERT(data != NULL);
   ASSERT(data->state != TIMESYNC_RUNNING);
   ASSERT(data->timer == NULL);

   g_debug("Starting time sync loop.\n");

   /* 
    * Turn slew on and set it to nominal.  
    */
   TimeSyncResetSlew(data);

   g_debug("New sync period is %d sec.\n", data->timeSyncPeriod);

   if (!TimeSyncDoSync(data->slewCorrection, TIMESYNC_PERIODIC, FALSE, data)) {
      g_warning("Unable to synchronize time when starting time loop.\n");
   }

   data->timer = g_timeout_source_new(data->timeSyncPeriod * 1000);
   VMTOOLSAPP_ATTACH_SOURCE(ctx, data->timer, ToolsDaemonTimeSyncLoop,
                            data, NULL);

   data->state = TIMESYNC_RUNNING;
   return TRUE;
}


/**
 * Stop the "time synchronization" loop.
 *
 * @param[in]  ctx      The application context.
 * @param[in]  data     Time sync data.
 */

static void
TimeSyncStopLoop(ToolsAppCtx *ctx,
                 TimeSyncData *data)
{
   ASSERT(data != NULL);
   ASSERT(data->state == TIMESYNC_RUNNING);
   ASSERT(data->timer != NULL);

   g_debug("Stopping time sync loop.\n");

   TimeSyncSetSlewState(data, FALSE);
   TimeSync_DisableTimeSlew();

   g_source_destroy(data->timer);
   g_source_unref(data->timer);
   data->timer = NULL;

   data->state = TIMESYNC_STOPPED;
}


/**
 * Sync the guest's time with the host's.
 *
 * @param[in]  data     RPC request data.
 *
 * @return TRUE on success.
 */

static gboolean
TimeSyncTcloHandler(RpcInData *data)
{
   Bool backwardSync;
   uint32 argVal;
   unsigned int index = 0;
   TimeSyncData *syncData = data->clientData;

   if (!StrUtil_GetNextUintToken(&argVal, &index, data->args, " ")) {
      return RPCIN_SETRETVALS(data,
                              "Unable to tokenize TimeSync RPC data", FALSE);
   }
   backwardSync = argVal == 1;
   if (!TimeSyncDoSync(syncData->slewCorrection, TIMESYNC_STEP,
                       backwardSync, syncData)) {
      return RPCIN_SETRETVALS(data, "Unable to sync time", FALSE);
   } else {
      return RPCIN_SETRETVALS(data, "", TRUE);
   }
}


/**
 * Parses boolean option string.
 *
 * @param[in]  string     Option string to be parsed.
 * @param[out] gboolean   Value of the option.
 *
 * @return TRUE on success.
 */

static gboolean
ParseBoolOption(const gchar *string,
                gboolean *value)
{
      if (strcmp(string, "1") == 0) {
         *value = TRUE;
      } else if (strcmp(string, "0") == 0) {
         *value = FALSE;
      } else {
         return FALSE;
      }
      return TRUE;
}

/**
 * Handles a "Set_Option" callback. Processes the time sync related options.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  option   Option being set.
 * @param[in]  value    Option value.
 * @param[in]  plugin   Plugin registration data.
 *
 * @return TRUE on success.
 */

static gboolean
TimeSyncSetOption(gpointer src,
                  ToolsAppCtx *ctx,
                  const gchar *option,
                  const gchar *value,
                  ToolsPluginData *plugin)
{
   static gboolean syncBeforeLoop;
   TimeSyncData *data = plugin->_private;

   if (strcmp(option, TOOLSOPTION_SYNCTIME) == 0) {
      gboolean start;
      if (!ParseBoolOption(value, &start)) {
         return FALSE;
      }

      if (start && data->state != TIMESYNC_RUNNING) {
         /*
          * Try the one-shot time sync if time sync transitions from
          * 'off' to 'on' and TOOLSOPTION_SYNCTIME_ENABLE is turned on.
          * Note that during startup we receive TOOLSOPTION_SYNCTIME
          * before receiving TOOLSOPTION_SYNCTIME_ENABLE and so the
          * one-shot sync will not be done here. Nor should it because
          * the startup synchronization behavior is controlled by
          * TOOLSOPTION_SYNCTIME_STARTUP which is handled separately.
          */
         if (data->state == TIMESYNC_STOPPED && syncBeforeLoop) {
            TimeSyncDoSync(data->slewCorrection, TIMESYNC_STEP, TRUE, data);
         }

         if (!TimeSyncStartLoop(ctx, data)) {
            g_warning("Unable to change time sync period.\n");
            return FALSE;
         }

      } else if (!start) {
         if (data->state == TIMESYNC_RUNNING) {
            TimeSyncStopLoop(ctx, data);
         } else {
            data->state = TIMESYNC_STOPPED;
         }
      }

   } else if (strcmp(option, TOOLSOPTION_SYNCTIME_SLEWCORRECTION) == 0) {
      data->slewCorrection = strcmp(value, "0");
      g_debug("Daemon: Setting slewCorrection, %d.\n", data->slewCorrection);

   } else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERCENTCORRECTION) == 0) {
      int32 percent;

      g_debug("Daemon: Setting slewPercentCorrection to %s.\n", value);
      if (!StrUtil_StrToInt(&percent, value)) {
         return FALSE;
      }
      if (percent <= 0 || percent > 100) {
         data->slewPercentCorrection = TIMESYNC_PERCENT_CORRECTION;
      } else {
         data->slewPercentCorrection = percent;
      }

   } else if (strcmp(option, TOOLSOPTION_SYNCTIME_PERIOD) == 0) {
      uint32 period;

      if (!StrUtil_StrToUint(&period, value)) {
         return FALSE;
      }

      if (period <= 0)
         period = TIMESYNC_TIME;

      /*
       * If the sync loop is running and the time sync period has changed,
       * restart the loop with the new period value. If the sync loop is
       * not running, just remember the new sync period value.
       */
      if (period != data->timeSyncPeriod) {
         data->timeSyncPeriod = period;

         if (data->state == TIMESYNC_RUNNING) {
            TimeSyncStopLoop(ctx, data);
            if (!TimeSyncStartLoop(ctx, data)) {
               g_warning("Unable to change time sync period.\n");
               return FALSE;
            }
         }
      }

   } else if (strcmp(option, TOOLSOPTION_SYNCTIME_STARTUP_BACKWARD) == 0) {
      if (!ParseBoolOption(value, &gTimeSyncToolsStartupAllowBackward)) {
         return FALSE;
      }
   } else if (strcmp(option, TOOLSOPTION_SYNCTIME_STARTUP) == 0) {
      static gboolean doneAlready = FALSE;
      gboolean doSync;

      if (!ParseBoolOption(value, &doSync)) {
         return FALSE;
      }

      if (doSync && !doneAlready &&
          !TimeSyncDoSync(data->slewCorrection, TIMESYNC_STEP,
                          gTimeSyncToolsStartupAllowBackward, data)) {
         g_warning("Unable to sync time during startup.\n");
         return FALSE;
      }

      doneAlready = TRUE;

   } else if (strcmp(option, TOOLSOPTION_SYNCTIME_ENABLE) == 0) {
      if (!ParseBoolOption(value, &syncBeforeLoop)) {
         return FALSE;
      }

   } else if (strcmp(option, TOOLSOPTION_SYNCTIME_GUEST_RESYNC) == 0) {
      if (!ParseBoolOption(value, &data->guestResync)) {
         return FALSE;
      }
      g_debug("guestResync = %d\n", data->guestResync);

   } else if (strcmp(option, TOOLSOPTION_SYNCTIME_GUEST_RESYNC_TIMEOUT) == 0) {
      if (!StrUtil_StrToUint(&data->guestResyncTimeout, value)) {
         return FALSE;
      }
      g_debug("guestResyncTimeout = %d\n", data->guestResyncTimeout);

   } else {
      return FALSE;
   }

   return TRUE;
}


/**
 * Handles a shutdown callback; cleans up internal plugin state.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  plugin   Plugin registration data.
 */

static void
TimeSyncShutdown(gpointer src,
                 ToolsAppCtx *ctx,
                 ToolsPluginData *plugin)
{
   TimeSyncData *data = plugin->_private;

#if defined(__linux__) && !defined(USERWORLD)
   TimeInfo_Shutdown();
#endif
   if (data->state == TIMESYNC_RUNNING) {
      TimeSyncStopLoop(ctx, data);
   }

   g_free(data);
}


/**
 * Plugin entry point. Initializes internal state and returns the registration
 * data.
 *
 * @param[in]  ctx   The app context.
 *
 * @return The registration data.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "timeSync",
      NULL,
      NULL
   };

   TimeSyncData *data = g_malloc(sizeof (TimeSyncData));
   RpcChannelCallback rpcs[] = {
      { TIMESYNC_SYNCHRONIZE, TimeSyncTcloHandler, data, NULL, NULL, 0 },
#if defined(__linux__) && !defined(USERWORLD)
      { TIMEINFO_UPDATE, TimeInfo_TcloHandler, data, NULL, NULL, 0 }
#endif
   };
   ToolsPluginSignalCb sigs[] = {
      { TOOLS_CORE_SIG_SET_OPTION, TimeSyncSetOption, &regData },
      { TOOLS_CORE_SIG_SHUTDOWN, TimeSyncShutdown, &regData }
   };
   ToolsAppReg regs[] = {
      { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) },
      { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
   };

#if defined(__linux__) && !defined(USERWORLD)
   TimeInfo_Init(ctx);
#endif
   data->slewActive = FALSE;
   data->slewCorrection = FALSE;
   data->slewPercentCorrection = TIMESYNC_PERCENT_CORRECTION;
   data->state = TIMESYNC_INITIALIZING;
   data->slewState = TimeSyncUncalibrated;
   data->timeSyncPeriod = TIMESYNC_TIME;
   data->timer = NULL;
   data->guestResync = FALSE;
   data->guestResyncTimeout = 0;
   data->guestResyncTimer = NULL;
   data->ctx = ctx;
   regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));
   regData._private = data;

   return &regData;
}
   0707010000039D000081A40000000000000000000000016822550500000630000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/timeSync.h   /*********************************************************
 * Copyright (C) 2009-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _TIMESYNC_INT_H_
#define _TIMESYNC_INT_H_

/**
 * @file timeSync.h
 *
 * Functions and definitions related to syncing time.
 */

#define G_LOG_DOMAIN "timeSync"
#include "vm_basic_types.h"

#define US_PER_SEC 1000000

Bool
TimeSync_GetCurrentTime(int64 *now);

Bool
TimeSync_AddToCurrentTime(int64 delta);

Bool
TimeSync_Slew(int64 delta,
              int64 timeSyncPeriod,
              int64 *remaining);

Bool
TimeSync_DisableTimeSlew(void);

Bool
TimeSync_PLLUpdate(int64 offset);

Bool
TimeSync_PLLSetFrequency(int64 ppmCorrection);

Bool
TimeSync_PLLSupported(void);

Bool
TimeSync_IsGuestSyncServiceRunning(void);

Bool
TimeSync_DoGuestResync(void *_ctx);

#endif /* _TIMESYNC_INT_H_ */

0707010000039E000081A40000000000000000000000016822550500000FE3000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/timeSyncPosix.c  /*********************************************************
 * Copyright (C) 2009-2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file timeSyncPosix.c
 *
 * Implementation of time sync functions for POSIX systems.
 */

#include "timeSync.h"
#include "timeSyncPosix.h"

#include <errno.h>
#include <glib.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "vm_assert.h"


/*
 ******************************************************************************
 * TimeSync_AddToCurrentTime --                                         */ /**
 *
 * Adjust the current system time by adding the given number of
 * microseconds. This function disables any time slewing to correctly set
 * the guest time.
 *
 * @param[in] delta    Microseconds to add.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

Bool
TimeSync_AddToCurrentTime(int64 delta)
{
   struct timeval tv;
   int64 newTime;
   int64 now;

   if (!TimeSync_GetCurrentTime(&now)) {
      return FALSE;
   }

   newTime = now + delta;
   ASSERT(newTime > 0);

   /*
    * timeval.tv_sec is a 32-bit signed integer. So, Linux will treat
    * newTime as a time before the epoch if newTime is a time 68 years
    * after the epoch (beacuse of overflow).
    *
    * If it is a 64-bit linux, everything should be fine.
    */
   if (sizeof tv.tv_sec < 8 && newTime / US_PER_SEC > MAX_INT32) {
      g_debug("overflow: delta=%"FMT64"d, now=%"FMT64"d\n", delta, now);
      return FALSE;
   }

   TimeSyncWriteTimeVal(newTime, &tv);

   if (settimeofday(&tv, NULL) < 0) {
      return FALSE;
   }

   return TRUE;
}


/*
 ******************************************************************************
 * TimeSync_GetCurrentTime --                                           */ /**
 *
 * Get the system time in seconds & microseconds.
 *
 * @param[in] secs      Where to store the number of seconds.
 * @param[in] usecs     Where to store the number of microseconds.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

Bool
TimeSync_GetCurrentTime(int64 *now)
{
   struct timeval tv;

   ASSERT(now);

   if (gettimeofday(&tv, NULL) < 0) {
      return FALSE;
   }

   *now = (int64)tv.tv_sec * US_PER_SEC + (int64)tv.tv_usec;

   return TRUE;
}


/*
 ******************************************************************************
 * TimeSync_IsGuestSyncServiceRunning --                                */ /**
 *
 * Check if the guest time sync service is running.
 *
 * @return TRUE if running and FALSE if not running or not implemented.
 *
 ******************************************************************************
 */

Bool
TimeSync_IsGuestSyncServiceRunning(void)
{
   // Not Implemented.
   return FALSE;
}


/*
 ******************************************************************************
 * TimeSync_DoGuestResync --                                            */ /**
 *
 * Issue a resync command to the guest time sync service.
 *
 * @return TRUE on success and FALSE on failure or if not implemented.
 *
 ******************************************************************************
 */

Bool
TimeSync_DoGuestResync(void *_ctx)
{
   // Not Implemented.
   return FALSE;
}
 0707010000039F000081A400000000000000000000000168225505000007E8000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/timeSync/timeSyncPosix.h  /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _TIMESYNC_POSIX_H_
#define _TIMESYNC_POSIX_H_

/**
 * @file timeSyncPosix.h
 *
 * Posix specific functions and definitions related to syncing time.
 */

#include "timeSync.h"
#include "vm_assert.h"
#include <sys/time.h>

/*
 ******************************************************************************
 * TimeSyncWriteTimeVal --                                              */ /**
 *
 * Convert time represented as microseconds, to a timeval.  This function
 * handles positive and negative values for "time."  For a timeval to be
 * valid tv_usec must be between 0 and 999999.  See
 * http://www.gnu.org/s/libc/manual/html_node/Elapsed-Time.html for more
 * details.
 *
 ******************************************************************************
 */

static INLINE void
TimeSyncWriteTimeVal(int64 time, struct timeval *tv)
{
   int64 sec = time / US_PER_SEC;
   int64 usec = time - sec * US_PER_SEC;
   if (usec < 0) {
      usec += US_PER_SEC;
      sec--;
   }
   ASSERT(0 <= usec && usec < US_PER_SEC &&
          time == sec * US_PER_SEC + usec);
   tv->tv_sec = sec;
   tv->tv_usec = usec;
}

#endif /* _TIMESYNC_INT_H_ */

070701000003A0000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix   070701000003A1000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000003A2000081A400000000000000000000000168225505000006EC000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix/Makefile.am   ################################################################################
### Copyright (C) 2009-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @COMMON_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libvix.la

libvix_la_CPPFLAGS =
libvix_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libvix_la_CPPFLAGS += -I$(top_srcdir)/vgauth/public
libvix_la_CPPFLAGS += @XDR_CPPFLAGS@

libvix_la_LDFLAGS =
libvix_la_LDFLAGS += @PLUGIN_LDFLAGS@

libvix_la_LIBADD =
libvix_la_LIBADD += @VIX_LIBADD@
libvix_la_LIBADD += @VMTOOLS_LIBS@
libvix_la_LIBADD += @HGFS_LIBS@
libvix_la_LIBADD += $(top_builddir)/lib/auth/libAuth.la
libvix_la_LIBADD += $(top_builddir)/lib/foundryMsg/libFoundryMsg.la
libvix_la_LIBADD += $(top_builddir)/lib/impersonate/libImpersonate.la
if ENABLE_VGAUTH
   libvix_la_LIBADD += $(top_builddir)/vgauth/lib/libvgauth.la
endif
libvix_la_LIBADD += @XDR_LIBS@

libvix_la_SOURCES =
libvix_la_SOURCES += foundryToolsDaemon.c
libvix_la_SOURCES += vixPlugin.c
libvix_la_SOURCES += vixTools.c
libvix_la_SOURCES += vixToolsEnvVars.c
070701000003A3000081A4000000000000000000000001682255050000800F000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix/foundryToolsDaemon.c  /*********************************************************
 * Copyright (c) 2003-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * foundryToolsDaemon.c --
 *
 *    VIX-specific TCLO cmds that are called through the backdoor
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#if defined(__linux__)
#include <sys/wait.h>
#include <mntent.h>
#include <paths.h>
#endif


#ifdef _WIN32
#include <io.h>
#else
#include <errno.h>
#include <unistd.h>
#endif

#ifdef _MSC_VER
#   include <Windows.h>
#   include <WinSock2.h>
#   include <WinSpool.h>
#   include "windowsu.h"
#elif _WIN32
#   include "win95.h"
#endif

#include "vmware.h"
#include "procMgr.h"
#include "vm_version.h"
#include "message.h"

#define G_LOG_DOMAIN  "vix"
#include <glib.h>
#include <glib/gstdio.h>

#include "vixPluginInt.h"
#include "vmware/tools/utils.h"

#include "util.h"
#include "strutil.h"
#include "str.h"
#include "file.h"
#include "err.h"
#include "hostinfo.h"
#include "guest_os.h"
#include "guest_msg_def.h"
#include "conf.h"
#include "vixCommands.h"
#include "base64.h"
#include "syncDriver.h"
#include "hgfsServerManager.h"
#include "hgfs.h"
#include "system.h"
#include "codeset.h"
#include "vixToolsInt.h"

#if defined(__linux__)
#include "mntinfo.h"
#include "hgfsDevLinux.h"
#endif

/* Only Win32, Linux, Solaris and FreeBSD use impersonation functions. */
#if !defined(__APPLE__)
#include "impersonate.h"
#endif

#include "vixOpenSource.h"

#define MAX64_DECIMAL_DIGITS 20          /* 2^64 = 18,446,744,073,709,551,616 */

#if defined(__linux__) || defined(_WIN32)

# if defined(_WIN32)
#  define DECLARE_SYNCDRIVER_ERROR(name) DWORD name = ERROR_SUCCESS
#  define SYNCDRIVERERROR ERROR_GEN_FAILURE
# else
#  define DECLARE_SYNCDRIVER_ERROR(name) int name = 0
#  define SYNCDRIVERERROR errno
# endif

static SyncDriverHandle gSyncDriverHandle = SYNCDRIVER_INVALID_HANDLE;

static Bool ToolsDaemonSyncDriverThawCallback(void *clientData);
#endif

static char *ToolsDaemonTcloGetQuotedString(const char *args,
                                            const char **endOfArg);

static VixError ToolsDaemonTcloGetEncodedQuotedString(const char *args,
                                                      const char **endOfArg,
                                                      char **result);

gboolean ToolsDaemonTcloReceiveVixCommand(RpcInData *data);

#if defined(__linux__) || defined(_WIN32)
gboolean ToolsDaemonTcloSyncDriverFreeze(RpcInData *data);

gboolean ToolsDaemonTcloSyncDriverThaw(RpcInData *data);
#endif

gboolean ToolsDaemonTcloMountHGFS(RpcInData *data);

void ToolsDaemonTcloReportProgramCompleted(const char *requestName,
                                           VixError err,
                                           int exitCode,
                                           int64 pid,
                                           void *clientData);

/*
 * These constants are a bad hack. I really should generate the result
 * strings twice, once to compute the length and then allocate the buffer,
 * and a second time to write the buffer.
 */
#define DEFAULT_RESULT_MSG_MAX_LENGTH     1024

static Bool thisProcessRunsAsRoot = FALSE;


/*
 *-----------------------------------------------------------------------------
 *
 * FoundryToolsDaemonRunProgram --
 *
 *    Run a named program on the guest.
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
FoundryToolsDaemonRunProgram(RpcInData *data) // IN
{
   VixError err;
   char *requestName = NULL;
   char *commandLine = NULL;
   char *commandLineArgs = NULL;
   char *credentialTypeStr = NULL;
   char *obfuscatedNamePassword = NULL;
   char *directoryPath = NULL;
   char *environmentVariables = NULL;
   static char resultBuffer[DEFAULT_RESULT_MSG_MAX_LENGTH];
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   ProcMgr_Pid pid = -1;
   GMainLoop *eventQueue = ((ToolsAppCtx *)data->appCtx)->mainLoop;

   /*
    * Parse the arguments. Some of these are optional, so they
    * may be NULL.
    */
   requestName = ToolsDaemonTcloGetQuotedString(data->args, &data->args);

   err = ToolsDaemonTcloGetEncodedQuotedString(data->args, &data->args,
                                               &commandLine);
   if (err != VIX_OK) {
      goto quit;
   }

   err = ToolsDaemonTcloGetEncodedQuotedString(data->args, &data->args,
                                               &commandLineArgs);
   if (err != VIX_OK) {
      goto quit;
   }

   credentialTypeStr = ToolsDaemonTcloGetQuotedString(data->args, &data->args);
   obfuscatedNamePassword = ToolsDaemonTcloGetQuotedString(data->args, &data->args);
   directoryPath = ToolsDaemonTcloGetQuotedString(data->args, &data->args);
   environmentVariables = ToolsDaemonTcloGetQuotedString(data->args, &data->args);

   /*
    * Make sure we are passed the correct arguments.
    * Some of these arguments (like credentialTypeStr and obfuscatedNamePassword) are optional,
    * so they may be NULL.
    */
   if ((NULL == requestName) || (NULL == commandLine)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   if ((NULL != credentialTypeStr)
         && (*credentialTypeStr)
         && (thisProcessRunsAsRoot)) {
      impersonatingVMWareUser = VixToolsImpersonateUserImpl(credentialTypeStr,
                                                            VIX_USER_CREDENTIAL_NONE,
                                                            obfuscatedNamePassword,
                                                            &userToken);
      if (!impersonatingVMWareUser) {
         err = VIX_E_GUEST_USER_PERMISSIONS;
         goto quit;
      }
   }

   err = VixToolsRunProgramImpl(requestName,
                                commandLine,
                                commandLineArgs,
                                0,
                                userToken,
                                eventQueue,
                                (int64 *) &pid);

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   /*
    * All VMXI tools commands return results that start with a VMXI error
    * and a guest-OS-specific error.
    */
   Str_Sprintf(resultBuffer,
               sizeof(resultBuffer),
               "%"FMT64"d %d %"FMT64"d",
               err,
               Err_Errno(),
               (int64) pid);
   RPCIN_SETRETVALS(data, resultBuffer, TRUE);

   /*
    * These were allocated by ToolsDaemonTcloGetQuotedString.
    */
   free(requestName);
   free(commandLine);
   free(credentialTypeStr);
   free(obfuscatedNamePassword);
   free(directoryPath);
   free(environmentVariables);
   free(commandLineArgs);

   return TRUE;
} // FoundryToolsDaemonRunProgram


/*
 *-----------------------------------------------------------------------------
 *
 * FoundryToolsDaemonGetToolsProperties --
 *
 *    Get information about test features.
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
FoundryToolsDaemonGetToolsProperties(RpcInData *data) // IN
{
   VixError err;
   int additionalError = 0;
   static char resultBuffer[DEFAULT_RESULT_MSG_MAX_LENGTH];
   char *serializedBuffer = NULL;
   size_t serializedBufferLength = 0;
   char *base64Buffer = NULL;
   size_t base64BufferLength = 0;
   Bool success;
   char *returnBuffer = NULL;
   GKeyFile *confDictRef;

   /*
    * Collect some values about the host.
    */
   confDictRef = data->clientData;

   err = VixTools_GetToolsPropertiesImpl(confDictRef,
                                         &serializedBuffer,
                                         &serializedBufferLength);
   if (VIX_OK == err) {
      base64BufferLength = Base64_EncodedLength(serializedBuffer, serializedBufferLength) + 1;
      base64Buffer = Util_SafeMalloc(base64BufferLength);
      success = Base64_Encode(serializedBuffer,
                              serializedBufferLength,
                              base64Buffer,
                              base64BufferLength,
                              &base64BufferLength);
      if (!success) {
         base64Buffer[0] = 0;
         err = VIX_E_FAIL;
         goto quit;
      }
      base64Buffer[base64BufferLength] = 0;
   }


quit:
   returnBuffer = base64Buffer;
   if (NULL == base64Buffer) {
      returnBuffer = "";
   }
   if (VIX_OK != err) {
      additionalError = Err_Errno();
   }

   /*
    * All VMXI tools commands return results that start with a VMXI error
    * and a guest-OS-specific error.
    */
   Str_Sprintf(resultBuffer,
               sizeof(resultBuffer),
               "%"FMT64"d %d %s",
               err,
               additionalError,
               returnBuffer);
   RPCIN_SETRETVALS(data, resultBuffer, TRUE);

   free(serializedBuffer);
   free(base64Buffer);

   return TRUE;
} // FoundryToolsDaemonGetToolsProperties


/**
 * Initializes internal state of the Foundry daemon.
 *
 * @param[in]  ctx      Application context.
 */

void
FoundryToolsDaemon_Initialize(ToolsAppCtx *ctx)
{
   thisProcessRunsAsRoot = TOOLS_IS_MAIN_SERVICE(ctx);

   /*
    * TODO: Add the original/native environment (envp) to ToolsAppContext so
    * we can know what the environment variables were before the loader scripts
    * changed them.
    */
   (void) VixTools_Initialize(thisProcessRunsAsRoot,
#if defined(__FreeBSD__)
                              ctx->envp,   // envp
#else
                              NULL,        // envp
#endif
                              ToolsDaemonTcloReportProgramCompleted,
                              ctx);

#if !defined(__APPLE__)
   if (thisProcessRunsAsRoot) {
      Impersonate_Init();
   }
#endif

}


/**
 * Uninitializes internal state of the Foundry daemon.
 *
 * @param[in]  ctx      Application context.
 */

void
FoundryToolsDaemon_Uninitialize(ToolsAppCtx *ctx)
{
   VixTools_Uninitialize();
}


/**
 * Restrict VIX commands in Foundry daemon.
 *
 * @param[in]  ctx        Application context.
 * @param[in]  restricted TRUE/FALSE=>enable/disable restriction.
 */

void
FoundryToolsDaemon_RestrictVixCommands(ToolsAppCtx *ctx, gboolean restricted)
{
   VixTools_RestrictCommands(restricted);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonTcloGetQuotedString --
 *
 *    Extract a quoted string from the middle of an argument string.
 *    This is different than normal tokenizing in a few ways:
 *       * Whitespace is a separator outside quotes, but not inside quotes.
 *       * Quotes always come in pairs, so "" is am empty string. An empty
 *          string may appear anywhere in the string, even at the end, so
 *          a string that is "" contains 1 empty string, not 2.
 *       * The string may use whitespace to separate the op-name from the params,
 *          and then quoted params to skip whitespace inside a param.
 *
 * Return value:
 *    Allocates the string.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static char *
ToolsDaemonTcloGetQuotedString(const char *args,      // IN
                               const char **endOfArg) // OUT
{
   char *resultStr = NULL;
   char *endStr;

   while ((*args) && ('\"' != *args)) {
      args++;
   }
   if ('\"' == *args) {
      args++;
   }

   resultStr = Util_SafeStrdup(args);

   endStr = resultStr;
   while (*endStr) {
      if (('\\' == *endStr) && (*(endStr + 1))) {
         endStr += 2;
      } else if ('\"' == *endStr) {
         *endStr = 0;
         endStr++;
         break;
      } else {
         endStr++;
      }
   }

   if (NULL != endOfArg) {
      args += (endStr - resultStr);
      while (' ' == *args) {
         args++;
      }
      *endOfArg = args;
   }

   return resultStr;
} // ToolsDaemonTcloGetQuotedString


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonTcloGetEncodedQuotedString --
 *
 *    This is a wrapper for ToolsDaemonTcloGetQuotedString.
 *    It just decoded the string.
 *
 * Return value:
 *    Allocates the string.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
ToolsDaemonTcloGetEncodedQuotedString(const char *args,      // IN
                                      const char **endOfArg, // OUT
                                      char **result)         // OUT
{
   VixError err;
   char *rawResultStr = NULL;
   char *resultStr = NULL;

   rawResultStr = ToolsDaemonTcloGetQuotedString(args, endOfArg);
   if (NULL == rawResultStr) {
      err = VIX_OK;
      goto quit;
   }

   err = VixMsg_DecodeString(rawResultStr, &resultStr);

quit:
   free(rawResultStr);
   *result = resultStr;

   return err;
}

#if defined(__linux__) || defined(_WIN32)

/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonTcloSyncDriverFreeze --
 *
 *    Use the Sync Driver to freeze I/O in the guest..
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
ToolsDaemonTcloSyncDriverFreeze(RpcInData *data)
{
   static char resultBuffer[DEFAULT_RESULT_MSG_MAX_LENGTH];
   VixError err = VIX_OK;
   char *driveList;
   char *timeout;
   int timeoutVal;
   DECLARE_SYNCDRIVER_ERROR(sysError);
   ToolsAppCtx *ctx = data->appCtx;
   GKeyFile *confDictRef = ctx->config;
   Bool enableNullDriver;
   GSource *timer;
   char *excludedFileSystems;
   Bool ignoreFrozenFS;

   /*
    * Parse the arguments
    */
   driveList = ToolsDaemonTcloGetQuotedString(data->args, &data->args);
   timeout = ToolsDaemonTcloGetQuotedString(data->args, &data->args);

   /*
    * Validate the arguments.
    */
   if (NULL == driveList || NULL == timeout) {
      err = VIX_E_INVALID_ARG;
      g_warning("%s: Failed to get string args\n", __FUNCTION__);
      goto quit;
   }

   if (!StrUtil_StrToInt(&timeoutVal, timeout) || timeoutVal < 0) {
      g_warning("%s: Bad args, timeout '%s'\n",
                __FUNCTION__, timeout);
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   g_debug("%s: Got request to freeze '%s', timeout %d\n",
           __FUNCTION__, driveList, timeoutVal);

   /* Disallow multiple freeze calls. */
   if (gSyncDriverHandle != SYNCDRIVER_INVALID_HANDLE) {
      err = VIX_E_OBJECT_IS_BUSY;
      goto quit;
   }

   enableNullDriver = VMTools_ConfigGetBoolean(confDictRef,
                                               "vmbackup",
                                               "enableNullDriver",
                                               FALSE);
   excludedFileSystems = VMTools_ConfigGetString(confDictRef,
                                                  "vmbackup",
                                                  "excludedFileSystems",
                                                  NULL);
   ignoreFrozenFS = VMTools_ConfigGetBoolean(confDictRef,
                                             "vmbackup",
                                             "ignoreFrozenFileSystems",
                                             FALSE);

   /* Perform the actual freeze. */
   if (!SyncDriver_Freeze(driveList, enableNullDriver, &gSyncDriverHandle,
                          excludedFileSystems, ignoreFrozenFS) ||
       SyncDriver_QueryStatus(gSyncDriverHandle, INFINITE) != SYNCDRIVER_IDLE) {
      g_warning("%s: Failed to Freeze drives '%s'\n",
                __FUNCTION__, driveList);
      err = VIX_E_FAIL;
      sysError = SYNCDRIVERERROR;
      if (gSyncDriverHandle != SYNCDRIVER_INVALID_HANDLE) {
         SyncDriver_Thaw(gSyncDriverHandle);
         SyncDriver_CloseHandle(&gSyncDriverHandle);
      }
      goto quit;
   }

   /* Start the timer callback to automatically thaw. */
   if (0 != timeoutVal) {
      g_debug("%s: Starting timer callback %d\n",
              __FUNCTION__, timeoutVal);
      timer = g_timeout_source_new(timeoutVal * 10);
      VMTOOLSAPP_ATTACH_SOURCE(ctx, timer, ToolsDaemonSyncDriverThawCallback, NULL, NULL);
      g_source_unref(timer);
   }

quit:
   /*
    * These were allocated by ToolsDaemonTcloGetQuotedString.
    */
   free(driveList);
   free(timeout);

   /*
    * All Foundry tools commands return results that start with a
    * foundry error and a guest-OS-specific error.
    */
   Str_Sprintf(resultBuffer, sizeof resultBuffer, "%"FMT64"d %d", err, sysError);
   g_message("%s: returning %s\n", __FUNCTION__, resultBuffer);
   return RPCIN_SETRETVALS(data, resultBuffer, TRUE);
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonSyncDriverThawCallback --
 *
 *      Callback to thaw all currently frozen drives if they have not been
 *      thawed already.
 *
 * Results:
 *      TRUE (returning FALSE will stop the event loop)
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(__linux__) || defined(_WIN32)
static Bool
ToolsDaemonSyncDriverThawCallback(void *clientData) // IN (ignored)
{
   g_debug("%s: Timed out waiting for thaw.\n", __FUNCTION__);

   if (gSyncDriverHandle == SYNCDRIVER_INVALID_HANDLE) {
      g_warning("%s: No drives are frozen.\n", __FUNCTION__);
      goto exit;
   }
   if (!SyncDriver_Thaw(gSyncDriverHandle)) {
      g_warning("%s: Failed to thaw.\n", __FUNCTION__);
   }

exit:
   SyncDriver_CloseHandle(&gSyncDriverHandle);
   return TRUE;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonTcloSyncDriverThaw --
 *
 *    Thaw I/O previously frozen by the Sync Driver.
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(__linux__) || defined(_WIN32)
gboolean
ToolsDaemonTcloSyncDriverThaw(RpcInData *data) // IN
{
   static char resultBuffer[DEFAULT_RESULT_MSG_MAX_LENGTH];
   VixError err = VIX_OK;
   DECLARE_SYNCDRIVER_ERROR(sysError);

   /*
    * This function has no arguments that we care about.
    */

   g_debug("%s: Got request to thaw\n", __FUNCTION__);

   if (gSyncDriverHandle == SYNCDRIVER_INVALID_HANDLE) {
      err = VIX_E_GUEST_VOLUMES_NOT_FROZEN;
      sysError = SYNCDRIVERERROR;
      g_warning("%s: No drives are frozen.\n", __FUNCTION__);
   } else if (!SyncDriver_Thaw(gSyncDriverHandle)) {
      err = VIX_E_FAIL;
      sysError = SYNCDRIVERERROR;
      g_warning("%s: Failed to Thaw drives\n", __FUNCTION__);
   }

   SyncDriver_CloseHandle(&gSyncDriverHandle);

   /*
    * All Foundry tools commands return results that start with a
    * foundry error and a guest-OS-specific error.
    */
   Str_Sprintf(resultBuffer, sizeof resultBuffer, "%"FMT64"d %d", err, sysError);
   g_message("%s: returning %s\n", __FUNCTION__, resultBuffer);
   return RPCIN_SETRETVALS(data, resultBuffer, TRUE);
}
#endif


#if defined(__linux__)
/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonCheckMountedHGFS --
 *
 *    Check if the HGFS file system is already mounted.
 *
 * Return value:
 *    VIX_OK and vmhgfsMntFound is TRUE if mounted or FALSE if not.
 *    set VixError otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
ToolsDaemonCheckMountedHGFS(Bool isFuseEnabled,      // IN:
                            Bool *vmhgfsMntFound)    // OUT: HGFS is mounted
{
   MNTHANDLE mtab;
   DECLARE_MNTINFO(mnt);
   const char *fsName;
   const char *fsType;
   VixError err = VIX_OK;

   if ((mtab = OPEN_MNTFILE("r")) == NULL) {
      err = VIX_E_FAIL;
      g_warning("%s: ERROR: opening mounted file system table -> %d\n", __FUNCTION__, errno);
      goto exit;
   }

   *vmhgfsMntFound = FALSE;
   if (isFuseEnabled) {
      fsName = HGFS_FUSENAME;
      fsType = HGFS_FUSETYPE;
   } else {
      fsName = ".host:/";
      fsType = HGFS_NAME;
   }
   while (GETNEXT_MNTINFO(mtab, mnt)) {
      if ((strcmp(MNTINFO_NAME(mnt), fsName) == 0) &&
            (strcmp(MNTINFO_FSTYPE(mnt), fsType) == 0) &&
            (strcmp(MNTINFO_MNTPT(mnt), HGFS_MOUNT_POINT) == 0)) {
         *vmhgfsMntFound = TRUE;
         g_debug("%s: mnt fs \"%s\" type \"%s\" dir \"%s\"\n", __FUNCTION__,
                  MNTINFO_NAME(mnt), MNTINFO_FSTYPE(mnt), MNTINFO_MNTPT(mnt));
         break;
      }
   }
   /*
    * Coverity flags this invocation of CLOSE_MNTFILE because the macro does
    * a test whose results are ignored.  However, it also has a needed side
    * effect, so this invocation is correct.
    */
   /* coverity[no_effect_test] */
   CLOSE_MNTFILE(mtab);

exit:
   return err;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonTcloMountHGFS --
 *
 *    Mount the HGFS file system.
 *
 *    This will do nothing if the file system is already mounted. In some cases
 *    it might be necessary to create the mount path too.
 *
 * Return value:
 *    TRUE always and VixError status for the RPC call reply.
 *    VIX_OK if mount succeeded or was already mounted
 *    VIX_E_FAIL if we couldn't check the mount was available
 *    VIX_E_HGFS_MOUNT_FAIL if the mount operation itself failed
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
ToolsDaemonTcloMountHGFS(RpcInData *data) // IN
{
   VixError err = VIX_OK;
   static char resultBuffer[DEFAULT_RESULT_MSG_MAX_LENGTH];

#if defined(__linux__)
#define MOUNT_PATH_BIN       "/bin/mount"
#define MOUNT_PATH_USR_BIN   "/usr" MOUNT_PATH_BIN
#define MOUNT_HGFS_PATH      "/mnt/hgfs"
#define MOUNT_HGFS_ARGS      " -t vmhgfs .host:/ " MOUNT_HGFS_PATH

   /*
    * Look for a vmhgfs mount at /mnt/hgfs. If one exists, nothing
    * else needs to be done.  If one doesn't exist, then mount at
    * that location.
    */
   ProcMgr_ProcArgs vmhgfsExecProcArgs;
   Bool execRes;
   const char *mountCmd = NULL;
   Bool isFuseEnabled = TRUE;
   Bool vmhgfsMntFound = FALSE;
   Bool vmhgfsMntPointCreated = FALSE;
   Bool validFuseExitCode;
   int fuseExitCode;
   int ret;

   vmhgfsExecProcArgs.envp = NULL;
   vmhgfsExecProcArgs.workingDirectory = NULL;

   execRes = ProcMgr_ExecSyncWithExitCode("/usr/bin/vmhgfs-fuse --enabled",
                                          &vmhgfsExecProcArgs,
                                          &validFuseExitCode,
                                          &fuseExitCode);
   if (!execRes) {
      if (validFuseExitCode && fuseExitCode == 2) {
         g_warning("%s: vmhgfs-fuse -> FUSE not installed\n", __FUNCTION__);
         err = VIX_E_HGFS_MOUNT_FAIL;
         goto exit;
      }
      g_message("%s: vmhgfs-fuse -> %d: not supported on this kernel version\n",
                __FUNCTION__, validFuseExitCode ? fuseExitCode : 0);
      isFuseEnabled = FALSE;
   }

   err = ToolsDaemonCheckMountedHGFS(isFuseEnabled, &vmhgfsMntFound);
   if (err != VIX_OK) {
      goto exit;
   }

   if (vmhgfsMntFound) {
      g_message("%s: vmhgfs already mounted\n", __FUNCTION__);
      goto exit;
   }

   /* Verify that mount point exists, if not create it. */
   ret = g_access(MOUNT_HGFS_PATH, F_OK);
   if (ret != 0) {
      g_message("%s: no mount point found, create %s\n", __FUNCTION__, MOUNT_HGFS_PATH);
      ret = g_mkdir_with_parents(MOUNT_HGFS_PATH, 0755);
      if (ret != 0) {
         err = VIX_E_HGFS_MOUNT_FAIL;
         g_warning("%s: ERROR: vmhgfs mount point creation -> %d\n", __FUNCTION__, errno);
         goto exit;
      }
      vmhgfsMntPointCreated = TRUE;
   }

   /* Do the HGFS mount. */
   if (isFuseEnabled) {
      mountCmd = "/usr/bin/vmhgfs-fuse .host:/ /mnt/hgfs -o subtype=vmhgfs-fuse,allow_other";
   } else {
      /*
       * We need to call the mount program, not the mount system call. The
       * mount program does several additional things, like compute the mount
       * options from the contents of /etc/fstab, and invoke custom mount
       * programs like the one needed for HGFS.
       */
      ret = g_access(MOUNT_PATH_USR_BIN, F_OK);
      if (ret == 0) {
         mountCmd = MOUNT_PATH_USR_BIN MOUNT_HGFS_ARGS;
      } else {
         ret = g_access(MOUNT_PATH_BIN, F_OK);
         if (ret == 0) {
            mountCmd = MOUNT_PATH_BIN MOUNT_HGFS_ARGS;
         } else {
            g_warning("%s: failed to find mount -> %d\n", __FUNCTION__, errno);
            err = VIX_E_HGFS_MOUNT_FAIL;
            goto exit;
         }
      }
   }

   g_debug("%s: Mounting: %s\n", __FUNCTION__, mountCmd);
   execRes = ProcMgr_ExecSync(mountCmd, &vmhgfsExecProcArgs);
   if (!execRes) {
      err = VIX_E_HGFS_MOUNT_FAIL;
      g_warning("%s: ERROR: no vmhgfs mount\n", __FUNCTION__);
   }
exit:
   if (err != VIX_OK) {
      if (vmhgfsMntPointCreated) {
         ret = g_rmdir(MOUNT_HGFS_PATH);
         if (ret != 0) {
            g_warning("%s: vmhgfs mount point not deleted %d\n", __FUNCTION__, errno);
         }
      }
   }
#endif

   /*
    * All tools commands return results that start with an error
    * and a guest-OS-specific error.
    */
   Str_Sprintf(resultBuffer,
               sizeof(resultBuffer),
               "%"FMT64"d %d",
               err,
               Err_Errno());
   RPCIN_SETRETVALS(data, resultBuffer, TRUE);

   g_message("%s: returning %s\n", __FUNCTION__, resultBuffer);

   return TRUE;
} // ToolsDaemonTcloMountHGFS


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonTcloReportProgramCompleted --
 *
 *
 * Return value:
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
ToolsDaemonTcloReportProgramCompleted(const char *requestName,    // IN
                                      VixError err,               // IN
                                      int exitCode,               // IN
                                      int64 pid,                  // IN
                                      void *clientData)           // IN
{
   Bool sentResult;
   ToolsAppCtx *ctx = clientData;
   gchar *msg = g_strdup_printf("%s %s %"FMT64"d %d %d %"FMT64"d",
                                VIX_BACKDOORCOMMAND_RUN_PROGRAM_DONE,
                                requestName,
                                err,
                                Err_Errno(),
                                exitCode,
                                (int64) pid);

   sentResult = RpcChannel_Send(ctx->rpc, msg, strlen(msg) + 1, NULL, NULL);
   g_free(msg);

   if (!sentResult) {
      g_warning("%s: Unable to send results from polling the result program.\n",
                __FUNCTION__);
   }
} // ToolsDaemonTcloReportProgramCompleted


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsDaemonTcloReceiveVixCommand --
 *
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

gboolean
ToolsDaemonTcloReceiveVixCommand(RpcInData *data) // IN
{
   VixError err = VIX_OK;
   uint32 additionalError = 0;
   char *requestName = NULL;
   VixCommandRequestHeader *requestMsg = NULL;
   size_t maxResultBufferSize;
   size_t tcloBufferLen;
   char *resultValue = NULL;
   size_t resultValueLength = 0;
   Bool deleteResultValue = FALSE;
   char *destPtr = NULL;
   int vixPrefixDataSize = (MAX64_DECIMAL_DIGITS * 2)
                             + (sizeof(' ') * 2)
                             + sizeof('\0')
                             + sizeof(' ') * 10;   // for RPC header

   /*
    * Our temporary buffer will be the same size as what the
    * Tclo/RPC system can handle, which is GUESTMSG_MAX_IN_SIZE.
    */
   static char tcloBuffer[GUESTMSG_MAX_IN_SIZE];

   ToolsAppCtx *ctx = data->appCtx;
   GMainLoop *eventQueue = ctx->mainLoop;
   GKeyFile *confDictRef = ctx->config;

   requestName = ToolsDaemonTcloGetQuotedString(data->args, &data->args);

   /*
    * Skip the NULL, char, and then the rest of the buffer should just
    * be a Vix command object.
    */
   while (*data->args) {
      data->args += 1;
   }
   data->args += 1;
   err = VixMsg_ValidateMessage((char *) data->args, data->argsSize);
   if (VIX_OK != err) {
      goto quit;
   }
   requestMsg = (VixCommandRequestHeader *) data->args;
   maxResultBufferSize = sizeof(tcloBuffer) - vixPrefixDataSize;

   err = VixTools_ProcessVixCommand(requestMsg,
                                    requestName,
                                    maxResultBufferSize,
                                    confDictRef,
                                    eventQueue,
                                    &resultValue,
                                    &resultValueLength,
                                    &deleteResultValue);

   /*
    * NOTE: We have always been returning an additional 32 bit error (errno,
    * or GetLastError() for Windows) along with the 64 bit VixError. The VMX
    * side has been dropping the higher order 32 bits of VixError (by copying
    * it onto a 32 bit error). They do save the additional error but as far
    * as we can tell, it was not getting used by foundry. So at this place,
    * for certain guest commands that have extra error information tucked into
    * the higher order 32 bits of the VixError, we use that extra error as the
    * additional error to be sent back to VMX.
    */
   additionalError = VixTools_GetAdditionalError(requestMsg->opCode, err);
   if (additionalError) {
      g_message("%s: command %u, additionalError = %u\n",
                __FUNCTION__, requestMsg->opCode, additionalError);
   } else {
      g_debug("%s: command %u, additionalError = %u\n",
              __FUNCTION__, requestMsg->opCode, additionalError);
   }

quit:
   tcloBufferLen = resultValueLength + vixPrefixDataSize;

   /*
    * If we generated a message larger than tclo/Rpc can handle,
    * we did something wrong.  Our code should never have done this.
    */
   if (tcloBufferLen > sizeof tcloBuffer) {
      ASSERT(0);
      resultValue[0] = 0;
      tcloBufferLen = tcloBufferLen - resultValueLength;
      err = VIX_E_OUT_OF_MEMORY;
   }

   /*
    * All Foundry tools commands return results that start with a foundry error
    * and a guest-OS-specific error.
    */
   Str_Sprintf(tcloBuffer,
               sizeof tcloBuffer,
               "%"FMT64"d %d ",
               err,
               additionalError);
   destPtr = tcloBuffer + strlen(tcloBuffer);

   /*
    * If this is a binary result, then we put a # at the end of the ascii to
    * mark the end of ascii and the start of the binary data.
    */
   if ((NULL != requestMsg)
         && (requestMsg->commonHeader.commonFlags & VIX_COMMAND_GUEST_RETURNS_BINARY)) {
      *(destPtr++) = '#';
      data->resultLen = destPtr - tcloBuffer + resultValueLength;
   }

   /*
    * Copy the result. Don't use a strcpy, since this may be a binary buffer.
    */
   memcpy(destPtr, resultValue, resultValueLength);
   destPtr += resultValueLength;

   /*
    * If this is not binary data, then it should be a NULL terminated string.
    */
   if ((NULL == requestMsg)
         || !(requestMsg->commonHeader.commonFlags & VIX_COMMAND_GUEST_RETURNS_BINARY)) {
      *(destPtr++) = 0;
      data->resultLen = strlen(tcloBuffer) + 1;
   }

   data->result = tcloBuffer;

   if (deleteResultValue) {
      free(resultValue);
   }
   free(requestName);

   return TRUE;
} // ToolsDaemonTcloReceiveVixCommand

 070701000003A4000081A40000000000000000000000016822550500001515000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix/vixPlugin.c   /*********************************************************
 * Copyright (c) 2008-2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vixPlugin.c
 *
 * Tools Service entry point for the VIX plugin.
 */

#define G_LOG_DOMAIN "vix"

#include <string.h>

#include "vmware.h"
#include "syncDriver.h"
#include "vixCommands.h"
#include "vixPluginInt.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/vmbackup.h"

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif

/**
 * IO freeze signal handler. Restrict VIX commands.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The application context.
 * @param[in]  freeze   Whether I/O is being frozen.
 * @param[in]  data     Unused.
 */

static void
VixIOFreeze(gpointer src,
            ToolsAppCtx *ctx,
            gboolean freeze,
            gpointer data)
{
   FoundryToolsDaemon_RestrictVixCommands(ctx, freeze);
}

/**
 * Clean up internal state on shutdown.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Unused.
 * @param[in]  plugin   Plugin registration data.
 */

static void
VixShutdown(gpointer src,
            ToolsAppCtx *ctx,
            ToolsPluginData *plugin)
{
   FoundryToolsDaemon_Uninitialize(ctx);
}


/**
 *  Sends vix capabilites.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Unused.
 * @param[in]  set      Whether capabilities are being set.
 * @param[in]  data     Unused.
 *
 * @return List of capabilities.
 */

static GArray *
VixCapabilitiesCb(gpointer src,
                  ToolsAppCtx *ctx,
                  gboolean set,
                  gpointer data)
{
   const ToolsAppCapability caps[] = {
      { TOOLS_CAP_NEW, NULL, CAP_HOST_VERIFIED_SAML_TOKEN, 1},
   };

   return VMTools_WrapArray(caps, sizeof *caps, ARRAYSIZE(caps));
}


/**
 * Returns the registration data for either the guestd or userd process.
 *
 * @param[in]  ctx   The application context.
 *
 * @return The registration data.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "vix",
      NULL,
      NULL
   };

   RpcChannelCallback rpcs[] = {
      { VIX_BACKDOORCOMMAND_RUN_PROGRAM,
         FoundryToolsDaemonRunProgram, NULL, NULL, NULL, 0 },
      { VIX_BACKDOORCOMMAND_GET_PROPERTIES,
         FoundryToolsDaemonGetToolsProperties, NULL, NULL, 0 },
      { VIX_BACKDOORCOMMAND_COMMAND,
         ToolsDaemonTcloReceiveVixCommand, NULL, NULL, 0 },
      { VIX_BACKDOORCOMMAND_MOUNT_VOLUME_LIST,
         ToolsDaemonTcloMountHGFS, NULL, NULL, NULL, 0 },
   };
   ToolsPluginSignalCb sigs[] = {
      { TOOLS_CORE_SIG_SHUTDOWN, VixShutdown, &regData },
      { TOOLS_CORE_SIG_CAPABILITIES, VixCapabilitiesCb, NULL }
   };
   ToolsAppReg regs[] = {
      { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) },
      { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) }
   };

#if defined(G_PLATFORM_WIN32)
   ToolsCore_InitializeCOM(ctx);
#endif

   FoundryToolsDaemon_Initialize(ctx);
   regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));

   if (TOOLS_IS_MAIN_SERVICE(ctx) && SyncDriver_Init()) {
      size_t i;
      size_t reg;

      for (reg = 0; reg < ARRAYSIZE(regs); reg++) {
         if (regs[reg].type == TOOLS_APP_SIGNALS) {
            /*
             * If running the system daemon and if the sync driver is active,
             * add signal registrations for IO_FREEZE signal.
             */
            ToolsPluginSignalCb sysSigs[] = {
               { TOOLS_CORE_SIG_IO_FREEZE, VixIOFreeze, NULL }
            };

            for (i = 0; i < ARRAYSIZE(sysSigs); i++) {
               g_array_append_val(regs[reg].data, sysSigs[i]);
            }
         }
#if defined(_WIN32) || defined(__linux__)
         else if (regs[reg].type == TOOLS_APP_GUESTRPC) {
            /*
             * If running the system daemon and if the sync driver is active,
             * add RPC registrations for sync driver RPC commands.
             */
            RpcChannelCallback sysRpcs[] = {
               { VIX_BACKDOORCOMMAND_SYNCDRIVER_FREEZE,
                  ToolsDaemonTcloSyncDriverFreeze, NULL, NULL, NULL, 0 },
               { VIX_BACKDOORCOMMAND_SYNCDRIVER_THAW,
                  ToolsDaemonTcloSyncDriverThaw, NULL, NULL, NULL, 0 }
            };

            for (i = 0; i < ARRAYSIZE(sysRpcs); i++) {
               g_array_append_val(regs[reg].data, sysRpcs[i]);
            }
         }
#endif
      }
   }

   return &regData;
}
   070701000003A5000081A40000000000000000000000016822550500000700000000000000000000000000000000000000004700000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix/vixPluginInt.h    /*********************************************************
 * Copyright (C) 2008-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VIXPLUGININT_H_
#define _VIXPLUGININT_H_

/**
 * @file vixPluginInt.h
 *
 * Prototypes of VIX RPC handlers found in foundryToolsDaemon.c.
 */

#define Debug        g_debug
#define Warning      g_warning

#include "vmware/tools/guestrpc.h"
#include "vmware/tools/plugin.h"

void
FoundryToolsDaemon_Initialize(ToolsAppCtx *ctx);
void
FoundryToolsDaemon_Uninitialize(ToolsAppCtx *ctx);

void
FoundryToolsDaemon_RestrictVixCommands(ToolsAppCtx *ctx, gboolean restricted);

gboolean
FoundryToolsDaemonGetToolsProperties(RpcInData *data);

gboolean
ToolsDaemonTcloMountHGFS(RpcInData *data);

gboolean
ToolsDaemonTcloReceiveVixCommand(RpcInData *data);

gboolean
FoundryToolsDaemonRunProgram(RpcInData *data);

#if defined(__linux__) || defined(_WIN32)
gboolean
ToolsDaemonTcloSyncDriverFreeze(RpcInData *data);

gboolean
ToolsDaemonTcloSyncDriverThaw(RpcInData *data);
#endif

#endif /* _VIXPLUGININT_H_ */

070701000003A6000081A40000000000000000000000016822550500058E5A000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix/vixTools.c    /*********************************************************
 * Copyright (c) 2007-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vixTools.c --
 *
 *    VIX commands that run in the guest OS.
 */

/*
 * When adding new functions, be sure to update
 * VixToolsCheckIfVixCommandEnabled() and VixToolsSetAPIEnabledProperties()
 * (adding a property and associated code in apps/lib/foundry/foundryVM.c
 * if necessary).  The enabled properties provide hints to an API developer
 * as to which APIs are available, and can be affected to guest OS attributes
 * or guest-side configuration.
 *
 * See Vim.Vm.Guest.QueryDisabledMethods()
 *
 */

/*
 * Logging messages:
 *
 * All guestOps should log some common information:
 *
 * g_debug() of the operation and arguments for that guestop.
 *    This data could be considered sensitive so it should not be visible
 *    at default logging levels.
 * g_message() of the operation and its VIX return code.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>

#ifdef _WIN32
#include <WTypes.h>
#include <io.h>
#include "wminic.h"
#include "windowsu.h"
#include <sys/stat.h>
#include <time.h>
#define  SECURITY_WIN32
#include <Security.h>
#else
#include <unistd.h>
#endif

#if defined(sun) || defined(__FreeBSD__) || defined(__APPLE__)
#include <sys/stat.h>
#endif

#ifdef _MSC_VER
#   include <windows.h>
#elif _WIN32
#   include "win95.h"
#endif

#include "vmware.h"
#include "procMgr.h"
#include "timeutil.h"
#include "vm_version.h"
#include "message.h"
#include "dynarray.h"

#define G_LOG_DOMAIN  "vix"
#include <glib.h>

#include "util.h"
#include "strutil.h"
#include "str.h"
#include "file.h"
#include "err.h"
#include "hostinfo.h"
#include "guest_os.h"
#include "guest_msg_def.h"
#include "conf.h"
#include "vixCommands.h"
#include "base64.h"
#include "hostinfo.h"
#include "hgfsServerManager.h"
#include "hgfs.h"
#include "system.h"
#include "codeset.h"
#include "posix.h"
#include "unicode.h"
#include "hashTable.h"
#include "su.h"
#include "escape.h"

#if defined(__linux__) || defined(_WIN32)
#include "netutil.h"
#endif

#include "impersonate.h"
#include "vixOpenSource.h"
#include "vixToolsInt.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/log.h"

#ifdef _WIN32
#include "registryWin32.h"
#include "windowsu.h"
#endif /* _WIN32 */
#include "hgfsHelper.h"

#ifdef __linux__
#include "mntinfo.h"
#include <sys/vfs.h>
#endif

/*
 * No support for userworld.  Enable support for open vm tools when
 * USE_VGAUTH is defined.
 *
 * XXX - Currently no support for vgauth in Windows on arm64.
 */
#if ((defined(__linux__) && !defined(USERWORLD)) || defined(_WIN32)) && \
     (!defined(OPEN_VM_TOOLS) || defined(USE_VGAUTH)) && !defined(_ARM64_)
#define SUPPORT_VGAUTH 1
#else
#define SUPPORT_VGAUTH 0
#endif

#ifdef _WIN32
/*
 * vmwsu can't generate an impersonation token for local SYSTEM.
 */
#define  ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS 1
#endif

#if SUPPORT_VGAUTH
#include "VGAuthCommon.h"
#include "VGAuthError.h"
#include "VGAuthAuthentication.h"
#include "VGAuthAlias.h"

#define VMTOOLSD_APP_NAME "vmtoolsd"

#define VIXTOOLS_CONFIG_USE_VGAUTH_NAME "useVGAuth"
#define USE_VGAUTH_DEFAULT TRUE

static gboolean gSupportVGAuth = USE_VGAUTH_DEFAULT;
static gboolean QueryVGAuthConfig(GKeyFile *confDictRef);

#if ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS
static gchar *gCurrentUsername = NULL;

#define VIXTOOLS_CONFIG_ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS \
      "allowLocalSystemImpersonationBypass"
/*
 * This isn't on by default because it won't leave a complete
 * audit trail.
 */
#define ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS_DEFAULT FALSE

#endif

/*
 * XXX
 *
 * Holds the current impersonation token.
 *
 * This is a hack, dependent on there only being one impersonation
 * possible at a time anyways.  We need the HANDLE from inside the
 * VGAuthUserHandle to pass to other functions, so we can't throw
 * it out until Unimpersonate().
 *
 * A cleaner solution would be to not treat the userToken as a void *
 * (which is really a HANDLE on Windows) but instead make a small wrapper
 * struct containing a type and an optional HANDLE.  But this would
 * require massive changes all over, and make it very hard to turn off
 * VGAuth compilation.
 */

static VGAuthUserHandle *currentUserHandle = NULL;

#endif

#ifdef _WIN32
/*
 * Check bug 2508431 for more details. If an application is not built
 * with proper flags, 'creating a remote thread' to get the process
 * command line will crash the target process. To avoid any such crash,
 * 'remote thread' approach is not used by default.
 *
 * But 'remote thread' approach can be turned on (for whatever reason)
 * by setting the following option to true in the tools.conf file.
 *
 * For few processes, 'WMI' can provide detailed commandline information.
 * But using 'WMI' is a heavy weight approach and may affect the CPU
 * performance and hence it is disabled by default. It can always be
 * turned on by a setting (as mentioned below) in the tools.conf file.
 */
#define VIXTOOLS_CONFIG_USE_REMOTE_THREAD_PROCESS_COMMAND_LINE  \
      "useRemoteThreadForProcessCommandLine"

#define VIXTOOLS_CONFIG_USE_WMI_PROCESS_COMMAND_LINE  \
      "useWMIForProcessCommandLine"

#define USE_REMOTE_THREAD_PROCESS_COMMAND_LINE_DEFAULT FALSE
#define USE_WMI_PROCESS_COMMAND_LINE_DEFAULT FALSE

#endif

/*
 * This should be an allocated string containing the impersonated username
 * while impersonation is active, and NULL when its not.
 */
char *gImpersonatedUsername = NULL;


#define SECONDS_BETWEEN_POLL_TEST_FINISHED     1

/*
 * This is used by the PRODUCT_VERSION_STRING macro.
 */
#ifndef PRODUCT_VERSION_NUMBER
#define PRODUCT_VERSION_NUMBER "1.0.0"
#endif


/*
 * The config file groupname for API configuration.
 */
#define  VIX_TOOLS_CONFIG_API_GROUPNAME               "guestoperations"

/*
 * Authentication configuration.
 * There are various forms of authentication supported,
 * e.g. InfrastructureAgents, NamePassword, SSPI, SAML etc.
 *
 * NOTE: "InfrastructureAgents" refers to hashed shared
 * secret form of authentication.
 */
#define  VIX_TOOLS_CONFIG_API_AUTHENTICATION          "Authentication"
#define  VIX_TOOLS_CONFIG_AUTHTYPE_AGENTS             "InfrastructureAgents"

/*
 * The switch that controls all APIs
 */
#define  VIX_TOOLS_CONFIG_API_ALL_NAME                "disabled"

/*
 * Individual API names for configuration.
 *
 * These match the WSDL names in the vSphere API.
 */
#define  VIX_TOOLS_CONFIG_API_START_PROGRAM_NAME      "StartProgramInGuest"
#define  VIX_TOOLS_CONFIG_API_LIST_PROCESSES_NAME     "ListProcessesInGuest"
#define  VIX_TOOLS_CONFIG_API_TERMINATE_PROCESS_NAME  "TerminateProcessInGuest"
#define  VIX_TOOLS_CONFIG_API_READ_ENV_VARS_NAME      "ReadEnvironmentVariableInGuest"

#define  VIX_TOOLS_CONFIG_API_MAKE_DIRECTORY_NAME     "MakeDirectoryInGuest"
#define  VIX_TOOLS_CONFIG_API_DELETE_FILE_NAME        "DeleteFileInGuest"
#define  VIX_TOOLS_CONFIG_API_DELETE_DIRECTORY_NAME   "DeleteDirectoryInGuest"
#define  VIX_TOOLS_CONFIG_API_MOVE_DIRECTORY_NAME     "MoveDirectoryInGuest"
#define  VIX_TOOLS_CONFIG_API_MOVE_FILE_NAME          "MoveFileInGuest"
#define  VIX_TOOLS_CONFIG_API_CREATE_TMP_FILE_NAME    "CreateTemporaryFileInGuest"
#define  VIX_TOOLS_CONFIG_API_CREATE_TMP_DIRECTORY_NAME          "CreateTemporaryDirectoryInGuest"
#define  VIX_TOOLS_CONFIG_API_LIST_FILES_NAME         "ListFilesInGuest"
#define  VIX_TOOLS_CONFIG_API_CHANGE_FILE_ATTRS_NAME  "ChangeFileAttributesInGuest"
#define  VIX_TOOLS_CONFIG_API_INITIATE_FILE_TRANSFER_FROM_GUEST_NAME  "InitiateFileTransferFromGuest"
#define  VIX_TOOLS_CONFIG_API_INITIATE_FILE_TRANSFER_TO_GUEST_NAME  "InitiateFileTransferToGuest"

#define  VIX_TOOLS_CONFIG_API_VALIDATE_CREDENTIALS_NAME   "ValidateCredentialsInGuest"
#define  VIX_TOOLS_CONFIG_API_ACQUIRE_CREDENTIALS_NAME   "AcquireCredentialsInGuest"
#define  VIX_TOOLS_CONFIG_API_RELEASE_CREDENTIALS_NAME   "ReleaseCredentialsInGuest"

#define VIX_TOOLS_CONFIG_API_ADD_GUEST_ALIAS_NAME      "AddGuestAlias"
#define VIX_TOOLS_CONFIG_API_REMOVE_GUEST_ALIAS_NAME   "RemoveGuestAlias"
#define VIX_TOOLS_CONFIG_API_REMOVE_GUEST_ALIAS_BY_CERT_NAME   "RemoveGuestAliasByCert"
#define VIX_TOOLS_CONFIG_API_LIST_GUEST_ALIASES_NAME    "ListGuestAliases"
#define VIX_TOOLS_CONFIG_API_LIST_GUEST_MAPPED_ALIASES_NAME  "ListGuestMappedAliases"

#define  VIX_TOOLS_CONFIG_API_CREATE_REGISTRY_KEY_NAME     "CreateRegistryKeyInGuest"
#define  VIX_TOOLS_CONFIG_API_LIST_REGISTRY_KEYS_NAME      "ListRegistryKeysInGuest"
#define  VIX_TOOLS_CONFIG_API_DELETE_REGISTRY_KEY_NAME     "DeleteRegistryKeyInGuest"
#define  VIX_TOOLS_CONFIG_API_SET_REGISTRY_VALUE_NAME      "SetRegistryValueInGuest"
#define  VIX_TOOLS_CONFIG_API_LIST_REGISTRY_VALUES_NAME    "ListRegistryValuesInGuest"
#define  VIX_TOOLS_CONFIG_API_DELETE_REGISTRY_VALUE_NAME   "DeleteRegistryValueInGuest"

/*
 * State of a single asynch runProgram.
 */
typedef struct VixToolsRunProgramState {
   VixRunProgramOptions runProgramOptions;
   ProcMgr_AsyncProc    *procState;

   char                 *tempScriptFilePath;

   char                 *requestName;

   char                 *userName;
   char                 *password;

   void                 *eventQueue;
} VixToolsRunProgramState;


/*
 * State of a single asynch startProgram.
 *
 * On Windows, keep the user's token and profile HANDLEs around
 * so the profile isn't unloaded until the program exits.
 */
typedef struct VixToolsStartProgramState {
   ProcMgr_AsyncProc    *procState;

#if defined(_WIN32) && SUPPORT_VGAUTH
   HANDLE hToken;
   HANDLE hProfile;
#endif

   void                 *eventQueue;
} VixToolsStartProgramState;


/*
 * Tracks processes started via StartProgram, so their exit information can
 * be returned with ListProcessesEx()
 *
 * We need live and dead because the exit status is fetched by from
 * a timer loop, and StartProgram of a very short lived program
 * followed immediately by a ListProcesses could miss the program
 * if we don't save it off for before the timer fires.
 *
 * This data is also useful to optimize ListProcessesEx.
 *
 * Note that we save off the procState so that we keep an open
 * handle to the process, to prevent its PID from being recycled.
 * We need to hold this open until we no longer save the result
 * of the exited program.  This is documented as 5 minutes
 * (VIX_TOOLS_EXITED_PROGRAM_REAP_TIME) in the VMODL.
 */
typedef struct VixToolsStartedProgramState {
   char *cmdName;
   char *fullCommandLine;
   char *user;
   uint64 pid;
   time_t startTime;
   int exitCode;
   time_t endTime;
   Bool isRunning;
   ProcMgr_AsyncProc *procState;
   struct VixToolsStartedProgramState *next;
} VixToolsStartedProgramState;

static VixToolsStartedProgramState *startedProcessList = NULL;

/*
 * How long we keep the info of exited processes.
 */
#define  VIX_TOOLS_EXITED_PROGRAM_REAP_TIME  (5 * 60)

/*
 * This is used to cache the results of ListProcessesEx when the reply
 * is too large to fit over the backdoor, so multiple trips are needed
 * to fetch it.
 */
static GHashTable *listProcessesResultsTable = NULL;

/*
 * How long to keep around cached results in case the Vix side dies.
 *
 * Err on the very large; would hate to have it kick in just because
 * the other side is slow or there's an immense ammount of data.
 */
#define  SECONDS_UNTIL_LISTPROC_CACHE_CLEANUP   (10 * 60)

typedef struct VixToolsCachedListProcessesResult {
   char *resultBuffer;
   size_t resultBufferLen;
   int key;
#ifdef _WIN32
   wchar_t *userName;
#else
   uid_t euid;
#endif
} VixToolsCachedListProcessesResult;

/*
 * Simple unique hashkey used for ListProcessesEx results.
 */
static uint32 listProcessesResultsKey = 1;

static void VixToolsFreeCachedResult(gpointer p);

/*
 * This structure is designed to implemente CreateTemporaryFile,
 * CreateTemporaryDirectory VI guest operations.
 */
typedef struct VixToolsGetTempFileCreateNameFuncData {
   char *filePrefix;
   char *tag;
   char *fileSuffix;
} VixToolsGetTempFileCreateNameFuncData;

/*
 * Global state.
 */
static Bool thisProcessRunsAsRoot = FALSE;
static Bool allowConsoleUserOps = FALSE;
static VixToolsReportProgramDoneProcType reportProgramDoneProc = NULL;
static void *reportProgramDoneData = NULL;

/*
 * Reference to global configuration dictionary.
 * This reference is initialized right before processing
 * any VIX command and is reset afterwards.
 */
static GKeyFile *gConfDictRef = NULL;

/*
 * Global state to decide if VIX commands should be restricted.
 *
 * Performing most of the VIX commands when quiesce snapshot operation
 * has frozen the guest filesystem could lead to deadlock in the tools
 * service (bug 1210773). This does not happen with VIM clients using
 * guestOps because hostd enforces the ordering of all VM operations.
 * However, it is possible for VIX clients to issue an op that ends up
 * accessing the guest filesystem in frozen state.
 */
static gboolean gRestrictCommands = FALSE;

#ifndef _WIN32
typedef struct VixToolsEnvironmentTableIterator {
   char **envp;
   size_t pos;
} VixToolsEnvironmentTableIterator;

/*
 * Stores the environment variables to use when executing guest applications.
 */
static HashTable *userEnvironmentTable = NULL;
#endif
static HgfsServerMgrData gVixHgfsBkdrConn;

#define SECONDS_BETWEEN_INVALIDATING_HGFS_SESSIONS    120
#define SECONDS_BETWEEN_INVALIDATING_PROC_HANDLES      60

static VixError VixToolsGetFileInfo(VixCommandRequestHeader *requestMsg,
                                    char **result);

static VixError VixToolsSetFileAttributes(VixCommandRequestHeader *requestMsg);

static gboolean VixToolsMonitorAsyncProc(void *clientData);
static gboolean VixToolsMonitorStartProgram(void *clientData);
static void VixToolsRegisterHgfsSessionInvalidator(void *clientData);
static gboolean VixToolsInvalidateInactiveHGFSSessions(void *clientData);

static GSource *gHgfsSessionInvalidatorTimer = NULL;
static guint gHgfsSessionInvalidatorTimerId;

static GSource *gProcHandleInvalidatorTimer = NULL;
static guint gProcHandleInvalidatorTimerId;
static void VixToolsRegisterProcHandleInvalidator(void *clientData);
static gboolean VixToolsInvalidateStaleProcHandles(void *clientData);

static void VixToolsPrintFileInfo(const char *filePathName,
                                  char *fileName,
                                  Bool escapeStrs,
                                  char **destPtr,
                                  char *endDestPtr);

static int VixToolsGetFileExtendedInfoLength(const char *filePathName,
                                             const char *fileName);

static char *VixToolsPrintFileExtendedInfoEx(const char *filePathName,
                                             const char *fileName);

static void VixToolsPrintFileExtendedInfo(const char *filePathName,
                                          const char *fileName,
                                          char **destPtr,
                                          char *endDestPtr);

static const char *fileInfoFormatString = "<FileInfo>"
                                          "<Name>%s</Name>"
                                          "<FileFlags>%d</FileFlags>"
                                          "<FileSize>%"FMT64"d</FileSize>"
                                          "<ModTime>%"FMT64"d</ModTime>"
                                          "</FileInfo>";

static const char *listFilesRemainingFormatString = "<rem>%d</rem>";

#ifdef _WIN32
static const char *fileExtendedInfoWindowsFormatString = "<fxi>"
                                          "<Name>%s</Name>"
                                          "<ft>%d</ft>"
                                          "<fs>%"FMT64"u</fs>"
                                          "<mt>%"FMT64"u</mt>"
                                          "<ct>%"FMT64"u</ct>"
                                          "<at>%"FMT64"u</at>"
                                          "</fxi>";
#else
static const char *fileExtendedInfoLinuxFormatString = "<fxi>"
                                          "<Name>%s</Name>"
                                          "<ft>%d</ft>"
                                          "<fs>%"FMT64"u</fs>"
                                          "<mt>%"FMT64"u</mt>"
                                          "<at>%"FMT64"u</at>"
                                          "<uid>%d</uid>"
                                          "<gid>%d</gid>"
                                          "<perm>%d</perm>"
                                          "<slt>%s</slt>"
                                          "</fxi>";
#endif

static VixError VixToolsGetTempFile(VixCommandRequestHeader *requestMsg,
                                    void *userToken,
                                    Bool useSystemTemp,
                                    char **tempFile,
                                    int *tempFileFd);

static void VixToolsFreeRunProgramState(VixToolsRunProgramState *asyncState);
static void VixToolsFreeStartProgramState(VixToolsStartProgramState *asyncState);

static void VixToolsUpdateStartedProgramList(VixToolsStartedProgramState *state);
static void VixToolsFreeStartedProgramState(VixToolsStartedProgramState *state);

static VixError VixToolsStartProgramImpl(const char *requestName,
                                         const char *programPath,
                                         const char *arguments,
                                         const char *workingDir,
                                         int numEnvVars,
                                         const char **envVars,
                                         Bool startMinimized,
                                         void *userToken,
                                         void *eventQueue,
                                         int64 *pid);

static char *VixToolsGetImpersonatedUsername(void *userToken);

static const char *scriptFileBaseName = "vixScript";

static VixError VixToolsMoveObject(VixCommandRequestHeader *requestMsg);

static VixError VixToolsCreateTempFile(VixCommandRequestHeader *requestMsg,
                                       char **result);

static VixError VixToolsCreateTempFileInt(VixCommandRequestHeader *requestMsg,
                                          Bool useSystemTemp,
                                          char **result);

static VixError VixToolsReadVariable(VixCommandRequestHeader *requestMsg,
                                     char **result);

static VixError VixToolsGetEnvForUser(void *userToken,
                                      const char *name,
                                      char **value);

static VixError VixToolsReadEnvVariables(VixCommandRequestHeader *requestMsg,
                                         char **result);

static VixError VixToolsGetMultipleEnvVarsForUser(void *userToken,
                                                  const char *names,
                                                  unsigned int numNames,
                                                  char **result);

static VixError VixToolsGetAllEnvVarsForUser(void *userToken, char **result);

static VixError VixToolsWriteVariable(VixCommandRequestHeader *requestMsg);

static VixError VixToolsListProcesses(VixCommandRequestHeader *requestMsg,
                                      size_t maxBufferSize,
                                      char **result);

static VixError VixToolsPrintProcInfoEx(DynBuf *dstBuffer,
                                        const char *cmd,
                                        const char *name,
                                        uint64 pid,
                                        const char *user,
                                        time_t start,
                                        int exitCode,
                                        time_t exitTime);

static VixError VixToolsListDirectory(VixCommandRequestHeader *requestMsg,
                                      size_t maxBufferSize,
                                      char **result);

static VixError VixToolsListFiles(VixCommandRequestHeader *requestMsg,
                                  size_t maxBufferSize,
                                  char **result);

static VixError VixToolsInitiateFileTransferFromGuest(VixCommandRequestHeader *requestMsg,
                                                      char **result);

static VixError VixToolsInitiateFileTransferToGuest(VixCommandRequestHeader *requestMsg);

static VixError VixToolsKillProcess(VixCommandRequestHeader *requestMsg);

static VixError VixToolsCreateDirectory(VixCommandRequestHeader *requestMsg);

static VixError VixToolsRunScript(VixCommandRequestHeader *requestMsg,
                                  char *requestName,
                                  void *eventQueue,
                                  char **result);

static VixError VixToolsCheckUserAccount(VixCommandRequestHeader *requestMsg);

static VixError VixToolsProcessHgfsPacket(VixCommandHgfsSendPacket *requestMsg,
                                          GMainLoop *eventQueue,
                                          char **result,
                                          size_t *resultValueResult);

static VixError VixToolsListFileSystems(VixCommandRequestHeader *requestMsg,
                                        char **result);

#if defined(_WIN32) || defined(__linux__)
static VixError VixToolsPrintFileSystemInfo(char **destPtr,
                                            const char *endDestPtr,
                                            const char *name,
                                            uint64 size,
                                            uint64 freeSpace,
                                            const char *type,
                                            Bool escapeStrs,
                                            Bool *truncated);
#endif

static VixError VixToolsValidateCredentials(VixCommandRequestHeader *requestMsg);

static VixError VixToolsAcquireCredentials(VixCommandRequestHeader *requestMsg,
                                           GMainLoop *eventQueue,
                                           char **result);

static VixError VixToolsReleaseCredentials(VixCommandRequestHeader *requestMsg);

static VixError VixToolsCreateRegKey(VixCommandRequestHeader *requestMsg);

static VixError VixToolsListRegKeys(VixCommandRequestHeader *requestMsg,
                                    size_t maxBufferSize,
                                    void *eventQueue,
                                    char **result);

static VixError VixToolsDeleteRegKey(VixCommandRequestHeader *requestMsg);

static VixError VixToolsSetRegValue(VixCommandRequestHeader *requestMsg);

static VixError VixToolsListRegValues(VixCommandRequestHeader *requestMsg,
                                      size_t maxBufferSize,
                                      void *eventQueue,
                                      char **result);

static VixError VixToolsDeleteRegValue(VixCommandRequestHeader *requestMsg);

#if defined(__linux__) || defined(_WIN32)
static VixError VixToolsGetGuestNetworkingConfig(VixCommandRequestHeader *requestMsg,
                                                 char **resultBuffer,
                                                 size_t *resultBufferLength);
#endif

#if defined(_WIN32)
static VixError VixToolsSetGuestNetworkingConfig(VixCommandRequestHeader *requestMsg);
#endif

static VixError VixTools_Base64EncodeBuffer(char **resultValuePtr, size_t *resultValLengthPtr);

static VixError VixToolsSetSharedFoldersProperties(VixPropertyListImpl *propList);

static VixError VixToolsSetAPIEnabledProperties(VixPropertyListImpl *propList,
                                                GKeyFile *confDictRef);

#if defined(_WIN32)
static HRESULT VixToolsEnableDHCPOnPrimary(void);

static HRESULT VixToolsEnableStaticOnPrimary(const char *ipAddr,
                                             const char *subnetMask);
#endif

static VixError VixToolsImpersonateUserImplEx(char const *credentialTypeStr,
                                              int credentialType,
                                              char const *obfuscatedNamePassword,
                                              Bool loadUserProfile,
                                              void **userToken);

static VixError VixToolsDoesUsernameMatchCurrentUser(const char *username);

static Bool VixToolsPidRefersToThisProcess(ProcMgr_Pid pid);

#ifndef _WIN32
static void VixToolsBuildUserEnvironmentTable(const char * const *envp);

static char **VixToolsEnvironmentTableToEnvp(const HashTable *envTable);

static int VixToolsEnvironmentTableEntryToEnvpEntry(const char *key, void *value,
                                                    void *clientData);

static void VixToolsFreeEnvp(char **envp);

#endif

static VixError FoundryToolsDaemon_TranslateSystemErr(void);

static VixError VixToolsRewriteError(uint32 opCode,
                                     VixError origError);

static size_t VixToolsXMLStringEscapedLen(const char *str, Bool escapeStr);

static Bool GuestAuthEnabled(void);

VixError GuestAuthPasswordAuthenticateImpersonate(
   char const *obfuscatedNamePassword,
   Bool loadUserProfile,
   void **userToken);

VixError GuestAuthSAMLAuthenticateAndImpersonate(
   char const *obfuscatedNamePassword,
   Bool loadUserProfile,
   Bool hostVerified,
   void **userToken);

void GuestAuthUnimpersonate();

#if SUPPORT_VGAUTH

VGAuthError TheVGAuthContext(VGAuthContext **ctx);

#ifdef _WIN32
static void GuestAuthUnloadUserProfileAndToken(HANDLE hToken, HANDLE hProfile);
#endif

#endif


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_Initialize --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixTools_Initialize(Bool thisProcessRunsAsRootParam,                                // IN
                    const char * const *originalEnvp,                               // IN
                    VixToolsReportProgramDoneProcType reportProgramDoneProcParam,   // IN
                    void *clientData)                                               // IN
{
   VixError err = VIX_OK;
#if SUPPORT_VGAUTH
   ToolsAppCtx *ctx = (ToolsAppCtx *) clientData;
#if ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS
   if (!gCurrentUsername) {
      gCurrentUsername = VixToolsGetCurrentUsername();
      ASSERT(gCurrentUsername);
      g_message("%s: Toolsd running as user '%s'\n",
                __FUNCTION__, gCurrentUsername);
   }
#endif
#endif

   /*
    * Run unit tests on DEVEL builds.
    */
   DEVEL_ONLY(TestVixToolsEnvVars());

   thisProcessRunsAsRoot = thisProcessRunsAsRootParam;
   reportProgramDoneProc = reportProgramDoneProcParam;
   reportProgramDoneData = clientData;

#ifndef _WIN32
   VixToolsBuildUserEnvironmentTable(originalEnvp);
#endif

   /* Register a straight through connection with the Hgfs server. */
   HgfsServerManager_DataInit(&gVixHgfsBkdrConn,
                              VIX_BACKDOORCOMMAND_COMMAND,
                              NULL,    // no RPC registration
                              NULL);   // rpc callback
   HgfsServerManager_Register(&gVixHgfsBkdrConn);

   listProcessesResultsTable = g_hash_table_new_full(g_int_hash, g_int_equal,
                                                     NULL,
                                                     VixToolsFreeCachedResult);

#if SUPPORT_VGAUTH
   /*
    * We don't set up the VGAuth log handler, since the default
    * does what we want, and trying to redirect VGAuth messages
    * to Log() causes recursion and a crash.
    */
#endif

#if SUPPORT_VGAUTH
   gSupportVGAuth = QueryVGAuthConfig(ctx->config);
#endif

#ifdef _WIN32
   err = VixToolsInitializeWin32();
   if (VIX_FAILED(err)) {
      return err;
   }
#endif

   return(err);
} // VixTools_Initialize


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_Uninitialize --
 *
 *
 * Return value:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixTools_Uninitialize(void) // IN
{
   if (NULL != gHgfsSessionInvalidatorTimer) {
      g_source_remove(gHgfsSessionInvalidatorTimerId);
      g_source_unref(gHgfsSessionInvalidatorTimer);
      gHgfsSessionInvalidatorTimer = NULL;
      gHgfsSessionInvalidatorTimerId = 0;
      g_message("%s: HGFS session Invalidator detached\n", __FUNCTION__);
   }

   if (NULL != gProcHandleInvalidatorTimer) {
      g_source_remove(gProcHandleInvalidatorTimerId);
      g_source_unref(gProcHandleInvalidatorTimer);
      gProcHandleInvalidatorTimer = NULL;
      gProcHandleInvalidatorTimerId = 0;
      g_debug("%s: Process Handle Invalidator detached\n", __FUNCTION__);
   }

   HgfsServerManager_Unregister(&gVixHgfsBkdrConn);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_RestrictCommands --
 *
 *
 * Return value:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixTools_RestrictCommands(gboolean restricted) // IN
{
   gRestrictCommands = restricted;
}


#ifndef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsBuildUserEnvironmentTable --
 *
 *      Takes an array of strings of the form "<key>=<value>" storing the
 *      environment variables (as per environ(7)) that should be used when
 *      running programs, and populates the hash table with them.
 *
 *      If 'envp' is NULL, skip creating the user environment table, so that
 *      we just use the current environment.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      May initialize the global userEnvironmentTable.
 *
 *-----------------------------------------------------------------------------
 */

static void
VixToolsBuildUserEnvironmentTable(const char * const *envp)   // IN: optional
{
   if (NULL == envp) {
      ASSERT(NULL == userEnvironmentTable);
      return;
   }

   if (NULL == userEnvironmentTable) {
      userEnvironmentTable = HashTable_Alloc(64,  // buckets (power of 2)
                                             HASH_STRING_KEY | HASH_FLAG_COPYKEY,
                                             free); // freeFn for the values
   } else {
      /*
       * If we're being reinitialized, we can just clear the table and
       * load the new values into it. They shouldn't have changed, but
       * in case they ever do this will cover it.
       */
      HashTable_Clear(userEnvironmentTable);
   }

   for (; NULL != *envp; envp++) {
      char *name;
      char *value;
      char *whereToSplit;
      size_t nameLen;

      whereToSplit = strchr(*envp, '=');
      if (NULL == whereToSplit) {
         /* Our code generated this list, so this shouldn't happen. */
         ASSERT(0);
         continue;
      }

      nameLen = whereToSplit - *envp;
      name = Util_SafeMalloc(nameLen + 1);
      memcpy(name, *envp, nameLen);
      name[nameLen] = '\0';

      whereToSplit++;   // skip over '='

      value = Util_SafeStrdup(whereToSplit);

      HashTable_Insert(userEnvironmentTable, name, value);
      DEBUG_ONLY(value = NULL;)  // the hash table now owns 'value'

      free(name);
      DEBUG_ONLY(name = NULL;)
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsEnvironmentTableToEnvp --
 *
 *      Take a hash table storing environment variables names and values and
 *      build an array out of them.
 *
 * Results:
 *      char ** - envp array as per environ(7). Must be freed using
 *      VixToolsFreeEnvp
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static char **
VixToolsEnvironmentTableToEnvp(const HashTable *envTable)   // IN
{
   char **envp;

   if (NULL != envTable) {
      VixToolsEnvironmentTableIterator itr;
      size_t numEntries = HashTable_GetNumElements(envTable);

      itr.envp = envp = Util_SafeMalloc((numEntries + 1) * sizeof *envp);
      itr.pos = 0;

      HashTable_ForEach(envTable, VixToolsEnvironmentTableEntryToEnvpEntry, &itr);

      ASSERT(numEntries == itr.pos);

      envp[numEntries] = NULL;
   } else {
      envp = NULL;
   }

   return envp;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsEnvironmentTableEntryToEnvpEntry --
 *
 *      Callback for HashTable_ForEach(). Gets called for each entry in an
 *      environment table, converting the key (environment variable name) and
 *      value (environment variable value) into a string of the form
 *      "<key>=<value>" and adding that to the envp array passed in with the
 *      VixToolsEnvironmentTableIterator client data.
 *
 * Results:
 *      int - always 0
 *
 * Side effects:
 *      Sets one entry in the envp.
 *
 *-----------------------------------------------------------------------------
 */

static int
VixToolsEnvironmentTableEntryToEnvpEntry(const char *key,     // IN
                                         void *value,         // IN
                                         void *clientData)    // IN/OUT
{
   VixToolsEnvironmentTableIterator *itr = clientData;

   itr->envp[itr->pos++] = Str_SafeAsprintf(NULL, "%s=%s", key, (char *)value);

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsFreeEnvp --
 *
 *      Free's an array of strings where both the strings and the array
 *      were heap allocated.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
VixToolsFreeEnvp(char **envp)   // IN
{
   if (NULL != envp) {
      char **itr;

      for (itr = envp; NULL != *itr; itr++) {
         free(*itr);
      }

      free(envp);
   }
}
#endif  // #ifndef _WIN32


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_SetConsoleUserPolicy --
 *
 * This allows an external client of the tools to enable/disable this security
 * setting. This may be controlled by config or higher level user settings
 * that are not available to this library.
 *
 * Return value:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixTools_SetConsoleUserPolicy(Bool allowConsoleUserOpsParam)     // IN
{
   allowConsoleUserOps = allowConsoleUserOpsParam;
} // VixTools_SetConsoleUserPolicy


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_SetRunProgramCallback --
 *
 * Register a callback that reports when a program has completed.
 * Different clients of this library will use different IPC mechanisms for
 * sending this message. For example, it may use the backdoor or a socket.
 * Different sockets may use different message protocols, such as the
 * backdoor-on-a-socket or the Foundry network message.
 *
 * Return value:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixTools_SetRunProgramCallback(VixToolsReportProgramDoneProcType reportProgramDoneProcParam, // IN
                               void *clientData)                                             // IN
{
   reportProgramDoneProc = reportProgramDoneProcParam;
   reportProgramDoneData = clientData;
} // VixTools_SetRunProgramCallback


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_RunProgram --
 *
 *    Run a named program on the guest.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixTools_RunProgram(VixCommandRequestHeader *requestMsg, // IN
                    char *requestName,                   // IN
                    void *eventQueue,                    // IN
                    char **result)                       // OUT
{
   VixError err = VIX_OK;
   VixMsgRunProgramRequest *runProgramRequest;
   const char *commandLine = NULL;
   const char *commandLineArgs = NULL;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   int64 pid = -1;
   static char resultBuffer[32];
   VMAutomationRequestParser parser;

   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *runProgramRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   runProgramRequest = (VixMsgRunProgramRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                          runProgramRequest->programNameLength,
                                            &commandLine);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *commandLine) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
   if (runProgramRequest->commandLineArgsLength > 0) {
      err = VMAutomationRequestParserGetString(&parser,
                                     runProgramRequest->commandLineArgsLength,
                                               &commandLineArgs);
      if (VIX_OK != err) {
         goto quit;
      }
   }

#ifdef _WIN32
   if (runProgramRequest->runProgramOptions & VIX_RUNPROGRAM_RUN_AS_LOCAL_SYSTEM) {
      if (!VixToolsUserIsMemberOfAdministratorGroup(requestMsg)) {
         err = VIX_E_GUEST_USER_PERMISSIONS;
         goto quit;
      }
      userToken = PROCESS_CREATOR_USER_TOKEN;
   }
#endif

   if (NULL == userToken) {
      err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
      if (VIX_OK != err) {
         goto quit;
      }
      impersonatingVMWareUser = TRUE;
   }

   err = VixToolsRunProgramImpl(requestName,
                                commandLine,
                                commandLineArgs,
                                runProgramRequest->runProgramOptions,
                                userToken,
                                eventQueue,
                                &pid);

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   Str_Sprintf(resultBuffer, sizeof(resultBuffer), "%"FMT64"d", pid);
   *result = resultBuffer;

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixTools_RunProgram


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_StartProgram --
 *
 *    Start a program on the guest.  Much like RunProgram, but
 *    with additional arguments.  Another key difference is that
 *    the program's exitCode and endTime will be available to ListProcessesEx
 *    for a short time.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixTools_StartProgram(VixCommandRequestHeader *requestMsg, // IN
                      char *requestName,                   // IN
                      void *eventQueue,                    // IN
                      char **result)                       // OUT
{
   VixError err = VIX_OK;
   VixMsgStartProgramRequest *startProgramRequest;
   const char *programPath = NULL;
   const char *arguments = NULL;
   const char *workingDir = NULL;
   const char **envVars = NULL;
   const char *bp = NULL;
   const char *cmdNameBegin = NULL;
   Bool impersonatingVMWareUser = FALSE;
   int64 pid = -1;
   int i;
   void *userToken = NULL;
   static char resultBuffer[32];    // more than enough to hold a 64 bit pid
   VixToolsStartedProgramState *spState;
   VMAutomationRequestParser parser;

   err = VMAutomationRequestParserInit(&parser,
                                      requestMsg, sizeof *startProgramRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   startProgramRequest = (VixMsgStartProgramRequest *) requestMsg;

   /*
    * It seems that this functions uses the a string format that includes
    * the '\0' terminator in the length fields.
    * This is different from other "old" vix guest command format.
    */
   err = VMAutomationRequestParserGetOptionalString(&parser,
                                      startProgramRequest->programPathLength,
                                            &programPath);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL == programPath || 0 == *programPath) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VMAutomationRequestParserGetOptionalString(&parser,
                                          startProgramRequest->argumentsLength,
                                                    &arguments);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VMAutomationRequestParserGetOptionalString(&parser,
                                         startProgramRequest->workingDirLength,
                                                    &workingDir);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL != workingDir && '\0' == workingDir[0]) {
      /* Let's treat an empty string the same as NULL: use the default. */
      workingDir = NULL;
   }

   err = VMAutomationRequestParserGetOptionalStrings(&parser,
                                             startProgramRequest->numEnvVars,
                                             startProgramRequest->envVarLength,
                                                    &bp);
   if (VIX_OK != err) {
      goto quit;
   }

   if (startProgramRequest->numEnvVars > 0) {
      envVars = Util_SafeMalloc(sizeof(char*) * (startProgramRequest->numEnvVars + 1));
      for (i = 0; i < startProgramRequest->numEnvVars; i++) {
         envVars[i] = bp;
         bp += strlen(envVars[i]) + 1;
      }
      envVars[i] = NULL;

      err = VixToolsValidateEnviron(envVars);
      if (VIX_OK != err) {
         goto quit;
      }
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s args: progamPath: '%s', arguments: '%s', workingDir: '%s'\n",
           __FUNCTION__, IMPERSONATED_USERNAME, programPath,
          (NULL != arguments) ? arguments : "",
          (NULL != workingDir) ? workingDir : "");

   err = VixToolsStartProgramImpl(requestName,
                                  programPath,
                                  arguments,
                                  workingDir,
                                  startProgramRequest->numEnvVars,
                                  envVars,
                                  startProgramRequest->startMinimized,
                                  userToken,
                                  eventQueue,
                                  &pid);

   if (VIX_OK == err) {

      /*
       * Save off the program so ListProcessesEx can find it.
       *
       * We store it here to avoid the hole between starting it and the
       * exited process polling proc.
       */
      spState = Util_SafeMalloc(sizeof(VixToolsStartedProgramState));

      /*
       * Build up the command line so the args are passed to the command.
       * To be safe, always put quotes around the program name. If the name
       * contains spaces (either in the file name of its directory path),
       * then the quotes are required. If the name doesn't contain spaces, then
       * unnecessary quotes don't seem to create a problem for both Windows and
       * Linux.
       */
      if (NULL != arguments) {
         spState->fullCommandLine = Str_SafeAsprintf(NULL,
                                                     "\"%s\" %s",
                                                     programPath,
                                                     arguments);
      } else {
         spState->fullCommandLine = Str_SafeAsprintf(NULL,
                                                     "\"%s\"",
                                                     programPath);
      }
#if defined(_WIN32)
      /*
       * For windows, we let the VIX client parse the
       * command line to get the real command name.
       */
      spState->cmdName = NULL;
#else
      /*
       * Find the last path separator, to get the cmd name.
       * If no separator is found, then use the whole name.
       */
      cmdNameBegin = strrchr(programPath, '/');
      if (NULL == cmdNameBegin) {
         cmdNameBegin = programPath;
      } else {
         /*
          * Skip over the last separator.
          */
         cmdNameBegin++;
      }
      spState->cmdName = Str_SafeAsprintf(NULL, "%s", cmdNameBegin);
#endif
      spState->user = VixToolsGetImpersonatedUsername(&userToken);
      spState->pid = (uint64) pid;
      spState->startTime = time(NULL);
      spState->exitCode = 0;
      spState->endTime = 0;
      spState->isRunning = TRUE;
      spState->next = NULL;
      spState->procState = NULL;

      // add it to the list of started programs
      VixToolsUpdateStartedProgramList(spState);

      if (NULL != eventQueue) {
         /*
          * Register a timer to periodically invalidate any stale
          * process handles.
          */
         VixToolsRegisterProcHandleInvalidator(eventQueue);
      }
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   Str_Sprintf(resultBuffer, sizeof(resultBuffer), "%"FMT64"d", pid);
   *result = resultBuffer;

   free((char **) envVars);

   guest_debug("%s: returning '%s'\n", __FUNCTION__, resultBuffer);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixTools_StartProgram


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsRunProgramImpl --
 *
 *    Run a named program on the guest.
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsRunProgramImpl(char *requestName,      // IN
                       const char *commandLine,      // IN
                       const char *commandLineArgs,  // IN
                       int  runProgramOptions, // IN
                       void *userToken,        // IN
                       void *eventQueue,       // IN
                       int64 *pid)             // OUT,OPTIONAL
{
   VixError err = VIX_OK;
   char *fullCommandLine = NULL;
   VixToolsRunProgramState *asyncState = NULL;
   char *tempCommandLine = NULL;
   char *startProgramFileName;
   char *stopProgramFileName;
   Bool programExists;
   Bool programIsExecutable;
   ProcMgr_ProcArgs procArgs;
#if defined(_WIN32)
   Bool forcedRoot = FALSE;
   STARTUPINFO si;
   wchar_t *envBlock = NULL;
#endif
   GSource *timer;

   if (NULL != pid) {
      *pid = (int64) -1;
   }


   tempCommandLine = Util_SafeStrdup(commandLine);
   startProgramFileName = tempCommandLine;

   while (' ' == *startProgramFileName) {
      startProgramFileName++;
   }
   if ('\"' == *startProgramFileName) {
      startProgramFileName++;
      stopProgramFileName = strstr(startProgramFileName, "\"");
   } else {
      stopProgramFileName = NULL;
   }
   if (NULL == stopProgramFileName) {
      stopProgramFileName = startProgramFileName + strlen(startProgramFileName);
   }
   *stopProgramFileName = 0;

   /*
    * Check that the program exists.
    * On linux, we run the program by exec'ing /bin/sh, and that does not
    * return a clear error code indicating that the program does not exist
    * or cannot be executed.
    * This is a common and user-correctable error, however, so we want to
    * check for it and return a specific error code in this case.
    *
    */

   programExists = File_Exists(startProgramFileName);
   programIsExecutable =
      (FileIO_Access(startProgramFileName, FILEIO_ACCESS_EXEC) ==
                                                       FILEIO_SUCCESS);

   free(tempCommandLine);

   if (!programExists) {
      err = VIX_E_FILE_NOT_FOUND;
      goto quit;
   }
   if (!programIsExecutable) {
      err = VIX_E_GUEST_USER_PERMISSIONS;
      goto quit;
   }

   /*
    * Build up the command line so the args are passed to the command.
    * To be safe, always put quotes around the program name. If the name
    * contains spaces (either in the file name of its directory path),
    * then the quotes are required. If the name doesn't contain spaces, then
    * unnecessary quotes don't seem to create a problem for both Windows and
    * Linux.
    */
   if (NULL != commandLineArgs) {
      fullCommandLine = Str_SafeAsprintf(NULL,
                                     "\"%s\" %s",
                                     commandLine,
                                     commandLineArgs);
   } else {
      fullCommandLine = Str_SafeAsprintf(NULL,
                                     "\"%s\"",
                                     commandLine);
   }

   if (NULL == fullCommandLine) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   /*
    * Save some strings in the state.
    */
   asyncState = Util_SafeCalloc(1, sizeof *asyncState);
   asyncState->requestName = Util_SafeStrdup(requestName);
   asyncState->runProgramOptions = runProgramOptions;

   memset(&procArgs, 0, sizeof procArgs);
#if defined(_WIN32)
   if (PROCESS_CREATOR_USER_TOKEN != userToken) {
      /*
       * If we are impersonating a user then use the user's environment
       * block. That way the user-specific environment variables will
       * be available to the application (such as the user's TEMP
       * directory instead of the system-wide one).
       */
      err = VixToolsGetEnvBlock(userToken, &envBlock);
      if (VIX_OK != err) {
         goto quit;
      }

      forcedRoot = Impersonate_ForceRoot();
   }

   memset(&si, 0, sizeof si);
   procArgs.hToken = (PROCESS_CREATOR_USER_TOKEN == userToken) ? NULL : userToken;
   procArgs.bInheritHandles = TRUE;
   procArgs.lpStartupInfo = &si;
   si.cb = sizeof si;
   procArgs.dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
   procArgs.lpEnvironment = envBlock;
   si.dwFlags = STARTF_USESHOWWINDOW;
   si.wShowWindow = (VIX_RUNPROGRAM_ACTIVATE_WINDOW & runProgramOptions)
                     ? SW_SHOWNORMAL : SW_MINIMIZE;
#elif !defined(__FreeBSD__)
   procArgs.envp = VixToolsEnvironmentTableToEnvp(userEnvironmentTable);
#endif

   asyncState->procState = ProcMgr_ExecAsync(fullCommandLine, &procArgs);

#if defined(_WIN32)
   if (forcedRoot) {
      Impersonate_UnforceRoot();
   }
#else
   VixToolsFreeEnvp(procArgs.envp);
   DEBUG_ONLY(procArgs.envp = NULL;)
#endif

   if (NULL == asyncState->procState) {
      err = VIX_E_PROGRAM_NOT_STARTED;
      goto quit;
   }

   if (NULL != pid) {
      *pid = (int64) ProcMgr_GetPid(asyncState->procState);
   }

   /*
    * Start a periodic procedure to check the app periodically
    */
   asyncState->eventQueue = eventQueue;
   timer = g_timeout_source_new(SECONDS_BETWEEN_POLL_TEST_FINISHED * 1000);
   g_source_set_callback(timer, VixToolsMonitorAsyncProc, asyncState, NULL);
   g_source_attach(timer, g_main_loop_get_context(eventQueue));
   g_source_unref(timer);

   /*
    * VixToolsMonitorAsyncProc will clean asyncState up when the program finishes.
    */
   asyncState = NULL;

quit:
   free(fullCommandLine);
#ifdef _WIN32
   if (NULL != envBlock) {
      VixToolsDestroyEnvironmentBlock(envBlock);
   }
#endif

   if (VIX_FAILED(err)) {
      VixToolsFreeRunProgramState(asyncState);
   }

   g_message("%s returning %"FMT64"d\n", __FUNCTION__, err);

   return err;
} // VixToolsRunProgramImpl


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsStartProgramImpl --
 *
 *    Start a named program on the guest.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    Saves off its state.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsStartProgramImpl(const char *requestName,            // IN
                         const char *programPath,            // IN
                         const char *arguments,              // IN
                         const char *workingDir,             // IN
                         int numEnvVars,                     // IN
                         const char **envVars,               // IN
                         Bool startMinimized,                // IN
                         void *userToken,                    // IN
                         void *eventQueue,                   // IN
                         int64 *pid)                         // OUT
{
   VixError err = VIX_OK;
   char *fullCommandLine = NULL;
   VixToolsStartProgramState *asyncState = NULL;
   char *tempCommandLine = NULL;
   char *startProgramFileName;
   char *stopProgramFileName;
   Bool programExists;
   Bool programIsExecutable;
   ProcMgr_ProcArgs procArgs;
   char *workingDirectory = NULL;
#if defined(_WIN32)
   Bool forcedRoot = FALSE;
   STARTUPINFO si;
   wchar_t *envBlock = NULL;
   Bool envBlockFromMalloc = TRUE;
#endif
   GSource *timer;
#if defined(_WIN32) && SUPPORT_VGAUTH
   VGAuthError vgErr;
   VGAuthContext *ctx;
   Bool holdVGAuthUserProfile;
#endif

   /*
    * Initialize this here so we can call free on its member variables
    * when quitting.
    */
   memset(&procArgs, 0, sizeof procArgs);

   if (NULL != pid) {
      *pid = (int64) -1;
   }

   tempCommandLine = Util_SafeStrdup(programPath);
   startProgramFileName = tempCommandLine;

   while (' ' == *startProgramFileName) {
      startProgramFileName++;
   }
   if ('\"' == *startProgramFileName) {
      startProgramFileName++;
      stopProgramFileName = strstr(startProgramFileName, "\"");
   } else {
      stopProgramFileName = NULL;
   }
   if (NULL == stopProgramFileName) {
      stopProgramFileName = startProgramFileName + strlen(startProgramFileName);
   }
   *stopProgramFileName = 0;

   /*
    * Check that the program exists.
    * On linux, we run the program by exec'ing /bin/sh, and that does not
    * return a clear error code indicating that the program does not exist
    * or cannot be executed.
    * This is a common and user-correctable error, however, so we want to
    * check for it and return a specific error code in this case.
    *
    */

   programExists = File_Exists(startProgramFileName);
   if (!programExists) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   programIsExecutable =
      (FileIO_Access(startProgramFileName, FILEIO_ACCESS_EXEC) ==
                                                       FILEIO_SUCCESS);
   if (!programIsExecutable) {
      err = VIX_E_GUEST_USER_PERMISSIONS;
      goto quit;
   }

   /* confidence check workingDir if set */
   if (NULL != workingDir && !File_IsDirectory(workingDir)) {
      err = VIX_E_NOT_A_DIRECTORY;
      goto quit;
   }

   /*
    * Adjust the workingDir if needed.
    * For non-Windows, we use the user's $HOME if workingDir isn't supplied.
    */
   if (NULL == workingDir) {
#if defined(__linux__) || defined(sun) || defined(__FreeBSD__) || defined(__APPLE__)
      char *username = NULL;

      if (!ProcMgr_GetImpersonatedUserInfo(&username, &workingDirectory)) {
         g_warning("%s: ProcMgr_GetImpersonatedUserInfo() failed fetching workingDirectory\n", __FUNCTION__);
         err = VIX_E_FAIL;
         goto quit;
      }

      free(username);
#elif defined(_WIN32)
      workingDirectory = (char *)workingDir;
#else
      /*
       * we shouldn't ever get here for unsupported guests, so just
       * be sure it builds.
       */
      workingDirectory = NULL;
#endif
   } else {
      workingDirectory = Util_SafeStrdup(workingDir);
   }


   /*
    * Build up the command line so the args are passed to the command.
    * To be safe, always put quotes around the program name. If the name
    * contains spaces (either in the file name of its directory path),
    * then the quotes are required. If the name doesn't contain spaces, then
    * unnecessary quotes don't seem to create a problem for both Windows and
    * Linux.
    */
   if (NULL != arguments) {
      fullCommandLine = Str_SafeAsprintf(NULL,
                                     "\"%s\" %s",
                                     programPath,
                                     arguments);
   } else {
      fullCommandLine = Str_SafeAsprintf(NULL,
                                     "\"%s\"",
                                     programPath);
   }

   if (NULL == fullCommandLine) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   /*
    * Save some state for when it completes.
    */
   asyncState = Util_SafeCalloc(1, sizeof *asyncState);
#if defined(_WIN32) && SUPPORT_VGAUTH
   asyncState->hToken = INVALID_HANDLE_VALUE;
   asyncState->hProfile = INVALID_HANDLE_VALUE;
#endif

#if defined(_WIN32)
   if (NULL != envVars) {
      err = VixToolsEnvironToEnvBlock(envVars, &envBlock);
      if (VIX_OK != err) {
         goto quit;
      }
   } else if (PROCESS_CREATOR_USER_TOKEN != userToken) {
      /*
       * If we are impersonating a user and that user did not supply
       * environment variables to pass, then use the user's environment
       * block. That way the user-specific environment variables will
       * be available to the application (such as the user's TEMP
       * directory instead of the system-wide one).
       */
      err = VixToolsGetEnvBlock(userToken, &envBlock);
      if (VIX_OK != err) {
         goto quit;
      }
      envBlockFromMalloc = FALSE;
   }

   if (PROCESS_CREATOR_USER_TOKEN != userToken) {
      forcedRoot = Impersonate_ForceRoot();
   }

   memset(&si, 0, sizeof si);
   procArgs.hToken = (PROCESS_CREATOR_USER_TOKEN == userToken) ? NULL : userToken;
   procArgs.bInheritHandles = TRUE;
   procArgs.lpStartupInfo = &si;
   procArgs.lpCurrentDirectory = UNICODE_GET_UTF16(workingDirectory);
   /*
    * The lpEnvironment is in UTF-16, so we need the CREATE_UNICODE_ENVIRONMENT
    * flag.
    */
   procArgs.dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
   procArgs.lpEnvironment = envBlock;
   si.cb = sizeof si;
   si.dwFlags = STARTF_USESHOWWINDOW;
   si.wShowWindow = (startMinimized) ? SW_MINIMIZE : SW_SHOWNORMAL;
#else
   procArgs.workingDirectory = workingDirectory;
   procArgs.envp = (char **)envVars;
#endif

#if defined(_WIN32) && SUPPORT_VGAUTH
   /*
    * Special case profile handling for StartProgram.  It should stay loaded
    * until the program exits, so copy the profile and user handles for
    * later cleanup, and clobber the profile handle so that it's not unloaded
    * when the impersonation ends.
    *
    * Only do this when we've actually impersonated via vgauth lib; it's not
    * needed when impersonation isn't done (eg vmusr or SYSTEM bypass) or
    * when impersonation is done via lib/impersonate (eg Credential type
    * VIX_USER_CREDENTIAL_TICKETED_SESSION which is based on Windows
    * Security Support Provider Interface (SSPI) and started by guestOp
    * VIX_COMMAND_ACQUIRE_CREDENTIALS)
    */
   holdVGAuthUserProfile = GuestAuthEnabled() &&
                           PROCESS_CREATOR_USER_TOKEN != userToken &&
                           NULL != currentUserHandle;
   if (holdVGAuthUserProfile) {
      HANDLE hToken = INVALID_HANDLE_VALUE;
      HANDLE hProfile = INVALID_HANDLE_VALUE;

      vgErr = TheVGAuthContext(&ctx);
      if (VGAUTH_FAILED(vgErr)) {
         err = VixToolsTranslateVGAuthError(vgErr);
         g_warning("%s: Couldn't get the vgauth context\n", __FUNCTION__);
         goto quit;
      }

      vgErr = VGAuth_UserHandleAccessToken(ctx, currentUserHandle, &hToken);
      if (VGAUTH_FAILED(vgErr)) {
         err = VixToolsTranslateVGAuthError(vgErr);
         g_warning("%s: Failed to get user token\n", __FUNCTION__);
         goto quit;
      }
      vgErr = VGAuth_UserHandleGetUserProfile(ctx, currentUserHandle,
                                              &hProfile);
      if (VGAUTH_FAILED(vgErr)) {
         err = VixToolsTranslateVGAuthError(vgErr);
         g_warning("%s: Failed to get user profile\n", __FUNCTION__);
         CloseHandle(hToken);
         goto quit;
      }

      asyncState->hToken = hToken;
      asyncState->hProfile = hProfile;
   }
#endif

   asyncState->procState = ProcMgr_ExecAsync(fullCommandLine, &procArgs);

#if defined(_WIN32)
   if (forcedRoot) {
      Impersonate_UnforceRoot();
   }
#endif

   if (NULL == asyncState->procState) {
      err = VIX_E_PROGRAM_NOT_STARTED;
      goto quit;
   }

   if (NULL != pid) {
      *pid = (int64) ProcMgr_GetPid(asyncState->procState);
   }

   guest_debug("%s: started '%s', pid %"FMT64"d\n",
               __FUNCTION__, fullCommandLine, *pid);

#if defined(_WIN32) && SUPPORT_VGAUTH
   /*
    * Clobber the profile handle before un-impersonation.
    */
   if (holdVGAuthUserProfile) {
      vgErr = VGAuth_UserHandleSetUserProfile(ctx, currentUserHandle,
                                              INVALID_HANDLE_VALUE);
      if (VGAUTH_FAILED(vgErr)) {
         err = VixToolsTranslateVGAuthError(vgErr);
         g_warning("%s: Failed to clobber user profile\n", __FUNCTION__);
         // VGAuth_EndImpersonation will take care of profile, close hToken
         CloseHandle(asyncState->hToken);
         asyncState->hToken = INVALID_HANDLE_VALUE;
         asyncState->hProfile = INVALID_HANDLE_VALUE;
         ProcMgr_Free(asyncState->procState);
         goto quit;
      }
   }
#endif

   /*
    * Start a periodic procedure to check the app periodically
    */
   asyncState->eventQueue = eventQueue;
   timer = g_timeout_source_new(SECONDS_BETWEEN_POLL_TEST_FINISHED * 1000);
   g_source_set_callback(timer, VixToolsMonitorStartProgram, asyncState, NULL);
   g_source_attach(timer, g_main_loop_get_context(eventQueue));
   g_source_unref(timer);

   /*
    * VixToolsMonitorStartProgram will clean asyncState up when the program
    * finishes.
    */
   asyncState = NULL;


quit:
   free(tempCommandLine);
   free(fullCommandLine);
   free(workingDirectory);
#ifdef _WIN32
   if (envBlockFromMalloc) {
      free(envBlock);
   } else {
      VixToolsDestroyEnvironmentBlock(envBlock);
   }
   UNICODE_RELEASE_UTF16(procArgs.lpCurrentDirectory);
#endif

   if (VIX_FAILED(err)) {
      VixToolsFreeStartProgramState(asyncState);
   }

   return err;
} // VixToolsStartProgramImpl


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsMonitorAsyncProc --
 *
 *    This polls a program running in the guest to see if it has completed.
 *    It is used by the test/dev code to detect when a test application
 *    completes.
 *
 * Return value:
 *    TRUE on non-glib implementation.
 *    FALSE on glib implementation.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
VixToolsMonitorAsyncProc(void *clientData) // IN
{
   VixError err = VIX_OK;
   VixToolsRunProgramState *asyncState;
   Bool procIsRunning = FALSE;
   int exitCode = 0;
   ProcMgr_Pid pid = -1;
   int result = -1;
   GSource *timer;
   char *requestName = NULL;
   VixRunProgramOptions runProgramOptions;

   asyncState = (VixToolsRunProgramState *)clientData;
   ASSERT(asyncState);

   /*
    * Check if the program has completed and VIX commands
    * are not being restricted. Performing cleanup involving
    * IO would deadlock the operations like quiesce snapshot
    * that freeze the filesystem.
    */
   procIsRunning = ProcMgr_IsAsyncProcRunning(asyncState->procState);
   if (!procIsRunning) {
      if (gRestrictCommands) {
         g_debug("%s: Deferring RunScript cleanup due to IO freeze\n",
                 __FUNCTION__);
      } else {
         goto cleanup;
      }
   }

   timer = g_timeout_source_new(SECONDS_BETWEEN_POLL_TEST_FINISHED * 1000);
   g_source_set_callback(timer, VixToolsMonitorAsyncProc, asyncState, NULL);
   g_source_attach(timer, g_main_loop_get_context(asyncState->eventQueue));
   g_source_unref(timer);
   return FALSE;

cleanup:

   /*
    * We need to always check the exit code, even if there is no need to
    * report it. On POSIX systems, ProcMgr_GetExitCode() does things like
    * call waitpid() to clean up the child process.
    */
   result = ProcMgr_GetExitCode(asyncState->procState, &exitCode);
   pid = ProcMgr_GetPid(asyncState->procState);
   if (0 != result) {
      exitCode = -1;
   }

   runProgramOptions = asyncState->runProgramOptions;
   requestName = Util_SafeStrdup(asyncState->requestName);

   VixToolsFreeRunProgramState(asyncState);

   /*
    * We may just be running to clean up after running a script, with the
    * results already reported.
    */
   if ((NULL != reportProgramDoneProc)
       && !(runProgramOptions & VIX_RUNPROGRAM_RETURN_IMMEDIATELY)) {
      (*reportProgramDoneProc)(requestName,
                               err,
                               exitCode,
                               (int64) pid,
                               reportProgramDoneData);
   }

   free(requestName);
   return FALSE;
} // VixToolsMonitorAsyncProc


/*
 *----------------------------------------------------------------------------
 *
 * VixToolsInvalidateStaleProcHandles --
 *
 *    Remove stale proc handles from started programs list.
 *
 * Return value:
 *    TRUE if the timer needs to be re-registerd.
 *    FALSE if the timer needs to be deleted.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

static gboolean
VixToolsInvalidateStaleProcHandles(void *clientData)   // IN:
{
   VixToolsUpdateStartedProgramList(NULL);

   if (startedProcessList != NULL) {
      /*
       * There are still proc handles on the list, so keep the periodic timer
       * registered.
       */
      return TRUE;
   } else {
      g_source_unref(gProcHandleInvalidatorTimer);
      gProcHandleInvalidatorTimer = NULL;
      gProcHandleInvalidatorTimerId = 0;
      g_debug("%s: Process Handle Invalidator is successfully detached\n",
              __FUNCTION__);
      return FALSE;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * VixToolsInvalidateInactiveHGFSSessions --
 *
 *    Send a request to HGFS server to invalidate inactive sessions.
 *    Registers a timer to call the invalidator.
 *
 * Return value:
 *    TRUE if the timer needs to be re-registerd.
 *    FALSE if the timer needs to be deleted.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

static gboolean
VixToolsInvalidateInactiveHGFSSessions(void *clientData)   // IN:
{
   if (HgfsServerManager_InvalidateInactiveSessions(&gVixHgfsBkdrConn) > 0) {
      /*
       * There are still active sessions, so keep the periodic timer
       * registered.
       */
      return TRUE;
   } else {

      g_debug("%s: HGFS session Invalidator is successfully detached\n",
              __FUNCTION__);

      g_source_unref(gHgfsSessionInvalidatorTimer);
      gHgfsSessionInvalidatorTimer = NULL;
      gHgfsSessionInvalidatorTimerId = 0;
      return FALSE;
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * VixToolsRegisterProcHandleInvalidator --
 *
 *    Check bug 2308222 for more details. This function is designed to
 *    cleanup any garbage proc handles in the Started Process List.
 *
 *    If there is a timer already registered, then this function doesn't
 *    do anything.
 *
 * Return value:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
VixToolsRegisterProcHandleInvalidator(void *clientData)    // IN:
{
   ASSERT(clientData);

   if (NULL != gProcHandleInvalidatorTimer) {
      return;
   }

   gProcHandleInvalidatorTimer =
         g_timeout_source_new(SECONDS_BETWEEN_INVALIDATING_PROC_HANDLES * 1000);

   g_source_set_callback(gProcHandleInvalidatorTimer,
                         VixToolsInvalidateStaleProcHandles,
                         NULL,
                         NULL);

   gProcHandleInvalidatorTimerId =
         g_source_attach(gProcHandleInvalidatorTimer,
                         g_main_loop_get_context((GMainLoop *) clientData));

   g_debug("%s: Process Handle Invalidator registered\n", __FUNCTION__);
}


/*
 *----------------------------------------------------------------------------
 *
 * VixToolsRegisterHgfsSessionInvalidator --
 *
 *    Check bug 783263 for more details. This function is designed to
 *    cleanup any hgfs state left by remote clients that got
 *    disconnected abruptly during a file copy process.
 *
 *    If there is a timer already registered, then this function doesn't
 *    do anything.
 *
 * Return value:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void
VixToolsRegisterHgfsSessionInvalidator(void *clientData)    // IN:
{
   ASSERT(clientData);

   if (NULL != gHgfsSessionInvalidatorTimer) {
      return;
   }

   gHgfsSessionInvalidatorTimer =
         g_timeout_source_new(SECONDS_BETWEEN_INVALIDATING_HGFS_SESSIONS * 1000);

   g_source_set_callback(gHgfsSessionInvalidatorTimer,
                         VixToolsInvalidateInactiveHGFSSessions,
                         NULL,
                         NULL);

   gHgfsSessionInvalidatorTimerId =
         g_source_attach(gHgfsSessionInvalidatorTimer,
                         g_main_loop_get_context((GMainLoop *) clientData));

   g_debug("%s: HGFS session Invalidator registered\n", __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsMonitorStartProgram --
 *
 *    This polls a program started by StartProgram to see if it has completed.
 *    If it has, saves off its exitCode and endTime so they can be queried
 *    via ListProcessesEx.
 *
 * Return value:
 *    TRUE on non-glib implementation.
 *    FALSE on glib implementation.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
VixToolsMonitorStartProgram(void *clientData) // IN
{
   VixToolsStartProgramState *asyncState;
   Bool procIsRunning = FALSE;
   int exitCode = 0;
   ProcMgr_Pid pid = -1;
   int result = -1;
   VixToolsStartedProgramState *spState;
   GSource *timer;

   asyncState = (VixToolsStartProgramState *) clientData;
   ASSERT(asyncState);

   /*
    * Check if the program has completed.
    */
   procIsRunning = ProcMgr_IsAsyncProcRunning(asyncState->procState);
   if (!procIsRunning) {
      goto done;
   }

   timer = g_timeout_source_new(SECONDS_BETWEEN_POLL_TEST_FINISHED * 1000);
   g_source_set_callback(timer, VixToolsMonitorStartProgram, asyncState, NULL);
   g_source_attach(timer, g_main_loop_get_context(asyncState->eventQueue));
   g_source_unref(timer);
   return FALSE;

done:

   result = ProcMgr_GetExitCode(asyncState->procState, &exitCode);
   pid = ProcMgr_GetPid(asyncState->procState);
   if (0 != result) {
      exitCode = -1;
   }

   /*
    * Save off the program exit state so ListProcessesEx can find it.
    *
    * We only bother to set pid, exitCode and endTime -- we have the
    * other data from when we made the initial record whne the
    * progrtam started; that record will be updated with the exitCode
    * and endTime.
    */
   spState = Util_SafeMalloc(sizeof(VixToolsStartedProgramState));
   spState->cmdName = NULL;
   spState->fullCommandLine = NULL;
   spState->user = NULL;
   spState->pid = pid;
   spState->startTime = 0;
   spState->exitCode = exitCode;
   spState->endTime = time(NULL);
   spState->isRunning = FALSE;
   spState->next = NULL;
   spState->procState = asyncState->procState;

   // add it to the list of exited programs
   VixToolsUpdateStartedProgramList(spState);

   VixToolsFreeStartProgramState(asyncState);

   return FALSE;
} // VixToolsMonitorStartProgram


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsUpdateStartedProgramList --
 *
 *    Adds a new started program's state to the saved list, and
 *    removes any that have been there too long.
 *
 * Return value:
 *    None
 *
 * Side effects:
 *    Apps that have been saved past their expiration date are dropped.
 *
 *-----------------------------------------------------------------------------
 */

static void
VixToolsUpdateStartedProgramList(VixToolsStartedProgramState *state)        // IN
{
   VixToolsStartedProgramState *spList = NULL;
   VixToolsStartedProgramState *last = NULL;
   VixToolsStartedProgramState *old = NULL;
   time_t now;

   now = time(NULL);

   /*
    * Update the 'running' record if the process has completed.
    */
   if (state && (state->isRunning == FALSE)) {
      spList = startedProcessList;
      while (spList) {
         if (spList->pid == state->pid) {
            /*
             * Update the two exit fields now that we have them
             */
            spList->exitCode = state->exitCode;
            spList->endTime = state->endTime;
            spList->isRunning = FALSE;

            guest_debug("%s: started program '%s' has completed, "
                        "exitCode %d\n", __FUNCTION__,
                        spList->fullCommandLine, spList->exitCode);

            /*
             * Don't let the procState be free'd on Windows to
             * keep OS from reusing the pid. We need to free
             * procState in case of Posix to avoid unnecessary
             * caching of FDs, which might make the service run
             * out of FDs as FDs are limited (usually 1024 by
             * default) for a process.
             */
#ifdef WIN32
            spList->procState = state->procState;
            state->procState = NULL;
#else
            spList->procState = NULL;
#endif

            VixToolsFreeStartedProgramState(state);
            // NULL it out so we don't try to add it later in this function
            state  = NULL;
            break;
         } else {
            spList = spList->next;
         }
      }
   }


   /*
    * Find and toss any old records.
    */
   spList = startedProcessList;
   while (spList) {
      /*
       * Confidence check we don't have a duplicate entry -- this should
       * only happen when the OS re-uses the PID before we reap the record
       * of its exit status.
       */
      if (state) {
         if (state->pid == spList->pid) {
            // XXX just whine for M/N, needs better fix in *main
            g_warning("%s: found duplicate entry in startedProcessList\n",
                      __FUNCTION__);
         }
      }

      if (!spList->isRunning &&
          (spList->endTime < (now - VIX_TOOLS_EXITED_PROGRAM_REAP_TIME))) {
         if (last) {
            last->next = spList->next;
         } else {
            startedProcessList = spList->next;
         }
         old = spList;
         spList = spList->next;
         VixToolsFreeStartedProgramState(old);
      } else {
         last = spList;
         spList = spList->next;
      }
   }


   /*
    * Add any new record to the list
    */
   if (state) {
      if (last) {
         last->next = state;
      } else {
         startedProcessList = state;
      }
   }

} // VixToolsUpdateStartedProgramList


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsFreeStartedProgramState --
 *
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixToolsFreeStartedProgramState(VixToolsStartedProgramState *spState) // IN
{
   if (NULL == spState) {
      return;
   }

   free(spState->cmdName);
   free(spState->fullCommandLine);
   free(spState->user);

   if (NULL != spState->procState) {
      ProcMgr_Free(spState->procState);
   }

   free(spState);
} // VixToolsFreeStartedProgramState


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsFindStartedProgramState --
 *
 *    Searches the list of running/exited apps to see if the given
 *    pid was started via StartProgram.
 *
 * Results:
 *    Any state matching the given pid.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixToolsStartedProgramState *
VixToolsFindStartedProgramState(uint64 pid)
{
   VixToolsStartedProgramState *spList;

   spList = startedProcessList;
   while (spList) {
      if (spList->pid == pid) {
         return spList;
      }
      spList = spList->next;
   }

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * FoundryToolsDaemon_TranslateSystemErr --
 *
 *    Looks at errno/GetLastError() and returns the foundry errcode
 *    that it best maps to.
 *
 * Return value:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
FoundryToolsDaemon_TranslateSystemErr(void)
{
#ifdef _WIN32
   return Vix_TranslateSystemError(GetLastError());
#else
   return Vix_TranslateSystemError(errno);
#endif
}


#if SUPPORT_VGAUTH
/*
 *-----------------------------------------------------------------------------
 *
 * FoundryToolsDaemon_TranslateSystemErr --
 *
 *    Looks at errno/GetLastError() and returns the foundry errcode
 *    that it best maps to.
 *
 * Return value:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsTranslateVGAuthError(VGAuthError vgErr)
{
   VixError err;

   switch (VGAUTH_ERROR_CODE(vgErr)) {
   case VGAUTH_E_OK:
      err = VIX_OK;
      break;
   case VGAUTH_E_INVALID_ARGUMENT:
      err = VIX_E_INVALID_ARG;
      break;
   case VGAUTH_E_INVALID_CERTIFICATE:
      err = VIX_E_INVALID_ARG; // XXX -- needs a Vix equiv
      break;
   case VGAUTH_E_PERMISSION_DENIED:
      err = VIX_E_GUEST_USER_PERMISSIONS;
      break;
   case VGAUTH_E_OUT_OF_MEMORY:
      err = VIX_E_OUT_OF_MEMORY;
      break;
   case VGAUTH_E_COMM:
      err = VIX_E_FAIL;
      break;
   case VGAUTH_E_NOTIMPLEMENTED:
      err = VIX_E_NOT_SUPPORTED;
      break;
   case VGAUTH_E_NOT_CONNECTED:
      err = VIX_E_FAIL;
      break;
   case VGAUTH_E_VERSION_MISMATCH:
      err = VIX_E_FAIL;
      break;
   case VGAUTH_E_SECURITY_VIOLATION:
      err = VIX_E_FAIL;
      break;
   case VGAUTH_E_CERT_ALREADY_EXISTS:
      err = VIX_E_INVALID_ARG;
      break;
   case VGAUTH_E_AUTHENTICATION_DENIED:
      err = VIX_E_INVALID_LOGIN_CREDENTIALS;
      break;
   case VGAUTH_E_INVALID_TICKET:
      err = VIX_E_INVALID_ARG;
      break;
   case VGAUTH_E_MULTIPLE_MAPPINGS:
      err = VIX_E_GUEST_AUTH_MULIPLE_MAPPINGS;
      break;
   case VGAUTH_E_ALREADY_IMPERSONATING:
      err = VIX_E_FAIL;
      break;
   case VGAUTH_E_NO_SUCH_USER:
      err = VIX_E_INVALID_ARG;
      break;
   case VGAUTH_E_SERVICE_NOT_RUNNING:
   case VGAUTH_E_SYSTEM_ERRNO:
   case VGAUTH_E_SYSTEM_WINDOWS:
   case VGAUTH_E_TOO_MANY_CONNECTIONS:
      err = VIX_E_FAIL;
      break;
   case VGAUTH_E_UNSUPPORTED:
      err = VIX_E_NOT_SUPPORTED;
      break;
   default:
      err = VIX_E_FAIL;
      g_warning("%s: error code "VGAUTHERR_FMT64X" has no translation\n",
                __FUNCTION__, vgErr);
      break;
   }
   g_debug("%s: translated VGAuth err "VGAUTHERR_FMT64X" to Vix err %"FMT64"d\n",
           __FUNCTION__, vgErr, err);


   return err;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_GetToolsPropertiesImpl --
 *
 *    Get information about test features.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixTools_GetToolsPropertiesImpl(GKeyFile *confDictRef,            // IN
                                char **resultBuffer,              // OUT
                                size_t *resultBufferLength)       // OUT
{
   VixError err = VIX_OK;
   VixPropertyListImpl propList;
   char *serializedBuffer = NULL;
   size_t serializedBufferLength = 0;
#if !defined(__FreeBSD__)
   char *guestName;
   int osFamily;
   char *packageList = NULL;
   char *powerOffScript = NULL;
   char *powerOnScript = NULL;
   char *resumeScript = NULL;
   char *suspendScript = NULL;
   char *osName = NULL;
   char *osNameFull = NULL;
   Bool foundHostName;
   char *tempDir = NULL;
   int wordSize = 32;


   VixPropertyList_Initialize(&propList);

   /*
    * Collect some values about the host.
    *
    * XXX: 512 is the old hardcoded value for the size of the "guestName"
    * buffer. Since Win32U_GetComputerName returns a new buffer, we do this
    * hack, since the GuestInfo API expects a pre-allocated buffer.
    */
   guestName = Util_SafeMalloc(512);
   foundHostName = System_GetNodeName(512, guestName);
   if (!foundHostName) {
      free(guestName);
#ifdef _WIN32
      /*
       * Give it another try to read NetBIOS name.
       */
      guestName = Win32U_GetComputerName();
#else
      guestName = Util_SafeStrdup("");
#endif
   }

#ifdef _WIN32
   osFamily = GUEST_OS_FAMILY_WINDOWS;
#else
   osFamily = GUEST_OS_FAMILY_LINUX;
#endif

   osNameFull = Hostinfo_GetOSName();
   if (osNameFull == NULL) {
      osNameFull = Util_SafeStrdup("");
   }

   osName = Hostinfo_GetOSGuestString();
   if (osName == NULL) {
      osName = Util_SafeStrdup("");
   }

   wordSize = Hostinfo_GetSystemBitness();
   if (wordSize <= 0) {
      wordSize = 32;
   }

   /*
    * TODO: Something with this.
    */
   packageList = "";

   if (confDictRef != NULL) {
      powerOffScript = VMTools_ConfigGetString(confDictRef, "powerops",
                                               CONFNAME_POWEROFFSCRIPT, NULL);
      powerOnScript = VMTools_ConfigGetString(confDictRef, "powerops",
                                              CONFNAME_POWERONSCRIPT, NULL);
      resumeScript = VMTools_ConfigGetString(confDictRef, "powerops",
                                             CONFNAME_RESUMESCRIPT, NULL);
      suspendScript = VMTools_ConfigGetString(confDictRef, "powerops",
                                              CONFNAME_SUSPENDSCRIPT, NULL);
   }

   tempDir = File_GetSafeRandomTmpDir(TRUE);

   /*
    * Now, record these values in a property list.
    */
   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_GUEST_OS_VERSION,
                                   osNameFull);
   if (VIX_OK != err) {
      goto quit;
   }
   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_GUEST_OS_VERSION_SHORT,
                                   osName);
   if (VIX_OK != err) {
      goto quit;
   }
   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_GUEST_TOOLS_PRODUCT_NAM,
                                   PRODUCT_SHORT_NAME);
   if (VIX_OK != err) {
      goto quit;
   }
   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_GUEST_TOOLS_VERSION,
                                   PRODUCT_VERSION_STRING);
   if (VIX_OK != err) {
      goto quit;
   }
   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_GUEST_NAME,
                                   guestName);
   if (VIX_OK != err) {
      goto quit;
   }
   err = VixPropertyList_SetInteger(&propList,
                                    VIX_PROPERTY_GUEST_TOOLS_API_OPTIONS,
                                    VIX_TOOLSFEATURE_SUPPORT_GET_HANDLE_STATE);
   if (VIX_OK != err) {
      goto quit;
   }
   err = VixPropertyList_SetInteger(&propList,
                                    VIX_PROPERTY_GUEST_OS_FAMILY,
                                    osFamily);
   if (VIX_OK != err) {
      goto quit;
   }
   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_GUEST_OS_PACKAGE_LIST,
                                   packageList);
   if (VIX_OK != err) {
      goto quit;
   }
   if (NULL != powerOffScript) {
      err = VixPropertyList_SetString(&propList,
                                      VIX_PROPERTY_GUEST_POWER_OFF_SCRIPT,
                                      powerOffScript);
      if (VIX_OK != err) {
         goto quit;
      }
   }
   if (NULL != resumeScript) {
      err = VixPropertyList_SetString(&propList,
                                      VIX_PROPERTY_GUEST_RESUME_SCRIPT,
                                      resumeScript);
      if (VIX_OK != err) {
         goto quit;
      }
   }
   if (NULL != powerOnScript) {
      err = VixPropertyList_SetString(&propList,
                                      VIX_PROPERTY_GUEST_POWER_ON_SCRIPT,
                                      powerOnScript);
      if (VIX_OK != err) {
         goto quit;
      }
   }
   if (NULL != suspendScript) {
      err = VixPropertyList_SetString(&propList,
                                      VIX_PROPERTY_GUEST_SUSPEND_SCRIPT,
                                      suspendScript);
      if (VIX_OK != err) {
         goto quit;
      }
   }
   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_VM_GUEST_TEMP_DIR_PROPERTY,
                                   tempDir);
   if (VIX_OK != err) {
      goto quit;
   }
   err = VixPropertyList_SetInteger(&propList,
                                    VIX_PROPERTY_GUEST_TOOLS_WORD_SIZE,
                                    wordSize);
   if (VIX_OK != err) {
      goto quit;
   }

   /* Retrieve the share folders UNC root path. */
   err = VixToolsSetSharedFoldersProperties(&propList);
   if (VIX_OK != err) {
      goto quit;
   }

   /* Set up the API status properties */
   err = VixToolsSetAPIEnabledProperties(&propList, confDictRef);
   if (VIX_OK != err) {
      goto quit;
   }

   /*
    * Serialize the property list to buffer then encode it.
    * This is the string we return to the VMX process.
    */
   err = VixPropertyList_Serialize(&propList,
                                   FALSE,
                                   &serializedBufferLength,
                                   &serializedBuffer);

   if (VIX_OK != err) {
      goto quit;
   }
   *resultBuffer = serializedBuffer;
   *resultBufferLength = (int)serializedBufferLength;
   serializedBuffer = NULL;

quit:
   VixPropertyList_RemoveAllWithoutHandles(&propList);
   free(guestName);
   free(serializedBuffer);
   free(tempDir);
   free(osName);
   free(osNameFull);
   free(suspendScript);
   free(resumeScript);
   free(powerOnScript);
   free(powerOffScript);
#else
   /*
    * FreeBSD. We do not require all the properties above.
    * We only Support VMODL Guest Ops for now (Bug 228398).
    */

   VixPropertyList_Initialize(&propList);

   /* InitiateFileTransfer(From|To)Guest operations require this */
   err = VixPropertyList_SetInteger(&propList,
                                    VIX_PROPERTY_GUEST_OS_FAMILY,
                                    GUEST_OS_FAMILY_LINUX);
   if (VIX_OK != err) {
      goto quit;
   }
   /* Retrieve the share folders UNC root path. */
   err = VixToolsSetSharedFoldersProperties(&propList);
   if (VIX_OK != err) {
      goto quit;
   }
   /*
    * Set up the API status properties.
    * This is done so that the client side can tell the
    * difference between OutOfDate tools and NotSupported.
    */
   err = VixToolsSetAPIEnabledProperties(&propList, confDictRef);
   if (VIX_OK != err) {
      goto quit;
   }
   /*
    * Serialize the property list to buffer then encode it.
    * This is the string we return to the VMX process.
    */
   err = VixPropertyList_Serialize(&propList,
                                   FALSE,
                                   &serializedBufferLength,
                                   &serializedBuffer);
   if (VIX_OK != err) {
      goto quit;
   }
   *resultBuffer = serializedBuffer;
   *resultBufferLength = (int)serializedBufferLength;
   serializedBuffer = NULL;

quit:
   VixPropertyList_RemoveAllWithoutHandles(&propList);
   free(serializedBuffer);
#endif // __FreeBSD__

   return err;
} // VixTools_GetToolsPropertiesImpl


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsSetSharedFoldersProperties --
 *
 *    Set information about the shared folders feature.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsSetSharedFoldersProperties(VixPropertyListImpl *propList)    // IN
{
   VixError err = VIX_OK;

   /* Retrieve the share folders UNC root path. */
   char *hgfsRootPath = NULL;

   if (!HgfsHlpr_QuerySharesDefaultRootPath(&hgfsRootPath)) {
      /* Exit ok as we have nothing to set from shared folders. */
      goto exit;
   }

   ASSERT(hgfsRootPath != NULL);

   err = VixPropertyList_SetString(propList,
                                   VIX_PROPERTY_GUEST_SHAREDFOLDERS_SHARES_PATH,
                                   hgfsRootPath);
   if (VIX_OK != err) {
      goto exit;
   }

exit:
   if (hgfsRootPath != NULL) {
      HgfsHlpr_FreeSharesRootPath(hgfsRootPath);
   }
   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetAPIDisabledFromConf --
 *
 *    Helper function for fetching the API config setting.
 *
 *    If the varName is NULL, only the global switch is checked.
 *
 * Return value:
 *    Bool
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VixToolsGetAPIDisabledFromConf(GKeyFile *confDictRef,            // IN
                               const char *varName)              // IN
{
   gboolean disabled = FALSE;
   char disabledName[128];

   /*
    * g_key_get_file_boolean() will also return FALSE if there's no
    * entry in the config file.
    */


   /*
    * First check the global control-switch, which will override the
    * per-API configs if its set.
    */
   if (confDictRef != NULL) {
      disabled = VMTools_ConfigGetBoolean(confDictRef,
                                          VIX_TOOLS_CONFIG_API_GROUPNAME,
                                          VIX_TOOLS_CONFIG_API_ALL_NAME,
                                          FALSE);
      if (disabled) {
         return TRUE;
      }
   }

   /*
    * Check the individual API if the global control-switch isn't on.
    */
   if (NULL != varName) {
      Str_Snprintf(disabledName, sizeof(disabledName), "%s.disabled", varName);
      if (confDictRef != NULL) {
         disabled = VMTools_ConfigGetBoolean(confDictRef,
                                             VIX_TOOLS_CONFIG_API_GROUPNAME,
                                             disabledName,
                                             FALSE);
      }
   }

#if !SUPPORT_VGAUTH
   /*
    * Make sure vgauth related stuff does not show as enabled.
    */
   if (NULL != varName) {
      if ((strcmp(varName, VIX_TOOLS_CONFIG_API_ADD_GUEST_ALIAS_NAME) == 0) ||
          (strcmp(varName, VIX_TOOLS_CONFIG_API_REMOVE_GUEST_ALIAS_NAME) == 0) ||
          (strcmp(varName, VIX_TOOLS_CONFIG_API_REMOVE_GUEST_ALIAS_BY_CERT_NAME) == 0) ||
          (strcmp(varName, VIX_TOOLS_CONFIG_API_LIST_GUEST_ALIASES_NAME) == 0) ||
          (strcmp(varName, VIX_TOOLS_CONFIG_API_LIST_GUEST_MAPPED_ALIASES_NAME) == 0)) {
         disabled = TRUE;
      }
   }
#endif

   return disabled;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsComputeEnabledProperty --
 *
 *    Wrapper function for setting ENABLED properties for VMODL APIs.
 *    For supported guest OSes, it uses VixToolsGetAPIDisabledFromConf() to
 *    check.  Otherwise its FALSE.
 *
 *
 * Return value:
 *    Bool
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VixToolsComputeEnabledProperty(GKeyFile *confDictRef,            // IN
                               const char *varName)              // IN
{
   return VixToolsGetAPIDisabledFromConf(confDictRef, varName);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsSetAPIEnabledProperties --
 *
 *    Set information about the state of APIs.
 *
 *    This is done for all guests, even those that can't do VMODL
 *    guest APIs, so that the client side knows if the tools are
 *    up-to-date.  If the client side doesn't see an ENABLED property
 *    for an API it knows about, it assumes the tools are out-of-date,
 *    and returns the appropriate error.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsSetAPIEnabledProperties(VixPropertyListImpl *propList,    // IN
                                GKeyFile *confDictRef)            // IN
{
   VixError err = VIX_OK;

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_START_PROGRAM_ENABLED,
                              VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_START_PROGRAM_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_LIST_PROCESSES_ENABLED,
                              VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_LIST_PROCESSES_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_TERMINATE_PROCESS_ENABLED,
                              VixToolsComputeEnabledProperty(confDictRef,
                                 VIX_TOOLS_CONFIG_API_TERMINATE_PROCESS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_READ_ENVIRONMENT_VARIABLE_ENABLED,
                              VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_READ_ENV_VARS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_VALIDATE_CREDENTIALS_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                   VIX_TOOLS_CONFIG_API_VALIDATE_CREDENTIALS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_ACQUIRE_CREDENTIALS_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_ACQUIRE_CREDENTIALS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_RELEASE_CREDENTIALS_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_RELEASE_CREDENTIALS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_MAKE_DIRECTORY_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                                  VIX_TOOLS_CONFIG_API_MAKE_DIRECTORY_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_DELETE_FILE_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                       VIX_TOOLS_CONFIG_API_DELETE_FILE_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_DELETE_DIRECTORY_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_DELETE_DIRECTORY_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_MOVE_DIRECTORY_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                                   VIX_TOOLS_CONFIG_API_MOVE_DIRECTORY_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_MOVE_FILE_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                       VIX_TOOLS_CONFIG_API_MOVE_FILE_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_CREATE_TEMP_FILE_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_CREATE_TMP_FILE_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_CREATE_TEMP_DIRECTORY_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_CREATE_TMP_DIRECTORY_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_LIST_FILES_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                       VIX_TOOLS_CONFIG_API_LIST_FILES_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_CHANGE_FILE_ATTRIBUTES_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_CHANGE_FILE_ATTRS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_INITIATE_FILE_TRANSFER_FROM_GUEST_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_INITIATE_FILE_TRANSFER_FROM_GUEST_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_INITIATE_FILE_TRANSFER_TO_GUEST_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_INITIATE_FILE_TRANSFER_TO_GUEST_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_ADD_AUTH_ALIAS_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_ADD_GUEST_ALIAS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }
   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_REMOVE_AUTH_ALIAS_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_REMOVE_GUEST_ALIAS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }
   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_LIST_AUTH_ALIASES_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_LIST_GUEST_ALIASES_NAME));
   if (VIX_OK != err) {
      goto exit;
   }
   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_LIST_MAPPED_ALIASES_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_LIST_GUEST_MAPPED_ALIASES_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_CREATE_REGISTRY_KEY_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_CREATE_REGISTRY_KEY_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_LIST_REGISTRY_KEYS_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_LIST_REGISTRY_KEYS_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_DELETE_REGISTRY_KEY_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_DELETE_REGISTRY_KEY_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_SET_REGISTRY_VALUE_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_SET_REGISTRY_VALUE_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_LIST_REGISTRY_VALUES_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_LIST_REGISTRY_VALUES_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_DELETE_REGISTRY_VALUE_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_DELETE_REGISTRY_VALUE_NAME));
   if (VIX_OK != err) {
      goto exit;
   }

   err = VixPropertyList_SetBool(propList,
                                 VIX_PROPERTY_GUEST_REMOVE_AUTH_ALIAS_BY_CERT_ENABLED,
                                 VixToolsComputeEnabledProperty(confDictRef,
                                    VIX_TOOLS_CONFIG_API_REMOVE_GUEST_ALIAS_BY_CERT_NAME));
   if (VIX_OK != err) {
      goto exit;
   }
exit:
   g_debug("%s: returning err %"FMT64"d\n", __FUNCTION__, err);
   return err;
} // VixToolsSetAPIEnabledProperties


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsReadRegistry --
 *
 *    Read an int from the registry on the guest.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsReadRegistry(VixCommandRequestHeader *requestMsg,  // IN
                     char **result)                        // OUT
{
#ifdef _WIN32
   VixError err = VIX_OK;
   char *registryPathName = NULL;
   int valueInt = 0;
   int errResult;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   char *valueStr = NULL;
   VixMsgRegistryRequest *registryRequest;
   VMAutomationRequestParser parser;

   /*
    * Parse the argument
    */
   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *registryRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   registryRequest = (VixMsgRegistryRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            registryRequest->registryKeyLength,
                                            &(const char*)registryPathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *registryPathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   if (VIX_PROPERTYTYPE_INTEGER == registryRequest->expectedRegistryKeyType) {
      errResult = Registry_ReadInteger(registryPathName, &valueInt);
      if (ERROR_SUCCESS != errResult) {
         /*
          * E_UNEXPECTED isn't a system err. Don't use Vix_TranslateSystemError
          */
         if (E_UNEXPECTED == errResult) {
            err = VIX_E_REG_INCORRECT_VALUE_TYPE;
         } else {
            err = Vix_TranslateSystemError(errResult);
         }
         goto quit;
      }

      valueStr = Str_SafeAsprintf(NULL, "%d", valueInt);
      if (NULL == valueStr) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
   } else if (VIX_PROPERTYTYPE_STRING == registryRequest->expectedRegistryKeyType) {
      errResult = Registry_ReadString(registryPathName, &valueStr);
      if (ERROR_SUCCESS != errResult) {
         /*
          * E_UNEXPECTED isn't a system err. Don't use Vix_TranslateSystemError
          */
         if (E_UNEXPECTED == errResult) {
            err = VIX_E_REG_INCORRECT_VALUE_TYPE;
         } else {
            err = Vix_TranslateSystemError(errResult);
         }
         goto quit;
      }
   } else {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   if (NULL == valueStr) {
      valueStr = Util_SafeStrdup("");
   }
   *result = valueStr;

   guest_debug("%s: returning '%s'\n", __FUNCTION__, valueStr);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;

#else
   return VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
} // VixToolsReadRegistry


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsWriteRegistry --
 *
 *    Write an integer to the registry on the guest.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsWriteRegistry(VixCommandRequestHeader *requestMsg) // IN
{
#ifdef _WIN32
   VixError err = VIX_OK;
   char *registryPathName = NULL;
   char *registryData = NULL;
   int errResult;
   int intValue;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgRegistryRequest *registryRequest;
   VMAutomationRequestParser parser;

   /*
    * Parse the argument
    */
   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *registryRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   registryRequest = (VixMsgRegistryRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            registryRequest->registryKeyLength,
                                            &(const char*)registryPathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *registryPathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   if (VIX_PROPERTYTYPE_INTEGER == registryRequest->expectedRegistryKeyType) {
      err = VMAutomationRequestParserGetData(&parser,
                                             registryRequest->dataToWriteSize,
                                             &(const char*)registryData);
      if (VIX_OK != err) {
         goto quit;
      }

      intValue = *((int *) registryData);

      errResult = Registry_WriteInteger(registryPathName, intValue);
      if (ERROR_SUCCESS != errResult) {
         err = Vix_TranslateSystemError(errResult);
         goto quit;
      }
   } else if (VIX_PROPERTYTYPE_STRING == registryRequest->expectedRegistryKeyType) {
      err = VMAutomationRequestParserGetOptionalString(&parser,
                                            registryRequest->dataToWriteSize,
                                               &(const char*)registryData);
      if (VIX_OK != err) {
         goto quit;
      }

      errResult = Registry_WriteString(registryPathName, registryData);
      if (ERROR_SUCCESS != errResult) {
         err = Vix_TranslateSystemError(errResult);
         goto quit;
      }
   } else {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;

#else
   return VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
} // VixToolsWriteRegistry


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsDeleteObject --
 *
 *    Delete a file on the guest.
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsDeleteObject(VixCommandRequestHeader *requestMsg)  // IN
{
   VixError err = VIX_OK;
   const char *pathName = NULL;
   int resultInt;
   Bool resultBool;
   Bool success;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgSimpleFileRequest *fileRequest;
   VMAutomationRequestParser parser;

   /*
    * Parse the argument
    */
   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *fileRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   fileRequest = (VixMsgSimpleFileRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            fileRequest->guestPathNameLength,
                                            &pathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *pathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s path: '%s'\n",
           __FUNCTION__, IMPERSONATED_USERNAME, pathName);

   ///////////////////////////////////////////
   if ((VIX_COMMAND_DELETE_GUEST_FILE == requestMsg->opCode) ||
       (VIX_COMMAND_DELETE_GUEST_FILE_EX == requestMsg->opCode)) {
      /*
       * if pathName is an invalid symbolic link, we still want to delete it.
       */
      if (FALSE == File_IsSymLink(pathName)) {
         if (!(File_Exists(pathName))) {
            err = FoundryToolsDaemon_TranslateSystemErr();
            goto quit;
         }

         if (!(File_IsFile(pathName))) {
            err = VIX_E_NOT_A_FILE;
            goto quit;
         }
      }

      resultInt = File_UnlinkNoFollow(pathName);
      if (0 != resultInt) {
         err = FoundryToolsDaemon_TranslateSystemErr();
      }
   ///////////////////////////////////////////
   } else if (VIX_COMMAND_DELETE_GUEST_REGISTRY_KEY == requestMsg->opCode) {
#ifdef _WIN32
      err = VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#else
      err = VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
   ///////////////////////////////////////////
   } else if (VIX_COMMAND_DELETE_GUEST_DIRECTORY == requestMsg->opCode) {
      resultBool = File_Exists(pathName);
      if (!resultBool) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
      resultBool = File_IsDirectory(pathName);
      if (!resultBool) {
         err = VIX_E_NOT_A_DIRECTORY;
         goto quit;
      }
      success = File_DeleteDirectoryTree(pathName);
      if (!success) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
   ///////////////////////////////////////////
   } else if (VIX_COMMAND_DELETE_GUEST_EMPTY_DIRECTORY == requestMsg->opCode) {
      resultBool = File_Exists(pathName);
      if (!resultBool) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
      resultBool = File_IsDirectory(pathName);
      if (!resultBool) {
         err = VIX_E_NOT_A_DIRECTORY;
         goto quit;
      }
      success = File_DeleteEmptyDirectory(pathName);
      if (!success) {
#if !defined(_WIN32)
         /*
          * If the specified directory is not empty then
          * File_DeleteEmptyDirectory() fails and
          * 1. errno is set to either EEXIST or ENOTEMPTY on linux platforms.
          * 2. errno is set EEXIST on Solaris platforms.
          *
          * To maintain consistency across different Posix platforms, lets
          * re-write the error before returning.
          */
         if (EEXIST == errno) {
            errno = ENOTEMPTY;
         }
#endif
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
   ///////////////////////////////////////////
   } else {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsDeleteObject


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsDeleteDirectory --
 *
 *    Delete a directory on the guest.
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsDeleteDirectory(VixCommandRequestHeader *requestMsg)  // IN
{
   VixError err = VIX_OK;
   const char *directoryPath = NULL;
   Bool success;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   Bool recursive = TRUE;
   VixMsgDeleteDirectoryRequest *deleteDirectoryRequest;
   VMAutomationRequestParser parser;

   ASSERT(NULL != requestMsg);

   /*
    * Parse the argument
    */
   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg,
                                       sizeof *deleteDirectoryRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   deleteDirectoryRequest = (VixMsgDeleteDirectoryRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            deleteDirectoryRequest->guestPathNameLength,
                                            &directoryPath);
   if (VIX_OK != err) {
      goto quit;
   }

   if ('\0' == *directoryPath) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   recursive = deleteDirectoryRequest->recursive;

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s path: '%s' recursive: %d\n",
           __FUNCTION__, IMPERSONATED_USERNAME, directoryPath, (int) recursive);

   success = File_Exists(directoryPath);
   if (!success) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   if (File_IsSymLink(directoryPath) || File_IsFile(directoryPath)) {
      err = VIX_E_NOT_A_DIRECTORY;
      goto quit;
   }

   if (recursive) {
      success = File_DeleteDirectoryTree(directoryPath);
   } else {
      success = File_DeleteEmptyDirectory(directoryPath);
   }

   if (!success) {
      if (!recursive) {
#if !defined(_WIN32)
         /*
          * If the specified directory is not empty then
          * File_DeleteEmptyDirectory() fails and
          * 1. errno is set to either EEXIST or ENOTEMPTY on linux platforms.
          * 2. errno is set EEXIST on Solaris platforms.
          *
          * To maintain consistency across different Posix platforms, lets
          * re-write the error before returning.
          */
         if (EEXIST == errno) {
            errno = ENOTEMPTY;
         }
#endif
      }
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsDeleteDirectory


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsObjectExists --
 *
 *    Find a file on the guest.
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsObjectExists(VixCommandRequestHeader *requestMsg,  // IN
                     char **result)                        // OUT
{
   VixError err = VIX_OK;
   char *pathName = NULL;
   int resultInt = 0;
   Bool resultBool;
   static char resultBuffer[32];
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgSimpleFileRequest *fileRequest;
   VMAutomationRequestParser parser;

   /*
    * Parse the argument
    */
   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *fileRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   fileRequest = (VixMsgSimpleFileRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            fileRequest->guestPathNameLength,
                                            (const char **)&pathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *pathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s path: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME, pathName);

   /*
    * Do the action appropriate for this type of object.
    */
   ///////////////////////////////////////////
   if (VIX_COMMAND_GUEST_FILE_EXISTS == requestMsg->opCode) {
      resultBool = File_IsFile(pathName);
      if (resultBool) {
         resultInt = 1;
      } else {
         resultInt = 0;
      }
   ///////////////////////////////////////////
   } else if (VIX_COMMAND_REGISTRY_KEY_EXISTS == requestMsg->opCode) {
#ifdef _WIN32
      resultInt = Registry_KeyExists(pathName);
#else
      resultInt = 0;
      err = VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
   ///////////////////////////////////////////
   } else if (VIX_COMMAND_DIRECTORY_EXISTS == requestMsg->opCode) {
      resultBool = File_IsDirectory(pathName);
      if (resultBool) {
         resultInt = 1;
      } else {
         resultInt = 0;
      }
   ///////////////////////////////////////////
   } else {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   Str_Sprintf(resultBuffer, sizeof(resultBuffer), "%d", resultInt);
   *result = resultBuffer;

   guest_debug("%s: returning '%s'\n", __FUNCTION__, resultBuffer);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsObjectExists


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsCreateTempFile --
 *
 *    Wrapper to call VixToolsCreateTempFileInt.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsCreateTempFile(VixCommandRequestHeader *requestMsg,   // IN
                       char **result)                         // OUT: UTF-8
{
   VixError err = VixToolsCreateTempFileInt(requestMsg, FALSE, result);
#ifdef _WIN32
   /*
    * PR 2155708: CreateTemporaryFileInGuest succeeds and returns a file
    * path that does not exist when using user name format "domain\user"
    * if the user does not have a profile folder created before, such as
    * by interactively logging onto Windows. What happens here is that
    * Win32 API UnloadUserProfile() deletes the temp user profile folder
    * "C:\Users\TEMP" in the end.
    * Verify existence of the returned path, retry the guest OP using
    * system temp folder if the path disappears.
    */
   if (VIX_SUCCEEDED(err) && *result != NULL && !File_Exists(*result)) {
      host_warning("%s: retry using system temp.\n",
                   __FUNCTION__);
      guest_warning("%s: '%s' does not exist, retry using system temp.\n",
                    __FUNCTION__, *result);
      free(*result);
      *result = NULL;
      err = VixToolsCreateTempFileInt(requestMsg, TRUE, result);
   }
#endif

   return err;
} // VixToolsCreateTempFile


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsCreateTempFileInt --
 *
 *    Create a temporary file on the guest.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsCreateTempFileInt(VixCommandRequestHeader *requestMsg,   // IN
                          Bool useSystemTemp,                    // IN
                          char **result)                         // OUT: UTF-8
{
   VixError err = VIX_OK;
   char *filePathName = NULL;
   int fd = -1;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;

   if ((VIX_COMMAND_CREATE_TEMPORARY_FILE != requestMsg->opCode) &&
       (VIX_COMMAND_CREATE_TEMPORARY_FILE_EX != requestMsg->opCode) &&
       (VIX_COMMAND_CREATE_TEMPORARY_DIRECTORY != requestMsg->opCode)) {
      ASSERT(0);
      err = VIX_E_FAIL;
      g_warning("%s: Received a request with an invalid opcode: %d\n",
                __FUNCTION__, requestMsg->opCode);
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);

   err = VixToolsGetTempFile(requestMsg, userToken, useSystemTemp,
                             &filePathName, &fd);
   if (VIX_FAILED(err)) {
      goto quit;
   }

   /*
    * Just close() the file, since we're not going to use it. But, when we
    * create a temporary directory, VixToolsGetTempFile() sets 'fd' to 0 on
    * success. On windows, close() shouldn't be called for invalid fd values.
    * So, call close() only if 'fd' is valid.
    */
   if (fd > 0) {
      if (close(fd) < 0) {
         g_warning("%s: Unable to close a file, errno is %d.\n",
                   __FUNCTION__, errno);
      }
   }

   *result = filePathName;

   guest_debug("%s: returning '%s'\n", __FUNCTION__, filePathName);

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsCreateTempFileInt


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsReadVariable --
 *
 *    Read an environment variable in the guest. The name of the environment
 *    variable is expected to be in UTF-8.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsReadVariable(VixCommandRequestHeader *requestMsg,   // IN
                     char **result)                         // OUT: UTF-8
{
   VixError err = VIX_OK;
   char *value = "";
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgReadVariableRequest *readRequest;
   const char *valueName = NULL;
   VMAutomationRequestParser parser;

   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *readRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   readRequest = (VixMsgReadVariableRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            readRequest->nameLength,
                                            &valueName);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s var: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME, valueName);

   switch (readRequest->variableType) {
   case VIX_GUEST_ENVIRONMENT_VARIABLE:
      /*
       * Alwasy get environment variable for the current user, even if the
       * current user is root/administrator
       */
#ifndef _WIN32
      /*
       * If we are maintaining our own set of environment variables
       * because the application we're running from changed the user's
       * environment, then we should be reading from that.
       */
      if (NULL != userEnvironmentTable) {
         if (HashTable_Lookup(userEnvironmentTable, valueName,
                              (void **) &value)) {
            value = Util_SafeStrdup(value);
         } else {
            value = Util_SafeStrdup("");
         }
         break;
      }
#endif

      err = VixToolsGetEnvForUser(userToken, valueName, &value);
      if (VIX_OK != err) {
         goto quit;
      }
      break;

   case VIX_GUEST_CONFIG:
   case VIX_VM_CONFIG_RUNTIME_ONLY:
   case VIX_VM_GUEST_VARIABLE:
   default:
      err = VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
      break;
   } // switch (readRequest->variableType)

   *result = value;

   guest_debug("%s: returning '%s'\n", __FUNCTION__, value);

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsReadVariable


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetEnvVarForUser --
 *
 *      Reads a single environment variable from the given user's
 *      environment.
 *
 * Results:
 *      VixError
 *      'value' points to a heap-allocated string containing the value.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsGetEnvForUser(void *userToken,       // IN
                      const char *name,      // IN
                      char **value)          // OUT
{
   VixError err;
   VixToolsUserEnvironment *env;

   ASSERT(NULL != value);

   err = VixToolsNewUserEnvironment(userToken, &env);
   if (VIX_FAILED(err)) {
      return err;
   }

   *value = VixToolsGetEnvFromUserEnvironment(env, name);
   VixToolsDestroyUserEnvironment(env);
   if (NULL == *value) {
      *value = Util_SafeStrdup("");
   }

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsReadEnvVariables --
 *
 *    Read environment variables in the guest. The name of the environment
 *    variables are expected to be in UTF-8.
 *
 *    If a variable doesn't exist, nothing is returned for it.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsReadEnvVariables(VixCommandRequestHeader *requestMsg,   // IN
                         char **result)                         // OUT: UTF-8
{
   VixError err = VIX_OK;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgReadEnvironmentVariablesRequest *readRequest;
   char *results = NULL;
   VMAutomationRequestParser parser;
   const char *names = NULL;

   err = VMAutomationRequestParserInit(&parser,
                                      requestMsg, sizeof *readRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   readRequest = (VixMsgReadEnvironmentVariablesRequest *) requestMsg;
   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s reading %d vars\n",
           __FUNCTION__, IMPERSONATED_USERNAME, readRequest->numNames);

   err = VMAutomationRequestParserGetOptionalStrings(&parser,
                                                     readRequest->numNames,
                                                     readRequest->namesLength,
                                                     &names);
   if (VIX_OK != err) {
      goto quit;
   }

   if (readRequest->numNames > 0) {
      err = VixToolsGetMultipleEnvVarsForUser(userToken, names,
                                              readRequest->numNames,
                                              &results);
      if (VIX_FAILED(err)) {
         goto quit;
      }
   } else {
      /*
       * If none are specified, return all of them.
       */
      err = VixToolsGetAllEnvVarsForUser(userToken, &results);
      if (VIX_FAILED(err)) {
         goto quit;
      }
   }

   *result = results;

   guest_debug("%s: returning '%s'\n", __FUNCTION__, results);

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsReadEnvVariables


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetMultipleEnvVarsForUser --
 *
 *      Populates result with an XML-like string containing all the
 *      environment variables listed starting at 'names' (each name is
 *      separated by a null character).
 *      The result string will contain zero or more entries of the form
 *      <ev>NAME=VALUE</ev> without any delimiting characters.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsGetMultipleEnvVarsForUser(void *userToken,       // IN
                                  const char *names,     // IN
                                  unsigned int numNames, // IN
                                  char **result)         // OUT
{
   VixError err;
   unsigned int i;
   char *resultLocal = Util_SafeStrdup("");  // makes the loop cleaner.
   VixToolsUserEnvironment *env;

#ifdef __FreeBSD__
   if (NULL == userEnvironmentTable) {
      err = VIX_E_FAIL;
      free(resultLocal);
      return err;
   }
#endif

   err = VixToolsNewUserEnvironment(userToken, &env);
   if (VIX_FAILED(err)) {
      env = NULL;
      goto quit;
   }

   for (i = 0; i < numNames; i++) {
      char *value;

#ifdef __FreeBSD__
      /*
       * We should check the original envp for all vars except
       * a few allowlisted ones that we set/unset on impersonate
       * user start/stop. for them we need to do getenv()
       */
      if (!strcmp(names, "USER") ||
          !strcmp(names, "HOME") ||
          !strcmp(names, "SHELL")) {
         value = VixToolsGetEnvFromUserEnvironment(env, names);
      }
      else {
         if (HashTable_Lookup(userEnvironmentTable,
                              names, (void **) &value)) {
            value = Util_SafeStrdup(value);
         } else {
            value = Util_SafeStrdup("");
         }
      }
#else
      value = VixToolsGetEnvFromUserEnvironment(env, names);
#endif

      if (NULL != value) {
         char *tmp = resultLocal;
         char *tmpVal;
         char *escapedName;

         escapedName = VixToolsEscapeXMLString(names);
         if (NULL == escapedName) {
            err = VIX_E_OUT_OF_MEMORY;
            goto loopCleanup;
         }

         tmpVal = VixToolsEscapeXMLString(value);
         if (NULL == tmpVal) {
            err = VIX_E_OUT_OF_MEMORY;
            goto loopCleanup;
         }
         free(value);
         value = tmpVal;

         resultLocal = Str_SafeAsprintf(NULL, "%s<ev>%s=%s</ev>",
                                    tmp, escapedName, value);
         free(tmp);
         if (NULL == resultLocal) {
            err = VIX_E_OUT_OF_MEMORY;
         }

      loopCleanup:
         free(value);
         free(escapedName);
         if (VIX_OK != err) {
            goto quit;
         }
      }

      names += strlen(names) + 1;
   }

   *result = resultLocal;
   resultLocal = NULL;
   err = VIX_OK;

quit:
   free(resultLocal);
   VixToolsDestroyUserEnvironment(env);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetAllEnvVarsForUser --
 *
 *      Populates result with an XML-like string containing all the
 *      environment variables set for the user represented by 'userToken'.
 *      The result string will contain zero or more entries of the form
 *      <ev>NAME=VALUE</ev> without any delimiting characters.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsGetAllEnvVarsForUser(void *userToken,     // IN
                             char **result)       // OUT
{
   VixError err;
   char *resultLocal;
   VixToolsEnvIterator *itr;
   char *envVar;
#ifdef __FreeBSD__
   char **envp;
   if (NULL == userEnvironmentTable) {
      err = VIX_E_FAIL;
      return err;
   }
   envp = VixToolsEnvironmentTableToEnvp(userEnvironmentTable);
#endif

   if (NULL == result) {
      err = VIX_E_FAIL;
      return err;
   }

   resultLocal = Util_SafeStrdup("");  // makes the loop cleaner.

#ifdef __FreeBSD__
   err = VixToolsNewEnvIterator(userToken, envp, &itr);
#else
   err = VixToolsNewEnvIterator(userToken, &itr);
#endif
   if (VIX_FAILED(err)) {
      goto quit;
   }

   while ((envVar = VixToolsGetNextEnvVar(itr)) != NULL) {
      char *tmp = resultLocal;
      char *tmpVal;
#ifdef __FreeBSD__
      /*
       * For variables we change during Impersonatation of user,
       * we need to fetch from getenv() system call, all else
       * can be read from the hash table of the original envp.
       */
      if (StrUtil_StartsWith(envVar, "USER=") ||
          StrUtil_StartsWith(envVar, "HOME=") ||
          StrUtil_StartsWith(envVar, "SHELL=")) {
         char *name = NULL;
         char *escapedName = NULL;
         char *whereToSplit;
         size_t nameLen;

         whereToSplit = strchr(envVar, '=');
         if (NULL == whereToSplit) {
            /* Our code generated this list, so this shouldn't happen. */
            ASSERT(0);
            continue;
         }

         nameLen = whereToSplit - envVar;
         name = Util_SafeMalloc(nameLen + 1);
         memcpy(name, envVar, nameLen);
         name[nameLen] = '\0';

         escapedName = VixToolsEscapeXMLString(name);

         free(envVar);
         envVar = Str_SafeAsprintf(NULL, "%s=%s",
                                   escapedName, Posix_Getenv(name));

         free(name);
         free(escapedName);
      }
#endif
      tmpVal = VixToolsEscapeXMLString(envVar);
      free(envVar);
      if (NULL == tmpVal) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      envVar = tmpVal;

      resultLocal = Str_SafeAsprintf(NULL, "%s<ev>%s</ev>", tmp, envVar);
      free(tmp);
      free(envVar);
      if (NULL == resultLocal) {
         g_warning("%s: Out of memory.\n", __FUNCTION__);
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
   }

quit:
   VixToolsDestroyEnvIterator(itr);
#ifdef __FreeBSD__
   VixToolsFreeEnvp(envp);
#endif
   *result = resultLocal;

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsWriteVariable --
 *
 *    Write an environment variable in the guest. The name of the environment
 *    variable and its value are expected to be in UTF-8.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    Yes, may change the environment variables.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsWriteVariable(VixCommandRequestHeader *requestMsg)   // IN
{
   VixError err = VIX_OK;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgWriteVariableRequest *writeRequest;
   char *valueName = NULL;
   char *value = NULL;
   int result;

   writeRequest = (VixMsgWriteVariableRequest *) requestMsg;
   err = VixMsg_ParseWriteVariableRequest(writeRequest, &valueName, &value);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s name: %s value %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME, valueName, value);

   switch (writeRequest->variableType) {
   case VIX_GUEST_ENVIRONMENT_VARIABLE:
#if !defined(_WIN32)
      /*
       * On Linux, we only allow root to set environment variables.
       * On Windows we can put ACLs on the registry keys, but we can't do that
       * on Linux. The threat is if an unpriveleged user changes path or lib
       * settings, which could cause a later call from a priveleged user
       * to RunProgramInGuest to misbehave by using compromised libs or environment.
       */
      if (1 != Util_HasAdminPriv()) {
         err = VIX_E_GUEST_USER_PERMISSIONS;
         goto quit;
      }
#endif
      /*
       * At this point, we want to set environmental variable for current
       * user, even if the current user is root/administrator
       */
      result = System_SetEnv(FALSE, valueName, value);
      if (0 != result) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }

#ifndef _WIN32
      /*
       * We need to make sure that this change is reflected in the table of
       * environment variables we use when launching programs. This is so if a
       * a user sets LD_LIBRARY_PATH with WriteVariable, and then calls
       * RunProgramInGuest, that program will see the new value.
       */
      if (NULL != userEnvironmentTable) {
         /*
          * The hash table will make a copy of valueName, but we have to supply
          * a deep copy of the value.
          */
         HashTable_ReplaceOrInsert(userEnvironmentTable, valueName,
                                   Util_SafeStrdup(value));
      }
#endif
      break;

   case VIX_GUEST_CONFIG:
   case VIX_VM_CONFIG_RUNTIME_ONLY:
   case VIX_VM_GUEST_VARIABLE:
   default:
      err = VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
      break;
   } // switch (readRequest->variableType)

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsWriteVariable


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsMoveObject --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsMoveObject(VixCommandRequestHeader *requestMsg)        // IN
{
   VixError err = VIX_OK;
   const char *srcFilePathName = NULL;
   const char *destFilePathName = NULL;
   Bool success;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   Bool overwrite = TRUE;
   VMAutomationRequestParser parser;
   int srcPathLen, destPathLen;


   if (VIX_COMMAND_MOVE_GUEST_FILE == requestMsg->opCode) {
      VixCommandRenameFileRequest *renameRequest;

      err = VMAutomationRequestParserInit(&parser,
                                          requestMsg, sizeof *renameRequest);
      if (VIX_OK != err) {
         goto quit;
      }

      renameRequest = (VixCommandRenameFileRequest *) requestMsg;
      srcPathLen = renameRequest->oldPathNameLength;
      destPathLen = renameRequest->newPathNameLength;
   } else if ((VIX_COMMAND_MOVE_GUEST_FILE_EX == requestMsg->opCode) ||
              (VIX_COMMAND_MOVE_GUEST_DIRECTORY == requestMsg->opCode)) {
      VixCommandRenameFileRequestEx *renameRequest;

      err = VMAutomationRequestParserInit(&parser,
                                          requestMsg, sizeof *renameRequest);
      if (VIX_OK != err) {
         goto quit;
      }

      renameRequest = (VixCommandRenameFileRequestEx *) requestMsg;
      srcPathLen = renameRequest->oldPathNameLength;
      destPathLen = renameRequest->newPathNameLength;
      overwrite = renameRequest->overwrite;
   } else {
      ASSERT(0);
      g_warning("%s: Invalid request with opcode %d received\n ",
                __FUNCTION__, requestMsg->opCode);
      err = VIX_E_FAIL;
      goto quit;
   }

   err = VMAutomationRequestParserGetString(&parser,
                                            srcPathLen,
                                            &srcFilePathName);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VMAutomationRequestParserGetString(&parser,
                                            destPathLen,
                                            &destFilePathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if ((0 == *srcFilePathName) || (0 == *destFilePathName)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s src: %s dst: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME,
           srcFilePathName, destFilePathName);

   if (!(File_Exists(srcFilePathName))) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   /*
    * Be careful. Renaming a file to itself can cause it to be deleted.
    * This should be a no-op anyway.
    */
#if !defined(sun) && !defined(__FreeBSD__)
   if (File_IsSameFile(srcFilePathName, destFilePathName)) {
      err = VIX_OK;
      goto quit;
   }
#else
   /*
    * Do something better for Solaris and FreeBSD once we support them.
    */
   if (strcmp(srcFilePathName, destFilePathName) == 0) {
      err = VIX_OK;
      goto quit;
   }
#endif

   /*
    * pre-check the dest arg -- File_Move() will return
    * diff err codes depending on OS, so catch it up front (bug 133165)
    */
   if (File_IsDirectory(destFilePathName)) {
      if ((VIX_COMMAND_MOVE_GUEST_FILE_EX == requestMsg->opCode) ||
          (VIX_COMMAND_MOVE_GUEST_DIRECTORY == requestMsg->opCode)) {

         /*
          * If File_IsDirectory() returns true, it doesn't mean the
          * filepath points to a real directory. It may point to a symlink.
          * So perform a quick symlink check. Do this only for opcodes
          * related to VI Guest Operations. Otherwise, it may affect
          * the existing tests.
          */
         if (!File_IsSymLink(destFilePathName)) {
            /*
             * If we are implementing opcodes related to VI Guest operations,
             * then return VIX_E_FILE_ALREADY_EXISTS. Don't change the error
             * code for opcode related to VIX C api. It will break the existing
             * tests.
            */
            err = VIX_E_FILE_ALREADY_EXISTS;
            goto quit;
         }
      } else {
         err = VIX_E_ALREADY_EXISTS;
         goto quit;
      }
   }

   if (VIX_COMMAND_MOVE_GUEST_FILE_EX == requestMsg->opCode) {
      if (File_IsDirectory(srcFilePathName)) {
         /*
          * Be careful while executing File_[File|Directory] operations.
          * In case of symlinks, these functions are smart engough to
          * resolve the final component pointed by the symlink and do
          * the check on the final component.
          *
          * For VI guest operations, MoveFile should return
          * VIX_E_NOT_A_FILE if the file path points to a real directory.
          * File_IsDirectory() returns true if it is invoked on a
          * symlink that points to a directory. So, we have to
          * filter out that case before returning VIX_E_NOT_A_FILE.
          */
         if (!File_IsSymLink(srcFilePathName)) {
            err = VIX_E_NOT_A_FILE;
            goto quit;
         }
      }
      if (!overwrite) {
         if (File_Exists(destFilePathName)) {
            err = VIX_E_FILE_ALREADY_EXISTS;
            goto quit;
         }
      }
   } else if (VIX_COMMAND_MOVE_GUEST_DIRECTORY == requestMsg->opCode) {
      /*
       * For VI guest operations, MoveDirectory should return
       * VIX_E_NOT_A_DIRECTORY if the file path doesn't point to a real
       * directory. File_IsDirectory() returns false if it is invoked on
       * a symlink that points to a file. So, we should include that
       * check before returning VIX_E_NOT_A_DIRECTORY.
       */
      if (!(File_IsDirectory(srcFilePathName)) ||
          (File_IsSymLink(srcFilePathName))) {
         err = VIX_E_NOT_A_DIRECTORY;
         goto quit;
      }

      /*
       * In case of moving a directory, File_Move() returns different
       * errors on different Guest Os if the destination file path points
       * to an existing file. We should catch them upfront and report them
       * to the user.
       * As per the documentation for rename() on linux, if the source
       * file points to an existing directory, then destionation file
       * should not point to anything other than a directory.
       */
      if (File_IsSymLink(destFilePathName) || File_IsFile(destFilePathName)) {
         err = VIX_E_FILE_ALREADY_EXISTS;
         goto quit;
      }
   }

   success = File_Move(srcFilePathName, destFilePathName, NULL);
   if (!success) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      g_warning("%s: File_Move failed.\n", __FUNCTION__);
      goto quit;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsMoveObject


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsInitiateFileTransferFromGuest --
 *
 *    This function is called to implement
 *    InitiateFileTransferFromGuest VI guest operation. Specified filepath
 *    should not point to a directory or a symlink.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsInitiateFileTransferFromGuest(VixCommandRequestHeader *requestMsg,    // IN
                                      char **result)                          // OUT
{
   VixError err = VIX_OK;
   const char *filePathName = NULL;
   char *resultBuffer = NULL;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   // re-use of ListFiles op
   VixMsgListFilesRequest *commandRequest = NULL;
   VMAutomationRequestParser parser;

   ASSERT(NULL != requestMsg);
   ASSERT(NULL != result);

   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *commandRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   commandRequest = (VixMsgListFilesRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            commandRequest->guestPathNameLength,
                                            &filePathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *filePathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s filePath: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME, filePathName);

   if (File_IsSymLink(filePathName)){
      g_warning("%s: File path cannot point to a symlink.\n", __FUNCTION__);
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   if (File_IsDirectory(filePathName)) {
      err = VIX_E_NOT_A_FILE;
      goto quit;
   }

   if (!File_Exists(filePathName)) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   resultBuffer = VixToolsPrintFileExtendedInfoEx(filePathName, filePathName);
   if (*resultBuffer == '\0') {
      err = VIX_E_FILE_ACCESS_ERROR;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   if (NULL == resultBuffer) {
      resultBuffer = Util_SafeStrdup("");
   }
   *result = resultBuffer;

   guest_debug("%s: returning '%s'\n", __FUNCTION__, resultBuffer);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsInitiateFileTransferFromGuest


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsInitiateFileTransferToGuest --
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsInitiateFileTransferToGuest(VixCommandRequestHeader *requestMsg)  // IN
{
   VixError err = VIX_OK;
   const char *guestPathName = NULL;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   Bool overwrite = TRUE;
   char *dirName = NULL;
   char *baseName = NULL;
   int32 fileAttributeOptions = 0;
#if defined(_WIN32)
   int fd = -1;
   char *tempFilePath = NULL;
   static char *tempFileBaseName = "vmware";
#endif
   FileIOResult res;

   VixCommandInitiateFileTransferToGuestRequest *commandRequest;
   VMAutomationRequestParser parser;

   ASSERT(NULL != requestMsg);

   /*
    * Parse the argument
    */
   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg,
                                       sizeof *commandRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   commandRequest = (VixCommandInitiateFileTransferToGuestRequest *) requestMsg;
   overwrite = commandRequest->overwrite;

   err = VMAutomationRequestParserGetString(&parser,
                                            commandRequest->guestPathNameLength,
                                            &guestPathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if ('\0' == *guestPathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   fileAttributeOptions = commandRequest->options;

#if defined(_WIN32)
   if ((fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_OWNERID) ||
       (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_GROUPID) ||
       (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_PERMISSIONS)) {
      g_warning("%s: Invalid attributes received for Windows Guest\n",
                __FUNCTION__);
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
#else
   if ((fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_HIDDEN) ||
       (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_READONLY)) {
      g_warning("%s: Invalid attributes received for Unix Guest\n",
                __FUNCTION__);
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
#endif

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s path: %s attrs: %d\n",
           __FUNCTION__, IMPERSONATED_USERNAME,
           guestPathName, fileAttributeOptions);

   if (File_IsSymLink(guestPathName)) {
      g_warning("%s: Filepath cannot point to a symlink.\n", __FUNCTION__);
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   if (File_Exists(guestPathName)) {
      if (File_IsDirectory(guestPathName)) {
         err = VIX_E_NOT_A_FILE;
      } else if (!overwrite) {
         err = VIX_E_FILE_ALREADY_EXISTS;
      } else {
         /*
          * If the file exists and overwrite flag is true, then check
          * if the file is writable. If not, return a proper error.
          */
         res = FileIO_Access(guestPathName, FILEIO_ACCESS_WRITE);
         if (FILEIO_SUCCESS != res) {
            /*
             * On Linux guests, FileIO_Access sets the proper errno
             * on failure. On Windows guests, last errno is not
             * set when FileIO_Access fails. So, we cannot use
             * FoundryToolsDaemon_TranslateSystemErr() to translate the
             * error. To maintain consistency for all the guests,
             * return an explicit VIX_E_FILE_ACCESS_ERROR.
             */
            err = VIX_E_FILE_ACCESS_ERROR;
            g_warning("%s: Unable to get access permissions for the file: %s\n",
                       __FUNCTION__, guestPathName);
         }
      }
      goto quit;
   }

   File_GetPathName(guestPathName, &dirName, &baseName);
   if ((NULL == dirName) || (NULL == baseName)) {
      g_debug("%s: File_GetPathName failed for '%s', dirName='%s', "
              "baseName='%s'.\n", __FUNCTION__, guestPathName,
              dirName ? dirName : "(null)",
              baseName ? baseName : "(null)");
      err = VIX_E_FILE_NAME_INVALID;
      goto quit;
   }

#ifndef _WIN32
   if ('\0' == *dirName && '/' == *guestPathName) {
      /*
       * dirName is empty and represents root directory
       * For *nix like paths, changing dirName to '/'
       */
      free(dirName);
      dirName = Util_SafeStrdup("/");
   }
#endif

   if (!File_IsDirectory(dirName)) {
#ifdef _WIN32
      DWORD sysErr = GetLastError();
#else
      int sysErr = errno;
#endif
      g_debug("%s: File_IsDirectory failed for '%s', err=%d.\n",
              __FUNCTION__, dirName, sysErr);
      err = VIX_E_FILE_NAME_INVALID;
      goto quit;
   }

#if defined(_WIN32)
   /*
    * Ideally, we just need to check if the user has proper write
    * access to create a child inside the directory. This can be
    * checked by calling FileIO_Access(). FileIO_Access works perfectly
    * fine for linux platforms. But on Windows, FileIO_Access just
    * checks the read-only attribute of the directory and returns the result
    * based on that. This is not the proper way to check the write
    * permissions.
    *
    * One other way to check the write access is to call CreateFile()
    * with GENERIC_WRITE and OPEN_EXISTING flags. Check the documentation
    * for CreateFile() at
    * http://msdn.microsoft.com/en-us/library/aa363858%28v=VS.85%29.aspx.
    * But this has got few limitations. CreateFile() doesn't return proper
    * result when called for directories on NTFS systems.
    * Checks the KB article available at
    * http://support.microsoft.com/kb/810881.
    *
    * So, for windows, the best bet is to create an empty temporary file
    * inside the directory and immediately unlink that. If creation is
    * successful, it ensures that the user has proper write access for
    * the directory.
    *
    * Since we are just checking the write access, there is no need to
    * create the temporary file with the exact specified filename. Any name
    * would be fine.
    */
   fd = File_MakeTempEx(dirName, tempFileBaseName, &tempFilePath);

   if (fd > 0) {
      close(fd);
      File_UnlinkNoFollow(tempFilePath);
   } else {
      /*
       * File_MakeTempEx() function internally uses Posix variant
       * functions and proper error will be stuffed in errno variable.
       * If File_MakeTempEx() fails, then use Vix_TranslateErrno()
       * to translate the errno to a proper foundry error.
       */
      err = Vix_TranslateErrno(errno);
      g_warning("%s: Unable to create a temp file to test directory "
                "permissions, errno is %d\n", __FUNCTION__, errno);
      goto quit;
   }

   free(tempFilePath);
#else
   /*
    * We need to check if the user has write access to create
    * a child inside the directory. Call FileIO_Access() to check
    * for the proper write permissions for the directory.
    */
   res = FileIO_Access(dirName, FILEIO_ACCESS_WRITE);

   if (FILEIO_SUCCESS != res) {
      /*
       * On Linux guests, FileIO_Access sets the proper errno
       * on failure. On Windows guests, last errno is not
       * set when FileIO_Access fails. So, we cannot use
       * FoundryToolsDaemon_TranslateSystemErr() to translate the
       * error. To maintain consistency for all the guests,
       * return an explicit VIX_E_FILE_ACCESS_ERROR.
       */
      err = VIX_E_FILE_ACCESS_ERROR;
      g_warning("%s: Unable to get access permissions for the directory: %s\n",
                __FUNCTION__, dirName);
      goto quit;
   }
#endif

quit:
   free(baseName);
   free(dirName);

   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsInitiateFileTransferToGuest


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListProcesses --
 *
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsListProcesses(VixCommandRequestHeader *requestMsg, // IN
                      size_t maxBufferSize,                // IN
                      char **result)                       // OUT
{
   VixError err = VIX_OK;
   int i;
   static char resultBuffer[GUESTMSG_MAX_IN_SIZE];
   ProcMgrProcInfoArray *procList = NULL;
   ProcMgrProcInfo *procInfo;
   char *destPtr;
   char *endDestPtr;
   char *cmdNamePtr = NULL;
   char *procBufPtr = NULL;
   size_t procBufSize;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   Bool escapeStrs;
   char *escapedName = NULL;
   char *escapedUser = NULL;
   char *escapedCmd = NULL;
   size_t procCount;

   ASSERT(maxBufferSize <= GUESTMSG_MAX_IN_SIZE);

   destPtr = resultBuffer;
   *destPtr = 0;

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s \n",
           __FUNCTION__, IMPERSONATED_USERNAME);

   escapeStrs = (requestMsg->requestFlags &
                 VIX_REQUESTMSG_ESCAPE_XML_DATA) != 0;

   procList = ProcMgr_ListProcesses();
   if (NULL == procList) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   endDestPtr = resultBuffer + maxBufferSize;

   if (escapeStrs) {
      destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr, "%s",
                             VIX_XML_ESCAPED_TAG);
   }

   procCount = ProcMgrProcInfoArray_Count(procList);
   for (i = 0; i < procCount; i++) {
      const char *name;
      const char *user;

      procInfo = ProcMgrProcInfoArray_AddressOf(procList, i);

      if (NULL != procInfo->procCmdName) {
         if (escapeStrs) {
            escapedCmd =
            VixToolsEscapeXMLString(procInfo->procCmdName);
            if (NULL == escapedCmd) {
               err = VIX_E_OUT_OF_MEMORY;
               goto quit;
            }
            cmdNamePtr = Str_SafeAsprintf(NULL,
                                          "<cmd>%s</cmd>",
                                          escapedCmd);
         } else {
            cmdNamePtr = Str_SafeAsprintf(NULL,
                                          "<cmd>%s</cmd>",
                                          procInfo->procCmdName);
         }
      } else {
         cmdNamePtr = Util_SafeStrdup("");
      }

      if (escapeStrs) {
         name = escapedName =
            VixToolsEscapeXMLString(procInfo->procCmdLine);
         if (NULL == escapedName) {
            err = VIX_E_OUT_OF_MEMORY;
            goto quit;
         }
      } else {
         name = procInfo->procCmdLine;
      }

      if (NULL != procInfo->procOwner) {
         if (escapeStrs) {
            user = escapedUser =
               VixToolsEscapeXMLString(procInfo->procOwner);
            if (NULL == escapedUser) {
               err = VIX_E_OUT_OF_MEMORY;
               goto quit;
            }
         } else {
            user = procInfo->procOwner;
         }
      } else {
         user = "";
      }

      procBufPtr = Str_SafeAsprintf(&procBufSize,
                                    "<proc>"
                                    "%s"              // has <cmd>...</cmd> tags if there is cmd, else "";
                                    "<name>%s</name>"
                                    "<pid>%d</pid>"
#if defined(_WIN32)
                                    "<debugged>%d</debugged>"
#endif
                                    "<user>%s</user>"
                                    "<start>%"FMT64"d</start>"
                                    "</proc>",
                                    cmdNamePtr, name, (int) procInfo->procId,
#if defined(_WIN32)
                                    (int) procInfo->procDebugged,
#endif
                                    user, (int64) procInfo->procStartTime);
      if (NULL == procBufPtr) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      if ((destPtr + procBufSize) < endDestPtr) {
         destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr,
                                "%s", procBufPtr);
      } else { // out of space
         Log("%s: proc list results too large, truncating", __FUNCTION__);
         goto quit;
      }
      free(cmdNamePtr);
      cmdNamePtr = NULL;
      free(procBufPtr);
      procBufPtr = NULL;
      free(escapedName);
      escapedName = NULL;
      free(escapedUser);
      escapedUser = NULL;
      free(escapedCmd);
      escapedCmd = NULL;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);
   ProcMgr_FreeProcList(procList);
   free(cmdNamePtr);
   free(procBufPtr);
   free(escapedName);
   free(escapedUser);
   free(escapedCmd);

   *result = resultBuffer;

   // XXX result too large for g_debug()

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return(err);
} // VixToolsListProcesses


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsFreeCachedResult --
 *
 *    Hash table value destroy func.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
VixToolsFreeCachedResult(gpointer ptr)          // IN
{
   VixToolsCachedListProcessesResult *p = (VixToolsCachedListProcessesResult *) ptr;

   if (NULL != p) {
      free(p->resultBuffer);
#ifdef _WIN32
      free(p->userName);
#endif
      free(p);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListProcCacheCleanup --
 *
 *
 * Return value:
 *    FALSE -- tells glib not to clean up
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
VixToolsListProcCacheCleanup(void *clientData) // IN
{
   int key = (int)(intptr_t)clientData;
   gboolean ret;

   ret = g_hash_table_remove(listProcessesResultsTable, &key);
   g_debug("%s: list proc cache timed out, purged key %d (found? %d)\n",
           __FUNCTION__, key, ret);

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListProcessesExGenerateData --
 *
 *    Does the work to generate the results into a string buffer.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    Allocates and creates the result buffer.
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsListProcessesExGenerateData(uint32 numPids,          // IN
                                    const uint64 *pids,      // IN
                                    GKeyFile *confDictRef,   // IN
                                    size_t *resultSize,      // OUT
                                    char **resultBuffer)     // OUT
{
   VixError err = VIX_OK;
   ProcMgrProcInfoArray *procList = NULL;
   ProcMgrProcInfo *procInfo;
   DynBuf dynBuffer;
   VixToolsStartedProgramState *spList;
   int numReported = 0;
   int i;
   int j;
   Bool bRet;
   size_t procCount;

#ifdef _WIN32
   gboolean useRemoteThreadProcCmdLine;
   gboolean useWMIProcCmdLine;
#endif

   DynBuf_Init(&dynBuffer);

   /*
    * First check the processes we've started via StartProgram, which
    * will find those running and recently deceased.
    */
   VixToolsUpdateStartedProgramList(NULL);
   if (numPids > 0) {
      for (i = 0; i < numPids; i++) {
         spList = startedProcessList;
         while (spList) {
            if (pids[i] == spList->pid) {
               err = VixToolsPrintProcInfoEx(&dynBuffer,
                                             spList->cmdName,
                                             spList->fullCommandLine,
                                             spList->pid,
                                             spList->user,
                                             spList->startTime,
                                             spList->exitCode,
                                             spList->endTime);
               if (VIX_OK != err) {
                  goto quit;
               }
               numReported++;
               break;
            }
            spList = spList->next;
         }
      }
   } else {
      spList = startedProcessList;
      while (spList) {
         err = VixToolsPrintProcInfoEx(&dynBuffer,
                                       spList->cmdName,
                                       spList->fullCommandLine,
                                       spList->pid,
                                       spList->user,
                                       spList->startTime,
                                       spList->exitCode,
                                       spList->endTime);
         if (VIX_OK != err) {
            goto quit;
         }
         spList = spList->next;
      }
   }

   /*
    * If we found data for all requested processes from the startedProcess
    * list, then we're done.
    */
   if (numPids > 0 && (numPids == numReported)) {
      g_debug("%s: found all %d requested pids on the startedProcess list; finished\n",
              __FUNCTION__, numPids);
      goto done;
   }

   /*
    * The startedProcess list didn't give everything we need, so
    * ask the OS.
    *
    * XXX Need a smarter version of ProcMgr_ListProcesses().
    * It should allow a list of pids for optimization, and
    * return an error code so there's no risk of errno/LastError
    * being clobbered.
    */
#ifdef _WIN32
   useRemoteThreadProcCmdLine = VMTools_ConfigGetBoolean(confDictRef,
                     VIX_TOOLS_CONFIG_API_GROUPNAME,
                     VIXTOOLS_CONFIG_USE_REMOTE_THREAD_PROCESS_COMMAND_LINE,
                     USE_REMOTE_THREAD_PROCESS_COMMAND_LINE_DEFAULT);

   useWMIProcCmdLine = VMTools_ConfigGetBoolean(confDictRef,
                          VIX_TOOLS_CONFIG_API_GROUPNAME,
                          VIXTOOLS_CONFIG_USE_WMI_PROCESS_COMMAND_LINE,
                          USE_WMI_PROCESS_COMMAND_LINE_DEFAULT);

   g_debug("Options for process cmdline: useRemoteThread: %d, useWMI: %d\n",
           useRemoteThreadProcCmdLine, useWMIProcCmdLine);

   procList = ProcMgr_ListProcessesEx(useRemoteThreadProcCmdLine,
                                      useWMIProcCmdLine);
#else
   procList = ProcMgr_ListProcesses();
#endif
   if (NULL == procList) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }


   /*
    * Now look at the running list.  Note that we set endTime
    * and exitCode to dummy values, since we'll be getting results on
    * the Vix side with GetNthProperty, and can have a mix of live and
    * dead processes.
    */
   procCount = ProcMgrProcInfoArray_Count(procList);
   if (numPids > 0) {
      for (i = 0; i < numPids; i++) {
         // ignore it if its on the started list -- we added it above
         if (VixToolsFindStartedProgramState(pids[i])) {
            continue;
         }
         for (j = 0; j < procCount; j++) {
            procInfo = ProcMgrProcInfoArray_AddressOf(procList, j);
            if (pids[i] == procInfo->procId) {
               err = VixToolsPrintProcInfoEx(&dynBuffer,
                                             procInfo->procCmdName,
                                             procInfo->procCmdLine,
                                             procInfo->procId,
                                             (NULL == procInfo->procOwner)
                                             ? "" : procInfo->procOwner,
                                             procInfo->procStartTime,
                                             0, 0);
               if (VIX_OK != err) {
                  goto quit;
               }
            }
         }
      }
   } else {
      for (i = 0; i < procCount; i++) {
         procInfo = ProcMgrProcInfoArray_AddressOf(procList, i);
         // ignore it if its on the started list -- we added it above
         if (VixToolsFindStartedProgramState(procInfo->procId)) {
            continue;
         }
         err = VixToolsPrintProcInfoEx(&dynBuffer,
                                       procInfo->procCmdName,
                                       procInfo->procCmdLine,
                                       procInfo->procId,
                                       (NULL == procInfo->procOwner)
                                       ? "" : procInfo->procOwner,
                                       procInfo->procStartTime,
                                       0, 0);
         if (VIX_OK != err) {
            goto quit;
         }
      }
   }

done:

   // add the final NUL
   bRet = DynBuf_Append(&dynBuffer, "", 1);
   if (!bRet) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   DynBuf_Trim(&dynBuffer);
   *resultSize = DynBuf_GetSize(&dynBuffer);
   *resultBuffer  = DynBuf_Detach(&dynBuffer);

quit:
   DynBuf_Destroy(&dynBuffer);
   ProcMgr_FreeProcList(procList);
   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListProcessesEx --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsListProcessesEx(VixCommandRequestHeader *requestMsg, // IN
                        size_t maxBufferSize,                // IN
                        GKeyFile *confDictRef,               // IN
                        void *eventQueue,                    // IN
                        char **result)                       // OUT
{
   VixError err = VIX_OK;
   char *fullResultBuffer = NULL;
   char *finalResultBuffer = NULL;
   size_t fullResultSize = 0;
   size_t curPacketLen = 0;
   int32 leftToSend = 0;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgListProcessesExRequest *listRequest;
   uint64 *pids = NULL;
   uint32 numPids;
   uint32 key;
   uint32 offset;
   int len;
   VixToolsCachedListProcessesResult *cachedResult = NULL;
   GSource *timer;
#ifdef _WIN32
   Bool bRet;
   wchar_t *userName = NULL;
#endif
   static const char resultHeaderFormatString[] =
                                 "<key>%u</key><totalSize>%d</totalSize>"
                                 "<leftToSend>%d</leftToSend>";
   // room for header plus 3 32-bit ints
   int resultHeaderSize = sizeof(resultHeaderFormatString) + 3 * 10;
   static const char leftHeaderFormatString[] =
                                 "<leftToSend>%d</leftToSend>";
   // room for header plus 1 32-bit ints
   int leftHeaderSize = sizeof(leftHeaderFormatString) + 10;

   ASSERT(maxBufferSize <= GUESTMSG_MAX_IN_SIZE);
   ASSERT(maxBufferSize > resultHeaderSize);

   listRequest = (VixMsgListProcessesExRequest *) requestMsg;

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);

#if defined(__APPLE__)
   /*
    * On MacOS, to fetch info on processes owned by others
    * we need to be root. Even /bin/ps and /bin/top in
    * MacOS have the setuid bit set to allow any user
    * list all processes. For linux & FreeBSD, this API
    * does return info on all processes by all users. So
    * to keep the result consistent on MacOS, we need to
    * stop impersonating user for this API.
    *
    * NOTE: We still do the impersonation before this
    * to authenticate the user as usual.
    */
   VixToolsUnimpersonateUser(userToken);
   impersonatingVMWareUser = FALSE;
#endif

   key = listRequest->key;
   offset = listRequest->offset;

   /*
    * If the request has a key, then go look up the cached results
    * it should point to.
    */
   if (0 != key) {

      // find the cached data
      cachedResult = g_hash_table_lookup(listProcessesResultsTable,
                                         &key);
      if (NULL == cachedResult) {
         g_warning("%s: failed to find cached data with key %d\n",
                   __FUNCTION__, key);
         err = VIX_E_FAIL;
         goto quit;
      }

      // confidence check offset
      if (listRequest->offset > cachedResult->resultBufferLen) {
         /*
          * Since this isn't user-set, assume any problem is in the
          * code and return VIX_E_FAIL
          */
         err = VIX_E_FAIL;
         goto quit;
      }

      // security check -- validate user
#ifdef _WIN32
      bRet = VixToolsGetUserName(&userName);
      if (!bRet) {
         g_warning("%s: VixToolsGetUserName() failed\n", __FUNCTION__);
         err = VIX_E_FAIL;
         goto quit;
      }
      if (0 != wcscmp(userName, cachedResult->userName)) {
         /*
          * Since this isn't user-set, assume any problem is in the
          * code and return VIX_E_FAIL
          */
         g_warning("%s: username mismatch validating cached data (have %S, want %S)\n",
                   __FUNCTION__, userName, cachedResult->userName);
         err = VIX_E_FAIL;
         goto quit;
      }
#else
      if (cachedResult->euid != Id_GetEUid()) {
         /*
          * Since this isn't user-set, assume any problem is in the
          * code and return VIX_E_FAIL
          */
         err = VIX_E_FAIL;
         g_warning("%s: euid mismatch validating cached data (want %d, got %d)\n",
                   __FUNCTION__, (int) cachedResult->euid, (int) Id_GetEUid());
         goto quit;
      }
#endif

   } else {
      /*
       * No key, so this is the initial/only request.  Generate data,
       * cache if necessary.
       */

      numPids = listRequest->numPids;
      if (numPids > 0) {
         pids = (uint64 *)((char *)requestMsg + sizeof(*listRequest));
      }

      err = VixToolsListProcessesExGenerateData(numPids, pids, confDictRef,
                                                &fullResultSize,
                                                &fullResultBuffer);

      /*
       * Check if the result is large enough to require more than one trip.
       * Stuff it in the hash table if so.
       */
      if ((fullResultSize + resultHeaderSize) > maxBufferSize) {
         g_debug("%s: answer requires caching.  have %d bytes\n",
                 __FUNCTION__, (int) (fullResultSize + resultHeaderSize));
         /*
          * Save it off in the hashtable.
          */
         key = listProcessesResultsKey++;
         cachedResult = Util_SafeMalloc(sizeof(*cachedResult));
         cachedResult->resultBufferLen = fullResultSize;
         cachedResult->resultBuffer = fullResultBuffer;
         cachedResult->key = key;
#ifdef _WIN32
         bRet = VixToolsGetUserName(&cachedResult->userName);
         if (!bRet) {
            g_warning("%s: failed to get current userName\n", __FUNCTION__);
            goto quit;
         }
#else
         cachedResult->euid = Id_GetEUid();
#endif

         g_hash_table_replace(listProcessesResultsTable, &cachedResult->key,
                              cachedResult);

         /*
          * Set timer callback to clean this up in case the Vix side
          * never finishes
          */
         timer = g_timeout_source_new(SECONDS_UNTIL_LISTPROC_CACHE_CLEANUP * 1000);
         g_source_set_callback(timer, VixToolsListProcCacheCleanup,
                               (void *)(intptr_t) key, NULL);
         g_source_attach(timer, g_main_loop_get_context(eventQueue));
         g_source_unref(timer);
      }
   }

   /*
    * Now package up the return data.
    */
   if (NULL != cachedResult) {
      int hdrSize;

      /*
       * For the first packet, sent the key and total size and leftToSend.
       * After that, just send leftToSend.
       */
      if (0 == offset) {
         hdrSize = resultHeaderSize;
      } else {
         hdrSize = leftHeaderSize;
      }

      leftToSend = cachedResult->resultBufferLen - offset;

      if (leftToSend > (maxBufferSize - hdrSize)) {
         curPacketLen = maxBufferSize - hdrSize;
      } else {
         curPacketLen = leftToSend;
      }

      leftToSend -= curPacketLen;

      finalResultBuffer = Util_SafeMalloc(curPacketLen + hdrSize + 1);
      if (0 == offset) {

         len = Str_Sprintf(finalResultBuffer, maxBufferSize,
                           resultHeaderFormatString,
                           key, (int) cachedResult->resultBufferLen,
                           leftToSend);
      } else {
         len = Str_Sprintf(finalResultBuffer, maxBufferSize,
                           leftHeaderFormatString,
                           leftToSend);
      }

      memcpy(finalResultBuffer + len,
             cachedResult->resultBuffer + offset, curPacketLen);
      finalResultBuffer[curPacketLen + len] = '\0';

      /*
       * All done, clean it out of the hash table.
       */
      if (0 == leftToSend) {
         g_hash_table_remove(listProcessesResultsTable, &key);
      }

   } else {
      /*
       * In the simple/common case, just return the basic proces info.
       */
      finalResultBuffer = fullResultBuffer;
   }


quit:
#ifdef _WIN32
   free(userName);
#endif
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   *result = finalResultBuffer;

   // XXX result too large for g_debug()

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return(err);
} // VixToolsListProcessesEx


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsPrintProcInfoEx --
 *
 *      Appends a single process entry to the XML-like string starting at
 *      *destPtr.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsPrintProcInfoEx(DynBuf *dstBuffer,             // IN/OUT
                        const char *cmd,               // IN
                        const char *name,              // IN
                        uint64 pid,                    // IN
                        const char *user,              // IN
                        time_t start,                  // IN
                        int exitCode,                  // IN
                        time_t exitTime)               // IN
{
   VixError err;
   char *escapedName = NULL;
   char *escapedCmd = NULL;
   char *escapedUser = NULL;
   char *cmdNamePtr = NULL;
   size_t bytesPrinted;
   char *procInfoEntry;
   Bool success;

   if (NULL != cmd) {
      escapedCmd = VixToolsEscapeXMLString(cmd);
      if (NULL == escapedCmd) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      cmdNamePtr = Str_SafeAsprintf(NULL, "<cmd>%s</cmd>", escapedCmd);
   } else {
      cmdNamePtr = Util_SafeStrdup("");
   }

   escapedName = VixToolsEscapeXMLString(name);
   if (NULL == escapedName) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   escapedUser = VixToolsEscapeXMLString(user);
   if (NULL == escapedUser) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   procInfoEntry = Str_SafeAsprintf(&bytesPrinted,
                                    "<proc>"
                                    "%s"              // has <cmd>...</cmd> tags if there is cmd, else "";
                                    "<name>%s</name>"
                                    "<pid>%"FMT64"d</pid>"
                                    "<user>%s</user>"
                                    "<start>%"FMT64"d</start>"
                                    "<eCode>%d</eCode>"
                                    "<eTime>%"FMT64"d</eTime>"
                                    "</proc>",
                                    cmdNamePtr, escapedName, pid, escapedUser,
                                    (int64) start, exitCode, (int64) exitTime);
   if (NULL == procInfoEntry) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   success = DynBuf_Append(dstBuffer, procInfoEntry, bytesPrinted);
   free(procInfoEntry);
   if (!success) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   err = VIX_OK;

quit:
   free(cmdNamePtr);
   free(escapedName);
   free(escapedUser);
   free(escapedCmd);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsKillProcess --
 *
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsKillProcess(VixCommandRequestHeader *requestMsg) // IN
{
   VixError err = VIX_OK;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixCommandKillProcessRequest *killProcessRequest;
#ifdef _WIN32
   DWORD dwErr;
   const VixToolsStartedProgramState *spState;
#else
   int sysErrno;
#endif

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   killProcessRequest = (VixCommandKillProcessRequest *) requestMsg;

   g_debug("%s: User: %s pid: %"FMT64"d\n",
           __FUNCTION__, IMPERSONATED_USERNAME, killProcessRequest->pid);

   /*
    * This is here for two reasons:
    *  1) If you force this process to quit, then it cannot report back to
    *     you that the command succeeded.
    *  2) On Linux, you can either always send a signal to youself,
    *     or it just compares the source and destination real, effective,
    *     and saved UIDs. Anyway, no matter who guestd is impersonating,
    *     this will succeed. However, normally a regular user cannot force
    *     guestd to quit, and should not be able to because of an implementation
    *     detail.
    */
   if (VixToolsPidRefersToThisProcess(killProcessRequest->pid)) {
      err = VIX_E_GUEST_USER_PERMISSIONS;
      goto quit;
   }

#if defined(__APPLE__)
   /*
    * On OS X, we can only impersonate the effective UID, not real.
    * But the kill(2) syscall looks at real UID.  This means its working
    * as root, and therefore its a massive security hole to leave it
    * as-is.
    *
    * Its unclear if anyone actually cares, so for now, just turn
    * it off.  Its trivial to workaround (RunProgram of 'kill <pid>'.)
    *
    * Two possible fixes if necessary:
    *
    * - kludge things and rewrite as a RunProgram("kill <pid>").  This
    *   will lose probably error info.
    * - do the kill inside fork(), sending back any error code
    *   on a pipe.
    */
   err = VIX_E_NOT_SUPPORTED;
   goto quit;
#endif

   if (!ProcMgr_KillByPid(killProcessRequest->pid)) {
      /*
       * Save off the error code so any debug statements added later
       * (or when debugging something else) doesn't change the error code.
       */
#ifdef _WIN32
      dwErr = GetLastError();
#else
      sysErrno = errno;
#endif


#ifdef _WIN32
      /*
       * If we know it's already gone, just say so.  If this gets called
       * on a process we started but is still on the 'exited' list,
       * then Windows returns an ACCESS_ERROR.  So rewrite it.
       */
       spState = VixToolsFindStartedProgramState(killProcessRequest->pid);
       if ((NULL != spState) && !spState->isRunning) {
         err = VIX_E_NO_SUCH_PROCESS;
         goto quit;
      }
#endif

      /*
       * Vix_TranslateSystemError() assumes that any perm error
       * is file related, and returns VIX_E_FILE_ACCESS_ERROR.  Bogus
       * for this case, so rewrite it here.
       */
#ifdef _WIN32
      if (ERROR_ACCESS_DENIED == dwErr) {
         err = VIX_E_GUEST_USER_PERMISSIONS;
         goto quit;
      }
#else
      if ((EPERM == sysErrno) || (EACCES == sysErrno)) {
         err = VIX_E_GUEST_USER_PERMISSIONS;
         goto quit;
      }
#endif


#ifdef _WIN32
      /*
       * Windows doesn't give us an obvious error for a non-existent
       * PID.  But we can make a pretty good guess that it returned
       * ERROR_INVALID_PARAMETER because the PID was bad, so rewrite
       * that error if we see it.
       */
      if (ERROR_INVALID_PARAMETER == dwErr) {
         err = VIX_E_NO_SUCH_PROCESS;
         goto quit;
      }
#endif

#ifdef _WIN32
      err = Vix_TranslateSystemError(dwErr);
#else
      err = Vix_TranslateSystemError(sysErrno);
#endif

      goto quit;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsKillProcess


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsCreateDirectory --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsCreateDirectory(VixCommandRequestHeader *requestMsg)  // IN
{
   VixError err = VIX_OK;
   const char *dirPathName = NULL;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   Bool createParentDirectories = TRUE;
   VMAutomationRequestParser parser;
   int dirPathLen;

   if (VIX_COMMAND_CREATE_DIRECTORY == requestMsg->opCode) {
      VixMsgCreateFileRequest *dirRequest = NULL;

      err = VMAutomationRequestParserInit(&parser,
                                          requestMsg, sizeof *dirRequest);
      if (VIX_OK != err) {
         goto quit;
      }

      dirRequest = (VixMsgCreateFileRequest *) requestMsg;
      dirPathLen = dirRequest->guestPathNameLength;
   } else if (VIX_COMMAND_CREATE_DIRECTORY_EX == requestMsg->opCode) {
      VixMsgCreateFileRequestEx *dirRequest = NULL;

      err = VMAutomationRequestParserInit(&parser,
                                          requestMsg, sizeof *dirRequest);
      if (VIX_OK != err) {
         goto quit;
      }

      dirRequest = (VixMsgCreateFileRequestEx *) requestMsg;
      dirPathLen = dirRequest->guestPathNameLength;
      createParentDirectories = dirRequest->createParentDirectories;
   } else {
      ASSERT(0);
      g_warning("%s: Invalid request with opcode %d received\n ",
                __FUNCTION__, requestMsg->opCode);
      err = VIX_E_FAIL;
      goto quit;
   }

   err = VMAutomationRequestParserGetString(&parser,
                                            dirPathLen, &dirPathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *dirPathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s dirPathName: %s createParent: %d\n",
           __FUNCTION__, IMPERSONATED_USERNAME,
           dirPathName, (int) createParentDirectories);

   if (File_Exists(dirPathName)) {
      err = VIX_E_FILE_ALREADY_EXISTS;
      goto quit;
   }

   if (createParentDirectories) {
      if (!(File_CreateDirectoryHierarchyEx(dirPathName, 0700, NULL))) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
   } else {
      if (!(File_CreateDirectoryEx(dirPathName, 0700))) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsCreateDirectory


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListDirectory --
 *
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsListDirectory(VixCommandRequestHeader *requestMsg,    // IN
                      size_t maxBufferSize,                   // IN
                      char **result)                          // OUT
{
   VixError err = VIX_OK;
   const char *dirPathName = NULL;
   char *fileList = NULL;
   char **fileNameList = NULL;
   size_t resultBufferSize = 0;
   size_t lastGoodResultBufferSize = 0;
   int numFiles = 0;
   int lastGoodNumFiles = 0;
   int fileNum;
   char *currentFileName;
   char *destPtr;
   char *endDestPtr;
   Bool impersonatingVMWareUser = FALSE;
   size_t formatStringLength;
   void *userToken = NULL;
   VixMsgListDirectoryRequest *listRequest = NULL;
   VixMsgSimpleFileRequest *legacyListRequest = NULL;
   Bool truncated = FALSE;
   int64 offset = 0;
   Bool isLegacyFormat;
   VMAutomationRequestParser parser;
   int dirPathLen;
   Bool escapeStrs;

   legacyListRequest = (VixMsgSimpleFileRequest *) requestMsg;
   if (legacyListRequest->fileOptions & VIX_LIST_DIRECTORY_USE_OFFSET) {
      /*
       * Support updated ListDirectory format.
       */
      err = VMAutomationRequestParserInit(&parser,
                                          requestMsg, sizeof *listRequest);
      if (VIX_OK != err) {
         goto quit;
      }

      listRequest = (VixMsgListDirectoryRequest *) requestMsg;
      offset = listRequest->offset;

      dirPathLen = listRequest->guestPathNameLength;
      isLegacyFormat = FALSE;
   } else {
      /*
       * Support legacy ListDirectory format.
       */
      err = VMAutomationRequestParserInit(&parser,
                                        requestMsg, sizeof *legacyListRequest);
      if (VIX_OK != err) {
         goto quit;
      }

      dirPathLen = legacyListRequest->guestPathNameLength;
      isLegacyFormat = TRUE;
   }

   err = VMAutomationRequestParserGetString(&parser,
                                            dirPathLen, &dirPathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *dirPathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s path: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME, dirPathName);

   escapeStrs = (requestMsg->requestFlags &
                 VIX_REQUESTMSG_ESCAPE_XML_DATA) != 0;

   if (!(File_IsDirectory(dirPathName))) {
      err = VIX_E_NOT_A_DIRECTORY;
      goto quit;
   }

   numFiles = File_ListDirectory(dirPathName, &fileNameList);
   if (numFiles < 0) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   /*
    * Calculate the size of the result buffer and keep track of the
    * max number of entries we can store.
    */
   resultBufferSize = 3; // truncation bool + space + '\0'
   if (escapeStrs) {
      resultBufferSize += strlen(VIX_XML_ESCAPED_TAG);
   }
   lastGoodResultBufferSize = resultBufferSize;
   ASSERT_NOT_IMPLEMENTED(lastGoodResultBufferSize < maxBufferSize);
   formatStringLength = strlen(fileInfoFormatString);

   for (fileNum = offset; fileNum < numFiles; fileNum++) {
      currentFileName = fileNameList[fileNum];

      resultBufferSize += formatStringLength;
      resultBufferSize += VixToolsXMLStringEscapedLen(currentFileName,
                                                      escapeStrs);
      resultBufferSize += 2; // DIRSEPC chars
      resultBufferSize += 10 + 20 + 20; // properties + size + modTime

      if (resultBufferSize < maxBufferSize) {
         /*
          * lastGoodNumFiles is a count (1 based), while fileNum is
          * an array index (zero based).  So lastGoodNumFiles is
          * fileNum + 1.
          */
         lastGoodNumFiles = fileNum + 1;
         lastGoodResultBufferSize = resultBufferSize;
      } else {
         truncated = TRUE;
         break;
      }
   }
   resultBufferSize = lastGoodResultBufferSize;

   /*
    * Print the result buffer.
    */
   fileList = Util_SafeMalloc(resultBufferSize);
   destPtr = fileList;
   endDestPtr = fileList + resultBufferSize;

   /*
    * Indicate if we have a truncated buffer with "1 ", otherwise "0 ".
    * This should only happen for non-legacy requests.
    */
   if (!isLegacyFormat) {
      if ((destPtr + 2) < endDestPtr) {
         *destPtr++ = truncated ? '1' : '0';
         *destPtr++ = ' ';
      } else {
         ASSERT(0);
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
   }

   if (escapeStrs) {
      destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr, "%s",
                             VIX_XML_ESCAPED_TAG);
   }

   for (fileNum = offset; fileNum < lastGoodNumFiles; fileNum++) {
      /* File_ListDirectory never returns "." or ".." */
      char *pathName;

      currentFileName = fileNameList[fileNum];

      pathName = Str_SafeAsprintf(NULL, "%s%s%s", dirPathName, DIRSEPS,
                                  currentFileName);

      VixToolsPrintFileInfo(pathName, currentFileName, escapeStrs, &destPtr,
                            endDestPtr);

      free(pathName);
   } // for (fileNum = 0; fileNum < lastGoodNumFiles; fileNum++)
   *destPtr = '\0';

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   if (NULL == fileList) {
      fileList = Util_SafeStrdup("");
   }
   *result = fileList;

   if (NULL != fileNameList) {
      for (fileNum = 0; fileNum < numFiles; fileNum++) {
         free(fileNameList[fileNum]);
      }
      free(fileNameList);
   }

   // XXX result too large for g_debug()

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsListDirectory


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListFiles --
 *
 *    This function is called to implement ListFilesInGuest VI Guest operation.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsListFiles(VixCommandRequestHeader *requestMsg,    // IN
                  size_t maxBufferSize,                   // IN
                  char **result)                          // OUT
{
   VixError err = VIX_OK;
   const char *dirPathName = NULL;
   char *fileList = NULL;
   char **fileNameList = NULL;
   size_t resultBufferSize = 0;
   size_t lastGoodResultBufferSize = 0;
   int numFiles = 0;
   int fileNum;
   char *currentFileName;
   char *destPtr;
   char *endDestPtr;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgListFilesRequest *listRequest = NULL;
   Bool truncated = FALSE;
   int offset = 0;
   Bool listingSingleFile = FALSE;
   const char *pattern = NULL;
   int index = 0;
   int maxResults = 0;
   int maxOffsetResults = 0;
   int count = 0;
   int remaining = 0;
   int numResults;
   GRegex *regex = NULL;
   char *pathName;
   VMAutomationRequestParser parser;

   ASSERT(NULL != requestMsg);

   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *listRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   listRequest = (VixMsgListFilesRequest *) requestMsg;

   /*
    * listRequest->offset is not part of the interface of ListFilesInGuest API,
    * listRequest->index and listRequest->maxResults are.
    *
    * When hostd sees the results from tools are truncated while requesting
    * maxResults number of file items, hostd issues another Vigor guest OP
    * ListFiles with same listRequest->index and listRequest->maxResults but
    * listRequest->offset set to the items number received. Hostd returns to
    * API client after seeing the results from tools are no longer truncated.
    *
    * listRequest->maxResults defaults to 50 in API spec. If a large number is
    * passed, truncation can happen when maxBufferSize is not big enough for
    * the results in one Vigor ListFiles call.
    *
    * maxBufferSize = GUESTMSG_MAX_IN_SIZE - vixPrefixDataSize
    *               = 64 * 1024 - 53
    */
   if (listRequest->offset >= (uint64)MAX_INT32) {
      g_warning("%s: Invalid offset value %"FMT64"u\n",
                __FUNCTION__, listRequest->offset);
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   offset = (int)listRequest->offset;
   index = listRequest->index;
   maxResults = listRequest->maxResults;
   /*
    * bora/vmx/automation/guestOps.c::GuestOpsListFiles() throws
    * VIX_E_INVALID_ARG if (index < 0) || (maxResults < 0) is TRUE.
    */
   ASSERT(offset >= 0 && index >= 0 && maxResults >= 0);

   /*
    * Do not fail the API if maxResults is 0, instead, return an empty file
    * list plus the remaining number of files.
    */
   if (maxResults > 0) {
      if (offset >= maxResults) {
         g_warning("%s: Invalid offset, offset is %d, maxResults is %d\n",
                   __FUNCTION__, offset, maxResults);
         err = VIX_E_INVALID_ARG;
         goto quit;
      }

      /*
       * This is the maximum number of results that can be returned
       * in this call.
       */
      maxOffsetResults = maxResults - offset;
   }

   err = VMAutomationRequestParserGetString(&parser,
                                            listRequest->guestPathNameLength,
                                            &dirPathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (listRequest->patternLength > 0) {
      err = VMAutomationRequestParserGetString(&parser,
                                               listRequest->patternLength,
                                               &pattern);
      if (VIX_OK != err) {
         goto quit;
      }

      g_debug("%s: pattern length is %d, value is '%s'\n",
              __FUNCTION__, listRequest->patternLength, pattern);
   }

   if (0 == *dirPathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s listing files in '%s' with pattern '%s' "
           "index %d, maxResults %d (offset %d)\n",
           __FUNCTION__, IMPERSONATED_USERNAME, dirPathName,
           (NULL != pattern) ? pattern : "",
           index, maxResults, (int) offset);

   if (pattern) {
      GError *gErr = NULL;
      regex = g_regex_new(pattern, 0, 0, &gErr);
      if (!regex) {
         g_warning("%s: bad regex pattern '%s' (%s);"
                   "failing with INVALID_ARG\n",
                   __FUNCTION__, pattern, gErr ? gErr->message : "");
         g_clear_error(&gErr);
         err = VIX_E_INVALID_ARG;
         goto quit;
      }
   }

   /*
    * First check for symlink -- File_IsDirectory() will lie
    * if its a symlink to a directory.
    */
   if (!File_IsSymLink(dirPathName) && File_IsDirectory(dirPathName)) {
      numFiles = File_ListDirectory(dirPathName, &fileNameList);
      if (numFiles < 0) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
      /*
       * File_ListDirectory() doesn't return '.' and '..', but we want them,
       * so add '.' and '..' to the list.  Place them in front since that's
       * a more normal location.
       */
      numFiles += 2;
      {
         char **newFileNameList = NULL;

         newFileNameList = Util_SafeMalloc(numFiles * sizeof(char *));
         newFileNameList[0] = Unicode_Alloc(".", STRING_ENCODING_UTF8);
         newFileNameList[1] = Unicode_Alloc("..", STRING_ENCODING_UTF8);
         memcpy(newFileNameList + 2, fileNameList, (numFiles - 2) * sizeof(char *));
         free(fileNameList);
         fileNameList = newFileNameList;
      }
   } else {
      if (File_Exists(dirPathName)) {
         listingSingleFile = TRUE;
         numFiles = 1;
         fileNameList = Util_SafeMalloc(sizeof(char *));
         fileNameList[0] = Util_SafeStrdup(dirPathName);
      } else {
         /*
          * We don't know what they intended to list, but we'll
          * assume file since that gives a fairly sane error.
          */
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
   }

   /*
    * Calculate the size of the result buffer and keep track of the
    * max number of entries we can store.  Also compute the number
    * we won't be returning (anything > maxResults).
    */
   resultBufferSize = 3; // truncation bool + space + '\0'
   // space for the 'remaining' tag up front
   resultBufferSize += strlen(listFilesRemainingFormatString) + 10;
   lastGoodResultBufferSize = resultBufferSize;
   ASSERT_NOT_IMPLEMENTED(lastGoodResultBufferSize < maxBufferSize);

   /*
    * If a regex pattern is specified, apply it first. The request index
    * and offset parameters are referring to the filtered entries.
    */
   if (regex) {
      int newNumFiles = 0;

      for (fileNum = 0; fileNum < numFiles; fileNum++) {
         currentFileName = fileNameList[fileNum];
         fileNameList[fileNum] = NULL;
         if (g_regex_match(regex, currentFileName, 0, NULL)) {
            fileNameList[newNumFiles++] = currentFileName;
         } else {
            free(currentFileName);
         }
      }

      numFiles = newNumFiles;
   }

   if (maxResults > 0) {
      for (fileNum = index + offset;
           fileNum < numFiles;
           fileNum++) {

         currentFileName = fileNameList[fileNum];

         if (listingSingleFile) {
            resultBufferSize += VixToolsGetFileExtendedInfoLength(
                                   currentFileName, currentFileName);
         } else {
            pathName = Str_SafeAsprintf(NULL, "%s%s%s", dirPathName, DIRSEPS,
                                        currentFileName);
            resultBufferSize += VixToolsGetFileExtendedInfoLength(
                                pathName, currentFileName);
            free(pathName);
         }

         if (resultBufferSize < maxBufferSize) {
            lastGoodResultBufferSize = resultBufferSize;
            count++;
            if (count == maxOffsetResults) {
               remaining = numFiles - fileNum - 1;
               break;
            }
         } else {
            truncated = TRUE;
            remaining = numFiles - fileNum;
            break;
         }
      }
      resultBufferSize = lastGoodResultBufferSize;
      numResults = count;
   } else {
      remaining = (index < numFiles) ? (numFiles - index) : 0;
      numResults = 0;
   }

   /*
    * Print the result buffer.
    */
   fileList = Util_SafeMalloc(resultBufferSize);
   destPtr = fileList;
   endDestPtr = fileList + resultBufferSize;

   /*
    * Indicate if we have a truncated buffer with "1 ", otherwise "0 ".
    * This should only happen for non-legacy requests.
    */
   if ((destPtr + 2) < endDestPtr) {
      *destPtr++ = truncated ? '1' : '0';
      *destPtr++ = ' ';
   } else {
      ASSERT(0);
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr,
                          listFilesRemainingFormatString, remaining);

   for (fileNum = index + offset, count = 0;
        count < numResults;
        fileNum++) {

      currentFileName = fileNameList[fileNum];

      if (listingSingleFile) {
         pathName = Util_SafeStrdup(currentFileName);
      } else {
         pathName = Str_SafeAsprintf(NULL, "%s%s%s", dirPathName, DIRSEPS,
                                     currentFileName);
      }

      /*
       * When File_GetSize(pathName) fails, the file is not printed.
       */
      VixToolsPrintFileExtendedInfo(pathName, currentFileName,
                                    &destPtr, endDestPtr);

      free(pathName);
      count++;
   }
   *destPtr = '\0';

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   if (NULL != regex) {
      g_regex_unref(regex);
   }

   if (NULL == fileList) {
      fileList = Util_SafeStrdup("");
   }
   *result = fileList;

   if (NULL != fileNameList) {
      for (fileNum = 0; fileNum < numFiles; fileNum++) {
         free(fileNameList[fileNum]);
      }
      free(fileNameList);
   }

   // XXX result too large for g_debug()

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsListFiles


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetFileExtendedInfoLength --
 *
 *    This function calculates the total number of bytes required to hold
 *    the extended info about the specified file.
 *
 * Return value:
 *    Size of extended info buffer.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

int
VixToolsGetFileExtendedInfoLength(const char *filePathName,   // IN
                                  const char *fileName)       // IN
{
   int fileExtendedInfoBufferSize = 0;

   ASSERT(NULL != filePathName);
   ASSERT(NULL != fileName);

#ifdef _WIN32
   fileExtendedInfoBufferSize = strlen(fileExtendedInfoWindowsFormatString);
#else
   fileExtendedInfoBufferSize = strlen(fileExtendedInfoLinuxFormatString);
#endif

   fileExtendedInfoBufferSize += 2; // DIRSEPC chars
   fileExtendedInfoBufferSize += 10 + 20 + (20 * 2); // properties + size + times
#ifdef _WIN32
   fileExtendedInfoBufferSize += 20;                // createTime
#else
   fileExtendedInfoBufferSize += 10 * 3;            // uid, gid, perms
#endif

#if defined(__linux__) || defined(sun) || defined(__FreeBSD__)
   if (File_IsSymLink(filePathName)) {
      char *symlinkTarget;
      symlinkTarget = Posix_ReadLink(filePathName);
      if (NULL != symlinkTarget) {
         fileExtendedInfoBufferSize +=
            VixToolsXMLStringEscapedLen(symlinkTarget, TRUE);
      }
      free(symlinkTarget);
   }
#endif

   fileExtendedInfoBufferSize += VixToolsXMLStringEscapedLen(fileName, TRUE);

   return fileExtendedInfoBufferSize;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetFileInfo --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsGetFileInfo(VixCommandRequestHeader *requestMsg,    // IN
                    char **result)                          // OUT
{
   VixError err = VIX_OK;
   char *resultBuffer = NULL;
   size_t resultBufferSize = 0;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   char *destPtr;
   const char *filePathName;
   VixMsgSimpleFileRequest *simpleFileReq;
   VMAutomationRequestParser parser;

   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *simpleFileReq);
   if (VIX_OK != err) {
      goto quit;
   }

   simpleFileReq = (VixMsgSimpleFileRequest *)requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            simpleFileReq->guestPathNameLength,
                                            &filePathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (0 == *filePathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s path: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME, filePathName);

   if (!(File_Exists(filePathName))) {
      err = VIX_E_FILE_NOT_FOUND;
      goto quit;
   }

   /*
    * Calculate the size of the result buffer.
    */
   resultBufferSize = strlen(fileInfoFormatString)
                           + 1 // strlen("")
                           + 20 + 20 + 10; // space for the modTime, size and flags.
   resultBuffer = Util_SafeMalloc(resultBufferSize);

   /*
    * Print the result buffer
    */
   destPtr = resultBuffer;
   VixToolsPrintFileInfo(filePathName, "", FALSE, &destPtr, resultBuffer + resultBufferSize);

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   if (NULL == resultBuffer) {
      resultBuffer = Util_SafeStrdup("");
   }
   *result = resultBuffer;

   guest_debug("%s: returning '%s'\n", __FUNCTION__, resultBuffer);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsGetFileInfo


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsSetFileAttributes --
 *
 *    Set the file attributes for a specified file.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsSetFileAttributes(VixCommandRequestHeader *requestMsg)    // IN
{
   VixError err = VIX_OK;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   const char *filePathName = NULL;
   VixMsgSetGuestFileAttributesRequest *setGuestFileAttributesRequest = NULL;
   struct timespec timeBuf;
   Bool success = FALSE;
   int64 createTime;
   int64 accessTime;
   int64 modificationTime;
   VMAutomationRequestParser parser;
   int64 tempTime;
   Bool timeAttributeSpecified = FALSE;
   Bool windowsAttributeSpecified = FALSE;
   int32 fileAttributeOptions = 0;

#ifdef _WIN32
   DWORD fileAttr = 0;
#else
   int ownerId = 0;
   int groupId = 0;
   struct stat statbuf;
#endif

   ASSERT(NULL != requestMsg);

   /*
    * Parse the argument
    */
   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg,
                                       sizeof *setGuestFileAttributesRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   setGuestFileAttributesRequest =
               (VixMsgSetGuestFileAttributesRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                            setGuestFileAttributesRequest->guestPathNameLength,
                                            &filePathName);
   if (VIX_OK != err) {
      goto quit;
   }

   if ('\0' == *filePathName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   fileAttributeOptions = setGuestFileAttributesRequest->fileOptions;

   if ((fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_HIDDEN) ||
       (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_READONLY)) {
      windowsAttributeSpecified = TRUE;
   }

   if ((fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_ACCESS_DATE) ||
       (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_MODIFY_DATE)) {
      timeAttributeSpecified = TRUE;
   }

#if defined(_WIN32)
   if ((fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_OWNERID) ||
       (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_GROUPID) ||
       (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_PERMISSIONS)) {
      g_warning("%s: Invalid attributes received for Windows Guest\n",
                __FUNCTION__);
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
#else
   if (windowsAttributeSpecified) {
      g_warning("%s: Invalid attributes received for Posix Guest\n",
                __FUNCTION__);
      err = VIX_E_INVALID_ARG;
      goto quit;
   }
#endif

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s path: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME, filePathName);

   if (!(File_Exists(filePathName))) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   if (timeAttributeSpecified) {
      success = File_GetTimes(filePathName,
                              &createTime,
                              &accessTime,
                              &modificationTime,
                              &tempTime);

      if (!success) {
         g_warning("%s: Failed to get the times.\n", __FUNCTION__);
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }

      /*
       * User specifies the time in Unix Time Format. File_SetTimes()
       * accepts times in Windows NT Format. We should convert the time
       * from Unix Format to Windows NT Format.
       */

      if (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_ACCESS_DATE ) {
         timeBuf.tv_sec  = setGuestFileAttributesRequest->accessTime;
         timeBuf.tv_nsec = 0;
         accessTime      = TimeUtil_UnixTimeToNtTime(timeBuf);
      }

      if (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_MODIFY_DATE) {
         timeBuf.tv_sec    = setGuestFileAttributesRequest->modificationTime;
         timeBuf.tv_nsec   = 0;
         modificationTime  = TimeUtil_UnixTimeToNtTime(timeBuf);
      }

      success = File_SetTimes(filePathName,
                              createTime,
                              accessTime,
                              modificationTime,
                              modificationTime);
      if (!success) {
         g_warning("%s: Failed to set the times.\n", __FUNCTION__);
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
   }
#if defined(_WIN32)
   if (windowsAttributeSpecified) {
      fileAttr = Win32U_GetFileAttributes(filePathName);

      if (fileAttr != INVALID_FILE_ATTRIBUTES) {
         if (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_HIDDEN) {
            if (setGuestFileAttributesRequest->hidden) {
               fileAttr |= FILE_ATTRIBUTE_HIDDEN;
            } else {
               fileAttr &= (~FILE_ATTRIBUTE_HIDDEN);
            }
         }

         if (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_READONLY) {
            if (setGuestFileAttributesRequest->readOnly) {
               fileAttr |= FILE_ATTRIBUTE_READONLY;
            } else {
               fileAttr &= (~FILE_ATTRIBUTE_READONLY);
            }
         }

         if (!Win32U_SetFileAttributes(filePathName, fileAttr)) {
            err = FoundryToolsDaemon_TranslateSystemErr();
            g_warning("%s: Failed to set the file attributes\n", __FUNCTION__);
            goto quit;
         }
      } else {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("%s: Failed to get the file attributes\n", __FUNCTION__);
         goto quit;
      }
   }
#else
   if ((fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_OWNERID) ||
       (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_GROUPID)) {

      if (-1 != Posix_Stat(filePathName, &statbuf)) {
         ownerId = statbuf.st_uid;
         groupId = statbuf.st_gid;
      } else {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("%s: Posix_Stat(%s) failed with %d\n",
                   __FUNCTION__, filePathName, errno);
         goto quit;
      }

      if (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_OWNERID) {
         ownerId = setGuestFileAttributesRequest->ownerId;
      }

      if (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_GROUPID) {
         groupId = setGuestFileAttributesRequest->groupId;
      }

      if (Posix_Chown(filePathName, ownerId, groupId)) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("%s: Failed to set the owner/group Id\n", __FUNCTION__);
         goto quit;
      }
   }

   /*
    * NOTE: Setting ownership clears SUID and SGID bits, therefore set the
    * file permissions after setting ownership.
    */
   if (fileAttributeOptions & VIX_FILE_ATTRIBUTE_SET_UNIX_PERMISSIONS) {
      success = File_SetFilePermissions(filePathName,
                                        setGuestFileAttributesRequest->permissions);
      if (!success) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("%s: Failed to set the file permissions\n", __FUNCTION__);
         goto quit;
      }
   }
#endif

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsSetGuestFileAttributes


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsPrintFileInfo --
 *
 *    This does not retrieve some of the more interesting properties, like
 *    read-only, owner name, and permissions. I'll add those later.
 *
 *    This also does not yet provide UTF-8 versions of some of the File_ functions,
 *    so that may create problems on international guests.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
VixToolsPrintFileInfo(const char *filePathName,     // IN
                      char *fileName,               // IN
                      Bool escapeStrs,              // IN
                      char **destPtr,               // IN/OUT
                      char *endDestPtr)             // IN
{
   int64 fileSize = 0;
   int64 modTime;
   int32 fileProperties = 0;
   char *escapedFileName = NULL;

   modTime = File_GetModTime(filePathName);
   if (File_IsDirectory(filePathName)) {
      fileProperties |= VIX_FILE_ATTRIBUTES_DIRECTORY;
   } else {
      if (File_IsSymLink(filePathName)) {
         fileProperties |= VIX_FILE_ATTRIBUTES_SYMLINK;
      }
      if (File_IsFile(filePathName)) {
         fileSize = File_GetSize(filePathName);
      }
   }

   if (escapeStrs) {
      fileName = escapedFileName = VixToolsEscapeXMLString(fileName);
      ASSERT_MEM_ALLOC(NULL != escapedFileName);
   }

   *destPtr += Str_Sprintf(*destPtr,
                           endDestPtr - *destPtr,
                           fileInfoFormatString,
                           fileName,
                           fileProperties,
                           fileSize,
                           modTime);
   free(escapedFileName);
} // VixToolsPrintFileInfo


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsPrintFileExtendedInfo --
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
VixToolsPrintFileExtendedInfo(const char *filePathName,     // IN
                              const char *fileName,         // IN
                              char **destPtr,               // IN/OUT
                              char *endDestPtr)             // IN
{
   int64 fileSize = 0;
   VmTimeType modTime = 0;
   VmTimeType accessTime = 0;
   int32 fileProperties = 0;
#ifdef _WIN32
   DWORD fileAttr = 0;
   Bool hidden = FALSE;
   Bool readOnly = FALSE;
   VmTimeType createTime = 0;
#else
   int permissions = 0;
   int ownerId = 0;
   int groupId = 0;
   char *symlinkTarget = NULL;
   char *tmp;
#endif
   struct stat statbuf;
   char *escapedFileName = NULL;

   /*
    * First check for symlink -- File_IsDirectory() will lie
    * if its a symlink to a directory.
    */
   if (File_IsSymLink(filePathName)) {
      fileProperties |= VIX_FILE_ATTRIBUTES_SYMLINK;
   } else if (File_IsDirectory(filePathName)) {
      fileProperties |= VIX_FILE_ATTRIBUTES_DIRECTORY;
   } else if (File_IsFile(filePathName)) {
      /*
       * File_GetSize fails and returns -1 when
       *  - the file does not exist any more
       *  - the caller has lost permission to access the file
       *  - the file is exclusively locked at the moment
       *
       * The above could happen as a race condition while guest OP
       * is in progress.
       */
#if defined(VMX86_DEBUG)
      gchar *failThisFile;
      failThisFile = VMTools_ConfigGetString(gConfDictRef,
                                             VIX_TOOLS_CONFIG_API_GROUPNAME,
                                             "failThisFileGetSize",
                                             NULL);
      if (g_strcmp0(failThisFile, filePathName) == 0) {
         g_info("%s: Fail this File_GetSize(%s)...\n",
                __FUNCTION__, filePathName);
         fileSize = -1;
      } else {
         fileSize = File_GetSize(filePathName);
      }
      g_free(failThisFile);
#else
      fileSize = File_GetSize(filePathName);
#endif
      if (fileSize < 0) {
         g_warning("%s: File_GetSize(%s) returned %"FMT64"d\n",
                   __FUNCTION__, filePathName, fileSize);
         /*
          * Special handling: skip this file item when File_GetSize fails.
          */
         return;
      }
   }

#if !defined(_WIN32)
   /*
    * If the file is a symlink, figure out where it points.
    */
   if (fileProperties & VIX_FILE_ATTRIBUTES_SYMLINK) {
      symlinkTarget = Posix_ReadLink(filePathName);
   }

   /*
    * Have a nice empty value if it's not a link or there's some error
    * reading the link.
    */
   if (NULL == symlinkTarget) {
      symlinkTarget = Util_SafeStrdup("");
   }

   tmp = VixToolsEscapeXMLString(symlinkTarget);
   ASSERT_MEM_ALLOC(NULL != tmp);
   free(symlinkTarget);
   symlinkTarget = tmp;
#endif

#ifdef _WIN32
   fileAttr = Win32U_GetFileAttributes(filePathName);
   if (fileAttr != INVALID_FILE_ATTRIBUTES) {
      if (fileAttr & FILE_ATTRIBUTE_HIDDEN) {
         fileProperties |= VIX_FILE_ATTRIBUTES_HIDDEN;
      }
      if (fileAttr & FILE_ATTRIBUTE_READONLY) {
         fileProperties |= VIX_FILE_ATTRIBUTES_READONLY;
      }
   }
#endif

   if (Posix_Stat(filePathName, &statbuf) != -1) {
#if !defined(_WIN32)
      ownerId = statbuf.st_uid;
      groupId = statbuf.st_gid;
      permissions = statbuf.st_mode;
#endif
      /*
       * We want create time.  ctime is the inode change time for Linux,
       * so we can't report anything.
       */
#ifdef _WIN32
      createTime = statbuf.st_ctime;
#endif
      modTime = statbuf.st_mtime;
      accessTime = statbuf.st_atime;
   } else {
      g_warning("%s: Posix_Stat(%s) failed with %d\n",
                __FUNCTION__, filePathName, errno);
   }

   escapedFileName = VixToolsEscapeXMLString(fileName);
   ASSERT_MEM_ALLOC(NULL != escapedFileName);

#ifdef _WIN32
   *destPtr += Str_Sprintf(*destPtr,
                           endDestPtr - *destPtr,
                           fileExtendedInfoWindowsFormatString,
                           escapedFileName,
                           fileProperties,
                           fileSize,
                           modTime,
                           createTime,
                           accessTime,
                           hidden,
                           readOnly);
#else
   *destPtr += Str_Sprintf(*destPtr,
                           endDestPtr - *destPtr,
                           fileExtendedInfoLinuxFormatString,
                           escapedFileName,
                           fileProperties,
                           fileSize,
                           modTime,
                           accessTime,
                           ownerId,
                           groupId,
                           permissions,
                           symlinkTarget);
   free(symlinkTarget);
#endif
   free(escapedFileName);
} // VixToolsPrintFileExtendedInfo


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsPrintFileExtendedInfoEx --
 *
 *    Given a specified file, this function returns a properly XML
 *    formatted string representing the extended information of the file.
 *
 * Return value:
 *    char * - Dynamically allocated string that holds the extended info
 *    about the specified file. It is the responsibility of the caller
 *    to free the memory.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static char *
VixToolsPrintFileExtendedInfoEx(const char *filePathName,          // IN
                                const char *fileName)              // IN
{
   int resultBufferSize;
   char *destPtr = NULL;
   char *endDestPtr = NULL;
   char *resultBuffer = NULL;

   resultBufferSize = VixToolsGetFileExtendedInfoLength(filePathName,
                                                        fileName);
   resultBuffer = Util_SafeMalloc(resultBufferSize);
   destPtr = resultBuffer;
   endDestPtr = resultBuffer + resultBufferSize;

   VixToolsPrintFileExtendedInfo(filePathName, filePathName, &destPtr,
                                 endDestPtr);

   *destPtr = '\0';
   return resultBuffer;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsCheckUserAccount --
 *
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsCheckUserAccount(VixCommandRequestHeader *requestMsg) // IN
{
   VixError err = VIX_OK;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   return err;
} // VixToolsCheckUserAccount


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsRunScript --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsRunScript(VixCommandRequestHeader *requestMsg,  // IN
                  char *requestName,                    // IN
                  void *eventQueue,                     // IN
                  char **result)                        // OUT
{
   VixError err = VIX_OK;
   const char *propertiesString = NULL;
   const char *script = NULL;
   const char *interpreterName = NULL;
   char *fileSuffix = "";
   Bool impersonatingVMWareUser = FALSE;
   VixToolsRunProgramState *asyncState = NULL;
   void *userToken = NULL;
   char *tempDirPath = NULL;
   char *tempScriptFilePath = NULL;
   char *fullCommandLine = NULL;
   int var;
   int fd = -1;
   int writeResult;
   Bool programExists;
   Bool programIsExecutable;
   int64 pid = (int64) -1;
   static char resultBuffer[32];
   VixMsgRunScriptRequest *scriptRequest;
   const char *interpreterFlags = "";
   ProcMgr_ProcArgs procArgs;
#if defined(_WIN32)
   Bool forcedRoot = FALSE;
   wchar_t *envBlock = NULL;
#endif
   GSource *timer;
   VMAutomationRequestParser parser;

   err = VMAutomationRequestParserInit(&parser,
                                       requestMsg, sizeof *scriptRequest);
   if (VIX_OK != err) {
      goto quit;
   }

   scriptRequest = (VixMsgRunScriptRequest *) requestMsg;

   err = VMAutomationRequestParserGetString(&parser,
                                          scriptRequest->interpreterNameLength,
                                            &interpreterName);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VMAutomationRequestParserGetString(&parser,
                                            scriptRequest->propertiesLength,
                                            &propertiesString);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VMAutomationRequestParserGetString(&parser,
                                            scriptRequest->scriptLength,
                                            &script);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);


   if (0 == *interpreterName) {
#ifdef _WIN32
      //interpreterName = "cmd.exe";
      fileSuffix = ".bat";
#else
      interpreterName = "/bin/sh";
#endif
   }

   if (*interpreterName) {
      programExists = File_Exists(interpreterName);

      /*
       * TODO: replace FileIO_Access with something more UTF8/forward-
       * thinking.
       */

      programIsExecutable =
         (FileIO_Access(interpreterName, FILEIO_ACCESS_EXEC) ==
                                                   FILEIO_SUCCESS);
      if (!programExists) {
         err = VIX_E_FILE_NOT_FOUND;
         goto quit;
      }
      if (!programIsExecutable) {
         err = VIX_E_GUEST_USER_PERMISSIONS;
         goto quit;
      }
   }

   /*
    * Create a temporary file that we can run as a script.
    * TODO: Plumb a file suffix/extention throught to the File
    * module's code, so that we can avoid duplicating this code.
    */

#ifdef _WIN32
   if (PROCESS_CREATOR_USER_TOKEN != userToken) {
      err = VixToolsGetUserTmpDir(userToken, &tempDirPath);

      /*
       * Don't give up if VixToolsGetUserTmpDir() failed. It might just
       * have failed to load DLLs, so we might be running on Win 9x.
       * Just fall through to use the old fashioned File_GetSafeRandomTmpDir().
       */

      err = VIX_OK;
   }
#endif

   if (NULL == tempDirPath) {
      tempDirPath = File_GetSafeRandomTmpDir(TRUE);

      if (NULL == tempDirPath) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }
   }
   for (var = 0; var < MAX_INT32; var++) {
      free(tempScriptFilePath);
      tempScriptFilePath = Str_SafeAsprintf(NULL,
                                            "%s"DIRSEPS"%s%d%s",
                                            tempDirPath,
                                            scriptFileBaseName,
                                            var,
                                            fileSuffix);
      if (NULL == tempScriptFilePath) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }

      fd = Posix_Open(tempScriptFilePath, // UTF-8
                      O_CREAT | O_EXCL
#if defined(_WIN32)
                     | O_BINARY
#endif
                     | O_RDWR,
                      0600);
      if (fd >= 0) {
         break;
      }
#if defined(_WIN32)
      if ((errno == EACCES) && (File_Exists(tempScriptFilePath))) {
         /*
          * On windows, Posix_Open() fails with EACCES if there is any
          * permissions check failure while creating the file. Also, EACCES is
          * returned if a directory already exists with the same name. In such
          * case, we need to check if a file already exists and ignore
          * EACCES error.
          */
         continue;
      }
#endif
      if (errno != EEXIST) {
         /*
          * While persistence is generally a worthwhile trail, if something
          * happens to the temp directory while we're using it (e.g., someone
          * deletes it), we should not try 4+ billion times.
          */
         break;
      }
   }
   if (fd < 0) {
      /*
       * We use Posix variant function i.e. Posix_Open to create a
       * temporary file. If Posix_Open() fails, then proper error is
       * stuffed in errno variable. So, use Vix_TranslateErrno()
       * to translate the errno to a proper foundry error.
       */
      err = Vix_TranslateErrno(errno);
      g_warning("%s: Unable to create a temporary file, errno is %d.\n",
                __FUNCTION__, errno);
      goto quit;
   }

#if defined(_WIN32)
   writeResult = _write(fd, script, (unsigned int)strlen(script));
#else
   writeResult = write(fd, script, strlen(script));
#endif

   if (writeResult < 0) {
      /*
       * Yes, I'm duplicating code by running this check before the call to
       * close(), but if close() succeeds it will clobber the errno, causing
       * something confusing to be reported to the user.
       */
      err = Vix_TranslateErrno(errno);
      g_warning("%s: Unable to write the script to the temporary file, errno is %d.\n",
                __FUNCTION__, errno);
      if (close(fd) < 0) {
         g_warning("%s: Unable to close a file, errno is %d\n",
                   __FUNCTION__, errno);
      }
      goto quit;
   }

   if (close(fd) < 0) {
      /*
       * If close() fails, we don't want to try to run the script. According to
       * the man page:
       *    "Not checking the return value of close is a common but nevertheless
       *     serious programming error.  It is quite possible that errors on a
       *     previous write(2) operation  are first reported at the final close. Not
       *     checking the return value when closing the file may lead to silent loss
       *     of data.  This can especially be observed with NFS and disk quotas."
       */
      err = Vix_TranslateErrno(errno);
      g_warning("%s: Unable to close a file, errno is %d\n",
                __FUNCTION__, errno);
      goto quit;
   }

   if (*interpreterName) {
      fullCommandLine = Str_SafeAsprintf(NULL, // resulting string length
                                     "\"%s\" %s \"%s\"",
                                     interpreterName,
                                     interpreterFlags,
                                     tempScriptFilePath);
   } else {
      fullCommandLine = Str_SafeAsprintf(NULL,  // resulting string length
                                     "\"%s\"",
                                     tempScriptFilePath);
   }

   if (NULL == fullCommandLine) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   /*
    * Save some strings in the state.
    */
   asyncState = Util_SafeCalloc(1, sizeof *asyncState);
   asyncState->tempScriptFilePath = tempScriptFilePath;
   tempScriptFilePath = NULL;
   asyncState->requestName = Util_SafeStrdup(requestName);
   asyncState->runProgramOptions = scriptRequest->scriptOptions;

   memset(&procArgs, 0, sizeof procArgs);
#if defined(_WIN32)
   if (PROCESS_CREATOR_USER_TOKEN != userToken) {
      /*
       * If we are impersonating a user then use the user's environment
       * block. That way the user-specific environment variables will
       * be available to the application (such as the user's TEMP
       * directory instead of the system-wide one).
       */
      err = VixToolsGetEnvBlock(userToken, &envBlock);
      if (VIX_OK != err) {
         goto quit;
      }

      forcedRoot = Impersonate_ForceRoot();
   }
   procArgs.hToken = (PROCESS_CREATOR_USER_TOKEN == userToken) ? NULL : userToken;
   procArgs.bInheritHandles = TRUE;
   procArgs.dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
   procArgs.lpEnvironment = envBlock;
#else
   procArgs.envp = VixToolsEnvironmentTableToEnvp(userEnvironmentTable);
#endif

   asyncState->procState = ProcMgr_ExecAsync(fullCommandLine, &procArgs);

#if defined(_WIN32)
   if (forcedRoot) {
      Impersonate_UnforceRoot();
   }
#else
   VixToolsFreeEnvp(procArgs.envp);
   DEBUG_ONLY(procArgs.envp = NULL;)
#endif

   if (NULL == asyncState->procState) {
      err = VIX_E_PROGRAM_NOT_STARTED;
      goto quit;
   }

   pid = (int64) ProcMgr_GetPid(asyncState->procState);

   asyncState->eventQueue = eventQueue;
   timer = g_timeout_source_new(SECONDS_BETWEEN_POLL_TEST_FINISHED * 1000);
   g_source_set_callback(timer, VixToolsMonitorAsyncProc, asyncState, NULL);
   g_source_attach(timer, g_main_loop_get_context(eventQueue));
   g_source_unref(timer);

   /*
    * VixToolsMonitorAsyncProc will clean asyncState up when the program finishes.
    */
   asyncState = NULL;

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   if (VIX_FAILED(err)) {
      VixToolsFreeRunProgramState(asyncState);
   }

#ifdef _WIN32
   if (NULL != envBlock) {
      VixToolsDestroyEnvironmentBlock(envBlock);
   }
#endif

   free(fullCommandLine);
   free(tempDirPath);
   free(tempScriptFilePath);

   Str_Sprintf(resultBuffer, sizeof(resultBuffer), "%"FMT64"d", pid);
   *result = resultBuffer;

   guest_debug("%s: returning '%s'\n", __FUNCTION__, resultBuffer);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
} // VixToolsRunScript


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsImpersonateUser --
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsImpersonateUser(VixCommandRequestHeader *requestMsg,   // IN
                        Bool loadUserProfile,                  // IN
                        void **userToken)                      // OUT
{
   VixError err = VIX_OK;
   char *credentialField;
   int credentialType;

   credentialField = ((char *) requestMsg)
                           + requestMsg->commonHeader.headerLength
                           + requestMsg->commonHeader.bodyLength;

   credentialType = requestMsg->userCredentialType;

   switch (credentialType) {
   case VIX_USER_CREDENTIAL_TICKETED_SESSION:
   {
      VixCommandTicketedSession *commandTicketedSession = (VixCommandTicketedSession *) credentialField;
      size_t ticketLength = commandTicketedSession->ticketLength;

      credentialField += sizeof(VixCommandTicketedSession);

      if (ticketLength != strlen(credentialField)) {
         g_warning("%s: Ticket Length Does Not Match Expected\n", __FUNCTION__);
         err = VIX_E_INVALID_MESSAGE_BODY;
         goto done;
      }

      err = VixToolsImpersonateUserImplEx(NULL,
                                          credentialType,
                                          credentialField,
                                          loadUserProfile,
                                          userToken);
      break;
   }
   case VIX_USER_CREDENTIAL_NAME_PASSWORD:
   case VIX_USER_CREDENTIAL_NAME_PASSWORD_OBFUSCATED:
   case VIX_USER_CREDENTIAL_NAMED_INTERACTIVE_USER:
   {
      VixCommandNamePassword *namePasswordStruct =
         (VixCommandNamePassword *) credentialField;
      credentialField += sizeof(*namePasswordStruct);

      err = VixToolsImpersonateUserImplEx(NULL,
                                          credentialType,
                                          credentialField,
                                          loadUserProfile,
                                          userToken);
      if ((VIX_OK != err)
          && ((VIX_USER_CREDENTIAL_NAME_PASSWORD_OBFUSCATED == credentialType)
              || (VIX_USER_CREDENTIAL_NAME_PASSWORD == credentialType))) {
         /*
          * Windows does not allow you to login with an empty password. Only
          * the console allows this login, which means the console does not
          * call the simple public LogonUser api.
          *
          * See the description for ERROR_ACCOUNT_RESTRICTION.
          * For example, the error codes are described here:
          *      http://support.microsoft.com/kb/155012
          */
#ifdef _WIN32
         if (namePasswordStruct->passwordLength <= 0) {
            err = VIX_E_EMPTY_PASSWORD_NOT_ALLOWED_IN_GUEST;
         }
#endif
      }
      break;
   }
#if SUPPORT_VGAUTH
   case VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN:
   case VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN_HOST_VERIFIED:
   {
      VixCommandSAMLToken *samlStruct =
         (VixCommandSAMLToken *) credentialField;
      credentialField += sizeof(*samlStruct);

      err = VixToolsImpersonateUserImplEx(NULL,
                                          credentialType,
                                          credentialField,
                                          loadUserProfile,
                                          userToken);
      break;
   }
#endif
   case VIX_USER_CREDENTIAL_SSPI:
      /*
       * SSPI currently only supported in ticketed sessions
       */
   default:
      g_warning("%s: Unsupported credentialType = %d\n",
                __FUNCTION__, credentialType);
      err = VIX_E_NOT_SUPPORTED;
   }

done:
   if (err != VIX_OK) {
      g_warning("%s: impersonation failed (%"FMT64"d)\n",
                  __FUNCTION__, err);
   } else {
      g_debug("%s: successfully impersonated user %s\n", __FUNCTION__,
              IMPERSONATED_USERNAME);
   }

   return(err);
} // VixToolsImpersonateUser


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsImpersonateUserImpl --
 *
 *    Little compatability wrapper for legacy Foundry Tools implementations.
 *
 * Return value:
 *    TRUE on success
 *    FALSE on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
VixToolsImpersonateUserImpl(char const *credentialTypeStr,         // IN
                            int credentialType,                    // IN
                            char const *obfuscatedNamePassword,    // IN
                            void **userToken)                      // OUT
{
   return(VIX_OK == VixToolsImpersonateUserImplEx(credentialTypeStr,
                                                  credentialType,
                                                  obfuscatedNamePassword,
                                                  TRUE,
                                                  userToken));
} // VixToolsImpersonateUserImpl


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsImpersonateUserImplEx --
 *
 *   On Windows:
 *   To retrieve the security context of another user
 *   call LogonUser to log the user whom you want to impersonate on to the
 *   local computer, specifying the name of the user account, the user's
 *   domain, and the user's password. This function returns a pointer to
 *   a handle to the access token of the logged-on user as an out parameter.
 *   Call ImpersonateLoggedOnUser using the handle to the access token obtained
 *   in the call to LogonUser.
 *   Run RegEdt32 to load the registry hive of the impersonated user manually.
 *
 * Return value:
 *    VIX_OK on success, or an appropriate error code on failure.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsImpersonateUserImplEx(char const *credentialTypeStr,         // IN
                              int credentialType,                    // IN
                              char const *obfuscatedNamePassword,    // IN
                              Bool loadUserProfile,                  // IN
                              void **userToken)                      // OUT
{
   VixError err = VIX_E_INVALID_LOGIN_CREDENTIALS;

   if (NULL == userToken) {
      g_warning("%s: Invalid userToken pointer\n", __FUNCTION__);
      return VIX_E_FAIL;
   }

   *userToken = NULL;

///////////////////////////////////////////////////////////////////////
// NOTE: The following lines need to be uncommented to disable either
// FreeBSD and/or MacOS support for VMODL Guest Operations completely.
//#if defined(__FreeBSD__)
//   return VIX_E_NOT_SUPPORTED;
//#endif
//#if defined(__APPLE__)
//   return VIX_E_NOT_SUPPORTED;
//#endif
///////////////////////////////////////////////////////////////////////
   {
      AuthToken authToken;
      char *unobfuscatedUserName = NULL;
      char *unobfuscatedPassword = NULL;
      Bool success = FALSE;

      if (NULL != credentialTypeStr) {
         if (!StrUtil_StrToInt(&credentialType, credentialTypeStr)) {
            /*
             * This is an internal error, since the VMX supplies this string.
             */
            err = VIX_E_FAIL;
            goto quit;
         }
      }

      /*
       * If the VMX asks us to run commands in the context of the current
       * user, make sure that the user who requested the command is the
       * same as the current user.
       * We don't need to make sure the password is valid (in fact we should
       * not receive one) because the VMX should have validated the
       * password by other means. Currently it sends it to the Tools daemon.
       */
      if (VIX_USER_CREDENTIAL_NAMED_INTERACTIVE_USER == credentialType) {
         if (!thisProcessRunsAsRoot) {
            err = VixMsg_DeObfuscateNamePassword(obfuscatedNamePassword,
                                                 &unobfuscatedUserName,
                                                 &unobfuscatedPassword);
            if (err != VIX_OK) {
               goto quit;
            }

            /*
             * Make sure that the user who requested the command is the
             * current user.
             */

            err = VixToolsDoesUsernameMatchCurrentUser(unobfuscatedUserName);
            if (VIX_OK != err) {
               goto quit;
            }

            *userToken = PROCESS_CREATOR_USER_TOKEN;
            gImpersonatedUsername = Util_SafeStrdup(unobfuscatedUserName);

            g_debug("%s: allowing interactive mode for user '%s'\n",
                    __FUNCTION__, gImpersonatedUsername);

            goto quit;
         } else {
            /*
             * This should only be sent to vmware-user, not guestd.
             * Something is wrong.
             */
            ASSERT(0);
            err = VIX_E_FAIL;
            goto quit;
         }
      }


      /*
       * Use the GuestAuth library to do name-password authentication
       * and impersonation.
       */

      if (GuestAuthEnabled() &&
          (VIX_USER_CREDENTIAL_NAME_PASSWORD == credentialType ||
           VIX_USER_CREDENTIAL_NAME_PASSWORD_OBFUSCATED == credentialType)) {
         err =
            GuestAuthPasswordAuthenticateImpersonate(obfuscatedNamePassword,
                                                     loadUserProfile,
                                                     userToken);
      }

#if SUPPORT_VGAUTH
      else if ((VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN == credentialType)
         || (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN_HOST_VERIFIED == credentialType)
         ) {
         if (GuestAuthEnabled()) {
            Bool hostVerified =
               (credentialType == VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN_HOST_VERIFIED)
               ? TRUE : FALSE;
            err = GuestAuthSAMLAuthenticateAndImpersonate(obfuscatedNamePassword,
                                                          loadUserProfile,
                                                          hostVerified,
                                                          userToken);
         } else {
            err = VIX_E_NOT_SUPPORTED;
         }
      }
#endif

      /* Get the authToken and impersonate */
      else if (VIX_USER_CREDENTIAL_TICKETED_SESSION == credentialType) {
#ifdef _WIN32
         char *username;

         err = VixToolsGetTokenHandleFromTicketID(obfuscatedNamePassword,
                                                  &username,
                                                  &authToken);

         if (VIX_OK != err) {
            goto quit;
         }

         unobfuscatedUserName = Util_SafeStrdup(username);
         *userToken = (void *) authToken;
         success = Impersonate_Do(unobfuscatedUserName, authToken);
         if (!success) {
            err = VIX_E_INVALID_LOGIN_CREDENTIALS;
            goto quit;
         }

         gImpersonatedUsername = Util_SafeStrdup(username);
         err = VIX_OK;
#else
         err = VIX_E_NOT_SUPPORTED;
#endif
      } else if (VIX_USER_CREDENTIAL_NAME_PASSWORD == credentialType ||
                 VIX_USER_CREDENTIAL_NAME_PASSWORD_OBFUSCATED == credentialType) {

         /*
          * Other credential types, like guest, are all turned into
          * a name/password by the VMX.
          */

         err = VixMsg_DeObfuscateNamePassword(obfuscatedNamePassword,
                                              &unobfuscatedUserName,
                                              &unobfuscatedPassword);
         if (err != VIX_OK) {
            goto quit;
         }

         authToken = Auth_AuthenticateUser(unobfuscatedUserName,
                                           unobfuscatedPassword);
         if (NULL == authToken) {
            err = VIX_E_INVALID_LOGIN_CREDENTIALS;
            goto quit;
         }

         *userToken = (void *) authToken;
#ifdef _WIN32
         success = Impersonate_Do(unobfuscatedUserName, authToken);
#else
         /*
          * Use a tools-special version of user impersonation, since
          * lib/impersonate model isn't quite what we want on linux.
          */
         success = ProcMgr_ImpersonateUserStart(unobfuscatedUserName,
                                                authToken);
#endif
         if (!success) {
            err = VIX_E_INVALID_LOGIN_CREDENTIALS;
            goto quit;
         }

         gImpersonatedUsername = Util_SafeStrdup(unobfuscatedUserName);
         err = VIX_OK;
      } else {
         /*
          * If this is something else, then we are talking to a newer
          * version of the VMX.
          */
         err = VIX_E_NOT_SUPPORTED;
      }

quit:
      free(unobfuscatedUserName);
      Util_ZeroFreeString(unobfuscatedPassword);
   }

   return err;
} // VixToolsImpersonateUserImplEx


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsUnimpersonateUser --
 *
 *
 * Return value:
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixToolsUnimpersonateUser(void *userToken)
{
   free(gImpersonatedUsername);
   gImpersonatedUsername = NULL;

#if SUPPORT_VGAUTH
#if ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS
   if (PROCESS_CREATOR_USER_TOKEN == userToken) {
      g_debug("%s: Faking unimpersonate\n", __FUNCTION__);
   }
#endif
   if (NULL != currentUserHandle) {
      GuestAuthUnimpersonate();
      return;
   }
#endif
   if (PROCESS_CREATOR_USER_TOKEN != userToken) {
#if defined(_WIN32)
      Impersonate_Undo();
#else
      ProcMgr_ImpersonateUserStop();
#endif
   }
} // VixToolsUnimpersonateUser


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsLogoutUser --
 *
 *
 * Return value:
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixToolsLogoutUser(void *userToken)    // IN
{
   if (PROCESS_CREATOR_USER_TOKEN == userToken) {
      return;
   }

#if SUPPORT_VGAUTH
   if (NULL != currentUserHandle) {
#ifdef _WIN32
      // close the handle we copied out
      CloseHandle((HANDLE) userToken);
#endif
      VGAuth_UserHandleFree(currentUserHandle);
      currentUserHandle = NULL;
      return;
   }
#endif

   if (NULL != userToken) {
      AuthToken authToken = (AuthToken) userToken;
      Auth_CloseToken(authToken);
   }
} // VixToolsLogoutUser


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetImpersonatedUsername --
 *
 * Return value:
 *    The name of the user currently being impersonated.  Must be freed
 *    by caller.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static char *
VixToolsGetImpersonatedUsername(void *userToken)
{
   char *userName = NULL;
   char *homeDir = NULL;

#if SUPPORT_VGAUTH
   if (NULL != currentUserHandle) {
      VGAuthContext *ctx;
      VGAuthError vgErr = TheVGAuthContext(&ctx);
      ASSERT(vgErr == VGAUTH_E_OK);

      vgErr = VGAuth_UserHandleUsername(ctx, currentUserHandle, &userName);
      if (VGAUTH_FAILED(vgErr)) {
         g_warning("%s: Unable to get username from userhandle %p\n",
                   __FUNCTION__, currentUserHandle);
      }
      return userName;
   }
#endif

   if (!ProcMgr_GetImpersonatedUserInfo(&userName, &homeDir)) {
      return Util_SafeStrdup("XXX failed to get username XXX");
   }
   free(homeDir);

   return userName;
} // VixToolsUnimpersonateUser


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsFreeRunProgramState --
 *
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixToolsFreeRunProgramState(VixToolsRunProgramState *asyncState) // IN
{
   if (NULL == asyncState) {
      return;
   }

   if (NULL != asyncState->tempScriptFilePath) {
      /*
       * Use UnlinkNoFollow() since we created the file and we know it is not
       * a symbolic link.
       */
      File_UnlinkNoFollow(asyncState->tempScriptFilePath);
   }
   if (NULL != asyncState->procState) {
      ProcMgr_Free(asyncState->procState);
   }

   free(asyncState->requestName);
   free(asyncState->tempScriptFilePath);
   free(asyncState);
} // VixToolsFreeRunProgramState


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsFreeStartProgramState --
 *
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
VixToolsFreeStartProgramState(VixToolsStartProgramState *asyncState) // IN
{
   if (NULL == asyncState) {
      return;
   }
#if defined(_WIN32) && SUPPORT_VGAUTH
   /*
    * Unload the user profile if saved.
    */
   if (asyncState->hProfile != INVALID_HANDLE_VALUE &&
       asyncState->hToken != INVALID_HANDLE_VALUE) {
      GuestAuthUnloadUserProfileAndToken(asyncState->hToken,
                                         asyncState->hProfile);
   }
#endif

   free(asyncState);
} // VixToolsFreeStartProgramState


/*
 *----------------------------------------------------------------------------
 *
 *  VixToolsGetTempFileCreateNameFunc --
 *
 *       This function is designed as part of implementing CreateTempFile,
 *       CreateTempDirectory VI guest operations.
 *
 *       This function will be passed to File_MakeTempEx2 when
 *       VixToolsGetTempFile() is called.
 *
 *  Return Value:
 *       If success, a dynamically allocated string with the base name of
 *       of the file. NULL otherwise.
 *
 *  Side effects:
 *        None.
 *
 *----------------------------------------------------------------------------
 */

static char *
VixToolsGetTempFileCreateNameFunc(uint32 num,     // IN:
                                  void *payload)  // IN:
{
   char *fileName = NULL;

   VixToolsGetTempFileCreateNameFuncData *data =
                           (VixToolsGetTempFileCreateNameFuncData *) payload;

   if (payload == NULL) {
      goto quit;
   }

   if ((data->filePrefix == NULL) ||
       (data->tag == NULL) ||
       (data->fileSuffix == NULL)) {
      goto quit;
   }

   fileName = Str_SafeAsprintf(NULL,
                               "%s%s%u%s",
                               data->filePrefix,
                               data->tag,
                               num,
                               data->fileSuffix);

quit:
   return fileName;
} // VixToolsGetTempFileCreateNameFunc


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetTempFile --
 *
 *     Creates and opens a new temporary file, appropriate for the user
 *     that is represented by the userToken.
 *
 * Return value:
 *     VixError
 *     *tempFile will point to the name of the temporary file, or NULL on error.
 *     *fd will be the file descriptor of the temporary file, or -1 on error.
 *
 * Side effects:
 *     The temp file will be created and opened.
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsGetTempFile(VixCommandRequestHeader *requestMsg,   // IN
                    void *userToken,                       // IN
                    Bool useSystemTemp,                    // IN
                    char **tempFile,                       // OUT
                    int *tempFileFd)                       // OUT
{
   VixError err = VIX_E_FAIL;
   char *tempFilePath = NULL;
   int fd = -1;
   char *directoryPath = NULL;
   VixToolsGetTempFileCreateNameFuncData data;
   Bool createTempFile = TRUE;

   if (NULL == tempFile || NULL == tempFileFd) {
      ASSERT(0);
      return err;
   }

   *tempFile = NULL;
   *tempFileFd = -1;

   data.filePrefix = NULL;
   data.fileSuffix = NULL;
   data.tag = Util_SafeStrdup("vmware");

   if ((VIX_COMMAND_CREATE_TEMPORARY_FILE_EX == requestMsg->opCode) ||
       (VIX_COMMAND_CREATE_TEMPORARY_DIRECTORY == requestMsg->opCode)) {
      VixMsgCreateTempFileRequestEx *makeTempFileRequest;
      char *tempPtr = NULL;

      makeTempFileRequest = (VixMsgCreateTempFileRequestEx *) requestMsg;

      if ((requestMsg->commonHeader.bodyLength +
           requestMsg->commonHeader.headerLength) !=
          (((uint64) sizeof(*makeTempFileRequest)) +
           makeTempFileRequest->filePrefixLength + 1 +
           makeTempFileRequest->fileSuffixLength + 1 +
           makeTempFileRequest->directoryPathLength + 1 +
           makeTempFileRequest->propertyListLength)) {
         ASSERT(0);
         g_warning("%s: Invalid request message received\n", __FUNCTION__);
         err = VIX_E_INVALID_MESSAGE_BODY;
         goto quit;
      }

      tempPtr = ((char *) makeTempFileRequest) + sizeof(*makeTempFileRequest);

      if ('\0' != *(tempPtr + makeTempFileRequest->filePrefixLength)) {
         ASSERT(0);
         g_warning("%s: Invalid request message received\n", __FUNCTION__);
         err = VIX_E_INVALID_MESSAGE_BODY;
         goto quit;
      }

      data.filePrefix = Util_SafeStrdup(tempPtr);
      tempPtr += makeTempFileRequest->filePrefixLength + 1;

      if ('\0' != *(tempPtr + makeTempFileRequest->fileSuffixLength)) {
         ASSERT(0);
         g_warning("%s: Invalid request message received\n", __FUNCTION__);
         err = VIX_E_INVALID_MESSAGE_BODY;
         goto quit;
      }

      data.fileSuffix = Util_SafeStrdup(tempPtr);
      tempPtr += makeTempFileRequest->fileSuffixLength + 1;

      if ('\0' != *(tempPtr + makeTempFileRequest->directoryPathLength)) {
         ASSERT(0);
         g_warning("%s: Invalid request message received\n", __FUNCTION__);
         err = VIX_E_INVALID_MESSAGE_BODY;
         goto quit;
      }

      if (VIX_COMMAND_CREATE_TEMPORARY_DIRECTORY == requestMsg->opCode) {
         createTempFile = FALSE;
      }

      directoryPath = Util_SafeStrdup(tempPtr);
   } else {
      data.filePrefix = Util_SafeStrdup("");
      data.fileSuffix = Util_SafeStrdup("");
      directoryPath = Util_SafeStrdup("");
   }

#ifdef _WIN32
   /*
    * Don't try this if we're not impersonating anyone, since either
    *   1) It's running as System and System won't have the environment variables
    *      we want.
    *   2) It's the console user and then it's running within the user's session and
    *      we don't know who we're impersonating and also the environment variables
    *      will be directly present in the environment, so GetTempPath will do the
    *      trick.
    */
   if (PROCESS_CREATOR_USER_TOKEN != userToken) {
      if (!(strcmp(directoryPath, ""))) {
         free(directoryPath);
         directoryPath = NULL;
         if (useSystemTemp) {
            wchar_t tempPath[MAX_PATH];
            DWORD dwRet = GetTempPathW(ARRAYSIZE(tempPath), tempPath);
            if (0 < dwRet && dwRet < ARRAYSIZE(tempPath)) {
               directoryPath = Unicode_AllocWithUTF16(tempPath);
               err = VIX_OK;
            } else {
               g_warning("%s: unable to get temp path: windows error code %d\n",
                         __FUNCTION__, GetLastError());
               err = VIX_E_FAIL;
            }
         } else {
            err = VixToolsGetUserTmpDir(userToken, &directoryPath);
         }
      } else {
         /*
          * Initially, when 'err' variable is defined, it is initialized to
          * VIX_E_FAIL. At this point in the code, user has already specified
          * the directory path in which the temporary file has to be created.
          * This is completely fine. So, just set 'err' to VIX_OK.
          */
         err = VIX_OK;
      }

      if (VIX_SUCCEEDED(err)) {

         /*
          * If the specified directory path doesn't exist or points to an
          * existing regular file, then File_MakeTempEx2() returns different
          * errors on Windows and Linux platforms. So, check for the proper
          * filetype and return proper errors before calling
          * File_MakeTempEx2().
          */
         if (!File_Exists(directoryPath)) {
            err = FoundryToolsDaemon_TranslateSystemErr();
            goto quit;
         }

         if (File_IsFile(directoryPath)) {
            err = VIX_E_NOT_A_DIRECTORY;
            goto quit;
         }

         fd = File_MakeTempEx2(directoryPath,
                               createTempFile,
                               VixToolsGetTempFileCreateNameFunc,
                               &data,
                               &tempFilePath);
         if (fd < 0) {
            /*
             * File_MakeTempEx() function internally uses Posix variant
             * functions and proper error will be stuffed in errno variable.
             * If File_MakeTempEx() fails, then use Vix_TranslateErrno()
             * to translate the errno to a proper foundry error.
             */
            err = Vix_TranslateErrno(errno);
            goto quit;
         }
      } else {
         /*
          * Don't give up if VixToolsGetUserTmpDir() failed. It might just
          * have failed to load DLLs, so we might be running on Win 9x.
          * Just fall through to use the old fashioned
          * File_GetSafeRandomTmpDir().
          */

         ASSERT(directoryPath == NULL);
         directoryPath = Util_SafeStrdup("");
         err = VIX_OK;
      }
   }
#endif

   if (NULL == tempFilePath) {
      if (!strcmp(directoryPath, "")) {
         free(directoryPath);
         directoryPath = NULL;
         directoryPath = File_GetSafeRandomTmpDir(TRUE);
      }

      /*
       * If the specified directory path doesn't exist or points to an
       * existing regular file, then File_MakeTempEx2() returns different
       * errors on Windows and Linux platforms. So, check for the proper
       * filetype and return proper errors before calling
       * File_MakeTempEx2().
       */
      if (!File_Exists(directoryPath)) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         goto quit;
      }

      if (File_IsFile(directoryPath)) {
         err = VIX_E_NOT_A_DIRECTORY;
         goto quit;
      }

      fd = File_MakeTempEx2(directoryPath,
                            createTempFile,
                            VixToolsGetTempFileCreateNameFunc,
                            &data,
                            &tempFilePath);
      if (fd < 0) {
         /*
          * File_MakeTempEx2() function internally uses Posix variant
          * functions and proper error will be stuffed in errno variable.
          * If File_MakeTempEx2() fails, then use Vix_TranslateErrno()
          * to translate the errno to a proper foundry error.
          */
         err = Vix_TranslateErrno(errno);
         goto quit;
      }
   }

   *tempFile = tempFilePath;
   *tempFileFd = fd;
   err = VIX_OK;

quit:
   free(data.filePrefix);
   free(data.fileSuffix);
   free(data.tag);

   free(directoryPath);

   return err;
} // VixToolsGetTempFile


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsProcessHgfsPacket --
 *
 *    This sends a packet to the HGFS server in the guest.
 *    We parse the user credential type and authentication
 *    information as strings, followed by the actual HGFS packet
 *    that is to be sent to the HGFS Server in the guest Tools.
 *
 *    The authentication information is used to impersonate a user
 *    in the guest using VixToolsImpersonateUser, and then calls
 *    HgfsServerManager_ProcessPacket to issue the HGFS packet to
 *    the HGFS Server. The HGFS Server reply is contained in the
 *    HGFS reply packet, which will be returned back to
 *    us and handled in VMAutomationOnBackdoorCallReturns.
 *
 * Results:
 *    VIX_OK if success, VixError error code otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsProcessHgfsPacket(VixCommandHgfsSendPacket *requestMsg,   // IN
                          GMainLoop *eventQueue,                  // IN
                          char **result,                          // OUT
                          size_t *resultValueResult)              // OUT
{
   VixError err = VIX_OK;
   void *userToken = NULL;
   Bool impersonatingVMWareUser = FALSE;
   const char *hgfsPacket;
   size_t hgfsReplyPacketSize = 0;
   static char hgfsReplyPacket[HGFS_LARGE_PACKET_MAX];
   VMAutomationRequestParser parser;

   if ((NULL == requestMsg) || (0 == requestMsg->hgfsPacketSize)) {
      ASSERT(0);
      err = VIX_E_FAIL;
      goto quit;
   }

   err = VMAutomationRequestParserInit(&parser,
                                      &requestMsg->header, sizeof *requestMsg);
   if (VIX_OK != err) {
      goto quit;
   }

   /*
    * When transferring file to/from guest, VixToolsProcessHgfsPacket is
    * repeatedly called. Skip loading user profile, which is unnecessary
    * here, to minimize performance impact.
    */
   err = VixToolsImpersonateUser((VixCommandRequestHeader *) requestMsg,
                                 FALSE, // Do not load user profile
                                 &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   err = VMAutomationRequestParserGetData(&parser,
                                          requestMsg->hgfsPacketSize,
                                          &hgfsPacket);
   if (VIX_OK != err) {
      goto quit;
   }

   hgfsReplyPacketSize = sizeof hgfsReplyPacket;

   /*
    * Impersonation was okay, so let's give our packet to
    * the HGFS server and forward the reply packet back.
    */
   HgfsServerManager_ProcessPacket(&gVixHgfsBkdrConn,          // connection
                                   hgfsPacket,                 // packet in buf
                                   requestMsg->hgfsPacketSize, // packet in size
                                   hgfsReplyPacket,            // packet out buf
                                   &hgfsReplyPacketSize);      // in/out size

   if (eventQueue != NULL) {
      /*
       * Register a timer to periodically invalidate any inactive
       * HGFS sessions.
       */
      VixToolsRegisterHgfsSessionInvalidator(eventQueue);
   }

   if (NULL != resultValueResult) {
      *resultValueResult = hgfsReplyPacketSize;
   }
   if (NULL != result) {
      *result = hgfsReplyPacket;
   }

quit:
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   return err;
} // VixToolsProcessHgfsPacket


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListFileSystems --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsListFileSystems(VixCommandRequestHeader *requestMsg, // IN
                        char **result)                       // OUT
{
   VixError err = VIX_OK;
   static char resultBuffer[GUESTMSG_MAX_IN_SIZE];
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   char *destPtr;
   char *endDestPtr;
   Bool escapeStrs;
#if defined(_WIN32) || defined(__linux__)
   Bool truncated;
#endif
#if defined(_WIN32)
   char **driveList = NULL;
   int numDrives = -1;
   uint64 freeBytesToUser = 0;
   uint64 totalBytesToUser = 0;
   uint64 freeBytes = 0;
   char *fileSystemType;
   int i;
#endif
#ifdef __linux__
   MNTHANDLE fp;
   DECLARE_MNTINFO(mnt);
   const char *mountfile = NULL;
#endif

   destPtr = resultBuffer;
   *destPtr = 0;
   endDestPtr = resultBuffer + sizeof(resultBuffer);

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);

   escapeStrs = (requestMsg->requestFlags &
                 VIX_REQUESTMSG_ESCAPE_XML_DATA) != 0;

#if defined(_WIN32)
   numDrives = Win32U_GetLogicalDriveStrings(&driveList);
   if (-1 == numDrives) {
      g_warning("%s: unable to get drive listing: windows error code %d\n",
                __FUNCTION__, GetLastError());
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   if (escapeStrs) {
      destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr, "%s",
                             VIX_XML_ESCAPED_TAG);
   }

   for (i = 0; i < numDrives; i++) {
      if (!Win32U_GetDiskFreeSpaceEx(driveList[i],
                                     (PULARGE_INTEGER) &freeBytesToUser,
                                     (PULARGE_INTEGER) &totalBytesToUser,
                                     (PULARGE_INTEGER) &freeBytes)) {
         /*
          * If we encounter an error, just return 0 values for the space info
          */
         freeBytesToUser = 0;
         totalBytesToUser = 0;
         freeBytes = 0;

         g_warning("unable to get drive size info: windows error code %d\n",
                    GetLastError());
      }

      // If it fails, fileSystemType will be NULL
      Win32U_GetVolumeInformation(driveList[i],
                                  NULL,
                                  NULL,
                                  NULL,
                                  NULL,
                                  &fileSystemType);
      err = VixToolsPrintFileSystemInfo(&destPtr, endDestPtr,
                                        driveList[i], totalBytesToUser,
                                        freeBytesToUser,
                                        fileSystemType ? fileSystemType : "",
                                        escapeStrs, &truncated);
      if ((VIX_OK != err) || truncated) {
         goto quit;
      }
      free(fileSystemType);
   }

#elif defined(__linux__)

   mountfile = "/etc/mtab";

   fp = Posix_Setmntent(mountfile, "r");
   if (fp == NULL) {
      g_warning("failed to open mount file\n");
      err = VIX_E_FILE_NOT_FOUND;
      goto quit;
   }

   while (GETNEXT_MNTINFO(fp, mnt)) {
      struct statfs statfsbuf;
      uint64 size, freeSpace;

      if (Posix_Statfs(MNTINFO_MNTPT(mnt), &statfsbuf)) {
         g_warning("%s unable to stat mount point %s\n",
                   __FUNCTION__, MNTINFO_MNTPT(mnt));
         continue;
      }
      size = (uint64) statfsbuf.f_blocks * (uint64) statfsbuf.f_bsize;
      freeSpace = (uint64) statfsbuf.f_bfree * (uint64) statfsbuf.f_bsize;
      err = VixToolsPrintFileSystemInfo(&destPtr, endDestPtr,
                                        MNTINFO_NAME(mnt), size, freeSpace,
                                        MNTINFO_FSTYPE(mnt), escapeStrs,
                                        &truncated);
      if ((VIX_OK != err) || truncated) {
         goto quit;
      }
   }
   /*
    * Coverity flags this invocation of CLOSE_MNTFILE because the macro does
    * a test whose results are ignored.  However, it also has a needed side
    * effect, so this invocation is correct.
    */
   /* coverity[no_effect_test] */
   CLOSE_MNTFILE(fp);
#else
   err = VIX_E_NOT_SUPPORTED;
#endif

quit:
#if defined(_WIN32)
   for (i = 0; i < numDrives; i++) {
      free(driveList[i]);
   }

   free(driveList);
#endif

   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   *result = resultBuffer;

   // XXX result too large for g_debug()

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return(err);
} // VixToolsListFileSystems


#if defined(_WIN32) || defined(__linux__)
/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsPrintFileSystemInfo --
 *
 *      Appends a single file system entry to the XML-like string starting at
 *      *destPtr.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsPrintFileSystemInfo(char **destPtr,                // IN/OUT
                            const char *endDestPtr,        // IN
                            const char *name,              // IN
                            uint64 size,                   // IN
                            uint64 freeSpace,              // IN
                            const char *type,              // IN
                            Bool escapeStrs,               // IN
                            Bool *truncated)               // OUT
{
   VixError err;
   char *escapedName = NULL;
   char *escapedType = NULL;
   int bytesPrinted;

   ASSERT(endDestPtr > *destPtr);

   *truncated = FALSE;

   if (escapeStrs) {
      name = escapedName = VixToolsEscapeXMLString(name);
      if (NULL == escapedName) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }

      type = escapedType = VixToolsEscapeXMLString(type);
      if (NULL == escapedType) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
   }

   bytesPrinted = Str_Snprintf(*destPtr, endDestPtr - *destPtr,
                                "<filesystem>"
                               "<name>%s</name>"
                               "<size>%"FMT64"u</size>"
                               "<freeSpace>%"FMT64"u</freeSpace>"
                               "<type>%s</type>"
                               "</filesystem>",
                               name, size, freeSpace, type);
   if (bytesPrinted != -1) {
      *destPtr += bytesPrinted;
   } else { // out of space
      **destPtr = '\0';
      g_warning("%s: file system list results too large, truncating",
                 __FUNCTION__);
      *truncated = TRUE;
      err = VIX_OK;
      goto quit;
   }

   err = VIX_OK;

quit:
   free(escapedName);
   free(escapedType);

   return err;
}
#endif // #if defined(_WIN32) || defined(__linux__)


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsValidateCredentials --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsValidateCredentials(VixCommandRequestHeader *requestMsg)    // IN
{
   VixError err;
   void *userToken = NULL;

   err = VixToolsImpersonateUser((VixCommandRequestHeader *) requestMsg,
                                 TRUE,
                                 &userToken);
   if (VIX_OK == err) {
      g_debug("%s: User: %s\n", __FUNCTION__, IMPERSONATED_USERNAME);
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);


   return err;
}

/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsAcquireCredentials --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsAcquireCredentials(VixCommandRequestHeader *requestMsg,    // IN
                           GMainLoop *eventQueue,                  // IN
                           char **result)                          // OUT
{
   VixError err;

#if !defined(_WIN32)
   err = VIX_E_NOT_SUPPORTED;
   goto quit;
#else
   err = VixToolsAuthenticateWithSSPI(requestMsg, eventQueue, result);

   if (VIX_OK != err) {
      g_warning("%s: Failed to authenticate with SSPI with error %d\n",
                __FUNCTION__, err);
      goto quit;
   }
#endif

quit:
   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsReleaseCredentials --
 *
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsReleaseCredentials(VixCommandRequestHeader *requestMsg)    // IN
{
   VixError err = VIX_OK;

#if !defined(_WIN32)
   err = VIX_E_NOT_SUPPORTED;
#else
    err = VixToolsReleaseCredentialsImpl(requestMsg);
#endif

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetGuestNetworkingConfig --
 *
 *
 * Return value:
 *    VIX_OK on success
 *    VixError on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

#if defined(__linux__) || defined(_WIN32)
VixError
VixToolsGetGuestNetworkingConfig(VixCommandRequestHeader *requestMsg,   // IN
                                 char **resultBuffer,                   // OUT
                                 size_t *resultBufferLength)            // OUT
{
   VixError err = VIX_OK;
   VixPropertyListImpl propList;
   char *serializedBuffer = NULL;
   size_t serializedBufferLength = 0;
   GuestNic *nicEntry = NULL;
   VmIpAddress *ipAddr;

   ASSERT(NULL != requestMsg);
   ASSERT(NULL != resultBuffer);
   ASSERT(NULL != resultBufferLength);

   VixPropertyList_Initialize(&propList);

   nicEntry = NetUtil_GetPrimaryNic();
   if (NULL == nicEntry) {
      err = FoundryToolsDaemon_TranslateSystemErr();
      goto quit;
   }

   ipAddr = &nicEntry->ips.ips_val[0];

   /*
    *  Now, record these values in a property list.
    */
   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_VM_IP_ADDRESS,
                                   ipAddr->ipAddress);
   if (VIX_OK != err) {
      goto quit;
   }

#if defined(_WIN32)
   err = VixPropertyList_SetBool(&propList,
                                 VIX_PROPERTY_VM_DHCP_ENABLED,
                                 ipAddr->dhcpEnabled);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VixPropertyList_SetString(&propList,
                                   VIX_PROPERTY_VM_SUBNET_MASK,
                                   ipAddr->subnetMask);
   if (VIX_OK != err) {
      goto quit;
   }
#endif

   /*
    * Serialize the property list to buffer then encode it.
    * This is the string we return to the VMX process.
    */
   err = VixPropertyList_Serialize(&propList,
                                   FALSE,
                                   &serializedBufferLength,
                                   &serializedBuffer);

   if (VIX_OK != err) {
      goto quit;
   }
   *resultBuffer = serializedBuffer;
   *resultBufferLength = serializedBufferLength;
   serializedBuffer = NULL;


quit:
   VixPropertyList_RemoveAllWithoutHandles(&propList);
   if (NULL != nicEntry) {
      VMX_XDR_FREE(xdr_GuestNic, nicEntry);
      free(nicEntry);
   }

   return err;
} // VixToolsGetGuestNetworkingConfig
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsSetGuestNetworkingConfig --
 *
 *
 * Return value:
 *    Vix_OK on success
 *    VixError on failure
 *
 * Side effects:
 *    networking configuration on hte guest may change
 *
 *-----------------------------------------------------------------------------
 */

#if defined(_WIN32)
VixError
VixToolsSetGuestNetworkingConfig(VixCommandRequestHeader *requestMsg)    // IN
{
   VixError err = VIX_OK;
   Bool impersonatingVMWareUser = FALSE;
   void *userToken = NULL;
   VixMsgSetGuestNetworkingConfigRequest *setGuestNetworkingConfigRequest = NULL;
   VixPropertyListImpl propList;
   VixPropertyValue *propertyPtr = NULL;
   char *messageBody = NULL;
   char ipAddr[IP_ADDR_SIZE];
   char subnetMask[IP_ADDR_SIZE];
   Bool dhcpEnabled = FALSE;
   HRESULT hrErr;

   ASSERT(NULL != requestMsg);

   ipAddr[0] = '\0';
   subnetMask[0] = '\0';

   err = VixToolsImpersonateUser(requestMsg, TRUE, &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);

   setGuestNetworkingConfigRequest = (VixMsgSetGuestNetworkingConfigRequest *)requestMsg;
   messageBody = (char *) requestMsg + sizeof(*setGuestNetworkingConfigRequest);

   VixPropertyList_Initialize(&propList);
   err = VixPropertyList_Deserialize(&propList,
                                     messageBody,
                                     setGuestNetworkingConfigRequest -> bufferSize,
                                     VIX_PROPERTY_LIST_BAD_ENCODING_ERROR);
   if (VIX_OK != err) {
      goto quit;
   }

   propertyPtr = propList.properties;
   while (propertyPtr != NULL) {
      switch (propertyPtr->propertyID) {
      ///////////////////////////////////////////
      case VIX_PROPERTY_VM_DHCP_ENABLED:
         if (propertyPtr->value.boolValue) {
            dhcpEnabled = TRUE;
         }
         break;

      ///////////////////////////////////////////
      case VIX_PROPERTY_VM_IP_ADDRESS:
         if (strlen(propertyPtr->value.strValue) < sizeof ipAddr) {
            Str_Strcpy(ipAddr,
                       propertyPtr->value.strValue,
                       sizeof ipAddr);
            } else {
               err = VIX_E_INVALID_ARG;
               goto quit;
            }
         break;

      ///////////////////////////////////////////
      case VIX_PROPERTY_VM_SUBNET_MASK:
         if (strlen(propertyPtr->value.strValue) < sizeof subnetMask) {
            Str_Strcpy(subnetMask,
                       propertyPtr->value.strValue,
                       sizeof subnetMask);
         } else {
            err = VIX_E_INVALID_ARG;
            goto quit;
         }
         break;

      ///////////////////////////////////////////
      default:
         /*
          * Be more tolerant.  Igonore unknown properties.
          */
         break;
      } // switch

      propertyPtr = propertyPtr->next;
   } // while {propList.properties != NULL)

   if (dhcpEnabled) {
      hrErr = VixToolsEnableDHCPOnPrimary();
   } else {
      if (('\0' != ipAddr[0]) ||
          ('\0' != subnetMask[0])) {
         hrErr = VixToolsEnableStaticOnPrimary(ipAddr, subnetMask);
      } else {
         /*
          * Setting static ip, both ip and subnet mask are missing
          */
         err = VIX_E_MISSING_REQUIRED_PROPERTY;
         goto quit;
      }
   }
   if (S_OK != hrErr) {
      if (FACILITY_WIN32 != HRESULT_FACILITY(hrErr)) {
         err = Vix_TranslateCOMError(hrErr);
      } else {
         err = Vix_TranslateSystemError(hrErr);
      }
   }

quit:
   VixPropertyList_RemoveAllWithoutHandles(&propList);

   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;

} // VixToolsSetGuestNetworkingConfig
#endif


#if SUPPORT_VGAUTH

/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsAddAuthAlias --
 *
 *    Calls to VGAuth to add a new alias.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    VGAuth alias store is updated.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsAddAuthAlias(VixCommandRequestHeader *requestMsg)    // IN
{
   VixError err = VIX_OK;
   VGAuthError vgErr;
   void *userToken = NULL;
   VGAuthContext *ctx = NULL;
   VixMsgAddAuthAliasRequest *req;
   const char *userName;
   const char *pemCert;
   const char *subjectName;
   const char *aliasComment;
   VGAuthAliasInfo ai;
   VMAutomationRequestParser parser;
   Bool impersonatingVMWareUser = FALSE;

   err = VMAutomationRequestParserInit(&parser, requestMsg, sizeof *req);
   if (VIX_OK != err) {
      goto quit;
   }

   req = (VixMsgAddAuthAliasRequest *) requestMsg;
   err = VMAutomationRequestParserGetOptionalString(&parser, req->userNameLen,
                                                    &userName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL == userName || 0 == *userName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VMAutomationRequestParserGetOptionalString(&parser, req->pemCertLen,
                                                    &pemCert);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL == pemCert || 0 == *pemCert) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   if ((req->subjectType != VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED) &&
       (req->subjectType != VIX_GUEST_AUTH_SUBJECT_TYPE_ANY)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VMAutomationRequestParserGetOptionalString(&parser, req->subjectNameLen,
                                                    &subjectName);
   if (VIX_OK != err) {
      goto quit;
   }

   if ((req->subjectType == VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED) &&
       (NULL == subjectName || 0 == *subjectName)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VMAutomationRequestParserGetOptionalString(&parser, req->aliasCommentLen,
                                                    &aliasComment);
   if (VIX_OK != err) {
      goto quit;
   }

   err = VixToolsImpersonateUser((VixCommandRequestHeader *) requestMsg,
                                 TRUE,
                                 &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);
   /*
    * For aliasStore APIs, make a fresh context so we know
    * the security is correct.
    */
   vgErr = VGAuth_Init(VMTOOLSD_APP_NAME, 0, NULL, &ctx);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto quit;
   }

   ai.subject.type = (req->subjectType == VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED) ?
      VGAUTH_SUBJECT_NAMED : VGAUTH_SUBJECT_ANY;
   ai.subject.val.name = (char *) subjectName;
   ai.comment = (char *) aliasComment;

   vgErr = VGAuth_AddAlias(ctx, userName, req->addMapping, pemCert, &ai,
                           0, NULL);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
   }

quit:
   if (ctx) {
      vgErr = VGAuth_Shutdown(ctx);
      if (VGAUTH_FAILED(vgErr)) {
         err = VixToolsTranslateVGAuthError(vgErr);
         // fall thru
      }
   }

   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsRemoveAuthAlias --
 *
 *    Calls to VGAuth to remove an alias.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    VGAuth Alias store is updated.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsRemoveAuthAlias(VixCommandRequestHeader *requestMsg)    // IN
{
   VixError err = VIX_OK;
   VGAuthError vgErr;
   void *userToken = NULL;
   VGAuthContext *ctx = NULL;
   VixMsgRemoveAuthAliasRequest *req;
   const char *userName;
   const char *pemCert;
   const char *subjectName;
   VGAuthSubject subj;
   VMAutomationRequestParser parser;
   Bool impersonatingVMWareUser = FALSE;

   err = VMAutomationRequestParserInit(&parser, requestMsg, sizeof *req);
   if (VIX_OK != err) {
      goto quit;
   }

   req = (VixMsgRemoveAuthAliasRequest *) requestMsg;
   err = VMAutomationRequestParserGetOptionalString(&parser, req->userNameLen,
                                                    &userName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL == userName || 0 == *userName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VMAutomationRequestParserGetOptionalString(&parser, req->pemCertLen,
                                                    &pemCert);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL == pemCert || 0 == *pemCert) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   if ((req->subjectType != VIX_GUEST_AUTH_SUBJECT_TYPE_NONE) &&
       (req->subjectType != VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED) &&
       (req->subjectType != VIX_GUEST_AUTH_SUBJECT_TYPE_ANY)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VMAutomationRequestParserGetOptionalString(&parser, req->subjectNameLen,
                                                    &subjectName);
   if (VIX_OK != err) {
      goto quit;
   }

   if ((req->subjectType == VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED) &&
       (NULL == subjectName || 0 == *subjectName)) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser((VixCommandRequestHeader *) requestMsg,
                                 TRUE,
                                 &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);
   /*
    * For aliasStore APIs, make a fresh context so we know
    * the security is correct.
    */
   vgErr = VGAuth_Init(VMTOOLSD_APP_NAME, 0, NULL, &ctx);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto quit;
   }

   if (VIX_GUEST_AUTH_SUBJECT_TYPE_NONE == req->subjectType) {
#ifdef notyet
      /*
       * XXX turn on this assert() 'soon' -- if done now it could be hit
       * with these tools and an old hostd/VMX that still shares the opcode.
       */
      ASSERT(requestMsg->opCode == VIX_COMMAND_REMOVE_AUTH_ALIAS_BY_CERT);
#endif
      vgErr = VGAuth_RemoveAliasByCert(ctx, userName, pemCert, 0, NULL);
   } else {
      ASSERT(requestMsg->opCode == VIX_COMMAND_REMOVE_AUTH_ALIAS);
      subj.type = (req->subjectType == VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED) ?
         VGAUTH_SUBJECT_NAMED : VGAUTH_SUBJECT_ANY;
      subj.val.name = (char *) subjectName;

      vgErr = VGAuth_RemoveAlias(ctx, userName, pemCert, &subj, 0, NULL);
   }
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
   }

quit:
   if (ctx) {
      vgErr = VGAuth_Shutdown(ctx);
      if (VGAUTH_FAILED(vgErr)) {
         err = VixToolsTranslateVGAuthError(vgErr);
         // fall thru
      }
   }
   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListAuthAliases --
 *
 *    Calls to VGAuth to list user aliases.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    VGAuth Alias store is updated.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsListAuthAliases(VixCommandRequestHeader *requestMsg, // IN
                        size_t maxBufferSize,                // IN
                        char **result)                       // OUT
{
   VixError err = VIX_OK;
   VGAuthError vgErr;
   void *userToken = NULL;
   VGAuthContext *ctx = NULL;
   VixMsgListAuthAliasesRequest *req;
   const char *userName;
   VMAutomationRequestParser parser;
   Bool impersonatingVMWareUser = FALSE;
   int num = 0;
   int i;
   int j;
   VGAuthUserAlias *uaList = NULL;
   static char resultBuffer[GUESTMSG_MAX_IN_SIZE];
   char *destPtr;
   char *endDestPtr;
   char *tmpBuf = NULL;
   size_t recordSize;
   char *escapedStr = NULL;
   char *escapedStr2 = NULL;

   ASSERT(maxBufferSize <= GUESTMSG_MAX_IN_SIZE);

   *result = NULL;

   destPtr = resultBuffer;
   *destPtr = 0;

   err = VMAutomationRequestParserInit(&parser, requestMsg, sizeof *req);
   if (VIX_OK != err) {
      goto quit;
   }

   req = (VixMsgListAuthAliasesRequest *) requestMsg;
   err = VMAutomationRequestParserGetOptionalString(&parser, req->userNameLen,
                                                    &userName);
   if (VIX_OK != err) {
      goto quit;
   }

   if (NULL == userName || 0 == *userName) {
      err = VIX_E_INVALID_ARG;
      goto quit;
   }

   err = VixToolsImpersonateUser((VixCommandRequestHeader *) requestMsg,
                                 TRUE,
                                 &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);

   /*
    * For aliasStore APIs, make a fresh context so we know
    * the security is correct.
    */
   vgErr = VGAuth_Init(VMTOOLSD_APP_NAME, 0, NULL, &ctx);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto quit;
   }

   vgErr = VGAuth_QueryUserAliases(ctx, userName, 0, NULL, &num, &uaList);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto quit;
   }

   endDestPtr = resultBuffer + maxBufferSize;
   destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr, "%s",
                          VIX_XML_ESCAPED_TAG);
   for (i = 0; i < num; i++) {
      char *recordBuf = NULL;

      escapedStr = VixToolsEscapeXMLString(uaList[i].pemCert);
      if (escapedStr == NULL) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      tmpBuf = Str_Asprintf(NULL, "<record><pemCert>%s</pemCert>",
                            escapedStr);
      free(escapedStr);
      escapedStr = NULL;
      if (tmpBuf == NULL) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      for (j = 0; j < uaList[i].numInfos; j++) {
         char *nextBuf;

         if (uaList[i].infos[j].comment) {
            escapedStr = VixToolsEscapeXMLString(uaList[i].infos[j].comment);
            if (escapedStr == NULL) {
               err = VIX_E_OUT_OF_MEMORY;
               goto quit;
            }
         }
         if (uaList[i].infos[j].subject.type == VGAUTH_SUBJECT_NAMED) {
            escapedStr2 = VixToolsEscapeXMLString(uaList[i].infos[j].subject.val.name);
            if (escapedStr2 == NULL) {
               err = VIX_E_OUT_OF_MEMORY;
               goto quit;
            }
         }
         nextBuf = Str_Asprintf(NULL,
                                "%s"
                                "<alias>"
                                "<type>%d</type>"
                                "<name>%s</name>"
                                "<comment>%s</comment>"
                                "</alias>",
                                tmpBuf,
                                (uaList[i].infos[j].subject.type ==
                                   VGAUTH_SUBJECT_NAMED) ?
                                      VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED :
                                      VIX_GUEST_AUTH_SUBJECT_TYPE_ANY,
                                escapedStr2 ? escapedStr2 : "",
                                escapedStr ? escapedStr : "");
         if (nextBuf == NULL) {
            err = VIX_E_OUT_OF_MEMORY;
            goto quit;
         }
         free(tmpBuf);
         tmpBuf = nextBuf;
         free(escapedStr);
         escapedStr = NULL;
         free(escapedStr2);
         escapedStr2 = NULL;
      }
      recordBuf = Str_Asprintf(&recordSize,
                               "%s</record>",
                               tmpBuf);
      free(tmpBuf);
      tmpBuf = NULL;
      if (recordBuf == NULL) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      if ((destPtr + recordSize) < endDestPtr) {
         destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr,
                                "%s", recordBuf);
      } else {
         free(recordBuf);
         recordBuf = NULL;
         Log("%s: ListAuth list results too large, truncating", __FUNCTION__);
         goto quit;
      }
      free(recordBuf);
      recordBuf = NULL;
   }

   *result = resultBuffer;

quit:
   free(tmpBuf);
   free(escapedStr);
   free(escapedStr2);
   VGAuth_FreeUserAliasList(num, uaList);
   if (ctx) {
      vgErr = VGAuth_Shutdown(ctx);
      if (VGAUTH_FAILED(vgErr)) {
         err = VixToolsTranslateVGAuthError(vgErr);
         // fall thru
      }
   }

   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListMappedAliases --
 *
 *    Calls to VGAuth to list mapped aliases.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    VGAuth Alias store is updated.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsListMappedAliases(VixCommandRequestHeader *requestMsg, // IN
                          size_t maxBufferSize,                // IN
                          char **result)                       // OUT
{
   VixError err = VIX_OK;
   VGAuthError vgErr;
   void *userToken = NULL;
   VGAuthContext *ctx = NULL;
   VixMsgListMappedAliasesRequest *req;
   VMAutomationRequestParser parser;
   Bool impersonatingVMWareUser = FALSE;
   int num = 0;
   int i;
   int j;
   VGAuthMappedAlias *maList = NULL;
   static char resultBuffer[GUESTMSG_MAX_IN_SIZE];
   char *destPtr;
   char *endDestPtr;
   char *tmpBuf = NULL;
   char *escapedStr = NULL;
   char *escapedStr2 = NULL;
   size_t recordSize;

   ASSERT(maxBufferSize <= GUESTMSG_MAX_IN_SIZE);

   *result = NULL;
   destPtr = resultBuffer;
   *destPtr = 0;

   err = VMAutomationRequestParserInit(&parser, requestMsg, sizeof *req);
   if (VIX_OK != err) {
      goto quit;
   }

   req = (VixMsgListMappedAliasesRequest *) requestMsg;
   err = VixToolsImpersonateUser((VixCommandRequestHeader *) requestMsg,
                                 TRUE,
                                 &userToken);
   if (VIX_OK != err) {
      goto quit;
   }
   impersonatingVMWareUser = TRUE;

   g_debug("%s: User: %s\n",
           __FUNCTION__, IMPERSONATED_USERNAME);

   vgErr = TheVGAuthContext(&ctx);
   if (vgErr != VGAUTH_E_OK) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto quit;
   }

   /*
    * For aliasStore APIs, make a fresh context so we know
    * the security is correct.
    */
   vgErr = VGAuth_Init(VMTOOLSD_APP_NAME, 0, NULL, &ctx);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto quit;
   }

   vgErr = VGAuth_QueryMappedAliases(ctx, 0, NULL, &num, &maList);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto quit;
   }

   endDestPtr = resultBuffer + maxBufferSize;
   destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr, "%s",
                          VIX_XML_ESCAPED_TAG);
   for (i = 0; i < num; i++) {
      char *recordBuf = NULL;

      escapedStr = VixToolsEscapeXMLString(maList[i].pemCert);
      if (escapedStr == NULL) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      escapedStr2 = VixToolsEscapeXMLString(maList[i].userName);
      if (escapedStr2 == NULL) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      tmpBuf = Str_Asprintf(NULL, "<record><pemCert>%s</pemCert>"
                            "<userName>%s</userName>",
                            escapedStr,
                            escapedStr2);
      g_free(escapedStr2);
      g_free(escapedStr);
      escapedStr = NULL;
      escapedStr2 = NULL;
      if (tmpBuf == NULL) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      for (j = 0; j < maList[i].numSubjects; j++) {
         char *nextBuf;

         if (maList[i].subjects[j].type == VGAUTH_SUBJECT_NAMED) {
            escapedStr = VixToolsEscapeXMLString(maList[i].subjects[j].val.name);
            if (escapedStr == NULL) {
               err = VIX_E_OUT_OF_MEMORY;
               goto quit;
            }
         }
         nextBuf = Str_Asprintf(NULL,
                                "%s"
                                "<alias>"
                                "<type>%d</type>"
                                "<name>%s</name>"
                                "</alias>",
                                tmpBuf,
                                (maList[i].subjects[j].type ==
                                   VGAUTH_SUBJECT_NAMED) ?
                                      VIX_GUEST_AUTH_SUBJECT_TYPE_NAMED :
                                      VIX_GUEST_AUTH_SUBJECT_TYPE_ANY,
                                escapedStr ? escapedStr : "");
         if (nextBuf == NULL) {
            err = VIX_E_OUT_OF_MEMORY;
            goto quit;
         }
         free(tmpBuf);
         tmpBuf = nextBuf;
         free(escapedStr);
         escapedStr = NULL;
      }
      recordBuf = Str_Asprintf(&recordSize,
                               "%s</record>",
                               tmpBuf);
      free(tmpBuf);
      tmpBuf = NULL;
      if (recordBuf == NULL) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      if ((destPtr + recordSize) < endDestPtr) {
         destPtr += Str_Sprintf(destPtr, endDestPtr - destPtr,
                                "%s", recordBuf);
      } else {
         free(recordBuf);
         recordBuf = NULL;
         Log("%s: ListMapped results too large, truncating", __FUNCTION__);
         goto quit;
      }
      free(recordBuf);
      recordBuf = NULL;
   }

   *result = resultBuffer;

quit:
   free(tmpBuf);
   free(escapedStr);
   free(escapedStr2);
   VGAuth_FreeMappedAliasList(num, maList);
   if (ctx) {
      vgErr = VGAuth_Shutdown(ctx);
      if (VGAUTH_FAILED(vgErr)) {
         err = VixToolsTranslateVGAuthError(vgErr);
         // fall thru
      }
   }

   if (impersonatingVMWareUser) {
      VixToolsUnimpersonateUser(userToken);
   }
   VixToolsLogoutUser(userToken);

   g_message("%s: opcode %d returning %"FMT64"d\n", __FUNCTION__,
             requestMsg->opCode, err);

   return err;
}
#endif   // SUPPORT_VGAUTH


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsCreateRegKey --
 *
 *    Calls the function to create a new Windows Registry Key.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    May affect applications reading the key.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsCreateRegKey(VixCommandRequestHeader *requestMsg)    // IN
{
#ifdef _WIN32
   return VixToolsCreateRegKeyImpl(requestMsg);
#else
   return VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListRegKeys --
 *
 *    Calls the function to list all subkeys for a given Windows Registry Key.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsListRegKeys(VixCommandRequestHeader *requestMsg,    // IN
                    size_t maxBufferSize,                   // IN
                    void *eventQueue,                       // IN
                    char **result)                          // OUT
{
#ifdef _WIN32
   return VixToolsListRegKeysImpl(requestMsg, maxBufferSize, eventQueue, result);
#else
   return VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsDeleteRegKey --
 *
 *    Calls the function to delete a Windows Registry Key.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    May affect applications reading the key.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsDeleteRegKey(VixCommandRequestHeader *requestMsg)    // IN
{
#ifdef _WIN32
   return VixToolsDeleteRegKeyImpl(requestMsg);
#else
   return VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsSetRegValue --
 *
 *    Calls the function to set/create a Windows Registry Value for a given Key.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    May affect applications reading the key.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsSetRegValue(VixCommandRequestHeader *requestMsg)    // IN
{
#ifdef _WIN32
   return VixToolsSetRegValueImpl(requestMsg);
#else
   return VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsListRegValues --
 *
 *    Calls the function to list all values for a given Windows Registry Key.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsListRegValues(VixCommandRequestHeader *requestMsg,    // IN
                      size_t maxBufferSize,                   // IN
                      void *eventQueue,                       // IN
                      char **result)                          // OUT
{
#ifdef _WIN32
   return VixToolsListRegValuesImpl(requestMsg, maxBufferSize, eventQueue, result);
#else
   return VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsDeleteRegValue --
 *
 *    Calls the function to delete a Windows Registry Value for a given Key.
 *
 * Return value:
 *    VixError
 *
 * Side effects:
 *    May affect applications reading the key.
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsDeleteRegValue(VixCommandRequestHeader *requestMsg)    // IN
{
#ifdef _WIN32
   return VixToolsDeleteRegValueImpl(requestMsg);
#else
   return VIX_E_OP_NOT_SUPPORTED_ON_GUEST;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsDoesUsernameMatchCurrentUser --
 *
 *    Check if the provider username matches the current user.
 *
 * Return value:
 *    VIX_OK if it does, otherwise an appropriate error code.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static VixError
VixToolsDoesUsernameMatchCurrentUser(const char *username)  // IN
{
   VixError err = VIX_E_FAIL;

#ifdef _WIN32
   char *currentUser = NULL;
   DWORD currentUserSize = 0;
   DWORD retVal = 0;
   HANDLE processToken = INVALID_HANDLE_VALUE;
   PTOKEN_USER processTokenInfo = NULL;
   DWORD processTokenInfoSize = 0;
   char *sidUserName = NULL;
   DWORD sidUserNameSize = 0;
   char *sidDomainName = NULL;
   DWORD sidDomainNameSize = 0;
   SID_NAME_USE sidNameUse;

   /*
    * Check to see if the user provided a '<Domain>\<User>' formatted username
    */
   if (NULL != Str_Strchr(username, '\\')) {
      /*
       * A '<Domain>\<User>' formatted username was provided.
       * We must retrieve the domain as well as the username to verify
       * the current vixtools user matches the username provided
       */
      retVal = OpenProcessToken(GetCurrentProcess(),
                                TOKEN_READ,
                                &processToken);

      if (!retVal || !processToken) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("unable to open process token: windows error code %d\n",
                   GetLastError());

         goto quit;
      }

      // Determine necessary buffer size
      GetTokenInformation(processToken,
                          TokenUser,
                          NULL,
                          0,
                          &processTokenInfoSize);

      if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("unable to get token info: windows error code %d\n",
                   GetLastError());

         goto quit;
      }

      processTokenInfo = Util_SafeMalloc(processTokenInfoSize);

      if (!GetTokenInformation(processToken,
                               TokenUser,
                               processTokenInfo,
                               processTokenInfoSize,
                               &processTokenInfoSize)) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("unable to get token info: windows error code %d\n",
                   GetLastError());

         goto quit;
      }

      // Retrieve user name and domain name based on user's SID.
      Win32U_LookupAccountSid(NULL,
                              processTokenInfo->User.Sid,
                              NULL,
                              &sidUserNameSize,
                              NULL,
                              &sidDomainNameSize,
                              &sidNameUse);

      if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("unable to lookup account sid: windows error code %d\n",
                   GetLastError());
         goto quit;
      }

      sidUserName = Util_SafeMalloc(sidUserNameSize);
      sidDomainName = Util_SafeMalloc(sidDomainNameSize);

      if (!Win32U_LookupAccountSid(NULL,
                                   processTokenInfo->User.Sid,
                                   sidUserName,
                                   &sidUserNameSize,
                                   sidDomainName,
                                   &sidDomainNameSize,
                                   &sidNameUse)) {
         err = FoundryToolsDaemon_TranslateSystemErr();
         g_warning("unable to lookup account sid: windows error code %d\n",
                   GetLastError());
         goto quit;
     }

      // Populate currentUser with Domain + '\' + Username
      currentUser = Str_SafeAsprintf(NULL, "%s\\%s", sidDomainName, sidUserName);
   } else {
      /*
       * For Windows, get the name of the owner of this process, then
       * compare it to the provided username.
       */
      if (!Win32U_GetUserName(currentUser, &currentUserSize)) {
         if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
            err = FoundryToolsDaemon_TranslateSystemErr();
            goto quit;
         }

         currentUser = Util_SafeMalloc(currentUserSize);

         if (!Win32U_GetUserName(currentUser, &currentUserSize)) {
            err = FoundryToolsDaemon_TranslateSystemErr();
            goto quit;
         }
      }
   }

   if (0 != Unicode_CompareIgnoreCase(username, currentUser)) {
      err = VIX_E_INTERACTIVE_SESSION_USER_MISMATCH;
      goto quit;
   }

   err = VIX_OK;

quit:
   free(sidDomainName);
   free(sidUserName);
   free(processTokenInfo);
   CloseHandle(processToken);
   free(currentUser);

#else /* Below is the POSIX case. */
   uid_t currentUid;
   struct passwd pwd;
   struct passwd *ppwd = &pwd;
   char *buffer = NULL; // a pool of memory for Posix_Getpwnam_r() to use.
   long bufferSize;

   /*
    * For POSIX systems, look up the uid of 'username', and compare
    * it to the uid of the owner of this process. This handles systems
    * where multiple usernames map to the name user.
    */

   /*
    * Get the maximum size buffer needed by getpwuid_r.
    * Multiply by 4 to compensate for the conversion to UTF-8 by
    * the Posix_Getpwnam_r() wrapper.
    */
   errno = 0;
   bufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
   if ((errno != 0) || (bufferSize <= 0)) {
      bufferSize = 16 * 1024;  // Unlimited; pick something reasonable
   }

   bufferSize *= 4;

   buffer = Util_SafeMalloc((size_t)bufferSize);

   if (Posix_Getpwnam_r(username, &pwd, buffer, bufferSize, &ppwd) != 0 ||
       NULL == ppwd) {
      /*
       * This username should exist, since it should have already
       * been validated by guestd. Assume it is a system error.
       */
      err = FoundryToolsDaemon_TranslateSystemErr();
      g_warning("Unable to get the uid for username %s.\n", username);
      goto quit;
   }

   /*
    * In the Windows version, GetUserNameW() returns the name of the
    * user the thread is impersonating (if it is impersonating someone),
    * so geteuid() seems to be the moral equivalent.
    */
   currentUid = geteuid();

   if (currentUid != ppwd->pw_uid) {
      err = VIX_E_INTERACTIVE_SESSION_USER_MISMATCH;
      goto quit;
   }

   err = VIX_OK;

 quit:
   Util_ZeroFree(buffer, bufferSize);

#endif

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsPidRefersToThisProcess --
 *
 *    Determines if the given pid refers to the current process, in
 *    that if it passed to the appropriate OS-specific process killing
 *    function, will this process get killed.
 *
 * Return value:
 *    TRUE if killing pid kills us, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

Bool
VixToolsPidRefersToThisProcess(ProcMgr_Pid pid)  // IN
{
#ifdef _WIN32
   return (GetCurrentProcessId() == pid);
#else
   /*
    * POSIX is complicated. Pid could refer to this process directly,
    * be 0 which forces all processes in this process's group to quit, be -1
    * which works on everything to which it can send a signal, or be -1 times
    * the process group ID of this process.
    */
   return ((getpid() == pid) || (0 == pid) || (-1 == pid) ||
           ((pid < -1) && (getpgrp() == (pid * -1))));
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsCheckIfVixCommandEnabled --
 *
 *    Checks to see if the opcode has been disabled via the tools
 *    configuration.
 *
 *    This does not affect VIX_COMMAND_GET_TOOLS_STATE; that always
 *    needs to work.
 *
 *    Many non-VMODL APIs do not have an API specific option; those
 *    are only affected by the global setting.
 *
 * Return value:
 *    TRUE if enabled, FALSE otherwise.
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VixToolsCheckIfVixCommandEnabled(int opcode,                          // IN
                                 GKeyFile *confDictRef)               // IN
{
   Bool enabled = TRUE;
   switch (opcode) {
      /*
       * We always let this through, since its needed to do basic
       * init work.
       */
      case VIX_COMMAND_GET_TOOLS_STATE:
         enabled = TRUE;
         break;

      case VIX_COMMAND_LIST_PROCESSES:
      case VIX_COMMAND_LIST_PROCESSES_EX:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                    VIX_TOOLS_CONFIG_API_LIST_PROCESSES_NAME);
         break;

      case VIX_COMMAND_LIST_FILES:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_LIST_FILES_NAME);
         break;
      case VIX_COMMAND_DELETE_GUEST_FILE:
      case VIX_COMMAND_DELETE_GUEST_FILE_EX:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_DELETE_FILE_NAME);
         break;
      case VIX_COMMAND_DELETE_GUEST_DIRECTORY:
      case VIX_COMMAND_DELETE_GUEST_EMPTY_DIRECTORY:
      case VIX_COMMAND_DELETE_GUEST_DIRECTORY_EX:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_DELETE_DIRECTORY_NAME);
         break;
      case VIX_COMMAND_KILL_PROCESS:
      case VIX_COMMAND_TERMINATE_PROCESS:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_TERMINATE_PROCESS_NAME);
         break;
      case VIX_COMMAND_CREATE_DIRECTORY:
      case VIX_COMMAND_CREATE_DIRECTORY_EX:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_MAKE_DIRECTORY_NAME);
         break;
      case VIX_COMMAND_MOVE_GUEST_FILE:
      case VIX_COMMAND_MOVE_GUEST_FILE_EX:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_MOVE_FILE_NAME);
         break;
      case VIX_COMMAND_MOVE_GUEST_DIRECTORY:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_MOVE_DIRECTORY_NAME);
         break;
      case VIX_COMMAND_START_PROGRAM:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_START_PROGRAM_NAME);
         break;
      case VIX_COMMAND_CREATE_TEMPORARY_FILE:
      case VIX_COMMAND_CREATE_TEMPORARY_FILE_EX:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                   VIX_TOOLS_CONFIG_API_CREATE_TMP_FILE_NAME);
         break;
      case VIX_COMMAND_CREATE_TEMPORARY_DIRECTORY:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_CREATE_TMP_DIRECTORY_NAME);
         break;
      case VIX_COMMAND_READ_ENV_VARIABLES:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_READ_ENV_VARS_NAME);
         break;
      case VIX_COMMAND_SET_GUEST_FILE_ATTRIBUTES:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_CHANGE_FILE_ATTRS_NAME);
         break;

      case VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_INITIATE_FILE_TRANSFER_FROM_GUEST_NAME);
         break;

      case VIX_COMMAND_INITIATE_FILE_TRANSFER_TO_GUEST:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_INITIATE_FILE_TRANSFER_TO_GUEST_NAME);
         break;

      case VIX_COMMAND_VALIDATE_CREDENTIALS:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_VALIDATE_CREDENTIALS_NAME);
         break;

      case VIX_COMMAND_ACQUIRE_CREDENTIALS:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_ACQUIRE_CREDENTIALS_NAME);
         break;

      case VIX_COMMAND_RELEASE_CREDENTIALS:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_RELEASE_CREDENTIALS_NAME);
         break;

      case VIX_COMMAND_ADD_AUTH_ALIAS:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_ADD_GUEST_ALIAS_NAME);
         break;

      case VIX_COMMAND_REMOVE_AUTH_ALIAS:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                               VIX_TOOLS_CONFIG_API_REMOVE_GUEST_ALIAS_NAME);
         break;

      case VIX_COMMAND_REMOVE_AUTH_ALIAS_BY_CERT:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                               VIX_TOOLS_CONFIG_API_REMOVE_GUEST_ALIAS_BY_CERT_NAME);
         break;

      case VIX_COMMAND_LIST_AUTH_PROVIDER_ALIASES:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_LIST_GUEST_ALIASES_NAME);
         break;

      case VIX_COMMAND_LIST_AUTH_MAPPED_ALIASES:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                              VIX_TOOLS_CONFIG_API_LIST_GUEST_MAPPED_ALIASES_NAME);
         break;

      case VIX_COMMAND_CREATE_REGISTRY_KEY:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_CREATE_REGISTRY_KEY_NAME);
         break;

      case VIX_COMMAND_LIST_REGISTRY_KEYS:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_LIST_REGISTRY_KEYS_NAME);
         break;

      case VIX_COMMAND_DELETE_REGISTRY_KEY:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_DELETE_REGISTRY_KEY_NAME);
         break;

      case VIX_COMMAND_SET_REGISTRY_VALUE:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_SET_REGISTRY_VALUE_NAME);
         break;

      case VIX_COMMAND_LIST_REGISTRY_VALUES:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_LIST_REGISTRY_VALUES_NAME);
         break;

      case VIX_COMMAND_DELETE_REGISTRY_VALUE:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef,
                                VIX_TOOLS_CONFIG_API_DELETE_REGISTRY_VALUE_NAME);
         break;

      /*
       * None of these opcode have a matching config entry (yet),
       * so they can all share.
       */
      case VIX_COMMAND_CHECK_USER_ACCOUNT:
      case VIX_COMMAND_LOGOUT_IN_GUEST:
      case VIX_COMMAND_GUEST_FILE_EXISTS:
      case VIX_COMMAND_DIRECTORY_EXISTS:
      case VIX_COMMAND_GET_FILE_INFO:
      case VIX_COMMAND_LIST_FILESYSTEMS:
      case VIX_COMMAND_READ_VARIABLE:
      case VIX_COMMAND_WRITE_VARIABLE:
      case VIX_COMMAND_GET_GUEST_NETWORKING_CONFIG:
      case VIX_COMMAND_SET_GUEST_NETWORKING_CONFIG:

      case VIX_COMMAND_REGISTRY_KEY_EXISTS:
      case VIX_COMMAND_READ_REGISTRY:
      case VIX_COMMAND_WRITE_REGISTRY:
      case VIX_COMMAND_DELETE_GUEST_REGISTRY_KEY:

      /*
       * These may want to use the VMODL API name that most closely
       * matches, but for now, leave them alone.
       */
      case VIX_COMMAND_RUN_SCRIPT_IN_GUEST:
      case VIX_COMMAND_RUN_PROGRAM:
      case VIX_COMMAND_LIST_DIRECTORY:
      case VMXI_HGFS_SEND_PACKET_COMMAND:
      default:
         enabled = !VixToolsGetAPIDisabledFromConf(confDictRef, NULL);
         break;
   }

   return enabled;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_ProcessVixCommand --
 *
 *
 * Return value:
 *    VIX_OK on success
 *    VixError on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixTools_ProcessVixCommand(VixCommandRequestHeader *requestMsg,   // IN
                           char *requestName,                     // IN
                           size_t maxResultBufferSize,            // IN
                           GKeyFile *confDictRef,                 // IN
                           GMainLoop *eventQueue,                 // IN
                           char **resultBuffer,                   // OUT
                           size_t *resultLen,                     // OUT
                           Bool *deleteResultBufferResult)        // OUT
{
   VixError err = VIX_OK;
   char *resultValue = NULL;
   size_t resultValueLength = 0;
   Bool mustSetResultValueLength = TRUE;
   Bool deleteResultValue = FALSE;


   if (NULL != resultBuffer) {
      *resultBuffer = NULL;
   }
   if (NULL != resultLen) {
      *resultLen = 0;
   }
   if (NULL != deleteResultBufferResult) {
      *deleteResultBufferResult = FALSE;
   }

   g_message("%s: command %d\n", __FUNCTION__, requestMsg->opCode);

   /*
    * PR 1210773: Check if new VIX commands can be processed.
    *
    * Most of the VIX commands require access to the guest
    * filesystem and therefore they could block when quiesced
    * snapshot operation has frozen the guest filesystem. A
    * blocked VIX command would not allow Tools service to
    * process other important ops like resuming filesystem
    * because Tools service is single threaded. Effectively,
    * a VIX command could deadlock a quiesce snapshot operation.
    *
    * A quiesce snapshot operation that follows a long running
    * VIX command like runprogram/startprogram is not an issue
    * because the running command gets blocked temporarily
    * only when it needs to access the filesystem, otherwise
    * it continues to run like any other application inside
    * guest.
    *
    * Return a generic error to make clients retry the command
    * in a graceful manner.
    */
   if (gRestrictCommands) {
      g_warning("%s: IO freeze restricted command %d\n",
                __FUNCTION__, requestMsg->opCode);
      err = VIX_E_OBJECT_IS_BUSY;
      goto quit;
   }

   /*
    * Set the global reference to configuration dictionary.
    * We do this to avoid passing this reference through multiple
    * interfaces for consumers like VixToolsImpersonateUser().
    *
    * ASSUMPTION: We are single threaded here, so we don't need
    * to acquire any locks for this step.
    */
   ASSERT(confDictRef != NULL);
   gConfDictRef = confDictRef;

   if (!VixToolsCheckIfVixCommandEnabled(requestMsg->opCode, confDictRef)) {
      err = VIX_E_OPERATION_DISABLED;
      g_message("%s: command %d disabled by configuration\n",
                __FUNCTION__, requestMsg->opCode);
      goto quit;
   }

   switch (requestMsg->opCode) {
      ////////////////////////////////////
      case VIX_COMMAND_CHECK_USER_ACCOUNT:
      case VIX_COMMAND_LOGOUT_IN_GUEST:
         err = VixToolsCheckUserAccount(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_GET_TOOLS_STATE:
         err = VixTools_GetToolsPropertiesImpl(confDictRef,
                                               &resultValue,
                                               &resultValueLength);
         if (VIX_FAILED(err)) {
            /*
             * VixTools_GetToolsPropertiesImpl failed, so resultVal is still NULL,
             * so let it get replaced with the empty string at the quit label.
             */
            goto quit;
         }

         /*
          * resultVal always points to something heap-allocated after this point
          */
         deleteResultValue = TRUE;

         err = VixTools_Base64EncodeBuffer(&resultValue, &resultValueLength);
         mustSetResultValueLength = FALSE;

         break;

      ////////////////////////////////////
      case VIX_COMMAND_LIST_PROCESSES:
         err = VixToolsListProcesses(requestMsg,
                                     maxResultBufferSize,
                                     &resultValue);
         // resultValue is static. Do not free it.
         break;

      ////////////////////////////////////
      case VIX_COMMAND_LIST_PROCESSES_EX:
         err = VixToolsListProcessesEx(requestMsg,
                                       maxResultBufferSize,
                                       confDictRef,
                                       eventQueue,
                                       &resultValue);
         deleteResultValue = TRUE;
         break;

      ////////////////////////////////////
      case VIX_COMMAND_LIST_DIRECTORY:
         err = VixToolsListDirectory(requestMsg,
                                     maxResultBufferSize,
                                     &resultValue);
         deleteResultValue = TRUE;
         break;

      ////////////////////////////////////
      case VIX_COMMAND_LIST_FILES:
         err = VixToolsListFiles(requestMsg,
                                 maxResultBufferSize,
                                 &resultValue);
         deleteResultValue = TRUE;
         break;
      ////////////////////////////////////
      case VIX_COMMAND_DELETE_GUEST_FILE:
      case VIX_COMMAND_DELETE_GUEST_FILE_EX:
      case VIX_COMMAND_DELETE_GUEST_REGISTRY_KEY:
      case VIX_COMMAND_DELETE_GUEST_DIRECTORY:
      case VIX_COMMAND_DELETE_GUEST_EMPTY_DIRECTORY:
         err = VixToolsDeleteObject(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_DELETE_GUEST_DIRECTORY_EX:
         err = VixToolsDeleteDirectory(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_REGISTRY_KEY_EXISTS:
      case VIX_COMMAND_GUEST_FILE_EXISTS:
      case VIX_COMMAND_DIRECTORY_EXISTS:
         err = VixToolsObjectExists(requestMsg, &resultValue);
         // resultValue is static. Do not free it.
         break;

      ////////////////////////////////////
      case VIX_COMMAND_READ_REGISTRY:
         err = VixToolsReadRegistry(requestMsg, &resultValue);
         deleteResultValue = TRUE;
         break;

      ////////////////////////////////////
      case VIX_COMMAND_WRITE_REGISTRY:
         err = VixToolsWriteRegistry(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_KILL_PROCESS:
      case VIX_COMMAND_TERMINATE_PROCESS:
         err = VixToolsKillProcess(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_CREATE_DIRECTORY:
      case VIX_COMMAND_CREATE_DIRECTORY_EX:
         err = VixToolsCreateDirectory(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_MOVE_GUEST_FILE:
      case VIX_COMMAND_MOVE_GUEST_FILE_EX:
      case VIX_COMMAND_MOVE_GUEST_DIRECTORY:
         err = VixToolsMoveObject(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_RUN_SCRIPT_IN_GUEST:
         err = VixToolsRunScript(requestMsg, requestName, eventQueue, &resultValue);
         // resultValue is static. Do not free it.
         break;

      ////////////////////////////////////
      case VIX_COMMAND_RUN_PROGRAM:
         err = VixTools_RunProgram(requestMsg, requestName, eventQueue, &resultValue);
         // resultValue is static. Do not free it.
         break;

      ////////////////////////////////////
      case VIX_COMMAND_START_PROGRAM:
         err = VixTools_StartProgram(requestMsg, requestName, eventQueue, &resultValue);
         // resultValue is static. Do not free it.
         break;

      ////////////////////////////////////
      case VIX_COMMAND_CREATE_TEMPORARY_FILE:
      case VIX_COMMAND_CREATE_TEMPORARY_FILE_EX:
      case VIX_COMMAND_CREATE_TEMPORARY_DIRECTORY:
         err = VixToolsCreateTempFile(requestMsg, &resultValue);
         deleteResultValue = TRUE;
         break;

      ///////////////////////////////////
      case VIX_COMMAND_READ_VARIABLE:
         err = VixToolsReadVariable(requestMsg, &resultValue);
         deleteResultValue = TRUE;
         break;

      ///////////////////////////////////
      case VIX_COMMAND_READ_ENV_VARIABLES:
         err = VixToolsReadEnvVariables(requestMsg, &resultValue);
         deleteResultValue = TRUE;
         break;

      ///////////////////////////////////
      case VIX_COMMAND_WRITE_VARIABLE:
         err = VixToolsWriteVariable(requestMsg);
         break;

      ///////////////////////////////////
      case VIX_COMMAND_GET_FILE_INFO:
         err = VixToolsGetFileInfo(requestMsg, &resultValue);
         deleteResultValue = TRUE;
         break;

      ///////////////////////////////////
      case VIX_COMMAND_SET_GUEST_FILE_ATTRIBUTES:
         err = VixToolsSetFileAttributes(requestMsg);
         break;

      ///////////////////////////////////
      case VMXI_HGFS_SEND_PACKET_COMMAND:
         err = VixToolsProcessHgfsPacket((VixCommandHgfsSendPacket *) requestMsg,
                                         eventQueue,
                                         &resultValue,
                                         &resultValueLength);
         deleteResultValue = FALSE; // TRUE;
         mustSetResultValueLength = FALSE;
         break;

#if defined(__linux__) || defined(_WIN32)
      ////////////////////////////////////
      case VIX_COMMAND_GET_GUEST_NETWORKING_CONFIG:
         err = VixToolsGetGuestNetworkingConfig(requestMsg,
                                                &resultValue,
                                                &resultValueLength);
         if (VIX_FAILED(err)) {
            /*
             * VixToolsGetGuestNetworkingConfig() failed, so resultVal is still NULL,
             * so let it get replaced with the empty string at the quit label.
             */
            goto quit;
         }

         /*
          * resultVal always points to something heap-allocated after this point
          */
         deleteResultValue = TRUE;
         mustSetResultValueLength = FALSE;
         break;
#endif

#if defined(_WIN32)
      ////////////////////////////////////
      case VIX_COMMAND_SET_GUEST_NETWORKING_CONFIG:
         err = VixToolsSetGuestNetworkingConfig(requestMsg);
         break;
#endif

      ////////////////////////////////////
      case VIX_COMMAND_LIST_FILESYSTEMS:
         err = VixToolsListFileSystems(requestMsg, &resultValue);
         // resultValue is static. Do not free it.
         break;

      ////////////////////////////////////
      case VIX_COMMAND_INITIATE_FILE_TRANSFER_FROM_GUEST:
         err = VixToolsInitiateFileTransferFromGuest(requestMsg,
                                                     &resultValue);
         deleteResultValue = TRUE;
         break;

      ////////////////////////////////////
      case VIX_COMMAND_INITIATE_FILE_TRANSFER_TO_GUEST:
         err = VixToolsInitiateFileTransferToGuest(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_VALIDATE_CREDENTIALS:
         err = VixToolsValidateCredentials(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_ACQUIRE_CREDENTIALS:
         err = VixToolsAcquireCredentials(requestMsg, eventQueue, &resultValue);
         // resultValue is static. Do not free it.
         break;

      ////////////////////////////////////
      case VIX_COMMAND_RELEASE_CREDENTIALS:
         err = VixToolsReleaseCredentials(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_WAIT_FOR_TOOLS:
         /*
          * Older VMX's can send this.  We don't want to do anything, but
          * we also don't want it to be treated as unknown and return
          * VIX_E_UNRECOGNIZED_COMMAND_IN_GUEST.
          */
         break;

      case VIX_COMMAND_CAPTURE_SCREEN:
         /*
          * The VMX sends this through just to validate the auth info.
          * Just no-op it so we don't fall through to the 'default'.
          */
         break;

#if SUPPORT_VGAUTH
      case VIX_COMMAND_ADD_AUTH_ALIAS:
         err = VixToolsAddAuthAlias(requestMsg);
         break;
      case VIX_COMMAND_REMOVE_AUTH_ALIAS:
      case VIX_COMMAND_REMOVE_AUTH_ALIAS_BY_CERT:
         err = VixToolsRemoveAuthAlias(requestMsg);
         break;
      case VIX_COMMAND_LIST_AUTH_PROVIDER_ALIASES:
          err = VixToolsListAuthAliases(requestMsg, maxResultBufferSize,
                                        &resultValue);
         // resultValue is static. Do not free it.
         break;
      case VIX_COMMAND_LIST_AUTH_MAPPED_ALIASES:
          err = VixToolsListMappedAliases(requestMsg, maxResultBufferSize,
                                          &resultValue);
         // resultValue is static. Do not free it.
         break;
#endif

      ////////////////////////////////////
      case VIX_COMMAND_CREATE_REGISTRY_KEY:
         err = VixToolsCreateRegKey(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_LIST_REGISTRY_KEYS:
         err = VixToolsListRegKeys(requestMsg,
                                   maxResultBufferSize,
                                   eventQueue,
                                   &resultValue);
         deleteResultValue = TRUE;
         break;

      ////////////////////////////////////
      case VIX_COMMAND_DELETE_REGISTRY_KEY:
         err = VixToolsDeleteRegKey(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_SET_REGISTRY_VALUE:
         err = VixToolsSetRegValue(requestMsg);
         break;

      ////////////////////////////////////
      case VIX_COMMAND_LIST_REGISTRY_VALUES:
         err = VixToolsListRegValues(requestMsg,
                                     maxResultBufferSize,
                                     eventQueue,
                                     &resultValue);
         deleteResultValue = TRUE;
         break;

      ////////////////////////////////////
      case VIX_COMMAND_DELETE_REGISTRY_VALUE:
         err = VixToolsDeleteRegValue(requestMsg);
         break;

      ////////////////////////////////////
      default:
         /*
          * If the opcode is not recognized, tools might be old and the
          * VIX client might be sending new opcodes. In such case,
          * we should return VIX_E_UNRECOGNIZED_COMMAND_IN_GUEST.
          */
         err = VIX_E_UNRECOGNIZED_COMMAND_IN_GUEST;
         break;
   } // switch (requestMsg->opCode)

quit:
   if (NULL == resultValue) {
      // Prevent "(null)" from getting sprintf'ed into the result buffer
      resultValue = "";
      deleteResultValue = FALSE;
   }

   /*
    * Some commands return both a result and its length. Some return just
    * the result. Others return nothing at all. Previously, we assumed that
    * all results are based on plain-text, but this is incorrect (for example,
    * VixToolsProcessHgfsPacket will return a binary packet).
    *
    * Instead, let's assume that commands returning without a length are based
    * on plain-text. This seems reasonable, because any binary result must
    * provide a length if one is to make sense of it.
    */
   if (mustSetResultValueLength) {
      resultValueLength = strlen(resultValue);
   }

   if (NULL != resultBuffer) {
      *resultBuffer = resultValue;
   }
   if (NULL != resultLen) {
      *resultLen = resultValueLength;
   }
   if (NULL != deleteResultBufferResult) {
      *deleteResultBufferResult = deleteResultValue;
   }

   /*
    * Remaps specific errors for backward compatibility purposes.
    */
   err = VixToolsRewriteError(requestMsg->opCode, err);

   /*
    * Reset the global reference to configuration dictionary
    */
   gConfDictRef = NULL;

   return(err);
} // VixTools_ProcessVixCommand


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsRewriteError --
 *
 *    Rewrites the error if necessary.
 *
 *    Some errors returned by tools need to be changed so
 *    that error code consistency with old VIX is maintained.
 *
 *    So specific errors from specific operations are rewritten here.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsRewriteError(uint32 opCode,          // IN
                     VixError origError)     // IN
{
   VixError newError = origError;

   switch (opCode) {
      /*
       * This should include all non-VI guest operations.
       */
   case VIX_COMMAND_CHECK_USER_ACCOUNT:
   case VIX_COMMAND_LOGOUT_IN_GUEST:
   case VIX_COMMAND_GET_TOOLS_STATE:
   case VIX_COMMAND_LIST_PROCESSES:
   case VIX_COMMAND_LIST_DIRECTORY:
   case VIX_COMMAND_DELETE_GUEST_FILE:
   case VIX_COMMAND_DELETE_GUEST_REGISTRY_KEY:
   case VIX_COMMAND_DELETE_GUEST_DIRECTORY:
   case VIX_COMMAND_DELETE_GUEST_EMPTY_DIRECTORY:
   case VIX_COMMAND_REGISTRY_KEY_EXISTS:
   case VIX_COMMAND_GUEST_FILE_EXISTS:
   case VIX_COMMAND_DIRECTORY_EXISTS:
   case VIX_COMMAND_READ_REGISTRY:
   case VIX_COMMAND_WRITE_REGISTRY:
   case VIX_COMMAND_KILL_PROCESS:
   case VIX_COMMAND_CREATE_DIRECTORY:
   case VIX_COMMAND_MOVE_GUEST_FILE:
   case VIX_COMMAND_RUN_SCRIPT_IN_GUEST:
   case VIX_COMMAND_RUN_PROGRAM:
   case VIX_COMMAND_CREATE_TEMPORARY_FILE:
   case VIX_COMMAND_READ_VARIABLE:
   case VIX_COMMAND_WRITE_VARIABLE:
   case VIX_COMMAND_GET_FILE_INFO:
   case VMXI_HGFS_SEND_PACKET_COMMAND:
   case VIX_COMMAND_GET_GUEST_NETWORKING_CONFIG:
   case VIX_COMMAND_LIST_FILESYSTEMS:
   case VIX_COMMAND_WAIT_FOR_TOOLS:
   case VIX_COMMAND_CAPTURE_SCREEN:
      ASSERT(VIX_ERROR_CODE(origError) == origError);
      switch (origError) {
      case VIX_E_INVALID_LOGIN_CREDENTIALS:
         newError = VIX_E_GUEST_USER_PERMISSIONS;
         break;
      }
      break;
   }

   return newError;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_GetAdditionalError --
 *
 *    Gets the vix extra/additional error if any.
 *
 *    Some errors returned by tools may have extra error in
 *    the higher order 32 bits. We need to pass that back.
 *
 * Results:
 *      uint32
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

uint32
VixTools_GetAdditionalError(uint32 opCode,    // IN
                            VixError error)   // IN
{
   uint32 err;

   switch (opCode) {
      case VIX_COMMAND_CREATE_REGISTRY_KEY:
      case VIX_COMMAND_LIST_REGISTRY_KEYS:
      case VIX_COMMAND_DELETE_REGISTRY_KEY:
      case VIX_COMMAND_SET_REGISTRY_VALUE:
      case VIX_COMMAND_LIST_REGISTRY_VALUES:
      case VIX_COMMAND_DELETE_REGISTRY_VALUE:
         err = VIX_ERROR_EXTRA_ERROR(error);
         break;
      default:
        err = Err_Errno();
   }

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixTools_Base64EncodeBuffer --
 *
 * Return value:
 *    VIX_OK on success
 *    VixError on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixTools_Base64EncodeBuffer(char **resultValuePtr,      // IN/OUT
                            size_t *resultValLengthPtr) // IN/OUT
{
   VixError err = VIX_OK;
   char *base64Buffer = NULL;
   size_t base64BufferLength = 0;
   Bool success = FALSE;

   ASSERT(resultValuePtr != NULL);
   ASSERT(*resultValuePtr != NULL);
   ASSERT(resultValLengthPtr != NULL);

   base64BufferLength = Base64_EncodedLength(*resultValuePtr, *resultValLengthPtr) + 1;
   base64Buffer = Util_SafeMalloc(base64BufferLength);
   success = Base64_Encode(*resultValuePtr,
                           *resultValLengthPtr,
                           base64Buffer,
                           base64BufferLength,
                           &base64BufferLength);
   if (!success) {
      (*resultValuePtr)[0] = 0;
      free(base64Buffer);
      base64Buffer = NULL;
      err = VIX_E_FAIL;
      goto quit;
   }

   base64Buffer[base64BufferLength] = 0;

   free(*resultValuePtr);
   *resultValuePtr = base64Buffer;
   *resultValLengthPtr = base64BufferLength;

quit:
   return err;

} // VixTools_Base64EncodeBuffer


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsEnableDHCPOnPrimary --
 *
 *      Enable DHCP on primary NIC. A primary NIC is the
 *      first interface you get using ipconfig. You can change the order
 *      of NIC cards on a computer via Windows GUI.
 *
 * Results:
 *      S_OK on success.  COM error codes on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

#if defined(_WIN32)
HRESULT
VixToolsEnableDHCPOnPrimary(void)
{
   HRESULT ret;
   GuestNic *primaryNic;

   primaryNic = NetUtil_GetPrimaryNic();
   if (NULL == primaryNic) {
      return HRESULT_FROM_WIN32(GetLastError());
   }

   ret = WMI_EnableDHCP(primaryNic->macAddress);
   VMX_XDR_FREE(xdr_GuestNic, primaryNic);
   free(primaryNic);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsEnableStaticOnPrimary --
 *
 *      Set the IP address and/or subnet mask of the primary NIC. A primary NIC
 *      is the first interface you get using ipconfig. You can change the order
 *      of NIC cards on a computer via Windows GUI.
 *
 * Results:
 *      S_OK on success.  COM error codes on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

HRESULT
VixToolsEnableStaticOnPrimary(const char *ipAddr,       // IN
                              const char *subnetMask)   // IN
{
   HRESULT ret;
   GuestNic *primaryNic;
   VmIpAddress *primaryIp;
   char actualIpAddress[IP_ADDR_SIZE];
   char actualSubnetMask[IP_ADDR_SIZE];

   if ((NULL == ipAddr) ||
       (NULL == subnetMask)) {
      return E_INVALIDARG;
   }

   actualIpAddress[0] = '\0';
   actualSubnetMask[0] = '\0';

   primaryNic = NetUtil_GetPrimaryNic();
   if (NULL == primaryNic) {
      return HRESULT_FROM_WIN32(GetLastError());
   }

   /*
    * Set IP address if client provides it.
    */

   primaryIp = &primaryNic->ips.ips_val[0];

   if ('\0' != ipAddr[0]) {
      Str_Strcpy(actualIpAddress,
                 ipAddr,
                 sizeof actualIpAddress);
   } else {
      Str_Strcpy(actualIpAddress,
                 primaryIp->ipAddress,
                 sizeof actualIpAddress);
   }

   /*
    * Set subnet mask if client provides it.
    */
   if ('\0' != subnetMask[0]) {
      Str_Strcpy(actualSubnetMask,
                 subnetMask,
                 sizeof actualSubnetMask);
   } else {
      Str_Strcpy(actualSubnetMask,
                 primaryIp->subnetMask,
                 sizeof actualSubnetMask);
   }

   ret = WMI_EnableStatic(primaryNic->macAddress,
                          actualIpAddress,
                          actualSubnetMask);

   VMX_XDR_FREE(xdr_GuestNic, primaryNic);
   free(primaryNic);
   return ret;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsEscapeXMLString --
 *
 *      Escapes a string to be included in VMAutomation XML.
 *
 * Results:
 *      Pointer to a heap-allocated escaped string.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
VixToolsEscapeXMLString(const char *str)    // IN
{
   /*
    * Escape the escape character (%) and the five characters that are XML
    * sensitive - ', ", &, < and >.
    */

   static const int bytesToEscape[] = {
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,   // ", %, & and '
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,   // < and >
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   };

   return Escape_Do(VIX_XML_ESCAPE_CHARACTER, bytesToEscape, str, strlen(str),
                    NULL);
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsXMLStringEscapedLen --
 *
 *      Computes the length of the supplied string if it were escaped
 *      (if escapeStr is TRUE), or the length of the string as is.
 *
 * Results:
 *      The length.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static size_t
VixToolsXMLStringEscapedLen(const char *str,    // IN
                            Bool escapeStr)     // IN
{
   if (escapeStr) {
      size_t totalLen = 0;

      while (TRUE) {
         size_t nextLen = strcspn(str, "%<>&\'\"");

         totalLen += nextLen;
         if ('\0' == str[nextLen]) {
            break;
         }

         /*
          * str[nextLen] is a character that needs to be escaped. Each
          * escapeStr that is escaped will take up 3 bytes (an escape
          * character and two hex digits) in the escaped string.
          */

         totalLen += 3;
         str += nextLen + 1;
      }

      return totalLen;
   } else {
      return strlen(str);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAuthEnabled --
 *
 *      Returns whether we use the guest auth library.
 *
 * Results:
 *      TRUE if we do. FALSE otherwise.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
GuestAuthEnabled(void)
{
#if SUPPORT_VGAUTH
   return gSupportVGAuth;
#else
   return FALSE;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAuthPasswordAuthenticateImpersonate
 *
 *      Do name-password authentication and impersonation using
 *      the GuestAuth library.
 *
 * Results:
 *      VIX_OK if successful.Other VixError code otherwise.
 *
 * Side effects:
 *      Current process impersonates.
 *
 *-----------------------------------------------------------------------------
 */

VixError
GuestAuthPasswordAuthenticateImpersonate(
   char const *obfuscatedNamePassword, // IN
   Bool loadUserProfile,               // IN
   void **userToken)                   // OUT
{
#if SUPPORT_VGAUTH
   VixError err;
   char *username = NULL;
   char *password = NULL;
   VGAuthContext *ctx = NULL;
   VGAuthError vgErr;
   VGAuthUserHandle *newHandle = NULL;
   VGAuthExtraParams extraParams[1];
   Bool impersonated = FALSE;

   extraParams[0].name = VGAUTH_PARAM_LOAD_USER_PROFILE;
   extraParams[0].value = loadUserProfile ? VGAUTH_PARAM_VALUE_TRUE :
                                            VGAUTH_PARAM_VALUE_FALSE;

   err = VixMsg_DeObfuscateNamePassword(obfuscatedNamePassword,
                                        &username,
                                        &password);
   if (err != VIX_OK) {
      goto done;
   }

   err = VIX_E_INVALID_LOGIN_CREDENTIALS;

   vgErr = TheVGAuthContext(&ctx);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }

   vgErr = VGAuth_ValidateUsernamePassword(ctx, username, password,
                                           0, NULL,
                                           &newHandle);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }

   vgErr = VGAuth_Impersonate(ctx, newHandle,
                              (int)ARRAYSIZE(extraParams),
                              extraParams);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }

   impersonated = TRUE;

#ifdef _WIN32
   // this is making a copy of the token, be sure to close it
   vgErr = VGAuth_UserHandleAccessToken(ctx, newHandle, userToken);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }
#endif

   currentUserHandle = newHandle;
   gImpersonatedUsername = Util_SafeStrdup(username);

   err = VIX_OK;

done:
   free(username);
   Util_ZeroFreeString(password);

   if (VIX_OK != err) {
      if (impersonated) {

         /*
          * Coverity flags this as dead code on non-Windows platforms,
          * where impersonated can't be TRUE if VIX_OK != err.
          */
         /* coverity[dead_error_begin] */
         vgErr = VGAuth_EndImpersonation(ctx);
         ASSERT(vgErr == VGAUTH_E_OK);
      }
      VGAuth_UserHandleFree(newHandle);
      newHandle = NULL;
   }
   return err;
#else
   return VIX_E_NOT_SUPPORTED;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAuthSAMLAuthenticateAndImpersonate
 *
 *      Do SAML bearer token authentication and impersonation using
 *      the GuestAuth library.
 *
 * Results:
 *      VIX_OK if successful.  Other VixError code otherwise.
 *
 * Side effects:
 *      Current process impersonates.
 *
 *-----------------------------------------------------------------------------
 */

VixError
GuestAuthSAMLAuthenticateAndImpersonate(
   char const *obfuscatedNamePassword, // IN
   Bool loadUserProfile,               // IN
   Bool hostVerified,                  // IN
   void **userToken)                   // OUT
{
#if SUPPORT_VGAUTH
   VixError err;
   char *token = NULL;
   char *username = NULL;
   VGAuthContext *ctx = NULL;
   VGAuthError vgErr;
   VGAuthUserHandle *newHandle = NULL;
   VGAuthExtraParams extraParams[1];
   VGAuthExtraParams hostVerfiedParams[1];
   Bool impersonated = FALSE;

   extraParams[0].name = VGAUTH_PARAM_LOAD_USER_PROFILE;
   extraParams[0].value = loadUserProfile ? VGAUTH_PARAM_VALUE_TRUE :
                                            VGAUTH_PARAM_VALUE_FALSE;

   err = VixMsg_DeObfuscateNamePassword(obfuscatedNamePassword,
                                        &token,
                                        &username);
   if (err != VIX_OK) {
      goto done;
   }

   err = VIX_E_INVALID_LOGIN_CREDENTIALS;

   vgErr = TheVGAuthContext(&ctx);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }

   hostVerfiedParams[0].name = VGAUTH_PARAM_SAML_HOST_VERIFIED;
   hostVerfiedParams[0].value = hostVerified ? VGAUTH_PARAM_VALUE_TRUE :
                                               VGAUTH_PARAM_VALUE_FALSE;
   vgErr = VGAuth_ValidateSamlBearerToken(ctx,
                                          token,
                                          username,
                                          (int)ARRAYSIZE(hostVerfiedParams),
                                          hostVerfiedParams,
                                          &newHandle);
#if ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS
   /*
    * Special support for local SYSTEM account.
    *
    * If validation fails, try again without token
    * creation, and if it passes, fake the impersonation.
    */

   // normal case worked
   if (!VGAUTH_FAILED(vgErr)) {
      goto impersonate;
   }

   /*
    * If the config is off, bypass the special-case.
    */
   if (!VMTools_ConfigGetBoolean(gConfDictRef,
                      VIX_TOOLS_CONFIG_API_GROUPNAME,
                      VIXTOOLS_CONFIG_ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS,
                      ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS_DEFAULT)) {
      g_debug("%s: SAML authn failed, %s not set, skipping local SYSTEM check",
              __FUNCTION__,
              VIXTOOLS_CONFIG_ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS);
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }

   /*
    * VGAUTH_E_FAIL will be returned if token creation fails.
    */
   if (vgErr != VGAUTH_E_FAIL) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   } else {
      /*
       * See if we have a SAML token associated with the toolsd owner.
       * If this returns OK, we don't bother to impersonate.
       * If it fails, return an error.
       */
      err = VixToolsCheckSAMLForSystem(ctx,
                                       vgErr,
                                       token,
                                       username,
                                       gCurrentUsername,
                                       hostVerified,
                                       userToken,
                                       &currentUserHandle);
      goto done;
   }
#else
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }
#endif

#if ALLOW_LOCAL_SYSTEM_IMPERSONATION_BYPASS
impersonate:
#endif
   vgErr = VGAuth_Impersonate(ctx, newHandle,
                              (int)ARRAYSIZE(extraParams),
                              extraParams);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }

   impersonated = TRUE;

#ifdef _WIN32
   // this is making a copy of the token, be sure to close it
   vgErr = VGAuth_UserHandleAccessToken(ctx, newHandle, userToken);
   if (VGAUTH_FAILED(vgErr)) {
      err = VixToolsTranslateVGAuthError(vgErr);
      goto done;
   }
#endif

   currentUserHandle = newHandle;
   gImpersonatedUsername = VixToolsGetImpersonatedUsername(NULL);

   err = VIX_OK;

done:
   Util_ZeroFreeString(token);
   Util_ZeroFreeString(username);

   if (VIX_OK != err) {
      if (impersonated) {

         /*
          * Coverity flags this as dead code on non-Windows platforms,
          * where impersonated can't be TRUE if VIX_OK != err.
          */
         /* coverity[dead_error_begin] */
         vgErr = VGAuth_EndImpersonation(ctx);
         ASSERT(vgErr == VGAUTH_E_OK);
      }
      VGAuth_UserHandleFree(newHandle);
      newHandle = NULL;
   }

   return err;
#else
   return VIX_E_NOT_SUPPORTED;
#endif
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestAuthUnimpersonate
 *
 *      End the current impersonation using the VGAuth library.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      Current process un-impersonates.
 *
 *-----------------------------------------------------------------------------
 */

void
GuestAuthUnimpersonate(void)
{
#if SUPPORT_VGAUTH
   VGAuthContext *ctx;
   VGAuthError vgErr = TheVGAuthContext(&ctx);
   ASSERT(vgErr == VGAUTH_E_OK);


   vgErr = VGAuth_EndImpersonation(ctx);
   ASSERT(vgErr == VGAUTH_E_OK);

#else
   ASSERT(0);
#endif
}


#if SUPPORT_VGAUTH
/*
 *-----------------------------------------------------------------------------
 *
 * QueryVGAuthConfig
 *
 *      Check the tools configuration to see if VGAuth should be used.
 *
 * Results:
 *      TRUE if vgauth should be used, FALSE if not.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
QueryVGAuthConfig(GKeyFile *confDictRef)                       // IN
{
   gboolean retVal = USE_VGAUTH_DEFAULT;

   if (confDictRef != NULL) {
      retVal = VMTools_ConfigGetBoolean(confDictRef,
                                        VIX_TOOLS_CONFIG_API_GROUPNAME,
                                        VIXTOOLS_CONFIG_USE_VGAUTH_NAME,
                                        USE_VGAUTH_DEFAULT);
   }

   g_message("%s: vgauth usage is: %d\n", __FUNCTION__, retVal);

   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * TheVGAuthContext
 *
 *      Get the global VGAuthContext object.
 *
 *      Lazily create the global VGAuthContext when needed.
 *      We need a single shared context to handle authentication in order to
 *      properly share the SSPI handshake state(s).
 *
 *      Creating the global context may also cause the VGAuth Service to
 *      be started.
 *
 *      This context should only be used when not impersonating, since it
 *      will be running over the SUPER_USER connection and can cause
 *      security issues if used when impersonating.
 *
 *
 * Results:
 *      VGAUTH_E_OK if successful, the global context object is returned in
 *      the OUT parameter ctx.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VGAuthError
TheVGAuthContext(VGAuthContext **ctx) // OUT
{
   static VGAuthContext *vgaCtx = NULL;
   VGAuthError vgaCode = VGAUTH_E_OK;

   /*
    * XXX This needs to handle errors better -- if the VGAuthService
    * service gets reset, the context will point to junk and anything
    * using it will fail.
    *
    * Maybe add a no-op API here to poke it?  Or make the underlying
    * VGAuth code smarter.
    */
   if (vgaCtx == NULL) {
      vgaCode = VGAuth_Init(VMTOOLSD_APP_NAME, 0, NULL, &vgaCtx);
   }

   *ctx = vgaCtx;
   return vgaCode;
}


#ifdef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * GuestAuthUnloadUserProfileAndToken --
 *
 *    Unload user profile and close user token.
 *
 *    Helper to handle StartProgram cleanup.
 *
 * Return value:
 *    None
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static void
GuestAuthUnloadUserProfileAndToken(HANDLE hToken,
                                   HANDLE hProfile)
{
   if (GuestAuthEnabled()) {
      g_debug("%s: special-case profile unload %p\n", __FUNCTION__, hProfile);
      if (!UnloadUserProfile(hToken, hProfile)) {
         g_warning("%s: UnloadUserProfile() failed %d\n",
                    __FUNCTION__, GetLastError());
      }
      CloseHandle(hToken);
   }
}
#endif // _WIN32

#endif // SUPPORT_VGAUTH

  070701000003A7000081A40000000000000000000000016822550500004346000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix/vixToolsEnvVars.c /*********************************************************
 * Copyright (c) 2010-2018, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vixToolsEnvVars.c --
 *
 *      Routines that encapsulate the complexity of dealing with
 *      environment variables when the process may be impersonating
 *      a user.
 */

#include <stdlib.h>
#ifdef __APPLE__
#include <crt_externs.h>
#endif

#include "util.h"
#include "unicode.h"
#include "dynbuf.h"
#include "str.h"
#include "posix.h"
#include "vixToolsInt.h"


#ifndef _WIN32
extern char **environ;
#endif

struct VixToolsEnvIterator {
#ifdef _WIN32
   enum {
      VIX_TOOLS_ENV_TYPE_ENV_BLOCK = 1,
      VIX_TOOLS_ENV_TYPE_ENVIRON,
   } envType;
   union {
      /* Used when envType is VIX_TOOLS_ENV_TYPE_ENV_BLOCK. */
      struct {
         wchar_t *envBlock;     // Keep the original around to free.
         wchar_t *currEnvVar;
      } eb;
      /* Used when envType is VIX_TOOLS_ENV_TYPE_ENVIRON. */
      wchar_t **env;
   } data;
#else
   char **environ;
#endif
};


struct VixToolsUserEnvironment {
#ifdef _WIN32
   Bool impersonated;
   wchar_t *envBlock;      // Only used when impersonated == TRUE.
#else
   // The POSIX versions don't need any state currently.
#endif
};



/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsNewEnvIterator --
 *
 *      Create a new environment variable iterator for the user
 *      represented by 'userToken'.
 *      The resulting VixToolsEnvIterator must be freed using
 *      VixToolsDestroyEnvIterator.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsNewEnvIterator(void *userToken,                  // IN
#ifdef __FreeBSD__
                       char **envp,                      // IN
#endif
                       VixToolsEnvIterator **envItr)     // OUT
{
   VixError err = VIX_OK;
   VixToolsEnvIterator *it = Util_SafeMalloc(sizeof *it);

   if (NULL == envItr) {
      err = VIX_E_FAIL;
      goto quit;
   }

   *envItr = NULL;

#ifdef _WIN32
   if (PROCESS_CREATOR_USER_TOKEN != userToken) {
      /*
       * The process is impersonating a user, so retrieve the user's
       * environment block instead of using the process's environment.
       */
      it->envType = VIX_TOOLS_ENV_TYPE_ENV_BLOCK;
      err = VixToolsGetEnvBlock(userToken, &it->data.eb.envBlock);
      if (VIX_FAILED(err)) {
         goto quit;
      }
      it->data.eb.currEnvVar = it->data.eb.envBlock;
   } else {
      /*
       * The action is being performed as the user running the process
       * so the process's environment is fine.
       * TODO: Is this totally equivilent to the behavior when impersonated?
       * Would fetching the environment block include changes to the user's
       * or system's environment made after the process is running?
       */
      it->envType = VIX_TOOLS_ENV_TYPE_ENVIRON;
      it->data.env = _wenviron;
   }
#elif defined(__APPLE__)
   it->environ = *_NSGetEnviron();
#elif defined(__FreeBSD__)
   it->environ = envp;
#else
   it->environ = environ;
#endif
   *envItr = it;
quit:
   if (VIX_FAILED(err)) {
      free(it);
   }

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetNextEnvVar --
 *
 *      Get the next envariable variable pair in the form NAME=VALUE.
 *
 * Results:
 *      A heap-allocated UTF-8 string, or NULL when the iterator has
 *      reached the end.
 *
 * Side effects:
 *      Advances the iterator.
 *
 *-----------------------------------------------------------------------------
 */

char *
VixToolsGetNextEnvVar(VixToolsEnvIterator *envItr)    // IN
{
   char *envVar;

   if (NULL == envItr) {
      return NULL;
   }

#ifdef _WIN32
   if (VIX_TOOLS_ENV_TYPE_ENV_BLOCK == envItr->envType) {
      if (L'\0' == envItr->data.eb.currEnvVar[0]) {
         envVar = NULL;
      } else {
         envVar = Unicode_AllocWithUTF16(envItr->data.eb.currEnvVar);
         while(*envItr->data.eb.currEnvVar++);
      }
   } else if (VIX_TOOLS_ENV_TYPE_ENVIRON == envItr->envType) {
      if (NULL == *envItr->data.env) {
         envVar = NULL;
      } else {
         envVar = Unicode_AllocWithUTF16(*envItr->data.env);
         envItr->data.env++;
      }
   } else {
      /* Is someone using uninitialized memory? */
      NOT_IMPLEMENTED();
   }
#else
   if (NULL == *envItr->environ) {
      envVar = NULL;
   } else {
      envVar = Unicode_Alloc(*envItr->environ, STRING_ENCODING_DEFAULT);
      envItr->environ++;
   }
#endif
   return envVar;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsDestroyEnvIterator --
 *
 *      Free()s any memory associated with the VixToolsEnvIterator.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
VixToolsDestroyEnvIterator(VixToolsEnvIterator *envItr)   // IN
{
   if (NULL != envItr) {
#ifdef _WIN32
      if (VIX_TOOLS_ENV_TYPE_ENV_BLOCK == envItr->envType) {
         if (NULL != envItr->data.eb.envBlock) {
            VixToolsDestroyEnvironmentBlock(envItr->data.eb.envBlock);
         }
      }
#endif
      free(envItr);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsNewUserEnvironment --
 *
 *      Create a new UserEnvironment that can be used to query for
 *      environment variables.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsNewUserEnvironment(void *userToken,                   // IN
                           VixToolsUserEnvironment **env)     // OUT
{
   VixError err = VIX_OK;
   VixToolsUserEnvironment *myEnv = Util_SafeMalloc(sizeof *myEnv);

   if (NULL == env) {
      err = VIX_E_FAIL;
      goto quit;
   }

   *env = NULL;

#ifdef _WIN32
   if (PROCESS_CREATOR_USER_TOKEN != userToken) {
      myEnv->impersonated = TRUE;
      err = VixToolsGetEnvBlock(userToken, &myEnv->envBlock);
      if (VIX_FAILED(err)) {
         goto quit;
      }
   } else {
      myEnv->impersonated = FALSE;
      /* We will just read from the process's environment. */
   }
#endif

   *env = myEnv;

quit:
   if (VIX_FAILED(err)) {
      free(myEnv);
   }

   return err;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsGetEnvFromUserEnvironment --
 *
 *      Looks up the environment variable given by 'name' in the provided
 *      user environment.
 *
 * Results:
 *      A heap-allocated UTF-8 string, or NULL if the environment variable
 *      is not found.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

char *
VixToolsGetEnvFromUserEnvironment(const VixToolsUserEnvironment *env,  // IN
                                  const char *name)                    // IN
{
   char *envVar;

   if (NULL == env) {
      return NULL;
   }

#ifdef _WIN32
   if (env->impersonated) {
      envVar = VixToolsGetEnvVarFromEnvBlock(env->envBlock, name);
   } else {
      envVar = Util_SafeStrdup(Posix_Getenv(name));
   }
#else
   envVar = Util_SafeStrdup(Posix_Getenv(name));
#endif

   return envVar;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsDestroyUserEnvironment --
 *
 *      Releases any resources used by the VixToolsUserEnvironment.
 *      The VixToolsUserEnvironment must not be used after calling
 *      this function.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
VixToolsDestroyUserEnvironment(VixToolsUserEnvironment *env)   // IN
{
   if (NULL != env) {
#ifdef _WIN32
      if (NULL != env->envBlock) {
         if (env->impersonated) {
            VixToolsDestroyEnvironmentBlock(env->envBlock);
         }
      }
#endif
      free(env);
   }
}


#ifdef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsEnvironToEnvBlock --
 *
 *      Converts a NULL terminated array of UTF-8 environment variables in
 *      the form NAME=VALUE to an Win32 environment block, which is a single
 *      contiguous array containing UTF-16 environment variables in the same
 *      form, each separated by a UTF-16 null character, followed by two
 *      training null characters.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsEnvironToEnvBlock(char const * const *env,        // IN: UTF-8
                          wchar_t **envBlock)             // OUT
{
   VixError err;
   DynBuf buf;
   Bool res;
   static const wchar_t nullTerm[] = { L'\0', L'\0' };

   DynBuf_Init(&buf);

   if ((NULL == env) || (NULL == envBlock)) {
      err = VIX_E_FAIL;
      goto quit;
   }

   *envBlock = NULL;

   while (NULL != *env) {
      wchar_t *envVar = Unicode_GetAllocUTF16(*env);

      res = DynBuf_Append(&buf, envVar,
                          (wcslen(envVar) + 1) * sizeof(*envVar));
      free(envVar);
      if (!res) {
         err = VIX_E_OUT_OF_MEMORY;
         goto quit;
      }
      env++;
   }

   /*
    * Append two null characters at the end. This adds an extra (third) null
    * if there was at least one environment variable (since there already
    * is one after the last string) but we need both if there were no
    * environment variables in the input array. I'll waste two bytes to
    * keep the code a little simpler.
    */
   res = DynBuf_Append(&buf, nullTerm, sizeof nullTerm);
   if (!res) {
      err = VIX_E_OUT_OF_MEMORY;
      goto quit;
   }

   *envBlock = DynBuf_Detach(&buf);
   err = VIX_OK;

quit:
   DynBuf_Destroy(&buf);

   return err;
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * VixToolsValidateEnviron --
 *
 *      Ensures that the NULL terminated array of strings contains
 *      properly formated environment variables.
 *
 * Results:
 *      VixError
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

VixError
VixToolsValidateEnviron(char const * const *env)   // IN
{
   if (NULL == env) {
      return VIX_E_FAIL;
   }

   while (NULL != *env) {
      /*
       * Each string should contain at least one '=', to delineate between
       * the name and the value.
       */
      if (NULL == Str_Strchr(*env, '=')) {
         return VIX_E_INVALID_ARG;
      }
      env++;
   }

   return VIX_OK;
}


#ifdef VMX86_DEVEL
#ifdef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * TestVixToolsEnvironToEnvBlockEmptyEnviron --
 *
 *      Tests VixToolsEnvironToEnvBlock() with an empty environment: an
 *      char ** pointing to a single NULL pointer.
 *
 * Results:
 *      Passes or ASSERTs.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
TestVixToolsEnvironToEnvBlockEmptyEnviron(void)
{
   const char *environ1[] = { NULL };
   wchar_t *envBlock;
   VixError err;

   err = VixToolsEnvironToEnvBlock(environ1, &envBlock);
   ASSERT(VIX_OK == err);

   ASSERT((L'\0' == envBlock[0]) && (L'\0' == envBlock[1]));
   free(envBlock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * TestVixToolsEnvironToEnvBlockTwoGood --
 *
 *      Tests VixToolsEnvironToEnvBlock() with an environment containing
 *      two valid entries.
 *
 * Results:
 *      Passes or ASSERTs
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
TestVixToolsEnvironToEnvBlockTwoGood(void)
{
   const char *environ1[] = { "foo=bar", "env=block", NULL };
   wchar_t *envBlock, *currPos;
   VixError err;

   err = VixToolsEnvironToEnvBlock(environ1, &envBlock);
   ASSERT(VIX_OK == err);

   currPos = envBlock;
   ASSERT(wcscmp(currPos, L"foo=bar") == 0);
   currPos += wcslen(L"foo=bar") + 1;
   ASSERT(wcscmp(currPos, L"env=block") == 0);
   free(envBlock);
}


/*
 *-----------------------------------------------------------------------------
 *
 * TestVixToolsEnvironToEnvBlock --
 *
 *      Runs unit tests for VixToolsEnvironToEnvBlock().
 *
 * Results:
 *      Passes or ASSERTs.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
TestVixToolsEnvironToEnvBlock(void)
{
   TestVixToolsEnvironToEnvBlockEmptyEnviron();
   TestVixToolsEnvironToEnvBlockTwoGood();
}
#endif // #ifdef _WIN32


/*
 *-----------------------------------------------------------------------------
 *
 * TestVixToolsValidateEnvironEmptyEnviron --
 *
 *      Tests VixToolsEnvironToEnvBlock() with an empty environment.
 *
 * Results:
 *      Passes or ASSERTs.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
TestVixToolsValidateEnvironEmptyEnviron(void)
{
   const char *environ1[] = { NULL };
   VixError err;

   err = VixToolsValidateEnviron(environ1);
   ASSERT(VIX_OK == err);
}


/*
 *-----------------------------------------------------------------------------
 *
 * TestVixToolsValidateEnvironTwoGoodVars --
 *
 *      Tests VixToolsEnvironToEnvBlock() with an environment containing
 *      two valid environment variables.
 *
 * Results:
 *      Passes or ASSERTs.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
TestVixToolsValidateEnvironTwoGoodVars(void)
{
   const char *environ1[] = { "foo=bar", "vix=api", NULL };
   VixError err;

   err = VixToolsValidateEnviron(environ1);
   ASSERT(VIX_OK == err);
}


/*
 *-----------------------------------------------------------------------------
 *
 * TestVixToolsValidateEnvironOneBad --
 *
 *      Tests VixToolsEnvironToEnvBlock() with an environment containing
 *      one invalid environment variable.
 *
 * Results:
 *      Passes or ASSERTs.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
TestVixToolsValidateEnvironOneBad(void)
{
   const char *environ1[] = { "noequals", NULL };
   VixError err;

   err = VixToolsValidateEnviron(environ1);
   ASSERT(VIX_E_INVALID_ARG == err);
}


/*
 *-----------------------------------------------------------------------------
 *
 * TestVixToolsValidateEnvironSecondBad --
 *
 *      Tests VixToolsEnvironToEnvBlock() with an environment containing
 *      one valid environment variable followed by one invalid environment
 *      variable.
 *
 * Results:
 *      Passes or ASSERTs.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
TestVixToolsValidateEnvironSecondBad(void)
{
   const char *environ1[] = { "foo=bar", "noequals", NULL };
   VixError err;

   err = VixToolsValidateEnviron(environ1);
   ASSERT(VIX_E_INVALID_ARG == err);
}


/*
 *-----------------------------------------------------------------------------
 *
 * TestVixToolsValidateEnviron --
 *
 *      Run unit tests for VixToolsValidateEnviron().
 *
 * Results:
 *      Passes or ASSERTs.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
TestVixToolsValidateEnviron(void)
{
   TestVixToolsValidateEnvironEmptyEnviron();
   TestVixToolsValidateEnvironTwoGoodVars();
   TestVixToolsValidateEnvironOneBad();
   TestVixToolsValidateEnvironSecondBad();
}


/*
 *-----------------------------------------------------------------------------
 *
 * TextVixToolsEnvVars --
 *
 *      Run unit tests for functions in this file.
 *
 * Results:
 *      Passes or ASSERTs.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
TestVixToolsEnvVars(void)
{
#ifdef _WIN32
   TestVixToolsEnvironToEnvBlock();
#endif
   TestVixToolsValidateEnviron();
}
#endif // #ifdef VMX86_DEVEL
  070701000003A8000081A40000000000000000000000016822550500001E72000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vix/vixToolsInt.h /*********************************************************
 * Copyright (c) 2010-2020,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vixToolsInt.h --
 *
 *      Helper routines shared between different files in the vixTools
 *      module.
 */

#ifndef __VIX_TOOLS_INT_H__
#define __VIX_TOOLS_INT_H__

#include "vmware.h"
#include "vix.h"
#include "vixCommands.h"
#include <glib.h>

/*
 * Needed for VGAuth support code.
 */
#ifdef _WIN32
#include "VGAuthCommon.h"
#include "VGAuthError.h"
#include "VGAuthAuthentication.h"
#include "VGAuthAlias.h"
#endif

#define PROCESS_CREATOR_USER_TOKEN       ((void *)1)

#ifdef _WIN32

#define VIX_TOOLS_MAX_SSPI_SESSIONS 50
#define VIX_TOOLS_MAX_TICKETED_SESSIONS 50

#endif

extern char *gImpersonatedUsername;
#define  IMPERSONATED_USERNAME   ((gImpersonatedUsername) ? gImpersonatedUsername : "Unset")

typedef struct VixToolsEnvIterator VixToolsEnvIterator;

typedef struct VixToolsUserEnvironment VixToolsUserEnvironment;

typedef void (*VixToolsReportProgramDoneProcType)(const char *requestName,
                                                  VixError err,
                                                  int exitCode,
                                                  int64 pid,
                                                  void *clientData);

VixError VixTools_Initialize(Bool thisProcessRunsAsRootArg,
                             const char * const *originalEnvp,
                             VixToolsReportProgramDoneProcType reportProgramDoneProc,
                             void *clientData);

void VixTools_Uninitialize(void);

#ifdef _WIN32
VixError VixToolsTranslateVGAuthError(VGAuthError vgErr);
#endif

VixError VixToolsImpersonateUser(VixCommandRequestHeader *requestMsg,
                                 Bool loadUserProfile,
                                 void **userToken);

void VixTools_SetConsoleUserPolicy(Bool allowConsoleUserOpsParam);

void VixTools_SetRunProgramCallback(VixToolsReportProgramDoneProcType reportProgramDoneProc,
                                    void *clientData);

void VixTools_RestrictCommands(gboolean restricted);

/*
 * These are internal procedures that are exposed for the legacy
 * tclo callbacks.
 */
VixError VixToolsRunProgramImpl(char *requestName,
                                const char *commandLine,
                                const char *commandLineArgs,
                                int runProgramOptions,
                                void *userToken,
                                void *eventQueue,
                                int64 *pid);

VixError VixTools_GetToolsPropertiesImpl(GKeyFile *confDictRef,
                                         char **resultBuffer,
                                         size_t *resultBufferLength);

VixError VixTools_ProcessVixCommand(VixCommandRequestHeader *requestMsg,
                                    char *requestName,
                                    size_t maxResultBufferSize,
                                    GKeyFile *confDictRef,
                                    GMainLoop *eventQueue,
                                    char **resultBuffer,
                                    size_t *resultLen,
                                    Bool *deleteResultBufferResult);

uint32 VixTools_GetAdditionalError(uint32 opCode,
                                   VixError error);

Bool VixToolsImpersonateUserImpl(char const *credentialTypeStr,
                                 int credentialType,
                                 char const *password,
                                 void **userToken);

void VixToolsUnimpersonateUser(void *userToken);

void VixToolsLogoutUser(void *userToken);

VixError VixToolsNewEnvIterator(void *userToken,
#ifdef __FreeBSD__
                                char **envp,
#endif
                                VixToolsEnvIterator **envItr);

char *VixToolsGetNextEnvVar(VixToolsEnvIterator *envItr);

void VixToolsDestroyEnvIterator(VixToolsEnvIterator *envItr);

VixError VixToolsNewUserEnvironment(void *userToken,
                                    VixToolsUserEnvironment **env);

char *VixToolsGetEnvFromUserEnvironment(const VixToolsUserEnvironment *env,
                                        const char *name);

void VixToolsDestroyUserEnvironment(VixToolsUserEnvironment *env);

VixError VixToolsValidateEnviron(char const * const *env);

char *VixToolsGetEnvVarFromEnvBlock(const wchar_t *envBlock,
                                    const char *envVarName);

char *VixToolsEscapeXMLString(const char *str);

#ifdef _WIN32
VixError VixToolsInitializeWin32();

Bool VixToolsGetUserName(wchar_t **userName);

VixError VixToolsGetEnvBlock(void *userToken,
                             wchar_t **envBlock);

Bool VixToolsDestroyEnvironmentBlock(wchar_t *envBlock);

VixError VixToolsEnvironToEnvBlock(char const * const *env,
                                   wchar_t **envBlock);

VixError VixToolsGetUserTmpDir(void *userToken,
                               char **tmpDirPath);

Bool VixToolsUserIsMemberOfAdministratorGroup(VixCommandRequestHeader *requestMsg);

void VixToolsDeinitSspiSessionList();
void VixToolsDeinitTicketedSessionList();


VixError VixToolsAuthenticateWithSSPI(VixCommandRequestHeader *requestMsg,
                                      GMainLoop *eventQueue,
                                      char **resultBuffer);

VixError VixToolsGetTokenHandleFromTicketID(const char *ticketID,
                                            char **username,
                                            HANDLE *hToken);

VixError VixToolsReleaseCredentialsImpl(VixCommandRequestHeader *requestMsg);

VixError VixToolsCreateRegKeyImpl(VixCommandRequestHeader *requestMsg);

VixError VixToolsListRegKeysImpl(VixCommandRequestHeader *requestMsg,
                                 size_t maxBufferSize,
                                 void *eventQueue,
                                 char **result);

VixError VixToolsDeleteRegKeyImpl(VixCommandRequestHeader *requestMsg);

VixError VixToolsSetRegValueImpl(VixCommandRequestHeader *requestMsg);

VixError VixToolsListRegValuesImpl(VixCommandRequestHeader *requestMsg,
                                   size_t maxBufferSize,
                                   void *eventQueue,
                                   char **result);

VixError VixToolsDeleteRegValueImpl(VixCommandRequestHeader *requestMsg);

gchar *VixToolsGetCurrentUsername(void);

VixError VixToolsCheckSAMLForSystem(VGAuthContext *ctx,
                                    VGAuthError origErr,
                                    const char *token,
                                    const char *username,
                                    char *serviceUsername,
                                    Bool hostVerified,
                                    void **userToken,
                                    VGAuthUserHandle **curUserHandle);
#endif // _WIN32

#ifdef VMX86_DEVEL
void TestVixToolsEnvVars(void);
#endif

#endif // #ifndef __VIX_TOOLS_INT_H__
  070701000003A9000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup  070701000003AA000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000003AB000081A40000000000000000000000016822550500000A0E000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/Makefile.am  ################################################################################
### Copyright (C) 2009-2018 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @VMSVC_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libvmbackup.la

libvmbackup_la_CPPFLAGS =
libvmbackup_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libvmbackup_la_CPPFLAGS += @XDR_CPPFLAGS@

libvmbackup_la_LDFLAGS =
libvmbackup_la_LDFLAGS += @PLUGIN_LDFLAGS@

libvmbackup_la_LIBADD =
libvmbackup_la_LIBADD += @GOBJECT_LIBS@
libvmbackup_la_LIBADD += @VMTOOLS_LIBS@
libvmbackup_la_LIBADD += @XDR_LIBS@

libvmbackup_la_SOURCES =
libvmbackup_la_SOURCES += nullProvider.c
libvmbackup_la_SOURCES += scriptOps.c
libvmbackup_la_SOURCES += stateMachine.c
libvmbackup_la_SOURCES += syncDriverOps.c
if LINUX
libvmbackup_la_SOURCES += syncManifest.c
endif
libvmbackup_la_SOURCES += vmBackupSignals.c

BUILT_SOURCES =
BUILT_SOURCES += vmBackupSignals.c
BUILT_SOURCES += vmBackupSignals.h
BUILT_SOURCES += guestQuiesce.h
BUILT_SOURCES += guestQuiesce_xdr.c

CLEANFILES =
CLEANFILES += vmBackupSignals.c
CLEANFILES += vmBackupSignals.h
CLEANFILES += guestQuiesce.h
CLEANFILES += guestQuiesce_xdr.c

EXTRA_DIST =
EXTRA_DIST += vmBackupSignals.gm

libvmbackup_la_SOURCES += guestQuiesce_xdr.c

guestQuiesce.h: guestQuiesce.x
		@RPCGEN_WRAPPER@ services/plugins/vmbackup/guestQuiesce.x $@

guestQuiesce_xdr.c: guestQuiesce.x guestQuiesce.h
		@RPCGEN_WRAPPER@ services/plugins/vmbackup/guestQuiesce.x $@

vmBackupSignals.c: $(top_srcdir)/services/plugins/vmbackup/vmBackupSignals.gm
	glib-genmarshal --body $(top_srcdir)/services/plugins/vmbackup/vmBackupSignals.gm > \
	   $@ || (rm -f $@ && exit 1)

vmBackupSignals.h: $(top_srcdir)/services/plugins/vmbackup/vmBackupSignals.gm
	glib-genmarshal --header $(top_srcdir)/services/plugins/vmbackup/vmBackupSignals.gm > \
	   $@ || (rm -f $@ && exit 1)

  070701000003AC000081A40000000000000000000000016822550500000882000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/README   This is an attempt to describe the VmBackup state machine using ASCII diagrams.

=== Main State Machine ===

The main state machine, implemented in stateMachine.c, works as follows:

         -------------------> IDLE <-----------|
         |                      |              |
         | b, e                 | a            |
         |                      |              |
         |        e            \/              |
   SCRIPT_ERROR <--------- SCRIPT_FREEZE       |
         ^                   | b'    |         |
         |      e           \/       |         |
    SYNC_ERROR <-- SYNC_FREEZE_WAIT  |         |
         | d                 | b     | b       |
         |        e         \/      \/         | b, e
    SYNC_ERROR <----------- SYNC_FREEZE        |
         ^                      |              |
         |                      | c            |
         |     e               \/              |
         |------------------ SYNC_THAW         |
                                |              |
                                | d            |
                               \/              |
                            SCRIPT_THAW --------
                                |              |
                                | f            | g
                               \/              |
                            COMPLETE_WAIT-------

The transitions mean the following events / conditions:

a. vmbackup.start RPC
   b'. vmbackup operation is running in background (Linux specific)
b. vmbackup operation is finished
c. vmbackup.snapshotDone RPC
d. sync provider operation is finished
e. error condition: runtime error, or vmbackup.abort RPC
f. thaw scripts run finished
g. get notification that snapshot succeeds

Sending a vmbackup.start RPC while the state machine is not IDLE causes an
error to be returned to the client, but the state machine is not changed.
Same behavior occurs for multiple vmbackup.snapshotDone RPCs. Multiple
vmbackup.abort messages are ignored.

Transitions to IDLE cause the backup operation to be finalized, and a "done"
event to be sent to the VMX. Transitions from IDLE cause a "reset" event to be
sent to the VMX.

  070701000003AD000081A4000000000000000000000001682255050000093A000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/guestQuiesce.x   /*********************************************************
 * Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * guestQuiesce.x --
 *
 *    Definition of the data structures used in the GuestRpc commands to
 *    provide information about guest quiescing settings.
 */

enum GuestQuiesceParamsVersion {
   GUESTQUIESCEPARAMS_V1 = 1,
   GUESTQUIESCEPARAMS_V2 = 2
};

const GUESTQUIESCE_SCRIPTARG_MAX_LEN = 256;
const GUESTQUIESCE_DISKUUID_MAX_LEN = 3200;   /* (UUID_MAXLEN + 1) * 64 disks */


/*  Guest Quiescing parameters. */
struct GuestQuiesceParamsV1 {
   Bool createManifest;     /* Create manifest describing the operations */
   Bool quiesceApps;        /* Allow application quiescing */
   Bool quiesceFS;          /* Allow file system quiescing */
   Bool writableSnapshot;   /* Assume writable snapshot is allowed */
   Bool execScripts;        /* Run custom scripts created by the users */
   string scriptArg<GUESTQUIESCE_SCRIPTARG_MAX_LEN>;  /* Argument to  scripts */
   uint32 timeout;          /* Time out for the quiesce operation*/
   string diskUuids<GUESTQUIESCE_DISKUUID_MAX_LEN>;   /* disk Uuids */
};

/*  Guest Quiescing parameters V2. */
struct GuestQuiesceParamsV2 {
   struct GuestQuiesceParamsV1 paramsV1;
   uint32 vssBackupContext;
   uint32 vssBackupType;
   Bool vssBootableSystemState;
   Bool vssPartialFileSupport;
};


union GuestQuiesceParams switch (GuestQuiesceParamsVersion ver) {
case GUESTQUIESCEPARAMS_V1:
   struct GuestQuiesceParamsV1 *guestQuiesceParamsV1;
case GUESTQUIESCEPARAMS_V2:
   struct GuestQuiesceParamsV2 *guestQuiesceParamsV2;
};
  070701000003AE000081A40000000000000000000000016822550500002206000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/nullProvider.c   /*********************************************************
 * Copyright (c) 2010-2016, 2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file nullProvider.c
 *
 * Implements a sync provider that doesn't really do anything, so that we can at
 * least run freeze / thaw scripts if no lower-level freeze functionality is
 * available.
 */

#if !defined(_WIN32)
#  include <unistd.h>
#endif

#include "vmBackupInt.h"


#if defined(_WIN32)

/*
 ******************************************************************************
 * VmBackupNullStart --                                                 */ /**
 *
 * Sends the "commit snapshot" event to the host.
 *
 * @param[in] state         Backup state.
 * @param[in] clientData    Unused.
 *
 * @return Whether successfully sent the signal to the host.
 *
 ******************************************************************************
 */

static Bool
VmBackupNullStart(VmBackupState *state,
                  void *clientData)
{
   VmBackup_SetCurrentOp(state, NULL, NULL, __FUNCTION__);
   return VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, "");
}


/*
 ******************************************************************************
 * VmBackupNullSnapshotDone --                                          */ /**
 *
 * Does nothing, just keep the backup state machine alive.
 *
 * @param[in] state         Backup state.
 * @param[in] clientData    Unused.
 *
 * @return TRUE.
 *
 ******************************************************************************
 */

static Bool
VmBackupNullSnapshotDone(VmBackupState *state,
                         void *clientData)
{
   VmBackup_SetCurrentOp(state, NULL, NULL, __FUNCTION__);
   return TRUE;
}

#else

/*
 ******************************************************************************
 * VmBackupNullReadyForSnapshot --                                      */ /**
 *
 * Sends an event to the VMX indicating that the guest is ready for a
 * snapshot to be taken (i.e., scripts have run and Nulldriver is
 * enabled).
 *
 * @param[in] state         Backup state.
 *
 * @return TRUE, unless sending the message fails.
 *
 ******************************************************************************
 */

static Bool
VmBackupNullReadyForSnapshot(VmBackupState *state)
{
   Bool success;

   g_debug("*** %s\n", __FUNCTION__);
   success = VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, "");
   if (success) {
      state->freezeStatus = VMBACKUP_FREEZE_FINISHED;
   } else {
      g_warning("Failed to send commit event to host");
      state->freezeStatus = VMBACKUP_FREEZE_ERROR;
   }
   return success;
}


/*
 ******************************************************************************
 * VmBackupNullOpQuery --                                               */ /**
 *
 * Checks the status of the operation that is enabling or disabling the
 * Null driver. Nulldriver is enabled immediately and there is nothing
 * to disable.
 *
 * @param[in] op        VmBackupOp.
 *
 * @return VMBACKUP_STATUS_FINISHED always.
 *
 ******************************************************************************
 */

static VmBackupOpStatus
VmBackupNullOpQuery(VmBackupOp *op) // IN
{
   return VMBACKUP_STATUS_FINISHED;
}


/*
 ******************************************************************************
 * VmBackupNullOpRelease --                                             */ /**
 *
 * Cleans up data held by the op object.
 *
 * @param[in] op        VmBackupOp.
 *
 ******************************************************************************
 */

static void
VmBackupNullOpRelease(VmBackupOp *op)  // IN
{
   g_free(op);
}


/*
 ******************************************************************************
 * VmBackupNullOpCancel --                                              */ /**
 *
 * Cancel an ongoing Nulldriver operation. This doesn't actually
 * do anything because there is no operation to cancel as such.
 *
 * @param[in] op        VmBackupOp.
 *
 ******************************************************************************
 */

static void
VmBackupNullOpCancel(VmBackupOp *op)   // IN
{
   /* Nothing to do */
}


/*
 ******************************************************************************
 * VmBackupNullStart --                                                 */ /**
 *
 * Calls sync(2) on POSIX systems. Sets up an asynchronous operation
 * for tracking.
 *
 * @param[in] ctx           Plugin context.
 * @param[in] state         Backup state.
 *
 ******************************************************************************
 */

static void
VmBackupNullStart(ToolsAppCtx *ctx,
                  void *clientData)
{
   VmBackupOp *op;
   VmBackupState *state = (VmBackupState*) clientData;

   g_debug("*** %s\n", __FUNCTION__);

   op = g_new0(VmBackupOp, 1);
   op->queryFn = VmBackupNullOpQuery;
   op->cancelFn = VmBackupNullOpCancel;
   op->releaseFn = VmBackupNullOpRelease;

   /*
    * This is more of a "let's at least do something" than something that
    * will actually ensure data integrity...
    */
   sync();

   VmBackup_SetCurrentOp(state,
                         op,
                         VmBackupNullReadyForSnapshot,
                         __FUNCTION__);
}


/*
 ******************************************************************************
 * VmBackupNullSnapshotDone --                                          */ /**
 *
 * Does nothing except setting up an asynchronous operation to keep the
 * backup state machine alive.
 *
 * @param[in] state         Backup state.
 * @param[in] clientData    Unused.
 *
 * @return TRUE.
 *
 ******************************************************************************
 */

static Bool
VmBackupNullSnapshotDone(VmBackupState *state,
                         void *clientData)
{
   VmBackupOp *op;

   g_debug("*** %s\n", __FUNCTION__);

   op = g_new0(VmBackupOp, 1);
   op->queryFn = VmBackupNullOpQuery;
   op->cancelFn = VmBackupNullOpCancel;
   op->releaseFn = VmBackupNullOpRelease;

   VmBackup_SetCurrentOp(state, op, NULL, __FUNCTION__);
   return TRUE;
}


/*
 ******************************************************************************
 * VmBackupNullUndo --                                                  */ /**
 *
 * Update the state machine state with the currentOpName.
 *
 * Can be called when snapshot times out.  See PR2993571 and PR3003917.
 *
 * @param[in] state        the backup state
 * @param[in] clientData   client data
 *
 * @return TRUE
 *
 ******************************************************************************
 */

static Bool
VmBackupNullUndo(VmBackupState *state,
                 void *clientData)
{
   g_debug("*** %s\n", __FUNCTION__);
   VmBackup_SetCurrentOp(state, NULL, NULL, __FUNCTION__);
   return TRUE;
}

#endif

/*
 ******************************************************************************
 * VmBackupNullRelease --                                               */ /**
 *
 * Frees memory associated with this sync provider.
 *
 * @param[in] provider     The provider.
 *
 ******************************************************************************
 */

static void
VmBackupNullRelease(VmBackupSyncProvider *provider)
{
   g_free(provider);
}


/*
 ******************************************************************************
 * VmBackup_NewNullProvider --                                          */ /**
 *
 * Returns a new null provider.
 *
 * @return A VmBackupSyncProvider, never NULL.
 *
 ******************************************************************************
 */

VmBackupSyncProvider *
VmBackup_NewNullProvider(void)
{
   VmBackupSyncProvider *provider;

   provider = g_new(VmBackupSyncProvider, 1);
   provider->start = VmBackupNullStart;
#if !defined(_WIN32)
   provider->undo = VmBackupNullUndo;
#endif
   provider->snapshotDone = VmBackupNullSnapshotDone;
   provider->release = VmBackupNullRelease;
   provider->clientData = NULL;

   return provider;
}

  070701000003AF000081A40000000000000000000000016822550500003CF8000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/scriptOps.c  /*********************************************************
 * Copyright (c) 2007-2019, 2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * scriptOps.c --
 *
 * Functions for handling freeze/thaw scripts.
 */

#include "vmBackupInt.h"

#include <stdlib.h>
#include <string.h>

#include "vm_basic_defs.h"
#include "file.h"
#include "guestApp.h"
#include "procMgr.h"
#include "str.h"
#include "util.h"
#include "vmware/tools/log.h"


/*
 * These are legacy scripts used before the vmbackup-based backups. To
 * aid people who will be transitioned to the new scheme after we're
 * deprecating the old code paths, also check for them when running
 * freeze / thaw scripts. The paths were hardcoded like this in hostd
 * before (although they were configurable in hostd's config file), so
 * there's no point in figuring out the correct Windows directory for
 * this particular feature.
 */

#if defined(_WIN32)
#  define   LEGACY_FREEZE_SCRIPT    "c:\\windows\\pre-freeze-script.bat"
#  define   LEGACY_THAW_SCRIPT      "c:\\windows\\post-thaw-script.bat"
#else
#  define   LEGACY_FREEZE_SCRIPT    "/usr/sbin/pre-freeze-script"
#  define   LEGACY_THAW_SCRIPT      "/usr/sbin/post-thaw-script"
#endif


typedef struct VmBackupScript {
   char *path;
   ProcMgr_AsyncProc *proc;
} VmBackupScript;


typedef struct VmBackupScriptOp {
   VmBackupOp callbacks;
   Bool canceled;
   Bool thawFailed;
   VmBackupScriptType type;
   VmBackupState *state;
} VmBackupScriptOp;


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupGetScriptPath --
 *
 *    Returns the path where the scripts to be executed reside.
 *
 * Result
 *    A string with the requested path.
 *
 * Side effects:
 *    Allocates memory for the path.
 *
 *-----------------------------------------------------------------------------
 */

static char *
VmBackupGetScriptPath(void)
{
   char *scriptPath = NULL;
   char *installPath = GuestApp_GetInstallPath();

   if (installPath == NULL) {
      return NULL;
   }

   scriptPath = Str_Asprintf(NULL, "%s%s%s", installPath, DIRSEPS, "backupScripts.d");
   free(installPath);

   return scriptPath;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VmBackupRunNextScript --
 *
 *    Runs the next script for the given operation. If thawing (or running
 *    scripts after a failure), this function will try as much as possible
 *    to start a script, meaning that if it fails to start a script it will
 *    try to start the preceding one until one script is run, or it runs out
 *    of scripts to try.
 *
 * Results:
 *    -1: an error occurred.
 *    0: no more scripts to run.
 *    1: script was started.
 *
 * Side effects:
 *    Increments (or decrements) the "current script" index in the backup state.
 *
 *-----------------------------------------------------------------------------
 */

static int
VmBackupRunNextScript(VmBackupScriptOp *op)  // IN/OUT
{
   const char *scriptOp;
   int ret = 0;
   ssize_t index;
   VmBackupScript *scripts = op->state->scripts;

   switch (op->type) {
   case VMBACKUP_SCRIPT_FREEZE:
      index = ++op->state->currentScript;
      scriptOp = "freeze";
      break;

   case VMBACKUP_SCRIPT_FREEZE_FAIL:
      index = --op->state->currentScript;
      scriptOp = "freezeFail";
      break;

   case VMBACKUP_SCRIPT_THAW:
      index = --op->state->currentScript;
      scriptOp = "thaw";
      break;

   default:
      NOT_REACHED();
   }

   while (index >= 0 && scripts[index].path != NULL) {
      if (File_IsFile(scripts[index].path)) {
         char *cmd;

         if (op->state->scriptArg != NULL && op->state->scriptArg[0] != '\0') {
            cmd = Str_Asprintf(NULL, "\"%s\" %s \"%s\"", scripts[index].path,
                               scriptOp, op->state->scriptArg);
         } else {
            cmd = Str_Asprintf(NULL, "\"%s\" %s", scripts[index].path,
                               scriptOp);
         }
         if (cmd != NULL) {
            host_debug("Running script: %s\n", scripts[index].path);
            guest_debug("Running script: %s\n", cmd);
            scripts[index].proc = ProcMgr_ExecAsync(cmd, NULL);
         } else {
            g_debug("Failed to allocate memory to run script: %s\n",
                    scripts[index].path);
            scripts[index].proc = NULL;
         }
         vm_free(cmd);

         if (scripts[index].proc == NULL) {
            if (op->type == VMBACKUP_SCRIPT_FREEZE) {
               ret = -1;
               break;
            } else {
               op->thawFailed = TRUE;
            }
         } else {
            ret = 1;
            break;
         }
      }

      if (op->type == VMBACKUP_SCRIPT_FREEZE) {
         index = ++op->state->currentScript;
      } else {
         index = --op->state->currentScript;
      }

      /*
       * This happens if all thaw/fail scripts failed to start. Since the first
       * entry may be a legacy script (which may not exist), need to check
       * whether the interesting failure is the first or the second entry in
       * the script list.
       */
      if (index == -1) {
         size_t failIdx = 0;
         if (!File_IsFile(scripts[0].path)) {
            failIdx = 1;
         }
         if (scripts[failIdx].proc == NULL && scripts[failIdx].path != NULL) {
            ret = -1;
         }
      }
   }

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupStringCompare --
 *
 *    Comparison function used to sort the script list in ascending order.
 *
 * Result
 *    The result of strcmp(str1, str2).
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static int
VmBackupStringCompare(const void *str1,   // IN
                      const void *str2)   // IN
{
   return strcmp(* (char * const *) str1,* (char * const *) str2);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupScriptOpQuery --
 *
 *    Checks the status of the current running script. If the script is
 *    finished, run the next script in the queue or, if no scripts are left,
 *    return a "finished" status.
 *
 * Result
 *    The status of the operation.
 *
 * Side effects:
 *    Might start a new process.
 *
 *-----------------------------------------------------------------------------
 */

static VmBackupOpStatus
VmBackupScriptOpQuery(VmBackupOp *_op) // IN
{
   VmBackupOpStatus ret = VMBACKUP_STATUS_PENDING;
   VmBackupScriptOp *op = (VmBackupScriptOp *) _op;
   VmBackupScript *scripts = op->state->scripts;
   VmBackupScript *currScript = NULL;

   if (scripts != NULL && op->state->currentScript >= 0) {
      currScript = &scripts[op->state->currentScript];
   }

   if (op->canceled) {
      ret = VMBACKUP_STATUS_CANCELED;
      goto exit;
   } else if (scripts == NULL || currScript == NULL || currScript->proc == NULL) {
      ret = VMBACKUP_STATUS_FINISHED;
      goto exit;
   }

   if (!ProcMgr_IsAsyncProcRunning(currScript->proc)) {
      int exitCode;
      Bool succeeded;

      succeeded = (ProcMgr_GetExitCode(currScript->proc, &exitCode) == 0 &&
                   exitCode == 0);
      ProcMgr_Free(currScript->proc);
      currScript->proc = NULL;

      /*
       * If thaw scripts fail, keep running and only notify the failure after
       * all others have run.
       */
      if (!succeeded) {
          if (op->type == VMBACKUP_SCRIPT_FREEZE) {
             ret = VMBACKUP_STATUS_ERROR;
             goto exit;
          } else if (op->type == VMBACKUP_SCRIPT_THAW) {
             op->thawFailed = TRUE;
          }
      }

      switch (VmBackupRunNextScript(op)) {
      case -1:
         ret = VMBACKUP_STATUS_ERROR;
         break;

      case 0:
         ret = op->thawFailed ? VMBACKUP_STATUS_ERROR : VMBACKUP_STATUS_FINISHED;
         break;

      default:
         break;
      }
   }

exit:
   if (ret == VMBACKUP_STATUS_ERROR) {
      /* Report the script error to the host */
      VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR,
                         VMBACKUP_SCRIPT_ERROR,
                         "Custom quiesce script failed.");
   }
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupScriptOpRelease --
 *
 *    Frees memory allocated for the state object. Behavior is undefined
 *    if the memory is freed before the query function says the operation
 *    if finished.
 *
 * Result
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VmBackupScriptOpRelease(VmBackupOp *_op)  // IN
{
   VmBackupScriptOp *op = (VmBackupScriptOp *) _op;

   if (op->type != VMBACKUP_SCRIPT_FREEZE && op->state->scripts != NULL) {
      size_t i;
      VmBackupScript *scripts = op->state->scripts;

      for (i = 0; scripts[i].path != NULL; i++) {
         free(scripts[i].path);
         if (scripts[i].proc != NULL) {
            ProcMgr_Free(scripts[i].proc);
         }
      }
      free(op->state->scripts);
      op->state->scripts = NULL;
      op->state->currentScript = 0;
   }

   free(op);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupScriptOpCancel --
 *
 *    Cancels the current operation.  Forces any currently running script
 *    to quit and flags the operation as canceled.
 *
 * Result
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VmBackupScriptOpCancel(VmBackupOp *_op)   // IN
{
   VmBackupScriptOp *op = (VmBackupScriptOp *) _op;
   VmBackupScript *scripts = op->state->scripts;
   VmBackupScript *currScript = NULL;
   ProcMgr_Pid pid;

   if (scripts != NULL) {
      currScript = &scripts[op->state->currentScript];
      ASSERT(currScript->proc != NULL);

      pid = ProcMgr_GetPid(currScript->proc);
      if (!ProcMgr_KillByPid(pid)) {
         // XXX: what to do in this situation? other than log and cry?
      } else {
         int exitCode;
         ProcMgr_GetExitCode(currScript->proc, &exitCode);
      }
   }

   op->canceled = TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupNewScriptOp --
 *
 *    Creates a new state object to monitor the execution of OnFreeze or
 *    OnThaw scripts. This will identify all the scripts in the backup scripts
 *    directory and add them to an execution queue.
 *
 *    Note: there is some state created when instantianting the "OnFreeze"
 *    scripts which is only released after the "OnThaw" scripts are run. So
 *    the caller has to make sure that thaw (or fail) scripts are run every
 *    time the freeze scripts are run.
 *
 * Result
 *    A pointer to the operation state, or NULL on failure.
 *
 * Side effects:
 *    If there are scripts to be executed, the first one is started.
 *
 *-----------------------------------------------------------------------------
 */

VmBackupOp *
VmBackup_NewScriptOp(VmBackupScriptType type, // IN
                     VmBackupState *state)    // IN
{
   Bool fail = FALSE;
   char **fileList = NULL;
   char *scriptDir;
   int numFiles = 0;
   size_t i;
   VmBackupScriptOp *op = NULL;

   scriptDir = VmBackupGetScriptPath();
   if (scriptDir == NULL) {
      goto exit;
   }

   op = calloc(1, sizeof *op);
   if (op == NULL) {
      goto exit;
   }

   op->state = state;
   op->type = type;
   op->callbacks.queryFn = VmBackupScriptOpQuery;
   op->callbacks.cancelFn = VmBackupScriptOpCancel;
   op->callbacks.releaseFn = VmBackupScriptOpRelease;

   g_debug("Trying to run scripts from %s\n", scriptDir);

   /*
    * Load the list of scripts to run when freezing. The same list will be
    * used later in case of failure, or when thawing, in reverse order.
    *
    * This logic won't recurse into directories, so only files directly under
    * the script dir will be considered.
    *
    * Legacy scripts will be the first ones to run (or last ones in the
    * case of thawing). If either the legacy freeze or thaw script
    * exist, the first entry in the script list will be reserved for
    * them, and their path might not exist (in case, for example, the
    * freeze script exists but the thaw script doesn't).
    */
   if (type == VMBACKUP_SCRIPT_FREEZE) {
      VmBackupScript *scripts = NULL;
      int legacy = 0;
      size_t idx = 0;

      state->scripts = NULL;
      state->currentScript = 0;

      if (File_IsFile(LEGACY_FREEZE_SCRIPT) ||
          File_IsFile(LEGACY_THAW_SCRIPT)) {
         legacy = 1;
      }

      if (File_IsDirectory(scriptDir)) {
         numFiles = File_ListDirectory(scriptDir, &fileList);
      }

      if (numFiles + legacy > 0) {
         scripts = calloc(numFiles + legacy + 1, sizeof *scripts);
         if (scripts == NULL) {
            fail = TRUE;
            goto exit;
         }

         /*
          * VmBackupRunNextScript increments the index, so need to make it point
          * to "before the first script".
          */
         state->currentScript = -1;
         state->scripts = scripts;
      }

      if (legacy > 0) {
         scripts[idx++].path = Util_SafeStrdup(LEGACY_FREEZE_SCRIPT);
      }

      if (numFiles > 0) {
         size_t i;

         if (numFiles > 1) {
            qsort(fileList, (size_t) numFiles, sizeof *fileList, VmBackupStringCompare);
         }

         for (i = 0; i < numFiles; i++) {
            char *script;

            script = Str_Asprintf(NULL, "%s%c%s", scriptDir, DIRSEPC, fileList[i]);
            if (script == NULL) {
               fail = TRUE;
               goto exit;
            } else if (File_IsFile(script)) {
               scripts[idx++].path = script;
            } else {
               free(script);
            }
         }
      }
   } else if (state->scripts != NULL) {
      VmBackupScript *scripts = state->scripts;
      if (strcmp(scripts[0].path, LEGACY_FREEZE_SCRIPT) == 0) {
         vm_free(scripts[0].path);
         scripts[0].path = Util_SafeStrdup(LEGACY_THAW_SCRIPT);
      }
   }

   /*
    * If there are any scripts to be executed, start the first one. If we get to
    * this point, we won't free the scripts array until VmBackupScriptOpRelease
    * is called after thawing (or after the sync provider failed and the "fail"
    * scripts are run).
    */
   fail = (state->scripts != NULL && VmBackupRunNextScript(op) == -1);

exit:
   /* Free the file list. */
   for (i = 0; i < numFiles; i++) {
      free(fileList[i]);
   }
   free(fileList);

   if (fail && op != NULL) {
      VmBackup_Release((VmBackupOp *) op);
      op = NULL;
   }
   free(scriptDir);
   return (VmBackupOp *) op;
}

070701000003B0000081A4000000000000000000000001682255050000C281000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/stateMachine.c   /*********************************************************
 * Copyright (c) 2007-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file stateMachine.c
 *
 * Implements a generic state machine for executing backup operations
 * asynchronously. Since VSS is based on an asynchronous polling model,
 * we're basing all backup operations on a similar model controlled by this
 * state machine, even if it would be more eficient to use an event-driven
 * approach in some cases.
 *
 * For a description of the state machine, check the README file.
 *
 * The sync provider state machine depends on the particular implementation.
 * For the sync driver, it enables the driver and waits for a "snapshot done"
 * message before finishing. For the VSS subsystem, the sync provider just
 * implements a VSS backup cycle.
 */

#include "vmBackupInt.h"

#include <glib-object.h>
#include <gmodule.h>
#include "guestApp.h"
#include "str.h"
#include "strutil.h"
#include "util.h"
#include "vmBackupSignals.h"
#include "guestQuiesce.h"
#if !defined(_WIN32)
#include "vmware/tools/threadPool.h"
#endif
#include "vmware/tools/utils.h"
#include "vmware/tools/vmbackup.h"
#include "vmware/tools/log.h"
#include "xdrutil.h"

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif

#if defined(__linux__)
#include <errno.h>
#include <string.h>
#ifdef VM_X86_ANY
#include <sys/io.h>
#include "ioplGet.h"
#else
#define NO_IOPL
#endif
#endif

#define VMBACKUP_ENQUEUE_EVENT() do {                                         \
   gBackupState->timerEvent = g_timeout_source_new(gBackupState->pollPeriod); \
   VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx,                                \
                            gBackupState->timerEvent,                         \
                            VmBackupAsyncCallback,                            \
                            NULL,                                             \
                            NULL);                                            \
} while (0)

/*
 * Macros to read values from config file.
 */
#define VMBACKUP_CONFIG_GET_BOOL(config, key, defVal)       \
   VMTools_ConfigGetBoolean(config, "vmbackup", key, defVal)

#define VMBACKUP_CONFIG_GET_STR(config, key, defVal)        \
   VMTools_ConfigGetString(config, "vmbackup", key, defVal)

#define VMBACKUP_CONFIG_GET_INT(config, key, defVal)        \
   VMTools_ConfigGetInteger(config, "vmbackup", key, defVal)

#define VMBACKUP_CFG_ENABLEVSS      "enableVSS"
#define VMBACKUP_CFG_ENABLENVME     "enableNVMe"

/**
 * Default value for VMBACKUP_CFG_ENABLENVME setting in
 * tools configuration file.
 *
 * TRUE will allow the host to use NVMe feature in snapshot,
 * FALSE otherwise.
 */
#define VMBACKUP_CFG_ENABLENVME_DEFAULT                  TRUE

static VmBackupState *gBackupState = NULL;

static Bool
VmBackupEnableSync(void);

static Bool
VmBackupEnableSyncWait(void);

static Bool
VmBackupEnableCompleteWait(void);


/**
 * Returns the configured timeout value.
 *
 * @param[in]  config   Config file to read from.
 * @param[in]  defValue Default value if the timeout key is not found or error.
 *
 * @return value of the timeout key if read successfully,
 *         defValue otherwise.
 */

static gint
VmBackupGetTimeout(GKeyFile *config,
                   const gint defValue)
{
   gint timeout = VMBACKUP_CONFIG_GET_INT(config, "timeout", defValue);
   if (timeout < 0 || timeout > (G_MAXINT / 1000)) {
      g_warning("Invalid timeout %d. Using default %us.",
                timeout, defValue);
      timeout = defValue;
   }

   return timeout;
}


/**
 * Returns a string representation of the given state machine state.
 *
 * @param[in]  state    State of interest.
 *
 * @return A string representation of the state.
 */

static const char *
VmBackupGetStateName(VmBackupMState state)
{
   switch (state) {
   case VMBACKUP_MSTATE_IDLE:
      return "IDLE";

   case VMBACKUP_MSTATE_SCRIPT_FREEZE:
      return "SCRIPT_FREEZE";

   case VMBACKUP_MSTATE_SYNC_FREEZE_WAIT:
      return "SYNC_FREEZE_WAIT";

   case VMBACKUP_MSTATE_SYNC_FREEZE:
      return "SYNC_FREEZE";

   case VMBACKUP_MSTATE_SYNC_THAW:
      return "SYNC_THAW";

   case VMBACKUP_MSTATE_SCRIPT_THAW:
      return "SCRIPT_THAW";

   case VMBACKUP_MSTATE_COMPLETE_WAIT:
      return "COMPLETE_WAIT";

   case VMBACKUP_MSTATE_SCRIPT_ERROR:
      return "SCRIPT_ERROR";

   case VMBACKUP_MSTATE_SYNC_ERROR:
      return "SYNC_ERROR";

   default:
      NOT_IMPLEMENTED();
   }
}


/**
 * Sends a keep alive backup event to the VMX.
 *
 * @param[in]  clientData     Unused.
 *
 * @return FALSE
 */

static gboolean
VmBackupKeepAliveCallback(void *clientData)
{
   g_debug("*** %s\n", __FUNCTION__);
   ASSERT(gBackupState != NULL);
   g_source_unref(gBackupState->keepAlive);
   gBackupState->keepAlive = NULL;
   VmBackup_SendEvent(VMBACKUP_EVENT_KEEP_ALIVE, 0, "");
   return FALSE;
}


#if defined __linux__ && !defined NO_IOPL
static Bool
VmBackupPrivSendMsg(gchar *msg,
                    char **result,
                    size_t *resultLen)
{
   Bool success;
   unsigned int oldLevel;

   ASSERT(gBackupState != NULL);

   g_debug("*** %s\n", __FUNCTION__);

   oldLevel = Iopl_Get();

   g_debug("Raising the IOPL, oldLevel=%u\n", oldLevel);
   if (iopl(3) < 0) {
      g_warning("Error raising the IOPL, %s\n", strerror(errno));
   }

   success = RpcChannel_Send(gBackupState->ctx->rpc, msg,
                             strlen(msg) + 1,
                             result, resultLen);

   if (iopl(oldLevel) < 0) {
      g_warning("Error restoring the IOPL, %s\n", strerror(errno));
   }

   return success;
}
#endif


/**
 * Sends a command to the VMX asking it to update VMDB about a new backup event.
 * This will restart the keep-alive timer.
 *
 * As the name implies, does not cancel the quiesce operation on failure.
 *
 * @param[in]  event    The event to set.
 * @param[in]  code     Error code.
 * @param[in]  desc     Error description.
 *
 * @return TRUE on success.
 */

Bool
VmBackup_SendEventNoAbort(const char *event,
                          const uint32 code,
                          const char *desc)
{
   Bool success;
   char *result = NULL;
   size_t resultLen;
   gchar *msg;

   ASSERT(gBackupState != NULL);

   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState->keepAlive != NULL) {
      g_source_destroy(gBackupState->keepAlive);
      g_source_unref(gBackupState->keepAlive);
      gBackupState->keepAlive = NULL;
   }

   msg = g_strdup_printf(VMBACKUP_PROTOCOL_EVENT_SET" %s %u %s",
                         event, code, desc);
   g_debug("Sending vmbackup event: %s\n", msg);

#if defined __linux__ && !defined NO_IOPL
   if (gBackupState->needsPriv) {
      success = VmBackupPrivSendMsg(msg, &result, &resultLen);
   } else {
      success = RpcChannel_Send(gBackupState->ctx->rpc,
                                msg, strlen(msg) + 1,
                                &result, &resultLen);
      if (!success) {
         const char *privErr = "Guest is not privileged";
         if (resultLen > strlen(privErr) &&
             strncmp(result, privErr, strlen(privErr)) == 0) {
            g_debug("Failed to send event: %s\n", result);
            vm_free(result);

            /*
             * PR1444259:
             * Some hosts enforce privilege elevation for sending this
             * event, especially 5.5. This is Linux specific because
             * version 9.4.x on Linux only triggers host side check for
             * privilege elevation by sending iopl_elevation capability
             * to the host.
             */
            gBackupState->needsPriv = TRUE;

            g_debug("Sending event with priv: %s\n", msg);
            success = VmBackupPrivSendMsg(msg, &result, &resultLen);
         } else {
            gBackupState->needsPriv = FALSE;
         }
      }
   }
#else
   success = RpcChannel_Send(gBackupState->ctx->rpc,
                             msg, strlen(msg) + 1,
                             &result, &resultLen);
#endif

   if (success) {
      ASSERT(gBackupState->keepAlive == NULL);
      gBackupState->keepAlive =
         g_timeout_source_new(VMBACKUP_KEEP_ALIVE_PERIOD / 2);
      VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx,
                               gBackupState->keepAlive,
                               VmBackupKeepAliveCallback,
                               NULL,
                               NULL);
   } else {
      g_warning("Failed to send vmbackup event: %s, result: %s.\n",
                msg, VM_SAFE_STR(result));
   }
   vm_free(result);
   g_free(msg);

   return success;
}


/**
 * Sends a command to the VMX asking it to update VMDB about a new backup event.
 * This will restart the keep-alive timer.
 *
 * Cancels the quiesce operation on RPC failure.
 *
 * @param[in]  event    The event to set.
 * @param[in]  code     Error code.
 * @param[in]  desc     Error description.
 *
 * @return TRUE on success.
 */

Bool
VmBackup_SendEvent(const char *event,
                   const uint32 code,
                   const char *desc)
{
   Bool success = VmBackup_SendEventNoAbort(event, code, desc);

   if (!success  && gBackupState->rpcState != VMBACKUP_RPC_STATE_IGNORE) {
      g_debug("Changing rpcState from %d to %d\n",
              gBackupState->rpcState, VMBACKUP_RPC_STATE_ERROR);
      gBackupState->rpcState = VMBACKUP_RPC_STATE_ERROR;
   }

   return success;
}


/**
 * Cleans up the backup state object and sends a "done" event to the VMX.
 */

static void
VmBackupFinalize(void)
{
   g_debug("*** %s\n", __FUNCTION__);
   ASSERT(gBackupState != NULL);

   if (gBackupState->abortTimer != NULL) {
      g_source_destroy(gBackupState->abortTimer);
      g_source_unref(gBackupState->abortTimer);
   }

   g_mutex_lock(&gBackupState->opLock);
   if (gBackupState->currentOp != NULL) {
      VmBackup_Cancel(gBackupState->currentOp);
      VmBackup_Release(gBackupState->currentOp);
   }
   g_mutex_unlock(&gBackupState->opLock);

   VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_DONE, VMBACKUP_SUCCESS, "");

   if (gBackupState->timerEvent != NULL) {
      g_source_destroy(gBackupState->timerEvent);
      g_source_unref(gBackupState->timerEvent);
   }

   if (gBackupState->keepAlive != NULL) {
      g_source_destroy(gBackupState->keepAlive);
      g_source_unref(gBackupState->keepAlive);
   }

   gBackupState->provider->release(gBackupState->provider);
   if (gBackupState->completer != NULL) {
      gBackupState->completer->release(gBackupState->completer);
   }
   g_mutex_clear(&gBackupState->opLock);
   vm_free(gBackupState->configDir);
   g_free(gBackupState->scriptArg);
   g_free(gBackupState->volumes);
   g_free(gBackupState->snapshots);
   g_free(gBackupState->excludedFileSystems);
   g_free(gBackupState->errorMsg);
   g_free(gBackupState);
   gBackupState = NULL;
}


/**
 * Starts the execution of the scripts for the given action type.
 *
 * @param[in]  type        Type of scripts being started.
 *
 * @return TRUE, unless starting the scripts fails for some reason.
 */

static Bool
VmBackupStartScripts(VmBackupScriptType type)
{
   const char *opName;
   VmBackupMState nextState;
   g_debug("*** %s\n", __FUNCTION__);

   switch (type) {
      case VMBACKUP_SCRIPT_FREEZE:
         opName = "VmBackupOnFreeze";
         nextState = VMBACKUP_MSTATE_SCRIPT_FREEZE;
         break;

      case VMBACKUP_SCRIPT_FREEZE_FAIL:
         opName = "VmBackupOnFreezeFail";
         nextState = VMBACKUP_MSTATE_SCRIPT_ERROR;
         break;

      case VMBACKUP_SCRIPT_THAW:
         opName = "VmBackupOnThaw";
         nextState = VMBACKUP_MSTATE_SCRIPT_THAW;
         break;

      default:
         NOT_REACHED();
   }

   if (gBackupState->execScripts &&
       !VmBackup_SetCurrentOp(gBackupState,
                              VmBackup_NewScriptOp(type, gBackupState),
                              NULL,
                              opName)) {
      VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR,
                         VMBACKUP_SCRIPT_ERROR,
                         "Error when starting custom quiesce scripts.");
      return FALSE;
   }

   gBackupState->machineState = nextState;
   return TRUE;
}


/**
 * Puts the state machine in the right state when an error occurs. The caller
 * should check for the state of the backup upon this function returning: if
 * it's IDLE, it means the backup state should be cleaned up.
 *
 * @return Whether the backup operation should be finalized.
 */

static gboolean
VmBackupOnError(void)
{
   switch (gBackupState->machineState) {
   case VMBACKUP_MSTATE_SCRIPT_FREEZE:
   case VMBACKUP_MSTATE_SYNC_ERROR:
      /* Next state is "script error". */
      if (!VmBackupStartScripts(VMBACKUP_SCRIPT_FREEZE_FAIL)) {
         gBackupState->machineState = VMBACKUP_MSTATE_IDLE;
      }
      break;

   case VMBACKUP_MSTATE_SYNC_FREEZE_WAIT:
   case VMBACKUP_MSTATE_SYNC_FREEZE:
   case VMBACKUP_MSTATE_SYNC_THAW:
      /* Next state is "sync error". */
      gBackupState->pollPeriod = 1000;
      gBackupState->machineState = VMBACKUP_MSTATE_SYNC_ERROR;
      g_signal_emit_by_name(gBackupState->ctx->serviceObj,
                            TOOLS_CORE_SIG_IO_FREEZE,
                            gBackupState->ctx,
                            FALSE);
      break;

   case VMBACKUP_MSTATE_SCRIPT_THAW:
   case VMBACKUP_MSTATE_COMPLETE_WAIT:
      /* Next state is "idle". */
      gBackupState->machineState = VMBACKUP_MSTATE_IDLE;
      break;

   default:
      g_error("Unexpected machine state on error: %s\n",
              VmBackupGetStateName(gBackupState->machineState));
   }

   return (gBackupState->machineState == VMBACKUP_MSTATE_IDLE);
}


/**
 * Cancels the current operation, unless we're already in an error state.
 */

static void
VmBackupDoAbort(void)
{
   g_debug("*** %s\n", __FUNCTION__);
   ASSERT(gBackupState != NULL);

   /*
    * Once we cancel the operation, we don't care about RPC state.
    */
   gBackupState->rpcState = VMBACKUP_RPC_STATE_IGNORE;

   if (gBackupState->machineState != VMBACKUP_MSTATE_SCRIPT_ERROR &&
       gBackupState->machineState != VMBACKUP_MSTATE_SYNC_ERROR) {
      const char *eventMsg = "Quiesce canceled.";
      /* Mark the current operation as cancelled. */
      g_mutex_lock(&gBackupState->opLock);
      if (gBackupState->currentOp != NULL) {
         VmBackup_Cancel(gBackupState->currentOp);
         VmBackup_Release(gBackupState->currentOp);
         gBackupState->currentOp = NULL;
      }
      g_mutex_unlock(&gBackupState->opLock);

#ifdef __linux__
      /* If quiescing has been completed, then undo it.  */
      if (gBackupState->machineState == VMBACKUP_MSTATE_SYNC_FREEZE) {
         g_debug("Canceling with file system already quiesced, undo quiescing "
                 "operation.\n");
         if (!gBackupState->provider->undo(gBackupState,
                                      gBackupState->provider->clientData)) {
            g_debug("Quiescing undo failed.\n");
            eventMsg = "Quiesce could not be canceled.";
         }
      }
#endif

      VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ABORT,
                         VMBACKUP_REMOTE_ABORT,
                         eventMsg);

      /* Transition to the error state. */
      if (VmBackupOnError()) {
         VmBackupFinalize();
      }
   }
}


/**
 * Timer callback to cancel the current operation.
 *
 * @param[in] data    Unused.
 *
 * @return FALSE
 */

static gboolean
VmBackupAbortTimer(gpointer data)
{
   ASSERT(gBackupState != NULL);
   g_warning("Canceling backup operation due to timeout.");
   g_source_unref(gBackupState->abortTimer);
   gBackupState->abortTimer = NULL;
   VmBackupDoAbort();
   return FALSE;
}


/**
 * Post-process the current operation. Calls the queued
 * operations as needed.
 *
 * @param[out]    pending     Tells if currentOp is pending.
 *
 * @return TRUE if currentOp finished or pending, FALSE on error.
 */

static gboolean
VmBackupPostProcessCurrentOp(gboolean *pending)
{
   gboolean retVal = TRUE;
   VmBackupOpStatus status = VMBACKUP_STATUS_FINISHED;

   g_debug("*** %s\n", __FUNCTION__);
   ASSERT(gBackupState != NULL);

   *pending = FALSE;

   g_mutex_lock(&gBackupState->opLock);

   if (gBackupState->currentOp != NULL) {
      g_debug("%s: checking %s\n", __FUNCTION__, gBackupState->currentOpName);
      status = VmBackup_QueryStatus(gBackupState->currentOp);
   }

   switch (status) {
   case VMBACKUP_STATUS_PENDING:
      *pending = TRUE;
      goto exit;

   case VMBACKUP_STATUS_FINISHED:
      if (gBackupState->currentOpName != NULL) {
         g_debug("Async request '%s' completed\n", gBackupState->currentOpName);
         VmBackup_Release(gBackupState->currentOp);
         gBackupState->currentOpName = NULL;
      }
      gBackupState->currentOp = NULL;
      break;

   default:
      {
         gchar *msg;
         if (gBackupState->errorMsg != NULL) {
            msg = g_strdup_printf("'%s' operation failed: %s",
                                  gBackupState->currentOpName,
                                  gBackupState->errorMsg);
         } else {
            msg = g_strdup_printf("'%s' operation failed.",
                                  gBackupState->currentOpName);
         }
         VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR,
                            VMBACKUP_UNEXPECTED_ERROR,
                            msg);
         g_free(msg);

         VmBackup_Release(gBackupState->currentOp);
         gBackupState->currentOp = NULL;
         retVal = FALSE;
         goto exit;
      }
   }

   /*
    * Keep calling the registered callback until it's either NULL, or
    * an asynchronous operation is scheduled.
    */
   while (gBackupState->callback != NULL) {
      VmBackupCallback cb = gBackupState->callback;
      gBackupState->callback = NULL;

      /*
       * Callback should not acquire opLock, but instead assume that
       * it holds the lock already.
       */
      if (cb(gBackupState)) {
         if (gBackupState->currentOp != NULL || gBackupState->forceRequeue) {
            goto exit;
         }
      } else {
         retVal = FALSE;
         goto exit;
      }
   }

exit:
   g_mutex_unlock(&gBackupState->opLock);
   return retVal;
}


/**
 * Callback to advance the state machine to next state once
 * current operation finishes.
 *
 * @param[in]  clientData     Unused.
 *
 * @return FALSE
 */

static gboolean
VmBackupAsyncCallback(void *clientData)
{
   gboolean opPending;
   g_debug("*** %s\n", __FUNCTION__);
   ASSERT(gBackupState != NULL);

   g_source_unref(gBackupState->timerEvent);
   gBackupState->timerEvent = NULL;

   /*
    * Move the state machine to the next state, if the
    * currentOp has finished.
    */
   if (!VmBackupPostProcessCurrentOp(&opPending)) {
      VmBackupOnError();
      goto exit;
   } else {
      /*
       * State transition can't be done if currentOp
       * has not finished yet.
       */
      if (opPending) {
         goto exit;
      }

      /*
       * VMX state might have changed when we were processing
       * currentOp. This is usually detected by failures in
       * sending backup event to the host.
       */
      if (gBackupState->rpcState == VMBACKUP_RPC_STATE_ERROR) {
         g_warning("Canceling backup operation due to RPC errors.");
         VmBackupDoAbort();

         /*
          * Check gBackupState, since canceling could cause a transition to
          * VMBACKUP_MSTATE_IDLE, in which case the VmBackupState structure
          * would be freed and gBackupState would be NULL.
          */
         if (gBackupState == NULL) {
            return FALSE;
         }
         goto exit;
      }
   }

   switch (gBackupState->machineState) {
   case VMBACKUP_MSTATE_SCRIPT_FREEZE:
      /* Next state is "sync freeze wait". */
      if (!VmBackupEnableSyncWait()) {
         VmBackupOnError();
      }
      break;

   case VMBACKUP_MSTATE_SYNC_FREEZE_WAIT:
      /* Next state is "sync freeze". */
      if (!VmBackupEnableSync()) {
         VmBackupOnError();
      }
      break;

   case VMBACKUP_MSTATE_SYNC_FREEZE:
      /*
       * The SYNC_FREEZE -> SYNC_THAW transition is handled by the RPC callback,
       * so this case is a no-op.
       */
      break;

   case VMBACKUP_MSTATE_SYNC_THAW:
      /* Next state is "script thaw". */
      g_signal_emit_by_name(gBackupState->ctx->serviceObj,
                            TOOLS_CORE_SIG_IO_FREEZE,
                            gBackupState->ctx,
                            FALSE);
      if (!VmBackupStartScripts(VMBACKUP_SCRIPT_THAW)) {
         VmBackupOnError();
      }
      break;

   case VMBACKUP_MSTATE_SCRIPT_THAW:
      /* Next state is "complete wait" or "idle". */
      if (!VmBackupEnableCompleteWait()) {
         VmBackupOnError();
      }
      break;

   case VMBACKUP_MSTATE_SCRIPT_ERROR:
   case VMBACKUP_MSTATE_COMPLETE_WAIT:
      /* Next state is "idle". */
      gBackupState->machineState = VMBACKUP_MSTATE_IDLE;
      break;

   case VMBACKUP_MSTATE_SYNC_ERROR:
      /* Next state is "script error". */
      if (!VmBackupStartScripts(VMBACKUP_SCRIPT_FREEZE_FAIL)) {
         VmBackupOnError();
      }
      break;

   default:
      g_error("Unexpected machine state: %s\n",
              VmBackupGetStateName(gBackupState->machineState));
   }

exit:
   /* If the state machine is back in IDLE, it means the backup operation finished. */
   if (gBackupState->machineState == VMBACKUP_MSTATE_IDLE) {
      VmBackupFinalize();
   } else {
      gBackupState->forceRequeue = FALSE;
      VMBACKUP_ENQUEUE_EVENT();
   }
   return FALSE;
}


/**
 * Calls the sync provider's start function and moves the state
 * machine to next state.
 *
 * For Windows next state is VMBACKUP_MSTATE_SYNC_FREEZE, whereas
 * next state is VMBACKUP_MSTATE_SYNC_FREEZE_WAIT for Linux. Details
 * below.
 *
 * @return Whether the provider's start callback was successful.
 */

static Bool
VmBackupEnableSyncWait(void)
{
   g_debug("*** %s\n", __FUNCTION__);
   g_signal_emit_by_name(gBackupState->ctx->serviceObj,
                         TOOLS_CORE_SIG_IO_FREEZE,
                         gBackupState->ctx,
                         TRUE);
#if defined(_WIN32)
   gBackupState->freezeStatus = VMBACKUP_FREEZE_FINISHED;
   if (!gBackupState->provider->start(gBackupState,
                                      gBackupState->provider->clientData)) {
#else
   /*
    * PR 1020224:
    * For Linux, FIFREEZE could take really long at times,
    * especially when guest is running IO load. We have also
    * seen slowness in performing open() on NFS mount points.
    * So, we need to run freeze operation in a separate thread
    * and track it with an extra state in the state machine.
    */
   gBackupState->freezeStatus = VMBACKUP_FREEZE_PENDING;
   if (!ToolsCorePool_SubmitTask(gBackupState->ctx,
                                 gBackupState->provider->start,
                                 gBackupState,
                                 NULL)) {
      g_warning("Failed to submit backup start task.");
#endif
      g_signal_emit_by_name(gBackupState->ctx->serviceObj,
                            TOOLS_CORE_SIG_IO_FREEZE,
                            gBackupState->ctx,
                            FALSE);
      VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR,
                         VMBACKUP_SYNC_ERROR,
                         "Error when enabling the sync provider.");
      return FALSE;
   }

#if defined(_WIN32)
   /* Move to next state */
   gBackupState->machineState = VMBACKUP_MSTATE_SYNC_FREEZE;
#else
   g_debug("Submitted backup start task.");
   /* Move to next state */
   gBackupState->machineState = VMBACKUP_MSTATE_SYNC_FREEZE_WAIT;
#endif

   return TRUE;
}


/**
 * Calls the completer's start function and moves the state
 * machine to next state.
 *
 * @return Whether the completer's start callback was successful.
 */

static Bool
VmBackupEnableCompleteWait(void)
{
   Bool ret = TRUE;

   g_debug("*** %s\n", __FUNCTION__);

   if (gBackupState->completer == NULL) {
      gBackupState->machineState = VMBACKUP_MSTATE_IDLE;
      goto exit;
   }

   if (gBackupState->abortTimer != NULL) {
      g_source_destroy(gBackupState->abortTimer);
      g_source_unref(gBackupState->abortTimer);

      /* Host make timeout maximum as 15 minutes for complete process. */
      if (gBackupState->timeout > GUEST_QUIESCE_DEFAULT_TIMEOUT_IN_SEC) {
         gBackupState->timeout = GUEST_QUIESCE_DEFAULT_TIMEOUT_IN_SEC;
      }

      if (gBackupState->timeout != 0) {
         g_debug("Using completer timeout: %u\n", gBackupState->timeout);
         gBackupState->abortTimer =
            g_timeout_source_new_seconds(gBackupState->timeout);
         VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx,
            gBackupState->abortTimer,
            VmBackupAbortTimer,
            NULL,
            NULL);
      }
   }

   if (gBackupState->completer->start(gBackupState,
                                      gBackupState->completer->clientData)) {
      /* Move to next state */
      gBackupState->machineState = VMBACKUP_MSTATE_COMPLETE_WAIT;
   } else {
      VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR,
                         VMBACKUP_SYNC_ERROR,
                         "Error when enabling the sync provider.");
      ret = FALSE;
   }

exit:
   return ret;
}


/**
 * After sync provider's start function returns and moves the state
 * machine to VMBACKUP_MSTATE_SYNC_FREEZE state.
 *
 * @return Whether the provider's start callback was successful.
 */

static Bool
VmBackupEnableSync(void)
{
   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState->freezeStatus == VMBACKUP_FREEZE_ERROR) {
      g_signal_emit_by_name(gBackupState->ctx->serviceObj,
                            TOOLS_CORE_SIG_IO_FREEZE,
                            gBackupState->ctx,
                            FALSE);
      VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR,
                         VMBACKUP_SYNC_ERROR,
                         "Error when enabling the sync provider.");
      return FALSE;

   } else if (gBackupState->freezeStatus == VMBACKUP_FREEZE_CANCELED ||
              gBackupState->freezeStatus == VMBACKUP_FREEZE_FINISHED) {
      /* Move to next state */
      gBackupState->machineState = VMBACKUP_MSTATE_SYNC_FREEZE;
   } else {
      ASSERT(gBackupState->freezeStatus == VMBACKUP_FREEZE_PENDING);
   }

   return TRUE;
}


/**
 * Starts the quiesce operation according to the supplied specification unless
 * some unexpected error occurs.
 *
 * @param[in]  data          RPC data.
 * @param[in]  forceQuiesce  Only allow Vss quiescing on Windows platform or
 *                           SyncDriver quiescing on Linux platform ( only file
 *                           system quiescing )
 *
 * @return TRUE on success.
 */

static gboolean
VmBackupStartCommon(RpcInData *data,
                    gboolean forceQuiesce)
{
   ToolsAppCtx *ctx = data->appCtx;
   VmBackupSyncProvider *provider = NULL;
   VmBackupSyncCompleter *completer = NULL;

   size_t i;

   /* List of available providers, in order of preference for loading. */
   struct SyncProvider {
      VmBackupSyncProvider *(*ctor)(void);
      const gchar *cfgEntry;
   } providers[] = {
#if defined(_WIN32)
      { VmBackup_NewVssProvider, VMBACKUP_CFG_ENABLEVSS},
#endif
      { VmBackup_NewSyncDriverProvider, "enableSyncDriver" },
      { VmBackup_NewNullProvider, NULL },
   };

   if (forceQuiesce) {
      if (gBackupState->quiesceApps || gBackupState->quiesceFS) {
         /*
          * If quiescing is requested on windows platform,
          * only allow VSS provider
          */
#if defined(_WIN32)
         if (VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                      VMBACKUP_CFG_ENABLEVSS, TRUE)) {
            provider = VmBackup_NewVssProvider();
            if (provider != NULL) {
               completer = VmBackup_NewVssCompleter(provider);
               if (completer == NULL) {
                  g_warning("VSS completion helper cannot be initialized.");
                  provider->release(provider);
                  provider = NULL;
               }
            }
         }
#elif defined(__linux__)
         /*
          * If quiescing is requested on linux platform,
          * only allow SyncDriver provider
          */
         if (gBackupState->quiesceFS &&
             VMBACKUP_CONFIG_GET_BOOL(ctx->config, "enableSyncDriver", TRUE)) {
            provider = VmBackup_NewSyncDriverOnlyProvider();
         }
#endif
      } else {
         /* If no quiescing is requested only allow null provider */
         provider = VmBackup_NewNullProvider();
      }
   } else {
      /* Instantiate the sync provider. */
      for (i = 0; i < ARRAYSIZE(providers); i++) {
         struct SyncProvider *sp = &providers[i];

         if (VMBACKUP_CONFIG_GET_BOOL(ctx->config, sp->cfgEntry, TRUE)) {
            provider = sp->ctor();
            if (provider != NULL) {
#if defined(_WIN32)
               if (sp->cfgEntry != NULL &&
                   Str_Strcmp(sp->cfgEntry, VMBACKUP_CFG_ENABLEVSS) == 0) {
                  completer = VmBackup_NewVssCompleter(provider);
                  if (completer == NULL) {
                     g_warning("VSS completion helper cannot be initialized.");
                     provider->release(provider);
                     provider = NULL;
                     continue;
                  }
                  break;
               }
#else
               break;
#endif
            }
         }
      }
   }

   if (provider == NULL) {
      g_warning("Requested quiescing cannot be initialized.");
      goto error;
   }

   /* Instantiate the backup state and start the operation. */
   gBackupState->ctx = data->appCtx;
   gBackupState->pollPeriod = 1000;
   gBackupState->machineState = VMBACKUP_MSTATE_IDLE;
   gBackupState->freezeStatus = VMBACKUP_FREEZE_FINISHED;
   gBackupState->provider = provider;
   gBackupState->completer = completer;
   gBackupState->needsPriv = FALSE;
   g_mutex_init(&gBackupState->opLock);
   gBackupState->enableNullDriver = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                             "enableNullDriver",
                                                             TRUE);
   gBackupState->rpcState = VMBACKUP_RPC_STATE_NORMAL;

   g_debug("Using quiesceApps = %d, quiesceFS = %d, allowHWProvider = %d,"
           " execScripts = %d, scriptArg = %s, timeout = %u,"
           " enableNullDriver = %d, forceQuiesce = %d\n",
           gBackupState->quiesceApps, gBackupState->quiesceFS,
           gBackupState->allowHWProvider, gBackupState->execScripts,
           (gBackupState->scriptArg != NULL) ? gBackupState->scriptArg : "",
           gBackupState->timeout, gBackupState->enableNullDriver, forceQuiesce);
#if defined(__linux__)
   gBackupState->excludedFileSystems =
         VMBACKUP_CONFIG_GET_STR(ctx->config, "excludedFileSystems", NULL);
   gBackupState->ignoreFrozenFS =
       VMBACKUP_CONFIG_GET_BOOL(ctx->config, "ignoreFrozenFileSystems", FALSE);

   g_debug("Using excludedFileSystems = \"%s\", ignoreFrozenFileSystems = %d\n",
           (gBackupState->excludedFileSystems != NULL) ?
            gBackupState->excludedFileSystems : "(null)",
           gBackupState->ignoreFrozenFS);
#endif
   g_debug("Quiescing volumes: %s",
           (gBackupState->volumes) ? gBackupState->volumes : "(null)");

   gBackupState->configDir = GuestApp_GetConfPath();
   if (gBackupState->configDir == NULL) {
      g_warning("Error getting configuration directory.");
      goto error;
   }

   VmBackup_SendEvent(VMBACKUP_EVENT_RESET, VMBACKUP_SUCCESS, "");

   if (!VmBackupStartScripts(VMBACKUP_SCRIPT_FREEZE)) {
      goto error;
   }

   /*
    * VC has a 15 minute timeout for quiesced snapshots. After that timeout,
    * it just discards the operation and sends an error to the caller. But
    * Tools can still keep running, blocking any new quiesced snapshot
    * requests. So we set up our own timer (which is configurable, in case
    * anyone wants to play with it), so that we cancel any ongoing operation
    * if we also hit that timeout.
    *
    * First check if the timeout is specified by the RPC command, if not,
    * check the tools.conf file, otherwise use the default.
    *
    * See bug 506106.
    */
   if (gBackupState->timeout == 0) {
      gBackupState->timeout = VmBackupGetTimeout(ctx->config,
                                 GUEST_QUIESCE_DEFAULT_TIMEOUT_IN_SEC);
   }

   /* Treat "0" as no timeout. */
   if (gBackupState->timeout != 0) {
      gBackupState->abortTimer =
          g_timeout_source_new_seconds(gBackupState->timeout);
      VMTOOLSAPP_ATTACH_SOURCE(gBackupState->ctx,
                               gBackupState->abortTimer,
                               VmBackupAbortTimer,
                               NULL,
                               NULL);
   }

   VMBACKUP_ENQUEUE_EVENT();
   return RPCIN_SETRETVALS(data, "", TRUE);

error:
   if (gBackupState->keepAlive != NULL) {
      g_source_destroy(gBackupState->keepAlive);
      g_source_unref(gBackupState->keepAlive);
      gBackupState->keepAlive = NULL;
   }
   if (gBackupState->provider) {
      gBackupState->provider->release(gBackupState->provider);
   }
   if (gBackupState->completer) {
      gBackupState->completer->release(gBackupState->completer);
   }
   vm_free(gBackupState->configDir);
   g_free(gBackupState->scriptArg);
   g_free(gBackupState->volumes);
   g_free(gBackupState);
   gBackupState = NULL;
   return RPCIN_SETRETVALS(data, "Error initializing quiesce operation.",
                           FALSE);
}


/* RpcIn callbacks. */


/**
 * Handler for the "vmbackup.start" message. Starts the "freeze" scripts
 * unless there's another backup operation going on or some other
 * unexpected error occurs.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE on success.
 */

static gboolean
VmBackupStart(RpcInData *data)
{
   ToolsAppCtx *ctx = data->appCtx;

   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState != NULL) {
      return RPCIN_SETRETVALS(data, "Quiesce operation already in progress.",
                              FALSE);
   }
   gBackupState = g_new0(VmBackupState, 1);
   if (data->argsSize > 0) {
      int generateManifests = 0;
      uint32 index = 0;

      if (StrUtil_GetNextIntToken(&generateManifests, &index, data->args, " ")) {
         gBackupState->generateManifests = generateManifests;
      }
      gBackupState->quiesceApps = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                           "quiesceApps",
                                                           TRUE);
      gBackupState->quiesceFS = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                         "quiesceFS",
                                                         TRUE);
      gBackupState->allowHWProvider = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                             "allowHWProvider",
                                                             TRUE);
      gBackupState->execScripts = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                           "execScripts",
                                                           TRUE);
      gBackupState->scriptArg = VMBACKUP_CONFIG_GET_STR(ctx->config,
                                                        "scriptArg",
                                                        NULL);
      gBackupState->timeout = VmBackupGetTimeout(ctx->config, 0);
      gBackupState->vssUseDefault = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                             "vssUseDefault",
                                                             TRUE);

      /* get volume uuids if provided */
      if (data->args[index] != '\0') {
         gBackupState->volumes = g_strndup(data->args + index,
                                           data->argsSize - index);
      }
   }
   return VmBackupStartCommon(data, VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                             "forceQuiesce",
                                                             FALSE));
}


/**
 * Handler for the "vmbackup.startWithOpts" message. Starts processing the
 * quiesce operation according to the supplied specification unless there's
 * another backup operation going on or some other unexpected error occurs.
 *
 * . If createManifest is true, the guest generates a manifest about the
 *   application involved during quiescing.
 * . If quiesceApps is true, the guest involves applications during
 *   quiescing. If quiesceFS is true, the guest performs file system
 *   quiescing. If both quiesceApps and quiesceFS are true, the guest
 *   falls back to file system quiescing if application quiescing is not
 *   supported in the guest. If both quiesceApps and quiesceFS are false,
 *   the guest performs no quiescing but will still run the custom scripts
 *   provided execScripts is true.
 * . If writableSnapshot is true, the guest assumes that writable snapshot
 *   based quiescing can be performed.
 * . If execScripts is true, the guest calls pre-freeze and post-thaw
 *   scripts before and after quiescing.
 * . The scriptArg string is passed to the pre-freeze and post-thaw scripts
 *   as an argument so that the scripts can be configured to perform
 *   actions based this argument.
 * . The timeout in seconds overrides the default timeout of 15 minutes
 *   that the guest uses to cancel a long quiesce operation. If the timeout
 *   is 0, the default timeout is used.
 * . The volumes argument is a list of diskUuids separated by space.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE on success.
 */

static gboolean
VmBackupStartWithOpts(RpcInData *data)
{
   ToolsAppCtx *ctx = data->appCtx;
   GuestQuiesceParams *params;
   GuestQuiesceParamsV1 *paramsV1 = NULL;
   GuestQuiesceParamsV2 *paramsV2;
   gboolean retval;

   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState != NULL) {
      return RPCIN_SETRETVALS(data, "Quiesce operation already in progress.",
                              FALSE);
   }
   params = (GuestQuiesceParams *)data->args;

#if defined(_WIN32)
   if (params->ver != GUESTQUIESCEPARAMS_V1 &&
       params->ver != GUESTQUIESCEPARAMS_V2) {
      g_warning("%s: Incompatible quiesce parameter version. \n", __FUNCTION__);
      return RPCIN_SETRETVALS(data, "Incompatible quiesce parameter version",
                              FALSE);
   }
#else
   if (params->ver != GUESTQUIESCEPARAMS_V1) {
      g_warning("%s: Incompatible quiesce parameter version. \n", __FUNCTION__);
      return RPCIN_SETRETVALS(data, "Incompatible quiesce parameter version",
                              FALSE);
   }
#endif

   gBackupState = g_new0(VmBackupState, 1);

   if (params->ver == GUESTQUIESCEPARAMS_V1) {
      paramsV1 = params->GuestQuiesceParams_u.guestQuiesceParamsV1;
      gBackupState->vssUseDefault = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                             "vssUseDefault",
                                                             TRUE);
   } else if (params->ver == GUESTQUIESCEPARAMS_V2) {
      paramsV2 = params->GuestQuiesceParams_u.guestQuiesceParamsV2;
      paramsV1 = &paramsV2->paramsV1;
      gBackupState->vssBackupContext = paramsV2->vssBackupContext;
      gBackupState->vssBackupType = paramsV2->vssBackupType;
      gBackupState->vssBootableSystemState = paramsV2->vssBootableSystemState;
      gBackupState->vssPartialFileSupport = paramsV2->vssPartialFileSupport;
      gBackupState->vssUseDefault = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                             "vssUseDefault",
                                                             FALSE);
   }

   if (paramsV1 != NULL) {
      gBackupState->generateManifests = paramsV1->createManifest;
      gBackupState->quiesceApps = paramsV1->quiesceApps;
      gBackupState->quiesceFS = paramsV1->quiesceFS;
      gBackupState->allowHWProvider = paramsV1->writableSnapshot;
      gBackupState->execScripts = paramsV1->execScripts;
      gBackupState->scriptArg = g_strndup(paramsV1->scriptArg,
                                          strlen(paramsV1->scriptArg));
      gBackupState->timeout = paramsV1->timeout;
      gBackupState->volumes = g_strndup(paramsV1->diskUuids,
                                        strlen(paramsV1->diskUuids));
   }

   retval = VmBackupStartCommon(data, VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                                               "forceQuiesce",
                                                               TRUE));
   return retval;
}


/**
 * Cancels the current operation if one is active, and stops the backup
 * process. If the sync provider has been activated, tell it to cancel 
 * the ongoing operation.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE
 */

static gboolean
VmBackupAbort(RpcInData *data)
{
   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState == NULL) {
      return RPCIN_SETRETVALS(data, "Error: no quiesce operation in progress",
                              FALSE);
   }

   VmBackupDoAbort();
   return RPCIN_SETRETVALS(data, "", TRUE);
}


/**
 * Notifies the sync provider to thaw the file systems and puts the state
 * machine in the SYNC_THAW state.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE
 */

static gboolean
VmBackupSnapshotDone(RpcInData *data)
{
   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState == NULL) {
      return RPCIN_SETRETVALS(data, "Error: no quiesce operation in progress", FALSE);
   } else if (gBackupState->machineState != VMBACKUP_MSTATE_SYNC_FREEZE) {
      g_warning("Error: unexpected state for snapshot done message: %s",
                VmBackupGetStateName(gBackupState->machineState));
      return RPCIN_SETRETVALS(data,
                              "Error: unexpected state for quiesce done message.",
                              FALSE);
   } else {
      if (data->argsSize > 1) {
         gBackupState->snapshots = g_strndup(data->args + 1, data->argsSize - 1);
      }
      if (!gBackupState->provider->snapshotDone(gBackupState,
                                                gBackupState->provider->clientData)) {
         VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR,
                            VMBACKUP_SYNC_ERROR,
                            "Error when notifying the sync provider.");
         if (VmBackupOnError()) {
            VmBackupFinalize();
         }
      } else {
         gBackupState->machineState = VMBACKUP_MSTATE_SYNC_THAW;
      }
      return RPCIN_SETRETVALS(data, "", TRUE);
   }
}


/**
 * Notifies the completer to complete the provider process
 * machine in the COMPLETE_WAIT state.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE
 */

static gboolean
VmBackupSnapshotCompleted(RpcInData *data)
{
   g_debug("*** %s\n", __FUNCTION__);

   if (gBackupState == NULL ||
       gBackupState->completer == NULL) {
      return RPCIN_SETRETVALS(data, "Error: no quiesce complete in progress",
                              FALSE);
   } else if (gBackupState->machineState != VMBACKUP_MSTATE_COMPLETE_WAIT) {
      g_warning("Error: unexpected state for snapshot complete message: %s",
                VmBackupGetStateName(gBackupState->machineState));
      return RPCIN_SETRETVALS(data,
                              "Error: unexpected state for complete message.",
                              FALSE);
   } else {
      if (!gBackupState->completer->snapshotCompleted(gBackupState,
             gBackupState->completer->clientData)) {
         VmBackup_SendEvent(VMBACKUP_EVENT_REQUESTOR_ERROR,
                            VMBACKUP_SYNC_ERROR,
                            "Error when notifying the sync completer.");
         if (VmBackupOnError()) {
            VmBackupFinalize();
         }
      }
      return RPCIN_SETRETVALS(data, "", TRUE);
   }
}


/**
 * Prints some information about the plugin's state to the log.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Unused.
 * @param[in]  data     Unused.
 */

static void
VmBackupDumpState(gpointer src,
                  ToolsAppCtx *ctx,
                  gpointer data)
{
   if (gBackupState == NULL) {
      ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "Backup is idle.\n");
   } else {
      ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN,
                         "Backup is in state: %s\n",
                         VmBackupGetStateName(gBackupState->machineState));
   }
}


/**
 * Reset callback.  Currently does nothing.
 *
 * @param[in]  src      The source object.  Unused.
 * @param[in]  ctx      Unused.
 * @param[in]  data     Unused.
 */

static void
VmBackupReset(gpointer src,
              ToolsAppCtx *ctx,
              gpointer data)
{

}


/**
 * Cleans up the plugin.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  data     Unused.
 */

static void
VmBackupShutdown(gpointer src,
                 ToolsAppCtx *ctx,
                 gpointer data)
{
   g_debug("*** %s\n", __FUNCTION__);
   if (gBackupState != NULL) {
      VmBackupFinalize();
   }
}


/*
 *******************************************************************************
 * VmBackupCapabilities --                                               */ /**
 *
 * Sends the vmbackup capability to the VMX.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  set      Whether setting or unsetting the capability.
 * @param[in]  data     Unused.
 *
 * @return An array with the capabilities to set.
 *
 *******************************************************************************
 */

static GArray *
VmBackupCapabilities(gpointer src,
                     ToolsAppCtx *ctx,
                     gboolean set,
                     gpointer data)
{
   ToolsAppCapability caps[] = {
      { TOOLS_CAP_NEW, NULL, CAP_VMBACKUP_NVME, },
   };
   Bool enableNVMe = VMBACKUP_CONFIG_GET_BOOL(ctx->config,
                                              VMBACKUP_CFG_ENABLENVME,
                                              VMBACKUP_CFG_ENABLENVME_DEFAULT);

   g_debug("%s - vmbackup NVMe feature is %s\n", __FUNCTION__,
           enableNVMe ? "enabled" : "disabled");

   caps[0].value = enableNVMe && set ? 1 : 0;

   return VMTools_WrapArray(caps, sizeof *caps, ARRAYSIZE(caps));
}


/**
 * Plugin entry point. Initializes internal plugin state.
 *
 * @param[in]  ctx   The app context.
 *
 * @return The registration data. Returns NULL if there is no sync provider
 *         available to quiesce the filesystems.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "vmbackup",
      NULL,
      NULL
   };

   RpcChannelCallback rpcs[] = {
      { VMBACKUP_PROTOCOL_START, VmBackupStart, NULL, NULL, NULL, 0 },
      /* START_WITH_OPTS command supported only on Windows for now */
      { VMBACKUP_PROTOCOL_START_WITH_OPTS, VmBackupStartWithOpts, NULL,
                    xdr_GuestQuiesceParams, NULL, sizeof (GuestQuiesceParams) },
      { VMBACKUP_PROTOCOL_ABORT, VmBackupAbort, NULL, NULL, NULL, 0 },
      { VMBACKUP_PROTOCOL_SNAPSHOT_COMPLETED, VmBackupSnapshotCompleted, NULL,
                    NULL, NULL, 0 },
      { VMBACKUP_PROTOCOL_SNAPSHOT_DONE, VmBackupSnapshotDone, NULL, NULL, NULL, 0 }
   };
   ToolsPluginSignalCb sigs[] = {
      { TOOLS_CORE_SIG_CAPABILITIES, VmBackupCapabilities, NULL },
      { TOOLS_CORE_SIG_DUMP_STATE, VmBackupDumpState, NULL },
      { TOOLS_CORE_SIG_RESET, VmBackupReset, NULL },
      { TOOLS_CORE_SIG_SHUTDOWN, VmBackupShutdown, NULL },
   };
   ToolsAppReg regs[] = {
      { TOOLS_APP_GUESTRPC, VMTools_WrapArray(rpcs, sizeof *rpcs, ARRAYSIZE(rpcs)) },
      { TOOLS_APP_SIGNALS, VMTools_WrapArray(sigs, sizeof *sigs, ARRAYSIZE(sigs)) },
   };

#if defined(G_PLATFORM_WIN32)
   /*
    * If initializing COM fails (unlikely), we'll fallback to the sync driver
    * or the null provider, depending on the configuration. On success, send
    * a request to unregister the VMware snapshot provider.
    */
   if (ToolsCore_InitializeCOM(ctx)) {
      VmBackup_UnregisterSnapshotProvider();
   } else {
      g_warning("Failed to initialize COM, VSS support will be unavailable.");
   }
#endif

   regData.regs = VMTools_WrapArray(regs, sizeof *regs, ARRAYSIZE(regs));

   g_signal_new(TOOLS_CORE_SIG_IO_FREEZE,
                G_OBJECT_TYPE(ctx->serviceObj),
                0,
                0,
                NULL,
                NULL,
                g_cclosure_user_marshal_VOID__POINTER_BOOLEAN,
                G_TYPE_NONE,
                2,
                G_TYPE_POINTER,
                G_TYPE_BOOLEAN);

   return &regData;
}

   070701000003B1000081A40000000000000000000000016822550500004D40000000000000000000000000000000000000004D00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/syncDriverOps.c  /*********************************************************
 * Copyright (C) 2007-2019, 2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * syncDriverOps.c --
 *
 * Implements the sync driver provider for the backup state machine.
 */

#include "vmBackupInt.h"

#include "file.h"
#include "procMgr.h"
#include "syncDriver.h"
#include "util.h"
#include "syncManifest.h"

#ifdef _WIN32
#include <windows.h>
#include <process.h>
#endif

/*
 * Define an enumeration type VmBackupOpType and a corresponding array
 * VmBackupOpName whose entries provide the printable names of the
 * enumeration ids in VmBackupOpType.
 *
 * VmBackupOpType and VmBackupOpName are each defined as an invocation
 * of a macro VMBACKUP_OPLIST.  VMBACKUP_OPLIST specifies a list of
 * enumeration ids using a macro VMBACKUP_OP that must be defined before
 * invoking VMBACKUP_OPLIST.  VMBACKUP_OP takes a single argument, which
 * should be an enumeration id, and is defined to generate from the id
 * either the id itself or a string to be used as its printable name.  The
 * result is that an invocation of VMBACKUP_OPLIST generates either the
 * list of enumeration ids or the list of their printable names.
 */
#define VMBACKUP_OPLIST         \
    VMBACKUP_OP(OP_FREEZE),     \
    VMBACKUP_OP(OP_THAW),       \
    VMBACKUP_OP(OP_UNDO),

#define VMBACKUP_OPID(id)       id
#define VMBACKUP_OPNAME(id)     #id

#undef VMBACKUP_OP
#define VMBACKUP_OP(id)       VMBACKUP_OPID(id)

typedef enum {
   VMBACKUP_OPLIST
} VmBackupOpType;

#undef VMBACKUP_OP
#define VMBACKUP_OP(id)       VMBACKUP_OPNAME(id)

static const char *VmBackupOpName[] = {
   VMBACKUP_OPLIST
};

#undef VMBACKUP_OP

typedef struct VmBackupDriverOp {
   VmBackupOp callbacks;
   const char *volumes;
   VmBackupOpType opType;
   Bool canceled;
   SyncDriverHandle *syncHandle;
   SyncManifest *manifest;
} VmBackupDriverOp;

/*
 *-----------------------------------------------------------------------------
 *
 * VmBackupDriverThaw --
 *
 *    Thaws the frozen filesystems, and cleans up internal state kept by the
 *    code.
 *
 * Results:
 *    Whether thawing was successful.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmBackupDriverThaw(SyncDriverHandle *handle)
{
   Bool success = SyncDriver_Thaw(*handle);
   SyncDriver_CloseHandle(handle);
   return success;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupDriverOpQuery --
 *
 *    Checks the status of the operation that is enabling or disabling the
 *    sync driver.
 *
 * Result
 *    VMBACKUP_STATUS_PENDING:   still working.
 *    VMBACKUP_STATUS_FINISHED:  done.
 *    VMBACKUP_STATUS_CANCELED:  cancel request fulfilled.
 *    VMBACKUP_STATUS_ERROR:     oops.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static VmBackupOpStatus
VmBackupDriverOpQuery(VmBackupOp *_op) // IN
{
   VmBackupDriverOp *op = (VmBackupDriverOp *) _op;
   VmBackupOpStatus ret;

   if (op->opType == OP_FREEZE) {
      SyncDriverStatus st = SyncDriver_QueryStatus(*op->syncHandle, 0);

      g_debug("SyncDriver status: %d\n", st);
      switch(st) {
      case SYNCDRIVER_BUSY:
         ret = VMBACKUP_STATUS_PENDING;
         break;

      case SYNCDRIVER_IDLE:
         if (op->canceled) {
            VmBackupDriverThaw(op->syncHandle);
         }
         /*
          * This prevents the release callback from freeing the handle, which
          * will be used when thawing in the POSIX case.
          */
         op->syncHandle = NULL;
         ret = (op->canceled) ? VMBACKUP_STATUS_CANCELED : VMBACKUP_STATUS_FINISHED;
         break;

      default:
         VmBackupDriverThaw(op->syncHandle);
         ret = VMBACKUP_STATUS_ERROR;
         break;
      }
   } else {
      if (op->manifest != NULL) {
         SyncManifestSend(op->manifest);
      }
      ret = VMBACKUP_STATUS_FINISHED;
   }

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupDriverOpRelease --
 *
 *    Cleans up data held by the state object.
 *
 * Result
 *    The status of the app.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VmBackupDriverOpRelease(VmBackupOp *_op)  // IN
{
   VmBackupDriverOp *op = (VmBackupDriverOp *) _op;

   g_free(op->syncHandle);
   SyncManifestRelease(op->manifest);
   free(op);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupDriverOpCancel --
 *
 *    Cancel an ongoing sync driver operation. This doesn't actually
 *    cancel the operation, but rather waits for it to finish, since
 *    just forcing the worker thread to quit might have undesired side
 *    effects.  This will not cancel thaw operations.
 *
 * Result
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VmBackupDriverOpCancel(VmBackupOp *_op)   // IN
{
   VmBackupDriverOp *op = (VmBackupDriverOp *) _op;
   op->canceled = TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupNewDriverOp --
 *
 *    Enables or disables the sync driver. Note: "volumes" is not copied,
 *    to avoid unnecessary waste of memory, since it's kept in the global
 *    backup state structure.
 *
 * Result
 *    A state object, unless some error occurs.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static VmBackupDriverOp *
VmBackupNewDriverOp(VmBackupState *state,       // IN
                    VmBackupOpType opType,      // IN
                    SyncDriverHandle *handle,   // IN
                    const char *volumes,        // IN
                    Bool useNullDriverPrefs)    // IN
{
   Bool success;
   VmBackupDriverOp *op = NULL;

   g_return_val_if_fail((handle == NULL ||
                         *handle == SYNCDRIVER_INVALID_HANDLE) ||
                        opType != OP_FREEZE,
                        NULL);

   op = Util_SafeMalloc(sizeof *op);
   memset(op, 0, sizeof *op);

   op->callbacks.queryFn = VmBackupDriverOpQuery;
   op->callbacks.cancelFn = VmBackupDriverOpCancel;
   op->callbacks.releaseFn = VmBackupDriverOpRelease;
   op->opType = opType;
   op->volumes = volumes;

   op->syncHandle = g_new0(SyncDriverHandle, 1);
   *op->syncHandle = (handle != NULL) ? *handle : SYNCDRIVER_INVALID_HANDLE;

   switch (opType) {
      case OP_FREEZE:
         success = SyncDriver_Freeze(op->volumes,
                                     useNullDriverPrefs ?
                                        state->enableNullDriver : FALSE,
                                     op->syncHandle,
                                     state->excludedFileSystems,
                                     state->ignoreFrozenFS);
         break;
      case OP_THAW:
         op->manifest = SyncNewManifest(state, *op->syncHandle);
         success = VmBackupDriverThaw(op->syncHandle);
         break;
      default:
         ASSERT(opType == OP_UNDO);
         success = VmBackupDriverThaw(op->syncHandle);
         break;
   }
   if (!success) {
      g_warning("Error trying to perform %s on filesystems.",
                VmBackupOpName[opType]);
      g_free(op->syncHandle);
      SyncManifestRelease(op->manifest);
      free(op);
      op = NULL;
   }
   return op;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverReadyForSnapshot --
 *
 *    Sends an event to the VMX indicating that the guest is ready for a
 *    snapshot to be taken (i.e., scripts have run and sync driver is
 *    enabled).
 *
 * Result
 *    TRUE, unless sending the message fails.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmBackupSyncDriverReadyForSnapshot(VmBackupState *state)
{
   SyncDriverHandle *handle = state->clientData;

   g_debug("*** %s\n", __FUNCTION__);
   if (handle != NULL && *handle != SYNCDRIVER_INVALID_HANDLE) {
      Bool success;
      success = VmBackup_SendEvent(VMBACKUP_EVENT_SNAPSHOT_COMMIT, 0, "");
      if (success) {
         state->freezeStatus = VMBACKUP_FREEZE_FINISHED;
      } else {
         /*
          * If the vmx does not know this event (e.g. due to an RPC timeout),
          * then filesystems need to be thawed here because snapshotDone
          * will not be sent by the vmx.
          */
         g_debug("VMX state changed; thawing filesystems.\n");
         if (!VmBackupDriverThaw(handle)) {
            g_warning("Error thawing filesystems.\n");
         }
         state->freezeStatus = VMBACKUP_FREEZE_ERROR;
      }
      return success;
   }

   /* op failed */
   state->freezeStatus = VMBACKUP_FREEZE_ERROR;
   return TRUE;
}


/* Sync provider implementation. */

#if defined(_WIN32)
/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverStart --
 *
 *    Starts an asynchronous operation to enable the sync driver.
 *
 * Result
 *    TRUE, unless an error occurs.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmBackupSyncDriverStart(VmBackupState *state,
                        void *clientData)
{
   VmBackupDriverOp *op;

   g_debug("*** %s\n", __FUNCTION__);
   op = VmBackupNewDriverOp(state, OP_FREEZE, NULL, state->volumes, TRUE);

   if (op != NULL) {
      state->clientData = op->syncHandle;
   }

   return VmBackup_SetCurrentOp(state,
                                (VmBackupOp *) op,
                                VmBackupSyncDriverReadyForSnapshot,
                                __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverOnlyStart --
 *
 *    Starts an asynchronous operation to enable the sync driver without using
 *    NullDriver fallback.
 *
 * Result
 *    TRUE, unless an error occurs.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmBackupSyncDriverOnlyStart(VmBackupState *state,
                            void *clientData)
{
   VmBackupDriverOp *op;

   g_debug("*** %s\n", __FUNCTION__);
   op = VmBackupNewDriverOp(state, OP_FREEZE, NULL, state->volumes, FALSE);

   if (op != NULL) {
      state->clientData = op->syncHandle;
   }

   return VmBackup_SetCurrentOp(state,
                                (VmBackupOp *) op,
                                VmBackupSyncDriverReadyForSnapshot,
                                __FUNCTION__);
}

#else

/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverStart --
 *
 *    Starts an asynchronous operation to enable the sync driver.
 *
 * Result
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VmBackupSyncDriverStart(ToolsAppCtx *ctx,
                        void *clientData)
{
   VmBackupDriverOp *op;
   VmBackupState *state = (VmBackupState*) clientData;

   g_debug("*** %s\n", __FUNCTION__);
   op = VmBackupNewDriverOp(state, OP_FREEZE, NULL, state->volumes, TRUE);

   if (op != NULL) {
      state->clientData = op->syncHandle;
   }

   VmBackup_SetCurrentOp(state,
                         (VmBackupOp *) op,
                         VmBackupSyncDriverReadyForSnapshot,
                         __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverOnlyStart --
 *
 *    Starts an asynchronous operation to enable the sync driver without using
 *    NullDriver fallback.
 *
 * Result
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VmBackupSyncDriverOnlyStart(ToolsAppCtx *ctx,
                            void *clientData)
{
   VmBackupDriverOp *op;
   VmBackupState *state = (VmBackupState*) clientData;

   g_debug("*** %s\n", __FUNCTION__);
   op = VmBackupNewDriverOp(state, OP_FREEZE, NULL, state->volumes, FALSE);

   if (op != NULL) {
      state->clientData = op->syncHandle;
   }

   VmBackup_SetCurrentOp(state,
                         (VmBackupOp *) op,
                         VmBackupSyncDriverReadyForSnapshot,
                         __FUNCTION__);
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverSnapshotDone --
 *
 *    Starts an asynchronous operation to disable the sync driver.
 *
 * Result
 *    TRUE, unless an error occurs.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmBackupSyncDriverSnapshotDone(VmBackupState *state,
                               void *clientData)
{
   VmBackupDriverOp *op;

   g_debug("*** %s\n", __FUNCTION__);

   op = VmBackupNewDriverOp(state, OP_THAW, state->clientData, NULL, TRUE);
   g_free(state->clientData);
   state->clientData = NULL;

   return VmBackup_SetCurrentOp(state, (VmBackupOp *) op, NULL, __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverOnlySnapshotDone --
 *
 *    Starts an asynchronous operation to disable the sync driver
 *    that does not fallback to NullDriver.
 *
 * Result
 *    TRUE, unless an error occurs.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmBackupSyncDriverOnlySnapshotDone(VmBackupState *state,
                                   void *clientData)
{
   VmBackupDriverOp *op;

   g_debug("*** %s\n", __FUNCTION__);

   op = VmBackupNewDriverOp(state, OP_THAW, state->clientData, NULL, FALSE);
   g_free(state->clientData);
   state->clientData = NULL;

   return VmBackup_SetCurrentOp(state, (VmBackupOp *) op, NULL, __FUNCTION__);
}


#if defined(__linux__)
/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverUndo --
 *
 *    Undo a completed quiescing operation.
 *
 * Result
 *    TRUE, unless an error occurs.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmBackupSyncDriverUndo(VmBackupState *state,
                       void *clientData)
{
   VmBackupDriverOp *op;

   g_debug("*** %s\n", __FUNCTION__);

   op = VmBackupNewDriverOp(state, OP_UNDO, state->clientData, NULL, TRUE);
   g_free(state->clientData);
   state->clientData = NULL;

   return VmBackup_SetCurrentOp(state, (VmBackupOp *) op, NULL, __FUNCTION__);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverOnlyUndo --
 *
 *    Undo a completed quiescing operation.
 *
 * Result
 *    TRUE, unless an error occurs.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
VmBackupSyncDriverOnlyUndo(VmBackupState *state,
                           void *clientData)
{
   VmBackupDriverOp *op;

   g_debug("*** %s\n", __FUNCTION__);

   op = VmBackupNewDriverOp(state, OP_UNDO, state->clientData, NULL, FALSE);
   g_free(state->clientData);
   state->clientData = NULL;

   return VmBackup_SetCurrentOp(state, (VmBackupOp *) op, NULL, __FUNCTION__);
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackupSyncDriverRelease --
 *
 *    Frees the given pointer.
 *
 * Result
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
VmBackupSyncDriverRelease(struct VmBackupSyncProvider *provider)
{
   free(provider);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackup_NewSyncDriverProviderInternal --
 *
 *    Returns a new VmBackupSyncProvider that will enable the sync driver
 *    as part of the "sync" operation of a backup. If useNullDriverPrefs is
 *    set to TRUE, VmBackupSyncProvider created will fallback (if required)
 *    to NullDriver based on the preferences. If useNullDriverPrefs is set
 *    to FALSE, VmBackupSyncProvider created will ignore the preferences and
 *    have its' fixed behavior, which is to not use NullDriver.
 *
 * Result
 *    NULL on error.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static VmBackupSyncProvider *
VmBackup_NewSyncDriverProviderInternal(Bool useNullDriverPrefs)
{
   VmBackupSyncProvider *provider;

   if (!SyncDriver_Init()) {
      g_debug("Error initializing the sync driver.\n");
      return NULL;
   }

   provider = Util_SafeMalloc(sizeof *provider);
   if (useNullDriverPrefs) {
      provider->start = VmBackupSyncDriverStart;
      provider->snapshotDone = VmBackupSyncDriverSnapshotDone;
#if defined(__linux__)
      provider->undo = VmBackupSyncDriverUndo;
#endif
   } else {
      provider->start = VmBackupSyncDriverOnlyStart;
      provider->snapshotDone = VmBackupSyncDriverOnlySnapshotDone;
#if defined(__linux__)
      provider->undo = VmBackupSyncDriverOnlyUndo;
#endif
   }

   provider->release = VmBackupSyncDriverRelease;
   provider->clientData = NULL;

   return provider;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackup_NewSyncDriverProvider --
 *
 *    Returns a new VmBackupSyncProvider that will enable the sync driver
 *    as part of the "sync" operation of a backup. This provider uses
 *    NullDriver fallback based on the preferences set in tools.conf.
 *
 * Result
 *    NULL on error.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

VmBackupSyncProvider *
VmBackup_NewSyncDriverProvider(void)
{
   return VmBackup_NewSyncDriverProviderInternal(TRUE);
}


#if defined(__linux__)

/*
 *-----------------------------------------------------------------------------
 *
 *  VmBackup_NewSyncDriverOnlyProvider --
 *
 *    Returns a new VmBackupSyncProvider that will enable the sync driver
 *    as part of the "sync" operation of a backup. This provider does not
 *    use NullDriver fallback (ignores the preferences set in tools.conf).
 *
 * Result
 *    NULL on error.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

VmBackupSyncProvider *
VmBackup_NewSyncDriverOnlyProvider(void)
{
   return VmBackup_NewSyncDriverProviderInternal(FALSE);
}

#endif
070701000003B2000081A40000000000000000000000016822550500001316000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/syncManifest.c   /*********************************************************
 * Copyright (C) 2017-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * syncManifest.c --
 *
 * Implements a simple xml-based backup manifest file for quiesced snapshots
 * on Linux.
 */

#include "vmBackupInt.h"
#include "syncDriver.h"
#include "syncManifest.h"
#include "vm_tools_version.h"
#include "vmware/tools/log.h"

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

/*
 * Name and format of the manifest file.
 */
static const char syncManifestName[] = "quiesce_manifest.xml";
static const char syncManifestFmt[] = {
   "<quiesceManifest>\n"
   "   <productVersion>%d</productVersion>\n"  /* version of tools */
   "   <providerName>%s</providerName>\n"      /* name of backend provider */
   "</quiesceManifest>\n"
};

/*
 * tools.conf switch to enable manifest generation
 */
static const char syncManifestSwitch[] = "enableXmlManifest";


/*
 *-----------------------------------------------------------------------------
 *
 * SyncNewManifest --
 *
 *    Create a new SyncManifest structure.
 *
 * Results:
 *    Pointer to structure on success, otherwise NULL.
 *
 *-----------------------------------------------------------------------------
 */

SyncManifest *
SyncNewManifest(VmBackupState *state,          // IN
                SyncDriverHandle handle)       // IN
{
   Bool providerQuiesces;
   const char *providerName;
   SyncManifest *manifest;

   if (!VMTools_ConfigGetBoolean(state->ctx->config, "vmbackup",
                                 syncManifestSwitch, TRUE)) {
      g_debug("No backup manifest - %s is false\n", syncManifestSwitch);
      return NULL;
   }

   if (!state->generateManifests) {
      g_debug("No backup manifest requested\n");
      return NULL;
   }

   SyncDriver_GetAttr(handle, &providerName, &providerQuiesces);
   if (!providerQuiesces) {
      g_debug("No backup manifest needed since using "
              "non-quiescing backend.\n");
      return NULL;
   }

   manifest = g_new0(SyncManifest, 1);
   manifest->path = g_strdup_printf("%s/%s", state->configDir,
                                    syncManifestName);
   manifest->providerName = g_strdup(providerName);
   return manifest;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncManifestRelease --
 *
 *    Free a manifest structure.
 *
 * Results:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

void
SyncManifestRelease(SyncManifest *manifest)    // IN
{
   if (manifest != NULL) {
      g_free(manifest->path);
      g_free(manifest->providerName);
      g_free(manifest);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * SyncManifestSend --
 *
 *    Generate manifest file and send it to vmx.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    On success, writes out the backup manifest file for a quiesced
 *    snapshot and sends the file's path to vmx.
 *
 *-----------------------------------------------------------------------------
 */

Bool
SyncManifestSend(SyncManifest *manifest)       // IN
{
   FILE *f;
   int ret;

   unlink(manifest->path);
   f = fopen(manifest->path, "w");
   if (f == NULL) {
      g_warning("Error opening backup manifest file %s\n",
                manifest->path);
      return FALSE;
   }

   ret = fprintf(f, syncManifestFmt, TOOLS_VERSION_CURRENT,
                 manifest->providerName);
   fclose(f);
   if (ret < 0) {
      g_warning("Error writing backup manifest file %s: %d %s\n",
                manifest->path, errno, strerror(errno));
      return FALSE;
   }

   if (!VmBackup_SendEventNoAbort(VMBACKUP_EVENT_GENERIC_MANIFEST,
                                  VMBACKUP_SUCCESS, manifest->path)) {
      /* VmBackup_SendEventNoAbort logs the error */
      g_info("Non-fatal error occurred while sending %s, continuing "
             "with the operation", VMBACKUP_EVENT_GENERIC_MANIFEST);
      return FALSE;
   }

   g_debug("Backup manifest was sent successfully.\n");
   return TRUE;
}
  070701000003B3000081A40000000000000000000000016822550500000737000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/syncManifest.h   /*********************************************************
 * Copyright (C) 2017-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file syncManifest.h
 *
 * Interface definitions for sync driver manifest file generation.
 * On Linux, generate an xml-format manifest file when quiescing
 * is done via the sync driver backend that uses "FIFREEZE" and
 * "FITHAW" ioctls.  On other OSes, or on Linux with other sync
 * driver backends, no manifest is generated for the sync driver.
 */

#ifndef _SYNCMANIFEST_H_
#define _SYNCMANIFEST_H_

#if defined(__linux__)

typedef struct {
   char *path;
   char *providerName;
} SyncManifest;

SyncManifest *
SyncNewManifest(VmBackupState *state, SyncDriverHandle handle);

Bool
SyncManifestSend(SyncManifest *manifest);

void
SyncManifestRelease(SyncManifest *manifest);

#else /* !defined(__linux__) */

typedef void SyncManifest;

#define SyncNewManifest(s, h)            (NULL)
#define SyncManifestSend(m)              (TRUE)
#define SyncManifestRelease(m)           ASSERT(m == NULL)

#endif /* defined(__linux__) */

#endif /* _SYNCMANIFEST_H_*/
 070701000003B4000081A400000000000000000000000168225505000022DE000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/vmBackupInt.h    /*********************************************************
 * Copyright (c) 2008-2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmBackupInt.h
 *
 * Internal definitions used by the vmbackup code.
 */

#ifndef _VMBACKUPINT_H_
#define _VMBACKUPINT_H_

#define G_LOG_DOMAIN "vmbackup"

#include <glib.h>
#include "vmware.h"
#include "vmware/guestrpc/vmbackup.h"
#include "vmware/tools/plugin.h"
#if !defined(_WIN32)
#include "vmware/tools/threadPool.h"
#endif

/*
 * The default timeout in seconds for guest OS quiescing process
 */
#define GUEST_QUIESCE_DEFAULT_TIMEOUT_IN_SEC     (15 * 60)

typedef enum {
   VMBACKUP_STATUS_PENDING,
   VMBACKUP_STATUS_FINISHED,
   VMBACKUP_STATUS_CANCELED,
   VMBACKUP_STATUS_ERROR
} VmBackupOpStatus;

typedef enum {
   VMBACKUP_FREEZE_PENDING,
   VMBACKUP_FREEZE_FINISHED,
   VMBACKUP_FREEZE_CANCELED,
   VMBACKUP_FREEZE_ERROR
} VmBackupFreezeStatus;

typedef enum {
   VMBACKUP_SCRIPT_FREEZE,
   VMBACKUP_SCRIPT_FREEZE_FAIL,
   VMBACKUP_SCRIPT_THAW
} VmBackupScriptType;

typedef enum {
   VMBACKUP_MSTATE_IDLE,
   VMBACKUP_MSTATE_SCRIPT_FREEZE,
   VMBACKUP_MSTATE_SYNC_FREEZE_WAIT,
   VMBACKUP_MSTATE_SYNC_FREEZE,
   VMBACKUP_MSTATE_SYNC_THAW,
   VMBACKUP_MSTATE_SCRIPT_THAW,
   VMBACKUP_MSTATE_COMPLETE_WAIT,
   VMBACKUP_MSTATE_SCRIPT_ERROR,
   VMBACKUP_MSTATE_SYNC_ERROR
} VmBackupMState;

typedef enum {
   VMBACKUP_RPC_STATE_NORMAL,
   VMBACKUP_RPC_STATE_ERROR,
   VMBACKUP_RPC_STATE_IGNORE
} VmBackupRpcState;

/**
 * This is a "base struct" for asynchronous operations monitored by the
 * state machine. Each implementation should provide these three functions
 * at the start of the struct so that the state machine can properly
 * interact with it.
 */

typedef struct VmBackupOp {
   VmBackupOpStatus (*queryFn)(struct VmBackupOp *);
   void (*releaseFn)(struct VmBackupOp *);
   void (*cancelFn)(struct VmBackupOp *);
} VmBackupOp;


struct VmBackupSyncProvider;
struct VmBackupSyncCompleter;

/**
 * Holds information about the current state of the backup operation.
 * Don't modify the fields directly - rather, use VmBackup_SetCurrentOp,
 * which does most of the handling needed by users of the state machine.
 *
 * NOTE 1: The thread for freeze operation modifies currentOp in BackupState
 *         which is also accessed by the AsyncCallback driving the state
 *         machine (run by main thread). Also, gcc might generate two
 *         instructions for writing a 64-bit value. Therefore, protect the
 *         access to currentOp and related fields using opLock mutex.
 *
 * NOTE 2: Only used by Linux guests, ignored on Windows guests and is
 *         initialized to "false" when the VmBackupState is initialized
 *         at the start of a backup operation.
 */

typedef struct VmBackupState {
   ToolsAppCtx   *ctx;
   VmBackupOp    *currentOp;
   const char    *currentOpName;
   GMutex         opLock;          // See note 1 above
   char          *volumes;
   char          *snapshots;
   guint          pollPeriod;
   GSource       *abortTimer;
   GSource       *timerEvent;
   GSource       *keepAlive;
   Bool (*callback)(struct VmBackupState *);
   Bool           forceRequeue;
   Bool           generateManifests;
   Bool           quiesceApps;
   Bool           quiesceFS;
   char          *excludedFileSystems;
   Bool           allowHWProvider;
   Bool           execScripts;
   Bool           enableNullDriver;
   Bool           ignoreFrozenFS;   // See note 2 above
   Bool           needsPriv;
   gchar         *scriptArg;
   guint          timeout;
   gpointer       clientData;
   void          *scripts;
   char          *configDir;
   ssize_t        currentScript;
   gchar         *errorMsg;
   VmBackupMState machineState;
   VmBackupFreezeStatus freezeStatus;
   struct VmBackupSyncProvider *provider;
   struct VmBackupSyncCompleter *completer;
   gint           vssBackupContext;
   gint           vssBackupType;
   Bool           vssBootableSystemState;
   Bool           vssPartialFileSupport;
   Bool           vssUseDefault;
   VmBackupRpcState rpcState;
} VmBackupState;

typedef Bool (*VmBackupCallback)(VmBackupState *);
typedef Bool (*VmBackupProviderCallback)(VmBackupState *, void *clientData);
typedef Bool (*VmBackupCompleterCallback)(VmBackupState *, void *clientData);


/**
 * Defines the interface between the state machine and the implementation
 * of the "sync provider": either the VSS requestor or the sync driver
 * provider, currently.
 */

typedef struct VmBackupSyncProvider {
#if defined(_WIN32)
   VmBackupProviderCallback start;
#else
   ToolsCorePoolCb start;
   VmBackupProviderCallback undo;
#endif
   VmBackupProviderCallback snapshotDone;
   void (*release)(struct VmBackupSyncProvider *);
   void *clientData;
} VmBackupSyncProvider;

/**
 * Defines the interface between the state machine and the implementation
 * of the "sync completer": either the VSS completer or the sync driver
 * completer, currently.
 */

typedef struct VmBackupSyncCompleter {
   VmBackupCompleterCallback start;
   VmBackupCompleterCallback snapshotCompleted;
   void (*release)(struct VmBackupSyncCompleter *);
   void *clientData;
} VmBackupSyncCompleter;


/**
 * Sets the current asynchronous operation being monitored, and an
 * optional callback for after it's done executing. If the operation
 * is NULL, the callback is set to execute later (currently, later = 200ms).
 *
 * @param[in]  state          The backup state.
 * @param[in]  op             The current op to set.
 * @param[in]  callback       Function to call after the operation is finished.
 * @param[in]  currentOpName  Operation name, for debugging.
 *
 * @return TRUE if @a op is not NULL.
 */

static INLINE Bool
VmBackup_SetCurrentOp(VmBackupState *state,
                      VmBackupOp *op,
                      VmBackupCallback callback,
                      const char *currentOpName)
{
   ASSERT(state != NULL);
   ASSERT(state->currentOp == NULL);
   ASSERT(currentOpName != NULL);

   g_mutex_lock(&state->opLock);

   state->currentOp = op;
   state->callback = callback;
   state->currentOpName = currentOpName;
   state->forceRequeue = (callback != NULL && op == NULL);

   g_mutex_unlock(&state->opLock);

   return (op != NULL);
}


/**
 * Convenience function to call the operation-specific query function.
 *
 * @param[in]  op    The backup op.
 *
 * @return The operation's status.
 */

static INLINE VmBackupOpStatus
VmBackup_QueryStatus(VmBackupOp *op)
{
   ASSERT(op != NULL);
   return op->queryFn(op);
}


/**
 * Convenience function to call the operation-specific cancel function.
 * Code calling this function should still call VmBackup_QueryStatus()
 * waiting for it to return a finished status (i.e., something other
 * than VMBACKUP_STATUS_PENDING).
 *
 * @param[in]  op    The backup op.
 */

static INLINE void
VmBackup_Cancel(VmBackupOp *op)
{
   ASSERT(op != NULL);
   op->cancelFn(op);
}


/**
 * Convenience function to call the operation-specific release function.
 * Releasing a state object that hasn't finished yet (i.e.,
 * VmBackup_QueryStatus returns VMBACKUP_STATUS_PENDING) can result in
 * undefined behavior.
 *
 * @param[in]  op    The backup op.
 */

static INLINE void
VmBackup_Release(VmBackupOp *op)
{
   if (op != NULL) {
      ASSERT(op->releaseFn != NULL);
      op->releaseFn(op);
   }
}


VmBackupSyncProvider *
VmBackup_NewNullProvider(void);

VmBackupSyncProvider *
VmBackup_NewSyncDriverProvider(void);

#if defined(__linux__)
VmBackupSyncProvider *
VmBackup_NewSyncDriverOnlyProvider(void);
#endif

#if defined(G_PLATFORM_WIN32)
VmBackupSyncProvider *
VmBackup_NewVssProvider(void);

VmBackupSyncCompleter *
VmBackup_NewVssCompleter(VmBackupSyncProvider *provider);

void
VmBackup_UnregisterSnapshotProvider(void);
#endif

VmBackupOp *
VmBackup_NewScriptOp(VmBackupScriptType freeze,
                     VmBackupState *state);

Bool
VmBackup_SendEvent(const char *event,
                   const uint32 code,
                   const char *desc);


Bool
VmBackup_SendEventNoAbort(const char *event,
                          const uint32 code,
                          const char *desc);

#endif /* _VMBACKUPINT_H_*/

  070701000003B5000081A4000000000000000000000001682255050000040F000000000000000000000000000000000000005000000000open-vm-tools-12.5.2/open-vm-tools/services/plugins/vmbackup/vmBackupSignals.gm   ##########################################################
# Copyright (C) 2010-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

##
# @file vmBackupSignals.gm
#
# Defines the custom GClosure marshal functions for the vmbackup signals.
#
# @see plugin.h
#

# The "io freeze" signal.
VOID:POINTER,BOOLEAN

 070701000003B6000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd  070701000003B7000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000003B8000081A40000000000000000000000016822550500000BF7000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/Makefile.am  ################################################################################
### Copyright (c) 2009-2024 Broadcom. All Rights Reserved.
### Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc.
### and/or its subsidiaries.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmtoolsd

vmtoolsd_CPPFLAGS =
vmtoolsd_CPPFLAGS += @VMTOOLS_CPPFLAGS@
vmtoolsd_CPPFLAGS += @GMODULE_CPPFLAGS@
vmtoolsd_CPPFLAGS += @GOBJECT_CPPFLAGS@
vmtoolsd_CPPFLAGS += @GTHREAD_CPPFLAGS@
vmtoolsd_CPPFLAGS += -I$(builddir)
vmtoolsd_CPPFLAGS += -DVMTOOLSD_PLUGIN_ROOT=\"$(pkglibdir)/plugins\"

vmtoolsd_LDADD =
vmtoolsd_LDADD += @VMTOOLS_LIBS@
vmtoolsd_LDADD += @GMODULE_LIBS@
vmtoolsd_LDADD += @GOBJECT_LIBS@
vmtoolsd_LDADD += @GTHREAD_LIBS@
if LINUX
vmtoolsd_LDADD += ../../lib/globalConfig/libGlobalConfig.la
endif

vmtoolsd_SOURCES =
vmtoolsd_SOURCES += cmdLine.c
vmtoolsd_SOURCES += mainLoop.c
vmtoolsd_SOURCES += mainPosix.c
vmtoolsd_SOURCES += pluginMgr.c
vmtoolsd_SOURCES += serviceObj.c
vmtoolsd_SOURCES += threadPool.c
vmtoolsd_SOURCES += toolsRpc.c
vmtoolsd_SOURCES += svcSignals.c
vmtoolsd_SOURCES += toolsHangDetector.c

BUILT_SOURCES =
BUILT_SOURCES += svcSignals.c
BUILT_SOURCES += svcSignals.h

CLEANFILES =
CLEANFILES += svcSignals.c
CLEANFILES += svcSignals.h

EXTRA_DIST =
EXTRA_DIST += svcSignals.gm

svcSignals.c: $(top_srcdir)/services/vmtoolsd/svcSignals.gm
	glib-genmarshal --body $(top_srcdir)/services/vmtoolsd/svcSignals.gm > \
	   $@ || (rm -f $@ && exit 1)

svcSignals.h: $(top_srcdir)/services/vmtoolsd/svcSignals.gm
	glib-genmarshal --header $(top_srcdir)/services/vmtoolsd/svcSignals.gm > \
	   $@ || (rm -f $@ && exit 1)

if HAVE_ICU
   vmtoolsd_LDADD += @ICU_LIBS@
   vmtoolsd_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS)     \
                      $(LIBTOOLFLAGS) --mode=link $(CXX)       \
                      $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
                      $(LDFLAGS) -o $@
else
   vmtoolsd_LINK = $(LINK)
endif

# Message catalogs.
install-data-hook:
	@INSTVMSG@ vmtoolsd $(srcdir)/l10n $(DESTDIR)$(datadir)

install-exec-hook:
	$(INSTALL) -d $(DESTDIR)/etc/vmware-tools
	$(INSTALL) -d $(DESTDIR)/$(PAM_PREFIX)/pam.d/
	$(INSTALL) $(top_srcdir)/pam/generic $(DESTDIR)/$(PAM_PREFIX)/pam.d/vmtoolsd

uninstall-hook:
	rm -f $(DESTDIR)/$(PAM_PREFIX)/pam.d/vmtoolsd
	rm -rf $(DESTDIR)/etc/vmware-tools

 070701000003B9000081A400000000000000000000000168225505000032C7000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/cmdLine.c    /*********************************************************
 * Copyright (c) 2008-2021,2023-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file cmdLine.c
 *
 *    Parses the daemon's command line arguments. Some commands may cause the
 *    process to exit.
 */

#include "toolsCoreInt.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(G_PLATFORM_WIN32)
#  include <unistd.h>
#endif
#include <glib/gi18n.h>

#include "vm_assert.h"
#include "conf.h"
#include "str.h"
#include "vmcheck.h"
#include "vmtoolsd_version.h"
#include "vmware/tools/log.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/guestrpc.h"
#include "vm_version.h"


/**
 * Runs the given Tools RPC command, printing the result to the terminal and
 * exiting the application afterwards.
 *
 * @param[in]  option      Unused.
 * @param[in]  value       RPC command.
 * @param[in]  data        Unused.
 * @param[out] error       Unused.
 *
 * @return This function doesn't return.
 */

static gboolean
ToolsCoreRunCommand(const gchar *option,
                    const gchar *value,
                    gpointer data,
                    GError **error)
{
#if defined(_WIN32)
   VMTools_AttachConsole();
#endif
   if (VmCheck_IsVirtualWorld()) {
      char *result = NULL;
      Bool status = RpcChannel_SendOne(&result, NULL, "%s", value);

      if (!status) {
         g_printerr("%s\n", result ? result : "NULL");
      } else {
         g_print("%s\n", result);
      }

      vm_free(result);
      exit(status ? 0 : 1);
   }
   g_printerr("%s\n",
              SU_(cmdline.rpcerror, "Unable to send command to VMware hypervisor."));
   exit(1);
}


/**
 * Reads the contents of a file and runs it as a Tools RPC command,
 * printing the result to the terminal and
 * exiting the application afterwards.
 *
 * @param[in]  option      Unused.
 * @param[in]  fileName    Name of the file containing an RPC command.
 * @param[in]  data        Unused.
 * @param[out] error       Unused.
 *
 * @return This function doesn't return.
 */

static gboolean
ToolsCoreRunCommandFromFile(const gchar *option,
                            const gchar *fileName,
                            gpointer data,
                            GError **error)
{
#if defined(_WIN32)
   VMTools_AttachConsole();
#endif
   if (VmCheck_IsVirtualWorld()) {
      char *result = NULL;
      Bool status;
      gchar *fileContents = NULL;

      if (!g_file_get_contents(fileName, &fileContents, NULL, NULL)) {
         g_printerr(SU_(cmdline.cmdfile.read,
                        "Unable to read command from file %s.\n"),
                        fileName);
         exit(1);
      }

      g_strchomp(fileContents);

      status = RpcChannel_SendOne(&result, NULL, "%s", fileContents);
      if (!status) {
         g_printerr("%s\n", (NULL != result) ? result : "NULL");
      } else {
         g_print("%s\n", result);
      }

      g_free(fileContents);
      vm_free(result);
      exit(status ? 0 : 1);
   }
   g_printerr("%s\n",
              SU_(cmdline.cmdfile.rpcerror,
                  "Unable to send command from file to VMware hypervisor."));
   exit(1);
}


#if defined(G_PLATFORM_WIN32)

/**
 * Function used to ignore command line arguments.
 *
 * @param[in]  option      Unused.
 * @param[in]  value       Unused.
 * @param[in]  data        Unused.
 * @param[out] error       Unused.
 *
 * @return TRUE
 */

static gboolean
ToolsCoreIgnoreArg(const gchar *option,
                   const gchar *value,
                   gpointer data,
                   GError **error)
{
   return TRUE;
}


/**
 * Signals a specific event in a running service instance. Since this function
 * doesn't know whether the service is running through the SCM, it first tries
 * to open a local event, and if that fails, tries a global event.
 *
 * @param[in]  svcname     Name of the service to be signaled.
 * @param[in]  evtFmt      Format string for the event name. It should expect
 *                         a single string parameter.
 *
 * @return Whether successfully signaled the running service.
 */

static gboolean
ToolsCoreSignalEvent(const gchar *svcname,
                     const wchar_t *evtFmt)
{
   gboolean ret = FALSE;
   gchar *msg;
   wchar_t *evt;
   HANDLE h = NULL;

   ASSERT(svcname != NULL);

   evt = Str_Aswprintf(NULL, evtFmt, L"Local", svcname);
   if (evt == NULL) {
      g_printerr("Out of memory!\n");
      goto exit;
   }

   h = OpenEvent(EVENT_MODIFY_STATE, FALSE, evt);
   if (h != NULL) {
      goto dispatch;
   }

   vm_free(evt);
   evt = Str_Aswprintf(NULL, evtFmt, L"Global", svcname);
   if (evt == NULL) {
      g_printerr("Out of memory!\n");
      goto exit;
   }

   h = OpenEvent(EVENT_MODIFY_STATE, FALSE, evt);
   if (h == NULL) {
      goto error;
   }

dispatch:
   if (!SetEvent(h)) {
      goto error;
   }

   ret = TRUE;
   goto exit;

error:
   msg = g_win32_error_message(GetLastError());
   g_printerr("Cannot open event: %s\n", msg);
   g_free(msg);

exit:
   vm_free(evt);
   CloseHandle(h);
   return ret;
}

#endif


/**
 * Error hook called when command line parsing fails. On Win32, make sure we
 * have a terminal where to show the error message.
 *
 * @param[in] context    Unused.
 * @param[in] group      Unused.
 * @param[in] data       Unused.
 * @param[in] error      Unused.
 */

static void
ToolsCoreCmdLineError(GOptionContext *context,
                      GOptionGroup *group,
                      gpointer data,
                      GError **error)
{
#if defined(_WIN32)
   VMTools_AttachConsole();
#endif
}


/**
 * Parses the command line. For a list of available options, look at the source
 * below, where the option array is declared.
 *
 * @param[out] state    Parsed options will be placed in this struct.
 * @param[in]  argc     Argument count.
 * @param[in]  argv     Argument array.
 *
 * @return TRUE on success.
 */

gboolean
ToolsCore_ParseCommandLine(ToolsServiceState *state,
                           int argc,
                           char *argv[])
{
   int i = 0;
   char *cmdStr = NULL;
   gboolean ret = FALSE;
   gboolean version = FALSE;
#if defined(G_PLATFORM_WIN32)
   gboolean dumpState = FALSE;
   gboolean forceQuit = FALSE;
#endif
   gboolean unused;
   GOptionEntry clOptions[] = {
      { "name", 'n', 0, G_OPTION_ARG_STRING, &state->name,
         SU_(cmdline.name, "Name of the service being started."),
         SU_(cmdline.name.argument, "svcname") },
      { "common-path", '\0', 0, G_OPTION_ARG_FILENAME, &state->commonPath,
         SU_(cmdline.commonpath, "Path to the common plugin directory."),
         SU_(cmdline.path, "path") },
      { "plugin-path", 'p', 0, G_OPTION_ARG_FILENAME, &state->pluginPath,
         SU_(cmdline.pluginpath, "Path to the plugin directory."),
         SU_(cmdline.path, "path") },
      { "cmd", '\0', 0, G_OPTION_ARG_CALLBACK, ToolsCoreRunCommand,
         SU_(cmdline.rpc, "Sends an RPC command to the host and exits."),
         SU_(cmdline.rpc.command, "command") },
      { "cmdfile", '\0', 0, G_OPTION_ARG_CALLBACK, ToolsCoreRunCommandFromFile,
         SU_(cmdline.cmdfile, "Sends an RPC command from a file to the host and exits."),
         SU_(cmdline.cmdfile.command, "command file") },
#if defined(G_PLATFORM_WIN32)
      { "dump-state", 's', 0, G_OPTION_ARG_NONE, &dumpState,
         SU_(cmdline.state, "Dumps the internal state of a running service instance to the logs."),
         NULL },
      { "kill", 'k', 0, G_OPTION_ARG_NONE, &forceQuit,
         SU_(cmdline.forceQuit, "Stops a running instance of a tools service."),
         NULL },
      { "install", 'i', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, ToolsCoreIgnoreArg,
         SU_(cmdline.install, "Installs the service with the Service Control Manager."),
         SU_(cmdline.install.args, "args") },
      { "uninstall", 'u', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, ToolsCoreIgnoreArg,
         SU_(cmdline.uninstall, "Uninstalls the service from the Service Control Manager."),
         NULL },
      { "displayname", 'd', 0, G_OPTION_ARG_STRING, &state->displayName,
         SU_(cmdline.displayname, "Service display name (only used with -i)."),
         SU_(cmdline.displayname.argument, "name") },
#else
      { "background", 'b', 0, G_OPTION_ARG_FILENAME, &state->pidFile,
         SU_(cmdline.background, "Runs in the background and creates a pid file."),
         SU_(cmdline.background.pidfile, "pidfile") },
      { "blockFd", '\0', 0, G_OPTION_ARG_INT, &state->ctx.blockFD,
         SU_(cmdline.blockfd, "File descriptor for the VMware blocking fs."),
         SU_(cmdline.blockfd.fd, "fd") },
      { "uinputFd", '\0', 0, G_OPTION_ARG_INT, &state->ctx.uinputFD,
         SU_(cmdline.uinputfd, "File descriptor for the uinput device."),
         SU_(cmdline.uinputfd.fd, "fd") },
#endif
      { "config", 'c', 0, G_OPTION_ARG_FILENAME, &state->configFile,
         SU_(cmdline.config, "Uses the config file at the given path."),
         SU_(cmdline.path, "path") },
      { "debug", 'g', 0, G_OPTION_ARG_FILENAME, &state->debugPlugin,
         SU_(cmdline.debug, "Runs in debug mode, using the given plugin."),
         SU_(cmdline.path, "path") },
      { "log", 'l', 0, G_OPTION_ARG_NONE, &unused,
         SU_(cmdline.log, "Ignored, kept for backwards compatibility."),
         NULL },
      { "version", 'v', 0, G_OPTION_ARG_NONE, &version,
         SU_(cmdline.version, "Prints the daemon version and exits."),
         NULL },
      { NULL }
   };
   GError *error = NULL;
   GOptionContext *context = NULL;

#if !defined(G_PLATFORM_WIN32)
   state->ctx.blockFD = -1;
   state->ctx.uinputFD = -1;
#endif

   /*
    * Form the commandline for debug log before calling
    * g_option_context_parse(), because it modifies argv.
    */
   cmdStr = Str_SafeAsprintf(NULL, "%s", argv[0]);
   for (i = 1; i < argc; i++) {
      char *prefix = cmdStr;
      cmdStr = Str_SafeAsprintf(NULL, "%s %s", prefix, argv[i]);
      free(prefix);
      /*
       * NOTE: We can't log the cmdStr here, we can
       * only log it after logging gets configured.
       * Logging it before ToolsCore_ReloadConfig call
       * will not generate proper logs.
       */
   }

   context = g_option_context_new(NULL);
   g_option_context_set_summary(context, N_("Runs the VMware Tools daemon."));
   g_option_context_add_main_entries(context, clOptions, NULL);
   g_option_group_set_error_hook(g_option_context_get_main_group(context),
                                 ToolsCoreCmdLineError);

   if (!g_option_context_parse(context, &argc, &argv, &error)) {
      g_printerr("%s: %s\n", N_("Command line parsing failed"), error->message);
      goto exit;
   }

   if (version) {
      g_print("%s %s (%s)\n", _("VMware Tools daemon, version"),
              VMTOOLSD_VERSION_STRING, BUILD_NUMBER);
      exit(0);
   }

   if (state->name == NULL) {
      state->name = VMTOOLS_GUEST_SERVICE;
      state->mainService = TRUE;
   } else {
      if (strcmp(state->name, VMTOOLS_USER_SERVICE) != 0 &&
          strcmp(state->name, VMTOOLS_GUEST_SERVICE) != 0) {
         g_printerr("%s is an invalid service name.\n", state->name);
         goto exit;
      }
      state->mainService = TOOLS_IS_MAIN_SERVICE(state);
   }

   /* Configure logging system. */
   ToolsCore_ReloadConfig(state, TRUE);

   /* Log the commandline for debugging purposes. */
   g_info("CmdLine: \"%s\"\n", cmdStr);

#if defined(G_PLATFORM_WIN32)
   if (forceQuit) {
      exit(ToolsCoreSignalEvent(state->name, QUIT_EVENT_NAME_FMT) ? 0 : 1);
   }
   if (dumpState) {
      exit(ToolsCoreSignalEvent(state->name, DUMP_STATE_EVENT_NAME_FMT) ? 0 : 1);
   }
#else
   /* If not running the "vmusr" service, ignore the blockFd and uinputFd parameter. */
   if (!TOOLS_IS_USER_SERVICE(state)) {
      if (state->ctx.blockFD >= 0) {
         close(state->ctx.blockFD);
      }
      state->ctx.blockFD = -1;

      if (state->ctx.uinputFD >= 0) {
         close(state->ctx.uinputFD);
      }
      state->ctx.uinputFD = -1;
   }
#endif

   ret = TRUE;

exit:
   free(cmdStr);
   g_clear_error(&error);
   g_option_context_free(context);
   return ret;
}

 070701000003BA000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n 070701000003BB000081A40000000000000000000000016822550500000AA7000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/de.vmsg ##########################################################
# Copyright (C) 2010, 2020-2021 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "Wird im Hintergrund ausgeführt und erstellt eine pid-Datei."

cmdline.background.pidfile = "pidfile"

cmdline.blockfd = "Dateideskriptor für das Dateisystem, das VMware blockiert."

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "Pfad zum Plug-In-Verzeichnis."

cmdline.config = "Verwendet die Konfigurationsdatei im angegebenen Pfad."

cmdline.debug = "Wird unter Verwendung des angegebenen Plug-Ins im Debug-Modus ausgeführt."

cmdline.displayname = "Anzeigename des Dienstes (wird nur mit -i verwendet)."

cmdline.displayname.argument = "Name"

cmdline.install = "Installiert den Dienst mit dem Service Control Manager (SCM)."

cmdline.install.args = "args"

cmdline.forceQuit = "Stoppt eine laufende Instanz eines Tools-Dienstes."

cmdline.log = "Ignoriert; für Abwärtskompatibilität beibehalten."

cmdline.name = "Name des Dienstes, der gestartet wird."

cmdline.name.argument = "svcname"

cmdline.path = "Pfad"

cmdline.pluginpath = "Pfad des Plug-In-Verzeichnisses."

cmdline.rpc = "Sendet einen RPC-Befehl an den Host und wird beendet."

cmdline.rpc.command = "Befehl"

cmdline.rpcerror = "Befehl konnte nicht an VMware-Hypervisor gesendet werden."

cmdline.cmdfile = "Sendet einen RPC-Befehl aus einer Datei an den Host und wird beendet."

cmdline.cmdfile.command = "Befehlsdatei"

cmdline.cmdfile.rpcerror = "Befehl konnte nicht aus Datei an VMware-Hypervisor gesendet werden."

cmdline.cmdfile.read = "Befehl kann nicht aus Datei %1$s gelesen werden.\n"


cmdline.state = "Führt einen Dump des internen Zustands der Instanz eines laufenden Dienstes in die Protokolle aus."

cmdline.uinputfd = "Dateideskriptor für das Eingabegerät"

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "Deinstalliert den Dienst vom Service Control Manager (SCM)."

cmdline.version = "Druckt die Version des Daemons und wird beendet."

 070701000003BC000081A400000000000000000000000168225505000009CE000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/en.vmsg ##########################################################
# Copyright (C) 2010, 2020-2021 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "Runs in the background and creates a pid file."

cmdline.background.pidfile = "pidfile"

cmdline.blockfd = "File descriptor for the VMware blocking fs."

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "Path to the common plugin directory."

cmdline.config = "Uses the config file at the given path."

cmdline.debug = "Runs in debug mode, using the given plugin."

cmdline.displayname = "Service display name (only used with -i)."

cmdline.displayname.argument = "name"

cmdline.install = "Installs the service with the Service Control Manager."

cmdline.install.args = "args"

cmdline.forceQuit = "Stops a running instance of a tools service."

cmdline.log = "Ignored, kept for backwards compatibility."

cmdline.name = "Name of the service being started."

cmdline.name.argument = "svcname"

cmdline.path = "path"

cmdline.pluginpath = "Path to the plugin directory."

cmdline.rpc = "Sends an RPC command to the host and exits."

cmdline.rpc.command = "command"

cmdline.rpcerror = "Unable to send command to VMware hypervisor."

cmdline.cmdfile = "Sends an RPC command from a file to the host and exits."

cmdline.cmdfile.command = "command file"

cmdline.cmdfile.rpcerror = "Unable to send command from file to VMware hypervisor."

cmdline.cmdfile.read = "Unable to read command from file %1$s.\n"


cmdline.state = "Dumps the internal state of a running service instance to the logs."

cmdline.uinputfd = "File descriptor for the uinput device."

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "Uninstalls the service from the Service Control Manager."

cmdline.version = "Prints the daemon version and exits."

  070701000003BD000081A40000000000000000000000016822550500000B0C000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/es.vmsg ##########################################################
# Copyright (c) 2010-2024 Broadcom. All Rights Reserved.
# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "Se ejecuta en segundo plano y crea un archivo pid."

cmdline.background.pidfile = "archivo pid"

cmdline.blockfd = "Descriptor del archivo para el sistema de archivos de bloqueo de VMware."

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "Ruta al directorio de complementos frecuentes."

cmdline.config = "Utiliza el archivo de configuración en la ruta dada."

cmdline.debug = "Se ejecuta en modo debug, utilizando el complemento dado."

cmdline.displayname = "Nombre para mostrar del servicio (solo se usa con -i)."

cmdline.displayname.argument = "nombre"

cmdline.install = "Instala el servicio con el Administrador de control de servicios."

cmdline.install.args = "argumentos"

cmdline.forceQuit = "Detiene una instancia en ejecución de un servicio de herramientas."

cmdline.log = "Se ignora, se conserva para compatibilidad con versiones anteriores."

cmdline.name = "Nombre del servicio que se está iniciando."

cmdline.name.argument = "nombre de servicio"

cmdline.path = "ruta"

cmdline.pluginpath = "Ruta al directorio de complementos."

cmdline.rpc = "Envía un comando RPC al host y sale."

cmdline.rpc.command = "comando"

cmdline.rpcerror = "No se puede enviar el comando al hipervisor de VMware."

cmdline.cmdfile = "Envía un comando RPC de un archivo al host y sale."

cmdline.cmdfile.command = "archivo de comandos"

cmdline.cmdfile.rpcerror = "No se puede enviar el comando de un archivo al hipervisor de VMware."

cmdline.cmdfile.read = "No se puede leer el comando del archivo %1$s.\n"


cmdline.state = "Vuelca el estado interno de una instancia de servicio en ejecución en los registros."

cmdline.uinputfd = "Descriptor del archivo para el dispositivo de entrada."

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "Desinstala el servicio desde el Administrador de control de servicios."

cmdline.version = "Imprime la versión daemon y sale."

070701000003BE000081A40000000000000000000000016822550500000B25000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/fr.vmsg ##########################################################
# Copyright (C) 2010, 2020-2021 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "S'exécute en arrière-plan et crée un fichier PID."

cmdline.background.pidfile = "pidfile"

cmdline.blockfd = "Le descripteur de fichier pour le fs bloquant VMware."

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "Chemin vers les répertoires de plug-ins communs."

cmdline.config = "Utilise le fichier de config pour le chemin donné."

cmdline.debug = "S'exécute en mode débogage à l'aide du module d'extension spécifié."

cmdline.displayname = "Nom d'affichage du service (uniquement utilisé avec -i)."

cmdline.displayname.argument = "nom"

cmdline.install = "Installe le service avec le Service Control Manager."

cmdline.install.args = "args"

cmdline.forceQuit = "Arrête une 'instance en cours d'exécution d'un service d'outils."

cmdline.log = "Ignoré, conservé pour compatibilité descendante."

cmdline.name = "Nom du service en cours de démarrage."

cmdline.name.argument = "svcname"

cmdline.path = "chemin"

cmdline.pluginpath = "Chemin vers le répertoire des plug-ins."

cmdline.rpc = "Envoie une commande d'appel de procédure à distance à l'hôte puis quitte l'application."

cmdline.rpc.command = "commande"

cmdline.rpcerror = "Impossible d'envoyer la commande à l'hyperviseur VMware."

cmdline.cmdfile = "Envoie une commande d'appel de procédure à distance à l'hôte depuis un fichier, puis quitte l'application."

cmdline.cmdfile.command = "fichier de commandes"

cmdline.cmdfile.rpcerror = "Impossible d'envoyer la commande à l'hyperviseur VMware depuis le fichier."

cmdline.cmdfile.read = "Impossible de lire la commande depuis le fichier %1$s.\n"


cmdline.state = "Transfère l'état interne d'un service en cours d'exécution dans les journaux."

cmdline.uinputfd = "Descripteur de fichier pour le périphérique uinput."

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "Désinstalle le service du Gestionnaire de contrôle du service."

cmdline.version = "Imprime la version démon et quitte l'application."

   070701000003BF000081A40000000000000000000000016822550500000AC4000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/it.vmsg ##########################################################
# Copyright (c) 2010-2024 Broadcom. All Rights Reserved.
# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "Viene eseguito in background e crea un file pid."

cmdline.background.pidfile = "file pid"

cmdline.blockfd = "Descrittore file per il file system di blocco VMware."

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "Percorso della directory plugin comune."

cmdline.config = "Utilizza il file di configurazione disponibile nel percorso specificato."

cmdline.debug = "Viene eseguito in modalità debug, utilizzando il plugin specificato."

cmdline.displayname = "Nome visualizzato del servizio (utilizzato solo con -i)."

cmdline.displayname.argument = "nome"

cmdline.install = "Installa il servizio in Gestione controllo servizi."

cmdline.install.args = "argomenti"

cmdline.forceQuit = "Interrompe un'istanza in esecuzione di un servizio di Tools."

cmdline.log = "Ignorato, mantenuto per la compatibilità con versioni precedenti."

cmdline.name = "Nome del servizio che sta per essere avviato."

cmdline.name.argument = "nome servizio"

cmdline.path = "percorso"

cmdline.pluginpath = "Percorso della directory plugin."

cmdline.rpc = "Invia all'host un comando RPC ed esce."

cmdline.rpc.command = "comando"

cmdline.rpcerror = "Impossibile inviare il comando al supervisore VMware."

cmdline.cmdfile = "Invia un comando RPC da un file all'host ed esce."

cmdline.cmdfile.command = "file di comando"

cmdline.cmdfile.rpcerror = "Impossibile inviare il comando dal file all'hypervisor VMware."

cmdline.cmdfile.read = "Impossibile leggere il comando dal file %1$s.\n"


cmdline.state = "Invia ai registri lo stato interno di un'istanza del servizio in esecuzione."

cmdline.uinputfd = "Descrittore file per il dispositivo di input."

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "Disinstalla il servizio da Gestione controllo servizi."

cmdline.version = "Stampa la versione daemon ed esce."

070701000003C0000081A40000000000000000000000016822550500000BD6000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/ja.vmsg ##########################################################
# Copyright (C) 2010, 2020-2021 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "バックグラウンドで実行し、pid ファイルを作成します。"

cmdline.background.pidfile = "pidfile"

cmdline.blockfd = "VMware ブロック fs のファイル記述子です。"

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "共通プラグイン ディレクトリのパスです。"

cmdline.config = "指定されたパスで設定ファイルを使用します。"

cmdline.debug = "指定されたプラグインを使用して、デバッグ モードで実行します。"

cmdline.displayname = "サービス表示名 (-i のみで使用)。"

cmdline.displayname.argument = "名前"

cmdline.install = "Service Control Manager を使ってサービスをインストールします。"

cmdline.install.args = "args"

cmdline.forceQuit = "Tools Service の実行中のインスタンスを停止します。"

cmdline.log = "無視されます。後方互換性のために保持されています。"

cmdline.name = "開始されているサービスの名前です。"

cmdline.name.argument = "svcname"

cmdline.path = "path"

cmdline.pluginpath = "プラグイン ディレクトリのパスです。"

cmdline.rpc = "ホストへ RPC コマンドを送信し、終了します。"

cmdline.rpc.command = "コマンド"

cmdline.rpcerror = "VMware ハイパーバイザーにコマンドを送信できません。"

cmdline.cmdfile = "ファイルからホストへ RPC コマンドを送信し、終了します。"

cmdline.cmdfile.command = "コマンド ファイル"

cmdline.cmdfile.rpcerror = "ファイルから VMware ハイパーバイザーにコマンドを送信できません。"

cmdline.cmdfile.read = "ファイル %1$s からコマンドを読み取ることができません。\n"


cmdline.state = "実行中のサービス インスタンスの内部状態をログにダンプします。"

cmdline.uinputfd = "uinput デバイスのファイル記述子。"

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "Service Control Manager からサービスをアンインストールします。"

cmdline.version = "デーモンのバージョンを出力し、終了します。"

  070701000003C1000081A40000000000000000000000016822550500000B23000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/ko.vmsg ##########################################################
# Copyright (C) 2010, 2020-2021 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "백그라운드로 실행하고 pid 파일을 생성합니다."

cmdline.background.pidfile = "pidfile"

cmdline.blockfd = "VMware 차단 fs에 대한 파일 설명자입니다."

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "일반 플러그인 디렉토리에 대한 경로입니다."

cmdline.config = "지정된 경로에 있는 구성 파일을 사용합니다."

cmdline.debug = "지정된 플러그인을 사용하여 디버그 모드를 실행합니다."

cmdline.displayname = "서비스 표시 이름입니다(-i와 함께 사용)."

cmdline.displayname.argument = "이름"

cmdline.install = "서비스 제어 관리자를 사용하여 서비스를 설치합니다."

cmdline.install.args = "args"

cmdline.forceQuit = "실행 중인 VMware Tools 서비스의 인스턴스를 중지합니다."

cmdline.log = "무시되며, 이전 버전과의 호환성을 위해 유지됩니다."

cmdline.name = "시작될 서비스의 이름입니다."

cmdline.name.argument = "svcname"

cmdline.path = "경로"

cmdline.pluginpath = "플러그인 디렉토리에 대한 경로입니다."

cmdline.rpc = "RPC 명령을 호스트에 보내고 종료합니다."

cmdline.rpc.command = "명령"

cmdline.rpcerror = "VMware 하이퍼바이저에 명령을 보낼 수 없습니다."

cmdline.cmdfile = "파일에서 RPC 명령을 호스트에 보내고 종료합니다."

cmdline.cmdfile.command = "명령 파일"

cmdline.cmdfile.rpcerror = "파일에서 VMware 하이퍼바이저에 명령을 보낼 수 없습니다."

cmdline.cmdfile.read = "%1$s 파일에서 명령을 읽을 수 없습니다.\n"


cmdline.state = "실행 중인 서비스 인스턴스의 내부 상태를 로그에 덤프합니다."

cmdline.uinputfd = "uinput 장치에 대한 파일 설명자입니다."

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "서비스 제어 관리자에서 서비스를 제거합니다."

cmdline.version = "대몬 버전을 출력하고 종료합니다."

 070701000003C2000081A400000000000000000000000168225505000009B2000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/zh_CN.vmsg  ##########################################################
# Copyright (C) 2010, 2020-2021 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "在后台运行，并创建 pid 文件。"

cmdline.background.pidfile = "pidfile"

cmdline.blockfd = "VMware blocking fs 的文件描述符。"

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "公用插件目录路径。"

cmdline.config = "使用位于给定路径的配置文件。"

cmdline.debug = "使用给定插件以调试模式运行。"

cmdline.displayname = "服务显示名称 (仅与 -i 一起使用)。"

cmdline.displayname.argument = "名称"

cmdline.install = "使用服务控制管理器安装服务。"

cmdline.install.args = "args"

cmdline.forceQuit = "停止正在运行的 Tools 服务的实例。"

cmdline.log = "已忽略，保留用于实现向后兼容性。"

cmdline.name = "正在启动的服务的名称。"

cmdline.name.argument = "svcname"

cmdline.path = "路径"

cmdline.pluginpath = "到插件目录的路径。"

cmdline.rpc = "向主机发送 RPC 命令，然后退出。"

cmdline.rpc.command = "命令"

cmdline.rpcerror = "无法向 VMware 管理程序发送命令。"

cmdline.cmdfile = "通过文件向主机发送 RPC 命令，然后退出。"

cmdline.cmdfile.command = "命令文件"

cmdline.cmdfile.rpcerror = "无法通过文件向 VMware 管理程序发送命令。"

cmdline.cmdfile.read = "无法从文件 %1$s 中读取命令。\n"


cmdline.state = "将正在运行的服务实例的内部状况转储到日志。"

cmdline.uinputfd = "uinput 设备的文件描述符。"

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "从服务控制管理器卸载服务。"

cmdline.version = "打印守护进程版本，然后退出。"

  070701000003C3000081A400000000000000000000000168225505000009A4000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/l10n/zh_TW.vmsg  ##########################################################
# Copyright (C) 2010, 2020-2021 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

cmdline.background = "在背景中執行並建立 pid 檔案。"

cmdline.background.pidfile = "pidfile"

cmdline.blockfd = "VMware 封鎖 fs 的檔案描述元。"

cmdline.blockfd.fd = "fd"

cmdline.commonpath = "一般外掛程式目錄的路徑。"

cmdline.config = "使用指定路徑下的組態檔。"

cmdline.debug = "使用指定外掛程式以偵錯模式執行。"

cmdline.displayname = "服務顯示名稱 (僅與 -i 搭配使用)。"

cmdline.displayname.argument = "名稱"

cmdline.install = "透過服務控制管理員安裝服務。"

cmdline.install.args = "args"

cmdline.forceQuit = "停止工具服務的執行中執行個體。"

cmdline.log = "已略過，保留用於向後相容性。"

cmdline.name = "啟動中服務的名稱。"

cmdline.name.argument = "svcname"

cmdline.path = "路徑"

cmdline.pluginpath = "外掛程式目錄的路徑。"

cmdline.rpc = "向主機傳送 RPC 命令，然後結束。"

cmdline.rpc.command = "命令"

cmdline.rpcerror = "無法向 VMware Hypervisor 傳送命令。"

cmdline.cmdfile = "從檔案向主機傳送 RPC 命令，然後結束。"

cmdline.cmdfile.command = "命令檔案"

cmdline.cmdfile.rpcerror = "無法從檔案向 VMware Hypervisor 傳送命令。"

cmdline.cmdfile.read = "無法從檔案 %1$s 讀取命令。\n"


cmdline.state = "將執行中服務執行個體的內部狀態傾印到記錄。"

cmdline.uinputfd = "uinput 裝置的檔案描述元。"

cmdline.uinputfd.fd = "fd"

cmdline.uninstall = "從服務控制管理員解除安裝服務。"

cmdline.version = "列印精靈版本，然後結束。"

070701000003C4000081A4000000000000000000000001682255050000925D000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/mainLoop.c   /*********************************************************
 * Copyright (c) 2008-2024 Broadcom. All Rights Reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file mainLoop.c
 *
 *    Functions for running the tools service's main loop.
 */

#if defined(_WIN32)
#  define MODULE_NAME(x)   #x "." G_MODULE_SUFFIX
#else
#  define MODULE_NAME(x)   "lib" #x "." G_MODULE_SUFFIX
#endif

#include <stdlib.h>
#include "toolsCoreInt.h"
#include "conf.h"
#include "guestApp.h"
#include "serviceObj.h"
#include "toolsHangDetector.h"
#include "str.h"
#include "system.h"
#include "util.h"
#include "vmcheck.h"
#include "vm_tools_version.h"
#include "vm_version.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/log.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/vmbackup.h"

#if (defined(_WIN32) && !defined(_ARM64_)) || \
    (defined(__linux__) && !defined(USERWORLD))
#  include "vmware/tools/guestStore.h"
#  include "globalConfig.h"

#endif

/*
 * guestStoreClient library is needed for both GuestStore-based tools upgrade
 * and also for GlobalConfig module.
 */
#if (defined(_WIN32) &&  !defined(_ARM64_)) || defined(GLOBALCONFIG_SUPPORTED)
#  include "guestStoreClient.h"
#endif

#if defined(_WIN32)
#  include "codeset.h"
#  include "toolsNotify.h"
#  include "vsockets.h"
#  include "windowsu.h"
#else
#  include "posix.h"
#endif


/*
 * Establish the default and maximum vmusr RPC channel error limits
 * that will be used to detect that the single allowed toolbox-dnd channel
 * is not available.
 */

/*
 * Lowest number of RPC channel errors to reasonably indicate that the
 * single allowed toolbox-dnd channel is currently in use by another
 * process.
 */
#define VMUSR_CHANNEL_ERR_MIN 3        /* approximately 3 secs. */

/*
 * The default number of vmusr channel errors before quitting the vmusr
 * process start-up.
 */
#define VMUSR_CHANNEL_ERR_DEFAULT 5    /* approximately 5  secs. */

/*
 * Arbitrary upper vmusr channel error count limit.
 */
#define VMUSR_CHANNEL_ERR_MAX 15       /* approximately 15 secs. */

#define CONFNAME_MAX_CHANNEL_ATTEMPTS "maxChannelAttempts"

#if defined(GLOBALCONFIG_SUPPORTED)
/*
 * The state of the global conf module.
 */
static gboolean gGlobalConfStarted = FALSE;
#endif


/*
 ******************************************************************************
 * ToolsCoreCleanup --                                                  */ /**
 *
 * Cleans up the main loop after it has executed. After this function
 * returns, the fields of the state object shouldn't be used anymore.
 *
 * @param[in]  state       Service state.
 *
 ******************************************************************************
 */

static void
ToolsCoreCleanup(ToolsServiceState *state)
{
#if (defined(_WIN32) && !defined(_ARM64_)) || \
    (defined(__linux__) && !defined(USERWORLD))
   if (state->mainService) {
      /*
       * Shut down guestStore plugin first to prevent worker threads from being
       * blocked in client lib synchronous recv() call.
       */
      ToolsPluginSvcGuestStore_Shutdown(&state->ctx);

   }
#endif

   ToolsCorePool_Shutdown(&state->ctx);
   ToolsCore_UnloadPlugins(state);
#if defined(__linux__)
   if (state->mainService) {
      ToolsCore_ReleaseVsockFamily(state);
   }
#endif

#if (defined(_WIN32) && !defined(_ARM64_)) || defined(GLOBALCONFIG_SUPPORTED)
   /*
    * guestStoreClient library is needed for both GuestStore-based tools
    * upgrade and also for GlobalConfig module.
    */
   if (state->mainService && GuestStoreClient_DeInit()) {
      g_info("%s: De-initialized GuestStore client.\n", __FUNCTION__);
   }
#endif

#if defined(_WIN32)
   if (state->mainService && ToolsNotify_End()) {
      g_info("%s: End Tools notifications.\n", __FUNCTION__);
   }
#endif

   if (state->ctx.rpc != NULL) {
      RpcChannel_Stop(state->ctx.rpc);
      RpcChannel_Destroy(state->ctx.rpc);
      state->ctx.rpc = NULL;
   }
   g_key_file_free(state->ctx.config);
   g_main_loop_unref(state->ctx.mainLoop);

#if defined(_WIN32)
   if (state->ctx.comInitialized) {
      CoUninitialize();
      state->ctx.comInitialized = FALSE;
   }
#endif

#if !defined(_WIN32)
   if (state->ctx.envp) {
      System_FreeNativeEnviron(state->ctx.envp);
      state->ctx.envp = NULL;
   }
#endif

   g_object_set(state->ctx.serviceObj, TOOLS_CORE_PROP_CTX, NULL, NULL);
   g_object_unref(state->ctx.serviceObj);
   state->ctx.serviceObj = NULL;
   state->ctx.config = NULL;
   state->ctx.mainLoop = NULL;
}


/**
 * Loads the debug library and calls its initialization function. This function
 * panics is something goes wrong.
 *
 * @param[in]  state    Service state.
 */

static void
ToolsCoreInitializeDebug(ToolsServiceState *state)
{
   RpcDebugLibData *libdata;
   RpcDebugInitializeFn initFn;

   state->debugLib = g_module_open(MODULE_NAME(vmrpcdbg), G_MODULE_BIND_LOCAL);
   if (state->debugLib == NULL) {
      g_error("Cannot load vmrpcdbg library.\n");
   }

   if (!g_module_symbol(state->debugLib,
                        "RpcDebug_Initialize",
                        (gpointer *) &initFn)) {
      g_error("Cannot find symbol: RpcDebug_Initialize\n");
   }

   libdata = initFn(&state->ctx, state->debugPlugin);
   ASSERT(libdata != NULL);
   ASSERT(libdata->debugPlugin != NULL);

   state->debugData = libdata;
#if defined(_WIN32)
   VMTools_AttachConsole();
#endif
}


/**
 * Timer callback that just calls ToolsCore_ReloadConfig().
 *
 * @param[in]  clientData  Service state.
 *
 * @return TRUE.
 */

static gboolean
ToolsCoreConfFileCb(gpointer clientData)
{
   ToolsCore_ReloadConfig(clientData, FALSE);
   return TRUE;
}


/**
 * IO freeze signal handler. Disables the conf file check task if I/O is
 * frozen, re-enable it otherwise. See bug 529653.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      Unused.
 * @param[in]  freeze   Whether I/O is being frozen.
 * @param[in]  state    Service state.
 */

static void
ToolsCoreIOFreezeCb(gpointer src,
                    ToolsAppCtx *ctx,
                    gboolean freeze,
                    ToolsServiceState *state)
{
   if (state->configCheckTask > 0 && freeze) {
      g_source_remove(state->configCheckTask);
      state->configCheckTask = 0;
      VMTools_SuspendLogIO();
   } else if (state->configCheckTask == 0 && !freeze) {
      VMTools_ResumeLogIO();
      state->configCheckTask = g_timeout_add(CONF_POLL_TIME * 1000,
                                             ToolsCoreConfFileCb,
                                             state);
   }
}


/*
 ******************************************************************************
 * ToolsCoreReportVersionData --                                         */ /**
 *
 * Report version info as guest variables.
 *
 * @param[in]  state       Service state.
 *
 ******************************************************************************
 */

static void
ToolsCoreReportVersionData(ToolsServiceState *state)
{
   char *value;
   const static char cmdPrefix[] = "info-set guestinfo.vmtools.";

   /*
    * These values are documented with specific formats.  Do not change
    * the formats, as client code can depend on them.
    */


   /*
    * Version description as a human-readable string.  This value should
    * not be parsed, so its format can be modified if necessary.
    */
   value = g_strdup_printf("%sdescription "
#ifdef OPEN_VM_TOOLS
                           "open-vm-tools %s build %s",
#else
                           "VMware Tools %s build %s",
#endif
                           cmdPrefix,
                           TOOLS_VERSION_CURRENT_STR,
                           BUILD_NUMBER_NUMERIC_STRING);
   if (!RpcChannel_Send(state->ctx.rpc, value,
                        strlen(value) + 1, NULL, NULL)) {
      g_warning("%s: failed to send description", __FUNCTION__);
   }
   g_free(value);

   /*
    * Version number as a code-readable string.  This value can
    * be parsed, so its format should not be modified.
    */
   value = g_strdup_printf("%sversionString "
                           "%s", cmdPrefix, TOOLS_VERSION_CURRENT_STR);
   if (!RpcChannel_Send(state->ctx.rpc, value,
                        strlen(value) + 1, NULL, NULL)) {
      g_warning("%s: failed to send versionString", __FUNCTION__);
   }
   g_free(value);

   /*
    * Version number as a code-readable integer.  This value can
    * be parsed, so its format should not be modified.
    */
   value = g_strdup_printf("%sversionNumber "
                           "%d", cmdPrefix, TOOLS_VERSION_CURRENT);
   if (!RpcChannel_Send(state->ctx.rpc, value,
                         strlen(value) + 1, NULL, NULL)) {
      g_warning("%s: failed to send versionNumber", __FUNCTION__);
   }
   g_free(value);

   /*
    * Build number as a code-readable integer.  This value can
    * be parsed, so its format should not be modified.
    */
   value = g_strdup_printf("%sbuildNumber "
                           "%d", cmdPrefix, BUILD_NUMBER_NUMERIC);
   if (!RpcChannel_Send(state->ctx.rpc, value,
                        strlen(value) + 1, NULL, NULL)) {
      g_warning("%s: failed to send buildNumber", __FUNCTION__);
   }
   g_free(value);
}


/*
 ******************************************************************************
 * ToolsCoreSetOptionSignalCb --
 *
 *  The SET_OPTION signal callback. The signal was triggered from
 *  ToolsCoreRpcSetOption. This function is needed to safely run code
 *  outside of the TCLO RPC handler.
 *
 *  Check for TOOLSOPTION_GUEST_LOG_LEVEL,
 *  and reinitialize the Vmx Guest Logger.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The ToolsAppCtx for passing config.
 * @param[in]  option   The option key.
 * @param[in]  value    The option value.
 * @param[in]  data     Unused.
 *
 * Result:
 *      TRUE on success.
 *
 * Side-effects:
 *      None
 *
 ******************************************************************************
 */

static gboolean
ToolsCoreSetOptionSignalCb(gpointer src,               // IN
                           ToolsAppCtx *ctx,           // IN
                           const gchar *option,        // IN
                           const gchar *value,         // IN
                           gpointer data)              // IN
{
   if (strcmp(option, TOOLSOPTION_GUEST_LOG_LEVEL) == 0) {
      ASSERT(value); /* Caller ensures */
      g_info("Received the tools set option for the guest log level '%s'.\n",
             value);

      /* Reuse the existing RPC channel */
      VMTools_SetupVmxGuestLog(FALSE, ctx->config, value);
   }

   return TRUE;
}


/*
 ******************************************************************************
 *
 * ToolsCoreResetSignalCb --
 *  The RESET signal callback. The signal was triggered from
 *  ToolsCoreCheckReset. This function is needed to safely run code
 *  outside of the RPC Channel reset code.
 *
 *  Reinitialize the Vmx Guest Logger.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The ToolsAppCtx for passing the config.
 * @param[in]  data     Unused.
 *
 ******************************************************************************
 */

static void
ToolsCoreResetSignalCb(gpointer src,          // IN
                       ToolsAppCtx *ctx,      // IN
                       gpointer data)         // IN
{
   g_info("Reinitialize the Vmx Guest Logger with a new RPC channel.\n");
   VMTools_SetupVmxGuestLog(TRUE, ctx->config, NULL); /* New RPC channel */
   g_info("Clear out the tools hang detector RPC cache state\n");
   ToolsCoreHangDetector_RpcReset();
}


/*
 ******************************************************************************
 * ToolsCoreRunLoop --                                                  */ /**
 *
 * Loads and registers all plugins, and runs the service's main loop.
 *
 * @param[in]  state       Service state.
 *
 * @return Exit code.
 *
 ******************************************************************************
 */

static int
ToolsCoreRunLoop(ToolsServiceState *state)
{
#if defined(_WIN32)
   /*
    * Verify VSockets are fully initialized before any real work.
    * For example, this can be broken by OS upgrades, see PR 2743009.
    */
   if (state->mainService) {
      VSockets_Initialized();
   }
#endif

   if (!ToolsCore_InitRpc(state)) {
      return 1;
   }

   /*
    * Start the RPC channel if it's been created. The channel may be NULL if this is
    * not running in the context of a VM.
    */
   if (state->ctx.rpc && !RpcChannel_Start(state->ctx.rpc)) {
      return 1;
   }

   /* Report version info as guest Vars */
   if (state->ctx.rpc) {
      ToolsCoreReportVersionData(state);
   }

#if defined(_WIN32)
   /*
    * Call ToolsNotify_Start() to create the global VMToolsNeedReboot event
    * before loading plugins so that plugins can open the event in their init
    * routines.
    */
   if (state->mainService && ToolsNotify_Start(&state->ctx)) {
      g_info("%s: Successfully started tools notifications.\n", __FUNCTION__);
   }
#endif

#if (defined(_WIN32) && !defined(_ARM64_)) || defined(GLOBALCONFIG_SUPPORTED)
   /*
    * guestStoreClient library is needed for both GuestStore-based tools
    * upgrade and also for GlobalConfig module.
    */
   if (state->mainService && GuestStoreClient_Init()) {
      g_info("%s: Initialized GuestStore client.\n", __FUNCTION__);
   }
#endif

   if (!ToolsCore_LoadPlugins(state)) {
      return 1;
   }

#if defined(__linux__)
   /*
    * Init a reference to vSocket family in the main service.
    */
   if (state->mainService) {
      ToolsCore_InitVsockFamily(state);
   }
#endif

   /*
    * The following criteria needs to hold for the main loop to be run:
    *
    * . no plugin has requested the service to shut down during initialization.
    * . we're either on a VMware hypervisor, or running an unknown service name.
    * . we're running in debug mode.
    *
    * In the non-VMware hypervisor case, just exit with a '0' return status (see
    * bug 297528 for why '0').
    */
   if (state->ctx.errorCode == 0 &&
       (state->ctx.isVMware ||
        ToolsCore_GetTcloName(state) == NULL ||
        state->debugPlugin != NULL)) {
      ToolsCore_RegisterPlugins(state);

      /*
       * Listen for the I/O freeze signal. We have to disable the config file
       * check when I/O is frozen or the (Win32) sync driver may cause the service
       * to hang (and make the VM unusable until it times out).
       */
      if (g_signal_lookup(TOOLS_CORE_SIG_IO_FREEZE,
                          G_OBJECT_TYPE(state->ctx.serviceObj)) != 0) {
         g_signal_connect(state->ctx.serviceObj,
                          TOOLS_CORE_SIG_IO_FREEZE,
                          G_CALLBACK(ToolsCoreIOFreezeCb),
                          state);
      }

      if (g_signal_lookup(TOOLS_CORE_SIG_SET_OPTION,
                          G_OBJECT_TYPE(state->ctx.serviceObj)) != 0) {
         g_signal_connect(state->ctx.serviceObj,
                          TOOLS_CORE_SIG_SET_OPTION,
                          G_CALLBACK(ToolsCoreSetOptionSignalCb),
                          NULL);
      }

      if (g_signal_lookup(TOOLS_CORE_SIG_RESET,
                          G_OBJECT_TYPE(state->ctx.serviceObj)) != 0) {
         g_signal_connect(state->ctx.serviceObj,
                          TOOLS_CORE_SIG_RESET,
                          G_CALLBACK(ToolsCoreResetSignalCb),
                          NULL);
      }

      state->configCheckTask = g_timeout_add(CONF_POLL_TIME * 1000,
                                             ToolsCoreConfFileCb,
                                             state);

#if defined(__APPLE__)
      ToolsCore_CFRunLoop(state);
#else
      /*
       * For now exclude the MAC due to limited testing.
       */
      if (state->mainService) {
         if (ToolsCoreHangDetector_Start(&state->ctx)) {
            g_info("%s: Successfully started tools hang detector.\n",
                   __FUNCTION__);
         }
      }

#if defined(GLOBALCONFIG_SUPPORTED)
      if (GlobalConfig_Start(&state->ctx)) {
         g_info("%s: Successfully started global config module.\n",
                  __FUNCTION__);
         gGlobalConfStarted = TRUE;
      }
#endif

      g_main_loop_run(state->ctx.mainLoop);
#endif
   }

   ToolsCoreCleanup(state);
   return state->ctx.errorCode;
}


/**
 * Logs some information about the runtime state of the service: loaded
 * plugins, registered GuestRPC callbacks, etc. Also fires a signal so
 * that plugins can log their state if they want to.
 *
 * @param[in]  state    The service state.
 */

void
ToolsCore_DumpState(ToolsServiceState *state)
{
   guint i;
   const char *providerStates[] = {
      "idle",
      "active",
      "error"
   };

   ASSERT_ON_COMPILE(ARRAYSIZE(providerStates) == TOOLS_PROVIDER_MAX);

   if (!g_main_loop_is_running(state->ctx.mainLoop)) {
      ToolsCore_LogState(TOOLS_STATE_LOG_ROOT,
                         "VM Tools Service '%s': not running.\n",
                         state->name);
      return;
   }

   ToolsCore_LogState(TOOLS_STATE_LOG_ROOT,
                      "VM Tools Service '%s':\n",
                      state->name);
   ToolsCore_LogState(TOOLS_STATE_LOG_CONTAINER,
                      "Plugin path: %s\n",
                      state->pluginPath);

   for (i = 0; i < state->providers->len; i++) {
      ToolsAppProviderReg *prov = &g_array_index(state->providers,
                                                 ToolsAppProviderReg,
                                                 i);
      ToolsCore_LogState(TOOLS_STATE_LOG_CONTAINER,
                         "App provider: %s (%s)\n",
                         prov->prov->name,
                         providerStates[prov->state]);
      if (prov->prov->dumpState != NULL) {
         prov->prov->dumpState(&state->ctx, prov->prov, NULL);
      }
   }

   ToolsCore_DumpPluginInfo(state);

   g_signal_emit_by_name(state->ctx.serviceObj,
                         TOOLS_CORE_SIG_DUMP_STATE,
                         &state->ctx);
}


/**
 * Return the RpcChannel failure threshold for the tools user service.
 *
 * @param[in]      state       The service state.
 *
 * @return  The RpcChannel failure limit for the user tools service.
 */

guint
ToolsCore_GetVmusrLimit(ToolsServiceState *state)      // IN
{
   gint errorLimit = 0;      /* Special value 0 means no error threshold. */

   if (TOOLS_IS_USER_SERVICE(state)) {
      errorLimit = VMTools_ConfigGetInteger(state->ctx.config,
                                            state->name,
                                            CONFNAME_MAX_CHANNEL_ATTEMPTS,
                                            VMUSR_CHANNEL_ERR_DEFAULT);

      /*
       * A zero value is allowed and will disable the single vmusr
       * process restriction.
       */
      if (errorLimit != 0 &&
          (errorLimit < VMUSR_CHANNEL_ERR_MIN ||
           errorLimit > VMUSR_CHANNEL_ERR_MAX)) {
         g_warning("%s: Invalid %s: %s (%d) specified in tools configuration; "
                   "using default value (%d)\n", __FUNCTION__,
                   state->name, CONFNAME_MAX_CHANNEL_ATTEMPTS,
                   errorLimit, VMUSR_CHANNEL_ERR_DEFAULT);
         errorLimit = VMUSR_CHANNEL_ERR_DEFAULT;
      }
   }

   return errorLimit;
}


/**
 * Returns the name of the TCLO app name. This will only return non-NULL
 * if the service is either the tools "guestd" or "userd" service.
 *
 * @param[in]  state    The service state.
 *
 * @return The app name, or NULL if not running a known TCLO app.
 */

const char *
ToolsCore_GetTcloName(ToolsServiceState *state)
{
   if (state->mainService) {
      return TOOLS_DAEMON_NAME;
   } else if (TOOLS_IS_USER_SERVICE(state)) {
      return TOOLS_DND_NAME;
   } else {
      return NULL;
   }
}


/**
 * Reloads the config file and re-configure the logging subsystem if the
 * log file was updated. If the config file is being loaded for the first
 * time, try to upgrade it to the new version if an old version is
 * detected.
 *
 * @param[in]  state       Service state.
 * @param[in]  reset       Whether to reset the logging subsystem.
 */

void
ToolsCore_ReloadConfig(ToolsServiceState *state,
                       gboolean reset)
{
   gboolean first = state->ctx.config == NULL;
   gboolean loaded;

#if defined(GLOBALCONFIG_SUPPORTED)
   gboolean globalConfLoaded = FALSE;

   if (gGlobalConfStarted) {
      globalConfLoaded =  GlobalConfig_LoadConfig(&state->globalConfig,
                                                  &state->globalConfigMtime);
      if (globalConfLoaded) {
         /*
         * Set the configMtime to 0 so that the config file from the file system
         * is reloaded. Else, the config is loaded only if it's been modified
         * since the last check.
         */
         g_info("%s: globalconfig reloaded.\n", __FUNCTION__);
         state->configMtime = 0;
      }
   }
#endif

   loaded = VMTools_LoadConfig(state->configFile,
                               G_KEY_FILE_NONE,
                               &state->ctx.config,
                               &state->configMtime);

#if defined(GLOBALCONFIG_SUPPORTED)
   if (loaded || globalConfLoaded) {
      gboolean configUpdated = VMTools_AddConfig(state->globalConfig,
                                                 state->ctx.config);
      loaded = loaded || configUpdated;
   }
#endif

   if (!first && loaded) {
      g_info("Config file reloaded.\n");

      /*
       * Inform plugins of config file update.
       */
      ASSERT(state->ctx.serviceObj != NULL);
      g_signal_emit_by_name(state->ctx.serviceObj,
                            TOOLS_CORE_SIG_CONF_RELOAD,
                            &state->ctx);
   }

   if (state->ctx.config == NULL) {
      /* Couldn't load the config file. Just create an empty dictionary. */
      state->ctx.config = g_key_file_new();
   }

   if (reset || loaded) {
      VMTools_ConfigLogging(state->name,
                            state->ctx.config,
                            TRUE,
                            reset);

      /*
       * Reinitialize the level setting of the VMX Guest Logger
       * since either the config is changing or we are forcing a reload
       * of the tools logging subsystem.
       * However, reuse the RPC channel since it is not affected.
       */
      VMTools_SetupVmxGuestLog(FALSE, state->ctx.config, NULL);
   }
}


#if defined(_WIN32)

/**
 * Gets error message for the last error.
 *
 * @param[in]  error    Error code to be converted to string message.
 *
 * @return The error message, or NULL in case of failure.
 */

static char *
ToolCoreGetLastErrorMsg(DWORD error)
{
   char *msg = Win32U_FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
                                     FORMAT_MESSAGE_IGNORE_INSERTS,
                                     NULL,
                                     error,
                                     0,       // Default language
                                     NULL);
   if (msg == NULL) {
      g_warning("Failed to get error message for %d, error=%d.\n",
                error, GetLastError());
      return NULL;
   }

   return msg;
}


/**
 * Check the version for a file using GetFileVersionInfo method
 *
 * @param[in]  pluginPath        plugin path name.
 * @param[in]  checkBuildNumber  inlcude check for build number.
 *
 * @return TRUE if plugin version matches the tools version,
 *         FALSE in case of a mismatch.
 */

gboolean
ToolsCore_CheckModuleVersion(const gchar *pluginPath,
                             gboolean checkBuildNumber)
{
   WCHAR *pluginPathW = NULL;
   void *buffer = NULL;
   DWORD bufferLen = 0;
   DWORD dummy = 0;
   VS_FIXEDFILEINFO *fixedFileInfo = NULL;
   UINT fixedFileInfoLen = 0;
   ToolsVersionComponents toolsVer = {0};
   uint32 pluginVersion[4] = {0};
   gboolean result = FALSE;
   static const uint32 toolsBuildNumber = PRODUCT_BUILD_NUMBER_NUMERIC;

   if (!CodeSet_Utf8ToUtf16le(pluginPath,
                              strlen(pluginPath),
                              (char **)&pluginPathW,
                              NULL)) {
      g_debug("%s: Could not convert file %s to UTF-16\n",
              __FUNCTION__, pluginPath);
      goto exit;
   }

   bufferLen = GetFileVersionInfoSizeW(pluginPathW, &dummy);
   if (bufferLen == 0) {
      g_debug("%s: Failed to get info size from %s %u",
              __FUNCTION__, pluginPath, GetLastError());
      goto exit;
   }

   buffer = g_malloc(bufferLen);
   if (!buffer) {
      g_debug("%s: malloc failed for %s", __FUNCTION__, pluginPath);
      goto exit;
   }

   if (!GetFileVersionInfoW(pluginPathW, 0, bufferLen, buffer)) {
      g_debug("%s: Failed to get info size from %s %u",
              __FUNCTION__, pluginPath, GetLastError());
      goto exit;
   }

   if (!VerQueryValueW(buffer, L"\\", (void **)&fixedFileInfo, &fixedFileInfoLen)) {
      g_debug("%s: Failed to get fixed file info from %s %u",
              __FUNCTION__, pluginPath, GetLastError());
      goto exit;
   }

   if (fixedFileInfoLen < sizeof *fixedFileInfo) {
      g_debug("%s: Fixed file info from %s is too short: %d",
                __FUNCTION__, pluginPath, fixedFileInfoLen);
      goto exit;
   }

   /* Using Product version. File version is also available. */
   pluginVersion[0] = (uint16)(fixedFileInfo->dwProductVersionMS >> 16);
   pluginVersion[1] = (uint16)(fixedFileInfo->dwProductVersionMS >>  0);
   pluginVersion[2] = (uint16)(fixedFileInfo->dwProductVersionLS >> 16);
   pluginVersion[3] = (uint16)(fixedFileInfo->dwProductVersionLS >>  0);

   TOOLS_VERSION_UINT_TO_COMPONENTS(TOOLS_VERSION_CURRENT, &toolsVer);

   result = (pluginVersion[0] == toolsVer.major &&
             pluginVersion[1] == toolsVer.minor &&
             pluginVersion[2] == toolsVer.base);

   if (result && checkBuildNumber) {
      result =  pluginVersion[3] == toolsBuildNumber;
   }

exit:
   if (!result) {
      g_warning("%s: Failed or no version check %s : %u.%u.%u.%u",
                __FUNCTION__, pluginPath, pluginVersion[0], pluginVersion[1],
                pluginVersion[2], pluginVersion[3]);
   }
   g_free(buffer);
   free(pluginPathW);
   return result;
}
#endif


/**
 * Gets an environment variable for the current process.
 *
 * @param[in]  name       Name of the env variable.
 *
 * @return The value of env variable, or NULL in case of error.
 */

static gchar *
ToolsCoreEnvGetVar(const char *name)      // IN
{
   gchar *value;

#if defined(_WIN32)
   DWORD valueSize;
   /*
    * Win32U_GetEnvironmentVariable requires buffer to be accurate size.
    * So, we need to get the value size first.
    *
    * Windows bug: GetEnvironmentVariable() does not clear stale
    * error when the return value is 0 because of env variable
    * holding empty string value (just NUL-char). So, we need to
    * clear it before we call the Win32 API.
    */
   SetLastError(ERROR_SUCCESS);
   valueSize = Win32U_GetEnvironmentVariable(name, NULL, 0);
   if (valueSize == 0) {
      goto error;
   }

   value = g_malloc(valueSize);
   SetLastError(ERROR_SUCCESS);
   if (Win32U_GetEnvironmentVariable(name, value, valueSize) == 0) {
      g_free(value);
      goto error;
   }

   return value;

error:
{
   DWORD error = GetLastError();
   if (error == ERROR_SUCCESS) {
      g_message("Env variable %s is empty.\n", name);
   } else if (error == ERROR_ENVVAR_NOT_FOUND) {
      g_message("Env variable %s not found.\n", name);
   } else {
      char *errorMsg = ToolCoreGetLastErrorMsg(error);
      if (errorMsg != NULL) {
         g_warning("Failed to get env variable size %s, error=%s.\n",
                   name, errorMsg);
         free(errorMsg);
      } else {
         g_warning("Failed to get env variable size %s, error=%d.\n",
                   name, error);
      }
   }
   return NULL;
}
#else
   value = Posix_Getenv(name);
   return value == NULL ? value : g_strdup(value);
#endif
}


/**
 * Sets an environment variable for the current process.
 *
 * @param[in]  name       Name of the env variable.
 * @param[in]  value      Value for the env variable.
 *
 * @return gboolean, TRUE on success or FALSE in case of error.
 */

static gboolean
ToolsCoreEnvSetVar(const char *name,      // IN
                   const char *value)     // IN
{
#if defined(_WIN32)
   if (!Win32U_SetEnvironmentVariable(name, value)) {
      char *errorMsg;
      DWORD error = GetLastError();

      errorMsg = ToolCoreGetLastErrorMsg(error);
      if (errorMsg != NULL) {
         g_warning("Failed to set env variable %s=%s, error=%s.\n",
                   name, value, errorMsg);
         free(errorMsg);
      } else {
         g_warning("Failed to set env variable %s=%s, error=%d.\n",
                   name, value, error);
      }
      return FALSE;
   }
#else
   if (Posix_Setenv(name, value, TRUE) != 0) {
      g_warning("Failed to set env variable %s=%s, error=%s.\n",
                name, value, strerror(errno));
      return FALSE;
   }
#endif
   return TRUE;
}


/**
 * Unsets an environment variable for the current process.
 *
 * @param[in]  name       Name of the env variable.
 *
 * @return gboolean, TRUE on success or FALSE in case of error.
 */

static gboolean
ToolsCoreEnvUnsetVar(const char *name)    // IN
{
#if defined(_WIN32)
   if (!Win32U_SetEnvironmentVariable(name, NULL)) {
      char *errorMsg;
      DWORD error = GetLastError();

      errorMsg = ToolCoreGetLastErrorMsg(error);
      if (errorMsg != NULL) {
         g_warning("Failed to unset env variable %s, error=%s.\n",
                   name, errorMsg);
         free(errorMsg);
      } else {
         g_warning("Failed to unset env variable %s, error=%d.\n",
                   name, error);
      }
      return FALSE;
   }
#else
   if (Posix_Unsetenv(name) != 0) {
      g_warning("Failed to unset env variable %s, error=%s.\n",
                name, strerror(errno));
      return FALSE;
   }
#endif
   return TRUE;
}


/**
 * Setup environment variables for the current process from
 * a given config group.
 *
 * @param[in]  ctx       Application context.
 * @param[in]  group     Configuration group to be read.
 * @param[in]  doUnset   Whether to unset the environment vars.
 */

static void
ToolsCoreInitEnvGroup(ToolsAppCtx *ctx,   // IN
                      const gchar *group, // IN
                      gboolean doUnset)   // IN
{
   gsize i;
   gsize length;
   GError *err = NULL;
   gchar **keys = g_key_file_get_keys(ctx->config, group, &length, &err);
   if (err != NULL) {
      if (err->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
         g_warning("Failed to get keys for config group %s (err=%d).\n",
                   group, err->code);
      }
      g_clear_error(&err);
      g_info("Skipping environment initialization for %s from %s config.\n",
             ctx->name, group);
      return;
   }

   g_info("Found %"FMTSZ"d environment variable(s) in %s config.\n",
          length, group);

   /*
    * Following 2 formats are supported:
    * 1. <variableName> = <value>
    * 2. <serviceName>.<variableName> = <value>
    *
    * Variables specified in format #1 are applied to all services and
    * variables specified in format #2 are applied to specified service only.
    */
   for (i = 0; i < length; i++) {
      const gchar *name = NULL;
      const gchar *key = keys[i];
      const gchar *delim;

      /*
       * Pick the keys that have service name prefix or no prefix.
       */
      delim = strchr(key, '.');
      if (delim == NULL) {
         name = key;
      } else if (strncmp(key, ctx->name, delim - key) == 0) {
         name = delim + 1;
      }

      /*
       * Ignore entries with empty env variable names.
       */
      if (name != NULL && *name != '\0') {
         gchar *oldValue = ToolsCoreEnvGetVar(name);
         if (doUnset) {
            /*
             * We can't avoid duplicate removals, but removing a non-existing
             * environment variable is a no-op anyway.
             */
            if (ToolsCoreEnvUnsetVar(name)) {
               g_message("Removed env var %s=[%s]\n",
                         name, oldValue == NULL ? "(null)" : oldValue);
            }
         } else {
            gchar *value = VMTools_ConfigGetString(ctx->config, group,
                                                   key, NULL);
            if (value != NULL) {
               /*
                * Get rid of trailing space.
                */
               g_strchomp(value);

               /*
                * Avoid updating environment var if it is already set to
                * the same value.
                *
                * Also, g_key_file_get_keys() does not filter out duplicates
                * but, VMTools_ConfigGetString returns only last entry
                * for the key. So, by comparing old value, we avoid setting
                * the environment multiple times when there are duplicates.
                *
                * NOTE: Need to use g_strcmp0 because oldValue can be NULL.
                * As value can't be NULL but oldValue can be NULL, we might
                * still do an unnecessary update in cases like setting a
                * variable to empty/no value twice. However, it does not harm
                * and is not worth avoiding it.
                */
               if (g_strcmp0(oldValue, value) == 0) {
                  g_info("Env var %s already set to [%s], skipping.\n",
                         name, oldValue);
                  g_free(oldValue);
                  g_free(value);
                  continue;
               }
               g_debug("Changing env var %s from [%s] -> [%s]\n",
                       name, oldValue == NULL ? "(null)" : oldValue, value);
               if (ToolsCoreEnvSetVar(name, value)) {
                  g_message("Updated env var %s from [%s] -> [%s]\n",
                            name, oldValue == NULL ? "(null)" : oldValue,
                            value);
               }
               g_free(value);
            }
         }
         g_free(oldValue);
      }
   }

   g_info("Initialized environment for %s from %s config.\n",
          ctx->name, group);
   g_strfreev(keys);
}


/**
 * Setup environment variables for the current process.
 *
 * @param[in]  ctx       Application context.
 */

static void
ToolsCoreInitEnv(ToolsAppCtx *ctx)
{
   /*
    * First apply unset environment configuration to start clean.
    */
   ToolsCoreInitEnvGroup(ctx, CONFGROUPNAME_UNSET_ENVIRONMENT, TRUE);
   ToolsCoreInitEnvGroup(ctx, CONFGROUPNAME_SET_ENVIRONMENT, FALSE);
}


/**
 * Performs any initial setup steps for the service's main loop.
 *
 * @param[in]  state       Service state.
 */

void
ToolsCore_Setup(ToolsServiceState *state)
{
   GMainContext *gctx;
   ToolsServiceProperty ctxProp = { TOOLS_CORE_PROP_CTX };

   /* Initializes the app context. */
   gctx = g_main_context_default();
   state->ctx.version = TOOLS_CORE_API_V1;
   state->ctx.name = state->name;
   state->ctx.errorCode = EXIT_SUCCESS;
#if defined(__APPLE__)
   /*
    * Mac OS doesn't use g_main_loop_run(), so need to create the loop as
    * "running".
    */
   state->ctx.mainLoop = g_main_loop_new(gctx, TRUE);
#else
   state->ctx.mainLoop = g_main_loop_new(gctx, FALSE);
#endif
   /*
    * TODO: Build vmcheck library
    */
   state->ctx.isVMware = VmCheck_IsVirtualWorld();
   g_main_context_unref(gctx);

   g_type_init();
   state->ctx.serviceObj = g_object_new(TOOLSCORE_TYPE_SERVICE, NULL);
   state->ctx.registerServiceProperty =
      (RegisterServiceProperty)ToolsCoreService_RegisterProperty;

   /* Register the core properties. */
   ToolsCoreService_RegisterProperty(state->ctx.serviceObj,
                                     &ctxProp);
   g_object_set(state->ctx.serviceObj, TOOLS_CORE_PROP_CTX, &state->ctx, NULL);
   /* Initialize the environment from config. */
   ToolsCoreInitEnv(&state->ctx);
   ToolsCorePool_Init(&state->ctx);

   /* Initializes the debug library if needed. */
   if (state->debugPlugin != NULL) {
      ToolsCoreInitializeDebug(state);
   }
}


/**
 * Runs the service's main loop.
 *
 * @param[in]  state       Service state.
 *
 * @return Exit code.
 */

int
ToolsCore_Run(ToolsServiceState *state)
{
   if (state->debugData != NULL) {
      int ret = state->debugData->run(&state->ctx,
                                      ToolsCoreRunLoop,
                                      state,
                                      state->debugData);
      g_module_close(state->debugLib);
      state->debugData = NULL;
      state->debugLib = NULL;
      return ret;
   }
   return ToolsCoreRunLoop(state);
}

   070701000003C5000081A40000000000000000000000016822550500002677000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/mainPosix.c  /*********************************************************
 * Copyright (c) 2008-2020,2022-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file mainPosix.c
 *
 *    Service entry point for the POSIX version of the tools daemon.
 */


#include "toolsCoreInt.h"
#include <locale.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <glib/gstdio.h>
#include "file.h"
#include "guestApp.h"
#include "hostinfo.h"
#include "su.h"
#include "system.h"
#include "unicode.h"
#include "util.h"
#include "vmcheck.h"
#include "vmware/tools/log.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/utils.h"

#if !defined(__APPLE__)
#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);
#endif

static ToolsServiceState gState = { NULL, };


/**
 * Reloads the service configuration - including forcing rotation of log
 * files by reinitializing the logging subsystem.
 *
 * @param[in]  info     Unused.
 * @param[in]  data     Service state.
 *
 * @return TRUE
 */

static gboolean
ToolsCoreSigHUPCb(const siginfo_t *info,
                  gpointer data)
{
   ToolsCore_ReloadConfig(data, TRUE);
   return TRUE;
}


/**
 * Handles a signal that would terminate the process. Asks the main loop
 * to exit nicely.
 *
 * @param[in]  info     Unused.
 * @param[in]  data     Pointer to the main loop to be stopped.
 *
 * @return FALSE
 */

static gboolean
ToolsCoreSigHandler(const siginfo_t *info,
                    gpointer data)
{
   g_main_loop_quit((GMainLoop *)data);
   return FALSE;
}


/**
 * Handles a USR1 signal; logs the current service state.
 * Also shutdown rpc connection so we can do tools upgrade.
 *
 * @param[in]  info     Unused.
 * @param[in]  data     Unused.
 *
 * @return TRUE
 */

static gboolean
ToolsCoreSigUsrHandler(const siginfo_t *info,
                       gpointer data)
{
   ToolsCore_DumpState(&gState);

   if (TOOLS_IS_USER_SERVICE(&gState.ctx)) {
      g_info("Shutting down guestrpc on signal USR1 ...\n");
      g_signal_emit_by_name(gState.ctx.serviceObj,
                            TOOLS_CORE_SIG_NO_RPC,
                            &gState.ctx);
      RpcChannel_Destroy(gState.ctx.rpc);
      gState.ctx.rpc = NULL;
   }

   return TRUE;
}


/**
 * Perform (optional) work before or after running the main loop.
 *
 * @param[in]  state    Service state.
 * @param[in]  before   TRUE if before running the main loop, FALSE if after.
 */

static void
ToolsCoreWorkAroundLoop(ToolsServiceState *state,
                        gboolean before)
{
#ifdef __APPLE__
   if (state->mainService) {
      char *libDir = GuestApp_GetInstallPath();
      char *argv[] = {
         NULL,
         before ? "--startInternal" : "--stopInternal",
         NULL,
      };

      if (!libDir) {
         g_error("Failed to retrieve libDir.\n");
      }

      argv[0] = g_strdup_printf("%s/services.sh", libDir);
      free(libDir);
      if (!argv[0]) {
         g_error("Failed to construct argv[0].\n");
      }

      g_spawn_sync(NULL, argv, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
      free(argv[0]);
   }
#endif
}


/**
 * Tools function to set close-on-exec flg for the fd.
 *
 * @param[in] fd   open file descriptor.
 *
 * @return TRUE on success, FALSE otherwise.
 */

static gboolean
ToolsSetCloexecFlag(int fd)
{
   int flags;

   if (fd == -1) {
      /* fd is not present, no need to manipulate */
      return TRUE;
   }

   flags = fcntl(fd, F_GETFD, 0);
   if (flags < 0) {
      g_printerr("Couldn't get the flags set for fd %d, error %u.", fd, errno);
      return FALSE;
   }
   flags |= FD_CLOEXEC;
   if (fcntl(fd, F_SETFD, flags) < 0) {
      g_printerr("Couldn't set close-on-exec for fd %d, error %u.", fd, errno);
      return FALSE;
   }

   return TRUE;
}


/**
 * Tools function to close the fds.
 */

static void
ToolsCloseFds(void)
{
   if (gState.ctx.blockFD != -1) {
      close(gState.ctx.blockFD);
   }

   /*
    * uinputFD will be available only for wayland.
    */
   if (gState.ctx.uinputFD != -1) {
      close(gState.ctx.uinputFD);
   }
}


/**
 * Tools daemon entry function.
 *
 * @param[in] argc   Argument count.
 * @param[in] argv   Argument array.
 * @param[in] envp   User environment.
 *
 * @return 0 on successful execution, error code otherwise.
 */

int
main(int argc,
     char *argv[],
     const char *envp[])
{
   int i;
   int ret = EXIT_FAILURE;
   char **argvCopy;
   GSource *src;

   /*
    * Check that environment is a VM
    */
   if (!VmCheck_IsVirtualWorld()) {
      goto exit;
   }

   Unicode_Init(argc, &argv, NULL);

   /*
    * ToolsCore_ParseCommandLine() uses g_option_context_parse(), which modifies
    * argv. We don't want that to happen, so we make a copy of the array and
    * use that as the argument instead.
    */
   argvCopy = g_malloc(argc * sizeof *argvCopy);
   for (i = 0; i < argc; i++) {
      argvCopy[i] = argv[i];
   }

   setlocale(LC_ALL, "");

   i = atexit(VMTools_TeardownVmxGuestLog);
   ASSERT(i == 0);
   VMTools_UseVmxGuestLog(VMTOOLS_APP_NAME);
   VMTools_ConfigLogging(G_LOG_DOMAIN, NULL, TRUE, FALSE);
   VMTools_SetupVmxGuestLog(FALSE, NULL, NULL);

   VMTools_BindTextDomain(VMW_TEXT_DOMAIN, NULL, NULL);

   if (!ToolsCore_ParseCommandLine(&gState, argc, argvCopy)) {
      g_free(argvCopy);
      goto exit;
   }
   g_free(argvCopy);
   argvCopy = NULL;

   /*
    * Drops privilege to the real uid and gid of the process
    * for the "vmusr" service.
    */
   if (TOOLS_IS_USER_SERVICE(&gState)) {
      uid_t uid = getuid();
      gid_t gid = getgid();

      if ((Id_SetREGid(gid, gid) != 0) ||
          (Id_SetREUid(uid, uid) != 0)) {
         g_printerr("could not drop privileges: %s", strerror(errno));
         ToolsCloseFds();
         goto exit;
      }
      if (!ToolsSetCloexecFlag(gState.ctx.blockFD) ||
          !ToolsSetCloexecFlag(gState.ctx.uinputFD)) {
         ToolsCloseFds();
         goto exit;
      }
   }

   if (gState.pidFile != NULL) {
      /*
       * If argv[0] is not an absolute path, make it so; all other path
       * arguments should have been given as absolute paths if '--background'
       * was used, or things may not work as expected.
       */
      if (!g_path_is_absolute(argv[0])) {
         gchar *abs = g_find_program_in_path(argv[0]);
         if (abs == NULL || strcmp(abs, argv[0]) == 0) {
            char *cwd = File_Cwd(NULL);
            g_free(abs);
            abs = g_strdup_printf("%s%c%s", cwd, DIRSEPC, argv[0]);
            vm_free(cwd);
         }
         argv[0] = abs;
      }

      /*
       * Need to remove --background from the command line or we'll get
       * into an infinite loop. ToolsCore_ParseCommandLine() already
       * validated that "-b" has an argument, so it's safe to assume the
       * data is there.
       */
      for (i = 1; i < argc; i++) {
         size_t count = 0;
         if (strcmp(argv[i], "--background") == 0 ||
             strcmp(argv[i], "-b") == 0) {
            count = 2;
         } else if (g_str_has_prefix(argv[i], "--background=")) {
            count = 1;
         }
         if (count) {
            memmove(argv + i, argv + i + count, (argc - i - count) * sizeof *argv);
            argv[argc - count] = NULL;
            break;
         }
      }

      if (!Hostinfo_Daemonize(argv[0],
                              argv,
                              HOSTINFO_DAEMONIZE_LOCKPID,
                              gState.pidFile, NULL, 0)) {
         goto exit;
      }
      return 0;
   }

   ToolsCore_Setup(&gState);

   src = VMTools_NewSignalSource(SIGHUP);
   VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src,
                            ToolsCoreSigHUPCb, &gState, NULL);
   g_source_unref(src);

   src = VMTools_NewSignalSource(SIGINT);
   VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src,
                            ToolsCoreSigHandler, gState.ctx.mainLoop, NULL);
   g_source_unref(src);

   src = VMTools_NewSignalSource(SIGQUIT);
   VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src,
                            ToolsCoreSigHandler, gState.ctx.mainLoop, NULL);
   g_source_unref(src);

   /* On Mac OS, launchd uses SIGTERM. */
   src = VMTools_NewSignalSource(SIGTERM);
   VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src,
                            ToolsCoreSigHandler, gState.ctx.mainLoop, NULL);
   g_source_unref(src);

   src = VMTools_NewSignalSource(SIGUSR1);
   VMTOOLSAPP_ATTACH_SOURCE(&gState.ctx, src, ToolsCoreSigUsrHandler, NULL, NULL);
   g_source_unref(src);

   /* Ignore SIGUSR2 by default. */
   signal(SIGUSR2, SIG_IGN);

   signal(SIGPIPE, SIG_IGN);

   /*
    * Save the original environment so that we can safely spawn other
    * applications (since we may have to modify the original environment
    * to launch vmtoolsd successfully).
    */
   gState.ctx.envp = System_GetNativeEnviron(envp);

   ToolsCoreWorkAroundLoop(&gState, TRUE);
   ret = ToolsCore_Run(&gState);
   ToolsCoreWorkAroundLoop(&gState, FALSE);

   if (gState.pidFile != NULL) {
      g_unlink(gState.pidFile);
   }
exit:
   return ret;
}
 070701000003C6000081A40000000000000000000000016822550500006EB8000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/pluginMgr.c  /*********************************************************
 * Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file pluginMgr.c
 *
 *    Provides functions for loading and manipulating Tools plugins.
 */

#include <string.h>
#include "toolsCoreInt.h"

#include "vm_assert.h"
#include "guestApp.h"
#include "serviceObj.h"
#include "util.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/log.h"
#include "vmware/tools/utils.h"


/** Defines the internal data about a plugin. */
typedef struct ToolsPlugin {
   gchar               *fileName;
   GModule             *module;
   ToolsPluginOnLoad    onload;
   ToolsPluginData     *data;
} ToolsPlugin;


#ifdef USE_APPLOADER
static Bool (*LoadDependencies)(char *libName, Bool useShipped);
#endif

typedef void (*PluginDataCallback)(ToolsServiceState *state,
                                   ToolsPluginData *plugin);

typedef gboolean (*PluginAppRegCallback)(ToolsServiceState *state,
                                         ToolsPluginData *plugin,
                                         ToolsAppType type,
                                         ToolsAppProviderReg *preg,
                                         gpointer reg);


/**
 * State dump callback for application registration information.
 *
 * @param[in]  state The service state.
 * @param[in]  plugin   The plugin information.
 * @param[in]  type     Application type.
 * @param[in]  preg     Provider information.
 * @param[in]  reg      Application registration.
 *
 * @return TRUE
 */

static gboolean
ToolsCoreDumpAppInfo(ToolsServiceState *state,
                     ToolsPluginData *plugin,
                     ToolsAppType type,
                     ToolsAppProviderReg *preg,
                     gpointer reg)
{
   if (preg != NULL) {
      if (preg->prov->dumpState != NULL) {
         preg->prov->dumpState(&state->ctx, preg->prov, reg);
      } else {
         ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN,
                            "App type %u (no provider info).\n",
                            type);
      }
    } else {
      ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN,
                         "App type %u (no provider).\n",
                         type);
   }
   return TRUE;
}


/**
 * State dump callback for generic plugin information.
 *
 * @param[in]  state    The service state.
 * @param[in]  plugin   The plugin information.
 */

static void
ToolsCoreDumpPluginInfo(ToolsServiceState *state,
                        ToolsPluginData *plugin)
{
   ToolsCore_LogState(TOOLS_STATE_LOG_CONTAINER, "Plugin: %s\n", plugin->name);

   if (plugin->regs == NULL) {
      ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "No registrations.\n");
   }
}


/**
 * State dump callback for service properties.
 *
 * @param[in]  ctx   The application context.
 * @param[in]  prov  Unused.
 * @param[in]  reg   The application registration data.
 */

static void
ToolsCoreDumpProperty(ToolsAppCtx *ctx,
                      ToolsAppProvider *prov,
                      gpointer reg)
{
   if (reg != NULL) {
      ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "Service property: %s.\n",
                         ((ToolsServiceProperty *)reg)->name);
   }
}


/**
 * State dump callback for GuestRPC applications.
 *
 * @param[in]  ctx   The application context.
 * @param[in]  prov  Unused.
 * @param[in]  reg   The application registration data.
 */

static void
ToolsCoreDumpRPC(ToolsAppCtx *ctx,
                 ToolsAppProvider *prov,
                 gpointer reg)
{
   if (reg != NULL) {
      RpcChannelCallback *cb = reg;
      ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "RPC callback: %s\n", cb->name);
   }
}


/**
 * State dump callback for signal connections.
 *
 * @param[in]  ctx   The application context.
 * @param[in]  prov  Unused.
 * @param[in]  reg   The application registration data.
 */

static void
ToolsCoreDumpSignal(ToolsAppCtx *ctx,
                    ToolsAppProvider *prov,
                    gpointer reg)
{
   if (reg != NULL) {
      ToolsPluginSignalCb *sig = reg;
      ToolsCore_LogState(TOOLS_STATE_LOG_PLUGIN, "Signal callback: %s\n", sig->signame);
   }
}


/**
 * Frees memory associated with a ToolsPlugin instance. If the plugin hasn't
 * been initialized yet, this will unload the shared object.
 *
 * @param[in]  plugin   ToolsPlugin instance.
 */

static void
ToolsCoreFreePlugin(ToolsPlugin *plugin)
{
   if (plugin->module != NULL && !g_module_close(plugin->module)) {
      g_warning("Error unloading plugin '%s': %s\n",
                plugin->fileName,
                g_module_error());
   }
   g_free(plugin->fileName);
   g_free(plugin);
}


/**
 * Callback to register applications with the given provider.
 *
 * @param[in]  state    The service state.
 * @param[in]  plugin   The plugin information.
 * @param[in]  type     Application type.
 * @param[in]  preg     Provider information.
 * @param[in]  reg      Application registration.
 *
 * @return Whether to continue registering other apps.
 */

static gboolean
ToolsCoreRegisterApp(ToolsServiceState *state,
                     ToolsPluginData *plugin,
                     ToolsAppType type,
                     ToolsAppProviderReg *preg,
                     gpointer reg)
{
   gboolean error = TRUE;

   if (type == TOOLS_APP_PROVIDER) {
      /* We should already have registered all providers. */
      return TRUE;
   }

   ASSERT(preg != NULL);

   if (preg->state == TOOLS_PROVIDER_ERROR) {
      g_warning("Plugin %s wants to register app of type %d but the "
                "provider failed to activate.\n", plugin->name, type);
      goto exit;
   }

   /*
    * Register the app with the provider, activating it if necessary. If
    * it fails to activate, tag it so we don't try again.
    */
   if (preg->state == TOOLS_PROVIDER_IDLE) {
      if (preg->prov->activate != NULL) {
         GError *err = NULL;
         preg->prov->activate(&state->ctx, preg->prov, &err);
         if (err != NULL) {
            g_warning("Error activating provider %s: %s.\n",
                      preg->prov->name, err->message);
            preg->state = TOOLS_PROVIDER_ERROR;
            g_clear_error(&err);
            goto exit;
         }
      }
      preg->state = TOOLS_PROVIDER_ACTIVE;
   }

   if (!preg->prov->registerApp(&state->ctx, preg->prov, plugin, reg)) {
      g_warning("Failed registration of app type %d (%s) from plugin %s.",
                type, preg->prov->name, plugin->name);
      goto exit;
   }
   error = FALSE;

exit:
   if (error && plugin->errorCb != NULL) {
      return plugin->errorCb(&state->ctx, type, reg, plugin);
   }
   return TRUE;
}


/**
 * Callback to register application providers.
 *
 * @param[in]  state    The service state.
 * @param[in]  plugin   The plugin information.
 * @param[in]  type     Application type.
 * @param[in]  preg     Provider information.
 * @param[in]  reg      Application registration.
 *
 * @return TRUE
 */

static gboolean
ToolsCoreRegisterProvider(ToolsServiceState *state,
                          ToolsPluginData *plugin,
                          ToolsAppType type,
                          ToolsAppProviderReg *preg,
                          gpointer reg)
{
   if (type == TOOLS_APP_PROVIDER) {
      guint k;
      ToolsAppProvider *prov = reg;
      ToolsAppProviderReg newreg = { prov, TOOLS_PROVIDER_IDLE };

      ASSERT(prov->name != NULL);
      ASSERT(prov->registerApp != NULL);

      /* Assert that no two providers choose the same app type. */
      for (k = 0; k < state->providers->len; k++) {
         ToolsAppProviderReg *existing = &g_array_index(state->providers,
                                                        ToolsAppProviderReg,
                                                        k);
         ASSERT(prov->regType != existing->prov->regType);
         g_return_val_if_fail(prov->regType != existing->prov->regType, TRUE);
      }

      g_array_append_val(state->providers, newreg);
   }

   return TRUE;
}


/**
 * Iterates through the list of plugins, and through each plugin's app
 * registration data, calling the appropriate callback for each piece
 * of data.
 *
 * One of the two callback arguments must be provided.
 *
 * @param[in]  state       Service state.
 * @param[in]  pluginCb    Callback called for each plugin data instance.
 * @param[in]  appRegCb    Callback called for each application registration.
 */

static void
ToolsCoreForEachPlugin(ToolsServiceState *state,
                       PluginDataCallback pluginCb,
                       PluginAppRegCallback appRegCb)
{
   guint i;

   ASSERT(pluginCb != NULL || appRegCb != NULL);

   for (i = 0; i < state->plugins->len; i++) {
      ToolsPlugin *plugin = g_ptr_array_index(state->plugins, i);
      GArray *regs = (plugin->data != NULL) ? plugin->data->regs : NULL;
      guint j;

      if (pluginCb != NULL) {
         pluginCb(state, plugin->data);
      }

      if (regs == NULL || appRegCb == NULL) {
         continue;
      }

      for (j = 0; j < regs->len; j++) {
         guint k;
         guint pregIdx;
         ToolsAppReg *reg = &g_array_index(regs, ToolsAppReg, j);
         ToolsAppProviderReg *preg = NULL;

         /* Find the provider for the desired reg type. */
         for (k = 0; k < state->providers->len; k++) {
            ToolsAppProviderReg *tmp = &g_array_index(state->providers,
                                                      ToolsAppProviderReg,
                                                      k);
            if (tmp->prov->regType == reg->type) {
               preg = tmp;
               pregIdx = k;
               break;
            }
         }

         if (preg == NULL) {
            g_message("Cannot find provider for app type %d, plugin %s may not work.\n",
                      reg->type, plugin->data->name);
            if (plugin->data->errorCb != NULL &&
                !plugin->data->errorCb(&state->ctx, reg->type, NULL, plugin->data)) {
               break;
            }
            continue;
         }

         for (k = 0; k < reg->data->len; k++) {
            gpointer appdata = &reg->data->data[preg->prov->regSize * k];
            if (!appRegCb(state, plugin->data, reg->type, preg, appdata)) {
               /* Break out of the outer loop. */
               j = regs->len;
               break;
            }

            /*
             * The registration callback may have modified the provider array,
             * so we need to re-read the provider pointer.
             */
            preg = &g_array_index(state->providers, ToolsAppProviderReg, pregIdx);
         }
      }
   }
}


/**
 * Callback to register service properties.
 *
 * @param[in]  ctx      The application context.
 * @param[in]  prov     Unused.
 * @param[in]  plugin   Unused.
 * @param[in]  reg      The property registration data.
 *
 * @return TRUE
 */

static gboolean
ToolsCoreRegisterProperty(ToolsAppCtx *ctx,
                          ToolsAppProvider *prov,
                          ToolsPluginData *plugin,
                          gpointer reg)
{
   ToolsCoreService_RegisterProperty(ctx->serviceObj, reg);
   return TRUE;
}


/**
 * Registration callback for GuestRPC applications.
 *
 * @param[in]  ctx      The application context.
 * @param[in]  prov     Unused.
 * @param[in]  plugin   Unused.
 * @param[in]  reg      The application registration data.
 *
 * @return TRUE.
 */

static gboolean
ToolsCoreRegisterRPC(ToolsAppCtx *ctx,
                     ToolsAppProvider *prov,
                     ToolsPluginData *plugin,
                     gpointer reg)
{
   RpcChannel_RegisterCallback(ctx->rpc, reg);
   return TRUE;
}


/**
 * Registration callback for signal connections.
 *
 * @param[in]  ctx      The application context.
 * @param[in]  prov     Unused.
 * @param[in]  plugin   Unused.
 * @param[in]  reg      The application registration data.
 *
 * @return TRUE if the signal exists.
 */

static gboolean
ToolsCoreRegisterSignal(ToolsAppCtx *ctx,
                        ToolsAppProvider *prov,
                        ToolsPluginData *plugin,
                        gpointer reg)
{
   gboolean valid;
   guint sigId;
   GQuark sigDetail;
   ToolsPluginSignalCb *sig = reg;

   valid = g_signal_parse_name(sig->signame,
                               G_OBJECT_TYPE(ctx->serviceObj),
                               &sigId,
                               &sigDetail,
                               FALSE);
   if (valid) {
      g_signal_connect(ctx->serviceObj,
                       sig->signame,
                       sig->callback,
                       sig->clientData);
      return TRUE;
   }

   g_debug("Plugin '%s' unable to connect to signal '%s'.\n", plugin->name,
           sig->signame);
   return FALSE;
}


/**
 * Compares two strings. To be used with g_ptr_array_sort.
 *
 * @param[in]  _str1    Pointer to string for comparison.
 * @param[in]  _str2    Pointer to string for comparison.
 *
 * @return Result of strcmp.
 */

static gint
ToolsCoreStrPtrCompare(gconstpointer _str1,
                       gconstpointer _str2)
{
   const gchar *str1 = *((const gchar **) _str1);
   const gchar *str2 = *((const gchar **) _str2);
   return strcmp(str1, str2);
}


/**
 * Loads all the plugins found in the given directory, adding the registration
 * data to the given array.
 *
 * @param[in]  ctx         Application context.
 * @param[in]  pluginPath  Path where to look for plugins.
 * @param[out] regs        Array where to store plugin registration info.
 */

static gboolean
ToolsCoreLoadDirectory(ToolsAppCtx *ctx,
                       const gchar *pluginPath,
                       GPtrArray *regs)
{
   gboolean ret = FALSE;
   const gchar *staticEntry;
   guint i;
   GDir *dir = NULL;
   GError *err = NULL;
   GPtrArray *plugins;

   dir = g_dir_open(pluginPath, 0, &err);
   if (dir == NULL) {
      g_warning("Error opening dir: %s\n", err->message);
      g_clear_error(&err);
      goto exit;
   }

   plugins = g_ptr_array_new();

   /*
    * Load plugins in alphabetical order, so the load order is the same
    * regardless of how the filesystem returns entries.
    */
   while ((staticEntry = g_dir_read_name(dir)) != NULL) {
      if (g_str_has_suffix(staticEntry, "." G_MODULE_SUFFIX)) {
         g_ptr_array_add(plugins, g_strdup(staticEntry));
      }
   }

   g_dir_close(dir);

   g_ptr_array_sort(plugins, ToolsCoreStrPtrCompare);

   for (i = 0; i < plugins->len; i++) {
      gchar *entry;
      gchar *path;
      GModule *module = NULL;
      ToolsPlugin *plugin = NULL;
      ToolsPluginOnLoad onload;

      entry = g_ptr_array_index(plugins, i);
      path = g_strdup_printf("%s%c%s", pluginPath, DIRSEPC, entry);

      if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
         g_warning("File '%s' is not a regular file, skipping.\n", entry);
         goto next;
      }

#ifdef USE_APPLOADER
      /* Trying loading the plugins with system libraries */
      if (!LoadDependencies(path, FALSE)) {
         g_warning("Loading of library dependencies for %s failed.\n", entry);
         goto next;
      }
#endif

#ifdef _WIN32
      /*
       * Only load compatible versions of a plugin which requires that a plugin
       * and tools product versions match.
       * Using FALSE compares the major.minor.base components of the version.
       * Version format is: "major.minor.base.buildnumber" e.g. "11.2.0.19761"
       * Use TRUE for a more strict check to verify all four version components.
       */
      if (!ToolsCore_CheckModuleVersion(path, FALSE)) {
         g_warning("%s: Version check of plugin '%s' failed: not loaded.\n",
                    __FUNCTION__, path);
         goto next;
      }
#endif

      module = g_module_open(path, G_MODULE_BIND_LOCAL);
#ifdef USE_APPLOADER
      if (module == NULL) {
         g_info("Opening plugin '%s' with system libraries failed: %s\n",
                   entry, g_module_error());
         /* Falling back to the shipped libraries */
         if (!LoadDependencies(path, TRUE)) {
            g_warning("Loading of shipped library dependencies for %s failed.\n",
                     entry);
            goto next;
         }
         module = g_module_open(path, G_MODULE_BIND_LOCAL);
      }
#endif
      if (module == NULL) {
         g_warning("Opening plugin '%s' failed: %s.\n", entry, g_module_error());
         goto next;
      }

      if (!g_module_symbol(module, "ToolsOnLoad", (gpointer *) &onload)) {
         g_warning("Lookup of plugin entry point for '%s' failed.\n", entry);
         goto next;
      }

      plugin = g_malloc(sizeof *plugin);
      plugin->fileName = entry;
      plugin->data = NULL;
      plugin->module = module;
      plugin->onload = onload;
      g_ptr_array_add(regs, plugin);

   next:
      g_free(path);
      if (plugin == NULL && module != NULL) {
         if (!g_module_close(module)) {
            g_warning("Error unloading plugin '%s': %s\n", entry, g_module_error());
         }
      }
   }

   g_ptr_array_free(plugins, TRUE);
   ret = TRUE;

exit:
   return ret;
}


/**
 * State dump callback for logging information about loaded plugins.
 *
 * @param[in]  state    The service state.
 */

void
ToolsCore_DumpPluginInfo(ToolsServiceState *state)
{
   if (state->plugins == NULL) {
      g_message("   No plugins loaded.");
   } else {
      ToolsCoreForEachPlugin(state, ToolsCoreDumpPluginInfo, ToolsCoreDumpAppInfo);
   }
}


/**
 * Loads all plugins present in the plugin directory. If the plugin path
 * is NULL, then default directories are used in case the service is either
 * the main tools service of the user daemon, otherwise failure is returned.
 *
 * @param[in]  state    The service state.
 *
 * @return Whether loading the plugins was successful.
 */

gboolean
ToolsCore_LoadPlugins(ToolsServiceState *state)
{
   gboolean pluginDirExists;
   gboolean ret = FALSE;
   gchar *pluginRoot;
   guint i;
   GPtrArray *plugins = NULL;

#if defined(sun) && defined(__x86_64__)
   const char *subdir = "/amd64";
#else
   const char *subdir = "";
#endif

#if defined(OPEN_VM_TOOLS)
   pluginRoot = g_strdup(VMTOOLSD_PLUGIN_ROOT);
#else
   char *instPath = GuestApp_GetInstallPath();
   pluginRoot = g_strdup_printf("%s%cplugins", instPath, DIRSEPC);
   vm_free(instPath);
#endif

   ASSERT(g_module_supported());

#ifdef USE_APPLOADER
   {
      Bool ret;
      GModule *mainModule = g_module_open(NULL, G_MODULE_BIND_LAZY);
      ASSERT(mainModule);

      ret = g_module_symbol(mainModule, "AppLoader_LoadLibraryDependencies",
                            (gpointer *)&LoadDependencies);
      g_module_close(mainModule);

      if (!ret) {
         g_critical("Unable to locate library dependency loading function.\n");
         goto exit;
      }
   }
#endif

   plugins = g_ptr_array_new();

   /*
    * First, load plugins from the common directory. The common directory
    * is not required to exist unless provided on the command line.
    */
   if (state->commonPath == NULL) {
      state->commonPath = g_strdup_printf("%s%s%c%s",
                                          pluginRoot,
                                          subdir,
                                          DIRSEPC,
                                          TOOLSCORE_COMMON);
   } else if (!g_file_test(state->commonPath, G_FILE_TEST_IS_DIR)) {
      g_warning("Common plugin path is not a directory: %s\n", state->commonPath);
      goto exit;
   }

   if (g_file_test(state->commonPath, G_FILE_TEST_IS_DIR) &&
       !ToolsCoreLoadDirectory(&state->ctx, state->commonPath, plugins)) {
      goto exit;
   }

   /*
    * Load the container-specific plugins. Ignore if the plugin directory
    * doesn't exist when running in debug mode.
    */

   if (state->pluginPath == NULL) {
      state->pluginPath = g_strdup_printf("%s%s%c%s",
                                          pluginRoot,
                                          subdir,
                                          DIRSEPC,
                                          state->name);
   }

   pluginDirExists = g_file_test(state->pluginPath, G_FILE_TEST_IS_DIR);
   if (state->debugPlugin == NULL && !pluginDirExists) {
      g_warning("Plugin path is not a directory: %s\n", state->pluginPath);
      goto exit;
   }

   if (pluginDirExists &&
       !ToolsCoreLoadDirectory(&state->ctx, state->pluginPath, plugins)) {
      goto exit;
   }


   /*
    * All plugins are loaded, now initialize them.
    */

   state->plugins = g_ptr_array_new();

   for (i = 0; i < plugins->len; i++) {
      ToolsPlugin *plugin = g_ptr_array_index(plugins, i);

      plugin->data = plugin->onload(&state->ctx);

      if (plugin->data == NULL) {
         g_info("Plugin '%s' didn't provide deployment data, unloading.\n",
                plugin->fileName);
         ToolsCoreFreePlugin(plugin);
      } else if (state->ctx.errorCode != 0) {
         /* Break early if a plugin has requested the container to quit. */
         ToolsCoreFreePlugin(plugin);
         break;
      } else {
         ASSERT(plugin->data->name != NULL);
         g_module_make_resident(plugin->module);
         g_ptr_array_add(state->plugins, plugin);
         VMTools_BindTextDomain(plugin->data->name, NULL, NULL);
         g_message("Plugin '%s' initialized.\n", plugin->data->name);
      }
   }


   /*
    * If there is a debug plugin, see if it exports standard plugin registration
    * data too.
    */
   if (state->debugData != NULL && state->debugData->debugPlugin->plugin != NULL) {
      ToolsPluginData *data = state->debugData->debugPlugin->plugin;
      ToolsPlugin *plugin = g_malloc(sizeof *plugin);
      plugin->fileName = NULL;
      plugin->module = NULL;
      plugin->data = data;
      VMTools_BindTextDomain(data->name, NULL, NULL);
      g_ptr_array_add(state->plugins, plugin);
   }

   ret = TRUE;

exit:
   if (plugins != NULL) {
      g_ptr_array_free(plugins, TRUE);
   }
   g_free(pluginRoot);
   return ret;
}


/**
 * Registers all RPC handlers provided by the loaded and enabled plugins.
 *
 * @param[in]  state    The service state.
 */

void
ToolsCore_RegisterPlugins(ToolsServiceState *state)
{
   ToolsAppProvider *fakeProv;
   ToolsAppProviderReg fakeReg;

   if (state->plugins == NULL) {
      return;
   }

   /*
    * Create "fake" app providers for the functionality provided by
    * vmtoolsd (GuestRPC channel, glib signals, custom app providers).
    */
   state->providers = g_array_new(FALSE, TRUE, sizeof (ToolsAppProviderReg));

   if (state->ctx.rpc != NULL) {
      fakeProv = g_malloc0(sizeof *fakeProv);
      fakeProv->regType = TOOLS_APP_GUESTRPC;
      fakeProv->regSize = sizeof (RpcChannelCallback);
      fakeProv->name = "GuestRPC";
      fakeProv->registerApp = ToolsCoreRegisterRPC;
      fakeProv->dumpState = ToolsCoreDumpRPC;

      fakeReg.prov = fakeProv;
      fakeReg.state = TOOLS_PROVIDER_ACTIVE;
      g_array_append_val(state->providers, fakeReg);
   }

   fakeProv = g_malloc0(sizeof *fakeProv);
   fakeProv->regType = TOOLS_APP_SIGNALS;
   fakeProv->regSize = sizeof (ToolsPluginSignalCb);
   fakeProv->name = "Signals";
   fakeProv->registerApp = ToolsCoreRegisterSignal;
   fakeProv->dumpState = ToolsCoreDumpSignal;

   fakeReg.prov = fakeProv;
   fakeReg.state = TOOLS_PROVIDER_ACTIVE;
   g_array_append_val(state->providers, fakeReg);

   fakeProv = g_malloc0(sizeof *fakeProv);
   fakeProv->regType = TOOLS_APP_PROVIDER;
   fakeProv->regSize = sizeof (ToolsAppProvider);
   fakeProv->name = "App Provider";
   fakeProv->registerApp = NULL;
   fakeProv->dumpState = NULL;

   fakeReg.prov = fakeProv;
   fakeReg.state = TOOLS_PROVIDER_ACTIVE;
   g_array_append_val(state->providers, fakeReg);

   fakeProv = g_malloc0(sizeof *fakeProv);
   fakeProv->regType = TOOLS_SVC_PROPERTY;
   fakeProv->regSize = sizeof (ToolsServiceProperty);
   fakeProv->name = "Service Properties";
   fakeProv->registerApp = ToolsCoreRegisterProperty;
   fakeProv->dumpState = ToolsCoreDumpProperty;

   fakeReg.prov = fakeProv;
   fakeReg.state = TOOLS_PROVIDER_ACTIVE;
   g_array_append_val(state->providers, fakeReg);


   /*
    * First app providers need to be identified, so that we know that they're
    * available for use by plugins who need them.
    */
   ToolsCoreForEachPlugin(state, NULL, ToolsCoreRegisterProvider);

   /*
    * Now that we know all app providers, register all the apps, activating
    * individual app providers as necessary.
    */
   ToolsCoreForEachPlugin(state, NULL, ToolsCoreRegisterApp);
}


/**
 * Calls the shutdown callback for all loaded plugins, and cleans up the list
 * of loaded plugins. Plugins are unloaded in the opposite order they were
 * loaded.
 *
 * Note that if a plugin does not provide a shutdown callback, it may leak
 * data that may have been dynamically allocated in the plugin registration
 * info. Since this function is intended to be called once during service
 * shutdown, this it not that big of a deal.
 *
 * @param[in]  state    The service state.
 */

void
ToolsCore_UnloadPlugins(ToolsServiceState *state)
{
   guint i;

   if (state->plugins == NULL) {
      return;
   }

   /* 
    * Signal handlers in some plugins may require RPC Channel. Therefore, we don't
    * emit the signal if RPC channel is not available. See PR 1798412 for details.
    */
   if (state->capsRegistered && state->ctx.rpc) {
      GArray *pcaps = NULL;
      g_signal_emit_by_name(state->ctx.serviceObj,
                            TOOLS_CORE_SIG_CAPABILITIES,
                            &state->ctx,
                            FALSE,
                            &pcaps);

      if (pcaps != NULL) {
         ToolsCore_SetCapabilities(state->ctx.rpc, pcaps, FALSE);
         g_array_free(pcaps, TRUE);
      }
   }

   /*
    * Stop all app providers, and free the memory we allocated for the
    * internal app providers.
    */
   for (i = 0; state->providers != NULL && i < state->providers->len; i++) {
       ToolsAppProviderReg *preg = &g_array_index(state->providers,
                                                  ToolsAppProviderReg,
                                                  i);

      if (preg->prov->shutdown != NULL && preg->state == TOOLS_PROVIDER_ACTIVE) {
         preg->prov->shutdown(&state->ctx, preg->prov);
      }

      if (preg->prov->regType == TOOLS_APP_GUESTRPC ||
          preg->prov->regType == TOOLS_APP_SIGNALS ||
          preg->prov->regType == TOOLS_APP_PROVIDER ||
          preg->prov->regType == TOOLS_SVC_PROPERTY) {
         g_free(preg->prov);
      }
   }

   g_signal_emit_by_name(state->ctx.serviceObj, TOOLS_CORE_SIG_SHUTDOWN, &state->ctx);

   while (state->plugins->len > 0) {
      ToolsPlugin *plugin = g_ptr_array_index(state->plugins, state->plugins->len - 1);
      GArray *regs = (plugin->data != NULL) ? plugin->data->regs : NULL;

      g_message("Unloading plugin '%s'.\n",
                plugin->data != NULL ? plugin->data->name : "unknown");

      if (regs != NULL) {
         guint i;
         for (i = 0; i < regs->len; i++) {
            ToolsAppReg *reg = &g_array_index(regs, ToolsAppReg, i);
            if (reg->data != NULL) {
               g_array_free(reg->data, TRUE);
            }
         }
         g_array_free(regs, TRUE);
      }

      g_ptr_array_remove_index(state->plugins, state->plugins->len - 1);
      ToolsCoreFreePlugin(plugin);
  }

   if (state->providers != NULL) {
      g_array_free(state->providers, TRUE);
      state->providers = NULL;
   }

   g_ptr_array_free(state->plugins, TRUE);
   state->plugins = NULL;
}
070701000003C7000081A40000000000000000000000016822550500003882000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/serviceObj.c /*********************************************************
 * Copyright (C) 2008-2019, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file serviceObj.c
 *
 * Implementation of the "ToolsCore_Service" gobject.
 */

#include "toolsCoreInt.h"
#include "serviceObj.h"
#include "svcSignals.h"


typedef struct ServiceProperty {
   guint       id;
   gchar      *name;
   gpointer    value;
} ServiceProperty;

static gpointer gToolsCoreServiceParentClass;

/**
 * Accumulator function for the "set option" signal. If a handler returns
 * TRUE, sets the result of the signal propagation to TRUE.
 *
 * @param[in]  ihint       Unused.
 * @param[out] retval      Return value of the signal.
 * @param[in]  handlerRet  Return value from the current handler.
 * @param[in]  data        Unused.
 *
 * @return TRUE
 */

static gboolean
ToolsCoreSetOptionAccumulator(GSignalInvocationHint *ihint,
                              GValue *retval,
                              const GValue *handlerRet,
                              gpointer data)
{
   if (!g_value_get_boolean(retval)) {
      g_value_set_boolean(retval, g_value_get_boolean(handlerRet));
   }
   return TRUE;
}


/**
 * Accumulator function for the "capabilities" signal. Just aggregates the
 * contents of all returned GArray instances into a "higher level" instance.
 *
 * @param[in]  ihint       Unused.
 * @param[out] retval      Return value of the signal.
 * @param[in]  handlerRet  Return value from the current handler.
 * @param[in]  data        Unused.
 *
 * @return TRUE.
 */

static gboolean
ToolsCoreCapabilitiesAccumulator(GSignalInvocationHint *ihint,
                                 GValue *retval,
                                 const GValue *handlerRet,
                                 gpointer data)
{
   GArray *caps = g_value_get_pointer(handlerRet);

   if (caps != NULL) {
      guint i;
      GArray *acc = g_value_get_pointer(retval);

      if (acc == NULL) {
         acc = g_array_new(FALSE, TRUE, sizeof (ToolsAppCapability));
         g_value_set_pointer(retval, acc);
      }

      for (i = 0; i < caps->len; i++) {
         g_array_append_val(acc, g_array_index(caps, ToolsAppCapability, i));
      }

      g_array_free(caps, TRUE);
   }

   return TRUE;
}


#if defined(_WIN32)
/**
 * Accumulator function for the "service control" signal. Updates the return
 * value according to the signal's documentation.
 *
 * The gobject library initializes the return value to "0" regardless of
 * what the signal emitter sets it to. So the accumulator does two things
 * to have a non-zero default return value:
 *
 *    - if the current return value is zero, it's set to the default return
 *      value (ERROR_CALL_NOT_IMPLEMENTED).
 *    - the return value is always offset by one; so the signal emitter
 *      should decrement the return value when looking at it.
 *
 * @param[in]     ihint       Unused.
 * @param[in,out] retval      Return value of the signal (offset by 1).
 * @param[in]     handlerRet  Return value from the current handler.
 * @param[in]     data        Unused.
 *
 * @return TRUE.
 */

static gboolean
ToolsCoreServiceControlAccumulator(GSignalInvocationHint *ihint,
                                   GValue *retval,
                                   const GValue *handlerRet,
                                   gpointer data)
{
   guint ret = g_value_get_uint(retval);
   guint handlerVal = g_value_get_uint(handlerRet);

   if (ret == 0) {
      ret = ERROR_CALL_NOT_IMPLEMENTED + 1;
   }

   switch (ret) {
   case ERROR_CALL_NOT_IMPLEMENTED + 1:
      ret = handlerVal + 1;
      break;

   case NO_ERROR + 1:
      if (handlerVal != ERROR_CALL_NOT_IMPLEMENTED) {
         ret = handlerVal + 1;
      }
      break;

   default:
      break;
   }

   g_value_set_uint(retval, ret);
   return TRUE;
}
#endif


/*
 *******************************************************************************
 * ToolsCoreServiceGetProperty --                                         */ /**
 *
 * Gets the value of a property in the object.
 *
 * @param[in]  object   The instance.
 * @param[in]  id       Property ID.
 * @param[out] value    Where to set the value.
 * @param[in]  pspec    Unused.
 *
 *******************************************************************************
 */

static void
ToolsCoreServiceGetProperty(GObject *object,
                            guint id,
                            GValue *value,
                            GParamSpec *pspec)
{
   ToolsCoreService *self = (ToolsCoreService *) object;

   id -= 1;

   g_mutex_lock(&self->lock);

   if (id < self->props->len) {
      ServiceProperty *p = &g_array_index(self->props, ServiceProperty, id);
      g_value_set_pointer(value, p->value);
   }

   g_mutex_unlock(&self->lock);
}


/*
 *******************************************************************************
 * ToolsCoreServiceSetProperty --                                         */ /**
 *
 * Sets a property in the given object. If the property is found, a "notify"
 * signal is sent so that interested listeners can act on the change.
 *
 * @param[in] object The instance.
 * @param[in] id     Property ID.
 * @param[in] value  Value to set.
 * @param[in] pspec  Unused.
 *
 *******************************************************************************
 */

static void
ToolsCoreServiceSetProperty(GObject *object,
                            guint id,
                            const GValue *value,
                            GParamSpec *pspec)
{
   ServiceProperty *p = NULL;
   ToolsCoreService *self = (ToolsCoreService *) object;

   id -= 1;

   g_mutex_lock(&self->lock);

   if (id < self->props->len) {
      p = &g_array_index(self->props, ServiceProperty, id);
      p->value = g_value_get_pointer(value);
   }

   g_mutex_unlock(&self->lock);

   if (p != NULL) {
      g_object_notify(object, p->name);
   }
}


/*
 *******************************************************************************
 * ToolsCoreServiceCtor --                                                */ /**
 *
 * Object constructor. Initialize internal state.
 *
 * @param[in] type      Object type.
 * @param[in] nparams   Param count.
 * @param[in] params    Construction parameters.
 *
 * @return A new instance.
 *
 *******************************************************************************
 */

static GObject *
ToolsCoreServiceCtor(GType type,
                     guint nparams,
                     GObjectConstructParam *params)
{
   GObject *object;
   ToolsCoreService *self;

   object = G_OBJECT_CLASS(gToolsCoreServiceParentClass)->constructor(type,
                                                                      nparams,
                                                                      params);

   self = TOOLSCORE_SERVICE(object);
   g_mutex_init(&self->lock);
   self->props = g_array_new(FALSE, FALSE, sizeof (ServiceProperty));

   return object;
}


/*
 *******************************************************************************
 * ToolsCoreServiceDtor --                                                */ /**
 *
 * Object destructor. Frees memory associated with the object. Goes through the
 * list of properties to make sure all of them have been cleaned up before the
 * service exits, printing a warning otherwise.
 *
 * @param[in]  object   The object being destructed.
 *
 *******************************************************************************
 */

static void
ToolsCoreServiceDtor(GObject *object)
{
   ToolsCoreService *self = (ToolsCoreService *) object;
   guint i;

   for (i = 0; i < self->props->len; i++) {
      ServiceProperty *p = &g_array_index(self->props, ServiceProperty, i);
      if (p->value != NULL) {
         g_warning("Property '%s' was not cleaned up before shut down.",
                   p->name);
      }
      g_free(p->name);
   }

   g_array_free(self->props, TRUE);
   g_mutex_clear(&self->lock);
}


/**
 * Initializes the ToolsCoreService class. Sets up the signals that are sent
 * by the vmtoolsd service.
 *
 * @param[in]  klass    The class instance to initialize.
 */

static void
ToolsCore_Service_class_init(gpointer _klass,
                             gpointer klassData)
{
   ToolsCoreServiceClass *klass = _klass;

   gToolsCoreServiceParentClass = g_type_class_peek_parent(_klass);

   g_signal_new(TOOLS_CORE_SIG_CAPABILITIES,
                G_OBJECT_CLASS_TYPE(klass),
                G_SIGNAL_RUN_LAST,
                0,
                ToolsCoreCapabilitiesAccumulator,
                NULL,
                g_cclosure_user_marshal_POINTER__POINTER_BOOLEAN,
                G_TYPE_POINTER,
                2,
                G_TYPE_POINTER,
                G_TYPE_BOOLEAN);
   g_signal_new(TOOLS_CORE_SIG_CONF_RELOAD,
                G_OBJECT_CLASS_TYPE(klass),
                G_SIGNAL_RUN_LAST,
                0,
                NULL,
                NULL,
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);
   g_signal_new(TOOLS_CORE_SIG_DUMP_STATE,
                G_OBJECT_CLASS_TYPE(klass),
                G_SIGNAL_RUN_LAST,
                0,
                NULL,
                NULL,
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);
   g_signal_new(TOOLS_CORE_SIG_RESET,
                G_OBJECT_CLASS_TYPE(klass),
                G_SIGNAL_RUN_LAST,
                0,
                NULL,
                NULL,
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);
   g_signal_new(TOOLS_CORE_SIG_NO_RPC,
                G_OBJECT_CLASS_TYPE(klass),
                G_SIGNAL_RUN_LAST,
                0,
                NULL,
                NULL,
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);
   g_signal_new(TOOLS_CORE_SIG_SET_OPTION,
                G_OBJECT_CLASS_TYPE(klass),
                G_SIGNAL_RUN_LAST,
                0,
                ToolsCoreSetOptionAccumulator,
                NULL,
                g_cclosure_user_marshal_BOOLEAN__POINTER_STRING_STRING,
                G_TYPE_BOOLEAN,
                3,
                G_TYPE_POINTER,
                G_TYPE_STRING,
                G_TYPE_STRING);
   g_signal_new(TOOLS_CORE_SIG_SHUTDOWN,
                G_OBJECT_CLASS_TYPE(klass),
                G_SIGNAL_RUN_LAST,
                0,
                NULL,
                NULL,
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE,
                1,
                G_TYPE_POINTER);
#if defined(G_PLATFORM_WIN32)
   g_signal_new(TOOLS_CORE_SIG_SERVICE_CONTROL,
                G_OBJECT_CLASS_TYPE(klass),
                G_SIGNAL_RUN_LAST,
                0,
                ToolsCoreServiceControlAccumulator,
                NULL,
                g_cclosure_user_marshal_UINT__POINTER_POINTER_UINT_UINT_POINTER,
                G_TYPE_UINT,
                5,
                G_TYPE_POINTER,
                G_TYPE_POINTER,
                G_TYPE_UINT,
                G_TYPE_UINT,
                G_TYPE_POINTER);
#endif

   G_OBJECT_CLASS(klass)->constructor = ToolsCoreServiceCtor;
   G_OBJECT_CLASS(klass)->finalize = ToolsCoreServiceDtor;
   G_OBJECT_CLASS(klass)->set_property = ToolsCoreServiceSetProperty;
   G_OBJECT_CLASS(klass)->get_property = ToolsCoreServiceGetProperty;
}


/**
 * Initializes the ToolsCoreService type if it hasn't been done yet, and
 * return the type instance. This method is not thread safe.
 *
 * @return The ToolsCoreService type.
 */

GType
ToolsCore_Service_get_type(void)
{
   static GType type = 0;
   if (type == 0) {
      static const GTypeInfo info = {
         sizeof (ToolsCoreServiceClass),
         NULL,                               /* base_init */
         NULL,                               /* base_finalize */
         ToolsCore_Service_class_init,
         NULL,                               /* class_finalize */
         NULL,                               /* class_data */
         sizeof (ToolsCoreService),
         0,                                  /* n_preallocs */
         NULL,                               /* instance_init */
      };
      type = g_type_register_static(G_TYPE_OBJECT,
                                    "ToolsCoreService",
                                    &info,
                                    0);
   }
   return type;
}


/*
 *******************************************************************************
 * ToolsCoreService_RegisterProperty --                                   */ /**
 *
 * Installs a new property in the service object.
 *
 * @param[in] obj    Service object.
 * @param[in] prop   Property to install.
 *
 *******************************************************************************
 */

void
ToolsCoreService_RegisterProperty(ToolsCoreService *obj,
                                  ToolsServiceProperty *prop)
{
   static guint PROP_ID_SEQ = 0;

   ServiceProperty sprop;
   ToolsCoreServiceClass *klass = TOOLSCORESERVICE_GET_CLASS(obj);
   GParamSpec *pspec = g_param_spec_pointer(prop->name,
                                            prop->name,
                                            prop->name,
                                            G_PARAM_READWRITE);

   g_mutex_lock(&obj->lock);

   sprop.id = ++PROP_ID_SEQ;
   sprop.name = g_strdup(prop->name);
   sprop.value = NULL;
   g_array_append_val(obj->props, sprop);
   g_object_class_install_property(G_OBJECT_CLASS(klass), sprop.id, pspec);

   g_mutex_unlock(&obj->lock);
}

  070701000003C8000081A4000000000000000000000001682255050000085F000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/serviceObj.h /*********************************************************
 * Copyright (C) 2009-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _SERVICEOBJ_H_
#define _SERVICEOBJ_H_

/**
 * @file serviceObj.h
 *
 * Interface of the "core service" object. This interface is not really
 * public, just the type itself, so that plugins can provide their own
 * signals for communicating with other plugins in the same process. For
 * this reason, it doesn't provide all the GObject boilerplate macros.
 */

#include <glib-object.h>

#define TOOLSCORE_TYPE_SERVICE   ToolsCore_Service_get_type()
#define TOOLSCORESERVICE_GET_CLASS(object) \
   (G_TYPE_INSTANCE_GET_CLASS((object), TOOLSCORE_TYPE_SERVICE, ToolsCoreServiceClass))
#define TOOLSCORE_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),                   \
                                                           TOOLSCORE_TYPE_SERVICE,  \
                                                           ToolsCoreService))

typedef struct ToolsCoreService {
   GObject        parent;
   GMutex         lock;
   GArray        *props;
} ToolsCoreService;

typedef struct ToolsCoreServiceClass {
   GObjectClass   parentClass;
} ToolsCoreServiceClass;

GType
ToolsCore_Service_get_type(void);

void
ToolsCoreService_RegisterProperty(ToolsCoreService *obj,
                                  ToolsServiceProperty *prop);


#endif /* _SERVICEOBJ_H_ */

 070701000003C9000081A400000000000000000000000168225505000004AD000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/svcSignals.gm    ##########################################################
# Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

##
# @file svcSignals.gm
#
# Defines the custom GClosure marshal functions for the core services signals.
#
# @see plugin.h
#

# The "capabilities" signal.
POINTER:POINTER,BOOLEAN

# The "set option" signal.
BOOLEAN:POINTER,STRING,STRING

# The "service control" signal.
# Used on Win32 only.
UINT:POINTER,POINTER,UINT,UINT,POINTER

   070701000003CA000081A40000000000000000000000016822550500003FDB000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/threadPool.c /*********************************************************
 * Copyright (C) 2010-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file threadPool.c
 *
 * Implementation of the shared thread pool defined in threadPool.h.
 */

#include <limits.h>
#include <string.h>
#include "vmware.h"
#include "toolsCoreInt.h"
#include "serviceObj.h"
#include "vmware/tools/threadPool.h"

#define DEFAULT_MAX_IDLE_TIME       5000
#define DEFAULT_MAX_THREADS         5
#define DEFAULT_MAX_UNUSED_THREADS  0

typedef struct ThreadPoolState {
   ToolsCorePool  funcs;
   gboolean       active;
   ToolsAppCtx   *ctx;
   GThreadPool   *pool;
   GQueue        *workQueue;
   GPtrArray     *threads;
   GMutex         lock;
   guint          nextWorkId;
} ThreadPoolState;


typedef struct WorkerTask {
   guint             id;
   guint             srcId;
   ToolsCorePoolCb   cb;
   gpointer          data;
   GDestroyNotify    dtor;
} WorkerTask;


typedef struct StandaloneTask {
   gboolean          active;
   ToolsCorePoolCb   cb;
   ToolsCorePoolCb   interrupt;
   gpointer          data;
   GThread          *thread;
   GDestroyNotify    dtor;
} StandaloneTask;


static ThreadPoolState gState;


/*
 *******************************************************************************
 * ToolsCorePoolCompareTask --                                            */ /**
 *
 * Compares two WorkerTask instances.
 *
 * @param[in] p1  Pointer to WorkerTask.
 * @param[in] p2  Pointer to WorkerTask.
 *
 * @return > 0, 0, < 0  if p1's ID is less than, equal, or greater than p2's.
 *
 *******************************************************************************
 */

static gint
ToolsCorePoolCompareTask(gconstpointer p1,
                         gconstpointer p2)
{
   const WorkerTask *t1 = p1;
   const WorkerTask *t2 = p2;

   if (t1 != NULL && t2 != NULL) {
      return (t2->id - t1->id);
   }

   if (t1 == NULL && t2 == NULL) {
      return 0;
   }

   return (t1 != NULL) ? -1 : 1;
}


/*
 *******************************************************************************
 * ToolsCorePoolDestroyThread --                                          */ /**
 *
 * Releases resources associated with a StandaloneTask, joining the thread
 * that's executing it.
 *
 * @param[in] data   A StandaloneTask.
 *
 *******************************************************************************
 */

static void
ToolsCorePoolDestroyThread(gpointer data)
{
   StandaloneTask *task = data;
   g_thread_join(task->thread);
   if (task->dtor != NULL) {
      task->dtor(task->data);
   }
   g_free(task);
}


/*
 *******************************************************************************
 * ToolsCorePoolDestroyTask --                                            */ /**
 *
 * Frees memory associated with a WorkerTask, calling its destructor if one is
 * registered.
 *
 * @param[in] data   A WorkerTask.
 *
 *******************************************************************************
 */

static void
ToolsCorePoolDestroyTask(gpointer data)
{
   WorkerTask *work = data;
   if (work->dtor != NULL) {
      work->dtor(work->data);
   }
   g_free(work);
}


/*
 *******************************************************************************
 * ToolsCorePoolDoWork --                                                 */ /**
 *
 * Execute a work item.
 *
 * @param[in] data   A WorkerTask.
 *
 * @return FALSE
 *
 *******************************************************************************
 */

static gboolean
ToolsCorePoolDoWork(gpointer data)
{
   WorkerTask *work = data;

   /*
    * In single threaded mode, remove the task being executed from the queue.
    * In multi-threaded mode, the thread pool callback already did this.
    */
   if (gState.pool == NULL) {
      g_mutex_lock(&gState.lock);
      g_queue_remove(gState.workQueue, work);
      g_mutex_unlock(&gState.lock);
   }

   work->cb(gState.ctx, work->data);
   return FALSE;
}


/*
 *******************************************************************************
 * ToolsCorePoolNoOp --                                                   */ /**
 *
 * Idle callback for destroying a standalone thread. Does nothing, since the
 * actual destruction is done by ToolsCorePoolDestroyThread.
 *
 * @param[in] data   Unused.
 *
 * @return FALSE
 *
 *******************************************************************************
 */

static gboolean
ToolsCorePoolNoOp(gpointer data)
{
   return FALSE;
}


/*
 *******************************************************************************
 * ToolsCorePoolRunThread --                                              */ /**
 *
 * Standalone thread runner. Executes the task associated with the thread, and
 * schedule a task to clean up the thread state when done.
 *
 * @param[in] data   A StandaloneTask.
 *
 * @return NULL
 *
 *******************************************************************************
 */

static gpointer
ToolsCorePoolRunThread(gpointer data)
{
   StandaloneTask *task = data;

   task->cb(gState.ctx, task->data);
   task->active = FALSE;

   g_mutex_lock(&gState.lock);
   /* If not active, the shutdown function will clean things up. */
   if (gState.active) {
      g_ptr_array_remove(gState.threads, task);
      g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
                      ToolsCorePoolNoOp,
                      task,
                      ToolsCorePoolDestroyThread);
   }
   g_mutex_unlock(&gState.lock);

   return NULL;
}


/*
 *******************************************************************************
 * ToolsCorePoolRunWorker --                                              */ /**
 *
 * Thread pool callback function. Dequeues the next work item from the work
 * queue and execute it.
 *
 * @param[in] state        Description of state.
 * @param[in] clientData   Description of clientData.
 *
 *******************************************************************************
 */

static void
ToolsCorePoolRunWorker(gpointer state,
                       gpointer clientData)
{
   WorkerTask *work;

   g_mutex_lock(&gState.lock);
   work = g_queue_pop_tail(gState.workQueue);
   g_mutex_unlock(&gState.lock);

   ASSERT(work != NULL);

   ToolsCorePoolDoWork(work);
   ToolsCorePoolDestroyTask(work);
}


/*
 *******************************************************************************
 * ToolsCorePoolSubmit --                                                 */ /**
 *
 * Submits a new task for execution in one of the shared worker threads.
 *
 * @see ToolsCorePool_SubmitTask()
 *
 * @param[in] ctx    Application context.
 * @param[in] cb     Function to execute the task.
 * @param[in] data   Opaque data for the task.
 * @param[in] dtor   Destructor for the task data.
 *
 * @return New task's ID, or 0 on error.
 *
 *******************************************************************************
 */

static guint
ToolsCorePoolSubmit(ToolsAppCtx *ctx,
                    ToolsCorePoolCb cb,
                    gpointer data,
                    GDestroyNotify dtor)
{
   guint id = 0;
   WorkerTask *task = g_malloc0(sizeof *task);

   task->srcId = 0;
   task->cb = cb;
   task->data = data;
   task->dtor = dtor;

   g_mutex_lock(&gState.lock);

   if (!gState.active) {
      g_free(task);
      goto exit;
   }

   /*
    * XXX: a reeeeeeeeeally long running task could cause clashes (e.g., reusing
    * the same task ID after the counter wraps). That shouldn't really happen in
    * practice (and is an abuse of the thread pool, and could cause issues if
    * someone sets the pool size to 0 or 1), but it might be good to have more
    * fail-safe code here.
    */
   if (gState.nextWorkId + 1 == UINT_MAX) {
      task->id = UINT_MAX;
      gState.nextWorkId = 0;
   } else {
      task->id = ++gState.nextWorkId;
   }

   id = task->id;

   /*
    * We always add the task to the queue, even in single threaded mode, so
    * that it can be canceled. In single threaded mode, it's unlikely someone
    * will be able to cancel it before it runs, but they can try.
    */
   g_queue_push_head(gState.workQueue, task);

   if (gState.pool != NULL) {
      GError *err = NULL;

      /* The client data pointer is bogus, just to avoid passing NULL. */
      g_thread_pool_push(gState.pool, &gState, &err);
      if (err == NULL) {
         goto exit;
      } else {
         g_warning("error sending work request, executing in service thread: %s",
                   err->message);
         g_clear_error(&err);
      }
   }

   /* Run the task in the service's thread. */
   task->srcId = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
                                 ToolsCorePoolDoWork,
                                 task,
                                 ToolsCorePoolDestroyTask);

exit:
   g_mutex_unlock(&gState.lock);
   return id;
}


/*
 *******************************************************************************
 * ToolsCorePoolCancel --                                                 */ /**
 *
 * Cancels a queue task.
 *
 * @see ToolsCorePool_CancelTask()
 *
 * @param[in] id  Task ID.
 *
 *******************************************************************************
 */

static void
ToolsCorePoolCancel(guint id)
{
   GList *taskLnk;
   WorkerTask *task = NULL;
   WorkerTask search = { id, };

   g_return_if_fail(id != 0);

   g_mutex_lock(&gState.lock);
   if (!gState.active) {
      goto exit;
   }

   taskLnk = g_queue_find_custom(gState.workQueue, &search, ToolsCorePoolCompareTask);
   if (taskLnk != NULL) {
      task = taskLnk->data;
      g_queue_delete_link(gState.workQueue, taskLnk);
   }

exit:
   g_mutex_unlock(&gState.lock);

   if (task != NULL) {
      if (task->srcId > 0) {
         g_source_remove(task->srcId);
      } else {
         ToolsCorePoolDestroyTask(task);
      }
   }
}


/*
 *******************************************************************************
 * ToolsCorePoolStart --                                                  */ /**
 *
 * Start a new task in a dedicated thread.
 *
 * @see ToolsCorePool_StartThread()
 *
 * @param[in] ctx        Application context.
 * @param[in] threadName Name for the new thread.
 * @param[in] cb         Callback that executes the task.
 * @param[in] interrupt  Callback that interrupts the task.
 * @param[in] data       Opaque data.
 * @param[in] dtor       Destructor for the task data.
 *
 * @return TRUE iff thread was successfully started.
 *
 *******************************************************************************
 */

static gboolean
ToolsCorePoolStart(ToolsAppCtx *ctx,
                   const gchar *threadName,
                   ToolsCorePoolCb cb,
                   ToolsCorePoolCb interrupt,
                   gpointer data,
                   GDestroyNotify dtor)
{
   GError *err = NULL;
   StandaloneTask *task = NULL;

   g_mutex_lock(&gState.lock);
   if (!gState.active) {
      goto exit;
   }

   task = g_malloc0(sizeof *task);
   task->active = TRUE;
   task->cb = cb;
   task->interrupt = interrupt;
   task->data = data;
   task->dtor = dtor;
   task->thread = g_thread_try_new(threadName, ToolsCorePoolRunThread, task, &err);

   if (err == NULL) {
      g_ptr_array_add(gState.threads, task);
   } else {
      g_warning("failed to start thread: %s.", err->message);
      g_clear_error(&err);
      g_free(task);
      task = NULL;
   }

exit:
   g_mutex_unlock(&gState.lock);
   return task != NULL;
}


/*
 *******************************************************************************
 * ToolsCorePool_Init --                                                  */ /**
 *
 * Initializes the shared thread pool. Reads configuration data from the
 * container-specific section of the config dictionary, so different containers
 * can have different configuration. Exports the thread pool functions through
 * the service's object.
 *
 * @param[in] ctx Application context.
 *
 *******************************************************************************
 */

void
ToolsCorePool_Init(ToolsAppCtx *ctx)
{
   gint maxThreads;
   GError *err = NULL;

   ToolsServiceProperty prop = { TOOLS_CORE_PROP_TPOOL };

   gState.funcs.submit = ToolsCorePoolSubmit;
   gState.funcs.cancel = ToolsCorePoolCancel;
   gState.funcs.start = ToolsCorePoolStart;
   gState.ctx = ctx;

   maxThreads = g_key_file_get_integer(ctx->config, ctx->name,
                                       "pool.maxThreads", &err);
   if (err != NULL) {
      maxThreads = DEFAULT_MAX_THREADS;
      g_clear_error(&err);
   }

   if (maxThreads > 0) {
      gState.pool = g_thread_pool_new(ToolsCorePoolRunWorker,
                                      NULL, maxThreads, FALSE, &err);
      if (err == NULL) {
         gint maxIdleTime;
         gint maxUnused;

         maxIdleTime = g_key_file_get_integer(ctx->config, ctx->name,
                                              "pool.maxIdleTime", &err);
         if (err != NULL || maxIdleTime <= 0) {
            maxIdleTime = DEFAULT_MAX_IDLE_TIME;
            g_clear_error(&err);
         }

         maxUnused = g_key_file_get_integer(ctx->config, ctx->name,
                                            "pool.maxUnusedThreads", &err);
         if (err != NULL || maxUnused < 0) {
            maxUnused = DEFAULT_MAX_UNUSED_THREADS;
            g_clear_error(&err);
         }

         g_thread_pool_set_max_idle_time(maxIdleTime);
         g_thread_pool_set_max_unused_threads(maxUnused);
      } else {
         g_warning("error initializing thread pool, running single threaded: %s",
                   err->message);
         g_clear_error(&err);
      }
   }

   gState.active = TRUE;
   g_mutex_init(&gState.lock);
   gState.threads = g_ptr_array_new();
   gState.workQueue = g_queue_new();

   ToolsCoreService_RegisterProperty(ctx->serviceObj, &prop);
   g_object_set(ctx->serviceObj, TOOLS_CORE_PROP_TPOOL, &gState.funcs, NULL);
}


/*
 *******************************************************************************
 * ToolsCorePool_Shutdown --                                              */ /**
 *
 * Shuts down the shared thread pool. This function will interrupt any running
 * threads (by calling their registered interrupt function), and wait for all
 * running tasks to finish before cleaning the remaining tasks and shared state.
 *
 * @param[in] ctx Application context.
 *
 *******************************************************************************
 */

void
ToolsCorePool_Shutdown(ToolsAppCtx *ctx)
{
   guint i;

   g_mutex_lock(&gState.lock);
   gState.active = FALSE;
   g_mutex_unlock(&gState.lock);

   /* Notify all spawned threads to stop. */
   for (i = 0; i < gState.threads->len; i++) {
      StandaloneTask *task = g_ptr_array_index(gState.threads, i);
      if (task->active && task->interrupt) {
         task->interrupt(gState.ctx, task->data);
      }
   }

   /* Stop the thread pool. */
   if (gState.pool != NULL) {
      g_thread_pool_free(gState.pool, TRUE, TRUE);
   }

   /* Join all spawned threads. */
   for (i = 0; i < gState.threads->len; i++) {
      StandaloneTask *task = g_ptr_array_index(gState.threads, i);
      ToolsCorePoolDestroyThread(task);
   }

   /* Destroy all pending tasks. */
   while (1) {
      WorkerTask *task = g_queue_pop_tail(gState.workQueue);
      if (task != NULL) {
         ToolsCorePoolDestroyTask(task);
      } else {
         break;
      }
   }

   /* Cleanup. */
   g_ptr_array_free(gState.threads, TRUE);
   g_queue_free(gState.workQueue);
   g_mutex_clear(&gState.lock);
   memset(&gState, 0, sizeof gState);
   g_object_set(ctx->serviceObj, TOOLS_CORE_PROP_TPOOL, NULL, NULL);
}

 070701000003CB000081A400000000000000000000000168225505000012DB000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/toolsCoreInt.h   /*********************************************************
 * Copyright (c) 2008-2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _TOOLSCOREINT_H_
#define _TOOLSCOREINT_H_

/**
 * @file toolsCoreInt.h
 *
 *    Internal functions for the tools daemon.
 */

#define VMW_TEXT_DOMAIN    "vmtoolsd"
#define G_LOG_DOMAIN       VMW_TEXT_DOMAIN
#define TOOLSCORE_COMMON   "common"

#include <glib-object.h>
#include <gmodule.h>
#include <time.h>
#if (defined(_WIN32)  && !defined(_ARM64_)) || \
    (defined(__linux__) && !defined(USERWORLD))
/* Need this header for GLOBALCONFIG_SUPPORTED definition.*/
#include "globalConfig.h"
#endif
#include "vmware/tools/plugin.h"
#include "vmware/tools/rpcdebug.h"

/* Used by the Windows implementation to communicate with other processes. */
#if defined(G_PLATFORM_WIN32)
#  define QUIT_EVENT_NAME_FMT         L"%S\\VMwareToolsQuitEvent_%s"
#  define DUMP_STATE_EVENT_NAME_FMT   L"%S\\VMwareToolsDumpStateEvent_%s"
#endif

/* On Mac OS, G_MODULE_SUFFIX seems to be defined to "so"... */
#if defined(__APPLE__)
#  if defined(G_MODULE_SUFFIX)
#     undef G_MODULE_SUFFIX
#  endif
#  define G_MODULE_SUFFIX "dylib"
#endif

#define VMTOOLS_APP_NAME "vmtools"

/** State of app providers. */
typedef enum {
   TOOLS_PROVIDER_IDLE,
   TOOLS_PROVIDER_ACTIVE,
   TOOLS_PROVIDER_ERROR,

   /* Keep this as the last one, always. */
   TOOLS_PROVIDER_MAX
} ToolsAppProviderState;

/** Defines the internal app provider data. */
typedef struct ToolsAppProviderReg {
   ToolsAppProvider       *prov;
   ToolsAppProviderState   state;
} ToolsAppProviderReg;

/** Defines internal service state. */
typedef struct ToolsServiceState {
   gchar         *name;
   gchar         *configFile;
   time_t         configMtime;
#if defined(GLOBALCONFIG_SUPPORTED)
   GKeyFile      *globalConfig;
   time_t         globalConfigMtime;
#endif
   guint          configCheckTask;
   gboolean       mainService;
   gboolean       capsRegistered;
   gchar         *commonPath;
   gchar         *pluginPath;
   GPtrArray     *plugins;
#if defined(_WIN32)
   gchar         *displayName;
#else
   gchar         *pidFile;
#endif
   GModule       *debugLib;
   gchar         *debugPlugin;
   RpcDebugLibData  *debugData;
   ToolsAppCtx    ctx;
   GArray        *providers;
#if defined(__linux__)
   /*
    * We hold a reference to vSocket device to avoid
    * address family re-registration when someone
    * connects over vSocket. We have vsockFamily
    * here mainly because it does not cost much
    * and it is useful for debug logs.
    */
   int            vsockDev;
   int            vsockFamily;
#endif
} ToolsServiceState;


gboolean
ToolsCore_ParseCommandLine(ToolsServiceState *state,
                           int argc,
                           char *argv[]);

void
ToolsCore_DumpPluginInfo(ToolsServiceState *state);

void
ToolsCore_DumpState(ToolsServiceState *state);

guint
ToolsCore_GetVmusrLimit(ToolsServiceState *state);

const char *
ToolsCore_GetTcloName(ToolsServiceState *state);

int
ToolsCore_Run(ToolsServiceState *state);

void
ToolsCore_Setup(ToolsServiceState *state);

gboolean
ToolsCore_InitRpc(ToolsServiceState *state);

#if defined(__linux__)
void
ToolsCore_InitVsockFamily(ToolsServiceState *state);

void
ToolsCore_ReleaseVsockFamily(ToolsServiceState *state);
#endif

gboolean
ToolsCore_LoadPlugins(ToolsServiceState *state);

void
ToolsCore_ReloadConfig(ToolsServiceState *state,
                       gboolean reset);

void
ToolsCore_RegisterPlugins(ToolsServiceState *state);

void
ToolsCore_SetCapabilities(RpcChannel *chan,
                          GArray *caps,
                          gboolean set);
#if defined(_WIN32)
gboolean
ToolsCore_CheckModuleVersion(const gchar *pluginPath,
                             gboolean checkBuildNumber);
#endif

void
ToolsCore_UnloadPlugins(ToolsServiceState *state);

#if defined(__APPLE__)
void
ToolsCore_CFRunLoop(ToolsServiceState *state);
#endif

void
ToolsCorePool_Init(ToolsAppCtx *ctx);

void
ToolsCorePool_Shutdown(ToolsAppCtx *ctx);

#endif /* _TOOLSCOREINT_H_ */

 070701000003CC000081A4000000000000000000000001682255050000371D000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/toolsHangDetector.c  /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file toolsHangDetector.c
 *
 *    Implementation of the tools hang detection and reporting
 */

#include <string.h>
#include <glib.h>

#include "vmware.h"
#include "vmware/tools/log.h"
#include "vmware/tools/threadPool.h"
#include "toolsHangDetector.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/guestrpc.h"

#define SLEEP_INTERVAL 1         /* approximately 1 second */
#define CHECKIN_INTERVAL 1       /* approximately 1 second */
#define COUNTER_RESET_VALUE 5    /* approximately 5 seconds */
#define STARVE_THRESHOLD 1.5

typedef enum {
   NORMAL,
   HUNG
} DetectedMode;

typedef struct HangDetectorState {
   /* 'mutex' and 'cond' protect concurrent accesses to 'terminate' flag */
   GMutex mutex;
   GCond cond;
   gboolean terminate;

   gint atomic;
   DetectedMode mode;
   GSource *checkinTimer;
   /*
    * Each slot records the time when the matched counter value is seen.
    * This helps figure out whether the detector thread was running under
    * a resource contention.
    */
   gint64 timeSeq[COUNTER_RESET_VALUE+1];
   gboolean vmxRejectedHealthUpdate;
} HangDetectorState;

static HangDetectorState gDetectorState;


/*
 ******************************************************************************
 * DetectorInit --                                                       */ /**
 *
 * Initialization
 *
 * Note: No need to call g_mutex_init, g_mutex_clear, g_cond_init,
 *       and g_cond_clear on statically allocated GMutex and GCond.
 *       Directly use them according to glib documentation.
 *
 ******************************************************************************
 */

static void
DetectorInit(void)
{
   HangDetectorState *state = &gDetectorState;

   state->terminate = FALSE;
   state->mode = NORMAL;
   state->vmxRejectedHealthUpdate = FALSE;
   g_atomic_int_set(&state->atomic, COUNTER_RESET_VALUE);
}


/*
 ******************************************************************************
 * DetectorFree --                                                       */ /**
 *
 * Resets any global state and frees up memory
 *
 * @param[in] args   Currently unused
 *
 ******************************************************************************
 */

static void
DetectorFree(UNUSED_PARAM(gpointer args))
{
   HangDetectorState *state = &gDetectorState;

   if (state->checkinTimer) {
      g_source_destroy(state->checkinTimer);
      g_source_unref(state->checkinTimer);
      state->checkinTimer = NULL;
   }
}


/*
 ******************************************************************************
 * DetectorTerminate --                                                  */ /**
 *
 * Signals the detector thread to exit.
 *
 * @param[in] ctx    Application context
 * @param[in] args   Currently unused
 *
 ******************************************************************************
 */

static void
DetectorTerminate(ToolsAppCtx *ctx,
                  UNUSED_PARAM(gpointer args))
{
   HangDetectorState *state = &gDetectorState;

   g_mutex_lock(&state->mutex);

   state->terminate = TRUE;
   g_cond_signal(&state->cond);

   g_mutex_unlock(&state->mutex);
}


/*
 ******************************************************************************
 * UpdateVmx --                                                         */ /**
 *
 * Notify the VMX about a tools service hang/recover event
 *
 * @param[in] event   event to put into the notification RPCI command.
 *
 ******************************************************************************
 */

static void
UpdateVmx(const char *event)
{
   HangDetectorState *state = &gDetectorState;
   RpcChannel *chan;
   gchar *msg;

   if (state->vmxRejectedHealthUpdate) {
      return;
   }

   chan = BackdoorChannel_New();
   if (NULL == chan) {
      g_warning("Failed to create a RPCI channel to send tools health event.\n");
      return;
   }

   if (!RpcChannel_Start(chan)) {
      g_warning("Failed to start a RPCI channel to send tools health event.\n");
      RpcChannel_Destroy(chan);
      return;
   }

   msg = g_strdup_printf("%s %s", UPDATE_TOOLS_HEALTH_CMD, event);
   ASSERT(NULL != msg);

   if(!RpcChannel_Send(chan, msg, strlen(msg), NULL, NULL)) {
      g_warning("Failed to send RPCI message: %s\n", msg);
      state->vmxRejectedHealthUpdate = TRUE;
   }

   g_free(msg);
   RpcChannel_Destroy(chan);
}


/*
 ******************************************************************************
 * GetTimeSeqString --                                                   */ /**
 *
 * The time sequence string is a sequence of elapsed time how long the detector
 * waited to run in the past.
 *
 * This is mainly for debugging right now.
 *
 * @return    the time sequence string in a statically allocated location.
 *            Do not free.
 *
 ******************************************************************************
 */

static const gchar *
GetTimeSeqString(void) {
   static gchar TimeSequence[COUNTER_RESET_VALUE * 8];
   gulong used = 0;
   gint i;
   HangDetectorState *state = &gDetectorState;

   for (i = 0; i < COUNTER_RESET_VALUE; ++i) {
      double elapsed = (state->timeSeq[i] - state->timeSeq[i+1])
         / (double)G_TIME_SPAN_SECOND;
      gulong left =  sizeof TimeSequence - used;
      gint ret;

      if (i == 0) {
         ret = g_snprintf(&TimeSequence[used], left, "%.2fs", elapsed);
      } else {
         ret = g_snprintf(&TimeSequence[used], left, ", %.2fs", elapsed);
      }

      if (ret < 0 || ret >= left) {
         TimeSequence[used] = '\0';
         break;
      }

      used += ret;
   }

   return TimeSequence;
}


/*
 ******************************************************************************
 * UpdateStateToHung --                                                  */ /**
 *
 * Update the current state to HUNG, and notify VMX
 *
 ******************************************************************************
 */

static void
UpdateStateToHung(void)
{
   HangDetectorState *state = &gDetectorState;
   double elapsed;

   state->mode = HUNG;

   elapsed = (state->timeSeq[0] - state->timeSeq[COUNTER_RESET_VALUE]) /
      (double)G_TIME_SPAN_SECOND;

   g_info("tools hang detector time sequence %s.", GetTimeSeqString());

   if (elapsed > SLEEP_INTERVAL * COUNTER_RESET_VALUE * STARVE_THRESHOLD) {
      g_info("tools service was slow for the last %.2f seconds.", elapsed);

      UpdateVmx(TOOLS_HEALTH_GUEST_SLOW_KEY);
   } else {
      g_info("tools service hung.");

      UpdateVmx(TOOLS_HEALTH_HUNG_KEY);
   }
}


/*
 ******************************************************************************
 * UpdateStateToNormal --                                                */ /**
 *
 * Update the current state to NORMAL, and notify VMX
 *
 ******************************************************************************
 */

static void
UpdateStateToNormal(void)
{
   HangDetectorState *state = &gDetectorState;

   state->mode = NORMAL;

   g_info("tools service recovered from a hang.");

   UpdateVmx(TOOLS_HEALTH_NORMAL_KEY);
}


/*
 ******************************************************************************
 * DetectorUpdate --                                                     */ /**
 *
 * Check the counter value and send proper updates to VMX
 *
 * @param[in] value   Counter value
 * @param[in] now     The current time stamp
 *
 ******************************************************************************
 */

static void
DetectorUpdate(gint value,
               gint64 now)
{
   HangDetectorState *state = &gDetectorState;

   if (value >= 0 && value <= COUNTER_RESET_VALUE) {
      state->timeSeq[value] = now;
   }

   if (state->mode == NORMAL) {
      if (value <= 0) {
         UpdateStateToHung();
      }
   } else {
      if (value > 0) {
         UpdateStateToNormal();
      }
   }
}


/*
 ******************************************************************************
 * SleepToExit --                                                        */ /**
 *
 * Sleep until the time specified, or return if the caller should terminate.
 *
 * @param[in] endTime  The end time stamp to sleep up to.
 *
 * @return TRUE if the caller should terminate, e.g. signaled by a terminator
 *         FALSE otherwise
 *
 ******************************************************************************
 */

static gboolean
SleepToExit(gint64 endTime)
{
   HangDetectorState *state = &gDetectorState;
   gboolean ret;

   g_mutex_lock(&state->mutex);

   while (!state->terminate) {
      if (!g_cond_wait_until(&state->cond, &state->mutex, endTime)) {
         /* endTime passed */
         ret = FALSE;
         goto exit;
      }
   }

   ret = TRUE;

exit:

   g_mutex_unlock(&state->mutex);

   return ret;
}


/*
 ******************************************************************************
 * DetectorThread --                                                     */ /**
 *
 * Detector thread entry function
 *
 * @param[in] ctx    Application context
 * @param[in] args   Currently unused
 *
 ******************************************************************************
 */

static void
DetectorThread(ToolsAppCtx *ctx,
               UNUSED_PARAM(gpointer args))
{
   HangDetectorState *state = &gDetectorState;

   while (1) {
      gint old = g_atomic_int_add(&state->atomic, -1);
      gint64 now = g_get_monotonic_time();
      gint64 endTime;

      DetectorUpdate(old, now);

      endTime = now + SLEEP_INTERVAL * G_TIME_SPAN_SECOND;
      if (SleepToExit(endTime)) {
         break;
      }
   }
}


/*
 ******************************************************************************
 * DetectorCheckin --                                                    */ /**
 *
 * Check in with the detector by resetting the counter
 *
 * @param[in] args   Currently unused
 *
 * @return TRUE   always, otherwise the event source is removed by glib
 *
 ******************************************************************************
 */

static gboolean
DetectorCheckin(UNUSED_PARAM(gpointer args))
{
   HangDetectorState *state = &gDetectorState;

   g_atomic_int_set(&state->atomic, COUNTER_RESET_VALUE);

   return TRUE;
}


/*
 ******************************************************************************
 * ScheduleCheckinTimer --                                               */ /**
 *
 * Schedule the periodic checkin timer with the main loop
 *
 * @param[in] ctx    Application Context
 *
 * @return TRUE iff timer is successfully scheduled
 *
 ******************************************************************************
 */

static gboolean
ScheduleCheckinTimer(ToolsAppCtx *ctx)
{
   HangDetectorState *state = &gDetectorState;
   GMainContext *mainCtx = g_main_loop_get_context(ctx->mainLoop);
   GSource *eventSource;

   ASSERT(NULL == state->checkinTimer);
   ASSERT(NULL != mainCtx);

   eventSource = VMTools_CreateTimer(CHECKIN_INTERVAL * 1000);

   if (NULL == eventSource) {
      return FALSE;
   }

   g_source_set_callback(eventSource, DetectorCheckin, NULL, NULL);
   g_source_attach(eventSource, mainCtx);

   state->checkinTimer = eventSource;

   return TRUE;
}


/*
 ******************************************************************************
 * ToolsCoreHangDetector_Start --                                        */ /**
 *
 * Register the checkin function to the tools main loop as a timer handler.
 * Start the detector thread to watch for the tools hang.
 *
 * @param[in] ctx    Application context.
 *
 * @return TRUE iff the hang detector is successfully started
 *
 ******************************************************************************
 */

gboolean
ToolsCoreHangDetector_Start(ToolsAppCtx *ctx)
{
   gboolean ret;
   GKeyFile *cfg = ctx->config;
   gboolean disabled;

   ASSERT(NULL != cfg);
   disabled = g_key_file_get_boolean(cfg, VMTOOLS_GUEST_SERVICE,
                                     "toolsHangDetectorDisabled",
                                     NULL);
   if (disabled) {
      g_info("tools hang detector is disabled");
      ret = FALSE;
      goto exit;
   }

   DetectorInit();

   ret = ScheduleCheckinTimer(ctx);
   if (!ret) {
      g_info("Unable to schedule hang detector checkin timer on the main loop");
      goto exit;
   }

   ret = ToolsCorePool_StartThread(ctx,
                                   "HangDetector",
                                   DetectorThread,
                                   DetectorTerminate,
                                   NULL,
                                   DetectorFree);
   if (!ret) {
      g_info("Unable to start the detector thread");
      DetectorFree(NULL);
   }

exit:

   return ret;
}


/*
 ******************************************************************************
 * ToolsCoreHangDetector_RpcReset --                                     */ /**
 *
 * Rpc Reset Handler for the tools hang detector module.
 * Just reset the vmxRejectedHealthUpdate boolean flag in case
 * the VM is migrated to a newer version of host that now supports the
 * health update.
 *
 ******************************************************************************
 */

void
ToolsCoreHangDetector_RpcReset(void)
{
   HangDetectorState *state = &gDetectorState;

   state->vmxRejectedHealthUpdate = FALSE;
}
   070701000003CD000081A4000000000000000000000001682255050000048F000000000000000000000000000000000000004900000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/toolsHangDetector.h  /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _TOOLS_HANG_DETECTOR_H_
#define _TOOLS_HANG_DETECTOR_H_

/**
 * @file toolsHangDetector.h
 *
 * Interface of the tools hang detection module.
 */

gboolean ToolsCoreHangDetector_Start(ToolsAppCtx *ctx);
void ToolsCoreHangDetector_RpcReset(void);

#endif /* _TOOLS_HANG_DETECTOR_H_ */
 070701000003CE000081A40000000000000000000000016822550500004608000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/services/vmtoolsd/toolsRpc.c   /*********************************************************
 * Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file toolsRpc.c
 *
 *    Functions related to the GuestRPC channel provided by the service.
 *    Provides the interface for the service to bring up the RPC channel,
 *    and handlers for the RPC messages which are handled by the service
 *    itself.
 */

#include <stdlib.h>
#include <string.h>
#if !defined(_WIN32)
#include <unistd.h>
#endif
#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "conf.h"
#include "guestApp.h"
#include "str.h"
#include "strutil.h"
#include "toolsCoreInt.h"
#include "vmtoolsd_version.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/log.h"
#include "vm_version.h"
#if defined(__linux__)
#include "vmci_sockets.h"
#endif

/**
 * Take action after an RPC channel reset.
 *
 * @param[in]  chan     The RPC channel.
 * @param[in]  success  Whether reset was successful.
 * @param[in]  _state   The service state.
 */

static void
ToolsCoreCheckReset(RpcChannel *chan,
                    gboolean success,
                    gpointer _state)
{
   ToolsServiceState *state = _state;
   static gboolean version_sent = FALSE;

   ASSERT(state != NULL);
   ASSERT(chan == state->ctx.rpc);

   if (success) {
      const gchar *app;
      gchar *msg;

      app = ToolsCore_GetTcloName(state);
      if (app == NULL) {
         app = state->name;
      }

      msg = g_strdup_printf("vmx.capability.unified_loop %s", app);
      if (!RpcChannel_Send(state->ctx.rpc, msg, strlen(msg) + 1, NULL, NULL)) {
         g_warning("VMX doesn't support the Tools unified loop.\n"
                   "Some functionality (like setting options) may not work.\n");
      }
      g_free(msg);

      if (!version_sent) {
         /*
          * Log the Tools version to the VMX log file. We don't really care
          * if sending the message fails.
          */
         msg = g_strdup_printf("log %s: Version: %s (%s)",
                               app, VMTOOLSD_VERSION_STRING, BUILD_NUMBER);
         RpcChannel_Send(state->ctx.rpc, msg, strlen(msg) + 1, NULL, NULL);
         g_free(msg);
         /* send message only once to prevent log spewing: */
         version_sent = TRUE;
      }

      g_signal_emit_by_name(state->ctx.serviceObj,
                            TOOLS_CORE_SIG_RESET,
                            &state->ctx);
#if defined(__linux__)
      if (state->mainService) {
         /*
          * Release the existing vSocket family.
          */
         ToolsCore_ReleaseVsockFamily(state);
         ToolsCore_InitVsockFamily(state);
      }
#endif
   } else {
      VMTOOLSAPP_ERROR(&state->ctx, EXIT_FAILURE);
   }
}


#if !defined(_WIN32)
/**
 * ToolsCoreAppChannelFail --
 *
 * Call-back function for RpcChannel to report that the RPC channel for the
 * toolbox-dnd (vmusr) application cannot be acquired. This would signify
 * that the channel is currently in use by another vmusr process.
 *
 * @param[in]  _state   The service state.
 */

static void
ToolsCoreAppChannelFail(UNUSED_PARAM(gpointer _state))
{
   char *cmdGrepVmusrTools;
#if !defined(__APPLE__)
   ToolsServiceState *state = _state;
#endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(sun)
   static const char  *vmusrGrepExpr = "'vmtoolsd.*vmusr'";
#if defined(sun)
   static const char *psCmd = "ps -aef";
#else
   static const char *psCmd = "ps ax";     /* using BSD syntax */
#endif
#else  /* Mac OS */
   static const char  *vmusrGrepExpr = "'vmware-tools-daemon.*vmusr'";
   static const char *psCmd = "ps -ex";
#endif

   cmdGrepVmusrTools = Str_Asprintf(NULL, "%s | egrep %s | egrep -v 'grep|%d'",
                                    psCmd, vmusrGrepExpr, (int) getpid());

   /*
    * Check if there is another vmtoolsd vmusr process running on the
    * system and log the appropriate warning message before terminating
    * this vmusr process.
    */
   if (system(cmdGrepVmusrTools) == 0) {
      g_warning("Exiting the vmusr process. Another vmusr process is "
                "currently running.\n");
   } else {
      g_warning("Exiting the vmusr process; unable to acquire the channel.\n");
   }
   free(cmdGrepVmusrTools);

#if !defined(__APPLE__)
   if (g_main_loop_is_running(state->ctx.mainLoop)) {
      g_warning("Calling g_main_loop_quit() to terminate the process.\n");
      g_main_loop_quit(state->ctx.mainLoop);
   } else {
      g_warning("Exiting the process.\n");
      exit(1);
   }
#else  /* Mac OS */
   /*
    * On Mac OS X, always exit with non-zero status. This is a signal to
    * launchd that the vmusr process had a "permanent" failure and should
    * not be automatically restarted for this user session.
    */
   g_warning("Exiting the process.\n");
   exit(1);
#endif
}
#endif


/**
 * Checks all loaded plugins for their capabilities, and sends the data to the
 * host. The code will try to send all capabilities, just logging errors as
 * they occur.
 *
 * @param[in]  data     The RPC data.
 *
 * @return TRUE.
 */

static gboolean
ToolsCoreRpcCapReg(RpcInData *data)
{
   char *confPath = GuestApp_GetConfPath();
   gchar *msg;
   GArray *pcaps = NULL;
   ToolsServiceState *state = data->clientData;

   g_signal_emit_by_name(state->ctx.serviceObj,
                         TOOLS_CORE_SIG_CAPABILITIES,
                         &state->ctx,
                         TRUE,
                         &pcaps);

   if (pcaps != NULL) {
      ToolsCore_SetCapabilities(state->ctx.rpc, pcaps, TRUE);
      g_array_free(pcaps, TRUE);
   }

   /* Tell the host the location of the conf directory. */
   msg = g_strdup_printf("tools.capability.guest_conf_directory %s", confPath);
   if (!RpcChannel_Send(state->ctx.rpc, msg, strlen(msg) + 1, NULL, NULL)) {
      g_warning("Unable to register guest conf directory capability.\n");
   }
   g_free(msg);
   msg = NULL;

   /* Send the tools version to the VMX. */
   if (state->mainService) {
      uint32 version;
      uint32 type = TOOLS_TYPE_UNKNOWN;
      char *result = NULL;
      size_t resultLen;
      gchar *toolsVersion;
      gboolean hideVersion = g_key_file_get_boolean(state->ctx.config,
                                                    "vmtools",
                                                    CONFNAME_HIDETOOLSVERSION,
                                                    NULL);

#if defined(_WIN32)
      type = TOOLS_TYPE_MSI;
#else
#if defined(OPEN_VM_TOOLS)
      type = TOOLS_TYPE_OVT;
#else
      {
         static int is_osp = -1;

         if (is_osp == -1) {
            is_osp = (access("/usr/lib/vmware-tools/dsp", F_OK) == 0);
         }
         type = is_osp ? TOOLS_TYPE_OSP : TOOLS_TYPE_TARBALL;
      }
#endif
#endif

      version = hideVersion ? TOOLS_VERSION_UNMANAGED : TOOLS_VERSION_CURRENT;

      /*
       * First try "tools.set.versiontype", if that fails because host is too
       * old, fall back to "tools.set.version."
       */
      toolsVersion = g_strdup_printf("tools.set.versiontype %u %u", version, type);

      if (!RpcChannel_Send(state->ctx.rpc, toolsVersion, strlen(toolsVersion) + 1,
                           &result, &resultLen)) {
         GError *gerror = NULL;
         gboolean disableVersion;
         vm_free(result);
         g_free(toolsVersion);

         /*
          * Fall back to old behavior for OSPs and OVT so that tools will be
          * reported as guest managed.
          */
         disableVersion = g_key_file_get_boolean(state->ctx.config,
                                                          "vmtools",
                                                          CONFNAME_DISABLETOOLSVERSION,
                                                          &gerror);

         /* By default disableVersion is FALSE, except for open-vm-tools */
         if (type == TOOLS_TYPE_OVT && gerror != NULL) {
            g_debug("gerror->code = %d when checking for %s\n", gerror->code, CONFNAME_DISABLETOOLSVERSION);
            g_clear_error(&gerror);
            disableVersion = TRUE;
         }

         version = disableVersion ? TOOLS_VERSION_UNMANAGED : TOOLS_VERSION_CURRENT;
         toolsVersion = g_strdup_printf("tools.set.version %u", version);

         if (!RpcChannel_Send(state->ctx.rpc, toolsVersion, strlen(toolsVersion) + 1,
                              &result, &resultLen)) {
            g_warning("Error setting tools version: %s.\n",
                      VM_SAFE_STR(result));
         }
      }
      vm_free(result);
      g_free(toolsVersion);
   }

   state->capsRegistered = TRUE;
   free(confPath);
   return RPCIN_SETRETVALS(data, "", TRUE);
}


/**
 * Handles a "set option" RPC. Calls the plugins which have registered interest
 * in the option being set.
 *
 * @param[in]  data     The RPC data.
 *
 * @return Whether the option was successfully processed.
 */

static gboolean
ToolsCoreRpcSetOption(RpcInData *data)
{

   gboolean retVal = FALSE;
   char *option;
   char *value;
   unsigned int index = 0;
   ToolsServiceState *state = data->clientData;

   /* Parse the option & value string. */
   option = StrUtil_GetNextToken(&index, data->args, " ");
   /* Ignore leading space before value. */
   index++;
   value = StrUtil_GetNextToken(&index, data->args, "");

   if (option != NULL && value != NULL && strlen(value) != 0) {

      g_debug("Setting option '%s' to '%s'.\n", option, value);
      g_signal_emit_by_name(state->ctx.serviceObj,
                            TOOLS_CORE_SIG_SET_OPTION,
                            &state->ctx,
                            option,
                            value,
                            &retVal);
   }

   vm_free(option);
   vm_free(value);

   RPCIN_SETRETVALS(data, retVal ? "" : "Unknown or invalid option", retVal);

   return retVal;
}


/**
 * Initializes the RPC channel. Currently this instantiates an RpcIn loop.
 * This function should only be called once.
 *
 * @param[in]  state    The service state.
 *
 * @return TRUE on success.
 */

gboolean
ToolsCore_InitRpc(ToolsServiceState *state)
{
   static RpcChannelCallback rpcs[] = {
      { "Capabilities_Register", ToolsCoreRpcCapReg, NULL, NULL, NULL, 0 },
      { "Set_Option", ToolsCoreRpcSetOption, NULL, NULL, NULL, 0 },
   };

   const gchar *app;
   GMainContext *mainCtx = g_main_loop_get_context(state->ctx.mainLoop);

   ASSERT(state->ctx.rpc == NULL);

   if (state->debugPlugin != NULL) {
      app = "debug";
      state->ctx.rpc = state->debugData->newDebugChannel(&state->ctx,
                                                         state->debugData);
   } else {
      /*
       * Currently we try to bring up an RpcIn channel, which will only run
       * inside a Virtual Machine. Some plugins may still want to launch and at
       * least begin even in not in a VM (typically because the installation is dual
       * purposed between a VM and Bootcamp) - plugins may wish to undo some state
       * if not in a VM.
       *
       * XXX: this should be relaxed when we try to bring up a VMCI or TCP channel.
       */
      if (!state->ctx.isVMware) {
         g_info("The %s service needs to run inside a virtual machine.\n",
                state->name);
         state->ctx.rpc = NULL;
      } else {
         state->ctx.rpc = RpcChannel_New();
      }
      app = ToolsCore_GetTcloName(state);
      if (app == NULL) {
         g_warning("Trying to start RPC channel for invalid %s container.", state->name);
         return FALSE;
      }
   }

   if (state->ctx.rpc) {

      /*
       * Default tools RpcChannel setup: No channel error threshold limit and
       *                                 no notification callback function.
       */
      RpcChannelFailureCb failureCb = NULL;
      guint errorLimit = 0;
      size_t i;

#if !defined(_WIN32)

      /* For the *nix user service app. */
      if (TOOLS_IS_USER_SERVICE(state)) {
         failureCb = ToolsCoreAppChannelFail;
         errorLimit = ToolsCore_GetVmusrLimit(state);
      }
#endif

      RpcChannel_Setup(state->ctx.rpc,
                       app,
                       mainCtx,
                       &state->ctx,
                       ToolsCoreCheckReset,
                       state,
                       failureCb,
                       errorLimit);

      /* Register the "built in" RPCs. */
      for (i = 0; i < ARRAYSIZE(rpcs); i++) {
         RpcChannelCallback *rpc = &rpcs[i];
         rpc->clientData = state;
         RpcChannel_RegisterCallback(state->ctx.rpc, rpc);
      }
   }

   return TRUE;
}


/**
 * Sends a list of capabilities to the host.
 *
 * @param[in]  chan     The RPC channel.
 * @param[in]  caps     The list of capabilities.
 * @param[in]  set      TRUE is setting capabilities (otherwise they're set to 0).
 */

void
ToolsCore_SetCapabilities(RpcChannel *chan,
                          GArray *caps,
                          gboolean set)
{
   char *result;
   size_t resultLen;
   guint i;
   gchar *newcaps = NULL;

   for (i = 0; i < caps->len; i++) {
      gchar *tmp;
      ToolsAppCapability *cap =  &g_array_index(caps, ToolsAppCapability, i);
      switch (cap->type) {
      case TOOLS_CAP_OLD:
         result = NULL;
         tmp = g_strdup_printf("tools.capability.%s %u",
                               cap->name,
                               set ? cap->value : 0);
         if (!RpcChannel_Send(chan, tmp, strlen(tmp) + 1, &result, &resultLen)) {
            g_warning("Error sending capability %s: %s\n", cap->name,
                      VM_SAFE_STR(result));
         }
         vm_free(result);
         g_free(tmp);
         break;

      case TOOLS_CAP_OLD_NOVAL:
         /*
          * This is kind of weird, because of the way the VMX treats RPCs and
          * what is expected of these capabilities without arguments. For a
          * few details, see the comments in RpcOut_sendOne() (rpcout.c).
          * Basically, for the VMX handlers not to complain, we need to send the
          * RPC with the empty space at the end, and not consider the NULL
          * character when counting the bytes.
          */
         if (set) {
            tmp = g_strdup_printf("tools.capability.%s ", cap->name);
            if (!RpcChannel_Send(chan, tmp, strlen(tmp), &result, &resultLen)) {
               g_warning("Error sending capability %s: %s\n", cap->name,
                         VM_SAFE_STR(result));
            }
            vm_free(result);
            g_free(tmp);
         }
         break;

      case TOOLS_CAP_NEW:
         if (newcaps == NULL) {
            newcaps = g_strdup(GUEST_CAP_FEATURES);
         }
         tmp = g_strdup_printf("%s %d=%u",
                               newcaps,
                               cap->index,
                               set ? cap->value : 0);
         g_free(newcaps);
         newcaps = tmp;
         break;

      default:
         g_error("Invalid capability type: %d\n", cap->type);
      }
   }

   if (newcaps != NULL) {
      result = NULL;
      if (!RpcChannel_Send(chan, newcaps, strlen(newcaps) + 1, &result, &resultLen)) {
         g_warning("Error sending new-style capabilities: %s\n",
                   VM_SAFE_STR(result));
      }
      vm_free(result);
      g_free(newcaps);
   }
}


#if defined(__linux__)
/**
 * Initializes the vSocket address family and sticks a reference
 * to it in the service state.
 *
 * @param[in]  state    The service state.
 */

void
ToolsCore_InitVsockFamily(ToolsServiceState *state)
{
   int vsockDev = -1;
   int vsockFamily = -1;

   ASSERT(state);

   state->vsockDev = -1;
   state->vsockFamily = -1;

   if (!state->ctx.rpc) {
      /*
       * Nothing more to do when there is no RPC channel.
       */
      g_debug("No RPC channel; skipping reference to vSocket family.\n");
      return;
   }

   switch (RpcChannel_GetType(state->ctx.rpc)) {
   case RPCCHANNEL_TYPE_INACTIVE:
   case RPCCHANNEL_TYPE_PRIV_VSOCK:
   case RPCCHANNEL_TYPE_UNPRIV_VSOCK:
      return;
   case RPCCHANNEL_TYPE_BKDOOR:
      vsockFamily = VMCISock_GetAFValueFd(&vsockDev);
      if (vsockFamily == -1) {
         /*
          * vSocket driver may not be loaded, log and continue.
          */
         g_warning("Couldn't get vSocket family.\n");
      } else if (vsockDev >= 0) {
         g_debug("Saving reference to vSocket device=%d, family=%d\n",
                 vsockDev, vsockFamily);
         state->vsockFamily = vsockFamily;
         state->vsockDev = vsockDev;
      }
      return;
   default:
      NOT_IMPLEMENTED();
   }
}


/**
 * Releases the reference to vSocket address family.
 *
 * @param[in]  state    The service state.
 *
 * @return TRUE on success.
 */

void
ToolsCore_ReleaseVsockFamily(ToolsServiceState *state)
{
   ASSERT(state);

   /*
    * vSocket device is not opened in case of new kernels.
    * Therefore, we release it only if it was opened.
    */
   if (state->vsockFamily >= 0 && state->vsockDev >= 0) {
      g_debug("Releasing reference to vSocket device=%d, family=%d\n",
              state->vsockDev, state->vsockFamily);
      VMCISock_ReleaseAFValueFd(state->vsockDev);
      state->vsockDev = -1;
      state->vsockFamily = -1;
   }
}
#endif
070701000003CF000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002900000000open-vm-tools-12.5.2/open-vm-tools/tests  070701000003D0000081A40000000000000000000000016822550500000493000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/tests/Makefile.am  ################################################################################
### Copyright (c) 2009-2016,2022,2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =
SUBDIRS += vmrpcdbg
SUBDIRS += testDebug
SUBDIRS += testPlugin
SUBDIRS += testVmblock





STATICLIBS = *.a
LTLIBS     = *.la

install-exec-local:
	rm -f $(DESTDIR)$(TEST_PLUGIN_INSTALLDIR)/$(STATICLIBS)
	rm -f $(DESTDIR)$(TEST_PLUGIN_INSTALLDIR)/$(LTLIBS)
 070701000003D1000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/tests/testDebug    070701000003D2000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/tests/testDebug/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000003D3000081A400000000000000000000000168225505000007D0000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/tests/testDebug/Makefile.am    ################################################################################
### Copyright (c) 2009-2016, 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @TEST_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libtestDebug.la

libtestDebug_la_CPPFLAGS =
libtestDebug_la_CPPFLAGS += @CUNIT_CPPFLAGS@
libtestDebug_la_CPPFLAGS += @GOBJECT_CPPFLAGS@
libtestDebug_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libtestDebug_la_CPPFLAGS += @XDR_CPPFLAGS@

libtestDebug_la_LDFLAGS =
libtestDebug_la_LDFLAGS += @PLUGIN_LDFLAGS@

libtestDebug_la_LIBADD =
libtestDebug_la_LIBADD += @CUNIT_LIBS@
libtestDebug_la_LIBADD += @GOBJECT_LIBS@
libtestDebug_la_LIBADD += @VMTOOLS_LIBS@
libtestDebug_la_LIBADD += @XDR_LIBS@
libtestDebug_la_LIBADD += ../vmrpcdbg/libvmrpcdbg.la

libtestDebug_la_SOURCES =
libtestDebug_la_SOURCES += testDebug.c
libtestDebug_la_SOURCES += testData_xdr.c

BUILT_SOURCES =
BUILT_SOURCES += testData_xdr.c
BUILT_SOURCES += testData.h

# XXX: see explanation in lib/guestRpc/Makefile.am
CFLAGS += -Wno-unused

CLEANFILES =
CLEANFILES += testData_xdr.c
CLEANFILES += testData.h

testData.h: $(top_srcdir)/tests/testPlugin/testData.x
	@RPCGEN_WRAPPER@ tests/testPlugin/testData.x $@

testData_xdr.c: testData.h
	@RPCGEN_WRAPPER@ tests/testPlugin/testData.x $@

070701000003D4000081A4000000000000000000000001682255050000213A000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/tests/testDebug/testDebug.c    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file testDebug.c
 *
 * A simple debug plugin that validates the messages sent by the service after
 * a "reset" is received, and also interacts with the test plugin to test the
 * functions provided by the service.
 */

#define G_LOG_DOMAIN "testDebug"
#include <glib-object.h>
#include <CUnit/CUnit.h>

#include "util.h"
#include "testData.h"
#include "xdrutil.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/rpcdebug.h"

static gboolean
TestDebugValidateReset(RpcInData *data, gboolean ret);

static gboolean
TestDebugValidateUnknown(RpcInData *data, gboolean ret);

static gboolean
TestDebugValidateRpc3(RpcInData *data, gboolean ret);

#define SET_OPTION_TEST ("Set_Option " TOOLSOPTION_BROADCASTIP " 1")

/** RPC messages injected into the application using RpcDebug_SendNext(). */
static RpcDebugMsgMapping gRpcMessages[] = {
   { "reset", sizeof "reset", TestDebugValidateReset, FALSE },
   { "ping", sizeof "ping", NULL, FALSE },
   { "Capabilities_Register", sizeof "Capabilities_Register", NULL, FALSE },
   { "test.rpcin.unknown", sizeof "test.rpcin.unknown", TestDebugValidateUnknown, FALSE },
   /* This one is initialized manually, since it contains dynamic data. */
   { NULL, 0, NULL, TRUE },
   { "test.rpcin.msg2", sizeof "test.rpcin.msg2", NULL, FALSE },
   { "test.rpcin.msg3", sizeof "test.rpcin.msg3", TestDebugValidateRpc3, FALSE },
   { SET_OPTION_TEST, sizeof SET_OPTION_TEST, NULL, FALSE },
   { "Capabilities_Register", sizeof "Capabilities_Register", NULL, FALSE },
   /* NULL terminator. */
   { NULL, 0, NULL, FALSE }
};

static gboolean gSignalReceived = FALSE;
static ToolsAppCtx *gCtx;


/**
 * Handles a "test-signal" sent by the test plugin. Just sets a static variable
 * that will be checked by TestDebugReceiveRpc1 to make sure custom signals are
 * working.
 *
 * @param[in]  src      Unused.
 * @param[in]  data     Unused.
 */

static void
TestDebugHandleSignal(gpointer src,
                      gpointer data)
{
   g_debug("Received test signal.\n");
   gSignalReceived = TRUE;
}


/**
 * Validates the response from a "reset".
 *
 * @param[in]  data     RPC request data.
 * @param[in]  ret      Return value from RPC handler.
 *
 * @return @a ret.
 */

static gboolean
TestDebugValidateReset(RpcInData *data,
                       gboolean ret)
{
   RPCDEBUG_ASSERT(data->result != NULL, FALSE);
   CU_ASSERT_STRING_EQUAL(data->result, "ATR debug");
   return (gboolean) ret;
}


/**
 * Validates a "test.rpcout.msg1" message sent by the test plugin. This message
 * is sent when the plugin receives a "test.rpcin.msg1" RPC, and contains an
 * XDR-encoded TestPluginData struct.
 *
 * @param[in]  data        Incoming data.
 * @param[in]  dataLen     Size of incoming data.
 * @param[out] result      Result sent back to the application.
 * @param[out] resultLen   Length of result.
 *
 * @return TRUE (asserts on failure).
 */

static gboolean
TestDebugReceiveRpc1(char *data,
                     size_t dataLen,
                     char **result,
                     size_t *resultLen)
{
   TestPluginData *details = (TestPluginData *) data;

   CU_ASSERT(gSignalReceived);
   CU_ASSERT_STRING_EQUAL(details->data, "rpc1test");
   CU_ASSERT_EQUAL(details->f_int, 1357);
   CU_ASSERT(details->f_bool);

   return TRUE;
}


/**
 * Validates the response of the "msg3" RPC.
 *
 * @param[in] data   RPC data.
 * @param[in] ret    RPC result.
 *
 * @return Whether the RPC succeded.
 */

static gboolean
TestDebugValidateRpc3(RpcInData *data,
                      gboolean ret)
{
   TestPluginData pdata = { NULL, };

   CU_ASSERT(XdrUtil_Deserialize(data->result, data->resultLen,
                                 xdr_TestPluginData, &pdata));

   CU_ASSERT_STRING_EQUAL(pdata.data, "Hello World!");
   CU_ASSERT_EQUAL(pdata.f_int, 8642);
   CU_ASSERT(pdata.f_bool);

   VMX_XDR_FREE(xdr_TestPluginData, &pdata);
   return ret;
}


/**
 * Validates a "version" message sent during capability registration.
 * No validation is actually done - the message is just printed.
 *
 * @param[in]  data        Incoming data.
 * @param[in]  dataLen     Size of incoming data.
 * @param[out] result      Result sent back to the application.
 * @param[out] resultLen   Length of result.
 *
 * @return TRUE.
 */

static gboolean
TestDebugReceiveVersion(char *data,
                        size_t dataLen,
                        char **result,
                        size_t *resultLen)
{
   g_debug("Received tools version message: %s\n", data);
   RpcDebug_SetResult("", result, resultLen);
   return TRUE;
}


/**
 * Validates the results for an unknown RPC sent to the guest.
 *
 * @param[in]  data     RPC request data.
 * @param[in]  ret      Return value from RPC handler.
 *
 * @return The opposite of @a ret.
 */

static gboolean
TestDebugValidateUnknown(RpcInData *data,
                         gboolean ret)
{
   CU_ASSERT_STRING_EQUAL(data->result, "Unknown Command");
   return !ret;
}


/**
 * Populates the RPC request data with the next message in the queue.
 *
 * @param[in]  rpcdata     Data for the injected RPC request data.
 *
 * @return TRUE if sending messages, FALSE if no more messages to be sent.
 */

static gboolean
TestDebugSendNext(RpcDebugMsgMapping *rpcdata)
{
   static RpcDebugMsgList msgList = { gRpcMessages, 0 };
   if (!RpcDebug_SendNext(rpcdata, &msgList)) {
      /*
       * Test for bug 391553: send two resets in sequence without
       * pumping the main loop. The channel should handle the second
       * reset successfully instead of asserting.
       */
      int i;
      for (i = 0; i < 2; i++) {
         RpcInData data;
         memset(&data, 0, sizeof data);
         data.clientData = gCtx->rpc;
         data.appCtx = gCtx;
         data.args = "reset";
         data.argsSize = sizeof "reset";

         g_debug("reset test %d\n", i + 1);
         RpcChannel_Dispatch(&data);
      }
      return FALSE;
   }
   return TRUE;
}


/**
 * Returns the debug plugin's registration data.
 *
 * @param[in]  ctx      The application context.
 *
 * @return The application data.
 */

TOOLS_MODULE_EXPORT RpcDebugPlugin *
RpcDebugOnLoad(ToolsAppCtx *ctx)
{
   static RpcDebugRecvMapping recvFns[] = {
      { "tools.set.version", TestDebugReceiveVersion, NULL, 0 },
      { "test.rpcout.msg1", TestDebugReceiveRpc1,
        xdr_TestPluginData, sizeof (TestPluginData) },
      { NULL, NULL }
   };
   static ToolsPluginData pluginData = {
      "testDebug",
      NULL,
      NULL,
      NULL,
   };
   static RpcDebugPlugin regData = {
      recvFns,
      NULL,
      TestDebugSendNext,
      NULL,
      &pluginData,
   };

   /* Standard plugin interface, used to listen for signals. */
   {
      ToolsPluginSignalCb sigs[] = {
         { "test-signal", TestDebugHandleSignal, NULL },
      };
      ToolsAppReg regs[] = {
         { TOOLS_APP_SIGNALS, VMTOOLS_WRAP_ARRAY(sigs) },
      };

      pluginData.regs = VMTOOLS_WRAP_ARRAY(regs);
   }

   /* Initialize the paylod of the "test.rpcin.msg1" RPC. */
   {
      TestPluginData testdata;
      testdata.data = "rpc1test";
      testdata.f_int = 1357;
      testdata.f_bool = TRUE;

      /* Build the command for the "test.rpcin.msg1" RPC. */
      if (!RpcChannel_BuildXdrCommand("test.rpcin.msg1",
                                      xdr_TestPluginData,
                                      &testdata,
                                      &gRpcMessages[4].message,
                                      &gRpcMessages[4].messageLen)) {
         g_error("Failed to create test.rpcin.msg1 command.\n");
      }
   }

   gCtx = ctx;
   return &regData;
}

  070701000003D5000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/tests/testPlugin   070701000003D6000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/tests/testPlugin/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000003D7000081A400000000000000000000000168225505000007BF000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/tests/testPlugin/Makefile.am   ################################################################################
### Copyright (c) 2009-2016, 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

plugindir = @TEST_PLUGIN_INSTALLDIR@
plugin_LTLIBRARIES = libtestPlugin.la

libtestPlugin_la_CPPFLAGS =
libtestPlugin_la_CPPFLAGS += @CUNIT_CPPFLAGS@
libtestPlugin_la_CPPFLAGS += @GOBJECT_CPPFLAGS@
libtestPlugin_la_CPPFLAGS += @PLUGIN_CPPFLAGS@
libtestPlugin_la_CPPFLAGS += @XDR_CPPFLAGS@

libtestPlugin_la_LDFLAGS =
libtestPlugin_la_LDFLAGS += @PLUGIN_LDFLAGS@

libtestPlugin_la_LIBADD =
libtestPlugin_la_LIBADD += @CUNIT_LIBS@
libtestPlugin_la_LIBADD += @GOBJECT_LIBS@
libtestPlugin_la_LIBADD += @VMTOOLS_LIBS@
libtestPlugin_la_LIBADD += @XDR_LIBS@

libtestPlugin_la_SOURCES =
libtestPlugin_la_SOURCES += testData_xdr.c
libtestPlugin_la_SOURCES += testPlugin.c

BUILT_SOURCES =
BUILT_SOURCES += testData_xdr.c
BUILT_SOURCES += testData.h

# XXX: see explanation in lib/guestRpc/Makefile.am
CFLAGS += -Wno-unused

CLEANFILES =
CLEANFILES += testData_xdr.c
CLEANFILES += testData.h

EXTRA_DIST =
EXTRA_DIST += testData.x

testData.h: testData.x
	@RPCGEN_WRAPPER@ tests/testPlugin/testData.x $@

testData_xdr.c: testData.x testData.h
	@RPCGEN_WRAPPER@ tests/testPlugin/testData.x $@

 070701000003D8000081A40000000000000000000000016822550500000437000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/tests/testPlugin/testData.x    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file testData.x
 *
 *    Just define a struct for testing automatic XDR serialization.
 */

struct TestPluginData {
   string      data<128>;
   uint32      f_int;
   Bool        f_bool;
};

 070701000003D9000081A40000000000000000000000016822550500002F26000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/tests/testPlugin/testPlugin.c  /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file testPlugin.c
 *
 *    Implements a test plugin for the tools services. The plugin register for
 *    a few RPCs that are never sent by the VMX, so to "use" it you have to use
 *    a debug plugin that sends those RPCs. The test debug plugin does that.
 */

#define G_LOG_DOMAIN "test"

#include <glib-object.h>
#include <gmodule.h>
#include <CUnit/CUnit.h>

#include "testData.h"
#include "util.h"
#include "vmware/tools/log.h"
#include "vmware/tools/plugin.h"
#include "vmware/tools/rpcdebug.h"
#include "vmware/tools/utils.h"

#define TEST_APP_PROVIDER        "TestProvider"
#define TEST_APP_NAME            "TestProviderApp1"
#define TEST_APP_ERROR           "TestProviderError"
#define TEST_APP_DONT_REGISTER   "TestProviderDontRegister"

#define TEST_SIG_INVALID   "TestInvalidSignal"

typedef struct TestApp {
   const char *name;
} TestApp;


static gboolean gInvalidAppError = FALSE;
static gboolean gInvalidAppProvider = FALSE;
static gboolean gInvalidSigError = FALSE;
static gboolean gValidAppRegistration = FALSE;


/**
 * Handles a "test.rpcin.msg1" RPC message. The incoming data should be an
 * XDR-encoded TestPluginData struct; the struct is written back
 * to the RPC channel using RpcChannel_Send (command "test.rpcout.msg1").
 *
 * Also emits a "test-signal", to test custom signal registration.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE on success.
 */

static gboolean
TestPluginRpc1(RpcInData *data)
{
   ToolsAppCtx *ctx = data->appCtx;
   TestPluginData *testdata = (TestPluginData *) data->args;
   char *cmd;
   size_t cmdLen;

   CU_ASSERT_STRING_EQUAL(testdata->data, "rpc1test");
   CU_ASSERT_EQUAL(testdata->f_int, 1357);
   CU_ASSERT(testdata->f_bool);

   g_signal_emit_by_name(ctx->serviceObj, "test-signal");

   if (!RpcChannel_BuildXdrCommand("test.rpcout.msg1",
                                   xdr_TestPluginData,
                                   testdata,
                                   &cmd,
                                   &cmdLen)) {
      vm_error("Failed to create test.rpcout.msg1 command.");
   }

   if (!RpcChannel_Send(ctx->rpc, cmd, cmdLen, NULL, NULL)) {
      vm_error("Failed to send 'test.rpcout.msg1' message.");
   }

   vm_free(cmd);

   vm_debug("Successfully handled rpc %s", data->name);
   return RPCIN_SETRETVALS(data, "", TRUE);
}


/**
 * Handles a "test.rpcin.msg2" RPC message.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE on success.
 */

static gboolean
TestPluginRpc2(RpcInData *data)
{
   vm_debug("%s", data->name);
   return RPCIN_SETRETVALS(data, "", TRUE);
}


/**
 * Handles a "test.rpcin.msg3" RPC message. Logs information to the
 * output and returns data to be serialized using XDR.
 *
 * @param[in]  data     RPC data.
 *
 * @return TRUE on success.
 */

static gboolean
TestPluginRpc3(RpcInData *data)
{
   TestPluginData *ret;
   vm_debug("%s", data->name);

   ret = g_malloc(sizeof *ret);
   ret->data = Util_SafeStrdup("Hello World!");
   ret->f_int = 8642;
   ret->f_bool = TRUE;

   data->result = (char *) ret;
   return TRUE;
}


/**
 * Called by the service core when the host requests the capabilities supported
 * by the guest tools.
 *
 * @param[in]  src      Unused.
 * @param[in]  ctx      The app context.
 * @param[in]  set      Whether capabilities are being set or unset (unused).
 * @param[in]  plugin   Plugin registration data.
 *
 * @return A list of capabilities to be sent to the host.
 */

static GArray *
TestPluginCapabilities(gpointer src,
                       ToolsAppCtx *ctx,
                       gboolean set,
                       ToolsPluginData *plugin)
{
   ToolsAppCapability caps[] = {
      { TOOLS_CAP_OLD, "resolution_set", 0, 1 },
      { TOOLS_CAP_OLD, "display_topology_set", 0, 2 },
      { TOOLS_CAP_NEW, NULL, UNITY_CAP_START_MENU, 1 },
      { TOOLS_CAP_NEW, NULL, GHI_CAP_SHELL_ACTION_BROWSE, 1 }
   };

   vm_debug("got capability signal, setting = %d.", set);
   return VMTools_WrapArray(caps, sizeof *caps, ARRAYSIZE(caps));
}


/**
 * Handles a reset signal; just logs debug information. This callback is
 * called when the service receives a "reset" message from the VMX, meaning
 * the VMX may be restarting the RPC channel (due, for example, to restoring
 * a snapshot, resuming a VM or a VMotion), and should be used to reset any
 * application state that depends on the VMX.
 *
 * @param[in]  src      Event source.
 * @param[in]  ctx      The app context.
 * @param[in]  plugin   Plugin registration data.
 *
 * @return TRUE on success.
 */

static gboolean
TestPluginReset(gpointer src,
                ToolsAppCtx *ctx,
                ToolsPluginData *plugin)
{
   RPCDEBUG_ASSERT(ctx != NULL, FALSE);
   vm_debug("reset signal for app %s", ctx->name);
   return TRUE;
}


#if defined(G_PLATFORM_WIN32)
/**
 * Handles a service control signal; this is only called on Windows.
 *
 * @param[in] src                    The source object.
 * @param[in] ctx                    The application context.
 * @param[in] serviceStatusHandle    Handle of type SERVICE_STATUS_HANDLE.
 * @param[in] controlCode            Control code.
 * @param[in] eventType              Unused.
 * @param[in] eventData              Unused.
 * @param[in] data                   Unused.
 *
 * @retval ERROR_CALL_NOT_IMPLEMENTED
 */

static DWORD
TestPluginServiceControl(gpointer src,
                         ToolsAppCtx *ctx,
                         gpointer serviceStatusHandle,
                         guint controlCode,
                         guint eventType,
                         gpointer eventData,
                         gpointer data)
{
   vm_debug("Got service control signal, code = %u, event = %u",
            controlCode, eventType);
   return ERROR_CALL_NOT_IMPLEMENTED;
}
#endif


/**
 * Handles a shutdown callback; just logs debug information. This is called
 * before the service is shut down, and should be used to clean up any resources
 * that were initialized by the application.
 *
 * @param[in]  src      The source object.
 * @param[in]  ctx      The app context.
 * @param[in]  plugin   Plugin registration data.
 */

static void
TestPluginShutdown(gpointer src,
                   ToolsAppCtx *ctx,
                   ToolsPluginData *plugin)
{
   vm_debug("shutdown signal.");
   CU_ASSERT(gInvalidSigError);
   CU_ASSERT(gInvalidAppError);
   CU_ASSERT(gInvalidAppProvider);
   CU_ASSERT(gValidAppRegistration);
}


/**
 * Handles a "Set_Option" callback. Just logs debug information. This callback
 * is called when the VMX sends a "Set_Option" command to tools, to configure
 * different options whose values are kept outside of the virtual machine.
 *
 * @param[in]  src      Event source.
 * @param[in]  ctx      The app context.
 * @param[in]  plugin   Plugin registration data.
 * @param[in]  option   Option being set.
 * @param[in]  value    Option value.
 *
 * @return TRUE on success.
 */

static gboolean
TestPluginSetOption(gpointer src,
                    ToolsAppCtx *ctx,
                    const gchar *option,
                    const gchar *value,
                    ToolsPluginData *plugin)
{
   vm_debug("set '%s' to '%s'", option, value);
   return TRUE;
}


/**
 * Prints out the registration data for the test provider.
 *
 * @param[in] ctx     Unused.
 * @param[in] prov    Unused.
 * @param[in] plugin  Unused.
 * @param[in] reg     Registration data (should be a string).
 *
 * @retval FALSE if registration value is TEST_APP_ERROR.
 * @retval TRUE otherwise.
 */

static gboolean
TestProviderRegisterApp(ToolsAppCtx *ctx,
                        ToolsAppProvider *prov,
                        ToolsPluginData *plugin,
                        gpointer reg)
{
   TestApp *app = reg;
   vm_debug("registration data is '%s'", app->name);
   gValidAppRegistration |= strcmp(app->name, TEST_APP_NAME) == 0;
   CU_ASSERT(strcmp(app->name, TEST_APP_DONT_REGISTER) != 0);
   return (strcmp(app->name, TEST_APP_ERROR) != 0);
}


/**
 * Registration error callback; make sure it's called for the errors we expect.
 *
 * @see plugin.h (for parameter descriptions)
 *
 * @retval FALSE for TEST_APP_ERROR.
 * @retval TRUE otherwise.
 */

static gboolean
TestPluginErrorCb(ToolsAppCtx *ctx,
                  ToolsAppType type,
                  gpointer data,
                  ToolsPluginData *plugin)
{
   /* Make sure the non-existant signal we tried to register fires an error. */
   if (type == TOOLS_APP_SIGNALS) {
      ToolsPluginSignalCb *sig = data;
      CU_ASSERT(strcmp(sig->signame, TEST_SIG_INVALID) == 0);
      gInvalidSigError = TRUE;
   }

   /* Make sure we're notified about the "error" app we tried to register. */
   if (type == 42) {
      TestApp *app = data;
      CU_ASSERT(strcmp(app->name, TEST_APP_ERROR) == 0);
      gInvalidAppError = TRUE;
      return FALSE;
   }

   /* Make sure we're notified about a non-existant app provider. */
   if (type == 43) {
      CU_ASSERT(data == NULL);
      gInvalidAppProvider = TRUE;
   }

   return TRUE;
}


/**
 * Plugin entry point. Returns the registration data. This is called once when
 * the plugin is loaded into the service process.
 *
 * @param[in]  ctx   The app context.
 *
 * @return The registration data.
 */

TOOLS_MODULE_EXPORT ToolsPluginData *
ToolsOnLoad(ToolsAppCtx *ctx)
{
   static ToolsPluginData regData = {
      "testPlugin",
      NULL,
      TestPluginErrorCb,
      NULL
   };

   RpcChannelCallback rpcs[] = {
      { "test.rpcin.msg1",
         TestPluginRpc1, NULL, xdr_TestPluginData, NULL,
         sizeof (TestPluginData) },
      { "test.rpcin.msg2",
         TestPluginRpc2, NULL, NULL, NULL, 0 },
      { "test.rpcin.msg3",
            TestPluginRpc3, NULL, NULL, xdr_TestPluginData, 0 }
   };
   ToolsAppProvider provs[] = {
      { TEST_APP_PROVIDER, 42, sizeof (char *), NULL, TestProviderRegisterApp, NULL, NULL }
   };
   ToolsPluginSignalCb sigs[] = {
      { TOOLS_CORE_SIG_RESET, TestPluginReset, &regData },
      { TOOLS_CORE_SIG_SHUTDOWN, TestPluginShutdown, &regData },
      { TOOLS_CORE_SIG_CAPABILITIES, TestPluginCapabilities, &regData },
      { TOOLS_CORE_SIG_SET_OPTION, TestPluginSetOption, &regData },
#if defined(G_PLATFORM_WIN32)
      { TOOLS_CORE_SIG_SERVICE_CONTROL, TestPluginServiceControl, &regData },
#endif
      { TEST_SIG_INVALID, TestPluginReset, &regData },
   };
   TestApp tapp[] = {
      { TEST_APP_NAME },
      { TEST_APP_ERROR },
      { TEST_APP_DONT_REGISTER }
   };
   TestApp tnoprov[] = {
      { "TestAppNoProvider" }
   };
   ToolsAppReg regs[] = {
      { TOOLS_APP_GUESTRPC, VMTOOLS_WRAP_ARRAY(rpcs) },
      { TOOLS_APP_PROVIDER, VMTOOLS_WRAP_ARRAY(provs) },
      { TOOLS_APP_SIGNALS,  VMTOOLS_WRAP_ARRAY(sigs) },
      { 42,                 VMTOOLS_WRAP_ARRAY(tapp) },
      { 43,                 VMTOOLS_WRAP_ARRAY(tnoprov) },
   };

   vm_info("loading test plugin...");

   g_signal_new("test-signal",
                G_OBJECT_TYPE(ctx->serviceObj),
                0,
                0,
                NULL,
                NULL,
                g_cclosure_marshal_VOID__VOID,
                G_TYPE_NONE,
                0);

   regData.regs = VMTOOLS_WRAP_ARRAY(regs);
   return &regData;
}

  070701000003DA000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/tests/testVmblock  070701000003DB000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/tests/testVmblock/COPYING  		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000003DC000081A400000000000000000000000168225505000005DB000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/tests/testVmblock/Makefile.am  ################################################################################
### Copyright (c) 2009-2016,2020-2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

  noinst_PROGRAMS =
if HAVE_FUSE
  noinst_PROGRAMS += vmware-testvmblock-fuse
  noinst_PROGRAMS += vmware-testvmblock-manual-fuse
endif
if HAVE_FUSE3
  noinst_PROGRAMS += vmware-testvmblock-fuse
  noinst_PROGRAMS += vmware-testvmblock-manual-fuse
endif

AM_CFLAGS =
AM_CFLAGS += -DVMX86_DEVEL
AM_CFLAGS += -DVMX86_DEBUG

AM_LDFLAGS =
AM_LDFLAGS += -lpthread

vmware_testvmblock_fuse_CFLAGS = $(AM_CFLAGS) -Dvmblock_fuse
vmware_testvmblock_fuse_SOURCES = vmblocktest.c

vmware_testvmblock_manual_fuse_CFLAGS = $(AM_CFLAGS) -Dvmblock_fuse
vmware_testvmblock_manual_fuse_SOURCES = manual-blocker.c
 070701000003DD000081A40000000000000000000000016822550500000B0F000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/tests/testVmblock/manual-blocker.c /*********************************************************
 * Copyright (C) 2008-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * manual-blocker.c --
 *
 *      A small test program for manually manipulating vmblock.
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <string.h>

#include "vmblock_user.h"


/*
 *-----------------------------------------------------------------------------
 *
 * main --
 *
 *      Takes the target file to block as a command line arg. Sits in a loop
 *      waiting for commands.
 *
 * Results:
 *      Returns 0 on success and nonzero on failure.
 *
 * Side effects:
 *      None/all.
 *
 *-----------------------------------------------------------------------------
 */

int
main(int argc,
     char *argv[])
{
   int status;

   if (argc < 2 ||
       strcmp(argv[1], "-h") == 0 ||
       strcmp(argv[1], "--help") == 0) {
      printf("Usage: %s <path>\n", argv[0]);
      puts("a to Add a block, d to Delete a block, or l to List blocks (to"
           " vmblock's log).\n");
      return 1;
   }

   int fd = open(VMBLOCK_DEVICE, VMBLOCK_DEVICE_MODE);
   if (fd <= 0) {
      perror("open");
      return 2;
   }
   printf("Opened " VMBLOCK_DEVICE " as fd %d.\n", fd);

   while (1) {
      char op = getchar();

      if (op == 'a') {
         status = VMBLOCK_CONTROL(fd, VMBLOCK_ADD_FILEBLOCK, argv[1]);
         if (status != 0) {
            perror(NULL);
         } else {
            printf("%s blocked.\n", argv[1]);
         }
      } else if (op == 'd') {
         status = VMBLOCK_CONTROL(fd, VMBLOCK_DEL_FILEBLOCK, argv[1]);
         if (status != 0) {
            perror(NULL);
         } else {
            printf("%s unblocked.\n", argv[1]);
         }
      } else if (op == 'l') {
         status = VMBLOCK_CONTROL(fd, VMBLOCK_LIST_FILEBLOCKS, argv[1]);
         if (status != 0) {
            perror(NULL);
         } else {
            printf("Check vmblock's log for list of blocks.\n");
         }
      }
   }

   return 0;
}
 070701000003DE000081A4000000000000000000000001682255050000491D000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/tests/testVmblock/vmblocktest.c    /*********************************************************
 * Copyright (C) 2006-2017,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmblocktest.c --
 *
 *   Test program for the vmblock file system.  Ensures correctness and
 *   stability.
 *
 */

#if !defined(__linux__) && !defined(sun) && !defined(__FreeBSD__) && !defined(vmblock_fuse)
# error "vmblocktest.c needs to be ported to your OS."
#endif

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#ifdef sun
# include <stropts.h>
#endif

#include "vmblock_user.h"
#include "vm_basic_types.h"

#define CONTROLFILE               VMBLOCK_DEVICE
#define CONTROLFILE_MODE          VMBLOCK_DEVICE_MODE
#define REALROOT                   "/tmp/VMwareDnD/"
#define FILENAME                   "/foo"
#define ACCESSORFULLNAME(dir)      VMBLOCK_FS_ROOT "/" dir FILENAME
#define BLOCKERFULLNAME(dir)       REALROOT dir

#define lprintf(...)                                                    \
   do {                                                                 \
      pthread_mutex_lock(&print_lock);                                  \
      printf(__VA_ARGS__);                                              \
      fflush(stdout);                                                   \
      pthread_mutex_unlock(&print_lock);                                \
   } while(0)

#define lfprintf(stream, ...)                                           \
   do {                                                                 \
      pthread_mutex_lock(&print_lock);                                  \
      fprintf(stream, __VA_ARGS__);                                     \
      fflush(stream);                                                   \
      pthread_mutex_unlock(&print_lock);                                \
   } while(0)

#define Log(fmt, args...)          lprintf(fmt, ## args)
#define ERROR(fmt, args...)        lfprintf(stderr, fmt, ## args)
#define THREAD_LOG(fmt, args...)   lprintf(" (%lx) " fmt, (unsigned long)pthread_self(), ## args)
#define THREAD_ERROR(fmt, args...) lfprintf(stderr, " (%"FMTPID") " fmt, getpid(), ## args)

#if defined (__linux__) || defined(__FreeBSD__)
# define os_thread_yield()      sched_yield()
#elif defined(sun)
# define os_thread_yield()      yield()
#endif

/*
 * This program may optionally throttle accessor thread generation
 * via POSIX semaphores.
 */
#if defined(USE_SEMAPHORES)
sem_t sem;
# define SEM_THREADS    (unsigned int)10
# define SEM_INIT()     sem_init(&sem, 0, SEM_THREADS)
# define SEM_DESTROY()  sem_destroy(&sem)
# define SEM_WAIT()     sem_wait(&sem)
# define SEM_POST()     sem_post(&sem)
# define PTHREAD_SEMAPHORE_CLEANUP()    pthread_cleanup_push(sem_post,  \
                                                             (void *)&sem)
#else
# define SEM_INIT()
# define SEM_DESTROY()
# define SEM_WAIT()
# define SEM_POST()
# define PTHREAD_SEMAPHORE_CLEANUP()
#endif

/* Types */
typedef struct FileState {
   /* Accessors will access the file through the vmblock namespace */
   char *accessorName;
   /* The blocker will add blocks using the real file's name */
   char *blockerName;
   Bool blocked;
   unsigned int waiters;
} FileState;

typedef struct ThreadInfo {
   int blockFd;
   pthread_mutex_t *lock;
   FileState *files;
   size_t filesArraySize;
   unsigned int sleepTime;
} ThreadInfo;

/* Variables */
static Bool programQuit = FALSE;
static FileState files[] = {
   { ACCESSORFULLNAME("0"), BLOCKERFULLNAME("0"), FALSE, 0 },
   { ACCESSORFULLNAME("1"), BLOCKERFULLNAME("1"), FALSE, 0 },
   { ACCESSORFULLNAME("2"), BLOCKERFULLNAME("2"), FALSE, 0 },
   { ACCESSORFULLNAME("3"), BLOCKERFULLNAME("3"), FALSE, 0 },
   { ACCESSORFULLNAME("4"), BLOCKERFULLNAME("4"), FALSE, 0 },
   { ACCESSORFULLNAME("5"), BLOCKERFULLNAME("5"), FALSE, 0 },
   { ACCESSORFULLNAME("6"), BLOCKERFULLNAME("6"), FALSE, 0 },
   { ACCESSORFULLNAME("7"), BLOCKERFULLNAME("7"), FALSE, 0 },
   { ACCESSORFULLNAME("8"), BLOCKERFULLNAME("8"), FALSE, 0 },
   { ACCESSORFULLNAME("9"), BLOCKERFULLNAME("9"), FALSE, 0 },
};

/* Thread entry points */
static void *blocker(void *arg);
static void *accessor(void *arg);

/* Utility functions */
static Bool addBlock(int fd, char const *filename);
static Bool delBlock(int fd, char const *filename);
static Bool listBlocks(int fd);
#ifdef VMBLOCK_PURGE_FILEBLOCKS
static Bool purgeBlocks(int fd);
#endif
static unsigned int getRand(unsigned int max);
static void sighandler(int sig);

pthread_mutex_t print_lock;

/*
 *----------------------------------------------------------------------------
 *
 * main --
 *
 *    Does all necessary setup then starts the blocker thread and continually
 *    starts accessor threads.
 *
 * Results:
 *    EXIT_SUCCESS and EXIT_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
main(int argc,
     char *argv[])
{
   int ret = EXIT_SUCCESS;
   int i;
   int blockFd;
   char *progname;
   pthread_t blockerThread;
   pthread_mutex_t filesLock;
   pthread_attr_t attr;
   ThreadInfo info;
   int count;

   progname = basename(argv[0]);
   pthread_mutex_init(&filesLock, NULL);

   SEM_INIT();
   pthread_mutex_init(&print_lock, NULL);

   /* Open device user to add/delete blocks */
   blockFd = open(CONTROLFILE, CONTROLFILE_MODE);
   if (blockFd < 0) {
      ERROR("%s: could not open " CONTROLFILE "\n", progname);
      exit(EXIT_FAILURE);
   }

   /* Provide ability to just list blocks */
   if (argc > 1) {
      if (strcmp(argv[1], "-list") == 0) {
         listBlocks(blockFd);
#ifdef VMBLOCK_PURGE_FILEBLOCKS
      } else if (strcmp(argv[1], "-purge") == 0) {
         purgeBlocks(blockFd);
#endif
      }
      close(blockFd);
      exit(EXIT_SUCCESS);
   }

   /* Create directories/files used during test */
   for (i = 0; i < sizeof files/sizeof files[0]; i++) {
      int err;
      struct stat statbuf;
      char buf[PATH_MAX];

      err = stat(files[i].blockerName, &statbuf);
      if (!err) {
         if (S_ISDIR(statbuf.st_mode)) {
            goto create_file;
         }

         ERROR("%s: file [%s] already exists and is not a directory\n",
               progname, files[i].blockerName);
         goto exit_failure;
      }

      err = mkdir(files[i].blockerName, S_IRWXU | S_IRWXG);
      if (err) {
         ERROR("%s: could not create [%s]\n", progname, files[i].blockerName);
         goto exit_failure;
      }

create_file:
      strncpy(buf, files[i].blockerName, sizeof buf - 1);
      buf[sizeof buf - 1] = '\0';
      strncat(buf, FILENAME, sizeof buf - strlen(files[i].blockerName));
      err = stat(buf, &statbuf);
      if (!err) {
         if (S_ISREG(statbuf.st_mode)) {
            continue;
         }

         ERROR("%s: file [%s] already exists and is not a regular file\n",
               progname, buf);
         goto exit_failure;
      }

      err = creat(buf, S_IRUSR | S_IRGRP);
      if (err < 0) {
         ERROR("%s: could not create [%s]\n", progname, buf);
         goto exit_failure;
      }

      continue;

exit_failure:
      close(blockFd);
      exit(EXIT_FAILURE);
   }

   if (signal(SIGINT, sighandler) == SIG_ERR ||
       signal(SIGTERM, sighandler) == SIG_ERR) {
      ERROR("%s: could not install signal handlers\n", progname);
      close(blockFd);
      exit(EXIT_FAILURE);
   }

   /* Seems cleaner than a bunch of globals ... */
   info.blockFd = blockFd;
   info.lock = &filesLock;
   info.files = files;
   info.filesArraySize = sizeof files/sizeof files[0];
   info.sleepTime = 1;

   /*
    * Start a thread that flips a random file's state, then sleeps for a while
    * and does it again.
    */
   if (pthread_create(&blockerThread, NULL, blocker, &info)) {
      ERROR("%s: could not create blocker thread\n", progname);
      close(blockFd);
      exit(EXIT_FAILURE);
   }

   /*
    * Start a bunch of threads that try to open a random file, check its status
    * once they have it open (to make sure it is not blocked), then close it
    * and exit.
    */
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   count = 0;
   while (!programQuit) {
      pthread_t thread;
      int rv;
      SEM_WAIT();
      /* Create threads until we cannot anymore */
      if ((rv = pthread_create(&thread, &attr, accessor, &info)) != 0) {
         SEM_POST();
         if (rv == EAGAIN || rv == ENOMEM) {
            os_thread_yield();
            continue;
         }
         ERROR("%s: could not create an accessor thread (%d total)\n",
               progname, count);
         ERROR("%s: pthread_create: %s\n", progname, strerror(rv));
         ret = EXIT_FAILURE;
         break;
      }
      count++;
   }

   Log("%s: Not creating any more accessor threads.\n", progname);

   programQuit = TRUE;
   pthread_join(blockerThread, NULL);

   pthread_mutex_destroy(&filesLock);
   close(blockFd);

   Log("%s: Exiting with %s.\n",
       progname, ret == EXIT_SUCCESS ? "success" : "failure");

   exit(ret);
}


/*
 *----------------------------------------------------------------------------
 *
 * blocker --
 *
 *    Entry point for the single blocker thread.  Continuously picks a file at
 *    random and changes its state by adding or deleting a block on that file.
 *
 * Results:
 *    EXIT_SUCCESS and EXIT_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void *
blocker(void *arg)  // IN
{
   ThreadInfo *info = (ThreadInfo *)arg;
   unsigned int index;

   if (!info) {
      THREAD_ERROR("blocker: no thread info provided\n");
      pthread_exit((void *)EXIT_FAILURE);
   }

   while (!programQuit) {
      index = getRand(info->filesArraySize - 1);

      pthread_mutex_lock(info->lock);

      if (info->files[index].blocked) {
         info->files[index].blocked = FALSE;
         if ( !delBlock(info->blockFd, info->files[index].blockerName) ) {
            THREAD_ERROR("blocker: could not delete block on [%s]\n",
                         info->files[index].blockerName);
            goto error;
         }
      } else if (info->files[index].waiters == 0) {
         /*
          * Only add a new block if all previous waiters are done.  This
          * ensures we don't get incorrect error messages from accessor threads
          * even though the open(2) and check are not atomic in the accessor.
          */
         info->files[index].blocked = TRUE;
         if ( !addBlock(info->blockFd, info->files[index].blockerName) ) {
            THREAD_ERROR("blocker: could not add block on [%s]\n",
                         info->files[index].blockerName);
            goto error;
         }
      }

      pthread_mutex_unlock(info->lock);

      sleep(info->sleepTime);
   }

   pthread_mutex_lock(info->lock);
   for (index = 0; index < info->filesArraySize; index++) {
      if (info->files[index].blocked) {
         info->files[index].blocked = FALSE;
#ifndef TEST_CLOSE_FD
         THREAD_LOG("blocker: deleting block for [%s]\n",
                    info->files[index].blockerName);
         if ( !delBlock(info->blockFd, info->files[index].blockerName) ) {
            THREAD_ERROR("blocker: could not delete existing block on exit for [%s]\n",
                         info->files[index].blockerName);
         }
#else
         THREAD_LOG("blocker: unmarking block for [%s], left for unblock on release\n",
                    info->files[index].blockerName);
#endif
      }
   }
   pthread_mutex_unlock(info->lock);

   pthread_exit(EXIT_SUCCESS);

error:
   programQuit = TRUE;
   pthread_mutex_unlock(info->lock);
   pthread_exit((void *)EXIT_FAILURE);
}


/*
 *----------------------------------------------------------------------------
 *
 * accessor --
 *
 *    Entry point for accessor threads.  Picks a file at random and attempts to
 *    open it.  Once it is opened, ensures the state of the file is not blocked
 *    and outputs an error message accordingly.
 *
 * Results:
 *    EXIT_SUCCESS and EXIT_FAILURE.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static void *
accessor(void *arg)  // IN
{
   ThreadInfo *info = (ThreadInfo *)arg;
   unsigned int index;
   int fd;
   uintptr_t ret = EXIT_SUCCESS;

   PTHREAD_SEMAPHORE_CLEANUP();

   if (!info) {
      THREAD_ERROR("blocker: no thread info provided\n");
      pthread_exit((void *)EXIT_FAILURE);
   }

   index = getRand(info->filesArraySize - 1);

   /*
    * We can't hold the lock while calling open(2), since we will deadlock
    * waiting for the blocker thread to remove the block on the file.  So, we
    * bump the waiter count to ensure that a new block is not placed on this
    * file until we have checked its state.  This prevents incorrect error
    * messages that would happen if the blocker placed a new block on the file
    * between our open(2) call returning and acquiring the lock.
    *
    * The fact that we can't hold the lock through open(2) also means that it's
    * not atomic with respect to checking the file's blocked flag.  Given this,
    * it's possible that we'll miss some errors if the block is removed after
    * open(2) returns but before we check the blocked flag -- hopefully running
    * this test for a very long time will be sufficient to catch these cases.
    * (Having the blocker sleep increases the likelihood of seeing such
    * errors.)
    */
   pthread_mutex_lock(info->lock);
   info->files[index].waiters++;
   pthread_mutex_unlock(info->lock);

   fd = open(info->files[index].accessorName, O_RDONLY);
   if (fd < 0) {
      if (errno == EMFILE) {
         /* We already have hit the maximum number of file descriptors */
         pthread_mutex_lock(info->lock);
         info->files[index].waiters--;
         pthread_mutex_unlock(info->lock);
         pthread_exit((void *)EXIT_FAILURE);
      }
      THREAD_ERROR("accessor: could not open file [%s]\n",
                   info->files[index].accessorName);
      THREAD_ERROR("accessor: open: %s\n", strerror(errno));
      pthread_exit((void *)EXIT_FAILURE);
   }

   pthread_mutex_lock(info->lock);
   info->files[index].waiters--;

   if (info->files[index].blocked) {
      THREAD_ERROR("accessor: [ERROR] accessed file [%s] while blocked (%d)\n",
                   info->files[index].accessorName, info->files[index].blocked);
      ret = EXIT_FAILURE;
   }
   pthread_mutex_unlock(info->lock);
   close(fd);

   pthread_exit((void *)ret);
}


/*
 *----------------------------------------------------------------------------
 *
 * addBlock --
 *
 *    Adds a block on the specified filename.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    Future open(2)s on the file will block until delBlock() is called for
 *    that file.
 *
 *----------------------------------------------------------------------------
 */

static Bool
addBlock(int fd,                // IN: fd of control device
         char const *filename)  // IN: filename to add block for
{
   Log("Blocking [%s]\n", filename);
   return VMBLOCK_CONTROL(fd, VMBLOCK_ADD_FILEBLOCK, filename) == 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * delBlock --
 *
 *    Deletes a block on the specified filename.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    Previous open(2)s that had blocked will now complete.
 *
 *----------------------------------------------------------------------------
 */

static Bool
delBlock(int fd,                // IN: fd of control device
         char const *filename)  // IN: filename to delete block for
{
   Log("Unblocking [%s]\n", filename);
   return VMBLOCK_CONTROL(fd, VMBLOCK_DEL_FILEBLOCK, filename) == 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * listBlocks --
 *
 *    Tells the kernel module to list all existing blocks.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
listBlocks(int fd)      // IN: fd of control device
{
   Log("Listing blocks (check kernel log output)\n");
   return VMBLOCK_CONTROL(fd, VMBLOCK_LIST_FILEBLOCKS, NULL) == 0;
}


#ifdef VMBLOCK_PURGE_FILEBLOCKS
/*
 *----------------------------------------------------------------------------
 *
 * purgeBlocks --
 *
 *    Tells the kernel module to purge all existing blocks, regardless of
 *    who opened them.
 *
 * Results:
 *    TRUE on success, FALSE on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
purgeBlocks(int fd)      // IN: fd of control device
{
   Log("Purging all blocks\n");
   return VMBLOCK_CONTROL(fd, VMBLOCK_PURGE_FILEBLOCKS, NULL) == 0;
}
#endif


/*
 *----------------------------------------------------------------------------
 *
 * getRand --
 *
 *    Retrieves the next random number within the range [0, max].
 *
 * Results:
 *    A random number.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static unsigned int
getRand(unsigned int max)   // IN: max value returnable
{
   return random() % max;
}


/*
 *----------------------------------------------------------------------------
 *
 * sighandler --
 *
 *    Sets the programQuit flag when a signal is received.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    Program will quit.
 *
 *----------------------------------------------------------------------------
 */

static void
sighandler(int sig)
{
   programQuit = TRUE;
}
   070701000003DF000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/tests/vmrpcdbg 070701000003E0000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/tests/vmrpcdbg/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000003E1000081A40000000000000000000000016822550500000610000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/tests/vmrpcdbg/Makefile.am ################################################################################
### Copyright (c) 2009-2016, 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

noinst_LTLIBRARIES = libvmrpcdbg.la

libvmrpcdbg_la_CPPFLAGS =
libvmrpcdbg_la_CPPFLAGS += @CUNIT_CPPFLAGS@
libvmrpcdbg_la_CPPFLAGS += @GMODULE_CPPFLAGS@
libvmrpcdbg_la_CPPFLAGS += @VMTOOLS_CPPFLAGS@
libvmrpcdbg_la_CPPFLAGS += @XDR_CPPFLAGS@
libvmrpcdbg_la_CPPFLAGS += -I$(top_srcdir)/lib/rpcChannel

libvmrpcdbg_la_LDFLAGS =
libvmrpcdbg_la_LDFLAGS += @PLUGIN_LDFLAGS@

libvmrpcdbg_la_LIBADD =
libvmrpcdbg_la_LIBADD += @CUNIT_LIBS@
libvmrpcdbg_la_LIBADD += @GMODULE_LIBS@
libvmrpcdbg_la_LIBADD += @VMTOOLS_LIBS@
libvmrpcdbg_la_LIBADD += @XDR_LIBS@

libvmrpcdbg_la_SOURCES =
libvmrpcdbg_la_SOURCES += debugChannel.c
libvmrpcdbg_la_SOURCES += vmrpcdbg.c

070701000003E2000081A40000000000000000000000016822550500002320000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/tests/vmrpcdbg/debugChannel.c  /*********************************************************
 * Copyright (C) 2008-2016,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file debugChannel.c
 *
 * Implements an RPC Channel that is backed by a "debug plugin". The plugin
 * provides information about what data should be "read" by the RPC Channel,
 * and sinks for the data the application writes to the channel, so that the
 * plugin can perform validation.
 */

#define G_LOG_DOMAIN "rpcdbg"

#include <gmodule.h>

#include "strutil.h"
#include "util.h"
#include "vmrpcdbgInt.h"
#include "rpcChannelInt.h"
#include "vmxrpc.h"
#include "xdrutil.h"
#include "vmware/tools/utils.h"

typedef struct DbgChannelData {
   ToolsAppCtx      *ctx;
   gboolean          hasLibRef;
   RpcDebugPlugin   *plugin;
   GSource          *msgTimer;
} DbgChannelData;

/**
 * Reads one RPC from the plugin and dispatches it to the application.
 * This function will ask the service process to stop running if a
 * failure occurs, where failure is defined either by the validation
 * function returning FALSE, or, if no validation function is provided,
 * the application's callback returning FALSE.
 *
 * @param[in]  _chan    The RPC channel instance.
 *
 * @return TRUE if the callback should continue to be scheduled.
 */

static gboolean
RpcDebugDispatch(gpointer _chan)
{
   gboolean ret;
   RpcChannel *chan = _chan;
   DbgChannelData *cdata = chan->_private;
   RpcDebugPlugin *plugin = cdata->plugin;
   RpcInData data;
   RpcDebugMsgMapping rpcdata;

   memset(&data, 0, sizeof data);
   memset(&rpcdata, 0, sizeof rpcdata);

   if (plugin->sendFn == NULL || !plugin->sendFn(&rpcdata)) {
      RpcDebug_DecRef(cdata->ctx);
      cdata->hasLibRef = FALSE;
      return FALSE;
   } else if (rpcdata.message == NULL) {
      /*
       * Nothing to send. Maybe the debug plugin is waiting for something to
       * happen before sending another message.
       */
      return TRUE;
   }

   data.clientData = chan;
   data.appCtx = cdata->ctx;
   data.args = rpcdata.message;
   data.argsSize = rpcdata.messageLen;

   ret = RpcChannel_Dispatch(&data);
   if (rpcdata.validateFn != NULL) {
      ret = rpcdata.validateFn(&data, ret);
   } else if (!ret) {
      g_debug("RpcChannel_Dispatch returned error for RPC.\n");
   }

   if (data.freeResult) {
      vm_free(data.result);
   }

   if (rpcdata.freeMsg) {
      vm_free(rpcdata.message);
   }

   if (!ret) {
      VMTOOLSAPP_ERROR(cdata->ctx, 1);
      RpcDebug_DecRef(cdata->ctx);
      cdata->hasLibRef = FALSE;
      return FALSE;
   }
   return TRUE;
}


/**
 * Starts sending data to the service. The function will send one RPC
 * approximately every 100ms, to somewhat mimic the behavior of the
 * backdoor-based channel.
 *
 * @param[in]  chan     The RPC channel instance.
 *
 * @return TRUE.
 */

static gboolean
RpcDebugStart(RpcChannel *chan)
{
   DbgChannelData *data = chan->_private;

   ASSERT(data->ctx != NULL);
   ASSERT(data->msgTimer == NULL);

   data->msgTimer = g_timeout_source_new(100);
   VMTOOLSAPP_ATTACH_SOURCE(data->ctx,
                            data->msgTimer,
                            RpcDebugDispatch,
                            chan,
                            NULL);
   return TRUE;
}


/**
 * Stops the channel; cancel the timer that sends polls the debug plugin for
 * messages to send.
 *
 * @param[in]  chan  Unused.
 */

static void
RpcDebugStop(RpcChannel *chan)
{
   DbgChannelData *data = chan->_private;
   if (data->msgTimer != NULL) {
      g_source_unref(data->msgTimer);
      data->msgTimer = NULL;
   }
}


/**
 * Sends the given data to the plugin. This function tries to parse the
 * incoming data based on the "cmd args" convention, and call the "receive"
 * function provided by the debug plugin.
 *
 * @param[in]  chan        The RPC channel instance.
 * @param[in]  data        Data to send.
 * @param[in]  dataLen     Number of bytes to send.
 * @param[out] rpcStatus   Status of RPC command from other side.
 * @param[out] result      Response from other side.
 * @param[out] resultLen   Number of bytes in response.
 *
 * @return The result from the plugin's validation function, or TRUE if
 *         a validation function was not provided.
 */

static gboolean
RpcDebugSend(RpcChannel *chan,
             char const *data,
             size_t dataLen,
             Bool *rpcStatus,
             char **result,
             size_t *resultLen)
{
   char *copy;
   gpointer xdrdata = NULL;
   DbgChannelData *cdata = chan->_private;
   RpcDebugPlugin *plugin = cdata->plugin;
   RpcDebugRecvMapping *mapping = NULL;
   RpcDebugRecvFn recvFn = NULL;
   gboolean ret = TRUE;

   ASSERT(cdata->ctx != NULL);

   /* Be paranoid. Like the VMX, NULL-terminate the incoming data. */
   copy = g_malloc(dataLen + 1);
   memcpy(copy, data, dataLen);
   copy[dataLen] = '\0';

   /* Try to find a mapping the the command in the RPC. */
   if (plugin->recvFns) {
      char *cmd;
      unsigned int idx = 0;

      cmd = StrUtil_GetNextToken(&idx, copy, " ");
      if (cmd != NULL) {
         RpcDebugRecvMapping *test;
         for (test = plugin->recvFns; test->name != NULL; test++) {
            if (strcmp(test->name, cmd) == 0) {
               mapping = test;
               break;
            }
         }
      }
      vm_free(cmd);
   }

   if (mapping != NULL) {
      recvFn = mapping->recvFn;
      if (mapping->xdrProc != NULL) {
         char *start;

         ASSERT(mapping->xdrSize > 0);

         /* Find out where the XDR data starts. */
         start = strchr(copy, ' ');
         if (start == NULL) {
            RpcDebug_SetResult("Can't find command delimiter.", result, resultLen);
            ret = FALSE;
            goto exit;
         }
         start++;

         xdrdata = g_malloc0(mapping->xdrSize);
         if (!XdrUtil_Deserialize(start,
                                  dataLen - (start - copy),
                                  mapping->xdrProc,
                                  xdrdata)) {
            RpcDebug_SetResult("XDR deserialization failed.", result, resultLen);
            ret = FALSE;
            goto exit;
         }
      }
   } else {
      recvFn = plugin->dfltRecvFn;
   }

   if (recvFn != NULL) {
      ret = recvFn((xdrdata != NULL) ? xdrdata : copy, dataLen, result, resultLen);
   } else {
      RpcDebug_SetResult("", result, resultLen);
   }

exit:
   if (xdrdata != NULL) {
      VMX_XDR_FREE(mapping->xdrProc, xdrdata);
      g_free(xdrdata);
   }
   g_free(copy);
   /* For now, just make rpcStatus same as ret */
   *rpcStatus = ret;
   return ret;
}


/**
 * Intiializes internal state for the inbound channel.
 *
 * @param[in]  chan     The RPC channel instance.
 * @param[in]  ctx      Unused.
 * @param[in]  appName  Unused.
 * @param[in]  appCtx   A ToolsAppCtx instance.
 */

static void
RpcDebugSetup(RpcChannel *chan,
              GMainContext *ctx,
              const char *appName,
              gpointer appCtx)
{
   DbgChannelData *cdata = chan->_private;
   cdata->ctx = appCtx;
}


/**
 * Cleans up the internal channel state.
 *
 * @param[in]  chan     The RPC channel instance.
 */

static void
RpcDebugShutdown(RpcChannel *chan)
{
   DbgChannelData *cdata = chan->_private;
   ASSERT(cdata->ctx != NULL);
   if (cdata->hasLibRef) {
      RpcDebug_DecRef(cdata->ctx);
   }
   g_free(chan->_private);
}


/**
 * Instantiates a new RPC Debug Channel. This function will load and initialize
 * the given debug plugin.
 *
 * This function will panic is something wrong happens while loading the plugin.
 *
 * @param[in]  ctx         The application context.
 * @param[in]  data        Debug library data.
 *
 * @return A new channel.
 */

RpcChannel *
RpcDebug_NewDebugChannel(ToolsAppCtx *ctx,
                         RpcDebugLibData *data)
{
   DbgChannelData *cdata;
   RpcChannel *ret;
   static RpcChannelFuncs funcs = {
      RpcDebugStart,
      RpcDebugStop,
      RpcDebugSend,
      RpcDebugSetup,
      RpcDebugShutdown,
      NULL,
      NULL
   };

   ASSERT(data != NULL);
   ret = RpcChannel_Create();
   ret->funcs = &funcs;

   cdata = g_malloc0(sizeof *cdata);
   cdata->plugin = data->debugPlugin;
   ret->_private = cdata;

   RpcDebug_IncRef();
   return ret;
}

070701000003E3000081A40000000000000000000000016822550500001CE3000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/tests/vmrpcdbg/vmrpcdbg.c  /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file vmrpcdbg.c
 *
 * Implementation of the library functions not related to the RPC channel.
 */

#define G_LOG_DOMAIN "rpcdbg"

#include <gmodule.h>
#include "CUnit/Basic.h"
#include <CUnit/CUnit.h>

#include "util.h"
#include "vmrpcdbgInt.h"

#include "vm_version.h"
#include "embed_version.h"
#include "vmtoolsd_version.h"
VM_EMBED_VERSION(VMTOOLSD_VERSION_STRING);

static GModule *gPlugin = NULL;

#if defined(_WIN64) && (_MSC_VER == 1500) && GLIB_CHECK_VERSION(2, 46, 0)
/*
 * Turn off optimizer for this compiler, since something with new glib makes it
 * go into an infinite loop, only on 64bit and only with beta.
 */
#pragma optimize("", off)
#endif


/*
 * Static variables to hold the app's main loop data. CUnit test functions
 * don't take any parameters so there's no other way to do this...
 */
static struct {
   void (*mainLoop)(gpointer);
   gpointer          loopData;
   RpcDebugLibData  *libData;
   ToolsAppCtx      *ctx;
   gint              refCount;
} gLibRunData;


/*
 ******************************************************************************
 * RpcDebugRunLoop --                                                   */ /**
 *
 * Runs the app's main loop as part of a CUnit test.
 *
 ******************************************************************************
 */

static void
RpcDebugRunLoop(void)
{
   ASSERT(gLibRunData.libData);
   ASSERT(gLibRunData.mainLoop);
   ASSERT(gLibRunData.loopData);
   gLibRunData.mainLoop(gLibRunData.loopData);

   if (gLibRunData.libData->debugPlugin->shutdownFn != NULL) {
      gLibRunData.libData->debugPlugin->shutdownFn(gLibRunData.ctx,
                                                   gLibRunData.libData->debugPlugin);
   }
}


/*
 ******************************************************************************
 * RpcDebugRun --                                                       */ /**
 *
 * Runs the main application's main loop function through CUnit so that we
 * get all the test tracking / reporting goodness that it provides.
 *
 * @param[in] runMainLoop     A function that runs the application's main loop.
 *                            The function should take one argument,
 * @param[in] runData         Argument to be passed to the main loop function.
 * @param[in] ldata           Debug library data.
 *
 * @return CUnit test run result (cast to int).
 *
 ******************************************************************************
 */

static int
RpcDebugRun(ToolsAppCtx *ctx,
            gpointer runMainLoop,
            gpointer runData,
            RpcDebugLibData *ldata)
{
   CU_ErrorCode err;
   CU_Suite *suite;
   CU_Test *test;

   ASSERT(runMainLoop != NULL);
   ASSERT(ldata != NULL);

   err = CU_initialize_registry();
   ASSERT(err == CUE_SUCCESS);

   suite = CU_add_suite(g_module_name(gPlugin), NULL, NULL);
   ASSERT(suite != NULL);

   test = CU_add_test(suite, g_module_name(gPlugin), RpcDebugRunLoop);
   ASSERT_NOT_IMPLEMENTED(test != NULL);

   gLibRunData.ctx = ctx;
   gLibRunData.libData = ldata;
   gLibRunData.mainLoop = runMainLoop;
   gLibRunData.loopData = runData;

   err = CU_basic_run_tests();

   /* Clean up internal library / debug plugin state. */
   ASSERT(g_atomic_int_get(&gLibRunData.refCount) >= 0);

   if (gPlugin != NULL) {
      g_module_close(gPlugin);
      gPlugin = NULL;
   }

   if (CU_get_failure_list() != NULL) {
      err = 1;
   }

   CU_cleanup_registry();
   memset(&gLibRunData, 0, sizeof gLibRunData);
   return (int) err;
}


/**
 * Decreases the internal ref count of the library. When the ref count reaches
 * zero, this function will ask the application's main loop to stop running.
 *
 * @param[in]  ctx   The application contexnt.
 */

void
RpcDebug_DecRef(ToolsAppCtx *ctx)
{
   if (g_atomic_int_dec_and_test(&gLibRunData.refCount)) {
      g_main_loop_quit(ctx->mainLoop);
   }
}


/**
 * Increases the internal ref count of the library. Test code that needs the
 * process to stay alive should call this function to ensure that.
 */

void
RpcDebug_IncRef(void)
{
   g_atomic_int_inc(&gLibRunData.refCount);
}


/**
 * Initializes the debug library and loads the debug plugin at the given path.
 * This function panics if something goes wrong.
 *
 * @param[in]  ctx         The application context.
 * @param[in]  dbgPlugin   Path to the debug plugin.
 *
 * @return Structure containing the debug library's information.
 */

RpcDebugLibData *
RpcDebug_Initialize(ToolsAppCtx *ctx,
                    gchar *dbgPlugin)
{
   RpcDebugOnLoadFn onload;
   RpcDebugLibData *ldata;

   ASSERT(gPlugin == NULL);

   ldata = g_malloc(sizeof *ldata);

   gPlugin = g_module_open(dbgPlugin, G_MODULE_BIND_LOCAL);
   if (gPlugin == NULL) {
      g_error("Can't load plugin: %s\n", dbgPlugin);
   }

   if (!g_module_symbol(gPlugin, "RpcDebugOnLoad", (gpointer *) &onload)) {
      g_error("No entry point in debug plugin %s\n", dbgPlugin);
   }

   ldata->debugPlugin = onload(ctx);
   if (ldata->debugPlugin == NULL) {
      g_error("No registration data from plugin %s\n", dbgPlugin);
   }

   ldata->newDebugChannel = RpcDebug_NewDebugChannel;
   ldata->run = RpcDebugRun;

   return ldata;
}


/**
 * Places the next item on the given RPC message list into the given RPC data.
 * Updates the current index of the list.
 *
 * @param[in]  rpcdata     The injected RPC data.
 * @param[in]  list        The message list.
 *
 * @return TRUE if updated the RPC data, FALSE if reached the end of the list.
 */

gboolean
RpcDebug_SendNext(RpcDebugMsgMapping *rpcdata,
                  RpcDebugMsgList *list)
{
   if (list->mappings[list->index].message != NULL) {
      rpcdata->message = list->mappings[list->index].message;
      rpcdata->messageLen = list->mappings[list->index].messageLen;
      rpcdata->validateFn = list->mappings[list->index].validateFn;
      rpcdata->freeMsg = list->mappings[list->index].freeMsg;
      list->index++;
      return TRUE;
   }
   return FALSE;
}


/**
 * Sets @a res / @a len when responding to an RPC.
 *
 * @param[in]  str   The string to set.
 * @param[out] res   Where to store the result.
 * @param[out] len   Where to store the length.
 */

void
RpcDebug_SetResult(const char *str,
                   char **res,
                   size_t *len)
{
   if (res != NULL) {
      *res = Util_SafeStrdup(str);
   }
   if (len != NULL) {
      *len = strlen(str);
   }
}

#if defined(_WIN64) && (_MSC_VER == 1500) && GLIB_CHECK_VERSION(2, 46, 0)
/*
 * Restore optimizer.
 */
#pragma optimize("", on)
#endif

 070701000003E4000081A400000000000000000000000168225505000004A2000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/tests/vmrpcdbg/vmrpcdbgInt.h   /*********************************************************
 * Copyright (C) 2009-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMRPCDBGINT_H_
#define _VMRPCDBGINT_H_

/**
 * @file vmrpcdbgInt.h
 *
 * Internal definitions for the vmrpcdbg library.
 */

#include "vmware/tools/rpcdebug.h"

RpcChannel *
RpcDebug_NewDebugChannel(ToolsAppCtx *ctx,
                         RpcDebugLibData *data);

#endif /* _VMRPCDBGINT_H_ */

  070701000003E5000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002B00000000open-vm-tools-12.5.2/open-vm-tools/toolbox    070701000003E6000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/toolbox/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000003E7000081A4000000000000000000000001682255050000096B000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/toolbox/Makefile.am    ################################################################################
### Copyright (c) 2007-2018,2020-2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-toolbox-cmd

vmware_toolbox_cmd_LDADD =
vmware_toolbox_cmd_LDADD += ../libguestlib/libguestlib.la
vmware_toolbox_cmd_LDADD += @VMTOOLS_LIBS@
vmware_toolbox_cmd_LDADD += @XDR_LIBS@

vmware_toolbox_cmd_CPPFLAGS =
vmware_toolbox_cmd_CPPFLAGS += @VMTOOLS_CPPFLAGS@
vmware_toolbox_cmd_CPPFLAGS += @XDR_CPPFLAGS@

vmware_toolbox_cmd_SOURCES =
vmware_toolbox_cmd_SOURCES += toolbox-cmd.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-config.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-devices.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-info.c
if LINUX
   vmware_toolbox_cmd_SOURCES += toolboxcmd-gueststore.c
   vmware_toolbox_cmd_SOURCES += toolboxcmd-globalconf.c
   vmware_toolbox_cmd_LDADD   += ../lib/globalConfig/libGlobalConfig.la
   vmware_toolbox_cmd_LDADD   += @GOBJECT_LIBS@
endif
vmware_toolbox_cmd_SOURCES += toolboxcmd-logging.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-scripts.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-shrink.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-stat.c
vmware_toolbox_cmd_SOURCES += toolboxcmd-time.c

if HAVE_ICU
   vmware_toolbox_cmd_LDADD += @ICU_LIBS@
   vmware_toolbox_cmd_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
                             $(LIBTOOLFLAGS) --mode=link $(CXX) \
                             $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
                             $(LDFLAGS) -o $@
else
   vmware_toolbox_cmd_LINK = $(LINK)
endif

# Message catalogs.
install-data-hook:
	@INSTVMSG@ toolboxcmd $(srcdir)/l10n $(DESTDIR)$(datadir)

 070701000003E8000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n   070701000003E9000081A400000000000000000000000168225505000034EE000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/de.vmsg   ##########################################################
# Copyright (c) 2010-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "Befehl"

arg.config.key = "Konfigurationsschlüssel"

arg.config.operation = "Konfigurationsvorgang"

arg.config.section = "Konfigurationsabschnitt"

arg.config.value = "Konfigurationswert"

arg.devicename = "Gerätename"

arg.info.class = "info-Infoklasse"

arg.info.subcommand = "info-Vorgang"

arg.logging.level = "Ebene der Protokollierung"

arg.logging.service = "Dienstname der Protokollierung"

arg.logging.subcommand = "Vorgang der Protokollierung"

arg.mountpoint = "Mount-Punkt"

arg.scriptpath = "Skriptpfad"

arg.scripttype = "Skripttyp"

arg.subcommand = "Unterbefehl"

device.connect.error = "Gerät '%1$s' kann nicht angeschlossen werden.\n"

device.disconnect.error = "Das Gerät '%1$s' kann nicht getrennt werden.\n"

device.notfound = "Fehler beim Abrufen der Schnittstelleninformationen: Gerät nicht gefunden.\n"

disk.shrink.canceled = "Vorgang zum Verkleinern der Festplatte abgebrochen.\n"

disk.shrink.complete = "Vorgang zum Verkleinern der Festplatte abgeschlossen.\n"

disk.shrink.conflict = "Fehler: Die Toolbox betrachtet die Festplattenverkleinerung als aktiviert, während der Host die Funktion als deaktiviert behandelt.\n\n Schließen Sie die Toolbox und öffnen Sie sie erneut, um eine Synchronisierung mit dem Host vorzunehmen.\n"

disk.shrink.disabled = "Das Verkleinern von Festplatten ist für diese virtuelle Maschine deaktiviert.\n\nDie Verkleinerung ist für verknüpfte Klone, übergeordnete Elemente verknüpfter Klone, \nFestplatten mit vorab zugewiesenem Speicherplatz, Snapshots oder aufgrund weiterer Faktoren deaktiviert. \nWeitere Informationen finden Sie im Benutzerhandbuch.\n"

disk.shrink.error = "Fehler beim Verkleinern der Festplatte: %1$s\n"

disk.shrink.incomplete = "Vorgang zum Verkleinern der Festplatte nicht abgeschlossen.\n"

disk.shrink.partition.error = "Partitionsdaten konnten nicht erfasst werden.\n"

disk.shrink.partition.notfound = "Die Partition %1$s wurde nicht gefunden\n"

disk.shrink.partition.unsupported = "Die Partition %1$s kann nicht verkleinert werden\n"

disk.shrink.unavailable = "Die Verkleinerungsfunktion ist nicht verfügbar,\n\nweil Sie entweder eine alte Version eines VMware-Produkts ausführen oder weil zu viele Kommunikationskanäle offen sind.\n\nWenn Sie eine alte Version eines VMware-Produkts ausführen, sollten Sie ein Upgrade in Erwägung ziehen.\n\nWenn zu viele Kommunikationskanäle geöffnet sind, sollten Sie Ihre virtuelle Maschine aus- und anschließend wieder einschalten.\n"

disk.shrink.ignoreFreeSpaceWarnings = "Ignorieren Sie während des Verkleinerungsvorgangs etwaige Warnungen bezüglich des Speicherplatzes.\n"

disk.wipe.ignoreFreeSpaceWarnings = "Ignorieren Sie während des Wiper-Vorgangs etwaige Warnungen bezüglich des Speicherplatzes.\n"

disk.wiper.file.error = "Fehler, Erstellen der Wiper-Datei nicht möglich.\n"

disk.wiper.progress = "\rProgress: %1$d"

error.message = "Fehler: %1$s\n"

error.missing = "%1$s: %2$s fehlt\n"

error.noadmin.posix = "%1$s: Sie müssen root-Benutzer sein, um die %2$s-Vorgänge auszuführen.\n"

error.noadmin.win = "%1$s: Zum Ausführen der %2$s-Vorgänge sind Administratorberechtigungen erforderlich.\nVerwenden Sie eine Administrator-Eingabeaufforderung, um diese Aufgaben abzuschließen.\n"

error.novirtual = "%1$s muss innerhalb einer virtuellen Maschine ausgeführt werden.\n"

error.unknown = "%1$s: %2$s '%3$s' unbekannt\n"

help.config = "%1$s: Tools-Konfiguration bearbeiten\nNutzung: %2$s %3$s <Unterbefehl>\n\nUnterbefehle:\n   get <Abschnitt> <Schlüssel>: Aktuellen Wert für <Schlüssel> anzeigen\n   HINWEIS: Wenn der <Schlüssel> in tools.conf nicht vorhanden ist, wird\n   sein Wert aus der globalen Konfiguration zurückgegeben, falls vorhanden\n   set <Abschnitt> <Schlüssel> <Wert>: <Schlüssel> auf <Wert> einstellen\n\n   remove <Abschnitt> <Schlüssel>: <Schlüssel> entfernen\n\n<Abschnitt> kann ein beliebiger unterstützter Abschnitt sein, z. B. logging, guestoperations oder guestinfo.\n<Schlüssel> kann ein beliebiger Konfigurationsschlüssel sein.\n<Wert> kann ein beliebiger Wert sein.\n"

help.device = "%1$s: Funktionen für die Hardwaregeräte der virtuellen Maschine\nNutzung: %2$s %3$s <Unterbefehl> [Argumente]\n'dev' ist der Name des Geräts.\n\nUnterbefehle:\n   enable <dev>: Aktivieren des Geräts 'dev' \n   disable <dev>: Deaktivieren des Geräts 'dev'\n   list: Auflisten aller verfügbaren Geräte\n   status <dev>: Ausgeben des Status eines Geräts\n"

help.disk = "%1$s: Führt Verkleinerungen von Festplatten durch\nNutzung: %2$s %3$s <Unterbefehl> [Argumente]\n\nUnterbefehle:\n   list: Auflisten der verfügbaren Speicherorte\n   shrink <Speicherort>: Löschen und Verkleinern eines Dateisystems am angegebenen Speicherort\n   shrinkonly: Verkleinern alle Festplatten\n   wipe <Speicherort>: Löschen eines Dateisystems am angegebenen Speicherort\n"

help.globalconf = "%1$s: Globale Konfigurationsdownloads aus GuestStore verwalten\nNutzung: %2$s %3$s <Unterbefehel>\n\nUnterbefehle nur für ESX-Gäste:\n   enable: Globales Konfigurationsmodul aktivieren\n   disable: Globales Konfigurationsmodul deaktivieren\n   refresh: Neuen Download der globalen Konfiguration aus dem GuestStore auslösen\n   status: Status des globalen Konfigurationsmodul drucken\n"

help.gueststore = "%1$s: Ressourceninhalt aus dem GuestStore abrufen\nNutzung: %2$s %3$s <Unterbefehl>\n\nUnterbefehle nur für ESX-Gäste:\n   getcontent <Ressourcenpfad> <Ausgabedatei>: Ressourceninhalt aus dem GuestStore abrufen un in Ausgabedatei speichern.\n\n<Ressourcenpfad> startet mit / und stellt eine eindeutige Ressource im GuestStore dar. Wenn er mit / endet, wird standardmäßig die zugrunde liegende Ressource 'metadata.json' abgerufen.\n<Ausgabedatei> ist der Pfad zu einer Datei, in der der Ressourceninhalt gespeichert werden soll.\n"

help.hint = "Siehe '%1$s %2$s%3$s%4$s' für weitere Informationen.\n"

help.info = "%1$s: Aktualisieren von Gastbetriebssysteminformationen auf dem Host\nNutzung: %2$s %3$s update <Infoklasse>\n\nUnterbefehle:\n   update < Infoklasse >: Aktualisieren von durch <Infoklasse> ermittelte Informationen\n< Infoklasse > kann 'network' sein\n"

help.logging = "%1$s: Tools-Protokollierung verändern\nNutzung: %2$s %3$s level <Unterbefehl> <Dienstname> <level>\n\nUnterbefehle:\n   get <Dienstname>: Aktuelle Ebene anzeigen\n   HINWEIS: Wenn die Ebene in tools.conf nicht vorhanden ist, wird ihr\n   Wert aus der globalen Konfiguration zurückgegeben, falls vorhanden\n   set <Dienstname> <Ebene>: Aktuelle Ebene festlegen\n\n<Dienstname> kann jeder unterstützte Dienst sein wie vmsvc oder vmusr\n<Ebene> kann für einen Fehler, ein kritisches Ereignis, eine Warnung, Info, Meldung oder ein Debugging stehen\n   Standard ist %4$s\n"

help.main = "Nutzung: %1$s <Befehl> [Optionen] [Unterbefehl]\nWenn Sie Hilfe zu einem bestimmten Befehl benötigen, geben Sie '%2$s %3$s <Befehl>' ein.\nGeben Sie '%4$s -v' ein, um die VMware Tools-Version anzuzeigen.\nVerwenden Sie die Option '-q' zum Unterdrücken der stdout-Ausgabe.\nFür die meisten Befehle gibt es Unterbefehle.\n\nVerfügbare Befehle:\n   config\n   device\n   disk (nicht auf allen Betriebssystemen verfügbar)\n   globalconf (nicht auf allen Betriebssystemen verfügbar)\n   gueststore (nicht auf allen Betriebssystemen verfügbar)\n   info\n   logging\n   script\n   stat\n   timesync\n   upgrade (nicht auf allen Betriebssystemen verfügbar)\n"

help.script = "%1$s: Steuerung der Skripts, die als Reaktion auf Betriebsvorgänge ausgeführt werden\nNutzung: %2$s %3$s <power|resume|suspend|shutdown> <Unterbefehl> [Argumente]\n\nUnterbefehle:\n   enable: Angegebenes Skript aktivieren und dessen Pfad auf den Standardpfad zurücksetzen\n   disable: Angegebenes Skript deaktivieren\n   set <Vollständiger Pfad>: Angegebenes Skript auf den angegebenen Pfad festlegen\n   default: Standardpfad an das angegebene Skript ausgeben\n   current: Aktuellen Pfad des angegebenen Skripts ausgeben\n   HINWEIS: Wenn der Pfad in tools.conf nicht vorhanden ist, wird dessen\n   Wert aus der globalen Konfiguration zurückgegeben, falls vorhanden\n"

help.stat = "%1$s: Drucken von hilfreichen Gast- und Hostinformationen\nNutzung: %2$s %3$s <Unterbefehl>\n\nUnterbefehle:\n   hosttime: Ausgeben der Hostuhrzeit\n   speed: Ausgeben der CPU-Geschwindigkeit in MHz\nUnterbefehle nur für ESX-Gäste:\n   sessionid: Ausgeben der aktuellen Sitzungs-ID\n   balloon: Ausgeben der Balloon-Arbeitsspeicher-Informationen\n   swap: Ausgeben der Auslagerungsinformationen für den Arbeitsspeicher\n   memlimit: Ausgeben des Arbeitsspeicher-Limits\n   memres: Ausgeben der Arbeitsspeicherreservierung\n   cpures: Ausgeben der CPU-Reservierung\n   cpulimit: Ausgeben des CPU-Limits\n  raw [<Codierung> <Statistikname>]: Drucken von statistischen Rohdaten\n      <Codierung> steht für 'text', 'json', 'xml' oder 'yaml'.\n      <Statistikname> beinhaltet session, host, resources, vscsi und\n      vnet (einige Statistiken wie vsci bestehen aus zwei Wörtern, z. B. 'vscsi scsi0:0').\n      Druckt verfügbare Statistiken wenn für <Codierung> und <Statistikname>\n      keine Argumente angegeben wurden.\n"

help.timesync = "%1$s: Funktionen für die Steuerung der Zeitsynchronisierung auf dem Gastbetriebssystem\Nutzung: %2$s %3$s <Unterbefehl>\n\nUnterbefehle:\n   enable: Aktivieren der Zeitsynchronisierung\n   disable: Deaktivieren der Zeitsynchronisierung\n   status: Ausgeben des Status der Zeitsynchronisierung\n"

help.upgrade = "%1$s: Funktionen für das Upgrade von VMware Tools.\nNutzung: %2$s %3$s <Unterbefehl> [Argumente]\nUnterbefehle:\n   status: Überprüfen des Upgrade-Status für VMware Tools.\n   start: Starten eines automatischen Upgrade von VMware Tools.\n\nDamit die Upgrades funktionieren, muss der VMware Tools-Dienst ausgeführt werden.\n"

globalconf.refresh.failed  = "'%1$s' ist fehlgeschlagen, da das globalconf-Modul deaktiviert ist.\n"

globalconf.start_vmtools = "%1$s: Dienst %2$s wird gestartet.\n"

globalconf.status = "Der Status des globalconf-Modul lautet '%1$s'\n"

globalconf.stop_vmtools = "%1$s: Dienst %2$s wird angehalten.\n"

globalconf.update_config = "%1$s: Die Konfiguration wird aktualisiert.\n"

gueststore.content_size = "Größe des Inhalts in Byte: "

gueststore.error.client_lib = "'%1$s' ist fehlgeschlagen, Fehler in der GuestStore-Client-Bibliothek: %2$s.\n"

gueststore.progress = "\rFortschritt: %1$d%%"

option.disabled = "Deaktiviert"

option.enabled = "Aktiviert"

result.error.failed = "'%1$s' ist fehlgeschlagen. Weitere Informationen finden Sie im %2$s-Protokoll.\n"

result.succeeded = "'%1$s' war erfolgreich.\n"

script.notfound = "%1$s ist nicht vorhanden.\n"

script.operation = "Vorgang"

script.unknownop = "Kein Skript für den Vorgang %1$s.\n"

script.write.error = "Fehler beim Schreiben der Konfiguration: %1$s\n"

stat.balloon.failed = "Balloon-Arbeitsspeicher konnte nicht abgerufen werden: %1$s\n"

stat.cpumax.failed = "CPU-Limit konnte nicht abgerufen werden: %1$s\n"

stat.cpumin.failed = "CPU-Mindestwert konnte nicht abgerufen werden: %1$s\n"

stat.formattime.failed = "Hostuhrzeit kann nicht formatiert werden.\n"

stat.get.failed = "Statistiken konnten nicht abgerufen werden: %1$s\n"

stat.getsession.failed = "Sitzungs-ID konnte nicht abgerufen werden: %1$s\n"

stat.getspeed.failed = "Prozessorgeschwindigkeit nicht abrufbar.\n"

stat.gettime.failed = "Hostuhrzeit konnte nicht abgerufen werden.\n"

stat.maxmem.failed = "Arbeitsspeicher-Limit konnte nicht abgerufen werden: %1$s\n"

stat.memres.failed = "Arbeitsspeicherreservierung konnte nicht abgerufen werden: %1$s\n"

stat.memswap.failed = "Ausgelagerter Arbeitsspeicher konnte nicht abgerufen werden: %1$s\n"

stat.openhandle.failed = "OpenHandle fehlgeschlagen: %1$s\n"

stat.update.failed = "UpdateInfo fehlgeschlagen: %1$s\n"

stat.processorSpeed.info = "%1$u MHz\n"

stat.memoryBalloon.info = "%1$u MB\n"

stat.memoryReservation.info = "%1$u MB\n"

stat.memorySwapped.info = "%1$u MB\n"

stat.memoryLimit.info = "%1$u MB\n"

stat.cpuReservation.info = "%1$u MHz\n"

stat.cpuLimit.info = "%1$u MHz\n"

upgrade.available = "Eine neue Version von VMware Tools steht zur Verfügung.\n"

upgrade.error.check_error = "Fehler beim Überprüfen der Verfügbarkeit von Upgrades.\n"

upgrade.error.error = "Fehler beim Starten des Upgrades von VMware Tools.\n"

upgrade.error.not_supported = "Der Host unterstützt kein automatisches Upgrade von VMware Tools.\n"

upgrade.error.unknown_reply = "Unerwartete Antwort vom Host: %1$s\n"

upgrade.started = "Das Upgrade wird durchgeführt.\n"

upgrade.uptodate = "VMware Tools ist auf dem neuesten Stand.\n"

  070701000003EA000081A40000000000000000000000016822550500002CAF000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/en.vmsg   ##########################################################
# Copyright (c) 2010-2017,2020-2021 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "command"

arg.config.key = "config key"

arg.config.operation = "config operation"

arg.config.section = "config section"

arg.config.value = "config value"

arg.devicename = "device name"

arg.info.class = "info infoclass"

arg.info.subcommand = "info operation"

arg.logging.level = "logging level"

arg.logging.service = "logging servicename"

arg.logging.subcommand = "logging operation"

arg.mountpoint = "mount point"

arg.scriptpath = "script path"

arg.scripttype = "script type"

arg.subcommand = "subcommand"

device.connect.error = "Unable to connect device %1$s.\n"

device.disconnect.error = "Unable to disconnect device %1$s.\n"

device.notfound = "Error fetching interface information: device not found.\n"

disk.shrink.canceled = "Disk shrink canceled.\n"

disk.shrink.complete = "Disk shrinking complete.\n"

disk.shrink.conflict = "Error, The Toolbox believes disk shrinking is enabled while the host believes it is disabled.\n\n Please close and reopen the Toolbox to synchronize it with the host.\n"

disk.shrink.disabled = "Shrink disk is disabled for this virtual machine.\n\nShrinking is disabled for linked clones, parents of linked clones, \npre-allocated disks, snapshots, or due to other factors. \nSee the User's manual for more information.\n"

disk.shrink.error = "Error while shrinking: %1$s\n"

disk.shrink.incomplete = "Shrinking not completed.\n"

disk.shrink.partition.error = "Unable to collect partition data.\n"

disk.shrink.partition.notfound = "Unable to find partition %1$s\n"

disk.shrink.partition.unsupported = "Partition %1$s is not shrinkable\n"

disk.shrink.unavailable = "The shrink feature is not available,\n\neither because you are running an old version of a VMware product, or because too many communication channels are open.\n\nIf you are running an old version of a VMware product, you should consider upgrading.\n\nIf too many communication channels are open, you should power off your virtual machine and then power it back on.\n"

disk.shrink.ignoreFreeSpaceWarnings = "Please disregard any warnings about disk space for the duration of shrink process.\n"

disk.wipe.ignoreFreeSpaceWarnings = "Please disregard any warnings about disk space for the duration of wipe process.\n"

disk.wiper.file.error = "Error, Unable to create wiper file.\n"

disk.wiper.progress = "\rProgress: %1$d"

error.message = "Error: %1$s\n"

error.missing = "%1$s: Missing %2$s\n"

error.noadmin.posix = "%1$s: You must be root to perform %2$s operations.\n"

error.noadmin.win = "%1$s: Administrator permissions are needed to perform %2$s operations.\nUse an administrator command prompt to complete these tasks.\n"

error.novirtual = "%1$s must be run inside a virtual machine.\n"

error.unknown = "%1$s: Unknown %2$s '%3$s'\n"

help.config = "%1$s: modify Tools configuration\nUsage: %2$s %3$s <subcommand>\n\nSubcommands:\n   get <section> <key>: display current value for <key>\n   NOTE: If the <key> is not present in tools.conf, its\n   value from the global configuration is returned if present\n   set <section> <key> <value>: set <key> to <value>\n\n   remove <section> <key>: remove <key>\n\n<section> can be any supported section, such as logging, guestoperations or guestinfo.\n<key> can be any configuration key.\n<value> can be any value.\n"

help.device = "%1$s: functions related to the virtual machine's hardware devices\nUsage: %2$s %3$s <subcommand> [args]\ndev is the name of the device.\n\nSubcommands:\n   enable <dev>: enable the device dev\n   disable <dev>: disable the device dev\n   list: list all available devices\n   status <dev>: print the status of a device\n"

help.disk = "%1$s: perform disk shrink operations\nUsage: %2$s %3$s <subcommand> [args]\n\nSubcommands:\n   list: list available locations\n   shrink <location>: wipes and shrinks a file system at the given location\n   shrinkonly: shrinks all disks\n   wipe <location>: wipes a file system at the given location\n"

help.globalconf = "%1$s: Manage global configuration downloads from the GuestStore\nUsage: %2$s %3$s <subcommand>\n\nESX guests only subcommands:\n   enable: Enable the global configuration module\n   disable: Disable the global configuration module\n   refresh: Trigger a new download of the global configuration from the GuestStore\n   status: Print the status of the global configuration module\n"

help.gueststore = "%1$s: get resource content from GuestStore\nUsage: %2$s %3$s <subcommand>\n\nESX guests only subcommands:\n   getcontent <resource path> <output file>: get resource content from GuestStore and save to output file.\n\n<resource path> starts with / and represents a unique resource in GuestStore. If it ends with /, defaults to retrieve the underlying 'metadata.json' resource.\n<output file> is the path of a file to save resource content to.\n"

help.hint = "Try '%1$s %2$s%3$s%4$s' for more information.\n"

help.info = "%1$s: update guest information on the host\nUsage: %2$s %3$s update <infoclass>\n\nSubcommands:\n   update <infoclass>: update information identified by <infoclass>\n<infoclass> can be 'network'\n"

help.logging = "%1$s: modify tools logging\nUsage: %2$s %3$s level <subcommand> <servicename> <level>\n\nSubcommands:\n   get <servicename>: display current level\n   NOTE: If the level is not present in tools.conf, its\n   value from the global configuration is returned if present\n   set <servicename> <level>: set current level\n\n<servicename> can be any supported service, such as vmsvc or vmusr\n<level> can be one of error, critical, warning, info, message, debug\n   default is %4$s\n"

help.main = "Usage: %1$s <command> [options] [subcommand]\nType '%2$s %3$s <command>' for help on a specific command.\nType '%4$s -v' to see the VMware Tools version.\nUse '-q' option to suppress stdout output.\nMost commands take a subcommand.\n\nAvailable commands:\n   config\n   device\n   disk (not available on all operating systems)\n   globalconf (not available on all operating systems)\n   gueststore (not available on all operating systems)\n   info\n   logging\n   script\n   stat\n   timesync\n   upgrade (not available on all operating systems)\n"

help.script = "%1$s: control the scripts run in response to power operations\nUsage: %2$s %3$s <power|resume|suspend|shutdown> <subcommand> [args]\n\nSubcommands:\n   enable: enable the given script and restore its path to the default\n   disable: disable the given script\n   set <full_path>: set the given script to the given path\n   default: print the default path of the given script\n   current: print the current path of the given script\n   NOTE: If the path is not present in tools.conf, its\n   value from the global configuration is returned if present\n"

help.stat = "%1$s: print useful guest and host information\nUsage: %2$s %3$s <subcommand>\n\nSubcommands:\n   hosttime: print the host time\n   speed: print the CPU speed in MHz\nESX guests only subcommands:\n   sessionid: print the current session id\n   balloon: print memory ballooning information\n   swap: print memory swapping information\n   memlimit: print memory limit information\n   memres: print memory reservation information\n   cpures: print CPU reservation information\n   cpulimit: print CPU limit information\n   raw [<encoding> <stat name>]: print raw stat information\n      <encoding> can be one of 'text', 'json', 'xml', 'yaml'.\n      <stat name> includes session, host, resources, vscsi and\n      vnet (Some stats like vscsi are two words, e.g. 'vscsi scsi0:0').\n      Prints the available stats if <encoding> and <stat name>\n      arguments are not specified.\n"

help.timesync = "%1$s: functions for controlling time synchronization on the guest OS\nUsage: %2$s %3$s <subcommand>\n\nSubcommands:\n   enable: enable time synchronization\n   disable: disable time synchronization\n   status: print the time synchronization status\n"

help.upgrade = "%1$s: functions related to upgrading VMware Tools.\nUsage: %2$s %3$s <subcommand> [args]\nSubcommands:\n   status: check the VMware Tools upgrade status.\n   start: initiate an auto-upgrade of VMware Tools.\n\nFor upgrades to work, the VMware Tools service needs to be running.\n"

globalconf.refresh.failed  = "'%1$s' failed, since globalconf module is disabled.\n"

globalconf.start_vmtools = "%1$s: Starting the %2$s service.\n"

globalconf.status = "The status of globalconf module is '%1$s'\n"

globalconf.stop_vmtools = "%1$s: Stopping the %2$s service.\n"

globalconf.update_config = "%1$s: Updating the Configuration.\n"

gueststore.content_size = "Content size in bytes: "

gueststore.error.client_lib = "'%1$s' failed, GuestStore client library error: %2$s.\n"

gueststore.progress = "\rProgress: %1$d%%"

option.disabled = "Disabled"

option.enabled = "Enabled"

result.error.failed = "'%1$s' failed, check %2$s log for more information.\n"

result.succeeded = "'%1$s' succeeded.\n"

script.notfound = "%1$s doesn't exist.\n"

script.operation = "operation"

script.unknownop = "No script for operation %1$s.\n"

script.write.error = "Error writing config: %1$s\n"

stat.balloon.failed = "Failed to get ballooned memory: %1$s\n"

stat.cpumax.failed = "Failed to get CPU limit: %1$s\n"

stat.cpumin.failed = "Failed to get CPU minimum: %1$s\n"

stat.formattime.failed = "Unable to format host time.\n"

stat.get.failed = "Failed to get stat: %1$s\n"

stat.getsession.failed = "Failed to get session ID: %1$s\n"

stat.getspeed.failed = "Unable to get processor speed.\n"

stat.gettime.failed = "Unable to get host time.\n"

stat.maxmem.failed = "Failed to get memory limit: %1$s\n"

stat.memres.failed = "Failed to get memory reservation: %1$s\n"

stat.memswap.failed = "Failed to get swapped memory: %1$s\n"

stat.openhandle.failed = "OpenHandle failed: %1$s\n"

stat.update.failed = "UpdateInfo failed: %1$s\n"

stat.processorSpeed.info = "%1$u MHz\n"

stat.memoryBalloon.info = "%1$u MB\n"

stat.memoryReservation.info = "%1$u MB\n"

stat.memorySwapped.info = "%1$u MB\n"

stat.memoryLimit.info = "%1$u MB\n"

stat.cpuReservation.info = "%1$u MHz\n"

stat.cpuLimit.info = "%1$u MHz\n"

upgrade.available = "A new version of VMware Tools is available.\n"

upgrade.error.check_error = "Error checking upgrade availability.\n"

upgrade.error.error = "Error starting VMware Tools upgrade.\n"

upgrade.error.not_supported = "The host does not support auto upgrade of VMware Tools.\n"

upgrade.error.unknown_reply = "Unexpected host reply: %1$s\n"

upgrade.started = "The upgrade has started.\n"

upgrade.uptodate = "VMware Tools are up-to-date.\n"

 070701000003EB000081A400000000000000000000000168225505000034DF000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/es.vmsg   ##########################################################
# Copyright (c) 2010-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "comando"

arg.config.key = "clave de configuración"

arg.config.operation = "operación de configuración"

arg.config.section = "sección de configuración"

arg.config.value = "valor de configuración"

arg.devicename = "nombre de dispositivo"

arg.info.class = "clase de información"

arg.info.subcommand = "operación de información"

arg.logging.level = "nivel de registro"

arg.logging.service = "nombre del servicio de registro"

arg.logging.subcommand = "operación de registro"

arg.mountpoint = "punto de montaje"

arg.scriptpath = "ruta del script"

arg.scripttype = "tipo de script"

arg.subcommand = "subcomando"

device.connect.error = "No se puede conectar el dispositivo %1$s.\n"

device.disconnect.error = "No se puede desconectar el dispositivo %1$s.\n"

device.notfound = "Error al obtener la información de la interfaz: no se ha encontrado el dispositivo.\n"

disk.shrink.canceled = "Se ha cancelado la reducción de disco.\n"

disk.shrink.complete = "Reducción de disco completa.\n"

disk.shrink.conflict = "Error. El cuadro de herramientas considera que la reducción de disco está habilitada mientras que el host cree que está deshabilitada.\n\n Cierre y vuelva a abrir el cuadro de herramientas para sincronizarlo con el host.\n"

disk.shrink.disabled = "La reducción de disco está deshabilitada para esta máquina virtual.\n\nLa reducción está deshabilitada para clones vinculados, elementos primarios de clones vinculados, \ndiscos previamente asignados, instantáneas o debido a otros factores. \nConsulte el Manual del usuario para obtener más información.\n"

disk.shrink.error = "Error durante la reducción: %1$s\n"

disk.shrink.incomplete = "No se ha completado la reducción.\n"

disk.shrink.partition.error = "No se pueden recopilar los datos de la partición.\n"

disk.shrink.partition.notfound = "No se puede encontrar la partición %1$s\n"

disk.shrink.partition.unsupported = "La partición %1$s no es reducible\n"

disk.shrink.unavailable = "La característica de reducción no está disponible,\n\nya sea porque está ejecutando una versión antigua de un producto de VMware o porque hay abiertos demasiados canales de comunicación.\n\nSi está ejecutando una versión antigua de un producto de VMware, debería considerar la posibilidad de actualizarla.\n\nSi hay abiertos demasiados canales de comunicación, debería apagar la máquina virtual y volver a encenderla.\n"

disk.shrink.ignoreFreeSpaceWarnings = "Ignore cualquier aviso sobre el espacio en disco durante el proceso de reducción.\n"

disk.wipe.ignoreFreeSpaceWarnings = "Ignore cualquier aviso sobre el espacio en disco durante el proceso de borrado.\n"

disk.wiper.file.error = "Error, no se puede crear un archivo wiper.\n"

disk.wiper.progress = "\rProgreso: %1$d"

error.message = "Error: %1$s\n"

error.missing = "%1$s: Falta %2$s\n"

error.noadmin.posix = "%1$s: Debe ser raíz para realizar operaciones %2$s.\n"

error.noadmin.win = "%1$s: Se necesitan permisos de administrador para realizar operaciones de %2$s.\nUtilice un símbolo del sistema de administrador para completar estas tareas.\n"

error.novirtual = "%1$s tiene que ejecutarse dentro de una máquina virtual.\n"

error.unknown = "%1$s: %2$s '%3$s' desconocido\n"

help.config = "%1$s: modificar la configuración de Tools\nUso: %2$s %3$s <subcomando>\n\nSubcomandos:\n   get <sección> <clave>: muestra el valor actual de <clave>\n   NOTA: Si la <clave> no está presente en tools.conf, su\n   valor de la configuración global se devuelve si está presente\n   set <sección> <clave> <valor>: establece <clave> en <valor>\n\n   remove <sección> <clave>: elimina <clave>\n\n<sección> puede ser cualquier sección admitida como, por ejemplo, logging, guestoperations o guestinfo.\n<clave> puede ser cualquier clave de configuración.\n<valor> puede ser cualquier valor.\n"

help.device = "%1$s: funciones relacionadas con los dispositivos de hardware de la máquina virtual\nUso: %2$s %3$s <subcomando> [argumentos]\ndev es el nombre del dispositivo.\n\nSubcomandos:\n   enable <dev>: habilita el dev del dispositivo\n   disable <dev>: deshabilita el dev del dispositivo\n   list: enumera todos los dispositivos disponibles\n   status <dev>: imprime el estado de un dispositivo\n"

help.disk = "%1$s: realizar operaciones de reducción de disco\nUso: %2$s %3$s <subcomando> [argumentos]\n\nSubcomandos:\n   list: enumera las ubicaciones disponibles\n   shrink <ubicación>: borra y reduce un sistema de archivos en la ubicación en cuestión\n   shrinkonly: reduce todos los discos\n   wipe <ubicación>: borra un sistema de archivos en la ubicación en cuestión\n"

help.globalconf = "%1$s: administrar las descargas de configuración global de GuestStore\nUso: %2$s %3$s <subcomando>\n\nSubcomandos solo de invitados de ESX:\n   enable: habilita el módulo de configuración global.\n   disable: deshabilita el módulo de configuración global\n   refresh: activa una nueva descarga de la configuración global desde GuestStore\n   status: imprime el estado del módulo de configuración global\n"

help.gueststore = "%1$s: obtener contenido de recursos de GuestStore\nUso: %2$s %3$s <subcomando>\n\nSubcomandos solo de invitados de ESX:\n   getcontent <ruta de acceso de recurso> <archivo de salida>: obtener contenido de recursos de GuestStore y guardar en el archivo de salida.\n\n<ruta de acceso de recurso> empieza con / y representa un recurso único en GuestStore. Si termina con /, el valor predeterminado es recuperar el recurso 'metadata.json' subyacente.\n<archivo de salida> es la ruta de acceso de un archivo en el que se guardará el contenido de los recursos.\n"

help.hint = "Pruebe '%1$s %2$s%3$s%4$s' para obtener más información.\n"

help.info = "%1$s: actualiza la información del invitado en el host\nUso: %2$s %3$s actualiza <claseinformación>\n\nSubcomandos:\n   actualización <claseinformación>: actualiza la información identificada por <claseinformación>\n<claseinformación> puede ser 'red'\n"

help.logging = "%1$s: modificar el registro de herramientas\nUso: %2$s de nivel %3$s <subcomando> <nombreservicio> <nivel>\n\nSubcomandos:\n   get <nombreservicio>: muestra el nivel actual\n   NOTA: Si el nivel no está presente en tools.conf, su\n   valor de la configuración global se devuelve si está presente\n   set <nombreservicio> <nivel>: establece el nivel actual\n\n<nombreservicio> puede ser cualquier servicio admitido, como vmsvc o vmusr\n<nivel> puede ser un error, un fallo crítico, una advertencia, una información, un mensaje o una depuración\n   El valor predeterminado es %4$s\n"

help.main = "Uso: %1$s <comando> [opciones] [subcomando]\nEscriba '%2$s %3$s <comando>' para obtener ayuda sobre un comando específico.\nEscriba '%4$s -v' para consultar la versión de VMware Tools.\nUtilice la opción '-q' para suprimir el resultado stdout.\nLa mayoría de comandos tiene un subcomando.\n\nComandos disponibles:\n   config\n   device\n   disk (no disponible en todos los sistemas operativos)\n   globalconf (no disponible en todos los sistemas operativos)\n   gueststore (no disponible en todos los sistemas operativos)\n   info\n   logging\n   script\n   stat\n   timesync\n   upgrade (no disponible en todos los sistemas operativos)\n"

help.script = "%1$s: controlar la ejecución de secuencias de comandos en respuesta a operaciones de encendido\nUso: %2$s %3$s <power|resume|suspend|shutdown> <subcomando> [argumentos]\n\nSubcomandos:\n   enable: habilita la secuencia de comandos en cuestión y restaura la ruta de acceso predeterminada\n   disable: deshabilita la secuencia de comandos en cuestión\n   set <ruta de acceso completa>: establece la secuencia de comandos en cuestión en la ruta de acceso determinada\n   default: imprime la ruta de acceso predeterminada de la secuencia de comandos en cuestión\n   current: imprime la ruta de acceso actual de la secuencia de comandos en cuestión\n   NOTA: Si la ruta de acceso no está presente en tools.conf, su\n   valor de la configuración global se devuelve si está presente\n"

help.stat = "%1$s: imprimir información útil sobre hosts e invitados\nUso: %2$s %3$s <subcomando>\n\nSubcomandos:\n   hosttime: imprime la hora del host\n   speed: imprime la velocidad de la CPU en MHz\nSubcomandos solo de invitados de ESX:\n   sessionid: imprime el identificador de sesión actual\n   balloon: imprime información de aumento de memoria\n   swap: imprime información de intercambio de memoria\n   memlimit: imprime información de límite de memoria\n   memres: imprime información de reserva de memoria\n   cpures: imprime información de reserva de la CPU\n   cpulimit: imprime información del límite de la CPU\n   raw [<codificación> <nombre de estadísticas>]: imprime información de estadísticas sin formato\n      <codificación> puede ser uno de  'text', 'json', 'xml', 'yaml'.\n      <nombre de estadísticas> incluye sesión, host, recursos, vscsi y\n      vnet (Algunas estadísticas como vscsi son dos palabras, por ejemplo, 'vscsi scsi0:0').\n      Imprime las estadísticas disponibles si no se han especificado los argumentos de <codificación> y <nombre de estadísticas>.\n"

help.timesync = "%1$s: funciones para controlar la sincronización horaria en el SO invitado\nUso: %2$s %3$s <subcomando>\n\nSubcomandos:\n   enable: habilita la sincronización horaria\n   disable: deshabilita la sincronización horaria\n   status: imprime el estado de la sincronización horaria\n"

help.upgrade = "%1$s: funciones relacionadas con la actualización de VMware Tools.\nUso: %2$s %3$s <subcomando> [argumentos]\nSubcomandos:\n   status: comprueba el estado de actualización de VMware Tools.\n   start: inicia la actualización automática de VMware Tools.\n\nPara que funcionen las actualizaciones, debe ejecutarse el servicio de VMware Tools.\n"

globalconf.refresh.failed  = "'%1$s' no se realizó correctamente, ya que el módulo globalconf está deshabilitado.\n"

globalconf.start_vmtools = "%1$s: iniciando el servicio %2$s.\n"

globalconf.status = "El estado del módulo globalconf es '%1$s'\n"

globalconf.stop_vmtools = "%1$s: deteniendo el servicio %2$s.\n"

globalconf.update_config = "%1$s: actualizando la configuración.\n"

gueststore.content_size = "Tamaño de contenido en bytes: "

gueststore.error.client_lib = "'%1$s' no se realizó correctamente, error de biblioteca de cliente de GuestStore: %2$s.\n"

gueststore.progress = "\rProgreso: %1$d%%"

option.disabled = "Desactivada"

option.enabled = "Activada"

result.error.failed = "'%1$s' no se realizó correctamente, compruebe el registro de %2$s para obtener más información.\n"

result.succeeded = "'%1$s' se realizó correctamente.\n"

script.notfound = "%1$s no existe.\n"

script.operation = "operación"

script.unknownop = "No existe un script para la operación %1$s.\n"

script.write.error = "Error al escribir la configuración: %1$s\n"

stat.balloon.failed = "Error al obtener la memoria inflada: %1$s\n"

stat.cpumax.failed = "Error al obtener el límite de la CPU: %1$s\n"

stat.cpumin.failed = "Error al obtener el mínimo de la CPU: %1$s\n"

stat.formattime.failed = "No se puede formatear la hora del host.\n"

stat.get.failed = "Error al obtener stat: %1$s\n"

stat.getsession.failed = "Error al obtener identificador de sesión: %1$s\n"

stat.getspeed.failed = "No se puede obtener la velocidad del procesador.\n"

stat.gettime.failed = "No se puede obtener la hora del host.\n"

stat.maxmem.failed = "Error al obtener el límite de memoria: %1$s\n"

stat.memres.failed = "Error al obtener la reserva de memoria: %1$s\n"

stat.memswap.failed = "Error al obtener la memoria intercambiada: %1$s\n"

stat.openhandle.failed = "Error de OpenHandle: %1$s\n"

stat.update.failed = "Error de UpdateInfo: %1$s\n"

stat.processorSpeed.info = "%1$u MHz\n"

stat.memoryBalloon.info = "%1$u MB\n"

stat.memoryReservation.info = "%1$u MB\n"

stat.memorySwapped.info = "%1$u MB\n"

stat.memoryLimit.info = "%1$u MB\n"

stat.cpuReservation.info = "%1$u MHz\n"

stat.cpuLimit.info = "%1$u MHz\n"

upgrade.available = "Está disponible una nueva versión de VMware Tools.\n"

upgrade.error.check_error = "Error al comprobar la disponibilidad de la actualización.\n"

upgrade.error.error = "Error al iniciar la actualización de VMware Tools.\n"

upgrade.error.not_supported = "El host no es compatible con la actualización automática de VMware Tools.\n"

upgrade.error.unknown_reply = "Respuesta de host inesperada: %1$s\n"

upgrade.started = "Se ha iniciado la actualización.\n"

upgrade.uptodate = "VMware Tools está actualizado.\n"

 070701000003EC000081A40000000000000000000000016822550500003670000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/fr.vmsg   ##########################################################
# Copyright (c) 2010-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "commande"

arg.config.key = "clé de configuration"

arg.config.operation = "opération de configuration"

arg.config.section = "section de configuration"

arg.config.value = "valeur de configuration"

arg.devicename = "Nom du périphérique"

arg.info.class = "infos classeinfos"

arg.info.subcommand = "infos opération"

arg.logging.level = "niveau de journalisation"

arg.logging.service = "nom du service de journalisation"

arg.logging.subcommand = "opération de journalisation"

arg.mountpoint = "Point de montage"

arg.scriptpath = "chemin de script"

arg.scripttype = "type de script"

arg.subcommand = "sous-commande"

device.connect.error = "Impossible de connecter le périphérique %1$s.\n"

device.disconnect.error = "Impossible de déconnecter le périphérique %1$s.\n"

device.notfound = "Erreur lors de l'extraction des informations de l'interface : périphérique introuvable.\n"

disk.shrink.canceled = "Réduction du disque annulée.\n"

disk.shrink.complete = "Réduction du disque terminée.\n"

disk.shrink.conflict = "Erreur, la Boîte à outils a repéré que la réduction de disque est activée alors que l'hôte considère qu'elle est désactivée.\n\n Fermez puis rouvrez la Boîte à outils pour la synchroniser avec l'hôte.\n"

disk.shrink.disabled = "L'option de réduction du disque est désactivée sur cette machine virtuelle.\n\nLa réduction est désactivée pour les clones liés, les parents des clones liés, \nles disques pré-affectés, les snapshots, ou en raison d'autres facteurs. \nReportez-vous au Manuel d'utilisateur pour plus d'informations.\n"

disk.shrink.error = "Erreur lors de la réduction : %1$s\n"

disk.shrink.incomplete = "Réduction non terminée.\n"

disk.shrink.partition.error = "Impossible de récupérer les données de partition.\n"

disk.shrink.partition.notfound = "Impossible de trouver la partition %1$s\n"

disk.shrink.partition.unsupported = "La partition %1$s ne peut pas être réduite\n"

disk.shrink.unavailable = "La fonction de réduction n'est pas disponible,\n\nsoit parce que vous exécutez une ancienne version d'un produit VMware, soit parce que trop de canaux de communication sont ouverts.\n\nSi vous exécutez une ancienne version d'un produit VMware, vous devez envisager de le mettre à niveau.\n\nSi trop de canaux de communication sont ouverts, vous devez mettre hors tension votre machine virtuelle puis la remettre sous tension.\n"

disk.shrink.ignoreFreeSpaceWarnings = "Ignorez tout avertissement relatif à l'espace disque pour la durée du processus de réduction.\n"

disk.wipe.ignoreFreeSpaceWarnings = "Ignorez tout avertissement relatif à l'espace disque pour la durée du processus d'effacement.\n"

disk.wiper.file.error = "Erreur, impossible de créer un fichier d'effacement.\n"

disk.wiper.progress = "\rProgression : %1$d"

error.message = "Erreur : %1$s\n"

error.missing = "%1$s : manquant %2$s\n"

error.noadmin.posix = "%1$s : vous devez être à la racine pour pouvoir effectuer les opérations %2$s.\n"

error.noadmin.win = "%1$s : les autorisations d'administrateur sont nécessaires pour pouvoir effectuer les opérations %2$s.\nUtilisez une invite de commande d'administrateur pour effectuer ces tâches.\n"

error.novirtual = "%1$s doit être exécuté sur une machine virtuelle.\n"

error.unknown = "%1$s : %2$s inconnu '%3$s'\n"

help.config = "%1$s : modifiez la configuration de Tools\nUtilisation : %2$s %3$s <subcommand>\n\nSous-commandes :\n   get <section> <key> : affichez la valeur actuelle de <key>\n   REMARQUE : si <key> est absente de tools.conf, sa\n   valeur de configuration globale est renvoyée si présente\n   set <section> <key> <value> : définissez <key> sur <value>\n\n  remove <section> <key> : supprimez <key>\n\n<section> peut être n'importe quelle section prise en charge, telle que logging, guestoperations ou guestinfo.\n<key> peut être n'importe quelle clé de configuration.\n<value> peut être n'importe quelle valeur.\n"

help.device = "%1$s : fonctions apparentées aux appareils matériels de la machine virtuelle\nSyntaxe : %2$s %3$s <sous-commande> [args]\ndev est le nom du périphérique.\n\nSous-commandes :\n   enable <dev> : activer le périphérique dev\n   disable <dev> : désactiver le périphérique dev\n   list : liste de tous les périphériques disponibles\n   status <dev> : imprime l'état d'un périphérique\n"

help.disk = "%1$s : effectue les opérations de réduction des disques\nSyntaxe : %2$s %3$s <sous-commande> [args]\n\nSous-commandes :\n   list : liste des emplacements disponibles\n   shrink <emplacement> : efface et réduit un système de fichiers à l'emplacement donné\n   shrinkonly : réduit tous les disques\n   wipe <emplacement> : efface un système de fichiers à l'emplacement donné\n"

help.globalconf = "%1$s : gérer les téléchargements de la configuration globale depuis GuestStore\nUtilisation : %2$s %3$s <subcommand>\n\nSous-commandes des invités ESX uniquement :\n   enable : activer le module de configuration globale.\n   disable : désactiver le module de configuration globale\n  refresh : déclencher un nouveau téléchargement de la configuration globale depuis GuestStore\n   status : imprimer l'état du module de configuration globale\n"

help.gueststore = "%1$s : obtenez le contenu des ressources de GuestStore\nUtilisation : %2$s %3$s <subcommand>\n\nSous-commandes des invités ESX uniquement :\n   getcontent <resource path> <output file> : obtenez le contenu des ressources de GuestStore et enregistrez-le dans le fichier de sortie.\n\n<resource path> commence par / et représente une ressource unique dans GuestStore. En cas de terminaison par /, utilise la valeur par défaut pour récupérer la ressource « metadata.json » sous-jacente.\n<output file> est le chemin d'accès d'un fichier dans lequel enregistrer le contenu des ressources.\n"

help.hint = "Pour plus d'informations, essayez '%1$s %2$s%3$s%4$s'.\n"

help.info = "%1$s : mettre à jour des informations d'invité sur l'hôte\nSyntaxe : %2$s %3$s met à jour <classeinfos>\n\nSous-commandes :\n   mettre à jour <classeinfos> : met à jour des informations identifiées par <classeinfos>\n<classeinfos> peut être 'réseau'\n"

help.logging = "%1$s : modifiez la journalisation de Tools\nUtilisation : %2$s %3$s level <subcommand> <servicename> <level>\n\nSous-commandes :\n   get <servicename> : affichez les niveaux actuels\n   REMARQUE : si le niveau est absent de tools.conf, sa\n   valeur de configuration globale est renvoyée si présente\n   set <servicename> <level> : définissez le niveau actuel\n\n<servicename> peut être n'importe quel service pris en charge, tel que vmsvc ou vmusr\n<level> peut être l'un des suivants : erreur, critique, avertissement, info, message, débogage\n   la valeur par défaut est %4$s\n"

help.main = "Utilisation : %1$s <command> [options] [subcommand]\nEntrez « %2$s %3$s <command> » pour obtenir de l'aide sur une commande spécifique.\nEntrez « %4$s -v » pour consulter la version de VMware Tools.\nUtilisez l'option « -q » pour supprimer la sortie stdout.\nLa plupart des commandes utilisent une sous-commande.\n\nCommandes disponibles :\n   config\n   device\n   disk (non disponible sur tous les systèmes d'expoloitation)\n   globalconf (non disponible sur tous les systèmes d'expoloitation)\n   gueststore (non disponible sur tous les systèmes d'expoloitation)\n   info\n   logging\n   script\n   stat\n   timesync\n   upgrade (non disponible sur tous les systèmes d'expoloitation)\n"

help.script = "%1$s : vérifiez que les scripts s'exécutent en réponse aux opérations d'alimentation\nUtilisation : %2$s %3$s <power|resume|suspend|shutdown> <subcommand> [args]\n\nSous-commandes :\n   enable : activez le script donné et rétablissez la valeur par défaut de son chemin d'accès\n   disable : désactivez le script donné\n   set <full_path> : définissez le script donné sur le chemin d'accès donné\n   default : imprimez le chemin d'accès par défaut du script donné\n   current : imprimez le chemin d'accès actuel du script donné\n   REMARQUE : si le chemin d'accès est absent de tools.conf, sa\n   valeur de configuration globale est renvoyée si présente\n"

help.stat = "%1$s : imprimez des informations utiles sur le client et l'hôte\nSyntaxe : %2$s %3$s <sous-commande>\n\nSous-commandes :\n   hosttime : imprimez l'heure de l'hôte\n   speed : imprimez la vitesse du CPU en MHz\nClients ESX uniquement des sous-commandes :\n   sessionid : imprimez l'ID de session actuel\n   balloon : imprimez des informations sur le gonflage de mémoire\n   swap : imprimez des informations sur l'échange de mémoire\n   memlimit : imprimez des informations sur la limite de mémoire\n   memres : imprimez des informations sur la réservation de mémoire\n   cpures : imprimez des informations sur la réservation de CPU\n   cpulimit : imprimez des informations sur la limite de CPU\n   raw [<codage> <nom statistique>] : imprimez des informations statistiques brutes\n      <codage> peut être 'text', 'json', 'xml', 'yaml'.\n      <nom statistique> inclut la session, l'hôte, les ressources, vscsi et\n      vnet (certaines statistiques comme vscsi sont deux mots, par exemple 'vscsi scsi0:0').\n      Imprime les statistiques disponibles si les arguments <codage> et <nom statistique>\n      ne sont pas spécifiés.\n"

help.timesync = "%1$s : fonctions pour contrôler la synchronisation temporelle sur le système d'exploitation client\nSyntaxe : %2$s %3$s <sous-commande>\n\nSous-commandes :\n   enable : activez la synchronisation temporelle\n   disable : désactivez la synchronisation temporelle\n   status : imprimez l'état de la synchronisation temporelle\n"

help.upgrade = "%1$s : fonctions en rapport avec la mise à niveau de VMware Tools.\nSyntaxe : %2$s %3$s <sous-commande> [args]\nSous-commandes :\n   status : vérifie le statut de mise à niveau de VMware Tools.\n   start : initialise une mise à niveau automatique de VMware Tools.\n\nPour que les mises à niveau fonctionnent, le service VMware Tools doit être en cours d'exécution.\n"

globalconf.refresh.failed  = "Échec de « %1$s », car le module globalconf est désactivé.\n"

globalconf.start_vmtools = "%1$s : démarrage du service %2$s.\n"

globalconf.status = "L'état du module globalconf est « %1$s »\n"

globalconf.stop_vmtools = "%1$s : arrêt du service %2$s.\n"

globalconf.update_config = "%1$s : mise à jour de la configuration.\n"

gueststore.content_size = "Taille du contenu en octets : "

gueststore.error.client_lib = "Échec de « %1$s », erreur de la bibliothèque du client GuestStore : %2$s.\n"

gueststore.progress = "\rProgression : %1$d%%"

option.disabled = "Désactivé"

option.enabled = "Activé"

result.error.failed = "Échec de « %1$s ». Pour plus d'informations, consultez le journal %2$s.\n"

result.succeeded = "Réussite de « %1$s ».\n"

script.notfound = "%1$s n'existe pas.\n"

script.operation = "opération"

script.unknownop = "Pas de script pour l'opération %1$s.\n"

script.write.error = "Erreur lors de l'écriture des config : %1$s\n"

stat.balloon.failed = "Échec de l'obtention d'une mémoire gonflée : %1$s\n"

stat.cpumax.failed = "Échec de l'obtention d'une limite CPU : %1$s\n"

stat.cpumin.failed = "Échec de l'obtention d'un CPU minimum : %1$s\n"

stat.formattime.failed = "Impossible de formater l'heure de l'hôte.\n"

stat.get.failed = "Impossible d'obtenir la statistique : %1$s\n"

stat.getsession.failed = "Échec de l'obtention de l'ID de la session : %1$s\n"

stat.getspeed.failed = "Impossible d'obtenir la vitesse du processeur.\n"

stat.gettime.failed = "Impossible d'obtenir l'heure de l'hôte.\n"

stat.maxmem.failed = "Échec de l'obtention d'une limite de mémoire : %1$s\n"

stat.memres.failed = "Échec de la réservation d'une mémoire : %1$s\n"

stat.memswap.failed = "Échec de l'obtention d'une mémoire échangée : %1$s\n"

stat.openhandle.failed = "OpenHandle a échoué : %1$s\n"

stat.update.failed = "UpdateInfo a échoué : %1$s\n"

stat.processorSpeed.info = "%1$u MHz\n"

stat.memoryBalloon.info = "%1$u Mo\n"

stat.memoryReservation.info = "%1$u Mo\n"

stat.memorySwapped.info = "%1$u Mo\n"

stat.memoryLimit.info = "%1$u Mo\n"

stat.cpuReservation.info = "%1$u MHz\n"

stat.cpuLimit.info = "%1$u MHz\n"

upgrade.available = "Une nouvelle version de VMware Tools est disponible.\n"

upgrade.error.check_error = "Erreur lors de la vérification de la disponibilité de la mise à jour.\n"

upgrade.error.error = "Erreur lors du démarrage de la mise à jour de VMware Tools.\n"

upgrade.error.not_supported = "L'hôte ne prend pas en charge les mises à jour automatiques de VMware Tools.\n"

upgrade.error.unknown_reply = "Réponse inattendue de l'hôte : %1$s\n"

upgrade.started = "La mise à jour a commencé.\n"

upgrade.uptodate = "Les VMware Tools sont mis à jour.\n"

070701000003ED000081A40000000000000000000000016822550500003401000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/it.vmsg   ##########################################################
# Copyright (c) 2010-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "comando"

arg.config.key = "chiave di configurazione"

arg.config.operation = "operazione di configurazione"

arg.config.section = "sezione di configurazione"

arg.config.value = "valore di configurazione"

arg.devicename = "nome dispositivo"

arg.info.class = "info classe info"

arg.info.subcommand = "info operazione"

arg.logging.level = "livello di registrazione"

arg.logging.service = "nome servizio registrazione"

arg.logging.subcommand = "operazione di registrazione"

arg.mountpoint = "punto di montaggio"

arg.scriptpath = "percorso script"

arg.scripttype = "tipo script"

arg.subcommand = "sottocomando"

device.connect.error = "Impossibile connettere il dispositivo %1$s.\n"

device.disconnect.error = "Impossibile disconnettere il dispositivo %1$s.\n"

device.notfound = "Errore durante il recupero delle informazioni di interfaccia: dispositivo non trovato.\n"

disk.shrink.canceled = "Riduzione disco annullata.\n"

disk.shrink.complete = "Riduzione disco completata.\n"

disk.shrink.conflict = "La Toolbox ritiene che la funzione di riduzione del disco sia abilitata, mentre il sistema host ritiene che sia disabilitata. \n\n Chiudere e riaprire la Toolbox per sincronizzarla con il sistema host.\n"

disk.shrink.disabled = "La compattazione del disco è disabilitata per questa macchina virtuale.\n\nLa compattazione è disattivata per i cloni collegati, l'elemento padre dei cloni collegati, \ndischi pre-allocati, istantanee, o a causa di altri fattori. \nConsultare il manuale dell'utente per ulteriori informazioni.\n"

disk.shrink.error = "Errore durante la riduzione: %1$s\n"

disk.shrink.incomplete = "Riduzione non completata.\n"

disk.shrink.partition.error = "Impossibile raccogliere i dati della partizione.\n"

disk.shrink.partition.notfound = "Impossibile trovare la partizione %1$s\n"

disk.shrink.partition.unsupported = "La partizione %1$s non può essere ridotta\n"

disk.shrink.unavailable = "La funzionalità di riduzione non è disponibile.\n\nLa versione di un prodotto di VMware in esecuzione è obsoleta oppure sono aperti troppi canali di comunicazione.\n\nSe si utilizza una versione obsoleta di un prodotto VMware, passare all'aggiornamento.\n\nSe sono aperti troppi canali di comunicazione, disattivare la macchina virtuale, quindi riattivarla.\n"

disk.shrink.ignoreFreeSpaceWarnings = "Ignorare eventuali avvisi sullo spazio su disco per tutta la durata processo di compattazione.\n"

disk.wipe.ignoreFreeSpaceWarnings = "Ignorare eventuali avvisi sullo spazio su disco per tutta la durata del processo di cancellazione.\n"

disk.wiper.file.error = "Errore: impossibile creare il file wiper.\n"

disk.wiper.progress = "\\rAvanzamento: %1$d"

error.message = "Errore: %1$s\n"

error.missing = "%1$s: %2$s mancante\n"

error.noadmin.posix = "%1$s: è necessario essere utente root per poter eseguire operazioni %2$s.\n"

error.noadmin.win = "%1$s: Sono necessarie le autorizzazioni di amministratore per eseguire operazioni %2$s.\nUtilizzare un prompt dei comandi di amministratore per completare queste attività.\n"

error.novirtual = "%1$s deve essere eseguito all'interno di una macchina virtuale.\n"

error.unknown = "%1$s: %2$s '%3$s' sconosciuto\n"

help.config = "%1$s: modifica la configurazione di Tools\nUtilizzo: %2$s %3$s <comando secondario>\n\nComandi secondari:\n   get <sezione> <chiave>: visualizza il valore corrente per <chiave>\n   NOTA: se <chiave> non è presente in tools.conf, viene\n   restituito il valore corrispondente della configurazione globale, se disponibile\n   set <sezione> <chiave> <valore>: imposta <chiave> su <valore>\n\n   remove <sezione> <chiave>: rimuove <chiave>\n\n<sezione> può essere qualsiasi sezione supportata come logging, guestoperations o guestinfo.\n<chiave> può essere qualsiasi chiave di configurazione.\n<valore> può essere qualsiasi valore.\n"

help.device = "%1$s: funzioni relative ai dispositivi hardware della macchina virtuale\nUtilizzo: %2$s %3$s <comando secondario> [args]\ndev è il nome del dispositivo.\n\nComandi secondari:\n   attiva <dev>: attivare il dispositivo dev\n   disattiva <dev>: disattivare il dispositivo dev\n   elenca: elencare tutti i dispositivi disponibili\n   stato <dev>: stampare lo stato di un dispositivo\n"

help.disk = "%1$s: eseguire le operazioni di compattazione\nUtilizzo: %2$s %3$s <comando secondario> [args]\n\nComandi secondari:\n   elenco: elencare le posizioni disponibili\n   compatta <posizione>: pulisce e compatta un sistema di file nella posizione data\n   compatta solo: compatta tutti i dischi\n   pulisci <posizione>: pulisce un sistema di file nella posizione data\n"

help.globalconf = "%1$s: gestisce i download della configurazione globale da GuestStore\nUtilizzo: %2$s %3$s <comando secondario>\n\nSolo comandi secondari del guest ESX:\n   enable: abilita il modulo di configurazione globale\n   disable: disabilita il modulo di configurazione globale\n   refresh: attiva un nuovo download della configurazione globale da GuestStore\n   status: stampa lo stato del modulo di configurazione globale\n"

help.gueststore = "%1$s: ottiene il contenuto della risorsa da GuestStore\nUtilizzo: %2$s %3$s <comando secondario>\n\nSolo comandi secondari di guest ESX:\n   getcontent <percorso risorsa> <file di output>: ottiene il contenuto della risorsa da GuestStore ed esegue il salvataggio nel file di output.\n\n<percorso risorsa> inizia con / e rappresenta una risorsa univoca in GuestStore. Se termina con /, per impostazione predefinita recupera la risorsa 'metadata.json' sottostante.\n<file di output> è il percorso di un file in cui salvare il contenuto della risorsa.\n"

help.hint = "Provare '%1$s %2$s%3$s%4$s' per ulteriori informazioni.\n"

help.info = "%1$s: aggiornare informazioni guest su host\nUtilizzo: %2$s %3$s aggiorna <classe info>\n\nComandi secondari:\n   aggiorna <classe info>: aggiornare informazioni identificate da <classe info>\n<classe info> può essere "rete"\n"

help.logging = "%1$s: modifica la registrazione di Tools\nUtilizzo: %2$s %3$s level <comando secondario> <nome servizio> <livello>\n\nComandi secondari:\n   get <nome servizio>: visualizza il livello corrente\n   NOTA: se il livello non è presente in tools.conf, viene\n   restituito il valore corrispondente della configurazione globale se disponibile\n   set <nome servizio> <livello>: imposta il livello corrente\n\n<nome servizio> può essere qualsiasi servizio supportato, come vmsvc o vmusr\n<livello> può essere error, critical, warning, info, message o debug\n   il valore predefinito è %4$s\n"

help.main = "Utilizzo: %1$s <comando> [opzioni] [comando secondario]\nDigitare '%2$s %3$s <comando>' per visualizzare informaziomi su un comando specifico.\nDigitare '%4$s -v' per visualizzare la versione di VMware Tools.\nUtilizzare l'opzione '-q' per eliminare l'output stdout.\nLa maggior parte dei comandi accetta un comando secondario.\n\nComandi disponibili:\n   config\n   device\n   disk (non disponibile in tutti i sistemi operativi)\n   globalconf (non disponibile in tutti i sistemi operativi)\n   gueststore (non disponibile in tutti i sistemi operativi)\n   info\n   logging\n   script\n   stat\n   timesync\n   upgrade (non disponibile in tutti i sistemi operativi)\n"

help.script = "%1$s: controlla gli script eseguiti in risposta alle operazioni di alimentazione\nUtilizzo: %2$s %3$s <power|resume|suspend|shutdown> <comando secondario> [argomenti]\n\nComandi secondari:\n   enable: abilita lo script dato e ripristina il suo percorso sul valore predefinito\n   disable: disabilita lo script dato\n   set <percorso_completo>: imposta lo script dato sul percorso dato\n   default: stampa il percorso predefinito dello script dato\n   current: stampa il percorso corrente dello script dato\n   NOTA: se il percorso non è presente in tools.conf, viene\n   restituito il valore corrispondente della configurazione globale se disponibile\n"

help.stat = "%1$s: stampare informazioni utili su guest e host\nUtilizzo: %2$s %3$s <comando secondario>\n\nComandi secondari:\n   hosttime: stampare il tempo dell'host\n   speed: stampare la velocità della CPU in MHz\nESX ospita solo comandi secondari:\n   sessionid: stampare l'id della sessione corrente\n   balloon: stampare le informazioni di ballooning della memoria\n   swap: stampare le informazioni di swap della memoria\n   memlimit: stampare le informazioni di limite della memoria\n   memres: stampare le informazioni di prenotazione della memoria\n   cpures: stampare le informazioni di prenotazione della CPU\n   cpulimit: stampare le informazioni di limite della CPU\n   raw [<codifica> <nome stato>]: stampare informazioni stato non elaborate\n      <codifica> può essere "text", "json", "xml", "yaml".\n      <nome stato> include sessione, host, risorse, vscsi e\n      vnet (alcuni stati, come vscsi, sono composti da due parole, ad es. "vscsi scsi0:0").\n      Stampa gli stati disponibili se <codifica> e <nome stato>\n      non sono argomenti specificati.\n"

help.timesync = "%1$s: funzioni per il controllo di sincronizzazione dell'ora sul sistema operativo guest\nUtilizzo: %2$s %3$s <comando secondario>\n\nComandi secondari:\n   attiva: attivare la sincronizzazione dell'ora\n   disattiva: disattivare la sincronizzazione dell'ora\n   stato: stampare lo stato di sincronizzazione dell'ora\n"

help.upgrade = "%1$s: funzioni relative all'aggiornamento di VMware Tools.\nUtilizzo: %2$s %3$s <comando secondario> [args]\nComandi secondari:\n   stato: controllare lo stato di aggiornamento di VMware Tools.\n   avvio: avviare un aggiornamento automatico di VMware Tools.\n\nPer il funzionamento degli aggiornamenti, il servizio VMware Tools deve essere in esecuzione.\n"

globalconf.refresh.failed  = "'%1$s' non riuscito perché il modulo globalconf è disabilitato.\n"

globalconf.start_vmtools = "%1$s: avvio del servizio %2$s.\n"

globalconf.status = "Lo stato del modulo globalconf è '%1$s'\n"

globalconf.stop_vmtools = "%1$s: arresto del servizio %2$s.\n"

globalconf.update_config = "%1$s: aggiornamento della configurazione.\n"

gueststore.content_size = "Dimensioni dei contenuti in byte: "

gueststore.error.client_lib = "'%1$s' non riuscito, errore della libreria client GuestStore: %2$s.\n"

gueststore.progress = "\rAvanzamento: %1$d%%"

option.disabled = "Disabilitato"

option.enabled = "Abilitata"

result.error.failed = "'%1$s' non riuscito. Controllare il registro %2$s per ulteriori informazioni.\n"

result.succeeded = "'%1$s' riuscito.\n"

script.notfound = "%1$s inesistente.\n"

script.operation = "operazione"

script.unknownop = "Nessuno script per l'operazione %1$s.\n"

script.write.error = "Errore durante la scrittura della configurazione: %1$s\n"

stat.balloon.failed = "Impossibile ottenere la memoria dinamica: %1$s\n"

stat.cpumax.failed = "Impossibile ottenere il limite di CPU: %1$s\n"

stat.cpumin.failed = "Impossibile ottenere il minimo di CPU: %1$s\n"

stat.formattime.failed = "Impossibile formattare l'ora host.\n"

stat.get.failed = "Impossibile ottenere statistiche: %1$s\\n"

stat.getsession.failed = "Impossibile ottenere l'ID sessione: %1$s\n"

stat.getspeed.failed = "Impossibile ottenere la velocità del processore.\n"

stat.gettime.failed = "Impossibile ottenere l'ora host.\n"

stat.maxmem.failed = "Impossibile ottenere il limite di memoria: %1$s\n"

stat.memres.failed = "Impossibile ottenere la prenotazione di memoria: %1$s\n"

stat.memswap.failed = "Impossibile ottenere la memoria di swap: %1$s\n"

stat.openhandle.failed = "OpenHandle non riuscito: %1$s\n"

stat.update.failed = "UpdateInfo non riuscito: %1$s\n"

stat.processorSpeed.info = "%1$u MHz\n"

stat.memoryBalloon.info = "%1$u MB\n"

stat.memoryReservation.info = "%1$u MB\n"

stat.memorySwapped.info = "%1$u MB\n"

stat.memoryLimit.info = "%1$u MB\n"

stat.cpuReservation.info = "%1$u MHz\n"

stat.cpuLimit.info = "%1$u MHz\n"

upgrade.available = "È disponibile una nuova versione di VMware Tools.\n"

upgrade.error.check_error = "Errore durante il controllo della disponibilità di aggiornamenti.\n"

upgrade.error.error = "Errore durante l'avvio dell'aggiornamento di VMware Tools.\n"

upgrade.error.not_supported = "Aggiornamento automatico di VMware Tools non supportato dall'host.\n"

upgrade.error.unknown_reply = "Risposta host imprevista: %1$s\n"

upgrade.started = "Aggiornamento avviato.\n"

upgrade.uptodate = "VMware Tools è aggiornato.\n"

   070701000003EE000081A40000000000000000000000016822550500003B7A000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/ja.vmsg   ##########################################################
# Copyright (c) 2010-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "コマンド"

arg.config.key = "設定キー"

arg.config.operation = "設定操作"

arg.config.section = "セクションの設定"

arg.config.value = "設定値"

arg.devicename = "デバイス名"

arg.info.class = "情報カテゴリ情報"

arg.info.subcommand = "操作情報"

arg.logging.level = "ログ レベル"

arg.logging.service = "サービス名のログ"

arg.logging.subcommand = "操作のログ"

arg.mountpoint = "マウント ポイント"

arg.scriptpath = "スクリプト パス"

arg.scripttype = "スクリプト タイプ"

arg.subcommand = "サブコマンド"

device.connect.error = "デバイス %1$s に接続できません。\n"

device.disconnect.error = "デバイス %1$s を切断できません。\n"

device.notfound = "インターフェイス情報の取得中のエラー: デバイスが見つかりませんでした。\n"

disk.shrink.canceled = "ディスク圧縮がキャンセルされました。\n"

disk.shrink.complete = "ディスクの圧縮が完了しました。\n"

disk.shrink.conflict = "エラー。ツールボックスでディスクの圧縮が有効であると判断されましたが、ホストでは無効であると判断されています。\n\n ツールボックスをいったん閉じてから開いて、ホストと同期してください。\n"

disk.shrink.disabled = "ディスクの圧縮はこの仮想マシンでは無効になっています。\n\nリンクされたクローン、リンクされたクローンの親、\n事前割り当て済みのディスク、スナップショット、またはその他の要因により、圧縮は無効になっています。\n詳細については、ユーザー マニュアルを参照してください。\n"

disk.shrink.error = "圧縮中にエラーが発生しました: %1$s\n"

disk.shrink.incomplete = "圧縮が完了しませんでした。\n"

disk.shrink.partition.error = "パーティション データを収集できません。\n"

disk.shrink.partition.notfound = "パーティション %1$s が見つかりません\n"

disk.shrink.partition.unsupported = "パーティション %1$s は圧縮できません\n"

disk.shrink.unavailable = "圧縮機能が使用できません。\n\n実行している VMware 製品のバージョンが古いか、開いている通信チャネルが多すぎることが原因として考えられます。\n\nVMware 製品のバージョンが古い場合は、アップグレードを検討してください。\n\n多数の通信チャネルを開いている場合は、仮想マシンをパワーオフしてから再開してください。\n"

disk.shrink.ignoreFreeSpaceWarnings = "圧縮プロセス中に表示されるディスク領域に関する警告はすべて無視してください。\n"

disk.wipe.ignoreFreeSpaceWarnings = "ワイプ プロセス中に表示されるディスク領域に関する警告はすべて無視してください。\n"

disk.wiper.file.error = "エラー。ワイパー ファイルを作成できません。\n"

disk.wiper.progress = "\r進行状況: %1$d"

error.message = "エラー: %1$s\n"

error.missing = "%1$s: %2$s が見つかりません\n"

error.noadmin.posix = "%1$s: %2$s 操作を実行するには、root ユーザーである必要があります。\n"

error.noadmin.win = "%1$s: %2$s 操作を実行するには、管理者権限が必要です。\nこれらのタスクを実行するには、管理者コマンド プロンプトを使用してください。\n"

error.novirtual = "%1$s は仮想マシン内で実行する必要があります。\n"

error.unknown = "%1$s: 不明な %2$s '%3$s'\n"

help.config = "%1$s: VMware Tools の設定を変更します\n使用方法: %2$s %3$s <サブコマンド>\n\nサブコマンド: \n   get <セクション> <キー>: <キー> の現在値を表示します\n   注: tools.conf に <キー> が存在しない場合は、その\n 値がグローバル設定から返されます (存在する場合)\n   set <セクション> <キー> <値>: <キー> を <値> に設定します\n\n   remove <セクション> <キー>: <キー> を削除します\n\n<セクション> には、logging、guestoperations、または guestinfo などのサポートされる任意のセクションを指定できます。\n<キー> には、任意の構成キーを指定できます。\n<値> には任意の値を指定できます。\n"

help.device = "%1$s: 仮想マシンのハードウェア デバイスに関連する機能\n使用方法： %2$s %3$s <サブコマンド> [引数]\ndev にはデバイス名を指定します。\n\nサブコマンド：\n   enable <デバイス>： デバイス dev を有効にする\n   disable <デバイス>： デバイス dev を無効にする\n   list： 有効なデバイスを一覧表示\n   status <デバイス>： デバイスのステータスを出力\n"

help.disk = "%1$s: ディスク圧縮操作を実行\n使用方法: %2$s %3$s <サブコマンド> [引数]\n\nサブコマンド:\n   list: 使用可能な場所を一覧表示\n   shrink <場所>: 指定された場所のファイル システムをワイプおよび圧縮\n   shrinkonly: すべてのディスクを圧縮\n   wipe <場所>: 指定された場所のファイル システムをワイプ\n"

help.globalconf = "%1$s: GuestStore からのグローバル設定のダウンロードを管理します\n使用方法: %2$s %3$s <サブコマンド>\n\nESX ゲストのみのサブコマンド:\n   enable: グローバル設定モジュールを有効にします\n   disable: グローバル設定モジュールを無効にします\n   refresh: GuestStore からのグローバル設定の新しいダウンロードをトリガします\n   status: グローバル設定モジュールのステータスを出力します\n"

help.gueststore = "%1$s: GuestStore からリソース コンテンツを取得します\n使用方法: %2$s %3$s <サブコマンド>\n\nESX ゲストのみのサブコマンド:\n   getcontent <リソース パス> <出力ファイル>: GuestStore からリソース コンテンツを取得し、出力ファイルに保存します。\n\n<リソース パス>は / で始まり、GuestStore 内の一意のリソースを表します。/ で終わる場合、デフォルトでは基盤となる「metadata.json」リソースを取得します。\n<出力ファイル> は、リソース コンテンツを保存するファイルのパスです。\n"

help.hint = "詳細については、「%1$s %2$s%3$s%4$s」を参照してください。\n"

help.info = "%1$s: ホストのゲスト情報を更新します\n使用方法: %2$s %3$s update <情報カテゴリ>\n\nサブコマンド:\n   update <情報カテゴリ>: <情報カテゴリ> で特定される情報を更新します\n<情報カテゴリ> には「network」を指定できます\n"

help.logging = "%1$s: VMware Tools ログを変更します\n使用方法: %2$s %3$s level <サブコマンド> <サービス名> <レベル>\n\nサブコマンド:\n   get <サービス名>: 現在のレベルを表示します\n   注: tools.conf にレベルが存在しない場合は、その\n 値がグローバル設定から返されます (存在する場合)\n   set <サービス名> <レベル>: 現在のレベルを設定します\n\n<サービス名> は、vmsvc や vmusr などのサポートされているサービスを指定できます\n<レベル> は、エラー、重大、警告、情報、メッセージ、デバッグのいずれかを指定できます\n   デフォルトは %4$s です\n"

help.main = "使用方法: %1$s <コマンド> [オプション] [サブコマンド]\n「%2$s %3$s <コマンド>」と入力すると、そのコマンドのヘルプを表示できます。\nVMware Tools のバージョンを確認するには「%4$s -v」と入力します。\nstdout 出力を抑止するには「-q」オプションを使用します。\nほとんどのコマンドではサブコマンドも使用されます。\n\n使用可能なコマンド: \n   config\n   device\n   disk (一部のオペレーティング システムでのみ使用可能)\n   globalconf (一部のオペレーティング システムでのみ使用可能)\n   gueststore (一部のオペレーティング システムでのみ使用可能)\n   info\n   logging\n   script\n   stat\n   timesync\n   upgrade (一部のオペレーティング システムでのみ使用可能)\n"

help.script = "%1$s: 電源操作に対応して実行されるスクリプトを制御します\n使用方法: %2$s %3$s <power|resume|suspend|shutdown> <サブコマンド> [引数]\n\nサブコマンド:\n   enable: 指定されたスクリプトを有効にして、そのパスをデフォルトに復元します\n   disable: 指定されたスクリプトを無効にします\n   set <フル パス>: 指定されたスクリプトを指定されたパスに設定します\n   default: 指定されたスクリプトのデフォルトのパスを出力します\n   current: 指定されたスクリプトの現在のパスを出力します\n   注: tools.conf にパスが存在しない場合は、その\n 値がグローバル設定から返されます (存在する場合)\n"

help.stat = "%1$s: 役に立つゲストおよびホスト情報を出力\n使用方法: %2$s %3$s <サブコマンド>\n\nサブコマンド:\n   hosttime: ホスト時刻を出力\n   speed: CPU 速度 (MHz) を出力\nESX ゲストのみのサブコマンド:\n   sessionid: 現在のセッション ID を出力\n   balloon: メモリのバルーニング情報を出力\n   swap: メモリのスワップ情報を出力\n   memlimit: メモリの制限情報を出力\n   memres: メモリの予約情報を出力\n   cpures: CPU の予約情報を出力\n   cpulimit: CPU の制限情報を出力\n   raw [<エンコーディング> <統計名>]: RAW 統計情報を出力\n      <エンコーディング> には、「text'」、「json」、「xml」、「yaml」のいずれかを指定できます。\n      <統計名> には、セッション、ホスト、リソース、vscsi および\n      vnet が含まれます（vscsi などのいくつかの統計は、たとえば「vscsi scsi0:0」など、2 語になります）。\n      <エンコーディング> および <統計名>\n      の引数が指定されない場合、利用可能な統計が出力されます。\n"

help.timesync = "%1$s: ゲスト OS の時刻の同期を制御するための機能\n使用方法: %2$s %3$s <サブコマンド>\n\nサブコマンド:\n   enable: 時刻の同期を有効にする\n   disable: 時刻の同期を無効にする\n   status: 時刻の同期の状態を出力\n"

help.upgrade = "%1$s: VMware Tools のアップグレードに関連する機能。\n使用方法: %2$s %3$s <サブコマンド> [引数]\nサブコマンド:\n   status： VMware Tools のアップグレード ステータスを確認\n   start: VMware Tools の自動アップグレードを開始\n\nアップグレードが機能するには、VMware Tools サービスを実行している必要があります。\n"

globalconf.refresh.failed  = "globalconf モジュールが無効になっているため、'%1$s' に失敗しました。\n"

globalconf.start_vmtools = "%1$s: %2$s サービスを開始しています。\n"

globalconf.status = "globalconf モジュールのステータスは '%1$s' です\n"

globalconf.stop_vmtools = "%1$s: %2$s サービスを停止しています。\n"

globalconf.update_config = "%1$s: 設定を更新しています。\n"

gueststore.content_size = "コンテンツ サイズ (バイト): "

gueststore.error.client_lib = "'%1$s' に失敗しました。GuestStore クライアント ライブラリ エラー: %2$s。\n"

gueststore.progress = "\r進行状況: %1$d%%"

option.disabled = "無効"

option.enabled = "有効"

result.error.failed = "'%1$s' に失敗しました。詳細については、%2$s ログを確認してください。\n"

result.succeeded = "'%1$s' が正常に完了しました。\n"

script.notfound = "%1$s は存在しません。\n"

script.operation = "操作"

script.unknownop = "操作 %1$s のスクリプトがありません。\n"

script.write.error = "設定書き込みエラー: %1$s\n"

stat.balloon.failed = "バルーン メモリの取得に失敗しました: %1$s\n"

stat.cpumax.failed = "CPU 制限の取得に失敗しました: %1$s\n"

stat.cpumin.failed = "CPU 最小値の取得に失敗しました: %1$s\n"

stat.formattime.failed = "ホスト時刻をフォーマットできません。\n"

stat.get.failed = "統計の取得に失敗しました: %1$s\n"

stat.getsession.failed = "セッション ID の取得に失敗しました: %1$s\n"

stat.getspeed.failed = "プロセッサ速度を取得できません。\n"

stat.gettime.failed = "ホスト時刻を取得できません。\n"

stat.maxmem.failed = "メモリ制限の取得に失敗しました: %1$s\n"

stat.memres.failed = "メモリ予約の取得に失敗しました: %1$s\n"

stat.memswap.failed = "スワップ メモリの取得に失敗しました: %1$s\n"

stat.openhandle.failed = "OpenHandle が失敗しました: %1$s\n"

stat.update.failed = "UpdateInfo が失敗しました: %1$s\n"

stat.processorSpeed.info = "%1$u MHz\n"

stat.memoryBalloon.info = "%1$u MB\n"

stat.memoryReservation.info = "%1$u MB\n"

stat.memorySwapped.info = "%1$u MB\n"

stat.memoryLimit.info = "%1$u MB\n"

stat.cpuReservation.info = "%1$u MHz\n"

stat.cpuLimit.info = "%1$u MHz\n"

upgrade.available = "VMware Tools の新しいバージョンを利用できます。\n"

upgrade.error.check_error = "利用できるアップグレードがあるかどうかを確認中にエラーが発生しました。\n"

upgrade.error.error = "VMware Tools のアップグレードの開始中にエラーが発生しました。\n"

upgrade.error.not_supported = "ホストは、VMware Tools の自動アップグレードをサポートしていません。\n"

upgrade.error.unknown_reply = "予期しないホスト応答: %1$s\n"

upgrade.started = "アップグレードが開始されました。\n"

upgrade.uptodate = "VMware Tools は最新です。\n"

  070701000003EF000081A400000000000000000000000168225505000035FA000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/ko.vmsg   ##########################################################
# Copyright (c) 2010-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "명령"

arg.config.key = "구성 키"

arg.config.operation = "구성 작업"

arg.config.section = "구성 섹션"

arg.config.value = "구성 값"

arg.devicename = "디바이스 이름"

arg.info.class = "정보 정보 클래스"

arg.info.subcommand = "정보 작업"

arg.logging.level = "로깅 수준"

arg.logging.service = "로깅 서비스 이름"

arg.logging.subcommand = "로깅 작업"

arg.mountpoint = "마운트 지점"

arg.scriptpath = "스크립트 경로"

arg.scripttype = "스크립트 유형"

arg.subcommand = "하위 명령"

device.connect.error = "디바이스 %1$s에 연결할 수 없습니다.\n"

device.disconnect.error = "디바이스 %1$s의 연결을 끊을 수 없습니다.\n"

device.notfound = "인터페이스 정보 가져오기 오류: 디바이스를 찾을 수 없습니다.\n"

disk.shrink.canceled = "디스크 축소가 취소되었습니다.\n"

disk.shrink.complete = "디스크 축소가 완료되었습니다.\n"

disk.shrink.conflict = "오류. 도구 상자에서는 디스크 축소를 사용한다고 간주하는 반면 호스트에서는 디스크 축소를 사용하지 않는다고 간주합니다.\n\n도구 상자를 닫았다가 다시 열어 이 설정을 호스트와 동기화하십시오.\n"

disk.shrink.disabled = "이 가상 시스템에 대해 디스크 축소가 사용되지 않습니다.\n\n링크드 클론, 링크드 클론의 상위, 미리 할당된 디스크, 스냅샷에 대해 \n축소가 사용되지 않거나 다른 원인 때문일 수 있습니다. \n자세한 내용은 사용 설명서를 참조하십시오.\n"

disk.shrink.error = "축소하는 동안 오류 발생: %1$s\n"

disk.shrink.incomplete = "축소가 완료되지 않았습니다.\n"

disk.shrink.partition.error = "파티션 데이터를 수집할 수 없습니다.\n"

disk.shrink.partition.notfound = "파티션 %1$s을(를) 찾을 수 없음\n"

disk.shrink.partition.unsupported = "파티션 %1$s 축소 불가능\n"

disk.shrink.unavailable = "축소 기능을 사용할 수 없습니다.\n\n이전 버전의 VMware 제품을 실행하고 있거나 통신 채널이 너무 많이 열려 있기 때문입니다.\n\n이전 버전의 VMware 제품을 실행하고 있는 경우에는 업그레이드를 고려해 보십시오.\n너무 많은 통신 채널이 열려 있는 경우에는 가상 시스템의 전원을 껐다가 다시 켜야 합니다.\n"

disk.shrink.ignoreFreeSpaceWarnings = "축소 프로세스가 실행되는 동안에는 디스크 공간에 대한 주의를 무시하십시오.\n"

disk.wipe.ignoreFreeSpaceWarnings = "지우기 프로세스가 실행되는 동안에는 디스크 공간에 대한 주의를 무시하십시오.\n"

disk.wiper.file.error = "오류. 와이퍼 파일을 생성할 수 없습니다.\n"

disk.wiper.progress = "\r진행률: %1$d"

error.message = "오류: %1$s\n"

error.missing = "%1$s: %2$s이(가) 없음\n"

error.noadmin.posix = "%1$s: %2$s 작업은 루트 사용자만 수행할 수 있습니다.\n"

error.noadmin.win = "%1$s: %2$s 작업을 수행하려면 관리자 권한이 필요합니다.\n이러한 작업을 완료하려면 관리자 명령 프롬프트를 사용하십시오.\n"

error.novirtual = "%1$s은(는) 가상 시스템 내부에서 실행해야 합니다.\n"

error.unknown = "%1$s: 알 수 없는 %2$s '%3$s'\n"

help.config = "%1$s: 도구 구성 수정\n사용법: %2$s %3$s <하위 명령>\n\n하위 명령:\n <섹션> <키> 획득: <키>에 대한 현재 값 표시\n 참고: <키>이(가) tools.conf에 없으면\n전역 구성의 값이 반환됩니다(있을 경우).\n<섹션> <키> <값> 설정: <키>을(를) <값>(으)로 설정\n\n<섹션> <키> 제거: <키> 제거\n\n<섹션>은(는) 어떤 섹션(예: logging, guestoperations 또는 guestinfo)이라도 될 수 있습니다.\n<키>은(는) 어떤 구성 키라도 될 수 있습니다.\n<값>은(는) 어떤 값이라도 될 수 있습니다.\n"

help.device = "%1$s: 가상 시스템의 하드웨어 디바이스와 관련된 기능\n사용법: %2$s %3$s <하위 명령> [인수]\ndev는 디바이스 이름입니다.\n\n하위 명령:\n   enable <dev>: 디바이스 dev가 사용되도록 설정합니다.\n   disable <dev>: 디바이스 dev가 사용되지 않도록 설정합니다.\n   list: 사용 가능한 모든 디바이스를 나열합니다.\n   status <dev>: 디바이스의 상태를 출력합니다.\n"

help.disk = "%1$s: 디스크 축소 작업을 수행합니다.\n사용법: %2$s %3$s <하위명령> [인수]\n\n하위 명령:\n   list: 사용 가능한 위치를 나열합니다.\n   shrink <위치>: 지정된 위치에서 파일 시스템을 지우고 축소합니다.\n   shrinkonly: 모든 디스크를 축소합니다.\n   wipe <위치>: 지정된 위치에서 파일 시스템을 지웁니다.\n"

help.globalconf = "%1$s: GuestStore에서의 전역 구성 다운로드를 관리합니다.\n사용법: %2$s %3$s <하위 명령>\n\nESX 게스트 전용 하위 명령:\n사용: 전역 구성 모듈을 사용하도록 설정합니다.\n 사용 안 함: 전역 구성 모듈을 사용하지 않도록 설정합니다.\n 새로 고침: GuestStore에서의 전역 구성 새 다운로드를 트리거합니다.\n 상태: 전역 구성 모듈의 상태를 인쇄합니다.\n"

help.gueststore = "%1$s: GuestStore에서 리소스 콘텐츠 가져오기\사용법: %2$s %3$s <하위 명령>\n\nESX 게스트 전용 하위 명령:\n <리소스 경로> <출력 파일> 콘텐츠 가져오기: GuestStore에서 리소스 콘텐츠를 가져온 후 출력 파일에 저장합니다.\n\n<리소스 경로>은(는) /로 시작하고 GuestStore의 고유한 리소스를 나타냅니다. /로 끝나는 경우 기본 'metadata. json' 리소스를 검색합니다.\n<파일 출력>은(는) 리소스 콘텐츠를 저장할 파일의 경로입니다.\n"

help.hint = "자세한 내용을 보려면 '%1$s %2$s%3$s%4$s'을(를) 시도해 보십시오.\n"

help.info = "%1$s: 호스트에서 게스트 정보 업데이트\n사용법: %2$s %3$s 업데이트 <정보 클래스>\n\n하위 명령:\n   update <정보 클래스>: <정보 클래스>에서 식별한 정보 업데이트\n<정보 클래스>는 '네트워크'일 수 있음\n"

help.logging = "%1$s: 도구 로깅 수정\n사용법: %2$s %3$s 수준 <하위 명령> <서비스 이름> <수준>\n\n하위 명령:\n <서비스 이름> 가져오기: 현재 수준 표시\n 참고: 도구 구성에 수준이 없으면\n전역 구성의 값이 반환됩니다(있을 경우).\n <서비스 이름> <수준> 설정: 현재 수준 설정\n\n<서비스 이름>은(는) vmsvc 또는 vmusr과 같은 지원 서비스일 수 있습니다.\n<수준>은(는) 오류, 중요, 경고, 정보, 메시지, 디버그 중 하나일 수 있습니다.\n 기본값은 %4$s입니다.\n"

help.main = "사용법: %1$s <명령> [옵션] [하위 명령]\n특정 명령에 대한 도움말을 보려면 '%2$s %3$s <명령>'을(를) 입력합니다.\nVMware Tools 버전을 확인하려면 '%4$s -v'를 입력합니다.\nstdout 출력을 표시하지 않으려면 '-q' 옵션을 사용합니다.\n대부분의 명령에 하위 명령이 사용됩니다.\n\n사용 가능한 명령:\n config\n device\n disk(일부 운영 체제에서만 사용할 수 있음)\n globalconf(일부 운영 체제에서만 사용할 수 있음)\n gueststore(일부 운영 체제에서만 사용할 수 있음)\n info\n logging\n script\n stat\n timesync\n upgrade(일부 운영 체제에서만 사용할 수 있음)\n"

help.script = "%1$s: 전원 작업에 대한 응답으로 실행되는 스크립트를 제어합니다.\n사용법: %2$s %3$s <power|resume|suspend|shutdown> <하위 명령> [인수]\n\n하위 명령:\n 사용: 지정된 스크립트가 사용되도록 설정하고 해당 경로를 기본값으로 복원합니다.\n 사용 안 함: 지정된 스크립트가 사용되지 않도록 설정합니다.\n <전체_경로> 설정: 지정된 스크립트를 지정된 경로로 설정합니다.\n 기본값: 지정된 스크립트의 기본 경로를 출력합니다.\n 현재: 지정된 스크립트의 현재 경로를 출력합니다.\n 참고: 경로가 tools.conf에 없는 경우\n 전역 구성의 값이 반환됩니다(있는 경우)\n"

help.stat = "%1$s: 유용한 게스트 및 호스트 정보 인쇄\n사용법: %2$s %3$s <하위 명령>\n\n하위 명령:\n   hosttime: 호스트 시간 인쇄\n   speed: CPU 속도(MHz) 인쇄\nESX 게스트 전용 하위 명령:\n   sessionid: 현재 세션 ID 인쇄\n   balloon: 메모리 벌루닝 정보 인쇄\n   swap: 메모리 스와핑 정보 인쇄\n   memlimit: 메모리 제한 정보 인쇄\n   memres: 메모리 예약 정보 인쇄\n   cpures: CPU 예약 정보 인쇄\n   cpulimit: CPU 제한 정보 인쇄\n   raw [<인코딩> <통계 이름>]: 원시 통계 정보 인쇄\n      <인코딩>은 'text', 'json', 'xml', 'yaml' 중 하나일 수 있습니다.\n      <통계 이름>은 세션, 호스트, 리소스, vscsi 및\n      vnet을 포함합니다(vscsi와 같은 일부 통계는 'vscsi scsi0:0'과 같이 2개의 단어로 구성됨).\n      <인코딩> 및 <통계 이름>\n      인수가 지정되지 않은 경우 사용 가능한 통계를 인쇄합니다.\n"

help.timesync = "%1$s: 게스트 OS의 시간 동기화 제어 기능\n사용법: %2$s %3$s <하위 명령>\n\n하위 명령:\n   enable: 시간 동기화 사용\n   disable: 시간 동기화 사용 안 함\n   status: 시간 동기화 상태 인쇄\n"

help.upgrade = "%1$s: VMware Tools 업그레이드와 관련된 기능입니다.\n사용법: %2$s %3$s <하위 명령> [인수]\n하위 명령:\n   status: VMware Tools 업그레이드 상태를 확인합니다.\n   start: VMware Tools의 자동 업그레이드를 시작합니다.\n\n업그레이드가 수행되려면 VMware Tools 서비스가 실행되어야 합니다.\n"

globalconf.refresh.failed  = "전역 구성 모듈을 사용하지 않도록 설정했으므로 '%1$s'이(가) 실패했습니다.\n"

globalconf.start_vmtools = "%1$s: %2$s 서비스를 시작하는 중입니다.\n"

globalconf.status = "전역 구성 모듈의 상태는 '%1$s'입니다.\n"

globalconf.stop_vmtools = "%1$s: %2$s 서비스를 중지하는 중입니다.\n"

globalconf.update_config = "%1$s: 구성을 업데이트하는 중입니다.\n"

gueststore.content_size = "콘텐츠 크기(바이트): "

gueststore.error.client_lib = "'%1$s'이(가) 실패했습니다. GuestStore 클라이언트 라이브러리 오류: %2$s.\n"

gueststore.progress = "\r진행: %1$d%%"

option.disabled = "사용 안 함"

option.enabled = "사용"

result.error.failed = "'%1$s'이(가) 실패했습니다. 자세한 내용은 %2$s 로그를 확인하십시오.\n"

result.succeeded = "'%1$s'이(가) 성공했습니다.\n"

script.notfound = "%1$s이(가) 없습니다.\n"

script.operation = "작업"

script.unknownop = "작업 %1$s에 대한 스크립트가 없습니다.\n"

script.write.error = "구성을 쓰는 동안 오류 발생: %1$s\n"

stat.balloon.failed = "풍선 메모리를 가져오지 못했습니다. %1$s\n"

stat.cpumax.failed = "CPU 제한을 가져오지 못했습니다. %1$s\n"

stat.cpumin.failed = "CPU 최소값을 가져오지 못했습니다. %1$s\n"

stat.formattime.failed = "호스트 시간의 형식을 지정할 수 없습니다.\n"

stat.get.failed = "통계를 가져오지 못했습니다. %1$s\n"

stat.getsession.failed = "세션 ID를 가져오지 못했습니다. %1$s\n"

stat.getspeed.failed = "프로세서 속도를 가져올 수 없습니다.\n"

stat.gettime.failed = "호스트 시간을 가져올 수 없습니다.\n"

stat.maxmem.failed = "메모리 제한을 가져오지 못했습니다. %1$s\n"

stat.memres.failed = "메모리 예약을 가져오지 못했습니다. %1$s\n"

stat.memswap.failed = "스와핑된 메모리를 가져오지 못했습니다. %1$s\n"

stat.openhandle.failed = "OpenHandle 실패: %1$s\n"

stat.update.failed = "UpdateInfo 실패: %1$s\n"

stat.processorSpeed.info = "%1$uMHz\n"

stat.memoryBalloon.info = "%1$uMB\n"

stat.memoryReservation.info = "%1$uMB\n"

stat.memorySwapped.info = "%1$uMB\n"

stat.memoryLimit.info = "%1$uMB\n"

stat.cpuReservation.info = "%1$uMHz\n"

stat.cpuLimit.info = "%1$uMHz\n"

upgrade.available = "VMware Tools의 새 버전을 사용할 수 있습니다.\n"

upgrade.error.check_error = "업그레이드 제공 여부를 확인하는 동안 오류가 발생했습니다.\n"

upgrade.error.error = "VMware Tools 업그레이드를 시작하는 동안 오류가 발생했습니다.\n"

upgrade.error.not_supported = "호스트에서 VMware Tools의 자동 업그레이드를 지원하지 않습니다.\n"

upgrade.error.unknown_reply = "예기치 않은 호스트 응답: %1$s\n"

upgrade.started = "업그레이드가 시작되었습니다.\n"

upgrade.uptodate = "VMware Tools가 최신 버전입니다.\n"

  070701000003F0000081A40000000000000000000000016822550500002AB6000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/zh_CN.vmsg    ##########################################################
# Copyright (c) 2010-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "命令"

arg.config.key = "配置项"

arg.config.operation = "配置操作"

arg.config.section = "配置节"

arg.config.value = "配置值"

arg.devicename = "设备名称"

arg.info.class = "信息的信息类"

arg.info.subcommand = "信息操作"

arg.logging.level = "日志记录级别"

arg.logging.service = "日志记录服务名"

arg.logging.subcommand = "日志记录操作"

arg.mountpoint = "挂载点"

arg.scriptpath = "脚本路径"

arg.scripttype = "脚本类型"

arg.subcommand = "子命令"

device.connect.error = "无法连接设备 %1$s。\n"

device.disconnect.error = "无法断开与设备 %1$s 的连接。\n"

device.notfound = "获取接口信息时出错: 未找到设备。\n"

disk.shrink.canceled = "磁盘缩小已取消。\n"

disk.shrink.complete = "磁盘缩小完成。\n"

disk.shrink.conflict = "错误，工具箱认为已启用磁盘压缩，而主机认为已禁用磁盘压缩。\n\n 请关闭再重新打开工具箱，以将其与主机同步。\n"

disk.shrink.disabled = "为该虚拟机禁用了磁盘压缩。\n\n由于链接的克隆、链接克隆的父级、\n预分配的磁盘、快照或其他因素，已禁用压缩。\n有关详细信息，请参见用户手册。\n"

disk.shrink.error = "缩小时出现错误: %1$s\n"

disk.shrink.incomplete = "缩小未完成。\n"

disk.shrink.partition.error = "无法收集分区数据。\n"

disk.shrink.partition.notfound = "无法找到分区 %1$s\n"

disk.shrink.partition.unsupported = "分区 %1$s 不可缩小\n"

disk.shrink.unavailable = "压缩功能不可用，\n\n这是因为您运行较低版本的 VMware 产品，或者打开的通信通道太多。\n\n如果运行的是较低版本的 VMware 产品，应考虑进行升级。\n\n如果打开的通信通道太多，应关闭虚拟机的电源，然后重新打开虚拟机的电源。\n"

disk.shrink.ignoreFreeSpaceWarnings = "请忽略压缩过程期间有关磁盘空间的任何警告。\n"

disk.wipe.ignoreFreeSpaceWarnings = "请忽略擦除过程期间有关磁盘空间的任何警告。\n"

disk.wiper.file.error = "错误，无法创建擦除器文件。\n"

disk.wiper.progress = "\r进度：%1$d"

error.message = "错误: %1$s\n"

error.missing = "%1$s: 缺失 %2$s\n"

error.noadmin.posix = "%1$s: 您必须是 root 用户才能执行 %2$s 操作。\n"

error.noadmin.win = "%1$s: 需要具有管理员权限才能执行 %2$s 操作。\n请使用管理员命令提示符完成这些任务。\n"

error.novirtual = "%1$s 必须在虚拟机内部运行。\n"

error.unknown = "%1$s: %2$s“%3$s”未知\n"

help.config = "%1$s: 修改 Tools 配置\n用法: %2$s %3$s <子命令>\n\n子命令:\n   get <节> <项>: 显示 <项> 的当前值\n   注意: 如果 tools.conf 中不存在 <项>，\n  则返回全局配置中的值 (如果存在)\n   set <节> <项> <值>: 将 <项> 设置为 <值>\n\n   remove <节> <项>: 移除 <项>\n\n<节> 可以是受支持的任何节，如 logging、guestoperations 或 guestinfo。\n<项> 可以是任何配置项。\n<值> 可以是任何值。\n"

help.device = "%1$s: 与虚拟机的硬件设备相关的功能\n用法: %2$s %3$s <子命令> [参数]\ndev 是设备的名称。\n\n子命令:\n   enable <dev>: 启用设备 dev\n   disable <dev>: 禁用设备 dev\n   list: 列出所有可用的设备\n   status <dev>: 打印设备的状态\n"

help.disk = "%1$s: 执行磁盘压缩操作\n用法: %2$s %3$s <子命令> [参数]\n\n子命令:\n   list: 列出可用的位置\n   shrink <位置>: 擦除并压缩指定位置的文件系统\n   shrinkonly: 压缩所有磁盘\n   wipe <位置>: 擦除指定位置的文件系统\n"

help.globalconf = "%1$s: 管理从 GuestStore 下载的全局配置\n用法: %2$s %3$s <子命令>\n\n仅 ESX 客户机子命令:\n   enable: 启用全局配置模块\n   disable: 禁用全局配置模块\n   refresh: 从 GuestStore 触发全局配置的新下载\n   status: 打印全局配置模块的状态\n"

help.gueststore = "%1$s: 从 GuestStore 获取资源内容\n用法: %2$s %3$s <子命令>\n\n仅 ESX 客户机子命令:\n   getcontent <资源路径> <输出文件>: 从 GuestStore 获取资源内容并保存到输出文件。\n\n<资源路径> 以“/”开头，并代表 GuestStore 中的唯一资源。如果以“/”结尾，则默认检索底层的“metadata.json”资源。\n<输出文件> 是将资源内容保存到的文件的路径。\n"

help.hint = "有关详细信息，请尝试“%1$s %2$s%3$s%4$s”。\n"

help.info = "%1$s: 更新主机上的来宾信息\n用法: %2$s %3$s update <信息类>\n\n子命令:\n   update <信息类>: 更新由 <信息类> 标识的信息\n<信息类> 可以为“network”\n"

help.logging = "%1$s: 修改 Tools 日志记录\n用法: %2$s %3$s level <子命令> <服务名> <级别>\n\n子命令:\n   get <服务名>: 显示当前的级别\n   注意: 如果 tools.conf 中不存在该级别，\n  则返回全局配置中的值 (如果存在)\n   set <服务名> <级别>: 设置当前级别\n\n<服务名> 可以是受支持的任何服务，包括 vmsvc 或 vmusr\n<级别> 可以是 error、critical、warning、info、message 或 debug 中的一种\n   默认为 %4$s\n"

help.main = "用法: %1$s <命令> [选项] [子命令]\n键入“%2$s %3$s <命令>”可获取有关特定命令的帮助。\n键入“%4$s -v”可查看 VMware Tools 版本。\n使用“-q”选项可取消 stdout 输出。\n大多数命令都有子命令。\n\n可用命令:\n   config\n   device\n   disk (并非所有操作系统都支持)\n   globalconf (并非所有操作系统都支持)\n   gueststore (并非所有操作系统都支持)\n   info\n   logging\n   script\n   stat\n   timesync\n   upgrade (并非所有操作系统都支持)\n"

help.script = "%1$s: 控制脚本运行以响应打开电源操作\n用法: %2$s %3$s <power|resume|suspend|shutdown> <子命令> [参数]\n\n子命令:\n   enable: 启用给定脚本，并将其路径恢复为默认值\n   disable: 禁用给定脚本\n   set <完整路径>: 将给定脚本设置为给定路径\n   default: 打印给定脚本的默认路径\n   current: 打印给定脚本的当前路径\n   注意: 如果 tools.conf 中不存在该路径，\n   则返回全局配置中的值 (如果存在)\n"

help.stat = "%1$s: 打印有用的来宾和主机信息\n用法: %2$s %3$s <子命令>\n\n子命令:\n   hosttime: 打印主机时间\n   speed: 打印 CPU 速度 (以 MHz 为单位)\n仅 ESX 来宾子命令:\n   sessionid: 打印当前会话 id\n   balloon: 打印内存扩大信息\n   swap: 打印内存交换信息\n   memlimit: 打印内存限制信息\n   memres: 打印内存保留信息\n   cpures: 打印 CPU 保留信息\n   cpulimit: 打印 CPU 限制信息\n   raw [<编码> <统计名称>]: 打印原始统计信息\n      <编码> 可以为“text”、“json”、“xml”和“yaml”之一。\n      <统计名称> 包括 session、host、resources、vscsi 和\n      vnet (诸如 vscsi 之类的某些统计由两个单词组成，例如“vscsi scsi0:0”)。\n      如果未指定 <编码> 和 <统计名称> 参数，\n      则会打印可用的统计信息。\n"

help.timesync = "%1$s: 用于控制来宾操作系统上的时间同步的功能\n用法: %2$s %3$s <子命令>\n\n子命令:\n   enable: 启用时间同步\n   disable: 禁用时间同步\n   status: 打印时间同步状态\n"

help.upgrade = "%1$s: 与升级 VMware Tools 相关的功能。\n用法: %2$s %3$s <子命令> [参数]\n子命令:\n   status: 检查 VMware Tools 升级状态。\n   start: 启动 VMware Tools 自动升级。\n\n要使升级正常进行，需要运行 VMware Tools 服务。\n"

globalconf.refresh.failed  = "“%1$s”失败，因为 globalconf 模块已禁用。\n"

globalconf.start_vmtools = "%1$s: 正在启动 %2$s 服务。\n"

globalconf.status = "globalconf 模块的状态为“%1$s”\n"

globalconf.stop_vmtools = "%1$s: 正在停止 %2$s 服务。\n"

globalconf.update_config = "%1$s: 正在更新配置。\n"

gueststore.content_size = "内容大小 (字节): "

gueststore.error.client_lib = "“%1$s”失败，GuestStore 客户端库错误: %2$s。\n"

gueststore.progress = "\r进度: %1$d%%"

option.disabled = "已禁用"

option.enabled = "已启用"

result.error.failed = "“%1$s”失败，请查看 %2$s 日志，以查看更多信息。\n"

result.succeeded = "“%1$s”成功。\n"

script.notfound = "%1$s 不存在。\n"

script.operation = "操作"

script.unknownop = "没有用于操作 %1$s 的脚本。\n"

script.write.error = "写入配置时出错: %1$s\n"

stat.balloon.failed = "无法获取虚拟增长内存: %1$s\n"

stat.cpumax.failed = "无法获取 CPU 限制: %1$s\n"

stat.cpumin.failed = "无法获取 CPU 下限: %1$s\n"

stat.formattime.failed = "无法格式化主机时间。\n"

stat.get.failed = "无法获取统计信息: %1$s\n"

stat.getsession.failed = "无法获取会话 ID: %1$s\n"

stat.getspeed.failed = "无法获取处理器速度。\n"

stat.gettime.failed = "无法获取主机时间。\n"

stat.maxmem.failed = "无法获取内存限制: %1$s\n"

stat.memres.failed = "无法获取内存预留: %1$s\n"

stat.memswap.failed = "无法获取已交换内存: %1$s\n"

stat.openhandle.failed = "OpenHandle 失败: %1$s\n"

stat.update.failed = "UpdateInfo 失败: %1$s\n"

stat.processorSpeed.info = "%1$u MHz\n"

stat.memoryBalloon.info = "%1$u MB\n"

stat.memoryReservation.info = "%1$u MB\n"

stat.memorySwapped.info = "%1$u MB\n"

stat.memoryLimit.info = "%1$u MB\n"

stat.cpuReservation.info = "%1$u MHz\n"

stat.cpuLimit.info = "%1$u MHz\n"

upgrade.available = "新版本的 VMware Tools 可用。\n"

upgrade.error.check_error = "检查升级可用性时出错。\n"

upgrade.error.error = "启动 VMware Tools 升级时出错。\n"

upgrade.error.not_supported = "主机不支持自动升级 VMware Tools。\n"

upgrade.error.unknown_reply = "主机回复异常: %1$s\n"

upgrade.started = "升级已开始。\n"

upgrade.uptodate = "VMware Tools 为最新版本。\n"

  070701000003F1000081A40000000000000000000000016822550500002B42000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/toolbox/l10n/zh_TW.vmsg    ##########################################################
# Copyright (c) 2010-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

arg.command = "命令"

arg.config.key = "組態金鑰"

arg.config.operation = "組態作業"

arg.config.section = "組態區段"

arg.config.value = "組態值"

arg.devicename = "裝置名稱"

arg.info.class = "資訊的資訊類別"

arg.info.subcommand = "資訊作業"

arg.logging.level = "記錄層級"

arg.logging.service = "記錄服務名稱"

arg.logging.subcommand = "記錄作業"

arg.mountpoint = "掛接點"

arg.scriptpath = "指令碼路徑"

arg.scripttype = "指令碼類型"

arg.subcommand = "子命令"

device.connect.error = "無法連線裝置 %1$s。\n"

device.disconnect.error = "無法中斷與裝置 %1$s 的連線。\n"

device.notfound = "擷取介面資訊時發生錯誤: 找不到裝置。\n"

disk.shrink.canceled = "已取消磁碟壓縮。\n"

disk.shrink.complete = "磁碟壓縮完成。\n"

disk.shrink.conflict = "錯誤，工具箱認為已啟用磁碟壓縮，但主機認為已停用磁碟壓縮。\n\n請關閉工具箱後再重新開啟，使其與主機同步。\n"

disk.shrink.disabled = "此虛擬機器已停用磁碟壓縮。\n\n針對連結複製、連結複製的父系、\n預先配置的磁碟及快照，或由於其他因素，已停用壓縮。\n如需詳細資訊，請參閱使用者手冊。\n"

disk.shrink.error = "壓縮時發生錯誤: %1$s\n"

disk.shrink.incomplete = "未完成壓縮。\n"

disk.shrink.partition.error = "無法收集磁碟分割資料。\n"

disk.shrink.partition.notfound = "找不到磁碟分割 %1$s\n"

disk.shrink.partition.unsupported = "磁碟分割 %1$s 不可壓縮\n"

disk.shrink.unavailable = "壓縮功能無法使用，\n\n因為您正在執行舊版 VMware 產品，或者已開啟過多通訊通道。\n\n如果您正在執行舊版 VMware 產品，請考慮進行升級。\n\n如果已開啟過多通訊通道，請關閉虛擬機器的電源，然後重新開啟其電源。\n"

disk.shrink.ignoreFreeSpaceWarnings = "請忽略壓縮程序期間有關磁碟空間的任何警告。\n"

disk.wipe.ignoreFreeSpaceWarnings = "請忽略抹除程序期間有關磁碟空間的任何警告。\n"

disk.wiper.file.error = "錯誤，無法建立抹除器檔案。\n"

disk.wiper.progress = "\r進度: %1$d"

error.message = "錯誤: %1$s\n"

error.missing = "%1$s: 遺失 %2$s\n"

error.noadmin.posix = "%1$s: 您必須是根使用者才能執行 %2$s 作業。\n"

error.noadmin.win = "%1$s: 需要管理員權限才能執行 %2$s 作業。\n請使用管理員命令提示字元來完成這些工作。\n"

error.novirtual = "%1$s 必須在虛擬機器中執行。\n"

error.unknown = "%1$s: 未知 %2$s「%3$s」\n"

help.config = "%1$s: 修改工具組態\n用法: %2$s %3$s <子命令>\n\n子命令:\n   get <區段> <金鑰>: 顯示目前 <金鑰> 的值\n   備註: 如果 tools.conf 中未顯示 <金鑰>，則其\n   在全域組態中的值會在顯示時傳回\n   set <區段> <金鑰> <值>: 設定 <金鑰> 至 <值>\n\n   remove <區段> <金鑰>: remove <金鑰>\n\n<區段> 可為任何受支援的區段，例如記錄、guestoperations 或 guestinfo。\n<金鑰> 可為任何組態金鑰。\n<值> 可為任何值。\n"

help.device = "%1$s: 與虛擬機器之硬體裝置相關的功能\n用法: %2$s %3$s <subcommand> [args]\ndev 是裝置名稱。\n\n子命令:\n   enable <dev>: 啟用裝置 dev\n   disable <dev>: 停用裝置 dev\n   list: 列出所有可用裝置\n   status <dev>: 列印裝置狀態\n"

help.disk = "%1$s: 執行磁碟壓縮作業\n用法: %2$s %3$s <subcommand> [args]\n\n子命令:\n   list: 列出可用位置\n   shrink <location>: 抹除和壓縮指定位置的檔案系統\n   shrinkonly: 壓縮所有磁碟\n   wipe <location>: 抹除指定位置的檔案系統\n"

help.globalconf = "%1$s: 從 GuestStore 管理全域組態下載\n用法: %2$s %3$s <子命令>\n\n僅 ESX 客體的子命令:\n   enable: 啟用全域組態模組。\n   disable: 停用全域組態模組\n   refresh: 從 GuestStore 觸發全域組態的新下載\n   status: 列印全域組態模組的狀態\n"

help.gueststore = "%1$s: 從 GuestStore 取得資源內容\n用法: %2$s %3$s <子命令>\n\n僅 ESX 客體的子命令:\n   getcontent < 資源路徑 > < 輸出檔案 >: 從 GuestStore 取得資源內容並儲存至輸出檔案。\n\n<資源路徑> 開頭為 /，代表 GuestStore 中的唯一資源。如果結尾為 /，則預設為擷取基礎的「metadata json」資源。\n\n<輸出檔案> 是要儲存資源內容的目標檔案路徑。\n"

help.hint = "如需詳細資訊，請嘗試「%1$s%2$s%3$s%4$s」。\n"

help.info = "%1$s: 更新主機上的客體資訊\n用法: %2$s %3$s update <資訊類別>\n\n子命令:\n   update <資訊類別>: 更新依 <資訊類別> 識別之資訊\n<資訊類別> 可為「network」\n"

help.logging = "%1$s: 修改工具記錄\n用法: %2$s %3$s 層級 <子命令> <服務名稱> <層級>\n\n子命令:\n   get <服務名稱>: 顯示目前層級\n   備註: 如果 tools.conf 中未顯示，則其\n   在全域組態中的值會在顯示時傳回\n   set <服務名稱> <層級>: 設定目前層級\n\n<服務名稱> 可為任何受支援的服務，例如 vmsvc 或 vmusr\n<層級> 可為錯誤、嚴重、警告、資訊、訊息、偵錯之一\n   預設為 %4$s\n"

help.main = "用法: %1$s <命令> [選項] [子命令]\n輸入「%2$s %3$s <命令>」以取得特定命令的說明。\n輸入「%4$s -v」以查看 VMware Tools 版本。\n使用「-q」選項以隱藏 stdout 輸出。\n大多數命令都有子命令。\n\n可用命令:\n   config\n   device\n   disk (並非所有作業系統皆可使用)\n   globalconf (並非所有作業系統皆可使用)\n   gueststore (並非所有作業系統皆可使用)\n   info\n   logging\n   script\n   stat\n   timesync\n   upgrade (並非所有作業系統皆可使用)\n"

help.script = "%1$s: 控制指令碼執行以回應電源作業\n用法: %2$s %3$s <power|resume|suspend|shutdown> <子命令> [args]\n\n子命令:\n   enable: 啟用指定指令碼，並將其路徑還原為預設值\n   disable: 停用指定指令碼\n   set <full_path>: 將指定指令碼設定為指定路徑\n   default: 列印指定指令碼的預設路徑\n   current: 列印指定指令碼的目前路徑\n   備註: 如果 tools.conf 中未顯示路徑，則其\n   在全域組態中的值會在顯示時傳回\n"

help.stat = "%1$s: 列印實用的客體和主機資訊\n用法: %2$s %3$s <子命令>\n\n子命令:\n   hosttime: 列印主機時間\n   speed: 列印 CPU 速度 (MHz)\n僅 ESX 客體子命令:\n   sessionid: 列印目前的工作階段識別碼\n   balloon: 列印記憶體佔用資訊\n   swap: 列印記憶體交換資訊\n   memlimit: 列印記憶體限制資訊\n   memres: 列印記憶體保留區資訊\n   cpures: 列印 CPU 保留區資訊\n   cpulimit: 列印 CPU 限制資訊\n   raw [<代碼> <統計名称>]: 列印原始統計資訊\n      <代碼> 可為「text」、「json」、「xml」、「yaml」其中之一。\n      <統計名称> 包含「session」、「host」、「resources」、「vscsi」和\n      vnet」(某些狀態如 vscsi 為兩個字，例如「vscsi scsi0:0」)。\n      如果並未指定引數 <代碼> 和 <統計名称>\n      則列印可用的統計資料。\n"

help.timesync = "%1$s: 用於控制客體作業系統時間同步的功能\n用法: %2$s %3$s <subcommand>\n\n子命令:\n   enable: 啟用時間同步\n   disable: 停用時間同步\n   status: 列印時間同步狀態\n"

help.upgrade = "%1$s: 與升級 VMware Tools 相關的功能。\n用法: %2$s %3$s <subcommand> [args]\n子命令:\n   status: 檢查 VMware Tools 升級狀態。\n   start: 啟動 VMware Tools 自動升級。\n\n若要升級作業正常運作，需要執行 VMware Tools 服務。\n"

globalconf.refresh.failed  = "「%1$s」失敗，因為 globalconf 模組已停用。\n"

globalconf.start_vmtools = "%1$s: 正在啟動 %2$s 服務。\n"

globalconf.status = "globalconf 模組的狀態為「%1$s」\n"

globalconf.stop_vmtools = "%1$s: 正在停止 %2$s 服務。\n"

globalconf.update_config = "%1$s: 正在更新組態。\n"

gueststore.content_size = "內容大小 (位元組): "

gueststore.error.client_lib = "「%1$s」失敗，GuestStore 用戶端程式庫錯誤: %2$s。\n"

gueststore.progress = "\r進度: %1$d%%"

option.disabled = "已停用"

option.enabled = "已啟用"

result.error.failed = "「%1$s」失敗，請檢查 %2$s 記錄以取得詳細資訊。\n"

result.succeeded = "「%1$s」成功。\n"

script.notfound = "%1$s 不存在。\n"

script.operation = "作業"

script.unknownop = "沒有用於作業 %1$s 的指令碼。\n"

script.write.error = "寫入組態時發生錯誤: %1$s\n"

stat.balloon.failed = "無法取得已佔用的記憶體: %1$s\n"

stat.cpumax.failed = "無法取得 CPU 限制: %1$s\n"

stat.cpumin.failed = "無法取得 CPU 下限: %1$s\n"

stat.formattime.failed = "無法格式化主機時間。\n"

stat.get.failed = "無法取得統計資料: %1$s\n"

stat.getsession.failed = "無法取得工作階段識別碼: %1$s\n"

stat.getspeed.failed = "無法取得處理器速度。\n"

stat.gettime.failed = "無法取得主機時間。\n"

stat.maxmem.failed = "無法取得記憶體限制: %1$s\n"

stat.memres.failed = "無法取得記憶體保留區: %1$s\n"

stat.memswap.failed = "無法取得已交換的記憶體: %1$s\n"

stat.openhandle.failed = "OpenHandle 失敗: %1$s\n"

stat.update.failed = "UpdateInfo 失敗: %1$s\n"

stat.processorSpeed.info = "%1$u MHz\n"

stat.memoryBalloon.info = "%1$u MB\n"

stat.memoryReservation.info = "%1$u MB\n"

stat.memorySwapped.info = "%1$u MB\n"

stat.memoryLimit.info = "%1$u MB\n"

stat.cpuReservation.info = "%1$u MHz\n"

stat.cpuLimit.info = "%1$u MHz\n"

upgrade.available = "新版本的 VMware Tools 可供使用。\n"

upgrade.error.check_error = "檢查升級可用性時發生錯誤。\n"

upgrade.error.error = "開始 VMware Tools 升級時發生錯誤。\n"

upgrade.error.not_supported = "主機不支援 VMware Tools 自動升級。\n"

upgrade.error.unknown_reply = "未預期的主機回復: %1$s\n"

upgrade.started = "升級已開始。\n"

upgrade.uptodate = "VMware Tools 為最新版本。\n"

  070701000003F2000081A40000000000000000000000016822550500003A7A000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolbox-cmd.c  /*********************************************************
 * Copyright (c) 2008-2021,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolbox-cmd.c --
 *
 *     The toolbox app with a command line interface.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <locale.h>
#include <glib/gstdio.h>

#include "toolboxCmdInt.h"
#include "toolboxcmd_version.h"
#include "system.h"
#include "unicode.h"
#include "vmcheck.h"
#include "vmware/tools/guestrpc.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/log.h"
#include "vmware/tools/utils.h"
#if defined(_WIN32)
#include "vmware/tools/win32util.h"
#include "globalConfig.h"
#endif

#include "vm_version.h"
#include "vm_product_versions.h"

#include "vm_version.h"
#include "embed_version.h"
VM_EMBED_VERSION(TOOLBOXCMD_VERSION_STRING);


typedef int (*ToolboxCmdFunc)(char **argv,
                              int argc,
                              gboolean quiet);

typedef void (*ToolboxHelpFunc)(const char *progName,
                                const char *cmd);


/*
 * Local Data
 */

const typedef struct CmdTable {
   const char *command;       /* The name of the command. */
   ToolboxCmdFunc func;       /* The function to execute. */
   Bool requireArguments;     /* The function requires arguments. */
   Bool requireRoot;          /* Indicates whether root is required. */
   ToolboxHelpFunc helpFunc;  /* The help function associated with the command. */
} CmdTable;


static struct option long_options[] = {
   /* quiet sets a flag */
   { "quiet", no_argument, 0, 'q' },
   /* These options don't set a flag.
      We distinguish them by their indices. */
   { "help", no_argument, 0, 'h' },
   { "version", no_argument, 0, 'v' },
   { 0, 0, 0, 0 } };

static gboolean gQuiet = FALSE;
static const char *options = "hqv";

/*
 * Local Functions
 */

static int
HelpCommand(char **argv,
            int argc,
            gboolean quiet);

static void
ToolboxCmdHelp(const char *progName,
               const char *cmd);


/*
 * The commands table.
 * Must go after function declarations
 */
static CmdTable commands[] = {
   { "timesync",   TimeSync_Command,   TRUE,  FALSE, TimeSync_Help},
   { "script",     Script_Command,     FALSE, TRUE,  Script_Help},
#if !defined(USERWORLD)
   { "disk",       Disk_Command,       TRUE,  TRUE,  Disk_Help},
#endif
   { "stat",       Stat_Command,       TRUE,  FALSE, Stat_Help},
   { "device",     Device_Command,     TRUE,  FALSE, Device_Help},
#if defined(_WIN32) || \
   (defined(__linux__) && !defined(OPEN_VM_TOOLS) && !defined(USERWORLD))
   { "upgrade",    Upgrade_Command,    TRUE,  TRUE,  Upgrade_Help},
#endif
#if defined(_WIN32) || \
   (defined(__linux__) && !defined(USERWORLD))
   { "gueststore", GuestStore_Command, TRUE,  FALSE, GuestStore_Help},
#endif
#if defined(GLOBALCONFIG_SUPPORTED)
   { "globalconf", GlobalConf_Command, TRUE,  TRUE,  GlobalConf_Help},
#endif
   { "logging",    Logging_Command,    TRUE,  TRUE,  Logging_Help},
   { "info",       Info_Command,       TRUE,  TRUE,  Info_Help},
   { "config",     Config_Command,     TRUE,  TRUE,  Config_Help},
   { "help",       HelpCommand,        FALSE, FALSE, ToolboxCmdHelp},
};


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsCmd_MissingEntityError --
 *
 *      Print out error message regarding missing argument.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
ToolsCmd_MissingEntityError(const char *name,     // IN: command name (argv[0])
                            const char *entity)   // IN: what is missing
{
   g_printerr(SU_(error.missing, "%s: Missing %s\n"), name, entity);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsCmd_Print --
 *
 *      Prints a message to stdout unless quiet output was requested.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
ToolsCmd_Print(const char *fmt,
               ...)
{
   if (!gQuiet) {
      gchar *str;
      va_list args;

      va_start(args, fmt);
      g_vasprintf(&str, fmt, args);
      va_end(args);

      g_print("%s", str);
      g_free(str);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsCmd_PrintErr --
 *
 *      Prints a message to stderr unless quiet output was requested.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
ToolsCmd_PrintErr(const char *fmt,
                  ...)
{
   if (!gQuiet) {
      gchar *str;
      va_list args;

      va_start(args, fmt);
      g_vasprintf(&str, fmt, args);
      va_end(args);

      g_printerr("%s", str);
      g_free(str);
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsCmd_SendRPC --
 *
 *    Sends an RPC message to the host.
 *
 * Results:
 *    The return value from the RPC.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

gboolean
ToolsCmd_SendRPC(const char *rpc,      // IN
                 size_t rpcLen,        // IN
                 char **result,        // OUT
                 size_t *resultLen)    // OUT
{
   char *lrpc = (char *) rpc;
   RpcChannel *chan = RpcChannel_New();
   gboolean ret = RpcChannel_Start(chan);

   if (!ret) {
      g_warning("Error starting RPC channel.");
      goto exit;
   }

   ret = RpcChannel_Send(chan, lrpc, rpcLen, result, resultLen);

exit:
   RpcChannel_Destroy(chan);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsCmd_FreeRPC --
 *
 *    Free the memory allocated for the results from
 *    ToolsCmd_SendRPC calls.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

void
ToolsCmd_FreeRPC(void *ptr)      // IN
{
   RpcChannel_Free(ptr);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolsCmd_UnknownEntityError --
 *
 *      Print out error message regarding unknown argument.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
ToolsCmd_UnknownEntityError(const char *name,    // IN: command name (argv[0])
                            const char *entity,  // IN: what is unknown
                            const char *str)     // IN: errorneous string
{
   g_printerr(SU_(error.unknown, "%s: Unknown %s '%s'\n"), name, entity, str);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ToolboxCmdHelp --
 *
 *      Print out usage information to stdout.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static void
ToolboxCmdHelp(const char *progName,   // IN
               const char *cmd)        // IN
{
   g_print(SU_(help.main, "Usage: %s <command> [options] [subcommand]\n"
                          "Type '%s %s <command>' for help on a specific command.\n"
                          "Type '%s -v' to see the VMware Tools version.\n"
                          "Use '-q' option to suppress stdout output.\n"
                          "Most commands take a subcommand.\n\n"
                          "Available commands:\n"
                          "   config\n"
                          "   device\n"
                          "   disk (not available on all operating systems)\n"
                          "   globalconf (not available on all operating systems)\n"
                          "   gueststore (not available on all operating systems)\n"
                          "   info\n"
                          "   logging\n"
                          "   script\n"
                          "   stat\n"
                          "   timesync\n"
                          "   upgrade (not available on all operating systems)\n"),
           progName, progName, cmd, progName);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HelpCommand --
 *
 *      Handle and parse help commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns other exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
HelpCommand(char **argv,      // IN: Command line arguments
            int argc,         // IN: Length of argv
            gboolean quiet)   // IN
{
   int retval = EXIT_SUCCESS;

   if (++optind < argc) {
      int i;

      for (i = 0; i < ARRAYSIZE(commands); i++) {
         if (toolbox_strcmp(commands[i].command, argv[optind]) == 0) {
            commands[i].helpFunc(argv[0], commands[i].command);
            return EXIT_SUCCESS;
         }
      }
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
      retval = EX_USAGE;
   }

   ToolboxCmdHelp(argv[0], argv[optind - 1]);
   return retval;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ParseCommand --
 *
 *      Parse the non optional command line arguments.
 *
 * Results:
 *      Returns the CmdTable pointer on success or calls exit on error.
 *
 * Side effects:
 *      Calls exit on parse errors.
 *
 *-----------------------------------------------------------------------------
 */

static CmdTable *
ParseCommand(char **argv, // IN: Command line arguments
             int argc)    // IN: Length of command line arguments
{
   int i;

   for (i = 0; i < ARRAYSIZE(commands); i++) {
      if (toolbox_strcmp(commands[i].command, argv[optind]) == 0) {
         return &commands[i];
      }
   }

   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * main --
 *
 *      This is main
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns different exit code on failure.
 *
 * Side effects:
 *      The vmware-toolbox-cmd will run and do a variety of tricks for your
 *      amusement.
 *
 *-----------------------------------------------------------------------------
 */

int
#if defined(_WIN32)
wmain(int argc,         // IN: length of command line arguments
      wchar_t **wargv)  // IN: Command line arguments
#else
main(int argc,    // IN: length of command line arguments
     char **argv) // IN: Command line arguments
#endif
{
   Bool show_help = FALSE;
   Bool show_version = FALSE;
   CmdTable *cmd = NULL;
   GKeyFile *conf = NULL;
   int c;
   int retval = EXIT_FAILURE;

#if defined(_WIN32)
   char **argv;

   WinUtil_EnableSafePathSearching(TRUE);
#   if defined(VMX86_RELEASE)
   WinUtil_VerifyExePathW();
#   endif

   Unicode_InitW(argc, wargv, NULL, &argv, NULL);
#else
   Unicode_Init(argc, &argv, NULL);
#endif

   setlocale(LC_ALL, "");
   VMTools_LoadConfig(NULL, G_KEY_FILE_NONE, &conf, NULL);

   TOOLBOXCMD_LOAD_GLOBALCONFIG(conf)

   VMTools_ConfigLogging("toolboxcmd", conf, FALSE, FALSE);
   VMTools_BindTextDomain(VMW_TEXT_DOMAIN, NULL, NULL);

   if (!VmCheck_IsVirtualWorld()) {
      g_printerr(SU_(error.novirtual, "%s must be run inside a virtual machine.\n"),
                 argv[0]);
      goto exit;
   }

   /*
    * Parse the command line optional arguments
    */
   while (1) {
      int option_index = 0;

      c = getopt_long(argc, argv, options, long_options, &option_index);

      /* Detect the end of the options. */
      if (c == -1) {
         break;
      }

      switch (c) {
      case 'h':
         show_help = TRUE;
         break;

      case 'v':
         show_version = TRUE;
         break;

      case 'q':
         gQuiet = TRUE;
         break;

      case '?':
         /* getopt_long already printed an error message. */
         g_printerr(SU_(help.hint, "Try '%s %s%s%s' for more information.\n"),
                    argv[0], "-h", "", "");
         goto exit;

      default:
         goto exit;
      }
   }

   if (show_version) {
      g_print("%s (%s)\n", TOOLBOXCMD_VERSION_STRING, BUILD_NUMBER);
      retval = EXIT_SUCCESS;
   } else if (show_help) {
      ToolboxCmdHelp(argv[0], "help");
      retval = EXIT_SUCCESS;
   } else {
      /* Process any remaining command line arguments (not options), and
       * execute corresponding command
       */
      if (optind >= argc) {
         ToolsCmd_MissingEntityError(argv[0], SU_(arg.command, "command"));
         retval = EX_USAGE;
      } else if ((cmd = ParseCommand(argv, argc)) == NULL) {
         ToolsCmd_UnknownEntityError(argv[0], SU_(arg.command, "command"), argv[optind]);
         retval = EX_USAGE;
      } else if (cmd->requireRoot && !System_IsUserAdmin()) {
#if defined(_WIN32)
         g_printerr(SU_(error.noadmin.win,
                        "%s: Administrator permissions are needed to perform %s operations.\n"
                        "Use an administrator command prompt to complete these tasks.\n"),
                    argv[0], cmd->command);

#else
         g_printerr(SU_(error.noadmin.posix,
                        "%s: You must be root to perform %s operations.\n"),
                    argv[0], cmd->command);
#endif
         retval = EX_NOPERM;
      } else if (cmd->requireArguments && ++optind >= argc) {
         ToolsCmd_MissingEntityError(argv[0], SU_(arg.subcommand, "subcommand"));
         retval = EX_USAGE;
      } else {
         retval = cmd->func(argv, argc, gQuiet);
      }

      if (retval == EX_USAGE && (cmd == NULL || strcmp(cmd->command, "help"))) {
         g_printerr(SU_(help.hint, "Try '%s %s%s%s' for more information.\n"),
                    argv[0], "help", cmd ? " " : "", cmd ? cmd->command : "");
      }
   }

exit:
   if (conf != NULL) {
      g_key_file_free(conf);
   }

   return retval;
}
  070701000003F3000081A400000000000000000000000168225505000012F4000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxCmdInt.h    /*********************************************************
 * Copyright (c) 2008-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolbox-cmd.h --
 *
 *     Common defines used by the toolbox-cmd.
 */
#ifndef _TOOLBOX_CMD_INT_H_
#define _TOOLBOX_CMD_INT_H_

#define G_LOG_MAIN "toolboxcmd"
#define VMW_TEXT_DOMAIN G_LOG_MAIN

#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#   include "getoptwin32.h"
#else
#   include <getopt.h>
#   include <sysexits.h>
#   include <unistd.h>
#endif

#if defined(_WIN32) || (defined(__linux__) && !defined(USERWORLD))
#include "globalConfig.h"
#endif

#include "vmGuestLib.h"

/*
 * Some platforms (such as Win32) don't have sysexits.h and thus don't have
 * generic program exit codes.
 */

#ifndef EX_USAGE
#define EX_USAGE 64 /* command line usage error */
#endif

#ifndef EX_UNAVAILABLE
#define EX_UNAVAILABLE 69 /* service unavailable */
#endif

#ifndef EX_SOFTWARE
#define EX_SOFTWARE 70 /* internal software error */
#endif

#ifndef EX_OSERR
#define EX_OSERR 71 /* system error (e.g., can't fork) */
#endif

#ifndef EX_OSFILE
#define EX_OSFILE 72 /* critical OS file missing */
#endif

#ifndef EX_TEMPFAIL
#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
#endif

#ifndef EX_NOPERM
#define EX_NOPERM 77 /* permission denied */
#endif

#ifndef VM_EX_INTERRUPT
#define VM_EX_INTERRUPT 130 /* SIGINT received */
#endif

/*
 * We want to have commands and arguments on Windows to be
 * case-instensitive, everywhere else we expect lowercase
 * for commands and case-sensitivity for arguments.
 */
#ifdef _WIN32
#   define toolbox_strcmp  stricmp
#else
#   define toolbox_strcmp  strcmp
#endif

/*
 * Common functions.
 */

void
ToolsCmd_MissingEntityError(const char *name,
                            const char *entity);

void
ToolsCmd_UnknownEntityError(const char *name,
                            const char *entity,
                            const char *str);

void
ToolsCmd_Print(const char *fmt,
               ...) PRINTF_DECL(1, 2);

void
ToolsCmd_PrintErr(const char *fmt,
                  ...) PRINTF_DECL(1, 2);

gboolean
ToolsCmd_SendRPC(const char *rpc,
                 size_t rpcLen,
                 char **result,
                 size_t *resultLen);

void
ToolsCmd_FreeRPC(void *ptr);

/*
 * Command declarations.
 */

/**
 * A shorthand macro for declaring a command entry. This just declares
 * two functions ("foo_Command" and "foo_Help" for a command "foo").
 * The command implementation should provide those functions, which will
 * be used by the main dispatch table when executing commands.
 */

#define DECLARE_COMMAND(name)             \
   int name##_Command(char **argv,        \
                      int argc,           \
                      gboolean quiet);    \
   void name##_Help(const char *progName, \
                    const char *cmd);

DECLARE_COMMAND(Device);
DECLARE_COMMAND(Disk);
DECLARE_COMMAND(Script);
DECLARE_COMMAND(Stat);
DECLARE_COMMAND(TimeSync);
DECLARE_COMMAND(Logging);
DECLARE_COMMAND(Info);
DECLARE_COMMAND(Config);

#if defined(_WIN32) || \
   (defined(__linux__) && !defined(OPEN_VM_TOOLS) && !defined(USERWORLD))
DECLARE_COMMAND(Upgrade);
#endif

#if defined(_WIN32) || \
   (defined(__linux__) && !defined(USERWORLD))
DECLARE_COMMAND(GuestStore);
#endif

#if defined(GLOBALCONFIG_SUPPORTED)

DECLARE_COMMAND(GlobalConf)

#define TOOLBOXCMD_LOAD_GLOBALCONFIG(conf)                        \
   {                                                              \
      if (GlobalConfig_GetEnabled(conf)) {                        \
         GKeyFile *__globalConf = NULL;                           \
         if (GlobalConfig_LoadConfig(&__globalConf, NULL)) {      \
            VMTools_AddConfig(__globalConf, conf);                \
            g_key_file_free(__globalConf);                        \
         }                                                        \
      }                                                           \
   }
#else

#define TOOLBOXCMD_LOAD_GLOBALCONFIG(conf)

#endif

#endif /*_TOOLBOX_CMD_H_*/
070701000003F4000081A40000000000000000000000016822550500001FE9000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-config.c    /*********************************************************
 * Copyright (c) 2016,2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-config.c --
 *
 *    VMTools config operations.
 *
 *    Supports a basic set/get of individual tools.conf key-value pairs.
 *
 */

#include <time.h>

#include "conf.h"
#include "toolboxCmdInt.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/log.h"


/*
 *-----------------------------------------------------------------------------
 *
 * ConfigSet --
 *
 *      Set a config entry.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
ConfigSet(const char *section,         // config section
          const char *key,             // key
          const char *value)           // value
{
   GKeyFile *confDict = NULL;
   GError *err = NULL;
   int ret = EXIT_SUCCESS;

   VMTools_LoadConfig(NULL,
                      G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
                      &confDict,
                      NULL);

   if (confDict == NULL) {
      confDict = g_key_file_new();
   }

   g_key_file_set_string(confDict, section,
                         key, value);

   if (!VMTools_WriteConfig(NULL, confDict, &err)) {
      ToolsCmd_PrintErr(SU_(script.write.error, "Error writing config: %s\n"),
                        err ? err->message : "");
      g_clear_error(&err);
      ret = EX_TEMPFAIL;
   }

   g_key_file_free(confDict);

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ConfigGet --
 *
 *      Get config value.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
ConfigGet(const char *section,      // section
          const char *key)          // key
{
   GKeyFile *confDict = NULL;
   int ret = EXIT_SUCCESS;
   gchar *value = NULL;

   VMTools_LoadConfig(NULL,
                      G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
                      &confDict,
                      NULL);

   if (confDict) {
      TOOLBOXCMD_LOAD_GLOBALCONFIG(confDict)
      value = g_key_file_get_string(confDict, section,
                                    key, NULL);
   } else {
      ret = EX_UNAVAILABLE;
   }

   if (value) {
      g_print("[%s] %s = %s\n", section, key, value);
   } else {
      g_print("[%s] %s UNSET\n", section, key);
   }

   g_key_file_free(confDict);
   g_free(value);

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ConfigRemove --
 *
 *      Remove config key.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
ConfigRemove(const char *section,      // section
             const char *key)          // key
{
   GKeyFile *confDict = NULL;
   int ret = EXIT_SUCCESS;
   GError *err = NULL;

   VMTools_LoadConfig(NULL,
                      G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
                      &confDict,
                      NULL);

   // be idempotent -- ignore any error about non-existant config or key
   if (confDict) {
      /*
       * our ancient FreeBSD glib expects g_key_file_remove_key()
       * to return void, and since we don't care anyways, ignore the
       * return so it builds everywhere.
       */
      (void) g_key_file_remove_key(confDict, section,
                                   key, NULL);
   } else {
      return EX_UNAVAILABLE;
   }

   if (!VMTools_WriteConfig(NULL, confDict, &err)) {
      ToolsCmd_PrintErr(SU_(script.write.error, "Error writing config: %s\n"),
                        err ? err->message : "");
      g_clear_error(&err);
      ret = EX_TEMPFAIL;
   }

   g_key_file_free(confDict);

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Config_Command --
 *
 *      Handle and parse config commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
Config_Command(char **argv,      // IN: Command line arguments
               int argc,         // IN: Length of command line arguments
               gboolean quiet)   // IN
{
   const char *op;
   const char *section;
   const char *key;

   if (optind >= argc) {
      ToolsCmd_MissingEntityError(argv[0],
                                  SU_(arg.config.operation, "config operation"));
      return EX_USAGE;
   }

   if ((optind + 1) >= argc) {
      ToolsCmd_MissingEntityError(argv[0],
                                  SU_(arg.config.section, "config section"));
      return EX_USAGE;
   }

   if ((optind + 2) >= argc) {
      ToolsCmd_MissingEntityError(argv[0],
                                  SU_(arg.config.key, "config key"));
      return EX_USAGE;
   }

   op = argv[optind];
   section = argv[optind + 1];
   key = argv[optind + 2];

   if (toolbox_strcmp(op, "set") == 0) {
      const char *value;

      if ((optind + 3) >= argc) {
         ToolsCmd_MissingEntityError(argv[0],
                                     SU_(arg.config.value, "config value"));
         return EX_USAGE;
      }
      value = argv[optind + 3];

      return ConfigSet(section, key, value);
   } else if (toolbox_strcmp(op, "get") == 0) {
      return ConfigGet(section, key);
   } else if (toolbox_strcmp(op, "remove") == 0) {
      return ConfigRemove(section, key);
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
      return EX_USAGE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Config_Help --
 *
 *      Prints the help for the config command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Config_Help(const char *progName, // IN: The name of the program obtained from argv[0]
            const char *cmd)      // IN
{
   g_print(SU_(help.config,
               "%s: modify Tools configuration\n"
               "Usage: %s %s <subcommand>\n\n"
               "Subcommands:\n"
               "   get <section> <key>: display current value for <key>\n"
               "   NOTE: If the <key> is not present in tools.conf, its\n"
               "   value from the global configuration is returned if present\n"
               "   set <section> <key> <value>: set <key> to <value>\n\n"
               "   remove <section> <key>: remove <key>\n\n"
               "<section> can be any supported section, such as logging, guestoperations or guestinfo.\n"
               "<key> can be any configuration key.\n"
               "<value> can be any value.\n"),
           cmd, progName, cmd);
}

   070701000003F5000081A40000000000000000000000016822550500002668000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-devices.c   /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-devices.c --
 *
 *     The devices functions for toolbox-cmd
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "toolboxCmdInt.h"
#include "backdoor.h"
#include "backdoor_def.h"
#include "backdoor_types.h"
#include "removable_device.h"
#include "vmware/tools/i18n.h"

#define MAX_DEVICES 50


/*
 *-----------------------------------------------------------------------------
 *
 * SetDeviceState --
 *
 *      Ask the VMX to change the connected state of a device.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
SetDeviceState(uint16 id,      // IN: Device ID
               Bool connected) // IN
{
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_TOGGLEDEVICE;
   bp.in.size = (connected ? 0x80000000 : 0) | id;
   Backdoor(&bp);
   return bp.out.ax.word ? TRUE : FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GetDeviceListElement --
 *
 *      Retrieve 4 bytes of of information about a removable device.
 *
 * Results:
 *      TRUE on success. '*data' is set
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
GetDeviceListElement(uint16 id,     // IN : Device ID
                     uint16 offset, // IN : Offset in the RD_Info
                                    //      structure
                     uint32 *data)  // OUT: Piece of RD_Info structure
{
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_GETDEVICELISTELEMENT;
   bp.in.size = (id << 16) | offset;
   Backdoor(&bp);
   if (bp.out.ax.word == FALSE) {
      return FALSE;
   }
   *data = bp.out.bx.word;
   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GetDeviceInfo --
 *
 *      Retrieve information about a removable device.
 *
 * Results:
 *      TRUE on success
 *      FALSE on failure
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
GetDeviceInfo(uint16 id,     // IN: Device ID
              RD_Info *info) // OUT
{
   uint16 offset;
   uint32 *p;

   /*
    * XXX It is theoretically possible to SEGV here as we can write up to 3
    *     bytes beyond the end of the 'info' structure. I think alignment
    *     saves us in practice.
    */
   for (offset = 0, p = (uint32 *)info;
        offset < sizeof *info;
        offset += sizeof (uint32), p++) {
      if (GetDeviceListElement(id, offset, p) == FALSE) {
         return FALSE;
      }
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DevicesList  --
 *
 *      prints device names and status to stdout.
 *
 * Results:
 *      EXIT_SUCCESS.
 *
 * Side effects:
 *      Prints to stdout.
 *
 *-----------------------------------------------------------------------------
 */

static int
DevicesList(void)
{
   int i;
   for (i = 0; i < MAX_DEVICES; i++) {
      RD_Info info;
      if (GetDeviceInfo(i, &info) && strlen(info.name) > 0) {
         const char *status = info.enabled ? SU_(option.enabled, "Enabled")
                                           : SU_(option.disabled, "Disabled");
         g_print("%s: %s\n", info.name, status);
      }
   }
   return EXIT_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DevicesGetStatus  --
 *
 *      Prints device names to stdout.
 *
 * Results:
 *      Returns EXIT_SUCCESS if device is enabled.
 *      Returns EX_UNAVAILABLE if device is disabled.
 *      Returns EXIT_OSFILE if devName was not found.
 *
 * Side effects:
 *      Print to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
DevicesGetStatus(char *devName)  // IN: Device Name
{
   int i;
   for (i = 0; i < MAX_DEVICES; i++) {
      RD_Info info;
      if (GetDeviceInfo(i, &info)
          && toolbox_strcmp(info.name, devName) == 0) {
         if (info.enabled) {
            ToolsCmd_Print("%s\n", SU_(option.enabled, "Enabled"));
            return EXIT_SUCCESS;
         } else {
            ToolsCmd_Print("%s\n", SU_(option.disabled, "Disabled"));
            return EX_UNAVAILABLE;
         }
         return EXIT_SUCCESS;
      }
   }
   ToolsCmd_PrintErr("%s",
                     SU_(device.notfound,
                         "Error fetching interface information: device not found.\n"));
   return EX_OSFILE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * DevicesSetStatus  --
 *
 *      Sets device status to the value in enable.
 *
 * Results:
 *      EXIT_SUCCESS on success
 *      EXIT_TEMPFAIL on failure to connect/disconnect a device
 *      EXIT_OSFILE if device is not found
 *
 * Side effects:
 *      Possibly connects or disconnects a device.
 *      Print to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
DevicesSetStatus(char *devName,  // IN: device name
                 Bool enable)    // IN: status
{
   int dev_id;
   for (dev_id = 0; dev_id < MAX_DEVICES; dev_id++) {
      RD_Info info;
      if (GetDeviceInfo(dev_id, &info) &&
          toolbox_strcmp(info.name, devName) == 0) {
         if (!SetDeviceState(dev_id, enable)) {
            if (enable) {
               ToolsCmd_PrintErr(SU_(device.connect.error,
                                     "Unable to connect device %s.\n"),
                                 info.name);
            } else {
               ToolsCmd_PrintErr(SU_(device.disconnect.error,
                                     "Unable to disconnect device %s.\n"),
                                 info.name);
            }
            return EX_TEMPFAIL;
         }
         goto exit;
      }
   }

   ToolsCmd_PrintErr("%s",
                     SU_(device.notfound,
                         "Error fetching interface information: device not found.\n"));
   return EX_OSFILE;

exit:
   if (enable) {
      ToolsCmd_Print("%s\n", SU_(option.enabled, "Enabled"));
   } else {
      ToolsCmd_Print("%s\n", SU_(option.disabled, "Disabled"));
   }
   return EXIT_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Device_Command --
 *
 *      Handle and parse device commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the exit code on errors.
 *
 * Side effects:
 *      Might enable or disable a device.
 *
 *-----------------------------------------------------------------------------
 */

int
Device_Command(char **argv,    // IN: Command line arguments
               int argc,       // IN: Length of command line arguments
               gboolean quiet) // IN
{
   char *subcommand = argv[optind];
   Bool haveDeviceArg = optind + 1 < argc;

   if (toolbox_strcmp(subcommand, "list") == 0) {
      return DevicesList();
   } else if (toolbox_strcmp(subcommand, "status") == 0) {
      if (haveDeviceArg) {
         return DevicesGetStatus(argv[optind + 1]);
      }
   } else if (toolbox_strcmp(subcommand, "enable") == 0) {
      if (haveDeviceArg) {
         return DevicesSetStatus(argv[optind + 1], TRUE);
      }
   } else if (toolbox_strcmp(subcommand, "disable") == 0) {
      if (haveDeviceArg) {
         return DevicesSetStatus(argv[optind + 1], FALSE);
      }
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  subcommand);
      return EX_USAGE;
   }

   ToolsCmd_MissingEntityError(argv[0], SU_(arg.devicename, "device name"));
   return EX_USAGE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Device_Help --
 *
 *      Prints the help for device commands.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Device_Help(const char *progName, // IN: The name of the program obtained from argv[0]
            const char *cmd)      // IN
{
   g_print(SU_(help.device, "%s: functions related to the virtual machine's hardware devices\n"
                            "Usage: %s %s <subcommand> [args]\n"
                            "dev is the name of the device.\n"
                            "\n"
                            "Subcommands:\n"
                            "   enable <dev>: enable the device dev\n"
                            "   disable <dev>: disable the device dev\n"
                            "   list: list all available devices\n"
                            "   status <dev>: print the status of a device\n"),
           cmd, progName, cmd);
}

070701000003F6000081A400000000000000000000000168225505000024F5000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-globalconf.c    /*********************************************************
 * Copyright (c) 2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-globalconf.c --
 *
 *     Global Configuration operations for toolbox-cmd.
 */

#include "globalConfig.h"

#if !defined(GLOBALCONFIG_SUPPORTED)
#   error This file should not be compiled
#endif

#include "vm_product.h"
#include "vm_assert.h"
#include "vm_basic_defs.h"
#include "toolboxCmdInt.h"
#include "vmware/tools/i18n.h"

/*
 * GuestStore client library error messages
 *
 * Localization can be done in this way if needed:
 *
 * #define GUESTSTORE_LIB_ERR_ITEM(err, msgid, msg) MSGID(msgid) msg,
 *
 * call this to get localized error message:
 *
 * VMTools_GetString(VMW_TEXT_DOMAIN, guestStoreLibErrMsgs[errCode])
 */
#define GUESTSTORE_LIB_ERR_ITEM(err, msgid, msg) msg,
static const char * const guestStoreLibErrMsgs[] = {
GUESTSTORE_LIB_ERR_LIST
};
#undef GUESTSTORE_LIB_ERR_ITEM


/*
 *-----------------------------------------------------------------------------
 *
 * GlobalConfRefresh --
 *
 *      Trigger a new download of the global configuration from the GuestStore.
 *
 * Results:
 *      GSLIBERR_SUCCESS on success.
 *      GuestStoreClientError on error.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static GuestStoreClientError
GlobalConfRefresh(GKeyFile *confDict)
{
   GuestStoreClientError downloadStatus;

   ASSERT(confDict != NULL);

   if (!GuestStoreClient_Init()) {
      g_critical("GuestStoreClient_Init failed.\n");
      downloadStatus = GSLIBERR_NOT_INITIALIZED;
   } else {
      downloadStatus = GlobalConfig_DownloadConfig(confDict);
      GuestStoreClient_DeInit();
   }

   return downloadStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GlobalConfStatus --
 *
 *      Handler for 'status', 'enable', 'disable' commands for globalconf.
 *      'status' will query and prints the status.
 *      'enable' will enable the module. tools.conf is updated.
 *      'disable' will disable the module. tools.conf is updated.
 *      If the tools 'vmsvc' service is running, then the service
 *      is stopped, config is updated and the service is started.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL, EX_SOFTWARE or EX_USAGE on error.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */


static int
GlobalConfStatus(const char *command)   // IN: command specified by the user.
{
   GKeyFile *confDict = NULL;
   int ret = EXIT_SUCCESS;
   gboolean currentEnabledState;
   gboolean desiredEnabledState;

   ASSERT(command != NULL);

   VMTools_LoadConfig(NULL,
                      G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
                      &confDict,
                      NULL);

   if (confDict == NULL) {
      confDict = g_key_file_new();
   }

   currentEnabledState = GlobalConfig_GetEnabled(confDict);

   if (toolbox_strcmp(command, "status") == 0) {
      ToolsCmd_Print(SU_(globalconf.status,
                         "The status of globalconf module is '%s'\n"),
                     currentEnabledState ?
                        SU_(option.enabled,
                            "Enabled") :
                        SU_(option.disabled,
                            "Disabled"));
      desiredEnabledState = currentEnabledState;
   } else if (toolbox_strcmp(command, "enable") == 0) {
      desiredEnabledState = TRUE;
   } else if (toolbox_strcmp(command, "disable") == 0) {
      desiredEnabledState = FALSE;
   } else {
      desiredEnabledState = currentEnabledState;
      ret = EX_USAGE;
   }

   if (currentEnabledState != desiredEnabledState) {
      GError *err = NULL;
      GlobalConfig_SetEnabled(desiredEnabledState, confDict);

      ToolsCmd_Print(SU_(globalconf.update_config,
                         "%s: Updating the Configuration.\n"),
                     command);
      if (!VMTools_WriteConfig(NULL, confDict, &err)) {
         g_warning("%s: Error writing config: %s.\n",
                   __FUNCTION__, err ? err->message : "");
         g_clear_error(&err);
         ret = EX_TEMPFAIL;
         goto error;
      }

      if (!desiredEnabledState) {
         if (GlobalConfig_DeleteConfig()) {
            g_debug("%s: Deleted the global configuration.\n", __FUNCTION__);
         } else {
            g_warning("%s: Failed to delete the global configuration.\n",
                      __FUNCTION__);
         }
      }
   }

error:
   g_key_file_free(confDict);

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GlobalConf_Command --
 *
 *      Parse and handle globalconf command.
 *
 * Results:
 *      0 on success.
 *
 *      Error code from GuestStore client library or
 *      general process error exit code.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
GlobalConf_Command(char **argv,      // IN: Command line arguments
                   int argc,         // IN: Length of the command line arguments
                   gboolean quiet)   // IN
{
   int ret;

   if (toolbox_strcmp(argv[optind], "refresh") == 0) {
      GuestStoreClientError downloadStatus;
      GKeyFile *confDict = NULL;

      VMTools_LoadConfig(NULL,
                        G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
                        &confDict,
                        NULL);

      if (confDict == NULL) {
         confDict = g_key_file_new();
      }

      ret = EX_SOFTWARE;

      if (!GlobalConfig_GetEnabled(confDict)) {
         ToolsCmd_PrintErr(SU_(globalconf.refresh.failed,
                               "'%s' failed, since globalconf module is"
                               " disabled.\n"),
                           argv[optind]);
         g_key_file_free(confDict);
         return ret;
      }

      downloadStatus = GlobalConfRefresh(confDict);
      g_key_file_free(confDict);

      if (downloadStatus == GSLIBERR_SUCCESS) {
         ToolsCmd_Print(SU_(result.succeeded,
                            "'%s' succeeded.\n"),
                        argv[optind]);
         ret = EXIT_SUCCESS;
      } else if (downloadStatus < GUESTSTORE_LIB_ERR_MAX) {
         ToolsCmd_PrintErr(SU_(gueststore.error.client_lib,
                               "'%s' failed, GuestStore client library "
                               "error: %s.\n"), argv[optind],
                           guestStoreLibErrMsgs[downloadStatus]);
      } else {
         ToolsCmd_PrintErr(SU_(result.error.failed,
                               "'%s' failed, check %s log for "
                               "more information.\n"),
                           argv[optind], argv[0]);
      }
   } else if (toolbox_strcmp(argv[optind], "status") == 0 ||
              toolbox_strcmp(argv[optind], "enable") == 0 ||
              toolbox_strcmp(argv[optind], "disable") == 0) {
      ret = GlobalConfStatus(argv[optind]);
      if (ret != EXIT_SUCCESS) {
         ToolsCmd_PrintErr(SU_(result.error.failed,
                               "'%s' failed, check %s log for "
                               "more information.\n"),
                           argv[optind], argv[0]);
      }
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
      ret = EX_USAGE;
   }

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GlobalConf_Help --
 *
 *      Prints the help for the globalconf command.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
GlobalConf_Help(const char *progName, // IN: Name of the program from argv[0]
                const char *cmd)      // IN
{
   g_print(SU_(help.globalconf,
               "%s: Manage global configuration downloads "
               "from the GuestStore\n"
               "Usage: %s %s <subcommand>\n\n"
               "ESX guests only subcommands:\n"
               "   enable: "
               "Enable the global configuration module\n"
               "   disable: "
               "Disable the global configuration module\n"
               "   refresh: "
               "Trigger a new download of the global configuration "
               "from the GuestStore\n"
               "   status: "
               "Print the status of the global configuration module\n"),
           cmd, progName, cmd);
}
   070701000003F7000081A40000000000000000000000016822550500001D0A000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-gueststore.c    /*********************************************************
 * Copyright (c) 2019-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-gueststore.c --
 *
 *     GuestStore getcontent operation for toolbox-cmd.
 */


#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>

#if defined(_WIN32)
#include <windows.h>
#else
#include <unistd.h>
#include <dlfcn.h>
#endif

#include "vm_assert.h"
#include "vm_basic_defs.h"
#include "toolboxCmdInt.h"
#include "vmware/tools/i18n.h"
#include "guestStoreClient.h"


/*
 * GuestStore client library error messages
 *
 * Localization can be done in this way if needed:
 *
 * #define GUESTSTORE_LIB_ERR_ITEM(a, b, c) MSGID(b) c,
 *
 * call this to get localized error message:
 *
 * VMTools_GetString(VMW_TEXT_DOMAIN, guestStoreLibErrMsgs[errCode])
 */
#define GUESTSTORE_LIB_ERR_ITEM(a, b, c) c,
static const char * const guestStoreLibErrMsgs[] = {
GUESTSTORE_LIB_ERR_LIST
};
#undef GUESTSTORE_LIB_ERR_ITEM

/*
 * Passed in from main(), command line --quiet (q) option.
 */
static gboolean gQuiet = FALSE;


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreReportProgress --
 *
 *      Report progress from GuestStore client library.
 *
 * Results:
 *      TRUE to continue getting content.
 *      FALSE to cancel getting content.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static Bool
GuestStoreReportProgress(int64 fileSize,       // IN
                         int64 bytesReceived,  // IN
                         void *clientData)     // IN
{
   static Bool first = TRUE;
   static int lastProgress = 0;
   int percentage;
   int progress;
   int i;

   if (gQuiet) {
      return TRUE;
   }

   if (first) {
      g_print("%s", SU_(gueststore.content_size,
                        "Content size in bytes: "));

      g_print("%" FMT64 "d\n", fileSize);

      first = FALSE;
   }

   percentage = (int)((bytesReceived * 100) / fileSize);
   progress = percentage / 5;  // increment by every 5%
   if (progress == lastProgress) {
      return TRUE;
   }

   lastProgress = progress;

   g_print(SU_(gueststore.progress,
               "\rProgress: %d%%"),
           percentage);
   g_print(" [");

   for (i = 0; i < progress; i++) {
      putchar('=');
   }

   g_print(">%*c", 20 - i, ']');
   fflush(stdout);

   if (percentage == 100) {
      g_print("\n");
   }

   return TRUE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStoreRemovePathEnclosingQuotes --
 *
 *      Remove the beginning and ending quotes in a path.
 *
 * Results:
 *      Path without enclosing quotes.
 *
 * Side effects:
 *      Input path may have been changed.
 *
 *-----------------------------------------------------------------------------
 */

static char *
GuestStoreRemovePathEnclosingQuotes(char *path)  // IN/OUT
{
   char *lastChar;

   if (*path != '"') {
      return path;
   }

   path++;
   lastChar = path + strlen(path) - 1;
   if (*lastChar == '"') {
      *lastChar = '\0';
   }

   return path;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStore_Command --
 *
 *      Handle and parse gueststore command.
 *
 * Results:
 *      0 on success.
 *
 *      Error code from GuestStore client library or
 *      general process error exit code.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

int
GuestStore_Command(char **argv,      // IN: Command line arguments
                   int argc,         // IN: Length of the command line arguments
                   gboolean quiet)   // IN
{
   int exitCode;
   char *contentPath;
   char *outputPath;

   if (toolbox_strcmp(argv[optind], "getcontent") != 0) {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
      return EX_USAGE;
   }

   if (optind != (argc - 3)) {
      return EX_USAGE;
   }

   gQuiet = quiet;

   if (!GuestStoreClient_Init()) {
      g_critical("GuestStoreClient_Init failed.\n");
      exitCode = EX_SOFTWARE;
      goto exit;
   }

   contentPath = GuestStoreRemovePathEnclosingQuotes(argv[argc - 2]);
   outputPath = GuestStoreRemovePathEnclosingQuotes(argv[argc - 1]);

   exitCode = GuestStoreClient_GetContent(contentPath, outputPath,
                                          GuestStoreReportProgress, NULL);
   if (exitCode != GSLIBERR_SUCCESS) {
      g_critical("GuestStoreClient_GetContent failed: error=%d.\n", exitCode);
   }

   if (!GuestStoreClient_DeInit()) {
      g_warning("GuestStoreClient_DeInit failed.\n");
   }

exit:

   if (exitCode == GSLIBERR_SUCCESS) {
      ToolsCmd_Print(SU_(result.succeeded,
                         "'%s' succeeded.\n"),
                     argv[optind]);
   } else if (exitCode < GUESTSTORE_LIB_ERR_MAX) {
      ToolsCmd_PrintErr(SU_(gueststore.error.client_lib,
                            "'%s' failed, GuestStore client library "
                            "error: %s.\n"),
                        argv[optind], guestStoreLibErrMsgs[exitCode]);
   } else {
      ToolsCmd_PrintErr(SU_(result.error.failed,
                            "'%s' failed, check %s log for "
                            "more information.\n"),
                        argv[optind], argv[0]);
   }

   return exitCode;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GuestStore_Help --
 *
 *      Prints the help for the gueststore command.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
GuestStore_Help(const char *progName, // IN: Name of the program from argv[0]
                const char *cmd)      // IN
{
   g_print(SU_(help.gueststore,
               "%s: get resource content from GuestStore\n"
               "Usage: %s %s <subcommand>\n\n"
               "ESX guests only subcommands:\n"
               "   getcontent <resource path> <output file>: "
               "get resource content from GuestStore and "
               "save to output file.\n\n"
               "<resource path> starts with / and represents a unique "
               "resource in GuestStore. If it ends with /, defaults to "
               "retrieve the underlying 'metadata.json' resource.\n"
               "<output file> is the path of a file "
               "to save resource content to.\n"),
           cmd, progName, cmd);
}
  070701000003F8000081A40000000000000000000000016822550500001F8E000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-info.c  /*********************************************************
 * Copyright (c) 2015-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-info.c --
 *
 *    Various guest info operations for toolbox-cmd.
 */

#include <time.h>

#include "conf.h"
#include "nicInfo.h"
#include "dynxdr.h"
#include "xdrutil.h"
#include "toolboxCmdInt.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/log.h"
#ifdef _WIN32
#include "netutil.h"
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * InfoSendNetworkXdr --
 *
 *      Send network info to the VMX.
 *
 * Results:
 *      Returns TRUE on success.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
InfoSendNetworkXdr(GuestNicProto *message,
                   GuestInfoType type)
{
   Bool status = FALSE;
   XDR xdrs;
   gchar *request;
   char *reply = NULL;
   size_t replyLen;

   /* Add the RPC preamble: message name, and type. */
   request = g_strdup_printf("%s  %d ", GUEST_INFO_COMMAND, type);

   if (DynXdr_Create(&xdrs) == NULL) {
      goto exit;
   }

   /* Write preamble and serialized nic info to XDR stream. */
   if (!DynXdr_AppendRaw(&xdrs, request, strlen(request)) ||
       !xdr_GuestNicProto(&xdrs, message)) {
      g_warning("Error serializing nic info v%d data.", message->ver);
   } else {
      status = ToolsCmd_SendRPC(DynXdr_Get(&xdrs), xdr_getpos(&xdrs),
                                &reply, &replyLen);
      if (!status) {
         g_warning("%s: update failed: request \"%s\", reply \"%s\".\n",
                    __FUNCTION__, request, VM_SAFE_STR(reply));
      }
      ToolsCmd_FreeRPC(reply);
   }

   /*
    * DynXdr_Destroy only tries to free storage returned by a call to
    * DynXdr_Create(NULL).
    */
   /* coverity[address_free] */
   DynXdr_Destroy(&xdrs, TRUE);

exit:
   g_free(request);
   return status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * InfoUpdateNetwork --
 *
 *      Update network info.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
InfoUpdateNetwork(void)
{
   int ret = EXIT_SUCCESS;
   NicInfoV3 *info = NULL;
   GuestNicProto msg = { 0 };
   GuestInfoType type = INFO_IPADDRESS_V3;

   GKeyFile *confDict = NULL;
   int maxIPv4RoutesToGather = NICINFO_MAX_ROUTES;
   int maxIPv6RoutesToGather = NICINFO_MAX_ROUTES;

#ifdef _WIN32
   DWORD dwRet = NetUtil_LoadIpHlpApiDll();
   if (dwRet != ERROR_SUCCESS) {
      g_warning("NetUtil_LoadIpHlpApiDll() failed.\n");
      return EXIT_FAILURE;
   }
#endif

   // Get the config options of max IPv4/IPv6 routes to gather
   VMTools_LoadConfig(NULL, G_KEY_FILE_NONE, &confDict, NULL);

   if (confDict != NULL) {
      maxIPv4RoutesToGather =
               VMTools_ConfigGetInteger(confDict,
                                        CONFGROUPNAME_GUESTINFO,
                                        CONFNAME_GUESTINFO_MAXIPV4ROUTES,
                                        NICINFO_MAX_ROUTES);
      if (maxIPv4RoutesToGather < 0 ||
          maxIPv4RoutesToGather > NICINFO_MAX_ROUTES) {
         g_warning("Invalid %s.%s value: %d. Using default %u.\n",
                   CONFGROUPNAME_GUESTINFO,
                   CONFNAME_GUESTINFO_MAXIPV4ROUTES,
                   maxIPv4RoutesToGather,
                   NICINFO_MAX_ROUTES);
         maxIPv4RoutesToGather = NICINFO_MAX_ROUTES;
      }

      maxIPv6RoutesToGather =
               VMTools_ConfigGetInteger(confDict,
                                        CONFGROUPNAME_GUESTINFO,
                                        CONFNAME_GUESTINFO_MAXIPV6ROUTES,
                                        NICINFO_MAX_ROUTES);
      if (maxIPv6RoutesToGather < 0 ||
          maxIPv6RoutesToGather > NICINFO_MAX_ROUTES) {
         g_warning("Invalid %s.%s value: %d. Using default %u.\n",
                   CONFGROUPNAME_GUESTINFO,
                   CONFNAME_GUESTINFO_MAXIPV6ROUTES,
                   maxIPv6RoutesToGather,
                   NICINFO_MAX_ROUTES);
         maxIPv6RoutesToGather = NICINFO_MAX_ROUTES;
      }
      g_key_file_free(confDict);
   }

   if (!GuestInfo_GetNicInfo(maxIPv4RoutesToGather,
                             maxIPv6RoutesToGather,
                             &info, NULL)) {
      g_warning("Failed to get nic info.\n");
      ret = EXIT_FAILURE;
      goto done;
   }

   // Only useful for VMXs that support V3
   msg.ver = NIC_INFO_V3;
   msg.GuestNicProto_u.nicInfoV3 = info;
   if (!InfoSendNetworkXdr(&msg, type)) {
      ret = EXIT_FAILURE;
   }

   GuestInfo_FreeNicInfo(info);

done:
#ifdef _WIN32
   NetUtil_FreeIpHlpApiDll();
#endif

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Info_Command --
 *
 *      Handle and parse info commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
Info_Command(char **argv,      // IN: Command line arguments
             int argc,         // IN: Length of command line arguments
             gboolean quiet)   // IN
{
   char *subcommand;
   char *class;

   // subcommand: 'update'
   if ((optind) >= argc) {
      ToolsCmd_MissingEntityError(argv[0],
                                  SU_(arg.info.subcommand, "info operation"));
      return EX_USAGE;
   }
   subcommand = argv[optind];

   // info class: 'network'
   if ((optind + 1) >= argc) {
      ToolsCmd_MissingEntityError(argv[0],
                                  SU_(arg.info.class, "info infoclass"));
      return EX_USAGE;
   }

   class = argv[optind + 1];

   if (toolbox_strcmp(subcommand, "update") == 0) {
      if (toolbox_strcmp(class, "network") == 0) {
         return InfoUpdateNetwork();
      } else {
         ToolsCmd_UnknownEntityError(argv[0],
                                     SU_(arg.info.class, "info infoclass"),
                                     class);
         return EX_USAGE;
      }
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  subcommand);
      return EX_USAGE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Info_Help --
 *
 *      Prints the help for the info command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Info_Help(const char *progName, // IN: The name of the program obtained from argv[0]
          const char *cmd)      // IN
{
   g_print(SU_(help.info,
               "%s: update guest information on the host\n"
               "Usage: %s %s update <infoclass>\n\n"
               "Subcommands:\n"
               "   update <infoclass>: update information identified by <infoclass>\n"
               "<infoclass> can be 'network'\n"),
           cmd, progName, cmd);
}

  070701000003F9000081A400000000000000000000000168225505000020F5000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-logging.c   /*********************************************************
 * Copyright (c) 2015-2016,2020-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-logging.c --
 *
 *    Various logging operations for toolbox-cmd.
 *
 *    Note:
 *    Servicenames are not confidence checked.  This means it can report
 *    the default value for a bogus servicename, or setting values for
 *    an unsupported servicename.  But any confidence checking would require
 *    all possible servicenames to #define themselves.  Lack of
 *    a confidence check overrrides this complexity.
 *
 *    TODO:  This currently just modifies the tools.conf file, which means
 *    that if tools is running, it can talke up to 5 seconds to react
 *    to any changes.  It would be better if toolsd could be poked to
 *    shrink that delay.
 */

#include <time.h>

#include "conf.h"
#include "toolboxCmdInt.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/utils.h"
#include "vmware/tools/log.h"

#define LOGGING_CONF_SECTION "logging"

/*
 *-----------------------------------------------------------------------------
 *
 * LoggingCheckLevel --
 *
 *      Confidence check logging level.
 *
 * Results:
 *      Returns TRUE if its a valid logging level, FALSE if not.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
LoggingCheckLevel(char *level)           // logging level
{
   if ((strcmp("error", level) == 0) ||
       (strcmp("critical", level) == 0) ||
       (strcmp("warning", level) == 0) ||
       (strcmp("message", level) == 0) ||
       (strcmp("info", level) == 0) ||
       (strcmp("debug", level) == 0)) {
      return TRUE;
   } else {
      return FALSE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * LoggingSetLevel --
 *
 *      Set logging level
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
LoggingSetLevel(char *service,         // service
                char *level)           // logging level
{
   GKeyFile *confDict = NULL;
   gchar *confName;
   GError *err = NULL;
   int ret = EXIT_SUCCESS;

   VMTools_LoadConfig(NULL,
                      G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
                      &confDict,
                      NULL);

   if (confDict == NULL) {
      confDict = g_key_file_new();
   }

   confName = g_strdup_printf("%s.level", service);

   g_key_file_set_string(confDict, LOGGING_CONF_SECTION,
                         confName, level);

   if (!VMTools_WriteConfig(NULL, confDict, &err)) {
      ToolsCmd_PrintErr(SU_(script.write.error, "Error writing config: %s\n"),
                        err->message);
      g_clear_error(&err);
      ret = EX_TEMPFAIL;
   }

   g_key_file_free(confDict);
   g_free(confName);

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * LoggingGetLevel --
 *
 *      Get current logging level
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
LoggingGetLevel(char *service)         // service
{
   GKeyFile *confDict = NULL;
   gchar *confName;
   int ret = EXIT_SUCCESS;
   gchar *level;

   VMTools_LoadConfig(NULL,
                      G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
                      &confDict,
                      NULL);

   if (confDict == NULL) {
      confDict = g_key_file_new();
   }

   TOOLBOXCMD_LOAD_GLOBALCONFIG(confDict)

   confName = g_strdup_printf("%s.level", service);

   level = g_key_file_get_string(confDict, LOGGING_CONF_SECTION,
                                 confName, NULL);

   if (level) {
      g_print("%s = %s\n", confName, level);
   } else {
      g_print("%s = %s\n", confName, VMTOOLS_LOGGING_LEVEL_DEFAULT);
   }
   g_key_file_free(confDict);
   g_free(confName);
   g_free(level);

   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Logging_Command --
 *
 *      Handle and parse logging commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
Logging_Command(char **argv,      // IN: Command line arguments
                int argc,         // IN: Length of command line arguments
                gboolean quiet)   // IN
{
   char *subcommand = argv[optind];
   char *op;

   if ((optind + 1) >= argc) {
      ToolsCmd_MissingEntityError(argv[0],
                                  SU_(arg.logging.subcommand, "logging operation"));
      return EX_USAGE;
   }
   if ((optind + 2) >= argc) {
      ToolsCmd_MissingEntityError(argv[0],
                                  SU_(arg.logging.service, "logging servicename"));
      return EX_USAGE;
   }

   op = argv[optind + 1];

   if (toolbox_strcmp(subcommand, "level") == 0) {
      if (toolbox_strcmp(op, "set") == 0) {
         if ((optind + 3) >= argc) {
            ToolsCmd_MissingEntityError(argv[0],
                                        SU_(arg.logging.level, "logging level"));
            return EX_USAGE;
         } else {
            if (!LoggingCheckLevel(argv[optind + 3])) {
               ToolsCmd_UnknownEntityError(argv[0],
                                           SU_(arg.logging.level, "logging level"),
                                           argv[optind + 3]);
               return EX_USAGE;
            }
            return LoggingSetLevel(argv[optind + 2], argv[optind + 3]);
         }
      } else if (toolbox_strcmp(op, "get") == 0) {
         return LoggingGetLevel(argv[optind + 2]);
      } else {
         ToolsCmd_UnknownEntityError(argv[0],
                                     SU_(arg.subcommand, "subcommand"),
                                     argv[optind + 1]);
         return EX_USAGE;
      }
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
      return EX_USAGE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Logging_Help --
 *
 *      Prints the help for the logging command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Logging_Help(const char *progName, // IN: The name of the program obtained from argv[0]
             const char *cmd)      // IN
{
   g_print(SU_(help.logging,
               "%s: modify tools logging\n"
               "Usage: %s %s level <subcommand> <servicename> <level>\n\n"
               "Subcommands:\n"
               "   get <servicename>: display current level\n"
               "   NOTE: If the level is not present in tools.conf, its\n"
               "   value from the global configuration is returned if present\n"
               "   set <servicename> <level>: set current level\n\n"
               "<servicename> can be any supported service, such as vmsvc or vmusr\n"
               "<level> can be one of error, critical, warning, info, message, debug\n"
               "   default is %s\n"),
           cmd, progName, cmd, VMTOOLS_LOGGING_LEVEL_DEFAULT);
}

   070701000003FA000081A400000000000000000000000168225505000032A9000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-scripts.c   /*********************************************************
 * Copyright (c) 2008-2016,2020-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-scripts.c --
 *
 *     The scripts functions for the linux toolbox-cmd
 */

#include <glib.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "conf.h"
#include "file.h"
#include "guestApp.h"
#include "system.h"
#include "toolboxCmdInt.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/utils.h"


#define SCRIPT_SUSPEND "suspend"
#define SCRIPT_RESUME  "resume"
#define SCRIPT_OFF     "shutdown"
#define SCRIPT_ON      "power"

typedef enum ScriptType {
   Default,
   Current
} ScriptType;


/*
 *-----------------------------------------------------------------------------
 *
 * GetConfName --
 *
 *      Gets the apm name.
 *
 * Results:
 *      The apm name.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static const char *
GetConfName(const char *apm) // IN: apm name.
{
   if (toolbox_strcmp(apm, SCRIPT_SUSPEND) == 0) {
      return CONFNAME_SUSPENDSCRIPT;
   } else if (toolbox_strcmp(apm, SCRIPT_RESUME) == 0) {
      return CONFNAME_RESUMESCRIPT;
   } else if (toolbox_strcmp(apm, SCRIPT_OFF) == 0) {
      return CONFNAME_POWEROFFSCRIPT;
   } else if (toolbox_strcmp(apm, SCRIPT_ON) == 0) {
     return CONFNAME_POWERONSCRIPT;
   } else {
      return NULL;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * LoadConfFile --
 *
 *      Loads the tools configuration file. If running as admin, tries to
 *      upgrade it if an old-style file is found.
 *
 * Results:
 *      A new GKeyFile *. If the conf file is not valid, it will be empty.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static GKeyFile *
LoadConfFile(void)
{
   GKeyFile *confDict = NULL;

   VMTools_LoadConfig(NULL,
                      G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
                      &confDict,
                      NULL);

   if (confDict == NULL) {
      confDict = g_key_file_new();
   }

   return confDict;
}


/*
 *-----------------------------------------------------------------------------
 *
 * GetConfEntry --
 *
 *      Gets the entry in the config dictionary.
 *
 * Results:
 *      EXIT_SUCCESS on success, error code otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
GetConfEntry(const char *progName,  // IN: program name (argv[0])
             const char *apm,       // IN: apm name
             ScriptType type)       // IN: Script type (default or current)
{
   gchar *entry;
   GKeyFile *confDict;
   const char *confName;
   int len;
   int ret;

   confName = GetConfName(apm);
   if (!confName) {
      ToolsCmd_UnknownEntityError(progName,
                                  SU_(script.operation, "operation"),
                                  apm);
      return EX_USAGE;
   }

   confDict = LoadConfFile();
   TOOLBOXCMD_LOAD_GLOBALCONFIG(confDict)

   switch (type) {
   case Current:
      entry = g_key_file_get_string(confDict, "powerops", confName, NULL);
      if (entry) {
         break;
      }
      /* Fall through */

   default:
      entry = g_strdup(GuestApp_GetDefaultScript(confName));
      break;
   }

   len = strlen(entry);
   if (len > 0) {

      /* If script path is not absolute, assume the Tools install path. */
      if (!g_path_is_absolute(entry)) {
         char *defaultPath = GuestApp_GetInstallPath();
         char *tmp;
         Bool quoted;

         ASSERT(defaultPath != NULL);

         /* Cope with old configs that added quotes around script paths. */
         quoted = (entry[0] == '"' && entry[len - 1] == '"');
         tmp = g_strdup_printf("%s%c%.*s", defaultPath, DIRSEPC,
                                quoted ? len - 2 : len,
                                quoted ? entry + 1 : entry);

         vm_free(defaultPath);

         g_free(entry);
         entry = tmp;
      }

      g_print("%s\n", entry);
      ret = EXIT_SUCCESS;
   } else {
      ToolsCmd_PrintErr(SU_(script.unknownop, "No script for operation %s.\n"),
                        apm);
      ret = EX_TEMPFAIL;
   }

   g_free(entry);
   g_key_file_free(confDict);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ScriptGetDefault  --
 *
 *      Gets the path to default script.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_USAGE on parse errors.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Print to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
ScriptGetDefault(const char *progName, // IN: program name (argv[0])
                 const char *apm)      // IN: APM name
{
   return GetConfEntry(progName, apm, Default);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ScriptGetCurrent  --
 *
 *      Gets the path to Current script.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_USAGE on parse errors.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Print to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
ScriptGetCurrent(const char *progName, // IN: program name (argv[0])
                 const char *apm)      // IN: apm function name
{
   return GetConfEntry(progName, apm, Current);
}


/*
 *-----------------------------------------------------------------------------
 *
 * ScriptToggle  --
 *
 *      enables/disable script.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_USAGE on parse errors.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Enables/Disables a script
 *      Print to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
ScriptToggle(const char *progName,  // IN: program name (argv[0])
             const char *apm,       // IN: APM name
             Bool enable)           // IN: status
{
   const char *path;
   const char *confName;
   int ret = EXIT_SUCCESS;
   GKeyFile *confDict;
   GError *err = NULL;

   confName = GetConfName(apm);

   if (!confName) {
      ToolsCmd_UnknownEntityError(progName,
                                  SU_(script.operation, "operation"),
                                  apm);
      return EX_USAGE;
   }

   confDict = LoadConfFile();

   if (!enable) {
      path = "";
   } else {
      path = GuestApp_GetDefaultScript(confName);
   }

   g_key_file_set_string(confDict, "powerops", confName, path);
   if (!VMTools_WriteConfig(NULL, confDict, &err)) {
      ToolsCmd_PrintErr(SU_(script.write.error, "Error writing config: %s\n"),
                        err->message);
      g_clear_error(&err);
      ret = EX_TEMPFAIL;
   }

   g_key_file_free(confDict);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ScriptSet  --
 *
 *      Sets a script to the given path.
 *
 * Results:
 *      EX_OSFILE if path doesn't exist.
 *      EXIT_SUCCESS on success.
 *      EX_USAGE on parse errors.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Sets a script.
 *      Print to stderr and exit on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
ScriptSet(const char *progName,  // IN: program name (argv[0])
          const char *apm,       // IN: APM name
          const char *path)      // IN: Verbosity flag
{
   const char *confName;
   int ret = EXIT_SUCCESS;
   GKeyFile *confDict = NULL;
   GError *err = NULL;

   if (!File_Exists(path)) {
      ToolsCmd_PrintErr(SU_(script.notfound, "%s doesn't exist.\n"), path);
      return EX_OSFILE;
   }

   confName = GetConfName(apm);
   if (!confName) {
      ToolsCmd_UnknownEntityError(progName,
                                  SU_(script.operation, "operation"),
                                  apm);
      return EX_USAGE;
   }

   confDict = LoadConfFile();
   g_key_file_set_string(confDict, "powerops", confName, path);

   if (!VMTools_WriteConfig(NULL, confDict, &err)) {
      ToolsCmd_PrintErr(SU_(script.write.error, "Error writing config: %s\n"),
                        err->message);
      g_clear_error(&err);
      ret = EX_TEMPFAIL;
   }

   g_key_file_free(confDict);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ScriptCheckName  --
 *
 *      Check if it is known script
 *
 * Results:
 *      TRUE if name is known, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
ScriptCheckName(const char *apm) // IN: script name
{
   return GetConfName(apm) != NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Script_Command --
 *
 *      Handle and parse script commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the exit code on errors.
 *
 * Side effects:
 *      Might enables, disables, or change APM scripts.
 *
 *-----------------------------------------------------------------------------
 */

int
Script_Command(char **argv,    // IN: command line arguments.
               int argc,       // IN: the length of the command line arguments.
               gboolean quiet) // IN
{
   const char *apm;

   if (++optind >= argc) {
      ToolsCmd_MissingEntityError(argv[0], SU_(arg.scripttype, "script type"));
      return EX_USAGE;
   }

   apm = argv[optind++];

   if (!ScriptCheckName(apm)) {
      ToolsCmd_UnknownEntityError(argv[0], SU_(arg.scripttype, "script type"), apm);
      return EX_USAGE;
   }

   if (optind >= argc) {
      ToolsCmd_MissingEntityError(argv[0], SU_(arg.subcommand, "subcommand"));
      return EX_USAGE;
   }

   if (toolbox_strcmp(argv[optind], "default") == 0) {
      return ScriptGetDefault(argv[0], apm);
   } else if (toolbox_strcmp(argv[optind], "current") == 0) {
      return ScriptGetCurrent(argv[0], apm);
   } else if (toolbox_strcmp(argv[optind], "set") == 0) {
      if (++optind >= argc) {
         ToolsCmd_MissingEntityError(argv[0], SU_(arg.scriptpath, "script path"));
         return EX_USAGE;
      }
      return ScriptSet(argv[0], apm, argv[optind]);
   } else if (toolbox_strcmp(argv[optind], "enable") == 0) {
      return ScriptToggle(argv[0], apm, TRUE);
   } else if (toolbox_strcmp(argv[optind], "disable") == 0) {
      return ScriptToggle(argv[0], apm, FALSE);
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
      return EX_USAGE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Script_Help --
 *
 *      Prints the help for the script command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Script_Help(const char *progName, // IN: The name of the program obtained from argv[0]
            const char *cmd)      // IN
{
   g_print(SU_(help.script,
               "%s: control the scripts run in response to power operations\n"
               "Usage: %s %s <power|resume|suspend|shutdown> <subcommand> [args]\n\n"
               "Subcommands:\n"
               "   enable: enable the given script and restore its path to the default\n"
               "   disable: disable the given script\n"
               "   set <full_path>: set the given script to the given path\n"
               "   default: print the default path of the given script\n"
               "   current: print the current path of the given script\n"
               "   NOTE: If the path is not present in tools.conf, its\n"
               "   value from the global configuration is returned if present\n"),
           cmd, progName, cmd);
}

   070701000003FB000081A400000000000000000000000168225505000042DE000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-shrink.c    /*********************************************************
 * Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-shrink.c --
 *
 *     The shrink operations for toolbox-cmd
 */


#include <string.h>
#include <stdlib.h>

#ifndef _WIN32
#   include <signal.h>
#endif

#include "vm_assert.h"
#include "toolboxCmdInt.h"
#include "guestApp.h"
#include "wiper.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/log.h"

#ifndef _WIN32
static void ShrinkWiperDestroy(int signal);
#endif

#define SHRINK_DISABLED_ERR                                       \
   "Shrink disk is disabled for this virtual machine.\n\n"        \
   "Shrinking is disabled for linked clones, parents of "         \
   "linked clones, \npre-allocated disks, snapshots, or due to "  \
   "other factors. \nSee the User's manual for more "             \
   "information.\n"

#define SHRINK_FEATURE_ERR                                                    \
   "The shrink feature is not available,\n\n"                                 \
   "either because you are running an old version of a VMware product, "      \
   "or because too many communication channels are open.\n\n"                 \
   "If you are running an old version of a VMware product, you should "       \
   "consider upgrading.\n\n"                                                  \
   "If too many communication channels are open, you should power off your "  \
   "virtual machine and then power it back on.\n"

#define SHRINK_CONFLICT_ERR                                 \
   "Error, The Toolbox believes disk shrinking is "         \
   "enabled while the host believes it is disabled.\n\n "   \
   "Please close and reopen the Toolbox to synchronize "    \
   "it with the host.\n"

static Wiper_State *wiper = NULL;

#define WIPER_STATE_CMD "disk.wiper.enable"

typedef enum {
   WIPER_UNAVAILABLE,
   WIPER_DISABLED,
   WIPER_ENABLED,
} WiperState;


/*
 *-----------------------------------------------------------------------------
 *
 * ShrinkGetWiperState  --
 *
 *      Gets the state of the shrink backend in the host.
 *
 * Results:
 *      The shrink backend state.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static WiperState
ShrinkGetWiperState(void)
{
   char *result = NULL;
   size_t resultLen;
   WiperState state = WIPER_UNAVAILABLE;

   if (ToolsCmd_SendRPC(WIPER_STATE_CMD, sizeof WIPER_STATE_CMD - 1,
                        &result, &resultLen)) {
      if (resultLen == 1 && strcmp(result, "1") == 0) {
         state = WIPER_ENABLED;
      } else {
         state = WIPER_DISABLED;
      }
   }
   ToolsCmd_FreeRPC(result);
   return state;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ShrinkGetMountPoints  --
 *
 *      Gets a list of wiper partitions.
 *
 * Results:
 *      The WiperPartion_List.
 *
 * Side effects:
 *      Prints to stderr on errors.
 *
 *-----------------------------------------------------------------------------
 */

static Bool
ShrinkGetMountPoints(WiperPartition_List *pl) // OUT: Known mount points
{
   WiperState state = ShrinkGetWiperState();

   switch (state) {
   case WIPER_UNAVAILABLE:
      ToolsCmd_PrintErr("%s",
                        SU_(disk.shrink.unavailable, SHRINK_FEATURE_ERR));
      break;

   case WIPER_DISABLED:
      ToolsCmd_PrintErr("%s",
                        SU_(disk.shrink.disabled, SHRINK_DISABLED_ERR));
      break;

   case WIPER_ENABLED:
      if (WiperPartition_Open(pl, TRUE)) {
         return TRUE;
      }

      ToolsCmd_PrintErr("%s",
                        SU_(disk.shrink.partition.error,
                           "Unable to collect partition data.\n"));
      break;

   default:
      NOT_REACHED();
   }

   return FALSE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ShrinkList  --
 *
 *      Prints mount points to stdout.
 *
 * Results:
 *      EXIT_SUCCESS.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
ShrinkList(void)
{
   WiperPartition_List plist;
   DblLnkLst_Links *curr;
   uint32 countShrink = 0;
   WiperState wstate = ShrinkGetWiperState();

   if (!ShrinkGetMountPoints(&plist)) {
      return EX_TEMPFAIL;
   }

   DblLnkLst_ForEach(curr, &plist.link) {
      WiperPartition *p = DblLnkLst_Container(curr, WiperPartition, link);
      if (p->type != PARTITION_UNSUPPORTED &&
          (wstate == WIPER_ENABLED || Wiper_IsWipeSupported(p))) {
         g_print("%s\n", p->mountPoint);
         countShrink++;
      }
   }

   WiperPartition_Close(&plist);

   /*
    * No shrinkable/wipable disks found.
    */
   if (countShrink == 0) {
      g_debug("No shrinkable disks found\n");
      ToolsCmd_PrintErr("%s",
                        SU_(disk.shrink.disabled, SHRINK_DISABLED_ERR));
      return EX_TEMPFAIL;
   }

   return EXIT_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ShrinkDiskSendRPC  --
 *
 *      Shrink all shrinkable disks/partitions, returning only when the shrink
 *      RPC operation is done or canceled.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on errors.
 *
 *-----------------------------------------------------------------------------
 */

static int
ShrinkDiskSendRPC(void)
{
   char *result = NULL;
   size_t resultLen;
   int retVal;

   ToolsCmd_PrintErr("\n");

   if (ToolsCmd_SendRPC(DISK_SHRINK_CMD, sizeof DISK_SHRINK_CMD - 1,
                        &result, &resultLen)) {
      ToolsCmd_Print("%s",
                     SU_(disk.shrink.complete, "Disk shrinking complete.\n"));
      retVal =  EXIT_SUCCESS;
   } else {
      ToolsCmd_PrintErr(SU_(disk.shrink.error, "Error while shrinking: %s\n"),
                        VM_SAFE_STR(result));
      retVal =  EX_TEMPFAIL;
   }

   ToolsCmd_FreeRPC(result);
   return retVal;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ShrinkDoAllDiskShrinkOnly  --
 *
 *      Shrink all shrinkable disks/partitions if it is enabled. Layered around
 *      ShrinkDoAllDiskShrinkCommon. This routine does not invoke the disk wipe
 *      operation step.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on errors.
 *
 *-----------------------------------------------------------------------------
 */

static int
ShrinkDoAllDiskShrinkOnly(void)
{
   WiperPartition_List plist;
   DblLnkLst_Links *curr;
   Bool canShrink = FALSE;
   WiperState wstate = ShrinkGetWiperState();

#ifndef _WIN32
   signal(SIGINT, ShrinkWiperDestroy);
#endif

   if (!ShrinkGetMountPoints(&plist)) {
      return EX_TEMPFAIL;
   }

   DblLnkLst_ForEach(curr, &plist.link) {
      WiperPartition *p = DblLnkLst_Container(curr, WiperPartition, link);
      if (p->type != PARTITION_UNSUPPORTED &&
          (wstate == WIPER_ENABLED || Wiper_IsWipeSupported(p))) {
         canShrink = TRUE;
         break;
      }
   }

   WiperPartition_Close(&plist);

   /*
    * Verify that shrinking is permitted on at least 1 disk.
    */

   if (!canShrink) {
      g_debug("No shrinkable disks found\n");
      ToolsCmd_PrintErr("%s",
                        SU_(disk.shrink.disabled, SHRINK_DISABLED_ERR));
      return EX_TEMPFAIL;
   }

   return ShrinkDiskSendRPC();
}


/*
 *-----------------------------------------------------------------------------
 *
 * ShrinkDoWipeAndShrink  --
 *
 *      Wipe a single partition, returning only when the wiper
 *      operation is done or canceled.
 *      Caller can optionally indicate whether a disk shrink operation is required
 *      to be performed after the wipe operation or not.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_OSFILE if partition is not found.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      The wipe operation will fill the partition with dummy files.
 *      Prints to stderr on errors.
 *
 *-----------------------------------------------------------------------------
 */

static int
ShrinkDoWipeAndShrink(char *mountPoint,         // IN: mount point
                      gboolean quiet,           // IN: verbosity flag
                      gboolean performShrink)   // IN: perform a shrink operation
{
   int i;
   int progress = 0;
   unsigned char *err;
   WiperPartition *part = NULL;
   WiperPartition_List plist;
   int rc;

#if defined(_WIN32)
   DWORD currPriority = GetPriorityClass(GetCurrentProcess());
#else
   signal(SIGINT, ShrinkWiperDestroy);
#endif

   if (ShrinkGetMountPoints(&plist)) {
      DblLnkLst_Links *curr, *nextElem;
      DblLnkLst_ForEachSafe(curr, nextElem, &plist.link) {
         WiperPartition *p = DblLnkLst_Container(curr, WiperPartition, link);
         if (toolbox_strcmp(p->mountPoint, mountPoint) == 0) {
            WiperSinglePartition_Close(part);
            part = p;
            /*
             * Detach the element we are interested in so it is not
             * destroyed when we call WiperPartition_Close.
             */
            DblLnkLst_Unlink1(&part->link);
            if (part->type != PARTITION_UNSUPPORTED) {
               break;
            }
         }
      }
      WiperPartition_Close(&plist);
   }

   if (part == NULL) {
      ToolsCmd_PrintErr(SU_(disk.shrink.partition.notfound,
                            "Unable to find partition %s\n"),
                        mountPoint);
      return EX_OSFILE;
   }

   if (part->type == PARTITION_UNSUPPORTED) {
      ToolsCmd_PrintErr(SU_(disk.shrink.partition.unsupported,
                            "Partition %s is not shrinkable\n"),
                        part->mountPoint);
      rc = EX_UNAVAILABLE;
      goto out;
   }

   /*
    * Verify that wiping/shrinking are permitted before going through with the
    * wiping operation.
    */
   if (ShrinkGetWiperState() != WIPER_ENABLED && !Wiper_IsWipeSupported(part)) {
      g_debug("%s cannot be wiped / shrunk\n", mountPoint);
      ToolsCmd_PrintErr("%s",
                        SU_(disk.shrink.disabled, SHRINK_DISABLED_ERR));
      rc = EX_TEMPFAIL;
      goto out;
   }

   /*
    * During the initial 'wipe' process, the Toolbox CLI first fills the
    * entire guest's disk space with files filled with zeroes. During this step,
    * user may notice few warning messages related to 'low disk space' in the
    * guest operating system. We need to print a warning message to disregard
    * such warnings in the guest operating system.
    */
   if (performShrink) {
      ToolsCmd_Print("%s", SU_(disk.shrink.ignoreFreeSpaceWarnings,
                               "Please disregard any warnings about disk space "
                               "for the duration of shrink process.\n"));
   } else {
      ToolsCmd_Print("%s", SU_(disk.wipe.ignoreFreeSpaceWarnings,
                               "Please disregard any warnings about disk space "
                               "for the duration of wipe process.\n"));
   }

   wiper = Wiper_Start(part, MAX_WIPER_FILE_SIZE);

#if defined(_WIN32)
   /*
    * On Win32, lower the process priority during wipe, so other applications
    * can still run (sort of) normally while we're filling the disk.
    */
   if (!SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS)) {
      g_debug("Unable to lower process priority: %u.", GetLastError());
   }
#endif

   while (progress < 100 && wiper != NULL) {
      err = Wiper_Next(&wiper, &progress);
      if (strlen(err) > 0) {
         if (strcmp(err, "error.create") == 0) {
            ToolsCmd_PrintErr("%s",
                              SU_(disk.wiper.file.error,
                                  "Error, Unable to create wiper file.\n"));
         } else {
            ToolsCmd_PrintErr(SU_(error.message, "Error: %s\n"), err);
         }
         /* progress < 100 will result in "rc" of EX_TEMPFAIL */
         break;
      }

      if (!quiet) {
         g_print(SU_(disk.wiper.progress, "\rProgress: %d"), progress);
         g_print(" [");
         for (i = 0; i <= progress / 10; i++) {
            putchar('=');
         }
         g_print(">%*c", 10 - i + 1, ']');
         fflush(stdout);
      }
   }

#if defined(_WIN32)
   /* Go back to our original priority. */
   if (!SetPriorityClass(GetCurrentProcess(), currPriority)) {
      g_debug("Unable to restore process priority: %u.", GetLastError());
   }
#endif

   g_print("\n");
   if (progress < 100) {
      rc = EX_TEMPFAIL;
   } else if (performShrink) {
      rc = ShrinkDiskSendRPC();
   } else {
      rc = EXIT_SUCCESS;
      g_debug("Shrink skipped.\n");
   }

   if (rc != EXIT_SUCCESS) {
      ToolsCmd_PrintErr("%s",
                        SU_(disk.shrink.incomplete, "Shrinking not completed.\n"));
   }

out:
   WiperSinglePartition_Close(part);
   free(wiper);
   wiper = NULL;
   return rc;
}


#ifndef _WIN32
/*
 *-----------------------------------------------------------------------------
 *
 * ShrinkWiperDestroy  --
 *
 *      Catch SIGINT and cancel wiper operation.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The wipe operation will be canceled, and "zero" files removed.
 *      We will also exit the vmware-toolbox-cmd program.
 *
 *-----------------------------------------------------------------------------
 */

void
ShrinkWiperDestroy(int signal)	// IN: Signal caught
{
   if (wiper != NULL) {
      Wiper_Cancel(&wiper);
      wiper = NULL;
   }
   ToolsCmd_Print("%s", SU_(disk.shrink.canceled, "Disk shrink canceled.\n"));
   exit(VM_EX_INTERRUPT);
}
#endif


/*
 *-----------------------------------------------------------------------------
 *
 * Disk_Command --
 *
 *      Handle and parse disk commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit code on errors.
 *
 * Side effects:
 *      Might shrink disk
 *
 *-----------------------------------------------------------------------------
 */

int
Disk_Command(char **argv,      // IN: command line arguments
             int argc,         // IN: The length of the command line arguments
             gboolean quiet)   // IN
{
   if (toolbox_strcmp(argv[optind], "list") == 0) {
      return ShrinkList();
   } else if (toolbox_strcmp(argv[optind], "shrink") == 0) {
      if (++optind >= argc) {
         ToolsCmd_MissingEntityError(argv[0], SU_(arg.mountpoint, "mount point"));
      } else {
         return ShrinkDoWipeAndShrink(argv[optind], quiet,
                                      TRUE /* perform shrink */);
      }
   } else if (toolbox_strcmp(argv[optind], "wipe") == 0) {
      if (++optind >= argc) {
         ToolsCmd_MissingEntityError(argv[0], SU_(arg.mountpoint, "mount point"));
      } else {
         return ShrinkDoWipeAndShrink(argv[optind], quiet,
                                      FALSE /* do not perform shrink */);
      }
   } else if (toolbox_strcmp(argv[optind], "shrinkonly") == 0) {
      return ShrinkDoAllDiskShrinkOnly();
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
   }
   return EX_USAGE;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Disk_Help --
 *
 *      Prints the help for the disk command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Disk_Help(const char *progName, // IN: The name of the program obtained from argv[0]
          const char *cmd)      // IN
{
   g_print(SU_(help.disk, "%s: perform disk shrink operations\n"
                          "Usage: %s %s <subcommand> [args]\n\n"
                          "Subcommands:\n"
                          "   list: list available locations\n"
                          "   shrink <location>: wipes and shrinks a file system at the given location\n"
                          "   shrinkonly: shrinks all disks\n"
                          "   wipe <location>: wipes a file system at the given location\n"),
           cmd, progName, cmd);
}

  070701000003FC000081A400000000000000000000000168225505000045F0000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-stat.c  /*********************************************************
 * Copyright (C) 2008-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-stat.c --
 *
 *     Various stat operations for toolbox-cmd
 */

#include <time.h>
#include "toolboxCmdInt.h"
#include "backdoor.h"
#include "backdoor_def.h"
#include "vmware/tools/i18n.h"


/*
 *-----------------------------------------------------------------------------
 *
 * OpenHandle  --
 *
 *      Opens a VMGuestLibHandle.
 *
 * Results:
 *      0 on success.
 *      EX_UNAVAILABLE on failure to open handle.
 *      EX_TEMPFAIL on failure to update info.
 *
 * Side effects:
 *      Prints to stderr and exits on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
OpenHandle(VMGuestLibHandle *glHandle, // OUT: The guestlib handle
           VMGuestLibError *glError)   // OUT: The errors when opening the handle
{
   *glError = VMGuestLib_OpenHandle(glHandle);
   if (*glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.openhandle.failed,
                            "OpenHandle failed: %s\n"),
                        VMGuestLib_GetErrorText(*glError));
      *glHandle = NULL;
      return EX_UNAVAILABLE;
   }
   *glError = VMGuestLib_UpdateInfo(*glHandle);
   if (*glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.update.failed,
                            "UpdateInfo failed: %s\n"),
                        VMGuestLib_GetErrorText(*glError));
      VMGuestLib_CloseHandle(*glHandle);
      *glHandle = NULL;
      return EX_TEMPFAIL;
   }
   return 0;  // We don't return EXIT_SUCCESSS to indicate that this is not
              // an exit code

}


/*
 *-----------------------------------------------------------------------------
 *
 * StatProcessorSpeed  --
 *
 *      Gets the Processor Speed.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatProcessorSpeed(void)
{
   int32 speed;
   Backdoor_proto bp;
   bp.in.cx.halfs.low = BDOOR_CMD_GETMHZ;
   Backdoor(&bp);
   speed = bp.out.ax.word;
   if (speed <= 0) {
      ToolsCmd_PrintErr("%s",
                        SU_(stat.getspeed.failed, "Unable to get processor speed.\n"));
      return EX_TEMPFAIL;
   }
   g_print(SU_(stat.processorSpeed.info, "%u MHz\n"), speed);
   return EXIT_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatHostTime  --
 *
 *      Gets the host machine's time.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatHostTime(void)
{
   int64 hostSecs;
   int64 hostUsecs;
   time_t sec;
   char buf[256];
   gchar *timeUtf8;
   Backdoor_proto bp;

   bp.in.cx.halfs.low = BDOOR_CMD_GETTIMEFULL;
   Backdoor(&bp);
   if (bp.out.ax.word == BDOOR_MAGIC) {
      hostSecs = ((uint64)bp.out.si.word << 32) | bp.out.dx.word;
   } else {
      /* Falling back to older command. */
      bp.in.cx.halfs.low = BDOOR_CMD_GETTIME;
      Backdoor(&bp);
      /*
       * This backdoor returns uint32 time value in bp.out.ax.word or
       * MAX_UINT32 in case of error.
       */
      if (bp.out.ax.word == MAX_UINT32) {
         hostSecs = -1;
      } else {
         hostSecs = bp.out.ax.word;
      }
   }
   hostUsecs = bp.out.bx.word;

   if (hostSecs <= 0) {
      ToolsCmd_PrintErr("%s",
                        SU_(stat.gettime.failed, "Unable to get host time.\n"));
      return EX_TEMPFAIL;
   }

   sec = hostSecs + (hostUsecs / 1000000);
   if (strftime(buf, sizeof buf, "%d %b %Y %H:%M:%S", localtime(&sec)) == 0) {
      ToolsCmd_PrintErr("%s",
                        SU_(stat.formattime.failed, "Unable to format host time.\n"));
      return EX_TEMPFAIL;
   }

   timeUtf8 = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
   if (timeUtf8 == NULL) {
      ToolsCmd_PrintErr("%s",
                        SU_(stat.formattime.failed, "Unable to format host time.\n"));
      return EX_TEMPFAIL;
   }

   g_print("%s\n", timeUtf8);
   g_free(timeUtf8);
   return EXIT_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatGetSessionID --
 *
 *      Gets the Session ID for the virtual machine
 *      Works only if the host is ESX.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatGetSessionID(void)
{
   int exitStatus;
   uint64 session;
   VMGuestLibHandle glHandle;
   VMGuestLibError glError;

   exitStatus = OpenHandle(&glHandle, &glError);
   if (exitStatus) {
      return exitStatus;
   }
   glError = VMGuestLib_GetSessionId(glHandle, &session);
   if (glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.getsession.failed,
                            "Failed to get session ID: %s\n"),
                        VMGuestLib_GetErrorText(glError));
      exitStatus = EX_TEMPFAIL;
   } else {
      g_print("0x%"FMT64"x\n", session);
   }
   VMGuestLib_CloseHandle(glHandle);
   return exitStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatGetMemoryBallooned  --
 *
 *      Retrieves memory ballooned.
 *      Works only if the host is ESX.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatGetMemoryBallooned(void)
{
   int exitStatus;
   uint32 memBallooned;
   VMGuestLibHandle glHandle;
   VMGuestLibError glError;

   exitStatus = OpenHandle(&glHandle, &glError);
   if (exitStatus) {
      return exitStatus;
   }
   glError = VMGuestLib_GetMemBalloonedMB(glHandle, &memBallooned);
   if (glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.balloon.failed,
                            "Failed to get ballooned memory: %s\n"),
                        VMGuestLib_GetErrorText(glError));
      exitStatus = EX_TEMPFAIL;
   } else {
      g_print(SU_(stat.memoryBalloon.info, "%u MB\n"), memBallooned);
   }
   VMGuestLib_CloseHandle(glHandle);
   return exitStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatGetMemoryReservation  --
 *
 *      Retrieves min memory.
 *      Works only if the host is ESX.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatGetMemoryReservation(void)
{
   int exitStatus;
   uint32  memReservation;
   VMGuestLibHandle glHandle;
   VMGuestLibError glError;

   exitStatus = OpenHandle(&glHandle, &glError);
   if (exitStatus) {
      return exitStatus;
   }
   glError = VMGuestLib_GetMemReservationMB(glHandle, &memReservation);
   if (glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.memres.failed,
                            "Failed to get memory reservation: %s\n"),
                        VMGuestLib_GetErrorText(glError));
      exitStatus = EX_TEMPFAIL;
   } else {
      g_print(SU_(stat.memoryReservation.info, "%u MB\n"), memReservation);
   }
   VMGuestLib_CloseHandle(glHandle);
   return exitStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatGetMemorySwapped  --
 *
 *      Retrieves swapped memory.
 *      Works only if the host is ESX.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on error.
 *      If OpenHandle fails the program exits.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatGetMemorySwapped(void)
{
   int exitStatus;
   uint32 memSwapped;
   VMGuestLibHandle glHandle;
   VMGuestLibError glError;

   exitStatus = OpenHandle(&glHandle, &glError);
   if (exitStatus) {
      return exitStatus;
   }
   glError = VMGuestLib_GetMemSwappedMB(glHandle, &memSwapped);
   if (glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.memswap.failed,
                            "Failed to get swapped memory: %s\n"),
                        VMGuestLib_GetErrorText(glError));
      exitStatus = EX_TEMPFAIL;
   } else {
      g_print(SU_(stat.memorySwapped.info, "%u MB\n"), memSwapped);
   }
   VMGuestLib_CloseHandle(glHandle);
   return exitStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatGetMemoryLimit --
 *
 *      Retrieves max memory.
 *      Works only if the host is ESX.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatGetMemoryLimit(void)
{
   int exitStatus;
   uint32 memLimit;
   VMGuestLibHandle glHandle;
   VMGuestLibError glError;

   exitStatus = OpenHandle(&glHandle, &glError);
   if (exitStatus) {
      return exitStatus;
   }
   glError = VMGuestLib_GetMemLimitMB(glHandle, &memLimit);
   if (glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.maxmem.failed,
                            "Failed to get memory limit: %s\n"),
                        VMGuestLib_GetErrorText(glError));
      exitStatus = EX_TEMPFAIL;
   } else {
      g_print(SU_(stat.memoryLimit.info, "%u MB\n"), memLimit);
   }
   VMGuestLib_CloseHandle(glHandle);
   return exitStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatGetCpuReservation  --
 *
 *      Retrieves cpu min speed.
 *      Works only if the host is ESX.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatGetCpuReservation(void)
{
   int exitStatus;
   uint32 cpuReservation;
   VMGuestLibHandle glHandle;
   VMGuestLibError glError;

   exitStatus = OpenHandle(&glHandle, &glError);
   if (exitStatus) {
      return exitStatus;
   }
   glError = VMGuestLib_GetCpuReservationMHz(glHandle, &cpuReservation);
   if (glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.cpumin.failed,
                            "Failed to get CPU minimum: %s\n"),
                        VMGuestLib_GetErrorText(glError));
      exitStatus = EX_TEMPFAIL;
   } else {
      g_print(SU_(stat.cpuReservation.info, "%u MHz\n"), cpuReservation);
   }
   VMGuestLib_CloseHandle(glHandle);
   return exitStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatGetCpuLimit  --
 *
 *      Retrieves cpu max speed .
 *      Works only if the host is ESX.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatGetCpuLimit(void)
{
   int exitStatus;
   uint32 cpuLimit;
   VMGuestLibHandle glHandle;
   VMGuestLibError glError;

   exitStatus = OpenHandle(&glHandle, &glError);
   if (exitStatus) {
      return exitStatus;
   }
   glError = VMGuestLib_GetCpuLimitMHz(glHandle, &cpuLimit);
   if (glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.cpumax.failed,
                            "Failed to get CPU limit: %s\n"),
                        VMGuestLib_GetErrorText(glError));
      exitStatus = EX_TEMPFAIL;
   } else {
      g_print(SU_(stat.cpuLimit.info, "%u MHz\n"), cpuLimit);
   }
   VMGuestLib_CloseHandle(glHandle);
   return exitStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StatGetRaw  --
 *
 *      Retrieves semi-structured stat.
 *      Works only if the host is ESX.
 *
 * Results:
 *      EXIT_SUCCESS on success.
 *      EX_TEMPFAIL on failure.
 *
 * Side effects:
 *      Prints to stderr on error.
 *
 *-----------------------------------------------------------------------------
 */

static int
StatGetRaw(const char *encoding,  // IN
           const char *stat,      // IN
           const char *param)     // IN
{
   int exitStatus = EXIT_SUCCESS;
   VMGuestLibError glError;
   char *result = NULL;
   size_t resultSize = 0;
   char *arg = g_strdup_printf("%s %s", stat, param);

   glError = VMGuestLib_StatGet(encoding, arg, &result, &resultSize);
   if (glError != VMGUESTLIB_ERROR_SUCCESS) {
      ToolsCmd_PrintErr(SU_(stat.get.failed,
                            "Failed to get stat: %s\n"),
                        VMGuestLib_GetErrorText(glError));
      exitStatus = EX_TEMPFAIL;
   } else {
      g_print("%*s", (int)resultSize, result);
   }
   VMGuestLib_StatFree(result, resultSize);
   g_free(arg);
   return exitStatus;
}


/*
 *-----------------------------------------------------------------------------
 *
 * Stat_Command --
 *
 *      Handle and parse stat commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit codes on errors.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
Stat_Command(char **argv,      // IN: Command line arguments
             int argc,         // IN: Length of command line arguments
             gboolean quiet)   // IN
{
   if (toolbox_strcmp(argv[optind], "hosttime") == 0) {
      return StatHostTime();
   } else if (toolbox_strcmp(argv[optind], "sessionid") == 0) {
      return StatGetSessionID();
   } else if (toolbox_strcmp(argv[optind], "balloon") == 0) {
      return StatGetMemoryBallooned();
   } else if (toolbox_strcmp(argv[optind], "swap") == 0) {
      return StatGetMemorySwapped();
   } else if (toolbox_strcmp(argv[optind], "memlimit") == 0) {
      return StatGetMemoryLimit();
   } else if (toolbox_strcmp(argv[optind], "memres") == 0) {
      return StatGetMemoryReservation();
   } else if (toolbox_strcmp(argv[optind], "cpures") == 0) {
      return StatGetCpuReservation();
   } else if (toolbox_strcmp(argv[optind], "cpulimit") == 0) {
      return StatGetCpuLimit();
   } else if (toolbox_strcmp(argv[optind], "speed") == 0) {
      return StatProcessorSpeed();
   } else if (toolbox_strcmp(argv[optind], "raw") == 0) {
      return StatGetRaw((optind + 1 < argc) ? argv[optind + 1] : "", // encoding
                        (optind + 2 < argc) ? argv[optind + 2] : "", // stat
                        (optind + 3 < argc) ? argv[optind + 3] : "");// param
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
      return EX_USAGE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * Stat_Help --
 *
 *      Prints the help for the stat command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
Stat_Help(const char *progName, // IN: The name of the program obtained from argv[0]
          const char *cmd)      // IN
{
   g_print(SU_(help.stat, "%s: print useful guest and host information\n"
                          "Usage: %s %s <subcommand>\n\n"
                          "Subcommands:\n"
                          "   hosttime: print the host time\n"
                          "   speed: print the CPU speed in MHz\n"
                          "ESX guests only subcommands:\n"
                          "   sessionid: print the current session id\n"
                          "   balloon: print memory ballooning information\n"
                          "   swap: print memory swapping information\n"
                          "   memlimit: print memory limit information\n"
                          "   memres: print memory reservation information\n"
                          "   cpures: print CPU reservation information\n"
                          "   cpulimit: print CPU limit information\n"
                          "   raw [<encoding> <stat name>]: print raw stat information\n"
                          "      <encoding> can be one of 'text', 'json', 'xml', 'yaml'.\n"
                          "      <stat name> includes session, host, resources, vscsi and\n"
                          "      vnet (Some stats like vscsi are two words, e.g. 'vscsi scsi0:0').\n"
                          "      Prints the available stats if <encoding> and <stat name>\n"
                          "      arguments are not specified.\n"),
           cmd, progName, cmd);
}

070701000003FD000081A40000000000000000000000016822550500001B12000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd-time.c  /*********************************************************
 * Copyright (C) 2008-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd-time.c --
 *
 *     The time sync operations for toolbox-cmd
 */

#include "toolboxCmdInt.h"
#include "backdoor.h"
#include "backdoor_def.h"
#include "vmware/guestrpc/tclodefs.h"
#include "vmware/guestrpc/timesync.h"
#include "vmware/tools/i18n.h"
#include "vmware/tools/log.h"


/*
 *-----------------------------------------------------------------------------
 *
 * TimeSyncGetOldOptions --
 *
 *    Retrieve the tools options from VMware using the old (deprecated) method.
 *
 * Return value:
 *    The tools options
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

static uint32
TimeSyncGetOldOptions(void)
{
   Backdoor_proto bp;
   bp.in.cx.halfs.low = BDOOR_CMD_GETGUIOPTIONS;
   Backdoor(&bp);
   return bp.out.ax.word;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  TimeSyncSet --
 *
 *      Enable/disable time sync
 *
 * Results:
 *      None
 *
 * Side effects:
 *      If time syncing is turned on the system time may be changed
 *      Prints to stderr and exits on errors
 *
 *-----------------------------------------------------------------------------
 */

static gboolean
TimeSyncSet(Bool enable,      // IN:
            char **reply,     // OUT:
            size_t *replyLen) // OUT:
{
   gchar *msg = g_strdup_printf("vmx.set_option %s %s %s",
                                TOOLSOPTION_SYNCTIME,
                                !enable ? "1" : "0",
                                enable ? "1" : "0");
   return ToolsCmd_SendRPC(msg, strlen(msg) + 1, reply, replyLen);
}


/*
 *-----------------------------------------------------------------------------
 *
 *  TimeSyncEnable --
 *
 *      Enable time sync.
 *
 * Results:
 *      EXIT_SUCCESS on success, EXIT_FAILURE on failure.
 *
 * Side effects:
 *      Same as TimeSyncSet.
 *
 *-----------------------------------------------------------------------------
 */

static int
TimeSyncEnable(void)
{
   int ret = EXIT_SUCCESS;
   char *reply = NULL;
   size_t replyLen;

   if ((TimeSyncGetOldOptions() & VMWARE_GUI_SYNC_TIME) ||
       TimeSyncSet(TRUE, &reply, &replyLen)) {
      ToolsCmd_Print("%s\n", SU_(option.enabled, "Enabled"));
   } else {
      ToolsCmd_PrintErr(SU_(error.message, "Error: %s\n"),
                        VM_SAFE_STR(reply));
      ret = EXIT_FAILURE;
   }

   ToolsCmd_FreeRPC(reply);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  TimeSyncDisable --
 *
 *      Disable time sync.
 *
 * Results:
 *      EXIT_SUCCESS on success, EXIT_FAILURE on failure.
 *
 * Side effects:
 *      Same as TimeSyncSet.
 *
 *-----------------------------------------------------------------------------
 */

static int
TimeSyncDisable(void)
{
   int ret = EXIT_SUCCESS;
   char *reply = NULL;
   size_t replyLen;

   if (!(TimeSyncGetOldOptions() & VMWARE_GUI_SYNC_TIME) ||
       TimeSyncSet(FALSE, &reply, &replyLen)) {
      ToolsCmd_Print("%s\n", SU_(option.disabled, "Disabled"));
   } else {
      ToolsCmd_PrintErr(SU_(error.message, "Error: %s\n"),
                        VM_SAFE_STR(reply));
      ret = EXIT_FAILURE;
   }

   ToolsCmd_FreeRPC(reply);
   return ret;
}


/*
 *-----------------------------------------------------------------------------
 *
 *  TimeSyncStatus --
 *
 *      Checks the status of time sync in VMX.
 *
 * Results:
 *      EXIT_SUCCESS: time sync is enabled.
 *      EX_UNAVAILABLE: time sync is disabled.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

static int
TimeSyncStatus(void)
{
   Bool status = FALSE;
   if (TimeSyncGetOldOptions() & VMWARE_GUI_SYNC_TIME) {
      status = TRUE;
   }
   if (status) {
      ToolsCmd_Print("%s\n", SU_(option.enabled, "Enabled"));
      return EXIT_SUCCESS;
   } else {
      ToolsCmd_Print("%s\n", SU_(option.disabled, "Disabled"));
      return EX_UNAVAILABLE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * TimeSyncCommand --
 *
 *      Parse and Handle timesync commands.
 *
 * Results:
 *      Returns EXIT_SUCCESS on success.
 *      Returns the appropriate exit code errors.
 *
 * Side effects:
 *      Might enable time sync, which would change the time in the guest os.
 *
 *-----------------------------------------------------------------------------
 */

int
TimeSync_Command(char **argv,     // IN: command line arguments
                 int argc,        // IN: The length of the command line arguments
                 gboolean quiet)  // IN
{
   if (toolbox_strcmp(argv[optind], "enable") == 0) {
      return TimeSyncEnable();
   } else if (toolbox_strcmp(argv[optind], "disable") == 0) {
      return TimeSyncDisable();
   } else if (toolbox_strcmp(argv[optind], "status") == 0) {
      return TimeSyncStatus();
   } else {
      ToolsCmd_UnknownEntityError(argv[0],
                                  SU_(arg.subcommand, "subcommand"),
                                  argv[optind]);
      return EX_USAGE;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * TimeSync_Help --
 *
 *      Prints the help for timesync command.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

void
TimeSync_Help(const char *progName, // IN: The name of the program obtained from argv[0]
              const char *cmd)      // IN
{
   g_print(SU_(help.timesync, "%s: functions for controlling time synchronization on the guest OS\n"
                              "Usage: %s %s <subcommand>\n\n"
                              "Subcommands:\n"
                              "   enable: enable time synchronization\n"
                              "   disable: disable time synchronization\n"
                              "   status: print the time synchronization status\n"),
           cmd, progName, cmd);
}

  070701000003FE000081A40000000000000000000000016822550500000625000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/toolbox/toolboxcmd_version.h   /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * toolboxcmd_version.h --
 *
 * Version definitions for the vmware-toolbox-cmd program
 */

#ifndef _TOOLBOXCMD_VERSION_H_
#define _TOOLBOXCMD_VERSION_H_

/*
 * This component's version is coupled with Tools versioning. The effect
 * is that the version increments with each build, and with each Tools
 * version bump. If and when it becomes necessary to version the component
 * manually, make sure that the version is bumped any time the component or
 * its dependencies are changed.
 */
#include "vm_tools_version.h"
#define TOOLBOXCMD_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define TOOLBOXCMD_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _TOOLBOXCMD_VERSION_H_ */
   070701000003FF000081A40000000000000000000000016822550500004CCB000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/tools.conf # ************************************************************************
# Copyright (c) 2019-2024 Broadcom.  All Rights Reserved.
# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
# *************************************************************************

# "CAUTION: tools.conf is highly syntax sensitive file. Use extreme caution
# while editing it. If modified, it is automatically re-loaded by
# VMware Tools services every 5 seconds."
#
# Lines must not end with trailing white space.

[unsetenvironment]
# Defines environment variables to be removed from the service reading
# the configuration file. Supported formats are:
#
# 1. <variableName>=
# 2. <serviceName>.<variableName>=
#
# Where <serviceName> refers to the 'vmsvc' and 'vmusr',
# <variableName> refers to the name of the environment
# variable to be removed. '=' sign after <variableName>
# is mandatory to maintain the configuration file syntax.
# However, anything after '=' is ignored.
#
# Case-sensitive behavior is defined by the operating system.
#
# Note: unsetenvironment group is processed before setenvironment group.
# As the service environment is setup at start up time, any changes
# in this group require service to be restarted in order to take effect.
#
# Unsetting PATH for all services:
# PATH=
#
# Unsetting PATH for vmsvc only:
# vmsvc.PATH=
#
# Unsetting PATH for vmusr only:
# vmusr.PATH=

[setenvironment]
# Defines environment variables to be set for the service reading
# the configuration file. Supported formats are:
#
# 1. <variableName>=<variableValue>
# 2. <serviceName>.<variableName>=<variableValue>
#
# Where <serviceName> refers to the 'vmsvc' and 'vmusr',
# <variableName> refers to the name of the environment
# variable to be set, and <variableValue> refers to the
# value to be assigned to the environment variable.
#
# Case-sensitive behavior is defined by the operating system.
#
# Note: setenvironment group is processed after unsetenvironment group.
# As the service environment is setup at start up time, any changes
# in this group require service to be restarted in order to take effect.
#
# Setting TMPDIR for all services:
# TMPDIR=/vmware/temp
#
# Setting TMPDIR for vmsvc only:
# vmsvc.TMPDIR=/vmware/vmsvc/temp
#
# Setting TMPDIR for vmusr only:
# vmusr.TMPDIR=/vmware/vmusr/temp

[logging]
# set to false to turn off logging
#log = true

# Log destinations for various services
# By default, logs go to
# %windir%/temp/vmware-<servicename>.log
# for Windows, and
# /var/log/vmware-<servicename>-<username>.log
# for Linux, MacOS and Solaris.

# Possible values for handler are:
# file: logs to a file. Set *.data to the file name
# file+: same as 'file', but appends to the file
#  All file paths used in *.data value need to be in Unix
#  format (forward slashes) and in utf-8, for all operating
#  systems.
# vmx: logs to the host (ESXi, Workstation, Fusion)
# std: Logs to stdout for level >= 'message',
# and to stderr for more severe than 'message'.
# syslog: logs to syslog
# outputdebugstring: uses OutputDebugString (Windows only)

# If handler is 'syslog' and the OS is Linux, the facility
# can be set with <domain>.facility. The facility value can be one of
# 'local0'..'local7', 'daemon' or 'user'. The default is 'user'.
#vmtoolsd.facility = user

# possible values for level are:
# debug, info, message, warning, critical, error
# Note that "debug" level logs generate huge amounts of logs and may also
# include sensitive data required for diagnosis. Therefore, this level should
# be used only for the duration of diagnosis of an issue and reverted back to
# default setting post diagnosis.

# Enable tools service logging to a file.
#vmtoolsd.level = debug
#vmtoolsd.handler = file
#vmtoolsd.data = c:/tmp/vmtoolsd-${USER}.log

# Enable 'vmsvc' service logging to a file.
#vmsvc.level = debug
#vmsvc.handler = file
#vmsvc.data = c:/tmp/vmsvc.log

# Enable VMwareResolutionSet.exe logging to a file.
# Comment this for Linux guest, sometimes vmusr logs are not generated due
# to this being uncommented
#vmresset.level = debug
#vmresset.handler = file+
#vmresset.data = c:/tmp/vmresset.log

# Enable new "vmusr" service logging to a file.
#vmusr.level = debug
#vmusr.handler = file
#vmusr.data = c:/tmp/vmusr.${USER}.log

# Set the following configuration if you want to collect the logs for
# vmware-toolbox-cmd utility
#toolboxcmd.level = debug
#toolboxcmd.handler = file
#toolboxcmd.data = c:/tmp/vmtoolboxcmd-${USER}.log

# With no explicit logging configuration for deployPkg, its default log path in
# Linux is /var/log/vmware-imc/toolsDeployPkg.log, and in Windows is
# %WINDIR%/Temp/vmware-imc/toolsDeployPkg.log
# Set the following configuration if you want to redirect the deployPkg log to
# any existing location other than the default.
#deployPkg.level = debug
#deployPkg.handler = file
#deployPkg.data = c:/tmp/toolsDeployPkg-${USER}.log

# Redirecting the deployPkg log to the vmx log file. Please note that
# "log = true" and the vmsvc log handler setting to vmx are also neccessary
# if you want to redirect the deployPkg log to vmx.
#log = true
#vmsvc.level = debug
#vmsvc.handler = vmx
#deployPkg.level = debug
#deployPkg.handler = vmx

# Enable old VMwareUser/vmware-user logging to file.
#log.file = c:/tmp/vmtools.log

# Enable "hgfsServer" request handling logging to the appropriate service file.
#hgfsServer.level = debug

# Enable  "hgfs" manager and transport logging to the appropriate service file.
#hgfsd.level = debug

#vmbackup.level = debug
#vmbackup.handler = vmx

#vmvss.level = debug
#vmvss.handler = vmx

# Default 4096, 0=> deactivate log caching
#maxCacheEntries=4096


# Set the following configurations for modifying network script logging file.
# Only for Linux, Mac OS X, Solaris, and FreeBSD
#network.handler = file
#network.data = /tmp/network.log
#network.maxOldLogFiles = 9

# Redirect network script logs to vmx
#network.handler = vmx

[powerops]
# Custom scripts for power operations
# This can be an absolute path, or a path relative to the tools
# install path (/etc/vmware-tools/ for Linux).
# For more information on configuring and querying custom scripts with
# VMware Tools, see the "Use Custom VMware Tools Scripts" section of the
# "VMware Tools Configuration Utility User's Guide".

# Runs when the virtual machine is being powered on rather than resumed.
# Also runs after virtual machine restarts.
# The default script has no effect on networking for the virtual machine.
#poweron-script=poweron-vm-default

# Runs when the virtual machine is being powered off or reset.
# The default script has no effect on networking for the virtual machine.
#poweroff-script=poweroff-vm-default

# Runs when the virtual machine is resumed after it was suspended.
# On Windows guest operating systems, if the virtual machine is configured to
# use DHCP, the default script renews the IP address of the virtual machine.
# On Linux, Mac OS X, Solaris, and FreeBSD guest operating systems,
# this script starts networking for the virtual machine.
#resume-script=resume-vm-default

# Runs when the virtual machine is being suspended.
# On Windows guest operating systems, if the virtual machine is configured
# to use DHCP, the default script releases the IP address of the virtual
# machine.
# On Linux, Mac OS X, Solaris, and FreeBSD, this script stops networking for
# the virtual machine.
#suspend-script=suspend-vm-default

[guestinfo]

# Set to true to deactivate the perf monitor.
#disable-perf-mon=false

# Set to true to deactivate DiskInfo.
#disable-query-diskinfo=false

# User-defined poll interval in seconds. Set to 0 to deactivate polling.
#poll-interval=30

# User-defined stats interval in seconds. Set to 0 to deactivate stats collection.
#stats-interval=20

# Whether stat results should be written to the log.
#enable-stat-logging=false

# Set a comma separated list of network interface names that can be the
# primary ones. These will be sorted to the top. Interface names can use
# wildcards like '*' and '?'. Default is no value.
#primary-nics=

# Set a comma separated list of network interface names that have low priority
# (so they will be sorted to the end). Interface names can use wildcards like
# '*' and '?'. Default is no value.
#low-priority-nics=

# Set a comma separated list of network interface names that shall be ignored.
# Interface names can use wildcards like '*' and '?'.
# Default for Linux and all non-Windows:
#exclude-nics=veth*,docker*,virbr*,antrea-*,cali*
# Default for Windows:
#exclude-nics=vEthernet*

# max umber of IPv4 routes to gather.
#max-ipv4-routes=100

# max umber of IPv6 routes to gather.
#max-ipv6-routes=100

# whether to include reserved space in diskInfo space metrics on Linux
#diskinfo-include-reserved=false

[globalconf]

# The GlobalConf feature provides an ability for the vSphere administrators
# to distribute a 'VMware Tools Configuration File' (tools.conf) via the
# GuestStore for multiple VMs at scale.

# Defines the configuration to activate/deactivate the GlobalConf module.
# Set to true to enable(activate) the module.
# Set to false to deactivate the module. Default false.
#enabled=false

# Defines a custom GlobalConf poll interval (in seconds).
# Default 3600 seconds. Minimum 1800 seconds.
#poll-interval=3600

# Defines the global configuration resource in GuestStore.
# Windows guests
#resource=/vmware/configurations/vmtools/windows/tools.conf
#
# Linux guests
#resource=/vmware/configurations/vmtools/linux/tools.conf

[componentmgr]

# This plugin manages the known and enabled components add/remove status.
# The plugin polls at regular interval and triggers action add/remove for
# all the known and enabled components in the componentMgr plugin.

# Default and minimum polling interval in seconds (0 => polling deactivated)
#poll-interval=180

# Comma separated list of components managed by the plugin. If not specified,
# default value is all, which means all components are enabled by default.
# A special value of none means no component, which is equivalent to disabling
# the plugin completely. Value is parsed left to right and parsing stops at
# first occurrence of all or none or end of line.
#included=all

[appinfo]

# This plugin collects info about running applications in guest OS.

# Set to true to deactivate the appinfo plugin.
#disabled=false

# User-defined poll interval in seconds. Set to 0 to deactivate the plugin.
#poll-interval=21600

# For Windows guest, set to true to use WMI for getting the application
# version info, otherwise native Win32 API is used.
#useWMI=false

# Whether to remove the duplicate applications information in the
# guestinfo variable.
#remove-duplicates=true

[containerinfo]

# This plugin collects info about running containers in guest OS.

# User-defined poll interval in seconds. Set to 0 to deactivate the plugin.
#poll-interval=21600

# Maximum number of containers to be retrieved per namespace.
#max-containers=256

# Whether to remove the duplicate containers information in the
# guestinfo variable.
#remove-duplicates=true

# Unix socket to use to communicate with the docker daemon.
#docker-unix-socket=/var/run/docker.sock

# The unix socket to connect to communicate with containerd grpc server
# for retrieving the list of running containers.
#containerd-unix-socket=/run/containerd/containerd.sock

# List of namespaces to be queried for the running containers.
# The value for this key is a comman separated list.
#allowed-namespaces=moby,k8s.io,default

[servicediscovery]

# This plugin provides admins with additional info for better VM management.

# Set to true to deactivate the servicediscovery plugin.
#disabled=false

[unity]
#
# Unity is available for Windows only.
#

# Set to true to override system decisions about whether unity should be available.
#forceEnable=false

# Override the desktop background color when in Unity mode.
#desktop.backgroundColor=

# The socket type can be 'ipsocket' or 'vsocket':
#pbrpc.socketType

[resolutionKMS]

# Default is true if tools finds an xf86-video-vmware driver with
# version >= 13.2.0. If you don't have X installed, set this to true manually.
# This only affects tools for Linux.
#enable=true

[guestosinfo]

# Override the short OS name sent by tools.
#short-name=

# Override the long OS name sent by tools. Ignored if short-name is not set.
#long-name=

[vmbackup]

# enableSyncDriver is Linux only.
#enableSyncDriver=true

# enableVSS is Windows only.
#enableVSS=true

# vss.disableAppQuiescing is Windows only.
# This setting can be used to force file system quiescing on Windows systems
# having problems with application quiescing.
# See https://kb.vmware.com/s/article/2146204
#vss.disableAppQuiescing=false

# Linux:
# The value of excludedFileSystems is a comma-separated list of glob-style
# patterns specifying the file system mount points to be excluded from
# quiescing operation. The patterns may use '*' (wildcard) to represent
# any string of characters and '?' (joker) to represent any single character.
# The characters represented by the patterns '*' and '?' may include any
# characters, including '/'.
#
# Windows:
# The value of excludedFileSystems is a comma-separated list of mount points
# specifying the volumes to be excluded from quiesced snapshots.
# Each mount point must be a full path separated and ended with "\\".
# For example, to exclude volumes with drive letter 'E' or mount point
# "F:\mount\", use:
# excludedFileSystems=E:\\,F:\\mount\\
# This setting is ignored when application quiescing is used.

#excludedFileSystems=

# Linux:
# It is possible that filesystems are being frozen in pre-freeze scripts
# to control the order in which those specific filesystems are to be frozen.
# The vmtoolsd process must be informed of all such filesystems with the help
# of "excludedFileSystems" setting of tools.conf.
#
# A temporary workaround is available (starting from 12.3.0) for admins to allow
# quiesceing operation to succeed until the "excludedFileSystems" list
# is configured.
#
# If another process thaws the file system while a quiescing operation
# operation is ongoing, the snapshot may be compromised. Once the
# "excludedFileSystems" list is configured this setting MUST be unset (or set
# to false).
#
# The value of ignoreFrozenFileSystems is a true or false; the default is
# false.
#
# Set to true to ignore pre-frozen file systems during the quiescing operation.
#
# ignoreFrozenFileSystems is Linux only (Not supported on Windows).
#ignoreFrozenFileSystems=false

# execScripts specifies whether to execute scripts as part of the quiescing
# operation. Scripts are executed from the scripts directory along with the
# legacy scripts.
#
# Scripts directory:
# Linux: /etc/vmware-tools/backupScripts.d
# Windows: <Install-Path>\backupScripts.d
#
# Legacy scripts:
# Linux: /usr/sbin/pre-freeze-script and /usr/sbin/post-thaw-script
# Windows: C:\windows\pre-freeze-script.bat and C:\windows\post-thaw-script.bat
#
# On each quiescing operation, scripts are invoked before quiescing and
# either after a quiescing failure or after thawing.
# The first argument passed to each script is
# "freeze", when invoked before quiescing;
# "freezefail", when invoked after a quiescing failure; or
# "thaw", when invoked after thawing.
# When invoked before quiescing, scripts from the directory are invoked in
# alphabetically ascending order; when invoked following a quiescing failure
# or thawing, they are invoked in the reverse order. Any subdirectories are
# ignored.
# Note that the legacy pre-freeze-script is invoked only before quiescing as
# the first script and post-thaw-script is invoked after a quiescing failure
# as well as after thawing as the last script.

#execScripts=true

# Additional argument to be passed to scripts
#scriptArg=

[guestoperations]

# to deactivate all guest ops
#disabled=false

# Whether to use vgauth for guest op authentication
#useVGAuth=true

[autoupgrade]

# The "allow-upgrade" option controls whether automatic upgrades (or reinstalls)
# are allowed.
#allow-upgrade=true

# The autoupgrade plugin is only available for Windows.
# The "allow-add-feature" and "allow-remove-feature" control whether adding
# or removing a feature will be allowed.
# The allow-msi-transforms option controls whether TRANSFORMS property is
# allowed.

#allow-add-feature=true
#allow-remove-feature=true
#allow-msi-transforms=false

[deployPkg]

# to deactivate guest customization
#enable-customization=false

# This "wait-cloudinit-timeout" option controls how long does guest
# customization wait for cloud-init execution done when it detects cloud-init
# is available in guest.
# Guest customization will continue executing as soon as it detects cloud-init
# execution done within this option's value in seconds.
# If cloud-init is still running beyond this option's value in seconds, guest
# customization will continue executing regardless cloud-init execution status.
# Minimum valid value is 0 second, set to 0 to deactivate waiting.
# Maximum valid value is 1800 seconds (30 minutes).
# Default value is 30 seconds.
#wait-cloudinit-timeout=30

[cbhelper]

# The carbonblack helper plugin is only available for Windows.

# User-defined poll interval in seconds. Set to 0 to deactivate polling.
#poll-interval=60

[gueststoreupgrade]

# The guestStoreUpgrade plugin is only available for Windows.

# The policy value is one of the settings listed below.
# off         = no VMware Tools upgrade from GuestStore. Feature is
#               deactivated.
# manual      = (Default) VMware Tools upgrade from GuestStore is
#               manually started.
# powercycle  = VMware Tools upgrade from GuestStore on system
#               power on.

#policy=manual

# Time interval for periodically checking available VMware Tools package
# version in the GuestStore.
# User-defined poll interval in seconds. Set to 0 to deactivate polling.
# Minimum valid value is 900 seconds (15 minutes)
# Default value is 3600 seconds (60 minutes)
#poll-interval=3600

# VMware Tools package version metadata key to specify a VMware Tools
# package version in the GuestStore.
# User-defined key for VMware Tools package version.
# Default value is "vmtools" which points to the latest version of
# VMware Tools package in the GuestStore.
#vmtools-version-key=vmtools

[devicehelper]

# The deviceHelper plugin is only available for Windows.

# Set to true to deactivate the deviceHelper plugin.
#disabled=false

[gitray]

# The gitray plugin is only available for Windows
# with Complete VMTools install or with File
# Introspection Custom install.

# By default the gitray plugin is enabled
# To disable gitray user plugin set
#enabled=false

[giamsi]

# The giamsi plugin for the Windows AntiMalware Scan Interface (AMSI) configuration.

# By default the fileless AMSI is enabled.
# To disable the ASMI plugin
#enabled=false

# Valid values are subject to change with future VMware Tools releases.
# Refer to the tools.conf.example for any changes.

# By default, the maximum client connections supported is 32.
# The values in range are 16<amsi-max-client-connections<64.
# Values lesser than the minimum will use the minimum (16) value.
# Values greater than the maximum will use the maximum (64) value.
#amsi-max-client-connections=24

# By default, the maximum script size supported is 131072 (128kb) bytes
# The range of values supported are 4096<amsi-max-script-size-in-bytes<524288
# Values lesser than the minimum will use the minimum (4096) value.
# Values greater than the maximum will use the maximum (524288) value.
#amsi-max-script-size-in-bytes=262144
 07070100000400000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002800000000open-vm-tools-12.5.2/open-vm-tools/udev   07070100000401000081A400000000000000000000000168225505000001CB000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/udev/99-vmware-scsi-udev.rules # Copyright (C) 2016,2019 VMware, Inc.  All rights reserved.
#
# This file is part of open-vm-tools

ACTION=="add", SUBSYSTEMS=="scsi", ATTRS{vendor}=="VMware*", ATTRS{model}=="Virtual disk*", ENV{DEVTYPE}=="disk", RUN+="/bin/sh -c 'echo 180 >/sys$env{DEVPATH}/device/timeout'"
ACTION=="add", SUBSYSTEMS=="scsi", ATTRS{vendor}=="VMware*", ATTRS{model}=="VMware Virtual S", ENV{DEVTYPE}=="disk", RUN+="/bin/sh -c 'echo 180 >/sys$env{DEVPATH}/device/timeout'"

 07070100000402000081A4000000000000000000000001682255050000040D000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/udev/Makefile.am   ################################################################################
### Copyright (C) 2016,2019 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

install-data-local:
	$(INSTALL) -d $(DESTDIR)$(UDEVRULESDIR)
	$(INSTALL) -m 644 $(srcdir)/99-vmware-scsi-udev.rules $(DESTDIR)$(UDEVRULESDIR)

   07070100000403000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth 07070100000404000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/vgauth/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000405000081A400000000000000000000000168225505000003CE000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vgauth/Makefile.am ################################################################################
### Copyright (c) 2014-2017,2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

SUBDIRS =
SUBDIRS += lib
SUBDIRS += cli
SUBDIRS += service
SUBDIRS += test
  07070100000406000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli 07070100000407000081A40000000000000000000000016822550500000846000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/Makefile.am ################################################################################
### Copyright (C) 2014-2016, 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-vgauth-cmd

vmware_vgauth_cmd_SOURCES =
vmware_vgauth_cmd_SOURCES += main.c

vmware_vgauth_cmd_CPPFLAGS =
vmware_vgauth_cmd_CPPFLAGS += -DVMTOOLS_USE_GLIB
vmware_vgauth_cmd_CPPFLAGS += @GLIB2_CPPFLAGS@
vmware_vgauth_cmd_CPPFLAGS += -I$(top_srcdir)/libvmtools
vmware_vgauth_cmd_CPPFLAGS += -I$(top_srcdir)/vgauth/public
vmware_vgauth_cmd_CPPFLAGS += -I$(top_srcdir)/vgauth/common

vmware_vgauth_cmd_LDADD =
vmware_vgauth_cmd_LDADD += @VMTOOLS_LIBS@
vmware_vgauth_cmd_LDADD += @GLIB2_LIBS@
vmware_vgauth_cmd_LDADD += @GTHREAD_LIBS@
vmware_vgauth_cmd_LDADD += @SSL_LIBS@
vmware_vgauth_cmd_LDADD += ../lib/libvgauth.la
vmware_vgauth_cmd_LDADD += -lssl
vmware_vgauth_cmd_LDADD += -lcrypto

if HAVE_ICU
   vmware_vgauth_cmd_LDADD += @ICU_LIBS@
   vmware_vgauth_cmd_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS)     \
                            $(LIBTOOLFLAGS) --mode=link $(CXX)       \
                            $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
                            $(LDFLAGS) -o $@
else
   vmware_vgauth_cmd_LINK = $(LINK)
endif

# Message catalogs.
# rename file name to "VGAuthCli" which used in main
install-data-hook:
	@INSTVMSG@ VGAuthCli $(srcdir)/l10n $(DESTDIR)$(datadir)  07070100000408000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n    07070100000409000081A40000000000000000000000016822550500000D6D000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/de.vmsg    ##########################################################
# Copyright (c) 2011-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s: Das Hinzufügen des Alias für den Benutzer '%2$s': %3$s ist fehlgeschlagen.\n"
addsubj.success = "%1$s: Alias hinzugefügt\n"

addoptions.comment = "Inhaberkommentar"

addoptions.file = "PEM-Dateiname"

addoptions.global = "Zertifikat der globalen Zuordnungsdatei hinzufügen"

addoptions.subject = "Der SAML-Inhaber"

addoptions.username = "Benutzer, dessen Zertifikatsspeicher hinzugefügt wird zu"

addoptions.verbose = "Detaillierter Vorgang"

cmdline.help.appoption = "Anwendungsoptionen"
cmdline.help.helpoption = "Hilfeoptionen"
cmdline.help.hint = "Hilfeoptionen anzeigen"
cmdline.help.usage = "Nutzung"
cmdline.parse = "Die Analyse der Befehlszeile ist fehlgeschlagen"

cmdline.summary.pemfile = "PEM-Datei"
cmdline.summary.subject = "Inhaber"
cmdline.summary.username = "Benutzername"
cmdline.summary.comm = "Kommentar"
cmdline.summary.note = "Hinweis: Wenn kein Benutzername angegeben wird, entfernt %1$s nur die zugeordneten Aliasnamen"

list.comment = "Kommentar"
list.count = "%1$s: Es wurden %2$d Aliase für den Benutzer '%3$s' gefunden.\n"

list.error = "%1$s: Die Auflistung der Aliase für den Benutzer '%2$s': %3$s ist fehlgeschlagen.\n"
list.subject = "Inhaber"

listmapped.count = "%1$s: Es wurden %2$d zugeordnete Aliase gefunden.\n"

listmapped.error = "%1$s: Die Auflistung der zugeordneten Aliase %2$s ist fehlgeschlagen.\n"

listmapped.subject = "Inhaber"

listmapped.username = "Benutzername"

listoptions.username = "Benutzer, dessen Zertifikatsspeicher angefordert wird"

listoptions.verbose = "Detaillierter Vorgang"

loadfile.fail = "%1$s: PEM-Datei '%2$s' kann nicht gelesen werden\n"

name.any = "<ALLE>"

removesubj.fail = "%1$s: Das Entfernen des Alias für den Benutzer '%2$s': %3$s ist fehlgeschlagen.\n"

removesubj.success = "%1$s: Alias entfernt\n"

removeoptions.file = "PEM-Dateiname"

removeoptions.subject = "Der SAML-Inhaber"
removeoptions.username = "Benutzer, dessen Zertifikatsspeicher entfernt wird von"

removeoptions.verbose = "Detaillierter Vorgang"

removeall.fail = "%1$s: Fehler beim Entfernen von Alias für Benutzer '%2$s' Inhaber '%3$s': %4$s.\n"

removeall.removefail = "%1$s: Fehler beim Entfernen von Alias für Benutzer '%2$s' Inhaber '%3$s' pemCert '%4$s': %5$s.\n"

removeall.success = "%1$s: alle Aliase entfernt\n"

removealloptions.subject = "Der SAML-Inhaber"
removealloptions.username = "Benutzer, dessen Zertifikatsspeicher entfernt wird von"

removealloptions.verbose = "Detaillierter Vorgang"

vgauth.init.failed = "Die Initialisierung von VGAuth ist fehlgeschlagen"
   0707010000040A000081A40000000000000000000000016822550500000C07000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/en.vmsg    ##########################################################
# Copyright (C) 2011-2017,2020 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s: Failed to add alias for user '%2$s': %3$s.\n"
addsubj.success = "%1$s: alias added\n"

addoptions.comment = "subject comment"

addoptions.file = "PEM file name"

addoptions.global = "Add the certificate to the global mapping file"

addoptions.subject = "The SAML subject"

addoptions.username = "User whose certificate store is being added to"

addoptions.verbose = "Verbose operation"

cmdline.help.appoption = "Application Options"
cmdline.help.helpoption = "Help Options"
cmdline.help.hint = "Show help options"
cmdline.help.usage = "Usage"
cmdline.parse = "Command line parsing failed"

cmdline.summary.pemfile = "PEM-file"
cmdline.summary.subject = "subject"
cmdline.summary.username = "username"
cmdline.summary.comm = "comment"
cmdline.summary.note = "Note: If no username is provided, %1$s removes only the mapped aliases"

list.comment = "Comment"
list.count = "%1$s Found %2$d aliases for user '%3$s'\n"

list.error = "%1$s: Failed to list aliases for user '%2$s': %3$s.\n"
list.subject = "Subject"

listmapped.count = "%1$s Found %2$d mapped aliases\n"

listmapped.error = "%1$s: Failed to list mapped aliases: %2$s.\n"

listmapped.subject = "Subject"

listmapped.username = "Username"

listoptions.username = "User whose certificate store is being queried"

listoptions.verbose = "Verbose operation"

loadfile.fail = "%1$s: Unable to read PEM file '%2$s'\n"

name.any = "<ANY>"

removesubj.fail = "%1$s: Failed to remove alias for user '%2$s': %3$s.\n"

removesubj.success = "%1$s: alias removed\n"

removeoptions.file = "PEM file name"

removeoptions.subject = "The SAML subject"
removeoptions.username = "User whose certificate store is being removed from"

removeoptions.verbose = "Verbose operation"

removeall.fail = "%1$s: Failed to remove alias for user '%2$s' subject '%3$s': %4$s.\n"

removeall.removefail = "%1$s: Failed to remove alias for user '%2$s' subject '%3$s' pemCert '%4$s': %5$s.\n"

removeall.success = "%1$s: all aliases removed\n"

removealloptions.subject = "The SAML subject"
removealloptions.username = "User whose certificate store is being removed from"

removealloptions.verbose = "Verbose operation"

vgauth.init.failed = "Failed to init VGAuth"
 0707010000040B000081A40000000000000000000000016822550500000D27000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/es.vmsg    ##########################################################
# Copyright (c) 2011-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s: No se pudo agregar el alias del usuario '%2$s': %3$s.\n"
addsubj.success = "%1$s: alias agregado\n"

addoptions.comment = "comentario del tema"

addoptions.file = "nombre de archivo PEM"

addoptions.global = "Añadir el certificado al archivo de asignación global"

addoptions.subject = "Tema de SAML"

addoptions.username = "El usuario cuyo almacén de certificados se está añadiendo a"

addoptions.verbose = "Operación detallada"

cmdline.help.appoption = "Opciones de la aplicación"
cmdline.help.helpoption = "Opciones de ayuda"
cmdline.help.hint = "Mostrar opciones de ayuda"
cmdline.help.usage = "Uso"
cmdline.parse = "Error de análisis de línea de comando"

cmdline.summary.pemfile = "Archivo PEM"
cmdline.summary.subject = "asunto"
cmdline.summary.username = "nombre de usuario"
cmdline.summary.comm = "comentario"
cmdline.summary.note = "Nota: Si no se proporciona un nombre de usuario, %1$s elimina solo los alias asignados"

list.comment = "Comentario"
list.count = "%1$s Se han encontrado %2$d alias para el usuario '%3$s'\n"

list.error = "%1$s: No se pudieron enumerar los alias del usuario '%2$s': %3$s.\n"
list.subject = "Asunto"

listmapped.count = "%1$s Se han encontrado %2$d alias asignados\n"

listmapped.error = "%1$s: No se pudieron enumerar los alias asignados: %2$s.\n"

listmapped.subject = "Asunto"

listmapped.username = "Nombre de usuario"

listoptions.username = "El usuario cuyo almacén de certificados se está consultando"

listoptions.verbose = "Operación detallada"

loadfile.fail = "%1$s: No se puede leer el archivo PEM '%2$s'\n"

name.any = "<CUALQUIERA>"

removesubj.fail = "%1$s: No se pudo quitar el alias del usuario '%2$s': %3$s.\n"

removesubj.success = "%1$s: alias quitado\n"

removeoptions.file = "nombre de archivo PEM"

removeoptions.subject = "Tema de SAML"
removeoptions.username = "El usuario cuyo almacén de certificados se va a eliminar de"

removeoptions.verbose = "Operación detallada"

removeall.fail = "%1$s: No se pudo eliminar el alias del usuario '%2$s', asunto '%3$s': %4$s.\n"

removeall.removefail = "%1$s: No se pudo eliminar el alias del usuario '%2$s', asunto '%3$s', pemCert '%4$s': %5$s.\n"

removeall.success = "%1$s: se eliminaron todos los alias\n"

removealloptions.subject = "Tema de SAML"
removealloptions.username = "El usuario cuyo almacén de certificados se va a eliminar de"

removealloptions.verbose = "Operación detallada"

vgauth.init.failed = "No se ha podido iniciar VGAuth"
 0707010000040C000081A40000000000000000000000016822550500000DD0000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/fr.vmsg    ##########################################################
# Copyright (c) 2011-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s : échec de l'ajout de l'alias pour l'utilisateur '%2$s' : %3$s.\n"
addsubj.success = "%1$s : alias ajouté\n"

addoptions.comment = "commentaire sur le sujet"

addoptions.file = "Nom de fichier PEM"

addoptions.global = "Ajouter le certificat au fichier de mappage global"

addoptions.subject = "Le sujet SAML"

addoptions.username = "Utilisateur dont le magasin de certificats est ajouté à"

addoptions.verbose = "Opération en mode détaillé"

cmdline.help.appoption = "Options d'application"
cmdline.help.helpoption = "Options d'aide"
cmdline.help.hint = "Afficher les options d'aide"
cmdline.help.usage = "Utilisation"
cmdline.parse = "Échec de l'analyse de la ligne de commande"

cmdline.summary.pemfile = "Fichier PEM"
cmdline.summary.subject = "sujet"
cmdline.summary.username = "nom d'utilisateur"
cmdline.summary.comm = "commentaire"
cmdline.summary.note = "Remarque : si aucun nom d'utilisateur n'est indiqué, %1$s supprime uniquement les alias mappés"

list.comment = "Commentaire"
list.count = "%1$s a trouvé %2$d alias pour l'utilisateur '%3$s'\n"

list.error = "%1$s : échec de la création de la liste d'alias pour l'utilisateur '%2$s' : %3$s.\n"
list.subject = "Sujet"

listmapped.count = "%1$s a trouvé %2$d alias mappés\n"

listmapped.error = "%1$s : échec de la création de la liste d'alias mappés : %2$s.\n"

listmapped.subject = "Sujet"

listmapped.username = "Nom d'utilisateur"

listoptions.username = "Utilisateur dont le magasin de certificats est demandé"

listoptions.verbose = "Opération en mode détaillé"

loadfile.fail = "%1$s : impossible de lire le fichier PEM '%2$s'\n"

name.any = "<QUELCONQUE>"

removesubj.fail = "%1$s : échec de la suppression de l'alias pour l'utilisateur '%2$s' : %3$s.\n"

removesubj.success = "%1$s : alias supprimé\n"

removeoptions.file = "Nom de fichier PEM"

removeoptions.subject = "Le sujet SAML"
removeoptions.username = "Utilisateur pour lequel le magasin de certificats est supprimé"

removeoptions.verbose = "Opération en mode détaillé"

removeall.fail = "%1$s : échec de la suppression de l'alias pour l'utilisateur « %2$s », sujet « %3$s » : %4$s.\n"

removeall.removefail = "%1$s : échec de la suppression de l'alias pour l'utilisateur « %2$s », sujet « %3$s », pemCert « %4$s » : %5$s.\n"

removeall.success = "%1$s : tous les alias supprimés\n"

removealloptions.subject = "Le sujet SAML"
removealloptions.username = "Utilisateur pour lequel le magasin de certificats est supprimé"

removealloptions.verbose = "Opération en mode détaillé"

vgauth.init.failed = "Échec de l'initialisation de VGAuth"
0707010000040D000081A40000000000000000000000016822550500000CCE000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/it.vmsg    ##########################################################
# Copyright (c) 2011-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s: Impossibile aggiungere alias per l'utente '%2$s': %3$s.\n"
addsubj.success = "%1$s: alias aggiunto\n"

addoptions.comment = "commento oggetti"

addoptions.file = "Nome file PEM"

addoptions.global = "Aggiungere il certificato al file di mappatura globale"

addoptions.subject = "L'oggetto SAML"

addoptions.username = "Utente a cui viene aggiunto l'archivio certificati"

addoptions.verbose = "Operazione Verbose"

cmdline.help.appoption = "Opzioni applicazione"
cmdline.help.helpoption = "Opzioni Guida"
cmdline.help.hint = "Visualizza le opzioni della Guida"
cmdline.help.usage = "Utilizzo"
cmdline.parse = "Analisi della riga di comando non riuscita"

cmdline.summary.pemfile = "File PEM"
cmdline.summary.subject = "oggetto"
cmdline.summary.username = "nome utente"
cmdline.summary.comm = "commento"
cmdline.summary.note = "Nota: se non viene specificato alcun nome utente, %1$s rimuove solo gli alias mappati"

list.comment = "Commento"
list.count = "%1$s Trovati %2$d alias per l'utente '%3$s'\n"

list.error = "%1$s: Impossibile elencare gli alias per l'utente '%2$s': %3$s.\n"
list.subject = "Oggetto"

listmapped.count = "%1$s Trovati %2$d alias mappati\n"

listmapped.error = "%1$s: Impossibile elencare gli alias mappati:%2$s.\n"

listmapped.subject = "Oggetto"

listmapped.username = "Nome utente"

listoptions.username = "Utente il cui archivio certificati è oggetto di query"

listoptions.verbose = "Operazione Verbose"

loadfile.fail = "%1$s: Impossibile leggere il file PEM '%2$s'\n"

name.any = "<QUALSIASI>"

removesubj.fail = "%1$s: Impossibile rimuovere l'alias per l'utente  '%2$s': %3$s.\n"

removesubj.success = "%1$s: alias rimosso\n"

removeoptions.file = "Nome file PEM"

removeoptions.subject = "L'oggetto SAML"
removeoptions.username = "Utente da cui viene rimosso l'archivio certificati"

removeoptions.verbose = "Operazione Verbose"

removeall.fail = "%1$s: impossibile rimuovere l'alias per l'utente '%2$s' oggetto '%3$s': %4$s.\n"

removeall.removefail = "%1$s: impossibile rimuovere l'alias per l'utente '%2$s' oggetto '%3$s' pemCert '%4$s': %5$s.\n"

removeall.success = "%1$s: tutti gli alias sono stati rimossi\n"

removealloptions.subject = "L'oggetto SAML"
removealloptions.username = "Utente da cui viene rimosso l'archivio certificati"

removealloptions.verbose = "Operazione Verbose"

vgauth.init.failed = "Impossibile inizializzare VGAuth"
  0707010000040E000081A40000000000000000000000016822550500000F2B000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/ja.vmsg    ##########################################################
# Copyright (c) 2011-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s: ユーザー '%2$s' のエイリアスを追加できませんでした: %3$s。\n"
addsubj.success = "%1$s: エイリアスが追加されました\n"

addoptions.comment = "サブジェクトのコメント"

addoptions.file = "PEM ファイル名"

addoptions.global = "グローバル マッピング ファイルに証明書を追加"

addoptions.subject = "SAML サブジェクト"

addoptions.username = "証明書ストアが追加されているユーザー"

addoptions.verbose = "詳細な操作"

cmdline.help.appoption = "アプリケーション オプション"
cmdline.help.helpoption = "ヘルプ オプション"
cmdline.help.hint = "ヘルプ オプションを表示"
cmdline.help.usage = "使用方法"
cmdline.parse = "コマンド ラインの解析に失敗しました"

cmdline.summary.pemfile = "PEM ファイル"
cmdline.summary.subject = "サブジェクト"
cmdline.summary.username = "ユーザー名"
cmdline.summary.comm = "コメント"
cmdline.summary.note = "注: ユーザー名が指定されていない場合、%1$s はマッピングされたエイリアスのみを削除します"

list.comment = "コメント"
list.count = "%1$s でユーザー '%3$s' のエイリアスが %2$d 個検出されました\n"

list.error = "%1$s: ユーザー '%2$s' のエイリアスを一覧表示できませんでした: %3$s。\n"
list.subject = "サブジェクト"

listmapped.count = "%1$s でマップされた %2$d 個のエイリアスが検出されました\n"

listmapped.error = "%1$s: マップされたエイリアスを一覧表示できませんでした: %2$s。\n"

listmapped.subject = "サブジェクト"

listmapped.username = "ユーザー名"

listoptions.username = "証明書ストアが照会されているユーザー"

listoptions.verbose = "詳細な操作"

loadfile.fail = "%1$s: PEM ファイル '%2$s' を読み取ることができません\n"

name.any = "<ANY>"

removesubj.fail = "%1$s: ユーザー '%2$s' のエイリアスを削除できませんでした: %3$s。\n"

removesubj.success = "%1$s: エイリアスが削除されました\n"

removeoptions.file = "PEM ファイル名"

removeoptions.subject = "SAML サブジェクト"
removeoptions.username = "次の場所から証明書ストアが削除されているユーザー"

removeoptions.verbose = "詳細な操作"

removeall.fail = "%1$s: ユーザー '%2$s' のサブジェクト '%3$s' を削除できませんでした: %4$s。\n"

removeall.removefail = "%1$s: ユーザー '%2$s' のサブジェクト '%3$s' の pemCert '%4$s' のエイリアスを削除できませんでした: %5$s。\n"

removeall.success = "%1$s: すべてのエイリアスが削除されました\n"

removealloptions.subject = "SAML サブジェクト"
removealloptions.username = "次の場所から証明書ストアが削除されているユーザー"

removealloptions.verbose = "詳細な操作"

vgauth.init.failed = "VGAuth を初期化できませんでした"
 0707010000040F000081A40000000000000000000000016822550500000D6A000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/ko.vmsg    ##########################################################
# Copyright (c) 2011-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s: 사용자 '%2$s'에 대한 별칭을 추가하지 못했습니다. %3$s.\n"
addsubj.success = "%1$s: 별칭이 추가됨\n"

addoptions.comment = "제목 설명"

addoptions.file = "PEM 파일 이름"

addoptions.global = "글로벌 매핑 파일에 인증서 추가"

addoptions.subject = "SAML 주체"

addoptions.username = "인증서 저장소를 추가 중인 사용자"

addoptions.verbose = "자세한 정보 표시 작업"

cmdline.help.appoption = "애플리케이션 옵션"
cmdline.help.helpoption = "도움말 옵션"
cmdline.help.hint = "도움말 옵션 표시"
cmdline.help.usage = "사용"
cmdline.parse = "명령줄 구문 분석 실패"

cmdline.summary.pemfile = "PEM 파일"
cmdline.summary.subject = "제목"
cmdline.summary.username = "사용자 이름"
cmdline.summary.comm = "설명"
cmdline.summary.note = "참고: 사용자 이름이 제공되지 않으면 %1$s이(가) 매핑된 별칭만 제거합니다."

list.comment = "설명"
list.count = "%1$s에서 사용자 '%3$s'의 %2$d개 별칭이 확인되었습니다.\n"

list.error = "%1$s: 사용자 '%2$s'의 별칭을 나열하지 못했습니다. %3$s.\n"
list.subject = "제목"

listmapped.count = "%1$s에서 %2$d개의 매핑된 별칭을 찾았습니다.\n"

listmapped.error = "%1$s: 매핑된 별칭을 나열하지 못했습니다. %2$s.\n"

listmapped.subject = "제목"

listmapped.username = "사용자 이름"

listoptions.username = "인증서 저장소를 쿼리 중인 사용자"

listoptions.verbose = "자세한 정보 표시 작업"

loadfile.fail = "%1$s: PEM 파일 '%2$s'을(를) 읽을 수 없습니다.\n"

name.any = "<모두>"

removesubj.fail = "%1$s: 사용자 '%2$s'에 대한 별칭을 제거하지 못했습니다. %3$s.\n"

removesubj.success = "%1$s: 별칭이 제거됨\n"

removeoptions.file = "PEM 파일 이름"

removeoptions.subject = "SAML 주체"
removeoptions.username = "인증서 저장소를 제거 중인 사용자"

removeoptions.verbose = "자세한 정보 표시 작업"

removeall.fail = "%1$s: 사용자 '%2$s' 주체 '%3$s'에 대한 별칭을 제거하지 못했습니다. %4$s.\n"

removeall.removefail = "%1$s: 사용자 '%2$s' 주체 '%3$s' pemCert '%4$s'에 대한 별칭을 제거하지 못했습니다. %5$s.\n"

removeall.success = "%1$s: 모든 별칭이 제거됨\n"

removealloptions.subject = "SAML 주체"
removealloptions.username = "인증서 저장소를 제거 중인 사용자"

removealloptions.verbose = "자세한 정보 표시 작업"

vgauth.init.failed = "VGAuth 초기화 실패"
  07070100000410000081A40000000000000000000000016822550500000BFE000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/zh_CN.vmsg ##########################################################
# Copyright (c) 2011-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s: 无法为用户“%2$s”添加别名: %3$s。\n"
addsubj.success = "%1$s: 已添加别名\n"

addoptions.comment = "主题注释"

addoptions.file = "PEM 文件名"

addoptions.global = "将证书添加到全局映射文件中"

addoptions.subject = "SAML 主题"

addoptions.username = "要在以下位置添加证书存储的用户"

addoptions.verbose = "详细操作"

cmdline.help.appoption = "应用程序选项"
cmdline.help.helpoption = "帮助选项"
cmdline.help.hint = "显示帮助选项"
cmdline.help.usage = "用法"
cmdline.parse = "命令行分析失败"

cmdline.summary.pemfile = "PEM 文件"
cmdline.summary.subject = "主题"
cmdline.summary.username = "用户名"
cmdline.summary.comm = "注释"
cmdline.summary.note = "注意: 如果未提供用户名，%1$s 将仅移除映射的别名"

list.comment = "注释"
list.count = "%1$s 已找到用户“%3$s”的 %2$d 别名\n"

list.error = "%1$s: 无法列出用户“%2$s”的别名: %3$s。\n"
list.subject = "主题"

listmapped.count = "%1$s 已找到 %2$d 映射的别名\n"

listmapped.error = "%1$s: 无法列出映射的别名: %2$s。\n"

listmapped.subject = "主题"

listmapped.username = "用户名"

listoptions.username = "要对其证书存储进行查询的用户"

listoptions.verbose = "详细操作"

loadfile.fail = "%1$s: 无法读取 PEM 文件“%2$s”\n"

name.any = "<任何>"

removesubj.fail = "%1$s: 无法删除用户“%2$s”的别名: %3$s。\n"

removesubj.success = "%1$s: 已删除别名\n"

removeoptions.file = "PEM 文件名"

removeoptions.subject = "SAML 主题"
removeoptions.username = "要从以下位置移除其证书存储的用户"

removeoptions.verbose = "详细操作"

removeall.fail = "%1$s: 无法移除主题“%3$s”中用户“%2$s”的别名: %4$s。\n"

removeall.removefail = "%1$s: 无法移除主题“%3$s”和 pemCert“%4$s”中用户“%2$s”的别名: %5$s。\n"

removeall.success = "%1$s: 已移除所有别名\n"

removealloptions.subject = "SAML 主题"
removealloptions.username = "要从以下位置移除其证书存储的用户"

removealloptions.verbose = "详细操作"

vgauth.init.failed = "无法初始化 VGAuth"
  07070100000411000081A40000000000000000000000016822550500000C55000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/l10n/zh_TW.vmsg ##########################################################
# Copyright (c) 2011-2017,2020-2022 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

addsubj.fail = "%1$s: 無法為使用者「%2$s」新增別名: %3$s。\n"
addsubj.success = "%1$s: 已新增別名\n"

addoptions.comment = "主旨註解"

addoptions.file = "PEM 檔案名稱"

addoptions.global = "將憑證新增至全域對應檔案"

addoptions.subject = "SAML 主旨"

addoptions.username = "正將其憑證存放區新增至以下項目的使用者"

addoptions.verbose = "詳細資訊作業"

cmdline.help.appoption = "應用程式選項"
cmdline.help.helpoption = "説明選項"
cmdline.help.hint = "顯示說明選項"
cmdline.help.usage = "用法"
cmdline.parse = "命令行剖析失敗"

cmdline.summary.pemfile = "PEM 檔案"
cmdline.summary.subject = "主旨"
cmdline.summary.username = "使用者名稱"
cmdline.summary.comm = "註解"
cmdline.summary.note = "備註: 如果未提供使用者名稱，則 %1$s 僅會移除對應的別名"

list.comment = "註解"
list.count = "%1$s 找到使用者「%3$s」的 %2$d 個別名\n"

list.error = "%1$s: 無法列出使用者「%2$s」的別名: %3$s。\n"
list.subject = "主旨"

listmapped.count = "%1$s 找到 %2$d 個已對應的別名\n"

listmapped.error = "%1$s: 無法列出已對應的別名: %2$s。\n"

listmapped.subject = "主旨"

listmapped.username = "使用者名稱"

listoptions.username = "正在查詢其憑證存放區的使用者"

listoptions.verbose = "詳細資訊作業"

loadfile.fail = "%1$s: 無法讀取 PEM 檔案「%2$s」\n"

name.any = "<任何>"

removesubj.fail = "%1$s: 無法移除使用者「%2$s」的別名: %3$s。\n"

removesubj.success = "%1$s: 已移除別名\n"

removeoptions.file = "PEM 檔案名稱"

removeoptions.subject = "SAML 主旨"
removeoptions.username = "正從以下項目移除其憑證存放區的使用者"

removeoptions.verbose = "詳細資訊作業"

removeall.fail = "%1$s: 無法移除使用者「%2$s」主題「%3$s」的別名: %4$s。\n"

removeall.removefail = "%1$s: 無法移除使用者「%2$s」主題「%3$s」pemCert「%4$s」的別名: %5$s。\n"

removeall.success = "%1$s: 已移除所有別名\n"

removealloptions.subject = "SAML 主旨"
removealloptions.username = "正從以下項目移除其憑證存放區的使用者"

removealloptions.verbose = "詳細資訊作業"

vgauth.init.failed = "無法初始化 VGAuth"
   07070100000412000081A40000000000000000000000016822550500007785000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/vgauth/cli/main.c  /*********************************************************
 * Copyright (c) 2011-2020, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file main.c
 *
 *    The GuestAuth certificate manipulation command line tool.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#ifndef _WIN32
#include <unistd.h>
#include <errno.h>
#endif
#include <glib.h>

#include "VGAuthBasicDefs.h"
#include "VGAuthAlias.h"
#include "VGAuthCommon.h"
#include "VGAuthError.h"
#include "VGAuthLog.h"
#include "VGAuthUtil.h"
#define VMW_TEXT_DOMAIN "VGAuthCli"
#include "i18n.h"
#include "prefs.h"
#ifdef _WIN32
#   include "winUtil.h"
#endif

static gchar *appName;

static gboolean verbose = FALSE;


#ifdef _WIN32


/*
 ******************************************************************************
 * GetHelp --                                                            */ /**
 *
 * Get help message for CLI on windows
 *
 * @param[in] context      The GOptionContext for generating the message
 * @param[in] paramStr     The command line operation arguments
 * @param[in] cmdOptions   The command line options for given operation
 *
 ******************************************************************************
 */

static gchar *
GetHelp(GOptionContext *context,
        const char *paramStr,
        const GOptionEntry *cmdOptions)
{
   gchar *helpMsg;
   const gchar *lUsage = SU_(cmdline.help.usage, "Usage");
   const gchar *lHelpOption = SU_(cmdline.help.helpoption, "Help Options");
   const gchar *lAppOption = SU_(cmdline.help.appoption, "Application Options");
   const gchar *lHint = SU_(cmdline.help.hint, "Show help options");

   if (cmdOptions == NULL){
      helpMsg = g_strdup_printf("%s:\n"
                                "  %s %s\n\n"
                                "%s\n\n"
                                "%s:\n"
                                "  -h, --help       %s\n",
                                lUsage,
                                appName, paramStr,
                                g_option_context_get_summary(context),
                                lHelpOption,
                                lHint);
   } else {
      gchar *optionMsg;
      GString *optStr;
      int i;
      optStr = g_string_new(NULL);
      for (i = 0; cmdOptions[i].long_name != NULL; i++) {
         g_string_append_printf(optStr,
                                "  -%c, --%-8s    %s\n",
                                cmdOptions[i].short_name,
                                cmdOptions[i].long_name,
                                cmdOptions[i].description);
      }
      optionMsg = g_string_free(optStr, FALSE);

      helpMsg = g_strdup_printf("%s:\n"
                                "  %s %s\n\n"
                                "%s\n\n"
                                "%s:\n"
                                "  -h, --help       %s\n"
                                "%s:\n"
                                "%s",
                                lUsage,
                                appName, paramStr,
                                g_option_context_get_summary(context),
                                lHelpOption,
                                lHint,
                                lAppOption,
                                optionMsg);
      g_free(optionMsg);
   }

   return helpMsg;
}

#endif



/*
 ******************************************************************************
 * Usage --                                                              */ /**
 *
 * Usage message for CLI
 *
 * @param[in] optContext   The GOptionContext for generating the message
 * @param[in] paramStr     The command line operation arguments
 * @param[in] cmdOptions   The command line options for given operation
 *
 ******************************************************************************
 */

static void
Usage(GOptionContext *optContext,
      const char *paramStr,
      const GOptionEntry *cmdOptions)
{
   gchar *usage;
#ifdef _WIN32
   usage = GetHelp(optContext, paramStr, cmdOptions);
#else
   usage = g_option_context_get_help(optContext, TRUE, NULL);
#endif

   g_printerr("%s", usage);
   g_free(usage);
   exit(-1);
}


/*
 ******************************************************************************
 * CliLog --                                                             */ /**
 *
 * Error message logging function for the CLI.
 *
 * @param[in]     logDomain   The glib logging domain, which is set by the
 *                            various glib components and vgauth itself.
 * @param[in]     logLevel    The severity of the message.
 * @param[in]     msg         The error message.
 * @param[in]     userData    Any userData specified in the call to
 *                            VGAuth_SetLogHandler()
 *
 ******************************************************************************
 */

static void
CliLog(const char *logDomain,
       int logLevel,
       const char *msg,
       void *userData)
{
   // ignore all but errors
   if (logLevel & G_LOG_LEVEL_WARNING) {
      g_printerr("%s[%d]: %s", logDomain, logLevel, msg);
#ifdef VMX86_DEBUG
   } else {
      fprintf(stderr, "Dropping message %s[%d]: %s", logDomain, logLevel, msg);
#endif
   }
}


/*
 ******************************************************************************
 * SubjectName --                                                        */ /**
 *
 * Returns the name value for a subject, or <ANY>.
 *
 * @param[in] s      The VGAuthSubject.
 *
 ******************************************************************************
 */

static const gchar *
SubjectName(VGAuthSubject *s)
{
   if (s->type == VGAUTH_SUBJECT_NAMED) {
      return s->val.name;
   } else {
      return SU_(name.any, "<ANY>");
   }
}


/*
 ******************************************************************************
 * CliLoadPemFile --                                                     */ /**
 *
 * Loads a PEM certificate from a file.  The caller should g_free() the
 * return value when finished.
 *
 * @param[in]  fileName          The filename of the cert in PEM format.
 *
 * @return The contents of the certificate file.
 *
 ******************************************************************************
 */

static gchar *
CliLoadPemFILE(const gchar *fileName)
{
   gchar *contents = NULL;
   gsize fileSize;
   GError *gErr = NULL;

   /*
    * XXX
    *
    * Might be nice for this to handle stdin.  Either a NULL
    * filename or "-" ?
    */
   if (!g_file_get_contents(fileName, &contents, &fileSize, &gErr)) {
      g_printerr(SU_(loadfile.fail,
                     "%s: Unable to read PEM file '%s'\n"),
                 appName, gErr->message);
      g_error_free(gErr);
   }

   return contents;
}


/*
 ******************************************************************************
 * CliAddAlias --                                                        */ /**
 *
 * Adds a certficate and subject for the user.
 *
 * @param[in]  ctx                  The VGAuthContext.
 * @param[in]  userName             The user whose store is being changed.
 * @param[in]  subject              The associated subject name.
 * @param[in]  pemFileName          The filename of the cert in PEM format.
 * @param[in]  addMapped            Set if a link is also to be added to
 *                                  the mapping file.
 * @param[in]  comment              The comment.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
CliAddAlias(VGAuthContext *ctx,
            const char *userName,
            const char *subject,
            const char *pemFilename,
            gboolean addMapped,
            const char *comment)
{
   gchar *pemCert;
   VGAuthError err;
   VGAuthAliasInfo ai;

   pemCert = CliLoadPemFILE(pemFilename);
   if (NULL == pemCert) {
      return VGAUTH_E_INVALID_CERTIFICATE;
   }

   /*
    * The 'comment' cmdline arg is optional, but the underlying API needs
    * a real value.
    */
   ai.comment = (comment) ? (char *) comment : "";

   ai.subject.type = VGAUTH_SUBJECT_NAMED;
   ai.subject.val.name = (char *) subject;

   err = VGAuth_AddAlias(ctx, userName, addMapped, pemCert, &ai, 0, NULL);
   if (VGAUTH_E_OK != err) {
      g_printerr(SU_(addsubj.fail,
                    "%s: Failed to add alias for user '%s': %s.\n"),
                 appName, userName, VGAuth_GetErrorText(err, NULL));
   } else if (verbose) {
      g_print(SU_(addsubj.success, "%s: alias added\n"), appName);
   }

   g_free(pemCert);

   return err;
}


/*
 ******************************************************************************
 * CliRemoveAlias --                                                     */ /**
 *
 * Removes a certficate for the user.
 *
 * @param[in]  ctx                  The VGAuthContext.
 * @param[in]  userName             The user whose store is being changed.
 * @param[in]  subject              The associated subject.
 * @param[in]  pemFileName          The filename of the cert in PEM format.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
CliRemoveAlias(VGAuthContext *ctx,
                 const char *userName,
                 const char *subject,
                 const char *pemFilename)
{
   VGAuthError err;
   gchar *pemCert;
   VGAuthSubject subj;

   pemCert = CliLoadPemFILE(pemFilename);
   if (NULL == pemCert) {
      return VGAUTH_E_INVALID_CERTIFICATE;
   }

   if (subject) {
      subj.val.name = (char *) subject;
      subj.type = VGAUTH_SUBJECT_NAMED;
      err = VGAuth_RemoveAlias(ctx, userName, pemCert, &subj, 0, NULL);
   } else {
      err = VGAuth_RemoveAliasByCert(ctx, userName, pemCert, 0, NULL);
   }

   if (VGAUTH_E_OK != err) {
      g_printerr(SU_(removesubj.fail,
                     "%s: Failed to remove alias for user '%s': %s.\n"),
                 appName, userName, VGAuth_GetErrorText(err, NULL));
   } else if (verbose) {
      g_print(SU_(removesubj.success, "%s: alias removed\n"), appName);
   }

   g_free(pemCert);

   return err;
}


/*
 ******************************************************************************
 * CliRemoveAllAlias --                                                  */ /**
 *
 * Removes aliases for given subject and [userName].
 * If userName is not provided, only remove mapped aliases.
 *
 * @param[in]  ctx                  The VGAuthContext.
 * @param[in]  subject              The associated subject.
 * @param[in]  userName             The user whose store is being changed.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
CliRemoveAllAlias(VGAuthContext *ctx,
                  const char *subject,
                  const char *userName)
{
   VGAuthError err;
   gboolean fail = FALSE;
   int num = 0;
   int i;
   int j;

   if (NULL != userName) {
      VGAuthUserAlias *uaList = NULL;

      /* get aliases by userName */
      err = VGAuth_QueryUserAliases(ctx, userName, 0, NULL, &num, &uaList);
      if (VGAUTH_E_OK != err) {
         g_printerr(SU_(list.error,
                        "%s: Failed to list aliases for user '%s': %s.\n"),
                    appName, userName, VGAuth_GetErrorText(err, NULL));
         return err;
      }

      /* find matched aliases, remove */
      for(i = 0; i < num; i++) {
         for(j = 0; j < uaList[i].numInfos; j++) {
            if (!g_strcmp0(subject, uaList[i].infos[j].subject.val.name)) {
               err = VGAuth_RemoveAlias(ctx,
                                        userName,
                                        uaList[i].pemCert,
                                        &(uaList[i].infos[j].subject),
                                        0,
                                        NULL);
               if (VGAUTH_E_OK != err) {
                  g_printerr(SU_(removeall.removefail,
                                 "%s: Failed to remove alias for user '%s'"
                                 " subject '%s' pemCert '%s': %s.\n"),
                             appName,
                             userName,
                             subject,
                             uaList[i].pemCert,
                             VGAuth_GetErrorText(err, NULL));
                  fail = TRUE;
               }

               break;
            }
         }

         if (fail){
            break;
         }
      }

      VGAuth_FreeUserAliasList(num, uaList);
   } else {
      VGAuthMappedAlias *maList = NULL;

      /* no userName provided, so only can get mapped aliases */
      err = VGAuth_QueryMappedAliases(ctx, 0, NULL, &num, &maList);
      if (VGAUTH_E_OK != err) {
         g_printerr(SU_(listmapped.error,
                        "%s: Failed to list mapped aliases: %s.\n"),
                     appName, VGAuth_GetErrorText(err, NULL));
         return err;
      }

      /* find matched aliases, remove */
      for(i = 0; i < num; i++) {
         for(j = 0; j < maList[i].numSubjects; j++) {
            if (!g_strcmp0(subject, maList[i].subjects[j].val.name)){
               err = VGAuth_RemoveAlias(ctx,
                                        maList[i].userName,
                                        maList[i].pemCert,
                                        &(maList[i].subjects[j]),
                                        0,
                                        NULL);
               if (VGAUTH_E_OK != err) {
                  g_printerr(SU_(removeall.removefail,
                                 "%s: Failed to remove alias for user '%s'"
                                 " subject '%s' pemCert '%s': %s.\n"),
                             appName,
                             maList[i].userName,
                             subject,
                             maList[i].pemCert,
                             VGAuth_GetErrorText(err, NULL));
                  fail = TRUE;
               }

               break;
            }
         }

         if (fail){
            break;
         }
      }

      VGAuth_FreeMappedAliasList(num, maList);
   }

   if (VGAUTH_E_OK == err && verbose) {
      g_print(SU_(removeall.success, "%s: all aliases removed\n"), appName);
   }

   return err;
}


/*
 ******************************************************************************
 * CliList --                                                            */ /**
 *
 * List all UserAliases for a user.
 *
 * @param[in]  ctx                  The VGAuthContext.
 * @param[in]  userName             The user whose store is being queried.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
CliList(VGAuthContext *ctx,
        const char *userName)
{
   VGAuthError err;
   int num;
   int i;
   int j;
   VGAuthUserAlias *uaList;

   err = VGAuth_QueryUserAliases(ctx, userName, 0, NULL, &num, &uaList);
   if (VGAUTH_E_OK != err) {
      g_printerr(SU_(list.error,
                     "%s: Failed to list aliases for user '%s': %s.\n"),
                 appName, userName, VGAuth_GetErrorText(err, NULL));
      return err;
   }

   if (verbose) {
      g_print(SU_(list.count, "%s Found %d aliases for user '%s'\n"),
              appName, num, userName);
   }

   for (i = 0; i < num; i++) {
      g_print("%s\n", uaList[i].pemCert);
      for (j = 0; j < uaList[i].numInfos; j++) {
         g_print("\t%s: %s %s: %s\n",
                 SU_(list.subject, "Subject"),
                 SubjectName(&(uaList[i].infos[j].subject)),
                 SU_(list.comment, "Comment"),
                 uaList[i].infos[j].comment);
      }
   }
   VGAuth_FreeUserAliasList(num, uaList);

   return err;
}


/*
 ******************************************************************************
 * CliListMapped --                                                      */ /**
 *
 * List all IdProviders in the mapping file.
 *
 * @param[in]  ctx                  The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
CliListMapped(VGAuthContext *ctx)
{
   VGAuthError err;
   int num;
   VGAuthMappedAlias *maList;
   int i;
   int j;

   err = VGAuth_QueryMappedAliases(ctx, 0, NULL, &num, &maList);
   if (VGAUTH_E_OK != err) {
      g_printerr(SU_(listmapped.error,
                     "%s: Failed to list mapped aliases: %s.\n"),
                 appName, VGAuth_GetErrorText(err, NULL));
      return err;
   }

   if (verbose) {
      g_print(SU_(listmapped.count, "%s Found %d mapped aliases\n"),
              appName, num);
   }
   for (i = 0; i < num; i++) {
      g_print("%s\n%s:%s\n", maList[i].pemCert,
              SU_(listmapped.username, "Username"),
              maList[i].userName);
      for (j = 0; j < maList[i].numSubjects; j++) {
         g_print("\t%s: %s\n",
                 SU_(listmapped.subject, "Subject"),
                 SubjectName(&(maList[i].subjects[j])));

      }
   }
   VGAuth_FreeMappedAliasList(num, maList);

   return err;
}


/*
 ******************************************************************************
 * InitMsgCatalog --                                                     */ /**
 *
 * Initialize language setting according to machine locale
 *
 ******************************************************************************
 */

static void
InitMsgCatalog(void)
{
   PrefHandle prefs;
   gchar *msgCatalog;

   /*
    * Do this first, so any noise from the locale setup is properly filtered.
    */
   VGAuth_SetLogHandler(CliLog, NULL, 0, NULL);

   /*
    * Find the location of the i18n catalogs.
    */
   setlocale(LC_ALL, "");
   prefs = Pref_Init(VGAUTH_PREF_CONFIG_FILENAME);
   msgCatalog = Pref_GetString(prefs,
                               VGAUTH_PREF_LOCALIZATION_DIR,
                               VGAUTH_PREF_GROUP_NAME_LOCALIZATION,
                               VGAUTH_PREF_DEFAULT_LOCALIZATION_CATALOG);

   I18n_BindTextDomain(VMW_TEXT_DOMAIN,    // domain -- base name of vmsg files
                       NULL,               // locale -- let it figure it out
                       msgCatalog);        // path to message catalogs
   g_free(msgCatalog);
   Pref_Shutdown(prefs);
}


/*
 ******************************************************************************
 * mainRun --                                                            */ /**
 *
 * Initializes and parses commandline args.
 *
 * @param[in]  argc        Number of command line arguments.
 * @param[in]  argv        The command line arguments.
 *
 * @return 0 if the operation ran successfully, -1 if there was an error during
 *         execution.
 *
 ******************************************************************************
 */

#ifdef _WIN32
#define use_glib_parser 0
#else
#define use_glib_parser 1
#endif

static int
mainRun(int argc,
        char *argv[])
{
   VGAuthError err;
   VGAuthContext *ctx = NULL;
   gboolean doAdd = FALSE;
   gboolean doRemove = FALSE;
   gboolean doList = FALSE;
   gboolean doRemoveAll = FALSE;
   gboolean addMapped = FALSE;
   gchar **argvCopy = NULL;
   int argcCopy;
   char *userName = NULL;
   char *pemFilename = NULL;
   gchar *comment = NULL;
   gchar *summaryMsg;
   gchar *noteMsg = NULL;
   gchar *subject = NULL;
   GOptionEntry *cmdOptions = NULL;
   const gchar *paramStr = "[add | list | remove | removeAll]\n";
   const gchar *lUsername = SU_(cmdline.summary.username, "username");
   const gchar *lSubject = SU_(cmdline.summary.subject, "subject");
   const gchar *lPEMfile = SU_(cmdline.summary.pemfile, "PEM-file");
   const gchar *lComm = SU_(cmdline.summary.comm, "comment");
   const gchar *lNote = SU_(cmdline.summary.note,
                            "Note: If no username is provided, "
                            "%s removes only the mapped aliases");

#if (use_glib_parser == 0)
   int i;
#else
   GError *gErr = NULL;
#endif

   GOptionEntry listOptions[] = {
      { "username", 'u', 0, G_OPTION_ARG_STRING, &userName,
         SU_(listoptions.username,
             "User whose certificate store is being queried"), NULL },
      { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
         SU_(listoptions.verbose,
             "Verbose operation"), NULL },
      { NULL }
   };
   GOptionEntry removeOptions[] = {
      { "username", 'u', 0, G_OPTION_ARG_STRING, &userName,
         SU_(removeoptions.username,
             "User whose certificate store is being removed from"), NULL },
      { "file", 'f', 0, G_OPTION_ARG_STRING, &pemFilename,
         SU_(removeoptions.file, "PEM file name"), NULL },
      { "subject", 's', 0, G_OPTION_ARG_STRING, &subject,
         SU_(removeoptions.subject, "The SAML subject"), NULL },
      { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
         SU_(removeoptions.verbose, "Verbose operation"), NULL },
      { NULL }
   };
   GOptionEntry addOptions[] = {
      { "username", 'u', 0, G_OPTION_ARG_STRING, &userName,
         SU_(addoptions.username,
             "User whose certificate store is being added to"), NULL },
      { "file", 'f', 0, G_OPTION_ARG_STRING, &pemFilename,
         SU_(addoptions.file, "PEM file name"), NULL },
      { "subject", 's', 0, G_OPTION_ARG_STRING, &subject,
         SU_(addoptions.subject, "The SAML subject"), NULL },
      { "global", 'g', 0, G_OPTION_ARG_NONE, &addMapped,
         SU_(addoptions.global,
             "Add the certificate to the global mapping file"), NULL },
      { "comment", 'c', 0, G_OPTION_ARG_STRING, &comment,
         SU_(addoptions.comment, "subject comment"), NULL},
      { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
         SU_(addoptions.verbose, "Verbose operation"), NULL },
      { NULL }
   };
   GOptionEntry removeAllOptions[] = {
      { "username", 'u', 0, G_OPTION_ARG_STRING, &userName,
         SU_(removealloptions.username,
             "User whose certificate store is being removed from"), NULL },
      { "subject", 's', 0, G_OPTION_ARG_STRING, &subject,
         SU_(removealloptions.subject, "The SAML subject"), NULL },
      { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
         SU_(removealloptions.verbose, "Verbose operation"), NULL },
      { NULL }
   };
   GOptionContext *context;

   appName = g_path_get_basename(argv[0]);

   /*
    * The option parser needs to modify these, and using the variables
    * coming into main doesn't work.
    */
   argcCopy = argc;
   argvCopy = argv;

   /*
    * Set up the option parser
    */
   g_set_prgname(appName);
   noteMsg = g_strdup_printf(lNote, "removeAll");
   context = g_option_context_new(paramStr);
   summaryMsg = g_strdup_printf(
      "add --global --username=%s --file=%s --subject=%s "
             "[ --comment=%s ]\n"
      "remove --username=%s --file=%s [ --subject=%s ]\n"
      "removeAll --subject=%s [ --username=%s ]\n"
      "list [ --username=%s ]\n\n"
      "%s",
      lUsername, lPEMfile, lSubject, lComm,
      lUsername, lPEMfile, lSubject,
      lSubject, lUsername,
      lUsername,
      noteMsg);

   g_option_context_set_summary(context, summaryMsg);
   g_free(summaryMsg);
   g_free(noteMsg);
   if (argc < 2) {
      Usage(context, paramStr, cmdOptions);
   }

   /*
    * Determine the command and set up the appropriate option table.
    */
   if (strcmp(argvCopy[1], "add") == 0) {
      doAdd = TRUE;
      g_option_context_add_main_entries(context, addOptions, NULL);
      cmdOptions = addOptions;
   } else if (strcmp(argvCopy[1], "remove") == 0) {
      doRemove = TRUE;
      g_option_context_add_main_entries(context, removeOptions, NULL);
      cmdOptions = removeOptions;
   } else if (strcmp(argvCopy[1], "list") == 0) {
      doList = TRUE;
      g_option_context_add_main_entries(context, listOptions, NULL);
      cmdOptions = listOptions;
   } else if (strcmp(argvCopy[1], "removeAll") == 0) {
      doRemoveAll = TRUE;
      g_option_context_add_main_entries(context, removeAllOptions, NULL);
      cmdOptions = removeAllOptions;
   } else {
      Usage(context, paramStr, cmdOptions);
   }

#if (use_glib_parser == 0)
   /*
    * In Windows, g_option_context_parse() does the wrong thing for the
    * incoming Unicode cmdline.  Modern glib (2.40 or later) solves this
    * with g_option_context_parse_strv(), but we're stuck with an older
    * glib for now.
    *
    * So instead lets do it all by hand.
    *
    * This does almost everything for the two types of options this code
    * cares about.  It doesn't handle multiple
    * short options behind a single dash, or '--' alone meaning
    * to stop parsing.
    *
    * XXX fix this when we upgrade glib.
    */
   for (i = 2; i < argc; i++) {
      GOptionEntry *e;
      gboolean match = FALSE;

      e = &cmdOptions[0];
      while (e && e->long_name) {
         gsize len;
         char longName[32];
         char shortName[3];

         g_snprintf(longName, sizeof(longName), "--%s", e->long_name);
         g_snprintf(shortName, sizeof(shortName), "-%c", e->short_name);
         len = strlen(longName);

         // short options don't support '='
         if (strcmp(shortName, argv[i]) == 0) {
            if (e->arg == G_OPTION_ARG_STRING) {
               if (argv[i+1]) {
                  *(gchar **) e->arg_data = argv[++i];
                  match = TRUE;
               } else {
                  Usage(context, paramStr, cmdOptions);
               }
            } else if (e->arg == G_OPTION_ARG_NONE) {
               *(gboolean *) e->arg_data = TRUE;
               match = TRUE;
            }
         } else if (strncmp(longName, argv[i], len) == 0 &&
                   (argv[i][len] == '=' || argv[i][len] == '\0')) {
            gchar *val = NULL;

            if (e->arg == G_OPTION_ARG_STRING) {
               if (argv[i][len] == '=') {
                  val = (gchar *) &(argv[i][len+1]);
               } else if (argv[i+1]) {
                  val = argv[++i];
               }
               *(gchar **) e->arg_data = val;
               match = TRUE;
            } else if ((e->arg == G_OPTION_ARG_NONE) && argv[i][len] == '\0') {
               *(gboolean *) e->arg_data = TRUE;
               match = TRUE;
            } else {
               Usage(context, paramStr, cmdOptions);
            }
         }
         if (match) {
            goto next;
         }
         e++;
      }
next:
      if (!match) {
         Usage(context, paramStr, cmdOptions);
      }
   }
#else
   if (!g_option_context_parse(context, &argcCopy, &argvCopy, &gErr)) {
      g_printerr("%s: %s: %s\n", appName,
                 SU_(cmdline.parse, "Command line parsing failed"),
                 gErr->message);
      g_error_free(gErr);
      exit(-1);
   }
#endif

   /*
    * XXX pull this if we use stdin for the cert contents.
    */
   if (((doAdd || doRemove) && !pemFilename) ||(doRemoveAll && !subject)) {
      Usage(context, paramStr, cmdOptions);
   }

   err = VGAuth_Init(appName, 0, NULL, &ctx);
   if (VGAUTH_E_OK != err) {
      g_printerr("%s\n", SU_(vgauth.init.failed, "Failed to init VGAuth"));
      exit(-1);
   }

   /*
    * XXX
    * If username is unset, should it use the current user?
    * This breaks the model where no username means listMapped.
    * Can we do it just for add/remove, or is that too confusing?
    * Add an explicit listmapped instead?
    */

   if (doAdd) {
      err = CliAddAlias(ctx, userName, subject, pemFilename, addMapped, comment);
   } else if (doRemove) {
      err= CliRemoveAlias(ctx, userName, subject, pemFilename);
   } else if (doList) {
      if (userName) {
         err = CliList(ctx, userName);
      } else {
         err = CliListMapped(ctx);
      }
   } else if (doRemoveAll) {
      err = CliRemoveAllAlias(ctx, subject, userName);
   }

   VGAuth_Shutdown(ctx);
   g_free(appName);
   return (err == VGAUTH_E_OK) ? 0 : -1;
}


#ifdef _WIN32


/*
 ******************************************************************************
 * wmain --                                                              */ /**
 *
 * Initializes and parses commandline args.
 *
 * @param[in]  argc        Number of command line arguments.
 * @param[in]  argv        The command line arguments in unicode.
 *
 * @return 0 if the operation ran successfully, -1 if there was an error during
 *         execution.
 *
 ******************************************************************************
 */

int
wmain(int argc,
      wchar_t *argv[])
{
   int retval = -1;
   int i;
   char **argvUtf8 = g_malloc0((argc + 1) * sizeof (char*));

#ifdef _WIN32
   WinUtil_EnableSafePathSearching();
#if defined(VMX86_RELEASE)
   WinUtil_VerifyExePathW();
#endif
#endif

   for (i = 0; i < argc; ++i) {
      CHK_UTF16_TO_UTF8(argvUtf8[i], argv[i], goto end);
   }

   InitMsgCatalog();

   retval = mainRun(argc, argvUtf8);

end:

   for (i = 0; i < argc; ++i) {
      g_free(argvUtf8[i]);
   }

   g_free(argvUtf8);

   return retval;
}

#else


/*
 ******************************************************************************
 * main --                                                               */ /**
 *
 * Initializes and parses commandline args.
 *
 * @param[in]  argc        Number of command line arguments.
 * @param[in]  argv        The command line arguments.
 *
 * @return 0 if the operation ran successfully, -1 if there was an error during
 *         execution.
 *
 ******************************************************************************
 */

int
main(int argc,
     char *argv[])
{
   InitMsgCatalog();
   return mainRun(argc, argv);
}

#endif
   07070100000413000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common  07070100000414000081A40000000000000000000000016822550500000CAC000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/VGAuthBasicDefs.h    /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VGAUTH_BASIC_DEFS_H_
#define _VGAUTH_BASIC_DEFS_H_
/*
 * @file VGAuthBasicDefs.h
 *
 *       Shared types and macros for the VGAuth project.
 */

#include "VGAuthError.h"
#include "VGAuthUtil.h"

#ifndef _WIN32
#include <stdint.h>
#endif

#ifdef _WIN32
#define WIN32_ONLY(x) x
#define POSIX_ONLY(x)
#else
#define WIN32_ONLY(x)
#define POSIX_ONLY(x) x
#endif

#if defined(__GNUC__)
# define PRINTF_DECL(fmtPos, varPos)                                  \
   __attribute__((__format__(__printf__, fmtPos, varPos)))
#else
# define PRINTF_DECL(fmtPos, varPos)
#endif

#ifdef __cplusplus
#define ASSERT(cond) ((!(cond)) ? Util_Assert(#cond, __FILE__, __LINE__) : (void) 0)
#else
#define ASSERT(cond) ((!(cond)) ? Util_Assert(#cond, __FILE__, __LINE__) : 0)
#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

#ifndef UNUSED_TYPE
// XXX _Pragma would better but doesn't always work right now.
#  define UNUSED_TYPE(_parm) UNUSED_PARAM(_parm)
#endif

#define ASSERT_ON_COMPILE(e) \
   do { \
      enum { AssertOnCompileMisused = ((e) ? 1: -1) }; \
      UNUSED_TYPE(typedef char AssertOnCompileFailed[AssertOnCompileMisused]); \
   } while (0)

#ifndef _WIN32
/*
 * Some common platform interface may require a HANDLE parameter for Windows
 * Define it for other platforms as well to get a better type safety and
 * cleaner code.
 */

typedef void* HANDLE;
#endif

/*
 * Printf format specifiers for size_t and 64-bit number.
 * Use them like this:
 *    printf("%"FMT64"d\n", big);
 *
 * This transforms the bora/lib version into the glib names so
 * we can easily port code.
 */
#define FMT64 G_GINT64_MODIFIER
#define FMTSZ G_GSIZE_MODFIER

#define VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, syserr) \
   do { \
      ((VGAuthErrorFields *) &err)->error = VGAUTH_E_SYSTEM_ERRNO; \
      ((VGAuthErrorFields *) &err)->extraError = syserr; \
   } while (0)

#define VGAUTH_ERROR_SET_SYSTEM_WINDOWS(err, syserr) \
   do { \
      ((VGAuthErrorFields *) &err)->error = VGAUTH_E_SYSTEM_WINDOWS; \
      ((VGAuthErrorFields *) &err)->extraError = syserr; \
   } while (0)


#endif // _VGAUTH_BASIC_DEFS_H_
07070100000415000081A400000000000000000000000168225505000029F0000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/VGAuthLog.c  /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file VGAuthLog.c --
 *
 *       Log functions.
 */

#ifdef _WIN32
#include <windows.h>
#else
/*
 * Need GNU definition of strerror_r for better compatibility
 * across different glibc versions.
 */
#define _GNU_SOURCE
#include <errno.h>
#include <unistd.h>
#include <string.h>
#endif
#include <stdlib.h>

#include <glib.h>
#include <glib/gstdio.h>

#include "VGAuthBasicDefs.h"
#include "VGAuthLog.h"
#ifdef _WIN32
#include "winUtil.h"
#endif


#ifdef _WIN32

/*
 ******************************************************************************
 * LogErrorWinCodeV--                                                    */ /**
 *
 * Log an error message
 *
 * @param[in]   code       The Windows system error code
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 * @param[in]   args       The printf like var args
 *
 ******************************************************************************
 */

static void
LogErrorWinCodeV(int code,
                 const char *func,
                 const char *file,
                 unsigned line,
                 const char *fmt,
                 va_list args)
{
   char *msgBuf = WinUtil_GetErrorText(code);
   gchar buf[4096];

   g_vsnprintf(buf, sizeof buf, fmt, args);
   buf[sizeof buf - 1] = '\0';

   g_warning("[function %s, file %s, line %d], %s, [Win32 Error = %d] %s\n",
             func, file, line, buf, code, (const char *)msgBuf);

   g_free(msgBuf);
}


/*
 ******************************************************************************
 * LogErrorWinCode --                                                    */ /**
 *
 * Log an error message
 *
 * @param[in]   code       The Windows system error code
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 *
 ******************************************************************************
 */

void LogErrorWinCode(int code,
                     const char *func,
                     const char *file,
                     unsigned line,
                     const char *fmt,
                     ...)
{
   va_list args;

   va_start(args, fmt);
   LogErrorWinCodeV(code, func, file, line, fmt, args);
   va_end(args);
}


/*
 ******************************************************************************
 * LogErrorWin --                                                        */ /**
 *
 * Log an error message after a Windows system API call
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 *
 ******************************************************************************
 */

void LogErrorWin(const char *func,
                 const char *file,
                 unsigned line,
                 const char *fmt,
                 ...)
{
   int code = GetLastError();
   va_list args;

   va_start(args, fmt);
   LogErrorWinCodeV(code, func, file, line, fmt, args);
   va_end(args);
}


/*
 ******************************************************************************
 * LogErrorPosixCodeV--                                                  */ /**
 *
 * Log an error message
 *
 * @param[in]   code       The Posix error code (errno)
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 * @param[in]   args       The printf like var args
 *
 ******************************************************************************
 */

static void
LogErrorPosixCodeV(int code,
                   const char *func,
                   const char *file,
                   unsigned line,
                   const char *fmt,
                   va_list args)
{
   char errMsg[4096];
   gchar buf[4096];

   g_vsnprintf(buf, sizeof buf, fmt, args);
   buf[sizeof buf - 1] = '\0';
   strerror_s(errMsg, sizeof errMsg, code);
   g_warning("[function %s, file %s, line %d], %s, [errno = %d] %s\n",
             func, file, line, buf, code, errMsg);
}

#else

/*
 ******************************************************************************
 * LogErrorPosixCodeV--                                                  */ /**
 *
 * Log an error message
 *
 * @param[in]   code       The Posix error code (errno)
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 * @param[in]   args       The printf like var args
 *
 ******************************************************************************
 */

static void
LogErrorPosixCodeV(int code,
                   const char *func,
                   const char *file,
                   unsigned line,
                   const char *fmt,
                   va_list args)
{
   char errMsg[4096];
   gchar buf[4096];

   g_vsnprintf(buf, sizeof buf, fmt, args);
   buf[sizeof buf - 1] = '\0';

#ifdef sun
   strerror_r(code, errMsg, sizeof errMsg);
   g_warning("[function %s, file %s, line %d], %s, [errno = %d], %s\n",
             func, file, line, buf, code, errMsg);
#else
   g_warning("[function %s, file %s, line %d], %s, [errno = %d], %s\n",
             func, file, line, buf, code,
             strerror_r(code, errMsg, sizeof errMsg));
#endif
}

#endif


/*
 ******************************************************************************
 * LogErrorPosixCode --                                                  */ /**
 *
 * Log an error message
 *
 * @param[in]   code       The Posix error code (errno)
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 *
 ******************************************************************************
 */

void LogErrorPosixCode(int code,
                       const char *func,
                       const char *file,
                       unsigned line,
                       const char *fmt,
                       ...)
{
   va_list args;

   va_start(args, fmt);
   LogErrorPosixCodeV(code, func, file, line, fmt, args);
   va_end(args);
}


/*
 ******************************************************************************
 * LogErrorPosix --                                                      */ /**
 *
 * Log an error message after a Posix API call
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 *
 ******************************************************************************
 */

void LogErrorPosix(const char *func,
                   const char *file,
                   unsigned line,
                   const char *fmt,
                   ...)
{
   int code = errno;
   va_list args;

   va_start(args, fmt);
   LogErrorPosixCodeV(code, func, file, line, fmt, args);
   va_end(args);
}


/*
 ******************************************************************************
 * LogInfo --                                                           */ /**
 *
 * Log an information message
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 *
 ******************************************************************************
 */

void LogInfo(const char *func,
             const char *file,
             unsigned line,
             const char *fmt,
             ...)
{
   gchar buf[4096];

   va_list args;
   va_start(args, fmt);
   g_vsnprintf(buf, sizeof buf, fmt, args);
   buf[sizeof buf - 1] = '\0';
   va_end(args);

   g_message("[function %s, file %s, line %d], %s\n",
             func, file, line, buf);
}


/*
 ******************************************************************************
 * LogWarning --                                                         */ /**
 *
 * Log an error message
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 *
 ******************************************************************************
 */

void LogWarning(const char *func,
                const char *file,
                unsigned line,
                const char *fmt,
                ...)
{
   gchar buf[4096];

   va_list args;
   va_start(args, fmt);
   g_vsnprintf(buf, sizeof buf, fmt, args);
   buf[sizeof buf - 1] = '\0';
   va_end(args);

   g_warning("[function %s, file %s, line %d], %s\n",
             func, file, line, buf);
}


/*
 ******************************************************************************
 * LogDebug --                                                           */ /**
 *
 * Log a debug message
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   fmt        The printf like format string
 *
 ******************************************************************************
 */

void LogDebug(const char *func,
              const char *file,
              unsigned line,
              const char *fmt,
              ...)
{
   gchar buf[4096];

   va_list args;
   va_start(args, fmt);
   g_vsnprintf(buf, sizeof buf, fmt, args);
   buf[sizeof buf - 1] = '\0';
   va_end(args);

   g_debug("[function %s, file %s, line %d], %s\n",
           func, file, line, buf);
}
07070100000416000081A40000000000000000000000016822550500000E32000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/VGAuthLog.h  /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file VGAuthLog.h --
 *
 *    Convenient log macros and declarations
 */

#ifndef _VGAUTH_LOG_H_
#define _VGAUTH_LOG_H_

#include "VGAuthBasicDefs.h"

/* Windows functions and macros*/
#ifdef _WIN32

void LogErrorWin(const char *func, const char *file,
                 unsigned line, const char *fmt, ...) PRINTF_DECL(4, 5);
#define VGAUTH_LOG_ERR_WIN(fmt, ...) do {                                      \
      LogErrorWin(__FUNCTION__, __FILE__, __LINE__, fmt, __VA_ARGS__);  \
   } while (0)

void LogErrorWinCode(int code, const char *func, const char *file,
                     unsigned line, const char *fmt, ...) PRINTF_DECL(5, 6);

#define VGAUTH_LOG_ERR_WIN_CODE(code, fmt, ...) do {                  \
      LogErrorWinCode(code, __FUNCTION__, __FILE__, __LINE__,  \
                      fmt, __VA_ARGS__);                       \
   } while (0)

#endif /* _WIN32 */

/* Common functions and macros */

/*
 * These differ from Warning(), Log() and Debug() by adding the
 * __FUNCTION__, __FILE__ and __LINE__ for you.
 */

void LogErrorPosix(const char *func, const char *file,
                   unsigned line, const char *fmt, ...) PRINTF_DECL(4, 5);
#define VGAUTH_LOG_ERR_POSIX(fmt, ...) do {                                    \
      LogErrorPosix(__FUNCTION__, __FILE__, __LINE__, fmt, __VA_ARGS__); \
   } while (0)

void LogErrorPosixCode(int code, const char *func, const char *file,
                       unsigned line, const char *fmt, ...) PRINTF_DECL(5, 6);

#define VGAUTH_LOG_ERR_POSIX_CODE(code, fmt, ...) do {                 \
      LogErrorPosixCode(code, __FUNCTION__, __FILE__, __LINE__, \
                        fmt, __VA_ARGS__);                      \
   } while (0)


/*
 * Logs a message at Log()/g_message() level.
 */
void LogInfo(const char *func, const char *file,
             unsigned line, const char *fmt, ...) PRINTF_DECL(4, 5);
#define VGAUTH_LOG_INFO(fmt, ...) do {                                         \
      LogInfo(__FUNCTION__, __FILE__, __LINE__, fmt, __VA_ARGS__);      \
   } while (0)


/*
 * Logs an error at Warning/g_warning() level.
 */
void LogWarning(const char *func, const char *file,
                unsigned line, const char *fmt, ...) PRINTF_DECL(4, 5);
#define VGAUTH_LOG_WARNING(fmt, ...) do {                                      \
      LogWarning(__FUNCTION__, __FILE__, __LINE__, fmt, __VA_ARGS__);   \
   } while (0)


/*
 * Logs a message at Debug/g_debug() level.
 */
void LogDebug(const char *func, const char *file,
              unsigned line, const char *fmt, ...) PRINTF_DECL(4, 5);
#define VGAUTH_LOG_DEBUG(fmt, ...) do {                                      \
      LogDebug(__FUNCTION__, __FILE__, __LINE__, fmt, __VA_ARGS__);   \
   } while (0)

#endif
  07070100000417000081A40000000000000000000000016822550500005043000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/VGAuthProto.h    /*********************************************************
 * Copyright (c) 2011-2016,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VGAUTHPROTO_H_
#define _VGAUTHPROTO_H_

#include "VGAuthError.h"

/* clang-format off */

/*
 * @file VGAuthProto.h
 *
 * Defines the wire protocol between the VGAuth client library
 * and the keystore/ticket service.
 */

/*
 * All requests generate replies.
 *
 * Per-operation comments below document the sucess case; the failure
 * case would have the same request data, but the same fixed error
 * format is returned for any operation.  Other common data (sequence number,
 * the request name) is also implied.
 *
 * If a request causes an error, the error code and an error message
 * are returned.
 *
 * If a request is successful, any response is returned.
 *
 * All requests contain a sequnce number, generated by the client.
 * The response or error returned from the service will contain this
 * sequence number.
 *
 * Note that 'superuser' below means root/system to distinguish
 * from a root certificate.
 *
 */


/*
 * The name of the public pipe used to start the initial handshake.
 */
#ifdef _WIN32
#define  SERVICE_PUBLIC_PIPE_NAME   "\\\\.\\pipe\\vgauth-service"
#else
#define  SERVICE_PUBLIC_PIPE_NAME   "/var/run/vmware/guestServicePipe"
#endif

#ifdef _WIN32
#define  SUPERUSER_NAME "system"
#else
#define  SUPERUSER_NAME "root"
#endif


/*
 * XXX For readbility/commentary purposes, all element names are
 * on the long/descriptive side.  If space becomes an issue, they
 * can be squashed.
 *
 * Format strings are #define here to avoid a bunch of duplicate
 * 'static char *' if this header is included in many places.
 * Of course, that means if they get used in multiple spots, we have
 * waste again.  TBD which makes the most sense.  Another benefit of
 * #defines is that they can easily be combined.
 *
 */

#define VGAUTH_XML_PREAMBLE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"

/*
 * Common tags.
 */
#define VGAUTH_PROTOCOL_VERSION "1"

#define VGAUTH_REQUESTNAME_ELEMENT_NAME "requestName"
#define VGAUTH_SEQUENCENO_ELEMENT_NAME "sequenceNumber"
#define VGAUTH_USERNAME_ELEMENT_NAME "userName"
#define VGAUTH_ERRORCODE_ELEMENT_NAME "errorCode"
#define VGAUTH_ERRORMSG_ELEMENT_NAME "errorMsg"
#define VGAUTH_TICKET_ELEMENT_NAME "ticket"
#define VGAUTH_PID_ELEMENT_NAME "pid"
#define VGAUTH_TOKEN_ELEMENT_NAME "token"
#define VGAUTH_SIGALGORITHM_ELEMENT_NAME "sigAlg"
#define VGAUTH_DATA_ELEMENT_NAME "data"
#define VGAUTH_SAMLTOKEN_ELEMENT_NAME "samlToken"
#define VGAUTH_CHALLENGE_EVENT_ELEMENT_NAME "challengeEvent"
#define VGAUTH_ALIASINFO_ELEMENT_NAME "aliasInfo"
#define VGAUTH_MAPPEDALIASES_ELEMENT_NAME "mappedAliases"
#define VGAUTH_SUBJECTS_ELEMENT_NAME "subjects"
#define VGAUTH_SUBJECT_ELEMENT_NAME "subject"
#define VGAUTH_ANYSUBJECT_ELEMENT_NAME "anySubject"
#define VGAUTH_COMMENT_ELEMENT_NAME "comment"
#define VGAUTH_ALIAS_ELEMENT_NAME "alias"
#define VGAUTH_VALIDATE_ONLY_ELEMENT_NAME "validateOnly"
#define VGAUTH_HOST_VERIFIED_ELEMENT_NAME "hostVerified"

/*
 * Complex types
 */


#define  VGAUTH_PEMCERT_ELEMENT_NAME "pemCert"

#define VGAUTH_USERNAME_FORMAT \
       "<"VGAUTH_USERNAME_ELEMENT_NAME">%s</"VGAUTH_USERNAME_ELEMENT_NAME">"

#define VGAUTH_PEMCERT_FORMAT \
       "<"VGAUTH_PEMCERT_ELEMENT_NAME">%s</"VGAUTH_PEMCERT_ELEMENT_NAME">"

#define VGAUTH_SUBJECT_FORMAT \
   "<"VGAUTH_SUBJECT_ELEMENT_NAME">%s</"VGAUTH_SUBJECT_ELEMENT_NAME">"

#define VGAUTH_ANYSUBJECT_FORMAT \
   "<"VGAUTH_ANYSUBJECT_ELEMENT_NAME"/>"

#define VGAUTH_COMMENT_FORMAT \
   "<"VGAUTH_COMMENT_ELEMENT_NAME">%s</"VGAUTH_COMMENT_ELEMENT_NAME">"

/*
 * A NamedAliasInfo contains a subject and a comment.
 */
#define VGAUTH_NAMEDALIASINFO_FORMAT \
   "<"VGAUTH_ALIASINFO_ELEMENT_NAME">" \
      VGAUTH_SUBJECT_FORMAT \
      VGAUTH_COMMENT_FORMAT \
   "</"VGAUTH_ALIASINFO_ELEMENT_NAME">"

/*
 * An AnyAliasInfo contains an anySubject tag and a comment.
 */
#define VGAUTH_ANYALIASINFO_FORMAT \
   "<"VGAUTH_ALIASINFO_ELEMENT_NAME">" \
      VGAUTH_ANYSUBJECT_FORMAT \
      VGAUTH_COMMENT_FORMAT \
   "</"VGAUTH_ALIASINFO_ELEMENT_NAME">"

/*
 * An Alias contains a pemCert and 1 or more of a mix of
 * AnyAliasInfo and NamedAliasInfo
 */
#define VGAUTH_ALIAS_FORMAT_START \
   "<"VGAUTH_ALIAS_ELEMENT_NAME">" \
   VGAUTH_PEMCERT_FORMAT

#define VGAUTH_ALIAS_FORMAT_END \
   "</"VGAUTH_ALIAS_ELEMENT_NAME">" \

/*
 * A MappedAlias contains a userName, pemCert and an array of a
 * NamedSubject or AnySubject
 */
#define VGAUTH_MAPPEDALIASES_FORMAT_START \
   "<"VGAUTH_MAPPEDALIASES_ELEMENT_NAME">" \
   VGAUTH_USERNAME_FORMAT \
   VGAUTH_PEMCERT_FORMAT \
   "<"VGAUTH_SUBJECTS_ELEMENT_NAME">"

#define VGAUTH_MAPPEDALIASES_FORMAT_END \
   "</"VGAUTH_SUBJECTS_ELEMENT_NAME">" \
   "</"VGAUTH_MAPPEDALIASES_ELEMENT_NAME">" \

/*
 * A UserHandleInfo contains a type, and optional data determined by that type.
 */
#define VGAUTH_USERHANDLEINFO_ELEMENT_NAME "userHandleInfo"
#define VGAUTH_USERHANDLETYPE_ELEMENT_NAME "userHandleType"
#define VGAUTH_USERHANDLESAMLINFO_ELEMENT_NAME "samlInfo"
#define VGAUTH_USERHANDLESAMLSUBJECT_ELEMENT_NAME "samlSubject"

/*
 * Possible userHandle types
 */
#define VGAUTH_USERHANDLE_TYPE_NAMEPASSWORD "namePassword"
#define VGAUTH_USERHANDLE_TYPE_SSPI         "sspi"
#define VGAUTH_USERHANDLE_TYPE_SAML         "saml"
#define VGAUTH_USERHANDLE_TYPE_SAML_INFO_ONLY    "samlInfoOnly"

#define VGAUTH_USERHANDLESAMLINFO_FORMAT_START \
   "<"VGAUTH_USERHANDLESAMLINFO_ELEMENT_NAME">"\
   "<"VGAUTH_USERHANDLESAMLSUBJECT_ELEMENT_NAME">" \
      "%s" \
   "</"VGAUTH_USERHANDLESAMLSUBJECT_ELEMENT_NAME">"
/*
 * Followed by one of VGAUTH_NAMEDALIASINFO_FORMAT or
 * VGAUTH_ANYALIASINFO_FORMAT.
 */
#define VGAUTH_USERHANDLESAMLINFO_FORMAT_END \
   "</"VGAUTH_USERHANDLESAMLINFO_ELEMENT_NAME">"\


#define VGAUTH_USERHANDLEINFO_FORMAT_START \
   "<"VGAUTH_USERHANDLEINFO_ELEMENT_NAME">"\
   "<"VGAUTH_USERHANDLETYPE_ELEMENT_NAME">" \
   "%s" \
   "</"VGAUTH_USERHANDLETYPE_ELEMENT_NAME">"
/*
 * Followed by VGAUTH_USERHANDLESAMLINFO_FORMAT_{START,END} if the type is
 * VGAUTH_USERHANDLE_TYPE_SAML.
 */

#define VGAUTH_USERHANDLEINFO_FORMAT_END \
   "</"VGAUTH_USERHANDLEINFO_ELEMENT_NAME">"\

/** Requests */

#define VGAUTH_REQUEST_ELEMENT_NAME "request"
#define VGAUTH_REPLY_ELEMENT_NAME "reply"

/*
 * Request envelope
 */
#define VGAUTH_REQUEST_FORMAT_START \
   VGAUTH_XML_PREAMBLE "<"VGAUTH_REQUEST_ELEMENT_NAME">" \
   "<"VGAUTH_SEQUENCENO_ELEMENT_NAME">%d</"VGAUTH_SEQUENCENO_ELEMENT_NAME">"

#define VGAUTH_REQUEST_FORMAT_END "</"VGAUTH_REQUEST_ELEMENT_NAME">"

/*
 * Reply envelope
 */
#define VGAUTH_REPLY_FORMAT_START \
   VGAUTH_XML_PREAMBLE "<"VGAUTH_REPLY_ELEMENT_NAME">" \
   "<"VGAUTH_SEQUENCENO_ELEMENT_NAME">%d</"VGAUTH_SEQUENCENO_ELEMENT_NAME">"

#define VGAUTH_REPLY_FORMAT_END "</"VGAUTH_REPLY_ELEMENT_NAME">"


/*
 * Error response.
 */
#define VGAUTH_ERROR_FORMAT \
   VGAUTH_XML_PREAMBLE \
   "<"VGAUTH_REPLY_ELEMENT_NAME">" \
       "<"VGAUTH_SEQUENCENO_ELEMENT_NAME">%d</"VGAUTH_SEQUENCENO_ELEMENT_NAME">" \
       "<"VGAUTH_ERRORCODE_ELEMENT_NAME">"VGAUTHERR_FMT64"</"VGAUTH_ERRORCODE_ELEMENT_NAME">" \
       "<"VGAUTH_ERRORMSG_ELEMENT_NAME">%s</"VGAUTH_ERRORMSG_ELEMENT_NAME">" \
   "</"VGAUTH_REPLY_ELEMENT_NAME">"


/*
 * SessionRequest should be sent as the first request upon
 * connection.  It exchanges the version number of the client and service,
 * and request that the service set up a private and secure connection
 * for the supplied user.
 *
 * Permissions: any user
 *
 * Request:
 *    version #
 *    username
 * =>
 *    version #
 *    pipe name
 *
 */
#define VGAUTH_REQUESTSESSION_ELEMENT_NAME   "SessionRequest"
#define VGAUTH_VERSION_ELEMENT_NAME "version"

#define VGAUTH_SESSION_REQUEST_FORMAT \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTSESSION_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
       "<"VGAUTH_VERSION_ELEMENT_NAME">"VGAUTH_PROTOCOL_VERSION"</"VGAUTH_VERSION_ELEMENT_NAME">" \
       "<"VGAUTH_USERNAME_ELEMENT_NAME">%s</"VGAUTH_USERNAME_ELEMENT_NAME">" \
   VGAUTH_REQUEST_FORMAT_END

#define  VGAUTH_PIPENAME_ELEMENT_NAME "pipeName"

#define VGAUTH_SESSION_REPLY_FORMAT \
   VGAUTH_REPLY_FORMAT_START \
       "<"VGAUTH_VERSION_ELEMENT_NAME">"VGAUTH_PROTOCOL_VERSION"</"VGAUTH_VERSION_ELEMENT_NAME">" \
       "<"VGAUTH_PIPENAME_ELEMENT_NAME">%s</"VGAUTH_PIPENAME_ELEMENT_NAME">" \
    VGAUTH_REPLY_FORMAT_END


/*
 * Connection
 *
 * Simple handshake for the secure channel that deals with a specified
 * user.  The first request sent after a connection to the namedPipe
 * coming back from SessionRequest.
 *
 * Permissions: expected to be done by the user owning the pipe
 *
 * Request:
 *   pid - The client process ID on Windows. On other platforms, it can be
 *         an empty string.
 *   On Windows vista and later version Windows, the server can call
 *   GetNamedPipeClientProcessId() to retrieve the client PID.
 *   However, the function is not available on an earlier Windows system
 *   such as XP.
 *   Therefore, the client sends the pid in this request, and the service shall
 *   verify the it by impersonating the named pipe client and opening the
 *   process identified by the pid.
 * =>
 *   (empty)
 */
#define VGAUTH_REQUESTCONNECT_ELEMENT_NAME   "Connect"

#define VGAUTH_CONNECT_REQUEST_FORMAT \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTCONNECT_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
       "<"VGAUTH_PID_ELEMENT_NAME">%s</"VGAUTH_PID_ELEMENT_NAME">" \
    VGAUTH_REQUEST_FORMAT_END

#define VGAUTH_CONNECT_REPLY_FORMAT \
    VGAUTH_REPLY_FORMAT_START \
   "<"VGAUTH_CHALLENGE_EVENT_ELEMENT_NAME">%s</"VGAUTH_CHALLENGE_EVENT_ELEMENT_NAME">" \
    VGAUTH_REPLY_FORMAT_END


/*
 * AddAlias
 *
 * Adds a certificate and subject to the specified user's alias store.
 *
 * This will fail with a permission error if the request doesn't
 * come over a namedPipe owned by superuser or 'user'.
 *
 * Request:
 *   userName
 *   addMappedLink (boolean)
 *   pemCert
 *   either a NamedAliasInfo or an AnyAliasInfo
 * =>
 *   (empty)
 */

#define VGAUTH_REQUESTADDALIAS_ELEMENT_NAME  "AddAlias"

#define VGAUTH_ADDMAPPEDLINK_ELEMENT_NAME "addMappedLink"

#define VGAUTH_ADDALIAS_REQUEST_FORMAT_START \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTADDALIAS_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
       "<"VGAUTH_USERNAME_ELEMENT_NAME">%s</"VGAUTH_USERNAME_ELEMENT_NAME">" \
       "<"VGAUTH_ADDMAPPEDLINK_ELEMENT_NAME">%d</"VGAUTH_ADDMAPPEDLINK_ELEMENT_NAME">" \
       VGAUTH_PEMCERT_FORMAT

/*
 * Followed by either VGAUTH_NAMEDALIASINFO_FORMAT or VGAUTH_ANYALIASINFO_FORMAT
 */

#define VGAUTH_ADDALIAS_REQUEST_FORMAT_END \
   VGAUTH_REQUEST_FORMAT_END


#define VGAUTH_ADDALIAS_REPLY_FORMAT \
   VGAUTH_REPLY_FORMAT_START \
   VGAUTH_REPLY_FORMAT_END


/*
 * RemoveAlias
 *
 * Remove a Subject from the pemCert in the specified user's alias store.
 * Any mapped link is also removed.  If no Subject is specified,
 * entries for all Subjects associated with the cert will be removed.
 *
 * This will fail with a permission error if the request doesn't
 * come over a namedPipe owned by superuser or the owner of the ticket.
 *
 * Request:
 *   userName
 *   pemCertificate
 *   an optional AnySubject or NamedSubject.  If neither is specified, all
 *       Subjects and the pemCert are removed.
 * =>
 *   (empty)
 */

#define VGAUTH_REQUESTREMOVEALIAS_ELEMENT_NAME  "RemoveAlias"

#define VGAUTH_REMOVEALIAS_REQUEST_FORMAT_START \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTREMOVEALIAS_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
       VGAUTH_USERNAME_FORMAT \
       VGAUTH_PEMCERT_FORMAT
/*
 * followed by either
 * VGAUTH_NAMEDSUBJECT_FORMAT or VGAUTH_ANYSUBJECT_FORMAT or nothing.
 */

#define VGAUTH_REMOVEALIAS_REQUEST_FORMAT_END \
   VGAUTH_REQUEST_FORMAT_END


#define VGAUTH_REMOVEALIAS_REPLY_FORMAT \
   VGAUTH_REPLY_FORMAT_START \
   VGAUTH_REPLY_FORMAT_END


/*
 * QueryUserAliases
 *
 * Queries the subjects and certificates from the specified user's alias store.
 *
 * This will fail with a permission error if the request doesn't
 * come over a namedPipe owned by superuser or 'user'.
 *
 * Request:
 *   userName
 * =>
 *   array of Alias
 */

#define VGAUTH_REQUESTQUERYALIASES_ELEMENT_NAME  "QueryAliases"

#define VGAUTH_QUERYALIASES_REQUEST_FORMAT \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTQUERYALIASES_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
       "<"VGAUTH_USERNAME_ELEMENT_NAME">%s</"VGAUTH_USERNAME_ELEMENT_NAME">" \
   VGAUTH_REQUEST_FORMAT_END


#define VGAUTH_QUERYALIASES_REPLY_FORMAT_START \
   VGAUTH_REPLY_FORMAT_START

/*
 * Followed by 0 or more VGAUTH_ALIAS_FORMAT_START,
 * with 1 or more
 * VGAUTH_NAMEDALIASINFO_FORMAT or VGAUTH_ANYALIASINFO_FORMAT
 * Closed by VGAUTH_ALIAS_FORMAT_END
 */

#define VGAUTH_QUERYALIASES_REPLY_FORMAT_END \
   VGAUTH_REPLY_FORMAT_END


/*
 * QueryMappedAliases
 *
 * Queries all the certificate/subject pairs and associated users
 * from the mapping file.
 *
 * Permissions: any user
 *
 * Request:
 * =>
 *   array of pemCerts
 *   array of userName
 *   array of NameSubject or AnySubject
 */

#define VGAUTH_REQUESTQUERYMAPPEDALIASES_ELEMENT_NAME  "QueryMappedAliases"

#define VGAUTH_QUERYMAPPEDALIASES_REQUEST_FORMAT \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTQUERYMAPPEDALIASES_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
   VGAUTH_REQUEST_FORMAT_END


#define VGAUTH_QUERYMAPPEDALIASES_REPLY_FORMAT_START \
   VGAUTH_REPLY_FORMAT_START

/*
 * Followed by an array of 0 or more VGAUTH_MAPPEDALIASES_FORMAT_START, which
 * contains a userName and pemCert, and an array of VGAUTH_SUBJECT_FORMAT
 * or VGAUTH_ANYSUBJECT_FORMAT, closed by VGAUTH_MAPPEDALIASES_FORMAT_END.
 */

#define VGAUTH_QUERYMAPPEDALIASES_REPLY_FORMAT_END \
   VGAUTH_REPLY_FORMAT_END


/*
 * CreateTicket
 *
 * Creates a ticket for the specified user.
 *
 * This will fail with a permission error if the request doesn't
 * come over a namedPipe owned by superuser or 'user'.
 *
 * Request:
 *   userName
 *   token - The access token that the service shall duplicate on Windows.
 *           On other platform, it can be an empty string.
 *           On Windows, the service duplicates the access token when creating a
 *           ticket object. Later the access token can be returned to the
 *           client upon a ValidateTicket request.
 *   userHandleInfo
 * =>
 *   Ticket
 */

#define VGAUTH_REQUESTCREATETICKET_ELEMENT_NAME  "CreateTicket"

#define VGAUTH_CREATETICKET_REQUEST_FORMAT_START \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTCREATETICKET_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
       "<"VGAUTH_USERNAME_ELEMENT_NAME">%s</"VGAUTH_USERNAME_ELEMENT_NAME">" \
       "<"VGAUTH_TOKEN_ELEMENT_NAME">%s</"VGAUTH_TOKEN_ELEMENT_NAME">" \
       VGAUTH_USERHANDLEINFO_FORMAT_START \
/*
 * Followed by optional VGAUTH_USERHANDLESAMLINFO_FORMAT_START.
 */

#define VGAUTH_CREATETICKET_REQUEST_FORMAT_END \
   VGAUTH_USERHANDLEINFO_FORMAT_END \
   VGAUTH_REQUEST_FORMAT_END

#define VGAUTH_CREATETICKET_REPLY_FORMAT \
   VGAUTH_REPLY_FORMAT_START \
   "<"VGAUTH_TICKET_ELEMENT_NAME">%s</"VGAUTH_TICKET_ELEMENT_NAME">" \
   VGAUTH_REPLY_FORMAT_END


/*
 * ValidateTicket
 *
 * Validates a ticket.
 *
 * This will fail with a permission error if the ticket in the request doesn't
 * come over a namedPipe owned by superuser or 'user'.
 *
 * Request:
 *   Ticket
 * =>
 *   userName
 *   token - On Windows, the impersonation HANDLE, converted as a base 10
 *           number to a string, that represents the user that the ticket has
 *           authenticated the caller to. The service duplicates the
 *           impersonation HANDLE to the client process. The HANDLE value is
 *           only meaningful in the client process address space.
 *           On other platform, it is an empty string.
 *   userHandleInfo
 */

#define VGAUTH_REQUESTVALIDATETICKET_ELEMENT_NAME  "ValidateTicket"

#define VGAUTH_VALIDATETICKET_REQUEST_FORMAT \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTVALIDATETICKET_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
      "<"VGAUTH_TICKET_ELEMENT_NAME">%s</"VGAUTH_TICKET_ELEMENT_NAME">" \
   VGAUTH_REQUEST_FORMAT_END

#define VGAUTH_VALIDATETICKET_REPLY_FORMAT_START \
   VGAUTH_REPLY_FORMAT_START \
       "<"VGAUTH_USERNAME_ELEMENT_NAME">%s</"VGAUTH_USERNAME_ELEMENT_NAME">" \
       "<"VGAUTH_TOKEN_ELEMENT_NAME">%s</"VGAUTH_TOKEN_ELEMENT_NAME">" \
       VGAUTH_USERHANDLEINFO_FORMAT_START
/*
 * Followed by an optional VGAUTH_USERHANDLESAMLINFO_FORMAT_{START,END}.
 */

#define VGAUTH_VALIDATETICKET_REPLY_FORMAT_END \
   VGAUTH_USERHANDLEINFO_FORMAT_END \
   VGAUTH_REPLY_FORMAT_END


/*
 * RevokeTicket
 *
 * Revokes a ticket.
 *
 * This will fail with a permission error if the ticket in the request doesn't
 * come over a namedPipe owned by superuser or 'user'.
 *
 * Request:
 *   Ticket
 * =>
 *   (empty)
 */

#define VGAUTH_REQUESTREVOKETICKET_ELEMENT_NAME  "RevokeTicket"

#define VGAUTH_REVOKETICKET_REQUEST_FORMAT \
   VGAUTH_REQUEST_FORMAT_START \
       "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTREVOKETICKET_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
      "<"VGAUTH_TICKET_ELEMENT_NAME">%s</"VGAUTH_TICKET_ELEMENT_NAME">" \
   VGAUTH_REQUEST_FORMAT_END

#define VGAUTH_REVOKETICKET_REPLY_FORMAT \
   VGAUTH_REPLY_FORMAT_START \
   VGAUTH_REPLY_FORMAT_END


/*
 * ValidateSamlToken
 *
 * Validates a SAML Bearer token against the certificate store.
 *
 * Returns the user info for doing impersonation, as well as any attributes
 * associated with the certificates in the certstore used for validation.
 *
 * This will fail with a permission error if the ticket in the request doesn't
 * come over a namedPipe owned by superuser.
 *
 * If valdiateOnly is set, then on Windows the service only validates the
 * SAML token, and does not create an access token on Windows.  This
 * flag is ignored on *ix.
 *
 * If hostVerified is set, then the service will skip the signature
 * check in the SAML token.
 *
 * Request:
 *   SAML token
 *   user
 *   validateOnly (bool)
 *   hostVerified (bool)
 * =>
 *   user
 *   token (empty for non-Windows)
 *   userHandleInfo
 */

#define VGAUTH_REQUESTVALIDATESAMLBEARERTOKEN_ELEMENT_NAME "ValidateSamlBToken"

#define VGAUTH_VALIDATESAMLBEARERTOKEN_REQUEST_FORMAT \
   VGAUTH_REQUEST_FORMAT_START \
      "<"VGAUTH_REQUESTNAME_ELEMENT_NAME">"VGAUTH_REQUESTVALIDATESAMLBEARERTOKEN_ELEMENT_NAME"</"VGAUTH_REQUESTNAME_ELEMENT_NAME">" \
      "<"VGAUTH_SAMLTOKEN_ELEMENT_NAME">%s</"VGAUTH_SAMLTOKEN_ELEMENT_NAME">" \
      "<"VGAUTH_USERNAME_ELEMENT_NAME">%s</"VGAUTH_USERNAME_ELEMENT_NAME">" \
      "<"VGAUTH_VALIDATE_ONLY_ELEMENT_NAME">%s</"VGAUTH_VALIDATE_ONLY_ELEMENT_NAME">" \
      "<"VGAUTH_HOST_VERIFIED_ELEMENT_NAME">%s</"VGAUTH_HOST_VERIFIED_ELEMENT_NAME">" \
   VGAUTH_REQUEST_FORMAT_END

#define VGAUTH_VALIDATESAMLBEARERTOKEN_REPLY_FORMAT_START \
   VGAUTH_REPLY_FORMAT_START \
       "<"VGAUTH_USERNAME_ELEMENT_NAME">%s</"VGAUTH_USERNAME_ELEMENT_NAME">" \
       "<"VGAUTH_TOKEN_ELEMENT_NAME">%s</"VGAUTH_TOKEN_ELEMENT_NAME">" \
       VGAUTH_USERHANDLESAMLINFO_FORMAT_START
/*
 * Followed by the VGAUTH_NAMEDALIASINFO_FORMAT or
 * VGAUTH_ANYALIASINFO_FORMAT.
 */

#define VGAUTH_VALIDATESAMLBEARERTOKEN_REPLY_FORMAT_END \
   VGAUTH_USERHANDLESAMLINFO_FORMAT_END \
   VGAUTH_REPLY_FORMAT_END

/* clang-format on */
#endif   // _VGAUTHPROTO_H_
 07070100000418000081A4000000000000000000000001682255050000256C000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/VGAuthUtil.c /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file VGAuthUtil.c --
 *
 *       utility functions.
 */
#include <stdlib.h>

#include <glib.h>
#include <glib/gstdio.h>

#include "VGAuthBasicDefs.h"
#include "VGAuthLog.h"
#include "VGAuthUtil.h"

#ifdef _WIN32
#include <windows.h>
#endif


/*
 ******************************************************************************
 * HangThread --                                                         */ /**
 *
 * Hang the current thread.
 * Debug function to help diagnose IPC issues and unit tests such as pipe full.
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 *
 ******************************************************************************
 */

void HangThread(const char *func,
                const char *file,
                unsigned line)
{
   LogDebug(func, file, line, "Hang the calling thread");

   do {
      g_usleep(1000 * G_USEC_PER_SEC);
   } while (TRUE);
}


#ifdef _WIN32
/*
 ******************************************************************************
 * Convert_Utf8ToUtf16 --                                                */ /**
 *
 * Convert a NULL terminated UTF8 string to a NULL terminiated UTF16 string
 * Log an error if the conversion fails.
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   str        The input NULL terminated UTF8 string
 *
 * @return a utf16 string result
 *
 ******************************************************************************
 */

gunichar2 *
Convert_Utf8ToUtf16(const char *func,
                    const char *file,
                    unsigned line,
                    const gchar *str)
{
   glong nRead = 0;
   glong nWritten = 0;
   GError *pError = NULL;
   gunichar2 *result = g_utf8_to_utf16(str, -1, &nRead, &nWritten, &pError);

   if (!result) {
      LogWarning(func, file, line, "g_utf8_to_utf16() failed, %s, "
                 "read %d byte(s), written %d wchar(s)", pError->message,
                 (int)nRead, (int)nWritten);
      g_clear_error(&pError);
   }

   return result;
}


/*
 ******************************************************************************
 * Convert_Utf16ToUtf8 --                                                */ /**
 *
 * Convert a NULL terminated UTF16 string to a NULL terminated UTF8 string
 * Log an error if the conversion fails.
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   str        The input NULL terminated UTF16 string
 *
 * @return a utf8 string result
 *
 ******************************************************************************
 */

gchar *
Convert_Utf16ToUtf8(const char *func,
                    const char *file,
                    unsigned line,
                    const gunichar2 *str)
{
   glong nRead = 0;
   glong nWritten = 0;
   GError *pError = NULL;
   gchar *result = g_utf16_to_utf8(str, -1, &nRead, &nWritten, &pError);

   if (!result) {
      LogWarning(func, file, line, "g_utf16_to_utf8() failed, %s, "
                 "read %d wchar(s), written %d byte(s)", pError->message,
                 (int)nRead, (int)nWritten);
      g_clear_error(&pError);
   }

   return result;
}


/*
 ******************************************************************************
 * Convert_TextToUnsignedInt32 --                                        */ /**
 *
 * Convert a NULL terminated ascii string to a unsigned 32bit number
 * Log an error if the conversion fails
 *
 * @param[in]   func       The function name
 * @param[in]   file       The file name
 * @param[in]   line       The line number
 * @param[in]   str        The input NULL terminated ascii string
 * @param[out]  result     The converted unsigned 32bit value
 *
 * @return TRUE on sucess, FALSE otherwise
 *
 ******************************************************************************
 */

gboolean
Convert_TextToUnsignedInt32(const char *func,
                            const char *file,
                            unsigned line,
                            const char *str,
                            unsigned int *result)
{
   /*
    * On 32/64bit Windows, sizeof(unsigned long) = sizeof(unsigned int) = 4
    * On 32bit Linux, sizeof(unsigned long) = sizeof(unsigned int) = 4
    * On 64bit Linux, sizeof(unsigned long) = 8, sizeof(unsigned int) = 4
    */
   unsigned long value;
   char *stopChar;

   value = strtoul(str, &stopChar, 10);
   if (value == 0 || value == ULONG_MAX) {
      LogErrorPosix(func, file, line, "strtoul(%s) failed", str);
      return FALSE;
   }

#ifndef _WIN32
   if (!Check_Is32bitNumber((size_t)value)) {
      LogWarning(func, file, line, "Convert to uint32 overflowed, input = %s",
                 str);
      return FALSE;
   }
#endif

   *result = (unsigned int)value;

   return TRUE;
}


/*
 ******************************************************************************
 * Check_Is32bitNumber --                                               */ /**
 *
 * Check if the number is a 32bit number
 *
 * @param[in]   number     The number to check
 *
 * @note   size_t is 64bit on a 64bit system, and 32bit on a 32bit system
 *
 * @return TRUE if the number is a 32bit number, FALSE otherwise.
 *
 ******************************************************************************
 */

gboolean
Check_Is32bitNumber(size_t number)
{
   unsigned long long number64 = (unsigned long long)number;

   /*
    * Cast to 64bit to shift 32bit
    * Otherwise, the C compiler warns and generates no-op code
    */
   number64 >>=32;

   return (number64 == 0);
}


/*
 ******************************************************************************
 * Convert_UnsignedInt32ToText --                                       */ /**
 *
 * Convert a unsigned 32bit number to its text representation
 * Log an error if the conversion fails
 *
 * @param[in]   number       The number to convert
 *
 * @return   the result ascii string
 *
 ******************************************************************************
 */

gchar *
Convert_UnsignedInt32ToText(unsigned int number)
{
   return g_strdup_printf("%d", number);
}
#endif  // #ifdef _WIN32


/*
 ******************************************************************************
 * Util_CheckExpiration --                                               */ /**
 *
 * Checks whether, given a start time and duration, the current time is
 * passed that duration.
 *
 * @param[in]  start     The start time.
 * @param[in]  duration  The timeout in seconds.
 *
 * @return  TRUE if the current time is passed the duration or FALSE otherwise.
 *
 ******************************************************************************
 */

gboolean
Util_CheckExpiration(const GTimeVal *start,
                     unsigned int duration)
{
   GTimeVal now;
   GTimeVal expire;

   /*
    * XXX blech.  Move to GDateTime as soon as the toolchain glib
    * supports it (version 2.26).
    */
   g_get_current_time(&now);
   expire = *start;

   expire.tv_sec += duration;

   /*
    * We don't need the precision, so just ignore micros.
    */
   return now.tv_sec > expire.tv_sec;
}


/*
 ******************************************************************************
 * Util_Assert --                                                        */ /**
 *
 * Asserts after spewing some location data.
 *
 * @param[in]  cond      The assertion cause.
 * @param[in]  file      The file.
 * @param[in]  lineNum   The line number.
 *
 ******************************************************************************
 */

void
Util_Assert(const char *cond,
            const char *file,
            int lineNum)
{
   g_warning("Assertion '%s' failed at %s:%d\n", cond, file, lineNum);
#ifdef _WIN32
#ifdef VMX86_DEBUG
   DebugBreak();
#endif
#endif
   g_assert(0);
}


/*
 ******************************************************************************
 * Util_Utf8CaseCmp --                                                   */ /**
 *
 * Case insensitive comparison for utf8 strings which can have non-ascii
 * characters.
 *
 * @param[in]  str1      Null terminated utf8 string.
 * @param[in]  str2      Null terminated utf8 string.
 *
 ******************************************************************************
 */

int
Util_Utf8CaseCmp(const gchar *str1,
                 const gchar *str2)
{
   int ret;
   gchar *str1Case;
   gchar *str2Case;

   str1Case = g_utf8_casefold(str1, -1);
   str2Case = g_utf8_casefold(str2, -1);

   ret = g_strcmp0(str1Case, str2Case);

   g_free(str1Case);
   g_free(str2Case);

   return ret;
}
07070100000419000081A40000000000000000000000016822550500001044000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/VGAuthUtil.h /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file VGAuthUtil.h --
 *
 *    Convenient utility macros and declarations
 */

#ifndef _VGAUTH_UTIL_H_
#define _VGAUTH_UTIL_H_

#include <glib.h>

/* For unit testing the code */
void HangThread(const char *func, const char *file, unsigned line);
#define HANG_THREAD() do {                                              \
      HangThread(__FUNCTION__, __FILE__, __LINE__);                     \
   } while (0)

#ifdef _WIN32
gunichar2 *Convert_Utf8ToUtf16(const char *func, const char *file,
                               unsigned line, const gchar *str);
#define CHK_UTF8_TO_UTF16(utf16Out, utf8In, onErrStmt)  do {            \
      utf16Out = Convert_Utf8ToUtf16(__FUNCTION__, __FILE__, __LINE__,  \
                                     utf8In);                           \
      if (!utf16Out) {                                                  \
         onErrStmt;                                                     \
      }                                                                 \
   } while(0)

gchar *Convert_Utf16ToUtf8(const char *func, const char *file,
                           unsigned line, const gunichar2 *str);
#define CHK_UTF16_TO_UTF8(utf8Out, utf16In, onErrStmt)  do {            \
      utf8Out = Convert_Utf16ToUtf8(__FUNCTION__, __FILE__, __LINE__,   \
                                    utf16In);                           \
      if (!utf8Out) {                                                   \
         onErrStmt;                                                     \
      }                                                                 \
   } while(0)

gboolean Convert_TextToUnsignedInt32(const char *func, const char *file,
                                     unsigned line, const char *repr,
                                     unsigned int *result);
#define CHK_TEXT_TO_UINT32(uint32Out, textIn, onErrStmt)  do {          \
      gboolean ok = Convert_TextToUnsignedInt32(__FUNCTION__, __FILE__, \
                                                __LINE__, textIn,       \
                                                &uint32Out);            \
      if (!ok) {                                                        \
         onErrStmt;                                                     \
      }                                                                 \
   } while(0)

gboolean Check_Is32bitNumber(size_t number);

gchar *Convert_UnsignedInt32ToText(unsigned int number);
#endif // #ifdef _WIN32

gboolean Util_CheckExpiration(const GTimeVal *start, unsigned int duration);

/*
 * Converts a utf8 path into the local encoding.  No-op on Windows.
 */
#ifdef _WIN32
#define GET_FILENAME_LOCAL(path, err) (gchar *) path
#define RELEASE_FILENAME_LOCAL(path) (void) (path)
#define DIRSEPS "\\"
#else
#define GET_FILENAME_LOCAL(path, err) g_filename_from_utf8((path), -1, NULL, NULL, (err))
#define RELEASE_FILENAME_LOCAL(path) g_free(path)
#define DIRSEPS "/"
#endif

/*
 * Macro literal fun.
 *
 * Given:
 *
 * #define FOO foo
 *
 * Then:
 *
 * MAKESTR(FOO) becomes "FOO"
 * XTR(FOO) becomes "foo"
 */
#ifndef MAKESTR
#define MAKESTR(x) #x
#define XSTR(x) MAKESTR(x)
#endif

void Util_Assert(const char *cond, const char *file, int lineNum);

int Util_Utf8CaseCmp(const gchar *str1, const gchar *str2);

#endif
0707010000041A000081A4000000000000000000000001682255050000160C000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/audit.c  /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file audit.h
 *
 * Auditing support.
 */

#include "audit.h"
#include <stdarg.h>
#include <stdio.h>

#ifdef _WIN32
#include "auditMessages.h"
#include <windows.h>
#else
#include <syslog.h>
#endif
#include "VGAuthLog.h"

/*
 * Since the syslog() APIs are global, the audit system must be as well.
 */

static gboolean doLogSuccess = TRUE;
static gboolean auditInited = FALSE;
#ifdef _WIN32
static HANDLE hAuditSource = INVALID_HANDLE_VALUE;
#endif

/*
 ******************************************************************************
 * Audit_Init --                                                         */ /**
 *
 * Initializes the audit library.
 *
 * @param[in] appName     The application name, which is included as part of the
 *                        audit message.
 * @param[in] logSuccess  If set, audit success messages are logged.
 *
 ******************************************************************************
 */

void
Audit_Init(const char *appName,
           gboolean logSuccess)
{
#ifdef _WIN32
   wchar_t *appName16 =
      (wchar_t *) g_utf8_to_utf16(appName, -1, NULL, NULL, NULL);
   hAuditSource = RegisterEventSourceW(NULL, appName16);
   if (NULL == hAuditSource) {
      VGAUTH_LOG_ERR_WIN("RegisterEventSourceW() failed");
   }
   g_free(appName16);

#else // !_WIN32

   int facility;

   /*
    * LOG_AUTHPRIV is the new name on Linux.
    * Solaris (and older Linux?) want LOG_AUTH.
    */
#ifdef LOG_AUTHPRIV
   facility = LOG_AUTHPRIV;
#else
   facility = LOG_AUTH;
#endif

   openlog(appName, LOG_PID, facility);
#endif

   auditInited = TRUE;
   doLogSuccess = logSuccess;
}


/*
 ******************************************************************************
 * Audit_Shutdown --                                                     */ /**
 *
 * Shuts down the audit library.
 *
 ******************************************************************************
 */

void
Audit_Shutdown(void)
{
#ifdef _WIN32
   DeregisterEventSource(hAuditSource);
   hAuditSource = INVALID_HANDLE_VALUE;
#else
   closelog();
#endif
   auditInited = FALSE;
}


/*
 ******************************************************************************
 * Audit_Event --                                                         */ /**
 *
 * Logs an auditing event.
 * Note that a final '.' in the @a fmt is added by the underlying system
 * (Windows events add them.)
 *
 * @param[in] isSuccess   If true, the message is a successful event.
 * @param[in] fmt         The format message for the event.
 *
 ******************************************************************************
 */

void
Audit_Event(gboolean isSuccess,
            const char *fmt,
            ...)
{
   va_list args;

   va_start(args, fmt);
   Audit_EventV(isSuccess, fmt, args);
   va_end(args);
}


/*
 ******************************************************************************
 * Audit_EventV --                                                       */ /**
 *
 * Logs an auditing event using va_list arguments.
 * Note that a final '.' in the @a fmt is added by the underlying system
 * (Windows events add them.)
 *
 * @param[in] isSuccess   If true, the message is a successful event.
 * @param[in] fmt         The format message for the event.
 * @param[in] args        The arguments for @a fmt.
 *
 ******************************************************************************
 */

void
Audit_EventV(gboolean isSuccess,
             const char *fmt,
             va_list args)
{
   gchar *buf;

   if (isSuccess && !doLogSuccess) {
      return;
   }
   buf = g_strdup_vprintf(fmt, args);

#ifdef VMX86_DEBUG
   if (!auditInited) {
      fprintf(stderr, "Audit Event being dropped!: %s\n", buf);
      goto done;
   }
#endif

#ifdef _WIN32
   {
      wchar_t *buf16 = (wchar_t *) g_utf8_to_utf16(buf, -1, NULL, NULL, NULL);

      ReportEventW(hAuditSource,
                   isSuccess ? EVENTLOG_AUDIT_SUCCESS : EVENTLOG_AUDIT_FAILURE,
                   0,                        // category
                   VGAUTH_AUDIT_MESSAGE,     // Event ID
                   NULL,                     // user
                   1,                        // numStrings
                   0,                        // data size
                   &buf16,                   // string array
                   NULL);                    // any binary data

      g_free(buf16);
   }
#else
   /*
    * XXX
    *
    * This may need tuning.  Other apps (sshd) seems to use LOG_INFO
    * for both success and failure events, but that feels wrong.
    */
   /*
    * The message gets a final '.' in the event viewer.  Add one here for
    * syslog()
    */
   syslog(isSuccess ? LOG_INFO : LOG_WARNING, "%s.", buf);
#endif

#ifdef VMX86_DEBUG
done:
#endif
   g_free(buf);
}

0707010000041B000081A400000000000000000000000168225505000004F8000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/audit.h  /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _AUDIT_H_
#define _AUDIT_H_

/*
 * @file audit.h
 *
 * Auditing support.
 */

#include "VGAuthBasicDefs.h"
#include <glib.h>

void Audit_Init(const char *appName, gboolean logSuccess);

void Audit_Shutdown(void);

void Audit_Event(gboolean isSuccess, const char *fmt, ...) PRINTF_DECL(2, 3);

void Audit_EventV(gboolean isSuccess, const char *fmt, va_list args);

#endif // _AUDIT_H_
0707010000041C000081A40000000000000000000000016822550500007474000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/certverify.c /*********************************************************
 * Copyright (c) 2011-2016, 2018-2019, 2021-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file certverify.c
 *
 * Code to handle certficate verification with OpenSSL.
 */

#include "VGAuthError.h"
#include "VGAuthBasicDefs.h"
#include "VGAuthAuthentication.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/err.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "certverify.h"

VGAuthError CertVerify_CheckSignature(VGAuthHashAlg hash,
                                      EVP_PKEY *publicKey,
                                      size_t dataLen,
                                      const unsigned char *data,
                                      size_t signatureLen,
                                      const unsigned char *signature);



/*
 ******************************************************************************
 * VGAuthVerifyInit --                                                   */ /**
 *
 * Initializes OpenSSL for verify work.
 *
 ******************************************************************************
 */

void
CertVerify_Init(void)
{
   /*
    * XXX SSL init test -- loads err strings from both libs.
    * We may need just ERR_load_crypto_strings(); using this
    * to verify the make/deploy stuff doesn't leave libssl out.
    */
   SSL_load_error_strings();

   /*
    * XXX This may need tuning.  All we need for signature checks
    * are the digests.  We may want to add the ciphers or algorithms as
    * well, but OpenSSL_add_all_algorithms() and OpenSSL_add_all_ciphers()
    * can add a lot of bloat.
    */
   OpenSSL_add_all_digests();
}


/*
 ******************************************************************************
 * VerifyDumpSSLErrors --                                                */ /**
 *
 * Drains the ssl error stack and redirects them through glib.
 *
 ******************************************************************************
 */

static void
VerifyDumpSSLErrors(void)
{
   int flags;
   int line;
   const char *data;
   const char *file;
   unsigned long code;
#if OPENSSL_VERSION_NUMBER >= 0X30000000L
   const char *func;
#endif

#if OPENSSL_VERSION_NUMBER >= 0X30000000L
   code = ERR_get_error_all(&file, &line, &func, &data, &flags);
#else
   code = ERR_get_error_line_data(&file, &line, &data, &flags);
#endif
   while (code) {
#if OPENSSL_VERSION_NUMBER >= 0X30000000L
      g_warning("SSL error: %lu (%s) in %s func %s line %d\n",
                code, ERR_error_string(code, NULL), file, func, line);
#else
      g_warning("SSL error: %lu (%s) in %s line %d\n",
                code, ERR_error_string(code, NULL), file, line);
#endif
      if (data && (flags & ERR_TXT_STRING)) {
         g_warning("SSL error data: %s\n", data);
      }
      /*
       * Note -- the docs mention the ERR_TXT_MALLOCED flag, but that doesn't
       * mean we got a copy of 'data'.  If 'data' is free'd here, it 'works'
       * until the SSL error buffer starts getting reused and a double
       * free happens.
       */
#if OPENSSL_VERSION_NUMBER >= 0X30000000L
      code = ERR_get_error_all(&file, &line, &func, &data, &flags);
#else
      code = ERR_get_error_line_data(&file, &line, &data, &flags);
#endif
   }
}


/*
 ******************************************************************************
 * VerifyCallback --                                                     */ /**
 *
 * Store verification callback.  Alows customization and error logging
 * during verification.
 *
 * @param[in]  ok      The current state of the verification.
 * @param[in]  ctx     The x509 store context.
 *
 * @return 1 on success, 0 on failure.
 *
 ******************************************************************************
 */

static int
VerifyCallback(int ok,
               X509_STORE_CTX *ctx)
{
   int ret = ok;
   int certErr = X509_STORE_CTX_get_error(ctx);
   X509 *curCert = X509_STORE_CTX_get_current_cert(ctx);
   char nameBuf[512];

   /*
    * XXX
    *
    * This is a legacy function that has some issues, but setting up a bio
    * just for a bit of debug seems excessive.
    */
   if (NULL != curCert) {
      X509_NAME_oneline(X509_get_subject_name(curCert), nameBuf, sizeof(nameBuf) - 1);
      nameBuf[sizeof(nameBuf)-1] = '\0';
   } else {
      /* Ignore return, returns length of the source string */
      /* coverity[check_return] */
      g_strlcpy(nameBuf, "<NO CERT SUBJECT>", sizeof nameBuf);
   }
   g_debug("%s: name: %s ok: %d error '%s' (%d) at %d depth lookup:%s\n",
           __FUNCTION__,
           nameBuf,
           ok,
           X509_verify_cert_error_string(certErr),
           certErr,
           X509_STORE_CTX_get_error_depth(ctx),
           X509_verify_cert_error_string(certErr));

   if (!ok) {
      switch (certErr) {
         // self-signed is ok
      case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
      case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
         g_debug("%s: allowing error %d\n", __FUNCTION__, certErr);
         ret = 1;
         break;
      default:
         g_warning("%s: error '%s' (%d) treated as failure\n",
                   __FUNCTION__, X509_verify_cert_error_string(certErr),
                   certErr);
         break;
      }
   }

   return ret;
}


/*
 ******************************************************************************
 * CertVerify_StripPEMCert --                                            */ /**
 *
 * Cleans off any leading or trailing delimiters from a PEM certificate.
 * The resulting string needs to be g_free()d.
 *
 * Assumes the data is in the openssl form, but allows for some fudge
 * factor in the way the '---' are handled in case of hand-editing.
 * This may be excessive, but since we're currently thinking people can
 * hand-edit things, and its not that much harder, lets try it.
 * Of course, if we get a test case that tries to do this, I'm sure
 * they can beat it if they try hard enough.
 *
 * @param[in]   pemCert     The pemCert to clean.
 *
 * @return The cleaned PEM cert.
 *
 ******************************************************************************
 */

gchar *
CertVerify_StripPEMCert(const gchar *pemCert)
{
   gchar *result = g_strdup(pemCert);
   gchar *b;
   gchar *e;
   gsize len;

   /*
    * Find the the -----END CERTIFICATE----- or a variant.
    */
   e = g_strrstr(result, "\n--");
   if (NULL != e) {
      *(e+1) = '\0';
   }

   /*
    * Find the the -----BEGIN CERTIFICATE----- or a variant.
    */
   b = g_strstr_len(result, strlen(result), "--\n");
   if (NULL != b) {
      b += 3;
      len = strlen(b) + 1;
      memmove(result, b, len);
   }

   return result;
}


static gchar *sslCertHeader = "-----BEGIN CERTIFICATE-----\n";
static gchar *sslCertFooter = "-----END CERTIFICATE-----\n";


/*
 ******************************************************************************
 * CertVerify_EncodePEMForSSL --                                         */ /**
 *
 * OpenSSL is absurdly picky about PEM.  It must have the proper header
 * and footer.  It also insists on having newlines every 64 chars.
 * When we pull the PEM out of something like a SAML token, its
 * not good enough, so this code will convert base64 to something SSL
 * likes.
 *
 * @param[in]   pemCert     The pemCert to clean.
 *
 * @return The OpenSSL-safe PEM cert.
 *
 ******************************************************************************
 */

gchar *
CertVerify_EncodePEMForSSL(const gchar *pemCert)
{
   gchar *tmpCertStr = NULL;
   gchar *result;
   char *t;
   guchar *binCert;
   gsize len;
   int cnt;
   gsize strLen;
   gchar *cleanCertStr = NULL;

   /*
    * Make sure its just base64 data.
    */
   tmpCertStr = CertVerify_StripPEMCert(pemCert);

   /*
    * Decode
    */
   binCert = g_base64_decode(tmpCertStr, &len);
   g_free(tmpCertStr);

   /*
    * Now re-encode -- this way we flush any whitespace out of the original.
    */
   cleanCertStr = g_base64_encode(binCert, len);

   /*
    * rebuild, with a newline every 64 chars.
    */
   len = strlen(cleanCertStr);
   strLen = len +
      strlen(sslCertHeader) +
      strlen(sslCertFooter) +
      len/64 +                   // newline every 64 chars
      1 +                        // +1 for any leftover
      1;                         // final NUL

   result = g_malloc0(strLen);

   /*
    * Now rebuild it, with the SSL wrapper and newlines every 64 chars.
    */
   memcpy(result, sslCertHeader, strlen(sslCertHeader));
   tmpCertStr = result + strlen(sslCertHeader);

   t = cleanCertStr;
   cnt = 0;
   while (*t) {
      *tmpCertStr++ = *t++;
      if (++cnt == 64) {
         *tmpCertStr++ = '\n';
         cnt = 0;
      }
   }
   if (cnt != 0) {      // don't add an double newline
      *tmpCertStr++ = '\n';
   }
   memcpy(tmpCertStr, sslCertFooter, strlen(sslCertFooter));

   g_free(cleanCertStr);
   g_free(binCert);

   return result;
}


/*
 ******************************************************************************
 * CertStringToX509 --                                                   */ /**
 *
 * Creates an openssl x509 object from a pemCert string.
 *
 * @param[in]  pemCert      The certificate in PEM format.
 *
 * @return a new X509 object containing the cert.
 *
 ******************************************************************************
 */

static X509 *
CertStringToX509(const char *pemCert)
{
   BIO *bio;
   X509 *newCert = NULL;
   char *sslCertStr = NULL;
   gsize len;

   ASSERT(pemCert);

   /*
    * Don't blow up if fed junk.
    */
   len = strlen(pemCert);
   if (len < strlen(sslCertHeader)) {
      return NULL;
   }

   /*
    * XXX This assums that if the header is there, the footer is too.
    * It'd safer but more wasteful to always force it to be added.
    */
   if (memcmp(sslCertHeader, pemCert, strlen(sslCertHeader)) != 0) {
      sslCertStr = CertVerify_EncodePEMForSSL(pemCert);
   } else {
      sslCertStr = g_strdup(pemCert);
   }

   // create a BIO from the string
   bio = BIO_new_mem_buf((void *) sslCertStr, -1);
   if (NULL == bio) {
      VerifyDumpSSLErrors();
      g_warning("%s: unable to convert string to BIO\n", __FUNCTION__);
      goto done;
   }

   // read the x509 object from it
   newCert = PEM_read_bio_X509(bio, NULL, 0, NULL);
   if (NULL == newCert) {
      VerifyDumpSSLErrors();
      g_warning("%s: unable to convert string to x509\n", __FUNCTION__);
   }

   // make sure the data isn't free()d with the BIO
   // XXX is this necessary?
//   BIO_set_close(bio, BIO_NOCLOSE);
   BIO_free(bio);

done:

   g_free(sslCertStr);

   return newCert;
}


/*
 ******************************************************************************
 * CertVerifyX509ToString --                                             */ /**
 *
 * Debug support for X509 certs; convert them to human readable text.
 *
 * @param[in]  x  An X509 structure containing a cert.
 *
 * @return Allocated string containing the cert in human-readable text.
 *
 ******************************************************************************
 */

static gchar *
CertVerifyX509ToString(X509 *x)
{
   BIO *mem;
   char *str;
   gchar *retVal;
   int len;

   mem = BIO_new(BIO_s_mem());
   if (!mem) {
      g_warning("%s: out of memory creating BIO\n", __FUNCTION__);
      return NULL;
   }

   X509_print(mem, x);

   len = BIO_get_mem_data(mem, &str);

   retVal = g_strndup(str, len);

   BIO_set_close(mem, BIO_CLOSE);
   BIO_free(mem);

   return retVal;
}


/*
 ******************************************************************************
 * CertVerify_CertToX509String --                                        */ /**
 *
 * Debug support for certs; convert them to human readable text.
 *
 * @param[in]  pemCert A certficate in PEM format.
 *
 * @return Allocated string containing the cert in human-readable text.
 *
 ******************************************************************************
 */

gchar *
CertVerify_CertToX509String(const gchar *pemCert)
{
   X509 *x;
   gchar *retVal = NULL;

   x = CertStringToX509(pemCert);
   if (x) {
      retVal = CertVerifyX509ToString(x);
   }
   X509_free(x);

   return retVal;
}


/*
 ******************************************************************************
 * CertVerify_IsWellFormedPEMCert --                                     */ /**
 *
 * Checks to see if a PEM cert string can be converted into a x509
 * object.  Note that it does not verify the contents of the cert for
 * proper contents, expiration, revocation, etc; just
 * if the string can be converted into an x509 cert.
 *
 * @param[in]  pemCert      The certificate in PEM format.
 *
 * @return TRUE if the string contains a PEM cert.
 *
 ******************************************************************************
 */

gboolean
CertVerify_IsWellFormedPEMCert(const char *pemCert)
{
   X509 *x509Cert;

   if (NULL == pemCert) {
      return FALSE;
   }

   x509Cert = CertStringToX509(pemCert);
   if (NULL == x509Cert) {
      return FALSE;
   }
   X509_free(x509Cert);
   return TRUE;
}


#if 0
/*
 * ssl stack test code.
 *
 * ssl stacks act as expected, but the terminology in the docs is
 * confusing.  'pop' is doc'd to return the last element on the
 * stack, but it does do the expected LIFO.
 * 'shift' claims it returns the first element, but it actually
 * returns the first thing added.
 *
 * this bit of code helped make sense of it, so leaving behind
 * as a framework to experiment with other stack operations.
 */
static void
VerifyTstSSLStacks(void)
{
   STACK_OF(ASN1_INTEGER) *istack;
   ASN1_INTEGER *aint;

   istack = sk_ASN1_INTEGER_new_null();
   for (i = 0; i < 10; i++) {
      aint = ASN1_INTEGER_new();
      ASN1_INTEGER_set(aint, i);
      sk_ASN1_INTEGER_push(istack, aint);
   }

   // 'shift' does FIFO
   while ((aint = sk_ASN1_INTEGER_shift(istack)) != NULL) {
      printf("shifted value: %ld\n", ASN1_INTEGER_get(aint));
   }
   // 'pop' does LIFO
   while ((aint = sk_ASN1_INTEGER_pop(istack)) != NULL) {
      printf("popped value: %ld\n", ASN1_INTEGER_get(aint));
   }
}
#endif


/*
 ******************************************************************************
 * PEMChainToStack --                                                    */ /**
 *
 * Converts an array of PEM certificates into a stack of x509 objects.
 *
 * @param[in]  numCerts      The number of certs to convert.
 * @param[in]  pemCerts      The certs.
 * @param[out] newChain      The resulting chain.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

static VGAuthError
PEMChainToStack(int numCerts,
                const char **pemCerts,
                STACK_OF(X509) **newChain)
{
   STACK_OF(X509) *chain = NULL;
   int i;
   X509 *x509Cert;
   VGAuthError err = VGAUTH_E_OK;

   *newChain = NULL;
   if (numCerts > 0) {
      chain = sk_X509_new_null();
      if (NULL == chain) {
         err = VGAUTH_E_FAIL;
         g_warning("%s: failed to create X509 stack\n", __FUNCTION__);
         goto done;
      }

      for (i = 0; i < numCerts; i++) {
         x509Cert = CertStringToX509(pemCerts[i]);
         if (NULL == x509Cert) {
            err = VGAUTH_E_INVALID_CERTIFICATE;
            g_warning("%s: failed to convert PEM cert to X509\n", __FUNCTION__);
            goto done;
         }

         if (0 == sk_X509_push(chain, x509Cert)) {
            VerifyDumpSSLErrors();
            err = VGAUTH_E_FAIL;
            X509_free(x509Cert);
            g_warning("%s: failed to add cert to stack\n", __FUNCTION__);
            goto done;
         }
      }
      ASSERT(sk_X509_num(chain) == numCerts);
   }
   *newChain = chain;

done:
   if (VGAUTH_E_OK != err) {
      sk_X509_pop_free(chain, X509_free);
   }

   return err;
}


/*
 ******************************************************************************
 * CertVerify_CertChain --                                               */ /**
 *
 * @brief Verifies a complete certificate chain.
 *
 * Verifies that all certs are properly signed, in the proper date range, etc.
 * The pemLeafCert is the cert being validated.
 * The pemUntrustedCertChain contains the certs passed in which are
 * not trusted (eg, those not found in the certstore).
 * The pemTrustedCerts contains all certificates that are in the certstore.
 *
 * @param[in]  pemLeafCert              The leaf cert in PEM format.
 * @param[in]  numUntrustedCerts        The size of the untrusted chain.
 * @param[in]  pemUntrustedCertChain    The chain of untrusted certificates.
 * @param[in]  numTrustedCerts          The size of the trusted chain.
 * @param[in]  pemTrustedCertChain      The chain of trusted certificates.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
CertVerify_CertChain(const char *pemLeafCert,
                     int numUntrustedCerts,
                     const char **pemUntrustedCertChain,
                     int numTrustedCerts,
                     const char **pemTrustedCertChain)
{
   VGAuthError err = VGAUTH_E_OK;
   int ret;
   STACK_OF(X509) *trustedChain = NULL;
   STACK_OF(X509) *untrustedChain = NULL;
   X509_STORE *store = NULL;
   X509_STORE_CTX *verifyCtx = NULL;
   X509 *leafCert;


   /*
    * Turn the leaf cert into an x509 object.
    */
   leafCert = CertStringToX509(pemLeafCert);
   if (NULL == leafCert) {
      err = VGAUTH_E_INVALID_CERTIFICATE;
      g_warning("%s: failed to convert PEM cert to X509\n", __FUNCTION__);
      goto done;
   }

   err = PEMChainToStack(numUntrustedCerts,
                         pemUntrustedCertChain,
                         &untrustedChain);
   if (VGAUTH_E_OK != err) {
      g_warning("%s: failed to convert untrusted chain\n", __FUNCTION__);
      goto done;
   }
   err = PEMChainToStack(numTrustedCerts,
                         pemTrustedCertChain,
                         &trustedChain);
   if (VGAUTH_E_OK != err) {
      g_warning("%s: failed to convert trusted chain\n", __FUNCTION__);
      goto done;
   }

   /*
    * Build X509 store.
    */
   store = X509_STORE_new();
   if (NULL == store) {
      err = VGAUTH_E_FAIL;
      VerifyDumpSSLErrors();
      g_warning("%s: unable to create x509 store\n", __FUNCTION__);
      goto done;
   }

   /*
    * Set the callback.
    *
    * XXX OpenSSL v1.0 has X509_STORE_set_verify_cb()
    */
   X509_STORE_set_verify_cb_func(store, VerifyCallback);

   /*
    * Do the verification.
    *
    * Note that the verify code is not happy seeing a self-signed (CA)
    * cert in the untrusted list.
    */
   verifyCtx = X509_STORE_CTX_new();
   if (NULL == verifyCtx) {
      err = VGAUTH_E_FAIL;
      VerifyDumpSSLErrors();
      g_warning("%s: unable to create x509 store context\n", __FUNCTION__);
      goto done;
   }

   /*
    * Set up the verify to look at leafCert.  We pass no additional
    * untrusted certs.
    */
   ret = X509_STORE_CTX_init(verifyCtx, store, leafCert, untrustedChain);
   if (1 != ret) {
      err = VGAUTH_E_FAIL;
      VerifyDumpSSLErrors();
      g_warning("%s: unable to init x509 store context\n", __FUNCTION__);
      goto done;
   }

   /*
    * Set the trusted list.  Anything self-signed needs to be here.
    */
   X509_STORE_CTX_trusted_stack(verifyCtx, trustedChain);

   /*
    * XXX
    *
    * Add CRLs
    */


   /*
    * And now check it
    */
   ret = X509_verify_cert(verifyCtx);
   if (ret <= 0) {
      err = VGAUTH_E_INVALID_CERTIFICATE;
      VerifyDumpSSLErrors();
      g_warning("%s: unable to verify x509 certificate (ret = %d)\n", __FUNCTION__, ret);
      goto done;
   }

done:
   sk_X509_pop_free(trustedChain, X509_free);
   sk_X509_pop_free(untrustedChain, X509_free);
   X509_free(leafCert);

   /*
    * SSL 'free' code doesn't seem to believe in doing NULL checks
    */
   if (verifyCtx) {
      X509_STORE_CTX_free(verifyCtx);
   }
   if (store) {
      X509_STORE_free(store);
   }

   return err;
}


/*
 ******************************************************************************
 * CertVerify_CheckSignatureUsingCert --                                 */ /**
 *
 * @brief Verifies the signature of binary data.
 *
 * Verifies that 'data' has been correctly signed using the private key
 * associated with the public key in the certificate given by pemCert.
 *
 * Does not make any checks on the validity of the certificate.
 *
 * @param[in] hash         Identifies the hash algorithm to used when computing
 *                         the signature.
 * @param[in] pemCert      The certificate containing the public key to use
 *                         when validating the signature.
 * @param[in] dataLen      The length of 'data' in bytes.
 * @param[in] data         The data that has been signed.
 * @param[in] signatureLen The length of 'signature' in bytes.
 * @param[in] signature    The signature of data.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
CertVerify_CheckSignatureUsingCert(VGAuthHashAlg hash,
                                   const char *pemCert,
                                   size_t dataLen,
                                   const unsigned char *data,
                                   size_t signatureLen,
                                   const unsigned char *signature)
{
   VGAuthError err;
   X509 *cert;
   X509_PUBKEY *x509PubKey;
   EVP_PKEY *publicKey;

   cert = CertStringToX509(pemCert);
   if (NULL == cert) {
      err = VGAUTH_E_INVALID_CERTIFICATE;
      g_warning("%s: failed to convert PEM cert to X509.\n", __FUNCTION__);
      goto done;
   }

   x509PubKey = X509_get_X509_PUBKEY(cert);
   publicKey = X509_PUBKEY_get(x509PubKey);
   if (NULL == publicKey) {
      VerifyDumpSSLErrors();
      g_warning("%s: unable to get the public key from the cert.\n", __FUNCTION__);
      err = VGAUTH_E_FAIL;
      goto done;
   }

   err = CertVerify_CheckSignature(hash, publicKey, dataLen, data,
                                   signatureLen, signature);
   EVP_PKEY_free(publicKey);

done:
   X509_free(cert);

   return err;
}


/*
 ******************************************************************************
 * CertVerify_CheckSignature --                                          */ /**
 *
 * @brief Verifies the signature of binary data.
 *
 * Verifies that 'data' has been correctly signed using the private key
 * associated with 'publicKey'.
 *
 * @param[in] hash         Identifies the hash algorithm to used when computing
 *                         the signature.
 * @param[in] pemCert      The public key to use when validating the signature.
 * @param[in] dataLen      The length of 'data' in bytes.
 * @param[in] data         The data that has been signed.
 * @param[in] signatureLen The length of 'signature' in bytes.
 * @param[in] signature    The signature of data.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
CertVerify_CheckSignature(VGAuthHashAlg hash,
                          EVP_PKEY *publicKey,
                          size_t dataLen,
                          const unsigned char *data,
                          size_t signatureLen,
                          const unsigned char *signature)
{
   VGAuthError err = VGAUTH_E_FAIL;
   EVP_MD_CTX *mdCtx = NULL;
   const EVP_MD *hashAlg;
   int ret;

   mdCtx = EVP_MD_CTX_new();
   if (mdCtx == NULL) {
      g_warning("%s: unable to allocate a message digest.\n", __FUNCTION__);
      return(VGAUTH_E_OUT_OF_MEMORY);
   }

   switch (hash) {
   case VGAUTH_HASH_ALG_SHA256:
      hashAlg = EVP_sha256();
      break;
   default:
      g_warning("%s: unrecognized hash algorithm %d.\n", __FUNCTION__, hash);
      err = VGAUTH_E_INVALID_ARGUMENT;
      goto done;
   }

   ret = EVP_VerifyInit(mdCtx, hashAlg);
   if (ret <= 0) {
      VerifyDumpSSLErrors();
      g_warning("%s: unable to initialize verificatation context (ret = %d)\n",
                __FUNCTION__, ret);
      goto done;
   }

   /*
    * Since we are synchronous, just compute the hash over all the data in
    * one shot. We probably should put some upper bound on the size of the
    * data.
    */
   ret = EVP_VerifyUpdate(mdCtx, data, dataLen);
   if (ret <= 0) {
      VerifyDumpSSLErrors();
      g_warning("%s: unable to update verificatation context (ret = %d)\n",
                __FUNCTION__, ret);
      goto done;
   }

   ret = EVP_VerifyFinal(mdCtx, signature, (unsigned int) signatureLen, publicKey);
   if (0 == ret) {
      g_debug("%s: verification failed!\n", __FUNCTION__);
      err = VGAUTH_E_AUTHENTICATION_DENIED;
      goto done;
   } else if (ret < 0) {
      VerifyDumpSSLErrors();
      g_warning("%s: error while verifying signature (ret = %d)\n",
                __FUNCTION__, ret);
      goto done;
   }

   err = VGAUTH_E_OK;

done:
   EVP_MD_CTX_free(mdCtx);

   return err;
}


/*
 * Finds a cert with a subject (if checkSubj is set) or issuer (if
 * checkSUbj is unset), matching 'val' in the list
 * of certs.  Returns a match or NULL.
 */

static X509 *
FindCert(GList *cList,
         X509_NAME *val,
         int checkSubj)
{
   GList *l;
   X509 *c;
   X509_NAME *v;

   l = cList;
   while (l != NULL) {
      c = (X509 *) l->data;
      if (checkSubj) {
         v = X509_get_subject_name(c);
      } else {
         v = X509_get_issuer_name(c);
      }
      if (X509_NAME_cmp(val, v) == 0) {
         return c;
      }
      l = l->next;
   }
   return NULL;
}


/*
 ******************************************************************************
 * CertVerify_CheckForUnrelatedCerts --                                  */ /**
 *
 * Looks over a list of certs.  If it finds that they are not all
 * part of the same chain, returns failure.
 *
 * @param[in]     numCerts      The number of certs in the chain.
 * @param[in]     pemCerts      The chain of certificates to verify.
 *
 * @return VGAUTH_E_OK on success, VGAUTH_E_FAIL if unrelated certs are found.
 *
 ******************************************************************************
 */

VGAuthError
CertVerify_CheckForUnrelatedCerts(int numCerts,
                                  const char **pemCerts)
{
   VGAuthError err = VGAUTH_E_FAIL;
   int chainLen = 0;
   int i;
   X509 **certs = NULL;
   GList *rawList = NULL;
   X509 *baseCert;
   X509 *curCert;
   X509_NAME *subject;
   X509_NAME *issuer;

   /* common single cert case; nothing to do */
   if (numCerts == 1) {
      return VGAUTH_E_OK;
   }

   /* convert all PEM to X509 objects */
   certs = g_malloc0(numCerts * sizeof(X509 *));
   for (i = 0; i < numCerts; i++) {
      certs[i] = CertStringToX509(pemCerts[i]);
      if (NULL == certs[i]) {
         g_warning("%s: failed to convert cert to X509\n", __FUNCTION__);
         goto done;
      }
   }

   /* choose the cert to start the chain.  shouldn't matter which */
   baseCert = certs[0];

   /* put the rest into a list */
   for (i = 1; i < numCerts; i++) {
      rawList = g_list_append(rawList, certs[i]);
   }

   /* now chase down to a leaf, looking for certs the baseCert issued */
   subject = X509_get_subject_name(baseCert);
   while ((curCert = FindCert(rawList, subject, 0)) != NULL) {
      /* pull it from the list */
      rawList = g_list_remove(rawList, curCert);
      /* set up the next find */
      subject = X509_get_subject_name(curCert);
   }

   /*
    * walk up to the root cert, by finding a cert where the
    * issuer equals the subject of the current
    */
   issuer = X509_get_issuer_name(baseCert);
   while ((curCert = FindCert(rawList, issuer, 1)) != NULL) {
      /* pull it from the list */
      rawList = g_list_remove(rawList, curCert);
      /* set up the next find */
      issuer = X509_get_issuer_name(curCert);
   }

   /*
    * At this point, anything on the list should be certs that are not part
    * of the chain that includes the original 'baseCert'.
    *
    * For a valid token, the list should be empty.
    */
   chainLen = g_list_length(rawList);
   if (chainLen != 0 ) {
      GList *l;

      g_warning("%s: %d unrelated certs found in list\n",
                __FUNCTION__, chainLen);

      /* debug helper */
      l = rawList;
      while (l != NULL) {
         X509* c = (X509 *) l->data;
         char *s = X509_NAME_oneline(X509_get_subject_name(c), NULL, 0);

         g_debug("%s: unrelated cert subject: %s\n", __FUNCTION__, s);
         free(s);
         l = l->next;
      }

      goto done;
   }

   g_debug("%s: Success!  no unrelated certs found\n", __FUNCTION__);
   err = VGAUTH_E_OK;

done:
   g_list_free(rawList);
   for (i = 0; i < numCerts; i++) {
      X509_free(certs[i]);
   }
   g_free(certs);
   return err;
}
0707010000041D000081A40000000000000000000000016822550500000B67000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/certverify.h /*********************************************************
 * Copyright (C) 2011-2016, 2020, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _CERTVERIFY_H_
#define _CERTVERIFY_H_

/*
 * @file certverify.h
 *
 * Certificate verification support.
 */

#include <glib.h>
#include "VGAuthAuthentication.h"
#include "openssl/opensslv.h"  // For OPENSSL_VERSION_NUMBER.

/* new API from OpenSSL 1.1.0
 *      https://www.openssl.org/docs/manmaster/crypto/EVP_DigestInit.html
 *
 * EVP_MD_CTX_create() and EVP_MD_CTX_destroy() were renamed to
 * EVP_MD_CTX_new() and EVP_MD_CTX_free() in OpenSSL 1.1.
 */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define EVP_MD_CTX_new()        EVP_MD_CTX_create()
#define EVP_MD_CTX_free(x)      EVP_MD_CTX_destroy((x))
#endif /* OpenSSL version < 1.1.0 */


/*
 * XXX Do we still need this?  What other algorithms do SAML tokens use?
 */
typedef int VGAuthHashAlg;
enum {
      /** SHA-2 with a 256-bit output size (also known as SHA-256). */
      VGAUTH_HASH_ALG_SHA256,
};

void CertVerify_Init(void);

gboolean CertVerify_IsWellFormedPEMCert(const char *pemCert);

VGAuthError CertVerify_CertChain(const char *pemLeafCert,
                                  int numUntrustedCerts,
                                  const char **pemUntrustedCertChain,
                                  int numTrustedCerts,
                                  const char **pemTrustedCertChain);

VGAuthError CertVerify_CheckSignatureUsingCert(VGAuthHashAlg hash,
                                               const char *pemCert,
                                               size_t dataLen,
                                               const unsigned char *data,
                                               size_t signatureLen,
                                               const unsigned char *signature);


VGAuthError CertVerify_CheckForUnrelatedCerts(int numCerts,
                                              const char **pemCerts);

gchar * CertVerify_StripPEMCert(const gchar *pemCert);

gchar * CertVerify_CertToX509String(const gchar *pemCert);

gchar * CertVerify_EncodePEMForSSL(const gchar *pemCert);

#endif // _CERTVERIFY_H_
 0707010000041E000081A40000000000000000000000016822550500005EF9000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/i18n.c   /*********************************************************
 * Copyright (c) 2011-2019,2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file i18n.c
 *
 * Implementation of i18n-related functions.
 *
 * Stolen primarily from bora-vmsoft/apps/vmtoolslib/i18n.c
 *
 * Includes chunks of bora/lib/misc/escape.c and bora/lib/dict/dictll.c
 * converted to use glib.
 */

#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#ifdef _WIN32
#include <windows.h>
#endif

#include "VGAuthBasicDefs.h"
#include "VGAuthUtil.h"
#include "i18n.h"

/* These come from msgid.h. See MsgHasMsgID for explanation. */
#define MSG_MAX_ID      128
/* The X hides MSG_MAGIC so it won't appear in the object file. */
#define MSG_MAGICAL(s)  (strncmp(s, MSG_MAGIC"X", MSG_MAGIC_LEN) == 0)

typedef struct MsgCatalog {
   GHashTable *utf8;
} MsgCatalog;


typedef struct MsgState {
   GHashTable *domains;  /* List of text domains. */
   GMutex lock;          /* Mutex to protect shared state. */
} MsgState;


static MsgState *msgState = NULL;



/*
 ******************************************************************************
 * Walk --                                                               */ /**
 *
 * While 'bufIn' points to a byte in 'sentinel', increment it.
 *
 * @param[in] bufIn     The input buffer.
 * @param[in] sentinel  The set of chars to Walk over.
 *
 * @return The incremented buffer.
 *
 ******************************************************************************
 */

static void const *
Walk(void const * const bufIn,
     int const * const sentinel)
{
   char const *buf;

   buf = (char const *)bufIn;
   ASSERT(buf);

   /* Unsigned does matter --hpreg */
   while (sentinel[(unsigned char)*buf]) {
      buf++;
   }

   return buf;
}


/*
XXX document the escaping/unescaping process: rationale for which chars we escape, and how we escape --hpreg
*/


/*
 * The dictionary line format:
 *
 *    <ws> <name> <ws> = <ws> <value> <ws> <comment>
 * or
 *    <ws> <name> <ws> = <ws> " <quoted-value> " <ws> <comment>
 *
 * where
 *    <name> does not contain any whitespace or = or #
 *    <value> does not contain any double-quote or #
 *    <quoted-value> does not contain any double-quote
 *    <comment> begins with # and ends at the end of the line
 *    <ws> is a sequence spaces and/or tabs
 *    <comment> and <ws> are optional
 */


/*
 ******************************************************************************
 * DictLL_UnmarshalLine --                                               */ /**
 *
 * Reads a dict line from the bufSize-byte buffer buf, which holds one or more
 * new-line delimited lines.  The buffer is not necessarily
 * null-terminated.
 *
 * @param[in]  buf     The input buffer.
 * @param[in]  bufsize The size of the input buffer.
 * @param[out] line    The complete line (must be g_free()d).
 * @param[out] name    The name (must be g_free()d).
 * @param[out] value   The value (must be g_free()d).
 *
 * @return The start of the next line, or NULL if at the end of buffer.
 ******************************************************************************
 */

static const char *
DictLL_UnmarshalLine(const char *buf,
                     size_t bufSize,
                     char **line,
                     char **name,
                     char **value)
{
   /* Space and tab --hpreg */
   static int const ws_in[] = {
      0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   };
   /* Everything but NUL, space, tab and pound --hpreg */
   static int const wsp_out[] = {
      0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
      0, 1, 1, 0, 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, 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,
   };
   /* Everything but NUL, space, tab, pound and equal --hpreg */
   static int const wspe_out[] = {
      0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
      0, 1, 1, 0, 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, 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, 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,
   };
   /* Everything but NUL and double quote --hpreg */
   static int const q_out[] = {
      0, 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, 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, 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,
   };
   char const *nBegin;
   char const *nEnd;
   char const *vBegin;
   char const *vEnd;
   char const *tmp;
   char *myLine;
   char *myName;
   char *myValue;
   const char *lineEnd;
   const char *nextLine;

   ASSERT(buf);
   ASSERT(line);
   ASSERT(name);
   ASSERT(value);

   /* Check for end of buffer. */
   if (bufSize == 0) {
      *line = NULL;
      *name = NULL;
      *value = NULL;
      return NULL;
   }

   /* Find end of this line, beginning of next. */
   lineEnd = memchr(buf, '\n', bufSize);
   if (lineEnd != NULL) {
      nextLine = lineEnd + 1;
   } else {
      nextLine = lineEnd = buf + bufSize;
   }

   /* Make local copy of line. */
   myLine = g_strndup(buf, lineEnd - buf);

   /* Check if the line is well-formed --hpreg */
   nBegin = Walk(myLine, ws_in);
   nEnd = Walk(nBegin, wspe_out);
   tmp = Walk(nEnd, ws_in);
   if (nBegin == nEnd || *tmp != '=') {
      goto weird;
   }
   tmp++;
   tmp = Walk(tmp, ws_in);
   if (*tmp == '"') {
      tmp++;
      vBegin = tmp;
      vEnd = Walk(vBegin, q_out);
      tmp = vEnd;
      if (*tmp != '"') {
         goto weird;
      }
      tmp++;
   } else {
      vBegin = tmp;
      vEnd = Walk(vBegin, wsp_out);
      tmp = vEnd;
   }
   tmp = Walk(tmp, ws_in);
   if (*tmp != '\0' && *tmp != '#') {
      goto weird;
   }

   /* The line is well-formed. Extract the name and value --hpreg */

   myName = g_strndup(nBegin, nEnd - nBegin);
   myValue = g_strndup(vBegin, vEnd - vBegin);
   ASSERT(myValue);

   *line = myLine;
   *name = myName;
   *value = myValue;

   return nextLine;

weird:
   /* The line is not well-formed. Let the upper layers handle it --hpreg */

   *line = myLine;
   *name = NULL;
   *value = NULL;

   return nextLine;
}


/*
 ******************************************************************************
 * MsgHasMsgID --                                                        */ /**
 *
 * Check that a string has a message ID. The full "MSG_MAGIC(...)" prefix is
 * required, not just MSG_MAGIC.
 *
 * Copied from msgid.h.
 *
 * @param[in] s    String to check.
 *
 * @return TRUE if the string has a message id.
 *
 ******************************************************************************
 */

static gboolean
MsgHasMsgID(const gchar *s)
{
   return MSG_MAGICAL(s) &&
          *(s += MSG_MAGIC_LEN) == '(' &&
          strchr(s + 1, ')') != NULL;
}


/*
 ******************************************************************************
 * MsgCatalogFree --                                                     */ /**
 *
 * Frees memory allocated for a MsgCatalog structure.
 *
 * @param[in] catalog    The catalog to free.
 *
 ******************************************************************************
 */

static void
MsgCatalogFree(gpointer c)
{
   MsgCatalog *catalog = (MsgCatalog *) c;
   ASSERT(catalog);
   if (catalog->utf8 != NULL) {
      g_hash_table_unref(catalog->utf8);
   }
   g_free(catalog);
}


/*
 ******************************************************************************
 * MsgInitState --                                                       */ /**
 *
 * Initializes the global message state.
 *
 * @param[in] unused   Unused.
 *
 * @return NULL.
 *
 ******************************************************************************
 */

static gpointer
MsgInitState(gpointer unused)
{
   ASSERT(msgState == NULL);
   msgState = g_new0(MsgState, 1);
   g_mutex_init(&msgState->lock);
   return NULL;
}


/*
 ******************************************************************************
 * MsgGetState --                                                        */ /**
 *
 * Get the internal msg state (lazily initialized if needed).
 *
 * @return The message state object.
 *
 ******************************************************************************
 */

static MsgState *
MsgGetState(void)
{
   static GOnce msgStateInit = G_ONCE_INIT;
   g_once(&msgStateInit, MsgInitState, NULL);
   return msgState;
}


/*
 ******************************************************************************
 * MsgGetCatalog --                                                      */ /**
 *
 * Retrives the message catalog for a specific domain. This function is not
 * thread-safe, so make sure calls to it are properly protected.
 *
 * @param[in] domain   The domain name (NULL for default).
 *
 * @return The catalog. If a specific catalog is not found, the default one
 *         is returned (which may be NULL).
 *
 ******************************************************************************
 */

static MsgCatalog *
MsgGetCatalog(const char *domain)
{
   MsgState *state = MsgGetState();
   MsgCatalog *catalog = NULL;

   ASSERT(domain != NULL);

   if (state->domains != NULL) {
      catalog = g_hash_table_lookup(state->domains, domain);
   }

   return catalog;
}


/*
 ******************************************************************************
 * MsgGetUserLanguage --                                                 */ /**
 *
 * Returns a string describing the user's default language using the
 * "language[_territory]" format (ISO 639-1 and ISO 3166-1, respectively) as
 * described in the setlocale(3) man page.
 *
 * @return Language code (caller should free).
 *
 ******************************************************************************
 */

static gchar *
MsgGetUserLanguage(void)
{
   gchar *lang;

#if defined(_WIN32)
   /*
    * Windows implementation. Derive the ISO names from the user's current
    * locale.
    */
   wchar_t ctryName[10]; /* MSDN says: max is nine characters + terminator. */
   wchar_t langName[10]; /* MSDN says: max is nine characters + terminator. */

   if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME,
                      ctryName, (sizeof(ctryName)/sizeof(langName[0]))) == 0 ||
       GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME,
                      langName, (sizeof(langName)/sizeof(langName[0]))) == 0) {
      g_warning("Couldn't retrieve user locale data, error = %u.", GetLastError());
      lang = g_strdup("C");
   } else {
      lang = g_strdup_printf("%S_%S", langName, ctryName);
   }
#else
   /*
    * POSIX implementation: just use setlocale() to query the data. Ignore any
    * codeset information.
    */
   char *tmp;
   /*
    * This is useful for testing, and also seems to be used by some
    * distros (NeoKylin) rather than the setlocale() APIs.
    * See PR 1672149
    */
   char *envLocale = getenv("LANG");
   if (envLocale != NULL) {
      lang = g_strdup(envLocale);
      g_debug("%s: Using LANG override of '%s'\n", __FUNCTION__, lang);
      return lang;
   }
   tmp = setlocale(LC_MESSAGES, NULL);
   if (tmp == NULL) {
      lang = g_strdup("C");
   } else {
      char *dot;

      lang = g_strdup(tmp);
      dot = strchr(lang, '.');
      if (dot != NULL) {
         *dot = '\0';
      }
   }
#endif

   return lang;
}


/*
 ******************************************************************************
 * MsgSetCatalog --                                                      */ /**
 *
 * Set the message catalog for a given domain. A NULL catalog clears the
 * current one. This function is not thread-safe, so make sure calls to it
 * are properly protected.
 *
 * @param[in] domain    The text domain being bound.
 * @param[in] catalog   The new message catalog.
 *
 ******************************************************************************
 */

static void
MsgSetCatalog(const char *domain,
              MsgCatalog *catalog)
{
   MsgState *state = MsgGetState();

   ASSERT(domain);

   if (state->domains == NULL) {
      state->domains = g_hash_table_new_full(g_str_hash,
                                             g_str_equal,
                                             g_free,
                                             MsgCatalogFree);
   }

   g_hash_table_insert(state->domains, g_strdup(domain), catalog);
}


/*
 ******************************************************************************
 * MsgLoadCatalog --                                                     */ /**
 *
 * Loads the message catalog at the given path into a new hash table.
 * The catalog entries are a simple <key> = <value>.  Line continutatiom
 * is not supported.
 *
 * @param[in] path    Path containing the message catalog (encoding should be
 *                    UTF-8).
 *
 * @return A new message catalog on success, NULL otherwise.
 *
 ******************************************************************************
 */

static MsgCatalog *
MsgLoadCatalog(const char *path)
{
   gchar *localPath;
   GError *err = NULL;
   GIOChannel *stream;
   gboolean error = FALSE;
   MsgCatalog *catalog = NULL;
   GHashTable *dict;

   localPath = GET_FILENAME_LOCAL(path, NULL);
   ASSERT(localPath != NULL);

   stream = g_io_channel_new_file(localPath, "r", &err);
   g_debug("%s: loading message catalog '%s'\n", __FUNCTION__, localPath);
   RELEASE_FILENAME_LOCAL(localPath);

   if (err != NULL) {
      g_debug("Unable to open '%s': %s\n", path, err->message);
      g_clear_error(&err);
      return NULL;
   }

   dict = g_hash_table_new_full(g_str_hash,
                                g_str_equal,
                                g_free,
                                g_free);
   for (;;) {
      char *name = NULL;
      char *value = NULL;
      gchar *line;
      gsize len;
      gsize term;
      char *unused = NULL;

      /* Read the next key / value pair. */

      g_io_channel_read_line(stream, &line, &len, &term, &err);

      if (err != NULL) {
         g_warning("Unable to read a line from '%s': %s\n",
                   path, err->message);
         g_clear_error(&err);
         error = TRUE;
         g_free(line);
         break;
      }

      if (line == NULL) {
         /* This signifies EOF. */
         break;
      }

      /*
       * Fix the line break to always be Unix-style, to make lib/dict
       * happy.
       */
      if (line[term] == '\r') {
         line[term] = '\n';
         if (len > term) {
            line[term + 1] = '\0';
         }
      }

      /*
       * Try to parse the string using the dictionary library.
       */
      if (DictLL_UnmarshalLine(line, len, &unused, &name, &value) == NULL) {
         g_warning("Couldn't parse line from catalog: %s", line);
         error = TRUE;
      }
      g_free(unused);
      g_free(line);

      if (error) {
         /*
          * If the local DictLL_UnmarshalLine() returns NULL, name and value
          * will remain NULL pointers.  No malloc'ed memory to free here.
          */
         /* coverity[leaked_storage] */
         break;
      }

      if (name != NULL) {
         gchar *val;
         ASSERT(value);

         if (!g_utf8_validate(name, -1, NULL) ||
             !g_utf8_validate(value, -1, NULL)) {
            g_warning("Invalid UTF-8 string in message catalog (key = %s)\n", name);
            error = TRUE;
            g_free(name);
            g_free(value);
            break;
         }

         // remove any escaped chars
         val = g_strcompress(value);
         g_free(value);

         // the hashtable takes ownership of the memory for 'name' and 'val'
         g_hash_table_insert(dict, name, val);
      }
   }

   g_io_channel_unref(stream);

   if (error) {
      g_hash_table_unref(dict);
      dict = NULL;
   } else {
      catalog = g_new0(MsgCatalog, 1);
      catalog->utf8 = dict;
   }

   return catalog;
}


/*
 ******************************************************************************
 * I18n_BindTextDomain --                                                */ /**
 *
 * Loads the message catalog for a text domain. Each text domain contains a
 * different set of messages loaded from a different catalog.
 *
 * If a catalog has already been bound to the given name, it is replaced with
 * the newly loaded data.
 *
 * @param[in] domain   Name of the text domain being loaded.
 * @param[in] lang     Language code for the text domain.
 * @param[in] catdir   Root directory of catalog files (NULL = default).
 *
 ******************************************************************************
 */

void
I18n_BindTextDomain(const char *domain,
                    const char *lang,
                    const char *catdir)
{
   gchar *file;
   gchar *usrlang = NULL;
   MsgState *state = MsgGetState();
   MsgCatalog *catalog;

   ASSERT(domain);

   /*
    * If the caller has asked for the default user language, detect it and
    * translate to our internal language string representation.
    */

   if (lang == NULL || *lang == '\0') {
      usrlang = MsgGetUserLanguage();
      lang = usrlang;
   } else {
      usrlang = g_strdup(lang);
   }

   /*
    * XXX may want to handle a NULL 'catdir', and look in relative
    * to the installed location.
    */

   g_debug("%s: user locale=%s\n", __FUNCTION__, lang);

   file = g_strdup_printf("%s%smessages%s%s%s%s.vmsg",
                          catdir, DIRSEPS, DIRSEPS, lang, DIRSEPS, domain);

   /*
    * If we couldn't find the catalog file for the user's language, see if
    * there's an encoding to chop off first, eg zh_CN.UTF-8
    */
   if (!g_file_test(file, G_FILE_TEST_IS_REGULAR)) {
      const char *sep = strrchr(lang, '.');
      if (sep != NULL) {
         usrlang[sep - lang] = '\0';
         g_free(file);
         file = g_strdup_printf("%s%smessages%s%s%s%s.vmsg",
                                catdir, DIRSEPS, DIRSEPS, usrlang, DIRSEPS, domain);
      }
   }

   /*
    * If we couldn't find the catalog file for the user's language, see if
    * we can find a more generic language (e.g., for "en_US", also try "en").
    */
   if (!g_file_test(file, G_FILE_TEST_IS_REGULAR)) {
      const char *sep = strrchr(lang, '_');
      if (sep != NULL) {
         usrlang[sep - lang] = '\0';
         g_free(file);
         file = g_strdup_printf("%s%smessages%s%s%s%s.vmsg",
                                catdir, DIRSEPS, DIRSEPS, usrlang, DIRSEPS, domain);
      }
   }

   catalog = MsgLoadCatalog(file);

   if (catalog == NULL) {
      if (strncmp(lang, "en", 2)) {
         /*
          * Don't warn about english dictionary, which may not exist (it is the
          * default translation).
          */
         g_message("Cannot load message catalog for domain '%s', language '%s', "
                   "catalog dir '%s'.\n", domain, lang, catdir);
      }
   } else {
      g_mutex_lock(&state->lock);
      MsgSetCatalog(domain, catalog);
      g_mutex_unlock(&state->lock);
   }
   g_free(file);
   g_free(usrlang);
}


/*
 ******************************************************************************
 * I18n_GetString --                                                     */ /**
 *
 * Retrieves a localized string in the requested encoding.
 *
 * All messages are retrieved based on the catalog data loaded in UTF-8.
 * Strings in other encodings are lazily converted from the UTF-8 version
 * as they are queried.
 *
 * @param[in] domain       Domain from where to fetch the messages.
 * @param[in] msgid        The message id, including the default English text.
 *
 * @return The string in the desired encoding.
 *
 ******************************************************************************
 */

const char *
I18n_GetString(const char *domain,
               const char *msgid)
{
   const char *idp;
   const char *strp;
   char idBuf[MSG_MAX_ID];
   size_t len;
   GHashTable *source = NULL;
   MsgCatalog *catalog;
   MsgState *state = MsgGetState();

   /* All message strings must be prefixed by the message ID. */
   ASSERT(domain != NULL);
   ASSERT(msgid != NULL);
   ASSERT(MsgHasMsgID(msgid));

   /*
    * Find the beginning of the ID (idp) and the string (strp).
    * The string should have the correct MSG_MAGIC(...)... form.
    */

   idp = msgid + MSG_MAGIC_LEN + 1;
   strp = strchr(idp, ')') + 1;

   len = strp - idp - 1;
   ASSERT(len <= MSG_MAX_ID - 1);
   memcpy(idBuf, idp, len);
   idBuf[len] = '\0';

   g_mutex_lock(&state->lock);

   catalog = MsgGetCatalog(domain);
   if (catalog != NULL) {
      source = catalog->utf8;
   }

   if (source != NULL) {
      const void *retval = g_hash_table_lookup(source, idBuf);
      if (NULL != retval) {
         strp = retval;
      }
   }

   g_mutex_unlock(&state->lock);

   return strp;
}

   0707010000041F000081A40000000000000000000000016822550500000978000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/i18n.h   /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _I18N_H_
#define _I18N_H_

/**
 * @file i18n.h
 *
 * @brief Functions and macros to help with internationalization of applications.
 *
 * To create a localizable string, use the MSGID macro in the following manner:
 *
 * @code
 *    I18n_GetString("domain", MSGID(message.id) "Default English text.")
 * @endcode
 *
 * Or, in shorthand form:
 *
 * @code
 *    SU_(message.id, "Default English text.")
 * @endcode
 *
 * This will instruct the code to retrive the message under key "message.id"
 * in the translation catalog for the configured locale.
 *
 * The shorthand macros use the VMW_TEXT_DOMAIN macro to identify the domain
 * from which translated messages will be loaded. Each domain should first be
 * initialized by calling I18n_BindTextDomain().
 */

#include <glib.h>

/*
 * Copied from msgid.h to avoid exposing VMware internal headers. Don't
 * change these values. Ever.
 */
#define MSG_MAGIC       "@&!*@*@"
#define MSG_MAGIC_LEN   7
#define MSGID(id)       MSG_MAGIC "(" #id ")"

/**
 * Shorthand macro to retrieve a localized message in UTF-8.
 *
 * @param[in]  msgid    The message ID.
 * @param[in]  en       English version of the message.
 *
 * @return A localized message.
 */
#define SU_(msgid, en) I18n_GetString(VMW_TEXT_DOMAIN, MSGID(msgid) en)

G_BEGIN_DECLS

void
I18n_BindTextDomain(const char *domain,
                    const char *locale,
                    const char *catdir);

const char *
I18n_GetString(const char *domain,
               const char *msgid);

G_END_DECLS


#endif /* _I18N_H_ */

07070100000420000081A4000000000000000000000001682255050000226C000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/prefs.c  /*********************************************************
 * Copyright (C) 2011-2016,2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file prefs.c --
 *
 *    Code to support preferences controlling the service and library.
 */

#include "VGAuthLog.h"
#include "VGAuthUtil.h"
#include "prefs.h"
#ifdef _WIN32
#include "winUtil.h"
#endif


/*
 ******************************************************************************
 * Pref_Init --                                                          */ /**
 *
 * Intializes the preferences.
 *
 * @param[in] configFilename  The name of the config file.
 *
 * @return A new PrefHandle.
 *
 ******************************************************************************
 */

PrefHandle
Pref_Init(const gchar *configFilename)
{
   GError *gErr = NULL;
   GKeyFile *keyFile;
   char *fileName = NULL;
   PrefHandle ph;

   /*
    * For Windows, check the registry for the value, and fallback to arg.
    */
#ifdef _WIN32
   fileName = WinUtil_ReadPrefsString(VGAUTH_REGISTRY_KEY,
                                      VGAUTH_REGISTRY_PREFFILE);
#endif

   if (!fileName) {
      fileName = g_strdup(configFilename);
   }
   g_message("%s: Using '%s' as preferences filepath\n",
             __FUNCTION__, fileName);

   keyFile = g_key_file_new();
   if (!g_key_file_load_from_file(keyFile,
                                  fileName,
                                  G_KEY_FILE_NONE,
                                  &gErr)) {
      g_debug("%s: g_key_file_load_from_file(%s) failed: %s\n", __FUNCTION__,
              fileName, gErr->message);
      g_error_free(gErr);
      gErr = NULL;
   }

   ph = g_malloc(sizeof(struct _PrefHandle));
   ph->fileName = fileName;
   ph->keyFile = keyFile;

   return ph;
}


/*
 ******************************************************************************
 * Pref_Shutdown --                                                      */ /**
 *
 * Closes a PrefHandle.
 *
 * @param[in] ph  The PrefHandle to close.
 *
 ******************************************************************************
 */

void
Pref_Shutdown(PrefHandle ph)
{
   g_key_file_free(ph->keyFile);
   g_free(ph->fileName);
   g_free(ph);
}


/*
 ******************************************************************************
 * Pref_GetString --                                                     */ /**
 *
 * Returns a string from the pref file.
 *
 * @param[in] ph             The handle to the preferences.
 * @param[in] prefName       The name of the pref.
 * @param[in] groupName      The config group to check.
 * @param[in] defaultValue   The default value of the pref.
 *
 * @return The value in the config file if set, otherwise defaultValue.
 *            Must be gfree'd by caller.
 *
 ******************************************************************************
 */

gchar *
Pref_GetString(PrefHandle ph,
               const gchar *prefName,
               const gchar *groupName,
               const gchar *defaultVal)
{
   gchar *retVal;
   GError *gErr = NULL;
   GKeyFile *keyFile = ph->keyFile;

   ASSERT(keyFile);
   retVal = g_key_file_get_string(keyFile,
                                  groupName,
                                  prefName,
                                  &gErr);
   if ((NULL == retVal) && (NULL != gErr)) {
      g_debug("%s: Pref_GetString(%s) failed: %s\n", __FUNCTION__,
              prefName, gErr->message);
      g_error_free(gErr);
      retVal = g_strdup(defaultVal);
   } else {
      /* Remove any trailing whitespace. */
      g_strchomp(retVal);
   }

   return retVal;
}


/*
 ******************************************************************************
 * Pref_GetInt --                                                        */ /**
 *
 * Returns an int from the pref file.
 *
 * @param[in] ph             The handle to the preferences.
 * @param[in] prefName   The name of the pref.
 * @param[in] groupName      The config group to check.
 * @param[in] defaultValue   The default value of the pref.
 *
 * @return The value in the config file if set, otherwise defaultValue.
 *
 ******************************************************************************
 */

int
Pref_GetInt(PrefHandle ph,
            const gchar *prefName,
            const gchar *groupName,
            int defaultVal)
{
   int retVal;
   GKeyFile *keyFile = ph->keyFile;
   GError *gErr = NULL;

   ASSERT(keyFile);
   retVal = g_key_file_get_integer(keyFile,
                                   groupName,
                                   prefName,
                                   &gErr);
   if ((0 == retVal) && (NULL != gErr)) {
      g_debug("%s: Pref_GetInt(%s) failed: %s\n", __FUNCTION__,
              prefName, gErr->message);
      g_error_free(gErr);
      retVal = defaultVal;
   }

   return retVal;
}


/*
 ******************************************************************************
 * Pref_GetBool --                                                       */ /**
 *
 * Returns a bool from the pref file.
 *
 * @param[in] ph             The handle to the preferences.
 * @param[in] prefName   The name of the pref.
 * @param[in] groupName      The config group to check.
 * @param[in] defaultValue   The default value of the pref.
 *
 * @return The value in the config file if set, otherwise defaultValue.
 *
 ******************************************************************************
 */

gboolean
Pref_GetBool(PrefHandle ph,
             const gchar *prefName,
             const gchar *groupName,
             gboolean defaultVal)
{
   gboolean retVal;
   GKeyFile *keyFile = ph->keyFile;
   GError *gErr = NULL;

   ASSERT(keyFile);
   retVal = g_key_file_get_boolean(keyFile,
                                   groupName,
                                   prefName,
                                   &gErr);
   if (!retVal && (NULL != gErr)) {
      g_debug("%s: Pref_GetBool(%s) failed: %s\n", __FUNCTION__,
                prefName, gErr->message);
      g_error_free(gErr);
      retVal = defaultVal;
   }

   return retVal;
}


/*
 ******************************************************************************
 * Pref_LogAllEntries --                                                 */ /**
 *
 * Logs the full contents of the prefs.  Useful for debugging.
 *
 * @param[in] ph  The PrefHandle to dunp.
 *
 ******************************************************************************
 */

void
Pref_LogAllEntries(const PrefHandle ph)
{
   gchar **groupNames = NULL;
   gsize numGroups;
   int i;
   gchar **keyNames = NULL;
   gchar *value = NULL;
   int j;
   gsize numKeys;
   GError *gErr = NULL;
   GKeyFile *keyFile = ph->keyFile;

   groupNames = g_key_file_get_groups(keyFile, &numGroups);
   g_message("%s: %d preference groups in file '%s'\n",
             __FUNCTION__, (int) numGroups, ph->fileName);
   for (i = 0; i < (int) numGroups; i++) {
      g_message("Group '%s'\n", groupNames[i]);
      keyNames = g_key_file_get_keys(keyFile,
                                     groupNames[i],
                                     &numKeys,
                                     &gErr);
      if (NULL != gErr) {
         g_warning("%s: g_key_file_get_keys(%s) failed: %s\n",
                   __FUNCTION__, groupNames[i], gErr->message);
         g_error_free(gErr);
         gErr = NULL;
         continue;
      }
      for (j = 0; j < (int) numKeys; j++) {
         value = g_key_file_get_value(keyFile, groupNames[i],
                                      keyNames[j],
                                      &gErr);
         if ((NULL == value) && (NULL != gErr)) {
            g_warning("%s: g_key_file_get_value(%s:%s) failed: %s\n",
                      __FUNCTION__, groupNames[i], keyNames[j], gErr->message);
            g_error_free(gErr);
            gErr = NULL;
            continue;
         }
         g_message("\t %s=%s\n", keyNames[j], value);
         g_free(value);
      }
      g_strfreev(keyNames);
   }
   g_message("%s: End of preferences\n", __FUNCTION__);
   g_strfreev(groupNames);
}
07070100000421000081A40000000000000000000000016822550500001A92000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/prefs.h  /*********************************************************
 * Copyright (C) 2011-2019,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file prefs.h --
 *
 *    Service preference definitions.
 *
 *    @addtogroup vgauth_prefs
 *    @{
 */

#ifndef _PREFS_H_
#define _PREFS_H_

#include <glib.h>


typedef struct _PrefHandle {
   GKeyFile *keyFile;
   char *fileName;
} *PrefHandle;

PrefHandle Pref_Init(const gchar *configFilename);
void Pref_Shutdown(PrefHandle ph);
gchar *Pref_GetString(PrefHandle ph,
                      const gchar *prefName,
                      const gchar *groupName,
                      const char *defaultVal);
int Pref_GetInt(PrefHandle ph,
                const gchar *prefName,
                const gchar *groupName,
                int defaultVal);
gboolean Pref_GetBool(PrefHandle ph,
                      const gchar *prefName,
                      const gchar *groupName,
                      gboolean defaultVal);

void Pref_LogAllEntries(const PrefHandle ph);


/*
 * Location of the prefs file.  Windows expects to find it in the
 * registry.
 */
#ifdef _WIN32
#define VGAUTH_REGISTRY_KEY "SOFTWARE\\VMware, Inc.\\VMware VGAuth"
#define VGAUTH_REGISTRY_PREFFILE "PreferencesFile"
// fallback value if registry isn't set
#define VGAUTH_PREF_CONFIG_FILENAME "c:\\Program Files\\VMware\\VMware Tools\\vgauth.conf"
#else
#define VGAUTH_PREF_CONFIG_FILENAME "/etc/vmware-tools/vgauth.conf"
// XXX temp til installer tweaks its location
#define VGAUTH_PREF_CONFIG_FILENAME_OLD "/etc/vmware/vgauth.conf"
#endif

/*
 * Preferences
 *
 * Glib configuration is similar to Windows .ini files.
 *
 * It uses a simple name=value syntax, with preferences separated by groups.
 * Groupnames are delineated with '[' and ']'.
 * Unlike Windows, glib requires at least one group.
 *
 * @verbatim
# Sample configuration
# Note -- do not use '"'s around strings, they will be treated as part
# of the string.  Also be sure to use a double '\' in Windows filenames,
# since '\' is the escape character, eg c:\\Program Files\\VMware\\schemas"
# String values must be in UTF8.
#
[service]
logfile=/tmp/log.out
samlSchemaDir=/usr/lib/vmware-vgauth/schemas
aliasStoreDir=/var/lib/vmware/VGAuth/aliasStore
loglevel=normal
enableLogging=true
enableCoreDumps=true
clockSkewAdjustment = 300

[ticket]
ticketTTL=3600

[auditing]
auditSuccessEvents=true

[localization]
msgCatalog = /etc/vmware-tools/vgauth/messages
# EOF
 @endverbatim
 * See http://developer.gnome.org/glib/2.28/glib-Key-value-file-parser.html#glib-Key-value-file-parser.description
 */

/** Service group name. */
#define VGAUTH_PREF_GROUP_NAME_SERVICE     "service"

/*
 * Pref names
 */
/** Whether to log to a file. */
#define VGAUTH_PREF_LOGTOFILE              "enableLogging"
/** Whether to allow core dumps. */
#define VGAUTH_PREF_ALLOW_CORE             "enableCoreDumps"
/** The location of the logfile. */
#define VGAUTH_PREF_NAME_LOGFILE           "logfile"
/** The logging level. */
#define VGAUTH_PREF_NAME_LOGLEVEL          "loglevel"
/** Maxiumum number of old log files to be kept. */
#define VGAUTH_PREF_NAME_MAX_OLD_LOGFILES  "maxOldLogFiles"
/** Maxiumum size in MB of each log file. */
#define VGAUTH_PREF_NAME_MAX_LOGSIZE       "maxLogSize"
/** Number of seconds a specific user's listen connection will go unreferenced until it is discarded. */
#define VGAUTH_PREF_NAME_LISTEN_TTL        "listenTTL"
/** Maximum number of data connections allowed for a non privileged user */
#define VGAUTH_PREF_NAME_MAX_DATA_CONNECTIONS_PER_USER  \
   "maxDataConnectionsPerUser"
/** Where the XML schema files used for SAML parsing were installed. */
#define VGAUTH_PREF_SAML_SCHEMA_DIR        "samlSchemaDir"
/** The location of the idstore */
#define VGAUTH_PREF_ALIASSTORE_DIR         "aliasStoreDir"
/** The number of seconds slack allowed in either direction in SAML token date checks. */
#define VGAUTH_PREF_CLOCK_SKEW_SECS        "clockSkewAdjustment"
/** If unrelated certificates are allowed in a SAML token */
#define VGAUTH_PREF_ALLOW_UNRELATED_CERTS  "allowUnrelatedCerts"

/** Ticket group name. */
#define VGAUTH_PREF_GROUP_NAME_TICKET      "ticket"

/** Number of seconds a ticket will go unreferenced until it is discarded. */
#define VGAUTH_PREF_NAME_TICKET_TTL        "ticketTTL"

/** Auditing group name. */
#define VGAUTH_PREF_GROUP_NAME_AUDIT       "auditing"

/** Whether to generate audit events for successful operations. */
#define VGAUTH_PREF_AUDIT_SUCCESS          "auditSuccessEvents"

/** SSPI group name. */
#define VGAUTH_PREF_GROUP_NAME_SSPI        "sspi"

/**
 * Number of seconds within which a SSPI authentication handshake must be
 * completed or it is discarded. Default is ten minutes.
 */
#define VGAUTH_PREF_NAME_SSPI_HANDSHAKE_TTL "sspiHandshakeTTL"


/** Localization group name. */
#define VGAUTH_PREF_GROUP_NAME_LOCALIZATION        "localization"

/** Where the localized version of the messages were installed. */
#define VGAUTH_PREF_LOCALIZATION_DIR        "msgCatalog"

/** If symlinks or junctions are allowed in alias store file path */
#define VGAUTH_PREF_ALLOW_SYMLINKS  "allowSymlinks"

/*
 * Pref values
 */

/** Normal logging level; informational messages and errors. */
#define SERVICE_LOGLEVEL_NORMAL             "normal"
/** Normal logging level plus debug messages. */
#define SERVICE_LOGLEVEL_VERBOSE            "verbose"
#define SERVICE_LOGLEVEL_DEBUG              "debug"

/** @} */

/*
 * Default values for the preferences. These should be defined next to the
 * preference name they refer to, but then they would show up in the generated
 * documentation.
 */

#define VGAUTH_PREF_DEFAULT_SSPI_HANDSHAKE_TTL (10 * 60)

/*
 * Parent directory of 'messages', which has <lang>/<app>.vmsg
 * below that.
 */
#ifdef _WIN32
#define VGAUTH_PREF_DEFAULT_LOCALIZATION_CATALOG "."
#else
#define VGAUTH_PREF_DEFAULT_LOCALIZATION_CATALOG "/etc/vmware-tools"
#endif

#define VGAUTH_PREF_DEFAULT_MAX_DATA_CONNECTIONS_PER_USER 5

#define VGAUTH_PREF_DEFAULT_CLOCK_SKEW_SECS (300)

#endif // _PREFS_H_

  07070100000422000081A4000000000000000000000001682255050000371B000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/usercheck.c  /*********************************************************
 * Copyright (C) 2011-2016,2019,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file usercheck.c --
 *
 *    Helper functions to check user existence.
 */

#include "usercheck.h"
#include "VGAuthLog.h"

#ifndef _WIN32
#include <stdio.h>
#include <errno.h>
#endif

#ifdef _WIN32
#include <Lm.h>
#pragma comment(lib, "netapi32.lib")
#endif

#include <string.h>

/*
 * XXX
 *
 * Lost connection issue with LDAP.
 *
 * On my dev Linux box (Redhat 5.6), the underlying username lookup
 * code will get an LDAP connection and hold it.  But the LDAP server
 * supports an 'idletimeout' feature, where it kicks off clients
 * that stop talking to it.  For me, that looks to be 3-4 minutes.
 * On the next username resolution attempt, the client side discovers
 * the TCP connection is gone when a send() fails.  But instead of
 * reconnecting and retrying, the underlying code just returns the EBADF
 * it got from the failed send().  But the next call works fine, since the
 * client code knows its connection is gone and reestablishes it.
 *
 *
 * The end result of this is that a user can do a VGAuth operation,
 * wait 4 minutes, try again, and be told the user doesn't exist.
 *
 *
 * Timeouts are controlled on the LDAP server side, and while they
 * aren't on by default for OpenLDAP, they're probably going to be on in
 * many cases, since otherwise the poor LDAP server can have huge numbers of
 * idle connections sucking resources.  So we can't expect customers
 * to not have timeouts.
 *
 * Another posible fix would be to essentially send our own keep-alives,
 * but this puts that stress back on the LDAP server.
 *
 * Another solution is to add "nss_connect_policy oneshot" to
 * /etc/ldap.conf.  This tells the client code to not keep its
 * connection open.  But we can't expect a customer to fix things
 * by changing their LDAP configuration.
 *
 * So the safe fix is to do the retry at our layer.
 *
 *
 * XXX Right now this is just on for Linux.  We may need it for
 * Solaris as well, but that path is untested.
 */

#define MAX_USER_NAME_LEN 256

/*
 * A single retry works for the LDAP case, but try more often in case NIS
 * or something else has a related issue.  Note that a bad username/uid won't
 * give the EBADF so we won't retry 'expected' failures.
 */
#define MAX_RETRIES  5

#ifndef _WIN32
/*
 ******************************************************************************
 * UsercheckLookupUser --                                                */ /**
 *
 * Returns the uid/gid of userName.
 * XXX locale issue lurking here.
 *
 * @param[in]   userName        The userName to look up.
 * @param[out]  uid             The uid of userName.
 * @param[out]  gid             The gid of userName.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
UsercheckLookupUser(const gchar *userName,
                    uid_t *uid,
                    gid_t *gid)
{
   struct passwd pw;
   struct passwd *ppw = &pw;
   char buffer[BUFSIZ];
#ifndef sun
   int ret;
   int retryCount = 0;

   /*
    * XXX Retry kludge -- see above.
    */
retry:
   if ((ret = getpwnam_r(userName, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
                         !ppw) {
      if ((EBADF == errno) && (++retryCount < MAX_RETRIES)) {
         g_debug("%s: getpwnam_r(%s) failed %d (%d), try #%d\n",
                 __FUNCTION__, userName, ret, errno, retryCount);
         goto retry;
      }
      return VGAUTH_E_NO_SUCH_USER;
   }
#else
   if ((ppw = getpwnam_r(userName, &pw, buffer, sizeof buffer)) == NULL) {
      return VGAUTH_E_NO_SUCH_USER;
   }
#endif

   *uid = ppw->pw_uid;
   *gid = ppw->pw_gid;

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * UsercheckLookupUid --                                                 */ /**
 *
 * Returns the username matching uid.
 *
 * @param[in]  uid             The uid to look up.
 * @param[out] userName        The userName of uid.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
UsercheckLookupUid(uid_t uid,
                   gchar **userName)
{
   struct passwd pw;
   struct passwd *ppw = &pw;
   char buffer[BUFSIZ];
#ifndef sun
   int error;
   int retryCount = 0;

   /*
    * XXX Retry kludge -- see above.
    */
retry:
   if ((error = getpwuid_r(uid, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      /*
       * getpwuid_r() and getpwnam_r() can return a 0 (success) but not
       * set the return pointer (ppw) if there's no entry for the user,
       * according to POSIX 1003.1-2003.
       */
      if ((EBADF == errno) && (++retryCount < MAX_RETRIES)) {
         g_debug("%s: getpwuid_r(%d) failed %d (%d), try #%d\n",
                 __FUNCTION__, uid, error, errno, retryCount);
         goto retry;
      }
      return VGAUTH_E_NO_SUCH_USER;
   }
#else
   if ((ppw = getpwuid_r(uid, &pw, buffer, sizeof buffer)) == NULL) {
      return VGAUTH_E_NO_SUCH_USER;
   }
#endif

   // XXX locale issue lurking here
   *userName = g_strdup(ppw->pw_name);

   return VGAUTH_E_OK;
}
#endif   // !_WIN32


/*
 ******************************************************************************
 * UsercheckIDCheckUserExists --                                         */ /**
 *
 * Checks to see if user exists in OS.
 *
 * @param[in]   userName        The userName to look up.
 *
 * @return TRUE if exists, FALSE if not.
 *
 ******************************************************************************
 */

gboolean
UsercheckUserExists(const gchar *userName)
{
   gboolean result = TRUE;
#ifdef _WIN32
   PSID pSidUser = WinUtil_LookupSid(userName);

   if (!pSidUser) {
      result = FALSE;
   } else {
      g_free(pSidUser);
   }

#else
   uid_t uid;
   gid_t gid;
   VGAuthError err;

   err = UsercheckLookupUser(userName, &uid, &gid);
   if (VGAUTH_E_OK != err) {
      result = FALSE;
   }
#endif
   return result;
}


/*
 ******************************************************************************
 * Usercheck_CompareByName --                                            */ /**
 *
 * Determines whether two usernames refer to the same logical account.
 *
 * @param[in]  u1    A username in UTF-8.
 * @param[in]  u2    A username in UTF-8 to compare with u1.
 *
 ******************************************************************************
 */

gboolean
Usercheck_CompareByName(const char *u1,
                        const char *u2)
{
   gboolean res = FALSE;

#ifdef _WIN32
   PSID sid1;
   PSID sid2 = NULL;

   /*
    * Usernames in Windows are case-insensitive. However, doing a UTF-8
    * friendly case-insensitive comparison is complex and expensive (see
    * g_utf8_casefold()), so just look-up the SIDs for each name and compare
    * those.
    * TODO: Does this cause any issues in cases where the network is down?
    */

   sid1 = WinUtil_LookupSid(u1);
   if (NULL == sid1) {
      goto done;
   }

   sid2 = WinUtil_LookupSid(u2);
   if (NULL == sid2) {
      goto done;
   }

   res = EqualSid(sid1, sid2) != 0;

done:
   g_free(sid2);
   g_free(sid1);
#else
   if (g_strcmp0(u1, u2) == 0) {
      res = TRUE;
   } else {
      uid_t uid1, uid2;
      gid_t dummy;

      /*
       * On Linux, it is possible to have more than one username refer to
       * the same UID, and thus the same user. So, the right way to check
       * is to look up the UIDs for each name and compare those.
       */

      if (UsercheckLookupUser(u1, &uid1, &dummy) != VGAUTH_E_OK) {
         goto done;
      }

      if (UsercheckLookupUser(u2, &uid2, &dummy) != VGAUTH_E_OK) {
         goto done;
      }

      res = uid1 == uid2;

   done:
      ;
   }
#endif

   return res;
}


/*
 ******************************************************************************
 * Usercheck_UsernameIsLegal --                                          */ /**
 *
 * Checks to see if the userName contains any illegal characters.
 *
 * @param[in]   userName        The userName to check.
 *
 * @return TRUE if its legal.
 *
 ******************************************************************************
 */

gboolean
Usercheck_UsernameIsLegal(const gchar *userName)
{
   /*
    * This catches the stuff that will upset the fileSystem when the
    * username is used as part of the aliasStore filename.  Note
    * that this is not a complete list.
    *
    * Different Linux distros seem to add additonal restrictions. QE has found
    * the following are legal chars in usernames:
    *
    * - Windows:        _!(){}$%^&'
    * - Ubuntu 12.04:   _.+-
    * - Rhel 6.1:       _.-
    *
    * Note that Rhel has restricions beyond Ubuntu.
    *
    * The illegal character list appears to be:
    *
    * Windows      /\@[]:;|=,+*?<>"
    * Ubuntu       /\[]:;|=,*<>"!(){}?$@%^&'
    * Rhel         /\[]:;|=,*<>"!(){}?$@%^&'+
    *
    * Note that '\' is valid with a domain username; this is the
    * restricted list for local usernames.
    */
   size_t len;
   size_t i = 0;
   int backSlashCnt = 0;
   /*
    * As user names are used to generate its alias store file name/path, it
    * should not contain path traversal characters ('/' and '\').
    */
   char *illegalChars = "<>/\\";

   len = strlen(userName);
   if (len > MAX_USER_NAME_LEN) {
      return FALSE;
   }

   while ((i += strcspn(userName + i, illegalChars)) < len) {
      /*
       * One backward slash is allowed for domain\username separator.
       */
      if (userName[i] != '\\' || ++backSlashCnt > 1) {
         return FALSE;
      }
      ++i;
   }

   return TRUE;
}


#ifdef _WIN32
/*
 ******************************************************************************
 * Usercheck_IsAdminMember --                                          */ /**
 *
 * Checks to see if the userName is a member of the Admninistrator group.
 *
 * This is currently written to support only the local Administrator
 * group.
 *
 * @param[in]   userName        The userName to check.
 *
 * @return TRUE if its legal.
 *
 ******************************************************************************
 */

gboolean
Usercheck_IsAdminMember(const gchar *userName)
{
   NET_API_STATUS status;
   gunichar2 *userNameW = NULL;
   LOCALGROUP_MEMBERS_INFO_1 *groupList;
   DWORD entriesRead;
   DWORD totalEntries;
   DWORD i;
   gboolean bRetVal = FALSE;
   BOOL ok;
   DWORD lastError;
   WCHAR *accountW = NULL;
   WCHAR *domainW = NULL;
   DWORD accountChar2Needed = 0;
   DWORD domainChar2Needed = 0;
   SID_NAME_USE eUse;
   PSID pSid;

   /*
    * XXX Should this cache some (all?) of the returned data for a perf boost?
    * Or does that open up bugs (security or other) where it might
    * change while the service is running?  The name of the group changing
    * seems unlikely; member changing less so.
    */

   /*
    * To avoid localization issues, start with the Administrators group's SID,
    * and find the name to pass to NetLocalGroupGetMembers to get
    * the group members.
    */
   pSid = WinUtil_GroupAdminSid();

   ok = LookupAccountSidW(NULL, pSid, NULL, &accountChar2Needed,
                          NULL, &domainChar2Needed, &eUse);
   ASSERT(!ok);
   lastError = GetLastError();
   if (lastError != ERROR_INSUFFICIENT_BUFFER) {
      VGAUTH_LOG_ERR_WIN_CODE(lastError, "LookupAccountSidW() failed");
      goto done;
   }

   ASSERT(accountChar2Needed > 0);
   ASSERT(domainChar2Needed > 0);

   accountW = (WCHAR *) g_malloc0(accountChar2Needed * sizeof(WCHAR));
   domainW = (WCHAR *) g_malloc0(domainChar2Needed * sizeof(WCHAR));

   ok = LookupAccountSidW(NULL, pSid, accountW, &accountChar2Needed,
                          domainW, &domainChar2Needed, &eUse);
   if (!ok) {
      VGAUTH_LOG_ERR_WIN("LookupAccountSidW failed");
      goto done;
   }

   /*
    * Since the query is being done on the local server, the domain
    * return value shouldn't matter (and should be 'BUILTIN').
    */

   // get everything in one shot
   status = NetLocalGroupGetMembers(NULL,       // server name
                                    accountW,    // group name
                                    1,          // level
                                    (LPBYTE *) &groupList, // return buffer
                                    MAX_PREFERRED_LENGTH,   // get it all
                                    &entriesRead,
                                    &totalEntries,
                                    NULL);         // resume handle

   if (status != NERR_Success) {
      VGAUTH_LOG_ERR_WIN_CODE(status, "NetLocalGroupGetMembers() failed");
      goto done;
   }

   CHK_UTF8_TO_UTF16(userNameW, userName, goto done);
   for (i = 0; i < entriesRead; i++) {
#ifdef VMX86_DEBUG
      g_debug("%s: checking input %S against group member #%d %S\n",
              __FUNCTION__, userNameW, i, groupList[i].lgrmi1_name);
#endif
      if (_wcsicmp(userNameW, groupList[i].lgrmi1_name) == 0) {
         bRetVal = TRUE;
         goto done;
      }
   }

done:
   g_free(pSid);
   if (groupList) {
      NetApiBufferFree(groupList);
   }
   g_free(userNameW);
   g_free(accountW);
   g_free(domainW);

   return bRetVal;
}
#endif   // _WIN32
 07070100000423000081A400000000000000000000000168225505000006B4000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/usercheck.h  /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _USERCHECK_H_
#define _USERCHECK_H_

/*
 * @file usercheck.h
 *
 * User existance support.
 */

#include <glib.h>
#include "VGAuthError.h"
#ifdef _WIN32
#include "winUtil.h"
#else
#include <sys/types.h>
#include <pwd.h>
#endif

gboolean UsercheckUserExists(const gchar *userName);
#ifndef _WIN32
VGAuthError UsercheckLookupUser(const gchar *userName,
                                uid_t *uid,                       // OUT
                                gid_t *gid);                      // OUT

VGAuthError UsercheckLookupUid(uid_t uid,
                               gchar **userName);

#endif

#ifdef _WIN32
gboolean Usercheck_IsAdminMember(const gchar *userName);
#endif

gboolean Usercheck_CompareByName(const char *u1, const char *u2);

gboolean Usercheck_UsernameIsLegal(const char *userName);

#endif // _USERCHECK_H_
07070100000424000081A40000000000000000000000016822550500001672000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/vmxlog.c /*********************************************************
 * Copyright (c) 2018-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file vmxlog.c
 *
 * Simple guest->VMX RPC log support that assumes VMCI is available.
 *
 */

#include <glib.h>
#include "vmxrpc.h"
#include "vmxlog.h"

#define LOG_RPC_CMD  "log"
#define LOG_RPC_CMD_NEW  "guest.log.text"

static gboolean gDisableVMXLogging = TRUE;

/*
 * Error codes for SendLogString()
 */
#define VMX_RPC_OK       1                    // success
#define VMX_RPC_UNKNOWN  0                    // RPC disabled or not supported
#define VMX_RPC_ERROR   -1                    // failed to send RPC

#define VMXLOG_SERVICE_NAME      "[vgauthservice]"


/*
 ******************************************************************************
 * VMXLog_Init --                                                        */ /**
 *
 * Initializes the VMX log facility.
 *
 * Returns -1 on error, 1 on success.
 *
 ******************************************************************************
 */

int
VMXLog_Init(void)
{
   gDisableVMXLogging = FALSE;
   return VMXRPC_Init();
}


/*
 ******************************************************************************
 * VMXLog_Shutdown --                                                    */ /**
 *
 * Shuts down the VMX log facility.
 *
 ******************************************************************************
 */

void
VMXLog_Shutdown(void)
{
   gDisableVMXLogging = TRUE;
}


/*
 ******************************************************************************
 * SendLogString --                                                      */ /**
 *
 * Sends a Log message to the VMX.
 *
 * @param[in] cmd       The message to send.
 *
 * Returns VMX_RPC_ERROR on failure, VMX_RPC_OK on success, VMX_RPC_UNKNOWN
 * if RPC failed (doesn't exist or disabled).
 ******************************************************************************
 */

static int
SendLogString(const gchar *cmd)
{
   gchar *reply = NULL;
   int ret;
   int retVal = VMX_RPC_OK;

   ret = VMXRPC_SendRpc(cmd, FALSE, &reply);
   if (ret >= 0) {
      if (g_strcmp0(reply, "disabled") == 0 ||
          g_strcmp0(reply, "Unknown command") == 0) {
         g_warning("%s: RPC unknown or disabled\n", __FUNCTION__);
         retVal = VMX_RPC_UNKNOWN;
      }
   } else {
      g_warning("%s: failed to send RPC packet\n", __FUNCTION__);
      retVal = VMX_RPC_ERROR;
   }

   g_free(reply);
   return retVal;
}


/*
 ******************************************************************************
 * VMXLog_LogV --                                                        */ /**
 *
 * Logs to the VMX using va_list arguments.
 *
 * @param[in] level       Logging level (currently unused).
 * @param[in] fmt         The format message for the event.
 * @param[in] args        The arguments for @a fmt.
 *
 ******************************************************************************
 */

void
VMXLog_LogV(int level,
            const char *fmt,
            va_list args)
{
   gchar *msg = NULL;
   gchar *cmd = NULL;
   int ret;
   // static gboolean useNewRpc = TRUE;
   // XXX the new RPC can quietly no-op on virtual hw < 17
   // is this fixable somehow, or should we just give up
   // on the new RPC completely?
   static gboolean useNewRpc = FALSE;
   static gboolean rpcBroken = FALSE;

   /*
    * RPCs don't work -- not in a VM or no vmci -- so drop any messages.
    */
   if (gDisableVMXLogging || rpcBroken) {
      return;
   }

   msg = g_strdup_vprintf(fmt, args);
again:
   /*
    * Try the new logging RPC, fail over to the old
    *
    * Possible optimization -- every N minutes, retry the new RPC in
    * case its been enabled dynamically.
    */
   if (useNewRpc) {
      /* XXX TODO use the level */
      cmd = g_strdup_printf("%s " VMXLOG_SERVICE_NAME " %s", LOG_RPC_CMD_NEW, msg);
   } else {
      cmd = g_strdup_printf("%s " VMXLOG_SERVICE_NAME " %s", LOG_RPC_CMD, msg);
   }

   ret = SendLogString(cmd);
   g_free(cmd);
   cmd = NULL;
   if ((ret == VMX_RPC_UNKNOWN) && useNewRpc) {
      g_debug("%s: new RPC Failed, using old\n", __FUNCTION__);
      useNewRpc = FALSE;
      goto again;
   } else if (ret == VMX_RPC_ERROR) {
      rpcBroken = TRUE;
      g_debug("%s: Error sending RPC, assume they aren't supported\n",
              __FUNCTION__);
   }

   g_free(msg);
   msg = NULL;
}


/*
 ******************************************************************************
 * VMXLog_Log --                                                        */ /**
 *
 * Logs to the VMX.
 *
 * @param[in] level       Logging level (currently unused).
 * @param[in] fmt         The format message for the event.
 * @param[in] args        The arguments for @a fmt.
 *
 ******************************************************************************
 */

void
VMXLog_Log(int level,
           const char *fmt,
           ...)
{
   va_list args;

   va_start(args, fmt);
   VMXLog_LogV(level, fmt, args);
   va_end(args);
}
  07070100000425000081A40000000000000000000000016822550500000533000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/vmxlog.h /*********************************************************
 * Copyright (C) 2018 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMXLOG_H_
#define _VMXLOG_H_

/*
 * @file vmxlog.h
 *
 * Logging to the VMX (vmware.log)
 */

#include "VGAuthBasicDefs.h"

int VMXLog_Init(void);

void VMXLog_Shutdown(void);

void VMXLog_Log(int level, const char *fmt, ...) PRINTF_DECL(2, 3);

/*
 * * XXX Future-proofing -- currently unused.
 */
#define VMXLOG_LEVEL_INFO     1
#define VMXLOG_LEVEL_WARNING  2

void VMXLog_LogV(int level, const char *fmt, va_list args);

#endif // _VMXLOG_H_

 07070100000426000081A400000000000000000000000168225505000041DF000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/vmxrpc.c /*********************************************************
 * Copyright (c) 2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file vmxrpc.c
 *
 * Simple guest->VMX RPC support.
 *
 * Doesn't share any Tools code or headers; key bits that can't change
 * are copied from vmci_sockets.h
 *
 */

#include <glib.h>
#include "vmxrpc.h"

#include <sys/types.h>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>


/*
 * Max number of times to try to connect to the VMX if the connection
 * keeps getting reset.  This can also occur if vsock is disabled for RPCs.
 * See PR 3048949, PR 728832.
 */
#define MAX_CONN_RETRIES 5

/*
 * VMX listening address
 */
const int VMX_CID = 0;
const int RPCI_PORT = 976;
#define VMADDR_PORT_ANY ((unsigned int) -1)

#define PRIVILEGED_PORT_MAX 1023
#define PRIVILEGED_PORT_MIN 1

static int gAddressFamily = -1;

/*
 * Some typedefs for portability.
 */
#ifdef _WIN32
/* still have to care about pre-C++11 so use the old instead of stdint.h */
typedef unsigned __int32 uint32;
typedef unsigned __int64 uint64;

#endif
#ifdef __linux__
typedef __uint32_t uint32;
typedef __uint64_t uint64;

#define SOCKET int
#endif
#ifdef sun
typedef uint32_t uint32;
typedef uint64_t uint64;

#define SOCKET int
#endif

#if defined(_WIN32)
typedef unsigned short sa_family_t;
#endif // _WIN32

#ifdef _WIN32
#define SYSERR_ECONNRESET        WSAECONNRESET
#define SYSERR_EADDRINUSE        WSAEADDRINUSE
#else
#define SYSERR_ECONNRESET        ECONNRESET
#define SYSERR_EADDRINUSE        EADDRINUSE
#endif


/*
 * Wrapper for socket errnos
 */
static int
GetSocketErrCode(void)
{
#ifdef _WIN32
   return WSAGetLastError();
#else
   return errno;
#endif
}


/*
 * Wrapper for socket close
 */
static void
Socket_Close(SOCKET fd)
{
#ifdef _WIN32
   closesocket(fd);
#else
   close(fd);
#endif
}


/*
 * start code cut&paste from vmci_sockets.h
 *
 * This is the subset from vmci_sockets.h required for our purposes.
 * this results in a few refs to other parts of the file that were
 * left out.
 */

#ifdef _WIN32
#     include <winioctl.h>
#     define VMCI_SOCKETS_DEVICE          L"\\\\.\\VMCI"
#     define VMCI_SOCKETS_VERSION         0x81032058
#     define VMCI_SOCKETS_GET_AF_VALUE    0x81032068
#     define VMCI_SOCKETS_GET_LOCAL_CID   0x8103206c
#     define VMCI_SOCKETS_UUID_2_CID      0x810320a4

static unsigned int
__VMCISock_DeviceIoControl(DWORD cmd)
{
   unsigned int val = (unsigned int)-1;
   HANDLE device = CreateFileW(VMCI_SOCKETS_DEVICE, GENERIC_READ, 0, NULL,
                               OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
   if (INVALID_HANDLE_VALUE != device) {
      DWORD ioReturn;
      DeviceIoControl(device, cmd, &val, sizeof val, &val, sizeof val,
                      &ioReturn, NULL);
      CloseHandle(device);
      device = INVALID_HANDLE_VALUE;
   }
   return val;
}
#endif   //_WIN32


/**
 * \brief Address structure for vSockets.
 *
 * The address family should be set to whatever VMCISock_GetAFValueFd()
 * returns.  The structure members should all align on their natural
 * boundaries without resorting to compiler packing directives.  The total
 * size of this structure should be exactly the same as that of \c struct
 * \c sockaddr.
 *
 * \see VMCISock_GetAFValueFd()
 */

struct sockaddr_vm {
#if defined(__APPLE__) || defined(__FreeBSD__)
   unsigned char svm_len;
#endif // __APPLE__ || __FreeBSD__

   /** \brief Address family. \see VMCISock_GetAFValueFd() */
   sa_family_t svm_family;

   /** \cond PRIVATE */
   unsigned short svm_reserved1;
   /** \endcond */

   /** \brief Port.  \see VMADDR_PORT_ANY */
   unsigned int svm_port;

   /** \brief Context ID.  \see VMADDR_CID_ANY */
   unsigned int svm_cid;

   /** \cond PRIVATE */
   unsigned char svm_zero[sizeof(struct sockaddr) -
#if defined(__APPLE__)
                             sizeof(unsigned char) -
#endif // __APPLE__
                             sizeof(sa_family_t) -
                             sizeof(unsigned short) -
                             sizeof(unsigned int) -
                             sizeof(unsigned int)];
   /** \endcond */
};
/* end code copied from vmci_sockets.h */


/*
 * Local version of htonll() which is missing in many environments.
 * Assumes the host is little-endian.
 */
static uint64
_vmxlog_htonll(uint64 s)
{
   uint64 out;
   unsigned char *buf = (unsigned char *) &out;

   buf[0] = s >> 56 & 0xff;
   buf[1] = s >> 48 & 0xff;
   buf[2] = s >> 40 & 0xff;
   buf[3] = s >> 32 & 0xff;
   buf[4] = s >> 24 & 0xff;
   buf[5] = s >> 16 & 0xff;
   buf[6] = s >>  8 & 0xff;
   buf[7] = s >>  0 & 0xff;

   return out;
}


/*
 ******************************************************************************
 * VMXRPC_MakePacket --                                                  */ /**
 *
 * Takes 'cmd' and builds an RPC packet out of it, putting in the
 * length and header info (properly byte swapped).
 *
 * See bora-vmsoft/lib/rpcChannel/simpleSocket.c:Socket_PackSendData()
 *
 * retPacket contains the new packet, which must be g_free()d.
 * Returns the size of the new packet.
 *
 * Returns -1 on failure, size of packet on success.
 *
 ******************************************************************************
 */

static int
VMXRPC_MakePacket(const char *cmd,
                  char **retPacket)
{
   int len;
   int tlen;
   uint32 plen;
   uint32 slen;
   char *packet = NULL;
   char *p;
   struct {
      uint32 type;
      uint32 fieldId;
      uint64 value;
   } hdr;
   struct {
      uint32 type;
      uint32 fieldId;
      uint64 value;
   } flags;
   struct {
      uint32 type;
      uint32 fieldId;
      uint32 len;
   } payload;

   *retPacket = NULL;
   if (cmd == NULL) {
      return -1;
   }

   len = (int) strlen(cmd);

   /* network byte order is important here */
   hdr.type = htonl(1);         // DMFIELDTYPE_INT64
   hdr.fieldId = htonl(1);      // GUESTRPCPKT_FIELD_TYPE
   hdr.value = _vmxlog_htonll(1);       // GUESTRPCPKT_TYPE_DATA

   /*
    * Adding the fast_close flag GUESTRPCPKT_FIELD_FAST_CLOSE in the packet
    * to indicate vmx to close the channel as soon as the response is sent.
    */
   flags.type = htonl(1);            // DMFIELDTYPE_INT64
   flags.fieldId = htonl(3);         // GUESTRPCPKT_FIELD_FAST_CLOSE
   flags.value = _vmxlog_htonll(1);  // GUESTRPCPKT_TYPE_DATA

   /*
    * this part of the data doesn't seem to care about network byte
    * order, but do it anyways.
    */
   payload.type = htonl(2);     // DMFIELDTYPE_STRING
   payload.fieldId = htonl(2);  // GUESTRPCPKT_FIELD_PAYLOAD
   payload.len = htonl(len);    // length of 'cmd'

   plen = sizeof(hdr) + sizeof(flags) + sizeof(payload) + len;

   tlen = plen + sizeof(int);
   packet = (char *) g_malloc(tlen);
   p = packet;

   /* use network byte order overall length */
   slen = htonl(plen);
   memcpy(p, &slen, sizeof slen);
   p += sizeof slen;

   memcpy(p, &hdr, sizeof hdr);
   p += sizeof hdr;
   memcpy(p, &flags, sizeof flags);
   p += sizeof flags;
   memcpy(p, &payload, sizeof payload);
   p += sizeof payload;
   memcpy(p, cmd, len);

   *retPacket = packet;

   return tlen;
}


/*
 ******************************************************************************
 * GetAddressFamily --                                                   */ /**
 *
 * Returns the vsock socket family on success, -1 on failure.
 *
 * This assumes modern vsock is in the kernel.
 ******************************************************************************
 */

static int
GetAddressFamily(void)
{
#ifdef _WIN32
   return __VMCISock_DeviceIoControl(VMCI_SOCKETS_GET_AF_VALUE);
#else
   const int AF_VSOCK_LOCAL = 40;
   int s = socket(AF_VSOCK_LOCAL, SOCK_DGRAM, 0);
   if (s != -1) {
      close(s);
      return AF_VSOCK_LOCAL;
   }

   return -1;
#endif
}


/*
 ******************************************************************************
 * CreateVMCISocket --                                                   */ /**
 *
 * Creates, binds and connects a socket to the VMX.
 *
 * @param[in] useSecure   If TRUE, use bind to a reserved port locally to allow
 *                        for a secure channel.
 *
 * Returns a new socket that should be close()d or -1 on failure.
 *
 ******************************************************************************
 */

static SOCKET
CreateVMCISocket(gboolean useSecure)
{
   struct sockaddr_vm localAddr;
   struct sockaddr_vm addr;
   int ret;
   int errCode;
   unsigned int localPort = PRIVILEGED_PORT_MAX;
   SOCKET fd;
   int retryCount = 0;

again:
   fd = socket(gAddressFamily, SOCK_STREAM, 0);
   if (fd < 0) {
      g_warning("%s: socket() failed %d\n", __FUNCTION__, GetSocketErrCode());
      return -1;
   }

   memset(&localAddr, 0, sizeof localAddr);
   localAddr.svm_family = gAddressFamily;
#ifdef _WIN32
   localAddr.svm_cid = __VMCISock_DeviceIoControl(VMCI_SOCKETS_GET_LOCAL_CID);
#else
   localAddr.svm_cid = -1;
#endif

   if (useSecure) {
      while (localPort >= PRIVILEGED_PORT_MIN) {
         localAddr.svm_port = localPort;
         ret = bind(fd, (struct sockaddr *)&localAddr, sizeof localAddr);
         if (ret != 0) {
            errCode = GetSocketErrCode();
            if (errCode == SYSERR_EADDRINUSE) {
               g_debug("%s: bind() failed w/ ADDRINUSE, trying another port\n",
                       __FUNCTION__);
               --localPort;
               continue; /* Try next port */
            } else {
               // unexpected failure, bail
               g_warning("%s: bind() failed %d\n", __FUNCTION__, errCode);
               goto err;
            }
         }  else {
            g_debug("%s: bind() worked for port %d\n", __FUNCTION__, localPort);
            goto bound;
         }
      }
      g_warning("%s: failed to find a bindable port\n", __FUNCTION__);
      goto err;
   } else {
      localAddr.svm_port = VMADDR_PORT_ANY;
      ret = bind(fd, (struct sockaddr *)&localAddr, sizeof localAddr);
      if (ret != 0) {
         g_warning("%s: bind() failed %d\n", __FUNCTION__, GetSocketErrCode());
         goto err;
      }
   }

bound:
   /* connect to destination */
   memset(&addr, 0, sizeof addr);
   addr.svm_family = gAddressFamily;
   addr.svm_cid = VMX_CID;
   addr.svm_port = RPCI_PORT;

   ret = connect(fd, (struct sockaddr *)&addr, sizeof addr);
   if (ret < 0) {
      errCode = GetSocketErrCode();
      if (errCode == SYSERR_ECONNRESET && useSecure) {
         /*
          * VMX might be slow releasing a port pair
          * when another client closed the client side end.
          * Try next port.
          * This can also occur if there's no listen socket in the VMX.
          */
         g_debug("%s: connect() failed with RESET, trying another port\n",
                 __FUNCTION__);
         if (++retryCount >= MAX_CONN_RETRIES) {
            g_warning("%s: connect() RESET %d times, giving up\n",
                      __FUNCTION__, MAX_CONN_RETRIES);
            goto err;
         }
         localPort--;
         Socket_Close(fd);
         goto again;
      }
      g_warning("%s: connect() failed %d\n", __FUNCTION__, GetSocketErrCode());
      goto err;
   }

   return fd;
err:
   Socket_Close(fd);

   return -1;
}



/*
 ******************************************************************************
 * VMXRPC_Init --                                                        */ /**
 *
 * Initializes VMX secure RPCs.
 *
 * Returns -1 on error, 1 on success.
 *
 ******************************************************************************
 */

int
VMXRPC_Init(void)
{
   if (gAddressFamily != -1) {
      // already initted
      return 1;
   }
#ifdef _WIN32
   int ret;
   WSADATA wsaData;

   ret = WSAStartup(MAKEWORD(2,0), &wsaData);
   if (ret != 0) {
      g_warning("%s: Failed to init winsock (%d)\n", __FUNCTION__, ret);
      return -1;
   }
#endif
   gAddressFamily = GetAddressFamily();
   if (gAddressFamily < 0) {
      g_warning("%s: Failed to set up VMX logging\n", __FUNCTION__);
      return -1;
   }

   return 1;
}


/*
 ******************************************************************************
 * VMXRPC_SendRpc --                                                     */ /**
 *
 * Sends RPC packet to the VMX.  Returns any response if retBuf is
 * non-NULL.
 *
 * @param[in] cmd         RPC command
 * @param[in] useSecure   If TRUE, use bind to a reserved port locally to allow
 *                        for a secure channel.
 * @param[out] retBuf     RPC reply.
 *
 * Returns -1 on failure, or the length of the returned reply on success (0 if
 * retBuf is NULL).
 *
 ******************************************************************************
 */

int
VMXRPC_SendRpc(const gchar *cmd,
               gboolean useSecure,
               gchar **retBuf)
{
   SOCKET sock;
   char *fullReply = NULL;
   char *bp;
   uint32 repLen;
   uint32 curLen;
   int ret;
   int retVal = 0;
   gchar *packet = NULL;
   int packetLen;
   struct {
      uint32 len;
   } hdr;
   // bytes into the DataMap stream where the reply starts
#define REPLY_OFFSET 14

   if (VMXRPC_Init() != 1) {
      g_warning("%s: couldn't get VMCI address family\n", __FUNCTION__);
      return -1;
   }

   sock = CreateVMCISocket(useSecure);
   if (sock < 0) {
      g_warning("%s: failed to create VMCI socket\n", __FUNCTION__);
      return -1;
   }

   packetLen = VMXRPC_MakePacket(cmd, &packet);
   if (packetLen <= 0) {
      g_warning("%s: failed to build RPC packet\n", __FUNCTION__);
      retVal = -1;
      goto done;
   }

   ret = send(sock, packet, packetLen, 0);
   if (ret != packetLen) {
      g_warning("%s: failed to send packet (%d)\n",
                __FUNCTION__, GetSocketErrCode());
      retVal = -1;
      goto done;
   }

   // get the header, which is the length of the rest of the reply
   ret = recv(sock, (char *) &hdr, sizeof hdr, 0);
   if (ret != sizeof(hdr)) {
      g_warning("%s: failed to read reply length (wanted %d, got %d) (%d)\n",
                __FUNCTION__, (int) sizeof(hdr), ret, GetSocketErrCode());
      retVal = -1;
      goto done;
   }
   repLen = ntohl(hdr.len);
   g_debug("%s: reply len: %u\n", __FUNCTION__, repLen);
   if (repLen < REPLY_OFFSET) {
      g_warning("%s: reply len too small (%u)\n",
                __FUNCTION__, repLen);
      retVal = -1;
      goto done;
   }
   // +1 to ensure a NUL at the end
   fullReply = g_malloc0((sizeof(char) * repLen) + 1);
   curLen = 0;
   bp = fullReply;
   // handle the case it somehow gets chopped up
   while (curLen < repLen) {
      ret = recv(sock, bp, repLen - curLen, 0);
      if (ret < 0) {
         g_warning("%s: failed to read reply  packet (%d)\n",
                   __FUNCTION__, GetSocketErrCode());
         retVal = -1;
         goto done;
      } else if (ret == 0) {  // unexpected EOF, fail
         g_warning("%s: unexpected EOF, failing\n", __FUNCTION__);
         retVal = -1;
         goto done;
      }
      curLen += ret;
      bp += ret;
   }

done:
#ifdef _WIN32
   closesocket(sock);
#else
   close(sock);
#endif
   if (retVal >= 0 && retBuf != NULL) {
      // the useful part starts at REPLY_OFFSET after the DataMaps
      *retBuf = g_strdup(fullReply + REPLY_OFFSET);
      retVal = repLen - REPLY_OFFSET;
   }
   g_free(fullReply);
   g_free(packet);

   return retVal;
}


#ifdef TEST
/* build test app  with
 * $ gcc -g -DTEST -I/usr/include/glib-2.0 \
 *    -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -o /tmp/vmxrpc-test \
 *    vmxrpc.c -lglib-2.0
 */
int
main(int argc, char **argv)
{
   char *reply;
   int ret;

   if (argc < 2) {
      fprintf(stderr, "%s: needs an RPC arg\n", argv[0]);
      exit(-1);
   }
   ret = VMXRPC_SendRpc(argv[1], FALSE, &reply);
   if (ret < 0) {
      fprintf(stderr, "%s: failed to send RPC\n", argv[0]);
      exit(-1);
   } else {
      puts(reply);
      g_free(reply);
   }
   exit(0);
}
#endif // TEST
 07070100000427000081A40000000000000000000000016822550500000448000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/common/vmxrpc.h /*********************************************************
 * Copyright (c) 2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef _VMXRPC_H_
#define _VMXRPC_H_


/*
 * @file vmxrpc.h
 *
 * Support for RPCs to the VMX.
 */

int VMXRPC_SendRpc(const gchar *cmd, gboolean useSecure, gchar **retBuf);

int VMXRPC_Init(void);

#endif // _VMXRPC_H_
07070100000428000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib 07070100000429000081A40000000000000000000000016822550500000A16000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/Makefile.am ################################################################################
### Copyright (C) 2014-2016,2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

lib_LTLIBRARIES = libvgauth.la

libvgauth_la_LIBADD =
libvgauth_la_LIBADD += @VGAUTH_LIBADD@
libvgauth_la_LIBADD += @GLIB2_LIBS@
libvgauth_la_LIBADD += @GTHREAD_LIBS@
libvgauth_la_LIBADD += @VMTOOLS_LIBS@
libvgauth_la_LIBADD += @SSL_LIBS@
libvgauth_la_LIBADD += -lssl
libvgauth_la_LIBADD += -lcrypto

libvgauth_la_SOURCES =
libvgauth_la_SOURCES += alias.c
libvgauth_la_SOURCES += auth.c
libvgauth_la_SOURCES += authPosix.c
libvgauth_la_SOURCES += comm.c
libvgauth_la_SOURCES += common.c
libvgauth_la_SOURCES += errortext.c
libvgauth_la_SOURCES += impersonate.c
libvgauth_la_SOURCES += impersonateLinux.c
libvgauth_la_SOURCES += netPosix.c
libvgauth_la_SOURCES += proto.c
libvgauth_la_SOURCES += ../common/audit.c
libvgauth_la_SOURCES += ../common/certverify.c
libvgauth_la_SOURCES += ../common/i18n.c
libvgauth_la_SOURCES += ../common/prefs.c
libvgauth_la_SOURCES += ../common/usercheck.c
libvgauth_la_SOURCES += ../common/VGAuthLog.c
libvgauth_la_SOURCES += ../common/VGAuthUtil.c

libvgauth_la_CPPFLAGS =
libvgauth_la_CPPFLAGS += @GLIB2_CPPFLAGS@
libvgauth_la_CPPFLAGS += -DG_LOG_DOMAIN=\"VCGA\"
libvgauth_la_CPPFLAGS += @PAM_CPPFLAGS@
libvgauth_la_CPPFLAGS += @SSL_CPPFLAGS@
libvgauth_la_CPPFLAGS += -I$(top_srcdir)/libvmtools
libvgauth_la_CPPFLAGS += -I$(top_srcdir)/vgauth/public
libvgauth_la_CPPFLAGS += -I$(top_srcdir)/vgauth/common

libvgauth_la_LDFLAGS =
# We require GCC, so we're fine passing compiler-specific flags.
libvgauth_la_LDFLAGS += -Wl,-z,defs
# Needed for OS's that don't link shared libraries against libc by
#default, e.g. FreeBSD
libvgauth_la_LIBADD += -lc

# Message catalogs.
install-data-hook:
	@INSTVMSG@ libvgauth.la $(srcdir)/l10n $(DESTDIR)$(datadir)
  0707010000042A000081A40000000000000000000000016822550500002DC3000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/VGAuthInt.h /*********************************************************
 * Copyright (c) 2011-2017,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file VGAuthInt.h
 *
 * Private functions and data types for client library.
 */

#ifndef _VGAUTHINT_H_
#define _VGAUTHINT_H_

#include "VGAuthBasicDefs.h"
#include "VGAuthCommon.h"
#include "VGAuthAuthentication.h"
#include "VGAuthAlias.h"
#include "audit.h"
#include "prefs.h"

#define VMW_TEXT_DOMAIN "VGAuthLib"
#include "i18n.h"

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/types.h>
#endif

#include <glib.h>
#include <glib/gstdio.h>

/*
 * Use this for any informational messages, eg "VGAuth initialized".
 */
#define Log   g_message

/*
 * Use this for any error reporting, such as unexpected failures from APIs
 * or bad input to VGAuth APIs.
 */
#define Warning   g_warning

/*
 * Use this for any debugging messages.
 */
#define Debug  g_debug


/*
 * Set this to be horribly inefficient but to be sure that nothing
 * is assuming it will get a full packet as sent by a single syscall
 * on the other end.
 */
#define  NETWORK_FORCE_TINY_PACKETS 0


/*
 * State of the client/service communication channel
 */
typedef struct VCAGComm {
   gboolean connected;
   unsigned int sequenceNumber;
   gchar *userName;           // the user we're runing as, used for
                              // setting up the comm pipe permissions
#ifdef UNITTEST
   gboolean fileTest;
   gboolean bufTest;

   FILE *testFp;

   char testBuffer[10240];
   gsize bufLen;
   gsize bufLoc;
#endif

#ifdef _WIN32
   HANDLE hPipe;
#else
   int sock;
#endif
   char *pipeName;
} VGAuthComm;

struct VGAuthUserHandle;

struct VGAuthContext {
   /*
    * Needed for pam(3) initialization.
    */
   char *applicationName;

   int numExtraParams;
   VGAuthExtraParams *extraParams;

#ifdef _WIN32
   /*
    * Used for authentication using SSPI, to track the SSPI challenge
    * and response handshakes that are in progress.
    */
   GHashTable *sspiHandshakes;
#endif

   /*
    *
    * Connection data for keystore service, etc
    */
   VGAuthComm comm;

   /*
    * Impersonation state.
    */
   gboolean isImpersonating;

   /*
    * Impersonated user.
    */
   VGAuthUserHandle *impersonatedUser;

   /*
    * XXX optimization -- keep a comm channel alive for superuser?
    *
    * An app that just does validation would probably just be connected
    * as root all the time anyways.  But it could be useful for something
    * that did both certstore work and validation.
    */

};


typedef enum {
   VGAUTH_HANDLE_FLAG_NONE             = 0x0,
   /* handle cannot be impersonated */
   VGAUTH_HANDLE_FLAG_CAN_IMPERSONATE  = 0x1,
   /* handle cannot be used by CreateTicket */
   VGAUTH_HANDLE_FLAG_CAN_CREATE_TICKET = 0x2,

   /* normal handle */
   VGAUTH_HANDLE_FLAG_NORMAL            =  (VGAUTH_HANDLE_FLAG_CAN_IMPERSONATE |
                                           VGAUTH_HANDLE_FLAG_CAN_CREATE_TICKET),
} VGAuthHandleFlag;


typedef struct AuthDetails {
   VGAuthUserHandleType type;
   union {
      struct {
         char *subject;
         VGAuthAliasInfo aliasInfo;
      } samlData;
   } val;
} AuthDetails;

struct VGAuthUserHandle {
   char *userName;
   VGAuthHandleFlag   flags;
   AuthDetails details;
#ifdef _WIN32
   HANDLE token;
   HANDLE hProfile;
#else
   uid_t uid;
#endif
   int refCount;
};


extern PrefHandle gPrefs;


void VGAuth_AuditEvent(VGAuthContext *ctx,
                       gboolean isSuccess,
                       const char *fmt, ...) PRINTF_DECL(3, 4);

gboolean VGAuth_IsRunningAsRoot(void);
gchar *VGAuth_GetCurrentUsername(void);


VGAuthError VGAuth_ConnectToServiceAsUser(VGAuthContext *ctx,
                                          const char *userName);
VGAuthError VGAuth_ConnectToServiceAsCurrentUser(VGAuthContext *ctx);
gboolean VGAuth_IsConnectedToServiceAsUser(VGAuthContext *ctx,
                                           const char *userName);
gboolean VGAuth_IsConnectedToServiceAsAnyUser(VGAuthContext *ctx);

VGAuthError VGAuth_InitConnection(VGAuthContext *ctx);
VGAuthError VGAuth_CloseConnection(VGAuthContext *ctx);

VGAuthError VGAuth_CommSendData(VGAuthContext *ctx,
                                gchar *request);

VGAuthError VGAuth_CommReadData(VGAuthContext *ctx,
                                gsize *len,
                                gchar **response);

VGAuthError VGAuth_SendConnectRequest(VGAuthContext *ctx);

VGAuthError VGAuth_SendSessionRequest(VGAuthContext *ctx,
                                      const char *userName,
                                      char **pipeName);           // OUT

VGAuthError VGAuth_SendCreateTicketRequest(VGAuthContext *ctx,
                                           VGAuthUserHandle *handle,
                                           char **ticket);               // OUT
VGAuthError VGAuth_SendValidateTicketRequest(VGAuthContext *ctx,
                                             const char *ticket,
                                             VGAuthUserHandle **handle); // OUT
VGAuthError VGAuth_SendRevokeTicketRequest(VGAuthContext *ctx,
                                           const char *ticket);

VGAuthError VGAuth_SendAddAliasRequest(VGAuthContext *ctx,
                                         const char *userName,
                                         gboolean addMappedLink,
                                         const char *pemCert,
                                         VGAuthAliasInfo *si);

VGAuthError VGAuth_SendRemoveAliasRequest(VGAuthContext *ctx,
                                          const char *userName,
                                          const char *pemCert,
                                          VGAuthSubject *subj);

VGAuthError VGAuth_SendQueryUserAliasesRequest(VGAuthContext *ctx,
                                               const char *userName,
                                               int *num,             // OUT
                                               VGAuthUserAlias **uaList);// OUT

VGAuthError VGAuth_SendQueryMappedAliasesRequest(VGAuthContext *ctx,
                                                 int *num,            // OUT
                                                 VGAuthMappedAlias **maList); // OUT

/* clang-format off */
VGAuthError VGAuth_SendValidateSamlBearerTokenRequest(VGAuthContext *ctx,
                                                      gboolean validateOnly,
                                                      gboolean hostVerified,
                                                      const char *samlToken,
                                                      const char *userName,
                                                      VGAuthUserHandle **userHandle);


/* clang-format on */

VGAuthError VGAuth_CreateHandleForUsername(VGAuthContext *ctx,
                                           const char *userName,
                                           VGAuthUserHandleType type,
                                           HANDLE token,
                                           VGAuthUserHandle **handle);   // OUT

VGAuthError VGAuth_SetUserHandleSamlInfo(VGAuthContext *ctx,
                                         VGAuthUserHandle *handle,
                                         const char *samlSubject,
                                         VGAuthAliasInfo *si);

VGAuthError VGAuthImpersonateImpl(VGAuthContext *ctx,
                                  VGAuthUserHandle *handle,
                                  gboolean loadUserProfile);

VGAuthError VGAuthEndImpersonationImpl(VGAuthContext *ctx);

VGAuthError VGAuth_NetworkConnect(VGAuthContext *ctx);

gboolean VGAuth_NetworkValidatePublicPipeOwner(VGAuthContext *ctx);

VGAuthError VGAuth_NetworkWriteBytes(VGAuthContext *ctx,
                                     gsize len,
                                     gchar *buffer);

VGAuthError VGAuth_NetworkReadBytes(VGAuthContext *ctx,
                                    gsize *len,
                                    gchar **buffer);


VGAuthError VGAuthValidateUsernamePasswordImpl(VGAuthContext *ctx,
                                               const char *userName,
                                               const char *password,
                                               VGAuthUserHandle **handle);

#ifdef UNITTEST
VGAuthError VGAuthComm_SetTestBufferInput(VGAuthContext *ctx,
                                          const char *buffer);

VGAuthError VGAuthComm_SetTestFileInput(VGAuthContext *ctx,
                                        const char *filename);

void VGAuth_UnitTestReplies(VGAuthContext *ctx);
#endif

#ifdef _WIN32
VGAuthError VGAuth_MakeToken(VGAuthContext *ctx, const char *userName,
                             VGAuthUserHandleType type,
                             VGAuthUserHandle **handle);
#endif

VGAuthError VGAuthInitAuthentication(VGAuthContext *ctx);
VGAuthError VGAuthInitAuthenticationPlatform(VGAuthContext *ctx);

void VGAuthShutdownAuthentication(VGAuthContext *ctx);
void VGAuthShutdownAuthenticationPlatform(VGAuthContext *ctx);

VGAuthError VGAuthGenerateSSPIChallengeImpl(VGAuthContext *ctx,
                                            size_t sspiRequestLen,
                                            const unsigned char *sspiRequest,
                                            unsigned int *id,
                                            size_t *challengeLen,
                                            unsigned char **challenge);

VGAuthError VGAuthValdiateSSPIResponseImpl(VGAuthContext *ctx,
                                           unsigned int id,
                                           size_t responseLen,
                                           const unsigned char *response,
                                           VGAuthUserHandle **userHandle);

#define VGAuthValidateExtraParams(numEP, ep)      \
   VGAuthValidateExtraParamsImpl(__FUNCTION__, (numEP), ep)

VGAuthError VGAuthValidateExtraParamsImpl(const char *funcName,
                                       int numExtraParams,
                                       const VGAuthExtraParams *params);

#define VGAuthGetBoolExtraParam(numEP, ep, name, defValue, value)      \
   VGAuthGetBoolExtraParamImpl(__FUNCTION__, (numEP), ep,              \
                               name, defValue, (value))

VGAuthError VGAuthGetBoolExtraParamImpl(const char *funcName,
                                        int numExtraParams,
                                        const VGAuthExtraParams *params,
                                        const char *paramName,
                                        gboolean defValue,
                                        gboolean *paramValue);

void VGAuth_FreeAliasInfoContents(VGAuthAliasInfo *si);
void VGAuth_CopyAliasInfo(const VGAuthAliasInfo *src,
                          VGAuthAliasInfo *dst);

#endif   // _VGAUTHINT_H_
 0707010000042B000081A4000000000000000000000001682255050000518C000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/alias.c /*********************************************************
 * Copyright (c) 2011-2016, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file alias.c
 *
 * Alias APIs
 *
 * An Alias is a combination of a certificate and a list
 * of subject names with an optional comment.  These are used to
 * map SAML token users into guest users.
 */

#include "VGAuthAlias.h"
#include "VGAuthInt.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "usercheck.h"


/*
 ******************************************************************************
 * VGAuthFreeSubjectContents --                                          */ /**
 *
 * @brief Frees any data inside a VGAuthSubject structure.
 *
 * Does not free the VGAuthSubject itself.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  subj       The VGAuthSubject contents to be freed.
 *
 ******************************************************************************
 */

static void
VGAuthFreeSubjectContents(VGAuthSubject *subj)
{
   if (NULL == subj) {
      return;
   }

   switch (subj->type) {
   case VGAUTH_SUBJECT_ANY:
      break;
   case VGAUTH_SUBJECT_NAMED:
      g_free(subj->val.name);
      break;
   }
}


/*
 ******************************************************************************
 * VGAuth_FreeAliasInfoContents --                                       */ /**
 *
 * @brief Frees any data inside a VGAuthAliasInfo structure.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  ai       The VGAuthAliasInfo to be clear.
 *
 ******************************************************************************
 */

void
VGAuth_FreeAliasInfoContents(VGAuthAliasInfo *ai)
{
   if (NULL == ai) {
      return;
   }

   VGAuthFreeSubjectContents(&(ai->subject));
   g_free(ai->comment);
}


/*
 ******************************************************************************
 * VGAuth_FreeAliasInfo --                                               */ /**
 *
 * @brief Frees a VGAuthAliasInfo structure.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  ai       The VGAuthAliasInfo to free.
 *
 ******************************************************************************
 */

void
VGAuth_FreeAliasInfo(VGAuthAliasInfo *ai)
{
   if (NULL == ai) {
      return;
   }

   VGAuth_FreeAliasInfoContents(ai);
   g_free(ai);
}


/*
 ******************************************************************************
 * VGAuth_CopyAliasInfo --                                               */ /**
 *
 * Copies a VGAuthAliasInfo structure.
 *
 * @param[in]  src       The src VGAuthAliasInfo.
 * @param[in]  dst       The dst VGAuthAliasInfo.
 *
 ******************************************************************************
 */

void
VGAuth_CopyAliasInfo(const VGAuthAliasInfo *src,
                     VGAuthAliasInfo *dst)
{
   if ((NULL == src) || (NULL == dst)) {
      ASSERT(0);
      return;
   }

   dst->subject.type = src->subject.type;
   dst->subject.val.name = g_strdup(src->subject.val.name);
   dst->comment = g_strdup(src->comment);
}


/*
 ******************************************************************************
 * VGAuth_FreeUserAliasList --                                           */ /**
 *
 * @brief Frees a list of VGAuthUserAlias structures.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  num      The length of the list.
 * @param[in]  uaList   The list to be freed.
 *
 ******************************************************************************
 */

void
VGAuth_FreeUserAliasList(int num,
                         VGAuthUserAlias *uaList)
{
   int i;
   int j;

   if (NULL == uaList) {
      return;
   }

   for (i = 0; i < num; i++) {
      for (j = 0; j < uaList[i].numInfos; j++) {
         VGAuth_FreeAliasInfoContents(&(uaList[i].infos[j]));
      }
      g_free(uaList[i].infos);
      g_free(uaList[i].pemCert);
   }
   g_free(uaList);
}


/*
 ******************************************************************************
 * VGAuth_FreeMappedAliasList --                                         */ /**
 *
 * @brief Frees a list of VGAuthMappedAlias structures.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  num      The length of the list.
 * @param[in]  maList   The list to be freed.
 *
 ******************************************************************************
 */

void
VGAuth_FreeMappedAliasList(int num,
                           VGAuthMappedAlias *maList)
{
   int i;
   int j;

   if (NULL == maList) {
      return;
   }

   for (i = 0; i < num; i++) {
      g_free(maList[i].pemCert);
      for (j = 0; j < maList[i].numSubjects; j++) {
         VGAuthFreeSubjectContents(&(maList[i].subjects[j]));
      }
      g_free(maList[i].subjects);
      g_free(maList[i].userName);
   }
   g_free(maList);
}


/*
 ******************************************************************************
 * VGAuthValidateSubject --                                              */ /**
 *
 * @brief Does a confidence check on a Subject parameter.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  subj       The VGAuthSubject to be checked.
 *
 ******************************************************************************
 */

static VGAuthError
VGAuthValidateSubject(VGAuthSubject *subj)
{
   if (NULL == subj) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if ((VGAUTH_SUBJECT_ANY != subj->type) &&
       (VGAUTH_SUBJECT_NAMED != subj->type)) {
      Warning("%s: invalid Subject type %d\n", __FUNCTION__, subj->type);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if ((VGAUTH_SUBJECT_NAMED == subj->type) &&
       (NULL == subj->val.name || !g_utf8_validate(subj->val.name, -1, NULL))) {
      Warning("%s: invalid Subject name\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_AddAlias --                                                    */ /**
 *
 * @brief Adds a certificate and AliasInfo to @a userName's alias store,
 * also adding a mapped link if requested.
 *
 * Any extraneous whitespace is removed from the beginning and end of the PEM
 * string before it is stored.
 *
 * If the pemCert already exists, the @a ai is added to any existing
 * @a AliasInfos.  If both exist, the operation is a no-op.
 *
 * @remark Must be called by superuser, or the owner of the alias store.
 *
 * @param[in]  ctx             The VGAuthContext.
 * @param[in]  userName        The name of the user with whom to associate the
 *                             certificate.
 * @param[in]  addMapping      If set, an entry in the mapping file is also
 *                             created.
 * @param[in]  pemCert         The certificate being added.
 * @param[in]  ai              The AliasInfo to be associated with this cert.
 * @param[in]  numExtraParams  The number of elements in extraParams.
 * @param[in]  extraParams     Any optional, additional paramaters to the
 *                             function. Currently none are supported, so
 *                             this must be NULL.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @retval VGAUTH_E_OK If called with the same arguments more than once
 *         (the operation is treated as a no-op).
 * @retval VGAUTH_E_SERVICE_NOT_RUNNING If the service cannot be contacted.
 * @retval VGAUTH_E_PERMISSION_DENIED If not called by superuser or the
 *         owner of the alias store.
 * @retval VGAUTH_E_NO_SUCH_USER If @a userName cannot be looked up.
 * @retval VGAUTH_E_MULTIPLE_MAPPINGS If @a addMapping is TRUE and the
 *         certificate and subject already exists in the mapping file
 *         associated with a different user.
 * @retval VGAUTH_E_INVALID_CERTIFICATE If @a cert is not a
 *         well-formed PEM x509 certificate.
 * @retval VGAUTH_ERROR_SET_SYSTEM_ERRNO If a syscall fails.  Use
 *         VGAUTH_ERROR_EXTRA_ERROR on the return value to get the errno.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_AddAlias(VGAuthContext *ctx,
                const char *userName,
                VGAuthBool addMapping,
                const char *pemCert,
                VGAuthAliasInfo *ai,
                int numExtraParams,
                const VGAuthExtraParams *extraParams)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == userName) || (NULL == pemCert) ||
       (NULL == ai) || (NULL == ai->comment)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(userName, -1, NULL)) {
      Warning("%s: invalid username\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!Usercheck_UsernameIsLegal(userName)) {
      Warning("%s: username contains illegal chars\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   /*
    * This is safe to do for Add only -- we need to handle the deleted-user
    * case for Remove and Query, but Add can't work since we can't
    * put proper security on the aliasStore file.
    */
   if (!UsercheckUserExists(userName)) {
      Warning("%s: username does not exist\n", __FUNCTION__);
      return VGAUTH_E_NO_SUCH_USER;
   }

   if (!g_utf8_validate(pemCert, -1, NULL)) {
      Warning("%s: invalid pemCert\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(ai->comment, -1, NULL)) {
      Warning("%s: invalid comment\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateSubject(&(ai->subject));
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuth_SendAddAliasRequest(ctx,
                                    userName,
                                    addMapping ? TRUE : FALSE,
                                    pemCert,
                                    ai);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_RemoveAlias --                                                 */ /**
 *
 * Removes @a subject from the alias store belonging
 * to @a userName.
 *
 * @remark Must be called by superuser, or the owner of the alias store.
 * @remark @a userName need not be valid in the OS; this allows removal of
 *         data belonging to a deleted user.
 *
 * @param[in]  ctx             The VGAuthContext.
 * @param[in]  userName        The name of the user from whose store to remove
 *                             the certificate.
 * @param[in]  pemCert         The certificate of the alias provider being
 *                             removed, in PEM format.
 * @param[in]  subject         The subject to be removed, in PEM format.
 * @param[in]  numExtraParams  The number of elements in extraParams.
 * @param[in]  extraParams     Any optional, additional paramaters to the
 *                             function. Currently none are supported, so
 *                             this must be NULL.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument or if the specified
 *         alias does not exist in the alias store.
 * @retval VGAUTH_E_SERVICE_NOT_RUNNING If the service cannot be contacted.
 * @retval VGAUTH_E_PERMISSION_DENIED If not called by superuser or the
 *         owner of the alias store.
 * @retval VGAUTH_ERROR_SET_SYSTEM_ERRNO If a syscall fails.  Use
 *         VGAUTH_ERROR_EXTRA_ERROR on the return value to get the errno.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_RemoveAlias(VGAuthContext *ctx,
                   const char *userName,
                   const char *pemCert,
                   VGAuthSubject *subject,
                   int numExtraParams,
                   const VGAuthExtraParams *extraParams)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == userName) || (NULL == pemCert) ||
       (NULL == subject)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(userName, -1, NULL)) {
      Warning("%s: invalid username\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!Usercheck_UsernameIsLegal(userName)) {
      Warning("%s: username contains illegal chars\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(pemCert, -1, NULL)) {
      Warning("%s: invalid PEM certificate\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateSubject(subject);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuth_SendRemoveAliasRequest(ctx,
                                       userName,
                                       pemCert,
                                       subject);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_RemoveAliasByCert --                                           */ /**
 *
 * Removes a cert and all associated subjects from the store belonging
 * to @a userName.
 *
 * @remark Must be called by superuser, or the owner of the alias store.
 * @remark @a userName need not be valid in the OS; this allows removal of
 *         data belonging to a deleted user.
 *
 * @param[in]  ctx             The VGAuthContext.
 * @param[in]  userName        The name of the user from whose store to remove
 *                             the certificate.
 * @param[in]  pemCert         The certificate to be removed, in PEM format.
 * @param[in]  numExtraParams  The number of elements in extraParams.
 * @param[in]  extraParams     Any optional, additional paramaters to the
 *                             function. Currently none are supported, so
 *                             this must be NULL.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument or if @a pemCert
 *         does not exist in the alias store.
 * @retval VGAUTH_E_SERVICE_NOT_RUNNING If the service cannot be contacted.
 * @retval VGAUTH_E_PERMISSION_DENIED If not called by superuser or the
 *         owner of the alias store.
 * @retval VGAUTH_ERROR_SET_SYSTEM_ERRNO If a syscall fails.  Use
 *         VGAUTH_ERROR_EXTRA_ERROR on the return value to get the errno.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_RemoveAliasByCert(VGAuthContext *ctx,
                         const char *userName,
                         const char *pemCert,
                         int numExtraParams,
                         const VGAuthExtraParams *extraParams)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == userName) || (NULL == pemCert)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(userName, -1, NULL)) {
      Warning("%s: invalid username\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!Usercheck_UsernameIsLegal(userName)) {
      Warning("%s: username contains illegal chars\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(pemCert, -1, NULL)) {
      Warning("%s: invalid PEM certificate\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   /*
    * Re-use the wire message with just no Subject.
    */
   err = VGAuth_SendRemoveAliasRequest(ctx,
                                       userName,
                                       pemCert,
                                       NULL);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_QueryUserAliases --                                            */ /**
 *
 * @brief Returns the list of VGAuthUserAlias associated with the
 * alias store owned by @a userName.
 *
 * @remark Must be called by superuser, or the owner of the alias store.
 * @remark @a userName need not be valid in the OS; this allows queries of
 *         data belonging to a deleted user.
 *
 * @param[in]  ctx             The VGAuthContext.
 * @param[in]  userName        The name of the user whose alias store should
 *                             be queried.
 * @param[in]  numExtraParams  The number of elements in extraParams.
 * @param[in]  extraParams     Any optional, additional paramaters to the
 *                             function. Currently none are supported, so
 *                             this must be NULL.
 * @param[out] num             The number of aliases being returned.
 * @param[out] uaList          The VGAuthUserAlias being returned.
 *                             Free this list with VGAuth_FreeUserAliasList().
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @retval VGAUTH_E_PERMISSION_DENIED If not called by superuser or the
 *         owner of the alias store.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_QueryUserAliases(VGAuthContext *ctx,
                        const char *userName,
                        int numExtraParams,
                        const VGAuthExtraParams *extraParams,
                        int *num,
                        VGAuthUserAlias **uaList)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == userName) || (NULL == num) ||
       (NULL == uaList)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(userName, -1, NULL)) {
      Warning("%s: invalid username\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!Usercheck_UsernameIsLegal(userName)) {
      Warning("%s: username contains illegal chars\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuth_SendQueryUserAliasesRequest(ctx,
                                            userName,
                                            num,
                                            uaList);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_QueryMappedAliases --                                          */ /**
 *
 * @brief Returns all the certificate/subject pairs and their associated user
 * from the mapping file.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  ctx             The VGAuthContext.
 * @param[in]  numExtraParams  The number of elements in extraParams.
 * @param[in]  extraParams     Any optional, additional paramaters to the
 *                             function. Currently none are supported, so
 *                             this must be NULL.
 * @param[out] num             The number of aliases being returned.
 * @param[out] maList          The list of VGAuthMappedAlias.
 *                             Free this with VGAuth_FreeMappedAliasList().
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_QueryMappedAliases(VGAuthContext *ctx,
                          int numExtraParams,
                          const VGAuthExtraParams *extraParams,
                          int *num,
                          VGAuthMappedAlias **maList)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == num) || (NULL == maList)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuth_SendQueryMappedAliasesRequest(ctx,
                                              num,
                                              maList);

   return err;
}
0707010000042C000081A400000000000000000000000168225505000059C0000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/auth.c  /*********************************************************
 * Copyright (c) 2011-2017,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file auth.c
 *
 * Authentication APIs
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "VGAuthInt.h"
#include "VGAuthLog.h"
#include "VGAuthUtil.h"
#include "usercheck.h"


/*
 ******************************************************************************
 * VGAuthInitAuthentication --                                           */ /**
 *
 * Initializes any resources needed for authentication.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuthInitAuthentication(VGAuthContext *ctx)
{
   return VGAuthInitAuthenticationPlatform(ctx);
}


/*
 ******************************************************************************
 * VGAuthShutdownAuthentication --                                       */ /**
 *
 * Releases any resources used for authentication.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 ******************************************************************************
 */

void
VGAuthShutdownAuthentication(VGAuthContext *ctx)
{
   VGAuthShutdownAuthenticationPlatform(ctx);
}


/*
 ******************************************************************************
 * VGAuth_CreateTicket --                                                */ /**
 *
 * @brief Creates a new ticket associated with the user represented by
 * @a handle.
 *
 * @remark On a non-Windows OS, the function must be called by root
 *         or the user associated with @a handle.
 *         On Windows, the function must be called by the local system account
 *         or an account in the administrators group or the user associated
 *         with @a handle.
 *
 * @param[in]  ctx            The VGAuthContext.
 * @param[in]  handle         The handle representing the user.
 * @param[in]  numExtraParams The number of elements in extraParams.
 * @param[in]  extraParams    Any optional, additional paramaters to the
 *                            function. Currently none are supported, so this
 *                            must be NULL.
 * @param[out] newTicket      The resulting ticket. Must be freed by the caller
 *                            using VGAuth_FreeBuffer().
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @retval VGAUTH_E_SERVICE_NOT_RUNNING If the service cannot be contacted.
 * @retval VGAUTH_E_PERMISSION_DENIED If not called by superuser or the
 *    the user associated with @a handle.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_CreateTicket(VGAuthContext *ctx,
                    VGAuthUserHandle *handle,
                    int numExtraParams,
                    const VGAuthExtraParams *extraParams,
                    char **newTicket)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == handle) || (NULL == newTicket)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!(handle->flags & VGAUTH_HANDLE_FLAG_CAN_CREATE_TICKET)) {
      Warning("%s: called on handle that doesn't not support operation \n",
              __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }
   err = VGAuth_SendCreateTicketRequest(ctx, handle, newTicket);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_ValidateTicket --                                              */ /**
 *
 * @brief Validates @a ticket and returns a handle associated with it.
 *
 * @remark On a non-Windows OS, the function must be called by root.
 *         On Windows, the function must be called by the local system account
 *         or an account in the administrators group.
 *
 * @param[in]  ctx            The VGAuthContext.
 * @param[in]  ticket         The ticket to be validated.
 * @param[in]  numExtraParams The number of elements in extraParams.
 * @param[in]  extraParams    Any optional, additional paramaters to the
 *                            function. Currently none are supported, so this
 *                            must be NULL.
 * @param[out] handle         The resulting handle representing the user
 *                            associated with the ticket.
 *                            Must be freed with VGAuth_UserHandleFree().
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @retval VGAUTH_E_INVALID_TICKET If @a ticket is not valid.
 * @retval VGAUTH_E_SERVICE_NOT_RUNNING If the service cannot be contacted.
 * @retval VGAUTH_E_PERMISSION_DENIED If not called by superuser.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_ValidateTicket(VGAuthContext *ctx,
                      const char *ticket,
                      int numExtraParams,
                      const VGAuthExtraParams *extraParams,
                      VGAuthUserHandle **handle)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == ticket) || (NULL == handle)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(ticket, -1, NULL)) {
      Warning("%s: invalid ticket\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuth_SendValidateTicketRequest(ctx, ticket, handle);
   if (err != VGAUTH_E_OK) {
      goto done;
   }

done:

   return err;
}


/*
 ******************************************************************************
 * VGAuth_RevokeTicket --                                                */ /**
 *
 * @brief Revokes @a ticket.
 *
 * If the ticket does not exist or the calling user does not own it,
 * this operation is a no-op and returns VGAUTH_E_OK.
 *
 * @remark On a non-Windows OS, the function must be called by root
 *         or the owner of the ticket.
 *         On Windows, the function must be called by the local system account
 *         or an account in the administrators group
 *         or the owner of the ticket.
 *
 * @param[in]  ctx            The VGAuthContext.
 * @param[in]  ticket         The ticket to be revoked.
 * @param[in]  numExtraParams The number of elements in extraParams.
 * @param[in]  extraParams    Any optional, additional paramaters to the
 *                            function. Currently none are supported, so this
 *                            must be NULL.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @retval VGAUTH_E_SERVICE_NOT_RUNNING If the service cannot be contacted.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_RevokeTicket(VGAuthContext *ctx,
                    const char *ticket,
                    int numExtraParams,
                    const VGAuthExtraParams *extraParams)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == ticket)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(ticket, -1, NULL)) {
      Warning("%s: invalid ticket\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuth_SendRevokeTicketRequest(ctx, ticket);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_GenerateSSPIChallenge --                                       */ /**
 *
 * @brief Generates a challenge for an SSPI authentication.
 *
 * Takes an initial request to begin an SSPI negotiation and generates a
 * challenge used to complete the negotiation. This uses the "Negotate"
 * security package to perform the authentication. The client first calls
 * AcquireCredentialsHandle(). The client then calls InitializeSecurityContext().
 * The resulting partially-formed context is passed to this function.
 *
 * For more information, see the MSDN documentation on SSPI.
 *
 * @remark On Windows, the function must be called by the local system account
 *         or an account from the administrators group.
 *
 * @param[in]  ctx            The VGAuthContext.
 * @param[in]  requestLen     The size of the request in bytes.
 * @param[in]  request        The SSPI request.
 * @param[in]  numExtraParams The number of elements in extraParams.
 * @param[in]  extraParams    Any optional, additional paramaters to the
 *                            function. Currently none are supported, so this
 *                            must be NULL.
 * @param[out] id             An identifier to use when validating the
 *                            response.
 * @param[out] challengeLen   The length of the challenge in bytes.
 * @param[out] challenge      The SSPI challenge to send to the client. Must be
 *                            freed with VGAuth_FreeBuffer().
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a NULL argument.
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_GenerateSSPIChallenge(VGAuthContext *ctx,
                             size_t requestLen,
                             const unsigned char *request,
                             int numExtraParams,
                             const VGAuthExtraParams *extraParams,
                             unsigned int *id,
                             size_t *challengeLen,
                             unsigned char **challenge)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == request) || (NULL == challengeLen) ||
       (NULL == challenge) || (NULL == id)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   return VGAuthGenerateSSPIChallengeImpl(ctx, requestLen, request, id,
                                          challengeLen, challenge);
}


/*
 ******************************************************************************
 * VGAuth_ValidateSSPIResponse --                                        */ /**
 *
 * @brief Validates an SSPI response.
 *
 * The client should use InitializeSecurityContext() to generate the response
 * from the challenge returned by VGAuth_GenerateSSPIChallenge().
 * The response must be received within a per-system configurable timeout,
 * or the challenge will be discarded, causing @a id to no longer be valid.
 *
 * For more information, see the MSDN documentation on SSPI.
 *
 * @remark On Windows, the function must be called by the local system account
 *         or an account from the administrators group.
 *
 * @param[in]  ctx            The VGAuthContext. Must be the same VGAuthContext
 *                            as was passed to the corresponding call to
 *                            VGAuth_GenerateSSPIChallenge().
 * @param[in]  id             The identifier returned by
 *                            VGAuth_GenerateSSPIChallenge() when the challenge
 *                            was generated.
 * @param[in]  responseLen    The length of the response in bytes.
 * @param[in]  response       The response to be validated.
 * @param[in]  numExtraParams The number of elements in extraParams.
 * @param[in]  extraParams    Any optional, additional paramaters to the
 *                            function. Currently none are supported, so this
 *                            must be NULL.
 * @param[out] handle         The resulting handle representing the user.
 *                            Must be freed with VGAuth_UserHandleFree().
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a NULL argument.
 * @retval VGAUTH_E_AUTHENTICATION_DENIED If the response fails validation.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_ValidateSSPIResponse(VGAuthContext *ctx,
                            unsigned int id,
                            size_t responseLen,
                            const unsigned char *response,
                            int numExtraParams,
                            const VGAuthExtraParams *extraParams,
                            VGAuthUserHandle **handle)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == response) || (NULL == handle)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   return VGAuthValdiateSSPIResponseImpl(ctx, id, responseLen, response,
                                         handle);
}


/*
 ******************************************************************************
 * VGAuth_ValidateSamlBearerToken --                                     */ /**
 *
 * @brief Authenticate using a SAML bearer token.
 *
 * Takes a SAML bearer token and determines whether that token is valid
 * and whether the principal specified in the "Subject" field is trusted
 * given the current identity provider store for the requested user.
 *
 * The token is valid if:
 * <ol>
 *    <li>it is well formed and conforms the SAML assertion schema,</li>
 *    <li>any conditions specified in the assertion, including any
 *        "NotBefore" or "NotOnOrAfter" information, must be true,</li>
 *    <li>the Subject element contains a SubjectConfirmation element and the
 *        SubjectConfirmation method is "bearer," and</li>
 *    <li>the assertion is correctly signed by a certificate contained within
 *        the assertion.</li>
 * </ol>
 *
 * The principal is trusted if
 * <ol>
 *    <li>the issuer of the token has certificate where a chain of trust can
 *        be established to an identity provider certificate in the local
 *        user's identity provider store, and</li>
 *    <li>the subject named in the token is on the list of trusted principals
 *        associated with the matching identity provider certificate or that
 *        identity provider certificate allows any principal to be
 *        authenticated.</li>
 * </ol>
 *
 * @remark Supported @a extraParams:
 *         VGAUTH_PARAM_VALIDATE_INFO_ONLY, which must have the value
 *         VGAUTH_PARAM_VALUE_TRUE or VGAUTH_PARAM_VALUE_FALSE.
 *         If set, SAML token validation is done, but the returned
 *         @a handle cannot be used for impersonation or ticket
 *         creation.
 *
 *         VGAUTH_PARAM_SAML_HOST_VERIFIED, which must have the value
 *         VGAUTH_PARAM_VALUE_TRUE or VGAUTH_PARAM_VALUE_FALSE.
 *         If set, the SAML token has been verified by the host
 *         and this service will skip that step when validating.
 *
 * @param[in]  ctx            The VGAuthContext.
 * @param[in]  samlToken      The SAML token to be validated.
 * @param[in]  userName       The user to authenticate as. Optional.
 *                            If the user is not specified, the mapped
 *                            identities files will be used to determine
 *                            which user to authenticate as, based on the
 *                            token issuer's certificiate and the token's
 *                            subject.
 * @param[in]  numExtraParams The number of elements in extraParams.
 * @param[in]  extraParams    Any optional, additional paramaters to the
 *                            function.
 * @param[out] handle         The resulting handle representing the user
 *                            associated with the username.
 *                            Must be freed with VGAuth_UserHandleFree().
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @retval VGAUTH_E_SERVICE_NOT_RUNNING If the service cannot be contacted.
 * @retval VGAUTH_E_PERMISSION_DENIED If not called by superuser.
 * @retval VGAUTH_E_AUTHENTICATION_DENIED If the token is not validate or the
 *                                        principal is not trusted.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */


VGAuthError
VGAuth_ValidateSamlBearerToken(VGAuthContext *ctx,
                               const char *samlToken,
                               const char *userName,
                               int numExtraParams,
                               const VGAuthExtraParams *extraParams,
                               VGAuthUserHandle **handle)
{
   VGAuthError err;
   VGAuthUserHandle *newHandle = NULL;
   gboolean validateOnly;
   gboolean hostVerified;

   /*
    * arg check
    */
   if ((NULL == ctx) || (NULL == samlToken) || (NULL == handle)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   *handle = NULL;

   if (!g_utf8_validate(samlToken, -1, NULL)) {
      Warning("%s: SAML token is not valid UTF-8.\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if ((NULL != userName) && !g_utf8_validate(userName, -1, NULL)) {
      Warning("%s: Username is not valid UTF-8.\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if ((NULL != userName) && !Usercheck_UsernameIsLegal(userName)) {
      Warning("Username '%s' contains invalid characters\n", userName);
      return VGAUTH_E_INVALID_ARGUMENT;
   }


   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuthGetBoolExtraParam(numExtraParams, extraParams,
                                 VGAUTH_PARAM_VALIDATE_INFO_ONLY,
                                 FALSE,
                                 &validateOnly);
   if (VGAUTH_E_OK != err) {
      return err;
   }
   err = VGAuthGetBoolExtraParam(numExtraParams, extraParams,
                                 VGAUTH_PARAM_SAML_HOST_VERIFIED,
                                 FALSE,
                                 &hostVerified);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuth_SendValidateSamlBearerTokenRequest(ctx,
                                                   validateOnly,
                                                   hostVerified,
                                                   samlToken,
                                                   userName,
                                                   &newHandle);
   if (VGAUTH_E_OK != err) {
      goto done;
   }

   *handle = newHandle;

done:

   return err;
}


/*
 ******************************************************************************
 * VGAuth_ValidateUsernamePassword --                                    */ /**
 *
 * @brief Validates a username/password, and returns a handle associated with
 * that user.
 *
 * Note that on Windows an empty password will not be accepted unless Group
 * Policy has been changed to accept it. See Microsoft knowledge base article
 * number 303846 for more information.
 *
 * @remark On a non-Windows OS, the function must be called by root.
 *         On Windows, the function must be called by the local system account
 *         or an account in the administrators group.
 *
 * @param[in]  ctx            The VGAuthContext.
 * @param[in]  userName       The username to be validated.
 * @param[in]  password       The password to be validated.
 * @param[in]  numExtraParams The number of elements in extraParams.
 * @param[in]  extraParams    Any optional, additional paramaters to the
 *                            function. Currently none are supported, so this
 *                            must be NULL.
 * @param[out] handle         The resulting handle representing the user
 *                            associated with @a userName.
 *                            Must be freed with VGAuth_UserHandleFree().
 *
 * @retval VGAUTH_E_AUTHENTICATION_DENIED If @a userName cannot be looked up,
 *     or @a password is not correct for @a userName.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_ValidateUsernamePassword(VGAuthContext *ctx,
                                const char *userName,
                                const char *password,
                                int numExtraParams,
                                const VGAuthExtraParams *extraParams,
                                VGAuthUserHandle **handle)
{
   VGAuthError err;

   if ((NULL == ctx) || (NULL == userName) || (NULL == password) || (NULL == handle)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!g_utf8_validate(userName, -1, NULL)) {
      Warning("User not in UTF-8\n");
      return VGAUTH_E_INVALID_ARGUMENT;
   }
   if (!g_utf8_validate(password, -1, NULL)) {
      Warning("Password not in UTF-8\n");
      return VGAUTH_E_INVALID_ARGUMENT;
   }
   if (userName[0] == '\0') {
      Warning("Empty Username\n");
      return VGAUTH_E_INVALID_ARGUMENT;
   }
   if (!Usercheck_UsernameIsLegal(userName)) {
      Warning("Username '%s' contains invalid characters\n", userName);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuthValidateUsernamePasswordImpl(ctx,
                                            userName,
                                            password,
                                            handle);

   if (VGAUTH_E_OK == err) {
      VGAuth_AuditEvent(ctx,
                        TRUE,
                        SU_(auth.password.valid,
                            "Username and password successfully validated for '%s'"),
                        userName);
   } else {
      VGAuth_AuditEvent(ctx,
                        FALSE,
                        SU_(auth.password.invalid,
                            "Username and password mismatch for '%s'"),
                        userName);
   }

   return err;
}
0707010000042D000081A40000000000000000000000016822550500003311000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/authPosix.c /*********************************************************
 * Copyright (C) 2011-2017, 2019, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file authPosix.h
 *
 * Posix user authentication.
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h> // for access, crypt, etc.

/*
 * USE_PAM should be defined in the makefile, since it impacts
 * what system libraries (-ld, -lcrypt) will be needed.
 *
 * XXX non-PAM code doesn't enforce a delay after failure to
 * slow down a brute-force attack.
 */
#ifdef USE_PAM
#  include <security/pam_appl.h>
#  include <dlfcn.h>
#else
# ifdef __APPLE__
#  error - Apple not supported.
# else
#  include <shadow.h>
# endif
#endif

#include <pwd.h>

#if defined(sun)
#  include <crypt.h>
#endif

#include "VGAuthInt.h"


#ifdef USE_PAM
#if defined(sun)
#define CURRENT_PAM_LIBRARY	"libpam.so.1"
#else
#define CURRENT_PAM_LIBRARY	"libpam.so.0"
#endif

static typeof(&pam_start) dlpam_start;
static typeof(&pam_end) dlpam_end;
static typeof(&pam_authenticate) dlpam_authenticate;
static typeof(&pam_setcred) dlpam_setcred;
static typeof(&pam_acct_mgmt) dlpam_acct_mgmt;
static typeof(&pam_strerror) dlpam_strerror;
#if 0  /* These three functions are not used yet */
static typeof(&pam_open_session) dlpam_open_session;
static typeof(&pam_close_session) dlpam_close_session;
static typeof(&pam_chauthtok) dlpam_chauthtok;
#endif

static struct {
   void       **procaddr;
   const char  *procname;
} authPAMImported[] = {
#define IMPORT_SYMBOL(x) { (void **)&dl##x, #x }
   IMPORT_SYMBOL(pam_start),
   IMPORT_SYMBOL(pam_end),
   IMPORT_SYMBOL(pam_authenticate),
   IMPORT_SYMBOL(pam_setcred),
   IMPORT_SYMBOL(pam_acct_mgmt),
   IMPORT_SYMBOL(pam_strerror),
#undef IMPORT_SYMBOL
};

static void *authPamLibraryHandle = NULL;


/*
 ******************************************************************************
 * AuthLoadPAM --                                                        */ /**
 *
 * Loads and initializes PAM library.
 *
 * @return TRUE on success
 *
 ******************************************************************************
 */

static gboolean
AuthLoadPAM(void)
{
   void *pam_library;
   int i;

   if (authPamLibraryHandle) {
      return TRUE;
   }
   pam_library = dlopen(CURRENT_PAM_LIBRARY, RTLD_NOW | RTLD_GLOBAL);
   if (!pam_library) {
      /*
       * XXX do we even try to configure the pam libraries?
       * potential nightmare on all the possible guest OSes
       */

      Warning("System PAM libraries are unusable: %s\n", dlerror());

      return FALSE;
   }
   for (i = 0; i < sizeof(authPAMImported)/sizeof(*authPAMImported); i++) {
      void *symbol = dlsym(pam_library, authPAMImported[i].procname);

      if (!symbol) {
         Warning("PAM library does not contain required function: %s\n",
                 dlerror());
         dlclose(pam_library);
         return FALSE;
      }

      *(authPAMImported[i].procaddr) = symbol;
   }

   authPamLibraryHandle = pam_library;
   Log("PAM up and running.\n");

   return TRUE;
}

/*
 * Holds the username & password for the PAM_conversation callbacks.
 */
typedef struct PamData {
   const char *username;
   const char *password;
} PamData;


/*
 ******************************************************************************
 * PAM_conv --                                                           */ /**
 *
 * PAM conversation function.  This is passed to pam_start
 * and is used by pam to provide communication between the
 * application and loaded modules.  See pam_conv(3) for details.
 *
 * @param[in]  num_msg      Number of messages to process.
 * @param[in]  msg          The messages.
 * @param[out] resp         Responses to the messages.
 * @param[in]  appdata_ptr  Application data (username/password).
 *
 * @return PAM_SUCCESS on success, the appropriate PAM error on failure.
 *
 ******************************************************************************
 */

#if defined(sun)
static int
PAM_conv(int num_msg,
         struct pam_message **msg,
         struct pam_response **resp,
         void *appdata_ptr)
#else
static int
PAM_conv(int num_msg,
         const struct pam_message **msg,
         struct pam_response **resp,
         void *appdata_ptr)
#endif
{
   PamData *pd = (PamData *) appdata_ptr;
   int count;
   struct pam_response *reply = calloc(num_msg, sizeof(struct pam_response));

   if (!reply) {
      return PAM_CONV_ERR;
   }

   for (count = 0; count < num_msg; count++) {
      switch (msg[count]->msg_style) {
      case PAM_PROMPT_ECHO_ON:
         reply[count].resp_retcode = PAM_SUCCESS;
         reply[count].resp = pd->username ? strdup(pd->username) : NULL;
         /* PAM frees resp */
         break;
      case PAM_PROMPT_ECHO_OFF:
         reply[count].resp_retcode = PAM_SUCCESS;
         reply[count].resp = pd->password ? strdup(pd->password) : NULL;
         /* PAM frees resp */
         break;
      case PAM_TEXT_INFO:
         reply[count].resp_retcode = PAM_SUCCESS;
         reply[count].resp = NULL;
         /* ignore it... */
         break;
      case PAM_ERROR_MSG:
         reply[count].resp_retcode = PAM_SUCCESS;
         reply[count].resp = NULL;
         /* Must be an error of some sort... */
         break;
      default:
         while (--count >= 0) {
            free(reply[count].resp);
         }
         free(reply);

         return PAM_CONV_ERR;
      }
   }

   *resp = reply;

   return PAM_SUCCESS;
}

static struct pam_conv PAM_conversation = {
    &PAM_conv,
    NULL
};
#endif /* USE_PAM */


/*
 ******************************************************************************
 * VGAuthValidateUsernamePasswordImpl --                                 */ /**
 *
 * Validates a username/password.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  userName   The username to be validated.
 * @param[in]  password   The password to be validated.
 * @param[out] handle     The resulting handle representing the user
 *                        associated with the username.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuthValidateUsernamePasswordImpl(VGAuthContext *ctx,
                                   const char *userName,
                                   const char *password,
                                   VGAuthUserHandle **handle)
{
#ifdef USE_PAM
   pam_handle_t *pamh;
   int pam_error;
   PamData pd;
   VGAuthError err;
#else
   struct passwd *pwd;
#endif

#ifdef USE_PAM
   if (!AuthLoadPAM()) {
      return VGAUTH_E_FAIL;
   }

   pd.username = userName;
   pd.password = password;
   PAM_conversation.appdata_ptr = &pd;
   pam_error = dlpam_start(ctx->applicationName,
                           userName,
                           &PAM_conversation, &pamh);
   if (pam_error != PAM_SUCCESS) {
      Warning("Failed to start PAM (error: %d).\n", pam_error);
      return VGAUTH_E_FAIL;
   }

   pam_error = dlpam_authenticate(pamh, 0);
   if (pam_error == PAM_SUCCESS) {
      pam_error = dlpam_acct_mgmt(pamh, 0);
      if (pam_error == PAM_SUCCESS) {
         pam_error = dlpam_setcred(pamh, PAM_ESTABLISH_CRED);
      }
   }
   dlpam_end(pamh, pam_error);
   if (pam_error != PAM_SUCCESS) {
      switch (pam_error) {
         /*
          * Most PAM errors get mapped to VGAUTH_E_AUTHENTICATION_DENIED,
          * but some are mapped into VGAUTH_E_FAIL.
          */
         case PAM_OPEN_ERR:
         case PAM_SYMBOL_ERR:
         case PAM_SERVICE_ERR:
         case PAM_SYSTEM_ERR:
         case PAM_BUF_ERR:
         case PAM_NO_MODULE_DATA:
         case PAM_CONV_ERR:
         case PAM_ABORT:
#ifndef sun   /* The following error codes are undefined on Solaris. */
         case PAM_BAD_ITEM:
         case PAM_CONV_AGAIN:
         case PAM_INCOMPLETE:
#endif
            err = VGAUTH_E_FAIL;
            break;

         default:
            err = VGAUTH_E_AUTHENTICATION_DENIED;
            break;

      }
      Warning("PAM error: %s (%d), mapped to VGAuth error "VGAUTHERR_FMT64"\n",
              dlpam_strerror(pamh, pam_error), pam_error, err);
      return err;
   }

#else /* !USE_PAM */

   setpwent(); // XXX Is this needed?
   pwd = getpwnam(userName);
   endpwent(); // XXX Is this needed?

   if (!pwd) {
      // No such user
      return VGAUTH_E_AUTHENTICATION_DENIED;
   }

   if (*pwd->pw_passwd != '\0') {
      const char *passwd = pwd->pw_passwd;
      const char *crypt_pw;

      // looks like a shadow password, so use it instead
      if (strcmp(passwd, "x") == 0) {
         struct spwd *sp = getspnam(userName);
         if (sp) {
            passwd = sp->sp_pwdp;
         }
      }

      crypt_pw = crypt(password, passwd);
      if (!crypt_pw || (strcmp(crypt_pw, passwd) != 0)) {
         // Incorrect password
         return VGAUTH_E_AUTHENTICATION_DENIED;
      }

      // Clear out crypt()'s internal state, too.
      crypt("glurp", passwd);
   }
#endif /* !USE_PAM */

   return VGAuth_CreateHandleForUsername(ctx, userName,
                                         VGAUTH_AUTH_TYPE_NAMEPASSWORD,
                                         NULL, handle);
}


/*
 ******************************************************************************
 * VGAuthInitAuthenticationPlatform --                                   */ /**
 *
 * Initializes any POSIX-specific authentication resources.
 *
 * @param[in]  ctx        The VGAuthContext to initialize.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuthInitAuthenticationPlatform(VGAuthContext *ctx)
{
   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuthShutdownAuthenticationPlatform --                                   */ /**
 *
 * Cleans up any POSIX-specific authentication resources.
 *
 * @param[in]  ctx        The VGAuthContext to shutdown.
 *
 ******************************************************************************
 */

void
VGAuthShutdownAuthenticationPlatform(VGAuthContext *ctx)
{
}


/*
 ******************************************************************************
 * VGAuthGenerateSSPIChallengeImpl --                                    */ /**
 *
 * Not supported.
 *
 * @param[in]  ctx            The VGAuthContext.
 * @param[in]  requestLen     The size of the request in bytes.
 * @param[in]  request        The SSPI request.
 * @param[out] id             An identifier to use when validating the response.
 * @param[out] challengeLen   The length of the challenge in bytes.
 * @param[out] challenge      The SSPI challenge to send to the client.
 *
 * @return VGAUTH_E_UNSUPPORTED
 *
 ******************************************************************************
 */

VGAuthError
VGAuthGenerateSSPIChallengeImpl(VGAuthContext *ctx,
                                size_t requestLen,
                                const unsigned char *request,
                                unsigned int *id,
                                size_t *challengeLen,
                                unsigned char **challenge)
{
   return VGAUTH_E_UNSUPPORTED;
}


/*
 ******************************************************************************
 * VGAuthValdiateSSPIResponseImpl --                                     */ /**
 *
 * Not supported.
 *
 * @param[in]  ctx           The VGAuthContext.
 * @param[in]  id            Used to identify which SSPI challenge
 *                           this response correspends to.
 * @param[in]  responseLen   The length of the response in bytes.
 * @param[in]  response      The SSPI response.
 * @param[out] userHandle    The resulting handle representing the user
 *                           associated with the username.
 *
 * @return VGAUTH_E_UNSUPPORTED
 *
 ******************************************************************************
 */

VGAuthError
VGAuthValdiateSSPIResponseImpl(VGAuthContext *ctx,
                               unsigned int id,
                               size_t responseLen,
                               const unsigned char *response,
                               VGAuthUserHandle **userHandle)
{
   return VGAUTH_E_UNSUPPORTED;
}
   0707010000042E000081A4000000000000000000000001682255050000319B000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/comm.c  /*********************************************************
 * Copyright (C) 2011-2016, 2019, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file comm.c
 *
 * Client communcation support
 */

#include <stdlib.h>
#include <string.h>

#include "VGAuthInt.h"
#include "VGAuthProto.h"
#include "usercheck.h"


/*
 ******************************************************************************
 * VGAuth_IsConnectedToServiceAsUser --                                  */ /**
 *
 * Checks if the context has a connection to the service.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  userName   The user.
 *
 * @return TRUE if the user has a connection to the service.
 *
 ******************************************************************************
 */

gboolean
VGAuth_IsConnectedToServiceAsUser(VGAuthContext *ctx,
                                  const char *userName)
{
   /*
    * If we have a connection, and the user is correct, then we're
    * set.
    */
   return ctx->comm.connected &&
      Usercheck_CompareByName(userName, ctx->comm.userName);
}


/*
 ******************************************************************************
 * VGAuth_IsConnectedToServiceAsAnyUser --                               */ /**
 *
 * Checks if the context has a connection to the service.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return TRUE if there is a connection to the service.
 *
 ******************************************************************************
 */

gboolean
VGAuth_IsConnectedToServiceAsAnyUser(VGAuthContext *ctx)
{
   return ctx->comm.connected;
}


/*
 ******************************************************************************
 * VGAuth_InitConnection --                                              */ /**
 *
 * Initializes the connection.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_InitConnection(VGAuthContext *ctx)
{
#ifdef _WIN32
   ctx->comm.hPipe = INVALID_HANDLE_VALUE;
#else
   /*
    * Be sure to init to a bad fd.  Closing stdin is Bad.
    */
   ctx->comm.sock = -1;
#endif

   ctx->comm.connected = FALSE;
   ctx->comm.sequenceNumber = 0;

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_CloseConnection --                                             */ /**
 *
 * Closes the connection.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_CloseConnection(VGAuthContext *ctx)
{
   VGAuthError err = VGAUTH_E_OK;

   if (NULL == ctx) {
      return err;
   }


   ctx->comm.sequenceNumber = 0;

   g_free(ctx->comm.userName);
   ctx->comm.userName = NULL;

#ifdef _WIN32
   if (ctx->comm.hPipe != INVALID_HANDLE_VALUE) {
      CloseHandle(ctx->comm.hPipe);
      ctx->comm.hPipe = INVALID_HANDLE_VALUE;
   }
#else
   if (ctx->comm.sock >= 0) {
      close(ctx->comm.sock);
   }
#endif

   g_free(ctx->comm.pipeName);
   ctx->comm.pipeName = NULL;

#ifdef UNITTEST
   if (ctx->comm.fileTest) {
      fclose(ctx->comm.testFp);
   }
#endif

   ctx->comm.connected = FALSE;

   return err;
}


/*
 ******************************************************************************
 * VGAuth_ConnectToServiceAsUser --                                      */ /**
 *
 * Makes the connection to the public service, handles the initial
 * handshake, then connects to the user-specific pipe.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  userName   The user.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_ConnectToServiceAsUser(VGAuthContext *ctx,
                              const char *userName)
{
   VGAuthError err = VGAUTH_E_OK;
   gchar *pipeName = NULL;
   VGAuthContext *pubCtx = NULL;

   if (VGAuth_IsConnectedToServiceAsUser(ctx, userName)) {
      Debug("%s: already connected as '%s'\n", __FUNCTION__, userName);
      // treat this as a no-op
      goto done;
   }

   /*
    * If currently connected (presumably as another user), close down
    * and re-open.
    */

   VGAuth_CloseConnection(ctx);

   /*
    * Make a temp context to connect to the public pipe.
    */
   pubCtx = g_malloc0(sizeof(VGAuthContext));
   if (NULL == pubCtx) {
      return VGAUTH_E_OUT_OF_MEMORY;
   }

   pubCtx->comm.pipeName = g_strdup(SERVICE_PUBLIC_PIPE_NAME);
   pubCtx->comm.userName = g_strdup(SUPERUSER_NAME);
   err = VGAuth_InitConnection(pubCtx);
   if (err != VGAUTH_E_OK) {
      Warning("%s: Failed to init public pipe connection "VGAUTHERR_FMT64X"\n",
              __FUNCTION__, err);
      goto done;
   }

   err = VGAuth_NetworkConnect(pubCtx);
   if (err != VGAUTH_E_OK) {
      Warning("%s: Failed to connect to public pipe "VGAUTHERR_FMT64X"\n",
              __FUNCTION__, err);
      goto done;
   }


   /*
    * The public pipe should be owned by superUser, otherwise
    * we have a spoofer.
    */
   if (!VGAuth_NetworkValidatePublicPipeOwner(pubCtx)) {
      Warning("%s: security violation!  public pipe is not owned by super-user!\n",
              __FUNCTION__);
      err = VGAUTH_E_SECURITY_VIOLATION;
      goto done;
   }

   /*
    * SessionRequest will get back a user-specific pipeName.
    */
   err = VGAuth_SendSessionRequest(pubCtx, userName, &pipeName);
   if (err != VGAUTH_E_OK) {
      Warning("%s: Failed to initiate session "VGAUTHERR_FMT64X"\n",
              __FUNCTION__, err);
      goto done;
   }

   /*
    * Set up for the user pipe.
    */
   ctx->comm.userName = g_strdup(userName);
   ctx->comm.pipeName = g_strdup(pipeName);

   err = VGAuth_NetworkConnect(ctx);
   if (err != VGAUTH_E_OK) {
      Warning("%s: Failed to connect to private pipe "VGAUTHERR_FMT64X"\n",
              __FUNCTION__, err);
      goto done;
   }

   /*
    * Do initial handshake.
    */
   err = VGAuth_SendConnectRequest(ctx);
   if (err != VGAUTH_E_OK) {
      Warning("%s: Failed to connect user session "VGAUTHERR_FMT64X"\n",
              __FUNCTION__, err);
      goto done;
   }

   /*
    * The user-private connection is good to go.
    */

done:
   VGAuth_CloseConnection(pubCtx);
   g_free(pubCtx);

   g_free(pipeName);
   return err;
}


/*
 ******************************************************************************
 * VGAuth_ConnectToServiceAsCurrentUser --                               */ /**
 *
 * Makes the connection to the public service, handles the initial
 * handshake, then connects to the user-specific pipe.
 *
 * This is a wrapper on VGAuth_ConnectToServiceAsUser() using the
 * current user.  This is useful for requests like QueryMappedCerts
 * which can be done as any user; we know the current user will be able to
 * connect to its private pipe to the service.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_ConnectToServiceAsCurrentUser(VGAuthContext *ctx)
{
   VGAuthError err;
   gchar *currentUsername;

   currentUsername = VGAuth_GetCurrentUsername();
   if (NULL == currentUsername) {
      return VGAUTH_E_FAIL;
   }

   err = VGAuth_ConnectToServiceAsUser(ctx, currentUsername);

   g_free(currentUsername);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_CommSendData --                                                */ /**
 *
 * Sends a NUL-terminated string to the service.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  packet     The data to be sent.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_CommSendData(VGAuthContext *ctx,
                    gchar *packet)
{
   return VGAuth_NetworkWriteBytes(ctx, strlen(packet), packet);
}


/*
 ******************************************************************************
 * VGAuth_CommReadData --                                                */ /**
 *
 * Reads some data from the service.  This will just be the next chunk
 * read off the wire, and may not be a complete packet.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[out] userName   The length of the data read.
 * @param[out] response   The data read.  Should be g_free()d by caller.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_CommReadData(VGAuthContext *ctx,
                    gsize *len,
                    gchar **response)
{
   VGAuthError err = VGAUTH_E_OK;
#ifdef UNITTEST
   if (ctx->comm.fileTest) {
      char buf[2];
      char *rBuf;

      ctx->comm.sequenceNumber = -1;
      /*
       * This is absurdly inefficient, but it lets me put a bunch
       * of test replies in a single file.
       */
      rBuf = fgets(buf, 2, ctx->comm.testFp);
      if (NULL == rBuf) {     // EOF
         *len = 0;
         err = VGAUTH_E_COMM;
         goto quit;
      }
      *len = 1;
      *response = g_strdup(buf);
   } else if (ctx->comm.bufTest) {
      // XXX can make this reply with chunks for extra testing
      if (ctx->comm.bufLoc == ctx->comm.bufLen) {
         *len = 0;
         err = VGAUTH_E_COMM;
         goto quit;
      }
      *response = g_strdup(ctx->comm.testBuffer);
      *len = ctx->comm.bufLen;
      ctx->comm.bufLoc = ctx->comm.bufLen;
   } else
#endif
   {
      err = VGAuth_NetworkReadBytes(ctx, len, response);
   }
#ifdef UNITTEST
quit:
#endif
   return err;
}


#ifdef UNITTEST
/*
 ******************************************************************************
 * VGAuthComm_SetTestFileInput --                                        */ /**
 *
 * Sets up the communication channel as a file for use in testing.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  filename   The file containing the test input.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuthComm_SetTestFileInput(VGAuthContext *ctx,
                            const char *filename)
{
   VGAuthError err = VGAUTH_E_OK;

   ctx->comm.testFp = g_fopen(filename, "r");
   if (NULL == ctx->comm.testFp) {
      fprintf(stderr, "Failed to open test input file '%s'\n", filename);
      err = VGAUTH_E_COMM;
   } else {
      ctx->comm.fileTest = TRUE;
   }
   return err;
}


/*
 ******************************************************************************
 * VGAuthComm_SetTestBufferInput --                                      */ /**
 *
 * Sets up the communication channel as a buffer for use in testing.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  buffer     The buffer containing the test input.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuthComm_SetTestBufferInput(VGAuthContext *ctx,
                              const char *buffer)
{
   VGAuthError err;
   size_t bufLen;

   ctx->comm.bufTest = TRUE;
   ctx->comm.bufLoc = 0;
   bufLen = strlen(buffer);

   if (bufLen > sizeof ctx->comm.testBuffer - 1) {
      fprintf(stderr, "Test buffer too large.\n");
      err = VGAUTH_E_INVALID_ARGUMENT;
   } else {
      ctx->comm.bufLen = bufLen;
      strncpy(ctx->comm.testBuffer, buffer, sizeof ctx->comm.testBuffer - 1);
      ctx->comm.testBuffer[sizeof ctx->comm.testBuffer - 1] = '\0';
      err = VGAUTH_E_OK;
   }

   return err;
}
#endif

 0707010000042F000081A4000000000000000000000001682255050000519A000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/common.c    /*********************************************************
 * Copyright (c) 2011-2017, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * @file common.c
 *
 * Common client functionality
 */


#include "VGAuthCommon.h"
#include "VGAuthInt.h"
#include "buildNumber.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifndef _WIN32
#define  PAM_DIRECTORY  "/etc/pam.d"
#include <errno.h>
#endif

PrefHandle gPrefs = NULL;


/*
 ******************************************************************************
 * VGAuthValidateExtraParamsImpl --                                      */ /**
 *
 * Checks that the number of elements is sane and that all the keys and
 * values are valid.
 *
 * @param[in]  funcName    The name of the calling function.
 * @param[in]  numParams   The number of elements in the params array.
 * @param[in]  params      The params to validate.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT If one of the extra parameters is invalid
 *                                   or the number of extra parameters is
 *                                   inconsistent with the provided array.
 * @reval VGAUTH_E_OK If the extra parameters validate successfully.
 *
 ******************************************************************************
 */

VGAuthError
VGAuthValidateExtraParamsImpl(const char *funcName,
                              int numParams,
                              const VGAuthExtraParams *params)
{
   int i;

   if ((numParams < 0) || (numParams > 0 && NULL == params)) {
      Warning("%s: invalid number of parameters: %d.\n", funcName, numParams);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   for (i = 0; i < numParams; i++) {
      if (NULL == params[i].name) {
         Warning("%s: incomplete ExtraParam setting at index %d.\n",
                 funcName, i);
         return VGAUTH_E_INVALID_ARGUMENT;
      }
      if (!g_utf8_validate(params[i].name, -1, NULL) ||
          ((params[i].value != NULL) &&
           !g_utf8_validate(params[i].value, -1, NULL))) {
         Warning("%s: non-UTF-8 parameter at index %d.\n",funcName, i);
         return VGAUTH_E_INVALID_ARGUMENT;
      }
   }

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuthGetBoolExtraParamImpl --                                        */ /**
 *
 * Get the boolean value of the specified extra param in the params array.
 *
 * @param[in]  funcName    The name of the calling function.
 * @param[in]  numParams   The number of elements in the params array.
 * @param[in]  params      The params array to get param value from.
 * @param[in]  paramName   The param name to get its value.
 * @param[in]  defValue    The param default value if not set in the array.
 * @param[out] paramValue  Returned param value, TRUE or FALSE.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT If incomplete arguments are passed in,
 *                                   the specified extra parameter is passed
 *                                   in the array multiple times or the
 *                                   parameter value is invalid.
 * @reval VGAUTH_E_OK If no error is encountered.
 *
 ******************************************************************************
 */

VGAuthError
VGAuthGetBoolExtraParamImpl(const char *funcName,
                            int numParams,
                            const VGAuthExtraParams *params,
                            const char *paramName,
                            gboolean defValue,
                            gboolean *paramValue)
{
   gboolean paramSet = FALSE;
   int i;

   if ((numParams < 0) || (numParams > 0 && NULL == params)) {
      Warning("%s: invalid number of parameters: %d.\n", funcName, numParams);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (NULL == paramName || NULL == paramValue) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   *paramValue = defValue;

   for (i = 0; i < numParams; i++) {
      if (g_strcmp0(params[i].name, paramName) == 0) {
         // only allow it to be set once
         if (paramSet) {
            Warning("%s: extraParam '%s' passed multiple times.\n",
                    funcName, params[i].name);
            return VGAUTH_E_INVALID_ARGUMENT;
         }
         if (params[i].value) {
            if (g_ascii_strcasecmp(VGAUTH_PARAM_VALUE_TRUE,
                                   params[i].value) == 0) {
               *paramValue = TRUE;
               paramSet = TRUE;
            } else if (g_ascii_strcasecmp(VGAUTH_PARAM_VALUE_FALSE,
                                          params[i].value) == 0) {
               *paramValue = FALSE;
               paramSet = TRUE;
            } else {
               Warning("%s: Unrecognized value '%s' for boolean param %s\n",
                       funcName, params[i].value, params[i].name);
               return VGAUTH_E_INVALID_ARGUMENT;
            }
         } else {
            return VGAUTH_E_INVALID_ARGUMENT;
         }
      }
   }

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_Init --                                                        */ /**
 *
 * @brief Initializes the library, and specifies any configuration information.
 *
 * @a applicationName is the name of the application (argv[0]), and is needed
 * on Posix operating systems to initialize @b pam(3).
 *
 * @remark Can be called by any user.
 *
 * @param[in]  applicationName   The name of the application.
 * @param[in]  numExtraParams    The number of elements in extraParams.
 * @param[in]  extraParams       Any optional, additional paramaters to the
 *                               function. Currently none are supported, so
 *                               this must be NULL.
 * @param[out] ctx               The new VGAuthContext.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @retval VGAUTH_E_OUT_OF_MEMORY For an out-of-memory failure.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_Init(const char *applicationName,
            int numExtraParams,
            const VGAuthExtraParams *extraParams,
            VGAuthContext **ctx)
{
   VGAuthContext *newCtx = NULL;
   VGAuthError err = VGAUTH_E_OK;
   static gboolean firstTime = TRUE;
   int i;

   /*
    * The application name cannot be an empty string.
    */
   if ((NULL == applicationName) || ('\0' == *applicationName) ||
       (NULL == ctx)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   *ctx = NULL;

   /* XXX process any options */

   if (!g_utf8_validate(applicationName, -1, NULL)) {
      Warning("%s: invalid applicationName\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   newCtx = g_malloc0(sizeof(VGAuthContext));
   if (NULL == newCtx) {
      return VGAUTH_E_OUT_OF_MEMORY;
   }

   newCtx->applicationName = g_strdup(applicationName);
   newCtx->isImpersonating = FALSE;
   newCtx->impersonatedUser = NULL;

   /*
    * Only init prefs, i18n and auditing once.
    */
   if (firstTime) {
      gboolean logSuccessAudits;
      gchar *msgCatalog;

      gPrefs = Pref_Init(VGAUTH_PREF_CONFIG_FILENAME);
      logSuccessAudits = Pref_GetBool(gPrefs,
                                      VGAUTH_PREF_AUDIT_SUCCESS,
                                      VGAUTH_PREF_GROUP_NAME_AUDIT,
                                      TRUE);
      msgCatalog = Pref_GetString(gPrefs,
                                  VGAUTH_PREF_LOCALIZATION_DIR,
                                  VGAUTH_PREF_GROUP_NAME_LOCALIZATION,
                                  VGAUTH_PREF_DEFAULT_LOCALIZATION_CATALOG);

      I18n_BindTextDomain(VMW_TEXT_DOMAIN, NULL, msgCatalog);
      g_free(msgCatalog);
      Audit_Init("VGAuth", logSuccessAudits);

      firstTime = FALSE;
   }

   newCtx->numExtraParams = numExtraParams;
   newCtx->extraParams = g_malloc0(sizeof(*newCtx->extraParams) *
                                   numExtraParams);
   for (i = 0; i < numExtraParams; i++) {
      newCtx->extraParams[i].name = g_strdup(extraParams[i].name);
      newCtx->extraParams[i].value = g_strdup(extraParams[i].value);
   }

   err = VGAuth_InitConnection(newCtx);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuthInitAuthentication(newCtx);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   *ctx = newCtx;

   Log("VGAuth '%s' initialized for application '%s'.  Context created at %p\n",
       BUILD_NUMBER,
       newCtx->applicationName, newCtx);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_Shutdown --                                                    */ /**
 *
 * @brief Cleans up a context and any associated data.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT If @a ctx is NULL.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_Shutdown(VGAuthContext *ctx)
{
   int i;

   if (NULL == ctx) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   VGAuthShutdownAuthentication(ctx);

   VGAuth_CloseConnection(ctx);

   for (i = 0; i < ctx->numExtraParams; i++) {
      g_free(ctx->extraParams[i].name);
      g_free(ctx->extraParams[i].value);
   }
   g_free(ctx->extraParams);

   Log("VGAuth context at %p shutdown for application '%s'\n",
       ctx, ctx->applicationName);
   g_free(ctx->applicationName);
   g_free(ctx);

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_InstallClient --                                               */ /**
 *
 * @brief Provides any OS specific support that may be required:
 * system config entries, registry tweaks, etc.
 *
 * Note that on Posix, PAM configuration files are case-insensitive.  The
 * application name will be lower-cased to create a PAM configuration
 * filename.
 *
 * Note that there can be issues running 32 bit code in a 64 bit OS.
 * On at least one tested system, a 32 bit test on a 64 bit OS failed
 * to load PAM modules with ELF errors.  Users should always try to match
 * the native OS.  The vgauth installer should enforce this.
 *
 * @remark Must be called by root.
 *
 * @param[in]  ctx               The VGAuthContext.
 * @param[in]  numExtraParams    The number of elements in extraParams.
 * @param[in]  extraParams       Any optional, additional paramaters to the
 *                               function. Currently none are supported, so
 *                               this must be NULL.
 *
 * @retval VGAUTH_E_PERMISSION_DENIED If not called as root.
 * @retval VGAUTH_ERROR_SET_SYSTEM_ERRNO If a syscall fails.  Use
 *    VGAUTH_ERROR_EXTRA_ERROR on the return value to get the errno.
 * @retval VGAUTH_E_INVALID_ARGUMENT If @a ctx is NULL or one of the extra
 *                                   parameters is invalid.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_InstallClient(VGAuthContext *ctx,
                     int numExtraParams,
                     const VGAuthExtraParams *extraParams)
{
   VGAuthError err;

   if (NULL == ctx) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

#ifdef _WIN32
   return VGAUTH_E_OK;
#elif defined(__linux__)
   {
   gchar *fileName;
   gchar *lowAppName;
   FILE *fp;
   /*
    * XXX
    *
    * This has worked for currently tested distros, but could
    * be improved.  I stole it from the tools installer, but they've
    * since improved it further to use 'include' statements, and do different
    * things depending on the distro.  It'd also be nice to somehow
    * share code with the installer.  See bug 889444.
    */

   static char *fileContents =
"#%PAM-1.0\n"
"# \n"
"# This file was generated by vgauth\n"
"# \n"
"auth           sufficient   pam_unix2.so shadow\n"
"auth           sufficient   pam_unix.so shadow\n"
"auth           required     pam_unix_auth.so shadow\n"
"account        sufficient   pam_unix2.so\n"
"account        sufficient   pam_unix.so\n"
"account        required     pam_unix_auth.so\n";

   if (!VGAuth_IsRunningAsRoot()) {
      return VGAUTH_E_PERMISSION_DENIED;
   }

   /*
    * PAM will convert a mixed-case application name into all lower case,
    * so make the lowercase version of the appName.
    */
   lowAppName = g_ascii_strdown(ctx->applicationName, -1);

   fileName = g_strdup_printf(PAM_DIRECTORY"/%s", lowAppName);

   /*
    * XXX add NO_CLOBBER check to catch some app that already has the same name?
    * Some concern that we can't do anything about it on Windows.
    */

   fp = g_fopen(fileName, "w+");
   if (NULL == fp) {
      VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
      Warning("%s: Unable to open PAM config file %s for creation\n",
              __FUNCTION__, fileName);
      goto done;
   }
   if (g_fprintf(fp, "%s", fileContents) < 0) {
      VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
      Warning("%s: Unable to fprintf() PAM config file contents\n",
              __FUNCTION__);
      goto done;
   }
   err = VGAUTH_E_OK;
done:
   if (fp != NULL) {
      if (fclose(fp) != 0) {
         if (err == VGAUTH_E_OK) {
            VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
         }
         Warning("%s: Unable to close PAM config file\n", __FUNCTION__);
      }
   }
   g_free(fileName);
   g_free(lowAppName);

   return err;
   }
#elif defined(sun)
   return VGAUTH_E_OK;
#else
#error VGAuth_InstallClient unsupported on this platform.
#endif
}


/*
 ******************************************************************************
 * VGAuth_UninstallClient --                                             */ /**
 *
 * @brief Removes any OS specific support that may be required:
 * system config entries, registry tweaks, etc.
 *
 * @remark Must be called by root.
 *
 * @param[in]  ctx               The VGAuthContext.
 * @param[in]  numExtraParams    The number of elements in extraParams.
 * @param[in]  extraParams       Any optional, additional paramaters to the
 *                               function. Currently none are supported, so
 *                               this must be NULL.
 *
 * @retval VGAUTH_E_PERMISSION_DENIED If not called as root.
 * @retval VGAUTH_ERROR_SET_SYSTEM_ERRNO If a syscall fails.  Use
 *    VGAUTH_ERROR_EXTRA_ERROR on the return value to get the @a errno.
 * @retval VGAUTH_E_INVALID_ARGUMENT If @a ctx is NULL or if one of the extra
 *                                   parameters is invalid.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_UninstallClient(VGAuthContext *ctx,
                       int numExtraParams,
                       const VGAuthExtraParams *extraParams)
{
   VGAuthError err;

   if (NULL == ctx) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

#ifdef _WIN32
   return VGAUTH_E_OK;
#elif defined(__linux__)
   {
   gchar *fileName;
   gchar *lowAppName;

   if (!VGAuth_IsRunningAsRoot()) {
      return VGAUTH_E_PERMISSION_DENIED;
   }

   /*
    * PAM will convert a mixed-case application name into all lower case,
    * so make the lowercase version of the file.
    */
   lowAppName = g_ascii_strdown(ctx->applicationName, -1);
   fileName = g_strdup_printf(PAM_DIRECTORY"/%s", lowAppName);

   if (g_unlink(fileName) != 0) {
      VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
      Warning("%s: Unable to remove PAM config file '%s'\n",
              __FUNCTION__, fileName);
      goto done;
   }

   err = VGAUTH_E_OK;
done:
   g_free(fileName);
   g_free(lowAppName);
   return err;
   }
#elif defined(sun)
   return VGAUTH_E_OK;
#else
#error VGAuth_UninstallClient unsupported on this platform.
#endif   // linux
}


/*
 ******************************************************************************
 * VGAuth_SetLogHandler --                                               */ /**
 *
 * @brief Sets the global log handler.
 *
 * All VGAuth and glib errors, warnings and debug messages will go through
 * @a logFunc.
 *
 * @li VGAuth errors will use the glib loglevel @b G_LOG_LEVEL_WARNING.
 * @li VGAuth information messages will use the glib loglevel @b G_LOG_LEVEL_MESSAGE.
 * @li VGAuth debug messages will use the glib loglevel @b G_LOG_LEVEL_DEBUG.
 *
 * Note that any bad utf8 string arguments will be passed through
 * unmodified, so an error handler may want to confidence check the data.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  logFunc          The function to be called for logging messages.
 * @param[in]  userData         The data to be passed to the logFunc.
 * @param[in]  numExtraParams   The number of elements in extraParams.
 * @param[in]  extraParams      Any optional, additional paramaters to the
 *                              function. Currently none are supported, so
 *                              this must be NULL.
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a NULL @a logFunc or if one of the
 *                                   extra parameters is invalid.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SetLogHandler(VGAuthLogFunc logFunc,
                     void *userData,
                     int numExtraParams,
                     const VGAuthExtraParams *extraParams)
{
   VGAuthError err;

   if (NULL == logFunc) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   /*
    * This makes everything using glib, no matter what log domain is
    * used, go through logFunc.
    */
   (void) g_log_set_default_handler((GLogFunc) logFunc, userData);

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_FreeBuffer --                                                  */ /**
 *
 * Frees a buffer returned from the VGAuth library.
 *
 * @param[in]  buffer  The buffer to be freed.
 *
 ******************************************************************************
 */

void
VGAuth_FreeBuffer(void *buffer)
{
   if (buffer != NULL) {
      g_free(buffer);
   }
}


/*
 ******************************************************************************
 * VGAuth_AuditEvent --                                                  */ /**
 *
 * This is a wrapper on AuditEvent to deal with the issue that an
 * app could have multiple VGAuthContexts, with differing applicationNames.
 * Rather than re-initing the Audit system each time (which can be racy
 * without adding locks), we'll just init once, then prepend the
 * applicationName to each message.
 *
 * @param[in] ctx         The VGAuthContext.
 * @param[in] isSuccess   If true, the message is a successful event.
 * @param[in] fmt         The format message for the event.
 *
 ******************************************************************************
 */

void
VGAuth_AuditEvent(VGAuthContext *ctx,
                  gboolean isSuccess,
                  const char *fmt,
                  ...)

{
   gchar *msg;
   va_list args;

   /*
    * If we ever expose a VGAuthExtraParams to toggle successful audits,
    * we'll have to look at isSuccess here and possibly drop successful
    * events.
    */
   va_start(args, fmt);
   msg = g_strdup_vprintf(fmt, args);
   Audit_Event(isSuccess, "%s: %s", ctx->applicationName, msg);
   va_end(args);

   g_free(msg);
}
  07070100000430000081A40000000000000000000000016822550500001795000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/errortext.c /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file errortext.c
 *
 * Error descriptions.
 */

#include "VGAuthCommon.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef struct VGAuthErrorCodeInfo {
   VGAuthError err;
   const char *name;
   const char *msg;
} VGAuthErrorCodeInfo;

#define _DEFINE_VGAUTH_ERR(err, str) {err, #err, str},

/*
 * This is the global table that maps error codes to human-readable
 * descriptions.
 *
 * Note, that the UI requires strings to never end with
 * a period. So, if a string contains several sentences, then
 * the last sentence does not end with a period. See Bug 52793.
 *
 * It would be nice to be able to combine this with the doxygen comments
 * in VGAuthError.h.  However, this can be spun as a feature, since the
 * doxygen commentary is intended for developers, while these are intended
 * for users.
 * See the differences in the VGAUTH_E_SYSTEM_ERROR descriptions as an example.
 */

const static VGAuthErrorCodeInfo vgauthErrorCodeInfoList[] =
{
   _DEFINE_VGAUTH_ERR(VGAUTH_E_OK, "The operation was successful")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_INVALID_ARGUMENT, "One of the parameters was invalid")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_INVALID_CERTIFICATE, "The certificate is not a well-formed x509 document")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_PERMISSION_DENIED, "Insufficient permissions")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_OUT_OF_MEMORY, "Out of memory")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_COMM, "Internal communication error between library and service")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_NOTIMPLEMENTED, "Not implemented")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_NOT_CONNECTED, "Not connected to the service")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_VERSION_MISMATCH, "Service/library version mismatch")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_SECURITY_VIOLATION, "Potential security violation detected")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_CERT_ALREADY_EXISTS, "The certificate already exists")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_AUTHENTICATION_DENIED, "Authentication denied")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_INVALID_TICKET, "Invalid ticket")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_MULTIPLE_MAPPINGS, "The certificate was found associated with more than one user, or a chain contained multiple matches against the mapping file")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_ALREADY_IMPERSONATING, "The context is already impersonating")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_NO_SUCH_USER, "User cannot be found")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_SERVICE_NOT_RUNNING, "Service not running")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_SYSTEM_ERRNO, "An OS-specific operation failed")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_SYSTEM_WINDOWS, "An OS-specific operation failed")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_TOO_MANY_CONNECTIONS,
                      "The user exceeded its max number of connections")
   _DEFINE_VGAUTH_ERR(VGAUTH_E_UNSUPPORTED, "The operation is not supported")


   /*
    * Add new error definitions above.
    *
    * VGAUTH_E_FAIL must appear last, to catch any unspecfied errors.
    */

   _DEFINE_VGAUTH_ERR(VGAUTH_E_FAIL, "Unknown error")
};


/*
 ******************************************************************************
 * VGAuthErrorInfo --                                                    */ /**
 *
 * Returns the full description of the error.
 *
 * @param[in]  err        The VGAuthError.
 *
 * @return A VGAuthErrorInfo of the error.
 *
 ******************************************************************************
 */

static const VGAuthErrorCodeInfo *
VGAuthGetErrorInfo(VGAuthError err)
{
   const VGAuthErrorCodeInfo *errorInfo;

   err = VGAUTH_ERROR_CODE(err);

   for (errorInfo = vgauthErrorCodeInfoList;
        errorInfo->err != err && errorInfo->err != VGAUTH_E_FAIL;
        ++errorInfo);

   return errorInfo;
}


/*
 ******************************************************************************
 * VGAuth_GetErrorText --                                                */ /**
 *
 * Returns explanatory text for an error code.  This returns a reference
 * to a static global string, do not free it.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  errCode    The VGAuthError code.
 * @param[in]  language   The language to use in RFC-1766 language code (currently unused).
 *
 * @return A description of @a errCode.
 *
 ******************************************************************************
 */

const char *
VGAuth_GetErrorText(VGAuthError errCode,
                    const char *language)
{
   const VGAuthErrorCodeInfo *errorInfo;

   errorInfo = VGAuthGetErrorInfo(errCode);

   /*
    * XXX Add error localization.
    */

   return errorInfo->msg;
}


/*
 ******************************************************************************
 * VGAuth_GetErrorName --                                                */ /**
 *
 * Returns the name of the error code.  This returns a reference
 * to a static global string, do not free it.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  errCode   The VGAuthError code.
 *
 * @return The name of @a errCode.
 *
 ******************************************************************************
 */

const char *
VGAuth_GetErrorName(VGAuthError errCode)
{
   return VGAuthGetErrorInfo(errCode)->name;
}
   07070100000431000081A400000000000000000000000168225505000035FD000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/impersonate.c   /*********************************************************
 * Copyright (C) 2011-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file impersonate.c
 *
 * Impersonation APIs
 */

#include "VGAuthInt.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#endif

/*
 ******************************************************************************
 * VGAuth_IsRunningAsRoot --                                             */ /**
 *
 * Checks if the user is running as root/system.
 *
 * @return TRUE if the user is running as root.
 *
 ******************************************************************************
 */

gboolean
VGAuth_IsRunningAsRoot(void)
{
   gboolean isRoot = FALSE;
#ifndef _WIN32
   uid_t uid;

   uid = getuid();

   if (0 == uid) {
      isRoot = TRUE;
   }
#endif

   return isRoot;
}


/*
 ******************************************************************************
 * VGAuth_CreateHandleForUsername --                                     */ /**
 *
 * Creates a new VGAuthUserHandle associated with @a userName.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  userName   The user.
 * @param[in]  token      The access token on Windows. The ownership is passed
 *                        to the returned UserHandle object if successful.
 *                        The parameter is ignored on other platforms.
 * @param[out] handle     The new handle.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_CreateHandleForUsername(VGAuthContext *ctx,
                               const char *userName,
                               const VGAuthUserHandleType type,
                               HANDLE token,
                               VGAuthUserHandle **handle)
{
   VGAuthError err = VGAUTH_E_OK;
   VGAuthUserHandle *newHandle;

   if (!g_utf8_validate(userName, -1, NULL)) {
      Warning("%s: invalid username\n", __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   newHandle = g_malloc0(sizeof(VGAuthUserHandle));

   newHandle->userName = g_strdup(userName);
   newHandle->details.type = type;
   newHandle->flags = VGAUTH_HANDLE_FLAG_NONE;

   switch (type) {
   case VGAUTH_AUTH_TYPE_NAMEPASSWORD:
   case VGAUTH_AUTH_TYPE_SSPI:
   case VGAUTH_AUTH_TYPE_SAML:
      newHandle->flags = VGAUTH_HANDLE_FLAG_NORMAL;
      break;
   case VGAUTH_AUTH_TYPE_SAML_INFO_ONLY:
      break;
   default:
      Warning("%s: trying to create handle with unsupported type %d\n",
              __FUNCTION__, type);
   }

#ifdef _WIN32
   newHandle->token = token;
   newHandle->hProfile = INVALID_HANDLE_VALUE;
#endif

   newHandle->refCount = 1;
   *handle = newHandle;

   Debug("%s: Created handle %p\n", __FUNCTION__, newHandle);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_SetUserHandleSamlInfo --                                       */ /**
 *
 * Sets the SAML data associated with a userhandle.
 *
 * @param[in]  ctx         The VGAuthContext.
 * @param[in]  handle      The handle being updated.
 * @param[in]  samlSubject The SAML subject to be associated with the handle.
 * @param[in]  ai          The AliasInfo to be associated with the handle.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SetUserHandleSamlInfo(VGAuthContext *ctx,
                             VGAuthUserHandle *handle,
                             const char *samlSubject,
                             VGAuthAliasInfo *ai)
{
   ASSERT((handle->details.type == VGAUTH_AUTH_TYPE_SAML)
          || (handle->details.type == VGAUTH_AUTH_TYPE_SAML_INFO_ONLY));

   handle->details.val.samlData.subject = g_strdup(samlSubject);
   VGAuth_CopyAliasInfo(ai, &(handle->details.val.samlData.aliasInfo));

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_UserHandleUsername --                                          */ /**
 *
 * @brief Returns the user associated with @a handle.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  handle     The handle.
 * @param[out] userName   The user. Must be freed by the caller using
 *                        VGAuth_FreeBuffer().
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_UserHandleUsername(VGAuthContext *ctx,
                          VGAuthUserHandle *handle,
                          char **userName)                       // OUT
{
   if ((NULL == ctx) || (NULL == handle) || (NULL == userName)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   *userName = g_strdup(handle->userName);

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_UserHandleType --                                              */ /**
 *
 * @brief Returns the type of @a handle.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  handle     The handle.
 *
 * @return The handle type or VGAUTH_AUTH_TYPE_UNKNOWN on error.
 *
 ******************************************************************************
 */

VGAuthUserHandleType
VGAuth_UserHandleType(VGAuthContext *ctx,
                      VGAuthUserHandle *handle)
{
   if ((NULL == ctx) || (NULL == handle)) {
      Warning("%s: Invalid arguments\n", __FUNCTION__);
      return VGAUTH_AUTH_TYPE_UNKNOWN;
   }

   return handle->details.type;
}


/*
 ******************************************************************************
 * VGAuth_UserHandleSamlData --                                          */ /**
 *
 * @brief Returns the user associated with @a handle.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  ctx                     The VGAuthContext.
 * @param[in]  handle                  The handle.
 * @param[out] samlTokenSubject        The SAML subject in the SAML token
 *                                     used to create the userHandle.
 *                                     Must be freed by the caller using
 *                                     VGAuth_FreeBuffer().
 * @param[out] matchedAliasInfo        The VGAuthAliasInfo used to validate
 *                                     the SAML token used in the userHandle.
 *                                     Must be freed by calling
 *                                     VGAuth_FreeAliasInfo()
 *
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_UserHandleSamlData(VGAuthContext *ctx,
                          VGAuthUserHandle *handle,
                          char **samlTokenSubject,
                          VGAuthAliasInfo **matchedAliasInfo)
{
   VGAuthAliasInfo *ai;

   if ((NULL == ctx) || (NULL == handle) || (NULL == samlTokenSubject) ||
       (NULL == matchedAliasInfo)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if ((handle->details.type != VGAUTH_AUTH_TYPE_SAML) &&
       (handle->details.type != VGAUTH_AUTH_TYPE_SAML_INFO_ONLY)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   ai = g_malloc0(sizeof(VGAuthAliasInfo));

   VGAuth_CopyAliasInfo(&(handle->details.val.samlData.aliasInfo), ai);

   *samlTokenSubject = g_strdup(handle->details.val.samlData.subject);
   *matchedAliasInfo = ai;

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_UserHandleFree --                                              */ /**
 *
 * @brief Frees a #VGAuthUserHandle.
 *
 * @remark Can be called by any user.
 *
 * @param[in]  handle     The handle to be freed.  No-op if NULL.
 *
 ******************************************************************************
 */

void
VGAuth_UserHandleFree(VGAuthUserHandle *handle)
{
   if (NULL == handle) {
      return;
   }

   ASSERT(handle->refCount > 0);
   if (handle->refCount <= 0) {
      Warning("%s: invalid user handle reference count %d\n",
              __FUNCTION__, handle->refCount);
      return;
   }

   handle->refCount--;

   if (handle->refCount > 0) {
      return;
   }

   WIN32_ONLY(CloseHandle(handle->token));

   g_free(handle->userName);

   if (handle->details.type == VGAUTH_AUTH_TYPE_SAML ||
       handle->details.type == VGAUTH_AUTH_TYPE_SAML_INFO_ONLY) {
      g_free(handle->details.val.samlData.subject);
      VGAuth_FreeAliasInfoContents(&(handle->details.val.samlData.aliasInfo));
   }

   Debug("%s: Freeing handle %p\n", __FUNCTION__, handle);

   g_free(handle);
}


/*
 ******************************************************************************
 * VGAuth_Impersonate --                                                 */ /**
 *
 * @brief Starts impersonating the user represented by @a handle.
 *
 * Note that this will change the entire process on Linux to the
 * user represented by the #VGAuthUserHandle (so it must be called by root).
 *
 * The effective uid/gid, @b $HOME, @b $USER and @b $SHELL are changed;
 * however, no @b $SHELL startup files are run, so you cannot assume that
 * other environment variables have been changed.
 *
 * Calls to the API cannot be nested; call VGAuth_EndImpersonation()
 * before another call to VGAuth_Impersonate() is made.
 *
 * @remark Must be called by superuser.
 *         One @a extraParams is supported for Windows:
 *         VGAUTH_PARAM_LOAD_USER_PROFILE, which must have the value
 *         VGAUTH_PARAM_VALUE_TRUE or VGAUTH_PARAM_VALUE_FALSE.
 *         If set true, load user profile before impersonation.
 *
 * @param[in]  ctx             The VGAuthContext.
 * @param[in]  handle          The handle representing the user to be
 *                             impersonated.
 * @param[in]  numExtraParams  The number of elements in extraParams.
 * @param[in]  extraParams     Any optional, additional paramaters to the
 *                             function. Currently none are supported, so
 *                             this must be NULL.
 *
 * @retval VGAUTH_E_ALREADY_IMPERSONATING If the context is already impersonating.
 * @retval VGAUTH_E_INVALID_ARGUMENT For a bad argument.
 * @return VGAUTH_E_OK on success, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_Impersonate(VGAuthContext *ctx,
                   VGAuthUserHandle *handle,
                   int numExtraParams,
                   const VGAuthExtraParams *extraParams)
{
   VGAuthError err;
   gboolean loadUserProfile;

   if ((NULL == ctx) || (NULL == handle)) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!(handle->flags & VGAUTH_HANDLE_FLAG_CAN_IMPERSONATE)) {
      Warning("%s: called on handle that doesn't not support operation \n",
              __FUNCTION__);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   err = VGAuthValidateExtraParams(numExtraParams, extraParams);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   err = VGAuthGetBoolExtraParam(numExtraParams, extraParams,
                                 VGAUTH_PARAM_LOAD_USER_PROFILE,
                                 FALSE,
                                 &loadUserProfile);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   if (ctx->isImpersonating) {
      return VGAUTH_E_ALREADY_IMPERSONATING;
   }

   err = VGAuthImpersonateImpl(ctx,
                               handle,
                               loadUserProfile);
   if (VGAUTH_E_OK == err) {
      ctx->isImpersonating = TRUE;
      handle->refCount++;
      ctx->impersonatedUser = handle;
   }

   return err;
}


/*
 ******************************************************************************
 * VGAuth_EndImpersonation --                                            */ /**
 *
 * @brief Ends the current impersonation.
 *
 * Restores the process to superUser, and resets @b $USER, @b $HOME and @b $SHELL.
 *
 * @remark Must be called by superuser.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success or no-op, VGAuthError on failure.
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_EndImpersonation(VGAuthContext *ctx)
{
   VGAuthError err;

   if (NULL == ctx) {
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   if (!ctx->isImpersonating) {
      Debug("%s: not currently impersonating; ignoring\n", __FUNCTION__);
      return VGAUTH_E_OK;
   }

   err = VGAuthEndImpersonationImpl(ctx);
   if (VGAUTH_E_OK == err) {
      ctx->isImpersonating = FALSE;
      VGAuth_UserHandleFree(ctx->impersonatedUser);
      ctx->impersonatedUser = NULL;
   }

   return err;
}
   07070100000432000081A40000000000000000000000016822550500001B2A000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/impersonateLinux.c  /*********************************************************
 * Copyright (C) 2011-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file impersonateLinux.c
 *
 * Linux impersonation APIs
 */

/*
 * Pull in setresuid()/setresgid() if possible.  Do this first, to
 * be sure we don't get unistd.h w/o _GNU_SOURCE defined.
 */
#define  _GNU_SOURCE
#include <unistd.h>

#if !defined(__FreeBSD__) && !defined(sun) && !defined(__APPLE__)
#include <asm/param.h>
#include <locale.h>
#include <sys/stat.h>
#endif
#include <sys/types.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>

#include "VGAuthInt.h"


#if (__GLIBC__ == 2) && (__GLIBC_MINOR__ < 3)
#include <sys/syscall.h>
/*
 * Implements the setresuid and setresgid system calls (they are not wrapped
 * by glibc until 2.3.2).
 */

static inline int
setresuid(uid_t ruid,
          uid_t euid,
          uid_t suid)
{
   return syscall(SYS_setresuid, ruid, euid, suid);
}


static inline int
setresgid(gid_t ruid,
          gid_t euid,
          gid_t suid)
{
   return syscall(SYS_setresgid, ruid, euid, suid);
}
#endif


/*
 ******************************************************************************
 * VGAuthImpersonateImpl --                                              */ /**
 *
 * Does the real work to start impersonating the user represented by handle.
 *
 * Note that this will change the entire process on Linux to the
 * user represented by the VGAuthUserHandle (so it must be called by root).
 *
 * The effective uid/gid, $HOME, $USER and $SHELL are changed;
 * however, no $SHELL startup files are run, so you cannot assume that
 * other environment variables have been changed.
 *
 * @param[in]  ctx              The VGAuthContext.
 * @param[in]  handle           The handle representing the user to be
 *                              impersonated.
 * @param[in]  loadUserProfile  Unused parameter.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuthImpersonateImpl(VGAuthContext *ctx,
                      VGAuthUserHandle *handle,
                      UNUSED_PARAM(gboolean loadUserProfile))
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   gid_t root_gid;
   int error;
   int ret;

   if ((error = getpwuid_r(0, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      /*
       * getpwuid_r() and getpwnam_r() can return a 0 (success) but not
       * set the return pointer (ppw) if there's no entry for the user,
       * according to POSIX 1003.1-2003.
       */
      Warning("Failed to lookup root (%d)\n", error);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   root_gid = ppw->pw_gid;

   if ((error = getpwnam_r(handle->userName, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      Warning("Failed to lookup user '%s' (%d)\n", handle->userName, error);
      // XXX add VGAUTH_E_INVALIDUSER ???
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   // first change group
   ret = setresgid(ppw->pw_gid, ppw->pw_gid, root_gid);
   if (ret < 0) {
      Warning("Failed to setresgid() for user %s (%d)\n", handle->userName, errno);
      return VGAUTH_E_FAIL;
   }
   ret = initgroups(ppw->pw_name, ppw->pw_gid);
   if (ret < 0) {
      Warning("Failed to initgroups() for user %s (%d)\n", handle->userName, errno);
      goto failure;
   }
   // now user
   ret = setresuid(ppw->pw_uid, ppw->pw_uid, 0);
   if (ret < 0) {
      Warning("Failed to setresuid() for user %s (%d)\n", handle->userName, errno);
      goto failure;
   }

   // set env
   setenv("USER", ppw->pw_name, 1);
   setenv("HOME", ppw->pw_dir, 1);
   setenv("SHELL", ppw->pw_shell, 1);

   return VGAUTH_E_OK;

failure:
   // try to restore on error
   VGAuth_EndImpersonation(ctx);

   return VGAUTH_E_FAIL;
}


/*
 ******************************************************************************
 * VGAuthEndImpersonationImpl --                                         */ /**
 *
 * Ends the current impersonation, restoring the process to superUser,
 * and resetting $USER, $HOME and $SHELL.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuthEndImpersonationImpl(VGAuthContext *ctx)
{
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   int error;
   int ret;

   if ((error = getpwuid_r(0, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      Warning("Failed to lookup root (%d)\n", error);
      return VGAUTH_E_INVALID_ARGUMENT;
   }

   // first change back user
   ret = setresuid(ppw->pw_uid, ppw->pw_uid, 0);
   if (ret < 0) {
      Warning("Failed to setresuid() for root (%d)\n", errno);
      return VGAUTH_E_FAIL;
   }

   // now group
   ret = setresgid(ppw->pw_gid, ppw->pw_gid, ppw->pw_gid);
   if (ret < 0) {
      Warning("Failed to setresgid() for root (%d)\n", errno);
      return VGAUTH_E_FAIL;
   }
   ret = initgroups(ppw->pw_name, ppw->pw_gid);
   if (ret < 0) {
      Warning("Failed to initgroups() for root (%d)\n", errno);
      return VGAUTH_E_FAIL;
   }

   // set env
   setenv("USER", ppw->pw_name, 1);
   setenv("HOME", ppw->pw_dir, 1);
   setenv("SHELL", ppw->pw_shell, 1);

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_GetCurrentUsername --                                          */ /**
 *
 * Returns the name of the current effective user.  Must be g_free()d.
 *
 * @return The name of the current user, NULL on failure.
 *
 ******************************************************************************
 */

gchar *
VGAuth_GetCurrentUsername(void)
{
   uid_t uid = geteuid();
   char buffer[BUFSIZ];
   struct passwd pw;
   struct passwd *ppw = &pw;
   int error;
   gchar *userName = NULL;

   if ((error = getpwuid_r(uid, &pw, buffer, sizeof buffer, &ppw)) != 0 ||
       !ppw) {
      Warning("Failed to look up username for current uid (%d)\n", error);
      return userName;
   }

   userName = g_strdup(ppw->pw_name);

   return userName;
}
  07070100000433000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003300000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n    07070100000434000081A40000000000000000000000016822550500000488000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/de.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "Benutzername und Kennwort für '%1$s' stimmen nicht überein"
auth.password.valid = "Benutzername und Kennwort wurden erfolgreich für '%1$s' bestätigt"
auth.sspi.badid = "Versuch der Authentifizierung mithilfe einer ungültigen oder abgelaufenen SSPI-Anfrage-ID: %1$u"
07070100000435000081A40000000000000000000000016822550500000454000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/en.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "Username and password mismatch for '%1$s'"
auth.password.valid = "Username and password successfully validated for '%1$s'"
auth.sspi.badid = "Attempt to authenticate using an invalid or expired SSPI challenge ID: %1$u"
07070100000436000081A40000000000000000000000016822550500000487000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/es.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "El nombre de usuario y la contraseña de '%1$s' no coinciden"
auth.password.valid = "El nombre de usuario y la contraseña de '%1$s' se han validado correctamente"
auth.sspi.badid = "Intento de autenticación utilizando un SSPI challenge ID no válido o caducado: %1$u"
 07070100000437000081A4000000000000000000000001682255050000048F000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/fr.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "Non-concordance du nom d'utilisateur et du mot de passe pour '%1$s'"
auth.password.valid = "Validation réussie du nom d'utilisateur et du mot de passe pour '%1$s'"
auth.sspi.badid = "Tentative d'authentification à l'aide d'un ID de demande SSPI non valide ou expiré : %1$u"
 07070100000438000081A4000000000000000000000001682255050000046A000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/it.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "Nome utente e password non corrispondenti per '%1$s'"
auth.password.valid = "Nome utente e password convalidati correttamente per '%1$s'"
auth.sspi.badid = "Tentativo di autenticazione utilizzando un ID test SSPI non valido o scaduto: %1$u"
  07070100000439000081A40000000000000000000000016822550500000496000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/ja.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "'%1$s' のユーザー名とパスワードが一致しません"
auth.password.valid = "'%1$s' のユーザー名とパスワードが正しく検証されました"
auth.sspi.badid = "無効または期限切れの SSPI チャレンジ ID を使用して認証を試みます: %1$u"
  0707010000043A000081A400000000000000000000000168225505000004AA000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/ko.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "'%1$s'에 대해 사용자 이름과 암호가 일치하지 않습니다."
auth.password.valid = "'%1$s'에 대해 사용자 이름 및 암호가 유효한 것으로 확인되었습니다."
auth.sspi.badid = "잘못되었거나 만료된 SSPI 챌린지 ID를 사용하여 인증하려고 합니다. %1$u"
  0707010000043B000081A40000000000000000000000016822550500000445000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/zh_CN.vmsg ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "“%1$s”的用户名和密码不匹配"
auth.password.valid = "已成功验证“%1$s”的用户名和密码"
auth.sspi.badid = "尝试使用无效或过期的 SSPI 质询 ID 进行身份验证: %1$u"
   0707010000043C000081A4000000000000000000000001682255050000044D000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/l10n/zh_TW.vmsg ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

auth.password.invalid = "「%1$s」的使用者名稱與密碼不符"
auth.password.valid = "已成功驗證「%1$s」的使用者名稱和密碼"
auth.sspi.badid = "嘗試使用無效或到期的 SSPI 驗證識別碼進行驗證: %1$u"
   0707010000043D000081A40000000000000000000000016822550500001FF3000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/netPosix.c  /*********************************************************
 * Copyright (c) 2011-2016,2019,2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file netPosix.c
 *
 * Client posix networking
 */

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/unistd.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include "VGAuthInt.h"
#include "VGAuthLog.h"


/*
 ******************************************************************************
 * VGAuth_NetworkConnect --                                              */ /**
 *
 * Creates connection to pipe specified in ctx.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_NetworkConnect(VGAuthContext *ctx)
{
   VGAuthError err = VGAUTH_E_OK;
   int fd;
   int ret;
   struct sockaddr_un sockaddr;
   /*
    * For some reason, this is simply hardcoded in sys/un.h
    */
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX   108
#endif

   sockaddr.sun_family = AF_UNIX;

   fd = socket(AF_UNIX, SOCK_STREAM, 0);
   if (fd < 0) {
      VGAUTH_LOG_ERR_POSIX("socket() failed for %s", ctx->comm.pipeName);
      return VGAUTH_E_COMM;
   }

   /* Ignore return, returns the length of the source string */
   /* coverity[check_return] */
   g_strlcpy(sockaddr.sun_path, ctx->comm.pipeName, UNIX_PATH_MAX);

   do {
      ret = connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
   } while (ret == -1 && errno == EINTR);

   if (ret < 0) {
      int saveErrno = errno;

      VGAUTH_LOG_ERR_POSIX("connect() failed for %s", ctx->comm.pipeName);
      close(fd);
      /*
       * Assume that ENOENT means the service isn't running (or
       * its pipe has been deleted), and ECONNREFUSED means the service
       * is down, so we can give a more end-user helpful error code.
       */
      if ((ECONNREFUSED == saveErrno) || (ENOENT == saveErrno)) {
         return VGAUTH_E_SERVICE_NOT_RUNNING;
         /*
          * Pass up a permission failure.
          */
      } else if (EACCES == saveErrno) {
         return VGAUTH_E_PERMISSION_DENIED;
         /*
          * Treat anything else as a generic comm error.
          */
      } else {
         return VGAUTH_E_COMM;
      }
   }

   ctx->comm.sock = fd;
   ctx->comm.connected = TRUE;

   return err;
}


/*
 ******************************************************************************
 * VGAuth_NetworkValidatePublicPipeOwner --                              */ /**
 *
 * Security check -- validates that the pipe is owned by the super user,
 * to try to catch spoofing.
 *
 * @param[in]  ctx        The VGAuthContext.
 *
 * @return TRUE if the pipe is owned by ther proper user.
 *         FALSE otherwise or if failed.
 *
 ******************************************************************************
 */

gboolean
VGAuth_NetworkValidatePublicPipeOwner(VGAuthContext *ctx)
{
   gboolean retval = FALSE;
   int ret;

#ifdef __linux__
   struct ucred peerCred;
   socklen_t peerCredLen = sizeof peerCred;

   /*
    * On Linux, the SO_PEERCRED socket option will give us the PID,
    * effective UID, and GID of the peer (the server in this case).
    */

   ret = getsockopt(ctx->comm.sock, SOL_SOCKET, SO_PEERCRED, &peerCred,
                    &peerCredLen);
   if (ret < 0) {
      VGAUTH_LOG_ERR_POSIX("getsockopt() failed on %s", ctx->comm.pipeName);
      goto done;
   }

   retval = (peerCred.uid == 0);

done:

   return retval;

#else

   struct stat stbuf;

   /*
    * XXX: fstat() on a UNIX domain socket does not return the UID of the
    * file's owner, but the UID of the client process (i.e., us). Also,
    * SO_PEERCRED is only available on Linux. So, we are left with using
    * stat() the pipe's filename. This introduces TOCTOU issues, but at least
    * it gives us a cursory check against someone else spoofing the service.
    */
   ret = g_stat(ctx->comm.pipeName, &stbuf);
   if (ret < 0) {
      VGAUTH_LOG_ERR_POSIX("g_stat() failed on %s", ctx->comm.pipeName);
      goto done;
   }

   retval = (stbuf.st_uid == 0);

done:

   return retval;
#endif
}


/*
 ******************************************************************************
 * VGAuth_NetworkReadBytes --                                            */ /**
 *
 * Reads the available data on the connection.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[out] len        The length of the data.  0 if the connection is lost.
 * @param[out] buffer     The data.  Should be g_free()d.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_NetworkReadBytes(VGAuthContext *ctx,
                        gsize *len,
                        gchar **buffer)
{
   VGAuthError err = VGAUTH_E_OK;
   int ret;
#if NETWORK_FORCE_TINY_PACKETS
#define  READ_BUFSIZE   1
#else
#define  READ_BUFSIZE   10240
#endif
   char buf[READ_BUFSIZE];

   *len = 0;
   *buffer = NULL;

   do {
      ret = recv(ctx->comm.sock, buf, sizeof(buf), 0);
      if (ret == 0) {
         Warning("%s: EOF on socket\n", __FUNCTION__);
         return err;
      }
   } while (ret == -1 && errno == EINTR);

   if (ret < 0) {
      VGAUTH_LOG_ERR_POSIX("error reading from %s", ctx->comm.pipeName);
      return VGAUTH_E_COMM;
   }

   *buffer = g_strndup(buf, ret);
   *len = ret;

   return err;
}


/*
 ******************************************************************************
 * VGAuthIgnoreSigPipe --                                                */ /**
 *
 * Ignore the SIGPIPE
 *
 ******************************************************************************
 */

static void
VGAuthIgnoreSigPipe(void)
{
   static gboolean alreadySetup = FALSE;
   if (alreadySetup) {
      return;
   }

   signal(SIGPIPE, SIG_IGN);

   alreadySetup = TRUE;
}


/*
 ******************************************************************************
 * VGAuth_NetworkWriteBytes --                                           */ /**
 *
 * Writes bytes to the connection in the ctx.
 *
 * @param[in]  ctx        The VGAuthContext.
 * @param[in]  len        The length of the data.
 * @param[in]  buffer     The data.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_NetworkWriteBytes(VGAuthContext *ctx,
                         gsize len,
                         gchar *buffer)
{
   VGAuthError err = VGAUTH_E_OK;
   gsize sent = 0;
   int ret;

   if (len == 0) {
      Warning("%s: asked to send %d bytes; bad caller?\n",
              __FUNCTION__, (int) len);
      return err;
   }

   VGAuthIgnoreSigPipe();

   do {
retry:
#if NETWORK_FORCE_TINY_PACKETS
      ret = send(ctx->comm.sock, buffer + sent, 1, 0);
#else
      ret = send(ctx->comm.sock, buffer + sent, len - sent, 0);
#endif
      if (ret < 0) {
         if (EINTR == errno) {
            goto retry;
         }
         VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
         VGAUTH_LOG_ERR_POSIX("send() failed on %s", ctx->comm.pipeName);
         return err;
      }
      sent += ret;
   } while (sent < len);

   return err;
}
 0707010000043E000081A4000000000000000000000001682255050001275E000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vgauth/lib/proto.c /*********************************************************
 * Copyright (c) 2012-2017, 2019-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file proto.c
 *
 * Client/service protocol
 */
#ifndef _WIN32
#include <errno.h>
#endif

#include <stdlib.h>
#include <string.h>

#include <glib/gstdio.h>

#include "VGAuthInt.h"
#include "VGAuthProto.h"
#include "VGAuthLog.h"
#include "VGAuthUtil.h"
#include "usercheck.h"

/* cranks up parser debugging */
#define VGAUTH_PROTO_TRACE 0

/*
 * Reply types
 */
typedef enum {
   PROTO_REQUEST_UNKNOWN,
   PROTO_REPLY_ERROR,
   PROTO_REPLY_SESSION_REQ,
   PROTO_REPLY_CONN,
   PROTO_REPLY_ADDALIAS,
   PROTO_REPLY_REMOVEALIAS,
   PROTO_REPLY_QUERYALIASES,
   PROTO_REPLY_QUERYMAPPEDALIASES,
   PROTO_REPLY_CREATETICKET,
   PROTO_REPLY_VALIDATETICKET,
   PROTO_REPLY_REVOKETICKET,
   PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN,
} ProtoReplyType;

/*
 * Possible parse states for replies.
 */
typedef enum {
   PARSE_STATE_NONE,

   PARSE_STATE_SEQ,

   PARSE_STATE_ERROR,
   PARSE_STATE_ERROR_CODE,
   PARSE_STATE_ERROR_MSG,

   PARSE_STATE_REPLY,

   PARSE_STATE_VERSION,
   PARSE_STATE_PIPENAME,

   PARSE_STATE_PEMCERT,
   PARSE_STATE_CERTCOMMENT,

   PARSE_STATE_ALIAS,
   PARSE_STATE_ALIASINFO,
   PARSE_STATE_NAMEDSUBJECT,
   PARSE_STATE_ANYSUBJECT,
   PARSE_STATE_COMMENT,

   PARSE_STATE_MAPPEDALIAS,
   PARSE_STATE_SUBJECTS,

   PARSE_STATE_TICKET,

   PARSE_STATE_USERHANDLEINFO,
   PARSE_STATE_USERHANDLETYPE,
   PARSE_STATE_USERHANDLESAMLINFO,
   PARSE_STATE_USERHANDLESAMLSUBJECT,

   PARSE_STATE_USERNAME,
   PARSE_STATE_TOKEN,
   PARSE_STATE_CHALLENGE_EVENT,
} ProtoParseState;


/*
 * The reply structure.
 */
struct ProtoReply {
   gboolean complete;
   int sequenceNumber;

   /*
    * The client knows what its expecting back, which is
    * used as a confidence check against what's actually read,
    * as well as telling us what to allocate for complex replies.
    */
   ProtoReplyType expectedReplyType;

   /*
    * If its an error, this will be set instead.
    */
   ProtoReplyType actualReplyType;

   ProtoParseState parseState;

   VGAuthError errorCode;

   union {
      struct {
         gchar *errorMsg;
      } error;
      struct {
         int version;
         gchar *pipeName;
      } sessionReq;
      struct {
         gchar *challengeEvent;
      } connect;
      struct {
         int num;
         VGAuthUserAlias *uaList;
      } queryUserAliases;
      struct {
         int num;
         VGAuthMappedAlias *maList;
      } queryMappedAliases;
      struct {
         gchar *ticket;
      } createTicket;
      struct {
         gchar *userName;
         gchar *token;
         VGAuthUserHandleType type;
         gchar *samlSubject;
         VGAuthAliasInfo aliasInfo;
      } validateTicket;
      struct {
         gchar *userName;
         char *comment;
         gchar *token;
         gchar *samlSubject;
         VGAuthAliasInfo aliasInfo;
      } validateSamlBToken;
   } replyData;

#if VGAUTH_PROTO_TRACE
   gchar *rawData;
#endif
};


typedef struct ProtoReply ProtoReply;


#if VGAUTH_PROTO_TRACE

/*
 ******************************************************************************
 * ProtoSubjectToString --                                               */ /**
 *
 * Debugging.  Returns the name of a VGAuthSubject.
 *
 * @param[in]  subj         The VGAuthSubject to dump.
 *
 ******************************************************************************
 */

static const gchar *
ProtoSubjectToString(const VGAuthSubject *subj)
{
   if (VGAUTH_SUBJECT_NAMED == subj->type) {
      return subj->val.name;
   } else if (VGAUTH_SUBJECT_ANY == subj->type) {
      return "<ANY>";
   } else {
      return "<UNKNOWN>";
   }
}


/*
 ******************************************************************************
 * Proto_DumpReply --                                                    */ /**
 *
 * Debugging.  Spews a ProtoReply to stdout.
 *
 * @param[in]  reply        The reply to dump.
 *
 ******************************************************************************
 */

static void
Proto_DumpReply(ProtoReply *reply)
{
   int i;
   int j;
   VGAuthUserAlias *ua;
   VGAuthAliasInfo *ai;

   printf("raw data: %s\n", reply->rawData ? reply->rawData : "<none>");
   printf("complete: %d\n", reply->complete);
   printf("sequenceNumber: %d\n", reply->sequenceNumber);
   printf("expectedReplyType: %d\n", reply->expectedReplyType);
   printf("actualReplyType: %d\n", reply->actualReplyType);
   printf("error code: "VGAUTHERR_FMT64X"\n", reply->errorCode);

   switch (reply->actualReplyType) {
   case PROTO_REPLY_ERROR:
      printf("error message: '%s'\n", reply->replyData.error.errorMsg ? reply->replyData.error.errorMsg :  "<none>");
      break;
   case PROTO_REPLY_SESSION_REQ:
      printf("version #: %d\n", reply->replyData.sessionReq.version);
      printf("pipeName: '%s'\n", reply->replyData.sessionReq.pipeName);
      break;
   case PROTO_REPLY_CONN:
   case PROTO_REPLY_ADDALIAS:
   case PROTO_REPLY_REMOVEALIAS:
   case PROTO_REPLY_REVOKETICKET:
      break;
   case PROTO_REPLY_QUERYALIASES:
      printf("#%d UserAliases:\n", reply->replyData.queryUserAliases.num);
      for (i = 0; i < reply->replyData.queryUserAliases.num; i++) {
         ua = &(reply->replyData.queryUserAliases.uaList[i]);
         printf("permCert: '%s'\n", ua->pemCert);
         for (j = 0; j < ua->numInfos; j++) {
            ai = &(ua->infos[j]);
            printf("\tsubject: '%s'\n", ProtoSubjectToString(&(ai->subject)));
            printf("\tcomment: '%s'\n", ai->comment);
         }
      }
      break;
   case PROTO_REPLY_QUERYMAPPEDALIASES:
      printf("#%d identities:\n", reply->replyData.queryMappedAliases.num);
      for (i = 0; i < reply->replyData.queryMappedAliases.num; i++) {
         printf("pemCert: '%s'\n", reply->replyData.queryMappedAliases.maList[i].pemCert);
         for (j = 0; j < reply->replyData.queryMappedAliases.maList[i].numSubjects; j++) {
            printf("subject #%d: '%s'\n", j, ProtoSubjectToString(&reply->replyData.queryMappedAliases.maList[i].subjects[j]));
         }
         printf("mapped user: '%s'\n", reply->replyData.queryMappedAliases.maList[i].userName);
      }
      break;
   case PROTO_REPLY_CREATETICKET:
      printf("ticket '%s'\n", reply->replyData.createTicket.ticket);
      break;
   case PROTO_REPLY_VALIDATETICKET:
      printf("username: '%s'\n", reply->replyData.validateTicket.userName);
      printf("validate type: %d\n", reply->replyData.validateTicket.type);
      if (VGAUTH_AUTH_TYPE_SAML == reply->replyData.validateTicket.type) {
         printf("SAML subject: '%s'\n",
                reply->replyData.validateTicket.samlSubject);
         ai = &(reply->replyData.validateTicket.aliasInfo);
         printf("\tsubject: '%s'\n", ProtoSubjectToString(&(ai->subject)));
         printf("\tcomment: '%s'\n", ai->comment);
      }
      break;
   case PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN:
      printf("username: '%s'\n", reply->replyData.validateSamlBToken.userName);
      printf("SAML subject: '%s'\n",
             reply->replyData.validateTicket.samlSubject);
      ai = &(reply->replyData.validateTicket.aliasInfo);
      printf("\tsubject: '%s'\n", ProtoSubjectToString(&(ai->subject)));
      printf("\tcomment: '%s'\n", ai->comment);
      break;
   default:
      printf("no reply specific data\n");
      break;
   }
}
#endif      // VGAUTH_PROTO_TRACE


/*
 ******************************************************************************
 * Proto_ConcatXMLStrings --                                             */ /**
 *
 * Concatenates 2 XML strings and returns the new string.
 * g_free()s the two inputs.
 * Result must be g_free()d.
 *
 * @param[in]  str1     The first string.
 * @param[in]  str2     The second string.
 *
 * @return The new string.
 *
 ******************************************************************************
 */

static gchar *
Proto_ConcatXMLStrings(gchar *str1,
                       gchar *str2)
{
   gchar *newStr;

   newStr = g_strdup_printf("%s%s", str1, str2);
   g_free(str1);
   g_free(str2);

   return newStr;
}


/*
 ******************************************************************************
 * ProtoUserHandleTypeString --                                          */ /**
 *
 * Returns the type of a VGAuthUserHandle as a protocol string.
 *
 * @param[in]  userHandle        The VGAuthUSerHandle.
 *
 * @return The type as a string.
 *
 ******************************************************************************
 */

static const gchar *
ProtoUserHandleTypeString(const VGAuthUserHandle *userHandle)
{
   switch (userHandle->details.type) {
   case VGAUTH_AUTH_TYPE_NAMEPASSWORD:
      return VGAUTH_USERHANDLE_TYPE_NAMEPASSWORD;
   case VGAUTH_AUTH_TYPE_SSPI:
      return VGAUTH_USERHANDLE_TYPE_SSPI;
      break;
   case VGAUTH_AUTH_TYPE_SAML:
      return VGAUTH_USERHANDLE_TYPE_SAML;
   case VGAUTH_AUTH_TYPE_SAML_INFO_ONLY:
      return VGAUTH_USERHANDLE_TYPE_SAML_INFO_ONLY;
   case VGAUTH_AUTH_TYPE_UNKNOWN:
   default:
      ASSERT(0);
      Warning("%s: Unsupported handleType %d\n", __FUNCTION__, userHandle->details.type);
      return "<UNKNOWN>";
   }
}


/*
 ******************************************************************************
 * Proto_StartElement --                                                 */ /**
 *
 * Called by the XML parser when it sees the start of a new
 * element.  Used to update the current parser state, and allocate
 * any space that may be needed for processing that state.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  elementName         The name of the element being started.
 * @param[in]  attributeNames      The names of any attributes on the element.
 * @param[in]  attributeValues     The values of any attributes on the element.
 * @param[in]  userData            The current ProtoReply as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
Proto_StartElement(GMarkupParseContext *parseContext,
                   const gchar *elementName,
                   const gchar **attributeNames,
                   const gchar **attributeValues,
                   gpointer userData,
                   GError **error)
{
   ProtoReply *reply = (ProtoReply *) userData;

#if VGAUTH_PROTO_TRACE
   Debug("%s: elementName '%s', parseState %d, cur reply type %d\n", __FUNCTION__, elementName, reply->parseState, reply->expectedReplyType);
#endif

   switch (reply->parseState) {
   case PARSE_STATE_NONE:
      /*
       * We're in 'idle' mode, expecting a fresh reply.
       */
      if (g_strcmp0(elementName, VGAUTH_REPLY_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_REPLY;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, reply->parseState);
      }
      break;
   case PARSE_STATE_REPLY:
      /*
       * We're in 'reply' mode, expecting some element inside the reply.
       */
      if (g_strcmp0(elementName, VGAUTH_SEQUENCENO_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_SEQ;
      } else if (g_strcmp0(elementName, VGAUTH_ERRORCODE_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_ERROR_CODE;
         reply->actualReplyType = PROTO_REPLY_ERROR;
      } else if (g_strcmp0(elementName, VGAUTH_ERRORMSG_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_ERROR_MSG;
         reply->actualReplyType = PROTO_REPLY_ERROR;
      } else if (g_strcmp0(elementName, VGAUTH_VERSION_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_VERSION;
         if (PROTO_REPLY_SESSION_REQ != reply->expectedReplyType) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         }
      } else if (g_strcmp0(elementName, VGAUTH_PIPENAME_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_PIPENAME;
         if (PROTO_REPLY_SESSION_REQ != reply->expectedReplyType) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         }
      } else if (g_strcmp0(elementName, VGAUTH_TOKEN_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_TOKEN;
         if ((PROTO_REPLY_VALIDATETICKET != reply->expectedReplyType) &&
            (PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN !=
             reply->expectedReplyType)) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         }
      } else if (g_strcmp0(elementName, VGAUTH_USERHANDLEINFO_ELEMENT_NAME) == 0) {
         if (PROTO_REPLY_VALIDATETICKET != reply->expectedReplyType) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         } else {
            reply->parseState = PARSE_STATE_USERHANDLEINFO;
         }
      } else if (g_strcmp0(elementName, VGAUTH_CHALLENGE_EVENT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_CHALLENGE_EVENT;
         if ((PROTO_REPLY_CONN != reply->expectedReplyType)) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         }
      } else if (g_strcmp0(elementName, VGAUTH_USERNAME_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_USERNAME;
         if ((PROTO_REPLY_VALIDATETICKET != reply->expectedReplyType) &&
             (PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN !=
              reply->expectedReplyType)) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         }
         reply->parseState = PARSE_STATE_USERNAME;
      } else if (g_strcmp0(elementName, VGAUTH_TICKET_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_TICKET;
         if (PROTO_REPLY_CREATETICKET != reply->expectedReplyType) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         }
      } else if (g_strcmp0(elementName, VGAUTH_COMMENT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_CERTCOMMENT;
         if (PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN !=
             reply->expectedReplyType) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         }
      } else if (g_strcmp0(elementName, VGAUTH_ALIAS_ELEMENT_NAME) == 0) {
         VGAuthUserAlias *a;

         if (PROTO_REPLY_QUERYALIASES != reply->expectedReplyType) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         } else {
            reply->parseState = PARSE_STATE_ALIAS;

            a = reply->replyData.queryUserAliases.uaList;
            reply->replyData.queryUserAliases.num++;
            a = g_realloc_n(a,
                            reply->replyData.queryUserAliases.num,
                            sizeof(VGAuthUserAlias));
            reply->replyData.queryUserAliases.uaList = a;
            reply->replyData.queryUserAliases.uaList[reply->replyData.queryUserAliases.num - 1].numInfos = 0;
            reply->replyData.queryUserAliases.uaList[reply->replyData.queryUserAliases.num - 1].infos = NULL;
         }
      } else if (g_strcmp0(elementName, VGAUTH_MAPPEDALIASES_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_MAPPEDALIAS;
         reply->replyData.queryMappedAliases.num++;
         reply->replyData.queryMappedAliases.maList = g_realloc_n(reply->replyData.queryMappedAliases.maList,
                                        reply->replyData.queryMappedAliases.num,
                                        sizeof(VGAuthMappedAlias));
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].pemCert = NULL;
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].userName = NULL;
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].numSubjects = 0;
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].subjects = NULL;

      } else if (g_strcmp0(elementName, VGAUTH_USERHANDLESAMLINFO_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_USERHANDLESAMLINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, reply->parseState);
      }
      break;
   case PARSE_STATE_ALIAS:
      if (g_strcmp0(elementName, VGAUTH_PEMCERT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_PEMCERT;
      } else if (g_strcmp0(elementName, VGAUTH_ALIASINFO_ELEMENT_NAME) == 0) {
         VGAuthAliasInfo *info;
         VGAuthUserAlias *ip = &(reply->replyData.queryUserAliases.uaList[reply->replyData.queryUserAliases.num - 1]);

         reply->parseState = PARSE_STATE_ALIASINFO;

         // grow the AliasInfo array
         info = ip->infos;
         ip->numInfos++;

         info = g_realloc_n(info,
                            ip->numInfos,
                            sizeof(VGAuthAliasInfo));
         ip->infos = info;
         ip->infos[ip->numInfos - 1].subject.type = -1;
         ip->infos[ip->numInfos - 1].subject.val.name = NULL;
         ip->infos[ip->numInfos - 1].comment = NULL;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, reply->parseState);
      }
      break;
   case PARSE_STATE_USERHANDLEINFO:
      if (PROTO_REPLY_VALIDATETICKET != reply->expectedReplyType) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Got '%s' when expecting a reply of type %d",
                     elementName, reply->expectedReplyType);
      }
      if (g_strcmp0(elementName, VGAUTH_USERHANDLETYPE_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_USERHANDLETYPE;
      } else if (g_strcmp0(elementName, VGAUTH_USERHANDLESAMLINFO_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_USERHANDLESAMLINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, reply->parseState);
      }
      break;
   case PARSE_STATE_USERHANDLESAMLINFO:
      if (g_strcmp0(elementName, VGAUTH_USERHANDLESAMLSUBJECT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_USERHANDLESAMLSUBJECT;
      } else if (g_strcmp0(elementName, VGAUTH_ALIASINFO_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_ALIASINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, reply->parseState);
      }
      break;
   case PARSE_STATE_ALIASINFO:
      if (g_strcmp0(elementName, VGAUTH_COMMENT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_COMMENT;
      } else if (g_strcmp0(elementName, VGAUTH_SUBJECT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_NAMEDSUBJECT;
      } else if (g_strcmp0(elementName, VGAUTH_ANYSUBJECT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_ANYSUBJECT;
         /*
          * Since this is an empty-element tag, the Contents code will
          * not be called, so do the work here.
          */
         if (PROTO_REPLY_QUERYALIASES == reply->expectedReplyType) {
            VGAuthAliasInfo *info;
            VGAuthUserAlias *ip = &(reply->replyData.queryUserAliases.uaList[reply->replyData.queryUserAliases.num - 1]);

            info = &(ip->infos[ip->numInfos - 1]);
            info->subject.type = VGAUTH_SUBJECT_ANY;
         } else if (PROTO_REPLY_VALIDATETICKET == reply->expectedReplyType) {
            reply->replyData.validateTicket.aliasInfo.subject.type = VGAUTH_SUBJECT_ANY;
         } else if (PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN == reply->expectedReplyType) {
            reply->replyData.validateSamlBToken.aliasInfo.subject.type = VGAUTH_SUBJECT_ANY;
         } else {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Got '%s' when expecting a reply of type %d",
                        elementName, reply->expectedReplyType);
         }
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, reply->parseState);
      }
      break;
   case PARSE_STATE_MAPPEDALIAS:
      if (g_strcmp0(elementName, VGAUTH_USERNAME_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_USERNAME;
      } else if (g_strcmp0(elementName, VGAUTH_PEMCERT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_PEMCERT;
      } else if (g_strcmp0(elementName, VGAUTH_SUBJECTS_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_SUBJECTS;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, reply->parseState);
      }
      break;
   case PARSE_STATE_SUBJECTS:
      {
      int n;
      VGAuthSubject *subjs;
      VGAuthSubjectType sType = -1;

      if (g_strcmp0(elementName, VGAUTH_SUBJECT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_NAMEDSUBJECT;
         sType = VGAUTH_SUBJECT_NAMED;
      } else if (g_strcmp0(elementName, VGAUTH_ANYSUBJECT_ELEMENT_NAME) == 0) {
         reply->parseState = PARSE_STATE_ANYSUBJECT;
         sType = VGAUTH_SUBJECT_ANY;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, reply->parseState);
         break;
      }

      // got a new Subject or AnySubject, grow
      n = ++(reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].numSubjects);
      subjs = reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].subjects;
      subjs = g_realloc_n(subjs, n,  sizeof(VGAuthSubject));
      subjs[n - 1].type = sType;
      subjs[n - 1].val.name = NULL;
      reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].subjects = subjs;
      }
      break;
   default:
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Unexpected element '%s' in parse state %d",
                  elementName, reply->parseState);

      break;
   }
}


/*
 ******************************************************************************
 * Proto_EndElement --                                                   */ /**
 *
 * Called by the XML parser when the end of an element is reached.
 * Used here to pop the parse state.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  elementName         The name of the element being started.
 * @param[in]  userData            The current ProtoReply as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
Proto_EndElement(GMarkupParseContext *parseContext,
                 const gchar *elementName,
                 gpointer userData,
                 GError **error)
{
   ProtoReply *reply = (ProtoReply *) userData;

#if VGAUTH_PROTO_TRACE
   Debug("%s: elementName '%s'\n", __FUNCTION__, elementName);
#endif

   switch (reply->parseState) {
   case PARSE_STATE_SEQ:
   case PARSE_STATE_ERROR_CODE:
   case PARSE_STATE_ERROR_MSG:
   case PARSE_STATE_VERSION:
   case PARSE_STATE_PIPENAME:
   case PARSE_STATE_TICKET:
   case PARSE_STATE_TOKEN:
   case PARSE_STATE_CHALLENGE_EVENT:
   case PARSE_STATE_ALIAS:
   case PARSE_STATE_MAPPEDALIAS:
   case PARSE_STATE_USERHANDLEINFO:
      reply->parseState = PARSE_STATE_REPLY;
      break;
   case PARSE_STATE_USERNAME:
      if (PROTO_REPLY_QUERYMAPPEDALIASES == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_MAPPEDALIAS;
      } else {
         reply->parseState = PARSE_STATE_REPLY;
      }
      break;
   case PARSE_STATE_ALIASINFO:
      if (PROTO_REPLY_QUERYALIASES == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_ALIAS;
      } else if (PROTO_REPLY_VALIDATETICKET == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_USERHANDLESAMLINFO;
      } else if (PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_USERHANDLESAMLINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Bad parse state, popping aliasInfo in reply type %d",
                     reply->expectedReplyType);
      }
      break;
   case PARSE_STATE_SUBJECTS:
      reply->parseState = PARSE_STATE_MAPPEDALIAS;
      break;
   case PARSE_STATE_NAMEDSUBJECT:
   case PARSE_STATE_ANYSUBJECT:
      if (PROTO_REPLY_QUERYALIASES == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_ALIASINFO;
      } else if (PROTO_REPLY_QUERYMAPPEDALIASES == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_SUBJECTS;
      } else if (PROTO_REPLY_VALIDATETICKET == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_ALIASINFO;
      } else if (PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_ALIASINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Bad parse state, popping subject in reply type %d",
                     reply->expectedReplyType);
      }
      break;
   case PARSE_STATE_COMMENT:
      reply->parseState = PARSE_STATE_ALIASINFO;
      break;
   case PARSE_STATE_PEMCERT:
      if (PROTO_REPLY_QUERYALIASES == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_ALIAS;
      } else if (PROTO_REPLY_QUERYMAPPEDALIASES == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_MAPPEDALIAS;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Bad parse state, popping pemCert in reply type %d",
                     reply->expectedReplyType);
      }
      break;
   case PARSE_STATE_CERTCOMMENT:
      reply->parseState = PARSE_STATE_REPLY;
      break;
   case PARSE_STATE_REPLY:
      reply->complete = TRUE;
      reply->parseState = PARSE_STATE_NONE;
      break;
   case PARSE_STATE_USERHANDLETYPE:
      reply->parseState = PARSE_STATE_USERHANDLEINFO;
      break;
   case PARSE_STATE_USERHANDLESAMLINFO:
      if (PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN == reply->expectedReplyType) {
         reply->parseState = PARSE_STATE_REPLY;
      } else {
         reply->parseState = PARSE_STATE_USERHANDLEINFO;
      }
      break;
   case PARSE_STATE_USERHANDLESAMLSUBJECT:
      reply->parseState = PARSE_STATE_USERHANDLESAMLINFO;
      break;
   default:
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Bad parse state, popping unknown parse state %d",
                  reply->parseState);
      ASSERT(0);
   }
}


/*
 ******************************************************************************
 * Proto_TextContents --                                                 */ /**
 *
 * Called by the parser with the contents of an element.
 * Used to store the values.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  text                The contents of the current element
 *                                 (not NUL terminated)
 * @param[in]  textSize            The length of the text.
 * @param[in]  userData            The current ProtoReply as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
Proto_TextContents(GMarkupParseContext *parseContext,
                   const gchar *text,
                   gsize textSize,
                   gpointer userData,
                   GError **error)
{
   ProtoReply *reply = (ProtoReply *) userData;
   gchar *val;
   VGAuthUserHandleType t = VGAUTH_AUTH_TYPE_UNKNOWN;

#if VGAUTH_PROTO_TRACE
   Debug("%s: parseState %d, text '%*s'\n", __FUNCTION__, reply->parseState, (int) textSize, text);
#endif

   val = g_strndup(text, textSize);

   switch (reply->parseState) {
   case PARSE_STATE_SEQ:
      reply->sequenceNumber = atoi(val);
      g_free(val);
      break;

   case PARSE_STATE_ERROR_CODE:
      reply->errorCode = atoi(val);
      g_free(val);
      break;
   case PARSE_STATE_ERROR_MSG:
      reply->replyData.error.errorMsg = val;
      break;

   case PARSE_STATE_VERSION:
      reply->replyData.sessionReq.version = atoi(val);
      if (reply->expectedReplyType != PROTO_REPLY_SESSION_REQ) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found version number in reply type %d",
                     reply->expectedReplyType);
      }
      g_free(val);
      break;
   case PARSE_STATE_PIPENAME:
      if (reply->expectedReplyType != PROTO_REPLY_SESSION_REQ) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found pipeName in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      } else {
         reply->replyData.sessionReq.pipeName = val;
      }
      break;

   case PARSE_STATE_TICKET:
      if (reply->expectedReplyType != PROTO_REPLY_CREATETICKET) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found ticket in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      } else {
         reply->replyData.createTicket.ticket = val;
      }
      break;

   case PARSE_STATE_TOKEN:
      if (reply->expectedReplyType == PROTO_REPLY_VALIDATETICKET) {
         reply->replyData.validateTicket.token = val;
      } else if (reply->expectedReplyType ==
                 PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN) {
         reply->replyData.validateSamlBToken.token = val;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found token in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      }
      break;

   case PARSE_STATE_CHALLENGE_EVENT:
      if (reply->expectedReplyType == PROTO_REPLY_CONN) {
         reply->replyData.connect.challengeEvent = val;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found token in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      }
      break;

   case PARSE_STATE_USERNAME:
      if (reply->expectedReplyType == PROTO_REPLY_VALIDATETICKET) {
         reply->replyData.validateTicket.userName = val;
      } else if (reply->expectedReplyType ==
                 PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN) {
         reply->replyData.validateSamlBToken.userName = val;
      } else if (reply->expectedReplyType == PROTO_REPLY_QUERYMAPPEDALIASES) {
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].userName = val;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found username in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      }
      break;

   case PARSE_STATE_PEMCERT:
      if (PROTO_REPLY_QUERYALIASES == reply->expectedReplyType) {
         reply->replyData.queryUserAliases.uaList[reply->replyData.queryUserAliases.num - 1].pemCert = val;
      } else if (reply->expectedReplyType == PROTO_REPLY_QUERYMAPPEDALIASES) {
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].pemCert = val;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found pemCert in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      }
      break;
   case PARSE_STATE_CERTCOMMENT:
      if (reply->expectedReplyType == PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN) {
         reply->replyData.validateSamlBToken.comment = val;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found cert comment in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      }
      break;

   case PARSE_STATE_REPLY:
   case PARSE_STATE_ALIAS:
   case PARSE_STATE_ALIASINFO:
   case PARSE_STATE_SUBJECTS:
   case PARSE_STATE_MAPPEDALIAS:
   case PARSE_STATE_USERHANDLEINFO:
   case PARSE_STATE_USERHANDLESAMLINFO:
      /*
       * Should just be whitespace, so drop it
       */
      g_free(val);
      break;
   case PARSE_STATE_USERHANDLESAMLSUBJECT:
      if (PROTO_REPLY_VALIDATETICKET == reply->expectedReplyType) {
         reply->replyData.validateTicket.samlSubject = val;
      } else if (PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN == reply->expectedReplyType) {
         reply->replyData.validateSamlBToken.samlSubject = val;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found SAMLSubject in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      }
      break;
   case PARSE_STATE_USERHANDLETYPE:
      if (PROTO_REPLY_VALIDATETICKET == reply->expectedReplyType) {
         if (g_strcmp0(val, VGAUTH_USERHANDLE_TYPE_NAMEPASSWORD) == 0) {
            t = VGAUTH_AUTH_TYPE_NAMEPASSWORD;
         } else if (g_strcmp0(val, VGAUTH_USERHANDLE_TYPE_SSPI) == 0) {
            t = VGAUTH_AUTH_TYPE_SSPI;
         } else if (g_strcmp0(val, VGAUTH_USERHANDLE_TYPE_SAML) == 0) {
            t = VGAUTH_AUTH_TYPE_SAML;
         } else if (g_strcmp0(val, VGAUTH_USERHANDLE_TYPE_SAML_INFO_ONLY) == 0) {
            t = VGAUTH_AUTH_TYPE_SAML_INFO_ONLY;
         } else {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Found unrecognized userHandle type %s", val);
         }
         reply->replyData.validateTicket.type = t;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found UserHandleType in reply type %d",
                     reply->expectedReplyType);
      }
      g_free(val);
      break;
   case PARSE_STATE_NAMEDSUBJECT:
      if (PROTO_REPLY_QUERYALIASES == reply->expectedReplyType) {
         VGAuthUserAlias *a;

         a = &(reply->replyData.queryUserAliases.uaList[reply->replyData.queryUserAliases.num - 1]);
         a->infos[a->numInfos - 1].subject.val.name = val;
         a->infos[a->numInfos - 1].subject.type = VGAUTH_SUBJECT_NAMED;
      } else if (reply->expectedReplyType == PROTO_REPLY_QUERYMAPPEDALIASES) {
         int idx = reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].numSubjects;
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].subjects[idx - 1].val.name = val;
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].subjects[idx - 1].type = VGAUTH_SUBJECT_NAMED;
      } else if (reply->expectedReplyType == PROTO_REPLY_VALIDATETICKET) {
         reply->replyData.validateTicket.aliasInfo.subject.type = VGAUTH_SUBJECT_NAMED;
         reply->replyData.validateTicket.aliasInfo.subject.val.name = val;
      } else if (reply->expectedReplyType == PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN) {
         reply->replyData.validateSamlBToken.aliasInfo.subject.type = VGAUTH_SUBJECT_NAMED;
         reply->replyData.validateSamlBToken.aliasInfo.subject.val.name = val;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found NamedSubject in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      }
      break;
   case PARSE_STATE_ANYSUBJECT:
      /*
       * Won't usually hit this code, since we use an empty-element tag.
       */
      if (PROTO_REPLY_QUERYALIASES == reply->expectedReplyType) {
         VGAuthUserAlias *a;

         a = &(reply->replyData.queryUserAliases.uaList[reply->replyData.queryUserAliases.num - 1]);
         a->infos[a->numInfos - 1].subject.type = VGAUTH_SUBJECT_ANY;
      } else if (reply->expectedReplyType == PROTO_REPLY_QUERYMAPPEDALIASES) {
         reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].subjects[reply->replyData.queryMappedAliases.maList[reply->replyData.queryMappedAliases.num - 1].numSubjects - 1].type = VGAUTH_SUBJECT_ANY;
      } else if (reply->expectedReplyType == PROTO_REPLY_VALIDATETICKET) {
         reply->replyData.validateTicket.aliasInfo.subject.type = VGAUTH_SUBJECT_ANY;
      } else if (reply->expectedReplyType == PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN) {
         reply->replyData.validateSamlBToken.aliasInfo.subject.type = VGAUTH_SUBJECT_ANY;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found AnySubject in reply type %d",
                     reply->expectedReplyType);
      }
      g_free(val);
      break;
   case PARSE_STATE_COMMENT:
      if (PROTO_REPLY_QUERYALIASES == reply->expectedReplyType) {
         VGAuthUserAlias *a;

         a = &(reply->replyData.queryUserAliases.uaList[reply->replyData.queryUserAliases.num - 1]);
         a->infos[a->numInfos - 1].comment = val;
      } else if (reply->expectedReplyType == PROTO_REPLY_VALIDATETICKET) {
         reply->replyData.validateTicket.aliasInfo.comment = val;
      } else if (reply->expectedReplyType == PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN) {
         reply->replyData.validateSamlBToken.aliasInfo.comment = val;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found comment in reply type %d",
                     reply->expectedReplyType);
         g_free(val);
      }
      break;
   default:
      g_warning("Unexpected value '%s' in unhandled parseState %d in %s\n",
                val, reply->parseState, __FUNCTION__);
      g_free(val);
      ASSERT(0);
   }
}


/*
 * Describes the parser functions.
 */
static GMarkupParser wireParser = {
   Proto_StartElement,
   Proto_EndElement,
   Proto_TextContents,
   NULL,
   NULL,
};


/*
 ******************************************************************************
 * Proto_NewReply --                                                     */ /**
 *
 * Creates a new ProtoReply
 *
 * @param[in]  expectedReplyType         The type of the new reply.
 *
 * @return The new ProtoReply *.
 *
 ******************************************************************************
 */

ProtoReply *
Proto_NewReply(ProtoReplyType expectedReplyType)
{
   ProtoReply *reply = g_malloc0(sizeof(ProtoReply));
   reply->parseState = PARSE_STATE_NONE;
   reply->complete = FALSE;
   reply->errorCode = VGAUTH_E_OK;
   reply->expectedReplyType = expectedReplyType;
   reply->actualReplyType = expectedReplyType;
#if VGAUTH_PROTO_TRACE
   reply->rawData = NULL;
#endif

   return reply;
}


/*
 ******************************************************************************
 * Proto_FreeReply --                                                    */ /**
 *
 * Frees a reply.
 *
 * @param[in]  reply         The reply to free.
 *
 ******************************************************************************
 */

static void
Proto_FreeReply(ProtoReply *reply)
{
   if (NULL == reply) {
      return;
   }

#if VGAUTH_PROTO_TRACE
   g_free(reply->rawData);
#endif
   switch (reply->actualReplyType) {
   case PROTO_REQUEST_UNKNOWN:
      // partial/empty request -- no-op
      Debug("%s: Freeing an request of unknown type.\n", __FUNCTION__);
      break;
   case PROTO_REPLY_ERROR:
      g_free(reply->replyData.error.errorMsg);
      break;
   case PROTO_REPLY_SESSION_REQ:
      g_free(reply->replyData.sessionReq.pipeName);
      break;
   case PROTO_REPLY_CONN:
      g_free(reply->replyData.connect.challengeEvent);
      break;
   case PROTO_REPLY_ADDALIAS:
   case PROTO_REPLY_REMOVEALIAS:
   case PROTO_REPLY_REVOKETICKET:
      break;
   case PROTO_REPLY_QUERYMAPPEDALIASES:
      VGAuth_FreeMappedAliasList(reply->replyData.queryMappedAliases.num,
                                 reply->replyData.queryMappedAliases.maList);
      break;
   case PROTO_REPLY_QUERYALIASES:
      VGAuth_FreeUserAliasList(reply->replyData.queryUserAliases.num,
                                reply->replyData.queryUserAliases.uaList);
      break;
   case PROTO_REPLY_CREATETICKET:
      g_free(reply->replyData.createTicket.ticket);
      break;
   case PROTO_REPLY_VALIDATETICKET:
      g_free(reply->replyData.validateTicket.userName);
      g_free(reply->replyData.validateTicket.token);
      g_free(reply->replyData.validateTicket.samlSubject);
      VGAuth_FreeAliasInfoContents(&(reply->replyData.validateTicket.aliasInfo));
      break;
   case PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN:
      g_free(reply->replyData.validateSamlBToken.comment);
      g_free(reply->replyData.validateSamlBToken.userName);
      g_free(reply->replyData.validateSamlBToken.token);
      g_free(reply->replyData.validateSamlBToken.samlSubject);
      VGAuth_FreeAliasInfoContents(&(reply->replyData.validateSamlBToken.aliasInfo));
      break;
   }
   g_free(reply);
}


/*
 ******************************************************************************
 * Proto_ConfidenceCheckReply --                                             */ /**
 *
 * Verifies a reply is internally consistent and the type is what we expected.
 *
 * @param[in]  reply                       The reply to check.
 * @param[in]  expectedSequenceNumber      The sequence number that
 *                                         should be in the reply.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
Proto_ConfidenceCheckReply(ProtoReply *reply,
                       int expectedSequenceNumber)
{
#if VGAUTH_PROTO_TRACE
   ASSERT(strncmp(reply->rawData, VGAUTH_XML_PREAMBLE,
                  strlen(VGAUTH_XML_PREAMBLE)) == 0);
#endif
   if (PROTO_REPLY_ERROR != reply->actualReplyType) {
      if (reply->actualReplyType != reply->expectedReplyType) {
         Warning("%s: expected reply type %d doesn't match actual type %d\n",
                 __FUNCTION__, reply->expectedReplyType, reply->actualReplyType);
         return VGAUTH_E_COMM;
      }
   }

   if (-1 != expectedSequenceNumber) {
      if (reply->sequenceNumber != expectedSequenceNumber) {
         Warning("%s: sequence number check failed:  wanted %d, got %d\n",
                 __FUNCTION__, expectedSequenceNumber, reply->sequenceNumber);
         return VGAUTH_E_COMM;
      }
   }

   /*
    * If it's an error, kick out now.
    */
   if (PROTO_REPLY_ERROR == reply->actualReplyType) {
      return VGAUTH_E_OK;
   }

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * VGAuth_ReadAndParseResponse --                                        */ /**
 *
 * Reads the next reply off the wire and returns it in wireReply.
 *
 * @param[in]  ctx                       The VGAuthContext.
 * @param[in]  expectedReplyType         The expected reply type.
 * @param[out] wireReply                 The complete reply.  The caller
 *                                       should use Proto_FreeReply on
 *                                       it when finished.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_ReadAndParseResponse(VGAuthContext *ctx,
                            ProtoReplyType expectedReplyType,
                            ProtoReply **wireReply)
{
   VGAuthError err = VGAUTH_E_OK;
   GMarkupParseContext *parseContext;
   gsize len;
   ProtoReply *reply;
   gboolean bRet;
   GError *gErr = NULL;

   reply = Proto_NewReply(expectedReplyType);

   parseContext = g_markup_parse_context_new(&wireParser,
                                             0,
                                             reply,
                                             NULL);

   /*
    * May take multiple reads if reply is broken up by the underlying
    * transport.
    */
   while (!reply->complete) {
      gchar *rawReply = NULL;

      err = VGAuth_CommReadData(ctx, &len, &rawReply);
      if (0 == len) {      // EOF -- not expected
         err = VGAUTH_E_COMM;
         Warning("%s: EOF on datastream when trying to parse\n", __FUNCTION__);
         goto quit;
      }
      if (VGAUTH_E_OK != err) {
         goto quit;
      }
#if VGAUTH_PROTO_TRACE
      if (reply->rawData) {
         reply->rawData = g_strdup_printf("%s%s", reply->rawData, rawReply);
      } else {
         reply->rawData = g_strdup(rawReply);
      }
#endif
      bRet = g_markup_parse_context_parse(parseContext,
                                          rawReply,
                                          len,
                                          &gErr);
      g_free(rawReply);
      if (!bRet) {
         /*
          * XXX Could drain the wire here, but since this should
          * never happen, just treat it as fatal for this socket.
          */
         err = VGAUTH_E_COMM;
         Warning("%s: g_markup_parse_context_parse() failed: %s\n",
                 __FUNCTION__, gErr->message);
         g_error_free(gErr);
         goto quit;
      }
      /*
       * XXX need some way to break out if packet never completed
       * yet socket left valid.  timer?
       */
   }

#if VGAUTH_PROTO_TRACE
   Proto_DumpReply(reply);
#endif

   err = Proto_ConfidenceCheckReply(reply, ctx->comm.sequenceNumber);

   if (VGAUTH_E_OK != err) {
      Warning("%s: reply confidence check failed\n", __FUNCTION__);
      goto quit;
   }

   if (PROTO_REPLY_ERROR == reply->actualReplyType) {
      Debug("%s: service sent back error "VGAUTHERR_FMT64X" (%s)\n",
            __FUNCTION__,
            reply->errorCode, reply->replyData.error.errorMsg);
      err = reply->errorCode;
   }
   goto done;

quit:
   Proto_FreeReply(reply);
   reply = NULL;
done:
   *wireReply = reply;
   g_markup_parse_context_free(parseContext);
   return err;
}


/*
 ******************************************************************************
 * VGAuth_SendSessionRequest --                                          */ /**
 *
 * Sends the sessionRequest message and verifies the returning
 * reply.  The pipeName member of the ctx->comm is filled in.
 *
 * @param[in]  ctx                       The VGAuthContext.
 * @param[in]  userName                  The name of the user.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendSessionRequest(VGAuthContext *ctx,
                          const char *userName,
                          char **pipeName)                  // OUT
{
   VGAuthError err;
   gchar *packet;
   ProtoReply *reply = NULL;

   packet = g_markup_printf_escaped(VGAUTH_SESSION_REQUEST_FORMAT,
                                    ctx->comm.sequenceNumber,
                                    userName);

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to send packet\n", __FUNCTION__);
      goto quit;
   }

   err = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_SESSION_REQ, &reply);
   if (VGAUTH_E_OK != err) {
      Warning("%s: read & parse reply failed\n", __FUNCTION__);
      goto quit;
   }

   /* version # check */
   if (reply->replyData.sessionReq.version != atoi(VGAUTH_PROTOCOL_VERSION)) {
      Warning("%s: version mismatch client is %d, service %d\n",
              __FUNCTION__, atoi(VGAUTH_PROTOCOL_VERSION),
              reply->replyData.sessionReq.version);
      /* XXX error out, or pretend?  */
   }

   *pipeName = g_strdup(reply->replyData.sessionReq.pipeName);

   ctx->comm.sequenceNumber++;

quit:
   Proto_FreeReply(reply);
   g_free(packet);
   return err;
}


/*
 ******************************************************************************
 * VGAuthErrorPipeClosed --                                              */ /**
 *
 * Check if the error code contains a system error that
 * the other end closed the pipe
 *
 * @param[in]  err       A VGAuthError code
 *
 * @return TRUE if the error code contains a system error that the other end
 *         closed the pipe. FALSE otherwise.
 *
 ******************************************************************************
 */

static gboolean
VGAuthErrorPipeClosed(VGAuthError err)
{
#ifdef _WIN32
   return VGAUTH_ERROR_EXTRA_ERROR(err) == ERROR_NO_DATA;
#else
   return VGAUTH_ERROR_EXTRA_ERROR(err) == EPIPE;
#endif
}


/*
 ******************************************************************************
 * VGAuth_SendConnectRequest --                                          */ /**
 *
 * Sends the connect message and verifies the returning reply.
 *
 * @param[in]  ctx                       The VGAuthContext.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendConnectRequest(VGAuthContext *ctx)
{
   VGAuthError err = VGAUTH_E_OK;
   VGAuthError err2;
   gchar *packet;
   ProtoReply *reply = NULL;
   char *pid = NULL;
#ifdef _WIN32
   unsigned int challengeEventValue;
   HANDLE hChallengeEvent;
   DWORD dwPid = GetCurrentProcessId();

   pid = Convert_UnsignedInt32ToText(dwPid);
#endif

   /* Value of pid is always NULL on non-Windows platforms */
   /* coverity[dead_error_line] */
   packet = g_markup_printf_escaped(VGAUTH_CONNECT_REQUEST_FORMAT,
                                    ctx->comm.sequenceNumber,
                                    pid ? pid : "");

   err = VGAuth_CommSendData(ctx, packet);
   /*
    * Bail out if failed.
    * However, continue to read the service response
    * if the service closed the pipe prematurely.
    */
   if (VGAUTH_FAILED(err) && !VGAuthErrorPipeClosed(err)) {
      VGAUTH_LOG_WARNING("failed to send packet, %s", packet);
      goto done;
   }

   err2 = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_CONN, &reply);
   if (VGAUTH_E_OK != err2) {
      VGAUTH_LOG_WARNING("read & parse reply failed, as user %s",
         ctx->comm.userName);
      err = err2;
      goto done;
   }

#ifdef _WIN32
   err = VGAUTH_E_FAIL;
   CHK_TEXT_TO_UINT32(challengeEventValue,
                      reply->replyData.connect.challengeEvent,
                      goto done);
   hChallengeEvent = (HANDLE)(size_t)challengeEventValue;
   if (!SetEvent(hChallengeEvent)) {
      VGAUTH_LOG_ERR_WIN("SetEvent() failed, pipe = %s", ctx->comm.pipeName);
      CloseHandle(hChallengeEvent);
      goto done;
   }
   CloseHandle(hChallengeEvent);
   err = VGAUTH_E_OK;
#endif

   ctx->comm.sequenceNumber++;

done:
   Proto_FreeReply(reply);
   g_free(packet);
   g_free(pid);
   return err;
}


/*
 ******************************************************************************
 * VGAuth_SendAddAliasRequest --                                         */ /**
 *
 * Sends the AddAlias message and verifies the returning reply.
 *
 * @param[in]  ctx                       The VGAuthContext.
 * @param[in]  userName                  The user of the identity store
 *                                       being changed.
 * @param[in]  addMappedLink             If TRUE, adds an entry to the
 *                                       mapping file.
 * @param[in]  pemCert                   The certificate to add.
 * @param[in]  ai                        The associated AliasInfo.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendAddAliasRequest(VGAuthContext *ctx,
                           const char *userName,
                           gboolean addMappedLink,
                           const char *pemCert,
                           VGAuthAliasInfo *ai)
{
   VGAuthError err = VGAUTH_E_OK;
   gchar *packet = NULL;
   ProtoReply *reply = NULL;
   gchar *aiPacket = NULL;

   if (!VGAuth_IsConnectedToServiceAsUser(ctx, userName)) {
      err = VGAuth_ConnectToServiceAsUser(ctx, userName);
      if (VGAUTH_E_OK != err) {
         goto quit;
      }
   }

   packet = g_markup_printf_escaped(VGAUTH_ADDALIAS_REQUEST_FORMAT_START,
                                    ctx->comm.sequenceNumber,
                                    userName,
                                    addMappedLink,
                                    pemCert);

   if (VGAUTH_SUBJECT_NAMED == ai->subject.type) {
      aiPacket = g_markup_printf_escaped(VGAUTH_NAMEDALIASINFO_FORMAT,
                                         ai->subject.val.name,
                                         ai->comment);
   } else {
      aiPacket = g_markup_printf_escaped(VGAUTH_ANYALIASINFO_FORMAT,
                                         ai->comment);
   }
   packet = Proto_ConcatXMLStrings(packet, aiPacket);
   packet = Proto_ConcatXMLStrings(packet,
                                   g_strdup(VGAUTH_ADDALIAS_REQUEST_FORMAT_END));

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to send packet\n", __FUNCTION__);
      goto quit;
   }

   err = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_ADDALIAS, &reply);
   if (VGAUTH_E_OK != err) {
      Warning("%s: read & parse reply failed\n", __FUNCTION__);
      goto quit;
   }

   ctx->comm.sequenceNumber++;

quit:
   Proto_FreeReply(reply);
   g_free(packet);
   return err;
}


/*
 ******************************************************************************
 * VGAuth_SendRemoveAliasRequest --                                      */ /**
 *
 * Sends the RemoveAlias message and verifies the returning reply.
 *
 * @param[in]  ctx          The VGAuthContext.
 * @param[in]  userName     The user of the identity store being changed.
 * @param[in]  pemCert      The certifcate to be removed, in PEM format.
 * @param[in]  subj         The subject to be removed (NULL if all).
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendRemoveAliasRequest(VGAuthContext *ctx,
                              const char *userName,
                              const char *pemCert,
                              VGAuthSubject *subj)
{
   VGAuthError err = VGAUTH_E_OK;
   gchar *packet = NULL;
   ProtoReply *reply = NULL;
   gchar *sPacket;

   /*
    * Try connecting as user if we can, otherwise try root.
    * This allows for removing entries from deleted users.
    */
   if (UsercheckUserExists(userName)) {
      if (!VGAuth_IsConnectedToServiceAsUser(ctx, userName)) {
         err = VGAuth_ConnectToServiceAsUser(ctx, userName);
         if (VGAUTH_E_OK != err) {
            goto quit;
         }
      }
   } else {
      if (!VGAuth_IsConnectedToServiceAsUser(ctx, SUPERUSER_NAME)) {
         err = VGAuth_ConnectToServiceAsUser(ctx, SUPERUSER_NAME);
         if (VGAUTH_E_OK != err) {
            goto quit;
         }
      }
   }

   packet = g_markup_printf_escaped(VGAUTH_REMOVEALIAS_REQUEST_FORMAT_START,
                                    ctx->comm.sequenceNumber,
                                    userName,
                                    pemCert);

   if (subj) {
      if (VGAUTH_SUBJECT_NAMED == subj->type) {
         sPacket = g_markup_printf_escaped(VGAUTH_SUBJECT_FORMAT,
                                           subj->val.name);
      } else {
         sPacket = g_strdup(VGAUTH_ANYSUBJECT_FORMAT);
      }
      packet = Proto_ConcatXMLStrings(packet, sPacket);
   }
   packet = Proto_ConcatXMLStrings(packet,
                                   g_strdup(VGAUTH_REMOVEALIAS_REQUEST_FORMAT_END));

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to send packet\n", __FUNCTION__);
      goto quit;
   }

   err = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_REMOVEALIAS, &reply);
   if (VGAUTH_E_OK != err) {
      Warning("%s: read & parse reply failed\n", __FUNCTION__);
      goto quit;
   }

   ctx->comm.sequenceNumber++;

quit:
   Proto_FreeReply(reply);
   g_free(packet);
   return err;
}


/*
 ******************************************************************************
 * VGAuth_SendQueryUserAliasesRequest --                                 */ /**
 *
 * Sends the QueryAliases message and verifies the returning reply.
 *
 * @param[in]  ctx                 The VGAuthContext.
 * @param[in]  userName            The user of the identity store
 *                                 being queried.
 * @param[out] num                 The number of VGAuthUserAlias being returned.
 * @param[out] uaList              The resulting UserAliases.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendQueryUserAliasesRequest(VGAuthContext *ctx,
                                   const char *userName,
                                   int *num,
                                   VGAuthUserAlias **uaList)
{
   VGAuthError err = VGAUTH_E_OK;
   gchar *packet = NULL;
   ProtoReply *reply = NULL;

   *uaList = NULL;
   *num = 0;

   /*
    * Try connecting as user if we can, otherwise try root.
    * This allows for querying certs for deleted users.
    */
   if (UsercheckUserExists(userName)) {
      if (!VGAuth_IsConnectedToServiceAsUser(ctx, userName)) {
         err = VGAuth_ConnectToServiceAsUser(ctx, userName);
         if (VGAUTH_E_OK != err) {
            goto quit;
         }
      }
   } else {
      if (!VGAuth_IsConnectedToServiceAsUser(ctx, SUPERUSER_NAME)) {
         err = VGAuth_ConnectToServiceAsUser(ctx, SUPERUSER_NAME);
         if (VGAUTH_E_OK != err) {
            goto quit;
         }
      }
   }

   packet = g_markup_printf_escaped(VGAUTH_QUERYALIASES_REQUEST_FORMAT,
                                    ctx->comm.sequenceNumber,
                                    userName);

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to send packet\n", __FUNCTION__);
      goto quit;
   }

   err = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_QUERYALIASES, &reply);
   if (VGAUTH_E_OK != err) {
      Warning("%s: read & parse reply failed\n", __FUNCTION__);
      goto quit;
   }

   // just copy the reply data
   *num = reply->replyData.queryUserAliases.num;
   *uaList = reply->replyData.queryUserAliases.uaList;

   // clear out reply before free
   reply->replyData.queryUserAliases.num = 0;
   reply->replyData.queryUserAliases.uaList = NULL;

   ctx->comm.sequenceNumber++;

quit:
   Proto_FreeReply(reply);
   g_free(packet);
   return err;
}


/*
 ******************************************************************************
 * VGAuth_SendQueryMappedAliasesRequest --                               */ /**
 *
 * Sends the QueryMappedAliases message and verifies the returning reply.
 *
 * @param[in]  ctx              The VGAuthContext.
 * @param[out] num              The number of identities.
 * @param[out] maList           The VGAuthMappedAliases being returned.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendQueryMappedAliasesRequest(VGAuthContext *ctx,
                                     int *num,
                                     VGAuthMappedAlias **maList)
{
   VGAuthError err = VGAUTH_E_OK;
   gchar *packet = NULL;
   ProtoReply *reply = NULL;

   *num = 0;
   *maList = NULL;

   /*
    * QueryMappedCerts has no security restrictions, so we don't care
    * what user is used.
    */
   if (!VGAuth_IsConnectedToServiceAsAnyUser(ctx)) {
      err = VGAuth_ConnectToServiceAsCurrentUser(ctx);
      if (VGAUTH_E_OK != err) {
         goto quit;
      }
   }

   packet = g_markup_printf_escaped(VGAUTH_QUERYMAPPEDALIASES_REQUEST_FORMAT,
                                    ctx->comm.sequenceNumber);

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to send packet\n", __FUNCTION__);
      goto quit;
   }

   err = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_QUERYMAPPEDALIASES, &reply);
   if (VGAUTH_E_OK != err) {
      Warning("%s: read & parse reply failed\n", __FUNCTION__);
      goto quit;
   }

   // just copy the reply data
   *num = reply->replyData.queryMappedAliases.num;
   *maList = reply->replyData.queryMappedAliases.maList;

   // clear out reply before free
   reply->replyData.queryMappedAliases.num = 0;
   reply->replyData.queryMappedAliases.maList = NULL;

   ctx->comm.sequenceNumber++;

quit:
   Proto_FreeReply(reply);
   g_free(packet);
   return err;
}


/*
 ******************************************************************************
 * VGAuth_SendCreateTicketRequest --                                     */ /**
 *
 * Sends the CreateTicket message and verifies the returning reply.
 *
 * @param[in]  ctx                       The VGAuthContext.
 * @param[in]  userHandle                The VGAuthUserHandle.
 * @param[out] ticket                    The new ticket.
 *
 * @note   token is optional on Windows, ignored on other platforms
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendCreateTicketRequest(VGAuthContext *ctx,
                               VGAuthUserHandle *userHandle,
                               char **ticket)
{
   VGAuthError err = VGAUTH_E_OK;
   gchar *packet = NULL;
   ProtoReply *reply = NULL;
   char *tokenInText = NULL;
   char *sPacket;
   VGAuthAliasInfo *ai;

   *ticket = NULL;

   if (!VGAuth_IsConnectedToServiceAsUser(ctx, userHandle->userName)) {
      err = VGAuth_ConnectToServiceAsUser(ctx, userHandle->userName);
      if (VGAUTH_E_OK != err) {
         goto quit;
      }
   }

#ifdef _WIN32
   ASSERT(Check_Is32bitNumber((size_t)userHandle->token));
   tokenInText =
      Convert_UnsignedInt32ToText((unsigned int)(size_t)userHandle->token);
#endif

   /* Value of tokenInText is always NULL on non-Windows platforms */
   /* coverity[dead_error_line] */
   packet = g_markup_printf_escaped(VGAUTH_CREATETICKET_REQUEST_FORMAT_START,
                                    ctx->comm.sequenceNumber,
                                    userHandle->userName,
                                    tokenInText ? tokenInText : "",
                                    ProtoUserHandleTypeString(userHandle));

   if (VGAUTH_AUTH_TYPE_SAML == userHandle->details.type) {
      sPacket = g_markup_printf_escaped(VGAUTH_USERHANDLESAMLINFO_FORMAT_START,
                                        userHandle->details.val.samlData.subject);
      packet = Proto_ConcatXMLStrings(packet, sPacket);

      ai = &(userHandle->details.val.samlData.aliasInfo);
      if (VGAUTH_SUBJECT_NAMED == ai->subject.type) {
         sPacket = g_markup_printf_escaped(VGAUTH_NAMEDALIASINFO_FORMAT,
                                           ai->subject.val.name,
                                           ai->comment);
      } else {
         sPacket = g_markup_printf_escaped(VGAUTH_ANYALIASINFO_FORMAT,
                                           ai->comment);
      }
      packet = Proto_ConcatXMLStrings(packet, sPacket);
      packet = Proto_ConcatXMLStrings(packet,
                                      g_strdup(VGAUTH_USERHANDLESAMLINFO_FORMAT_END));
   }
   packet = Proto_ConcatXMLStrings(packet,
                                   g_strdup(VGAUTH_CREATETICKET_REQUEST_FORMAT_END));

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to send packet\n", __FUNCTION__);
      goto quit;
   }

   err = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_CREATETICKET, &reply);
   if (VGAUTH_E_OK != err) {
      Warning("%s: read & parse reply failed\n", __FUNCTION__);
      goto quit;
   }

   *ticket = g_strdup(reply->replyData.createTicket.ticket);

   ctx->comm.sequenceNumber++;

quit:
   Proto_FreeReply(reply);
   g_free(packet);
   g_free(tokenInText);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_SendValidateTicketRequest --                                   */ /**
 *
 * Sends the ValidateTicket message and verifies the returning reply.
 *
 * @param[in]  ctx                       The VGAuthContext.
 * @param[in]  ticket                    The ticket to validate.
 * @param[out] userHandle                The new VGAuthUserHandle based on
 *                                       the ticket.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendValidateTicketRequest(VGAuthContext *ctx,
                                 const char *ticket,
                                 VGAuthUserHandle **userHandle)
{
   VGAuthError err;
   VGAuthError retCode = VGAUTH_E_FAIL;
   VGAuthUserHandle *newHandle = NULL;
   gchar *packet = NULL;
   ProtoReply *reply = NULL;
   HANDLE token = NULL;
#ifdef _WIN32
   unsigned int tokenValue;
#endif

   *userHandle = NULL;

   /*
    * Note that only root can validate a ticket.
    */
   if (!VGAuth_IsConnectedToServiceAsUser(ctx, SUPERUSER_NAME)) {
      err = VGAuth_ConnectToServiceAsUser(ctx, SUPERUSER_NAME);
      if (VGAUTH_E_OK != err) {
         retCode = err;
         goto done;
      }
   }

   packet = g_markup_printf_escaped(VGAUTH_VALIDATETICKET_REQUEST_FORMAT,
                                    ctx->comm.sequenceNumber,
                                    ticket);

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      retCode = err;
      VGAUTH_LOG_WARNING("%s", "VGAuth_CommSendData() failed");
      goto done;
   }

   err = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_VALIDATETICKET, &reply);
   if (VGAUTH_E_OK != err) {
      retCode = err;
      VGAUTH_LOG_WARNING("%s", "VGAuth_ReadAndParseResponse() failed");
      goto done;
   }

#ifdef _WIN32
   CHK_TEXT_TO_UINT32(tokenValue, reply->replyData.validateTicket.token,
                      goto done);
   token = (HANDLE)(size_t)tokenValue;
#endif

   err = VGAuth_CreateHandleForUsername(ctx,
                                        reply->replyData.validateTicket.userName,
                                        reply->replyData.validateTicket.type,
                                        token, &newHandle);
   if (err != VGAUTH_E_OK) {
#ifdef _WIN32
      CloseHandle(token);
#endif
      goto done;
   }

   if (VGAUTH_AUTH_TYPE_SAML == reply->replyData.validateTicket.type) {
      err = VGAuth_SetUserHandleSamlInfo(ctx,
                                         newHandle,
                                         reply->replyData.validateTicket.samlSubject,
                                         &(reply->replyData.validateTicket.aliasInfo));
      if (err != VGAUTH_E_OK) {
#ifdef _WIN32
         CloseHandle(token);
#endif
         goto done;
      }

   }

   *userHandle = newHandle;

   ctx->comm.sequenceNumber++;

   retCode = VGAUTH_E_OK;

done:

   Proto_FreeReply(reply);
   g_free(packet);

   return retCode;
}


/*
 ******************************************************************************
 * VGAuth_SendRevokeTicketRequest --                                     */ /**
 *
 * Sends the RevokeTicket message.
 *
 * @param[in]  ctx                       The VGAuthContext.
 * @param[in]  ticket                    The ticket to revoke.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendRevokeTicketRequest(VGAuthContext *ctx,
                               const char *ticket)
{
   VGAuthError err;
   gchar *packet = NULL;
   ProtoReply *reply = NULL;

   /*
    * Note that only root or the owner can revoke a ticket.
    *
    * If we're root, fine.  Otherwise, try to connect as current
    * user, which may also be root.
    */
   if (!VGAuth_IsConnectedToServiceAsUser(ctx, SUPERUSER_NAME)) {
      err = VGAuth_ConnectToServiceAsCurrentUser(ctx);
      if (VGAUTH_E_OK != err) {
         goto done;
      }
   }

   packet = g_markup_printf_escaped(VGAUTH_REVOKETICKET_REQUEST_FORMAT,
                                    ctx->comm.sequenceNumber,
                                    ticket);

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      VGAUTH_LOG_WARNING("%s", "VGAuth_CommSendData() failed");
      goto done;
   }

   err = VGAuth_ReadAndParseResponse(ctx, PROTO_REPLY_REVOKETICKET, &reply);
   if (VGAUTH_E_OK != err) {
      VGAUTH_LOG_WARNING("%s", "VGAuth_ReadAndParseResponse() failed");
      goto done;
   }

   ctx->comm.sequenceNumber++;

done:

   Proto_FreeReply(reply);
   g_free(packet);

   return err;
}


/*
 ******************************************************************************
 * VGAuth_SendValidateSamlBearerTokenRequest --                          */ /**
 *
 * Sends the ValidateSamlToken message and verifies the returning reply.
 *
 * @param[in]  ctx                       The VGAuthContext.
 * @param[in]  validateOnly              If set, only validation should
 *                                       occur, not access token creation.
 * @param[in]  samlToken                 The SAML token.
 * @param[in]  userName                  The user to authenticate as.
 * @param[out] userHandle                The resulting new userHandle.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
VGAuth_SendValidateSamlBearerTokenRequest(VGAuthContext *ctx,
                                          gboolean validateOnly,
                                          gboolean hostVerified,
                                          const char *samlToken,
                                          const char *userName,
                                          VGAuthUserHandle **userHandle)
{
   VGAuthError err = VGAUTH_E_OK;
   VGAuthUserHandle *newHandle = NULL;
   gchar *packet = NULL;
   ProtoReply *reply = NULL;
   HANDLE token = NULL;
#ifdef _WIN32
   unsigned int tokenValue;
#endif
   VGAuthUserHandleType hType;

   *userHandle = NULL;

   /*
    * ValidateSAMLBearerToken has no security restrictions, so we don't care
    * what user is used.
    */
   if (!VGAuth_IsConnectedToServiceAsAnyUser(ctx)) {
      err = VGAuth_ConnectToServiceAsCurrentUser(ctx);
      if (VGAUTH_E_OK != err) {
         goto quit;
      }
   }

   packet = g_markup_printf_escaped(VGAUTH_VALIDATESAMLBEARERTOKEN_REQUEST_FORMAT,
                                    ctx->comm.sequenceNumber,
                                    samlToken,
                                    userName ? userName : "",
                                    validateOnly ? "1" : "0",
                                    hostVerified ? "1" : "0");

   err = VGAuth_CommSendData(ctx, packet);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to send packet\n", __FUNCTION__);
      goto quit;
   }

   err = VGAuth_ReadAndParseResponse(ctx,
                                     PROTO_REPLY_VALIDATE_SAML_BEARER_TOKEN,
                                     &reply);
   if (VGAUTH_E_OK != err) {
      Warning("%s: read & parse reply failed\n", __FUNCTION__);
      goto quit;
   }


   if (!validateOnly) {
      hType = VGAUTH_AUTH_TYPE_SAML;
#ifdef _WIN32
      CHK_TEXT_TO_UINT32(tokenValue, reply->replyData.validateSamlBToken.token,
                         goto quit);
      token = (HANDLE)(size_t)tokenValue;
#endif
   } else {
      hType = VGAUTH_AUTH_TYPE_SAML_INFO_ONLY;
   }

   err = VGAuth_CreateHandleForUsername(ctx,
                                        reply->replyData.validateSamlBToken.userName,
                                        hType,
                                        token, &newHandle);
   if (err != VGAUTH_E_OK) {
      Warning("%s: failed to create userHandle\n", __FUNCTION__);
      goto quit;
   }

   /*
    * Pull the rest of the userHandle info out of packet and add it
    * to userHandle
    */
   err = VGAuth_SetUserHandleSamlInfo(ctx,
                                      newHandle,
                                      reply->replyData.validateSamlBToken.samlSubject,
                                      &(reply->replyData.validateSamlBToken.aliasInfo));
   if (err != VGAUTH_E_OK) {
      Warning("%s: failed to set the SAML info on the userHandle\n", __FUNCTION__);
      goto quit;
   }

   *userHandle = newHandle;

   ctx->comm.sequenceNumber++;

quit:
   Proto_FreeReply(reply);
   g_free(packet);

   return err;
}
  0707010000043F000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003100000000open-vm-tools-12.5.2/open-vm-tools/vgauth/public  07070100000440000081A40000000000000000000000016822550500001802000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/public/VGAuthAlias.h    /*********************************************************
 * Copyright (C) 2012-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file VGAuthAlias.h
 *
 * Client library alias management API definitions.
 *
 * @addtogroup vgauth_alias VGAuth Alias Management
 * @{
 *
 */
#ifndef _VGAUTHALIAS_H_
#define _VGAUTHALIAS_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "VGAuthCommon.h"

/**
 * The types of subjects.  Any is a special case.
 */
typedef enum {
   /** The Subject field in a SAML token must match in the token verification process. */
   VGAUTH_SUBJECT_NAMED,

   /** Any Subject field in a SAML token can be matched in the token verification process. */
   VGAUTH_SUBJECT_ANY,
} VGAuthSubjectType;


/**
 * VGAuthSubject is either ANY or NAMED, in which case it contains
 * the Subject name.
 *
 * When authenticating a SAML token, the Subject is used to determine
 * what guest user the SAML token can be authenticated as.  If no
 * entry in the Alias store is found using that Subject, then
 * if the special ANY subject exists in the alias store, that entry
 * will be used.
 */
typedef struct VGAuthSubject {
   VGAuthSubjectType type; /**< The subject type. */
   union {
      char *name;          /**< The subject name, if type VGAUTH_SUBJECT_NAMED */
   } val;
} VGAuthSubject;


/**
 * VGAuthAliasInfo combines a subject and a comment.  Each alias can
 * have its own comment describing its use.
 */
typedef struct VGAuthAliasInfo {
   VGAuthSubject subject; /**< The subject. */
   char *comment;         /**< User-supplied data to describe the subject. */
} VGAuthAliasInfo;


/**
 * Describes all subjects that are associated with a certficate.
 */
typedef struct VGAuthUserAlias {
   char *pemCert;                   /**< The certficate in PEM format.  */
   int numInfos;
   VGAuthAliasInfo *infos;          /**< The AliasInfos associated with the
                                      certificate. */
} VGAuthUserAlias;


/**
 * Describes an entry in the Alias mapping file.
 */
typedef struct VGAuthMappedAlias {
   char *pemCert;          /**< The certificate in PEM format. */
   int numSubjects;        /**< The number of Subjects associated with
                             the mapping file entry. */
   VGAuthSubject *subjects;  /**< The Subjects  associated with
                               the mapping file entry. */

   char *userName;        /**< The username associated with the  mapping file entry. */
} VGAuthMappedAlias;


/*
 * Adds the given VGAuthAliasInfo associated with the pemCert
 * to the alias store of @a userName.
 *
 * If addMapping is TRUE, also create an entry in the mapping file from
 * the cert and subject to @a userName.
 */
VGAuthError VGAuth_AddAlias(VGAuthContext *ctx,
                            const char *userName,
                            VGAuthBool addMapping,
                            const char *pemCert,
                            VGAuthAliasInfo *si,
                            int numExtraParams,
                            const VGAuthExtraParams *extraParams);

/*
 * Removes the VGAuthAliasInfo from the @a userName alias store for @a
 * subject, also removing any associated mapping entry.
 */
VGAuthError VGAuth_RemoveAlias(VGAuthContext *ctx,
                               const char *userName,
                               const char *pemCert,
                               VGAuthSubject *subject,
                               int numExtraParams,
                               const VGAuthExtraParams *extraParams);
/*
 * Removes all VGAuthUserAliases with the matching PEM certificate from
 * the @a userName alias store for all subjects, also removing any associated
 * mapping entry.
 */
VGAuthError VGAuth_RemoveAliasByCert(VGAuthContext *ctx,
                                     const char *userName,
                                     const char *pemCert,
                                     int numExtraParams,
                                     const VGAuthExtraParams *extraParams);

/*
 * Lists all the VGAuthUserAlias from the alias store belonging
 * to @a userName.
 */
VGAuthError VGAuth_QueryUserAliases(VGAuthContext *ctx,
                                    const char *userName,
                                    int numExtraParams,
                                    const VGAuthExtraParams *extraParams,
                                    int *num,                     // OUT
                                    VGAuthUserAlias **uaList);    // OUT

/*
 * Lists all the certificates/subject pairs in the mapping file and their
 * associated user.
 */
VGAuthError VGAuth_QueryMappedAliases(VGAuthContext *ctx,
                                      int numExtraParams,
                                      const VGAuthExtraParams *extraParams,
                                      int *num,                       // OUT
                                      VGAuthMappedAlias **maList);    // OUT

/*
 * Data structure cleanup functions.
 */

/*
 * Frees an array of VGAuthAlias.
 */
void VGAuth_FreeUserAliasList(int num, VGAuthUserAlias *uaList);

/*
 * Frees a VGAuthAliasInfo and contents.
 */
void VGAuth_FreeAliasInfo(VGAuthAliasInfo *ai);

/*
 * Frees an array of VGAuthMappedAlias.
 */
void VGAuth_FreeMappedAliasList(int num, VGAuthMappedAlias *maList);

/** @} */

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif   // _VGAUTHALIAS_H_

  07070100000441000081A40000000000000000000000016822550500001DFF000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/vgauth/public/VGAuthAuthentication.h   /*********************************************************
 * Copyright (c) 2011-2019,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 *
 * @file VGAuthAuthentication.h
 *
 * Client library authentication and impersonation API defintions.
 *
 * @addtogroup vgauth_auth VGAuth Authentication and Impersonation
 * @{
 *
 */
#ifndef _VGAUTHAUTHENTICATION_H_
#define _VGAUTHAUTHENTICATION_H_

#ifdef __cplusplus
extern "C" {
#endif

/* Include stddef.h to get the definition of size_t. */
#include <stddef.h>
#include "VGAuthAlias.h"

#ifdef _WIN32
#if !defined(WIN32_LEAN_AND_MEAN) && !defined(VGAUTH_NO_WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif

#include "VGAuthCommon.h"

/* Ticket APIs */

/**
 * Opaque handle to data describing a user.
 */
typedef struct VGAuthUserHandle VGAuthUserHandle;

/*
 * The possible types of VGAuthUserHandles.
 */
typedef enum {
   /** Unknown type */
   VGAUTH_AUTH_TYPE_UNKNOWN,
   /** The userHandle was created using namePassword validation. */
   VGAUTH_AUTH_TYPE_NAMEPASSWORD,
   /** The userHandle was created using SSPI validation. */
   VGAUTH_AUTH_TYPE_SSPI,
   /** The userHandle was created using SAML validation. */
   VGAUTH_AUTH_TYPE_SAML,
   /** The userHandle was created using SAML validation, but is not
    * valid for impersonation or ticket creation. */
   VGAUTH_AUTH_TYPE_SAML_INFO_ONLY,
} VGAuthUserHandleType;


/* VGAuthUserHandle APIs */
/*
 * Simple accessor for the user associated with a VGAuthUserHandle.
 */
VGAuthError VGAuth_UserHandleUsername(VGAuthContext *ctx,
                                      VGAuthUserHandle *handle,
                                      char **userName);                // OUT

#ifdef _WIN32
/*
 * Special accessor functions for Windows to set and get user and
 * profile handles.
 */

VGAuthError VGAuth_UserHandleAccessToken(VGAuthContext *ctx,
                                         VGAuthUserHandle *handle,
                                         HANDLE *authToken);

VGAuthError VGAuth_UserHandleGetUserProfile(VGAuthContext *ctx,
                                            VGAuthUserHandle *handle,
                                            HANDLE *hProfile);

VGAuthError VGAuth_UserHandleSetUserProfile(VGAuthContext *ctx,
                                            VGAuthUserHandle *handle,
                                            HANDLE hProfile);
#endif


/*
 * Returns the type of the UserHandle;  VGAUTH_AUTH_TYPE_UNKNOWN
 * on error.
 */
VGAuthUserHandleType VGAuth_UserHandleType(VGAuthContext *ctx,
                                           VGAuthUserHandle *handle);

VGAuthError VGAuth_UserHandleSamlData(VGAuthContext *ctx,
                                      VGAuthUserHandle *handle,
                                      char **samlTokenSubject,   // OUT
                                      VGAuthAliasInfo **matchedAliasInfo); // OUT



/*
 * Releases a VGAuthUserHandle.
 */
void VGAuth_UserHandleFree(VGAuthUserHandle *handle);


/*
 * Asks for a new ticket to be created associated with 'handle'.
 */
VGAuthError VGAuth_CreateTicket(VGAuthContext *ctx,
                                VGAuthUserHandle *handle,
                                int numExtraParams,
                                const VGAuthExtraParams *extraParams,
                                char **newTicket);      // OUT

/*
 * Returns the VGAuthUserHandle associated with the ticket.
 */
VGAuthError VGAuth_ValidateTicket(VGAuthContext *ctx,
                                  const char *ticket,
                                  int numExtraParams,
                                  const VGAuthExtraParams *extraParams,
                                  VGAuthUserHandle **handle);         // OUT


/*
 * Revokes a ticket.
 */
VGAuthError VGAuth_RevokeTicket(VGAuthContext *ctx,
                                const char *ticket,
                                int numExtraParams,
                                const VGAuthExtraParams *extraParams);



/* Name/Password authentication APIs */

/*
 * If the password is valid for userName, returns a VGAuthUserHandle.
 */
VGAuthError VGAuth_ValidateUsernamePassword(VGAuthContext *ctx,
                                            const char *userName,
                                            const char *password,
                                            int numExtraParams,
                                            const VGAuthExtraParams *extraParams,
                                            VGAuthUserHandle **handle); // OUT




/* SSPI Authentication APIs */

/*
 * Generates an SSPI challenge text.
 */
VGAuthError VGAuth_GenerateSSPIChallenge(VGAuthContext *ctx,
                                         size_t sspiRequestLen,
                                         const unsigned char *sspiRequest,
                                         int numExtraParams,
                                         const VGAuthExtraParams *extraParams,
                                         unsigned int *id,              // OUT
                                         size_t *challengeLen,          // OUT
                                         unsigned char **challenge);    // OUT

/*
 * Validates an SSPI response.
 */
VGAuthError VGAuth_ValidateSSPIResponse(VGAuthContext *ctx,
                                        unsigned int id,
                                        size_t responseLen,
                                        const unsigned char *response,
                                        int numExtraParams,
                                        const VGAuthExtraParams *extraParams,
                                        VGAuthUserHandle **handle);     // OUT



/* Alias Authentication APIs */

/*
 * SAML token validation.
 */

#define  VGAUTH_PARAM_VALIDATE_INFO_ONLY  "validateInfoOnly"

#define  VGAUTH_PARAM_SAML_HOST_VERIFIED "hostVerified"

VGAuthError VGAuth_ValidateSamlBearerToken(VGAuthContext *ctx,
                                           const char *samlToken,
                                           const char *userName,
                                           int numExtraParams,
                                           const VGAuthExtraParams *extraParams,
                                           VGAuthUserHandle **handle);  // OUT

/* Impersonation APIs */

#define  VGAUTH_PARAM_LOAD_USER_PROFILE  "loadUserProfile"

/*
 * Start impersonating the user described by VGAuthUserHandle.
 */
VGAuthError VGAuth_Impersonate(VGAuthContext *ctx,
                               VGAuthUserHandle *handle,
                               int numExtraParams,
                               const VGAuthExtraParams *extraParams);

/*
 * Ends the impersonation, restores the process to root.
 */
VGAuthError VGAuth_EndImpersonation(VGAuthContext *ctx);

/** @} */


#ifdef __cplusplus
} /* extern "C" */
#endif

#endif   // _VGAUTHAUTHENTICATION_H_
 07070100000442000081A40000000000000000000000016822550500000DF7000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/vgauth/public/VGAuthCommon.h   /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file VGAuthCommon.h
 *
 * Core APIs for VGAuth.
 *
 * @addtogroup vgauth_init VGAuth Initialization and Error Handling
 * @{
 *
 */
#ifndef _VGAUTHCOMMON_H_
#define _VGAUTHCOMMON_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "VGAuthError.h"


/**
 * VGAuth boolean
 */
typedef int VGAuthBool;
enum {
   /** False */
   VGAUTH_FALSE = 0,
   /** True */
   VGAUTH_TRUE = 1,
};


/**
 * Opaque handle to a VGAuth context, which contains context state and data.
 */
typedef struct VGAuthContext VGAuthContext;

/* Utility APIs */


/**
 * VGAuth extra parameters. Used to pass additional information to the
 * VGAuth library when making an API call.
 */
typedef struct VGAuthExtraParams {
   char *name;                      /**< The parameter's name. */
   char *value;                     /**< The parameters's value.  */
} VGAuthExtraParams;

#define VGAUTH_PARAM_VALUE_TRUE  "true"
#define VGAUTH_PARAM_VALUE_FALSE "false"

/*
 * Initalizes library, and specifies any configuration information.
 */
VGAuthError VGAuth_Init(const char *applicationName,
                        int numExtraParams,
                        const VGAuthExtraParams *extraParams,
                        VGAuthContext **ctx);

/*
 * Cleans up a context and any associated data.
 */
VGAuthError VGAuth_Shutdown(VGAuthContext *ctx);


/*
 * Provides any OS specific support that may be required.
 */
VGAuthError VGAuth_InstallClient(VGAuthContext *ctx,
                                 int numExtraParams,
                                 const VGAuthExtraParams *extraParams);


/*
 * Removes any OS specific support that may be required.
 */
VGAuthError VGAuth_UninstallClient(VGAuthContext *ctx,
                                   int numExtraParams,
                                   const VGAuthExtraParams *extraParams);


/**
 * The callback function used by #VGAuth_SetLogHandler().
 */
typedef void VGAuthLogFunc(const char *logDomain,
                           int logLevel,
                           const char *msg,
                           void *userData);

/*
 * Sets the log handler.  All VGAuth and glib errors, warnings and debug
 * messages will go through logFunc.
 */
VGAuthError VGAuth_SetLogHandler(VGAuthLogFunc logFunc,
                                 void *userData,
                                 int numExtraParams,
                                 const VGAuthExtraParams *extraParams);

void VGAuth_FreeBuffer(void *buffer);

const char *VGAuth_GetErrorName(VGAuthError err);
const char *VGAuth_GetErrorText(VGAuthError err,
                                const char *language);

/** @} */

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif   // _VGAUTHCOMMON_H_
 07070100000443000081A400000000000000000000000168225505000018A9000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/public/VGAuthError.h    /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file VGAuthError.h
 *
 * Enums common to both client library and service.
 *
 * @addtogroup vgauth_error VGAuth Error Codes
 * @{
 *
 */
#ifndef _VGAUTHERROR_H_
#define _VGAUTHERROR_H_

#ifdef __cplusplus
extern "C" {
#endif

#ifdef _WIN32
#include <BaseTsd.h>

typedef UINT32 vga_uint32;
typedef unsigned __int64 vga_uint64;
#else
#include <stdint.h>

typedef uint32_t vga_uint32;
typedef long long unsigned int vga_uint64;
#endif

/**
 * Returns the base VGAuthError.
 */
#define VGAUTH_ERROR_CODE(err) ((err) & 0xFFFFFFFF)

/**
 * Use to test the return value from a VGAuth API for success.
 */
#define VGAUTH_SUCCEDED(err)   (VGAUTH_E_OK == (err))

/**
 * Use to test the return value from a VGAuth API for failure.
 */
#define VGAUTH_FAILED(err)     (VGAUTH_E_OK != (err))

/*
 * Printf format specifiers for VGAuthError.
 * Use them like this:
 *    printf("VGAUTHERR_FMT64X"\n", err);
 */

#ifdef _MSC_VER
   #define VGAUTHERR_FMT64X     "0x%I64x"
   #define VGAUTHERR_FMT64      "%I64u"
#elif defined __APPLE__
   /* Mac OS hosts use the same formatters for 32- and 64-bit. */
   #define VGAUTHERR_FMT64X "0x%llx"
   #define VGAUTHERR_FMT64  "%llu"
#elif __GNUC__
   #if defined(sun) || defined(__FreeBSD__)
      #define VGAUTHERR_FMT64X    "0x%llx"
      #define VGAUTHERR_FMT64     "%llu"
   #else
      #define VGAUTHERR_FMT64X    "0x%Lx"
      #define VGAUTHERR_FMT64     "%Lu"
   #endif
#else
   #error - Need compiler define for FMT64 and FMTSZ
/* Doxygen input */

/**
 * Format specifier for VGAuth errors as a hexadecimal value.
 * @n
 * Example usage:
 * @code
 *    printf("Error in hex: "VGAUTHERR_FMT64X"\n", err);
 * @endcode
 */
#define VGAUTHERR_FMT64X

/**
 * Format specifier for VGAuth errors as a decimal value.
 * @n
 * Example usage:
 * @code
 *    printf("Error: "VGAUTHERR_FMT64"\n", err);
 * @endcode
 */
#define VGAUTHERR_FMT64

#endif

/**
 * This is an expanded view of a VGAuthError.
 * Every VGAuthError is a 64 bit unsigned int, so it can fit into this
 * struct.
 *
 * The extraError is optional.  It is guarenteed to be 0 when the error
 * is @b VGAUTH_E_OK, so any program that checks (@b VGAUTH_E_OK == err) or
 * (@b VGAUTH_E_OK != err) will always work.
 *
 * extraError may be set in certain error conditions when extra info may
 * be of use to the caller.
 *
 * The basic error field is a VGAuth error value, and it's the lsb
 * of the new struct. This means that a 64-bit error can be assigned
 * to an enum constant, like an integer. For example, err = @b VGAUTH_E_FAIL;
 * works.  This just leaves the flags and extraError fields as 0.
 */
typedef
#ifdef _MSC_VER
#   pragma pack(push, 1)
#elif __GNUC__
#else
#   error Compiler packing...
#endif
struct VGAuthErrorFields {
   vga_uint32   error;
   vga_uint32   extraError;
}
#ifdef _MSC_VER
#   pragma pack(pop)
#elif __GNUC__
__attribute__((__packed__))
#else
#   error Compiler packing...
#endif
VGAuthErrorFields;

/**
 * Extra error accessor macro.  Use to get additional error info from
 * #VGAUTH_E_SYSTEM_ERRNO or #VGAUTH_E_SYSTEM_WINDOWS.
 */
#define VGAUTH_ERROR_EXTRA_ERROR(err) ((VGAuthErrorFields *) &err)->extraError

/**
 * Error codes.
 */

typedef vga_uint64 VGAuthError;
enum {
   /** No error. */
   VGAUTH_E_OK                         = 0,

   /** Unspecified failure. */
   VGAUTH_E_FAIL                       = 1,
   /** Invalid argument passed to API. */
   VGAUTH_E_INVALID_ARGUMENT           = 2,
   /** Invalid certficate. */
   VGAUTH_E_INVALID_CERTIFICATE        = 3,
   /** Permission denied. */
   VGAUTH_E_PERMISSION_DENIED          = 4,
   /** Out of memory for operation. */
   VGAUTH_E_OUT_OF_MEMORY              = 5,
   /** Internal communication error between client and service. */
   VGAUTH_E_COMM                       = 6,
   /** Not implemented. */
   VGAUTH_E_NOTIMPLEMENTED             = 7,
   /** Not connected to service. */
   VGAUTH_E_NOT_CONNECTED              = 8,
   /** Version mismatch. */
   VGAUTH_E_VERSION_MISMATCH           = 9,
   /** Security violation. */
   VGAUTH_E_SECURITY_VIOLATION         = 10,
   /** The certficate already exists. */
   VGAUTH_E_CERT_ALREADY_EXISTS        = 11,
   /** Authentication denied. */
   VGAUTH_E_AUTHENTICATION_DENIED      = 12,
   /** Ticket is invalid. */
   VGAUTH_E_INVALID_TICKET             = 13,
   /**
    * The cert was found associated with more than one user, or a chain
    * contained multiple matches againast the mappings file.
    */
   VGAUTH_E_MULTIPLE_MAPPINGS          = 14,
   /** The context is already impersonating. */
   VGAUTH_E_ALREADY_IMPERSONATING      = 15,
   /** User does not exist. */
   VGAUTH_E_NO_SUCH_USER               = 16,
   /** Operation failed because service does not appear to be running. */
   VGAUTH_E_SERVICE_NOT_RUNNING        = 17,
   /**
    * Failed to process an OS-specific Posix API operation;
    * use #VGAUTH_ERROR_EXTRA_ERROR for the OS specific Posix errno.
    */
   VGAUTH_E_SYSTEM_ERRNO               = 18,
   /**
    * Failed to process an OS-specific Win32 API operation;
    * use #VGAUTH_ERROR_EXTRA_ERROR for the OS specific Windows errno.
    */
   VGAUTH_E_SYSTEM_WINDOWS             = 19,
   /** Maximum number of connections is reached */
   VGAUTH_E_TOO_MANY_CONNECTIONS       = 20,
   /** Operation not supported. */
   VGAUTH_E_UNSUPPORTED                = 21,
};


/** @} */

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif   // _VGAUTHERROR_H_
   07070100000444000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service 07070100000445000081A40000000000000000000000016822550500000FEE000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/Makefile.am ################################################################################
### Copyright (C) 2014-2020, 2022, 2023 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = VGAuthService

VGAuthService_SOURCES =
VGAuthService_SOURCES += main.c
VGAuthService_SOURCES += fileLogger.c
VGAuthService_SOURCES += gio.c
VGAuthService_SOURCES += log.c
VGAuthService_SOURCES += servicePosix.c
VGAuthService_SOURCES += signalSource.c
VGAuthService_SOURCES += ../serviceImpl/alias.c
VGAuthService_SOURCES += ../serviceImpl/file.c
VGAuthService_SOURCES += ../serviceImpl/filePosix.c
VGAuthService_SOURCES += ../serviceImpl/netPosix.c
VGAuthService_SOURCES += ../serviceImpl/proto.c
VGAuthService_SOURCES += ../serviceImpl/random.c
VGAuthService_SOURCES += ../serviceImpl/saml-xmlsec1.c
VGAuthService_SOURCES += ../serviceImpl/service.c
VGAuthService_SOURCES += ../serviceImpl/ticket.c
VGAuthService_SOURCES += ../serviceImpl/verify.c

VGAuthService_SOURCES += ../common/audit.c
VGAuthService_SOURCES += ../common/certverify.c
VGAuthService_SOURCES += ../common/i18n.c
VGAuthService_SOURCES += ../common/prefs.c
VGAuthService_SOURCES += ../common/usercheck.c
VGAuthService_SOURCES += ../common/VGAuthLog.c
VGAuthService_SOURCES += ../common/VGAuthUtil.c
VGAuthService_SOURCES += ../common/vmxlog.c
VGAuthService_SOURCES += ../common/vmxrpc.c

VGAuthServicedir = /etc/vmware-tools/vgauth/schemas
VGAuthService_SCRIPTS =
VGAuthService_SCRIPTS += ../serviceImpl/schemas/datatypes.dtd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/saml-schema-assertion-2.0.xsd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/xenc-schema.xsd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/xmldsig-core-schema.xsd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/XMLSchema.dtd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/XMLSchema-hasFacetAndProperty.xsd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/XMLSchema-instance.xsd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/XMLSchema.xsd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/xml.xsd
VGAuthService_SCRIPTS += ../serviceImpl/schemas/catalog.xml

VGAuthService_CPPFLAGS =
VGAuthService_CPPFLAGS += @GLIB2_CPPFLAGS@
VGAuthService_CPPFLAGS += @XMLSEC1_CPPFLAGS@
VGAuthService_CPPFLAGS += @SSL_CPPFLAGS@
VGAuthService_CPPFLAGS += -I$(top_srcdir)/vgauth/public
VGAuthService_CPPFLAGS += -I$(top_srcdir)/vgauth/common
VGAuthService_CPPFLAGS += -I$(top_srcdir)/vgauth/serviceImpl

VGAuthService_LDADD =
VGAuthService_LDADD += @GLIB2_LIBS@
VGAuthService_LDADD += @GTHREAD_LIBS@
VGAuthService_LDADD += @XMLSEC1_LIBS@
VGAuthService_LDADD += @SSL_LIBS@
VGAuthService_LDADD += -lssl
VGAuthService_LDADD += -lcrypto

if HAVE_ICU
   VGAuthService_LDADD += @ICU_LIBS@
endif

if VGAUTH_USE_CXX
  VGAuthService_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS)     \
                            $(LIBTOOLFLAGS) --mode=link $(CXX)       \
                            $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
                            $(LDFLAGS) -o $@
else
   VGAuthService_LINK = $(LINK)
endif

VGAuthMsgDir = $(datadir)/open-vm-tools

# Message catalogs.
install-data-hook:
	@INSTVMSG@ VGAuthService $(srcdir)/l10n $(DESTDIR)$(datadir)
	cat vgauth.conf | sed -e"s!@@VGAUTHSCHEMADIR@@!$(VGAuthServicedir)!" \
                   |  sed -e"s!@@VGAUTHMSGDIR@@!$(VGAuthMsgDir)!" > $(DESTDIR)/etc/vmware-tools/vgauth.conf
  07070100000446000081A40000000000000000000000016822550500002F88000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/fileLogger.c    /*********************************************************
 * Copyright (c) 2011-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file fileLogger.c
 *
 * Logger that uses file streams and provides optional log rotation.
 * Heavily 'borrows' from bora-lib/apps/vmtoolslib/fileLogger.c
 */

#include <stdio.h>
#include <string.h>
#include <glib/gstdio.h>
#ifdef _WIN32
#  include <process.h>
#  include <windows.h>
#  include <io.h>
#else
#  include <unistd.h>
#endif
#include "service.h"

typedef struct FileLoggerData {
   FILE             *file;
   gchar            *path;
   gint              logSize;
   gint64            maxSize;
   guint             maxFiles;
   gboolean          append;
   gboolean          error;
   GRWLock           lock;
} FileLoggerData;


/*
 ******************************************************************************
 * ServiceFileLoggerOpen --                                              */ /**
 *
 * Opens a log file for writing, backing up the existing log file if one is
 * present. Only one old log file is preserved.
 *
 * @note Make sure this function is called with the write lock held.
 *
 * @param[in] data   Log handler data.
 *
 * @return Log file pointer (NULL on error).
 *
 ******************************************************************************
 */

static FILE *
ServiceFileLoggerOpen(FileLoggerData *data)
{
   FILE *logfile = NULL;
   gchar *path;

   ASSERT(data != NULL);
   path = g_strdup_printf("%s.%d", data->path, 0);

   if (g_file_test(path, G_FILE_TEST_EXISTS)) {
      /* GStatBuf was added in 2.26. */
      GStatBuf fstats;

      /*
       * In order to determine whether we should rotate the logs,
       * we are calling the system call stat() to get the existing log file
       * size.
       * The time of check vs. time of use issue does not apply to this use
       * case, as even the file size is increasing, it will not affect the log
       * rotation decision. So Suppress the fs_check_call coverity warning.
       */
      /* coverity[fs_check_call] */
      if (g_stat(path, &fstats) > -1) {
         g_atomic_int_set(&data->logSize, (gint) fstats.st_size);
      }

      if (!data->append || g_atomic_int_get(&data->logSize) >= data->maxSize) {
         /*
          * Find the last log file and iterate back, changing the indices as we go,
          * so that the oldest log file has the highest index (the new log file
          * will always be index "0"). When not rotating, "maxFiles" is 1, so we
          * always keep one backup.
          */
         guint id;
         GPtrArray *logfiles = g_ptr_array_new();

         /*
          * Find the id of the last log file. The pointer array will hold
          * the names of all existing log files + the name of the last log
          * file, which may or may not exist.
          */
         for (id = 0; id < data->maxFiles; id++) {
            gchar *fname = g_strdup_printf("%s.%d", data->path, id);

            g_ptr_array_add(logfiles, fname);
            if (!g_file_test(fname, G_FILE_TEST_IS_REGULAR)) {
               break;
            }
         }

         /* Rename the existing log files, increasing their index by 1. */
         for (id = logfiles->len - 1; id > 0; id--) {
            gchar *dest = g_ptr_array_index(logfiles, id);
            gchar *src = g_ptr_array_index(logfiles, id - 1);

            if (!g_file_test(dest, G_FILE_TEST_IS_DIR) &&
                (!g_file_test(dest, G_FILE_TEST_EXISTS) ||
                 g_unlink(dest) == 0)) {
               /*
                * We should ignore an unlikely rename() system call failure,
                * as we should keep our service running with non-critical errors.
                * We cannot log the error because we are already in the log
                * handler context to avoid crash or recursive logging loop.
                */
               /* coverity[check_return] */
               g_rename(src, dest);
            } else {
               g_unlink(src);
            }
         }

         /* Cleanup. */
         for (id = 0; id < logfiles->len; id++) {
            g_free(g_ptr_array_index(logfiles, id));
         }
         g_ptr_array_free(logfiles, TRUE);
         g_atomic_int_set(&data->logSize, 0);
         data->append = FALSE;
      }
   }

   /* coverity[toctou] */
   logfile = g_fopen(path, data->append ? "a" : "w");
   /*
    * Make log readable only by root/Administrator.  Just log any error;
    * better a readable log than none at all so any issues are logged.
    */
#ifdef _WIN32
   {
      UserAccessControl uac;

      /* The default access only allows self and administrators */
      if (!UserAccessControl_Default(&uac)) {
         VGAUTH_LOG_WARNING("failed to set up logfile %s access control",
                            path);
      } else {
         BOOL ok;

         ok = WinUtil_SetFileSecurity(path,
                                UserAccessControl_GetSecurityDescriptor(&uac));
         if (!ok) {
            VGAUTH_LOG_WARNING("WinUtil_SetFileSecurity(%s) failed", path);
         }
         UserAccessControl_Destroy(&uac);
      }
   }
#else
   (void) ServiceFileSetPermissions(path, 0600);
#endif
   g_free(path);

#ifndef VMX86_DEBUG
   /*
    * Redirect anything unexpected that uses stderr.
    */
   if (NULL != logfile) {
      if (dup2(fileno(logfile), 2) == -1) {
         fprintf(logfile, "%s: failed to dup stderr to logfile\n", __FUNCTION__);
      }
   }
#endif

   return logfile;
}


/*
 ******************************************************************************
 * ServiceFileLogger_Log --                                              */ /**
 *
 * Logs a message to the configured destination file. Also opens the file for
 * writing if it hasn't been done yet.
 *
 * @param[in] domain    Log domain.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 * @param[in] _data     FileLoggerData pointer.
 *
 * @return Whether the message was successfully written.
 *
 ******************************************************************************
 */

gboolean
ServiceFileLogger_Log(const gchar *domain,
                      GLogLevelFlags level,
                      const gchar *message,
                      void *_data)
{
   gboolean ret = FALSE;
   FileLoggerData *data = (FileLoggerData *) _data;

   g_rw_lock_reader_lock(&data->lock);

   if (data->error) {
      goto exit;
   }

   if (data->file == NULL) {
      if (data->path == NULL) {
         /* We should only get in this situation if the domain's log level is "none". */
         ret = TRUE;
         goto exit;
      } else {
         /*
          * We need to drop the read lock and acquire a write lock to open
          * the log file.
          */
         g_rw_lock_reader_unlock(&data->lock);
         g_rw_lock_writer_lock(&data->lock);
         if (data->file == NULL && !data->error) {
            data->file = ServiceFileLoggerOpen(data);
            if (data->file == NULL) {
               data->error = TRUE;
               fprintf(stderr, "Unable to open log file %s\n", data->path);
            }
         }
         g_rw_lock_writer_unlock(&data->lock);
         g_rw_lock_reader_lock(&data->lock);
         if (data->error) {
	    /* Error set here or in another thread */
            goto exit;
         }
      }
   }

   /* Write the log file and do log rotation accounting. */
   if (fputs(message, data->file) >= 0) {
      if (data->maxSize > 0) {
         g_atomic_int_add(&data->logSize, (int) strlen(message));
#if defined(_WIN32)
         /* Account for \r. */
         g_atomic_int_add(&data->logSize, 1);
#endif
         if (g_atomic_int_get(&data->logSize) >= data->maxSize) {
            /* Drop the reader lock, grab the writer lock and re-check. */
            g_rw_lock_reader_unlock(&data->lock);
            g_rw_lock_writer_lock(&data->lock);
            if (!data->error && data->file != NULL &&
                g_atomic_int_get(&data->logSize) >= data->maxSize) {
               fclose(data->file);
               data->append = FALSE;
               data->file = ServiceFileLoggerOpen(data);
               if (data->file == NULL) {
                  data->error = TRUE;
                  fprintf(stderr, "Unable to reopen log file %s\n", data->path);
               }
            }
            g_rw_lock_writer_unlock(&data->lock);
            g_rw_lock_reader_lock(&data->lock);
         } else {
            fflush(data->file);
         }
      } else {
         fflush(data->file);
      }
      ret = TRUE;
   }

exit:
   g_rw_lock_reader_unlock(&data->lock);
   return ret;
}


/*
 ******************************************************************************
 * ServiceFileLogger_Init --                                             */ /**
 *
 * Initializes the file logger.
 *
 * @return The file logger data, or NULL on failure.
 *
 ******************************************************************************
 */

void *
ServiceFileLogger_Init(void)
{
   gchar *logFileName;
   FileLoggerData *data;
   gchar *defaultFilename;

#ifdef _WIN32
   {
      WCHAR pathW[MAX_PATH];

      if (GetTempPathW(MAX_PATH, pathW) != 0) {
         char *pathA = Convert_Utf16ToUtf8(__FUNCTION__,
                                           __FILE__, __LINE__,
                                           pathW);
         if (NULL == pathA) {
            Warning("%s: out of memory converting filePath\n", __FUNCTION__);
            return NULL;
         }
         defaultFilename = g_strdup_printf("%s%s", pathA, LOGFILENAME_DEFAULT);
         g_free(pathA);
      } else {
         defaultFilename = g_strdup(LOGFILENAME_PATH_DEFAULT);
      }
   }
#else
   defaultFilename = g_strdup(LOGFILENAME_PATH_DEFAULT);
#endif

   logFileName = Pref_GetString(gPrefs,
                                VGAUTH_PREF_NAME_LOGFILE,
                                VGAUTH_PREF_GROUP_NAME_SERVICE,
                                defaultFilename);

   Debug("%s: Using '%s' as logfile\n", __FUNCTION__, logFileName);

   g_free(defaultFilename);
   data = g_malloc0(sizeof(FileLoggerData));

   /*
    * XXX
    *
    * Not sure we want this -- it means we'll append to any existing
    * file, which preserves some data, but it may also cause confusion
    * when the service start isn't at the top of the file.
    */
   data->append = TRUE;

   g_rw_lock_init(&data->lock);

   if (logFileName != NULL) {
      data->path = g_filename_from_utf8(logFileName, -1, NULL, NULL, NULL);
      ASSERT(data->path != NULL);
      g_free(logFileName);

      /*
       * Read the rolling file configuration. By default, log rotation is enabled
       * with a max file size of 10MB and a maximum of 10 log files kept around.
       */
      data->maxFiles = Pref_GetInt(gPrefs,
                                   VGAUTH_PREF_NAME_MAX_OLD_LOGFILES,
                                   VGAUTH_PREF_GROUP_NAME_SERVICE, 10);
      if (data->maxFiles < 1) {
         data->maxFiles = 1;
      }

      /* Add 1 to account for the active log file. */
      data->maxFiles += 1;

      data->maxSize = Pref_GetInt(gPrefs,
                                  VGAUTH_PREF_NAME_MAX_LOGSIZE,
                                  VGAUTH_PREF_GROUP_NAME_SERVICE, 10);
      data->maxSize = data->maxSize * 1024 * 1024;
   }

   return data;
}

07070100000447000081A40000000000000000000000016822550500003938000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/gio.c   /*********************************************************
 * Copyright (C) 2011-2016,2018-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file gio.c --
 *
 *    Uses gio for setting up the main loop and watching for I/O.
 */


#include <signal.h>

#include "VGAuthLog.h"
#include "serviceInt.h"
#include "service.h"

static GMainLoop *mainLoop = NULL;

#define  VERBOSE_IO_DEBUG  1


static gboolean ServiceEndMainLoop(gpointer userData);


#ifdef _WIN32
typedef struct HandleGSource_ {
   /*
    * Do not put any new member above the gSource member
    * Add future new member after the gSource member
    */
   GSource gSource;
   GPollFD gPollFd;
} HandleGSource;


/**
 * Callback for the "prepare()" event source function. Does nothing.
 *
 * @param[in]  src         Unused.
 * @param[out] timeout     Set to -1.
 *
 * @return FALSE
 */

static gboolean
HandleGSourcePrepare(GSource *src,
                     gint *timeout)
{
   *timeout = -1;
   return FALSE;
}


/**
 * Checks whether the handle has been signaled.
 *
 * @param[in]  _src     The event source.
 *
 * @return Whether the handle is signaled.
 */

static gboolean
HandleGSourceCheck(GSource *_src)
{
   HandleGSource *src = (HandleGSource *) _src;
   return (src->gPollFd.revents & G_IO_IN);
}


/**
 * Calls the callback associated with the handle, if any.
 *
 * @param[in]  src         Unused.
 * @param[in]  callback    The callback to be called.
 * @param[in]  data        User-supplied data.
 *
 * @return The return value of the callback, or FALSE if the callback is NULL.
 */

static gboolean
HandleGSourceDispatch(GSource *src,
                      GSourceFunc callback,
                      gpointer data)
{
   return (callback != NULL) ? callback(data) : FALSE;
}


/**
 * Does nothing. The main glib code already does all the cleanup needed.
 *
 * @param[in]  _src     The event source.
 */

static void
HandleGSourceFinalize(GSource *_src)
{
}


/*
 ******************************************************************************
 * ServiceIONewHandleGSource --                                          */ /**
 *
 * Creates a new glib event source based on a given Windows handle
 *
 * @param[in]   h                 The Windows handle
 * @param[in]   func              The callback function of the event souce
 * @param[in]   data              The callback param of the event source
 *
 * @code
 *
 *    GSource *src = ServiceIONewHandleGSource(h);
 *    g_source_attach(src, glibContext);
 * @return      Pointer to the new glib event source
 *
 ******************************************************************************
 */

static GSource *
ServiceIONewHandleGSource(HANDLE h,
                          GSourceFunc func,
                          gpointer data)
{
   static GSourceFuncs srcFuncs = {
      HandleGSourcePrepare,
      HandleGSourceCheck,
      HandleGSourceDispatch,
      HandleGSourceFinalize,
      NULL,
      NULL
   };

   HandleGSource *ret;

   ASSERT(h != NULL && h != INVALID_HANDLE_VALUE);

   ret = (HandleGSource *) g_source_new(&srcFuncs, sizeof *ret);
   ret->gPollFd.fd = (intptr_t) h;
   ret->gPollFd.events = G_IO_IN;
   ret->gPollFd.revents = 0;

   g_source_add_poll(&ret->gSource, &ret->gPollFd);
   g_source_set_callback(&ret->gSource, func, data, NULL);

   return &ret->gSource;
}
#endif

#ifndef _WIN32

/*
 ******************************************************************************
 * ServiceSighupHandler --                                               */ /**
 *
 * Glib-wrapped signal handler for SIGHUP.
 *
 ******************************************************************************
 */

gboolean
ServiceSighupHandler(gpointer data)
{
   Log("Processing SIGHUP\n");
   Pref_Shutdown(gPrefs);
   gPrefs = Pref_Init(VGAUTH_PREF_CONFIG_FILENAME);
   Service_InitLogging(FALSE, TRUE);
   Service_ReloadPrefs();

   return TRUE;      // don't remove source
}


/*
 ******************************************************************************
 * ServiceSigtermHandler --                                              */ /**
 *
 * Glib-wrapped signal handler for SIGTERM.
 *
 ******************************************************************************
 */

gboolean
ServiceSigtermHandler(gpointer data)
{
   Log("Processing SIGTERM; service exiting\n");
   Pref_Shutdown(gPrefs);
   (void) ServiceEndMainLoop(NULL);
   Service_Shutdown();
   Log("END SERVICE");
   VMXLog_Log(VMXLOG_LEVEL_INFO, "%s END SERVICE",
              VGAUTH_SERVICE_NAME);
   VMXLog_Shutdown();

   /*
    * It's safe to just exit here, since we've been called by the glib
    * mainloop so cannot be in the middle of processing a request.
    */
   exit(0);

   /* NOTREACHED */
   return FALSE;
}


/*
 ******************************************************************************
 * ServiceSetSignalHandlers --                                           */ /**
 *
 * Sets up the signal handlers we care about.
 *
 ******************************************************************************
 */

void
ServiceSetSignalHandlers(void)
{
#define CATCH_SIGNAL(signum, fn)                                        \
   do {                                                                 \
      GSource *src;                                                     \
      src = ServiceNewSignalSource(signum);                             \
      g_source_set_callback(src, fn, NULL, NULL);                       \
      g_source_attach(src, NULL);                                       \
      g_source_unref(src);                                              \
   } while (0)

   /*
    * HUP means re-read prefs.
    */
   CATCH_SIGNAL(SIGHUP, ServiceSighupHandler);

   /*
    * TERM, QUIT, INT all exit cleanly.
    */
   CATCH_SIGNAL(SIGTERM, ServiceSigtermHandler);
   CATCH_SIGNAL(SIGQUIT, ServiceSigtermHandler);
   CATCH_SIGNAL(SIGINT, ServiceSigtermHandler);

#undef CATCH_SIGNAL
}

#endif   // !_WIN32


/*
 ******************************************************************************
 * ServiceIOHandleIO --                                                  */ /**
 * ServiceIOHandleIOGSource --                                           */ /**
 *
 * GIO callback function for IO on a socket.
 *
 * @param[in]   chan              The GIO channel with activity.
 * @param[in]   cond              The type of GIO activity.
 * @param[in]   userData          The userData specified when the callback
 *                                was set up.  The ServiceConnection.
 *
 * @return gboolean
 *
 ******************************************************************************
 */

static gboolean
ServiceIOHandleIOGSource(gpointer userData)
{
   ServiceConnection *conn = (ServiceConnection *) userData;
   VGAuthError err;

   /*
    * read data and try to parse it.  may be a partial.
    */
   err = ServiceProtoReadAndProcessRequest(conn);

   if (err != VGAUTH_E_OK) {
      return FALSE;
   }

   /*
    * Windows needs to initiate a new async read IO before polling again.
    * Do it here instead of immediately after the read() since we like to
    * do copyless buffer optimization later.
    * Read buffer needs to be processed before issuing another async read.
    */
#ifdef _WIN32
   ServiceNetworkStartRead(conn);
#endif

   return TRUE;
}


static gboolean
ServiceIOHandleIO(GIOChannel *chan,
                  GIOCondition cond,
                  gpointer userData)
{
   return ServiceIOHandleIOGSource(userData);
}


/*
 ******************************************************************************
 * ServiceIOAccept --                                                    */ /**
 * ServiceIOAcceptGSource --                                             */ /**
 *
 * GIO callback function for IO on a listening socket.
 * This should be called whenever there is activity on a listen socket.
 * It will accept() the new connection, creating a new ServiceConnection,
 * and start watching for IO on that new connection.
 *
 * @param[in]   chan              The GIO channel with activity.
 * @param[in]   cond              The type of GIO activity.
 * @param[in]   userData          The userData specified when the callback
 *                                was set up.  The ServiceConnection.
 *
 * @return gboolean
 *
 ******************************************************************************
 */


static gboolean
ServiceIOAcceptGSource(gpointer userData)
{
   ServiceConnection *newConn = NULL;
   ServiceConnection *lConn = (ServiceConnection *) userData;
   VGAuthError err;

   err = ServiceConnectionClone(lConn, &newConn);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to clone a new connection\n", __FUNCTION__);
      return FALSE;
   }

   err = ServiceAcceptConnection(lConn, newConn);
   if (VGAUTH_E_OK == err) {
#ifdef _WIN32
      GSource *gSourceData;
#else
      GIOChannel *echan;
#endif

      VGAUTH_LOG_DEBUG("Established a new pipe connection %d on %s", newConn->connId,
                       newConn->pipeName);
#ifdef _WIN32
      gSourceData = ServiceIONewHandleGSource(newConn->ol.hEvent,
                                              ServiceIOHandleIOGSource,
                                              (gpointer) newConn);
      newConn->gioId = g_source_attach(gSourceData, NULL);
      g_source_unref(gSourceData);
#else
      echan = g_io_channel_unix_new(newConn->sock);
      newConn->gioId = g_io_add_watch(echan, G_IO_IN, ServiceIOHandleIO,
                            (gpointer) newConn);
      g_io_channel_unref(echan);
#endif
   } else if (VGAUTH_E_TOO_MANY_CONNECTIONS == err) {
      ServiceConnectionShutdown(newConn);
   } else {
      ServiceConnectionShutdown(lConn);
      ServiceConnectionShutdown(newConn);
   }

   return TRUE;
}

static gboolean
ServiceIOAccept(GIOChannel *chan,
                GIOCondition cond,
                gpointer userData)
{
#if VERBOSE_IO_DEBUG
   Debug("%s: condition: %d\n", __FUNCTION__, cond);
#endif

   return ServiceIOAcceptGSource(userData);

}

/*
 ******************************************************************************
 * ServiceIOStartListen --                                            */ /**
 *
 * Starts listening on a ServiceConnection.
 * Creates a GIO channel to watch for activity
 *
 * @param[in]   conn              The ServiceConnection on which to start
 *                                listening.
 *
 * @return VGAuthError
 *
 ******************************************************************************
 */


VGAuthError
ServiceIOStartListen(ServiceConnection *conn)
{
   VGAuthError err = VGAUTH_E_OK;
#ifdef _WIN32
   GSource *gSource;
#else
   GIOChannel *lChan;
#endif

#ifdef _WIN32
   gSource = ServiceIONewHandleGSource(conn->ol.hEvent, ServiceIOAcceptGSource,
                                       (gpointer) conn);
   conn->gioId = g_source_attach(gSource, NULL);
   g_source_unref(gSource);
#else
   lChan = g_io_channel_unix_new(conn->sock);
   conn->gioId = g_io_add_watch(lChan, G_IO_IN, ServiceIOAccept, (gpointer) conn);
   g_io_channel_unref(lChan);
#endif

   return err;
}


/*
 ******************************************************************************
 * ServiceIOStopIO --                                                  */ /**
 *
 * Removes a GIO callback for a connection.
 *
 * @param[in]   conn           The connection to stop watching for activity.
 *
 * @return VGAuthError
 *
 ******************************************************************************
 */

VGAuthError
ServiceStopIO(ServiceConnection *conn)
{
   /*
    * The connection may not have been added to gpoll
    * This also make it idempotent
    */
   if (conn->gioId > 0) {
      g_source_remove(conn->gioId);
      conn->gioId = 0;
   }

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * ServiceIOPrepareMainLoop --                                         */ /**
 *
 * Prepares the glib main loop.
 *
 * @return VGAuthError
 *
 ******************************************************************************
 */

VGAuthError
ServiceIOPrepareMainLoop(void)
{
   VGAuthError err = VGAUTH_E_OK;

   mainLoop = g_main_loop_new(NULL, FALSE);

   return err;
}


/*
 ******************************************************************************
 * ServiceEndMainLoop --                                                 */ /**
 *
 * Tells the main to loop to exit.
 *
 * @param[in]  userData  The callback data (unused).
 *
 * @return FALSE to remove the source -- should only ever be called once.
 ******************************************************************************
 */

static gboolean
ServiceEndMainLoop(gpointer userData)
{
   Log("%s: about to stop main loop\n", __FUNCTION__);

   g_main_loop_quit(mainLoop);

   return FALSE;
}


#ifdef _WIN32
/*
 ******************************************************************************
 * ServiceIORegisterQuitEvent --                                         */ /**
 *
 * Registers an event that tells the main loop to exit.
 *
 * @param[in]  hQuitEvent The event that will be signaled when it's time to exit.
 *
 * @return VGAuthError
 *
 ******************************************************************************
 */

VGAuthError
ServiceIORegisterQuitEvent(HANDLE hQuitEvent)
{
   GSource *source;

   source = ServiceIONewHandleGSource(hQuitEvent, ServiceEndMainLoop, NULL);
   (void) g_source_attach(source, NULL);

   return VGAUTH_E_OK;
}
#endif


/*
 ******************************************************************************
 * ServiceIOMainLoop --                                            */ /**
 *
 * Runs the glib main loop.
 *
 * @return NEVER
 *
 ******************************************************************************
 */

VGAuthError
ServiceIOMainLoop(void)
{
   g_main_loop_run(mainLoop);

   Log("%s: main loop has exited", __FUNCTION__);

   return VGAUTH_E_OK;
}
07070100000448000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n    07070100000449000081A40000000000000000000000016822550500000B56000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/de.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "Möglicher Sicherheitsangriff!  Der Anfragetyp %1$d verfügt über einen Benutzernamen (%2$s), der nicht mit dem Pipe-Eigentümer (%3$s) übereinstimmt!"

validate.samlBearer.fail = "Die Überprüfung des SAML-Träger-Token ist fehlgeschlagen: %1$d"

validate.samlBearer.success = "Überprüfter SAML-Träger-Token für den Benutzer '%1$s'"

alias.addid = "Dem Aliasspeicher im Eigentum von '%1$s' durch den Benutzer '%2$s' hinzugefügtes Alias"

alias.dir.badperm = "Das Aliasspeicherverzeichnis '%1$s' verfügt über unzulässige Eigentümer oder Berechtigungen. Alle aktuell in '%2$s' gespeicherte Aliase stehen für die Authentifizierung nicht zur Verfügung"

alias.dir.notadir = "Das Aliasspeicherverzeichnis '%1$s' ist vorhanden, ist aber kein Verzeichnis"

alias.dir.renamefail = "Das Umbenennen des verdächtigen Aliasspeicherverzeichnisses '%1$s' in '%2$s' ist fehlgeschlagen"


alias.alias.badfile = "Der Aliasspeicher '%1$s' ist vorhanden, ist aber keine ordnungsgemäße Datei. Die Aliase für den Benutzer '%2$s' stehen für die Authentifizierung nicht zur Verfügung"

alias.alias.badperm = "Der Aliasspeicher '%1$s' verfügt über unzulässige Eigentümer oder Berechtigungen. Die Aliase für den Benutzer '%2$s' stehen für die Authentifizierung nicht zur Verfügung"

alias.alias.rename = "Der verdächtige Aliasspeicher '%1$s' wurde in '%2$s' umbenannt"

alias.alias.renamefail = "Das Umbenennen des verdächtigen Aliasspeichers '%1$s' in '%2$s' ist fehlgeschlagen"

alias.mapfile.badperm = "Die Zuordungsdatei '%1$s' des Aliasspeichers verfügt über unzulässige Eigentümer oder Berechtigungen. Die Aliase in der Zuordnungsdatei stehen für eine Authentifizierung nicht zur Verfügung"

alias.mapping.badfile = "Die Zuordnungsdatei '%1$s' ist vorhanden, ist aber keine ordnungsgemäße Datei. Die Aliase in der Zuordnungsdatei stehen für eine Authentifizierung nicht zur Verfügung"

alias.removeid = "Das Alias wurde aus dem Aliasspeicher im Eigentum von '%1$s' durch den Benutzer '%2$s' entfernt"
  0707010000044A000081A4000000000000000000000001682255050000096F000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/en.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "Possible security attack!  Request type %1$d has a userName (%2$s) which doesn't match the pipe owner (%3$s)!"

validate.samlBearer.fail = "Validation of SAML bearer token failed: %1$d"

validate.samlBearer.success = "Validated SAML bearer token for user '%1$s'"

alias.addid = "Alias added to Alias store owned by '%1$s' by user '%2$s'"

alias.dir.badperm = "Alias store directory '%1$s' has incorrect owner or permissions.  Any Aliases currently stored in '%2$s' will not be available for authentication"

alias.dir.notadir = "Alias store directory '%1$s' exists but is not a directory"

alias.dir.renamefail = "Failed to rename suspect Alias store directory '%1$s' to '%2$s'"


alias.alias.badfile = "Alias store '%1$s' exists but is not a regular file.  The Aliases for user '%2$s' will not be available for authentication"

alias.alias.badperm = "Alias store '%1$s' has incorrect owner or permissions.  The Aliases for user '%2$s' will not be available for authentication"

alias.alias.rename = "Suspect Alias store '%1$s' renamed to '%2$s'"

alias.alias.renamefail = "Failed to rename suspect Alias store '%1$s' to '%2$s'"

alias.mapfile.badperm = "Alias store mapping file '%1$s' has incorrect owner or permissions.  The Aliases in the mapping file will not be available for authentication"

alias.mapping.badfile = "Mapping file '%1$s' exists but is not a regular file.  The Aliases in the mapping file will not be available for authentication"

alias.removeid = "Alias removed from Alias store owned by '%1$s' by user '%2$s'"
 0707010000044B000081A40000000000000000000000016822550500000AD9000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/es.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "Posible ataque de seguridad. El tipo de solicitud %1$d tiene un nombre de usuario (%2$s) que no coincide con el propietario de la canalización (%3$s)."

validate.samlBearer.fail = "Error de validación de token de portador SAML: %1$d"

validate.samlBearer.success = "Token de portador SAML validado para el usuario '%1$s'"

alias.addid = "El usuario '%2$s' ha agregado el alias al almacén de alias propiedad de '%1$s'"

alias.dir.badperm = "El directorio de almacenamiento de alias '%1$s' tiene un propietario o permisos incorrectos. Los alias almacenados actualmente en '%2$s' no estarán disponibles para la autenticación"

alias.dir.notadir = "El directorio de almacenamiento de alias '%1$s' existe, pero no es un directorio"

alias.dir.renamefail = "No se pudo cambiar el nombre del directorio de almacenamiento de alias sospechoso de '%1$s' a '%2$s'"


alias.alias.badfile = "El almacén de alias '%1$s' existe, pero no es un archivo normal. Los alias del usuario '%2$s' no estarán disponibles para la autenticación"

alias.alias.badperm = "El almacén de alias '%1$s' tiene un propietario o permisos incorrectos. Los alias del usuario '%2$s' no estarán disponibles para la autenticación"

alias.alias.rename = "El nombre del almacén de alias sospechoso '%1$s' ha cambiado a '%2$s'"

alias.alias.renamefail = "No pudo cambiarse el nombre del almacén de alias sospechoso de '%1$s' a '%2$s'"

alias.mapfile.badperm = "El archivo de asignación del almacén de alias '%1$s' tiene un propietario o permisos incorrectos. Los alias del archivo de asignación no estarán disponibles para la autenticación"

alias.mapping.badfile = "El archivo de asignación '%1$s' existe, pero no es un archivo normal. Los alias del archivo de asignación no estarán disponibles para la autenticación"

alias.removeid = "El usuario '%2$s' ha quitado el alias del almacén de alias propiedad de '%1$s'"
   0707010000044C000081A40000000000000000000000016822550500000AF0000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/fr.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "Attaque possible au niveau de la sécurité ! Le type de demande %1$d a un nom d'utilisateur (%2$s) ne correspondant pas au propriétaire du canal (%3$s) !"

validate.samlBearer.fail = "Échec de la validation du jeton de support SAML : %1$d"

validate.samlBearer.success = "Jeton de support SAML validé pour l'utilisateur '%1$s'"

alias.addid = "Alias ajouté au magasin d'alias détenu par '%1$s' par l'utilisateur '%2$s'"

alias.dir.badperm = "Le répertoire du magasin d'alias '%1$s' a un détenteur ou des autorisations incorrects. Tous les alias actuellement stockés dans '%2$s' ne seront pas disponibles pour l'authentification"

alias.dir.notadir = "Le répertoire du magasin d'alias '%1$s' existe mais il ne s'agit pas d'un répertoire"

alias.dir.renamefail = "Échec de la modification du nom du répertoire du magasin d'alias suspect '%1$s' en '%2$s'"


alias.alias.badfile = "Le magasin d'alias '%1$s' existe mais il ne s'agit pas d'un fichier normal. Les alias de l'utilisateur '%2$s' ne seront pas disponibles pour l'authentification"

alias.alias.badperm = "Le magasin d'alias '%1$s' a un détenteur ou des autorisations incorrects. Les alias de l'utilisateur '%2$s' ne seront pas disponibles pour l'authentification"

alias.alias.rename = "Magasin d'alias suspect '%1$s' renommé en '%2$s'"

alias.alias.renamefail = "Échec de la modification du nom du magasin d'alias suspect '%1$s' en '%2$s'"

alias.mapfile.badperm = "Le fichier de mappage du magasin d'alias '%1$s' a un détenteur ou des autorisations incorrects. Les alias dans le fichier de mappage ne seront pas disponibles pour l'authentification"

alias.mapping.badfile = "Le fichier de mappage '%1$s' existe mais il ne s'agit pas d'un fichier normal. Les alias dans le fichier de mappage ne seront pas disponibles pour l'authentification"

alias.removeid = "Alias supprimés du magasin d'alias détenu par '%1$s' par l'utilisateur '%2$s'"
0707010000044D000081A40000000000000000000000016822550500000AA7000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/it.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "Possibile attacco alla sicurezza.  Il tipo di richiesta %1$d ha un nome utente (%2$s) che non corrisponde al proprietario del circuito (%3$s)!"

validate.samlBearer.fail = "Convalida del token di connessione SAML non riuscita: %1$d"

validate.samlBearer.success = "Token di connessione SAML convalidato per l'utente  '%1$s'"

alias.addid = "Alias aggiunto all'archivio Alias di proprietà di '%1$s' dall'utente '%2$s'"

alias.dir.badperm = "La directory di archiviazione alias '%1$s' ha proprietario errato o autorizzazioni errate.  Qualsiasi alias ​​attualmente memorizzato in '%2$s' non sarà disponibile per l'autenticazione"

alias.dir.notadir = "La directory dell'archivio di alias '%1$s' esiste ma non è una directory"

alias.dir.renamefail = "Impossibile rinominare la directory dell'archivio di alias sospetta da '%1$s' a '%2$s'"


alias.alias.badfile = "L'archivio alias '%1$s' esiste ma non è un file regolare.  Gli alias per l'utente '%2$s' non saranno disponibili per l'autenticazione"

alias.alias.badperm = "L'archivio di alias '%1$s' ha un proprietario errato o autorizzazioni errate.  Gli alias per l'utente '%2$s' non saranno disponibili per l'autenticazione"

alias.alias.rename = "Archivio di alias sospetti '%1$s' rinominato in '%2$s'"

alias.alias.renamefail = "Impossibile rinominare l'archivio di alias sospetti da '%1$s' a '%2$s'"

alias.mapfile.badperm = "Il file di mappatura dell'archivio di alias '%1$s' ha un proprietario errato o autorizzazioni errate. Gli alias ​​nel file di mappatura non saranno disponibili per l'autenticazione"

alias.mapping.badfile = "Il file di mappatura '%1$s' esiste ma non è un file regolare.  Gli alias ​​nel file di mappatura non saranno disponibili per l'autenticazione"

alias.removeid = "Alias rimosso dall'archivio di alias di proprietà di '%1$s' dall'utente '%2$s'"
 0707010000044E000081A40000000000000000000000016822550500000C44000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/ja.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "セキュリティ アタックである可能性があります! 要求の種類 %1$d には、パイプの所有者 (%3$s) と一致しないユーザー名 (%2$s) が含まれています!"

validate.samlBearer.fail = "SAML 伝送トークンの検証に失敗しました: %1$d"

validate.samlBearer.success = "ユーザー '%1$s' の SAML 伝送トークンが検証されました"

alias.addid = "'%1$s' が所有するエイリアス ストアにユーザー '%2$s' によってエイリアスが追加されました"

alias.dir.badperm = "エイリアス ストア ディレクトリ '%1$s' の所有者または権限が適切ではありません。'%2$s' に現在格納されているエイリアスは認証に使用できません"

alias.dir.notadir = "エイリアス ストア ディレクトリ '%1$s' は存在しますが、ディレクトリではありません"

alias.dir.renamefail = "問題のあるエイリアス ストア ディレクトリの名前 '%1$s' を '%2$s' に変更できませんでした"


alias.alias.badfile = "エイリアス ストア '%1$s' は存在しますが、通常のファイルではありません。ユーザー '%2$s' のエイリアスは認証に使用できません"

alias.alias.badperm = "エイリアス ストア '%1$s' の所有者または権限が適切ではありません。ユーザー '%2$s' のエイリアスは認証に使用できません"

alias.alias.rename = "問題のあるエイリアス ストアの名前 '%1$s' が '%2$s' に変更されました"

alias.alias.renamefail = "問題のあるエイリアス ストアの名前 '%1$s' を '%2$s' に変更できませんでした"

alias.mapfile.badperm = "エイリアス ストア マッピング ファイル '%1$s' の所有者または権限が適切ではありません。マッピング ファイルのエイリアスは認証に使用できません"

alias.mapping.badfile = "マッピング ファイル '%1$s' は存在しますが、通常のファイルではありません。マッピング ファイルのエイリアスは認証に使用できません"

alias.removeid = "エイリアスは、'%1$s' が所有するエイリアス ストアからユーザー '%2$s' によって削除されました"
0707010000044F000081A40000000000000000000000016822550500000B2F000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/ko.vmsg    ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "보안 공격이 발생할 수 있습니다. 요청 유형 %1$d에 파이프 소유자(%3$s)와 일치하지 않는 사용자 이름(%2$s)이 있습니다."

validate.samlBearer.fail = "SAML bearer 토큰의 유효성 검사에 실패했습니다. %1$d"

validate.samlBearer.success = "사용자 '%1$s'에 대한 SAML bearer 토큰이 유효한 것으로 확인되었습니다."

alias.addid = "사용자 '%2$s'이(가) '%1$s'이(가) 소유하는 별칭 저장소에 별칭을 추가했습니다."

alias.dir.badperm = "별칭 저장소 디렉토리 '%1$s'의 소유자나 권한이 올바르지 않습니다. 현재 '%2$s'에 저장되어 있는 별칭을 인증에 사용할 수 없습니다."

alias.dir.notadir = "별칭 저장소 디렉토리 '%1$s'이(가) 있지만 디렉토리가 아닙니다."

alias.dir.renamefail = "의심스러운 별칭 저장소 디렉토리 이름 '%1$s'을(를) '%2$s'(으)로 바꾸지 못했습니다."


alias.alias.badfile = "별칭 저장소 '%1$s'이(가) 있지만 일반 파일이 아닙니다. 사용자 '%2$s'의 별칭을 인증에 사용할 수 없습니다."

alias.alias.badperm = "별칭 저장소 '%1$s'의 소유자나 권한이 올바르지 않습니다. 사용자 '%2$s'의 별칭을 인증에 사용할 수 없습니다."

alias.alias.rename = "의심스러운 별칭 저장소 '%1$s' 이름이 '%2$s'(으)로 바뀌었습니다."

alias.alias.renamefail = "의심스러운 별칭 저장소 이름 '%1$s'을(를) '%2$s'(으)로 바꾸지 못했습니다."

alias.mapfile.badperm = "별칭 저장소 매핑 파일 '%1$s'의 소유자나 권한이 올바르지 않습니다. 매핑 파일의 별칭을 인증에 사용할 수 없습니다."

alias.mapping.badfile = "매핑 파일 '%1$s'이(가) 있지만 일반 파일이 아닙니다. 매핑 파일의 별칭을 인증에 사용할 수 없습니다."

alias.removeid = "사용자 '%2$s'이(가) '%1$s'이(가) 소유하는 별칭 저장소에서 별칭을 제거했습니다."
 07070100000450000081A4000000000000000000000001682255050000096E000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/zh_CN.vmsg ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "可能发生安全攻击! 请求类型 %1$d 具有的 userName (%2$s)与管道所有者(%3$s)不匹配!"

validate.samlBearer.fail = "SAML 持有者令牌验证失败: %1$d"

validate.samlBearer.success = "已验证用户“%1$s”的 SAML 持有者令牌"

alias.addid = "用户“%2$s”已将别名添加到“%1$s”拥有的别名存储中"

alias.dir.badperm = "别名存储目录“%1$s”具有不正确的所有者或权限。无法使用当前在“%2$s”中存储的任何别名进行身份验证"

alias.dir.notadir = "别名存储目录“%1$s”已存在，但不是一个目录"

alias.dir.renamefail = "无法将可疑别名存储目录“%1$s”重命名为“%2$s”"


alias.alias.badfile = "别名存储“%1$s”已存在，但不是一个常规文件。无法使用用户“%2$s”的别名进行身份验证"

alias.alias.badperm = "别名存储“%1$s”具有不正确的所有者或权限。无法使用用户“%2$s”的别名进行身份验证"

alias.alias.rename = "已将可疑别名存储“%1$s”重命名为“%2$s”"

alias.alias.renamefail = "无法将可疑别名存储“%1$s”重命名为“%2$s”"

alias.mapfile.badperm = "别名存储映射文件“%1$s”具有不正确的所有者或权限。无法使用映射文件中的别名进行身份验证"

alias.mapping.badfile = "映射文件“%1$s”已存在，但不是一个常规文件。无法使用映射文件中的别名进行身份验证"

alias.removeid = "用户“%2$s”已从“%1$s”拥有的别名存储中删除别名"
  07070100000451000081A40000000000000000000000016822550500000966000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/l10n/zh_TW.vmsg ##########################################################
# Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation version 2.1 and no later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
##########################################################

proto.attack = "可能存在安全性攻擊! 要求類型 %1$d 具有的使用者名稱 (%2$s) 與管道擁有者 (%3$s) 不相符!"

validate.samlBearer.fail = "驗證 SAML 承載者 Token 失敗: %1$d"

validate.samlBearer.success = "已驗證使用者「%1$s」的 SAML 承載者 Token"

alias.addid = "使用者「%2$s」已將別名新增至「%1$s」所擁有的別名存放區"

alias.dir.badperm = "別名存放區目錄 '%1$s' 的擁有者或權限不正確。目前儲存在 '%2$s' 中的任何別名均無法用於驗證"

alias.dir.notadir = "別名存放區目錄「%1$s」已存在，但並不是目錄"

alias.dir.renamefail = "無法將可疑的別名存放區目錄「%1$s」重新命名為「%2$s」"


alias.alias.badfile = "別名存放區 '%1$s' 已存在，但並不是一般檔案。使用者 '%2$s' 的別名將無法用於驗證"

alias.alias.badperm = "別名存放區 '%1$s' 的擁有者或權限不正確。使用者 '%2$s' 的別名將無法用於驗證"

alias.alias.rename = "已將可疑的別名存放區「%1$s」重新命名為「%2$s」"

alias.alias.renamefail = "無法將可疑的別名存放區「%1$s」重新命名為「%2$s」"

alias.mapfile.badperm = "別名存放區對應檔案 '%1$s' 的擁有者或權限不正確。對應檔案中的別名將無法用於驗證"

alias.mapping.badfile = "對應檔案 '%1$s' 已存在，但並不是一般檔案。對應檔案中的別名將無法用於驗證"

alias.removeid = "使用者「%2$s」已從「%1$s」所擁有的別名存放區中移除別名"
  07070100000452000081A40000000000000000000000016822550500002D8B000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/log.c   /*********************************************************
 * Copyright (C) 2011-2018, 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file log.c
 *
 *    Defines a logging infrastructure based on glib's logging facilities.
 *    Wrap the commonly used logging functions
 *    (Log/Warning/Debug), and provides configurability for where logs should
 *    go to.
 *
 *    To choose the logging domain for your source file, define G_LOG_DOMAIN
 *    before including glib.h.
 *
 *    Based heavily on apps/vmtoolslib/vmtoolsLog.c
 *
 *    This verson is cut down to handle just file-based logging, but I
 *    tried to leave enough structure so that it can be expanded to
 *    support syslog, the event viewer, etc if/when desired.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#ifdef _WIN32
#  include <windows.h>
#else
#  include <unistd.h>
#  include <sys/resource.h>
#  include <sys/time.h>
#endif
#include "service.h"
#include "buildNumber.h"
#ifdef _WIN32
#include "winCoreDump.h"
#endif

/** Tells whether the given log level is a fatal error. */
#define IS_FATAL(level) ((level) & G_LOG_FLAG_FATAL)

#ifdef _WIN32
static gboolean haveDebugConsole = FALSE;
#endif

/*
 * The glib log levels levels we care about.  We use WARNING, MESSAGE
 * and DEBUG (which we drop by default).
 * Add in CRITICAL, INFO, ERROR in case some glib support
 * code generates them.
 */
#define DEFAULT_LOG_LEVELS (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | \
         G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_INFO)

static GLogLevelFlags logWantedLevel = DEFAULT_LOG_LEVELS;
static gboolean enableCoreDump = TRUE;

static gboolean isLogOnStdout = FALSE;


/*
 ******************************************************************************
 * Service_SetLogOnStdout --                                             */ /**
 *
 * Caller tells us whether the log should go the stdout.
 *
 * @param[in] flag     The flag of whether we should log to the stdout
 ******************************************************************************
 */

void
Service_SetLogOnStdout(gboolean flag)
{
   isLogOnStdout = flag;
   gVerboseLogging = TRUE;
}


/*
 ******************************************************************************
 * GetTimeAsString --                                                    */ /**
 *
 * Returns the current time in human-readable format with millisecond
 * precision in UTC.
 *
 * @return The current time as a string.  Must be gfree'd by caller.
 ******************************************************************************
 */

static gchar *
GetTimeAsString(void)
{
   gchar *retStr = NULL;
   GDateTime *utcTime = g_date_time_new_now_utc();

   if (NULL != utcTime) {
      gchar *fmt = g_date_time_format(utcTime, "%FT%T");

      if (NULL != fmt) {
         gint usec = g_date_time_get_microsecond(utcTime)/1000;
         retStr = g_strdup_printf("%s.%03dZ", fmt, usec);
         g_free(fmt);
      }

      g_date_time_unref(utcTime);
   }
   return retStr;
}


/*
 ******************************************************************************
 * ServiceLogFormat --                                                   */ /**
 *
 * Creates a formatted message to be logged. The format of the message will be:
 *    [timestamp] [domain] [level] Log message
 *
 * @param[in] message      User log message.
 * @param[in] domain       Log domain.
 * @param[in] level        Log level.
 *
 * @return Formatted log message.  Should be g_free()'d.
 *
 ******************************************************************************
 */

static gchar *
ServiceLogFormat(const gchar *message,
                 const gchar *domain,
                 GLogLevelFlags level)
{
   char *msg = NULL;
   const char *slevel;
   gchar *tstamp;

   if (domain == NULL) {
      domain = "VGAuthService";
   }

   /*
    * glib 2.16 on Windows has a bug where its printf implementations don't
    * like NULL.
    */
   if (message == NULL) {
      message = "<null>";
   }

   switch (level & G_LOG_LEVEL_MASK) {
   case G_LOG_LEVEL_ERROR:
      slevel = "error";
      break;

   case G_LOG_LEVEL_CRITICAL:
      slevel = "critical";
      break;

   case G_LOG_LEVEL_WARNING:
      slevel = "warning";
      break;

   case G_LOG_LEVEL_MESSAGE:
      slevel = "message";
      break;

   case G_LOG_LEVEL_INFO:
      slevel = "info";
      break;

   case G_LOG_LEVEL_DEBUG:
      slevel = "debug";
      break;

   default:
      slevel = "unknown";
   }

   tstamp = GetTimeAsString();

   msg = g_strdup_printf("[%s] [%8s] [%s] %s\n",
                         (tstamp != NULL) ? tstamp : "no time",
                         slevel, domain, message);

   g_free(tstamp);
   if (NULL != msg) {
      size_t len;

      /*
       * The log messages from glib itself (and probably other libraries based
       * on glib) do not include a trailing new line. Most of our code does. So
       * we detect whether the original message already had a new line, and
       * remove it, to avoid having two newlines when printing our log messages.
       */
      len = strlen(msg);

      if (msg[len - 2] == '\n') {
         msg[len - 1] = '\0';
      }
   }

   return msg;
}


/*
 ******************************************************************************
 * ServiceLogPanic --                                                    */ /**
 *
 * Forces the program to quit, optionally creating a core dump.
 ******************************************************************************
 */

static void
ServiceLogPanic(void)
{
   if (enableCoreDump) {
#if defined(_WIN32)
      Win_MakeCoreDump();
#else
      abort();
#endif
   }
   /* Same behavior as Panic_Panic(). */
   exit(-1);
}


/*
 ******************************************************************************
 * Service_Log --                                                        */ /**
 *
 * Log handler function that does the common processing of log messages,
 * and delegates the actual printing of the message to the file handler.
 *
 * @param[in] domain    Log domain.
 * @param[in] level     Log level.
 * @param[in] message   Message to log.
 * @param[in] data      User-specified data.
 ******************************************************************************
 */

static void
Service_Log(const gchar *domain,
            GLogLevelFlags level,
            const gchar *message,
            gpointer data)
{
   if (level & logWantedLevel) {
      gchar *msg = ServiceLogFormat(message, domain, level);

      ServiceFileLogger_Log(domain, level, msg, data);

#ifdef _WIN32
      /*
       * Since dup()ing stdio in ServiceInitStdioConsole() doesn't want to work,
       * let's just dump it using the Win32 APIs.
       */
      if (haveDebugConsole) {
         DWORD written;
         HANDLE h = GetStdHandle(STD_ERROR_HANDLE);

         (void) WriteFile(h, msg, (DWORD) strlen(msg), &written, NULL);
      }
#endif

      g_free(msg);
   } else {
#ifdef VMX86_DEBUG
      fprintf(stderr, "%s: not logging message: '%s'\n", __FUNCTION__, message);
#endif
   }

   if (IS_FATAL(level)) {
      ServiceLogPanic();
   }
}


/*
 ******************************************************************************
 * Service_InitLogging --                                                */ /**
 *
 * Initializes the logging system according to the configuration in the given
 * dictionary.
 *
 * @param[in] haveConsole        Should be true in Windows if we have a service
 *                               debug window.
 * @param[in] restarting         Should be set if restarting to catch a pref
 *                               change.
 ******************************************************************************
 */

void
Service_InitLogging(gboolean haveConsole,
                    gboolean restarting)
{
   gboolean logEnabled;

   enableCoreDump = Pref_GetBool(gPrefs,
                                 VGAUTH_PREF_ALLOW_CORE,
                                 VGAUTH_PREF_GROUP_NAME_SERVICE, TRUE);

#ifdef _WIN32
   haveDebugConsole = haveConsole;
#endif

   /*
    * If core dumps are enabled (default: TRUE), then set up the exception
    * filter on Win32. On POSIX systems, try to modify the resource limit
    * to allow core dumps, but don't complain if it fails. Core dumps may
    * still fail, e.g., if the current directory is not writable by the
    * user running the process.
    */
   if (!restarting && enableCoreDump) {
#if defined(_WIN32)
      // XXX: Use a config option, perhaps share the directory with the logs
      Win_EnableCoreDump("C:\\TEMP");
#else
      struct rlimit limit = { 0, 0 };

      getrlimit(RLIMIT_CORE, &limit);
      if (limit.rlim_max != 0) {
         limit.rlim_cur = RLIM_INFINITY;

         limit.rlim_cur = MAX(limit.rlim_cur, limit.rlim_max);
         if (setrlimit(RLIMIT_CORE, &limit) == -1) {
            g_message("Failed to set core dump size limit, error %d (%s)\n",
                      errno, g_strerror(errno));
         } else {
            g_message("Core dump limit set to %d", (int) limit.rlim_cur);
         }
      }
#endif
   }

   logEnabled = !isLogOnStdout &&
      Pref_GetBool(gPrefs,
                   VGAUTH_PREF_LOGTOFILE,
                   VGAUTH_PREF_GROUP_NAME_SERVICE, TRUE);

   if (logEnabled) {
      void *data = ServiceFileLogger_Init();
      gchar *loglevel;

      if (NULL != data) {
         (void) g_log_set_default_handler((GLogFunc) Service_Log, data);
      } else {
         fprintf(stderr, "%s: Unable to set up file logger\n", __FUNCTION__);
      }

      loglevel = Pref_GetString(gPrefs,
                                VGAUTH_PREF_NAME_LOGLEVEL,
                                VGAUTH_PREF_GROUP_NAME_SERVICE,
                                SERVICE_LOGLEVEL_NORMAL);

      if (g_ascii_strcasecmp(loglevel, SERVICE_LOGLEVEL_NORMAL) == 0) {
         logWantedLevel = DEFAULT_LOG_LEVELS;
      } else if ((g_ascii_strcasecmp(loglevel, SERVICE_LOGLEVEL_VERBOSE) == 0) ||
                 (g_ascii_strcasecmp(loglevel, SERVICE_LOGLEVEL_DEBUG) == 0)) {
         logWantedLevel = DEFAULT_LOG_LEVELS | G_LOG_LEVEL_DEBUG;
         gVerboseLogging = TRUE;
      } else {
         logWantedLevel = DEFAULT_LOG_LEVELS;
#ifdef VMX86_DEBUG
         // add DEBUG for obj builds
         logWantedLevel |= G_LOG_LEVEL_DEBUG;
         gVerboseLogging = TRUE;
#endif
         Warning("%s: Unrecognized loglevel '%s'\n", __FUNCTION__, loglevel);
      }
      Log("VGAuthService%s '%s' logging at level '%s'\n",
          restarting ? " resetting" : "",
          BUILD_NUMBER,
          loglevel ? loglevel : "<default>");
      g_free(loglevel);

      /*
       * Once logging is set, dump all prefs so we know all the settings.
       * (This also works around the chicken-and-egg issue where any
       * noise from Pref_Init() is lost).
       */
      Pref_LogAllEntries(gPrefs);

   }
}
 07070100000453000081A40000000000000000000000016822550500004C1E000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/main.c  /*********************************************************
 * Copyright (c) 2011-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file main.c --
 *
 *    Entry point for the GuestAuth service.
 */

#include <locale.h>
#include "serviceInt.h"
#include "service.h"
#include "buildNumber.h"
#ifdef _WIN32
#include <tchar.h>
#include "winCoreDump.h"
#endif

#ifdef _WIN32

/*
 * Turn this on to build as a Windows service.  Note that this
 * affects the cmdline arg parsing, and the default no-argument case
 * runs as a Window service.
 * This means to be run in a non-service mode, you must pass a "-d' flag.
 *
 * Most of the service code was stolen from bora/apps/vmauthd/authdService.c
 */

#define SUPPORT_WIN_SERVICE 1

/*
 * XXX
 *
 * Note that to install the service, it should be executed with its
 * complete path name, since the (un)register code just uses argv[0]
 * as the path to the executable.  A relative path will just confuse
 * the service control manager.
 */


#if SUPPORT_WIN_SERVICE

/*
 * Details about the Windows service
 */
#define VGAUTH_DISPLAY_NAME "VMware Alias Manager and Ticket Service"
#define VGAUTH_DESCRIPTION "Alias Manager and Ticket Service"

static SERVICE_STATUS VGAuthServiceStatus = { 0 };
static SERVICE_STATUS_HANDLE VGAuthServiceStatusHandle = 0;
static HANDLE hServiceThread = NULL;
static HANDLE hServiceQuitEvent = NULL;
#endif

#endif   // _WIN32

#ifndef _WIN32
#define USE_POSIX_SERVICE 1

#include <sys/types.h>
#include <unistd.h>

static gboolean isRunningAsService = FALSE;
static char *pidFileName = "/var/run/vmware/vgauthsvclog_pid.txt";

#endif


/*
 ******************************************************************************
 * ServiceHelp --                                                        */ /**
 *
 * Dump some simple help.
 *
 ******************************************************************************
 */

static void
ServiceHelp(char *arg)
{
   printf("Usage: %s [OPTION]\n", arg);
   printf("Service to support SAML token and ticketing authentication"
         " for VMware products.\n\n");
#ifdef _WIN32
   printf("\t-r\tRegister as a Windows Service.\n");
   printf("\t-u\tUnregister as a Windows Service.\n");
   printf("\t-d\tRun as a normal program, sending logging to stdio.\n");
   printf("\t-s\tRun as a normal program, sending logging to a file.\n");
#else
#if USE_POSIX_SERVICE
   printf("\t-k\tShut down the running instance that was started as a daemon.\n");
   printf("\t-s\tRun in daemon mode.\n");
   printf("\t-b\tRun in background mode, using a pid lock file.\n");
#endif
#endif
   printf("\t-h\tDisplay this help and exit.\n");
}


/*
 ******************************************************************************
 * ServiceStartAndRun --                                               */ /**
 *
 * Does the work to start up and run the service.  Never returns.
 *
 ******************************************************************************
 */

static void
ServiceStartAndRun(void)
{
   VGAuthError err;
   ServiceConnection *publicConn;

   gboolean auditSuccess = Pref_GetBool(gPrefs,
                                        VGAUTH_PREF_AUDIT_SUCCESS,
                                        VGAUTH_PREF_GROUP_NAME_AUDIT,
                                        TRUE);
   gchar *msgCatalog = Pref_GetString(gPrefs,
                                      VGAUTH_PREF_LOCALIZATION_DIR,
                                      VGAUTH_PREF_GROUP_NAME_LOCALIZATION,
                                      VGAUTH_PREF_DEFAULT_LOCALIZATION_CATALOG);

   setlocale(LC_ALL, "");
   I18n_BindTextDomain(VMW_TEXT_DOMAIN, NULL, msgCatalog);
   g_free(msgCatalog);

   Audit_Init(VGAUTH_SERVICE_NAME, auditSuccess);

   Log("INIT SERVICE\n");

   VMXLog_Init();
   VMXLog_Log(VMXLOG_LEVEL_INFO, "%s %s starting up",
              VGAUTH_SERVICE_NAME, BUILD_NUMBER);

#ifdef _WIN32
   if (ServiceOldInstanceExists()) {
      Warning("%s: another instance is running; exiting\n", __FUNCTION__);
      exit(-1);
   }
#endif

   err = ServiceAliasInitAliasStore();
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to init alias store; exiting\n", __FUNCTION__);
      exit(-1);
   }

   err = ServiceInitTickets();
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to init tickets; exiting\n", __FUNCTION__);
      exit(-1);
   }

   err = ServiceInitVerify();
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to init verification; exiting\n", __FUNCTION__);
      exit(-1);
   }

   err = ServiceRegisterIOFunctions(ServiceIOStartListen, ServiceStopIO);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to register IO functions; exiting\n", __FUNCTION__);
      exit(-1);
   }


   err = ServiceCreatePublicConnection(&publicConn);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to create public listen sock; exiting\n", __FUNCTION__);
      exit(-1);
   }

   err = ServiceIOStartListen(publicConn);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to listen on public sock; exiting\n", __FUNCTION__);
      exit(-1);
   }

   err = ServiceIOPrepareMainLoop();
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to set up main loop; exiting\n", __FUNCTION__);
      exit(-1);
   }

   /*
    * This never comes back
    */
   Log("BEGIN SERVICE\n");
   err = ServiceIOMainLoop();
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to run main loop; exiting\n", __FUNCTION__);
      exit(-1);
   }

   // NOTREACHED
   return;
}


#if SUPPORT_WIN_SERVICE

/*
 ******************************************************************************
 * ServiceDoRegisterService --                                           */ /**
 *
 * (Un)registers as a Windows service.
 * Expects the path to be absolute, with the full command name.
 *
 * @param[in]   path       The path to the executable.
 * @param[in]   doRegister Set if the service is being registered.
 *
 ******************************************************************************
 */

static void
ServiceDoRegisterService(gchar *path,
                         gboolean doRegister)
{
   gboolean bRet;
   gchar *errString;

   /*
    * XXX may want to add code to turn a non-absolute path into one,
    * and tack on ".exe" if needed.  Since in theory this code
    * will only be run by an installer which knows the full path,
    * leaving it out for now.
    */

   bRet = ServiceRegisterService(doRegister,
                                 VGAUTH_SERVICE_NAME,
                                 VGAUTH_DISPLAY_NAME,
                                 VGAUTH_DESCRIPTION,
                                 path,
                                 &errString);
   if (!bRet) {
      fprintf(stderr, "%s: %s\n", path, errString);
   } else {
      if (doRegister) {
         printf("Successfully registered %s.\n", VGAUTH_DISPLAY_NAME);
      } else {
         printf("Successfully unregistered %s.\n", VGAUTH_DISPLAY_NAME);
      }
   }

   g_free(errString);
}


/*
 ******************************************************************************
 * ServiceCtrlHandler --                                                 */ /**
 *
 * @param[in]   Opcode   The opcode from the service control manager.
 *
 * Handler for Windows service control messages.
 ******************************************************************************
 */

VOID WINAPI
ServiceCtrlHandler(DWORD opCode)
{
   DWORD status;

   switch(opCode) {
   case SERVICE_CONTROL_PAUSE:
      /*
       * Do whatever it takes to pause here.
       *
       * XXX we don't actually pause any operations...
       */
      VGAuthServiceStatus.dwCurrentState = SERVICE_PAUSED;
      Log("Service Paused.\n");
      break;

   case SERVICE_CONTROL_CONTINUE:
      // Do whatever it takes to continue here.
      VGAuthServiceStatus.dwCurrentState = SERVICE_RUNNING;
      Log("Service Continuing.\n");
      break;

   case SERVICE_CONTROL_STOP:
      // Do whatever it takes to stop here.
      VGAuthServiceStatus.dwWin32ExitCode = 0;
      VGAuthServiceStatus.dwCurrentState  = SERVICE_STOP_PENDING;
      VGAuthServiceStatus.dwCheckPoint    = 0;
      VGAuthServiceStatus.dwWaitHint      = 0;

      if (!SetServiceStatus(VGAuthServiceStatusHandle, &VGAuthServiceStatus)) {
         status = GetLastError();
         VGAUTH_LOG_ERR_WIN("SetServiceStatus failed while stopping\n");
         return;
      }

      if (hServiceThread != NULL) {
         SetEvent(hServiceQuitEvent);
         if (WaitForSingleObject(hServiceThread, 15000) != WAIT_OBJECT_0) {
            Log("Forced to clobber service thread\n");
            TerminateThread(hServiceThread, 0);
         }
         CloseHandle(hServiceThread);
         hServiceThread = NULL;
         CloseHandle(hServiceQuitEvent);
         hServiceQuitEvent = NULL;
      }

      VGAuthServiceStatus.dwCurrentState = SERVICE_STOPPED;
      Log("Service Stopped.\n");
      break;

   case SERVICE_CONTROL_INTERROGATE:
      Log("Service being interrogated....\n");
      break;

   default:
      Warning("Unknown service opcode %d\n", opCode);
      break;
   }

   // Send current status.
   if (!SetServiceStatus(VGAuthServiceStatusHandle, &VGAuthServiceStatus)) {
      VGAUTH_LOG_ERR_WIN("SetServiceStatus failed.\n");
   }

   return;
}


/*
 ******************************************************************************
 * ServiceStartServiceThread --                                          */ /**
 *
 * Starts a thread to do the real work.
 *
 * @return TRUE on success
 ******************************************************************************
 */

static gboolean
ServiceStartServiceThread(void)
{
   VGAuthError err;

   if (!(hServiceQuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL))) {
      VGAUTH_LOG_ERR_WIN("Failed to create shutdown event");
      return FALSE;
   }

   hServiceThread = CreateThread(NULL, 0,
                                 (LPTHREAD_START_ROUTINE) ServiceStartAndRun,
                                 NULL, 0, NULL);

   if (NULL == hServiceThread) {
      VGAUTH_LOG_ERR_WIN("Failed to start service thread\n");
      return FALSE;
   }

   err = ServiceIORegisterQuitEvent(hServiceQuitEvent);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to register quit event\n");
      return FALSE;
   }

   return TRUE;
}


/*
 ******************************************************************************
 * ServiceServiceStart --                                                */ /**
 *
 * Service entry point.
 *
 * @param[in]   argc       Number of args (unused).
 * @param[in]   argv       Arguments (unused).
 *
 ******************************************************************************
 */

VOID WINAPI
ServiceServiceStart(DWORD argc,
                    LPTSTR *argv)
{
   BOOL bRet;
   DWORD status;

   VGAuthServiceStatus.dwServiceType        = SERVICE_WIN32;
   VGAuthServiceStatus.dwCurrentState       = SERVICE_START_PENDING;
   VGAuthServiceStatus.dwControlsAccepted   = SERVICE_ACCEPT_STOP;

   VGAuthServiceStatusHandle = RegisterServiceCtrlHandler(_T(VGAUTH_SERVICE_NAME),
                                                           ServiceCtrlHandler);

   if (0 == VGAuthServiceStatusHandle) {
      Warning("%s: RegisterServiceCtrlHandler failed %d\n",
              __FUNCTION__, GetLastError());
      return;
   }

   /*
    * Initialization code goes here.
    */

   bRet = ServiceStartServiceThread();

   /*
    * Handle any error
    */
   if (!bRet) {
      VGAuthServiceStatus.dwCurrentState       = SERVICE_STOPPED;
      VGAuthServiceStatus.dwCheckPoint         = 0;
      VGAuthServiceStatus.dwWaitHint           = 0;
      VGAuthServiceStatus.dwWin32ExitCode      = ERROR_SERVICE_SPECIFIC_ERROR;
      VGAuthServiceStatus.dwServiceSpecificExitCode = -1;

      if (!SetServiceStatus(VGAuthServiceStatusHandle,
                            &VGAuthServiceStatus)) {
         Warning("%s: SetServiceStatus error on failure %ld\n",
                 __FUNCTION__, GetLastError());
      }
      return;
   }

   /*
    * Initialization complete - report running status.
    */
   VGAuthServiceStatus.dwCurrentState       = SERVICE_RUNNING;
   VGAuthServiceStatus.dwCheckPoint         = 0;
   VGAuthServiceStatus.dwWaitHint           = 0;

   if (!SetServiceStatus(VGAuthServiceStatusHandle, &VGAuthServiceStatus)) {
      status = GetLastError();
      Warning("%s: SetServiceStatus error %ld\n", __FUNCTION__, status);
   }

#if 0
   /*
    * Fun race here -- if we spew too soon, the debug system isn't properly
    * set up and the Log thinks its recursed and blows up.
    * A Sleep() avoids this, but since we don't really need this noise,
    * turn it off.  Leave this warning in case someone wants to turn it back on.
    */
   Log("Service Started.\n");
#endif

   return;
}


/*
 ******************************************************************************
 * ServiceRunAsService --                                                */ /**
 *
 * Starts as a Windows service.
 *
 ******************************************************************************
 */

void
ServiceRunAsService(void)
{
   gboolean haveDebugConsole = FALSE;
   SERVICE_TABLE_ENTRY DispatchTable[] = {
      { _T(VGAUTH_SERVICE_NAME), ServiceServiceStart},
      { NULL,                     NULL              }
   };

#ifdef VMX86_DEBUG
   /*
    * In devel builds, create a console so warnings are visible.  NB the
    * console won't be visible unless you also check "allow service to
    * interact with desktop" in the Services manager.  I tried hard, and
    * failed, to get the console to show up on the interactive winsta/desk
    * after we move ourselves there -- we're able to create GUI windows
    * fine -- I can only conclude that Windows is broken in this regard.
    * Google for "SetProcessWindowStation AllocConsole" and read the
    * Apache source, or see me, for an amusing story about this.  -- mginzton
    */

   haveDebugConsole = ServiceInitStdioConsole();
#endif

   /*
    * XXX: We should be able to get everything automatically redirected
    * to the new console. Try skip the InitLogging code... TBD
    * When we're in service mode, make sure the debug isn't lost.
    */
   Service_InitLogging(haveDebugConsole, FALSE);

   if (!StartServiceCtrlDispatcher(DispatchTable)) {
      Warning("%s: StartServiceCtrlDispatcher error = %d\n",
              __FUNCTION__, GetLastError());
   }
}
#endif   // SUPPORT_WIN_SERVICE


/*
 ******************************************************************************
 * main --                                                              */ /**
 *
 * The one, the only: main(). Runs the GuestAuth service.
 *
 * @param[in]  argc        Number of command line arguments.
 * @param[in]  argv        The command line arguments.
 *
 * @return 0 if the service ran successfully, -1 if there was an error during
 *         start-up or execution.
 *
 ******************************************************************************
 */

int
main(int argc,
     char *argv[])
{
#ifdef _WIN32
   WinUtil_EnableSafePathSearching();
#if defined(VMX86_RELEASE)
   WinUtil_VerifyExePathW();
#endif
#endif

   gPrefs = Pref_Init(VGAUTH_PREF_CONFIG_FILENAME);

   /*
    * Determine where the service is running from, so resources can
    * be found relative to it.
    */
   if (!g_path_is_absolute(argv[0])) {
      gchar *abs = g_find_program_in_path(argv[0]);
      if (abs == NULL || g_strcmp0(abs, argv[0]) == 0) {
         gchar *cwd = g_get_current_dir();
         g_free(abs);
         abs = g_build_filename(cwd, argv[0], NULL);
         g_free(cwd);
      }
      gInstallDir = g_path_get_dirname(abs);
      g_free(abs);
   } else {
      gInstallDir = g_path_get_dirname(argv[0]);
   }

#ifdef _WIN32
#if SUPPORT_WIN_SERVICE

   /*
    * This is the path for the service control manager.
    */
   if (argc == 1) {
      ServiceRunAsService();
      return 0;
   } else if (argc == 2) {
      // register
      if (g_strcmp0(argv[1], "-r") == 0) {
         ServiceDoRegisterService(argv[0], TRUE);
         return 0;
      // unregister
      } else if (g_strcmp0(argv[1], "-u") == 0) {
         ServiceDoRegisterService(argv[0], FALSE);
         return 0;
      // run as a cmdline app for debugging
      } else if (g_strcmp0(argv[1], "-d") == 0) {
         Service_SetLogOnStdout(TRUE);
         Service_InitLogging(FALSE, FALSE);
         ServiceStartAndRun();
         return 0;
       // run on cmdline, using log file
      } else if (g_strcmp0(argv[1], "-s") == 0) {
         Service_InitLogging(FALSE, FALSE);
         ServiceStartAndRun();
         return 0;
      } else if (g_strcmp0(argv[1], "-h") == 0) {
         ServiceHelp(argv[0]);
         return 0;
      }
   }

#else
   Service_SetLogOnStdout(TRUE);
   Service_InitLogging(FALSE, FALSE);
   ServiceStartAndRun();
#endif
#else // !_WIN32

#if USE_POSIX_SERVICE
   /*
    * Parse arguments.
    *
    * "-b" tells it to run as a daemon.
    * "-s" tells it to run in service mode (logging to a file).
    * "-k" tells it to force itself to quit.
    *
    * When running as a daemon, we restart, except with -b changed
    * to -s so we properly log to a file.
    *
    * This code assumes the only arguments supported are "-b" and "-k".
    * The replacement of "-b" before calling ServiceDamonize()
    * will need work if that changes.
    */

   if (argc > 1) {
      if (g_strcmp0(argv[1], "-k") == 0) {            // self cancel mode
         if (!ServiceSuicide(pidFileName)) {
            exit(-1);
         } else {
            exit(0);
         }
      } else if (g_strcmp0(argv[1], "-s") == 0) {     // service mode
         isRunningAsService = TRUE;
         Service_InitLogging(FALSE, FALSE);
      } else if (g_strcmp0(argv[1], "-b") == 0) {     // background mode
         Service_InitLogging(FALSE, FALSE);
         /*
          * We have to remove this flag to prevent an infinite loop.
          */
         argv[1] = g_strdup("-s");
         if (!ServiceDaemonize(argv[0],
                               argv,
                               SERVICE_DAEMONIZE_LOCKPID,
                               pidFileName)) {
            Warning("%s: failed to daemonize\n", __FUNCTION__);
            return -1;
         }
         // NOTREACHED
         return 0;
      } else if (g_strcmp0(argv[1], "-h") == 0) {     // help
         ServiceHelp(argv[0]);
         return 0;
      } else {
         Warning("%s: unrecognized args\n", __FUNCTION__);
      }
   } else {
      /* The foreground mode */
      Service_SetLogOnStdout(TRUE);
      Service_InitLogging(FALSE, FALSE);
   }
#endif   // USE_POSIX_SERVICE
   ServiceSetSignalHandlers();
   ServiceStartAndRun();

#endif   // !_WIN32

   return 0;
}
  07070100000454000081A40000000000000000000000016822550500000BEA000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/service.h   /*********************************************************
 * Copyright (C) 2011-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file service.h --
 *
 *    Stand-alone service functions and data types.
 */

#ifndef _SERVICE_H_
#define _SERVICE_H_

#ifdef _WIN32
#include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <glib.h>
#include <glib/gstdio.h>

#include "VGAuthError.h"
#include "VGAuthLog.h"
#include "serviceInt.h"
#include "audit.h"
#include "vmxlog.h"


#define VGAUTH_SERVICE_NAME "VGAuthService"

VGAuthError ServiceIOStartListen(ServiceConnection *conn);

VGAuthError ServiceIOPrepareMainLoop(void);

VGAuthError ServiceIOMainLoop(void);

VGAuthError ServiceStopIO(ServiceConnection *conn);

#ifdef _WIN32
VGAuthError ServiceIORegisterQuitEvent(HANDLE hQuitEvent);

gboolean ServiceRegisterService(gboolean bRegister,
                                const gchar *name,
                                const gchar *displayName,
                                const gchar *description,
                                const gchar *binaryPath,
                                gchar **errString);

gboolean ServiceInitStdioConsole(void);
#endif

#ifndef _WIN32
typedef enum ServiceDaemonizeFlags {
   SERVICE_DAEMONIZE_DEFAULT = 0,
   SERVICE_DAEMONIZE_LOCKPID = (1 << 0),
} ServiceDaemonizeFlags;

gboolean ServiceDaemonize(const char *path,
                          char * const *args,
                          ServiceDaemonizeFlags flags,
                          const char *pidPath);

GSource *ServiceNewSignalSource(int signum);

void ServiceSetSignalHandlers(void);

gboolean ServiceSuicide(const char *pidPath);
#endif

#ifdef _WIN32
#define LOGFILENAME_DEFAULT "vgauthsvclog.txt"
#define LOGFILENAME_PATH_DEFAULT "c:\\temp\\" LOGFILENAME_DEFAULT
#else
#define LOGFILENAME_PATH_DEFAULT "/var/log/vmware-vgauthsvc.log"
#endif

void Service_SetLogOnStdout(gboolean flag);
void Service_InitLogging(gboolean haveDebugConsole, gboolean restart);
void *ServiceFileLogger_Init(void);
gboolean ServiceFileLogger_Log(const gchar *domain,
                               GLogLevelFlags level,
                               const gchar *message,
                               gpointer data);

#endif   // _SERVICE_H_
  07070100000455000081A40000000000000000000000016822550500003F50000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/servicePosix.c  /*********************************************************
 * Copyright (C) 2011-2017, 2019-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>

#if defined(sun)
#include <sys/systeminfo.h>
#endif

#if defined(__FreeBSD__) || defined(__APPLE__)
# include <sys/sysctl.h>
#endif

/*
 * XXX we can probably remove a lot of these; carried over from hostinfoPosix.c
 * til we're building on Mac and can check.
 */
#if defined(__APPLE__)
#include <assert.h>
#include <CoreServices/CoreServices.h>
#include <mach-o/dyld.h>
#include <mach/host_info.h>
#include <mach/mach_host.h>
#include <mach/mach_init.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <sys/mman.h>
#elif defined(__FreeBSD__)
#if !defined(RLIMIT_AS)
#  if defined(RLIMIT_VMEM)
#     define RLIMIT_AS RLIMIT_VMEM
#  else
#     define RLIMIT_AS RLIMIT_RSS
#  endif
#endif
#endif

#if defined(__APPLE__) || defined(__FreeBSD__)
#include <paths.h>
#endif

#include "serviceInt.h"
#include "service.h"
#include "VGAuthBasicDefs.h"


#if !defined(_PATH_DEVNULL)
#define _PATH_DEVNULL "/dev/null"
#endif


/*
 ******************************************************************************
 * ServiceResetProcessState --                                           */ /**
 *
 * Based on bora/lib/misc/hostinfoPosix.c:Hostinfo_ResetProcessState()
 * Clean up signal handlers and file descriptors before an exec().
 *
 * @param[in]     keepFds    Array of fds to leave open.
 * @param[in]     numKeepFds Number of fds to leave open.
 *
 ******************************************************************************
 */

static void
ServiceResetProcessState(int *keepFds,
                         size_t numKeepFds)
{
   int s, fd;
   struct sigaction sa;
   struct rlimit rlim;

   /*
    * Disable itimers before resetting the signal handlers.
    * Otherwise, the process may still receive timer signals:
    * SIGALRM, SIGVTARLM, or SIGPROF.
    */

   struct itimerval it;
   it.it_value.tv_sec = it.it_value.tv_usec = 0;
   it.it_interval.tv_sec = it.it_interval.tv_usec = 0;
   setitimer(ITIMER_REAL, &it, NULL);
   setitimer(ITIMER_VIRTUAL, &it, NULL);
   setitimer(ITIMER_PROF, &it, NULL);

   for (s = 1; s <= NSIG; s++) {
      sa.sa_handler = SIG_DFL;
      sigfillset(&sa.sa_mask);
      sa.sa_flags = SA_RESTART;
      sigaction(s, &sa, NULL);
   }

   for (fd = (int) sysconf(_SC_OPEN_MAX) - 1; fd > STDERR_FILENO; fd--) {
      size_t i;

      for (i = 0; i < numKeepFds; i++) {
         if (fd == keepFds[i]) {
            break;
         }
      }
      if (i == numKeepFds) {
         (void) close(fd);
      }
   }
   if (getrlimit(RLIMIT_AS, &rlim) == 0) {
      rlim.rlim_cur = rlim.rlim_max;
      setrlimit(RLIMIT_AS, &rlim);
   }
}


/*
 ******************************************************************************
 * ServiceSuicide --                                                     */ /**
 *
 * @brief Service self cancel
 *
 * Reads the pid from pidPath and forces the process ID'ed there to quit.
 * Useful for shutdown scripts.
 *
 * @param[in]   pidPath     NUL-terminated UTF-8 path to read PID.
 *
 * @return FALSE if the process could not be canceled.
 ******************************************************************************
 */

gboolean
ServiceSuicide(const char *pidPath)
{
   char pidBuf[32];
   int pid;
   int ret;
   int errCode;
   gboolean bRet = FALSE;
   FILE *pidPathFp = g_fopen(pidPath, "r");

   if (NULL == pidPathFp) {
      Warning("%s: failed to open pid file '%s', error %u\n",
              __FUNCTION__, pidPath, errno);
      return FALSE;
   }

   if (fgets(pidBuf, sizeof pidBuf, pidPathFp)) {
      pid = atoi(pidBuf);
      if (pid <= 0) {
         Warning("%s bad pid %d read; skipping\n", __FUNCTION__,
                 pid);
         goto done;
      }
      Debug("%s: sending SIGTERM to service at pid %d\n", __FUNCTION__, pid);
      ret = kill(pid, SIGTERM);
      errCode = errno;
      if (0 != ret) {
         if (ESRCH == errCode) {
            Debug("%s: pid %d not found, returning success\n",
                  __FUNCTION__, pid);
            bRet = TRUE;
         } else {
            Warning("%s: kill(%d) failed, error %u\n",
                    __FUNCTION__, pid, errCode);
         }
      } else {
         bRet = TRUE;
      }
   }

done:
   if (pidPathFp) {
      fclose(pidPathFp);
   }
   return bRet;
}



/*
 ******************************************************************************
 * ServiceDaemonize --                                                   */ /**
 *
 * @brief Cross-platform daemon(3)-like wrapper.
 *
 * Stripped down from bora/lib/misc/hostinfoPosix.c:Hostinfo_Daemonize()
 *
 * The current process is restarted with the given arguments.
 * The process state is reset (see Hostinfo_ResetProcessState).
 * A new session is created (so the process has no controlling terminal).
 *
 * Restarts the current process as a daemon, given the path to the
 * process.  This means:
 *
 * * You're detached from your parent.  (Your parent doesn't
 *   need to wait for you to exit.)
 * * Your process no longer has a controlling terminal or
 *   process group.
 * * Your stdin/stdout/stderr fds are redirected to /dev/null. All
 *   other descriptors, except for the ones that are passed in the
 *   parameter keepFds, are closed.
 * * Your signal handlers are reset to SIG_DFL in the daemonized
 *   process, and all the signals are unblocked.
 * * Your main() function is called with the specified NULL-terminated
 *   argument list.
 *
 * (Don't forget that the first string in args is argv[0] -- the
 * name of the process).
 *
 *
 * All stdio file descriptors of the daemon process are redirected to /dev/null.
 *
 * If pidPath is non-NULL, then upon success, writes the PID
 * (as a US-ASCII string followed by a newline) of the daemon
 * process to that path.
 *
 * If 'flags' contains SERVICE_DAEMONIZE_LOCKPID and pidPath is
 * non-NULL, then an exclusive flock(2) is taken on pidPath to prevent
 * multiple instances of the service from running.
 *
 * @param[in]   path        NUL-terminated UTF-8 path to exec
 * @param[in]   args        NUL-terminated UTF-8 argv list
 * @param[in]   flags       Any flags.
 * @param[in]   pidPath     NUL-terminated UTF-8 path to write PID.
 *
 *
 * @return FALSE if the process could not be daemonized.  errno contains
 *      the error on failure.
 *      Doesn't return if the process was daemonized.
 *
 ******************************************************************************
 */

gboolean
ServiceDaemonize(const char *path,
                 char * const *args,
                 ServiceDaemonizeFlags flags,
                 const char *pidPath)
{
   /*
    * We use the double-fork method to make a background process whose
    * parent is init instead of the original process.
    *
    * We do this instead of calling daemon(), because daemon() is
    * deprecated on Mac OS 10.5 hosts, and calling it causes a compiler
    * warning.
    *
    * We must exec() after forking, because Mac OS library frameworks
    * depend on internal Mach ports, which are not correctly propagated
    * across fork calls.  exec'ing reinitializes the frameworks, which
    * causes them to reopen their Mach ports.
    */

   int pidPathFd = -1;
   int childPid;
   int pipeFds[2] = { -1, -1 };
   int err = EINVAL;
   sigset_t sig;
   int fd;
   int saveFds[2];
   int numSavedFds = 0;

   ASSERT_ON_COMPILE(sizeof (errno) <= sizeof err);
   ASSERT(args);
   ASSERT(path);

   if (pidPath) {
      if (!ServiceNetworkCreateSocketDir()) {
         return FALSE;
      }
      pidPathFd = g_open(pidPath, O_WRONLY | O_CREAT, 0644);
      if (pidPathFd == -1) {
         err = errno;
         Warning("%s: Couldn't open PID path [%s], error %u.\n",
                 __FUNCTION__, pidPath, err);
         errno = err;
         return FALSE;
      }

      /*
       * Lock this file to take a mutex on daemonizing this process. The child
       * will keep this file descriptor open for as long as it is running.
       *
       * flock(2) is a BSD extension (also supported on Linux) which creates a
       * lock that is inherited by the child after fork(2). fcntl(2) locks do
       * not have this property. Solaris only supports fcntl(2) locks.
       */
#ifndef sun
      if ((flags & SERVICE_DAEMONIZE_LOCKPID) &&
          flock(pidPathFd, LOCK_EX | LOCK_NB) == -1) {
         err = errno;
         Warning("%s: Lock held on PID path [%s], error %u, not daemonizing.\n",
                 __FUNCTION__, pidPath, err);
         errno = err;
         close(pidPathFd);
         return FALSE;
      }
#endif
      saveFds[numSavedFds++] = pidPathFd;
   }

   if (pipe(pipeFds) == -1) {
      err = errno;
      Warning("%s: Couldn't create pipe, error %u.\n", __FUNCTION__, err);
      pipeFds[0] = pipeFds[1] = -1;
      goto cleanup;
   }

   saveFds[numSavedFds++] = pipeFds[1];

   if (fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC) == -1) {
      err = errno;
      Warning("%s: Couldn't set close-on-exec for fd %d, error %u.\n",
              __FUNCTION__, pipeFds[1], err);
      goto cleanup;
   }

   childPid = fork();

   switch (childPid) {
   case -1:
      err = errno;
      Warning("%s: Couldn't fork first child, error %u.\n", __FUNCTION__,
              err);
      goto cleanup;
   case 0:
      /* We're the first child.  Continue on. */
      break;
   default:
      {
         /* We're the original process.  Check if the first child exited. */
         int status;

         close(pipeFds[1]);
         waitpid(childPid, &status, 0);
         if (WIFEXITED(status) && WEXITSTATUS(status) != EXIT_SUCCESS) {
            Warning("%s: Child %d exited with error %d.\n",
                    __FUNCTION__, childPid, WEXITSTATUS(status));
            goto cleanup;
         } else if (WIFSIGNALED(status)) {
            Warning("%s: Child %d exited with signal %d.\n",
                    __FUNCTION__, childPid, WTERMSIG(status));
            goto cleanup;
         }

         /*
          * Check if the second child exec'ed successfully.  If it had
          * an error, it will write an int errno to this pipe before
          * exiting.  Otherwise, its end of the pipe will be closed on
          * exec and this call will fail as expected.
          * The assumption is that we don't get a partial read. In case,
          * it did happen, we can detect it by the number of bytes read.
          */

         while (TRUE) {
            int res = read(pipeFds[0], &err, sizeof err);

            if (res > 0) {
               Warning("%s: Child could not exec %s, read %d, error %u.\n",
                       __FUNCTION__, path, res, err);
               goto cleanup;
            } else if ((res == -1) && (errno == EINTR)) {
               continue;
            }
            break;
         }

         err = 0;
         goto cleanup;
      }
   }
   /*
    * Reset the signal mask to unblock all
    * signals. fork() clears pending signals.
    */

   ServiceResetProcessState(saveFds, numSavedFds);
   sigfillset(&sig);
   sigprocmask(SIG_UNBLOCK, &sig, NULL);

   if (setsid() == -1) {
      Warning("%s: Couldn't create new session, error %d.\n",
              __FUNCTION__, errno);

      _exit(EXIT_FAILURE);
   }

   switch (fork()) {
   case -1:
      {
         Warning("%s: Couldn't fork, error %d.\n",
                 __FUNCTION__, errno);

         return FALSE;
      }
   case 0:
      // We're the second child.  Continue on.
      break;
   default:
      /*
       * We're the first child.  We don't need to exist any more.
       *
       * Exiting here causes the child to be reparented to the
       * init process, so the original process doesn't need to wait
       * for the child we forked off.
       */

      _exit(EXIT_SUCCESS);
   }

   if (chdir("/") == -1) {
      int err = errno;

      Warning("%s: Couldn't chdir to /, error %u.\n", __FUNCTION__, err);

      _exit(EXIT_FAILURE);
   }

   (void) umask(0);

   fd = open(_PATH_DEVNULL, O_RDONLY);
   if (fd != -1) {
      dup2(fd, STDIN_FILENO);
      close(fd);
   }

   fd = open(_PATH_DEVNULL, O_WRONLY);
   if (fd != -1) {
      dup2(fd, STDOUT_FILENO);
      dup2(fd, STDERR_FILENO);
      close(fd);
   }

   if (pidPath) {
      int64_t pid;
      char pidString[32];
      int pidStringLen;

      ASSERT_ON_COMPILE(sizeof (pid_t) <= sizeof pid);
      ASSERT(pidPathFd >= 0);

      pid = getpid();
      pidStringLen = g_snprintf(pidString, sizeof pidString,
                                "%"FMT64"d\n", pid);
      if (pidStringLen <= 0) {
         err = EINVAL;

         if (write(pipeFds[1], &err, sizeof err) == -1) {
            Warning("%s: Couldn't write to parent pipe: %u, original "
                    "error: %u.\n", __FUNCTION__, errno, err);
         }
         _exit(EXIT_FAILURE);
      }

      if (ftruncate(pidPathFd, 0) == -1) {
         err = errno;
         Warning("%s: Couldn't truncate path [%s], error %d.\n",
                 __FUNCTION__, pidPath, err);

         if (write(pipeFds[1], &err, sizeof err) == -1) {
            Warning("%s: Couldn't write to parent pipe: %u, original "
                    "error: %u.\n", __FUNCTION__, errno, err);
         }
         _exit(EXIT_FAILURE);
      }

      if (write(pidPathFd, pidString, pidStringLen) != pidStringLen) {
         err = errno;
         Warning("%s: Couldn't write PID to path [%s], error %d.\n",
                 __FUNCTION__, pidPath, err);

         if (write(pipeFds[1], &err, sizeof err) == -1) {
            Warning("%s: Couldn't write to parent pipe: %u, original "
                    "error: %u.\n", __FUNCTION__, errno, err);
         }
         _exit(EXIT_FAILURE);
      }

      if (fsync(pidPathFd) == -1) {
         err = errno;
         Warning("%s: Couldn't flush PID to path [%s], error %d.\n",
                 __FUNCTION__, pidPath, err);

         if (write(pipeFds[1], &err, sizeof err) == -1) {
            Warning("%s: Couldn't write to parent pipe: %u, original "
                    "error: %u.\n", __FUNCTION__, errno, err);
         }
         _exit(EXIT_FAILURE);
      }
   }

   /*
    * XXX
    * The original code translated the path and argv into the default
    * locale -- we may need to do that again.
    */
   if (execv(path, args) == -1) {
      err = errno;
      Warning("%s: Couldn't exec %s, error %d.\n", __FUNCTION__, path, err);

      /* Let the original process know we failed to exec. */
      if (write(pipeFds[1], &err, sizeof err) == -1) {
         Warning("%s: Couldn't write to parent pipe: %u, "
                 "original error: %u.\n", __FUNCTION__, errno, err);
      }
      _exit(EXIT_FAILURE);
   }

   // NOT_REACHED

  cleanup:
   if (pipeFds[0] != -1) {
      close(pipeFds[0]);
   }
   if (pipeFds[1] != -1) {
      close(pipeFds[1]);
   }

   if (err != 0 && pidPath) {
      /*
       * Unlink pidPath on error before closing pidPathFd to avoid racing
       * with another process attempting to daemonize and unlinking the
       * file it created instead.
       */
      g_unlink(pidPath);
      errno = err;
   }

   if (pidPath) {
      close(pidPathFd);
   }
   return err == 0;
}

07070100000456000081A400000000000000000000000168225505000029E3000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/signalSource.c  /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file signalSource.c
 *
 *    A GSource implementation that is activated by OS signals.
 *
 *    Caveat: if the process is receiving a lot of signals in a short period
 *    of time, it's possible that the sources will not be notified for all
 *    the instances of a particular signal. So this mechanism shouldn't be
 *    used for reliable event delivery.
 *
 *    Stolen from apps/lib/vmtoolslib
 */


#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

#include "service.h"

typedef enum {
   SIG_SRC_UNHANDLED = 0,
   SIG_SRC_IDLE,
   SIG_SRC_SIGNALED
} SignalState;

/* Use NSIG if it's defined, otherwise use a hardcoded limit. */
#if defined(NSIG)
#  define MAX_SIGNALS   NSIG
#else
#  define MAX_SIGNALS   64
#endif

typedef struct SignalHandler {
   gboolean                initialized;
   int                     wakeupPipe[2];
   struct sigaction        handler;
   GPollFD                 wakeupFd;
   SignalState             signals[MAX_SIGNALS];
   siginfo_t               currSignal;
} SignalHandler;

static SignalHandler sigHandler = { FALSE, };
G_LOCK_DEFINE_STATIC(gLock);

typedef struct SignalSource {
   GSource     src;
   int         signum;
} SignalSource;

/** Type of callback used by the signal event source. */
typedef gboolean (*SignalSourceCb)(const siginfo_t *, gpointer);


/*
 ******************************************************************************
 * SignalSourceReadSigInfo --                                            */ /**
 *
 * Reads one siginfo_t struct from the pipe if data is available, and
 * place it in the global state variable. This allows us to, eventually,
 * service all the processed signals, although in a not very efficient
 * way...
 ******************************************************************************
 */

static inline void
SignalSourceReadSigInfo(void)
{
   if (sigHandler.wakeupFd.revents & G_IO_IN) {
      siginfo_t info;
      ssize_t nbytes = read(sigHandler.wakeupFd.fd, &info, sizeof info);
      if (nbytes == -1) {
         g_warning("Signal source: reading from wake up fd failed.");
         return;
      } else {
         /* XXX: Maybe we should handle this in some other way? */
         ASSERT(nbytes == sizeof info);
         ASSERT(info.si_signo < MAX_SIGNALS);
      }
      memcpy(&sigHandler.currSignal, &info, sizeof info);
      // make coverity happy
      if (info.si_signo >= 0 && info.si_signo < MAX_SIGNALS) {
         sigHandler.signals[info.si_signo] = SIG_SRC_SIGNALED;
      }
      sigHandler.wakeupFd.revents = 0;
   }
}


/*
 ******************************************************************************
 * SignalSourceSigHandler --                                             */ /**
 *
 * Handles a signal. Writes the signal information to the wakeup pipe.
 *
 * According to signal(7), "write()" is safe to call from a signal handling
 * context. If the write fails, though, signal delivery might be delayed.
 *
 * @param[in]  signum      Signal received.
 * @param[in]  info        Information about the signal.
 * @param[in]  context     Unused.
 ******************************************************************************
 */

static void
SignalSourceSigHandler(int signum,
                       siginfo_t *info,
                       void *context)
{
   ssize_t bytes;
   siginfo_t dummy;
   if (signum >= MAX_SIGNALS) {
      return;
   }

   if (info == NULL) {
      /*
       * Solaris seems to call the handler with a NULL info struct in certain
       * situations. I noticed it when hitting CTRL-C (sending SIGINT) in a
       * terminal; calling "kill [pid]" (sending SIGTERM) seemed to work fine.
       * Still, it's better to handle this case.
       */
      memset(&dummy, 0, sizeof dummy);
      dummy.si_signo = signum;
      info = &dummy;
   }
   bytes = write(sigHandler.wakeupPipe[1], info, sizeof *info);

   if (bytes == -1) {
      if (errno == EAGAIN) {
         /*
          * Pipe is full. If this ever becomes a problem, more pipes will
          * have to be created...
          */
         g_warning("Too many signals queued, this shouldn't happen.\n");
         ASSERT(FALSE);
      } else {
         g_warning("Could not queue signal %d (error %d: %s)\n",
                   signum, errno, strerror(errno));
      }
   }
}


/*
 ******************************************************************************
 * SignalSourcePrepare --                                                */ /**
 *
 * Does nothing.
 *
 * @param[in]  _src        Unused.
 * @param[out] timeout     Set to -1.
 *
 * @return FALSE
 ******************************************************************************
 */

static gboolean
SignalSourcePrepare(GSource *_src,
                    gint *timeout)
{
   *timeout = -1;
   return FALSE;
}


/*
 ******************************************************************************
 * SignalSourceCheck --                                                  */ /**
 *
 * Checks whether the process received the signal the source is watching.
 *
 * @param[in]  _src     The event source.
 *
 * @return Whether the source's signal was received.
 ******************************************************************************
 */

static gboolean
SignalSourceCheck(GSource *_src)
{
   SignalSource *src = (SignalSource *) _src;
   SignalSourceReadSigInfo();
   return (sigHandler.signals[src->signum] == SIG_SRC_SIGNALED);
}


/*
 ******************************************************************************
 * SignalSourceDispatch --                                               */ /**
 *
 * Calls the callback associated with the handle, if any. Resets the source's
 * signal state to "not signaled".
 *
 * @param[in]  _src        The event source.
 * @param[in]  _callback   The callback to be called.
 * @param[in]  data        User-supplied data.
 *
 * @return The return value of the callback, or FALSE if the callback is NULL.
 ******************************************************************************
 */

static gboolean
SignalSourceDispatch(GSource *_src,
                     GSourceFunc _callback,
                     gpointer data)
{
   SignalSourceCb callback = (SignalSourceCb) _callback;
   SignalSource *src = (SignalSource *) _src;
   sigHandler.signals[src->signum] = SIG_SRC_IDLE;
   return (callback != NULL) ? callback(&sigHandler.currSignal, data)
                             : FALSE;
}


/*
 ******************************************************************************
 * SignalSourceFinalize --                                               */ /**
 *
 * Destroys the event source. Nothing needs to be done.
 *
 * @param[in]  src      The event source.
 ******************************************************************************
 */

static void
SignalSourceFinalize(GSource *src)
{
}


/*
 ******************************************************************************
 * ServiceNewSignalSource --                                             */ /**
 *
 * Creates a new source for the given signal.
 *
 * Rather than processing the events in the signal handling context, the main
 * loop is woken up and callbacks are processed in the main loop's thread.
 *
 * The same "wakeup" file descriptors are used for all sources, so if sources
 * are added to different main loop instances, all of them will be woken up
 * if any signal for which handlers are registered occurs.
 *
 * This code assumes that the rest of the app is not setting signal
 * handlers directly, at least for signals for which glib sources have
 * been set up.
 *
 * Also note that on older Linux systems (pre-NPTL), some real-time signals
 * are used by the pthread library and shouldn't be used by applications.
 *
 * Example of setting a handler for a signal:
 *
 * @code
 *
 *    GSource *src = VMTools_NewSignalSource(signum);
 *    g_source_set_callback(src, MyCallback, myData, NULL);
 *    g_source_attach(src, myContext);
 *
 * @endcode
 *
 * @note This API is not available on Win32.
 *
 * @param[in]  signum   Signal to watch.
 *
 * @return Pointer to the new source, NULL if failed to set signal handler.
 ******************************************************************************
 */

GSource *
ServiceNewSignalSource(int signum)
{
   static GSourceFuncs srcFuncs = {
      SignalSourcePrepare,
      SignalSourceCheck,
      SignalSourceDispatch,
      SignalSourceFinalize,
      NULL,
      NULL
   };
   SignalSource *ret;

   ASSERT(signum < MAX_SIGNALS);
   ASSERT(signum != SIGKILL && signum != SIGSTOP);

   G_LOCK(gLock);
   if (!sigHandler.initialized) {
      memset(&sigHandler, 0, sizeof sigHandler);
      if (pipe(sigHandler.wakeupPipe) == -1 ||
          fcntl(sigHandler.wakeupPipe[0], F_SETFL, O_RDONLY | O_NONBLOCK) < 0 ||
          fcntl(sigHandler.wakeupPipe[1], F_SETFL, O_WRONLY | O_NONBLOCK) < 0) {
         ASSERT(FALSE);
      }
      sigHandler.wakeupFd.fd = sigHandler.wakeupPipe[0];
      sigHandler.wakeupFd.events = G_IO_IN | G_IO_ERR;
      sigHandler.handler.sa_sigaction = SignalSourceSigHandler;
      sigHandler.handler.sa_flags = SA_SIGINFO;
      sigHandler.initialized = TRUE;
   }
   G_UNLOCK(gLock);

   /*
    * Sets the signal handler if it hasn't been set yet. The code is
    * racy, but it's OK if 2 threads are trying to install the same
    * signal handler (one will win and all will be fine).
    */
   if (sigHandler.signals[signum] == SIG_SRC_UNHANDLED) {
      if (sigaction(signum, &sigHandler.handler, NULL) == -1) {
         g_warning("Cannot set signal handler: %s\n", strerror(errno));
         return NULL;
      }
      sigHandler.signals[signum] = SIG_SRC_IDLE;
   }

   ret = (SignalSource *) g_source_new(&srcFuncs, sizeof *ret);
   ret->signum = signum;

   g_source_add_poll(&ret->src, &sigHandler.wakeupFd);
   return &ret->src;
}
 07070100000457000081A4000000000000000000000001682255050000005C000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/service/vgauth.conf [service]
samlSchemaDir = @@VGAUTHSCHEMADIR@@

[localization]
msgCatalog = @@VGAUTHMSGDIR@@
07070100000458000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl 07070100000459000081A4000000000000000000000001682255050001A81A000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/alias.c /*********************************************************
 * Copyright (c) 2011-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file alias.c --
 *
 *    Functions to support the Alias store.
 */

#include <errno.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _WIN32
#include <io.h>
#include <windows.h>
#include <Shlobj.h>
#include "VGAuthLog.h"
#else
#include <unistd.h>
#endif

#include "serviceInt.h"
#include "certverify.h"
#include "VGAuthProto.h"
#include "vmxlog.h"
#include "VGAuthUtil.h"

// puts the identity store in an easy to find place
#undef WIN_TEST_MODE

/*
 * Note that we can't have trailing DIRSEP on these paths, or the
 * file security checks in ServiceAliasInitAliasStore() will fail.
 * A path of "/foo/bar/" is Not the same as "/foo/bar", at least
 * to (l)stat() and g_file_test()
 */
#ifdef _WIN32
#ifdef WIN_TEST_MODE
#define DEFAULT_ALIASSTORE_ROOT_DIR   "C:\\aliasStore"
#else
#define ALIAS_STORE_REL_DIRECTORY   "VMware\\VGAuth\\aliasStore"
#define DEFAULT_ALIASSTORE_ROOT_DIR   "C:\\Documents and Settings\\All Users\\Application Data\\" ALIAS_STORE_REL_DIRECTORY
#endif
#else
#define DEFAULT_ALIASSTORE_ROOT_DIR   "/var/lib/vmware/VGAuth/aliasStore"
#endif

#define ALIASSTORE_MAPFILE_NAME  "mapping.xml"
#define ALIASSTORE_FILE_PREFIX   "user-"
#define ALIASSTORE_FILE_SUFFIX   ".xml"

static gboolean allowSymlinks = FALSE;
static gchar *aliasStoreRootDir = DEFAULT_ALIASSTORE_ROOT_DIR;

#ifdef _WIN32
/*
 * Still used to create the Alias directory; passed through to
 * g_mkdir_with_parents()
 */
#define ALIASSTORE_DIR_PERMS     (0)
#else
#define ALIASSTORE_FILE_PERMS    (S_IRUSR | S_IWUSR) // 0600
#define ALIASSTORE_MAPFILE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) // 0644
#define ALIASSTORE_DIR_PERMS     (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) // 0755
#endif

/*
 * Maximum size of an alias or mapping file in bytes, to prevent DoS attacks.
 */
#define ALIASSTORE_FILE_MAX_SIZE       (10 * 1024 * 1024)

/*
 * Alias store XML details.
 */

/*
per-user file layout:

<userAliases>
 <alias>
   <pemCert> </pemCert>
   <aliasInfos>
      <aliasInfo>
         <subject>foo\bar</subject>
         <comment> this is a comment </comment>
      </aliasInfo>
      <aliasInfo>
         <subject>baz</subject>
         <comment> this is another comment </comment>
      </aliasInfo>
      <aliasInfo>
         <anySubject/>
         <comment> this is a comment for anyUser</comment>
      </aliasInfo>
   </aliasInfos>
 </alias>
</userAliases>
 */

#define ALIASSTORE_XML_PREAMBLE VGAUTH_XML_PREAMBLE

#define ALIASINFO_USER_ALIASES_ELEMENT_NAME "userAliases"

#define ALIASINFO_ALIAS_ELEMENT_NAME "alias"
#define ALIASINFO_PEMCERT_ELEMENT_NAME "pemCertificate"
#define ALIASINFO_ALIASINFOS_ELEMENT_NAME "aliasInfos"
#define ALIASINFO_ALIASINFO_ELEMENT_NAME "aliasInfo"
#define ALIASINFO_SUBJECT_ELEMENT_NAME "subject"
#define ALIASINFO_ANYSUBJECT_ELEMENT_NAME "anySubject"
#define ALIASINFO_COMMENT_ELEMENT_NAME "comment"

static char *AliasStoreXMLUserAliasesStart =
   "<"ALIASINFO_USER_ALIASES_ELEMENT_NAME">\n";

static char *AliasStoreXMLUserAliasesEnd =
   "</"ALIASINFO_USER_ALIASES_ELEMENT_NAME">\n";

static char *AliasStoreXMLFormatAliasStart =
   "<"ALIASINFO_ALIAS_ELEMENT_NAME">\n"
   "   <"ALIASINFO_PEMCERT_ELEMENT_NAME">%s</"ALIASINFO_PEMCERT_ELEMENT_NAME">\n"
   "   <"ALIASINFO_ALIASINFOS_ELEMENT_NAME">\n";

static char *AliasStoreXMLFormatNamedAliasInfo =
   "      <"ALIASINFO_ALIASINFO_ELEMENT_NAME">\n"
   "         <"ALIASINFO_SUBJECT_ELEMENT_NAME">%s</"ALIASINFO_SUBJECT_ELEMENT_NAME">\n"
   "         <"ALIASINFO_COMMENT_ELEMENT_NAME">%s</"ALIASINFO_COMMENT_ELEMENT_NAME">\n"
   "      </"ALIASINFO_ALIASINFO_ELEMENT_NAME">\n";

static char *AliasStoreXMLFormatAnyAliasInfo =
   "      <"ALIASINFO_ALIASINFO_ELEMENT_NAME">\n"
   "         <"ALIASINFO_ANYSUBJECT_ELEMENT_NAME"/>\n"
   "         <"ALIASINFO_COMMENT_ELEMENT_NAME">%s</"ALIASINFO_COMMENT_ELEMENT_NAME">\n"
   "      </"ALIASINFO_ALIASINFO_ELEMENT_NAME">\n";

static char *AliasStoreXMLFormatAliasEnd =
   "   </"ALIASINFO_ALIASINFOS_ELEMENT_NAME">\n"
   "</"ALIASINFO_ALIAS_ELEMENT_NAME">\n";

/*
 * Possible parse states for an alias file.
 */
typedef enum AliasParseState {
   ALIAS_PARSE_STATE_NONE,            // init
   ALIAS_PARSE_STATE_ALIASES,         // working on the list of aliases
   ALIAS_PARSE_STATE_ALIAS,           // working on an alias
   ALIAS_PARSE_STATE_PEMCERT,         // working on a PEM cert
   ALIAS_PARSE_STATE_ALIASINFOS,      // working on a list of aliasInfos
   ALIAS_PARSE_STATE_ALIASINFO,       // working on a aliasInfo
   ALIAS_PARSE_STATE_SUBJECT,         // working on a named subject
   ALIAS_PARSE_STATE_ANYSUBJECT,      // working on an anySubject
   ALIAS_PARSE_STATE_COMMENT,         // working on a comment
} AliasParseState;

/*
 * Used when parsing an alias store.
 */
typedef struct _AliasParseList {
   AliasParseState state;
   int num;
   ServiceAlias *aList;
} AliasParseList;



/*
 * Mapping file XML details.
 */
#define MAP_MAPPINGS_ELEMENT_NAME "mappings"

#define MAP_MAPPING_ELEMENT_NAME "mapping"
#define MAP_PEMCERT_ELEMENT_NAME "pemCertificate"
#define MAP_SUBJECTS_ELEMENT_NAME "subjects"
#define MAP_SUBJECT_ELEMENT_NAME "subject"
#define MAP_ANYSUBJECT_ELEMENT_NAME "anySubject"
#define MAP_USERNAME_ELEMENT_NAME "userName"

static char *MapfileXMLStart =
   "<"MAP_MAPPINGS_ELEMENT_NAME">\n";

static char *MapfileXMLEnd =
   "</"MAP_MAPPINGS_ELEMENT_NAME">\n";

static char *MapfileXMLMappingStart =
   "  <"MAP_MAPPING_ELEMENT_NAME">\n"
   "    <"MAP_PEMCERT_ELEMENT_NAME">%s</"MAP_PEMCERT_ELEMENT_NAME">\n"
   "    <"MAP_USERNAME_ELEMENT_NAME">%s</"MAP_USERNAME_ELEMENT_NAME">\n"
   "    <"MAP_SUBJECTS_ELEMENT_NAME">\n";

static char *MapfileXMLMappingEnd =
   "    </"MAP_SUBJECTS_ELEMENT_NAME">\n"
   "  </"MAP_MAPPING_ELEMENT_NAME">\n";

static char *MapfileXMLNamedSubject =
   "      <"MAP_SUBJECT_ELEMENT_NAME">%s</"MAP_SUBJECT_ELEMENT_NAME">\n";

static char *MapfileXMLAnySubject =
   "      <"MAP_ANYSUBJECT_ELEMENT_NAME"/>\n";

/*
 * Possible parse states for a mapping file.
 */
typedef enum MappedParseState {
   MAP_PARSE_STATE_NONE,           // init
   MAP_PARSE_STATE_MAPPINGS,       // working on the list of mappings
   MAP_PARSE_STATE_MAPPING,        // working on a mapping
   MAP_PARSE_STATE_PEMCERT,        // working on a PEM cert
   MAP_PARSE_STATE_SUBJECTS,       // working on a list of subjects
   MAP_PARSE_STATE_SUBJECT,        // working on a named subject
   MAP_PARSE_STATE_ANYSUBJECT,     // working on an anySubject
   MAP_PARSE_STATE_USERNAME,       // working on a userName
} MappedParseState;


/*
 * Used when parsing a mapping file.
 */
typedef struct _MappedAliasParseList {
   MappedParseState state;
   int num;
   ServiceMappedAlias *maList;
} MappedAliasParseList;

/*
mapping file layout:

<mappings>
 <mapping>
   <pemCert> </pemCert>
   <user>guestusername</user>
   <subjects>
         <subject>baz</subject>
         <anySubject/>
   </subjects>
  </mapping>
</mappings>

 */

#ifdef _WIN32
#define ISPATHSEP(c)  ((c) == '\\' || (c) == '/')
#else
#define ISPATHSEP(c)  ((c) == '/')
#endif


/*
 ******************************************************************************
 * ServiceAliasIsSubjectEqual --                                         */ /**
 *
 * Compares two Subjects.
 *
 * @param[in]   t1   Type of the first subject.
 * @param[in]   t2   Type of the second subject.
 * @param[in]   n1   Name of the first subject.
 * @param[in]   n2   Name of the second subject.
 *
 * @return   TRUE if they match.
 *
 ******************************************************************************
 */

gboolean
ServiceAliasIsSubjectEqual(ServiceSubjectType t1,
                           ServiceSubjectType t2,
                           const gchar *n1,
                           const gchar *n2)
{
   gboolean bRet = TRUE;

   if (t1 != t2) {
      return FALSE;
   }

   /*
    * Do a case insenstive comparison.  glib has a pure ascii strcmp,
    * but this data could be non-ascii, so instead use the utf8 function
    * to make it case insensitive and then do a compare.
    */
   if (t1 == SUBJECT_TYPE_NAMED) {
      gchar *n1case;
      gchar *n2case;

      n1case = g_utf8_casefold(n1, -1);
      n2case = g_utf8_casefold(n2, -1);
      bRet = (g_strcmp0(n1case, n2case) == 0) ? TRUE : FALSE;
      g_free(n1case);
      g_free(n2case);
   }

   return bRet;
}


/*
 ******************************************************************************
 * ServiceComparePEMCerts --                                             */ /**
 *
 * Compares two PEM certificates, returns TRUE if they are the same.
 * This does a lot more than just a strcmp() because we may have
 * extraneous whitespace or other junk as in an openssl PEM
 * format which has -----BEGIN CERTIFICATE----- and
 * -----END CERTIFICATE-----.
 *
 * @param[in]   pemCert1      The first cert.
 * @param[in]   pemCert2      The second cert.
 *
 * @return TRUE if the certs are the same.
 *
 ******************************************************************************
 */

gboolean
ServiceComparePEMCerts(const gchar *pemCert1,
                       const gchar *pemCert2)
{
   gchar *cleanCert1;
   gchar *cleanCert2;
   guchar *binCert1;
   guchar *binCert2;
   gsize len1;
   gsize len2;
   gboolean bRet;

   /*
    * First pull off any openssl headers.  The base64 decoder
    * will ignore anything non-base64 (like whitespace) in the
    * text, but it'll treat the 'BEGIN/END' text in the delimiter
    * as real data.
    */
   cleanCert1 = CertVerify_StripPEMCert(pemCert1);
   cleanCert2 = CertVerify_StripPEMCert(pemCert2);

   binCert1 = g_base64_decode(cleanCert1, &len1);
   binCert2 = g_base64_decode(cleanCert2, &len2);

   if (len1 != len2) {
      bRet = FALSE;
      goto done;
   }

   bRet = memcmp(binCert1, binCert2, len1) == 0;

done:
   g_free(cleanCert1);
   g_free(cleanCert2);
   g_free(binCert1);
   g_free(binCert2);

   return bRet;
}


/*
 ******************************************************************************
 * ServiceUserNameToAliasStoreFileName --                                */ /**
 *
 * Map a user name into its VGAuth alias file name,
 * escaping the backslash on Windows.
 *
 * @param[in]   userName       The user name
 *
 * @return   the result ascii string
 *
 ******************************************************************************
 */

static gchar *
ServiceUserNameToAliasStoreFileName(const gchar *userName)
{
   gchar *escapedName = ServiceEncodeUserName(userName);
   gchar *aliasFileName = g_strdup_printf("%s"DIRSEP"%s%s%s",
                                          aliasStoreRootDir,
                                          ALIASSTORE_FILE_PREFIX,
                                          escapedName,
                                          ALIASSTORE_FILE_SUFFIX);

   g_free(escapedName);
   return aliasFileName;
}


/*
 ******************************************************************************
 * ServiceUserNameToTmpAliasStoreFileName --                             */ /**
 *
 * Map a user name into its VGAuth tmp alias file name,
 * escaping the backslash on Windows.
 *
 * @param[in]   userName       The user name
 *
 * @return   the result ascii string
 *
 ******************************************************************************
 */

static gchar *
ServiceUserNameToTmpAliasStoreFileName(const gchar *userName)
{
   gchar *escapedName = ServiceEncodeUserName(userName);
   gchar *tmpAliasFileName = g_strdup_printf("%s"DIRSEP"%sXXXXXX",
                                             aliasStoreRootDir,
                                             escapedName);
   g_free(escapedName);
   return tmpAliasFileName;
}


/*
 ******************************************************************************
 * ServiceLoadAliasFileContents --                                       */ /**
 *
 * Securely loads an alias or mapping file, preventing TOCTOU (Time of Check,
 * Time of Use) bugs.  This also provides a size confidence check, preventing
 * a DoS attack.
 *
 * Steps:
 * 1 - test file for existence and type
 * 2 - size check
 * 3 - verfify ownership and permissions
 * 4 - open file
 * 5 - re-fetch attributes via handle
 * 6 - recheck permissions (by handle or statbuf)
 * 7 - verfiy nothing important changed between the checks
 * 8 - read the file contents
 *
 * Note that it looks like steps 1-3 might be skippable, and we could just
 * check the file after it is opened.  But that would allow symlinks
 * through.  A few comparisons might be optimized out, but those
 * are cheap compared to the syscalls.
 *
 * @param[in]   fileName       name of the file to be read.
 * @param[in]   userName       The user name of the expected owner.
 *                             Can be NULL for map file.
 * @param[out]  contents       The file contents.  Must be g_free()d.
 * @param[out]  fileSize       The file size.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

#ifdef _WIN32
static VGAuthError
ServiceLoadFileContentsWin(const gchar *fileName,
                           const gchar *userName,
                           gchar **contents,
                           gsize *fileSize)
{
   DWORD toRead;
   VGAuthError err = VGAUTH_E_FAIL;
   gchar *buf;
   gchar *bp;
   HANDLE hFile = NULL;
   WIN32_FILE_ATTRIBUTE_DATA fileAttrs;
   BY_HANDLE_FILE_INFORMATION fileInfoHandle;
   gunichar2 *fileNameW = NULL;
   BOOL ok;
   DWORD bytesRead;
   gchar *realPath = NULL;

   *fileSize = 0;
   *contents = NULL;

   CHK_UTF8_TO_UTF16(fileNameW, fileName, goto done);

   /*
    * Get baseline size and type data.
    */
   if (!GetFileAttributesExW(fileNameW, GetFileExInfoStandard,
                             &fileAttrs)) {
      VGAUTH_LOG_ERR_WIN("failed to get attributes of %s; failing read\n",
              fileName);
      err = VGAUTH_E_FAIL;
      goto done;
   }

   /*
    * For windows, be sure its not a directory, device, or is
    * a reparse point (symlink).
    */
#define FILE_ATTRS_NORMAL(a) (!((a) & (FILE_ATTRIBUTE_DEVICE | \
                                       FILE_ATTRIBUTE_DIRECTORY | \
                                       FILE_ATTRIBUTE_REPARSE_POINT)))

   // verify file type
   if (!FILE_ATTRS_NORMAL(fileAttrs.dwFileAttributes)) {
      if (userName == NULL) {
         Audit_Event(FALSE,
                     SU_(alias.mapping.badfile,
                         "Mapping file '%s' exists but is not a regular file.  "
                         "The Aliases in the mapping file will not be available for "
                         "authentication"),
                     fileName);
         Warning("%s: mapping file %s exists but isn't a regular file (%d), "
                 "punting\n",
                 __FUNCTION__, fileName, fileAttrs.dwFileAttributes);
      } else {
         Audit_Event(FALSE,
                     SU_(alias.alias.badfile,
                         "Alias store '%s' exists but is not a regular file.  "
                         "The Aliases for user '%s' will not be available for "
                         "authentication"),
                     fileName, userName);
         Warning("%s: alias file %s exists but isn't a regular file (%d), "
                 "punting\n",
                 __FUNCTION__, fileName, fileAttrs.dwFileAttributes);
      }
      err = VGAUTH_E_FAIL;
      goto done;
   }

   // confidence check size
   if ((fileAttrs.nFileSizeHigh != 0) ||
       (fileAttrs.nFileSizeLow > ALIASSTORE_FILE_MAX_SIZE)) {
      Warning("%s: size of %s too large %d %d; failing read\n", __FUNCTION__,
              fileName, fileAttrs.nFileSizeHigh, fileAttrs.nFileSizeLow);
      err = VGAUTH_E_FAIL;
      goto done;
   }

   // check ownership
   err = ServiceFileVerifyAdminGroupOwned(fileName);
   if (err == VGAUTH_E_OK) {
      if (userName == NULL) {
         err = ServiceFileVerifyEveryoneReadable(fileName);
      } else {
         err = ServiceFileVerifyUserAccess(fileName,
                                           userName);
      }
   }
   if (err != VGAUTH_E_OK) {
      if (userName == NULL) {
         Audit_Event(FALSE,
                  SU_(alias.mapfile.badperm,
                      "Alias store mapping file '%s' has incorrect"
                      " owner or permissions.  "
                      "The Aliases in the mapping file will not be "
                      "available for authentication"),
                  fileName);
      } else {
         Audit_Event(FALSE,
                     SU_(alias.alias.badperm,
                         "Alias store '%s' has incorrect owner"
                         " or permissions.  "
                         "The Aliases for user '%s' will not be available "
                         "for authentication"),
                     fileName, userName);
      }
      goto done;
   }

   /*
    * Open the file
    */
   hFile = CreateFileW(fileNameW,
                       GENERIC_READ,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_READONLY,
                       NULL);
   if (hFile == INVALID_HANDLE_VALUE) {
      VGAUTH_LOG_ERR_WIN("failed to open file %s", fileName);
      err = VGAUTH_E_FAIL;
      goto done;
   }

   /*
    * Double-check attributes
    */
   if (!GetFileInformationByHandle(hFile, &fileInfoHandle)) {
      VGAUTH_LOG_ERR_WIN("failed to get attributes of %s; failing read\n",
              fileName);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }

   if (fileAttrs.dwFileAttributes != fileInfoHandle.dwFileAttributes) {
      Warning("%s: dwFileAttributes changed mid-read %d %d; failing read\n",
              __FUNCTION__,
              fileAttrs.dwFileAttributes, fileInfoHandle.dwFileAttributes);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }
   if ((fileAttrs.nFileSizeHigh != fileInfoHandle.nFileSizeHigh) ||
       (fileAttrs.nFileSizeLow != fileInfoHandle.nFileSizeLow)) {
      Warning("%s: file size of %s changed mid-read; failing read\n",
              __FUNCTION__, fileName);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }

   /*
    * Double-check ownership
    */
   err = ServiceFileVerifyAdminGroupOwnedByHandle(hFile);
   if (err == VGAUTH_E_OK) {
      if (userName == NULL) {
         err = ServiceFileVerifyEveryoneReadableByHandle(hFile);
      } else {
         err = ServiceFileVerifyUserAccessByHandle(hFile, userName);
      }
   }

   if (err != VGAUTH_E_OK) {
      Warning("%s: file ownsership changed mid-read; failing read\n",
              __FUNCTION__);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }

   if (!allowSymlinks) {
      /*
       * Check if fileName is real path.
       */
      if ((realPath = ServiceFileGetPathByHandle(hFile)) == NULL) {
         err = VGAUTH_E_FAIL;
         goto done;
      }
      if (Util_Utf8CaseCmp(realPath, fileName) != 0) {
         Warning("%s: Real path (%s) is not same as file path (%s)\n",
                 __FUNCTION__, realPath, fileName);
         err = VGAUTH_E_FAIL;
         goto done;
      }
   }

   /*
    * Now finally read the contents.
    */
   toRead = fileAttrs.nFileSizeLow;
   buf = g_malloc0(toRead + 1);
   bp = buf;
   while (toRead > 0) {
      ok = ReadFile(hFile, bp, toRead, &bytesRead, NULL);
      if (!ok) {
         VGAUTH_LOG_ERR_WIN("ReadFile(%s) failed", fileName);
         err = VGAUTH_E_FAIL;
         g_free(buf);
         goto done;
      }
      toRead -= bytesRead;
      bp += bytesRead;
   }


done:
   if (err == VGAUTH_E_OK) {
      *contents = buf;
      *fileSize = fileAttrs.nFileSizeLow;
   }
   if (hFile) {
      CloseHandle(hFile);
   }
   g_free(fileNameW);
   g_free(realPath);

   return err;
}

#else    // !_WIN32

static VGAuthError
ServiceLoadFileContentsPosix(const gchar *fileName,
                             const gchar *userName,
                             gchar **contents,
                             gsize *fileSize)
{
   int ret;
   uid_t uid = -1;
   gid_t gid = -1;
   gsize toRead;
   struct stat lstatBuf;
   struct stat fstatBuf;
   VGAuthError err = VGAUTH_E_FAIL;
   gchar *buf;
   gchar *bp;
   int fd = -1;
   gchar realPath[PATH_MAX] = { 0 };

   *fileSize = 0;
   *contents = NULL;

   /*
    * No time-of-check to time-of-use issue between this lstat() call and the
    * subsequent open() since the open() is followed by fstat() and a series
    * of checks of the fstat results against the lstat results to ensure that
    * key file attributes did not change between the lstat() and the open().
    */
   /* coverity[fs_check_call] */
   ret = g_lstat(fileName, &lstatBuf);
   if (ret != 0) {
      Warning("%s: lstat(%s) failed (%d %d)\n",
              __FUNCTION__, fileName, ret, errno);
      return VGAUTH_E_FAIL;
   }

   if (lstatBuf.st_size > ALIASSTORE_FILE_MAX_SIZE) {
      Warning("%s: size of %s too large %d; failing read\n", __FUNCTION__,
              fileName, (int) lstatBuf.st_size);
      return VGAUTH_E_FAIL;
   }

   /*
    * Check the file type and verify the permissions.
    * A NULL userName means we're looking at the
    * mapfile.
    */
   if (NULL == userName) {
      if (!S_ISREG(lstatBuf.st_mode)) {
         Audit_Event(FALSE,
                     SU_(alias.mapping.badfile,
                         "Mapping file '%s' exists but is not a regular file.  "
                         "The Aliases in the mapping file will not be available for "
                         "authentication"),
                     fileName);
         Warning("%s: mapping file %s exists but isn't a regular file, punting\n",
                 __FUNCTION__, fileName);
         return VGAUTH_E_FAIL;
      }
      err = ServiceFileVerifyFileOwnerAndPerms(fileName,
                                               SUPERUSER_NAME,
                                               ALIASSTORE_MAPFILE_PERMS,
                                               &uid, &gid);
      if (err != VGAUTH_E_OK) {
         Audit_Event(FALSE,
                     SU_(alias.mapfile.badperm,
                         "Alias store mapping file '%s' has incorrect"
                         " owner or permissions.  "
                         "The Aliases in the mapping file will not be "
                         "available for authentication"),
                  fileName);
         return err;
      }
   } else {
      if (!S_ISREG(lstatBuf.st_mode)) {
         Audit_Event(FALSE,
                     SU_(alias.alias.badfile,
                         "Alias store '%s' exists but is not a regular file.  "
                         "The Aliases for user '%s' will not be available for "
                         "authentication"),
                     fileName, userName);
         Warning("%s: file %s exists but isn't a regular file, punting\n",
                 __FUNCTION__, fileName);
         return VGAUTH_E_FAIL;
      }


      err = ServiceFileVerifyFileOwnerAndPerms(fileName,
                                               userName,
                                               ALIASSTORE_FILE_PERMS,
                                               &uid, &gid);
      if (err != VGAUTH_E_OK) {
         Audit_Event(FALSE,
                     SU_(alias.alias.badperm,
                         "Alias store '%s' has incorrect owner"
                         " or permissions.  "
                         "The Aliases for user '%s' will not be available "
                         "for authentication"),
                     fileName, userName);
         return err;
      }
   }

   /*
    * Now open the file.
    */
   fd = g_open(fileName, O_RDONLY);
   if (fd < 0) {
      Warning("%s: failed to open %s for read (%d)\n",
              __FUNCTION__, fileName, errno);
      return VGAUTH_E_FAIL;
   }

   /*
    * fstat() to make sure it wasn't changed between the first check
    * and the open().
    */
   ret = fstat(fd, &fstatBuf);
   if (ret != 0) {
      Warning("%s: fstat(%s) failed (%d %d)\n",
              __FUNCTION__, fileName, ret, errno);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }

   /*
    * Now the confidence checks.
    */
   if (lstatBuf.st_size != fstatBuf.st_size) {
      Warning("%s: size of %s changed (%d vs %d)\n", __FUNCTION__,
              fileName, (int) lstatBuf.st_size, (int) fstatBuf.st_size);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }

   if (lstatBuf.st_mode != fstatBuf.st_mode) {
      Warning("%s: mode of %s changed (%d vs %d)\n", __FUNCTION__,
              fileName, (int) lstatBuf.st_mode, (int) fstatBuf.st_mode);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }

   if ((lstatBuf.st_uid != fstatBuf.st_uid) ||
       (uid != fstatBuf.st_uid)) {
      Warning("%s: uid of %s changed (%d vs %d vs %d)\n", __FUNCTION__,
              fileName, (int) lstatBuf.st_uid, (int) fstatBuf.st_uid, (int) uid);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }

   if ((lstatBuf.st_gid != fstatBuf.st_gid) ||
       (gid != fstatBuf.st_gid)) {
      Warning("%s: gid of %s changed (%d vs %d vs %d)\n", __FUNCTION__,
              fileName, (int) lstatBuf.st_gid, (int) fstatBuf.st_gid, (int) gid);
      // XXX audit this?
      err = VGAUTH_E_FAIL;
      goto done;
   }

   if (!allowSymlinks) {
      /*
       * Check if fileName is real path.
       */
      if (realpath(fileName, realPath) == NULL) {
         Warning("%s: realpath() failed. errno (%d)\n", __FUNCTION__, errno);
         err = VGAUTH_E_FAIL;
         goto done;
      }
      if (g_strcmp0(realPath, fileName) != 0) {
         Warning("%s: Real path (%s) is not same as file path (%s)\n",
                 __FUNCTION__, realPath, fileName);
         err = VGAUTH_E_FAIL;
         goto done;
      }
   }

   /*
    * All confidence checks passed; read the bits.
    */
   toRead = lstatBuf.st_size;
   buf = g_malloc0(toRead + 1);
   bp = buf;
   while (toRead > 0) {
      ret = read(fd, bp, toRead);
      if (ret < 0) {
         Warning("%s: failed to read from file %s (%d)\n",
                 __FUNCTION__, fileName, errno);
         // XXX audit this?
         err = VGAUTH_E_FAIL;
         g_free(buf);
         goto done;
      } else if (ret == 0) {     // eof
         break;
      }

      bp += ret;
      toRead -= ret;
   }

done:
   if (VGAUTH_E_OK == err) {
      *contents = buf;
      *fileSize = lstatBuf.st_size;
   }

   close(fd);

   return err;
}
#endif   // !_WIN32


static VGAuthError
ServiceLoadFileContents(const gchar *fileName,
                        const gchar *userName,
                        gchar **contents,
                        gsize *fileSize)
{
#ifdef _WIN32
   return ServiceLoadFileContentsWin(fileName,
                                     userName,
                                     contents,
                                     fileSize);
#else
   return ServiceLoadFileContentsPosix(fileName,
                                       userName,
                                       contents,
                                       fileSize);
#endif
}


/*
 ******************************************************************************
 * AliasDumpAliases --                                                   */ /**
 *
 * Dumps a list of Aliases to the given FILE*.
 *
 * @param[in]   fp           The FILE to dump to.
 * @param[in]   num          The number of entries.
 * @param[in]   aList        The list.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
AliasDumpAliases(FILE *fp,
                 int num,
                 ServiceAlias *aList)
{
   int i;
   int j;
   gchar *text;
   int ret;
   VGAuthError err = VGAUTH_E_OK;
   ServiceAliasInfo *si;

   ASSERT(fp);
   ASSERT((0 == num) || (NULL != aList));

   ret = fputs(ALIASSTORE_XML_PREAMBLE "\n", fp);
   if (ret == EOF) {
      Warning("%s: fputs() failed\n", __FUNCTION__);
      err = VGAUTH_E_FAIL;
      goto done;
   }

   ret = fputs(AliasStoreXMLUserAliasesStart, fp);
   if (ret == EOF) {
      Warning("%s: fputs() failed\n", __FUNCTION__);
      err = VGAUTH_E_FAIL;
      goto done;
   }

   for (i = 0; i < num; i++) {
      text = g_markup_printf_escaped(AliasStoreXMLFormatAliasStart,
                                     g_strstrip(aList[i].pemCert));
      ret = fputs(text, fp);
      g_free(text);
      if (ret == EOF) {
         Warning("%s: fputs() failed\n", __FUNCTION__);
         err = VGAUTH_E_FAIL;
         goto done;
      }

      for (j = 0; j < aList[i].num; j++) {
         si = &(aList[i].infos[j]);
         if (si->type == SUBJECT_TYPE_NAMED) {
            text = g_markup_printf_escaped(AliasStoreXMLFormatNamedAliasInfo,
                                           si->name,
                                           si->comment);
         } else {
            text = g_markup_printf_escaped(AliasStoreXMLFormatAnyAliasInfo,
                                           si->comment);
         }
         ret = fputs(text, fp);
         g_free(text);
         if (ret == EOF) {
            Warning("%s: fputs() failed\n", __FUNCTION__);
            err = VGAUTH_E_FAIL;
            goto done;
         }
      }

      ret = fputs(AliasStoreXMLFormatAliasEnd, fp);
      if (ret == EOF) {
         Warning("%s: fputs() failed\n", __FUNCTION__);
         err = VGAUTH_E_FAIL;
         goto done;
      }
   }

   ret = fputs(AliasStoreXMLUserAliasesEnd, fp);
   if (ret == EOF) {
      Warning("%s: fputs() failed\n", __FUNCTION__);
      err = VGAUTH_E_FAIL;
      goto done;
   }

done:

   return err;
}


/*
 ******************************************************************************
 * AliasDumpMappedAliasesFile --                                         */ /**
 *
 * Dumps a list of MappedAliases to a FILE *.
 *
 * @param[in]   fp           The FILE to dump to.
 * @param[in]   num          The number of entries.
 * @param[in]   maList       The list.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
AliasDumpMappedAliasesFile(FILE *fp,
                           int num,
                           ServiceMappedAlias *maList)
{
   int i;
   int j;
   gchar *text;
   int ret;
   VGAuthError err = VGAUTH_E_OK;

   ret = fputs(ALIASSTORE_XML_PREAMBLE "\n", fp);
   if (ret == EOF) {
      Warning("%s: fputs() failed\n", __FUNCTION__);
      err = VGAUTH_E_FAIL;
      goto done;
   }

   ret = fputs(MapfileXMLStart, fp);
   if (ret == EOF) {
      Warning("%s: fputs() failed\n", __FUNCTION__);
      err = VGAUTH_E_FAIL;
      goto done;
   }

   for (i = 0; i < num; i++) {
      text = g_markup_printf_escaped(MapfileXMLMappingStart,
                                     g_strstrip(maList[i].pemCert),
                                     maList[i].userName);
      ret = fputs(text, fp);
      g_free(text);
      if (ret == EOF) {
         Warning("%s: fputs() failed\n", __FUNCTION__);
         err = VGAUTH_E_FAIL;
         goto done;
      }

      for (j = 0; j < maList[i].num; j++) {
         if (maList[i].subjects[j].type == SUBJECT_TYPE_ANY) {
            ret = fputs(MapfileXMLAnySubject, fp);
         } else {
            text = g_markup_printf_escaped(MapfileXMLNamedSubject,
                                           maList[i].subjects[j].name);
            ret = fputs(text, fp);
            g_free(text);
         }
         if (ret == EOF) {
            Warning("%s: fputs() failed\n", __FUNCTION__);
            err = VGAUTH_E_FAIL;
            goto done;
         }
      }

      ret = fputs(MapfileXMLMappingEnd, fp);
      if (ret == EOF) {
         Warning("%s: fputs() failed\n", __FUNCTION__);
         err = VGAUTH_E_FAIL;
         goto done;
      }
   }

   ret = fputs(MapfileXMLEnd, fp);
   if (ret == EOF) {
      Warning("%s: fputs() failed\n", __FUNCTION__);
      err = VGAUTH_E_FAIL;
      goto done;
   }

done:
   return err;
}


/*
 ******************************************************************************
 * AliasStartElement --                                                  */ /**
 *
 * Used by the per-user Alias parser.
 *
 * Called by the XML parser when it sees the start of a new
 * element.  Used to update the current parser state, and allocate
 * any space that may be needed for processing that state.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  elementName         The name of the element being started.
 * @param[in]  attributeNames      The names of any attributes on the element.
 * @param[in]  attributeValues     The values of any attributes on the element.
 * @param[in]  userData            The AliasParseList as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
AliasStartElement(GMarkupParseContext *parseContext,
                  const gchar *elementName,
                  const gchar **attributeNames,
                  const gchar **attributeValues,
                  gpointer userData,
                  GError **error)
{
   AliasParseList *list = (AliasParseList *) userData;
   ServiceAliasInfo *infos;

   ASSERT(list);

   switch (list->state) {
   case ALIAS_PARSE_STATE_NONE:
      if (g_strcmp0(elementName, ALIASINFO_USER_ALIASES_ELEMENT_NAME) == 0) {
         list->state = ALIAS_PARSE_STATE_ALIASES;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      break;
   case ALIAS_PARSE_STATE_ALIASES:
      /*
       * Starting a new Alias, alloc and grow.
       */
      if (g_strcmp0(elementName, ALIASINFO_ALIAS_ELEMENT_NAME) == 0) {
         list->num++;
         list->aList = g_realloc_n(list->aList,
                                   list->num, sizeof(ServiceAlias));
         list->aList[list->num - 1].pemCert = NULL;
         list->aList[list->num - 1].num = 0;
         list->aList[list->num - 1].infos = NULL;
         list->state = ALIAS_PARSE_STATE_ALIAS;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      break;
   case ALIAS_PARSE_STATE_ALIAS:
      if (g_strcmp0(elementName, ALIASINFO_PEMCERT_ELEMENT_NAME) == 0) {
         list->state = ALIAS_PARSE_STATE_PEMCERT;
      } else if (g_strcmp0(elementName, ALIASINFO_ALIASINFOS_ELEMENT_NAME) == 0) {
         list->state = ALIAS_PARSE_STATE_ALIASINFOS;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      break;
   case ALIAS_PARSE_STATE_ALIASINFOS:
      if (g_strcmp0(elementName, ALIASINFO_ALIASINFO_ELEMENT_NAME) == 0) {
         int n;

         list->state = ALIAS_PARSE_STATE_ALIASINFO;

         // grow
         infos = list->aList[list->num - 1].infos;
         n = ++(list->aList[list->num - 1].num);
         infos = g_realloc_n(infos, n, sizeof(ServiceAliasInfo));
         infos[n - 1].type = -1;
         infos[n - 1].name = NULL;
         infos[n - 1].comment = NULL;
         list->aList[list->num - 1].infos = infos;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      break;
   case ALIAS_PARSE_STATE_ALIASINFO:
      if (g_strcmp0(elementName, ALIASINFO_SUBJECT_ELEMENT_NAME) == 0) {
         list->state = ALIAS_PARSE_STATE_SUBJECT;
      } else if (g_strcmp0(elementName, ALIASINFO_ANYSUBJECT_ELEMENT_NAME) == 0) {
         list->state = ALIAS_PARSE_STATE_ANYSUBJECT;
         /*
          * For empty-tag elements, the Contents code is never called, so set
          * the type here.
          */
         list->aList[list->num - 1].infos[list->aList[list->num - 1].num - 1].type =
            SUBJECT_TYPE_ANY;
      } else if (g_strcmp0(elementName, ALIASINFO_COMMENT_ELEMENT_NAME) == 0) {
         list->state = ALIAS_PARSE_STATE_COMMENT;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      break;
   default:
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Unexpected element '%s' in parse state %d",
                  elementName, list->state);
      break;
   }
}


/*
 ******************************************************************************
 * AliasTextContents --                                                  */ /**
 *
 * Used by the per-user Alias parser.
 *
 * Called by the parser with the contents of an element.
 * Used to store the values.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  text                The contents of the current element
 *                                 (not NUL terminated)
 * @param[in]  textSize            The length of the text.
 * @param[in]  userData            The AliasParseList as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
AliasTextContents(GMarkupParseContext *parseContext,
                  const gchar *text,
                  gsize textSize,
                  gpointer userData,
                  GError **error)
{
   AliasParseList *list = (AliasParseList *) userData;
   gchar *val;
   ServiceAliasInfo *infos;
   int idx;
   gboolean duplicate_found = FALSE;

   ASSERT(list);

   val = g_strndup(text, textSize);

   switch (list->state) {
   case ALIAS_PARSE_STATE_NONE:
   case ALIAS_PARSE_STATE_ALIASES:
   case ALIAS_PARSE_STATE_ALIAS:
   case ALIAS_PARSE_STATE_ALIASINFOS:
   case ALIAS_PARSE_STATE_ALIASINFO:
   case ALIAS_PARSE_STATE_ANYSUBJECT:
      // no-op -- should just be whitespace
      break;
   case ALIAS_PARSE_STATE_PEMCERT:
      if (list->aList[list->num - 1].pemCert != NULL) {
         duplicate_found = TRUE;
         break;
      }
      /*
       * Any extra whitespace in the PEM will confuse the uniqueness
       * check.
       */
      list->aList[list->num - 1].pemCert = g_strstrip(val);
      val = NULL;
      break;
   case ALIAS_PARSE_STATE_COMMENT:
      infos = list->aList[list->num - 1].infos;
      ASSERT(infos);
      idx = list->aList[list->num - 1].num - 1;
      if (infos[idx].comment != NULL) {
         duplicate_found = TRUE;
         break;
      }
      infos[idx].comment = val;
      val = NULL;
      break;
   case ALIAS_PARSE_STATE_SUBJECT:
      infos = list->aList[list->num - 1].infos;
      ASSERT(infos);
      idx = list->aList[list->num - 1].num - 1;
      if (infos[idx].name != NULL) {
         duplicate_found = TRUE;
         break;
      }
      infos[idx].name = val;
      infos[idx].type = SUBJECT_TYPE_NAMED;
      val = NULL;
      break;
   }

   if (duplicate_found) {
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Unexpected contents '%s' in parse state %d",
                  val, list->state);
   }
   g_free(val);
}


/*
 ******************************************************************************
 * AliasEndElement --                                                    */ /**
 *
 * Used by the per-user Alias parser.
 *
 * Called by the XML parser when the end of an element is reached.
 * Used here to pop the parse state.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  elementName         The name of the element being finished.
 * @param[in]  userData            The AliasParseState as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
AliasEndElement(GMarkupParseContext *parseContext,
                const gchar *elementName,
                gpointer userData,
                GError **error)
{
   AliasParseList *list = (AliasParseList *) userData;

   ASSERT(list);

   switch (list->state) {
   case ALIAS_PARSE_STATE_ALIASES:
      list->state = ALIAS_PARSE_STATE_NONE;
      break;
   case ALIAS_PARSE_STATE_ALIAS:
      list->state = ALIAS_PARSE_STATE_ALIASES;
      break;
   case ALIAS_PARSE_STATE_PEMCERT:
   case ALIAS_PARSE_STATE_ALIASINFOS:
      list->state = ALIAS_PARSE_STATE_ALIAS;
      break;
   case ALIAS_PARSE_STATE_ALIASINFO:
      list->state = ALIAS_PARSE_STATE_ALIASINFOS;
      break;
   case ALIAS_PARSE_STATE_SUBJECT:
   case ALIAS_PARSE_STATE_ANYSUBJECT:
   case ALIAS_PARSE_STATE_COMMENT:
      list->state = ALIAS_PARSE_STATE_ALIASINFO;
      break;
   case ALIAS_PARSE_STATE_NONE:
      // XXX error this?
      ASSERT(0);
   }
}


/*
 ******************************************************************************
 * AliasCheckAliasFilePerms --                                           */ /**
 *
 * Verifies the alias file permissions are as expected.
 *
 * @param[in]   fileName        The file to check.
 * @param[in]   userName        Expected owner of the file.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
AliasCheckAliasFilePerms(const gchar *fileName,
                         const gchar *userName)
{
   VGAuthError err;

#ifdef _WIN32
   err = ServiceFileVerifyAdminGroupOwned(fileName);
   if (err == VGAUTH_E_OK) {
      err = ServiceFileVerifyUserAccess(fileName,
                                        userName);
   }
#else
   err = ServiceFileVerifyFileOwnerAndPerms(fileName,
                                            userName,
                                            ALIASSTORE_FILE_PERMS,
                                            NULL, NULL);
#endif

   if (VGAUTH_E_OK != err) {
      Audit_Event(FALSE,
                  SU_(alias.alias.badperm,
                      "Alias store '%s' has incorrect owner"
                      " or permissions.  "
                      "The Aliases for user '%s' will not be available "
                      "for authentication"),
                  fileName, userName);
   }
   return err;
}


/*
 ******************************************************************************
 * AliasCheckMapFilePerms --                                             */ /**
 *
 * Verifies the mapping file permissions are as expected.
 *
 * @param[in]   fileName        The file to check.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
AliasCheckMapFilePerms(const gchar *fileName)
{
   VGAuthError err;

#ifdef _WIN32
   err = ServiceFileVerifyAdminGroupOwned(fileName);
   if (err == VGAUTH_E_OK) {
      err = ServiceFileVerifyEveryoneReadable(fileName);
   }
#else
   err = ServiceFileVerifyFileOwnerAndPerms(fileName,
                                            SUPERUSER_NAME,
                                            ALIASSTORE_MAPFILE_PERMS,
                                            NULL, NULL);
#endif
   if (VGAUTH_E_OK != err) {
      Audit_Event(FALSE,
                  SU_(alias.mapfile.badperm,
                      "Alias store mapping file '%s' has incorrect"
                      " owner or permissions.  "
                      "The Aliases in the mapping file will not be "
                      "available for authentication"),
                  fileName);
   }

   return err;
}


/*
 ******************************************************************************
 * AliasLoadAliases --                                                   */ /**
 *
 * Reads and parses the Alias file for userName.
 *
 * @param[in]   userName        The user whose store is to be loaded.
 * @param[out]  num             The number of certs read.
 * @param[out]  aList           The Aliases read.  The caller should
 *                              call ServiceAliasFreeAliasList() when done.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
AliasLoadAliases(const gchar *userName,
                 int *num,
                 ServiceAlias **aList)
{
   static const GMarkupParser aliasParser = {
      AliasStartElement,
      AliasEndElement,
      AliasTextContents,
      NULL,
      NULL,
   };
   GMarkupParseContext *context;
   gboolean bRet;
   gchar *fileContents = NULL;
   gsize fileSize;
   gchar *aliasFilename;
   AliasParseList list;
   VGAuthError err;
   GError *gErr = NULL;

   ASSERT(num);
   ASSERT(aList);

   *num = 0;
   *aList = NULL;

   list.state = ALIAS_PARSE_STATE_NONE;
   list.aList = NULL;
   list.num = 0;

   context = g_markup_parse_context_new(&aliasParser, 0, &list, NULL);

   aliasFilename = ServiceUserNameToAliasStoreFileName(userName);

   /*
    * If it's not there, then we have nothing to read.
    */
   if (!g_file_test(aliasFilename, G_FILE_TEST_EXISTS)) {
      goto done;
   }

   err = ServiceLoadFileContents(aliasFilename, userName,
                                 &fileContents, &fileSize);
   if (err != VGAUTH_E_OK) {
      goto done;
   }

   /*
    * Now parse it.  If all goes well, 'list' will be properly
    * populated with the results.
    */
   bRet = g_markup_parse_context_parse(context, fileContents, fileSize, &gErr);
   if (!bRet) {
      Warning("%s: Unable to parse contents of '%s': %s\n", __FUNCTION__,
              aliasFilename, gErr->message);
      g_error_free(gErr);
      err = VGAUTH_E_FAIL;
      goto cleanup;
   }

done:
   /*
    * We're just transferring the data to the caller to free.
    */
   *num = list.num;
   *aList = list.aList;
   err = VGAUTH_E_OK;

cleanup:
   if (err != VGAUTH_E_OK) {
      ServiceAliasFreeAliasList(list.num, list.aList);
   }
   g_markup_parse_context_free(context);
   g_free(aliasFilename);
   g_free(fileContents);
   return err;
}


/*
 ******************************************************************************
 * MappedStartElement --                                                 */ /**
 *
 * Used by the mapping file parser.
 *
 * Called by the XML parser when it sees the start of a new
 * element.  Used to update the current parser state, and allocate
 * any space that may be needed for processing that state.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  elementName         The name of the element being started.
 * @param[in]  attributeNames      The names of any attributes on the element.
 * @param[in]  attributeValues     The values of any attributes on the element.
 * @param[in]  userData            The MappedAliasParseList as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
MappedStartElement(GMarkupParseContext *parseContext,
                   const gchar *elementName,
                   const gchar **attributeNames,
                   const gchar **attributeValues,
                   gpointer userData,
                   GError **error)
{
   MappedAliasParseList *list = (MappedAliasParseList *) userData;
   ServiceSubject *subjs;
   ServiceSubjectType sType = -1;
   int n;

   ASSERT(list);

   switch (list->state) {
   case MAP_PARSE_STATE_NONE:
      if (g_strcmp0(elementName, MAP_MAPPINGS_ELEMENT_NAME) == 0) {
         list->state = MAP_PARSE_STATE_MAPPINGS;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      break;
   case MAP_PARSE_STATE_MAPPINGS:
      /*
       * Starting a new Mapping, alloc and grow.
       */
      if (g_strcmp0(elementName, MAP_MAPPING_ELEMENT_NAME) == 0) {
         list->num++;
         list->maList = g_realloc_n(list->maList,
                                    list->num, sizeof(ServiceMappedAlias));
         list->maList[list->num - 1].pemCert = NULL;
         list->maList[list->num - 1].userName = NULL;
         list->maList[list->num - 1].num = 0;
         list->maList[list->num - 1].subjects = NULL;
         list->state = MAP_PARSE_STATE_MAPPING;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      break;
   case MAP_PARSE_STATE_MAPPING:
      if (g_strcmp0(elementName, ALIASINFO_PEMCERT_ELEMENT_NAME) == 0) {
         list->state = MAP_PARSE_STATE_PEMCERT;
      } else if (g_strcmp0(elementName, MAP_SUBJECTS_ELEMENT_NAME) == 0) {
         list->state = MAP_PARSE_STATE_SUBJECTS;
      } else if (g_strcmp0(elementName, MAP_USERNAME_ELEMENT_NAME) == 0) {
         list->state = MAP_PARSE_STATE_USERNAME;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      break;
   case MAP_PARSE_STATE_SUBJECTS:
      if (g_strcmp0(elementName, MAP_SUBJECT_ELEMENT_NAME) == 0) {
         list->state = MAP_PARSE_STATE_SUBJECT;
         sType = SUBJECT_TYPE_NAMED;
      } else if (g_strcmp0(elementName, MAP_ANYSUBJECT_ELEMENT_NAME) == 0) {
         list->state = MAP_PARSE_STATE_ANYSUBJECT;
         sType = SUBJECT_TYPE_ANY;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, list->state);
      }
      // grow
      n = ++(list->maList[list->num - 1].num);
      subjs = list->maList[list->num - 1].subjects;
      subjs = g_realloc_n(subjs, n, sizeof(ServiceSubject));

      subjs[n - 1].type = sType;
      subjs[n - 1].name = NULL;
      list->maList[list->num - 1].subjects = subjs;
      break;
   default:
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Unexpected element '%s' in parse state %d",
                  elementName, list->state);
      break;
   }
}


/*
 ******************************************************************************
 * MappedTextContents --                                                 */ /**
 *
 * Used by the mapping file parser.
 *
 * Called by the parser with the contents of an element.
 * Used to store the values.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  text                The contents of the current element
 *                                 (not NUL terminated)
 * @param[in]  textSize            The length of the text.
 * @param[in]  userData            The MappedAliasParseList as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
MappedTextContents(GMarkupParseContext *parseContext,
                   const gchar *text,
                   gsize textSize,
                   gpointer userData,
                   GError **error)
{
   MappedAliasParseList *list = (MappedAliasParseList *) userData;
   gchar *val;
   int idx;
   gboolean duplicate_found = FALSE;

   ASSERT(list);

   val = g_strndup(text, textSize);

   switch (list->state) {
   case MAP_PARSE_STATE_NONE:
   case MAP_PARSE_STATE_MAPPINGS:
   case MAP_PARSE_STATE_MAPPING:
   case MAP_PARSE_STATE_SUBJECTS:
   case MAP_PARSE_STATE_ANYSUBJECT:
      // no-op -- should just be whitespace
      break;
   case MAP_PARSE_STATE_PEMCERT:
      if (list->maList[list->num - 1].pemCert != NULL) {
         duplicate_found = TRUE;
         break;
      }
      /*
       * Any extra whitespace in the PEM will confuse the uniqueness
       * check.
       */
      list->maList[list->num - 1].pemCert = g_strstrip(val);
      val = NULL;
      break;
   case MAP_PARSE_STATE_USERNAME:
      if (list->maList[list->num - 1].userName != NULL) {
         duplicate_found = TRUE;
         break;
      }
      list->maList[list->num - 1].userName = val;
      val = NULL;
      break;
   case MAP_PARSE_STATE_SUBJECT:
      idx = list->maList[list->num - 1].num;
      if (list->maList[list->num - 1].subjects[idx - 1].name != NULL) {
         duplicate_found = TRUE;
         break;
      }
      list->maList[list->num - 1].subjects[idx - 1].name = val;
      list->maList[list->num - 1].subjects[idx - 1].type = SUBJECT_TYPE_NAMED;
      val = NULL;
      break;
   }

   if (duplicate_found) {
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Unexpected contents '%s' in parse state %d",
                  val, list->state);
   }
   g_free(val);
}


/*
 ******************************************************************************
 * MappedEndElement --                                                   */ /**
 *
 * Used by the mapping file parser.
 *
 * Called by the XML parser when the end of an element is reached.
 * Used here to pop the parse state.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  elementName         The name of the element being finished.
 * @param[in]  userData            The MappedAliasParseList as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
MappedEndElement(GMarkupParseContext *parseContext,
                 const gchar *elementName,
                 gpointer userData,
                 GError **error)
{
   MappedAliasParseList *list = (MappedAliasParseList *) userData;

   ASSERT(list);

   switch (list->state) {
   case MAP_PARSE_STATE_MAPPINGS:
      list->state = MAP_PARSE_STATE_NONE;
      break;
   case MAP_PARSE_STATE_MAPPING:
      list->state = MAP_PARSE_STATE_MAPPINGS;
      break;
   case MAP_PARSE_STATE_PEMCERT:
   case MAP_PARSE_STATE_SUBJECTS:
   case MAP_PARSE_STATE_USERNAME:
      list->state = MAP_PARSE_STATE_MAPPING;
      break;
   case MAP_PARSE_STATE_SUBJECT:
   case MAP_PARSE_STATE_ANYSUBJECT:
      list->state = MAP_PARSE_STATE_SUBJECTS;
      break;
   case MAP_PARSE_STATE_NONE:
      // XXX set an error here
      ASSERT(0);
   }
}


/*
 ******************************************************************************
 * AliasLoadMapped --                                                    */ /**
 *
 * Reads and parses the mapping file.
 *
 * @param[out]  num             The number of entries read.
 * @param[out]  maList          The ServiceMappedAliases read.  The caller
 *                              should call ServiceAliasFreeMappedAliasList()
 *                              when done.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
AliasLoadMapped(int *num,
                ServiceMappedAlias **maList)
{
   static const GMarkupParser mappedIdParser = {
      MappedStartElement,
      MappedEndElement,
      MappedTextContents,
      NULL,
      NULL,
   };
   GMarkupParseContext *context = NULL;
   gboolean bRet;
   gchar *fileContents = NULL;
   gsize fileSize;
   gchar *mapFilename;
   MappedAliasParseList list;
   VGAuthError err;
   GError *gErr = NULL;

   ASSERT(num);
   ASSERT(maList);

   *num = 0;
   *maList = NULL;

   list.state = MAP_PARSE_STATE_NONE;
   list.maList = NULL;
   list.num = 0;

   context = g_markup_parse_context_new(&mappedIdParser, 0, &list, NULL);

   mapFilename = g_strdup_printf("%s"DIRSEP"%s",
                                 aliasStoreRootDir,
                                 ALIASSTORE_MAPFILE_NAME);

   /*
    * If its not there, then we have nothing to read.
    */
   if (!g_file_test(mapFilename, G_FILE_TEST_EXISTS)) {
      goto done;
   }

   err = ServiceLoadFileContents(mapFilename, NULL,
                                 &fileContents, &fileSize);
   if (err != VGAUTH_E_OK) {
      goto done;
   }

   /*
    * Now parse it.  If all goes well, 'list' will be properly
    * populated with the results.
    */
   bRet = g_markup_parse_context_parse(context, fileContents, fileSize, &gErr);
   if (!bRet) {
      Warning("%s: Unable to parse contents of '%s': %s\n", __FUNCTION__,
              mapFilename, gErr->message);
      g_error_free(gErr);
      err = VGAUTH_E_FAIL;
      goto cleanup;
   }

done:
   /*
    * We're just transferring the certs to the caller to free.
    */
   *num = list.num;
   *maList = list.maList;
   err = VGAUTH_E_OK;

cleanup:
   if (err != VGAUTH_E_OK) {
      ServiceAliasFreeMappedAliasList(list.num, list.maList);
   }
   g_markup_parse_context_free(context);
   g_free(mapFilename);
   g_free(fileContents);
   return err;

   return VGAUTH_E_OK;
}

/*
 ******************************************************************************
 * AliasSafeRenameFiles --                                               */ /**
 *
 * Renames the alias and map files, making sure we don't lose the originals
 * in an error situation.  Since the two files must be in sync, we need
 * to do both at once to allow for restoration on an error.  Note that
 * updating either file is optional, and the caller indicates the need
 * based on a non-NULL filename.
 *
 * @param[in]   srcAliasFile   The alias file being saved.
 * @param[in]   srcMapFile     Any mapping file being saved.
 * @param[in]   userName       The user whose alias is being modified.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
AliasSafeRenameFiles(const gchar *srcAliasFilename,
                     const gchar *srcMapFilename,
                     const gchar *userName)
{
   VGAuthError err;
   gchar *aliasFilename;
   gchar *aliasBackupFilename = NULL;
   gchar *mapFilename = NULL;
   gchar *mapBackupFilename = NULL;


   aliasFilename = ServiceUserNameToAliasStoreFileName(userName);


   /*
    * Do some confidence checks on the files.
    */
   if (NULL != srcAliasFilename) {
      if (g_file_test(aliasFilename, G_FILE_TEST_EXISTS) &&
          !g_file_test(aliasFilename, G_FILE_TEST_IS_REGULAR)) {
         Audit_Event(FALSE,
                     SU_(alias.alias.badfile,
                         "Alias store '%s' exists but is not a regular file.  "
                         "The Aliases for user '%s' will not be available for "
                         "authentication"),
                     aliasFilename, userName);
         Warning("%s: alias store file %s exists but isn't a regular file, punting\n",
                 __FUNCTION__, aliasFilename);
         err = VGAUTH_E_FAIL;
         goto done;
      }
   }

   if (NULL != srcMapFilename) {
      mapFilename = g_strdup_printf("%s"DIRSEP"%s",
                                    aliasStoreRootDir,
                                    ALIASSTORE_MAPFILE_NAME);

      if (g_file_test(mapFilename, G_FILE_TEST_EXISTS) &&
          !g_file_test(mapFilename, G_FILE_TEST_IS_REGULAR)) {
         Audit_Event(FALSE,
                     SU_(alias.mapping.badfile,
                         "Mapping file '%s' exists but is not a regular file.  "
                         "The Aliases in the mapping file will not be "
                         "available for authentication"),
                     mapFilename);
         Warning("%s: map file %s exists but isn't a regular file, punting\n",
                 __FUNCTION__, mapFilename);
         err = VGAUTH_E_FAIL;
         goto done;
      }
   }


   /*
    * Back up the real files so we can recover on an error.
    */
   if (NULL != srcAliasFilename) {
      if (g_file_test(aliasFilename, G_FILE_TEST_EXISTS)) {
         aliasBackupFilename = g_strdup_printf("%s.bak", aliasFilename);
         if (ServiceFileRenameFile(aliasFilename, aliasBackupFilename) < 0) {
            err = VGAUTH_E_FAIL;
            goto done;
         }
      }
   }

   if (NULL != srcMapFilename) {
      if (g_file_test(mapFilename, G_FILE_TEST_EXISTS)) {
         mapBackupFilename = g_strdup_printf("%s.bak", mapFilename);
         if (ServiceFileRenameFile(mapFilename, mapBackupFilename) < 0) {
            err = VGAUTH_E_FAIL;
            goto restore;
         }
      }
   }


   /*
    * Now rename the passed-in files as the official.
    */

   if (NULL != srcAliasFilename) {
      if (ServiceFileRenameFile(srcAliasFilename, aliasFilename) < 0) {
         err = VGAUTH_E_FAIL;
         goto restore;
      }
   }
   if (NULL != srcMapFilename) {
      if (ServiceFileRenameFile(srcMapFilename, mapFilename) < 0) {
         err = VGAUTH_E_FAIL;
         goto restore;
      }
   }

   /*
    * Clean up backups.
    */
   if (aliasBackupFilename && ServiceFileUnlinkFile(aliasBackupFilename) < 0) {
      /* XXX not much to do -- ServiceFileUnlinkFile() spewed error */
   }

   if (mapBackupFilename && ServiceFileUnlinkFile(mapBackupFilename) < 0) {
      /* XXX not much to do -- ServiceFileUnlinkFile() spewed error */
   }

   err = VGAUTH_E_OK;

   /*
    * It all worked, skip the restore section to the cleanup.
    */
   goto done;

restore:
   /*
    * Something went wrong, try to recover.  If this fails,
    * all we can do is whine about it.
    */
   Warning("%s: trying to restore files\n", __FUNCTION__);
   if (ServiceFileRenameFile(aliasBackupFilename, aliasFilename) < 0) {
      Warning("%s: failed to restore %s\n", __FUNCTION__, aliasFilename);
   }

   if (NULL != srcMapFilename) {
      if (ServiceFileRenameFile(mapBackupFilename, mapFilename) < 0) {
         Warning("%s: failed to restore %s\n", __FUNCTION__, mapFilename);
      }
   }

done:
   g_free(aliasFilename);
   g_free(mapFilename);
   g_free(aliasBackupFilename);
   g_free(mapBackupFilename);

   return err;
}


/*
 ******************************************************************************
 * AliasSaveAliasesAndMapped --                                          */ /**
 *
 * Store a list of Aliases in a store.  The mapping file
 * is modified at the same time, since it must stay in sync.
 * Ensures permissions are correct.
 *
 * @param[in]   userName        The user whose store is to be used.
 * @param[in]   num             The number of entries to store.
 * @param[in]   aList           The list of Aliases to store.
 * @param[in]   updateMap       True if the mapfile needs to be updated.
 * @param[in]   numMapped       The number of mapfile entries.
 * @param[in]   maList          The list of MappedAliases to store.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
AliasSaveAliasesAndMapped(const gchar *userName,
                          int num,
                          ServiceAlias *aList,
                          gboolean updateMap,
                          int numMapped,
                          ServiceMappedAlias *maList)
{
   VGAuthError err = VGAUTH_E_OK;
   FILE *fp = NULL;
   int fd;
   gchar *tmpAliasFilename = NULL;
   gchar *tmpMapFilename = NULL;
   int rc;
   gboolean emptyAliasFile = (num == 0);

   /*
    * Special case for empty lists.
    *
    * If the aList is empty, and we can't look up the user,
    * just remove it.
    *
    * This solves an issue with the 'deleted user' unit test.
    * Otherwise the tmp user can have a different uid/Sid
    * (especially on Windows) and think there's a security issue.
    */
   if (emptyAliasFile) {
      gchar *aliasFilename = ServiceUserNameToAliasStoreFileName(userName);
      if (ServiceFileUnlinkFile(aliasFilename)) {
         /* XXX not much to do -- ServiceFileUnlinkFile() spewed error */
      }
      Debug("%s: removed empty alias file '%s'\n", __FUNCTION__, aliasFilename);
      g_free(aliasFilename);
      goto updateMap;
   }

   /*
    * Make a tmpfile for the new alias file, so we can
    * recover if something goes wrong.
    */
   tmpAliasFilename = ServiceUserNameToTmpAliasStoreFileName(userName);

#ifdef _WIN32
   /*
    * Create file with the right permissions.
    * It seemed simpler to let glib open the file then set the permission.
    * However, that would open a security hole, where malicious user
    * could have a window to modify the file.
    */
   {
      UserAccessControl uac;
      /* The default access only allows self and administrators */
      if (!UserAccessControl_Default(&uac)) {
         err = VGAUTH_E_FAIL;
         goto cleanup;
      }

      fd = ServiceFileWinMakeTempfile(&tmpAliasFilename, &uac);
      UserAccessControl_Destroy(&uac);
   }
#else
   fd = ServiceFilePosixMakeTempfile(tmpAliasFilename,
                                     ALIASSTORE_FILE_PERMS);
#endif

   if (fd < 0) {
      err = VGAUTH_E_FAIL;
      goto cleanup;
   }

   /*
    * We want to be sure this file (and the mapping below) is flushed
    * to disk asap, to minmize file corruption issues due to unexpected
    * OS failure.
    *
    * fflush()/fsync() should do it on Linux, but Windows wants a M$-private
    * magic flag plus fflush().  Windows flags are:
    * - 'w' write
    * - 'b' binary (don't do CR/LF translation)
    * - 'c' commit to disk on fflush or _flushall
    *
    * Another glib-ish option would be to use g_set_file_contents(),
    * which also uses fsync() when it can.  However, this doesn't
    * seem to use the 'c'/fflush for Windows, so it appears to be
    * a poor option.  Also, it uses its own tmpfile which it sets
    * to mode 0666.
    */
#ifdef WIN32
   fp = fdopen(fd, "wbc");
#else
   fp = fdopen(fd, "w");
#endif
   if (NULL == fp) {
      VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
      Warning("%s: fdopen() failed\n", __FUNCTION__);
#ifdef _WIN32
      _close(fd);
#else
      close(fd);
#endif
      goto cleanup;
   }
   err = AliasDumpAliases(fp, num, aList);
   if (err != VGAUTH_E_OK) {
      goto cleanup;
   }

   if (fflush(fp) != 0) {
      VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
      Warning("%s: fflush() failed\n", __FUNCTION__);
      goto cleanup;
   }
#ifndef WIN32
   if (fsync(fileno(fp)) != 0) {
      VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
      Warning("%s: fsync() failed\n", __FUNCTION__);
      goto cleanup;
   }
#endif
   rc = fclose(fp);
   fp = NULL;
   if (rc != 0) {
      VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
      Warning("%s: fclose() failed\n", __FUNCTION__);
      goto cleanup;
   }

   /*
    * Make the file owned by the user so they can hand-edit.
    * Note that this may fail for a couple reasons -- the user
    * may have been deleted, or we just can't look it up because
    * of a network issue when using LDAP/NIS/etc.
    *
    * If the user is deleted, we could just leave the file owned
    * by SUPERUSER.  But the user may just be inaccesible, so copy
    * the perms from the current version of the file.
    */

#ifdef _WIN32
   /*
    * On Windows, the cert files are owned by the Administrators group.
    * We only need to grant the user in the ACL.
    */
   if (UsercheckUserExists(userName)) {
      UserAccessControl uac;
      BOOL ok;

      if (!UserAccessControl_GrantUser(&uac, userName, GENERIC_ALL)) {
         err = VGAUTH_E_FAIL;
         goto cleanup;
      }

      ok = WinUtil_SetFileAcl(tmpAliasFilename,
                              UserAccessControl_GetAcl(&uac));
      UserAccessControl_Destroy(&uac);

      if (!ok) {
         err = VGAUTH_E_FAIL;
         goto cleanup;
      }

   } else {
      BOOL ok;
      gchar *origAliasFilename = ServiceUserNameToAliasStoreFileName(userName);

      ok = WinUtil_CopyFileAcl(origAliasFilename, tmpAliasFilename);
      g_free(origAliasFilename);

      if (!ok) {
         err = VGAUTH_E_FAIL;
         goto cleanup;
      }

   }
#else
   /*
    * On Linux we have to set the ownership of the file to be the user.
    */

   if (UsercheckUserExists(userName)) {
      err = ServiceFileSetOwner(tmpAliasFilename, userName);
      if (err != VGAUTH_E_OK) {
         goto cleanup;
      }
   } else {
      gchar *origAliasFilename = ServiceUserNameToAliasStoreFileName(userName);

      err = ServiceFileCopyOwnership(origAliasFilename, tmpAliasFilename);
      g_free(origAliasFilename);

      if (VGAUTH_E_OK != err) {
         goto cleanup;
      }
   }
#endif

updateMap:
   if (updateMap) {
      /*
       * Special case for empty map files -- if its empty, just remove it.
       */
      if (numMapped == 0) {
         gchar *mapFilename = g_strdup_printf("%s"DIRSEP"%s",
                                              aliasStoreRootDir,
                                              ALIASSTORE_MAPFILE_NAME);
         if (ServiceFileUnlinkFile(mapFilename)) {
            /* XXX not much to do -- ServiceFileUnlinkFile() spewed error */
         }
         Debug("%s: removed empty map file '%s'\n", __FUNCTION__, mapFilename);
         g_free(mapFilename);

         if (emptyAliasFile) {
            goto done;
         }
         goto rename;
      }

      tmpMapFilename = g_strdup_printf("%s"DIRSEP"%sXXXXXX",
                                       aliasStoreRootDir,
                                       ALIASSTORE_MAPFILE_NAME);
#ifdef _WIN32
      {
         UserAccessControl uac;
         /* The default access only allows self and administrators */
         if(!UserAccessControl_Default(&uac)) {
            err = VGAUTH_E_FAIL;
            goto cleanup;
         }
         fd = ServiceFileWinMakeTempfile(&tmpMapFilename, &uac);
         UserAccessControl_Destroy(&uac);
      }
#else
      fd = ServiceFilePosixMakeTempfile(tmpMapFilename,
                                        ALIASSTORE_MAPFILE_PERMS);
#endif

      if (fd < 0) {
         err = VGAUTH_E_FAIL;
         goto cleanup;
      }

#ifdef WIN32
      fp = fdopen(fd, "wbc");
#else
      fp = fdopen(fd, "w");
#endif
      if (NULL == fp) {
         VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
         Warning("%s: fdopen() failed\n", __FUNCTION__);
#ifdef _WIN32
         _close(fd);
#else
         close(fd);
#endif
         goto cleanup;
      }
      err = AliasDumpMappedAliasesFile(fp, numMapped, maList);
      if (err != VGAUTH_E_OK) {
         goto cleanup;
      }

      if (fflush(fp) != 0) {
         VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
         Warning("%s: fflush() failed\n", __FUNCTION__);
         goto cleanup;
      }
#ifndef WIN32
      if (fsync(fileno(fp)) != 0) {
         VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
         Warning("%s: fsync() failed\n", __FUNCTION__);
         goto cleanup;
      }
#endif
      rc = fclose(fp);
      fp = NULL;
      if (rc != 0) {
         VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
         Warning("%s: fclose() failed\n", __FUNCTION__);
         goto cleanup;
      }

#ifdef _WIN32
      /*
       * On Windows, the map file is owned by the Administrators group.
       * We make it world readable to match the Unix group/other readable
       * permission.
       */
      {
         UserAccessControl uac;
         BOOL ok;

         if (!UserAccessControl_GrantEveryone(&uac, GENERIC_READ)) {
            err = VGAUTH_E_FAIL;
            goto cleanup;
         }

         ok = WinUtil_SetFileAcl(tmpMapFilename,
                                 UserAccessControl_GetAcl(&uac));
         UserAccessControl_Destroy(&uac);

         if (!ok) {
            err = VGAUTH_E_FAIL;
            goto cleanup;
         }
      }
#endif
   }

rename:
   /*
    * Make the tmpfiles become the real in a way we can try to recover from.
    */
   err = AliasSafeRenameFiles(tmpAliasFilename, tmpMapFilename, userName);
   if (err != VGAUTH_E_OK) {
      goto cleanup;
   }

   goto done;

cleanup:
   if (fp != NULL) {
      fclose(fp);
   }

   /*
    * If we fail somwhere, all we can do is try to clean up.
    * If this fails, there's nothing we can do.
    */
   if (ServiceFileUnlinkFile(tmpAliasFilename)) {
      /* XXX not much to do -- ServiceFileUnlinkFile() spewed error */
   }
   if (tmpMapFilename && ServiceFileUnlinkFile(tmpMapFilename)) {
      /* XXX not much to do -- ServiceFileUnlinkFile() spewed error */
   }

done:
   g_free(tmpAliasFilename);
   g_free(tmpMapFilename);
   return err;
}


/*
 ******************************************************************************
 * ServiceAliasCopyAliasInfoContents --                                  */ /**
 *
 * Copies the contents of a ServiceAliasInfo *.
 *
 * @param[in]   src    The source ServiceAliasInfo.
 * @param[in]   dst    The dest ServiceAliasInfo.
 ******************************************************************************
 */

void
ServiceAliasCopyAliasInfoContents(const ServiceAliasInfo *src,
                                  ServiceAliasInfo *dst)
{
   dst->type = src->type;
   dst->name = g_strdup(src->name);
   dst->comment = g_strdup(src->comment);
}


/*
 ******************************************************************************
 * ServiceAliasFreeAliasInfoContents --                                  */ /**
 *
 * Frees the contents of a ServiceAliasInfo *.
 *
 * @param[in]   si     The ServiceAliasInfo to free.
 ******************************************************************************
 */

void
ServiceAliasFreeAliasInfoContents(ServiceAliasInfo *ai)
{
   g_free(ai->name);
   g_free(ai->comment);
}


/*
 ******************************************************************************
 * ServiceAliasFreeAliasInfo --                                          */ /**
 *
 * Frees a ServiceAliasInfo *.
 *
 * @param[in]   ai     The ServiceAliasInfo to free.
 ******************************************************************************
 */

void
ServiceAliasFreeAliasInfo(ServiceAliasInfo *ai)
{
   if (NULL == ai) {
      return;
   }
   ServiceAliasFreeAliasInfoContents(ai);

   g_free(ai);
}


/*
 ******************************************************************************
 * ServiceAliasFreeAliasListContents --                                  */ /**
 *
 * Frees the contents of a ServiceAlias.
 *
 * @param[in]   sa     The ServiceAlias.
 ******************************************************************************
 */

void
ServiceAliasFreeAliasListContents(ServiceAlias *sa)
{
   int i;

   g_free(sa->pemCert);
   for (i = 0; i < sa->num; i++) {
      g_free(sa->infos[i].name);
      g_free(sa->infos[i].comment);
   }
   g_free(sa->infos);
}


/*
 ******************************************************************************
 * ServiceAliasFreeAliasList --                                          */ /**
 *
 * Frees an array of ServiceAlias.
 *
 * @param[in]   num     The size of the array.
 * @param[in]   aList   The list of ServiceAlias.
 ******************************************************************************
 */

void
ServiceAliasFreeAliasList(int num,
                          ServiceAlias *aList)
{
   int i;

   for (i = 0; i < num; i++) {
      ServiceAliasFreeAliasListContents(&(aList[i]));
   }
   g_free(aList);
}


/*
 ******************************************************************************
 * ServiceAliasFreeMappedAliasListContents --                            */ /**
 *
 * Frees the contents of a ServiceMappedAlias.
 *
 * @param[in]   ma      The ServiceMappedAlias.
 ******************************************************************************
 */

void
ServiceAliasFreeMappedAliasListContents(ServiceMappedAlias *ma)
{
   int i;

   g_free(ma->pemCert);
   for (i = 0; i < ma->num; i++) {
      if (SUBJECT_TYPE_NAMED == ma->subjects[i].type) {
         g_free(ma->subjects[i].name);
      }
   }
   g_free(ma->subjects);
   g_free(ma->userName);
}


/*
 ******************************************************************************
 * ServiceAliasFreeMappedAliasList --                                    */ /**
 *
 * Frees an array of ServiceMappedAlias.
 *
 * @param[in]   num     The size of the array.
 * @param[in]   ma      The list of ServiceMappedAlias.
 ******************************************************************************
 */

void
ServiceAliasFreeMappedAliasList(int num,
                                ServiceMappedAlias *ma)
{
   int i;

   for (i = 0; i < num; i++) {
      ServiceAliasFreeMappedAliasListContents(&(ma[i]));
   }
   g_free(ma);
}


/*
 ******************************************************************************
 * ServiceAliasAddAlias --                                               */ /**
 *
 * Adds a certificate and AliasInfo to userName's store.
 *
 * @param[in]   reqUserName     The user making the request.
 * @param[in]   userName        The user whose store is to be used.
 * @param[in]   addMapped       Set if a link is also to be made in the
 *                              mapping file.
 * @param[in]   pemCert         The cert to be stored.
 * @param[in]   ai              The associated AliasInfo.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceAliasAddAlias(const gchar *reqUserName,
                     const gchar *userName,
                     gboolean addMapped,
                     const gchar *pemCert,
                     ServiceAliasInfo *ai)
{
   VGAuthError err;
   int num;
   ServiceAlias *aList = NULL;
   ServiceAliasInfo *newAi;
   int numMapped = 0;
   ServiceMappedAlias *maList = NULL;
   gboolean updatedMapList = FALSE;
   int matchCertIdx = -1;
   int i;
   int j;

   if (!UsercheckUserExists(userName)) {
      Debug("%s: no such user '%s'\n", __FUNCTION__, userName);
      return VGAUTH_E_NO_SUCH_USER;
   }

   if (!CertVerify_IsWellFormedPEMCert(pemCert)) {
      return VGAUTH_E_INVALID_CERTIFICATE;
   }

   /*
    * Load any that already exist.
    */
   err = AliasLoadAliases(userName, &num, &aList);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   /*
    * Do a dup check.  This may be overly simplistic, and it can be
    * tricked by whitespace changes in some situations.
    */
   for (i = 0; i < num; i++) {
      if (ServiceComparePEMCerts(pemCert, aList[i].pemCert)) {
         matchCertIdx = i;
         for (j = 0; j < aList[i].num; j++) {
            if (ServiceAliasIsSubjectEqual(aList[i].infos[j].type, ai->type,
                                           aList[i].infos[j].name, ai->name)) {
               Debug("%s: client tried to add a duplicate subject '%s' for user '%s'\n",
                     __FUNCTION__,
                     (ai->type == SUBJECT_TYPE_ANY) ? "<ANY>" : ai->name,
                     userName);
               // this is a no-op, but we may have a mapFile change
               err = VGAUTH_E_OK;
               if (addMapped) {
                  goto check_map;
               } else {
                  goto done;
               }
            }
         }
      }
   }

   /*
    * If the cert already exists, just add the subject to it.
    */
   if (matchCertIdx != -1) {
      aList[matchCertIdx].num++;
      aList[matchCertIdx].infos = g_realloc_n(aList[matchCertIdx].infos,
                                              aList[matchCertIdx].num,
                                              sizeof(ServiceAliasInfo));
      newAi = &(aList[matchCertIdx].infos[aList[matchCertIdx].num - 1]);
      newAi->type = ai->type;
      newAi->name = g_strdup(ai->name);
      newAi->comment = g_strdup(ai->comment);
   } else {
      /*
       * If its a new cert, add the incoming cert and subject to our list.
       */
      aList = g_realloc_n(aList, num + 1, sizeof(ServiceAlias));
      aList[num].pemCert = g_strdup(pemCert);
      aList[num].num = 1;
      aList[num].infos = g_malloc0(sizeof(ServiceAliasInfo));
      newAi = &(aList[num].infos[0]);
      num++;
      newAi->type = ai->type;
      newAi->name = g_strdup(ai->name);
      newAi->comment = g_strdup(ai->comment);
   }

check_map:
   if (addMapped) {
      err = AliasLoadMapped(&numMapped, &maList);

      /*
       * Do a dup check -- be sure the cert/subject combo
       * doesn't already exist.
       */
      for (i = 0; i < numMapped; i++) {
         if (ServiceComparePEMCerts(pemCert, maList[i].pemCert))  {
            for (j = 0; j < maList[i].num; j++) {
               if (ServiceAliasIsSubjectEqual(maList[i].subjects[j].type, ai->type,
                                              maList[i].subjects[j].name, ai->name)) {
                  Debug("%s: client tried to add a duplicate mapping entry for "
                        "subject '%s' and cert '%s'\n",
                        __FUNCTION__,
                        (ai->type == SUBJECT_TYPE_ANY) ? "<ANY>" : ai->name,
                        maList[i].pemCert);
                  err = VGAUTH_E_MULTIPLE_MAPPINGS;
                  goto done;
               }
            }
            // if its a new subject for the same user, add it
            if (g_strcmp0(maList[i].userName, userName) == 0) {
               // additional subject for cert/username pair
               maList[i].num++;
               maList[i].subjects = g_realloc_n(maList[i].subjects,
                                                maList[i].num,
                                                sizeof(ServiceSubject));
               maList[i].subjects[maList[i].num - 1].type = ai->type;
               maList[i].subjects[maList[i].num - 1].name = g_strdup(ai->name);
               updatedMapList = TRUE;
            }
         }
      }

      /*
       * If new, add it to the array.
       */
      if (!updatedMapList) {
         maList = g_realloc_n(maList,
                              numMapped + 1,
                              sizeof(ServiceMappedAlias));
         maList[numMapped].pemCert = g_strdup(pemCert);
         maList[numMapped].userName = g_strdup(userName);
         maList[numMapped].num = 1;
         maList[numMapped].subjects = g_malloc0(sizeof(ServiceSubject));
         maList[numMapped].subjects[0].type = ai->type;
         maList[numMapped].subjects[0].name = g_strdup(ai->name);
         numMapped++;
      }
   }

   err = AliasSaveAliasesAndMapped(userName, num, aList,
                                   addMapped, numMapped, maList);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to save Aliases\n", __FUNCTION__);
   } else {
      Audit_Event(TRUE,
                 SU_(alias.addid,
                     "Alias added to Alias store owned by '%s' by user '%s'"),
                 userName, reqUserName);
      // security -- don't expose username
      VMXLog_Log(VMXLOG_LEVEL_WARNING,
                 "%s: alias added with Subject '%s'",
                 __FUNCTION__,
                 (ai->type == SUBJECT_TYPE_ANY) ? "<ANY>" : ai->name);
   }

done:

   ServiceAliasFreeAliasList(num, aList);
   if (addMapped) {
      ServiceAliasFreeMappedAliasList(numMapped, maList);
   }

   return err;
}


/*
 ******************************************************************************
 * AliasShrinkAliasList --                                               */ /**
 *
 * Helper function for shrinking a ServiceAlias list.
 *
 * @param[in]    aList          The list of ServiceAliases.
 * @param[in]    num            The new size.
 * @param[in]    idx            Where to start the shrink.
 ******************************************************************************
 */

static void
AliasShrinkAliasList(ServiceAlias *aList,
                     int num,
                     int idx)
{
   ASSERT(idx <= num);

   memmove(&(aList[idx]), &(aList[idx+1]), sizeof(ServiceAlias) *
                          (num - idx));
#ifdef VMX86_DEBUG
   memset(&(aList[num]), '\0', sizeof(ServiceAlias));
#endif
}


/*
 ******************************************************************************
 * AliasShrinkMapList --                                                 */ /**
 *
 * Helper function for shrinking a ServiceMappedAlias list.
 *
 * @param[in]    maList         The list of ServiceMappedAlias.
 * @param[in]    num            The new size.
 * @param[in]    idx            Where to start the shrink.
 ******************************************************************************
 */

static void
AliasShrinkMapList(ServiceMappedAlias *maList,
                   int num,
                   int idx)
{
   ASSERT(idx <= num);

   memmove(&(maList[idx]), &(maList[idx+1]), sizeof(ServiceMappedAlias) *
                          (num - idx));
#ifdef VMX86_DEBUG
   memset(&(maList[num]), '\0', sizeof(ServiceMappedAlias));
#endif
}


/*
 ******************************************************************************
 * ServiceAliasRemoveAlias --                                            */ /**
 *
 * Removes a cert/Subject from userName's store.
 *
 * @param[in]   reqUserName     The user making the request.
 * @param[in]   userName        The user whose store is to be used.
 * @param[in]   pemCert         The cert to be removed in PEM format.
 * @param[in]   subj            The subject.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceAliasRemoveAlias(const gchar *reqUserName,
                        const gchar *userName,
                        const gchar *pemCert,
                        ServiceSubject *subj)
{
   VGAuthError err;
   VGAuthError savedErr = VGAUTH_E_OK;
   int numIds;
   ServiceAlias *aList = NULL;
   gboolean removeAll = (subj->type == SUBJECT_TYPE_UNSET);
   int numMapped = 0;
   ServiceMappedAlias *maList = NULL;
   ServiceAliasInfo *ai;
   ServiceSubject *s;
   int i;
   int j;
   int userIdIdx = -1;
   int userSubjIdx = -1;
   gboolean updateMap = FALSE;

   /*
    * We don't verify the user exists in a Remove operation, to allow
    * cleanup of deleted user's stores, but we do check whether the
    * user name is legal or not.
    */
   if (!Usercheck_UsernameIsLegal(userName)) {
      Warning("%s: Illegal user name '%s'\n", __FUNCTION__, userName);
      return VGAUTH_E_FAIL;
   }

   if (!CertVerify_IsWellFormedPEMCert(pemCert)) {
      return VGAUTH_E_INVALID_CERTIFICATE;
   }

   /*
    * Load user's store.
    */
   err = AliasLoadAliases(userName, &numIds, &aList);
   if (VGAUTH_E_OK != err) {
      return err;
   }

   if (removeAll) {
      for (i = 0; i < numIds; i++) {
         if (ServiceComparePEMCerts(pemCert, aList[i].pemCert)) {
            userIdIdx = i;
            break;
         }
      }
      // nothing found
      if (-1 == userIdIdx) {
         err = VGAUTH_E_INVALID_ARGUMENT;
         goto done;
      }

      ServiceAliasFreeAliasListContents(&(aList[userIdIdx]));
      numIds--;
      AliasShrinkAliasList(aList, numIds, userIdIdx);
   } else {
      // removing a specific subject
      for (i = 0; i < numIds; i++) {
         if (ServiceComparePEMCerts(pemCert, aList[i].pemCert)) {
            userIdIdx = i;
            for (j = 0; j < aList[i].num; j++) {
               ai = &(aList[i].infos[j]);
               if (ServiceAliasIsSubjectEqual(ai->type, subj->type,
                                              ai->name, subj->name)) {
                  userSubjIdx = j;
                  break;
               }
            }

            if (userSubjIdx >= 0) {
               g_free(aList[userIdIdx].infos[userSubjIdx].name);
               g_free(aList[userIdIdx].infos[userSubjIdx].comment);
               aList[userIdIdx].num--;
               ASSERT(aList[userIdIdx].num >= 0);
               memmove(&(aList[userIdIdx].infos[userSubjIdx]),
                       &(aList[userIdIdx].infos[userSubjIdx+1]),
                       sizeof(ServiceAliasInfo) *
                              (aList[userIdIdx].num - userSubjIdx));
#ifdef VMX86_DEBUG
               memset(&(aList[userIdIdx].infos[aList[userIdIdx].num]), '\0',
                         sizeof(ServiceAliasInfo));
#endif
            } else {
               err = VGAUTH_E_INVALID_ARGUMENT;
               goto done;
            }
            // if all the subjects are gone, toss the whole thing
            if (aList[i].num == 0) {
               numIds--;
               ServiceAliasFreeAliasListContents(&(aList[i]));
               AliasShrinkAliasList(aList, numIds, userIdIdx);
            }
            break;
         }
      }
      if (-1 == userIdIdx) {
         /*
          * No match, but continue on through the mapped code
          * in case we have a orphaned mapped alias from a buggy
          * earlier version.
          */
         savedErr = VGAUTH_E_INVALID_ARGUMENT;
      }
   }

   /*
    * Now clear out any mapped alias.  This may fail to find a match.
    */
   err = AliasLoadMapped(&numMapped, &maList);
   if (VGAUTH_E_OK != err) {
      goto done;
   }

   if (numMapped == 0) {
      goto update;
   }

   if (removeAll) {
      userIdIdx = -1;
      for (i = 0; i < numMapped; i++) {
         if (ServiceComparePEMCerts(pemCert, maList[i].pemCert) &&
             (g_strcmp0(userName, maList[i].userName) == 0)) {
            userIdIdx = i;
            break;
         }
      }
      // nothing found -- not an error for mapping, but be sure to update
      if (-1 == userIdIdx) {
         goto update;
      }

      ServiceAliasFreeMappedAliasListContents(&(maList[userIdIdx]));
      numMapped--;
      AliasShrinkMapList(maList, numMapped, userIdIdx);
      updateMap = TRUE;
   } else {
      // removing a specific subject
      for (i = 0; i < numMapped; i++) {
         userIdIdx = -1;
         userSubjIdx = -1;
         if (ServiceComparePEMCerts(pemCert, maList[i].pemCert)) {
            userIdIdx = i;
            for (j = 0; j < maList[i].num; j++) {
               s = &(maList[i].subjects[j]);
               if (ServiceAliasIsSubjectEqual(s->type, subj->type,
                                              s->name, subj->name)) {
                  userSubjIdx = j;
                  break;
               }
            }

            if (userSubjIdx >= 0) {
               g_free(maList[userIdIdx].subjects[userSubjIdx].name);
               maList[userIdIdx].num--;
               ASSERT(maList[userIdIdx].num >= 0);
               memmove(&(maList[userIdIdx].subjects[userSubjIdx]),
                       &(maList[userIdIdx].subjects[userSubjIdx+1]),
                       sizeof(ServiceSubject) *
                              (maList[userIdIdx].num - userSubjIdx));
#ifdef VMX86_DEBUG
               memset(&(maList[userIdIdx].subjects[maList[userIdIdx].num]), '\0',
                         sizeof(ServiceSubject));
#endif
               // if all the subjects are gone, toss the whole thing
               if (maList[i].num == 0) {
                  numMapped--;
                  ServiceAliasFreeMappedAliasListContents(&(maList[i]));
                  AliasShrinkMapList(maList, numMapped, userIdIdx);
               }
               updateMap = TRUE;
               break;
            }
         }
      }
   }

update:
   err = AliasSaveAliasesAndMapped(userName, numIds, aList,
                                   updateMap, numMapped, maList);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to save Aliases\n", __FUNCTION__);
   } else {
      Audit_Event(TRUE,
                 SU_(alias.removeid,
                     "Alias removed from Alias store owned by '%s' by user '%s'"),
                 userName, reqUserName);
      if (removeAll) {
         // security -- don't expose username
         VMXLog_Log(VMXLOG_LEVEL_WARNING,
                    "%s: all aliases removed for requested username",
                    __FUNCTION__);
      } else {
         // security -- don't expose username
         VMXLog_Log(VMXLOG_LEVEL_WARNING,
                    "%s: alias removed with Subject '%s'",
                    __FUNCTION__,
                    (subj->type == SUBJECT_TYPE_ANY) ? "<ANY>" : subj->name);
      }
   }

done:
   ServiceAliasFreeAliasList(numIds, aList);
   ServiceAliasFreeMappedAliasList(numMapped, maList);

   /*
    * If we couldn't find the alias, but dropped though to the mapped
    * code anyways to catch orphans, return that error.
    */
   if (savedErr != VGAUTH_E_OK) {
      return savedErr;
   } else {
      return err;
   }
}


/*
 ******************************************************************************
 * ServiceAliasQueryAliases --                                           */ /**
 *
 * Queries all aliases from userName's store.
 *
 * @param[in]   userName        The user whose store is to be used.
 * @param[out]  num             The number of entries being returned.
 * @param[out]  aList           The entries being returned.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceAliasQueryAliases(const gchar *userName,
                         int *num,
                         ServiceAlias **aList)
{
   VGAuthError err;

   *num = 0;
   *aList = NULL;

#if 0
   /*
    * XXX
    * This is tricky.  The first reaction is that we should return an error.
    * But this breaks the "admin cleaning up after a deleted user"
    * scenario.  See bug 920481.
    *
    * One possible compromise is to return VGAUTH_E_NO_SUCH_USER
    * IFF there's no data, but that feels sleazy too.
    */
   if (!UsercheckUserExists(userName)) {
      Debug("%s: no such user '%s'\n", __FUNCTION__, userName);
      return VGAUTH_E_NO_SUCH_USER;
   }
#endif

   /*
    * We don't verify the user exists in a Query operation to allow
    * cleaning up after a deleted user, but we do check whether the
    * user name is legal or not.
    */
   if (!Usercheck_UsernameIsLegal(userName)) {
      Warning("%s: Illegal user name '%s'\n", __FUNCTION__, userName);
      return VGAUTH_E_FAIL;
   }

   err = AliasLoadAliases(userName, num, aList);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to load Aliases for '%s'\n", __FUNCTION__, userName);
   }

   return err;
}


/*
 ******************************************************************************
 * ServiceAliasQueryMappedAliases --                                     */ /**
 *
 * Returns the contents of the mapping file.
 *
 * @param[out]  num             The number of entries being returned.
 * @param[out]  maList          The ServiceMappedAliases being returned.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceAliasQueryMappedAliases(int *num,
                               ServiceMappedAlias **maList)
{
   VGAuthError err;

   *num = 0;
   *maList = NULL;

   err = AliasLoadMapped(num, maList);
   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to load mapped aliases\n", __FUNCTION__);
   }

   return err;
}


/*
 ******************************************************************************
 * ServiceIDVerifyStoreContents --                                       */ /**
 *
 * Looks at every file in the alias store, validating ownership and permissions.
 * If a file fails the validation check, its renamed to file.bad.
 *
 * If we fail to rename a bad file, the function returns an error.
 *
 * XXX add contents/XML confidence check too?
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
ServiceIDVerifyStoreContents(void)
{
   VGAuthError err;
   GDir *dir;
   GError *gErr;
   const gchar *fileName;
   gboolean saveBadFile = FALSE;

   dir = g_dir_open(aliasStoreRootDir, 0, &gErr);
   if (NULL == dir) {
      Warning("%s: failed to open alias store %s: %s\n",
              __FUNCTION__, aliasStoreRootDir, gErr->message);
      g_error_free(gErr);
      return VGAUTH_E_FAIL;
   }

   while ((fileName = g_dir_read_name(dir)) != NULL) {
      gchar *fullFileName = g_strdup_printf("%s"DIRSEP"%s",
                                            aliasStoreRootDir,
                                            fileName);
      // mapping file is special
      if (g_strcmp0(ALIASSTORE_MAPFILE_NAME, fileName) == 0) {
         err = AliasCheckMapFilePerms(fullFileName);
         if (VGAUTH_E_OK != err) {
            saveBadFile = TRUE;
         }
      } else if (g_str_has_prefix(fileName, ALIASSTORE_FILE_PREFIX) &&
                 g_str_has_suffix(fileName, ALIASSTORE_FILE_SUFFIX)) {
         gchar *userName;
         gchar *startUserName;
         gchar *suffix;
         gchar *decodedUserName;

         startUserName = g_strdup(fileName);
         userName = startUserName;
         userName += strlen(ALIASSTORE_FILE_PREFIX);
         suffix = g_strrstr(userName, ALIASSTORE_FILE_SUFFIX);
         ASSERT(suffix);
         *suffix = '\0';

         decodedUserName = ServiceDecodeUserName(userName);
         err = AliasCheckAliasFilePerms(fullFileName, decodedUserName);
         if (VGAUTH_E_OK != err) {
            saveBadFile = TRUE;
         }
         g_free(decodedUserName);
         g_free(startUserName);
      }

      if (saveBadFile) {
         gchar *badFileName = g_strdup_printf("%s.bad",
                                              fullFileName);
         err = ServiceFileRenameFile(fullFileName, badFileName);
         if (VGAUTH_E_OK != err) {
            Audit_Event(FALSE,
                        SU_(alias.alias.renamefail,
                            "Failed to rename suspect Alias store '%s' to '%s'"),
                        fullFileName, badFileName);
            /*
             * XXX the best way to handle this would be to add it to
             * a list of prohibited bad files and keep going.  but that's
             * a lot of risky work that's very hard to test, so punt for now.
             */
            g_free(badFileName);
            g_free(fullFileName);
            g_dir_close(dir);
            return VGAUTH_E_FAIL;
         } else {
            Audit_Event(TRUE,
                        SU_(alias.alias.rename,
                            "Suspect Alias store '%s' renamed to '%s'"),
                        fullFileName, badFileName);
         }
         g_free(badFileName);
         saveBadFile = FALSE;
      }
      g_free(fullFileName);
   }

   g_dir_close(dir);

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * ServiceValidateAliases --                                             */ /**
 *
 * Looks at the alias store, and flags any orphaned mapped alias (have no
 * associated per-user alias) that could have been left by bugs in
 * previous releases.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
ServiceValidateAliases(void)
{
   VGAuthError err;
   int numMapped = 0;
   ServiceMappedAlias *maList = NULL;
   int numIds;
   ServiceAlias *aList = NULL;
   ServiceSubject *mappedSubj;
   ServiceSubject *badSubj;
   ServiceAliasInfo *ai;
   int i;
   int j;
   int k;
   int l;
   gboolean foundMatch;

   err = AliasLoadMapped(&numMapped, &maList);
   if (VGAUTH_E_OK != err) {
      goto done;
   }

   if (numMapped == 0) {
      err = VGAUTH_E_OK;
      goto done;
   }

   for (i = 0; i < numMapped; i++) {
      foundMatch = FALSE;
      badSubj = NULL;
      err = AliasLoadAliases(maList[i].userName, &numIds, &aList);
      if (err != VGAUTH_E_OK) {
         Warning("%s: Failed to load alias for user '%s'\n",
                 __FUNCTION__, maList[i].userName);
         continue;
      }

      // iterate over subjects
      for (j = 0; j < maList[i].num; j++) {
         mappedSubj = &(maList[i].subjects[j]);
         badSubj = mappedSubj;
         for (k = 0; k < numIds; k++) {
            if (ServiceComparePEMCerts(maList[i].pemCert, aList[k].pemCert)) {
               for (l = 0; l < aList[k].num; l++) {
                  ai = &(aList[k].infos[l]);
                  if (ServiceAliasIsSubjectEqual(mappedSubj->type, ai->type,
                                              mappedSubj->name, ai->name)) {
                     foundMatch = TRUE;
                     badSubj = NULL;
                     goto next;
                  }
               }
            }
         }
      }
next:
      ServiceAliasFreeAliasList(numIds, aList);
      if (!foundMatch) {
         /* badSubj should always have a value because maList won't be empty */
         /* coverity[var_deref_op] */
         Warning("%s: orphaned mapped alias: user %s subj %s cert %s\n",
                 __FUNCTION__, maList[i].userName,
                 (badSubj->type == SUBJECT_TYPE_NAMED ? badSubj->name : "ANY"),
                 maList[i].pemCert);
#if 0
         /*
          * This could clear the orphaned alias, but a) that could
          * confuse a user and b) if its buggy, its made things worse.
          */
         err = ServiceAliasRemoveAlias("SERVICE-SANITY-CHECK",
                                        maList[i].userName,
                                        maList[i].pemCert,
                                        badSubj);
#endif
      }
   }

done:
   ServiceAliasFreeMappedAliasList(numMapped, maList);

   return err;
}


/*
 ******************************************************************************
 * ServiceAliasInitAliasStore --                                         */ /**
 *
 * Initializes the alias store, creating directory tree.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceAliasInitAliasStore(void)
{
   int ret;
   VGAuthError err = VGAUTH_E_OK;
   gboolean saveBadDir = FALSE;
   char *defaultDir = NULL;
   size_t len;

#ifdef _WIN32
   {
      WCHAR pathW[MAX_PATH];
      HRESULT hRes;

      hRes = SHGetFolderPathW(NULL,
                              CSIDL_APPDATA,
                              NULL,
                              0,
                              pathW);
      if (hRes == S_OK) {
         char *pathA = Convert_Utf16ToUtf8(__FUNCTION__,
                                           __FILE__, __LINE__,
                                           pathW);
         if (NULL == pathA) {
            Warning("%s: out of memory converting filePath\n", __FUNCTION__);
            return VGAUTH_E_FAIL;
         }
         defaultDir = g_strdup_printf("%s"DIRSEPS"%s",
                                      pathA, ALIAS_STORE_REL_DIRECTORY);
         g_free(pathA);
      } else {
         defaultDir = g_strdup(DEFAULT_ALIASSTORE_ROOT_DIR);
      }
   }
#else
   defaultDir = g_strdup(DEFAULT_ALIASSTORE_ROOT_DIR);
#endif

   allowSymlinks = Pref_GetBool(gPrefs,
                                VGAUTH_PREF_ALLOW_SYMLINKS,
                                VGAUTH_PREF_GROUP_NAME_SERVICE,
                                FALSE);
   /*
    * Find the alias store directory.  This allows an installer to put
    * it somewhere else if necessary.
    *
    * Note that this pref is not re-read on a signal unlike others.
    * It should be a one-off at startup, and doesn't need to be dynamically
    * changeable.
    */
   aliasStoreRootDir = Pref_GetString(gPrefs,
                                      VGAUTH_PREF_ALIASSTORE_DIR,
                                      VGAUTH_PREF_GROUP_NAME_SERVICE,
                                      defaultDir);

   /*
    * Remove the trailing separator if any from aliasStoreRootDir path.
    */
   len = strlen(aliasStoreRootDir);
   if (ISPATHSEP(aliasStoreRootDir[len - 1])) {
      aliasStoreRootDir[len - 1] = '\0';
   }

   Log("Using '%s' for alias store root directory\n", aliasStoreRootDir);

   g_free(defaultDir);

   /*
    * Security check -- if the alias store already exists, be sure
    * the file perms are correct.  If not, we may have been hacked,
    * so throw an audit event.
    *
    * If we think the directory is bad, save it off if we can,
    * but keep going so that tickets can still be used.
    *
    */
   if (g_file_test(aliasStoreRootDir, G_FILE_TEST_EXISTS)) {

      /*
       * g_file_test() on a symlink will return true for IS_DIR
       * if it's a link pointing to a directory.  Seems pretty bogus to me,
       * but they use stat() instead of lstat().
       */
      if (g_file_test(aliasStoreRootDir, G_FILE_TEST_IS_DIR) &&
          !g_file_test(aliasStoreRootDir, G_FILE_TEST_IS_SYMLINK)) {
#ifdef _WIN32
         err = ServiceFileVerifyAdminGroupOwned(aliasStoreRootDir);
#else
         err = ServiceFileVerifyFileOwnerAndPerms(aliasStoreRootDir,
                                                  SUPERUSER_NAME,
                                                  ALIASSTORE_DIR_PERMS,
                                                  NULL, NULL);
#endif
         if (VGAUTH_E_OK != err) {
            Audit_Event(FALSE,
                        SU_(alias.dir.badperm,
                            "Alias store directory '%s' has incorrect owner or "
                            "permissions.  "
                            "Any Aliases currently stored in '%s' will not be "
                            "available for authentication"),
                        aliasStoreRootDir, aliasStoreRootDir);
            saveBadDir = TRUE;
         }
         err = ServiceIDVerifyStoreContents();
         if (VGAUTH_E_OK != err) {
            Warning("%s: alias store had invalid contents\n", __FUNCTION__);
            /*
             * At this time, an error means we couldn't rename away
             * a suspect file, and that means something really screwy
             * is going on, so failing is the best option.
             */
            return err;
         }
      } else {
         Audit_Event(FALSE,
                     SU_(alias.dir.notadir,
                         "Alias store directory '%s' exists but is not a directory"),
                     aliasStoreRootDir);
         saveBadDir = TRUE;
      }

      /*
       * Confidence check the alias store.
       */
      err = ServiceValidateAliases();
   }

   if (saveBadDir) {
      gchar *badRootDirName = g_strdup_printf("%s.bad", aliasStoreRootDir);
      err = ServiceFileRenameFile(aliasStoreRootDir, badRootDirName);
      if (VGAUTH_E_OK != err) {
         Audit_Event(FALSE,
                     SU_(alias.dir.renamefail,
                         "Failed to rename suspect Alias store directory '%s' to '%s'"),
                     aliasStoreRootDir, badRootDirName);
         // XXX making this fatal for now.  can we do anything better?
         g_free(badRootDirName);
         return VGAUTH_E_FAIL;
      }
      g_free(badRootDirName);
   }


   /*
    * Create the alias store -- do it here rather than depend on the
    * installer, so we can survive something changing it post-install.
    */
   ret = ServiceFileMakeDirTree(aliasStoreRootDir, ALIASSTORE_DIR_PERMS);
   if (ret < 0) {
      Warning("%s: failed to set up Alias store directory tree\n", __FUNCTION__);
      return VGAUTH_E_FAIL;
   }

   return err;
}
  0707010000045A000081A400000000000000000000000168225505000009F1000000000000000000000000000000000000003D00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/file.c  /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file file.c --
 *
 *    Common file functions.  These are wrappers on glib calls
 *    that log errno.  Since glib uses the posix Win32 APIs, these
 *    should work everywhere.  Any OS-specific APIS are broken
 *    out into file{Posix,Win32}.c
 */

#include <errno.h>

#include "serviceInt.h"
#include "VGAuthLog.h"

/*
 ******************************************************************************
 * ServiceFileRenameFile --                                              */ /**
 *
 * Wrapper on g_rename() that logs errno details.
 *
 * @param[in]   srcName       The source file name.
 * @param[in]   dstName       The destination file name.
 *
 * @return 0 on success, -1 on error
 *
 ******************************************************************************
 */

int
ServiceFileRenameFile(const gchar *srcName,
                      const gchar *dstName)
{
   int ret;

   ret = g_rename(srcName, dstName);
   if (ret < 0) {
      Warning("%s: g_rename(%s, %s) failed (%d)\n", __FUNCTION__, srcName,
              dstName, errno);
   }

   return ret;
}


/*
 ******************************************************************************
 * ServiceFileUnlinkFile --                                              */ /**
 *
 * Wrapper on g_unlink() that logs errno details.
 *
 * @param[in]   fileName       The file name.
 *
 * @return 0 on success, -1 on error
 *
 ******************************************************************************
 */

int
ServiceFileUnlinkFile(const gchar *fileName)
{
   int ret;

   ret = g_unlink(fileName);
   if (ret < 0) {
      VGAUTH_LOG_ERR_POSIX("g_unlink(%s) failed", fileName);
   }

   return ret;
}

   0707010000045B000081A40000000000000000000000016822550500002ECC000000000000000000000000000000000000004200000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/filePosix.c /*********************************************************
 * Copyright (c) 2011-2016, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
* @file filePosix.c --
*
*    Posix file functions.  These are glib calls in most cases,
*    but we also need file perms and ownership, as well as Posix
*    errnos.
*/

#include <sys/types.h>
#include <unistd.h>
#include <errno.h>

#include "VGAuthLog.h"

#include "serviceInt.h"


#if defined(sun) && defined(__i386__)
#define FMTUID "l"
#define FMTGID "l"
#define FMTMODE "l"
#else
#define FMTUID ""
#define FMTGID ""
#define FMTMODE ""
#endif


/*
 ******************************************************************************
 * ServiceFilePosixMakeTempfile --                                       */ /**
 *
 * Wrapper on g_mkstemp() that logs errno details.
 *
 * @param[in]   fileName       The file name.
 * @param[in]   mode           The file open modes/permissions.
 *
 * @return A new fd on success, -1 on error
 *
 ******************************************************************************
 */

int
ServiceFilePosixMakeTempfile(gchar *fileName,
                             int mode)
{
   int fd;

   /* TODO: Once all OSes use GLIB 2.22+, just use g_mkstemp_full(). */

   fd = g_mkstemp(fileName);
   if (fd < 0) {
      VGAUTH_LOG_ERR_POSIX("g_mkstemp(%s) failed", fileName);
      goto done;
   }

  /*
   * Make sure that g_mkstemp() doesn't create a file writeable by our group
   * or by others, since that could create a window for an attacker to modify
   * the file.
   */
#ifdef VMX86_DEVEL
   {
      struct stat st;

      if (fstat(fd, &st) == 0) {
         ASSERT(!(st.st_mode & S_IWGRP) && !(st.st_mode & S_IWOTH));
      } else {
         VGAUTH_LOG_ERR_POSIX("Failed to stat temp file %s!", fileName);
      }
   }
#endif

   if (fchmod(fd, mode) != 0) {
      VGAUTH_LOG_ERR_POSIX("Failed to change ownership of %s", fileName);
      close(fd);
      fd = -1;
      goto done;
   }

done:
   return fd;
}


/*
 ******************************************************************************
 * ServiceFileSetOwner --                                                */ /**
 *
 * Changes the file to be owned by userName.
 *
 * @param[in]   fileName       The file name.
 * @param[in]   userName       The user to become owner of the file.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceFileSetOwner(const gchar *fileName,
                   const gchar *userName)
{
   VGAuthError err;
   uid_t uid;
   gid_t gid;

   err = UsercheckLookupUser(userName, &uid, &gid);
   if (err != VGAUTH_E_OK) {
      Warning("%s: Unable to look up userinfo to change ownership of '%s' to '%s'\n",
              __FUNCTION__, fileName, userName);
      return err;
   }

   if (chown(fileName, uid, gid) < 0) {
      Warning("%s: chown() failed, %d\n", __FUNCTION__, errno);
      return VGAUTH_E_PERMISSION_DENIED;
   }

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * ServiceFileCopyOwnership --                                           */ /**
 *
 * Changes dstFilename to have the same ownership as srcFilename.
 *
 * @param[in]   srcFilename       The source file name.
 * @param[in]   dstFilename       The name of the file to change.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceFileCopyOwnership(const gchar *srcFilename,
                         const gchar *dstFilename)
{
   VGAuthError err = VGAUTH_E_OK;
   uid_t uid;
   gid_t gid;
   int ret;
   struct stat stbuf;  // XXX docs say GStatBuf, but what we have
                       // in the toolchain uses this older format.

   ret = g_lstat(srcFilename, &stbuf);
   if (ret < 0) {
      VGAUTH_ERROR_SET_SYSTEM_ERRNO(err, errno);
      Warning("%s: g_lstat() failed on '%s', %d\n",
              __FUNCTION__, srcFilename, errno);
      goto done;
   } else {
      uid = stbuf.st_uid;
      gid = stbuf.st_gid;
   }

   if (chown(dstFilename, uid, gid) < 0) {
      Warning("%s: chown() failed, %d\n", __FUNCTION__, errno);
      err = VGAUTH_E_PERMISSION_DENIED;
   }

done:
   return err;
}


/*
 ******************************************************************************
 * ServiceFileMakeDirTree --                                             */ /**
 *
 * Wrapper on g_mkdir_with_parents() that logs errno details.
 *
 * @param[in]   fileName       The file name.
 * @param[in]   mode           The file open modes/permissions.
 *
 * @return 0 on success, -1 on error
 *
 ******************************************************************************
 */

int
ServiceFileMakeDirTree(const gchar *dirName,
                      int mode)
{
   int ret;

   ret = g_mkdir_with_parents(dirName, mode);
   if (ret < 0) {
      Warning("%s: g_mkdir_with_parents(%s, 0%o) failed (%d)\n",
              __FUNCTION__, dirName, mode, errno);
   }

   /*
    * Potential security issue here.  If the directory tree already exists
    * and doesn't have the desired perms, we could leave the certstore
    * wide open.  Any caller may want to use ServiceFileSetPermissions()
    * if the path is sensitive.
    */
   return ret;
}


/*
 ******************************************************************************
 * ServiceFileSetPermissions --                                          */ /**
 *
 * Sets the permissions on a file.
 *
 * @param[in]   fileName       The file name.
 * @param[in]   mode           The file permissions.
 *
 * @return 0 on success, -1 on error
 *
 ******************************************************************************
 */

int
ServiceFileSetPermissions(const gchar *fileName,
                         int mode)
{
   int ret;

   ret = chmod(fileName, mode);
   if (ret < 0) {
      Warning("%s: chmod() failed, %d\n", __FUNCTION__, errno);
   }

   return ret;
}


/*
 ******************************************************************************
 * ServiceFileGetPermissions --                                          */ /**
 *
 * Gets the permissions on a file.
 *
 * @param[in]   fileName       The file name.
 * @param[out]  mode           The file permissions.
 *
 * @return 0 on success, -1 on error
 *
 ******************************************************************************
 */

int
ServiceFileGetPermissions(const gchar *fileName,
                         int *mode)
{
   int ret;
   struct stat stbuf;  // XXX docs say GStatBuf, but what we have
                       // in the toolchain uses this older format.

   ret = g_lstat(fileName, &stbuf);
   if (ret < 0) {
      Warning("%s: g_lstat() failed on '%s', %d\n", __FUNCTION__, fileName, errno);
   } else {
      *mode = stbuf.st_mode;
   }

   return ret;
}


/*
 ******************************************************************************
 * ServiceFileVerifyFileOwnerAndPerms --                                 */ /**
 *
 * Validates the owner and permissions of the given file.
 *
 * If the user cannot be looked up, and the uid of the file can also not
 * be looked up, assume the user has been removed or we have a network
 * user issue, and ignore the ownership check.
 *
 * Returns the uids found for subsequent confidence checks.
 *
 * @param[in]   fileName       The file name.
 * @param[in]   userName       The expected owner.
 * @param[in]   mode           The expected file permissions.
 * @param[out]  uid            The actual uid of the file.
 * @param[out]  gid            The actual gid of the file.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceFileVerifyFileOwnerAndPerms(const char *fileName,
                                   const char *userName,
                                   int mode,
                                   uid_t *uidRet,
                                   gid_t *gidRet)
{
   int ret;
   struct stat stbuf;  // XXX docs say GStatBuf, but what we have
                      // in the toolchain uses this older format.
   VGAuthError err;
   uid_t uid;
   gid_t gid;

   ret = g_lstat(fileName, &stbuf);
   if (ret < 0) {
      Warning("%s: g_lstat() failed on '%s', %d\n", __FUNCTION__, fileName, errno);
      return VGAUTH_E_FAIL;
   }

   err = UsercheckLookupUser(userName, &uid, &gid);
   if (err != VGAUTH_E_OK) {
      gchar *uidUserName = NULL;

      Warning("%s: Unable to look up userinfo of '%s' to check ownership of '%s'\n",
          __FUNCTION__, userName, fileName);

      /*
       * We can't find the user.  But we don't know if its because they no
       * longer exist, or if they are just unavailable (eg NIS can't be
       * reached).  So check the uid on the file.  If that has no
       * match, we can be pretty sure the file is OK but the user is not
       * accessible.
       *
       * Note that NIS may come back between the two calls.  If it does,
       * we trust the uid->name comparison.
       */

      err = UsercheckLookupUid(stbuf.st_uid, &uidUserName);
      if (VGAUTH_E_OK == err) {
         /*
          * We got a match, check the name -- maybe NIS came back.
          */
         if (g_strcmp0(uidUserName, userName) != 0) {
            /*
             * Name mis-match.  Suspected hack attempt.
             */
            Warning("%s: Unable to look up userinfo of '%s' to check ownership "
                    "of '%s', but found valid entry for uid %"FMTUID"d\n",
                    __FUNCTION__, userName, fileName, stbuf.st_uid);
            g_free(uidUserName);
            return VGAUTH_E_SECURITY_VIOLATION;
         } else {
            Warning("%s: username '%s' lookup failed, but found uid %"FMTUID"d -- "
                    "temp NIS outage?\n", __FUNCTION__, userName, stbuf.st_uid);
         }
      } else {
         Warning("%s: failed to look up uid %"FMTUID"d; assuming user is deleted or "
                 "NIS is inaccessible\n", __FUNCTION__, stbuf.st_uid);
      }
      g_free(uidUserName);

      /*
       * If we can't lookup by name, assume deleted or unavailable user and
       * continue.
       * Set the uid/gid so they'll match and it will then do the
       * perm check.
       */
      uid = stbuf.st_uid;
      gid = stbuf.st_gid;
   }

   if (uid != stbuf.st_uid) {
      Warning("%s: uid mismatch for %s (want %"FMTUID"d, found %"FMTUID"d)\n",
              __FUNCTION__, fileName, uid, stbuf.st_uid);
      return VGAUTH_E_SECURITY_VIOLATION;
   }

   if (gid != stbuf.st_gid) {
      Warning("%s: gid mismatch for %s (want %"FMTGID"d, found %"FMTGID"d)\n",
              __FUNCTION__, fileName, gid, stbuf.st_gid);
      return VGAUTH_E_SECURITY_VIOLATION;
   }

   if (mode != (stbuf.st_mode & 0777)) {
      Warning("%s: file permission mismatch for %s (want 0%o, found "
              "0%"FMTMODE"o)\n", __FUNCTION__, fileName, mode, stbuf.st_mode);
      return VGAUTH_E_SECURITY_VIOLATION;
   }

   if (uidRet) {
      *uidRet = uid;
   }
   if (gidRet) {
      *gidRet = gid;
   }

   return VGAUTH_E_OK;
}
0707010000045C000081A4000000000000000000000001682255050000389E000000000000000000000000000000000000004100000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/netPosix.c  /*********************************************************
 * Copyright (c) 2011-2017, 2019-2022 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file netPosix.c --
 *
 *    Networking interfaces for posix systems.
 */


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/unistd.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include "serviceInt.h"
#include "VGAuthProto.h"


/*
 ******************************************************************************
 * ServiceNetworkCreateSocketDir --                                      */ /**
 *
 * Creates the directory for the UNIX domain sockets and pid files.
 *
 * @return TRUE on success, FALSE on failure.
 *
 ******************************************************************************
 */

gboolean
ServiceNetworkCreateSocketDir(void)
{
   gboolean bRet = TRUE;
   char *socketDir = g_path_get_dirname(SERVICE_PUBLIC_PIPE_NAME);

   ASSERT(socketDir != NULL);

   /*
    * Punt if its there but not a directory.
    *
    * g_file_test() on a symlink will return true for IS_DIR
    * if it's a link pointing to a directory, since glib
    * uses stat() instead of lstat().
    */
   if (g_file_test(socketDir, G_FILE_TEST_EXISTS) &&
       (!g_file_test(socketDir, G_FILE_TEST_IS_DIR) ||
       g_file_test(socketDir, G_FILE_TEST_IS_SYMLINK))) {
      bRet = FALSE;
      Warning("%s: socket dir path '%s' already exists as a non-directory; "
              "canceling\n", __FUNCTION__, socketDir);
      goto quit;
   }

   /*
    * XXX May want to add some security checks here.
    */
   if (!g_file_test(socketDir, G_FILE_TEST_EXISTS)) {
      int ret;

      ret = ServiceFileMakeDirTree(socketDir, 0755);
      if (ret < 0) {
         bRet = FALSE;
         Warning("%s: failed to create socket dir '%s' error: %d\n",
                 __FUNCTION__, socketDir, ret);
         goto quit;
      }
      Log("%s: Created socket directory '%s'\n", __FUNCTION__, socketDir);
   }
quit:
   g_free(socketDir);

   return bRet;
}


/*
 ******************************************************************************
 * ServiceNetworkListen --                                               */ /**
 *
 * Creates the UNIX domain socket and starts listening on it.
 *
 * @param[in]   conn          The ServiceConnection describing the connection.
 * @param[in]   makeSecure    If set, the new pipe is restricted in access to
 *                            the userName in the ServiceConnection.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceNetworkListen(ServiceConnection *conn,            // IN/OUT
                     gboolean makeSecure)
{
   int sock = -1;
   struct sockaddr_un sockaddr;
   int ret;
   VGAuthError err = VGAUTH_E_OK;
   mode_t mode;
   struct stat stbuf;
   uid_t uid;
   gid_t gid;

   /*
    * For some reason, this is simply hardcoded in sys/un.h
    */
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX   108
#endif

   ASSERT(strlen(conn->pipeName) < UNIX_PATH_MAX);

   conn->sock = -1;

   /*
    * Make sure the socket dir exists.  In theory this is only ever done once,
    * but something could clobber it.
    */
   if (!ServiceNetworkCreateSocketDir()) {
      err = VGAUTH_E_COMM;
      goto quit;
   }

   sock = socket(PF_UNIX, SOCK_STREAM, 0);
   if (sock < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: socket() failed, %d\n", __FUNCTION__, errno);
      goto quit;
   }

   sockaddr.sun_family = PF_UNIX;

   ret = g_unlink(conn->pipeName);
   if (ret < 0 && errno != ENOENT) {
      Warning("%s: unlink(%s) failed, %d - continuing\n", __FUNCTION__,
              conn->pipeName, errno);
   }

   /* Ignore return, returns the length of the source string */
   /* coverity[check_return] */
   g_strlcpy(sockaddr.sun_path, conn->pipeName, UNIX_PATH_MAX);

   ret = bind(sock, (struct sockaddr *) &sockaddr, sizeof sockaddr);
   if (ret < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: bind(%s) failed, %d\n", __FUNCTION__, conn->pipeName, errno);
      goto quit;
   }

   /*
    * Adjust security as needed.
    */
   if (stat(conn->pipeName, &stbuf) < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: stat(%s) failed, %d\n", __FUNCTION__, conn->pipeName, errno);
      goto quit;
   }

   mode = stbuf.st_mode;
   mode &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
   if (makeSecure) {
      mode = (S_IRUSR | S_IWUSR);
   } else {
      mode = (S_IRUSR | S_IWUSR |
               S_IRGRP | S_IWGRP |
               S_IROTH | S_IWOTH);
   }

   if (chmod(conn->pipeName, mode) < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: chmod(%s) failed, %d\n", __FUNCTION__, conn->pipeName, errno);
      goto quit;
   }

   if (makeSecure) {
      err = UsercheckLookupUser(conn->userName, &uid, &gid);
      if (err != VGAUTH_E_OK) {
         err = VGAUTH_E_NO_SUCH_USER;
         Warning("%s: failed to get uid/gid for user '%s'\n",
                 __FUNCTION__, conn->userName);
         goto quit;
      }
      if (chown(conn->pipeName, uid, gid) < 0) {
         err = VGAUTH_E_COMM;
         Warning("%s: chown(%s) failed, %d\n", __FUNCTION__, conn->pipeName, errno);
         goto quit;
      }
   }

   if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: fcntl() failed, %d\n", __FUNCTION__, errno);
      goto quit;
   }

   if (listen(sock, 32) < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: listen() failed, %d\n", __FUNCTION__, errno);
      goto quit;
   }

   conn->sock = sock;

   return VGAUTH_E_OK;

quit:
   if (sock >= 0) {
      close(sock);
   }
   return err;
}


/*
 ******************************************************************************
 * ServiceNetworkAcceptConnection --                                     */ /**
 *
 * Accepts a connection on a socket.
 *
 * @param[in]   connIn            The connection that owns the socket being
 *                                accept()ed.
 * @param[out]  connOut           The new connection.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceNetworkAcceptConnection(ServiceConnection *connIn,
                               ServiceConnection *connOut)
{
   int newfd;
   struct sockaddr_un sockaddr;
   int addrlen = sizeof(struct sockaddr_un);
   VGAuthError err = VGAUTH_E_OK;

   memset(&sockaddr, 0, addrlen);
   newfd = accept(connIn->sock, (struct sockaddr *) &sockaddr, &addrlen);
   if (newfd < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: accept() failed, %d\n", __FUNCTION__, errno);
      goto quit;
   }

   Debug("%s: got new connection on '%s', sock %d\n",
         __FUNCTION__, connIn->pipeName, newfd);

   connOut->sock = newfd;

   return VGAUTH_E_OK;
quit:
   return err;
}


/*
 ******************************************************************************
 * ServiceNetworkCloseConnection --                                      */ /**
 *
 * Closes the network connection.
 *
 * @param[in]   conn    The connection to be closed.
 *
 ******************************************************************************
 */

void
ServiceNetworkCloseConnection(ServiceConnection *conn)
{
   if (conn->sock != -1) {
      close(conn->sock);
   }
   conn->sock = -1;
}


/*
 ******************************************************************************
 * ServiceNetworkRemoveListenPipe --                                     */ /**
 *
 * Closes the listening connection's pipe.
 *
 * @param[in]   conn    The listen connection owning the pipe to be removed.
 *
 ******************************************************************************
 */

void
ServiceNetworkRemoveListenPipe(ServiceConnection *conn)
{
   /* coverity[check_return] */
   ServiceFileUnlinkFile(conn->pipeName);
}


/*
 ******************************************************************************
 * ServiceNetworkReadData --                                             */ /**
 *
 * Reads some data off the wire.
 *
 * @param[in]   conn    The connection.
 * @param[out]  len     How much data was read.
 * @param[out]  data    The data.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceNetworkReadData(ServiceConnection *conn,
                       gsize *len,                                     // OUT
                       gchar **data)                                   // OUT
{
   VGAuthError err = VGAUTH_E_OK;
   int ret;
#if NETWORK_FORCE_TINY_PACKETS
#define  READ_BUFSIZE   1
#else
#define  READ_BUFSIZE   10240
#endif
   char buf[READ_BUFSIZE];

   *len = 0;
   *data = NULL;

   do {
      ret = recv(conn->sock, buf, sizeof(buf), 0);
      if (ret == 0) {
         Debug("%s: EOF on socket\n", __FUNCTION__);
         conn->eof = TRUE;
         return err;
      }
   } while (ret == -1 && errno == EINTR);

   if (ret < 0) {
      Warning("%s: error %d reading from socket\n", __FUNCTION__, errno);
      return VGAUTH_E_COMM;
   }

   *data = g_strndup(buf, ret);
   *len = ret;

   return err;
}


/*
 ******************************************************************************
 * ServiceNetworkWriteData --                                            */ /**
 *
 * Writes some data on the wire.
 *
 * @param[in]   conn    The connection.
 * @param[in]   len     How much data to write.
 * @param[in]   data    The data.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceNetworkWriteData(ServiceConnection *conn,
                        gsize len,
                        gchar *data)
{
   VGAuthError err = VGAUTH_E_OK;
   gsize sent = 0;
   int ret;

   if (len == 0) {
      Debug("%s: asked to send %d bytes; bad caller?\n", __FUNCTION__, (int) len);
      return err;
   }

   /*
    * XXX Potential DOS issue here.  This could wedge if the socket
    * gets full and is never drained (client asks for QueryMappedCerts,
    * reply is huge, client suspends before we start sending it,
    * buffer fills, service loops.)
    *
    * A couple potential fixes:
    * - instead of writing, toss it on a queue and watch the writable
    *   flag on the sock and dump when we can.
    * - have the current code just give up and declare the client dead
    *   if we get EAGAIN too many times in a row.
    *
    * Second fix is simpler, but might bite us in a dev situation if
    * we sit in a breakpoint too long.
    */

   do {
retry:
#if NETWORK_FORCE_TINY_PACKETS
      ret = send(conn->sock, data + sent, 1, 0);
#else
      ret = send(conn->sock, data + sent, len - sent, 0);
#endif
      if (ret < 0) {
         if (EINTR == errno) {
            goto retry;
         }
         Warning("%s: send() failed, errno %d\n", __FUNCTION__, errno);
         return VGAUTH_E_COMM;
      }
      sent += ret;
   } while (sent < len);

   return err;
}


/*****************************************************************************/

#ifdef SUPPORT_TCP

/*
 * XXX this is untested, and may not even compile, but is
 * left as a starting point for an external client.
 */

VGAuthError
ServiceNetworkAcceptTCPConnection(ServiceConnection *connIn,
                                  ServiceConnection *connOut)
{
   int newfd;
   struct sockaddr_in sockaddr;
   int addrlen = sizeof(sockaddr);
   char *ipaddr = NULL;
   VGAuthError err = VGAUTH_E_OK;

   memset(&sockaddr, 0, sizeof(sockaddr));
   newfd = accept(connIn->sock, (struct sockaddr *) &sockaddr, &addrlen);
   if (newfd < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: accept() failed, %d\n", __FUNCTION__, errno);
      goto quit;
   }

   ipaddr = inet_ntoa(sockaddr.sin_addr);
   Debug("%s: got new connection from %s\n", __FUNCTION__, ipaddr);

   connOut->sock = newfd;

   return VGAUTH_E_OK;
quit:
   return err;
}


VGAuthError
ServiceNetworkTCPListen(ServiceConnection *conn)         // IN/OUT
{
   int sock;
   struct sockaddr_in sockaddr;
   int ret;
   VGAuthError err = VGAUTH_E_OK;
   int fd;
   struct protoent protobuf;
   char *buf;
   struct protoent *proto_ent;

   buf = g_malloc(2048);
   if (getprotobyname_r("TCP", &protobuf, buf, 2048, &proto_ent)) {
      err = VGAUTH_E_COMM;
      Warning("%s: getprotobyname_r() failed, %d\n", __FUNCTION__, errno);
      g_free(buf);
      goto quit;
   }


   sock = socket(AF_INET, SOCK_STREAM, proto_ent->p_proto);
   if (sock < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: socket() failed, %d\n", __FUNCTION__, errno);
      g_free(buf);
      goto quit;
   }

   g_free(buf);

   memset(&sockaddr, 0, sizeof(sockaddr));
   sockaddr.sin_family = AF_INET;
   sockaddr.sin_port = htons(conn->port);
   sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);

#ifdef SO_REUSEADDR
   {
      int one = 1;
      setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int));
   }
#endif

   ret = bind(sock, (struct sockaddr *) &sockaddr, sizeof sockaddr);

   if (ret < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: bind() failed, %d\n", __FUNCTION__, errno);
      goto quit;
   }

   if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: fcntl() failed, %d\n", __FUNCTION__, errno);
      goto quit;
   }

   if (listen(sock, 32) < 0) {
      err = VGAUTH_E_COMM;
      Warning("%s: listen() failed, %d\n", __FUNCTION__, errno);
      goto quit;
   }

   conn->sock = sock;

   return VGAUTH_E_OK;

quit:
   close(sock);
   return err;
}

#endif   // SUPPORT_TCP
  0707010000045D000081A40000000000000000000000016822550500012A67000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/proto.c /*********************************************************
 * Copyright (c) 2011-2016,2019-2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file proto.c --
 *
 *    Service/client protocol interfaces
 */

#include <stdlib.h>
#include <string.h>

#include <glib/gstdio.h>

#include "VGAuthLog.h"
#include "serviceInt.h"
#include "VGAuthProto.h"
#ifdef _WIN32
#include "winToken.h"
#include "winDupHandle.h"
#endif

/* cranks up parser debugging */
#define VGAUTH_PROTO_TRACE 0

/*
 * Request types
 */
typedef enum {
   PROTO_REQUEST_UNKNOWN,
   PROTO_REQUEST_SESSION_REQ,
   PROTO_REQUEST_CONN,
   PROTO_REQUEST_ADDALIAS,
   PROTO_REQUEST_REMOVEALIAS,
   PROTO_REQUEST_QUERYALIASES,
   PROTO_REQUEST_QUERYMAPPEDALIASES,
   PROTO_REQUEST_CREATETICKET,
   PROTO_REQUEST_VALIDATETICKET,
   PROTO_REQUEST_REVOKETICKET,
   PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN,
} ProtoRequestType;

/*
 * Possible parse states for requests.
 */
typedef enum {
   PARSE_STATE_NONE,
   PARSE_STATE_REQUEST,
   PARSE_STATE_SEQNO,
   PARSE_STATE_REQNAME,
   PARSE_STATE_VERSION,
   PARSE_STATE_USERNAME,
   PARSE_STATE_TICKET,
   PARSE_STATE_ADDLINK,
   PARSE_STATE_PEMCERT,
   PARSE_STATE_ALIASINFO,
   PARSE_STATE_COMMENT,
   PARSE_STATE_SUBJECT,
   PARSE_STATE_ANYSUBJECT,
   PARSE_STATE_PID,
   PARSE_STATE_TOKEN,
   PARSE_STATE_SAMLTOKEN,
   PARSE_STATE_USERHANDLEINFO,
   PARSE_STATE_USERHANDLETYPE,
   PARSE_STATE_USERHANDLESAMLINFO,
   PARSE_STATE_USERHANDLESAMLSUBJECT,
   PARSE_STATE_SAML_VALIDATE_ONLY,
   PARSE_STATE_SAML_HOST_VERIFIED,
} ProtoParseState;

/*
 * The request structure
 */

struct ProtoRequest {
   gboolean complete;
   int sequenceNumber;

   ProtoRequestType reqType;

   ProtoParseState parseState;

   union {
      struct {
         int version;
         gchar *userName;
      } sessionReq;

      struct {
         /*
          * The client PID that the client declares
          * This field is only used on Windows
          */
         gchar *pid;
      } connect;

      struct {
         gchar *userName;
         gboolean addMapped;
         gchar *pemCert;
         ServiceAliasInfo aliasInfo;
      } addAlias;

      struct {
         gchar *userName;
         gchar *pemCert;
         ServiceSubject subject;
      } removeAlias;

      struct {
         gchar *userName;
      } queryAliases;

      struct {
         gchar *userName;
         /* The received client token HANDLE */
         gchar *token;
         ServiceValidationResultsType type;
         // only used if the type is VALIDATION_RESULTS_TYPE_SAML
         ServiceValidationResultsData samlData;
      } createTicket;

      struct {
         gchar *ticket;
      } validateTicket;

      struct {
         gchar *ticket;
      } revokeTicket;

      struct {
         gchar *samlToken;
         gchar *userName;
         gboolean validateOnly;
         gboolean hostVerified;
      } validateSamlBToken;

   } reqData;

#if VGAUTH_PROTO_TRACE
   gchar *rawData;
#endif
};


static VGAuthError ServiceProtoValidateSamlBearerToken(ServiceConnection *conn,
                                                       ProtoRequest *req);


/*
 ******************************************************************************
 * ProtoRequestTypeText --                                               */ /**
 *
 * Return the text representation of the protocol request type.
 *
 * @param[in]  t        A protocol request type
 *
 ******************************************************************************
 */

static const char *
ProtoRequestTypeText(ProtoRequestType t)
{
   switch (t) {
   case PROTO_REQUEST_UNKNOWN:
      return "UNKNOWN";
   case PROTO_REQUEST_SESSION_REQ:
      return "SESSION";
   case PROTO_REQUEST_CONN:
      return "CONNECT";
   case PROTO_REQUEST_ADDALIAS:
      return "ADDALIAS";
   case PROTO_REQUEST_REMOVEALIAS:
      return "REMOVEALIAS";
   case PROTO_REQUEST_QUERYALIASES:
      return "QUERYALIASES";
   case PROTO_REQUEST_QUERYMAPPEDALIASES:
      return "QUERYMAPPEDALIASES";
   case PROTO_REQUEST_CREATETICKET:
      return "CREATETICKET";
   case PROTO_REQUEST_VALIDATETICKET:
      return "VALIDATETICKET";
   case PROTO_REQUEST_REVOKETICKET:
      return "REVOKETICKET";
   case PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN:
      return "VALIDATE_SAML_BEARER_TOKEN";
   default:
      return "INVALID";
   }
}


/*
 ******************************************************************************
 * ProtoValidationTypeString --                                          */ /**
 *
 * Returns the XML wire name of a ServiceValidationResultsType.
 *
 * @param[in]  userHandl         The VGAuthUSerHandle.
 *
 ******************************************************************************
 */

static const gchar *
ProtoValidationTypeString(const ServiceValidationResultsType t)
{
   switch (t) {
   case VALIDATION_RESULTS_TYPE_NAMEPASSWORD:
      return VGAUTH_USERHANDLE_TYPE_NAMEPASSWORD;
   case VALIDATION_RESULTS_TYPE_SSPI:
      return VGAUTH_USERHANDLE_TYPE_SSPI;
   case VALIDATION_RESULTS_TYPE_SAML:
      return VGAUTH_USERHANDLE_TYPE_SAML;
   case VALIDATION_RESULTS_TYPE_SAML_INFO_ONLY:
      return VGAUTH_USERHANDLE_TYPE_SAML_INFO_ONLY;
   case VALIDATION_RESULTS_TYPE_UNKNOWN:
   default:
      ASSERT(0);
      Warning("%s: Tried to convert a validationType of %d to a string\n",
              __FUNCTION__, t);
      return "<UNKNOWN>";
   }
}


/*
 ******************************************************************************
 * Proto_DumpRequest --                                                  */ /**
 *
 * Debugging.  Spews a ProtoRequest to stdout.
 *
 * @param[in]  req        The request to dump.
 *
 ******************************************************************************
 */

static void
Proto_DumpRequest(ProtoRequest *req)
{
#if VGAUTH_PROTO_TRACE
   printf("raw data: %s\n", req->rawData ? req->rawData : "<none>");
#endif
   Debug("complete: %d\n", req->complete);
   Debug("sequenceNumber: %d\n", req->sequenceNumber);
   Log("requestType: %d(%s REQ)\n", req->reqType,
       ProtoRequestTypeText(req->reqType));

   switch (req->reqType) {
   case PROTO_REQUEST_SESSION_REQ:
      Debug("version #: %d\n", req->reqData.sessionReq.version);
      Log("userName: '%s'\n", req->reqData.sessionReq.userName);
      break;
   case PROTO_REQUEST_CONN:
      // no details
      break;
   case PROTO_REQUEST_ADDALIAS:
      Log("userName: %s\n", req->reqData.addAlias.userName);
      Log("addMapped: %d\n", req->reqData.addAlias.addMapped);
      Debug("pemCert: %s\n", req->reqData.addAlias.pemCert);
      if (req->reqData.addAlias.aliasInfo.type == SUBJECT_TYPE_NAMED) {
         Log("Subject: %s\n", req->reqData.addAlias.aliasInfo.name);
      } else  if (req->reqData.addAlias.aliasInfo.type == SUBJECT_TYPE_ANY) {
         Log("ANY Subject\n");
      } else {
         Warning("*** UNKNOWN Subject type ***\n");
      }
      Log("comment: %s\n", req->reqData.addAlias.aliasInfo.comment);
      break;
   case PROTO_REQUEST_REMOVEALIAS:
      Log("userName: %s\n", req->reqData.removeAlias.userName);
      Debug("pemCert: %s\n", req->reqData.removeAlias.pemCert);
      if (req->reqData.removeAlias.subject.type == SUBJECT_TYPE_NAMED) {
         Log("Subject: %s\n", req->reqData.removeAlias.subject.name);
      } else  if (req->reqData.removeAlias.subject.type == SUBJECT_TYPE_ANY) {
         Log("ANY Subject\n");
      } else {
         Log("No Subject type specified (assuming removeAll case)\n");
      }
      break;
   case PROTO_REQUEST_QUERYALIASES:
      Log("userName: %s\n", req->reqData.queryAliases.userName);
      break;
   case PROTO_REQUEST_QUERYMAPPEDALIASES:
      // no details
      break;
   case PROTO_REQUEST_CREATETICKET:
      Log("userName '%s'\n", req->reqData.createTicket.userName);
      break;
   case PROTO_REQUEST_VALIDATETICKET:
      Log("ticket '%s'\n", req->reqData.validateTicket.ticket);
      break;
   case PROTO_REQUEST_REVOKETICKET:
      Log("ticket '%s'\n", req->reqData.revokeTicket.ticket);
      break;
   case PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN:
      Debug("token '%s'\n", req->reqData.validateSamlBToken.samlToken);
      Log("username '%s'\n", req->reqData.validateSamlBToken.userName);
      Log("validate Only '%s'\n",
            req->reqData.validateSamlBToken.validateOnly ? "TRUE" : "FALSE");
      Log("hostVerified '%s'\n",
            req->reqData.validateSamlBToken.hostVerified ? "TRUE" : "FALSE");
      break;
   default:
      Warning("Unknown request type -- no request specific data\n");
      break;
   }
}


/*
 ******************************************************************************
 * Proto_RequestNameToType --                                            */ /**
 *
 * Converts a request name to a ProtoRequestType
 *
 * @param[in]  name     The request name.
 *
 * @return the matching ProtoRequestType
 *
 ******************************************************************************
 */

typedef struct {
   ProtoRequestType type;
   const char *reqName;
} ReqName;

static const ReqName reqNameList[] = {
   { PROTO_REQUEST_SESSION_REQ, VGAUTH_REQUESTSESSION_ELEMENT_NAME },
   { PROTO_REQUEST_CONN, VGAUTH_REQUESTCONNECT_ELEMENT_NAME },
   { PROTO_REQUEST_ADDALIAS, VGAUTH_REQUESTADDALIAS_ELEMENT_NAME },
   { PROTO_REQUEST_REMOVEALIAS, VGAUTH_REQUESTREMOVEALIAS_ELEMENT_NAME },
   { PROTO_REQUEST_QUERYALIASES, VGAUTH_REQUESTQUERYALIASES_ELEMENT_NAME },
   { PROTO_REQUEST_QUERYMAPPEDALIASES, VGAUTH_REQUESTQUERYMAPPEDALIASES_ELEMENT_NAME },
   { PROTO_REQUEST_CREATETICKET, VGAUTH_REQUESTCREATETICKET_ELEMENT_NAME },
   { PROTO_REQUEST_VALIDATETICKET, VGAUTH_REQUESTVALIDATETICKET_ELEMENT_NAME },
   { PROTO_REQUEST_REVOKETICKET, VGAUTH_REQUESTREVOKETICKET_ELEMENT_NAME },
   { PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN,
     VGAUTH_REQUESTVALIDATESAMLBEARERTOKEN_ELEMENT_NAME },
};

static ProtoRequestType
Proto_RequestNameToType(const gchar *name)
{
   int i;

   for (i = 0; i < G_N_ELEMENTS(reqNameList); i++) {
      if (g_strcmp0(name, reqNameList[i].reqName) == 0) {
         return reqNameList[i].type;
      }
   }

   return PROTO_REQUEST_UNKNOWN;
}


/*
 ******************************************************************************
 * Proto_StartElement --                                                 */ /**
 *
 * Called by the XML parser when it sees the start of a new
 * element.  Used to update the current parser state, and allocate
 * any space that may be needed for processing that state.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  elementName         The name of the element being started.
 * @param[in]  attributeNames      The names of any attributes on the element.
 * @param[in]  attributeValues     The values of any attributes on the element.
 * @param[in]  userData            The current ProtoRequest as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
Proto_StartElement(GMarkupParseContext *parseContext,
                   const gchar *elementName,
                   const gchar **attributeNames,
                   const gchar **attributeValues,
                   gpointer userData,
                   GError **error)
{
   ProtoRequest *req = (ProtoRequest *) userData;

#if VGAUTH_PROTO_TRACE
   Debug("%s: elementName '%s', parseState %d, request type %d\n", __FUNCTION__, elementName, req->parseState, req->reqType);
#endif

   switch (req->parseState) {
   case PARSE_STATE_NONE:
      /*
       * We're in 'idle' mode, expecting a fresh request.
       */
      if (g_strcmp0(elementName, VGAUTH_REQUEST_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_REQUEST;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, req->parseState);
      }
      break;
   case PARSE_STATE_REQUEST:
      /*
       * We're in 'request' mode, expecting some element inside the request.
       */
      if (g_strcmp0(elementName, VGAUTH_REQUESTNAME_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_REQNAME;
      } else if (g_strcmp0(elementName, VGAUTH_SEQUENCENO_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_SEQNO;
      } else if (g_strcmp0(elementName, VGAUTH_USERNAME_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_USERNAME;
      } else if (g_strcmp0(elementName, VGAUTH_VERSION_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_VERSION;
      } else if (g_strcmp0(elementName, VGAUTH_TICKET_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_TICKET;
      } else if (g_strcmp0(elementName, VGAUTH_ADDMAPPEDLINK_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_ADDLINK;
      } else if (g_strcmp0(elementName, VGAUTH_PEMCERT_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_PEMCERT;
      } else if (g_strcmp0(elementName, VGAUTH_PID_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_PID;
      } else if (g_strcmp0(elementName, VGAUTH_TOKEN_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_TOKEN;
      } else if (g_strcmp0(elementName, VGAUTH_SAMLTOKEN_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_SAMLTOKEN;
      } else if (g_strcmp0(elementName, VGAUTH_VALIDATE_ONLY_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_SAML_VALIDATE_ONLY;
      } else if (g_strcmp0(elementName, VGAUTH_HOST_VERIFIED_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_SAML_HOST_VERIFIED;
      } else if (g_strcmp0(elementName, VGAUTH_ALIASINFO_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_ALIASINFO;
      } else if (g_strcmp0(elementName, VGAUTH_SUBJECT_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_SUBJECT;
      } else if (g_strcmp0(elementName, VGAUTH_USERHANDLEINFO_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_USERHANDLEINFO;
      } else if (g_strcmp0(elementName, VGAUTH_ANYSUBJECT_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_ANYSUBJECT;
         /*
          * Proto_TextContents will never get called for an empty-element
          * tag, so set the value here.
          */
         req->parseState = PARSE_STATE_ANYSUBJECT;
         if (req->reqType != PROTO_REQUEST_REMOVEALIAS) {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Unexpected element '%s' in parse state %d",
                        elementName, req->parseState);
         } else {
            req->reqData.removeAlias.subject.type = SUBJECT_TYPE_ANY;
         }
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, req->parseState);
      }
      break;
   case PARSE_STATE_ALIASINFO:
      /*
       * We're parsing a AliasInfo, expecting one of its components.
       */
      if (g_strcmp0(elementName, VGAUTH_SUBJECT_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_SUBJECT;
      } else if (g_strcmp0(elementName, VGAUTH_ANYSUBJECT_ELEMENT_NAME) == 0) {
         /*
          * Proto_TextContents will never get called for an empty-element
          * tag, so set the value here.
          */
         req->parseState = PARSE_STATE_ANYSUBJECT;
         if (req->reqType == PROTO_REQUEST_ADDALIAS) {
            req->reqData.addAlias.aliasInfo.type = SUBJECT_TYPE_ANY;
         } else if (req->reqType == PROTO_REQUEST_CREATETICKET) {
            req->reqData.createTicket.samlData.aliasInfo.type = SUBJECT_TYPE_ANY;
         } else {
            g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                        "Unexpected element '%s' in parse state %d",
                        elementName, req->parseState);
         }
      } else if (g_strcmp0(elementName, VGAUTH_COMMENT_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_COMMENT;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, req->parseState);
      }
      break;
   case PARSE_STATE_USERHANDLEINFO:
      /*
       * We're parsing a UserHandleInfo, expecting one of its components.
       */
      if (g_strcmp0(elementName, VGAUTH_USERHANDLETYPE_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_USERHANDLETYPE;
      } else if (g_strcmp0(elementName, VGAUTH_USERHANDLESAMLINFO_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_USERHANDLESAMLINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, req->parseState);
      }
      break;
   case PARSE_STATE_USERHANDLESAMLINFO:
      /*
       * We're parsing a UserHandleSamlInfo, expecting one of its components.
       */
      if (g_strcmp0(elementName, VGAUTH_USERHANDLESAMLSUBJECT_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_USERHANDLESAMLSUBJECT;
      } else if (g_strcmp0(elementName, VGAUTH_ALIASINFO_ELEMENT_NAME) == 0) {
         req->parseState = PARSE_STATE_ALIASINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Unexpected element '%s' in parse state %d",
                     elementName, req->parseState);
      }
      break;
   default:
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Unexpected element '%s' in parse state %d",
                  elementName, req->parseState);
      break;
   }
}


/*
 ******************************************************************************
 * Proto_EndElement --                                                   */ /**
 *
 * Called by the XML parser when the end of an element is reached.
 * Used here to pop the parse state.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  elementName         The name of the element being finished.
 * @param[in]  userData            The current ProtoRequest as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
Proto_EndElement(GMarkupParseContext *parseContext,
                 const gchar *elementName,
                 gpointer userData,
                 GError **error)
{
   ProtoRequest *req = (ProtoRequest *) userData;

#if VGAUTH_PROTO_TRACE
   Debug("%s: elementName '%s'\n", __FUNCTION__, elementName);
#endif

   switch (req->parseState) {
   case PARSE_STATE_SEQNO:
   case PARSE_STATE_REQNAME:
   case PARSE_STATE_VERSION:
   case PARSE_STATE_USERNAME:
   case PARSE_STATE_ADDLINK:
   case PARSE_STATE_TICKET:
   case PARSE_STATE_PID:
   case PARSE_STATE_TOKEN:
   case PARSE_STATE_SAMLTOKEN:
   case PARSE_STATE_SAML_VALIDATE_ONLY:
   case PARSE_STATE_SAML_HOST_VERIFIED:
   case PARSE_STATE_USERHANDLEINFO:
      req->parseState = PARSE_STATE_REQUEST;
      break;
   case PARSE_STATE_ALIASINFO:
      if (req->reqType == PROTO_REQUEST_ADDALIAS) {
         req->parseState = PARSE_STATE_REQUEST;
      } else if (req->reqType == PROTO_REQUEST_CREATETICKET) {
         req->parseState = PARSE_STATE_USERHANDLESAMLINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Bad parse state, popping aliasInfo in req type %d",
                     req->reqType);
      }
      break;
   case PARSE_STATE_REQUEST:
      req->complete = TRUE;
      req->parseState = PARSE_STATE_NONE;
      break;
   case PARSE_STATE_PEMCERT:
      if (req->reqType == PROTO_REQUEST_ADDALIAS) {
         req->parseState = PARSE_STATE_REQUEST;
      } else if (req->reqType == PROTO_REQUEST_REMOVEALIAS) {
         req->parseState = PARSE_STATE_REQUEST;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Bad parse state, popping pemCert in req type %d",
                     req->reqType);
      }
      break;
   case PARSE_STATE_COMMENT:
      req->parseState = PARSE_STATE_ALIASINFO;
      break;
   case PARSE_STATE_SUBJECT:
   case PARSE_STATE_ANYSUBJECT:
      if (req->reqType == PROTO_REQUEST_ADDALIAS) {
         req->parseState = PARSE_STATE_ALIASINFO;
      } else if (req->reqType == PROTO_REQUEST_REMOVEALIAS) {
         req->parseState = PARSE_STATE_REQUEST;
      } else if (req->reqType == PROTO_REQUEST_CREATETICKET) {
         req->parseState = PARSE_STATE_ALIASINFO;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Bad parse state, popping (any)subject state %d",
                     req->parseState);
      }
      break;
   case PARSE_STATE_USERHANDLESAMLINFO:
   case PARSE_STATE_USERHANDLETYPE:
      req->parseState = PARSE_STATE_USERHANDLEINFO;
      break;
   case PARSE_STATE_USERHANDLESAMLSUBJECT:
      req->parseState = PARSE_STATE_USERHANDLESAMLINFO;
      break;
   default:
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Bad parse state, popping unknown parse state %d",
                  req->parseState);
      ASSERT(0);
   }

}


/*
 ******************************************************************************
 * Proto_TextContents --                                                 */ /**
 *
 * Called by the parser with the contents of an element.
 * Used to store the values.
 *
 * @param[in]  parseContext        The XML parse context.
 * @param[in]  text                The contents of the current element
 *                                 (not NUL terminated)
 * @param[in]  textSize            The length of the text.
 * @param[in]  userData            The current ProtoRequest as callback data.
 * @param[out] error               Any error.
 *
 ******************************************************************************
 */

static void
Proto_TextContents(GMarkupParseContext *parseContext,
                   const gchar *text,
                   gsize textSize,
                   gpointer userData,
                   GError **error)
{
   ProtoRequest *req = (ProtoRequest *) userData;
   gchar *val;
   int iVal;
   gboolean duplicate_found = FALSE;

#if VGAUTH_PROTO_TRACE
   Debug("%s: parseState %d, text '%*s'\n", __FUNCTION__, req->parseState, (int) textSize, text);
#endif

   /*
    * Simple string values should be set only once, but a malicious client
    * could send them multiple times, which could cause a leak if not
    * checked.
    */
#define SET_CHECK_DUP(var, val) \
   if ((var) != NULL) { duplicate_found = TRUE; goto done; } \
   else { (var) = (val); }

   val = g_strndup(text, textSize);

   switch (req->parseState) {
   case PARSE_STATE_SEQNO:
      req->sequenceNumber = atoi(val);
      break;
   case PARSE_STATE_REQNAME:
      if (req->reqType != PROTO_REQUEST_UNKNOWN) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Trying to handle new request of type %s when processing"
                     " a request of type %d",
                     val, req->reqType);
         goto done;
      }
      req->reqType = Proto_RequestNameToType(val);

      /*
       * Do any special init work once we've chosen the request type.
       */
      if (req->reqType == PROTO_REQUEST_REMOVEALIAS) {
         // init removeAlias to be UNSET, so that we handle the removeAll case
         req->reqData.removeAlias.subject.type = SUBJECT_TYPE_UNSET;
      }
      break;
   case PARSE_STATE_VERSION:
      if (req->reqType != PROTO_REQUEST_SESSION_REQ) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found version number in req type %d",
                     req->reqType);
         goto done;
      }
      req->reqData.sessionReq.version = atoi(val);
      break;
   case PARSE_STATE_USERNAME:
      switch (req->reqType) {
      case PROTO_REQUEST_SESSION_REQ:
         SET_CHECK_DUP(req->reqData.sessionReq.userName, val);
         break;
      case PROTO_REQUEST_ADDALIAS:
         SET_CHECK_DUP(req->reqData.addAlias.userName, val);
         break;
      case PROTO_REQUEST_REMOVEALIAS:
         SET_CHECK_DUP(req->reqData.removeAlias.userName, val);
         break;
      case PROTO_REQUEST_QUERYALIASES:
         SET_CHECK_DUP(req->reqData.queryAliases.userName, val);
         break;
      case PROTO_REQUEST_CREATETICKET:
         SET_CHECK_DUP(req->reqData.createTicket.userName, val);
         break;
      case PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN:
         SET_CHECK_DUP(req->reqData.validateSamlBToken.userName, val);
         break;
      default:
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found username in req type %d",
                     req->reqType);
         goto done;
      }
      val = NULL;
      break;
   case PARSE_STATE_TICKET:
      if (req->reqType == PROTO_REQUEST_VALIDATETICKET) {
         SET_CHECK_DUP(req->reqData.validateTicket.ticket, val);
      } else if (req->reqType == PROTO_REQUEST_REVOKETICKET) {
         SET_CHECK_DUP(req->reqData.revokeTicket.ticket, val);
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found ticket in req type %d",
                     req->reqType);
         goto done;
      }
      val = NULL;
      break;
   case PARSE_STATE_ADDLINK:
      if (req->reqType == PROTO_REQUEST_ADDALIAS) {
         req->reqData.addAlias.addMapped = ((atoi(val) == 1) ? TRUE : FALSE);
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found addMappedLink in req type %d",
                     req->reqType);
         goto done;
      }
      break;
   case PARSE_STATE_REQUEST:
   case PARSE_STATE_ALIASINFO:
   case PARSE_STATE_USERHANDLEINFO:
   case PARSE_STATE_USERHANDLESAMLINFO:
      /*
       * Should just be whitespace, ignore
       */
      break;
   case PARSE_STATE_SUBJECT:
      if (req->reqType == PROTO_REQUEST_ADDALIAS) {
         SET_CHECK_DUP(req->reqData.addAlias.aliasInfo.name, val);
         req->reqData.addAlias.aliasInfo.type = SUBJECT_TYPE_NAMED;
      } else if (req->reqType == PROTO_REQUEST_REMOVEALIAS) {
         SET_CHECK_DUP(req->reqData.removeAlias.subject.name, val);
         req->reqData.removeAlias.subject.type = SUBJECT_TYPE_NAMED;
      } else if (req->reqType == PROTO_REQUEST_CREATETICKET) {
         SET_CHECK_DUP(req->reqData.createTicket.samlData.aliasInfo.name, val);
         req->reqData.createTicket.samlData.aliasInfo.type = SUBJECT_TYPE_NAMED;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found SUBJECT in req type %d",
                     req->reqType);
         goto done;
      }
      val = NULL;
      break;
   case PARSE_STATE_ANYSUBJECT:
      /*
       * Don't expect to ever get here, but sombody may not use
       * an empty-element tag.
       */
      if (req->reqType == PROTO_REQUEST_ADDALIAS) {
         req->reqData.addAlias.aliasInfo.type = SUBJECT_TYPE_ANY;
         req->reqData.addAlias.aliasInfo.name = NULL;
      } else if (req->reqType == PROTO_REQUEST_REMOVEALIAS) {
         req->reqData.removeAlias.subject.type = SUBJECT_TYPE_ANY;
         req->reqData.removeAlias.subject.name = NULL;
      } else if (req->reqType == PROTO_REQUEST_CREATETICKET) {
         req->reqData.createTicket.samlData.aliasInfo.type = SUBJECT_TYPE_ANY;
         req->reqData.createTicket.samlData.aliasInfo.name = NULL;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found ANYSUBJECT in req type %d",
                     req->reqType);
         goto done;
       }
      break;
   case PARSE_STATE_COMMENT:
      if (req->reqType == PROTO_REQUEST_ADDALIAS) {
         SET_CHECK_DUP(req->reqData.addAlias.aliasInfo.comment, val);
      } else if (req->reqType == PROTO_REQUEST_CREATETICKET) {
         SET_CHECK_DUP(req->reqData.createTicket.samlData.aliasInfo.comment, val);
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found comment in req type %d",
                     req->reqType);
         goto done;
      }
      val = NULL;
      break;
   case PARSE_STATE_PEMCERT:
      if (req->reqType == PROTO_REQUEST_ADDALIAS) {
         SET_CHECK_DUP(req->reqData.addAlias.pemCert, val);
      } else if (req->reqType == PROTO_REQUEST_REMOVEALIAS) {
         SET_CHECK_DUP(req->reqData.removeAlias.pemCert, val);
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found pemCert in req type %d",
                     req->reqType);
         goto done;
      }
      val = NULL;
      break;
   case PARSE_STATE_PID:
      switch (req->reqType) {
      case PROTO_REQUEST_CONN:
         SET_CHECK_DUP(req->reqData.connect.pid, val);
         break;
      default:
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found pid in req type %d",
                     req->reqType);
         goto done;
      }
      val = NULL;
      break;
   case PARSE_STATE_TOKEN:
      switch (req->reqType) {
      case PROTO_REQUEST_CREATETICKET:
         SET_CHECK_DUP(req->reqData.createTicket.token, val);
         break;
      default:
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found token in req type %d",
                     req->reqType);
         goto done;
      }
      val = NULL;
      break;
   case PARSE_STATE_SAMLTOKEN:
      if (req->reqType != PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found SAML token in req type %d",
                     req->reqType);
         goto done;
      }
      SET_CHECK_DUP(req->reqData.validateSamlBToken.samlToken, val);
      val = NULL;
      break;
   case PARSE_STATE_SAML_VALIDATE_ONLY:

      if (req->reqType != PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found validateOnly option in req type %d",
                     req->reqType);
         goto done;
      }
      iVal = atoi(val);
      req->reqData.validateSamlBToken.validateOnly = (iVal) ? TRUE : FALSE;
      break;
   case PARSE_STATE_SAML_HOST_VERIFIED:

      if (req->reqType != PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found hostVerified option in req type %d",
                     req->reqType);
         goto done;
      }
      iVal = atoi(val);
      req->reqData.validateSamlBToken.hostVerified = (iVal) ? TRUE : FALSE;
      break;
   case PARSE_STATE_USERHANDLETYPE:
      {
      ServiceValidationResultsType t = VALIDATION_RESULTS_TYPE_UNKNOWN;

      if (req->reqType != PROTO_REQUEST_CREATETICKET) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found userHandle type in req type %d",
                     req->reqType);
         goto done;
      }
      if (g_strcmp0(val, VGAUTH_USERHANDLE_TYPE_NAMEPASSWORD) == 0) {
         t = VALIDATION_RESULTS_TYPE_NAMEPASSWORD;
      } else if (g_strcmp0(val, VGAUTH_USERHANDLE_TYPE_SSPI) == 0) {
         t = VALIDATION_RESULTS_TYPE_SSPI;
      } else if (g_strcmp0(val, VGAUTH_USERHANDLE_TYPE_SAML) == 0) {
         t = VALIDATION_RESULTS_TYPE_SAML;
      } else if (g_strcmp0(val, VGAUTH_USERHANDLE_TYPE_SAML_INFO_ONLY) == 0) {
         t = VALIDATION_RESULTS_TYPE_SAML_INFO_ONLY;
      } else {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found unrecognized userHandle type %s", val);
         goto done;
      }
      req->reqData.createTicket.type = t;
      // let val be freed below
      }
      break;
   case PARSE_STATE_USERHANDLESAMLSUBJECT:
      if (req->reqType != PROTO_REQUEST_CREATETICKET) {
         g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                     "Found samlSubject in req type %d",
                     req->reqType);
         goto done;
      }
      SET_CHECK_DUP(req->reqData.createTicket.samlData.samlSubject, val);
      val = NULL;
      break;
   default:
      ASSERT(0);
      break;
   }
done:
   if (duplicate_found) {
      g_set_error(error, G_MARKUP_ERROR_PARSE, VGAUTH_E_INVALID_ARGUMENT,
                  "Unexpected contents '%s' in parse state %d",
                  val, req->parseState);
   }
   g_free(val);
#undef SET_CHECK_DUP
}


/*
 * Describes the parser functions.
 */
static GMarkupParser wireParser = {
   Proto_StartElement,
   Proto_EndElement,
   Proto_TextContents,
   NULL,
   NULL,
};


/*
 ******************************************************************************
 * Proto_NewRequest --                                                   */ /**
 *
 * Creates a new ProtoRequest object.
 *
 * @return the new ProtoRequest *
 *
 ******************************************************************************
 */

ProtoRequest *
Proto_NewRequest(void)
{
   ProtoRequest *req = g_malloc0(sizeof(ProtoRequest));

   req->parseState = PARSE_STATE_NONE;
   req->complete = FALSE;
#if VGAUTH_PROTO_TRACE
   req->rawData = NULL;
#endif
   req->reqType = PROTO_REQUEST_UNKNOWN;

   return req;
}


/*
 ******************************************************************************
 * Proto_FreeRequest --                                                  */ /**
 *
 * Frees a req.
 *
 * @param[in]  req         The request to free.
 *
 ******************************************************************************
 */

static void
Proto_FreeRequest(ProtoRequest *req)
{
   if (NULL == req) {
      return;
   }

#if VGAUTH_PROTO_TRACE
   g_free(req->rawData);
#endif

   switch (req->reqType) {
   case PROTO_REQUEST_UNKNOWN:
      // partial/empty request -- no-op
      break;
   case PROTO_REQUEST_SESSION_REQ:
      g_free(req->reqData.sessionReq.userName);
      break;
   case PROTO_REQUEST_CONN:
      g_free(req->reqData.connect.pid);
      break;
   case PROTO_REQUEST_ADDALIAS:
      g_free(req->reqData.addAlias.userName);
      g_free(req->reqData.addAlias.pemCert);
      // will be NULL if ANY, so should be safe
      g_free(req->reqData.addAlias.aliasInfo.name);
      g_free(req->reqData.addAlias.aliasInfo.comment);
      break;
   case PROTO_REQUEST_REMOVEALIAS:
      g_free(req->reqData.removeAlias.userName);
      g_free(req->reqData.removeAlias.pemCert);
      // wll be NULL if ANY or unset, so should be safe
      g_free(req->reqData.removeAlias.subject.name);
      break;
   case PROTO_REQUEST_QUERYALIASES:
      g_free(req->reqData.queryAliases.userName);
      break;
   case PROTO_REQUEST_QUERYMAPPEDALIASES:
      //empty
      break;
   case PROTO_REQUEST_CREATETICKET:
      g_free(req->reqData.createTicket.userName);
      g_free(req->reqData.createTicket.token);
      ServiceAliasFreeAliasInfoContents(&(req->reqData.createTicket.samlData.aliasInfo));
      g_free(req->reqData.createTicket.samlData.samlSubject);
      break;
   case PROTO_REQUEST_VALIDATETICKET:
      g_free(req->reqData.validateTicket.ticket);
      break;
   case PROTO_REQUEST_REVOKETICKET:
      g_free(req->reqData.revokeTicket.ticket);
      break;
   case PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN:
      g_free(req->reqData.validateSamlBToken.samlToken);
      g_free(req->reqData.validateSamlBToken.userName);
      break;
   default:
      Warning("%s: trying to free unknown request type %d\n",
              __FUNCTION__, req->reqType);
   }

   g_free(req);
}


/*
 ******************************************************************************
 * Proto_ConfidenceCheckRequest -                                            */ /**
 *
 * Verifies a request is internally consistent and the type is what we expected.
 *
 * @param[in]  request                 The request to check.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
Proto_ConfidenceCheckRequest(ProtoRequest *request)
{
   /*
    * There's not much else to do here for now.  The parser does
    * most of the work, and we have no other rules.  The service doesn't
    * care about sequence numbers, or matching a request to a reply.
    */
#if VGAUTH_PROTO_TRACE
   ASSERT(strncmp(request->rawData, VGAUTH_XML_PREAMBLE,
                  strlen(VGAUTH_XML_PREAMBLE)) == 0);
#endif
   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * ServiceProtoReadAndProcessRequest --                                  */ /**
 *
 * Called when data is ready to be read from a client.  Reads that data,
 * parses it, and if it completes a request, process that request.
 *
 * @param[in]  conn                 The ServiceConnection.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoReadAndProcessRequest(ServiceConnection *conn)
{
   ProtoRequest *req = conn->curRequest;
   VGAuthError err = VGAUTH_E_OK;
   gchar *data = NULL;
   gsize len;
   gboolean bRet;
   GError *gErr = NULL;

   /*
    * If nothing is currently being processed start a fresh one.
    */
   if (NULL == req) {
      req = Proto_NewRequest();
      conn->curRequest = req;

      conn->parseContext = g_markup_parse_context_new(&wireParser,
                                                      0,
                                                      req,
                                                      NULL);
   }

   /*
    * Suck some bytes, parse them.
    */
   if (!req->complete) {
      err = ServiceNetworkReadData(conn, &len, &data);

      if (conn->eof) { // EOF
         err = VGAUTH_E_COMM;
         Debug("%s: read EOF on Connection %d\n", __FUNCTION__, conn->connId);
         goto quit;
      }

      if (err != VGAUTH_E_OK) {
         goto quit;
      }
#if VGAUTH_PROTO_TRACE
      if (req->rawData) {
         req->rawData = g_strdup_printf("%s%s", req->rawData, data);
      } else {
         req->rawData = g_strdup(data);
      }
#endif
      bRet = g_markup_parse_context_parse(conn->parseContext,
                                          data,
                                          len,
                                          &gErr);
      g_free(data);
      if (!bRet) {
         err = VGAUTH_E_COMM;
         Warning("%s: g_markup_parse_context_parse() failed: %s\n",
                 __FUNCTION__, gErr->message);
         g_error_free(gErr);
         goto quit;
      }
   }

   /*
    * If the parser says we have a complete request, process it.
    */
   if (req->complete) {
      Proto_DumpRequest(req);
      err = Proto_ConfidenceCheckRequest(req);
      if (err != VGAUTH_E_OK) {
         Warning("%s: request confidence check failed\n", __FUNCTION__);
      }

      // only try to handle it if the confidence check passed
      if (err == VGAUTH_E_OK) {
         err = ServiceProtoDispatchRequest(conn, req);
      }

      /*
       * Reset the protocol parser.
       */
      ServiceProtoCleanupParseState(conn);
   }

quit:
   /*
    * If something went wrong, clean up.  Any error means bad data coming
    * from the client, and we don't even try to recover -- just slam
    * the door.
    */
   if (err != VGAUTH_E_OK) {
      ServiceConnectionShutdown(conn);
   }

   return err;
}


/*
 ******************************************************************************
 * Proto_SecurityCheckRequest --                                         */ /**
 *
 * Verfies that superUser-only requests come over a superUser pipe,
 * and only superUser or the owner of a certstore can manipulate it.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The request to check.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
Proto_SecurityCheckRequest(ServiceConnection *conn,
                           ProtoRequest *req)
{
   VGAuthError err;
   gboolean isSecure = ServiceNetworkIsConnectionPrivateSuperUser(conn);

   if (conn->isPublic && req->reqType != PROTO_REQUEST_SESSION_REQ) {
      return VGAUTH_E_PERMISSION_DENIED;
   }

   switch (req->reqType) {
      /*
       * This comes over the public connection; alwsys let it through.
       */
   case PROTO_REQUEST_SESSION_REQ:
      err = VGAUTH_E_OK;
      break;
      /*
       * No security issues with Connect or QueryMappedCerts
       */
   case PROTO_REQUEST_CONN:
   case PROTO_REQUEST_QUERYMAPPEDALIASES:
      err = VGAUTH_E_OK;
      break;
      /*
       * These request can come over any user connection; always let
       * them through if they are coming from root or the owner of
       * the certstore being changed.
       */
   case PROTO_REQUEST_ADDALIAS:
   case PROTO_REQUEST_REMOVEALIAS:
   case PROTO_REQUEST_QUERYALIASES:
   case PROTO_REQUEST_CREATETICKET:
      if (isSecure) {
         err = VGAUTH_E_OK;
      } else {
         const gchar *connOwner = conn->userName;
         const gchar *reqUser = NULL;

         if (req->reqType == PROTO_REQUEST_ADDALIAS) {
            reqUser = req->reqData.addAlias.userName;
         } else if (req->reqType == PROTO_REQUEST_REMOVEALIAS) {
            reqUser = req->reqData.removeAlias.userName;
         } else if (req->reqType == PROTO_REQUEST_QUERYALIASES) {
            reqUser = req->reqData.queryAliases.userName;
         } else if (req->reqType == PROTO_REQUEST_CREATETICKET) {
            reqUser = req->reqData.createTicket.userName;
         } else {
            ASSERT(0);
         }

         if (Usercheck_CompareByName(connOwner, reqUser)) {
            err = VGAUTH_E_OK;
         } else {
            Audit_Event(FALSE,
                        SU_(proto.attack, "Possible security attack!  Request type %d has a "
                        "userName (%s) which doesn't match the pipe owner (%s)!"),
                        req->reqType, reqUser, connOwner);
            Warning("%s: Possible security attack!  Request type %d has a "
                    "userName (%s) which doesn't match the pipe owner (%s)!\n",
                    __FUNCTION__, req->reqType, reqUser, connOwner);
            err = VGAUTH_E_PERMISSION_DENIED;
         }
      }
      break;
      /*
       * These requests must come through a super-user owned private
       * connection.
       */
   case PROTO_REQUEST_VALIDATETICKET:
      err = (isSecure) ? VGAUTH_E_OK : VGAUTH_E_PERMISSION_DENIED;
      break;
   case PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN:
      /*
       * CAF wants to be able to validate as any user.
       */
      err = VGAUTH_E_OK;
      break;
   case PROTO_REQUEST_REVOKETICKET:
      /*
       * We want to allow just SUPERUSER and the ticket's owner to do the
       * Revoke.  But returning VGAUTH_E_PERMISSION_DENIED is also a hint
       * to an attacker that the ticket is valid.  So rather than
       * blow it off, we just ignore security at this layer,
       * and let the request fall through to ServiceRevokeTicket(),
       * which will turn a security issue into a no-op.
       */
      err = VGAUTH_E_OK;
      break;
   default:
      Warning("%s: Unrecognized request type '%d'\n",
              __FUNCTION__, req->reqType);
      err = VGAUTH_E_PERMISSION_DENIED;
      break;
   }

   return err;
}


/*
 ******************************************************************************
 * Proto_MakeErrorReply --                                               */ /**
 * ProtoMakeErrorReplyInt --                                             */ /**
 *
 * Generates an error reply string.  Must be g_free()d by caller.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   err           The error code.
 * @param[in]   errMsg        The error message.
 *
 * #return A new string containing the error message.
 *
 ******************************************************************************
 */

static gchar *
ProtoMakeErrorReplyInt(ServiceConnection *conn,
                       int reqSeqno,
                       VGAuthError err,
                       const char *errMsg)
{
   gchar *packet;
   gchar *escapedErrMsg = g_markup_escape_text(errMsg, -1);

   /*
    * g_markup_printf_escaped() is broken when the printf format
    * contains the Windows FMT64 format string %I64
    */

   packet = g_strdup_printf(VGAUTH_ERROR_FORMAT,
                            reqSeqno,
                            err,
                            escapedErrMsg);
   g_free(escapedErrMsg);

   Log("Returning error message '%s'\n", packet);

   return packet;
}


static gchar *
Proto_MakeErrorReply(ServiceConnection *conn,
                     ProtoRequest *req,
                     VGAuthError err,
                     const char *errMsg)
{
   return ProtoMakeErrorReplyInt(conn, req->sequenceNumber, err, errMsg);
}


/*
 ******************************************************************************
 * ServiceProtoDispatchRequest --                                        */ /**
 *
 * Dispatches and executes a request.  The function doing the processing will
 * generate any replies.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoDispatchRequest(ServiceConnection *conn,
                            ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;


   /*
    * Many requests must come across a superUser owned pipe.
    * Verify that here.
    */
   err = Proto_SecurityCheckRequest(conn, req);
   if (err != VGAUTH_E_OK) {
      Warning("%s: security check failed for request type %d\n",
              __FUNCTION__, req->reqType);
      packet = Proto_MakeErrorReply(conn,
                                    req,
                                    err,
                                    "Security check failed");
      goto sendError;
   }

#ifdef _WIN32
   /*
    * Check if we need to complete an earlier pid verification
    */
   if (conn->pidVerifyState == PID_VERIFY_PENDING) {
      err = ServiceEndVerifyPid(conn);
      if (err != VGAUTH_E_OK) {
         VGAUTH_LOG_WARNING("ServiceEndVerifyPid() failed, pipe = %s", conn->pipeName);
         packet = Proto_MakeErrorReply(conn,
                                       req,
                                       err,
                                       "Pid verification failed");
         goto sendError;
      }
   }

   /*
    * Check that we have the client proc handle to process the following
    * requests.
    */
   switch(req->reqType) {
   case PROTO_REQUEST_CREATETICKET:
   case PROTO_REQUEST_VALIDATETICKET:
   case PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN:
      if (conn->hProc == NULL) {
         VGAUTH_LOG_WARNING("Invalid client process HANDLE, possibly missing Connect, "
                            "pipe = %s", conn->pipeName);
         err = VGAUTH_E_FAIL;
         packet = Proto_MakeErrorReply(conn,
                                       req,
                                       err,
                                       "Client process handle check failed");
         goto sendError;
      }

      break;
   default:
      break;
   }
#endif

   switch (req->reqType) {
   case PROTO_REQUEST_SESSION_REQ:
      err = ServiceProtoHandleSessionRequest(conn, req);
      break;
   case PROTO_REQUEST_CONN:
      err = ServiceProtoHandleConnection(conn, req);
      break;
   case PROTO_REQUEST_ADDALIAS:
      err = ServiceProtoAddAlias(conn, req);
      break;
   case PROTO_REQUEST_REMOVEALIAS:
      err = ServiceProtoRemoveAlias(conn, req);
      break;
   case PROTO_REQUEST_QUERYALIASES:
      err = ServiceProtoQueryAliases(conn, req);
      break;
   case PROTO_REQUEST_QUERYMAPPEDALIASES:
      err = ServiceProtoQueryMappedAliases(conn, req);
      break;
   case PROTO_REQUEST_CREATETICKET:
      err = ServiceProtoCreateTicket(conn, req);
      break;
   case PROTO_REQUEST_VALIDATETICKET:
      err = ServiceProtoValidateTicket(conn, req);
      break;
   case PROTO_REQUEST_REVOKETICKET:
      err = ServiceProtoRevokeTicket(conn, req);
      break;
   case PROTO_REQUEST_VALIDATE_SAML_BEARER_TOKEN:
      err = ServiceProtoValidateSamlBearerToken(conn, req);
      break;
   default:
      /*
       * Be polite, send an error, and then fail cleanly
       */
      err = VGAUTH_E_NOTIMPLEMENTED;
      packet = Proto_MakeErrorReply(conn,
                                    req,
                                    err,
                                    "Unrecognized request");
sendError:
      /*
       * Don't really care if it works since we're about to
       * shut it down anyways.
       */
      (void) ServiceNetworkWriteData(conn, strlen(packet), packet);
      g_free(packet);
      break;
   }

   // 'err' is from ServiceNetworkWriteData(), not from the operation
   Log("%s: processed reqType %d(%s REQ), returning "
       VGAUTHERR_FMT64" on connection %d\n", __FUNCTION__,
       req->reqType, ProtoRequestTypeText(req->reqType), err, conn->connId);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoHandleSessionRequest --                                   */ /**
 *
 * Handles a SessionRequest request.  Creates a new listener pipe
 * for the incoming user, and replies to the caller.  Also does
 * any version negotiation.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The SessionRequest request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoHandleSessionRequest(ServiceConnection *conn,
                                 ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;
   gchar *pipeName = NULL;

   /*
    * Do any argument checking.  For now, the version number must
    * match.
    */

   if (req->reqData.sessionReq.version != atoi(VGAUTH_PROTOCOL_VERSION)) {
      err = VGAUTH_E_VERSION_MISMATCH;
      Warning("%s: version mismatch.  Client is %d, want %d\n",
              __FUNCTION__, req->reqData.sessionReq.version,
              atoi(VGAUTH_PROTOCOL_VERSION));
      packet = Proto_MakeErrorReply(conn, req, err,
                                    "sessionRequest failed; version mismatch");
      goto send_err;
   }

   err = ServiceStartUserConnection(req->reqData.sessionReq.userName,
                                    &pipeName);
   if (err != VGAUTH_E_OK) {
      packet = Proto_MakeErrorReply(conn, req, err, "sessionRequest failed");
   } else {
      packet = g_markup_printf_escaped(VGAUTH_SESSION_REPLY_FORMAT,
                                       req->sequenceNumber,
                                       pipeName);
   }

send_err:
   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      Warning("%s: failed to send SessionReq reply\n", __FUNCTION__);
   }

   g_free(pipeName);
   g_free(packet);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoHandleConnection --                                       */ /**
 *
 * Handles a Connect request -- just a simple reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The Connect request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoHandleConnection(ServiceConnection *conn,
                             ProtoRequest *req)
{
   VGAuthError err = VGAUTH_E_OK;
   VGAuthError err2;
   gchar *packet;
   char *event = NULL;

#ifdef _WIN32
   err = ServiceStartVerifyPid(conn, req->reqData.connect.pid, &event);
#endif

   if (err != VGAUTH_E_OK) {
      /* Value of err is always VGAUTH_E_OK on non-Windows platforms */
      /* coverity[dead_error_line] */
      packet = Proto_MakeErrorReply(conn, req, err, "connect failed");
   } else {
      /* Value of event is always NULL on non-Windows platforms */
      /* coverity[dead_error_line] */
      packet = g_markup_printf_escaped(VGAUTH_CONNECT_REPLY_FORMAT,
                                       req->sequenceNumber,
                                       event ? event : "");
   }

   err2 = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err2 != VGAUTH_E_OK) {
      Warning("%s: failed to send Connect reply\n", __FUNCTION__);
      if (err == VGAUTH_E_OK) {
         err = err2;
      }
   }
   g_free(packet);
   g_free(event);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoAddAlias --                                               */ /**
 *
 * Protocol layer for AddAlias.  Calls to alias code
 * to save the data, sends a reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The AddAlias request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoAddAlias(ServiceConnection *conn,
                     ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;

   /*
    * The alias code will do argument validation.
    */
   err = ServiceAliasAddAlias(conn->userName,
                              req->reqData.addAlias.userName,
                              req->reqData.addAlias.addMapped,
                              req->reqData.addAlias.pemCert,
                              &(req->reqData.addAlias.aliasInfo));

   if (err != VGAUTH_E_OK) {
      packet = Proto_MakeErrorReply(conn, req, err, "addAlias failed");
   } else {
      packet = g_markup_printf_escaped(VGAUTH_ADDALIAS_REPLY_FORMAT,
                                       req->sequenceNumber);
   }

   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      Warning("%s: failed to send AddSubject reply\n", __FUNCTION__);
   }

   g_free(packet);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoRemoveAlias --                                            */ /**
 *
 * Protocol layer for RemoveAlias.  Calls to alias code
 * to remove the cert, sends reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The RemoveAlias request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoRemoveAlias(ServiceConnection *conn,
                        ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;

   /*
    * The alias code will do argument validation.
    */
   err = ServiceAliasRemoveAlias(conn->userName,
                                 req->reqData.removeAlias.userName,
                                 req->reqData.removeAlias.pemCert,
                                 &(req->reqData.removeAlias.subject));

   if (err != VGAUTH_E_OK) {
      packet = Proto_MakeErrorReply(conn, req, err, "removeAlias failed");
   } else {
      packet = g_markup_printf_escaped(VGAUTH_REMOVEALIAS_REPLY_FORMAT,
                                       req->sequenceNumber);
   }

   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      Warning("%s: failed to send RemoveAlias reply\n", __FUNCTION__);
   }

   g_free(packet);

   return err;
}


/*
 ******************************************************************************
 * Proto_ConcatXMLStrings --                                             */ /**
 *
 * Concatenates 2 XML strings and returns the new string.
 * g_free()s the two inputs.
 * Result must be g_free()d.
 *
 * @param[in]  str1     The first string.
 * @param[in]  str2     The second string.
 *
 * @return The new string.
 *
 ******************************************************************************
 */

static gchar *
Proto_ConcatXMLStrings(gchar *str1,
                       gchar *str2)
{
   gchar *newStr;

   if (NULL == str2) {
      return str1;
   }
   newStr = g_strdup_printf("%s%s", str1, str2);
   g_free(str1);
   g_free(str2);

   return newStr;
}


/*
 ******************************************************************************
 * ServiceProtoQueryAliases --                                           */ /**
 *
 * Protocol layer for QueryAliases.  Calls to alias code
 * for the list of certs and associated aliasInfos, sends reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The QueryAliases request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoQueryAliases(ServiceConnection *conn,
                         ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;
   int num;
   ServiceAlias *aList;

   /*
    * The alias code will do argument validation.
    */
   err = ServiceAliasQueryAliases(req->reqData.queryAliases.userName,
                                  &num,
                                  &aList);

   if (err != VGAUTH_E_OK) {
      packet = Proto_MakeErrorReply(conn, req, err, "queryAliases failed");
   } else {
      int i;
      gchar *endPacket;

      packet = g_markup_printf_escaped(VGAUTH_QUERYALIASES_REPLY_FORMAT_START,
                                       req->sequenceNumber);
      // now the aliases
      for (i = 0; i < num; i++) {
         gchar *certPacket;
         int j;

         certPacket = g_markup_printf_escaped(VGAUTH_ALIAS_FORMAT_START,
                                              aList[i].pemCert);
         packet = Proto_ConcatXMLStrings(packet, certPacket);
         for (j = 0; j < aList[i].num; j++) {
            gchar *aiPacket;
            ServiceAliasInfo *ai = &(aList[i].infos[j]);

            if (ai->type == SUBJECT_TYPE_ANY) {
               aiPacket = g_markup_printf_escaped(VGAUTH_ANYALIASINFO_FORMAT,
                                                  ai->comment);
            } else if (ai->type == SUBJECT_TYPE_NAMED) {
               aiPacket = g_markup_printf_escaped(VGAUTH_NAMEDALIASINFO_FORMAT,
                                                  ai->name,
                                                  ai->comment);
            } else {
               aiPacket = NULL;
               ASSERT(0);
            }
            packet = Proto_ConcatXMLStrings(packet, aiPacket);
         }
         packet = Proto_ConcatXMLStrings(packet,
                                         g_markup_printf_escaped(VGAUTH_ALIAS_FORMAT_END));
      }

      // now the end of the reply
      endPacket = g_markup_printf_escaped(VGAUTH_QUERYALIASES_REPLY_FORMAT_END);
      packet = Proto_ConcatXMLStrings(packet, endPacket);


      ServiceAliasFreeAliasList(num, aList);
   }

   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      Warning("%s: failed to send QueryAliases reply\n", __FUNCTION__);
   }

   g_free(packet);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoQueryMappedAliases --                                     */ /**
 *
 * Protocol layer for QueryMappedAliases.  Calls to alias code
 * for the list of certs and subjects, sends reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The QueryMappedAliases request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoQueryMappedAliases(ServiceConnection *conn,
                               ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;
   int num;
   ServiceMappedAlias *maList;

   /*
    * The alias code will do argument validation.
    */
   err = ServiceAliasQueryMappedAliases(&num,
                                        &maList);

   if (err != VGAUTH_E_OK) {
      packet = Proto_MakeErrorReply(conn, req, err, "queryMappedIds failed");
   } else {
      int i;
      gchar *endPacket;

      packet = g_markup_printf_escaped(VGAUTH_QUERYMAPPEDALIASES_REPLY_FORMAT_START,
                                       req->sequenceNumber);
      for (i = 0; i < num; i++) {
         gchar *tPacket;
         int j;

         tPacket = g_markup_printf_escaped(VGAUTH_MAPPEDALIASES_FORMAT_START,
                                           maList[i].userName,
                                           maList[i].pemCert);
         packet = Proto_ConcatXMLStrings(packet, tPacket);
         for (j = 0; j < maList[i].num; j++) {
            if (maList[i].subjects[j].type == SUBJECT_TYPE_ANY) {
               tPacket = g_markup_printf_escaped(VGAUTH_ANYSUBJECT_FORMAT);
            } else if (maList[i].subjects[j].type == SUBJECT_TYPE_NAMED) {
               tPacket = g_markup_printf_escaped(VGAUTH_SUBJECT_FORMAT,
                                                 maList[i].subjects[j].name);
            } else {
               tPacket = NULL;
               ASSERT(0);
            }
            packet = Proto_ConcatXMLStrings(packet, tPacket);
         }
         packet = Proto_ConcatXMLStrings(packet,
                                         g_markup_printf_escaped(VGAUTH_MAPPEDALIASES_FORMAT_END));
      }

      // now the end of the reply
      endPacket = g_markup_printf_escaped(VGAUTH_QUERYMAPPEDALIASES_REPLY_FORMAT_END);
      packet = Proto_ConcatXMLStrings(packet, endPacket);

      ServiceAliasFreeMappedAliasList(num, maList);
   }

   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      Warning("%s: failed to send QueryAliases reply\n", __FUNCTION__);
   }

   g_free(packet);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoCreateTicket --                                           */ /**
 *
 * Protocol layer for CreateTicket.  Calls to ticket code
 * for the new ticket, sends reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The CreateTicket request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoCreateTicket(ServiceConnection *conn,
                         ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;
   char *ticket;

   /*
    * The ticket code will do argument validation.
    */
#ifdef _WIN32
   err = ServiceCreateTicketWin(req->reqData.createTicket.userName,
                                req->reqData.createTicket.type,
                                &(req->reqData.createTicket.samlData),
                                conn->hProc,
                                req->reqData.createTicket.token,
                                &ticket);
#else
   err = ServiceCreateTicketPosix(req->reqData.createTicket.userName,
                                  req->reqData.createTicket.type,
                                  &(req->reqData.createTicket.samlData),
                                  &ticket);
#endif

   if (err != VGAUTH_E_OK) {
      packet = Proto_MakeErrorReply(conn, req, err, "createTicket failed");
   } else {
      packet = g_markup_printf_escaped(VGAUTH_CREATETICKET_REPLY_FORMAT,
                                       req->sequenceNumber,
                                       ticket);
      g_free(ticket);
   }

   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      Warning("%s: failed to send CreateTicket reply\n", __FUNCTION__);
   }

   g_free(packet);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoValidateTicket --                                         */ /**
 *
 * Protocol layer for ValidateTicket.  Calls to ticket code
 * to validate the ticket, sends reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The ValidateTicket request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoValidateTicket(ServiceConnection *conn,
                           ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;
   gchar *sPacket;
   char *userName = NULL;
   char *token = NULL;
   ServiceValidationResultsType type;
   ServiceValidationResultsData *svd = NULL;

   /*
    * The ticket code will do argument validation.
    */
#ifdef _WIN32
   err = ServiceValidateTicketWin(req->reqData.validateTicket.ticket,
                                  conn->hProc,
                                  &userName,
                                  &type,
                                  &svd,
                                  &token);
#else
   err = ServiceValidateTicketPosix(req->reqData.validateTicket.ticket,
                                    &userName,
                                    &type,
                                    &svd);
#endif

   if (err != VGAUTH_E_OK) {
      packet = Proto_MakeErrorReply(conn, req, err, "validateTicket failed");
   } else {
      /* Value of token is always NULL on non-Windows platforms */
      /* coverity[dead_error_line] */
      packet = g_markup_printf_escaped(VGAUTH_VALIDATETICKET_REPLY_FORMAT_START,
                                       req->sequenceNumber,
                                       userName,
                                       token ? token : "",
                                       ProtoValidationTypeString(type));
      if (VALIDATION_RESULTS_TYPE_SAML == type) {
         sPacket = g_markup_printf_escaped(VGAUTH_USERHANDLESAMLINFO_FORMAT_START,
                                           svd->samlSubject);
         packet = Proto_ConcatXMLStrings(packet, sPacket);
         if (SUBJECT_TYPE_NAMED == svd->aliasInfo.type) {
               sPacket = g_markup_printf_escaped(VGAUTH_NAMEDALIASINFO_FORMAT,
                                                  svd->aliasInfo.name,
                                                  svd->aliasInfo.comment);
         } else {
               sPacket = g_markup_printf_escaped(VGAUTH_ANYALIASINFO_FORMAT,
                                                  svd->aliasInfo.comment);
         }
         packet = Proto_ConcatXMLStrings(packet, sPacket);
         packet = Proto_ConcatXMLStrings(packet,
                                         g_strdup(VGAUTH_USERHANDLESAMLINFO_FORMAT_END));
      }
      packet = Proto_ConcatXMLStrings(packet, g_strdup(VGAUTH_VALIDATETICKET_REPLY_FORMAT_END));
   }

   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      VGAUTH_LOG_WARNING("ServiceNetWorkWriteData() failed, pipe = %s",
                         conn->pipeName);
      goto done;
   }

done:

   g_free(userName);
   g_free(token);
   g_free(packet);
   ServiceFreeValidationResultsData(svd);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoRevokeTicket --                                           */ /**
 *
 * Protocol layer for RevokeTicket.  Calls to ticket code
 * to revoke the ticket, sends reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The RevokeTicket request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceProtoRevokeTicket(ServiceConnection *conn,
                         ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;

   err = ServiceRevokeTicket(conn, req->reqData.revokeTicket.ticket);
   if (err != VGAUTH_E_OK) {
      packet = Proto_MakeErrorReply(conn, req, err, "revokeTicket failed");
   } else {
      packet = g_markup_printf_escaped(VGAUTH_REVOKETICKET_REPLY_FORMAT,
                                       req->sequenceNumber);
   }

   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      VGAUTH_LOG_WARNING("ServiceNetWorkWriteData() failed, pipe = %s",
                         conn->pipeName);
   }

   g_free(packet);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoValidateSamlBearerToken --                                */ /**
 *
 * Protocol layer for ValidateSamlBearerToken.  Calls to validate code
 * to validate the token, sends reply.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   req           The ValidateSamlToken request to process.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
ServiceProtoValidateSamlBearerToken(ServiceConnection *conn,
                                    ProtoRequest *req)
{
   VGAuthError err;
   gchar *packet;
   gchar *sPacket;
   char *userName = NULL;
   char *subjectName = NULL;
   char *comment = NULL;
   char *tokenStr = NULL;
   ServiceAliasInfo *ai = NULL;

   /*
    * The validate code will do argument validation.
    */
   err = SAML_VerifyBearerTokenAndChain(req->reqData.validateSamlBToken.samlToken,
                                        req->reqData.validateSamlBToken.userName,
                                        req->reqData.validateSamlBToken.hostVerified,
                                        &userName,
                                        &subjectName,
                                        &ai);
#ifdef _WIN32
   /*
    * Only create a token in the non-info-only mode
    */
   if ((err == VGAUTH_E_OK) &&
       !req->reqData.validateSamlBToken.validateOnly) {
      HANDLE userToken = NULL;

      err = WinToken_GenerateTokenForUser(userName, &userToken);
      if (err == VGAUTH_E_OK) {
         tokenStr = ServiceDupHandleTo(conn->hProc, userToken);
         if (!tokenStr) {
            VGAUTH_LOG_WARNING("ServiceDupHandleTo() failed, user = %s",
                               userName);
            err = VGAUTH_E_FAIL;
         } else {
            // close our copy after duping into client process
            CloseHandle(userToken);
         }
      } else {
         VGAUTH_LOG_WARNING("WinToken_GenerateTokenForUser() failed, user = %s",
                            userName);
      }
   } else {
      Debug("%s: skipping token creation\n", __FUNCTION__);
   }
#endif
   if (err != VGAUTH_E_OK) {
      Audit_Event(FALSE,
                  SU_(validate.samlBearer.fail,
                      "Validation of SAML bearer token failed: %d"),
                  (int) err);    // localization code can't deal with
                                 // differing types of uint64


      /*
       * Rewrite some errors to hide any data that could be useful to an
       * attacker.  Do this at this stage so that we still have
       * useful debug and possibly auditing reasons.
       */
      if (err ==  VGAUTH_E_INVALID_CERTIFICATE) {
         err = VGAUTH_E_AUTHENTICATION_DENIED;
      }
      packet = Proto_MakeErrorReply(conn, req, err,
                                    "validateSamlToken failed");
   } else {
      Audit_Event(FALSE,
                  SU_(validate.samlBearer.success,
                      "Validated SAML bearer token for user '%s'"),
                  userName);
      /* Value of tokenStr is always NULL on non-Windows platforms */
      /* coverity[dead_error_line] */
      packet = g_markup_printf_escaped(VGAUTH_VALIDATESAMLBEARERTOKEN_REPLY_FORMAT_START,
                                       req->sequenceNumber,
                                       userName ? userName : "",
                                       tokenStr ? tokenStr : "",
                                       subjectName ? subjectName : "");

      if (SUBJECT_TYPE_NAMED == ai->type) {
            sPacket = g_markup_printf_escaped(VGAUTH_NAMEDALIASINFO_FORMAT,
                                               ai->name,
                                               ai->comment);
      } else {
            sPacket = g_markup_printf_escaped(VGAUTH_ANYALIASINFO_FORMAT,
                                              ai->comment);
      }
      packet = Proto_ConcatXMLStrings(packet, sPacket);
      packet = Proto_ConcatXMLStrings(packet,
                                      g_strdup(VGAUTH_VALIDATESAMLBEARERTOKEN_REPLY_FORMAT_END));
   }

   err = ServiceNetworkWriteData(conn, strlen(packet), packet);
   if (err != VGAUTH_E_OK) {
      VGAUTH_LOG_WARNING("ServiceNetWorkWriteData() failed, pipe = %s", conn->pipeName);
      goto done;
   }

done:
   g_free(userName);
   g_free(subjectName);
   g_free(packet);
   g_free(comment);
   g_free(tokenStr);
   ServiceAliasFreeAliasInfo(ai);

   return err;
}


/*
 ******************************************************************************
 * ServiceProtoCleanupParseState --                                      */ /**
 *
 * Resets the current parse state.
 *
 * @param[in]  conn                       The connection.
 *
 ******************************************************************************
 */

void
ServiceProtoCleanupParseState(ServiceConnection *conn)
{
   // g_markup_parse_context_free() whines if passed a NULL
   if (NULL != conn->parseContext) {
      g_markup_parse_context_free(conn->parseContext);
      conn->parseContext = NULL;
   }

   Proto_FreeRequest(conn->curRequest);
   conn->curRequest = NULL;
}


/*
 ******************************************************************************
 * ServiceReplyTooManyConnections --                                     */ /**
 *
 * Send the too many connection error message to the client
 *
 * @param[in]  conn                       The connection.
 * @param[in]  connLimit                  The concurrent connection limit
 *
 ******************************************************************************
 */

void
ServiceReplyTooManyConnections(ServiceConnection *conn,
                               int connLimit)
{
   gchar *packet =
      ProtoMakeErrorReplyInt(conn, 0,
                             VGAUTH_E_TOO_MANY_CONNECTIONS,
                            "The user exceeded its max number of connections");

   (void) ServiceNetworkWriteData(conn, strlen(packet), packet);

   VGAUTH_LOG_WARNING("User %s exceeding concurrent connection limit of "
                      "%d connections (connection ID is %d)",
                      conn->userName, connLimit, conn->connId);

   g_free(packet);
}
 0707010000045E000081A40000000000000000000000016822550500000DB8000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/random.c    /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file random.c --
 *
 *    Random byte generation.
 */

#include "serviceInt.h"
#include "VGAuthLog.h"

#ifdef _WIN32
#include <wincrypt.h>
#else
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#define GENERIC_RANDOM_DEVICE "/dev/urandom"
#endif

/*
 ******************************************************************************
 * ServiceRandomBytes --                                                 */ /**
 *
 * Fills a buffer with random bytes.
 * Taken with minor changes from bora/lib/misc/random.c
 *
 * @param[in]  size      The size of the buffer.
 * @param[out] buffer    The buffer to fill.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceRandomBytes(int size,
                   guchar *buffer)
{
#if defined(_WIN32)
   HCRYPTPROV csp;

   if (CryptAcquireContext(&csp, NULL, NULL, PROV_RSA_FULL,
                           CRYPT_VERIFYCONTEXT) == FALSE) {
      VGAUTH_LOG_ERR_WIN("CryptAcquireContext failed\n");
      return VGAUTH_E_FAIL;
   }

   if (CryptGenRandom(csp, size, buffer) == FALSE) {
      CryptReleaseContext(csp, 0);
      VGAUTH_LOG_ERR_WIN("CryptGenRandom failed\n");
      return VGAUTH_E_FAIL;
   }

   if (CryptReleaseContext(csp, 0) == FALSE) {
      VGAUTH_LOG_ERR_WIN("CryptReleaseContext failed\n");
      return VGAUTH_E_FAIL;
   }
#else
   int fd;

   /*
    * We use /dev/urandom and not /dev/random because it is good enough and
    * because it cannot block. --hpreg
    */

   fd = open(GENERIC_RANDOM_DEVICE, O_RDONLY);
   if (fd == -1) {
      Warning("%s: Failed to open random device: %d\n", __FUNCTION__, errno);

      return VGAUTH_E_FAIL;
   }

   /* Although /dev/urandom does not block, it can return short reads. */
   while (size > 0) {
      gsize bytesRead = read(fd, buffer, size);

      if (bytesRead == 0 || (bytesRead == -1 && errno != EINTR)) {
         int error = errno;

         close(fd);
         Warning("%s: read error: %d\n", __FUNCTION__, error);

         return VGAUTH_E_FAIL;
      }
      if (bytesRead > 0) {
         size -= bytesRead;
         buffer = ((guchar *) buffer) + bytesRead;
      }
   }

   /*
    * Note we don't retry the close() on an EINTR:
    * http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
    *
    * This'll probably turn out to be in conflict with some other flavor
    * of *ix.
    */
   if (close(fd) == -1) {
      Warning("%s: Failed to close: %d\n", __FUNCTION__, errno);

      return VGAUTH_E_FAIL;
   }
#endif

   return VGAUTH_E_OK;
}
0707010000045F000081A4000000000000000000000001682255050000CF30000000000000000000000000000000000000004500000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/saml-xmlsec1.c  /*********************************************************
 * Copyright (c) 2016-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file saml-xmlsec1.c
 *
 * Code for authenticating users based on SAML tokens.
 */

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include <libxml/tree.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/catalog.h>
#include <libxml/xmlschemas.h>
#include <libxml/xmlIO.h>
#include <libxml/uri.h>

// PR 3416639, xmlFile* APIs were deprecated in libxml2 2.13.0
// Ignore the deprecated warnings after updating libxml2 to 2.13.3
// ToDo: Define the deprecated APIs locally and remove the
// XML_IGNORE_DEPRECATION_WARNINGS
#ifdef _WIN32
#define XML_IGNORE_DEPRECATION_WARNINGS \
    __pragma(warning(push)) \
    __pragma(warning(disable : 4996))
#define XML_POP_WARNINGS __pragma(warning(pop))
#else
#define XML_IGNORE_DEPRECATION_WARNINGS \
    _Pragma("GCC diagnostic push") \
    _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#define XML_POP_WARNINGS _Pragma("GCC diagnostic pop")
#endif

#include <xmlsec/xmlsec.h>
#include <xmlsec/xmltree.h>
#include <xmlsec/xmldsig.h>
#include <xmlsec/templates.h>
#include <xmlsec/crypto.h>
#include <xmlsec/errors.h>

#include <glib.h>

#include "prefs.h"
#include "serviceInt.h"
#include "certverify.h"
#include "vmxlog.h"

/*
 * XXX
 *
 * Optimization idea: stash a hash (SHA512) of a valid token, and bypass
 * the full assertion process when we see that token again. The expiration
 * date of the token must also be saved off (and beware the time skew issue).
 *
 * Note that there's some extra complexity here:
 *
 * 1 - AddAlias sets up a cert/user mapping
 * 2 - a SAML token is used (and cached) using this cert/user combo
 * 3 - RemoveAlias removes the combo
 * 4 - the cached token still works
 *
 * So the cache should only bypass the token validation, not the certificate
 * check in ServiceVerifyAndCheckTrustCertChainForSubject()
 *
 * Also TBD is how much this buys us in the real world.  With short
 * token lifetimes, its less interesting.  Its also possible that
 * it will have no measureable affect because the token verification
 * will be lost in the noise of the API plumbing from VC->hostd->VMX->tools.
 *
 * The security folks have signed off on this, so long as we store only
 * in memory.
 *
 */

static int gClockSkewAdjustment = VGAUTH_PREF_DEFAULT_CLOCK_SKEW_SECS;
static gboolean gAllowUnrelatedCerts = FALSE;
static xmlSchemaPtr gParsedSchemas = NULL;
static xmlSchemaValidCtxtPtr gSchemaValidateCtx = NULL;

#define CATALOG_FILENAME            "catalog.xml"
#define SAML_SCHEMA_FILENAME        "saml-schema-assertion-2.0.xsd"


/*
 ******************************************************************************
 * UserXmlFileOpen --                                                    */ /**
 *
 * User defined version of libxml2 export xmlFileOpen.
 *
 * This function opens a file with its unescaped name only.
 *
 * xmlInitParser() calls xmlRegisterDefaultInputCallbacks() which calls
 *    xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
 *                              xmlFileRead, xmlFileClose)
 *
 * UserXmlFileOpen is registered at the end of the xmlInputCallback table by
 *    xmlRegisterInputCallbacks(xmlFileMatch, UserXmlFileOpen,
 *                              xmlFileRead, xmlFileClose)
 *
 * Based on libxml2 xmlIO.c, precedence is given to user defined handlers.
 *
 * @param[in]  filename          The URI file name.
 *
 * @return A handler or NULL in case of failure.
 ******************************************************************************
 */

static void *
UserXmlFileOpen(const char *filename)
{
   char *unescaped;
   void *retval = NULL;

   g_debug("%s: Incoming file name is \"%s\"\n", __FUNCTION__, filename);

   unescaped = xmlURIUnescapeString(filename, 0, NULL);
   if (unescaped != NULL) {
      g_debug("%s: Opening file \"%s\"\n", __FUNCTION__, unescaped);
XML_IGNORE_DEPRECATION_WARNINGS
      retval = xmlFileOpen(unescaped);
XML_POP_WARNINGS
      xmlFree(unescaped);
   }

   if (retval == NULL) {
      g_warning("%s: Failed to open file \"%s\"\n", __FUNCTION__, filename);
      /*
       * Do not retry xmlFileOpen(filename) here.
       * Calling system API to open escaped file paths is risky. This can
       * cause unexpected not-secured paths being accessed and expose
       * privilege escalation vulnerabilities.
       */
   }

   return retval;
}


/*
 * Hack to test expired tokens and by-pass the time checks.
 *
 * Turning this on allows the VerifySAMLTokenFileTest() unit test
 * which reads a token from the file to be fed an old token (eg
 * from a log) and not have it fail because of the time-based
 * assertions.
 *
 * Note that setting this *will* cause negative tests looking for
 * time checks to fail.
 */
/* #define TEST_VERIFY_SIGN_ONLY 1 */

/*
 ******************************************************************************
 * XmlErrorHandler --                                                    */ /**
 *
 * Error handler for xml2.
 *
 * @param[in]  ctx           Context (unused).
 * @param[in]  msg           The error message in printf format.
 * @param[in]  ...           Any args for the msg.
 *
 ******************************************************************************
 */

static void
XmlErrorHandler(void *ctx,
                const char *msg,
                ...)
{
   gchar msgStr[1024];
   va_list argPtr;
   va_start(argPtr, msg);
   vsnprintf(msgStr, sizeof msgStr, msg, argPtr);
   va_end(argPtr);

   /*
    * Treat all as warning.
    */
   g_warning("XML Error: %s", msgStr);
   VMXLog_Log(VMXLOG_LEVEL_WARNING, "XML Error: %s", msgStr);
}


/*
 ******************************************************************************
 * XmlSecErrorHandler --                                                 */ /**
 *
 * Error handler for xmlsec.
 *
 * @param[in]  file          The name of the file generating the error.
 * @param[in]  line          The line number generating the error.
 * @param[in]  func          The function generating the error.
 * @param[in]  errorObject   The error specific object.
 * @param[in]  errorSubject  The error specific subject.
 * @param[in]  reason        The error code.
 * @param[in]  msg           The additional error message.
 *
 ******************************************************************************
 */

static void
XmlSecErrorHandler(const char *file,
                   int line,
                   const char *func,
                   const char *errorObject,
                   const char *errorSubject,
                   int reason,
                   const char *msg)
{
   /*
    * Treat all as warning.  */
   g_warning("XMLSec Error: %s:%s(line %d) object %s"
             " subject %s reason: %d, msg: %s",
             file, func, line,
             errorObject ? errorObject : "<UNSET>",
             errorSubject ? errorSubject : "<UNSET>",
             reason, msg);
   VMXLog_Log(VMXLOG_LEVEL_WARNING,
              "XMLSec Error: %s:%s(line %d) object %s"
              " subject %s reason: %d, msg: %s",
              file, func, line,
              errorObject ? errorObject : "<UNSET>",
              errorSubject ? errorSubject : "<UNSET>",
              reason, msg);
}


/*
 ******************************************************************************
 * LoadCatalogAndSchema --                                               */ /**
 *
 * Loads the schemas for validation.
 *
 * Using a catalog here ala xmllint.  Another option would be an
 * additional schema acting like a catalog.
 *
 * @param[in]   catPath       Path to the catalog file.
 * @param[in]   schemaPath    Path to the SAML schema file.
 *
 * return TRUE on success
 ******************************************************************************
 *
 */

static gboolean
LoadCatalogAndSchema(void)
{
   int ret;
   gboolean retVal = FALSE;
   xmlSchemaParserCtxtPtr ctx = NULL;
   gchar *catalogPath = NULL;
   gchar *schemaPath = NULL;
   gchar *schemaDir = NULL;

   schemaDir = Pref_GetString(gPrefs,
                              VGAUTH_PREF_SAML_SCHEMA_DIR,
                              VGAUTH_PREF_GROUP_NAME_SERVICE,
                              NULL);

   if (NULL == schemaDir) {
#ifdef _WIN32
      /*
       * To make life easier for the Windows installer, assume
       * the schema directory is next to the executable.  Also
       * check in ../ in case we're in a dev environment.
       */
      schemaDir = g_build_filename(gInstallDir, "schemas", NULL);
      if (!(g_file_test(schemaDir, G_FILE_TEST_EXISTS) &&
            g_file_test(schemaDir, G_FILE_TEST_IS_DIR))) {

         gchar *newDir = g_build_filename(gInstallDir, "..", "schemas", NULL);

         Debug("%s: schemas not found in Windows install loc '%s',"
               " trying dev location of '%s'\n", __FUNCTION__, schemaDir, newDir);

         g_free(schemaDir);
         schemaDir = newDir;
      }
#else
      /*
       * TODO -- clean this up to make a better default for Linux.
       */
      schemaDir = g_build_filename(gInstallDir, "..", "schemas", NULL);
#endif
   }
   Log("%s: Using '%s' for SAML schemas\n", __FUNCTION__, schemaDir);
   catalogPath = g_build_filename(schemaDir, CATALOG_FILENAME, NULL);
   schemaPath = g_build_filename(schemaDir, SAML_SCHEMA_FILENAME, NULL);

   /*
    * Skip calling xmlInitializeCatalog().
    *
    * xmlLoadCatalog() just adds to the default catalog, and won't return an
    * error if it doesn't exist so long as a default catalog is set.
    *
    * So confidence check its existence.
    */
   if (!g_file_test(catalogPath, G_FILE_TEST_EXISTS)) {
      g_warning("Error: catalog file not found at \"%s\"\n", catalogPath);
      retVal = FALSE;
      goto done;
   }
   ret = xmlLoadCatalog(catalogPath);
   if (ret < 0) {
      g_warning("Error: Failed to load catalog at \"%s\"\n", catalogPath);
      retVal = FALSE;
      goto done;
   }

   ctx = xmlSchemaNewParserCtxt(schemaPath);
   if (NULL == ctx) {
      g_warning("Failed to create schema parser context\n");
      retVal = FALSE;
      goto done;
   }

   xmlSchemaSetParserErrors(ctx,
                            (xmlSchemaValidityErrorFunc) XmlErrorHandler,
                            (xmlSchemaValidityErrorFunc) XmlErrorHandler,
                            NULL);
   gParsedSchemas = xmlSchemaParse(ctx);
   if (NULL == gParsedSchemas) {
      /*
       * This shouldn't happen.  Means somebody mucked with our
       * schemas.
       */
      g_warning("Error: Failed to parse schemas\n");
      retVal = FALSE;
      goto done;
   }

   /*
    * Set up the validaton context for later use.
    */
   gSchemaValidateCtx = xmlSchemaNewValidCtxt(gParsedSchemas);
   if (NULL == gSchemaValidateCtx) {
      g_warning("Failed to create schema validation context\n");
      retVal = FALSE;
      goto done;
   }
   xmlSchemaSetValidErrors(gSchemaValidateCtx,
                           XmlErrorHandler,
                           XmlErrorHandler,
                           NULL);

   retVal = TRUE;
done:
   if (NULL != ctx) {
      xmlSchemaFreeParserCtxt(ctx);
   }
   g_free(catalogPath);
   g_free(schemaPath);
   g_free(schemaDir);

   return retVal;
}


/*
 ******************************************************************************
 * FreeSchemas --                                                        */ /**
 *
 * Frees global schema data.
 ******************************************************************************
 *
 */

static void
FreeSchemas(void)
{
   if (NULL != gSchemaValidateCtx) {
      xmlSchemaFreeValidCtxt(gSchemaValidateCtx);
      gSchemaValidateCtx = NULL;
   }
   if (NULL != gParsedSchemas) {
      xmlSchemaFree(gParsedSchemas);
      gParsedSchemas = NULL;
   }
}


/*
 ******************************************************************************
 * LoadPrefs --                                                          */ /**
 *
 * Loads any preferences SAML cares about.
 ******************************************************************************
 *
 */

static void
LoadPrefs(void)
{
   gClockSkewAdjustment = Pref_GetInt(gPrefs, VGAUTH_PREF_CLOCK_SKEW_SECS,
                                      VGAUTH_PREF_GROUP_NAME_SERVICE,
                                      VGAUTH_PREF_DEFAULT_CLOCK_SKEW_SECS);
    Log("%s: Allowing %d of clock skew for SAML date validation\n",
        __FUNCTION__, gClockSkewAdjustment);
    gAllowUnrelatedCerts = Pref_GetBool(gPrefs,
                                        VGAUTH_PREF_ALLOW_UNRELATED_CERTS,
                                        VGAUTH_PREF_GROUP_NAME_SERVICE,
                                        FALSE);
}


/*
 ******************************************************************************
 * SAML_Init --                                                          */ /**
 *
 * Performs any initialization needed for SAML processing.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
SAML_Init(void)
{
   int ret;

   /*
    * Init the xml parser
    */
   xmlInitParser();

   /*
    * Verify the xml2 version -- if this is too old
    * its fatal, so we may want to use a different check.
    */
   LIBXML_TEST_VERSION

   /* set up the xml2 error handler */
   xmlSetGenericErrorFunc(NULL, XmlErrorHandler);

   /*
    * Register user defined UserXmlFileOpen
    */
XML_IGNORE_DEPRECATION_WARNINGS
   xmlRegisterInputCallbacks(xmlFileMatch, UserXmlFileOpen,
                             xmlFileRead, xmlFileClose);
XML_POP_WARNINGS

   /*
    * Load schemas
    */
   if (!LoadCatalogAndSchema()) {
      g_warning("Failed to load schemas\n");
      return VGAUTH_E_FAIL;
   }

   /* init xmlsec */
   ret = xmlSecInit();
   if (ret < 0) {
      g_warning("xmlSecInit() failed %d\n", ret);
      return VGAUTH_E_FAIL;
   }

   /*
    * set up the error callback
    */
   xmlSecErrorsSetCallback(XmlSecErrorHandler);

   /*
    * version check xmlsec1
    */
   if (xmlSecCheckVersion() != 1) {
      g_warning("Error: xmlsec1 lib version mismatch\n");
      return VGAUTH_E_FAIL;
   }

#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
    /*
     * Load the openssl crypto engine if we are supporting dynamic
     * loading for xmlsec-crypto libraries.
     */
    if(xmlSecCryptoDLLoadLibrary("openssl") < 0) {
        g_warning("Error: unable to load openssl xmlsec-crypto library.\n "
                  "Make sure that you have xmlsec1-openssl installed and\n"
                  "check shared libraries path\n"
                  "(LD_LIBRARY_PATH) environment variable.\n");
        VMXLog_Log(VMXLOG_LEVEL_WARNING,
                   "Error: unable to load openssl xmlsec-crypto library.\n "
                   "Make sure that you have xmlsec1-openssl installed and\n"
                   "check shared libraries path\n"
                   "(LD_LIBRARY_PATH) environment variable.\n");
      return VGAUTH_E_FAIL;
    }
#endif /* XMLSEC_CRYPTO_DYNAMIC_LOADING */

   /*
    * init the xmlsec1 crypto app layer
    */
   ret = xmlSecCryptoAppInit(NULL);
   if (ret < 0) {
      g_warning("xmlSecCryptoAppInit() failed %d\n", ret);
      return VGAUTH_E_FAIL;
   }

   /*
    * Do crypto-engine specific initialization
    */
   ret = xmlSecCryptoInit();
   if (ret < 0) {
      g_warning("xmlSecCryptoInit() failed %d\n", ret);
      return VGAUTH_E_FAIL;
   }

   /*
    * Load prefs
    */
   LoadPrefs();

   Log("%s: Using xmlsec1 %d.%d.%d for XML signature support\n",
       __FUNCTION__, XMLSEC_VERSION_MAJOR, XMLSEC_VERSION_MINOR,
       XMLSEC_VERSION_SUBMINOR);
   VMXLog_Log(VMXLOG_LEVEL_WARNING,
              "%s: Using xmlsec1 %d.%d.%d for XML signature support\n",
              __FUNCTION__, XMLSEC_VERSION_MAJOR, XMLSEC_VERSION_MINOR,
              XMLSEC_VERSION_SUBMINOR);

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * SAML_Shutdown --                                                      */ /**
 *
 * Performs any clean-up of resources allocated by SAML code.
 *
 ******************************************************************************
 */

void
SAML_Shutdown()
{
   FreeSchemas();
   xmlSecCryptoShutdown();
   xmlSecCryptoAppShutdown();
   xmlSecShutdown();

#if 0
   /*
    * This is not thread safe:
    * http://0pointer.de/blog/projects/beware-of-xmlCleanupParser
    * and should only be called just before exit()
    * Because of this, our symbol-checker hates it:  See PR 407137
    */
   xmlCleanupParser();
#endif
}


/*
 ******************************************************************************
 * SAML_Reload --                                                        */ /**
 *
 * Reload any in-memory state used by the SAML module.
 *
 ******************************************************************************
 */

void
SAML_Reload()
{
   FreeSchemas();
   LoadPrefs();
   LoadCatalogAndSchema();
}


/*
 ******************************************************************************
 * FreeCertArray --                                                      */ /**
 *
 * Frees a simple array of pemCert.
 *
 * @param[in]  num      Number of certs in array.
 * @param[in]  certs    Array of certs to free.
 *
 ******************************************************************************
 */
static void
FreeCertArray(int num,
              gchar **certs)
{
   int i;

   for (i = 0; i < num; i++) {
      g_free(certs[i]);
   }
   g_free(certs);
}


/*
 * Public API for use by the unit tests.
 */

void
SAML_FreeCertArray(int num,
                   gchar **certs)
{
   FreeCertArray(num, certs);
}


/*
 ******************************************************************************
 * FindAttrValue --                                                      */ /**
 *
 * Returns the value of a attribute in an XML node.
 *
 * @param[in] node     XML subtree node.
 * @param[in] attrName Name of the attribute.
 *
 * @return Attribute value if exists.  The caller must free this with xmlFree().
 *
 ******************************************************************************
 */

static xmlChar *
FindAttrValue(const xmlNodePtr node,
              const gchar *attrName)
{
   xmlAttrPtr attr;
   xmlChar *name;

   /*
    * Find the attribute
    */
   attr = xmlHasProp(node, attrName);
   if ((attr == NULL) || (attr->children == NULL)) {
      return NULL;
   }

   /*
    * get the attribute value
    */
   name = xmlNodeListGetString(node->doc, attr->children, 1);

   return name;
}


/*
 ******************************************************************************
 * RegisterID --                                                         */ /**
 *
 * Register the document ID with the xml parser.
 *
 * This needs to be done if the document ID doesn't use the standard.
 * Otherwise the signing fails when setting up the reference.
 * SAML likes using 'ID' intead of the default 'xml:id', so
 * this is needed for both signing and verification.
 *
 * This is a no-op if the schemas have been loaded since they
 * set it up.
 *
 * See xmlsec1 FAQ 3.2
 *
 * Based on https://www.aleksey.com/pipermail/xmlsec/2003/001768.html
 *
 * @param[in]  node        The XML node on which to set the ID.
 * @param[in]  idName      The name of the ID.
 *
 * @return TRUE on success.
 ******************************************************************************
 */

static gboolean
RegisterID(xmlNodePtr node,
           const xmlChar *idName)
{
   xmlAttrPtr attr;
   xmlAttrPtr tmp;
   xmlChar *name;

   /*
    * find pointer to id attribute
    */
   attr = xmlHasProp(node, idName);
   if ((attr == NULL) || (attr->children == NULL)) {
      return FALSE;
   }

   /*
    * get the attribute (id) value
    */
   name = xmlNodeListGetString(node->doc, attr->children, 1);
   if (name == NULL) {
      return FALSE;
   }

   /*
    * check that we don't have the id already registered
    */
   tmp = xmlGetID(node->doc, name);
   if (tmp != NULL) {
      xmlFree(name);
      /* no-op if its already there */
      return TRUE;
   }

   /*
    * finally register id
    */
   xmlAddID(NULL, node->doc, name, attr);

   xmlFree(name);
   return TRUE;
}


/*
 ******************************************************************************
 * FindNodeByName --                                                     */ /**
 *
 * Searches under the specified node for one with a matching name.
 *
 * @param[in] root     XML subtree root under which to search.
 * @param[in] nodeName Name of node to find.
 *
 * @return matching xmlNodePtr or NULL.  Caller should not free this node.
 *
 ******************************************************************************
 */

static xmlNodePtr
FindNodeByName(xmlNodePtr root,
               char *nodeName)
{
   xmlNodePtr cur;

   cur = root->children;
   while (cur != NULL) {
      if (cur->type == XML_ELEMENT_NODE) {
         if (xmlStrEqual(nodeName, cur->name)) {
            break;
         }
      }
      cur = cur->next;
   }

   return cur;
}


/*
 ******************************************************************************
 * FindAllNodesByName --                                                 */ /**
 *
 * Searches under the specified node for all with a matching name.
 *
 * @param[in]  root     XML subtree root under which to search.
 * @param[in]  nodeName Name of node to find.
 * @param[out] nodeName Array of matches.
 *
 * @return Number of matching nodes.  Caller needs to free the array
 *         of Nodes, but not the nodes themselves.
 *
 ******************************************************************************
 */

static int
FindAllNodesByName(xmlNodePtr root,
                   char *nodeName,
                   xmlNodePtr **nodes)
{
   xmlNodePtr cur;
   xmlNodePtr *list = NULL;
   int count = 0;

   cur = root->children;
   while (cur != NULL) {
      if (cur->type == XML_ELEMENT_NODE) {
         if (xmlStrEqual(nodeName, cur->name)) {
            list = g_realloc_n(list,
                               sizeof(xmlNodePtr),
                               count + 1);
            list[count++] = cur;
         }
      }
      cur = cur->next;
   }
   *nodes = list;

   return count;
}


/*
 ******************************************************************************
 * ValidateDoc --                                                        */ /**
 *
 * Validates the XML document against the schema.
 *
 * @param[in]  doc         Parsed XML document.
 *
 ******************************************************************************
 */

static gboolean
ValidateDoc(xmlDocPtr doc)
{
   int ret;

   ret = xmlSchemaValidateDoc(gSchemaValidateCtx, doc);
   if (ret < 0) {
      g_warning("Failed to validate doc against schema\n");
   }

   return (ret == 0) ? TRUE : FALSE;
}


/*
 ******************************************************************************
 * CheckTimeAttr --                                                      */ /**
 *
 * Checks that the given attribute with the given name is a timestamp and
 * compares it against the current time.
 *
 * @param[in]  node         The node containing the attribute.
 * @param[in]  attrName     The name of the attribute.
 * @param[in]  notBefore    Whether the condition given by the attribute
 *                          should be in the past or 'now' (TRUE).
 *
 ******************************************************************************
 */

static gboolean
CheckTimeAttr(const xmlNodePtr node,
              const gchar *attrName,
              gboolean notBefore)
{
   xmlChar *timeAttr;
   GTimeVal attrTime;
   GTimeVal now;
   glong diff;
   gboolean retVal;

   timeAttr = FindAttrValue(node, attrName);
   if ((NULL == timeAttr) || (0 == *timeAttr)) {
      /*
       * The presence of all time restrictions in SAML are optional, so if
       * the attribute is not present, that is fine.
       */
      retVal = TRUE;
      goto done;
   }

   if (!g_time_val_from_iso8601(timeAttr, &attrTime)) {
      g_warning("%s: Could not parse %s value (%s).\n", __FUNCTION__, attrName,
                timeAttr);
      retVal = FALSE;
      goto done;
   }

   g_get_current_time(&now);

   /*
    * Check the difference, doing the math so that a positive
    * value is bad.  Ignore the micros field since precision
    * is unnecessary here because we see unsynced clocks in
    * the real world.
    */
   if (notBefore) {
      // expect time <= now
      diff = attrTime.tv_sec - now.tv_sec;
   } else {
      // expect now <= time
      diff = now.tv_sec - attrTime.tv_sec;
   }

   /*
    * A negative value is fine, a postive value
    * greater than the clock skew range is bad.
    */
   if (diff > gClockSkewAdjustment) {
      g_warning("%s: FAILED SAML assertion (timeStamp %s, delta %d) %s.\n",
                __FUNCTION__, timeAttr, (int) diff,
                notBefore ? "is not yet valid" : "has expired");
      VMXLog_Log(VMXLOG_LEVEL_WARNING,
                 "%s: FAILED SAML assertion (timeStamp %s, delta %d) %s.\n",
                __FUNCTION__, timeAttr, (int) diff,
                notBefore ? "is not yet valid" : "has expired");
      retVal = FALSE;
      goto done;
   }

   retVal = TRUE;

done:
   if (timeAttr) {
      xmlFree(timeAttr);
   }
   return retVal;
}


/*
 ******************************************************************************
 * CheckAudience --                                                      */ /**
 *
 * Checks whether the given audience URI refers to this machine.
 *
 * @param[in]  audience   An audience URI that a token is targetted for.
 *
 * @return TRUE if the audience URI refers to this machine, FALSE otherwise.
 *
 ******************************************************************************
 */

static gboolean
CheckAudience(const xmlChar *audience)
{
   gboolean ret;

   /*
    * Our SSO server doesn't set Recipient, so this only gets used by test code
    * whch uses a simple hostname check.
    *
    * Something like a VC UUID might be more accurate in a virtual
    * machine.
    */

   ret = strstr(audience, g_get_host_name()) != NULL;
   g_debug("%s: audience check: token: '%s', host: '%s' ? %d\n",
           __FUNCTION__,
           audience, g_get_host_name(), ret);
   return ret;
}


/*
 ******************************************************************************
 * VerifySubject --                                                      */ /**
 *
 * Extracts the name of the subject and enforces any conditions in
 * SubjectConfirmation elements.
 * Subjects are described in section 2.4 of the SAML Core specification.
 *
 * Example Subject XML:
 * <saml:Subject>
 *    <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
 *       scott@example.org
 *    </saml:NameID>
 *    <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
 *       <saml:SubjectConfirmationData NotOnOrAfter="2011-12-08T00:42:10Z">
 *       </saml:SubjectConfirmationData>
 *    </saml:SubjectConfirmation>
 * </saml:Subject>
 *
 * @param[in]     doc         The parsed SAML token.
 * @param[out]    subjectRet  The Subject NameId.  Should be g_free()d by
 *                            caller.
 *
 * @return TRUE if the conditions in at least one SubjectConfirmation is met,
 *         FALSE otherwise.
 *
 ******************************************************************************
 */

static gboolean
VerifySubject(xmlDocPtr doc,
              gchar **subjectRet)
{
   xmlNodePtr subjNode;
   xmlNodePtr nameIDNode;
   xmlNodePtr child;
   gchar *subjectVal = NULL;
   gboolean validSubjectFound = FALSE;
   xmlChar *tmp;

   if (NULL != subjectRet) {
      *subjectRet = NULL;
   }

   subjNode = FindNodeByName(xmlDocGetRootElement(doc), "Subject");
   if (NULL == subjNode) {
      g_warning("No Subject node found\n");
      goto done;
   }

   /*
    * Pull out the NameID for later checks elsewhere.
    */
   nameIDNode = FindNodeByName(subjNode, "NameID");
   if (NULL == nameIDNode) {
      g_warning("%s: NameID not found in Subject\n", __FUNCTION__);
      goto done;
   }
   tmp = xmlNodeGetContent(nameIDNode);
   subjectVal = g_strdup(tmp);
   xmlFree(tmp);

   /*
    * Find all the SubjectConfirmation nodes and see if at least one
    * can be validated.
    */
   for (child = subjNode->children; child != NULL; child = child->next) {
      xmlChar *method;
      xmlNodePtr subjConfirmData;

      if (child->type == XML_ELEMENT_NODE) {
         if (!xmlStrEqual(child->name, "SubjectConfirmation")) {
            continue;
         }
         method = FindAttrValue(child, "Method");
         if ((NULL == method) || (0 == *method)) {
            // should not happen since this is required
            g_warning("%s: Missing SubjectConfirmation method\n", __FUNCTION__);
            xmlFree(method);
            goto done;
         }
         if (!xmlStrEqual(method, "urn:oasis:names:tc:SAML:2.0:cm:bearer")) {
            g_warning("%s: method %s not bearer\n", __FUNCTION__, method);
            xmlFree(method);
            continue;
         }
         xmlFree(method);

         subjConfirmData = FindNodeByName(child, "SubjectConfirmationData");
         if (NULL != subjConfirmData) {
            xmlChar *recipient;

            if (!CheckTimeAttr(subjConfirmData, "NotBefore", TRUE) ||
                !CheckTimeAttr(subjConfirmData, "NotOnOrAfter", FALSE)) {
               g_warning("%s: subjConfirmData time check failed\n",
                         __FUNCTION__);
               continue;
            }

            /*
             * Recipient isn't always there.
             */
            recipient = FindAttrValue(subjConfirmData, "Recipient");
            if ((NULL != recipient) && (0 != *recipient) &&
                !CheckAudience(recipient)) {
               g_debug("%s: failed recipient check\n", __FUNCTION__);
               xmlFree(recipient);
               continue;
            }
            xmlFree(recipient);
         }

         /*
          * passed all the checks, we have a match so kick out
          */
         validSubjectFound = TRUE;
         break;
      }
   }

done:
   if (validSubjectFound && (NULL != subjectRet)) {
      *subjectRet = subjectVal;
   } else {
      g_free(subjectVal);
   }
   return validSubjectFound;
}


/*
 ******************************************************************************
 * VerifyConditions --                                                   */ /**
 *
 * Enforces conditions specified by the "saml:Conditions" element
 * under the root element.
 * Conditions are described in section 2.5 of the SAML Core specification.
 *
 * Example Conditions XML:
 *    <saml:Conditions NotBefore="2011-12-08T00:41:10Z"
 *     NotOnOrAfter="2011-12-08T00:42:10Z">
 *       <saml:AudienceRestriction>
 *          <saml:Audience>https://sp.example.com/SAML2</saml:Audience>
 *       </saml:AudienceRestriction>
 *    </saml:Conditions>
 *
 * @param[in]  doc  The parsed SAML token.
 *
 * @return TRUE if the conditions are met; FALSE otherwise.
 *
 ******************************************************************************
 */

static gboolean
VerifyConditions(xmlDocPtr doc)
{
   xmlNodePtr condNode;

   /*
    * There should be at most one Conditions element and the schema checking
    * done by the parser should enforce that.
    */
   condNode = FindNodeByName(xmlDocGetRootElement(doc), "Conditions");
   if (NULL == condNode) {
      // Conditions are optional.
      g_debug("%s: No Conditions found, accepting\n", __FUNCTION__);
      return TRUE;
   }

   if (!CheckTimeAttr(condNode, "NotBefore", TRUE) ||
       !CheckTimeAttr(condNode, "NotOnOrAfter", FALSE)) {
      g_warning("%s: Time Conditions failed!\n", __FUNCTION__);
      return FALSE;
   }

   /*
    * <Condition> is a generic element, intended as an extension point.
    * We don't know about any. According to the general processng rules, if
    * we find a condition we don't know about, the result of the validation
    * is "indeterminate" and we should reject the assertion.
    */
   if (FindNodeByName(condNode, "Condition") != NULL) {
      g_warning("%s: Unrecognized condition found!\n", __FUNCTION__);
      return FALSE;
   }

   /*
    * <AudienceRestriction> defines a set a URIs that describe what
    * audience the assertioned is addressed to or intended for.
    * But it's very generic. From the spec (section 2.5.1.4):
    *    A URI reference that identifies an intended audience. The URI
    *    reference MAY identify a document that describes the terms and
    *    conditions of audience membership. It MAY also contain the unique
    *    identifier URI from a SAML name identifier that describes a system
    *    entity.
    *
    * Our SSO server doesn't set it, so no point in checking it.
    */

#if 0
   // TODO nothing looks at this
   /*
    * <OneTimeUse> element is specified to disallow caching. We don't
    * cache, so it doesn't affect our validation.
    * However, we need to communicate it to clients so they do not cache.
    */
   oneTimeUse = (FindChildByName(condNode, "OneTimeUse")
                       != NULL);
#endif

   /*
    * <ProxyRestriction> only applies if a service wants to make their own
    * assertions based on a SAML assertion. That should not apply here.
    */

   return TRUE;
}


/*
 ******************************************************************************
 * BuildCertChain --                                                     */ /**
 *
 * Pulls the certs out of the parsed SAML token, adds them to the
 * key manager, and returns them as a list.
 *
 * @param[in]  x509Node     x509 data node.
 * @param[in]  mgr          KeyManager
 * @param[out] numCerts     Number of certs being returned.
 * @param[out] certChain    Array containing the certs in OpenSSL PEM
 *                          format.  Array and contents must be g_free()d
 *                          by caller.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

static gboolean
BuildCertChain(xmlNodePtr x509Node,
               xmlSecKeysMngrPtr mgr,
               int *numCerts,
               gchar ***certChain)
{
   gboolean bRet = FALSE;
   xmlNodePtr *x509CertNodes = NULL;
   int num;
   int i;
   int ret;
   gchar **certList = NULL;

   num = FindAllNodesByName(x509Node,
                            (char *) xmlSecNodeX509Certificate,
                            &x509CertNodes);
   if (num == 0) {
      g_warning("Missing x509 certificate node(s)\n");
      goto done;
   }

   certList = g_malloc0_n(num + 1, sizeof(gchar *));

   for (i = 0; i < num; i++) {
      gchar *pemCert = NULL;
      xmlChar *base64Cert;

      base64Cert = xmlNodeGetContent(x509CertNodes[i]);
      if (NULL == base64Cert) {
         g_warning("Missing x509 certificate base64 data\n");
         goto done;
      }

      /*
       * Turn the raw base64 into PEM.  Thanks for being so anal,
       * OpenSSL.
       */
      pemCert = CertVerify_EncodePEMForSSL(base64Cert);
      xmlFree(base64Cert);

      /*
       * Add cert to the keymanager.
       */
      /* coverity[string_null] */
      ret = xmlSecCryptoAppKeysMngrCertLoadMemory(mgr,
                                                  pemCert,
                                                  (xmlSecSize) strlen(pemCert),
                                                  xmlSecKeyDataFormatPem,
                                                  xmlSecKeyDataTypeTrusted);
      if (ret < 0) {
         g_warning("%s: Failed to add cert to key manager\n", __FUNCTION__);
         g_warning("PEM cert: %s\n", pemCert);
         VMXLog_Log(VMXLOG_LEVEL_WARNING,
                    "%s: Failed to add cert to key manager\n", __FUNCTION__);
         /*
          * XXX
          *
          * Certificates can have data (eg email addresses)
          * we don't want to log those to the VMX due to privacy concerns.
          * So let's not log to VMX at all until we have a reliable way to
          * cleanse them -- assuming that doesn't make them worthless
          * since the data won't match anything in the aliasStore
          * or a SAML token.
          */
#if 0
           VMXLog_Log(VMXLOG_LEVEL_WARNING, "PEM cert: %s\n", pemCert);
#endif
         goto done;
      }

      /*
       * add pemCert to the returned list
       */
      certList[i] = pemCert;
   }

   bRet = TRUE;
   *numCerts = num;
   *certChain = certList;

done:
   if (!bRet) {
      FreeCertArray(num, certList);
   }
   g_free(x509CertNodes);

   return bRet;
}


/*
 ******************************************************************************
 * VerifySignature --                                                    */ /**
 *
 * Verifies the signature on an XML document.
 *
 * @param[in]  doc          Parsed XML document.
 * @param[in]  hostVerified If set, signature verifcation can be skipped.
 * @param[out] numCerts     Number of certs in the token.
 * @param[out] certChain    Certs in the token. Caller should g_free() array and
 *                          contents.
 *
 * @return TRUE on success.
 *
 ******************************************************************************
 */

static gboolean
VerifySignature(xmlDocPtr doc,
                gboolean hostVerified,
                int *numCerts,
                gchar ***certChain)
{
   xmlNodePtr dsigNode;
   xmlNodePtr keyInfoNode;
   xmlNodePtr x509Node;
   xmlSecDSigCtxPtr dsigCtx = NULL;
   xmlSecKeysMngrPtr mgr = NULL;
   int ret;
   int num = 0;
   gchar **certList = NULL;
   gboolean bRet;
   gboolean retCode = FALSE;

   *numCerts = 0;
   *certChain = NULL;

   /*
    * First pull out the signature to get to the x509 cert.
    */
   dsigNode = xmlSecFindNode(xmlDocGetRootElement(doc),
                             xmlSecNodeSignature, xmlSecDSigNs);
   if (NULL == dsigNode) {
      g_warning("Missing signature node\n");
      goto done;
   }

   keyInfoNode = xmlSecFindNode(dsigNode, xmlSecNodeKeyInfo,
                                xmlSecDSigNs);
   if (NULL == keyInfoNode) {
      g_warning("Missing KeyInfo node\n");
      goto done;
   }

   x509Node = xmlSecFindNode(keyInfoNode, xmlSecNodeX509Data,
                             xmlSecDSigNs);
   if (NULL == x509Node) {
      g_warning("Missing x509 node\n");
      goto done;
   }

   /*
    * Make a key manager to hold the certs.
    */
   mgr = xmlSecKeysMngrCreate();
   if (mgr == NULL) {
      g_warning("Failed to create key manager");
      goto done;
   }

   ret = xmlSecCryptoAppDefaultKeysMngrInit(mgr);
   if (ret < 0) {
      g_warning("Failed to init key manager\n");
      goto done;
   }


   /*
    * Get the cert chain from the token.
    *
    * xmlsec1 wants to validate the cert chain in the token
    * so it needs the full chain, not just the public key from
    * the first cert.
    *
    * Also save it off for later use by the alias store check.
    */
   bRet = BuildCertChain(x509Node, mgr, &num, &certList);
   if (FALSE == bRet) {
      g_warning("Failed to add cert to key manager\n");
      goto done;
   }

   if (hostVerified) {
      // XXX add a check that the sig is replaced with the expected value
      g_debug("%s: token is hostVerified, skipping signature check",
              __FUNCTION__);
      goto verified;
   }

   /*
    * Create a signature context with the key manager
    */
   dsigCtx = xmlSecDSigCtxCreate(mgr);
   if (NULL == dsigCtx) {
      g_warning("Missing signature node\n");
      goto done;
   }

   /*
    * The vgauth service code expects the id to be "ID".  xmlSec
    * won't handle the URI ref in the signature unless we
    *
    * a) use 'xml:id' (the default) instead of "ID"
    * or
    * b) register the ID
    *
    * We can't control what the SSO server does, so its "b".
    */
   bRet = RegisterID(xmlDocGetRootElement(doc), "ID");
   if (bRet == FALSE) {
      g_warning("Failed to register ID\n");
      goto done;
   }

   /* Use only X509 certs to validate the signature */
   if (xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData),
                        BAD_CAST xmlSecKeyDataX509Id) < 0) {
      g_warning("Failed to limit allowed key data\n");
      goto done;
   }

   /*
    * Verify signature.  This just returns if the signature code worked
    * or not, not if the signature is correct.
    */
   ret = xmlSecDSigCtxVerify(dsigCtx, dsigNode);
   if (ret < 0) {
      g_warning("Signature verify failed\n");
      goto done;
   }

   /*
    * No need to verify the Reference explicitly because the
    * xmlsec1 library takes care of it.
    */

   /*
    * Check status to verify the signature is correct.
    *
    * This check can fail due to build issues.  If xmlSecSize is
    * different between this layer and the xmlsec library,
    * dsigCtx->status can be at the wrong offset.  So
    * dump the value of status, which should be either
    * 1 (xmlSecDSigStatusSucceeded) or 2 (xmlSecDSigStatusInvalid).
    * If its something else, that could be a sign that there's a build issue
    * and that libxmlsec1 is using a different size type than its callers.
    * Please see xmlSecSize changelog in
    * https://www.aleksey.com/xmlsec/news.html
    *
    */
   if (dsigCtx->status != xmlSecDSigStatusSucceeded) {
      g_warning("%s: Signature is invalid (got %d)\n",
                __FUNCTION__, dsigCtx->status);
      VMXLog_Log(VMXLOG_LEVEL_WARNING,
                 "%s: signature is invalid (got %d)\n", __FUNCTION__,
                 dsigCtx->status);
      goto done;
   }

verified:
   retCode = TRUE;
   *numCerts = num;
   *certChain = certList;
done:
   if (!retCode) {
      FreeCertArray(num, certList);
   }
   if (dsigCtx) {
      xmlSecDSigCtxDestroy(dsigCtx);
   }
   if (mgr) {
      xmlSecKeysMngrDestroy(mgr);
   }

   return retCode;
}


/*
 * Public API for use by the unit tests.
 */

gboolean
SAML_VerifySignature(xmlDocPtr doc,
                     gboolean hostVerified,
                     int *numCerts,
                     gchar ***certChain)
{
   return VerifySignature(doc,
                          hostVerified,
                          numCerts,
                          certChain);
}


/*
 ******************************************************************************
 * VerifySAMLToken --                                                    */ /**
 *
 * Verifies a XML text as a SAML token.
 * Parses the XML, then verifies Subject, Conditions and Signature.
 *
 * @param[in]  token         Text of SAML token.
 * @param[in]  hostVerfied   If true, the signature check can be skipped.
 * @param[out] subject       Subject of SAML token,  Caller must g_free().
 * @param[out] numCerts      Number of certs in the token.
 * @param[out] certChain     Certs in the token. Caller should g_free()
 *                           array and contents.
 *
 * @return matching TRUE on success.
 *
 ******************************************************************************
 */

static gboolean
VerifySAMLToken(const gchar *token,
                gboolean hostVerified,
                gchar **subject,
                int *numCerts,
                gchar ***certChain)
{
   xmlDocPtr doc = NULL;
   int retCode = FALSE;
   gboolean bRet;
   /*
    * If we want to set extra options, use this path.
    */
#if PARSE_WITH_OPTIONS
   xmlParserCtxtPtr parseCtx = NULL;

   parseCtx = xmlCreateDocParserCtxt(token);

   /*
    * Don't allow extra stuff to be pulled off the net.
    * The schema validation should prevent this from getting
    * through, but it might still be nice to prevent network issues
    * from slowing things down.
    */
   xmlCtxtUseOptions(parseCtx, XML_PARSE_NONET);
   doc = xmlCtxtReadMemory(parseCtx,
                           token,
                           strlen(token),
                           NULL, NULL, 0);
#else
   /*
    * Tell libxml to substitute the entities (XML_PARSE_NOENT).
    * Tell libxml to load the external DTD (XML_PARSE_DTDLOAD).
    * Tell libxml to add default attributes from the DTD (XML_PARSE_DTDATTR).
    */
   doc = xmlReadMemory(token, (int)strlen(token), NULL, NULL,
                       XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_DTDLOAD);
#endif
   if ((NULL == doc) || (xmlDocGetRootElement(doc) == NULL)) {
      g_warning("Failed to parse document\n");
      goto done;
   }

   bRet = ValidateDoc(doc);
   if (FALSE == bRet) {
      g_warning("Failed to validate token against schema\n");
      goto done;
   }

   bRet = VerifySubject(doc, subject);
#ifndef TEST_VERIFY_SIGN_ONLY
   if (FALSE == bRet) {
      g_warning("Failed to verify Subject node\n");
      goto done;
   }
#endif

   bRet = VerifyConditions(doc);
#ifndef TEST_VERIFY_SIGN_ONLY
   if (FALSE == bRet) {
      g_warning("Failed to verify Conditions\n");
      goto done;
   }
#endif

   bRet = VerifySignature(doc,
                          hostVerified,
                          numCerts, certChain);
   if (FALSE == bRet) {
      g_warning("Failed to verify Signature\n");
      // XXX Can we log the token at this point without risking security?
      goto done;
   }

   retCode = TRUE;
done:
#if PARSE_WITH_OPTIONS
   if (NULL != parseCtx) {
      xmlFreeParserCtxt(parseCtx);
   }
#endif
   if (!retCode && (NULL != subject)) {
      g_free(*subject);
      *subject = NULL;
   }
   if (doc) {
      xmlFreeDoc(doc);
   }

   return retCode;
}


// XXX remove this?  hostVerified can be tested just fine with the 'real'
// API, the test-only shortcut may be overkill.  Though once this is
// out of dev, we could add the extra param to SAML_VerifyBearerToken()
// and fix all the test calls.

/*
 ******************************************************************************
 * SAML_VerifyBearerTokenEx --                                           */ /**
 *
 * Determines whether the SAML bearer token can be used to authenticate.
 * A token consists of a single SAML assertion.
 *
 * This is currently only used from the test code.
 *
 * @param[in]  xmlText      The text of the SAML assertion.
 * @param[in]  userName     Optional username to authenticate as.
 * @param[in]  hostVerified If set, then the signature verification will
 *                          be skipped.
 * @param[out] userNameOut  The user that the token has authenticated as.
 * @param[out] subjNameOut  The subject in the token.  Caller must g_free().
 * @param[out] verifyAi     The alias info associated with the entry
 *                          in the alias store used to verify the
 *                          SAML cert.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
SAML_VerifyBearerTokenEx(const char *xmlText,
                         const char *userName,                // UNUSED
                         gboolean hostVerified,
                         char **userNameOut,                  // UNUSED
                         char **subjNameOut,
                         ServiceAliasInfo **verifyAi)         // UNUSED
{
   gboolean ret;
   gchar **certChain = NULL;
   int num = 0;

   ret = VerifySAMLToken(xmlText,
                         hostVerified,
                         subjNameOut,
                         &num,
                         &certChain);

   // clean up -- this code doesn't look at the chain
   FreeCertArray(num, certChain);

   return (ret == TRUE) ? VGAUTH_E_OK : VGAUTH_E_AUTHENTICATION_DENIED;
}


/*
 ******************************************************************************
 * SAML_VerifyBearerToken --                                             */ /**
 *
 * Determines whether the SAML bearer token can be used to authenticate.
 * A token consists of a single SAML assertion.
 *
 * This is currently only used from the test code.
 *
 * @param[in]  xmlText     The text of the SAML assertion.
 * @param[in]  userName    Optional username to authenticate as.
 * @param[out] userNameOut The user that the token has authenticated as.
 * @param[out] subjNameOut The subject in the token.  Caller must g_free().
 * @param[out] verifyAi    The alias info associated with the entry
 *                         in the alias store used to verify the
 *                         SAML cert.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
SAML_VerifyBearerToken(const char *xmlText,
                       const char *userName,                // UNUSED
                       char **userNameOut,                  // UNUSED
                       char **subjNameOut,
                       ServiceAliasInfo **verifyAi)         // UNUSED
{
   gboolean ret;
   gchar **certChain = NULL;
   int num = 0;

   ret = VerifySAMLToken(xmlText,
                         FALSE,  // XXX keep original to minimze test changes
                         subjNameOut,
                         &num,
                         &certChain);

   // clean up -- this code doesn't look at the chain
   FreeCertArray(num, certChain);

   return (ret == TRUE) ? VGAUTH_E_OK : VGAUTH_E_AUTHENTICATION_DENIED;
}


/*
 ******************************************************************************
 * SAML_VerifyBearerTokenAndChain --                                     */ /**
 *
 * Determines whether the SAML bearer token can be used to authenticate.
 * A token consists of a single SAML assertion.
 * The token must first be verified, then the certificate chain used
 * verify it must be checked against the appropriate certificate store.
 *
 * @param[in]  xmlText      The text of the SAML assertion.
 * @param[in]  userName     Optional username to authenticate as.
 * @param[in]  hostVerified If true, skip signature verification.
 * @param[out] userNameOut  The user that the token has authenticated as.
 * @param[out] subjNameOut  The subject in the token.  Caller must g_free().
 * @param[out] verifyAi     The alias info associated with the entry
 *                          in the alias store used to verify the
 *                          SAML cert.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
SAML_VerifyBearerTokenAndChain(const char *xmlText,
                               const char *userName,
                               gboolean hostVerified,
                               char **userNameOut,
                               char **subjNameOut,
                               ServiceAliasInfo **verifyAi)
{
   VGAuthError err;
   gboolean bRet;
   int num;
   gchar **certChain = NULL;
   ServiceSubject subj;

   *userNameOut = NULL;
   *subjNameOut = NULL;
   *verifyAi = NULL;

   bRet = VerifySAMLToken(xmlText,
                          hostVerified,
                          subjNameOut,
                          &num,
                          &certChain);

   if (FALSE == bRet) {
      return VGAUTH_E_AUTHENTICATION_DENIED;
   }

   if (!gAllowUnrelatedCerts) {
      err = CertVerify_CheckForUnrelatedCerts(num, (const char **) certChain);
      if (err != VGAUTH_E_OK) {
         VMXLog_Log(VMXLOG_LEVEL_WARNING,
                    "Unrelated certs found in SAML token, failing\n");
         FreeCertArray(num, certChain);
         return VGAUTH_E_AUTHENTICATION_DENIED;
      }
   }

   subj.type = SUBJECT_TYPE_NAMED;
   subj.name = *subjNameOut;
   err = ServiceVerifyAndCheckTrustCertChainForSubject(num,
                                                       (const char **) certChain,
                                                       userName,
                                                       &subj,
                                                       userNameOut,
                                                       verifyAi);
   g_debug("%s: ServiceVerifyAndCheckTrustCertChainForSubject() "
           "returned "VGAUTHERR_FMT64"\n", __FUNCTION__, err);
   FreeCertArray(num, certChain);

   return err;
}

07070100000460000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas 07070100000461000081A40000000000000000000000016822550500001697000000000000000000000000000000000000006000000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/XMLSchema-hasFacetAndProperty.xsd   <?xml version='1.0'?>
<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" [
<!ENTITY % s ''>
<!ENTITY % p ''>
<!-- keep this XML 1.0 correct -->
<!ATTLIST schema xmlns:hfp CDATA #IMPLIED
                 xmlns:xhtml CDATA #IMPLIED
                 xmlns:xsi CDATA #IMPLIED
                 xsi:schemaLocation CDATA #IMPLIED>
<!ELEMENT xhtml:p ANY>
<!ELEMENT xhtml:em ANY>
]>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.w3.org/2001/XMLSchema-hasFacetAndProperty" xmlns:hfp="http://www.w3.org/2001/XMLSchema-hasFacetAndProperty" xmlns:xhtml="http://www.w3.org/1999/xhtml"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.w3.org/1999/xhtml http://www.w3.org/1999/xhtml.xsd">

 <annotation>
  <documentation>
   <xhtml:p> This schema defines 2 elements for use in the
appinfo portion section of (potentially) all builtin datatypes in the schema
for XML Schema Part 2: Datatypes.  </xhtml:p>
   <xhtml:p> One intended use of
this appinfo is in the generation of the HTML version of the XML Schema Part 2:
Datatypes specification itself.  All portions of the HTML text which describe
the facets and/or properties of each datatype is generated by an XSLT
transformation which keys off of this appinfo.  </xhtml:p>
   <xhtml:p> Schema
processors may have another use for this appinfo (although one certainly not
required in any way by the specification).  The information may be useful in
dynamically building validation modules/classes and/or user-interfaces for
schema creation.  </xhtml:p>
  </documentation>
 </annotation>

 <element name="hasFacet">
  <annotation>
   <documentation>
   <xhtml:p>
    hasFacet is used to signal that the contraining facet
    given in the name attribute is applicable to a primitive
    datatype (and all types derived from it).
</xhtml:p>
   <xhtml:p>
    Note: this element will only appear in the appinfo of
    primitive types or built-in types derived by "list".
   </xhtml:p>
   <xhtml:p>
    A schema processor (or the XSLT which generates the
    HTML version of the XML Schema Part 2: Datatypes
    specification) which reads a derived simpleType
    definition should walk up the base type chain until
    it reaches the primitive type at the top of the chain
    and "push" all facets found their down to all derived
    types in the chain.
   </xhtml:p>
   </documentation>
  </annotation>
  <complexType>
   <attribute name="name" use="required">
    <simpleType>
     <annotation>
      <documentation>
       <xhtml:p>
        This datatype names all existing contraining facets.
</xhtml:p>
       <xhtml:p>
        Question: should each of the enumerations below be
        given a documentation annotation, which would contain
        the text to be used in the definition of the facet
        in the XML Schema Part 2: Datatypes specification?
        Might be nice to try to collect all of that information
        together here.
       </xhtml:p>
      </documentation>
     </annotation>
     <restriction base="NMTOKEN">
      <enumeration value="length"/>
      <enumeration value="minLength"/>
      <enumeration value="maxLength"/>
      <enumeration value="pattern"/>
      <enumeration value="enumeration"/>
      <enumeration value="maxInclusive"/>
      <enumeration value="maxExclusive"/>
      <enumeration value="minInclusive"/>
      <enumeration value="minExclusive"/>
      <enumeration value="totalDigits"/>
      <enumeration value="fractionDigits"/>
      <enumeration value="whiteSpace"/>
      <enumeration value="maxScale"/>
      <enumeration value="minScale"/>
     </restriction>
    </simpleType>
   </attribute>
  </complexType>
 </element>

 <element name="hasProperty">
  <annotation>
   <documentation>
    <xhtml:p> hasProperty is used to signal that the property
given in the name attribute has the value given in the value attribute for the
datatype in which it occurs (and all types derived from it, which do not
override the value of the property).  </xhtml:p>
    <xhtml:p> Note: this element
may appear in the appinfo of primitive and built-in derived types.  </xhtml:p>
    <xhtml:p> A schema processor (or the XSLT which generates the HTML version of
the XML Schema Part 2: Datatypes specification) which reads a simpleType
definition should gather the information from any occurances of hasProperty in
that simpleType definition, and then walk up the base type chain gathering
information from any occurances of hasProperty (unless a value was given to the
name in a dervied type) until either it reaches the primitive type at the top
of the chain or it has gathered values for all existing properties.  </xhtml:p>
   </documentation>
  </annotation>
  <complexType>
   <attribute name="name" use="required">
    <simpleType>
     <annotation>
      <documentation>
       <xhtml:p> This datatype names all existing fundamental
facets, otherwise known as properties (with the exception of
<xhtml:em>equality</xhtml:em>, a property which has no
<xhtml:em>value</xhtml:em>).  </xhtml:p>
       <xhtml:p> Question: should each of
the enumerations below be given a documentation annotation, which would contain
the text to be used in the definition of the properties in the XML Schema Part
2: Datatypes specification? Might be nice to try to collect all of that
information together here.  </xhtml:p>
      </documentation>
     </annotation>
     <restriction base="NMTOKEN">
      <enumeration value="ordered"/>
      <enumeration value="bounded"/>
      <enumeration value="cardinality"/>
      <enumeration value="numeric"/>
     </restriction>
    </simpleType>
   </attribute>
   <attribute name="value" type="normalizedString" use="required"/>
  </complexType>
 </element>
</schema>
 07070100000462000081A400000000000000000000000168225505000004BF000000000000000000000000000000000000005500000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/XMLSchema-instance.xsd  <?xml version='1.0'?>
<!DOCTYPE xs:schema SYSTEM "XMLSchema.dtd" [
<!ELEMENT p ANY>
<!ELEMENT a ANY>
<!ATTLIST a href CDATA #IMPLIED>
<!ELEMENT hr ANY>
<!ELEMENT h1 ANY>
<!ELEMENT br ANY>
]>
<xs:schema targetNamespace="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="http://www.w3.org/1999/xhtml">
  <xs:annotation>
   <xs:documentation>
    <h1>XML Schema instance namespace</h1>
    <p>See <a href="http://www.w3.org/TR/xmlschema-1/">the XML Schema
       Recommendation</a> for an introduction</p>


    <hr />
    $Date: 2001/03/16 20:25:57 $<br />
    $Id: XMLSchema-instance.xsd,v 1.4 2001/03/16 20:25:57 ht Exp $
  </xs:documentation>
 </xs:annotation>
 <xs:annotation>
  <xs:documentation><p>This schema should never be used as such:
                    <a href="http://www.w3.org/TR/xmlschema-1/#no-xsi">the XML
                    Schema Recommendation</a> forbids the declaration of
                    attributes in this namespace</p>
  </xs:documentation>
 </xs:annotation>

 <xs:attribute name="nil"/>
 <xs:attribute name="type"/>
 <xs:attribute name="schemaLocation"/>
 <xs:attribute name="noNamespaceSchemaLocation"/>
</xs:schema>
 07070100000463000081A40000000000000000000000016822550500003EC8000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/XMLSchema.dtd   <!-- DTD for XML Schemas: Part 1: Structures
     Public Identifier: "-//W3C//DTD XMLSCHEMA 200102//EN"
     Official Location: http://www.w3.org/2001/XMLSchema.dtd -->
<!-- $Id: XMLSchema.dtd,v 1.31 2001/10/24 15:50:16 ht Exp $ -->
<!-- Note this DTD is NOT normative, or even definitive. -->           <!--d-->
<!-- prose copy in the structures REC is the definitive version -->    <!--d-->
<!-- (which shouldn't differ from this one except for this -->         <!--d-->
<!-- comment and entity expansions, but just in case) -->              <!--d-->
<!-- With the exception of cases with multiple namespace
     prefixes for the XML Schema namespace, any XML document which is
     not valid per this DTD given redefinitions in its internal subset of the
     'p' and 's' parameter entities below appropriate to its namespace
     declaration of the XML Schema namespace is almost certainly not
     a valid schema. -->

<!-- The simpleType element and its constituent parts
     are defined in XML Schema: Part 2: Datatypes -->
<!ENTITY % xs-datatypes PUBLIC 'datatypes' 'datatypes.dtd' >

<!ENTITY % p 'xs:'> <!-- can be overriden in the internal subset of a
                         schema document to establish a different
                         namespace prefix -->
<!ENTITY % s ':xs'> <!-- if %p is defined (e.g. as foo:) then you must
                         also define %s as the suffix for the appropriate
                         namespace declaration (e.g. :foo) -->
<!ENTITY % nds 'xmlns%s;'>

<!-- Define all the element names, with optional prefix -->
<!ENTITY % schema "%p;schema">
<!ENTITY % complexType "%p;complexType">
<!ENTITY % complexContent "%p;complexContent">
<!ENTITY % simpleContent "%p;simpleContent">
<!ENTITY % extension "%p;extension">
<!ENTITY % element "%p;element">
<!ENTITY % unique "%p;unique">
<!ENTITY % key "%p;key">
<!ENTITY % keyref "%p;keyref">
<!ENTITY % selector "%p;selector">
<!ENTITY % field "%p;field">
<!ENTITY % group "%p;group">
<!ENTITY % all "%p;all">
<!ENTITY % choice "%p;choice">
<!ENTITY % sequence "%p;sequence">
<!ENTITY % any "%p;any">
<!ENTITY % anyAttribute "%p;anyAttribute">
<!ENTITY % attribute "%p;attribute">
<!ENTITY % attributeGroup "%p;attributeGroup">
<!ENTITY % include "%p;include">
<!ENTITY % import "%p;import">
<!ENTITY % redefine "%p;redefine">
<!ENTITY % notation "%p;notation">

<!-- annotation elements -->
<!ENTITY % annotation "%p;annotation">
<!ENTITY % appinfo "%p;appinfo">
<!ENTITY % documentation "%p;documentation">

<!-- Customisation entities for the ATTLIST of each element type.
     Define one of these if your schema takes advantage of the
     anyAttribute='##other' in the schema for schemas -->

<!ENTITY % schemaAttrs ''>
<!ENTITY % complexTypeAttrs ''>
<!ENTITY % complexContentAttrs ''>
<!ENTITY % simpleContentAttrs ''>
<!ENTITY % extensionAttrs ''>
<!ENTITY % elementAttrs ''>
<!ENTITY % groupAttrs ''>
<!ENTITY % allAttrs ''>
<!ENTITY % choiceAttrs ''>
<!ENTITY % sequenceAttrs ''>
<!ENTITY % anyAttrs ''>
<!ENTITY % anyAttributeAttrs ''>
<!ENTITY % attributeAttrs ''>
<!ENTITY % attributeGroupAttrs ''>
<!ENTITY % uniqueAttrs ''>
<!ENTITY % keyAttrs ''>
<!ENTITY % keyrefAttrs ''>
<!ENTITY % selectorAttrs ''>
<!ENTITY % fieldAttrs ''>
<!ENTITY % includeAttrs ''>
<!ENTITY % importAttrs ''>
<!ENTITY % redefineAttrs ''>
<!ENTITY % notationAttrs ''>
<!ENTITY % annotationAttrs ''>
<!ENTITY % appinfoAttrs ''>
<!ENTITY % documentationAttrs ''>

<!ENTITY % complexDerivationSet "CDATA">
      <!-- #all or space-separated list drawn from derivationChoice -->
<!ENTITY % blockSet "CDATA">
      <!-- #all or space-separated list drawn from
                      derivationChoice + 'substitution' -->

<!ENTITY % mgs '%all; | %choice; | %sequence;'>
<!ENTITY % cs '%choice; | %sequence;'>
<!ENTITY % formValues '(qualified|unqualified)'>


<!ENTITY % attrDecls    '((%attribute;| %attributeGroup;)*,(%anyAttribute;)?)'>

<!ENTITY % particleAndAttrs '((%mgs; | %group;)?, %attrDecls;)'>

<!-- This is used in part2 -->
<!ENTITY % restriction1 '((%mgs; | %group;)?)'>

%xs-datatypes;

<!-- the duplication below is to produce an unambiguous content model
     which allows annotation everywhere -->
<!ELEMENT %schema; ((%include; | %import; | %redefine; | %annotation;)*,
                    ((%simpleType; | %complexType;
                      | %element; | %attribute;
                      | %attributeGroup; | %group;
                      | %notation; ),
                     (%annotation;)*)* )>
<!ATTLIST %schema;
   targetNamespace      %URIref;               #IMPLIED
   version              CDATA                  #IMPLIED
   %nds;                %URIref;               #FIXED 'http://www.w3.org/2001/XMLSchema'
   xmlns                CDATA                  #IMPLIED
   finalDefault         %complexDerivationSet; ''
   blockDefault         %blockSet;             ''
   id                   ID                     #IMPLIED
   elementFormDefault   %formValues;           'unqualified'
   attributeFormDefault %formValues;           'unqualified'
   xml:lang             CDATA                  #IMPLIED
   %schemaAttrs;>
<!-- Note the xmlns declaration is NOT in the Schema for Schemas,
     because at the Infoset level where schemas operate,
     xmlns(:prefix) is NOT an attribute! -->
<!-- The declaration of xmlns is a convenience for schema authors -->

<!-- The id attribute here and below is for use in external references
     from non-schemas using simple fragment identifiers.
     It is NOT used for schema-to-schema reference, internal or
     external. -->

<!-- a type is a named content type specification which allows attribute
     declarations-->
<!-- -->

<!ELEMENT %complexType; ((%annotation;)?,
                         (%simpleContent;|%complexContent;|
                          %particleAndAttrs;))>

<!ATTLIST %complexType;
          name      %NCName;                        #IMPLIED
          id        ID                              #IMPLIED
          abstract  %boolean;                       #IMPLIED
          final     %complexDerivationSet;          #IMPLIED
          block     %complexDerivationSet;          #IMPLIED
          mixed (true|false) 'false'
          %complexTypeAttrs;>

<!-- particleAndAttrs is shorthand for a root type -->
<!-- mixed is disallowed if simpleContent, overriden if complexContent
     has one too. -->

<!-- If anyAttribute appears in one or more referenced attributeGroups
     and/or explicitly, the intersection of the permissions is used -->

<!ELEMENT %complexContent; ((%annotation;)?, (%restriction;|%extension;))>
<!ATTLIST %complexContent;
          mixed (true|false) #IMPLIED
          id    ID           #IMPLIED
          %complexContentAttrs;>

<!-- restriction should use the branch defined above, not the simple
     one from part2; extension should use the full model  -->

<!ELEMENT %simpleContent; ((%annotation;)?, (%restriction;|%extension;))>
<!ATTLIST %simpleContent;
          id    ID           #IMPLIED
          %simpleContentAttrs;>

<!-- restriction should use the simple branch from part2, not the
     one defined above; extension should have no particle  -->

<!ELEMENT %extension; ((%annotation;)?, (%particleAndAttrs;))>
<!ATTLIST %extension;
          base  %QName;      #REQUIRED
          id    ID           #IMPLIED
          %extensionAttrs;>

<!-- an element is declared by either:
 a name and a type (either nested or referenced via the type attribute)
 or a ref to an existing element declaration -->

<!ELEMENT %element; ((%annotation;)?, (%complexType;| %simpleType;)?,
                     (%unique; | %key; | %keyref;)*)>
<!-- simpleType or complexType only if no type|ref attribute -->
<!-- ref not allowed at top level -->
<!ATTLIST %element;
            name               %NCName;               #IMPLIED
            id                 ID                     #IMPLIED
            ref                %QName;                #IMPLIED
            type               %QName;                #IMPLIED
            minOccurs          %nonNegativeInteger;   #IMPLIED
            maxOccurs          CDATA                  #IMPLIED
            nillable           %boolean;              #IMPLIED
            substitutionGroup  %QName;                #IMPLIED
            abstract           %boolean;              #IMPLIED
            final              %complexDerivationSet; #IMPLIED
            block              %blockSet;             #IMPLIED
            default            CDATA                  #IMPLIED
            fixed              CDATA                  #IMPLIED
            form               %formValues;           #IMPLIED
            %elementAttrs;>
<!-- type and ref are mutually exclusive.
     name and ref are mutually exclusive, one is required -->
<!-- In the absence of type AND ref, type defaults to type of
     substitutionGroup, if any, else the ur-type, i.e. unconstrained -->
<!-- default and fixed are mutually exclusive -->

<!ELEMENT %group; ((%annotation;)?,(%mgs;)?)>
<!ATTLIST %group;
          name        %NCName;               #IMPLIED
          ref         %QName;                #IMPLIED
          minOccurs   %nonNegativeInteger;   #IMPLIED
          maxOccurs   CDATA                  #IMPLIED
          id          ID                     #IMPLIED
          %groupAttrs;>

<!ELEMENT %all; ((%annotation;)?, (%element;)*)>
<!ATTLIST %all;
          minOccurs   (1)                    #IMPLIED
          maxOccurs   (1)                    #IMPLIED
          id          ID                     #IMPLIED
          %allAttrs;>

<!ELEMENT %choice; ((%annotation;)?, (%element;| %group;| %cs; | %any;)*)>
<!ATTLIST %choice;
          minOccurs   %nonNegativeInteger;   #IMPLIED
          maxOccurs   CDATA                  #IMPLIED
          id          ID                     #IMPLIED
          %choiceAttrs;>

<!ELEMENT %sequence; ((%annotation;)?, (%element;| %group;| %cs; | %any;)*)>
<!ATTLIST %sequence;
          minOccurs   %nonNegativeInteger;   #IMPLIED
          maxOccurs   CDATA                  #IMPLIED
          id          ID                     #IMPLIED
          %sequenceAttrs;>

<!-- an anonymous grouping in a model, or
     a top-level named group definition, or a reference to same -->

<!-- Note that if order is 'all', group is not allowed inside.
     If order is 'all' THIS group must be alone (or referenced alone) at
     the top level of a content model -->
<!-- If order is 'all', minOccurs==maxOccurs==1 on element/any inside -->
<!-- Should allow minOccurs=0 inside order='all' . . . -->

<!ELEMENT %any; (%annotation;)?>
<!ATTLIST %any;
            namespace       CDATA                  '##any'
            processContents (skip|lax|strict)      'strict'
            minOccurs       %nonNegativeInteger;   '1'
            maxOccurs       CDATA                  '1'
            id              ID                     #IMPLIED
            %anyAttrs;>

<!-- namespace is interpreted as follows:
                  ##any      - - any non-conflicting WFXML at all

                  ##other    - - any non-conflicting WFXML from namespace other
                                  than targetNamespace

                  ##local    - - any unqualified non-conflicting WFXML/attribute
                  one or     - - any non-conflicting WFXML from
                  more URI        the listed namespaces
                  references

                  ##targetNamespace ##local may appear in the above list,
                    with the obvious meaning -->

<!ELEMENT %anyAttribute; (%annotation;)?>
<!ATTLIST %anyAttribute;
            namespace       CDATA              '##any'
            processContents (skip|lax|strict)  'strict'
            id              ID                 #IMPLIED
            %anyAttributeAttrs;>
<!-- namespace is interpreted as for 'any' above -->

<!-- simpleType only if no type|ref attribute -->
<!-- ref not allowed at top level, name iff at top level -->
<!ELEMENT %attribute; ((%annotation;)?, (%simpleType;)?)>
<!ATTLIST %attribute;
          name      %NCName;      #IMPLIED
          id        ID            #IMPLIED
          ref       %QName;       #IMPLIED
          type      %QName;       #IMPLIED
          use       (prohibited|optional|required) #IMPLIED
          default   CDATA         #IMPLIED
          fixed     CDATA         #IMPLIED
          form      %formValues;  #IMPLIED
          %attributeAttrs;>
<!-- type and ref are mutually exclusive.
     name and ref are mutually exclusive, one is required -->
<!-- default for use is optional when nested, none otherwise -->
<!-- default and fixed are mutually exclusive -->
<!-- type attr and simpleType content are mutually exclusive -->

<!-- an attributeGroup is a named collection of attribute decls, or a
     reference thereto -->
<!ELEMENT %attributeGroup; ((%annotation;)?,
                       (%attribute; | %attributeGroup;)*,
                       (%anyAttribute;)?) >
<!ATTLIST %attributeGroup;
                 name       %NCName;       #IMPLIED
                 id         ID             #IMPLIED
                 ref        %QName;        #IMPLIED
                 %attributeGroupAttrs;>

<!-- ref iff no content, no name.  ref iff not top level -->

<!-- better reference mechanisms -->
<!ELEMENT %unique; ((%annotation;)?, %selector;, (%field;)+)>
<!ATTLIST %unique;
          name     %NCName;       #REQUIRED
	  id       ID             #IMPLIED
	  %uniqueAttrs;>

<!ELEMENT %key;    ((%annotation;)?, %selector;, (%field;)+)>
<!ATTLIST %key;
          name     %NCName;       #REQUIRED
	  id       ID             #IMPLIED
	  %keyAttrs;>

<!ELEMENT %keyref; ((%annotation;)?, %selector;, (%field;)+)>
<!ATTLIST %keyref;
          name     %NCName;       #REQUIRED
	  refer    %QName;        #REQUIRED
	  id       ID             #IMPLIED
	  %keyrefAttrs;>

<!ELEMENT %selector; ((%annotation;)?)>
<!ATTLIST %selector;
          xpath %XPathExpr; #REQUIRED
          id    ID          #IMPLIED
          %selectorAttrs;>
<!ELEMENT %field; ((%annotation;)?)>
<!ATTLIST %field;
          xpath %XPathExpr; #REQUIRED
          id    ID          #IMPLIED
          %fieldAttrs;>

<!-- Schema combination mechanisms -->
<!ELEMENT %include; (%annotation;)?>
<!ATTLIST %include;
          schemaLocation %URIref; #REQUIRED
          id             ID       #IMPLIED
          %includeAttrs;>

<!ELEMENT %import; (%annotation;)?>
<!ATTLIST %import;
          namespace      %URIref; #IMPLIED
          schemaLocation %URIref; #IMPLIED
          id             ID       #IMPLIED
          %importAttrs;>

<!ELEMENT %redefine; (%annotation; | %simpleType; | %complexType; |
                      %attributeGroup; | %group;)*>
<!ATTLIST %redefine;
          schemaLocation %URIref; #REQUIRED
          id             ID       #IMPLIED
          %redefineAttrs;>

<!ELEMENT %notation; (%annotation;)?>
<!ATTLIST %notation;
	  name        %NCName;    #REQUIRED
	  id          ID          #IMPLIED
	  public      CDATA       #REQUIRED
	  system      %URIref;    #IMPLIED
	  %notationAttrs;>

<!-- Annotation is either application information or documentation -->
<!-- By having these here they are available for datatypes as well
     as all the structures elements -->

<!ELEMENT %annotation; (%appinfo; | %documentation;)*>
<!ATTLIST %annotation; %annotationAttrs;>

<!-- User must define annotation elements in internal subset for this
     to work -->
<!ELEMENT %appinfo; ANY>   <!-- too restrictive -->
<!ATTLIST %appinfo;
          source     %URIref;      #IMPLIED
          id         ID         #IMPLIED
          %appinfoAttrs;>
<!ELEMENT %documentation; ANY>   <!-- too restrictive -->
<!ATTLIST %documentation;
          source     %URIref;   #IMPLIED
          id         ID         #IMPLIED
          xml:lang   CDATA      #IMPLIED
          %documentationAttrs;>

<!NOTATION XMLSchemaStructures PUBLIC
           'structures' 'http://www.w3.org/2001/XMLSchema.xsd' >
<!NOTATION XML PUBLIC
           'REC-xml-1998-0210' 'http://www.w3.org/TR/1998/REC-xml-19980210' >
07070100000464000081A400000000000000000000000168225505000151A3000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/XMLSchema.xsd   <?xml version="1.0" encoding="UTF-8"?>
<!-- XML Schema schema for XML Schemas: Part 1: Structures -->
<!-- Note this schema is NOT the normative structures schema. -->
<!-- The prose copy in the structures REC is the normative -->
<!-- version (which shouldn't differ from this one except for -->
<!-- this comment and entity expansions, but just in case -->
<!DOCTYPE xs:schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "XMLSchema.dtd" [

<!-- provide ID type information even for parsers which only read the
     internal subset -->
<!ATTLIST xs:schema          id  ID  #IMPLIED>
<!ATTLIST xs:complexType     id  ID  #IMPLIED>
<!ATTLIST xs:complexContent  id  ID  #IMPLIED>
<!ATTLIST xs:simpleContent   id  ID  #IMPLIED>
<!ATTLIST xs:extension       id  ID  #IMPLIED>
<!ATTLIST xs:element         id  ID  #IMPLIED>
<!ATTLIST xs:group           id  ID  #IMPLIED>
<!ATTLIST xs:all             id  ID  #IMPLIED>
<!ATTLIST xs:choice          id  ID  #IMPLIED>
<!ATTLIST xs:sequence        id  ID  #IMPLIED>
<!ATTLIST xs:any             id  ID  #IMPLIED>
<!ATTLIST xs:anyAttribute    id  ID  #IMPLIED>
<!ATTLIST xs:attribute       id  ID  #IMPLIED>
<!ATTLIST xs:attributeGroup  id  ID  #IMPLIED>
<!ATTLIST xs:unique          id  ID  #IMPLIED>
<!ATTLIST xs:key             id  ID  #IMPLIED>
<!ATTLIST xs:keyref          id  ID  #IMPLIED>
<!ATTLIST xs:selector        id  ID  #IMPLIED>
<!ATTLIST xs:field           id  ID  #IMPLIED>
<!ATTLIST xs:include         id  ID  #IMPLIED>
<!ATTLIST xs:import          id  ID  #IMPLIED>
<!ATTLIST xs:redefine        id  ID  #IMPLIED>
<!ATTLIST xs:notation        id  ID  #IMPLIED>
<!--
     keep this schema XML1.0 DTD valid
  -->
        <!ENTITY % schemaAttrs 'xmlns:hfp CDATA #IMPLIED'>

        <!ELEMENT hfp:hasFacet EMPTY>
        <!ATTLIST hfp:hasFacet
                name NMTOKEN #REQUIRED>

        <!ELEMENT hfp:hasProperty EMPTY>
        <!ATTLIST hfp:hasProperty
                name NMTOKEN #REQUIRED
                value CDATA #REQUIRED>
<!--
        Make sure that processors that do not read the external
        subset will know about the various IDs we declare
  -->
        <!ATTLIST xs:simpleType id ID #IMPLIED>
        <!ATTLIST xs:maxExclusive id ID #IMPLIED>
        <!ATTLIST xs:minExclusive id ID #IMPLIED>
        <!ATTLIST xs:maxInclusive id ID #IMPLIED>
        <!ATTLIST xs:minInclusive id ID #IMPLIED>
        <!ATTLIST xs:totalDigits id ID #IMPLIED>
        <!ATTLIST xs:fractionDigits id ID #IMPLIED>
        <!ATTLIST xs:length id ID #IMPLIED>
        <!ATTLIST xs:minLength id ID #IMPLIED>
        <!ATTLIST xs:maxLength id ID #IMPLIED>
        <!ATTLIST xs:enumeration id ID #IMPLIED>
        <!ATTLIST xs:pattern id ID #IMPLIED>
        <!ATTLIST xs:appinfo id ID #IMPLIED>
        <!ATTLIST xs:documentation id ID #IMPLIED>
        <!ATTLIST xs:list id ID #IMPLIED>
        <!ATTLIST xs:union id ID #IMPLIED>
        ]>
<xs:schema targetNamespace="http://www.w3.org/2001/XMLSchema" blockDefault="#all" elementFormDefault="qualified" version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xml:lang="EN" xmlns:hfp="http://www.w3.org/2001/XMLSchema-hasFacetAndProperty">
 <xs:annotation>
  <xs:documentation>
    Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp
    Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp
  </xs:documentation>
 </xs:annotation>

 <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/2004/PER-xmlschema-1-20040318/structures.html">
   The schema corresponding to this document is normative,
   with respect to the syntactic constraints it expresses in the
   XML Schema language.  The documentation (within &lt;documentation&gt; elements)
   below, is not normative, but rather highlights important aspects of
   the W3C Recommendation of which this is a part</xs:documentation>
 </xs:annotation>

 <xs:annotation>
   <xs:documentation>
   The simpleType element and all of its members are defined
      towards the end of this schema document</xs:documentation>
 </xs:annotation>

 <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd">
   <xs:annotation>
     <xs:documentation>
       Get access to the xml: attribute groups for xml:lang
       as declared on 'schema' and 'documentation' below
     </xs:documentation>
   </xs:annotation>
 </xs:import>

 <xs:complexType name="openAttrs">
   <xs:annotation>
     <xs:documentation>
       This type is extended by almost all schema types
       to allow attributes from other namespaces to be
       added to user schemas.
     </xs:documentation>
   </xs:annotation>
   <xs:complexContent>
     <xs:restriction base="xs:anyType">
       <xs:anyAttribute namespace="##other" processContents="lax"/>
     </xs:restriction>
   </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="annotated">
   <xs:annotation>
     <xs:documentation>
       This type is extended by all types which allow annotation
       other than &lt;schema&gt; itself
     </xs:documentation>
   </xs:annotation>
   <xs:complexContent>
     <xs:extension base="xs:openAttrs">
       <xs:sequence>
         <xs:element ref="xs:annotation" minOccurs="0"/>
       </xs:sequence>
       <xs:attribute name="id" type="xs:ID"/>
     </xs:extension>
   </xs:complexContent>
 </xs:complexType>

 <xs:group name="schemaTop">
  <xs:annotation>
   <xs:documentation>
   This group is for the
   elements which occur freely at the top level of schemas.
   All of their types are based on the "annotated" type by extension.</xs:documentation>
  </xs:annotation>
  <xs:choice>
   <xs:group ref="xs:redefinable"/>
   <xs:element ref="xs:element"/>
   <xs:element ref="xs:attribute"/>
   <xs:element ref="xs:notation"/>
  </xs:choice>
 </xs:group>

 <xs:group name="redefinable">
  <xs:annotation>
   <xs:documentation>
   This group is for the
   elements which can self-redefine (see &lt;redefine&gt; below).</xs:documentation>
  </xs:annotation>
  <xs:choice>
   <xs:element ref="xs:simpleType"/>
   <xs:element ref="xs:complexType"/>
   <xs:element ref="xs:group"/>
   <xs:element ref="xs:attributeGroup"/>
  </xs:choice>
 </xs:group>

 <xs:simpleType name="formChoice">
  <xs:annotation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
  </xs:annotation>
  <xs:restriction base="xs:NMTOKEN">
   <xs:enumeration value="qualified"/>
   <xs:enumeration value="unqualified"/>
  </xs:restriction>
 </xs:simpleType>

 <xs:simpleType name="reducedDerivationControl">
  <xs:annotation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
  </xs:annotation>
  <xs:restriction base="xs:derivationControl">
   <xs:enumeration value="extension"/>
   <xs:enumeration value="restriction"/>
  </xs:restriction>
 </xs:simpleType>

 <xs:simpleType name="derivationSet">
  <xs:annotation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
   <xs:documentation>
   #all or (possibly empty) subset of {extension, restriction}</xs:documentation>
  </xs:annotation>
  <xs:union>
   <xs:simpleType>
    <xs:restriction base="xs:token">
     <xs:enumeration value="#all"/>
    </xs:restriction>
   </xs:simpleType>
   <xs:simpleType>
    <xs:list itemType="xs:reducedDerivationControl"/>
   </xs:simpleType>
  </xs:union>
 </xs:simpleType>

 <xs:simpleType name="typeDerivationControl">
  <xs:annotation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
  </xs:annotation>
  <xs:restriction base="xs:derivationControl">
   <xs:enumeration value="extension"/>
   <xs:enumeration value="restriction"/>
   <xs:enumeration value="list"/>
   <xs:enumeration value="union"/>
  </xs:restriction>
 </xs:simpleType>

  <xs:simpleType name="fullDerivationSet">
  <xs:annotation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
   <xs:documentation>
   #all or (possibly empty) subset of {extension, restriction, list, union}</xs:documentation>
  </xs:annotation>
  <xs:union>
   <xs:simpleType>
    <xs:restriction base="xs:token">
     <xs:enumeration value="#all"/>
    </xs:restriction>
   </xs:simpleType>
   <xs:simpleType>
    <xs:list itemType="xs:typeDerivationControl"/>
   </xs:simpleType>
  </xs:union>
 </xs:simpleType>

 <xs:element name="schema" id="schema">
  <xs:annotation>
    <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-schema"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:openAttrs">
     <xs:sequence>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
       <xs:element ref="xs:include"/>
       <xs:element ref="xs:import"/>
       <xs:element ref="xs:redefine"/>
       <xs:element ref="xs:annotation"/>
      </xs:choice>
      <xs:sequence minOccurs="0" maxOccurs="unbounded">
       <xs:group ref="xs:schemaTop"/>
       <xs:element ref="xs:annotation" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
     </xs:sequence>
     <xs:attribute name="targetNamespace" type="xs:anyURI"/>
     <xs:attribute name="version" type="xs:token"/>
     <xs:attribute name="finalDefault" type="xs:fullDerivationSet" use="optional" default=""/>
     <xs:attribute name="blockDefault" type="xs:blockSet" use="optional" default=""/>
     <xs:attribute name="attributeFormDefault" type="xs:formChoice" use="optional" default="unqualified"/>
     <xs:attribute name="elementFormDefault" type="xs:formChoice" use="optional" default="unqualified"/>
     <xs:attribute name="id" type="xs:ID"/>
     <xs:attribute ref="xml:lang"/>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>

  <xs:key name="element">
   <xs:selector xpath="xs:element"/>
   <xs:field xpath="@name"/>
  </xs:key>

  <xs:key name="attribute">
   <xs:selector xpath="xs:attribute"/>
   <xs:field xpath="@name"/>
  </xs:key>

  <xs:key name="type">
   <xs:selector xpath="xs:complexType|xs:simpleType"/>
   <xs:field xpath="@name"/>
  </xs:key>

  <xs:key name="group">
   <xs:selector xpath="xs:group"/>
   <xs:field xpath="@name"/>
  </xs:key>

  <xs:key name="attributeGroup">
   <xs:selector xpath="xs:attributeGroup"/>
   <xs:field xpath="@name"/>
  </xs:key>

  <xs:key name="notation">
   <xs:selector xpath="xs:notation"/>
   <xs:field xpath="@name"/>
  </xs:key>

  <xs:key name="identityConstraint">
   <xs:selector xpath=".//xs:key|.//xs:unique|.//xs:keyref"/>
   <xs:field xpath="@name"/>
  </xs:key>

 </xs:element>

 <xs:simpleType name="allNNI">
  <xs:annotation><xs:documentation>
   for maxOccurs</xs:documentation></xs:annotation>
  <xs:union memberTypes="xs:nonNegativeInteger">
   <xs:simpleType>
    <xs:restriction base="xs:NMTOKEN">
     <xs:enumeration value="unbounded"/>
    </xs:restriction>
   </xs:simpleType>
  </xs:union>
 </xs:simpleType>

 <xs:attributeGroup name="occurs">
  <xs:annotation><xs:documentation>
   for all particles</xs:documentation></xs:annotation>
  <xs:attribute name="minOccurs" type="xs:nonNegativeInteger" use="optional" default="1"/>
  <xs:attribute name="maxOccurs" type="xs:allNNI" use="optional" default="1"/>
 </xs:attributeGroup>

 <xs:attributeGroup name="defRef">
  <xs:annotation><xs:documentation>
   for element, group and attributeGroup,
   which both define and reference</xs:documentation></xs:annotation>
  <xs:attribute name="name" type="xs:NCName"/>
  <xs:attribute name="ref" type="xs:QName"/>
 </xs:attributeGroup>

 <xs:group name="typeDefParticle">
  <xs:annotation>
    <xs:documentation>
   'complexType' uses this</xs:documentation></xs:annotation>
  <xs:choice>
   <xs:element name="group" type="xs:groupRef"/>
   <xs:element ref="xs:all"/>
   <xs:element ref="xs:choice"/>
   <xs:element ref="xs:sequence"/>
  </xs:choice>
 </xs:group>



 <xs:group name="nestedParticle">
  <xs:choice>
   <xs:element name="element" type="xs:localElement"/>
   <xs:element name="group" type="xs:groupRef"/>
   <xs:element ref="xs:choice"/>
   <xs:element ref="xs:sequence"/>
   <xs:element ref="xs:any"/>
  </xs:choice>
 </xs:group>

 <xs:group name="particle">
  <xs:choice>
   <xs:element name="element" type="xs:localElement"/>
   <xs:element name="group" type="xs:groupRef"/>
   <xs:element ref="xs:all"/>
   <xs:element ref="xs:choice"/>
   <xs:element ref="xs:sequence"/>
   <xs:element ref="xs:any"/>
  </xs:choice>
 </xs:group>

 <xs:complexType name="attribute">
  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:sequence>
     <xs:element name="simpleType" minOccurs="0" type="xs:localSimpleType"/>
    </xs:sequence>
    <xs:attributeGroup ref="xs:defRef"/>
    <xs:attribute name="type" type="xs:QName"/>
    <xs:attribute name="use" use="optional" default="optional">
     <xs:simpleType>
      <xs:restriction base="xs:NMTOKEN">
       <xs:enumeration value="prohibited"/>
       <xs:enumeration value="optional"/>
       <xs:enumeration value="required"/>
      </xs:restriction>
     </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="default" type="xs:string"/>
    <xs:attribute name="fixed" type="xs:string"/>
    <xs:attribute name="form" type="xs:formChoice"/>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="topLevelAttribute">
  <xs:complexContent>
   <xs:restriction base="xs:attribute">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:element name="simpleType" minOccurs="0" type="xs:localSimpleType"/>
    </xs:sequence>
    <xs:attribute name="ref" use="prohibited"/>
    <xs:attribute name="form" use="prohibited"/>
    <xs:attribute name="use" use="prohibited"/>
    <xs:attribute name="name" use="required" type="xs:NCName"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:group name="attrDecls">
  <xs:sequence>
   <xs:choice minOccurs="0" maxOccurs="unbounded">
    <xs:element name="attribute" type="xs:attribute"/>
    <xs:element name="attributeGroup" type="xs:attributeGroupRef"/>
   </xs:choice>
   <xs:element ref="xs:anyAttribute" minOccurs="0"/>
  </xs:sequence>
 </xs:group>

 <xs:element name="anyAttribute" type="xs:wildcard" id="anyAttribute">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-anyAttribute"/>
  </xs:annotation>
 </xs:element>

 <xs:group name="complexTypeModel">
  <xs:choice>
      <xs:element ref="xs:simpleContent"/>
      <xs:element ref="xs:complexContent"/>
      <xs:sequence>
       <xs:annotation>
        <xs:documentation>
   This branch is short for
   &lt;complexContent&gt;
   &lt;restriction base="xs:anyType"&gt;
   ...
   &lt;/restriction&gt;
   &lt;/complexContent&gt;</xs:documentation>
       </xs:annotation>
       <xs:group ref="xs:typeDefParticle" minOccurs="0"/>
       <xs:group ref="xs:attrDecls"/>
      </xs:sequence>
  </xs:choice>
 </xs:group>

 <xs:complexType name="complexType" abstract="true">
  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:group ref="xs:complexTypeModel"/>
    <xs:attribute name="name" type="xs:NCName">
     <xs:annotation>
      <xs:documentation>
      Will be restricted to required or forbidden</xs:documentation>
     </xs:annotation>
    </xs:attribute>
    <xs:attribute name="mixed" type="xs:boolean" use="optional" default="false">
     <xs:annotation>
      <xs:documentation>
      Not allowed if simpleContent child is chosen.
      May be overriden by setting on complexContent child.</xs:documentation>
    </xs:annotation>
    </xs:attribute>
    <xs:attribute name="abstract" type="xs:boolean" use="optional" default="false"/>
    <xs:attribute name="final" type="xs:derivationSet"/>
    <xs:attribute name="block" type="xs:derivationSet"/>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="topLevelComplexType">
  <xs:complexContent>
   <xs:restriction base="xs:complexType">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:group ref="xs:complexTypeModel"/>
    </xs:sequence>
    <xs:attribute name="name" type="xs:NCName" use="required"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="localComplexType">
  <xs:complexContent>
   <xs:restriction base="xs:complexType">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:group ref="xs:complexTypeModel"/>
    </xs:sequence>
    <xs:attribute name="name" use="prohibited"/>
    <xs:attribute name="abstract" use="prohibited"/>
    <xs:attribute name="final" use="prohibited"/>
    <xs:attribute name="block" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="restrictionType">
  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:sequence>
     <xs:choice minOccurs="0">
      <xs:group ref="xs:typeDefParticle"/>
      <xs:group ref="xs:simpleRestrictionModel"/>
     </xs:choice>
     <xs:group ref="xs:attrDecls"/>
    </xs:sequence>
    <xs:attribute name="base" type="xs:QName" use="required"/>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="complexRestrictionType">
  <xs:complexContent>
   <xs:restriction base="xs:restrictionType">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:choice minOccurs="0">
      <xs:annotation>
       <xs:documentation>This choice is added simply to
                   make this a valid restriction per the REC</xs:documentation>
      </xs:annotation>
      <xs:group ref="xs:typeDefParticle"/>
     </xs:choice>
     <xs:group ref="xs:attrDecls"/>
    </xs:sequence>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="extensionType">
  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:sequence>
     <xs:group ref="xs:typeDefParticle" minOccurs="0"/>
     <xs:group ref="xs:attrDecls"/>
    </xs:sequence>
    <xs:attribute name="base" type="xs:QName" use="required"/>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:element name="complexContent" id="complexContent">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-complexContent"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:annotated">
     <xs:choice>
      <xs:element name="restriction" type="xs:complexRestrictionType"/>
      <xs:element name="extension" type="xs:extensionType"/>
     </xs:choice>
     <xs:attribute name="mixed" type="xs:boolean">
      <xs:annotation>
       <xs:documentation>
       Overrides any setting on complexType parent.</xs:documentation>
      </xs:annotation>
    </xs:attribute>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>
 </xs:element>

 <xs:complexType name="simpleRestrictionType">
  <xs:complexContent>
   <xs:restriction base="xs:restrictionType">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:choice minOccurs="0">
      <xs:annotation>
       <xs:documentation>This choice is added simply to
                   make this a valid restriction per the REC</xs:documentation>
      </xs:annotation>
      <xs:group ref="xs:simpleRestrictionModel"/>
     </xs:choice>
     <xs:group ref="xs:attrDecls"/>
    </xs:sequence>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="simpleExtensionType">
  <xs:complexContent>
   <xs:restriction base="xs:extensionType">
    <xs:sequence>
     <xs:annotation>
      <xs:documentation>
      No typeDefParticle group reference</xs:documentation>
     </xs:annotation>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:group ref="xs:attrDecls"/>
    </xs:sequence>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:element name="simpleContent" id="simpleContent">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-simpleContent"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:annotated">
     <xs:choice>
      <xs:element name="restriction" type="xs:simpleRestrictionType"/>
      <xs:element name="extension" type="xs:simpleExtensionType"/>
     </xs:choice>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>
 </xs:element>

 <xs:element name="complexType" type="xs:topLevelComplexType" id="complexType">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-complexType"/>
  </xs:annotation>
 </xs:element>


  <xs:simpleType name="blockSet">
   <xs:annotation>
    <xs:documentation>
    A utility type, not for public use</xs:documentation>
    <xs:documentation>
    #all or (possibly empty) subset of {substitution, extension,
    restriction}</xs:documentation>
   </xs:annotation>
   <xs:union>
    <xs:simpleType>
     <xs:restriction base="xs:token">
      <xs:enumeration value="#all"/>
     </xs:restriction>
    </xs:simpleType>
    <xs:simpleType>
     <xs:list>
      <xs:simpleType>
       <xs:restriction base="xs:derivationControl">
        <xs:enumeration value="extension"/>
        <xs:enumeration value="restriction"/>
        <xs:enumeration value="substitution"/>
       </xs:restriction>
      </xs:simpleType>
     </xs:list>
    </xs:simpleType>
   </xs:union>
  </xs:simpleType>

 <xs:complexType name="element" abstract="true">
  <xs:annotation>
   <xs:documentation>
   The element element can be used either
   at the top level to define an element-type binding globally,
   or within a content model to either reference a globally-defined
   element or type or declare an element-type binding locally.
   The ref form is not allowed at the top level.</xs:documentation>
  </xs:annotation>

  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:sequence>
     <xs:choice minOccurs="0">
      <xs:element name="simpleType" type="xs:localSimpleType"/>
      <xs:element name="complexType" type="xs:localComplexType"/>
     </xs:choice>
     <xs:group ref="xs:identityConstraint" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attributeGroup ref="xs:defRef"/>
    <xs:attribute name="type" type="xs:QName"/>
    <xs:attribute name="substitutionGroup" type="xs:QName"/>
    <xs:attributeGroup ref="xs:occurs"/>
    <xs:attribute name="default" type="xs:string"/>
    <xs:attribute name="fixed" type="xs:string"/>
    <xs:attribute name="nillable" type="xs:boolean" use="optional" default="false"/>
    <xs:attribute name="abstract" type="xs:boolean" use="optional" default="false"/>
    <xs:attribute name="final" type="xs:derivationSet"/>
    <xs:attribute name="block" type="xs:blockSet"/>
    <xs:attribute name="form" type="xs:formChoice"/>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="topLevelElement">
  <xs:complexContent>
   <xs:restriction base="xs:element">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:choice minOccurs="0">
      <xs:element name="simpleType" type="xs:localSimpleType"/>
      <xs:element name="complexType" type="xs:localComplexType"/>
     </xs:choice>
     <xs:group ref="xs:identityConstraint" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="ref" use="prohibited"/>
    <xs:attribute name="form" use="prohibited"/>
    <xs:attribute name="minOccurs" use="prohibited"/>
    <xs:attribute name="maxOccurs" use="prohibited"/>
    <xs:attribute name="name" use="required" type="xs:NCName"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="localElement">
  <xs:complexContent>
   <xs:restriction base="xs:element">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:choice minOccurs="0">
      <xs:element name="simpleType" type="xs:localSimpleType"/>
      <xs:element name="complexType" type="xs:localComplexType"/>
     </xs:choice>
     <xs:group ref="xs:identityConstraint" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="substitutionGroup" use="prohibited"/>
    <xs:attribute name="final" use="prohibited"/>
    <xs:attribute name="abstract" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:element name="element" type="xs:topLevelElement" id="element">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-element"/>
  </xs:annotation>
 </xs:element>

 <xs:complexType name="group" abstract="true">
  <xs:annotation>
   <xs:documentation>
   group type for explicit groups, named top-level groups and
   group references</xs:documentation>
  </xs:annotation>
  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:group ref="xs:particle" minOccurs="0" maxOccurs="unbounded"/>
    <xs:attributeGroup ref="xs:defRef"/>
    <xs:attributeGroup ref="xs:occurs"/>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="realGroup">
  <xs:complexContent>
   <xs:restriction base="xs:group">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:choice minOccurs="0" maxOccurs="1">
      <xs:element ref="xs:all"/>
      <xs:element ref="xs:choice"/>
      <xs:element ref="xs:sequence"/>
     </xs:choice>
    </xs:sequence>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="namedGroup">
  <xs:complexContent>
   <xs:restriction base="xs:realGroup">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:choice minOccurs="1" maxOccurs="1">
      <xs:element name="all">
       <xs:complexType>
        <xs:complexContent>
         <xs:restriction base="xs:all">
          <xs:group ref="xs:allModel"/>
          <xs:attribute name="minOccurs" use="prohibited"/>
          <xs:attribute name="maxOccurs" use="prohibited"/>
          <xs:anyAttribute namespace="##other" processContents="lax"/>
         </xs:restriction>
        </xs:complexContent>
       </xs:complexType>
      </xs:element>
      <xs:element name="choice" type="xs:simpleExplicitGroup"/>
      <xs:element name="sequence" type="xs:simpleExplicitGroup"/>
     </xs:choice>
    </xs:sequence>
    <xs:attribute name="name" use="required" type="xs:NCName"/>
    <xs:attribute name="ref" use="prohibited"/>
    <xs:attribute name="minOccurs" use="prohibited"/>
    <xs:attribute name="maxOccurs" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="groupRef">
  <xs:complexContent>
   <xs:restriction base="xs:realGroup">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="ref" use="required" type="xs:QName"/>
    <xs:attribute name="name" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="explicitGroup">
  <xs:annotation>
   <xs:documentation>
   group type for the three kinds of group</xs:documentation>
  </xs:annotation>
  <xs:complexContent>
   <xs:restriction base="xs:group">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:group ref="xs:nestedParticle" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="name" type="xs:NCName" use="prohibited"/>
    <xs:attribute name="ref" type="xs:QName" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="simpleExplicitGroup">
  <xs:complexContent>
   <xs:restriction base="xs:explicitGroup">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:group ref="xs:nestedParticle" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="minOccurs" use="prohibited"/>
    <xs:attribute name="maxOccurs" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:group name="allModel">
  <xs:sequence>
      <xs:element ref="xs:annotation" minOccurs="0"/>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
       <xs:annotation>
        <xs:documentation>This choice with min/max is here to
                          avoid a pblm with the Elt:All/Choice/Seq
                          Particle derivation constraint</xs:documentation>
       </xs:annotation>
       <xs:element name="element" type="xs:narrowMaxMin"/>
      </xs:choice>
     </xs:sequence>
 </xs:group>


 <xs:complexType name="narrowMaxMin">
  <xs:annotation>
   <xs:documentation>restricted max/min</xs:documentation>
  </xs:annotation>
  <xs:complexContent>
   <xs:restriction base="xs:localElement">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:choice minOccurs="0">
      <xs:element name="simpleType" type="xs:localSimpleType"/>
      <xs:element name="complexType" type="xs:localComplexType"/>
     </xs:choice>
     <xs:group ref="xs:identityConstraint" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="minOccurs" use="optional" default="1">
     <xs:simpleType>
      <xs:restriction base="xs:nonNegativeInteger">
       <xs:enumeration value="0"/>
       <xs:enumeration value="1"/>
      </xs:restriction>
     </xs:simpleType>
    </xs:attribute>
    <xs:attribute name="maxOccurs" use="optional" default="1">
     <xs:simpleType>
      <xs:restriction base="xs:allNNI">
       <xs:enumeration value="0"/>
       <xs:enumeration value="1"/>
      </xs:restriction>
     </xs:simpleType>
    </xs:attribute>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

  <xs:complexType name="all">
   <xs:annotation>
    <xs:documentation>
   Only elements allowed inside</xs:documentation>
   </xs:annotation>
   <xs:complexContent>
    <xs:restriction base="xs:explicitGroup">
     <xs:group ref="xs:allModel"/>
     <xs:attribute name="minOccurs" use="optional" default="1">
      <xs:simpleType>
       <xs:restriction base="xs:nonNegativeInteger">
        <xs:enumeration value="0"/>
        <xs:enumeration value="1"/>
       </xs:restriction>
      </xs:simpleType>
     </xs:attribute>
     <xs:attribute name="maxOccurs" use="optional" default="1">
      <xs:simpleType>
       <xs:restriction base="xs:allNNI">
        <xs:enumeration value="1"/>
       </xs:restriction>
      </xs:simpleType>
     </xs:attribute>
     <xs:anyAttribute namespace="##other" processContents="lax"/>
    </xs:restriction>
   </xs:complexContent>
  </xs:complexType>

 <xs:element name="all" id="all" type="xs:all">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-all"/>
  </xs:annotation>
 </xs:element>

 <xs:element name="choice" type="xs:explicitGroup" id="choice">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-choice"/>
  </xs:annotation>
 </xs:element>

 <xs:element name="sequence" type="xs:explicitGroup" id="sequence">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-sequence"/>
  </xs:annotation>
 </xs:element>

 <xs:element name="group" type="xs:namedGroup" id="group">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-group"/>
  </xs:annotation>
 </xs:element>

 <xs:complexType name="wildcard">
  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:attribute name="namespace" type="xs:namespaceList" use="optional" default="##any"/>
    <xs:attribute name="processContents" use="optional" default="strict">
     <xs:simpleType>
      <xs:restriction base="xs:NMTOKEN">
       <xs:enumeration value="skip"/>
       <xs:enumeration value="lax"/>
       <xs:enumeration value="strict"/>
      </xs:restriction>
     </xs:simpleType>
    </xs:attribute>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:element name="any" id="any">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-any"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:wildcard">
     <xs:attributeGroup ref="xs:occurs"/>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>
 </xs:element>

  <xs:annotation>
   <xs:documentation>
   simple type for the value of the 'namespace' attr of
   'any' and 'anyAttribute'</xs:documentation>
  </xs:annotation>
  <xs:annotation>
   <xs:documentation>
   Value is
              ##any      - - any non-conflicting WFXML/attribute at all

              ##other    - - any non-conflicting WFXML/attribute from
                              namespace other than targetNS

              ##local    - - any unqualified non-conflicting WFXML/attribute

              one or     - - any non-conflicting WFXML/attribute from
              more URI        the listed namespaces
              references
              (space separated)

    ##targetNamespace or ##local may appear in the above list, to
        refer to the targetNamespace of the enclosing
        schema or an absent targetNamespace respectively</xs:documentation>
  </xs:annotation>

 <xs:simpleType name="namespaceList">
  <xs:annotation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
  </xs:annotation>
  <xs:union>
   <xs:simpleType>
    <xs:restriction base="xs:token">
     <xs:enumeration value="##any"/>
     <xs:enumeration value="##other"/>
    </xs:restriction>
   </xs:simpleType>
   <xs:simpleType>
    <xs:list>
     <xs:simpleType>
      <xs:union memberTypes="xs:anyURI">
       <xs:simpleType>
        <xs:restriction base="xs:token">
         <xs:enumeration value="##targetNamespace"/>
         <xs:enumeration value="##local"/>
        </xs:restriction>
       </xs:simpleType>
      </xs:union>
     </xs:simpleType>
    </xs:list>
   </xs:simpleType>
  </xs:union>
 </xs:simpleType>

 <xs:element name="attribute" type="xs:topLevelAttribute" id="attribute">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-attribute"/>
  </xs:annotation>
 </xs:element>

 <xs:complexType name="attributeGroup" abstract="true">
  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:group ref="xs:attrDecls"/>
    <xs:attributeGroup ref="xs:defRef"/>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="namedAttributeGroup">
  <xs:complexContent>
   <xs:restriction base="xs:attributeGroup">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
     <xs:group ref="xs:attrDecls"/>
    </xs:sequence>
    <xs:attribute name="name" use="required" type="xs:NCName"/>
    <xs:attribute name="ref" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:complexType name="attributeGroupRef">
  <xs:complexContent>
   <xs:restriction base="xs:attributeGroup">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="ref" use="required" type="xs:QName"/>
    <xs:attribute name="name" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

 <xs:element name="attributeGroup" type="xs:namedAttributeGroup" id="attributeGroup">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-attributeGroup"/>
  </xs:annotation>
 </xs:element>

 <xs:element name="include" id="include">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-include"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:annotated">
     <xs:attribute name="schemaLocation" type="xs:anyURI" use="required"/>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>
 </xs:element>

 <xs:element name="redefine" id="redefine">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-redefine"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:openAttrs">
     <xs:choice minOccurs="0" maxOccurs="unbounded">
      <xs:element ref="xs:annotation"/>
      <xs:group ref="xs:redefinable"/>
     </xs:choice>
     <xs:attribute name="schemaLocation" type="xs:anyURI" use="required"/>
     <xs:attribute name="id" type="xs:ID"/>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>
 </xs:element>

 <xs:element name="import" id="import">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-import"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:annotated">
     <xs:attribute name="namespace" type="xs:anyURI"/>
     <xs:attribute name="schemaLocation" type="xs:anyURI"/>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>
 </xs:element>

 <xs:element name="selector" id="selector">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-selector"/>
  </xs:annotation>
  <xs:complexType>
  <xs:complexContent>
   <xs:extension base="xs:annotated">
     <xs:attribute name="xpath" use="required">
      <xs:simpleType>
       <xs:annotation>
        <xs:documentation>A subset of XPath expressions for use
in selectors</xs:documentation>
        <xs:documentation>A utility type, not for public
use</xs:documentation>
       </xs:annotation>
       <xs:restriction base="xs:token">
        <xs:annotation>
         <xs:documentation>The following pattern is intended to allow XPath
                           expressions per the following EBNF:
          Selector    ::=    Path ( '|' Path )*
          Path    ::=    ('.//')? Step ( '/' Step )*
          Step    ::=    '.' | NameTest
          NameTest    ::=    QName | '*' | NCName ':' '*'
                           child:: is also allowed
         </xs:documentation>
        </xs:annotation>
        <xs:pattern value="(\.//)?(((child::)?((\i\c*:)?(\i\c*|\*)))|\.)(/(((child::)?((\i\c*:)?(\i\c*|\*)))|\.))*(\|(\.//)?(((child::)?((\i\c*:)?(\i\c*|\*)))|\.)(/(((child::)?((\i\c*:)?(\i\c*|\*)))|\.))*)*">
        </xs:pattern>
       </xs:restriction>
      </xs:simpleType>
     </xs:attribute>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>
 </xs:element>

 <xs:element name="field" id="field">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-field"/>
  </xs:annotation>
  <xs:complexType>
  <xs:complexContent>
   <xs:extension base="xs:annotated">
     <xs:attribute name="xpath" use="required">
      <xs:simpleType>
       <xs:annotation>
        <xs:documentation>A subset of XPath expressions for use
in fields</xs:documentation>
        <xs:documentation>A utility type, not for public
use</xs:documentation>
       </xs:annotation>
       <xs:restriction base="xs:token">
        <xs:annotation>
         <xs:documentation>The following pattern is intended to allow XPath
                           expressions per the same EBNF as for selector,
                           with the following change:
          Path    ::=    ('.//')? ( Step '/' )* ( Step | '@' NameTest )
         </xs:documentation>
        </xs:annotation>
        <xs:pattern value="(\.//)?((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)/)*((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)|((attribute::|@)((\i\c*:)?(\i\c*|\*))))(\|(\.//)?((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)/)*((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)|((attribute::|@)((\i\c*:)?(\i\c*|\*)))))*">
        </xs:pattern>
       </xs:restriction>
      </xs:simpleType>
     </xs:attribute>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>
 </xs:element>

 <xs:complexType name="keybase">
  <xs:complexContent>
   <xs:extension base="xs:annotated">
    <xs:sequence>
     <xs:element ref="xs:selector"/>
     <xs:element ref="xs:field" minOccurs="1" maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="name" type="xs:NCName" use="required"/>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>

 <xs:group name="identityConstraint">
  <xs:annotation>
   <xs:documentation>The three kinds of identity constraints, all with
                     type of or derived from 'keybase'.
   </xs:documentation>
  </xs:annotation>
  <xs:choice>
   <xs:element ref="xs:unique"/>
   <xs:element ref="xs:key"/>
   <xs:element ref="xs:keyref"/>
  </xs:choice>
 </xs:group>

 <xs:element name="unique" type="xs:keybase" id="unique">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-unique"/>
  </xs:annotation>
 </xs:element>
 <xs:element name="key" type="xs:keybase" id="key">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-key"/>
  </xs:annotation>
 </xs:element>
 <xs:element name="keyref" id="keyref">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-keyref"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:keybase">
     <xs:attribute name="refer" type="xs:QName" use="required"/>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>
 </xs:element>

 <xs:element name="notation" id="notation">
  <xs:annotation>
   <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-notation"/>
  </xs:annotation>
  <xs:complexType>
   <xs:complexContent>
    <xs:extension base="xs:annotated">
     <xs:attribute name="name" type="xs:NCName" use="required"/>
     <xs:attribute name="public" type="xs:public"/>
     <xs:attribute name="system" type="xs:anyURI"/>
    </xs:extension>
   </xs:complexContent>
  </xs:complexType>
 </xs:element>

 <xs:simpleType name="public">
  <xs:annotation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
   <xs:documentation>
   A public identifier, per ISO 8879</xs:documentation>
  </xs:annotation>
  <xs:restriction base="xs:token"/>
 </xs:simpleType>

 <xs:element name="appinfo" id="appinfo">
   <xs:annotation>
     <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-appinfo"/>
   </xs:annotation>
   <xs:complexType mixed="true">
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
     <xs:any processContents="lax"/>
    </xs:sequence>
    <xs:attribute name="source" type="xs:anyURI"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:complexType>
 </xs:element>

 <xs:element name="documentation" id="documentation">
   <xs:annotation>
     <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-documentation"/>
   </xs:annotation>
   <xs:complexType mixed="true">
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
     <xs:any processContents="lax"/>
    </xs:sequence>
    <xs:attribute name="source" type="xs:anyURI"/>
    <xs:attribute ref="xml:lang"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:complexType>
 </xs:element>

 <xs:element name="annotation" id="annotation">
   <xs:annotation>
     <xs:documentation source="http://www.w3.org/TR/xmlschema-1/#element-annotation"/>
   </xs:annotation>
   <xs:complexType>
    <xs:complexContent>
     <xs:extension base="xs:openAttrs">
      <xs:choice minOccurs="0" maxOccurs="unbounded">
       <xs:element ref="xs:appinfo"/>
       <xs:element ref="xs:documentation"/>
      </xs:choice>
      <xs:attribute name="id" type="xs:ID"/>
     </xs:extension>
    </xs:complexContent>
   </xs:complexType>
 </xs:element>

 <xs:annotation>
  <xs:documentation>
   notations for use within XML Schema schemas</xs:documentation>
 </xs:annotation>

 <xs:notation name="XMLSchemaStructures" public="structures" system="http://www.w3.org/2000/08/XMLSchema.xsd"/>
 <xs:notation name="XML" public="REC-xml-19980210" system="http://www.w3.org/TR/1998/REC-xml-19980210"/>

 <xs:complexType name="anyType" mixed="true">
  <xs:annotation>
   <xs:documentation>
   Not the real urType, but as close an approximation as we can
   get in the XML representation</xs:documentation>
  </xs:annotation>
  <xs:sequence>
   <xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
  </xs:sequence>
  <xs:anyAttribute processContents="lax"/>
 </xs:complexType>

  <xs:annotation>
    <xs:documentation>
      First the built-in primitive datatypes.  These definitions are for
      information only, the real built-in definitions are magic.
    </xs:documentation>

    <xs:documentation>
      For each built-in datatype in this schema (both primitive and
      derived) can be uniquely addressed via a URI constructed
      as follows:
        1) the base URI is the URI of the XML Schema namespace
        2) the fragment identifier is the name of the datatype

      For example, to address the int datatype, the URI is:

        http://www.w3.org/2001/XMLSchema#int

      Additionally, each facet definition element can be uniquely
      addressed via a URI constructed as follows:
        1) the base URI is the URI of the XML Schema namespace
        2) the fragment identifier is the name of the facet

      For example, to address the maxInclusive facet, the URI is:

        http://www.w3.org/2001/XMLSchema#maxInclusive

      Additionally, each facet usage in a built-in datatype definition
      can be uniquely addressed via a URI constructed as follows:
        1) the base URI is the URI of the XML Schema namespace
        2) the fragment identifier is the name of the datatype, followed
           by a period (".") followed by the name of the facet

      For example, to address the usage of the maxInclusive facet in
      the definition of int, the URI is:

        http://www.w3.org/2001/XMLSchema#int.maxInclusive

    </xs:documentation>
  </xs:annotation>

  <xs:simpleType name="string" id="string">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#string"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="preserve" id="string.preserve"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="boolean" id="boolean">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="finite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#boolean"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="boolean.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="float" id="float">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="total"/>
        <hfp:hasProperty name="bounded" value="true"/>
        <hfp:hasProperty name="cardinality" value="finite"/>
        <hfp:hasProperty name="numeric" value="true"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#float"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="float.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="double" id="double">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="total"/>
        <hfp:hasProperty name="bounded" value="true"/>
        <hfp:hasProperty name="cardinality" value="finite"/>
        <hfp:hasProperty name="numeric" value="true"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#double"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="double.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="decimal" id="decimal">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="totalDigits"/>
        <hfp:hasFacet name="fractionDigits"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="total"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="true"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#decimal"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="decimal.whiteSpace"/>
    </xs:restriction>
   </xs:simpleType>

   <xs:simpleType name="duration" id="duration">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#duration"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="duration.whiteSpace"/>
    </xs:restriction>
   </xs:simpleType>

 <xs:simpleType name="dateTime" id="dateTime">
    <xs:annotation>
    <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#dateTime"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="dateTime.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="time" id="time">
    <xs:annotation>
    <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#time"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="time.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="date" id="date">
   <xs:annotation>
    <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#date"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="date.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="gYearMonth" id="gYearMonth">
   <xs:annotation>
    <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gYearMonth"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="gYearMonth.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="gYear" id="gYear">
    <xs:annotation>
    <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gYear"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="gYear.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

 <xs:simpleType name="gMonthDay" id="gMonthDay">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
       <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gMonthDay"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
         <xs:whiteSpace value="collapse" fixed="true" id="gMonthDay.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="gDay" id="gDay">
    <xs:annotation>
  <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gDay"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
         <xs:whiteSpace value="collapse" fixed="true" id="gDay.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

 <xs:simpleType name="gMonth" id="gMonth">
    <xs:annotation>
  <xs:appinfo>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="maxInclusive"/>
        <hfp:hasFacet name="maxExclusive"/>
        <hfp:hasFacet name="minInclusive"/>
        <hfp:hasFacet name="minExclusive"/>
        <hfp:hasProperty name="ordered" value="partial"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#gMonth"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
         <xs:whiteSpace value="collapse" fixed="true" id="gMonth.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

   <xs:simpleType name="hexBinary" id="hexBinary">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#binary"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="hexBinary.whiteSpace"/>
    </xs:restriction>
   </xs:simpleType>

 <xs:simpleType name="base64Binary" id="base64Binary">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#base64Binary"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="base64Binary.whiteSpace"/>
    </xs:restriction>
   </xs:simpleType>

   <xs:simpleType name="anyURI" id="anyURI">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#anyURI"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="anyURI.whiteSpace"/>
    </xs:restriction>
   </xs:simpleType>

  <xs:simpleType name="QName" id="QName">
    <xs:annotation>
        <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#QName"/>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="QName.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

   <xs:simpleType name="NOTATION" id="NOTATION">
    <xs:annotation>
        <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#NOTATION"/>
      <xs:documentation>
        NOTATION cannot be used directly in a schema; rather a type
        must be derived from it by specifying at least one enumeration
        facet whose value is the name of a NOTATION declared in the
        schema.
      </xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:anySimpleType">
      <xs:whiteSpace value="collapse" fixed="true" id="NOTATION.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:annotation>
    <xs:documentation>
      Now the derived primitive types
    </xs:documentation>
  </xs:annotation>

  <xs:simpleType name="normalizedString" id="normalizedString">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#normalizedString"/>
    </xs:annotation>
    <xs:restriction base="xs:string">
      <xs:whiteSpace value="replace" id="normalizedString.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="token" id="token">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#token"/>
    </xs:annotation>
    <xs:restriction base="xs:normalizedString">
      <xs:whiteSpace value="collapse" id="token.whiteSpace"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="language" id="language">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#language"/>
    </xs:annotation>
    <xs:restriction base="xs:token">
      <xs:pattern value="[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*" id="language.pattern">
        <xs:annotation>
          <xs:documentation source="http://www.ietf.org/rfc/rfc3066.txt">
            pattern specifies the content of section 2.12 of XML 1.0e2
            and RFC 3066 (Revised version of RFC 1766).
          </xs:documentation>
        </xs:annotation>
      </xs:pattern>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="IDREFS" id="IDREFS">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#IDREFS"/>
    </xs:annotation>
    <xs:restriction>
      <xs:simpleType>
        <xs:list itemType="xs:IDREF"/>
      </xs:simpleType>
        <xs:minLength value="1" id="IDREFS.minLength"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="ENTITIES" id="ENTITIES">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#ENTITIES"/>
    </xs:annotation>
    <xs:restriction>
      <xs:simpleType>
        <xs:list itemType="xs:ENTITY"/>
      </xs:simpleType>
        <xs:minLength value="1" id="ENTITIES.minLength"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="NMTOKEN" id="NMTOKEN">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#NMTOKEN"/>
    </xs:annotation>
    <xs:restriction base="xs:token">
      <xs:pattern value="\c+" id="NMTOKEN.pattern">
        <xs:annotation>
          <xs:documentation source="http://www.w3.org/TR/REC-xml#NT-Nmtoken">
            pattern matches production 7 from the XML spec
          </xs:documentation>
        </xs:annotation>
      </xs:pattern>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="NMTOKENS" id="NMTOKENS">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasFacet name="length"/>
        <hfp:hasFacet name="minLength"/>
        <hfp:hasFacet name="maxLength"/>
        <hfp:hasFacet name="enumeration"/>
        <hfp:hasFacet name="whiteSpace"/>
        <hfp:hasFacet name="pattern"/>
        <hfp:hasProperty name="ordered" value="false"/>
        <hfp:hasProperty name="bounded" value="false"/>
        <hfp:hasProperty name="cardinality" value="countably infinite"/>
        <hfp:hasProperty name="numeric" value="false"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#NMTOKENS"/>
    </xs:annotation>
    <xs:restriction>
      <xs:simpleType>
        <xs:list itemType="xs:NMTOKEN"/>
      </xs:simpleType>
        <xs:minLength value="1" id="NMTOKENS.minLength"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="Name" id="Name">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#Name"/>
    </xs:annotation>
    <xs:restriction base="xs:token">
      <xs:pattern value="\i\c*" id="Name.pattern">
        <xs:annotation>
          <xs:documentation source="http://www.w3.org/TR/REC-xml#NT-Name">
            pattern matches production 5 from the XML spec
          </xs:documentation>
        </xs:annotation>
      </xs:pattern>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="NCName" id="NCName">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#NCName"/>
    </xs:annotation>
    <xs:restriction base="xs:Name">
      <xs:pattern value="[\i-[:]][\c-[:]]*" id="NCName.pattern">
        <xs:annotation>
          <xs:documentation source="http://www.w3.org/TR/REC-xml-names/#NT-NCName">
            pattern matches production 4 from the Namespaces in XML spec
          </xs:documentation>
        </xs:annotation>
      </xs:pattern>
    </xs:restriction>
  </xs:simpleType>

   <xs:simpleType name="ID" id="ID">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#ID"/>
    </xs:annotation>
    <xs:restriction base="xs:NCName"/>
   </xs:simpleType>

   <xs:simpleType name="IDREF" id="IDREF">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#IDREF"/>
    </xs:annotation>
    <xs:restriction base="xs:NCName"/>
   </xs:simpleType>

   <xs:simpleType name="ENTITY" id="ENTITY">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#ENTITY"/>
    </xs:annotation>
    <xs:restriction base="xs:NCName"/>
   </xs:simpleType>

  <xs:simpleType name="integer" id="integer">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#integer"/>
    </xs:annotation>
    <xs:restriction base="xs:decimal">
      <xs:fractionDigits value="0" fixed="true" id="integer.fractionDigits"/>
      <xs:pattern value="[\-+]?[0-9]+"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="nonPositiveInteger" id="nonPositiveInteger">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#nonPositiveInteger"/>
    </xs:annotation>
    <xs:restriction base="xs:integer">
      <xs:maxInclusive value="0" id="nonPositiveInteger.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="negativeInteger" id="negativeInteger">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#negativeInteger"/>
    </xs:annotation>
    <xs:restriction base="xs:nonPositiveInteger">
      <xs:maxInclusive value="-1" id="negativeInteger.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="long" id="long">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasProperty name="bounded" value="true"/>
        <hfp:hasProperty name="cardinality" value="finite"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#long"/>
    </xs:annotation>
    <xs:restriction base="xs:integer">
      <xs:minInclusive value="-9223372036854775808" id="long.minInclusive"/>
      <xs:maxInclusive value="9223372036854775807" id="long.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="int" id="int">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#int"/>
    </xs:annotation>
    <xs:restriction base="xs:long">
      <xs:minInclusive value="-2147483648" id="int.minInclusive"/>
      <xs:maxInclusive value="2147483647" id="int.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="short" id="short">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#short"/>
    </xs:annotation>
    <xs:restriction base="xs:int">
      <xs:minInclusive value="-32768" id="short.minInclusive"/>
      <xs:maxInclusive value="32767" id="short.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="byte" id="byte">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#byte"/>
    </xs:annotation>
    <xs:restriction base="xs:short">
      <xs:minInclusive value="-128" id="byte.minInclusive"/>
      <xs:maxInclusive value="127" id="byte.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="nonNegativeInteger" id="nonNegativeInteger">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#nonNegativeInteger"/>
    </xs:annotation>
    <xs:restriction base="xs:integer">
      <xs:minInclusive value="0" id="nonNegativeInteger.minInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="unsignedLong" id="unsignedLong">
    <xs:annotation>
      <xs:appinfo>
        <hfp:hasProperty name="bounded" value="true"/>
        <hfp:hasProperty name="cardinality" value="finite"/>
      </xs:appinfo>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#unsignedLong"/>
    </xs:annotation>
    <xs:restriction base="xs:nonNegativeInteger">
      <xs:maxInclusive value="18446744073709551615" id="unsignedLong.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="unsignedInt" id="unsignedInt">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#unsignedInt"/>
    </xs:annotation>
    <xs:restriction base="xs:unsignedLong">
      <xs:maxInclusive value="4294967295" id="unsignedInt.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="unsignedShort" id="unsignedShort">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#unsignedShort"/>
    </xs:annotation>
    <xs:restriction base="xs:unsignedInt">
      <xs:maxInclusive value="65535" id="unsignedShort.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="unsignedByte" id="unsignedByte">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#unsignedByte"/>
    </xs:annotation>
    <xs:restriction base="xs:unsignedShort">
      <xs:maxInclusive value="255" id="unsignedByte.maxInclusive"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="positiveInteger" id="positiveInteger">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#positiveInteger"/>
    </xs:annotation>
    <xs:restriction base="xs:nonNegativeInteger">
      <xs:minInclusive value="1" id="positiveInteger.minInclusive"/>
    </xs:restriction>
  </xs:simpleType>

 <xs:simpleType name="derivationControl">
  <xs:annotation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
  </xs:annotation>
  <xs:restriction base="xs:NMTOKEN">
   <xs:enumeration value="substitution"/>
   <xs:enumeration value="extension"/>
   <xs:enumeration value="restriction"/>
   <xs:enumeration value="list"/>
   <xs:enumeration value="union"/>
  </xs:restriction>
 </xs:simpleType>

 <xs:group name="simpleDerivation">
  <xs:choice>
    <xs:element ref="xs:restriction"/>
    <xs:element ref="xs:list"/>
    <xs:element ref="xs:union"/>
  </xs:choice>
 </xs:group>

 <xs:simpleType name="simpleDerivationSet">
  <xs:annotation>
   <xs:documentation>
   #all or (possibly empty) subset of {restriction, union, list}
   </xs:documentation>
   <xs:documentation>
   A utility type, not for public use</xs:documentation>
  </xs:annotation>
  <xs:union>
   <xs:simpleType>
    <xs:restriction base="xs:token">
     <xs:enumeration value="#all"/>
    </xs:restriction>
   </xs:simpleType>
   <xs:simpleType>
    <xs:list>
     <xs:simpleType>
      <xs:restriction base="xs:derivationControl">
       <xs:enumeration value="list"/>
       <xs:enumeration value="union"/>
       <xs:enumeration value="restriction"/>
      </xs:restriction>
     </xs:simpleType>
    </xs:list>
   </xs:simpleType>
  </xs:union>
 </xs:simpleType>

  <xs:complexType name="simpleType" abstract="true">
    <xs:complexContent>
      <xs:extension base="xs:annotated">
        <xs:group ref="xs:simpleDerivation"/>
        <xs:attribute name="final" type="xs:simpleDerivationSet"/>
        <xs:attribute name="name" type="xs:NCName">
          <xs:annotation>
            <xs:documentation>
              Can be restricted to required or forbidden
            </xs:documentation>
          </xs:annotation>
        </xs:attribute>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:complexType name="topLevelSimpleType">
    <xs:complexContent>
      <xs:restriction base="xs:simpleType">
        <xs:sequence>
          <xs:element ref="xs:annotation" minOccurs="0"/>
          <xs:group ref="xs:simpleDerivation"/>
        </xs:sequence>
        <xs:attribute name="name" use="required" type="xs:NCName">
          <xs:annotation>
            <xs:documentation>
              Required at the top level
            </xs:documentation>
          </xs:annotation>
        </xs:attribute>
       <xs:anyAttribute namespace="##other" processContents="lax"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>

  <xs:complexType name="localSimpleType">
    <xs:complexContent>
      <xs:restriction base="xs:simpleType">
        <xs:sequence>
          <xs:element ref="xs:annotation" minOccurs="0"/>
          <xs:group ref="xs:simpleDerivation"/>
        </xs:sequence>
        <xs:attribute name="name" use="prohibited">
          <xs:annotation>
            <xs:documentation>
              Forbidden when nested
            </xs:documentation>
          </xs:annotation>
        </xs:attribute>
        <xs:attribute name="final" use="prohibited"/>
       <xs:anyAttribute namespace="##other" processContents="lax"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>

  <xs:element name="simpleType" type="xs:topLevelSimpleType" id="simpleType">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-simpleType"/>
    </xs:annotation>
  </xs:element>

  <xs:group name="facets">
   <xs:annotation>
    <xs:documentation>
       We should use a substitution group for facets, but
       that's ruled out because it would allow users to
       add their own, which we're not ready for yet.
    </xs:documentation>
   </xs:annotation>
   <xs:choice>
    <xs:element ref="xs:minExclusive"/>
    <xs:element ref="xs:minInclusive"/>
    <xs:element ref="xs:maxExclusive"/>
    <xs:element ref="xs:maxInclusive"/>
    <xs:element ref="xs:totalDigits"/>
    <xs:element ref="xs:fractionDigits"/>
    <xs:element ref="xs:length"/>
    <xs:element ref="xs:minLength"/>
    <xs:element ref="xs:maxLength"/>
    <xs:element ref="xs:enumeration"/>
    <xs:element ref="xs:whiteSpace"/>
    <xs:element ref="xs:pattern"/>
   </xs:choice>
  </xs:group>

  <xs:group name="simpleRestrictionModel">
   <xs:sequence>
    <xs:element name="simpleType" type="xs:localSimpleType" minOccurs="0"/>
    <xs:group ref="xs:facets" minOccurs="0" maxOccurs="unbounded"/>
   </xs:sequence>
  </xs:group>

  <xs:element name="restriction" id="restriction">
   <xs:complexType>
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-restriction">
          base attribute and simpleType child are mutually
          exclusive, but one or other is required
        </xs:documentation>
      </xs:annotation>
      <xs:complexContent>
        <xs:extension base="xs:annotated">
         <xs:group ref="xs:simpleRestrictionModel"/>
         <xs:attribute name="base" type="xs:QName" use="optional"/>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

  <xs:element name="list" id="list">
   <xs:complexType>
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-list">
          itemType attribute and simpleType child are mutually
          exclusive, but one or other is required
        </xs:documentation>
      </xs:annotation>
      <xs:complexContent>
        <xs:extension base="xs:annotated">
          <xs:sequence>
            <xs:element name="simpleType" type="xs:localSimpleType" minOccurs="0"/>
          </xs:sequence>
          <xs:attribute name="itemType" type="xs:QName" use="optional"/>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

  <xs:element name="union" id="union">
   <xs:complexType>
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-union">
          memberTypes attribute must be non-empty or there must be
          at least one simpleType child
        </xs:documentation>
      </xs:annotation>
      <xs:complexContent>
        <xs:extension base="xs:annotated">
          <xs:sequence>
            <xs:element name="simpleType" type="xs:localSimpleType" minOccurs="0" maxOccurs="unbounded"/>
          </xs:sequence>
          <xs:attribute name="memberTypes" use="optional">
            <xs:simpleType>
              <xs:list itemType="xs:QName"/>
            </xs:simpleType>
          </xs:attribute>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

  <xs:complexType name="facet">
    <xs:complexContent>
      <xs:extension base="xs:annotated">
        <xs:attribute name="value" use="required"/>
        <xs:attribute name="fixed" type="xs:boolean" use="optional" default="false"/>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

 <xs:complexType name="noFixedFacet">
  <xs:complexContent>
   <xs:restriction base="xs:facet">
    <xs:sequence>
     <xs:element ref="xs:annotation" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="fixed" use="prohibited"/>
    <xs:anyAttribute namespace="##other" processContents="lax"/>
   </xs:restriction>
  </xs:complexContent>
 </xs:complexType>

  <xs:element name="minExclusive" id="minExclusive" type="xs:facet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-minExclusive"/>
    </xs:annotation>
  </xs:element>
  <xs:element name="minInclusive" id="minInclusive" type="xs:facet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-minInclusive"/>
    </xs:annotation>
  </xs:element>

  <xs:element name="maxExclusive" id="maxExclusive" type="xs:facet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-maxExclusive"/>
    </xs:annotation>
  </xs:element>
  <xs:element name="maxInclusive" id="maxInclusive" type="xs:facet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-maxInclusive"/>
    </xs:annotation>
  </xs:element>

  <xs:complexType name="numFacet">
    <xs:complexContent>
      <xs:restriction base="xs:facet">
       <xs:sequence>
         <xs:element ref="xs:annotation" minOccurs="0"/>
       </xs:sequence>
       <xs:attribute name="value" type="xs:nonNegativeInteger" use="required"/>
       <xs:anyAttribute namespace="##other" processContents="lax"/>
      </xs:restriction>
    </xs:complexContent>
  </xs:complexType>

  <xs:element name="totalDigits" id="totalDigits">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-totalDigits"/>
    </xs:annotation>
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="xs:numFacet">
          <xs:sequence>
            <xs:element ref="xs:annotation" minOccurs="0"/>
          </xs:sequence>
          <xs:attribute name="value" type="xs:positiveInteger" use="required"/>
         <xs:anyAttribute namespace="##other" processContents="lax"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>
  <xs:element name="fractionDigits" id="fractionDigits" type="xs:numFacet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-fractionDigits"/>
    </xs:annotation>
  </xs:element>

  <xs:element name="length" id="length" type="xs:numFacet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-length"/>
    </xs:annotation>
  </xs:element>
  <xs:element name="minLength" id="minLength" type="xs:numFacet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-minLength"/>
    </xs:annotation>
  </xs:element>
  <xs:element name="maxLength" id="maxLength" type="xs:numFacet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-maxLength"/>
    </xs:annotation>
  </xs:element>

  <xs:element name="enumeration" id="enumeration" type="xs:noFixedFacet">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-enumeration"/>
    </xs:annotation>
  </xs:element>

  <xs:element name="whiteSpace" id="whiteSpace">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-whiteSpace"/>
    </xs:annotation>
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="xs:facet">
          <xs:sequence>
            <xs:element ref="xs:annotation" minOccurs="0"/>
          </xs:sequence>
          <xs:attribute name="value" use="required">
            <xs:simpleType>
              <xs:restriction base="xs:NMTOKEN">
                <xs:enumeration value="preserve"/>
                <xs:enumeration value="replace"/>
                <xs:enumeration value="collapse"/>
              </xs:restriction>
            </xs:simpleType>
          </xs:attribute>
         <xs:anyAttribute namespace="##other" processContents="lax"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

  <xs:element name="pattern" id="pattern">
    <xs:annotation>
      <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#element-pattern"/>
    </xs:annotation>
    <xs:complexType>
      <xs:complexContent>
        <xs:restriction base="xs:noFixedFacet">
          <xs:sequence>
            <xs:element ref="xs:annotation" minOccurs="0"/>
          </xs:sequence>
          <xs:attribute name="value" type="xs:string" use="required"/>
         <xs:anyAttribute namespace="##other" processContents="lax"/>
        </xs:restriction>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

</xs:schema>
 07070100000465000081A400000000000000000000000168225505000001D9000000000000000000000000000000000000004A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/catalog.xml <?xml version="1.0"?>
<!DOCTYPE catalog PUBLIC "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN" "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd">
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
  <uri name="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd" uri="xmldsig-core-schema.xsd"/>
  <uri name="http://www.w3.org/TR/2002/REC-xmlenc-core-20021210/xenc-schema.xsd" uri="xenc-schema.xsd"/>
</catalog>
   07070100000466000081A400000000000000000000000168225505000018D4000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/datatypes.dtd   <!--
        DTD for XML Schemas: Part 2: Datatypes
        $Id: datatypes.dtd,v 1.23 2001/03/16 17:36:30 ht Exp $
        Note this DTD is NOT normative, or even definitive. - - the
        prose copy in the datatypes REC is the definitive version
        (which shouldn't differ from this one except for this comment
        and entity expansions, but just in case)
  -->

<!--
        This DTD cannot be used on its own, it is intended
        only for incorporation in XMLSchema.dtd, q.v.
  -->

<!-- Define all the element names, with optional prefix -->
<!ENTITY % simpleType "%p;simpleType">
<!ENTITY % restriction "%p;restriction">
<!ENTITY % list "%p;list">
<!ENTITY % union "%p;union">
<!ENTITY % maxExclusive "%p;maxExclusive">
<!ENTITY % minExclusive "%p;minExclusive">
<!ENTITY % maxInclusive "%p;maxInclusive">
<!ENTITY % minInclusive "%p;minInclusive">
<!ENTITY % totalDigits "%p;totalDigits">
<!ENTITY % fractionDigits "%p;fractionDigits">
<!ENTITY % length "%p;length">
<!ENTITY % minLength "%p;minLength">
<!ENTITY % maxLength "%p;maxLength">
<!ENTITY % enumeration "%p;enumeration">
<!ENTITY % whiteSpace "%p;whiteSpace">
<!ENTITY % pattern "%p;pattern">

<!--
        Customisation entities for the ATTLIST of each element
        type. Define one of these if your schema takes advantage
        of the anyAttribute='##other' in the schema for schemas
  -->

<!ENTITY % simpleTypeAttrs "">
<!ENTITY % restrictionAttrs "">
<!ENTITY % listAttrs "">
<!ENTITY % unionAttrs "">
<!ENTITY % maxExclusiveAttrs "">
<!ENTITY % minExclusiveAttrs "">
<!ENTITY % maxInclusiveAttrs "">
<!ENTITY % minInclusiveAttrs "">
<!ENTITY % totalDigitsAttrs "">
<!ENTITY % fractionDigitsAttrs "">
<!ENTITY % lengthAttrs "">
<!ENTITY % minLengthAttrs "">
<!ENTITY % maxLengthAttrs "">
<!ENTITY % enumerationAttrs "">
<!ENTITY % whiteSpaceAttrs "">
<!ENTITY % patternAttrs "">

<!-- Define some entities for informative use as attribute
        types -->
<!ENTITY % URIref "CDATA">
<!ENTITY % XPathExpr "CDATA">
<!ENTITY % QName "NMTOKEN">
<!ENTITY % QNames "NMTOKENS">
<!ENTITY % NCName "NMTOKEN">
<!ENTITY % nonNegativeInteger "NMTOKEN">
<!ENTITY % boolean "(true|false)">
<!ENTITY % simpleDerivationSet "CDATA">
<!--
        #all or space-separated list drawn from derivationChoice
  -->

<!--
        Note that the use of 'facet' below is less restrictive
        than is really intended:  There should in fact be no
        more than one of each of minInclusive, minExclusive,
        maxInclusive, maxExclusive, totalDigits, fractionDigits,
        length, maxLength, minLength within datatype,
        and the min- and max- variants of Inclusive and Exclusive
        are mutually exclusive. On the other hand,  pattern and
        enumeration may repeat.
  -->
<!ENTITY % minBound "(%minInclusive; | %minExclusive;)">
<!ENTITY % maxBound "(%maxInclusive; | %maxExclusive;)">
<!ENTITY % bounds "%minBound; | %maxBound;">
<!ENTITY % numeric "%totalDigits; | %fractionDigits;">
<!ENTITY % ordered "%bounds; | %numeric;">
<!ENTITY % unordered
   "%pattern; | %enumeration; | %whiteSpace; | %length; |
   %maxLength; | %minLength;">
<!ENTITY % facet "%ordered; | %unordered;">
<!ENTITY % facetAttr
        "value CDATA #REQUIRED
        id ID #IMPLIED">
<!ENTITY % fixedAttr "fixed %boolean; #IMPLIED">
<!ENTITY % facetModel "(%annotation;)?">
<!ELEMENT %simpleType;
        ((%annotation;)?, (%restriction; | %list; | %union;))>
<!ATTLIST %simpleType;
    name      %NCName; #IMPLIED
    final     %simpleDerivationSet; #IMPLIED
    id        ID       #IMPLIED
    %simpleTypeAttrs;>
<!-- name is required at top level -->
<!ELEMENT %restriction; ((%annotation;)?,
                         (%restriction1; |
                          ((%simpleType;)?,(%facet;)*)),
                         (%attrDecls;))>
<!ATTLIST %restriction;
    base      %QName;                  #IMPLIED
    id        ID       #IMPLIED
    %restrictionAttrs;>
<!--
        base and simpleType child are mutually exclusive,
        one is required.

        restriction is shared between simpleType and
        simpleContent and complexContent (in XMLSchema.xsd).
        restriction1 is for the latter cases, when this
        is restricting a complex type, as is attrDecls.
  -->
<!ELEMENT %list; ((%annotation;)?,(%simpleType;)?)>
<!ATTLIST %list;
    itemType      %QName;             #IMPLIED
    id        ID       #IMPLIED
    %listAttrs;>
<!--
        itemType and simpleType child are mutually exclusive,
        one is required
  -->
<!ELEMENT %union; ((%annotation;)?,(%simpleType;)*)>
<!ATTLIST %union;
    id            ID       #IMPLIED
    memberTypes   %QNames;            #IMPLIED
    %unionAttrs;>
<!--
        At least one item in memberTypes or one simpleType
        child is required
  -->

<!ELEMENT %maxExclusive; %facetModel;>
<!ATTLIST %maxExclusive;
        %facetAttr;
        %fixedAttr;
        %maxExclusiveAttrs;>
<!ELEMENT %minExclusive; %facetModel;>
<!ATTLIST %minExclusive;
        %facetAttr;
        %fixedAttr;
        %minExclusiveAttrs;>

<!ELEMENT %maxInclusive; %facetModel;>
<!ATTLIST %maxInclusive;
        %facetAttr;
        %fixedAttr;
        %maxInclusiveAttrs;>
<!ELEMENT %minInclusive; %facetModel;>
<!ATTLIST %minInclusive;
        %facetAttr;
        %fixedAttr;
        %minInclusiveAttrs;>

<!ELEMENT %totalDigits; %facetModel;>
<!ATTLIST %totalDigits;
        %facetAttr;
        %fixedAttr;
        %totalDigitsAttrs;>
<!ELEMENT %fractionDigits; %facetModel;>
<!ATTLIST %fractionDigits;
        %facetAttr;
        %fixedAttr;
        %fractionDigitsAttrs;>

<!ELEMENT %length; %facetModel;>
<!ATTLIST %length;
        %facetAttr;
        %fixedAttr;
        %lengthAttrs;>
<!ELEMENT %minLength; %facetModel;>
<!ATTLIST %minLength;
        %facetAttr;
        %fixedAttr;
        %minLengthAttrs;>
<!ELEMENT %maxLength; %facetModel;>
<!ATTLIST %maxLength;
        %facetAttr;
        %fixedAttr;
        %maxLengthAttrs;>

<!-- This one can be repeated -->
<!ELEMENT %enumeration; %facetModel;>
<!ATTLIST %enumeration;
        %facetAttr;
        %enumerationAttrs;>

<!ELEMENT %whiteSpace; %facetModel;>
<!ATTLIST %whiteSpace;
        %facetAttr;
        %fixedAttr;
        %whiteSpaceAttrs;>

<!-- This one can be repeated -->
<!ELEMENT %pattern; %facetModel;>
<!ATTLIST %pattern;
        %facetAttr;
        %patternAttrs;>
07070100000467000081A4000000000000000000000001682255050000324D000000000000000000000000000000000000005C00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/saml-schema-assertion-2.0.xsd   <?xml version="1.0" encoding="US-ASCII"?>
<schema
    targetNamespace="urn:oasis:names:tc:SAML:2.0:assertion"
    xmlns="http://www.w3.org/2001/XMLSchema"
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
    xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
    elementFormDefault="unqualified"
    attributeFormDefault="unqualified"
    blockDefault="substitution"
    version="2.0">
    <import namespace="http://www.w3.org/2000/09/xmldsig#"
        schemaLocation="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd"/>
    <import namespace="http://www.w3.org/2001/04/xmlenc#"
        schemaLocation="http://www.w3.org/TR/2002/REC-xmlenc-core-20021210/xenc-schema.xsd"/>
    <annotation>
        <documentation>
            Document identifier: saml-schema-assertion-2.0
            Location: http://docs.oasis-open.org/security/saml/v2.0/
            Revision history:
            V1.0 (November, 2002):
              Initial Standard Schema.
            V1.1 (September, 2003):
              Updates within the same V1.0 namespace.
            V2.0 (March, 2005):
              New assertion schema for SAML V2.0 namespace.
        </documentation>
    </annotation>
    <attributeGroup name="IDNameQualifiers">
        <attribute name="NameQualifier" type="string" use="optional"/>
        <attribute name="SPNameQualifier" type="string" use="optional"/>
    </attributeGroup>
    <element name="BaseID" type="saml:BaseIDAbstractType"/>
    <complexType name="BaseIDAbstractType" abstract="true">
        <attributeGroup ref="saml:IDNameQualifiers"/>
    </complexType>
    <element name="NameID" type="saml:NameIDType"/>
    <complexType name="NameIDType">
        <simpleContent>
            <extension base="string">
                <attributeGroup ref="saml:IDNameQualifiers"/>
                <attribute name="Format" type="anyURI" use="optional"/>
                <attribute name="SPProvidedID" type="string" use="optional"/>
            </extension>
        </simpleContent>
    </complexType>
    <complexType name="EncryptedElementType">
        <sequence>
            <element ref="xenc:EncryptedData"/>
            <element ref="xenc:EncryptedKey" minOccurs="0" maxOccurs="unbounded"/>
        </sequence>
    </complexType>
    <element name="EncryptedID" type="saml:EncryptedElementType"/>
    <element name="Issuer" type="saml:NameIDType"/>
    <element name="AssertionIDRef" type="NCName"/>
    <element name="AssertionURIRef" type="anyURI"/>
    <element name="Assertion" type="saml:AssertionType"/>
    <complexType name="AssertionType">
        <sequence>
            <element ref="saml:Issuer"/>
            <element ref="ds:Signature" minOccurs="0"/>
            <element ref="saml:Subject" minOccurs="0"/>
            <element ref="saml:Conditions" minOccurs="0"/>
            <element ref="saml:Advice" minOccurs="0"/>
            <choice minOccurs="0" maxOccurs="unbounded">
                <element ref="saml:Statement"/>
                <element ref="saml:AuthnStatement"/>
                <element ref="saml:AuthzDecisionStatement"/>
                <element ref="saml:AttributeStatement"/>
            </choice>
        </sequence>
        <attribute name="Version" type="string" use="required"/>
        <attribute name="ID" type="ID" use="required"/>
        <attribute name="IssueInstant" type="dateTime" use="required"/>
    </complexType>
    <element name="Subject" type="saml:SubjectType"/>
    <complexType name="SubjectType">
        <choice>
            <sequence>
                <choice>
                    <element ref="saml:BaseID"/>
                    <element ref="saml:NameID"/>
                    <element ref="saml:EncryptedID"/>
                </choice>
                <element ref="saml:SubjectConfirmation" minOccurs="0" maxOccurs="unbounded"/>
            </sequence>
            <element ref="saml:SubjectConfirmation" maxOccurs="unbounded"/>
        </choice>
    </complexType>
    <element name="SubjectConfirmation" type="saml:SubjectConfirmationType"/>
    <complexType name="SubjectConfirmationType">
        <sequence>
            <choice minOccurs="0">
                <element ref="saml:BaseID"/>
                <element ref="saml:NameID"/>
                <element ref="saml:EncryptedID"/>
            </choice>
            <element ref="saml:SubjectConfirmationData" minOccurs="0"/>
        </sequence>
        <attribute name="Method" type="anyURI" use="required"/>
    </complexType>
    <element name="SubjectConfirmationData" type="saml:SubjectConfirmationDataType"/>
    <complexType name="SubjectConfirmationDataType" mixed="true">
        <complexContent>
            <restriction base="anyType">
                <sequence>
                    <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
                </sequence>
                <attribute name="NotBefore" type="dateTime" use="optional"/>
                <attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
                <attribute name="Recipient" type="anyURI" use="optional"/>
                <attribute name="InResponseTo" type="NCName" use="optional"/>
                <attribute name="Address" type="string" use="optional"/>
                <anyAttribute namespace="##other" processContents="lax"/>
            </restriction>
        </complexContent>
    </complexType>
    <complexType name="KeyInfoConfirmationDataType" mixed="false">
        <complexContent>
            <restriction base="saml:SubjectConfirmationDataType">
                <sequence>
                    <element ref="ds:KeyInfo" maxOccurs="unbounded"/>
                </sequence>
            </restriction>
        </complexContent>
    </complexType>
    <element name="Conditions" type="saml:ConditionsType"/>
    <complexType name="ConditionsType">
        <choice minOccurs="0" maxOccurs="unbounded">
            <element ref="saml:Condition"/>
            <element ref="saml:AudienceRestriction"/>
            <element ref="saml:OneTimeUse"/>
            <element ref="saml:ProxyRestriction"/>
        </choice>
        <attribute name="NotBefore" type="dateTime" use="optional"/>
        <attribute name="NotOnOrAfter" type="dateTime" use="optional"/>
    </complexType>
    <element name="Condition" type="saml:ConditionAbstractType"/>
    <complexType name="ConditionAbstractType" abstract="true"/>
    <element name="AudienceRestriction" type="saml:AudienceRestrictionType"/>
    <complexType name="AudienceRestrictionType">
        <complexContent>
            <extension base="saml:ConditionAbstractType">
                <sequence>
                    <element ref="saml:Audience" maxOccurs="unbounded"/>
                </sequence>
            </extension>
        </complexContent>
    </complexType>
    <element name="Audience" type="anyURI"/>
    <element name="OneTimeUse" type="saml:OneTimeUseType" />
    <complexType name="OneTimeUseType">
        <complexContent>
            <extension base="saml:ConditionAbstractType"/>
        </complexContent>
    </complexType>
    <element name="ProxyRestriction" type="saml:ProxyRestrictionType"/>
    <complexType name="ProxyRestrictionType">
    <complexContent>
        <extension base="saml:ConditionAbstractType">
            <sequence>
                <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>
            </sequence>
            <attribute name="Count" type="nonNegativeInteger" use="optional"/>
        </extension>
	</complexContent>
    </complexType>
    <element name="Advice" type="saml:AdviceType"/>
    <complexType name="AdviceType">
        <choice minOccurs="0" maxOccurs="unbounded">
            <element ref="saml:AssertionIDRef"/>
            <element ref="saml:AssertionURIRef"/>
            <element ref="saml:Assertion"/>
            <element ref="saml:EncryptedAssertion"/>
            <any namespace="##other" processContents="lax"/>
        </choice>
    </complexType>
    <element name="EncryptedAssertion" type="saml:EncryptedElementType"/>
    <element name="Statement" type="saml:StatementAbstractType"/>
    <complexType name="StatementAbstractType" abstract="true"/>
    <element name="AuthnStatement" type="saml:AuthnStatementType"/>
    <complexType name="AuthnStatementType">
        <complexContent>
            <extension base="saml:StatementAbstractType">
                <sequence>
                    <element ref="saml:SubjectLocality" minOccurs="0"/>
                    <element ref="saml:AuthnContext"/>
                </sequence>
                <attribute name="AuthnInstant" type="dateTime" use="required"/>
                <attribute name="SessionIndex" type="string" use="optional"/>
                <attribute name="SessionNotOnOrAfter" type="dateTime" use="optional"/>
            </extension>
        </complexContent>
    </complexType>
    <element name="SubjectLocality" type="saml:SubjectLocalityType"/>
    <complexType name="SubjectLocalityType">
        <attribute name="Address" type="string" use="optional"/>
        <attribute name="DNSName" type="string" use="optional"/>
    </complexType>
    <element name="AuthnContext" type="saml:AuthnContextType"/>
    <complexType name="AuthnContextType">
        <sequence>
            <choice>
                <sequence>
                    <element ref="saml:AuthnContextClassRef"/>
                    <choice minOccurs="0">
                        <element ref="saml:AuthnContextDecl"/>
                        <element ref="saml:AuthnContextDeclRef"/>
                    </choice>
                </sequence>
                <choice>
                    <element ref="saml:AuthnContextDecl"/>
                    <element ref="saml:AuthnContextDeclRef"/>
                </choice>
            </choice>
            <element ref="saml:AuthenticatingAuthority" minOccurs="0" maxOccurs="unbounded"/>
        </sequence>
    </complexType>
    <element name="AuthnContextClassRef" type="anyURI"/>
    <element name="AuthnContextDeclRef" type="anyURI"/>
    <element name="AuthnContextDecl" type="anyType"/>
    <element name="AuthenticatingAuthority" type="anyURI"/>
    <element name="AuthzDecisionStatement" type="saml:AuthzDecisionStatementType"/>
    <complexType name="AuthzDecisionStatementType">
        <complexContent>
            <extension base="saml:StatementAbstractType">
                <sequence>
                    <element ref="saml:Action" maxOccurs="unbounded"/>
                    <element ref="saml:Evidence" minOccurs="0"/>
                </sequence>
                <attribute name="Resource" type="anyURI" use="required"/>
                <attribute name="Decision" type="saml:DecisionType" use="required"/>
            </extension>
        </complexContent>
    </complexType>
    <simpleType name="DecisionType">
        <restriction base="string">
            <enumeration value="Permit"/>
            <enumeration value="Deny"/>
            <enumeration value="Indeterminate"/>
        </restriction>
    </simpleType>
    <element name="Action" type="saml:ActionType"/>
    <complexType name="ActionType">
        <simpleContent>
            <extension base="string">
                <attribute name="Namespace" type="anyURI" use="required"/>
            </extension>
        </simpleContent>
    </complexType>
    <element name="Evidence" type="saml:EvidenceType"/>
    <complexType name="EvidenceType">
        <choice maxOccurs="unbounded">
            <element ref="saml:AssertionIDRef"/>
            <element ref="saml:AssertionURIRef"/>
            <element ref="saml:Assertion"/>
            <element ref="saml:EncryptedAssertion"/>
        </choice>
    </complexType>
    <element name="AttributeStatement" type="saml:AttributeStatementType"/>
    <complexType name="AttributeStatementType">
        <complexContent>
            <extension base="saml:StatementAbstractType">
                <choice maxOccurs="unbounded">
                    <element ref="saml:Attribute"/>
                    <element ref="saml:EncryptedAttribute"/>
                </choice>
            </extension>
        </complexContent>
    </complexType>
    <element name="Attribute" type="saml:AttributeType"/>
    <complexType name="AttributeType">
        <sequence>
            <element ref="saml:AttributeValue" minOccurs="0" maxOccurs="unbounded"/>
        </sequence>
        <attribute name="Name" type="string" use="required"/>
        <attribute name="NameFormat" type="anyURI" use="optional"/>
        <attribute name="FriendlyName" type="string" use="optional"/>
        <anyAttribute namespace="##other" processContents="lax"/>
    </complexType>
    <element name="AttributeValue" type="anyType" nillable="true"/>
    <element name="EncryptedAttribute" type="saml:EncryptedElementType"/>
</schema>
   07070100000468000081A4000000000000000000000001682255050000141A000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/xenc-schema.xsd <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "XMLSchema.dtd" [
   <!ATTLIST schema
     xmlns:xenc CDATA #FIXED 'http://www.w3.org/2001/04/xmlenc#'
     xmlns:ds CDATA #FIXED 'http://www.w3.org/2000/09/xmldsig#'>
   <!ENTITY xenc 'http://www.w3.org/2001/04/xmlenc#'>
   <!ENTITY % p ''>
   <!ENTITY % s ''>
  ]>
<schema xmlns="http://www.w3.org/2001/XMLSchema" version="1.0" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" targetNamespace="http://www.w3.org/2001/04/xmlenc#" elementFormDefault="qualified">

  <import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd"/>

  <complexType name="EncryptedType" abstract="true">
    <sequence>
      <element name="EncryptionMethod" type="xenc:EncryptionMethodType" minOccurs="0"/>
      <element ref="ds:KeyInfo" minOccurs="0"/>
      <element ref="xenc:CipherData"/>
      <element ref="xenc:EncryptionProperties" minOccurs="0"/>
    </sequence>
    <attribute name="Id" type="ID" use="optional"/>
    <attribute name="Type" type="anyURI" use="optional"/>
    <attribute name="MimeType" type="string" use="optional"/>
    <attribute name="Encoding" type="anyURI" use="optional"/>
  </complexType>

  <complexType name="EncryptionMethodType" mixed="true">
    <sequence>
      <element name="KeySize" minOccurs="0" type="xenc:KeySizeType"/>
      <element name="OAEPparams" minOccurs="0" type="base64Binary"/>
      <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
    </sequence>
    <attribute name="Algorithm" type="anyURI" use="required"/>
  </complexType>

    <simpleType name="KeySizeType">
      <restriction base="integer"/>
    </simpleType>

  <element name="CipherData" type="xenc:CipherDataType"/>
  <complexType name="CipherDataType">
     <choice>
       <element name="CipherValue" type="base64Binary"/>
       <element ref="xenc:CipherReference"/>
     </choice>
    </complexType>

   <element name="CipherReference" type="xenc:CipherReferenceType"/>
   <complexType name="CipherReferenceType">
       <choice>
         <element name="Transforms" type="xenc:TransformsType" minOccurs="0"/>
       </choice>
       <attribute name="URI" type="anyURI" use="required"/>
   </complexType>

     <complexType name="TransformsType">
       <sequence>
         <element ref="ds:Transform" maxOccurs="unbounded"/>
       </sequence>
     </complexType>


  <element name="EncryptedData" type="xenc:EncryptedDataType"/>
  <complexType name="EncryptedDataType">
    <complexContent>
      <extension base="xenc:EncryptedType">
       </extension>
    </complexContent>
  </complexType>

  <!-- Children of ds:KeyInfo -->

  <element name="EncryptedKey" type="xenc:EncryptedKeyType"/>
  <complexType name="EncryptedKeyType">
    <complexContent>
      <extension base="xenc:EncryptedType">
        <sequence>
          <element ref="xenc:ReferenceList" minOccurs="0"/>
          <element name="CarriedKeyName" type="string" minOccurs="0"/>
        </sequence>
        <attribute name="Recipient" type="string" use="optional"/>
      </extension>
    </complexContent>
  </complexType>

    <element name="AgreementMethod" type="xenc:AgreementMethodType"/>
    <complexType name="AgreementMethodType" mixed="true">
      <sequence>
        <element name="KA-Nonce" minOccurs="0" type="base64Binary"/>
        <!-- <element ref="ds:DigestMethod" minOccurs="0"/> -->
        <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
        <element name="OriginatorKeyInfo" minOccurs="0" type="ds:KeyInfoType"/>
        <element name="RecipientKeyInfo" minOccurs="0" type="ds:KeyInfoType"/>
      </sequence>
      <attribute name="Algorithm" type="anyURI" use="required"/>
    </complexType>

  <!-- End Children of ds:KeyInfo -->

  <element name="ReferenceList">
    <complexType>
      <choice minOccurs="1" maxOccurs="unbounded">
        <element name="DataReference" type="xenc:ReferenceType"/>
        <element name="KeyReference" type="xenc:ReferenceType"/>
      </choice>
    </complexType>
  </element>

  <complexType name="ReferenceType">
    <sequence>
      <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
    </sequence>
    <attribute name="URI" type="anyURI" use="required"/>
  </complexType>


  <element name="EncryptionProperties" type="xenc:EncryptionPropertiesType"/>
  <complexType name="EncryptionPropertiesType">
    <sequence>
      <element ref="xenc:EncryptionProperty" maxOccurs="unbounded"/>
    </sequence>
    <attribute name="Id" type="ID" use="optional"/>
  </complexType>

    <element name="EncryptionProperty" type="xenc:EncryptionPropertyType"/>
    <complexType name="EncryptionPropertyType" mixed="true">
      <choice maxOccurs="unbounded">
        <any namespace="##other" processContents="lax"/>
      </choice>
      <attribute name="Target" type="anyURI" use="optional"/>
      <attribute name="Id" type="ID" use="optional"/>
      <anyAttribute namespace="http://www.w3.org/XML/1998/namespace"/>
    </complexType>

</schema>
  07070100000469000081A40000000000000000000000016822550500002240000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/xml.xsd <?xml version='1.0'?>
<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns   ="http://www.w3.org/1999/xhtml"
  xml:lang="en">

 <xs:annotation>
  <xs:documentation>
   <div>
    <h1>About the XML namespace</h1>

    <div class="bodytext">
     <p>
      This schema document describes the XML namespace, in a form
      suitable for import by other schema documents.
     </p>
     <p>
      See <a href="http://www.w3.org/XML/1998/namespace.html">
      http://www.w3.org/XML/1998/namespace.html</a> and
      <a href="http://www.w3.org/TR/REC-xml">
      http://www.w3.org/TR/REC-xml</a> for information
      about this namespace.
     </p>
     <p>
      Note that local names in this namespace are intended to be
      defined only by the World Wide Web Consortium or its subgroups.
      The names currently defined in this namespace are listed below.
      They should not be used with conflicting semantics by any Working
      Group, specification, or document instance.
     </p>
     <p>
      See further below in this document for more information about <a
      href="#usage">how to refer to this schema document from your own
      XSD schema documents</a> and about <a href="#nsversioning">the
      namespace-versioning policy governing this schema document</a>.
     </p>
    </div>
   </div>
  </xs:documentation>
 </xs:annotation>

 <xs:attribute name="lang">
  <xs:annotation>
   <xs:documentation>
    <div>

      <h3>lang (as an attribute name)</h3>
      <p>
       denotes an attribute whose value
       is a language code for the natural language of the content of
       any element; its value is inherited.  This name is reserved
       by virtue of its definition in the XML specification.</p>

    </div>
    <div>
     <h4>Notes</h4>
     <p>
      Attempting to install the relevant ISO 2- and 3-letter
      codes as the enumerated possible values is probably never
      going to be a realistic possibility.
     </p>
     <p>
      See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
       http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
      and the IANA language subtag registry at
      <a href="http://www.iana.org/assignments/language-subtag-registry">
       http://www.iana.org/assignments/language-subtag-registry</a>
      for further information.
     </p>
     <p>
      The union allows for the 'un-declaration' of xml:lang with
      the empty string.
     </p>
    </div>
   </xs:documentation>
  </xs:annotation>
  <xs:simpleType>
   <xs:union memberTypes="xs:language">
    <xs:simpleType>
     <xs:restriction base="xs:string">
      <xs:enumeration value=""/>
     </xs:restriction>
    </xs:simpleType>
   </xs:union>
  </xs:simpleType>
 </xs:attribute>

 <xs:attribute name="space">
  <xs:annotation>
   <xs:documentation>
    <div>

      <h3>space (as an attribute name)</h3>
      <p>
       denotes an attribute whose
       value is a keyword indicating what whitespace processing
       discipline is intended for the content of the element; its
       value is inherited.  This name is reserved by virtue of its
       definition in the XML specification.</p>

    </div>
   </xs:documentation>
  </xs:annotation>
  <xs:simpleType>
   <xs:restriction base="xs:NCName">
    <xs:enumeration value="default"/>
    <xs:enumeration value="preserve"/>
   </xs:restriction>
  </xs:simpleType>
 </xs:attribute>

 <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
   <xs:documentation>
    <div>

      <h3>base (as an attribute name)</h3>
      <p>
       denotes an attribute whose value
       provides a URI to be used as the base for interpreting any
       relative URIs in the scope of the element on which it
       appears; its value is inherited.  This name is reserved
       by virtue of its definition in the XML Base specification.</p>

     <p>
      See <a
      href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
      for information about this attribute.
     </p>
    </div>
   </xs:documentation>
  </xs:annotation>
 </xs:attribute>

 <xs:attribute name="id" type="xs:ID">
  <xs:annotation>
   <xs:documentation>
    <div>

      <h3>id (as an attribute name)</h3>
      <p>
       denotes an attribute whose value
       should be interpreted as if declared to be of type ID.
       This name is reserved by virtue of its definition in the
       xml:id specification.</p>

     <p>
      See <a
      href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
      for information about this attribute.
     </p>
    </div>
   </xs:documentation>
  </xs:annotation>
 </xs:attribute>

 <xs:attributeGroup name="specialAttrs">
  <xs:attribute ref="xml:base"/>
  <xs:attribute ref="xml:lang"/>
  <xs:attribute ref="xml:space"/>
  <xs:attribute ref="xml:id"/>
 </xs:attributeGroup>

 <xs:annotation>
  <xs:documentation>
   <div>

    <h3>Father (in any context at all)</h3>

    <div class="bodytext">
     <p>
      denotes Jon Bosak, the chair of
      the original XML Working Group.  This name is reserved by
      the following decision of the W3C XML Plenary and
      XML Coordination groups:
     </p>
     <blockquote>
       <p>
	In appreciation for his vision, leadership and
	dedication the W3C XML Plenary on this 10th day of
	February, 2000, reserves for Jon Bosak in perpetuity
	the XML name "xml:Father".
       </p>
     </blockquote>
    </div>
   </div>
  </xs:documentation>
 </xs:annotation>

 <xs:annotation>
  <xs:documentation>
   <div xml:id="usage" id="usage">
    <h2><a name="usage">About this schema document</a></h2>

    <div class="bodytext">
     <p>
      This schema defines attributes and an attribute group suitable
      for use by schemas wishing to allow <code>xml:base</code>,
      <code>xml:lang</code>, <code>xml:space</code> or
      <code>xml:id</code> attributes on elements they define.
     </p>
     <p>
      To enable this, such a schema must import this schema for
      the XML namespace, e.g. as follows:
     </p>
     <pre>
          &lt;schema . . .>
           . . .
           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
     </pre>
     <p>
      or
     </p>
     <pre>
           &lt;import namespace="http://www.w3.org/XML/1998/namespace"
                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
     </pre>
     <p>
      Subsequently, qualified reference to any of the attributes or the
      group defined below will have the desired effect, e.g.
     </p>
     <pre>
          &lt;type . . .>
           . . .
           &lt;attributeGroup ref="xml:specialAttrs"/>
     </pre>
     <p>
      will define a type which will schema-validate an instance element
      with any of those attributes.
     </p>
    </div>
   </div>
  </xs:documentation>
 </xs:annotation>

 <xs:annotation>
  <xs:documentation>
   <div id="nsversioning" xml:id="nsversioning">
    <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
    <div class="bodytext">
     <p>
      In keeping with the XML Schema WG's standard versioning
      policy, this schema document will persist at
      <a href="http://www.w3.org/2009/01/xml.xsd">
       http://www.w3.org/2009/01/xml.xsd</a>.
     </p>
     <p>
      At the date of issue it can also be found at
      <a href="http://www.w3.org/2001/xml.xsd">
       http://www.w3.org/2001/xml.xsd</a>.
     </p>
     <p>
      The schema document at that URI may however change in the future,
      in order to remain compatible with the latest version of XML
      Schema itself, or with the XML namespace itself.  In other words,
      if the XML Schema or XML namespaces change, the version of this
      document at <a href="http://www.w3.org/2001/xml.xsd">
       http://www.w3.org/2001/xml.xsd
      </a>
      will change accordingly; the version at
      <a href="http://www.w3.org/2009/01/xml.xsd">
       http://www.w3.org/2009/01/xml.xsd
      </a>
      will not change.
     </p>
     <p>
      Previous dated (and unchanging) versions of this schema
      document are at:
     </p>
     <ul>
      <li><a href="http://www.w3.org/2009/01/xml.xsd">
	http://www.w3.org/2009/01/xml.xsd</a></li>
      <li><a href="http://www.w3.org/2007/08/xml.xsd">
	http://www.w3.org/2007/08/xml.xsd</a></li>
      <li><a href="http://www.w3.org/2004/10/xml.xsd">
	http://www.w3.org/2004/10/xml.xsd</a></li>
      <li><a href="http://www.w3.org/2001/03/xml.xsd">
	http://www.w3.org/2001/03/xml.xsd</a></li>
     </ul>
    </div>
   </div>
  </xs:documentation>
 </xs:annotation>

</xs:schema>

0707010000046A000081A400000000000000000000000168225505000027A0000000000000000000000000000000000000005600000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/schemas/xmldsig-core-schema.xsd <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "XMLSchema.dtd" [
   <!ATTLIST schema
     xmlns:ds CDATA #FIXED "http://www.w3.org/2000/09/xmldsig#">
   <!ENTITY dsig 'http://www.w3.org/2000/09/xmldsig#'>
   <!ENTITY % p ''>
   <!ENTITY % s ''>
  ]>
<!-- Schema for XML Signatures
    http://www.w3.org/2000/09/xmldsig#
    $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $

    Copyright 2001 The Internet Society and W3C (Massachusetts Institute
    of Technology, Institut National de Recherche en Informatique et en
    Automatique, Keio University). All Rights Reserved.
    http://www.w3.org/Consortium/Legal/

    This document is governed by the W3C Software License [1] as described
    in the FAQ [2].

    [1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
    [2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" targetNamespace="http://www.w3.org/2000/09/xmldsig#" version="0.1" elementFormDefault="qualified">

<!-- Basic Types Defined for Signatures -->

<simpleType name="CryptoBinary">
  <restriction base="base64Binary">
  </restriction>
</simpleType>

<!-- Start Signature -->

<element name="Signature" type="ds:SignatureType"/>
<complexType name="SignatureType">
  <sequence>
    <element ref="ds:SignedInfo"/>
    <element ref="ds:SignatureValue"/>
    <element ref="ds:KeyInfo" minOccurs="0"/>
    <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
  </sequence>
  <attribute name="Id" type="ID" use="optional"/>
</complexType>

  <element name="SignatureValue" type="ds:SignatureValueType"/>
  <complexType name="SignatureValueType">
    <simpleContent>
      <extension base="base64Binary">
        <attribute name="Id" type="ID" use="optional"/>
      </extension>
    </simpleContent>
  </complexType>

<!-- Start SignedInfo -->

<element name="SignedInfo" type="ds:SignedInfoType"/>
<complexType name="SignedInfoType">
  <sequence>
    <element ref="ds:CanonicalizationMethod"/>
    <element ref="ds:SignatureMethod"/>
    <element ref="ds:Reference" maxOccurs="unbounded"/>
  </sequence>
  <attribute name="Id" type="ID" use="optional"/>
</complexType>

  <element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
  <complexType name="CanonicalizationMethodType" mixed="true">
    <sequence>
      <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
      <!-- (0,unbounded) elements from (1,1) namespace -->
    </sequence>
    <attribute name="Algorithm" type="anyURI" use="required"/>
  </complexType>

  <element name="SignatureMethod" type="ds:SignatureMethodType"/>
  <complexType name="SignatureMethodType" mixed="true">
    <sequence>
      <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
      <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
      <!-- (0,unbounded) elements from (1,1) external namespace -->
    </sequence>
    <attribute name="Algorithm" type="anyURI" use="required"/>
  </complexType>

<!-- Start Reference -->

<element name="Reference" type="ds:ReferenceType"/>
<complexType name="ReferenceType">
  <sequence>
    <element ref="ds:Transforms" minOccurs="0"/>
    <element ref="ds:DigestMethod"/>
    <element ref="ds:DigestValue"/>
  </sequence>
  <attribute name="Id" type="ID" use="optional"/>
  <attribute name="URI" type="anyURI" use="optional"/>
  <attribute name="Type" type="anyURI" use="optional"/>
</complexType>

  <element name="Transforms" type="ds:TransformsType"/>
  <complexType name="TransformsType">
    <sequence>
      <element ref="ds:Transform" maxOccurs="unbounded"/>
    </sequence>
  </complexType>

  <element name="Transform" type="ds:TransformType"/>
  <complexType name="TransformType" mixed="true">
    <choice minOccurs="0" maxOccurs="unbounded">
      <any namespace="##other" processContents="lax"/>
      <!-- (1,1) elements from (0,unbounded) namespaces -->
      <element name="XPath" type="string"/>
    </choice>
    <attribute name="Algorithm" type="anyURI" use="required"/>
  </complexType>

<!-- End Reference -->

<element name="DigestMethod" type="ds:DigestMethodType"/>
<complexType name="DigestMethodType" mixed="true">
  <sequence>
    <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
  </sequence>
  <attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>

<element name="DigestValue" type="ds:DigestValueType"/>
<simpleType name="DigestValueType">
  <restriction base="base64Binary"/>
</simpleType>

<!-- End SignedInfo -->

<!-- Start KeyInfo -->

<element name="KeyInfo" type="ds:KeyInfoType"/>
<complexType name="KeyInfoType" mixed="true">
  <choice maxOccurs="unbounded">
    <element ref="ds:KeyName"/>
    <element ref="ds:KeyValue"/>
    <element ref="ds:RetrievalMethod"/>
    <element ref="ds:X509Data"/>
    <element ref="ds:PGPData"/>
    <element ref="ds:SPKIData"/>
    <element ref="ds:MgmtData"/>
    <any processContents="lax" namespace="##other"/>
    <!-- (1,1) elements from (0,unbounded) namespaces -->
  </choice>
  <attribute name="Id" type="ID" use="optional"/>
</complexType>

  <element name="KeyName" type="string"/>
  <element name="MgmtData" type="string"/>

  <element name="KeyValue" type="ds:KeyValueType"/>
  <complexType name="KeyValueType" mixed="true">
   <choice>
     <element ref="ds:DSAKeyValue"/>
     <element ref="ds:RSAKeyValue"/>
     <any namespace="##other" processContents="lax"/>
   </choice>
  </complexType>

  <element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
  <complexType name="RetrievalMethodType">
    <sequence>
      <element ref="ds:Transforms" minOccurs="0"/>
    </sequence>
    <attribute name="URI" type="anyURI"/>
    <attribute name="Type" type="anyURI" use="optional"/>
  </complexType>

<!-- Start X509Data -->

<element name="X509Data" type="ds:X509DataType"/>
<complexType name="X509DataType">
  <sequence maxOccurs="unbounded">
    <choice>
      <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
      <element name="X509SKI" type="base64Binary"/>
      <element name="X509SubjectName" type="string"/>
      <element name="X509Certificate" type="base64Binary"/>
      <element name="X509CRL" type="base64Binary"/>
      <any namespace="##other" processContents="lax"/>
    </choice>
  </sequence>
</complexType>

<complexType name="X509IssuerSerialType">
  <sequence>
    <element name="X509IssuerName" type="string"/>
    <element name="X509SerialNumber" type="integer"/>
  </sequence>
</complexType>

<!-- End X509Data -->

<!-- Begin PGPData -->

<element name="PGPData" type="ds:PGPDataType"/>
<complexType name="PGPDataType">
  <choice>
    <sequence>
      <element name="PGPKeyID" type="base64Binary"/>
      <element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
      <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
    </sequence>
    <sequence>
      <element name="PGPKeyPacket" type="base64Binary"/>
      <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
    </sequence>
  </choice>
</complexType>

<!-- End PGPData -->

<!-- Begin SPKIData -->

<element name="SPKIData" type="ds:SPKIDataType"/>
<complexType name="SPKIDataType">
  <sequence maxOccurs="unbounded">
    <element name="SPKISexp" type="base64Binary"/>
    <any namespace="##other" processContents="lax" minOccurs="0"/>
  </sequence>
</complexType>

<!-- End SPKIData -->

<!-- End KeyInfo -->

<!-- Start Object (Manifest, SignatureProperty) -->

<element name="Object" type="ds:ObjectType"/>
<complexType name="ObjectType" mixed="true">
  <sequence minOccurs="0" maxOccurs="unbounded">
    <any namespace="##any" processContents="lax"/>
  </sequence>
  <attribute name="Id" type="ID" use="optional"/>
  <attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
  <attribute name="Encoding" type="anyURI" use="optional"/>
</complexType>

<element name="Manifest" type="ds:ManifestType"/>
<complexType name="ManifestType">
  <sequence>
    <element ref="ds:Reference" maxOccurs="unbounded"/>
  </sequence>
  <attribute name="Id" type="ID" use="optional"/>
</complexType>

<element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
<complexType name="SignaturePropertiesType">
  <sequence>
    <element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
  </sequence>
  <attribute name="Id" type="ID" use="optional"/>
</complexType>

   <element name="SignatureProperty" type="ds:SignaturePropertyType"/>
   <complexType name="SignaturePropertyType" mixed="true">
     <choice maxOccurs="unbounded">
       <any namespace="##other" processContents="lax"/>
       <!-- (1,1) elements from (1,unbounded) namespaces -->
     </choice>
     <attribute name="Target" type="anyURI" use="required"/>
     <attribute name="Id" type="ID" use="optional"/>
   </complexType>

<!-- End Object (Manifest, SignatureProperty) -->

<!-- Start Algorithm Parameters -->

<simpleType name="HMACOutputLengthType">
  <restriction base="integer"/>
</simpleType>

<!-- Start KeyValue Element-types -->

<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
<complexType name="DSAKeyValueType">
  <sequence>
    <sequence minOccurs="0">
      <element name="P" type="ds:CryptoBinary"/>
      <element name="Q" type="ds:CryptoBinary"/>
    </sequence>
    <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
    <element name="Y" type="ds:CryptoBinary"/>
    <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
    <sequence minOccurs="0">
      <element name="Seed" type="ds:CryptoBinary"/>
      <element name="PgenCounter" type="ds:CryptoBinary"/>
    </sequence>
  </sequence>
</complexType>

<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
<complexType name="RSAKeyValueType">
  <sequence>
    <element name="Modulus" type="ds:CryptoBinary"/>
    <element name="Exponent" type="ds:CryptoBinary"/>
  </sequence>
</complexType>

<!-- End KeyValue Element-types -->

<!-- End Signature -->

</schema>
0707010000046B000081A4000000000000000000000001682255050000731B000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/service.c   /*********************************************************
 * Copyright (C) 2011-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file service.c --
 *
 *    Creates and sets up ServiceConnection
 */

#include "VGAuthLog.h"
#include "serviceInt.h"
#include "VGAuthProto.h"
#include "VGAuthUtil.h"
#ifdef _WIN32
#include "winUtil.h"
#include <glib.h>
#endif

static ServiceStartListeningForIOFunc startListeningIOFunc = NULL;
static ServiceStopListeningForIOFunc stopListeningIOFunc = NULL;

static GHashTable *listenConnectionMap = NULL;

static gboolean ServiceInitListenConnectionMap();
static ServiceConnection *ServiceLookupListenConnection(const char *userName);
static void ServiceMapListenConnection(const char *userName,
                                       ServiceConnection *userConn);

// Logs information about when connections are reaped.
#define LISTENCONN_TABLE_DEBUG 0
/*
 * Set to turn off listen connecton re-use.  Helps find issues with
 * user deletion.
 */
#define LISTENCONN_NO_REUSE 0

/*
 * Throw out idle listen connections after 30 minutes.
 */

#define  LISTENCONN_EXPIRE_TIME_IN_SECONDS_DEFAULT (30 * 60)

static int listenConnExpireTime = LISTENCONN_EXPIRE_TIME_IN_SECONDS_DEFAULT;
static int reapCheckTime;

PrefHandle gPrefs = NULL;
static gboolean reapTimerRunning = FALSE;

gboolean gVerboseLogging = FALSE;

/*
 * The directory where the service binary resides.
 */
char *gInstallDir;

/*
 * The data connection map that keeps tracks of the number of connection for
 * each user. PairType = <gchar *, int *>
 */
static GHashTable *dataConnectionMap = NULL;
static int dataConnectionMaxPerUser =
   VGAUTH_PREF_DEFAULT_MAX_DATA_CONNECTIONS_PER_USER;

static gboolean ServiceInitDataConnectionMap();
static void ServiceDataConnectionIncrement(const char *user);
static void ServiceDataConnectionDecrement(const char *user);
gboolean ServiceDataConnectionCheckLimit(const char *user);
gboolean ServiceIsSuperUser(const char *user);


/*
 ******************************************************************************
 * ServiceConnectionGetNextConnectionId --                               */ /**
 *
 * Get an unqiue connection Id
 *
 * @return a connection Id
 *
 ******************************************************************************
 */

static int
ServiceConnectionGetNextConnectionId(void)
{
   static int TheNextId = 0;
   return TheNextId++;
}


/*
 ******************************************************************************
 * ServiceRegisterIOFunctions --                                         */ /**
 *
 * Sets up IO function hooks, for when the service library needs to call
 * out to IO control.
 *
 * @param[in]   startFunc       The function called when we want to start
 *                              listening for IO on a connection.
 * @param[in]   stopFunc        The function called when we no longer
 *                              care about IO on a connection.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceRegisterIOFunctions(ServiceStartListeningForIOFunc startFunc,
                           ServiceStopListeningForIOFunc stopFunc)
{
   startListeningIOFunc = startFunc;
   stopListeningIOFunc = stopFunc;

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * ServiceCreatePublicConnection --                                     */ /**
 *
 * Creates the connection that listens on the public pipe.
 *
 * @param[out]  returnConn        The new connection.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceCreatePublicConnection(ServiceConnection **returnConn) // OUT
{
   ServiceConnection *newConn;
   VGAuthError err;

   if(!ServiceInitListenConnectionMap()) {
      VGAUTH_LOG_WARNING("%s", "Failed to initialize the listen connection map");
      return VGAUTH_E_FAIL;
   }

   if(!ServiceInitDataConnectionMap()) {
      VGAUTH_LOG_WARNING("%s", "Failed to initialize the data connection map");
      return VGAUTH_E_FAIL;
   }

   newConn = g_malloc0(sizeof(ServiceConnection));

#ifdef _WIN32
   newConn->hComm = INVALID_HANDLE_VALUE;
#endif

   newConn->connId = ServiceConnectionGetNextConnectionId();

   newConn->pipeName = g_strdup(SERVICE_PUBLIC_PIPE_NAME);
   newConn->userName = g_strdup(SUPERUSER_NAME);

   err = ServiceNetworkListen(newConn, FALSE);

   if (VGAUTH_E_OK != err) {
      Warning("%s: failed to setup public listen channel\n", __FUNCTION__);
      ServiceConnectionShutdown(newConn);
      return err;
   }

   newConn->isPublic = TRUE;
   *returnConn = newConn;

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * ServiceEncodeUserName --                                              */ /**
 *
 * Esacape the backslash domain\username separator on Windows
 *
 * @param[in]   userName       The original user name
 *
 * @return   the result ascii string of the encoded user name
 *
 ******************************************************************************
 */

gchar *
ServiceEncodeUserName(const char *userName)
{
#ifdef _WIN32
   char *user = NULL;
   char *domain = NULL;
   char *result = NULL;
   BOOL ok;

   ok = WinUtil_ParseUserName(userName, '\\', &user, &domain);
   ASSERT(ok);

   if (domain) {
      result = g_strdup_printf("%s+%s", domain, user);
   } else {
      result = g_strdup(userName);
   }

   g_free(user);
   g_free(domain);

   return result;
#else
   return g_strdup(userName);
#endif
}


/*
 ******************************************************************************
 * ServiceDecodeUserName --                                              */ /**
 *
 * Restore an already escaped name to the domain\user format on Windows
 *
 * @param[in]   userName       The encoded user name
 *
 * @return   the result ascii string of the original user name
 *
 ******************************************************************************
 */

gchar *
ServiceDecodeUserName(const char *userName)
{
#ifdef _WIN32
   char *user = NULL;
   char *domain = NULL;
   char *result = NULL;
   BOOL ok;

   ok = WinUtil_ParseUserName(userName, '+', &user, &domain);
   ASSERT(ok);

   if (domain) {
      result = g_strdup_printf("%s"DIRSEP"%s", domain, user);
   } else {
      result = g_strdup(userName);
   }

   g_free(user);
   g_free(domain);

   return result;
#else
   return g_strdup(userName);
#endif
}


/*
 ******************************************************************************
 * ServiceUserNameToPipeName --                                          */ /**
 *
 * Map a user name into its VGAuth pipe name, escaping the backslash on Windows
 *
 * @param[in]   userName       The user name
 *
 * @return   the result ascii string
 *
 ******************************************************************************
 */

static gchar *
ServiceUserNameToPipeName(const char *userName)
{
   gchar *escapedName = ServiceEncodeUserName(userName);
#ifdef _WIN32
   /*
    * Adding below pragma only in windows to suppress the compile time warning
    * about unavailability of g_uuid_string_random() since compiler flag
    * GLIB_VERSION_MAX_ALLOWED is defined to GLIB_VERSION_2_34.
    * TODO: Remove below pragma when GLIB_VERSION_MAX_ALLOWED is bumped up to
    * or greater than GLIB_VERSION_2_52.
    */
#pragma warning(suppress : 4996)
   gchar *uuidStr = g_uuid_string_random();
   /*
    * Add a unique suffix to avoid a name collision with an existing named pipe
    * created by someone else (intentionally or by accident).
    * This is not needed for Linux; name collisions on sockets are already
    * avoided there since (1) file system paths to VGAuthService sockets are in
    * a directory that is writable only by root and (2) VGAuthService unlinks a
    * socket path before binding it to a newly created socket.
    */
   gchar *pipeName = g_strdup_printf("%s-%s-%s",
                                     SERVICE_PUBLIC_PIPE_NAME,
                                     escapedName,
                                     uuidStr);

   g_free(uuidStr);
#else
   gchar *pipeName = g_strdup_printf("%s-%s",
                                     SERVICE_PUBLIC_PIPE_NAME,
                                     escapedName);
#endif

   g_free(escapedName);
   return pipeName;
}


/*
 ******************************************************************************
 * ServiceCreateUserConnection --                                        */ /**
 *
 * Creates a connection that listens on user private pipe.
 *
 * @param[in]   userName          The name of the user for the pipe.
 * @param[out]  returnConn        The new connection.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceCreateUserConnection(const char *userName,
                            ServiceConnection **returnConn) // OUT
{
   ServiceConnection *newConn;
   VGAuthError err;

   newConn = g_malloc0(sizeof(ServiceConnection));

#ifdef _WIN32
   newConn->hComm = INVALID_HANDLE_VALUE;
#endif

   newConn->connId = ServiceConnectionGetNextConnectionId();

   newConn->userName = g_strdup(userName);
   newConn->pipeName = ServiceUserNameToPipeName(userName);

   err = ServiceNetworkListen(newConn, TRUE);

   if (VGAUTH_E_OK != err) {
      ServiceConnectionShutdown(newConn);
      Warning("%s: failed to setup private listen channel\n", __FUNCTION__);
      return err;
   }

   newConn->isPublic = FALSE;
   newConn->isListener = TRUE;
   *returnConn = newConn;

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * ServiceConnectionClone --                                             */ /**
 *
 * Copies a ServiceConnection structure.
 *
 * @param[in]   parent          The ServiceConnection to be cloned.
 * @param[out]  clone           The new connection.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceConnectionClone(ServiceConnection *parent,
                       ServiceConnection **clone)        // OUT
{
   ServiceConnection *newConn;

   ASSERT(parent);
   ASSERT(clone);

   newConn = g_malloc0(sizeof(ServiceConnection));

#ifdef _WIN32
   newConn->hComm = INVALID_HANDLE_VALUE;
#endif

   newConn->connId = ServiceConnectionGetNextConnectionId();

   newConn->pipeName = g_strdup(parent->pipeName);
   newConn->userName = g_strdup(parent->userName);

   newConn->isPublic = parent->isPublic;

   *clone = newConn;

   return VGAUTH_E_OK;
}


/*
 ******************************************************************************
 * ServiceConnectionShutdown --                                          */ /**
 *
 * Shuts down and frees a ServiceConnection.  Input is no
 * longer watched for on this connection, and any network resources
 * are closed.
 *
 * @param[in]   conn          The ServiceConnection to shut down.
 *
 ******************************************************************************
 */

void
ServiceConnectionShutdown(ServiceConnection *conn)
{
   if (NULL == conn) {
      return;
   }

   ASSERT(stopListeningIOFunc);

   (* stopListeningIOFunc) (conn);

   ServiceNetworkCloseConnection(conn);

   ServiceProtoCleanupParseState(conn);

   if (conn->isListener) {
      ServiceNetworkRemoveListenPipe(conn);
   }

   if (conn->dataConnectionIncremented) {
      ServiceDataConnectionDecrement(conn->userName);
   }

   g_free(conn->pipeName);
   g_free(conn->userName);

   g_free(conn);
}


/*
 ******************************************************************************
 * ServiceHashConnectionShutdown --                                      */ /**
 *
 * Wrapper for ServiceConnectionShutdown() to be called by the hash table's free.
 *
 * @param[in]   userData        The ServiceConnection to shut down.
 *
 ******************************************************************************
 */

static void
ServiceHashConnectionShutdown(gpointer userData)
{
   ServiceConnection *conn = (ServiceConnection *) userData;

   ServiceConnectionShutdown(conn);
}


/*
 ******************************************************************************
 * ServiceStartUserConnection --                                         */ /**
 *
 * Creates a new listen connection for 'userName' if there is none.
 * If there is already the listen connection for 'userName' reuse it.
 *
 * @param[in]   userName          The user for whom to create a new listener.
 * @param[out]  pipeName          The name of the new pipe to be used.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceStartUserConnection(const char *userName,
                           char **pipeName)
{
   ServiceConnection *userConn;
   VGAuthError err = VGAUTH_E_OK;
   gboolean connReuse = FALSE;
   gboolean bRet;

   /* Check if we can find the listening connection in the map */
   userConn = ServiceLookupListenConnection(userName);
   if (userConn) {
      connReuse = TRUE;
   }
#if LISTENCONN_NO_REUSE
   // remove from hash table which will call ServiceConnectionShutdown()
   (void) g_hash_table_remove(listenConnectionMap, userName);
#else
   bRet = UsercheckUserExists(userName);
   if (userConn) {
      if (bRet) {
         /*
          * If we have a cached conn -- and the user is still around (seems
          * like a weird corner case, but the 'deleted user' unit test hits
          * this) -- reuse.
          */
         *pipeName = g_strdup(userConn->pipeName);
         goto done;
       } else {
         /*
          * Have a conn, but can't find the user -- clean up before we make
          * a new conn.  This can happen if the service is hit by a network
          * glitch or the LDAP bug (see usercheck.c for details).
          *
          * Throw out the old, let it (try) to make a new.  If its the
          * LDAP bug, the listen will succeed, and we don't want to
          * end up with two userConn's for the same user.
          */
          Debug("%s: Already have a connection for user '%s', but the user "
                "check failed, so tearing down the connection and trying "
                "to rebuild\n",
                __FUNCTION__, userName);
         (void) g_hash_table_remove(listenConnectionMap, userName);
         // fall thru and let a new conn get created
      }
   }
#endif

   err = ServiceCreateUserConnection(userName, &userConn);
   if (err != VGAUTH_E_OK) {
      goto done;
   }

   err = (* startListeningIOFunc) (userConn);
   if (err != VGAUTH_E_OK) {
      goto done;
   }

   *pipeName = g_strdup(userConn->pipeName);

   /* Insert the new connection into the map */
   ServiceMapListenConnection(userName, userConn);

done:
   if (err != VGAUTH_E_OK && userConn) {
      if (connReuse) {
         VGAUTH_LOG_DEBUG("%s: removing dead userConn for %s from hashtable",
                          __FUNCTION__, userName);
         (void) g_hash_table_remove(listenConnectionMap, userName);
      } else {
         VGAUTH_LOG_DEBUG("%s: removing failed userConn for %s",
                          __FUNCTION__, userName);
         ServiceConnectionShutdown(userConn);
      }
   } else if (NULL != userConn) {
      g_get_current_time(&userConn->lastUse);
   }

   return err;
}


/*
 ******************************************************************************
 * ServiceNetworkIsConnectionPrivateSuperUser --                         */ /**
 *
 * Checks to see if the connection is private to superUser or a member
 * of the Administrators group.
 *
 * @param[int]  conn        The connection.
 *
 * @return TRUE if the connection is private to superUser
 *
 ******************************************************************************
 */

gboolean
ServiceNetworkIsConnectionPrivateSuperUser(ServiceConnection *conn)
{
   if (conn->isPublic) {
      return FALSE;
   } else {
      return ServiceIsSuperUser(conn->userName);
   }
}


/*
 ******************************************************************************
 * ServiceListenHashRemoveCheck --                                       */ /**
 *
 * Hash table remove check function.
 *
 * @param[in]  key       Hash table key.
 * @param[in]  value     Hash table value.
 * @param[in]  userData  Any user data (unused)
 *
 * @returns TRUE if the entry is to be removed from the table.
 *
 ******************************************************************************
 */

static gboolean
ServiceListenHashRemoveCheck(gpointer key,
                             gpointer value,
                             gpointer userData)
{
   ServiceConnection *conn = (ServiceConnection *) value;;

   if (Util_CheckExpiration(&(conn->lastUse), listenConnExpireTime)) {
      Debug("%s: removing old listen conn for user %s\n", __FUNCTION__, conn->userName);
      return TRUE;
   }

   return FALSE;
}


/*
 ******************************************************************************
 * ServiceListenReapTimerCallback --                                     */ /**
 *
 * Callback for a timer which looks for old user listen connections to reap.
 *
 * @param[in] userData   Any user data (unused).
 *
 * @return TRUE if the timer is to be kept, FALSE if not.
 *
 ******************************************************************************
 */

static gboolean
ServiceListenReapTimerCallback(gpointer userData)
{
   int numRemoved;

#if LISTENCONN_TABLE_DEBUG
   Debug("%s: looking for listen connection to reap\n", __FUNCTION__);
#endif

   numRemoved = g_hash_table_foreach_remove(listenConnectionMap,
                                            ServiceListenHashRemoveCheck, NULL);

#if LISTENCONN_TABLE_DEBUG
   Debug("%s: reaped %d listen connection\n", __FUNCTION__, numRemoved);
#endif

   /*
    * Keep the timer running if we still have entries in the hashtable.
    */
   reapTimerRunning = (g_hash_table_size(listenConnectionMap) > 0);
#if LISTENCONN_TABLE_DEBUG
   Debug("%s: reapTimerRunning? %d\n", __FUNCTION__, reapTimerRunning);
#endif

   return reapTimerRunning;
}


/*
 ******************************************************************************
 * ServiceInitListenConnectionPrefs --                                    */ /**
 *
 * Reads any preferences for the listen connection.
 *
 ******************************************************************************
 */

void
ServiceInitListenConnectionPrefs(void)
{
   listenConnExpireTime = Pref_GetInt(gPrefs,
                                      VGAUTH_PREF_NAME_LISTEN_TTL,
                                      VGAUTH_PREF_GROUP_NAME_SERVICE,
                                      LISTENCONN_EXPIRE_TIME_IN_SECONDS_DEFAULT);
   if (listenConnExpireTime <= 0) {
      Warning(VGAUTH_PREF_NAME_LISTEN_TTL
              " set to invalid value of %d, using default of %d instead\n",
              listenConnExpireTime, LISTENCONN_EXPIRE_TIME_IN_SECONDS_DEFAULT);
      listenConnExpireTime = LISTENCONN_EXPIRE_TIME_IN_SECONDS_DEFAULT;
   }
   Debug("%s: listen conn TTL set to %d seconds\n",
         __FUNCTION__, listenConnExpireTime);

   /*
    * Compute the reapCheckTime based on the TTL.
    */
   reapCheckTime = listenConnExpireTime / 10;
   if (reapCheckTime <= 0) {
      reapCheckTime = 1;
   }
   Debug("%s: computed reapCheckTime as %d seconds\n",
         __FUNCTION__, reapCheckTime);

}


/*
 ******************************************************************************
 * ServiceInitListenConnectionMap --                                     */ /**
 *
 * Initialize the listen connection map.
 *
 * @return  TRUE if successful. FALSE otherwise.
 *
 ******************************************************************************
 */

gboolean
ServiceInitListenConnectionMap(void)
{
   ServiceInitListenConnectionPrefs();

   ASSERT(listenConnectionMap == NULL);

   listenConnectionMap = g_hash_table_new_full(g_str_hash, g_str_equal,
                                               g_free, ServiceHashConnectionShutdown);

   return (listenConnectionMap != NULL);
}


/*
 ******************************************************************************
 * ServiceLookupListenConnection --                                      */ /**
 *
 * Checks to see if the listening connection for the userName is started.
 *
 * @param[in]  userName        The user name of the listen connection.
 *
 * @return  The listen connection of the user if it is already created.
 *          NULL otherwise.
 *
 ******************************************************************************
 */

ServiceConnection *
ServiceLookupListenConnection(const char *userName)
{
   ASSERT(listenConnectionMap);

   return (ServiceConnection *)
      g_hash_table_lookup(listenConnectionMap, userName);
}


/*
 ******************************************************************************
 * ServiceMapListenConnection --                                         */ /**
 *
 * Insert the listen connection of the user into the listen connecton map.
 *
 * @param[in]  userName        The user name of the listen connection.
 * @param[in]  userConn        The listen connection.
 *
 ******************************************************************************
 */

void
ServiceMapListenConnection(const char *userName,
                           ServiceConnection *userConn)
{
   ASSERT(listenConnectionMap);
   ASSERT(g_hash_table_lookup(listenConnectionMap, userName) == NULL);

   g_hash_table_insert(listenConnectionMap, g_strdup(userName), userConn);

   /*
    * Set up a reap timer if it's not already running.
    */
   if (!reapTimerRunning) {
      (void) g_timeout_add_seconds(reapCheckTime,
                                   ServiceListenReapTimerCallback,
                                   NULL);
      reapTimerRunning = TRUE;
   }
}


/*
 ******************************************************************************
 * Service_ReloadPrefs --                                                */ /**
 *
 * Reload any preferences used by the service implementation.
 *
 ******************************************************************************
 */

void
Service_ReloadPrefs(void)
{
   ServiceInitTicketPrefs();
   ServiceInitListenConnectionPrefs();
   SAML_Reload();
}


/*
 ******************************************************************************
 * Service_Shutdown --                                                */ /**
 *
 * Shutdown the service implementation.
 *
 ******************************************************************************
 */

void
Service_Shutdown(void)
{
   SAML_Shutdown();
}


/*
 ******************************************************************************
 * ServiceAcceptConnection --                                            */ /**
 *
 * Accepts a connection on a socket/pipe
 *
 * @param[in,out] connIn  The connection that owns the socket being accept()ed.
 * @param[in,out] onnOut  The new connection.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceAcceptConnection(ServiceConnection *connIn,
                        ServiceConnection *connOut)
{
   VGAuthError err = ServiceNetworkAcceptConnection(connIn, connOut);

   if (err != VGAUTH_E_OK) {
      return err;
   }

   ServiceDataConnectionIncrement(connOut->userName);
   connOut->dataConnectionIncremented = TRUE;

   // Check the per user connection limit
   if (!ServiceDataConnectionCheckLimit(connOut->userName)) {
      ServiceReplyTooManyConnections(connOut, dataConnectionMaxPerUser);
      return VGAUTH_E_TOO_MANY_CONNECTIONS;
   } else {
      return VGAUTH_E_OK;
   }
}

/*
 ******************************************************************************
 * ServiceInitDataConnectionPrefs --                                     */ /**
 *
 * Reads any preferences for data connections.
 *
 ******************************************************************************
 */

void
ServiceInitDataConnectionPrefs(void)
{
   dataConnectionMaxPerUser =
      Pref_GetInt(gPrefs,
                  VGAUTH_PREF_NAME_MAX_DATA_CONNECTIONS_PER_USER,
                  VGAUTH_PREF_GROUP_NAME_SERVICE,
                  VGAUTH_PREF_DEFAULT_MAX_DATA_CONNECTIONS_PER_USER);

   if (dataConnectionMaxPerUser <= 0) {
      Warning(VGAUTH_PREF_NAME_MAX_DATA_CONNECTIONS_PER_USER
              " set to invalid value of %d, using default of %d instead\n",
              dataConnectionMaxPerUser,
              VGAUTH_PREF_DEFAULT_MAX_DATA_CONNECTIONS_PER_USER);
      dataConnectionMaxPerUser =
         VGAUTH_PREF_DEFAULT_MAX_DATA_CONNECTIONS_PER_USER;
   }

   VGAUTH_LOG_DEBUG("Maximum number of data connections per user set to %d",
                    dataConnectionMaxPerUser);
}


/*
 ******************************************************************************
 * ServiceInitDataConnectionMap --                                       */ /**
 *
 * Initialize the data connection map.
 *
 * @return  TRUE if successful. FALSE otherwise.
 *
 ******************************************************************************
 */

gboolean
ServiceInitDataConnectionMap(void)
{
   ServiceInitDataConnectionPrefs();

   ASSERT(dataConnectionMap == NULL);

   dataConnectionMap = g_hash_table_new_full(g_str_hash, g_str_equal,
                                             g_free, g_free);

   return (dataConnectionMap != NULL);
}


/*
 ******************************************************************************
 * ServiceDataConnectionIncrement --                                     */ /**
 *
 * Increment the connection count for the user
 *
 * @param[in]  user   The user for whom we increment the count
 *
 ******************************************************************************
 */

void
ServiceDataConnectionIncrement(const char *user)
{
   int *pCount = g_hash_table_lookup(dataConnectionMap, user);

   if (pCount) {
      (*pCount)++;
   } else {
      pCount = g_malloc0(sizeof *pCount);
      (*pCount)++;
      g_hash_table_insert(dataConnectionMap, g_strdup(user), pCount);
   }
}


/*
 ******************************************************************************
 * ServiceDataConnectionDecrement --                                     */ /**
 *
 * Decrement the connection count for the user
 *
 * @param[in]  user   The user for whom we decrement the count
 *
 ******************************************************************************
 */

void
ServiceDataConnectionDecrement(const char *user)
{
   int *pCount = g_hash_table_lookup(dataConnectionMap, user);

   ASSERT(pCount);
   ASSERT(pCount > 0);
   (*pCount)--;
}


/*
 ******************************************************************************
 * ServiceDataConnectionDecrement --                                     */ /**
 *
 * Check if the user has exceeded its maximum connection limit
 *
 * @param[in]  user   The user for whom we check its connection limit
 *
 * @return  TRUE   if user can still have more connection(s)
 *          FALSE  otherwise
 *
 ******************************************************************************
 */

gboolean
ServiceDataConnectionCheckLimit(const char *user)
{
   int *pCount = g_hash_table_lookup(dataConnectionMap, user);

   if (!pCount) {
      return TRUE;
   }

   if (*pCount <= dataConnectionMaxPerUser) {
      return TRUE;
   }

   // no limit for the super user
   return ServiceIsSuperUser(user);
}


/*
 ******************************************************************************
 * ServiceIsSuperUser --                                                 */ /**
 *
 * Checks to see if a user is the super user or a member of the Administrator
 * group.
 *
 * @param[int]  user   The user name
 *
 * @return TRUE if the user is the super user
 *         FALSE otherwise
 *
 ******************************************************************************
 */

gboolean
ServiceIsSuperUser(const char *user)
{
#ifdef _WIN32
   /*
    * For Windows, accept either superUser or a member of the Administrator
    * group.  These may overlap, but check both to be safe.
    */
   gboolean bRet;

   bRet = Usercheck_CompareByName(user, SUPERUSER_NAME);
   if (bRet) {
      return TRUE;
   }
   return Usercheck_IsAdminMember(user);
#else
   // On linux, we only care about root
   return Usercheck_CompareByName(user, SUPERUSER_NAME);
#endif
}
 0707010000046C000081A40000000000000000000000016822550500003FBB000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/serviceInt.h    /*********************************************************
 * Copyright (c) 2011-2017,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file serviceInt.h --
 *
 *    Service internal data types.
 */

#ifndef _SERVICEINT_H_
#define _SERVICEINT_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "VGAuthAuthentication.h"
#include "VGAuthBasicDefs.h"
#include "VGAuthError.h"
#include "prefs.h"
#include "usercheck.h"
#include "audit.h"

#define VMW_TEXT_DOMAIN "VGAuthService"
#include "i18n.h"

#ifdef _WIN32
#include <windows.h>
#include <winsock2.h>
enum { BUFSIZE = 4096 };
#include "userAccessControl.h"
#endif

#ifdef _WIN32
#define DIRSEP "\\"
#else
#define DIRSEP "/"
#endif

/*
 * Use this for any informational messages, eg "service started".
 */
#define Log   g_message

/*
 * Use this for any error reporting, such as unexpected failures from APIs
 * or syscalls.
 */
#define Warning   g_warning

/*
 * Use this for any debugging messages.
 */
#define Debug  g_debug


/*
 * Set this to be horribly inefficient but to be sure that nothing
 * is assuming it will get a full packet as sent by a single syscall
 * on the other end.
 */
#define  NETWORK_FORCE_TINY_PACKETS 0

typedef struct ProtoRequest ProtoRequest;

#ifdef _WIN32
typedef enum _IOState {
   IO_IDLE = 0,
   IO_PENDING, /* need to wait an issued IO */
   IO_COMPLETE, /* need to process a completed IO */
   IO_BROKEN, /* The other end has closed the pipe handle */
} IOState;

typedef enum _PidVerifyState {
   PID_VERIFY_NONE = 0,    /* Initial state or the check is not needed */
   PID_VERIFY_PENDING, /* Wait for the check to complete */
   PID_VERIFY_GOOD,    /* The check completed successfully */
   PID_VERIFY_BAD      /* The check failed */
} PidVerifyState;
#endif

typedef struct _ServiceConnection {
   gboolean isPublic;

   char  *pipeName;
   char  *userName;

   /*
    * The request currently being processed
    */
   ProtoRequest *curRequest;
   GMarkupParseContext *parseContext;

#ifdef SUPPORT_TCP
   int port;
#endif

   /*
    * To be renamed to gTag
    * Windows: GSource Id returned from g_source_attach()
    * Linux: GSource id returned from g_io_add_watch()
    */
   guint gioId;

#ifdef _WIN32
   HANDLE hComm;
   OVERLAPPED ol;
   gchar readBuffer[BUFSIZE];
   int bytesRead;
   IOState ioState;
   UserAccessControl m_uac;
   DWORD pid;
   HANDLE hProc;
   HANDLE hChallengeEvent;
   HANDLE hChallengeEventDup;
   PidVerifyState pidVerifyState;
#else
   int sock;
#endif

   gboolean eof;
   int connId;
   gboolean isListener;

   /*
    * The last time a listen connection was used.
    */
   GTimeVal lastUse;
   gboolean dataConnectionIncremented;
} ServiceConnection;


typedef enum {
   SUBJECT_TYPE_NAMED,
   SUBJECT_TYPE_ANY,
   SUBJECT_TYPE_UNSET,     // special case for removing all subjects
} ServiceSubjectType;

/*
 * The service's idea of a Subject.
 */
typedef struct _ServiceSubject {
   ServiceSubjectType type;
   gchar *name;
} ServiceSubject;

/*
 * The service's idea of a AliasInfo.
 */
typedef struct _ServiceAliasInfo {
   ServiceSubjectType type;
   gchar *name;
   gchar *comment;
} ServiceAliasInfo;

/*
 * The service's idea of an Alias.
 */
typedef struct _ServiceAlias {
   gchar *pemCert;
   int num;
   ServiceAliasInfo *infos;
} ServiceAlias;

/*
 * The service's idea of an MappedAlias.
 */
typedef struct _ServiceMappedAlias {
   gchar *pemCert;
   int num;
   ServiceSubject *subjects;
   gchar *userName;
} ServiceMappedAlias;

/*
 * Possible types of validation.
 */
typedef enum {
   VALIDATION_RESULTS_TYPE_UNKNOWN,
   VALIDATION_RESULTS_TYPE_SAML,
   VALIDATION_RESULTS_TYPE_SSPI,
   VALIDATION_RESULTS_TYPE_NAMEPASSWORD,
   VALIDATION_RESULTS_TYPE_SAML_INFO_ONLY,
} ServiceValidationResultsType;

/*
 * Data associated with a userHandle validation.
 *
 * Right now this is just SAML data.  If we ever add more,
 * this could become a union.
 */
typedef struct _ServiceValidationResultsData {
   char *samlSubject;
   ServiceAliasInfo aliasInfo;
} ServiceValidationResultsData;


/*
 * Global vars
 */

extern PrefHandle gPrefs;
extern gboolean gVerboseLogging;

extern char *gInstallDir;

#ifndef _WIN32
gboolean ServiceNetworkCreateSocketDir(void);
#endif

VGAuthError ServiceNetworkListen(ServiceConnection *conn,            // IN/OUT
                                 gboolean makeSecure);

void ServiceInitListenConnectionPrefs(void);

void ServiceNetworkRemoveListenPipe(ServiceConnection *conn);

#ifdef _WIN32
VGAuthError ServiceNetworkStartRead(ServiceConnection *conn);        // IN/OUT
#endif

VGAuthError ServiceNetworkAcceptConnection(ServiceConnection *connIn,
                                           ServiceConnection *connOut);// IN/OUT

void ServiceNetworkCloseConnection(ServiceConnection *conn);

VGAuthError ServiceNetworkReadData(ServiceConnection *conn,
                                   gsize *len,                        // OUT
                                   gchar **data);                     // OUT

VGAuthError ServiceNetworkWriteData(ServiceConnection *conn,
                                    gsize len,
                                    gchar *data);

gboolean ServiceNetworkIsConnectionPrivateSuperUser(ServiceConnection *conn);

/*
 * May have to expose more of these.  This is all we need at this point.
 */
typedef VGAuthError (* ServiceStartListeningForIOFunc)(ServiceConnection *conn);
typedef VGAuthError (* ServiceStopListeningForIOFunc)(ServiceConnection *conn);

VGAuthError ServiceRegisterIOFunctions(ServiceStartListeningForIOFunc startFunc,
                                       ServiceStopListeningForIOFunc stopFunc);


/*
 * Ticket functions
 */

VGAuthError ServiceInitTickets(void);
void ServiceInitTicketPrefs(void);

#ifdef _WIN32
VGAuthError ServiceCreateTicketWin(const char *userName,
                                   ServiceValidationResultsType type,
                                   ServiceValidationResultsData *svData,
                                   HANDLE clientProcHandle,
                                   const char *token,
                                   char **ticket);
VGAuthError ServiceValidateTicketWin(const char *ticket,
                                     HANDLE clientProcHandle,
                                     char **userName,
                                     ServiceValidationResultsType *type,
                                     ServiceValidationResultsData **svData,
                                     char **token);
#else
VGAuthError ServiceCreateTicketPosix(const char *userName,
                                     ServiceValidationResultsType type,
                                     ServiceValidationResultsData *svData,
                                     char **ticket);
VGAuthError ServiceValidateTicketPosix(const char *ticket,
                                       char **userName,
                                       ServiceValidationResultsType *type,
                                       ServiceValidationResultsData **svData);
#endif
VGAuthError ServiceRevokeTicket(ServiceConnection *conn,
                                const char *ticket);
VGAuthError ServiceLookupTicketOwner(const char *ticket,
                                     char **userName);


/*
 * Alias functions
 */
VGAuthError ServiceAliasInitAliasStore(void);

VGAuthError ServiceAliasAddAlias(const gchar *reqUserName,
                                 const gchar *userName,
                                 gboolean addMapped,
                                 const gchar *pemCert,
                                 ServiceAliasInfo *ai);

VGAuthError ServiceAliasRemoveAlias(const gchar *reqUserName,
                                    const gchar *userName,
                                    const gchar *pemCert,
                                    ServiceSubject *subj);

VGAuthError ServiceAliasQueryAliases(const gchar *userName,
                                     int *num,
                                     ServiceAlias **aList);

VGAuthError ServiceAliasQueryMappedAliases(int *num,
                                           ServiceMappedAlias **maList);

void ServiceAliasFreeAliasList(int num, ServiceAlias *aList);

void ServiceAliasFreeAliasInfo(ServiceAliasInfo *ai);

void ServiceAliasFreeAliasInfoContents(ServiceAliasInfo *ai);

void ServiceAliasCopyAliasInfoContents(const ServiceAliasInfo *src,
                                       ServiceAliasInfo *dst);

void ServiceAliasFreeMappedAliasList(int num, ServiceMappedAlias *maList);

gboolean ServiceAliasIsSubjectEqual(ServiceSubjectType t1,
                                    ServiceSubjectType t2,
                                    const gchar *n1,
                                    const gchar *n2);

gboolean ServiceComparePEMCerts(const gchar *pemCert1,
                                const gchar *pemCert2);

/*
 * Connection functions
 */
void ServiceConnectionShutdown(ServiceConnection *conn);

VGAuthError ServiceConnectionClone(ServiceConnection *parent,
                                   ServiceConnection **clone);       // OUT

VGAuthError ServiceCreatePublicConnection(ServiceConnection **returnConn);// OUT

VGAuthError ServiceCreateUserConnection(const char *userName,
                                        ServiceConnection **returnConn); // OUT


/*
 * Protocol parsing and generation.
 */
VGAuthError ServiceProtoReadAndProcessRequest(ServiceConnection *conn);

VGAuthError ServiceProtoDispatchRequest(ServiceConnection *conn,
                                        ProtoRequest *req);

void ServiceProtoCleanupParseState(ServiceConnection *conn);

VGAuthError ServiceStartUserConnection(const char *userName,
                                       char **pipeName);       // OUT

VGAuthError ServiceProtoHandleSessionRequest(ServiceConnection *conn,
                                             ProtoRequest *req);

VGAuthError ServiceProtoHandleConnection(ServiceConnection *conn,
                                         ProtoRequest *req);

VGAuthError ServiceProtoAddAlias(ServiceConnection *conn,
                                   ProtoRequest *req);

VGAuthError ServiceProtoRemoveAlias(ServiceConnection *conn,
                                    ProtoRequest *req);

VGAuthError ServiceProtoQueryAliases(ServiceConnection *conn,
                                     ProtoRequest *req);

VGAuthError ServiceProtoQueryMappedAliases(ServiceConnection *conn,
                                           ProtoRequest *req);

VGAuthError ServiceProtoCreateTicket(ServiceConnection *conn,
                                     ProtoRequest *req);

VGAuthError ServiceProtoValidateTicket(ServiceConnection *conn,
                                       ProtoRequest *req);

VGAuthError ServiceProtoRevokeTicket(ServiceConnection *conn,
                                     ProtoRequest *req);

/*
 * File wrapper functions.
 */
int ServiceFileRenameFile(const gchar *srcName,
                          const gchar *dstName);

int ServiceFileUnlinkFile(const gchar *fileName);

#ifdef _WIN32
int ServiceFileWinMakeTempfile(gchar **fileName,
                               UserAccessControl *uac);
#else
int ServiceFilePosixMakeTempfile(gchar *fileName,
                                 int mode);
#endif

#ifndef _WIN32
VGAuthError ServiceFileSetOwner(const gchar *fileName,
                                const char *userName);
#endif

VGAuthError ServiceFileCopyOwnership(const gchar *srcFilename,
                                     const gchar *dstFilename);

int ServiceFileMakeDirTree(const gchar *dirName,
                           int mode);

#ifndef _WIN32
int ServiceFileSetPermissions(const gchar *fileName,
                              int mode);

int ServiceFileGetPermissions(const gchar *fileName,
                              int *mode);
#endif

#ifdef _WIN32
VGAuthError ServiceFileVerifyAdminGroupOwned(const char *fileName);
VGAuthError ServiceFileVerifyEveryoneReadable(const char *fileName);
VGAuthError ServiceFileVerifyUserAccess(const char *fileName,
                                        const char *userName);
VGAuthError ServiceFileVerifyAdminGroupOwnedByHandle(const HANDLE hFile);
VGAuthError ServiceFileVerifyEveryoneReadableByHandle(const HANDLE hFile);
VGAuthError ServiceFileVerifyUserAccessByHandle(const HANDLE hFile,
                                                const char *userName);
gchar *ServiceFileGetPathByHandle(HANDLE hFile);
#else
VGAuthError ServiceFileVerifyFileOwnerAndPerms(const char *fileName,
                                               const char *userName,
                                               int mode,
                                               uid_t *uidRet,
                                               gid_t *gidRet);
#endif

VGAuthError ServiceRandomBytes(int size,
                               guchar *buffer);


#ifdef _WIN32
VGAuthError ServiceStartVerifyPid(ServiceConnection *conn,
                                  const char *pidInText,
                                  char **event);
VGAuthError ServiceEndVerifyPid(ServiceConnection *conn);
#endif


VGAuthError ServiceVerifyAndCheckTrustCertChainForSubject(int numCerts,
                                                          const char **pemCertChain,
                                                          const char *userName,
                                                          ServiceSubject *subj,
                                                          char **userNameOut,
                                                          ServiceAliasInfo **verifyAi);


VGAuthError ServiceInitVerify(void);


void Service_ReloadPrefs(void);
void Service_Shutdown(void);

gchar *ServiceEncodeUserName(const char *userName);
gchar *ServiceDecodeUserName(const char *userName);

VGAuthError SAML_Init(void);

/* clang-format off */
VGAuthError SAML_VerifyBearerTokenEx(const char *xmlText,
                                     const char *userName,
                                     gboolean hostVerified,
                                     char **userNameOut,
                                     char **subjectNameOut,
                                     ServiceAliasInfo **verifyAi);
VGAuthError SAML_VerifyBearerToken(const char *xmlText,
                                   const char *userName,
                                   char **userNameOut,
                                   char **subjectNameOut,
                                   ServiceAliasInfo **verifyAi);
VGAuthError SAML_VerifyBearerTokenAndChain(const char *xmlText,
                                           const char *userName,
                                           gboolean hostVerified,
                                           char **userNameOut,
                                           char **subjectNameOut,
                                           ServiceAliasInfo **verifyAi);
/* clang-format on */

void SAML_Shutdown(void);
void SAML_Reload(void);

void ServiceFreeValidationResultsData(ServiceValidationResultsData *samlData);

WIN32_ONLY(gboolean ServiceOldInstanceExists(void);)

void ServiceReplyTooManyConnections(ServiceConnection *conn,
                                    int connLimit);

VGAuthError ServiceAcceptConnection(ServiceConnection *lConn,
                                    ServiceConnection *newConn);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif   // _SERVICEINT_H_
 0707010000046D000081A40000000000000000000000016822550500005DFF000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/ticket.c    /*********************************************************
 * Copyright (C) 2011-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file ticket.c --
 *
 *    Code to support ticket creation and sharing.
 */

#include "VGAuthLog.h"
#include "VGAuthUtil.h"
#include "serviceInt.h"
#ifdef _WIN32
#include "winDupHandle.h"
#endif

/*
 * XXX TODO
 *
 * There may be a bug in the ticket implementation.
 *
 * Right now, a ticket doesn't care about any expiration details
 * of the authn method used to create it.  This means a bearer
 * token that expires in 5 minutes could be used to create a ticket
 * with a far longer expiration time.
 *
 * This seems like a possible security problem, and it should instead
 * try to obey the auth time of the original authn method.
 *
 * This may be messy to implement securely, since we currently
 * lose the expiration date.  It could be passed around along with the
 * Subject and AliasInfo we already store, but a bad client could
 * cheat -- but a bad client can do other horrible things.  Since CreateTicket
 * can be called as a normal user, this seems hackable.
 *
 * Should CreateTicket be restricted to superUser?
 *
 * Also -- we currently would support making a ticket from a ticket.
 * Are there any security concerns here?
 */

/*
 * Debugging flags.
 */

/*
 * Set to spew the hashtable on add/remove.
 * Note that this allows secure information (tickets) to be seen.
 */
#define TICKET_TABLE_DEBUG 0


/*
 * Default to 24 hours, to match the SSPI ticket code in vixToolsWin32.c
 */
#define  TICKET_EXPIRE_TIME_IN_SECONDS_DEFAULT (24 * 60 * 60)

static void ServiceReapOldTickets(void);

typedef struct {
   gchar *ticket;
   gchar *userName;
#ifdef _WIN32
   HANDLE userToken;
#endif

   ServiceValidationResultsType type;
   /*
    * XXX may want to turn this into a union if we add data for other
    * validation types.
    */
   ServiceValidationResultsData *svData;

   // XXX For the expiration time, make it relative to the last
   // Validate, like the SSPI code.
   // This may want to be relative to the create time instead.

   // XXX I wanted to use GDateTime, but its in glib 2.26, and we're at 2.24.
   // Its probably a good idea to upgrade asap.
   GTimeVal lastUse;
} TicketInfo;

static GHashTable *ticketTable = NULL;
static int ticketExpireTime = TICKET_EXPIRE_TIME_IN_SECONDS_DEFAULT;
static int reapCheckTime;
static gboolean reapTimerRunning = FALSE;


/*
 ******************************************************************************
 * ServiceFreeValidationResultsData --                                   */ /**
 *
 * @param[in] svd   ServiceValidationResultsData to be freed.
 *
 ******************************************************************************
 */

void
ServiceFreeValidationResultsData(ServiceValidationResultsData *svd)
{
   if (NULL == svd) {
      return;
   }
   g_free(svd->samlSubject);
   ServiceAliasFreeAliasInfoContents(&(svd->aliasInfo));
   g_free(svd);
}


/*
 ******************************************************************************
 * TicketFreeTicketInfo --                                               */ /**
 *
 * @param[in] info   TicketInfo to be freed.
 *
 ******************************************************************************
 */

static void
TicketFreeTicketInfo(gpointer i)
{
   TicketInfo *info = (TicketInfo *) i;

   g_free(info->ticket);
   g_free(info->userName);
   ServiceFreeValidationResultsData(info->svData);
#ifdef _WIN32
   CloseHandle(info->userToken);
#endif
   g_free(info);
}


/*
 ******************************************************************************
 * TicketReapTimerCallback --                                            */ /**
 *
 * Callback for a timer which looks for old tickets to reap.
 *
 * @param[in] userData   Any user data (unused).
 *
 * @return TRUE if the timer is to be kept, FALSE if not.
 *
 ******************************************************************************
 */

static gboolean
TicketReapTimerCallback(gpointer userData)
{
   ServiceReapOldTickets();
   /*
    * Keep the timer running if we still have entries in the hashtable.
    */
   reapTimerRunning = (g_hash_table_size(ticketTable) > 0);

#if TICKET_TABLE_DEBUG
   Debug("%s: reapTimerRunning? %d\n", __FUNCTION__, reapTimerRunning);
#endif

   return reapTimerRunning;
}


/*
 ******************************************************************************
 * ServiceInitTicketPrefs --                                             */ /**
 *
 * Reads preferences used by the ticket code.
 *
 ******************************************************************************
 */

void
ServiceInitTicketPrefs(void)
{
   ticketExpireTime = Pref_GetInt(gPrefs,
                                  VGAUTH_PREF_NAME_TICKET_TTL,
                                  VGAUTH_PREF_GROUP_NAME_TICKET,
                                  TICKET_EXPIRE_TIME_IN_SECONDS_DEFAULT);
   if (ticketExpireTime <= 0) {
      Warning(VGAUTH_PREF_NAME_TICKET_TTL
              " set to invalid value of %d, using default of %d instead\n",
              ticketExpireTime, TICKET_EXPIRE_TIME_IN_SECONDS_DEFAULT);
      ticketExpireTime = TICKET_EXPIRE_TIME_IN_SECONDS_DEFAULT;
   }
   Debug("%s: ticket TTL set to %d seconds\n", __FUNCTION__, ticketExpireTime);

   /*
    * Compute the reapCheckTime based on the TTL.
    */
   reapCheckTime = ticketExpireTime / 10;
   if (reapCheckTime <= 0) {
      reapCheckTime = 1;
   }
   Debug("%s: computed reapCheckTime as %d seconds\n",
         __FUNCTION__, reapCheckTime);

}


/*
 ******************************************************************************
 * ServiceInitTickets --                                                 */ /**
 *
 * Sets up the service ticket table.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceInitTickets(void)
{
   ServiceInitTicketPrefs();

   /*
    * We use the ticket inside the TicketInfo struct as the key, so
    * there's no free function specified for it.
    */
   ticketTable = g_hash_table_new_full(g_str_hash,
                                       g_str_equal,
                                       NULL,                 // key destroy func
                                       TicketFreeTicketInfo);// value destroy func

   return VGAUTH_E_OK;
}


#if TICKET_TABLE_DEBUG
/*
 ******************************************************************************
 * DumpTicketTable --                                                    */ /**
 *
 * Hashtable debug code to dump the entire table.
 *
 ******************************************************************************
 */

static void
DumpTicketTable()
{
   GHashTableIter iter;
   gpointer key, value;
   TicketInfo *info;

   g_hash_table_iter_init(&iter, ticketTable);
   while (g_hash_table_iter_next(&iter, &key, &value)) {
      info = (TicketInfo *) value;
      printf("key: %s (%p), val (%p) (ticket %s, user %s)\n",
             (gchar *) key, key, value, info->ticket, info->userName);
   }
}
#endif // TICKET_TABLE_DEBUG


/*
 ******************************************************************************
 * TicketGenerateTicket --                                               */ /**
 *
 * Creates a new string ticket.  The caller needs to double-check that it is
 * not already in use.
 *
 * @param[in]   userName      The userName associated with the ticket to be
 *                            created.
 *
 * @return The new ticket.  The caller is repsonsible for freeing it.
 *
 ******************************************************************************
 */

static gchar *
TicketGenerateTicket(const gchar *userName)
{
#define RAND_BUF_SIZE   8
   guchar rndBuf[RAND_BUF_SIZE];
   gchar *newTicket;
   gchar *b64rnd;
   VGAuthError err;

   err = ServiceRandomBytes(RAND_BUF_SIZE, rndBuf);
   if (VGAUTH_E_OK != err) {
      return NULL;
   }

   b64rnd = g_base64_encode(rndBuf, RAND_BUF_SIZE);

   /*
    * Use a constant string, the username, and a 256 bits
    * of base64'd random data.
    */
   newTicket = g_strdup_printf("Ticket-%s-%s", userName, b64rnd);

   g_free(b64rnd);

   return newTicket;
}


/*
 ******************************************************************************
 * ServiceCreateTicketInfo --                                            */ /**
 *
 * Creates a TicketInfo object associated with user, adds it to the ticket
 * table and returns it.
 *
 * @param[in]   userName      The userName associated with the ticket to be
 * @param[in]   type          The type of validation used to create the
 *                            ticket.
 * @param[in]   svData        Any SAML validation data if the @a type is
 *                            VAIDATION_TYPE_SAML.
 * @param[out]  info          The ticket info object created for the new ticket
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

static VGAuthError
ServiceCreateTicketInfo(const char *userName,
                        const ServiceValidationResultsType type,
                        const ServiceValidationResultsData *svData,
                        TicketInfo **info)
{
   VGAuthError retCode = VGAUTH_E_FAIL;
   TicketInfo *newInfo;
   gchar *newTicket = NULL;

   /*
    * Get a new ticket, and be sure it's not a dup.
    */
   while (TRUE) {
      g_free(newTicket);
      newTicket = TicketGenerateTicket(userName);
      if (NULL == newTicket) {
         VGAUTH_LOG_WARNING("TicketGenerateTicket() failed, user = %s", userName);
         goto done;
      } else if (NULL == g_hash_table_lookup(ticketTable, newTicket)) {
         break;
      }
   }

   newInfo = g_malloc0(sizeof(TicketInfo));

   /*
    * GHashTable needs both key and value to be allocated outside of it,
    * so pass the ownership of newTicket to newInfo
    */
   newInfo->ticket = newTicket;
   newTicket = NULL;

   newInfo->userName = g_strdup(userName);
   g_get_current_time(&newInfo->lastUse);

   newInfo->type = type;
   if (VALIDATION_RESULTS_TYPE_SAML == type) {
      newInfo->svData = g_malloc0(sizeof(ServiceValidationResultsData));
      newInfo->svData->samlSubject = g_strdup(svData->samlSubject);
      ServiceAliasCopyAliasInfoContents(&(svData->aliasInfo),
                                        &(newInfo->svData->aliasInfo));
   }


   g_hash_table_insert(ticketTable, newInfo->ticket, newInfo);

#if TICKET_TABLE_DEBUG
   Debug("%s: dumping ticket table after add\n", __FUNCTION__);
   DumpTicketTable();
#endif

   /*
    * Set up a reap timer if it's not already running.
    */
   if (!reapTimerRunning) {
      (void) g_timeout_add_seconds(reapCheckTime,
                                   TicketReapTimerCallback,
                                   NULL);
      reapTimerRunning = TRUE;
   }

   *info = newInfo;

   retCode = VGAUTH_E_OK;

done:

   g_free(newTicket);

   return retCode;
}


#ifdef _WIN32
/*
 ******************************************************************************
 * ServiceCreateTicketWin --                                             */ /**
 *
 * Creates a ticket associated with user, and returns it.
 *
 * @param[in]   userName      The userName associated with the ticket to be
 *                            created.
 * @param[in]   type          The type of validation used to create the
 *                            ticket.
 * @param[in]   svData        Any SAML validation data if the @a type is
 *                            VALIDATION_RESULTS_TYPE_SAML.
 * @param[in]   clientProcHandle  The HANDLE that identifies the client process
 *                            where the access token is duplicated from.
 * @param[in]   token         The user's access token HANDLE from the client.
 * @param[out]  ticket        The new ticket.  To be freed by the caller.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceCreateTicketWin(const char *userName,
                       const ServiceValidationResultsType type,
                       ServiceValidationResultsData *svData,
                       HANDLE clientProcHandle,
                       const char *token,
                       char **ticket)
{
   VGAuthError retCode = VGAUTH_E_FAIL;
   TicketInfo *newInfo = NULL;
   HANDLE copyHandle = ServiceDupHandleFrom(clientProcHandle, token);

   if (!copyHandle) {
      VGAUTH_LOG_WARNING("ServiceDupHandleFrom() failed, user = %s", userName);
      goto done;
   }

   retCode = ServiceCreateTicketInfo(userName, type, svData, &newInfo);
   if (retCode != VGAUTH_E_OK) {
      VGAUTH_LOG_WARNING("ServiceCreateTicketInfo() failed");
      goto done;
   }

   newInfo->userToken = copyHandle;
   copyHandle = NULL;

   *ticket = g_strdup(newInfo->ticket);

done:

   if (copyHandle) {
      CloseHandle(copyHandle);
   }

   return retCode;
}
#else


/*
 ******************************************************************************
 * ServiceCreateTicketPosix --                                           */ /**
 *
 * Creates a ticket associated with user, and returns it.
 *
 * @param[in]   userName      The userName associated with the ticket to be
 *                            created.
 * @param[in]   type          The type of validation used to create the
 *                            ticket.
 * @param[in]   svData        Any SAML validation data if the @a type is
 *                            VALIDATION_RESULTS_TYPE_SAML.
 * @param[out]  ticket        The new ticket.  To be freed by the caller.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceCreateTicketPosix(const char *userName,
                         const ServiceValidationResultsType type,
                         ServiceValidationResultsData *svData,
                         char **ticket)
{
   VGAuthError retCode;
   TicketInfo *newInfo = NULL;

   retCode = ServiceCreateTicketInfo(userName, type, svData, &newInfo);
   if (retCode != VGAUTH_E_OK) {
      goto done;
   }

   *ticket = g_strdup(newInfo->ticket);

done:
   return retCode;
}
#endif


/*
 ******************************************************************************
 * TicketHashRemoveCheck --                                             */ /**
 *
 * Hash table remove check function.
 *
 * @param[in]  key       Hash table key.
 * @param[in]  value     Hash table value.
 * @param[in]  userData  Any user data (unused)
 *
 * @returns TRUE if the entry is to be removed from the table.
 *
 ******************************************************************************
 */

static gboolean
TicketHashRemoveCheck(gpointer key,
                      gpointer value,
                      gpointer userData)
{
   TicketInfo *info = (TicketInfo *) value;

   if (Util_CheckExpiration(&(info->lastUse), ticketExpireTime)) {
#if TICKET_TABLE_DEBUG
      Debug("%s: removing old ticket %s\n", __FUNCTION__, info->ticket);
#endif
      return TRUE;
   }

   return FALSE;
}


/*
 ******************************************************************************
 * ServiceReapOldTickets --                                              */ /**
 *
 * Looks for any expired tickets and removes them.
 *
 ******************************************************************************
 */

static void
ServiceReapOldTickets(void)
{
   int numRemoved;

#if TICKET_TABLE_DEBUG
   Debug("%s: dumping ticket table before reap\n", __FUNCTION__);
   DumpTicketTable();
#endif

   numRemoved = g_hash_table_foreach_remove(ticketTable,
                                            TicketHashRemoveCheck, NULL);

#if TICKET_TABLE_DEBUG
   Debug("%s: reaped %d tickets\n", __FUNCTION__, numRemoved);

   Debug("%s: dumping ticket table after reap\n", __FUNCTION__);
   DumpTicketTable();
#endif
}


#ifdef _WIN32
/*
 ******************************************************************************
 * ServiceValidateTicketWin --                                           */ /**
 *
 * Validates a ticket, returning the associated user if it's good.
 *
 * @param[in]   ticket        The ticket being validated.
 * @param[in]   clientProcHandle  The HANDLE that identifes the client process
 *                            where the access token is duplicated into.
 * @param[in]   type          The type of validation used to create the
 *                            ticket.
 * @param[in]   svData        Any SAML validation data if the @a type is
 *                            VALIDATION_RESULTS_TYPE_SAML.
 * @param[out]  userName      The userName associated with the ticket if valid.
 * @param[out]  token         The value in text of the access token that is
 *                            duplicated to the client process if the ticket is
 *                            valid.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceValidateTicketWin(const char *ticket,
                         HANDLE clientProcHandle,
                         char **userName,
                         ServiceValidationResultsType *type,
                         ServiceValidationResultsData **svData,
                         char **token)
{
   TicketInfo *info;
   VGAuthError err = VGAUTH_E_FAIL;
   char *dupTokenInText;
   ServiceValidationResultsData *retSvData = NULL;

   *userName = NULL;
   *token = NULL;
   *type = VALIDATION_RESULTS_TYPE_UNKNOWN;

   ServiceReapOldTickets();
   info = g_hash_table_lookup(ticketTable, ticket);

   if (!info) {
      err = VGAUTH_E_INVALID_TICKET;
      goto done;
   }

   dupTokenInText = ServiceDupHandleTo(clientProcHandle, info->userToken);
   if (!dupTokenInText) {
      VGAUTH_LOG_WARNING("ServiceDupHandleTo() failed, user = %s", info->userName);
      goto done;
   }

   /* all OK */

   g_get_current_time(&info->lastUse);

   *userName = g_strdup(info->userName);
   *token = dupTokenInText;
   if (VALIDATION_RESULTS_TYPE_SAML == info->type) {
      retSvData = g_malloc0(sizeof(ServiceValidationResultsData));
      retSvData->samlSubject = g_strdup(info->svData->samlSubject);
      ServiceAliasCopyAliasInfoContents(&(info->svData->aliasInfo),
                                        &(retSvData->aliasInfo));
   }
   *type = info->type;
   *svData = retSvData;

   err = VGAUTH_E_OK;

done:
   return err;
}
#else


/*
 ******************************************************************************
 * ServiceValidateTicketPosix --                                         */ /**
 *
 * Validates a ticket, returning the associated user if it's good.
 *
 * @param[in]   ticket        The ticket being validated.
 * @param[out]  userName      The userName associated with the ticket if valid.
 * @param[in]   type          The type of validation used to create the
 *                            ticket.
 * @param[in]   svData        Any SAML validation data if the @a type is
 *                            VAIDATION_TYPE_SAML.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceValidateTicketPosix(const char *ticket,
                           char **userName,
                           ServiceValidationResultsType *type,
                           ServiceValidationResultsData **svData)
{
   TicketInfo *info;
   ServiceValidationResultsData *retSvData = NULL;
   VGAuthError err;

   ServiceReapOldTickets();
   info = g_hash_table_lookup(ticketTable, ticket);

   if (NULL != info) {
      // update the last access time
      g_get_current_time(&info->lastUse);

      *userName = g_strdup(info->userName);
      *type = info->type;
      if (VALIDATION_RESULTS_TYPE_SAML == info->type) {
         retSvData = g_malloc0(sizeof(ServiceValidationResultsData));
         retSvData->samlSubject = g_strdup(info->svData->samlSubject);
         ServiceAliasCopyAliasInfoContents(&(info->svData->aliasInfo),
                                           &(retSvData->aliasInfo));
      }

      err = VGAUTH_E_OK;
   } else {
      *userName = NULL;
      *type = VALIDATION_RESULTS_TYPE_UNKNOWN;
      err = VGAUTH_E_INVALID_TICKET;
   }
   *svData = retSvData;

   return err;
}
#endif


/*
 ******************************************************************************
 * ServiceRevokeTicket --                                                */ /**
 *
 * Revokes a ticket.
 *
 * @param[in]   conn          The ServiceConnection.
 * @param[in]   ticket        The ticket being validated.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceRevokeTicket(ServiceConnection *conn,
                    const char *ticket)
{
   TicketInfo *info;
   VGAuthError err;

   ServiceReapOldTickets();
   info = g_hash_table_lookup(ticketTable, ticket);

   if (NULL != info) {
      /*
       * Security check.  Allow only SUPERUSER or the ticket's owner to
       * Revoke it.  We do it here instead of in
       * Proto_SecurityCheckRequest() because we want to treat this as a
       * no-op, since otherwise an attacker can confirm the existance of a
       * ticket by getting back a permission error.
       */
      if (!(ServiceNetworkIsConnectionPrivateSuperUser(conn) ||
          Usercheck_CompareByName(conn->userName, info->userName))) {
         /*
          * Both an auditing event and debug noise may be visible
          * to the attacker, so don't spew.
          */
         err = VGAUTH_E_OK;
         goto done;
      }
      if (!g_hash_table_remove(ticketTable, ticket)) {
         // this shouldn't happen, since we just found it
         ASSERT(0);
         err = VGAUTH_E_FAIL;
      } else {
         err = VGAUTH_E_OK;
      }
   } else {
      /*
       * If a bad/old ticket is revoked, we'll just pretend it worked
       * anyways.  This makes it hard to test, but its better from an
       * dev point of view, since otherwise the dev has to ignore
       * INVALID_TICKET, and assume the ticket got reaped or otherwise
       * flushed.
       */
      err = VGAUTH_E_OK;
   }

done:
   return err;
}


/*
 ******************************************************************************
 * ServiceLookupTicketOwner --                                           */ /**
 *
 * Returns the owner of a ticket.
 *
 * @param[in]   ticket        The ticket being validated.
 * @param[out]  userName      The owner of the ticket.  Must be g_free()d.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceLookupTicketOwner(const char *ticket,
                         char **userName)
{
   TicketInfo *info;
   VGAuthError err;

   ServiceReapOldTickets();
   info = g_hash_table_lookup(ticketTable, ticket);

   if (NULL != info) {
      *userName = g_strdup(info->userName);
      err = VGAUTH_E_OK;
   } else {
      err = VGAUTH_E_INVALID_TICKET;
   }

   return err;
}
 0707010000046E000081A400000000000000000000000168225505000031D2000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/serviceImpl/verify.c    /*********************************************************
 * Copyright (C) 2011-2020 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * @file verify.c
 *
 * Code to handle certficate verification.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "serviceInt.h"
#include "certverify.h"
#include "vmxlog.h"

/*
 ******************************************************************************
 * ServiceInitVerify --                                                 */ /**
 *
 * Inits the verification code.
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceInitVerify(void)
{

   CertVerify_Init();
   return SAML_Init();
}


/*
 ******************************************************************************
 * ServiceVerifyAndCheckTrustCertChainForSubject --                      */ /**
 *
 * @brief Checks the alias store for a username and subject to be sure certs are
 * trusted, and then verifies a certificate chain.
 *
 * Validates a certificate chain.  Verifies that all certs are properly
 * signed, in the proper date range, etc.  It is assumed that the first
 * element in the chain is the leaf cert being validated, with the rest
 * of the chain being certs that support that validation.  If userName
 * is not set, it uses the mapping file and the root cert of the chain
 * to find the user, and userNameOut is filled in with that user.
 *
 * #param[in]     numCerts      The number of certs in the chain.
 * @param[in]     pemCertChain  The chain of certificates to verify.
 * @param[in]     userName      The owner of the alias store to use to fetch any
 *                              extra certificates required for verification.
 * @param[in]     subj          The SAML subject to match.
 * @param[out]    userNameOut   If userName was NULL, this returns the
 *                              user from the mapping file associated with
 *                              the certs, otherwise points to a
 *                              heap-allocated copy of userName.
 * @param[out]    verifyAi      The ServiceAliasInfo associated with the
 *                              alias that was matched for @subj.
 *                              Should be freed with ServiceAliasFreeAliasInfo().
 *
 * @return VGAUTH_E_OK on success, VGAuthError on failure
 *
 ******************************************************************************
 */

VGAuthError
ServiceVerifyAndCheckTrustCertChainForSubject(int numCerts,
                                              const char **pemCertChain,
                                              const char *userName,
                                              ServiceSubject *subj,
                                              char **userNameOut,
                                              ServiceAliasInfo **verifyAi)
{
   VGAuthError err;
   int numMapped = 0;
   ServiceMappedAlias *maList = NULL;
   int numStoreCerts = 0;
   ServiceAlias *aList = NULL;
   int matchIdIdx = -1;
   int matchSiIdx = -1;
   ServiceAliasInfo *ai;
   char **trustedCerts = NULL;
   int numTrusted = 0;
   char **untrustedCerts = NULL;
   int numUntrusted = 0;
   char *queryUserName = NULL;
   char *leafCert = NULL;
   gboolean foundTrusted;
   int i;
   int j;
   int k;

   *userNameOut = NULL;
   *verifyAi = NULL;

   ASSERT(subj);
   ASSERT(numCerts > 0);

   /*
    * Dump the token cert chain for debugging purposes.
    */
   if (gVerboseLogging) {
      for (i = 0; i < numCerts; i++) {
         gchar *chainx509 = CertVerify_CertToX509String(pemCertChain[i]);
         Debug("%s: Token chain cert #%d:\n%s", __FUNCTION__, i, chainx509);
         g_free(chainx509);
      }
   }

   /*
    * If we have no userName, look through the mapping file for a match
    * from the cert chain.
    */
   if (NULL == userName || *userName == '\0') {
      err = ServiceAliasQueryMappedAliases(&numMapped, &maList);

      if (VGAUTH_E_OK != err) {
         goto done;
      }
      if (0 == numMapped) {
         /*
          * No username, no mapped certs, no chance.
          */
         Warning("%s: no mapping entries or specified userName\n",
                 __FUNCTION__);
         VMXLog_Log(VMXLOG_LEVEL_WARNING,
                    "%s: no mapping entries or specified userName\n",
                    __FUNCTION__);
         err = VGAUTH_E_AUTHENTICATION_DENIED;
         goto done;
      }

      /*
       * Search for a match in the mapped store.
       */
      for (i = 0; i < numCerts; i++) {
         for (j = 0; j < numMapped; j++) {
            if (ServiceComparePEMCerts(pemCertChain[i], maList[j].pemCert)) {
               /*
                * Make sure we don't have multiple matches with different users.
                * Two possible scenarios that can trigger this:
                * - the mapping file could be inconsistent
                * - the chain coming in could have more than one cert that
                *   exists in the mapping file, belonging to different users
                */
               if ((NULL != queryUserName) &&
                   g_strcmp0(queryUserName, maList[j].userName) != 0) {
                  Warning("%s: found more than one user in map file chain\n",
                          __FUNCTION__);
                  VMXLog_Log(VMXLOG_LEVEL_WARNING,
                             "%s: found more than one user in map file chain\n",
                          __FUNCTION__);
                  err = VGAUTH_E_MULTIPLE_MAPPINGS;
                  goto done;
               }

               for (k = 0; k < maList[j].num; k++) {
                  if ((maList[j].subjects[k].type == SUBJECT_TYPE_ANY) ||
                      ServiceAliasIsSubjectEqual(subj->type,
                                                 maList[j].subjects[k].type,
                                                 subj->name,
                                                 maList[j].subjects[k].name)) {
                     queryUserName = g_strdup(maList[j].userName);
                     break;
                  }
               }

            }
         }
      }
      /*
       * Subject went unmatched, so fail.
       */
      if (NULL == queryUserName) {
         Warning("%s: no matching cert and subject found in mapping file\n",
                 __FUNCTION__);
         VMXLog_Log(VMXLOG_LEVEL_WARNING,
                    "%s: no matching cert and subject found in mapping file\n",
                    __FUNCTION__);
         err = VGAUTH_E_AUTHENTICATION_DENIED;
         goto done;
      }
   } else {
      queryUserName = g_strdup(userName);
   }

   /*
    * Make sure the user exists -- Query supports deleted users
    * to allow for cleanup.
    */
   if (!UsercheckUserExists(queryUserName)) {
      Warning("%s: User '%s' doesn't exist\n", __FUNCTION__, queryUserName);
      VMXLog_Log(VMXLOG_LEVEL_WARNING,
                 "%s: User doesn't exist\n", __FUNCTION__);
      err = VGAUTH_E_AUTHENTICATION_DENIED;
      goto done;
   }

   err = ServiceAliasQueryAliases(queryUserName, &numStoreCerts, &aList);
   if (VGAUTH_E_OK != err) {
      goto done;
   }

   /*
    * Dump the store cert chain for debugging purposes.
    */
   if (gVerboseLogging) {
      Debug("%s: %d certs in store for user %s\n",  __FUNCTION__,
            numStoreCerts, queryUserName);
      for (i = 0; i < numStoreCerts; i++) {
         gchar *storex509 = CertVerify_CertToX509String(aList[i].pemCert);

         Debug("%s: Store chain cert #%d:\n%s", __FUNCTION__, i, storex509);
         g_free(storex509);
      }
   }


   /*
    * Split the incoming chain into trusted and untrusted certs
    */
   for (i = 0; i < numCerts; i++) {
      int foundAnyIdx;
      int foundSubjectIdx;

      foundTrusted = FALSE;
      for (j = 0; j < numStoreCerts; j++) {
         if (ServiceComparePEMCerts(pemCertChain[i], aList[j].pemCert)) {
            /*
             * Remember the root cert, so we can return its AliasInfo
             * if all checks out.
             */
            matchIdIdx = j;
            foundAnyIdx = -1;
            foundSubjectIdx = -1;

            for (k = 0; k < aList[j].num; k++) {
               if (aList[j].infos[k].type == SUBJECT_TYPE_ANY) {
                  foundAnyIdx = k;
               } else if (ServiceAliasIsSubjectEqual(subj->type,
                                                     aList[j].infos[k].type,
                                                     subj->name,
                                                     aList[j].infos[k].name)) {
                  foundSubjectIdx = k;
               }
            }
            if ((foundSubjectIdx >= 0) || (foundAnyIdx >= 0)) {
               numTrusted++;
               trustedCerts = g_realloc_n(trustedCerts,
                                          numTrusted, sizeof(*trustedCerts));
               trustedCerts[numTrusted - 1] = g_strdup(pemCertChain[i]);
               foundTrusted = TRUE;
               /*
                * Remember the matching ai, so we can return its comment
                * if all checks out.  Note that a specific subject match takes
                * precendence over an ANY match.
                */
               matchSiIdx = (foundSubjectIdx >= 0) ?
                  foundSubjectIdx : foundAnyIdx;
            }
         }
      }
      if (!foundTrusted) {
         numUntrusted++;
         untrustedCerts = g_realloc_n(untrustedCerts,
                                      numUntrusted, sizeof(*untrustedCerts));
         untrustedCerts[numUntrusted - 1] = g_strdup(pemCertChain[i]);
      }
   }

   /*
    * Make sure we have at least one trusted cert.
    */
   if (numTrusted == 0) {
      err = VGAUTH_E_AUTHENTICATION_DENIED;
      Warning("%s: No trusted certs in chain\n", __FUNCTION__);
      VMXLog_Log(VMXLOG_LEVEL_WARNING,
                 "%s: No trusted certs in chain\n", __FUNCTION__);
      goto done;
   }

   /*
    * Pull out the leaf -- it should be either the first trusted
    * or untrusted cert
    */
   if (g_strcmp0(pemCertChain[0], trustedCerts[0]) == 0) {
      numTrusted--;
      leafCert = trustedCerts[0];
      memmove(trustedCerts, &(trustedCerts[1]), sizeof(*trustedCerts) * numTrusted);
   /* coverity[var_deref_op] */
   } else if (g_strcmp0(pemCertChain[0], untrustedCerts[0]) == 0) {
      numUntrusted--;
      leafCert = untrustedCerts[0];
      memmove(untrustedCerts, &(untrustedCerts[1]), sizeof(*untrustedCerts) * numUntrusted);
   } else {
      ASSERT(0);
   }

   err = CertVerify_CertChain(leafCert,
                              numUntrusted,
                              (const char **) untrustedCerts,
                              numTrusted,
                              (const char **) trustedCerts);
   if (VGAUTH_E_OK != err) {
      VMXLog_Log(VMXLOG_LEVEL_WARNING,
                 "%s: cert chain validation failed\n", __FUNCTION__);
      goto done;
   }

   Debug("%s: cert chain successfully validated", __FUNCTION__);

   /*
    * Save off AliasInfo.
    *
    * XXX unclear on what should be done here if we have multiple
    * trusted certs in the alias store.  For now, use the root-most
    * (last found).
    */
   ai = g_malloc0(sizeof(ServiceAliasInfo));
   ASSERT(matchIdIdx >= 0 && matchSiIdx >= 0);
   ai->type = aList[matchIdIdx].infos[matchSiIdx].type;
   ai->name = g_strdup(aList[matchIdIdx].infos[matchSiIdx].name);
   ai->comment = g_strdup(aList[matchIdIdx].infos[matchSiIdx].comment);
   *verifyAi = ai;
   *userNameOut = queryUserName;
   queryUserName = NULL;

done:
   ServiceAliasFreeMappedAliasList(numMapped, maList);

   ServiceAliasFreeAliasList(numStoreCerts, aList);

   for (i = 0; i < numTrusted; i++) {
      g_free(trustedCerts[i]);
   }
   g_free(trustedCerts);

   for (i = 0; i < numUntrusted; i++) {
      g_free(untrustedCerts[i]);
   }
   g_free(untrustedCerts);

   g_free(leafCert);
   g_free(queryUserName);

   return err;
}
  0707010000046F000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/test    07070100000470000081A400000000000000000000000168225505000007D1000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/test/Makefile.am    ################################################################################
### Copyright (C) 2014-2017 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-vgauth-smoketest

vmware_vgauth_smoketest_SOURCES =
vmware_vgauth_smoketest_SOURCES += main.c

vmware_vgauth_smoketest_CPPFLAGS =
vmware_vgauth_smoketest_CPPFLAGS += -DVMTOOLS_USE_GLIB
vmware_vgauth_smoketest_CPPFLAGS += @GLIB2_CPPFLAGS@
vmware_vgauth_smoketest_CPPFLAGS += -I$(top_srcdir)/libvmtools
vmware_vgauth_smoketest_CPPFLAGS += -I$(top_srcdir)/vgauth/public
vmware_vgauth_smoketest_CPPFLAGS += -I$(top_srcdir)/vgauth/common

vmware_vgauth_smoketest_LDADD =
vmware_vgauth_smoketest_LDADD += @VMTOOLS_LIBS@
vmware_vgauth_smoketest_LDADD += @GLIB2_LIBS@
vmware_vgauth_smoketest_LDADD += @GTHREAD_LIBS@
vmware_vgauth_smoketest_LDADD += @SSL_LIBS@
vmware_vgauth_smoketest_LDADD += ../lib/libvgauth.la

if HAVE_ICU
   vmware_vgauth_smoketest_LDADD += @ICU_LIBS@
   vmware_vgauth_smoketest_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS)     \
                            $(LIBTOOLFLAGS) --mode=link $(CXX)       \
                            $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
                            $(LDFLAGS) -o $@
else
   vmware_vgauth_smoketest_LINK = $(LINK)
endif
   07070100000471000081A400000000000000000000000168225505000005CD000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vgauth/test/README.txt  *    The VGAuthService smoke test.
 *
 *    This does some very basic vgauth effort to verify that it
 *    properly validates SAML tokens.  This verifies that vgauth
 *    was built and installed properly.
 *
 *    This uses a built-in SAML token with a 1000 year lifetime,
 *    to avoid any issues with the XML security library on the signing
 *    side.
 *
 *    This test must be run as root on the same system as VGAuthService.
 *    This test should only be run in a test environment, since it
 *    will clear out any existing aliases.
 *
 *    To use:
 *    - start VGAuthService (/usr/bin/VGAuithService -s )
 *    - run the smoketest
 *
 *
 *    Steps:
 *    - clear out any existing aliases
 *    - add an alias using the built-in cert
 *    - validate the SAML token
 *
 *    Possible reasons for failure:
 *    - VGAuthService wasn't started
 *    - VGAuthService failed to start up properly
 *       - unable to find support files (schemas)
 *       - unable to access various files/directories
 *       - parts of xmlsec1 missing (openssl crypto lib missing)
 *       - SAML verification failed to init (xmlsec1 build issues)
 *    - token fails to validate
 *       - this test was run after 12/18/3015
 *       - libxmlsec1 may have been built using a different size type than
 *         its callers, which can make some data structures a different size
 *         than in the library. See xmlSecSize changelog in
 *         https://www.aleksey.com/xmlsec/news.html
   07070100000472000081A400000000000000000000000168225505000043C4000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vgauth/test/main.c /*********************************************************
 * Copyright (C) 2016-2017 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/**
 * @file main.c
 *
 *    The VGAuthService smoke test.
 *
 *    This does some very basic vgauth effort to verify that it
 *    properly validates SAML tokens.
 *
 *    This uses a built-in SAML token with a 1000 year lifetime,
 *    to avoid any issues with the XML security library on the signing
 *    side.
 *
 *    This test must be run as root on the same system as VGAuthService.
 *    This test should only be run in a test environment, since it
 *    will clear out any existing aliases.
 *
 *    Steps:
 *    - clear out any existing aliases
 *    - add an alias using the built-in cert
 *    - validate the SAML token
 *
 *    Possible reasons for failure:
 *    - VGAuthService wasn't started
 *    - VGAuthService failed to start up properly
 *       - unable to find support files (schemas)
 *       - unable to access various files/directories
 *       - parts of xmlsec1 missing (openssl crypto lib missing)
 *       - SAML verification failed to init (xmlsec1 build issues)
 *    - token fails to validate
 *       - this test was run after 12/18/3015
 *       - xmlsec1-config lies about how xmlsec1 was built
 *          some packages leave out -DXMLSEC_NO_SIZE_T,
 *          which can make some data structures a different size
 *          than in the library
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#include <errno.h>
#endif
#include <glib.h>

#include "VGAuthBasicDefs.h"
#include "VGAuthAlias.h"
#include "VGAuthAuthentication.h"
#include "VGAuthCommon.h"
#include "VGAuthError.h"
#include "VGAuthLog.h"
#include "VGAuthUtil.h"

static gchar *appName;

#define ALIAS_USER_NAME    "root"
#define SUBJECT_NAME       "SmokeSubject"
#define COMMENT            "Smoke comment"

#define MAKE_PEM_FROM_BASE64(base64) \
   "-----BEGIN CERTIFICATE-----\n"   \
   base64                            \
   "-----END CERTIFICATE-----\n"
/*
 * Self-signed smoketest cert.
 *
 * Not Before: Aug 16 22:29:21 2016 GMT
 * Not After : Dec 18 22:29:21 3015 GMT
 *
 */
#define SMOKETEST_CERT_BASE64 \
"MIIDZTCCAk2gAwIBAgIJALuLD4JnajhkMA0GCSqGSIb3DQEBBQUAMEgxCzAJBgNV\n" \
"BAYTAlhYMRMwEQYDVQQIDApTbW9rZVN0YXRlMRIwEAYDVQQHDAlTbW9rZUNpdHkx\n" \
"EDAOBgNVBAoMB1Ntb2tlQ28wIBcNMTYwODE2MjIyOTIxWhgPMzAxNTEyMTgyMjI5\n" \
"MjFaMEgxCzAJBgNVBAYTAlhYMRMwEQYDVQQIDApTbW9rZVN0YXRlMRIwEAYDVQQH\n" \
"DAlTbW9rZUNpdHkxEDAOBgNVBAoMB1Ntb2tlQ28wggEiMA0GCSqGSIb3DQEBAQUA\n" \
"A4IBDwAwggEKAoIBAQDcRD+tNhOwxtEDDhnwQ94Qn+eEI4Nh6zXBP5CfnbMIHYo0\n" \
"1tzxLWOaJsN8/WoHy2cbeQkXGiGHpzuJIndhkL3XZpRdKTLIw95EVJkChYJi8ZUl\n" \
"LnaLIPsG1bpVOSuf+0qGcRyoItXBlvvYMZ5JAdUncHYnJ2NAbvqZVIH0sSafupzv\n" \
"w5txeQ7ufIcCzHYzSIFiX82CVMq/xuSQULVAZXoIfjNqMlwhYQn/EiSFb+y3kUa+\n" \
"xDzNWNyzv4H+7/6C+qz2KxTUbBEKT/lsuIVYVJ5R+1vZ2MnGkqsz8ELttXk0tAK+\n" \
"pfUAg7ugOhpF2rdvNOt4874Kkdj8a2It/JKqN3kBAgMBAAGjUDBOMB0GA1UdDgQW\n" \
"BBR9OuZuejgPVz64LWnhOfO1d6u0dTAfBgNVHSMEGDAWgBR9OuZuejgPVz64LWnh\n" \
"OfO1d6u0dTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCZ91zS4zKZ\n" \
"uQv5rXn/zJtJ7d1pWnywh26n5bBlNQS3N7nAQPG5fvK2MB2rztE45Anq056YcYL7\n" \
"TTDDDPz9dGndThGyusHbzO/lV7UHCQUzMr0joItxrQoX7/4OPBMyARBLAE5wRa85\n" \
"uXm0D/Z6AAKJLz30yaQ+kBwTlIVhJFFhQv2zGZ3vB7CN0zNAZ/4s6lo+ejHj4Dhc\n" \
"PFsUDwWnqqp9iqZMX3vxp3BEuxUsSzXtuwBytvWcS/6i1LUl41obD4RNxZ3llQTN\n" \
"+uXVUFTTt0NgCbMJq5G8Nz4ziyjgxT94tB/AMwRmJzPSvew3vGMFhF7Fm0Z3Oxn5\n" \
"kWMiikdSCME8\n"


const char *smoketestPEMCert = MAKE_PEM_FROM_BASE64(SMOKETEST_CERT_BASE64);

const char *smoketestBase64Cert = SMOKETEST_CERT_BASE64;

/*
 * Token
 */

static const gchar *token =
"<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_b07b804c-7c29-ea16-7300-4f3d6f7928ac\" IssueInstant=\"2004-12-05T09:22:05Z\" Version=\"2.0\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
"<saml:Issuer>https://sso.eng.vmware.com</saml:Issuer>\n"
"<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n"
"<ds:SignedInfo>\n"
"<ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\n"
"<ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"/>\n"
"<ds:Reference URI=\"#_b07b804c-7c29-ea16-7300-4f3d6f7928ac\">\n"
"<ds:Transforms>\n"
"<ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\n"
"</ds:Transforms>\n"
"<ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"/>\n"
"<ds:DigestValue>w0kRFhuuzMenlkrfZttAweUTHcyRsQtHRn2L01Rmsa4=</ds:DigestValue>\n"
"</ds:Reference>\n"
"</ds:SignedInfo>\n"
"<ds:SignatureValue>B54Qp2fO+YyMPK/6gYzCDigLZdOO3vEu8getiKB4a8s14ySoH6aQtq/RjgNSW8jr\n"
"yNox9NRxc8ipBXC/noF8UBw6sUPpxsifMabWdMb9XvoZKufdDDrYKxQ4LwGjKF9y\n"
"i2dO/Saw8kZ8CQKYvbNt0KkMqbQZNtDtM6AVAobWXuZioYyphQSJ6YZVwJnLh6wv\n"
"sI0DgBqjFI91pID4n4N4SZq+tr2u8wcepnSIcmFNZ+BVdy7TKnjqTnjaDCG0Y0Uk\n"
"P5wtWOAVpqTGMmTDpVwAtKfs089tDw/doGds+FIAXd6oR2eECo9j7SOm0i0V9pEn\n"
"/nIe1Di7JNVJfl9V+g/bfA==</ds:SignatureValue>\n"
"<ds:KeyInfo>\n"
"<ds:X509Data>\n"
"<ds:X509Certificate>MIIDZTCCAk2gAwIBAgIJALuLD4JnajhkMA0GCSqGSIb3DQEBBQUAMEgxCzAJBgNV\n"
"BAYTAlhYMRMwEQYDVQQIDApTbW9rZVN0YXRlMRIwEAYDVQQHDAlTbW9rZUNpdHkx\n"
"EDAOBgNVBAoMB1Ntb2tlQ28wIBcNMTYwODE2MjIyOTIxWhgPMzAxNTEyMTgyMjI5\n"
"MjFaMEgxCzAJBgNVBAYTAlhYMRMwEQYDVQQIDApTbW9rZVN0YXRlMRIwEAYDVQQH\n"
"DAlTbW9rZUNpdHkxEDAOBgNVBAoMB1Ntb2tlQ28wggEiMA0GCSqGSIb3DQEBAQUA\n"
"A4IBDwAwggEKAoIBAQDcRD+tNhOwxtEDDhnwQ94Qn+eEI4Nh6zXBP5CfnbMIHYo0\n"
"1tzxLWOaJsN8/WoHy2cbeQkXGiGHpzuJIndhkL3XZpRdKTLIw95EVJkChYJi8ZUl\n"
"LnaLIPsG1bpVOSuf+0qGcRyoItXBlvvYMZ5JAdUncHYnJ2NAbvqZVIH0sSafupzv\n"
"w5txeQ7ufIcCzHYzSIFiX82CVMq/xuSQULVAZXoIfjNqMlwhYQn/EiSFb+y3kUa+\n"
"xDzNWNyzv4H+7/6C+qz2KxTUbBEKT/lsuIVYVJ5R+1vZ2MnGkqsz8ELttXk0tAK+\n"
"pfUAg7ugOhpF2rdvNOt4874Kkdj8a2It/JKqN3kBAgMBAAGjUDBOMB0GA1UdDgQW\n"
"BBR9OuZuejgPVz64LWnhOfO1d6u0dTAfBgNVHSMEGDAWgBR9OuZuejgPVz64LWnh\n"
"OfO1d6u0dTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCZ91zS4zKZ\n"
"uQv5rXn/zJtJ7d1pWnywh26n5bBlNQS3N7nAQPG5fvK2MB2rztE45Anq056YcYL7\n"
"TTDDDPz9dGndThGyusHbzO/lV7UHCQUzMr0joItxrQoX7/4OPBMyARBLAE5wRa85\n"
"uXm0D/Z6AAKJLz30yaQ+kBwTlIVhJFFhQv2zGZ3vB7CN0zNAZ/4s6lo+ejHj4Dhc\n"
"PFsUDwWnqqp9iqZMX3vxp3BEuxUsSzXtuwBytvWcS/6i1LUl41obD4RNxZ3llQTN\n"
"+uXVUFTTt0NgCbMJq5G8Nz4ziyjgxT94tB/AMwRmJzPSvew3vGMFhF7Fm0Z3Oxn5\n"
"kWMiikdSCME8\n"
"</ds:X509Certificate>\n"
"</ds:X509Data>\n"
"</ds:KeyInfo>\n"
"</ds:Signature><saml:Subject>\n"
"<saml:NameID Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:transient\">SmokeSubject</saml:NameID>\n"
"<saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\">\n"
"<saml:SubjectConfirmationData NotOnOrAfter=\"2116-07-23T23:29:34.677406Z\"/>\n"
"</saml:SubjectConfirmation>\n"
"</saml:Subject>\n"
"<saml:Conditions NotBefore=\"2016-08-16T23:29:34.677402Z\" NotOnOrAfter=\"2116-07-23T23:29:34.677229Z\">\n"
"<saml:AudienceRestriction>\n"
"<saml:Audience>https://sp.example.com/SAML2</saml:Audience></saml:AudienceRestriction>\n"
"</saml:Conditions>\n"
"<saml:AuthnStatement AuthnInstant=\"2004-12-05T09:22:00Z\" SessionIndex=\"b07b804c-7c29-ea16-7300-4f3d6f7928ac\">\n"
"<saml:AuthnContext>\n"
"<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>\n"
"</saml:AuthnContext>\n"
"</saml:AuthnStatement>\n"
"<saml:AttributeStatement>\n"
"<saml:Attribute FriendlyName=\"eduPersonAffiliation\" Name=\"urn:oid:1.3.6.1.4.1.5923.1.1.1.1\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" xmlns:x500=\"urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500\" x500:Encoding=\"LDAP\">\n"
"<saml:AttributeValue xsi:type=\"xs:string\">member</saml:AttributeValue>\n"
"<saml:AttributeValue xsi:type=\"xs:string\">staff</saml:AttributeValue>\n"
"</saml:Attribute>\n"
"</saml:AttributeStatement>\n"
"</saml:Assertion>\n"
;


/*
 ******************************************************************************
 * Usage --                                                              */ /**
 *
 * Usage message for smoke test
 *
 ******************************************************************************
 */

static void
Usage(void)
{
   fprintf(stderr, "Usage: %s\n", appName);
   exit(-1);
}


/*
 ******************************************************************************
 * Log --                                                                */ /**
 *
 * Error message logging function.
 *
 * @param[in]     logDomain   The glib logging domain, which is set by the
 *                            various glib components and vgauth itself.
 * @param[in]     logLevel    The severity of the message.
 * @param[in]     msg         The error message.
 * @param[in]     userData    Any userData specified in the call to
 *                            VGAuth_SetLogHandler() (unused)
 *
 ******************************************************************************
 */

static void
Log(const char *logDomain,
    int logLevel,
    const char *msg,
    void *userData)
{
   g_printerr("%s[%d]: %s", logDomain, logLevel, msg);
}


/*
 ******************************************************************************
 * CleanAliases --                                                       */ /**
 *
 * Clears out the alias store for the given user, and the map file.
 *
 * @param[in]  ctx        VGAuth context
 * @param[in]  userName   The user whose alias store is to be cleaned.
 *
 * @return A SAMl token on success, NULL failure.
 *
 ******************************************************************************
 */

static VGAuthError
CleanAliases(VGAuthContext *ctx,
             const gchar *userName)
{
   VGAuthError err;
   int num;
   int i;
   VGAuthMappedAlias *maList;
   VGAuthUserAlias *uaList;

   // clear out mapped aliaes
   err = VGAuth_QueryMappedAliases(ctx, 0, NULL, &num, &maList);
   if (err != VGAUTH_E_OK) {
      g_printerr("VGAuth_QueryMappedAliases() failed "VGAUTHERR_FMT64"\n",
                 err);
      return err;
   }

   for (i = 0; i < num; i++) {
      err = VGAuth_RemoveAliasByCert(ctx, maList[i].userName,
                                     maList[i].pemCert, 0, NULL);
      if (err != VGAUTH_E_OK) {
         g_printerr("VGAuth_RemoveAliasByCert() failed "VGAUTHERR_FMT64"\n",
                    err);
         return err;
      }
   }
   VGAuth_FreeMappedAliasList(num, maList);
   err = VGAuth_QueryMappedAliases(ctx, 0, NULL, &num, &maList);
   if (err != VGAUTH_E_OK || num != 0) {
         g_printerr("sitll have mapped aliases or "
                    "VGAuth_QueryMappedAliases() failed "VGAUTHERR_FMT64"\n",
                    err);
         return err;
   }

   // clear out user aliases
   err = VGAuth_QueryUserAliases(ctx, userName, 0, NULL,
                                 &num, &uaList);
   if (err != VGAUTH_E_OK) {
      g_printerr("VGAuth_QueryUserAliases() failed "VGAUTHERR_FMT64"\n",
                 err);
      return err;
   }
   for (i = 0; i < num; i++) {
      err = VGAuth_RemoveAliasByCert(ctx, userName,
                                     uaList[i].pemCert, 0, NULL);
      if (err != VGAUTH_E_OK) {
         g_printerr("VGAuth_RemoveAliasByCert() failed "VGAUTHERR_FMT64"\n",
                    err);
         return err;
      }
   }

   err = VGAuth_QueryUserAliases(ctx, userName, 0, NULL,
                                 &num, &uaList);
   if (err != VGAUTH_E_OK || num != 0) {
      g_printerr("aliases left or VGAuth_QueryUserAliases() failed "
                 VGAUTHERR_FMT64"\n",
                 err);
      return err;
   }

   return err;
}


/*
 ******************************************************************************
 * AddAlias --                                                           */ /**
 *
 * Generates a SAMl token with a given Subject.
 *
 * @param[in]  ctx        VGAuth context
 * @param[in]  cert       The certificate for the alias.
 * @param[in]  user       The user whose alias store is to be updated.
 * @param[in]  subject    The Subject for the alias.
 * @param[in]  comment    The Comment for the alias.
 *
 * @return A SAMl token on success, NULL failure.
 *
 ******************************************************************************
 */

static VGAuthError
AddAlias(VGAuthContext *ctx,
         const gchar *cert,
         const gchar *user,
         const gchar *subject,
         const gchar *comment)
{
   VGAuthError err;
   VGAuthAliasInfo ai;

   ai.subject.type = VGAUTH_SUBJECT_NAMED;
   ai.subject.val.name = (gchar *)subject;
   ai.comment = (gchar *)comment;
   err = VGAuth_AddAlias(ctx, user, FALSE, cert, &ai, 0, NULL);
   if (err != VGAUTH_E_OK) {
      g_printerr("VGAuth_AddAlias() failed "VGAUTHERR_FMT64"\n",
                 err);
      return err;
   }

   return err;
}


/*
 ******************************************************************************
 * ValidateToken --                                                      */ /**
 *
 * Validates a SAMl token.
 *
 * @param[in]  ctx        VGAuth context
 * @param[in]  userName   The user associated with the token.
 * @param[in]  token      A SAML token.
 *
 * @return VGAUTH_E_OK on success, an error on failure.
 *
 ******************************************************************************
 */

static VGAuthError
ValidateToken(VGAuthContext *ctx,
              const gchar *userName,
              const gchar *token)
{
   VGAuthError err;
   VGAuthExtraParams extraParams[1];
   VGAuthUserHandle *userHandle = NULL;
   VGAuthAliasInfo *retAi = NULL;
   char *retSamlSubject = NULL;
   char *retUserName = NULL;

   /*
    * Use info-only -- its all we need.
    */
   extraParams[0].name = VGAUTH_PARAM_VALIDATE_INFO_ONLY;
   extraParams[0].value = VGAUTH_PARAM_VALUE_TRUE;
   err = VGAuth_ValidateSamlBearerToken(ctx,
                                        token,
                                        userName,
                                        1,
                                        extraParams,
                                        &userHandle);
   if (VGAUTH_E_OK != err) {
      g_printerr("Failed to validate token");
      goto done;
   }

   err = VGAuth_UserHandleUsername(ctx, userHandle, &retUserName);
   if (VGAUTH_E_OK != err) {
      g_printerr("Failed to get username off handle");
      goto done;
   }
   err = VGAuth_UserHandleSamlData(ctx,
                                   userHandle,
                                   &retSamlSubject,
                                   &retAi);
   if (VGAUTH_E_OK != err) {
      g_printerr("Failed to get SAML subject data off handle");
      goto done;
   }
   printf("Token details: user: %s (expected %s) subject: %s (expected %s)\n",
          retUserName, ALIAS_USER_NAME, retSamlSubject, SUBJECT_NAME);

done:
   VGAuth_UserHandleFree(userHandle);
   g_free(retUserName);
   g_free(retSamlSubject);
   VGAuth_FreeAliasInfo(retAi);
   return err;
}


/*
 ******************************************************************************
 * main --                                                               */ /**
 *
 * Main entry point.
 *
 * @param[in]  argc        Number of command line arguments.
 * @param[in]  argv        The command line arguments.
 *
 * @return 0 if the operation ran successfully, -1 if there was an error during
 *         execution.
 *
 ******************************************************************************
 */

int
main(int argc,
     char *argv[])
{
   VGAuthError err;
   VGAuthContext *ctx;

   appName = g_path_get_basename(argv[0]);
   if (argc != 1) {
      Usage();
   }

   VGAuth_SetLogHandler(Log, NULL, 0, NULL);

   err = VGAuth_Init(appName, 0, NULL, &ctx);
   if (VGAUTH_E_OK != err) {
      g_printerr("Failed to init VGAuth");
      return -1;
   }

   // make sure we start with a clean slate
   err = CleanAliases(ctx, ALIAS_USER_NAME);
   if (VGAUTH_E_OK != err) {
      g_printerr("Failed to clean alias store");
      return -1;
   }

   err = AddAlias(ctx, smoketestPEMCert,
                  ALIAS_USER_NAME, SUBJECT_NAME, COMMENT);
   if (VGAUTH_E_OK != err) {
      g_printerr("Failed to add alias");
      return -1;
   }

   err = ValidateToken(ctx, ALIAS_USER_NAME, token);
   if (VGAUTH_E_OK != err) {
      g_printerr("Failed to validate SAML token");
      return -1;
   }

   printf("PASSED!\n");

   // make sure we end with a clean slate
   err = CleanAliases(ctx, ALIAS_USER_NAME);
   if (VGAUTH_E_OK != err) {
      g_printerr("Failed to clean alias store");
      return -1;
   }

   VGAuth_Shutdown(ctx);
   g_free(appName);
   return 0;
}
07070100000473000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/vgauthImport   07070100000474000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vgauthImport/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000475000081A40000000000000000000000016822550500000923000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/vgauthImport/Makefile.am   ################################################################################
### Copyright (C) 2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################
bin_PROGRAMS = vmware-alias-import

vmware_alias_import_LDADD =
vmware_alias_import_LDADD += @GLIB2_LIBS@
vmware_alias_import_LDADD += ../lib/rpcOut/libRpcOut.la
vmware_alias_import_LDADD += ../lib/message/libMessage.la
vmware_alias_import_LDADD += ../lib/backdoor/libBackdoor.la
vmware_alias_import_LDADD += ../lib/dataMap/libDataMap.la
vmware_alias_import_LDADD += ../lib/hashMap/libHashMap.la
vmware_alias_import_LDADD += ../lib/err/libErr.la
vmware_alias_import_LDADD += ../lib/string/libString.la
vmware_alias_import_LDADD += ../lib/vmCheck/libVmCheck.la
vmware_alias_import_LDADD += ../lib/vmSignal/libVmSignal.la
vmware_alias_import_LDADD += ../lib/misc/libMisc.la
vmware_alias_import_LDADD += ../vgauth/lib/libvgauth.la
vmware_alias_import_LDADD += ../lib/stubs/libStubs.la

vmware_alias_import_CPPFLAGS =
vmware_alias_import_CPPFLAGS += @GLIB2_CPPFLAGS@
vmware_alias_import_CPPFLAGS += -DUSE_RPCI_ONLY
vmware_alias_import_CPPFLAGS += -I$(top_srcdir)/vgauth/public

vmware_alias_import_rpcchandir = $(top_srcdir)/lib/rpcChannel

vmware_alias_import_SOURCES =
vmware_alias_import_SOURCES += vgauthImport.c
vmware_alias_import_SOURCES += $(vmware_alias_import_rpcchandir)/bdoorChannel.c
vmware_alias_import_SOURCES += $(vmware_alias_import_rpcchandir)/rpcChannel.c
if HAVE_VSOCK
vmware_alias_import_SOURCES += $(vmware_alias_import_rpcchandir)/vsockChannel.c
vmware_alias_import_SOURCES += $(vmware_alias_import_rpcchandir)/simpleSocket.c
endif
 07070100000476000081A40000000000000000000000016822550500003DC9000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/vgauthImport/vgauthImport.c    /*********************************************************
 * Copyright (c) 2012,2018-2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vgauthImport.c --
 *
 *      Command line tool to import vgauth config data from the
 *      namespace DB and apply it to the running vgauth service.
 */

#include <stdio.h>
#include <stdlib.h>
#include <glib.h>

#include "vmware.h"
#include "vmcheck.h"
#include "util.h"
#include "dynbuf.h"

#include "VGAuthCommon.h"
#include "VGAuthError.h"
#include "VGAuthAuthentication.h"
#include "VGAuthAlias.h"
#include "vmware/tools/guestrpc.h"
#ifdef _WIN32
#include "vmware/tools/win32util.h"
#endif

#define  VGAUTHIMPORT_APP_NAME "vgauthImport"

#define NOT_VMWARE_ERROR "Failed sending message to VMware.\n"

static Bool ProcessNamespace(char *namespace);

#define NSDB_PRIV_GET_VALUES_CMD "namespace-priv-get-values"
#define NSDB_PRIV_SET_KEYS_CMD "namespace-priv-set-keys"

/*
 * The namspaceDB key names used.
 */

/*
 * These 5 are set by the solution on the vSphere side.
 */
#define KEY_NAME_CERT "certificate"
#define KEY_NAME_SUBJECT "subject"
#define KEY_NAME_COMMENT "comment"
#define KEY_NAME_USER "guestUsername"
#define KEY_NAME_ADDMAPPED "addMapped"

/*
 * This is set by this tool to allow the solution to know when
 * the data was imported.
 */
#define KEY_NAME_IMPORTED "imported"


/*
 * The underlying APIs will return values in the order they're
 * queried.  This structure simplifies deserializing the answers.
 */
typedef struct {
   const char *keyName;
   char *val;
} KeyNameValue;

static KeyNameValue nameVals[] = {
   { KEY_NAME_CERT, NULL},
   { KEY_NAME_SUBJECT, NULL},
   { KEY_NAME_COMMENT, NULL},
   { KEY_NAME_USER, NULL},
   { KEY_NAME_ADDMAPPED, NULL},
};


static gboolean verboseLogFlag = FALSE;
static gchar *gAppName = NULL;

static const gchar *usageMsg = "namespaceID";
static const gchar *summaryMsg = "";

/*
 ******************************************************************************
 * PrintUsage --                                                         */ /**
 *
 * Prints the usage mesage.
 *
 ******************************************************************************
 */

static void
PrintUsage(GOptionContext *optCtx)
{
   gchar *usage;

   usage = g_option_context_get_help(optCtx, TRUE, NULL);
   g_printerr("%s", usage);
   g_free(usage);
}


/*
 ******************************************************************************
 * vgauthLog --                                                          */ /**
 *
 * VGAuth error log handler.  Catches any noise generated by VGAuth
 * lib or glib underneath it.
 *
 * @param[in]  logDomain   The source domain of the error message.
 * @param[in]  loglevl     The priority level of the error.
 * @param[in]  msg         The error message.
 * @param[in]  userData    Any user-supplied data.
 *
 ******************************************************************************
 */

static void
vgauthLog(const char *logDomain,
          int loglevel,
          const char *msg,
          void *userData)
{
   /*
    * Ignore debug and message levels.
    */
   if (loglevel &
       (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) {
      fprintf(stderr, "VGAuth error: [%s] %s\n", logDomain, msg);
   }
}


/*
 ******************************************************************************
 * ProcessQueryReply --                                                  */ /**
 *
 * Processes the return data from the namespace-get call.
 *
 * Parses the data coming back from the namespace-get-values call, and
 * updates the VGAuth alias store with the information.
 *
 * @param[in]  result    The raw data.  UTF-8 bytestream with embedded NULs.
 * @param[in]  ersultLen The length of the data.
 *
 * @return TRUE if successful, FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
ProcessQueryReply(char *result,
                  size_t resultLen)
{
   char *p = result;
   VGAuthAliasInfo ai;
   char *pemCert = NULL;
   char *userName = NULL;
   Bool addMapped = FALSE;
   Bool status = TRUE;
   VGAuthError vgErr;
   VGAuthContext *ctx = NULL;
   int i;
   int numMapped = 0;
   VGAuthMappedAlias *maList = NULL;
   Bool alreadyExists = FALSE;

   ASSERT(result);
   ai.subject.type = VGAUTH_SUBJECT_NAMED;
   ai.comment = "";
   ai.subject.val.name = NULL;

   /*
    * Deserialize the results.
    */
   for (i = 0; i < ARRAYSIZE(nameVals); i++) {
      nameVals[i].val = Util_SafeStrdup(p);
      p += strlen(p) + 1;
      ASSERT(p <= (result + resultLen));
   }

   /*
    * Rename them so the VGAuth APIs have sane variable names.
    */
   pemCert = nameVals[0].val;
   ai.subject.val.name = nameVals[1].val;
   ai.comment = nameVals[2].val;
   userName = nameVals[3].val;
   addMapped = (*(nameVals[4].val) == 't' || *(nameVals[4].val) == 'T');

   if (verboseLogFlag) {
      printf("Certficate: \n%s\n", pemCert);
      printf("Subject: %s\n", ai.subject.val.name);
      printf("Comment: %s\n", ai.comment);
      printf("GuestUsername: %s\n", userName);
      printf("AddMapped: %d\n", addMapped);
   }


   /*
    * Verify we got at least the args we require.
    */
   if ((NULL == userName) || (NULL == pemCert) ||
       (NULL == ai.subject.val.name)) {
      fprintf(stderr, "Missing required keys\n");
      status = FALSE;
      goto done;
   }

   /*
    * Clear out extra whitespace.
    *
    * XXX VGAuth will clean up any whitespace in the cert, so this
    * is needed to properly match.  May have to go to the full cert clean
    * that vgauth uses (base64 decode, base64 encode), or compare
    * the base64 decoded data.
    */
   g_strstrip(pemCert);

   /*
    * Now tell VGAuth.
    */
   VGAuth_SetLogHandler(vgauthLog, NULL, 0, NULL);
   vgErr = VGAuth_Init(VGAUTHIMPORT_APP_NAME, 0, NULL, &ctx);
   if (VGAUTH_FAILED(vgErr)) {
      fprintf(stderr, "Failed to init VGAuth context\n");
      status = FALSE;
      goto done;
   }

   /*
    * Make sure we don't already have the entry.
    *
    * We only care for the addMapped case, since a duplicate mapped
    * entry is an error.  In the non-mapped case, a dup is a no-op.
    */
   if (addMapped) {
      vgErr = VGAuth_QueryMappedAliases(ctx, 0, NULL,
                                        &numMapped, &maList);
      if (VGAUTH_FAILED(vgErr)) {
         fprintf(stderr, "VGAuth_QueryMappedAliases failed.\n");
         status = FALSE;
         goto done;
      }

      for (i = 0; i < numMapped; i++) {
         if ((strcmp(pemCert, maList[i].pemCert) == 0) &&
             (strcmp(userName, maList[i].userName) == 0)) {
            int j;

            for (j = 0; j < maList[i].numSubjects; j++) {
               if (maList[i].subjects[j].type != VGAUTH_SUBJECT_NAMED) {
                  continue;
               }
               if (strcmp(ai.subject.val.name,
                          maList[i].subjects[j].val.name) == 0) {
                  fprintf(stderr,
                          "Entry already exists; just updating 'import' timestamp\n");
                  alreadyExists = TRUE;
               }
            }
         }
      }
   }

   /*
    * XXX
    *
    * Support a 'clobber' cmdline arg, that does a remove/add?
    * Should only be useful for a comment tweak.
    */

   if (!alreadyExists) {
      vgErr = VGAuth_AddAlias(ctx, userName, addMapped,
                              pemCert, &ai, 0, NULL);
      if (VGAUTH_FAILED(vgErr)) {
         fprintf(stderr, "Failed to set VGAuth data\n");
         status = FALSE;
         goto done;
      }
   } else {
      /*
       * XXX
       *
       * Lets return success, so the timestamp still gets updated.
       * May want to tie this to a cmdline flag.
       */
      status = TRUE;
   }

done:
   if (NULL != ctx) {
      VGAuth_Shutdown(ctx);
   }
   for (i = 0; i < ARRAYSIZE(nameVals); i++) {
      free(nameVals[i].val);
   }
   VGAuth_FreeMappedAliasList(numMapped, maList);

   return status;
}


/*
 ******************************************************************************
 * SetImportTime --                                                      */ /**
 *
 * Updates the namespace 'import' value with the current time.
 *
 * @param[in]  namespace  The namespace to be updated.
 *
 * @return TRUE if successful, FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
SetImportTime(char *namespace)
{
   char *curTimeStr;
   Bool status;
   DynBuf buf;
   char *result = NULL;
   size_t resultLen;
   GTimeVal now;

   /*
    * Use ISO timestamp to be VIM friendly.
    */
   g_get_current_time(&now);
   curTimeStr = g_time_val_to_iso8601(&now);

   DynBuf_Init(&buf);

   /*
    * Format is:
    *
    * namespace-set-keys <namespace>\0<numOps>\0<op>\0<key>\0<value>\0<oldVal>
    *
    * We have just a single op, and want to always set the value, clobbering
    * anything already there.
    */
   if (!DynBuf_Append(&buf, NSDB_PRIV_SET_KEYS_CMD,
                      strlen(NSDB_PRIV_SET_KEYS_CMD)) ||
       !DynBuf_Append(&buf, " ", 1) ||
       !DynBuf_AppendString(&buf, namespace) ||
       !DynBuf_AppendString(&buf, "1") || // numOps
       !DynBuf_AppendString(&buf, "0") || // op 0 == setAlways
       !DynBuf_AppendString(&buf, KEY_NAME_IMPORTED) ||
       !DynBuf_AppendString(&buf, curTimeStr) ||
       !DynBuf_Append(&buf, "", 1)) {                 // NULL for set always
      fprintf(stderr, "Could not construct update request buffer\n");
      status = FALSE;
   } else {
      status = RpcChannel_SendOneRaw(DynBuf_Get(&buf),
                                     DynBuf_GetSize(&buf),
                                     &result, &resultLen);
      if (!status) {
         fprintf(stderr, "Failed to update %s field after processing namespace '%s\n",
                 KEY_NAME_IMPORTED, namespace);
      }
      free(result);
   }
   g_free(curTimeStr);
   DynBuf_Destroy(&buf);

   return status;
}


/*
 ******************************************************************************
 * QueryNamespace --                                                     */ /**
 *
 * Queries the namespace DB for the keys we want.
 *
 * @param[in]  namespace      The namespace name.
 * @param[out] resultData     The result data.
 * @param[out] resultDataLen  The length of the result data.
 *
 * @return TRUE if successful, FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
QueryNamespace(char *namespace,
               char **resultData,
               size_t *resultDataLen)
{
   char *result = NULL;
   size_t resultLen = 0;
   DynBuf buf;
   int i;
   Bool status = FALSE;

   ASSERT(namespace);

   DynBuf_Init(&buf);

   /*
    * Format is
    *
    * namespace-get-values <namespace>\0<key>\0...
    *
    */
   if (!DynBuf_Append(&buf, NSDB_PRIV_GET_VALUES_CMD,
                      strlen(NSDB_PRIV_GET_VALUES_CMD)) ||
       !DynBuf_Append(&buf, " ", 1) ||
       !DynBuf_AppendString(&buf, namespace)) {
      fprintf(stderr, "Could not contruct request buffer\n");
      goto done;
   }
   for (i = 0; i < ARRAYSIZE(nameVals); i++) {
      if (!DynBuf_AppendString(&buf, nameVals[i].keyName)) {
         fprintf(stderr, "Could not contruct request buffer\n");
         goto done;
      }
   }

   status = RpcChannel_SendOneRaw(DynBuf_Get(&buf), DynBuf_GetSize(&buf),
                                  &result, &resultLen);

done:
   *resultData = result;
   *resultDataLen = resultLen;
   DynBuf_Destroy(&buf);
   return status;
}


/*
 ******************************************************************************
 * ProcessNamespace --                                                   */ /**
 *
 * Processes the data in 'namespace'.  Queries the namespace DB for
 * the keys we want, sends them to VGAuth, and updates the namespace
 * with a timestamp noting when the data was processed.
 *
 * @param[in]  namespace   The namespace name.
 *
 * @return TRUE if successful, FALSE otherwise.
 *
 ******************************************************************************
 */

static Bool
ProcessNamespace(char *namespace)
{
   char *result = NULL;
   size_t resultLen = 0;
   Bool status = FALSE;

   ASSERT(namespace);

   status = QueryNamespace(namespace, &result, &resultLen);

   if (!status) {
      /*
       * Underlying error message works just fine so use it.
       */
      fprintf(stderr, "Error: %s\n", result ? result : "NULL");
      fprintf(stderr, "Failed to read namespace '%s'\n", namespace);
   } else {
      if (resultLen == 0) {
         fprintf(stderr, "Error: No keys found in namespace '%s'\n", namespace);
      } else {
         status = ProcessQueryReply(result, resultLen);

         if (status ) {
            status = SetImportTime(namespace);
         } else {
            fprintf(stderr,
                    "Failed to import vgauth record from namespace '%s'\n",
                    namespace);
         }
      }
   }

   free(result);

   return status;
}


/*
 ******************************************************************************
 * main --                                                               */ /**
 *
 * Main entry point.
 *
 * @param[in]  argc  Number of args.
 * @param[in]  argv  The args.
 *
 ******************************************************************************
 */

int
main(int argc, char *argv[])
{
   int argcCopy;
   char **argvCopy;
   GError *gErr = NULL;
   int success = -1;
   const GOptionEntry options[] = {
      { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verboseLogFlag,
        "Verbose logging mode", NULL },
      { NULL },
   };
   GOptionContext *optCtx;

/*
 * Using WinUtil_EnableSafePathSearching() and WinUtil_VerifyExePathW() from
 * "vmware/tools/win32util.h" rather than the ones
 * in vgauth/common/winUtil.h, since this app is generic toolbox apps.
 */
#ifdef _WIN32
   WinUtil_EnableSafePathSearching(TRUE);
#if defined(VMX86_RELEASE)
   WinUtil_VerifyExePathW();
#endif
#endif

   /*
    * The options parser needs to modify these, and using the variables
    * coming into main doesn't work.
    */
   argcCopy = argc;
   argvCopy = argv;

   gAppName = g_path_get_basename(argv[0]);

   g_set_prgname(gAppName);
   optCtx = g_option_context_new(usageMsg);
   g_option_context_set_summary(optCtx, summaryMsg);
   g_option_context_add_main_entries(optCtx, options, NULL);

   /*
    * Check if environment is VM
    */
   if (!VmCheck_IsVirtualWorld()) {
      g_printerr("Error: %s must be run inside a virtual machine"
                 " on a VMware hypervisor product.\n", gAppName);
      goto out;
   }

   if (!g_option_context_parse(optCtx, &argcCopy, &argvCopy, &gErr)) {
      fprintf(stderr, "%s: %s\n", gAppName, gErr->message);
      g_error_free(gErr);
      goto out;
   }

   if (argc < 2) {
      PrintUsage(optCtx);
      goto out;
   }

   success = ProcessNamespace(argv[1]) ? 0 : 1;

out:
   g_option_context_free(optCtx);
   g_free(gAppName);

   return success;
}

   07070100000477000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003000000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse   07070100000478000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000479000081A400000000000000000000000168225505000008BC000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse/Makefile.am   ################################################################################
### Copyright (c) 2008-2016,2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-vmblock-fuse

# _XOPEN_SOURCE needs to be defined to at least 600 when compiling
# vmblock-fuse; it's needed by os.h to get pthread_rwlock_t and by fsops.c to
# get lstat.
# -D_FILE_OFFSET_BITS=64 required for fuse.

AM_CFLAGS =
AM_CFLAGS += -Dvmblock_fuse
AM_CFLAGS += -U_XOPEN_SOURCE
AM_CFLAGS += -D_XOPEN_SOURCE=600
AM_CFLAGS += -DUSERLEVEL
AM_CFLAGS += -D_FILE_OFFSET_BITS=64
AM_CFLAGS += @FUSE_CPPFLAGS@
AM_CFLAGS += @FUSE3_CPPFLAGS@
AM_CFLAGS += @GLIB2_CPPFLAGS@
AM_CFLAGS += -I$(top_srcdir)/modules/shared/vmblock
AM_CFLAGS += -I$(srcdir)

vmware_vmblock_fuse_LDADD =
vmware_vmblock_fuse_LDADD += @FUSE_LIBS@
vmware_vmblock_fuse_LDADD += @FUSE3_LIBS@
vmware_vmblock_fuse_LDADD += @GLIB2_LIBS@

vmware_vmblock_fuse_SOURCES =
vmware_vmblock_fuse_SOURCES += util.c
vmware_vmblock_fuse_SOURCES += fsops.c
vmware_vmblock_fuse_SOURCES += main.c

vmware_vmblock_fuse_SOURCES += $(top_srcdir)/modules/shared/vmblock/block.c
vmware_vmblock_fuse_SOURCES += $(top_srcdir)/modules/shared/vmblock/stubs.c

if HAVE_ICU
   vmware_vmblock_fuse_LDADD += @ICU_LIBS@
   vmware_vmblock_fuse_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
                      $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) \
                      $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
else
   vmware_vmblock_fuse_LINK = $(LINK)
endif
0707010000047A000081A40000000000000000000000016822550500001F8A000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse/design.txt    -----------------------
| VMBlock Fuse Design |
-----------------------

Filesystem User Interface
-------------------------
Reexports /tmp/VMwareDnD. I'll call this the target directory.

Mount point will contain:
    * /blockdir/
    * /dev
    * /version - Proposed, not implemented.

/blockdir/ - Contains symlinks to the contents of the target directory. In the linux port
this is called /mountPoint/; it is created by the blocking driver and then the fs is
mounted there. That's kind of confusing here, because we don't have 2 separate pieces.
It's just a special directory within the file system.

/dev - Control file. Control operations happen by opening this file and doing a write on
it. The data that we write is a character representing the operation to be done,
sometimes followed by the path of the file to be blocked or unblocked (its original path,
eg: "/tmp/VMwareDnD/foo/bar"). The count argument to write must be the length of the
string. This will be the length of the path + 1 for the operation character.

COMMAND                        OPERATION CHARACTER    EXAMPLE
Add block                      a                      write(fd, "a/some/path/in/target/dir", 25)
Delete block                   d                      write(fd, "d/another/path/in/target/dir", 28)
List blocks (only available    l (lower case L)       write(fd, "l", 1)
    with VMX86_DEVEL)

I was hoping to use the same interface as the linux kernel port, but doing writes with
too-long sizes through fuse isn't supported and may not always work.

When an entry in /blockdir/ is blocked, any readlink() on that entry will block.

The control file is owned and only writable by root. We might want to change that (more
below). Right now we have a wrapper with root priveleges acquire a vmblock control file
descriptor, but we want to get rid of it. Specifically, this will be useful for remote
DnD where there might not be a process with root permission.

/version - NEW. XXX: Maybe we want to add a read-only file which contains a version
number? This would allow us to make changes later and have it be easy for the tools to
know what they can do.


Internal Design
---------------
Uses FUSE's default multithreaded mode where normally, each filesystem request is serviced
by it's own thread. This will allow accesses to block without preventing new accesses.

IMPORTANT: Please read the deadlock section for more about the threading model.

I'm using the common blocking code that the rest of the vmblock implementations share. It
stores blocks as a linked list of structs which contain the direct path of the file being
blocked (eg: "/tmp/VMwareDnD/foo/bar"), variables to link it into the list, an id of the
control file which caused the block, a completion/condition variable, and a reference
count. The purpose of the ref count is so that the block instance is only cleaned up once
all threads waiting on it have woken up. Or more specifically, so that the last thread to
wake up knows that the block isn't being used any more and will clean it up.

The common blocking code uses a few constructs which are defined by port specific headers
(os.h). For vmblock-fuse, the global blocked list will be protected by a pthread rwlock.
Blocking will be done by waiting on condition variables. The completion type is a struct
containing a mutex, condition variable, and done int (treated as a bool). Macros are
defined to manipulate these properly. The atomic type used for reference counting is a
vm_atomic type.


Deadlock
--------
Older versions of fuse are limited to having 10 worker threads for processing filesystem
requests. Once this limit is exceeded, fuse will block any operation until a thread
becomes available to handle it. So if a block is placed on a file and many requests to
access it are made and block, the filesystem will deadlock.

This limit has been removed in fuse 2.6.0.
The macfuse patch to fuse doesn't seem to affect this functionality and it's based off of
fuse 2.7. I haven't testing it on macfuse though.

Another possible deadlock from a fuse-devel email:

On Wed, 9 Jul 2008, bargav yaarov wrote:

> I have a point within my read handler where I hold a pthread_rwlock_t. At the same
> time, another read handler is blocked waiting to acquire this lock. Now, if I attempt
> to unmount at this time, the fuse library code calls pthread_cancel() against the
> handling thread and I am left in a deadlocked state. How do you suggest I handle this
> situation?

Actually, CVS version of fuse has a fix for this:

    * Only enable cancelation when reading a request, otherwise cancellation could happen
      with a mutex held, which could hang the process on umount

... OK, I put out a prerelease so there's no need to mess with CVS:

http://sourceforge.net/project/showfiles.php?group_id=121684&package_id=140422

XXX: Check if I hit this problem.


Testing
-------
    * Some unit tests for fsops functions- bora/modules/vmblock/test/fuse-fsops-test.cpp.
    * Existing full vmblock tester- bora/modules/vmblock/test/vmblocktest.c.
    * Very simple testing program to block and unblock a single file- demo.c.


Enforcing Permissions
---------------------
The vmblock-fuse code correctly report the file permissions but doesn't check them. It
should be mounted with the "-o default_permissions" option which will tell fuse to do the
checking for us.

If the filesystem it going to be mounted by a non-root user for system wide use, it
should also be given the "-o allow_other" option to allow users other than the one that
mounted it to be able to use it. This will require enabling the "user_allow_other" option
in /etc/fuse.conf.


API
---
There are some helper functions, macros, and constants in the public header vmblock.h.
I've added a new set of them which work with vmblock-fuse and I hope make using it API
compatible with the other vmblock ports. I need to look at users of vmblock to make sure
they access vmblock via the header the way I expect.


Blocking Indexing Software
--------------------------
There are a number of program which may be running on various systems which index the
contents of the computer. If an indexer was to hit a blocked file, it would get stuck
which is undesireable. For the linux kernel port, the blocked files are in /proc which
shouldn't be indexed. On solaris and freebsd, it's in /var/run which I guess is also not
normally indexed. Right now, vmblock-fuse gets mounted for system wide use in
/tmp/vmblock/.

(s)locate/updatedb (linux)
On ubuntu 8.04, /etc/updatedb.conf it set to ignore /tmp. I would guess this is pretty
standard.

beagle (linux)
According to http://beagle-project.org/Indexing_Data, by default, everything in $(HOME)
is indexed.

spotlight (Mac OS)
I don't know if /tmp even exists on Mac OS. I need to revisit this once I get vmblock-fuse
running on Mac OS.

google desktop search (windows, linux)
On linux, doesn't index /tmp. Also doesn't index hidden (.*) directories. This might be
useful to know later on. In case it matters, a list of things that it doesn't search on
windows is at http://desktop.google.com/support/bin/answer.py?answer=12634.


Unprivileged User Blocking
--------------------------
If I have time later on, I'd like to investigate having non-root users be able to create
blocks. This will be helpful for scenarios where we don't have a process with root
privileges to do our blocking for us, like remote copy/paste. The security aspects need
some careful thought but I'd like to jot down some notes on thoughts Elliot and I had
about possible ways to do this.

    * Have control file be accessible by any user as far as vmblock-fuse is concerned.
      Have each user mount their own copy of vmblock which only they can access. Possibly
      in their home dir. This will basically work with the current code if mounted
      without the default_permissions option. I might want to change the permissions it
      shows on the files.
    * Have a single vmblock-fuse mount. Any user can add blocks but those blocks only
      apply to accesses by that user.
  0707010000047B000081A40000000000000000000000016822550500006673000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse/fsops.c   /*********************************************************
 * Copyright (c) 2008-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fsops.c --
 *
 *      Vmblock fuse filesystem operations.
 *
 *      See design.txt for more information.
 */

#include <stdio.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <stdint.h>

#include "fsops.h"
#include "block.h"
#include "vm_basic_types.h"
#include "vm_assert.h"

/* Regular directories on a linux ext3 partition are 4K. */

#define DIR_SIZE (4 * 1024)

typedef struct vmblockSpecialDirEntry {
   char *path;
   int mode;
   unsigned int nlink;
   size_t size;
} vmblockSpecialDirEntry;

static vmblockSpecialDirEntry specialDirEntries[] = {
   { "/",               S_IFDIR | 0555, 3, DIR_SIZE },
   { CONTROL_FILE,      S_IFREG | 0600, 1, 0 },
   { REDIRECT_DIR,      S_IFDIR | 0555, 3, DIR_SIZE },
   { NOTIFY_DIR,        S_IFDIR | 0555, 3, DIR_SIZE },
   { NULL,              0,              0, 0 }
};
static vmblockSpecialDirEntry symlinkDirEntry =
   { REDIRECT_DIR "/*", S_IFLNK | 0777, 1, -1 };

static vmblockSpecialDirEntry notifyDirEntry =
   { NOTIFY_DIR "/*",   S_IFREG | 0444, 1, 0 };

#if FUSE_MAJOR_VERSION == 3
#define CALL_FUSE_FILLER(buf, name, stbuf, off, flags) \
   filler(buf, name, stbuf, off, flags)
#else
#define CALL_FUSE_FILLER(buf, name, stbuf, off, flags) \
   filler(buf, name, stbuf, off)
#endif

/*
 *-----------------------------------------------------------------------------
 *
 * RealReadLink --
 *
 *      Gets the target of a symlink.
 *
 *      The same idea as unix readlink() except that it returns 0 on
 *      success, and fills buf with a null terminated string. If target doesn't
 *      fit in buf, it is truncated.
 *
 * Results:
 *      0 on success. Possible errors (as negative values):
 *      -EINVAL       bufSize is not positive.
 *      -ENOENT       path does not exist.
 *      -ENAMETOOLONG path or target of symlink was too long.
 *      Any other errors lstat can return.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
RealReadLink(const char *path,   // IN: Path within vmblock filesystem. Must be
                                 //     within the redirect directory.
             char *buf,          // OUT: Target of link if valid (or part of it).
             size_t bufSize)     // IN: Size of buf.
{
   int status;
   const char redirectPrefix[] = REDIRECT_DIR "/";
   const int redirectPrefixLength = sizeof redirectPrefix - 1;
   const char targetPrefix[] = TARGET_DIR "/";
   const int targetPrefixLength = sizeof targetPrefix - 1;
   const char *relativeTarget = path + redirectPrefixLength;
   char target[PATH_MAX + 1];
   const size_t spaceForRelativeTarget = sizeof target - targetPrefixLength;
   struct stat dummyStatBuf; // Don't care what goes here.

   /* TARGET_DIR + '/' needs to leave room for relative target. */
   ASSERT_ON_COMPILE(sizeof TARGET_DIR + 1 < PATH_MAX);

   ASSERT(strncmp(path, redirectPrefix, redirectPrefixLength) == 0 &&
          strlen(path) > redirectPrefixLength);
   if (bufSize < 1) {
      return -EINVAL;
   }

   /*
    * Assemble path to destination of link. This goes into a temporary buffer
    * instead of directly into buf, because buf may not be big enough for the
    * whole thing, but this should still return success if the target
    * exists which means it must lstat the full target path.
    */

   strlcpy(target, targetPrefix, sizeof target);

   /*
    * spaceForRelativeTarget must be greater than strlen(relativeTarget) to
    * leave room for the nul terminator.
    */

   if (spaceForRelativeTarget <= strlen(relativeTarget)) {
      return -ENAMETOOLONG;
   }
   strlcpy(target + targetPrefixLength, relativeTarget, spaceForRelativeTarget);

   /* Verify that target exists. */

   status = lstat(target, &dummyStatBuf);
   if (status != 0) {
      return -errno;
   }

   strlcpy(buf, target, bufSize);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockReadLink --
 *
 *      Gets the target of a symlink. Blocks if there is a block on the path.
 *
 *      Basically the same as unix readlink() except that it returns 0 on
 *      success, fills buf with a null terminated string, and adds our blocking
 *      functionality.
 *
 * Results:
 *      0 on success. Possible errors (as negative values):
 *      -EINVAL       bufSize is not positive.
 *      -ENOENT       path is not within the redirect directory.
 *      -ENAMETOOLONG path or target of symlink was too long.
 *      Any other errors lstat can return.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockReadLink(const char *path,   // IN: Path within vmblock (top level) mount point.
                char *buf,          // OUT: Target of link if valid (or part of it).
                size_t bufSize)     // IN: Size of buf.
{
   int status;
   char target[PATH_MAX + 1];

   if (strncmp(path, REDIRECT_DIR, strlen(REDIRECT_DIR)) != 0) {
      return -ENOENT;
   }
   status = RealReadLink(path, target, sizeof target);
   if (status < 0) {
      return status;
   }

   BlockWaitOnFile(target, NULL);

   strlcpy(buf, target, bufSize);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * SetTimesToNow --
 *
 *      Sets the atime, mtime, and ctime of a stat struct to the current time.
 *      If there's an error getting the current time, sets them to 0.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
SetTimesToNow(struct stat *statBuf)      // OUT
{
   int status;
   struct timeval time;

   status = gettimeofday(&time, NULL);
   if (status < 0) {
      statBuf->st_atime = statBuf->st_mtime = statBuf->st_ctime = 0;
   } else {
      statBuf->st_atime = statBuf->st_mtime = statBuf->st_ctime = time.tv_sec;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockGetAttr --
 *
 *      Gets the attributes of a directory entry. Equivalent to stat().
 *
 *      Returns fixed results for /, /dev, and /blockdir. For anything within
 *      /blockdir, if a target exists with that name in the target directory,
 *      it returns fixed stats for a symlink. Reads the stats to return from the
 *      globals specialDirEntries and symlinkDirEntries.
 *
 * Results:
 *      0 on success. Possible errors (as negative values):
 *      -ENOENT  File or directory does not exist.
 *      -ENAMETOOLONG path too long.
 *      Any other errors lstat can return.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
int
VMBlockGetAttr(const char *path,          // IN: File to get attributes of.
               struct stat *statBuf,      // OUT: Where to put the attributes.
               struct fuse_file_info *fi) // IN: Ignored
#else
int
VMBlockGetAttr(const char *path,        // IN: File to get attributes of.
               struct stat *statBuf)    // OUT: Where to put the attributes.
#endif
{
   vmblockSpecialDirEntry *dirEntry;
   ASSERT(path != NULL);
   ASSERT(statBuf != NULL);

   if (strlen(path) > PATH_MAX) {
      return -ENAMETOOLONG;
   }

   for (dirEntry = specialDirEntries; dirEntry->path != NULL; ++dirEntry) {
      if (strcmp(path, dirEntry->path) == 0) {
         memset(statBuf, 0, sizeof *statBuf);
         statBuf->st_mode = dirEntry->mode;
         statBuf->st_nlink = dirEntry->nlink;
         statBuf->st_size = dirEntry->size;
         SetTimesToNow(statBuf);
         return 0;
      }
   }
   if (strncmp(path, REDIRECT_DIR, strlen(REDIRECT_DIR)) == 0) {
      char target[PATH_MAX + 1];
      int status = RealReadLink(path, target, sizeof target);

      LOG(4, "%s: Called RealReadLink which returned: %d\n", __func__, status);
      if (status != 0) {
         return status;
      }
      memset(statBuf, 0, sizeof *statBuf);
      statBuf->st_mode = symlinkDirEntry.mode;
      statBuf->st_nlink = symlinkDirEntry.nlink;
      statBuf->st_size = strlen(target);
      SetTimesToNow(statBuf);
      return 0;
   }
   if (strncmp(path, NOTIFY_DIR, strlen(NOTIFY_DIR)) == 0) {
      memset(statBuf, 0, sizeof *statBuf);
      statBuf->st_mode = notifyDirEntry.mode;
      statBuf->st_nlink = notifyDirEntry.nlink;
      statBuf->st_size = notifyDirEntry.size;
      SetTimesToNow(statBuf);
      return 0;
   }
   return -ENOENT;
}


/*
 *-----------------------------------------------------------------------------
 *
 * ExternalReadDir --
 *
 *      Gets the contents of a directory outside the vmblock-fuse filesystem.
 *
 * Results:
 *      0 on success. Possible errors (as negative values):
 *      -EACCES  Permission denied.
 *      -EBADF   Invalid directory stream descriptor (I don't think this can
 *               happen).
 *      -EMFILE  Too many file descriptors in use by process.
 *      -ENFILE  Too many files are currently open in the system.
 *      -ENOENT  Directory does not exist, or name is an empty string.
 *      -ENOMEM  Insufficient memory to complete the operation.
 *      -ENOTDIR path is not a directory.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
ExternalReadDir(const char *blockPath,           // IN:
                const char *realPath,            // IN: Full (real) path to
                                                 //     directory to read.
                void *buf,                       // OUT: Destination for
                                                 //      directory listing.
                fuse_fill_dir_t filler,          // IN: Function to use to add
                                                 //     an entry to buf. Returns
                                                 //     1 if buffer full, 0
                                                 //     otherwise.
                off_t offset,                    // IN: Ignored.
                struct fuse_file_info *fileInfo) // IN: Ignored.
{
   int status;
   struct dirent *dentry = NULL;
   struct stat statBuf;
   DIR *dir = NULL;

   LOG(4, "%s: blockPath: %s, realPath: %s\n", __func__, blockPath, realPath);
   dir = opendir(realPath);
   if (dir == NULL) {
      return -errno;
   }

   /*
    * readdir() only needs to fill in the type bits of the mode in the stat
    * struct it passes to filler().
    * http://sourceforge.net/mailarchive/forum.php?thread_name=E1KNlwx-00008e-Fb%40pomaz-ex.szeredi.hu&forum_name=fuse-devel
    */

   memset(&statBuf, 0, sizeof statBuf);
   if (strncmp(blockPath, NOTIFY_DIR, strlen(NOTIFY_DIR)) == 0) {
      statBuf.st_mode = S_IFREG;
   } else {
      statBuf.st_mode = S_IFLNK;
   }

   /* Clear errno because readdir won't change it if it succeeds. */
   errno = 0;

   while ((dentry = readdir(dir)) != NULL) {
      status = CALL_FUSE_FILLER(buf, dentry->d_name, &statBuf, 0, 0);
      if (status == 1) {
         break;
      }
   }

   if (errno != 0) {
      return -errno;
   }

   status = closedir(dir);
   if (status != 0) {
      return -errno;
   }
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockReadDir --
 *
 *      Gets the contents of a directory within the filesystem.
 *
 *      If filler() runs out of memory, it will store an error in buf (which fuse
 *      will check) and then return 1.
 *      http://sourceforge.net/mailarchive/forum.php?thread_name=E1KMUnu-0007gI-29%40pomaz-ex.szeredi.hu&forum_name=fuse-devel
 *
 * Results:
 *      0 on success or filler() fail. Possible errors (as negative values):
 *      -EACCES  Permission denied.
 *      -EBADF   Invalid directory stream descriptor (I don't think this can
 *               happen).
 *      -EMFILE  Too many file descriptors in use by process.
 *      -ENFILE  Too many files are currently open in the system.
 *      -ENOENT  Directory does not exist, or name is an empty string.
 *      -ENOMEM  Insufficient memory to complete the operation.
 *      -ENOTDIR path is not a directory.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
int
VMBlockReadDir(const char *path,                // IN: Directory to read.
               void *buf,                       // OUT: Where to put directory
                                                //      listing.
               fuse_fill_dir_t filler,          // IN: Function to add an entry
                                                //     to buf.
               off_t offset,                    // IN: Ignored.
               struct fuse_file_info *fileInfo, // IN: Ignored.
               enum fuse_readdir_flags flags)   // IN: Ignored.
#else
int
VMBlockReadDir(const char *path,                // IN: Directory to read.
               void *buf,                       // OUT: Where to put directory
                                                //      listing.
               fuse_fill_dir_t filler,          // IN: Function to add an entry
                                                //     to buf.
               off_t offset,                    // IN: Ignored.
               struct fuse_file_info *fileInfo) // IN: Ignored.
#endif
{
   struct stat fileStat;
   struct stat dirStat;
   LOG(4, "%s: path: %s\n", __func__, path);

   /*
    * readdir() only needs to fill in the type bits of the mode in the stat
    * struct it passes to filler().
    * http://sourceforge.net/mailarchive/forum.php?thread_name=E1KNlwx-00008e-Fb%40pomaz-ex.szeredi.hu&forum_name=fuse-devel
    */

   memset(&fileStat, 0, sizeof fileStat);
   fileStat.st_mode = S_IFREG;
   memset(&dirStat, 0, sizeof dirStat);
   dirStat.st_mode = S_IFDIR;

   if (strcmp(path, "/") == 0) {
      (void)(CALL_FUSE_FILLER(buf, ".", &dirStat, 0, 0) ||
             CALL_FUSE_FILLER(buf, "..", &dirStat, 0, 0) ||
             CALL_FUSE_FILLER(buf, VMBLOCK_DEVICE_NAME, &fileStat, 0, 0) ||
             CALL_FUSE_FILLER(buf, REDIRECT_DIR_NAME, &dirStat, 0, 0) ||
             CALL_FUSE_FILLER(buf, NOTIFY_DIR_NAME, &dirStat, 0, 0));
      return 0;
   } else if (   (strcmp(path, REDIRECT_DIR) == 0)
              || (strcmp(path, NOTIFY_DIR) == 0)) {
      return ExternalReadDir(path, TARGET_DIR, buf, filler, offset, fileInfo);
   } else {
      return -ENOENT;
   }
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockOpen --
 *
 *      Opens the control file. Trying to open anything else will fail.
 *
 *      The file handle that is filled into fileInfo is a memory address so it
 *      can be any number that is at least 1 and at most (void *)(-1);
 *
 *      fileInfo will be marked as requiring direct io. This disables any
 *      caching, which may not be needed, but can't hurt. It also somehow helps
 *      allow the offset to advance past 4k which matters if we take out seeking
 *      to 0 before writing.
 *
 * Results:
 *      0 on success. Possible errors (as negative values):
 *      -ENOENT  path is anything other than the control file.
 *      -ENOMEM  Not enough memory available.
 *      If this was a proper file system, it would probably do additional
 *      checking and return -EISDIR or -ENAMETOOLONG in the right situations,
 *      but we don't care about that. If it's not the control file, it's
 *      -ENOENT.
 *
 * Side effects:
 *      Allocates one byte of memory, the address of which is used as a unique
 *      identifier of the open file.
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockOpen(const char *path,                   // IN
            struct fuse_file_info *fileInfo)    // IN/OUT
{
   /*
    * The blocking code needs a unique value associated with each open file to
    * know who owns what block. The memory address of a malloc()ed byte is used
    * for this purpose.
    */

   char *uniqueValue = NULL;

   if (   (strcmp(path, CONTROL_FILE) != 0)
       && (strncmp(path, NOTIFY_DIR, strlen(NOTIFY_DIR)) != 0)) {
      return -ENOENT;
   }

   uniqueValue = malloc(sizeof *uniqueValue);
   if (uniqueValue == NULL) {
      return -ENOMEM;
   }

   fileInfo->fh = CharPointerToFuseFileHandle(uniqueValue);
   fileInfo->direct_io = 1;
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * StripExtraPathSeparators --
 *
 *      Removes extra repeated '/' characters from a null terminated string.
 *      Eg: "/foo//bar" -> "/foo/bar".
 *      Also removes any trailing '/'s from the string.
 *
 *      Sometimes programs wind up building paths with extra '/'s in them which
 *      works, but we want to know that they're the same for blocking purposes.
 *      Similarly, we want /foo/bar/ and /foo/bar to match.
 *
 * Results:
 *      path has extra separators removed (see above).
 *      Returns the new length of the string.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

size_t
StripExtraPathSeparators(char *path)      // IN/OUT: nul terminated string.
{
   unsigned int i;
   size_t length;
   Bool lastCharWasSep = FALSE;

   ASSERT(path != NULL);
   length = strlen(path);

   for (i = 0; i < length; i++) {
      if (path[i] == '/') {
         if (lastCharWasSep) {
            memmove(path + i - 1, path + i, length - i + 1);
            --length;
            ASSERT(i > 0);
            --i;
         } else {
            lastCharWasSep = TRUE;
         }
      } else {
         lastCharWasSep = FALSE;
      }
   }

   /*
    * Strip trailing slash when appropriate.
    */

   if (length > 1 && path[length - 1] == '/') {
      --length;
   }

   path[length] = '\0';
   return length;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockWrite --
 *
 *      Writes to the control file to perform a blocking operation.
 *
 *      The write is the means by which a file block is add or removed. In a
 *      development build, it also allows all blocks to be listed. The desired
 *      operation is indicated by the first character in buf. Constants are
 *      defined in vmblock.h:
 *      VMBLOCK_ADD_FILEBLOCK: Add block
 *      VMBLOCK_DEL_FILEBLOCK: Remove block
 *      VMBLOCK_LIST_FILEBLOCKS: List blocks (only with VMX86_DEVEL)
 *
 *      The control file must already be open.
 *
 * Results:
 *      Returns size argument on success. Possible errors (as negative values):
 *      -EINVAL Operation character is not a valid value.
 *      -ENAMETOOLONG File to be blocked is too long.
 *      -ENOENT Tried to remove a block that doesn't exist.
 *      Maybe others.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockWrite(const char *path,                 // IN: Must be control file.
             const char *buf,                  // IN: Operation and file operand.
                                               //     Not nul terminated for
                                               //     consistency with other
                                               //     special fses, eg: procfs.
             size_t size,                      // IN: Size of buf.
             off_t offset,                     // IN: Ignored.
             struct fuse_file_info *fileInfo)  // IN: Ignored.
{
   int status;
   char trimmedBuf[PATH_MAX + 1];
   os_blocker_id_t blockerId;

   LOG(4, "%s: path: %s, size: %"FMTSZ"u\n", __func__, path, size);
   LOG(4, "%s: fileInfo->fh: %p\n", __func__,
      FuseFileHandleToCharPointer(fileInfo->fh));
   ASSERT(strcmp(path, CONTROL_FILE) == 0);
   if (size > PATH_MAX) {
      return -ENAMETOOLONG;
   }

   memcpy(trimmedBuf, buf, size);
   trimmedBuf[size] = '\0';
   LOG(4, "%s: buf: %s\n", __func__, trimmedBuf);
   StripExtraPathSeparators(trimmedBuf);

   blockerId = FuseFileHandleToCharPointer(fileInfo->fh);
   switch (trimmedBuf[0]) {
   case VMBLOCK_ADD_FILEBLOCK:
      status = BlockAddFileBlock(trimmedBuf + 1, blockerId);
      break;
   case VMBLOCK_DEL_FILEBLOCK:
      status = BlockRemoveFileBlock(trimmedBuf + 1, blockerId);
      break;
#ifdef VMX86_DEVEL
   case VMBLOCK_LIST_FILEBLOCKS:
      BlockListFileBlocks();
      status = 0;
      break;
#endif // VMX86_DEVEL
   default:
      status = -EINVAL;
   }
   return status == 0 ? size : status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockRead --
 *
 *      Reads from the control file yield the FUSE greeting string that is
 *      used by the vmware user process to detect whether it is dealing with
 *      FUSE-based or in-kernel block driver.
 *
 *      The control file must already be open.
 *
 * Results:
 *      Returns sizeof(VMBLOCK_FUSE_READ_RESPONSE) on success.
 *      Possible error (as negative value):
 *      -EINVAL if size of supplied buffer is too small.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockRead(const char *path,                 // IN: Must be control file.
            char *buf,                        // IN/OUT: oputput buffer
            size_t size,                      // IN: Size of buf.
            off_t offset,                     // IN: Ignored.
            struct fuse_file_info *fileInfo)  // IN: Ignored.
{
   const char redirectPrefix[] = REDIRECT_DIR "/";
   const char redirectPrefixLength = sizeof redirectPrefix - 1;
   const char notifyPrefix[] = NOTIFY_DIR "/";
   const char notifyPrefixLength = sizeof notifyPrefix - 1;
   const char *relativePath = path + notifyPrefixLength;

   LOG(4, "%s: path: %s, size: %"FMTSZ"u\n", __func__, path, size);
   LOG(4, "%s: fileInfo->fh: %p\n", __func__,
       FuseFileHandleToCharPointer(fileInfo->fh));

   if (strcmp(path, CONTROL_FILE) == 0) {
      if (size < sizeof VMBLOCK_FUSE_READ_RESPONSE) {
         return -EINVAL;
      }
      memcpy(buf, VMBLOCK_FUSE_READ_RESPONSE, sizeof VMBLOCK_FUSE_READ_RESPONSE);
      return sizeof VMBLOCK_FUSE_READ_RESPONSE;
   }

   if (strncmp(path, NOTIFY_DIR, strlen(NOTIFY_DIR)) == 0) {
      char target[PATH_MAX+1];
      char targetLink[PATH_MAX+1];

      strlcpy(target, redirectPrefix, sizeof target);
      strlcpy(target + redirectPrefixLength,
              relativePath,
              sizeof target - redirectPrefixLength);
      if (RealReadLink(target, targetLink, sizeof targetLink) < 0) {
         return -EINVAL;
      }
      return BlockWaitFileBlock(targetLink, OS_UNKNOWN_BLOCKER);
   }

   return -EINVAL;
}

/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockRelease --
 *
 *      Releases an open (control) file. Removes any blocks created via this
 *      file.
 *
 * Results:
 *      Returns 0.
 *
 * Side effects:
 *      Free's the memory allocated as a unique identifier for the open file.
 *
 *-----------------------------------------------------------------------------
 */

int
VMBlockRelease(const char *path,                   // IN: Must be control file.
               struct fuse_file_info *fileInfo)    // IN/OUT: Contains fh which is
                                                   //         freed.
{
   char *blockerId = FuseFileHandleToCharPointer(fileInfo->fh);

   ASSERT(path);
   ASSERT(fileInfo);

   if (strcmp(path, CONTROL_FILE) == 0) {
      ASSERT(blockerId != NULL);
      BlockRemoveAllBlocks(blockerId);
   }
   free(blockerId);
   blockerId = NULL;
   fileInfo->fh = CharPointerToFuseFileHandle(NULL);
   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockInit --
 *
 *      Initializes the filesystem.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Calls BlockInit which allocates memory and does other initialization.
 *
 *-----------------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
void *
VMBlockInit(struct fuse_conn_info *conn,
            struct fuse_config *config)
#else
#if FUSE_USE_VERSION < 26
void *
VMBlockInit(void)
#else
void *
VMBlockInit(struct fuse_conn_info *conn)
#endif
#endif
{
   BlockInit();
   return NULL;
}


/*
 *-----------------------------------------------------------------------------
 *
 * VMBlockDestroy --
 *
 *      Cleans up after the filesystem.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Calls BlockDestroy which frees memory and does other cleanup.
 *
 *-----------------------------------------------------------------------------
 */

void
VMBlockDestroy(void *private_data)    // IN: Not used.
{
   BlockCleanup();
}


struct fuse_operations vmblockOperations = {
   .readlink = VMBlockReadLink,
   .getattr  = VMBlockGetAttr,
   .readdir  = VMBlockReadDir,
   .open     = VMBlockOpen,
   .write    = VMBlockWrite,
   .read     = VMBlockRead,
   .release  = VMBlockRelease,
   .init     = VMBlockInit,
   .destroy  = VMBlockDestroy
};
 0707010000047C000081A40000000000000000000000016822550500001040000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse/fsops.h   /*********************************************************
 * Copyright (c) 2008-2018,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fsops.h --
 *
 *      Vmblock fuse filesystem operations.
 *
 *      The file ops and internal functions are prototyped here and the main
 *      function put in a separate file to enable unit testing of these
 *      functions directly. This is also the reason none of the functions are
 *      static.
 */

#ifndef _VMBLOCK_FUSE_H_
#define _VMBLOCK_FUSE_H_

/*
 *  FUSE_USE_VERSION must be set before the fuse or fuse3 headers are
 *  included.  If undefined, fall back to previous default used.
 */
#ifndef FUSE_USE_VERSION
/*
 * FUSE_USE_VERSION sets the version of the FUSE API that will be exported.
 * Version 25 is the newest version supported by the libfuse in our toolchain
 * as of 2008-07.
 */
#define FUSE_USE_VERSION 25
#endif

#include <fuse.h>

#include "vmblock.h"
#include "vm_assert.h"
#include "vm_basic_types.h"

#define REDIRECT_DIR_NAME VMBLOCK_CONTROL_MOUNTPOINT
#define REDIRECT_DIR "/" VMBLOCK_CONTROL_MOUNTPOINT
#define TARGET_DIR "/tmp/VMwareDnD"
#define CONTROL_FILE "/" VMBLOCK_DEVICE_NAME
#define NOTIFY_DIR_NAME VMBLOCK_FUSE_NOTIFY_MNTPNT
#define NOTIFY_DIR "/" NOTIFY_DIR_NAME

/*
 * FS operation functions
 */

int VMBlockReadLink(const char *path, char *buf, size_t bufSize);

#if FUSE_MAJOR_VERSION == 3
int VMBlockGetAttr(const char *path, struct stat *statBuf,
                   struct fuse_file_info *fi);
int VMBlockReadDir(const char *path, void *buf, fuse_fill_dir_t filler,
                   off_t offset, struct fuse_file_info *fileInfo,
                   enum fuse_readdir_flags);
#else
int VMBlockGetAttr(const char *path, struct stat *statBuf);
int VMBlockReadDir(const char *path, void *buf, fuse_fill_dir_t filler,
                   off_t offset, struct fuse_file_info *fileInfo);
#endif
int VMBlockOpen(const char *path, struct fuse_file_info *fileInfo);
int VMBlockWrite(const char *path, const char *buf, size_t size, off_t offset,
                 struct fuse_file_info *fileInfo);
int VMBlockRelease(const char *path, struct fuse_file_info *fileInfo);

extern struct fuse_operations vmblockOperations;

/*
 * Internal functions
 */

int RealReadLink(const char *path, char *buf, size_t bufSize);
void SetTimesToNow(struct stat *statBuf);
int ExternalReadDir(const char *blockPath, const char *realPath,
                    void *buf, fuse_fill_dir_t filler,
                    off_t offset, struct fuse_file_info *fileInfo);
size_t StripExtraPathSeparators(char *path);


/*
 * CharPointerToFuseFileHandle and FuseFileHandleToCharPointer --
 *
 *      Simple functions to keep all typecasting in one place.
 *
 *      Storing a pointer in the fh field of fuse_file_info is the recommended
 *      way to associate a pointer with an open file according to the fuse FAQ:
 *      http://fuse.sourceforge.net/wiki/index.php/FAQ#Is_it_possible_to_store_a_pointer_to_private_data_in_the_fuse_file_info_structurex3f.
 */

static INLINE uint64_t
CharPointerToFuseFileHandle(const char *pointer)               // IN
{
   ASSERT(sizeof (uint64_t) >= sizeof (char *));
   return (uintptr_t)(pointer);
}

static INLINE char *
FuseFileHandleToCharPointer(const uint64_t fileHandle)   // IN
{
   ASSERT(fileHandle <= (uintptr_t)((void *)(-1)));
   return (char *)((uintptr_t)(fileHandle));
}


#endif /* _VMBLOCK_FUSE_H_ */
0707010000047D000081A400000000000000000000000168225505000008F2000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse/main.c    /*********************************************************
 * Copyright (c) 2008-2016,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * main.c --
 *
 *      Entry point for vmblock-fuse file system.
 */

#include <string.h>
#include <stdio.h>

#include "fsops.h"

int LOGLEVEL_THRESHOLD = 0;


/*
 *-----------------------------------------------------------------------------
 *
 * main --
 *
 *      Entry point for the vmblock-fuse file system. Calls fuse_main(). See
 *      http://fuse.sourceforge.net/doxygen/fuse_8h.html#3bf31250361d44c2436d76f47f2400ed
 *      for more information.
 *
 *      There are many command line options that fuse filesystems can take. Run
 *      with --help for a listing or consult the fuse documentation.
 *      Options which are likely to be usefor for vmblock-fuse are
 *      -o default_permissions and -o allow_other.
 *
 *      If the -d option is specified, enables our logging in addition to
 *      what fuse does.
 *
 * Results:
 *      Returns 0 on success and nonzero on failure.
 *
 * Side effects:
 *      None/all.
 *
 *-----------------------------------------------------------------------------
 */

int
main(int argc,           // IN
     char *argv[])       // IN
{
   int i;
   for (i = 1; i < argc && strcmp(argv[i], "--") != 0; ++i) {
      if (strcmp(argv[i], "-d") == 0) {
         LOGLEVEL_THRESHOLD = 4;
      }
   }
#if FUSE_USE_VERSION < 26
   return fuse_main(argc, argv, &vmblockOperations);
#else
   return fuse_main(argc, argv, &vmblockOperations, NULL);
#endif
}
  0707010000047E000081A40000000000000000000000016822550500001614000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse/os.h  /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * os.h --
 *
 *      OS-specific definitions.
 */


#ifndef __OS_H__
#define __OS_H__

#include <limits.h>
#include <errno.h>
#include <pthread.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>

#include "vm_assert.h"

typedef pthread_rwlock_t                os_rwlock_t;
typedef void                            os_kmem_cache_t; // Not used.
typedef struct os_completion_t {
	pthread_cond_t cv;
	pthread_mutex_t mutex;
	int completed;
}                                       os_completion_t;
typedef gint                            os_atomic_t;
typedef char *                          os_blocker_id_t;

#define OS_UNKNOWN_BLOCKER              0
#define OS_ENOMEM                       (-ENOMEM)
#define OS_ENOENT                       (-ENOENT)
#define OS_EEXIST                       (-EEXIST)
#define OS_PATH_MAX                     PATH_MAX

#define OS_FMTTID                       "u"
#define os_threadid                     ((unsigned)(pthread_self()))

#define os_panic(fmt, args)             \
({                                      \
   vfprintf(stderr, fmt, args);         \
   abort();                             \
})

#define os_rwlock_init(lock)            pthread_rwlock_init(lock, NULL)
#define os_rwlock_destroy(lock)         pthread_rwlock_destroy(lock)

/*
 * XXX I don't know of anything better for os_rwlock_held that pthreads offers.
 */

#define os_rwlock_held(lock)            TRUE
#define os_read_lock(lock)              pthread_rwlock_rdlock(lock)
#define os_write_lock(lock)             pthread_rwlock_wrlock(lock)
#define os_read_unlock(lock)            pthread_rwlock_unlock(lock)
#define os_write_unlock(lock)           pthread_rwlock_unlock(lock)

/*
 * os_kmem_cache_create can't evaluate to NULL because there's a !NULL check
 * on its result.
 */

#define os_kmem_cache_create(name, size, align, ctor)  ((void *)1)
#define os_kmem_cache_destroy(cache)
#define os_kmem_cache_alloc(cache)                     malloc(sizeof(struct BlockInfo))
#define os_kmem_cache_free(cache, elem)                free(elem)

/*
 * Completion Functions
 */

#define os_completion_init(comp)                        \
({                                                      \
   pthread_cond_init(&(comp)->cv, NULL);                \
   pthread_mutex_init(&(comp)->mutex, NULL);            \
   (comp)->completed = 0;                               \
})
#define os_completion_destroy(comp)                     \
({                                                      \
   pthread_cond_destroy(&(comp)->cv);                   \
   pthread_mutex_destroy(&(comp)->mutex);               \
})
#define os_wait_for_completion(comp)                    \
({                                                      \
    pthread_mutex_lock(&(comp)->mutex);                 \
    while ((comp)->completed == 0) {                    \
       pthread_cond_wait(&(comp)->cv, &(comp)->mutex);  \
    }                                                   \
    pthread_mutex_unlock(&(comp)->mutex);               \
    0;                                                  \
})
#define os_complete_all(comp)                           \
({                                                      \
    pthread_mutex_lock(&(comp)->mutex);                 \
    (comp)->completed = 1;                              \
    pthread_cond_broadcast(&(comp)->cv);                \
    pthread_mutex_unlock(&(comp)->mutex);               \
    0;                                                  \
})

/*
 * Atomic Value Functions
 *
 * os_atomic_dec_and_test needs to test against 1 because ReadDecInt returns
 * the value before the dec.
 */

#define os_atomic_dec_and_test(atomic)  g_atomic_int_dec_and_test(atomic)
#define os_atomic_dec(atomic)           g_atomic_int_add((atomic), -1)
#define os_atomic_inc(atomic)           g_atomic_int_inc(atomic)
#define os_atomic_set(atomic, val)      g_atomic_int_set((atomic), (val))
#define os_atomic_read(atomic)          g_atomic_int_get(atomic)

/*
 * Extra stuff fuse port needs defined (ie not in os.h for other ports).
 */

#ifdef VMX86_DEVEL
extern int LOGLEVEL_THRESHOLD;
#  define LOG(level, fmt, args...)                              \
     ((void) (LOGLEVEL_THRESHOLD >= (level) ?                   \
              fprintf(stderr, "DEBUG:  " fmt, ## args) :        \
              0)                                                \
     )
#else
#  define LOG(level, fmt, args...)
#endif
#define Warning(fmt, args...)                                   \
     fprintf(stderr, "WARNING: " fmt, ## args)

/*
 * The GNU C library doesn't include strlcpy.
 * XXX: This prototype is moving. See util.c.
 */

size_t strlcpy(char *dest, const char *src, size_t count);

#endif /* __OS_H__ */
0707010000047F000081A400000000000000000000000168225505000006BA000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/vmblock-fuse/util.c    /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#include <string.h>

/*
 *----------------------------------------------------------------------------
 *
 * strlcpy --
 *
 *    XXX: Copied from vmblock/linux/module.c. Share them.
 *
 *    Copies at most count - 1 bytes from src to dest, and ensures dest is NUL
 *    terminated.
 *
 * Results:
 *    Length of src.  If src >= count, src was truncated in copy.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

size_t
strlcpy(char *dest,         // OUT: destination to copy string to
        const char *src,    // IN : source to copy string from
        size_t count)       // IN : size of destination buffer
{
   size_t ret;
   size_t len;

   ret = strlen(src);
   len = ret >= count ? count - 1 : ret;
   memcpy(dest, src, len);
   dest[len] = '\0';
   return ret;
}
  07070100000480000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003200000000open-vm-tools-12.5.2/open-vm-tools/vmblockmounter 07070100000481000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vmblockmounter/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000482000081A4000000000000000000000001682255050000061B000000000000000000000000000000000000003E00000000open-vm-tools-12.5.2/open-vm-tools/vmblockmounter/Makefile.am ################################################################################
### Copyright (C) 2011-2016 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

sbin_PROGRAMS = mount.vmblock

mount_vmblock_LDADD =
mount_vmblock_LDADD += ../lib/stubs/libStubs.la

mount_vmblock_SOURCES =
mount_vmblock_SOURCES += vmblockmounter.c

if FREEBSD
install-exec-hook:
	mv $(DESTDIR)$(sbindir)/mount.vmblock \
		$(DESTDIR)$(sbindir)/mount_vmblock
	-$(MKDIR_P) $(DESTDIR)/sbin
	-$(LN_S) $(DESTDIR)$(sbindir)/mount_vmblock \
		$(DESTDIR)/sbin/mount_vmblock &> /dev/null
uninstall-hook:
	rm -f $(DESTDIR)$(sbindir)/mount_vmblock
else
install-exec-hook:
	-$(MKDIR_P) $(DESTDIR)/sbin
	-$(LN_S) $(DESTDIR)$(sbindir)/mount.vmblock \
		$(DESTDIR)/sbin/mount.vmblock &> /dev/null
uninstall-hook:
	rm -f $(DESTDIR)/sbin/mount.vmblock
endif !FREEBSD
 07070100000483000081A4000000000000000000000001682255050000193D000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/vmblockmounter/vmblockmounter.c    /*********************************************************
 * Copyright (c) 2011-2016, 2021, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmblockmounter.c --
 *
 *      Helper app for mounting vmblock filesystem on FreeBSD and Solaris.
 *      Linux does not need it as it knows how to mount pseudo-filesystems
 *      without a helper program.
 */

#define _GNU_SOURCE

#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#if defined(__FreeBSD__)
#   include <sys/uio.h>
#   include <sys/param.h>
#endif
#include <sys/mount.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "vm_basic_types.h"
#include "vm_basic_defs.h"
#include "vm_assert.h"
#include "vmblock.h"
#include "vmblockmounter_version.h"

#include "vm_version.h"
#include "embed_version.h"
VM_EMBED_VERSION(VMBLOCKMOUNTER_VERSION_STRING);

#define LOG(format, ...) (beVerbose ? printf(format, ##__VA_ARGS__) : 0)

static char *thisProgram;
static char *thisProgramBase;
static Bool beVerbose = FALSE;

/*
 *-----------------------------------------------------------------------------
 *
 * PrintVersion --
 *
 *    Displays version.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
PrintVersion(void)
{
   printf("%s version: %s\n", thisProgramBase, VMBLOCKMOUNTER_VERSION_STRING);
}


/*
 *-----------------------------------------------------------------------------
 *
 * PrintUsage --
 *
 *    Displays usage for the vmblock mounting utility.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static void
PrintUsage(FILE *fd)   // IN: File stream to use for output
{
   fprintf(fd, "Usage: %s <source> <dir> [-o <options>]\n", thisProgramBase);
   fprintf(fd, "Mount the vmblock filesystem at given mount point.\n");
   fprintf(fd, "\n");
   fprintf(fd, "This command is intended to be run from within /bin/mount by\n");
   fprintf(fd, "passing the option '-t %s'. For example:\n", VMBLOCK_FS_NAME);
   fprintf(fd, "  mount -t %s /tmp/VMwareDnD /var/run/vmblock\n",
           VMBLOCK_FS_NAME);
}


/*-----------------------------------------------------------------------------
 *
 * main --
 *
 *    Main entry point. Parses the mount options received, makes a call to
 *    mount(2), and handles the results.
 *
 * Results:
 *    Zero on success, non-zero on failure.
 *
 * Side effects:
 *    May mount a vmblock filesystem.
 *
 *-----------------------------------------------------------------------------
 */

int
main(int argc,          // IN
     char *argv[])      // IN
{
   int c;
   int i;
   int result = EXIT_FAILURE;
   int mntRes = -1;
   struct stat statBuf;
   char *sourceDir;
   char *mountPoint;

   thisProgram = argv[0];

   /* Compute the base name of the program, too. */
   thisProgramBase = strrchr(thisProgram, '/');
   if (thisProgramBase != NULL) {
      thisProgramBase++;
   } else {
      thisProgramBase = thisProgram;
   }

   while ((c = getopt(argc, argv, "hvV")) != -1) {
      switch (c) {
      case 'h':
         PrintUsage(stdout);
         result = EXIT_SUCCESS;
         goto out;

      case 'v':
         beVerbose = TRUE;
         break;

      case 'V':
         PrintVersion();
         result = EXIT_SUCCESS;
         goto out;

      case '?':
      default:
         PrintUsage(stderr);
         goto out;
      }
   }

   LOG("Original command line: \"%s", thisProgram);
   for (i = 1; i < argc; i++) {
      LOG(" %s", argv[i]);
   }
   LOG("\"\n");

   /* After getopt_long(3), optind is the first non-option argument. */
   if (argc != optind + 2) {
      fprintf(stderr, "Error: invalid number of arguments\n");
      PrintUsage(stderr);
      goto out;
   }

   sourceDir = argv[optind];
   mountPoint = argv[optind + 1];

   /* Do some confidence checks on our desired mount point. */
   if (stat(mountPoint, &statBuf)) {
      perror("Error: cannot stat mount point");
      goto out;
   }

   if (S_ISDIR(statBuf.st_mode) == 0) {
      fprintf(stderr,
              "Error: mount point \"%s\" is not a directory\n", mountPoint);
      goto out;
   }

   if (access(mountPoint, X_OK) < 0) {
      fprintf(stderr,
              "Error: no access rights to mount point \"%s\"\n", mountPoint);
      goto out;
   }

   /* Do the same checks on the source directory. */
   if (stat(sourceDir, &statBuf)) {
      perror("Error: cannot stat source directory");
      goto out;
   }

   if (S_ISDIR(statBuf.st_mode) == 0) {
      fprintf(stderr, "Error: source \"%s\" is not a directory\n", sourceDir);
      goto out;
   }

   if (access(sourceDir, X_OK) < 0) {
      fprintf(stderr, "Error: no access rights to source \"%s\"\n", sourceDir);
      goto out;
   }

   /* Go! */
#if defined(sun)
   mntRes = mount(sourceDir, mountPoint, MS_DATA, VMBLOCK_FS_NAME);
#elif defined(__FreeBSD__)
   {
      struct iovec iov[] = {
         { .iov_base = "fstype", .iov_len = sizeof "fstype" },
         { .iov_base = VMBLOCK_FS_NAME, .iov_len = sizeof VMBLOCK_FS_NAME },
         { .iov_base = "fspath", .iov_len = sizeof "fspath" },
         { .iov_base = mountPoint, .iov_len = strlen(mountPoint) + 1 },
         { .iov_base = "target", .iov_len = sizeof "target" },
         { .iov_base = sourceDir, .iov_len = strlen(sourceDir) + 1 }
      };
      mntRes = nmount(iov, ARRAYSIZE(iov), MNT_NOSUID);
   }
#else
#error "Unsupported OS"
#endif

   if (mntRes) {
      perror("Error: cannot mount filesystem");
      goto out;
   }

   result = EXIT_SUCCESS;

out:
   return result;
}

   07070100000484000081A40000000000000000000000016822550500000644000000000000000000000000000000000000004B00000000open-vm-tools-12.5.2/open-vm-tools/vmblockmounter/vmblockmounter_version.h    /*********************************************************
 * Copyright (C) 2011-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmblockmounter_version.h --
 *
 * Version definitions for the vmblock mount helper application.
 */

#ifndef _VMBLOCKMOUNTER_VERSION_H_
#define _VMBLOCKMOUNTER_VERSION_H_

/*
 * This component's version is coupled with Tools versioning. The effect
 * is that the version increments with each build, and with each Tools
 * version bump. If and when it becomes necessary to version the component
 * manually, make sure that the version is bumped any time the component or
 * its dependencies are changed.
 */
#include "vm_tools_version.h"
#define VMBLOCKMOUNTER_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define VMBLOCKMOUNTER_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _VMBLOCKMOUNTER_VERSION_H_ */
07070100000485000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002F00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse    07070100000486000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/COPYING    		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   07070100000487000081A4000000000000000000000001682255050000087D000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/Makefile.am    ################################################################################
### Copyright (c) 2015-2016,2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmhgfs-fuse

AM_CFLAGS =
AM_CFLAGS += @FUSE_CPPFLAGS@
AM_CFLAGS += @FUSE3_CPPFLAGS@
AM_CFLAGS += @GLIB2_CPPFLAGS@

vmhgfs_fuse_LDADD =
vmhgfs_fuse_LDADD += @FUSE_LIBS@
vmhgfs_fuse_LDADD += @FUSE3_LIBS@
vmhgfs_fuse_LDADD += @GLIB2_LIBS@
vmhgfs_fuse_LDADD += @VMTOOLS_LIBS@

# The linker processes the libraries in sequence, and order matters here.
vmhgfs_fuse_LDADD += ../lib/hgfs/libHgfs.la
vmhgfs_fuse_LDADD += ../lib/hgfsBd/libHgfsBd.la
vmhgfs_fuse_LDADD += ../lib/rpcOut/libRpcOut.la
vmhgfs_fuse_LDADD += ../lib/message/libMessage.la
vmhgfs_fuse_LDADD += ../lib/backdoor/libBackdoor.la

vmhgfs_fuse_LDADD += ../lib/string/libString.la

vmhgfs_fuse_SOURCES =
vmhgfs_fuse_SOURCES += bdhandler.c
vmhgfs_fuse_SOURCES += cache.c
vmhgfs_fuse_SOURCES += config.c
vmhgfs_fuse_SOURCES += dir.c
vmhgfs_fuse_SOURCES += file.c
vmhgfs_fuse_SOURCES += filesystem.c
vmhgfs_fuse_SOURCES += fsutil.c
vmhgfs_fuse_SOURCES += link.c
vmhgfs_fuse_SOURCES += main.c
vmhgfs_fuse_SOURCES += request.c
vmhgfs_fuse_SOURCES += session.c
vmhgfs_fuse_SOURCES += transport.c

#vmhgfs_fuse_SOURCES += stubs.c
vmhgfs_fuse_SOURCES += $(top_srcdir)/lib/stubs/stub-debug.c
vmhgfs_fuse_SOURCES += $(top_srcdir)/lib/stubs/stub-log.c
vmhgfs_fuse_SOURCES += $(top_srcdir)/lib/stubs/stub-panic.c

   07070100000488000081A4000000000000000000000001682255050000196A000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/bdhandler.c    /*********************************************************
 * Copyright (C) 2013,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * bdhandler.c --
 *
 * Background thread for handling backdoor requests and replies.
 */

/* Must come before any kernel header file. */


#include "bdhandler.h"
#include "hgfsBd.h"
#include "hgfsProto.h"
#include "module.h"
#include "request.h"
#include "rpcout.h"
#include "transport.h"
#include "vm_assert.h"

static HgfsTransportChannel bdChannel;


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsBdChannelOpen --
 *
 *      Open the backdoor in an idempotent way.
 *
 * Results:
 *      Existing or updated channel status, HGFS_CHANNEL_CONNECTED on success.
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static HgfsChannelStatus
HgfsBdChannelOpen(HgfsTransportChannel *channel) // IN: Channel
{
   pthread_mutex_lock(&channel->connLock);
   switch (channel->status) {
   case HGFS_CHANNEL_UNINITIALIZED:
      LOG(8, ("Backdoor uninitialized.\n"));
      break;
   case HGFS_CHANNEL_CONNECTED:
      LOG(8, ("Backdoor already connected.\n"));
      break;
   case HGFS_CHANNEL_NOTCONNECTED:
      if (HgfsBd_OpenBackdoor((RpcOut **)&channel->priv)) {
         LOG(8, ("Backdoor opened and connected.\n"));
         channel->status = HGFS_CHANNEL_CONNECTED;
         ASSERT(channel->priv != NULL);
      } else {
         LOG(8, ("ERROR: Backdoor cannot connect.\n"));
      }
      break;
   default:
      ASSERT(0); /* Not reached. */
      LOG(2, ("ERROR: Backdoor status %d is unknown resetting.\n",
              channel->status));
      channel->status = HGFS_CHANNEL_UNINITIALIZED;
   }

   pthread_mutex_unlock(&channel->connLock);
   return channel->status;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsBdChannelCloseInt --
 *
 *      Close the backdoor in an idempotent way.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsBdChannelCloseInt(HgfsTransportChannel *channel) // IN: Channel
{
   if (channel->status == HGFS_CHANNEL_CONNECTED) {
      ASSERT(channel->priv != NULL);
      HgfsBd_CloseBackdoor((RpcOut **)&channel->priv);
      ASSERT(channel->priv == NULL);
      channel->status = HGFS_CHANNEL_NOTCONNECTED;
   }
   LOG(8, ("Backdoor closed.\n"));
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsBdChannelClose --
 *
 *      Close the backdoor in an idempotent way.
 *
 * Results:
 *      None
 *
 * Side effects:
 *      None
 *
 *-----------------------------------------------------------------------------
 */

static void
HgfsBdChannelClose(HgfsTransportChannel *channel) // IN: Channel
{
   pthread_mutex_lock(&channel->connLock);
   HgfsBdChannelCloseInt(channel);
   pthread_mutex_unlock(&channel->connLock);
   LOG(8, ("Backdoor closed.\n"));
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsBdChannelSend --
 *
 *     Send a request via backdoor.
 *
 * Results:
 *     0 on success, negative error on failure.
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

static int
HgfsBdChannelSend(HgfsTransportChannel *channel, // IN: Channel
                  HgfsReq *req)                  // IN: request to send
{
   char const *replyPacket = NULL;
   size_t payloadSize;
   int ret;

   ASSERT(req);
   ASSERT(req->state == HGFS_REQ_STATE_UNSENT);
   ASSERT(req->payloadSize <= HgfsLargePacketMax(FALSE));

   pthread_mutex_lock(&channel->connLock);

   if (channel->status != HGFS_CHANNEL_CONNECTED) {
      LOG(6, ("Backdoor not opened.\n"));
      pthread_mutex_unlock(&channel->connLock);
      return -ENOTCONN;
   }

   payloadSize = req->payloadSize;
   LOG(8, ("Backdoor sending.\n"));
   ret = HgfsBd_Dispatch(channel->priv, HGFS_REQ_PAYLOAD(req), &payloadSize,
                         &replyPacket);
   if (ret == 0) {
      LOG(8, ("Backdoor reply received.\n"));
      /* Request sent successfully. Copy the reply and wake the client. */
      ASSERT(replyPacket);
      HgfsCompleteReq(req, replyPacket, payloadSize);
   } else {
      /* Map rpc failure to EIO. */
      ret = -EIO;
   }

   pthread_mutex_unlock(&channel->connLock);

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsBdChannelExit --
 *
 *     Tear down the channel.
 *
 * Results:
 *     None
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

static void
HgfsBdChannelExit(HgfsTransportChannel *channel)  // IN
{
   pthread_mutex_lock(&channel->connLock);
   HgfsBdChannelCloseInt(channel);
   channel->status = HGFS_CHANNEL_UNINITIALIZED;
   pthread_mutex_unlock(&channel->connLock);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsBdChannelInit --
 *
 *     Initialize backdoor channel.
 *
 * Results:
 *     Always return pointer to back door channel.
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

HgfsTransportChannel*
HgfsBdChannelInit(void)
{
   bdChannel.name = "backdoor";
   bdChannel.ops.open = HgfsBdChannelOpen;
   bdChannel.ops.close = HgfsBdChannelClose;
   bdChannel.ops.send = HgfsBdChannelSend;
   bdChannel.ops.recv = NULL;
   bdChannel.ops.exit = HgfsBdChannelExit;
   bdChannel.priv = NULL;
   pthread_mutex_init(&bdChannel.connLock, NULL);
   bdChannel.status = HGFS_CHANNEL_NOTCONNECTED;
   return &bdChannel;
}
  07070100000489000081A4000000000000000000000001682255050000045D000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/bdhandler.h    /*********************************************************
 * Copyright (C) 2013 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * bdhandler.h --
 *
 * Backdoor channel implementation.
 */

#ifndef _HGFS_DRIVER_BDHANDLER_H_
#define _HGFS_DRIVER_BDHANDLER_H_

#include "transport.h"

HgfsTransportChannel *HgfsBdChannelInit(void);

#endif // _HGFS_DRIVER_BDHANDLER_H_
   0707010000048A000081A40000000000000000000000016822550500002FBA000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/cache.c    /*********************************************************
 * Copyright (C) 2013-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * cache.c --
 *
 * Module-specific components of the vmhgfs driver.
 */
#include "module.h"
#if !defined(__FreeBSD__) && !defined(__SOLARIS__)
#include <glib.h>
#endif

/*
 * We make the default attribute cache timeout 1 second which is the same
 * as the FUSE driver.
 * This can be overridden with the mount option attr_timeout=T
 */
#define CACHE_TIMEOUT HGFS_DEFAULT_TTL
#define CACHE_PURGE_TIME 10
#define CACHE_PURGE_SLEEP_TIME 30
#define HASH_THRESHOLD_SIZE (2046 * 4)
#define HASH_PURGE_SIZE (HASH_THRESHOLD_SIZE / 2)
#include "cache.h"

/*
 * HgfsAttrCache, holds an entry for each path
 */

typedef struct HgfsAttrCache {
   HgfsAttrInfo attr; /* Attribute of a file or directory */
   uint64 changeTime; /* time the attribute was last updated */
   struct list_head list; /* used in linked list implementation */
   char path[0];      /* path of the file corresponding the the attr */
} HgfsAttrCache;


/* Head of the list */
struct HgfsAttrCache attrList;

/*Lock for accessing the attribute cache*/
static pthread_mutex_t HgfsAttrCacheLock = PTHREAD_MUTEX_INITIALIZER;

#if !defined(__FreeBSD__) && !defined(__SOLARIS__)
static void HgfsInvalidateParentsChildren(const char* parent);
#endif

/*
 * Lists are used to manage attribute cache in Solaris and FreeBSD,
 * HashTables are used in Linux. HashTables perform better and hence
 * once newer version of glib with hash tables are packaged for Solaris
 * and FreeBSD, this section will go away.
 */

#if defined(__FreeBSD__) || defined(__SOLARIS__)

/*
 *----------------------------------------------------------------------
 *
 * HgfsInitCache
 *
 *    Initializes the list attrList to cache attributes.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 *
 */

void
HgfsInitCache()
{
   INIT_LIST_HEAD(&attrList.list);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsGetAttrCache
 *
 *    Retrieves the attr from the list for a given path.
 *
 * Results:
 *    0 on success else -1 on error
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsGetAttrCache(const char* path,   //IN: Path of file or directory
                 HgfsAttrInfo *attr) //IN: Attribute for a given path
{
   HgfsAttrCache *tmp;
   int res = -1;
   int diff;

   pthread_mutex_lock(&HgfsAttrCacheLock);

   attrList.changeTime = HGFS_GET_TIME(time(NULL));
   list_for_each_entry(tmp, &attrList.list, list) {
      LOG(4, ("tmp->path = %d\n", tmp->path));
      if (strcmp(path, tmp->path) == 0) {
         LOG(4, ("cache hit. path = %s\n", tmp->path));

         diff = (HGFS_GET_TIME(time(NULL)) - tmp->changeTime) / 10000000;

         LOG(4, ("time since last updated is %d seconds\n", diff));
         if (diff <= CACHE_TIMEOUT) {
            *attr = tmp->attr;
            res = 0;
         }
         break;
      }
   }

   pthread_mutex_unlock(&HgfsAttrCacheLock);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsSetAttrCache
 *
 *    Updates the list with the given (key, attr)pair.
 *
 * Results:
 *    0 on success else negative value on error
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsSetAttrCache(const char* path,   //IN: Path of file or directory
                 HgfsAttrInfo *attr) //IN: Attribute for a given path
{
   HgfsAttrCache *tmp;
   int res = 0;

   pthread_mutex_lock(&HgfsAttrCacheLock);

   attrList.changeTime = HGFS_GET_TIME(time(NULL));
   list_for_each_entry (tmp, &attrList.list, list) {
      if (strcmp(path, tmp->path) == 0) {
         tmp->attr = *attr;
         tmp->changeTime = HGFS_GET_TIME(time(NULL));
         LOG(4, ("cache entry updated. path = %s\n", tmp->path));
         goto out;
      }
   }

   tmp = malloc(sizeof(HgfsAttrCache) + strlen(path) + 1);
   if (tmp == NULL) {
      res = -ENOMEM;
      goto out;
   }
   INIT_LIST_HEAD(&tmp->list);
   Str_Strcpy(tmp->path, path, strlen(path) + 1);
   tmp->attr = *attr;
   tmp->changeTime = HGFS_GET_TIME(time(NULL));
   list_add(&tmp->list, &attrList.list);
   LOG(4, ("cache entry added. path = %s\n", tmp->path));

out:
   pthread_mutex_unlock(&HgfsAttrCacheLock);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsInvalidateAttrCache
 *
 *    Invalidate the attribute list entry for a given path.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
HgfsInvalidateAttrCache(const char* path)      //IN: Path to file
{
   HgfsAttrCache *tmp;

   pthread_mutex_lock(&HgfsAttrCacheLock);

   list_for_each_entry (tmp, &attrList.list, list) {
      if (strcmp(path, tmp->path) == 0) {
         list_del(&tmp->list);
         free(tmp);
         break;
      }
   }

   pthread_mutex_unlock(&HgfsAttrCacheLock);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsPurgeCache:
 *
 *    This routine is called by an independent thread to purge the cache,
 *    deletion is based on time of last update.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void*
HgfsPurgeCache(void* unused) //IN: Thread argument
{
   HgfsAttrCache *tmp;
   HgfsAttrCache *prev;
   int diff;

   while (1)
   {
      sleep(CACHE_PURGE_SLEEP_TIME);

      pthread_mutex_lock(&HgfsAttrCacheLock);

      list_for_each_entry_safe (tmp, prev, &attrList.list, list) {
         diff = (HGFS_GET_TIME(time(NULL)) - tmp->changeTime) / 10000000;
         if (diff > CACHE_PURGE_TIME) {
            list_del (&tmp->list);
            free (tmp);
         }
      }

      pthread_mutex_unlock(&HgfsAttrCacheLock);
   }
   return 0;
}

#else


GHashTable *g_hash_table;

/*
 *----------------------------------------------------------------------
 *
 * HgfsInitCache
 *
 *    Creates a hash table with string as a key.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *----------------------------------------------------------------------
 *
 */

void
HgfsInitCache()
{
   g_hash_table = g_hash_table_new(g_str_hash, g_str_equal);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsGetAttrCache
 *
 *    Retrieves the attr from the HashTable for a given path.
 *
 * Results:
 *    0 on success else -1 on error
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsGetAttrCache(const char* path,   //IN: Path of file or directory
                 HgfsAttrInfo *attr) //IN: Attribute for a given path
{
   HgfsAttrCache *tmp;
   int res = -1;

   pthread_mutex_lock(&HgfsAttrCacheLock);

   tmp = (HgfsAttrCache *)g_hash_table_lookup(g_hash_table, path);
   if (tmp != NULL) {
      int diff;

      LOG(4, ("cache hit. path = %s\n", tmp->path));

      diff = (HGFS_GET_TIME(time(NULL)) - tmp->changeTime) / 10000000;
      LOG(4, ("time since last updated is %d seconds\n", diff));
      if ( diff <= CACHE_TIMEOUT ) {
         *attr = tmp->attr;
         res = 0;
      }
   }

   pthread_mutex_unlock(&HgfsAttrCacheLock);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsSetAttrCache
 *
 *    Updates the HashTable with the given (key, attr) pair.
 *
 * Results:
 *    0 on success else negative value on error
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsSetAttrCache(const char* path,         //IN: Path of file or directory
                 HgfsAttrInfo *attr)       //IN: Attribute for a given path
{
   HgfsAttrCache *tmp;
   int res = 0;

   pthread_mutex_lock(&HgfsAttrCacheLock);

   tmp = (HgfsAttrCache *)g_hash_table_lookup(g_hash_table, path);
   if (tmp != NULL) {
      tmp->attr = *attr;
      tmp->changeTime = HGFS_GET_TIME(time(NULL));
      goto out;
   }

   tmp = malloc(sizeof(HgfsAttrCache) + strlen(path) + 1);
   if (tmp == NULL) {
      res = -ENOMEM;
      goto out;
   }

   Str_Strcpy(tmp->path, path, strlen(path) + 1);
   tmp->attr = *attr;
   tmp->changeTime = HGFS_GET_TIME(time(NULL));

   g_hash_table_insert(g_hash_table, (gpointer)tmp->path, (gpointer)tmp);

out:
   pthread_mutex_unlock(&HgfsAttrCacheLock);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsInvalidateAttrCache
 *
 *    Invalidate the HashTable entry for a path.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
HgfsInvalidateAttrCache(const char* path)      //IN: Path to file
{
   HgfsAttrCache *tmp;

   pthread_mutex_lock(&HgfsAttrCacheLock);
   tmp = (HgfsAttrCache *)g_hash_table_lookup(g_hash_table, path);
   if (tmp != NULL) {
      tmp->changeTime = 0;
      if (tmp->attr.type == HGFS_FILE_TYPE_DIRECTORY) {
         HgfsInvalidateParentsChildren(tmp->path);
      }
   }
   pthread_mutex_unlock(&HgfsAttrCacheLock);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsInvalidateParentsChildren
 *
 *    This routine is called by the general function to invalidate a cache
 *    entry. If the entry is a directory this function is called to invalidate
 *    any cached children.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static void
HgfsInvalidateParentsChildren(const char* parent)      //IN: parent
{
   gpointer key, value;
   GHashTableIter iter;
   size_t parentLen = Str_Strlen(parent, PATH_MAX);

   LOG(4, ("Invalidating cache children for parent = %s\n",
           parent));

   g_hash_table_iter_init(&iter, g_hash_table);

   while (g_hash_table_iter_next(&iter, &key, &value)) {
      HgfsAttrCache *child = (HgfsAttrCache *)value;

      if (Str_Strncasecmp(parent, child->path, parentLen) == 0) {
         LOG(10, ("Invalidating cache child = %s\n", child->path));
         child->changeTime = 0;
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsPurgeCache
 *
 *    This routine is called by an independent thread to purge the cache,
 *    for performance reasons, the deletion is done in random based
 *    on the order of iteration.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void*
HgfsPurgeCache(void* unused)      //IN: Thread argument
{
   gpointer key, value;
   GHashTableIter iter;

   while (1) {
      sleep(CACHE_PURGE_SLEEP_TIME);

      pthread_mutex_lock(&HgfsAttrCacheLock);

      if (g_hash_table_size(g_hash_table) >= HASH_THRESHOLD_SIZE) {
         g_hash_table_iter_init(&iter, g_hash_table);
         while (g_hash_table_iter_next(&iter, &key, &value) &&
               (g_hash_table_size(g_hash_table) >= HASH_PURGE_SIZE)) {
            g_hash_table_iter_remove(&iter);
            free(value);
         }
      }

      pthread_mutex_unlock(&HgfsAttrCacheLock);
   }
   return 0;
}
#endif
  0707010000048B000081A400000000000000000000000168225505000004D2000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/cache.h    /*********************************************************
 * Copyright (C) 2013 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * cache.h --
 *
 * Declarations of cache management functions
 */

#ifndef _HGFS_DRIVER_CACHE_H_
#define _HGFS_DRIVER_CACHE_H_

int HgfsGetAttrCache(const char* path, HgfsAttrInfo *attr);
int HgfsSetAttrCache(const char* path, HgfsAttrInfo *attr);
void HgfsInitCache();
void* HgfsPurgeCache(void*);
void HgfsInvalidateAttrCache(const char* path);

#endif
  0707010000048C000081A4000000000000000000000001682255050000394D000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/config.c   /*********************************************************
 * Copyright (c) 2015-2018,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * config.c --
 *
 */

#include "module.h"
#include <fuse_lowlevel.h>
#include <sys/utsname.h>

#ifdef VMX86_DEVEL
int LOGLEVEL_THRESHOLD = 4;
#endif

/* Kernel version as integer macro */
#define KERNEL_RELEASE(x, y, z) (((x) << 16) | ((y) << 8) | (z))

/*
 * System state for eligibility for HGFS FUSE to be enabled.
 * See comments in the Usage function which must match the enum
 * and messages table below.
 */

#define HGFS_DEFS \
   DEFINE_HFGS_DEF(HGFS_SYSCOMPAT_FUSE_ENABLED,     "HGFS FUSE client enabled") \
   DEFINE_HFGS_DEF(HGFS_SYSCOMPAT_OS_NOT_SUPPORTED, "HGFS FUSE client not supported for this OS version") \
   DEFINE_HFGS_DEF(HGFS_SYSCOMPAT_OS_NO_FUSE,       "HGFS FUSE client needs FUSE environment")

#undef DEFINE_HFGS_DEF

#define DEFINE_HFGS_DEF(a, b) a,

typedef enum {
   HGFS_DEFS
} HgfsSystemCompatibility;

#undef DEFINE_HFGS_DEF

#define DEFINE_HFGS_DEF(a, b) b,

const char *HgfsSystemCompatibilityMsg[] = {
   HGFS_DEFS
};

#undef DEFINE_HFGS_DEF

enum {
   KEY_HELP,
   KEY_VERSION,
   KEY_BIG_WRITES,
   KEY_NO_BIG_WRITES,
   KEY_ENABLED_FUSE,
};

#define VMHGFS_OPT(t, p, v) { t, offsetof(struct vmhgfsConfig, p), v }

const struct fuse_opt vmhgfsOpts[] = {
#ifdef VMX86_DEVEL
     VMHGFS_OPT("--loglevel %i",    logLevel, 4),
     VMHGFS_OPT("-l %i",            logLevel, 4),
#endif
     /* We will change the default value, unless it is specified explicitly. */
#if FUSE_MAJOR_VERSION != 3
     FUSE_OPT_KEY("big_writes",     KEY_BIG_WRITES),
     FUSE_OPT_KEY("nobig_writes",   KEY_NO_BIG_WRITES),
#endif

     FUSE_OPT_KEY("-V",             KEY_VERSION),
     FUSE_OPT_KEY("--version",      KEY_VERSION),
     FUSE_OPT_KEY("-h",             KEY_HELP),
     FUSE_OPT_KEY("--help",         KEY_HELP),
     FUSE_OPT_KEY("-e",             KEY_ENABLED_FUSE),
     FUSE_OPT_KEY("--enabled",      KEY_ENABLED_FUSE),
     FUSE_OPT_END
};


/*
 *----------------------------------------------------------------------
 *
 * Usage
 *
 *    Show Usage.
 *
 * Results:
 *    None.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */
static void
Usage(char *prog_name)  // IN
{
   fprintf(stderr,
           "Usage:\t%s sharedir mountpoint [options]\nExamples:\n"
           "\t%s .host:/ /mnt/hgfs\n"
           "\t%s .host:/foo/bar /mnt/bar\n\n"
           "general options:\n"
           "    -o opt,[opt...]        mount options\n"
           "    -h   --help            print help\n"
           "    -V   --version         print version\n"
           "    -e   --enabled         check if system is enabled\n"
           "                           for the HGFS FUSE client. Exits with:\n"
           "                           0 - system is enabled for HGFS FUSE\n"
           "                           1 - system OS version is not supported for HGFS FUSE\n"
           "                           2 - system needs FUSE packages for HGFS FUSE\n"
           "\n"
#ifdef VMX86_DEVEL
           "vmhgfs options:\n"
           "    -l   --loglevel NUM    set loglevel=NUM only available in debug build.\n"
           "\n"
#endif
           , prog_name, prog_name, prog_name);
}

#define LIB_MODULEPATH         "/lib/modules"
#define MODULES_DEP            "modules.dep"
#if FUSE_MAJOR_VERSION == 3
#define FUSER_MOUNT_BIN        "/bin/fusermount3"
#define FUSER_MOUNT_USR_BIN    "/usr/bin/fusermount3"
#else
#define FUSER_MOUNT_BIN        "/bin/fusermount"
#define FUSER_MOUNT_USR_BIN    "/usr/bin/fusermount"
#endif
#define PROC_FILESYSTEMS       "/proc/filesystems"
#define FUSER_KERNEL_FS        "fuse"

/*
 *----------------------------------------------------------------------
 *
 * SysCompatFusermountCheck
 *
 *    Test if the FUSE fusermount command is installed.
 *
 * Results:
 *    FALSE the HGFS FUSE client is not installed, TRUE if it is installed.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */
static Bool
SysCompatFusermountCheck(void)  // IN:
{
   Bool fusermountExists = TRUE;
   int accessResult;

   /*
    * Check both locations as it varies e.g. SUSE uses "/usr/bin"
    * and Ubuntu uses "/bin".
    */
   accessResult = access(FUSER_MOUNT_BIN, F_OK);
   if (accessResult == -1) {
      accessResult = access(FUSER_MOUNT_USR_BIN, F_OK);
      if (accessResult == -1) {
         fprintf(stderr, "failed to access %s or %s %d\n",
                 FUSER_MOUNT_BIN, FUSER_MOUNT_USR_BIN, errno);
         fusermountExists = FALSE;
      }
   }

   return fusermountExists;
}


/*
 *----------------------------------------------------------------------
 *
 * SysCompatIsInstalledFuse
 *
 *    Test if the FUSE file system is installed but not yet loaded.
 *
 * Results:
 *    FALSE the HGFS FUSE client is not installed, TRUE if it is installed.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */
static Bool
SysCompatIsInstalledFuse(char *utsRelease)  // IN: kernel release
{
   char *fuseFilesystem;
   char *modulesDep;
   char *modulesDepData = NULL;
   struct stat modulesDepStat;
   size_t modulesDepLen;
   size_t modulesDepDataSize;
   size_t modulesDepFileSize;
   int modulesDepFd = -1;
   Bool installedFuse = FALSE;
   int statResult;

   modulesDep = Str_Asprintf(&modulesDepLen, "%s/%s/%s",
                             LIB_MODULEPATH, utsRelease, MODULES_DEP);

   if (modulesDep == NULL) {
      fprintf(stderr, "failed to create path str for %s\n", MODULES_DEP);
      goto exit;
   }

   modulesDepFd = open(modulesDep, O_RDONLY);
   if (modulesDepFd == -1) {
      fprintf(stderr, "failed to open %s %d\n", modulesDep, errno);
      goto exit;
   }

   statResult = fstat(modulesDepFd, &modulesDepStat);
   if (statResult == -1) {
      fprintf(stderr, "failed to stat %s %d\n", modulesDep, errno);
      goto exit;
   }
   modulesDepFileSize = (size_t)modulesDepStat.st_size;
   modulesDepData = malloc(modulesDepFileSize + 1);
   if (modulesDepData == NULL) {
      fprintf(stderr, "failed to alloc data buf for %s\n", modulesDep);
      goto exit;
   }

   modulesDepDataSize = read(modulesDepFd,
                             modulesDepData,
                             modulesDepFileSize);

   if (modulesDepDataSize != modulesDepFileSize) {
      fprintf(stderr, "failed to read %s %d\n", modulesDep, errno);
      goto exit;
   }

   modulesDepData[modulesDepDataSize] = '\0';
   fuseFilesystem = strstr(modulesDepData,
                           FUSER_KERNEL_FS);

   installedFuse = (fuseFilesystem != NULL);

exit:
   if (modulesDepFd != -1) {
      close(modulesDepFd);
   }
   free(modulesDepData);
   free(modulesDep);
   return installedFuse;
}


/*
 *----------------------------------------------------------------------
 *
 * SysCompatIsRegisteredFuse
 *
 *    Test if the FUSE file system is registered.
 *
 * Results:
 *    FALSE the FUSE is not registered, TRUE if it is.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */
static Bool
SysCompatIsRegisteredFuse(void)  // IN
{
   Bool registeredFuse = FALSE;
   int procFilesystemsFd;
   char procFilesystems[4096];
   char *fuseFilesystem;
   ssize_t procFilesystemsDataSize;

   procFilesystemsFd = open(PROC_FILESYSTEMS, O_RDONLY);
   if (procFilesystemsFd == -1) {
      fprintf(stderr, "failed to open %s %d\n", PROC_FILESYSTEMS, errno);
      goto exit;
   }

   procFilesystemsDataSize = read(procFilesystemsFd,
                                  procFilesystems,
                                  sizeof procFilesystems - 1);

   if (procFilesystemsDataSize < sizeof FUSER_KERNEL_FS) {
      fprintf(stderr, "failed to read %s %d\n", PROC_FILESYSTEMS, errno);
      goto exit;
   }

   procFilesystems[procFilesystemsDataSize] = '\0';
   fuseFilesystem = strstr(procFilesystems,
                           FUSER_KERNEL_FS);


   registeredFuse = (fuseFilesystem != NULL);

exit:
   if (procFilesystemsFd != -1) {
      close(procFilesystemsFd);
   }
   return registeredFuse;
}


/*
 *----------------------------------------------------------------------
 *
 * SysCompatCheck
 *
 *    Check if the system is compatible for FUSE.
 *
 * Results:
 *    0 the system compatible so FUSE is enabled,
 *    1 the system OS version is not supported (disabled)
 *    2 the system does not have FUSE installed.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */
static HgfsSystemCompatibility
SysCompatCheck(char *prog_name)  // IN
{
   struct utsname buf;
   HgfsSystemCompatibility sysCompat = HGFS_SYSCOMPAT_OS_NOT_SUPPORTED;
   char *utsRelease = NULL;
   int k[3] = {0};
   int release;

   if (uname(&buf) == -1) {
      fprintf(stderr, "%s: failed to retrieve kernel info %d\n", __func__, errno);
      goto exit;
   }

   utsRelease = strdup(buf.release);

   if (sscanf(utsRelease, "%d.%d.%d", &k[0], &k[1], &k[2]) == 0) {
      fprintf(stderr, "%s: failed to extract kernel release\n", __func__);
      goto exit;
   } else {
      release = KERNEL_RELEASE(k[0], k[1], k[2]);
   }

   if (release < KERNEL_RELEASE(3, 10, 0)) {
      fprintf(stderr, "%s: incompatible kernel version %02d.%02d.%02d\n",
              __func__, k[0], k[1], k[2]);
      goto exit;
   }

   if (!SysCompatIsRegisteredFuse()) {
      /* Check if FUSE is installed but not loaded yet. */
      if (!SysCompatIsInstalledFuse(utsRelease)) {
         fprintf(stderr, "%s: failed FUSE install checks\n", __func__);
         sysCompat = HGFS_SYSCOMPAT_OS_NO_FUSE;
         goto exit;
      }
   }

   /*
    * Finally check the system paths to see if the user has the
    * needed fusemount binary installed.
    */
   if (!SysCompatFusermountCheck()) {
      sysCompat = HGFS_SYSCOMPAT_OS_NO_FUSE;
      goto exit;
   }

   sysCompat = HGFS_SYSCOMPAT_FUSE_ENABLED;

exit:
   free(utsRelease);
   fprintf(stderr, "%s: %d - %s\n", prog_name, sysCompat,
           HgfsSystemCompatibilityMsg[sysCompat]);
   return sysCompat;
}


/*
 *----------------------------------------------------------------------
 *
 * vmhgfsOptProc
 *
 *    Process options the fuse way. check the fuse document for details.
 *
 * Results:
 *    -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
 *
 * Side effects:
 *    In KEY_HELP or KEY_VERSION cases, they will be processed and
 *    the process will exit immediately.
 *
 *----------------------------------------------------------------------
 */

int
vmhgfsOptProc(void *data,                // IN
              const char *arg,           // IN
              int key,                   // IN
              struct fuse_args *outargs) // OUT
{
#if FUSE_MAJOR_VERSION != 3
   struct vmhgfsConfig *config = data;
#endif

   switch (key) {
   case FUSE_OPT_KEY_NONOPT:
      if (!gState->basePath &&
         strncmp(arg, HOSTNAME_PREFIX, strlen(HOSTNAME_PREFIX)) == 0) {
         const char *p = arg + strlen(HOSTNAME_PREFIX);
         char *q;

         /*
         * remove hostname and trailing spaces. e.g.
         * ".host:/" => ""
         * ".host:/abc/" => "/abc"
         */
         gState->basePath = strdup(p);
         q = gState->basePath + strlen(gState->basePath) - 1;
         if (*q == '/' && q >= gState->basePath) {
            *q = '\0';
         }
         gState->basePathLen = strlen(gState->basePath);
         if (gState->basePathLen == 0) {
            free(gState->basePath);
            gState->basePath = NULL;
         }
         return 0;
      }
      return 1;

#if FUSE_MAJOR_VERSION != 3
   case KEY_BIG_WRITES:
      config->addBigWrites = TRUE;
      return 0;

   case KEY_NO_BIG_WRITES:
      config->addBigWrites = FALSE;
      return 0;
#endif

   case KEY_HELP:
      Usage(outargs->argv[0]);
#if FUSE_MAJOR_VERSION != 3
      fuse_opt_add_arg(outargs, "-ho");
      fuse_main(outargs->argc, outargs->argv, NULL, NULL);
#else
      fprintf(stdout, "FUSE options:\n");
      fuse_cmdline_help();
      fuse_lib_help(outargs);
#endif
      exit(1);

   case KEY_ENABLED_FUSE: {
      HgfsSystemCompatibility sysCompat;
      sysCompat = SysCompatCheck(outargs->argv[0]);
      exit(sysCompat);
   }

   case KEY_VERSION:
      fprintf(stderr, "%s: version %s\n\n", outargs->argv[0],
              VMHGFS_DRIVER_VERSION_STRING);
      fuse_opt_add_arg(outargs, "--version");
      fuse_main(outargs->argc, outargs->argv, NULL, NULL);
      exit(0);
   }
   return 1;
}


/*
 *----------------------------------------------------------------------
 *
 * vmhgfsPreprocessArgs
 *
 *    Process arguments we care about before passing them on to fuse.
 *
 * Results:
 *    Returns -1 on error, 0 on success.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
vmhgfsPreprocessArgs(struct fuse_args *outargs)    // IN/OUT
{
   struct vmhgfsConfig config;
   int res;

   gState->basePath = NULL;
   gState->basePathLen = 0;

   VMTools_LoadConfig(NULL, G_KEY_FILE_NONE, &gState->conf, NULL);
   VMTools_ConfigLogging(G_LOG_DOMAIN, gState->conf, FALSE, FALSE);

#ifdef VMX86_DEVEL
   config.logLevel = LOGLEVEL_THRESHOLD;
#endif
#if defined(__APPLE__) || FUSE_MAJOR_VERSION == 3
   /* osxfuse and fuse3 does not have option 'big_writes'. */
   config.addBigWrites = FALSE;
#else
   config.addBigWrites = TRUE;
#endif

   res = fuse_opt_parse(outargs, &config, vmhgfsOpts, vmhgfsOptProc);
   if (res != 0) {
      goto exit;
   }

#ifdef VMX86_DEVEL
   LOGLEVEL_THRESHOLD = config.logLevel;
#endif
   /* Default option changes for vmhgfs fuse client. */
   if (config.addBigWrites) {
      res = fuse_opt_add_arg(outargs, "-obig_writes");
      if (res != 0) {
         goto exit;
      }
   }

exit:
   return res;
}
   0707010000048D000081A40000000000000000000000016822550500000518000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/config.h   /*********************************************************
 * Copyright (C) 2015-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * config.h --
 *
 */

#ifndef _VMHGFS_FUSE_CONFIG_H_
#define _VMHGFS_FUSE_CONFIG_H_

#define HOSTNAME_PREFIX ".host:"

struct vmhgfsConfig {
#ifdef VMX86_DEVEL
   int logLevel;
#endif
   int addBigWrites;
   int addAllowOther;
};

int vmhgfsOptProc(void *data, const char *arg,
                  int key, struct fuse_args *outargs);

int vmhgfsPreprocessArgs(struct fuse_args *outargs);

#endif //_VMHGFS_FUSE_CONFIG_H_
0707010000048E000081A400000000000000000000000168225505000074E0000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/dir.c  /*********************************************************
 * Copyright (c) 2013,2019,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * dir.c --
 *
 * File operations for the hgfs driver.
 */
#include "module.h"


#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)




/*
 *----------------------------------------------------------------------
 *
 * 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(const char *path,    // IN: Path of the dir to open
                       HgfsOp opUsed,       // IN: Op to be used
                       HgfsReq *req)        // IN/OUT: Packet to write into
{
   size_t reqSize;

   ASSERT(path);
   ASSERT(req);
   LOG(4, ("Path = %s \n", path));
   switch (opUsed) {
   case HGFS_OP_SEARCH_OPEN_V3: {
      int result;
      HgfsRequestSearchOpenV3 *requestV3 = HgfsGetRequestPayload(req);

      requestV3->dirName.flags = 0;
      requestV3->dirName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
      requestV3->dirName.fid = HGFS_INVALID_HANDLE;
      requestV3->reserved = 0;
      reqSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();
      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (reqSize - 1),
                                requestV3->dirName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed\n"));
         return -EINVAL;
      }
      LOG(4, ("After conversion = %s\n", requestV3->dirName.name));
      requestV3->dirName.length = result;
      reqSize += result;
      break;
   }

   case HGFS_OP_SEARCH_OPEN: {
      int result;
      HgfsRequestSearchOpen *request;

      request = (HgfsRequestSearchOpen *)(HGFS_REQ_PAYLOAD(req));

      reqSize = sizeof *request;
      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (reqSize - 1),
                                request->dirName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed\n"));
         return -EINVAL;
      }
      LOG(4, ("After conversion = %s\n", request->dirName.name));
      request->dirName.length = result;
      reqSize += result;
      break;
   }

   default:
      LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   req->payloadSize = reqSize;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   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. If the Open
 *    succeeds, we store the search handle sent by the server in
 *     the handle parameter so it can be reused later.
 *
 * Results:
 *    Returns zero on success, error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsDirOpen(const char* path,       // IN: Path of dir to open
            HgfsHandle* handle)     // OUT: Handle to the dir
{
   HgfsReq *req;
   int result;
   HgfsOp opUsed;
   HgfsStatus replyStatus;

   ASSERT(path);
   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request.\n"));
      result = -ENOMEM;
      goto out;
   }

retry:
   opUsed = hgfsVersionSearchOpen;

   result = HgfsPackDirOpenRequest(path, opUsed, req);
   if (result != 0) {
      LOG(4, ("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 = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {
      case 0:
         if (opUsed == HGFS_OP_SEARCH_OPEN_V3) {
            HgfsReplySearchOpenV3 *requestV3 = HgfsGetReplyPayload(req);
            *handle = requestV3->search;
         } else {
            HgfsReplySearchOpen *request = (HgfsReplySearchOpen *)HGFS_REQ_PAYLOAD(req);
            *handle = request->search;
         }
         LOG(6, ("Set handle to %u\n", *handle));
         break;
      case -EPROTO:
         /* Retry with older version(s). Set globally. */
         if (opUsed == HGFS_OP_SEARCH_OPEN_V3) {
            LOG(4, ("Version 3 not supported. Falling back to version 1.\n"));
            hgfsVersionSearchOpen = HGFS_OP_SEARCH_OPEN;
            goto retry;
         }
         LOG(4, ("Server returned error: %d, opUsed = %d\n", result, opUsed));
         break;
      default:
         LOG(4, ("Server returned error: %d\n", result));
         break;
      }
   } else if (result == -EIO) {
      LOG(4, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsReadDirFromReply --
 *
 *    This function reads directory entries from the reply packet
 *    contained in the specified request structure. It calls filldir
 *    to copy each entry into the vfsDirent buffer.
 *
 *    For V1 and V2 search read reply, only one entry is returned from
 *    server, while for V3 we may have multiple directory entries. The
 *    number of entries can be read from the reply packet.
 *
 * Results:
 *    0 on success, anything else on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
HgfsReadDirFromReply(uint32 *f_pos,     // IN/OUT: Offset
                     void *vfsDirent,   // OUT: Buffer to copy dentries into
                     fuse_fill_dir_t filldir, // IN:  Filler function
                     HgfsReq *req,      // IN:  The request containing reply
                     HgfsOp opUsed,     // IN:  request type
                     Bool *done)        // OUT: Set true when there are no
                                        //      more entries
{
   uint32 replyCount;
   HgfsAttrInfo attr;
   HgfsDirEntry *hgfsDirent = NULL; /* Only for V3. */
   char *escName;                   /* Buffer for escaped version of name */
   size_t escNameLength = NAME_MAX + 1;
   int result = 0;

   ASSERT(req);

   escName = malloc(escNameLength);
   if (!escName) {
      LOG(4, ("Out of memory allocating escaped name buffer.\n"));
      return  -ENOMEM;
   }

   replyCount = 1;
   if (opUsed == HGFS_OP_SEARCH_READ_V3) {
      HgfsReplySearchReadV3 *replyV3 = HgfsGetReplyPayload(req);

      replyCount = replyV3->count;
      hgfsDirent = (HgfsDirEntry *)replyV3->payload;
      if (replyCount == 0) {
         /* We're at the end of the directory. */
         *done = TRUE;
         goto out;
      }
   }

   LOG(8, ("Reply counter %u, opUsed %d\n", replyCount, opUsed));
   while (replyCount-- > 0) {
      void *rawAttr;
      char *fileName;
      uint32 fileNameLength;
      ino_t ino;
      uint32 d_type;
      struct stat st;

      switch(opUsed) {
      case HGFS_OP_SEARCH_READ_V3: {
         rawAttr =  &hgfsDirent->attr;
         fileName = hgfsDirent->fileName.name;
         fileNameLength = hgfsDirent->fileName.length;
         break;
      }
      case HGFS_OP_SEARCH_READ_V2: {
         HgfsReplySearchReadV2 *replyV2;
         replyV2 = (HgfsReplySearchReadV2 *)(HGFS_REQ_PAYLOAD(req));
         rawAttr = &replyV2->attr;
         fileName = replyV2->fileName.name;
         fileNameLength = replyV2->fileName.length;
         break;
      }
      case HGFS_OP_SEARCH_READ: {
         HgfsReplySearchRead *replyV1;
         replyV1 = (HgfsReplySearchRead *)(HGFS_REQ_PAYLOAD(req));
         rawAttr = &replyV1->attr;
         fileName = replyV1->fileName.name;
         fileNameLength = replyV1->fileName.length;
         break;
      }
      default:
         LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
         result = -EPROTO;
         goto out;
      }

      /* Make sure name length is legal. */
      if (fileNameLength > NAME_MAX) {
         /*
          * Skip dentry if its name is too long. We don't try to read next
          * entry in this reply. It is ok since it happens rarely.
          */
         (*f_pos)++;
         result = -ENAMETOOLONG;
         goto out;
      } else if (fileNameLength == 0) {
         /* We're at the end of the directory. */
         *done = TRUE;
         goto out;
      }
      result = HgfsUnpackCommonAttr(rawAttr, opUsed, &attr);
      if (result != 0) {
         goto out;
      }

      /*
       * 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 = HgfsEscape_Do(fileName,
                             fileNameLength,
			     escNameLength,
			     escName);

      /*
       * 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.
       *
       * HgfsEscape_Do 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.
          */
         LOG(4, ("HgfsEscape_Do() returns %d\n", result));
         (*f_pos)++;
         continue;
      }

      /* Reuse fileNameLength to store the filename length after escape. */
      fileNameLength = 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;
      }

      ino = attr.hostFileId;
      memset(&st, 0, sizeof(st));
      st.st_blksize = HGFS_BLOCKSIZE;
      st.st_blocks = HgfsCalcBlockSize(attr.size);
      st.st_size = attr.size;
      st.st_ino = ino;
      st.st_mode = d_type << 12;
#if FUSE_MAJOR_VERSION == 3
      result = filldir(vfsDirent, escName, &st, 0, 0);
#else
      result = filldir(vfsDirent, escName, &st, 0);
#endif

      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.
          *
          * The filldir errors are normal when the user buffer is small,
          * so we return ENOSPC and let the caller treat it specially.
          */
         LOG(4, ("filldir() returns %d\n", result));
         result = -ENOSPC;
         break;
      }
      (*f_pos)++;

      /* For V3, there may be remaining entries to process. */
      if (opUsed == HGFS_OP_SEARCH_READ_V3) {
         if (hgfsDirent->nextEntry > 0) {
            ASSERT(replyCount > 0);
         }
         hgfsDirent = (HgfsDirEntry *)((unsigned long)hgfsDirent +
                                       hgfsDirent->nextEntry);
      }
   }

out:
   free(escName);
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsRequestDirEntries --
 *
 *    Get the directory entries with the given offset from the server.
 *    The server may return 0, 1, or more than 2 entries depending on
 *    the protocol version.
 *
 * Results:
 *    Returns zero on success, negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
HgfsRequestDirEntries(HgfsHandle searchHandle, // IN: Handle to dir
                      uint32 offset,           // IN: Offset of next dentry to get
                      HgfsReq *req,            // IN/OUT: the request
                      HgfsOp *opUsed)          // OUT: request type
{
   HgfsStatus replyStatus;
   int result = 0;

  retry:
   *opUsed = hgfsVersionSearchRead;
   if (*opUsed == HGFS_OP_SEARCH_READ_V3) {
      HgfsRequestSearchReadV3 *request = HgfsGetRequestPayload(req);

      request->search = searchHandle;
      request->offset = offset;
      request->reserved = 0;
      request->flags = 0 /* HGFS_SEARCH_READ_FLAG_MULTIPLE_REPLY */;
      req->payloadSize = sizeof(*request) + HgfsGetRequestHeaderSize();

   } else {
      HgfsRequestSearchRead *request;

      request = (HgfsRequestSearchRead *)(HGFS_REQ_PAYLOAD(req));
      request->search = searchHandle;
      request->offset = offset;
      req->payloadSize = sizeof *request;
   }

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, *opUsed);

   /* Send the request and process the reply. */
   result = HgfsSendRequest(req);
   if (result == 0) {
      LOG(6, ("Got reply\n"));
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      /* Retry with older version(s). Set globally. */
      if (result == -EPROTO) {
         if (*opUsed == HGFS_OP_SEARCH_READ_V3) {
            LOG(4, ("Version 3 not supported. Falling back to version 2.\n"));
            hgfsVersionSearchRead = HGFS_OP_SEARCH_READ_V2;
            goto retry;
         } else if (*opUsed == HGFS_OP_SEARCH_READ_V2) {
            LOG(4, ("Version 2 not supported. Falling back to version 1.\n"));
            hgfsVersionSearchRead = HGFS_OP_SEARCH_READ;
            goto retry;
         }
      }
   } else if (result == -EIO) {
      LOG(4, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }

   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.
 *
 * 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
 *
 *----------------------------------------------------------------------
 */

int
HgfsReaddir(HgfsHandle handle,        // IN:  Directory handle to read from
            void *dirent,             // OUT: Buffer to copy dentries into
            fuse_fill_dir_t filldir)  // IN:  Filler function
{
   Bool done = FALSE;
   HgfsReq *request;
   int result = 0;
   uint32 f_pos = 0;

   ASSERT(dirent);

   request = HgfsGetNewRequest();
   if (!request) {
      LOG(4, ("Out of memory while getting new request\n"));
      return -ENOMEM;
   }
   while (!done) {
      HgfsOp opUsed;
      /* Nonzero result = we failed to get valid reply from server. */
      result = HgfsRequestDirEntries(handle,
                                     f_pos,
                                     request,
                                     &opUsed);
      if (result) {
         LOG(4, ("Error getting dentries from server\n"));
         break;
      }

      result = HgfsReadDirFromReply(&f_pos, dirent, filldir, request, opUsed,
                                    &done);

      LOG(4, ("f_pos = %d\n", f_pos));
      if (result == -ENAMETOOLONG) {
         continue;
      } else if (result == -ENOSPC) {
         result = 0;
         break;
      } else if (result < 0) {
         LOG(4, ("Error reading dentries from reply packet. Return %d\n", result));
         break;
      }
   }

   if (done == TRUE) {
      LOG(6, ("End of dir reached.\n"));
   }
   HgfsFreeRequest(request);
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * 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(const char *path,
                         int permsMode,         // IN: Mode to assign dir
                         HgfsOp opUsed,         // IN: Op to be used.
                         HgfsReq *req)          // IN/OUT: Packet to write into
{
   size_t reqSize;


   ASSERT(req);

   switch (opUsed) {
   case HGFS_OP_CREATE_DIR_V3: {
      int result;
      HgfsRequestCreateDirV3 *requestV3 = HgfsGetRequestPayload(req);

      reqSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();
      requestV3->fileName.flags = 0;
      requestV3->fileName.fid = HGFS_INVALID_HANDLE;
      requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (reqSize - 1),
                                requestV3->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }
      requestV3->fileName.length = result;
      reqSize += result;
      requestV3->mask = HGFS_CREATE_DIR_MASK;

      /* Set permissions. */
      requestV3->specialPerms = (permsMode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9;
      requestV3->ownerPerms = (permsMode & S_IRWXU) >> 6;
      requestV3->groupPerms = (permsMode & S_IRWXG) >> 3;
      requestV3->otherPerms = (permsMode & S_IRWXO);
      requestV3->fileAttr = 0;
      break;
   }
   case HGFS_OP_CREATE_DIR_V2: {
      int result;
      HgfsRequestCreateDirV2 *requestV2;

      requestV2 = (HgfsRequestCreateDirV2 *)(HGFS_REQ_PAYLOAD(req));

      reqSize = sizeof *requestV2;

      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (reqSize - 1),
                                requestV2->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }
      requestV2->fileName.length = result;
      reqSize += result;
      requestV2->mask = HGFS_CREATE_DIR_MASK;

      /* Set permissions. */
      requestV2->specialPerms = (permsMode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9;
      requestV2->ownerPerms = (permsMode & S_IRWXU) >> 6;
      requestV2->groupPerms = (permsMode & S_IRWXG) >> 3;
      requestV2->otherPerms = (permsMode & S_IRWXO);
      break;
   }
   case HGFS_OP_CREATE_DIR: {
      int result;
      HgfsRequestCreateDir *request;

      request = (HgfsRequestCreateDir *)(HGFS_REQ_PAYLOAD(req));

      reqSize = sizeof *request;
      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (reqSize - 1),
                                request->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }
      request->fileName.length = result;
      reqSize += result;
      /* Set permissions. */
      request->permissions = (permsMode & S_IRWXU) >> 6;
      break;
   }
   default:
      LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   req->payloadSize = reqSize;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsMkdir --
 *
 *    Handle a mkdir request
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsMkdir(const char *path,     // IN: Path to directory
          int permsMode)        // IN: Mode to set
{
   HgfsReq *req;
   HgfsStatus replyStatus;
   HgfsOp opUsed;
   int result = 0;

   ASSERT(path);

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request.\n"));
      result = -ENOMEM;
      goto out;
   }

retry:
   opUsed = hgfsVersionCreateDir;
   result = HgfsPackCreateDirRequest(path, permsMode, opUsed, req);
   if (result != 0) {
      LOG(4, ("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, ("Got reply.\n"));
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {
      case 0:
         LOG(6, ("Directory created successfully, instantiating dentry.\n"));
         /*
          * 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, ("Version 3 not supported. Falling back to version 2.\n"));
            hgfsVersionCreateDir = HGFS_OP_CREATE_DIR_V2;
            goto retry;
         } else if (opUsed == HGFS_OP_CREATE_DIR_V2) {
            LOG(4, ("Version 2 not supported. Falling back to version 1.\n"));
            hgfsVersionCreateDir = HGFS_OP_CREATE_DIR;
            goto retry;
         }

         /* Fallthrough. */
         default:
            LOG(6, ("Directory was not created, error %d\n", result));
            break;
         }
   } else if (result == -EIO) {
      LOG(4, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsDelete --
 *
 *    Handle both unlink and rmdir requests.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsDelete(const char* path,       // IN: Path to file
           HgfsOp op)              // IN: Opcode for file type (file or dir)*/
{
   HgfsReq *req = NULL;
   int result = 0;
   HgfsStatus replyStatus;
   uint32 reqSize;
   HgfsOp opUsed;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *clearReadOnlyAttr = &newAttr;
   Bool clearedReadOnly = FALSE;

   if ((op != HGFS_OP_DELETE_FILE) &&
       (op != HGFS_OP_DELETE_DIR)) {
      LOG(4, ("Invalid opcode. op = %d\n", op));
      result = -EINVAL;
      goto out;
   }

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request.\n"));
      result = -ENOMEM;
      goto out;
   }

  retry:
   if (op == HGFS_OP_DELETE_FILE) {
      opUsed = hgfsVersionDeleteFile;
   } else {
      opUsed = hgfsVersionDeleteDir;
   }

   if (opUsed == HGFS_OP_DELETE_FILE_V3 ||
       opUsed == HGFS_OP_DELETE_DIR_V3) {
      HgfsRequestDeleteV3 *request = HgfsGetRequestPayload(req);

      reqSize = sizeof(*request) + HgfsGetRequestHeaderSize();
      request->hints = 0;
      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE), reqSize),
                                request->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         result = -EINVAL;
         goto out;
      }
      request->fileName.length = result;
      reqSize += result;
      request->fileName.fid = HGFS_INVALID_HANDLE;
      request->fileName.flags = 0;
      request->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE;
      request->reserved = 0;

   } else {
      HgfsRequestDelete *request;

      request = (HgfsRequestDelete *)(HGFS_REQ_PAYLOAD(req));
      /* Fill out the request packet. */
      reqSize = sizeof *request;
      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE), reqSize),
                                request->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         result = -EINVAL;
         goto out;
      }
      request->fileName.length = result;
      reqSize += result;
   }

   req->payloadSize = reqSize;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   result = HgfsSendRequest(req);
   switch (result) {
   case 0:
      LOG(6, ("Got reply\n"));
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {

      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.
          *
          * Note, currently existing Windows HGFS servers that are running
          * shares against NTFS volumes do NOT handle the ACLs when setting
          * a file as writable. Only the attribute for read only is cleared.
          * This maybe okay but could be inadequate if the ACLs are read only
          * for the current user. For those cases, the second attempt will
          * still fail. The server code should be fixed to address this failing
          * the set attributes for read only if it cannot do both.
          *
          * XXX: I think old servers will send -EPERM here. Is this entirely
          * safe?
          */
         if (!clearedReadOnly) {
            result = HgfsClearReadOnly(path, clearReadOnlyAttr);
            if (result == 0) {
               clearedReadOnly = TRUE;
               LOG(4, ("removed read-only, retrying delete\n"));
               goto retry;
            }
            LOG(4, ("failed to remove read-only attribute\n"));
         } else {
            (void)HgfsRestoreReadOnly(path, clearReadOnlyAttr);
            LOG(4, ("second attempt failed\n"));
         }
         break;

      case -EPROTO:
         /* Retry with older version(s). Set globally. */
         if (opUsed == HGFS_OP_DELETE_DIR_V3) {
            LOG(4, ("Version 3 not supported. Falling back to version 1.\n"));
            hgfsVersionDeleteDir = HGFS_OP_DELETE_DIR;
            goto retry;
         } else if (opUsed == HGFS_OP_DELETE_FILE_V3) {
            LOG(4, ("Version 3 not supported. Falling back to version 1.\n"));
            hgfsVersionDeleteFile = HGFS_OP_DELETE_FILE;
            goto retry;
         }

         LOG(4, ("Server returned error: %d\n", result));
         break;
      default:
         break;
      }
      break;
   default:
      LOG(4, ("Send returned error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   return result;
}
0707010000048F000081A4000000000000000000000001682255050000AE44000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/file.c /*********************************************************
 * Copyright (c) 2013-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * file.c --
 *
 * File operations for the hgfs driver.
 */

#include "cpName.h"
#include "hgfsProto.h"
#include "module.h"
#include "request.h"
#include "hgfsUtil.h"
#include "fsutil.h"
#include "file.h"
#include "vm_assert.h"
#include "vm_basic_types.h"



static int
HgfsGetOpenFlags(uint32 flags);


/*
 * 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(const char *path,          // IN: Path to file
                    struct fuse_file_info *fi, // IN: File info structure
                    mode_t permsMode,          // IN: Permissions, in this context
                    HgfsOpenValid mask,        // IN: Open validation mask
                    HgfsOp opUsed,             // IN: Op to use
                    HgfsReq *req)              // IN/OUT: Packet to write into
{
   size_t reqSize;
   int openMode, openFlags;

   ASSERT(path);
   ASSERT(req);

   openMode = HgfsGetOpenMode(fi->flags);
   if (openMode < 0) {
      LOG(4, ("Failed to get open mode.\n"));
      return -EINVAL;
   }
   openFlags = HgfsGetOpenFlags(fi->flags);
   if (openFlags < 0) {
      LOG(4, ("Failed to get open flags.\n"));
      return -EINVAL;
   }

   switch (opUsed) {
    case HGFS_OP_OPEN_V3: {
      int result;
      HgfsRequestOpenV3 *requestV3 = HgfsGetRequestPayload(req);

      reqSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();

      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (reqSize - 1),
                                requestV3->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }

      requestV3->fileName.length = result;
      reqSize += result;
      /* Linux clients need case-sensitive lookups. */
      requestV3->fileName.flags = 0;
      requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
      requestV3->fileName.fid = HGFS_INVALID_HANDLE;

      requestV3->mask = mask;
      requestV3->mode = openMode;
      requestV3->flags = openFlags;

      /* Set permissions. */
      if (requestV3->mask & HGFS_FILE_OPEN_PERMS) {
         requestV3->specialPerms = (permsMode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9;
         requestV3->ownerPerms = (permsMode & S_IRWXU) >> 6;
         requestV3->groupPerms = (permsMode & S_IRWXG) >> 3;
         requestV3->otherPerms = (permsMode & 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: {
      int result;
      HgfsRequestOpenV2 *requestV2;

      requestV2 = (HgfsRequestOpenV2 *)(HGFS_REQ_PAYLOAD(req));

      reqSize = sizeof *requestV2;

      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (reqSize - 1),
                                requestV2->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }

      requestV2->fileName.length = result;
      reqSize += result;
      requestV2->mask = mask;
      requestV2->mode = openMode;
      requestV2->flags = openFlags;

      /* Set permissions, requires discussion... default, will set max permission*/
      if (requestV2->mask & HGFS_FILE_OPEN_PERMS) {
         requestV2->specialPerms = (permsMode & (S_ISUID | S_ISGID | S_ISVTX)) >> 9;
         requestV2->ownerPerms = (permsMode & S_IRWXU) >> 6;
         requestV2->groupPerms = (permsMode & S_IRWXG) >> 3;
         requestV2->otherPerms = (permsMode & S_IRWXO);
      }

      /* XXX: Request no lock for now. */
      requestV2->desiredLock = HGFS_LOCK_NONE;
      break;
   }
   case HGFS_OP_OPEN: {
      int result;
      HgfsRequestOpen *request;

      request = (HgfsRequestOpen *)(HGFS_REQ_PAYLOAD(req));
      reqSize = sizeof *request;

      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (reqSize - 1),
                                request->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }

      request->fileName.length = result;
      reqSize += result;
      request->mode = openMode;
      request->flags = openFlags;

      /* Set permissions. */
      request->permissions = (permsMode & S_IRWXU) >> 6;
      break;
   }
   default:
      LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   req->payloadSize = reqSize;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   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
                    HgfsLockType *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 = HgfsGetReplyPayload(req);
      replySize = sizeof(*replyV3) + HgfsGetReplyHeaderSize();
      *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, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      ASSERT(FALSE);
      return -EPROTO;
   }

   if (opUsed < HGFS_OP_OPEN_V3 && 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, ("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, ("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, ("Invalid open flags %o. Ignoring the O_TRUNC flag.\n", flags));
      result = HGFS_OPEN_CREATE_SAFE;
      break;
   }

   return result;
}


/*
 * HGFS file operations for files.
 */

/*
 *----------------------------------------------------------------------
 *
 * HgfsOpenInt --
 *
 *    We send an "Open" request to the server with the file path
 *    If the Open succeeds, we store the filehandle sent by the server
 *    in the file info struct so it can be accessed by
 *    read/write/close.
 *
 * Results:
 *    Returns zero if on success, error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsOpenInt(const char *path,           //IN: Path to a file
            struct fuse_file_info *fi,  //OUT: File info structure
            mode_t permsMode,           //IN: Permissions, in this context
            HgfsOpenValid mask)         //IN: Open validation mask
{
   HgfsReq *req;
   HgfsOp opUsed;
   HgfsStatus replyStatus;
   HgfsHandle replyFile;
   HgfsLockType replyLock;
   int result = 0;

   ASSERT(NULL != path);
   ASSERT(NULL != fi);

   LOG(4, ("Entry(%s)\n", path));

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request.\n"));
      result = -ENOMEM;
      goto out;
   }

   fi->fh = HGFS_INVALID_HANDLE;

retry:
   /*
    * Set up pointers using the proper struct This lets us check the
    * version exactly once and use the pointers later.
    */

   opUsed = hgfsVersionOpen;
   result = HgfsPackOpenRequest(path, fi, permsMode, mask, opUsed, req);
   if (result != 0) {
      LOG(4, ("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 = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {
      case 0:
         result = HgfsUnpackOpenReply(req, opUsed, &replyFile, &replyLock);
         if (result != 0) {
            break;
         }

         fi->fh = (uint64_t)replyFile;
         LOG( 4,("Server file handle: %"FMT64"u\n", fi->fh));

         break;

      case -EPROTO:
         /* Retry with older version(s). Set globally. */
         if (opUsed == HGFS_OP_OPEN_V3) {
            LOG(4, ("Version 3 not supported. Falling back to version 2.\n"));
            hgfsVersionOpen = HGFS_OP_OPEN_V2;
            goto retry;
         }

         /* Retry with Version 1 of Open. Set globally. */
         if (opUsed == HGFS_OP_OPEN_V2) {
            LOG(4, ("Version 2 not supported. Falling back to version 1.\n"));
            hgfsVersionOpen = HGFS_OP_OPEN;
            goto retry;
         }

         /* Fallthrough. */
      default:
         break;
      }
   } else if (result == -EIO) {
      LOG(8, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }
out:
   HgfsFreeRequest(req);
   LOG(4, ("Exit(0x%"FMT64"x -> %d)\n",fi->fh, result));
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsOpen --
 *
 *    Called whenever a process opens a file in our filesystem.
 *
 * Results:
 *    Returns zero if on success, error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsOpen(const char *path,             //IN: Path to a file
            struct fuse_file_info *fi) //OUT: File info structure
{
     return HgfsOpenInt(path, fi, 0, HGFS_FILE_OPEN_MASK);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsCreate --
 *
 *    Called whenever a process request to create a file
 *
 * Results:
 *    Returns zero if on success, error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsCreate(const char *path,          //IN: Path to a file
           mode_t permsMode,          //IN: Permission to open the file
           struct fuse_file_info *fi) //OUT: File info structure
{
     return HgfsOpenInt(path, fi, permsMode, HGFS_FILE_CREATE_MASK);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsDoRead --
 *
 *    Do one read request. Called by HgfsRead, 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.
 *
 *
 * 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(NULL != buf);

   LOG(4, ("Entry(handle = %u, 0x%"FMTSZ"x @ 0x%"FMT64"x)\n", handle, count, offset));

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request\n"));
      result = -ENOMEM;
      goto out;
   }

 retry:
   opUsed = hgfsVersionRead;
   if (opUsed == HGFS_OP_READ_V3) {
      HgfsRequestReadV3 *requestV3 = HgfsGetRequestPayload(req);

      requestV3->file = handle;
      requestV3->offset = offset;
      requestV3->requiredSize = count;
      requestV3->reserved = 0;

      req->payloadSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();

   } else {
      HgfsRequestRead *request;

      request = (HgfsRequestRead *)(HGFS_REQ_PAYLOAD(req));
      request->file = handle;
      request->offset = offset;
      request->requiredSize = count;
      req->payloadSize = sizeof *request;
   }

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   /* Send the request and process the reply. */
   result = HgfsSendRequest(req);
   if (result == 0) {
      /* Get the reply. */
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {
      case 0:
         if (opUsed == HGFS_OP_READ_V3) {
            HgfsReplyReadV3 * replyV3 = HgfsGetReplyPayload(req);

            actualSize = replyV3->actualSize;
            payload = replyV3->payload;

         } else {
            actualSize = ((HgfsReplyRead *)HGFS_REQ_PAYLOAD(req))->actualSize;
            payload = ((HgfsReplyRead *)HGFS_REQ_PAYLOAD(req))->payload;
         }

         /* Confidence check on read size. */
         if (actualSize > count) {
            LOG(4, ("Server reply: read too big!\n"));
            result = -EPROTO;
            goto out;
         }

         if (0 == actualSize) {
            /* We got no bytes, so don't need to copy to user. */
            LOG(8, ("Server reply returned zero\n"));
            result = actualSize;
            goto out;
         }

         /* Return result. */
         memcpy(buf, payload, actualSize);
         LOG(8, ("Copied %u\n", actualSize));
         result = actualSize;
         break;

      case -EPROTO:
         /* Retry with older version(s). Set globally. */
         if (opUsed == HGFS_OP_READ_V3) {
            LOG(4, ("Version 3 not supported. Falling back to version 1.\n"));
            hgfsVersionRead = HGFS_OP_READ;
            goto retry;
         }
         break;

      default:
         break;
      }
   } else if (result == -EIO) {
      LOG(8, ("Error: send request timed out\n"));
   } else if (result == -EPROTO) {
      LOG(4, ("Error: send request server returned error: %d\n", result));
   } else {
      LOG(4, ("Error: send request unknown : %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   LOG(4, ("Exit(%d)\n", result));
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsMaxIOSize --
 *
 *    Get the maximum IO size based on the agreed maximum packet size.
 *
 * Results:
 *    The maximum IO size.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static uint32
HgfsMaxIOSize(void)
{
   uint32 maxIOSize = gState->maxPacketSize - HGFS_HEADER_SIZE_MAX;

   if (maxIOSize > 0 && maxIOSize < HgfsLargeIoMax(FALSE)) {
      return maxIOSize;
   }
   return HgfsLargeIoMax(FALSE);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsRead --
 *
 *    Called whenever a process reads from a file in our filesystem.
 *
 * Results:
 *    Returns the number of bytes read on success, or an error on
 *    failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

ssize_t
HgfsRead(struct fuse_file_info *fi,  // IN:  File info struct
         char  *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 = 0;
   char *buffer = buf;
   loff_t curOffset = offset;
   size_t nextCount, remainingCount = count;
   uint32 maxIOSize = HgfsMaxIOSize();

   ASSERT(NULL != fi);
   ASSERT(NULL != buf);

   LOG(4, ("Entry(0x%"FMT64"x 0x%"FMTSZ"x bytes @ 0x%"FMT64"x)\n",
           fi->fh, count, offset));

    do {
      nextCount = (remainingCount > maxIOSize) ? maxIOSize : remainingCount;
      LOG(4, ("Issue DoRead(0x%"FMT64"x 0x%"FMTSZ"x bytes @ 0x%"FMT64"x)\n",
              fi->fh, nextCount, curOffset));
      result = HgfsDoRead(fi->fh, buffer, nextCount, curOffset);
      if (result < 0) {
         LOG(8, ("Error: DoRead: -> %d\n", result));
         goto out;
      }
      remainingCount -= result;
      curOffset += result;
      buffer += result;

   } while ((result > 0) && (remainingCount > 0));

  memset(buffer, 0, remainingCount);

  out:
   LOG(4, ("Exit(%"FMTSZ"d)\n", count - remainingCount));
   return (count - remainingCount);
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsDoWrite --
 *
 *    Do one write request. Called by HgfsWrite, 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.
 *
 * 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 the 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, ("Out of memory while getting new request\n"));
      result = -ENOMEM;
      goto out;
   }
   LOG( 4,("handle = %u \n", handle));
 retry:
   opUsed = hgfsVersionWrite;
   if (opUsed == HGFS_OP_WRITE_V3) {
      HgfsRequestWriteV3 *requestV3 = HgfsGetRequestPayload(req);

      requestV3->file = handle;
      requestV3->flags = 0;
      requestV3->offset = offset;
      requestV3->requiredSize = count;
      requestV3->reserved = 0;
      payload = requestV3->payload;
      requiredSize = requestV3->requiredSize;
      reqSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();

   } else {
      HgfsRequestWrite *request;

      request = (HgfsRequestWrite *)(HGFS_REQ_PAYLOAD(req));
      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;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   /* Send the request and process the reply. */
   result = HgfsSendRequest(req);
   if (result == 0) {
      /* Get the reply. */
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {
      case 0:
         if (opUsed == HGFS_OP_WRITE_V3) {
            HgfsReplyWriteV3 * replyV3 = HgfsGetReplyPayload(req);

            actualSize = replyV3->actualSize;

         } else {
            actualSize = ((HgfsReplyWrite *)HGFS_REQ_PAYLOAD(req))->actualSize;
         }

         /* Return result. */
         LOG(6, ("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, ("Version 3 not supported. Falling back to version 1.\n"));
            hgfsVersionWrite = HGFS_OP_WRITE;
            goto retry;
         }
         break;

      default:
         LOG(4, ("Server returned error: %d\n", result));
         break;
      }
   } else if (result == -EIO) {
      LOG(8, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsWrite --
 *
 *    Called whenever a process writes to a file in our filesystem.
 *
 * Results:
 *    Returns the number of bytes written on success, or an error on
 *    failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

ssize_t
HgfsWrite(struct fuse_file_info *fi,  // IN: File info structure
         const char  *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;
   const char *buffer = buf;
   loff_t curOffset = offset;
   size_t nextCount, remainingCount = count;
   ssize_t bytesWritten = 0;
   uint32 maxIOSize = HgfsMaxIOSize();

   ASSERT(NULL != buf);
   ASSERT(NULL != fi);

   LOG(6, ("Entry(0x%"FMT64"x off bytes 0x%"FMTSZ"x @ 0x%"FMT64"x)\n",
           fi->fh, count, offset));

   do {
      nextCount = (remainingCount > maxIOSize) ? maxIOSize : remainingCount;
      LOG(4, ("Issue DoWrite(0x%"FMT64"x 0x%"FMTSZ"x bytes @ 0x%"FMT64"x)\n",
              fi->fh, nextCount, curOffset));

      result = HgfsDoWrite(fi->fh, buffer, nextCount, curOffset);
      if (result < 0) {
         bytesWritten = result;
         LOG(4, ("Error: written 0x%"FMTSZ"x bytes DoWrite -> %d\n",
             count - remainingCount, result));
         goto out;
      }
      remainingCount -= result;
      curOffset += result;
      buffer += result;

   } while ((result > 0) && (remainingCount > 0));

   bytesWritten = count - remainingCount;

out:
   LOG(6, ("Exit(0x%"FMTSZ"x)\n", bytesWritten));
   return bytesWritten;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsRename --
 *
 *    Handle rename requests.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsRename(const char* from, const char* to)
{
   HgfsReq *req;
   int result = 0;
   uint32 reqSize;
   HgfsOp opUsed;
   HgfsStatus replyStatus;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *clearReadOnlyAttr = &newAttr;
   Bool clearedReadOnly = FALSE;

   ASSERT(from);
   ASSERT(to);

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request\n"));
      result = -ENOMEM;
      goto out;
   }

retry:
   opUsed = hgfsVersionRename;
   if (opUsed == HGFS_OP_RENAME_V3) {
      HgfsRequestRenameV3 *requestV3 = HgfsGetRequestPayload(req);

      requestV3->hints = 0;
      requestV3->oldName.flags = 0;
      requestV3->oldName.fid = HGFS_INVALID_HANDLE;
      requestV3->oldName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
      requestV3->reserved = 0;
      reqSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();
      /* Convert old name to CP format. */
      result = CPName_ConvertTo(from,
                                HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE), reqSize),
                                requestV3->oldName.name);
      if (result < 0) {
         LOG(4, ("oldName CP conversion failed\n"));
         result = -EINVAL;
         goto out;
      }

      requestV3->oldName.length = result;
      reqSize += result;
   } else {
      HgfsRequestRename *request = (HgfsRequestRename *)HGFS_REQ_PAYLOAD(req);

      reqSize = sizeof *request;
      /* Convert old name to CP format. */
      result = CPName_ConvertTo(from,
                                HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE), reqSize),
                                request->oldName.name);
      if (result < 0) {
         LOG(4, ("oldName CP conversion failed\n"));
         result = -EINVAL;
         goto out;
      }

      request->oldName.length = 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 *requestV3 = HgfsGetRequestPayload(req);
      HgfsFileNameV3 *newNameP;

      newNameP = (HgfsFileNameV3 *)((char *)&requestV3->oldName +
                                    sizeof requestV3->oldName + result);

      LOG(6, ("New name: \"%s\"\n", newNameP->name));

      /* Convert new name to CP format. */
      result = CPName_ConvertTo(to,
                                HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE), reqSize) - result,
                                newNameP->name);
      if (result < 0) {
         LOG(4, ("newName CP conversion failed\n"));
         result = -EINVAL;
         goto out;
      }
      /*
       * The usage of the space allocated in req early in the function is kept
       * in reqSize. If oldName length was 0 we're not causing an overrun.
       */
      /* coverity[overrun-local] */
      newNameP->length = result;
      reqSize += result;
      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);

      LOG(6, ("New name: \"%s\"\n", newNameP->name));

      /* Convert new name to CP format. */
      result = CPName_ConvertTo(to,
                                HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE), reqSize) - result,
                                newNameP->name);
      if (result < 0) {
         LOG(4, ("newName CP conversion failed\n"));
         result = -EINVAL;
         goto out;
      }
      /*
       * The usage of the space allocated in req early in the function is kept
       * in reqSize. If oldName length was 0 we're not causing an overrun.
       */
      /* coverity[overrun-local] */
      newNameP->length = result;
      reqSize += result;
   }

   req->payloadSize = reqSize;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   result = HgfsSendRequest(req);
   switch (result) {
   case 0:
      LOG(6, ("Got reply\n"));
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {
      case -EPROTO:
         /* Retry with older version(s). Set globally. */
         if (opUsed == HGFS_OP_RENAME_V3) {
            hgfsVersionRename = HGFS_OP_RENAME;
            goto retry;
         } else {
            LOG(4, ("Server returned error: %d\n", result));
         }
         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.
          *
          * Note, currently existing Windows HGFS servers that are running
          * shares against NTFS volumes do NOT handle the ACLs when setting
          * a file as writable. Only the attribute for read only is cleared.
          * This maybe okay but could be inadequate if the ACLs are read only
          * for the current user. For those cases, the second attempt will
          * still fail. The server code should be fixed to address this failing
          * the set attributes for read only if it cannot do both.
          *
          * XXX: I think old servers will send -EPERM here. Is this entirely
          * safe?
          * We can receive EACCES or EPERM if we don't have the correct
          * permission on the source file. So lets not assume that we have
          * a target and only clear the target if there is one.
          */
         if (!clearedReadOnly) {
            result = HgfsClearReadOnly(to, clearReadOnlyAttr);
            if (result == 0) {
               clearedReadOnly = TRUE;
               LOG(4, ("removed read-only, retrying delete\n"));
               goto retry;
            }
            LOG(4, ("failed to remove read-only attribute\n"));
         } else {
            (void)HgfsRestoreReadOnly(to, clearReadOnlyAttr);
            LOG(4, ("second attempt failed\n"));
         }
         break;
      default:
         LOG(4, ("Server protocol result %d\n", result));
      }
      break;
   default:
      LOG(4, ("Send returned error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   LOG(6, ("Exit(%d)\n", result));
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsPackSetattrRequest --
 *
 *    Setup the Setattr request, depending on the op version.
 *
 * 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(const char *path,   // IN:  path to file
                       HgfsAttrInfo *attr, // IN: attributes to set
                       HgfsOp opUsed,      // IN: Op to be used
                       HgfsReq *req)       // IN/OUT: req packet
{
   HgfsAttrV2 *attrV2;
   size_t reqBufferSize;
   size_t reqSize;
   ASSERT(req);

   switch (opUsed) {
   case HGFS_OP_SETATTR_V3: {
      int result;
      HgfsRequestSetattrV3 *requestV3 = HgfsGetRequestPayload(req);

      attrV2 = &requestV3->attr;

      /*
       * 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);
      requestV3->hints = 0;

      /*
       * 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.
       */
      requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
      requestV3->fileName.fid = HGFS_INVALID_HANDLE;
      requestV3->fileName.flags = 0;
      requestV3->reserved = 0;
      reqSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();
      reqBufferSize = HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE), reqSize);
      result = CPName_ConvertTo(path,
                                reqBufferSize,
                                requestV3->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }
      requestV3->fileName.length = result;
      reqSize += result;

      attrV2->mask = attr->mask;
      if (attr->mask & (HGFS_ATTR_VALID_SPECIAL_PERMS |
                          HGFS_ATTR_VALID_OWNER_PERMS |
                          HGFS_ATTR_VALID_GROUP_PERMS |
                          HGFS_ATTR_VALID_OTHER_PERMS)) {
         attrV2->specialPerms = attr->specialPerms;
         attrV2->ownerPerms = attr->ownerPerms;
         attrV2->groupPerms = attr->groupPerms;
         attrV2->otherPerms = attr->otherPerms;
      }
      if (attr->mask & HGFS_ATTR_VALID_USERID) {
         attrV2->userId = attr->userId;
      }
      if (attr->mask & HGFS_ATTR_VALID_GROUPID) {
         attrV2->groupId = attr->groupId;
      }
      if (attr->mask & HGFS_ATTR_VALID_SIZE) {
         attrV2->size = attr->size;
      }
      if (attr->mask & HGFS_ATTR_VALID_ACCESS_TIME) {
         attrV2->accessTime = attr->accessTime;
         requestV3->hints |= HGFS_ATTR_HINT_SET_ACCESS_TIME;
      }
      if (attr->mask & HGFS_ATTR_VALID_WRITE_TIME) {
         attrV2->writeTime = attr->writeTime;
         requestV3->hints |= HGFS_ATTR_HINT_SET_WRITE_TIME;
      }

      break;
   }
   case HGFS_OP_SETATTR_V2: {
      int result;
      HgfsRequestSetattrV2 *requestV2;

      requestV2 = (HgfsRequestSetattrV2 *)(HGFS_REQ_PAYLOAD(req));

      attrV2 = &requestV2->attr;

      /*
       * 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);
      requestV2->hints = 0;

      reqSize = sizeof *requestV2;
      reqBufferSize = HGFS_NAME_BUFFER_SIZE(HgfsLargePacketMax(FALSE), requestV2);
      result = CPName_ConvertTo(path,
                                reqBufferSize,
                                requestV2->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }
      requestV2->fileName.length = result;
      reqSize += result;

      if (attr->mask & (HGFS_ATTR_VALID_SPECIAL_PERMS |
                          HGFS_ATTR_VALID_OWNER_PERMS |
                          HGFS_ATTR_VALID_GROUP_PERMS |
                          HGFS_ATTR_VALID_OTHER_PERMS)) {
         attrV2->specialPerms = attr->specialPerms;
         attrV2->ownerPerms = attr->ownerPerms;
         attrV2->groupPerms = attr->groupPerms;
         attrV2->otherPerms = attr->otherPerms;
      }
      if (attr->mask & HGFS_ATTR_VALID_USERID) {
         attrV2->userId = attr->userId;
      }
      if (attr->mask & HGFS_ATTR_VALID_GROUPID) {
         attrV2->groupId = attr->groupId;
      }
      if (attr->mask & HGFS_ATTR_VALID_SIZE) {
         attrV2->size = attr->size;
      }
      if (attr->mask & HGFS_ATTR_VALID_ACCESS_TIME) {
         attrV2->accessTime = attr->accessTime;
         requestV2->hints |= HGFS_ATTR_HINT_SET_ACCESS_TIME;
      }
      if (attr->mask & HGFS_ATTR_VALID_WRITE_TIME) {
         attrV2->writeTime = attr->writeTime;
         requestV2->hints |= HGFS_ATTR_HINT_SET_WRITE_TIME;
      }

      break;
   }
   case HGFS_OP_SETATTR: {
      int result;
      HgfsAttr *attrV1;
      HgfsRequestSetattr *request;
      HgfsAttrChanges *update;

      request = (HgfsRequestSetattr *)(HGFS_REQ_PAYLOAD(req));
      attrV1 = &request->attr;
      update = &request->update;

      reqSize = sizeof *request;
      reqBufferSize = HGFS_NAME_BUFFER_SIZE(HgfsLargePacketMax(FALSE), request);
      result = CPName_ConvertTo(path,
                                reqBufferSize,
                                request->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }
      request->fileName.length = result;
      reqSize += result;

      /*
       * Clear attributes before touching them.
       * We can't rely on GetNewRequest() to zero our structures, so
       * make sure to zero them all here.
       */
      memset(attrV1, 0, sizeof *attrV1);
      memset(update, 0, sizeof *update);

      if (attr->mask & (HGFS_ATTR_VALID_SPECIAL_PERMS |
                          HGFS_ATTR_VALID_OWNER_PERMS |
                          HGFS_ATTR_VALID_GROUP_PERMS |
                          HGFS_ATTR_VALID_OTHER_PERMS)) {
         *update |= HGFS_ATTR_PERMISSIONS;
         attrV1->permissions = attr->effectivePerms;
      }
      if (attr->mask & HGFS_ATTR_VALID_SIZE) {
         *update |= HGFS_ATTR_SIZE;
         attrV1->size = attr->size;
      }
      if (attr->mask & HGFS_ATTR_VALID_ACCESS_TIME) {
         *update |= HGFS_ATTR_ACCESS_TIME |
         HGFS_ATTR_ACCESS_TIME_SET;
         attrV1->accessTime = attr->accessTime;
      }
      if (attr->mask & HGFS_ATTR_VALID_WRITE_TIME) {
         *update |= HGFS_ATTR_WRITE_TIME |
         HGFS_ATTR_WRITE_TIME_SET ;
         attrV1->writeTime = attr->writeTime;
      }

      break;
   }
   default:
      LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   req->payloadSize = reqSize;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   LOG(6, ("Exit(0)\n"));
   return 0;
}


/*
 * Public function implementations.
 */

/*
 *----------------------------------------------------------------------
 *
 * HgfsSetattr --
 *
 *    Handle a setattr request.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsSetattr(const char* path,       //IN: Path to file
            HgfsAttrInfo *attr)     //IN: Attribute to set
{
   HgfsReq *req;
   HgfsStatus replyStatus;
   int result = 0;
   HgfsOp opUsed;

   LOG(4, ("Entry(%s)\n", path));

   req = HgfsGetNewRequest();
   if (!req) {
      result = -ENOMEM;
      LOG(4, ("Error: out of memory -> %d\n", result));
      goto out;
   }

retry:
   /* Fill out the request packet. */
   opUsed = hgfsVersionSetattr;
   result = HgfsPackSetattrRequest(path, attr, opUsed, req);

   /* Send the request and process the reply. */
   result = HgfsSendRequest(req);
   if (result == 0) {
      /* Get the reply. */
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {
      case -EPROTO:
         /* Retry with older version(s). Set globally. */
         if (opUsed == HGFS_OP_SETATTR_V3) {
            LOG(4, ("Error: reply EPROTO: Version 3 -> version 2.\n"));
            hgfsVersionSetattr = HGFS_OP_SETATTR_V2;
            goto retry;
         } else if (opUsed == HGFS_OP_SETATTR_V2) {
            LOG(4, ("Error: reply EPROTO: Version 2 -> version 1.\n"));
            hgfsVersionSetattr = HGFS_OP_SETATTR;
            goto retry;
         }

         /* Fallthrough. */
      default:
         break;
      }
   } else if (result == -EIO) {
      LOG(8, ("Error: EIO: send timed out\n"));
   } else if (result == -EPROTO) {
      LOG(4, ("Error: EPROTO: send -> %d\n", result));
   } else {
      LOG(4, ("Error: unknown: send -> %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   LOG(6, ("Exit(%d)\n", result));
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsRelease --
 *
 *    Called when the last user of a file closes it.
 *
 * Results:
 *    Returns zero on success, or an error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsRelease(HgfsHandle handle)  //IN:File handle to close
{
   HgfsReq *req;
   HgfsOp opUsed;
   HgfsStatus replyStatus;
   int result;

   LOG(6, ("Entry(handle = %u)\n", handle));

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request\n"));
      result = -ENOMEM;
      goto out;
   }

retry:
   opUsed = hgfsVersionClose;
   if (opUsed == HGFS_OP_CLOSE_V3) {
      HgfsRequestCloseV3 *requestV3 = HgfsGetRequestPayload(req);

      requestV3->file = handle;
      requestV3->reserved = 0;
      req->payloadSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();

   } else {
      HgfsRequestClose *request;

      request = (HgfsRequestClose *)(HGFS_REQ_PAYLOAD(req));
      request->file = handle;
      req->payloadSize = sizeof *request;
   }

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   /* Send the request and process the reply. */
   result = HgfsSendRequest(req);
   if (result == 0) {
      /* Get the reply. */
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      switch (result) {
      case 0:
         LOG(4, ("Released handle %u\n", handle));
         break;
      case -EPROTO:
         /* Retry with older version(s). Set globally. */
         if (opUsed == HGFS_OP_CLOSE_V3) {
            LOG(4, ("Version 3 not supported. Falling back to version 1.\n"));
            hgfsVersionClose = HGFS_OP_CLOSE;
            goto retry;
         }
         break;
      default:
         LOG(4, ("Failed. handle = %u\n", handle));
         break;
      }
   } else if (result == -EIO) {
      LOG(4, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   LOG(6, ("Exit(%d)\n", result));
   return result;
}
07070100000490000081A4000000000000000000000001682255050000064B000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/file.h /*********************************************************
 * Copyright (C) 2013 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * file.h --
 *
 * High-level filesystem operations for the filesystem portion of
 * the vmhgfs driver.
 */

#ifndef _HGFS_DRIVER_FILE_H_
#define _HGFS_DRIVER_FILE_H_

#define HGFS_FILE_OPEN_PERMS (HGFS_OPEN_VALID_SPECIAL_PERMS | \
			      HGFS_OPEN_VALID_OWNER_PERMS | \
			      HGFS_OPEN_VALID_GROUP_PERMS | \
			      HGFS_OPEN_VALID_OTHER_PERMS)

#define HGFS_FILE_OPEN_MASK (HGFS_OPEN_VALID_MODE | \
			     HGFS_OPEN_VALID_FLAGS | \
			     HGFS_OPEN_VALID_FILE_NAME | \
			     HGFS_OPEN_VALID_SERVER_LOCK)

#define HGFS_FILE_CREATE_MASK (HGFS_FILE_OPEN_MASK | \
			       HGFS_FILE_OPEN_PERMS)

/* Public functions (with respect to the entire module). */
int HgfsRelease(HgfsHandle handle);

#endif // _HGFS_DRIVER_FILE_H_
 07070100000491000081A40000000000000000000000016822550500002278000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/filesystem.c   /*********************************************************
 * Copyright (C) 2013,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * filesystem.c --
 *
 * High-level filesystem operations for the filesystem portion of
 * the vmhgfs driver.
 */

#include "filesystem.h"
#include "transport.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"



/* Synchronization primitives. */
pthread_mutex_t hgfsBigLock;


/* Global protocol version switch. */
HgfsOp hgfsVersionCreateSession;
HgfsOp hgfsVersionDestroySession;
HgfsOp hgfsVersionOpen;
HgfsOp hgfsVersionRead;
HgfsOp hgfsVersionWrite;
HgfsOp hgfsVersionClose;
HgfsOp hgfsVersionSearchOpen;
HgfsOp hgfsVersionSearchRead;
HgfsOp hgfsVersionSearchClose;
HgfsOp hgfsVersionGetattr;
HgfsOp hgfsVersionSetattr;
HgfsOp hgfsVersionCreateDir;
HgfsOp hgfsVersionDeleteFile;
HgfsOp hgfsVersionDeleteDir;
HgfsOp hgfsVersionRename;
HgfsOp hgfsVersionQueryVolumeInfo;
HgfsOp hgfsVersionCreateSymlink;

HgfsFuseState HFState;
HgfsFuseState *gState = &HFState;

/*
 *-----------------------------------------------------------------------------
 *
 * HgfsResetOps --
 *
 *      Reset ops with more than one opcode back to the desired opcode.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

void
HgfsResetOps(void)
{
   hgfsVersionCreateSession   = HGFS_OP_CREATE_SESSION_V4;
   hgfsVersionDestroySession  = HGFS_OP_DESTROY_SESSION_V4;
   hgfsVersionOpen            = HGFS_OP_OPEN_V3;
   hgfsVersionRead            = HGFS_OP_READ_V3;
   hgfsVersionWrite           = HGFS_OP_WRITE_V3;
   hgfsVersionClose           = HGFS_OP_CLOSE_V3;
   hgfsVersionSearchOpen      = HGFS_OP_SEARCH_OPEN_V3;
   hgfsVersionSearchRead      = HGFS_OP_SEARCH_READ_V3;
   hgfsVersionSearchClose     = HGFS_OP_SEARCH_CLOSE_V3;
   hgfsVersionGetattr         = HGFS_OP_GETATTR_V3;
   hgfsVersionSetattr         = HGFS_OP_SETATTR_V3;
   hgfsVersionCreateDir       = HGFS_OP_CREATE_DIR_V3;
   hgfsVersionDeleteFile      = HGFS_OP_DELETE_FILE_V3;
   hgfsVersionDeleteDir       = HGFS_OP_DELETE_DIR_V3;
   hgfsVersionRename          = HGFS_OP_RENAME_V3;
   hgfsVersionQueryVolumeInfo = HGFS_OP_QUERY_VOLUME_INFO_V3;
   hgfsVersionCreateSymlink   = HGFS_OP_CREATE_SYMLINK_V3;
}


/*
 *----------------------------------------------------------------------
 *
 * 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(const char *path,        // IN: File pointer for this open
                           HgfsOp opUsed,           // IN: Op to be used.
                           HgfsReq *req)            // IN/OUT: Packet to write into
{
   size_t requestSize;


   ASSERT(req);

   switch (opUsed) {
   case HGFS_OP_QUERY_VOLUME_INFO_V3: {
      int result;
      HgfsRequestQueryVolumeV3 *requestV3 = HgfsGetRequestPayload(req);

      requestV3->fileName.flags = 0;
      requestV3->fileName.fid = HGFS_INVALID_HANDLE;
      requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
      requestV3->reserved = 0;
      requestSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();
      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (requestSize - 1),
                                requestV3->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }
      requestV3->fileName.length = result;
      requestSize += result;
      break;
   }
   case HGFS_OP_QUERY_VOLUME_INFO: {
      int result;
      HgfsRequestQueryVolume *request;

      request = (HgfsRequestQueryVolume *)(HGFS_REQ_PAYLOAD(req));

      requestSize = sizeof *request;
      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                HgfsLargePacketMax(FALSE) - (requestSize - 1),
                                request->fileName.name);
      if (result < 0) {
         LOG(4, ("CP conversion failed.\n"));
         return -EINVAL;
      }
      request->fileName.length = result;
      requestSize += result;
      break;
   }
   default:
      LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   req->payloadSize = requestSize;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   return 0;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsStatfs --
 *
 *    Hgfs 'statfs' method. Called when statfs(2) is invoked on the
 *    filesystem.
 *
 * Results:
 *    0 on success
 *    error < 0 on failure
 *
 * Side effects:
 *    None
 *
 *-----------------------------------------------------------------------------
 */

int
HgfsStatfs(const char* path,            // IN : Path to the file
           struct statvfs *stat)        // OUT: Stat to fill in
{
   HgfsReq *req;
   int result = 0;
   HgfsOp opUsed;
   HgfsStatus replyStatus;
   uint64 freeBytes;
   uint64 totalBytes;

   LOG(6, ("Entered.\n"));
   memset(stat, 0, sizeof *stat);

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request.\n"));
      result = -ENOMEM;
      goto out;
   }

  retry:
   opUsed = hgfsVersionQueryVolumeInfo;
   result = HgfsPackQueryVolumeRequest(path, opUsed, req);
   if (result != 0) {
      LOG(4, ("Error packing request.\n"));
      goto out;
   }

   result = HgfsSendRequest(req);
   if (result == 0) {
      LOG(6, ("Got reply.\n"));
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);

      /*
       * If the statfs succeeded on the server, copy the stats
       * into the statvfs struct, otherwise return an error.
       */
      switch (result) {
      case 0:
         stat->f_bsize = HGFS_BLOCKSIZE;
         if (opUsed == HGFS_OP_QUERY_VOLUME_INFO_V3) {
            HgfsReplyQueryVolumeV3 * replyV3 = HgfsGetReplyPayload(req);

            totalBytes = replyV3->totalBytes;
            freeBytes = replyV3->freeBytes;

         } else {
            totalBytes = ((HgfsReplyQueryVolume *)HGFS_REQ_PAYLOAD(req))->totalBytes;
            freeBytes = ((HgfsReplyQueryVolume *)HGFS_REQ_PAYLOAD(req))->freeBytes;
         }
         stat->f_blocks = (totalBytes + HGFS_BLOCKSIZE - 1) / HGFS_BLOCKSIZE;
         stat->f_bfree = (freeBytes + HGFS_BLOCKSIZE - 1) / HGFS_BLOCKSIZE;
         stat->f_bavail = stat->f_bfree;

         /*
          * Files application requires this field see bug 2287577.
          * This is using the same as the GNU C Library defined NAME_MAX
          */
         stat->f_namemax = NAME_MAX;
         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, ("Version 3 not supported. Falling back to version 1.\n"));
            hgfsVersionQueryVolumeInfo = HGFS_OP_QUERY_VOLUME_INFO;
            goto retry;
         }
         break;

      default:
         break;
      }
   } else if (result == -EIO) {
      LOG(4, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   return result;
}
07070100000492000081A40000000000000000000000016822550500000715000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/filesystem.h   /*********************************************************
 * Copyright (C) 2013,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * filesystem.h --
 *
 * High-level filesystem operations for the filesystem portion of
 * the vmhgfs driver.
 */

#ifndef _HGFS_DRIVER_FILESYSTEM_H_
#define _HGFS_DRIVER_FILESYSTEM_H_

#define G_LOG_DOMAIN "vmhgfs-fuse"

#include "vm_basic_types.h"
#include <sys/statvfs.h>
#include "vmware/tools/utils.h"
#include "vmware/tools/log.h"

typedef struct HgfsFuseState {
   Bool sessionEnabled;
   uint64 sessionId;
   uint8 headerVersion;
   uint32 maxPacketSize;
   /*
    * When mount a subdirectory of hgfs shared directory, basePath holds
    * the prefix to the root. e.g. 'mount.vmhgfs .host:/shared/sub /hgfs',
    * base path would be '/shared/sub' (trailing '/' will be removed).
    */
   char *basePath;
   size_t basePathLen;

   GKeyFile *conf;

} HgfsFuseState;

/* Public functions (with respect to the entire module). */
int HgfsStatfs(const char *path, struct statvfs *stat);

#endif // _HGFS_DRIVER_FILESYSTEM_H_
   07070100000493000081A400000000000000000000000168225505000058E6000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/fsutil.c   /*********************************************************
 * Copyright (C) 2013,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * fsutil.c --
 *
 * Functions used in more than one type of filesystem operation will be
 * exported from this file.
 */

#include <limits.h>
#include "module.h"
#include "cache.h"

typedef unsigned short umode_t;


static int
HgfsPackGetattrRequest(HgfsReq *req,
                       HgfsHandle handle,
                       const char* path,
                       Bool allowHandleReuse,
                       HgfsOp opUsed,
                       HgfsAttrInfo *attr);



/*
 *----------------------------------------------------------------------
 *
 * 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;
   void *rawAttr;
   HgfsOp opUsed;

   ASSERT(req);
   ASSERT(attr);

   opUsed = attr->requestType;
   switch (opUsed) {
   case HGFS_OP_GETATTR_V3: {
      HgfsReplyGetattrV3 *getattrReplyV3 = HgfsGetReplyPayload(req);

      rawAttr = &getattrReplyV3->attr;
      break;
   }
   case HGFS_OP_GETATTR_V2: {
      HgfsReplyGetattrV2 *getattrReplyV2 =
         (HgfsReplyGetattrV2 *)(HGFS_REQ_PAYLOAD(req));

      rawAttr = &getattrReplyV2->attr;
      break;
   }
   case HGFS_OP_GETATTR: {
      HgfsReplyGetattr *getattrReplyV1;
      getattrReplyV1 = (HgfsReplyGetattr *)(HGFS_REQ_PAYLOAD(req));
      rawAttr = &getattrReplyV1->attr;
      break;
   }
   default:
      LOG(4, ("Unexpected op in reply packet. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   result = HgfsUnpackCommonAttr(rawAttr, opUsed, attr);
   if (result != 0) {
      return result;
   }

   /* GetattrV2+ also wants a symlink target if it exists. */
   if (attr->requestType == HGFS_OP_GETATTR_V3) {
      HgfsReplyGetattrV3 *replyV3 = HgfsGetReplyPayload(req);

      name = replyV3->symlinkTarget.name;
      length = replyV3->symlinkTarget.length;

      /* Skip the symlinkTarget if it's too long. */
      if (length > HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE),
                                          sizeof *replyV3 + sizeof (HgfsReply))) {
         LOG(4, ("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(HgfsLargePacketMax(FALSE), replyV2)) {
         LOG(4, ("symlink target name too long, ignoring\n"));
         return -ENAMETOOLONG;
      }
   }

   if (length != 0) {
      attr->fileName = (char*)malloc(length + 1);
      if (attr->fileName == NULL) {
         LOG(4, ("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';
   }

   /*
    * Set the access mode. For hosts that don't give us group or other
    * bits (Windows), we use the owner bits in their stead.
    */
   ASSERT(attr->mask & HGFS_ATTR_VALID_OWNER_PERMS);
   if ((attr->mask & HGFS_ATTR_VALID_GROUP_PERMS) == 0) {
      attr->groupPerms = attr->ownerPerms;
      attr->mask |= HGFS_ATTR_VALID_GROUP_PERMS;
   }
   if ((attr->mask & HGFS_ATTR_VALID_OTHER_PERMS) == 0) {
      attr->otherPerms = attr->ownerPerms;
      attr->mask |= HGFS_ATTR_VALID_OTHER_PERMS;
   }

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsPackGetattrRequest --
 *
 *    Setup the getattr request
 *
 * Results:
 *    Returns zero on success, or negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
HgfsPackGetattrRequest(HgfsReq *req,            // IN/OUT: Request buffer
                       HgfsHandle handle,       // IN: file handle
                       const char* path,        // IN: path to a file
                       Bool handleReuse,        // 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;
   ASSERT(attr);
   ASSERT(req);
   ASSERT(path);
   attr->requestType = opUsed;
   (void) handle;
   (void) handleReuse;

   switch (opUsed) {
   case HGFS_OP_GETATTR_V3: {
      HgfsRequestGetattrV3 *requestV3 = HgfsGetRequestPayload(req);

      /* Fill out the request packet. */
      requestV3->hints = 0;
      requestV3->fileName.flags = 0;
      requestV3->fileName.fid = HGFS_INVALID_HANDLE;
      requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;

      requestV3->reserved = 0;
      reqSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();
      reqBufferSize = HGFS_NAME_BUFFER_SIZET(HgfsLargePacketMax(FALSE), reqSize);

      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                reqBufferSize,
                                requestV3->fileName.name);
      LOG(8, ("Converted path %s\n", requestV3->fileName.name));
      if (result < 0) {
         LOG(8, ("CP conversion failed.\n"));
         result = -EINVAL;
         goto out;
      }
      requestV3->fileName.length = result;
      break;
   }

   case HGFS_OP_GETATTR_V2: {
      HgfsRequestGetattrV2 *requestV2;

      LOG(8, ("Version 2 OP type encountered\n"));

      requestV2 = (HgfsRequestGetattrV2 *)(HGFS_REQ_PAYLOAD(req));
      requestV2->hints = 0;
      reqSize = sizeof *requestV2;
      reqBufferSize = HGFS_NAME_BUFFER_SIZE(HgfsLargePacketMax(FALSE), requestV2);

      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                reqBufferSize,
                                requestV2->fileName.name);
      LOG(8, ("Converted path %s\n", requestV2->fileName.name));
      if (result < 0) {
         LOG(8, ("CP conversion failed.\n"));
         result = -EINVAL;
         goto out;
      }
      requestV2->fileName.length = result;
      break;
   }

   case HGFS_OP_GETATTR: {
      HgfsRequestGetattr *requestV1;
      requestV1 = (HgfsRequestGetattr *)(HGFS_REQ_PAYLOAD(req));
      reqSize = sizeof *requestV1;
      reqBufferSize = HGFS_NAME_BUFFER_SIZE(HgfsLargePacketMax(FALSE), requestV1);

      /* Convert to CP name. */
      result = CPName_ConvertTo(path,
                                reqBufferSize,
                                requestV1->fileName.name);
      LOG(8, ("Converted path %s\n", requestV1->fileName.name));
      if (result < 0) {
         LOG(8, ("CP conversion failed.\n"));
         result = -EINVAL;
         goto out;
      }
      requestV1->fileName.length = result;
      break;
   }

   default:
      LOG(8, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      result = -EPROTO;
      goto out;
   }

   req->payloadSize = reqSize + result;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   result = 0;

out:
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsUnpackCommonAttr --
 *
 *    This function abstracts the HgfsAttr struct behind HgfsAttrInfo.
 *
 * Results:
 *    Zero on success, non-zero otherwise.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */
int
HgfsUnpackCommonAttr(void *rawAttr,          // IN: Attr in reply packet
                     HgfsOp requestType,     // IN: request type
                     HgfsAttrInfo *attrInfo) // OUT: Attributes
{
   HgfsAttrV2 *attrV2 = NULL;
   HgfsAttr *attrV1 = NULL;

   ASSERT(rawAttr);
   ASSERT(attrInfo);

   switch (requestType) {
   case HGFS_OP_GETATTR_V3:
      attrV2 = rawAttr;
      break;
   case HGFS_OP_GETATTR_V2:
      attrV2 = rawAttr;
      break;
   case HGFS_OP_GETATTR:
      attrV1 = rawAttr;
      break;
   case HGFS_OP_SEARCH_READ_V3:
      attrV2 = rawAttr;
      break;
   case HGFS_OP_SEARCH_READ_V2:
      attrV2 = rawAttr;
      break;
   case HGFS_OP_SEARCH_READ:
      attrV1 = rawAttr;
      break;
   default:
      LOG(4, ("Unexpected op in reply packet: requestType = %d\n", requestType));
      return -EPROTO;
   }

   attrInfo->requestType = requestType;
   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;
      }
      /* Windows Host */
      if (attrV2->mask & HGFS_ATTR_VALID_NON_STATIC_FILEID) {
         attrInfo->hostFileId = attrV2->hostFileId;
         attrInfo->mask |= HGFS_ATTR_VALID_NON_STATIC_FILEID;
      }

      if (attrV2->mask & HGFS_ATTR_VALID_EFFECTIVE_PERMS) {
         attrInfo->effectivePerms = attrV2->effectivePerms;
         attrInfo->mask |= HGFS_ATTR_VALID_EFFECTIVE_PERMS;
      }
   } 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 |
         HGFS_ATTR_VALID_EFFECTIVE_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;
      attrInfo->effectivePerms = attrV1->permissions;
   }

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * 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(HgfsHandle handle,      // IN: file handle
                   const char* path,       // IN: path
                   HgfsAttrInfo *attr)     // OUT: Attr to copy into
{
   HgfsReq *req;
   HgfsStatus replyStatus;
   HgfsOp opUsed;
   int result = 0;
   Bool allowHandleReuse = TRUE;

   ASSERT(attr);
   LOG( 4,("path = %s, handle = %u\n", path, handle));

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(8, ("Out of memory while getting new request\n"));
      result = -ENOMEM;
      goto out;
   }

retry:
   opUsed = hgfsVersionGetattr;
   LOG(4, ("Before  HgfsPackGetattrRequest\n"));
   result = HgfsPackGetattrRequest(req, handle,
                       path, allowHandleReuse, opUsed, attr);

   LOG(4, ("Before Send, Path = %s result = %d \n", path, result));

   if (result != 0) {
      LOG(8, ("No attrs.\n"));
      goto out;
   }

   result = HgfsSendRequest(req);

   LOG( 4,("After Send, path = %s result = %d \n", path, result));

   if (result == 0) {
      LOG(8, ("Got reply\n"));
      replyStatus = HgfsGetReplyStatus(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:
         /*
          * 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(8, ("Version 3 not supported. Falling back to version 2.\n"));
            hgfsVersionGetattr = HGFS_OP_GETATTR_V2;
            goto retry;
         } else if (attr->requestType == HGFS_OP_GETATTR_V2) {
            LOG(8, ("Version 2 not supported. Falling back to version 1.\n"));
            hgfsVersionGetattr = HGFS_OP_GETATTR;
            goto retry;
         }

         /* Fallthrough. */
      default:
         break;
      }
   } else if (result == -EIO) {
      LOG(8, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(8, ("Server returned error: %d\n", result));
   } else {
      LOG(8, ("Unknown error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * 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, ("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, ("invalid open flags %o\n", flags));
      result = -1;
      break;
   }

   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * 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, ("Unknown error: %u\n", hgfsStatus));
      return -EIO;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsCalcBlockSize --
 *
 *    Calculate the number of 512 byte blocks used.
 *
 *    Round the size to the next whole block and divide by the block size
 *    to get the number of 512 byte blocks.
 *    Note, this is taken from the nfs client and is simply performing:
 *    (size + 512-1)/ 512)
 *
 * Results:
 *    The number of 512 byte blocks for the size.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

unsigned long
HgfsCalcBlockSize(uint64 tsize)
{
   loff_t used = (tsize + 511) >> 9;
   return (used > ULONG_MAX) ? ULONG_MAX : used;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsClearReadOnly --
 *
 *    Try to remove the file/dir read only attribute.
 *
 *    Note when running on Windows servers the entry may have the read-only
 *    flag set and prevent a rename or delete operation from occuring.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsClearReadOnly(const char* path,           // IN: file/dir to remove read only
                  HgfsAttrInfo *enableWrite)  // OUT: attributes
{
   int result;

   LOG(4, ("Entry(path = %s)\n", path));

   result = HgfsGetAttrCache(path, enableWrite);
   LOG(4, ("Retrieve attr from cache. result = %d \n", result));
   if (result != 0) {
      result = HgfsPrivateGetattr(HGFS_INVALID_HANDLE,
                                  path,
                                  enableWrite);
   }

   if (result != 0) {
      LOG(4, ("error: attributes for read-only file\n"));
      goto out;
   }

   LOG(4, ("%s perms %#o %#o %#o\n", path, enableWrite->ownerPerms,
           enableWrite->groupPerms, enableWrite->otherPerms));

   /*
    * Use only the permissions bits and add write for the owner.
    */
   enableWrite->mask &= (HGFS_ATTR_VALID_SPECIAL_PERMS |
                         HGFS_ATTR_VALID_OWNER_PERMS |
                         HGFS_ATTR_VALID_GROUP_PERMS |
                         HGFS_ATTR_VALID_OTHER_PERMS);
   enableWrite->ownerPerms |= HGFS_PERM_WRITE;

   result = HgfsSetattr(path, enableWrite);

out:
   LOG(4, ("Exit(%d)\n", result));
   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsRestoreReadOnly --
 *
 *    Try to restore the file/dir read only attribute.
 *
 *    Note This is the store for the above clear operation and expects
 *    the attributes to have valid owner permissons at a minimum.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsRestoreReadOnly(const char* path,           // IN: file/dir to remove read only
                    HgfsAttrInfo *enableWrite)  // IN: attributes
{
   int result;

   LOG(4, ("Entry(path = %s)\n", path));

   /*
    * Clear the write permissions bit for the owner.
    */
   ASSERT((enableWrite->mask & HGFS_ATTR_VALID_OWNER_PERMS) != 0);

   enableWrite->ownerPerms &= ~HGFS_PERM_WRITE;
   result = HgfsSetattr(path, enableWrite);
   LOG(4, ("Exit(%d)\n", result));
   return result;
}
  07070100000494000081A40000000000000000000000016822550500000F9C000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/fsutil.h   /*********************************************************
 * Copyright (C) 2013 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * 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_


#include "request.h"
#include "vm_basic_types.h"
#include "hgfsProto.h"
#include <fuse.h>

#if defined(__FreeBSD__) || defined(__SOLARIS__) || defined(__APPLE__)
typedef long long loff_t;
#endif

/*
 * Struct used to pass around attributes.
 * 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 */
   HgfsPermissions effectivePerms; /* Permissions in effect for the user on the
                                      host. */
   uint32 userId;                  /* UID */
   uint32 groupId;                 /* GID */
   uint64 hostFileId;              /* Inode number */
   char *fileName;                 /* Either symlink target or filename */
} HgfsAttrInfo;

int
HgfsClearReadOnly(const char* path,
                  HgfsAttrInfo *enableWrite);

int
HgfsRestoreReadOnly(const char* path,
                    HgfsAttrInfo *enableWrite);

ssize_t
HgfsWrite(struct fuse_file_info *fi,
          const char  *buf,
          size_t count,
          loff_t offset);

int
HgfsRename(const char* from, const char* to);

/* HGFS file operations for files. */

int
HgfsOpen(const char *path,
         struct fuse_file_info *fi);

int
HgfsCreate(const char *path,
           mode_t permsMode,
           struct fuse_file_info *fi);

ssize_t
HgfsRead(struct fuse_file_info *fi,
         char  *buf,
         size_t count,
         loff_t offset);

int
HgfsSetattr(const char* path,
            HgfsAttrInfo *attr);


int HgfsUnpackCommonAttr(void *rawAttr,
                         HgfsOp requestType,
                         HgfsAttrInfo *attrInfo);

int
HgfsPrivateGetattr(HgfsHandle handle,
                   const char* path,
                   HgfsAttrInfo *attr);

int
HgfsStatusConvertToLinux(HgfsStatus hgfsStatus);

int
HgfsGetOpenMode(uint32 flags);


int
HgfsDirOpen(const char* path, HgfsHandle* handle);

int
HgfsReaddir(HgfsHandle handle,
            void *dirent,
            fuse_fill_dir_t filldir);

int
HgfsMkdir(const char *path,
          int mode);

int
HgfsDelete(const char* path,
           HgfsOp op);

int
HgfsSymlink(const char* source,
            const char *symname);

void
HgfsResetOps(void);

unsigned long
HgfsCalcBlockSize(uint64 tsize);

#endif // _HGFS_DRIVER_FSUTIL_H_
07070100000495000081A40000000000000000000000016822550500001E2F000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/link.c /*********************************************************
 * Copyright (c) 2013-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * link.c --
 *
 * Symlink-specific operations for HGFS driver
 */



#include "module.h"


/*
 *----------------------------------------------------------------------
 *
 * 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(const char* symlink,     // IN: path of the link
                             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;
   size_t targetNameBytes;
   size_t requestSize;

   targetNameBytes = strlen(symname) + 1;

   switch (opUsed) {
   case HGFS_OP_CREATE_SYMLINK_V3: {
      int result;
      HgfsFileNameV3 *fileNameP;
      requestV3 = HgfsGetRequestPayload(req);

      requestV3->symlinkName.flags = 0;
      requestV3->symlinkName.fid = HGFS_INVALID_HANDLE;
      requestV3->symlinkName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
      requestV3->reserved = 0;
      requestSize = sizeof(*requestV3) + HgfsGetRequestHeaderSize();
      /* Convert symlink name to CP format. */
      result = CPName_ConvertTo(symlink,
                                HgfsLargePacketMax(FALSE) - (requestSize - 1),
                                requestV3->symlinkName.name);
      if (result < 0) {
         LOG(4, ("SymlinkName CP conversion failed.\n"));
         return -EINVAL;
      }
      requestV3->symlinkName.length = result;
      requestSize += result;

      /* Copy target name into request packet. */
      if (targetNameBytes > HgfsLargePacketMax(FALSE) - (requestSize - 1)) {
         LOG(4, ("Target name is too long.\n"));
         return -EINVAL;
      }

      fileNameP = (HgfsFileNameV3 *)((char *)&requestV3->symlinkName +
                                     sizeof requestV3->symlinkName + result);
      memcpy(fileNameP->name, symname, targetNameBytes);
      LOG(6, ("Target name: \"%s\"\n", fileNameP->name));
      /* Convert target name to CPName-lite format. */
      CPNameLite_ConvertTo(fileNameP->name, targetNameBytes - 1, '/');
      /*
       * The req size is always sufficient to hold the request data.
       * There is no overrun here, coverity has issue with how the data is
       * packed (name fields data are interlaced).
       */
      /* coverity[overrun-local] */
      fileNameP->length = targetNameBytes - 1;
      fileNameP->flags = 0;
      fileNameP->fid = HGFS_INVALID_HANDLE;
      fileNameP->caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
      break;
   }
   case HGFS_OP_CREATE_SYMLINK: {
      int result;
      HgfsFileName *fileNameP;
      request = (HgfsRequestSymlinkCreate *)(HGFS_REQ_PAYLOAD(req));

      requestSize = sizeof *request;
      /* Convert symlink name to CP format. */
      result = CPName_ConvertTo(symlink,
                                HgfsLargePacketMax(FALSE) - (requestSize - 1),
                                request->symlinkName.name);
      if (result < 0) {
         LOG(4, ("SymlinkName CP conversion failed.\n"));
         return -EINVAL;
      }
      request->symlinkName.length = result;
      requestSize += result;

      /* Copy target name into request packet. */
      if (targetNameBytes > HgfsLargePacketMax(FALSE) - (requestSize - 1)) {
         LOG(4, ("Target name is too long.\n"));
         return -EINVAL;
      }

      fileNameP = (HgfsFileName *)((char *)&request->symlinkName +
                                   sizeof request->symlinkName + result);
      memcpy(fileNameP->name, symname, targetNameBytes);
      LOG(6, ("Target name: \"%s\"\n", fileNameP->name));
      /* Convert target name to CPName-lite format. */
      CPNameLite_ConvertTo(fileNameP->name, targetNameBytes - 1, '/');
      /*
       * The req size is always sufficient to hold the request data.
       * There is no overrun here, coverity has issue with how the data is
       * packed (name fields data are interlaced).
       */
      /* coverity[overrun-local] */
      fileNameP->length = targetNameBytes - 1;
      break;
   }
   default:
      LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   req->payloadSize = requestSize;

   /*
    * 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.
    */
   req->payloadSize += targetNameBytes - 1;

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsSymlink --
 *
 *    Handle a symlink request
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsSymlink(const char* source,    // IN: Source name
            const char *symname)   // IN: Target name
{
   HgfsReq *req;
   int result = 0;
   HgfsOp opUsed;
   HgfsStatus replyStatus;

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request.\n"));
      result = -ENOMEM;
      goto out;
   }

  retry:
   opUsed = hgfsVersionCreateSymlink;
   result = HgfsPackSymlinkCreateRequest(source, symname, opUsed, req);
   if (result != 0) {
      LOG(4, ("Error packing request.\n"));
      goto out;
   }

   result = HgfsSendRequest(req);
   if (result == 0) {
      LOG(6, ("Got reply.\n"));
      replyStatus = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(replyStatus);
      if (result == 0) {
         LOG(6, ("Symlink created successfully, instantiating dentry.\n"));
      } else if (result == -EPROTO) {
         /* Retry with older version(s). Set globally. */
         if (opUsed == HGFS_OP_CREATE_SYMLINK_V3) {
            LOG(4, ("Version 3 not supported. Falling back to version 2.\n"));
            hgfsVersionCreateSymlink = HGFS_OP_CREATE_SYMLINK;
            goto retry;
         } else {
            LOG(6, ("Symlink was not created, error %d\n", result));
         }
      }
   } else if (result == -EIO) {
      LOG(4, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);

   return result;
}
 07070100000496000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/linux  07070100000497000081A40000000000000000000000016822550500001DAA000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/linux/list.h   /*********************************************************
 * Copyright (C) 2013 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

#ifndef __LIST_H
#define __LIST_H

/*
 * This file is from Linux Kernel (include/linux/list.h)
 * and modified by simply removing hardware prefetching of list items.
 * Here by copyright, credits attributed to wherever they belong.
 * Kulesh Shanmugasundaram (kulesh [squiggly] isis.poly.edu)
 */

/*
 * Simple doubly linked list implementation.
 *
 * Some of the internal functions ("__xxx") are useful when
 * manipulating whole lists rather than single entries, as
 * sometimes we already know the next/prev entries and we can
 * generate better code by using them directly rather than
 * using the generic single-entry routines.
 */

struct list_head {
	struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

#define INIT_LIST_HEAD(ptr) do { \
	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next)
{
	next->prev = new;
	new->next = next;
	new->prev = prev;
	prev->next = new;
}

/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}

/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
	next->prev = prev;
	prev->next = next;
}

/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
 */
static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = (void *) 0;
	entry->prev = (void *) 0;
}

/**
 * list_del_init - deletes entry from list and reinitialize it.
 * @entry: the element to delete from the list.
 */
static inline void list_del_init(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	INIT_LIST_HEAD(entry);
}

/**
 * list_move - delete from one list and add as another's head
 * @list: the entry to move
 * @head: the head that will precede our entry
 */
static inline void list_move(struct list_head *list, struct list_head *head)
{
        __list_del(list->prev, list->next);
        list_add(list, head);
}

/**
 * list_move_tail - delete from one list and add as another's tail
 * @list: the entry to move
 * @head: the head that will follow our entry
 */
static inline void list_move_tail(struct list_head *list,
				  struct list_head *head)
{
        __list_del(list->prev, list->next);
        list_add_tail(list, head);
}

/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(struct list_head *head)
{
	return head->next == head;
}

static inline void __list_splice(struct list_head *list,
				 struct list_head *head)
{
	struct list_head *first = list->next;
	struct list_head *last = list->prev;
	struct list_head *at = head->next;

	first->prev = head;
	head->next = first;

	last->next = at;
	at->prev = last;
}

/**
 * list_splice - join two lists
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static inline void list_splice(struct list_head *list, struct list_head *head)
{
	if (!list_empty(list))
		__list_splice(list, head);
}

/**
 * list_splice_init - join two lists and reinitialise the emptied list.
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * The list at @list is reinitialised
 */
static inline void list_splice_init(struct list_head *list,
				    struct list_head *head)
{
	if (!list_empty(list)) {
		__list_splice(list, head);
		INIT_LIST_HEAD(list);
	}
}

/**
 * list_entry - get the struct for this entry
 * @ptr:	the &struct list_head pointer.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

/**
 * list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop counter.
 * @head:	the head for your list.
 */
#define list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); \
        	pos = pos->next)
/**
 * list_for_each_prev	-	iterate over a list backwards
 * @pos:	the &struct list_head to use as a loop counter.
 * @head:	the head for your list.
 */
#define list_for_each_prev(pos, head) \
	for (pos = (head)->prev; pos != (head); \
        	pos = pos->prev)

/**
 * list_for_each_safe	-	iterate over a list safe against removal of list entry
 * @pos:	the &struct list_head to use as a loop counter.
 * @n:		another &struct list_head to use as temporary storage
 * @head:	the head for your list.
 */
#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)

/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop counter.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#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))

/**
 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:	the type * to use as a loop counter.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry_safe(pos, n, head, member)			\
	for (pos = list_entry((head)->next, typeof(*pos), member),	\
		n = list_entry(pos->member.next, typeof(*pos), member);	\
	     &pos->member != (head); 					\
	     pos = n, n = list_entry(n->member.next, typeof(*n), member))


#endif
  07070100000498000081A4000000000000000000000001682255050000847A000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/main.c /*********************************************************
 * Copyright (c) 2013,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * main.c --
 *
 * Main entry points for fuse file operations for HGFS
 */

#include "module.h"
#include "cache.h"
#include "filesystem.h"
#include "file.h"

/*
 *----------------------------------------------------------------------
 *
 * getAbsPath
 *
 *    This function takes the file path used by the local file system
 *    (which is relative to the HGFS mount) and generates the HGFS absolute
 *    path to send to the HGFS server.
 *    the absolute path will be allocated, which is the HGFS share and
 *    all other intermediate subdirectories that contain the file or folder.
 *
 * Results:
 *    zero on success, negative number for error.
 *
 * Side effects:
 *    Caller is responsible to free the absolute path if it's different
 *    from the path.
 *
 *----------------------------------------------------------------------
 */

static int
getAbsPath(const char *path, // IN
           char **abspath)   // OUT
{
   size_t size;
   int res = 0;

   if (gState->basePathLen > 0) {
      size = gState->basePathLen + strlen(path) + 1;
      *abspath = (char *)malloc(size);
      if (*abspath == NULL) {
         LOG(4, ("Can't allocate memory!\n"));
         res = -ENOMEM;
         goto exit;
      }
      snprintf(*abspath, size, "%s%s", gState->basePath, path);

   } else {
      *abspath = (char *)path;
   }

exit:
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * freeAbsPath
 *
 *    This function free's the absolute path allocated from getAbsPath
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static void
freeAbsPath(char *abspath)  // IN
{
   if (gState->basePathLen > 0) {
      free(abspath);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_getattr
 *
 *    Get the attributes from the HGFS server and populate struct stat.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
static int
hgfs_getattr(const char *path,           //IN: path of a file/directory
             struct stat *stbuf,         //IN/OUT: file/directoy attribute
             struct fuse_file_info *fi)  //IN/OUT: Unused
#else
static int
hgfs_getattr(const char *path,    //IN: path of a file/directory
             struct stat *stbuf)  //IN/OUT: file/directoy attribute
#endif
{
   HgfsHandle fileHandle = HGFS_INVALID_HANDLE;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *attr = &newAttr;
   uint32 d_type;
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s)\n", path));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsGetAttrCache(abspath, attr);
   LOG(4, ("Retrieve attr from cache. result = %d \n", res));
   if (res != 0) {
      /* Retrieve new complete attribute settings and update the cache. */
      res = HgfsPrivateGetattr(fileHandle, abspath, attr);
      LOG(4, ("Retrieve attr from server. result = %d \n", res));
      if (res == 0 ) {
         HgfsSetAttrCache(abspath, attr);
      }
   }

   if (res < 0) {
      goto exit;
   }

   LOG(4, ("fill stat for %s\n", abspath));

   memset(stbuf, 0, sizeof *stbuf);

   if (attr->mask & HGFS_ATTR_VALID_SPECIAL_PERMS) {
      stbuf->st_mode |= (attr->specialPerms << 9);
   }
   if (attr->mask & HGFS_ATTR_VALID_OWNER_PERMS) {
      stbuf->st_mode |= (attr->ownerPerms << 6);
   }
   if (attr->mask & HGFS_ATTR_VALID_GROUP_PERMS) {
      stbuf->st_mode |= (attr->groupPerms << 3);
   }
   if (attr->mask & HGFS_ATTR_VALID_OTHER_PERMS) {
      stbuf->st_mode |= (attr->otherPerms);
   }

   /* Mask the access mode. */
   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:
      d_type = DT_UNKNOWN;
      break;
   }

   stbuf->st_mode |= d_type << 12;
   stbuf->st_blksize = HGFS_BLOCKSIZE;
   stbuf->st_blocks = HgfsCalcBlockSize(attr->size);
   stbuf->st_size = attr->size;
   stbuf->st_ino = attr->hostFileId;
   stbuf->st_nlink = 1;
   stbuf->st_uid = attr->userId;
   stbuf->st_gid = attr->groupId;
   stbuf->st_rdev = 0;

   if (attr->mask & HGFS_ATTR_VALID_ACCESS_TIME) {
      HGFS_SET_TIME(stbuf->st_atime, attr->accessTime);
   }
   if (attr->mask & HGFS_ATTR_VALID_WRITE_TIME) {
      HGFS_SET_TIME(stbuf->st_mtime, attr->writeTime);
   }
   if (attr->mask & HGFS_ATTR_VALID_CHANGE_TIME) {
      HGFS_SET_TIME(stbuf->st_ctime, attr->attrChangeTime);
   }

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_access
 *
 *    Dummy routine, not fully implemented.
 *
 * Results:
 *    Returns zero.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_access(const char *path,  //IN: Path to a file.
            int mask)          //IN: Mask
{
   HgfsHandle fileHandle = HGFS_INVALID_HANDLE;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *attr = &newAttr;
   uint32 effectivePermissions;
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s, mask = %#o)\n", path, mask));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsGetAttrCache(path, attr);
   LOG(4, ("Retrieve attr from cache. result = %d \n", res));
   if (res != 0) {
      /* Retrieve new complete attribute settings and update the cache. */
      res = HgfsPrivateGetattr(fileHandle, abspath, attr);
      LOG(4, ("Retrieve attr from server. result = %d \n", res));
      if (res == 0 ) {
         HgfsSetAttrCache(abspath, attr);
      }
   }

   if (res < 0) {
      goto exit;
   }

   if (mask == F_OK) {
      res = 0;  /* assume the above attr retrieval did the validation */
      goto exit;
   }

   if (attr->mask & HGFS_ATTR_VALID_EFFECTIVE_PERMS) {
      effectivePermissions = attr->effectivePerms;
   } else {
     /*
      * If the server did not return actual effective permissions then
      * need to calculate ourselves. However we should avoid unnecessary
      * denial of access so perform optimistic permissions calculation.
      * It is safe since host enforces necessary restrictions regardless of
      * the client's decisions.
      */
     effectivePermissions = (attr->ownerPerms |
                             attr->groupPerms |
                             attr->otherPerms);
  }

  if ((effectivePermissions & mask) != mask) {
     res = -EACCES;
  }

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_readlink
 *
 *    Read the file pointed by the symbolic link.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_readlink(const char *path, //IN: path to a file
              char *buf,        //OUT: buffer to store the filename
              size_t size)      //IN: size of buf
{
   char *abspath = NULL;
   int res = 0;
   HgfsHandle fileHandle = 0;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *attr = &newAttr;

   LOG(4, ("Entry(path = %s, %#"FMTSZ"x)\n", path, size));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   /* The attributes fileName field will hold the symlink target name. */
   res = HgfsPrivateGetattr(fileHandle, abspath, attr);
   LOG(4, ("ReadLink: Path = %s, attr->fileName = %s \n", abspath, attr->fileName));
   if (res < 0) {
      goto exit;
   }

   if (size > strlen(attr->fileName)) {
      Str_Strcpy(buf, attr->fileName,
                 strlen(attr->fileName) + 1);
      LOG(4, ("ReadLink: link target name = %s\n", buf));
   } else {
      res = -ENOBUFS;
   }

exit:
   free(attr->fileName);
   freeAbsPath(abspath);
   LOG(4, ("Exit(%d)\n", res));
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_readdir
 *
 *    Read the directoy file.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
static int
hgfs_readdir(const char *path,              //IN: path to a directory
             void *buf,                     //OUT: buffer to fill the dir entry
             fuse_fill_dir_t filler,        //IN: function pointer to fill buf
             off_t offset,                  //IN: offset to read the dir
             struct fuse_file_info *fi,     //IN: file info set by open call
             enum fuse_readdir_flags flags) //IN: unused
#else
static int
hgfs_readdir(const char *path,          //IN: path to a directory
             void *buf,                 //OUT: buffer to fill the dir entry
             fuse_fill_dir_t filler,    //IN: function pointer to fill buf
             off_t offset,              //IN: offset to read the dir
             struct fuse_file_info *fi) //IN: file info set by open call
#endif
{
   char *abspath = NULL;
   int res = 0;
   HgfsHandle fileHandle = HGFS_INVALID_HANDLE;

   LOG(4, ("Entry(path = %s, @ %#"FMT64"x)\n", path, offset));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsDirOpen(abspath, &fileHandle);
   if (res < 0) {
      goto exit;
   }

   fi->fh = fileHandle;
   res = HgfsReaddir(fileHandle, buf, filler);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_mknod
 *
 *    Dummy routine.
 *
 * Results:
 *    Returns -1.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_mknod(const char *path,   //IN: Path to a file
           mode_t mode,        //IN: Mode to set
           dev_t rdev)         //IN: Device type
{
#if defined(__APPLE__)
   LOG(4, ("Entry(path = %s, mode = %#o, %u)\n", path, mode, rdev));
#else
   LOG(4, ("Entry(path = %s, mode = %#o, %"FMT64"u)\n", path, mode, rdev));
#endif
   LOG(4, ("Dummy routine. Not implemented!"));
   LOG(4, ("Exit(0)\n"));
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_mkdir
 *
 *    Create directory.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_mkdir(const char *path,  //IN: path to a new dir
           mode_t mode)       //IN: Mode of dir to be created
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s, mode = %#o)\n", path, mode));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsMkdir(abspath, mode);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_unlink
 *
 *    Delete file.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_unlink(const char *path) //IN: path to a file
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s)\n", path));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsDelete(abspath, HGFS_OP_DELETE_FILE);
   if (res == 0) {
      HgfsInvalidateAttrCache(abspath);
   }

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_rmdir
 *
 *    Delete directory.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_rmdir(const char *path) //IN: path to a dir
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s)\n", path));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsDelete(abspath, HGFS_OP_DELETE_DIR);
   if (res == 0) {
      HgfsInvalidateAttrCache(abspath);
   }

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_symlink
 *
 *    Create symbolic link.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_symlink(const char *symname,   //IN: symname target
             const char *source)    //IN: source name
{
   char *absSource = NULL;
   int res;

   LOG(4, ("Entry(from = %s, to = %s)\n", symname, source));
   res = getAbsPath(source, &absSource);
   if (res < 0) {
      goto exit;
   }

   LOG(4, ("symname = %s, abs source = %s)\n", symname, absSource));
   res = HgfsSymlink(absSource, symname);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(absSource);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_rename
 *
 *    Rename file or directory.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
static int
hgfs_rename(const char *from,    //IN: from path name
            const char *to,      //IN: to path name
            unsigned int flags)  //IN: unused
#else
static int
hgfs_rename(const char *from,  //IN: from path name
            const char *to)    //IN: to path name
#endif
{
   char *absfrom = NULL;
   char *absto = NULL;
   int res;

   LOG(4, ("Entry(from = %s, to = %s)\n", from, to));
   res = getAbsPath(from, &absfrom);
   if (res < 0) {
      goto exit;
   }
   res = getAbsPath(to, &absto);
   if (res < 0) {
      goto exit;
   }

   res = HgfsRename(absfrom, absto);
   if (res == 0) {
      HgfsInvalidateAttrCache(absfrom);
      HgfsInvalidateAttrCache(absto);
   }

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(absfrom);
   freeAbsPath(absto);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_link
 *
 *    Dummy routine.
 *
 * Results:
 *    Returns -1.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_link(const char *from,  //IN: From path
          const char *to)    //IN: To path
{
   char *absfrom = NULL;
   char *absto = NULL;
   int res = 0;

   LOG(4, ("Entry(from = %s, to = %s)\n", from, to));
   res = getAbsPath(from, &absfrom);
   if (res < 0) {
      goto exit;
   }
   res = getAbsPath(to, &absto);
   if (res < 0) {
      goto exit;
   }

   /*Do nothing.*/
   res = -1;

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(absfrom);
   freeAbsPath(absto);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_chmod
 *
 *    Change access mode of a file.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
static int
hgfs_chmod(const char *path,          //IN: path to a file
           mode_t mode,               //IN: mode to set
           struct fuse_file_info *fi) //IN/OUT: unused
#else
static int
hgfs_chmod(const char *path,   //IN: path to a file
           mode_t mode)        //IN: mode to set
#endif
{
   char *abspath = NULL;
   int res;
   HgfsHandle fileHandle = HGFS_INVALID_HANDLE;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *attr = &newAttr;

   LOG(4, ("Entry(path = %s, mode = %#o)\n", path, mode));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   attr->mask = (HGFS_ATTR_VALID_SPECIAL_PERMS |
                 HGFS_ATTR_VALID_OWNER_PERMS |
                 HGFS_ATTR_VALID_GROUP_PERMS |
                 HGFS_ATTR_VALID_OTHER_PERMS);
   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_ACCESS_TIME;
   attr->accessTime = attr->attrChangeTime = HGFS_GET_TIME(time(NULL));

   res = HgfsSetattr(abspath, attr);
   if (res < 0) {
      LOG(4, ("path = %s , HgfsSetattr failed. res = %d\n", abspath, res));
      goto exit;
   }

   /* Retrieve new complete attribute settings and update the cache. */
   res = HgfsPrivateGetattr(fileHandle, abspath, attr);
   if (res < 0) {
      LOG(4, ("path = %s , res = %d\n", abspath, res));
      goto exit;
   }
   HgfsSetAttrCache(abspath, attr);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_chown
 *
 *    Change uid or gid of a file.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
static int
hgfs_chown(const char *path,           //IN: Path to a file
           uid_t uid,                  //IN: User id
           gid_t gid,                  //IN: Group id
           struct fuse_file_info *fi)  //IN/OUT: unused
#else
static int
hgfs_chown(const char *path,  //IN: Path to a file
           uid_t uid,         //IN: User id
           gid_t gid)         //IN: Group id
#endif
{
   HgfsHandle fileHandle = HGFS_INVALID_HANDLE;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *attr = &newAttr;
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s, uid = %u, gid = %u)\n", abspath, uid, gid));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   attr->mask = HGFS_ATTR_VALID_USERID;
   attr->userId = uid;

   attr->mask |= HGFS_ATTR_VALID_GROUPID;
   attr->groupId = gid;

   attr->mask |= HGFS_ATTR_VALID_ACCESS_TIME;
   attr->accessTime = attr->attrChangeTime = HGFS_GET_TIME(time(NULL));

   res = HgfsSetattr(abspath, attr);
   if (res < 0) {
      LOG(4, ("path = %s , HgfsSetattr failed. res = %d\n", abspath, res));
      goto exit;
   }

   /* Retrieve new complete attribute settings and update the cache. */
   res = HgfsPrivateGetattr(fileHandle, abspath, attr);
   if (res < 0) {
      LOG(4, ("path = %s , res = %d\n", abspath, res));
      goto exit;
   }
   HgfsSetAttrCache(abspath, attr);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_truncate
 *
 *    Truncate a file to the given size.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
static int
hgfs_truncate(const char *path,           //IN: path to a file
              off_t size,                 //IN: new size
              struct fuse_file_info *fi)  //IN/OUT: unused
#else
static int
hgfs_truncate(const char *path,  //IN: path to a file
              off_t size)        //IN: new size
#endif
{
   HgfsHandle fileHandle = HGFS_INVALID_HANDLE;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *attr = &newAttr;
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s, size %"FMT64"x)\n", path, size));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   attr->mask = HGFS_ATTR_VALID_SIZE;
   attr->size = size;

   attr->mask |= (HGFS_ATTR_VALID_WRITE_TIME |
                  HGFS_ATTR_VALID_ACCESS_TIME |
                  HGFS_ATTR_VALID_CHANGE_TIME);
   attr->writeTime = attr->accessTime = attr->attrChangeTime = HGFS_GET_TIME(time(NULL));

   res = HgfsSetattr(abspath, attr);
   if (res < 0) {
      LOG(4, ("path = %s , HgfsSetattr failed. res = %d\n", abspath, res));
      goto exit;
   }

   /* Retrieve new complete attribute settings and update the cache. */
   res = HgfsPrivateGetattr(fileHandle, abspath, attr);
   if (res < 0) {
      LOG(4, ("path = %s , res = %d\n", abspath, res));
      goto exit;
   }
   HgfsSetAttrCache(abspath, attr);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_utimens/hgfs_utime
 *
 *    Update access and write time of a file.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
static int
hgfs_utimens(const char *path,              //IN: path to a file
             const struct timespec ts[2],   //IN: new time
             struct fuse_file_info *fi)     //IN/OUT: unused
#else
static int
hgfs_utimens(const char *path,              //IN: path to a file
             const struct timespec ts[2])   //IN: new time
#endif
{
   HgfsHandle fileHandle = HGFS_INVALID_HANDLE;
   HgfsAttrInfo newAttr = {0};
   HgfsAttrInfo *attr = &newAttr;
   time_t accessTimeSec, accessTimeNsec, writeTimeSec, writeTimeNsec;
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s)\n", path));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsGetAttrCache(abspath, attr);
   LOG(4, ("Retrieve attr from cache. result = %d \n", res));
   if (res != 0) {
      /* Retrieve new complete attribute settings and update the cache. */
      res = HgfsPrivateGetattr(fileHandle, abspath, attr);
      LOG(4, ("Retrieve attr from server. result = %d \n", res));
      if (res == 0 ) {
         HgfsSetAttrCache(abspath, attr);
      }
   }

   if (res != 0) {
      /* Failed to retrieve file attributes. */
      goto exit;
   }

   if (attr->type == HGFS_FILE_TYPE_SYMLINK) {
      /*
       * utimensat() has a 'flag' parameter which is not available in fuse.
       * by default, let's assume AT_SYMLINK_NOFOLLOW. but since there is
       * neither a way to pass not 'followSymlinks' to setattr, let's simply
       * do nothing for symlink.
       */
      goto exit;
   }

   attr->mask = (HGFS_ATTR_VALID_WRITE_TIME |
                 HGFS_ATTR_VALID_ACCESS_TIME);

   accessTimeSec = ts[0].tv_sec;
   accessTimeNsec = ts[0].tv_nsec;
   writeTimeSec = ts[1].tv_sec;
   writeTimeNsec = ts[1].tv_nsec;
   attr->accessTime = HgfsConvertToNtTime(accessTimeSec, accessTimeNsec);
   attr->writeTime = HgfsConvertToNtTime(writeTimeSec, writeTimeNsec);

   res = HgfsSetattr(abspath, attr);
   if (res < 0) {
      LOG(4, ("abspath = %s , HgfsSetattr failed. res = %d\n", abspath, res));
      goto exit;
   }

   /* Retrieve new complete attribute settings and update the cache. */
   res = HgfsPrivateGetattr(fileHandle, abspath, attr);
   if (res < 0) {
      LOG(4, ("abspath = %s , res = %d\n", abspath, res));
      goto exit;
   }
   HgfsSetAttrCache(abspath, attr);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_open
 *
 *    Open file at a given path.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_open(const char *path,          //IN: path to a file
          struct fuse_file_info *fi) //IN: file info structure
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s)\n", path));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsOpen(abspath, fi);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_create
 *
 *    Create a new file, we do it by calling HgfsOpen.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_create(const char *path,          //IN: path to a file
            mode_t mode,               //IN: file mode
            struct fuse_file_info *fi) //IN: file info structure
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s, mode = %#o)\n", path, mode));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsCreate(abspath, mode, fi);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_read
 *
 *    Read the file using the handle, if the handle is zero
 *    then open the file first and then read.
 *
 * Results:
 *    Returns the number of bytes read from the file.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_read(const char *path,          //IN: path to a file
          char *buf,                 //OUT: to store the data
          size_t size,               //IN: size to read
          off_t offset,              //IN: starting point to read
          struct fuse_file_info *fi) //IN: file info structure
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s, fi->fh = %#"FMT64"x, %#"FMTSZ"x bytes @ %#"FMT64"x)\n",
           path, fi->fh, size, offset));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   if (fi->fh == HGFS_INVALID_HANDLE) {
      res = HgfsOpen(abspath, fi);
      if (res) {
         goto exit;
      }
   }
   res = HgfsRead(fi, buf, size, offset);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_write
 *
 *    Write to the file using the handle, if the handle is zero
 *    then open the file first and then write.
 *
 * Results:
 *    Returns the number of bytes written to the file.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_write(const char *path,          //IN: path to a file
           const char *buf,           //IN: data to write
           size_t size,               //IN: size to write
           off_t offset,              //IN: starting point to write
           struct fuse_file_info *fi) //IN: file info structure
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s, fi->fh = %#"FMT64"x, write %#"FMTSZ"x bytes @ %#"FMT64"x)\n",
           path, fi->fh, size, offset));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   if (fi->fh == HGFS_INVALID_HANDLE) {
      res = HgfsOpen(abspath, fi);
      if (res) {
         goto exit;
      }
   }

   res = HgfsWrite(fi, buf, size, offset);
   if (res >= 0) {
      /*
       * Positive result indicates the number of bytes written.
       * For zero bytes and no error, we still purge the cache
       * this could effect the attributes.
       */
      HgfsInvalidateAttrCache(abspath);
   }

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}

/*
 *----------------------------------------------------------------------
 *
 * hgfs_statfs
 *
 *    Stat the host for total and free bytes on disk.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_statfs(const char *path,      //IN: Path to the filesystem
            struct statvfs *stbuf) //OUT:Struct to fill data
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s)\n", path));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsStatfs(abspath, stbuf);

exit:
   LOG(4, ("Exit(%d)\n", res));
   freeAbsPath(abspath);
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_release
 *
 *    Release a file.
 *
 * Results:
 *    Returns zero.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static int
hgfs_release(const char *path,                //IN: path to a file
             struct fuse_file_info *fi)       //IN: file info structure
{
   char *abspath = NULL;
   int res;

   LOG(4, ("Entry(path = %s, fi->fh = %#"FMT64"x)\n", path, fi->fh));
   res = getAbsPath(path, &abspath);
   if (res < 0) {
      goto exit;
   }

   res = HgfsRelease(fi->fh);
   if (0 == res) {
      fi->fh = HGFS_INVALID_HANDLE;
   }

exit:
   LOG(4, ("Exit(0)\n"));
   freeAbsPath(abspath);
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_init
 *
 *    Initialization routine. We spawn the cache purge thread here.
 *
 * Results:
 *    Returns NULL.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

#if FUSE_MAJOR_VERSION == 3
static void*
hgfs_init(struct fuse_conn_info *conn, // IN: unused
          struct fuse_config *cfg)     // IN/OUT: unused
#else
static void*
hgfs_init(struct fuse_conn_info *conn) // IN: unused
#endif
{
   pthread_t purgeCacheThread;
   int dummy;
   int res;

   LOG(4, ("Entry()\n"));

   /*
    * dummy argument is required for Solaris and FreeBSD while creating
    * thread otherwise the program crashes.
    */
   res = pthread_create(&purgeCacheThread, NULL,
                        HgfsPurgeCache, &dummy);
   if (res < 0) {
      LOG(4, ("Pthread create fail. error = %d\n", res));
   }

   res = HgfsCreateSession();
   if (res < 0) {
      LOG(4, ("Create session failed. error = %d\n", res));
   }

   LOG(4, ("Exit(NULL)\n"));
   return NULL;
}


/*
 *----------------------------------------------------------------------
 *
 * hgfs_destroy
 *
 *    Cleanup routine.
 *
 * Results:
 *    Returns NULL.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

static void
hgfs_destroy(void *data) // IN: unused
{
   int res;

   LOG(4, ("Entry()\n"));

   res = HgfsDestroySession();
   if (res < 0) {
      LOG(4, ("Destroy session failed. error = %d\n", res));
   }

   HgfsTransportExit();

   free(gState->basePath);

   if (gState->conf != NULL) {
      g_key_file_free(gState->conf);
      gState->conf = NULL;
   }

   LOG(4, ("Exit()\n"));
}


/*--------------------------------------------------------------------------- */
static struct fuse_operations vmhgfs_operations = {
   .getattr     = hgfs_getattr,
   .access      = hgfs_access,
   .readlink    = hgfs_readlink,
   .readdir     = hgfs_readdir,
   .mknod       = hgfs_mknod,
   .mkdir       = hgfs_mkdir,
   .symlink     = hgfs_symlink,
   .unlink      = hgfs_unlink,
   .rmdir       = hgfs_rmdir,
   .rename      = hgfs_rename,
   .link        = hgfs_link,
   .chmod       = hgfs_chmod,
   .chown       = hgfs_chown,
   .truncate    = hgfs_truncate,
   .utimens     = hgfs_utimens,
   .open        = hgfs_open,
   .read        = hgfs_read,
   .write       = hgfs_write,
   .statfs      = hgfs_statfs,
   .release     = hgfs_release,
   .create      = hgfs_create,
   .init        = hgfs_init,
   .destroy     = hgfs_destroy,
};


/*
 *----------------------------------------------------------------------
 *
 * main
 *
 *    Starting point of the program.
 *
 * Results:
 *    Returns zero on success, or a negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
main(int argc,       //IN: Argument count
     char *argv[])   //IN: Argument list
{
   struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
   int res;

   res = vmhgfsPreprocessArgs(&args);
   if (res != 0) {
      fprintf(stderr, "Error parsing arguments!\n");
      exit(1);
   }

   /* Initialization */
   umask(0);
   HgfsResetOps();
   res = HgfsTransportInit();
   if (res != 0) {
      fprintf(stderr, "Error %d cannot open connection!\n", res);
      return res;
   }
   HgfsInitCache();

   return fuse_main(args.argc, args.argv, &vmhgfs_operations, NULL);
}

  07070100000499000081A40000000000000000000000016822550500000F70000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/module.h   /*********************************************************
 * Copyright (c) 2013,2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * module.h --
 *
 * Global module definitions for the entire FUSE based HGFS
 */

#ifndef _VMHGFS_FUSE_MODULE_H_
#define _VMHGFS_FUSE_MODULE_H_

/*
 * FUSE_USE_VERSION must be set before the fuse or fuse3 headers are
 * included.  If undefined, fall back to previous default used.
 */
#ifndef FUSE_USE_VERSION
#   define FUSE_USE_VERSION 29
#endif

#include <sys/types.h>
#include "hgfsUtil.h"
#include "vm_assert.h"
#include <stdlib.h>
#include "vm_assert.h"
#include "cpName.h"
#include "cpNameLite.h"
#include "hgfsDevLinux.h"
#include "request.h"
#include "fsutil.h"
#include "filesystem.h"
#include "vm_basic_types.h"
#include "vmhgfs_version.h"
#include "vmware.h"
#include "str.h"
#include "codeset.h"
#include "rpcout.h"
#include "hgfsProto.h"
#include <errno.h>
#include <linux/list.h>
#include <stdio.h>
#include <fuse.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/time.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif
#include <sys/stat.h>
#ifndef __SOLARIS__
#include <sys/types.h>
#endif

#include "transport.h"
#include "session.h"
#include "config.h"

#if defined(__SOLARIS__) || defined(__APPLE__)
#define DT_UNKNOWN      0
#define DT_FIFO         1
#define DT_CHR          2
#define DT_DIR          4
#define DT_BLK          6
#define DT_REG          8
#define DT_LNK          10
#define DT_SOCK         12
#define DT_WHT          14
#define NAME_MAX        255    /* # chars in a file name */
#endif

#include "hgfsEscape.h"

#ifdef VMX86_DEVEL
extern int LOGLEVEL_THRESHOLD;

#define LGPFX        "vmhgfs-fuse"
#define LGPFX_FMT    "%s:%s:"

#define LOG(level, args)                                   \
   do {                                                    \
      if (level <= LOGLEVEL_THRESHOLD) {                   \
         Log(LGPFX_FMT, LGPFX, __FUNCTION__);              \
         Log args;                                         \
      }                                                    \
   } while (0)

#else
#define LOG(level, args)
#endif

/* Blocksize to be set in superblock. (XXX how is this used?) */
#define HGFS_BLOCKSIZE 1024

#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)
/*
 * Global synchronization primitives.
 */

/* Other global state. */
extern HgfsOp hgfsVersionCreateSession;
extern HgfsOp hgfsVersionDestroySession;
extern HgfsOp hgfsVersionOpen;
extern HgfsOp hgfsVersionRead;
extern HgfsOp hgfsVersionWrite;
extern HgfsOp hgfsVersionClose;
extern HgfsOp hgfsVersionSearchOpen;
extern HgfsOp hgfsVersionSearchRead;
extern HgfsOp hgfsVersionSearchClose;
extern HgfsOp hgfsVersionGetattr;
extern HgfsOp hgfsVersionSetattr;
extern HgfsOp hgfsVersionCreateDir;
extern HgfsOp hgfsVersionDeleteFile;
extern HgfsOp hgfsVersionDeleteDir;
extern HgfsOp hgfsVersionRename;
extern HgfsOp hgfsVersionQueryVolumeInfo;
extern HgfsOp hgfsVersionCreateSymlink;

extern HgfsFuseState *gState;

#endif // _VMHGFS_FUSE_MODULE_H_
0707010000049A000081A40000000000000000000000016822550500002CA0000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/request.c  /*********************************************************
 * Copyright (C) 2013,2019, 2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * request.c --
 *
 * Functions dealing with the creation, deletion, and sending of HGFS
 * requests are defined here.
 */

#include "linux/list.h"
#include "module.h"
#include "request.h"
#include "transport.h"
#include "fsutil.h"
#include "vm_assert.h"

static HgfsHandle hgfsIdCounter;
pthread_mutex_t hgfsIdLock = PTHREAD_MUTEX_INITIALIZER;


/*
 *----------------------------------------------------------------------
 *
 * 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 = (HgfsReq*) malloc(sizeof(HgfsReq));
   if (req == NULL) {
      LOG(4, ("Can't allocate memory.\n"));
      return NULL;
   }
   INIT_LIST_HEAD(&req->list);
   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);
   pthread_mutex_lock(&hgfsIdLock);
   req->id = hgfsIdCounter;
   hgfsIdCounter++;
   pthread_mutex_unlock(&hgfsIdLock);

   return req;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsPackHeader --
 *
 *    Fill the header fields for the request. If session is enabled,
 *    use new header.
 *
 * Results:
 *    HGFS_STATUS_SUCCESS for a valid header
 *    else HGFS_STATUS_PROTOCOL_ERROR
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

HgfsStatus
HgfsPackHeader(HgfsReq *req,  // IN/OUT:
               HgfsOp opUsed) // IN
{
   if (gState->sessionEnabled) { /* use new header */
      HgfsHeader *header = (HgfsHeader*)HGFS_REQ_PAYLOAD(req);

      LOG(4, ("sessionEnabled, use HgfsHeader. opUsed = %d\n", opUsed));
      header->version = gState->headerVersion;
      header->dummy = HGFS_OP_NEW_HEADER;
      header->headerSize = sizeof *header;
      header->packetSize = req->payloadSize;
      header->requestId = req->id;
      header->op = opUsed;
      header->sessionId = gState->sessionId;
      header->flags = HGFS_PACKET_FLAG_REQUEST;
      /*
       * Currently unused fields which can be used in version 2 and later.
       * Version 1 didn't zero these fields, hence the server cannot determine
       * their validity.
       */
      header->status = 0;
      header->information = 0;
      header->reserved = 0;
      memset(&header->reserved1[0], 0, sizeof header->reserved1);

   } else {
      HgfsRequest *header = (HgfsRequest*)HGFS_REQ_PAYLOAD(req);

      LOG(4, ("not sessionEnabled, use HgfsRequest. opUsed = %d\n", opUsed));
      header->id = req->id;
      header->op = opUsed;
   }

   return HGFS_STATUS_SUCCESS;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsUnpackHeader --
 *
 *    Validate that server reply contains valid HgfsHeader that matches
 *    current session.
 *    Extract various useful fields from the protocol header of the reply.
 *
 * Results:
 *    HGFS_STATUS_SUCCESS for a valid header
 *    else HGFS_STATUS_PROTOCOL_ERROR
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

HgfsStatus
HgfsUnpackHeader(void *serverReply,           // IN:  server reply
                 size_t replySize,            // IN:  reply data size
                 uint8 *headerVersion,        // OUT: version
                 uint64 *sessionId,           // OUT: unique session id
                 uint32 *requestId,           // OUT: unique request id
                 uint32 *headerFlags,         // OUT: header flags
                 uint32 *information,         // OUT: generic information
                 HgfsOp *opcode,              // OUT: request opcode
                 HgfsStatus *replyStatus,     // OUT: reply status
                 void **payload,              // OUT: pointer to the payload
                 size_t *payloadSize)         // OUT: size of the payload
{
   HgfsHeader *header = (HgfsHeader *)serverReply;

   /* First some confidence checking. */
   if ((replySize < sizeof (HgfsHeader)) ||
       (header->dummy != HGFS_OP_NEW_HEADER) ||
       (header->packetSize > replySize) ||
       (header->headerSize > header->packetSize)) {
      return HGFS_STATUS_PROTOCOL_ERROR;
   }

   *headerVersion = header->version;
   *sessionId = header->sessionId;
   *requestId = header->requestId;
   *opcode = header->op;
   *headerFlags = header->flags;
   *information = header->information;
   *payloadSize = header->packetSize - header->headerSize;
   if (*payloadSize) {
      *payload = (char *)serverReply + header->headerSize;
   } else {
      *payload = NULL;
   }

   *replyStatus = header->status;

   return HGFS_STATUS_SUCCESS;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsGetRequestPayload --
 *
 * Results:
 *    Returns a pointer to the start of the payload data.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

INLINE void*
HgfsGetRequestPayload(HgfsReq *req)   // IN:
{
   if (gState->sessionEnabled) {
      return (void*) HGFS_REQ_GET_PAYLOAD_HDRV2(req);
   } else {
      return (void*) HGFS_REQ_PAYLOAD_V3(req);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsGetReplyPayload --
 *
 * Results:
 *    Returns a pointer to the start of the payload data.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

INLINE void*
HgfsGetReplyPayload(HgfsReq *rep)   // IN:
{
   if (gState->sessionEnabled) {
      return (void*) HGFS_REP_GET_PAYLOAD_HDRV2(rep);
   } else {
      return (void*) HGFS_REP_PAYLOAD_V3(rep);
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsGetRequestHeaderSize --
 *
 * Results:
 *    The size of request message header.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

INLINE size_t
HgfsGetRequestHeaderSize(void)
{
   return (gState->sessionEnabled ?
           sizeof(HgfsHeader) :
           sizeof(HgfsRequest));
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsGetReplyHeaderSize --
 *
 * Results:
 *    The size of reply message header.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

INLINE size_t
HgfsGetReplyHeaderSize(void)
{
   return (gState->sessionEnabled ?
           sizeof(HgfsHeader) :
           sizeof(HgfsReply));
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsSendRequest --
 *
 *    Send out an HGFS request via transport layer, and wait for the reply.
 *
 * Results:
 *    Returns zero on success, negative number on error.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

int
HgfsSendRequest(HgfsReq *req)       // IN/OUT: Outgoing request
{
   int ret;

   ASSERT(req);
   ASSERT(req->payloadSize <= HgfsLargePacketMax(FALSE));

   req->state = HGFS_REQ_STATE_UNSENT;

   LOG(8, ("Sending request id %d\n", req->id));
   LOG(4, ("Before sending \n"));

   ret = HgfsTransportSendRequest(req);
   LOG(4, ("After sending \n"));

   LOG(8, ("Request finished, return %d\n", ret));
   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsFreeRequest --
 *
 *    Free an HGFS request.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
HgfsFreeRequest(HgfsReq *req) // IN: Request to free
{
   free(req);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsReplyStatus --
 *
 *    Return reply status.
 *
 * Results:
 *    Returns reply status as per the protocol.
 *
 * Side effects:
 *    reestablish session when encounter session stale failure.
 *
 *----------------------------------------------------------------------
 */

HgfsStatus
HgfsGetReplyStatus(HgfsReq *req)  // IN
{
   HgfsStatus status;

   if (req->payloadSize < sizeof (HgfsReply)) {
      LOG(4, ("Malformed packet received.\n"));
      status = HGFS_STATUS_PROTOCOL_ERROR;
      goto out;
   }

   if (gState->sessionEnabled &&
      req->payloadSize < sizeof (HgfsHeader)) {
      /*
       * We have enabled an HGFS protocol session which uses the new header
       * format. And an HGFS protocol session uses the new header format only.
       * A reply without the new header indicates a message with the
       * old reply header format.
       */
      gState->sessionEnabled = FALSE;
   }

   if (gState->sessionEnabled) {
      HgfsHeader *header = (HgfsHeader *)(HGFS_REQ_PAYLOAD(req));
      status = header->status;

      if (status == HGFS_STATUS_STALE_SESSION) {
         LOG(4, ("Session stale! Try to recreate session ...\n"));
         HgfsCreateSession();
         /*
          * XXX: User might want to retry it later, and status will not be
          * changed here. But due to the fail safe directory access like
          * searching for dynamic library path, user hardly ever notice
          * the failure.
          */
      }

   } else {
      HgfsReply *reply = (HgfsReply *)(HGFS_REQ_PAYLOAD(req));
      status = reply->status;
   }

out:
   LOG(4, ("Exit(status = %d)\n", status));
   return status;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsCompleteReq --
 *
 *    Copies the reply packet into the request structure and wakes up
 *    the associated client.
 *
 * Results:
 *    None
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------
 */

void
HgfsCompleteReq(HgfsReq *req,       // IN: Request
                char const *reply,  // IN: Reply packet
                size_t replySize)   // IN: Size of reply packet
{
   ASSERT(req);
   ASSERT(reply);
   ASSERT(replySize <= HgfsLargePacketMax(FALSE));

   memcpy(HGFS_REQ_PAYLOAD(req), reply, replySize);
   req->payloadSize = replySize;
   req->state = HGFS_REQ_STATE_COMPLETED;
   if (!list_empty(&req->list)) {
      list_del_init(&req->list);
   }
}
0707010000049B000081A40000000000000000000000016822550500001207000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/request.h  /*********************************************************
 * Copyright (C) 2013 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * request.h --
 *
 * Functions dealing with the creation, deletion, and sending of HGFS
 * requests are defined here.
 */

#ifndef _VMHGFS_FUSE_REQUEST_H_
#define _VMHGFS_FUSE_REQUEST_H_

/* Must come before any kernel header file. */
//#include "driver-config.h"

#include <linux/list.h>
//#include "compat_sched.h"
//#include "compat_spinlock.h"
//#include "compat_wait.h"

#include "hgfs.h" /* For common HGFS definitions. */
#include "hgfsProto.h"
#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)

#define HGFS_REQ_PAYLOAD_V3(hgfsReq) (HGFS_REQ_PAYLOAD(hgfsReq) + sizeof(HgfsRequest))
#define HGFS_REP_PAYLOAD_V3(hgfsRep) (HGFS_REQ_PAYLOAD(hgfsRep) + sizeof(HgfsReply))

#define HGFS_REQ_GET_PAYLOAD_HDRV2(hgfsReq) (HGFS_REQ_PAYLOAD(hgfsReq) + sizeof(HgfsHeader))
#define HGFS_REP_GET_PAYLOAD_HDRV2(hgfsRep) (HGFS_REQ_PAYLOAD(hgfsRep) + sizeof(HgfsHeader))


/*
 * 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
 *    request handler's responsibility to submit this request to
 *    the channel. Requests in this state are on the global unsent list.
 *
 * HGFS_REQ_STATE_SUBMITTED:
 *    The packet has been sent, but the reply will arrive asynchronously.
 *    The request will be on the hgfsRepPending list, and whenever
 *    the reply arrives, the reply handler will remove the request from
 *    the hgfsRepPending list and stuff the reply into the request's
 *    packet buffer.
 *
 *    This is only for asynchronous channel communication.
 *
 * HGFS_REQ_STATE_COMPLETED:
 *    The request 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_SUBMITTED,
   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 */
   HgfsHandle 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_LARGE_PACKET_MAX + HGFS_CLIENT_CMD_LEN];
} HgfsReq;

/* Public functions (with respect to the entire module). */
HgfsReq *HgfsGetNewRequest(void);
HgfsStatus HgfsPackHeader(HgfsReq *req, HgfsOp opUsed);
HgfsStatus HgfsUnpackHeader(void *serverReply,
			    size_t replySize,
			    uint8 *headerVersion,
			    uint64 *sessionId,
			    uint32 *requestId,
			    uint32 *headerFlags,
			    uint32 *information,
			    HgfsOp *opcode,
			    HgfsStatus *replyStatus,
			    void **payload,
			    size_t *payloadSize);
void* HgfsGetRequestPayload(HgfsReq *req);
void* HgfsGetReplyPayload(HgfsReq *req);
size_t HgfsGetReplyHeaderSize(void);
size_t HgfsGetRequestHeaderSize(void);
int HgfsSendRequest(HgfsReq *req);
void HgfsFreeRequest(HgfsReq *req);
HgfsStatus HgfsGetReplyStatus(HgfsReq *req);
void HgfsCompleteReq(HgfsReq *req,
                     char const *reply,
                     size_t replySize);

#endif // _VMHGFS_FUSE_REQUEST_H_
 0707010000049C000081A40000000000000000000000016822550500002ADA000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/session.c  /*********************************************************
 * Copyright (C) 2015-2016,2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * session.c --
 *
 * Hgfs session related operations.
 */

#include "module.h"


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackCreateSessionRequestV4 --
 *
 *    Packs create session request to be sent to the server.
 *    This is the first request that is sent after a connection
 *    to a server had been established.
 *
 * Results:
 *    Returns zero on success, or negative error on failure.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsPackCreateSessionRequest(HgfsOp opUsed, // IN: Op to be used
                             HgfsReq *req)  // IN/OUT: Packet to write into
{
   switch (opUsed) {
   case HGFS_OP_CREATE_SESSION_V4: {
      HgfsRequestCreateSessionV4 *requestV4 = HgfsGetRequestPayload(req);

      requestV4->numCapabilities = 0;
      requestV4->maxPacketSize = HgfsLargePacketMax(FALSE);
      requestV4->reserved = 0;

      req->payloadSize = sizeof(*requestV4) + HgfsGetRequestHeaderSize();

      break;
   }
   default:
      LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   /* Fill in header here as payloadSize needs to be there. */
   HgfsPackHeader(req, opUsed);

   return 0;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsCreateSessionProcessResult --
 *
 *    Process create session server reply.
 *
 * Results:
 *    HGFS_STATUS_SUCCESS if everything is right,
 *    else HGFS_STATUS_PROTOCOL_ERROR
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

static HgfsStatus
HgfsCreateSessionProcessResult(const char *result,  // IN: Reply packet
                               size_t resultSize)   // IN: packet size
{
   HgfsStatus status = HGFS_STATUS_SUCCESS;
   uint64 sessionId = HGFS_INVALID_SESSION_ID;
   uint8 headerVersion = HGFS_HEADER_VERSION_1;
   Bool sessionIdPresent = FALSE;
   uint32 maxPacketSize = HgfsLargePacketMax(TRUE);

   uint32 information;
   HgfsHandle requestId;
   HgfsStatus tmpStatus;
   uint32 headerFlags = 0;
   HgfsOp op;
   void *replyPayload;
   size_t replyPayloadSize;
   HgfsReplyCreateSessionV4 *createSessionReply;

   tmpStatus = HgfsUnpackHeader((void *) result,
				resultSize,
				&headerVersion,
				&sessionId,
				&requestId,
				&headerFlags,
				&information,
				&op,
				&status,
				&replyPayload,
				&replyPayloadSize);
   /* Older servers set the header flags to zero. */
   if (0 != headerFlags) {
      if (0 == (headerFlags & HGFS_PACKET_FLAG_REPLY)) {
	 /* The server request flag must be set for these packets. */
	 status = HGFS_STATUS_PROTOCOL_ERROR;
	 goto out;
      }
   }
   if (tmpStatus != HGFS_STATUS_SUCCESS) {
      LOG(4, ("Malformed packet received. status=%d\n", tmpStatus));
      status = HGFS_STATUS_PROTOCOL_ERROR;
      goto out;
   }

   if (status == HGFS_STATUS_SUCCESS) {
      createSessionReply = (HgfsReplyCreateSessionV4 *) replyPayload;
      ASSERT(createSessionReply);

      LOG(4, ("Successfully created the session.\n"));

      /*
       * XXX: HgfsServer returns other properties like capabilities etc.
       * in the reply. In the future, we should extend the
       * structure to hold these properties returned from the HgfsServer
       * for CreateSession request.
       */
      sessionId = createSessionReply->sessionId;
      sessionIdPresent = TRUE;
      maxPacketSize = createSessionReply->maxPacketSize;
   }

out:
   gState->sessionId = sessionId;
   gState->headerVersion = headerVersion;
   gState->sessionEnabled = sessionIdPresent;
   gState->maxPacketSize = maxPacketSize;

   LOG(4, ("Exit(%d)\n", status));
   return status;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsCreateSession --
 *
 *    Sends a Create session request to the HGFS Server.
 *
 * Results:
 *    Returns zero on success, or negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

int
HgfsCreateSession(void)
{
   HgfsReq *req;
   int result;
   HgfsStatus status;
   HgfsOp opUsed;

   LOG(4, ("Entry()\n"));
   gState->sessionEnabled = TRUE;
   gState->headerVersion = HGFS_HEADER_VERSION;

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request.\n"));
      result = -ENOMEM;
      goto out;
   }

   opUsed = hgfsVersionCreateSession;
   result = HgfsPackCreateSessionRequest(opUsed, req);
   if (result != 0) {
      LOG(4, ("Error packing request.\n"));
      goto out;
   }

   result = HgfsSendRequest(req);
   if (result == 0) {
      LOG(6, ("Got reply.\n"));
      status = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(status);
      switch (result) {
      case 0:
         status = HgfsCreateSessionProcessResult(HGFS_REQ_PAYLOAD(req),
                                                 req->payloadSize);
         ASSERT(status == HGFS_STATUS_SUCCESS);
         break;
      case -EPROTO:
         /* Fallthrough. */
      default:
         LOG(6, ("Session was not created, error %d\n", result));
         gState->sessionEnabled = FALSE;
         break;
      }

   } else {
      gState->sessionEnabled = FALSE;

      if (result == -EIO) {
         LOG(4, ("Timed out. error: %d\n", result));
      } else if (result == -EPROTO) {
         LOG(4, ("Server returned error: %d\n", result));
      } else {
         LOG(4, ("Unknown error: %d\n", result));
      }
   }

out:
   HgfsFreeRequest(req);
   LOG(4, ("Exit(%d)\n", result));
   return result;
}


/*
 *-----------------------------------------------------------------------------
 *
 * HgfsPackDestroySessionRequestV4 --
 *
 *    Packs destroy session request to be sent to the server.
 *
 * Results:
 *    Returns zero on success, or negative error on failure.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

static int
HgfsPackDestroySessionRequest(HgfsOp opUsed, // IN: Op to be used
                             HgfsReq *req)   // IN/OUT: Packet to write into
{
   switch (opUsed) {
   case HGFS_OP_DESTROY_SESSION_V4: {
      HgfsRequestDestroySessionV4 *requestV4 =
         (HgfsRequestDestroySessionV4*)HgfsGetRequestPayload(req);

      requestV4->reserved = 0;

      req->payloadSize = sizeof(HgfsHeader) + sizeof(*requestV4);
      /* Fill in header here as payloadSize needs to be there. */
      HgfsPackHeader(req, opUsed);

      break;
   }
   default:
      LOG(4, ("Unexpected OP type encountered. opUsed = %d\n", opUsed));
      return -EPROTO;
   }

   return 0;
}



/*
 *----------------------------------------------------------------------------
 *
 * HgfsDestroySessionProcessResult --
 *
 *    Process destroy session server reply.
 *
 * Results:
 *    HGFS_STATUS_SUCCESS if everything is right,
 *    else HGFS_STATUS_PROTOCOL_ERROR
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

static HgfsStatus
HgfsDestroySessionProcessResult(const char *result,  // IN: Reply packet
                                size_t resultSize)   // IN: packet size
{
   uint8 headerVersion = HGFS_HEADER_VERSION_1;
   uint64 sessionId;
   HgfsHandle requestId;
   uint32 headerFlags = 0;
   uint32 information;
   HgfsOp op;
   size_t payloadSize;
   void *replyPayload;
   HgfsStatus status = HGFS_STATUS_SUCCESS;
   HgfsStatus tmpStatus;

   tmpStatus = HgfsUnpackHeader((void *) result,
				resultSize,
				&headerVersion,
				&sessionId,
				&requestId,
				&headerFlags,
				&information,
				&op,
				&status,
				&replyPayload,
				&payloadSize);
   if (tmpStatus != HGFS_STATUS_SUCCESS) {
      LOG(4, ("%s: Malformed packet received.\n", __FUNCTION__));
      status = HGFS_STATUS_PROTOCOL_ERROR;
      goto out;
   }

   if(status == HGFS_STATUS_SUCCESS) {
      LOG(4, ("Successfully destroyed the session.\n"));
   }

out:
   gState->sessionId = HGFS_INVALID_SESSION_ID;
   gState->sessionEnabled = FALSE;
   gState->maxPacketSize = HgfsLargePacketMax(TRUE);

   LOG(4, ("Exit(%d)\n", status));
   return status;
}


/*
 *----------------------------------------------------------------------------
 *
 * HgfsDestroySession --
 *
 *    Sends a Create session request to the HGFS Server in the guest.
 *
 * Results:
 *    Returns zero on success, or negative error on failure.
 *
 * Side effects:
 *    None
 *
 *----------------------------------------------------------------------------
 */

int
HgfsDestroySession(void)
{
   HgfsReq *req;
   int result;
   HgfsStatus status;
   HgfsOp opUsed;

   LOG(4, ("Entry()\n"));
   if (!gState->sessionEnabled) {
     return 0;
   }

   req = HgfsGetNewRequest();
   if (!req) {
      LOG(4, ("Out of memory while getting new request.\n"));
      result = -ENOMEM;
      goto out;
   }

   opUsed = hgfsVersionDestroySession;
   result = HgfsPackDestroySessionRequest(opUsed, req);
   if (result != 0) {
      LOG(4, ("Error packing request.\n"));
      goto out;
   }

   result = HgfsSendRequest(req);
   if (result == 0) {
      LOG(6, ("Got reply.\n"));
      status = HgfsGetReplyStatus(req);
      result = HgfsStatusConvertToLinux(status);
      switch (result) {
      case 0:
         status = HgfsDestroySessionProcessResult(HGFS_REQ_PAYLOAD(req),
                                                  req->payloadSize);
         ASSERT(status == HGFS_STATUS_SUCCESS);
         break;
      case -EPROTO:
         /* Fallthrough. */
      default:
         LOG(6, ("Session was not created, error %d\n", result));
         break;
      }
   } else if (result == -EIO) {
      LOG(4, ("Timed out. error: %d\n", result));
   } else if (result == -EPROTO) {
      LOG(4, ("Server returned error: %d\n", result));
   } else {
      LOG(4, ("Unknown error: %d\n", result));
   }

out:
   HgfsFreeRequest(req);
   LOG(4, ("Exit(%d)\n", result));
   return result;
}

  0707010000049D000081A4000000000000000000000001682255050000040F000000000000000000000000000000000000003900000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/session.h  /*********************************************************
 * Copyright (C) 2015-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * session.h --
 *
 */

#ifndef _VMHGFS_FUSE_SESSION_H_
#define _VMHGFS_FUSE_SESSION_H_

int HgfsCreateSession(void);
int HgfsDestroySession(void);

#endif
 0707010000049E000081A400000000000000000000000168225505000028D0000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/transport.c    /*********************************************************
 * Copyright (c) 2013-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * transport.c --
 *
 * This file handles the transport mechanisms available for HGFS.
 * This acts as a glue between the HGFS filesystem driver and the
 * actual transport channels (backdoor, tcp, vsock, ...).
 *
 * The sends happen in the process context, where as a thread
 * handles the asynchronous replies. A queue of pending replies is
 * maintained and is protected by a lock. The channel opens and close
 * is protected by a mutex.
 */



#include "bdhandler.h"
#include "hgfsProto.h"
#include "module.h"
#include "request.h"
#include "transport.h"
#include "vm_assert.h"

static HgfsTransportChannel *gHgfsActiveChannel;     /* Current active channel. */
static pthread_mutex_t gHgfsActiveChannelLock;       /* Current active channel lock. */
static Bool gHgfsActiveChannelLockInited;

static struct list_head gHgfsPendingRequests;        /* Pending requests queue. */
static pthread_mutex_t gHgfsPendingRequestsLock;     /* Pending requests queue lock. */
static Bool gHgfsPendingRequestsLockInited;


#define HgfsRequestId(req) ((HgfsRequest *)req)->id

static void HgfsTransportChannelClose(HgfsTransportChannel **channel);

/*
 * Private function implementations.
 */

/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportChannelOpen --
 *
 *     Open a new workable channel.
 *
 * Results:
 *     0 on success and the new channel, otherwise -ENOTCONN and NULL.
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

static int
HgfsTransportChannelOpen(HgfsTransportChannel **channel) // IN: active channel
{
   int result = 0;

   *channel = HgfsBdChannelInit();
   if (NULL != *channel) {
      HgfsChannelStatus status = (*channel)->ops.open(*channel);
      if (status != HGFS_CHANNEL_CONNECTED) {
         HgfsTransportChannelClose(channel);
         result = -ENOTCONN;
         *channel = NULL;
      }
   }

   return result;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportChannelClose --
 *
 *     Teardown current channel and stop current receive thread.
 *
 * Results:
 *     None
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

static void
HgfsTransportChannelClose(HgfsTransportChannel **channel) // active channel
{
   if (NULL != *channel) {
      HgfsTransportChannel *closeChannel = *channel;

      closeChannel->ops.close(closeChannel);
      closeChannel->ops.exit(closeChannel);
      *channel = NULL;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportChannelReset --
 *
 *     Called when current channel doesn't work. Find a new channel
 *     for transport.
 *
 * Results:
 *     TRUE on success, otherwise FALSE;
 *
 * Side effects:
 *     Teardown current opened channel and the receive thread, set up
 *     new channel and new receive thread.
 *
 *----------------------------------------------------------------------
 */

static Bool
HgfsTransportChannelReset(HgfsTransportChannel **channel) // IN: active channel
{
   Bool ret = FALSE;
   int openResult;

   HgfsTransportChannelClose(channel);
   openResult = HgfsTransportChannelOpen(channel);
   if (openResult == 0) {
      ret = TRUE;
   }
   LOG(8, ("Result: %d: %s.\n",openResult, ret ? "TRUE" : "FALSE"));
   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportEnqueueRequest --
 *
 *     Add the request to the gHgfsPendingRequests queue.
 *
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

static void
HgfsTransportEnqueueRequest(HgfsReq *req)   // IN: Request to add
{
   ASSERT(req);

   pthread_mutex_lock(&gHgfsPendingRequestsLock);
   list_add_tail(&req->list, &gHgfsPendingRequests);
   pthread_mutex_unlock(&gHgfsPendingRequestsLock);
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportDequeueRequest --
 *
 *     Removes the request from the gHgfsPendingRequests queue.
 *
 * Results:
 *     None
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

static void
HgfsTransportDequeueRequest(HgfsReq *req)   // IN: Request to dequeue
{
   ASSERT(req);

   pthread_mutex_lock(&gHgfsPendingRequestsLock);
   if (!list_empty(&req->list)) {
      list_del_init(&req->list);
   }
   pthread_mutex_unlock(&gHgfsPendingRequestsLock);
}


/*
 * Public function implementations.
 */

/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportProcessPacket --
 *
 *     Helper function to process received packets, called by the channel
 *     handler thread.
 *
 * Results:
 *     None
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

void
HgfsTransportProcessPacket(char *receivedPacket,    //IN: received packet
                           size_t receivedSize)     //IN: packet size
{
   struct list_head *cur, *next;
   HgfsHandle id;
   Bool found = FALSE;

   /* Got the reply. */

   ASSERT(receivedPacket != NULL && receivedSize > 0);
   id = HgfsRequestId(receivedPacket);
   LOG(8, ("Entered.\n"));
   LOG(6, ("Req id: %d\n", id));
   /*
    * Search through gHgfsPendingRequests queue for the matching id and wake up
    * the associated waiting process. Delete the req from the queue.
    */
   pthread_mutex_lock(&gHgfsPendingRequestsLock);
   list_for_each_safe(cur, next, &gHgfsPendingRequests) {
      HgfsReq *req;
      req = list_entry(cur, HgfsReq, list);
      if (req->id == id) {
         ASSERT(req->state == HGFS_REQ_STATE_SUBMITTED);
         HgfsCompleteReq(req, receivedPacket, receivedSize);
         found = TRUE;
         break;
      }
   }
   pthread_mutex_unlock(&gHgfsPendingRequestsLock);

   if (!found) {
      LOG(4, ("No matching id, dropping reply.\n"));
   }
   LOG(8, ("Exited.\n"));
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportSendRequest --
 *
 *     Sends the request via channel communication.
 *
 * Results:
 *     Zero on success, non-zero error on failure.
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

int
HgfsTransportSendRequest(HgfsReq *req)   // IN: Request to send
{
   int ret;
   ASSERT(req);
   ASSERT(req->state == HGFS_REQ_STATE_UNSENT);
   ASSERT(req->payloadSize <= HgfsLargePacketMax(FALSE));

   pthread_mutex_lock(&gHgfsActiveChannelLock);

   /* Try opening the channel. */
   if (NULL == gHgfsActiveChannel) {
      ret = HgfsTransportChannelOpen(&gHgfsActiveChannel);
      if (ret != 0) {
         goto exit;
      }
   }

   ASSERT(gHgfsActiveChannel->ops.send);

   HgfsTransportEnqueueRequest(req);

   ret = gHgfsActiveChannel->ops.send(gHgfsActiveChannel, req);
   if (ret < 0) {
      LOG(4, ("Send failed, status = %d. Try reopening the channel ...\n",
              ret));
      if (HgfsTransportChannelReset(&gHgfsActiveChannel)) {
         ret = gHgfsActiveChannel->ops.send(gHgfsActiveChannel, req);
      }
   }

exit:
   ASSERT(req->state == HGFS_REQ_STATE_COMPLETED ||
          req->state == HGFS_REQ_STATE_SUBMITTED ||
          req->state == HGFS_REQ_STATE_UNSENT);

   pthread_mutex_unlock(&gHgfsActiveChannelLock);

   if (ret < 0) {
      HgfsTransportDequeueRequest(req);
   }

   return ret;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportInit --
 *
 *     Initialize the transport.
 *
 *     Starts the reply thread, for handling incoming packets on the
 *     connected socket.
 *
 * Results:
 *     Zero on success and negative error on failure.
 *
 * Side effects:
 *     None
 *
 *----------------------------------------------------------------------
 */

int
HgfsTransportInit(void)
{
   int res;

   gHgfsActiveChannel = NULL;
   gHgfsPendingRequestsLockInited = FALSE;
   gHgfsActiveChannelLockInited = FALSE;
   INIT_LIST_HEAD(&gHgfsPendingRequests);

   res = pthread_mutex_init(&gHgfsPendingRequestsLock, NULL);
   if (res != 0) {
      res = -res;
      goto exit;
   }
   gHgfsPendingRequestsLockInited = TRUE;

   res = pthread_mutex_init(&gHgfsActiveChannelLock, NULL);
   if (res != 0) {
      res = -res;
      goto exit;
   }
   gHgfsActiveChannelLockInited = TRUE;

   res = HgfsTransportChannelOpen(&gHgfsActiveChannel);

exit:
   if (res != 0) {
      HgfsTransportExit();
   }
   return res;
}


/*
 *----------------------------------------------------------------------
 *
 * HgfsTransportExit --
 *
 *     Teardown the transport.
 *
 * Results:
 *     None
 *
 * Side effects:
 *     Cleans up everything, frees queues, closes channel.
 *
 *----------------------------------------------------------------------
 */

void
HgfsTransportExit(void)
{
   LOG(8, ("Entered.\n"));

   if (gHgfsActiveChannelLockInited) {
      pthread_mutex_lock(&gHgfsActiveChannelLock);
      HgfsTransportChannelClose(&gHgfsActiveChannel);
      pthread_mutex_unlock(&gHgfsActiveChannelLock);

      pthread_mutex_destroy(&gHgfsActiveChannelLock);
      gHgfsActiveChannelLockInited = FALSE;
   }

   ASSERT(list_empty(&gHgfsPendingRequests));

   if (gHgfsPendingRequestsLockInited) {
      pthread_mutex_destroy(&gHgfsPendingRequestsLock);
      gHgfsPendingRequestsLockInited = FALSE;
   }
   LOG(8, ("Exited.\n"));
}
0707010000049F000081A40000000000000000000000016822550500000912000000000000000000000000000000000000003B00000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/transport.h    /*********************************************************
 * Copyright (c) 2013-2024 Broadcom. All rights reserved.
 * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * transport.h --
 */

#ifndef _HGFS_DRIVER_TRANSPORT_H_
#define _HGFS_DRIVER_TRANSPORT_H_

#include "request.h"
#include <pthread.h>

typedef enum {
   HGFS_CHANNEL_UNINITIALIZED,
   HGFS_CHANNEL_NOTCONNECTED,
   HGFS_CHANNEL_CONNECTED,
} HgfsChannelStatus;

/*
 * There are the operations a channel should implement.
 */
struct HgfsTransportChannel;
typedef struct HgfsTransportChannelOps {
   HgfsChannelStatus (*open)(struct HgfsTransportChannel *);
   void (*close)(struct HgfsTransportChannel *);
   int (*send)(struct HgfsTransportChannel *, HgfsReq *);
   int (*recv)(struct HgfsTransportChannel *, char **, size_t *);
   void (*exit)(struct HgfsTransportChannel *);
} HgfsTransportChannelOps;

typedef struct HgfsTransportChannel {
   const char *name;               /* Channel name. */
   HgfsTransportChannelOps ops;    /* Channel ops. */
   HgfsChannelStatus status;       /* Connection status. */
   void *priv;                     /* Channel private data. */
   pthread_mutex_t connLock;       /* Protect _this_ struct. */
} HgfsTransportChannel;

/* Public functions (with respect to the entire module). */
int HgfsTransportInit(void);
void HgfsTransportExit(void);
int HgfsTransportSendRequest(HgfsReq *req);
void HgfsTransportProcessPacket(char *receivedPacket,
                                size_t receivedSize);

#endif // _HGFS_DRIVER_TRANSPORT_H_
  070701000004A0000081A400000000000000000000000168225505000004B5000000000000000000000000000000000000004000000000open-vm-tools-12.5.2/open-vm-tools/vmhgfs-fuse/vmhgfs_version.h   /*********************************************************
 * Copyright (C) 2013,2018-2019 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * vmhgfs_version.h --
 *
 * Version definitions for the Linux vmhgfs driver.
 */

#ifndef _VMHGFS_VERSION_H_
#define _VMHGFS_VERSION_H_

#define VMHGFS_DRIVER_VERSION          1.6.12.0
#define VMHGFS_DRIVER_VERSION_COMMAS   1,6,12,0
#define VMHGFS_DRIVER_VERSION_STRING   "1.6.12.0"

#endif /* _VMHGFS_VERSION_H_ */
   070701000004A1000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000003C00000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper   070701000004A2000081A40000000000000000000000016822550500006739000000000000000000000000000000000000004400000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000004A3000081A400000000000000000000000168225505000006F8000000000000000000000000000000000000004800000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/Makefile.am   ################################################################################
### Copyright (C) 2007-2017 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-user-suid-wrapper

AM_CPPFLAGS =
AM_CPPFLAGS += -DVMTOOLSD_PATH=\"$(bindir)/vmtoolsd\"

vmware_user_suid_wrapper_SOURCES =
vmware_user_suid_wrapper_SOURCES += main.c
vmware_user_suid_wrapper_SOURCES += wrapper-@TARGET_OS@.c

vmware_user_suid_wrapper_LDADD =
vmware_user_suid_wrapper_LDADD += ../lib/vmSignal/libVmSignal.la

desktopfile = vmware-user.desktop
desktopfilesrc = $(top_srcdir)/vmware-user-suid-wrapper/vmware-user.desktop.in

autostartdir = $(sysconfdir)/xdg/autostart
autostart_DATA = $(desktopfile)

CLEANFILES = $(desktopfile)

$(desktopfile): $(desktopfilesrc)
	sed 's,_bindir_,$(bindir),' $(desktopfilesrc) > $(desktopfile)

# The desktopEvents plugin will look for 'vmware-user'
# to restart.
install-exec-hook:
	cd $(DESTDIR)$(bindir) && \
	  ln -fs vmware-user-suid-wrapper \
	  vmware-user

uninstall-hook:
	rm -f $(DESTDIR)$(bindir)/vmware-user

070701000004A4000081A40000000000000000000000016822550500002108000000000000000000000000000000000000004300000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/main.c    /*********************************************************
 * Copyright (C) 2007-2018,2023 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/


/*
 * main.c --
 *
 *      This program is run as root to prepare the system for vmware-user.
 *      - It unmounts the vmblock file system, unloads the vmblock module, then
 *        reloads the module, mounts the file system, and opens a file descriptor
 *        that vmware-user can use to add and remove blocks.
 *        This must all happen as root since we cannot allow any random process
 *        to add and remove blocks in the blocking file system.
 *      - It opens the uinput device file and passes the file descriptor to
 *        vmware-user.
 */

#if !defined(sun) && !defined(__FreeBSD__) && !defined(__linux__)
# error This program is not supported on your platform.
#endif

#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#include "vmware.h"
#include "vmblock.h"
#include "vmsignal.h"
#include "wrapper.h"

#include "wrapper_version.h"
#include "vm_version.h"
#include "embed_version.h"
VM_EMBED_VERSION(WRAPPER_VERSION_STRING);


/*
 * Local functions (prototypes)
 */

static void MaskSignals(void);
static Bool StartVMwareUser(char *const envp[]);


/*
 *----------------------------------------------------------------------------
 *
 * main --
 *
 *    On platforms where this wrapper manages the vmblock module:
 *       Unmounts vmblock and unloads the module, then reloads the module,
 *       and remounts the file system, then starts vmware-user as described
 *       below.
 *
 *       This program is the only point at which vmblock is stopped or
 *       started.  This means we must always unload the module to ensure that
 *       we are using the newest installed version (since an upgrade could
 *       have occurred since the last time this program ran).
 *
 *    On all platforms:
 *       Acquires the vmblock control file descriptor, drops privileges, then
 *       starts vmware-user.
 *
 * Results:
 *    EXIT_SUCCESS on success and EXIT_FAILURE on failure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

int
main(int argc,
     char *argv[],
     char *envp[])
{
   MaskSignals();

   if (!StartVMwareUser(envp)) {
      Error("failed to start vmware-user\n");
      exit(EXIT_FAILURE);
   }

   exit(EXIT_SUCCESS);
}


/*
 * Local functions (definitions)
 */


/*
 *-----------------------------------------------------------------------------
 *
 * MaskSignals --
 *
 *      Sets SIG_IGN as the handler for SIGUSR1 and SIGUSR2 which may arrive
 *      prematurely from our services script.  See bug 542135.
 *
 * Results:
 *      Returns if applicable signals are blocked.
 *      Exits with EXIT_FAILURE otherwise.
 *
 * Side effects:
 *      SIG_IGN disposition persists across execve().  These signals will
 *      remain masked until vmware-user defines its own handlers.
 *
 *-----------------------------------------------------------------------------
 */

static void
MaskSignals(void)
{
   int const signals[] = {
      SIGUSR1,
      SIGUSR2
   };
   struct sigaction olds[ARRAYSIZE(signals)];

   if (Signal_SetGroupHandler(signals, olds, ARRAYSIZE(signals),
                              SIG_IGN) == 0) {
      /* Signal_SetGroupHandler will write error message to stderr. */
      exit(EXIT_FAILURE);
   }
}


/*
 *----------------------------------------------------------------------------
 *
 * StartVMwareUser --
 *
 *    Obtains the library directory from the Tools locations database, then
 *    opens a file descriptor (while still root) to add and remove blocks,
 *    and finally starts vmware-user.
 *
 * Results:
 *    Parent: TRUE on success, FALSE on failure.
 *    Child: FALSE on failure, no return on success.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------------
 */

static Bool
StartVMwareUser(char *const envp[])
{
   pid_t pid;
   int blockFd = -1;
   char blockFdStr[8];
   int uinputFd = -1;
   char uinputFdStr[8];
   int ret;
   char path[MAXPATHLEN];
   char *argv[8];
   size_t idx = 0;
   char *xdgSessionType;
   Bool useWayland = FALSE;

   if (!BuildExecPath(path, sizeof path)) {
      return FALSE;
   }

   /*
    * Now create a child process, obtain a file descriptor as root and
    * run vmware-user.
    */
   pid = fork();
   if (pid == -1) {
      Error("fork failed: %s\n", strerror(errno));
      return FALSE;
   } else if (pid != 0) {
      /* Parent */
      return TRUE;
   }

   /* Child */

   xdgSessionType = getenv("XDG_SESSION_TYPE");
   if (   (xdgSessionType != NULL)
       && (strstr(xdgSessionType, "wayland") != NULL)) {
      useWayland = TRUE;
   }

   /*
    * We know the file system is mounted and want to keep this suid
    * root wrapper as small as possible, so here we directly open(2) the
    * "device" instead of calling DnD_InitializeBlocking() and bringing along
    * a whole host of libs.
    */
   blockFd = open(VMBLOCK_FUSE_DEVICE, VMBLOCK_FUSE_DEVICE_MODE);
   if (blockFd < 0) {
      blockFd = open(VMBLOCK_DEVICE, VMBLOCK_DEVICE_MODE);
   }

   if (useWayland) {
      uinputFd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
      if (uinputFd < 0) {
         uinputFd = open("/dev/input/uinput", O_WRONLY | O_NONBLOCK);
      }
   }

   /*
    * Since vmware-user provides features that don't depend on vmblock, we
    * invoke vmware-user even if we couldn't obtain a file descriptor or we
    * can't parse the descriptor to pass as an argument.  We set up the
    * argument vector accordingly.
    */
   argv[idx++] = path;
   argv[idx++] = "-n";
   argv[idx++] = "vmusr";

   if (blockFd < 0) {
      Error("could not open %s\n", VMBLOCK_DEVICE);
   } else {
      ret = snprintf(blockFdStr, sizeof blockFdStr, "%d", blockFd);
      if (ret == 0 || ret >= sizeof blockFdStr) {
         Error("could not parse file descriptor (%d)\n", blockFd);
      } else {
         argv[idx++] = "--blockFd";
         argv[idx++] = blockFdStr;
      }
   }

   if (useWayland) {
      if (uinputFd < 0) {
         Error("could not open uinput device\n");
      } else {
         ret = snprintf(uinputFdStr, sizeof uinputFdStr, "%d", uinputFd);
         if (ret == 0 || ret >= sizeof uinputFdStr) {
            Error("could not parse uinput file descriptor (%d)\n", uinputFd);
         } else {
            argv[idx++] = "--uinputFd";
            argv[idx++] = uinputFdStr;
         }
      }
   }

   argv[idx++] = NULL;
   CompatExec(path, argv, envp);

   /*
    * CompatExec, if successful, doesn't return.  I.e., we're here only
    * if CompatExec fails.
    */
   Error("could not execute %s: %s\n", path, strerror(errno));
   _exit(EXIT_FAILURE);
}


#ifndef USES_LOCATIONS_DB
/*
 *-----------------------------------------------------------------------------
 *
 * BuildExecPath --
 *
 *	Writes the path to vmware-user to execPath.  This version, as opposed
 *	to the versions in $platform/wrapper.c, is only used when the locations
 *	database isn't used.
 *
 * Results:
 *      TRUE on success, FALSE on failure.
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
BuildExecPath(char *execPath,           // OUT: Buffer to store executable's path
              size_t execPathSize)      // IN : size of execPath buffer
{
   if (execPathSize < sizeof VMTOOLSD_PATH) {
      return FALSE;
   }
   strcpy(execPath, VMTOOLSD_PATH);
   return TRUE;
}
#endif // ifndef USES_LOCATIONS_DB
070701000004A5000081A40000000000000000000000016822550500000087000000000000000000000000000000000000005300000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/vmware-user.desktop.in    [Desktop Entry]
Type=Application
Exec=_bindir_/vmware-user-suid-wrapper
Name=VMware User Agent
NoDisplay=true
X-KDE-autostart-phase=1

 070701000004A6000081A40000000000000000000000016822550500000EA0000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/wrapper-freebsd.c /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * wrapper.c --
 *
 *      Platform specific code for the VMware User Agent setuid wrapper.
 */


#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "vmware.h"
#include "wrapper.h"


/*
 * Global functions
 */


#ifdef USES_LOCATIONS_DB
/*
 *-----------------------------------------------------------------------------
 *
 * BuildExecPath --
 *
 *      Determine & return path of vmware-user for use by execve(2).
 *
 * Results:
 *      TRUE on success, FALSE otherwise
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
BuildExecPath(char *execPath,           // OUT: Buffer to store executable's path
              size_t execPathSize)      // IN : size of execPath buffer
{
   char tmpPath[MAXPATHLEN];
   int execLen;

   /*
    * The locations database is the only path that's fixed, and it contains the
    * paths to all the other paths selected during Tools configuration.  The
    * locations database file is only writable by root, so we can trust it.
    */
   if (!QueryLocationsDB(LOCATIONS_PATH, QUERY_BINDIR, tmpPath, sizeof tmpPath)) {
      Error("could not obtain BINDIR\n");
      return FALSE;
   }

   if (strlcat(tmpPath,
               "/vmware-user-wrapper", sizeof tmpPath) >= sizeof tmpPath) {
      Error("could not construct program filename\n");
      return FALSE;
   }

   /*
    * From readlink(2), "The readlink() system call does not append a NUL
    * character to buf."  (NB:  This breaks if user ever replaces the symlink
    * with the target.)
    */
   if ((execLen = readlink(tmpPath, execPath, execPathSize - 1)) == -1) {
      Error("could not resolve symlink: %s\n", strerror(errno));
      return FALSE;
   }

   execPath[execLen] = '\0';

   /*
    * Now make sure that the target is actually part of our "trusted"
    * directory.  (Check that execPath has LIBDIR as a prefix and does
    * not contain "..".)
    */
   if (!QueryLocationsDB(LOCATIONS_PATH, QUERY_LIBDIR, tmpPath,
                         sizeof tmpPath)) {
      Error("could not obtain LIBDIR\n");
      return FALSE;
   }

   if ((strncmp(execPath, tmpPath, strlen(tmpPath)) != 0) ||
       (strstr(execPath, "..") != NULL)) {
      Error("vmware-user path untrusted\n");
      return FALSE;
   }

   return TRUE;
}
#endif // ifdef USES_LOCATIONS_DB


/*
 *----------------------------------------------------------------------------
 *
 * CompatExec --
 *
 *      Simple platform-dependent execve() wrapper.
 *
 * Results:
 *      False.
 *
 * Side effects:
 *      This function may not return.
 *
 *----------------------------------------------------------------------------
 */

Bool
CompatExec(const char *path, char * const argv[], char * const envp[])
{
   execve(path, argv, envp);
   return FALSE;
}
070701000004A7000081A40000000000000000000000016822550500000FBF000000000000000000000000000000000000004C00000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/wrapper-linux.c   /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * wrapper.c --
 *
 *      Platform specific code for the VMware User Agent setuid wrapper.
 */

#include <sys/mount.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "vmware.h"
#include "wrapper.h"


/*
 * Global functions
 */


#ifdef USES_LOCATIONS_DB
/*
 *-----------------------------------------------------------------------------
 *
 * BuildExecPath --
 *
 *      Determine & return path of vmware-user for use by execve(2).
 *
 * Results:
 *      TRUE on success, FALSE otherwise
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
BuildExecPath(char *execPath,           // OUT: Buffer to store executable's path
              size_t execPathSize)      // IN : size of execPath buffer
{
   char tmpPath[MAXPATHLEN];
   int execLen;

   /*
    * The locations database is the only path that's fixed, and it contains the
    * paths to all the other paths selected during Tools configuration.  The
    * locations database file is only writable by root, so we can trust it.
    */
   if (!QueryLocationsDB(LOCATIONS_PATH, QUERY_SBINDIR, tmpPath, sizeof tmpPath)) {
      Error("could not obtain SBINDIR\n");
      return FALSE;
   }

   if (strlen(tmpPath) + strlen("/vmtoolsd") + 1 > sizeof tmpPath) {
      Error("could not construct program filename\n");
      return FALSE;
   }
   strcat(tmpPath, "/vmtoolsd");

   /*
    * From readlink(2), "The readlink() system call does not append a NUL
    * character to buf."  (NB:  This breaks if user ever replaces the symlink
    * with the target.)
    */
   if ((execLen = readlink(tmpPath, execPath, execPathSize - 1)) == -1) {
      Error("could not resolve symlink: %s\n", strerror(errno));
      return FALSE;
   }

   execPath[execLen] = '\0';

   /*
    * Now make sure that the target is actually part of our "trusted"
    * directory.  (Check that execPath has LIBDIR as a prefix and does
    * not contain "..".)
    */
   if (!QueryLocationsDB(LOCATIONS_PATH, QUERY_LIBDIR, tmpPath,
                         sizeof tmpPath)) {
      Error("could not obtain LIBDIR\n");
      return FALSE;
   }

   if ((strncmp(execPath, tmpPath, strlen(tmpPath)) != 0) ||
       (strstr(execPath, "..") != NULL)) {
      Error("vmware-user path untrusted\n");
      return FALSE;
   }

   return TRUE;
}
#endif // ifdef USES_LOCATIONS_DB


/*
 *----------------------------------------------------------------------------
 *
 * CompatExec --
 *
 *      Simple platform-dependent execve() wrapper.
 *
 * Results:
 *      False.
 *
 * Side effects:
 *      This function may not return.
 *
 *----------------------------------------------------------------------------
 */

Bool
CompatExec(const char *path,    // IN: path to executable
           char * const argv[], // IN: argument vector (see execve)
           char * const envp[]) // IN: environment vector (see execve)
{
   execve(path, argv, envp);
   return FALSE;
}
 070701000004A8000081A40000000000000000000000016822550500000B76000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/wrapper-solaris.c /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * wrapper.c --
 *
 *      Platform dependent code for the VMware User Agent setuid wrapper.
 */

#include <sys/types.h>
#include <sys/systeminfo.h>

#include <errno.h>
#include <strings.h>
#include <unistd.h>

#include "wrapper.h"


/*
 * Global functions
 */


#ifdef USES_LOCATIONS_DB
/*
 *-----------------------------------------------------------------------------
 *
 * BuildExecPath --
 *
 *      Mount the VMBlock file system.
 *
 * Results:
 *      TRUE on success, FALSE otherwise
 *
 * Side effects:
 *      None.
 *
 *-----------------------------------------------------------------------------
 */

Bool
BuildExecPath(char *execPath,           // OUT: Path to executable for isaexec()
              size_t execPathSize)      // IN : size of execPath buffer
{
   /*
    * The locations database is the only path that's fixed, and it contains the
    * paths to all the other paths selected during Tools configuration.  The
    * locations database file is only writable by root, so we can trust it.
    */
   if (!QueryLocationsDB(LOCATIONS_PATH, QUERY_LIBDIR, execPath, execPathSize)) {
      Error("could not obtain LIBDIR\n");
      return FALSE;
   }

   /*
    * The wrapper script now emulates the work done by the isaexec command hence
    * we will simply call execve(2) below and allow the wrapper to do the rest.
    */
   if (strlcat(execPath,
               "/bin/vmware-user-wrapper", execPathSize) >= execPathSize) {
      Error("could not construct program filename\n");
      return FALSE;
   }

   return TRUE;
}
#endif // ifdef USES_LOCATIONS_DB


/*
 *----------------------------------------------------------------------------
 *
 * CompatExec --
 *
 *      Simple platform-dependent isaexec() wrapper.
 *
 * Results:
 *      False.
 *
 * Side effects:
 *      Ideally, this function should not return.
 *
 *----------------------------------------------------------------------------
 */

Bool
CompatExec(const char *path, char * const argv[], char * const envp[])
{
   execve(path, argv, envp);
   return FALSE;
}
  070701000004A9000081A40000000000000000000000016822550500000995000000000000000000000000000000000000004600000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/wrapper.h /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * wrapper.h --
 *
 *      Platform independent definitions for the VMware User Agent setuid
 *      wrapper.
 */

#ifndef _WRAPPER_H_
#define _WRAPPER_H_

#include <sys/types.h>

#include <stdio.h>

#include "vm_basic_types.h"
#include "vmblock.h"

#define progname                "vmware-user"
#define Error(fmt, args...)     fprintf(stderr, "%s: " fmt, progname, ##args);


/*
 * XXX Document official VMware Tools releases vs. open-vm-tools and the
 * use of the locations database in the former vs. compile-time pathing
 * in the latter.
 */
#ifdef USES_LOCATIONS_DB
#   define LOCATIONS_PATH       "/etc/vmware-tools/locations"

/*
 * Locations DB query selector.  Values in this enum are used as array
 * indexes, so any updates to this enum must follow updating
 * main.c::queryMappings.
 */

typedef enum {
   QUERY_LIBDIR = 0,    /* Ask for "LIBDIR" */
   QUERY_BINDIR,        /* Ask for "BINDIR" */
   QUERY_SBINDIR,       /* Ask for "SBINDIR" */
   QUERY_MAX            /* Upper limit -- Insert other queries above only. */
} Selector;
#else
#   ifndef VMTOOLSD_PATH
#      error This program requires either USES_LOCATIONS_DB or VMTOOLSD_PATH.
#   endif // ifndef VMTOOLSD_PATH
#endif // ifdef USES_LOCATIONS_DB


/*
 * Global functions
 */

extern Bool CompatExec(const char *, char * const [], char * const []);
extern Bool BuildExecPath(char *, size_t);

/* See above re: USES_LOCATIONS_DB. */
#ifdef USES_LOCATIONS_DB
extern Bool QueryLocationsDB(const char *, Selector, char *, size_t);
#endif // ifdef USES_LOCATIONS_DB


#endif // ifndef _WRAPPER_H_
   070701000004AA000081A40000000000000000000000016822550500000612000000000000000000000000000000000000004E00000000open-vm-tools-12.5.2/open-vm-tools/vmware-user-suid-wrapper/wrapper_version.h /*********************************************************
 * Copyright (C) 2008-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * wrapper_version.h --
 *
 * Version definitions for the vmware-user SUID wrapper.
 */

#ifndef _WRAPPER_VERSION_H_
#define _WRAPPER_VERSION_H_

/*
 * This component's version is coupled with Tools versioning. The effect
 * is that the version increments with each build, and with each Tools
 * version bump. If and when it becomes necessary to version the component
 * manually, make sure that the version is bumped any time the component or
 * its dependencies are changed.
 */
#include "vm_tools_version.h"
#define WRAPPER_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define WRAPPER_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _WRAPPER_VERSION_H_ */
  070701000004AB000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002E00000000open-vm-tools-12.5.2/open-vm-tools/vmwgfxctrl 070701000004AC000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003600000000open-vm-tools-12.5.2/open-vm-tools/vmwgfxctrl/COPYING 		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000004AD000081A40000000000000000000000016822550500000503000000000000000000000000000000000000003A00000000open-vm-tools-12.5.2/open-vm-tools/vmwgfxctrl/Makefile.am ################################################################################
### Copyright (C) 2021 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

AUTOMAKE_OPTIONS = subdir-objects

if ENABLE_VMWGFXCTRL

bin_PROGRAMS = vmwgfxctrl

vmwgfxctrl_LDADD =
vmwgfxctrl_LDADD += @VMTOOLS_LIBS@
vmwgfxctrl_LDADD += @LIBUDEV_LIBS@

vmwgfxctrl_CFLAGS = -std=gnu89

vmwgfxctrl_CPPFLAGS =
vmwgfxctrl_CPPFLAGS += @VMTOOLS_CPPFLAGS@
vmwgfxctrl_CPPFLAGS += @LIBUDEV_CFLAGS@

vmwgfxctrl_SOURCES =
vmwgfxctrl_SOURCES += main.c
vmwgfxctrl_LINK = $(LINK)

endif
 070701000004AE000081A400000000000000000000000168225505000039E4000000000000000000000000000000000000003500000000open-vm-tools-12.5.2/open-vm-tools/vmwgfxctrl/main.c  /*********************************************************
 * Copyright (C) 2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * main.c --
 *
 *      vmwgfxctrl is a utility application used to control the
 *      vmwgfx drm linux kernel driver.
 *
 *
 * To print the information about the current display topology
 *   vmwgfxctrl --print-topology
 *
 * Setting topology will most likely require root privileges
 *   sudo vmwgfxctrl --set-topology 1024x768+0+0
 *
 * The format to set-topology is
 *   sudo vmwgfxctrl --set-topology WxH+x+y
 *
 * with "WxH+x+y" repeated as many times as the number of screens
 *  being set e.g.
 *   sudo vmwgfxctrl --set-topology 800x600+0+0 800x600+800+0
 * will set two screen right next to each other (the second starts at
 * x=800, where the first one ends), both with height of 600.
 *
 */

#include "vm_basic_defs.h"

#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <vmwgfx_drm.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

/* The DRM device we are looking for */
#define VMWGFXCTRL_VENDOR     "0x15ad"
#define VMWGFXCTRL_SVGA2_DEVICE     "0x0405"
#define VMWGFXCTRL_SVGA3_DEVICE     "0x0406"
#define VMWGFXCTRL_KERNELNAME "vmwgfx"

/* Required DRM version for resolutionKMS */
#define VMWGFXCTRL_DRM_MAJOR  2
#define VMWGFXCTRL_DRM_MINOR  14

static int
vmwgfxOpenDRM(const char *node) // IN: Device node base name.
{
   struct udev *udev;
   struct udev_enumerate *enumerate;
   struct udev_list_entry *devices, *devListEntry;
   struct udev_device *dev;
   int fd = -1;
   int drmFd;
   const char *devNode = NULL;

   /* Force load the kernel module. */
   drmFd = drmOpen(VMWGFXCTRL_KERNELNAME, NULL);
   if (drmFd >= 0) {
      (void) drmDropMaster(drmFd);
   }

   udev = udev_new();
   if (!udev) {
      goto outNoUdev;
   }

   /*
     * Udev error return codes that are not caught immediately are
     * typically caught in the input argument check in the udev
     * function calls following the failing call!
     */
   enumerate = udev_enumerate_new(udev);
   if (udev_enumerate_add_match_subsystem(enumerate, "drm")) {
      goto outErr;
   }
   if (udev_enumerate_add_match_property(enumerate, "DEVTYPE", "drm_minor")) {
      goto outErr;
   }
   if (udev_enumerate_scan_devices(enumerate)) {
      goto outErr;
   }

   devices = udev_enumerate_get_list_entry(enumerate);
   udev_list_entry_foreach(devListEntry, devices) {
      const char *path, *vendor, *device;
      struct udev_device *parent;

      path = udev_list_entry_get_name(devListEntry);
      if (!path) {
         continue;
      }
      if (!strstr(path, node)) {
         continue;
      }

      dev = udev_device_new_from_syspath(udev, path);
      if (!dev) {
         goto outErr;
      }

      parent = udev_device_get_parent_with_subsystem_devtype(dev,
                                                             "pci",
                                                             NULL);
      if (!parent) {
         goto skipCheck;
      }

      vendor = udev_device_get_sysattr_value(parent, "vendor");
      device = udev_device_get_sysattr_value(parent, "device");
      if (!vendor || !device) {
         goto skipCheck;
      }

      if (strcmp(vendor, VMWGFXCTRL_VENDOR) != 0 ||
          (strcmp(device, VMWGFXCTRL_SVGA2_DEVICE) != 0 &&
           strcmp(device, VMWGFXCTRL_SVGA3_DEVICE) != 0)) {
         goto skipCheck;
      }

      devNode = udev_device_get_devnode(dev);
      if (!devNode) {
         goto outFound;
      }

      fd = open(devNode, O_RDWR);
      udev_device_unref(dev);
      break;

skipCheck:
      udev_device_unref(dev);
   }

   udev_enumerate_unref(enumerate);
   udev_unref(udev);

   if (drmFd >= 0) {
      drmClose(drmFd);
   }

   return fd;

outFound:
   udev_device_unref(dev);
outErr:
   udev_enumerate_unref(enumerate);
   udev_unref(udev);
outNoUdev:
   if (drmFd >= 0) {
      drmClose(drmFd);
   }

   return -1;
}


static bool
checkDRMVersion(int fd)  // IN: An open DRM file descriptor.
{
   drmVersionPtr ver = drmGetVersion(fd);

   if (!ver) {
      fprintf(stderr, "%s: Failed to get DRM version.\n", __func__);
      return false;
   }

   if (ver->version_major != VMWGFXCTRL_DRM_MAJOR ||
       ver->version_minor < VMWGFXCTRL_DRM_MINOR) {
      fprintf(stderr, "%s: Insufficient DRM version %d.%d for resolutionKMS.\n",
              __func__, ver->version_major, ver->version_minor);
      drmFreeVersion(ver);
      return false;
   }
   printf("DRM version %d.%d.\n", ver->version_major, ver->version_minor);

   drmFreeVersion(ver);
   return true;
}

static int
vmwgfxOpen(bool use_render_node)
{
   int fd = -1;

   if (use_render_node) {
      fd = vmwgfxOpenDRM("renderD");
      if (fd < 0) {
         fprintf(stderr, "%s: Failed to open DRM render node.\n", __func__);
      }
   }
   if (fd < 0) {
      fd = vmwgfxOpenDRM("card");
      if (fd >= 0) {
         (void) drmDropMaster(fd);
      }
   }
   if (fd < 0) {
      fprintf(stderr, "%s: Failed to open DRM card node.\n", __func__);
      goto outErr;
   }

   if (checkDRMVersion(fd)) {
      return fd;
   }

   close(fd);
outErr:
   return -1;
}

static bool
setTopology(int fd,
            const struct drm_vmw_rect *rects,
            unsigned int num_rects)
{
   struct drm_vmw_update_layout_arg arg;
   int ret;

   memset(&arg, 0, sizeof arg);
   arg.num_outputs = num_rects;
   arg.rects = (unsigned long) rects;

   ret = drmCommandWrite(fd, DRM_VMW_UPDATE_LAYOUT, &arg, sizeof arg);
   if (ret < 0) {
      fprintf(stderr, "%s, Error: write to kernel failed: %d\n",
              __func__, ret);
      return false;
   }

   return true;
}

static bool
parseRects(uint32_t num_rects,
           int arg,
           char **argv,
           struct drm_vmw_rect **rects)
{
   uint32_t j;
   /*
     * The argument string will look something like:
     *   WxH+x+y * count.
     *
     * e.g.
     *    640x480+0+0 640x480+640+0 640x480+1280+0
     * will set three 640x480 screens horizontally next to
     * each other (i.e. one 1920x480 viewport)
     */

   *rects = malloc(sizeof *rects[0] * num_rects);
   if (!*rects) {
      fprintf(stderr,
              "Failed to alloc buffer for display info");
      return false;
   }

   for (j = 0; j < num_rects; ++j) {
      char *p = argv[arg++];
      if (sscanf(p, "%ux%u+%d+%d", &(*rects)[j].w, &(*rects)[j].h,
                 &(*rects)[j].x, &(*rects)[j].y) != 4) {
         fprintf(stderr,
                 "Couldn't parse screen dimensions for topology #%d: '%s'. Expected WxH+x+y format (no spaces).\n",
                 j, p);
         return false;
      }
   }
   return true;
}

static const char *connector_type_names[] = {
   "unknown",
   "VGA",
   "DVI-I",
   "DVI-D",
   "DVI-A",
   "composite",
   "s-video",
   "LVDS",
   "component",
   "9-pin DIN",
   "DP",
   "HDMI-A",
   "HDMI-B",
   "TV",
   "eDP",
   "Virtual",
   "DSI",
   "DPI",
   "WRITEBACK"
};



static void
printProperty(int fd, int idx, drmModePropertyPtr props, uint64_t value)
{
   int j;

   printf("\t   %d: %s  (id=%i, flags=%i, count_values=%d)\n", idx, props->name,
          props->prop_id, props->flags, props->count_values);

   if (props->count_values) {
      printf("\t\tvalues       :");
      for (j = 0; j < props->count_values; j++) {
         printf(" %" PRIu64, props->values[j]);
      }
      printf("\n");
   }


   printf("\t\tcount_enums  : %d\n", props->count_enums);

   if (props->flags & DRM_MODE_PROP_BLOB) {
      drmModePropertyBlobPtr blob;

      blob = drmModeGetPropertyBlob(fd, value);
      if (blob) {
         printf("\t\tblob is %d length, %08X\n", blob->length, *(uint32_t *)blob->data);
         drmModeFreePropertyBlob(blob);
      } else {
         printf("\t\terror getting blob %" PRIu64 "\n", value);
      }

   } else {
      const char *name = NULL;
      for (j = 0; j < props->count_enums; j++) {
         printf("\t\t\t%lld = %s\n", props->enums[j].value, props->enums[j].name);
         if (props->enums[j].value == value) {
            name = props->enums[j].name;
         }
      }

      if (props->count_enums && name) {
         printf("\t\tcon_value    : %s\n", name);
      } else {
         printf("\t\tcon_value    : %" PRIu64 "\n", value);
      }
   }
}


static void
printMode(const struct drm_mode_modeinfo *mode, int idx, bool verbose)
{
   if (verbose) {
      printf("\t  %d: %s\n", idx, mode->name);
      printf("\t\tclock       : %i\n", mode->clock);
      printf("\t\thdisplay    : %i\n", mode->hdisplay);
      printf("\t\thsync_start : %i\n", mode->hsync_start);
      printf("\t\thsync_end   : %i\n", mode->hsync_end);
      printf("\t\thtotal      : %i\n", mode->htotal);
      printf("\t\thskew       : %i\n", mode->hskew);
      printf("\t\tvdisplay    : %i\n", mode->vdisplay);
      printf("\t\tvsync_start : %i\n", mode->vsync_start);
      printf("\t\tvsync_end   : %i\n", mode->vsync_end);
      printf("\t\tvtotal      : %i\n", mode->vtotal);
      printf("\t\tvscan       : %i\n", mode->vscan);
      printf("\t\tvrefresh    : %i\n", mode->vrefresh);
      printf("\t\tflags       : %i\n", mode->flags);
   } else {
      printf("\t  %d: \"%s\" %ix%i %i\n", idx,
             mode->name, mode->hdisplay,
             mode->vdisplay, mode->vrefresh);
   }
}


static const char *
drmModeConnectionToString(drmModeConnection modeConnection)
{
   switch (modeConnection) {
   case DRM_MODE_CONNECTED:
      return "DRM_MODE_CONNECTED";
   case DRM_MODE_DISCONNECTED:
      return "DRM_MODE_DISCONNECTED";
   case DRM_MODE_UNKNOWNCONNECTION:
      return "DRM_MODE_UNKNOWNCONNECTION";
   default:
      return "invalid";
   }
}


static void
printTopology(void)
{
   int i, j;
   int fd = vmwgfxOpen(false);
   if (fd < 0) {
      fprintf(stderr, "Wasn't able to open the drm device\n");
      exit(1);
   }

   drmModeResPtr res = drmModeGetResources(fd);
   if (res == 0) {
      printf("Failed to get resources from card\n");
      drmClose(fd);
      return;
   }

   printf("Resources\n");
   printf("  count_connectors : %i\n", res->count_connectors);
   printf("  count_encoders   : %i\n", res->count_encoders);
   printf("  count_crtcs      : %i\n", res->count_crtcs);
   printf("  count_fbs        : %i\n", res->count_fbs);
   printf("  min_size         : [%u, %u]\n", res->min_width, res->min_height);
   printf("  max_size         : [%u, %u]\n", res->max_width, res->max_height);
   printf("\n");

   for (i = 0; i < res->count_connectors; i++) {
      drmModeConnectorPtr connector = drmModeGetConnector(fd, res->connectors[i]);
      if (!connector) {
         printf("Could not get connector %i\n", res->connectors[i]);
         continue;
      }

      assert(ARRAYSIZE(connector_type_names) > connector->connector_type);
      const char *connector_type_name =
            connector_type_names[connector->connector_type];
      printf("Connector: %s-%d (%s)\n", connector_type_name,
             connector->connector_type_id,
             drmModeConnectionToString(connector->connection));

      if (connector->connection == DRM_MODE_CONNECTED) {
         printf("\tencoder id     : %i\n", connector->encoder_id);
         printf("\tsize           : %ix%i (mm)\n", connector->mmWidth, connector->mmHeight);
         printf("\tcount_modes    : %i\n", connector->count_modes);
         printf("\tcount_props    : %i\n", connector->count_props);

         if (connector->count_props) {
            printf("\tProperties:\n");
         }
         for (j = 0; j < connector->count_props; j++) {
            drmModePropertyPtr props = drmModeGetProperty(fd, connector->props[j]);
            if (props) {
               printProperty(fd, j, props, connector->prop_values[j]);
               drmModeFreeProperty(props);
            }
         }
         if (connector->count_modes) {
            printf("\tModes:\n");
         }
         for (j = 0; j < connector->count_modes; j++) {
            struct drm_mode_modeinfo *mode = (struct drm_mode_modeinfo *)&connector->modes[j];
            printMode(mode, j, false);
         }
      }

      drmModeFreeConnector(connector);
   }
   printf("\n");

   drmModeFreeResources(res);

   close(fd);
}

static void
run(int argc, char **argv)
{
   int i, j;
   for (i = 1; i < argc; ++i) {
      if (strcmp(argv[i], "--help") == 0) {
         printf("%s: \n", argv[0]);
         printf("\t--help prints out the help screen\n");
         printf("\t--print-topology prints out the currently set topology\n");
         printf("\t--set-topology WxH+x+y  (WxH+x+y repeated for each screen)\n");
         printf("\t\te.g. 640x480+0+0 800x480+640+0 640x800+0+480\n");
         return;
      } else if (strcmp(argv[i], "--set-topology") == 0) {
         struct drm_vmw_rect *rects;
         uint32_t num_rects = argc - i - 1;

         if (num_rects == 0) {
            fprintf(stderr, "%s: set-topology is missing the dimensions\n",
                    argv[0]);
            exit(1);
         }
         if (!parseRects(num_rects, i + 1, argv, &rects)) {
            exit(1);
         }

         printf("Setting topology for %d screens", num_rects);
         for (j = 0; j < num_rects; ++j) {
            printf(", [%d, %d, %u, %u]",
                   rects[j].x, rects[j].y,
                   rects[j].w, rects[j].h);
         }
         printf("\n");

         int fd = vmwgfxOpen(true);
         if (fd < 0) {
            fprintf(stderr, "Wasn't able to open the drm device\n");
            free(rects);
            exit(1);
         }
         setTopology(fd, rects, num_rects);
         close(fd);
         free(rects);
         return;
      } else if (strcmp(argv[i], "--print-topology") == 0) {
         printTopology();
         return;
      } else {
         fprintf(stderr, "Unknown argument '%s'\n", argv[i]);
         exit(1);
      }
   }
}

int main(int argc, char **argv)
{
   run(argc, argv);

   return 0;
}
070701000004AF000041ED0000000000000000000000026822550500000000000000000000000000000000000000000000002C00000000open-vm-tools-12.5.2/open-vm-tools/xferlogs   070701000004B0000081A40000000000000000000000016822550500006739000000000000000000000000000000000000003400000000open-vm-tools-12.5.2/open-vm-tools/xferlogs/COPYING   		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 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.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, 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 library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.

  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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.

  If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be 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.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.

  9. 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 Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.

  11. 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 Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.

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.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  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.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; 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.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
   070701000004B1000081A4000000000000000000000001682255050000077D000000000000000000000000000000000000003800000000open-vm-tools-12.5.2/open-vm-tools/xferlogs/Makefile.am   ################################################################################
### Copyright (c) 2007-2016,2020 VMware, Inc.  All rights reserved.
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of version 2 of the GNU General Public License as
### published by the Free Software Foundation.
###
### 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
################################################################################

bin_PROGRAMS = vmware-xferlogs

vmware_xferlogs_LDADD =
vmware_xferlogs_LDADD += @GLIB2_LIBS@
vmware_xferlogs_LDADD += ../lib/rpcOut/libRpcOut.la
vmware_xferlogs_LDADD += ../lib/rpcVmx/libRpcVmx.la
vmware_xferlogs_LDADD += ../lib/message/libMessage.la
vmware_xferlogs_LDADD += ../lib/backdoor/libBackdoor.la
vmware_xferlogs_LDADD += ../lib/string/libString.la
vmware_xferlogs_LDADD += ../lib/vmCheck/libVmCheck.la
vmware_xferlogs_LDADD += ../lib/vmSignal/libVmSignal.la
vmware_xferlogs_LDADD += ../lib/misc/libMisc.la
vmware_xferlogs_LDADD += ../lib/stubs/libStubs.la

vmware_xferlogs_CPPFLAGS =
vmware_xferlogs_CPPFLAGS += @GLIB2_CPPFLAGS@

vmware_xferlogs_SOURCES =
vmware_xferlogs_SOURCES += xferlogs.c

if HAVE_ICU
   vmware_xferlogs_LDADD += @ICU_LIBS@
   vmware_xferlogs_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
                          $(LIBTOOLFLAGS) --mode=link $(CXX) $(AM_CXXFLAGS) \
                          $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
else
   vmware_xferlogs_LINK = $(LINK)
endif
   070701000004B2000081A4000000000000000000000001682255050000361C000000000000000000000000000000000000003700000000open-vm-tools-12.5.2/open-vm-tools/xferlogs/xferlogs.c    /*********************************************************
 * Copyright (c) 2006-2021 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * xferlogs.c --
 *
 *      Simple program based on rpctool to dump the vm-support output to
 *      the vmx log file base64 encoded, it can dump any file supplied on the
 *      commandline to the vmx log. It also does the decoding part of it
 *      it can read the vmware.log file decode and write the encoded files in
 *      the directory it was invoked.
 *
 *      example of a transfer found in the vmx log file.
 *      Aug 24 18:48:09: vcpu-0| Guest: >Logfile Begins : /root/install.log: ver - 1
 *      Aug 24 18:48:09: vcpu-0| Guest: >SW5zdGFsbGluZyA0NDEgcGFja2FnZXMKCkluc3RhbGxpbmcgZ2xpYmMtY29tbW9uLTIuMi41LTM0
 *      Aug 24 18:48:09: vcpu-0| Guest: >LgpJbnN0YWxsaW5nIGh3ZGF0YS0wLjE0LTEuCkluc3RhbGxpbmcgaW5kZXhodG1sLTcuMy0zLgpJ
 *      Aug 24 18:48:09: vcpu-0| Guest: >bnN0YWxsaW5nIG1haWxjYXAtMi4xLjktMi4KSW5zdGFsbGluZyBtYW4tcGFnZXMtMS40OC0yLgpJ
 *      ....
 *      ....
 *      Aug 24 18:48:10: vcpu-0| Guest: >Mi4K
 *      Aug 24 18:48:10: vcpu-0| Guest: >Logfile Ends
 *
 */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <glib.h>

#include "vmware.h"
#include "vmsupport.h"
#include "vmcheck.h"
#include "debug.h"
#include "rpcvmx.h"
#include "rpcout.h"
#include "base64.h"
#include "str.h"
#include "strutil.h"

#include "xferlogs_version.h"
#include "vm_version.h"
#ifdef _WIN32
#include "vmware/tools/win32util.h"
#endif
#include "embed_version.h"
VM_EMBED_VERSION(XFERLOGS_VERSION_STRING);

/*
 * "The resultant base64-encoded data exceeds the original in length by the
 * ratio 4:3, and typically appears to consist of seemingly random characters.
 * As newlines, represented by a CR+LF pair, are inserted in the encoded data
 * every 76 characters, the actual length of the encoded data is approximately
 * 136.8% of the original." - Base64 Wiki
 * And just so that it produces 80 char output.
 */

#define BUF_BASE64_SIZE        57
#define BUF_OUT_SIZE           256
#define LOG_GUEST_MARK         "Guest: >"
#define LOG_START_MARK         ">Logfile Begins "
#define LOG_END_MARK           ">Logfile Ends "

typedef enum {
   NOT_IN_GUEST_LOGGING,
   IN_GUEST_LOGGING
} extractMode;

#define LOG_VERSION            1


/*
 *--------------------------------------------------------------------------
 *
 * xmitFile --
 *
 *       This function transfers a file using the rpc channel in base64
 *       encoding to the vmx logs.
 *
 * Results:
 *       None.
 *
 * Side effects:
 *       The program would exit if an error occurs in Base64_Encode.
 *       Output is added to the vmx log file.
 *
 *--------------------------------------------------------------------------
 */

static void
xmitFile(char *filename) //IN : file to be transmitted.
{
   FILE *fp;
   size_t readLen;
   char buf[BUF_BASE64_SIZE];

   /*
    * We have a unique identifier saying that this is guest dumping the
    * output of logs and not any other logging information from the guest.
    */
   char base64B[BUF_BASE64_SIZE * 2] = ">";
   char *base64Buf = base64B + 1;

   if (!(fp = fopen(filename, "rb"))) {
      Warning("Unable to open file %s with errno %d\n", filename, errno);
      exit(-1);
   }

   //XXX the format below is hardcoded and used by extractFile
   RpcVMX_Log("%s: %s: ver - %d", LOG_START_MARK, filename, LOG_VERSION);
   while ((readLen = fread(buf, 1, sizeof buf, fp)) > 0 ) {
      if (Base64_Encode(buf, readLen, base64Buf, sizeof base64B - 1, NULL)) {
         RpcVMX_Log("%s", base64B);
      } else {
         Warning("Error in Base64_Encode\n");
         goto exit;
      }
   }
exit:
   RpcVMX_Log(LOG_END_MARK);
   fclose(fp);
}


/*
 *--------------------------------------------------------------------------
 *
 * extractFile --
 *
 *       This function iterates through the vmx log file and for every
 *       line which has a "Guest: >" writes the unencoded base64 output to
 *       a file, depending on the state machine.
 *
 * Results:
 *       None
 *
 * Side effects:
 *       The program would exit if unable to read the input file.
 *       A series of decoded output files are created.
 *--------------------------------------------------------------------------
 */

static void
extractFile(char *filename) //IN: vmx log filename e.g. vmware.log
{
   FILE *fp;
   FILE *outfp = NULL;
   char buf[BUF_OUT_SIZE];
   uint8 base64Out[BUF_OUT_SIZE];
   size_t lenOut;
   char fname[256];
   char *ptrStr, *logInpFilename, *ver;
   int version;
   int filenu = 0; // output file enumerator
   DEBUG_ONLY(extractMode state = NOT_IN_GUEST_LOGGING);


   if (!(fp = fopen(filename, "rt"))) {
      Warning("Error opening file %s, errno %d - %s \n",
              filename, errno, strerror(errno));
      exit(-1);
   }

   while (fgets(buf, sizeof buf, fp)) {

      /*
       * The state machine determines when to open, write and close a file.
       */
      if (strstr(buf, LOG_GUEST_MARK)) {
         if (strstr(buf, LOG_START_MARK)) { //open a new output file.
            const char *ext;
            char tstamp[32];
            time_t now;

            /*
             * There could be multiple LOG_START_MARK in the log,
             * close existing one before opening a new file.
             */
            if (outfp) {
               ASSERT(state == IN_GUEST_LOGGING);
               Warning("Found a new start mark before end mark for "
                       "previous one\n");
               fclose(outfp);
               outfp = NULL;
            } else {
               ASSERT(state == NOT_IN_GUEST_LOGGING);
            }
            DEBUG_ONLY(state = IN_GUEST_LOGGING);

            /*
             * read the input filename, which was the filename written by the
             * guest.
             */
            logInpFilename = strstr(buf, LOG_START_MARK);
            logInpFilename += sizeof LOG_START_MARK;
            ptrStr = strstr(logInpFilename, ": ver ");
            if (ptrStr == NULL) {
               fprintf(stderr, "Invalid start log mark.");
               break;
            }
            *ptrStr = '\0';

            /*
             * Ignore the filename in the log, for obvious security reasons
             * and create a new filename consiting of time and enumerator.
             * Try to maintain the same extension reported by the guest,
             * though, if it's in the "allowed" list.
             */
            if (StrUtil_EndsWith(logInpFilename, ".zip")) {
               ext = "zip";
            } else if (StrUtil_EndsWith(logInpFilename, ".tar.gz")) {
               ext = "tar.gz";
            } else {
               /* Something else we don't expect from out vm-support scripts. */
               ext = "log";
            }

            time(&now);
            strftime(tstamp, sizeof tstamp, "%Y-%m-%d-%H-%M", localtime(&now));
            Str_Sprintf(fname, sizeof fname, "vm-support-%d-%s.%s",
                        filenu++, tstamp, ext);

            /*
             * Read the version information, if they dont match just warn
             * and leave the outfp null, so we do process the input file, but
             * dont write anything.
             */
            ptrStr++;
            ver = strstr(ptrStr, "ver - ");
            if (!ver) {
               Warning("No version information detected\n");
            } else {
               ver = ver + sizeof "ver - " - 1;
               version = strtol(ver, NULL, 0);
               if (version != LOG_VERSION) {
                  Warning("Input version %d doesn't match the\
                          version of this binary %d", version, LOG_VERSION);
               } else {
                  printf("Reading file %s to %s \n", logInpFilename, fname);
                  if (!(outfp = fopen(fname, "wb"))) {
                     Warning("Error opening file %s\n", fname);
                  }
               }
            }
         } else if (strstr(buf, LOG_END_MARK)) { // close the output file.
            /*
             * Need to check outfp, because we might get LOG_END_MARK
             * before LOG_START_MARK due to log rotation.
             */
            if (outfp) {
               ASSERT(state == IN_GUEST_LOGGING);
               fclose(outfp);
               outfp = NULL;
            } else {
               ASSERT(state == NOT_IN_GUEST_LOGGING);
               Warning("Reached file end mark without start mark\n");
            }
            DEBUG_ONLY(state = NOT_IN_GUEST_LOGGING);
         } else { // write to the output file
            if (outfp) {
               ASSERT(state == IN_GUEST_LOGGING);
               ptrStr = strstr(buf, LOG_GUEST_MARK);
               ptrStr += sizeof LOG_GUEST_MARK - 1;
               if (Base64_Decode(ptrStr, base64Out, BUF_OUT_SIZE, &lenOut)) {
                  if (fwrite(base64Out, 1, lenOut, outfp) != lenOut) {
                     Warning("Error writing output\n");
                  }
               } else {
                  Warning("Error decoding output %s\n", ptrStr);
               }
            } else {
               ASSERT(state == NOT_IN_GUEST_LOGGING);
               Warning("Missing file start mark\n");
            }
         }
      }
   }

   /*
    * We may need to close file in case LOG_END_MARK is missing.
    */
   if (outfp) {
      ASSERT(state == IN_GUEST_LOGGING);
      fclose(outfp);
   }
   fclose(fp);
}


static void
usage(GOptionContext *optCtx)
{
   gchar *usage =  g_option_context_get_help(optCtx, TRUE, NULL);
   g_print("%s", usage);
   g_free(usage);
}


int
main(int argc,
     char *argv[])
{
   gchar *gAppName;
   gchar *encBuffer = NULL; // storage for filename for 'enc' option
   gchar *decBuffer = NULL; // storage for filename for 'dec' option
   gchar *vmsupportStatus = NULL; // storage for vmsupport status for 'upd' opt
   int success = -1;
   int status;
   /*
    * This flag will be true if option passed starts with '-'.
    * This means we can use glib parser to see if it is a
    * valid option. Otherwise, try original parsing.
    */
   gboolean useGlibParser = (argc > 1) && (argv[1][0] == '-');

   GOptionEntry options[] = {
      {"put", 'p', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_FILENAME, &encBuffer,
       "encodes and transfers <filename> to the VMX log.", "<filename>"},
      {"get", 'g', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_FILENAME, &decBuffer,
       "extracts encoded data to <filename> from the VMX log.", "<filename>"},
      {"update", 'u', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING,
       &vmsupportStatus, "updates status of vmsupport to <status>.",
       "<status>"},
      {NULL},
   };
   GOptionContext *optCtx;

#ifdef _WIN32
   WinUtil_EnableSafePathSearching(TRUE);
#endif

   gAppName = g_path_get_basename(argv[0]);
   g_set_prgname(gAppName);
   optCtx = g_option_context_new(NULL);
   g_option_context_add_main_entries(optCtx, options, NULL);

   /*
    * Check if environment is a VM
    */
   if (!VmCheck_IsVirtualWorld()) {
      g_printerr("Error: %s must be run inside a virtual machine"
                 " on a VMware hypervisor product.\n", gAppName);
      goto out;
   }

   if (useGlibParser) {
     /*
      * numOptions will count the number of options passed
      * and fail if more than one option is passed.
      */
      int numOptions;
      GError *gErr = NULL;

      if (!g_option_context_parse(optCtx, &argc, &argv, &gErr)) {
         g_printerr("%s: %s\n", gAppName, gErr->message);
         g_error_free(gErr);
         goto out;
      }

      numOptions = (encBuffer != NULL ? 1 : 0) + (decBuffer != NULL ? 1 : 0) +
                   (vmsupportStatus != NULL ? 1 : 0);

      if (numOptions > 1) {
         g_printerr("%s: Use one option per command.\n", gAppName);
         usage(optCtx);
         goto out;
      }
   } else {
      /*
       * If not using glib parser then check for old style options
       */
      if (argc != 3) {
         g_printerr("%s: Incorrect number of arguments.\n", gAppName);
         usage(optCtx);
         goto out;
      }

      if ((strncmp(argv[1], "enc", 3) == 0)) {
         encBuffer = argv[2];
      } else if ((strncmp(argv[1], "dec", 3) == 0)) {
         decBuffer = argv[2];
      } else if ((strncmp(argv[1], "upd", 3) == 0)) {
         vmsupportStatus = argv[2];
      }
   }

   if (encBuffer != NULL) {
      xmitFile(encBuffer);
   } else if (decBuffer != NULL) {
      extractFile(decBuffer);
   } else if (vmsupportStatus != NULL) {
      if (!(StrUtil_StrToInt(&status, vmsupportStatus))) {
         g_printerr("%s: Bad value specified.\n", gAppName);
         goto out;
      }
      RpcOut_sendOne(NULL, NULL, RPC_VMSUPPORT_STATUS " %d", status);
   } else {
      g_printerr("%s: Incorrect usage.\n", gAppName);
      usage(optCtx);
      goto out;
   }

   success = 0;

out:
   if (useGlibParser) {
      g_free(encBuffer);
      g_free(decBuffer);
      g_free(vmsupportStatus);
   }

   g_option_context_free(optCtx);
   g_free(gAppName);
   return success;
}
070701000004B3000081A40000000000000000000000016822550500000617000000000000000000000000000000000000003F00000000open-vm-tools-12.5.2/open-vm-tools/xferlogs/xferlogs_version.h    /*********************************************************
 * Copyright (C) 2007-2016 VMware, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation version 2.1 and no later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *********************************************************/

/*
 * xferlogs_version.h --
 *
 * Version definitions for the log transfering utility.
 */

#ifndef _XFERLOGS_VERSION_H_
#define _XFERLOGS_VERSION_H_

/*
 * This component's version is coupled with Tools versioning. The effect
 * is that the version increments with each build, and with each Tools
 * version bump. If and when it becomes necessary to version the component
 * manually, make sure that the version is bumped any time the component or
 * its dependencies are changed.
 */
#include "vm_tools_version.h"
#define XFERLOGS_VERSION_COMMAS   TOOLS_VERSION_EXT_CURRENT_CSV
#define XFERLOGS_VERSION_STRING   TOOLS_VERSION_EXT_CURRENT_STR

#endif /* _XFERLOGS_VERSION_H_ */
 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!                                                                                                                                                                                                                27690 blocks
